diff --git a/build.zig b/build.zig index 3ba054e..67142ed 100644 --- a/build.zig +++ b/build.zig @@ -29,6 +29,7 @@ pub fn build(b: *std.Build) !void { .target = target, .optimize = optimize, .linkage = linkMode, + .dependency = b.dependency("phobos", .{}), }); } @@ -114,6 +115,10 @@ fn buildD(b: *std.Build, options: ldc2.DCompileStep) !void { } fn buildPhobos(b: *std.Build, options: buildOptions) !void { + const phobos_path = if (options.dependency) |dep| + dep.path("").getPath(b) + else + unreachable; const tagLabel = switch (options.optimize) { .Debug => "-debug", else => "", @@ -131,7 +136,187 @@ fn buildPhobos(b: *std.Build, options: buildOptions) !void { .linkage = options.linkage, .target = options.target, .optimize = options.optimize, - .sources = std_src, + .sources = &.{ + b.pathJoin(&.{ phobos_path, "etc/c/curl.d" }), + b.pathJoin(&.{ phobos_path, "etc/c/odbc/sql.d" }), + b.pathJoin(&.{ phobos_path, "etc/c/odbc/sqlext.d" }), + b.pathJoin(&.{ phobos_path, "etc/c/odbc/sqltypes.d" }), + b.pathJoin(&.{ phobos_path, "etc/c/odbc/sqlucode.d" }), + b.pathJoin(&.{ phobos_path, "etc/c/sqlite3.d" }), + b.pathJoin(&.{ phobos_path, "etc/c/zlib.d" }), + b.pathJoin(&.{ phobos_path, "phobos/sys/compiler.d" }), + b.pathJoin(&.{ phobos_path, "phobos/sys/meta.d" }), + b.pathJoin(&.{ phobos_path, "phobos/sys/system.d" }), + b.pathJoin(&.{ phobos_path, "phobos/sys/traits.d" }), + b.pathJoin(&.{ phobos_path, "std/algorithm/comparison.d" }), + b.pathJoin(&.{ phobos_path, "std/algorithm/internal.d" }), + b.pathJoin(&.{ phobos_path, "std/algorithm/iteration.d" }), + b.pathJoin(&.{ phobos_path, "std/algorithm/mutation.d" }), + b.pathJoin(&.{ phobos_path, "std/algorithm/package.d" }), + b.pathJoin(&.{ phobos_path, "std/algorithm/searching.d" }), + b.pathJoin(&.{ phobos_path, "std/algorithm/setops.d" }), + b.pathJoin(&.{ phobos_path, "std/algorithm/sorting.d" }), + b.pathJoin(&.{ phobos_path, "std/array.d" }), + b.pathJoin(&.{ phobos_path, "std/ascii.d" }), + b.pathJoin(&.{ phobos_path, "std/base64.d" }), + b.pathJoin(&.{ phobos_path, "std/bigint.d" }), + b.pathJoin(&.{ phobos_path, "std/bitmanip.d" }), + b.pathJoin(&.{ phobos_path, "std/checkedint.d" }), + b.pathJoin(&.{ phobos_path, "std/compiler.d" }), + b.pathJoin(&.{ phobos_path, "std/complex.d" }), + b.pathJoin(&.{ phobos_path, "std/concurrency.d" }), + b.pathJoin(&.{ phobos_path, "std/container/array.d" }), + b.pathJoin(&.{ phobos_path, "std/container/binaryheap.d" }), + b.pathJoin(&.{ phobos_path, "std/container/dlist.d" }), + b.pathJoin(&.{ phobos_path, "std/container/package.d" }), + b.pathJoin(&.{ phobos_path, "std/container/rbtree.d" }), + b.pathJoin(&.{ phobos_path, "std/container/slist.d" }), + b.pathJoin(&.{ phobos_path, "std/container/util.d" }), + b.pathJoin(&.{ phobos_path, "std/conv.d" }), + b.pathJoin(&.{ phobos_path, "std/csv.d" }), + b.pathJoin(&.{ phobos_path, "std/datetime/date.d" }), + b.pathJoin(&.{ phobos_path, "std/datetime/interval.d" }), + b.pathJoin(&.{ phobos_path, "std/datetime/package.d" }), + b.pathJoin(&.{ phobos_path, "std/datetime/stopwatch.d" }), + b.pathJoin(&.{ phobos_path, "std/datetime/systime.d" }), + b.pathJoin(&.{ phobos_path, "std/datetime/timezone.d" }), + b.pathJoin(&.{ phobos_path, "std/demangle.d" }), + b.pathJoin(&.{ phobos_path, "std/digest/crc.d" }), + b.pathJoin(&.{ phobos_path, "std/digest/hmac.d" }), + b.pathJoin(&.{ phobos_path, "std/digest/md.d" }), + b.pathJoin(&.{ phobos_path, "std/digest/murmurhash.d" }), + b.pathJoin(&.{ phobos_path, "std/digest/package.d" }), + b.pathJoin(&.{ phobos_path, "std/digest/ripemd.d" }), + b.pathJoin(&.{ phobos_path, "std/digest/sha.d" }), + b.pathJoin(&.{ phobos_path, "std/encoding.d" }), + b.pathJoin(&.{ phobos_path, "std/exception.d" }), + b.pathJoin(&.{ phobos_path, "std/experimental/allocator/building_blocks/affix_allocator.d" }), + b.pathJoin(&.{ phobos_path, "std/experimental/allocator/building_blocks/aligned_block_list.d" }), + b.pathJoin(&.{ phobos_path, "std/experimental/allocator/building_blocks/allocator_list.d" }), + b.pathJoin(&.{ phobos_path, "std/experimental/allocator/building_blocks/ascending_page_allocator.d" }), + b.pathJoin(&.{ phobos_path, "std/experimental/allocator/building_blocks/bitmapped_block.d" }), + b.pathJoin(&.{ phobos_path, "std/experimental/allocator/building_blocks/bucketizer.d" }), + b.pathJoin(&.{ phobos_path, "std/experimental/allocator/building_blocks/fallback_allocator.d" }), + b.pathJoin(&.{ phobos_path, "std/experimental/allocator/building_blocks/free_list.d" }), + b.pathJoin(&.{ phobos_path, "std/experimental/allocator/building_blocks/free_tree.d" }), + b.pathJoin(&.{ phobos_path, "std/experimental/allocator/building_blocks/kernighan_ritchie.d" }), + b.pathJoin(&.{ phobos_path, "std/experimental/allocator/building_blocks/null_allocator.d" }), + b.pathJoin(&.{ phobos_path, "std/experimental/allocator/building_blocks/package.d" }), + b.pathJoin(&.{ phobos_path, "std/experimental/allocator/building_blocks/quantizer.d" }), + b.pathJoin(&.{ phobos_path, "std/experimental/allocator/building_blocks/region.d" }), + b.pathJoin(&.{ phobos_path, "std/experimental/allocator/building_blocks/scoped_allocator.d" }), + b.pathJoin(&.{ phobos_path, "std/experimental/allocator/building_blocks/segregator.d" }), + b.pathJoin(&.{ phobos_path, "std/experimental/allocator/building_blocks/stats_collector.d" }), + b.pathJoin(&.{ phobos_path, "std/experimental/allocator/common.d" }), + b.pathJoin(&.{ phobos_path, "std/experimental/allocator/gc_allocator.d" }), + b.pathJoin(&.{ phobos_path, "std/experimental/allocator/mallocator.d" }), + b.pathJoin(&.{ phobos_path, "std/experimental/allocator/mmap_allocator.d" }), + b.pathJoin(&.{ phobos_path, "std/experimental/allocator/package.d" }), + b.pathJoin(&.{ phobos_path, "std/experimental/allocator/showcase.d" }), + b.pathJoin(&.{ phobos_path, "std/experimental/allocator/typed.d" }), + b.pathJoin(&.{ phobos_path, "std/experimental/checkedint.d" }), + b.pathJoin(&.{ phobos_path, "std/experimental/logger/core.d" }), + b.pathJoin(&.{ phobos_path, "std/experimental/logger/filelogger.d" }), + b.pathJoin(&.{ phobos_path, "std/experimental/logger/multilogger.d" }), + b.pathJoin(&.{ phobos_path, "std/experimental/logger/nulllogger.d" }), + b.pathJoin(&.{ phobos_path, "std/experimental/logger/package.d" }), + b.pathJoin(&.{ phobos_path, "std/file.d" }), + b.pathJoin(&.{ phobos_path, "std/format/internal/floats.d" }), + b.pathJoin(&.{ phobos_path, "std/format/internal/read.d" }), + b.pathJoin(&.{ phobos_path, "std/format/internal/write.d" }), + b.pathJoin(&.{ phobos_path, "std/format/package.d" }), + b.pathJoin(&.{ phobos_path, "std/format/read.d" }), + b.pathJoin(&.{ phobos_path, "std/format/spec.d" }), + b.pathJoin(&.{ phobos_path, "std/format/write.d" }), + b.pathJoin(&.{ phobos_path, "std/functional.d" }), + b.pathJoin(&.{ phobos_path, "std/getopt.d" }), + b.pathJoin(&.{ phobos_path, "std/int128.d" }), + b.pathJoin(&.{ phobos_path, "std/internal/attributes.d" }), + b.pathJoin(&.{ phobos_path, "std/internal/cstring.d" }), + b.pathJoin(&.{ phobos_path, "std/internal/digest/sha_SSSE3.d" }), + b.pathJoin(&.{ phobos_path, "std/internal/math/biguintarm.d" }), + b.pathJoin(&.{ phobos_path, "std/internal/math/biguintcore.d" }), + b.pathJoin(&.{ phobos_path, "std/internal/math/biguintnoasm.d" }), + b.pathJoin(&.{ phobos_path, "std/internal/math/biguintx86.d" }), + b.pathJoin(&.{ phobos_path, "std/internal/math/errorfunction.d" }), + b.pathJoin(&.{ phobos_path, "std/internal/math/gammafunction.d" }), + b.pathJoin(&.{ phobos_path, "std/internal/memory.d" }), + b.pathJoin(&.{ phobos_path, "std/internal/scopebuffer.d" }), + // b.pathJoin(&.{phobos_path, "std/internal/test/dummyrange.d"}), + // b.pathJoin(&.{phobos_path, "std/internal/test/range.d"}), + // b.pathJoin(&.{phobos_path, "std/internal/test/uda.d"}), + b.pathJoin(&.{ phobos_path, "std/internal/unicode_comp.d" }), + b.pathJoin(&.{ phobos_path, "std/internal/unicode_decomp.d" }), + b.pathJoin(&.{ phobos_path, "std/internal/unicode_grapheme.d" }), + b.pathJoin(&.{ phobos_path, "std/internal/unicode_norm.d" }), + b.pathJoin(&.{ phobos_path, "std/internal/unicode_tables.d" }), + b.pathJoin(&.{ phobos_path, "std/internal/windows/advapi32.d" }), + b.pathJoin(&.{ phobos_path, "std/json.d" }), + b.pathJoin(&.{ phobos_path, "std/logger/core.d" }), + b.pathJoin(&.{ phobos_path, "std/logger/filelogger.d" }), + b.pathJoin(&.{ phobos_path, "std/logger/multilogger.d" }), + b.pathJoin(&.{ phobos_path, "std/logger/nulllogger.d" }), + b.pathJoin(&.{ phobos_path, "std/logger/package.d" }), + b.pathJoin(&.{ phobos_path, "std/math/algebraic.d" }), + b.pathJoin(&.{ phobos_path, "std/math/constants.d" }), + b.pathJoin(&.{ phobos_path, "std/math/exponential.d" }), + b.pathJoin(&.{ phobos_path, "std/math/hardware.d" }), + b.pathJoin(&.{ phobos_path, "std/math/operations.d" }), + b.pathJoin(&.{ phobos_path, "std/math/package.d" }), + b.pathJoin(&.{ phobos_path, "std/math/remainder.d" }), + b.pathJoin(&.{ phobos_path, "std/math/rounding.d" }), + b.pathJoin(&.{ phobos_path, "std/math/traits.d" }), + b.pathJoin(&.{ phobos_path, "std/math/trigonometry.d" }), + b.pathJoin(&.{ phobos_path, "std/mathspecial.d" }), + b.pathJoin(&.{ phobos_path, "std/meta.d" }), + b.pathJoin(&.{ phobos_path, "std/mmfile.d" }), + b.pathJoin(&.{ phobos_path, "std/net/curl.d" }), + b.pathJoin(&.{ phobos_path, "std/net/isemail.d" }), + b.pathJoin(&.{ phobos_path, "std/numeric.d" }), + b.pathJoin(&.{ phobos_path, "std/outbuffer.d" }), + b.pathJoin(&.{ phobos_path, "std/package.d" }), + b.pathJoin(&.{ phobos_path, "std/parallelism.d" }), + b.pathJoin(&.{ phobos_path, "std/path.d" }), + b.pathJoin(&.{ phobos_path, "std/process.d" }), + b.pathJoin(&.{ phobos_path, "std/random.d" }), + b.pathJoin(&.{ phobos_path, "std/range/interfaces.d" }), + b.pathJoin(&.{ phobos_path, "std/range/package.d" }), + b.pathJoin(&.{ phobos_path, "std/range/primitives.d" }), + b.pathJoin(&.{ phobos_path, "std/regex/internal/backtracking.d" }), + b.pathJoin(&.{ phobos_path, "std/regex/internal/generator.d" }), + b.pathJoin(&.{ phobos_path, "std/regex/internal/ir.d" }), + b.pathJoin(&.{ phobos_path, "std/regex/internal/kickstart.d" }), + b.pathJoin(&.{ phobos_path, "std/regex/internal/parser.d" }), + // b.pathJoin(&.{phobos_path, "std/regex/internal/tests.d"}), + // b.pathJoin(&.{phobos_path, "std/regex/internal/tests2.d"}), + b.pathJoin(&.{ phobos_path, "std/regex/internal/thompson.d" }), + b.pathJoin(&.{ phobos_path, "std/regex/package.d" }), + b.pathJoin(&.{ phobos_path, "std/signals.d" }), + b.pathJoin(&.{ phobos_path, "std/socket.d" }), + b.pathJoin(&.{ phobos_path, "std/stdint.d" }), + b.pathJoin(&.{ phobos_path, "std/stdio.d" }), + b.pathJoin(&.{ phobos_path, "std/string.d" }), + b.pathJoin(&.{ phobos_path, "std/sumtype.d" }), + b.pathJoin(&.{ phobos_path, "std/system.d" }), + b.pathJoin(&.{ phobos_path, "std/traits.d" }), + b.pathJoin(&.{ phobos_path, "std/typecons.d" }), + b.pathJoin(&.{ phobos_path, "std/typetuple.d" }), + b.pathJoin(&.{ phobos_path, "std/uni/package.d" }), + b.pathJoin(&.{ phobos_path, "std/uri.d" }), + b.pathJoin(&.{ phobos_path, "std/utf.d" }), + b.pathJoin(&.{ phobos_path, "std/uuid.d" }), + b.pathJoin(&.{ phobos_path, "std/variant.d" }), + b.pathJoin(&.{ phobos_path, "std/windows/charset.d" }), + b.pathJoin(&.{ phobos_path, "std/windows/registry.d" }), + b.pathJoin(&.{ phobos_path, "std/windows/syserror.d" }), + b.pathJoin(&.{ phobos_path, "std/zip.d" }), + b.pathJoin(&.{ phobos_path, "std/zlib.d" }), + // b.pathJoin(&.{phobos_path, "test/betterc_module_tests.d"}), + // b.pathJoin(&.{phobos_path, "test/dub_stdx_allocator.d"}), + // b.pathJoin(&.{phobos_path, "test/dub_stdx_checkedint.d"}), + b.pathJoin(&.{ phobos_path, "tools/unicode_table_generator.d" }), + // b.pathJoin(&.{phobos_path, "unittest.d"}), + }, .dflags = &.{ "-w", "-conf=", @@ -144,13 +329,10 @@ fn buildPhobos(b: *std.Build, options: buildOptions) !void { "-lowmem", }, .importPaths = &.{ - "phobos", + phobos_path, "druntime/src", }, - .artifact = buildZlib(b, .{ - .target = options.target, - .optimize = options.optimize, - }), + .artifact = buildZlib(b, options), .use_zigcc = true, .t_options = try zcc.buildOptions(b, options.target), }); @@ -160,6 +342,7 @@ const buildOptions = struct { target: std.Build.ResolvedTarget, optimize: std.builtin.OptimizeMode, linkage: std.builtin.LinkMode = .static, + dependency: ?*std.Build.Dependency = null, }; const runtime_src = &[_][]const u8{ @@ -246,12 +429,11 @@ const runtime_src = &[_][]const u8{ }; fn buildZlib(b: *std.Build, options: buildOptions) *std.Build.Step.Compile { - const phobos_zlib_path = b.pathJoin(&.{ - "phobos", - "etc", - "c", - "zlib", - }); + const phobos_path = if (options.dependency) |dep| + dep.path("").getPath(b) + else + unreachable; + const phobos_zlib_path = b.pathJoin(&.{ phobos_path, "etc", "c", "zlib" }); const libz = b.addStaticLibrary(.{ .name = "z", .target = options.target, @@ -259,9 +441,9 @@ fn buildZlib(b: *std.Build, options: buildOptions) *std.Build.Step.Compile { }); libz.pie = true; - libz.addIncludePath(b.path(phobos_zlib_path)); + libz.addIncludePath(.{ .cwd_relative = phobos_zlib_path }); libz.addCSourceFiles(.{ - .root = b.path(phobos_zlib_path), + .root = .{ .cwd_relative = phobos_zlib_path }, .files = &.{ "adler32.c", "crc32.c", @@ -289,185 +471,3 @@ fn buildZlib(b: *std.Build, options: buildOptions) *std.Build.Step.Compile { libz.linkLibC(); return libz; } - -const std_src = &[_][]const u8{ - "phobos/etc/c/curl.d", - "phobos/etc/c/odbc/sql.d", - "phobos/etc/c/odbc/sqlext.d", - "phobos/etc/c/odbc/sqltypes.d", - "phobos/etc/c/odbc/sqlucode.d", - "phobos/etc/c/sqlite3.d", - "phobos/etc/c/zlib.d", - "phobos/phobos/sys/compiler.d", - "phobos/phobos/sys/meta.d", - "phobos/phobos/sys/system.d", - "phobos/phobos/sys/traits.d", - "phobos/std/algorithm/comparison.d", - "phobos/std/algorithm/internal.d", - "phobos/std/algorithm/iteration.d", - "phobos/std/algorithm/mutation.d", - "phobos/std/algorithm/package.d", - "phobos/std/algorithm/searching.d", - "phobos/std/algorithm/setops.d", - "phobos/std/algorithm/sorting.d", - "phobos/std/array.d", - "phobos/std/ascii.d", - "phobos/std/base64.d", - "phobos/std/bigint.d", - "phobos/std/bitmanip.d", - "phobos/std/checkedint.d", - "phobos/std/compiler.d", - "phobos/std/complex.d", - "phobos/std/concurrency.d", - "phobos/std/container/array.d", - "phobos/std/container/binaryheap.d", - "phobos/std/container/dlist.d", - "phobos/std/container/package.d", - "phobos/std/container/rbtree.d", - "phobos/std/container/slist.d", - "phobos/std/container/util.d", - "phobos/std/conv.d", - "phobos/std/csv.d", - "phobos/std/datetime/date.d", - "phobos/std/datetime/interval.d", - "phobos/std/datetime/package.d", - "phobos/std/datetime/stopwatch.d", - "phobos/std/datetime/systime.d", - "phobos/std/datetime/timezone.d", - "phobos/std/demangle.d", - "phobos/std/digest/crc.d", - "phobos/std/digest/hmac.d", - "phobos/std/digest/md.d", - "phobos/std/digest/murmurhash.d", - "phobos/std/digest/package.d", - "phobos/std/digest/ripemd.d", - "phobos/std/digest/sha.d", - "phobos/std/encoding.d", - "phobos/std/exception.d", - "phobos/std/experimental/allocator/building_blocks/affix_allocator.d", - "phobos/std/experimental/allocator/building_blocks/aligned_block_list.d", - "phobos/std/experimental/allocator/building_blocks/allocator_list.d", - "phobos/std/experimental/allocator/building_blocks/ascending_page_allocator.d", - "phobos/std/experimental/allocator/building_blocks/bitmapped_block.d", - "phobos/std/experimental/allocator/building_blocks/bucketizer.d", - "phobos/std/experimental/allocator/building_blocks/fallback_allocator.d", - "phobos/std/experimental/allocator/building_blocks/free_list.d", - "phobos/std/experimental/allocator/building_blocks/free_tree.d", - "phobos/std/experimental/allocator/building_blocks/kernighan_ritchie.d", - "phobos/std/experimental/allocator/building_blocks/null_allocator.d", - "phobos/std/experimental/allocator/building_blocks/package.d", - "phobos/std/experimental/allocator/building_blocks/quantizer.d", - "phobos/std/experimental/allocator/building_blocks/region.d", - "phobos/std/experimental/allocator/building_blocks/scoped_allocator.d", - "phobos/std/experimental/allocator/building_blocks/segregator.d", - "phobos/std/experimental/allocator/building_blocks/stats_collector.d", - "phobos/std/experimental/allocator/common.d", - "phobos/std/experimental/allocator/gc_allocator.d", - "phobos/std/experimental/allocator/mallocator.d", - "phobos/std/experimental/allocator/mmap_allocator.d", - "phobos/std/experimental/allocator/package.d", - "phobos/std/experimental/allocator/showcase.d", - "phobos/std/experimental/allocator/typed.d", - "phobos/std/experimental/checkedint.d", - "phobos/std/experimental/logger/core.d", - "phobos/std/experimental/logger/filelogger.d", - "phobos/std/experimental/logger/multilogger.d", - "phobos/std/experimental/logger/nulllogger.d", - "phobos/std/experimental/logger/package.d", - "phobos/std/file.d", - "phobos/std/format/internal/floats.d", - "phobos/std/format/internal/read.d", - "phobos/std/format/internal/write.d", - "phobos/std/format/package.d", - "phobos/std/format/read.d", - "phobos/std/format/spec.d", - "phobos/std/format/write.d", - "phobos/std/functional.d", - "phobos/std/getopt.d", - "phobos/std/int128.d", - "phobos/std/internal/attributes.d", - "phobos/std/internal/cstring.d", - "phobos/std/internal/digest/sha_SSSE3.d", - "phobos/std/internal/math/biguintarm.d", - "phobos/std/internal/math/biguintcore.d", - "phobos/std/internal/math/biguintnoasm.d", - "phobos/std/internal/math/biguintx86.d", - "phobos/std/internal/math/errorfunction.d", - "phobos/std/internal/math/gammafunction.d", - "phobos/std/internal/memory.d", - "phobos/std/internal/scopebuffer.d", - // "phobos/std/internal/test/dummyrange.d", - // "phobos/std/internal/test/range.d", - // "phobos/std/internal/test/uda.d", - "phobos/std/internal/unicode_comp.d", - "phobos/std/internal/unicode_decomp.d", - "phobos/std/internal/unicode_grapheme.d", - "phobos/std/internal/unicode_norm.d", - "phobos/std/internal/unicode_tables.d", - "phobos/std/internal/windows/advapi32.d", - "phobos/std/json.d", - "phobos/std/logger/core.d", - "phobos/std/logger/filelogger.d", - "phobos/std/logger/multilogger.d", - "phobos/std/logger/nulllogger.d", - "phobos/std/logger/package.d", - "phobos/std/math/algebraic.d", - "phobos/std/math/constants.d", - "phobos/std/math/exponential.d", - "phobos/std/math/hardware.d", - "phobos/std/math/operations.d", - "phobos/std/math/package.d", - "phobos/std/math/remainder.d", - "phobos/std/math/rounding.d", - "phobos/std/math/traits.d", - "phobos/std/math/trigonometry.d", - "phobos/std/mathspecial.d", - "phobos/std/meta.d", - "phobos/std/mmfile.d", - "phobos/std/net/curl.d", - "phobos/std/net/isemail.d", - "phobos/std/numeric.d", - "phobos/std/outbuffer.d", - "phobos/std/package.d", - "phobos/std/parallelism.d", - "phobos/std/path.d", - "phobos/std/process.d", - "phobos/std/random.d", - "phobos/std/range/interfaces.d", - "phobos/std/range/package.d", - "phobos/std/range/primitives.d", - "phobos/std/regex/internal/backtracking.d", - "phobos/std/regex/internal/generator.d", - "phobos/std/regex/internal/ir.d", - "phobos/std/regex/internal/kickstart.d", - "phobos/std/regex/internal/parser.d", - // "phobos/std/regex/internal/tests.d", - // "phobos/std/regex/internal/tests2.d", - "phobos/std/regex/internal/thompson.d", - "phobos/std/regex/package.d", - "phobos/std/signals.d", - "phobos/std/socket.d", - "phobos/std/stdint.d", - "phobos/std/stdio.d", - "phobos/std/string.d", - "phobos/std/sumtype.d", - "phobos/std/system.d", - "phobos/std/traits.d", - "phobos/std/typecons.d", - "phobos/std/typetuple.d", - "phobos/std/uni/package.d", - "phobos/std/uri.d", - "phobos/std/utf.d", - "phobos/std/uuid.d", - "phobos/std/variant.d", - "phobos/std/windows/charset.d", - "phobos/std/windows/registry.d", - "phobos/std/windows/syserror.d", - "phobos/std/zip.d", - "phobos/std/zlib.d", - // "phobos/test/betterc_module_tests.d", - // "phobos/test/dub_stdx_allocator.d", - // "phobos/test/dub_stdx_checkedint.d", - "phobos/tools/unicode_table_generator.d", - // "phobos/unittest.d", -}; diff --git a/build.zig.zon b/build.zig.zon index d5c8a25..e3c19e6 100644 --- a/build.zig.zon +++ b/build.zig.zon @@ -1,11 +1,15 @@ .{ .name = "druntime", - .version = "1.109.0", + .version = "1.109.1", .dependencies = .{ .abs = .{ .url = "git+https://github.com/kassane/anotherBuildStep#449e179e966ba95b8a2308a9982cde490b57b3d3", .hash = "12209e13ee461f2636956b46c663b17305a990338d02a8a70182043ff3a3a7a11356", }, + .phobos = .{ + .url = "git+https://github.com/ldc-developers/phobos#654241bed8f1b1460762e55412d9a9ea623f3cc7", + .hash = "1220411abc293322e196fd3a832363c5d9a1aa82a68061d343c1d20b406588218a04", + }, }, .paths = .{""}, } diff --git a/druntime/src/importc.h b/druntime/src/importc.h index 07fc854..89140d7 100644 --- a/druntime/src/importc.h +++ b/druntime/src/importc.h @@ -21,9 +21,9 @@ #define __IMPORTC__ 1 /******************** - * Some compilers define `__restrict` instead of `restrict` as C++ compilers - * don't recognize `restrict` as a keyword. ImportC assigns no semantics to - * `restrict`, so just ignore the keyword. + * Some compilers define `__restrict` instead of `restrict` as C++ compilers don't + * recognize `restrict` as a keyword. + * ImportC assigns no semantics to `restrict`, so just ignore the keyword. */ #define __restrict #define __restrict__ @@ -57,7 +57,7 @@ #define __forceinline #undef _Check_return_ -// #define _Check_return_ +//#define _Check_return_ #define __pragma(x) #undef _GLIBCXX_USE_FLOAT128 @@ -94,12 +94,12 @@ typedef unsigned long long __uint64_t; * __extension__ is a GNU C extension. It suppresses warnings * when placed before an expression. */ -#define __extension__ /* ignore it, as ImportC doesn't do warnings */ +#define __extension__ /* ignore it, as ImportC doesn't do warnings */ #define __builtin_isnan(x) isnan(x) #define __builtin_isfinite(x) finite(x) // IN_LLVM: replaced by symbol in __importc_builtins.di -// #define __builtin_alloca(x) alloca(x) +//#define __builtin_alloca(x) alloca(x) /******************************** * __has_extension is a clang thing: @@ -116,24 +116,20 @@ typedef unsigned long long __uint64_t; * OS-specific macros */ #if __APPLE__ -#define __builtin___memmove_chk(dest, src, len, x) \ - memmove(dest, src, len) // put it back to memmove() -#define __builtin___memcpy_chk(dest, src, len, x) memcpy(dest, src, len) -#define __builtin___memset_chk(dest, val, len, x) memset(dest, val, len) -#define __builtin___stpcpy_chk(dest, src, x) stpcpy(dest, src) -#define __builtin___stpncpy_chk(dest, src, len, x) stpncpy(dest, src, len) -#define __builtin___strcat_chk(dest, src, x) strcat(dest, src) -#define __builtin___strcpy_chk(dest, src, x) strcpy(dest, src) -#define __builtin___strncat_chk(dest, src, len, x) strncat(dest, src, len) -#define __builtin___strncpy_chk(dest, src, len, x) strncpy(dest, src, len) -#define __builtin___sprintf_chk(s, flag, os, fmt, ...) \ - sprintf(s, fmt, __VA_ARGS__) -#define __builtin___snprintf_chk(s, c, flag, os, fmt, ...) \ - snprintf(s, c, fmt, __VA_ARGS__) -#define __builtin___vsnprintf_chk(s, c, flag, os, fmt, ...) \ - vsnprintf(s, c, fmt, __VA_ARGS__) -#define __builtin___strlcat_chk(dest, src, x, n) strlcat(dest, src, x) -#define __builtin___strlcpy_chk(dest, src, x, n) strlcpy(dest, src, x) +#define __builtin___memmove_chk(dest, src, len, x) memmove(dest,src,len) // put it back to memmove() +#define __builtin___memcpy_chk(dest, src, len, x) memcpy(dest,src,len) +#define __builtin___memset_chk(dest, val, len, x) memset(dest,val,len) +#define __builtin___stpcpy_chk(dest, src, x) stpcpy(dest,src) +#define __builtin___stpncpy_chk(dest, src, len, x) stpncpy(dest,src,len) +#define __builtin___strcat_chk(dest, src, x) strcat(dest,src) +#define __builtin___strcpy_chk(dest, src, x) strcpy(dest,src) +#define __builtin___strncat_chk(dest, src, len, x) strncat(dest,src,len) +#define __builtin___strncpy_chk(dest, src, len, x) strncpy(dest,src,len) +#define __builtin___sprintf_chk(s, flag, os, fmt, ...) sprintf(s, fmt, __VA_ARGS__) +#define __builtin___snprintf_chk(s, c, flag, os, fmt, ...) snprintf(s, c, fmt, __VA_ARGS__) +#define __builtin___vsnprintf_chk(s, c, flag, os, fmt, ...) vsnprintf(s, c, fmt, __VA_ARGS__) +#define __builtin___strlcat_chk(dest, src, x, n) strlcat(dest,src,x) +#define __builtin___strlcpy_chk(dest, src, x, n) strlcpy(dest,src,x) #define __builtin_object_size #define __signed signed #endif @@ -156,24 +152,23 @@ typedef unsigned long long __uint64_t; // This header disables the Windows API Annotations macros // Need to include sal.h to get the pragma once to prevent macro redefinition. -#include "no_sal2.h" #include "sal.h" +#include "no_sal2.h" #endif /**************************** * Define it to do what other C compilers do. */ -#define __builtin_offsetof(t, i) \ - ((typeof(sizeof(0)))((char *)&((t *)0)->i - (char *)0)) +#define __builtin_offsetof(t,i) ((typeof(sizeof(0)))((char *)&((t *)0)->i - (char *)0)) -#define __builtin_bit_cast(t, e) (*(t *)(void *)&(e)) +#define __builtin_bit_cast(t,e) (*(t*)(void*)&(e)) /*************************** * C11 6.10.8.3 Conditional feature macros */ #define __STDC_NO_VLA__ 1 -#if linux // Microsoft won't allow the following macro +#if linux // Microsoft won't allow the following macro // Ubuntu's assert.h uses this #define __PRETTY_FUNCTION__ __func__ diff --git a/druntime/src/rt/sections_ldc.d b/druntime/src/rt/sections_ldc.d index abeffad..8d42a10 100644 --- a/druntime/src/rt/sections_ldc.d +++ b/druntime/src/rt/sections_ldc.d @@ -34,7 +34,6 @@ else version (OpenBSD) {} else version (Windows) {} else version (LDC): -import core.stdc.stdlib : alloca; import rt.minfo; debug(PRINTF) import core.stdc.stdio : printf; diff --git a/phobos/README.md b/phobos/README.md deleted file mode 100644 index c953678..0000000 --- a/phobos/README.md +++ /dev/null @@ -1,37 +0,0 @@ -![D Logo](http://dlang.org/images/dlogo.png) Phobos Standard Library -=================================================================== - -[![GitHub tag](https://img.shields.io/github/tag/dlang/phobos.svg?maxAge=86400)](https://github.com/dlang/phobos/releases) -[![Bugzilla Issues](https://img.shields.io/badge/issues-Bugzilla-green.svg)](https://issues.dlang.org/buglist.cgi?component=phobos&list_id=220147&product=D&resolution=---) -[![CircleCi](https://img.shields.io/circleci/project/dlang/phobos/master.svg?maxAge=86400)](https://circleci.com/gh/dlang/phobos) -[![Buildkite](https://badge.buildkite.com/01509e75200b3e9576028fe52c4546d6a6a9ad81aaf31a8790.svg?branch=master)](https://buildkite.com/dlang/phobos) -[![Code coverage](https://img.shields.io/codecov/c/github/dlang/phobos.svg?maxAge=86400)](https://codecov.io/gh/dlang/phobos) -[![license](https://img.shields.io/github/license/dlang/phobos.svg)](https://github.com/dlang/phobos/blob/master/LICENSE_1_0.txt) - -Phobos is the standard library that comes with the -[D Programming Language](http://dlang.org) Compiler. - - -* [Bugzilla bug tracker](http://d.puremagic.com/issues/) -* [Forum](http://forum.dlang.org/) -* [API Documentation](http://dlang.org/phobos/) -* [Wiki](http://wiki.dlang.org/) - -Download --------- - -Phobos is packaged together with the compiler. -You should -[download the whole precompiled package](http://dlang.org/download.html). - -To [build everything yourself](http://wiki.dlang.org/Building_DMD), -there is a [description in the wiki](http://wiki.dlang.org/Building_DMD). - -Phobos is distributed under Boost Software Licence. -See the [licence file](LICENSE_1_0.txt). - -I Want to Contribute --------------------- - -Great! -See the [CONTRIBUTING.md file](CONTRIBUTING.md). diff --git a/phobos/changelog/README.md b/phobos/changelog/README.md deleted file mode 100644 index b79acf7..0000000 --- a/phobos/changelog/README.md +++ /dev/null @@ -1,42 +0,0 @@ -This directory will get copied to dlang.org and cleared when master gets -merged into stable prior to a new release. - -How to add a new changelog entry to the pending changelog? -========================================================== - -Create a new file in the `changelog` folder. It should end with `.dd` and look -similar to a git commit message. The first line represents the title of the change. -After an empty line follows the long description: - -``` -My fancy title of the new feature - -A long description of the new feature in `std.range`. -It can be followed by an example: -------- -import std.range : padLeft, padRight; -import std.algorithm.comparison : equal; - -assert([1, 2, 3, 4, 5].padLeft(0, 7).equal([0, 0, 1, 2, 3, 4, 5])); - -assert("Hello World!".padRight('!', 15).equal("Hello World!!!!")); -------- -and links to the documentation, e.g. $(REF drop, std, range) or -$(REF_ALTTEXT a custom name for the function, drop, std, range). - -Links to the spec can look like this $(LINK2 $(ROOT_DIR)spec/module.html, this) -and of course you can link to other $(LINK2 https://forum.dlang.org/, external resources). -``` - -The title can't contain links (it's already one). -For more infos, see the [Ddoc spec](https://dlang.org/spec/ddoc.html). - -Preview changes ---------------- - -If you have cloned the [tools](https://github.com/dlang/tools) and [dlang.org](https://github.com/dlang/dlang.org) repo), -you can preview the changelog with: - -``` -make -C ../dlang.org -f posix.mak pending_changelog -``` diff --git a/phobos/changelog/std.process.Config.preExecDelegate.dd b/phobos/changelog/std.process.Config.preExecDelegate.dd deleted file mode 100644 index 393d26c..0000000 --- a/phobos/changelog/std.process.Config.preExecDelegate.dd +++ /dev/null @@ -1,25 +0,0 @@ -Add `std.process.Config.preExecDelegate` - -$(LINK2 $(ROOT_DIR)phobos/std_process.html#.Config.preExecDelegate, `std.process.Config.preExecDelegate`) -is just like -$(LINK2 $(ROOT_DIR)phobos/std_process.html#.Config.preExecFunction, `std.process.Config.preExecFunction`), -but can capture an environment, for example: - -------- -import core.sys.linux.sys.prctl : PR_SET_PDEATHSIG, prctl; -import std.process : Config, execute; - -void runProgram(int pdeathsig) -{ - execute( - ["program"], - config: Config( - preExecDelegate: () @trusted => - prctl(PR_SET_PDEATHSIG, pdeathsig, 0, 0, 0) != -1, - ), - ); -} -------- - -`preExecFunction` is retained for backwards compatibility. If both -`preExecFunction` and `preExecDelegate` are given, both are called. diff --git a/phobos/etc/c/curl.d b/phobos/etc/c/curl.d deleted file mode 100644 index e6a1043..0000000 --- a/phobos/etc/c/curl.d +++ /dev/null @@ -1,2343 +0,0 @@ -/** - This is an interface to the libcurl library. - - Converted to D from curl headers by $(LINK2 http://www.digitalmars.com/d/2.0/htod.html, htod) and - cleaned up by Jonas Drewsen (jdrewsen) - - Windows x86 note: - A DMD compatible libcurl static library can be downloaded from the dlang.org - $(LINK2 http://dlang.org/download.html, download page). -*/ - -/* ************************************************************************** - * _ _ ____ _ - * Project ___| | | | _ \| | - * / __| | | | |_) | | - * | (__| |_| | _ <| |___ - * \___|\___/|_| \_\_____| - */ - -/** - * Copyright (C) 1998 - 2010, Daniel Stenberg, <daniel@haxx.se>, et al. - * - * This software is licensed as described in the file COPYING, which - * you should have received as part of this distribution. The terms - * are also available at $(LINK http://curl.haxx.se/docs/copyright.html). - * - * You may opt to use, copy, modify, merge, publish, distribute and/or sell - * copies of the Software, and permit persons to whom the Software is - * furnished to do so, under the terms of the COPYING file. - * - * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY - * KIND, either express or implied. - * - ***************************************************************************/ - -module etc.c.curl; - -import core.stdc.config; -import core.stdc.time; -import std.socket; - -// linux -import core.sys.posix.sys.socket; - -// -// LICENSE FROM CURL HEADERS -// - -/** This is the global package copyright */ -enum LIBCURL_COPYRIGHT = "1996 - 2010 Daniel Stenberg, ."; - -/** This is the version number of the libcurl package from which this header - file origins: */ -enum LIBCURL_VERSION = "7.21.4"; - -/** The numeric version number is also available "in parts" by using these - constants */ -enum LIBCURL_VERSION_MAJOR = 7; -/// ditto -enum LIBCURL_VERSION_MINOR = 21; -/// ditto -enum LIBCURL_VERSION_PATCH = 4; - -/** This is the numeric version of the libcurl version number, meant for easier - parsing and comparions by programs. The LIBCURL_VERSION_NUM define will - always follow this syntax: - - 0xXXYYZZ - - Where XX, YY and ZZ are the main version, release and patch numbers in - hexadecimal (using 8 bits each). All three numbers are always represented - using two digits. 1.2 would appear as "0x010200" while version 9.11.7 - appears as "0x090b07". - - This 6-digit (24 bits) hexadecimal number does not show pre-release number, - and it is always a greater number in a more recent release. It makes - comparisons with greater than and less than work. -*/ - -enum LIBCURL_VERSION_NUM = 0x071504; - -/** - * This is the date and time when the full source package was created. The - * timestamp is not stored in git, as the timestamp is properly set in the - * tarballs by the maketgz script. - * - * The format of the date should follow this template: - * - * "Mon Feb 12 11:35:33 UTC 2007" - */ -enum LIBCURL_TIMESTAMP = "Thu Feb 17 12:19:40 UTC 2011"; - -/** Data type definition of curl_off_t. - * - * jdrewsen - Always 64bit signed and that is what long is in D. - * - * Comment below is from curlbuild.h: - * - * NOTE 2: - * - * For any given platform/compiler curl_off_t must be typedef'ed to a - * 64-bit wide signed integral data type. The width of this data type - * must remain constant and independent of any possible large file - * support settings. - * - * As an exception to the above, curl_off_t shall be typedef'ed to a - * 32-bit wide signed integral data type if there is no 64-bit type. - */ -alias curl_off_t = long; - -/// -alias CURL = void; - -/// jdrewsen - Get socket alias from std.socket -alias curl_socket_t = socket_t; - -/// jdrewsen - Would like to get socket error constant from std.socket by it is private atm. -version (Windows) -{ - import core.sys.windows.windows, core.sys.windows.winsock2; - enum CURL_SOCKET_BAD = SOCKET_ERROR; -} -version (Posix) enum CURL_SOCKET_BAD = -1; - -/// -extern (C) struct curl_httppost -{ - curl_httppost *next; /** next entry in the list */ - char *name; /** pointer to allocated name */ - c_long namelength; /** length of name length */ - char *contents; /** pointer to allocated data contents */ - c_long contentslength; /** length of contents field */ - char *buffer; /** pointer to allocated buffer contents */ - c_long bufferlength; /** length of buffer field */ - char *contenttype; /** Content-Type */ - curl_slist *contentheader; /** list of extra headers for this form */ - curl_httppost *more; /** if one field name has more than one - file, this link should link to following - files */ - c_long flags; /** as defined below */ - char *showfilename; /** The file name to show. If not set, the - actual file name will be used (if this - is a file part) */ - void *userp; /** custom pointer used for - HTTPPOST_CALLBACK posts */ -} - -enum HTTPPOST_FILENAME = 1; /** specified content is a file name */ -enum HTTPPOST_READFILE = 2; /** specified content is a file name */ -enum HTTPPOST_PTRNAME = 4; /** name is only stored pointer - do not free in formfree */ -enum HTTPPOST_PTRCONTENTS = 8; /** contents is only stored pointer - do not free in formfree */ -enum HTTPPOST_BUFFER = 16; /** upload file from buffer */ -enum HTTPPOST_PTRBUFFER = 32; /** upload file from pointer contents */ -enum HTTPPOST_CALLBACK = 64; /** upload file contents by using the - regular read callback to get the data - and pass the given pointer as custom - pointer */ - -/// -alias curl_progress_callback = int function(void *clientp, double dltotal, double dlnow, double ultotal, double ulnow); - -/** Tests have proven that 20K is a very bad buffer size for uploads on - Windows, while 16K for some odd reason performed a lot better. - We do the ifndef check to allow this value to easier be changed at build - time for those who feel adventurous. The practical minimum is about - 400 bytes since libcurl uses a buffer of this size as a scratch area - (unrelated to network send operations). */ -enum CURL_MAX_WRITE_SIZE = 16_384; - -/** The only reason to have a max limit for this is to avoid the risk of a bad - server feeding libcurl with a never-ending header that will cause reallocs - infinitely */ -enum CURL_MAX_HTTP_HEADER = (100*1024); - - -/** This is a magic return code for the write callback that, when returned, - will signal libcurl to pause receiving on the current transfer. */ -enum CURL_WRITEFUNC_PAUSE = 0x10000001; - -/// -alias curl_write_callback = size_t function(char *buffer, size_t size, size_t nitems, void *outstream); - -/** enumeration of file types */ -enum CurlFileType { - file, /// - directory, /// - symlink, /// - device_block, /// - device_char, /// - namedpipe, /// - socket, /// - door, /// - unknown /** is possible only on Sun Solaris now */ -} - -/// -alias curlfiletype = int; - -/// -enum CurlFInfoFlagKnown { - filename = 1, /// - filetype = 2, /// - time = 4, /// - perm = 8, /// - uid = 16, /// - gid = 32, /// - size = 64, /// - hlinkcount = 128 /// -} - -/** Content of this structure depends on information which is known and is - achievable (e.g. by FTP LIST parsing). Please see the url_easy_setopt(3) man - page for callbacks returning this structure -- some fields are mandatory, - some others are optional. The FLAG field has special meaning. */ - - -/** If some of these fields is not NULL, it is a pointer to b_data. */ -extern (C) struct _N2 -{ - char *time; /// - char *perm; /// - char *user; /// - char *group; /// - char *target; /** pointer to the target filename of a symlink */ -} - -/** Content of this structure depends on information which is known and is - achievable (e.g. by FTP LIST parsing). Please see the url_easy_setopt(3) man - page for callbacks returning this structure -- some fields are mandatory, - some others are optional. The FLAG field has special meaning. */ -extern (C) struct curl_fileinfo -{ - char *filename; /// - curlfiletype filetype; /// - time_t time; /// - uint perm; /// - int uid; /// - int gid; /// - curl_off_t size; /// - c_long hardlinks; /// - _N2 strings; /// - uint flags; /// - char *b_data; /// - size_t b_size; /// - size_t b_used; /// -} - -/** return codes for CURLOPT_CHUNK_BGN_FUNCTION */ -enum CurlChunkBgnFunc { - ok = 0, /// - fail = 1, /** tell the lib to end the task */ - skip = 2 /** skip this chunk over */ -} - -/** if splitting of data transfer is enabled, this callback is called before - download of an individual chunk started. Note that parameter "remains" works - only for FTP wildcard downloading (for now), otherwise is not used */ -alias curl_chunk_bgn_callback = c_long function(const(void) *transfer_info, void *ptr, int remains); - -/** return codes for CURLOPT_CHUNK_END_FUNCTION */ -enum CurlChunkEndFunc { - ok = 0, /// - fail = 1, /// -} -/** If splitting of data transfer is enabled this callback is called after - download of an individual chunk finished. - Note! After this callback was set then it have to be called FOR ALL chunks. - Even if downloading of this chunk was skipped in CHUNK_BGN_FUNC. - This is the reason why we don't need "transfer_info" parameter in this - callback and we are not interested in "remains" parameter too. */ -alias curl_chunk_end_callback = c_long function(void *ptr); - -/** return codes for FNMATCHFUNCTION */ -enum CurlFnMAtchFunc { - match = 0, /// - nomatch = 1, /// - fail = 2 /// -} - -/** callback type for wildcard downloading pattern matching. If the - string matches the pattern, return CURL_FNMATCHFUNC_MATCH value, etc. */ -alias curl_fnmatch_callback = int function(void *ptr, in const(char) *pattern, in const(char) *string); - -/// seek whence... -enum CurlSeekPos { - set, /// - current, /// - end /// -} - -/** These are the return codes for the seek callbacks */ -enum CurlSeek { - ok, /// - fail, /** fail the entire transfer */ - cantseek /** tell libcurl seeking can't be done, so - libcurl might try other means instead */ -} - -/// -alias curl_seek_callback = int function(void *instream, curl_off_t offset, int origin); - -/// -enum CurlReadFunc { - /** This is a return code for the read callback that, when returned, will - signal libcurl to immediately abort the current transfer. */ - abort = 0x10000000, - - /** This is a return code for the read callback that, when returned, - will const signal libcurl to pause sending data on the current - transfer. */ - pause = 0x10000001 -} - -/// -alias curl_read_callback = size_t function(char *buffer, size_t size, size_t nitems, void *instream); - -/// -enum CurlSockType { - ipcxn, /** socket created for a specific IP connection */ - last /** never use */ -} -/// -alias curlsocktype = int; - -/// -alias curl_sockopt_callback = int function(void *clientp, curl_socket_t curlfd, curlsocktype purpose); - -/** addrlen was a socklen_t type before 7.18.0 but it turned really - ugly and painful on the systems that lack this type */ -extern (C) struct curl_sockaddr -{ - int family; /// - int socktype; /// - int protocol; /// - uint addrlen; /** addrlen was a socklen_t type before 7.18.0 but it - turned really ugly and painful on the systems that - lack this type */ - sockaddr addr; /// -} - -/// -alias curl_opensocket_callback = curl_socket_t function(void *clientp, curlsocktype purpose, curl_sockaddr *address); - -/// -enum CurlIoError -{ - ok, /** I/O operation successful */ - unknowncmd, /** command was unknown to callback */ - failrestart, /** failed to restart the read */ - last /** never use */ -} -/// -alias curlioerr = int; - -/// -enum CurlIoCmd { - nop, /** command was unknown to callback */ - restartread, /** failed to restart the read */ - last, /** never use */ -} -/// -alias curliocmd = int; - -/// -alias curl_ioctl_callback = curlioerr function(CURL *handle, int cmd, void *clientp); - -/** - * The following typedef's are signatures of malloc, free, realloc, strdup and - * calloc respectively. Function pointers of these types can be passed to the - * curl_global_init_mem() function to set user defined memory management - * callback routines. - */ -alias curl_malloc_callback = void* function(size_t size); -/// ditto -alias curl_free_callback = void function(void *ptr); -/// ditto -alias curl_realloc_callback = void* function(void *ptr, size_t size); -/// ditto -alias curl_strdup_callback = char * function(in const(char) *str); -/// ditto -alias curl_calloc_callback = void* function(size_t nmemb, size_t size); - -/** the kind of data that is passed to information_callback*/ -enum CurlCallbackInfo { - text, /// - header_in, /// - header_out, /// - data_in, /// - data_out, /// - ssl_data_in, /// - ssl_data_out, /// - end /// -} -/// -alias curl_infotype = int; - -/// -alias curl_debug_callback = - int function(CURL *handle, /** the handle/transfer this concerns */ - curl_infotype type, /** what kind of data */ - char *data, /** points to the data */ - size_t size, /** size of the data pointed to */ - void *userptr /** whatever the user please */ - ); - -/** All possible error codes from all sorts of curl functions. Future versions - may return other values, stay prepared. - - Always add new return codes last. Never *EVER* remove any. The return - codes must remain the same! - */ -enum CurlError -{ - ok, /// - unsupported_protocol, /** 1 */ - failed_init, /** 2 */ - url_malformat, /** 3 */ - not_built_in, /** 4 - [was obsoleted in August 2007 for - 7.17.0, reused in April 2011 for 7.21.5] */ - couldnt_resolve_proxy, /** 5 */ - couldnt_resolve_host, /** 6 */ - couldnt_connect, /** 7 */ - ftp_weird_server_reply, /** 8 */ - remote_access_denied, /** 9 a service was denied by the server - due to lack of access - when login fails - this is not returned. */ - obsolete10, /** 10 - NOT USED */ - ftp_weird_pass_reply, /** 11 */ - obsolete12, /** 12 - NOT USED */ - ftp_weird_pasv_reply, /** 13 */ - ftp_weird_227_format, /** 14 */ - ftp_cant_get_host, /** 15 */ - obsolete16, /** 16 - NOT USED */ - ftp_couldnt_set_type, /** 17 */ - partial_file, /** 18 */ - ftp_couldnt_retr_file, /** 19 */ - obsolete20, /** 20 - NOT USED */ - quote_error, /** 21 - quote command failure */ - http_returned_error, /** 22 */ - write_error, /** 23 */ - obsolete24, /** 24 - NOT USED */ - upload_failed, /** 25 - failed upload "command" */ - read_error, /** 26 - couldn't open/read from file */ - out_of_memory, /** 27 */ - /** Note: CURLE_OUT_OF_MEMORY may sometimes indicate a conversion error - instead of a memory allocation error if CURL_DOES_CONVERSIONS - is defined - */ - operation_timedout, /** 28 - the timeout time was reached */ - obsolete29, /** 29 - NOT USED */ - ftp_port_failed, /** 30 - FTP PORT operation failed */ - ftp_couldnt_use_rest, /** 31 - the REST command failed */ - obsolete32, /** 32 - NOT USED */ - range_error, /** 33 - RANGE "command" didn't work */ - http_post_error, /** 34 */ - ssl_connect_error, /** 35 - wrong when connecting with SSL */ - bad_download_resume, /** 36 - couldn't resume download */ - file_couldnt_read_file, /** 37 */ - ldap_cannot_bind, /** 38 */ - ldap_search_failed, /** 39 */ - obsolete40, /** 40 - NOT USED */ - function_not_found, /** 41 */ - aborted_by_callback, /** 42 */ - bad_function_argument, /** 43 */ - obsolete44, /** 44 - NOT USED */ - interface_failed, /** 45 - CURLOPT_INTERFACE failed */ - obsolete46, /** 46 - NOT USED */ - too_many_redirects, /** 47 - catch endless re-direct loops */ - unknown_option, /** 48 - User specified an unknown option */ - telnet_option_syntax, /** 49 - Malformed telnet option */ - obsolete50, /** 50 - NOT USED */ - peer_failed_verification, /** 51 - peer's certificate or fingerprint - wasn't verified fine */ - got_nothing, /** 52 - when this is a specific error */ - ssl_engine_notfound, /** 53 - SSL crypto engine not found */ - ssl_engine_setfailed, /** 54 - can not set SSL crypto engine as default */ - send_error, /** 55 - failed sending network data */ - recv_error, /** 56 - failure in receiving network data */ - obsolete57, /** 57 - NOT IN USE */ - ssl_certproblem, /** 58 - problem with the local certificate */ - ssl_cipher, /** 59 - couldn't use specified cipher */ - ssl_cacert, /** 60 - problem with the CA cert (path?) */ - bad_content_encoding, /** 61 - Unrecognized transfer encoding */ - ldap_invalid_url, /** 62 - Invalid LDAP URL */ - filesize_exceeded, /** 63 - Maximum file size exceeded */ - use_ssl_failed, /** 64 - Requested FTP SSL level failed */ - send_fail_rewind, /** 65 - Sending the data requires a rewind that failed */ - ssl_engine_initfailed, /** 66 - failed to initialise ENGINE */ - login_denied, /** 67 - user, password or similar was not accepted and we failed to login */ - tftp_notfound, /** 68 - file not found on server */ - tftp_perm, /** 69 - permission problem on server */ - remote_disk_full, /** 70 - out of disk space on server */ - tftp_illegal, /** 71 - Illegal TFTP operation */ - tftp_unknownid, /** 72 - Unknown transfer ID */ - remote_file_exists, /** 73 - File already exists */ - tftp_nosuchuser, /** 74 - No such user */ - conv_failed, /** 75 - conversion failed */ - conv_reqd, /** 76 - caller must register conversion - callbacks using curl_easy_setopt options - CURLOPT_CONV_FROM_NETWORK_FUNCTION, - CURLOPT_CONV_TO_NETWORK_FUNCTION, and - CURLOPT_CONV_FROM_UTF8_FUNCTION */ - ssl_cacert_badfile, /** 77 - could not load CACERT file, missing or wrong format */ - remote_file_not_found, /** 78 - remote file not found */ - ssh, /** 79 - error from the SSH layer, somewhat - generic so the error message will be of - interest when this has happened */ - ssl_shutdown_failed, /** 80 - Failed to shut down the SSL connection */ - again, /** 81 - socket is not ready for send/recv, - wait till it's ready and try again (Added - in 7.18.2) */ - ssl_crl_badfile, /** 82 - could not load CRL file, missing or wrong format (Added in 7.19.0) */ - ssl_issuer_error, /** 83 - Issuer check failed. (Added in 7.19.0) */ - ftp_pret_failed, /** 84 - a PRET command failed */ - rtsp_cseq_error, /** 85 - mismatch of RTSP CSeq numbers */ - rtsp_session_error, /** 86 - mismatch of RTSP Session Identifiers */ - ftp_bad_file_list, /** 87 - unable to parse FTP file list */ - chunk_failed, /** 88 - chunk callback reported error */ - curl_last /** never use! */ -} -/// -alias CURLcode = int; - -/** This prototype applies to all conversion callbacks */ -alias curl_conv_callback = CURLcode function(char *buffer, size_t length); - -/** actually an OpenSSL SSL_CTX */ -alias curl_ssl_ctx_callback = - CURLcode function(CURL *curl, /** easy handle */ - void *ssl_ctx, /** actually an OpenSSL SSL_CTX */ - void *userptr - ); - -/// -enum CurlProxy { - http, /** added in 7.10, new in 7.19.4 default is to use CONNECT HTTP/1.1 */ - http_1_0, /** added in 7.19.4, force to use CONNECT HTTP/1.0 */ - socks4 = 4, /** support added in 7.15.2, enum existed already in 7.10 */ - socks5 = 5, /** added in 7.10 */ - socks4a = 6, /** added in 7.18.0 */ - socks5_hostname =7 /** Use the SOCKS5 protocol but pass along the - host name rather than the IP address. added - in 7.18.0 */ -} -/// -alias curl_proxytype = int; - -/// -enum CurlAuth : ulong { - none = 0UL, /** None */ - basic = 1UL << 0, /** Basic (default) */ - digest = 1UL << 1, /** Digest */ - negotiate = 1UL << 2, /** Negotiate (SPNEGO) */ - gssnegotiate = negotiate, /** GSS-Negotiate */ - gssapi = negotiate, /** GSS-Negoatiate */ - ntlm = 1UL << 3, /** NTLM */ - digest_ie = 1UL << 4, /** Digest with IE flavour */ - ntlm_WB = 1UL << 5, /** NTML delegated to winbind helper */ - bearer = 1UL << 6, /** Bearer token authentication */ - only = 1UL << 31, /** used together with a single other - type to force no auth or just that - single type */ - any = ~digest_ie, /** any allows */ - anysafe = ~(basic | digest_ie) /** any except basic */ -} - -/// -enum CurlSshAuth { - any = ~0, /** all types supported by the server */ - none = 0, /** none allowed, silly but complete */ - publickey = 1 << 0, /** public/private key files */ - password = 1 << 1, /** password */ - host = 1 << 2, /** host key files */ - keyboard = 1 << 3, /** keyboard interactive */ - agent = 1 << 4, /** agent (ssh-agent, pageant...) */ - gssapi = 1 << 5, /** gssapi (kerberos, ...) */ - - default_ = any // CURLSSH_AUTH_ANY; -} -/// -enum CURL_ERROR_SIZE = 256; -/** points to a zero-terminated string encoded with base64 - if len is zero, otherwise to the "raw" data */ -enum CurlKHType -{ - unknown, /// - rsa1, /// - rsa, /// - dss /// -} -/// -extern (C) struct curl_khkey -{ - const(char) *key; /** points to a zero-terminated string encoded with base64 - if len is zero, otherwise to the "raw" data */ - size_t len; /// - CurlKHType keytype; /// -} - -/** this is the set of return values expected from the curl_sshkeycallback - callback */ -enum CurlKHStat { - fine_add_to_file, /// - fine, /// - reject, /** reject the connection, return an error */ - defer, /** do not accept it, but we can't answer right now so - this causes a CURLE_DEFER error but otherwise the - connection will be left intact etc */ - last /** not for use, only a marker for last-in-list */ -} - -/** this is the set of status codes pass in to the callback */ -enum CurlKHMatch { - ok, /** match */ - mismatch, /** host found, key mismatch! */ - missing, /** no matching host/key found */ - last /** not for use, only a marker for last-in-list */ -} - -/// -alias curl_sshkeycallback = - int function(CURL *easy, /** easy handle */ - const(curl_khkey) *knownkey, /** known */ - const(curl_khkey) *foundkey, /** found */ - CurlKHMatch m, /** libcurl's view on the keys */ - void *clientp /** custom pointer passed from app */ - ); - -/** parameter for the CURLOPT_USE_SSL option */ -enum CurlUseSSL { - none, /** do not attempt to use SSL */ - tryssl, /** try using SSL, proceed anyway otherwise */ - control, /** SSL for the control connection or fail */ - all, /** SSL for all communication or fail */ - last /** not an option, never use */ -} -/// -alias curl_usessl = int; - -/** parameter for the CURLOPT_FTP_SSL_CCC option */ -enum CurlFtpSSL { - ccc_none, /** do not send CCC */ - ccc_passive, /** Let the server initiate the shutdown */ - ccc_active, /** Initiate the shutdown */ - ccc_last /** not an option, never use */ -} -/// -alias curl_ftpccc = int; - -/** parameter for the CURLOPT_FTPSSLAUTH option */ -enum CurlFtpAuth { - defaultauth, /** let libcurl decide */ - ssl, /** use "AUTH SSL" */ - tls, /** use "AUTH TLS" */ - last /** not an option, never use */ -} -/// -alias curl_ftpauth = int; - -/** parameter for the CURLOPT_FTP_CREATE_MISSING_DIRS option */ -enum CurlFtp { - create_dir_none, /** do NOT create missing dirs! */ - create_dir, /** (FTP/SFTP) if CWD fails, try MKD and then CWD again if MKD - succeeded, for SFTP this does similar magic */ - create_dir_retry, /** (FTP only) if CWD fails, try MKD and then CWD again even if MKD - failed! */ - create_dir_last /** not an option, never use */ -} -/// -alias curl_ftpcreatedir = int; - -/** parameter for the CURLOPT_FTP_FILEMETHOD option */ -enum CurlFtpMethod { - defaultmethod, /** let libcurl pick */ - multicwd, /** single CWD operation for each path part */ - nocwd, /** no CWD at all */ - singlecwd, /** one CWD to full dir, then work on file */ - last /** not an option, never use */ -} -/// -alias curl_ftpmethod = int; - -/** CURLPROTO_ defines are for the CURLOPT_*PROTOCOLS options */ -enum CurlProto { - http = 1, /// - https = 2, /// - ftp = 4, /// - ftps = 8, /// - scp = 16, /// - sftp = 32, /// - telnet = 64, /// - ldap = 128, /// - ldaps = 256, /// - dict = 512, /// - file = 1024, /// - tftp = 2048, /// - imap = 4096, /// - imaps = 8192, /// - pop3 = 16_384, /// - pop3s = 32_768, /// - smtp = 65_536, /// - smtps = 131_072, /// - rtsp = 262_144, /// - rtmp = 524_288, /// - rtmpt = 1_048_576, /// - rtmpe = 2_097_152, /// - rtmpte = 4_194_304, /// - rtmps = 8_388_608, /// - rtmpts = 16_777_216, /// - gopher = 33_554_432, /// - all = -1 /** enable everything */ -} - -/** long may be 32 or 64 bits, but we should never depend on anything else - but 32 */ -enum CURLOPTTYPE_LONG = 0; -/// ditto -enum CURLOPTTYPE_OBJECTPOINT = 10_000; -/// ditto -enum CURLOPTTYPE_FUNCTIONPOINT = 20_000; - -/// ditto -enum CURLOPTTYPE_OFF_T = 30_000; -/** name is uppercase CURLOPT_$(LT)name$(GT), - type is one of the defined CURLOPTTYPE_$(LT)type$(GT) - number is unique identifier */ - -/** The macro "##" is ISO C, we assume pre-ISO C doesn't support it. */ -alias LONG = CURLOPTTYPE_LONG; -/// ditto -alias OBJECTPOINT = CURLOPTTYPE_OBJECTPOINT; -/// ditto -alias FUNCTIONPOINT = CURLOPTTYPE_FUNCTIONPOINT; - -/// ditto -alias OFF_T = CURLOPTTYPE_OFF_T; - -/// -enum CurlOption { - /** This is the FILE * or void * the regular output should be written to. */ - file = 10_001, - /** The full URL to get/put */ - url, - /** Port number to connect to, if other than default. */ - port = 3, - /** Name of proxy to use. */ - proxy = 10_004, - /** "name:password" to use when fetching. */ - userpwd, - /** "name:password" to use with proxy. */ - proxyuserpwd, - /** Range to get, specified as an ASCII string. */ - range, - /** not used */ - - /** Specified file stream to upload from (use as input): */ - infile = 10_009, - /** Buffer to receive error messages in, must be at least CURL_ERROR_SIZE - * bytes big. If this is not used, error messages go to stderr instead: */ - errorbuffer, - /** Function that will be called to store the output (instead of fwrite). The - * parameters will use fwrite() syntax, make sure to follow them. */ - writefunction = 20_011, - /** Function that will be called to read the input (instead of fread). The - * parameters will use fread() syntax, make sure to follow them. */ - readfunction, - /** Time-out the read operation after this amount of seconds */ - timeout = 13, - /** If the CURLOPT_INFILE is used, this can be used to inform libcurl about - * how large the file being sent really is. That allows better error - * checking and better verifies that the upload was successful. -1 means - * unknown size. - * - * For large file support, there is also a _LARGE version of the key - * which takes an off_t type, allowing platforms with larger off_t - * sizes to handle larger files. See below for INFILESIZE_LARGE. - */ - infilesize, - /** POST static input fields. */ - postfields = 10_015, - /** Set the referrer page (needed by some CGIs) */ - referer, - /** Set the FTP PORT string (interface name, named or numerical IP address) - Use i.e '-' to use default address. */ - ftpport, - /** Set the User-Agent string (examined by some CGIs) */ - useragent, - /** If the download receives less than "low speed limit" bytes/second - * during "low speed time" seconds, the operations is aborted. - * You could i.e if you have a pretty high speed connection, abort if - * it is less than 2000 bytes/sec during 20 seconds. - */ - - /** Set the "low speed limit" */ - low_speed_limit = 19, - /** Set the "low speed time" */ - low_speed_time, - /** Set the continuation offset. - * - * Note there is also a _LARGE version of this key which uses - * off_t types, allowing for large file offsets on platforms which - * use larger-than-32-bit off_t's. Look below for RESUME_FROM_LARGE. - */ - resume_from, - /** Set cookie in request: */ - cookie = 10_022, - /** This points to a linked list of headers, struct curl_slist kind */ - httpheader, - /** This points to a linked list of post entries, struct curl_httppost */ - httppost, - /** name of the file keeping your private SSL-certificate */ - sslcert, - /** password for the SSL or SSH private key */ - keypasswd, - /** send TYPE parameter? */ - crlf = 27, - /** send linked-list of QUOTE commands */ - quote = 10_028, - /** send FILE * or void * to store headers to, if you use a callback it - is simply passed to the callback unmodified */ - writeheader, - /** point to a file to read the initial cookies from, also enables - "cookie awareness" */ - cookiefile = 10_031, - /** What version to specifically try to use. - See CURL_SSLVERSION defines below. */ - sslversion = 32, - /** What kind of HTTP time condition to use, see defines */ - timecondition, - /** Time to use with the above condition. Specified in number of seconds - since 1 Jan 1970 */ - timevalue, - /* 35 = OBSOLETE */ - - /** Custom request, for customizing the get command like - HTTP: DELETE, TRACE and others - FTP: to use a different list command - */ - customrequest = 10_036, - /** HTTP request, for odd commands like DELETE, TRACE and others */ - stderr, - /* 38 is not used */ - - /** send linked-list of post-transfer QUOTE commands */ - postquote = 10_039, - /** Pass a pointer to string of the output using full variable-replacement - as described elsewhere. */ - writeinfo, - verbose = 41, /** talk a lot */ - header, /** throw the header out too */ - noprogress, /** shut off the progress meter */ - nobody, /** use HEAD to get http document */ - failonerror, /** no output on http error codes >= 300 */ - upload, /** this is an upload */ - post, /** HTTP POST method */ - dirlistonly, /** return bare names when listing directories */ - append = 50, /** Append instead of overwrite on upload! */ - /** Specify whether to read the user+password from the .netrc or the URL. - * This must be one of the CURL_NETRC_* enums below. */ - netrc, - followlocation, /** use Location: Luke! */ - transfertext, /** transfer data in text/ASCII format */ - put, /** HTTP PUT */ - /* 55 = OBSOLETE */ - - /** Function that will be called instead of the internal progress display - * function. This function should be defined as the curl_progress_callback - * prototype defines. */ - progressfunction = 20_056, - /** Data passed to the progress callback */ - progressdata = 10_057, - /** We want the referrer field set automatically when following locations */ - autoreferer = 58, - /** Port of the proxy, can be set in the proxy string as well with: - `[host]:[port]` */ - proxyport, - /** size of the POST input data, if strlen() is not good to use */ - postfieldsize, - /** tunnel non-http operations through a HTTP proxy */ - httpproxytunnel, - /** Set the interface string to use as outgoing network interface */ - intrface = 10_062, - /** Set the krb4/5 security level, this also enables krb4/5 awareness. This - * is a string, 'clear', 'safe', 'confidential' or 'private'. If the string - * is set but doesn't match one of these, 'private' will be used. */ - krblevel, - /** Set if we should verify the peer in ssl handshake, set 1 to verify. */ - ssl_verifypeer = 64, - /** The CApath or CAfile used to validate the peer certificate - this option is used only if SSL_VERIFYPEER is true */ - cainfo = 10_065, - /* 66 = OBSOLETE */ - /* 67 = OBSOLETE */ - - /** Maximum number of http redirects to follow */ - maxredirs = 68, - /** Pass a long set to 1 to get the date of the requested document (if - possible)! Pass a zero to shut it off. */ - filetime, - /** This points to a linked list of telnet options */ - telnetoptions = 10_070, - /** Max amount of cached alive connections */ - maxconnects = 71, - /** What policy to use when closing connections when the cache is filled - up */ - closepolicy, - /* 73 = OBSOLETE */ - - /** Set to explicitly use a new connection for the upcoming transfer. - Do not use this unless you're absolutely sure of this, as it makes the - operation slower and is less friendly for the network. */ - fresh_connect = 74, - /** Set to explicitly forbid the upcoming transfer's connection to be re-used - when done. Do not use this unless you're absolutely sure of this, as it - makes the operation slower and is less friendly for the network. */ - forbid_reuse, - /** Set to a file name that contains random data for libcurl to use to - seed the random engine when doing SSL connects. */ - random_file = 10_076, - /** Set to the Entropy Gathering Daemon socket pathname */ - egdsocket, - /** Time-out connect operations after this amount of seconds, if connects - are OK within this time, then fine... This only aborts the connect - phase. [Only works on unix-style/SIGALRM operating systems] */ - connecttimeout = 78, - /** Function that will be called to store headers (instead of fwrite). The - * parameters will use fwrite() syntax, make sure to follow them. */ - headerfunction = 20_079, - /** Set this to force the HTTP request to get back to GET. Only really usable - if POST, PUT or a custom request have been used first. - */ - httpget = 80, - /** Set if we should verify the Common name from the peer certificate in ssl - * handshake, set 1 to check existence, 2 to ensure that it matches the - * provided hostname. */ - ssl_verifyhost, - /** Specify which file name to write all known cookies in after completed - operation. Set file name to "-" (dash) to make it go to stdout. */ - cookiejar = 10_082, - /** Specify which SSL ciphers to use */ - ssl_cipher_list, - /** Specify which HTTP version to use! This must be set to one of the - CURL_HTTP_VERSION* enums set below. */ - http_version = 84, - /** Specifically switch on or off the FTP engine's use of the EPSV command. By - default, that one will always be attempted before the more traditional - PASV command. */ - ftp_use_epsv, - /** type of the file keeping your SSL-certificate ("DER", "PEM", "ENG") */ - sslcerttype = 10_086, - /** name of the file keeping your private SSL-key */ - sslkey, - /** type of the file keeping your private SSL-key ("DER", "PEM", "ENG") */ - sslkeytype, - /** crypto engine for the SSL-sub system */ - sslengine, - /** set the crypto engine for the SSL-sub system as default - the param has no meaning... - */ - sslengine_default = 90, - /** Non-zero value means to use the global dns cache */ - dns_use_global_cache, - /** DNS cache timeout */ - dns_cache_timeout, - /** send linked-list of pre-transfer QUOTE commands */ - prequote = 10_093, - /** set the debug function */ - debugfunction = 20_094, - /** set the data for the debug function */ - debugdata = 10_095, - /** mark this as start of a cookie session */ - cookiesession = 96, - /** The CApath directory used to validate the peer certificate - this option is used only if SSL_VERIFYPEER is true */ - capath = 10_097, - /** Instruct libcurl to use a smaller receive buffer */ - buffersize = 98, - /** Instruct libcurl to not use any signal/alarm handlers, even when using - timeouts. This option is useful for multi-threaded applications. - See libcurl-the-guide for more background information. */ - nosignal, - /** Provide a CURLShare for mutexing non-ts data */ - share = 10_100, - /** indicates type of proxy. accepted values are CURLPROXY_HTTP (default), - CURLPROXY_SOCKS4, CURLPROXY_SOCKS4A and CURLPROXY_SOCKS5. */ - proxytype = 101, - /** Set the Accept-Encoding string. Use this to tell a server you would like - the response to be compressed. */ - encoding = 10_102, - /** Set pointer to private data */ - private_opt, - /** Set aliases for HTTP 200 in the HTTP Response header */ - http200aliases, - /** Continue to send authentication (user+password) when following locations, - even when hostname changed. This can potentially send off the name - and password to whatever host the server decides. */ - unrestricted_auth = 105, - /** Specifically switch on or off the FTP engine's use of the EPRT command ( it - also disables the LPRT attempt). By default, those ones will always be - attempted before the good old traditional PORT command. */ - ftp_use_eprt, - /** Set this to a bitmask value to enable the particular authentications - methods you like. Use this in combination with CURLOPT_USERPWD. - Note that setting multiple bits may cause extra network round-trips. */ - httpauth, - /** Set the ssl context callback function, currently only for OpenSSL ssl_ctx - in second argument. The function must be matching the - curl_ssl_ctx_callback proto. */ - ssl_ctx_function = 20_108, - /** Set the userdata for the ssl context callback function's third - argument */ - ssl_ctx_data = 10_109, - /** FTP Option that causes missing dirs to be created on the remote server. - In 7.19.4 we introduced the convenience enums for this option using the - CURLFTP_CREATE_DIR prefix. - */ - ftp_create_missing_dirs = 110, - /** Set this to a bitmask value to enable the particular authentications - methods you like. Use this in combination with CURLOPT_PROXYUSERPWD. - Note that setting multiple bits may cause extra network round-trips. */ - proxyauth, - /** FTP option that changes the timeout, in seconds, associated with - getting a response. This is different from transfer timeout time and - essentially places a demand on the FTP server to acknowledge commands - in a timely manner. */ - ftp_response_timeout, - /** Set this option to one of the CURL_IPRESOLVE_* defines (see below) to - tell libcurl to resolve names to those IP versions only. This only has - affect on systems with support for more than one, i.e IPv4 _and_ IPv6. */ - ipresolve, - /** Set this option to limit the size of a file that will be downloaded from - an HTTP or FTP server. - - Note there is also _LARGE version which adds large file support for - platforms which have larger off_t sizes. See MAXFILESIZE_LARGE below. */ - maxfilesize, - /** See the comment for INFILESIZE above, but in short, specifies - * the size of the file being uploaded. -1 means unknown. - */ - infilesize_large = 30_115, - /** Sets the continuation offset. There is also a LONG version of this; - * look above for RESUME_FROM. - */ - resume_from_large, - /** Sets the maximum size of data that will be downloaded from - * an HTTP or FTP server. See MAXFILESIZE above for the LONG version. - */ - maxfilesize_large, - /** Set this option to the file name of your .netrc file you want libcurl - to parse (using the CURLOPT_NETRC option). If not set, libcurl will do - a poor attempt to find the user's home directory and check for a .netrc - file in there. */ - netrc_file = 10_118, - /** Enable SSL/TLS for FTP, pick one of: - CURLFTPSSL_TRY - try using SSL, proceed anyway otherwise - CURLFTPSSL_CONTROL - SSL for the control connection or fail - CURLFTPSSL_ALL - SSL for all communication or fail - */ - use_ssl = 119, - /** The _LARGE version of the standard POSTFIELDSIZE option */ - postfieldsize_large = 30_120, - /** Enable/disable the TCP Nagle algorithm */ - tcp_nodelay = 121, - /* 122 OBSOLETE, used in 7.12.3. Gone in 7.13.0 */ - /* 123 OBSOLETE. Gone in 7.16.0 */ - /* 124 OBSOLETE, used in 7.12.3. Gone in 7.13.0 */ - /* 125 OBSOLETE, used in 7.12.3. Gone in 7.13.0 */ - /* 126 OBSOLETE, used in 7.12.3. Gone in 7.13.0 */ - /* 127 OBSOLETE. Gone in 7.16.0 */ - /* 128 OBSOLETE. Gone in 7.16.0 */ - - /** When FTP over SSL/TLS is selected (with CURLOPT_USE_SSL), this option - can be used to change libcurl's default action which is to first try - "AUTH SSL" and then "AUTH TLS" in this order, and proceed when a OK - response has been received. - - Available parameters are: - CURLFTPAUTH_DEFAULT - let libcurl decide - CURLFTPAUTH_SSL - try "AUTH SSL" first, then TLS - CURLFTPAUTH_TLS - try "AUTH TLS" first, then SSL - */ - ftpsslauth = 129, - ioctlfunction = 20_130, /// - ioctldata = 10_131, /// - /* 132 OBSOLETE. Gone in 7.16.0 */ - /* 133 OBSOLETE. Gone in 7.16.0 */ - - /** zero terminated string for pass on to the FTP server when asked for - "account" info */ - ftp_account = 10_134, - /** feed cookies into cookie engine */ - cookielist, - /** ignore Content-Length */ - ignore_content_length = 136, - /** Set to non-zero to skip the IP address received in a 227 PASV FTP server - response. Typically used for FTP-SSL purposes but is not restricted to - that. libcurl will then instead use the same IP address it used for the - control connection. */ - ftp_skip_pasv_ip, - /** Select "file method" to use when doing FTP, see the curl_ftpmethod - above. */ - ftp_filemethod, - /** Local port number to bind the socket to */ - localport, - /** Number of ports to try, including the first one set with LOCALPORT. - Thus, setting it to 1 will make no additional attempts but the first. - */ - localportrange, - /** no transfer, set up connection and let application use the socket by - extracting it with CURLINFO_LASTSOCKET */ - connect_only, - /** Function that will be called to convert from the - network encoding (instead of using the iconv calls in libcurl) */ - conv_from_network_function = 20_142, - /** Function that will be called to convert to the - network encoding (instead of using the iconv calls in libcurl) */ - conv_to_network_function, - /** Function that will be called to convert from UTF8 - (instead of using the iconv calls in libcurl) - Note that this is used only for SSL certificate processing */ - conv_from_utf8_function, - /** If the connection proceeds too quickly then need to slow it down */ - /** */ - /** limit-rate: maximum number of bytes per second to send or receive */ - max_send_speed_large = 30_145, - max_recv_speed_large, /// ditto - /** Pointer to command string to send if USER/PASS fails. */ - ftp_alternative_to_user = 10_147, - /** callback function for setting socket options */ - sockoptfunction = 20_148, - sockoptdata = 10_149, - /** set to 0 to disable session ID re-use for this transfer, default is - enabled (== 1) */ - ssl_sessionid_cache = 150, - /** allowed SSH authentication methods */ - ssh_auth_types, - /** Used by scp/sftp to do public/private key authentication */ - ssh_public_keyfile = 10_152, - ssh_private_keyfile, - /** Send CCC (Clear Command Channel) after authentication */ - ftp_ssl_ccc = 154, - /** Same as TIMEOUT and CONNECTTIMEOUT, but with ms resolution */ - timeout_ms, - connecttimeout_ms, /// ditto - /** set to zero to disable the libcurl's decoding and thus pass the raw body - data to the application even when it is encoded/compressed */ - http_transfer_decoding, - http_content_decoding, /// ditto - /** Permission used when creating new files and directories on the remote - server for protocols that support it, SFTP/SCP/FILE */ - new_file_perms, - new_directory_perms, /// ditto - /** Set the behaviour of POST when redirecting. Values must be set to one - of CURL_REDIR* defines below. This used to be called CURLOPT_POST301 */ - postredir, - /** used by scp/sftp to verify the host's public key */ - ssh_host_public_key_md5 = 10_162, - /** Callback function for opening socket (instead of socket(2)). Optionally, - callback is able change the address or refuse to connect returning - CURL_SOCKET_BAD. The callback should have type - curl_opensocket_callback */ - opensocketfunction = 20_163, - opensocketdata = 10_164, /// ditto - /** POST volatile input fields. */ - copypostfields, - /** set transfer mode (;type=$(LT)a|i$(GT)) when doing FTP via an HTTP proxy */ - proxy_transfer_mode = 166, - /** Callback function for seeking in the input stream */ - seekfunction = 20_167, - seekdata = 10_168, /// ditto - /** CRL file */ - crlfile, - /** Issuer certificate */ - issuercert, - /** (IPv6) Address scope */ - address_scope = 171, - /** Collect certificate chain info and allow it to get retrievable with - CURLINFO_CERTINFO after the transfer is complete. (Unfortunately) only - working with OpenSSL-powered builds. */ - certinfo, - /** "name" and "pwd" to use when fetching. */ - username = 10_173, - password, /// ditto - /** "name" and "pwd" to use with Proxy when fetching. */ - proxyusername, - proxypassword, /// ditto - /** Comma separated list of hostnames defining no-proxy zones. These should - match both hostnames directly, and hostnames within a domain. For - example, local.com will match local.com and www.local.com, but NOT - notlocal.com or www.notlocal.com. For compatibility with other - implementations of this, .local.com will be considered to be the same as - local.com. A single * is the only valid wildcard, and effectively - disables the use of proxy. */ - noproxy, - /** block size for TFTP transfers */ - tftp_blksize = 178, - /** Socks Service */ - socks5_gssapi_service = 10_179, - /** Socks Service */ - socks5_gssapi_nec = 180, - /** set the bitmask for the protocols that are allowed to be used for the - transfer, which thus helps the app which takes URLs from users or other - external inputs and want to restrict what protocol(s) to deal - with. Defaults to CURLPROTO_ALL. */ - protocols, - /** set the bitmask for the protocols that libcurl is allowed to follow to, - as a subset of the CURLOPT_PROTOCOLS ones. That means the protocol needs - to be set in both bitmasks to be allowed to get redirected to. Defaults - to all protocols except FILE and SCP. */ - redir_protocols, - /** set the SSH knownhost file name to use */ - ssh_knownhosts = 10_183, - /** set the SSH host key callback, must point to a curl_sshkeycallback - function */ - ssh_keyfunction = 20_184, - /** set the SSH host key callback custom pointer */ - ssh_keydata = 10_185, - /** set the SMTP mail originator */ - mail_from, - /** set the SMTP mail receiver(s) */ - mail_rcpt, - /** FTP: send PRET before PASV */ - ftp_use_pret = 188, - /** RTSP request method (OPTIONS, SETUP, PLAY, etc...) */ - rtsp_request, - /** The RTSP session identifier */ - rtsp_session_id = 10_190, - /** The RTSP stream URI */ - rtsp_stream_uri, - /** The Transport: header to use in RTSP requests */ - rtsp_transport, - /** Manually initialize the client RTSP CSeq for this handle */ - rtsp_client_cseq = 193, - /** Manually initialize the server RTSP CSeq for this handle */ - rtsp_server_cseq, - /** The stream to pass to INTERLEAVEFUNCTION. */ - interleavedata = 10_195, - /** Let the application define a custom write method for RTP data */ - interleavefunction = 20_196, - /** Turn on wildcard matching */ - wildcardmatch = 197, - /** Directory matching callback called before downloading of an - individual file (chunk) started */ - chunk_bgn_function = 20_198, - /** Directory matching callback called after the file (chunk) - was downloaded, or skipped */ - chunk_end_function, - /** Change match (fnmatch-like) callback for wildcard matching */ - fnmatch_function, - /** Let the application define custom chunk data pointer */ - chunk_data = 10_201, - /** FNMATCH_FUNCTION user pointer */ - fnmatch_data, - /** send linked-list of name:port:address sets */ - resolve, - /** Set a username for authenticated TLS */ - tlsauth_username, - /** Set a password for authenticated TLS */ - tlsauth_password, - /** Set authentication type for authenticated TLS */ - tlsauth_type, - /** the last unused */ - lastentry, - - writedata = file, /// convenient alias - readdata = infile, /// ditto - headerdata = writeheader, /// ditto - rtspheader = httpheader, /// ditto -} -/// -alias CURLoption = int; -/// -enum CURLOPT_SERVER_RESPONSE_TIMEOUT = CurlOption.ftp_response_timeout; - -/** Below here follows defines for the CURLOPT_IPRESOLVE option. If a host - name resolves addresses using more than one IP protocol version, this - option might be handy to force libcurl to use a specific IP version. */ -enum CurlIpResolve { - whatever = 0, /** default, resolves addresses to all IP versions that your system allows */ - v4 = 1, /** resolve to ipv4 addresses */ - v6 = 2 /** resolve to ipv6 addresses */ -} - -/** three convenient "aliases" that follow the name scheme better */ -enum CURLOPT_WRITEDATA = CurlOption.file; -/// ditto -enum CURLOPT_READDATA = CurlOption.infile; -/// ditto -enum CURLOPT_HEADERDATA = CurlOption.writeheader; -/// ditto -enum CURLOPT_RTSPHEADER = CurlOption.httpheader; - -/** These enums are for use with the CURLOPT_HTTP_VERSION option. */ -enum CurlHttpVersion { - none, /** setting this means we don't care, and that we'd - like the library to choose the best possible - for us! */ - v1_0, /** please use HTTP 1.0 in the request */ - v1_1, /** please use HTTP 1.1 in the request */ - last /** *ILLEGAL* http version */ -} - -/** - * Public API enums for RTSP requests - */ -enum CurlRtspReq { - none, /// - options, /// - describe, /// - announce, /// - setup, /// - play, /// - pause, /// - teardown, /// - get_parameter, /// - set_parameter, /// - record, /// - receive, /// - last /// -} - - /** These enums are for use with the CURLOPT_NETRC option. */ -enum CurlNetRcOption { - ignored, /** The .netrc will never be read. This is the default. */ - optional /** A user:password in the URL will be preferred to one in the .netrc. */, - required, /** A user:password in the URL will be ignored. - * Unless one is set programmatically, the .netrc - * will be queried. */ - last /// -} - -/// -enum CurlSslVersion { - default_version, /// - tlsv1, /// - sslv2, /// - sslv3, /// - last /** never use */ -} - -/// -enum CurlTlsAuth { - none, /// - srp, /// - last /** never use */ -} - -/** symbols to use with CURLOPT_POSTREDIR. - CURL_REDIR_POST_301 and CURL_REDIR_POST_302 can be bitwise ORed so that - CURL_REDIR_POST_301 | CURL_REDIR_POST_302 == CURL_REDIR_POST_ALL */ -enum CurlRedir { - get_all = 0, /// - post_301 = 1, /// - post_302 = 2, /// - /// - post_all = (1 | 2) // (CURL_REDIR_POST_301|CURL_REDIR_POST_302); -} -/// -enum CurlTimeCond { - none, /// - ifmodsince, /// - ifunmodsince, /// - lastmod, /// - last /// -} -/// -alias curl_TimeCond = int; - - -/** curl_strequal() and curl_strnequal() are subject for removal in a future - libcurl, see lib/README.curlx for details */ -extern (C) { -int curl_strequal(scope const(char) *s1, scope const(char) *s2); -/// ditto -int curl_strnequal(scope const(char) *s1, scope const(char) *s2, size_t n); -} -enum CurlForm { - nothing, /********** the first one is unused ************/ - copyname, - ptrname, - namelength, - copycontents, - ptrcontents, - contentslength, - filecontent, - array, - obsolete, - file, - buffer, - bufferptr, - bufferlength, - contenttype, - contentheader, - filename, - end, - obsolete2, - stream, - lastentry /** the last unused */ -} -alias CURLformoption = int; - - -/** structure to be used as parameter for CURLFORM_ARRAY */ -extern (C) struct curl_forms -{ - CURLformoption option; /// - const(char) *value; /// -} - -/** Use this for multipart formpost building - * - * Returns code for curl_formadd() - * - * Returns: - * - * $(UL - * $(LI CURL_FORMADD_OK on success ) - * $(LI CURL_FORMADD_MEMORY if the FormInfo allocation fails ) - * $(LI CURL_FORMADD_OPTION_TWICE if one option is given twice for one Form ) - * $(LI CURL_FORMADD_NULL if a null pointer was given for a char ) - * $(LI CURL_FORMADD_MEMORY if the allocation of a FormInfo struct failed ) - * $(LI CURL_FORMADD_UNKNOWN_OPTION if an unknown option was used ) - * $(LI CURL_FORMADD_INCOMPLETE if the some FormInfo is not complete (or error) ) - * $(LI CURL_FORMADD_MEMORY if a curl_httppost struct cannot be allocated ) - * $(LI CURL_FORMADD_MEMORY if some allocation for string copying failed. ) - * $(LI CURL_FORMADD_ILLEGAL_ARRAY if an illegal option is used in an array ) - * ) - * - ***************************************************************************/ -enum CurlFormAdd { - ok, /** first, no error */ - memory, /// - option_twice, /// - null_ptr, /// - unknown_option, /// - incomplete, /// - illegal_array, /// - disabled, /** libcurl was built with this disabled */ - last /// -} -/// -alias CURLFORMcode = int; - -extern (C) { - -/** - * Name: curl_formadd() - * - * Description: - * - * Pretty advanced function for building multi-part formposts. Each invoke - * adds one part that together construct a full post. Then use - * CURLOPT_HTTPPOST to send it off to libcurl. - */ -CURLFORMcode curl_formadd(curl_httppost **httppost, curl_httppost **last_post,...); - -/** - * callback function for curl_formget() - * The void *arg pointer will be the one passed as second argument to - * curl_formget(). - * The character buffer passed to it must not be freed. - * Should return the buffer length passed to it as the argument "len" on - * success. - */ -alias curl_formget_callback = size_t function(void *arg, const(char) *buf, size_t len); - -/** - * Name: curl_formget() - * - * Description: - * - * Serialize a curl_httppost struct built with curl_formadd(). - * Accepts a void pointer as second argument which will be passed to - * the curl_formget_callback function. - * Returns 0 on success. - */ -int curl_formget(curl_httppost *form, void *arg, curl_formget_callback append); -/** - * Name: curl_formfree() - * - * Description: - * - * Free a multipart formpost previously built with curl_formadd(). - */ -void curl_formfree(curl_httppost *form); - -/** - * Name: curl_getenv() - * - * Description: - * - * Returns a malloc()'ed string that MUST be curl_free()ed after usage is - * complete. DEPRECATED - see lib/README.curlx - */ -char * curl_getenv(scope const(char) *variable); - -/** - * Name: curl_version() - * - * Description: - * - * Returns a static ascii string of the libcurl version. - */ -char * curl_version(); - -/** - * Name: curl_easy_escape() - * - * Description: - * - * Escapes URL strings (converts all letters consider illegal in URLs to their - * %XX versions). This function returns a new allocated string or NULL if an - * error occurred. - */ -char * curl_easy_escape(CURL *handle, scope const(char) *string, int length); - -/** the previous version: */ -char * curl_escape(scope const(char) *string, int length); - - -/** - * Name: curl_easy_unescape() - * - * Description: - * - * Unescapes URL encoding in strings (converts all %XX codes to their 8bit - * versions). This function returns a new allocated string or NULL if an error - * occurred. - * Conversion Note: On non-ASCII platforms the ASCII %XX codes are - * converted into the host encoding. - */ -char * curl_easy_unescape(CURL *handle, scope const(char) *string, int length, int *outlength); - -/** the previous version */ -char * curl_unescape(scope const(char) *string, int length); - -/** - * Name: curl_free() - * - * Description: - * - * Provided for de-allocation in the same translation unit that did the - * allocation. Added in libcurl 7.10 - */ -void curl_free(void *p); - -/** - * Name: curl_global_init() - * - * Description: - * - * curl_global_init() should be invoked exactly once for each application that - * uses libcurl and before any call of other libcurl functions. - * - * This function is not thread-safe! - */ -CURLcode curl_global_init(c_long flags); - -/** - * Name: curl_global_init_mem() - * - * Description: - * - * curl_global_init() or curl_global_init_mem() should be invoked exactly once - * for each application that uses libcurl. This function can be used to - * initialize libcurl and set user defined memory management callback - * functions. Users can implement memory management routines to check for - * memory leaks, check for mis-use of the curl library etc. User registered - * callback routines with be invoked by this library instead of the system - * memory management routines like malloc, free etc. - */ -CURLcode curl_global_init_mem( - c_long flags, - curl_malloc_callback m, - curl_free_callback f, - curl_realloc_callback r, - curl_strdup_callback s, - curl_calloc_callback c -); - -/** - * Name: curl_global_cleanup() - * - * Description: - * - * curl_global_cleanup() should be invoked exactly once for each application - * that uses libcurl - */ -void curl_global_cleanup(); -} - -/** linked-list structure for the CURLOPT_QUOTE option (and other) */ -extern (C) { - -struct curl_slist -{ - char *data; - curl_slist *next; -} - -/** - * Name: curl_slist_append() - * - * Description: - * - * Appends a string to a linked list. If no list exists, it will be created - * first. Returns the new list, after appending. - */ -curl_slist * curl_slist_append(curl_slist *, const(char) *); - -/** - * Name: curl_slist_free_all() - * - * Description: - * - * free a previously built curl_slist. - */ -void curl_slist_free_all(curl_slist *); - -/** - * Name: curl_getdate() - * - * Description: - * - * Returns the time, in seconds since 1 Jan 1970 of the time string given in - * the first argument. The time argument in the second parameter is unused - * and should be set to NULL. - */ -time_t curl_getdate(const(char) *p, const(time_t) *unused); - -/** info about the certificate chain, only for OpenSSL builds. Asked - for with CURLOPT_CERTINFO / CURLINFO_CERTINFO */ -struct curl_certinfo -{ - int num_of_certs; /** number of certificates with information */ - curl_slist **certinfo; /** for each index in this array, there's a - linked list with textual information in the - format "name: value" */ -} - -} // extern (C) end - -/// -enum CURLINFO_STRING = 0x100000; -/// -enum CURLINFO_LONG = 0x200000; -/// -enum CURLINFO_DOUBLE = 0x300000; -/// -enum CURLINFO_SLIST = 0x400000; -/// -enum CURLINFO_MASK = 0x0fffff; - -/// -enum CURLINFO_TYPEMASK = 0xf00000; - -/// -enum CurlInfo { - none, /// - effective_url = 1_048_577, /// - response_code = 2_097_154, /// - total_time = 3_145_731, /// - namelookup_time, /// - connect_time, /// - pretransfer_time, /// - size_upload, /// - size_download, /// - speed_download, /// - speed_upload, /// - header_size = 2_097_163, /// - request_size, /// - ssl_verifyresult, /// - filetime, /// - content_length_download = 3_145_743, /// - content_length_upload, /// - starttransfer_time, /// - content_type = 1_048_594, /// - redirect_time = 3_145_747, /// - redirect_count = 2_097_172, /// - private_info = 1_048_597, /// - http_connectcode = 2_097_174, /// - httpauth_avail, /// - proxyauth_avail, /// - os_errno, /// - num_connects, /// - ssl_engines = 4_194_331, /// - cookielist, /// - lastsocket = 2_097_181, /// - ftp_entry_path = 1_048_606, /// - redirect_url, /// - primary_ip, /// - appconnect_time = 3_145_761, /// - certinfo = 4_194_338, /// - condition_unmet = 2_097_187, /// - rtsp_session_id = 1_048_612, /// - rtsp_client_cseq = 2_097_189, /// - rtsp_server_cseq, /// - rtsp_cseq_recv, /// - primary_port, /// - local_ip = 1_048_617, /// - local_port = 2_097_194, /// - /** Fill in new entries below here! */ - lastone = 42 -} -/// -alias CURLINFO = int; - -/** CURLINFO_RESPONSE_CODE is the new name for the option previously known as - CURLINFO_HTTP_CODE */ -enum CURLINFO_HTTP_CODE = CurlInfo.response_code; - -/// -enum CurlClosePolicy { - none, /// - oldest, /// - least_recently_used, /// - least_traffic, /// - slowest, /// - callback, /// - last /// -} -/// -alias curl_closepolicy = int; - -/// -enum CurlGlobal { - ssl = 1, /// - win32 = 2, /// - /// - all = (1 | 2), // (CURL_GLOBAL_SSL|CURL_GLOBAL_WIN32); - nothing = 0, /// - default_ = (1 | 2) /// all -} - -/****************************************************************************** - * Setup defines, protos etc for the sharing stuff. - */ - -/** Different data locks for a single share */ -enum CurlLockData { - none, /// - /** CURL_LOCK_DATA_SHARE is used internally to say that - * the locking is just made to change the internal state of the share - * itself. - */ - share, - cookie, /// - dns, /// - ssl_session, /// - connect, /// - last /// -} -/// -alias curl_lock_data = int; - -/** Different lock access types */ -enum CurlLockAccess { - none, /** unspecified action */ - shared_access, /** for read perhaps */ - single, /** for write perhaps */ - last /** never use */ -} -/// -alias curl_lock_access = int; - -/// -alias curl_lock_function = void function(CURL *handle, curl_lock_data data, curl_lock_access locktype, void *userptr); -/// -alias curl_unlock_function = void function(CURL *handle, curl_lock_data data, void *userptr); - -/// -alias CURLSH = void; - -/// -enum CurlShError { - ok, /** all is fine */ - bad_option, /** 1 */ - in_use, /** 2 */ - invalid, /** 3 */ - nomem, /** out of memory */ - last /** never use */ -} -/// -alias CURLSHcode = int; - -/** pass in a user data pointer used in the lock/unlock callback - functions */ -enum CurlShOption { - none, /** don't use */ - share, /** specify a data type to share */ - unshare, /** specify which data type to stop sharing */ - lockfunc, /** pass in a 'curl_lock_function' pointer */ - unlockfunc, /** pass in a 'curl_unlock_function' pointer */ - userdata, /** pass in a user data pointer used in the lock/unlock - callback functions */ - last /** never use */ -} -/// -alias CURLSHoption = int; - -extern (C) { -/// -CURLSH * curl_share_init(); -/// -CURLSHcode curl_share_setopt(CURLSH *, CURLSHoption option,...); -/// -CURLSHcode curl_share_cleanup(CURLSH *); -} - -/***************************************************************************** - * Structures for querying information about the curl library at runtime. - */ - -// CURLVERSION_* -enum CurlVer { - first, /// - second, /// - third, /// - fourth, /// - last /// -} -/// -alias CURLversion = int; - -/** The 'CURLVERSION_NOW' is the symbolic name meant to be used by - basically all programs ever that want to get version information. It is - meant to be a built-in version number for what kind of struct the caller - expects. If the struct ever changes, we redefine the NOW to another enum - from above. */ -enum CURLVERSION_NOW = CurlVer.fourth; - -/// -extern (C) struct _N28 -{ - CURLversion age; /** age of the returned struct */ - const(char) *version_; /** LIBCURL_VERSION */ - uint version_num; /** LIBCURL_VERSION_NUM */ - const(char) *host; /** OS/host/cpu/machine when configured */ - int features; /** bitmask, see defines below */ - const(char) *ssl_version; /** human readable string */ - c_long ssl_version_num; /** not used anymore, always 0 */ - const(char) *libz_version; /** human readable string */ - /** protocols is terminated by an entry with a NULL protoname */ - const(char) **protocols; - /** The fields below this were added in CURLVERSION_SECOND */ - const(char) *ares; - int ares_num; - /** This field was added in CURLVERSION_THIRD */ - const(char) *libidn; - /** These field were added in CURLVERSION_FOURTH. */ - /** Same as '_libiconv_version' if built with HAVE_ICONV */ - int iconv_ver_num; - const(char) *libssh_version; /** human readable string */ -} -/// -alias curl_version_info_data = _N28; - -/// -// CURL_VERSION_* -enum CurlVersion { - ipv6 = 1, /** IPv6-enabled */ - kerberos4 = 2, /** kerberos auth is supported */ - ssl = 4, /** SSL options are present */ - libz = 8, /** libz features are present */ - ntlm = 16, /** NTLM auth is supported */ - gssnegotiate = 32, /** Negotiate auth support */ - dbg = 64, /** built with debug capabilities */ - asynchdns = 128, /** asynchronous dns resolves */ - spnego = 256, /** SPNEGO auth */ - largefile = 512, /** supports files bigger than 2GB */ - idn = 1024, /** International Domain Names support */ - sspi = 2048, /** SSPI is supported */ - conv = 4096, /** character conversions supported */ - curldebug = 8192, /** debug memory tracking supported */ - tlsauth_srp = 16_384 /** TLS-SRP auth is supported */ -} - -extern (C) { -/** - * Name: curl_version_info() - * - * Description: - * - * This function returns a pointer to a static copy of the version info - * struct. See above. - */ -curl_version_info_data * curl_version_info(CURLversion ); - -/** - * Name: curl_easy_strerror() - * - * Description: - * - * The curl_easy_strerror function may be used to turn a CURLcode value - * into the equivalent human readable error string. This is useful - * for printing meaningful error messages. - */ -const(char)* curl_easy_strerror(CURLcode ); - -/** - * Name: curl_share_strerror() - * - * Description: - * - * The curl_share_strerror function may be used to turn a CURLSHcode value - * into the equivalent human readable error string. This is useful - * for printing meaningful error messages. - */ -const(char)* curl_share_strerror(CURLSHcode ); - -/** - * Name: curl_easy_pause() - * - * Description: - * - * The curl_easy_pause function pauses or unpauses transfers. Select the new - * state by setting the bitmask, use the convenience defines below. - * - */ -CURLcode curl_easy_pause(CURL *handle, int bitmask); -} - - -/// -enum CurlPause { - recv = 1, /// - recv_cont = 0, /// - send = 4, /// - send_cont = 0, /// - /// - all = (1 | 4), // CURLPAUSE_RECV | CURLPAUSE_SEND - /// - cont = (0 | 0), // CURLPAUSE_RECV_CONT | CURLPAUSE_SEND_CONT -} - -/* unfortunately, the easy.h and multi.h include files need options and info - stuff before they can be included! */ -/* *************************************************************************** - * _ _ ____ _ - * Project ___| | | | _ \| | - * / __| | | | |_) | | - * | (__| |_| | _ <| |___ - * \___|\___/|_| \_\_____| - * - * Copyright (C) 1998 - 2008, Daniel Stenberg, , et al. - * - * This software is licensed as described in the file COPYING, which - * you should have received as part of this distribution. The terms - * are also available at http://curl.haxx.se/docs/copyright.html. - * - * You may opt to use, copy, modify, merge, publish, distribute and/or sell - * copies of the Software, and permit persons to whom the Software is - * furnished to do so, under the terms of the COPYING file. - * - * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY - * KIND, either express or implied. - * - ***************************************************************************/ - -extern (C) { - /// - CURL * curl_easy_init(); - /// - CURLcode curl_easy_setopt(CURL *curl, CURLoption option,...); - /// - CURLcode curl_easy_perform(CURL *curl); - /// - void curl_easy_cleanup(CURL *curl); -} - -/** - * Name: curl_easy_getinfo() - * - * Description: - * - * Request internal information from the curl session with this function. The - * third argument MUST be a pointer to a long, a pointer to a char * or a - * pointer to a double (as the documentation describes elsewhere). The data - * pointed to will be filled in accordingly and can be relied upon only if the - * function returns CURLE_OK. This function is intended to get used *AFTER* a - * performed transfer, all results from this function are undefined until the - * transfer is completed. - */ -extern (C) CURLcode curl_easy_getinfo(CURL *curl, CURLINFO info,...); - - -/** - * Name: curl_easy_duphandle() - * - * Description: - * - * Creates a new curl session handle with the same options set for the handle - * passed in. Duplicating a handle could only be a matter of cloning data and - * options, internal state info and things like persistant connections cannot - * be transfered. It is useful in multithreaded applications when you can run - * curl_easy_duphandle() for each new thread to avoid a series of identical - * curl_easy_setopt() invokes in every thread. - */ -extern (C) CURL * curl_easy_duphandle(CURL *curl); - -/** - * Name: curl_easy_reset() - * - * Description: - * - * Re-initializes a CURL handle to the default values. This puts back the - * handle to the same state as it was in when it was just created. - * - * It does keep: live connections, the Session ID cache, the DNS cache and the - * cookies. - */ -extern (C) void curl_easy_reset(CURL *curl); - -/** - * Name: curl_easy_recv() - * - * Description: - * - * Receives data from the connected socket. Use after successful - * curl_easy_perform() with CURLOPT_CONNECT_ONLY option. - */ -extern (C) CURLcode curl_easy_recv(CURL *curl, void *buffer, size_t buflen, size_t *n); - -/** - * Name: curl_easy_send() - * - * Description: - * - * Sends data over the connected socket. Use after successful - * curl_easy_perform() with CURLOPT_CONNECT_ONLY option. - */ -extern (C) CURLcode curl_easy_send(CURL *curl, void *buffer, size_t buflen, size_t *n); - - -/* - * This header file should not really need to include "curl.h" since curl.h - * itself includes this file and we expect user applications to do #include - * without the need for especially including multi.h. - * - * For some reason we added this include here at one point, and rather than to - * break existing (wrongly written) libcurl applications, we leave it as-is - * but with this warning attached. - */ -/* *************************************************************************** - * _ _ ____ _ - * Project ___| | | | _ \| | - * / __| | | | |_) | | - * | (__| |_| | _ <| |___ - * \___|\___/|_| \_\_____| - * - * Copyright (C) 1998 - 2010, Daniel Stenberg, , et al. - * - * This software is licensed as described in the file COPYING, which - * you should have received as part of this distribution. The terms - * are also available at http://curl.haxx.se/docs/copyright.html. - * - * You may opt to use, copy, modify, merge, publish, distribute and/or sell - * copies of the Software, and permit persons to whom the Software is - * furnished to do so, under the terms of the COPYING file. - * - * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY - * KIND, either express or implied. - * - ***************************************************************************/ - -/// -alias CURLM = void; - -/// -enum CurlM { - call_multi_perform = -1, /** please call curl_multi_perform() or curl_multi_socket*() soon */ - ok, /// - bad_handle, /** the passed-in handle is not a valid CURLM handle */ - bad_easy_handle, /** an easy handle was not good/valid */ - out_of_memory, /** if you ever get this, you're in deep sh*t */ - internal_error, /** this is a libcurl bug */ - bad_socket, /** the passed in socket argument did not match */ - unknown_option, /** curl_multi_setopt() with unsupported option */ - last, /// -} -/// -alias CURLMcode = int; - -/** just to make code nicer when using curl_multi_socket() you can now check - for CURLM_CALL_MULTI_SOCKET too in the same style it works for - curl_multi_perform() and CURLM_CALL_MULTI_PERFORM */ -enum CURLM_CALL_MULTI_SOCKET = CurlM.call_multi_perform; - -/// -enum CurlMsg -{ - none, /// - done, /** This easy handle has completed. 'result' contains - the CURLcode of the transfer */ - last, /** no used */ -} -/// -alias CURLMSG = int; - -/// -extern (C) union _N31 -{ - void *whatever; /** message-specific data */ - CURLcode result; /** return code for transfer */ -} - -/// -extern (C) struct CURLMsg -{ - CURLMSG msg; /** what this message means */ - CURL *easy_handle; /** the handle it concerns */ - _N31 data; /// -} - -/** - * Name: curl_multi_init() - * - * Desc: inititalize multi-style curl usage - * - * Returns: a new CURLM handle to use in all 'curl_multi' functions. - */ -extern (C) CURLM * curl_multi_init(); - -/** - * Name: curl_multi_add_handle() - * - * Desc: add a standard curl handle to the multi stack - * - * Returns: CURLMcode type, general multi error code. - */ -extern (C) CURLMcode curl_multi_add_handle(CURLM *multi_handle, CURL *curl_handle); - - /** - * Name: curl_multi_remove_handle() - * - * Desc: removes a curl handle from the multi stack again - * - * Returns: CURLMcode type, general multi error code. - */ -extern (C) CURLMcode curl_multi_remove_handle(CURLM *multi_handle, CURL *curl_handle); - - /** - * Name: curl_multi_fdset() - * - * Desc: Ask curl for its fd_set sets. The app can use these to select() or - * poll() on. We want curl_multi_perform() called as soon as one of - * them are ready. - * - * Returns: CURLMcode type, general multi error code. - */ - -/** tmp decl */ -alias fd_set = int; -/// -extern (C) CURLMcode curl_multi_fdset( - CURLM *multi_handle, - fd_set *read_fd_set, - fd_set *write_fd_set, - fd_set *exc_fd_set, - int *max_fd -); - - /** - * Name: curl_multi_perform() - * - * Desc: When the app thinks there's data available for curl it calls this - * function to read/write whatever there is right now. This returns - * as soon as the reads and writes are done. This function does not - * require that there actually is data available for reading or that - * data can be written, it can be called just in case. It returns - * the number of handles that still transfer data in the second - * argument's integer-pointer. - * - * Returns: CURLMcode type, general multi error code. *NOTE* that this only - * returns errors etc regarding the whole multi stack. There might - * still have occurred problems on invidual transfers even when this - * returns OK. - */ -extern (C) CURLMcode curl_multi_perform(CURLM *multi_handle, int *running_handles); - - /** - * Name: curl_multi_cleanup() - * - * Desc: Cleans up and removes a whole multi stack. It does not free or - * touch any individual easy handles in any way. We need to define - * in what state those handles will be if this function is called - * in the middle of a transfer. - * - * Returns: CURLMcode type, general multi error code. - */ -extern (C) CURLMcode curl_multi_cleanup(CURLM *multi_handle); - -/** - * Name: curl_multi_info_read() - * - * Desc: Ask the multi handle if there's any messages/informationals from - * the individual transfers. Messages include informationals such as - * error code from the transfer or just the fact that a transfer is - * completed. More details on these should be written down as well. - * - * Repeated calls to this function will return a new struct each - * time, until a special "end of msgs" struct is returned as a signal - * that there is no more to get at this point. - * - * The data the returned pointer points to will not survive calling - * curl_multi_cleanup(). - * - * The 'CURLMsg' struct is meant to be very simple and only contain - * very basic informations. If more involved information is wanted, - * we will provide the particular "transfer handle" in that struct - * and that should/could/would be used in subsequent - * curl_easy_getinfo() calls (or similar). The point being that we - * must never expose complex structs to applications, as then we'll - * undoubtably get backwards compatibility problems in the future. - * - * Returns: A pointer to a filled-in struct, or NULL if it failed or ran out - * of structs. It also writes the number of messages left in the - * queue (after this read) in the integer the second argument points - * to. - */ -extern (C) CURLMsg * curl_multi_info_read(CURLM *multi_handle, int *msgs_in_queue); - -/** - * Name: curl_multi_strerror() - * - * Desc: The curl_multi_strerror function may be used to turn a CURLMcode - * value into the equivalent human readable error string. This is - * useful for printing meaningful error messages. - * - * Returns: A pointer to a zero-terminated error message. - */ -extern (C) const(char)* curl_multi_strerror(CURLMcode ); - -/** - * Name: curl_multi_socket() and - * curl_multi_socket_all() - * - * Desc: An alternative version of curl_multi_perform() that allows the - * application to pass in one of the file descriptors that have been - * detected to have "action" on them and let libcurl perform. - * See man page for details. - */ -enum CurlPoll { - none_ = 0, /** jdrewsen - underscored in order not to clash with reserved D symbols */ - in_ = 1, /// - out_ = 2, /// - inout_ = 3, /// - remove_ = 4 /// -} - -/// -alias CURL_SOCKET_TIMEOUT = CURL_SOCKET_BAD; - -/// -enum CurlCSelect { - in_ = 0x01, /** jdrewsen - underscored in order not to clash with reserved D symbols */ - out_ = 0x02, /// - err_ = 0x04 /// -} - -extern (C) { - /// - alias curl_socket_callback = - int function(CURL *easy, /** easy handle */ - curl_socket_t s, /** socket */ - int what, /** see above */ - void *userp, /** private callback pointer */ - void *socketp); /** private socket pointer */ -} - -/** - * Name: curl_multi_timer_callback - * - * Desc: Called by libcurl whenever the library detects a change in the - * maximum number of milliseconds the app is allowed to wait before - * curl_multi_socket() or curl_multi_perform() must be called - * (to allow libcurl's timed events to take place). - * - * Returns: The callback should return zero. - */ - -extern (C) { - alias curl_multi_timer_callback = - int function(CURLM *multi, /** multi handle */ - c_long timeout_ms, /** see above */ - void *userp); /** private callback pointer */ - /// ditto - CURLMcode curl_multi_socket(CURLM *multi_handle, curl_socket_t s, int *running_handles); - /// ditto - CURLMcode curl_multi_socket_action(CURLM *multi_handle, curl_socket_t s, int ev_bitmask, int *running_handles); - /// ditto - CURLMcode curl_multi_socket_all(CURLM *multi_handle, int *running_handles); -} - -/** This macro below was added in 7.16.3 to push users who recompile to use - the new curl_multi_socket_action() instead of the old curl_multi_socket() -*/ - -/** - * Name: curl_multi_timeout() - * - * Desc: Returns the maximum number of milliseconds the app is allowed to - * wait before curl_multi_socket() or curl_multi_perform() must be - * called (to allow libcurl's timed events to take place). - * - * Returns: CURLM error code. - */ -extern (C) CURLMcode curl_multi_timeout(CURLM *multi_handle, c_long *milliseconds); - -/// -enum CurlMOption { - socketfunction = 20_001, /** This is the socket callback function pointer */ - socketdata = 10_002, /** This is the argument passed to the socket callback */ - pipelining = 3, /** set to 1 to enable pipelining for this multi handle */ - timerfunction = 20_004, /** This is the timer callback function pointer */ - timerdata = 10_005, /** This is the argument passed to the timer callback */ - maxconnects = 6, /** maximum number of entries in the connection cache */ - lastentry /// -} -/// -alias CURLMoption = int; - -/** - * Name: curl_multi_setopt() - * - * Desc: Sets options for the multi handle. - * - * Returns: CURLM error code. - */ -extern (C) CURLMcode curl_multi_setopt(CURLM *multi_handle, CURLMoption option,...); - -/** - * Name: curl_multi_assign() - * - * Desc: This function sets an association in the multi handle between the - * given socket and a private pointer of the application. This is - * (only) useful for curl_multi_socket uses. - * - * Returns: CURLM error code. - */ -extern (C) CURLMcode curl_multi_assign(CURLM *multi_handle, curl_socket_t sockfd, void *sockp); diff --git a/phobos/etc/c/odbc/sql.d b/phobos/etc/c/odbc/sql.d deleted file mode 100644 index c931de2..0000000 --- a/phobos/etc/c/odbc/sql.d +++ /dev/null @@ -1,441 +0,0 @@ -/** - * Windows API header module - * - * Translated from MinGW Windows headers - * - * License: $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost License 1.0) - * Source: $(PHOBOSSRC etc/c/odbc/_sql.d) - -Declarations for interfacing with the ODBC library. - -See_Also: $(LINK2 https://docs.microsoft.com/en-us/sql/odbc/reference/syntax/odbc-api-reference, - ODBC API Reference on MSDN) - */ -module etc.c.odbc.sql; - -public import etc.c.odbc.sqltypes; - -enum ODBCVER = 0x0351; - -enum SQL_ACCESSIBLE_PROCEDURES=20; -enum SQL_ACCESSIBLE_TABLES=19; -enum SQL_ALL_TYPES=0; -enum SQL_ALTER_TABLE=86; -enum SQL_API_SQLALLOCCONNECT=1; -enum SQL_API_SQLALLOCENV=2; -enum SQL_API_SQLALLOCSTMT=3; -enum SQL_API_SQLBINDCOL=4; -enum SQL_API_SQLCANCEL=5; -enum SQL_API_SQLCOLUMNS=40; -enum SQL_API_SQLCONNECT=7; -enum SQL_API_SQLDATASOURCES=57; -enum SQL_API_SQLDESCRIBECOL=8; -enum SQL_API_SQLDISCONNECT=9; -enum SQL_API_SQLERROR=10; -enum SQL_API_SQLEXECDIRECT=11; -enum SQL_API_SQLEXECUTE=12; -enum SQL_API_SQLFETCH=13; -enum SQL_API_SQLFREECONNECT=14; -enum SQL_API_SQLFREEENV=15; -enum SQL_API_SQLFREESTMT=16; -enum SQL_API_SQLGETCONNECTOPTION=42; -enum SQL_API_SQLGETCURSORNAME=17; -enum SQL_API_SQLGETDATA=43; -enum SQL_API_SQLGETFUNCTIONS=44; -enum SQL_API_SQLGETINFO=45; -enum SQL_API_SQLGETSTMTOPTION=46; -enum SQL_API_SQLGETTYPEINFO=47; -enum SQL_API_SQLNUMRESULTCOLS=18; -enum SQL_API_SQLPARAMDATA=48; -enum SQL_API_SQLPREPARE=19; -enum SQL_API_SQLPUTDATA=49; -enum SQL_API_SQLROWCOUNT=20; -enum SQL_API_SQLSETCONNECTOPTION=50; -enum SQL_API_SQLSETCURSORNAME=21; -enum SQL_API_SQLSETPARAM=22; -enum SQL_API_SQLSETSTMTOPTION=51; -enum SQL_API_SQLSPECIALCOLUMNS=52; -enum SQL_API_SQLSTATISTICS=53; -enum SQL_API_SQLTABLES=54; -enum SQL_API_SQLTRANSACT=23; - -enum SQL_CB_DELETE=0; -enum SQL_CB_CLOSE=1; -enum SQL_CB_PRESERVE=2; - -enum SQL_CHAR=1; -enum SQL_CLOSE=0; -enum SQL_COMMIT=0; -enum SQL_CURSOR_COMMIT_BEHAVIOR=23; -enum SQL_DATA_AT_EXEC=-2; -enum SQL_DATA_SOURCE_NAME=2; -enum SQL_DATA_SOURCE_READ_ONLY=25; -enum SQL_DBMS_NAME=17; -enum SQL_DBMS_VER=18; -enum SQL_DECIMAL=3; -enum SQL_DEFAULT_TXN_ISOLATION=26; -enum SQL_DOUBLE=8; -enum SQL_DROP=1; -enum SQL_ERROR=-1; - -enum SQL_FD_FETCH_NEXT=1; -enum SQL_FD_FETCH_FIRST=2; -enum SQL_FD_FETCH_LAST=4; -enum SQL_FD_FETCH_PRIOR=8; -enum SQL_FD_FETCH_ABSOLUTE=16; -enum SQL_FD_FETCH_RELATIVE=32; - -enum SQL_FETCH_ABSOLUTE=5; -enum SQL_FETCH_DIRECTION=8; -enum SQL_FETCH_FIRST=2; -enum SQL_FETCH_LAST=3; -enum SQL_FETCH_NEXT=1; -enum SQL_FETCH_PRIOR=4; -enum SQL_FETCH_RELATIVE=6; -enum SQL_FLOAT=6; -enum SQL_GD_ANY_COLUMN=1; -enum SQL_GD_ANY_ORDER=2; -enum SQL_GETDATA_EXTENSIONS=81; -enum SQL_IC_LOWER=2; -enum SQL_IC_MIXED=4; -enum SQL_IC_SENSITIVE=3; -enum SQL_IC_UPPER=1; -enum SQL_IDENTIFIER_CASE=28; -enum SQL_IDENTIFIER_QUOTE_CHAR=29; - -enum SQL_INDEX_ALL=1; -enum SQL_INDEX_CLUSTERED=1; -enum SQL_INDEX_HASHED=2; -enum SQL_INDEX_OTHER=3; -enum SQL_INDEX_UNIQUE=0; - -enum SQL_INTEGER=4; -enum SQL_INTEGRITY=73; -enum SQL_INVALID_HANDLE=-2; - -enum SQL_MAX_CATALOG_NAME_LEN=34; -enum SQL_MAX_COLUMN_NAME_LEN=30; -enum SQL_MAX_COLUMNS_IN_GROUP_BY=97; -enum SQL_MAX_COLUMNS_IN_INDEX=98; -enum SQL_MAX_COLUMNS_IN_ORDER_BY=99; -enum SQL_MAX_COLUMNS_IN_SELECT=100; -enum SQL_MAX_COLUMNS_IN_TABLE=101; -enum SQL_MAX_CURSOR_NAME_LEN=31; -enum SQL_MAX_INDEX_SIZE=102; -enum SQL_MAX_MESSAGE_LENGTH=512; -enum SQL_MAX_ROW_SIZE=104; -enum SQL_MAX_SCHEMA_NAME_LEN=32; -enum SQL_MAX_STATEMENT_LEN=105; -enum SQL_MAX_TABLE_NAME_LEN=35; -enum SQL_MAX_TABLES_IN_SELECT=106; -enum SQL_MAX_USER_NAME_LEN=107; - -enum SQL_MAXIMUM_CATALOG_NAME_LENGTH=SQL_MAX_CATALOG_NAME_LEN; -enum SQL_MAXIMUM_COLUMN_NAME_LENGTH=SQL_MAX_COLUMN_NAME_LEN; -enum SQL_MAXIMUM_COLUMNS_IN_GROUP_BY=SQL_MAX_COLUMNS_IN_GROUP_BY; -enum SQL_MAXIMUM_COLUMNS_IN_INDEX=SQL_MAX_COLUMNS_IN_INDEX; -enum SQL_MAXIMUM_COLUMNS_IN_ORDER_BY=SQL_MAX_COLUMNS_IN_ORDER_BY; -enum SQL_MAXIMUM_COLUMNS_IN_SELECT=SQL_MAX_COLUMNS_IN_SELECT; -enum SQL_MAXIMUM_CURSOR_NAME_LENGTH=SQL_MAX_CURSOR_NAME_LEN; -enum SQL_MAXIMUM_INDEX_SIZE=SQL_MAX_INDEX_SIZE; -enum SQL_MAXIMUM_ROW_SIZE=SQL_MAX_ROW_SIZE; -enum SQL_MAXIMUM_SCHEMA_NAME_LENGTH=SQL_MAX_SCHEMA_NAME_LEN; -enum SQL_MAXIMUM_STATEMENT_LENGTH=SQL_MAX_STATEMENT_LEN; -enum SQL_MAXIMUM_TABLES_IN_SELECT=SQL_MAX_TABLES_IN_SELECT; -enum SQL_MAXIMUM_USER_NAME_LENGTH=SQL_MAX_USER_NAME_LEN; - -enum SQL_NC_HIGH=0; -enum SQL_NC_LOW=1; -enum SQL_NEED_DATA=99; -enum SQL_NO_NULLS=0; -enum SQL_NTS=-3; -enum LONG SQL_NTSL=-3; -enum SQL_NULL_COLLATION=85; -enum SQL_NULL_DATA=-1; -enum SQL_NULL_HDBC=0; -enum SQL_NULL_HENV=0; -enum SQL_NULL_HSTMT=0; -enum SQL_NULLABLE=1; -enum SQL_NULLABLE_UNKNOWN=2; -enum SQL_NUMERIC=2; -enum SQL_ORDER_BY_COLUMNS_IN_SELECT=90; -enum SQL_PC_PSEUDO=2; -enum SQL_PC_UNKNOWN=0; -enum SQL_REAL=7; -enum SQL_RESET_PARAMS=3; -enum SQL_ROLLBACK=1; -enum SQL_SCCO_LOCK=2; -enum SQL_SCCO_OPT_ROWVER=4; -enum SQL_SCCO_OPT_VALUES=8; -enum SQL_SCCO_READ_ONLY=1; -enum SQL_SCOPE_CURROW=0; -enum SQL_SCOPE_SESSION=2; -enum SQL_SCOPE_TRANSACTION=1; -enum SQL_SCROLL_CONCURRENCY=43; -enum SQL_SEARCH_PATTERN_ESCAPE=14; -enum SQL_SERVER_NAME=13; -enum SQL_SMALLINT=5; -enum SQL_SPECIAL_CHARACTERS=94; -enum SQL_STILL_EXECUTING=2; -//MACRO #define SQL_SUCCEEDED(rc) (((rc)&(~1))==0) - -enum SQL_SUCCESS=0; -enum SQL_SUCCESS_WITH_INFO=1; - -enum SQL_TC_ALL=2; -enum SQL_TC_DDL_COMMIT=3; -enum SQL_TC_DDL_IGNORE=4; -enum SQL_TC_DML=1; -enum SQL_TC_NONE=0; - - -enum SQL_TXN_CAPABLE=46; -enum SQL_TXN_ISOLATION_OPTION=72; -enum SQL_TXN_READ_COMMITTED=2; -enum SQL_TXN_READ_UNCOMMITTED=1; -enum SQL_TXN_REPEATABLE_READ=4; -enum SQL_TXN_SERIALIZABLE=8; - -enum SQL_TRANSACTION_CAPABLE=SQL_TXN_CAPABLE; -enum SQL_TRANSACTION_ISOLATION_OPTION=SQL_TXN_ISOLATION_OPTION; -enum SQL_TRANSACTION_READ_COMMITTED=SQL_TXN_READ_COMMITTED; -enum SQL_TRANSACTION_READ_UNCOMMITTED=SQL_TXN_READ_UNCOMMITTED; -enum SQL_TRANSACTION_REPEATABLE_READ=SQL_TXN_REPEATABLE_READ; -enum SQL_TRANSACTION_SERIALIZABLE=SQL_TXN_SERIALIZABLE; - -enum SQL_UNBIND=2; -enum SQL_UNKNOWN_TYPE=0; -enum SQL_USER_NAME=47; -enum SQL_VARCHAR=12; - -static if (ODBCVER >= 0x0200) -{ -enum SQL_AT_ADD_COLUMN = 1; -enum SQL_AT_DROP_COLUMN = 2; -} - -static if (ODBCVER >= 0x0201) -{ -enum SQL_OJ_LEFT = 1; -enum SQL_OJ_RIGHT = 2; -enum SQL_OJ_FULL = 4; -enum SQL_OJ_NESTED = 8; -enum SQL_OJ_NOT_ORDERED = 16; -enum SQL_OJ_INNER = 32; -enum SQL_OJ_ALL_COMPARISON_OPS = 64; -} - -static if (ODBCVER >= 0x0300) -{ -enum SQL_AM_CONNECTION=1; -enum SQL_AM_NONE=0; -enum SQL_AM_STATEMENT=2; -enum SQL_API_SQLALLOCHANDLE=1001; -enum SQL_API_SQLBINDPARAM=1002; -enum SQL_API_SQLCLOSECURSOR=1003; -enum SQL_API_SQLCOLATTRIBUTE=6; -enum SQL_API_SQLCOPYDESC=1004; -enum SQL_API_SQLENDTRAN=1005; -enum SQL_API_SQLFETCHSCROLL=1021; -enum SQL_API_SQLFREEHANDLE=1006; -enum SQL_API_SQLGETCONNECTATTR=1007; -enum SQL_API_SQLGETDESCFIELD=1008; -enum SQL_API_SQLGETDESCREC=1009; -enum SQL_API_SQLGETDIAGFIELD=1010; -enum SQL_API_SQLGETDIAGREC=1011; -enum SQL_API_SQLGETENVATTR=1012; -enum SQL_API_SQLGETSTMTATTR=1014; -enum SQL_API_SQLSETCONNECTATTR=1016; -enum SQL_API_SQLSETDESCFIELD=1017; -enum SQL_API_SQLSETDESCREC=1018; -enum SQL_API_SQLSETENVATTR=1019; -enum SQL_API_SQLSETSTMTATTR=1020; -enum SQL_ARD_TYPE=-99; -enum SQL_AT_ADD_CONSTRAINT=8; -enum SQL_ATTR_APP_PARAM_DESC=10011; -enum SQL_ATTR_APP_ROW_DESC=10010; -enum SQL_ATTR_AUTO_IPD=10001; -enum SQL_ATTR_CURSOR_SCROLLABLE=-1; -enum SQL_ATTR_CURSOR_SENSITIVITY=-2; -enum SQL_ATTR_IMP_PARAM_DESC=10013; -enum SQL_ATTR_IMP_ROW_DESC=10012; -enum SQL_ATTR_METADATA_ID=10014; -enum SQL_ATTR_OUTPUT_NTS=10001; -enum SQL_CATALOG_NAME=10003; -enum SQL_CODE_DATE=1; -enum SQL_CODE_TIME=2; -enum SQL_CODE_TIMESTAMP=3; -enum SQL_COLLATION_SEQ=10004; -enum SQL_CURSOR_SENSITIVITY=10001; -enum SQL_DATE_LEN=10; -enum SQL_DATETIME=9; -enum SQL_DEFAULT=99; - -enum SQL_DESC_ALLOC_AUTO=1; -enum SQL_DESC_ALLOC_USER=2; -enum SQL_DESC_ALLOC_TYPE=1099; -enum SQL_DESC_COUNT=1001; -enum SQL_DESC_TYPE=1002; -enum SQL_DESC_LENGTH=1003; -enum SQL_DESC_OCTET_LENGTH_PTR=1004; -enum SQL_DESC_PRECISION=1005; -enum SQL_DESC_SCALE=1006; -enum SQL_DESC_DATETIME_INTERVAL_CODE=1007; -enum SQL_DESC_NULLABLE=1008; -enum SQL_DESC_INDICATOR_PTR=1009; -enum SQL_DESC_DATA_PTR=1010; -enum SQL_DESC_NAME=1011; -enum SQL_DESC_UNNAMED=1012; -enum SQL_DESC_OCTET_LENGTH=1013; - -enum SQL_DESCRIBE_PARAMETER=10002; - -enum SQL_DIAG_ALTER_DOMAIN=3; -enum SQL_DIAG_ALTER_TABLE=4; -enum SQL_DIAG_CALL=7; -enum SQL_DIAG_CLASS_ORIGIN=8; -enum SQL_DIAG_CONNECTION_NAME=10; -enum SQL_DIAG_CREATE_ASSERTION=6; -enum SQL_DIAG_CREATE_CHARACTER_SET=8; -enum SQL_DIAG_CREATE_COLLATION=10; -enum SQL_DIAG_CREATE_DOMAIN=23; -enum SQL_DIAG_CREATE_INDEX=-1; -enum SQL_DIAG_CREATE_SCHEMA=64; -enum SQL_DIAG_CREATE_TABLE=77; -enum SQL_DIAG_CREATE_TRANSLATION=79; -enum SQL_DIAG_CREATE_VIEW=84; -enum SQL_DIAG_DELETE_WHERE=19; -enum SQL_DIAG_DROP_ASSERTION=24; -enum SQL_DIAG_DROP_CHARACTER_SET=25; -enum SQL_DIAG_DROP_COLLATION=26; -enum SQL_DIAG_DROP_DOMAIN=27; -enum SQL_DIAG_DROP_INDEX=(-2); -enum SQL_DIAG_DROP_SCHEMA=31; -enum SQL_DIAG_DROP_TABLE=32; -enum SQL_DIAG_DROP_TRANSLATION=33; -enum SQL_DIAG_DROP_VIEW=36; -enum SQL_DIAG_DYNAMIC_DELETE_CURSOR=38; -enum SQL_DIAG_DYNAMIC_FUNCTION=7; -enum SQL_DIAG_DYNAMIC_FUNCTION_CODE=12; -enum SQL_DIAG_DYNAMIC_UPDATE_CURSOR=81; -enum SQL_DIAG_GRANT=48; -enum SQL_DIAG_INSERT=50; -enum SQL_DIAG_MESSAGE_TEXT=6; -enum SQL_DIAG_NATIVE=5; -enum SQL_DIAG_NUMBER=2; -enum SQL_DIAG_RETURNCODE=1; -enum SQL_DIAG_REVOKE=59; -enum SQL_DIAG_ROW_COUNT=3; -enum SQL_DIAG_SELECT_CURSOR=85; -enum SQL_DIAG_SERVER_NAME=11; -enum SQL_DIAG_SQLSTATE=4; -enum SQL_DIAG_SUBCLASS_ORIGIN=9; -enum SQL_DIAG_UNKNOWN_STATEMENT=0; -enum SQL_DIAG_UPDATE_WHERE=82; - -enum SQL_FALSE=0; -enum SQL_HANDLE_DBC=2; -enum SQL_HANDLE_DESC=4; -enum SQL_HANDLE_ENV=1; -enum SQL_HANDLE_STMT=3; -enum SQL_INSENSITIVE=1; -enum SQL_MAX_CONCURRENT_ACTIVITIES=1; -enum SQL_MAX_DRIVER_CONNECTIONS=0; -enum SQL_MAX_IDENTIFIER_LEN=10005; -enum SQL_MAXIMUM_CONCURRENT_ACTIVITIES=SQL_MAX_CONCURRENT_ACTIVITIES; -enum SQL_MAXIMUM_DRIVER_CONNECTIONS=SQL_MAX_DRIVER_CONNECTIONS; -enum SQL_MAXIMUM_IDENTIFIER_LENGTH=SQL_MAX_IDENTIFIER_LEN; -enum SQL_NAMED=0; -enum SQL_NO_DATA=100; -enum SQL_NONSCROLLABLE=0; -enum SQL_NULL_HANDLE=0L; -enum SQL_NULL_HDESC=0; -enum SQL_OJ_CAPABILITIES=115; -enum SQL_OUTER_JOIN_CAPABILITIES=SQL_OJ_CAPABILITIES; -enum SQL_PC_NON_PSEUDO=1; - -enum SQL_PRED_NONE=0; -enum SQL_PRED_CHAR=1; -enum SQL_PRED_BASIC=2; - -enum SQL_ROW_IDENTIFIER=1; -enum SQL_SCROLLABLE=1; -enum SQL_SENSITIVE=2; -enum SQL_TIME_LEN=8; -enum SQL_TIMESTAMP_LEN=19; -enum SQL_TRUE=1; -enum SQL_TYPE_DATE=91; -enum SQL_TYPE_TIME=92; -enum SQL_TYPE_TIMESTAMP=93; -enum SQL_UNNAMED=1; -enum SQL_UNSPECIFIED=0; -enum SQL_XOPEN_CLI_YEAR=10000; -}//#endif /* ODBCVER >= 0x0300 */ - -extern (System) { - deprecated { - SQLRETURN SQLAllocConnect(SQLHENV, SQLHDBC*); - SQLRETURN SQLAllocEnv(SQLHENV*); - SQLRETURN SQLAllocStmt(SQLHDBC, SQLHSTMT*); - SQLRETURN SQLError(SQLHENV, SQLHDBC, SQLHSTMT, SQLCHAR*, SQLINTEGER*, SQLCHAR*, SQLSMALLINT, SQLSMALLINT*); - SQLRETURN SQLFreeConnect(SQLHDBC); - SQLRETURN SQLFreeEnv(SQLHENV); - SQLRETURN SQLSetParam(SQLHSTMT, SQLUSMALLINT, SQLSMALLINT, SQLSMALLINT, SQLULEN, SQLSMALLINT, SQLPOINTER, SQLLEN*); - SQLRETURN SQLGetConnectOption(SQLHDBC, SQLUSMALLINT, SQLPOINTER); - SQLRETURN SQLGetStmtOption(SQLHSTMT, SQLUSMALLINT, SQLPOINTER); - SQLRETURN SQLSetConnectOption(SQLHDBC, SQLUSMALLINT, SQLULEN); - SQLRETURN SQLSetStmtOption(SQLHSTMT, SQLUSMALLINT, SQLROWCOUNT); - } - SQLRETURN SQLBindCol(SQLHSTMT, SQLUSMALLINT, SQLSMALLINT, SQLPOINTER, SQLLEN, SQLLEN*); - SQLRETURN SQLCancel(SQLHSTMT); - SQLRETURN SQLConnect(SQLHDBC, SQLCHAR*, SQLSMALLINT, SQLCHAR*, SQLSMALLINT, SQLCHAR*, SQLSMALLINT); - SQLRETURN SQLDescribeCol(SQLHSTMT, SQLUSMALLINT, SQLCHAR*, SQLSMALLINT, SQLSMALLINT*, SQLSMALLINT*, SQLULEN*, SQLSMALLINT*, SQLSMALLINT*); - SQLRETURN SQLDisconnect(SQLHDBC); - SQLRETURN SQLExecDirect(SQLHSTMT, SQLCHAR*, SQLINTEGER); - SQLRETURN SQLExecute(SQLHSTMT); - SQLRETURN SQLFetch(SQLHSTMT); - SQLRETURN SQLFreeStmt(SQLHSTMT, SQLUSMALLINT); - SQLRETURN SQLGetCursorName(SQLHSTMT, SQLCHAR*, SQLSMALLINT, SQLSMALLINT*); - SQLRETURN SQLNumResultCols(SQLHSTMT, SQLSMALLINT*); - SQLRETURN SQLPrepare(SQLHSTMT, SQLCHAR*, SQLINTEGER); - SQLRETURN SQLRowCount(SQLHSTMT, SQLLEN*); - SQLRETURN SQLSetCursorName(SQLHSTMT, SQLCHAR*, SQLSMALLINT); - SQLRETURN SQLTransact(SQLHENV, SQLHDBC, SQLUSMALLINT); - SQLRETURN SQLColumns(SQLHSTMT, SQLCHAR*, SQLSMALLINT, SQLCHAR*, SQLSMALLINT, SQLCHAR*, SQLSMALLINT, SQLCHAR*, SQLSMALLINT); - SQLRETURN SQLGetData(SQLHSTMT, SQLUSMALLINT, SQLSMALLINT, SQLPOINTER, SQLLEN, SQLLEN*); - SQLRETURN SQLGetFunctions(SQLHDBC, SQLUSMALLINT, SQLUSMALLINT*); - SQLRETURN SQLGetInfo(SQLHDBC, SQLUSMALLINT, SQLPOINTER, SQLSMALLINT, SQLSMALLINT*); - SQLRETURN SQLGetTypeInfo(SQLHSTMT, SQLSMALLINT); - SQLRETURN SQLParamData(SQLHSTMT, SQLPOINTER*); - SQLRETURN SQLPutData(SQLHSTMT, SQLPOINTER, SQLLEN); - SQLRETURN SQLSpecialColumns(SQLHSTMT, SQLUSMALLINT, SQLCHAR*, SQLSMALLINT, SQLCHAR*, SQLSMALLINT, SQLCHAR*, SQLSMALLINT, SQLUSMALLINT, SQLUSMALLINT); - SQLRETURN SQLStatistics(SQLHSTMT, SQLCHAR*, SQLSMALLINT, SQLCHAR*, SQLSMALLINT, SQLCHAR*, SQLSMALLINT, SQLUSMALLINT, SQLUSMALLINT); - SQLRETURN SQLTables(SQLHSTMT, SQLCHAR*, SQLSMALLINT, SQLCHAR*, SQLSMALLINT, SQLCHAR*, SQLSMALLINT, SQLCHAR*, SQLSMALLINT); - SQLRETURN SQLDataSources(SQLHENV, SQLUSMALLINT, SQLCHAR*, SQLSMALLINT, SQLSMALLINT*, SQLCHAR*, SQLSMALLINT, SQLSMALLINT*); - - static if (ODBCVER >= 0x0300) - { - SQLRETURN SQLAllocHandle(SQLSMALLINT, SQLHANDLE, SQLHANDLE*); - SQLRETURN SQLBindParam(SQLHSTMT, SQLUSMALLINT, SQLSMALLINT, SQLSMALLINT, SQLULEN, SQLSMALLINT, SQLPOINTER, SQLLEN*); - SQLRETURN SQLCloseCursor(SQLHSTMT); - SQLRETURN SQLColAttribute(SQLHSTMT, SQLUSMALLINT, SQLUSMALLINT, SQLPOINTER, SQLSMALLINT, SQLSMALLINT*, SQLPOINTER); - SQLRETURN SQLCopyDesc(SQLHDESC, SQLHDESC); - SQLRETURN SQLEndTran(SQLSMALLINT, SQLHANDLE, SQLSMALLINT); - SQLRETURN SQLFetchScroll(SQLHSTMT, SQLSMALLINT, SQLROWOFFSET); - SQLRETURN SQLFreeHandle(SQLSMALLINT, SQLHANDLE); - SQLRETURN SQLGetConnectAttr(SQLHDBC, SQLINTEGER, SQLPOINTER, SQLINTEGER, SQLINTEGER*); - SQLRETURN SQLGetDescField(SQLHDESC, SQLSMALLINT, SQLSMALLINT, SQLPOINTER, SQLINTEGER, SQLINTEGER*); - SQLRETURN SQLGetDescRec(SQLHDESC, SQLSMALLINT, SQLCHAR*, SQLSMALLINT, SQLSMALLINT*, - SQLSMALLINT*, SQLSMALLINT*, SQLLEN*, SQLSMALLINT*, SQLSMALLINT*, SQLSMALLINT*); - SQLRETURN SQLGetDiagField(SQLSMALLINT, SQLHANDLE, SQLSMALLINT, SQLSMALLINT, SQLPOINTER, SQLSMALLINT, SQLSMALLINT*); - SQLRETURN SQLGetDiagRec(SQLSMALLINT, SQLHANDLE, SQLSMALLINT, SQLCHAR*, SQLINTEGER*, SQLCHAR*, SQLSMALLINT, SQLSMALLINT*); - SQLRETURN SQLGetEnvAttr(SQLHENV, SQLINTEGER, SQLPOINTER, SQLINTEGER, SQLINTEGER*); - SQLRETURN SQLGetStmtAttr(SQLHSTMT, SQLINTEGER, SQLPOINTER, SQLINTEGER, SQLINTEGER*); - SQLRETURN SQLSetConnectAttr(SQLHDBC, SQLINTEGER, SQLPOINTER, SQLINTEGER); - SQLRETURN SQLSetDescField(SQLHDESC, SQLSMALLINT, SQLSMALLINT, SQLPOINTER, SQLINTEGER); - SQLRETURN SQLSetDescRec(SQLHDESC, SQLSMALLINT, SQLSMALLINT, SQLSMALLINT, SQLLEN, SQLSMALLINT, - SQLSMALLINT, SQLPOINTER, SQLLEN*, SQLLEN*); - SQLRETURN SQLSetEnvAttr(SQLHENV, SQLINTEGER, SQLPOINTER, SQLINTEGER); - SQLRETURN SQLSetStmtAttr(SQLHSTMT, SQLINTEGER, SQLPOINTER, SQLINTEGER); - }/* (ODBCVER >= 0x0300) */ -} diff --git a/phobos/etc/c/odbc/sqlext.d b/phobos/etc/c/odbc/sqlext.d deleted file mode 100644 index 7bec9eb..0000000 --- a/phobos/etc/c/odbc/sqlext.d +++ /dev/null @@ -1,1306 +0,0 @@ -/** - * Windows API header module - * - * Translated from MinGW Windows headers - * - * License: $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost License 1.0) - * Source: $(PHOBOSSRC etc/c/odbc/_sqlext.d) - -See_Also: $(LINK2 https://docs.microsoft.com/en-us/sql/odbc/reference/syntax/odbc-api-reference, - ODBC API Reference on MSDN) - */ -module etc.c.odbc.sqlext; - -/* Conversion notes: - The MinGW file was a horrible mess. All of the #defines were sorted alphabetically, - which is crazy. This file needs a lot of work. - In MinGW, sqlext #includes sqlucode, but sqlucode #includes sqlext, - creating a circular dependency! -*/ - -public import etc.c.odbc.sql; - -enum SQL_SPEC_MAJOR = 3; -enum SQL_SPEC_MINOR = 51; -const char[] SQL_SPEC_STRING = "03.51"; -enum SQL_ACCESS_MODE = 101; -enum SQL_ACTIVE_CONNECTIONS = 0; -enum SQL_ACTIVE_STATEMENTS = 1; - -enum SQL_DATE = 9; -enum SQL_TIME = 10; -enum SQL_SIGNED_OFFSET = -20; -enum SQL_TINYINT = -6; -enum SQL_TIMESTAMP = 11; -enum SQL_UNSIGNED_OFFSET = -22; - -enum SQL_ADD = 4; -enum SQL_ALL_EXCEPT_LIKE = 2; - -enum SQL_API_ALL_FUNCTIONS = 0; -enum SQL_API_SQLCOLATTRIBUTES = 6; -enum SQL_API_SQLDRIVERCONNECT = 41; -enum SQL_API_SQLBROWSECONNECT = 55; -enum SQL_API_SQLCOLUMNPRIVILEGES = 56; -enum SQL_API_SQLDESCRIBEPARAM = 58; -enum SQL_API_SQLEXTENDEDFETCH = 59; -enum SQL_API_SQLFOREIGNKEYS = 60; -enum SQL_API_SQLMORERESULTS = 61; -enum SQL_API_SQLNATIVESQL = 62; -enum SQL_API_SQLNUMPARAMS = 63; -enum SQL_API_SQLPARAMOPTIONS = 64; -enum SQL_API_SQLPRIMARYKEYS = 65; -enum SQL_API_SQLPROCEDURECOLUMNS = 66; -enum SQL_API_SQLPROCEDURES = 67; -enum SQL_API_SQLSETPOS = 68; -enum SQL_API_SQLSETSCROLLOPTIONS = 69; -enum SQL_API_SQLTABLEPRIVILEGES = 70; -enum SQL_API_SQLDRIVERS = 71; -enum SQL_API_SQLBINDPARAMETER = 72; -enum SQL_API_LOADBYORDINAL = 199; - -enum SQL_ASYNC_ENABLE = 4; -enum SQL_ASYNC_ENABLE_OFF = 0UL; -enum SQL_ASYNC_ENABLE_ON = 1UL; -enum SQL_ASYNC_ENABLE_DEFAULT = SQL_ASYNC_ENABLE_OFF; - -enum SQL_ATTR_CONNECTION_DEAD = 1209; -enum SQL_ATTR_READONLY = 0; -enum SQL_ATTR_READWRITE_UNKNOWN = 2; -enum SQL_ATTR_WRITE = 1; - -enum SQL_AUTOCOMMIT = 102; -enum SQL_AUTOCOMMIT_OFF = 0UL; -enum SQL_AUTOCOMMIT_ON = 1UL; -enum SQL_AUTOCOMMIT_DEFAULT = SQL_AUTOCOMMIT_ON; -enum SQL_BEST_ROWID = 1; -enum SQL_BIGINT = -5; -enum SQL_BINARY = -2; -enum SQL_BIND_BY_COLUMN = 0UL; -enum SQL_BIND_TYPE = 5; -enum SQL_BIND_TYPE_DEFAULT = SQL_BIND_BY_COLUMN; -enum SQL_BIT = -7; - -enum SQL_BOOKMARK_PERSISTENCE = 82; - -// for BOOKMARK_PERSISTENCE -enum SQL_BP_CLOSE = 1; -enum SQL_BP_DELETE = 2; -enum SQL_BP_DROP = 4; -enum SQL_BP_TRANSACTION = 8; -enum SQL_BP_UPDATE = 16; -enum SQL_BP_OTHER_HSTMT = 32; -enum SQL_BP_SCROLL = 64; - -enum SQL_C_BINARY = SQL_BINARY; -enum SQL_C_BIT = SQL_BIT; -enum SQL_C_CHAR = SQL_CHAR; -enum SQL_C_DATE = SQL_DATE; -enum SQL_C_DOUBLE = SQL_DOUBLE; -enum SQL_C_FLOAT = SQL_REAL; -enum SQL_C_LONG = SQL_INTEGER; -enum SQL_C_SHORT = SQL_SMALLINT; -enum SQL_C_SLONG = SQL_C_LONG+SQL_SIGNED_OFFSET; -enum SQL_C_SSHORT = SQL_C_SHORT+SQL_SIGNED_OFFSET; -enum SQL_C_STINYINT = SQL_TINYINT+SQL_SIGNED_OFFSET; -enum SQL_C_TIME = SQL_TIME; -enum SQL_C_TIMESTAMP = SQL_TIMESTAMP; -enum SQL_C_TINYINT = SQL_TINYINT; -enum SQL_C_ULONG = SQL_C_LONG+SQL_UNSIGNED_OFFSET; -enum SQL_C_USHORT = SQL_C_SHORT+SQL_UNSIGNED_OFFSET; -enum SQL_C_UTINYINT = SQL_TINYINT+SQL_UNSIGNED_OFFSET; -enum SQL_C_BOOKMARK = SQL_C_ULONG; -enum SQL_C_DEFAULT = 99; - -enum SQL_CASCADE = 0; -enum SQL_CB_NON_NULL = 1; -enum SQL_CB_NULL = 0; -deprecated { -enum SQL_CC_CLOSE = SQL_CB_CLOSE;/* deprecated */ -enum SQL_CC_DELETE = SQL_CB_DELETE;/* deprecated */ -enum SQL_CC_PRESERVE = SQL_CB_PRESERVE;/* deprecated */ -} -enum SQL_CD_FALSE = 0L; -enum SQL_CD_TRUE = 1L; - -enum SQL_CN_ANY = 2; -enum SQL_CN_DIFFERENT = 1; -enum SQL_CN_NONE = 0; - -enum SQL_COLUMN_ALIAS = 87; - -enum SQL_COLUMN_COUNT = 0; -enum SQL_COLUMN_NAME = 1; -enum SQL_COLUMN_DISPLAY_SIZE = 6; -enum SQL_COLUMN_LABEL = 18; -enum SQL_COLUMN_LENGTH = 3; -enum SQL_COLUMN_MONEY = 9; -enum SQL_COLUMN_NULLABLE = 7; -enum SQL_COLUMN_OWNER_NAME = 16; -enum SQL_COLUMN_PRECISION = 4; -enum SQL_COLUMN_QUALIFIER_NAME = 17; -enum SQL_COLUMN_SCALE = 5; -enum SQL_COLUMN_UNSIGNED = 8; -enum SQL_COLUMN_UPDATABLE = 10; -enum SQL_COLUMN_AUTO_INCREMENT = 11; -enum SQL_COLUMN_CASE_SENSITIVE = 12; -enum SQL_COLUMN_SEARCHABLE = 13; -enum SQL_COLUMN_TYPE = 2; -enum SQL_COLUMN_TYPE_NAME = 14; -enum SQL_COLUMN_TABLE_NAME = 15; - -enum SQL_CONCAT_NULL_BEHAVIOR = 22; - -enum SQL_CONCUR_READ_ONLY = 1; -enum SQL_CONCUR_DEFAULT = SQL_CONCUR_READ_ONLY; -enum SQL_CONCUR_LOCK = 2; -enum SQL_CONCUR_ROWVER = 3; -enum SQL_CONCUR_TIMESTAMP = SQL_CONCUR_ROWVER;/* deprecated */ -enum SQL_CONCUR_VALUES = 4; - -enum SQL_CONCURRENCY = 7; -enum SQL_CONVERT_BIGINT = 53; -enum SQL_CONVERT_BINARY = 54; -enum SQL_CONVERT_BIT = 55; -enum SQL_CONVERT_CHAR = 56; -enum SQL_CONVERT_DATE = 57; -enum SQL_CONVERT_DECIMAL = 58; -enum SQL_CONVERT_DOUBLE = 59; -enum SQL_CONVERT_FLOAT = 60; -enum SQL_CONVERT_FUNCTIONS = 48; -enum SQL_CONVERT_INTEGER = 61; -enum SQL_CONVERT_LONGVARBINARY = 71; -enum SQL_CONVERT_LONGVARCHAR = 62; -enum SQL_CONVERT_NUMERIC = 63; -enum SQL_CONVERT_REAL = 64; -enum SQL_CONVERT_SMALLINT = 65; -enum SQL_CONVERT_TIME = 66; -enum SQL_CONVERT_TIMESTAMP = 67; -enum SQL_CONVERT_TINYINT = 68; -enum SQL_CONVERT_VARBINARY = 69; -enum SQL_CONVERT_VARCHAR = 70; -enum SQL_CORRELATION_NAME = 74; -enum SQL_CR_CLOSE = SQL_CB_CLOSE;/* deprecated */ -enum SQL_CR_DELETE = SQL_CB_DELETE;/* deprecated */ -enum SQL_CR_PRESERVE = SQL_CB_PRESERVE;/* deprecated */ - -enum : ULONG { - SQL_CUR_USE_IF_NEEDED = 0, - SQL_CUR_USE_ODBC, - SQL_CUR_USE_DRIVER, - SQL_CUR_DEFAULT = SQL_CUR_USE_DRIVER -} - -enum SQL_CURRENT_QUALIFIER = 109; -enum SQL_CURSOR_DYNAMIC = 2UL; -enum SQL_CURSOR_FORWARD_ONLY = 0UL; -enum SQL_CURSOR_KEYSET_DRIVEN = 1UL; -enum SQL_CURSOR_ROLLBACK_BEHAVIOR = 24; -enum SQL_CURSOR_STATIC = 3UL; -enum SQL_CURSOR_TYPE = 6; -enum SQL_CURSOR_TYPE_DEFAULT = SQL_CURSOR_FORWARD_ONLY; - -enum SQL_CV_CASCADED = 0x00000004L; -enum SQL_CV_CHECK_OPTION = 0x00000002L; -enum SQL_CV_CREATE_VIEW = 0x00000001L; -enum SQL_CV_LOCAL = 0x00000008L; -enum SQL_CVT_BIGINT = 0x00004000L; -enum SQL_CVT_BINARY = 0x00000400L; -enum SQL_CVT_BIT = 0x00001000L; -enum SQL_CVT_CHAR = 0x00000001L; -enum SQL_CVT_DATE = 0x00008000L; -enum SQL_CVT_DECIMAL = 0x00000004L; -enum SQL_CVT_DOUBLE = 0x00000080L; -enum SQL_CVT_FLOAT = 0x00000020L; -enum SQL_CVT_INTEGER = 0x00000008L; -enum SQL_CVT_LONGVARBINARY = 0x00040000L; -enum SQL_CVT_LONGVARCHAR = 0x00000200L; -enum SQL_CVT_NUMERIC = 0x00000002L; -enum SQL_CVT_REAL = 0x00000040L; -enum SQL_CVT_SMALLINT = 0x00000010L; -enum SQL_CVT_TIME = 0x00010000L; -enum SQL_CVT_TIMESTAMP = 0x00020000L; -enum SQL_CVT_TINYINT = 0x00002000L; -enum SQL_CVT_VARBINARY = 0x00000800L; -enum SQL_CVT_VARCHAR = 0x00000100L; -enum SQL_DATABASE_NAME = 16;/* deprecated */ - -enum SQL_DEFAULT_PARAM = -5; -enum SQL_DELETE = 3; - -enum SQL_DRIVER_COMPLETE = 1; -enum SQL_DRIVER_COMPLETE_REQUIRED = 3; -enum SQL_DRIVER_HDBC = 3; -enum SQL_DRIVER_HENV = 4; -enum SQL_DRIVER_HLIB = 76; -enum SQL_DRIVER_HSTMT = 5; -enum SQL_DRIVER_NAME = 6; -enum SQL_DRIVER_NOPROMPT = 0; -enum SQL_DRIVER_ODBC_VER = 77; -enum SQL_DRIVER_PROMPT = 2; -enum SQL_DRIVER_VER = 7; - -enum SQL_DTC_ENLIST_EXPENSIVE = 1; -enum SQL_DTC_UNENLIST_EXPENSIVE = 2; -enum SQL_DTC_TRANSITION_COST = 1750; -enum SQL_ENSURE = 1; -enum SQL_ENTIRE_ROWSET = 0; -enum SQL_EXPRESSIONS_IN_ORDERBY = 27; -enum SQL_FD_FETCH_BOOKMARK = 128; -enum SQL_FD_FETCH_PREV = SQL_FD_FETCH_PRIOR;/* deprecated */ -enum SQL_FD_FETCH_RESUME = 64; -enum SQL_FETCH_BOOKMARK = 8; -enum SQL_FETCH_PREV = SQL_FETCH_PRIOR;/* deprecated */ -enum SQL_FETCH_RESUME = 7;/* deprecated */ - -enum SQL_FILE_NOT_SUPPORTED = 0x0000; -enum SQL_FILE_TABLE = 0x0001; -enum SQL_FILE_QUALIFIER = 0x0002; -enum SQL_FILE_CATALOG = SQL_FILE_QUALIFIER; -enum SQL_FILE_USAGE = 84; - -enum SQL_FN_CVT_CONVERT = 0x00000001L; -enum SQL_FN_NUM_ABS = 0x00000001L; -enum SQL_FN_NUM_ACOS = 0x00000002L; -enum SQL_FN_NUM_ASIN = 0x00000004L; -enum SQL_FN_NUM_ATAN = 0x00000008L; -enum SQL_FN_NUM_ATAN2 = 0x00000010L; -enum SQL_FN_NUM_CEILING = 0x00000020L; -enum SQL_FN_NUM_COS = 0x00000040L; -enum SQL_FN_NUM_COT = 0x00000080L; -enum SQL_FN_NUM_DEGREES = 0x00040000L; -enum SQL_FN_NUM_EXP = 0x00000100L; -enum SQL_FN_NUM_FLOOR = 0x00000200L; -enum SQL_FN_NUM_LOG = 0x00000400L; -enum SQL_FN_NUM_LOG10 = 0x00080000L; -enum SQL_FN_NUM_MOD = 0x00000800L; -enum SQL_FN_NUM_PI = 0x00010000L; -enum SQL_FN_NUM_POWER = 0x00100000L; -enum SQL_FN_NUM_RADIANS = 0x00200000L; -enum SQL_FN_NUM_RAND = 0x00020000L; -enum SQL_FN_NUM_ROUND = 0x00400000L; -enum SQL_FN_NUM_SIGN = 0x00001000L; -enum SQL_FN_NUM_SIN = 0x00002000L; -enum SQL_FN_NUM_SQRT = 0x00004000L; -enum SQL_FN_NUM_TAN = 0x00008000L; -enum SQL_FN_NUM_TRUNCATE = 0x00800000L; -enum SQL_FN_STR_ASCII = 0x00002000L; -enum SQL_FN_STR_CHAR = 0x00004000L; -enum SQL_FN_STR_CONCAT = 0x00000001L; -enum SQL_FN_STR_DIFFERENCE = 0x00008000L; -enum SQL_FN_STR_INSERT = 0x00000002L; -enum SQL_FN_STR_LCASE = 0x00000040L; -enum SQL_FN_STR_LEFT = 0x00000004L; -enum SQL_FN_STR_LENGTH = 0x00000010L; -enum SQL_FN_STR_LOCATE = 0x00000020L; -enum SQL_FN_STR_LOCATE_2 = 0x00010000L; -enum SQL_FN_STR_LTRIM = 0x00000008L; -enum SQL_FN_STR_REPEAT = 0x00000080L; -enum SQL_FN_STR_REPLACE = 0x00000100L; -enum SQL_FN_STR_RIGHT = 0x00000200L; -enum SQL_FN_STR_RTRIM = 0x00000400L; -enum SQL_FN_STR_SOUNDEX = 0x00020000L; -enum SQL_FN_STR_SPACE = 0x00040000L; -enum SQL_FN_STR_SUBSTRING = 0x00000800L; -enum SQL_FN_STR_UCASE = 0x00001000L; -enum SQL_FN_SYS_DBNAME = 0x00000002L; -enum SQL_FN_SYS_IFNULL = 0x00000004L; -enum SQL_FN_SYS_USERNAME = 0x00000001L; -enum SQL_FN_TD_CURDATE = 0x00000002L; -enum SQL_FN_TD_CURTIME = 0x00000200L; -enum SQL_FN_TD_DAYNAME = 0x00008000L; -enum SQL_FN_TD_DAYOFMONTH = 0x00000004L; -enum SQL_FN_TD_DAYOFWEEK = 0x00000008L; -enum SQL_FN_TD_DAYOFYEAR = 0x00000010L; -enum SQL_FN_TD_HOUR = 0x00000400L; -enum SQL_FN_TD_MINUTE = 0x00000800L; -enum SQL_FN_TD_MONTH = 0x00000020L; -enum SQL_FN_TD_MONTHNAME = 0x00010000L; -enum SQL_FN_TD_NOW = 0x00000001L; -enum SQL_FN_TD_QUARTER = 0x00000040L; -enum SQL_FN_TD_SECOND = 0x00001000L; -enum SQL_FN_TD_TIMESTAMPADD = 0x00002000L; -enum SQL_FN_TD_TIMESTAMPDIFF = 0x00004000L; -enum SQL_FN_TD_WEEK = 0x00000080L; -enum SQL_FN_TD_YEAR = 0x00000100L; -enum SQL_FN_TSI_DAY = 0x00000010L; -enum SQL_FN_TSI_FRAC_SECOND = 0x00000001L; -enum SQL_FN_TSI_HOUR = 0x00000008L; -enum SQL_FN_TSI_MINUTE = 0x00000004L; -enum SQL_FN_TSI_MONTH = 0x00000040L; -enum SQL_FN_TSI_QUARTER = 0x00000080L; -enum SQL_FN_TSI_SECOND = 0x00000002L; -enum SQL_FN_TSI_WEEK = 0x00000020L; -enum SQL_FN_TSI_YEAR = 0x00000100L; -enum SQL_GB_GROUP_BY_CONTAINS_SELECT = 2; -enum SQL_GB_GROUP_BY_EQUALS_SELECT = 1; -enum SQL_GB_NO_RELATION = 3; -enum SQL_GB_NOT_SUPPORTED = 0; -enum SQL_GD_BLOCK = 4; -enum SQL_GD_BOUND = 8; -enum SQL_GET_BOOKMARK = 13; -enum SQL_GROUP_BY = 88; -enum SQL_IGNORE = -6; -enum SQL_INFO_FIRST = 0; -enum SQL_KEYSET_SIZE = 8; -enum SQL_KEYSET_SIZE_DEFAULT = 0UL; -enum SQL_KEYWORDS = 89; -enum SQL_LCK_EXCLUSIVE = 2; -enum SQL_LCK_NO_CHANGE = 1; -enum SQL_LCK_UNLOCK = 4; - -enum SQL_LEN_BINARY_ATTR_OFFSET = -100; -enum SQL_LEN_DATA_AT_EXEC_OFFSET = -100; -//MACRO #define SQL_LEN_BINARY_ATTR(length) (-(length)+SQL_LEN_BINARY_ATTR_OFFSET) -//MACRO #define SQL_LEN_DATA_AT_EXEC(length) (-(length)+SQL_LEN_DATA_AT_EXEC_OFFSET) - -enum SQL_LIKE_ESCAPE_CLAUSE = 113; -enum SQL_LIKE_ONLY = 1; -enum SQL_LOCK_EXCLUSIVE = 1; -enum SQL_LOCK_NO_CHANGE = 0; -enum SQL_LOCK_TYPES = 78; -enum SQL_LOCK_UNLOCK = 2; -enum SQL_LOGIN_TIMEOUT = 103; -enum SQL_LOGIN_TIMEOUT_DEFAULT = 15UL; -enum SQL_LONGVARBINARY = -4; -enum SQL_LONGVARCHAR = -1; -enum SQL_MAX_BINARY_LITERAL_LEN = 112; -enum SQL_MAX_CHAR_LITERAL_LEN = 108; -enum SQL_MAX_DSN_LENGTH = 32; -enum SQL_MAX_LENGTH = 3; -enum SQL_MAX_LENGTH_DEFAULT = 0UL; -enum SQL_MAX_OPTION_STRING_LENGTH = 256; -enum SQL_MAX_OWNER_NAME_LEN = 32; -enum SQL_MAX_PROCEDURE_NAME_LEN = 33; -enum SQL_MAX_QUALIFIER_NAME_LEN = 34; -enum SQL_MAX_ROW_SIZE_INCLUDES_LONG = 103; -enum SQL_MAX_ROWS = 1; -enum SQL_MAX_ROWS_DEFAULT = 0UL; - -enum SQL_MODE_READ_WRITE = 0UL; -enum SQL_MODE_READ_ONLY = 1UL; -enum SQL_MODE_DEFAULT = SQL_MODE_READ_WRITE; - -enum SQL_MULT_RESULT_SETS = 36; -enum SQL_MULTIPLE_ACTIVE_TXN = 37; -enum SQL_NC_END = 0x0004; -enum SQL_NC_START = 0x0002; -enum SQL_NEED_LONG_DATA_LEN = 111; -enum SQL_NNC_NON_NULL = 0x0001; -enum SQL_NNC_NULL = 0x0000; -enum SQL_NO_TOTAL = -4; -enum SQL_NON_NULLABLE_COLUMNS = 75; - -enum SQL_NOSCAN_OFF = 0UL; -enum SQL_NOSCAN_ON = 1UL; -enum SQL_NOSCAN = 2; -enum SQL_NOSCAN_DEFAULT = SQL_NOSCAN_OFF; - -enum SQL_NUMERIC_FUNCTIONS = 49; -enum SQL_OAC_LEVEL1 = 0x0001; -enum SQL_OAC_LEVEL2 = 0x0002; -enum SQL_OAC_NONE = 0x0000; -enum SQL_ODBC_API_CONFORMANCE = 9; -enum SQL_ODBC_CURSORS = 110; -enum SQL_ODBC_SAG_CLI_CONFORMANCE = 12; -enum SQL_ODBC_SQL_CONFORMANCE = 15; -enum SQL_ODBC_SQL_OPT_IEF = 73; -enum SQL_ODBC_VER = 10; -enum SQL_OPT_TRACE = 104; - -enum SQL_OPT_TRACE_FILE_DEFAULT = "\\SQL.LOG"; -enum SQL_OPT_TRACE_OFF = 0UL; -enum SQL_OPT_TRACE_DEFAULT = SQL_OPT_TRACE_OFF; -enum SQL_OPT_TRACE_ON = 1UL; - -enum SQL_OPT_TRACEFILE = 105; -enum SQL_OSC_CORE = 1; -enum SQL_OSC_EXTENDED = 2; -enum SQL_OSC_MINIMUM = 0; -enum SQL_OSCC_COMPLIANT = 1; -enum SQL_OSCC_NOT_COMPLIANT = 0; -enum SQL_OU_DML_STATEMENTS = 1; -enum SQL_OU_INDEX_DEFINITION = 8; -enum SQL_OU_PRIVILEGE_DEFINITION = 16; -enum SQL_OU_PROCEDURE_INVOCATION = 2; -enum SQL_OU_TABLE_DEFINITION = 4; -enum SQL_OUTER_JOINS = 38; -enum SQL_OWNER_TERM = 39; -enum SQL_OWNER_USAGE = 91; -enum SQL_PACKET_SIZE = 112; -enum SQL_PARAM_INPUT = 1; -enum SQL_PARAM_INPUT_OUTPUT = 2; -enum SQL_PARAM_OUTPUT = 4; -enum SQL_PARAM_TYPE_DEFAULT = SQL_PARAM_INPUT_OUTPUT; -enum SQL_PARAM_TYPE_UNKNOWN = 0; -enum SQL_PC_NOT_PSEUDO = 1; -enum SQL_POS_ADD = 16; -enum SQL_POS_DELETE = 8; -enum SQL_POS_OPERATIONS = 79; -enum SQL_POS_POSITION = 1; -enum SQL_POS_REFRESH = 2; -enum SQL_POS_UPDATE = 4; -enum SQL_POSITION = 0; -enum SQL_POSITIONED_STATEMENTS = 80; -enum SQL_PROCEDURE_TERM = 40; -enum SQL_PROCEDURES = 21; -enum SQL_PS_POSITIONED_DELETE = 1; -enum SQL_PS_POSITIONED_UPDATE = 2; -enum SQL_PS_SELECT_FOR_UPDATE = 4; -enum SQL_PT_FUNCTION = 2; -enum SQL_PT_PROCEDURE = 1; -enum SQL_PT_UNKNOWN = 0; -enum SQL_QL_END = 0x0002; -enum SQL_QL_START = 0x0001; -enum SQL_QU_DML_STATEMENTS = 1; -enum SQL_QU_INDEX_DEFINITION = 8; -enum SQL_QU_PRIVILEGE_DEFINITION = 16; -enum SQL_QU_PROCEDURE_INVOCATION = 2; -enum SQL_QU_TABLE_DEFINITION = 4; -enum SQL_QUALIFIER_LOCATION = 114; -enum SQL_QUALIFIER_NAME_SEPARATOR = 41; -enum SQL_QUALIFIER_TERM = 42; -enum SQL_QUALIFIER_USAGE = 92; -enum SQL_QUERY_TIMEOUT = 0; -enum SQL_QUERY_TIMEOUT_DEFAULT = 0UL; -enum SQL_QUICK = 0; -enum SQL_QUIET_MODE = 111; -enum SQL_QUOTED_IDENTIFIER_CASE = 93; - -enum SQL_RD_OFF = 0UL; -enum SQL_RD_ON = 1UL; -enum SQL_RD_DEFAULT = SQL_RD_ON; - -enum SQL_REFRESH = 1; -enum SQL_RESTRICT = 1; -enum SQL_RESULT_COL = 3; -enum SQL_RETRIEVE_DATA = 11; -enum SQL_RETURN_VALUE = 5; -enum SQL_ROW_ADDED = 4; -enum SQL_ROW_DELETED = 1; -enum SQL_ROW_ERROR = 5; -enum SQL_ROW_NOROW = 3; -enum SQL_ROW_NUMBER = 14; -enum SQL_ROW_SUCCESS = 0; -enum SQL_ROW_UPDATED = 2; -enum SQL_ROW_UPDATES = 11; -enum SQL_ROWSET_SIZE = 9; -enum SQL_ROWSET_SIZE_DEFAULT = 1UL; -enum SQL_ROWVER = 2; -enum SQL_SC_NON_UNIQUE = 0UL; -enum SQL_SC_TRY_UNIQUE = 1UL; -enum SQL_SC_UNIQUE = 2UL; -enum SQL_SCCO_OPT_TIMESTAMP = SQL_SCCO_OPT_ROWVER;/* deprecated */ -enum SQL_SCROLL_DYNAMIC = -2L;/* deprecated */ -enum SQL_SCROLL_FORWARD_ONLY = 0L;/* deprecated */ -enum SQL_SCROLL_KEYSET_DRIVEN = -1L;/* deprecated */ -enum SQL_SCROLL_OPTIONS = 44; -enum SQL_SCROLL_STATIC = -3L;/* deprecated */ -enum SQL_SEARCHABLE = 3; -enum SQL_SET_NULL = 2; -enum SQL_SETPARAM_VALUE_MAX = -1L; -enum SQL_SETPOS_MAX_LOCK_VALUE = SQL_LOCK_UNLOCK; -enum SQL_SETPOS_MAX_OPTION_VALUE = SQL_ADD; -enum SQL_SIMULATE_CURSOR = 10; -enum SQL_SO_DYNAMIC = 4; -enum SQL_SO_FORWARD_ONLY = 1; -enum SQL_SO_KEYSET_DRIVEN = 2; -enum SQL_SO_MIXED = 8; -enum SQL_SO_STATIC = 16; -enum SQL_SQ_COMPARISON = 1; -enum SQL_SQ_CORRELATED_SUBQUERIES = 16; -enum SQL_SQ_EXISTS = 2; -enum SQL_SQ_IN = 4; -enum SQL_SQ_QUANTIFIED = 8; -enum SQL_SQLSTATE_SIZE = 5; -enum SQL_SS_ADDITIONS = 1; -enum SQL_SS_DELETIONS = 2; -enum SQL_SS_UPDATES = 4; -enum SQL_STATIC_SENSITIVITY = 83; -enum SQL_STRING_FUNCTIONS = 50; -enum SQL_SUBQUERIES = 95; -enum SQL_SYSTEM_FUNCTIONS = 51; -enum SQL_TABLE_STAT = 0; -enum SQL_TABLE_TERM = 45; -enum SQL_TIMEDATE_ADD_INTERVALS = 109; -enum SQL_TIMEDATE_DIFF_INTERVALS = 110; -enum SQL_TIMEDATE_FUNCTIONS = 52; -enum SQL_TRANSLATE_DLL = 106; -enum SQL_TRANSLATE_OPTION = 107; -enum SQL_TXN_ISOLATION = 108; -enum SQL_TXN_VERSIONING = 16; -enum SQL_TYPE_NULL = 0; -enum SQL_U_UNION = 1; -enum SQL_U_UNION_ALL = 2; - -enum SQL_UB_OFF = 0UL; -enum SQL_UB_DEFAULT = SQL_UB_OFF; -enum SQL_UB_ON = 1UL; - -enum SQL_UNION = 96; -enum SQL_UNSEARCHABLE = 0; -enum SQL_UPDATE = 2; -enum SQL_USE_BOOKMARKS = 12; -enum SQL_VARBINARY = -3; - -enum SQL_COLATT_OPT_MAX = SQL_COLUMN_LABEL; -enum SQL_COLATT_OPT_MIN = SQL_COLUMN_COUNT; -enum SQL_PRED_SEARCHABLE = SQL_SEARCHABLE; - -//MACRO #define SQL_POSITION_TO(s, r) SQLSetPos(s, r, SQL_POSITION, SQL_LOCK_NO_CHANGE) - -//MACRO #define SQL_LOCK_RECORD(s, r, l) SQLSetPos(s, r, SQL_POSITION, l) - -//MACRO #define SQL_REFRESH_RECORD(s, r, l) SQLSetPos(s, r, SQL_REFRESH, l) - -//MACRO #define SQL_UPDATE_RECORD(s, r) SQLSetPos(s, r, SQL_UPDATE, SQL_LOCK_NO_CHANGE) - -//MACRO #define SQL_DELETE_RECORD(s, r) SQLSetPos(s, r, SQL_DELETE, SQL_LOCK_NO_CHANGE) - -//MACRO #define SQL_ADD_RECORD(s, r) SQLSetPos(s, r, SQL_ADD, SQL_LOCK_NO_CHANGE) - - -static if (ODBCVER < 0x0300) -{ -enum SQL_CONNECT_OPT_DRVR_START = 1000; -enum SQL_CONN_OPT_MAX = SQL_PACKET_SIZE; -enum SQL_CONN_OPT_MIN = SQL_ACCESS_MODE; -enum SQL_STMT_OPT_MAX = SQL_ROW_NUMBER; -enum SQL_STMT_OPT_MIN = SQL_QUERY_TIMEOUT; -enum SQL_TYPE_DRIVER_START = SQL_INTERVAL_YEAR; -enum SQL_TYPE_DRIVER_END = SQL_UNICODE_LONGVARCHAR; -enum SQL_TYPE_MIN = SQL_BIT; -enum SQL_TYPE_MAX = SQL_VARCHAR; -} - -static if (ODBCVER < 0x0300) -{ -enum SQL_NO_DATA_FOUND = 100; -enum SQL_INTERVAL_YEAR = -80; -enum SQL_INTERVAL_MONTH = -81; -enum SQL_INTERVAL_YEAR_TO_MONTH = -82; -enum SQL_INTERVAL_DAY = -83; -enum SQL_INTERVAL_HOUR = -84; -enum SQL_INTERVAL_MINUTE = -85; -enum SQL_INTERVAL_SECOND = -86; -enum SQL_INTERVAL_DAY_TO_HOUR = -87; -enum SQL_INTERVAL_DAY_TO_MINUTE = -88; -enum SQL_INTERVAL_DAY_TO_SECOND = -89; -enum SQL_INTERVAL_HOUR_TO_MINUTE = -90; -enum SQL_INTERVAL_HOUR_TO_SECOND = -91; -enum SQL_INTERVAL_MINUTE_TO_SECOND = -92; -} -else -{ -enum SQL_NO_DATA_FOUND = SQL_NO_DATA; -enum SQL_CODE_YEAR = 1; -enum SQL_CODE_MONTH = 2; -enum SQL_CODE_DAY = 3; -enum SQL_CODE_HOUR = 4; -enum SQL_CODE_MINUTE = 5; -enum SQL_CODE_SECOND = 6; -enum SQL_CODE_YEAR_TO_MONTH = 7; -enum SQL_CODE_DAY_TO_HOUR = 8; -enum SQL_CODE_DAY_TO_MINUTE = 9; -enum SQL_CODE_DAY_TO_SECOND = 10; -enum SQL_CODE_HOUR_TO_MINUTE = 11; -enum SQL_CODE_HOUR_TO_SECOND = 12; -enum SQL_CODE_MINUTE_TO_SECOND = 13; -enum SQL_INTERVAL_YEAR = 100 + SQL_CODE_YEAR; -enum SQL_INTERVAL_MONTH = 100 + SQL_CODE_MONTH; -enum SQL_INTERVAL_DAY = 100 + SQL_CODE_DAY; -enum SQL_INTERVAL_HOUR = 100 + SQL_CODE_HOUR; -enum SQL_INTERVAL_MINUTE = 100 + SQL_CODE_MINUTE; -enum SQL_INTERVAL_SECOND = 100 + SQL_CODE_SECOND; -enum SQL_INTERVAL_YEAR_TO_MONTH = 100 + SQL_CODE_YEAR_TO_MONTH; -enum SQL_INTERVAL_DAY_TO_HOUR = 100 + SQL_CODE_DAY_TO_HOUR; -enum SQL_INTERVAL_DAY_TO_MINUTE = 100 + SQL_CODE_DAY_TO_MINUTE; -enum SQL_INTERVAL_DAY_TO_SECOND = 100 + SQL_CODE_DAY_TO_SECOND; -enum SQL_INTERVAL_HOUR_TO_MINUTE = 100 + SQL_CODE_HOUR_TO_MINUTE; -enum SQL_INTERVAL_HOUR_TO_SECOND = 100 + SQL_CODE_HOUR_TO_SECOND; -enum SQL_INTERVAL_MINUTE_TO_SECOND = 100 + SQL_CODE_MINUTE_TO_SECOND; -}//[Yes] #endif - - -static if ((ODBCVER >= 0x0201) && (ODBCVER < 0x0300)) -{ -enum SQL_OJ_CAPABILITIES = 65003; -} - -static if (ODBCVER >= 0x0250) -{ -enum SQL_NO_ACTION = 3; -enum SQL_SET_DEFAULT = 4; -} - -static if (ODBCVER >= 0x0300) -{ -enum SQL_ACTIVE_ENVIRONMENTS = 116; -enum SQL_AD_ADD_CONSTRAINT_DEFERRABLE = 0x00000080L; -enum SQL_AD_ADD_CONSTRAINT_INITIALLY_DEFERRED = 0x00000020L; -enum SQL_AD_ADD_CONSTRAINT_INITIALLY_IMMEDIATE = 0x00000040L; -enum SQL_AD_ADD_CONSTRAINT_NON_DEFERRABLE = 0x00000100L; -enum SQL_AD_ADD_DOMAIN_CONSTRAINT = 0x00000002L; -enum SQL_AD_ADD_DOMAIN_DEFAULT = 0x00000008L; -enum SQL_AD_CONSTRAINT_NAME_DEFINITION = 0x00000001L; -enum SQL_AD_DROP_DOMAIN_CONSTRAINT = 0x00000004L; -enum SQL_AD_DROP_DOMAIN_DEFAULT = 0x00000010L; -enum SQL_AF_ALL = 0x00000040L; -enum SQL_AF_AVG = 0x00000001L; -enum SQL_AF_COUNT = 0x00000002L; -enum SQL_AF_DISTINCT = 0x00000020L; -enum SQL_AF_MAX = 0x00000004L; -enum SQL_AF_MIN = 0x00000008L; -enum SQL_AF_SUM = 0x00000010L; -enum SQL_AGGREGATE_FUNCTIONS = 169; -enum SQL_ALL_CATALOGS = "%"; -enum SQL_ALL_SCHEMAS = "%"; -enum SQL_ALL_TABLE_TYPES = "%"; -enum SQL_ALTER_DOMAIN = 117; -enum SQL_AM_CONNECTION = 1; -enum SQL_AM_NONE = 0; -enum SQL_AM_STATEMENT = 2; -enum SQL_API_ODBC3_ALL_FUNCTIONS = 999; -enum SQL_API_ODBC3_ALL_FUNCTIONS_SIZE = 250; -enum SQL_API_SQLALLOCHANDLESTD = 73; -enum SQL_API_SQLBULKOPERATIONS = 24; -enum SQL_ASYNC_MODE = 10021; -enum SQL_AT_ADD_COLUMN_COLLATION = 0x00000080L; -enum SQL_AT_ADD_COLUMN_DEFAULT = 0x00000040L; -enum SQL_AT_ADD_COLUMN_SINGLE = 0x00000020L; -enum SQL_AT_ADD_TABLE_CONSTRAINT = 0x00001000L; -enum SQL_AT_CONSTRAINT_DEFERRABLE = 0x00040000L; -enum SQL_AT_CONSTRAINT_INITIALLY_DEFERRED = 0x00010000L; -enum SQL_AT_CONSTRAINT_INITIALLY_IMMEDIATE = 0x00020000L; -enum SQL_AT_CONSTRAINT_NAME_DEFINITION = 0x00008000L; -enum SQL_AT_CONSTRAINT_NON_DEFERRABLE = 0x00080000L; -enum SQL_AT_DROP_COLUMN_CASCADE = 0x00000400L; -enum SQL_AT_DROP_COLUMN_DEFAULT = 0x00000200L; -enum SQL_AT_DROP_COLUMN_RESTRICT = 0x00000800L; -enum SQL_AT_DROP_TABLE_CONSTRAINT_CASCADE = 0x00002000L; -enum SQL_AT_DROP_TABLE_CONSTRAINT_RESTRICT = 0x00004000L; -enum SQL_AT_SET_COLUMN_DEFAULT = 0x00000100L; -enum SQL_ATTR_ACCESS_MODE = SQL_ACCESS_MODE; -enum SQL_ATTR_ASYNC_ENABLE = 4; -enum SQL_ATTR_AUTOCOMMIT = SQL_AUTOCOMMIT; -enum SQL_ATTR_CONCURRENCY = SQL_CONCURRENCY; -enum SQL_ATTR_CONNECTION_POOLING = 201; -enum SQL_ATTR_CONNECTION_TIMEOUT = 113; -enum SQL_ATTR_CP_MATCH = 202; -enum SQL_ATTR_CURRENT_CATALOG = SQL_CURRENT_QUALIFIER; -enum SQL_ATTR_CURSOR_TYPE = SQL_CURSOR_TYPE; -enum SQL_ATTR_DISCONNECT_BEHAVIOR = 114; -enum SQL_ATTR_ENABLE_AUTO_IPD = 15; -enum SQL_ATTR_ENLIST_IN_DTC = 1207; -enum SQL_ATTR_ENLIST_IN_XA = 1208; -enum SQL_ATTR_FETCH_BOOKMARK_PTR = 16; -enum SQL_ATTR_KEYSET_SIZE = SQL_KEYSET_SIZE; -enum SQL_ATTR_LOGIN_TIMEOUT = SQL_LOGIN_TIMEOUT; -enum SQL_ATTR_MAX_LENGTH = SQL_MAX_LENGTH; -enum SQL_ATTR_MAX_ROWS = SQL_MAX_ROWS; -enum SQL_ATTR_NOSCAN = SQL_NOSCAN; -enum SQL_ATTR_ODBC_CURSORS = SQL_ODBC_CURSORS; -enum SQL_ATTR_ODBC_VERSION = 200; -enum SQL_ATTR_PACKET_SIZE = SQL_PACKET_SIZE; -enum SQL_ATTR_PARAM_BIND_OFFSET_PTR = 17; -enum SQL_ATTR_PARAM_BIND_TYPE = 18; -enum SQL_ATTR_PARAM_OPERATION_PTR = 19; -enum SQL_ATTR_PARAM_STATUS_PTR = 20; -enum SQL_ATTR_PARAMS_PROCESSED_PTR = 21; -enum SQL_ATTR_PARAMSET_SIZE = 22; -enum SQL_ATTR_QUERY_TIMEOUT = SQL_QUERY_TIMEOUT; -enum SQL_ATTR_QUIET_MODE = SQL_QUIET_MODE; -enum SQL_ATTR_RETRIEVE_DATA = SQL_RETRIEVE_DATA; -enum SQL_ATTR_ROW_ARRAY_SIZE = 27; -enum SQL_ATTR_ROW_BIND_OFFSET_PTR = 23; -enum SQL_ATTR_ROW_BIND_TYPE = SQL_BIND_TYPE; -enum SQL_ATTR_ROW_NUMBER = SQL_ROW_NUMBER; -enum SQL_ATTR_ROW_OPERATION_PTR = 24; -enum SQL_ATTR_ROW_STATUS_PTR = 25; -enum SQL_ATTR_ROWS_FETCHED_PTR = 26; -enum SQL_ATTR_SIMULATE_CURSOR = SQL_SIMULATE_CURSOR; -enum SQL_ATTR_TRACE = SQL_OPT_TRACE; -enum SQL_ATTR_TRACEFILE = SQL_OPT_TRACEFILE; -enum SQL_ATTR_TRANSLATE_LIB = SQL_TRANSLATE_DLL; -enum SQL_ATTR_TRANSLATE_OPTION = SQL_TRANSLATE_OPTION; -enum SQL_ATTR_TXN_ISOLATION = SQL_TXN_ISOLATION; -enum SQL_ATTR_USE_BOOKMARKS = SQL_USE_BOOKMARKS; -enum SQL_BATCH_ROW_COUNT = 120; -enum SQL_BATCH_SUPPORT = 121; -enum SQL_BRC_EXPLICIT = 0x0000002; -enum SQL_BRC_PROCEDURES = 0x0000001; -enum SQL_BRC_ROLLED_UP = 0x0000004; -enum SQL_BS_ROW_COUNT_EXPLICIT = 0x00000002L; -enum SQL_BS_ROW_COUNT_PROC = 0x00000008L; -enum SQL_BS_SELECT_EXPLICIT = 0x00000001L; -enum SQL_BS_SELECT_PROC = 0x00000004L; -enum SQL_C_INTERVAL_DAY = SQL_INTERVAL_DAY; -enum SQL_C_INTERVAL_DAY_TO_HOUR = SQL_INTERVAL_DAY_TO_HOUR; -enum SQL_C_INTERVAL_DAY_TO_MINUTE = SQL_INTERVAL_DAY_TO_MINUTE; -enum SQL_C_INTERVAL_DAY_TO_SECOND = SQL_INTERVAL_DAY_TO_SECOND; -enum SQL_C_INTERVAL_HOUR = SQL_INTERVAL_HOUR; -enum SQL_C_INTERVAL_HOUR_TO_MINUTE = SQL_INTERVAL_HOUR_TO_MINUTE; -enum SQL_C_INTERVAL_HOUR_TO_SECOND = SQL_INTERVAL_HOUR_TO_SECOND; -enum SQL_C_INTERVAL_MINUTE = SQL_INTERVAL_MINUTE; -enum SQL_C_INTERVAL_MINUTE_TO_SECOND = SQL_INTERVAL_MINUTE_TO_SECOND; -enum SQL_C_INTERVAL_MONTH = SQL_INTERVAL_MONTH; -enum SQL_C_INTERVAL_SECOND = SQL_INTERVAL_SECOND; -enum SQL_C_INTERVAL_YEAR = SQL_INTERVAL_YEAR; -enum SQL_C_INTERVAL_YEAR_TO_MONTH = SQL_INTERVAL_YEAR_TO_MONTH; -enum SQL_C_NUMERIC = SQL_NUMERIC; -enum SQL_C_SBIGINT = SQL_BIGINT+SQL_SIGNED_OFFSET; -enum SQL_C_TYPE_DATE = SQL_TYPE_DATE; -enum SQL_C_TYPE_TIME = SQL_TYPE_TIME; -enum SQL_C_TYPE_TIMESTAMP = SQL_TYPE_TIMESTAMP; -enum SQL_C_UBIGINT = SQL_BIGINT+SQL_UNSIGNED_OFFSET; -enum SQL_C_VARBOOKMARK = SQL_C_BINARY; -enum SQL_CA_CONSTRAINT_DEFERRABLE = 0x00000040L; -enum SQL_CA_CONSTRAINT_INITIALLY_DEFERRED = 0x00000010L; -enum SQL_CA_CONSTRAINT_INITIALLY_IMMEDIATE = 0x00000020L; -enum SQL_CA_CONSTRAINT_NON_DEFERRABLE = 0x00000080L; -enum SQL_CA_CREATE_ASSERTION = 0x00000001L; -enum SQL_CA1_ABSOLUTE = 0x00000002L; -enum SQL_CA1_BOOKMARK = 0x00000008L; -enum SQL_CA1_BULK_ADD = 0x00010000L; -enum SQL_CA1_BULK_DELETE_BY_BOOKMARK = 0x00040000L; -enum SQL_CA1_BULK_FETCH_BY_BOOKMARK = 0x00080000L; -enum SQL_CA1_BULK_UPDATE_BY_BOOKMARK = 0x00020000L; -enum SQL_CA1_LOCK_EXCLUSIVE = 0x00000080L; -enum SQL_CA1_LOCK_NO_CHANGE = 0x00000040L; -enum SQL_CA1_LOCK_UNLOCK = 0x00000100L; -enum SQL_CA1_NEXT = 0x00000001L; -enum SQL_CA1_POS_DELETE = 0x00000800L; -enum SQL_CA1_POS_POSITION = 0x00000200L; -enum SQL_CA1_POS_REFRESH = 0x00001000L; -enum SQL_CA1_POS_UPDATE = 0x00000400L; -enum SQL_CA1_POSITIONED_DELETE = 0x00004000L; -enum SQL_CA1_POSITIONED_UPDATE = 0x00002000L; -enum SQL_CA1_RELATIVE = 0x00000004L; -enum SQL_CA1_SELECT_FOR_UPDATE = 0x00008000L; -enum SQL_CA2_CRC_APPROXIMATE = 0x00002000L; -enum SQL_CA2_CRC_EXACT = 0x00001000L; -enum SQL_CA2_LOCK_CONCURRENCY = 0x00000002L; - -enum SQL_CA2_MAX_ROWS_CATALOG = 0x00000800L; -enum SQL_CA2_MAX_ROWS_DELETE = 0x00000200L; -enum SQL_CA2_MAX_ROWS_INSERT = 0x00000100L; -enum SQL_CA2_MAX_ROWS_SELECT = 0x00000080L; -enum SQL_CA2_MAX_ROWS_UPDATE = 0x00000400L; -enum SQL_CA2_MAX_ROWS_AFFECTS_ALL = SQL_CA2_MAX_ROWS_SELECT | SQL_CA2_MAX_ROWS_INSERT | - SQL_CA2_MAX_ROWS_DELETE | SQL_CA2_MAX_ROWS_UPDATE | SQL_CA2_MAX_ROWS_CATALOG; - -enum SQL_CA2_OPT_ROWVER_CONCURRENCY = 0x00000004L; -enum SQL_CA2_OPT_VALUES_CONCURRENCY = 0x00000008L; -enum SQL_CA2_READ_ONLY_CONCURRENCY = 0x00000001L; -enum SQL_CA2_SENSITIVITY_ADDITIONS = 0x00000010L; -enum SQL_CA2_SENSITIVITY_DELETIONS = 0x00000020L; -enum SQL_CA2_SENSITIVITY_UPDATES = 0x00000040L; -enum SQL_CA2_SIMULATE_NON_UNIQUE = 0x00004000L; -enum SQL_CA2_SIMULATE_TRY_UNIQUE = 0x00008000L; -enum SQL_CA2_SIMULATE_UNIQUE = 0x00010000L; -enum SQL_CATALOG_LOCATION = SQL_QUALIFIER_LOCATION; -enum SQL_CATALOG_NAME_SEPARATOR = SQL_QUALIFIER_NAME_SEPARATOR; -enum SQL_CATALOG_TERM = SQL_QUALIFIER_TERM; -enum SQL_CATALOG_USAGE = SQL_QUALIFIER_USAGE; -enum SQL_CCOL_CREATE_COLLATION = 0x00000001L; -enum SQL_CCS_COLLATE_CLAUSE = 0x00000002L; -enum SQL_CCS_CREATE_CHARACTER_SET = 0x00000001L; -enum SQL_CCS_LIMITED_COLLATION = 0x00000004L; -enum SQL_CDO_COLLATION = 0x00000008L; -enum SQL_CDO_CONSTRAINT = 0x00000004L; -enum SQL_CDO_CONSTRAINT_DEFERRABLE = 0x00000080L; -enum SQL_CDO_CONSTRAINT_INITIALLY_DEFERRED = 0x00000020L; -enum SQL_CDO_CONSTRAINT_INITIALLY_IMMEDIATE = 0x00000040L; -enum SQL_CDO_CONSTRAINT_NAME_DEFINITION = 0x00000010L; -enum SQL_CDO_CONSTRAINT_NON_DEFERRABLE = 0x00000100L; -enum SQL_CDO_CREATE_DOMAIN = 0x00000001L; -enum SQL_CDO_DEFAULT = 0x00000002L; -enum SQL_CL_END = SQL_QL_END; -enum SQL_CL_START = SQL_QL_START; -enum SQL_COL_PRED_BASIC = SQL_ALL_EXCEPT_LIKE; -enum SQL_COL_PRED_CHAR = SQL_LIKE_ONLY; -enum SQL_COLUMN_DRIVER_START = 1000; -enum SQL_COLUMN_IGNORE = SQL_IGNORE; -enum SQL_COLUMN_NUMBER_UNKNOWN = -2; -enum SQL_CONVERT_GUID = 173; - -enum SQL_CONVERT_WCHAR = 122; -enum SQL_CONVERT_INTERVAL_DAY_TIME = 123; -enum SQL_CONVERT_INTERVAL_YEAR_MONTH = 124; -enum SQL_CONVERT_WLONGVARCHAR = 125; -enum SQL_CONVERT_WVARCHAR = 126; - -enum SQL_CREATE_ASSERTION = 127; -enum SQL_CREATE_CHARACTER_SET = 128; -enum SQL_CREATE_COLLATION = 129; -enum SQL_CREATE_DOMAIN = 130; -enum SQL_CREATE_SCHEMA = 131; -enum SQL_CREATE_TABLE = 132; -enum SQL_CREATE_TRANSLATION = 133; -enum SQL_CREATE_VIEW = 134; - - -enum SQL_CP_OFF = 0UL; -enum SQL_CP_DEFAULT = SQL_CP_OFF; -enum SQL_CP_ONE_PER_DRIVER = 1UL; -enum SQL_CP_ONE_PER_HENV = 2UL; - -enum SQL_CP_STRICT_MATCH = 0UL; -enum SQL_CP_MATCH_DEFAULT = SQL_CP_STRICT_MATCH; -enum SQL_CP_RELAXED_MATCH = 1UL; - -enum SQL_CS_CREATE_SCHEMA = 0x00000001L; -enum SQL_CS_AUTHORIZATION = 0x00000002L; -enum SQL_CS_DEFAULT_CHARACTER_SET = 0x00000004L; - -enum SQL_CT_COLUMN_COLLATION = 0x00000800L; -enum SQL_CT_COLUMN_CONSTRAINT = 0x00000200L; -enum SQL_CT_COLUMN_DEFAULT = 0x00000400L; -enum SQL_CT_COMMIT_DELETE = 0x00000004L; -enum SQL_CT_COMMIT_PRESERVE = 0x00000002L; -enum SQL_CT_CONSTRAINT_DEFERRABLE = 0x00000080L; -enum SQL_CT_CONSTRAINT_INITIALLY_DEFERRED = 0x00000020L; -enum SQL_CT_CONSTRAINT_INITIALLY_IMMEDIATE = 0x00000040L; -enum SQL_CT_CONSTRAINT_NAME_DEFINITION = 0x00002000L; -enum SQL_CT_CONSTRAINT_NON_DEFERRABLE = 0x00000100L; -enum SQL_CT_CREATE_TABLE = 0x00000001L; -enum SQL_CT_GLOBAL_TEMPORARY = 0x00000008L; -enum SQL_CT_LOCAL_TEMPORARY = 0x00000010L; -enum SQL_CT_TABLE_CONSTRAINT = 0x00001000L; - -enum SQL_CTR_CREATE_TRANSLATION = 0x00000001L; - -enum SQL_CU_DML_STATEMENTS = SQL_QU_DML_STATEMENTS; -enum SQL_CU_INDEX_DEFINITION = SQL_QU_INDEX_DEFINITION; -enum SQL_CU_PRIVILEGE_DEFINITION = SQL_QU_PRIVILEGE_DEFINITION; -enum SQL_CU_PROCEDURE_INVOCATION = SQL_QU_PROCEDURE_INVOCATION; -enum SQL_CU_TABLE_DEFINITION = SQL_QU_TABLE_DEFINITION; - -enum SQL_CVT_INTERVAL_YEAR_MONTH = 0x00080000L; -enum SQL_CVT_INTERVAL_DAY_TIME = 0x00100000L; -enum SQL_CVT_WCHAR = 0x00200000L; -enum SQL_CVT_WLONGVARCHAR = 0x00400000L; -enum SQL_CVT_WVARCHAR = 0x00800000L; -enum SQL_CVT_GUID = 0x01000000L; - -enum SQL_DA_DROP_ASSERTION = 0x00000001L; -enum SQL_DATETIME_LITERALS = 119; - -enum SQL_DB_DISCONNECT = 1UL; -enum SQL_DB_RETURN_TO_POOL = 0UL; -enum SQL_DB_DEFAULT = SQL_DB_RETURN_TO_POOL; - -enum SQL_DC_DROP_COLLATION = 0x00000001L; -enum SQL_DCS_DROP_CHARACTER_SET = 0x00000001L; -enum SQL_DD_CASCADE = 0x00000004L; -enum SQL_DD_DROP_DOMAIN = 0x00000001L; -enum SQL_DD_RESTRICT = 0x00000002L; -enum SQL_DDL_INDEX = 170; -enum SQL_DELETE_BY_BOOKMARK = 6; -enum SQL_DESC_ARRAY_SIZE = 20; -enum SQL_DESC_ARRAY_STATUS_PTR = 21; -enum SQL_DESC_AUTO_UNIQUE_VALUE = SQL_COLUMN_AUTO_INCREMENT; -enum SQL_DESC_BASE_COLUMN_NAME = 22; -enum SQL_DESC_BASE_TABLE_NAME = 23; -enum SQL_DESC_BIND_OFFSET_PTR = 24; -enum SQL_DESC_BIND_TYPE = 25; -enum SQL_DESC_CASE_SENSITIVE = SQL_COLUMN_CASE_SENSITIVE; -enum SQL_DESC_CATALOG_NAME = SQL_COLUMN_QUALIFIER_NAME; -enum SQL_DESC_CONCISE_TYPE = SQL_COLUMN_TYPE; -enum SQL_DESC_DATETIME_INTERVAL_PRECISION = 26; -enum SQL_DESC_DISPLAY_SIZE = SQL_COLUMN_DISPLAY_SIZE; -enum SQL_DESC_FIXED_PREC_SCALE = SQL_COLUMN_MONEY; -enum SQL_DESC_LABEL = SQL_COLUMN_LABEL; -enum SQL_DESC_LITERAL_PREFIX = 27; -enum SQL_DESC_LITERAL_SUFFIX = 28; -enum SQL_DESC_LOCAL_TYPE_NAME = 29; -enum SQL_DESC_MAXIMUM_SCALE = 30; -enum SQL_DESC_MINIMUM_SCALE = 31; -enum SQL_DESC_NUM_PREC_RADIX = 32; -enum SQL_DESC_PARAMETER_TYPE = 33; -enum SQL_DESC_ROWS_PROCESSED_PTR = 34; -enum SQL_DESC_SCHEMA_NAME = SQL_COLUMN_OWNER_NAME; -enum SQL_DESC_SEARCHABLE = SQL_COLUMN_SEARCHABLE; -enum SQL_DESC_TABLE_NAME = SQL_COLUMN_TABLE_NAME; -enum SQL_DESC_TYPE_NAME = SQL_COLUMN_TYPE_NAME; -enum SQL_DESC_UNSIGNED = SQL_COLUMN_UNSIGNED; -enum SQL_DESC_UPDATABLE = SQL_COLUMN_UPDATABLE; -enum SQL_DI_CREATE_INDEX = 0x00000001L; -enum SQL_DI_DROP_INDEX = 0x00000002L; - -enum SQL_DIAG_COLUMN_NUMBER = -1247; -enum SQL_DIAG_ROW_NUMBER = -1248; -enum SQL_DIAG_CURSOR_ROW_COUNT = -1249; - -enum SQL_DL_SQL92_DATE = 0x00000001L; -enum SQL_DL_SQL92_INTERVAL_DAY = 0x00000020L; -enum SQL_DL_SQL92_INTERVAL_DAY_TO_HOUR = 0x00000400L; -enum SQL_DL_SQL92_INTERVAL_DAY_TO_MINUTE = 0x00000800L; -enum SQL_DL_SQL92_INTERVAL_DAY_TO_SECOND = 0x00001000L; -enum SQL_DL_SQL92_INTERVAL_HOUR = 0x00000040L; -enum SQL_DL_SQL92_INTERVAL_HOUR_TO_MINUTE = 0x00002000L; -enum SQL_DL_SQL92_INTERVAL_HOUR_TO_SECOND = 0x00004000L; -enum SQL_DL_SQL92_INTERVAL_MINUTE = 0x00000080L; -enum SQL_DL_SQL92_INTERVAL_MINUTE_TO_SECOND = 0x00008000L; -enum SQL_DL_SQL92_INTERVAL_MONTH = 0x00000010L; -enum SQL_DL_SQL92_INTERVAL_SECOND = 0x00000100L; -enum SQL_DL_SQL92_INTERVAL_YEAR = 0x00000008L; -enum SQL_DL_SQL92_INTERVAL_YEAR_TO_MONTH = 0x00000200L; -enum SQL_DL_SQL92_TIME = 0x00000002L; -enum SQL_DL_SQL92_TIMESTAMP = 0x00000004L; -enum SQL_DM_VER = 171; -enum SQL_DRIVER_HDESC = 135; -enum SQL_DROP_ASSERTION = 136; -enum SQL_DROP_CHARACTER_SET = 137; -enum SQL_DROP_COLLATION = 138; -enum SQL_DROP_DOMAIN = 139; -enum SQL_DROP_SCHEMA = 140; -enum SQL_DROP_TABLE = 141; -enum SQL_DROP_TRANSLATION = 142; -enum SQL_DROP_VIEW = 143; -enum SQL_DS_CASCADE = 0x00000004L; -enum SQL_DS_DROP_SCHEMA = 0x00000001L; -enum SQL_DS_RESTRICT = 0x00000002L; -enum SQL_DT_CASCADE = 0x00000004L; -enum SQL_DT_DROP_TABLE = 0x00000001L; -enum SQL_DT_RESTRICT = 0x00000002L; -enum SQL_DTC_DONE = 0L; -enum SQL_DTR_DROP_TRANSLATION = 0x00000001L; -enum SQL_DV_CASCADE = 0x00000004L; -enum SQL_DV_DROP_VIEW = 0x00000001L; -enum SQL_DV_RESTRICT = 0x00000002L; -enum SQL_DYNAMIC_CURSOR_ATTRIBUTES1 = 144; -enum SQL_DYNAMIC_CURSOR_ATTRIBUTES2 = 145; -enum SQL_EXT_API_LAST = SQL_API_SQLBINDPARAMETER; -enum SQL_EXT_API_START = 40; -enum SQL_FETCH_BY_BOOKMARK = 7; -enum SQL_FETCH_FIRST_SYSTEM = 32; -enum SQL_FETCH_FIRST_USER = 31; -enum SQL_FN_CVT_CAST = 0x00000002L; -enum SQL_FN_STR_BIT_LENGTH = 0x00080000L; -enum SQL_FN_STR_CHAR_LENGTH = 0x00100000L; -enum SQL_FN_STR_CHARACTER_LENGTH = 0x00200000L; -enum SQL_FN_STR_OCTET_LENGTH = 0x00400000L; -enum SQL_FN_STR_POSITION = 0x00800000L; -enum SQL_FN_TD_CURRENT_DATE = 0x00020000L; -enum SQL_FN_TD_CURRENT_TIME = 0x00040000L; -enum SQL_FN_TD_CURRENT_TIMESTAMP = 0x00080000L; -enum SQL_FN_TD_EXTRACT = 0x00100000L; -enum SQL_FORWARD_ONLY_CURSOR_ATTRIBUTES1 = 146; -enum SQL_FORWARD_ONLY_CURSOR_ATTRIBUTES2 = 147; - /* #define SQL_FUNC_EXISTS(exists, api) - ((*(((UWORD*) (exists)) + ((api) >> 4)) & (1 << ((api) & 15)) ) ? - SQL_TRUE : SQL_FALSE ) - */ -enum SQL_GB_COLLATE = 0x0004; -enum SQL_HANDLE_SENV = 5; - -enum SQL_IK_NONE = 0; -enum SQL_IK_ASC = 1; -enum SQL_IK_DESC = 2; -enum SQL_IK_ALL = SQL_IK_ASC | SQL_IK_DESC; - -enum SQL_INDEX_KEYWORDS = 148; -enum SQL_INFO_DRIVER_START = 1000; -enum SQL_INFO_LAST = SQL_QUALIFIER_LOCATION; -enum SQL_INFO_SCHEMA_VIEWS = 149; -enum SQL_INITIALLY_DEFERRED = 5; -enum SQL_INITIALLY_IMMEDIATE = 6; -enum SQL_INSERT_STATEMENT = 172; -enum SQL_INTERVAL = 10; -enum SQL_IS_INSERT_LITERALS = 0x00000001L; -enum SQL_IS_INSERT_SEARCHED = 0x00000002L; -enum SQL_IS_INTEGER = -6; -enum SQL_IS_POINTER = -4; -enum SQL_IS_SELECT_INTO = 0x00000004L; -enum SQL_IS_SMALLINT = -8; -enum SQL_IS_UINTEGER = -5; -enum SQL_IS_USMALLINT = -7; -enum SQL_ISV_ASSERTIONS = 0x00000001L; -enum SQL_ISV_CHARACTER_SETS = 0x00000002L; -enum SQL_ISV_CHECK_CONSTRAINTS = 0x00000004L; -enum SQL_ISV_COLLATIONS = 0x00000008L; -enum SQL_ISV_COLUMN_DOMAIN_USAGE = 0x00000010L; -enum SQL_ISV_COLUMN_PRIVILEGES = 0x00000020L; -enum SQL_ISV_COLUMNS = 0x00000040L; -enum SQL_ISV_CONSTRAINT_COLUMN_USAGE = 0x00000080L; -enum SQL_ISV_CONSTRAINT_TABLE_USAGE = 0x00000100L; -enum SQL_ISV_DOMAIN_CONSTRAINTS = 0x00000200L; -enum SQL_ISV_DOMAINS = 0x00000400L; -enum SQL_ISV_KEY_COLUMN_USAGE = 0x00000800L; -enum SQL_ISV_REFERENTIAL_CONSTRAINTS = 0x00001000L; -enum SQL_ISV_SCHEMATA = 0x00002000L; -enum SQL_ISV_SQL_LANGUAGES = 0x00004000L; -enum SQL_ISV_TABLE_CONSTRAINTS = 0x00008000L; -enum SQL_ISV_TABLE_PRIVILEGES = 0x00010000L; -enum SQL_ISV_TABLES = 0x00020000L; -enum SQL_ISV_TRANSLATIONS = 0x00040000L; -enum SQL_ISV_USAGE_PRIVILEGES = 0x00080000L; -enum SQL_ISV_VIEW_COLUMN_USAGE = 0x00100000L; -enum SQL_ISV_VIEW_TABLE_USAGE = 0x00200000L; -enum SQL_ISV_VIEWS = 0x00400000L; -enum SQL_KEYSET_CURSOR_ATTRIBUTES1 = 150; -enum SQL_KEYSET_CURSOR_ATTRIBUTES2 = 151; -enum SQL_MAX_ASYNC_CONCURRENT_STATEMENTS = 10022; -enum SQL_NO_COLUMN_NUMBER = -1; -enum SQL_NO_ROW_NUMBER = -1; -enum SQL_NOT_DEFERRABLE = 7; -enum SQL_NUM_EXTENSIONS = SQL_EXT_API_LAST-SQL_EXT_API_START+1; -enum SQL_NUM_FUNCTIONS = 23; -enum SQL_ODBC_INTERFACE_CONFORMANCE = 152; - - enum : ULONG { - SQL_OIC_CORE = 1, - SQL_OIC_LEVEL1, - SQL_OIC_LEVEL2 - } - enum : ULONG { - SQL_OV_ODBC2 = 2, - SQL_OV_ODBC3 = 3 - } - -enum ULONG - SQL_PARAM_BIND_BY_COLUMN = 0, - SQL_PARAM_BIND_TYPE_DEFAULT = SQL_PARAM_BIND_BY_COLUMN; - -enum SQL_PARAM_ARRAY_ROW_COUNTS = 153; -enum SQL_PARAM_ARRAY_SELECTS = 154; -enum SQL_PARAM_DIAG_UNAVAILABLE = 1; -enum SQL_PARAM_ERROR = 5; -enum SQL_PARAM_IGNORE = 1; -enum SQL_PARAM_PROCEED = 0; -enum SQL_PARAM_SUCCESS = 0; -enum SQL_PARAM_SUCCESS_WITH_INFO = 6; -enum SQL_PARAM_UNUSED = 7; - -enum SQL_PARC_BATCH = 1; -enum SQL_PARC_NO_BATCH = 2; -enum SQL_PAS_BATCH = 1; -enum SQL_PAS_NO_BATCH = 2; -enum SQL_PAS_NO_SELECT = 3; - -enum SQL_ROW_IGNORE = 1; -enum SQL_ROW_NUMBER_UNKNOWN = -2; -enum SQL_ROW_PROCEED = 0; -enum SQL_ROW_SUCCESS_WITH_INFO = 6; - -enum SQL_SC_FIPS127_2_TRANSITIONAL = 0x00000002L; -enum SQL_SC_SQL92_ENTRY = 0x00000001L; -enum SQL_SC_SQL92_FULL = 0x00000008L; -enum SQL_SC_SQL92_INTERMEDIATE = 0x00000004L; - -enum SQL_SCC_ISO92_CLI = 0x00000002L; -enum SQL_SCC_XOPEN_CLI_VERSION1 = 0x00000001L; - -enum SQL_SCHEMA_TERM = SQL_OWNER_TERM; -enum SQL_SCHEMA_USAGE = SQL_OWNER_USAGE; -enum SQL_SDF_CURRENT_DATE = 0x00000001L; -enum SQL_SDF_CURRENT_TIME = 0x00000002L; -enum SQL_SDF_CURRENT_TIMESTAMP = 0x00000004L; -enum SQL_SFKD_CASCADE = 0x00000001L; -enum SQL_SFKD_NO_ACTION = 0x00000002L; -enum SQL_SFKD_SET_DEFAULT = 0x00000004L; -enum SQL_SFKD_SET_NULL = 0x00000008L; -enum SQL_SFKU_CASCADE = 0x00000001L; -enum SQL_SFKU_NO_ACTION = 0x00000002L; -enum SQL_SFKU_SET_DEFAULT = 0x00000004L; -enum SQL_SFKU_SET_NULL = 0x00000008L; -enum SQL_SG_DELETE_TABLE = 0x00000020L; -enum SQL_SG_INSERT_COLUMN = 0x00000080L; -enum SQL_SG_INSERT_TABLE = 0x00000040L; -enum SQL_SG_REFERENCES_COLUMN = 0x00000200L; -enum SQL_SG_REFERENCES_TABLE = 0x00000100L; -enum SQL_SG_SELECT_TABLE = 0x00000400L; -enum SQL_SG_UPDATE_COLUMN = 0x00001000L; -enum SQL_SG_UPDATE_TABLE = 0x00000800L; -enum SQL_SG_USAGE_ON_CHARACTER_SET = 0x00000002L; -enum SQL_SG_USAGE_ON_COLLATION = 0x00000004L; -enum SQL_SG_USAGE_ON_DOMAIN = 0x00000001L; -enum SQL_SG_USAGE_ON_TRANSLATION = 0x00000008L; -enum SQL_SG_WITH_GRANT_OPTION = 0x00000010L; -enum SQL_SNVF_BIT_LENGTH = 0x00000001L; -enum SQL_SNVF_CHAR_LENGTH = 0x00000002L; -enum SQL_SNVF_CHARACTER_LENGTH = 0x00000004L; -enum SQL_SNVF_EXTRACT = 0x00000008L; -enum SQL_SNVF_OCTET_LENGTH = 0x00000010L; -enum SQL_SNVF_POSITION = 0x00000020L; -enum SQL_SP_BETWEEN = 0x00000800L; -enum SQL_SP_COMPARISON = 0x00001000L; -enum SQL_SP_EXISTS = 0x00000001L; -enum SQL_SP_IN = 0x00000400L; -enum SQL_SP_ISNOTNULL = 0x00000002L; -enum SQL_SP_ISNULL = 0x00000004L; -enum SQL_SP_LIKE = 0x00000200L; -enum SQL_SP_MATCH_FULL = 0x00000008L; -enum SQL_SP_MATCH_PARTIAL = 0x00000010L; -enum SQL_SP_MATCH_UNIQUE_FULL = 0x00000020L; -enum SQL_SP_MATCH_UNIQUE_PARTIAL = 0x00000040L; -enum SQL_SP_OVERLAPS = 0x00000080L; -enum SQL_SP_QUANTIFIED_COMPARISON = 0x00002000L; -enum SQL_SP_UNIQUE = 0x00000100L; -enum SQL_SQL_CONFORMANCE = 118; -enum SQL_SQL92_DATETIME_FUNCTIONS = 155; -enum SQL_SQL92_FOREIGN_KEY_DELETE_RULE = 156; -enum SQL_SQL92_FOREIGN_KEY_UPDATE_RULE = 157; -enum SQL_SQL92_GRANT = 158; -enum SQL_SQL92_NUMERIC_VALUE_FUNCTIONS = 159; -enum SQL_SQL92_PREDICATES = 160; -enum SQL_SQL92_RELATIONAL_JOIN_OPERATORS = 161; -enum SQL_SQL92_REVOKE = 162; -enum SQL_SQL92_ROW_VALUE_CONSTRUCTOR = 163; -enum SQL_SQL92_STRING_FUNCTIONS = 164; -enum SQL_SQL92_VALUE_EXPRESSIONS = 165; -enum SQL_SR_CASCADE = 0x00000020L; -enum SQL_SR_DELETE_TABLE = 0x00000080L; -enum SQL_SR_GRANT_OPTION_FOR = 0x00000010L; -enum SQL_SR_INSERT_COLUMN = 0x00000200L; -enum SQL_SR_INSERT_TABLE = 0x00000100L; -enum SQL_SR_REFERENCES_COLUMN = 0x00000800L; -enum SQL_SR_REFERENCES_TABLE = 0x00000400L; -enum SQL_SR_RESTRICT = 0x00000040L; -enum SQL_SR_SELECT_TABLE = 0x00001000L; -enum SQL_SR_UPDATE_COLUMN = 0x00004000L; -enum SQL_SR_UPDATE_TABLE = 0x00002000L; -enum SQL_SR_USAGE_ON_CHARACTER_SET = 0x00000002L; -enum SQL_SR_USAGE_ON_COLLATION = 0x00000004L; -enum SQL_SR_USAGE_ON_DOMAIN = 0x00000001L; -enum SQL_SR_USAGE_ON_TRANSLATION = 0x00000008L; -enum SQL_SRJO_CORRESPONDING_CLAUSE = 0x00000001L; -enum SQL_SRJO_CROSS_JOIN = 0x00000002L; -enum SQL_SRJO_EXCEPT_JOIN = 0x00000004L; -enum SQL_SRJO_FULL_OUTER_JOIN = 0x00000008L; -enum SQL_SRJO_INNER_JOIN = 0x00000010L; -enum SQL_SRJO_INTERSECT_JOIN = 0x00000020L; -enum SQL_SRJO_LEFT_OUTER_JOIN = 0x00000040L; -enum SQL_SRJO_NATURAL_JOIN = 0x00000080L; -enum SQL_SRJO_RIGHT_OUTER_JOIN = 0x00000100L; -enum SQL_SRJO_UNION_JOIN = 0x00000200L; -enum SQL_SRVC_DEFAULT = 0x00000004L; -enum SQL_SRVC_NULL = 0x00000002L; -enum SQL_SRVC_ROW_SUBQUERY = 0x00000008L; -enum SQL_SRVC_VALUE_EXPRESSION = 0x00000001L; -enum SQL_SSF_CONVERT = 0x00000001L; -enum SQL_SSF_LOWER = 0x00000002L; -enum SQL_SSF_SUBSTRING = 0x00000008L; -enum SQL_SSF_TRANSLATE = 0x00000010L; -enum SQL_SSF_TRIM_BOTH = 0x00000020L; -enum SQL_SSF_TRIM_LEADING = 0x00000040L; -enum SQL_SSF_TRIM_TRAILING = 0x00000080L; -enum SQL_SSF_UPPER = 0x00000004L; -enum SQL_STANDARD_CLI_CONFORMANCE = 166; -enum SQL_STATIC_CURSOR_ATTRIBUTES1 = 167; -enum SQL_STATIC_CURSOR_ATTRIBUTES2 = 168; -enum SQL_SU_DML_STATEMENTS = SQL_OU_DML_STATEMENTS; -enum SQL_SU_INDEX_DEFINITION = SQL_OU_INDEX_DEFINITION; -enum SQL_SU_PRIVILEGE_DEFINITION = SQL_OU_PRIVILEGE_DEFINITION; -enum SQL_SU_PROCEDURE_INVOCATION = SQL_OU_PROCEDURE_INVOCATION; -enum SQL_SU_TABLE_DEFINITION = SQL_OU_TABLE_DEFINITION; -enum SQL_SVE_CASE = 0x00000001L; -enum SQL_SVE_CAST = 0x00000002L; -enum SQL_SVE_COALESCE = 0x00000004L; -enum SQL_SVE_NULLIF = 0x00000008L; -enum SQL_UB_FIXED = SQL_UB_ON; -enum SQL_UB_VARIABLE = 2UL; -enum SQL_UNION_STATEMENT = SQL_UNION; -enum SQL_UPDATE_BY_BOOKMARK = 5; -enum SQL_US_UNION = SQL_U_UNION; -enum SQL_US_UNION_ALL = SQL_U_UNION_ALL; -}//[Yes] #endif /* ODBCVER >= 0x300 */ -static if (ODBCVER >= 0x0350) -{ -enum SQL_DESC_ROWVER = 35; -enum SQL_GUID = -11; -enum SQL_C_GUID = SQL_GUID; - //#ifdef ODBC_STD - //#define SQLAllocHandle SQLAllocHandleStd - //#define SQLAllocEnv(p) SQLAllocHandleStd(SQL_HANDLE_ENV, SQL_NULL_HANDLE, p) - //#define SQL_YEAR SQL_CODE_YEAR - //#define SQL_MONTH SQL_CODE_MONTH - //#define SQL_DAY SQL_CODE_DAY - //#define SQL_HOUR SQL_CODE_HOUR - //#define SQL_MINUTE SQL_CODE_MINUTE - //#define SQL_SECOND SQL_CODE_SECOND - //#define SQL_YEAR_TO_MONTH SQL_CODE_YEAR_TO_MONTH - //#define SQL_DAY_TO_HOUR SQL_CODE_DAY_TO_HOUR - //#define SQL_DAY_TO_MINUTE SQL_CODE_DAY_TO_MINUTE - //#define SQL_DAY_TO_SECOND SQL_CODE_DAY_TO_SECOND - //#define SQL_HOUR_TO_MINUTE SQL_CODE_HOUR_TO_MINUTE - //#define SQL_HOUR_TO_SECOND SQL_CODE_HOUR_TO_SECOND - //#define SQL_MINUTE_TO_SECOND SQL_CODE_MINUTE_TO_SECOND - //#endif /* ODBC_STD */ -}//#endif /* ODBCVER >= 0x0350 */ - -//static if (ODBCVER >= 0x0351) -//{ -enum SQL_ATTR_ANSI_APP=115; -enum SQL_AA_TRUE=1L; -enum SQL_AA_FALSE=0L; -//}//[Yes] #endif - -enum TRACE_VERSION=1000; -enum TRACE_ON=1; - -const char [] SQL_ODBC_KEYWORDS = - "ABSOLUTE, ACTION, ADA, ADD, ALL, ALLOCATE, ALTER, AND, ANY, ARE, AS, " - ~ "ASC, ASSERTION, AT, AUTHORIZATION, AVG, " - ~ "BEGIN, BETWEEN, BIT, BIT_LENGTH, BOTH, BY, CASCADE, CASCADED, CASE, CAST, CATALOG, " - ~ "CHAR, CHAR_LENGTH, CHARACTER, CHARACTER_LENGTH, CHECK, CLOSE, COALESCE, " - ~ "COLLATE, COLLATION, COLUMN, COMMIT, CONNECT, CONNECTION, CONSTRAINT, " - ~ "CONSTRAINTS, CONTINUE, CONVERT, CORRESPONDING, COUNT, CREATE, CROSS, CURRENT, " - ~ "CURRENT_DATE, CURRENT_TIME, CURRENT_TIMESTAMP, CURRENT_USER, CURSOR, " - ~ "DATE, DAY, DEALLOCATE, DEC, DECIMAL, DECLARE, DEFAULT, DEFERRABLE, " - ~ "DEFERRED, DELETE, DESC, DESCRIBE, DESCRIPTOR, DIAGNOSTICS, DISCONNECT, " - ~ "DISTINCT, DOMAIN, DOUBLE, DROP, " - ~ "ELSE, END, END-EXEC, ESCAPE, EXCEPT, EXCEPTION, EXEC, EXECUTE, " - ~ "EXISTS, EXTERNAL, EXTRACT, " - ~ "FALSE, FETCH, FIRST, FLOAT, FOR, FOREIGN, FORTRAN, FOUND, FROM, FULL, " - ~ "GET, GLOBAL, GO, GOTO, GRANT, GROUP, HAVING, HOUR, " - ~ "IDENTITY, IMMEDIATE, IN, INCLUDE, INDEX, INDICATOR, INITIALLY, INNER, " - ~ "INPUT, INSENSITIVE, INSERT, INT, INTEGER, INTERSECT, INTERVAL, INTO, IS, ISOLATION, " - ~ "JOIN, KEY, LANGUAGE, LAST, LEADING, LEFT, LEVEL, LIKE, LOCAL, LOWER, " - ~ "MATCH, MAX, MIN, MINUTE, MODULE, MONTH, " - ~ "NAMES, NATIONAL, NATURAL, NCHAR, NEXT, NO, NONE, NOT, NULL, NULLIF, NUMERIC, " - ~ "OCTET_LENGTH, OF, ON, ONLY, OPEN, OPTION, OR, ORDER, OUTER, OUTPUT, OVERLAPS, " - ~ "PAD, PARTIAL, PASCAL, PLI, POSITION, PRECISION, PREPARE, PRESERVE, " - ~ "PRIMARY, PRIOR, PRIVILEGES, PROCEDURE, PUBLIC, " - ~ "READ, REAL, REFERENCES, RELATIVE, RESTRICT, REVOKE, RIGHT, ROLLBACK, ROWS" - ~ "SCHEMA, SCROLL, SECOND, SECTION, SELECT, SESSION, SESSION_USER, SET, SIZE, " - ~ "SMALLINT, SOME, SPACE, SQL, SQLCA, SQLCODE, SQLERROR, SQLSTATE, SQLWARNING, " - ~ "SUBSTRING, SUM, SYSTEM_USER, " - ~ "TABLE, TEMPORARY, THEN, TIME, TIMESTAMP, TIMEZONE_HOUR, TIMEZONE_MINUTE, " - ~ "TO, TRAILING, TRANSACTION, TRANSLATE, TRANSLATION, TRIM, TRUE, " - ~ "UNION, UNIQUE, UNKNOWN, UPDATE, UPPER, USAGE, USER, USING, " - ~ "VALUE, VALUES, VARCHAR, VARYING, VIEW, WHEN, WHENEVER, WHERE, WITH, WORK, WRITE, " - ~ "YEAR, ZONE"; -extern (System) { - SQLRETURN SQLDriverConnect(SQLHDBC, SQLHWND, SQLCHAR*, SQLSMALLINT, SQLCHAR*, SQLSMALLINT, SQLSMALLINT*, SQLUSMALLINT); - SQLRETURN SQLBrowseConnect(SQLHDBC, SQLCHAR*, SQLSMALLINT, SQLCHAR*, SQLSMALLINT, SQLSMALLINT*); - SQLRETURN SQLColumnPrivileges(SQLHSTMT, SQLCHAR*, SQLSMALLINT, SQLCHAR*, SQLSMALLINT, SQLCHAR*, SQLSMALLINT, SQLCHAR*, SQLSMALLINT); - SQLRETURN SQLColAttributes(SQLHSTMT, SQLUSMALLINT, SQLUSMALLINT, SQLPOINTER, SQLSMALLINT, SQLSMALLINT*, SQLLEN*); - SQLRETURN SQLDescribeParam(SQLHSTMT, SQLUSMALLINT, SQLSMALLINT*, SQLULEN*, SQLSMALLINT*, SQLSMALLINT*); - SQLRETURN SQLExtendedFetch(SQLHSTMT, SQLUSMALLINT, SQLINTEGER, SQLUINTEGER*, SQLUSMALLINT*); - SQLRETURN SQLForeignKeys(SQLHSTMT, SQLCHAR*, SQLSMALLINT, SQLCHAR*, SQLSMALLINT, SQLCHAR*, SQLSMALLINT, SQLCHAR*, SQLSMALLINT, SQLCHAR*, SQLSMALLINT, SQLCHAR*, SQLSMALLINT); - SQLRETURN SQLMoreResults(SQLHSTMT); - SQLRETURN SQLNativeSql(SQLHDBC, SQLCHAR*, SQLINTEGER, SQLCHAR*, SQLINTEGER, SQLINTEGER*); - SQLRETURN SQLNumParams(SQLHSTMT, SQLSMALLINT*); - SQLRETURN SQLParamOptions(SQLHSTMT, SQLUINTEGER, SQLUINTEGER*); - SQLRETURN SQLPrimaryKeys(SQLHSTMT, SQLCHAR*, SQLSMALLINT, SQLCHAR*, SQLSMALLINT, SQLCHAR*, SQLSMALLINT); - SQLRETURN SQLProcedureColumns(SQLHSTMT, SQLCHAR*, SQLSMALLINT, SQLCHAR*, SQLSMALLINT, SQLCHAR*, SQLSMALLINT, SQLCHAR*, SQLSMALLINT); - SQLRETURN SQLProcedures(SQLHSTMT, SQLCHAR*, SQLSMALLINT, SQLCHAR*, SQLSMALLINT, SQLCHAR*, SQLSMALLINT); - SQLRETURN SQLSetPos(SQLHSTMT, SQLUSMALLINT, SQLUSMALLINT, SQLUSMALLINT); - SQLRETURN SQLTablePrivileges(SQLHSTMT, SQLCHAR*, SQLSMALLINT, SQLCHAR*, SQLSMALLINT, SQLCHAR*, SQLSMALLINT); - SQLRETURN SQLDrivers(SQLHENV, SQLUSMALLINT, SQLCHAR*, SQLSMALLINT, SQLSMALLINT*, SQLCHAR*, SQLSMALLINT, SQLSMALLINT*); - SQLRETURN SQLBindParameter(SQLHSTMT, SQLUSMALLINT, SQLSMALLINT, SQLSMALLINT, SQLSMALLINT, SQLULEN, SQLSMALLINT, SQLPOINTER, SQLLEN, SQLLEN*); - SQLRETURN SQLSetScrollOptions(SQLHSTMT, SQLUSMALLINT, SQLLEN, SQLUSMALLINT);/* deprecated */ - DWORD ODBCGetTryWaitValue(); - BOOL ODBCSetTryWaitValue(DWORD); - RETCODE TraceOpenLogFile(LPWSTR, LPWSTR, DWORD); - RETCODE TraceCloseLogFile(); - VOID TraceReturn(RETCODE, RETCODE); - DWORD TraceVersion(); - //static if (ODBCVER >= 0x0300) - //{ - SQLRETURN SQLBulkOperations(SQLHSTMT, SQLSMALLINT); - SQLRETURN SQLAllocHandleStd( SQLSMALLINT, SQLHANDLE, SQLHANDLE*); - //} -} diff --git a/phobos/etc/c/odbc/sqltypes.d b/phobos/etc/c/odbc/sqltypes.d deleted file mode 100644 index 50e8044..0000000 --- a/phobos/etc/c/odbc/sqltypes.d +++ /dev/null @@ -1,177 +0,0 @@ -/** - * Windows API header module - * - * Translated from MinGW Windows headers - * - * License: $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost License 1.0) - * Source: $(PHOBOSSRC etc/c/odbc/_sqltypes.d) - -Declarations for interfacing with the ODBC library. - -See_Also: $(LINK2 https://docs.microsoft.com/en-us/sql/odbc/reference/syntax/odbc-api-reference, - ODBC API Reference on MSDN) - */ -module etc.c.odbc.sqltypes; - -version (ANSI) {} else version = Unicode; - -/* Conversion notes: - It's assumed that ODBC >= 0x0300. -*/ - -// Import windows types or declare appropriate aliases if we are not. -version (Windows) -{ - public import core.sys.windows.windef; - public import core.sys.windows.basetyps; // for GUID -} -else -{ - alias ushort USHORT, WORD; - alias uint ULONG, LONG, BOOL, DWORD; - alias void VOID; - alias void* PVOID, HANDLE, HWND; - alias ubyte UCHAR, BYTE; - alias wchar* LPWSTR; - - align(1) struct GUID { // size is 16 - align(1): - DWORD Data1; - WORD Data2; - WORD Data3; - BYTE[8] Data4; - } -} -alias GUID SQLGUID; - -alias byte SCHAR, SQLSCHAR; -alias int SDWORD, SLONG, SQLINTEGER; -alias short SWORD, SSHORT, RETCODE, SQLSMALLINT; -alias ULONG UDWORD; -alias USHORT UWORD, SQLUSMALLINT; -alias double SDOUBLE, LDOUBLE; -alias float SFLOAT; -alias PVOID PTR, HENV, HDBC, HSTMT, SQLPOINTER; -alias UCHAR SQLCHAR; -// #ifndef _WIN64 -alias UDWORD SQLUINTEGER; -// #endif - -//static if (ODBCVER >= 0x0300) -//{ -alias HANDLE SQLHANDLE; -alias SQLHANDLE SQLHENV, SQLHDBC, SQLHSTMT, SQLHDESC; -/* -} -else -{ -alias void* SQLHENV; -alias void* SQLHDBC; -alias void* SQLHSTMT; -} -*/ -alias SQLSMALLINT SQLRETURN; -alias HWND SQLHWND; -alias ULONG BOOKMARK; - -alias SQLINTEGER SQLLEN, SQLROWOFFSET; -alias SQLUINTEGER SQLROWCOUNT, SQLULEN; -alias DWORD SQLTRANSID; -alias SQLUSMALLINT SQLSETPOSIROW; -alias wchar SQLWCHAR; - -version (Unicode) -{ - alias SQLWCHAR SQLTCHAR; -} -else -{ - alias SQLCHAR SQLTCHAR; -} -//static if (ODBCVER >= 0x0300) -//{ -alias ubyte SQLDATE, SQLDECIMAL; -alias double SQLDOUBLE, SQLFLOAT; -alias ubyte SQLNUMERIC; -alias float SQLREAL; -alias ubyte SQLTIME, SQLTIMESTAMP, SQLVARCHAR; -alias long ODBCINT64, SQLBIGINT; -alias ulong SQLUBIGINT; -//} - -struct DATE_STRUCT { - SQLSMALLINT year; - SQLUSMALLINT month; - SQLUSMALLINT day; -} - -struct TIME_STRUCT { - SQLUSMALLINT hour; - SQLUSMALLINT minute; - SQLUSMALLINT second; -} - -struct TIMESTAMP_STRUCT { - SQLSMALLINT year; - SQLUSMALLINT month; - SQLUSMALLINT day; - SQLUSMALLINT hour; - SQLUSMALLINT minute; - SQLUSMALLINT second; - SQLUINTEGER fraction; -} - -//static if (ODBCVER >= 0x0300) -//{ -alias DATE_STRUCT SQL_DATE_STRUCT; -alias TIME_STRUCT SQL_TIME_STRUCT; -alias TIMESTAMP_STRUCT SQL_TIMESTAMP_STRUCT; - -enum SQLINTERVAL { - SQL_IS_YEAR = 1, - SQL_IS_MONTH, - SQL_IS_DAY, - SQL_IS_HOUR, - SQL_IS_MINUTE, - SQL_IS_SECOND, - SQL_IS_YEAR_TO_MONTH, - SQL_IS_DAY_TO_HOUR, - SQL_IS_DAY_TO_MINUTE, - SQL_IS_DAY_TO_SECOND, - SQL_IS_HOUR_TO_MINUTE, - SQL_IS_HOUR_TO_SECOND, - SQL_IS_MINUTE_TO_SECOND -} - -struct SQL_YEAR_MONTH_STRUCT { - SQLUINTEGER year; - SQLUINTEGER month; -} - -struct SQL_DAY_SECOND_STRUCT { - SQLUINTEGER day; - SQLUINTEGER hour; - SQLUINTEGER minute; - SQLUINTEGER second; - SQLUINTEGER fraction; -} - -struct SQL_INTERVAL_STRUCT { - SQLINTERVAL interval_type; - SQLSMALLINT interval_sign; - union _intval { - SQL_YEAR_MONTH_STRUCT year_month; - SQL_DAY_SECOND_STRUCT day_second; - } - _intval intval; -} - -enum SQL_MAX_NUMERIC_LEN = 16; - -struct SQL_NUMERIC_STRUCT { - SQLCHAR precision; - SQLSCHAR scale; - SQLCHAR sign; - SQLCHAR[SQL_MAX_NUMERIC_LEN] val; -} -// } ODBCVER >= 0x0300 diff --git a/phobos/etc/c/odbc/sqlucode.d b/phobos/etc/c/odbc/sqlucode.d deleted file mode 100644 index 33d2b56..0000000 --- a/phobos/etc/c/odbc/sqlucode.d +++ /dev/null @@ -1,170 +0,0 @@ -/** - * Windows API header module - * - * Translated from MinGW Windows headers - * - * License: $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost License 1.0) - * Source: $(PHOBOSSRC etc/c/odbc/_sqlucode.d) - -Declarations for interfacing with the ODBC library. - -See_Also: $(LINK2 https://docs.microsoft.com/en-us/sql/odbc/reference/syntax/odbc-api-reference, - ODBC API Reference on MSDN) - */ -module etc.c.odbc.sqlucode; - -version (ANSI) {} else version = Unicode; - -import etc.c.odbc.sqlext; - -enum SQL_WCHAR = -8; -enum SQL_WVARCHAR = -9; -enum SQL_WLONGVARCHAR = -10; -enum SQL_C_WCHAR = SQL_WCHAR; - -enum SQL_SQLSTATE_SIZEW = 10; -version (Unicode) -{ -enum SQL_C_TCHAR = SQL_C_WCHAR; -} -else -{ -enum SQL_C_TCHAR = SQL_C_CHAR; -} - -// Moved from sqlext -static if (ODBCVER <= 0x0300) -{ -enum SQL_UNICODE = -95; -enum SQL_UNICODE_VARCHAR = -96; -enum SQL_UNICODE_LONGVARCHAR = -97; -enum SQL_UNICODE_CHAR = SQL_UNICODE; -} -else -{ -enum SQL_UNICODE = SQL_WCHAR; -enum SQL_UNICODE_VARCHAR = SQL_WVARCHAR; -enum SQL_UNICODE_LONGVARCHAR = SQL_WLONGVARCHAR; -enum SQL_UNICODE_CHAR = SQL_WCHAR; -} - -extern (System) { - SQLRETURN SQLBrowseConnectA(SQLHDBC, SQLCHAR*, SQLSMALLINT, SQLCHAR*, SQLSMALLINT, SQLSMALLINT*); - SQLRETURN SQLBrowseConnectW(SQLHDBC, SQLWCHAR*, SQLSMALLINT, SQLWCHAR*, SQLSMALLINT, SQLSMALLINT*); - SQLRETURN SQLColAttributeA(SQLHSTMT, SQLSMALLINT, SQLSMALLINT, SQLPOINTER, SQLSMALLINT, SQLSMALLINT*, SQLPOINTER); - SQLRETURN SQLColAttributeW(SQLHSTMT, SQLUSMALLINT, SQLUSMALLINT, SQLPOINTER, SQLSMALLINT, SQLSMALLINT*, SQLPOINTER); - SQLRETURN SQLColAttributesA(SQLHSTMT, SQLUSMALLINT, SQLUSMALLINT, SQLPOINTER, SQLSMALLINT, SQLSMALLINT*, SQLLEN*); - SQLRETURN SQLColAttributesW(SQLHSTMT, SQLUSMALLINT, SQLUSMALLINT, SQLPOINTER, SQLSMALLINT, SQLSMALLINT*, SQLLEN*); - SQLRETURN SQLColumnPrivilegesA( SQLHSTMT, SQLCHAR*, SQLSMALLINT, SQLCHAR*, SQLSMALLINT, SQLCHAR*, SQLSMALLINT , SQLCHAR*, SQLSMALLINT ); - SQLRETURN SQLColumnPrivilegesW( SQLHSTMT, SQLWCHAR*, SQLSMALLINT, SQLWCHAR*, SQLSMALLINT, SQLWCHAR*, SQLSMALLINT, SQLWCHAR*, SQLSMALLINT ); - SQLRETURN SQLColumnsA(SQLHSTMT, SQLCHAR*, SQLSMALLINT, SQLCHAR*, SQLSMALLINT, SQLCHAR*, SQLSMALLINT , SQLCHAR*, SQLSMALLINT ); - SQLRETURN SQLColumnsW(SQLHSTMT, SQLWCHAR*, SQLSMALLINT, SQLWCHAR*, SQLSMALLINT, SQLWCHAR*, SQLSMALLINT , SQLWCHAR*, SQLSMALLINT ); - SQLRETURN SQLConnectA(SQLHDBC, SQLCHAR*, SQLSMALLINT, SQLCHAR*, SQLSMALLINT, SQLCHAR*, SQLSMALLINT); - SQLRETURN SQLConnectW(SQLHDBC, SQLWCHAR*, SQLSMALLINT, SQLWCHAR*, SQLSMALLINT, SQLWCHAR*, SQLSMALLINT); - SQLRETURN SQLDataSourcesA(SQLHENV, SQLUSMALLINT, SQLCHAR*, SQLSMALLINT, SQLSMALLINT*, SQLCHAR*, SQLSMALLINT, SQLSMALLINT*); - SQLRETURN SQLDataSourcesW(SQLHENV, SQLUSMALLINT, SQLWCHAR*, SQLSMALLINT, SQLSMALLINT*, SQLWCHAR*, SQLSMALLINT, SQLSMALLINT*); - SQLRETURN SQLDescribeColA(SQLHSTMT, SQLUSMALLINT, SQLCHAR*, SQLSMALLINT, SQLSMALLINT*, SQLSMALLINT*, SQLULEN*, SQLSMALLINT*, SQLSMALLINT*); - SQLRETURN SQLDescribeColW(SQLHSTMT, SQLUSMALLINT, SQLWCHAR*, SQLSMALLINT, SQLSMALLINT*, SQLSMALLINT*, SQLULEN*, SQLSMALLINT*, SQLSMALLINT*); - SQLRETURN SQLDriverConnectA(SQLHDBC, SQLHWND, SQLCHAR*, SQLSMALLINT, SQLCHAR*, SQLSMALLINT, SQLSMALLINT*, SQLUSMALLINT); - SQLRETURN SQLDriverConnectW(SQLHDBC, SQLHWND, SQLWCHAR*, SQLSMALLINT, SQLWCHAR*, SQLSMALLINT, SQLSMALLINT*, SQLUSMALLINT); - SQLRETURN SQLDriversA(SQLHENV, SQLUSMALLINT, SQLCHAR*, SQLSMALLINT, SQLSMALLINT*, SQLCHAR*, SQLSMALLINT, SQLSMALLINT*); - SQLRETURN SQLDriversW(SQLHENV, SQLUSMALLINT, SQLWCHAR*, SQLSMALLINT, SQLSMALLINT*, SQLWCHAR*, SQLSMALLINT, SQLSMALLINT*); - SQLRETURN SQLErrorA(SQLHENV, SQLHDBC, SQLHSTMT, SQLCHAR*, SQLINTEGER*, SQLCHAR*, SQLSMALLINT, SQLSMALLINT*); - SQLRETURN SQLErrorW(SQLHENV, SQLHDBC, SQLHSTMT, SQLWCHAR*, SQLINTEGER*, SQLWCHAR*, SQLSMALLINT, SQLSMALLINT*); - SQLRETURN SQLExecDirectA(SQLHSTMT, SQLCHAR*, SQLINTEGER); - SQLRETURN SQLExecDirectW(SQLHSTMT, SQLWCHAR*, SQLINTEGER); - SQLRETURN SQLForeignKeysA(SQLHSTMT, SQLCHAR*, SQLSMALLINT, SQLCHAR*, SQLSMALLINT, SQLCHAR*, SQLSMALLINT, SQLCHAR*, SQLSMALLINT, SQLCHAR*, SQLSMALLINT, SQLCHAR*, SQLSMALLINT); - SQLRETURN SQLForeignKeysW(SQLHSTMT, SQLWCHAR*, SQLSMALLINT, SQLWCHAR*, SQLSMALLINT, SQLWCHAR*, SQLSMALLINT, SQLWCHAR*, SQLSMALLINT, SQLWCHAR*, SQLSMALLINT, SQLWCHAR*, SQLSMALLINT); - SQLRETURN SQLGetConnectAttrA(SQLHDBC, SQLINTEGER, SQLPOINTER, SQLINTEGER, SQLINTEGER*); - SQLRETURN SQLGetConnectAttrW(SQLHDBC, SQLINTEGER, SQLPOINTER, SQLINTEGER, SQLINTEGER*); - SQLRETURN SQLGetConnectOptionA(SQLHDBC, SQLUSMALLINT, SQLPOINTER); - SQLRETURN SQLGetConnectOptionW(SQLHDBC, SQLUSMALLINT, SQLPOINTER); - SQLRETURN SQLGetCursorNameA(SQLHSTMT, SQLCHAR*, SQLSMALLINT, SQLSMALLINT*); - SQLRETURN SQLGetCursorNameW(SQLHSTMT, SQLWCHAR*, SQLSMALLINT, SQLSMALLINT*); - SQLRETURN SQLGetInfoA(SQLHDBC, SQLUSMALLINT, SQLPOINTER, SQLSMALLINT, SQLSMALLINT*); - SQLRETURN SQLGetInfoW(SQLHDBC, SQLUSMALLINT, SQLPOINTER, SQLSMALLINT, SQLSMALLINT*); - SQLRETURN SQLGetTypeInfoA(SQLHSTMT, SQLSMALLINT); - SQLRETURN SQLGetTypeInfoW(SQLHSTMT, SQLSMALLINT); - SQLRETURN SQLNativeSqlA(SQLHDBC, SQLCHAR*, SQLINTEGER, SQLCHAR*, SQLINTEGER, SQLINTEGER*); - SQLRETURN SQLNativeSqlW(SQLHDBC, SQLWCHAR*, SQLINTEGER, SQLWCHAR*, SQLINTEGER, SQLINTEGER*); - SQLRETURN SQLPrepareA(SQLHSTMT, SQLCHAR*, SQLINTEGER); - SQLRETURN SQLPrepareW(SQLHSTMT, SQLWCHAR*, SQLINTEGER); - SQLRETURN SQLPrimaryKeysA(SQLHSTMT, SQLCHAR*, SQLSMALLINT, SQLCHAR*, SQLSMALLINT, SQLCHAR*, SQLSMALLINT ); - SQLRETURN SQLPrimaryKeysW(SQLHSTMT, SQLWCHAR*, SQLSMALLINT, SQLWCHAR*, SQLSMALLINT, SQLWCHAR*, SQLSMALLINT); - SQLRETURN SQLProcedureColumnsA(SQLHSTMT, SQLCHAR*, SQLSMALLINT, SQLCHAR*, SQLSMALLINT, SQLCHAR*, SQLSMALLINT, SQLCHAR*, SQLSMALLINT); - SQLRETURN SQLProcedureColumnsW(SQLHSTMT, SQLWCHAR*, SQLSMALLINT, SQLWCHAR*, SQLSMALLINT, SQLWCHAR*, SQLSMALLINT, SQLWCHAR*, SQLSMALLINT); - SQLRETURN SQLProceduresA(SQLHSTMT, SQLCHAR*, SQLSMALLINT, SQLCHAR*, SQLSMALLINT, SQLCHAR*, SQLSMALLINT); - SQLRETURN SQLProceduresW(SQLHSTMT, SQLWCHAR*, SQLSMALLINT, SQLWCHAR*, SQLSMALLINT, SQLWCHAR*, SQLSMALLINT); - SQLRETURN SQLSetConnectAttrA(SQLHDBC, SQLINTEGER, SQLPOINTER, SQLINTEGER); - SQLRETURN SQLSetConnectAttrW(SQLHDBC, SQLINTEGER, SQLPOINTER, SQLINTEGER); - SQLRETURN SQLSetConnectOptionA(SQLHDBC, SQLUSMALLINT, SQLULEN); - SQLRETURN SQLSetConnectOptionW(SQLHDBC, SQLUSMALLINT, SQLULEN); - SQLRETURN SQLSetCursorNameA(SQLHSTMT, SQLCHAR*, SQLSMALLINT); - SQLRETURN SQLSetCursorNameW(SQLHSTMT, SQLWCHAR*, SQLSMALLINT); - SQLRETURN SQLSpecialColumnsA(SQLHSTMT, SQLUSMALLINT, SQLCHAR*, SQLSMALLINT, SQLCHAR*, SQLSMALLINT, SQLCHAR*, SQLSMALLINT , SQLUSMALLINT, SQLUSMALLINT); - SQLRETURN SQLSpecialColumnsW(SQLHSTMT, SQLUSMALLINT, SQLWCHAR*, SQLSMALLINT, SQLWCHAR*, SQLSMALLINT, SQLWCHAR*, SQLSMALLINT , SQLUSMALLINT, SQLUSMALLINT); - SQLRETURN SQLStatisticsA(SQLHSTMT, SQLCHAR*, SQLSMALLINT, SQLCHAR*, SQLSMALLINT, SQLCHAR*, SQLSMALLINT , SQLUSMALLINT, SQLUSMALLINT); - SQLRETURN SQLStatisticsW(SQLHSTMT, SQLWCHAR*, SQLSMALLINT, SQLWCHAR*, SQLSMALLINT, SQLWCHAR*, SQLSMALLINT , SQLUSMALLINT, SQLUSMALLINT); - SQLRETURN SQLTablePrivilegesA(SQLHSTMT, SQLCHAR*, SQLSMALLINT, SQLCHAR*, SQLSMALLINT, SQLCHAR*, SQLSMALLINT); - SQLRETURN SQLTablePrivilegesW(SQLHSTMT, SQLWCHAR*, SQLSMALLINT, SQLWCHAR*, SQLSMALLINT, SQLWCHAR*, SQLSMALLINT ); - SQLRETURN SQLTablesA(SQLHSTMT, SQLCHAR*, SQLSMALLINT, SQLCHAR*, SQLSMALLINT, SQLCHAR*, SQLSMALLINT, SQLCHAR*, SQLSMALLINT); - SQLRETURN SQLTablesW(SQLHSTMT, SQLWCHAR*, SQLSMALLINT, SQLWCHAR*, SQLSMALLINT, SQLWCHAR*, SQLSMALLINT, SQLWCHAR*, SQLSMALLINT); - static if (ODBCVER >= 0x0300) - { - SQLRETURN SQLGetDescFieldA(SQLHDESC, SQLSMALLINT, SQLSMALLINT, SQLPOINTER, SQLINTEGER, SQLINTEGER*); - SQLRETURN SQLGetDescFieldW(SQLHDESC, SQLSMALLINT, SQLSMALLINT, SQLPOINTER, SQLINTEGER, SQLINTEGER*); - SQLRETURN SQLSetDescFieldA(SQLHDESC, SQLSMALLINT, SQLSMALLINT, SQLPOINTER, SQLINTEGER); - SQLRETURN SQLSetDescFieldW(SQLHDESC, SQLSMALLINT, SQLSMALLINT, SQLPOINTER, SQLINTEGER); - SQLRETURN SQLGetDescRecA(SQLHDESC, SQLSMALLINT, SQLCHAR*, SQLSMALLINT, SQLSMALLINT*, SQLSMALLINT*, SQLSMALLINT*, SQLLEN*, SQLSMALLINT*, SQLSMALLINT*, SQLSMALLINT*); - SQLRETURN SQLGetDescRecW(SQLHDESC, SQLSMALLINT, SQLWCHAR*, SQLSMALLINT, SQLSMALLINT*, SQLSMALLINT*, SQLSMALLINT*, SQLLEN*, SQLSMALLINT*, SQLSMALLINT*, SQLSMALLINT*); - SQLRETURN SQLGetDiagFieldA(SQLSMALLINT, SQLHANDLE, SQLSMALLINT, SQLSMALLINT, SQLPOINTER, SQLSMALLINT, SQLSMALLINT*); - SQLRETURN SQLGetDiagFieldW(SQLSMALLINT, SQLHANDLE, SQLSMALLINT, SQLSMALLINT, SQLPOINTER, SQLSMALLINT, SQLSMALLINT*); - SQLRETURN SQLGetDiagRecA(SQLSMALLINT, SQLHANDLE, SQLSMALLINT, SQLCHAR*, SQLINTEGER*, SQLCHAR*, SQLSMALLINT, SQLSMALLINT*); - SQLRETURN SQLGetDiagRecW(SQLSMALLINT, SQLHANDLE, SQLSMALLINT, SQLWCHAR*, SQLINTEGER*, SQLWCHAR*, SQLSMALLINT, SQLSMALLINT*); - SQLRETURN SQLGetStmtAttrA(SQLHSTMT, SQLINTEGER, SQLPOINTER, SQLINTEGER, SQLINTEGER*); - SQLRETURN SQLGetStmtAttrW(SQLHSTMT, SQLINTEGER, SQLPOINTER, SQLINTEGER, SQLINTEGER*); - SQLRETURN SQLSetStmtAttrA(SQLHSTMT, SQLINTEGER, SQLPOINTER, SQLINTEGER); - SQLRETURN SQLSetStmtAttrW(SQLHSTMT, SQLINTEGER, SQLPOINTER, SQLINTEGER); - } // #endif /* (ODBCVER >= 0x0300) */ -} - -version (Unicode) -{ - alias SQLBrowseConnectW SQLBrowseConnect; - alias SQLColAttributeW SQLColAttribute; - alias SQLColAttributesW SQLColAttributes; - alias SQLColumnPrivilegesW SQLColumnPrivileges; - alias SQLColumnsW SQLColumns; - alias SQLConnectW SQLConnect; - alias SQLDataSourcesW SQLDataSources; - alias SQLDescribeColW SQLDescribeCol; - alias SQLDriverConnectW SQLDriverConnect; - alias SQLDriversW SQLDrivers; - alias SQLErrorW SQLError; - alias SQLExecDirectW SQLExecDirect; - alias SQLForeignKeysW SQLForeignKeys; - alias SQLGetConnectAttrW SQLGetConnectAttr; - alias SQLGetConnectOptionW SQLGetConnectOption; - alias SQLGetCursorNameW SQLGetCursorName; - alias SQLGetDescFieldW SQLGetDescField; - alias SQLGetDescRecW SQLGetDescRec; - alias SQLGetDiagFieldW SQLGetDiagField; - alias SQLGetDiagRecW SQLGetDiagRec; - alias SQLGetInfoW SQLGetInfo; - alias SQLGetStmtAttrW SQLGetStmtAttr; - alias SQLGetTypeInfoW SQLGetTypeInfo; - alias SQLNativeSqlW SQLNativeSql; - alias SQLPrepareW SQLPrepare; - alias SQLPrimaryKeysW SQLPrimaryKeys; - alias SQLProcedureColumnsW SQLProcedureColumns; - alias SQLProceduresW SQLProcedures; - alias SQLSetConnectAttrW SQLSetConnectAttr; - alias SQLSetConnectOptionW SQLSetConnectOption; - alias SQLSetCursorNameW SQLSetCursorName; - alias SQLSetDescFieldW SQLSetDescField; - alias SQLSetStmtAttrW SQLSetStmtAttr; - alias SQLSpecialColumnsW SQLSpecialColumns; - alias SQLStatisticsW SQLStatistics; - alias SQLTablePrivilegesW SQLTablePrivileges; - alias SQLTablesW SQLTables; -} diff --git a/phobos/etc/c/sqlite3.d b/phobos/etc/c/sqlite3.d deleted file mode 100644 index a06ff4c..0000000 --- a/phobos/etc/c/sqlite3.d +++ /dev/null @@ -1,2880 +0,0 @@ -module etc.c.sqlite3; -/* -** 2001-09-15 -** -** The author disclaims copyright to this source code. In place of -** a legal notice, here is a blessing: -** -** May you do good and not evil. -** May you find forgiveness for yourself and forgive others. -** May you share freely, never taking more than you give. -** -************************************************************************* -** This header file defines the interface that the SQLite library -** presents to client programs. If a C-function, structure, datatype, -** or constant definition does not appear in this file, then it is -** not a published API of SQLite, is subject to change without -** notice, and should not be referenced by programs that use SQLite. -** -** Some of the definitions that are in this file are marked as -** "experimental". Experimental interfaces are normally new -** features recently added to SQLite. We do not anticipate changes -** to experimental interfaces but reserve the right to make minor changes -** if experience from use "in the wild" suggest such changes are prudent. -** -** The official C-language API documentation for SQLite is derived -** from comments in this file. This file is the authoritative source -** on how SQLite interfaces are suppose to operate. -** -** The name of this file under configuration management is "sqlite.h.in". -** The makefile makes some minor changes to this file (such as inserting -** the version number) and changes its name to "sqlite3.h" as -** part of the build process. -*/ - -import core.stdc.stdarg : va_list; -import core.stdc.config : c_ulong; - -extern (C) __gshared nothrow: - -/** -* CAPI3REF: Compile-Time Library Version Numbers -*/ -enum SQLITE_VERSION = "3.33.0"; -/// Ditto -enum SQLITE_VERSION_NUMBER = 3033000; -/// Ditto -enum SQLITE_SOURCE_ID = "2020-08-14 13:23:32 fca8dc8b578f215a969cd899336378966156154710873e68b3d9ac5881b0ff3f"; - -/** -* CAPI3REF: Run-Time Library Version Numbers -*/ -extern immutable(char)* sqlite3_version; -/// Ditto -immutable(char)* sqlite3_libversion(); -/// Ditto -immutable(char)* sqlite3_sourceid(); -/// Ditto -int sqlite3_libversion_number(); - -/** -* CAPI3REF: Run-Time Library Compilation Options Diagnostics -*/ -int sqlite3_compileoption_used(const char *zOptName); -/// Ditto -immutable(char)* sqlite3_compileoption_get(int N); - -/** -* CAPI3REF: Test To See If The Library Is Threadsafe -*/ -int sqlite3_threadsafe(); - -/** -* CAPI3REF: Database Connection Handle -*/ -struct sqlite3; - -/// -alias sqlite3_int64 = long; -/// -alias sqlite3_uint64 = ulong; - -/** -* CAPI3REF: Closing A Database Connection -* -*/ -int sqlite3_close(sqlite3 *); -int sqlite3_close_v2(sqlite3*); - -/** -* The type for a callback function. -* This is legacy and deprecated. It is included for historical -* compatibility and is not documented. -*/ -alias sqlite3_callback = int function (void*,int,char**, char**); - -/** -* CAPI3REF: One-Step Query Execution Interface -*/ -int sqlite3_exec( - sqlite3*, /** An open database */ - const(char)*sql, /** SQL to be evaluated */ - int function (void*,int,char**,char**) callback, /** Callback function */ - void *, /** 1st argument to callback */ - char **errmsg /** Error msg written here */ -); - -/** -* CAPI3REF: Result Codes -*/ -enum -{ - SQLITE_OK = 0, /** Successful result */ -/* beginning-of-error-codes */ -/// Ditto - SQLITE_ERROR = 1, /** Generic error */ - SQLITE_INTERNAL = 2, /** Internal logic error in SQLite */ - SQLITE_PERM = 3, /** Access permission denied */ - SQLITE_ABORT = 4, /** Callback routine requested an abort */ - SQLITE_BUSY = 5, /** The database file is locked */ - SQLITE_LOCKED = 6, /** A table in the database is locked */ - SQLITE_NOMEM = 7, /** A malloc() failed */ - SQLITE_READONLY = 8, /** Attempt to write a readonly database */ - SQLITE_INTERRUPT = 9, /** Operation terminated by sqlite3_interrupt()*/ - SQLITE_IOERR = 10, /** Some kind of disk I/O error occurred */ - SQLITE_CORRUPT = 11, /** The database disk image is malformed */ - SQLITE_NOTFOUND = 12, /** Unknown opcode in sqlite3_file_control() */ - SQLITE_FULL = 13, /** Insertion failed because database is full */ - SQLITE_CANTOPEN = 14, /** Unable to open the database file */ - SQLITE_PROTOCOL = 15, /** Database lock protocol error */ - SQLITE_EMPTY = 16, /** Internal use only */ - SQLITE_SCHEMA = 17, /** The database schema changed */ - SQLITE_TOOBIG = 18, /** String or BLOB exceeds size limit */ - SQLITE_CONSTRAINT = 19, /** Abort due to constraint violation */ - SQLITE_MISMATCH = 20, /** Data type mismatch */ - SQLITE_MISUSE = 21, /** Library used incorrectly */ - SQLITE_NOLFS = 22, /** Uses OS features not supported on host */ - SQLITE_AUTH = 23, /** Authorization denied */ - SQLITE_FORMAT = 24, /** Not used */ - SQLITE_RANGE = 25, /** 2nd parameter to sqlite3_bind out of range */ - SQLITE_NOTADB = 26, /** File opened that is not a database file */ - SQLITE_NOTICE = 27, - SQLITE_WARNING = 28, - SQLITE_ROW = 100, /** sqlite3_step() has another row ready */ - SQLITE_DONE = 101 /** sqlite3_step() has finished executing */ -} -/* end-of-error-codes */ - -/** -* CAPI3REF: Extended Result Codes -*/ -enum -{ - SQLITE_ERROR_MISSING_COLLSEQ = (SQLITE_ERROR | (1 << 8)), - SQLITE_ERROR_RETRY = (SQLITE_ERROR | (2 << 8)), - SQLITE_ERROR_SNAPSHOT = (SQLITE_ERROR | (3 << 8)), - SQLITE_IOERR_READ = (SQLITE_IOERR | (1 << 8)), - SQLITE_IOERR_SHORT_READ = (SQLITE_IOERR | (2 << 8)), - SQLITE_IOERR_WRITE = (SQLITE_IOERR | (3 << 8)), - SQLITE_IOERR_FSYNC = (SQLITE_IOERR | (4 << 8)), - SQLITE_IOERR_DIR_FSYNC = (SQLITE_IOERR | (5 << 8)), - SQLITE_IOERR_TRUNCATE = (SQLITE_IOERR | (6 << 8)), - SQLITE_IOERR_FSTAT = (SQLITE_IOERR | (7 << 8)), - SQLITE_IOERR_UNLOCK = (SQLITE_IOERR | (8 << 8)), - SQLITE_IOERR_RDLOCK = (SQLITE_IOERR | (9 << 8)), - SQLITE_IOERR_DELETE = (SQLITE_IOERR | (10 << 8)), - SQLITE_IOERR_BLOCKED = (SQLITE_IOERR | (11 << 8)), - SQLITE_IOERR_NOMEM = (SQLITE_IOERR | (12 << 8)), - SQLITE_IOERR_ACCESS = (SQLITE_IOERR | (13 << 8)), - SQLITE_IOERR_CHECKRESERVEDLOCK = (SQLITE_IOERR | (14 << 8)), - SQLITE_IOERR_LOCK = (SQLITE_IOERR | (15 << 8)), - SQLITE_IOERR_CLOSE = (SQLITE_IOERR | (16 << 8)), - SQLITE_IOERR_DIR_CLOSE = (SQLITE_IOERR | (17 << 8)), - SQLITE_IOERR_SHMOPEN = (SQLITE_IOERR | (18 << 8)), - SQLITE_IOERR_SHMSIZE = (SQLITE_IOERR | (19 << 8)), - SQLITE_IOERR_SHMLOCK = (SQLITE_IOERR | (20 << 8)), - SQLITE_IOERR_SHMMAP = (SQLITE_IOERR | (21 << 8)), - SQLITE_IOERR_SEEK = (SQLITE_IOERR | (22 << 8)), - SQLITE_IOERR_DELETE_NOENT = (SQLITE_IOERR | (23 << 8)), - SQLITE_IOERR_MMAP = (SQLITE_IOERR | (24 << 8)), - SQLITE_IOERR_GETTEMPPATH = (SQLITE_IOERR | (25 << 8)), - SQLITE_IOERR_CONVPATH = (SQLITE_IOERR | (26 << 8)), - SQLITE_IOERR_VNODE = (SQLITE_IOERR | (27 << 8)), - SQLITE_IOERR_AUTH = (SQLITE_IOERR | (28 << 8)), - SQLITE_IOERR_BEGIN_ATOMIC = (SQLITE_IOERR | (29 << 8)), - SQLITE_IOERR_COMMIT_ATOMIC = (SQLITE_IOERR | (30 << 8)), - SQLITE_IOERR_ROLLBACK_ATOMIC = (SQLITE_IOERR | (31 << 8)), - SQLITE_IOERR_DATA = (SQLITE_IOERR | (32 << 8)), - SQLITE_LOCKED_SHAREDCACHE = (SQLITE_LOCKED | (1 << 8)), - SQLITE_LOCKED_VTAB = (SQLITE_LOCKED | (2 << 8)), - SQLITE_BUSY_RECOVERY = (SQLITE_BUSY | (1 << 8)), - SQLITE_BUSY_SNAPSHOT = (SQLITE_BUSY | (2 << 8)), - SQLITE_BUSY_TIMEOUT = (SQLITE_BUSY | (3 << 8)), - SQLITE_CANTOPEN_NOTEMPDIR = (SQLITE_CANTOPEN | (1 << 8)), - SQLITE_CANTOPEN_ISDIR = (SQLITE_CANTOPEN | (2 << 8)), - SQLITE_CANTOPEN_FULLPATH = (SQLITE_CANTOPEN | (3 << 8)), - SQLITE_CANTOPEN_CONVPATH = (SQLITE_CANTOPEN | (4 << 8)), - SQLITE_CANTOPEN_DIRTYWAL = (SQLITE_CANTOPEN | (5 << 8)), /* Not Used */ - SQLITE_CANTOPEN_SYMLINK = (SQLITE_CANTOPEN | (6 << 8)), - SQLITE_CORRUPT_VTAB = (SQLITE_CORRUPT | (1 << 8)), - SQLITE_CORRUPT_SEQUENCE = (SQLITE_CORRUPT | (2 << 8)), - SQLITE_CORRUPT_INDEX = (SQLITE_CORRUPT | (3 << 8)), - SQLITE_READONLY_RECOVERY = (SQLITE_READONLY | (1 << 8)), - SQLITE_READONLY_CANTLOCK = (SQLITE_READONLY | (2 << 8)), - SQLITE_READONLY_ROLLBACK = (SQLITE_READONLY | (3 << 8)), - SQLITE_READONLY_DBMOVED = (SQLITE_READONLY | (4 << 8)), - SQLITE_READONLY_CANTINIT = (SQLITE_READONLY | (5 << 8)), - SQLITE_READONLY_DIRECTORY = (SQLITE_READONLY | (6 << 8)), - SQLITE_ABORT_ROLLBACK = (SQLITE_ABORT | (2 << 8)), - SQLITE_CONSTRAINT_CHECK = (SQLITE_CONSTRAINT | (1 << 8)), - SQLITE_CONSTRAINT_COMMITHOOK = (SQLITE_CONSTRAINT | (2 << 8)), - SQLITE_CONSTRAINT_FOREIGNKEY = (SQLITE_CONSTRAINT | (3 << 8)), - SQLITE_CONSTRAINT_FUNCTION = (SQLITE_CONSTRAINT | (4 << 8)), - SQLITE_CONSTRAINT_NOTNULL = (SQLITE_CONSTRAINT | (5 << 8)), - SQLITE_CONSTRAINT_PRIMARYKEY = (SQLITE_CONSTRAINT | (6 << 8)), - SQLITE_CONSTRAINT_TRIGGER = (SQLITE_CONSTRAINT | (7 << 8)), - SQLITE_CONSTRAINT_UNIQUE = (SQLITE_CONSTRAINT | (8 << 8)), - SQLITE_CONSTRAINT_VTAB = (SQLITE_CONSTRAINT | (9 << 8)), - SQLITE_CONSTRAINT_ROWID = (SQLITE_CONSTRAINT |(10 << 8)), - QLITE_CONSTRAINT_PINNED = (SQLITE_CONSTRAINT |(11 << 8)), - SQLITE_NOTICE_RECOVER_WAL = (SQLITE_NOTICE | (1 << 8)), - SQLITE_NOTICE_RECOVER_ROLLBACK = (SQLITE_NOTICE | (2 << 8)), - SQLITE_WARNING_AUTOINDEX = (SQLITE_WARNING | (1 << 8)), - SQLITE_AUTH_USER = (SQLITE_AUTH | (1 << 8)), - SQLITE_OK_LOAD_PERMANENTLY = (SQLITE_OK | (1 << 8)), - SQLITE_OK_SYMLINK = (SQLITE_OK | (2 << 8)) -} - -/** -* CAPI3REF: Flags For File Open Operations -*/ -enum -{ - SQLITE_OPEN_READONLY = 0x00000001, /** Ok for sqlite3_open_v2() */ - SQLITE_OPEN_READWRITE = 0x00000002, /** Ok for sqlite3_open_v2() */ - SQLITE_OPEN_CREATE = 0x00000004, /** Ok for sqlite3_open_v2() */ - SQLITE_OPEN_DELETEONCLOSE = 0x00000008, /** VFS only */ - SQLITE_OPEN_EXCLUSIVE = 0x00000010, /** VFS only */ - SQLITE_OPEN_AUTOPROXY = 0x00000020, /** VFS only */ - SQLITE_OPEN_URI = 0x00000040, /** Ok for sqlite3_open_v2() */ - SQLITE_OPEN_MEMORY = 0x00000080, /** Ok for sqlite3_open_v2() */ - SQLITE_OPEN_MAIN_DB = 0x00000100, /** VFS only */ - SQLITE_OPEN_TEMP_DB = 0x00000200, /** VFS only */ - SQLITE_OPEN_TRANSIENT_DB = 0x00000400, /** VFS only */ - SQLITE_OPEN_MAIN_JOURNAL = 0x00000800, /** VFS only */ - SQLITE_OPEN_TEMP_JOURNAL = 0x00001000, /** VFS only */ - SQLITE_OPEN_SUBJOURNAL = 0x00002000, /** VFS only */ - SQLITE_OPEN_SUPER_JOURNAL = 0x00004000, /** VFS only */ - SQLITE_OPEN_NOMUTEX = 0x00008000, /** Ok for sqlite3_open_v2() */ - SQLITE_OPEN_FULLMUTEX = 0x00010000, /** Ok for sqlite3_open_v2() */ - SQLITE_OPEN_SHAREDCACHE = 0x00020000, /** Ok for sqlite3_open_v2() */ - SQLITE_OPEN_PRIVATECACHE = 0x00040000, /** Ok for sqlite3_open_v2() */ - SQLITE_OPEN_WAL = 0x00080000, /** VFS only */ - SQLITE_OPEN_NOFOLLOW = 0x01000000 /** Ok for sqlite3_open_v2() */ -} - -deprecated ("Legacy compatibility") -{ - alias SQLITE_OPEN_MASTER_JOURNAL = SQLITE_OPEN_SUPER_JOURNAL; /** VFS only */ -} - -/** -* CAPI3REF: Device Characteristics -*/ -enum -{ - SQLITE_IOCAP_ATOMIC = 0x00000001, - SQLITE_IOCAP_ATOMIC512 = 0x00000002, - SQLITE_IOCAP_ATOMIC1K = 0x00000004, - SQLITE_IOCAP_ATOMIC2K = 0x00000008, - SQLITE_IOCAP_ATOMIC4K = 0x00000010, - SQLITE_IOCAP_ATOMIC8K = 0x00000020, - SQLITE_IOCAP_ATOMIC16K = 0x00000040, - SQLITE_IOCAP_ATOMIC32K = 0x00000080, - SQLITE_IOCAP_ATOMIC64K = 0x00000100, - SQLITE_IOCAP_SAFE_APPEND = 0x00000200, - SQLITE_IOCAP_SEQUENTIAL = 0x00000400, - SQLITE_IOCAP_UNDELETABLE_WHEN_OPEN = 0x00000800, - SQLITE_IOCAP_POWERSAFE_OVERWRITE = 0x00001000, - SQLITE_IOCAP_IMMUTABLE = 0x00002000, - SQLITE_IOCAP_BATCH_ATOMIC = 0x00004000 -} - -/** -* CAPI3REF: File Locking Levels -*/ -enum -{ - SQLITE_LOCK_NONE = 0, - SQLITE_LOCK_SHARED = 1, - SQLITE_LOCK_RESERVED = 2, - SQLITE_LOCK_PENDING = 3, - SQLITE_LOCK_EXCLUSIVE = 4 -} - -/** -* CAPI3REF: Synchronization Type Flags -*/ -enum -{ - SQLITE_SYNC_NORMAL = 0x00002, - SQLITE_SYNC_FULL = 0x00003, - SQLITE_SYNC_DATAONLY = 0x00010 -} - -/** -* CAPI3REF: OS Interface Open File Handle -*/ -struct sqlite3_file -{ - const(sqlite3_io_methods)*pMethods; /* Methods for an open file */ -} - -/** -* CAPI3REF: OS Interface File Virtual Methods Object -*/ - -struct sqlite3_io_methods -{ - int iVersion; - int function (sqlite3_file*) xClose; - int function (sqlite3_file*, void*, int iAmt, sqlite3_int64 iOfst) xRead; - int function (sqlite3_file*, const void*, int iAmt, sqlite3_int64 iOfst) xWrite; - int function (sqlite3_file*, sqlite3_int64 size) xTruncate; - int function (sqlite3_file*, int flags) xSync; - int function (sqlite3_file*, sqlite3_int64 *pSize) xFileSize; - int function (sqlite3_file*, int) xLock; - int function (sqlite3_file*, int) xUnlock; - int function (sqlite3_file*, int *pResOut) xCheckReservedLock; - int function (sqlite3_file*, int op, void *pArg) xFileControl; - int function (sqlite3_file*) xSectorSize; - int function (sqlite3_file*) xDeviceCharacteristics; - /* Methods above are valid for version 1 */ - int function (sqlite3_file*, int iPg, int pgsz, int, void **) xShmMap; - int function (sqlite3_file*, int offset, int n, int flags) xShmLock; - void function (sqlite3_file*) xShmBarrier; - int function (sqlite3_file*, int deleteFlag) xShmUnmap; - /* Methods above are valid for version 2 */ - /* Additional methods may be added in future releases */ - int function (sqlite3_file*, sqlite3_int64 iOfst, int iAmt, void **pp) xFetch; - int function (sqlite3_file*, sqlite3_int64 iOfst, void *p) xUnfetch; -} - -/** -* CAPI3REF: Standard File Control Opcodes -*/ -enum -{ - SQLITE_FCNTL_LOCKSTATE = 1, - SQLITE_FCNTL_GET_LOCKPROXYFILE = 2, - SQLITE_FCNTL_SET_LOCKPROXYFILE = 3, - SQLITE_FCNTL_LAST_ERRNO = 4, - SQLITE_FCNTL_SIZE_HINT = 5, - SQLITE_FCNTL_CHUNK_SIZE = 6, - SQLITE_FCNTL_FILE_POINTER = 7, - SQLITE_FCNTL_SYNC_OMITTED = 8, - SQLITE_FCNTL_WIN32_AV_RETRY = 9, - SQLITE_FCNTL_PERSIST_WAL = 10, - SQLITE_FCNTL_OVERWRITE = 11, - SQLITE_FCNTL_VFSNAME = 12, - SQLITE_FCNTL_POWERSAFE_OVERWRITE = 13, - SQLITE_FCNTL_PRAGMA = 14, - SQLITE_FCNTL_BUSYHANDLER = 15, - SQLITE_FCNTL_TEMPFILENAME = 16, - SQLITE_FCNTL_MMAP_SIZE = 18, - SQLITE_FCNTL_TRACE = 19, - SQLITE_FCNTL_HAS_MOVED = 20, - SQLITE_FCNTL_SYNC = 21, - SQLITE_FCNTL_COMMIT_PHASETWO = 22, - SQLITE_FCNTL_WIN32_SET_HANDLE = 23, - SQLITE_FCNTL_WAL_BLOCK = 24, - SQLITE_FCNTL_ZIPVFS = 25, - SQLITE_FCNTL_RBU = 26, - SQLITE_FCNTL_VFS_POINTER = 27, - SQLITE_FCNTL_JOURNAL_POINTER = 28, - SQLITE_FCNTL_WIN32_GET_HANDLE = 29, - SQLITE_FCNTL_PDB = 30, - SQLITE_FCNTL_BEGIN_ATOMIC_WRITE = 31, - SQLITE_FCNTL_COMMIT_ATOMIC_WRITE = 32, - SQLITE_FCNTL_ROLLBACK_ATOMIC_WRITE = 33, - SQLITE_FCNTL_LOCK_TIMEOUT = 34, - SQLITE_FCNTL_DATA_VERSION = 35, - SQLITE_FCNTL_SIZE_LIMIT = 36, - SQLITE_FCNTL_CKPT_DONE = 37, - SQLITE_FCNTL_RESERVE_BYTES = 38, - SQLITE_FCNTL_CKPT_START = 39 -} - -deprecated ("deprecated names") -{ - alias SQLITE_GET_LOCKPROXYFILE = SQLITE_FCNTL_GET_LOCKPROXYFILE; - alias SQLITE_SET_LOCKPROXYFILE = SQLITE_FCNTL_SET_LOCKPROXYFILE; - alias SQLITE_LAST_ERRNO = SQLITE_FCNTL_LAST_ERRNO; -} - -/** -* CAPI3REF: Mutex Handle -*/ -struct sqlite3_mutex; - -/** -* CAPI3REF: Loadable Extension Thunk -*/ -struct sqlite3_api_routines; - -/** -* CAPI3REF: OS Interface Object -*/ - -alias xDlSymReturn = void * function(); -/// Ditto -alias sqlite3_syscall_ptr = void function(); - -struct sqlite3_vfs -{ - int iVersion; /** Structure version number (currently 2) */ - int szOsFile; /** Size of subclassed sqlite3_file */ - int mxPathname; /** Maximum file pathname length */ - sqlite3_vfs *pNext; /** Next registered VFS */ - const(char)*zName; /** Name of this virtual file system */ - void *pAppData; /** Pointer to application-specific data */ - int function (sqlite3_vfs*, const char *zName, sqlite3_file*, - int flags, int *pOutFlags) xOpen; - int function (sqlite3_vfs*, const char *zName, int syncDir) xDelete; - int function (sqlite3_vfs*, const char *zName, int flags, int *pResOut) xAccess; - int function (sqlite3_vfs*, const char *zName, int nOut, char *zOut) xFullPathname; - void* function (sqlite3_vfs*, const char *zFilename) xDlOpen; - void function (sqlite3_vfs*, int nByte, char *zErrMsg) xDlError; - xDlSymReturn function (sqlite3_vfs*,void*, const char *zSymbol) *xDlSym; - void function (sqlite3_vfs*, void*) xDlClose; - int function (sqlite3_vfs*, int nByte, char *zOut) xRandomness; - int function (sqlite3_vfs*, int microseconds) xSleep; - int function (sqlite3_vfs*, double*) xCurrentTime; - int function (sqlite3_vfs*, int, char *) xGetLastError; - /* - ** The methods above are in version 1 of the sqlite_vfs object - ** definition. Those that follow are added in version 2 or later - */ - int function (sqlite3_vfs*, sqlite3_int64*) xCurrentTimeInt64; - /* - ** The methods above are in versions 1 and 2 of the sqlite_vfs object. - ** Those below are for version 3 and greater. - */ - int function(sqlite3_vfs*, const char * zName, sqlite3_syscall_ptr) xSetSystemCall; - sqlite3_syscall_ptr function(sqlite3_vfs*, const char * zName) xGetSystemCall; - const(char)* function(sqlite3_vfs*, const char * zName) xNextSystemCall; - /* - ** The methods above are in versions 1 through 3 of the sqlite_vfs object. - ** New fields may be appended in figure versions. The iVersion - ** value will increment whenever this happens. - */ -} - -/** -* CAPI3REF: Flags for the xAccess VFS method -*/ -enum -{ - SQLITE_ACCESS_EXISTS = 0, - - SQLITE_ACCESS_READWRITE = 1, /** Used by PRAGMA temp_store_directory */ - SQLITE_ACCESS_READ = 2 /** Unused */ -} - -/** -* CAPI3REF: Flags for the xShmLock VFS method -*/ -enum -{ - SQLITE_SHM_UNLOCK = 1, - SQLITE_SHM_LOCK = 2, - SQLITE_SHM_SHARED = 4, - SQLITE_SHM_EXCLUSIVE = 8 -} - -/** -* CAPI3REF: Maximum xShmLock index -*/ -enum SQLITE_SHM_NLOCK = 8; - - -/** -* CAPI3REF: Initialize The SQLite Library -*/ -int sqlite3_initialize(); -/// Ditto -int sqlite3_shutdown(); -/// Ditto -int sqlite3_os_init(); -/// Ditto -int sqlite3_os_end(); - -/** -* CAPI3REF: Configuring The SQLite Library -*/ -int sqlite3_config(int, ...); - -/** -* CAPI3REF: Configure database connections -*/ -int sqlite3_db_config(sqlite3*, int op, ...); - -/** -* CAPI3REF: Memory Allocation Routines -*/ -struct sqlite3_mem_methods -{ - void* function (int) xMalloc; /** Memory allocation function */ - void function (void*) xFree; /** Free a prior allocation */ - void* function (void*,int) xRealloc; /** Resize an allocation */ - int function (void*) xSize; /** Return the size of an allocation */ - int function (int) xRoundup; /** Round up request size to allocation size */ - int function (void*) xInit; /** Initialize the memory allocator */ - void function (void*) xShutdown; /** Deinitialize the memory allocator */ - void *pAppData; /** Argument to xInit() and xShutdown() */ -} - -/** -* CAPI3REF: Configuration Options -*/ -enum -{ - SQLITE_CONFIG_SINGLETHREAD = 1, /** nil */ - SQLITE_CONFIG_MULTITHREAD = 2, /** nil */ - SQLITE_CONFIG_SERIALIZED = 3, /** nil */ - SQLITE_CONFIG_MALLOC = 4, /** sqlite3_mem_methods* */ - SQLITE_CONFIG_GETMALLOC = 5, /** sqlite3_mem_methods* */ - SQLITE_CONFIG_SCRATCH = 6, /** No longer used */ - SQLITE_CONFIG_PAGECACHE = 7, /** void*, int sz, int N */ - SQLITE_CONFIG_HEAP = 8, /** void*, int nByte, int min */ - SQLITE_CONFIG_MEMSTATUS = 9, /** boolean */ - SQLITE_CONFIG_MUTEX = 10, /** sqlite3_mutex_methods* */ - SQLITE_CONFIG_GETMUTEX = 11, /** sqlite3_mutex_methods* */ -/* previously SQLITE_CONFIG_CHUNKALLOC 12 which is now unused. */ - SQLITE_CONFIG_LOOKASIDE = 13, /** int int */ - SQLITE_CONFIG_PCACHE = 14, /** no-op */ - SQLITE_CONFIG_GETPCACHE = 15, /** no-op */ - SQLITE_CONFIG_LOG = 16, /** xFunc, void* */ - SQLITE_CONFIG_URI = 17, /** int */ - SQLITE_CONFIG_PCACHE2 = 18, /** sqlite3_pcache_methods2* */ - SQLITE_CONFIG_GETPCACHE2 = 19, /** sqlite3_pcache_methods2* */ - SQLITE_CONFIG_COVERING_INDEX_SCAN = 20, /** int */ - SQLITE_CONFIG_SQLLOG = 21, /** xSqllog, void* */ - SQLITE_CONFIG_MMAP_SIZE = 22, /** sqlite3_int64, sqlite3_int64 */ - SQLITE_CONFIG_WIN32_HEAPSIZE = 23, /** int nByte */ - SQLITE_CONFIG_PCACHE_HDRSZ = 24, /** int *psz */ - SQLITE_CONFIG_PMASZ = 25, /** unsigned int szPma */ - SQLITE_CONFIG_STMTJRNL_SPILL = 26, /** int nByte */ - SQLITE_CONFIG_SMALL_MALLOC = 27, /** boolean */ - SQLITE_CONFIG_SORTERREF_SIZE = 28, /** int nByte */ - SQLITE_CONFIG_MEMDB_MAXSIZE = 29 /** sqlite3_int64 */ -} - -/** -* CAPI3REF: Database Connection Configuration Options -*/ -enum -{ - SQLITE_DBCONFIG_MAINDBNAME = 1000, /** const char* */ - SQLITE_DBCONFIG_LOOKASIDE = 1001, /** void* int int */ - SQLITE_DBCONFIG_ENABLE_FKEY = 1002, /** int int* */ - SQLITE_DBCONFIG_ENABLE_TRIGGER = 1003, /** int int* */ - SQLITE_DBCONFIG_ENABLE_FTS3_TOKENIZER = 1004, /** int int* */ - SQLITE_DBCONFIG_ENABLE_LOAD_EXTENSION = 1005, /** int int* */ - SQLITE_DBCONFIG_NO_CKPT_ON_CLOSE = 1006, /** int int* */ - SQLITE_DBCONFIG_ENABLE_QPSG = 1007, /** int int* */ - SQLITE_DBCONFIG_TRIGGER_EQP = 1008, /** int int* */ - SQLITE_DBCONFIG_RESET_DATABASE = 1009, /** int int* */ - SQLITE_DBCONFIG_DEFENSIVE = 1010, /** int int* */ - SQLITE_DBCONFIG_WRITABLE_SCHEMA = 1011, /** int int* */ - SQLITE_DBCONFIG_LEGACY_ALTER_TABLE = 1012, /** int int* */ - SQLITE_DBCONFIG_DQS_DML = 1013, /** int int* */ - SQLITE_DBCONFIG_DQS_DDL = 1014, /** int int* */ - SQLITE_DBCONFIG_ENABLE_VIEW = 1015, /** int int* */ - SQLITE_DBCONFIG_LEGACY_FILE_FORMAT = 1016, /** int int* */ - SQLITE_DBCONFIG_TRUSTED_SCHEMA = 1017, /** int int* */ - SQLITE_DBCONFIG_MAX = 1017 /** Largest DBCONFIG */ -} - - -/** -* CAPI3REF: Enable Or Disable Extended Result Codes -*/ -int sqlite3_extended_result_codes(sqlite3*, int onoff); - -/** -* CAPI3REF: Last Insert Rowid -*/ -sqlite3_int64 sqlite3_last_insert_rowid(sqlite3*); - -/** -* CAPI3REF: Set the Last Insert Rowid value -*/ -void sqlite3_set_last_insert_rowid(sqlite3*,sqlite3_int64); - -/** -* CAPI3REF: Count The Number Of Rows Modified -*/ -int sqlite3_changes(sqlite3*); - -/** -* CAPI3REF: Total Number Of Rows Modified -*/ -int sqlite3_total_changes(sqlite3*); - -/** -* CAPI3REF: Interrupt A Long-Running Query -*/ -void sqlite3_interrupt(sqlite3*); - -/** -* CAPI3REF: Determine If An SQL Statement Is Complete -*/ -int sqlite3_complete(const char *sql); -/// Ditto -int sqlite3_complete16(const void *sql); - -/** -* CAPI3REF: Register A Callback To Handle SQLITE_BUSY Errors -*/ -int sqlite3_busy_handler(sqlite3*, int function (void*,int), void*); - -/** -* CAPI3REF: Set A Busy Timeout -*/ -int sqlite3_busy_timeout(sqlite3*, int ms); - -/** -* CAPI3REF: Convenience Routines For Running Queries -*/ -int sqlite3_get_table( - sqlite3 *db, /** An open database */ - const(char)*zSql, /** SQL to be evaluated */ - char ***pazResult, /** Results of the query */ - int *pnRow, /** Number of result rows written here */ - int *pnColumn, /** Number of result columns written here */ - char **pzErrmsg /** Error msg written here */ -); -/// -void sqlite3_free_table(char **result); - -/** -* CAPI3REF: Formatted String Printing Functions -*/ -char *sqlite3_mprintf(const char*,...); -char *sqlite3_vmprintf(const char*, va_list); -char *sqlite3_snprintf(int,char*,const char*, ...); -char *sqlite3_vsnprintf(int,char*,const char*, va_list); - -/** -* CAPI3REF: Memory Allocation Subsystem -*/ -void *sqlite3_malloc(int); -/// Ditto -void *sqlite3_malloc64(sqlite3_uint64); -/// Ditto -void *sqlite3_realloc(void*, int); -/// Ditto -void *sqlite3_realloc64(void*, sqlite3_uint64); -/// Ditto -void sqlite3_free(void*); -/// Ditto -sqlite3_uint64 sqlite3_msize(void*); - -/** -* CAPI3REF: Memory Allocator Statistics -*/ -sqlite3_int64 sqlite3_memory_used(); -sqlite3_int64 sqlite3_memory_highwater(int resetFlag); - -/** -* CAPI3REF: Pseudo-Random Number Generator -*/ -void sqlite3_randomness(int N, void *P); - -/** -* CAPI3REF: Compile-Time Authorization Callbacks -*/ -int sqlite3_set_authorizer( - sqlite3*, - int function (void*,int,const char*,const char*,const char*,const char*) xAuth, - void *pUserData -); - -/** -* CAPI3REF: Authorizer Return Codes -*/ -enum -{ - SQLITE_DENY = 1, /** Abort the SQL statement with an error */ - SQLITE_IGNORE = 2 /** Don't allow access, but don't generate an error */ -} - -/** -* CAPI3REF: Authorizer Action Codes -*/ -/******************************************* 3rd ************ 4th ***********/ -enum -{ - SQLITE_CREATE_INDEX = 1, /** Index Name Table Name */ - SQLITE_CREATE_TABLE = 2, /** Table Name NULL */ - SQLITE_CREATE_TEMP_INDEX = 3, /** Index Name Table Name */ - SQLITE_CREATE_TEMP_TABLE = 4, /** Table Name NULL */ - SQLITE_CREATE_TEMP_TRIGGER = 5, /** Trigger Name Table Name */ - SQLITE_CREATE_TEMP_VIEW = 6, /** View Name NULL */ - SQLITE_CREATE_TRIGGER = 7, /** Trigger Name Table Name */ - SQLITE_CREATE_VIEW = 8, /** View Name NULL */ - SQLITE_DELETE = 9, /** Table Name NULL */ - SQLITE_DROP_INDEX = 10, /** Index Name Table Name */ - SQLITE_DROP_TABLE = 11, /** Table Name NULL */ - SQLITE_DROP_TEMP_INDEX = 12, /** Index Name Table Name */ - SQLITE_DROP_TEMP_TABLE = 13, /** Table Name NULL */ - SQLITE_DROP_TEMP_TRIGGER = 14, /** Trigger Name Table Name */ - SQLITE_DROP_TEMP_VIEW = 15, /** View Name NULL */ - SQLITE_DROP_TRIGGER = 16, /** Trigger Name Table Name */ - SQLITE_DROP_VIEW = 17, /** View Name NULL */ - SQLITE_INSERT = 18, /** Table Name NULL */ - SQLITE_PRAGMA = 19, /** Pragma Name 1st arg or NULL */ - SQLITE_READ = 20, /** Table Name Column Name */ - SQLITE_SELECT = 21, /** NULL NULL */ - SQLITE_TRANSACTION = 22, /** Operation NULL */ - SQLITE_UPDATE = 23, /** Table Name Column Name */ - SQLITE_ATTACH = 24, /** Filename NULL */ - SQLITE_DETACH = 25, /** Database Name NULL */ - SQLITE_ALTER_TABLE = 26, /** Database Name Table Name */ - SQLITE_REINDEX = 27, /** Index Name NULL */ - SQLITE_ANALYZE = 28, /** Table Name NULL */ - SQLITE_CREATE_VTABLE = 29, /** Table Name Module Name */ - SQLITE_DROP_VTABLE = 30, /** Table Name Module Name */ - SQLITE_FUNCTION = 31, /** NULL Function Name */ - SQLITE_SAVEPOINT = 32, /** Operation Savepoint Name */ - SQLITE_COPY = 0, /** No longer used */ - SQLITE_RECURSIVE = 33 /** NULL NULL */ -} - -/** -* CAPI3REF: Tracing And Profiling Functions -*/ -deprecated void *sqlite3_trace(sqlite3*, void function (void*,const char*) xTrace, void*); -/// Ditto -deprecated void *sqlite3_profile(sqlite3*, void function (void*,const char*,sqlite3_uint64) xProfile, void*); - -/** -* CAPI3REF: SQL Trace Event Codes -*/ -enum -{ - SQLITE_TRACE_STMT = 0x01, - SQLITE_TRACE_PROFILE = 0x02, - SQLITE_TRACE_ROW = 0x04, - SQLITE_TRACE_CLOSE = 0x08 -} - -/** -* CAPI3REF: SQL Trace Hook -*/ -int sqlite3_trace_v2( - sqlite3*, - uint uMask, - int function (uint, void*, void*, void*) xCallback, - void* pCtx -); - -/** -* CAPI3REF: Query Progress Callbacks -*/ -void sqlite3_progress_handler(sqlite3*, int, int function (void*), void*); - -/** -* CAPI3REF: Opening A New Database Connection -*/ -int sqlite3_open( - const(char)*filename, /** Database filename (UTF-8) */ - sqlite3 **ppDb /** OUT: SQLite db handle */ -); -/// Ditto -int sqlite3_open16( - const(void)*filename, /** Database filename (UTF-16) */ - sqlite3 **ppDb /** OUT: SQLite db handle */ -); -/// Ditto -int sqlite3_open_v2( - const(char)*filename, /** Database filename (UTF-8) */ - sqlite3 **ppDb, /** OUT: SQLite db handle */ - int flags, /** Flags */ - const(char)*zVfs /** Name of VFS module to use */ -); - -/* -* CAPI3REF: Obtain Values For URI Parameters -*/ -const(char)* sqlite3_uri_parameter(const(char)* zFilename, const(char)* zParam); -/// Ditto -int sqlite3_uri_boolean(const(char)* zFile, const(char)* zParam, int bDefault); -/// Ditto -sqlite3_int64 sqlite3_uri_int64(const char*, const char*, sqlite3_int64); -/// Ditto -const(char)* sqlite3_uri_key(const(char)* zFilename, int N); - -/* -* CAPI3REF: Translate filenames -*/ -const(char)* sqlite3_filename_database(const(char)*); -/// Ditto -const(char)* sqlite3_filename_journal(const(char)*); -/// Ditto -const(char)* sqlite3_filename_wal(const(char)*); - -/* -* CAPI3REF: Database File Corresponding To A Journal -*/ -sqlite3_file* sqlite3_database_file_object(const(char)*); - -/* -* CAPI3REF: Create and Destroy VFS Filenames -*/ -char* sqlite3_create_filename( - const(char)* zDatabase, - const(char)* zJournal, - const(char)* zWal, - int nParam, - const(char*)* azParam -); -/// Ditto -void sqlite3_free_filename(char*); - -/** -* CAPI3REF: Error Codes And Messages -*/ -int sqlite3_errcode(sqlite3 *db); -/// Ditto -int sqlite3_extended_errcode(sqlite3 *db); -/// Ditto -const(char)* sqlite3_errmsg(sqlite3*); -/// Ditto -const(void)* sqlite3_errmsg16(sqlite3*); -/// Ditto -const(char)* sqlite3_errstr(int); - -/** -* CAPI3REF: SQL Statement Object -*/ -struct sqlite3_stmt; - -/** -* CAPI3REF: Run-time Limits -*/ -int sqlite3_limit(sqlite3*, int id, int newVal); - -/** -* CAPI3REF: Run-Time Limit Categories -*/ -enum -{ - SQLITE_LIMIT_LENGTH = 0, - SQLITE_LIMIT_SQL_LENGTH = 1, - SQLITE_LIMIT_COLUMN = 2, - SQLITE_LIMIT_EXPR_DEPTH = 3, - SQLITE_LIMIT_COMPOUND_SELECT = 4, - SQLITE_LIMIT_VDBE_OP = 5, - SQLITE_LIMIT_FUNCTION_ARG = 6, - SQLITE_LIMIT_ATTACHED = 7, - SQLITE_LIMIT_LIKE_PATTERN_LENGTH = 8, - SQLITE_LIMIT_VARIABLE_NUMBER = 9, - SQLITE_LIMIT_TRIGGER_DEPTH = 10, - SQLITE_LIMIT_WORKER_THREADS = 11 -} - -/** -* CAPI3REF: Prepare Flags -*/ -enum -{ - SQLITE_PREPARE_PERSISTENT = 0x01, - SQLITE_PREPARE_NORMALIZE = 0x02, - SQLITE_PREPARE_NO_VTAB = 0x04 -} - -/** -* CAPI3REF: Compiling An SQL Statement -*/ -int sqlite3_prepare( - sqlite3 *db, /** Database handle */ - const(char)*zSql, /** SQL statement, UTF-8 encoded */ - int nByte, /** Maximum length of zSql in bytes. */ - sqlite3_stmt **ppStmt, /** OUT: Statement handle */ - const(char*)*pzTail /** OUT: Pointer to unused portion of zSql */ -); -/// Ditto -int sqlite3_prepare_v2( - sqlite3 *db, /** Database handle */ - const(char)*zSql, /** SQL statement, UTF-8 encoded */ - int nByte, /** Maximum length of zSql in bytes. */ - sqlite3_stmt **ppStmt, /** OUT: Statement handle */ - const(char*)*pzTail /** OUT: Pointer to unused portion of zSql */ -); -/// Ditto -int sqlite3_prepare_v3( - sqlite3 *db, /** Database handle */ - const(char)* zSql, /** SQL statement, UTF-8 encoded */ - int nByte, /** Maximum length of zSql in bytes. */ - uint prepFlags, /** Zero or more SQLITE_PREPARE_ flags */ - sqlite3_stmt **ppStmt, /* OUT: Statement handle */ - const(char*)*pzTail /* OUT: Pointer to unused portion of zSql */ -); -/// Ditto -int sqlite3_prepare16( - sqlite3 *db, /** Database handle */ - const(void)*zSql, /** SQL statement, UTF-16 encoded */ - int nByte, /** Maximum length of zSql in bytes. */ - sqlite3_stmt **ppStmt, /** OUT: Statement handle */ - const(void*)*pzTail /** OUT: Pointer to unused portion of zSql */ -); -/// Ditto -int sqlite3_prepare16_v2( - sqlite3 *db, /** Database handle */ - const(void)*zSql, /** SQL statement, UTF-16 encoded */ - int nByte, /** Maximum length of zSql in bytes. */ - sqlite3_stmt **ppStmt, /** OUT: Statement handle */ - const(void*)*pzTail /** OUT: Pointer to unused portion of zSql */ -); -/// Ditto -int sqlite3_prepare16_v3( - sqlite3 *db, /** Database handle */ - const(void)*zSql, /** SQL statement, UTF-16 encoded */ - int nByte, /** Maximum length of zSql in bytes. */ - uint prepFlags, /** Zero or more SQLITE_PREPARE_ flags */ - sqlite3_stmt **ppStmt, /** OUT: Statement handle */ - const(void*)*pzTail /** OUT: Pointer to unused portion of zSql */ -); - -/** -* CAPI3REF: Retrieving Statement SQL -*/ -const(char)* sqlite3_sql(sqlite3_stmt *pStmt); -/// Ditto -char* sqlite3_expanded_sql(sqlite3_stmt *pStmt); -const(char)* sqlite3_normalized_sql(sqlite3_stmt *pStmt); - -/* -* CAPI3REF: Determine If An SQL Statement Writes The Database -*/ -int sqlite3_stmt_readonly(sqlite3_stmt *pStmt); - -/* -* CAPI3REF: Query The EXPLAIN Setting For A Prepared Statement -*/ -int sqlite3_stmt_isexplain(sqlite3_stmt *pStmt); - -/** -* CAPI3REF: Determine If A Prepared Statement Has Been Reset -*/ -int sqlite3_stmt_busy(sqlite3_stmt*); - - -/** -* CAPI3REF: Dynamically Typed Value Object -*/ -struct sqlite3_value; - -/** -* CAPI3REF: SQL Function Context Object -*/ -struct sqlite3_context; - -/** -* CAPI3REF: Binding Values To Prepared Statements -*/ -int sqlite3_bind_blob(sqlite3_stmt*, int, const void*, int n, void function (void*)); -/// Ditto -int sqlite3_bind_blob64(sqlite3_stmt*, int, const void*, sqlite3_uint64,void function (void*)); -/// Ditto -int sqlite3_bind_double(sqlite3_stmt*, int, double); -/// Ditto -int sqlite3_bind_int(sqlite3_stmt*, int, int); -/// Ditto -int sqlite3_bind_int64(sqlite3_stmt*, int, sqlite3_int64); -/// Ditto -int sqlite3_bind_null(sqlite3_stmt*, int); -/// Ditto -int sqlite3_bind_text(sqlite3_stmt*, int, const char*, int n, void function (void*)); -/// Ditto -int sqlite3_bind_text16(sqlite3_stmt*, int, const void*, int, void function (void*)); -/// Ditto -int sqlite3_bind_text64(sqlite3_stmt*, int, const char*, sqlite3_uint64,void function (void*), ubyte encoding); -/// Ditto -int sqlite3_bind_value(sqlite3_stmt*, int, const sqlite3_value*); -/// Ditto -int sqlite3_bind_zeroblob(sqlite3_stmt*, int, int n); -/// Ditto -int sqlite3_bind_zeroblob64(sqlite3_stmt*, int, sqlite3_uint64 n); - -/** -* CAPI3REF: Number Of SQL Parameters -*/ -int sqlite3_bind_parameter_count(sqlite3_stmt*); - -/** -* CAPI3REF: Name Of A Host Parameter -*/ -const(char)* sqlite3_bind_parameter_name(sqlite3_stmt*, int); - -/** -* CAPI3REF: Index Of A Parameter With A Given Name -*/ -int sqlite3_bind_parameter_index(sqlite3_stmt*, const char *zName); - -/** -* CAPI3REF: Reset All Bindings On A Prepared Statement -*/ -int sqlite3_clear_bindings(sqlite3_stmt*); - -/** -* CAPI3REF: Number Of Columns In A Result Set -*/ -int sqlite3_column_count(sqlite3_stmt *pStmt); - -/** -* CAPI3REF: Column Names In A Result Set -*/ -const(char)* sqlite3_column_name(sqlite3_stmt*, int N); -/// Ditto -const(void)* sqlite3_column_name16(sqlite3_stmt*, int N); - -/** -* CAPI3REF: Source Of Data In A Query Result -*/ -const(char)* sqlite3_column_database_name(sqlite3_stmt*,int); -/// Ditto -const(void)* sqlite3_column_database_name16(sqlite3_stmt*,int); -/// Ditto -const(char)* sqlite3_column_table_name(sqlite3_stmt*,int); -/// Ditto -const (void)* sqlite3_column_table_name16(sqlite3_stmt*,int); -/// Ditto -const (char)* sqlite3_column_origin_name(sqlite3_stmt*,int); -/// Ditto -const (void)* sqlite3_column_origin_name16(sqlite3_stmt*,int); - -/** -* CAPI3REF: Declared Datatype Of A Query Result -*/ -const (char)* sqlite3_column_decltype(sqlite3_stmt*,int); -/// Ditto -const (void)* sqlite3_column_decltype16(sqlite3_stmt*,int); - -/** -* CAPI3REF: Evaluate An SQL Statement -*/ -int sqlite3_step(sqlite3_stmt*); - -/** -* CAPI3REF: Number of columns in a result set -*/ -int sqlite3_data_count(sqlite3_stmt *pStmt); - -/** -* CAPI3REF: Fundamental Datatypes -*/ -enum -{ - SQLITE_INTEGER = 1, - SQLITE_FLOAT = 2, - SQLITE_BLOB = 4, - SQLITE_NULL = 5, - SQLITE3_TEXT = 3 -} - -/** -* CAPI3REF: Result Values From A Query -*/ -const (void)* sqlite3_column_blob(sqlite3_stmt*, int iCol); -/// Ditto -double sqlite3_column_double(sqlite3_stmt*, int iCol); -/// Ditto -int sqlite3_column_int(sqlite3_stmt*, int iCol); -/// Ditto -sqlite3_int64 sqlite3_column_int64(sqlite3_stmt*, int iCol); -/// Ditto -const (char)* sqlite3_column_text(sqlite3_stmt*, int iCol); -/// Ditto -const (void)* sqlite3_column_text16(sqlite3_stmt*, int iCol); -/// Ditto -sqlite3_value *sqlite3_column_value(sqlite3_stmt*, int iCol); -/// Ditto -int sqlite3_column_bytes(sqlite3_stmt*, int iCol); -/// Ditto -int sqlite3_column_bytes16(sqlite3_stmt*, int iCol); -/// Ditto -int sqlite3_column_type(sqlite3_stmt*, int iCol); - -/** -* CAPI3REF: Destroy A Prepared Statement Object -*/ -int sqlite3_finalize(sqlite3_stmt *pStmt); - -/** -* CAPI3REF: Reset A Prepared Statement Object -*/ -int sqlite3_reset(sqlite3_stmt *pStmt); - -/** -* CAPI3REF: Create Or Redefine SQL Functions -*/ -int sqlite3_create_function( - sqlite3 *db, - const(char)*zFunctionName, - int nArg, - int eTextRep, - void *pApp, - void function (sqlite3_context*,int,sqlite3_value**) xFunc, - void function (sqlite3_context*,int,sqlite3_value**) xStep, - void function (sqlite3_context*) xFinal -); -/// Ditto -int sqlite3_create_function16( - sqlite3 *db, - const(void)*zFunctionName, - int nArg, - int eTextRep, - void *pApp, - void function (sqlite3_context*,int,sqlite3_value**) xFunc, - void function (sqlite3_context*,int,sqlite3_value**) xStep, - void function (sqlite3_context*) xFinal -); -/// Ditto -int sqlite3_create_function_v2( - sqlite3 *db, - const(char)*zFunctionName, - int nArg, - int eTextRep, - void *pApp, - void function (sqlite3_context*,int,sqlite3_value**) xFunc, - void function (sqlite3_context*,int,sqlite3_value**) xStep, - void function (sqlite3_context*) xFinal, - void function (void*) xDestroy -); -/// Ditto -int sqlite3_create_window_function( - sqlite3 *db, - const(char)*zFunctionName, - int nArg, - int eTextRep, - void *pApp, - void function (sqlite3_context*,int,sqlite3_value**) xStep, - void function (sqlite3_context*) xFinal, - void function (sqlite3_context*) xValue, - void function (sqlite3_context*,int,sqlite3_value**) xInverse, - void function (void*) xDestroy -); - -/** -* CAPI3REF: Text Encodings -* -* These constant define integer codes that represent the various -* text encodings supported by SQLite. -*/ -enum -{ - SQLITE_UTF8 = 1, /** IMP: R-37514-35566 */ - SQLITE_UTF16LE = 2, /** IMP: R-03371-37637 */ - SQLITE_UTF16BE = 3, /** IMP: R-51971-34154 */ - SQLITE_UTF16 = 4, /** Use native byte order */ - SQLITE_ANY = 5, /** sqlite3_create_function only */ - SQLITE_UTF16_ALIGNED = 8 /** sqlite3_create_collation only */ -} - -/** -* CAPI3REF: Function Flags -*/ -enum SQLITE_DETERMINISTIC = 0x000000800; -enum SQLITE_DIRECTONLY = 0x000080000; -enum SQLITE_SUBTYPE = 0x000100000; -enum SQLITE_INNOCUOUS = 0x000200000; - -/** -* CAPI3REF: Deprecated Functions -*/ -deprecated int sqlite3_aggregate_count(sqlite3_context*); -deprecated int sqlite3_expired(sqlite3_stmt*); -deprecated int sqlite3_transfer_bindings(sqlite3_stmt*, sqlite3_stmt*); -deprecated int sqlite3_global_recover(); -deprecated void sqlite3_thread_cleanup(); -deprecated int sqlite3_memory_alarm(void function(void*,sqlite3_int64,int),void*,sqlite3_int64); - -/** -* CAPI3REF: Obtaining SQL Function Parameter Values -*/ -const (void)* sqlite3_value_blob(sqlite3_value*); -/// Ditto -int sqlite3_value_bytes(sqlite3_value*); -/// Ditto -int sqlite3_value_bytes16(sqlite3_value*); -/// Ditto -double sqlite3_value_double(sqlite3_value*); -/// Ditto -int sqlite3_value_int(sqlite3_value*); -/// Ditto -sqlite3_int64 sqlite3_value_int64(sqlite3_value*); -/// Ditto -const (char)* sqlite3_value_text(sqlite3_value*); -/// Ditto -const (void)* sqlite3_value_text16(sqlite3_value*); -/// Ditto -const (void)* sqlite3_value_text16le(sqlite3_value*); -/// Ditto -const (void)* sqlite3_value_text16be(sqlite3_value*); -/// Ditto -int sqlite3_value_type(sqlite3_value*); -/// Ditto -int sqlite3_value_numeric_type(sqlite3_value*); -/// Ditto -int sqlite3_value_nochange(sqlite3_value*); -/// Ditto -int sqlite3_value_frombind(sqlite3_value*); - -/* -* CAPI3REF: Finding The Subtype Of SQL Values -*/ -uint sqlite3_value_subtype(sqlite3_value*); - -/* -* CAPI3REF: Copy And Free SQL Values -*/ -sqlite3_value* sqlite3_value_dup(const sqlite3_value*); -void sqlite3_value_free(sqlite3_value*); - -/** -* CAPI3REF: Obtain Aggregate Function Context -*/ -void *sqlite3_aggregate_context(sqlite3_context*, int nBytes); - -/** -* CAPI3REF: User Data For Functions -*/ -void *sqlite3_user_data(sqlite3_context*); - -/** -* CAPI3REF: Database Connection For Functions -*/ -sqlite3 *sqlite3_context_db_handle(sqlite3_context*); - -/** -* CAPI3REF: Function Auxiliary Data -*/ -void *sqlite3_get_auxdata(sqlite3_context*, int N); -/// Ditto -void sqlite3_set_auxdata(sqlite3_context*, int N, void*, void function (void*)); - - -/** -* CAPI3REF: Constants Defining Special Destructor Behavior -*/ -alias sqlite3_destructor_type = void function (void*); -/// Ditto -enum -{ - SQLITE_STATIC = (cast(sqlite3_destructor_type) 0), - SQLITE_TRANSIENT = (cast (sqlite3_destructor_type) -1) -} - -/** -* CAPI3REF: Setting The Result Of An SQL Function -*/ -void sqlite3_result_blob(sqlite3_context*, const void*, int, void function(void*)); -/// Ditto -void sqlite3_result_blob64(sqlite3_context*,const void*,sqlite3_uint64,void function(void*)); -/// Ditto -void sqlite3_result_double(sqlite3_context*, double); -/// Ditto -void sqlite3_result_error(sqlite3_context*, const char*, int); -/// Ditto -void sqlite3_result_error16(sqlite3_context*, const void*, int); -/// Ditto -void sqlite3_result_error_toobig(sqlite3_context*); -/// Ditto -void sqlite3_result_error_nomem(sqlite3_context*); -/// Ditto -void sqlite3_result_error_code(sqlite3_context*, int); -/// Ditto -void sqlite3_result_int(sqlite3_context*, int); -/// Ditto -void sqlite3_result_int64(sqlite3_context*, sqlite3_int64); -/// Ditto -void sqlite3_result_null(sqlite3_context*); -/// Ditto -void sqlite3_result_text(sqlite3_context*, const char*, int, void function(void*)); -/// Ditto -void sqlite3_result_text64(sqlite3_context*, const char*,sqlite3_uint64,void function(void*), ubyte encoding); -/// Ditto -void sqlite3_result_text16(sqlite3_context*, const void*, int, void function(void*)); -/// Ditto -void sqlite3_result_text16le(sqlite3_context*, const void*, int, void function(void*)); -/// Ditto -void sqlite3_result_text16be(sqlite3_context*, const void*, int, void function(void*)); -/// Ditto -void sqlite3_result_value(sqlite3_context*, sqlite3_value*); -/// Ditto -void sqlite3_result_zeroblob(sqlite3_context*, int n); -/// Ditto -int sqlite3_result_zeroblob64(sqlite3_context*, sqlite3_uint64 n); - -/* -* CAPI3REF: Setting The Subtype Of An SQL Function -*/ -void sqlite3_result_subtype(sqlite3_context*,uint); - -/** -* CAPI3REF: Define New Collating Sequences -*/ -int sqlite3_create_collation( - sqlite3*, - const(char)*zName, - int eTextRep, - void *pArg, - int function (void*,int,const void*,int,const void*) xCompare -); -/// Ditto -int sqlite3_create_collation_v2( - sqlite3*, - const(char)*zName, - int eTextRep, - void *pArg, - int function (void*,int,const void*,int,const void*) xCompare, - void function (void*) xDestroy -); -/// Ditto -int sqlite3_create_collation16( - sqlite3*, - const(void)*zName, - int eTextRep, - void *pArg, - int function (void*,int,const void*,int,const void*) xCompare -); - -/** -* CAPI3REF: Collation Needed Callbacks -*/ -int sqlite3_collation_needed( - sqlite3*, - void*, - void function (void*,sqlite3*,int eTextRep,const char*) -); -/// Ditto -int sqlite3_collation_needed16( - sqlite3*, - void*, - void function (void*,sqlite3*,int eTextRep,const void*) -); - -/** -* Specify the activation key for a CEROD database. Unless -* activated, none of the CEROD routines will work. -*/ -void sqlite3_activate_cerod( - const(char)*zPassPhrase /** Activation phrase */ -); - -/** -* CAPI3REF: Suspend Execution For A Short Time -*/ -int sqlite3_sleep(int); - -/** -* CAPI3REF: Name Of The Folder Holding Temporary Files -*/ -extern char *sqlite3_temp_directory; - -/** -* CAPI3REF: Name Of The Folder Holding Database Files -*/ -extern char *sqlite3_data_directory; - -/** -* CAPI3REF: Win32 Specific Interface -*/ -int sqlite3_win32_set_directory( - c_ulong type, /** Identifier for directory being set or reset */ - void* zValue /** New value for directory being set or reset */ -); -/// Ditto -int sqlite3_win32_set_directory8( - c_ulong type, /** Identifier for directory being set or reset */ - void* zValue /** New value for directory being set or reset */ -); -/// Ditto -int sqlite3_win32_set_directory16( - c_ulong type, /** Identifier for directory being set or reset */ - void* zValue /** New value for directory being set or reset */ -); - -/** -* CAPI3REF: Win32 Directory Types -*/ -enum -{ - SQLITE_WIN32_DATA_DIRECTORY_TYPE = 1, - SQLITE_WIN32_TEMP_DIRECTORY_TYPE = 2 -} - -/** -* CAPI3REF: Test For Auto-Commit Mode -*/ -int sqlite3_get_autocommit(sqlite3*); - -/** -* CAPI3REF: Find The Database Handle Of A Prepared Statement -*/ -sqlite3 *sqlite3_db_handle(sqlite3_stmt*); - -/** -* CAPI3REF: Return The Filename For A Database Connection -*/ -const(char)* sqlite3_db_filename(sqlite3 *db, const char* zDbName); - -/** -* CAPI3REF: Determine if a database is read-only -*/ -int sqlite3_db_readonly(sqlite3 *db, const char * zDbName); - -/* -* CAPI3REF: Find the next prepared statement -*/ -sqlite3_stmt *sqlite3_next_stmt(sqlite3 *pDb, sqlite3_stmt *pStmt); - -/** -* CAPI3REF: Commit And Rollback Notification Callbacks -*/ -void *sqlite3_commit_hook(sqlite3*, int function (void*), void*); -/// Ditto -void *sqlite3_rollback_hook(sqlite3*, void function (void *), void*); - -/** -* CAPI3REF: Data Change Notification Callbacks -*/ -void *sqlite3_update_hook( - sqlite3*, - void function (void *,int ,char *, char *, sqlite3_int64), - void* -); - -/** -* CAPI3REF: Enable Or Disable Shared Pager Cache -*/ -int sqlite3_enable_shared_cache(int); - -/** -* CAPI3REF: Attempt To Free Heap Memory -*/ -int sqlite3_release_memory(int); - -/** -* CAPI3REF: Free Memory Used By A Database Connection -*/ -int sqlite3_db_release_memory(sqlite3*); - -/* -* CAPI3REF: Impose A Limit On Heap Size -*/ -sqlite3_int64 sqlite3_soft_heap_limit64(sqlite3_int64 N); -sqlite3_int64 sqlite3_hard_heap_limit64(sqlite3_int64 N); - -/** -* CAPI3REF: Deprecated Soft Heap Limit Interface -*/ -deprecated void sqlite3_soft_heap_limit(int N); - -/** -* CAPI3REF: Extract Metadata About A Column Of A Table -*/ -int sqlite3_table_column_metadata( - sqlite3 *db, /** Connection handle */ - const(char)*zDbName, /** Database name or NULL */ - const(char)*zTableName, /** Table name */ - const(char)*zColumnName, /** Column name */ - char **pzDataType, /** OUTPUT: Declared data type */ - char **pzCollSeq, /** OUTPUT: Collation sequence name */ - int *pNotNull, /** OUTPUT: True if NOT NULL constraint exists */ - int *pPrimaryKey, /** OUTPUT: True if column part of PK */ - int *pAutoinc /** OUTPUT: True if column is auto-increment */ -); - -/** -* CAPI3REF: Load An Extension -*/ -int sqlite3_load_extension( - sqlite3 *db, /** Load the extension into this database connection */ - const(char)*zFile, /** Name of the shared library containing extension */ - const(char)*zProc, /** Entry point. Derived from zFile if 0 */ - char **pzErrMsg /** Put error message here if not 0 */ -); - -/** -* CAPI3REF: Enable Or Disable Extension Loading -*/ -int sqlite3_enable_load_extension(sqlite3 *db, int onoff); - -/** -* CAPI3REF: Automatically Load Statically Linked Extensions -*/ -int sqlite3_auto_extension(void function () xEntryPoint); - -/** -* CAPI3REF: Cancel Automatic Extension Loading -*/ -int sqlite3_cancel_auto_extension(void function() xEntryPoint); - -/** -* CAPI3REF: Reset Automatic Extension Loading -*/ -void sqlite3_reset_auto_extension(); - -/** -* The interface to the virtual-table mechanism is currently considered -* to be experimental. The interface might change in incompatible ways. -* If this is a problem for you, do not use the interface at this time. -* -* When the virtual-table mechanism stabilizes, we will declare the -* interface fixed, support it indefinitely, and remove this comment. -*/ - -/** -* CAPI3REF: Virtual Table Object -*/ - -alias mapFunction = void function (sqlite3_context*,int,sqlite3_value**); - -/// Ditto -struct sqlite3_module -{ - int iVersion; - int function (sqlite3*, void *pAux, - int argc, const char **argv, - sqlite3_vtab **ppVTab, char**) xCreate; - int function (sqlite3*, void *pAux, - int argc, const char **argv, - sqlite3_vtab **ppVTab, char**) xConnect; - int function (sqlite3_vtab *pVTab, sqlite3_index_info*) xBestIndex; - int function (sqlite3_vtab *pVTab) xDisconnect; - int function (sqlite3_vtab *pVTab) xDestroy; - int function (sqlite3_vtab *pVTab, sqlite3_vtab_cursor **ppCursor) xOpen; - int function (sqlite3_vtab_cursor*) xClose; - int function (sqlite3_vtab_cursor*, int idxNum, const char *idxStr, - int argc, sqlite3_value **argv) xFilter; - int function (sqlite3_vtab_cursor*) xNext; - int function (sqlite3_vtab_cursor*) xEof; - int function (sqlite3_vtab_cursor*, sqlite3_context*, int) xColumn; - int function (sqlite3_vtab_cursor*, sqlite3_int64 *pRowid) xRowid; - int function (sqlite3_vtab *, int, sqlite3_value **, sqlite3_int64 *) xUpdate; - int function (sqlite3_vtab *pVTab) xBegin; - int function (sqlite3_vtab *pVTab) xSync; - int function (sqlite3_vtab *pVTab) xCommit; - int function (sqlite3_vtab *pVTab) xRollback; - int function (sqlite3_vtab *pVtab, int nArg, const char *zName, - mapFunction*, - void **ppArg) xFindFunction; - int function (sqlite3_vtab *pVtab, const char *zNew) xRename; - int function (sqlite3_vtab *pVTab, int) xSavepoint; - int function (sqlite3_vtab *pVTab, int) xRelease; - int function (sqlite3_vtab *pVTab, int) xRollbackTo; - int function (const char*) xShadowName; -} - -/** -* CAPI3REF: Virtual Table Indexing Information -*/ -struct sqlite3_index_info -{ - struct sqlite3_index_constraint - { - int iColumn; /** constrained. -1 for ROWID */ - char op; /** Constraint operator */ - char usable; /** True if this constraint is usable */ - int iTermOffset; /** Used internally - xBestIndex should ignore */ - } - struct sqlite3_index_orderby - { - int iColumn; /** Column number */ - char desc; /** True for DESC. False for ASC. */ - } - struct sqlite3_index_constraint_usage - { - int argvIndex; /** if >0, constraint is part of argv to xFilter */ - char omit; /** Do not code a test for this constraint */ - } - /* Inputs */ - int nConstraint; /** Number of entries in aConstraint */ - sqlite3_index_constraint* aConstraint; /** Table of WHERE clause constraints */ - int nOrderBy; /** Number of terms in the ORDER BY clause */ - sqlite3_index_orderby *aOrderBy; /** The ORDER BY clause */ - /* Outputs */ - sqlite3_index_constraint_usage *aConstraintUsage; - int idxNum; /** Number used to identify the index */ - char *idxStr; /** String, possibly obtained from sqlite3_malloc */ - int needToFreeIdxStr; /** Free idxStr using sqlite3_free() if true */ - int orderByConsumed; /** True if output is already ordered */ - double estimatedCost; /** Estimated cost of using this index */ - sqlite3_int64 estimatedRows; - int idxFlags; - sqlite3_uint64 colUsed; -} - -/** -* CAPI3REF: Virtual Table Scan Flags -*/ -enum -{ - SQLITE_INDEX_SCAN_UNIQUE = 1 -} - -/** -* CAPI3REF: Virtual Table Constraint Operator Codes -*/ -enum -{ - SQLITE_INDEX_CONSTRAINT_EQ = 2, - SQLITE_INDEX_CONSTRAINT_GT = 4, - SQLITE_INDEX_CONSTRAINT_LE = 8, - SQLITE_INDEX_CONSTRAINT_LT = 16, - SQLITE_INDEX_CONSTRAINT_GE = 32, - SQLITE_INDEX_CONSTRAINT_MATCH = 64, - SQLITE_INDEX_CONSTRAINT_LIKE = 65, - SQLITE_INDEX_CONSTRAINT_GLOB = 66, - SQLITE_INDEX_CONSTRAINT_REGEXP = 67, - SQLITE_INDEX_CONSTRAINT_NE = 68, - SQLITE_INDEX_CONSTRAINT_ISNOT = 69, - SQLITE_INDEX_CONSTRAINT_ISNOTNULL = 70, - SQLITE_INDEX_CONSTRAINT_ISNULL = 71, - SQLITE_INDEX_CONSTRAINT_IS = 72, - SQLITE_INDEX_CONSTRAINT_FUNCTION = 150 -} - -/** -* CAPI3REF: Register A Virtual Table Implementation -*/ -int sqlite3_create_module( - sqlite3 *db, /* SQLite connection to register module with */ - const(char)*zName, /* Name of the module */ - const(sqlite3_module)*p, /* Methods for the module */ - void *pClientData /* Client data for xCreate/xConnect */ -); -/// Ditto -int sqlite3_create_module_v2( - sqlite3 *db, /* SQLite connection to register module with */ - const(char)*zName, /* Name of the module */ - const(sqlite3_module)*p, /* Methods for the module */ - void *pClientData, /* Client data for xCreate/xConnect */ - void function (void*) xDestroy /* Module destructor function */ -); - -/** -* CAPI3REF: Remove Unnecessary Virtual Table Implementations -*/ -int sqlite3_drop_modules( - sqlite3 *db, /* Remove modules from this connection */ - const(char*)* azKeep /* Except, do not remove the ones named here */ -); - -/** -* CAPI3REF: Virtual Table Instance Object -*/ -struct sqlite3_vtab -{ - const(sqlite3_module)*pModule; /** The module for this virtual table */ - int nRef; /** NO LONGER USED */ - char *zErrMsg; /** Error message from sqlite3_mprintf() */ - /* Virtual table implementations will typically add additional fields */ -} - -/** -* CAPI3REF: Virtual Table Cursor Object -*/ -struct sqlite3_vtab_cursor -{ - sqlite3_vtab *pVtab; /** Virtual table of this cursor */ - /* Virtual table implementations will typically add additional fields */ -} - -/** -* CAPI3REF: Declare The Schema Of A Virtual Table -*/ -int sqlite3_declare_vtab(sqlite3*, const char *zSQL); - -/** -* CAPI3REF: Overload A Function For A Virtual Table -*/ -int sqlite3_overload_function(sqlite3*, const char *zFuncName, int nArg); - -/** -* The interface to the virtual-table mechanism defined above (back up -* to a comment remarkably similar to this one) is currently considered -* to be experimental. The interface might change in incompatible ways. -* If this is a problem for you, do not use the interface at this time. -* -* When the virtual-table mechanism stabilizes, we will declare the -* interface fixed, support it indefinitely, and remove this comment. -*/ - -/* -* CAPI3REF: A Handle To An Open BLOB -*/ -struct sqlite3_blob; - -/** -* CAPI3REF: Open A BLOB For Incremental I/O -*/ -int sqlite3_blob_open( - sqlite3*, - const(char)* zDb, - const(char)* zTable, - const(char)* zColumn, - sqlite3_int64 iRow, - int flags, - sqlite3_blob **ppBlob -); - -/** -* CAPI3REF: Move a BLOB Handle to a New Row -*/ -int sqlite3_blob_reopen(sqlite3_blob *, sqlite3_int64); - -/** -* CAPI3REF: Close A BLOB Handle -*/ -int sqlite3_blob_close(sqlite3_blob *); - -/** -* CAPI3REF: Return The Size Of An Open BLOB -*/ -int sqlite3_blob_bytes(sqlite3_blob *); - -/** -* CAPI3REF: Read Data From A BLOB Incrementally -*/ -int sqlite3_blob_read(sqlite3_blob *, void *Z, int N, int iOffset); - -/** -* CAPI3REF: Write Data Into A BLOB Incrementally -*/ -int sqlite3_blob_write(sqlite3_blob *, const void *z, int n, int iOffset); - -/** -* CAPI3REF: Virtual File System Objects -*/ -sqlite3_vfs *sqlite3_vfs_find(const char *zVfsName); -/// Ditto -int sqlite3_vfs_register(sqlite3_vfs*, int makeDflt); -/// Ditto -int sqlite3_vfs_unregister(sqlite3_vfs*); - -/** -* CAPI3REF: Mutexes -*/ -sqlite3_mutex *sqlite3_mutex_alloc(int); -/// Ditto -void sqlite3_mutex_free(sqlite3_mutex*); -/// Ditto -void sqlite3_mutex_enter(sqlite3_mutex*); -/// Ditto -int sqlite3_mutex_try(sqlite3_mutex*); -/// Ditto -void sqlite3_mutex_leave(sqlite3_mutex*); - -/** -* CAPI3REF: Mutex Methods Object -*/ -struct sqlite3_mutex_methods -{ - int function () xMutexInit; - int function () xMutexEnd; - sqlite3_mutex* function (int) xMutexAlloc; - void function (sqlite3_mutex *) xMutexFree; - void function (sqlite3_mutex *) xMutexEnter; - int function (sqlite3_mutex *) xMutexTry; - void function (sqlite3_mutex *) xMutexLeave; - int function (sqlite3_mutex *) xMutexHeld; - int function (sqlite3_mutex *) xMutexNotheld; -} - -/** -* CAPI3REF: Mutex Verification Routines -*/ - -//#ifndef NDEBUG -int sqlite3_mutex_held(sqlite3_mutex*); -/// Ditto -int sqlite3_mutex_notheld(sqlite3_mutex*); -//#endif - -/** -* CAPI3REF: Mutex Types -*/ -enum -{ - SQLITE_MUTEX_FAST = 0, - SQLITE_MUTEX_RECURSIVE = 1, - SQLITE_MUTEX_STATIC_MAIN = 2, - SQLITE_MUTEX_STATIC_MEM = 3, /** sqlite3_malloc() */ - SQLITE_MUTEX_STATIC_MEM2 = 4, /** NOT USED */ - SQLITE_MUTEX_STATIC_OPEN = 4, /** sqlite3BtreeOpen() */ - SQLITE_MUTEX_STATIC_PRNG = 5, /** sqlite3_randomness() */ - SQLITE_MUTEX_STATIC_LRU = 6, /** lru page list */ - SQLITE_MUTEX_STATIC_LRU2 = 7, /** NOT USED */ - SQLITE_MUTEX_STATIC_PMEM = 7, /** sqlite3PageMalloc() */ - SQLITE_MUTEX_STATIC_APP1 = 8, /** For use by application */ - SQLITE_MUTEX_STATIC_APP2 = 9, /** For use by application */ - SQLITE_MUTEX_STATIC_APP3 = 10, /** For use by application */ - SQLITE_MUTEX_STATIC_VFS1 = 11, /** For use by built-in VFS */ - SQLITE_MUTEX_STATIC_VFS2 = 12, /** For use by extension VFS */ - SQLITE_MUTEX_STATIC_VFS3 = 13, /** For use by application VFS */ -} - -deprecated ("Legacy compatibility") -{ - alias SQLITE_MUTEX_STATIC_MASTER = SQLITE_MUTEX_STATIC_MAIN; -} - -/** -* CAPI3REF: Retrieve the mutex for a database connection -*/ -sqlite3_mutex *sqlite3_db_mutex(sqlite3*); - -/** -* CAPI3REF: Low-Level Control Of Database Files -*/ -int sqlite3_file_control(sqlite3*, const char *zDbName, int op, void*); - -/** -* CAPI3REF: Testing Interface -*/ -int sqlite3_test_control(int op, ...); - -/** -* CAPI3REF: Testing Interface Operation Codes -*/ -enum -{ - SQLITE_TESTCTRL_FIRST = 5, - SQLITE_TESTCTRL_PRNG_SAVE = 5, - SQLITE_TESTCTRL_PRNG_RESTORE = 6, - SQLITE_TESTCTRL_PRNG_RESET = 7, /** NOT USED */ - SQLITE_TESTCTRL_BITVEC_TEST = 8, - SQLITE_TESTCTRL_FAULT_INSTALL = 9, - SQLITE_TESTCTRL_BENIGN_MALLOC_HOOKS = 10, - SQLITE_TESTCTRL_PENDING_BYTE = 11, - SQLITE_TESTCTRL_ASSERT = 12, - SQLITE_TESTCTRL_ALWAYS = 13, - SQLITE_TESTCTRL_RESERVE = 14, /** NOT USED */ - SQLITE_TESTCTRL_OPTIMIZATIONS = 15, - SQLITE_TESTCTRL_ISKEYWORD = 16, /** NOT USED */ - SQLITE_TESTCTRL_SCRATCHMALLOC = 17, /** NOT USED */ - SQLITE_TESTCTRL_INTERNAL_FUNCTIONS = 17, - SQLITE_TESTCTRL_LOCALTIME_FAULT = 18, - SQLITE_TESTCTRL_EXPLAIN_STMT = 19, /** NOT USED */ - SQLITE_TESTCTRL_ONCE_RESET_THRESHOLD = 19, - SQLITE_TESTCTRL_NEVER_CORRUPT = 20, - SQLITE_TESTCTRL_VDBE_COVERAGE = 21, - SQLITE_TESTCTRL_BYTEORDER = 22, - SQLITE_TESTCTRL_ISINIT = 23, - SQLITE_TESTCTRL_SORTER_MMAP = 24, - SQLITE_TESTCTRL_IMPOSTER = 25, - SQLITE_TESTCTRL_PARSER_COVERAGE = 26, - SQLITE_TESTCTRL_RESULT_INTREAL = 27, - SQLITE_TESTCTRL_PRNG_SEED = 28, - SQLITE_TESTCTRL_EXTRA_SCHEMA_CHECKS = 29, - SQLITE_TESTCTRL_LAST = 29, /** Largest TESTCTRL */ -} - -/** -* CAPI3REF: SQL Keyword Checking -*/ -int sqlite3_keyword_count(); -/// Ditto -int sqlite3_keyword_name(int, const(char*)*, int*); -/// Ditto -int sqlite3_keyword_check(const(char)*, int); - -/** -* CAPI3REF: Dynamic String Object -*/ -struct sqlite3_str; - -/** -* CAPI3REF: Create A New Dynamic String Object -*/ -sqlite3_str* sqlite3_str_new(sqlite3*); - -/** -* CAPI3REF: Finalize A Dynamic String -*/ -char* sqlite3_str_finish(sqlite3_str*); - -/** -* CAPI3REF: Add Content To A Dynamic String -*/ -void sqlite3_str_appendf(sqlite3_str*, const(char)* zFormat, ...); -/// Ditto -void sqlite3_str_vappendf(sqlite3_str*, const(char)* zFormat, va_list); -/// Ditto -void sqlite3_str_append(sqlite3_str*, const(char)* zIn, int N); -/// Ditto -void sqlite3_str_appendall(sqlite3_str*, const(char)* zIn); -/// Ditto -void sqlite3_str_appendchar(sqlite3_str*, int N, char C); -/// Ditto -void sqlite3_str_reset(sqlite3_str*); - -/** -* CAPI3REF: Status Of A Dynamic String -*/ -int sqlite3_str_errcode(sqlite3_str*); -int sqlite3_str_length(sqlite3_str*); -char* sqlite3_str_value(sqlite3_str*); - -/** -* CAPI3REF: SQLite Runtime Status -*/ -int sqlite3_status(int op, int *pCurrent, int *pHighwater, int resetFlag); -/// Ditto -int sqlite3_status64(int op, long *pCurrent, long *pHighwater, int resetFlag); - -/** -* CAPI3REF: Status Parameters -*/ -enum -{ - SQLITE_STATUS_MEMORY_USED = 0, - SQLITE_STATUS_PAGECACHE_USED = 1, - SQLITE_STATUS_PAGECACHE_OVERFLOW = 2, - SQLITE_STATUS_SCRATCH_USED = 3, /** NOT USED */ - SQLITE_STATUS_SCRATCH_OVERFLOW = 4, /** NOT USED */ - SQLITE_STATUS_MALLOC_SIZE = 5, - SQLITE_STATUS_PARSER_STACK = 6, - SQLITE_STATUS_PAGECACHE_SIZE = 7, - SQLITE_STATUS_SCRATCH_SIZE = 8, /** NOT USED */ - SQLITE_STATUS_MALLOC_COUNT = 9 -} - -/** -* CAPI3REF: Database Connection Status -*/ -int sqlite3_db_status(sqlite3*, int op, int *pCur, int *pHiwtr, int resetFlg); - -/** -* CAPI3REF: Status Parameters for database connections -*/ -enum -{ - SQLITE_DBSTATUS_LOOKASIDE_USED = 0, - SQLITE_DBSTATUS_CACHE_USED = 1, - SQLITE_DBSTATUS_SCHEMA_USED = 2, - SQLITE_DBSTATUS_STMT_USED = 3, - SQLITE_DBSTATUS_LOOKASIDE_HIT = 4, - SQLITE_DBSTATUS_LOOKASIDE_MISS_SIZE = 5, - SQLITE_DBSTATUS_LOOKASIDE_MISS_FULL = 6, - SQLITE_DBSTATUS_CACHE_HIT = 7, - SQLITE_DBSTATUS_CACHE_MISS = 8, - SQLITE_DBSTATUS_CACHE_WRITE = 9, - SQLITE_DBSTATUS_DEFERRED_FKS = 10, - SQLITE_DBSTATUS_CACHE_USED_SHARED = 11, - SQLITE_DBSTATUS_CACHE_SPILL = 12, - SQLITE_DBSTATUS_MAX = 12 /** Largest defined DBSTATUS */ -} - -/** -* CAPI3REF: Prepared Statement Status -*/ -int sqlite3_stmt_status(sqlite3_stmt*, int op,int resetFlg); - -/** -* CAPI3REF: Status Parameters for prepared statements -*/ -enum -{ - SQLITE_STMTSTATUS_FULLSCAN_STEP = 1, - SQLITE_STMTSTATUS_SORT = 2, - SQLITE_STMTSTATUS_AUTOINDEX = 3, - SQLITE_STMTSTATUS_VM_STEP = 4, - SQLITE_STMTSTATUS_REPREPARE = 5, - SQLITE_STMTSTATUS_RUN = 6, - SQLITE_STMTSTATUS_MEMUSED = 99 -} - -/** -* CAPI3REF: Custom Page Cache Object -*/ -struct sqlite3_pcache; - -/** -* CAPI3REF: Custom Page Cache Object -*/ -struct sqlite3_pcache_page -{ - void *pBuf; /* The content of the page */ - void *pExtra; /* Extra information associated with the page */ -} - -/** -* CAPI3REF: Application Defined Page Cache. -*/ -struct sqlite3_pcache_methods2 -{ - int iVersion; - void *pArg; - int function(void*) xInit; - void function(void*) xShutdown; - sqlite3_pcache * function(int szPage, int szExtra, int bPurgeable) xCreate; - void function(sqlite3_pcache*, int nCachesize) xCachesize; - int function(sqlite3_pcache*) xPagecount; - sqlite3_pcache_page * function(sqlite3_pcache*, uint key, int createFlag) xFetch; - void function(sqlite3_pcache*, sqlite3_pcache_page*, int discard) xUnpin; - void function(sqlite3_pcache*, sqlite3_pcache_page*, - uint oldKey, uint newKey) xRekey; - void function(sqlite3_pcache*, uint iLimit) xTruncate; - void function(sqlite3_pcache*) xDestroy; - void function(sqlite3_pcache*) xShrink; -} - -struct sqlite3_pcache_methods -{ - void *pArg; - int function (void*) xInit; - void function (void*) xShutdown; - sqlite3_pcache* function (int szPage, int bPurgeable) xCreate; - void function (sqlite3_pcache*, int nCachesize) xCachesize; - int function (sqlite3_pcache*) xPagecount; - void* function (sqlite3_pcache*, uint key, int createFlag) xFetch; - void function (sqlite3_pcache*, void*, int discard) xUnpin; - void function (sqlite3_pcache*, void*, uint oldKey, uint newKey) xRekey; - void function (sqlite3_pcache*, uint iLimit) xTruncate; - void function (sqlite3_pcache*) xDestroy; -} - -/** -* CAPI3REF: Online Backup Object -*/ -struct sqlite3_backup; - -/** -* CAPI3REF: Online Backup API. -*/ -sqlite3_backup *sqlite3_backup_init( - sqlite3 *pDest, /** Destination database handle */ - const(char)*zDestName, /** Destination database name */ - sqlite3 *pSource, /** Source database handle */ - const(char)*zSourceName /** Source database name */ -); -/// Ditto -int sqlite3_backup_step(sqlite3_backup *p, int nPage); -/// Ditto -int sqlite3_backup_finish(sqlite3_backup *p); -/// Ditto -int sqlite3_backup_remaining(sqlite3_backup *p); -/// Ditto -int sqlite3_backup_pagecount(sqlite3_backup *p); - -/** -* CAPI3REF: Unlock Notification -*/ -int sqlite3_unlock_notify( - sqlite3 *pBlocked, /** Waiting connection */ - void function (void **apArg, int nArg) xNotify, /** Callback function to invoke */ - void *pNotifyArg /** Argument to pass to xNotify */ -); - -/** -* CAPI3REF: String Comparison -*/ -int sqlite3_stricmp(const char * , const char * ); -int sqlite3_strnicmp(const char * , const char * , int); - -/* -* CAPI3REF: String Globbing -* -*/ -int sqlite3_strglob(const(char)* zGlob, const(char)* zStr); - -/* -* CAPI3REF: String LIKE Matching -*/ -int sqlite3_strlike(const(char)* zGlob, const(char)* zStr, uint cEsc); - -/** -* CAPI3REF: Error Logging Interface -*/ -void sqlite3_log(int iErrCode, const char *zFormat, ...); - -/** -* CAPI3REF: Write-Ahead Log Commit Hook -*/ -void *sqlite3_wal_hook( - sqlite3*, - int function (void *,sqlite3*,const char*,int), - void* -); - -/** -* CAPI3REF: Configure an auto-checkpoint -*/ -int sqlite3_wal_autocheckpoint(sqlite3 *db, int N); - -/** -* CAPI3REF: Checkpoint a database -*/ -int sqlite3_wal_checkpoint(sqlite3 *db, const char *zDb); - -/** -* CAPI3REF: Checkpoint a database -*/ -int sqlite3_wal_checkpoint_v2( - sqlite3 *db, /** Database handle */ - const(char)*zDb, /** Name of attached database (or NULL) */ - int eMode, /** SQLITE_CHECKPOINT_* value */ - int *pnLog, /** OUT: Size of WAL log in frames */ - int *pnCkpt /** OUT: Total number of frames checkpointed */ -); - -/** -* CAPI3REF: Checkpoint operation parameters -*/ -enum -{ - SQLITE_CHECKPOINT_PASSIVE = 0, - SQLITE_CHECKPOINT_FULL = 1, - SQLITE_CHECKPOINT_RESTART = 2, - SQLITE_CHECKPOINT_TRUNCATE = 3, -} - -/* -* CAPI3REF: Virtual Table Interface Configuration -*/ -int sqlite3_vtab_config(sqlite3*, int op, ...); - -/** -* CAPI3REF: Virtual Table Configuration Options -*/ -enum SQLITE_VTAB_CONSTRAINT_SUPPORT = 1; -enum SQLITE_VTAB_INNOCUOUS = 2; -enum SQLITE_VTAB_DIRECTONLY = 3; - -/* -* 2010 August 30 -* -* The author disclaims copyright to this source code. In place of -* a legal notice, here is a blessing: -* -* May you do good and not evil. -* May you find forgiveness for yourself and forgive others. -* May you share freely, never taking more than you give. -* -************************************************************************ -*/ - -//#ifndef _SQLITE3RTREE_H_ -//#define _SQLITE3RTREE_H_ - - -/* -* CAPI3REF: Determine The Virtual Table Conflict Policy -*/ -int sqlite3_vtab_on_conflict(sqlite3 *); - -/* -* CAPI3REF: Determine If Virtual Table Column Access Is For UPDATE -*/ -int sqlite3_vtab_nochange(sqlite3_context*); - -/* -* CAPI3REF: Determine The Collation For a Virtual Table Constraint -*/ -const(char)* sqlite3_vtab_collation (sqlite3_index_info*, int); - -/* -* CAPI3REF: Conflict resolution modes -*/ -enum -{ - SQLITE_ROLLBACK = 1, - SQLITE_FAIL = 3, - SQLITE_REPLACE = 5 -} - -/* -* CAPI3REF: Prepared Statement Scan Status Opcodes -*/ -enum -{ - SQLITE_SCANSTAT_NLOOP = 0, - SQLITE_SCANSTAT_NVISIT = 1, - SQLITE_SCANSTAT_EST = 2, - SQLITE_SCANSTAT_NAME = 3, - SQLITE_SCANSTAT_EXPLAIN = 4, - SQLITE_SCANSTAT_SELECTID = 5, -} - -/* -* CAPI3REF: Prepared Statement Scan Status -*/ -int sqlite3_stmt_scanstatus(sqlite3_stmt *pStmt, int idx, int iScanStatusOp, void *pOut); - -/* -* CAPI3REF: Zero Scan-Status Counters -*/ -void sqlite3_stmt_scanstatus_reset(sqlite3_stmt *); - -/* -* CAPI3REF: Flush caches to disk mid-transaction -*/ -int sqlite3_db_cacheflush(sqlite3 *); - -/* -* CAPI3REF: The pre-update hook -*/ -void* sqlite3_preupdate_hook( - sqlite3* db, - void function( - void* pCtx, - sqlite3* db, /** Database handle */ - int op, /** SQLITE_UPDATE, DELETE or INSERT */ - const(char)* zDb, /** Database name */ - const(char)* zName, /** Table name */ - sqlite3_int64 iKey1, /** Rowid of row about to be deleted/updated */ - sqlite3_int64 iKey2 - ) xPreUpdate, - void* -); -/// Ditto -int sqlite3_preupdate_old(sqlite3*, int, sqlite3_value**); -/// Ditto -int sqlite3_preupdate_count(sqlite3*); -/// Ditto -int sqlite3_preupdate_depth(sqlite3*); -/// Ditto -int sqlite3_preupdate_new(sqlite3*, int, sqlite3_value**); - -/* -* CAPI3REF: Database Snapshot -*/ -struct sqlite3_snapshot -{ - ubyte[48] hidden; -} - -/* -* CAPI3REF: Record A Database Snapshot -*/ -int sqlite3_snapshot_get(sqlite3 *db, char *zSchema, sqlite3_snapshot **ppSnapshot); - -/* -* CAPI3REF: Start a read transaction on an historical snapshot -*/ -int sqlite3_snapshot_open(sqlite3 *db, char *zSchema, sqlite3_snapshot *pSnapshot); - -/* -* CAPI3REF: Destroy a snapshot -*/ -void sqlite3_snapshot_free(sqlite3_snapshot *); - -/* -* CAPI3REF: Compare the ages of two snapshot handles -*/ -int sqlite3_snapshot_cmp(sqlite3_snapshot* p1, sqlite3_snapshot* p2); - -/* -* CAPI3REF: Recover snapshots from a wal file -*/ -int sqlite3_snapshot_recover (sqlite3* db, const(char)* zDb); - -/* -* CAPI3REF: Serialize a database -*/ -ubyte* sqlite3_serialize( - sqlite3* db, - const(char)* zSchema, - sqlite3_int64* piSize, - uint mFlags -); - -/* -* CAPI3REF: Serialize a database -*/ -enum -{ - SQLITE_SERIALIZE_NOCOPY = 0x001 -} - -/* -* CAPI3REF: Deserialize a database -*/ -int sqlite3_deserialize ( - sqlite3* db, - const(char)* zSchema, - ubyte* pData, - sqlite3_int64 szDb, - sqlite3_int64 szBuf, - uint mFlags -); - -/* -* CAPI3REF: Flags for sqlite3_deserialize() -*/ -enum -{ - SQLITE_DESERIALIZE_FREEONCLOSE = 1, - SQLITE_DESERIALIZE_RESIZEABLE = 2, - SQLITE_DESERIALIZE_READONLY = 4 -} - -/** -* Register a geometry callback named zGeom that can be used as part of an -* R-Tree geometry query as follows: -* -* SELECT ... FROM $(LT)rtree$(GT) WHERE $(LT)rtree col$(GT) MATCH $zGeom(... params ...) -*/ -int sqlite3_rtree_geometry_callback( - sqlite3 *db, - const(char)*zGeom, - int function (sqlite3_rtree_geometry *, int nCoord, double *aCoord, int *pRes) xGeom, - void *pContext -); - -/** -* A pointer to a structure of the following type is passed as the first -* argument to callbacks registered using rtree_geometry_callback(). -*/ -struct sqlite3_rtree_geometry -{ - void *pContext; /** Copy of pContext passed to s_r_g_c() */ - int nParam; /** Size of array aParam[] */ - double *aParam; /** Parameters passed to SQL geom function */ - void *pUser; /** Callback implementation user data */ - void function (void *) xDelUser; /** Called by SQLite to clean up pUser */ -} - -int sqlite3_rtree_query_callback( - sqlite3 *db, - const(char)* zQueryFunc, - int function(sqlite3_rtree_query_info*) xQueryFunc, - void *pContext, - void function(void*) xDestructor -); - -struct sqlite3_rtree_query_info -{ - void *pContext; /* pContext from when function registered */ - int nParam; /* Number of function parameters */ - double*aParam; /* value of function parameters */ - void *pUser; /* callback can use this, if desired */ - void function(void*) xDelUser; /* function to free pUser */ - double*aCoord; /* Coordinates of node or entry to check */ - uint *anQueue; /* Number of pending entries in the queue */ - int nCoord; /* Number of coordinates */ - int iLevel; /* Level of current node or entry */ - int mxLevel; /* The largest iLevel value in the tree */ - sqlite3_int64 iRowid; /* Rowid for current entry */ - double rParentScore; /* Score of parent node */ - int eParentWithin; /* Visibility of parent node */ - int eWithin; /* OUT: Visibility */ - double rScore; /* OUT: Write the score here */ - sqlite3_value **apSqlParam; /* Original SQL values of parameters */ -} - -enum -{ - NOT_WITHIN = 0, /** Object completely outside of query region */ - PARTLY_WITHIN = 1, /** Object partially overlaps query region */ - FULLY_WITHIN = 2 /** Object fully contained within query region */ -} - -/* -* CAPI3REF: Session Object Handle -*/ -struct sqlite3_session; - -/* -* CAPI3REF: Session Object Handle -*/ -struct sqlite3_changeset_iter; - -/* -* CAPI3REF: Create A New Session Object -*/ -int sqlite3session_create ( - sqlite3* db, - const(char)* zDb, - sqlite3_session** ppSession -); - -/* -* CAPI3REF: Delete A Session Object -*/ -void sqlite3session_delete(sqlite3_session* pSession); - -/* -* CAPI3REF: Enable Or Disable A Session Object -*/ -int sqlite3session_enable(sqlite3_session* pSession, int bEnable); - -/* -* CAPI3REF: Set Or Clear the Indirect Change Flag -*/ -int sqlite3session_indirect(sqlite3_session* pSession, int bIndirect); - -/* -* CAPI3REF: Attach A Table To A Session Object -*/ -int sqlite3session_attach(sqlite3_session* pSession, const(char)* zTab); - -/* -* CAPI3REF: Set a table filter on a Session Object -*/ -void sqlite3session_table_filter( - sqlite3_session* pSession, - int function (void* pCtx, const(char)* zTab) xFilter, - void* pCtx -); - -/* -* CAPI3REF: Generate A Changeset From A Session Object -*/ -int sqlite3session_changeset( - sqlite3_session* pSession, - int* pnChangeset, - void** ppChangeset -); - -/* -* CAPI3REF: Load The Difference Between Tables Into A Session -*/ -int sqlite3session_diff( - sqlite3_session* pSession, - const(char)* zFromDb, - const(char)* zTbl, - char** pzErrMsg -); - -/* -* CAPI3REF: Generate A Patchset From A Session Object -*/ -int sqlite3session_patchset( - sqlite3_session* pSession, - int* pnPatchset, - void** ppPatchset -); - -/* -* CAPI3REF: Test if a changeset has recorded any changes -*/ -int sqlite3session_isempty (sqlite3_session* pSession); - -/* -* CAPI3REF: Create An Iterator To Traverse A Changeset -*/ -int sqlite3changeset_start( - sqlite3_changeset_iter** pp, - int nChangeset, - void* pChangeset -); -/// Ditto -int sqlite3changeset_start_v2( - sqlite3_changeset_iter** pp, - int nChangeset, - void* pChangeset, - int flags -); - -/* -* CAPI3REF: Flags for sqlite3changeset_start_v2 -*/ -enum -{ - SQLITE_CHANGESETSTART_INVERT = 0x0002 -} - -/* -* CAPI3REF: Advance A Changeset Iterator -*/ -int sqlite3changeset_next(sqlite3_changeset_iter* pIter); - -/* -* CAPI3REF: Obtain The Current Operation From A Changeset Iterator -*/ -int sqlite3changeset_op( - sqlite3_changeset_iter* pIter, - const(char*)* pzTab, - int* pnCol, - int* pOp, - int* pbIndirect -); - -/* -* CAPI3REF: Obtain The Primary Key Definition Of A Table -*/ -int sqlite3changeset_pk( - sqlite3_changeset_iter* pIter, - ubyte** pabPK, - int* pnCol -); - -/* -* CAPI3REF: Obtain old.* Values From A Changeset Iterator -*/ -int sqlite3changeset_old( - sqlite3_changeset_iter* pIter, - int iVal, - sqlite3_value** ppValue -); - -/* -* CAPI3REF: Obtain new.* Values From A Changeset Iterator -*/ -int sqlite3changeset_new( - sqlite3_changeset_iter* pIter, - int iVal, - sqlite3_value** ppValue -); - -/* -* CAPI3REF: Obtain Conflicting Row Values From A Changeset Iterator -*/ -int sqlite3changeset_conflict( - sqlite3_changeset_iter* pIter, - int iVal, - sqlite3_value** ppValue -); - -/* -* CAPI3REF: Determine The Number Of Foreign Key Constraint Violations -*/ -int sqlite3changeset_fk_conflicts(sqlite3_changeset_iter* pIter, int* pnOut); - -/* -* CAPI3REF: Finalize A Changeset Iterator -*/ -int sqlite3changeset_finalize (sqlite3_changeset_iter* pIter); - -/* -* CAPI3REF: Invert A Changeset -*/ -int sqlite3changeset_invert( - int nIn, - const(void)* pIn, - int* pnOut, - void** ppOut -); - -/* -* CAPI3REF: Concatenate Two Changeset Objects -*/ -int sqlite3changeset_concat( - int nA, - void* pA, - int nB, - void* pB, - int* pnOut, - void** ppOut -); - -/* -* CAPI3REF: Changegroup Handle -*/ -struct sqlite3_changegroup; - -/* -* CAPI3REF: Create A New Changegroup Object -*/ -int sqlite3changegroup_new(sqlite3_changegroup** pp); - -/* -* CAPI3REF: Add A Changeset To A Changegroup -*/ -int sqlite3changegroup_add(sqlite3_changegroup*, int nData, void* pData); - -/* -* CAPI3REF: Obtain A Composite Changeset From A Changegroup -*/ -int sqlite3changegroup_output( - sqlite3_changegroup*, - int* pnData, - void** ppData -); - -/* -* CAPI3REF: Delete A Changegroup Object -*/ -void sqlite3changegroup_delete (sqlite3_changegroup*); - -/* -* CAPI3REF: Apply A Changeset To A Database -*/ -int sqlite3changeset_apply( - sqlite3* db, - int nChangeset, - void* pChangeset, - int function(void* pCtx, const(char)* zTab) xFilter, - int function(void* pCtx, int eConflict, sqlite3_changeset_iter* p) xConflict, - void* pCtx -); -/// Ditto -int sqlite3changeset_apply_v2( - sqlite3* db, - int nChangeset, - void* pChangeset, - int function(void* pCtx, const(char)* zTab) xFilter, - int function(void* pCtx, int eConflict, sqlite3_changeset_iter* p) xConflict, - void* pCtx, - void** ppRebase, - int* pnRebase, - int flags -); - -/* -* CAPI3REF: Flags for sqlite3changeset_apply_v2 -*/ -enum -{ - SQLITE_CHANGESETAPPLY_NOSAVEPOINT = 0x0001, - SQLITE_CHANGESETAPPLY_INVERT = 0x0002 -} - -/* -* CAPI3REF: Constants Passed To The Conflict Handler -*/ -enum -{ - SQLITE_CHANGESET_DATA = 1, - SQLITE_CHANGESET_NOTFOUND = 2, - SQLITE_CHANGESET_CONFLICT = 3, - SQLITE_CHANGESET_CONSTRAINT = 4, - SQLITE_CHANGESET_FOREIGN_KEY = 5 -} - -/* -* CAPI3REF: Constants Returned By The Conflict Handler -*/ -enum -{ - SQLITE_CHANGESET_OMIT = 0, - SQLITE_CHANGESET_REPLACE = 1, - SQLITE_CHANGESET_ABORT = 2 -} - -/* -* CAPI3REF: Rebasing changesets -*/ -struct sqlite3_rebaser; - -/* -* CAPI3REF: Create a changeset rebaser object -*/ -int sqlite3rebaser_create(sqlite3_rebaser** ppNew); - -/* -* CAPI3REF: Configure a changeset rebaser object -*/ -int sqlite3rebaser_configure( - sqlite3_rebaser*, - int nRebase, - const(void)* pRebase -); - -/* -* CAPI3REF: Rebase a changeset -*/ -int sqlite3rebaser_rebase( - sqlite3_rebaser*, - int nIn, - const(void)* pIn, - int* pnOut, - void** ppOut -); - -/* -* CAPI3REF: Delete a changeset rebaser object -*/ -void sqlite3rebaser_delete(sqlite3_rebaser* p); - -/* -* CAPI3REF: Streaming Versions of API functions -*/ -int sqlite3changeset_apply_strm( - sqlite3* db, - int function (void* pIn, void* pData, int* pnData) xInput, - void* pIn, - int function (void* pCtx, const(char)* zTab) xFilter, - int function (void* pCtx, int eConflict, sqlite3_changeset_iter* p) xConflict, - void* pCtx -); -/// Ditto -int sqlite3changeset_apply_v2_strm( - sqlite3* db, - int function (void* pIn, void* pData, int* pnData) xInput, - void* pIn, - int function (void* pCtx, const(char)* zTab) xFilter, - int function (void* pCtx, int eConflict, sqlite3_changeset_iter* p) xConflict, - void* pCtx, - void** ppRebase, - int* pnRebase, - int flags -); -/// Ditto -int sqlite3changeset_concat_strm( - int function (void* pIn, void* pData, int* pnData) xInputA, - void* pInA, - int function (void* pIn, void* pData, int* pnData) xInputB, - void* pInB, - int function (void* pOut, const(void)* pData, int nData) xOutput, - void* pOut -); -/// Ditto -int sqlite3changeset_invert_strm( - int function (void* pIn, void* pData, int* pnData) xInput, - void* pIn, - int function (void* pOut, const(void)* pData, int nData) xOutput, - void* pOut -); -/// Ditto -int sqlite3changeset_start_strm( - sqlite3_changeset_iter** pp, - int function (void* pIn, void* pData, int* pnData) xInput, - void* pIn -); -/// Ditto -int sqlite3changeset_start_v2_strm( - sqlite3_changeset_iter** pp, - int function (void* pIn, void* pData, int* pnData) xInput, - void* pIn, - int flags -); -/// Ditto -int sqlite3session_changeset_strm( - sqlite3_session* pSession, - int function (void* pOut, const(void)* pData, int nData) xOutput, - void* pOut -); -/// Ditto -int sqlite3session_patchset_strm( - sqlite3_session* pSession, - int function (void* pOut, const(void)* pData, int nData) xOutput, - void* pOut -); -/// Ditto -int sqlite3changegroup_add_strm( - sqlite3_changegroup*, - int function (void* pIn, void* pData, int* pnData) xInput, - void* pIn -); -/// Ditto -int sqlite3changegroup_output_strm( - sqlite3_changegroup*, - int function (void* pOut, const(void)* pData, int nData) xOutput, - void* pOut -); -/// Ditto -int sqlite3rebaser_rebase_strm( - sqlite3_rebaser* pRebaser, - int function (void* pIn, void* pData, int* pnData) xInput, - void* pIn, - int function (void* pOut, const(void)* pData, int nData) xOutput, - void* pOut -); - -/* -* CAPI3REF: Configure global parameters -*/ -int sqlite3session_config(int op, void* pArg); - -/* -* CAPI3REF: Values for sqlite3session_config() -*/ -enum -{ - SQLITE_SESSION_CONFIG_STRMSIZE = 1 -} - -/****************************************************************************** -* Interfaces to extend FTS5. -*/ -struct Fts5Context; -/// Ditto -alias fts5_extension_function = void function( - const Fts5ExtensionApi *pApi, - Fts5Context *pFts, - sqlite3_context *pCtx, - int nVal, - sqlite3_value **apVal -); -/// Ditto -struct Fts5PhraseIter -{ - const(ubyte) *a; - const(ubyte) *b; -} -/// Ditto -struct Fts5ExtensionApi -{ - int iVersion; /** Currently always set to 3 */ - void* function(Fts5Context*) xUserData; - int function(Fts5Context*) xColumnCount; - int function(Fts5Context*, sqlite3_int64 *pnRow) xRowCount; - int function(Fts5Context*, int iCol, sqlite3_int64 *pnToken) xColumnTotalSize; - int function(Fts5Context*, - const char *pText, int nText, - void *pCtx, - int function(void*, int, const char*, int, int, int) xToken - ) xTokenize; - int function(Fts5Context*) xPhraseCount; - int function(Fts5Context*, int iPhrase) xPhraseSize; - int function(Fts5Context*, int *pnInst) xInstCount; - int function(Fts5Context*, int iIdx, int *piPhrase, int *piCol, int *piOff) xInst; - sqlite3_int64 function(Fts5Context*) xRowid; - int function(Fts5Context*, int iCol, const char **pz, int *pn) xColumnText; - int function(Fts5Context*, int iCol, int *pnToken) xColumnSize; - int function(Fts5Context*, int iPhrase, void *pUserData, - int function(const Fts5ExtensionApi*,Fts5Context*,void*) - ) xQueryPhrase; - int function(Fts5Context*, void *pAux, void function(void*) xDelete) xSetAuxdata; - void* function(Fts5Context*, int bClear) xGetAuxdata; - int function(Fts5Context*, int iPhrase, Fts5PhraseIter*, int*, int*) xPhraseFirst; - void function(Fts5Context*, Fts5PhraseIter*, int *piCol, int *piOff) xPhraseNext; - int function(Fts5Context*, int iPhrase, Fts5PhraseIter*, int*) xPhraseFirstColumn; - void function(Fts5Context*, Fts5PhraseIter*, int* piCol) xPhraseNextColumn; -} -/// Ditto -struct Fts5Tokenizer; -struct fts5_tokenizer -{ - int function(void*, const char **azArg, int nArg, Fts5Tokenizer **ppOut) xCreate; - void function(Fts5Tokenizer*) xDelete; - int function(Fts5Tokenizer*, - void *pCtx, - int flags, - const char *pText, int nText, - int function( - void *pCtx, - int tflags, - const char *pToken, - int nToken, - int iStart, - int iEnd - ) xToken - ) xTokenize; -} -/// Ditto -enum FTS5_TOKENIZE_QUERY = 0x0001; -/// Ditto -enum FTS5_TOKENIZE_PREFIX = 0x0002; -/// Ditto -enum FTS5_TOKENIZE_DOCUMENT = 0x0004; -/// Ditto -enum FTS5_TOKENIZE_AUX = 0x0008; -/// Ditto -enum FTS5_TOKEN_COLOCATED = 0x0001; -/// Ditto -struct fts5_api -{ - int iVersion; - - int function( - fts5_api *pApi, - const char *zName, - void *pContext, - fts5_tokenizer *pTokenizer, - void function(void*) xDestroy - ) xCreateTokenizer; - - int function( - fts5_api *pApi, - const char *zName, - void **ppContext, - fts5_tokenizer *pTokenizer - ) xFindTokenizer; - - int function( - fts5_api *pApi, - const char *zName, - void *pContext, - fts5_extension_function xFunction, - void function(void*) xDestroy - ) xCreateFunction; -} diff --git a/phobos/etc/c/zlib.d b/phobos/etc/c/zlib.d deleted file mode 100644 index 0ff5f97..0000000 --- a/phobos/etc/c/zlib.d +++ /dev/null @@ -1,1809 +0,0 @@ -/* zlib.d: modified from zlib.h by Walter Bright */ -/* updated from 1.2.1 to 1.2.3 by Thomas Kuehne */ -/* updated from 1.2.3 to 1.2.8 by Dmitry Atamanov */ -/* updated from 1.2.8 to 1.2.11 by Iain Buclaw */ -/* updated from 1.2.11 to 1.2.12 by Brian Callahan */ - -module etc.c.zlib; - -import core.stdc.config; - -/* zlib.h -- interface of the 'zlib' general purpose compression library - version 1.3.1, January 22nd, 2024 - - Copyright (C) 1995-2024 Jean-loup Gailly and Mark Adler - - This software is provided 'as-is', without any express or implied - warranty. In no event will the authors be held liable for any damages - arising from the use of this software. - - Permission is granted to anyone to use this software for any purpose, - including commercial applications, and to alter it and redistribute it - freely, subject to the following restrictions: - - 1. The origin of this software must not be misrepresented; you must not - claim that you wrote the original software. If you use this software - in a product, an acknowledgment in the product documentation would be - appreciated but is not required. - 2. Altered source versions must be plainly marked as such, and must not be - misrepresented as being the original software. - 3. This notice may not be removed or altered from any source distribution. - - Jean-loup Gailly Mark Adler - jloup@gzip.org madler@alumni.caltech.edu - - - The data format used by the zlib library is described by RFCs (Request for - Comments) 1950 to 1952 in the files http://tools.ietf.org/html/rfc1950 - (zlib format), rfc1951 (deflate format) and rfc1952 (gzip format). -*/ - -nothrow: -@nogc: -extern (C): - -// Those are extern(D) as they should be mangled -extern(D) immutable string ZLIB_VERSION = "1.3.1"; -extern(D) immutable ZLIB_VERNUM = 0x1310; - -/* - The 'zlib' compression library provides in-memory compression and - decompression functions, including integrity checks of the uncompressed data. - This version of the library supports only one compression method (deflation) - but other algorithms will be added later and will have the same stream - interface. - - Compression can be done in a single step if the buffers are large enough, - or can be done by repeated calls of the compression function. In the latter - case, the application must provide more input and/or consume the output - (providing more output space) before each call. - - The compressed data format used by default by the in-memory functions is - the zlib format, which is a zlib wrapper documented in RFC 1950, wrapped - around a deflate stream, which is itself documented in RFC 1951. - - The library also supports reading and writing files in gzip (.gz) format - with an interface similar to that of stdio using the functions that start - with "gz". The gzip format is different from the zlib format. gzip is a - gzip wrapper, documented in RFC 1952, wrapped around a deflate stream. - - This library can optionally read and write gzip and raw deflate streams in - memory as well. - - The zlib format was designed to be compact and fast for use in memory - and on communications channels. The gzip format was designed for single- - file compression on file systems, has a larger header than zlib to maintain - directory information, and uses a different, slower check method than zlib. - - The library does not install any signal handler. The decoder checks - the consistency of the compressed data, so the library should never crash - even in the case of corrupted input. -*/ - -alias alloc_func = void* function (void* opaque, uint items, uint size); -alias free_func = void function (void* opaque, void* address); - -struct z_stream -{ - const(ubyte)* next_in; /* next input byte */ - uint avail_in; /* number of bytes available at next_in */ - c_ulong total_in; /* total nb of input bytes read so far */ - - ubyte* next_out; /* next output byte will go here */ - uint avail_out; /* remaining free space at next_out */ - c_ulong total_out; /* total nb of bytes output so far */ - - const(char)* msg; /* last error message, NULL if no error */ - void* state; /* not visible by applications */ - - alloc_func zalloc; /* used to allocate the internal state */ - free_func zfree; /* used to free the internal state */ - void* opaque; /* private data object passed to zalloc and zfree */ - - int data_type; /* best guess about the data type: binary or text - for deflate, or the decoding state for inflate */ - c_ulong adler; /* Adler-32 or CRC-32 value of the uncompressed data */ - c_ulong reserved; /* reserved for future use */ -} - -alias z_streamp = z_stream*; - -/* - gzip header information passed to and from zlib routines. See RFC 1952 - for more details on the meanings of these fields. -*/ -struct gz_header -{ - int text; /* true if compressed data believed to be text */ - c_ulong time; /* modification time */ - int xflags; /* extra flags (not used when writing a gzip file) */ - int os; /* operating system */ - byte *extra; /* pointer to extra field or Z_NULL if none */ - uint extra_len; /* extra field length (valid if extra != Z_NULL) */ - uint extra_max; /* space at extra (only when reading header) */ - byte* name; /* pointer to zero-terminated file name or Z_NULL */ - uint name_max; /* space at name (only when reading header) */ - byte* comment; /* pointer to zero-terminated comment or Z_NULL */ - uint comm_max; /* space at comment (only when reading header) */ - int hcrc; /* true if there was or will be a header crc */ - int done; /* true when done reading gzip header (not used - when writing a gzip file) */ -} - -alias gz_headerp = gz_header*; - -/* - The application must update next_in and avail_in when avail_in has dropped - to zero. It must update next_out and avail_out when avail_out has dropped - to zero. The application must initialize zalloc, zfree and opaque before - calling the init function. All other fields are set by the compression - library and must not be updated by the application. - - The opaque value provided by the application will be passed as the first - parameter for calls of zalloc and zfree. This can be useful for custom - memory management. The compression library attaches no meaning to the - opaque value. - - zalloc must return Z_NULL if there is not enough memory for the object. - If zlib is used in a multi-threaded application, zalloc and zfree must be - thread safe. In that case, zlib is thread-safe. When zalloc and zfree are - Z_NULL on entry to the initialization function, they are set to internal - routines that use the standard library functions malloc() and free(). - - On 16-bit systems, the functions zalloc and zfree must be able to allocate - exactly 65536 bytes, but will not be required to allocate more than this if - the symbol MAXSEG_64K is defined (see zconf.h). WARNING: On MSDOS, pointers - returned by zalloc for objects of exactly 65536 bytes *must* have their - offset normalized to zero. The default allocation function provided by this - library ensures this (see zutil.c). To reduce memory requirements and avoid - any allocation of 64K objects, at the expense of compression ratio, compile - the library with -DMAX_WBITS=14 (see zconf.h). - - The fields total_in and total_out can be used for statistics or progress - reports. After compression, total_in holds the total size of the - uncompressed data and may be saved for use by the decompressor (particularly - if the decompressor wants to decompress everything in a single step). -*/ - - /* constants */ - -enum -{ - Z_NO_FLUSH = 0, - Z_PARTIAL_FLUSH = 1, /* will be removed, use Z_SYNC_FLUSH instead */ - Z_SYNC_FLUSH = 2, - Z_FULL_FLUSH = 3, - Z_FINISH = 4, - Z_BLOCK = 5, - Z_TREES = 6, -} -/* Allowed flush values; see deflate() and inflate() below for details */ - -enum -{ - Z_OK = 0, - Z_STREAM_END = 1, - Z_NEED_DICT = 2, - Z_ERRNO = -1, - Z_STREAM_ERROR = -2, - Z_DATA_ERROR = -3, - Z_MEM_ERROR = -4, - Z_BUF_ERROR = -5, - Z_VERSION_ERROR = -6, -} -/* Return codes for the compression/decompression functions. Negative - * values are errors, positive values are used for special but normal events. - */ - -enum -{ - Z_NO_COMPRESSION = 0, - Z_BEST_SPEED = 1, - Z_BEST_COMPRESSION = 9, - Z_DEFAULT_COMPRESSION = -1, -} -/* compression levels */ - -enum -{ - Z_FILTERED = 1, - Z_HUFFMAN_ONLY = 2, - Z_RLE = 3, - Z_FIXED = 4, - Z_DEFAULT_STRATEGY = 0, -} -/* compression strategy; see deflateInit2() below for details */ - -enum -{ - Z_BINARY = 0, - Z_TEXT = 1, - Z_UNKNOWN = 2, - - Z_ASCII = Z_TEXT -} -/* Possible values of the data_type field for deflate() */ - -enum -{ - Z_DEFLATED = 8, -} -/* The deflate compression method (the only one supported in this version) */ - -/// for initializing zalloc, zfree, opaque (extern(D) for mangling) -extern(D) immutable void* Z_NULL = null; - - /* basic functions */ - -const(char)* zlibVersion(); -/* The application can compare zlibVersion and ZLIB_VERSION for consistency. - If the first character differs, the library code actually used is not - compatible with the zlib.h header file used by the application. This check - is automatically made by deflateInit and inflateInit. - */ - -int deflateInit(z_streamp strm, int level) -{ - return deflateInit_(strm, level, ZLIB_VERSION.ptr, z_stream.sizeof); -} -/* - Initializes the internal stream state for compression. The fields - zalloc, zfree and opaque must be initialized before by the caller. If - zalloc and zfree are set to Z_NULL, deflateInit updates them to use default - allocation functions. total_in, total_out, adler, and msg are initialized. - - The compression level must be Z_DEFAULT_COMPRESSION, or between 0 and 9: - 1 gives best speed, 9 gives best compression, 0 gives no compression at all - (the input data is simply copied a block at a time). Z_DEFAULT_COMPRESSION - requests a default compromise between speed and compression (currently - equivalent to level 6). - - deflateInit returns Z_OK if success, Z_MEM_ERROR if there was not enough - memory, Z_STREAM_ERROR if level is not a valid compression level, or - Z_VERSION_ERROR if the zlib library version (zlib_version) is incompatible - with the version assumed by the caller (ZLIB_VERSION). msg is set to null - if there is no error message. deflateInit does not perform any compression: - this will be done by deflate(). -*/ - - -int deflate(z_streamp strm, int flush); -/* - deflate compresses as much data as possible, and stops when the input - buffer becomes empty or the output buffer becomes full. It may introduce - some output latency (reading input without producing any output) except when - forced to flush. - - The detailed semantics are as follows. deflate performs one or both of the - following actions: - - - Compress more input starting at next_in and update next_in and avail_in - accordingly. If not all input can be processed (because there is not - enough room in the output buffer), next_in and avail_in are updated and - processing will resume at this point for the next call of deflate(). - - - Generate more output starting at next_out and update next_out and avail_out - accordingly. This action is forced if the parameter flush is non zero. - Forcing flush frequently degrades the compression ratio, so this parameter - should be set only when necessary. Some output may be provided even if - flush is zero. - - Before the call of deflate(), the application should ensure that at least - one of the actions is possible, by providing more input and/or consuming more - output, and updating avail_in or avail_out accordingly; avail_out should - never be zero before the call. The application can consume the compressed - output when it wants, for example when the output buffer is full (avail_out - == 0), or after each call of deflate(). If deflate returns Z_OK and with - zero avail_out, it must be called again after making room in the output - buffer because there might be more output pending. See deflatePending(), - which can be used if desired to determine whether or not there is more output - in that case. - - Normally the parameter flush is set to Z_NO_FLUSH, which allows deflate to - decide how much data to accumulate before producing output, in order to - maximize compression. - - If the parameter flush is set to Z_SYNC_FLUSH, all pending output is - flushed to the output buffer and the output is aligned on a byte boundary, so - that the decompressor can get all input data available so far. (In - particular avail_in is zero after the call if enough output space has been - provided before the call.) Flushing may degrade compression for some - compression algorithms and so it should be used only when necessary. This - completes the current deflate block and follows it with an empty stored block - that is three bits plus filler bits to the next byte, followed by four bytes - (00 00 ff ff). - - If flush is set to Z_PARTIAL_FLUSH, all pending output is flushed to the - output buffer, but the output is not aligned to a byte boundary. All of the - input data so far will be available to the decompressor, as for Z_SYNC_FLUSH. - This completes the current deflate block and follows it with an empty fixed - codes block that is 10 bits long. This assures that enough bytes are output - in order for the decompressor to finish the block before the empty fixed - codes block. - - If flush is set to Z_BLOCK, a deflate block is completed and emitted, as - for Z_SYNC_FLUSH, but the output is not aligned on a byte boundary, and up to - seven bits of the current block are held to be written as the next byte after - the next deflate block is completed. In this case, the decompressor may not - be provided enough bits at this point in order to complete decompression of - the data provided so far to the compressor. It may need to wait for the next - block to be emitted. This is for advanced applications that need to control - the emission of deflate blocks. - - If flush is set to Z_FULL_FLUSH, all output is flushed as with - Z_SYNC_FLUSH, and the compression state is reset so that decompression can - restart from this point if previous compressed data has been damaged or if - random access is desired. Using Z_FULL_FLUSH too often can seriously degrade - compression. - - If deflate returns with avail_out == 0, this function must be called again - with the same value of the flush parameter and more output space (updated - avail_out), until the flush is complete (deflate returns with non-zero - avail_out). In the case of a Z_FULL_FLUSH or Z_SYNC_FLUSH, make sure that - avail_out is greater than six when the flush marker begins, in order to avoid - repeated flush markers upon calling deflate() again when avail_out == 0. - - If the parameter flush is set to Z_FINISH, pending input is processed, - pending output is flushed and deflate returns with Z_STREAM_END if there was - enough output space. If deflate returns with Z_OK or Z_BUF_ERROR, this - function must be called again with Z_FINISH and more output space (updated - avail_out) but no more input data, until it returns with Z_STREAM_END or an - error. After deflate has returned Z_STREAM_END, the only possible operations - on the stream are deflateReset or deflateEnd. - - Z_FINISH can be used in the first deflate call after deflateInit if all the - compression is to be done in a single step. In order to complete in one - call, avail_out must be at least the value returned by deflateBound (see - below). Then deflate is guaranteed to return Z_STREAM_END. If not enough - output space is provided, deflate will not return Z_STREAM_END, and it must - be called again as described above. - - deflate() sets strm->adler to the Adler-32 checksum of all input read - so far (that is, total_in bytes). If a gzip stream is being generated, then - strm->adler will be the CRC-32 checksum of the input read so far. (See - deflateInit2 below.) - - deflate() may update strm->data_type if it can make a good guess about - the input data type (Z_BINARY or Z_TEXT). If in doubt, the data is - considered binary. This field is only for information purposes and does not - affect the compression algorithm in any manner. - - deflate() returns Z_OK if some progress has been made (more input - processed or more output produced), Z_STREAM_END if all input has been - consumed and all output has been produced (only when flush is set to - Z_FINISH), Z_STREAM_ERROR if the stream state was inconsistent (for example - if next_in or next_out was Z_NULL or the state was inadvertently written over - by the application), or Z_BUF_ERROR if no progress is possible (for example - avail_in or avail_out was zero). Note that Z_BUF_ERROR is not fatal, and - deflate() can be called again with more input and more output space to - continue compressing. -*/ - - -int deflateEnd(z_streamp strm); -/* - All dynamically allocated data structures for this stream are freed. - This function discards any unprocessed input and does not flush any pending - output. - - deflateEnd returns Z_OK if success, Z_STREAM_ERROR if the - stream state was inconsistent, Z_DATA_ERROR if the stream was freed - prematurely (some input or output was discarded). In the error case, msg - may be set but then points to a static string (which must not be - deallocated). -*/ - - -int inflateInit(z_streamp strm) -{ - return inflateInit_(strm, ZLIB_VERSION.ptr, z_stream.sizeof); -} -/* - Initializes the internal stream state for decompression. The fields - next_in, avail_in, zalloc, zfree and opaque must be initialized before by - the caller. In the current version of inflate, the provided input is not - read or consumed. The allocation of a sliding window will be deferred to - the first call of inflate (if the decompression does not complete on the - first call). If zalloc and zfree are set to Z_NULL, inflateInit updates - them to use default allocation functions. total_in, total_out, adler, and - msg are initialized. - - inflateInit returns Z_OK if success, Z_MEM_ERROR if there was not enough - memory, Z_VERSION_ERROR if the zlib library version is incompatible with the - version assumed by the caller, or Z_STREAM_ERROR if the parameters are - invalid, such as a null pointer to the structure. msg is set to null if - there is no error message. inflateInit does not perform any decompression. - Actual decompression will be done by inflate(). So next_in, and avail_in, - next_out, and avail_out are unused and unchanged. The current - implementation of inflateInit() does not process any header information -- - that is deferred until inflate() is called. -*/ - - -int inflate(z_streamp strm, int flush); -/* - inflate decompresses as much data as possible, and stops when the input - buffer becomes empty or the output buffer becomes full. It may introduce - some output latency (reading input without producing any output) except when - forced to flush. - - The detailed semantics are as follows. inflate performs one or both of the - following actions: - - - Decompress more input starting at next_in and update next_in and avail_in - accordingly. If not all input can be processed (because there is not - enough room in the output buffer), then next_in and avail_in are updated - accordingly, and processing will resume at this point for the next call of - inflate(). - - - Generate more output starting at next_out and update next_out and avail_out - accordingly. inflate() provides as much output as possible, until there is - no more input data or no more space in the output buffer (see below about - the flush parameter). - - Before the call of inflate(), the application should ensure that at least - one of the actions is possible, by providing more input and/or consuming more - output, and updating the next_* and avail_* values accordingly. If the - caller of inflate() does not provide both available input and available - output space, it is possible that there will be no progress made. The - application can consume the uncompressed output when it wants, for example - when the output buffer is full (avail_out == 0), or after each call of - inflate(). If inflate returns Z_OK and with zero avail_out, it must be - called again after making room in the output buffer because there might be - more output pending. - - The flush parameter of inflate() can be Z_NO_FLUSH, Z_SYNC_FLUSH, Z_FINISH, - Z_BLOCK, or Z_TREES. Z_SYNC_FLUSH requests that inflate() flush as much - output as possible to the output buffer. Z_BLOCK requests that inflate() - stop if and when it gets to the next deflate block boundary. When decoding - the zlib or gzip format, this will cause inflate() to return immediately - after the header and before the first block. When doing a raw inflate, - inflate() will go ahead and process the first block, and will return when it - gets to the end of that block, or when it runs out of data. - - The Z_BLOCK option assists in appending to or combining deflate streams. - To assist in this, on return inflate() always sets strm->data_type to the - number of unused bits in the last byte taken from strm->next_in, plus 64 if - inflate() is currently decoding the last block in the deflate stream, plus - 128 if inflate() returned immediately after decoding an end-of-block code or - decoding the complete header up to just before the first byte of the deflate - stream. The end-of-block will not be indicated until all of the uncompressed - data from that block has been written to strm->next_out. The number of - unused bits may in general be greater than seven, except when bit 7 of - data_type is set, in which case the number of unused bits will be less than - eight. data_type is set as noted here every time inflate() returns for all - flush options, and so can be used to determine the amount of currently - consumed input in bits. - - The Z_TREES option behaves as Z_BLOCK does, but it also returns when the - end of each deflate block header is reached, before any actual data in that - block is decoded. This allows the caller to determine the length of the - deflate block header for later use in random access within a deflate block. - 256 is added to the value of strm->data_type when inflate() returns - immediately after reaching the end of the deflate block header. - - inflate() should normally be called until it returns Z_STREAM_END or an - error. However if all decompression is to be performed in a single step (a - single call of inflate), the parameter flush should be set to Z_FINISH. In - this case all pending input is processed and all pending output is flushed; - avail_out must be large enough to hold all of the uncompressed data for the - operation to complete. (The size of the uncompressed data may have been - saved by the compressor for this purpose.) The use of Z_FINISH is not - required to perform an inflation in one step. However it may be used to - inform inflate that a faster approach can be used for the single inflate() - call. Z_FINISH also informs inflate to not maintain a sliding window if the - stream completes, which reduces inflate's memory footprint. If the stream - does not complete, either because not all of the stream is provided or not - enough output space is provided, then a sliding window will be allocated and - inflate() can be called again to continue the operation as if Z_NO_FLUSH had - been used. - - In this implementation, inflate() always flushes as much output as - possible to the output buffer, and always uses the faster approach on the - first call. So the effects of the flush parameter in this implementation are - on the return value of inflate() as noted below, when inflate() returns early - when Z_BLOCK or Z_TREES is used, and when inflate() avoids the allocation of - memory for a sliding window when Z_FINISH is used. - - If a preset dictionary is needed after this call (see inflateSetDictionary - below), inflate sets strm->adler to the Adler-32 checksum of the dictionary - chosen by the compressor and returns Z_NEED_DICT; otherwise it sets - strm->adler to the Adler-32 checksum of all output produced so far (that is, - total_out bytes) and returns Z_OK, Z_STREAM_END or an error code as described - below. At the end of the stream, inflate() checks that its computed Adler-32 - checksum is equal to that saved by the compressor and returns Z_STREAM_END - only if the checksum is correct. - - inflate() can decompress and check either zlib-wrapped or gzip-wrapped - deflate data. The header type is detected automatically, if requested when - initializing with inflateInit2(). Any information contained in the gzip - header is not retained unless inflateGetHeader() is used. When processing - gzip-wrapped deflate data, strm->adler32 is set to the CRC-32 of the output - produced so far. The CRC-32 is checked against the gzip trailer, as is the - uncompressed length, modulo 2^32. - - inflate() returns Z_OK if some progress has been made (more input processed - or more output produced), Z_STREAM_END if the end of the compressed data has - been reached and all uncompressed output has been produced, Z_NEED_DICT if a - preset dictionary is needed at this point, Z_DATA_ERROR if the input data was - corrupted (input stream not conforming to the zlib format or incorrect check - value, in which case strm->msg points to a string with a more specific - error), Z_STREAM_ERROR if the stream structure was inconsistent (for example - next_in or next_out was Z_NULL, or the state was inadvertently written over - by the application), Z_MEM_ERROR if there was not enough memory, Z_BUF_ERROR - if no progress was possible or if there was not enough room in the output - buffer when Z_FINISH is used. Note that Z_BUF_ERROR is not fatal, and - inflate() can be called again with more input and more output space to - continue decompressing. If Z_DATA_ERROR is returned, the application may - then call inflateSync() to look for a good compression block if a partial - recovery of the data is to be attempted. -*/ - - -int inflateEnd(z_streamp strm); -/* - All dynamically allocated data structures for this stream are freed. - This function discards any unprocessed input and does not flush any pending - output. - - inflateEnd returns Z_OK if success, or Z_STREAM_ERROR if the stream state - was inconsistent. -*/ - - /* Advanced functions */ - -/* - The following functions are needed only in some special applications. -*/ - -int deflateInit2(z_streamp strm, - int level, - int method, - int windowBits, - int memLevel, - int strategy) -{ - return deflateInit2_(strm, level, method, windowBits, memLevel, - strategy, ZLIB_VERSION.ptr, z_stream.sizeof); -} -/* - This is another version of deflateInit with more compression options. The - fields zalloc, zfree and opaque must be initialized before by the caller. - - The method parameter is the compression method. It must be Z_DEFLATED in - this version of the library. - - The windowBits parameter is the base two logarithm of the window size - (the size of the history buffer). It should be in the range 8 .. 15 for this - version of the library. Larger values of this parameter result in better - compression at the expense of memory usage. The default value is 15 if - deflateInit is used instead. - - For the current implementation of deflate(), a windowBits value of 8 (a - window size of 256 bytes) is not supported. As a result, a request for 8 - will result in 9 (a 512-byte window). In that case, providing 8 to - inflateInit2() will result in an error when the zlib header with 9 is - checked against the initialization of inflate(). The remedy is to not use 8 - with deflateInit2() with this initialization, or at least in that case use 9 - with inflateInit2(). - - windowBits can also be -8 .. -15 for raw deflate. In this case, -windowBits - determines the window size. deflate() will then generate raw deflate data - with no zlib header or trailer, and will not compute a check value. - - windowBits can also be greater than 15 for optional gzip encoding. Add - 16 to windowBits to write a simple gzip header and trailer around the - compressed data instead of a zlib wrapper. The gzip header will have no - file name, no extra data, no comment, no modification time (set to zero), no - header crc, and the operating system will be set to the appropriate value, - if the operating system was determined at compile time. If a gzip stream is - being written, strm->adler is a CRC-32 instead of an Adler-32. - - For raw deflate or gzip encoding, a request for a 256-byte window is - rejected as invalid, since only the zlib header provides a means of - transmitting the window size to the decompressor. - - The memLevel parameter specifies how much memory should be allocated - for the internal compression state. memLevel=1 uses minimum memory but is - slow and reduces compression ratio; memLevel=9 uses maximum memory for - optimal speed. The default value is 8. See zconf.h for total memory usage - as a function of windowBits and memLevel. - - The strategy parameter is used to tune the compression algorithm. Use the - value Z_DEFAULT_STRATEGY for normal data, Z_FILTERED for data produced by a - filter (or predictor), Z_HUFFMAN_ONLY to force Huffman encoding only (no - string match), or Z_RLE to limit match distances to one (run-length - encoding). Filtered data consists mostly of small values with a somewhat - random distribution. In this case, the compression algorithm is tuned to - compress them better. The effect of Z_FILTERED is to force more Huffman - coding and less string matching; it is somewhat intermediate between - Z_DEFAULT_STRATEGY and Z_HUFFMAN_ONLY. Z_RLE is designed to be almost as - fast as Z_HUFFMAN_ONLY, but give better compression for PNG image data. The - strategy parameter only affects the compression ratio but not the - correctness of the compressed output even if it is not set appropriately. - Z_FIXED prevents the use of dynamic Huffman codes, allowing for a simpler - decoder for special applications. - - deflateInit2 returns Z_OK if success, Z_MEM_ERROR if there was not enough - memory, Z_STREAM_ERROR if any parameter is invalid (such as an invalid - method), or Z_VERSION_ERROR if the zlib library version (zlib_version) is - incompatible with the version assumed by the caller (ZLIB_VERSION). msg is - set to null if there is no error message. deflateInit2 does not perform any - compression: this will be done by deflate(). -*/ - -int deflateSetDictionary(z_streamp strm, const(ubyte)* dictionary, uint dictLength); -/* - Initializes the compression dictionary from the given byte sequence - without producing any compressed output. When using the zlib format, this - function must be called immediately after deflateInit, deflateInit2 or - deflateReset, and before any call of deflate. When doing raw deflate, this - function must be called either before any call of deflate, or immediately - after the completion of a deflate block, i.e. after all input has been - consumed and all output has been delivered when using any of the flush - options Z_BLOCK, Z_PARTIAL_FLUSH, Z_SYNC_FLUSH, or Z_FULL_FLUSH. The - compressor and decompressor must use exactly the same dictionary (see - inflateSetDictionary). - - The dictionary should consist of strings (byte sequences) that are likely - to be encountered later in the data to be compressed, with the most commonly - used strings preferably put towards the end of the dictionary. Using a - dictionary is most useful when the data to be compressed is short and can be - predicted with good accuracy; the data can then be compressed better than - with the default empty dictionary. - - Depending on the size of the compression data structures selected by - deflateInit or deflateInit2, a part of the dictionary may in effect be - discarded, for example if the dictionary is larger than the window size - provided in deflateInit or deflateInit2. Thus the strings most likely to be - useful should be put at the end of the dictionary, not at the front. In - addition, the current implementation of deflate will use at most the window - size minus 262 bytes of the provided dictionary. - - Upon return of this function, strm->adler is set to the Adler-32 value - of the dictionary; the decompressor may later use this value to determine - which dictionary has been used by the compressor. (The Adler-32 value - applies to the whole dictionary even if only a subset of the dictionary is - actually used by the compressor.) If a raw deflate was requested, then the - Adler-32 value is not computed and strm->adler is not set. - - deflateSetDictionary returns Z_OK if success, or Z_STREAM_ERROR if a - parameter is invalid (e.g. dictionary being Z_NULL) or the stream state is - inconsistent (for example if deflate has already been called for this stream - or if not at a block boundary for raw deflate). deflateSetDictionary does - not perform any compression: this will be done by deflate(). -*/ - -int deflateGetDictionary(z_streamp strm, ubyte *dictionary, uint dictLength); -/* - Returns the sliding dictionary being maintained by deflate. dictLength is - set to the number of bytes in the dictionary, and that many bytes are copied - to dictionary. dictionary must have enough space, where 32768 bytes is - always enough. If deflateGetDictionary() is called with dictionary equal to - Z_NULL, then only the dictionary length is returned, and nothing is copied. - Similarly, if dictLength is Z_NULL, then it is not set. - - deflateGetDictionary() may return a length less than the window size, even - when more than the window size in input has been provided. It may return up - to 258 bytes less in that case, due to how zlib's implementation of deflate - manages the sliding window and lookahead for matches, where matches can be - up to 258 bytes long. If the application needs the last window-size bytes of - input, then that would need to be saved by the application outside of zlib. - - deflateGetDictionary returns Z_OK on success, or Z_STREAM_ERROR if the - stream state is inconsistent. -*/ - -int deflateCopy(z_streamp dest, z_streamp source); -/* - Sets the destination stream as a complete copy of the source stream. - - This function can be useful when several compression strategies will be - tried, for example when there are several ways of pre-processing the input - data with a filter. The streams that will be discarded should then be freed - by calling deflateEnd. Note that deflateCopy duplicates the internal - compression state which can be quite large, so this strategy is slow and can - consume lots of memory. - - deflateCopy returns Z_OK if success, Z_MEM_ERROR if there was not - enough memory, Z_STREAM_ERROR if the source stream state was inconsistent - (such as zalloc being Z_NULL). msg is left unchanged in both source and - destination. -*/ - -int deflateReset(z_streamp strm); -/* - This function is equivalent to deflateEnd followed by deflateInit, but - does not free and reallocate the internal compression state. The stream - will leave the compression level and any other attributes that may have been - set unchanged. total_in, total_out, adler, and msg are initialized. - - deflateReset returns Z_OK if success, or Z_STREAM_ERROR if the source - stream state was inconsistent (such as zalloc or state being Z_NULL). -*/ - -int deflateParams(z_streamp strm, int level, int strategy); -/* - Dynamically update the compression level and compression strategy. The - interpretation of level and strategy is as in deflateInit2(). This can be - used to switch between compression and straight copy of the input data, or - to switch to a different kind of input data requiring a different strategy. - If the compression approach (which is a function of the level) or the - strategy is changed, and if there have been any deflate() calls since the - state was initialized or reset, then the input available so far is - compressed with the old level and strategy using deflate(strm, Z_BLOCK). - There are three approaches for the compression levels 0, 1 .. 3, and 4 .. 9 - respectively. The new level and strategy will take effect at the next call - of deflate(). - - If a deflate(strm, Z_BLOCK) is performed by deflateParams(), and it does - not have enough output space to complete, then the parameter change will not - take effect. In this case, deflateParams() can be called again with the - same parameters and more output space to try again. - - In order to assure a change in the parameters on the first try, the - deflate stream should be flushed using deflate() with Z_BLOCK or other flush - request until strm.avail_out is not zero, before calling deflateParams(). - Then no more input data should be provided before the deflateParams() call. - If this is done, the old level and strategy will be applied to the data - compressed before deflateParams(), and the new level and strategy will be - applied to the data compressed after deflateParams(). - - deflateParams returns Z_OK on success, Z_STREAM_ERROR if the source stream - state was inconsistent or if a parameter was invalid, or Z_BUF_ERROR if - there was not enough output space to complete the compression of the - available input data before a change in the strategy or approach. Note that - in the case of a Z_BUF_ERROR, the parameters are not changed. A return - value of Z_BUF_ERROR is not fatal, in which case deflateParams() can be - retried with more output space. -*/ - -int deflateTune(z_streamp strm, int good_length, int max_lazy, int nice_length, - int max_chain); -/* - Fine tune deflate's internal compression parameters. This should only be - used by someone who understands the algorithm used by zlib's deflate for - searching for the best matching string, and even then only by the most - fanatic optimizer trying to squeeze out the last compressed bit for their - specific input data. Read the deflate.c source code for the meaning of the - max_lazy, good_length, nice_length, and max_chain parameters. - - deflateTune() can be called after deflateInit() or deflateInit2(), and - returns Z_OK on success, or Z_STREAM_ERROR for an invalid deflate stream. - */ - -size_t deflateBound(z_streamp strm, size_t sourceLen); -/* - deflateBound() returns an upper bound on the compressed size after - deflation of sourceLen bytes. It must be called after deflateInit() or - deflateInit2(), and after deflateSetHeader(), if used. This would be used - to allocate an output buffer for deflation in a single pass, and so would be - called before deflate(). If that first deflate() call is provided the - sourceLen input bytes, an output buffer allocated to the size returned by - deflateBound(), and the flush value Z_FINISH, then deflate() is guaranteed - to return Z_STREAM_END. Note that it is possible for the compressed size to - be larger than the value returned by deflateBound() if flush options other - than Z_FINISH or Z_NO_FLUSH are used. -*/ - -int deflatePending(z_streamp strm, uint* pending, int* bits); -/* - deflatePending() returns the number of bytes and bits of output that have - been generated, but not yet provided in the available output. The bytes not - provided would be due to the available output space having being consumed. - The number of bits of output not provided are between 0 and 7, where they - await more bits to join them in order to fill out a full byte. If pending - or bits are Z_NULL, then those values are not set. - - deflatePending returns Z_OK if success, or Z_STREAM_ERROR if the source - stream state was inconsistent. - */ - -int deflatePrime(z_streamp strm, int bits, int value); -/* - deflatePrime() inserts bits in the deflate output stream. The intent - is that this function is used to start off the deflate output with the bits - leftover from a previous deflate stream when appending to it. As such, this - function can only be used for raw deflate, and must be used before the first - deflate() call after a deflateInit2() or deflateReset(). bits must be less - than or equal to 16, and that many of the least significant bits of value - will be inserted in the output. - - deflatePrime returns Z_OK if success, Z_BUF_ERROR if there was not enough - room in the internal buffer to insert the bits, or Z_STREAM_ERROR if the - source stream state was inconsistent. -*/ - -int deflateSetHeader(z_streamp strm, gz_headerp head); -/* - deflateSetHeader() provides gzip header information for when a gzip - stream is requested by deflateInit2(). deflateSetHeader() may be called - after deflateInit2() or deflateReset() and before the first call of - deflate(). The text, time, os, extra field, name, and comment information - in the provided gz_header structure are written to the gzip header (xflag is - ignored -- the extra flags are set according to the compression level). The - caller must assure that, if not Z_NULL, name and comment are terminated with - a zero byte, and that if extra is not Z_NULL, that extra_len bytes are - available there. If hcrc is true, a gzip header crc is included. Note that - the current versions of the command-line version of gzip (up through version - 1.3.x) do not support header crc's, and will report that it is a "multi-part - gzip file" and give up. - - If deflateSetHeader is not used, the default gzip header has text false, - the time set to zero, and os set to the current operating system, with no - extra, name, or comment fields. The gzip header is returned to the default - state by deflateReset(). - - deflateSetHeader returns Z_OK if success, or Z_STREAM_ERROR if the source - stream state was inconsistent. -*/ - -int inflateInit2(z_streamp strm, int windowBits) -{ - return inflateInit2_(strm, windowBits, ZLIB_VERSION.ptr, z_stream.sizeof); -} -/* - This is another version of inflateInit with an extra parameter. The - fields next_in, avail_in, zalloc, zfree and opaque must be initialized - before by the caller. - - The windowBits parameter is the base two logarithm of the maximum window - size (the size of the history buffer). It should be in the range 8 .. 15 for - this version of the library. The default value is 15 if inflateInit is used - instead. windowBits must be greater than or equal to the windowBits value - provided to deflateInit2() while compressing, or it must be equal to 15 if - deflateInit2() was not used. If a compressed stream with a larger window - size is given as input, inflate() will return with the error code - Z_DATA_ERROR instead of trying to allocate a larger window. - - windowBits can also be zero to request that inflate use the window size in - the zlib header of the compressed stream. - - windowBits can also be -8 .. -15 for raw inflate. In this case, -windowBits - determines the window size. inflate() will then process raw deflate data, - not looking for a zlib or gzip header, not generating a check value, and not - looking for any check values for comparison at the end of the stream. This - is for use with other formats that use the deflate compressed data format - such as zip. Those formats provide their own check values. If a custom - format is developed using the raw deflate format for compressed data, it is - recommended that a check value such as an Adler-32 or a CRC-32 be applied to - the uncompressed data as is done in the zlib, gzip, and zip formats. For - most applications, the zlib format should be used as is. Note that comments - above on the use in deflateInit2() applies to the magnitude of windowBits. - - windowBits can also be greater than 15 for optional gzip decoding. Add - 32 to windowBits to enable zlib and gzip decoding with automatic header - detection, or add 16 to decode only the gzip format (the zlib format will - return a Z_DATA_ERROR). If a gzip stream is being decoded, strm->adler is a - CRC-32 instead of an Adler-32. Unlike the gunzip utility and gzread() (see - below), inflate() will *not* automatically decode concatenated gzip members. - inflate() will return Z_STREAM_END at the end of the gzip member. The state - would need to be reset to continue decoding a subsequent gzip member. This - *must* be done if there is more data after a gzip member, in order for the - decompression to be compliant with the gzip standard (RFC 1952). - - inflateInit2 returns Z_OK if success, Z_MEM_ERROR if there was not enough - memory, Z_VERSION_ERROR if the zlib library version is incompatible with the - version assumed by the caller, or Z_STREAM_ERROR if the parameters are - invalid, such as a null pointer to the structure. msg is set to null if - there is no error message. inflateInit2 does not perform any decompression - apart from possibly reading the zlib header if present: actual decompression - will be done by inflate(). (So next_in and avail_in may be modified, but - next_out and avail_out are unused and unchanged.) The current implementation - of inflateInit2() does not process any header information -- that is - deferred until inflate() is called. -*/ - -int inflateSetDictionary(z_streamp strm, const(ubyte)* dictionary, uint dictLength); -/* - Initializes the decompression dictionary from the given uncompressed byte - sequence. This function must be called immediately after a call of inflate, - if that call returned Z_NEED_DICT. The dictionary chosen by the compressor - can be determined from the Adler-32 value returned by that call of inflate. - The compressor and decompressor must use exactly the same dictionary (see - deflateSetDictionary). For raw inflate, this function can be called at any - time to set the dictionary. If the provided dictionary is smaller than the - window and there is already data in the window, then the provided dictionary - will amend what's there. The application must insure that the dictionary - that was used for compression is provided. - - inflateSetDictionary returns Z_OK if success, Z_STREAM_ERROR if a - parameter is invalid (e.g. dictionary being Z_NULL) or the stream state is - inconsistent, Z_DATA_ERROR if the given dictionary doesn't match the - expected one (incorrect Adler-32 value). inflateSetDictionary does not - perform any decompression: this will be done by subsequent calls of - inflate(). -*/ - -int inflateGetDictionary(z_streamp strm, ubyte* dictionary, uint* dictLength); -/* - Returns the sliding dictionary being maintained by inflate. dictLength is - set to the number of bytes in the dictionary, and that many bytes are copied - to dictionary. dictionary must have enough space, where 32768 bytes is - always enough. If inflateGetDictionary() is called with dictionary equal to - Z_NULL, then only the dictionary length is returned, and nothing is copied. - Similarly, if dictLength is Z_NULL, then it is not set. - - inflateGetDictionary returns Z_OK on success, or Z_STREAM_ERROR if the - stream state is inconsistent. -*/ - -int inflateSync(z_streamp strm); -/* - Skips invalid compressed data until a possible full flush point (see above - for the description of deflate with Z_FULL_FLUSH) can be found, or until all - available input is skipped. No output is provided. - - inflateSync searches for a 00 00 FF FF pattern in the compressed data. - All full flush points have this pattern, but not all occurrences of this - pattern are full flush points. - - inflateSync returns Z_OK if a possible full flush point has been found, - Z_BUF_ERROR if no more input was provided, Z_DATA_ERROR if no flush point - has been found, or Z_STREAM_ERROR if the stream structure was inconsistent. - In the success case, the application may save the current value of total_in - which indicates where valid compressed data was found. In the error case, - the application may repeatedly call inflateSync, providing more input each - time, until success or end of the input data. -*/ - -int inflateCopy(z_streamp dest, z_streamp source); -/* - Sets the destination stream as a complete copy of the source stream. - - This function can be useful when randomly accessing a large stream. The - first pass through the stream can periodically record the inflate state, - allowing restarting inflate at those points when randomly accessing the - stream. - - inflateCopy returns Z_OK if success, Z_MEM_ERROR if there was not - enough memory, Z_STREAM_ERROR if the source stream state was inconsistent - (such as zalloc being Z_NULL). msg is left unchanged in both source and - destination. -*/ - -int inflateReset(z_streamp strm); -/* - This function is equivalent to inflateEnd followed by inflateInit, - but does not free and reallocate the internal decompression state. The - stream will keep attributes that may have been set by inflateInit2. - - inflateReset returns Z_OK if success, or Z_STREAM_ERROR if the source - stream state was inconsistent (such as zalloc or state being Z_NULL). -*/ - -int inflateReset2(z_streamp strm, int windowBits); -/* - This function is the same as inflateReset, but it also permits changing - the wrap and window size requests. The windowBits parameter is interpreted - the same as it is for inflateInit2. If the window size is changed, then the - memory allocated for the window is freed, and the window will be reallocated - by inflate() if needed. - - inflateReset2 returns Z_OK if success, or Z_STREAM_ERROR if the source - stream state was inconsistent (such as zalloc or state being Z_NULL), or if - the windowBits parameter is invalid. -*/ - -int inflatePrime(z_streamp strm, int bits, int value); -/* - This function inserts bits in the inflate input stream. The intent is - that this function is used to start inflating at a bit position in the - middle of a byte. The provided bits will be used before any bytes are used - from next_in. This function should only be used with raw inflate, and - should be used before the first inflate() call after inflateInit2() or - inflateReset(). bits must be less than or equal to 16, and that many of the - least significant bits of value will be inserted in the input. - - If bits is negative, then the input stream bit buffer is emptied. Then - inflatePrime() can be called again to put bits in the buffer. This is used - to clear out bits leftover after feeding inflate a block description prior - to feeding inflate codes. - - inflatePrime returns Z_OK if success, or Z_STREAM_ERROR if the source - stream state was inconsistent. -*/ - -c_long inflateMark(z_streamp strm); -/* - This function returns two values, one in the lower 16 bits of the return - value, and the other in the remaining upper bits, obtained by shifting the - return value down 16 bits. If the upper value is -1 and the lower value is - zero, then inflate() is currently decoding information outside of a block. - If the upper value is -1 and the lower value is non-zero, then inflate is in - the middle of a stored block, with the lower value equaling the number of - bytes from the input remaining to copy. If the upper value is not -1, then - it is the number of bits back from the current bit position in the input of - the code (literal or length/distance pair) currently being processed. In - that case the lower value is the number of bytes already emitted for that - code. - - A code is being processed if inflate is waiting for more input to complete - decoding of the code, or if it has completed decoding but is waiting for - more output space to write the literal or match data. - - inflateMark() is used to mark locations in the input data for random - access, which may be at bit positions, and to note those cases where the - output of a code may span boundaries of random access blocks. The current - location in the input stream can be determined from avail_in and data_type - as noted in the description for the Z_BLOCK flush parameter for inflate. - - inflateMark returns the value noted above, or -65536 if the provided - source stream state was inconsistent. -*/ - -int inflateGetHeader(z_streamp strm, gz_headerp head); -/* - inflateGetHeader() requests that gzip header information be stored in the - provided gz_header structure. inflateGetHeader() may be called after - inflateInit2() or inflateReset(), and before the first call of inflate(). - As inflate() processes the gzip stream, head->done is zero until the header - is completed, at which time head->done is set to one. If a zlib stream is - being decoded, then head->done is set to -1 to indicate that there will be - no gzip header information forthcoming. Note that Z_BLOCK or Z_TREES can be - used to force inflate() to return immediately after header processing is - complete and before any actual data is decompressed. - - The text, time, xflags, and os fields are filled in with the gzip header - contents. hcrc is set to true if there is a header CRC. (The header CRC - was valid if done is set to one.) If extra is not Z_NULL, then extra_max - contains the maximum number of bytes to write to extra. Once done is true, - extra_len contains the actual extra field length, and extra contains the - extra field, or that field truncated if extra_max is less than extra_len. - If name is not Z_NULL, then up to name_max characters are written there, - terminated with a zero unless the length is greater than name_max. If - comment is not Z_NULL, then up to comm_max characters are written there, - terminated with a zero unless the length is greater than comm_max. When any - of extra, name, or comment are not Z_NULL and the respective field is not - present in the header, then that field is set to Z_NULL to signal its - absence. This allows the use of deflateSetHeader() with the returned - structure to duplicate the header. However if those fields are set to - allocated memory, then the application will need to save those pointers - elsewhere so that they can be eventually freed. - - If inflateGetHeader is not used, then the header information is simply - discarded. The header is always checked for validity, including the header - CRC if present. inflateReset() will reset the process to discard the header - information. The application would need to call inflateGetHeader() again to - retrieve the header from the next gzip stream. - - inflateGetHeader returns Z_OK if success, or Z_STREAM_ERROR if the source - stream state was inconsistent. -*/ - - -int inflateBackInit(z_stream* strm, int windowBits, ubyte* window) -{ - return inflateBackInit_(strm, windowBits, window, ZLIB_VERSION.ptr, z_stream.sizeof); -} -/* - Initialize the internal stream state for decompression using inflateBack() - calls. The fields zalloc, zfree and opaque in strm must be initialized - before the call. If zalloc and zfree are Z_NULL, then the default library- - derived memory allocation routines are used. windowBits is the base two - logarithm of the window size, in the range 8 .. 15. window is a caller - supplied buffer of that size. Except for special applications where it is - assured that deflate was used with small window sizes, windowBits must be 15 - and a 32K byte window must be supplied to be able to decompress general - deflate streams. - - See inflateBack() for the usage of these routines. - - inflateBackInit will return Z_OK on success, Z_STREAM_ERROR if any of - the parameters are invalid, Z_MEM_ERROR if the internal state could not be - allocated, or Z_VERSION_ERROR if the version of the library does not match - the version of the header file. -*/ - -alias in_func = uint function(void*, ubyte**); -alias out_func = int function(void*, ubyte*, uint); - -int inflateBack(z_stream* strm, - in_func f_in, - void* in_desc, - out_func f_out, - void* out_desc); -/* - inflateBack() does a raw inflate with a single call using a call-back - interface for input and output. This is potentially more efficient than - inflate() for file i/o applications, in that it avoids copying between the - output and the sliding window by simply making the window itself the output - buffer. inflate() can be faster on modern CPUs when used with large - buffers. inflateBack() trusts the application to not change the output - buffer passed by the output function, at least until inflateBack() returns. - - inflateBackInit() must be called first to allocate the internal state - and to initialize the state with the user-provided window buffer. - inflateBack() may then be used multiple times to inflate a complete, raw - deflate stream with each call. inflateBackEnd() is then called to free the - allocated state. - - A raw deflate stream is one with no zlib or gzip header or trailer. - This routine would normally be used in a utility that reads zip or gzip - files and writes out uncompressed files. The utility would decode the - header and process the trailer on its own, hence this routine expects only - the raw deflate stream to decompress. This is different from the default - behavior of inflate(), which expects a zlib header and trailer around the - deflate stream. - - inflateBack() uses two subroutines supplied by the caller that are then - called by inflateBack() for input and output. inflateBack() calls those - routines until it reads a complete deflate stream and writes out all of the - uncompressed data, or until it encounters an error. The function's - parameters and return types are defined above in the in_func and out_func - typedefs. inflateBack() will call in(in_desc, &buf) which should return the - number of bytes of provided input, and a pointer to that input in buf. If - there is no input available, in() must return zero -- buf is ignored in that - case -- and inflateBack() will return a buffer error. inflateBack() will - call out(out_desc, buf, len) to write the uncompressed data buf[0 .. len-1]. - out() should return zero on success, or non-zero on failure. If out() - returns non-zero, inflateBack() will return with an error. Neither in() nor - out() are permitted to change the contents of the window provided to - inflateBackInit(), which is also the buffer that out() uses to write from. - The length written by out() will be at most the window size. Any non-zero - amount of input may be provided by in(). - - For convenience, inflateBack() can be provided input on the first call by - setting strm->next_in and strm->avail_in. If that input is exhausted, then - in() will be called. Therefore strm->next_in must be initialized before - calling inflateBack(). If strm->next_in is Z_NULL, then in() will be called - immediately for input. If strm->next_in is not Z_NULL, then strm->avail_in - must also be initialized, and then if strm->avail_in is not zero, input will - initially be taken from strm->next_in[0 .. strm->avail_in - 1]. - - The in_desc and out_desc parameters of inflateBack() is passed as the - first parameter of in() and out() respectively when they are called. These - descriptors can be optionally used to pass any information that the caller- - supplied in() and out() functions need to do their job. - - On return, inflateBack() will set strm->next_in and strm->avail_in to - pass back any unused input that was provided by the last in() call. The - return values of inflateBack() can be Z_STREAM_END on success, Z_BUF_ERROR - if in() or out() returned an error, Z_DATA_ERROR if there was a format error - in the deflate stream (in which case strm->msg is set to indicate the nature - of the error), or Z_STREAM_ERROR if the stream was not properly initialized. - In the case of Z_BUF_ERROR, an input or output error can be distinguished - using strm->next_in which will be Z_NULL only if in() returned an error. If - strm->next_in is not Z_NULL, then the Z_BUF_ERROR was due to out() returning - non-zero. (in() will always be called before out(), so strm->next_in is - assured to be defined if out() returns non-zero.) Note that inflateBack() - cannot return Z_OK. -*/ - -int inflateBackEnd(z_stream* strm); -/* - All memory allocated by inflateBackInit() is freed. - - inflateBackEnd() returns Z_OK on success, or Z_STREAM_ERROR if the stream - state was inconsistent. -*/ - -uint zlibCompileFlags(); -/* Return flags indicating compile-time options. - - Type sizes, two bits each, 00 = 16 bits, 01 = 32, 10 = 64, 11 = other: - 1.0: size of uInt - 3.2: size of uLong - 5.4: size of voidpf (pointer) - 7.6: size of z_off_t - - Compiler, assembler, and debug options: - 8: ZLIB_DEBUG - 9: ASMV or ASMINF -- use ASM code - 10: ZLIB_WINAPI -- exported functions use the WINAPI calling convention - 11: 0 (reserved) - - One-time table building (smaller code, but not thread-safe if true): - 12: BUILDFIXED -- build static block decoding tables when needed - 13: DYNAMIC_CRC_TABLE -- build CRC calculation tables when needed - 14,15: 0 (reserved) - - Library content (indicates missing functionality): - 16: NO_GZCOMPRESS -- gz* functions cannot compress (to avoid linking - deflate code when not needed) - 17: NO_GZIP -- deflate can't write gzip streams, and inflate can't detect - and decode gzip streams (to avoid linking crc code) - 18-19: 0 (reserved) - - Operation variations (changes in library functionality): - 20: PKZIP_BUG_WORKAROUND -- slightly more permissive inflate - 21: FASTEST -- deflate algorithm with only one, lowest compression level - 22,23: 0 (reserved) - - The sprintf variant used by gzprintf (zero is best): - 24: 0 = vs*, 1 = s* -- 1 means limited to 20 arguments after the format - 25: 0 = *nprintf, 1 = *printf -- 1 means gzprintf() not secure! - 26: 0 = returns value, 1 = void -- 1 means inferred string length returned - - Remainder: - 27-31: 0 (reserved) - */ - - /* utility functions */ - -/* - The following utility functions are implemented on top of the basic - stream-oriented functions. To simplify the interface, some default options - are assumed (compression level and memory usage, standard memory allocation - functions). The source code of these utility functions can be modified if - you need special options. -*/ - -int compress(ubyte* dest, - size_t* destLen, - const(ubyte)* source, - size_t sourceLen); -/* - Compresses the source buffer into the destination buffer. sourceLen is - the byte length of the source buffer. Upon entry, destLen is the total size - of the destination buffer, which must be at least the value returned by - compressBound(sourceLen). Upon exit, destLen is the actual size of the - compressed data. compress() is equivalent to compress2() with a level - parameter of Z_DEFAULT_COMPRESSION. - - compress returns Z_OK if success, Z_MEM_ERROR if there was not - enough memory, Z_BUF_ERROR if there was not enough room in the output - buffer. -*/ - -int compress2(ubyte* dest, - size_t* destLen, - const(ubyte)* source, - size_t sourceLen, - int level); -/* - Compresses the source buffer into the destination buffer. The level - parameter has the same meaning as in deflateInit. sourceLen is the byte - length of the source buffer. Upon entry, destLen is the total size of the - destination buffer, which must be at least the value returned by - compressBound(sourceLen). Upon exit, destLen is the actual size of the - compressed data. - - compress2 returns Z_OK if success, Z_MEM_ERROR if there was not enough - memory, Z_BUF_ERROR if there was not enough room in the output buffer, - Z_STREAM_ERROR if the level parameter is invalid. -*/ - -size_t compressBound(size_t sourceLen); -/* - compressBound() returns an upper bound on the compressed size after - compress() or compress2() on sourceLen bytes. It would be used before a - compress() or compress2() call to allocate the destination buffer. -*/ - -int uncompress(ubyte* dest, - size_t* destLen, - const(ubyte)* source, - size_t sourceLen); -/* - Decompresses the source buffer into the destination buffer. sourceLen is - the byte length of the source buffer. Upon entry, destLen is the total size - of the destination buffer, which must be large enough to hold the entire - uncompressed data. (The size of the uncompressed data must have been saved - previously by the compressor and transmitted to the decompressor by some - mechanism outside the scope of this compression library.) Upon exit, destLen - is the actual size of the uncompressed data. - - uncompress returns Z_OK if success, Z_MEM_ERROR if there was not - enough memory, Z_BUF_ERROR if there was not enough room in the output - buffer, or Z_DATA_ERROR if the input data was corrupted or incomplete. In - the case where there is not enough room, uncompress() will fill the output - buffer with the uncompressed data up to that point. -*/ - -int uncompress2(ubyte* dest, - size_t* destLen, - const(ubyte)* source, - size_t* sourceLen); -/* - Same as uncompress, except that sourceLen is a pointer, where the - length of the source is *sourceLen. On return, *sourceLen is the number of - source bytes consumed. -*/ - - /* gzip file access functions */ - -/* - This library supports reading and writing files in gzip (.gz) format with - an interface similar to that of stdio, using the functions that start with - "gz". The gzip format is different from the zlib format. gzip is a gzip - wrapper, documented in RFC 1952, wrapped around a deflate stream. -*/ - -alias gzFile = void*; -alias z_off_t = int; // file offset -alias z_size_t = size_t; - -gzFile gzopen(const(char)* path, const(char)* mode); -/* - Open the gzip (.gz) file at path for reading and decompressing, or - compressing and writing. The mode parameter is as in fopen ("rb" or "wb") - but can also include a compression level ("wb9") or a strategy: 'f' for - filtered data as in "wb6f", 'h' for Huffman-only compression as in "wb1h", - 'R' for run-length encoding as in "wb1R", or 'F' for fixed code compression - as in "wb9F". (See the description of deflateInit2 for more information - about the strategy parameter.) 'T' will request transparent writing or - appending with no compression and not using the gzip format. - - "a" can be used instead of "w" to request that the gzip stream that will - be written be appended to the file. "+" will result in an error, since - reading and writing to the same gzip file is not supported. The addition of - "x" when writing will create the file exclusively, which fails if the file - already exists. On systems that support it, the addition of "e" when - reading or writing will set the flag to close the file on an execve() call. - - These functions, as well as gzip, will read and decode a sequence of gzip - streams in a file. The append function of gzopen() can be used to create - such a file. (Also see gzflush() for another way to do this.) When - appending, gzopen does not test whether the file begins with a gzip stream, - nor does it look for the end of the gzip streams to begin appending. gzopen - will simply append a gzip stream to the existing file. - - gzopen can be used to read a file which is not in gzip format; in this - case gzread will directly read from the file without decompression. When - reading, this will be detected automatically by looking for the magic two- - byte gzip header. - - gzopen returns NULL if the file could not be opened, if there was - insufficient memory to allocate the gzFile state, or if an invalid mode was - specified (an 'r', 'w', or 'a' was not provided, or '+' was provided). - errno can be checked to determine if the reason gzopen failed was that the - file could not be opened. -*/ - -gzFile gzdopen(int fd, const(char)* mode); -/* - Associate a gzFile with the file descriptor fd. File descriptors are - obtained from calls like open, dup, creat, pipe or fileno (if the file has - been previously opened with fopen). The mode parameter is as in gzopen. - - The next call of gzclose on the returned gzFile will also close the file - descriptor fd, just like fclose(fdopen(fd, mode)) closes the file descriptor - fd. If you want to keep fd open, use fd = dup(fd_keep); gz = gzdopen(fd, - mode);. The duplicated descriptor should be saved to avoid a leak, since - gzdopen does not close fd if it fails. If you are using fileno() to get the - file descriptor from a FILE *, then you will have to use dup() to avoid - double-close()ing the file descriptor. Both gzclose() and fclose() will - close the associated file descriptor, so they need to have different file - descriptors. - - gzdopen returns NULL if there was insufficient memory to allocate the - gzFile state, if an invalid mode was specified (an 'r', 'w', or 'a' was not - provided, or '+' was provided), or if fd is -1. The file descriptor is not - used until the next gz* read, write, seek, or close operation, so gzdopen - will not detect if fd is invalid (unless fd is -1). -*/ - -int gzbuffer(gzFile file, uint size); -/* - Set the internal buffer size used by this library's functions for file to - size. The default buffer size is 8192 bytes. This function must be called - after gzopen() or gzdopen(), and before any other calls that read or write - the file. The buffer memory allocation is always deferred to the first read - or write. Three times that size in buffer space is allocated. A larger - buffer size of, for example, 64K or 128K bytes will noticeably increase the - speed of decompression (reading). - - The new buffer size also affects the maximum length for gzprintf(). - - gzbuffer() returns 0 on success, or -1 on failure, such as being called - too late. -*/ - -int gzsetparams(gzFile file, int level, int strategy); -/* - Dynamically update the compression level and strategy for file. See the - description of deflateInit2 for the meaning of these parameters. Previously - provided data is flushed before applying the parameter changes. - - gzsetparams returns Z_OK if success, Z_STREAM_ERROR if the file was not - opened for writing, Z_ERRNO if there is an error writing the flushed data, - or Z_MEM_ERROR if there is a memory allocation error. -*/ - -int gzread(gzFile file, void* buf, uint len); -/* - Read and decompress up to len uncompressed bytes from file into buf. If - the input file is not in gzip format, gzread copies the given number of - bytes into the buffer directly from the file. - - After reaching the end of a gzip stream in the input, gzread will continue - to read, looking for another gzip stream. Any number of gzip streams may be - concatenated in the input file, and will all be decompressed by gzread(). - If something other than a gzip stream is encountered after a gzip stream, - that remaining trailing garbage is ignored (and no error is returned). - - gzread can be used to read a gzip file that is being concurrently written. - Upon reaching the end of the input, gzread will return with the available - data. If the error code returned by gzerror is Z_OK or Z_BUF_ERROR, then - gzclearerr can be used to clear the end of file indicator in order to permit - gzread to be tried again. Z_OK indicates that a gzip stream was completed - on the last gzread. Z_BUF_ERROR indicates that the input file ended in the - middle of a gzip stream. Note that gzread does not return -1 in the event - of an incomplete gzip stream. This error is deferred until gzclose(), which - will return Z_BUF_ERROR if the last gzread ended in the middle of a gzip - stream. Alternatively, gzerror can be used before gzclose to detect this - case. - - gzread returns the number of uncompressed bytes actually read, less than - len for end of file, or -1 for error. If len is too large to fit in an int, - then nothing is read, -1 is returned, and the error state is set to - Z_STREAM_ERROR. -*/ - -z_size_t gzfread(void* buf, z_size_t size, z_size_t nitems, gzFile file); -/* - Read and decompress up to nitems items of size size from file into buf, - otherwise operating as gzread() does. This duplicates the interface of - stdio's fread(), with size_t request and return types. If the library - defines size_t, then z_size_t is identical to size_t. If not, then z_size_t - is an unsigned integer type that can contain a pointer. - - gzfread() returns the number of full items read of size size, or zero if - the end of the file was reached and a full item could not be read, or if - there was an error. gzerror() must be consulted if zero is returned in - order to determine if there was an error. If the multiplication of size and - nitems overflows, i.e. the product does not fit in a z_size_t, then nothing - is read, zero is returned, and the error state is set to Z_STREAM_ERROR. - - In the event that the end of file is reached and only a partial item is - available at the end, i.e. the remaining uncompressed data length is not a - multiple of size, then the final partial item is nevertheless read into buf - and the end-of-file flag is set. The length of the partial item read is not - provided, but could be inferred from the result of gztell(). This behavior - is the same as the behavior of fread() implementations in common libraries, - but it prevents the direct use of gzfread() to read a concurrently written - file, resetting and retrying on end-of-file, when size is not 1. -*/ - -int gzwrite(gzFile file, void* buf, uint len); -/* - Compress and write the len uncompressed bytes at buf to file. gzwrite - returns the number of uncompressed bytes written or 0 in case of error. -*/ - -z_size_t gzfwrite(void* buf, z_size_t size, z_size_t nitems, gzFile file); -/* - Compress and write nitems items of size size from buf to file, duplicating - the interface of stdio's fwrite(), with size_t request and return types. If - the library defines size_t, then z_size_t is identical to size_t. If not, - then z_size_t is an unsigned integer type that can contain a pointer. - - gzfwrite() returns the number of full items written of size size, or zero - if there was an error. If the multiplication of size and nitems overflows, - i.e. the product does not fit in a z_size_t, then nothing is written, zero - is returned, and the error state is set to Z_STREAM_ERROR. -*/ - -int gzprintf(gzFile file, const(char)* format, ...); -/* - Convert, format, compress, and write the arguments (...) to file under - control of the string format, as in fprintf. gzprintf returns the number of - uncompressed bytes actually written, or a negative zlib error code in case - of error. The number of uncompressed bytes written is limited to 8191, or - one less than the buffer size given to gzbuffer(). The caller should assure - that this limit is not exceeded. If it is exceeded, then gzprintf() will - return an error (0) with nothing written. In this case, there may also be a - buffer overflow with unpredictable consequences, which is possible only if - zlib was compiled with the insecure functions sprintf() or vsprintf(), - because the secure snprintf() or vsnprintf() functions were not available. - This can be determined using zlibCompileFlags(). -*/ - -int gzputs(gzFile file, const(char)* s); -/* - Compress and write the given null-terminated string s to file, excluding - the terminating null character. - - gzputs returns the number of characters written, or -1 in case of error. -*/ - -const(char)* gzgets(gzFile file, const(char)* buf, int len); -/* - Read and decompress bytes from file into buf, until len-1 characters are - read, or until a newline character is read and transferred to buf, or an - end-of-file condition is encountered. If any characters are read or if len - is one, the string is terminated with a null character. If no characters - are read due to an end-of-file or len is less than one, then the buffer is - left untouched. - - gzgets returns buf which is a null-terminated string, or it returns NULL - for end-of-file or in case of error. If there was an error, the contents at - buf are indeterminate. -*/ - -int gzputc(gzFile file, int c); -/* - Compress and write c, converted to an unsigned char, into file. gzputc - returns the value that was written, or -1 in case of error. -*/ - -int gzgetc(gzFile file); -/* - Read and decompress one byte from file. gzgetc returns this byte or -1 - in case of end of file or error. This is implemented as a macro for speed. - As such, it does not do all of the checking the other functions do. I.e. - it does not check to see if file is NULL, nor whether the structure file - points to has been clobbered or not. -*/ - -int gzungetc(int c, gzFile file); -/* - Push c back onto the stream for file to be read as the first character on - the next read. At least one character of push-back is always allowed. - gzungetc() returns the character pushed, or -1 on failure. gzungetc() will - fail if c is -1, and may fail if a character has been pushed but not read - yet. If gzungetc is used immediately after gzopen or gzdopen, at least the - output buffer size of pushed characters is allowed. (See gzbuffer above.) - The pushed character will be discarded if the stream is repositioned with - gzseek() or gzrewind(). -*/ - -int gzflush(gzFile file, int flush); -/* - Flush all pending output to file. The parameter flush is as in the - deflate() function. The return value is the zlib error number (see function - gzerror below). gzflush is only permitted when writing. - - If the flush parameter is Z_FINISH, the remaining data is written and the - gzip stream is completed in the output. If gzwrite() is called again, a new - gzip stream will be started in the output. gzread() is able to read such - concatenated gzip streams. - - gzflush should be called only when strictly necessary because it will - degrade compression if called too often. -*/ - -z_off_t gzseek(gzFile file, z_off_t offset, int whence); -/* - Set the starting position to offset relative to whence for the next gzread - or gzwrite on file. The offset represents a number of bytes in the - uncompressed data stream. The whence parameter is defined as in lseek(2); - the value SEEK_END is not supported. - - If the file is opened for reading, this function is emulated but can be - extremely slow. If the file is opened for writing, only forward seeks are - supported; gzseek then compresses a sequence of zeroes up to the new - starting position. - - gzseek returns the resulting offset location as measured in bytes from - the beginning of the uncompressed stream, or -1 in case of error, in - particular if the file is opened for writing and the new starting position - would be before the current position. -*/ - -int gzrewind(gzFile file); -/* - Rewind file. This function is supported only for reading. - - gzrewind(file) is equivalent to (int)gzseek(file, 0L, SEEK_SET). -*/ - -z_off_t gztell(gzFile file); -/* - Return the starting position for the next gzread or gzwrite on file. - This position represents a number of bytes in the uncompressed data stream, - and is zero when starting, even if appending or reading a gzip stream from - the middle of a file using gzdopen(). - - gztell(file) is equivalent to gzseek(file, 0L, SEEK_CUR) -*/ - -z_off_t gzoffset(gzFile file); -/* - Return the current compressed (actual) read or write offset of file. This - offset includes the count of bytes that precede the gzip stream, for example - when appending or when using gzdopen() for reading. When reading, the - offset does not include as yet unused buffered input. This information can - be used for a progress indicator. On error, gzoffset() returns -1. -*/ - -int gzeof(gzFile file); -/* - Return true (1) if the end-of-file indicator for file has been set while - reading, false (0) otherwise. Note that the end-of-file indicator is set - only if the read tried to go past the end of the input, but came up short. - Therefore, just like feof(), gzeof() may return false even if there is no - more data to read, in the event that the last read request was for the exact - number of bytes remaining in the input file. This will happen if the input - file size is an exact multiple of the buffer size. - - If gzeof() returns true, then the read functions will return no more data, - unless the end-of-file indicator is reset by gzclearerr() and the input file - has grown since the previous end of file was detected. -*/ - -int gzdirect(gzFile file); -/* - Return true (1) if file is being copied directly while reading, or false - (0) if file is a gzip stream being decompressed. - - If the input file is empty, gzdirect() will return true, since the input - does not contain a gzip stream. - - If gzdirect() is used immediately after gzopen() or gzdopen() it will - cause buffers to be allocated to allow reading the file to determine if it - is a gzip file. Therefore if gzbuffer() is used, it should be called before - gzdirect(). - - When writing, gzdirect() returns true (1) if transparent writing was - requested ("wT" for the gzopen() mode), or false (0) otherwise. (Note: - gzdirect() is not needed when writing. Transparent writing must be - explicitly requested, so the application already knows the answer. When - linking statically, using gzdirect() will include all of the zlib code for - gzip file reading and decompression, which may not be desired.) -*/ - -int gzclose(gzFile file); -/* - Flush all pending output for file, if necessary, close file and - deallocate the (de)compression state. Note that once file is closed, you - cannot call gzerror with file, since its structures have been deallocated. - gzclose must not be called more than once on the same file, just as free - must not be called more than once on the same allocation. - - gzclose will return Z_STREAM_ERROR if file is not valid, Z_ERRNO on a - file operation error, Z_MEM_ERROR if out of memory, Z_BUF_ERROR if the - last read ended in the middle of a gzip stream, or Z_OK on success. -*/ - -int gzclose_r(gzFile file); -int gzclose_w(gzFile file); -/* - Same as gzclose(), but gzclose_r() is only for use when reading, and - gzclose_w() is only for use when writing or appending. The advantage to - using these instead of gzclose() is that they avoid linking in zlib - compression or decompression code that is not used when only reading or only - writing respectively. If gzclose() is used, then both compression and - decompression code will be included the application when linking to a static - zlib library. -*/ - -const(char)* gzerror(gzFile file, int* errnum); -/* - Return the error message for the last error which occurred on file. - errnum is set to zlib error number. If an error occurred in the file system - and not in the compression library, errnum is set to Z_ERRNO and the - application may consult errno to get the exact error code. - - The application must not modify the returned string. Future calls to - this function may invalidate the previously returned string. If file is - closed, then the string previously returned by gzerror will no longer be - available. - - gzerror() should be used to distinguish errors from end-of-file for those - functions above that do not distinguish those cases in their return values. -*/ - -void gzclearerr(gzFile file); -/* - Clear the error and end-of-file flags for file. This is analogous to the - clearerr() function in stdio. This is useful for continuing to read a gzip - file that is being written concurrently. -*/ - - /* checksum functions */ - -/* - These functions are not related to compression but are exported - anyway because they might be useful in applications using the compression - library. -*/ - -uint adler32(uint adler, const(ubyte)* buf, uint len); -/* - Update a running Adler-32 checksum with the bytes buf[0 .. len-1] and - return the updated checksum. An Adler-32 value is in the range of a 32-bit - unsigned integer. If buf is Z_NULL, this function returns the required - initial value for the checksum. - - An Adler-32 checksum is almost as reliable as a CRC-32 but can be computed - much faster. - - Usage example: - - uLong adler = adler32(0L, Z_NULL, 0); - - while (read_buffer(buffer, length) != EOF) - adler = adler32(adler, buffer, length); - - if (adler != original_adler) error(); -*/ - -uint adler32_z (uint adler, const(ubyte)* buf, z_size_t len); -/* - Same as adler32(), but with a size_t length. -*/ - -uint adler32_combine(uint adler1, uint adler2, z_off_t len2); -/* - Combine two Adler-32 checksums into one. For two sequences of bytes, seq1 - and seq2 with lengths len1 and len2, Adler-32 checksums were calculated for - each, adler1 and adler2. adler32_combine() returns the Adler-32 checksum of - seq1 and seq2 concatenated, requiring only adler1, adler2, and len2. Note - that the z_off_t type (like off_t) is a signed integer. If len2 is - negative, the result has no meaning or utility. -*/ - -uint crc32(uint crc, const(ubyte)* buf, uint len); -/* - Update a running CRC-32 with the bytes buf[0 .. len-1] and return the - updated CRC-32. A CRC-32 value is in the range of a 32-bit unsigned integer. - If buf is Z_NULL, this function returns the required initial value for the - crc. Pre- and post-conditioning (one's complement) is performed within this - function so it shouldn't be done by the application. - - Usage example: - - uLong crc = crc32(0L, Z_NULL, 0); - - while (read_buffer(buffer, length) != EOF) - crc = crc32(crc, buffer, length); - - if (crc != original_crc) error(); -*/ - -uint crc32_z(uint crc, const(ubyte)* buf, z_size_t len); -/* - Same as crc32(), but with a size_t length. -*/ - -uint crc32_combine(uint crc1, uint crc2, z_off_t len2); -/* - Combine two CRC-32 check values into one. For two sequences of bytes, - seq1 and seq2 with lengths len1 and len2, CRC-32 check values were - calculated for each, crc1 and crc2. crc32_combine() returns the CRC-32 - check value of seq1 and seq2 concatenated, requiring only crc1, crc2, and - len2. len2 must be non-negative. -*/ - -uint crc32_combine_gen(z_off_t len2); -/* - Return the operator corresponding to length len2, to be used with - crc32_combine_op(). len2 must be non-negative. -*/ - -uint crc32_combine_op(uint crc1, uint crc2, uint op); -/* - Give the same result as crc32_combine(), using op in place of len2. op is - is generated from len2 by crc32_combine_gen(). This will be faster than - crc32_combine() if the generated op is used more than once. -*/ - - /* various hacks, don't look :) */ - -/* deflateInit and inflateInit are macros to allow checking the zlib version - * and the compiler's view of z_stream: - */ -int deflateInit_(z_streamp strm, - int level, - const(char)* versionx, - int stream_size); - -int inflateInit_(z_streamp strm, - const(char)* versionx, - int stream_size); - -int deflateInit2_(z_streamp strm, - int level, - int method, - int windowBits, - int memLevel, - int strategy, - const(char)* versionx, - int stream_size); - -int inflateBackInit_(z_stream* strm, - int windowBits, - ubyte* window, - const(char)* z_version, - int stream_size); - -int inflateInit2_(z_streamp strm, - int windowBits, - const(char)* versionx, - int stream_size); - -const(char)* zError(int err); -int inflateSyncPoint(z_streamp z); -const(uint)* get_crc_table(); diff --git a/phobos/etc/c/zlib/ChangeLog b/phobos/etc/c/zlib/ChangeLog deleted file mode 100644 index b801a10..0000000 --- a/phobos/etc/c/zlib/ChangeLog +++ /dev/null @@ -1,1618 +0,0 @@ - - ChangeLog file for zlib - -Changes in 1.3.1 (22 Jan 2024) -- Reject overflows of zip header fields in minizip -- Fix bug in inflateSync() for data held in bit buffer -- Add LIT_MEM define to use more memory for a small deflate speedup -- Fix decision on the emission of Zip64 end records in minizip -- Add bounds checking to ERR_MSG() macro, used by zError() -- Neutralize zip file traversal attacks in miniunz -- Fix a bug in ZLIB_DEBUG compiles in check_match() -- Various portability and appearance improvements - -Changes in 1.3 (18 Aug 2023) -- Remove K&R function definitions and zlib2ansi -- Fix bug in deflateBound() for level 0 and memLevel 9 -- Fix bug when gzungetc() is used immediately after gzopen() -- Fix bug when using gzflush() with a very small buffer -- Fix crash when gzsetparams() attempted for transparent write -- Fix test/example.c to work with FORCE_STORED -- Rewrite of zran in examples (see zran.c version history) -- Fix minizip to allow it to open an empty zip file -- Fix reading disk number start on zip64 files in minizip -- Fix logic error in minizip argument processing -- Add minizip testing to Makefile -- Read multiple bytes instead of byte-by-byte in minizip unzip.c -- Add memory sanitizer to configure (--memory) -- Various portability improvements -- Various documentation improvements -- Various spelling and typo corrections - -Changes in 1.2.13 (13 Oct 2022) -- Fix configure issue that discarded provided CC definition -- Correct incorrect inputs provided to the CRC functions -- Repair prototypes and exporting of new CRC functions -- Fix inflateBack to detect invalid input with distances too far -- Have infback() deliver all of the available output up to any error -- Fix a bug when getting a gzip header extra field with inflate() -- Fix bug in block type selection when Z_FIXED used -- Tighten deflateBound bounds -- Remove deleted assembler code references -- Various portability and appearance improvements - -Changes in 1.2.12 (27 Mar 2022) -- Cygwin does not have _wopen(), so do not create gzopen_w() there -- Permit a deflateParams() parameter change as soon as possible -- Limit hash table inserts after switch from stored deflate -- Fix bug when window full in deflate_stored() -- Fix CLEAR_HASH macro to be usable as a single statement -- Avoid a conversion error in gzseek when off_t type too small -- Have Makefile return non-zero error code on test failure -- Avoid some conversion warnings in gzread.c and gzwrite.c -- Update use of errno for newer Windows CE versions -- Small speedup to inflate [psumbera] -- Return an error if the gzputs string length can't fit in an int -- Add address checking in clang to -w option of configure -- Don't compute check value for raw inflate if asked to validate -- Handle case where inflateSync used when header never processed -- Avoid the use of ptrdiff_t -- Avoid an undefined behavior of memcpy() in gzappend() -- Avoid undefined behaviors of memcpy() in gz*printf() -- Avoid an undefined behavior of memcpy() in _tr_stored_block() -- Make the names in functions declarations identical to definitions -- Remove old assembler code in which bugs have manifested -- Fix deflateEnd() to not report an error at start of raw deflate -- Add legal disclaimer to README -- Emphasize the need to continue decompressing gzip members -- Correct the initialization requirements for deflateInit2() -- Fix a bug that can crash deflate on some input when using Z_FIXED -- Assure that the number of bits for deflatePrime() is valid -- Use a structure to make globals in enough.c evident -- Use a macro for the printf format of big_t in enough.c -- Clean up code style in enough.c, update version -- Use inline function instead of macro for index in enough.c -- Clarify that prefix codes are counted in enough.c -- Show all the codes for the maximum tables size in enough.c -- Add gznorm.c example, which normalizes gzip files -- Fix the zran.c example to work on a multiple-member gzip file -- Add tables for crc32_combine(), to speed it up by a factor of 200 -- Add crc32_combine_gen() and crc32_combine_op() for fast combines -- Speed up software CRC-32 computation by a factor of 1.5 to 3 -- Use atomic test and set, if available, for dynamic CRC tables -- Don't bother computing check value after successful inflateSync() -- Correct comment in crc32.c -- Add use of the ARMv8 crc32 instructions when requested -- Use ARM crc32 instructions if the ARM architecture has them -- Explicitly note that the 32-bit check values are 32 bits -- Avoid adding empty gzip member after gzflush with Z_FINISH -- Fix memory leak on error in gzlog.c -- Fix error in comment on the polynomial representation of a byte -- Clarify gz* function interfaces, referring to parameter names -- Change macro name in inflate.c to avoid collision in VxWorks -- Correct typo in blast.c -- Improve portability of contrib/minizip -- Fix indentation in minizip's zip.c -- Replace black/white with allow/block. (theresa-m) -- minizip warning fix if MAXU32 already defined. (gvollant) -- Fix unztell64() in minizip to work past 4GB. (Daniël Hörchner) -- Clean up minizip to reduce warnings for testing -- Add fallthrough comments for gcc -- Eliminate use of ULL constants -- Separate out address sanitizing from warnings in configure -- Remove destructive aspects of make distclean -- Check for cc masquerading as gcc or clang in configure -- Fix crc32.c to compile local functions only if used - -Changes in 1.2.11 (15 Jan 2017) -- Fix deflate stored bug when pulling last block from window -- Permit immediate deflateParams changes before any deflate input - -Changes in 1.2.10 (2 Jan 2017) -- Avoid warnings on snprintf() return value -- Fix bug in deflate_stored() for zero-length input -- Fix bug in gzwrite.c that produced corrupt gzip files -- Remove files to be installed before copying them in Makefile.in -- Add warnings when compiling with assembler code - -Changes in 1.2.9 (31 Dec 2016) -- Fix contrib/minizip to permit unzipping with desktop API [Zouzou] -- Improve contrib/blast to return unused bytes -- Assure that gzoffset() is correct when appending -- Improve compress() and uncompress() to support large lengths -- Fix bug in test/example.c where error code not saved -- Remedy Coverity warning [Randers-Pehrson] -- Improve speed of gzprintf() in transparent mode -- Fix inflateInit2() bug when windowBits is 16 or 32 -- Change DEBUG macro to ZLIB_DEBUG -- Avoid uninitialized access by gzclose_w() -- Allow building zlib outside of the source directory -- Fix bug that accepted invalid zlib header when windowBits is zero -- Fix gzseek() problem on MinGW due to buggy _lseeki64 there -- Loop on write() calls in gzwrite.c in case of non-blocking I/O -- Add --warn (-w) option to ./configure for more compiler warnings -- Reject a window size of 256 bytes if not using the zlib wrapper -- Fix bug when level 0 used with Z_HUFFMAN or Z_RLE -- Add --debug (-d) option to ./configure to define ZLIB_DEBUG -- Fix bugs in creating a very large gzip header -- Add uncompress2() function, which returns the input size used -- Assure that deflateParams() will not switch functions mid-block -- Dramatically speed up deflation for level 0 (storing) -- Add gzfread(), duplicating the interface of fread() -- Add gzfwrite(), duplicating the interface of fwrite() -- Add deflateGetDictionary() function -- Use snprintf() for later versions of Microsoft C -- Fix *Init macros to use z_ prefix when requested -- Replace as400 with os400 for OS/400 support [Monnerat] -- Add crc32_z() and adler32_z() functions with size_t lengths -- Update Visual Studio project files [AraHaan] - -Changes in 1.2.8 (28 Apr 2013) -- Update contrib/minizip/iowin32.c for Windows RT [Vollant] -- Do not force Z_CONST for C++ -- Clean up contrib/vstudio [Roß] -- Correct spelling error in zlib.h -- Fix mixed line endings in contrib/vstudio - -Changes in 1.2.7.3 (13 Apr 2013) -- Fix version numbers and DLL names in contrib/vstudio/*/zlib.rc - -Changes in 1.2.7.2 (13 Apr 2013) -- Change check for a four-byte type back to hexadecimal -- Fix typo in win32/Makefile.msc -- Add casts in gzwrite.c for pointer differences - -Changes in 1.2.7.1 (24 Mar 2013) -- Replace use of unsafe string functions with snprintf if available -- Avoid including stddef.h on Windows for Z_SOLO compile [Niessink] -- Fix gzgetc undefine when Z_PREFIX set [Turk] -- Eliminate use of mktemp in Makefile (not always available) -- Fix bug in 'F' mode for gzopen() -- Add inflateGetDictionary() function -- Correct comment in deflate.h -- Use _snprintf for snprintf in Microsoft C -- On Darwin, only use /usr/bin/libtool if libtool is not Apple -- Delete "--version" file if created by "ar --version" [Richard G.] -- Fix configure check for veracity of compiler error return codes -- Fix CMake compilation of static lib for MSVC2010 x64 -- Remove unused variable in infback9.c -- Fix argument checks in gzlog_compress() and gzlog_write() -- Clean up the usage of z_const and respect const usage within zlib -- Clean up examples/gzlog.[ch] comparisons of different types -- Avoid shift equal to bits in type (caused endless loop) -- Fix uninitialized value bug in gzputc() introduced by const patches -- Fix memory allocation error in examples/zran.c [Nor] -- Fix bug where gzopen(), gzclose() would write an empty file -- Fix bug in gzclose() when gzwrite() runs out of memory -- Check for input buffer malloc failure in examples/gzappend.c -- Add note to contrib/blast to use binary mode in stdio -- Fix comparisons of differently signed integers in contrib/blast -- Check for invalid code length codes in contrib/puff -- Fix serious but very rare decompression bug in inftrees.c -- Update inflateBack() comments, since inflate() can be faster -- Use underscored I/O function names for WINAPI_FAMILY -- Add _tr_flush_bits to the external symbols prefixed by --zprefix -- Add contrib/vstudio/vc10 pre-build step for static only -- Quote --version-script argument in CMakeLists.txt -- Don't specify --version-script on Apple platforms in CMakeLists.txt -- Fix casting error in contrib/testzlib/testzlib.c -- Fix types in contrib/minizip to match result of get_crc_table() -- Simplify contrib/vstudio/vc10 with 'd' suffix -- Add TOP support to win32/Makefile.msc -- Support i686 and amd64 assembler builds in CMakeLists.txt -- Fix typos in the use of _LARGEFILE64_SOURCE in zconf.h -- Add vc11 and vc12 build files to contrib/vstudio -- Add gzvprintf() as an undocumented function in zlib -- Fix configure for Sun shell -- Remove runtime check in configure for four-byte integer type -- Add casts and consts to ease user conversion to C++ -- Add man pages for minizip and miniunzip -- In Makefile uninstall, don't rm if preceding cd fails -- Do not return Z_BUF_ERROR if deflateParam() has nothing to write - -Changes in 1.2.7 (2 May 2012) -- Replace use of memmove() with a simple copy for portability -- Test for existence of strerror -- Restore gzgetc_ for backward compatibility with 1.2.6 -- Fix build with non-GNU make on Solaris -- Require gcc 4.0 or later on Mac OS X to use the hidden attribute -- Include unistd.h for Watcom C -- Use __WATCOMC__ instead of __WATCOM__ -- Do not use the visibility attribute if NO_VIZ defined -- Improve the detection of no hidden visibility attribute -- Avoid using __int64 for gcc or solo compilation -- Cast to char * in gzprintf to avoid warnings [Zinser] -- Fix make_vms.com for VAX [Zinser] -- Don't use library or built-in byte swaps -- Simplify test and use of gcc hidden attribute -- Fix bug in gzclose_w() when gzwrite() fails to allocate memory -- Add "x" (O_EXCL) and "e" (O_CLOEXEC) modes support to gzopen() -- Fix bug in test/minigzip.c for configure --solo -- Fix contrib/vstudio project link errors [Mohanathas] -- Add ability to choose the builder in make_vms.com [Schweda] -- Add DESTDIR support to mingw32 win32/Makefile.gcc -- Fix comments in win32/Makefile.gcc for proper usage -- Allow overriding the default install locations for cmake -- Generate and install the pkg-config file with cmake -- Build both a static and a shared version of zlib with cmake -- Include version symbols for cmake builds -- If using cmake with MSVC, add the source directory to the includes -- Remove unneeded EXTRA_CFLAGS from win32/Makefile.gcc [Truta] -- Move obsolete emx makefile to old [Truta] -- Allow the use of -Wundef when compiling or using zlib -- Avoid the use of the -u option with mktemp -- Improve inflate() documentation on the use of Z_FINISH -- Recognize clang as gcc -- Add gzopen_w() in Windows for wide character path names -- Rename zconf.h in CMakeLists.txt to move it out of the way -- Add source directory in CMakeLists.txt for building examples -- Look in build directory for zlib.pc in CMakeLists.txt -- Remove gzflags from zlibvc.def in vc9 and vc10 -- Fix contrib/minizip compilation in the MinGW environment -- Update ./configure for Solaris, support --64 [Mooney] -- Remove -R. from Solaris shared build (possible security issue) -- Avoid race condition for parallel make (-j) running example -- Fix type mismatch between get_crc_table() and crc_table -- Fix parsing of version with "-" in CMakeLists.txt [Snider, Ziegler] -- Fix the path to zlib.map in CMakeLists.txt -- Force the native libtool in Mac OS X to avoid GNU libtool [Beebe] -- Add instructions to win32/Makefile.gcc for shared install [Torri] - -Changes in 1.2.6.1 (12 Feb 2012) -- Avoid the use of the Objective-C reserved name "id" -- Include io.h in gzguts.h for Microsoft compilers -- Fix problem with ./configure --prefix and gzgetc macro -- Include gz_header definition when compiling zlib solo -- Put gzflags() functionality back in zutil.c -- Avoid library header include in crc32.c for Z_SOLO -- Use name in GCC_CLASSIC as C compiler for coverage testing, if set -- Minor cleanup in contrib/minizip/zip.c [Vollant] -- Update make_vms.com [Zinser] -- Remove unnecessary gzgetc_ function -- Use optimized byte swap operations for Microsoft and GNU [Snyder] -- Fix minor typo in zlib.h comments [Rzesniowiecki] - -Changes in 1.2.6 (29 Jan 2012) -- Update the Pascal interface in contrib/pascal -- Fix function numbers for gzgetc_ in zlibvc.def files -- Fix configure.ac for contrib/minizip [Schiffer] -- Fix large-entry detection in minizip on 64-bit systems [Schiffer] -- Have ./configure use the compiler return code for error indication -- Fix CMakeLists.txt for cross compilation [McClure] -- Fix contrib/minizip/zip.c for 64-bit architectures [Dalsnes] -- Fix compilation of contrib/minizip on FreeBSD [Marquez] -- Correct suggested usages in win32/Makefile.msc [Shachar, Horvath] -- Include io.h for Turbo C / Borland C on all platforms [Truta] -- Make version explicit in contrib/minizip/configure.ac [Bosmans] -- Avoid warning for no encryption in contrib/minizip/zip.c [Vollant] -- Minor cleanup up contrib/minizip/unzip.c [Vollant] -- Fix bug when compiling minizip with C++ [Vollant] -- Protect for long name and extra fields in contrib/minizip [Vollant] -- Avoid some warnings in contrib/minizip [Vollant] -- Add -I../.. -L../.. to CFLAGS for minizip and miniunzip -- Add missing libs to minizip linker command -- Add support for VPATH builds in contrib/minizip -- Add an --enable-demos option to contrib/minizip/configure -- Add the generation of configure.log by ./configure -- Exit when required parameters not provided to win32/Makefile.gcc -- Have gzputc return the character written instead of the argument -- Use the -m option on ldconfig for BSD systems [Tobias] -- Correct in zlib.map when deflateResetKeep was added - -Changes in 1.2.5.3 (15 Jan 2012) -- Restore gzgetc function for binary compatibility -- Do not use _lseeki64 under Borland C++ [Truta] -- Update win32/Makefile.msc to build test/*.c [Truta] -- Remove old/visualc6 given CMakefile and other alternatives -- Update AS400 build files and documentation [Monnerat] -- Update win32/Makefile.gcc to build test/*.c [Truta] -- Permit stronger flushes after Z_BLOCK flushes -- Avoid extraneous empty blocks when doing empty flushes -- Permit Z_NULL arguments to deflatePending -- Allow deflatePrime() to insert bits in the middle of a stream -- Remove second empty static block for Z_PARTIAL_FLUSH -- Write out all of the available bits when using Z_BLOCK -- Insert the first two strings in the hash table after a flush - -Changes in 1.2.5.2 (17 Dec 2011) -- fix ld error: unable to find version dependency 'ZLIB_1.2.5' -- use relative symlinks for shared libs -- Avoid searching past window for Z_RLE strategy -- Assure that high-water mark initialization is always applied in deflate -- Add assertions to fill_window() in deflate.c to match comments -- Update python link in README -- Correct spelling error in gzread.c -- Fix bug in gzgets() for a concatenated empty gzip stream -- Correct error in comment for gz_make() -- Change gzread() and related to ignore junk after gzip streams -- Allow gzread() and related to continue after gzclearerr() -- Allow gzrewind() and gzseek() after a premature end-of-file -- Simplify gzseek() now that raw after gzip is ignored -- Change gzgetc() to a macro for speed (~40% speedup in testing) -- Fix gzclose() to return the actual error last encountered -- Always add large file support for windows -- Include zconf.h for windows large file support -- Include zconf.h.cmakein for windows large file support -- Update zconf.h.cmakein on make distclean -- Merge vestigial vsnprintf determination from zutil.h to gzguts.h -- Clarify how gzopen() appends in zlib.h comments -- Correct documentation of gzdirect() since junk at end now ignored -- Add a transparent write mode to gzopen() when 'T' is in the mode -- Update python link in zlib man page -- Get inffixed.h and MAKEFIXED result to match -- Add a ./config --solo option to make zlib subset with no library use -- Add undocumented inflateResetKeep() function for CAB file decoding -- Add --cover option to ./configure for gcc coverage testing -- Add #define ZLIB_CONST option to use const in the z_stream interface -- Add comment to gzdopen() in zlib.h to use dup() when using fileno() -- Note behavior of uncompress() to provide as much data as it can -- Add files in contrib/minizip to aid in building libminizip -- Split off AR options in Makefile.in and configure -- Change ON macro to Z_ARG to avoid application conflicts -- Facilitate compilation with Borland C++ for pragmas and vsnprintf -- Include io.h for Turbo C / Borland C++ -- Move example.c and minigzip.c to test/ -- Simplify incomplete code table filling in inflate_table() -- Remove code from inflate.c and infback.c that is impossible to execute -- Test the inflate code with full coverage -- Allow deflateSetDictionary, inflateSetDictionary at any time (in raw) -- Add deflateResetKeep and fix inflateResetKeep to retain dictionary -- Fix gzwrite.c to accommodate reduced memory zlib compilation -- Have inflate() with Z_FINISH avoid the allocation of a window -- Do not set strm->adler when doing raw inflate -- Fix gzeof() to behave just like feof() when read is not past end of file -- Fix bug in gzread.c when end-of-file is reached -- Avoid use of Z_BUF_ERROR in gz* functions except for premature EOF -- Document gzread() capability to read concurrently written files -- Remove hard-coding of resource compiler in CMakeLists.txt [Blammo] - -Changes in 1.2.5.1 (10 Sep 2011) -- Update FAQ entry on shared builds (#13) -- Avoid symbolic argument to chmod in Makefile.in -- Fix bug and add consts in contrib/puff [Oberhumer] -- Update contrib/puff/zeros.raw test file to have all block types -- Add full coverage test for puff in contrib/puff/Makefile -- Fix static-only-build install in Makefile.in -- Fix bug in unzGetCurrentFileInfo() in contrib/minizip [Kuno] -- Add libz.a dependency to shared in Makefile.in for parallel builds -- Spell out "number" (instead of "nb") in zlib.h for total_in, total_out -- Replace $(...) with `...` in configure for non-bash sh [Bowler] -- Add darwin* to Darwin* and solaris* to SunOS\ 5* in configure [Groffen] -- Add solaris* to Linux* in configure to allow gcc use [Groffen] -- Add *bsd* to Linux* case in configure [Bar-Lev] -- Add inffast.obj to dependencies in win32/Makefile.msc -- Correct spelling error in deflate.h [Kohler] -- Change libzdll.a again to libz.dll.a (!) in win32/Makefile.gcc -- Add test to configure for GNU C looking for gcc in output of $cc -v -- Add zlib.pc generation to win32/Makefile.gcc [Weigelt] -- Fix bug in zlib.h for _FILE_OFFSET_BITS set and _LARGEFILE64_SOURCE not -- Add comment in zlib.h that adler32_combine with len2 < 0 makes no sense -- Make NO_DIVIDE option in adler32.c much faster (thanks to John Reiser) -- Make stronger test in zconf.h to include unistd.h for LFS -- Apply Darwin patches for 64-bit file offsets to contrib/minizip [Slack] -- Fix zlib.h LFS support when Z_PREFIX used -- Add updated as400 support (removed from old) [Monnerat] -- Avoid deflate sensitivity to volatile input data -- Avoid division in adler32_combine for NO_DIVIDE -- Clarify the use of Z_FINISH with deflateBound() amount of space -- Set binary for output file in puff.c -- Use u4 type for crc_table to avoid conversion warnings -- Apply casts in zlib.h to avoid conversion warnings -- Add OF to prototypes for adler32_combine_ and crc32_combine_ [Miller] -- Improve inflateSync() documentation to note indeterminacy -- Add deflatePending() function to return the amount of pending output -- Correct the spelling of "specification" in FAQ [Randers-Pehrson] -- Add a check in configure for stdarg.h, use for gzprintf() -- Check that pointers fit in ints when gzprint() compiled old style -- Add dummy name before $(SHAREDLIBV) in Makefile [Bar-Lev, Bowler] -- Delete line in configure that adds -L. libz.a to LDFLAGS [Weigelt] -- Add debug records in assembler code [Londer] -- Update RFC references to use http://tools.ietf.org/html/... [Li] -- Add --archs option, use of libtool to configure for Mac OS X [Borstel] - -Changes in 1.2.5 (19 Apr 2010) -- Disable visibility attribute in win32/Makefile.gcc [Bar-Lev] -- Default to libdir as sharedlibdir in configure [Nieder] -- Update copyright dates on modified source files -- Update trees.c to be able to generate modified trees.h -- Exit configure for MinGW, suggesting win32/Makefile.gcc -- Check for NULL path in gz_open [Homurlu] - -Changes in 1.2.4.5 (18 Apr 2010) -- Set sharedlibdir in configure [Torok] -- Set LDFLAGS in Makefile.in [Bar-Lev] -- Avoid mkdir objs race condition in Makefile.in [Bowler] -- Add ZLIB_INTERNAL in front of internal inter-module functions and arrays -- Define ZLIB_INTERNAL to hide internal functions and arrays for GNU C -- Don't use hidden attribute when it is a warning generator (e.g. Solaris) - -Changes in 1.2.4.4 (18 Apr 2010) -- Fix CROSS_PREFIX executable testing, CHOST extract, mingw* [Torok] -- Undefine _LARGEFILE64_SOURCE in zconf.h if it is zero, but not if empty -- Try to use bash or ksh regardless of functionality of /bin/sh -- Fix configure incompatibility with NetBSD sh -- Remove attempt to run under bash or ksh since have better NetBSD fix -- Fix win32/Makefile.gcc for MinGW [Bar-Lev] -- Add diagnostic messages when using CROSS_PREFIX in configure -- Added --sharedlibdir option to configure [Weigelt] -- Use hidden visibility attribute when available [Frysinger] - -Changes in 1.2.4.3 (10 Apr 2010) -- Only use CROSS_PREFIX in configure for ar and ranlib if they exist -- Use CROSS_PREFIX for nm [Bar-Lev] -- Assume _LARGEFILE64_SOURCE defined is equivalent to true -- Avoid use of undefined symbols in #if with && and || -- Make *64 prototypes in gzguts.h consistent with functions -- Add -shared load option for MinGW in configure [Bowler] -- Move z_off64_t to public interface, use instead of off64_t -- Remove ! from shell test in configure (not portable to Solaris) -- Change +0 macro tests to -0 for possibly increased portability - -Changes in 1.2.4.2 (9 Apr 2010) -- Add consistent carriage returns to readme.txt's in masmx86 and masmx64 -- Really provide prototypes for *64 functions when building without LFS -- Only define unlink() in minigzip.c if unistd.h not included -- Update README to point to contrib/vstudio project files -- Move projects/vc6 to old/ and remove projects/ -- Include stdlib.h in minigzip.c for setmode() definition under WinCE -- Clean up assembler builds in win32/Makefile.msc [Rowe] -- Include sys/types.h for Microsoft for off_t definition -- Fix memory leak on error in gz_open() -- Symbolize nm as $NM in configure [Weigelt] -- Use TEST_LDSHARED instead of LDSHARED to link test programs [Weigelt] -- Add +0 to _FILE_OFFSET_BITS and _LFS64_LARGEFILE in case not defined -- Fix bug in gzeof() to take into account unused input data -- Avoid initialization of structures with variables in puff.c -- Updated win32/README-WIN32.txt [Rowe] - -Changes in 1.2.4.1 (28 Mar 2010) -- Remove the use of [a-z] constructs for sed in configure [gentoo 310225] -- Remove $(SHAREDLIB) from LIBS in Makefile.in [Creech] -- Restore "for debugging" comment on sprintf() in gzlib.c -- Remove fdopen for MVS from gzguts.h -- Put new README-WIN32.txt in win32 [Rowe] -- Add check for shell to configure and invoke another shell if needed -- Fix big fat stinking bug in gzseek() on uncompressed files -- Remove vestigial F_OPEN64 define in zutil.h -- Set and check the value of _LARGEFILE_SOURCE and _LARGEFILE64_SOURCE -- Avoid errors on non-LFS systems when applications define LFS macros -- Set EXE to ".exe" in configure for MINGW [Kahle] -- Match crc32() in crc32.c exactly to the prototype in zlib.h [Sherrill] -- Add prefix for cross-compilation in win32/makefile.gcc [Bar-Lev] -- Add DLL install in win32/makefile.gcc [Bar-Lev] -- Allow Linux* or linux* from uname in configure [Bar-Lev] -- Allow ldconfig to be redefined in configure and Makefile.in [Bar-Lev] -- Add cross-compilation prefixes to configure [Bar-Lev] -- Match type exactly in gz_load() invocation in gzread.c -- Match type exactly of zcalloc() in zutil.c to zlib.h alloc_func -- Provide prototypes for *64 functions when building zlib without LFS -- Don't use -lc when linking shared library on MinGW -- Remove errno.h check in configure and vestigial errno code in zutil.h - -Changes in 1.2.4 (14 Mar 2010) -- Fix VER3 extraction in configure for no fourth subversion -- Update zlib.3, add docs to Makefile.in to make .pdf out of it -- Add zlib.3.pdf to distribution -- Don't set error code in gzerror() if passed pointer is NULL -- Apply destination directory fixes to CMakeLists.txt [Lowman] -- Move #cmakedefine's to a new zconf.in.cmakein -- Restore zconf.h for builds that don't use configure or cmake -- Add distclean to dummy Makefile for convenience -- Update and improve INDEX, README, and FAQ -- Update CMakeLists.txt for the return of zconf.h [Lowman] -- Update contrib/vstudio/vc9 and vc10 [Vollant] -- Change libz.dll.a back to libzdll.a in win32/Makefile.gcc -- Apply license and readme changes to contrib/asm686 [Raiter] -- Check file name lengths and add -c option in minigzip.c [Li] -- Update contrib/amd64 and contrib/masmx86/ [Vollant] -- Avoid use of "eof" parameter in trees.c to not shadow library variable -- Update make_vms.com for removal of zlibdefs.h [Zinser] -- Update assembler code and vstudio projects in contrib [Vollant] -- Remove outdated assembler code contrib/masm686 and contrib/asm586 -- Remove old vc7 and vc8 from contrib/vstudio -- Update win32/Makefile.msc, add ZLIB_VER_SUBREVISION [Rowe] -- Fix memory leaks in gzclose_r() and gzclose_w(), file leak in gz_open() -- Add contrib/gcc_gvmat64 for longest_match and inflate_fast [Vollant] -- Remove *64 functions from win32/zlib.def (they're not 64-bit yet) -- Fix bug in void-returning vsprintf() case in gzwrite.c -- Fix name change from inflate.h in contrib/inflate86/inffas86.c -- Check if temporary file exists before removing in make_vms.com [Zinser] -- Fix make install and uninstall for --static option -- Fix usage of _MSC_VER in gzguts.h and zutil.h [Truta] -- Update readme.txt in contrib/masmx64 and masmx86 to assemble - -Changes in 1.2.3.9 (21 Feb 2010) -- Expunge gzio.c -- Move as400 build information to old -- Fix updates in contrib/minizip and contrib/vstudio -- Add const to vsnprintf test in configure to avoid warnings [Weigelt] -- Delete zconf.h (made by configure) [Weigelt] -- Change zconf.in.h to zconf.h.in per convention [Weigelt] -- Check for NULL buf in gzgets() -- Return empty string for gzgets() with len == 1 (like fgets()) -- Fix description of gzgets() in zlib.h for end-of-file, NULL return -- Update minizip to 1.1 [Vollant] -- Avoid MSVC loss of data warnings in gzread.c, gzwrite.c -- Note in zlib.h that gzerror() should be used to distinguish from EOF -- Remove use of snprintf() from gzlib.c -- Fix bug in gzseek() -- Update contrib/vstudio, adding vc9 and vc10 [Kuno, Vollant] -- Fix zconf.h generation in CMakeLists.txt [Lowman] -- Improve comments in zconf.h where modified by configure - -Changes in 1.2.3.8 (13 Feb 2010) -- Clean up text files (tabs, trailing whitespace, etc.) [Oberhumer] -- Use z_off64_t in gz_zero() and gz_skip() to match state->skip -- Avoid comparison problem when sizeof(int) == sizeof(z_off64_t) -- Revert to Makefile.in from 1.2.3.6 (live with the clutter) -- Fix missing error return in gzflush(), add zlib.h note -- Add *64 functions to zlib.map [Levin] -- Fix signed/unsigned comparison in gz_comp() -- Use SFLAGS when testing shared linking in configure -- Add --64 option to ./configure to use -m64 with gcc -- Fix ./configure --help to correctly name options -- Have make fail if a test fails [Levin] -- Avoid buffer overrun in contrib/masmx64/gvmat64.asm [Simpson] -- Remove assembler object files from contrib - -Changes in 1.2.3.7 (24 Jan 2010) -- Always gzopen() with O_LARGEFILE if available -- Fix gzdirect() to work immediately after gzopen() or gzdopen() -- Make gzdirect() more precise when the state changes while reading -- Improve zlib.h documentation in many places -- Catch memory allocation failure in gz_open() -- Complete close operation if seek forward in gzclose_w() fails -- Return Z_ERRNO from gzclose_r() if close() fails -- Return Z_STREAM_ERROR instead of EOF for gzclose() being passed NULL -- Return zero for gzwrite() errors to match zlib.h description -- Return -1 on gzputs() error to match zlib.h description -- Add zconf.in.h to allow recovery from configure modification [Weigelt] -- Fix static library permissions in Makefile.in [Weigelt] -- Avoid warnings in configure tests that hide functionality [Weigelt] -- Add *BSD and DragonFly to Linux case in configure [gentoo 123571] -- Change libzdll.a to libz.dll.a in win32/Makefile.gcc [gentoo 288212] -- Avoid access of uninitialized data for first inflateReset2 call [Gomes] -- Keep object files in subdirectories to reduce the clutter somewhat -- Remove default Makefile and zlibdefs.h, add dummy Makefile -- Add new external functions to Z_PREFIX, remove duplicates, z_z_ -> z_ -- Remove zlibdefs.h completely -- modify zconf.h instead - -Changes in 1.2.3.6 (17 Jan 2010) -- Avoid void * arithmetic in gzread.c and gzwrite.c -- Make compilers happier with const char * for gz_error message -- Avoid unused parameter warning in inflate.c -- Avoid signed-unsigned comparison warning in inflate.c -- Indent #pragma's for traditional C -- Fix usage of strwinerror() in glib.c, change to gz_strwinerror() -- Correct email address in configure for system options -- Update make_vms.com and add make_vms.com to contrib/minizip [Zinser] -- Update zlib.map [Brown] -- Fix Makefile.in for Solaris 10 make of example64 and minizip64 [Torok] -- Apply various fixes to CMakeLists.txt [Lowman] -- Add checks on len in gzread() and gzwrite() -- Add error message for no more room for gzungetc() -- Remove zlib version check in gzwrite() -- Defer compression of gzprintf() result until need to -- Use snprintf() in gzdopen() if available -- Remove USE_MMAP configuration determination (only used by minigzip) -- Remove examples/pigz.c (available separately) -- Update examples/gun.c to 1.6 - -Changes in 1.2.3.5 (8 Jan 2010) -- Add space after #if in zutil.h for some compilers -- Fix relatively harmless bug in deflate_fast() [Exarevsky] -- Fix same problem in deflate_slow() -- Add $(SHAREDLIBV) to LIBS in Makefile.in [Brown] -- Add deflate_rle() for faster Z_RLE strategy run-length encoding -- Add deflate_huff() for faster Z_HUFFMAN_ONLY encoding -- Change name of "write" variable in inffast.c to avoid library collisions -- Fix premature EOF from gzread() in gzio.c [Brown] -- Use zlib header window size if windowBits is 0 in inflateInit2() -- Remove compressBound() call in deflate.c to avoid linking compress.o -- Replace use of errno in gz* with functions, support WinCE [Alves] -- Provide alternative to perror() in minigzip.c for WinCE [Alves] -- Don't use _vsnprintf on later versions of MSVC [Lowman] -- Add CMake build script and input file [Lowman] -- Update contrib/minizip to 1.1 [Svensson, Vollant] -- Moved nintendods directory from contrib to root -- Replace gzio.c with a new set of routines with the same functionality -- Add gzbuffer(), gzoffset(), gzclose_r(), gzclose_w() as part of above -- Update contrib/minizip to 1.1b -- Change gzeof() to return 0 on error instead of -1 to agree with zlib.h - -Changes in 1.2.3.4 (21 Dec 2009) -- Use old school .SUFFIXES in Makefile.in for FreeBSD compatibility -- Update comments in configure and Makefile.in for default --shared -- Fix test -z's in configure [Marquess] -- Build examplesh and minigzipsh when not testing -- Change NULL's to Z_NULL's in deflate.c and in comments in zlib.h -- Import LDFLAGS from the environment in configure -- Fix configure to populate SFLAGS with discovered CFLAGS options -- Adapt make_vms.com to the new Makefile.in [Zinser] -- Add zlib2ansi script for C++ compilation [Marquess] -- Add _FILE_OFFSET_BITS=64 test to make test (when applicable) -- Add AMD64 assembler code for longest match to contrib [Teterin] -- Include options from $SFLAGS when doing $LDSHARED -- Simplify 64-bit file support by introducing z_off64_t type -- Make shared object files in objs directory to work around old Sun cc -- Use only three-part version number for Darwin shared compiles -- Add rc option to ar in Makefile.in for when ./configure not run -- Add -WI,-rpath,. to LDFLAGS for OSF 1 V4* -- Set LD_LIBRARYN32_PATH for SGI IRIX shared compile -- Protect against _FILE_OFFSET_BITS being defined when compiling zlib -- Rename Makefile.in targets allstatic to static and allshared to shared -- Fix static and shared Makefile.in targets to be independent -- Correct error return bug in gz_open() by setting state [Brown] -- Put spaces before ;;'s in configure for better sh compatibility -- Add pigz.c (parallel implementation of gzip) to examples/ -- Correct constant in crc32.c to UL [Leventhal] -- Reject negative lengths in crc32_combine() -- Add inflateReset2() function to work like inflateEnd()/inflateInit2() -- Include sys/types.h for _LARGEFILE64_SOURCE [Brown] -- Correct typo in doc/algorithm.txt [Janik] -- Fix bug in adler32_combine() [Zhu] -- Catch missing-end-of-block-code error in all inflates and in puff - Assures that random input to inflate eventually results in an error -- Added enough.c (calculation of ENOUGH for inftrees.h) to examples/ -- Update ENOUGH and its usage to reflect discovered bounds -- Fix gzerror() error report on empty input file [Brown] -- Add ush casts in trees.c to avoid pedantic runtime errors -- Fix typo in zlib.h uncompress() description [Reiss] -- Correct inflate() comments with regard to automatic header detection -- Remove deprecation comment on Z_PARTIAL_FLUSH (it stays) -- Put new version of gzlog (2.0) in examples with interruption recovery -- Add puff compile option to permit invalid distance-too-far streams -- Add puff TEST command options, ability to read piped input -- Prototype the *64 functions in zlib.h when _FILE_OFFSET_BITS == 64, but - _LARGEFILE64_SOURCE not defined -- Fix Z_FULL_FLUSH to truly erase the past by resetting s->strstart -- Fix deflateSetDictionary() to use all 32K for output consistency -- Remove extraneous #define MIN_LOOKAHEAD in deflate.c (in deflate.h) -- Clear bytes after deflate lookahead to avoid use of uninitialized data -- Change a limit in inftrees.c to be more transparent to Coverity Prevent -- Update win32/zlib.def with exported symbols from zlib.h -- Correct spelling errors in zlib.h [Willem, Sobrado] -- Allow Z_BLOCK for deflate() to force a new block -- Allow negative bits in inflatePrime() to delete existing bit buffer -- Add Z_TREES flush option to inflate() to return at end of trees -- Add inflateMark() to return current state information for random access -- Add Makefile for NintendoDS to contrib [Costa] -- Add -w in configure compile tests to avoid spurious warnings [Beucler] -- Fix typos in zlib.h comments for deflateSetDictionary() -- Fix EOF detection in transparent gzread() [Maier] - -Changes in 1.2.3.3 (2 October 2006) -- Make --shared the default for configure, add a --static option -- Add compile option to permit invalid distance-too-far streams -- Add inflateUndermine() function which is required to enable above -- Remove use of "this" variable name for C++ compatibility [Marquess] -- Add testing of shared library in make test, if shared library built -- Use ftello() and fseeko() if available instead of ftell() and fseek() -- Provide two versions of all functions that use the z_off_t type for - binary compatibility -- a normal version and a 64-bit offset version, - per the Large File Support Extension when _LARGEFILE64_SOURCE is - defined; use the 64-bit versions by default when _FILE_OFFSET_BITS - is defined to be 64 -- Add a --uname= option to configure to perhaps help with cross-compiling - -Changes in 1.2.3.2 (3 September 2006) -- Turn off silly Borland warnings [Hay] -- Use off64_t and define _LARGEFILE64_SOURCE when present -- Fix missing dependency on inffixed.h in Makefile.in -- Rig configure --shared to build both shared and static [Teredesai, Truta] -- Remove zconf.in.h and instead create a new zlibdefs.h file -- Fix contrib/minizip/unzip.c non-encrypted after encrypted [Vollant] -- Add treebuild.xml (see http://treebuild.metux.de/) [Weigelt] - -Changes in 1.2.3.1 (16 August 2006) -- Add watcom directory with OpenWatcom make files [Daniel] -- Remove #undef of FAR in zconf.in.h for MVS [Fedtke] -- Update make_vms.com [Zinser] -- Use -fPIC for shared build in configure [Teredesai, Nicholson] -- Use only major version number for libz.so on IRIX and OSF1 [Reinholdtsen] -- Use fdopen() (not _fdopen()) for Interix in zutil.h [Bäck] -- Add some FAQ entries about the contrib directory -- Update the MVS question in the FAQ -- Avoid extraneous reads after EOF in gzio.c [Brown] -- Correct spelling of "successfully" in gzio.c [Randers-Pehrson] -- Add comments to zlib.h about gzerror() usage [Brown] -- Set extra flags in gzip header in gzopen() like deflate() does -- Make configure options more compatible with double-dash conventions - [Weigelt] -- Clean up compilation under Solaris SunStudio cc [Rowe, Reinholdtsen] -- Fix uninstall target in Makefile.in [Truta] -- Add pkgconfig support [Weigelt] -- Use $(DESTDIR) macro in Makefile.in [Reinholdtsen, Weigelt] -- Replace set_data_type() with a more accurate detect_data_type() in - trees.c, according to the txtvsbin.txt document [Truta] -- Swap the order of #include and #include "zlib.h" in - gzio.c, example.c and minigzip.c [Truta] -- Shut up annoying VS2005 warnings about standard C deprecation [Rowe, - Truta] (where?) -- Fix target "clean" from win32/Makefile.bor [Truta] -- Create .pdb and .manifest files in win32/makefile.msc [Ziegler, Rowe] -- Update zlib www home address in win32/DLL_FAQ.txt [Truta] -- Update contrib/masmx86/inffas32.asm for VS2005 [Vollant, Van Wassenhove] -- Enable browse info in the "Debug" and "ASM Debug" configurations in - the Visual C++ 6 project, and set (non-ASM) "Debug" as default [Truta] -- Add pkgconfig support [Weigelt] -- Add ZLIB_VER_MAJOR, ZLIB_VER_MINOR and ZLIB_VER_REVISION in zlib.h, - for use in win32/zlib1.rc [Polushin, Rowe, Truta] -- Add a document that explains the new text detection scheme to - doc/txtvsbin.txt [Truta] -- Add rfc1950.txt, rfc1951.txt and rfc1952.txt to doc/ [Truta] -- Move algorithm.txt into doc/ [Truta] -- Synchronize FAQ with website -- Fix compressBound(), was low for some pathological cases [Fearnley] -- Take into account wrapper variations in deflateBound() -- Set examples/zpipe.c input and output to binary mode for Windows -- Update examples/zlib_how.html with new zpipe.c (also web site) -- Fix some warnings in examples/gzlog.c and examples/zran.c (it seems - that gcc became pickier in 4.0) -- Add zlib.map for Linux: "All symbols from zlib-1.1.4 remain - un-versioned, the patch adds versioning only for symbols introduced in - zlib-1.2.0 or later. It also declares as local those symbols which are - not designed to be exported." [Levin] -- Update Z_PREFIX list in zconf.in.h, add --zprefix option to configure -- Do not initialize global static by default in trees.c, add a response - NO_INIT_GLOBAL_POINTERS to initialize them if needed [Marquess] -- Don't use strerror() in gzio.c under WinCE [Yakimov] -- Don't use errno.h in zutil.h under WinCE [Yakimov] -- Move arguments for AR to its usage to allow replacing ar [Marot] -- Add HAVE_VISIBILITY_PRAGMA in zconf.in.h for Mozilla [Randers-Pehrson] -- Improve inflateInit() and inflateInit2() documentation -- Fix structure size comment in inflate.h -- Change configure help option from --h* to --help [Santos] - -Changes in 1.2.3 (18 July 2005) -- Apply security vulnerability fixes to contrib/infback9 as well -- Clean up some text files (carriage returns, trailing space) -- Update testzlib, vstudio, masmx64, and masmx86 in contrib [Vollant] - -Changes in 1.2.2.4 (11 July 2005) -- Add inflatePrime() function for starting inflation at bit boundary -- Avoid some Visual C warnings in deflate.c -- Avoid more silly Visual C warnings in inflate.c and inftrees.c for 64-bit - compile -- Fix some spelling errors in comments [Betts] -- Correct inflateInit2() error return documentation in zlib.h -- Add zran.c example of compressed data random access to examples - directory, shows use of inflatePrime() -- Fix cast for assignments to strm->state in inflate.c and infback.c -- Fix zlibCompileFlags() in zutil.c to use 1L for long shifts [Oberhumer] -- Move declarations of gf2 functions to right place in crc32.c [Oberhumer] -- Add cast in trees.c t avoid a warning [Oberhumer] -- Avoid some warnings in fitblk.c, gun.c, gzjoin.c in examples [Oberhumer] -- Update make_vms.com [Zinser] -- Initialize state->write in inflateReset() since copied in inflate_fast() -- Be more strict on incomplete code sets in inflate_table() and increase - ENOUGH and MAXD -- this repairs a possible security vulnerability for - invalid inflate input. Thanks to Tavis Ormandy and Markus Oberhumer for - discovering the vulnerability and providing test cases -- Add ia64 support to configure for HP-UX [Smith] -- Add error return to gzread() for format or i/o error [Levin] -- Use malloc.h for OS/2 [Necasek] - -Changes in 1.2.2.3 (27 May 2005) -- Replace 1U constants in inflate.c and inftrees.c for 64-bit compile -- Typecast fread() return values in gzio.c [Vollant] -- Remove trailing space in minigzip.c outmode (VC++ can't deal with it) -- Fix crc check bug in gzread() after gzungetc() [Heiner] -- Add the deflateTune() function to adjust internal compression parameters -- Add a fast gzip decompressor, gun.c, to examples (use of inflateBack) -- Remove an incorrect assertion in examples/zpipe.c -- Add C++ wrapper in infback9.h [Donais] -- Fix bug in inflateCopy() when decoding fixed codes -- Note in zlib.h how much deflateSetDictionary() actually uses -- Remove USE_DICT_HEAD in deflate.c (would mess up inflate if used) -- Add _WIN32_WCE to define WIN32 in zconf.in.h [Spencer] -- Don't include stderr.h or errno.h for _WIN32_WCE in zutil.h [Spencer] -- Add gzdirect() function to indicate transparent reads -- Update contrib/minizip [Vollant] -- Fix compilation of deflate.c when both ASMV and FASTEST [Oberhumer] -- Add casts in crc32.c to avoid warnings [Oberhumer] -- Add contrib/masmx64 [Vollant] -- Update contrib/asm586, asm686, masmx86, testzlib, vstudio [Vollant] - -Changes in 1.2.2.2 (30 December 2004) -- Replace structure assignments in deflate.c and inflate.c with zmemcpy to - avoid implicit memcpy calls (portability for no-library compilation) -- Increase sprintf() buffer size in gzdopen() to allow for large numbers -- Add INFLATE_STRICT to check distances against zlib header -- Improve WinCE errno handling and comments [Chang] -- Remove comment about no gzip header processing in FAQ -- Add Z_FIXED strategy option to deflateInit2() to force fixed trees -- Add updated make_vms.com [Coghlan], update README -- Create a new "examples" directory, move gzappend.c there, add zpipe.c, - fitblk.c, gzlog.[ch], gzjoin.c, and zlib_how.html -- Add FAQ entry and comments in deflate.c on uninitialized memory access -- Add Solaris 9 make options in configure [Gilbert] -- Allow strerror() usage in gzio.c for STDC -- Fix DecompressBuf in contrib/delphi/ZLib.pas [ManChesTer] -- Update contrib/masmx86/inffas32.asm and gvmat32.asm [Vollant] -- Use z_off_t for adler32_combine() and crc32_combine() lengths -- Make adler32() much faster for small len -- Use OS_CODE in deflate() default gzip header - -Changes in 1.2.2.1 (31 October 2004) -- Allow inflateSetDictionary() call for raw inflate -- Fix inflate header crc check bug for file names and comments -- Add deflateSetHeader() and gz_header structure for custom gzip headers -- Add inflateGetheader() to retrieve gzip headers -- Add crc32_combine() and adler32_combine() functions -- Add alloc_func, free_func, in_func, out_func to Z_PREFIX list -- Use zstreamp consistently in zlib.h (inflate_back functions) -- Remove GUNZIP condition from definition of inflate_mode in inflate.h - and in contrib/inflate86/inffast.S [Truta, Anderson] -- Add support for AMD64 in contrib/inflate86/inffas86.c [Anderson] -- Update projects/README.projects and projects/visualc6 [Truta] -- Update win32/DLL_FAQ.txt [Truta] -- Avoid warning under NO_GZCOMPRESS in gzio.c; fix typo [Truta] -- Deprecate Z_ASCII; use Z_TEXT instead [Truta] -- Use a new algorithm for setting strm->data_type in trees.c [Truta] -- Do not define an exit() prototype in zutil.c unless DEBUG defined -- Remove prototype of exit() from zutil.c, example.c, minigzip.c [Truta] -- Add comment in zlib.h for Z_NO_FLUSH parameter to deflate() -- Fix Darwin build version identification [Peterson] - -Changes in 1.2.2 (3 October 2004) -- Update zlib.h comments on gzip in-memory processing -- Set adler to 1 in inflateReset() to support Java test suite [Walles] -- Add contrib/dotzlib [Ravn] -- Update win32/DLL_FAQ.txt [Truta] -- Update contrib/minizip [Vollant] -- Move contrib/visual-basic.txt to old/ [Truta] -- Fix assembler builds in projects/visualc6/ [Truta] - -Changes in 1.2.1.2 (9 September 2004) -- Update INDEX file -- Fix trees.c to update strm->data_type (no one ever noticed!) -- Fix bug in error case in inflate.c, infback.c, and infback9.c [Brown] -- Add "volatile" to crc table flag declaration (for DYNAMIC_CRC_TABLE) -- Add limited multitasking protection to DYNAMIC_CRC_TABLE -- Add NO_vsnprintf for VMS in zutil.h [Mozilla] -- Don't declare strerror() under VMS [Mozilla] -- Add comment to DYNAMIC_CRC_TABLE to use get_crc_table() to initialize -- Update contrib/ada [Anisimkov] -- Update contrib/minizip [Vollant] -- Fix configure to not hardcode directories for Darwin [Peterson] -- Fix gzio.c to not return error on empty files [Brown] -- Fix indentation; update version in contrib/delphi/ZLib.pas and - contrib/pascal/zlibpas.pas [Truta] -- Update mkasm.bat in contrib/masmx86 [Truta] -- Update contrib/untgz [Truta] -- Add projects/README.projects [Truta] -- Add project for MS Visual C++ 6.0 in projects/visualc6 [Cadieux, Truta] -- Update win32/DLL_FAQ.txt [Truta] -- Update list of Z_PREFIX symbols in zconf.h [Randers-Pehrson, Truta] -- Remove an unnecessary assignment to curr in inftrees.c [Truta] -- Add OS/2 to exe builds in configure [Poltorak] -- Remove err dummy parameter in zlib.h [Kientzle] - -Changes in 1.2.1.1 (9 January 2004) -- Update email address in README -- Several FAQ updates -- Fix a big fat bug in inftrees.c that prevented decoding valid - dynamic blocks with only literals and no distance codes -- - Thanks to "Hot Emu" for the bug report and sample file -- Add a note to puff.c on no distance codes case - -Changes in 1.2.1 (17 November 2003) -- Remove a tab in contrib/gzappend/gzappend.c -- Update some interfaces in contrib for new zlib functions -- Update zlib version number in some contrib entries -- Add Windows CE definition for ptrdiff_t in zutil.h [Mai, Truta] -- Support shared libraries on Hurd and KFreeBSD [Brown] -- Fix error in NO_DIVIDE option of adler32.c - -Changes in 1.2.0.8 (4 November 2003) -- Update version in contrib/delphi/ZLib.pas and contrib/pascal/zlibpas.pas -- Add experimental NO_DIVIDE #define in adler32.c - - Possibly faster on some processors (let me know if it is) -- Correct Z_BLOCK to not return on first inflate call if no wrap -- Fix strm->data_type on inflate() return to correctly indicate EOB -- Add deflatePrime() function for appending in the middle of a byte -- Add contrib/gzappend for an example of appending to a stream -- Update win32/DLL_FAQ.txt [Truta] -- Delete Turbo C comment in README [Truta] -- Improve some indentation in zconf.h [Truta] -- Fix infinite loop on bad input in configure script [Church] -- Fix gzeof() for concatenated gzip files [Johnson] -- Add example to contrib/visual-basic.txt [Michael B.] -- Add -p to mkdir's in Makefile.in [vda] -- Fix configure to properly detect presence or lack of printf functions -- Add AS400 support [Monnerat] -- Add a little Cygwin support [Wilson] - -Changes in 1.2.0.7 (21 September 2003) -- Correct some debug formats in contrib/infback9 -- Cast a type in a debug statement in trees.c -- Change search and replace delimiter in configure from % to # [Beebe] -- Update contrib/untgz to 0.2 with various fixes [Truta] -- Add build support for Amiga [Nikl] -- Remove some directories in old that have been updated to 1.2 -- Add dylib building for Mac OS X in configure and Makefile.in -- Remove old distribution stuff from Makefile -- Update README to point to DLL_FAQ.txt, and add comment on Mac OS X -- Update links in README - -Changes in 1.2.0.6 (13 September 2003) -- Minor FAQ updates -- Update contrib/minizip to 1.00 [Vollant] -- Remove test of gz functions in example.c when GZ_COMPRESS defined [Truta] -- Update POSTINC comment for 68060 [Nikl] -- Add contrib/infback9 with deflate64 decoding (unsupported) -- For MVS define NO_vsnprintf and undefine FAR [van Burik] -- Add pragma for fdopen on MVS [van Burik] - -Changes in 1.2.0.5 (8 September 2003) -- Add OF to inflateBackEnd() declaration in zlib.h -- Remember start when using gzdopen in the middle of a file -- Use internal off_t counters in gz* functions to properly handle seeks -- Perform more rigorous check for distance-too-far in inffast.c -- Add Z_BLOCK flush option to return from inflate at block boundary -- Set strm->data_type on return from inflate - - Indicate bits unused, if at block boundary, and if in last block -- Replace size_t with ptrdiff_t in crc32.c, and check for correct size -- Add condition so old NO_DEFLATE define still works for compatibility -- FAQ update regarding the Windows DLL [Truta] -- INDEX update: add qnx entry, remove aix entry [Truta] -- Install zlib.3 into mandir [Wilson] -- Move contrib/zlib_dll_FAQ.txt to win32/DLL_FAQ.txt; update [Truta] -- Adapt the zlib interface to the new DLL convention guidelines [Truta] -- Introduce ZLIB_WINAPI macro to allow the export of functions using - the WINAPI calling convention, for Visual Basic [Vollant, Truta] -- Update msdos and win32 scripts and makefiles [Truta] -- Export symbols by name, not by ordinal, in win32/zlib.def [Truta] -- Add contrib/ada [Anisimkov] -- Move asm files from contrib/vstudio/vc70_32 to contrib/asm386 [Truta] -- Rename contrib/asm386 to contrib/masmx86 [Truta, Vollant] -- Add contrib/masm686 [Truta] -- Fix offsets in contrib/inflate86 and contrib/masmx86/inffas32.asm - [Truta, Vollant] -- Update contrib/delphi; rename to contrib/pascal; add example [Truta] -- Remove contrib/delphi2; add a new contrib/delphi [Truta] -- Avoid inclusion of the nonstandard in contrib/iostream, - and fix some method prototypes [Truta] -- Fix the ZCR_SEED2 constant to avoid warnings in contrib/minizip - [Truta] -- Avoid the use of backslash (\) in contrib/minizip [Vollant] -- Fix file time handling in contrib/untgz; update makefiles [Truta] -- Update contrib/vstudio/vc70_32 to comply with the new DLL guidelines - [Vollant] -- Remove contrib/vstudio/vc15_16 [Vollant] -- Rename contrib/vstudio/vc70_32 to contrib/vstudio/vc7 [Truta] -- Update README.contrib [Truta] -- Invert the assignment order of match_head and s->prev[...] in - INSERT_STRING [Truta] -- Compare TOO_FAR with 32767 instead of 32768, to avoid 16-bit warnings - [Truta] -- Compare function pointers with 0, not with NULL or Z_NULL [Truta] -- Fix prototype of syncsearch in inflate.c [Truta] -- Introduce ASMINF macro to be enabled when using an ASM implementation - of inflate_fast [Truta] -- Change NO_DEFLATE to NO_GZCOMPRESS [Truta] -- Modify test_gzio in example.c to take a single file name as a - parameter [Truta] -- Exit the example.c program if gzopen fails [Truta] -- Add type casts around strlen in example.c [Truta] -- Remove casting to sizeof in minigzip.c; give a proper type - to the variable compared with SUFFIX_LEN [Truta] -- Update definitions of STDC and STDC99 in zconf.h [Truta] -- Synchronize zconf.h with the new Windows DLL interface [Truta] -- Use SYS16BIT instead of __32BIT__ to distinguish between - 16- and 32-bit platforms [Truta] -- Use far memory allocators in small 16-bit memory models for - Turbo C [Truta] -- Add info about the use of ASMV, ASMINF and ZLIB_WINAPI in - zlibCompileFlags [Truta] -- Cygwin has vsnprintf [Wilson] -- In Windows16, OS_CODE is 0, as in MSDOS [Truta] -- In Cygwin, OS_CODE is 3 (Unix), not 11 (Windows32) [Wilson] - -Changes in 1.2.0.4 (10 August 2003) -- Minor FAQ updates -- Be more strict when checking inflateInit2's windowBits parameter -- Change NO_GUNZIP compile option to NO_GZIP to cover deflate as well -- Add gzip wrapper option to deflateInit2 using windowBits -- Add updated QNX rule in configure and qnx directory [Bonnefoy] -- Make inflate distance-too-far checks more rigorous -- Clean up FAR usage in inflate -- Add casting to sizeof() in gzio.c and minigzip.c - -Changes in 1.2.0.3 (19 July 2003) -- Fix silly error in gzungetc() implementation [Vollant] -- Update contrib/minizip and contrib/vstudio [Vollant] -- Fix printf format in example.c -- Correct cdecl support in zconf.in.h [Anisimkov] -- Minor FAQ updates - -Changes in 1.2.0.2 (13 July 2003) -- Add ZLIB_VERNUM in zlib.h for numerical preprocessor comparisons -- Attempt to avoid warnings in crc32.c for pointer-int conversion -- Add AIX to configure, remove aix directory [Bakker] -- Add some casts to minigzip.c -- Improve checking after insecure sprintf() or vsprintf() calls -- Remove #elif's from crc32.c -- Change leave label to inf_leave in inflate.c and infback.c to avoid - library conflicts -- Remove inflate gzip decoding by default--only enable gzip decoding by - special request for stricter backward compatibility -- Add zlibCompileFlags() function to return compilation information -- More typecasting in deflate.c to avoid warnings -- Remove leading underscore from _Capital #defines [Truta] -- Fix configure to link shared library when testing -- Add some Windows CE target adjustments [Mai] -- Remove #define ZLIB_DLL in zconf.h [Vollant] -- Add zlib.3 [Rodgers] -- Update RFC URL in deflate.c and algorithm.txt [Mai] -- Add zlib_dll_FAQ.txt to contrib [Truta] -- Add UL to some constants [Truta] -- Update minizip and vstudio [Vollant] -- Remove vestigial NEED_DUMMY_RETURN from zconf.in.h -- Expand use of NO_DUMMY_DECL to avoid all dummy structures -- Added iostream3 to contrib [Schwardt] -- Replace rewind() with fseek() for WinCE [Truta] -- Improve setting of zlib format compression level flags - - Report 0 for huffman and rle strategies and for level == 0 or 1 - - Report 2 only for level == 6 -- Only deal with 64K limit when necessary at compile time [Truta] -- Allow TOO_FAR check to be turned off at compile time [Truta] -- Add gzclearerr() function [Souza] -- Add gzungetc() function - -Changes in 1.2.0.1 (17 March 2003) -- Add Z_RLE strategy for run-length encoding [Truta] - - When Z_RLE requested, restrict matches to distance one - - Update zlib.h, minigzip.c, gzopen(), gzdopen() for Z_RLE -- Correct FASTEST compilation to allow level == 0 -- Clean up what gets compiled for FASTEST -- Incorporate changes to zconf.in.h [Vollant] - - Refine detection of Turbo C need for dummy returns - - Refine ZLIB_DLL compilation - - Include additional header file on VMS for off_t typedef -- Try to use _vsnprintf where it supplants vsprintf [Vollant] -- Add some casts in inffast.c -- Enhance comments in zlib.h on what happens if gzprintf() tries to - write more than 4095 bytes before compression -- Remove unused state from inflateBackEnd() -- Remove exit(0) from minigzip.c, example.c -- Get rid of all those darn tabs -- Add "check" target to Makefile.in that does the same thing as "test" -- Add "mostlyclean" and "maintainer-clean" targets to Makefile.in -- Update contrib/inflate86 [Anderson] -- Update contrib/testzlib, contrib/vstudio, contrib/minizip [Vollant] -- Add msdos and win32 directories with makefiles [Truta] -- More additions and improvements to the FAQ - -Changes in 1.2.0 (9 March 2003) -- New and improved inflate code - - About 20% faster - - Does not allocate 32K window unless and until needed - - Automatically detects and decompresses gzip streams - - Raw inflate no longer needs an extra dummy byte at end - - Added inflateBack functions using a callback interface--even faster - than inflate, useful for file utilities (gzip, zip) - - Added inflateCopy() function to record state for random access on - externally generated deflate streams (e.g. in gzip files) - - More readable code (I hope) -- New and improved crc32() - - About 50% faster, thanks to suggestions from Rodney Brown -- Add deflateBound() and compressBound() functions -- Fix memory leak in deflateInit2() -- Permit setting dictionary for raw deflate (for parallel deflate) -- Fix const declaration for gzwrite() -- Check for some malloc() failures in gzio.c -- Fix bug in gzopen() on single-byte file 0x1f -- Fix bug in gzread() on concatenated file with 0x1f at end of buffer - and next buffer doesn't start with 0x8b -- Fix uncompress() to return Z_DATA_ERROR on truncated input -- Free memory at end of example.c -- Remove MAX #define in trees.c (conflicted with some libraries) -- Fix static const's in deflate.c, gzio.c, and zutil.[ch] -- Declare malloc() and free() in gzio.c if STDC not defined -- Use malloc() instead of calloc() in zutil.c if int big enough -- Define STDC for AIX -- Add aix/ with approach for compiling shared library on AIX -- Add HP-UX support for shared libraries in configure -- Add OpenUNIX support for shared libraries in configure -- Use $cc instead of gcc to build shared library -- Make prefix directory if needed when installing -- Correct Macintosh avoidance of typedef Byte in zconf.h -- Correct Turbo C memory allocation when under Linux -- Use libz.a instead of -lz in Makefile (assure use of compiled library) -- Update configure to check for snprintf or vsnprintf functions and their - return value, warn during make if using an insecure function -- Fix configure problem with compile-time knowledge of HAVE_UNISTD_H that - is lost when library is used--resolution is to build new zconf.h -- Documentation improvements (in zlib.h): - - Document raw deflate and inflate - - Update RFCs URL - - Point out that zlib and gzip formats are different - - Note that Z_BUF_ERROR is not fatal - - Document string limit for gzprintf() and possible buffer overflow - - Note requirement on avail_out when flushing - - Note permitted values of flush parameter of inflate() -- Add some FAQs (and even answers) to the FAQ -- Add contrib/inflate86/ for x86 faster inflate -- Add contrib/blast/ for PKWare Data Compression Library decompression -- Add contrib/puff/ simple inflate for deflate format description - -Changes in 1.1.4 (11 March 2002) -- ZFREE was repeated on same allocation on some error conditions - This creates a security problem described in - http://www.zlib.org/advisory-2002-03-11.txt -- Returned incorrect error (Z_MEM_ERROR) on some invalid data -- Avoid accesses before window for invalid distances with inflate window - less than 32K -- force windowBits > 8 to avoid a bug in the encoder for a window size - of 256 bytes. (A complete fix will be available in 1.1.5) - -Changes in 1.1.3 (9 July 1998) -- fix "an inflate input buffer bug that shows up on rare but persistent - occasions" (Mark) -- fix gzread and gztell for concatenated .gz files (Didier Le Botlan) -- fix gzseek(..., SEEK_SET) in write mode -- fix crc check after a gzeek (Frank Faubert) -- fix miniunzip when the last entry in a zip file is itself a zip file - (J Lillge) -- add contrib/asm586 and contrib/asm686 (Brian Raiter) - See http://www.muppetlabs.com/~breadbox/software/assembly.html -- add support for Delphi 3 in contrib/delphi (Bob Dellaca) -- add support for C++Builder 3 and Delphi 3 in contrib/delphi2 (Davide Moretti) -- do not exit prematurely in untgz if 0 at start of block (Magnus Holmgren) -- use macro EXTERN instead of extern to support DLL for BeOS (Sander Stoks) -- added a FAQ file - -- Support gzdopen on Mac with Metrowerks (Jason Linhart) -- Do not redefine Byte on Mac (Brad Pettit & Jason Linhart) -- define SEEK_END too if SEEK_SET is not defined (Albert Chin-A-Young) -- avoid some warnings with Borland C (Tom Tanner) -- fix a problem in contrib/minizip/zip.c for 16-bit MSDOS (Gilles Vollant) -- emulate utime() for WIN32 in contrib/untgz (Gilles Vollant) -- allow several arguments to configure (Tim Mooney, Frodo Looijaard) -- use libdir and includedir in Makefile.in (Tim Mooney) -- support shared libraries on OSF1 V4 (Tim Mooney) -- remove so_locations in "make clean" (Tim Mooney) -- fix maketree.c compilation error (Glenn, Mark) -- Python interface to zlib now in Python 1.5 (Jeremy Hylton) -- new Makefile.riscos (Rich Walker) -- initialize static descriptors in trees.c for embedded targets (Nick Smith) -- use "foo-gz" in example.c for RISCOS and VMS (Nick Smith) -- add the OS/2 files in Makefile.in too (Andrew Zabolotny) -- fix fdopen and halloc macros for Microsoft C 6.0 (Tom Lane) -- fix maketree.c to allow clean compilation of inffixed.h (Mark) -- fix parameter check in deflateCopy (Gunther Nikl) -- cleanup trees.c, use compressed_len only in debug mode (Christian Spieler) -- Many portability patches by Christian Spieler: - . zutil.c, zutil.h: added "const" for zmem* - . Make_vms.com: fixed some typos - . Make_vms.com: msdos/Makefile.*: removed zutil.h from some dependency lists - . msdos/Makefile.msc: remove "default rtl link library" info from obj files - . msdos/Makefile.*: use model-dependent name for the built zlib library - . msdos/Makefile.emx, nt/Makefile.emx, nt/Makefile.gcc: - new makefiles, for emx (DOS/OS2), emx&rsxnt and mingw32 (Windows 9x / NT) -- use define instead of typedef for Bytef also for MSC small/medium (Tom Lane) -- replace __far with _far for better portability (Christian Spieler, Tom Lane) -- fix test for errno.h in configure (Tim Newsham) - -Changes in 1.1.2 (19 March 98) -- added contrib/minzip, mini zip and unzip based on zlib (Gilles Vollant) - See http://www.winimage.com/zLibDll/unzip.html -- preinitialize the inflate tables for fixed codes, to make the code - completely thread safe (Mark) -- some simplifications and slight speed-up to the inflate code (Mark) -- fix gzeof on non-compressed files (Allan Schrum) -- add -std1 option in configure for OSF1 to fix gzprintf (Martin Mokrejs) -- use default value of 4K for Z_BUFSIZE for 16-bit MSDOS (Tim Wegner + Glenn) -- added os2/Makefile.def and os2/zlib.def (Andrew Zabolotny) -- add shared lib support for UNIX_SV4.2MP (MATSUURA Takanori) -- do not wrap extern "C" around system includes (Tom Lane) -- mention zlib binding for TCL in README (Andreas Kupries) -- added amiga/Makefile.pup for Amiga powerUP SAS/C PPC (Andreas Kleinert) -- allow "make install prefix=..." even after configure (Glenn Randers-Pehrson) -- allow "configure --prefix $HOME" (Tim Mooney) -- remove warnings in example.c and gzio.c (Glenn Randers-Pehrson) -- move Makefile.sas to amiga/Makefile.sas - -Changes in 1.1.1 (27 Feb 98) -- fix macros _tr_tally_* in deflate.h for debug mode (Glenn Randers-Pehrson) -- remove block truncation heuristic which had very marginal effect for zlib - (smaller lit_bufsize than in gzip 1.2.4) and degraded a little the - compression ratio on some files. This also allows inlining _tr_tally for - matches in deflate_slow -- added msdos/Makefile.w32 for WIN32 Microsoft Visual C++ (Bob Frazier) - -Changes in 1.1.0 (24 Feb 98) -- do not return STREAM_END prematurely in inflate (John Bowler) -- revert to the zlib 1.0.8 inflate to avoid the gcc 2.8.0 bug (Jeremy Buhler) -- compile with -DFASTEST to get compression code optimized for speed only -- in minigzip, try mmap'ing the input file first (Miguel Albrecht) -- increase size of I/O buffers in minigzip.c and gzio.c (not a big gain - on Sun but significant on HP) - -- add a pointer to experimental unzip library in README (Gilles Vollant) -- initialize variable gcc in configure (Chris Herborth) - -Changes in 1.0.9 (17 Feb 1998) -- added gzputs and gzgets functions -- do not clear eof flag in gzseek (Mark Diekhans) -- fix gzseek for files in transparent mode (Mark Diekhans) -- do not assume that vsprintf returns the number of bytes written (Jens Krinke) -- replace EXPORT with ZEXPORT to avoid conflict with other programs -- added compress2 in zconf.h, zlib.def, zlib.dnt -- new asm code from Gilles Vollant in contrib/asm386 -- simplify the inflate code (Mark): - . Replace ZALLOC's in huft_build() with single ZALLOC in inflate_blocks_new() - . ZALLOC the length list in inflate_trees_fixed() instead of using stack - . ZALLOC the value area for huft_build() instead of using stack - . Simplify Z_FINISH check in inflate() - -- Avoid gcc 2.8.0 comparison bug a little differently than zlib 1.0.8 -- in inftrees.c, avoid cc -O bug on HP (Farshid Elahi) -- in zconf.h move the ZLIB_DLL stuff earlier to avoid problems with - the declaration of FAR (Gilles Vollant) -- install libz.so* with mode 755 (executable) instead of 644 (Marc Lehmann) -- read_buf buf parameter of type Bytef* instead of charf* -- zmemcpy parameters are of type Bytef*, not charf* (Joseph Strout) -- do not redeclare unlink in minigzip.c for WIN32 (John Bowler) -- fix check for presence of directories in "make install" (Ian Willis) - -Changes in 1.0.8 (27 Jan 1998) -- fixed offsets in contrib/asm386/gvmat32.asm (Gilles Vollant) -- fix gzgetc and gzputc for big endian systems (Markus Oberhumer) -- added compress2() to allow setting the compression level -- include sys/types.h to get off_t on some systems (Marc Lehmann & QingLong) -- use constant arrays for the static trees in trees.c instead of computing - them at run time (thanks to Ken Raeburn for this suggestion). To create - trees.h, compile with GEN_TREES_H and run "make test" -- check return code of example in "make test" and display result -- pass minigzip command line options to file_compress -- simplifying code of inflateSync to avoid gcc 2.8 bug - -- support CC="gcc -Wall" in configure -s (QingLong) -- avoid a flush caused by ftell in gzopen for write mode (Ken Raeburn) -- fix test for shared library support to avoid compiler warnings -- zlib.lib -> zlib.dll in msdos/zlib.rc (Gilles Vollant) -- check for TARGET_OS_MAC in addition to MACOS (Brad Pettit) -- do not use fdopen for Metrowerks on Mac (Brad Pettit)) -- add checks for gzputc and gzputc in example.c -- avoid warnings in gzio.c and deflate.c (Andreas Kleinert) -- use const for the CRC table (Ken Raeburn) -- fixed "make uninstall" for shared libraries -- use Tracev instead of Trace in infblock.c -- in example.c use correct compressed length for test_sync -- suppress +vnocompatwarnings in configure for HPUX (not always supported) - -Changes in 1.0.7 (20 Jan 1998) -- fix gzseek which was broken in write mode -- return error for gzseek to negative absolute position -- fix configure for Linux (Chun-Chung Chen) -- increase stack space for MSC (Tim Wegner) -- get_crc_table and inflateSyncPoint are EXPORTed (Gilles Vollant) -- define EXPORTVA for gzprintf (Gilles Vollant) -- added man page zlib.3 (Rick Rodgers) -- for contrib/untgz, fix makedir() and improve Makefile - -- check gzseek in write mode in example.c -- allocate extra buffer for seeks only if gzseek is actually called -- avoid signed/unsigned comparisons (Tim Wegner, Gilles Vollant) -- add inflateSyncPoint in zconf.h -- fix list of exported functions in nt/zlib.dnt and mdsos/zlib.def - -Changes in 1.0.6 (19 Jan 1998) -- add functions gzprintf, gzputc, gzgetc, gztell, gzeof, gzseek, gzrewind and - gzsetparams (thanks to Roland Giersig and Kevin Ruland for some of this code) -- Fix a deflate bug occurring only with compression level 0 (thanks to - Andy Buckler for finding this one) -- In minigzip, pass transparently also the first byte for .Z files -- return Z_BUF_ERROR instead of Z_OK if output buffer full in uncompress() -- check Z_FINISH in inflate (thanks to Marc Schluper) -- Implement deflateCopy (thanks to Adam Costello) -- make static libraries by default in configure, add --shared option -- move MSDOS or Windows specific files to directory msdos -- suppress the notion of partial flush to simplify the interface - (but the symbol Z_PARTIAL_FLUSH is kept for compatibility with 1.0.4) -- suppress history buffer provided by application to simplify the interface - (this feature was not implemented anyway in 1.0.4) -- next_in and avail_in must be initialized before calling inflateInit or - inflateInit2 -- add EXPORT in all exported functions (for Windows DLL) -- added Makefile.nt (thanks to Stephen Williams) -- added the unsupported "contrib" directory: - contrib/asm386/ by Gilles Vollant - 386 asm code replacing longest_match() - contrib/iostream/ by Kevin Ruland - A C++ I/O streams interface to the zlib gz* functions - contrib/iostream2/ by Tyge Løvset - Another C++ I/O streams interface - contrib/untgz/ by "Pedro A. Aranda Guti\irrez" - A very simple tar.gz file extractor using zlib - contrib/visual-basic.txt by Carlos Rios - How to use compress(), uncompress() and the gz* functions from VB -- pass params -f (filtered data), -h (huffman only), -1 to -9 (compression - level) in minigzip (thanks to Tom Lane) - -- use const for rommable constants in deflate -- added test for gzseek and gztell in example.c -- add undocumented function inflateSyncPoint() (hack for Paul Mackerras) -- add undocumented function zError to convert error code to string - (for Tim Smithers) -- Allow compilation of gzio with -DNO_DEFLATE to avoid the compression code -- Use default memcpy for Symantec MSDOS compiler -- Add EXPORT keyword for check_func (needed for Windows DLL) -- add current directory to LD_LIBRARY_PATH for "make test" -- create also a link for libz.so.1 -- added support for FUJITSU UXP/DS (thanks to Toshiaki Nomura) -- use $(SHAREDLIB) instead of libz.so in Makefile.in (for HPUX) -- added -soname for Linux in configure (Chun-Chung Chen, -- assign numbers to the exported functions in zlib.def (for Windows DLL) -- add advice in zlib.h for best usage of deflateSetDictionary -- work around compiler bug on Atari (cast Z_NULL in call of s->checkfn) -- allow compilation with ANSI keywords only enabled for TurboC in large model -- avoid "versionString"[0] (Borland bug) -- add NEED_DUMMY_RETURN for Borland -- use variable z_verbose for tracing in debug mode (L. Peter Deutsch) -- allow compilation with CC -- defined STDC for OS/2 (David Charlap) -- limit external names to 8 chars for MVS (Thomas Lund) -- in minigzip.c, use static buffers only for 16-bit systems -- fix suffix check for "minigzip -d foo.gz" -- do not return an error for the 2nd of two consecutive gzflush() (Felix Lee) -- use _fdopen instead of fdopen for MSC >= 6.0 (Thomas Fanslau) -- added makelcc.bat for lcc-win32 (Tom St Denis) -- in Makefile.dj2, use copy and del instead of install and rm (Frank Donahoe) -- Avoid expanded $Id$. Use "rcs -kb" or "cvs admin -kb" to avoid Id expansion -- check for unistd.h in configure (for off_t) -- remove useless check parameter in inflate_blocks_free -- avoid useless assignment of s->check to itself in inflate_blocks_new -- do not flush twice in gzclose (thanks to Ken Raeburn) -- rename FOPEN as F_OPEN to avoid clash with /usr/include/sys/file.h -- use NO_ERRNO_H instead of enumeration of operating systems with errno.h -- work around buggy fclose on pipes for HP/UX -- support zlib DLL with BORLAND C++ 5.0 (thanks to Glenn Randers-Pehrson) -- fix configure if CC is already equal to gcc - -Changes in 1.0.5 (3 Jan 98) -- Fix inflate to terminate gracefully when fed corrupted or invalid data -- Use const for rommable constants in inflate -- Eliminate memory leaks on error conditions in inflate -- Removed some vestigial code in inflate -- Update web address in README - -Changes in 1.0.4 (24 Jul 96) -- In very rare conditions, deflate(s, Z_FINISH) could fail to produce an EOF - bit, so the decompressor could decompress all the correct data but went - on to attempt decompressing extra garbage data. This affected minigzip too -- zlibVersion and gzerror return const char* (needed for DLL) -- port to RISCOS (no fdopen, no multiple dots, no unlink, no fileno) -- use z_error only for DEBUG (avoid problem with DLLs) - -Changes in 1.0.3 (2 Jul 96) -- use z_streamp instead of z_stream *, which is now a far pointer in MSDOS - small and medium models; this makes the library incompatible with previous - versions for these models. (No effect in large model or on other systems.) -- return OK instead of BUF_ERROR if previous deflate call returned with - avail_out as zero but there is nothing to do -- added memcmp for non STDC compilers -- define NO_DUMMY_DECL for more Mac compilers (.h files merged incorrectly) -- define __32BIT__ if __386__ or i386 is defined (pb. with Watcom and SCO) -- better check for 16-bit mode MSC (avoids problem with Symantec) - -Changes in 1.0.2 (23 May 96) -- added Windows DLL support -- added a function zlibVersion (for the DLL support) -- fixed declarations using Bytef in infutil.c (pb with MSDOS medium model) -- Bytef is define's instead of typedef'd only for Borland C -- avoid reading uninitialized memory in example.c -- mention in README that the zlib format is now RFC1950 -- updated Makefile.dj2 -- added algorithm.doc - -Changes in 1.0.1 (20 May 96) [1.0 skipped to avoid confusion] -- fix array overlay in deflate.c which sometimes caused bad compressed data -- fix inflate bug with empty stored block -- fix MSDOS medium model which was broken in 0.99 -- fix deflateParams() which could generate bad compressed data -- Bytef is define'd instead of typedef'ed (work around Borland bug) -- added an INDEX file -- new makefiles for DJGPP (Makefile.dj2), 32-bit Borland (Makefile.b32), - Watcom (Makefile.wat), Amiga SAS/C (Makefile.sas) -- speed up adler32 for modern machines without auto-increment -- added -ansi for IRIX in configure -- static_init_done in trees.c is an int -- define unlink as delete for VMS -- fix configure for QNX -- add configure branch for SCO and HPUX -- avoid many warnings (unused variables, dead assignments, etc...) -- no fdopen for BeOS -- fix the Watcom fix for 32 bit mode (define FAR as empty) -- removed redefinition of Byte for MKWERKS -- work around an MWKERKS bug (incorrect merge of all .h files) - -Changes in 0.99 (27 Jan 96) -- allow preset dictionary shared between compressor and decompressor -- allow compression level 0 (no compression) -- add deflateParams in zlib.h: allow dynamic change of compression level - and compression strategy -- test large buffers and deflateParams in example.c -- add optional "configure" to build zlib as a shared library -- suppress Makefile.qnx, use configure instead -- fixed deflate for 64-bit systems (detected on Cray) -- fixed inflate_blocks for 64-bit systems (detected on Alpha) -- declare Z_DEFLATED in zlib.h (possible parameter for deflateInit2) -- always return Z_BUF_ERROR when deflate() has nothing to do -- deflateInit and inflateInit are now macros to allow version checking -- prefix all global functions and types with z_ with -DZ_PREFIX -- make falloc completely reentrant (inftrees.c) -- fixed very unlikely race condition in ct_static_init -- free in reverse order of allocation to help memory manager -- use zlib-1.0/* instead of zlib/* inside the tar.gz -- make zlib warning-free with "gcc -O3 -Wall -Wwrite-strings -Wpointer-arith - -Wconversion -Wstrict-prototypes -Wmissing-prototypes" -- allow gzread on concatenated .gz files -- deflateEnd now returns Z_DATA_ERROR if it was premature -- deflate is finally (?) fully deterministic (no matches beyond end of input) -- Document Z_SYNC_FLUSH -- add uninstall in Makefile -- Check for __cpluplus in zlib.h -- Better test in ct_align for partial flush -- avoid harmless warnings for Borland C++ -- initialize hash_head in deflate.c -- avoid warning on fdopen (gzio.c) for HP cc -Aa -- include stdlib.h for STDC compilers -- include errno.h for Cray -- ignore error if ranlib doesn't exist -- call ranlib twice for NeXTSTEP -- use exec_prefix instead of prefix for libz.a -- renamed ct_* as _tr_* to avoid conflict with applications -- clear z->msg in inflateInit2 before any error return -- initialize opaque in example.c, gzio.c, deflate.c and inflate.c -- fixed typo in zconf.h (_GNUC__ => __GNUC__) -- check for WIN32 in zconf.h and zutil.c (avoid farmalloc in 32-bit mode) -- fix typo in Make_vms.com (f$trnlnm -> f$getsyi) -- in fcalloc, normalize pointer if size > 65520 bytes -- don't use special fcalloc for 32 bit Borland C++ -- use STDC instead of __GO32__ to avoid redeclaring exit, calloc, etc. -- use Z_BINARY instead of BINARY -- document that gzclose after gzdopen will close the file -- allow "a" as mode in gzopen -- fix error checking in gzread -- allow skipping .gz extra-field on pipes -- added reference to Perl interface in README -- put the crc table in FAR data (I dislike more and more the medium model :) -- added get_crc_table -- added a dimension to all arrays (Borland C can't count) -- workaround Borland C bug in declaration of inflate_codes_new & inflate_fast -- guard against multiple inclusion of *.h (for precompiled header on Mac) -- Watcom C pretends to be Microsoft C small model even in 32 bit mode -- don't use unsized arrays to avoid silly warnings by Visual C++: - warning C4746: 'inflate_mask' : unsized array treated as '__far' - (what's wrong with far data in far model?) -- define enum out of inflate_blocks_state to allow compilation with C++ - -Changes in 0.95 (16 Aug 95) -- fix MSDOS small and medium model (now easier to adapt to any compiler) -- inlined send_bits -- fix the final (:-) bug for deflate with flush (output was correct but - not completely flushed in rare occasions) -- default window size is same for compression and decompression - (it's now sufficient to set MAX_WBITS in zconf.h) -- voidp -> voidpf and voidnp -> voidp (for consistency with other - typedefs and because voidnp was not near in large model) - -Changes in 0.94 (13 Aug 95) -- support MSDOS medium model -- fix deflate with flush (could sometimes generate bad output) -- fix deflateReset (zlib header was incorrectly suppressed) -- added support for VMS -- allow a compression level in gzopen() -- gzflush now calls fflush -- For deflate with flush, flush even if no more input is provided -- rename libgz.a as libz.a -- avoid complex expression in infcodes.c triggering Turbo C bug -- work around a problem with gcc on Alpha (in INSERT_STRING) -- don't use inline functions (problem with some gcc versions) -- allow renaming of Byte, uInt, etc... with #define -- avoid warning about (unused) pointer before start of array in deflate.c -- avoid various warnings in gzio.c, example.c, infblock.c, adler32.c, zutil.c -- avoid reserved word 'new' in trees.c - -Changes in 0.93 (25 June 95) -- temporarily disable inline functions -- make deflate deterministic -- give enough lookahead for PARTIAL_FLUSH -- Set binary mode for stdin/stdout in minigzip.c for OS/2 -- don't even use signed char in inflate (not portable enough) -- fix inflate memory leak for segmented architectures - -Changes in 0.92 (3 May 95) -- don't assume that char is signed (problem on SGI) -- Clear bit buffer when starting a stored block -- no memcpy on Pyramid -- suppressed inftest.c -- optimized fill_window, put longest_match inline for gcc -- optimized inflate on stored blocks -- untabify all sources to simplify patches - -Changes in 0.91 (2 May 95) -- Default MEM_LEVEL is 8 (not 9 for Unix) as documented in zlib.h -- Document the memory requirements in zconf.h -- added "make install" -- fix sync search logic in inflateSync -- deflate(Z_FULL_FLUSH) now works even if output buffer too short -- after inflateSync, don't scare people with just "lo world" -- added support for DJGPP - -Changes in 0.9 (1 May 95) -- don't assume that zalloc clears the allocated memory (the TurboC bug - was Mark's bug after all :) -- let again gzread copy uncompressed data unchanged (was working in 0.71) -- deflate(Z_FULL_FLUSH), inflateReset and inflateSync are now fully implemented -- added a test of inflateSync in example.c -- moved MAX_WBITS to zconf.h because users might want to change that -- document explicitly that zalloc(64K) on MSDOS must return a normalized - pointer (zero offset) -- added Makefiles for Microsoft C, Turbo C, Borland C++ -- faster crc32() - -Changes in 0.8 (29 April 95) -- added fast inflate (inffast.c) -- deflate(Z_FINISH) now returns Z_STREAM_END when done. Warning: this - is incompatible with previous versions of zlib which returned Z_OK -- work around a TurboC compiler bug (bad code for b << 0, see infutil.h) - (actually that was not a compiler bug, see 0.81 above) -- gzread no longer reads one extra byte in certain cases -- In gzio destroy(), don't reference a freed structure -- avoid many warnings for MSDOS -- avoid the ERROR symbol which is used by MS Windows - -Changes in 0.71 (14 April 95) -- Fixed more MSDOS compilation problems :( There is still a bug with - TurboC large model - -Changes in 0.7 (14 April 95) -- Added full inflate support -- Simplified the crc32() interface. The pre- and post-conditioning - (one's complement) is now done inside crc32(). WARNING: this is - incompatible with previous versions; see zlib.h for the new usage - -Changes in 0.61 (12 April 95) -- workaround for a bug in TurboC. example and minigzip now work on MSDOS - -Changes in 0.6 (11 April 95) -- added minigzip.c -- added gzdopen to reopen a file descriptor as gzFile -- added transparent reading of non-gziped files in gzread -- fixed bug in gzread (don't read crc as data) -- fixed bug in destroy (gzio.c) (don't return Z_STREAM_END for gzclose) -- don't allocate big arrays in the stack (for MSDOS) -- fix some MSDOS compilation problems - -Changes in 0.5: -- do real compression in deflate.c. Z_PARTIAL_FLUSH is supported but - not yet Z_FULL_FLUSH -- support decompression but only in a single step (forced Z_FINISH) -- added opaque object for zalloc and zfree -- added deflateReset and inflateReset -- added a variable zlib_version for consistency checking -- renamed the 'filter' parameter of deflateInit2 as 'strategy' - Added Z_FILTERED and Z_HUFFMAN_ONLY constants - -Changes in 0.4: -- avoid "zip" everywhere, use zlib instead of ziplib -- suppress Z_BLOCK_FLUSH, interpret Z_PARTIAL_FLUSH as block flush - if compression method == 8 -- added adler32 and crc32 -- renamed deflateOptions as deflateInit2, call one or the other but not both -- added the method parameter for deflateInit2 -- added inflateInit2 -- simplified considerably deflateInit and inflateInit by not supporting - user-provided history buffer. This is supported only in deflateInit2 - and inflateInit2 - -Changes in 0.3: -- prefix all macro names with Z_ -- use Z_FINISH instead of deflateEnd to finish compression -- added Z_HUFFMAN_ONLY -- added gzerror() diff --git a/phobos/etc/c/zlib/LICENSE b/phobos/etc/c/zlib/LICENSE deleted file mode 100644 index ab8ee6f..0000000 --- a/phobos/etc/c/zlib/LICENSE +++ /dev/null @@ -1,22 +0,0 @@ -Copyright notice: - - (C) 1995-2022 Jean-loup Gailly and Mark Adler - - This software is provided 'as-is', without any express or implied - warranty. In no event will the authors be held liable for any damages - arising from the use of this software. - - Permission is granted to anyone to use this software for any purpose, - including commercial applications, and to alter it and redistribute it - freely, subject to the following restrictions: - - 1. The origin of this software must not be misrepresented; you must not - claim that you wrote the original software. If you use this software - in a product, an acknowledgment in the product documentation would be - appreciated but is not required. - 2. Altered source versions must be plainly marked as such, and must not be - misrepresented as being the original software. - 3. This notice may not be removed or altered from any source distribution. - - Jean-loup Gailly Mark Adler - jloup@gzip.org madler@alumni.caltech.edu diff --git a/phobos/etc/c/zlib/README b/phobos/etc/c/zlib/README deleted file mode 100644 index c5f9175..0000000 --- a/phobos/etc/c/zlib/README +++ /dev/null @@ -1,117 +0,0 @@ -ZLIB DATA COMPRESSION LIBRARY - -zlib 1.3.1 is a general purpose data compression library. All the code is -thread safe. The data format used by the zlib library is described by RFCs -(Request for Comments) 1950 to 1952 in the files -http://tools.ietf.org/html/rfc1950 (zlib format), rfc1951 (deflate format) and -rfc1952 (gzip format). - -All functions of the compression library are documented in the file zlib.h -(volunteer to write man pages welcome, contact zlib@gzip.org). A usage example -of the library is given in the file test/example.c which also tests that -the library is working correctly. Another example is given in the file -test/minigzip.c. The compression library itself is composed of all source -files in the root directory. - -To compile all files and run the test program, follow the instructions given at -the top of Makefile.in. In short "./configure; make test", and if that goes -well, "make install" should work for most flavors of Unix. For Windows, use -one of the special makefiles in win32/ or contrib/vstudio/ . For VMS, use -make_vms.com. - -Questions about zlib should be sent to , or to Gilles Vollant - for the Windows DLL version. The zlib home page is -http://zlib.net/ . Before reporting a problem, please check this site to -verify that you have the latest version of zlib; otherwise get the latest -version and check whether the problem still exists or not. - -PLEASE read the zlib FAQ http://zlib.net/zlib_faq.html before asking for help. - -Mark Nelson wrote an article about zlib for the Jan. 1997 -issue of Dr. Dobb's Journal; a copy of the article is available at -https://marknelson.us/posts/1997/01/01/zlib-engine.html . - -The changes made in version 1.3.1 are documented in the file ChangeLog. - -Unsupported third party contributions are provided in directory contrib/ . - -zlib is available in Java using the java.util.zip package. Follow the API -Documentation link at: https://docs.oracle.com/search/?q=java.util.zip . - -A Perl interface to zlib and bzip2 written by Paul Marquess -can be found at https://github.com/pmqs/IO-Compress . - -A Python interface to zlib written by A.M. Kuchling is -available in Python 1.5 and later versions, see -http://docs.python.org/library/zlib.html . - -zlib is built into tcl: http://wiki.tcl.tk/4610 . - -An experimental package to read and write files in .zip format, written on top -of zlib by Gilles Vollant , is available in the -contrib/minizip directory of zlib. - - -Notes for some targets: - -- For Windows DLL versions, please see win32/DLL_FAQ.txt - -- For 64-bit Irix, deflate.c must be compiled without any optimization. With - -O, one libpng test fails. The test works in 32 bit mode (with the -n32 - compiler flag). The compiler bug has been reported to SGI. - -- zlib doesn't work with gcc 2.6.3 on a DEC 3000/300LX under OSF/1 2.1 it works - when compiled with cc. - -- On Digital Unix 4.0D (formerly OSF/1) on AlphaServer, the cc option -std1 is - necessary to get gzprintf working correctly. This is done by configure. - -- zlib doesn't work on HP-UX 9.05 with some versions of /bin/cc. It works with - other compilers. Use "make test" to check your compiler. - -- gzdopen is not supported on RISCOS or BEOS. - -- For PalmOs, see http://palmzlib.sourceforge.net/ - - -Acknowledgments: - - The deflate format used by zlib was defined by Phil Katz. The deflate and - zlib specifications were written by L. Peter Deutsch. Thanks to all the - people who reported problems and suggested various improvements in zlib; they - are too numerous to cite here. - -Copyright notice: - - (C) 1995-2024 Jean-loup Gailly and Mark Adler - - This software is provided 'as-is', without any express or implied - warranty. In no event will the authors be held liable for any damages - arising from the use of this software. - - Permission is granted to anyone to use this software for any purpose, - including commercial applications, and to alter it and redistribute it - freely, subject to the following restrictions: - - 1. The origin of this software must not be misrepresented; you must not - claim that you wrote the original software. If you use this software - in a product, an acknowledgment in the product documentation would be - appreciated but is not required. - 2. Altered source versions must be plainly marked as such, and must not be - misrepresented as being the original software. - 3. This notice may not be removed or altered from any source distribution. - - Jean-loup Gailly Mark Adler - jloup@gzip.org madler@alumni.caltech.edu - -If you use the zlib library in a product, we would appreciate *not* receiving -lengthy legal documents to sign. The sources are provided for free but without -warranty of any kind. The library has been entirely written by Jean-loup -Gailly and Mark Adler; it does not include third-party code. We make all -contributions to and distributions of this project solely in our personal -capacity, and are not conveying any rights to any intellectual property of -any third parties. - -If you redistribute modified sources, we would appreciate that you include in -the file ChangeLog history information documenting your changes. Please read -the FAQ for more information on the distribution of modified source versions. diff --git a/phobos/etc/c/zlib/adler32.c b/phobos/etc/c/zlib/adler32.c deleted file mode 100644 index 04b81d2..0000000 --- a/phobos/etc/c/zlib/adler32.c +++ /dev/null @@ -1,164 +0,0 @@ -/* adler32.c -- compute the Adler-32 checksum of a data stream - * Copyright (C) 1995-2011, 2016 Mark Adler - * For conditions of distribution and use, see copyright notice in zlib.h - */ - -/* @(#) $Id$ */ - -#include "zutil.h" - -#define BASE 65521U /* largest prime smaller than 65536 */ -#define NMAX 5552 -/* NMAX is the largest n such that 255n(n+1)/2 + (n+1)(BASE-1) <= 2^32-1 */ - -#define DO1(buf,i) {adler += (buf)[i]; sum2 += adler;} -#define DO2(buf,i) DO1(buf,i); DO1(buf,i+1); -#define DO4(buf,i) DO2(buf,i); DO2(buf,i+2); -#define DO8(buf,i) DO4(buf,i); DO4(buf,i+4); -#define DO16(buf) DO8(buf,0); DO8(buf,8); - -/* use NO_DIVIDE if your processor does not do division in hardware -- - try it both ways to see which is faster */ -#ifdef NO_DIVIDE -/* note that this assumes BASE is 65521, where 65536 % 65521 == 15 - (thank you to John Reiser for pointing this out) */ -# define CHOP(a) \ - do { \ - unsigned long tmp = a >> 16; \ - a &= 0xffffUL; \ - a += (tmp << 4) - tmp; \ - } while (0) -# define MOD28(a) \ - do { \ - CHOP(a); \ - if (a >= BASE) a -= BASE; \ - } while (0) -# define MOD(a) \ - do { \ - CHOP(a); \ - MOD28(a); \ - } while (0) -# define MOD63(a) \ - do { /* this assumes a is not negative */ \ - z_off64_t tmp = a >> 32; \ - a &= 0xffffffffL; \ - a += (tmp << 8) - (tmp << 5) + tmp; \ - tmp = a >> 16; \ - a &= 0xffffL; \ - a += (tmp << 4) - tmp; \ - tmp = a >> 16; \ - a &= 0xffffL; \ - a += (tmp << 4) - tmp; \ - if (a >= BASE) a -= BASE; \ - } while (0) -#else -# define MOD(a) a %= BASE -# define MOD28(a) a %= BASE -# define MOD63(a) a %= BASE -#endif - -/* ========================================================================= */ -uLong ZEXPORT adler32_z(uLong adler, const Bytef *buf, z_size_t len) { - unsigned long sum2; - unsigned n; - - /* split Adler-32 into component sums */ - sum2 = (adler >> 16) & 0xffff; - adler &= 0xffff; - - /* in case user likes doing a byte at a time, keep it fast */ - if (len == 1) { - adler += buf[0]; - if (adler >= BASE) - adler -= BASE; - sum2 += adler; - if (sum2 >= BASE) - sum2 -= BASE; - return adler | (sum2 << 16); - } - - /* initial Adler-32 value (deferred check for len == 1 speed) */ - if (buf == Z_NULL) - return 1L; - - /* in case short lengths are provided, keep it somewhat fast */ - if (len < 16) { - while (len--) { - adler += *buf++; - sum2 += adler; - } - if (adler >= BASE) - adler -= BASE; - MOD28(sum2); /* only added so many BASE's */ - return adler | (sum2 << 16); - } - - /* do length NMAX blocks -- requires just one modulo operation */ - while (len >= NMAX) { - len -= NMAX; - n = NMAX / 16; /* NMAX is divisible by 16 */ - do { - DO16(buf); /* 16 sums unrolled */ - buf += 16; - } while (--n); - MOD(adler); - MOD(sum2); - } - - /* do remaining bytes (less than NMAX, still just one modulo) */ - if (len) { /* avoid modulos if none remaining */ - while (len >= 16) { - len -= 16; - DO16(buf); - buf += 16; - } - while (len--) { - adler += *buf++; - sum2 += adler; - } - MOD(adler); - MOD(sum2); - } - - /* return recombined sums */ - return adler | (sum2 << 16); -} - -/* ========================================================================= */ -uLong ZEXPORT adler32(uLong adler, const Bytef *buf, uInt len) { - return adler32_z(adler, buf, len); -} - -/* ========================================================================= */ -local uLong adler32_combine_(uLong adler1, uLong adler2, z_off64_t len2) { - unsigned long sum1; - unsigned long sum2; - unsigned rem; - - /* for negative len, return invalid adler32 as a clue for debugging */ - if (len2 < 0) - return 0xffffffffUL; - - /* the derivation of this formula is left as an exercise for the reader */ - MOD63(len2); /* assumes len2 >= 0 */ - rem = (unsigned)len2; - sum1 = adler1 & 0xffff; - sum2 = rem * sum1; - MOD(sum2); - sum1 += (adler2 & 0xffff) + BASE - 1; - sum2 += ((adler1 >> 16) & 0xffff) + ((adler2 >> 16) & 0xffff) + BASE - rem; - if (sum1 >= BASE) sum1 -= BASE; - if (sum1 >= BASE) sum1 -= BASE; - if (sum2 >= ((unsigned long)BASE << 1)) sum2 -= ((unsigned long)BASE << 1); - if (sum2 >= BASE) sum2 -= BASE; - return sum1 | (sum2 << 16); -} - -/* ========================================================================= */ -uLong ZEXPORT adler32_combine(uLong adler1, uLong adler2, z_off_t len2) { - return adler32_combine_(adler1, adler2, len2); -} - -uLong ZEXPORT adler32_combine64(uLong adler1, uLong adler2, z_off64_t len2) { - return adler32_combine_(adler1, adler2, len2); -} diff --git a/phobos/etc/c/zlib/compress.c b/phobos/etc/c/zlib/compress.c deleted file mode 100644 index f43bacf..0000000 --- a/phobos/etc/c/zlib/compress.c +++ /dev/null @@ -1,75 +0,0 @@ -/* compress.c -- compress a memory buffer - * Copyright (C) 1995-2005, 2014, 2016 Jean-loup Gailly, Mark Adler - * For conditions of distribution and use, see copyright notice in zlib.h - */ - -/* @(#) $Id$ */ - -#define ZLIB_INTERNAL -#include "zlib.h" - -/* =========================================================================== - Compresses the source buffer into the destination buffer. The level - parameter has the same meaning as in deflateInit. sourceLen is the byte - length of the source buffer. Upon entry, destLen is the total size of the - destination buffer, which must be at least 0.1% larger than sourceLen plus - 12 bytes. Upon exit, destLen is the actual size of the compressed buffer. - - compress2 returns Z_OK if success, Z_MEM_ERROR if there was not enough - memory, Z_BUF_ERROR if there was not enough room in the output buffer, - Z_STREAM_ERROR if the level parameter is invalid. -*/ -int ZEXPORT compress2(Bytef *dest, uLongf *destLen, const Bytef *source, - uLong sourceLen, int level) { - z_stream stream; - int err; - const uInt max = (uInt)-1; - uLong left; - - left = *destLen; - *destLen = 0; - - stream.zalloc = (alloc_func)0; - stream.zfree = (free_func)0; - stream.opaque = (voidpf)0; - - err = deflateInit(&stream, level); - if (err != Z_OK) return err; - - stream.next_out = dest; - stream.avail_out = 0; - stream.next_in = (z_const Bytef *)source; - stream.avail_in = 0; - - do { - if (stream.avail_out == 0) { - stream.avail_out = left > (uLong)max ? max : (uInt)left; - left -= stream.avail_out; - } - if (stream.avail_in == 0) { - stream.avail_in = sourceLen > (uLong)max ? max : (uInt)sourceLen; - sourceLen -= stream.avail_in; - } - err = deflate(&stream, sourceLen ? Z_NO_FLUSH : Z_FINISH); - } while (err == Z_OK); - - *destLen = stream.total_out; - deflateEnd(&stream); - return err == Z_STREAM_END ? Z_OK : err; -} - -/* =========================================================================== - */ -int ZEXPORT compress(Bytef *dest, uLongf *destLen, const Bytef *source, - uLong sourceLen) { - return compress2(dest, destLen, source, sourceLen, Z_DEFAULT_COMPRESSION); -} - -/* =========================================================================== - If the default memLevel or windowBits for deflateInit() is changed, then - this function needs to be updated. - */ -uLong ZEXPORT compressBound(uLong sourceLen) { - return sourceLen + (sourceLen >> 12) + (sourceLen >> 14) + - (sourceLen >> 25) + 13; -} diff --git a/phobos/etc/c/zlib/crc32.c b/phobos/etc/c/zlib/crc32.c deleted file mode 100644 index 6c38f5c..0000000 --- a/phobos/etc/c/zlib/crc32.c +++ /dev/null @@ -1,1049 +0,0 @@ -/* crc32.c -- compute the CRC-32 of a data stream - * Copyright (C) 1995-2022 Mark Adler - * For conditions of distribution and use, see copyright notice in zlib.h - * - * This interleaved implementation of a CRC makes use of pipelined multiple - * arithmetic-logic units, commonly found in modern CPU cores. It is due to - * Kadatch and Jenkins (2010). See doc/crc-doc.1.0.pdf in this distribution. - */ - -/* @(#) $Id$ */ - -/* - Note on the use of DYNAMIC_CRC_TABLE: there is no mutex or semaphore - protection on the static variables used to control the first-use generation - of the crc tables. Therefore, if you #define DYNAMIC_CRC_TABLE, you should - first call get_crc_table() to initialize the tables before allowing more than - one thread to use crc32(). - - MAKECRCH can be #defined to write out crc32.h. A main() routine is also - produced, so that this one source file can be compiled to an executable. - */ - -#ifdef MAKECRCH -# include -# ifndef DYNAMIC_CRC_TABLE -# define DYNAMIC_CRC_TABLE -# endif /* !DYNAMIC_CRC_TABLE */ -#endif /* MAKECRCH */ - -#include "zutil.h" /* for Z_U4, Z_U8, z_crc_t, and FAR definitions */ - - /* - A CRC of a message is computed on N braids of words in the message, where - each word consists of W bytes (4 or 8). If N is 3, for example, then three - running sparse CRCs are calculated respectively on each braid, at these - indices in the array of words: 0, 3, 6, ..., 1, 4, 7, ..., and 2, 5, 8, ... - This is done starting at a word boundary, and continues until as many blocks - of N * W bytes as are available have been processed. The results are combined - into a single CRC at the end. For this code, N must be in the range 1..6 and - W must be 4 or 8. The upper limit on N can be increased if desired by adding - more #if blocks, extending the patterns apparent in the code. In addition, - crc32.h would need to be regenerated, if the maximum N value is increased. - - N and W are chosen empirically by benchmarking the execution time on a given - processor. The choices for N and W below were based on testing on Intel Kaby - Lake i7, AMD Ryzen 7, ARM Cortex-A57, Sparc64-VII, PowerPC POWER9, and MIPS64 - Octeon II processors. The Intel, AMD, and ARM processors were all fastest - with N=5, W=8. The Sparc, PowerPC, and MIPS64 were all fastest at N=5, W=4. - They were all tested with either gcc or clang, all using the -O3 optimization - level. Your mileage may vary. - */ - -/* Define N */ -#ifdef Z_TESTN -# define N Z_TESTN -#else -# define N 5 -#endif -#if N < 1 || N > 6 -# error N must be in 1..6 -#endif - -/* - z_crc_t must be at least 32 bits. z_word_t must be at least as long as - z_crc_t. It is assumed here that z_word_t is either 32 bits or 64 bits, and - that bytes are eight bits. - */ - -/* - Define W and the associated z_word_t type. If W is not defined, then a - braided calculation is not used, and the associated tables and code are not - compiled. - */ -#ifdef Z_TESTW -# if Z_TESTW-1 != -1 -# define W Z_TESTW -# endif -#else -# ifdef MAKECRCH -# define W 8 /* required for MAKECRCH */ -# else -# if defined(__x86_64__) || defined(__aarch64__) -# define W 8 -# else -# define W 4 -# endif -# endif -#endif -#ifdef W -# if W == 8 && defined(Z_U8) - typedef Z_U8 z_word_t; -# elif defined(Z_U4) -# undef W -# define W 4 - typedef Z_U4 z_word_t; -# else -# undef W -# endif -#endif - -/* If available, use the ARM processor CRC32 instruction. */ -#if defined(__aarch64__) && defined(__ARM_FEATURE_CRC32) && W == 8 -# define ARMCRC32 -#endif - -#if defined(W) && (!defined(ARMCRC32) || defined(DYNAMIC_CRC_TABLE)) -/* - Swap the bytes in a z_word_t to convert between little and big endian. Any - self-respecting compiler will optimize this to a single machine byte-swap - instruction, if one is available. This assumes that word_t is either 32 bits - or 64 bits. - */ -local z_word_t byte_swap(z_word_t word) { -# if W == 8 - return - (word & 0xff00000000000000) >> 56 | - (word & 0xff000000000000) >> 40 | - (word & 0xff0000000000) >> 24 | - (word & 0xff00000000) >> 8 | - (word & 0xff000000) << 8 | - (word & 0xff0000) << 24 | - (word & 0xff00) << 40 | - (word & 0xff) << 56; -# else /* W == 4 */ - return - (word & 0xff000000) >> 24 | - (word & 0xff0000) >> 8 | - (word & 0xff00) << 8 | - (word & 0xff) << 24; -# endif -} -#endif - -#ifdef DYNAMIC_CRC_TABLE -/* ========================================================================= - * Table of powers of x for combining CRC-32s, filled in by make_crc_table() - * below. - */ - local z_crc_t FAR x2n_table[32]; -#else -/* ========================================================================= - * Tables for byte-wise and braided CRC-32 calculations, and a table of powers - * of x for combining CRC-32s, all made by make_crc_table(). - */ -# include "crc32.h" -#endif - -/* CRC polynomial. */ -#define POLY 0xedb88320 /* p(x) reflected, with x^32 implied */ - -/* - Return a(x) multiplied by b(x) modulo p(x), where p(x) is the CRC polynomial, - reflected. For speed, this requires that a not be zero. - */ -local z_crc_t multmodp(z_crc_t a, z_crc_t b) { - z_crc_t m, p; - - m = (z_crc_t)1 << 31; - p = 0; - for (;;) { - if (a & m) { - p ^= b; - if ((a & (m - 1)) == 0) - break; - } - m >>= 1; - b = b & 1 ? (b >> 1) ^ POLY : b >> 1; - } - return p; -} - -/* - Return x^(n * 2^k) modulo p(x). Requires that x2n_table[] has been - initialized. - */ -local z_crc_t x2nmodp(z_off64_t n, unsigned k) { - z_crc_t p; - - p = (z_crc_t)1 << 31; /* x^0 == 1 */ - while (n) { - if (n & 1) - p = multmodp(x2n_table[k & 31], p); - n >>= 1; - k++; - } - return p; -} - -#ifdef DYNAMIC_CRC_TABLE -/* ========================================================================= - * Build the tables for byte-wise and braided CRC-32 calculations, and a table - * of powers of x for combining CRC-32s. - */ -local z_crc_t FAR crc_table[256]; -#ifdef W - local z_word_t FAR crc_big_table[256]; - local z_crc_t FAR crc_braid_table[W][256]; - local z_word_t FAR crc_braid_big_table[W][256]; - local void braid(z_crc_t [][256], z_word_t [][256], int, int); -#endif -#ifdef MAKECRCH - local void write_table(FILE *, const z_crc_t FAR *, int); - local void write_table32hi(FILE *, const z_word_t FAR *, int); - local void write_table64(FILE *, const z_word_t FAR *, int); -#endif /* MAKECRCH */ - -/* - Define a once() function depending on the availability of atomics. If this is - compiled with DYNAMIC_CRC_TABLE defined, and if CRCs will be computed in - multiple threads, and if atomics are not available, then get_crc_table() must - be called to initialize the tables and must return before any threads are - allowed to compute or combine CRCs. - */ - -/* Definition of once functionality. */ -typedef struct once_s once_t; - -/* Check for the availability of atomics. */ -#if defined(__STDC__) && __STDC_VERSION__ >= 201112L && \ - !defined(__STDC_NO_ATOMICS__) - -#include - -/* Structure for once(), which must be initialized with ONCE_INIT. */ -struct once_s { - atomic_flag begun; - atomic_int done; -}; -#define ONCE_INIT {ATOMIC_FLAG_INIT, 0} - -/* - Run the provided init() function exactly once, even if multiple threads - invoke once() at the same time. The state must be a once_t initialized with - ONCE_INIT. - */ -local void once(once_t *state, void (*init)(void)) { - if (!atomic_load(&state->done)) { - if (atomic_flag_test_and_set(&state->begun)) - while (!atomic_load(&state->done)) - ; - else { - init(); - atomic_store(&state->done, 1); - } - } -} - -#else /* no atomics */ - -/* Structure for once(), which must be initialized with ONCE_INIT. */ -struct once_s { - volatile int begun; - volatile int done; -}; -#define ONCE_INIT {0, 0} - -/* Test and set. Alas, not atomic, but tries to minimize the period of - vulnerability. */ -local int test_and_set(int volatile *flag) { - int was; - - was = *flag; - *flag = 1; - return was; -} - -/* Run the provided init() function once. This is not thread-safe. */ -local void once(once_t *state, void (*init)(void)) { - if (!state->done) { - if (test_and_set(&state->begun)) - while (!state->done) - ; - else { - init(); - state->done = 1; - } - } -} - -#endif - -/* State for once(). */ -local once_t made = ONCE_INIT; - -/* - Generate tables for a byte-wise 32-bit CRC calculation on the polynomial: - x^32+x^26+x^23+x^22+x^16+x^12+x^11+x^10+x^8+x^7+x^5+x^4+x^2+x+1. - - Polynomials over GF(2) are represented in binary, one bit per coefficient, - with the lowest powers in the most significant bit. Then adding polynomials - is just exclusive-or, and multiplying a polynomial by x is a right shift by - one. If we call the above polynomial p, and represent a byte as the - polynomial q, also with the lowest power in the most significant bit (so the - byte 0xb1 is the polynomial x^7+x^3+x^2+1), then the CRC is (q*x^32) mod p, - where a mod b means the remainder after dividing a by b. - - This calculation is done using the shift-register method of multiplying and - taking the remainder. The register is initialized to zero, and for each - incoming bit, x^32 is added mod p to the register if the bit is a one (where - x^32 mod p is p+x^32 = x^26+...+1), and the register is multiplied mod p by x - (which is shifting right by one and adding x^32 mod p if the bit shifted out - is a one). We start with the highest power (least significant bit) of q and - repeat for all eight bits of q. - - The table is simply the CRC of all possible eight bit values. This is all the - information needed to generate CRCs on data a byte at a time for all - combinations of CRC register values and incoming bytes. - */ - -local void make_crc_table(void) { - unsigned i, j, n; - z_crc_t p; - - /* initialize the CRC of bytes tables */ - for (i = 0; i < 256; i++) { - p = i; - for (j = 0; j < 8; j++) - p = p & 1 ? (p >> 1) ^ POLY : p >> 1; - crc_table[i] = p; -#ifdef W - crc_big_table[i] = byte_swap(p); -#endif - } - - /* initialize the x^2^n mod p(x) table */ - p = (z_crc_t)1 << 30; /* x^1 */ - x2n_table[0] = p; - for (n = 1; n < 32; n++) - x2n_table[n] = p = multmodp(p, p); - -#ifdef W - /* initialize the braiding tables -- needs x2n_table[] */ - braid(crc_braid_table, crc_braid_big_table, N, W); -#endif - -#ifdef MAKECRCH - { - /* - The crc32.h header file contains tables for both 32-bit and 64-bit - z_word_t's, and so requires a 64-bit type be available. In that case, - z_word_t must be defined to be 64-bits. This code then also generates - and writes out the tables for the case that z_word_t is 32 bits. - */ -#if !defined(W) || W != 8 -# error Need a 64-bit integer type in order to generate crc32.h. -#endif - FILE *out; - int k, n; - z_crc_t ltl[8][256]; - z_word_t big[8][256]; - - out = fopen("crc32.h", "w"); - if (out == NULL) return; - - /* write out little-endian CRC table to crc32.h */ - fprintf(out, - "/* crc32.h -- tables for rapid CRC calculation\n" - " * Generated automatically by crc32.c\n */\n" - "\n" - "local const z_crc_t FAR crc_table[] = {\n" - " "); - write_table(out, crc_table, 256); - fprintf(out, - "};\n"); - - /* write out big-endian CRC table for 64-bit z_word_t to crc32.h */ - fprintf(out, - "\n" - "#ifdef W\n" - "\n" - "#if W == 8\n" - "\n" - "local const z_word_t FAR crc_big_table[] = {\n" - " "); - write_table64(out, crc_big_table, 256); - fprintf(out, - "};\n"); - - /* write out big-endian CRC table for 32-bit z_word_t to crc32.h */ - fprintf(out, - "\n" - "#else /* W == 4 */\n" - "\n" - "local const z_word_t FAR crc_big_table[] = {\n" - " "); - write_table32hi(out, crc_big_table, 256); - fprintf(out, - "};\n" - "\n" - "#endif\n"); - - /* write out braid tables for each value of N */ - for (n = 1; n <= 6; n++) { - fprintf(out, - "\n" - "#if N == %d\n", n); - - /* compute braid tables for this N and 64-bit word_t */ - braid(ltl, big, n, 8); - - /* write out braid tables for 64-bit z_word_t to crc32.h */ - fprintf(out, - "\n" - "#if W == 8\n" - "\n" - "local const z_crc_t FAR crc_braid_table[][256] = {\n"); - for (k = 0; k < 8; k++) { - fprintf(out, " {"); - write_table(out, ltl[k], 256); - fprintf(out, "}%s", k < 7 ? ",\n" : ""); - } - fprintf(out, - "};\n" - "\n" - "local const z_word_t FAR crc_braid_big_table[][256] = {\n"); - for (k = 0; k < 8; k++) { - fprintf(out, " {"); - write_table64(out, big[k], 256); - fprintf(out, "}%s", k < 7 ? ",\n" : ""); - } - fprintf(out, - "};\n"); - - /* compute braid tables for this N and 32-bit word_t */ - braid(ltl, big, n, 4); - - /* write out braid tables for 32-bit z_word_t to crc32.h */ - fprintf(out, - "\n" - "#else /* W == 4 */\n" - "\n" - "local const z_crc_t FAR crc_braid_table[][256] = {\n"); - for (k = 0; k < 4; k++) { - fprintf(out, " {"); - write_table(out, ltl[k], 256); - fprintf(out, "}%s", k < 3 ? ",\n" : ""); - } - fprintf(out, - "};\n" - "\n" - "local const z_word_t FAR crc_braid_big_table[][256] = {\n"); - for (k = 0; k < 4; k++) { - fprintf(out, " {"); - write_table32hi(out, big[k], 256); - fprintf(out, "}%s", k < 3 ? ",\n" : ""); - } - fprintf(out, - "};\n" - "\n" - "#endif\n" - "\n" - "#endif\n"); - } - fprintf(out, - "\n" - "#endif\n"); - - /* write out zeros operator table to crc32.h */ - fprintf(out, - "\n" - "local const z_crc_t FAR x2n_table[] = {\n" - " "); - write_table(out, x2n_table, 32); - fprintf(out, - "};\n"); - fclose(out); - } -#endif /* MAKECRCH */ -} - -#ifdef MAKECRCH - -/* - Write the 32-bit values in table[0..k-1] to out, five per line in - hexadecimal separated by commas. - */ -local void write_table(FILE *out, const z_crc_t FAR *table, int k) { - int n; - - for (n = 0; n < k; n++) - fprintf(out, "%s0x%08lx%s", n == 0 || n % 5 ? "" : " ", - (unsigned long)(table[n]), - n == k - 1 ? "" : (n % 5 == 4 ? ",\n" : ", ")); -} - -/* - Write the high 32-bits of each value in table[0..k-1] to out, five per line - in hexadecimal separated by commas. - */ -local void write_table32hi(FILE *out, const z_word_t FAR *table, int k) { - int n; - - for (n = 0; n < k; n++) - fprintf(out, "%s0x%08lx%s", n == 0 || n % 5 ? "" : " ", - (unsigned long)(table[n] >> 32), - n == k - 1 ? "" : (n % 5 == 4 ? ",\n" : ", ")); -} - -/* - Write the 64-bit values in table[0..k-1] to out, three per line in - hexadecimal separated by commas. This assumes that if there is a 64-bit - type, then there is also a long long integer type, and it is at least 64 - bits. If not, then the type cast and format string can be adjusted - accordingly. - */ -local void write_table64(FILE *out, const z_word_t FAR *table, int k) { - int n; - - for (n = 0; n < k; n++) - fprintf(out, "%s0x%016llx%s", n == 0 || n % 3 ? "" : " ", - (unsigned long long)(table[n]), - n == k - 1 ? "" : (n % 3 == 2 ? ",\n" : ", ")); -} - -/* Actually do the deed. */ -int main(void) { - make_crc_table(); - return 0; -} - -#endif /* MAKECRCH */ - -#ifdef W -/* - Generate the little and big-endian braid tables for the given n and z_word_t - size w. Each array must have room for w blocks of 256 elements. - */ -local void braid(z_crc_t ltl[][256], z_word_t big[][256], int n, int w) { - int k; - z_crc_t i, p, q; - for (k = 0; k < w; k++) { - p = x2nmodp((n * w + 3 - k) << 3, 0); - ltl[k][0] = 0; - big[w - 1 - k][0] = 0; - for (i = 1; i < 256; i++) { - ltl[k][i] = q = multmodp(i << 24, p); - big[w - 1 - k][i] = byte_swap(q); - } - } -} -#endif - -#endif /* DYNAMIC_CRC_TABLE */ - -/* ========================================================================= - * This function can be used by asm versions of crc32(), and to force the - * generation of the CRC tables in a threaded application. - */ -const z_crc_t FAR * ZEXPORT get_crc_table(void) { -#ifdef DYNAMIC_CRC_TABLE - once(&made, make_crc_table); -#endif /* DYNAMIC_CRC_TABLE */ - return (const z_crc_t FAR *)crc_table; -} - -/* ========================================================================= - * Use ARM machine instructions if available. This will compute the CRC about - * ten times faster than the braided calculation. This code does not check for - * the presence of the CRC instruction at run time. __ARM_FEATURE_CRC32 will - * only be defined if the compilation specifies an ARM processor architecture - * that has the instructions. For example, compiling with -march=armv8.1-a or - * -march=armv8-a+crc, or -march=native if the compile machine has the crc32 - * instructions. - */ -#ifdef ARMCRC32 - -/* - Constants empirically determined to maximize speed. These values are from - measurements on a Cortex-A57. Your mileage may vary. - */ -#define Z_BATCH 3990 /* number of words in a batch */ -#define Z_BATCH_ZEROS 0xa10d3d0c /* computed from Z_BATCH = 3990 */ -#define Z_BATCH_MIN 800 /* fewest words in a final batch */ - -unsigned long ZEXPORT crc32_z(unsigned long crc, const unsigned char FAR *buf, - z_size_t len) { - z_crc_t val; - z_word_t crc1, crc2; - const z_word_t *word; - z_word_t val0, val1, val2; - z_size_t last, last2, i; - z_size_t num; - - /* Return initial CRC, if requested. */ - if (buf == Z_NULL) return 0; - -#ifdef DYNAMIC_CRC_TABLE - once(&made, make_crc_table); -#endif /* DYNAMIC_CRC_TABLE */ - - /* Pre-condition the CRC */ - crc = (~crc) & 0xffffffff; - - /* Compute the CRC up to a word boundary. */ - while (len && ((z_size_t)buf & 7) != 0) { - len--; - val = *buf++; - __asm__ volatile("crc32b %w0, %w0, %w1" : "+r"(crc) : "r"(val)); - } - - /* Prepare to compute the CRC on full 64-bit words word[0..num-1]. */ - word = (z_word_t const *)buf; - num = len >> 3; - len &= 7; - - /* Do three interleaved CRCs to realize the throughput of one crc32x - instruction per cycle. Each CRC is calculated on Z_BATCH words. The - three CRCs are combined into a single CRC after each set of batches. */ - while (num >= 3 * Z_BATCH) { - crc1 = 0; - crc2 = 0; - for (i = 0; i < Z_BATCH; i++) { - val0 = word[i]; - val1 = word[i + Z_BATCH]; - val2 = word[i + 2 * Z_BATCH]; - __asm__ volatile("crc32x %w0, %w0, %x1" : "+r"(crc) : "r"(val0)); - __asm__ volatile("crc32x %w0, %w0, %x1" : "+r"(crc1) : "r"(val1)); - __asm__ volatile("crc32x %w0, %w0, %x1" : "+r"(crc2) : "r"(val2)); - } - word += 3 * Z_BATCH; - num -= 3 * Z_BATCH; - crc = multmodp(Z_BATCH_ZEROS, crc) ^ crc1; - crc = multmodp(Z_BATCH_ZEROS, crc) ^ crc2; - } - - /* Do one last smaller batch with the remaining words, if there are enough - to pay for the combination of CRCs. */ - last = num / 3; - if (last >= Z_BATCH_MIN) { - last2 = last << 1; - crc1 = 0; - crc2 = 0; - for (i = 0; i < last; i++) { - val0 = word[i]; - val1 = word[i + last]; - val2 = word[i + last2]; - __asm__ volatile("crc32x %w0, %w0, %x1" : "+r"(crc) : "r"(val0)); - __asm__ volatile("crc32x %w0, %w0, %x1" : "+r"(crc1) : "r"(val1)); - __asm__ volatile("crc32x %w0, %w0, %x1" : "+r"(crc2) : "r"(val2)); - } - word += 3 * last; - num -= 3 * last; - val = x2nmodp(last, 6); - crc = multmodp(val, crc) ^ crc1; - crc = multmodp(val, crc) ^ crc2; - } - - /* Compute the CRC on any remaining words. */ - for (i = 0; i < num; i++) { - val0 = word[i]; - __asm__ volatile("crc32x %w0, %w0, %x1" : "+r"(crc) : "r"(val0)); - } - word += num; - - /* Complete the CRC on any remaining bytes. */ - buf = (const unsigned char FAR *)word; - while (len) { - len--; - val = *buf++; - __asm__ volatile("crc32b %w0, %w0, %w1" : "+r"(crc) : "r"(val)); - } - - /* Return the CRC, post-conditioned. */ - return crc ^ 0xffffffff; -} - -#else - -#ifdef W - -/* - Return the CRC of the W bytes in the word_t data, taking the - least-significant byte of the word as the first byte of data, without any pre - or post conditioning. This is used to combine the CRCs of each braid. - */ -local z_crc_t crc_word(z_word_t data) { - int k; - for (k = 0; k < W; k++) - data = (data >> 8) ^ crc_table[data & 0xff]; - return (z_crc_t)data; -} - -local z_word_t crc_word_big(z_word_t data) { - int k; - for (k = 0; k < W; k++) - data = (data << 8) ^ - crc_big_table[(data >> ((W - 1) << 3)) & 0xff]; - return data; -} - -#endif - -/* ========================================================================= */ -unsigned long ZEXPORT crc32_z(unsigned long crc, const unsigned char FAR *buf, - z_size_t len) { - /* Return initial CRC, if requested. */ - if (buf == Z_NULL) return 0; - -#ifdef DYNAMIC_CRC_TABLE - once(&made, make_crc_table); -#endif /* DYNAMIC_CRC_TABLE */ - - /* Pre-condition the CRC */ - crc = (~crc) & 0xffffffff; - -#ifdef W - - /* If provided enough bytes, do a braided CRC calculation. */ - if (len >= N * W + W - 1) { - z_size_t blks; - z_word_t const *words; - unsigned endian; - int k; - - /* Compute the CRC up to a z_word_t boundary. */ - while (len && ((z_size_t)buf & (W - 1)) != 0) { - len--; - crc = (crc >> 8) ^ crc_table[(crc ^ *buf++) & 0xff]; - } - - /* Compute the CRC on as many N z_word_t blocks as are available. */ - blks = len / (N * W); - len -= blks * N * W; - words = (z_word_t const *)buf; - - /* Do endian check at execution time instead of compile time, since ARM - processors can change the endianness at execution time. If the - compiler knows what the endianness will be, it can optimize out the - check and the unused branch. */ - endian = 1; - if (*(unsigned char *)&endian) { - /* Little endian. */ - - z_crc_t crc0; - z_word_t word0; -#if N > 1 - z_crc_t crc1; - z_word_t word1; -#if N > 2 - z_crc_t crc2; - z_word_t word2; -#if N > 3 - z_crc_t crc3; - z_word_t word3; -#if N > 4 - z_crc_t crc4; - z_word_t word4; -#if N > 5 - z_crc_t crc5; - z_word_t word5; -#endif -#endif -#endif -#endif -#endif - - /* Initialize the CRC for each braid. */ - crc0 = crc; -#if N > 1 - crc1 = 0; -#if N > 2 - crc2 = 0; -#if N > 3 - crc3 = 0; -#if N > 4 - crc4 = 0; -#if N > 5 - crc5 = 0; -#endif -#endif -#endif -#endif -#endif - - /* - Process the first blks-1 blocks, computing the CRCs on each braid - independently. - */ - while (--blks) { - /* Load the word for each braid into registers. */ - word0 = crc0 ^ words[0]; -#if N > 1 - word1 = crc1 ^ words[1]; -#if N > 2 - word2 = crc2 ^ words[2]; -#if N > 3 - word3 = crc3 ^ words[3]; -#if N > 4 - word4 = crc4 ^ words[4]; -#if N > 5 - word5 = crc5 ^ words[5]; -#endif -#endif -#endif -#endif -#endif - words += N; - - /* Compute and update the CRC for each word. The loop should - get unrolled. */ - crc0 = crc_braid_table[0][word0 & 0xff]; -#if N > 1 - crc1 = crc_braid_table[0][word1 & 0xff]; -#if N > 2 - crc2 = crc_braid_table[0][word2 & 0xff]; -#if N > 3 - crc3 = crc_braid_table[0][word3 & 0xff]; -#if N > 4 - crc4 = crc_braid_table[0][word4 & 0xff]; -#if N > 5 - crc5 = crc_braid_table[0][word5 & 0xff]; -#endif -#endif -#endif -#endif -#endif - for (k = 1; k < W; k++) { - crc0 ^= crc_braid_table[k][(word0 >> (k << 3)) & 0xff]; -#if N > 1 - crc1 ^= crc_braid_table[k][(word1 >> (k << 3)) & 0xff]; -#if N > 2 - crc2 ^= crc_braid_table[k][(word2 >> (k << 3)) & 0xff]; -#if N > 3 - crc3 ^= crc_braid_table[k][(word3 >> (k << 3)) & 0xff]; -#if N > 4 - crc4 ^= crc_braid_table[k][(word4 >> (k << 3)) & 0xff]; -#if N > 5 - crc5 ^= crc_braid_table[k][(word5 >> (k << 3)) & 0xff]; -#endif -#endif -#endif -#endif -#endif - } - } - - /* - Process the last block, combining the CRCs of the N braids at the - same time. - */ - crc = crc_word(crc0 ^ words[0]); -#if N > 1 - crc = crc_word(crc1 ^ words[1] ^ crc); -#if N > 2 - crc = crc_word(crc2 ^ words[2] ^ crc); -#if N > 3 - crc = crc_word(crc3 ^ words[3] ^ crc); -#if N > 4 - crc = crc_word(crc4 ^ words[4] ^ crc); -#if N > 5 - crc = crc_word(crc5 ^ words[5] ^ crc); -#endif -#endif -#endif -#endif -#endif - words += N; - } - else { - /* Big endian. */ - - z_word_t crc0, word0, comb; -#if N > 1 - z_word_t crc1, word1; -#if N > 2 - z_word_t crc2, word2; -#if N > 3 - z_word_t crc3, word3; -#if N > 4 - z_word_t crc4, word4; -#if N > 5 - z_word_t crc5, word5; -#endif -#endif -#endif -#endif -#endif - - /* Initialize the CRC for each braid. */ - crc0 = byte_swap(crc); -#if N > 1 - crc1 = 0; -#if N > 2 - crc2 = 0; -#if N > 3 - crc3 = 0; -#if N > 4 - crc4 = 0; -#if N > 5 - crc5 = 0; -#endif -#endif -#endif -#endif -#endif - - /* - Process the first blks-1 blocks, computing the CRCs on each braid - independently. - */ - while (--blks) { - /* Load the word for each braid into registers. */ - word0 = crc0 ^ words[0]; -#if N > 1 - word1 = crc1 ^ words[1]; -#if N > 2 - word2 = crc2 ^ words[2]; -#if N > 3 - word3 = crc3 ^ words[3]; -#if N > 4 - word4 = crc4 ^ words[4]; -#if N > 5 - word5 = crc5 ^ words[5]; -#endif -#endif -#endif -#endif -#endif - words += N; - - /* Compute and update the CRC for each word. The loop should - get unrolled. */ - crc0 = crc_braid_big_table[0][word0 & 0xff]; -#if N > 1 - crc1 = crc_braid_big_table[0][word1 & 0xff]; -#if N > 2 - crc2 = crc_braid_big_table[0][word2 & 0xff]; -#if N > 3 - crc3 = crc_braid_big_table[0][word3 & 0xff]; -#if N > 4 - crc4 = crc_braid_big_table[0][word4 & 0xff]; -#if N > 5 - crc5 = crc_braid_big_table[0][word5 & 0xff]; -#endif -#endif -#endif -#endif -#endif - for (k = 1; k < W; k++) { - crc0 ^= crc_braid_big_table[k][(word0 >> (k << 3)) & 0xff]; -#if N > 1 - crc1 ^= crc_braid_big_table[k][(word1 >> (k << 3)) & 0xff]; -#if N > 2 - crc2 ^= crc_braid_big_table[k][(word2 >> (k << 3)) & 0xff]; -#if N > 3 - crc3 ^= crc_braid_big_table[k][(word3 >> (k << 3)) & 0xff]; -#if N > 4 - crc4 ^= crc_braid_big_table[k][(word4 >> (k << 3)) & 0xff]; -#if N > 5 - crc5 ^= crc_braid_big_table[k][(word5 >> (k << 3)) & 0xff]; -#endif -#endif -#endif -#endif -#endif - } - } - - /* - Process the last block, combining the CRCs of the N braids at the - same time. - */ - comb = crc_word_big(crc0 ^ words[0]); -#if N > 1 - comb = crc_word_big(crc1 ^ words[1] ^ comb); -#if N > 2 - comb = crc_word_big(crc2 ^ words[2] ^ comb); -#if N > 3 - comb = crc_word_big(crc3 ^ words[3] ^ comb); -#if N > 4 - comb = crc_word_big(crc4 ^ words[4] ^ comb); -#if N > 5 - comb = crc_word_big(crc5 ^ words[5] ^ comb); -#endif -#endif -#endif -#endif -#endif - words += N; - crc = byte_swap(comb); - } - - /* - Update the pointer to the remaining bytes to process. - */ - buf = (unsigned char const *)words; - } - -#endif /* W */ - - /* Complete the computation of the CRC on any remaining bytes. */ - while (len >= 8) { - len -= 8; - crc = (crc >> 8) ^ crc_table[(crc ^ *buf++) & 0xff]; - crc = (crc >> 8) ^ crc_table[(crc ^ *buf++) & 0xff]; - crc = (crc >> 8) ^ crc_table[(crc ^ *buf++) & 0xff]; - crc = (crc >> 8) ^ crc_table[(crc ^ *buf++) & 0xff]; - crc = (crc >> 8) ^ crc_table[(crc ^ *buf++) & 0xff]; - crc = (crc >> 8) ^ crc_table[(crc ^ *buf++) & 0xff]; - crc = (crc >> 8) ^ crc_table[(crc ^ *buf++) & 0xff]; - crc = (crc >> 8) ^ crc_table[(crc ^ *buf++) & 0xff]; - } - while (len) { - len--; - crc = (crc >> 8) ^ crc_table[(crc ^ *buf++) & 0xff]; - } - - /* Return the CRC, post-conditioned. */ - return crc ^ 0xffffffff; -} - -#endif - -/* ========================================================================= */ -unsigned long ZEXPORT crc32(unsigned long crc, const unsigned char FAR *buf, - uInt len) { - return crc32_z(crc, buf, len); -} - -/* ========================================================================= */ -uLong ZEXPORT crc32_combine64(uLong crc1, uLong crc2, z_off64_t len2) { -#ifdef DYNAMIC_CRC_TABLE - once(&made, make_crc_table); -#endif /* DYNAMIC_CRC_TABLE */ - return multmodp(x2nmodp(len2, 3), crc1) ^ (crc2 & 0xffffffff); -} - -/* ========================================================================= */ -uLong ZEXPORT crc32_combine(uLong crc1, uLong crc2, z_off_t len2) { - return crc32_combine64(crc1, crc2, (z_off64_t)len2); -} - -/* ========================================================================= */ -uLong ZEXPORT crc32_combine_gen64(z_off64_t len2) { -#ifdef DYNAMIC_CRC_TABLE - once(&made, make_crc_table); -#endif /* DYNAMIC_CRC_TABLE */ - return x2nmodp(len2, 3); -} - -/* ========================================================================= */ -uLong ZEXPORT crc32_combine_gen(z_off_t len2) { - return crc32_combine_gen64((z_off64_t)len2); -} - -/* ========================================================================= */ -uLong ZEXPORT crc32_combine_op(uLong crc1, uLong crc2, uLong op) { - return multmodp(op, crc1) ^ (crc2 & 0xffffffff); -} diff --git a/phobos/etc/c/zlib/crc32.h b/phobos/etc/c/zlib/crc32.h deleted file mode 100644 index 137df68..0000000 --- a/phobos/etc/c/zlib/crc32.h +++ /dev/null @@ -1,9446 +0,0 @@ -/* crc32.h -- tables for rapid CRC calculation - * Generated automatically by crc32.c - */ - -local const z_crc_t FAR crc_table[] = { - 0x00000000, 0x77073096, 0xee0e612c, 0x990951ba, 0x076dc419, - 0x706af48f, 0xe963a535, 0x9e6495a3, 0x0edb8832, 0x79dcb8a4, - 0xe0d5e91e, 0x97d2d988, 0x09b64c2b, 0x7eb17cbd, 0xe7b82d07, - 0x90bf1d91, 0x1db71064, 0x6ab020f2, 0xf3b97148, 0x84be41de, - 0x1adad47d, 0x6ddde4eb, 0xf4d4b551, 0x83d385c7, 0x136c9856, - 0x646ba8c0, 0xfd62f97a, 0x8a65c9ec, 0x14015c4f, 0x63066cd9, - 0xfa0f3d63, 0x8d080df5, 0x3b6e20c8, 0x4c69105e, 0xd56041e4, - 0xa2677172, 0x3c03e4d1, 0x4b04d447, 0xd20d85fd, 0xa50ab56b, - 0x35b5a8fa, 0x42b2986c, 0xdbbbc9d6, 0xacbcf940, 0x32d86ce3, - 0x45df5c75, 0xdcd60dcf, 0xabd13d59, 0x26d930ac, 0x51de003a, - 0xc8d75180, 0xbfd06116, 0x21b4f4b5, 0x56b3c423, 0xcfba9599, - 0xb8bda50f, 0x2802b89e, 0x5f058808, 0xc60cd9b2, 0xb10be924, - 0x2f6f7c87, 0x58684c11, 0xc1611dab, 0xb6662d3d, 0x76dc4190, - 0x01db7106, 0x98d220bc, 0xefd5102a, 0x71b18589, 0x06b6b51f, - 0x9fbfe4a5, 0xe8b8d433, 0x7807c9a2, 0x0f00f934, 0x9609a88e, - 0xe10e9818, 0x7f6a0dbb, 0x086d3d2d, 0x91646c97, 0xe6635c01, - 0x6b6b51f4, 0x1c6c6162, 0x856530d8, 0xf262004e, 0x6c0695ed, - 0x1b01a57b, 0x8208f4c1, 0xf50fc457, 0x65b0d9c6, 0x12b7e950, - 0x8bbeb8ea, 0xfcb9887c, 0x62dd1ddf, 0x15da2d49, 0x8cd37cf3, - 0xfbd44c65, 0x4db26158, 0x3ab551ce, 0xa3bc0074, 0xd4bb30e2, - 0x4adfa541, 0x3dd895d7, 0xa4d1c46d, 0xd3d6f4fb, 0x4369e96a, - 0x346ed9fc, 0xad678846, 0xda60b8d0, 0x44042d73, 0x33031de5, - 0xaa0a4c5f, 0xdd0d7cc9, 0x5005713c, 0x270241aa, 0xbe0b1010, - 0xc90c2086, 0x5768b525, 0x206f85b3, 0xb966d409, 0xce61e49f, - 0x5edef90e, 0x29d9c998, 0xb0d09822, 0xc7d7a8b4, 0x59b33d17, - 0x2eb40d81, 0xb7bd5c3b, 0xc0ba6cad, 0xedb88320, 0x9abfb3b6, - 0x03b6e20c, 0x74b1d29a, 0xead54739, 0x9dd277af, 0x04db2615, - 0x73dc1683, 0xe3630b12, 0x94643b84, 0x0d6d6a3e, 0x7a6a5aa8, - 0xe40ecf0b, 0x9309ff9d, 0x0a00ae27, 0x7d079eb1, 0xf00f9344, - 0x8708a3d2, 0x1e01f268, 0x6906c2fe, 0xf762575d, 0x806567cb, - 0x196c3671, 0x6e6b06e7, 0xfed41b76, 0x89d32be0, 0x10da7a5a, - 0x67dd4acc, 0xf9b9df6f, 0x8ebeeff9, 0x17b7be43, 0x60b08ed5, - 0xd6d6a3e8, 0xa1d1937e, 0x38d8c2c4, 0x4fdff252, 0xd1bb67f1, - 0xa6bc5767, 0x3fb506dd, 0x48b2364b, 0xd80d2bda, 0xaf0a1b4c, - 0x36034af6, 0x41047a60, 0xdf60efc3, 0xa867df55, 0x316e8eef, - 0x4669be79, 0xcb61b38c, 0xbc66831a, 0x256fd2a0, 0x5268e236, - 0xcc0c7795, 0xbb0b4703, 0x220216b9, 0x5505262f, 0xc5ba3bbe, - 0xb2bd0b28, 0x2bb45a92, 0x5cb36a04, 0xc2d7ffa7, 0xb5d0cf31, - 0x2cd99e8b, 0x5bdeae1d, 0x9b64c2b0, 0xec63f226, 0x756aa39c, - 0x026d930a, 0x9c0906a9, 0xeb0e363f, 0x72076785, 0x05005713, - 0x95bf4a82, 0xe2b87a14, 0x7bb12bae, 0x0cb61b38, 0x92d28e9b, - 0xe5d5be0d, 0x7cdcefb7, 0x0bdbdf21, 0x86d3d2d4, 0xf1d4e242, - 0x68ddb3f8, 0x1fda836e, 0x81be16cd, 0xf6b9265b, 0x6fb077e1, - 0x18b74777, 0x88085ae6, 0xff0f6a70, 0x66063bca, 0x11010b5c, - 0x8f659eff, 0xf862ae69, 0x616bffd3, 0x166ccf45, 0xa00ae278, - 0xd70dd2ee, 0x4e048354, 0x3903b3c2, 0xa7672661, 0xd06016f7, - 0x4969474d, 0x3e6e77db, 0xaed16a4a, 0xd9d65adc, 0x40df0b66, - 0x37d83bf0, 0xa9bcae53, 0xdebb9ec5, 0x47b2cf7f, 0x30b5ffe9, - 0xbdbdf21c, 0xcabac28a, 0x53b39330, 0x24b4a3a6, 0xbad03605, - 0xcdd70693, 0x54de5729, 0x23d967bf, 0xb3667a2e, 0xc4614ab8, - 0x5d681b02, 0x2a6f2b94, 0xb40bbe37, 0xc30c8ea1, 0x5a05df1b, - 0x2d02ef8d}; - -#ifdef W - -#if W == 8 - -local const z_word_t FAR crc_big_table[] = { - 0x0000000000000000, 0x9630077700000000, 0x2c610eee00000000, - 0xba51099900000000, 0x19c46d0700000000, 0x8ff46a7000000000, - 0x35a563e900000000, 0xa395649e00000000, 0x3288db0e00000000, - 0xa4b8dc7900000000, 0x1ee9d5e000000000, 0x88d9d29700000000, - 0x2b4cb60900000000, 0xbd7cb17e00000000, 0x072db8e700000000, - 0x911dbf9000000000, 0x6410b71d00000000, 0xf220b06a00000000, - 0x4871b9f300000000, 0xde41be8400000000, 0x7dd4da1a00000000, - 0xebe4dd6d00000000, 0x51b5d4f400000000, 0xc785d38300000000, - 0x56986c1300000000, 0xc0a86b6400000000, 0x7af962fd00000000, - 0xecc9658a00000000, 0x4f5c011400000000, 0xd96c066300000000, - 0x633d0ffa00000000, 0xf50d088d00000000, 0xc8206e3b00000000, - 0x5e10694c00000000, 0xe44160d500000000, 0x727167a200000000, - 0xd1e4033c00000000, 0x47d4044b00000000, 0xfd850dd200000000, - 0x6bb50aa500000000, 0xfaa8b53500000000, 0x6c98b24200000000, - 0xd6c9bbdb00000000, 0x40f9bcac00000000, 0xe36cd83200000000, - 0x755cdf4500000000, 0xcf0dd6dc00000000, 0x593dd1ab00000000, - 0xac30d92600000000, 0x3a00de5100000000, 0x8051d7c800000000, - 0x1661d0bf00000000, 0xb5f4b42100000000, 0x23c4b35600000000, - 0x9995bacf00000000, 0x0fa5bdb800000000, 0x9eb8022800000000, - 0x0888055f00000000, 0xb2d90cc600000000, 0x24e90bb100000000, - 0x877c6f2f00000000, 0x114c685800000000, 0xab1d61c100000000, - 0x3d2d66b600000000, 0x9041dc7600000000, 0x0671db0100000000, - 0xbc20d29800000000, 0x2a10d5ef00000000, 0x8985b17100000000, - 0x1fb5b60600000000, 0xa5e4bf9f00000000, 0x33d4b8e800000000, - 0xa2c9077800000000, 0x34f9000f00000000, 0x8ea8099600000000, - 0x18980ee100000000, 0xbb0d6a7f00000000, 0x2d3d6d0800000000, - 0x976c649100000000, 0x015c63e600000000, 0xf4516b6b00000000, - 0x62616c1c00000000, 0xd830658500000000, 0x4e0062f200000000, - 0xed95066c00000000, 0x7ba5011b00000000, 0xc1f4088200000000, - 0x57c40ff500000000, 0xc6d9b06500000000, 0x50e9b71200000000, - 0xeab8be8b00000000, 0x7c88b9fc00000000, 0xdf1ddd6200000000, - 0x492dda1500000000, 0xf37cd38c00000000, 0x654cd4fb00000000, - 0x5861b24d00000000, 0xce51b53a00000000, 0x7400bca300000000, - 0xe230bbd400000000, 0x41a5df4a00000000, 0xd795d83d00000000, - 0x6dc4d1a400000000, 0xfbf4d6d300000000, 0x6ae9694300000000, - 0xfcd96e3400000000, 0x468867ad00000000, 0xd0b860da00000000, - 0x732d044400000000, 0xe51d033300000000, 0x5f4c0aaa00000000, - 0xc97c0ddd00000000, 0x3c71055000000000, 0xaa41022700000000, - 0x10100bbe00000000, 0x86200cc900000000, 0x25b5685700000000, - 0xb3856f2000000000, 0x09d466b900000000, 0x9fe461ce00000000, - 0x0ef9de5e00000000, 0x98c9d92900000000, 0x2298d0b000000000, - 0xb4a8d7c700000000, 0x173db35900000000, 0x810db42e00000000, - 0x3b5cbdb700000000, 0xad6cbac000000000, 0x2083b8ed00000000, - 0xb6b3bf9a00000000, 0x0ce2b60300000000, 0x9ad2b17400000000, - 0x3947d5ea00000000, 0xaf77d29d00000000, 0x1526db0400000000, - 0x8316dc7300000000, 0x120b63e300000000, 0x843b649400000000, - 0x3e6a6d0d00000000, 0xa85a6a7a00000000, 0x0bcf0ee400000000, - 0x9dff099300000000, 0x27ae000a00000000, 0xb19e077d00000000, - 0x44930ff000000000, 0xd2a3088700000000, 0x68f2011e00000000, - 0xfec2066900000000, 0x5d5762f700000000, 0xcb67658000000000, - 0x71366c1900000000, 0xe7066b6e00000000, 0x761bd4fe00000000, - 0xe02bd38900000000, 0x5a7ada1000000000, 0xcc4add6700000000, - 0x6fdfb9f900000000, 0xf9efbe8e00000000, 0x43beb71700000000, - 0xd58eb06000000000, 0xe8a3d6d600000000, 0x7e93d1a100000000, - 0xc4c2d83800000000, 0x52f2df4f00000000, 0xf167bbd100000000, - 0x6757bca600000000, 0xdd06b53f00000000, 0x4b36b24800000000, - 0xda2b0dd800000000, 0x4c1b0aaf00000000, 0xf64a033600000000, - 0x607a044100000000, 0xc3ef60df00000000, 0x55df67a800000000, - 0xef8e6e3100000000, 0x79be694600000000, 0x8cb361cb00000000, - 0x1a8366bc00000000, 0xa0d26f2500000000, 0x36e2685200000000, - 0x95770ccc00000000, 0x03470bbb00000000, 0xb916022200000000, - 0x2f26055500000000, 0xbe3bbac500000000, 0x280bbdb200000000, - 0x925ab42b00000000, 0x046ab35c00000000, 0xa7ffd7c200000000, - 0x31cfd0b500000000, 0x8b9ed92c00000000, 0x1daede5b00000000, - 0xb0c2649b00000000, 0x26f263ec00000000, 0x9ca36a7500000000, - 0x0a936d0200000000, 0xa906099c00000000, 0x3f360eeb00000000, - 0x8567077200000000, 0x1357000500000000, 0x824abf9500000000, - 0x147ab8e200000000, 0xae2bb17b00000000, 0x381bb60c00000000, - 0x9b8ed29200000000, 0x0dbed5e500000000, 0xb7efdc7c00000000, - 0x21dfdb0b00000000, 0xd4d2d38600000000, 0x42e2d4f100000000, - 0xf8b3dd6800000000, 0x6e83da1f00000000, 0xcd16be8100000000, - 0x5b26b9f600000000, 0xe177b06f00000000, 0x7747b71800000000, - 0xe65a088800000000, 0x706a0fff00000000, 0xca3b066600000000, - 0x5c0b011100000000, 0xff9e658f00000000, 0x69ae62f800000000, - 0xd3ff6b6100000000, 0x45cf6c1600000000, 0x78e20aa000000000, - 0xeed20dd700000000, 0x5483044e00000000, 0xc2b3033900000000, - 0x612667a700000000, 0xf71660d000000000, 0x4d47694900000000, - 0xdb776e3e00000000, 0x4a6ad1ae00000000, 0xdc5ad6d900000000, - 0x660bdf4000000000, 0xf03bd83700000000, 0x53aebca900000000, - 0xc59ebbde00000000, 0x7fcfb24700000000, 0xe9ffb53000000000, - 0x1cf2bdbd00000000, 0x8ac2baca00000000, 0x3093b35300000000, - 0xa6a3b42400000000, 0x0536d0ba00000000, 0x9306d7cd00000000, - 0x2957de5400000000, 0xbf67d92300000000, 0x2e7a66b300000000, - 0xb84a61c400000000, 0x021b685d00000000, 0x942b6f2a00000000, - 0x37be0bb400000000, 0xa18e0cc300000000, 0x1bdf055a00000000, - 0x8def022d00000000}; - -#else /* W == 4 */ - -local const z_word_t FAR crc_big_table[] = { - 0x00000000, 0x96300777, 0x2c610eee, 0xba510999, 0x19c46d07, - 0x8ff46a70, 0x35a563e9, 0xa395649e, 0x3288db0e, 0xa4b8dc79, - 0x1ee9d5e0, 0x88d9d297, 0x2b4cb609, 0xbd7cb17e, 0x072db8e7, - 0x911dbf90, 0x6410b71d, 0xf220b06a, 0x4871b9f3, 0xde41be84, - 0x7dd4da1a, 0xebe4dd6d, 0x51b5d4f4, 0xc785d383, 0x56986c13, - 0xc0a86b64, 0x7af962fd, 0xecc9658a, 0x4f5c0114, 0xd96c0663, - 0x633d0ffa, 0xf50d088d, 0xc8206e3b, 0x5e10694c, 0xe44160d5, - 0x727167a2, 0xd1e4033c, 0x47d4044b, 0xfd850dd2, 0x6bb50aa5, - 0xfaa8b535, 0x6c98b242, 0xd6c9bbdb, 0x40f9bcac, 0xe36cd832, - 0x755cdf45, 0xcf0dd6dc, 0x593dd1ab, 0xac30d926, 0x3a00de51, - 0x8051d7c8, 0x1661d0bf, 0xb5f4b421, 0x23c4b356, 0x9995bacf, - 0x0fa5bdb8, 0x9eb80228, 0x0888055f, 0xb2d90cc6, 0x24e90bb1, - 0x877c6f2f, 0x114c6858, 0xab1d61c1, 0x3d2d66b6, 0x9041dc76, - 0x0671db01, 0xbc20d298, 0x2a10d5ef, 0x8985b171, 0x1fb5b606, - 0xa5e4bf9f, 0x33d4b8e8, 0xa2c90778, 0x34f9000f, 0x8ea80996, - 0x18980ee1, 0xbb0d6a7f, 0x2d3d6d08, 0x976c6491, 0x015c63e6, - 0xf4516b6b, 0x62616c1c, 0xd8306585, 0x4e0062f2, 0xed95066c, - 0x7ba5011b, 0xc1f40882, 0x57c40ff5, 0xc6d9b065, 0x50e9b712, - 0xeab8be8b, 0x7c88b9fc, 0xdf1ddd62, 0x492dda15, 0xf37cd38c, - 0x654cd4fb, 0x5861b24d, 0xce51b53a, 0x7400bca3, 0xe230bbd4, - 0x41a5df4a, 0xd795d83d, 0x6dc4d1a4, 0xfbf4d6d3, 0x6ae96943, - 0xfcd96e34, 0x468867ad, 0xd0b860da, 0x732d0444, 0xe51d0333, - 0x5f4c0aaa, 0xc97c0ddd, 0x3c710550, 0xaa410227, 0x10100bbe, - 0x86200cc9, 0x25b56857, 0xb3856f20, 0x09d466b9, 0x9fe461ce, - 0x0ef9de5e, 0x98c9d929, 0x2298d0b0, 0xb4a8d7c7, 0x173db359, - 0x810db42e, 0x3b5cbdb7, 0xad6cbac0, 0x2083b8ed, 0xb6b3bf9a, - 0x0ce2b603, 0x9ad2b174, 0x3947d5ea, 0xaf77d29d, 0x1526db04, - 0x8316dc73, 0x120b63e3, 0x843b6494, 0x3e6a6d0d, 0xa85a6a7a, - 0x0bcf0ee4, 0x9dff0993, 0x27ae000a, 0xb19e077d, 0x44930ff0, - 0xd2a30887, 0x68f2011e, 0xfec20669, 0x5d5762f7, 0xcb676580, - 0x71366c19, 0xe7066b6e, 0x761bd4fe, 0xe02bd389, 0x5a7ada10, - 0xcc4add67, 0x6fdfb9f9, 0xf9efbe8e, 0x43beb717, 0xd58eb060, - 0xe8a3d6d6, 0x7e93d1a1, 0xc4c2d838, 0x52f2df4f, 0xf167bbd1, - 0x6757bca6, 0xdd06b53f, 0x4b36b248, 0xda2b0dd8, 0x4c1b0aaf, - 0xf64a0336, 0x607a0441, 0xc3ef60df, 0x55df67a8, 0xef8e6e31, - 0x79be6946, 0x8cb361cb, 0x1a8366bc, 0xa0d26f25, 0x36e26852, - 0x95770ccc, 0x03470bbb, 0xb9160222, 0x2f260555, 0xbe3bbac5, - 0x280bbdb2, 0x925ab42b, 0x046ab35c, 0xa7ffd7c2, 0x31cfd0b5, - 0x8b9ed92c, 0x1daede5b, 0xb0c2649b, 0x26f263ec, 0x9ca36a75, - 0x0a936d02, 0xa906099c, 0x3f360eeb, 0x85670772, 0x13570005, - 0x824abf95, 0x147ab8e2, 0xae2bb17b, 0x381bb60c, 0x9b8ed292, - 0x0dbed5e5, 0xb7efdc7c, 0x21dfdb0b, 0xd4d2d386, 0x42e2d4f1, - 0xf8b3dd68, 0x6e83da1f, 0xcd16be81, 0x5b26b9f6, 0xe177b06f, - 0x7747b718, 0xe65a0888, 0x706a0fff, 0xca3b0666, 0x5c0b0111, - 0xff9e658f, 0x69ae62f8, 0xd3ff6b61, 0x45cf6c16, 0x78e20aa0, - 0xeed20dd7, 0x5483044e, 0xc2b30339, 0x612667a7, 0xf71660d0, - 0x4d476949, 0xdb776e3e, 0x4a6ad1ae, 0xdc5ad6d9, 0x660bdf40, - 0xf03bd837, 0x53aebca9, 0xc59ebbde, 0x7fcfb247, 0xe9ffb530, - 0x1cf2bdbd, 0x8ac2baca, 0x3093b353, 0xa6a3b424, 0x0536d0ba, - 0x9306d7cd, 0x2957de54, 0xbf67d923, 0x2e7a66b3, 0xb84a61c4, - 0x021b685d, 0x942b6f2a, 0x37be0bb4, 0xa18e0cc3, 0x1bdf055a, - 0x8def022d}; - -#endif - -#if N == 1 - -#if W == 8 - -local const z_crc_t FAR crc_braid_table[][256] = { - {0x00000000, 0xccaa009e, 0x4225077d, 0x8e8f07e3, 0x844a0efa, - 0x48e00e64, 0xc66f0987, 0x0ac50919, 0xd3e51bb5, 0x1f4f1b2b, - 0x91c01cc8, 0x5d6a1c56, 0x57af154f, 0x9b0515d1, 0x158a1232, - 0xd92012ac, 0x7cbb312b, 0xb01131b5, 0x3e9e3656, 0xf23436c8, - 0xf8f13fd1, 0x345b3f4f, 0xbad438ac, 0x767e3832, 0xaf5e2a9e, - 0x63f42a00, 0xed7b2de3, 0x21d12d7d, 0x2b142464, 0xe7be24fa, - 0x69312319, 0xa59b2387, 0xf9766256, 0x35dc62c8, 0xbb53652b, - 0x77f965b5, 0x7d3c6cac, 0xb1966c32, 0x3f196bd1, 0xf3b36b4f, - 0x2a9379e3, 0xe639797d, 0x68b67e9e, 0xa41c7e00, 0xaed97719, - 0x62737787, 0xecfc7064, 0x205670fa, 0x85cd537d, 0x496753e3, - 0xc7e85400, 0x0b42549e, 0x01875d87, 0xcd2d5d19, 0x43a25afa, - 0x8f085a64, 0x562848c8, 0x9a824856, 0x140d4fb5, 0xd8a74f2b, - 0xd2624632, 0x1ec846ac, 0x9047414f, 0x5ced41d1, 0x299dc2ed, - 0xe537c273, 0x6bb8c590, 0xa712c50e, 0xadd7cc17, 0x617dcc89, - 0xeff2cb6a, 0x2358cbf4, 0xfa78d958, 0x36d2d9c6, 0xb85dde25, - 0x74f7debb, 0x7e32d7a2, 0xb298d73c, 0x3c17d0df, 0xf0bdd041, - 0x5526f3c6, 0x998cf358, 0x1703f4bb, 0xdba9f425, 0xd16cfd3c, - 0x1dc6fda2, 0x9349fa41, 0x5fe3fadf, 0x86c3e873, 0x4a69e8ed, - 0xc4e6ef0e, 0x084cef90, 0x0289e689, 0xce23e617, 0x40ace1f4, - 0x8c06e16a, 0xd0eba0bb, 0x1c41a025, 0x92cea7c6, 0x5e64a758, - 0x54a1ae41, 0x980baedf, 0x1684a93c, 0xda2ea9a2, 0x030ebb0e, - 0xcfa4bb90, 0x412bbc73, 0x8d81bced, 0x8744b5f4, 0x4beeb56a, - 0xc561b289, 0x09cbb217, 0xac509190, 0x60fa910e, 0xee7596ed, - 0x22df9673, 0x281a9f6a, 0xe4b09ff4, 0x6a3f9817, 0xa6959889, - 0x7fb58a25, 0xb31f8abb, 0x3d908d58, 0xf13a8dc6, 0xfbff84df, - 0x37558441, 0xb9da83a2, 0x7570833c, 0x533b85da, 0x9f918544, - 0x111e82a7, 0xddb48239, 0xd7718b20, 0x1bdb8bbe, 0x95548c5d, - 0x59fe8cc3, 0x80de9e6f, 0x4c749ef1, 0xc2fb9912, 0x0e51998c, - 0x04949095, 0xc83e900b, 0x46b197e8, 0x8a1b9776, 0x2f80b4f1, - 0xe32ab46f, 0x6da5b38c, 0xa10fb312, 0xabcaba0b, 0x6760ba95, - 0xe9efbd76, 0x2545bde8, 0xfc65af44, 0x30cfafda, 0xbe40a839, - 0x72eaa8a7, 0x782fa1be, 0xb485a120, 0x3a0aa6c3, 0xf6a0a65d, - 0xaa4de78c, 0x66e7e712, 0xe868e0f1, 0x24c2e06f, 0x2e07e976, - 0xe2ade9e8, 0x6c22ee0b, 0xa088ee95, 0x79a8fc39, 0xb502fca7, - 0x3b8dfb44, 0xf727fbda, 0xfde2f2c3, 0x3148f25d, 0xbfc7f5be, - 0x736df520, 0xd6f6d6a7, 0x1a5cd639, 0x94d3d1da, 0x5879d144, - 0x52bcd85d, 0x9e16d8c3, 0x1099df20, 0xdc33dfbe, 0x0513cd12, - 0xc9b9cd8c, 0x4736ca6f, 0x8b9ccaf1, 0x8159c3e8, 0x4df3c376, - 0xc37cc495, 0x0fd6c40b, 0x7aa64737, 0xb60c47a9, 0x3883404a, - 0xf42940d4, 0xfeec49cd, 0x32464953, 0xbcc94eb0, 0x70634e2e, - 0xa9435c82, 0x65e95c1c, 0xeb665bff, 0x27cc5b61, 0x2d095278, - 0xe1a352e6, 0x6f2c5505, 0xa386559b, 0x061d761c, 0xcab77682, - 0x44387161, 0x889271ff, 0x825778e6, 0x4efd7878, 0xc0727f9b, - 0x0cd87f05, 0xd5f86da9, 0x19526d37, 0x97dd6ad4, 0x5b776a4a, - 0x51b26353, 0x9d1863cd, 0x1397642e, 0xdf3d64b0, 0x83d02561, - 0x4f7a25ff, 0xc1f5221c, 0x0d5f2282, 0x079a2b9b, 0xcb302b05, - 0x45bf2ce6, 0x89152c78, 0x50353ed4, 0x9c9f3e4a, 0x121039a9, - 0xdeba3937, 0xd47f302e, 0x18d530b0, 0x965a3753, 0x5af037cd, - 0xff6b144a, 0x33c114d4, 0xbd4e1337, 0x71e413a9, 0x7b211ab0, - 0xb78b1a2e, 0x39041dcd, 0xf5ae1d53, 0x2c8e0fff, 0xe0240f61, - 0x6eab0882, 0xa201081c, 0xa8c40105, 0x646e019b, 0xeae10678, - 0x264b06e6}, - {0x00000000, 0xa6770bb4, 0x979f1129, 0x31e81a9d, 0xf44f2413, - 0x52382fa7, 0x63d0353a, 0xc5a73e8e, 0x33ef4e67, 0x959845d3, - 0xa4705f4e, 0x020754fa, 0xc7a06a74, 0x61d761c0, 0x503f7b5d, - 0xf64870e9, 0x67de9cce, 0xc1a9977a, 0xf0418de7, 0x56368653, - 0x9391b8dd, 0x35e6b369, 0x040ea9f4, 0xa279a240, 0x5431d2a9, - 0xf246d91d, 0xc3aec380, 0x65d9c834, 0xa07ef6ba, 0x0609fd0e, - 0x37e1e793, 0x9196ec27, 0xcfbd399c, 0x69ca3228, 0x582228b5, - 0xfe552301, 0x3bf21d8f, 0x9d85163b, 0xac6d0ca6, 0x0a1a0712, - 0xfc5277fb, 0x5a257c4f, 0x6bcd66d2, 0xcdba6d66, 0x081d53e8, - 0xae6a585c, 0x9f8242c1, 0x39f54975, 0xa863a552, 0x0e14aee6, - 0x3ffcb47b, 0x998bbfcf, 0x5c2c8141, 0xfa5b8af5, 0xcbb39068, - 0x6dc49bdc, 0x9b8ceb35, 0x3dfbe081, 0x0c13fa1c, 0xaa64f1a8, - 0x6fc3cf26, 0xc9b4c492, 0xf85cde0f, 0x5e2bd5bb, 0x440b7579, - 0xe27c7ecd, 0xd3946450, 0x75e36fe4, 0xb044516a, 0x16335ade, - 0x27db4043, 0x81ac4bf7, 0x77e43b1e, 0xd19330aa, 0xe07b2a37, - 0x460c2183, 0x83ab1f0d, 0x25dc14b9, 0x14340e24, 0xb2430590, - 0x23d5e9b7, 0x85a2e203, 0xb44af89e, 0x123df32a, 0xd79acda4, - 0x71edc610, 0x4005dc8d, 0xe672d739, 0x103aa7d0, 0xb64dac64, - 0x87a5b6f9, 0x21d2bd4d, 0xe47583c3, 0x42028877, 0x73ea92ea, - 0xd59d995e, 0x8bb64ce5, 0x2dc14751, 0x1c295dcc, 0xba5e5678, - 0x7ff968f6, 0xd98e6342, 0xe86679df, 0x4e11726b, 0xb8590282, - 0x1e2e0936, 0x2fc613ab, 0x89b1181f, 0x4c162691, 0xea612d25, - 0xdb8937b8, 0x7dfe3c0c, 0xec68d02b, 0x4a1fdb9f, 0x7bf7c102, - 0xdd80cab6, 0x1827f438, 0xbe50ff8c, 0x8fb8e511, 0x29cfeea5, - 0xdf879e4c, 0x79f095f8, 0x48188f65, 0xee6f84d1, 0x2bc8ba5f, - 0x8dbfb1eb, 0xbc57ab76, 0x1a20a0c2, 0x8816eaf2, 0x2e61e146, - 0x1f89fbdb, 0xb9fef06f, 0x7c59cee1, 0xda2ec555, 0xebc6dfc8, - 0x4db1d47c, 0xbbf9a495, 0x1d8eaf21, 0x2c66b5bc, 0x8a11be08, - 0x4fb68086, 0xe9c18b32, 0xd82991af, 0x7e5e9a1b, 0xefc8763c, - 0x49bf7d88, 0x78576715, 0xde206ca1, 0x1b87522f, 0xbdf0599b, - 0x8c184306, 0x2a6f48b2, 0xdc27385b, 0x7a5033ef, 0x4bb82972, - 0xedcf22c6, 0x28681c48, 0x8e1f17fc, 0xbff70d61, 0x198006d5, - 0x47abd36e, 0xe1dcd8da, 0xd034c247, 0x7643c9f3, 0xb3e4f77d, - 0x1593fcc9, 0x247be654, 0x820cede0, 0x74449d09, 0xd23396bd, - 0xe3db8c20, 0x45ac8794, 0x800bb91a, 0x267cb2ae, 0x1794a833, - 0xb1e3a387, 0x20754fa0, 0x86024414, 0xb7ea5e89, 0x119d553d, - 0xd43a6bb3, 0x724d6007, 0x43a57a9a, 0xe5d2712e, 0x139a01c7, - 0xb5ed0a73, 0x840510ee, 0x22721b5a, 0xe7d525d4, 0x41a22e60, - 0x704a34fd, 0xd63d3f49, 0xcc1d9f8b, 0x6a6a943f, 0x5b828ea2, - 0xfdf58516, 0x3852bb98, 0x9e25b02c, 0xafcdaab1, 0x09baa105, - 0xfff2d1ec, 0x5985da58, 0x686dc0c5, 0xce1acb71, 0x0bbdf5ff, - 0xadcafe4b, 0x9c22e4d6, 0x3a55ef62, 0xabc30345, 0x0db408f1, - 0x3c5c126c, 0x9a2b19d8, 0x5f8c2756, 0xf9fb2ce2, 0xc813367f, - 0x6e643dcb, 0x982c4d22, 0x3e5b4696, 0x0fb35c0b, 0xa9c457bf, - 0x6c636931, 0xca146285, 0xfbfc7818, 0x5d8b73ac, 0x03a0a617, - 0xa5d7ada3, 0x943fb73e, 0x3248bc8a, 0xf7ef8204, 0x519889b0, - 0x6070932d, 0xc6079899, 0x304fe870, 0x9638e3c4, 0xa7d0f959, - 0x01a7f2ed, 0xc400cc63, 0x6277c7d7, 0x539fdd4a, 0xf5e8d6fe, - 0x647e3ad9, 0xc209316d, 0xf3e12bf0, 0x55962044, 0x90311eca, - 0x3646157e, 0x07ae0fe3, 0xa1d90457, 0x579174be, 0xf1e67f0a, - 0xc00e6597, 0x66796e23, 0xa3de50ad, 0x05a95b19, 0x34414184, - 0x92364a30}, - {0x00000000, 0xcb5cd3a5, 0x4dc8a10b, 0x869472ae, 0x9b914216, - 0x50cd91b3, 0xd659e31d, 0x1d0530b8, 0xec53826d, 0x270f51c8, - 0xa19b2366, 0x6ac7f0c3, 0x77c2c07b, 0xbc9e13de, 0x3a0a6170, - 0xf156b2d5, 0x03d6029b, 0xc88ad13e, 0x4e1ea390, 0x85427035, - 0x9847408d, 0x531b9328, 0xd58fe186, 0x1ed33223, 0xef8580f6, - 0x24d95353, 0xa24d21fd, 0x6911f258, 0x7414c2e0, 0xbf481145, - 0x39dc63eb, 0xf280b04e, 0x07ac0536, 0xccf0d693, 0x4a64a43d, - 0x81387798, 0x9c3d4720, 0x57619485, 0xd1f5e62b, 0x1aa9358e, - 0xebff875b, 0x20a354fe, 0xa6372650, 0x6d6bf5f5, 0x706ec54d, - 0xbb3216e8, 0x3da66446, 0xf6fab7e3, 0x047a07ad, 0xcf26d408, - 0x49b2a6a6, 0x82ee7503, 0x9feb45bb, 0x54b7961e, 0xd223e4b0, - 0x197f3715, 0xe82985c0, 0x23755665, 0xa5e124cb, 0x6ebdf76e, - 0x73b8c7d6, 0xb8e41473, 0x3e7066dd, 0xf52cb578, 0x0f580a6c, - 0xc404d9c9, 0x4290ab67, 0x89cc78c2, 0x94c9487a, 0x5f959bdf, - 0xd901e971, 0x125d3ad4, 0xe30b8801, 0x28575ba4, 0xaec3290a, - 0x659ffaaf, 0x789aca17, 0xb3c619b2, 0x35526b1c, 0xfe0eb8b9, - 0x0c8e08f7, 0xc7d2db52, 0x4146a9fc, 0x8a1a7a59, 0x971f4ae1, - 0x5c439944, 0xdad7ebea, 0x118b384f, 0xe0dd8a9a, 0x2b81593f, - 0xad152b91, 0x6649f834, 0x7b4cc88c, 0xb0101b29, 0x36846987, - 0xfdd8ba22, 0x08f40f5a, 0xc3a8dcff, 0x453cae51, 0x8e607df4, - 0x93654d4c, 0x58399ee9, 0xdeadec47, 0x15f13fe2, 0xe4a78d37, - 0x2ffb5e92, 0xa96f2c3c, 0x6233ff99, 0x7f36cf21, 0xb46a1c84, - 0x32fe6e2a, 0xf9a2bd8f, 0x0b220dc1, 0xc07ede64, 0x46eaacca, - 0x8db67f6f, 0x90b34fd7, 0x5bef9c72, 0xdd7beedc, 0x16273d79, - 0xe7718fac, 0x2c2d5c09, 0xaab92ea7, 0x61e5fd02, 0x7ce0cdba, - 0xb7bc1e1f, 0x31286cb1, 0xfa74bf14, 0x1eb014d8, 0xd5ecc77d, - 0x5378b5d3, 0x98246676, 0x852156ce, 0x4e7d856b, 0xc8e9f7c5, - 0x03b52460, 0xf2e396b5, 0x39bf4510, 0xbf2b37be, 0x7477e41b, - 0x6972d4a3, 0xa22e0706, 0x24ba75a8, 0xefe6a60d, 0x1d661643, - 0xd63ac5e6, 0x50aeb748, 0x9bf264ed, 0x86f75455, 0x4dab87f0, - 0xcb3ff55e, 0x006326fb, 0xf135942e, 0x3a69478b, 0xbcfd3525, - 0x77a1e680, 0x6aa4d638, 0xa1f8059d, 0x276c7733, 0xec30a496, - 0x191c11ee, 0xd240c24b, 0x54d4b0e5, 0x9f886340, 0x828d53f8, - 0x49d1805d, 0xcf45f2f3, 0x04192156, 0xf54f9383, 0x3e134026, - 0xb8873288, 0x73dbe12d, 0x6eded195, 0xa5820230, 0x2316709e, - 0xe84aa33b, 0x1aca1375, 0xd196c0d0, 0x5702b27e, 0x9c5e61db, - 0x815b5163, 0x4a0782c6, 0xcc93f068, 0x07cf23cd, 0xf6999118, - 0x3dc542bd, 0xbb513013, 0x700de3b6, 0x6d08d30e, 0xa65400ab, - 0x20c07205, 0xeb9ca1a0, 0x11e81eb4, 0xdab4cd11, 0x5c20bfbf, - 0x977c6c1a, 0x8a795ca2, 0x41258f07, 0xc7b1fda9, 0x0ced2e0c, - 0xfdbb9cd9, 0x36e74f7c, 0xb0733dd2, 0x7b2fee77, 0x662adecf, - 0xad760d6a, 0x2be27fc4, 0xe0beac61, 0x123e1c2f, 0xd962cf8a, - 0x5ff6bd24, 0x94aa6e81, 0x89af5e39, 0x42f38d9c, 0xc467ff32, - 0x0f3b2c97, 0xfe6d9e42, 0x35314de7, 0xb3a53f49, 0x78f9ecec, - 0x65fcdc54, 0xaea00ff1, 0x28347d5f, 0xe368aefa, 0x16441b82, - 0xdd18c827, 0x5b8cba89, 0x90d0692c, 0x8dd55994, 0x46898a31, - 0xc01df89f, 0x0b412b3a, 0xfa1799ef, 0x314b4a4a, 0xb7df38e4, - 0x7c83eb41, 0x6186dbf9, 0xaada085c, 0x2c4e7af2, 0xe712a957, - 0x15921919, 0xdececabc, 0x585ab812, 0x93066bb7, 0x8e035b0f, - 0x455f88aa, 0xc3cbfa04, 0x089729a1, 0xf9c19b74, 0x329d48d1, - 0xb4093a7f, 0x7f55e9da, 0x6250d962, 0xa90c0ac7, 0x2f987869, - 0xe4c4abcc}, - {0x00000000, 0x3d6029b0, 0x7ac05360, 0x47a07ad0, 0xf580a6c0, - 0xc8e08f70, 0x8f40f5a0, 0xb220dc10, 0x30704bc1, 0x0d106271, - 0x4ab018a1, 0x77d03111, 0xc5f0ed01, 0xf890c4b1, 0xbf30be61, - 0x825097d1, 0x60e09782, 0x5d80be32, 0x1a20c4e2, 0x2740ed52, - 0x95603142, 0xa80018f2, 0xefa06222, 0xd2c04b92, 0x5090dc43, - 0x6df0f5f3, 0x2a508f23, 0x1730a693, 0xa5107a83, 0x98705333, - 0xdfd029e3, 0xe2b00053, 0xc1c12f04, 0xfca106b4, 0xbb017c64, - 0x866155d4, 0x344189c4, 0x0921a074, 0x4e81daa4, 0x73e1f314, - 0xf1b164c5, 0xccd14d75, 0x8b7137a5, 0xb6111e15, 0x0431c205, - 0x3951ebb5, 0x7ef19165, 0x4391b8d5, 0xa121b886, 0x9c419136, - 0xdbe1ebe6, 0xe681c256, 0x54a11e46, 0x69c137f6, 0x2e614d26, - 0x13016496, 0x9151f347, 0xac31daf7, 0xeb91a027, 0xd6f18997, - 0x64d15587, 0x59b17c37, 0x1e1106e7, 0x23712f57, 0x58f35849, - 0x659371f9, 0x22330b29, 0x1f532299, 0xad73fe89, 0x9013d739, - 0xd7b3ade9, 0xead38459, 0x68831388, 0x55e33a38, 0x124340e8, - 0x2f236958, 0x9d03b548, 0xa0639cf8, 0xe7c3e628, 0xdaa3cf98, - 0x3813cfcb, 0x0573e67b, 0x42d39cab, 0x7fb3b51b, 0xcd93690b, - 0xf0f340bb, 0xb7533a6b, 0x8a3313db, 0x0863840a, 0x3503adba, - 0x72a3d76a, 0x4fc3feda, 0xfde322ca, 0xc0830b7a, 0x872371aa, - 0xba43581a, 0x9932774d, 0xa4525efd, 0xe3f2242d, 0xde920d9d, - 0x6cb2d18d, 0x51d2f83d, 0x167282ed, 0x2b12ab5d, 0xa9423c8c, - 0x9422153c, 0xd3826fec, 0xeee2465c, 0x5cc29a4c, 0x61a2b3fc, - 0x2602c92c, 0x1b62e09c, 0xf9d2e0cf, 0xc4b2c97f, 0x8312b3af, - 0xbe729a1f, 0x0c52460f, 0x31326fbf, 0x7692156f, 0x4bf23cdf, - 0xc9a2ab0e, 0xf4c282be, 0xb362f86e, 0x8e02d1de, 0x3c220dce, - 0x0142247e, 0x46e25eae, 0x7b82771e, 0xb1e6b092, 0x8c869922, - 0xcb26e3f2, 0xf646ca42, 0x44661652, 0x79063fe2, 0x3ea64532, - 0x03c66c82, 0x8196fb53, 0xbcf6d2e3, 0xfb56a833, 0xc6368183, - 0x74165d93, 0x49767423, 0x0ed60ef3, 0x33b62743, 0xd1062710, - 0xec660ea0, 0xabc67470, 0x96a65dc0, 0x248681d0, 0x19e6a860, - 0x5e46d2b0, 0x6326fb00, 0xe1766cd1, 0xdc164561, 0x9bb63fb1, - 0xa6d61601, 0x14f6ca11, 0x2996e3a1, 0x6e369971, 0x5356b0c1, - 0x70279f96, 0x4d47b626, 0x0ae7ccf6, 0x3787e546, 0x85a73956, - 0xb8c710e6, 0xff676a36, 0xc2074386, 0x4057d457, 0x7d37fde7, - 0x3a978737, 0x07f7ae87, 0xb5d77297, 0x88b75b27, 0xcf1721f7, - 0xf2770847, 0x10c70814, 0x2da721a4, 0x6a075b74, 0x576772c4, - 0xe547aed4, 0xd8278764, 0x9f87fdb4, 0xa2e7d404, 0x20b743d5, - 0x1dd76a65, 0x5a7710b5, 0x67173905, 0xd537e515, 0xe857cca5, - 0xaff7b675, 0x92979fc5, 0xe915e8db, 0xd475c16b, 0x93d5bbbb, - 0xaeb5920b, 0x1c954e1b, 0x21f567ab, 0x66551d7b, 0x5b3534cb, - 0xd965a31a, 0xe4058aaa, 0xa3a5f07a, 0x9ec5d9ca, 0x2ce505da, - 0x11852c6a, 0x562556ba, 0x6b457f0a, 0x89f57f59, 0xb49556e9, - 0xf3352c39, 0xce550589, 0x7c75d999, 0x4115f029, 0x06b58af9, - 0x3bd5a349, 0xb9853498, 0x84e51d28, 0xc34567f8, 0xfe254e48, - 0x4c059258, 0x7165bbe8, 0x36c5c138, 0x0ba5e888, 0x28d4c7df, - 0x15b4ee6f, 0x521494bf, 0x6f74bd0f, 0xdd54611f, 0xe03448af, - 0xa794327f, 0x9af41bcf, 0x18a48c1e, 0x25c4a5ae, 0x6264df7e, - 0x5f04f6ce, 0xed242ade, 0xd044036e, 0x97e479be, 0xaa84500e, - 0x4834505d, 0x755479ed, 0x32f4033d, 0x0f942a8d, 0xbdb4f69d, - 0x80d4df2d, 0xc774a5fd, 0xfa148c4d, 0x78441b9c, 0x4524322c, - 0x028448fc, 0x3fe4614c, 0x8dc4bd5c, 0xb0a494ec, 0xf704ee3c, - 0xca64c78c}, - {0x00000000, 0xb8bc6765, 0xaa09c88b, 0x12b5afee, 0x8f629757, - 0x37def032, 0x256b5fdc, 0x9dd738b9, 0xc5b428ef, 0x7d084f8a, - 0x6fbde064, 0xd7018701, 0x4ad6bfb8, 0xf26ad8dd, 0xe0df7733, - 0x58631056, 0x5019579f, 0xe8a530fa, 0xfa109f14, 0x42acf871, - 0xdf7bc0c8, 0x67c7a7ad, 0x75720843, 0xcdce6f26, 0x95ad7f70, - 0x2d111815, 0x3fa4b7fb, 0x8718d09e, 0x1acfe827, 0xa2738f42, - 0xb0c620ac, 0x087a47c9, 0xa032af3e, 0x188ec85b, 0x0a3b67b5, - 0xb28700d0, 0x2f503869, 0x97ec5f0c, 0x8559f0e2, 0x3de59787, - 0x658687d1, 0xdd3ae0b4, 0xcf8f4f5a, 0x7733283f, 0xeae41086, - 0x525877e3, 0x40edd80d, 0xf851bf68, 0xf02bf8a1, 0x48979fc4, - 0x5a22302a, 0xe29e574f, 0x7f496ff6, 0xc7f50893, 0xd540a77d, - 0x6dfcc018, 0x359fd04e, 0x8d23b72b, 0x9f9618c5, 0x272a7fa0, - 0xbafd4719, 0x0241207c, 0x10f48f92, 0xa848e8f7, 0x9b14583d, - 0x23a83f58, 0x311d90b6, 0x89a1f7d3, 0x1476cf6a, 0xaccaa80f, - 0xbe7f07e1, 0x06c36084, 0x5ea070d2, 0xe61c17b7, 0xf4a9b859, - 0x4c15df3c, 0xd1c2e785, 0x697e80e0, 0x7bcb2f0e, 0xc377486b, - 0xcb0d0fa2, 0x73b168c7, 0x6104c729, 0xd9b8a04c, 0x446f98f5, - 0xfcd3ff90, 0xee66507e, 0x56da371b, 0x0eb9274d, 0xb6054028, - 0xa4b0efc6, 0x1c0c88a3, 0x81dbb01a, 0x3967d77f, 0x2bd27891, - 0x936e1ff4, 0x3b26f703, 0x839a9066, 0x912f3f88, 0x299358ed, - 0xb4446054, 0x0cf80731, 0x1e4da8df, 0xa6f1cfba, 0xfe92dfec, - 0x462eb889, 0x549b1767, 0xec277002, 0x71f048bb, 0xc94c2fde, - 0xdbf98030, 0x6345e755, 0x6b3fa09c, 0xd383c7f9, 0xc1366817, - 0x798a0f72, 0xe45d37cb, 0x5ce150ae, 0x4e54ff40, 0xf6e89825, - 0xae8b8873, 0x1637ef16, 0x048240f8, 0xbc3e279d, 0x21e91f24, - 0x99557841, 0x8be0d7af, 0x335cb0ca, 0xed59b63b, 0x55e5d15e, - 0x47507eb0, 0xffec19d5, 0x623b216c, 0xda874609, 0xc832e9e7, - 0x708e8e82, 0x28ed9ed4, 0x9051f9b1, 0x82e4565f, 0x3a58313a, - 0xa78f0983, 0x1f336ee6, 0x0d86c108, 0xb53aa66d, 0xbd40e1a4, - 0x05fc86c1, 0x1749292f, 0xaff54e4a, 0x322276f3, 0x8a9e1196, - 0x982bbe78, 0x2097d91d, 0x78f4c94b, 0xc048ae2e, 0xd2fd01c0, - 0x6a4166a5, 0xf7965e1c, 0x4f2a3979, 0x5d9f9697, 0xe523f1f2, - 0x4d6b1905, 0xf5d77e60, 0xe762d18e, 0x5fdeb6eb, 0xc2098e52, - 0x7ab5e937, 0x680046d9, 0xd0bc21bc, 0x88df31ea, 0x3063568f, - 0x22d6f961, 0x9a6a9e04, 0x07bda6bd, 0xbf01c1d8, 0xadb46e36, - 0x15080953, 0x1d724e9a, 0xa5ce29ff, 0xb77b8611, 0x0fc7e174, - 0x9210d9cd, 0x2aacbea8, 0x38191146, 0x80a57623, 0xd8c66675, - 0x607a0110, 0x72cfaefe, 0xca73c99b, 0x57a4f122, 0xef189647, - 0xfdad39a9, 0x45115ecc, 0x764dee06, 0xcef18963, 0xdc44268d, - 0x64f841e8, 0xf92f7951, 0x41931e34, 0x5326b1da, 0xeb9ad6bf, - 0xb3f9c6e9, 0x0b45a18c, 0x19f00e62, 0xa14c6907, 0x3c9b51be, - 0x842736db, 0x96929935, 0x2e2efe50, 0x2654b999, 0x9ee8defc, - 0x8c5d7112, 0x34e11677, 0xa9362ece, 0x118a49ab, 0x033fe645, - 0xbb838120, 0xe3e09176, 0x5b5cf613, 0x49e959fd, 0xf1553e98, - 0x6c820621, 0xd43e6144, 0xc68bceaa, 0x7e37a9cf, 0xd67f4138, - 0x6ec3265d, 0x7c7689b3, 0xc4caeed6, 0x591dd66f, 0xe1a1b10a, - 0xf3141ee4, 0x4ba87981, 0x13cb69d7, 0xab770eb2, 0xb9c2a15c, - 0x017ec639, 0x9ca9fe80, 0x241599e5, 0x36a0360b, 0x8e1c516e, - 0x866616a7, 0x3eda71c2, 0x2c6fde2c, 0x94d3b949, 0x090481f0, - 0xb1b8e695, 0xa30d497b, 0x1bb12e1e, 0x43d23e48, 0xfb6e592d, - 0xe9dbf6c3, 0x516791a6, 0xccb0a91f, 0x740cce7a, 0x66b96194, - 0xde0506f1}, - {0x00000000, 0x01c26a37, 0x0384d46e, 0x0246be59, 0x0709a8dc, - 0x06cbc2eb, 0x048d7cb2, 0x054f1685, 0x0e1351b8, 0x0fd13b8f, - 0x0d9785d6, 0x0c55efe1, 0x091af964, 0x08d89353, 0x0a9e2d0a, - 0x0b5c473d, 0x1c26a370, 0x1de4c947, 0x1fa2771e, 0x1e601d29, - 0x1b2f0bac, 0x1aed619b, 0x18abdfc2, 0x1969b5f5, 0x1235f2c8, - 0x13f798ff, 0x11b126a6, 0x10734c91, 0x153c5a14, 0x14fe3023, - 0x16b88e7a, 0x177ae44d, 0x384d46e0, 0x398f2cd7, 0x3bc9928e, - 0x3a0bf8b9, 0x3f44ee3c, 0x3e86840b, 0x3cc03a52, 0x3d025065, - 0x365e1758, 0x379c7d6f, 0x35dac336, 0x3418a901, 0x3157bf84, - 0x3095d5b3, 0x32d36bea, 0x331101dd, 0x246be590, 0x25a98fa7, - 0x27ef31fe, 0x262d5bc9, 0x23624d4c, 0x22a0277b, 0x20e69922, - 0x2124f315, 0x2a78b428, 0x2bbade1f, 0x29fc6046, 0x283e0a71, - 0x2d711cf4, 0x2cb376c3, 0x2ef5c89a, 0x2f37a2ad, 0x709a8dc0, - 0x7158e7f7, 0x731e59ae, 0x72dc3399, 0x7793251c, 0x76514f2b, - 0x7417f172, 0x75d59b45, 0x7e89dc78, 0x7f4bb64f, 0x7d0d0816, - 0x7ccf6221, 0x798074a4, 0x78421e93, 0x7a04a0ca, 0x7bc6cafd, - 0x6cbc2eb0, 0x6d7e4487, 0x6f38fade, 0x6efa90e9, 0x6bb5866c, - 0x6a77ec5b, 0x68315202, 0x69f33835, 0x62af7f08, 0x636d153f, - 0x612bab66, 0x60e9c151, 0x65a6d7d4, 0x6464bde3, 0x662203ba, - 0x67e0698d, 0x48d7cb20, 0x4915a117, 0x4b531f4e, 0x4a917579, - 0x4fde63fc, 0x4e1c09cb, 0x4c5ab792, 0x4d98dda5, 0x46c49a98, - 0x4706f0af, 0x45404ef6, 0x448224c1, 0x41cd3244, 0x400f5873, - 0x4249e62a, 0x438b8c1d, 0x54f16850, 0x55330267, 0x5775bc3e, - 0x56b7d609, 0x53f8c08c, 0x523aaabb, 0x507c14e2, 0x51be7ed5, - 0x5ae239e8, 0x5b2053df, 0x5966ed86, 0x58a487b1, 0x5deb9134, - 0x5c29fb03, 0x5e6f455a, 0x5fad2f6d, 0xe1351b80, 0xe0f771b7, - 0xe2b1cfee, 0xe373a5d9, 0xe63cb35c, 0xe7fed96b, 0xe5b86732, - 0xe47a0d05, 0xef264a38, 0xeee4200f, 0xeca29e56, 0xed60f461, - 0xe82fe2e4, 0xe9ed88d3, 0xebab368a, 0xea695cbd, 0xfd13b8f0, - 0xfcd1d2c7, 0xfe976c9e, 0xff5506a9, 0xfa1a102c, 0xfbd87a1b, - 0xf99ec442, 0xf85cae75, 0xf300e948, 0xf2c2837f, 0xf0843d26, - 0xf1465711, 0xf4094194, 0xf5cb2ba3, 0xf78d95fa, 0xf64fffcd, - 0xd9785d60, 0xd8ba3757, 0xdafc890e, 0xdb3ee339, 0xde71f5bc, - 0xdfb39f8b, 0xddf521d2, 0xdc374be5, 0xd76b0cd8, 0xd6a966ef, - 0xd4efd8b6, 0xd52db281, 0xd062a404, 0xd1a0ce33, 0xd3e6706a, - 0xd2241a5d, 0xc55efe10, 0xc49c9427, 0xc6da2a7e, 0xc7184049, - 0xc25756cc, 0xc3953cfb, 0xc1d382a2, 0xc011e895, 0xcb4dafa8, - 0xca8fc59f, 0xc8c97bc6, 0xc90b11f1, 0xcc440774, 0xcd866d43, - 0xcfc0d31a, 0xce02b92d, 0x91af9640, 0x906dfc77, 0x922b422e, - 0x93e92819, 0x96a63e9c, 0x976454ab, 0x9522eaf2, 0x94e080c5, - 0x9fbcc7f8, 0x9e7eadcf, 0x9c381396, 0x9dfa79a1, 0x98b56f24, - 0x99770513, 0x9b31bb4a, 0x9af3d17d, 0x8d893530, 0x8c4b5f07, - 0x8e0de15e, 0x8fcf8b69, 0x8a809dec, 0x8b42f7db, 0x89044982, - 0x88c623b5, 0x839a6488, 0x82580ebf, 0x801eb0e6, 0x81dcdad1, - 0x8493cc54, 0x8551a663, 0x8717183a, 0x86d5720d, 0xa9e2d0a0, - 0xa820ba97, 0xaa6604ce, 0xaba46ef9, 0xaeeb787c, 0xaf29124b, - 0xad6fac12, 0xacadc625, 0xa7f18118, 0xa633eb2f, 0xa4755576, - 0xa5b73f41, 0xa0f829c4, 0xa13a43f3, 0xa37cfdaa, 0xa2be979d, - 0xb5c473d0, 0xb40619e7, 0xb640a7be, 0xb782cd89, 0xb2cddb0c, - 0xb30fb13b, 0xb1490f62, 0xb08b6555, 0xbbd72268, 0xba15485f, - 0xb853f606, 0xb9919c31, 0xbcde8ab4, 0xbd1ce083, 0xbf5a5eda, - 0xbe9834ed}, - {0x00000000, 0x191b3141, 0x32366282, 0x2b2d53c3, 0x646cc504, - 0x7d77f445, 0x565aa786, 0x4f4196c7, 0xc8d98a08, 0xd1c2bb49, - 0xfaefe88a, 0xe3f4d9cb, 0xacb54f0c, 0xb5ae7e4d, 0x9e832d8e, - 0x87981ccf, 0x4ac21251, 0x53d92310, 0x78f470d3, 0x61ef4192, - 0x2eaed755, 0x37b5e614, 0x1c98b5d7, 0x05838496, 0x821b9859, - 0x9b00a918, 0xb02dfadb, 0xa936cb9a, 0xe6775d5d, 0xff6c6c1c, - 0xd4413fdf, 0xcd5a0e9e, 0x958424a2, 0x8c9f15e3, 0xa7b24620, - 0xbea97761, 0xf1e8e1a6, 0xe8f3d0e7, 0xc3de8324, 0xdac5b265, - 0x5d5daeaa, 0x44469feb, 0x6f6bcc28, 0x7670fd69, 0x39316bae, - 0x202a5aef, 0x0b07092c, 0x121c386d, 0xdf4636f3, 0xc65d07b2, - 0xed705471, 0xf46b6530, 0xbb2af3f7, 0xa231c2b6, 0x891c9175, - 0x9007a034, 0x179fbcfb, 0x0e848dba, 0x25a9de79, 0x3cb2ef38, - 0x73f379ff, 0x6ae848be, 0x41c51b7d, 0x58de2a3c, 0xf0794f05, - 0xe9627e44, 0xc24f2d87, 0xdb541cc6, 0x94158a01, 0x8d0ebb40, - 0xa623e883, 0xbf38d9c2, 0x38a0c50d, 0x21bbf44c, 0x0a96a78f, - 0x138d96ce, 0x5ccc0009, 0x45d73148, 0x6efa628b, 0x77e153ca, - 0xbabb5d54, 0xa3a06c15, 0x888d3fd6, 0x91960e97, 0xded79850, - 0xc7cca911, 0xece1fad2, 0xf5facb93, 0x7262d75c, 0x6b79e61d, - 0x4054b5de, 0x594f849f, 0x160e1258, 0x0f152319, 0x243870da, - 0x3d23419b, 0x65fd6ba7, 0x7ce65ae6, 0x57cb0925, 0x4ed03864, - 0x0191aea3, 0x188a9fe2, 0x33a7cc21, 0x2abcfd60, 0xad24e1af, - 0xb43fd0ee, 0x9f12832d, 0x8609b26c, 0xc94824ab, 0xd05315ea, - 0xfb7e4629, 0xe2657768, 0x2f3f79f6, 0x362448b7, 0x1d091b74, - 0x04122a35, 0x4b53bcf2, 0x52488db3, 0x7965de70, 0x607eef31, - 0xe7e6f3fe, 0xfefdc2bf, 0xd5d0917c, 0xcccba03d, 0x838a36fa, - 0x9a9107bb, 0xb1bc5478, 0xa8a76539, 0x3b83984b, 0x2298a90a, - 0x09b5fac9, 0x10aecb88, 0x5fef5d4f, 0x46f46c0e, 0x6dd93fcd, - 0x74c20e8c, 0xf35a1243, 0xea412302, 0xc16c70c1, 0xd8774180, - 0x9736d747, 0x8e2de606, 0xa500b5c5, 0xbc1b8484, 0x71418a1a, - 0x685abb5b, 0x4377e898, 0x5a6cd9d9, 0x152d4f1e, 0x0c367e5f, - 0x271b2d9c, 0x3e001cdd, 0xb9980012, 0xa0833153, 0x8bae6290, - 0x92b553d1, 0xddf4c516, 0xc4eff457, 0xefc2a794, 0xf6d996d5, - 0xae07bce9, 0xb71c8da8, 0x9c31de6b, 0x852aef2a, 0xca6b79ed, - 0xd37048ac, 0xf85d1b6f, 0xe1462a2e, 0x66de36e1, 0x7fc507a0, - 0x54e85463, 0x4df36522, 0x02b2f3e5, 0x1ba9c2a4, 0x30849167, - 0x299fa026, 0xe4c5aeb8, 0xfdde9ff9, 0xd6f3cc3a, 0xcfe8fd7b, - 0x80a96bbc, 0x99b25afd, 0xb29f093e, 0xab84387f, 0x2c1c24b0, - 0x350715f1, 0x1e2a4632, 0x07317773, 0x4870e1b4, 0x516bd0f5, - 0x7a468336, 0x635db277, 0xcbfad74e, 0xd2e1e60f, 0xf9ccb5cc, - 0xe0d7848d, 0xaf96124a, 0xb68d230b, 0x9da070c8, 0x84bb4189, - 0x03235d46, 0x1a386c07, 0x31153fc4, 0x280e0e85, 0x674f9842, - 0x7e54a903, 0x5579fac0, 0x4c62cb81, 0x8138c51f, 0x9823f45e, - 0xb30ea79d, 0xaa1596dc, 0xe554001b, 0xfc4f315a, 0xd7626299, - 0xce7953d8, 0x49e14f17, 0x50fa7e56, 0x7bd72d95, 0x62cc1cd4, - 0x2d8d8a13, 0x3496bb52, 0x1fbbe891, 0x06a0d9d0, 0x5e7ef3ec, - 0x4765c2ad, 0x6c48916e, 0x7553a02f, 0x3a1236e8, 0x230907a9, - 0x0824546a, 0x113f652b, 0x96a779e4, 0x8fbc48a5, 0xa4911b66, - 0xbd8a2a27, 0xf2cbbce0, 0xebd08da1, 0xc0fdde62, 0xd9e6ef23, - 0x14bce1bd, 0x0da7d0fc, 0x268a833f, 0x3f91b27e, 0x70d024b9, - 0x69cb15f8, 0x42e6463b, 0x5bfd777a, 0xdc656bb5, 0xc57e5af4, - 0xee530937, 0xf7483876, 0xb809aeb1, 0xa1129ff0, 0x8a3fcc33, - 0x9324fd72}, - {0x00000000, 0x77073096, 0xee0e612c, 0x990951ba, 0x076dc419, - 0x706af48f, 0xe963a535, 0x9e6495a3, 0x0edb8832, 0x79dcb8a4, - 0xe0d5e91e, 0x97d2d988, 0x09b64c2b, 0x7eb17cbd, 0xe7b82d07, - 0x90bf1d91, 0x1db71064, 0x6ab020f2, 0xf3b97148, 0x84be41de, - 0x1adad47d, 0x6ddde4eb, 0xf4d4b551, 0x83d385c7, 0x136c9856, - 0x646ba8c0, 0xfd62f97a, 0x8a65c9ec, 0x14015c4f, 0x63066cd9, - 0xfa0f3d63, 0x8d080df5, 0x3b6e20c8, 0x4c69105e, 0xd56041e4, - 0xa2677172, 0x3c03e4d1, 0x4b04d447, 0xd20d85fd, 0xa50ab56b, - 0x35b5a8fa, 0x42b2986c, 0xdbbbc9d6, 0xacbcf940, 0x32d86ce3, - 0x45df5c75, 0xdcd60dcf, 0xabd13d59, 0x26d930ac, 0x51de003a, - 0xc8d75180, 0xbfd06116, 0x21b4f4b5, 0x56b3c423, 0xcfba9599, - 0xb8bda50f, 0x2802b89e, 0x5f058808, 0xc60cd9b2, 0xb10be924, - 0x2f6f7c87, 0x58684c11, 0xc1611dab, 0xb6662d3d, 0x76dc4190, - 0x01db7106, 0x98d220bc, 0xefd5102a, 0x71b18589, 0x06b6b51f, - 0x9fbfe4a5, 0xe8b8d433, 0x7807c9a2, 0x0f00f934, 0x9609a88e, - 0xe10e9818, 0x7f6a0dbb, 0x086d3d2d, 0x91646c97, 0xe6635c01, - 0x6b6b51f4, 0x1c6c6162, 0x856530d8, 0xf262004e, 0x6c0695ed, - 0x1b01a57b, 0x8208f4c1, 0xf50fc457, 0x65b0d9c6, 0x12b7e950, - 0x8bbeb8ea, 0xfcb9887c, 0x62dd1ddf, 0x15da2d49, 0x8cd37cf3, - 0xfbd44c65, 0x4db26158, 0x3ab551ce, 0xa3bc0074, 0xd4bb30e2, - 0x4adfa541, 0x3dd895d7, 0xa4d1c46d, 0xd3d6f4fb, 0x4369e96a, - 0x346ed9fc, 0xad678846, 0xda60b8d0, 0x44042d73, 0x33031de5, - 0xaa0a4c5f, 0xdd0d7cc9, 0x5005713c, 0x270241aa, 0xbe0b1010, - 0xc90c2086, 0x5768b525, 0x206f85b3, 0xb966d409, 0xce61e49f, - 0x5edef90e, 0x29d9c998, 0xb0d09822, 0xc7d7a8b4, 0x59b33d17, - 0x2eb40d81, 0xb7bd5c3b, 0xc0ba6cad, 0xedb88320, 0x9abfb3b6, - 0x03b6e20c, 0x74b1d29a, 0xead54739, 0x9dd277af, 0x04db2615, - 0x73dc1683, 0xe3630b12, 0x94643b84, 0x0d6d6a3e, 0x7a6a5aa8, - 0xe40ecf0b, 0x9309ff9d, 0x0a00ae27, 0x7d079eb1, 0xf00f9344, - 0x8708a3d2, 0x1e01f268, 0x6906c2fe, 0xf762575d, 0x806567cb, - 0x196c3671, 0x6e6b06e7, 0xfed41b76, 0x89d32be0, 0x10da7a5a, - 0x67dd4acc, 0xf9b9df6f, 0x8ebeeff9, 0x17b7be43, 0x60b08ed5, - 0xd6d6a3e8, 0xa1d1937e, 0x38d8c2c4, 0x4fdff252, 0xd1bb67f1, - 0xa6bc5767, 0x3fb506dd, 0x48b2364b, 0xd80d2bda, 0xaf0a1b4c, - 0x36034af6, 0x41047a60, 0xdf60efc3, 0xa867df55, 0x316e8eef, - 0x4669be79, 0xcb61b38c, 0xbc66831a, 0x256fd2a0, 0x5268e236, - 0xcc0c7795, 0xbb0b4703, 0x220216b9, 0x5505262f, 0xc5ba3bbe, - 0xb2bd0b28, 0x2bb45a92, 0x5cb36a04, 0xc2d7ffa7, 0xb5d0cf31, - 0x2cd99e8b, 0x5bdeae1d, 0x9b64c2b0, 0xec63f226, 0x756aa39c, - 0x026d930a, 0x9c0906a9, 0xeb0e363f, 0x72076785, 0x05005713, - 0x95bf4a82, 0xe2b87a14, 0x7bb12bae, 0x0cb61b38, 0x92d28e9b, - 0xe5d5be0d, 0x7cdcefb7, 0x0bdbdf21, 0x86d3d2d4, 0xf1d4e242, - 0x68ddb3f8, 0x1fda836e, 0x81be16cd, 0xf6b9265b, 0x6fb077e1, - 0x18b74777, 0x88085ae6, 0xff0f6a70, 0x66063bca, 0x11010b5c, - 0x8f659eff, 0xf862ae69, 0x616bffd3, 0x166ccf45, 0xa00ae278, - 0xd70dd2ee, 0x4e048354, 0x3903b3c2, 0xa7672661, 0xd06016f7, - 0x4969474d, 0x3e6e77db, 0xaed16a4a, 0xd9d65adc, 0x40df0b66, - 0x37d83bf0, 0xa9bcae53, 0xdebb9ec5, 0x47b2cf7f, 0x30b5ffe9, - 0xbdbdf21c, 0xcabac28a, 0x53b39330, 0x24b4a3a6, 0xbad03605, - 0xcdd70693, 0x54de5729, 0x23d967bf, 0xb3667a2e, 0xc4614ab8, - 0x5d681b02, 0x2a6f2b94, 0xb40bbe37, 0xc30c8ea1, 0x5a05df1b, - 0x2d02ef8d}}; - -local const z_word_t FAR crc_braid_big_table[][256] = { - {0x0000000000000000, 0x9630077700000000, 0x2c610eee00000000, - 0xba51099900000000, 0x19c46d0700000000, 0x8ff46a7000000000, - 0x35a563e900000000, 0xa395649e00000000, 0x3288db0e00000000, - 0xa4b8dc7900000000, 0x1ee9d5e000000000, 0x88d9d29700000000, - 0x2b4cb60900000000, 0xbd7cb17e00000000, 0x072db8e700000000, - 0x911dbf9000000000, 0x6410b71d00000000, 0xf220b06a00000000, - 0x4871b9f300000000, 0xde41be8400000000, 0x7dd4da1a00000000, - 0xebe4dd6d00000000, 0x51b5d4f400000000, 0xc785d38300000000, - 0x56986c1300000000, 0xc0a86b6400000000, 0x7af962fd00000000, - 0xecc9658a00000000, 0x4f5c011400000000, 0xd96c066300000000, - 0x633d0ffa00000000, 0xf50d088d00000000, 0xc8206e3b00000000, - 0x5e10694c00000000, 0xe44160d500000000, 0x727167a200000000, - 0xd1e4033c00000000, 0x47d4044b00000000, 0xfd850dd200000000, - 0x6bb50aa500000000, 0xfaa8b53500000000, 0x6c98b24200000000, - 0xd6c9bbdb00000000, 0x40f9bcac00000000, 0xe36cd83200000000, - 0x755cdf4500000000, 0xcf0dd6dc00000000, 0x593dd1ab00000000, - 0xac30d92600000000, 0x3a00de5100000000, 0x8051d7c800000000, - 0x1661d0bf00000000, 0xb5f4b42100000000, 0x23c4b35600000000, - 0x9995bacf00000000, 0x0fa5bdb800000000, 0x9eb8022800000000, - 0x0888055f00000000, 0xb2d90cc600000000, 0x24e90bb100000000, - 0x877c6f2f00000000, 0x114c685800000000, 0xab1d61c100000000, - 0x3d2d66b600000000, 0x9041dc7600000000, 0x0671db0100000000, - 0xbc20d29800000000, 0x2a10d5ef00000000, 0x8985b17100000000, - 0x1fb5b60600000000, 0xa5e4bf9f00000000, 0x33d4b8e800000000, - 0xa2c9077800000000, 0x34f9000f00000000, 0x8ea8099600000000, - 0x18980ee100000000, 0xbb0d6a7f00000000, 0x2d3d6d0800000000, - 0x976c649100000000, 0x015c63e600000000, 0xf4516b6b00000000, - 0x62616c1c00000000, 0xd830658500000000, 0x4e0062f200000000, - 0xed95066c00000000, 0x7ba5011b00000000, 0xc1f4088200000000, - 0x57c40ff500000000, 0xc6d9b06500000000, 0x50e9b71200000000, - 0xeab8be8b00000000, 0x7c88b9fc00000000, 0xdf1ddd6200000000, - 0x492dda1500000000, 0xf37cd38c00000000, 0x654cd4fb00000000, - 0x5861b24d00000000, 0xce51b53a00000000, 0x7400bca300000000, - 0xe230bbd400000000, 0x41a5df4a00000000, 0xd795d83d00000000, - 0x6dc4d1a400000000, 0xfbf4d6d300000000, 0x6ae9694300000000, - 0xfcd96e3400000000, 0x468867ad00000000, 0xd0b860da00000000, - 0x732d044400000000, 0xe51d033300000000, 0x5f4c0aaa00000000, - 0xc97c0ddd00000000, 0x3c71055000000000, 0xaa41022700000000, - 0x10100bbe00000000, 0x86200cc900000000, 0x25b5685700000000, - 0xb3856f2000000000, 0x09d466b900000000, 0x9fe461ce00000000, - 0x0ef9de5e00000000, 0x98c9d92900000000, 0x2298d0b000000000, - 0xb4a8d7c700000000, 0x173db35900000000, 0x810db42e00000000, - 0x3b5cbdb700000000, 0xad6cbac000000000, 0x2083b8ed00000000, - 0xb6b3bf9a00000000, 0x0ce2b60300000000, 0x9ad2b17400000000, - 0x3947d5ea00000000, 0xaf77d29d00000000, 0x1526db0400000000, - 0x8316dc7300000000, 0x120b63e300000000, 0x843b649400000000, - 0x3e6a6d0d00000000, 0xa85a6a7a00000000, 0x0bcf0ee400000000, - 0x9dff099300000000, 0x27ae000a00000000, 0xb19e077d00000000, - 0x44930ff000000000, 0xd2a3088700000000, 0x68f2011e00000000, - 0xfec2066900000000, 0x5d5762f700000000, 0xcb67658000000000, - 0x71366c1900000000, 0xe7066b6e00000000, 0x761bd4fe00000000, - 0xe02bd38900000000, 0x5a7ada1000000000, 0xcc4add6700000000, - 0x6fdfb9f900000000, 0xf9efbe8e00000000, 0x43beb71700000000, - 0xd58eb06000000000, 0xe8a3d6d600000000, 0x7e93d1a100000000, - 0xc4c2d83800000000, 0x52f2df4f00000000, 0xf167bbd100000000, - 0x6757bca600000000, 0xdd06b53f00000000, 0x4b36b24800000000, - 0xda2b0dd800000000, 0x4c1b0aaf00000000, 0xf64a033600000000, - 0x607a044100000000, 0xc3ef60df00000000, 0x55df67a800000000, - 0xef8e6e3100000000, 0x79be694600000000, 0x8cb361cb00000000, - 0x1a8366bc00000000, 0xa0d26f2500000000, 0x36e2685200000000, - 0x95770ccc00000000, 0x03470bbb00000000, 0xb916022200000000, - 0x2f26055500000000, 0xbe3bbac500000000, 0x280bbdb200000000, - 0x925ab42b00000000, 0x046ab35c00000000, 0xa7ffd7c200000000, - 0x31cfd0b500000000, 0x8b9ed92c00000000, 0x1daede5b00000000, - 0xb0c2649b00000000, 0x26f263ec00000000, 0x9ca36a7500000000, - 0x0a936d0200000000, 0xa906099c00000000, 0x3f360eeb00000000, - 0x8567077200000000, 0x1357000500000000, 0x824abf9500000000, - 0x147ab8e200000000, 0xae2bb17b00000000, 0x381bb60c00000000, - 0x9b8ed29200000000, 0x0dbed5e500000000, 0xb7efdc7c00000000, - 0x21dfdb0b00000000, 0xd4d2d38600000000, 0x42e2d4f100000000, - 0xf8b3dd6800000000, 0x6e83da1f00000000, 0xcd16be8100000000, - 0x5b26b9f600000000, 0xe177b06f00000000, 0x7747b71800000000, - 0xe65a088800000000, 0x706a0fff00000000, 0xca3b066600000000, - 0x5c0b011100000000, 0xff9e658f00000000, 0x69ae62f800000000, - 0xd3ff6b6100000000, 0x45cf6c1600000000, 0x78e20aa000000000, - 0xeed20dd700000000, 0x5483044e00000000, 0xc2b3033900000000, - 0x612667a700000000, 0xf71660d000000000, 0x4d47694900000000, - 0xdb776e3e00000000, 0x4a6ad1ae00000000, 0xdc5ad6d900000000, - 0x660bdf4000000000, 0xf03bd83700000000, 0x53aebca900000000, - 0xc59ebbde00000000, 0x7fcfb24700000000, 0xe9ffb53000000000, - 0x1cf2bdbd00000000, 0x8ac2baca00000000, 0x3093b35300000000, - 0xa6a3b42400000000, 0x0536d0ba00000000, 0x9306d7cd00000000, - 0x2957de5400000000, 0xbf67d92300000000, 0x2e7a66b300000000, - 0xb84a61c400000000, 0x021b685d00000000, 0x942b6f2a00000000, - 0x37be0bb400000000, 0xa18e0cc300000000, 0x1bdf055a00000000, - 0x8def022d00000000}, - {0x0000000000000000, 0x41311b1900000000, 0x8262363200000000, - 0xc3532d2b00000000, 0x04c56c6400000000, 0x45f4777d00000000, - 0x86a75a5600000000, 0xc796414f00000000, 0x088ad9c800000000, - 0x49bbc2d100000000, 0x8ae8effa00000000, 0xcbd9f4e300000000, - 0x0c4fb5ac00000000, 0x4d7eaeb500000000, 0x8e2d839e00000000, - 0xcf1c988700000000, 0x5112c24a00000000, 0x1023d95300000000, - 0xd370f47800000000, 0x9241ef6100000000, 0x55d7ae2e00000000, - 0x14e6b53700000000, 0xd7b5981c00000000, 0x9684830500000000, - 0x59981b8200000000, 0x18a9009b00000000, 0xdbfa2db000000000, - 0x9acb36a900000000, 0x5d5d77e600000000, 0x1c6c6cff00000000, - 0xdf3f41d400000000, 0x9e0e5acd00000000, 0xa224849500000000, - 0xe3159f8c00000000, 0x2046b2a700000000, 0x6177a9be00000000, - 0xa6e1e8f100000000, 0xe7d0f3e800000000, 0x2483dec300000000, - 0x65b2c5da00000000, 0xaaae5d5d00000000, 0xeb9f464400000000, - 0x28cc6b6f00000000, 0x69fd707600000000, 0xae6b313900000000, - 0xef5a2a2000000000, 0x2c09070b00000000, 0x6d381c1200000000, - 0xf33646df00000000, 0xb2075dc600000000, 0x715470ed00000000, - 0x30656bf400000000, 0xf7f32abb00000000, 0xb6c231a200000000, - 0x75911c8900000000, 0x34a0079000000000, 0xfbbc9f1700000000, - 0xba8d840e00000000, 0x79dea92500000000, 0x38efb23c00000000, - 0xff79f37300000000, 0xbe48e86a00000000, 0x7d1bc54100000000, - 0x3c2ade5800000000, 0x054f79f000000000, 0x447e62e900000000, - 0x872d4fc200000000, 0xc61c54db00000000, 0x018a159400000000, - 0x40bb0e8d00000000, 0x83e823a600000000, 0xc2d938bf00000000, - 0x0dc5a03800000000, 0x4cf4bb2100000000, 0x8fa7960a00000000, - 0xce968d1300000000, 0x0900cc5c00000000, 0x4831d74500000000, - 0x8b62fa6e00000000, 0xca53e17700000000, 0x545dbbba00000000, - 0x156ca0a300000000, 0xd63f8d8800000000, 0x970e969100000000, - 0x5098d7de00000000, 0x11a9ccc700000000, 0xd2fae1ec00000000, - 0x93cbfaf500000000, 0x5cd7627200000000, 0x1de6796b00000000, - 0xdeb5544000000000, 0x9f844f5900000000, 0x58120e1600000000, - 0x1923150f00000000, 0xda70382400000000, 0x9b41233d00000000, - 0xa76bfd6500000000, 0xe65ae67c00000000, 0x2509cb5700000000, - 0x6438d04e00000000, 0xa3ae910100000000, 0xe29f8a1800000000, - 0x21cca73300000000, 0x60fdbc2a00000000, 0xafe124ad00000000, - 0xeed03fb400000000, 0x2d83129f00000000, 0x6cb2098600000000, - 0xab2448c900000000, 0xea1553d000000000, 0x29467efb00000000, - 0x687765e200000000, 0xf6793f2f00000000, 0xb748243600000000, - 0x741b091d00000000, 0x352a120400000000, 0xf2bc534b00000000, - 0xb38d485200000000, 0x70de657900000000, 0x31ef7e6000000000, - 0xfef3e6e700000000, 0xbfc2fdfe00000000, 0x7c91d0d500000000, - 0x3da0cbcc00000000, 0xfa368a8300000000, 0xbb07919a00000000, - 0x7854bcb100000000, 0x3965a7a800000000, 0x4b98833b00000000, - 0x0aa9982200000000, 0xc9fab50900000000, 0x88cbae1000000000, - 0x4f5def5f00000000, 0x0e6cf44600000000, 0xcd3fd96d00000000, - 0x8c0ec27400000000, 0x43125af300000000, 0x022341ea00000000, - 0xc1706cc100000000, 0x804177d800000000, 0x47d7369700000000, - 0x06e62d8e00000000, 0xc5b500a500000000, 0x84841bbc00000000, - 0x1a8a417100000000, 0x5bbb5a6800000000, 0x98e8774300000000, - 0xd9d96c5a00000000, 0x1e4f2d1500000000, 0x5f7e360c00000000, - 0x9c2d1b2700000000, 0xdd1c003e00000000, 0x120098b900000000, - 0x533183a000000000, 0x9062ae8b00000000, 0xd153b59200000000, - 0x16c5f4dd00000000, 0x57f4efc400000000, 0x94a7c2ef00000000, - 0xd596d9f600000000, 0xe9bc07ae00000000, 0xa88d1cb700000000, - 0x6bde319c00000000, 0x2aef2a8500000000, 0xed796bca00000000, - 0xac4870d300000000, 0x6f1b5df800000000, 0x2e2a46e100000000, - 0xe136de6600000000, 0xa007c57f00000000, 0x6354e85400000000, - 0x2265f34d00000000, 0xe5f3b20200000000, 0xa4c2a91b00000000, - 0x6791843000000000, 0x26a09f2900000000, 0xb8aec5e400000000, - 0xf99fdefd00000000, 0x3accf3d600000000, 0x7bfde8cf00000000, - 0xbc6ba98000000000, 0xfd5ab29900000000, 0x3e099fb200000000, - 0x7f3884ab00000000, 0xb0241c2c00000000, 0xf115073500000000, - 0x32462a1e00000000, 0x7377310700000000, 0xb4e1704800000000, - 0xf5d06b5100000000, 0x3683467a00000000, 0x77b25d6300000000, - 0x4ed7facb00000000, 0x0fe6e1d200000000, 0xccb5ccf900000000, - 0x8d84d7e000000000, 0x4a1296af00000000, 0x0b238db600000000, - 0xc870a09d00000000, 0x8941bb8400000000, 0x465d230300000000, - 0x076c381a00000000, 0xc43f153100000000, 0x850e0e2800000000, - 0x42984f6700000000, 0x03a9547e00000000, 0xc0fa795500000000, - 0x81cb624c00000000, 0x1fc5388100000000, 0x5ef4239800000000, - 0x9da70eb300000000, 0xdc9615aa00000000, 0x1b0054e500000000, - 0x5a314ffc00000000, 0x996262d700000000, 0xd85379ce00000000, - 0x174fe14900000000, 0x567efa5000000000, 0x952dd77b00000000, - 0xd41ccc6200000000, 0x138a8d2d00000000, 0x52bb963400000000, - 0x91e8bb1f00000000, 0xd0d9a00600000000, 0xecf37e5e00000000, - 0xadc2654700000000, 0x6e91486c00000000, 0x2fa0537500000000, - 0xe836123a00000000, 0xa907092300000000, 0x6a54240800000000, - 0x2b653f1100000000, 0xe479a79600000000, 0xa548bc8f00000000, - 0x661b91a400000000, 0x272a8abd00000000, 0xe0bccbf200000000, - 0xa18dd0eb00000000, 0x62defdc000000000, 0x23efe6d900000000, - 0xbde1bc1400000000, 0xfcd0a70d00000000, 0x3f838a2600000000, - 0x7eb2913f00000000, 0xb924d07000000000, 0xf815cb6900000000, - 0x3b46e64200000000, 0x7a77fd5b00000000, 0xb56b65dc00000000, - 0xf45a7ec500000000, 0x370953ee00000000, 0x763848f700000000, - 0xb1ae09b800000000, 0xf09f12a100000000, 0x33cc3f8a00000000, - 0x72fd249300000000}, - {0x0000000000000000, 0x376ac20100000000, 0x6ed4840300000000, - 0x59be460200000000, 0xdca8090700000000, 0xebc2cb0600000000, - 0xb27c8d0400000000, 0x85164f0500000000, 0xb851130e00000000, - 0x8f3bd10f00000000, 0xd685970d00000000, 0xe1ef550c00000000, - 0x64f91a0900000000, 0x5393d80800000000, 0x0a2d9e0a00000000, - 0x3d475c0b00000000, 0x70a3261c00000000, 0x47c9e41d00000000, - 0x1e77a21f00000000, 0x291d601e00000000, 0xac0b2f1b00000000, - 0x9b61ed1a00000000, 0xc2dfab1800000000, 0xf5b5691900000000, - 0xc8f2351200000000, 0xff98f71300000000, 0xa626b11100000000, - 0x914c731000000000, 0x145a3c1500000000, 0x2330fe1400000000, - 0x7a8eb81600000000, 0x4de47a1700000000, 0xe0464d3800000000, - 0xd72c8f3900000000, 0x8e92c93b00000000, 0xb9f80b3a00000000, - 0x3cee443f00000000, 0x0b84863e00000000, 0x523ac03c00000000, - 0x6550023d00000000, 0x58175e3600000000, 0x6f7d9c3700000000, - 0x36c3da3500000000, 0x01a9183400000000, 0x84bf573100000000, - 0xb3d5953000000000, 0xea6bd33200000000, 0xdd01113300000000, - 0x90e56b2400000000, 0xa78fa92500000000, 0xfe31ef2700000000, - 0xc95b2d2600000000, 0x4c4d622300000000, 0x7b27a02200000000, - 0x2299e62000000000, 0x15f3242100000000, 0x28b4782a00000000, - 0x1fdeba2b00000000, 0x4660fc2900000000, 0x710a3e2800000000, - 0xf41c712d00000000, 0xc376b32c00000000, 0x9ac8f52e00000000, - 0xada2372f00000000, 0xc08d9a7000000000, 0xf7e7587100000000, - 0xae591e7300000000, 0x9933dc7200000000, 0x1c25937700000000, - 0x2b4f517600000000, 0x72f1177400000000, 0x459bd57500000000, - 0x78dc897e00000000, 0x4fb64b7f00000000, 0x16080d7d00000000, - 0x2162cf7c00000000, 0xa474807900000000, 0x931e427800000000, - 0xcaa0047a00000000, 0xfdcac67b00000000, 0xb02ebc6c00000000, - 0x87447e6d00000000, 0xdefa386f00000000, 0xe990fa6e00000000, - 0x6c86b56b00000000, 0x5bec776a00000000, 0x0252316800000000, - 0x3538f36900000000, 0x087faf6200000000, 0x3f156d6300000000, - 0x66ab2b6100000000, 0x51c1e96000000000, 0xd4d7a66500000000, - 0xe3bd646400000000, 0xba03226600000000, 0x8d69e06700000000, - 0x20cbd74800000000, 0x17a1154900000000, 0x4e1f534b00000000, - 0x7975914a00000000, 0xfc63de4f00000000, 0xcb091c4e00000000, - 0x92b75a4c00000000, 0xa5dd984d00000000, 0x989ac44600000000, - 0xaff0064700000000, 0xf64e404500000000, 0xc124824400000000, - 0x4432cd4100000000, 0x73580f4000000000, 0x2ae6494200000000, - 0x1d8c8b4300000000, 0x5068f15400000000, 0x6702335500000000, - 0x3ebc755700000000, 0x09d6b75600000000, 0x8cc0f85300000000, - 0xbbaa3a5200000000, 0xe2147c5000000000, 0xd57ebe5100000000, - 0xe839e25a00000000, 0xdf53205b00000000, 0x86ed665900000000, - 0xb187a45800000000, 0x3491eb5d00000000, 0x03fb295c00000000, - 0x5a456f5e00000000, 0x6d2fad5f00000000, 0x801b35e100000000, - 0xb771f7e000000000, 0xeecfb1e200000000, 0xd9a573e300000000, - 0x5cb33ce600000000, 0x6bd9fee700000000, 0x3267b8e500000000, - 0x050d7ae400000000, 0x384a26ef00000000, 0x0f20e4ee00000000, - 0x569ea2ec00000000, 0x61f460ed00000000, 0xe4e22fe800000000, - 0xd388ede900000000, 0x8a36abeb00000000, 0xbd5c69ea00000000, - 0xf0b813fd00000000, 0xc7d2d1fc00000000, 0x9e6c97fe00000000, - 0xa90655ff00000000, 0x2c101afa00000000, 0x1b7ad8fb00000000, - 0x42c49ef900000000, 0x75ae5cf800000000, 0x48e900f300000000, - 0x7f83c2f200000000, 0x263d84f000000000, 0x115746f100000000, - 0x944109f400000000, 0xa32bcbf500000000, 0xfa958df700000000, - 0xcdff4ff600000000, 0x605d78d900000000, 0x5737bad800000000, - 0x0e89fcda00000000, 0x39e33edb00000000, 0xbcf571de00000000, - 0x8b9fb3df00000000, 0xd221f5dd00000000, 0xe54b37dc00000000, - 0xd80c6bd700000000, 0xef66a9d600000000, 0xb6d8efd400000000, - 0x81b22dd500000000, 0x04a462d000000000, 0x33cea0d100000000, - 0x6a70e6d300000000, 0x5d1a24d200000000, 0x10fe5ec500000000, - 0x27949cc400000000, 0x7e2adac600000000, 0x494018c700000000, - 0xcc5657c200000000, 0xfb3c95c300000000, 0xa282d3c100000000, - 0x95e811c000000000, 0xa8af4dcb00000000, 0x9fc58fca00000000, - 0xc67bc9c800000000, 0xf1110bc900000000, 0x740744cc00000000, - 0x436d86cd00000000, 0x1ad3c0cf00000000, 0x2db902ce00000000, - 0x4096af9100000000, 0x77fc6d9000000000, 0x2e422b9200000000, - 0x1928e99300000000, 0x9c3ea69600000000, 0xab54649700000000, - 0xf2ea229500000000, 0xc580e09400000000, 0xf8c7bc9f00000000, - 0xcfad7e9e00000000, 0x9613389c00000000, 0xa179fa9d00000000, - 0x246fb59800000000, 0x1305779900000000, 0x4abb319b00000000, - 0x7dd1f39a00000000, 0x3035898d00000000, 0x075f4b8c00000000, - 0x5ee10d8e00000000, 0x698bcf8f00000000, 0xec9d808a00000000, - 0xdbf7428b00000000, 0x8249048900000000, 0xb523c68800000000, - 0x88649a8300000000, 0xbf0e588200000000, 0xe6b01e8000000000, - 0xd1dadc8100000000, 0x54cc938400000000, 0x63a6518500000000, - 0x3a18178700000000, 0x0d72d58600000000, 0xa0d0e2a900000000, - 0x97ba20a800000000, 0xce0466aa00000000, 0xf96ea4ab00000000, - 0x7c78ebae00000000, 0x4b1229af00000000, 0x12ac6fad00000000, - 0x25c6adac00000000, 0x1881f1a700000000, 0x2feb33a600000000, - 0x765575a400000000, 0x413fb7a500000000, 0xc429f8a000000000, - 0xf3433aa100000000, 0xaafd7ca300000000, 0x9d97bea200000000, - 0xd073c4b500000000, 0xe71906b400000000, 0xbea740b600000000, - 0x89cd82b700000000, 0x0cdbcdb200000000, 0x3bb10fb300000000, - 0x620f49b100000000, 0x55658bb000000000, 0x6822d7bb00000000, - 0x5f4815ba00000000, 0x06f653b800000000, 0x319c91b900000000, - 0xb48adebc00000000, 0x83e01cbd00000000, 0xda5e5abf00000000, - 0xed3498be00000000}, - {0x0000000000000000, 0x6567bcb800000000, 0x8bc809aa00000000, - 0xeeafb51200000000, 0x5797628f00000000, 0x32f0de3700000000, - 0xdc5f6b2500000000, 0xb938d79d00000000, 0xef28b4c500000000, - 0x8a4f087d00000000, 0x64e0bd6f00000000, 0x018701d700000000, - 0xb8bfd64a00000000, 0xddd86af200000000, 0x3377dfe000000000, - 0x5610635800000000, 0x9f57195000000000, 0xfa30a5e800000000, - 0x149f10fa00000000, 0x71f8ac4200000000, 0xc8c07bdf00000000, - 0xada7c76700000000, 0x4308727500000000, 0x266fcecd00000000, - 0x707fad9500000000, 0x1518112d00000000, 0xfbb7a43f00000000, - 0x9ed0188700000000, 0x27e8cf1a00000000, 0x428f73a200000000, - 0xac20c6b000000000, 0xc9477a0800000000, 0x3eaf32a000000000, - 0x5bc88e1800000000, 0xb5673b0a00000000, 0xd00087b200000000, - 0x6938502f00000000, 0x0c5fec9700000000, 0xe2f0598500000000, - 0x8797e53d00000000, 0xd187866500000000, 0xb4e03add00000000, - 0x5a4f8fcf00000000, 0x3f28337700000000, 0x8610e4ea00000000, - 0xe377585200000000, 0x0dd8ed4000000000, 0x68bf51f800000000, - 0xa1f82bf000000000, 0xc49f974800000000, 0x2a30225a00000000, - 0x4f579ee200000000, 0xf66f497f00000000, 0x9308f5c700000000, - 0x7da740d500000000, 0x18c0fc6d00000000, 0x4ed09f3500000000, - 0x2bb7238d00000000, 0xc518969f00000000, 0xa07f2a2700000000, - 0x1947fdba00000000, 0x7c20410200000000, 0x928ff41000000000, - 0xf7e848a800000000, 0x3d58149b00000000, 0x583fa82300000000, - 0xb6901d3100000000, 0xd3f7a18900000000, 0x6acf761400000000, - 0x0fa8caac00000000, 0xe1077fbe00000000, 0x8460c30600000000, - 0xd270a05e00000000, 0xb7171ce600000000, 0x59b8a9f400000000, - 0x3cdf154c00000000, 0x85e7c2d100000000, 0xe0807e6900000000, - 0x0e2fcb7b00000000, 0x6b4877c300000000, 0xa20f0dcb00000000, - 0xc768b17300000000, 0x29c7046100000000, 0x4ca0b8d900000000, - 0xf5986f4400000000, 0x90ffd3fc00000000, 0x7e5066ee00000000, - 0x1b37da5600000000, 0x4d27b90e00000000, 0x284005b600000000, - 0xc6efb0a400000000, 0xa3880c1c00000000, 0x1ab0db8100000000, - 0x7fd7673900000000, 0x9178d22b00000000, 0xf41f6e9300000000, - 0x03f7263b00000000, 0x66909a8300000000, 0x883f2f9100000000, - 0xed58932900000000, 0x546044b400000000, 0x3107f80c00000000, - 0xdfa84d1e00000000, 0xbacff1a600000000, 0xecdf92fe00000000, - 0x89b82e4600000000, 0x67179b5400000000, 0x027027ec00000000, - 0xbb48f07100000000, 0xde2f4cc900000000, 0x3080f9db00000000, - 0x55e7456300000000, 0x9ca03f6b00000000, 0xf9c783d300000000, - 0x176836c100000000, 0x720f8a7900000000, 0xcb375de400000000, - 0xae50e15c00000000, 0x40ff544e00000000, 0x2598e8f600000000, - 0x73888bae00000000, 0x16ef371600000000, 0xf840820400000000, - 0x9d273ebc00000000, 0x241fe92100000000, 0x4178559900000000, - 0xafd7e08b00000000, 0xcab05c3300000000, 0x3bb659ed00000000, - 0x5ed1e55500000000, 0xb07e504700000000, 0xd519ecff00000000, - 0x6c213b6200000000, 0x094687da00000000, 0xe7e932c800000000, - 0x828e8e7000000000, 0xd49eed2800000000, 0xb1f9519000000000, - 0x5f56e48200000000, 0x3a31583a00000000, 0x83098fa700000000, - 0xe66e331f00000000, 0x08c1860d00000000, 0x6da63ab500000000, - 0xa4e140bd00000000, 0xc186fc0500000000, 0x2f29491700000000, - 0x4a4ef5af00000000, 0xf376223200000000, 0x96119e8a00000000, - 0x78be2b9800000000, 0x1dd9972000000000, 0x4bc9f47800000000, - 0x2eae48c000000000, 0xc001fdd200000000, 0xa566416a00000000, - 0x1c5e96f700000000, 0x79392a4f00000000, 0x97969f5d00000000, - 0xf2f123e500000000, 0x05196b4d00000000, 0x607ed7f500000000, - 0x8ed162e700000000, 0xebb6de5f00000000, 0x528e09c200000000, - 0x37e9b57a00000000, 0xd946006800000000, 0xbc21bcd000000000, - 0xea31df8800000000, 0x8f56633000000000, 0x61f9d62200000000, - 0x049e6a9a00000000, 0xbda6bd0700000000, 0xd8c101bf00000000, - 0x366eb4ad00000000, 0x5309081500000000, 0x9a4e721d00000000, - 0xff29cea500000000, 0x11867bb700000000, 0x74e1c70f00000000, - 0xcdd9109200000000, 0xa8beac2a00000000, 0x4611193800000000, - 0x2376a58000000000, 0x7566c6d800000000, 0x10017a6000000000, - 0xfeaecf7200000000, 0x9bc973ca00000000, 0x22f1a45700000000, - 0x479618ef00000000, 0xa939adfd00000000, 0xcc5e114500000000, - 0x06ee4d7600000000, 0x6389f1ce00000000, 0x8d2644dc00000000, - 0xe841f86400000000, 0x51792ff900000000, 0x341e934100000000, - 0xdab1265300000000, 0xbfd69aeb00000000, 0xe9c6f9b300000000, - 0x8ca1450b00000000, 0x620ef01900000000, 0x07694ca100000000, - 0xbe519b3c00000000, 0xdb36278400000000, 0x3599929600000000, - 0x50fe2e2e00000000, 0x99b9542600000000, 0xfcdee89e00000000, - 0x12715d8c00000000, 0x7716e13400000000, 0xce2e36a900000000, - 0xab498a1100000000, 0x45e63f0300000000, 0x208183bb00000000, - 0x7691e0e300000000, 0x13f65c5b00000000, 0xfd59e94900000000, - 0x983e55f100000000, 0x2106826c00000000, 0x44613ed400000000, - 0xaace8bc600000000, 0xcfa9377e00000000, 0x38417fd600000000, - 0x5d26c36e00000000, 0xb389767c00000000, 0xd6eecac400000000, - 0x6fd61d5900000000, 0x0ab1a1e100000000, 0xe41e14f300000000, - 0x8179a84b00000000, 0xd769cb1300000000, 0xb20e77ab00000000, - 0x5ca1c2b900000000, 0x39c67e0100000000, 0x80fea99c00000000, - 0xe599152400000000, 0x0b36a03600000000, 0x6e511c8e00000000, - 0xa716668600000000, 0xc271da3e00000000, 0x2cde6f2c00000000, - 0x49b9d39400000000, 0xf081040900000000, 0x95e6b8b100000000, - 0x7b490da300000000, 0x1e2eb11b00000000, 0x483ed24300000000, - 0x2d596efb00000000, 0xc3f6dbe900000000, 0xa691675100000000, - 0x1fa9b0cc00000000, 0x7ace0c7400000000, 0x9461b96600000000, - 0xf10605de00000000}, - {0x0000000000000000, 0xb029603d00000000, 0x6053c07a00000000, - 0xd07aa04700000000, 0xc0a680f500000000, 0x708fe0c800000000, - 0xa0f5408f00000000, 0x10dc20b200000000, 0xc14b703000000000, - 0x7162100d00000000, 0xa118b04a00000000, 0x1131d07700000000, - 0x01edf0c500000000, 0xb1c490f800000000, 0x61be30bf00000000, - 0xd197508200000000, 0x8297e06000000000, 0x32be805d00000000, - 0xe2c4201a00000000, 0x52ed402700000000, 0x4231609500000000, - 0xf21800a800000000, 0x2262a0ef00000000, 0x924bc0d200000000, - 0x43dc905000000000, 0xf3f5f06d00000000, 0x238f502a00000000, - 0x93a6301700000000, 0x837a10a500000000, 0x3353709800000000, - 0xe329d0df00000000, 0x5300b0e200000000, 0x042fc1c100000000, - 0xb406a1fc00000000, 0x647c01bb00000000, 0xd455618600000000, - 0xc489413400000000, 0x74a0210900000000, 0xa4da814e00000000, - 0x14f3e17300000000, 0xc564b1f100000000, 0x754dd1cc00000000, - 0xa537718b00000000, 0x151e11b600000000, 0x05c2310400000000, - 0xb5eb513900000000, 0x6591f17e00000000, 0xd5b8914300000000, - 0x86b821a100000000, 0x3691419c00000000, 0xe6ebe1db00000000, - 0x56c281e600000000, 0x461ea15400000000, 0xf637c16900000000, - 0x264d612e00000000, 0x9664011300000000, 0x47f3519100000000, - 0xf7da31ac00000000, 0x27a091eb00000000, 0x9789f1d600000000, - 0x8755d16400000000, 0x377cb15900000000, 0xe706111e00000000, - 0x572f712300000000, 0x4958f35800000000, 0xf971936500000000, - 0x290b332200000000, 0x9922531f00000000, 0x89fe73ad00000000, - 0x39d7139000000000, 0xe9adb3d700000000, 0x5984d3ea00000000, - 0x8813836800000000, 0x383ae35500000000, 0xe840431200000000, - 0x5869232f00000000, 0x48b5039d00000000, 0xf89c63a000000000, - 0x28e6c3e700000000, 0x98cfa3da00000000, 0xcbcf133800000000, - 0x7be6730500000000, 0xab9cd34200000000, 0x1bb5b37f00000000, - 0x0b6993cd00000000, 0xbb40f3f000000000, 0x6b3a53b700000000, - 0xdb13338a00000000, 0x0a84630800000000, 0xbaad033500000000, - 0x6ad7a37200000000, 0xdafec34f00000000, 0xca22e3fd00000000, - 0x7a0b83c000000000, 0xaa71238700000000, 0x1a5843ba00000000, - 0x4d77329900000000, 0xfd5e52a400000000, 0x2d24f2e300000000, - 0x9d0d92de00000000, 0x8dd1b26c00000000, 0x3df8d25100000000, - 0xed82721600000000, 0x5dab122b00000000, 0x8c3c42a900000000, - 0x3c15229400000000, 0xec6f82d300000000, 0x5c46e2ee00000000, - 0x4c9ac25c00000000, 0xfcb3a26100000000, 0x2cc9022600000000, - 0x9ce0621b00000000, 0xcfe0d2f900000000, 0x7fc9b2c400000000, - 0xafb3128300000000, 0x1f9a72be00000000, 0x0f46520c00000000, - 0xbf6f323100000000, 0x6f15927600000000, 0xdf3cf24b00000000, - 0x0eaba2c900000000, 0xbe82c2f400000000, 0x6ef862b300000000, - 0xded1028e00000000, 0xce0d223c00000000, 0x7e24420100000000, - 0xae5ee24600000000, 0x1e77827b00000000, 0x92b0e6b100000000, - 0x2299868c00000000, 0xf2e326cb00000000, 0x42ca46f600000000, - 0x5216664400000000, 0xe23f067900000000, 0x3245a63e00000000, - 0x826cc60300000000, 0x53fb968100000000, 0xe3d2f6bc00000000, - 0x33a856fb00000000, 0x838136c600000000, 0x935d167400000000, - 0x2374764900000000, 0xf30ed60e00000000, 0x4327b63300000000, - 0x102706d100000000, 0xa00e66ec00000000, 0x7074c6ab00000000, - 0xc05da69600000000, 0xd081862400000000, 0x60a8e61900000000, - 0xb0d2465e00000000, 0x00fb266300000000, 0xd16c76e100000000, - 0x614516dc00000000, 0xb13fb69b00000000, 0x0116d6a600000000, - 0x11caf61400000000, 0xa1e3962900000000, 0x7199366e00000000, - 0xc1b0565300000000, 0x969f277000000000, 0x26b6474d00000000, - 0xf6cce70a00000000, 0x46e5873700000000, 0x5639a78500000000, - 0xe610c7b800000000, 0x366a67ff00000000, 0x864307c200000000, - 0x57d4574000000000, 0xe7fd377d00000000, 0x3787973a00000000, - 0x87aef70700000000, 0x9772d7b500000000, 0x275bb78800000000, - 0xf72117cf00000000, 0x470877f200000000, 0x1408c71000000000, - 0xa421a72d00000000, 0x745b076a00000000, 0xc472675700000000, - 0xd4ae47e500000000, 0x648727d800000000, 0xb4fd879f00000000, - 0x04d4e7a200000000, 0xd543b72000000000, 0x656ad71d00000000, - 0xb510775a00000000, 0x0539176700000000, 0x15e537d500000000, - 0xa5cc57e800000000, 0x75b6f7af00000000, 0xc59f979200000000, - 0xdbe815e900000000, 0x6bc175d400000000, 0xbbbbd59300000000, - 0x0b92b5ae00000000, 0x1b4e951c00000000, 0xab67f52100000000, - 0x7b1d556600000000, 0xcb34355b00000000, 0x1aa365d900000000, - 0xaa8a05e400000000, 0x7af0a5a300000000, 0xcad9c59e00000000, - 0xda05e52c00000000, 0x6a2c851100000000, 0xba56255600000000, - 0x0a7f456b00000000, 0x597ff58900000000, 0xe95695b400000000, - 0x392c35f300000000, 0x890555ce00000000, 0x99d9757c00000000, - 0x29f0154100000000, 0xf98ab50600000000, 0x49a3d53b00000000, - 0x983485b900000000, 0x281de58400000000, 0xf86745c300000000, - 0x484e25fe00000000, 0x5892054c00000000, 0xe8bb657100000000, - 0x38c1c53600000000, 0x88e8a50b00000000, 0xdfc7d42800000000, - 0x6feeb41500000000, 0xbf94145200000000, 0x0fbd746f00000000, - 0x1f6154dd00000000, 0xaf4834e000000000, 0x7f3294a700000000, - 0xcf1bf49a00000000, 0x1e8ca41800000000, 0xaea5c42500000000, - 0x7edf646200000000, 0xcef6045f00000000, 0xde2a24ed00000000, - 0x6e0344d000000000, 0xbe79e49700000000, 0x0e5084aa00000000, - 0x5d50344800000000, 0xed79547500000000, 0x3d03f43200000000, - 0x8d2a940f00000000, 0x9df6b4bd00000000, 0x2ddfd48000000000, - 0xfda574c700000000, 0x4d8c14fa00000000, 0x9c1b447800000000, - 0x2c32244500000000, 0xfc48840200000000, 0x4c61e43f00000000, - 0x5cbdc48d00000000, 0xec94a4b000000000, 0x3cee04f700000000, - 0x8cc764ca00000000}, - {0x0000000000000000, 0xa5d35ccb00000000, 0x0ba1c84d00000000, - 0xae72948600000000, 0x1642919b00000000, 0xb391cd5000000000, - 0x1de359d600000000, 0xb830051d00000000, 0x6d8253ec00000000, - 0xc8510f2700000000, 0x66239ba100000000, 0xc3f0c76a00000000, - 0x7bc0c27700000000, 0xde139ebc00000000, 0x70610a3a00000000, - 0xd5b256f100000000, 0x9b02d60300000000, 0x3ed18ac800000000, - 0x90a31e4e00000000, 0x3570428500000000, 0x8d40479800000000, - 0x28931b5300000000, 0x86e18fd500000000, 0x2332d31e00000000, - 0xf68085ef00000000, 0x5353d92400000000, 0xfd214da200000000, - 0x58f2116900000000, 0xe0c2147400000000, 0x451148bf00000000, - 0xeb63dc3900000000, 0x4eb080f200000000, 0x3605ac0700000000, - 0x93d6f0cc00000000, 0x3da4644a00000000, 0x9877388100000000, - 0x20473d9c00000000, 0x8594615700000000, 0x2be6f5d100000000, - 0x8e35a91a00000000, 0x5b87ffeb00000000, 0xfe54a32000000000, - 0x502637a600000000, 0xf5f56b6d00000000, 0x4dc56e7000000000, - 0xe81632bb00000000, 0x4664a63d00000000, 0xe3b7faf600000000, - 0xad077a0400000000, 0x08d426cf00000000, 0xa6a6b24900000000, - 0x0375ee8200000000, 0xbb45eb9f00000000, 0x1e96b75400000000, - 0xb0e423d200000000, 0x15377f1900000000, 0xc08529e800000000, - 0x6556752300000000, 0xcb24e1a500000000, 0x6ef7bd6e00000000, - 0xd6c7b87300000000, 0x7314e4b800000000, 0xdd66703e00000000, - 0x78b52cf500000000, 0x6c0a580f00000000, 0xc9d904c400000000, - 0x67ab904200000000, 0xc278cc8900000000, 0x7a48c99400000000, - 0xdf9b955f00000000, 0x71e901d900000000, 0xd43a5d1200000000, - 0x01880be300000000, 0xa45b572800000000, 0x0a29c3ae00000000, - 0xaffa9f6500000000, 0x17ca9a7800000000, 0xb219c6b300000000, - 0x1c6b523500000000, 0xb9b80efe00000000, 0xf7088e0c00000000, - 0x52dbd2c700000000, 0xfca9464100000000, 0x597a1a8a00000000, - 0xe14a1f9700000000, 0x4499435c00000000, 0xeaebd7da00000000, - 0x4f388b1100000000, 0x9a8adde000000000, 0x3f59812b00000000, - 0x912b15ad00000000, 0x34f8496600000000, 0x8cc84c7b00000000, - 0x291b10b000000000, 0x8769843600000000, 0x22bad8fd00000000, - 0x5a0ff40800000000, 0xffdca8c300000000, 0x51ae3c4500000000, - 0xf47d608e00000000, 0x4c4d659300000000, 0xe99e395800000000, - 0x47ecadde00000000, 0xe23ff11500000000, 0x378da7e400000000, - 0x925efb2f00000000, 0x3c2c6fa900000000, 0x99ff336200000000, - 0x21cf367f00000000, 0x841c6ab400000000, 0x2a6efe3200000000, - 0x8fbda2f900000000, 0xc10d220b00000000, 0x64de7ec000000000, - 0xcaacea4600000000, 0x6f7fb68d00000000, 0xd74fb39000000000, - 0x729cef5b00000000, 0xdcee7bdd00000000, 0x793d271600000000, - 0xac8f71e700000000, 0x095c2d2c00000000, 0xa72eb9aa00000000, - 0x02fde56100000000, 0xbacde07c00000000, 0x1f1ebcb700000000, - 0xb16c283100000000, 0x14bf74fa00000000, 0xd814b01e00000000, - 0x7dc7ecd500000000, 0xd3b5785300000000, 0x7666249800000000, - 0xce56218500000000, 0x6b857d4e00000000, 0xc5f7e9c800000000, - 0x6024b50300000000, 0xb596e3f200000000, 0x1045bf3900000000, - 0xbe372bbf00000000, 0x1be4777400000000, 0xa3d4726900000000, - 0x06072ea200000000, 0xa875ba2400000000, 0x0da6e6ef00000000, - 0x4316661d00000000, 0xe6c53ad600000000, 0x48b7ae5000000000, - 0xed64f29b00000000, 0x5554f78600000000, 0xf087ab4d00000000, - 0x5ef53fcb00000000, 0xfb26630000000000, 0x2e9435f100000000, - 0x8b47693a00000000, 0x2535fdbc00000000, 0x80e6a17700000000, - 0x38d6a46a00000000, 0x9d05f8a100000000, 0x33776c2700000000, - 0x96a430ec00000000, 0xee111c1900000000, 0x4bc240d200000000, - 0xe5b0d45400000000, 0x4063889f00000000, 0xf8538d8200000000, - 0x5d80d14900000000, 0xf3f245cf00000000, 0x5621190400000000, - 0x83934ff500000000, 0x2640133e00000000, 0x883287b800000000, - 0x2de1db7300000000, 0x95d1de6e00000000, 0x300282a500000000, - 0x9e70162300000000, 0x3ba34ae800000000, 0x7513ca1a00000000, - 0xd0c096d100000000, 0x7eb2025700000000, 0xdb615e9c00000000, - 0x63515b8100000000, 0xc682074a00000000, 0x68f093cc00000000, - 0xcd23cf0700000000, 0x189199f600000000, 0xbd42c53d00000000, - 0x133051bb00000000, 0xb6e30d7000000000, 0x0ed3086d00000000, - 0xab0054a600000000, 0x0572c02000000000, 0xa0a19ceb00000000, - 0xb41ee81100000000, 0x11cdb4da00000000, 0xbfbf205c00000000, - 0x1a6c7c9700000000, 0xa25c798a00000000, 0x078f254100000000, - 0xa9fdb1c700000000, 0x0c2eed0c00000000, 0xd99cbbfd00000000, - 0x7c4fe73600000000, 0xd23d73b000000000, 0x77ee2f7b00000000, - 0xcfde2a6600000000, 0x6a0d76ad00000000, 0xc47fe22b00000000, - 0x61acbee000000000, 0x2f1c3e1200000000, 0x8acf62d900000000, - 0x24bdf65f00000000, 0x816eaa9400000000, 0x395eaf8900000000, - 0x9c8df34200000000, 0x32ff67c400000000, 0x972c3b0f00000000, - 0x429e6dfe00000000, 0xe74d313500000000, 0x493fa5b300000000, - 0xececf97800000000, 0x54dcfc6500000000, 0xf10fa0ae00000000, - 0x5f7d342800000000, 0xfaae68e300000000, 0x821b441600000000, - 0x27c818dd00000000, 0x89ba8c5b00000000, 0x2c69d09000000000, - 0x9459d58d00000000, 0x318a894600000000, 0x9ff81dc000000000, - 0x3a2b410b00000000, 0xef9917fa00000000, 0x4a4a4b3100000000, - 0xe438dfb700000000, 0x41eb837c00000000, 0xf9db866100000000, - 0x5c08daaa00000000, 0xf27a4e2c00000000, 0x57a912e700000000, - 0x1919921500000000, 0xbccacede00000000, 0x12b85a5800000000, - 0xb76b069300000000, 0x0f5b038e00000000, 0xaa885f4500000000, - 0x04facbc300000000, 0xa129970800000000, 0x749bc1f900000000, - 0xd1489d3200000000, 0x7f3a09b400000000, 0xdae9557f00000000, - 0x62d9506200000000, 0xc70a0ca900000000, 0x6978982f00000000, - 0xccabc4e400000000}, - {0x0000000000000000, 0xb40b77a600000000, 0x29119f9700000000, - 0x9d1ae83100000000, 0x13244ff400000000, 0xa72f385200000000, - 0x3a35d06300000000, 0x8e3ea7c500000000, 0x674eef3300000000, - 0xd345989500000000, 0x4e5f70a400000000, 0xfa54070200000000, - 0x746aa0c700000000, 0xc061d76100000000, 0x5d7b3f5000000000, - 0xe97048f600000000, 0xce9cde6700000000, 0x7a97a9c100000000, - 0xe78d41f000000000, 0x5386365600000000, 0xddb8919300000000, - 0x69b3e63500000000, 0xf4a90e0400000000, 0x40a279a200000000, - 0xa9d2315400000000, 0x1dd946f200000000, 0x80c3aec300000000, - 0x34c8d96500000000, 0xbaf67ea000000000, 0x0efd090600000000, - 0x93e7e13700000000, 0x27ec969100000000, 0x9c39bdcf00000000, - 0x2832ca6900000000, 0xb528225800000000, 0x012355fe00000000, - 0x8f1df23b00000000, 0x3b16859d00000000, 0xa60c6dac00000000, - 0x12071a0a00000000, 0xfb7752fc00000000, 0x4f7c255a00000000, - 0xd266cd6b00000000, 0x666dbacd00000000, 0xe8531d0800000000, - 0x5c586aae00000000, 0xc142829f00000000, 0x7549f53900000000, - 0x52a563a800000000, 0xe6ae140e00000000, 0x7bb4fc3f00000000, - 0xcfbf8b9900000000, 0x41812c5c00000000, 0xf58a5bfa00000000, - 0x6890b3cb00000000, 0xdc9bc46d00000000, 0x35eb8c9b00000000, - 0x81e0fb3d00000000, 0x1cfa130c00000000, 0xa8f164aa00000000, - 0x26cfc36f00000000, 0x92c4b4c900000000, 0x0fde5cf800000000, - 0xbbd52b5e00000000, 0x79750b4400000000, 0xcd7e7ce200000000, - 0x506494d300000000, 0xe46fe37500000000, 0x6a5144b000000000, - 0xde5a331600000000, 0x4340db2700000000, 0xf74bac8100000000, - 0x1e3be47700000000, 0xaa3093d100000000, 0x372a7be000000000, - 0x83210c4600000000, 0x0d1fab8300000000, 0xb914dc2500000000, - 0x240e341400000000, 0x900543b200000000, 0xb7e9d52300000000, - 0x03e2a28500000000, 0x9ef84ab400000000, 0x2af33d1200000000, - 0xa4cd9ad700000000, 0x10c6ed7100000000, 0x8ddc054000000000, - 0x39d772e600000000, 0xd0a73a1000000000, 0x64ac4db600000000, - 0xf9b6a58700000000, 0x4dbdd22100000000, 0xc38375e400000000, - 0x7788024200000000, 0xea92ea7300000000, 0x5e999dd500000000, - 0xe54cb68b00000000, 0x5147c12d00000000, 0xcc5d291c00000000, - 0x78565eba00000000, 0xf668f97f00000000, 0x42638ed900000000, - 0xdf7966e800000000, 0x6b72114e00000000, 0x820259b800000000, - 0x36092e1e00000000, 0xab13c62f00000000, 0x1f18b18900000000, - 0x9126164c00000000, 0x252d61ea00000000, 0xb83789db00000000, - 0x0c3cfe7d00000000, 0x2bd068ec00000000, 0x9fdb1f4a00000000, - 0x02c1f77b00000000, 0xb6ca80dd00000000, 0x38f4271800000000, - 0x8cff50be00000000, 0x11e5b88f00000000, 0xa5eecf2900000000, - 0x4c9e87df00000000, 0xf895f07900000000, 0x658f184800000000, - 0xd1846fee00000000, 0x5fbac82b00000000, 0xebb1bf8d00000000, - 0x76ab57bc00000000, 0xc2a0201a00000000, 0xf2ea168800000000, - 0x46e1612e00000000, 0xdbfb891f00000000, 0x6ff0feb900000000, - 0xe1ce597c00000000, 0x55c52eda00000000, 0xc8dfc6eb00000000, - 0x7cd4b14d00000000, 0x95a4f9bb00000000, 0x21af8e1d00000000, - 0xbcb5662c00000000, 0x08be118a00000000, 0x8680b64f00000000, - 0x328bc1e900000000, 0xaf9129d800000000, 0x1b9a5e7e00000000, - 0x3c76c8ef00000000, 0x887dbf4900000000, 0x1567577800000000, - 0xa16c20de00000000, 0x2f52871b00000000, 0x9b59f0bd00000000, - 0x0643188c00000000, 0xb2486f2a00000000, 0x5b3827dc00000000, - 0xef33507a00000000, 0x7229b84b00000000, 0xc622cfed00000000, - 0x481c682800000000, 0xfc171f8e00000000, 0x610df7bf00000000, - 0xd506801900000000, 0x6ed3ab4700000000, 0xdad8dce100000000, - 0x47c234d000000000, 0xf3c9437600000000, 0x7df7e4b300000000, - 0xc9fc931500000000, 0x54e67b2400000000, 0xe0ed0c8200000000, - 0x099d447400000000, 0xbd9633d200000000, 0x208cdbe300000000, - 0x9487ac4500000000, 0x1ab90b8000000000, 0xaeb27c2600000000, - 0x33a8941700000000, 0x87a3e3b100000000, 0xa04f752000000000, - 0x1444028600000000, 0x895eeab700000000, 0x3d559d1100000000, - 0xb36b3ad400000000, 0x07604d7200000000, 0x9a7aa54300000000, - 0x2e71d2e500000000, 0xc7019a1300000000, 0x730aedb500000000, - 0xee10058400000000, 0x5a1b722200000000, 0xd425d5e700000000, - 0x602ea24100000000, 0xfd344a7000000000, 0x493f3dd600000000, - 0x8b9f1dcc00000000, 0x3f946a6a00000000, 0xa28e825b00000000, - 0x1685f5fd00000000, 0x98bb523800000000, 0x2cb0259e00000000, - 0xb1aacdaf00000000, 0x05a1ba0900000000, 0xecd1f2ff00000000, - 0x58da855900000000, 0xc5c06d6800000000, 0x71cb1ace00000000, - 0xfff5bd0b00000000, 0x4bfecaad00000000, 0xd6e4229c00000000, - 0x62ef553a00000000, 0x4503c3ab00000000, 0xf108b40d00000000, - 0x6c125c3c00000000, 0xd8192b9a00000000, 0x56278c5f00000000, - 0xe22cfbf900000000, 0x7f3613c800000000, 0xcb3d646e00000000, - 0x224d2c9800000000, 0x96465b3e00000000, 0x0b5cb30f00000000, - 0xbf57c4a900000000, 0x3169636c00000000, 0x856214ca00000000, - 0x1878fcfb00000000, 0xac738b5d00000000, 0x17a6a00300000000, - 0xa3add7a500000000, 0x3eb73f9400000000, 0x8abc483200000000, - 0x0482eff700000000, 0xb089985100000000, 0x2d93706000000000, - 0x999807c600000000, 0x70e84f3000000000, 0xc4e3389600000000, - 0x59f9d0a700000000, 0xedf2a70100000000, 0x63cc00c400000000, - 0xd7c7776200000000, 0x4add9f5300000000, 0xfed6e8f500000000, - 0xd93a7e6400000000, 0x6d3109c200000000, 0xf02be1f300000000, - 0x4420965500000000, 0xca1e319000000000, 0x7e15463600000000, - 0xe30fae0700000000, 0x5704d9a100000000, 0xbe74915700000000, - 0x0a7fe6f100000000, 0x97650ec000000000, 0x236e796600000000, - 0xad50dea300000000, 0x195ba90500000000, 0x8441413400000000, - 0x304a369200000000}, - {0x0000000000000000, 0x9e00aacc00000000, 0x7d07254200000000, - 0xe3078f8e00000000, 0xfa0e4a8400000000, 0x640ee04800000000, - 0x87096fc600000000, 0x1909c50a00000000, 0xb51be5d300000000, - 0x2b1b4f1f00000000, 0xc81cc09100000000, 0x561c6a5d00000000, - 0x4f15af5700000000, 0xd115059b00000000, 0x32128a1500000000, - 0xac1220d900000000, 0x2b31bb7c00000000, 0xb53111b000000000, - 0x56369e3e00000000, 0xc83634f200000000, 0xd13ff1f800000000, - 0x4f3f5b3400000000, 0xac38d4ba00000000, 0x32387e7600000000, - 0x9e2a5eaf00000000, 0x002af46300000000, 0xe32d7bed00000000, - 0x7d2dd12100000000, 0x6424142b00000000, 0xfa24bee700000000, - 0x1923316900000000, 0x87239ba500000000, 0x566276f900000000, - 0xc862dc3500000000, 0x2b6553bb00000000, 0xb565f97700000000, - 0xac6c3c7d00000000, 0x326c96b100000000, 0xd16b193f00000000, - 0x4f6bb3f300000000, 0xe379932a00000000, 0x7d7939e600000000, - 0x9e7eb66800000000, 0x007e1ca400000000, 0x1977d9ae00000000, - 0x8777736200000000, 0x6470fcec00000000, 0xfa70562000000000, - 0x7d53cd8500000000, 0xe353674900000000, 0x0054e8c700000000, - 0x9e54420b00000000, 0x875d870100000000, 0x195d2dcd00000000, - 0xfa5aa24300000000, 0x645a088f00000000, 0xc848285600000000, - 0x5648829a00000000, 0xb54f0d1400000000, 0x2b4fa7d800000000, - 0x324662d200000000, 0xac46c81e00000000, 0x4f41479000000000, - 0xd141ed5c00000000, 0xedc29d2900000000, 0x73c237e500000000, - 0x90c5b86b00000000, 0x0ec512a700000000, 0x17ccd7ad00000000, - 0x89cc7d6100000000, 0x6acbf2ef00000000, 0xf4cb582300000000, - 0x58d978fa00000000, 0xc6d9d23600000000, 0x25de5db800000000, - 0xbbdef77400000000, 0xa2d7327e00000000, 0x3cd798b200000000, - 0xdfd0173c00000000, 0x41d0bdf000000000, 0xc6f3265500000000, - 0x58f38c9900000000, 0xbbf4031700000000, 0x25f4a9db00000000, - 0x3cfd6cd100000000, 0xa2fdc61d00000000, 0x41fa499300000000, - 0xdffae35f00000000, 0x73e8c38600000000, 0xede8694a00000000, - 0x0eefe6c400000000, 0x90ef4c0800000000, 0x89e6890200000000, - 0x17e623ce00000000, 0xf4e1ac4000000000, 0x6ae1068c00000000, - 0xbba0ebd000000000, 0x25a0411c00000000, 0xc6a7ce9200000000, - 0x58a7645e00000000, 0x41aea15400000000, 0xdfae0b9800000000, - 0x3ca9841600000000, 0xa2a92eda00000000, 0x0ebb0e0300000000, - 0x90bba4cf00000000, 0x73bc2b4100000000, 0xedbc818d00000000, - 0xf4b5448700000000, 0x6ab5ee4b00000000, 0x89b261c500000000, - 0x17b2cb0900000000, 0x909150ac00000000, 0x0e91fa6000000000, - 0xed9675ee00000000, 0x7396df2200000000, 0x6a9f1a2800000000, - 0xf49fb0e400000000, 0x17983f6a00000000, 0x899895a600000000, - 0x258ab57f00000000, 0xbb8a1fb300000000, 0x588d903d00000000, - 0xc68d3af100000000, 0xdf84fffb00000000, 0x4184553700000000, - 0xa283dab900000000, 0x3c83707500000000, 0xda853b5300000000, - 0x4485919f00000000, 0xa7821e1100000000, 0x3982b4dd00000000, - 0x208b71d700000000, 0xbe8bdb1b00000000, 0x5d8c549500000000, - 0xc38cfe5900000000, 0x6f9ede8000000000, 0xf19e744c00000000, - 0x1299fbc200000000, 0x8c99510e00000000, 0x9590940400000000, - 0x0b903ec800000000, 0xe897b14600000000, 0x76971b8a00000000, - 0xf1b4802f00000000, 0x6fb42ae300000000, 0x8cb3a56d00000000, - 0x12b30fa100000000, 0x0bbacaab00000000, 0x95ba606700000000, - 0x76bdefe900000000, 0xe8bd452500000000, 0x44af65fc00000000, - 0xdaafcf3000000000, 0x39a840be00000000, 0xa7a8ea7200000000, - 0xbea12f7800000000, 0x20a185b400000000, 0xc3a60a3a00000000, - 0x5da6a0f600000000, 0x8ce74daa00000000, 0x12e7e76600000000, - 0xf1e068e800000000, 0x6fe0c22400000000, 0x76e9072e00000000, - 0xe8e9ade200000000, 0x0bee226c00000000, 0x95ee88a000000000, - 0x39fca87900000000, 0xa7fc02b500000000, 0x44fb8d3b00000000, - 0xdafb27f700000000, 0xc3f2e2fd00000000, 0x5df2483100000000, - 0xbef5c7bf00000000, 0x20f56d7300000000, 0xa7d6f6d600000000, - 0x39d65c1a00000000, 0xdad1d39400000000, 0x44d1795800000000, - 0x5dd8bc5200000000, 0xc3d8169e00000000, 0x20df991000000000, - 0xbedf33dc00000000, 0x12cd130500000000, 0x8ccdb9c900000000, - 0x6fca364700000000, 0xf1ca9c8b00000000, 0xe8c3598100000000, - 0x76c3f34d00000000, 0x95c47cc300000000, 0x0bc4d60f00000000, - 0x3747a67a00000000, 0xa9470cb600000000, 0x4a40833800000000, - 0xd44029f400000000, 0xcd49ecfe00000000, 0x5349463200000000, - 0xb04ec9bc00000000, 0x2e4e637000000000, 0x825c43a900000000, - 0x1c5ce96500000000, 0xff5b66eb00000000, 0x615bcc2700000000, - 0x7852092d00000000, 0xe652a3e100000000, 0x05552c6f00000000, - 0x9b5586a300000000, 0x1c761d0600000000, 0x8276b7ca00000000, - 0x6171384400000000, 0xff71928800000000, 0xe678578200000000, - 0x7878fd4e00000000, 0x9b7f72c000000000, 0x057fd80c00000000, - 0xa96df8d500000000, 0x376d521900000000, 0xd46add9700000000, - 0x4a6a775b00000000, 0x5363b25100000000, 0xcd63189d00000000, - 0x2e64971300000000, 0xb0643ddf00000000, 0x6125d08300000000, - 0xff257a4f00000000, 0x1c22f5c100000000, 0x82225f0d00000000, - 0x9b2b9a0700000000, 0x052b30cb00000000, 0xe62cbf4500000000, - 0x782c158900000000, 0xd43e355000000000, 0x4a3e9f9c00000000, - 0xa939101200000000, 0x3739bade00000000, 0x2e307fd400000000, - 0xb030d51800000000, 0x53375a9600000000, 0xcd37f05a00000000, - 0x4a146bff00000000, 0xd414c13300000000, 0x37134ebd00000000, - 0xa913e47100000000, 0xb01a217b00000000, 0x2e1a8bb700000000, - 0xcd1d043900000000, 0x531daef500000000, 0xff0f8e2c00000000, - 0x610f24e000000000, 0x8208ab6e00000000, 0x1c0801a200000000, - 0x0501c4a800000000, 0x9b016e6400000000, 0x7806e1ea00000000, - 0xe6064b2600000000}}; - -#else /* W == 4 */ - -local const z_crc_t FAR crc_braid_table[][256] = { - {0x00000000, 0xb8bc6765, 0xaa09c88b, 0x12b5afee, 0x8f629757, - 0x37def032, 0x256b5fdc, 0x9dd738b9, 0xc5b428ef, 0x7d084f8a, - 0x6fbde064, 0xd7018701, 0x4ad6bfb8, 0xf26ad8dd, 0xe0df7733, - 0x58631056, 0x5019579f, 0xe8a530fa, 0xfa109f14, 0x42acf871, - 0xdf7bc0c8, 0x67c7a7ad, 0x75720843, 0xcdce6f26, 0x95ad7f70, - 0x2d111815, 0x3fa4b7fb, 0x8718d09e, 0x1acfe827, 0xa2738f42, - 0xb0c620ac, 0x087a47c9, 0xa032af3e, 0x188ec85b, 0x0a3b67b5, - 0xb28700d0, 0x2f503869, 0x97ec5f0c, 0x8559f0e2, 0x3de59787, - 0x658687d1, 0xdd3ae0b4, 0xcf8f4f5a, 0x7733283f, 0xeae41086, - 0x525877e3, 0x40edd80d, 0xf851bf68, 0xf02bf8a1, 0x48979fc4, - 0x5a22302a, 0xe29e574f, 0x7f496ff6, 0xc7f50893, 0xd540a77d, - 0x6dfcc018, 0x359fd04e, 0x8d23b72b, 0x9f9618c5, 0x272a7fa0, - 0xbafd4719, 0x0241207c, 0x10f48f92, 0xa848e8f7, 0x9b14583d, - 0x23a83f58, 0x311d90b6, 0x89a1f7d3, 0x1476cf6a, 0xaccaa80f, - 0xbe7f07e1, 0x06c36084, 0x5ea070d2, 0xe61c17b7, 0xf4a9b859, - 0x4c15df3c, 0xd1c2e785, 0x697e80e0, 0x7bcb2f0e, 0xc377486b, - 0xcb0d0fa2, 0x73b168c7, 0x6104c729, 0xd9b8a04c, 0x446f98f5, - 0xfcd3ff90, 0xee66507e, 0x56da371b, 0x0eb9274d, 0xb6054028, - 0xa4b0efc6, 0x1c0c88a3, 0x81dbb01a, 0x3967d77f, 0x2bd27891, - 0x936e1ff4, 0x3b26f703, 0x839a9066, 0x912f3f88, 0x299358ed, - 0xb4446054, 0x0cf80731, 0x1e4da8df, 0xa6f1cfba, 0xfe92dfec, - 0x462eb889, 0x549b1767, 0xec277002, 0x71f048bb, 0xc94c2fde, - 0xdbf98030, 0x6345e755, 0x6b3fa09c, 0xd383c7f9, 0xc1366817, - 0x798a0f72, 0xe45d37cb, 0x5ce150ae, 0x4e54ff40, 0xf6e89825, - 0xae8b8873, 0x1637ef16, 0x048240f8, 0xbc3e279d, 0x21e91f24, - 0x99557841, 0x8be0d7af, 0x335cb0ca, 0xed59b63b, 0x55e5d15e, - 0x47507eb0, 0xffec19d5, 0x623b216c, 0xda874609, 0xc832e9e7, - 0x708e8e82, 0x28ed9ed4, 0x9051f9b1, 0x82e4565f, 0x3a58313a, - 0xa78f0983, 0x1f336ee6, 0x0d86c108, 0xb53aa66d, 0xbd40e1a4, - 0x05fc86c1, 0x1749292f, 0xaff54e4a, 0x322276f3, 0x8a9e1196, - 0x982bbe78, 0x2097d91d, 0x78f4c94b, 0xc048ae2e, 0xd2fd01c0, - 0x6a4166a5, 0xf7965e1c, 0x4f2a3979, 0x5d9f9697, 0xe523f1f2, - 0x4d6b1905, 0xf5d77e60, 0xe762d18e, 0x5fdeb6eb, 0xc2098e52, - 0x7ab5e937, 0x680046d9, 0xd0bc21bc, 0x88df31ea, 0x3063568f, - 0x22d6f961, 0x9a6a9e04, 0x07bda6bd, 0xbf01c1d8, 0xadb46e36, - 0x15080953, 0x1d724e9a, 0xa5ce29ff, 0xb77b8611, 0x0fc7e174, - 0x9210d9cd, 0x2aacbea8, 0x38191146, 0x80a57623, 0xd8c66675, - 0x607a0110, 0x72cfaefe, 0xca73c99b, 0x57a4f122, 0xef189647, - 0xfdad39a9, 0x45115ecc, 0x764dee06, 0xcef18963, 0xdc44268d, - 0x64f841e8, 0xf92f7951, 0x41931e34, 0x5326b1da, 0xeb9ad6bf, - 0xb3f9c6e9, 0x0b45a18c, 0x19f00e62, 0xa14c6907, 0x3c9b51be, - 0x842736db, 0x96929935, 0x2e2efe50, 0x2654b999, 0x9ee8defc, - 0x8c5d7112, 0x34e11677, 0xa9362ece, 0x118a49ab, 0x033fe645, - 0xbb838120, 0xe3e09176, 0x5b5cf613, 0x49e959fd, 0xf1553e98, - 0x6c820621, 0xd43e6144, 0xc68bceaa, 0x7e37a9cf, 0xd67f4138, - 0x6ec3265d, 0x7c7689b3, 0xc4caeed6, 0x591dd66f, 0xe1a1b10a, - 0xf3141ee4, 0x4ba87981, 0x13cb69d7, 0xab770eb2, 0xb9c2a15c, - 0x017ec639, 0x9ca9fe80, 0x241599e5, 0x36a0360b, 0x8e1c516e, - 0x866616a7, 0x3eda71c2, 0x2c6fde2c, 0x94d3b949, 0x090481f0, - 0xb1b8e695, 0xa30d497b, 0x1bb12e1e, 0x43d23e48, 0xfb6e592d, - 0xe9dbf6c3, 0x516791a6, 0xccb0a91f, 0x740cce7a, 0x66b96194, - 0xde0506f1}, - {0x00000000, 0x01c26a37, 0x0384d46e, 0x0246be59, 0x0709a8dc, - 0x06cbc2eb, 0x048d7cb2, 0x054f1685, 0x0e1351b8, 0x0fd13b8f, - 0x0d9785d6, 0x0c55efe1, 0x091af964, 0x08d89353, 0x0a9e2d0a, - 0x0b5c473d, 0x1c26a370, 0x1de4c947, 0x1fa2771e, 0x1e601d29, - 0x1b2f0bac, 0x1aed619b, 0x18abdfc2, 0x1969b5f5, 0x1235f2c8, - 0x13f798ff, 0x11b126a6, 0x10734c91, 0x153c5a14, 0x14fe3023, - 0x16b88e7a, 0x177ae44d, 0x384d46e0, 0x398f2cd7, 0x3bc9928e, - 0x3a0bf8b9, 0x3f44ee3c, 0x3e86840b, 0x3cc03a52, 0x3d025065, - 0x365e1758, 0x379c7d6f, 0x35dac336, 0x3418a901, 0x3157bf84, - 0x3095d5b3, 0x32d36bea, 0x331101dd, 0x246be590, 0x25a98fa7, - 0x27ef31fe, 0x262d5bc9, 0x23624d4c, 0x22a0277b, 0x20e69922, - 0x2124f315, 0x2a78b428, 0x2bbade1f, 0x29fc6046, 0x283e0a71, - 0x2d711cf4, 0x2cb376c3, 0x2ef5c89a, 0x2f37a2ad, 0x709a8dc0, - 0x7158e7f7, 0x731e59ae, 0x72dc3399, 0x7793251c, 0x76514f2b, - 0x7417f172, 0x75d59b45, 0x7e89dc78, 0x7f4bb64f, 0x7d0d0816, - 0x7ccf6221, 0x798074a4, 0x78421e93, 0x7a04a0ca, 0x7bc6cafd, - 0x6cbc2eb0, 0x6d7e4487, 0x6f38fade, 0x6efa90e9, 0x6bb5866c, - 0x6a77ec5b, 0x68315202, 0x69f33835, 0x62af7f08, 0x636d153f, - 0x612bab66, 0x60e9c151, 0x65a6d7d4, 0x6464bde3, 0x662203ba, - 0x67e0698d, 0x48d7cb20, 0x4915a117, 0x4b531f4e, 0x4a917579, - 0x4fde63fc, 0x4e1c09cb, 0x4c5ab792, 0x4d98dda5, 0x46c49a98, - 0x4706f0af, 0x45404ef6, 0x448224c1, 0x41cd3244, 0x400f5873, - 0x4249e62a, 0x438b8c1d, 0x54f16850, 0x55330267, 0x5775bc3e, - 0x56b7d609, 0x53f8c08c, 0x523aaabb, 0x507c14e2, 0x51be7ed5, - 0x5ae239e8, 0x5b2053df, 0x5966ed86, 0x58a487b1, 0x5deb9134, - 0x5c29fb03, 0x5e6f455a, 0x5fad2f6d, 0xe1351b80, 0xe0f771b7, - 0xe2b1cfee, 0xe373a5d9, 0xe63cb35c, 0xe7fed96b, 0xe5b86732, - 0xe47a0d05, 0xef264a38, 0xeee4200f, 0xeca29e56, 0xed60f461, - 0xe82fe2e4, 0xe9ed88d3, 0xebab368a, 0xea695cbd, 0xfd13b8f0, - 0xfcd1d2c7, 0xfe976c9e, 0xff5506a9, 0xfa1a102c, 0xfbd87a1b, - 0xf99ec442, 0xf85cae75, 0xf300e948, 0xf2c2837f, 0xf0843d26, - 0xf1465711, 0xf4094194, 0xf5cb2ba3, 0xf78d95fa, 0xf64fffcd, - 0xd9785d60, 0xd8ba3757, 0xdafc890e, 0xdb3ee339, 0xde71f5bc, - 0xdfb39f8b, 0xddf521d2, 0xdc374be5, 0xd76b0cd8, 0xd6a966ef, - 0xd4efd8b6, 0xd52db281, 0xd062a404, 0xd1a0ce33, 0xd3e6706a, - 0xd2241a5d, 0xc55efe10, 0xc49c9427, 0xc6da2a7e, 0xc7184049, - 0xc25756cc, 0xc3953cfb, 0xc1d382a2, 0xc011e895, 0xcb4dafa8, - 0xca8fc59f, 0xc8c97bc6, 0xc90b11f1, 0xcc440774, 0xcd866d43, - 0xcfc0d31a, 0xce02b92d, 0x91af9640, 0x906dfc77, 0x922b422e, - 0x93e92819, 0x96a63e9c, 0x976454ab, 0x9522eaf2, 0x94e080c5, - 0x9fbcc7f8, 0x9e7eadcf, 0x9c381396, 0x9dfa79a1, 0x98b56f24, - 0x99770513, 0x9b31bb4a, 0x9af3d17d, 0x8d893530, 0x8c4b5f07, - 0x8e0de15e, 0x8fcf8b69, 0x8a809dec, 0x8b42f7db, 0x89044982, - 0x88c623b5, 0x839a6488, 0x82580ebf, 0x801eb0e6, 0x81dcdad1, - 0x8493cc54, 0x8551a663, 0x8717183a, 0x86d5720d, 0xa9e2d0a0, - 0xa820ba97, 0xaa6604ce, 0xaba46ef9, 0xaeeb787c, 0xaf29124b, - 0xad6fac12, 0xacadc625, 0xa7f18118, 0xa633eb2f, 0xa4755576, - 0xa5b73f41, 0xa0f829c4, 0xa13a43f3, 0xa37cfdaa, 0xa2be979d, - 0xb5c473d0, 0xb40619e7, 0xb640a7be, 0xb782cd89, 0xb2cddb0c, - 0xb30fb13b, 0xb1490f62, 0xb08b6555, 0xbbd72268, 0xba15485f, - 0xb853f606, 0xb9919c31, 0xbcde8ab4, 0xbd1ce083, 0xbf5a5eda, - 0xbe9834ed}, - {0x00000000, 0x191b3141, 0x32366282, 0x2b2d53c3, 0x646cc504, - 0x7d77f445, 0x565aa786, 0x4f4196c7, 0xc8d98a08, 0xd1c2bb49, - 0xfaefe88a, 0xe3f4d9cb, 0xacb54f0c, 0xb5ae7e4d, 0x9e832d8e, - 0x87981ccf, 0x4ac21251, 0x53d92310, 0x78f470d3, 0x61ef4192, - 0x2eaed755, 0x37b5e614, 0x1c98b5d7, 0x05838496, 0x821b9859, - 0x9b00a918, 0xb02dfadb, 0xa936cb9a, 0xe6775d5d, 0xff6c6c1c, - 0xd4413fdf, 0xcd5a0e9e, 0x958424a2, 0x8c9f15e3, 0xa7b24620, - 0xbea97761, 0xf1e8e1a6, 0xe8f3d0e7, 0xc3de8324, 0xdac5b265, - 0x5d5daeaa, 0x44469feb, 0x6f6bcc28, 0x7670fd69, 0x39316bae, - 0x202a5aef, 0x0b07092c, 0x121c386d, 0xdf4636f3, 0xc65d07b2, - 0xed705471, 0xf46b6530, 0xbb2af3f7, 0xa231c2b6, 0x891c9175, - 0x9007a034, 0x179fbcfb, 0x0e848dba, 0x25a9de79, 0x3cb2ef38, - 0x73f379ff, 0x6ae848be, 0x41c51b7d, 0x58de2a3c, 0xf0794f05, - 0xe9627e44, 0xc24f2d87, 0xdb541cc6, 0x94158a01, 0x8d0ebb40, - 0xa623e883, 0xbf38d9c2, 0x38a0c50d, 0x21bbf44c, 0x0a96a78f, - 0x138d96ce, 0x5ccc0009, 0x45d73148, 0x6efa628b, 0x77e153ca, - 0xbabb5d54, 0xa3a06c15, 0x888d3fd6, 0x91960e97, 0xded79850, - 0xc7cca911, 0xece1fad2, 0xf5facb93, 0x7262d75c, 0x6b79e61d, - 0x4054b5de, 0x594f849f, 0x160e1258, 0x0f152319, 0x243870da, - 0x3d23419b, 0x65fd6ba7, 0x7ce65ae6, 0x57cb0925, 0x4ed03864, - 0x0191aea3, 0x188a9fe2, 0x33a7cc21, 0x2abcfd60, 0xad24e1af, - 0xb43fd0ee, 0x9f12832d, 0x8609b26c, 0xc94824ab, 0xd05315ea, - 0xfb7e4629, 0xe2657768, 0x2f3f79f6, 0x362448b7, 0x1d091b74, - 0x04122a35, 0x4b53bcf2, 0x52488db3, 0x7965de70, 0x607eef31, - 0xe7e6f3fe, 0xfefdc2bf, 0xd5d0917c, 0xcccba03d, 0x838a36fa, - 0x9a9107bb, 0xb1bc5478, 0xa8a76539, 0x3b83984b, 0x2298a90a, - 0x09b5fac9, 0x10aecb88, 0x5fef5d4f, 0x46f46c0e, 0x6dd93fcd, - 0x74c20e8c, 0xf35a1243, 0xea412302, 0xc16c70c1, 0xd8774180, - 0x9736d747, 0x8e2de606, 0xa500b5c5, 0xbc1b8484, 0x71418a1a, - 0x685abb5b, 0x4377e898, 0x5a6cd9d9, 0x152d4f1e, 0x0c367e5f, - 0x271b2d9c, 0x3e001cdd, 0xb9980012, 0xa0833153, 0x8bae6290, - 0x92b553d1, 0xddf4c516, 0xc4eff457, 0xefc2a794, 0xf6d996d5, - 0xae07bce9, 0xb71c8da8, 0x9c31de6b, 0x852aef2a, 0xca6b79ed, - 0xd37048ac, 0xf85d1b6f, 0xe1462a2e, 0x66de36e1, 0x7fc507a0, - 0x54e85463, 0x4df36522, 0x02b2f3e5, 0x1ba9c2a4, 0x30849167, - 0x299fa026, 0xe4c5aeb8, 0xfdde9ff9, 0xd6f3cc3a, 0xcfe8fd7b, - 0x80a96bbc, 0x99b25afd, 0xb29f093e, 0xab84387f, 0x2c1c24b0, - 0x350715f1, 0x1e2a4632, 0x07317773, 0x4870e1b4, 0x516bd0f5, - 0x7a468336, 0x635db277, 0xcbfad74e, 0xd2e1e60f, 0xf9ccb5cc, - 0xe0d7848d, 0xaf96124a, 0xb68d230b, 0x9da070c8, 0x84bb4189, - 0x03235d46, 0x1a386c07, 0x31153fc4, 0x280e0e85, 0x674f9842, - 0x7e54a903, 0x5579fac0, 0x4c62cb81, 0x8138c51f, 0x9823f45e, - 0xb30ea79d, 0xaa1596dc, 0xe554001b, 0xfc4f315a, 0xd7626299, - 0xce7953d8, 0x49e14f17, 0x50fa7e56, 0x7bd72d95, 0x62cc1cd4, - 0x2d8d8a13, 0x3496bb52, 0x1fbbe891, 0x06a0d9d0, 0x5e7ef3ec, - 0x4765c2ad, 0x6c48916e, 0x7553a02f, 0x3a1236e8, 0x230907a9, - 0x0824546a, 0x113f652b, 0x96a779e4, 0x8fbc48a5, 0xa4911b66, - 0xbd8a2a27, 0xf2cbbce0, 0xebd08da1, 0xc0fdde62, 0xd9e6ef23, - 0x14bce1bd, 0x0da7d0fc, 0x268a833f, 0x3f91b27e, 0x70d024b9, - 0x69cb15f8, 0x42e6463b, 0x5bfd777a, 0xdc656bb5, 0xc57e5af4, - 0xee530937, 0xf7483876, 0xb809aeb1, 0xa1129ff0, 0x8a3fcc33, - 0x9324fd72}, - {0x00000000, 0x77073096, 0xee0e612c, 0x990951ba, 0x076dc419, - 0x706af48f, 0xe963a535, 0x9e6495a3, 0x0edb8832, 0x79dcb8a4, - 0xe0d5e91e, 0x97d2d988, 0x09b64c2b, 0x7eb17cbd, 0xe7b82d07, - 0x90bf1d91, 0x1db71064, 0x6ab020f2, 0xf3b97148, 0x84be41de, - 0x1adad47d, 0x6ddde4eb, 0xf4d4b551, 0x83d385c7, 0x136c9856, - 0x646ba8c0, 0xfd62f97a, 0x8a65c9ec, 0x14015c4f, 0x63066cd9, - 0xfa0f3d63, 0x8d080df5, 0x3b6e20c8, 0x4c69105e, 0xd56041e4, - 0xa2677172, 0x3c03e4d1, 0x4b04d447, 0xd20d85fd, 0xa50ab56b, - 0x35b5a8fa, 0x42b2986c, 0xdbbbc9d6, 0xacbcf940, 0x32d86ce3, - 0x45df5c75, 0xdcd60dcf, 0xabd13d59, 0x26d930ac, 0x51de003a, - 0xc8d75180, 0xbfd06116, 0x21b4f4b5, 0x56b3c423, 0xcfba9599, - 0xb8bda50f, 0x2802b89e, 0x5f058808, 0xc60cd9b2, 0xb10be924, - 0x2f6f7c87, 0x58684c11, 0xc1611dab, 0xb6662d3d, 0x76dc4190, - 0x01db7106, 0x98d220bc, 0xefd5102a, 0x71b18589, 0x06b6b51f, - 0x9fbfe4a5, 0xe8b8d433, 0x7807c9a2, 0x0f00f934, 0x9609a88e, - 0xe10e9818, 0x7f6a0dbb, 0x086d3d2d, 0x91646c97, 0xe6635c01, - 0x6b6b51f4, 0x1c6c6162, 0x856530d8, 0xf262004e, 0x6c0695ed, - 0x1b01a57b, 0x8208f4c1, 0xf50fc457, 0x65b0d9c6, 0x12b7e950, - 0x8bbeb8ea, 0xfcb9887c, 0x62dd1ddf, 0x15da2d49, 0x8cd37cf3, - 0xfbd44c65, 0x4db26158, 0x3ab551ce, 0xa3bc0074, 0xd4bb30e2, - 0x4adfa541, 0x3dd895d7, 0xa4d1c46d, 0xd3d6f4fb, 0x4369e96a, - 0x346ed9fc, 0xad678846, 0xda60b8d0, 0x44042d73, 0x33031de5, - 0xaa0a4c5f, 0xdd0d7cc9, 0x5005713c, 0x270241aa, 0xbe0b1010, - 0xc90c2086, 0x5768b525, 0x206f85b3, 0xb966d409, 0xce61e49f, - 0x5edef90e, 0x29d9c998, 0xb0d09822, 0xc7d7a8b4, 0x59b33d17, - 0x2eb40d81, 0xb7bd5c3b, 0xc0ba6cad, 0xedb88320, 0x9abfb3b6, - 0x03b6e20c, 0x74b1d29a, 0xead54739, 0x9dd277af, 0x04db2615, - 0x73dc1683, 0xe3630b12, 0x94643b84, 0x0d6d6a3e, 0x7a6a5aa8, - 0xe40ecf0b, 0x9309ff9d, 0x0a00ae27, 0x7d079eb1, 0xf00f9344, - 0x8708a3d2, 0x1e01f268, 0x6906c2fe, 0xf762575d, 0x806567cb, - 0x196c3671, 0x6e6b06e7, 0xfed41b76, 0x89d32be0, 0x10da7a5a, - 0x67dd4acc, 0xf9b9df6f, 0x8ebeeff9, 0x17b7be43, 0x60b08ed5, - 0xd6d6a3e8, 0xa1d1937e, 0x38d8c2c4, 0x4fdff252, 0xd1bb67f1, - 0xa6bc5767, 0x3fb506dd, 0x48b2364b, 0xd80d2bda, 0xaf0a1b4c, - 0x36034af6, 0x41047a60, 0xdf60efc3, 0xa867df55, 0x316e8eef, - 0x4669be79, 0xcb61b38c, 0xbc66831a, 0x256fd2a0, 0x5268e236, - 0xcc0c7795, 0xbb0b4703, 0x220216b9, 0x5505262f, 0xc5ba3bbe, - 0xb2bd0b28, 0x2bb45a92, 0x5cb36a04, 0xc2d7ffa7, 0xb5d0cf31, - 0x2cd99e8b, 0x5bdeae1d, 0x9b64c2b0, 0xec63f226, 0x756aa39c, - 0x026d930a, 0x9c0906a9, 0xeb0e363f, 0x72076785, 0x05005713, - 0x95bf4a82, 0xe2b87a14, 0x7bb12bae, 0x0cb61b38, 0x92d28e9b, - 0xe5d5be0d, 0x7cdcefb7, 0x0bdbdf21, 0x86d3d2d4, 0xf1d4e242, - 0x68ddb3f8, 0x1fda836e, 0x81be16cd, 0xf6b9265b, 0x6fb077e1, - 0x18b74777, 0x88085ae6, 0xff0f6a70, 0x66063bca, 0x11010b5c, - 0x8f659eff, 0xf862ae69, 0x616bffd3, 0x166ccf45, 0xa00ae278, - 0xd70dd2ee, 0x4e048354, 0x3903b3c2, 0xa7672661, 0xd06016f7, - 0x4969474d, 0x3e6e77db, 0xaed16a4a, 0xd9d65adc, 0x40df0b66, - 0x37d83bf0, 0xa9bcae53, 0xdebb9ec5, 0x47b2cf7f, 0x30b5ffe9, - 0xbdbdf21c, 0xcabac28a, 0x53b39330, 0x24b4a3a6, 0xbad03605, - 0xcdd70693, 0x54de5729, 0x23d967bf, 0xb3667a2e, 0xc4614ab8, - 0x5d681b02, 0x2a6f2b94, 0xb40bbe37, 0xc30c8ea1, 0x5a05df1b, - 0x2d02ef8d}}; - -local const z_word_t FAR crc_braid_big_table[][256] = { - {0x00000000, 0x96300777, 0x2c610eee, 0xba510999, 0x19c46d07, - 0x8ff46a70, 0x35a563e9, 0xa395649e, 0x3288db0e, 0xa4b8dc79, - 0x1ee9d5e0, 0x88d9d297, 0x2b4cb609, 0xbd7cb17e, 0x072db8e7, - 0x911dbf90, 0x6410b71d, 0xf220b06a, 0x4871b9f3, 0xde41be84, - 0x7dd4da1a, 0xebe4dd6d, 0x51b5d4f4, 0xc785d383, 0x56986c13, - 0xc0a86b64, 0x7af962fd, 0xecc9658a, 0x4f5c0114, 0xd96c0663, - 0x633d0ffa, 0xf50d088d, 0xc8206e3b, 0x5e10694c, 0xe44160d5, - 0x727167a2, 0xd1e4033c, 0x47d4044b, 0xfd850dd2, 0x6bb50aa5, - 0xfaa8b535, 0x6c98b242, 0xd6c9bbdb, 0x40f9bcac, 0xe36cd832, - 0x755cdf45, 0xcf0dd6dc, 0x593dd1ab, 0xac30d926, 0x3a00de51, - 0x8051d7c8, 0x1661d0bf, 0xb5f4b421, 0x23c4b356, 0x9995bacf, - 0x0fa5bdb8, 0x9eb80228, 0x0888055f, 0xb2d90cc6, 0x24e90bb1, - 0x877c6f2f, 0x114c6858, 0xab1d61c1, 0x3d2d66b6, 0x9041dc76, - 0x0671db01, 0xbc20d298, 0x2a10d5ef, 0x8985b171, 0x1fb5b606, - 0xa5e4bf9f, 0x33d4b8e8, 0xa2c90778, 0x34f9000f, 0x8ea80996, - 0x18980ee1, 0xbb0d6a7f, 0x2d3d6d08, 0x976c6491, 0x015c63e6, - 0xf4516b6b, 0x62616c1c, 0xd8306585, 0x4e0062f2, 0xed95066c, - 0x7ba5011b, 0xc1f40882, 0x57c40ff5, 0xc6d9b065, 0x50e9b712, - 0xeab8be8b, 0x7c88b9fc, 0xdf1ddd62, 0x492dda15, 0xf37cd38c, - 0x654cd4fb, 0x5861b24d, 0xce51b53a, 0x7400bca3, 0xe230bbd4, - 0x41a5df4a, 0xd795d83d, 0x6dc4d1a4, 0xfbf4d6d3, 0x6ae96943, - 0xfcd96e34, 0x468867ad, 0xd0b860da, 0x732d0444, 0xe51d0333, - 0x5f4c0aaa, 0xc97c0ddd, 0x3c710550, 0xaa410227, 0x10100bbe, - 0x86200cc9, 0x25b56857, 0xb3856f20, 0x09d466b9, 0x9fe461ce, - 0x0ef9de5e, 0x98c9d929, 0x2298d0b0, 0xb4a8d7c7, 0x173db359, - 0x810db42e, 0x3b5cbdb7, 0xad6cbac0, 0x2083b8ed, 0xb6b3bf9a, - 0x0ce2b603, 0x9ad2b174, 0x3947d5ea, 0xaf77d29d, 0x1526db04, - 0x8316dc73, 0x120b63e3, 0x843b6494, 0x3e6a6d0d, 0xa85a6a7a, - 0x0bcf0ee4, 0x9dff0993, 0x27ae000a, 0xb19e077d, 0x44930ff0, - 0xd2a30887, 0x68f2011e, 0xfec20669, 0x5d5762f7, 0xcb676580, - 0x71366c19, 0xe7066b6e, 0x761bd4fe, 0xe02bd389, 0x5a7ada10, - 0xcc4add67, 0x6fdfb9f9, 0xf9efbe8e, 0x43beb717, 0xd58eb060, - 0xe8a3d6d6, 0x7e93d1a1, 0xc4c2d838, 0x52f2df4f, 0xf167bbd1, - 0x6757bca6, 0xdd06b53f, 0x4b36b248, 0xda2b0dd8, 0x4c1b0aaf, - 0xf64a0336, 0x607a0441, 0xc3ef60df, 0x55df67a8, 0xef8e6e31, - 0x79be6946, 0x8cb361cb, 0x1a8366bc, 0xa0d26f25, 0x36e26852, - 0x95770ccc, 0x03470bbb, 0xb9160222, 0x2f260555, 0xbe3bbac5, - 0x280bbdb2, 0x925ab42b, 0x046ab35c, 0xa7ffd7c2, 0x31cfd0b5, - 0x8b9ed92c, 0x1daede5b, 0xb0c2649b, 0x26f263ec, 0x9ca36a75, - 0x0a936d02, 0xa906099c, 0x3f360eeb, 0x85670772, 0x13570005, - 0x824abf95, 0x147ab8e2, 0xae2bb17b, 0x381bb60c, 0x9b8ed292, - 0x0dbed5e5, 0xb7efdc7c, 0x21dfdb0b, 0xd4d2d386, 0x42e2d4f1, - 0xf8b3dd68, 0x6e83da1f, 0xcd16be81, 0x5b26b9f6, 0xe177b06f, - 0x7747b718, 0xe65a0888, 0x706a0fff, 0xca3b0666, 0x5c0b0111, - 0xff9e658f, 0x69ae62f8, 0xd3ff6b61, 0x45cf6c16, 0x78e20aa0, - 0xeed20dd7, 0x5483044e, 0xc2b30339, 0x612667a7, 0xf71660d0, - 0x4d476949, 0xdb776e3e, 0x4a6ad1ae, 0xdc5ad6d9, 0x660bdf40, - 0xf03bd837, 0x53aebca9, 0xc59ebbde, 0x7fcfb247, 0xe9ffb530, - 0x1cf2bdbd, 0x8ac2baca, 0x3093b353, 0xa6a3b424, 0x0536d0ba, - 0x9306d7cd, 0x2957de54, 0xbf67d923, 0x2e7a66b3, 0xb84a61c4, - 0x021b685d, 0x942b6f2a, 0x37be0bb4, 0xa18e0cc3, 0x1bdf055a, - 0x8def022d}, - {0x00000000, 0x41311b19, 0x82623632, 0xc3532d2b, 0x04c56c64, - 0x45f4777d, 0x86a75a56, 0xc796414f, 0x088ad9c8, 0x49bbc2d1, - 0x8ae8effa, 0xcbd9f4e3, 0x0c4fb5ac, 0x4d7eaeb5, 0x8e2d839e, - 0xcf1c9887, 0x5112c24a, 0x1023d953, 0xd370f478, 0x9241ef61, - 0x55d7ae2e, 0x14e6b537, 0xd7b5981c, 0x96848305, 0x59981b82, - 0x18a9009b, 0xdbfa2db0, 0x9acb36a9, 0x5d5d77e6, 0x1c6c6cff, - 0xdf3f41d4, 0x9e0e5acd, 0xa2248495, 0xe3159f8c, 0x2046b2a7, - 0x6177a9be, 0xa6e1e8f1, 0xe7d0f3e8, 0x2483dec3, 0x65b2c5da, - 0xaaae5d5d, 0xeb9f4644, 0x28cc6b6f, 0x69fd7076, 0xae6b3139, - 0xef5a2a20, 0x2c09070b, 0x6d381c12, 0xf33646df, 0xb2075dc6, - 0x715470ed, 0x30656bf4, 0xf7f32abb, 0xb6c231a2, 0x75911c89, - 0x34a00790, 0xfbbc9f17, 0xba8d840e, 0x79dea925, 0x38efb23c, - 0xff79f373, 0xbe48e86a, 0x7d1bc541, 0x3c2ade58, 0x054f79f0, - 0x447e62e9, 0x872d4fc2, 0xc61c54db, 0x018a1594, 0x40bb0e8d, - 0x83e823a6, 0xc2d938bf, 0x0dc5a038, 0x4cf4bb21, 0x8fa7960a, - 0xce968d13, 0x0900cc5c, 0x4831d745, 0x8b62fa6e, 0xca53e177, - 0x545dbbba, 0x156ca0a3, 0xd63f8d88, 0x970e9691, 0x5098d7de, - 0x11a9ccc7, 0xd2fae1ec, 0x93cbfaf5, 0x5cd76272, 0x1de6796b, - 0xdeb55440, 0x9f844f59, 0x58120e16, 0x1923150f, 0xda703824, - 0x9b41233d, 0xa76bfd65, 0xe65ae67c, 0x2509cb57, 0x6438d04e, - 0xa3ae9101, 0xe29f8a18, 0x21cca733, 0x60fdbc2a, 0xafe124ad, - 0xeed03fb4, 0x2d83129f, 0x6cb20986, 0xab2448c9, 0xea1553d0, - 0x29467efb, 0x687765e2, 0xf6793f2f, 0xb7482436, 0x741b091d, - 0x352a1204, 0xf2bc534b, 0xb38d4852, 0x70de6579, 0x31ef7e60, - 0xfef3e6e7, 0xbfc2fdfe, 0x7c91d0d5, 0x3da0cbcc, 0xfa368a83, - 0xbb07919a, 0x7854bcb1, 0x3965a7a8, 0x4b98833b, 0x0aa99822, - 0xc9fab509, 0x88cbae10, 0x4f5def5f, 0x0e6cf446, 0xcd3fd96d, - 0x8c0ec274, 0x43125af3, 0x022341ea, 0xc1706cc1, 0x804177d8, - 0x47d73697, 0x06e62d8e, 0xc5b500a5, 0x84841bbc, 0x1a8a4171, - 0x5bbb5a68, 0x98e87743, 0xd9d96c5a, 0x1e4f2d15, 0x5f7e360c, - 0x9c2d1b27, 0xdd1c003e, 0x120098b9, 0x533183a0, 0x9062ae8b, - 0xd153b592, 0x16c5f4dd, 0x57f4efc4, 0x94a7c2ef, 0xd596d9f6, - 0xe9bc07ae, 0xa88d1cb7, 0x6bde319c, 0x2aef2a85, 0xed796bca, - 0xac4870d3, 0x6f1b5df8, 0x2e2a46e1, 0xe136de66, 0xa007c57f, - 0x6354e854, 0x2265f34d, 0xe5f3b202, 0xa4c2a91b, 0x67918430, - 0x26a09f29, 0xb8aec5e4, 0xf99fdefd, 0x3accf3d6, 0x7bfde8cf, - 0xbc6ba980, 0xfd5ab299, 0x3e099fb2, 0x7f3884ab, 0xb0241c2c, - 0xf1150735, 0x32462a1e, 0x73773107, 0xb4e17048, 0xf5d06b51, - 0x3683467a, 0x77b25d63, 0x4ed7facb, 0x0fe6e1d2, 0xccb5ccf9, - 0x8d84d7e0, 0x4a1296af, 0x0b238db6, 0xc870a09d, 0x8941bb84, - 0x465d2303, 0x076c381a, 0xc43f1531, 0x850e0e28, 0x42984f67, - 0x03a9547e, 0xc0fa7955, 0x81cb624c, 0x1fc53881, 0x5ef42398, - 0x9da70eb3, 0xdc9615aa, 0x1b0054e5, 0x5a314ffc, 0x996262d7, - 0xd85379ce, 0x174fe149, 0x567efa50, 0x952dd77b, 0xd41ccc62, - 0x138a8d2d, 0x52bb9634, 0x91e8bb1f, 0xd0d9a006, 0xecf37e5e, - 0xadc26547, 0x6e91486c, 0x2fa05375, 0xe836123a, 0xa9070923, - 0x6a542408, 0x2b653f11, 0xe479a796, 0xa548bc8f, 0x661b91a4, - 0x272a8abd, 0xe0bccbf2, 0xa18dd0eb, 0x62defdc0, 0x23efe6d9, - 0xbde1bc14, 0xfcd0a70d, 0x3f838a26, 0x7eb2913f, 0xb924d070, - 0xf815cb69, 0x3b46e642, 0x7a77fd5b, 0xb56b65dc, 0xf45a7ec5, - 0x370953ee, 0x763848f7, 0xb1ae09b8, 0xf09f12a1, 0x33cc3f8a, - 0x72fd2493}, - {0x00000000, 0x376ac201, 0x6ed48403, 0x59be4602, 0xdca80907, - 0xebc2cb06, 0xb27c8d04, 0x85164f05, 0xb851130e, 0x8f3bd10f, - 0xd685970d, 0xe1ef550c, 0x64f91a09, 0x5393d808, 0x0a2d9e0a, - 0x3d475c0b, 0x70a3261c, 0x47c9e41d, 0x1e77a21f, 0x291d601e, - 0xac0b2f1b, 0x9b61ed1a, 0xc2dfab18, 0xf5b56919, 0xc8f23512, - 0xff98f713, 0xa626b111, 0x914c7310, 0x145a3c15, 0x2330fe14, - 0x7a8eb816, 0x4de47a17, 0xe0464d38, 0xd72c8f39, 0x8e92c93b, - 0xb9f80b3a, 0x3cee443f, 0x0b84863e, 0x523ac03c, 0x6550023d, - 0x58175e36, 0x6f7d9c37, 0x36c3da35, 0x01a91834, 0x84bf5731, - 0xb3d59530, 0xea6bd332, 0xdd011133, 0x90e56b24, 0xa78fa925, - 0xfe31ef27, 0xc95b2d26, 0x4c4d6223, 0x7b27a022, 0x2299e620, - 0x15f32421, 0x28b4782a, 0x1fdeba2b, 0x4660fc29, 0x710a3e28, - 0xf41c712d, 0xc376b32c, 0x9ac8f52e, 0xada2372f, 0xc08d9a70, - 0xf7e75871, 0xae591e73, 0x9933dc72, 0x1c259377, 0x2b4f5176, - 0x72f11774, 0x459bd575, 0x78dc897e, 0x4fb64b7f, 0x16080d7d, - 0x2162cf7c, 0xa4748079, 0x931e4278, 0xcaa0047a, 0xfdcac67b, - 0xb02ebc6c, 0x87447e6d, 0xdefa386f, 0xe990fa6e, 0x6c86b56b, - 0x5bec776a, 0x02523168, 0x3538f369, 0x087faf62, 0x3f156d63, - 0x66ab2b61, 0x51c1e960, 0xd4d7a665, 0xe3bd6464, 0xba032266, - 0x8d69e067, 0x20cbd748, 0x17a11549, 0x4e1f534b, 0x7975914a, - 0xfc63de4f, 0xcb091c4e, 0x92b75a4c, 0xa5dd984d, 0x989ac446, - 0xaff00647, 0xf64e4045, 0xc1248244, 0x4432cd41, 0x73580f40, - 0x2ae64942, 0x1d8c8b43, 0x5068f154, 0x67023355, 0x3ebc7557, - 0x09d6b756, 0x8cc0f853, 0xbbaa3a52, 0xe2147c50, 0xd57ebe51, - 0xe839e25a, 0xdf53205b, 0x86ed6659, 0xb187a458, 0x3491eb5d, - 0x03fb295c, 0x5a456f5e, 0x6d2fad5f, 0x801b35e1, 0xb771f7e0, - 0xeecfb1e2, 0xd9a573e3, 0x5cb33ce6, 0x6bd9fee7, 0x3267b8e5, - 0x050d7ae4, 0x384a26ef, 0x0f20e4ee, 0x569ea2ec, 0x61f460ed, - 0xe4e22fe8, 0xd388ede9, 0x8a36abeb, 0xbd5c69ea, 0xf0b813fd, - 0xc7d2d1fc, 0x9e6c97fe, 0xa90655ff, 0x2c101afa, 0x1b7ad8fb, - 0x42c49ef9, 0x75ae5cf8, 0x48e900f3, 0x7f83c2f2, 0x263d84f0, - 0x115746f1, 0x944109f4, 0xa32bcbf5, 0xfa958df7, 0xcdff4ff6, - 0x605d78d9, 0x5737bad8, 0x0e89fcda, 0x39e33edb, 0xbcf571de, - 0x8b9fb3df, 0xd221f5dd, 0xe54b37dc, 0xd80c6bd7, 0xef66a9d6, - 0xb6d8efd4, 0x81b22dd5, 0x04a462d0, 0x33cea0d1, 0x6a70e6d3, - 0x5d1a24d2, 0x10fe5ec5, 0x27949cc4, 0x7e2adac6, 0x494018c7, - 0xcc5657c2, 0xfb3c95c3, 0xa282d3c1, 0x95e811c0, 0xa8af4dcb, - 0x9fc58fca, 0xc67bc9c8, 0xf1110bc9, 0x740744cc, 0x436d86cd, - 0x1ad3c0cf, 0x2db902ce, 0x4096af91, 0x77fc6d90, 0x2e422b92, - 0x1928e993, 0x9c3ea696, 0xab546497, 0xf2ea2295, 0xc580e094, - 0xf8c7bc9f, 0xcfad7e9e, 0x9613389c, 0xa179fa9d, 0x246fb598, - 0x13057799, 0x4abb319b, 0x7dd1f39a, 0x3035898d, 0x075f4b8c, - 0x5ee10d8e, 0x698bcf8f, 0xec9d808a, 0xdbf7428b, 0x82490489, - 0xb523c688, 0x88649a83, 0xbf0e5882, 0xe6b01e80, 0xd1dadc81, - 0x54cc9384, 0x63a65185, 0x3a181787, 0x0d72d586, 0xa0d0e2a9, - 0x97ba20a8, 0xce0466aa, 0xf96ea4ab, 0x7c78ebae, 0x4b1229af, - 0x12ac6fad, 0x25c6adac, 0x1881f1a7, 0x2feb33a6, 0x765575a4, - 0x413fb7a5, 0xc429f8a0, 0xf3433aa1, 0xaafd7ca3, 0x9d97bea2, - 0xd073c4b5, 0xe71906b4, 0xbea740b6, 0x89cd82b7, 0x0cdbcdb2, - 0x3bb10fb3, 0x620f49b1, 0x55658bb0, 0x6822d7bb, 0x5f4815ba, - 0x06f653b8, 0x319c91b9, 0xb48adebc, 0x83e01cbd, 0xda5e5abf, - 0xed3498be}, - {0x00000000, 0x6567bcb8, 0x8bc809aa, 0xeeafb512, 0x5797628f, - 0x32f0de37, 0xdc5f6b25, 0xb938d79d, 0xef28b4c5, 0x8a4f087d, - 0x64e0bd6f, 0x018701d7, 0xb8bfd64a, 0xddd86af2, 0x3377dfe0, - 0x56106358, 0x9f571950, 0xfa30a5e8, 0x149f10fa, 0x71f8ac42, - 0xc8c07bdf, 0xada7c767, 0x43087275, 0x266fcecd, 0x707fad95, - 0x1518112d, 0xfbb7a43f, 0x9ed01887, 0x27e8cf1a, 0x428f73a2, - 0xac20c6b0, 0xc9477a08, 0x3eaf32a0, 0x5bc88e18, 0xb5673b0a, - 0xd00087b2, 0x6938502f, 0x0c5fec97, 0xe2f05985, 0x8797e53d, - 0xd1878665, 0xb4e03add, 0x5a4f8fcf, 0x3f283377, 0x8610e4ea, - 0xe3775852, 0x0dd8ed40, 0x68bf51f8, 0xa1f82bf0, 0xc49f9748, - 0x2a30225a, 0x4f579ee2, 0xf66f497f, 0x9308f5c7, 0x7da740d5, - 0x18c0fc6d, 0x4ed09f35, 0x2bb7238d, 0xc518969f, 0xa07f2a27, - 0x1947fdba, 0x7c204102, 0x928ff410, 0xf7e848a8, 0x3d58149b, - 0x583fa823, 0xb6901d31, 0xd3f7a189, 0x6acf7614, 0x0fa8caac, - 0xe1077fbe, 0x8460c306, 0xd270a05e, 0xb7171ce6, 0x59b8a9f4, - 0x3cdf154c, 0x85e7c2d1, 0xe0807e69, 0x0e2fcb7b, 0x6b4877c3, - 0xa20f0dcb, 0xc768b173, 0x29c70461, 0x4ca0b8d9, 0xf5986f44, - 0x90ffd3fc, 0x7e5066ee, 0x1b37da56, 0x4d27b90e, 0x284005b6, - 0xc6efb0a4, 0xa3880c1c, 0x1ab0db81, 0x7fd76739, 0x9178d22b, - 0xf41f6e93, 0x03f7263b, 0x66909a83, 0x883f2f91, 0xed589329, - 0x546044b4, 0x3107f80c, 0xdfa84d1e, 0xbacff1a6, 0xecdf92fe, - 0x89b82e46, 0x67179b54, 0x027027ec, 0xbb48f071, 0xde2f4cc9, - 0x3080f9db, 0x55e74563, 0x9ca03f6b, 0xf9c783d3, 0x176836c1, - 0x720f8a79, 0xcb375de4, 0xae50e15c, 0x40ff544e, 0x2598e8f6, - 0x73888bae, 0x16ef3716, 0xf8408204, 0x9d273ebc, 0x241fe921, - 0x41785599, 0xafd7e08b, 0xcab05c33, 0x3bb659ed, 0x5ed1e555, - 0xb07e5047, 0xd519ecff, 0x6c213b62, 0x094687da, 0xe7e932c8, - 0x828e8e70, 0xd49eed28, 0xb1f95190, 0x5f56e482, 0x3a31583a, - 0x83098fa7, 0xe66e331f, 0x08c1860d, 0x6da63ab5, 0xa4e140bd, - 0xc186fc05, 0x2f294917, 0x4a4ef5af, 0xf3762232, 0x96119e8a, - 0x78be2b98, 0x1dd99720, 0x4bc9f478, 0x2eae48c0, 0xc001fdd2, - 0xa566416a, 0x1c5e96f7, 0x79392a4f, 0x97969f5d, 0xf2f123e5, - 0x05196b4d, 0x607ed7f5, 0x8ed162e7, 0xebb6de5f, 0x528e09c2, - 0x37e9b57a, 0xd9460068, 0xbc21bcd0, 0xea31df88, 0x8f566330, - 0x61f9d622, 0x049e6a9a, 0xbda6bd07, 0xd8c101bf, 0x366eb4ad, - 0x53090815, 0x9a4e721d, 0xff29cea5, 0x11867bb7, 0x74e1c70f, - 0xcdd91092, 0xa8beac2a, 0x46111938, 0x2376a580, 0x7566c6d8, - 0x10017a60, 0xfeaecf72, 0x9bc973ca, 0x22f1a457, 0x479618ef, - 0xa939adfd, 0xcc5e1145, 0x06ee4d76, 0x6389f1ce, 0x8d2644dc, - 0xe841f864, 0x51792ff9, 0x341e9341, 0xdab12653, 0xbfd69aeb, - 0xe9c6f9b3, 0x8ca1450b, 0x620ef019, 0x07694ca1, 0xbe519b3c, - 0xdb362784, 0x35999296, 0x50fe2e2e, 0x99b95426, 0xfcdee89e, - 0x12715d8c, 0x7716e134, 0xce2e36a9, 0xab498a11, 0x45e63f03, - 0x208183bb, 0x7691e0e3, 0x13f65c5b, 0xfd59e949, 0x983e55f1, - 0x2106826c, 0x44613ed4, 0xaace8bc6, 0xcfa9377e, 0x38417fd6, - 0x5d26c36e, 0xb389767c, 0xd6eecac4, 0x6fd61d59, 0x0ab1a1e1, - 0xe41e14f3, 0x8179a84b, 0xd769cb13, 0xb20e77ab, 0x5ca1c2b9, - 0x39c67e01, 0x80fea99c, 0xe5991524, 0x0b36a036, 0x6e511c8e, - 0xa7166686, 0xc271da3e, 0x2cde6f2c, 0x49b9d394, 0xf0810409, - 0x95e6b8b1, 0x7b490da3, 0x1e2eb11b, 0x483ed243, 0x2d596efb, - 0xc3f6dbe9, 0xa6916751, 0x1fa9b0cc, 0x7ace0c74, 0x9461b966, - 0xf10605de}}; - -#endif - -#endif - -#if N == 2 - -#if W == 8 - -local const z_crc_t FAR crc_braid_table[][256] = { - {0x00000000, 0xae689191, 0x87a02563, 0x29c8b4f2, 0xd4314c87, - 0x7a59dd16, 0x539169e4, 0xfdf9f875, 0x73139f4f, 0xdd7b0ede, - 0xf4b3ba2c, 0x5adb2bbd, 0xa722d3c8, 0x094a4259, 0x2082f6ab, - 0x8eea673a, 0xe6273e9e, 0x484faf0f, 0x61871bfd, 0xcfef8a6c, - 0x32167219, 0x9c7ee388, 0xb5b6577a, 0x1bdec6eb, 0x9534a1d1, - 0x3b5c3040, 0x129484b2, 0xbcfc1523, 0x4105ed56, 0xef6d7cc7, - 0xc6a5c835, 0x68cd59a4, 0x173f7b7d, 0xb957eaec, 0x909f5e1e, - 0x3ef7cf8f, 0xc30e37fa, 0x6d66a66b, 0x44ae1299, 0xeac68308, - 0x642ce432, 0xca4475a3, 0xe38cc151, 0x4de450c0, 0xb01da8b5, - 0x1e753924, 0x37bd8dd6, 0x99d51c47, 0xf11845e3, 0x5f70d472, - 0x76b86080, 0xd8d0f111, 0x25290964, 0x8b4198f5, 0xa2892c07, - 0x0ce1bd96, 0x820bdaac, 0x2c634b3d, 0x05abffcf, 0xabc36e5e, - 0x563a962b, 0xf85207ba, 0xd19ab348, 0x7ff222d9, 0x2e7ef6fa, - 0x8016676b, 0xa9ded399, 0x07b64208, 0xfa4fba7d, 0x54272bec, - 0x7def9f1e, 0xd3870e8f, 0x5d6d69b5, 0xf305f824, 0xdacd4cd6, - 0x74a5dd47, 0x895c2532, 0x2734b4a3, 0x0efc0051, 0xa09491c0, - 0xc859c864, 0x663159f5, 0x4ff9ed07, 0xe1917c96, 0x1c6884e3, - 0xb2001572, 0x9bc8a180, 0x35a03011, 0xbb4a572b, 0x1522c6ba, - 0x3cea7248, 0x9282e3d9, 0x6f7b1bac, 0xc1138a3d, 0xe8db3ecf, - 0x46b3af5e, 0x39418d87, 0x97291c16, 0xbee1a8e4, 0x10893975, - 0xed70c100, 0x43185091, 0x6ad0e463, 0xc4b875f2, 0x4a5212c8, - 0xe43a8359, 0xcdf237ab, 0x639aa63a, 0x9e635e4f, 0x300bcfde, - 0x19c37b2c, 0xb7abeabd, 0xdf66b319, 0x710e2288, 0x58c6967a, - 0xf6ae07eb, 0x0b57ff9e, 0xa53f6e0f, 0x8cf7dafd, 0x229f4b6c, - 0xac752c56, 0x021dbdc7, 0x2bd50935, 0x85bd98a4, 0x784460d1, - 0xd62cf140, 0xffe445b2, 0x518cd423, 0x5cfdedf4, 0xf2957c65, - 0xdb5dc897, 0x75355906, 0x88cca173, 0x26a430e2, 0x0f6c8410, - 0xa1041581, 0x2fee72bb, 0x8186e32a, 0xa84e57d8, 0x0626c649, - 0xfbdf3e3c, 0x55b7afad, 0x7c7f1b5f, 0xd2178ace, 0xbadad36a, - 0x14b242fb, 0x3d7af609, 0x93126798, 0x6eeb9fed, 0xc0830e7c, - 0xe94bba8e, 0x47232b1f, 0xc9c94c25, 0x67a1ddb4, 0x4e696946, - 0xe001f8d7, 0x1df800a2, 0xb3909133, 0x9a5825c1, 0x3430b450, - 0x4bc29689, 0xe5aa0718, 0xcc62b3ea, 0x620a227b, 0x9ff3da0e, - 0x319b4b9f, 0x1853ff6d, 0xb63b6efc, 0x38d109c6, 0x96b99857, - 0xbf712ca5, 0x1119bd34, 0xece04541, 0x4288d4d0, 0x6b406022, - 0xc528f1b3, 0xade5a817, 0x038d3986, 0x2a458d74, 0x842d1ce5, - 0x79d4e490, 0xd7bc7501, 0xfe74c1f3, 0x501c5062, 0xdef63758, - 0x709ea6c9, 0x5956123b, 0xf73e83aa, 0x0ac77bdf, 0xa4afea4e, - 0x8d675ebc, 0x230fcf2d, 0x72831b0e, 0xdceb8a9f, 0xf5233e6d, - 0x5b4baffc, 0xa6b25789, 0x08dac618, 0x211272ea, 0x8f7ae37b, - 0x01908441, 0xaff815d0, 0x8630a122, 0x285830b3, 0xd5a1c8c6, - 0x7bc95957, 0x5201eda5, 0xfc697c34, 0x94a42590, 0x3accb401, - 0x130400f3, 0xbd6c9162, 0x40956917, 0xeefdf886, 0xc7354c74, - 0x695ddde5, 0xe7b7badf, 0x49df2b4e, 0x60179fbc, 0xce7f0e2d, - 0x3386f658, 0x9dee67c9, 0xb426d33b, 0x1a4e42aa, 0x65bc6073, - 0xcbd4f1e2, 0xe21c4510, 0x4c74d481, 0xb18d2cf4, 0x1fe5bd65, - 0x362d0997, 0x98459806, 0x16afff3c, 0xb8c76ead, 0x910fda5f, - 0x3f674bce, 0xc29eb3bb, 0x6cf6222a, 0x453e96d8, 0xeb560749, - 0x839b5eed, 0x2df3cf7c, 0x043b7b8e, 0xaa53ea1f, 0x57aa126a, - 0xf9c283fb, 0xd00a3709, 0x7e62a698, 0xf088c1a2, 0x5ee05033, - 0x7728e4c1, 0xd9407550, 0x24b98d25, 0x8ad11cb4, 0xa319a846, - 0x0d7139d7}, - {0x00000000, 0xb9fbdbe8, 0xa886b191, 0x117d6a79, 0x8a7c6563, - 0x3387be8b, 0x22fad4f2, 0x9b010f1a, 0xcf89cc87, 0x7672176f, - 0x670f7d16, 0xdef4a6fe, 0x45f5a9e4, 0xfc0e720c, 0xed731875, - 0x5488c39d, 0x44629f4f, 0xfd9944a7, 0xece42ede, 0x551ff536, - 0xce1efa2c, 0x77e521c4, 0x66984bbd, 0xdf639055, 0x8beb53c8, - 0x32108820, 0x236de259, 0x9a9639b1, 0x019736ab, 0xb86ced43, - 0xa911873a, 0x10ea5cd2, 0x88c53e9e, 0x313ee576, 0x20438f0f, - 0x99b854e7, 0x02b95bfd, 0xbb428015, 0xaa3fea6c, 0x13c43184, - 0x474cf219, 0xfeb729f1, 0xefca4388, 0x56319860, 0xcd30977a, - 0x74cb4c92, 0x65b626eb, 0xdc4dfd03, 0xcca7a1d1, 0x755c7a39, - 0x64211040, 0xdddacba8, 0x46dbc4b2, 0xff201f5a, 0xee5d7523, - 0x57a6aecb, 0x032e6d56, 0xbad5b6be, 0xaba8dcc7, 0x1253072f, - 0x89520835, 0x30a9d3dd, 0x21d4b9a4, 0x982f624c, 0xcafb7b7d, - 0x7300a095, 0x627dcaec, 0xdb861104, 0x40871e1e, 0xf97cc5f6, - 0xe801af8f, 0x51fa7467, 0x0572b7fa, 0xbc896c12, 0xadf4066b, - 0x140fdd83, 0x8f0ed299, 0x36f50971, 0x27886308, 0x9e73b8e0, - 0x8e99e432, 0x37623fda, 0x261f55a3, 0x9fe48e4b, 0x04e58151, - 0xbd1e5ab9, 0xac6330c0, 0x1598eb28, 0x411028b5, 0xf8ebf35d, - 0xe9969924, 0x506d42cc, 0xcb6c4dd6, 0x7297963e, 0x63eafc47, - 0xda1127af, 0x423e45e3, 0xfbc59e0b, 0xeab8f472, 0x53432f9a, - 0xc8422080, 0x71b9fb68, 0x60c49111, 0xd93f4af9, 0x8db78964, - 0x344c528c, 0x253138f5, 0x9ccae31d, 0x07cbec07, 0xbe3037ef, - 0xaf4d5d96, 0x16b6867e, 0x065cdaac, 0xbfa70144, 0xaeda6b3d, - 0x1721b0d5, 0x8c20bfcf, 0x35db6427, 0x24a60e5e, 0x9d5dd5b6, - 0xc9d5162b, 0x702ecdc3, 0x6153a7ba, 0xd8a87c52, 0x43a97348, - 0xfa52a8a0, 0xeb2fc2d9, 0x52d41931, 0x4e87f0bb, 0xf77c2b53, - 0xe601412a, 0x5ffa9ac2, 0xc4fb95d8, 0x7d004e30, 0x6c7d2449, - 0xd586ffa1, 0x810e3c3c, 0x38f5e7d4, 0x29888dad, 0x90735645, - 0x0b72595f, 0xb28982b7, 0xa3f4e8ce, 0x1a0f3326, 0x0ae56ff4, - 0xb31eb41c, 0xa263de65, 0x1b98058d, 0x80990a97, 0x3962d17f, - 0x281fbb06, 0x91e460ee, 0xc56ca373, 0x7c97789b, 0x6dea12e2, - 0xd411c90a, 0x4f10c610, 0xf6eb1df8, 0xe7967781, 0x5e6dac69, - 0xc642ce25, 0x7fb915cd, 0x6ec47fb4, 0xd73fa45c, 0x4c3eab46, - 0xf5c570ae, 0xe4b81ad7, 0x5d43c13f, 0x09cb02a2, 0xb030d94a, - 0xa14db333, 0x18b668db, 0x83b767c1, 0x3a4cbc29, 0x2b31d650, - 0x92ca0db8, 0x8220516a, 0x3bdb8a82, 0x2aa6e0fb, 0x935d3b13, - 0x085c3409, 0xb1a7efe1, 0xa0da8598, 0x19215e70, 0x4da99ded, - 0xf4524605, 0xe52f2c7c, 0x5cd4f794, 0xc7d5f88e, 0x7e2e2366, - 0x6f53491f, 0xd6a892f7, 0x847c8bc6, 0x3d87502e, 0x2cfa3a57, - 0x9501e1bf, 0x0e00eea5, 0xb7fb354d, 0xa6865f34, 0x1f7d84dc, - 0x4bf54741, 0xf20e9ca9, 0xe373f6d0, 0x5a882d38, 0xc1892222, - 0x7872f9ca, 0x690f93b3, 0xd0f4485b, 0xc01e1489, 0x79e5cf61, - 0x6898a518, 0xd1637ef0, 0x4a6271ea, 0xf399aa02, 0xe2e4c07b, - 0x5b1f1b93, 0x0f97d80e, 0xb66c03e6, 0xa711699f, 0x1eeab277, - 0x85ebbd6d, 0x3c106685, 0x2d6d0cfc, 0x9496d714, 0x0cb9b558, - 0xb5426eb0, 0xa43f04c9, 0x1dc4df21, 0x86c5d03b, 0x3f3e0bd3, - 0x2e4361aa, 0x97b8ba42, 0xc33079df, 0x7acba237, 0x6bb6c84e, - 0xd24d13a6, 0x494c1cbc, 0xf0b7c754, 0xe1caad2d, 0x583176c5, - 0x48db2a17, 0xf120f1ff, 0xe05d9b86, 0x59a6406e, 0xc2a74f74, - 0x7b5c949c, 0x6a21fee5, 0xd3da250d, 0x8752e690, 0x3ea93d78, - 0x2fd45701, 0x962f8ce9, 0x0d2e83f3, 0xb4d5581b, 0xa5a83262, - 0x1c53e98a}, - {0x00000000, 0x9d0fe176, 0xe16ec4ad, 0x7c6125db, 0x19ac8f1b, - 0x84a36e6d, 0xf8c24bb6, 0x65cdaac0, 0x33591e36, 0xae56ff40, - 0xd237da9b, 0x4f383bed, 0x2af5912d, 0xb7fa705b, 0xcb9b5580, - 0x5694b4f6, 0x66b23c6c, 0xfbbddd1a, 0x87dcf8c1, 0x1ad319b7, - 0x7f1eb377, 0xe2115201, 0x9e7077da, 0x037f96ac, 0x55eb225a, - 0xc8e4c32c, 0xb485e6f7, 0x298a0781, 0x4c47ad41, 0xd1484c37, - 0xad2969ec, 0x3026889a, 0xcd6478d8, 0x506b99ae, 0x2c0abc75, - 0xb1055d03, 0xd4c8f7c3, 0x49c716b5, 0x35a6336e, 0xa8a9d218, - 0xfe3d66ee, 0x63328798, 0x1f53a243, 0x825c4335, 0xe791e9f5, - 0x7a9e0883, 0x06ff2d58, 0x9bf0cc2e, 0xabd644b4, 0x36d9a5c2, - 0x4ab88019, 0xd7b7616f, 0xb27acbaf, 0x2f752ad9, 0x53140f02, - 0xce1bee74, 0x988f5a82, 0x0580bbf4, 0x79e19e2f, 0xe4ee7f59, - 0x8123d599, 0x1c2c34ef, 0x604d1134, 0xfd42f042, 0x41b9f7f1, - 0xdcb61687, 0xa0d7335c, 0x3dd8d22a, 0x581578ea, 0xc51a999c, - 0xb97bbc47, 0x24745d31, 0x72e0e9c7, 0xefef08b1, 0x938e2d6a, - 0x0e81cc1c, 0x6b4c66dc, 0xf64387aa, 0x8a22a271, 0x172d4307, - 0x270bcb9d, 0xba042aeb, 0xc6650f30, 0x5b6aee46, 0x3ea74486, - 0xa3a8a5f0, 0xdfc9802b, 0x42c6615d, 0x1452d5ab, 0x895d34dd, - 0xf53c1106, 0x6833f070, 0x0dfe5ab0, 0x90f1bbc6, 0xec909e1d, - 0x719f7f6b, 0x8cdd8f29, 0x11d26e5f, 0x6db34b84, 0xf0bcaaf2, - 0x95710032, 0x087ee144, 0x741fc49f, 0xe91025e9, 0xbf84911f, - 0x228b7069, 0x5eea55b2, 0xc3e5b4c4, 0xa6281e04, 0x3b27ff72, - 0x4746daa9, 0xda493bdf, 0xea6fb345, 0x77605233, 0x0b0177e8, - 0x960e969e, 0xf3c33c5e, 0x6eccdd28, 0x12adf8f3, 0x8fa21985, - 0xd936ad73, 0x44394c05, 0x385869de, 0xa55788a8, 0xc09a2268, - 0x5d95c31e, 0x21f4e6c5, 0xbcfb07b3, 0x8373efe2, 0x1e7c0e94, - 0x621d2b4f, 0xff12ca39, 0x9adf60f9, 0x07d0818f, 0x7bb1a454, - 0xe6be4522, 0xb02af1d4, 0x2d2510a2, 0x51443579, 0xcc4bd40f, - 0xa9867ecf, 0x34899fb9, 0x48e8ba62, 0xd5e75b14, 0xe5c1d38e, - 0x78ce32f8, 0x04af1723, 0x99a0f655, 0xfc6d5c95, 0x6162bde3, - 0x1d039838, 0x800c794e, 0xd698cdb8, 0x4b972cce, 0x37f60915, - 0xaaf9e863, 0xcf3442a3, 0x523ba3d5, 0x2e5a860e, 0xb3556778, - 0x4e17973a, 0xd318764c, 0xaf795397, 0x3276b2e1, 0x57bb1821, - 0xcab4f957, 0xb6d5dc8c, 0x2bda3dfa, 0x7d4e890c, 0xe041687a, - 0x9c204da1, 0x012facd7, 0x64e20617, 0xf9ede761, 0x858cc2ba, - 0x188323cc, 0x28a5ab56, 0xb5aa4a20, 0xc9cb6ffb, 0x54c48e8d, - 0x3109244d, 0xac06c53b, 0xd067e0e0, 0x4d680196, 0x1bfcb560, - 0x86f35416, 0xfa9271cd, 0x679d90bb, 0x02503a7b, 0x9f5fdb0d, - 0xe33efed6, 0x7e311fa0, 0xc2ca1813, 0x5fc5f965, 0x23a4dcbe, - 0xbeab3dc8, 0xdb669708, 0x4669767e, 0x3a0853a5, 0xa707b2d3, - 0xf1930625, 0x6c9ce753, 0x10fdc288, 0x8df223fe, 0xe83f893e, - 0x75306848, 0x09514d93, 0x945eace5, 0xa478247f, 0x3977c509, - 0x4516e0d2, 0xd81901a4, 0xbdd4ab64, 0x20db4a12, 0x5cba6fc9, - 0xc1b58ebf, 0x97213a49, 0x0a2edb3f, 0x764ffee4, 0xeb401f92, - 0x8e8db552, 0x13825424, 0x6fe371ff, 0xf2ec9089, 0x0fae60cb, - 0x92a181bd, 0xeec0a466, 0x73cf4510, 0x1602efd0, 0x8b0d0ea6, - 0xf76c2b7d, 0x6a63ca0b, 0x3cf77efd, 0xa1f89f8b, 0xdd99ba50, - 0x40965b26, 0x255bf1e6, 0xb8541090, 0xc435354b, 0x593ad43d, - 0x691c5ca7, 0xf413bdd1, 0x8872980a, 0x157d797c, 0x70b0d3bc, - 0xedbf32ca, 0x91de1711, 0x0cd1f667, 0x5a454291, 0xc74aa3e7, - 0xbb2b863c, 0x2624674a, 0x43e9cd8a, 0xdee62cfc, 0xa2870927, - 0x3f88e851}, - {0x00000000, 0xdd96d985, 0x605cb54b, 0xbdca6cce, 0xc0b96a96, - 0x1d2fb313, 0xa0e5dfdd, 0x7d730658, 0x5a03d36d, 0x87950ae8, - 0x3a5f6626, 0xe7c9bfa3, 0x9abab9fb, 0x472c607e, 0xfae60cb0, - 0x2770d535, 0xb407a6da, 0x69917f5f, 0xd45b1391, 0x09cdca14, - 0x74becc4c, 0xa92815c9, 0x14e27907, 0xc974a082, 0xee0475b7, - 0x3392ac32, 0x8e58c0fc, 0x53ce1979, 0x2ebd1f21, 0xf32bc6a4, - 0x4ee1aa6a, 0x937773ef, 0xb37e4bf5, 0x6ee89270, 0xd322febe, - 0x0eb4273b, 0x73c72163, 0xae51f8e6, 0x139b9428, 0xce0d4dad, - 0xe97d9898, 0x34eb411d, 0x89212dd3, 0x54b7f456, 0x29c4f20e, - 0xf4522b8b, 0x49984745, 0x940e9ec0, 0x0779ed2f, 0xdaef34aa, - 0x67255864, 0xbab381e1, 0xc7c087b9, 0x1a565e3c, 0xa79c32f2, - 0x7a0aeb77, 0x5d7a3e42, 0x80ece7c7, 0x3d268b09, 0xe0b0528c, - 0x9dc354d4, 0x40558d51, 0xfd9fe19f, 0x2009381a, 0xbd8d91ab, - 0x601b482e, 0xddd124e0, 0x0047fd65, 0x7d34fb3d, 0xa0a222b8, - 0x1d684e76, 0xc0fe97f3, 0xe78e42c6, 0x3a189b43, 0x87d2f78d, - 0x5a442e08, 0x27372850, 0xfaa1f1d5, 0x476b9d1b, 0x9afd449e, - 0x098a3771, 0xd41ceef4, 0x69d6823a, 0xb4405bbf, 0xc9335de7, - 0x14a58462, 0xa96fe8ac, 0x74f93129, 0x5389e41c, 0x8e1f3d99, - 0x33d55157, 0xee4388d2, 0x93308e8a, 0x4ea6570f, 0xf36c3bc1, - 0x2efae244, 0x0ef3da5e, 0xd36503db, 0x6eaf6f15, 0xb339b690, - 0xce4ab0c8, 0x13dc694d, 0xae160583, 0x7380dc06, 0x54f00933, - 0x8966d0b6, 0x34acbc78, 0xe93a65fd, 0x944963a5, 0x49dfba20, - 0xf415d6ee, 0x29830f6b, 0xbaf47c84, 0x6762a501, 0xdaa8c9cf, - 0x073e104a, 0x7a4d1612, 0xa7dbcf97, 0x1a11a359, 0xc7877adc, - 0xe0f7afe9, 0x3d61766c, 0x80ab1aa2, 0x5d3dc327, 0x204ec57f, - 0xfdd81cfa, 0x40127034, 0x9d84a9b1, 0xa06a2517, 0x7dfcfc92, - 0xc036905c, 0x1da049d9, 0x60d34f81, 0xbd459604, 0x008ffaca, - 0xdd19234f, 0xfa69f67a, 0x27ff2fff, 0x9a354331, 0x47a39ab4, - 0x3ad09cec, 0xe7464569, 0x5a8c29a7, 0x871af022, 0x146d83cd, - 0xc9fb5a48, 0x74313686, 0xa9a7ef03, 0xd4d4e95b, 0x094230de, - 0xb4885c10, 0x691e8595, 0x4e6e50a0, 0x93f88925, 0x2e32e5eb, - 0xf3a43c6e, 0x8ed73a36, 0x5341e3b3, 0xee8b8f7d, 0x331d56f8, - 0x13146ee2, 0xce82b767, 0x7348dba9, 0xaede022c, 0xd3ad0474, - 0x0e3bddf1, 0xb3f1b13f, 0x6e6768ba, 0x4917bd8f, 0x9481640a, - 0x294b08c4, 0xf4ddd141, 0x89aed719, 0x54380e9c, 0xe9f26252, - 0x3464bbd7, 0xa713c838, 0x7a8511bd, 0xc74f7d73, 0x1ad9a4f6, - 0x67aaa2ae, 0xba3c7b2b, 0x07f617e5, 0xda60ce60, 0xfd101b55, - 0x2086c2d0, 0x9d4cae1e, 0x40da779b, 0x3da971c3, 0xe03fa846, - 0x5df5c488, 0x80631d0d, 0x1de7b4bc, 0xc0716d39, 0x7dbb01f7, - 0xa02dd872, 0xdd5ede2a, 0x00c807af, 0xbd026b61, 0x6094b2e4, - 0x47e467d1, 0x9a72be54, 0x27b8d29a, 0xfa2e0b1f, 0x875d0d47, - 0x5acbd4c2, 0xe701b80c, 0x3a976189, 0xa9e01266, 0x7476cbe3, - 0xc9bca72d, 0x142a7ea8, 0x695978f0, 0xb4cfa175, 0x0905cdbb, - 0xd493143e, 0xf3e3c10b, 0x2e75188e, 0x93bf7440, 0x4e29adc5, - 0x335aab9d, 0xeecc7218, 0x53061ed6, 0x8e90c753, 0xae99ff49, - 0x730f26cc, 0xcec54a02, 0x13539387, 0x6e2095df, 0xb3b64c5a, - 0x0e7c2094, 0xd3eaf911, 0xf49a2c24, 0x290cf5a1, 0x94c6996f, - 0x495040ea, 0x342346b2, 0xe9b59f37, 0x547ff3f9, 0x89e92a7c, - 0x1a9e5993, 0xc7088016, 0x7ac2ecd8, 0xa754355d, 0xda273305, - 0x07b1ea80, 0xba7b864e, 0x67ed5fcb, 0x409d8afe, 0x9d0b537b, - 0x20c13fb5, 0xfd57e630, 0x8024e068, 0x5db239ed, 0xe0785523, - 0x3dee8ca6}, - {0x00000000, 0x9ba54c6f, 0xec3b9e9f, 0x779ed2f0, 0x03063b7f, - 0x98a37710, 0xef3da5e0, 0x7498e98f, 0x060c76fe, 0x9da93a91, - 0xea37e861, 0x7192a40e, 0x050a4d81, 0x9eaf01ee, 0xe931d31e, - 0x72949f71, 0x0c18edfc, 0x97bda193, 0xe0237363, 0x7b863f0c, - 0x0f1ed683, 0x94bb9aec, 0xe325481c, 0x78800473, 0x0a149b02, - 0x91b1d76d, 0xe62f059d, 0x7d8a49f2, 0x0912a07d, 0x92b7ec12, - 0xe5293ee2, 0x7e8c728d, 0x1831dbf8, 0x83949797, 0xf40a4567, - 0x6faf0908, 0x1b37e087, 0x8092ace8, 0xf70c7e18, 0x6ca93277, - 0x1e3dad06, 0x8598e169, 0xf2063399, 0x69a37ff6, 0x1d3b9679, - 0x869eda16, 0xf10008e6, 0x6aa54489, 0x14293604, 0x8f8c7a6b, - 0xf812a89b, 0x63b7e4f4, 0x172f0d7b, 0x8c8a4114, 0xfb1493e4, - 0x60b1df8b, 0x122540fa, 0x89800c95, 0xfe1ede65, 0x65bb920a, - 0x11237b85, 0x8a8637ea, 0xfd18e51a, 0x66bda975, 0x3063b7f0, - 0xabc6fb9f, 0xdc58296f, 0x47fd6500, 0x33658c8f, 0xa8c0c0e0, - 0xdf5e1210, 0x44fb5e7f, 0x366fc10e, 0xadca8d61, 0xda545f91, - 0x41f113fe, 0x3569fa71, 0xaeccb61e, 0xd95264ee, 0x42f72881, - 0x3c7b5a0c, 0xa7de1663, 0xd040c493, 0x4be588fc, 0x3f7d6173, - 0xa4d82d1c, 0xd346ffec, 0x48e3b383, 0x3a772cf2, 0xa1d2609d, - 0xd64cb26d, 0x4de9fe02, 0x3971178d, 0xa2d45be2, 0xd54a8912, - 0x4eefc57d, 0x28526c08, 0xb3f72067, 0xc469f297, 0x5fccbef8, - 0x2b545777, 0xb0f11b18, 0xc76fc9e8, 0x5cca8587, 0x2e5e1af6, - 0xb5fb5699, 0xc2658469, 0x59c0c806, 0x2d582189, 0xb6fd6de6, - 0xc163bf16, 0x5ac6f379, 0x244a81f4, 0xbfefcd9b, 0xc8711f6b, - 0x53d45304, 0x274cba8b, 0xbce9f6e4, 0xcb772414, 0x50d2687b, - 0x2246f70a, 0xb9e3bb65, 0xce7d6995, 0x55d825fa, 0x2140cc75, - 0xbae5801a, 0xcd7b52ea, 0x56de1e85, 0x60c76fe0, 0xfb62238f, - 0x8cfcf17f, 0x1759bd10, 0x63c1549f, 0xf86418f0, 0x8ffaca00, - 0x145f866f, 0x66cb191e, 0xfd6e5571, 0x8af08781, 0x1155cbee, - 0x65cd2261, 0xfe686e0e, 0x89f6bcfe, 0x1253f091, 0x6cdf821c, - 0xf77ace73, 0x80e41c83, 0x1b4150ec, 0x6fd9b963, 0xf47cf50c, - 0x83e227fc, 0x18476b93, 0x6ad3f4e2, 0xf176b88d, 0x86e86a7d, - 0x1d4d2612, 0x69d5cf9d, 0xf27083f2, 0x85ee5102, 0x1e4b1d6d, - 0x78f6b418, 0xe353f877, 0x94cd2a87, 0x0f6866e8, 0x7bf08f67, - 0xe055c308, 0x97cb11f8, 0x0c6e5d97, 0x7efac2e6, 0xe55f8e89, - 0x92c15c79, 0x09641016, 0x7dfcf999, 0xe659b5f6, 0x91c76706, - 0x0a622b69, 0x74ee59e4, 0xef4b158b, 0x98d5c77b, 0x03708b14, - 0x77e8629b, 0xec4d2ef4, 0x9bd3fc04, 0x0076b06b, 0x72e22f1a, - 0xe9476375, 0x9ed9b185, 0x057cfdea, 0x71e41465, 0xea41580a, - 0x9ddf8afa, 0x067ac695, 0x50a4d810, 0xcb01947f, 0xbc9f468f, - 0x273a0ae0, 0x53a2e36f, 0xc807af00, 0xbf997df0, 0x243c319f, - 0x56a8aeee, 0xcd0de281, 0xba933071, 0x21367c1e, 0x55ae9591, - 0xce0bd9fe, 0xb9950b0e, 0x22304761, 0x5cbc35ec, 0xc7197983, - 0xb087ab73, 0x2b22e71c, 0x5fba0e93, 0xc41f42fc, 0xb381900c, - 0x2824dc63, 0x5ab04312, 0xc1150f7d, 0xb68bdd8d, 0x2d2e91e2, - 0x59b6786d, 0xc2133402, 0xb58de6f2, 0x2e28aa9d, 0x489503e8, - 0xd3304f87, 0xa4ae9d77, 0x3f0bd118, 0x4b933897, 0xd03674f8, - 0xa7a8a608, 0x3c0dea67, 0x4e997516, 0xd53c3979, 0xa2a2eb89, - 0x3907a7e6, 0x4d9f4e69, 0xd63a0206, 0xa1a4d0f6, 0x3a019c99, - 0x448dee14, 0xdf28a27b, 0xa8b6708b, 0x33133ce4, 0x478bd56b, - 0xdc2e9904, 0xabb04bf4, 0x3015079b, 0x428198ea, 0xd924d485, - 0xaeba0675, 0x351f4a1a, 0x4187a395, 0xda22effa, 0xadbc3d0a, - 0x36197165}, - {0x00000000, 0xc18edfc0, 0x586cb9c1, 0x99e26601, 0xb0d97382, - 0x7157ac42, 0xe8b5ca43, 0x293b1583, 0xbac3e145, 0x7b4d3e85, - 0xe2af5884, 0x23218744, 0x0a1a92c7, 0xcb944d07, 0x52762b06, - 0x93f8f4c6, 0xaef6c4cb, 0x6f781b0b, 0xf69a7d0a, 0x3714a2ca, - 0x1e2fb749, 0xdfa16889, 0x46430e88, 0x87cdd148, 0x1435258e, - 0xd5bbfa4e, 0x4c599c4f, 0x8dd7438f, 0xa4ec560c, 0x656289cc, - 0xfc80efcd, 0x3d0e300d, 0x869c8fd7, 0x47125017, 0xdef03616, - 0x1f7ee9d6, 0x3645fc55, 0xf7cb2395, 0x6e294594, 0xafa79a54, - 0x3c5f6e92, 0xfdd1b152, 0x6433d753, 0xa5bd0893, 0x8c861d10, - 0x4d08c2d0, 0xd4eaa4d1, 0x15647b11, 0x286a4b1c, 0xe9e494dc, - 0x7006f2dd, 0xb1882d1d, 0x98b3389e, 0x593de75e, 0xc0df815f, - 0x01515e9f, 0x92a9aa59, 0x53277599, 0xcac51398, 0x0b4bcc58, - 0x2270d9db, 0xe3fe061b, 0x7a1c601a, 0xbb92bfda, 0xd64819ef, - 0x17c6c62f, 0x8e24a02e, 0x4faa7fee, 0x66916a6d, 0xa71fb5ad, - 0x3efdd3ac, 0xff730c6c, 0x6c8bf8aa, 0xad05276a, 0x34e7416b, - 0xf5699eab, 0xdc528b28, 0x1ddc54e8, 0x843e32e9, 0x45b0ed29, - 0x78bedd24, 0xb93002e4, 0x20d264e5, 0xe15cbb25, 0xc867aea6, - 0x09e97166, 0x900b1767, 0x5185c8a7, 0xc27d3c61, 0x03f3e3a1, - 0x9a1185a0, 0x5b9f5a60, 0x72a44fe3, 0xb32a9023, 0x2ac8f622, - 0xeb4629e2, 0x50d49638, 0x915a49f8, 0x08b82ff9, 0xc936f039, - 0xe00de5ba, 0x21833a7a, 0xb8615c7b, 0x79ef83bb, 0xea17777d, - 0x2b99a8bd, 0xb27bcebc, 0x73f5117c, 0x5ace04ff, 0x9b40db3f, - 0x02a2bd3e, 0xc32c62fe, 0xfe2252f3, 0x3fac8d33, 0xa64eeb32, - 0x67c034f2, 0x4efb2171, 0x8f75feb1, 0x169798b0, 0xd7194770, - 0x44e1b3b6, 0x856f6c76, 0x1c8d0a77, 0xdd03d5b7, 0xf438c034, - 0x35b61ff4, 0xac5479f5, 0x6ddaa635, 0x77e1359f, 0xb66fea5f, - 0x2f8d8c5e, 0xee03539e, 0xc738461d, 0x06b699dd, 0x9f54ffdc, - 0x5eda201c, 0xcd22d4da, 0x0cac0b1a, 0x954e6d1b, 0x54c0b2db, - 0x7dfba758, 0xbc757898, 0x25971e99, 0xe419c159, 0xd917f154, - 0x18992e94, 0x817b4895, 0x40f59755, 0x69ce82d6, 0xa8405d16, - 0x31a23b17, 0xf02ce4d7, 0x63d41011, 0xa25acfd1, 0x3bb8a9d0, - 0xfa367610, 0xd30d6393, 0x1283bc53, 0x8b61da52, 0x4aef0592, - 0xf17dba48, 0x30f36588, 0xa9110389, 0x689fdc49, 0x41a4c9ca, - 0x802a160a, 0x19c8700b, 0xd846afcb, 0x4bbe5b0d, 0x8a3084cd, - 0x13d2e2cc, 0xd25c3d0c, 0xfb67288f, 0x3ae9f74f, 0xa30b914e, - 0x62854e8e, 0x5f8b7e83, 0x9e05a143, 0x07e7c742, 0xc6691882, - 0xef520d01, 0x2edcd2c1, 0xb73eb4c0, 0x76b06b00, 0xe5489fc6, - 0x24c64006, 0xbd242607, 0x7caaf9c7, 0x5591ec44, 0x941f3384, - 0x0dfd5585, 0xcc738a45, 0xa1a92c70, 0x6027f3b0, 0xf9c595b1, - 0x384b4a71, 0x11705ff2, 0xd0fe8032, 0x491ce633, 0x889239f3, - 0x1b6acd35, 0xdae412f5, 0x430674f4, 0x8288ab34, 0xabb3beb7, - 0x6a3d6177, 0xf3df0776, 0x3251d8b6, 0x0f5fe8bb, 0xced1377b, - 0x5733517a, 0x96bd8eba, 0xbf869b39, 0x7e0844f9, 0xe7ea22f8, - 0x2664fd38, 0xb59c09fe, 0x7412d63e, 0xedf0b03f, 0x2c7e6fff, - 0x05457a7c, 0xc4cba5bc, 0x5d29c3bd, 0x9ca71c7d, 0x2735a3a7, - 0xe6bb7c67, 0x7f591a66, 0xbed7c5a6, 0x97ecd025, 0x56620fe5, - 0xcf8069e4, 0x0e0eb624, 0x9df642e2, 0x5c789d22, 0xc59afb23, - 0x041424e3, 0x2d2f3160, 0xeca1eea0, 0x754388a1, 0xb4cd5761, - 0x89c3676c, 0x484db8ac, 0xd1afdead, 0x1021016d, 0x391a14ee, - 0xf894cb2e, 0x6176ad2f, 0xa0f872ef, 0x33008629, 0xf28e59e9, - 0x6b6c3fe8, 0xaae2e028, 0x83d9f5ab, 0x42572a6b, 0xdbb54c6a, - 0x1a3b93aa}, - {0x00000000, 0xefc26b3e, 0x04f5d03d, 0xeb37bb03, 0x09eba07a, - 0xe629cb44, 0x0d1e7047, 0xe2dc1b79, 0x13d740f4, 0xfc152bca, - 0x172290c9, 0xf8e0fbf7, 0x1a3ce08e, 0xf5fe8bb0, 0x1ec930b3, - 0xf10b5b8d, 0x27ae81e8, 0xc86cead6, 0x235b51d5, 0xcc993aeb, - 0x2e452192, 0xc1874aac, 0x2ab0f1af, 0xc5729a91, 0x3479c11c, - 0xdbbbaa22, 0x308c1121, 0xdf4e7a1f, 0x3d926166, 0xd2500a58, - 0x3967b15b, 0xd6a5da65, 0x4f5d03d0, 0xa09f68ee, 0x4ba8d3ed, - 0xa46ab8d3, 0x46b6a3aa, 0xa974c894, 0x42437397, 0xad8118a9, - 0x5c8a4324, 0xb348281a, 0x587f9319, 0xb7bdf827, 0x5561e35e, - 0xbaa38860, 0x51943363, 0xbe56585d, 0x68f38238, 0x8731e906, - 0x6c065205, 0x83c4393b, 0x61182242, 0x8eda497c, 0x65edf27f, - 0x8a2f9941, 0x7b24c2cc, 0x94e6a9f2, 0x7fd112f1, 0x901379cf, - 0x72cf62b6, 0x9d0d0988, 0x763ab28b, 0x99f8d9b5, 0x9eba07a0, - 0x71786c9e, 0x9a4fd79d, 0x758dbca3, 0x9751a7da, 0x7893cce4, - 0x93a477e7, 0x7c661cd9, 0x8d6d4754, 0x62af2c6a, 0x89989769, - 0x665afc57, 0x8486e72e, 0x6b448c10, 0x80733713, 0x6fb15c2d, - 0xb9148648, 0x56d6ed76, 0xbde15675, 0x52233d4b, 0xb0ff2632, - 0x5f3d4d0c, 0xb40af60f, 0x5bc89d31, 0xaac3c6bc, 0x4501ad82, - 0xae361681, 0x41f47dbf, 0xa32866c6, 0x4cea0df8, 0xa7ddb6fb, - 0x481fddc5, 0xd1e70470, 0x3e256f4e, 0xd512d44d, 0x3ad0bf73, - 0xd80ca40a, 0x37cecf34, 0xdcf97437, 0x333b1f09, 0xc2304484, - 0x2df22fba, 0xc6c594b9, 0x2907ff87, 0xcbdbe4fe, 0x24198fc0, - 0xcf2e34c3, 0x20ec5ffd, 0xf6498598, 0x198beea6, 0xf2bc55a5, - 0x1d7e3e9b, 0xffa225e2, 0x10604edc, 0xfb57f5df, 0x14959ee1, - 0xe59ec56c, 0x0a5cae52, 0xe16b1551, 0x0ea97e6f, 0xec756516, - 0x03b70e28, 0xe880b52b, 0x0742de15, 0xe6050901, 0x09c7623f, - 0xe2f0d93c, 0x0d32b202, 0xefeea97b, 0x002cc245, 0xeb1b7946, - 0x04d91278, 0xf5d249f5, 0x1a1022cb, 0xf12799c8, 0x1ee5f2f6, - 0xfc39e98f, 0x13fb82b1, 0xf8cc39b2, 0x170e528c, 0xc1ab88e9, - 0x2e69e3d7, 0xc55e58d4, 0x2a9c33ea, 0xc8402893, 0x278243ad, - 0xccb5f8ae, 0x23779390, 0xd27cc81d, 0x3dbea323, 0xd6891820, - 0x394b731e, 0xdb976867, 0x34550359, 0xdf62b85a, 0x30a0d364, - 0xa9580ad1, 0x469a61ef, 0xadaddaec, 0x426fb1d2, 0xa0b3aaab, - 0x4f71c195, 0xa4467a96, 0x4b8411a8, 0xba8f4a25, 0x554d211b, - 0xbe7a9a18, 0x51b8f126, 0xb364ea5f, 0x5ca68161, 0xb7913a62, - 0x5853515c, 0x8ef68b39, 0x6134e007, 0x8a035b04, 0x65c1303a, - 0x871d2b43, 0x68df407d, 0x83e8fb7e, 0x6c2a9040, 0x9d21cbcd, - 0x72e3a0f3, 0x99d41bf0, 0x761670ce, 0x94ca6bb7, 0x7b080089, - 0x903fbb8a, 0x7ffdd0b4, 0x78bf0ea1, 0x977d659f, 0x7c4ade9c, - 0x9388b5a2, 0x7154aedb, 0x9e96c5e5, 0x75a17ee6, 0x9a6315d8, - 0x6b684e55, 0x84aa256b, 0x6f9d9e68, 0x805ff556, 0x6283ee2f, - 0x8d418511, 0x66763e12, 0x89b4552c, 0x5f118f49, 0xb0d3e477, - 0x5be45f74, 0xb426344a, 0x56fa2f33, 0xb938440d, 0x520fff0e, - 0xbdcd9430, 0x4cc6cfbd, 0xa304a483, 0x48331f80, 0xa7f174be, - 0x452d6fc7, 0xaaef04f9, 0x41d8bffa, 0xae1ad4c4, 0x37e20d71, - 0xd820664f, 0x3317dd4c, 0xdcd5b672, 0x3e09ad0b, 0xd1cbc635, - 0x3afc7d36, 0xd53e1608, 0x24354d85, 0xcbf726bb, 0x20c09db8, - 0xcf02f686, 0x2ddeedff, 0xc21c86c1, 0x292b3dc2, 0xc6e956fc, - 0x104c8c99, 0xff8ee7a7, 0x14b95ca4, 0xfb7b379a, 0x19a72ce3, - 0xf66547dd, 0x1d52fcde, 0xf29097e0, 0x039bcc6d, 0xec59a753, - 0x076e1c50, 0xe8ac776e, 0x0a706c17, 0xe5b20729, 0x0e85bc2a, - 0xe147d714}, - {0x00000000, 0x177b1443, 0x2ef62886, 0x398d3cc5, 0x5dec510c, - 0x4a97454f, 0x731a798a, 0x64616dc9, 0xbbd8a218, 0xaca3b65b, - 0x952e8a9e, 0x82559edd, 0xe634f314, 0xf14fe757, 0xc8c2db92, - 0xdfb9cfd1, 0xacc04271, 0xbbbb5632, 0x82366af7, 0x954d7eb4, - 0xf12c137d, 0xe657073e, 0xdfda3bfb, 0xc8a12fb8, 0x1718e069, - 0x0063f42a, 0x39eec8ef, 0x2e95dcac, 0x4af4b165, 0x5d8fa526, - 0x640299e3, 0x73798da0, 0x82f182a3, 0x958a96e0, 0xac07aa25, - 0xbb7cbe66, 0xdf1dd3af, 0xc866c7ec, 0xf1ebfb29, 0xe690ef6a, - 0x392920bb, 0x2e5234f8, 0x17df083d, 0x00a41c7e, 0x64c571b7, - 0x73be65f4, 0x4a335931, 0x5d484d72, 0x2e31c0d2, 0x394ad491, - 0x00c7e854, 0x17bcfc17, 0x73dd91de, 0x64a6859d, 0x5d2bb958, - 0x4a50ad1b, 0x95e962ca, 0x82927689, 0xbb1f4a4c, 0xac645e0f, - 0xc80533c6, 0xdf7e2785, 0xe6f31b40, 0xf1880f03, 0xde920307, - 0xc9e91744, 0xf0642b81, 0xe71f3fc2, 0x837e520b, 0x94054648, - 0xad887a8d, 0xbaf36ece, 0x654aa11f, 0x7231b55c, 0x4bbc8999, - 0x5cc79dda, 0x38a6f013, 0x2fdde450, 0x1650d895, 0x012bccd6, - 0x72524176, 0x65295535, 0x5ca469f0, 0x4bdf7db3, 0x2fbe107a, - 0x38c50439, 0x014838fc, 0x16332cbf, 0xc98ae36e, 0xdef1f72d, - 0xe77ccbe8, 0xf007dfab, 0x9466b262, 0x831da621, 0xba909ae4, - 0xadeb8ea7, 0x5c6381a4, 0x4b1895e7, 0x7295a922, 0x65eebd61, - 0x018fd0a8, 0x16f4c4eb, 0x2f79f82e, 0x3802ec6d, 0xe7bb23bc, - 0xf0c037ff, 0xc94d0b3a, 0xde361f79, 0xba5772b0, 0xad2c66f3, - 0x94a15a36, 0x83da4e75, 0xf0a3c3d5, 0xe7d8d796, 0xde55eb53, - 0xc92eff10, 0xad4f92d9, 0xba34869a, 0x83b9ba5f, 0x94c2ae1c, - 0x4b7b61cd, 0x5c00758e, 0x658d494b, 0x72f65d08, 0x169730c1, - 0x01ec2482, 0x38611847, 0x2f1a0c04, 0x6655004f, 0x712e140c, - 0x48a328c9, 0x5fd83c8a, 0x3bb95143, 0x2cc24500, 0x154f79c5, - 0x02346d86, 0xdd8da257, 0xcaf6b614, 0xf37b8ad1, 0xe4009e92, - 0x8061f35b, 0x971ae718, 0xae97dbdd, 0xb9eccf9e, 0xca95423e, - 0xddee567d, 0xe4636ab8, 0xf3187efb, 0x97791332, 0x80020771, - 0xb98f3bb4, 0xaef42ff7, 0x714de026, 0x6636f465, 0x5fbbc8a0, - 0x48c0dce3, 0x2ca1b12a, 0x3bdaa569, 0x025799ac, 0x152c8def, - 0xe4a482ec, 0xf3df96af, 0xca52aa6a, 0xdd29be29, 0xb948d3e0, - 0xae33c7a3, 0x97befb66, 0x80c5ef25, 0x5f7c20f4, 0x480734b7, - 0x718a0872, 0x66f11c31, 0x029071f8, 0x15eb65bb, 0x2c66597e, - 0x3b1d4d3d, 0x4864c09d, 0x5f1fd4de, 0x6692e81b, 0x71e9fc58, - 0x15889191, 0x02f385d2, 0x3b7eb917, 0x2c05ad54, 0xf3bc6285, - 0xe4c776c6, 0xdd4a4a03, 0xca315e40, 0xae503389, 0xb92b27ca, - 0x80a61b0f, 0x97dd0f4c, 0xb8c70348, 0xafbc170b, 0x96312bce, - 0x814a3f8d, 0xe52b5244, 0xf2504607, 0xcbdd7ac2, 0xdca66e81, - 0x031fa150, 0x1464b513, 0x2de989d6, 0x3a929d95, 0x5ef3f05c, - 0x4988e41f, 0x7005d8da, 0x677ecc99, 0x14074139, 0x037c557a, - 0x3af169bf, 0x2d8a7dfc, 0x49eb1035, 0x5e900476, 0x671d38b3, - 0x70662cf0, 0xafdfe321, 0xb8a4f762, 0x8129cba7, 0x9652dfe4, - 0xf233b22d, 0xe548a66e, 0xdcc59aab, 0xcbbe8ee8, 0x3a3681eb, - 0x2d4d95a8, 0x14c0a96d, 0x03bbbd2e, 0x67dad0e7, 0x70a1c4a4, - 0x492cf861, 0x5e57ec22, 0x81ee23f3, 0x969537b0, 0xaf180b75, - 0xb8631f36, 0xdc0272ff, 0xcb7966bc, 0xf2f45a79, 0xe58f4e3a, - 0x96f6c39a, 0x818dd7d9, 0xb800eb1c, 0xaf7bff5f, 0xcb1a9296, - 0xdc6186d5, 0xe5ecba10, 0xf297ae53, 0x2d2e6182, 0x3a5575c1, - 0x03d84904, 0x14a35d47, 0x70c2308e, 0x67b924cd, 0x5e341808, - 0x494f0c4b}}; - -local const z_word_t FAR crc_braid_big_table[][256] = { - {0x0000000000000000, 0x43147b1700000000, 0x8628f62e00000000, - 0xc53c8d3900000000, 0x0c51ec5d00000000, 0x4f45974a00000000, - 0x8a791a7300000000, 0xc96d616400000000, 0x18a2d8bb00000000, - 0x5bb6a3ac00000000, 0x9e8a2e9500000000, 0xdd9e558200000000, - 0x14f334e600000000, 0x57e74ff100000000, 0x92dbc2c800000000, - 0xd1cfb9df00000000, 0x7142c0ac00000000, 0x3256bbbb00000000, - 0xf76a368200000000, 0xb47e4d9500000000, 0x7d132cf100000000, - 0x3e0757e600000000, 0xfb3bdadf00000000, 0xb82fa1c800000000, - 0x69e0181700000000, 0x2af4630000000000, 0xefc8ee3900000000, - 0xacdc952e00000000, 0x65b1f44a00000000, 0x26a58f5d00000000, - 0xe399026400000000, 0xa08d797300000000, 0xa382f18200000000, - 0xe0968a9500000000, 0x25aa07ac00000000, 0x66be7cbb00000000, - 0xafd31ddf00000000, 0xecc766c800000000, 0x29fbebf100000000, - 0x6aef90e600000000, 0xbb20293900000000, 0xf834522e00000000, - 0x3d08df1700000000, 0x7e1ca40000000000, 0xb771c56400000000, - 0xf465be7300000000, 0x3159334a00000000, 0x724d485d00000000, - 0xd2c0312e00000000, 0x91d44a3900000000, 0x54e8c70000000000, - 0x17fcbc1700000000, 0xde91dd7300000000, 0x9d85a66400000000, - 0x58b92b5d00000000, 0x1bad504a00000000, 0xca62e99500000000, - 0x8976928200000000, 0x4c4a1fbb00000000, 0x0f5e64ac00000000, - 0xc63305c800000000, 0x85277edf00000000, 0x401bf3e600000000, - 0x030f88f100000000, 0x070392de00000000, 0x4417e9c900000000, - 0x812b64f000000000, 0xc23f1fe700000000, 0x0b527e8300000000, - 0x4846059400000000, 0x8d7a88ad00000000, 0xce6ef3ba00000000, - 0x1fa14a6500000000, 0x5cb5317200000000, 0x9989bc4b00000000, - 0xda9dc75c00000000, 0x13f0a63800000000, 0x50e4dd2f00000000, - 0x95d8501600000000, 0xd6cc2b0100000000, 0x7641527200000000, - 0x3555296500000000, 0xf069a45c00000000, 0xb37ddf4b00000000, - 0x7a10be2f00000000, 0x3904c53800000000, 0xfc38480100000000, - 0xbf2c331600000000, 0x6ee38ac900000000, 0x2df7f1de00000000, - 0xe8cb7ce700000000, 0xabdf07f000000000, 0x62b2669400000000, - 0x21a61d8300000000, 0xe49a90ba00000000, 0xa78eebad00000000, - 0xa481635c00000000, 0xe795184b00000000, 0x22a9957200000000, - 0x61bdee6500000000, 0xa8d08f0100000000, 0xebc4f41600000000, - 0x2ef8792f00000000, 0x6dec023800000000, 0xbc23bbe700000000, - 0xff37c0f000000000, 0x3a0b4dc900000000, 0x791f36de00000000, - 0xb07257ba00000000, 0xf3662cad00000000, 0x365aa19400000000, - 0x754eda8300000000, 0xd5c3a3f000000000, 0x96d7d8e700000000, - 0x53eb55de00000000, 0x10ff2ec900000000, 0xd9924fad00000000, - 0x9a8634ba00000000, 0x5fbab98300000000, 0x1caec29400000000, - 0xcd617b4b00000000, 0x8e75005c00000000, 0x4b498d6500000000, - 0x085df67200000000, 0xc130971600000000, 0x8224ec0100000000, - 0x4718613800000000, 0x040c1a2f00000000, 0x4f00556600000000, - 0x0c142e7100000000, 0xc928a34800000000, 0x8a3cd85f00000000, - 0x4351b93b00000000, 0x0045c22c00000000, 0xc5794f1500000000, - 0x866d340200000000, 0x57a28ddd00000000, 0x14b6f6ca00000000, - 0xd18a7bf300000000, 0x929e00e400000000, 0x5bf3618000000000, - 0x18e71a9700000000, 0xdddb97ae00000000, 0x9ecfecb900000000, - 0x3e4295ca00000000, 0x7d56eedd00000000, 0xb86a63e400000000, - 0xfb7e18f300000000, 0x3213799700000000, 0x7107028000000000, - 0xb43b8fb900000000, 0xf72ff4ae00000000, 0x26e04d7100000000, - 0x65f4366600000000, 0xa0c8bb5f00000000, 0xe3dcc04800000000, - 0x2ab1a12c00000000, 0x69a5da3b00000000, 0xac99570200000000, - 0xef8d2c1500000000, 0xec82a4e400000000, 0xaf96dff300000000, - 0x6aaa52ca00000000, 0x29be29dd00000000, 0xe0d348b900000000, - 0xa3c733ae00000000, 0x66fbbe9700000000, 0x25efc58000000000, - 0xf4207c5f00000000, 0xb734074800000000, 0x72088a7100000000, - 0x311cf16600000000, 0xf871900200000000, 0xbb65eb1500000000, - 0x7e59662c00000000, 0x3d4d1d3b00000000, 0x9dc0644800000000, - 0xded41f5f00000000, 0x1be8926600000000, 0x58fce97100000000, - 0x9191881500000000, 0xd285f30200000000, 0x17b97e3b00000000, - 0x54ad052c00000000, 0x8562bcf300000000, 0xc676c7e400000000, - 0x034a4add00000000, 0x405e31ca00000000, 0x893350ae00000000, - 0xca272bb900000000, 0x0f1ba68000000000, 0x4c0fdd9700000000, - 0x4803c7b800000000, 0x0b17bcaf00000000, 0xce2b319600000000, - 0x8d3f4a8100000000, 0x44522be500000000, 0x074650f200000000, - 0xc27addcb00000000, 0x816ea6dc00000000, 0x50a11f0300000000, - 0x13b5641400000000, 0xd689e92d00000000, 0x959d923a00000000, - 0x5cf0f35e00000000, 0x1fe4884900000000, 0xdad8057000000000, - 0x99cc7e6700000000, 0x3941071400000000, 0x7a557c0300000000, - 0xbf69f13a00000000, 0xfc7d8a2d00000000, 0x3510eb4900000000, - 0x7604905e00000000, 0xb3381d6700000000, 0xf02c667000000000, - 0x21e3dfaf00000000, 0x62f7a4b800000000, 0xa7cb298100000000, - 0xe4df529600000000, 0x2db233f200000000, 0x6ea648e500000000, - 0xab9ac5dc00000000, 0xe88ebecb00000000, 0xeb81363a00000000, - 0xa8954d2d00000000, 0x6da9c01400000000, 0x2ebdbb0300000000, - 0xe7d0da6700000000, 0xa4c4a17000000000, 0x61f82c4900000000, - 0x22ec575e00000000, 0xf323ee8100000000, 0xb037959600000000, - 0x750b18af00000000, 0x361f63b800000000, 0xff7202dc00000000, - 0xbc6679cb00000000, 0x795af4f200000000, 0x3a4e8fe500000000, - 0x9ac3f69600000000, 0xd9d78d8100000000, 0x1ceb00b800000000, - 0x5fff7baf00000000, 0x96921acb00000000, 0xd58661dc00000000, - 0x10baece500000000, 0x53ae97f200000000, 0x82612e2d00000000, - 0xc175553a00000000, 0x0449d80300000000, 0x475da31400000000, - 0x8e30c27000000000, 0xcd24b96700000000, 0x0818345e00000000, - 0x4b0c4f4900000000}, - {0x0000000000000000, 0x3e6bc2ef00000000, 0x3dd0f50400000000, - 0x03bb37eb00000000, 0x7aa0eb0900000000, 0x44cb29e600000000, - 0x47701e0d00000000, 0x791bdce200000000, 0xf440d71300000000, - 0xca2b15fc00000000, 0xc990221700000000, 0xf7fbe0f800000000, - 0x8ee03c1a00000000, 0xb08bfef500000000, 0xb330c91e00000000, - 0x8d5b0bf100000000, 0xe881ae2700000000, 0xd6ea6cc800000000, - 0xd5515b2300000000, 0xeb3a99cc00000000, 0x9221452e00000000, - 0xac4a87c100000000, 0xaff1b02a00000000, 0x919a72c500000000, - 0x1cc1793400000000, 0x22aabbdb00000000, 0x21118c3000000000, - 0x1f7a4edf00000000, 0x6661923d00000000, 0x580a50d200000000, - 0x5bb1673900000000, 0x65daa5d600000000, 0xd0035d4f00000000, - 0xee689fa000000000, 0xedd3a84b00000000, 0xd3b86aa400000000, - 0xaaa3b64600000000, 0x94c874a900000000, 0x9773434200000000, - 0xa91881ad00000000, 0x24438a5c00000000, 0x1a2848b300000000, - 0x19937f5800000000, 0x27f8bdb700000000, 0x5ee3615500000000, - 0x6088a3ba00000000, 0x6333945100000000, 0x5d5856be00000000, - 0x3882f36800000000, 0x06e9318700000000, 0x0552066c00000000, - 0x3b39c48300000000, 0x4222186100000000, 0x7c49da8e00000000, - 0x7ff2ed6500000000, 0x41992f8a00000000, 0xccc2247b00000000, - 0xf2a9e69400000000, 0xf112d17f00000000, 0xcf79139000000000, - 0xb662cf7200000000, 0x88090d9d00000000, 0x8bb23a7600000000, - 0xb5d9f89900000000, 0xa007ba9e00000000, 0x9e6c787100000000, - 0x9dd74f9a00000000, 0xa3bc8d7500000000, 0xdaa7519700000000, - 0xe4cc937800000000, 0xe777a49300000000, 0xd91c667c00000000, - 0x54476d8d00000000, 0x6a2caf6200000000, 0x6997988900000000, - 0x57fc5a6600000000, 0x2ee7868400000000, 0x108c446b00000000, - 0x1337738000000000, 0x2d5cb16f00000000, 0x488614b900000000, - 0x76edd65600000000, 0x7556e1bd00000000, 0x4b3d235200000000, - 0x3226ffb000000000, 0x0c4d3d5f00000000, 0x0ff60ab400000000, - 0x319dc85b00000000, 0xbcc6c3aa00000000, 0x82ad014500000000, - 0x811636ae00000000, 0xbf7df44100000000, 0xc66628a300000000, - 0xf80dea4c00000000, 0xfbb6dda700000000, 0xc5dd1f4800000000, - 0x7004e7d100000000, 0x4e6f253e00000000, 0x4dd412d500000000, - 0x73bfd03a00000000, 0x0aa40cd800000000, 0x34cfce3700000000, - 0x3774f9dc00000000, 0x091f3b3300000000, 0x844430c200000000, - 0xba2ff22d00000000, 0xb994c5c600000000, 0x87ff072900000000, - 0xfee4dbcb00000000, 0xc08f192400000000, 0xc3342ecf00000000, - 0xfd5fec2000000000, 0x988549f600000000, 0xa6ee8b1900000000, - 0xa555bcf200000000, 0x9b3e7e1d00000000, 0xe225a2ff00000000, - 0xdc4e601000000000, 0xdff557fb00000000, 0xe19e951400000000, - 0x6cc59ee500000000, 0x52ae5c0a00000000, 0x51156be100000000, - 0x6f7ea90e00000000, 0x166575ec00000000, 0x280eb70300000000, - 0x2bb580e800000000, 0x15de420700000000, 0x010905e600000000, - 0x3f62c70900000000, 0x3cd9f0e200000000, 0x02b2320d00000000, - 0x7ba9eeef00000000, 0x45c22c0000000000, 0x46791beb00000000, - 0x7812d90400000000, 0xf549d2f500000000, 0xcb22101a00000000, - 0xc89927f100000000, 0xf6f2e51e00000000, 0x8fe939fc00000000, - 0xb182fb1300000000, 0xb239ccf800000000, 0x8c520e1700000000, - 0xe988abc100000000, 0xd7e3692e00000000, 0xd4585ec500000000, - 0xea339c2a00000000, 0x932840c800000000, 0xad43822700000000, - 0xaef8b5cc00000000, 0x9093772300000000, 0x1dc87cd200000000, - 0x23a3be3d00000000, 0x201889d600000000, 0x1e734b3900000000, - 0x676897db00000000, 0x5903553400000000, 0x5ab862df00000000, - 0x64d3a03000000000, 0xd10a58a900000000, 0xef619a4600000000, - 0xecdaadad00000000, 0xd2b16f4200000000, 0xabaab3a000000000, - 0x95c1714f00000000, 0x967a46a400000000, 0xa811844b00000000, - 0x254a8fba00000000, 0x1b214d5500000000, 0x189a7abe00000000, - 0x26f1b85100000000, 0x5fea64b300000000, 0x6181a65c00000000, - 0x623a91b700000000, 0x5c51535800000000, 0x398bf68e00000000, - 0x07e0346100000000, 0x045b038a00000000, 0x3a30c16500000000, - 0x432b1d8700000000, 0x7d40df6800000000, 0x7efbe88300000000, - 0x40902a6c00000000, 0xcdcb219d00000000, 0xf3a0e37200000000, - 0xf01bd49900000000, 0xce70167600000000, 0xb76bca9400000000, - 0x8900087b00000000, 0x8abb3f9000000000, 0xb4d0fd7f00000000, - 0xa10ebf7800000000, 0x9f657d9700000000, 0x9cde4a7c00000000, - 0xa2b5889300000000, 0xdbae547100000000, 0xe5c5969e00000000, - 0xe67ea17500000000, 0xd815639a00000000, 0x554e686b00000000, - 0x6b25aa8400000000, 0x689e9d6f00000000, 0x56f55f8000000000, - 0x2fee836200000000, 0x1185418d00000000, 0x123e766600000000, - 0x2c55b48900000000, 0x498f115f00000000, 0x77e4d3b000000000, - 0x745fe45b00000000, 0x4a3426b400000000, 0x332ffa5600000000, - 0x0d4438b900000000, 0x0eff0f5200000000, 0x3094cdbd00000000, - 0xbdcfc64c00000000, 0x83a404a300000000, 0x801f334800000000, - 0xbe74f1a700000000, 0xc76f2d4500000000, 0xf904efaa00000000, - 0xfabfd84100000000, 0xc4d41aae00000000, 0x710de23700000000, - 0x4f6620d800000000, 0x4cdd173300000000, 0x72b6d5dc00000000, - 0x0bad093e00000000, 0x35c6cbd100000000, 0x367dfc3a00000000, - 0x08163ed500000000, 0x854d352400000000, 0xbb26f7cb00000000, - 0xb89dc02000000000, 0x86f602cf00000000, 0xffedde2d00000000, - 0xc1861cc200000000, 0xc23d2b2900000000, 0xfc56e9c600000000, - 0x998c4c1000000000, 0xa7e78eff00000000, 0xa45cb91400000000, - 0x9a377bfb00000000, 0xe32ca71900000000, 0xdd4765f600000000, - 0xdefc521d00000000, 0xe09790f200000000, 0x6dcc9b0300000000, - 0x53a759ec00000000, 0x501c6e0700000000, 0x6e77ace800000000, - 0x176c700a00000000, 0x2907b2e500000000, 0x2abc850e00000000, - 0x14d747e100000000}, - {0x0000000000000000, 0xc0df8ec100000000, 0xc1b96c5800000000, - 0x0166e29900000000, 0x8273d9b000000000, 0x42ac577100000000, - 0x43cab5e800000000, 0x83153b2900000000, 0x45e1c3ba00000000, - 0x853e4d7b00000000, 0x8458afe200000000, 0x4487212300000000, - 0xc7921a0a00000000, 0x074d94cb00000000, 0x062b765200000000, - 0xc6f4f89300000000, 0xcbc4f6ae00000000, 0x0b1b786f00000000, - 0x0a7d9af600000000, 0xcaa2143700000000, 0x49b72f1e00000000, - 0x8968a1df00000000, 0x880e434600000000, 0x48d1cd8700000000, - 0x8e25351400000000, 0x4efabbd500000000, 0x4f9c594c00000000, - 0x8f43d78d00000000, 0x0c56eca400000000, 0xcc89626500000000, - 0xcdef80fc00000000, 0x0d300e3d00000000, 0xd78f9c8600000000, - 0x1750124700000000, 0x1636f0de00000000, 0xd6e97e1f00000000, - 0x55fc453600000000, 0x9523cbf700000000, 0x9445296e00000000, - 0x549aa7af00000000, 0x926e5f3c00000000, 0x52b1d1fd00000000, - 0x53d7336400000000, 0x9308bda500000000, 0x101d868c00000000, - 0xd0c2084d00000000, 0xd1a4ead400000000, 0x117b641500000000, - 0x1c4b6a2800000000, 0xdc94e4e900000000, 0xddf2067000000000, - 0x1d2d88b100000000, 0x9e38b39800000000, 0x5ee73d5900000000, - 0x5f81dfc000000000, 0x9f5e510100000000, 0x59aaa99200000000, - 0x9975275300000000, 0x9813c5ca00000000, 0x58cc4b0b00000000, - 0xdbd9702200000000, 0x1b06fee300000000, 0x1a601c7a00000000, - 0xdabf92bb00000000, 0xef1948d600000000, 0x2fc6c61700000000, - 0x2ea0248e00000000, 0xee7faa4f00000000, 0x6d6a916600000000, - 0xadb51fa700000000, 0xacd3fd3e00000000, 0x6c0c73ff00000000, - 0xaaf88b6c00000000, 0x6a2705ad00000000, 0x6b41e73400000000, - 0xab9e69f500000000, 0x288b52dc00000000, 0xe854dc1d00000000, - 0xe9323e8400000000, 0x29edb04500000000, 0x24ddbe7800000000, - 0xe40230b900000000, 0xe564d22000000000, 0x25bb5ce100000000, - 0xa6ae67c800000000, 0x6671e90900000000, 0x67170b9000000000, - 0xa7c8855100000000, 0x613c7dc200000000, 0xa1e3f30300000000, - 0xa085119a00000000, 0x605a9f5b00000000, 0xe34fa47200000000, - 0x23902ab300000000, 0x22f6c82a00000000, 0xe22946eb00000000, - 0x3896d45000000000, 0xf8495a9100000000, 0xf92fb80800000000, - 0x39f036c900000000, 0xbae50de000000000, 0x7a3a832100000000, - 0x7b5c61b800000000, 0xbb83ef7900000000, 0x7d7717ea00000000, - 0xbda8992b00000000, 0xbcce7bb200000000, 0x7c11f57300000000, - 0xff04ce5a00000000, 0x3fdb409b00000000, 0x3ebda20200000000, - 0xfe622cc300000000, 0xf35222fe00000000, 0x338dac3f00000000, - 0x32eb4ea600000000, 0xf234c06700000000, 0x7121fb4e00000000, - 0xb1fe758f00000000, 0xb098971600000000, 0x704719d700000000, - 0xb6b3e14400000000, 0x766c6f8500000000, 0x770a8d1c00000000, - 0xb7d503dd00000000, 0x34c038f400000000, 0xf41fb63500000000, - 0xf57954ac00000000, 0x35a6da6d00000000, 0x9f35e17700000000, - 0x5fea6fb600000000, 0x5e8c8d2f00000000, 0x9e5303ee00000000, - 0x1d4638c700000000, 0xdd99b60600000000, 0xdcff549f00000000, - 0x1c20da5e00000000, 0xdad422cd00000000, 0x1a0bac0c00000000, - 0x1b6d4e9500000000, 0xdbb2c05400000000, 0x58a7fb7d00000000, - 0x987875bc00000000, 0x991e972500000000, 0x59c119e400000000, - 0x54f117d900000000, 0x942e991800000000, 0x95487b8100000000, - 0x5597f54000000000, 0xd682ce6900000000, 0x165d40a800000000, - 0x173ba23100000000, 0xd7e42cf000000000, 0x1110d46300000000, - 0xd1cf5aa200000000, 0xd0a9b83b00000000, 0x107636fa00000000, - 0x93630dd300000000, 0x53bc831200000000, 0x52da618b00000000, - 0x9205ef4a00000000, 0x48ba7df100000000, 0x8865f33000000000, - 0x890311a900000000, 0x49dc9f6800000000, 0xcac9a44100000000, - 0x0a162a8000000000, 0x0b70c81900000000, 0xcbaf46d800000000, - 0x0d5bbe4b00000000, 0xcd84308a00000000, 0xcce2d21300000000, - 0x0c3d5cd200000000, 0x8f2867fb00000000, 0x4ff7e93a00000000, - 0x4e910ba300000000, 0x8e4e856200000000, 0x837e8b5f00000000, - 0x43a1059e00000000, 0x42c7e70700000000, 0x821869c600000000, - 0x010d52ef00000000, 0xc1d2dc2e00000000, 0xc0b43eb700000000, - 0x006bb07600000000, 0xc69f48e500000000, 0x0640c62400000000, - 0x072624bd00000000, 0xc7f9aa7c00000000, 0x44ec915500000000, - 0x84331f9400000000, 0x8555fd0d00000000, 0x458a73cc00000000, - 0x702ca9a100000000, 0xb0f3276000000000, 0xb195c5f900000000, - 0x714a4b3800000000, 0xf25f701100000000, 0x3280fed000000000, - 0x33e61c4900000000, 0xf339928800000000, 0x35cd6a1b00000000, - 0xf512e4da00000000, 0xf474064300000000, 0x34ab888200000000, - 0xb7beb3ab00000000, 0x77613d6a00000000, 0x7607dff300000000, - 0xb6d8513200000000, 0xbbe85f0f00000000, 0x7b37d1ce00000000, - 0x7a51335700000000, 0xba8ebd9600000000, 0x399b86bf00000000, - 0xf944087e00000000, 0xf822eae700000000, 0x38fd642600000000, - 0xfe099cb500000000, 0x3ed6127400000000, 0x3fb0f0ed00000000, - 0xff6f7e2c00000000, 0x7c7a450500000000, 0xbca5cbc400000000, - 0xbdc3295d00000000, 0x7d1ca79c00000000, 0xa7a3352700000000, - 0x677cbbe600000000, 0x661a597f00000000, 0xa6c5d7be00000000, - 0x25d0ec9700000000, 0xe50f625600000000, 0xe46980cf00000000, - 0x24b60e0e00000000, 0xe242f69d00000000, 0x229d785c00000000, - 0x23fb9ac500000000, 0xe324140400000000, 0x60312f2d00000000, - 0xa0eea1ec00000000, 0xa188437500000000, 0x6157cdb400000000, - 0x6c67c38900000000, 0xacb84d4800000000, 0xaddeafd100000000, - 0x6d01211000000000, 0xee141a3900000000, 0x2ecb94f800000000, - 0x2fad766100000000, 0xef72f8a000000000, 0x2986003300000000, - 0xe9598ef200000000, 0xe83f6c6b00000000, 0x28e0e2aa00000000, - 0xabf5d98300000000, 0x6b2a574200000000, 0x6a4cb5db00000000, - 0xaa933b1a00000000}, - {0x0000000000000000, 0x6f4ca59b00000000, 0x9f9e3bec00000000, - 0xf0d29e7700000000, 0x7f3b060300000000, 0x1077a39800000000, - 0xe0a53def00000000, 0x8fe9987400000000, 0xfe760c0600000000, - 0x913aa99d00000000, 0x61e837ea00000000, 0x0ea4927100000000, - 0x814d0a0500000000, 0xee01af9e00000000, 0x1ed331e900000000, - 0x719f947200000000, 0xfced180c00000000, 0x93a1bd9700000000, - 0x637323e000000000, 0x0c3f867b00000000, 0x83d61e0f00000000, - 0xec9abb9400000000, 0x1c4825e300000000, 0x7304807800000000, - 0x029b140a00000000, 0x6dd7b19100000000, 0x9d052fe600000000, - 0xf2498a7d00000000, 0x7da0120900000000, 0x12ecb79200000000, - 0xe23e29e500000000, 0x8d728c7e00000000, 0xf8db311800000000, - 0x9797948300000000, 0x67450af400000000, 0x0809af6f00000000, - 0x87e0371b00000000, 0xe8ac928000000000, 0x187e0cf700000000, - 0x7732a96c00000000, 0x06ad3d1e00000000, 0x69e1988500000000, - 0x993306f200000000, 0xf67fa36900000000, 0x79963b1d00000000, - 0x16da9e8600000000, 0xe60800f100000000, 0x8944a56a00000000, - 0x0436291400000000, 0x6b7a8c8f00000000, 0x9ba812f800000000, - 0xf4e4b76300000000, 0x7b0d2f1700000000, 0x14418a8c00000000, - 0xe49314fb00000000, 0x8bdfb16000000000, 0xfa40251200000000, - 0x950c808900000000, 0x65de1efe00000000, 0x0a92bb6500000000, - 0x857b231100000000, 0xea37868a00000000, 0x1ae518fd00000000, - 0x75a9bd6600000000, 0xf0b7633000000000, 0x9ffbc6ab00000000, - 0x6f2958dc00000000, 0x0065fd4700000000, 0x8f8c653300000000, - 0xe0c0c0a800000000, 0x10125edf00000000, 0x7f5efb4400000000, - 0x0ec16f3600000000, 0x618dcaad00000000, 0x915f54da00000000, - 0xfe13f14100000000, 0x71fa693500000000, 0x1eb6ccae00000000, - 0xee6452d900000000, 0x8128f74200000000, 0x0c5a7b3c00000000, - 0x6316dea700000000, 0x93c440d000000000, 0xfc88e54b00000000, - 0x73617d3f00000000, 0x1c2dd8a400000000, 0xecff46d300000000, - 0x83b3e34800000000, 0xf22c773a00000000, 0x9d60d2a100000000, - 0x6db24cd600000000, 0x02fee94d00000000, 0x8d17713900000000, - 0xe25bd4a200000000, 0x12894ad500000000, 0x7dc5ef4e00000000, - 0x086c522800000000, 0x6720f7b300000000, 0x97f269c400000000, - 0xf8becc5f00000000, 0x7757542b00000000, 0x181bf1b000000000, - 0xe8c96fc700000000, 0x8785ca5c00000000, 0xf61a5e2e00000000, - 0x9956fbb500000000, 0x698465c200000000, 0x06c8c05900000000, - 0x8921582d00000000, 0xe66dfdb600000000, 0x16bf63c100000000, - 0x79f3c65a00000000, 0xf4814a2400000000, 0x9bcdefbf00000000, - 0x6b1f71c800000000, 0x0453d45300000000, 0x8bba4c2700000000, - 0xe4f6e9bc00000000, 0x142477cb00000000, 0x7b68d25000000000, - 0x0af7462200000000, 0x65bbe3b900000000, 0x95697dce00000000, - 0xfa25d85500000000, 0x75cc402100000000, 0x1a80e5ba00000000, - 0xea527bcd00000000, 0x851ede5600000000, 0xe06fc76000000000, - 0x8f2362fb00000000, 0x7ff1fc8c00000000, 0x10bd591700000000, - 0x9f54c16300000000, 0xf01864f800000000, 0x00cafa8f00000000, - 0x6f865f1400000000, 0x1e19cb6600000000, 0x71556efd00000000, - 0x8187f08a00000000, 0xeecb551100000000, 0x6122cd6500000000, - 0x0e6e68fe00000000, 0xfebcf68900000000, 0x91f0531200000000, - 0x1c82df6c00000000, 0x73ce7af700000000, 0x831ce48000000000, - 0xec50411b00000000, 0x63b9d96f00000000, 0x0cf57cf400000000, - 0xfc27e28300000000, 0x936b471800000000, 0xe2f4d36a00000000, - 0x8db876f100000000, 0x7d6ae88600000000, 0x12264d1d00000000, - 0x9dcfd56900000000, 0xf28370f200000000, 0x0251ee8500000000, - 0x6d1d4b1e00000000, 0x18b4f67800000000, 0x77f853e300000000, - 0x872acd9400000000, 0xe866680f00000000, 0x678ff07b00000000, - 0x08c355e000000000, 0xf811cb9700000000, 0x975d6e0c00000000, - 0xe6c2fa7e00000000, 0x898e5fe500000000, 0x795cc19200000000, - 0x1610640900000000, 0x99f9fc7d00000000, 0xf6b559e600000000, - 0x0667c79100000000, 0x692b620a00000000, 0xe459ee7400000000, - 0x8b154bef00000000, 0x7bc7d59800000000, 0x148b700300000000, - 0x9b62e87700000000, 0xf42e4dec00000000, 0x04fcd39b00000000, - 0x6bb0760000000000, 0x1a2fe27200000000, 0x756347e900000000, - 0x85b1d99e00000000, 0xeafd7c0500000000, 0x6514e47100000000, - 0x0a5841ea00000000, 0xfa8adf9d00000000, 0x95c67a0600000000, - 0x10d8a45000000000, 0x7f9401cb00000000, 0x8f469fbc00000000, - 0xe00a3a2700000000, 0x6fe3a25300000000, 0x00af07c800000000, - 0xf07d99bf00000000, 0x9f313c2400000000, 0xeeaea85600000000, - 0x81e20dcd00000000, 0x713093ba00000000, 0x1e7c362100000000, - 0x9195ae5500000000, 0xfed90bce00000000, 0x0e0b95b900000000, - 0x6147302200000000, 0xec35bc5c00000000, 0x837919c700000000, - 0x73ab87b000000000, 0x1ce7222b00000000, 0x930eba5f00000000, - 0xfc421fc400000000, 0x0c9081b300000000, 0x63dc242800000000, - 0x1243b05a00000000, 0x7d0f15c100000000, 0x8ddd8bb600000000, - 0xe2912e2d00000000, 0x6d78b65900000000, 0x023413c200000000, - 0xf2e68db500000000, 0x9daa282e00000000, 0xe803954800000000, - 0x874f30d300000000, 0x779daea400000000, 0x18d10b3f00000000, - 0x9738934b00000000, 0xf87436d000000000, 0x08a6a8a700000000, - 0x67ea0d3c00000000, 0x1675994e00000000, 0x79393cd500000000, - 0x89eba2a200000000, 0xe6a7073900000000, 0x694e9f4d00000000, - 0x06023ad600000000, 0xf6d0a4a100000000, 0x999c013a00000000, - 0x14ee8d4400000000, 0x7ba228df00000000, 0x8b70b6a800000000, - 0xe43c133300000000, 0x6bd58b4700000000, 0x04992edc00000000, - 0xf44bb0ab00000000, 0x9b07153000000000, 0xea98814200000000, - 0x85d424d900000000, 0x7506baae00000000, 0x1a4a1f3500000000, - 0x95a3874100000000, 0xfaef22da00000000, 0x0a3dbcad00000000, - 0x6571193600000000}, - {0x0000000000000000, 0x85d996dd00000000, 0x4bb55c6000000000, - 0xce6ccabd00000000, 0x966ab9c000000000, 0x13b32f1d00000000, - 0xdddfe5a000000000, 0x5806737d00000000, 0x6dd3035a00000000, - 0xe80a958700000000, 0x26665f3a00000000, 0xa3bfc9e700000000, - 0xfbb9ba9a00000000, 0x7e602c4700000000, 0xb00ce6fa00000000, - 0x35d5702700000000, 0xdaa607b400000000, 0x5f7f916900000000, - 0x91135bd400000000, 0x14cacd0900000000, 0x4cccbe7400000000, - 0xc91528a900000000, 0x0779e21400000000, 0x82a074c900000000, - 0xb77504ee00000000, 0x32ac923300000000, 0xfcc0588e00000000, - 0x7919ce5300000000, 0x211fbd2e00000000, 0xa4c62bf300000000, - 0x6aaae14e00000000, 0xef73779300000000, 0xf54b7eb300000000, - 0x7092e86e00000000, 0xbefe22d300000000, 0x3b27b40e00000000, - 0x6321c77300000000, 0xe6f851ae00000000, 0x28949b1300000000, - 0xad4d0dce00000000, 0x98987de900000000, 0x1d41eb3400000000, - 0xd32d218900000000, 0x56f4b75400000000, 0x0ef2c42900000000, - 0x8b2b52f400000000, 0x4547984900000000, 0xc09e0e9400000000, - 0x2fed790700000000, 0xaa34efda00000000, 0x6458256700000000, - 0xe181b3ba00000000, 0xb987c0c700000000, 0x3c5e561a00000000, - 0xf2329ca700000000, 0x77eb0a7a00000000, 0x423e7a5d00000000, - 0xc7e7ec8000000000, 0x098b263d00000000, 0x8c52b0e000000000, - 0xd454c39d00000000, 0x518d554000000000, 0x9fe19ffd00000000, - 0x1a38092000000000, 0xab918dbd00000000, 0x2e481b6000000000, - 0xe024d1dd00000000, 0x65fd470000000000, 0x3dfb347d00000000, - 0xb822a2a000000000, 0x764e681d00000000, 0xf397fec000000000, - 0xc6428ee700000000, 0x439b183a00000000, 0x8df7d28700000000, - 0x082e445a00000000, 0x5028372700000000, 0xd5f1a1fa00000000, - 0x1b9d6b4700000000, 0x9e44fd9a00000000, 0x71378a0900000000, - 0xf4ee1cd400000000, 0x3a82d66900000000, 0xbf5b40b400000000, - 0xe75d33c900000000, 0x6284a51400000000, 0xace86fa900000000, - 0x2931f97400000000, 0x1ce4895300000000, 0x993d1f8e00000000, - 0x5751d53300000000, 0xd28843ee00000000, 0x8a8e309300000000, - 0x0f57a64e00000000, 0xc13b6cf300000000, 0x44e2fa2e00000000, - 0x5edaf30e00000000, 0xdb0365d300000000, 0x156faf6e00000000, - 0x90b639b300000000, 0xc8b04ace00000000, 0x4d69dc1300000000, - 0x830516ae00000000, 0x06dc807300000000, 0x3309f05400000000, - 0xb6d0668900000000, 0x78bcac3400000000, 0xfd653ae900000000, - 0xa563499400000000, 0x20badf4900000000, 0xeed615f400000000, - 0x6b0f832900000000, 0x847cf4ba00000000, 0x01a5626700000000, - 0xcfc9a8da00000000, 0x4a103e0700000000, 0x12164d7a00000000, - 0x97cfdba700000000, 0x59a3111a00000000, 0xdc7a87c700000000, - 0xe9aff7e000000000, 0x6c76613d00000000, 0xa21aab8000000000, - 0x27c33d5d00000000, 0x7fc54e2000000000, 0xfa1cd8fd00000000, - 0x3470124000000000, 0xb1a9849d00000000, 0x17256aa000000000, - 0x92fcfc7d00000000, 0x5c9036c000000000, 0xd949a01d00000000, - 0x814fd36000000000, 0x049645bd00000000, 0xcafa8f0000000000, - 0x4f2319dd00000000, 0x7af669fa00000000, 0xff2fff2700000000, - 0x3143359a00000000, 0xb49aa34700000000, 0xec9cd03a00000000, - 0x694546e700000000, 0xa7298c5a00000000, 0x22f01a8700000000, - 0xcd836d1400000000, 0x485afbc900000000, 0x8636317400000000, - 0x03efa7a900000000, 0x5be9d4d400000000, 0xde30420900000000, - 0x105c88b400000000, 0x95851e6900000000, 0xa0506e4e00000000, - 0x2589f89300000000, 0xebe5322e00000000, 0x6e3ca4f300000000, - 0x363ad78e00000000, 0xb3e3415300000000, 0x7d8f8bee00000000, - 0xf8561d3300000000, 0xe26e141300000000, 0x67b782ce00000000, - 0xa9db487300000000, 0x2c02deae00000000, 0x7404add300000000, - 0xf1dd3b0e00000000, 0x3fb1f1b300000000, 0xba68676e00000000, - 0x8fbd174900000000, 0x0a64819400000000, 0xc4084b2900000000, - 0x41d1ddf400000000, 0x19d7ae8900000000, 0x9c0e385400000000, - 0x5262f2e900000000, 0xd7bb643400000000, 0x38c813a700000000, - 0xbd11857a00000000, 0x737d4fc700000000, 0xf6a4d91a00000000, - 0xaea2aa6700000000, 0x2b7b3cba00000000, 0xe517f60700000000, - 0x60ce60da00000000, 0x551b10fd00000000, 0xd0c2862000000000, - 0x1eae4c9d00000000, 0x9b77da4000000000, 0xc371a93d00000000, - 0x46a83fe000000000, 0x88c4f55d00000000, 0x0d1d638000000000, - 0xbcb4e71d00000000, 0x396d71c000000000, 0xf701bb7d00000000, - 0x72d82da000000000, 0x2ade5edd00000000, 0xaf07c80000000000, - 0x616b02bd00000000, 0xe4b2946000000000, 0xd167e44700000000, - 0x54be729a00000000, 0x9ad2b82700000000, 0x1f0b2efa00000000, - 0x470d5d8700000000, 0xc2d4cb5a00000000, 0x0cb801e700000000, - 0x8961973a00000000, 0x6612e0a900000000, 0xe3cb767400000000, - 0x2da7bcc900000000, 0xa87e2a1400000000, 0xf078596900000000, - 0x75a1cfb400000000, 0xbbcd050900000000, 0x3e1493d400000000, - 0x0bc1e3f300000000, 0x8e18752e00000000, 0x4074bf9300000000, - 0xc5ad294e00000000, 0x9dab5a3300000000, 0x1872ccee00000000, - 0xd61e065300000000, 0x53c7908e00000000, 0x49ff99ae00000000, - 0xcc260f7300000000, 0x024ac5ce00000000, 0x8793531300000000, - 0xdf95206e00000000, 0x5a4cb6b300000000, 0x94207c0e00000000, - 0x11f9ead300000000, 0x242c9af400000000, 0xa1f50c2900000000, - 0x6f99c69400000000, 0xea40504900000000, 0xb246233400000000, - 0x379fb5e900000000, 0xf9f37f5400000000, 0x7c2ae98900000000, - 0x93599e1a00000000, 0x168008c700000000, 0xd8ecc27a00000000, - 0x5d3554a700000000, 0x053327da00000000, 0x80eab10700000000, - 0x4e867bba00000000, 0xcb5fed6700000000, 0xfe8a9d4000000000, - 0x7b530b9d00000000, 0xb53fc12000000000, 0x30e657fd00000000, - 0x68e0248000000000, 0xed39b25d00000000, 0x235578e000000000, - 0xa68cee3d00000000}, - {0x0000000000000000, 0x76e10f9d00000000, 0xadc46ee100000000, - 0xdb25617c00000000, 0x1b8fac1900000000, 0x6d6ea38400000000, - 0xb64bc2f800000000, 0xc0aacd6500000000, 0x361e593300000000, - 0x40ff56ae00000000, 0x9bda37d200000000, 0xed3b384f00000000, - 0x2d91f52a00000000, 0x5b70fab700000000, 0x80559bcb00000000, - 0xf6b4945600000000, 0x6c3cb26600000000, 0x1addbdfb00000000, - 0xc1f8dc8700000000, 0xb719d31a00000000, 0x77b31e7f00000000, - 0x015211e200000000, 0xda77709e00000000, 0xac967f0300000000, - 0x5a22eb5500000000, 0x2cc3e4c800000000, 0xf7e685b400000000, - 0x81078a2900000000, 0x41ad474c00000000, 0x374c48d100000000, - 0xec6929ad00000000, 0x9a88263000000000, 0xd87864cd00000000, - 0xae996b5000000000, 0x75bc0a2c00000000, 0x035d05b100000000, - 0xc3f7c8d400000000, 0xb516c74900000000, 0x6e33a63500000000, - 0x18d2a9a800000000, 0xee663dfe00000000, 0x9887326300000000, - 0x43a2531f00000000, 0x35435c8200000000, 0xf5e991e700000000, - 0x83089e7a00000000, 0x582dff0600000000, 0x2eccf09b00000000, - 0xb444d6ab00000000, 0xc2a5d93600000000, 0x1980b84a00000000, - 0x6f61b7d700000000, 0xafcb7ab200000000, 0xd92a752f00000000, - 0x020f145300000000, 0x74ee1bce00000000, 0x825a8f9800000000, - 0xf4bb800500000000, 0x2f9ee17900000000, 0x597feee400000000, - 0x99d5238100000000, 0xef342c1c00000000, 0x34114d6000000000, - 0x42f042fd00000000, 0xf1f7b94100000000, 0x8716b6dc00000000, - 0x5c33d7a000000000, 0x2ad2d83d00000000, 0xea78155800000000, - 0x9c991ac500000000, 0x47bc7bb900000000, 0x315d742400000000, - 0xc7e9e07200000000, 0xb108efef00000000, 0x6a2d8e9300000000, - 0x1ccc810e00000000, 0xdc664c6b00000000, 0xaa8743f600000000, - 0x71a2228a00000000, 0x07432d1700000000, 0x9dcb0b2700000000, - 0xeb2a04ba00000000, 0x300f65c600000000, 0x46ee6a5b00000000, - 0x8644a73e00000000, 0xf0a5a8a300000000, 0x2b80c9df00000000, - 0x5d61c64200000000, 0xabd5521400000000, 0xdd345d8900000000, - 0x06113cf500000000, 0x70f0336800000000, 0xb05afe0d00000000, - 0xc6bbf19000000000, 0x1d9e90ec00000000, 0x6b7f9f7100000000, - 0x298fdd8c00000000, 0x5f6ed21100000000, 0x844bb36d00000000, - 0xf2aabcf000000000, 0x3200719500000000, 0x44e17e0800000000, - 0x9fc41f7400000000, 0xe92510e900000000, 0x1f9184bf00000000, - 0x69708b2200000000, 0xb255ea5e00000000, 0xc4b4e5c300000000, - 0x041e28a600000000, 0x72ff273b00000000, 0xa9da464700000000, - 0xdf3b49da00000000, 0x45b36fea00000000, 0x3352607700000000, - 0xe877010b00000000, 0x9e960e9600000000, 0x5e3cc3f300000000, - 0x28ddcc6e00000000, 0xf3f8ad1200000000, 0x8519a28f00000000, - 0x73ad36d900000000, 0x054c394400000000, 0xde69583800000000, - 0xa88857a500000000, 0x68229ac000000000, 0x1ec3955d00000000, - 0xc5e6f42100000000, 0xb307fbbc00000000, 0xe2ef738300000000, - 0x940e7c1e00000000, 0x4f2b1d6200000000, 0x39ca12ff00000000, - 0xf960df9a00000000, 0x8f81d00700000000, 0x54a4b17b00000000, - 0x2245bee600000000, 0xd4f12ab000000000, 0xa210252d00000000, - 0x7935445100000000, 0x0fd44bcc00000000, 0xcf7e86a900000000, - 0xb99f893400000000, 0x62bae84800000000, 0x145be7d500000000, - 0x8ed3c1e500000000, 0xf832ce7800000000, 0x2317af0400000000, - 0x55f6a09900000000, 0x955c6dfc00000000, 0xe3bd626100000000, - 0x3898031d00000000, 0x4e790c8000000000, 0xb8cd98d600000000, - 0xce2c974b00000000, 0x1509f63700000000, 0x63e8f9aa00000000, - 0xa34234cf00000000, 0xd5a33b5200000000, 0x0e865a2e00000000, - 0x786755b300000000, 0x3a97174e00000000, 0x4c7618d300000000, - 0x975379af00000000, 0xe1b2763200000000, 0x2118bb5700000000, - 0x57f9b4ca00000000, 0x8cdcd5b600000000, 0xfa3dda2b00000000, - 0x0c894e7d00000000, 0x7a6841e000000000, 0xa14d209c00000000, - 0xd7ac2f0100000000, 0x1706e26400000000, 0x61e7edf900000000, - 0xbac28c8500000000, 0xcc23831800000000, 0x56aba52800000000, - 0x204aaab500000000, 0xfb6fcbc900000000, 0x8d8ec45400000000, - 0x4d24093100000000, 0x3bc506ac00000000, 0xe0e067d000000000, - 0x9601684d00000000, 0x60b5fc1b00000000, 0x1654f38600000000, - 0xcd7192fa00000000, 0xbb909d6700000000, 0x7b3a500200000000, - 0x0ddb5f9f00000000, 0xd6fe3ee300000000, 0xa01f317e00000000, - 0x1318cac200000000, 0x65f9c55f00000000, 0xbedca42300000000, - 0xc83dabbe00000000, 0x089766db00000000, 0x7e76694600000000, - 0xa553083a00000000, 0xd3b207a700000000, 0x250693f100000000, - 0x53e79c6c00000000, 0x88c2fd1000000000, 0xfe23f28d00000000, - 0x3e893fe800000000, 0x4868307500000000, 0x934d510900000000, - 0xe5ac5e9400000000, 0x7f2478a400000000, 0x09c5773900000000, - 0xd2e0164500000000, 0xa40119d800000000, 0x64abd4bd00000000, - 0x124adb2000000000, 0xc96fba5c00000000, 0xbf8eb5c100000000, - 0x493a219700000000, 0x3fdb2e0a00000000, 0xe4fe4f7600000000, - 0x921f40eb00000000, 0x52b58d8e00000000, 0x2454821300000000, - 0xff71e36f00000000, 0x8990ecf200000000, 0xcb60ae0f00000000, - 0xbd81a19200000000, 0x66a4c0ee00000000, 0x1045cf7300000000, - 0xd0ef021600000000, 0xa60e0d8b00000000, 0x7d2b6cf700000000, - 0x0bca636a00000000, 0xfd7ef73c00000000, 0x8b9ff8a100000000, - 0x50ba99dd00000000, 0x265b964000000000, 0xe6f15b2500000000, - 0x901054b800000000, 0x4b3535c400000000, 0x3dd43a5900000000, - 0xa75c1c6900000000, 0xd1bd13f400000000, 0x0a98728800000000, - 0x7c797d1500000000, 0xbcd3b07000000000, 0xca32bfed00000000, - 0x1117de9100000000, 0x67f6d10c00000000, 0x9142455a00000000, - 0xe7a34ac700000000, 0x3c862bbb00000000, 0x4a67242600000000, - 0x8acde94300000000, 0xfc2ce6de00000000, 0x270987a200000000, - 0x51e8883f00000000}, - {0x0000000000000000, 0xe8dbfbb900000000, 0x91b186a800000000, - 0x796a7d1100000000, 0x63657c8a00000000, 0x8bbe873300000000, - 0xf2d4fa2200000000, 0x1a0f019b00000000, 0x87cc89cf00000000, - 0x6f17727600000000, 0x167d0f6700000000, 0xfea6f4de00000000, - 0xe4a9f54500000000, 0x0c720efc00000000, 0x751873ed00000000, - 0x9dc3885400000000, 0x4f9f624400000000, 0xa74499fd00000000, - 0xde2ee4ec00000000, 0x36f51f5500000000, 0x2cfa1ece00000000, - 0xc421e57700000000, 0xbd4b986600000000, 0x559063df00000000, - 0xc853eb8b00000000, 0x2088103200000000, 0x59e26d2300000000, - 0xb139969a00000000, 0xab36970100000000, 0x43ed6cb800000000, - 0x3a8711a900000000, 0xd25cea1000000000, 0x9e3ec58800000000, - 0x76e53e3100000000, 0x0f8f432000000000, 0xe754b89900000000, - 0xfd5bb90200000000, 0x158042bb00000000, 0x6cea3faa00000000, - 0x8431c41300000000, 0x19f24c4700000000, 0xf129b7fe00000000, - 0x8843caef00000000, 0x6098315600000000, 0x7a9730cd00000000, - 0x924ccb7400000000, 0xeb26b66500000000, 0x03fd4ddc00000000, - 0xd1a1a7cc00000000, 0x397a5c7500000000, 0x4010216400000000, - 0xa8cbdadd00000000, 0xb2c4db4600000000, 0x5a1f20ff00000000, - 0x23755dee00000000, 0xcbaea65700000000, 0x566d2e0300000000, - 0xbeb6d5ba00000000, 0xc7dca8ab00000000, 0x2f07531200000000, - 0x3508528900000000, 0xddd3a93000000000, 0xa4b9d42100000000, - 0x4c622f9800000000, 0x7d7bfbca00000000, 0x95a0007300000000, - 0xecca7d6200000000, 0x041186db00000000, 0x1e1e874000000000, - 0xf6c57cf900000000, 0x8faf01e800000000, 0x6774fa5100000000, - 0xfab7720500000000, 0x126c89bc00000000, 0x6b06f4ad00000000, - 0x83dd0f1400000000, 0x99d20e8f00000000, 0x7109f53600000000, - 0x0863882700000000, 0xe0b8739e00000000, 0x32e4998e00000000, - 0xda3f623700000000, 0xa3551f2600000000, 0x4b8ee49f00000000, - 0x5181e50400000000, 0xb95a1ebd00000000, 0xc03063ac00000000, - 0x28eb981500000000, 0xb528104100000000, 0x5df3ebf800000000, - 0x249996e900000000, 0xcc426d5000000000, 0xd64d6ccb00000000, - 0x3e96977200000000, 0x47fcea6300000000, 0xaf2711da00000000, - 0xe3453e4200000000, 0x0b9ec5fb00000000, 0x72f4b8ea00000000, - 0x9a2f435300000000, 0x802042c800000000, 0x68fbb97100000000, - 0x1191c46000000000, 0xf94a3fd900000000, 0x6489b78d00000000, - 0x8c524c3400000000, 0xf538312500000000, 0x1de3ca9c00000000, - 0x07eccb0700000000, 0xef3730be00000000, 0x965d4daf00000000, - 0x7e86b61600000000, 0xacda5c0600000000, 0x4401a7bf00000000, - 0x3d6bdaae00000000, 0xd5b0211700000000, 0xcfbf208c00000000, - 0x2764db3500000000, 0x5e0ea62400000000, 0xb6d55d9d00000000, - 0x2b16d5c900000000, 0xc3cd2e7000000000, 0xbaa7536100000000, - 0x527ca8d800000000, 0x4873a94300000000, 0xa0a852fa00000000, - 0xd9c22feb00000000, 0x3119d45200000000, 0xbbf0874e00000000, - 0x532b7cf700000000, 0x2a4101e600000000, 0xc29afa5f00000000, - 0xd895fbc400000000, 0x304e007d00000000, 0x49247d6c00000000, - 0xa1ff86d500000000, 0x3c3c0e8100000000, 0xd4e7f53800000000, - 0xad8d882900000000, 0x4556739000000000, 0x5f59720b00000000, - 0xb78289b200000000, 0xcee8f4a300000000, 0x26330f1a00000000, - 0xf46fe50a00000000, 0x1cb41eb300000000, 0x65de63a200000000, - 0x8d05981b00000000, 0x970a998000000000, 0x7fd1623900000000, - 0x06bb1f2800000000, 0xee60e49100000000, 0x73a36cc500000000, - 0x9b78977c00000000, 0xe212ea6d00000000, 0x0ac911d400000000, - 0x10c6104f00000000, 0xf81debf600000000, 0x817796e700000000, - 0x69ac6d5e00000000, 0x25ce42c600000000, 0xcd15b97f00000000, - 0xb47fc46e00000000, 0x5ca43fd700000000, 0x46ab3e4c00000000, - 0xae70c5f500000000, 0xd71ab8e400000000, 0x3fc1435d00000000, - 0xa202cb0900000000, 0x4ad930b000000000, 0x33b34da100000000, - 0xdb68b61800000000, 0xc167b78300000000, 0x29bc4c3a00000000, - 0x50d6312b00000000, 0xb80dca9200000000, 0x6a51208200000000, - 0x828adb3b00000000, 0xfbe0a62a00000000, 0x133b5d9300000000, - 0x09345c0800000000, 0xe1efa7b100000000, 0x9885daa000000000, - 0x705e211900000000, 0xed9da94d00000000, 0x054652f400000000, - 0x7c2c2fe500000000, 0x94f7d45c00000000, 0x8ef8d5c700000000, - 0x66232e7e00000000, 0x1f49536f00000000, 0xf792a8d600000000, - 0xc68b7c8400000000, 0x2e50873d00000000, 0x573afa2c00000000, - 0xbfe1019500000000, 0xa5ee000e00000000, 0x4d35fbb700000000, - 0x345f86a600000000, 0xdc847d1f00000000, 0x4147f54b00000000, - 0xa99c0ef200000000, 0xd0f673e300000000, 0x382d885a00000000, - 0x222289c100000000, 0xcaf9727800000000, 0xb3930f6900000000, - 0x5b48f4d000000000, 0x89141ec000000000, 0x61cfe57900000000, - 0x18a5986800000000, 0xf07e63d100000000, 0xea71624a00000000, - 0x02aa99f300000000, 0x7bc0e4e200000000, 0x931b1f5b00000000, - 0x0ed8970f00000000, 0xe6036cb600000000, 0x9f6911a700000000, - 0x77b2ea1e00000000, 0x6dbdeb8500000000, 0x8566103c00000000, - 0xfc0c6d2d00000000, 0x14d7969400000000, 0x58b5b90c00000000, - 0xb06e42b500000000, 0xc9043fa400000000, 0x21dfc41d00000000, - 0x3bd0c58600000000, 0xd30b3e3f00000000, 0xaa61432e00000000, - 0x42bab89700000000, 0xdf7930c300000000, 0x37a2cb7a00000000, - 0x4ec8b66b00000000, 0xa6134dd200000000, 0xbc1c4c4900000000, - 0x54c7b7f000000000, 0x2dadcae100000000, 0xc576315800000000, - 0x172adb4800000000, 0xfff120f100000000, 0x869b5de000000000, - 0x6e40a65900000000, 0x744fa7c200000000, 0x9c945c7b00000000, - 0xe5fe216a00000000, 0x0d25dad300000000, 0x90e6528700000000, - 0x783da93e00000000, 0x0157d42f00000000, 0xe98c2f9600000000, - 0xf3832e0d00000000, 0x1b58d5b400000000, 0x6232a8a500000000, - 0x8ae9531c00000000}, - {0x0000000000000000, 0x919168ae00000000, 0x6325a08700000000, - 0xf2b4c82900000000, 0x874c31d400000000, 0x16dd597a00000000, - 0xe469915300000000, 0x75f8f9fd00000000, 0x4f9f137300000000, - 0xde0e7bdd00000000, 0x2cbab3f400000000, 0xbd2bdb5a00000000, - 0xc8d322a700000000, 0x59424a0900000000, 0xabf6822000000000, - 0x3a67ea8e00000000, 0x9e3e27e600000000, 0x0faf4f4800000000, - 0xfd1b876100000000, 0x6c8aefcf00000000, 0x1972163200000000, - 0x88e37e9c00000000, 0x7a57b6b500000000, 0xebc6de1b00000000, - 0xd1a1349500000000, 0x40305c3b00000000, 0xb284941200000000, - 0x2315fcbc00000000, 0x56ed054100000000, 0xc77c6def00000000, - 0x35c8a5c600000000, 0xa459cd6800000000, 0x7d7b3f1700000000, - 0xecea57b900000000, 0x1e5e9f9000000000, 0x8fcff73e00000000, - 0xfa370ec300000000, 0x6ba6666d00000000, 0x9912ae4400000000, - 0x0883c6ea00000000, 0x32e42c6400000000, 0xa37544ca00000000, - 0x51c18ce300000000, 0xc050e44d00000000, 0xb5a81db000000000, - 0x2439751e00000000, 0xd68dbd3700000000, 0x471cd59900000000, - 0xe34518f100000000, 0x72d4705f00000000, 0x8060b87600000000, - 0x11f1d0d800000000, 0x6409292500000000, 0xf598418b00000000, - 0x072c89a200000000, 0x96bde10c00000000, 0xacda0b8200000000, - 0x3d4b632c00000000, 0xcfffab0500000000, 0x5e6ec3ab00000000, - 0x2b963a5600000000, 0xba0752f800000000, 0x48b39ad100000000, - 0xd922f27f00000000, 0xfaf67e2e00000000, 0x6b67168000000000, - 0x99d3dea900000000, 0x0842b60700000000, 0x7dba4ffa00000000, - 0xec2b275400000000, 0x1e9fef7d00000000, 0x8f0e87d300000000, - 0xb5696d5d00000000, 0x24f805f300000000, 0xd64ccdda00000000, - 0x47dda57400000000, 0x32255c8900000000, 0xa3b4342700000000, - 0x5100fc0e00000000, 0xc09194a000000000, 0x64c859c800000000, - 0xf559316600000000, 0x07edf94f00000000, 0x967c91e100000000, - 0xe384681c00000000, 0x721500b200000000, 0x80a1c89b00000000, - 0x1130a03500000000, 0x2b574abb00000000, 0xbac6221500000000, - 0x4872ea3c00000000, 0xd9e3829200000000, 0xac1b7b6f00000000, - 0x3d8a13c100000000, 0xcf3edbe800000000, 0x5eafb34600000000, - 0x878d413900000000, 0x161c299700000000, 0xe4a8e1be00000000, - 0x7539891000000000, 0x00c170ed00000000, 0x9150184300000000, - 0x63e4d06a00000000, 0xf275b8c400000000, 0xc812524a00000000, - 0x59833ae400000000, 0xab37f2cd00000000, 0x3aa69a6300000000, - 0x4f5e639e00000000, 0xdecf0b3000000000, 0x2c7bc31900000000, - 0xbdeaabb700000000, 0x19b366df00000000, 0x88220e7100000000, - 0x7a96c65800000000, 0xeb07aef600000000, 0x9eff570b00000000, - 0x0f6e3fa500000000, 0xfddaf78c00000000, 0x6c4b9f2200000000, - 0x562c75ac00000000, 0xc7bd1d0200000000, 0x3509d52b00000000, - 0xa498bd8500000000, 0xd160447800000000, 0x40f12cd600000000, - 0xb245e4ff00000000, 0x23d48c5100000000, 0xf4edfd5c00000000, - 0x657c95f200000000, 0x97c85ddb00000000, 0x0659357500000000, - 0x73a1cc8800000000, 0xe230a42600000000, 0x10846c0f00000000, - 0x811504a100000000, 0xbb72ee2f00000000, 0x2ae3868100000000, - 0xd8574ea800000000, 0x49c6260600000000, 0x3c3edffb00000000, - 0xadafb75500000000, 0x5f1b7f7c00000000, 0xce8a17d200000000, - 0x6ad3daba00000000, 0xfb42b21400000000, 0x09f67a3d00000000, - 0x9867129300000000, 0xed9feb6e00000000, 0x7c0e83c000000000, - 0x8eba4be900000000, 0x1f2b234700000000, 0x254cc9c900000000, - 0xb4dda16700000000, 0x4669694e00000000, 0xd7f801e000000000, - 0xa200f81d00000000, 0x339190b300000000, 0xc125589a00000000, - 0x50b4303400000000, 0x8996c24b00000000, 0x1807aae500000000, - 0xeab362cc00000000, 0x7b220a6200000000, 0x0edaf39f00000000, - 0x9f4b9b3100000000, 0x6dff531800000000, 0xfc6e3bb600000000, - 0xc609d13800000000, 0x5798b99600000000, 0xa52c71bf00000000, - 0x34bd191100000000, 0x4145e0ec00000000, 0xd0d4884200000000, - 0x2260406b00000000, 0xb3f128c500000000, 0x17a8e5ad00000000, - 0x86398d0300000000, 0x748d452a00000000, 0xe51c2d8400000000, - 0x90e4d47900000000, 0x0175bcd700000000, 0xf3c174fe00000000, - 0x62501c5000000000, 0x5837f6de00000000, 0xc9a69e7000000000, - 0x3b12565900000000, 0xaa833ef700000000, 0xdf7bc70a00000000, - 0x4eeaafa400000000, 0xbc5e678d00000000, 0x2dcf0f2300000000, - 0x0e1b837200000000, 0x9f8aebdc00000000, 0x6d3e23f500000000, - 0xfcaf4b5b00000000, 0x8957b2a600000000, 0x18c6da0800000000, - 0xea72122100000000, 0x7be37a8f00000000, 0x4184900100000000, - 0xd015f8af00000000, 0x22a1308600000000, 0xb330582800000000, - 0xc6c8a1d500000000, 0x5759c97b00000000, 0xa5ed015200000000, - 0x347c69fc00000000, 0x9025a49400000000, 0x01b4cc3a00000000, - 0xf300041300000000, 0x62916cbd00000000, 0x1769954000000000, - 0x86f8fdee00000000, 0x744c35c700000000, 0xe5dd5d6900000000, - 0xdfbab7e700000000, 0x4e2bdf4900000000, 0xbc9f176000000000, - 0x2d0e7fce00000000, 0x58f6863300000000, 0xc967ee9d00000000, - 0x3bd326b400000000, 0xaa424e1a00000000, 0x7360bc6500000000, - 0xe2f1d4cb00000000, 0x10451ce200000000, 0x81d4744c00000000, - 0xf42c8db100000000, 0x65bde51f00000000, 0x97092d3600000000, - 0x0698459800000000, 0x3cffaf1600000000, 0xad6ec7b800000000, - 0x5fda0f9100000000, 0xce4b673f00000000, 0xbbb39ec200000000, - 0x2a22f66c00000000, 0xd8963e4500000000, 0x490756eb00000000, - 0xed5e9b8300000000, 0x7ccff32d00000000, 0x8e7b3b0400000000, - 0x1fea53aa00000000, 0x6a12aa5700000000, 0xfb83c2f900000000, - 0x09370ad000000000, 0x98a6627e00000000, 0xa2c188f000000000, - 0x3350e05e00000000, 0xc1e4287700000000, 0x507540d900000000, - 0x258db92400000000, 0xb41cd18a00000000, 0x46a819a300000000, - 0xd739710d00000000}}; - -#else /* W == 4 */ - -local const z_crc_t FAR crc_braid_table[][256] = { - {0x00000000, 0xccaa009e, 0x4225077d, 0x8e8f07e3, 0x844a0efa, - 0x48e00e64, 0xc66f0987, 0x0ac50919, 0xd3e51bb5, 0x1f4f1b2b, - 0x91c01cc8, 0x5d6a1c56, 0x57af154f, 0x9b0515d1, 0x158a1232, - 0xd92012ac, 0x7cbb312b, 0xb01131b5, 0x3e9e3656, 0xf23436c8, - 0xf8f13fd1, 0x345b3f4f, 0xbad438ac, 0x767e3832, 0xaf5e2a9e, - 0x63f42a00, 0xed7b2de3, 0x21d12d7d, 0x2b142464, 0xe7be24fa, - 0x69312319, 0xa59b2387, 0xf9766256, 0x35dc62c8, 0xbb53652b, - 0x77f965b5, 0x7d3c6cac, 0xb1966c32, 0x3f196bd1, 0xf3b36b4f, - 0x2a9379e3, 0xe639797d, 0x68b67e9e, 0xa41c7e00, 0xaed97719, - 0x62737787, 0xecfc7064, 0x205670fa, 0x85cd537d, 0x496753e3, - 0xc7e85400, 0x0b42549e, 0x01875d87, 0xcd2d5d19, 0x43a25afa, - 0x8f085a64, 0x562848c8, 0x9a824856, 0x140d4fb5, 0xd8a74f2b, - 0xd2624632, 0x1ec846ac, 0x9047414f, 0x5ced41d1, 0x299dc2ed, - 0xe537c273, 0x6bb8c590, 0xa712c50e, 0xadd7cc17, 0x617dcc89, - 0xeff2cb6a, 0x2358cbf4, 0xfa78d958, 0x36d2d9c6, 0xb85dde25, - 0x74f7debb, 0x7e32d7a2, 0xb298d73c, 0x3c17d0df, 0xf0bdd041, - 0x5526f3c6, 0x998cf358, 0x1703f4bb, 0xdba9f425, 0xd16cfd3c, - 0x1dc6fda2, 0x9349fa41, 0x5fe3fadf, 0x86c3e873, 0x4a69e8ed, - 0xc4e6ef0e, 0x084cef90, 0x0289e689, 0xce23e617, 0x40ace1f4, - 0x8c06e16a, 0xd0eba0bb, 0x1c41a025, 0x92cea7c6, 0x5e64a758, - 0x54a1ae41, 0x980baedf, 0x1684a93c, 0xda2ea9a2, 0x030ebb0e, - 0xcfa4bb90, 0x412bbc73, 0x8d81bced, 0x8744b5f4, 0x4beeb56a, - 0xc561b289, 0x09cbb217, 0xac509190, 0x60fa910e, 0xee7596ed, - 0x22df9673, 0x281a9f6a, 0xe4b09ff4, 0x6a3f9817, 0xa6959889, - 0x7fb58a25, 0xb31f8abb, 0x3d908d58, 0xf13a8dc6, 0xfbff84df, - 0x37558441, 0xb9da83a2, 0x7570833c, 0x533b85da, 0x9f918544, - 0x111e82a7, 0xddb48239, 0xd7718b20, 0x1bdb8bbe, 0x95548c5d, - 0x59fe8cc3, 0x80de9e6f, 0x4c749ef1, 0xc2fb9912, 0x0e51998c, - 0x04949095, 0xc83e900b, 0x46b197e8, 0x8a1b9776, 0x2f80b4f1, - 0xe32ab46f, 0x6da5b38c, 0xa10fb312, 0xabcaba0b, 0x6760ba95, - 0xe9efbd76, 0x2545bde8, 0xfc65af44, 0x30cfafda, 0xbe40a839, - 0x72eaa8a7, 0x782fa1be, 0xb485a120, 0x3a0aa6c3, 0xf6a0a65d, - 0xaa4de78c, 0x66e7e712, 0xe868e0f1, 0x24c2e06f, 0x2e07e976, - 0xe2ade9e8, 0x6c22ee0b, 0xa088ee95, 0x79a8fc39, 0xb502fca7, - 0x3b8dfb44, 0xf727fbda, 0xfde2f2c3, 0x3148f25d, 0xbfc7f5be, - 0x736df520, 0xd6f6d6a7, 0x1a5cd639, 0x94d3d1da, 0x5879d144, - 0x52bcd85d, 0x9e16d8c3, 0x1099df20, 0xdc33dfbe, 0x0513cd12, - 0xc9b9cd8c, 0x4736ca6f, 0x8b9ccaf1, 0x8159c3e8, 0x4df3c376, - 0xc37cc495, 0x0fd6c40b, 0x7aa64737, 0xb60c47a9, 0x3883404a, - 0xf42940d4, 0xfeec49cd, 0x32464953, 0xbcc94eb0, 0x70634e2e, - 0xa9435c82, 0x65e95c1c, 0xeb665bff, 0x27cc5b61, 0x2d095278, - 0xe1a352e6, 0x6f2c5505, 0xa386559b, 0x061d761c, 0xcab77682, - 0x44387161, 0x889271ff, 0x825778e6, 0x4efd7878, 0xc0727f9b, - 0x0cd87f05, 0xd5f86da9, 0x19526d37, 0x97dd6ad4, 0x5b776a4a, - 0x51b26353, 0x9d1863cd, 0x1397642e, 0xdf3d64b0, 0x83d02561, - 0x4f7a25ff, 0xc1f5221c, 0x0d5f2282, 0x079a2b9b, 0xcb302b05, - 0x45bf2ce6, 0x89152c78, 0x50353ed4, 0x9c9f3e4a, 0x121039a9, - 0xdeba3937, 0xd47f302e, 0x18d530b0, 0x965a3753, 0x5af037cd, - 0xff6b144a, 0x33c114d4, 0xbd4e1337, 0x71e413a9, 0x7b211ab0, - 0xb78b1a2e, 0x39041dcd, 0xf5ae1d53, 0x2c8e0fff, 0xe0240f61, - 0x6eab0882, 0xa201081c, 0xa8c40105, 0x646e019b, 0xeae10678, - 0x264b06e6}, - {0x00000000, 0xa6770bb4, 0x979f1129, 0x31e81a9d, 0xf44f2413, - 0x52382fa7, 0x63d0353a, 0xc5a73e8e, 0x33ef4e67, 0x959845d3, - 0xa4705f4e, 0x020754fa, 0xc7a06a74, 0x61d761c0, 0x503f7b5d, - 0xf64870e9, 0x67de9cce, 0xc1a9977a, 0xf0418de7, 0x56368653, - 0x9391b8dd, 0x35e6b369, 0x040ea9f4, 0xa279a240, 0x5431d2a9, - 0xf246d91d, 0xc3aec380, 0x65d9c834, 0xa07ef6ba, 0x0609fd0e, - 0x37e1e793, 0x9196ec27, 0xcfbd399c, 0x69ca3228, 0x582228b5, - 0xfe552301, 0x3bf21d8f, 0x9d85163b, 0xac6d0ca6, 0x0a1a0712, - 0xfc5277fb, 0x5a257c4f, 0x6bcd66d2, 0xcdba6d66, 0x081d53e8, - 0xae6a585c, 0x9f8242c1, 0x39f54975, 0xa863a552, 0x0e14aee6, - 0x3ffcb47b, 0x998bbfcf, 0x5c2c8141, 0xfa5b8af5, 0xcbb39068, - 0x6dc49bdc, 0x9b8ceb35, 0x3dfbe081, 0x0c13fa1c, 0xaa64f1a8, - 0x6fc3cf26, 0xc9b4c492, 0xf85cde0f, 0x5e2bd5bb, 0x440b7579, - 0xe27c7ecd, 0xd3946450, 0x75e36fe4, 0xb044516a, 0x16335ade, - 0x27db4043, 0x81ac4bf7, 0x77e43b1e, 0xd19330aa, 0xe07b2a37, - 0x460c2183, 0x83ab1f0d, 0x25dc14b9, 0x14340e24, 0xb2430590, - 0x23d5e9b7, 0x85a2e203, 0xb44af89e, 0x123df32a, 0xd79acda4, - 0x71edc610, 0x4005dc8d, 0xe672d739, 0x103aa7d0, 0xb64dac64, - 0x87a5b6f9, 0x21d2bd4d, 0xe47583c3, 0x42028877, 0x73ea92ea, - 0xd59d995e, 0x8bb64ce5, 0x2dc14751, 0x1c295dcc, 0xba5e5678, - 0x7ff968f6, 0xd98e6342, 0xe86679df, 0x4e11726b, 0xb8590282, - 0x1e2e0936, 0x2fc613ab, 0x89b1181f, 0x4c162691, 0xea612d25, - 0xdb8937b8, 0x7dfe3c0c, 0xec68d02b, 0x4a1fdb9f, 0x7bf7c102, - 0xdd80cab6, 0x1827f438, 0xbe50ff8c, 0x8fb8e511, 0x29cfeea5, - 0xdf879e4c, 0x79f095f8, 0x48188f65, 0xee6f84d1, 0x2bc8ba5f, - 0x8dbfb1eb, 0xbc57ab76, 0x1a20a0c2, 0x8816eaf2, 0x2e61e146, - 0x1f89fbdb, 0xb9fef06f, 0x7c59cee1, 0xda2ec555, 0xebc6dfc8, - 0x4db1d47c, 0xbbf9a495, 0x1d8eaf21, 0x2c66b5bc, 0x8a11be08, - 0x4fb68086, 0xe9c18b32, 0xd82991af, 0x7e5e9a1b, 0xefc8763c, - 0x49bf7d88, 0x78576715, 0xde206ca1, 0x1b87522f, 0xbdf0599b, - 0x8c184306, 0x2a6f48b2, 0xdc27385b, 0x7a5033ef, 0x4bb82972, - 0xedcf22c6, 0x28681c48, 0x8e1f17fc, 0xbff70d61, 0x198006d5, - 0x47abd36e, 0xe1dcd8da, 0xd034c247, 0x7643c9f3, 0xb3e4f77d, - 0x1593fcc9, 0x247be654, 0x820cede0, 0x74449d09, 0xd23396bd, - 0xe3db8c20, 0x45ac8794, 0x800bb91a, 0x267cb2ae, 0x1794a833, - 0xb1e3a387, 0x20754fa0, 0x86024414, 0xb7ea5e89, 0x119d553d, - 0xd43a6bb3, 0x724d6007, 0x43a57a9a, 0xe5d2712e, 0x139a01c7, - 0xb5ed0a73, 0x840510ee, 0x22721b5a, 0xe7d525d4, 0x41a22e60, - 0x704a34fd, 0xd63d3f49, 0xcc1d9f8b, 0x6a6a943f, 0x5b828ea2, - 0xfdf58516, 0x3852bb98, 0x9e25b02c, 0xafcdaab1, 0x09baa105, - 0xfff2d1ec, 0x5985da58, 0x686dc0c5, 0xce1acb71, 0x0bbdf5ff, - 0xadcafe4b, 0x9c22e4d6, 0x3a55ef62, 0xabc30345, 0x0db408f1, - 0x3c5c126c, 0x9a2b19d8, 0x5f8c2756, 0xf9fb2ce2, 0xc813367f, - 0x6e643dcb, 0x982c4d22, 0x3e5b4696, 0x0fb35c0b, 0xa9c457bf, - 0x6c636931, 0xca146285, 0xfbfc7818, 0x5d8b73ac, 0x03a0a617, - 0xa5d7ada3, 0x943fb73e, 0x3248bc8a, 0xf7ef8204, 0x519889b0, - 0x6070932d, 0xc6079899, 0x304fe870, 0x9638e3c4, 0xa7d0f959, - 0x01a7f2ed, 0xc400cc63, 0x6277c7d7, 0x539fdd4a, 0xf5e8d6fe, - 0x647e3ad9, 0xc209316d, 0xf3e12bf0, 0x55962044, 0x90311eca, - 0x3646157e, 0x07ae0fe3, 0xa1d90457, 0x579174be, 0xf1e67f0a, - 0xc00e6597, 0x66796e23, 0xa3de50ad, 0x05a95b19, 0x34414184, - 0x92364a30}, - {0x00000000, 0xcb5cd3a5, 0x4dc8a10b, 0x869472ae, 0x9b914216, - 0x50cd91b3, 0xd659e31d, 0x1d0530b8, 0xec53826d, 0x270f51c8, - 0xa19b2366, 0x6ac7f0c3, 0x77c2c07b, 0xbc9e13de, 0x3a0a6170, - 0xf156b2d5, 0x03d6029b, 0xc88ad13e, 0x4e1ea390, 0x85427035, - 0x9847408d, 0x531b9328, 0xd58fe186, 0x1ed33223, 0xef8580f6, - 0x24d95353, 0xa24d21fd, 0x6911f258, 0x7414c2e0, 0xbf481145, - 0x39dc63eb, 0xf280b04e, 0x07ac0536, 0xccf0d693, 0x4a64a43d, - 0x81387798, 0x9c3d4720, 0x57619485, 0xd1f5e62b, 0x1aa9358e, - 0xebff875b, 0x20a354fe, 0xa6372650, 0x6d6bf5f5, 0x706ec54d, - 0xbb3216e8, 0x3da66446, 0xf6fab7e3, 0x047a07ad, 0xcf26d408, - 0x49b2a6a6, 0x82ee7503, 0x9feb45bb, 0x54b7961e, 0xd223e4b0, - 0x197f3715, 0xe82985c0, 0x23755665, 0xa5e124cb, 0x6ebdf76e, - 0x73b8c7d6, 0xb8e41473, 0x3e7066dd, 0xf52cb578, 0x0f580a6c, - 0xc404d9c9, 0x4290ab67, 0x89cc78c2, 0x94c9487a, 0x5f959bdf, - 0xd901e971, 0x125d3ad4, 0xe30b8801, 0x28575ba4, 0xaec3290a, - 0x659ffaaf, 0x789aca17, 0xb3c619b2, 0x35526b1c, 0xfe0eb8b9, - 0x0c8e08f7, 0xc7d2db52, 0x4146a9fc, 0x8a1a7a59, 0x971f4ae1, - 0x5c439944, 0xdad7ebea, 0x118b384f, 0xe0dd8a9a, 0x2b81593f, - 0xad152b91, 0x6649f834, 0x7b4cc88c, 0xb0101b29, 0x36846987, - 0xfdd8ba22, 0x08f40f5a, 0xc3a8dcff, 0x453cae51, 0x8e607df4, - 0x93654d4c, 0x58399ee9, 0xdeadec47, 0x15f13fe2, 0xe4a78d37, - 0x2ffb5e92, 0xa96f2c3c, 0x6233ff99, 0x7f36cf21, 0xb46a1c84, - 0x32fe6e2a, 0xf9a2bd8f, 0x0b220dc1, 0xc07ede64, 0x46eaacca, - 0x8db67f6f, 0x90b34fd7, 0x5bef9c72, 0xdd7beedc, 0x16273d79, - 0xe7718fac, 0x2c2d5c09, 0xaab92ea7, 0x61e5fd02, 0x7ce0cdba, - 0xb7bc1e1f, 0x31286cb1, 0xfa74bf14, 0x1eb014d8, 0xd5ecc77d, - 0x5378b5d3, 0x98246676, 0x852156ce, 0x4e7d856b, 0xc8e9f7c5, - 0x03b52460, 0xf2e396b5, 0x39bf4510, 0xbf2b37be, 0x7477e41b, - 0x6972d4a3, 0xa22e0706, 0x24ba75a8, 0xefe6a60d, 0x1d661643, - 0xd63ac5e6, 0x50aeb748, 0x9bf264ed, 0x86f75455, 0x4dab87f0, - 0xcb3ff55e, 0x006326fb, 0xf135942e, 0x3a69478b, 0xbcfd3525, - 0x77a1e680, 0x6aa4d638, 0xa1f8059d, 0x276c7733, 0xec30a496, - 0x191c11ee, 0xd240c24b, 0x54d4b0e5, 0x9f886340, 0x828d53f8, - 0x49d1805d, 0xcf45f2f3, 0x04192156, 0xf54f9383, 0x3e134026, - 0xb8873288, 0x73dbe12d, 0x6eded195, 0xa5820230, 0x2316709e, - 0xe84aa33b, 0x1aca1375, 0xd196c0d0, 0x5702b27e, 0x9c5e61db, - 0x815b5163, 0x4a0782c6, 0xcc93f068, 0x07cf23cd, 0xf6999118, - 0x3dc542bd, 0xbb513013, 0x700de3b6, 0x6d08d30e, 0xa65400ab, - 0x20c07205, 0xeb9ca1a0, 0x11e81eb4, 0xdab4cd11, 0x5c20bfbf, - 0x977c6c1a, 0x8a795ca2, 0x41258f07, 0xc7b1fda9, 0x0ced2e0c, - 0xfdbb9cd9, 0x36e74f7c, 0xb0733dd2, 0x7b2fee77, 0x662adecf, - 0xad760d6a, 0x2be27fc4, 0xe0beac61, 0x123e1c2f, 0xd962cf8a, - 0x5ff6bd24, 0x94aa6e81, 0x89af5e39, 0x42f38d9c, 0xc467ff32, - 0x0f3b2c97, 0xfe6d9e42, 0x35314de7, 0xb3a53f49, 0x78f9ecec, - 0x65fcdc54, 0xaea00ff1, 0x28347d5f, 0xe368aefa, 0x16441b82, - 0xdd18c827, 0x5b8cba89, 0x90d0692c, 0x8dd55994, 0x46898a31, - 0xc01df89f, 0x0b412b3a, 0xfa1799ef, 0x314b4a4a, 0xb7df38e4, - 0x7c83eb41, 0x6186dbf9, 0xaada085c, 0x2c4e7af2, 0xe712a957, - 0x15921919, 0xdececabc, 0x585ab812, 0x93066bb7, 0x8e035b0f, - 0x455f88aa, 0xc3cbfa04, 0x089729a1, 0xf9c19b74, 0x329d48d1, - 0xb4093a7f, 0x7f55e9da, 0x6250d962, 0xa90c0ac7, 0x2f987869, - 0xe4c4abcc}, - {0x00000000, 0x3d6029b0, 0x7ac05360, 0x47a07ad0, 0xf580a6c0, - 0xc8e08f70, 0x8f40f5a0, 0xb220dc10, 0x30704bc1, 0x0d106271, - 0x4ab018a1, 0x77d03111, 0xc5f0ed01, 0xf890c4b1, 0xbf30be61, - 0x825097d1, 0x60e09782, 0x5d80be32, 0x1a20c4e2, 0x2740ed52, - 0x95603142, 0xa80018f2, 0xefa06222, 0xd2c04b92, 0x5090dc43, - 0x6df0f5f3, 0x2a508f23, 0x1730a693, 0xa5107a83, 0x98705333, - 0xdfd029e3, 0xe2b00053, 0xc1c12f04, 0xfca106b4, 0xbb017c64, - 0x866155d4, 0x344189c4, 0x0921a074, 0x4e81daa4, 0x73e1f314, - 0xf1b164c5, 0xccd14d75, 0x8b7137a5, 0xb6111e15, 0x0431c205, - 0x3951ebb5, 0x7ef19165, 0x4391b8d5, 0xa121b886, 0x9c419136, - 0xdbe1ebe6, 0xe681c256, 0x54a11e46, 0x69c137f6, 0x2e614d26, - 0x13016496, 0x9151f347, 0xac31daf7, 0xeb91a027, 0xd6f18997, - 0x64d15587, 0x59b17c37, 0x1e1106e7, 0x23712f57, 0x58f35849, - 0x659371f9, 0x22330b29, 0x1f532299, 0xad73fe89, 0x9013d739, - 0xd7b3ade9, 0xead38459, 0x68831388, 0x55e33a38, 0x124340e8, - 0x2f236958, 0x9d03b548, 0xa0639cf8, 0xe7c3e628, 0xdaa3cf98, - 0x3813cfcb, 0x0573e67b, 0x42d39cab, 0x7fb3b51b, 0xcd93690b, - 0xf0f340bb, 0xb7533a6b, 0x8a3313db, 0x0863840a, 0x3503adba, - 0x72a3d76a, 0x4fc3feda, 0xfde322ca, 0xc0830b7a, 0x872371aa, - 0xba43581a, 0x9932774d, 0xa4525efd, 0xe3f2242d, 0xde920d9d, - 0x6cb2d18d, 0x51d2f83d, 0x167282ed, 0x2b12ab5d, 0xa9423c8c, - 0x9422153c, 0xd3826fec, 0xeee2465c, 0x5cc29a4c, 0x61a2b3fc, - 0x2602c92c, 0x1b62e09c, 0xf9d2e0cf, 0xc4b2c97f, 0x8312b3af, - 0xbe729a1f, 0x0c52460f, 0x31326fbf, 0x7692156f, 0x4bf23cdf, - 0xc9a2ab0e, 0xf4c282be, 0xb362f86e, 0x8e02d1de, 0x3c220dce, - 0x0142247e, 0x46e25eae, 0x7b82771e, 0xb1e6b092, 0x8c869922, - 0xcb26e3f2, 0xf646ca42, 0x44661652, 0x79063fe2, 0x3ea64532, - 0x03c66c82, 0x8196fb53, 0xbcf6d2e3, 0xfb56a833, 0xc6368183, - 0x74165d93, 0x49767423, 0x0ed60ef3, 0x33b62743, 0xd1062710, - 0xec660ea0, 0xabc67470, 0x96a65dc0, 0x248681d0, 0x19e6a860, - 0x5e46d2b0, 0x6326fb00, 0xe1766cd1, 0xdc164561, 0x9bb63fb1, - 0xa6d61601, 0x14f6ca11, 0x2996e3a1, 0x6e369971, 0x5356b0c1, - 0x70279f96, 0x4d47b626, 0x0ae7ccf6, 0x3787e546, 0x85a73956, - 0xb8c710e6, 0xff676a36, 0xc2074386, 0x4057d457, 0x7d37fde7, - 0x3a978737, 0x07f7ae87, 0xb5d77297, 0x88b75b27, 0xcf1721f7, - 0xf2770847, 0x10c70814, 0x2da721a4, 0x6a075b74, 0x576772c4, - 0xe547aed4, 0xd8278764, 0x9f87fdb4, 0xa2e7d404, 0x20b743d5, - 0x1dd76a65, 0x5a7710b5, 0x67173905, 0xd537e515, 0xe857cca5, - 0xaff7b675, 0x92979fc5, 0xe915e8db, 0xd475c16b, 0x93d5bbbb, - 0xaeb5920b, 0x1c954e1b, 0x21f567ab, 0x66551d7b, 0x5b3534cb, - 0xd965a31a, 0xe4058aaa, 0xa3a5f07a, 0x9ec5d9ca, 0x2ce505da, - 0x11852c6a, 0x562556ba, 0x6b457f0a, 0x89f57f59, 0xb49556e9, - 0xf3352c39, 0xce550589, 0x7c75d999, 0x4115f029, 0x06b58af9, - 0x3bd5a349, 0xb9853498, 0x84e51d28, 0xc34567f8, 0xfe254e48, - 0x4c059258, 0x7165bbe8, 0x36c5c138, 0x0ba5e888, 0x28d4c7df, - 0x15b4ee6f, 0x521494bf, 0x6f74bd0f, 0xdd54611f, 0xe03448af, - 0xa794327f, 0x9af41bcf, 0x18a48c1e, 0x25c4a5ae, 0x6264df7e, - 0x5f04f6ce, 0xed242ade, 0xd044036e, 0x97e479be, 0xaa84500e, - 0x4834505d, 0x755479ed, 0x32f4033d, 0x0f942a8d, 0xbdb4f69d, - 0x80d4df2d, 0xc774a5fd, 0xfa148c4d, 0x78441b9c, 0x4524322c, - 0x028448fc, 0x3fe4614c, 0x8dc4bd5c, 0xb0a494ec, 0xf704ee3c, - 0xca64c78c}}; - -local const z_word_t FAR crc_braid_big_table[][256] = { - {0x00000000, 0xb029603d, 0x6053c07a, 0xd07aa047, 0xc0a680f5, - 0x708fe0c8, 0xa0f5408f, 0x10dc20b2, 0xc14b7030, 0x7162100d, - 0xa118b04a, 0x1131d077, 0x01edf0c5, 0xb1c490f8, 0x61be30bf, - 0xd1975082, 0x8297e060, 0x32be805d, 0xe2c4201a, 0x52ed4027, - 0x42316095, 0xf21800a8, 0x2262a0ef, 0x924bc0d2, 0x43dc9050, - 0xf3f5f06d, 0x238f502a, 0x93a63017, 0x837a10a5, 0x33537098, - 0xe329d0df, 0x5300b0e2, 0x042fc1c1, 0xb406a1fc, 0x647c01bb, - 0xd4556186, 0xc4894134, 0x74a02109, 0xa4da814e, 0x14f3e173, - 0xc564b1f1, 0x754dd1cc, 0xa537718b, 0x151e11b6, 0x05c23104, - 0xb5eb5139, 0x6591f17e, 0xd5b89143, 0x86b821a1, 0x3691419c, - 0xe6ebe1db, 0x56c281e6, 0x461ea154, 0xf637c169, 0x264d612e, - 0x96640113, 0x47f35191, 0xf7da31ac, 0x27a091eb, 0x9789f1d6, - 0x8755d164, 0x377cb159, 0xe706111e, 0x572f7123, 0x4958f358, - 0xf9719365, 0x290b3322, 0x9922531f, 0x89fe73ad, 0x39d71390, - 0xe9adb3d7, 0x5984d3ea, 0x88138368, 0x383ae355, 0xe8404312, - 0x5869232f, 0x48b5039d, 0xf89c63a0, 0x28e6c3e7, 0x98cfa3da, - 0xcbcf1338, 0x7be67305, 0xab9cd342, 0x1bb5b37f, 0x0b6993cd, - 0xbb40f3f0, 0x6b3a53b7, 0xdb13338a, 0x0a846308, 0xbaad0335, - 0x6ad7a372, 0xdafec34f, 0xca22e3fd, 0x7a0b83c0, 0xaa712387, - 0x1a5843ba, 0x4d773299, 0xfd5e52a4, 0x2d24f2e3, 0x9d0d92de, - 0x8dd1b26c, 0x3df8d251, 0xed827216, 0x5dab122b, 0x8c3c42a9, - 0x3c152294, 0xec6f82d3, 0x5c46e2ee, 0x4c9ac25c, 0xfcb3a261, - 0x2cc90226, 0x9ce0621b, 0xcfe0d2f9, 0x7fc9b2c4, 0xafb31283, - 0x1f9a72be, 0x0f46520c, 0xbf6f3231, 0x6f159276, 0xdf3cf24b, - 0x0eaba2c9, 0xbe82c2f4, 0x6ef862b3, 0xded1028e, 0xce0d223c, - 0x7e244201, 0xae5ee246, 0x1e77827b, 0x92b0e6b1, 0x2299868c, - 0xf2e326cb, 0x42ca46f6, 0x52166644, 0xe23f0679, 0x3245a63e, - 0x826cc603, 0x53fb9681, 0xe3d2f6bc, 0x33a856fb, 0x838136c6, - 0x935d1674, 0x23747649, 0xf30ed60e, 0x4327b633, 0x102706d1, - 0xa00e66ec, 0x7074c6ab, 0xc05da696, 0xd0818624, 0x60a8e619, - 0xb0d2465e, 0x00fb2663, 0xd16c76e1, 0x614516dc, 0xb13fb69b, - 0x0116d6a6, 0x11caf614, 0xa1e39629, 0x7199366e, 0xc1b05653, - 0x969f2770, 0x26b6474d, 0xf6cce70a, 0x46e58737, 0x5639a785, - 0xe610c7b8, 0x366a67ff, 0x864307c2, 0x57d45740, 0xe7fd377d, - 0x3787973a, 0x87aef707, 0x9772d7b5, 0x275bb788, 0xf72117cf, - 0x470877f2, 0x1408c710, 0xa421a72d, 0x745b076a, 0xc4726757, - 0xd4ae47e5, 0x648727d8, 0xb4fd879f, 0x04d4e7a2, 0xd543b720, - 0x656ad71d, 0xb510775a, 0x05391767, 0x15e537d5, 0xa5cc57e8, - 0x75b6f7af, 0xc59f9792, 0xdbe815e9, 0x6bc175d4, 0xbbbbd593, - 0x0b92b5ae, 0x1b4e951c, 0xab67f521, 0x7b1d5566, 0xcb34355b, - 0x1aa365d9, 0xaa8a05e4, 0x7af0a5a3, 0xcad9c59e, 0xda05e52c, - 0x6a2c8511, 0xba562556, 0x0a7f456b, 0x597ff589, 0xe95695b4, - 0x392c35f3, 0x890555ce, 0x99d9757c, 0x29f01541, 0xf98ab506, - 0x49a3d53b, 0x983485b9, 0x281de584, 0xf86745c3, 0x484e25fe, - 0x5892054c, 0xe8bb6571, 0x38c1c536, 0x88e8a50b, 0xdfc7d428, - 0x6feeb415, 0xbf941452, 0x0fbd746f, 0x1f6154dd, 0xaf4834e0, - 0x7f3294a7, 0xcf1bf49a, 0x1e8ca418, 0xaea5c425, 0x7edf6462, - 0xcef6045f, 0xde2a24ed, 0x6e0344d0, 0xbe79e497, 0x0e5084aa, - 0x5d503448, 0xed795475, 0x3d03f432, 0x8d2a940f, 0x9df6b4bd, - 0x2ddfd480, 0xfda574c7, 0x4d8c14fa, 0x9c1b4478, 0x2c322445, - 0xfc488402, 0x4c61e43f, 0x5cbdc48d, 0xec94a4b0, 0x3cee04f7, - 0x8cc764ca}, - {0x00000000, 0xa5d35ccb, 0x0ba1c84d, 0xae729486, 0x1642919b, - 0xb391cd50, 0x1de359d6, 0xb830051d, 0x6d8253ec, 0xc8510f27, - 0x66239ba1, 0xc3f0c76a, 0x7bc0c277, 0xde139ebc, 0x70610a3a, - 0xd5b256f1, 0x9b02d603, 0x3ed18ac8, 0x90a31e4e, 0x35704285, - 0x8d404798, 0x28931b53, 0x86e18fd5, 0x2332d31e, 0xf68085ef, - 0x5353d924, 0xfd214da2, 0x58f21169, 0xe0c21474, 0x451148bf, - 0xeb63dc39, 0x4eb080f2, 0x3605ac07, 0x93d6f0cc, 0x3da4644a, - 0x98773881, 0x20473d9c, 0x85946157, 0x2be6f5d1, 0x8e35a91a, - 0x5b87ffeb, 0xfe54a320, 0x502637a6, 0xf5f56b6d, 0x4dc56e70, - 0xe81632bb, 0x4664a63d, 0xe3b7faf6, 0xad077a04, 0x08d426cf, - 0xa6a6b249, 0x0375ee82, 0xbb45eb9f, 0x1e96b754, 0xb0e423d2, - 0x15377f19, 0xc08529e8, 0x65567523, 0xcb24e1a5, 0x6ef7bd6e, - 0xd6c7b873, 0x7314e4b8, 0xdd66703e, 0x78b52cf5, 0x6c0a580f, - 0xc9d904c4, 0x67ab9042, 0xc278cc89, 0x7a48c994, 0xdf9b955f, - 0x71e901d9, 0xd43a5d12, 0x01880be3, 0xa45b5728, 0x0a29c3ae, - 0xaffa9f65, 0x17ca9a78, 0xb219c6b3, 0x1c6b5235, 0xb9b80efe, - 0xf7088e0c, 0x52dbd2c7, 0xfca94641, 0x597a1a8a, 0xe14a1f97, - 0x4499435c, 0xeaebd7da, 0x4f388b11, 0x9a8adde0, 0x3f59812b, - 0x912b15ad, 0x34f84966, 0x8cc84c7b, 0x291b10b0, 0x87698436, - 0x22bad8fd, 0x5a0ff408, 0xffdca8c3, 0x51ae3c45, 0xf47d608e, - 0x4c4d6593, 0xe99e3958, 0x47ecadde, 0xe23ff115, 0x378da7e4, - 0x925efb2f, 0x3c2c6fa9, 0x99ff3362, 0x21cf367f, 0x841c6ab4, - 0x2a6efe32, 0x8fbda2f9, 0xc10d220b, 0x64de7ec0, 0xcaacea46, - 0x6f7fb68d, 0xd74fb390, 0x729cef5b, 0xdcee7bdd, 0x793d2716, - 0xac8f71e7, 0x095c2d2c, 0xa72eb9aa, 0x02fde561, 0xbacde07c, - 0x1f1ebcb7, 0xb16c2831, 0x14bf74fa, 0xd814b01e, 0x7dc7ecd5, - 0xd3b57853, 0x76662498, 0xce562185, 0x6b857d4e, 0xc5f7e9c8, - 0x6024b503, 0xb596e3f2, 0x1045bf39, 0xbe372bbf, 0x1be47774, - 0xa3d47269, 0x06072ea2, 0xa875ba24, 0x0da6e6ef, 0x4316661d, - 0xe6c53ad6, 0x48b7ae50, 0xed64f29b, 0x5554f786, 0xf087ab4d, - 0x5ef53fcb, 0xfb266300, 0x2e9435f1, 0x8b47693a, 0x2535fdbc, - 0x80e6a177, 0x38d6a46a, 0x9d05f8a1, 0x33776c27, 0x96a430ec, - 0xee111c19, 0x4bc240d2, 0xe5b0d454, 0x4063889f, 0xf8538d82, - 0x5d80d149, 0xf3f245cf, 0x56211904, 0x83934ff5, 0x2640133e, - 0x883287b8, 0x2de1db73, 0x95d1de6e, 0x300282a5, 0x9e701623, - 0x3ba34ae8, 0x7513ca1a, 0xd0c096d1, 0x7eb20257, 0xdb615e9c, - 0x63515b81, 0xc682074a, 0x68f093cc, 0xcd23cf07, 0x189199f6, - 0xbd42c53d, 0x133051bb, 0xb6e30d70, 0x0ed3086d, 0xab0054a6, - 0x0572c020, 0xa0a19ceb, 0xb41ee811, 0x11cdb4da, 0xbfbf205c, - 0x1a6c7c97, 0xa25c798a, 0x078f2541, 0xa9fdb1c7, 0x0c2eed0c, - 0xd99cbbfd, 0x7c4fe736, 0xd23d73b0, 0x77ee2f7b, 0xcfde2a66, - 0x6a0d76ad, 0xc47fe22b, 0x61acbee0, 0x2f1c3e12, 0x8acf62d9, - 0x24bdf65f, 0x816eaa94, 0x395eaf89, 0x9c8df342, 0x32ff67c4, - 0x972c3b0f, 0x429e6dfe, 0xe74d3135, 0x493fa5b3, 0xececf978, - 0x54dcfc65, 0xf10fa0ae, 0x5f7d3428, 0xfaae68e3, 0x821b4416, - 0x27c818dd, 0x89ba8c5b, 0x2c69d090, 0x9459d58d, 0x318a8946, - 0x9ff81dc0, 0x3a2b410b, 0xef9917fa, 0x4a4a4b31, 0xe438dfb7, - 0x41eb837c, 0xf9db8661, 0x5c08daaa, 0xf27a4e2c, 0x57a912e7, - 0x19199215, 0xbccacede, 0x12b85a58, 0xb76b0693, 0x0f5b038e, - 0xaa885f45, 0x04facbc3, 0xa1299708, 0x749bc1f9, 0xd1489d32, - 0x7f3a09b4, 0xdae9557f, 0x62d95062, 0xc70a0ca9, 0x6978982f, - 0xccabc4e4}, - {0x00000000, 0xb40b77a6, 0x29119f97, 0x9d1ae831, 0x13244ff4, - 0xa72f3852, 0x3a35d063, 0x8e3ea7c5, 0x674eef33, 0xd3459895, - 0x4e5f70a4, 0xfa540702, 0x746aa0c7, 0xc061d761, 0x5d7b3f50, - 0xe97048f6, 0xce9cde67, 0x7a97a9c1, 0xe78d41f0, 0x53863656, - 0xddb89193, 0x69b3e635, 0xf4a90e04, 0x40a279a2, 0xa9d23154, - 0x1dd946f2, 0x80c3aec3, 0x34c8d965, 0xbaf67ea0, 0x0efd0906, - 0x93e7e137, 0x27ec9691, 0x9c39bdcf, 0x2832ca69, 0xb5282258, - 0x012355fe, 0x8f1df23b, 0x3b16859d, 0xa60c6dac, 0x12071a0a, - 0xfb7752fc, 0x4f7c255a, 0xd266cd6b, 0x666dbacd, 0xe8531d08, - 0x5c586aae, 0xc142829f, 0x7549f539, 0x52a563a8, 0xe6ae140e, - 0x7bb4fc3f, 0xcfbf8b99, 0x41812c5c, 0xf58a5bfa, 0x6890b3cb, - 0xdc9bc46d, 0x35eb8c9b, 0x81e0fb3d, 0x1cfa130c, 0xa8f164aa, - 0x26cfc36f, 0x92c4b4c9, 0x0fde5cf8, 0xbbd52b5e, 0x79750b44, - 0xcd7e7ce2, 0x506494d3, 0xe46fe375, 0x6a5144b0, 0xde5a3316, - 0x4340db27, 0xf74bac81, 0x1e3be477, 0xaa3093d1, 0x372a7be0, - 0x83210c46, 0x0d1fab83, 0xb914dc25, 0x240e3414, 0x900543b2, - 0xb7e9d523, 0x03e2a285, 0x9ef84ab4, 0x2af33d12, 0xa4cd9ad7, - 0x10c6ed71, 0x8ddc0540, 0x39d772e6, 0xd0a73a10, 0x64ac4db6, - 0xf9b6a587, 0x4dbdd221, 0xc38375e4, 0x77880242, 0xea92ea73, - 0x5e999dd5, 0xe54cb68b, 0x5147c12d, 0xcc5d291c, 0x78565eba, - 0xf668f97f, 0x42638ed9, 0xdf7966e8, 0x6b72114e, 0x820259b8, - 0x36092e1e, 0xab13c62f, 0x1f18b189, 0x9126164c, 0x252d61ea, - 0xb83789db, 0x0c3cfe7d, 0x2bd068ec, 0x9fdb1f4a, 0x02c1f77b, - 0xb6ca80dd, 0x38f42718, 0x8cff50be, 0x11e5b88f, 0xa5eecf29, - 0x4c9e87df, 0xf895f079, 0x658f1848, 0xd1846fee, 0x5fbac82b, - 0xebb1bf8d, 0x76ab57bc, 0xc2a0201a, 0xf2ea1688, 0x46e1612e, - 0xdbfb891f, 0x6ff0feb9, 0xe1ce597c, 0x55c52eda, 0xc8dfc6eb, - 0x7cd4b14d, 0x95a4f9bb, 0x21af8e1d, 0xbcb5662c, 0x08be118a, - 0x8680b64f, 0x328bc1e9, 0xaf9129d8, 0x1b9a5e7e, 0x3c76c8ef, - 0x887dbf49, 0x15675778, 0xa16c20de, 0x2f52871b, 0x9b59f0bd, - 0x0643188c, 0xb2486f2a, 0x5b3827dc, 0xef33507a, 0x7229b84b, - 0xc622cfed, 0x481c6828, 0xfc171f8e, 0x610df7bf, 0xd5068019, - 0x6ed3ab47, 0xdad8dce1, 0x47c234d0, 0xf3c94376, 0x7df7e4b3, - 0xc9fc9315, 0x54e67b24, 0xe0ed0c82, 0x099d4474, 0xbd9633d2, - 0x208cdbe3, 0x9487ac45, 0x1ab90b80, 0xaeb27c26, 0x33a89417, - 0x87a3e3b1, 0xa04f7520, 0x14440286, 0x895eeab7, 0x3d559d11, - 0xb36b3ad4, 0x07604d72, 0x9a7aa543, 0x2e71d2e5, 0xc7019a13, - 0x730aedb5, 0xee100584, 0x5a1b7222, 0xd425d5e7, 0x602ea241, - 0xfd344a70, 0x493f3dd6, 0x8b9f1dcc, 0x3f946a6a, 0xa28e825b, - 0x1685f5fd, 0x98bb5238, 0x2cb0259e, 0xb1aacdaf, 0x05a1ba09, - 0xecd1f2ff, 0x58da8559, 0xc5c06d68, 0x71cb1ace, 0xfff5bd0b, - 0x4bfecaad, 0xd6e4229c, 0x62ef553a, 0x4503c3ab, 0xf108b40d, - 0x6c125c3c, 0xd8192b9a, 0x56278c5f, 0xe22cfbf9, 0x7f3613c8, - 0xcb3d646e, 0x224d2c98, 0x96465b3e, 0x0b5cb30f, 0xbf57c4a9, - 0x3169636c, 0x856214ca, 0x1878fcfb, 0xac738b5d, 0x17a6a003, - 0xa3add7a5, 0x3eb73f94, 0x8abc4832, 0x0482eff7, 0xb0899851, - 0x2d937060, 0x999807c6, 0x70e84f30, 0xc4e33896, 0x59f9d0a7, - 0xedf2a701, 0x63cc00c4, 0xd7c77762, 0x4add9f53, 0xfed6e8f5, - 0xd93a7e64, 0x6d3109c2, 0xf02be1f3, 0x44209655, 0xca1e3190, - 0x7e154636, 0xe30fae07, 0x5704d9a1, 0xbe749157, 0x0a7fe6f1, - 0x97650ec0, 0x236e7966, 0xad50dea3, 0x195ba905, 0x84414134, - 0x304a3692}, - {0x00000000, 0x9e00aacc, 0x7d072542, 0xe3078f8e, 0xfa0e4a84, - 0x640ee048, 0x87096fc6, 0x1909c50a, 0xb51be5d3, 0x2b1b4f1f, - 0xc81cc091, 0x561c6a5d, 0x4f15af57, 0xd115059b, 0x32128a15, - 0xac1220d9, 0x2b31bb7c, 0xb53111b0, 0x56369e3e, 0xc83634f2, - 0xd13ff1f8, 0x4f3f5b34, 0xac38d4ba, 0x32387e76, 0x9e2a5eaf, - 0x002af463, 0xe32d7bed, 0x7d2dd121, 0x6424142b, 0xfa24bee7, - 0x19233169, 0x87239ba5, 0x566276f9, 0xc862dc35, 0x2b6553bb, - 0xb565f977, 0xac6c3c7d, 0x326c96b1, 0xd16b193f, 0x4f6bb3f3, - 0xe379932a, 0x7d7939e6, 0x9e7eb668, 0x007e1ca4, 0x1977d9ae, - 0x87777362, 0x6470fcec, 0xfa705620, 0x7d53cd85, 0xe3536749, - 0x0054e8c7, 0x9e54420b, 0x875d8701, 0x195d2dcd, 0xfa5aa243, - 0x645a088f, 0xc8482856, 0x5648829a, 0xb54f0d14, 0x2b4fa7d8, - 0x324662d2, 0xac46c81e, 0x4f414790, 0xd141ed5c, 0xedc29d29, - 0x73c237e5, 0x90c5b86b, 0x0ec512a7, 0x17ccd7ad, 0x89cc7d61, - 0x6acbf2ef, 0xf4cb5823, 0x58d978fa, 0xc6d9d236, 0x25de5db8, - 0xbbdef774, 0xa2d7327e, 0x3cd798b2, 0xdfd0173c, 0x41d0bdf0, - 0xc6f32655, 0x58f38c99, 0xbbf40317, 0x25f4a9db, 0x3cfd6cd1, - 0xa2fdc61d, 0x41fa4993, 0xdffae35f, 0x73e8c386, 0xede8694a, - 0x0eefe6c4, 0x90ef4c08, 0x89e68902, 0x17e623ce, 0xf4e1ac40, - 0x6ae1068c, 0xbba0ebd0, 0x25a0411c, 0xc6a7ce92, 0x58a7645e, - 0x41aea154, 0xdfae0b98, 0x3ca98416, 0xa2a92eda, 0x0ebb0e03, - 0x90bba4cf, 0x73bc2b41, 0xedbc818d, 0xf4b54487, 0x6ab5ee4b, - 0x89b261c5, 0x17b2cb09, 0x909150ac, 0x0e91fa60, 0xed9675ee, - 0x7396df22, 0x6a9f1a28, 0xf49fb0e4, 0x17983f6a, 0x899895a6, - 0x258ab57f, 0xbb8a1fb3, 0x588d903d, 0xc68d3af1, 0xdf84fffb, - 0x41845537, 0xa283dab9, 0x3c837075, 0xda853b53, 0x4485919f, - 0xa7821e11, 0x3982b4dd, 0x208b71d7, 0xbe8bdb1b, 0x5d8c5495, - 0xc38cfe59, 0x6f9ede80, 0xf19e744c, 0x1299fbc2, 0x8c99510e, - 0x95909404, 0x0b903ec8, 0xe897b146, 0x76971b8a, 0xf1b4802f, - 0x6fb42ae3, 0x8cb3a56d, 0x12b30fa1, 0x0bbacaab, 0x95ba6067, - 0x76bdefe9, 0xe8bd4525, 0x44af65fc, 0xdaafcf30, 0x39a840be, - 0xa7a8ea72, 0xbea12f78, 0x20a185b4, 0xc3a60a3a, 0x5da6a0f6, - 0x8ce74daa, 0x12e7e766, 0xf1e068e8, 0x6fe0c224, 0x76e9072e, - 0xe8e9ade2, 0x0bee226c, 0x95ee88a0, 0x39fca879, 0xa7fc02b5, - 0x44fb8d3b, 0xdafb27f7, 0xc3f2e2fd, 0x5df24831, 0xbef5c7bf, - 0x20f56d73, 0xa7d6f6d6, 0x39d65c1a, 0xdad1d394, 0x44d17958, - 0x5dd8bc52, 0xc3d8169e, 0x20df9910, 0xbedf33dc, 0x12cd1305, - 0x8ccdb9c9, 0x6fca3647, 0xf1ca9c8b, 0xe8c35981, 0x76c3f34d, - 0x95c47cc3, 0x0bc4d60f, 0x3747a67a, 0xa9470cb6, 0x4a408338, - 0xd44029f4, 0xcd49ecfe, 0x53494632, 0xb04ec9bc, 0x2e4e6370, - 0x825c43a9, 0x1c5ce965, 0xff5b66eb, 0x615bcc27, 0x7852092d, - 0xe652a3e1, 0x05552c6f, 0x9b5586a3, 0x1c761d06, 0x8276b7ca, - 0x61713844, 0xff719288, 0xe6785782, 0x7878fd4e, 0x9b7f72c0, - 0x057fd80c, 0xa96df8d5, 0x376d5219, 0xd46add97, 0x4a6a775b, - 0x5363b251, 0xcd63189d, 0x2e649713, 0xb0643ddf, 0x6125d083, - 0xff257a4f, 0x1c22f5c1, 0x82225f0d, 0x9b2b9a07, 0x052b30cb, - 0xe62cbf45, 0x782c1589, 0xd43e3550, 0x4a3e9f9c, 0xa9391012, - 0x3739bade, 0x2e307fd4, 0xb030d518, 0x53375a96, 0xcd37f05a, - 0x4a146bff, 0xd414c133, 0x37134ebd, 0xa913e471, 0xb01a217b, - 0x2e1a8bb7, 0xcd1d0439, 0x531daef5, 0xff0f8e2c, 0x610f24e0, - 0x8208ab6e, 0x1c0801a2, 0x0501c4a8, 0x9b016e64, 0x7806e1ea, - 0xe6064b26}}; - -#endif - -#endif - -#if N == 3 - -#if W == 8 - -local const z_crc_t FAR crc_braid_table[][256] = { - {0x00000000, 0x81256527, 0xd93bcc0f, 0x581ea928, 0x69069e5f, - 0xe823fb78, 0xb03d5250, 0x31183777, 0xd20d3cbe, 0x53285999, - 0x0b36f0b1, 0x8a139596, 0xbb0ba2e1, 0x3a2ec7c6, 0x62306eee, - 0xe3150bc9, 0x7f6b7f3d, 0xfe4e1a1a, 0xa650b332, 0x2775d615, - 0x166de162, 0x97488445, 0xcf562d6d, 0x4e73484a, 0xad664383, - 0x2c4326a4, 0x745d8f8c, 0xf578eaab, 0xc460dddc, 0x4545b8fb, - 0x1d5b11d3, 0x9c7e74f4, 0xfed6fe7a, 0x7ff39b5d, 0x27ed3275, - 0xa6c85752, 0x97d06025, 0x16f50502, 0x4eebac2a, 0xcfcec90d, - 0x2cdbc2c4, 0xadfea7e3, 0xf5e00ecb, 0x74c56bec, 0x45dd5c9b, - 0xc4f839bc, 0x9ce69094, 0x1dc3f5b3, 0x81bd8147, 0x0098e460, - 0x58864d48, 0xd9a3286f, 0xe8bb1f18, 0x699e7a3f, 0x3180d317, - 0xb0a5b630, 0x53b0bdf9, 0xd295d8de, 0x8a8b71f6, 0x0bae14d1, - 0x3ab623a6, 0xbb934681, 0xe38defa9, 0x62a88a8e, 0x26dcfab5, - 0xa7f99f92, 0xffe736ba, 0x7ec2539d, 0x4fda64ea, 0xceff01cd, - 0x96e1a8e5, 0x17c4cdc2, 0xf4d1c60b, 0x75f4a32c, 0x2dea0a04, - 0xaccf6f23, 0x9dd75854, 0x1cf23d73, 0x44ec945b, 0xc5c9f17c, - 0x59b78588, 0xd892e0af, 0x808c4987, 0x01a92ca0, 0x30b11bd7, - 0xb1947ef0, 0xe98ad7d8, 0x68afb2ff, 0x8bbab936, 0x0a9fdc11, - 0x52817539, 0xd3a4101e, 0xe2bc2769, 0x6399424e, 0x3b87eb66, - 0xbaa28e41, 0xd80a04cf, 0x592f61e8, 0x0131c8c0, 0x8014ade7, - 0xb10c9a90, 0x3029ffb7, 0x6837569f, 0xe91233b8, 0x0a073871, - 0x8b225d56, 0xd33cf47e, 0x52199159, 0x6301a62e, 0xe224c309, - 0xba3a6a21, 0x3b1f0f06, 0xa7617bf2, 0x26441ed5, 0x7e5ab7fd, - 0xff7fd2da, 0xce67e5ad, 0x4f42808a, 0x175c29a2, 0x96794c85, - 0x756c474c, 0xf449226b, 0xac578b43, 0x2d72ee64, 0x1c6ad913, - 0x9d4fbc34, 0xc551151c, 0x4474703b, 0x4db9f56a, 0xcc9c904d, - 0x94823965, 0x15a75c42, 0x24bf6b35, 0xa59a0e12, 0xfd84a73a, - 0x7ca1c21d, 0x9fb4c9d4, 0x1e91acf3, 0x468f05db, 0xc7aa60fc, - 0xf6b2578b, 0x779732ac, 0x2f899b84, 0xaeacfea3, 0x32d28a57, - 0xb3f7ef70, 0xebe94658, 0x6acc237f, 0x5bd41408, 0xdaf1712f, - 0x82efd807, 0x03cabd20, 0xe0dfb6e9, 0x61fad3ce, 0x39e47ae6, - 0xb8c11fc1, 0x89d928b6, 0x08fc4d91, 0x50e2e4b9, 0xd1c7819e, - 0xb36f0b10, 0x324a6e37, 0x6a54c71f, 0xeb71a238, 0xda69954f, - 0x5b4cf068, 0x03525940, 0x82773c67, 0x616237ae, 0xe0475289, - 0xb859fba1, 0x397c9e86, 0x0864a9f1, 0x8941ccd6, 0xd15f65fe, - 0x507a00d9, 0xcc04742d, 0x4d21110a, 0x153fb822, 0x941add05, - 0xa502ea72, 0x24278f55, 0x7c39267d, 0xfd1c435a, 0x1e094893, - 0x9f2c2db4, 0xc732849c, 0x4617e1bb, 0x770fd6cc, 0xf62ab3eb, - 0xae341ac3, 0x2f117fe4, 0x6b650fdf, 0xea406af8, 0xb25ec3d0, - 0x337ba6f7, 0x02639180, 0x8346f4a7, 0xdb585d8f, 0x5a7d38a8, - 0xb9683361, 0x384d5646, 0x6053ff6e, 0xe1769a49, 0xd06ead3e, - 0x514bc819, 0x09556131, 0x88700416, 0x140e70e2, 0x952b15c5, - 0xcd35bced, 0x4c10d9ca, 0x7d08eebd, 0xfc2d8b9a, 0xa43322b2, - 0x25164795, 0xc6034c5c, 0x4726297b, 0x1f388053, 0x9e1de574, - 0xaf05d203, 0x2e20b724, 0x763e1e0c, 0xf71b7b2b, 0x95b3f1a5, - 0x14969482, 0x4c883daa, 0xcdad588d, 0xfcb56ffa, 0x7d900add, - 0x258ea3f5, 0xa4abc6d2, 0x47becd1b, 0xc69ba83c, 0x9e850114, - 0x1fa06433, 0x2eb85344, 0xaf9d3663, 0xf7839f4b, 0x76a6fa6c, - 0xead88e98, 0x6bfdebbf, 0x33e34297, 0xb2c627b0, 0x83de10c7, - 0x02fb75e0, 0x5ae5dcc8, 0xdbc0b9ef, 0x38d5b226, 0xb9f0d701, - 0xe1ee7e29, 0x60cb1b0e, 0x51d32c79, 0xd0f6495e, 0x88e8e076, - 0x09cd8551}, - {0x00000000, 0x9b73ead4, 0xed96d3e9, 0x76e5393d, 0x005ca193, - 0x9b2f4b47, 0xedca727a, 0x76b998ae, 0x00b94326, 0x9bcaa9f2, - 0xed2f90cf, 0x765c7a1b, 0x00e5e2b5, 0x9b960861, 0xed73315c, - 0x7600db88, 0x0172864c, 0x9a016c98, 0xece455a5, 0x7797bf71, - 0x012e27df, 0x9a5dcd0b, 0xecb8f436, 0x77cb1ee2, 0x01cbc56a, - 0x9ab82fbe, 0xec5d1683, 0x772efc57, 0x019764f9, 0x9ae48e2d, - 0xec01b710, 0x77725dc4, 0x02e50c98, 0x9996e64c, 0xef73df71, - 0x740035a5, 0x02b9ad0b, 0x99ca47df, 0xef2f7ee2, 0x745c9436, - 0x025c4fbe, 0x992fa56a, 0xefca9c57, 0x74b97683, 0x0200ee2d, - 0x997304f9, 0xef963dc4, 0x74e5d710, 0x03978ad4, 0x98e46000, - 0xee01593d, 0x7572b3e9, 0x03cb2b47, 0x98b8c193, 0xee5df8ae, - 0x752e127a, 0x032ec9f2, 0x985d2326, 0xeeb81a1b, 0x75cbf0cf, - 0x03726861, 0x980182b5, 0xeee4bb88, 0x7597515c, 0x05ca1930, - 0x9eb9f3e4, 0xe85ccad9, 0x732f200d, 0x0596b8a3, 0x9ee55277, - 0xe8006b4a, 0x7373819e, 0x05735a16, 0x9e00b0c2, 0xe8e589ff, - 0x7396632b, 0x052ffb85, 0x9e5c1151, 0xe8b9286c, 0x73cac2b8, - 0x04b89f7c, 0x9fcb75a8, 0xe92e4c95, 0x725da641, 0x04e43eef, - 0x9f97d43b, 0xe972ed06, 0x720107d2, 0x0401dc5a, 0x9f72368e, - 0xe9970fb3, 0x72e4e567, 0x045d7dc9, 0x9f2e971d, 0xe9cbae20, - 0x72b844f4, 0x072f15a8, 0x9c5cff7c, 0xeab9c641, 0x71ca2c95, - 0x0773b43b, 0x9c005eef, 0xeae567d2, 0x71968d06, 0x0796568e, - 0x9ce5bc5a, 0xea008567, 0x71736fb3, 0x07caf71d, 0x9cb91dc9, - 0xea5c24f4, 0x712fce20, 0x065d93e4, 0x9d2e7930, 0xebcb400d, - 0x70b8aad9, 0x06013277, 0x9d72d8a3, 0xeb97e19e, 0x70e40b4a, - 0x06e4d0c2, 0x9d973a16, 0xeb72032b, 0x7001e9ff, 0x06b87151, - 0x9dcb9b85, 0xeb2ea2b8, 0x705d486c, 0x0b943260, 0x90e7d8b4, - 0xe602e189, 0x7d710b5d, 0x0bc893f3, 0x90bb7927, 0xe65e401a, - 0x7d2daace, 0x0b2d7146, 0x905e9b92, 0xe6bba2af, 0x7dc8487b, - 0x0b71d0d5, 0x90023a01, 0xe6e7033c, 0x7d94e9e8, 0x0ae6b42c, - 0x91955ef8, 0xe77067c5, 0x7c038d11, 0x0aba15bf, 0x91c9ff6b, - 0xe72cc656, 0x7c5f2c82, 0x0a5ff70a, 0x912c1dde, 0xe7c924e3, - 0x7cbace37, 0x0a035699, 0x9170bc4d, 0xe7958570, 0x7ce66fa4, - 0x09713ef8, 0x9202d42c, 0xe4e7ed11, 0x7f9407c5, 0x092d9f6b, - 0x925e75bf, 0xe4bb4c82, 0x7fc8a656, 0x09c87dde, 0x92bb970a, - 0xe45eae37, 0x7f2d44e3, 0x0994dc4d, 0x92e73699, 0xe4020fa4, - 0x7f71e570, 0x0803b8b4, 0x93705260, 0xe5956b5d, 0x7ee68189, - 0x085f1927, 0x932cf3f3, 0xe5c9cace, 0x7eba201a, 0x08bafb92, - 0x93c91146, 0xe52c287b, 0x7e5fc2af, 0x08e65a01, 0x9395b0d5, - 0xe57089e8, 0x7e03633c, 0x0e5e2b50, 0x952dc184, 0xe3c8f8b9, - 0x78bb126d, 0x0e028ac3, 0x95716017, 0xe394592a, 0x78e7b3fe, - 0x0ee76876, 0x959482a2, 0xe371bb9f, 0x7802514b, 0x0ebbc9e5, - 0x95c82331, 0xe32d1a0c, 0x785ef0d8, 0x0f2cad1c, 0x945f47c8, - 0xe2ba7ef5, 0x79c99421, 0x0f700c8f, 0x9403e65b, 0xe2e6df66, - 0x799535b2, 0x0f95ee3a, 0x94e604ee, 0xe2033dd3, 0x7970d707, - 0x0fc94fa9, 0x94baa57d, 0xe25f9c40, 0x792c7694, 0x0cbb27c8, - 0x97c8cd1c, 0xe12df421, 0x7a5e1ef5, 0x0ce7865b, 0x97946c8f, - 0xe17155b2, 0x7a02bf66, 0x0c0264ee, 0x97718e3a, 0xe194b707, - 0x7ae75dd3, 0x0c5ec57d, 0x972d2fa9, 0xe1c81694, 0x7abbfc40, - 0x0dc9a184, 0x96ba4b50, 0xe05f726d, 0x7b2c98b9, 0x0d950017, - 0x96e6eac3, 0xe003d3fe, 0x7b70392a, 0x0d70e2a2, 0x96030876, - 0xe0e6314b, 0x7b95db9f, 0x0d2c4331, 0x965fa9e5, 0xe0ba90d8, - 0x7bc97a0c}, - {0x00000000, 0x172864c0, 0x2e50c980, 0x3978ad40, 0x5ca19300, - 0x4b89f7c0, 0x72f15a80, 0x65d93e40, 0xb9432600, 0xae6b42c0, - 0x9713ef80, 0x803b8b40, 0xe5e2b500, 0xf2cad1c0, 0xcbb27c80, - 0xdc9a1840, 0xa9f74a41, 0xbedf2e81, 0x87a783c1, 0x908fe701, - 0xf556d941, 0xe27ebd81, 0xdb0610c1, 0xcc2e7401, 0x10b46c41, - 0x079c0881, 0x3ee4a5c1, 0x29ccc101, 0x4c15ff41, 0x5b3d9b81, - 0x624536c1, 0x756d5201, 0x889f92c3, 0x9fb7f603, 0xa6cf5b43, - 0xb1e73f83, 0xd43e01c3, 0xc3166503, 0xfa6ec843, 0xed46ac83, - 0x31dcb4c3, 0x26f4d003, 0x1f8c7d43, 0x08a41983, 0x6d7d27c3, - 0x7a554303, 0x432dee43, 0x54058a83, 0x2168d882, 0x3640bc42, - 0x0f381102, 0x181075c2, 0x7dc94b82, 0x6ae12f42, 0x53998202, - 0x44b1e6c2, 0x982bfe82, 0x8f039a42, 0xb67b3702, 0xa15353c2, - 0xc48a6d82, 0xd3a20942, 0xeadaa402, 0xfdf2c0c2, 0xca4e23c7, - 0xdd664707, 0xe41eea47, 0xf3368e87, 0x96efb0c7, 0x81c7d407, - 0xb8bf7947, 0xaf971d87, 0x730d05c7, 0x64256107, 0x5d5dcc47, - 0x4a75a887, 0x2fac96c7, 0x3884f207, 0x01fc5f47, 0x16d43b87, - 0x63b96986, 0x74910d46, 0x4de9a006, 0x5ac1c4c6, 0x3f18fa86, - 0x28309e46, 0x11483306, 0x066057c6, 0xdafa4f86, 0xcdd22b46, - 0xf4aa8606, 0xe382e2c6, 0x865bdc86, 0x9173b846, 0xa80b1506, - 0xbf2371c6, 0x42d1b104, 0x55f9d5c4, 0x6c817884, 0x7ba91c44, - 0x1e702204, 0x095846c4, 0x3020eb84, 0x27088f44, 0xfb929704, - 0xecbaf3c4, 0xd5c25e84, 0xc2ea3a44, 0xa7330404, 0xb01b60c4, - 0x8963cd84, 0x9e4ba944, 0xeb26fb45, 0xfc0e9f85, 0xc57632c5, - 0xd25e5605, 0xb7876845, 0xa0af0c85, 0x99d7a1c5, 0x8effc505, - 0x5265dd45, 0x454db985, 0x7c3514c5, 0x6b1d7005, 0x0ec44e45, - 0x19ec2a85, 0x209487c5, 0x37bce305, 0x4fed41cf, 0x58c5250f, - 0x61bd884f, 0x7695ec8f, 0x134cd2cf, 0x0464b60f, 0x3d1c1b4f, - 0x2a347f8f, 0xf6ae67cf, 0xe186030f, 0xd8feae4f, 0xcfd6ca8f, - 0xaa0ff4cf, 0xbd27900f, 0x845f3d4f, 0x9377598f, 0xe61a0b8e, - 0xf1326f4e, 0xc84ac20e, 0xdf62a6ce, 0xbabb988e, 0xad93fc4e, - 0x94eb510e, 0x83c335ce, 0x5f592d8e, 0x4871494e, 0x7109e40e, - 0x662180ce, 0x03f8be8e, 0x14d0da4e, 0x2da8770e, 0x3a8013ce, - 0xc772d30c, 0xd05ab7cc, 0xe9221a8c, 0xfe0a7e4c, 0x9bd3400c, - 0x8cfb24cc, 0xb583898c, 0xa2abed4c, 0x7e31f50c, 0x691991cc, - 0x50613c8c, 0x4749584c, 0x2290660c, 0x35b802cc, 0x0cc0af8c, - 0x1be8cb4c, 0x6e85994d, 0x79adfd8d, 0x40d550cd, 0x57fd340d, - 0x32240a4d, 0x250c6e8d, 0x1c74c3cd, 0x0b5ca70d, 0xd7c6bf4d, - 0xc0eedb8d, 0xf99676cd, 0xeebe120d, 0x8b672c4d, 0x9c4f488d, - 0xa537e5cd, 0xb21f810d, 0x85a36208, 0x928b06c8, 0xabf3ab88, - 0xbcdbcf48, 0xd902f108, 0xce2a95c8, 0xf7523888, 0xe07a5c48, - 0x3ce04408, 0x2bc820c8, 0x12b08d88, 0x0598e948, 0x6041d708, - 0x7769b3c8, 0x4e111e88, 0x59397a48, 0x2c542849, 0x3b7c4c89, - 0x0204e1c9, 0x152c8509, 0x70f5bb49, 0x67dddf89, 0x5ea572c9, - 0x498d1609, 0x95170e49, 0x823f6a89, 0xbb47c7c9, 0xac6fa309, - 0xc9b69d49, 0xde9ef989, 0xe7e654c9, 0xf0ce3009, 0x0d3cf0cb, - 0x1a14940b, 0x236c394b, 0x34445d8b, 0x519d63cb, 0x46b5070b, - 0x7fcdaa4b, 0x68e5ce8b, 0xb47fd6cb, 0xa357b20b, 0x9a2f1f4b, - 0x8d077b8b, 0xe8de45cb, 0xfff6210b, 0xc68e8c4b, 0xd1a6e88b, - 0xa4cbba8a, 0xb3e3de4a, 0x8a9b730a, 0x9db317ca, 0xf86a298a, - 0xef424d4a, 0xd63ae00a, 0xc11284ca, 0x1d889c8a, 0x0aa0f84a, - 0x33d8550a, 0x24f031ca, 0x41290f8a, 0x56016b4a, 0x6f79c60a, - 0x7851a2ca}, - {0x00000000, 0x9fda839e, 0xe4c4017d, 0x7b1e82e3, 0x12f904bb, - 0x8d238725, 0xf63d05c6, 0x69e78658, 0x25f20976, 0xba288ae8, - 0xc136080b, 0x5eec8b95, 0x370b0dcd, 0xa8d18e53, 0xd3cf0cb0, - 0x4c158f2e, 0x4be412ec, 0xd43e9172, 0xaf201391, 0x30fa900f, - 0x591d1657, 0xc6c795c9, 0xbdd9172a, 0x220394b4, 0x6e161b9a, - 0xf1cc9804, 0x8ad21ae7, 0x15089979, 0x7cef1f21, 0xe3359cbf, - 0x982b1e5c, 0x07f19dc2, 0x97c825d8, 0x0812a646, 0x730c24a5, - 0xecd6a73b, 0x85312163, 0x1aeba2fd, 0x61f5201e, 0xfe2fa380, - 0xb23a2cae, 0x2de0af30, 0x56fe2dd3, 0xc924ae4d, 0xa0c32815, - 0x3f19ab8b, 0x44072968, 0xdbddaaf6, 0xdc2c3734, 0x43f6b4aa, - 0x38e83649, 0xa732b5d7, 0xced5338f, 0x510fb011, 0x2a1132f2, - 0xb5cbb16c, 0xf9de3e42, 0x6604bddc, 0x1d1a3f3f, 0x82c0bca1, - 0xeb273af9, 0x74fdb967, 0x0fe33b84, 0x9039b81a, 0xf4e14df1, - 0x6b3bce6f, 0x10254c8c, 0x8fffcf12, 0xe618494a, 0x79c2cad4, - 0x02dc4837, 0x9d06cba9, 0xd1134487, 0x4ec9c719, 0x35d745fa, - 0xaa0dc664, 0xc3ea403c, 0x5c30c3a2, 0x272e4141, 0xb8f4c2df, - 0xbf055f1d, 0x20dfdc83, 0x5bc15e60, 0xc41bddfe, 0xadfc5ba6, - 0x3226d838, 0x49385adb, 0xd6e2d945, 0x9af7566b, 0x052dd5f5, - 0x7e335716, 0xe1e9d488, 0x880e52d0, 0x17d4d14e, 0x6cca53ad, - 0xf310d033, 0x63296829, 0xfcf3ebb7, 0x87ed6954, 0x1837eaca, - 0x71d06c92, 0xee0aef0c, 0x95146def, 0x0aceee71, 0x46db615f, - 0xd901e2c1, 0xa21f6022, 0x3dc5e3bc, 0x542265e4, 0xcbf8e67a, - 0xb0e66499, 0x2f3ce707, 0x28cd7ac5, 0xb717f95b, 0xcc097bb8, - 0x53d3f826, 0x3a347e7e, 0xa5eefde0, 0xdef07f03, 0x412afc9d, - 0x0d3f73b3, 0x92e5f02d, 0xe9fb72ce, 0x7621f150, 0x1fc67708, - 0x801cf496, 0xfb027675, 0x64d8f5eb, 0x32b39da3, 0xad691e3d, - 0xd6779cde, 0x49ad1f40, 0x204a9918, 0xbf901a86, 0xc48e9865, - 0x5b541bfb, 0x174194d5, 0x889b174b, 0xf38595a8, 0x6c5f1636, - 0x05b8906e, 0x9a6213f0, 0xe17c9113, 0x7ea6128d, 0x79578f4f, - 0xe68d0cd1, 0x9d938e32, 0x02490dac, 0x6bae8bf4, 0xf474086a, - 0x8f6a8a89, 0x10b00917, 0x5ca58639, 0xc37f05a7, 0xb8618744, - 0x27bb04da, 0x4e5c8282, 0xd186011c, 0xaa9883ff, 0x35420061, - 0xa57bb87b, 0x3aa13be5, 0x41bfb906, 0xde653a98, 0xb782bcc0, - 0x28583f5e, 0x5346bdbd, 0xcc9c3e23, 0x8089b10d, 0x1f533293, - 0x644db070, 0xfb9733ee, 0x9270b5b6, 0x0daa3628, 0x76b4b4cb, - 0xe96e3755, 0xee9faa97, 0x71452909, 0x0a5babea, 0x95812874, - 0xfc66ae2c, 0x63bc2db2, 0x18a2af51, 0x87782ccf, 0xcb6da3e1, - 0x54b7207f, 0x2fa9a29c, 0xb0732102, 0xd994a75a, 0x464e24c4, - 0x3d50a627, 0xa28a25b9, 0xc652d052, 0x598853cc, 0x2296d12f, - 0xbd4c52b1, 0xd4abd4e9, 0x4b715777, 0x306fd594, 0xafb5560a, - 0xe3a0d924, 0x7c7a5aba, 0x0764d859, 0x98be5bc7, 0xf159dd9f, - 0x6e835e01, 0x159ddce2, 0x8a475f7c, 0x8db6c2be, 0x126c4120, - 0x6972c3c3, 0xf6a8405d, 0x9f4fc605, 0x0095459b, 0x7b8bc778, - 0xe45144e6, 0xa844cbc8, 0x379e4856, 0x4c80cab5, 0xd35a492b, - 0xbabdcf73, 0x25674ced, 0x5e79ce0e, 0xc1a34d90, 0x519af58a, - 0xce407614, 0xb55ef4f7, 0x2a847769, 0x4363f131, 0xdcb972af, - 0xa7a7f04c, 0x387d73d2, 0x7468fcfc, 0xebb27f62, 0x90acfd81, - 0x0f767e1f, 0x6691f847, 0xf94b7bd9, 0x8255f93a, 0x1d8f7aa4, - 0x1a7ee766, 0x85a464f8, 0xfebae61b, 0x61606585, 0x0887e3dd, - 0x975d6043, 0xec43e2a0, 0x7399613e, 0x3f8cee10, 0xa0566d8e, - 0xdb48ef6d, 0x44926cf3, 0x2d75eaab, 0xb2af6935, 0xc9b1ebd6, - 0x566b6848}, - {0x00000000, 0x65673b46, 0xcace768c, 0xafa94dca, 0x4eedeb59, - 0x2b8ad01f, 0x84239dd5, 0xe144a693, 0x9ddbd6b2, 0xf8bcedf4, - 0x5715a03e, 0x32729b78, 0xd3363deb, 0xb65106ad, 0x19f84b67, - 0x7c9f7021, 0xe0c6ab25, 0x85a19063, 0x2a08dda9, 0x4f6fe6ef, - 0xae2b407c, 0xcb4c7b3a, 0x64e536f0, 0x01820db6, 0x7d1d7d97, - 0x187a46d1, 0xb7d30b1b, 0xd2b4305d, 0x33f096ce, 0x5697ad88, - 0xf93ee042, 0x9c59db04, 0x1afc500b, 0x7f9b6b4d, 0xd0322687, - 0xb5551dc1, 0x5411bb52, 0x31768014, 0x9edfcdde, 0xfbb8f698, - 0x872786b9, 0xe240bdff, 0x4de9f035, 0x288ecb73, 0xc9ca6de0, - 0xacad56a6, 0x03041b6c, 0x6663202a, 0xfa3afb2e, 0x9f5dc068, - 0x30f48da2, 0x5593b6e4, 0xb4d71077, 0xd1b02b31, 0x7e1966fb, - 0x1b7e5dbd, 0x67e12d9c, 0x028616da, 0xad2f5b10, 0xc8486056, - 0x290cc6c5, 0x4c6bfd83, 0xe3c2b049, 0x86a58b0f, 0x35f8a016, - 0x509f9b50, 0xff36d69a, 0x9a51eddc, 0x7b154b4f, 0x1e727009, - 0xb1db3dc3, 0xd4bc0685, 0xa82376a4, 0xcd444de2, 0x62ed0028, - 0x078a3b6e, 0xe6ce9dfd, 0x83a9a6bb, 0x2c00eb71, 0x4967d037, - 0xd53e0b33, 0xb0593075, 0x1ff07dbf, 0x7a9746f9, 0x9bd3e06a, - 0xfeb4db2c, 0x511d96e6, 0x347aada0, 0x48e5dd81, 0x2d82e6c7, - 0x822bab0d, 0xe74c904b, 0x060836d8, 0x636f0d9e, 0xccc64054, - 0xa9a17b12, 0x2f04f01d, 0x4a63cb5b, 0xe5ca8691, 0x80adbdd7, - 0x61e91b44, 0x048e2002, 0xab276dc8, 0xce40568e, 0xb2df26af, - 0xd7b81de9, 0x78115023, 0x1d766b65, 0xfc32cdf6, 0x9955f6b0, - 0x36fcbb7a, 0x539b803c, 0xcfc25b38, 0xaaa5607e, 0x050c2db4, - 0x606b16f2, 0x812fb061, 0xe4488b27, 0x4be1c6ed, 0x2e86fdab, - 0x52198d8a, 0x377eb6cc, 0x98d7fb06, 0xfdb0c040, 0x1cf466d3, - 0x79935d95, 0xd63a105f, 0xb35d2b19, 0x6bf1402c, 0x0e967b6a, - 0xa13f36a0, 0xc4580de6, 0x251cab75, 0x407b9033, 0xefd2ddf9, - 0x8ab5e6bf, 0xf62a969e, 0x934dadd8, 0x3ce4e012, 0x5983db54, - 0xb8c77dc7, 0xdda04681, 0x72090b4b, 0x176e300d, 0x8b37eb09, - 0xee50d04f, 0x41f99d85, 0x249ea6c3, 0xc5da0050, 0xa0bd3b16, - 0x0f1476dc, 0x6a734d9a, 0x16ec3dbb, 0x738b06fd, 0xdc224b37, - 0xb9457071, 0x5801d6e2, 0x3d66eda4, 0x92cfa06e, 0xf7a89b28, - 0x710d1027, 0x146a2b61, 0xbbc366ab, 0xdea45ded, 0x3fe0fb7e, - 0x5a87c038, 0xf52e8df2, 0x9049b6b4, 0xecd6c695, 0x89b1fdd3, - 0x2618b019, 0x437f8b5f, 0xa23b2dcc, 0xc75c168a, 0x68f55b40, - 0x0d926006, 0x91cbbb02, 0xf4ac8044, 0x5b05cd8e, 0x3e62f6c8, - 0xdf26505b, 0xba416b1d, 0x15e826d7, 0x708f1d91, 0x0c106db0, - 0x697756f6, 0xc6de1b3c, 0xa3b9207a, 0x42fd86e9, 0x279abdaf, - 0x8833f065, 0xed54cb23, 0x5e09e03a, 0x3b6edb7c, 0x94c796b6, - 0xf1a0adf0, 0x10e40b63, 0x75833025, 0xda2a7def, 0xbf4d46a9, - 0xc3d23688, 0xa6b50dce, 0x091c4004, 0x6c7b7b42, 0x8d3fddd1, - 0xe858e697, 0x47f1ab5d, 0x2296901b, 0xbecf4b1f, 0xdba87059, - 0x74013d93, 0x116606d5, 0xf022a046, 0x95459b00, 0x3aecd6ca, - 0x5f8bed8c, 0x23149dad, 0x4673a6eb, 0xe9daeb21, 0x8cbdd067, - 0x6df976f4, 0x089e4db2, 0xa7370078, 0xc2503b3e, 0x44f5b031, - 0x21928b77, 0x8e3bc6bd, 0xeb5cfdfb, 0x0a185b68, 0x6f7f602e, - 0xc0d62de4, 0xa5b116a2, 0xd92e6683, 0xbc495dc5, 0x13e0100f, - 0x76872b49, 0x97c38dda, 0xf2a4b69c, 0x5d0dfb56, 0x386ac010, - 0xa4331b14, 0xc1542052, 0x6efd6d98, 0x0b9a56de, 0xeadef04d, - 0x8fb9cb0b, 0x201086c1, 0x4577bd87, 0x39e8cda6, 0x5c8ff6e0, - 0xf326bb2a, 0x9641806c, 0x770526ff, 0x12621db9, 0xbdcb5073, - 0xd8ac6b35}, - {0x00000000, 0xd7e28058, 0x74b406f1, 0xa35686a9, 0xe9680de2, - 0x3e8a8dba, 0x9ddc0b13, 0x4a3e8b4b, 0x09a11d85, 0xde439ddd, - 0x7d151b74, 0xaaf79b2c, 0xe0c91067, 0x372b903f, 0x947d1696, - 0x439f96ce, 0x13423b0a, 0xc4a0bb52, 0x67f63dfb, 0xb014bda3, - 0xfa2a36e8, 0x2dc8b6b0, 0x8e9e3019, 0x597cb041, 0x1ae3268f, - 0xcd01a6d7, 0x6e57207e, 0xb9b5a026, 0xf38b2b6d, 0x2469ab35, - 0x873f2d9c, 0x50ddadc4, 0x26847614, 0xf166f64c, 0x523070e5, - 0x85d2f0bd, 0xcfec7bf6, 0x180efbae, 0xbb587d07, 0x6cbafd5f, - 0x2f256b91, 0xf8c7ebc9, 0x5b916d60, 0x8c73ed38, 0xc64d6673, - 0x11afe62b, 0xb2f96082, 0x651be0da, 0x35c64d1e, 0xe224cd46, - 0x41724bef, 0x9690cbb7, 0xdcae40fc, 0x0b4cc0a4, 0xa81a460d, - 0x7ff8c655, 0x3c67509b, 0xeb85d0c3, 0x48d3566a, 0x9f31d632, - 0xd50f5d79, 0x02eddd21, 0xa1bb5b88, 0x7659dbd0, 0x4d08ec28, - 0x9aea6c70, 0x39bcead9, 0xee5e6a81, 0xa460e1ca, 0x73826192, - 0xd0d4e73b, 0x07366763, 0x44a9f1ad, 0x934b71f5, 0x301df75c, - 0xe7ff7704, 0xadc1fc4f, 0x7a237c17, 0xd975fabe, 0x0e977ae6, - 0x5e4ad722, 0x89a8577a, 0x2afed1d3, 0xfd1c518b, 0xb722dac0, - 0x60c05a98, 0xc396dc31, 0x14745c69, 0x57ebcaa7, 0x80094aff, - 0x235fcc56, 0xf4bd4c0e, 0xbe83c745, 0x6961471d, 0xca37c1b4, - 0x1dd541ec, 0x6b8c9a3c, 0xbc6e1a64, 0x1f389ccd, 0xc8da1c95, - 0x82e497de, 0x55061786, 0xf650912f, 0x21b21177, 0x622d87b9, - 0xb5cf07e1, 0x16998148, 0xc17b0110, 0x8b458a5b, 0x5ca70a03, - 0xfff18caa, 0x28130cf2, 0x78cea136, 0xaf2c216e, 0x0c7aa7c7, - 0xdb98279f, 0x91a6acd4, 0x46442c8c, 0xe512aa25, 0x32f02a7d, - 0x716fbcb3, 0xa68d3ceb, 0x05dbba42, 0xd2393a1a, 0x9807b151, - 0x4fe53109, 0xecb3b7a0, 0x3b5137f8, 0x9a11d850, 0x4df35808, - 0xeea5dea1, 0x39475ef9, 0x7379d5b2, 0xa49b55ea, 0x07cdd343, - 0xd02f531b, 0x93b0c5d5, 0x4452458d, 0xe704c324, 0x30e6437c, - 0x7ad8c837, 0xad3a486f, 0x0e6ccec6, 0xd98e4e9e, 0x8953e35a, - 0x5eb16302, 0xfde7e5ab, 0x2a0565f3, 0x603beeb8, 0xb7d96ee0, - 0x148fe849, 0xc36d6811, 0x80f2fedf, 0x57107e87, 0xf446f82e, - 0x23a47876, 0x699af33d, 0xbe787365, 0x1d2ef5cc, 0xcacc7594, - 0xbc95ae44, 0x6b772e1c, 0xc821a8b5, 0x1fc328ed, 0x55fda3a6, - 0x821f23fe, 0x2149a557, 0xf6ab250f, 0xb534b3c1, 0x62d63399, - 0xc180b530, 0x16623568, 0x5c5cbe23, 0x8bbe3e7b, 0x28e8b8d2, - 0xff0a388a, 0xafd7954e, 0x78351516, 0xdb6393bf, 0x0c8113e7, - 0x46bf98ac, 0x915d18f4, 0x320b9e5d, 0xe5e91e05, 0xa67688cb, - 0x71940893, 0xd2c28e3a, 0x05200e62, 0x4f1e8529, 0x98fc0571, - 0x3baa83d8, 0xec480380, 0xd7193478, 0x00fbb420, 0xa3ad3289, - 0x744fb2d1, 0x3e71399a, 0xe993b9c2, 0x4ac53f6b, 0x9d27bf33, - 0xdeb829fd, 0x095aa9a5, 0xaa0c2f0c, 0x7deeaf54, 0x37d0241f, - 0xe032a447, 0x436422ee, 0x9486a2b6, 0xc45b0f72, 0x13b98f2a, - 0xb0ef0983, 0x670d89db, 0x2d330290, 0xfad182c8, 0x59870461, - 0x8e658439, 0xcdfa12f7, 0x1a1892af, 0xb94e1406, 0x6eac945e, - 0x24921f15, 0xf3709f4d, 0x502619e4, 0x87c499bc, 0xf19d426c, - 0x267fc234, 0x8529449d, 0x52cbc4c5, 0x18f54f8e, 0xcf17cfd6, - 0x6c41497f, 0xbba3c927, 0xf83c5fe9, 0x2fdedfb1, 0x8c885918, - 0x5b6ad940, 0x1154520b, 0xc6b6d253, 0x65e054fa, 0xb202d4a2, - 0xe2df7966, 0x353df93e, 0x966b7f97, 0x4189ffcf, 0x0bb77484, - 0xdc55f4dc, 0x7f037275, 0xa8e1f22d, 0xeb7e64e3, 0x3c9ce4bb, - 0x9fca6212, 0x4828e24a, 0x02166901, 0xd5f4e959, 0x76a26ff0, - 0xa140efa8}, - {0x00000000, 0xef52b6e1, 0x05d46b83, 0xea86dd62, 0x0ba8d706, - 0xe4fa61e7, 0x0e7cbc85, 0xe12e0a64, 0x1751ae0c, 0xf80318ed, - 0x1285c58f, 0xfdd7736e, 0x1cf9790a, 0xf3abcfeb, 0x192d1289, - 0xf67fa468, 0x2ea35c18, 0xc1f1eaf9, 0x2b77379b, 0xc425817a, - 0x250b8b1e, 0xca593dff, 0x20dfe09d, 0xcf8d567c, 0x39f2f214, - 0xd6a044f5, 0x3c269997, 0xd3742f76, 0x325a2512, 0xdd0893f3, - 0x378e4e91, 0xd8dcf870, 0x5d46b830, 0xb2140ed1, 0x5892d3b3, - 0xb7c06552, 0x56ee6f36, 0xb9bcd9d7, 0x533a04b5, 0xbc68b254, - 0x4a17163c, 0xa545a0dd, 0x4fc37dbf, 0xa091cb5e, 0x41bfc13a, - 0xaeed77db, 0x446baab9, 0xab391c58, 0x73e5e428, 0x9cb752c9, - 0x76318fab, 0x9963394a, 0x784d332e, 0x971f85cf, 0x7d9958ad, - 0x92cbee4c, 0x64b44a24, 0x8be6fcc5, 0x616021a7, 0x8e329746, - 0x6f1c9d22, 0x804e2bc3, 0x6ac8f6a1, 0x859a4040, 0xba8d7060, - 0x55dfc681, 0xbf591be3, 0x500bad02, 0xb125a766, 0x5e771187, - 0xb4f1cce5, 0x5ba37a04, 0xaddcde6c, 0x428e688d, 0xa808b5ef, - 0x475a030e, 0xa674096a, 0x4926bf8b, 0xa3a062e9, 0x4cf2d408, - 0x942e2c78, 0x7b7c9a99, 0x91fa47fb, 0x7ea8f11a, 0x9f86fb7e, - 0x70d44d9f, 0x9a5290fd, 0x7500261c, 0x837f8274, 0x6c2d3495, - 0x86abe9f7, 0x69f95f16, 0x88d75572, 0x6785e393, 0x8d033ef1, - 0x62518810, 0xe7cbc850, 0x08997eb1, 0xe21fa3d3, 0x0d4d1532, - 0xec631f56, 0x0331a9b7, 0xe9b774d5, 0x06e5c234, 0xf09a665c, - 0x1fc8d0bd, 0xf54e0ddf, 0x1a1cbb3e, 0xfb32b15a, 0x146007bb, - 0xfee6dad9, 0x11b46c38, 0xc9689448, 0x263a22a9, 0xccbcffcb, - 0x23ee492a, 0xc2c0434e, 0x2d92f5af, 0xc71428cd, 0x28469e2c, - 0xde393a44, 0x316b8ca5, 0xdbed51c7, 0x34bfe726, 0xd591ed42, - 0x3ac35ba3, 0xd04586c1, 0x3f173020, 0xae6be681, 0x41395060, - 0xabbf8d02, 0x44ed3be3, 0xa5c33187, 0x4a918766, 0xa0175a04, - 0x4f45ece5, 0xb93a488d, 0x5668fe6c, 0xbcee230e, 0x53bc95ef, - 0xb2929f8b, 0x5dc0296a, 0xb746f408, 0x581442e9, 0x80c8ba99, - 0x6f9a0c78, 0x851cd11a, 0x6a4e67fb, 0x8b606d9f, 0x6432db7e, - 0x8eb4061c, 0x61e6b0fd, 0x97991495, 0x78cba274, 0x924d7f16, - 0x7d1fc9f7, 0x9c31c393, 0x73637572, 0x99e5a810, 0x76b71ef1, - 0xf32d5eb1, 0x1c7fe850, 0xf6f93532, 0x19ab83d3, 0xf88589b7, - 0x17d73f56, 0xfd51e234, 0x120354d5, 0xe47cf0bd, 0x0b2e465c, - 0xe1a89b3e, 0x0efa2ddf, 0xefd427bb, 0x0086915a, 0xea004c38, - 0x0552fad9, 0xdd8e02a9, 0x32dcb448, 0xd85a692a, 0x3708dfcb, - 0xd626d5af, 0x3974634e, 0xd3f2be2c, 0x3ca008cd, 0xcadfaca5, - 0x258d1a44, 0xcf0bc726, 0x205971c7, 0xc1777ba3, 0x2e25cd42, - 0xc4a31020, 0x2bf1a6c1, 0x14e696e1, 0xfbb42000, 0x1132fd62, - 0xfe604b83, 0x1f4e41e7, 0xf01cf706, 0x1a9a2a64, 0xf5c89c85, - 0x03b738ed, 0xece58e0c, 0x0663536e, 0xe931e58f, 0x081fefeb, - 0xe74d590a, 0x0dcb8468, 0xe2993289, 0x3a45caf9, 0xd5177c18, - 0x3f91a17a, 0xd0c3179b, 0x31ed1dff, 0xdebfab1e, 0x3439767c, - 0xdb6bc09d, 0x2d1464f5, 0xc246d214, 0x28c00f76, 0xc792b997, - 0x26bcb3f3, 0xc9ee0512, 0x2368d870, 0xcc3a6e91, 0x49a02ed1, - 0xa6f29830, 0x4c744552, 0xa326f3b3, 0x4208f9d7, 0xad5a4f36, - 0x47dc9254, 0xa88e24b5, 0x5ef180dd, 0xb1a3363c, 0x5b25eb5e, - 0xb4775dbf, 0x555957db, 0xba0be13a, 0x508d3c58, 0xbfdf8ab9, - 0x670372c9, 0x8851c428, 0x62d7194a, 0x8d85afab, 0x6caba5cf, - 0x83f9132e, 0x697fce4c, 0x862d78ad, 0x7052dcc5, 0x9f006a24, - 0x7586b746, 0x9ad401a7, 0x7bfa0bc3, 0x94a8bd22, 0x7e2e6040, - 0x917cd6a1}, - {0x00000000, 0x87a6cb43, 0xd43c90c7, 0x539a5b84, 0x730827cf, - 0xf4aeec8c, 0xa734b708, 0x20927c4b, 0xe6104f9e, 0x61b684dd, - 0x322cdf59, 0xb58a141a, 0x95186851, 0x12bea312, 0x4124f896, - 0xc68233d5, 0x1751997d, 0x90f7523e, 0xc36d09ba, 0x44cbc2f9, - 0x6459beb2, 0xe3ff75f1, 0xb0652e75, 0x37c3e536, 0xf141d6e3, - 0x76e71da0, 0x257d4624, 0xa2db8d67, 0x8249f12c, 0x05ef3a6f, - 0x567561eb, 0xd1d3aaa8, 0x2ea332fa, 0xa905f9b9, 0xfa9fa23d, - 0x7d39697e, 0x5dab1535, 0xda0dde76, 0x899785f2, 0x0e314eb1, - 0xc8b37d64, 0x4f15b627, 0x1c8feda3, 0x9b2926e0, 0xbbbb5aab, - 0x3c1d91e8, 0x6f87ca6c, 0xe821012f, 0x39f2ab87, 0xbe5460c4, - 0xedce3b40, 0x6a68f003, 0x4afa8c48, 0xcd5c470b, 0x9ec61c8f, - 0x1960d7cc, 0xdfe2e419, 0x58442f5a, 0x0bde74de, 0x8c78bf9d, - 0xaceac3d6, 0x2b4c0895, 0x78d65311, 0xff709852, 0x5d4665f4, - 0xdae0aeb7, 0x897af533, 0x0edc3e70, 0x2e4e423b, 0xa9e88978, - 0xfa72d2fc, 0x7dd419bf, 0xbb562a6a, 0x3cf0e129, 0x6f6abaad, - 0xe8cc71ee, 0xc85e0da5, 0x4ff8c6e6, 0x1c629d62, 0x9bc45621, - 0x4a17fc89, 0xcdb137ca, 0x9e2b6c4e, 0x198da70d, 0x391fdb46, - 0xbeb91005, 0xed234b81, 0x6a8580c2, 0xac07b317, 0x2ba17854, - 0x783b23d0, 0xff9de893, 0xdf0f94d8, 0x58a95f9b, 0x0b33041f, - 0x8c95cf5c, 0x73e5570e, 0xf4439c4d, 0xa7d9c7c9, 0x207f0c8a, - 0x00ed70c1, 0x874bbb82, 0xd4d1e006, 0x53772b45, 0x95f51890, - 0x1253d3d3, 0x41c98857, 0xc66f4314, 0xe6fd3f5f, 0x615bf41c, - 0x32c1af98, 0xb56764db, 0x64b4ce73, 0xe3120530, 0xb0885eb4, - 0x372e95f7, 0x17bce9bc, 0x901a22ff, 0xc380797b, 0x4426b238, - 0x82a481ed, 0x05024aae, 0x5698112a, 0xd13eda69, 0xf1aca622, - 0x760a6d61, 0x259036e5, 0xa236fda6, 0xba8ccbe8, 0x3d2a00ab, - 0x6eb05b2f, 0xe916906c, 0xc984ec27, 0x4e222764, 0x1db87ce0, - 0x9a1eb7a3, 0x5c9c8476, 0xdb3a4f35, 0x88a014b1, 0x0f06dff2, - 0x2f94a3b9, 0xa83268fa, 0xfba8337e, 0x7c0ef83d, 0xaddd5295, - 0x2a7b99d6, 0x79e1c252, 0xfe470911, 0xded5755a, 0x5973be19, - 0x0ae9e59d, 0x8d4f2ede, 0x4bcd1d0b, 0xcc6bd648, 0x9ff18dcc, - 0x1857468f, 0x38c53ac4, 0xbf63f187, 0xecf9aa03, 0x6b5f6140, - 0x942ff912, 0x13893251, 0x401369d5, 0xc7b5a296, 0xe727dedd, - 0x6081159e, 0x331b4e1a, 0xb4bd8559, 0x723fb68c, 0xf5997dcf, - 0xa603264b, 0x21a5ed08, 0x01379143, 0x86915a00, 0xd50b0184, - 0x52adcac7, 0x837e606f, 0x04d8ab2c, 0x5742f0a8, 0xd0e43beb, - 0xf07647a0, 0x77d08ce3, 0x244ad767, 0xa3ec1c24, 0x656e2ff1, - 0xe2c8e4b2, 0xb152bf36, 0x36f47475, 0x1666083e, 0x91c0c37d, - 0xc25a98f9, 0x45fc53ba, 0xe7caae1c, 0x606c655f, 0x33f63edb, - 0xb450f598, 0x94c289d3, 0x13644290, 0x40fe1914, 0xc758d257, - 0x01dae182, 0x867c2ac1, 0xd5e67145, 0x5240ba06, 0x72d2c64d, - 0xf5740d0e, 0xa6ee568a, 0x21489dc9, 0xf09b3761, 0x773dfc22, - 0x24a7a7a6, 0xa3016ce5, 0x839310ae, 0x0435dbed, 0x57af8069, - 0xd0094b2a, 0x168b78ff, 0x912db3bc, 0xc2b7e838, 0x4511237b, - 0x65835f30, 0xe2259473, 0xb1bfcff7, 0x361904b4, 0xc9699ce6, - 0x4ecf57a5, 0x1d550c21, 0x9af3c762, 0xba61bb29, 0x3dc7706a, - 0x6e5d2bee, 0xe9fbe0ad, 0x2f79d378, 0xa8df183b, 0xfb4543bf, - 0x7ce388fc, 0x5c71f4b7, 0xdbd73ff4, 0x884d6470, 0x0febaf33, - 0xde38059b, 0x599eced8, 0x0a04955c, 0x8da25e1f, 0xad302254, - 0x2a96e917, 0x790cb293, 0xfeaa79d0, 0x38284a05, 0xbf8e8146, - 0xec14dac2, 0x6bb21181, 0x4b206dca, 0xcc86a689, 0x9f1cfd0d, - 0x18ba364e}}; - -local const z_word_t FAR crc_braid_big_table[][256] = { - {0x0000000000000000, 0x43cba68700000000, 0xc7903cd400000000, - 0x845b9a5300000000, 0xcf27087300000000, 0x8cecaef400000000, - 0x08b734a700000000, 0x4b7c922000000000, 0x9e4f10e600000000, - 0xdd84b66100000000, 0x59df2c3200000000, 0x1a148ab500000000, - 0x5168189500000000, 0x12a3be1200000000, 0x96f8244100000000, - 0xd53382c600000000, 0x7d99511700000000, 0x3e52f79000000000, - 0xba096dc300000000, 0xf9c2cb4400000000, 0xb2be596400000000, - 0xf175ffe300000000, 0x752e65b000000000, 0x36e5c33700000000, - 0xe3d641f100000000, 0xa01de77600000000, 0x24467d2500000000, - 0x678ddba200000000, 0x2cf1498200000000, 0x6f3aef0500000000, - 0xeb61755600000000, 0xa8aad3d100000000, 0xfa32a32e00000000, - 0xb9f905a900000000, 0x3da29ffa00000000, 0x7e69397d00000000, - 0x3515ab5d00000000, 0x76de0dda00000000, 0xf285978900000000, - 0xb14e310e00000000, 0x647db3c800000000, 0x27b6154f00000000, - 0xa3ed8f1c00000000, 0xe026299b00000000, 0xab5abbbb00000000, - 0xe8911d3c00000000, 0x6cca876f00000000, 0x2f0121e800000000, - 0x87abf23900000000, 0xc46054be00000000, 0x403bceed00000000, - 0x03f0686a00000000, 0x488cfa4a00000000, 0x0b475ccd00000000, - 0x8f1cc69e00000000, 0xccd7601900000000, 0x19e4e2df00000000, - 0x5a2f445800000000, 0xde74de0b00000000, 0x9dbf788c00000000, - 0xd6c3eaac00000000, 0x95084c2b00000000, 0x1153d67800000000, - 0x529870ff00000000, 0xf465465d00000000, 0xb7aee0da00000000, - 0x33f57a8900000000, 0x703edc0e00000000, 0x3b424e2e00000000, - 0x7889e8a900000000, 0xfcd272fa00000000, 0xbf19d47d00000000, - 0x6a2a56bb00000000, 0x29e1f03c00000000, 0xadba6a6f00000000, - 0xee71cce800000000, 0xa50d5ec800000000, 0xe6c6f84f00000000, - 0x629d621c00000000, 0x2156c49b00000000, 0x89fc174a00000000, - 0xca37b1cd00000000, 0x4e6c2b9e00000000, 0x0da78d1900000000, - 0x46db1f3900000000, 0x0510b9be00000000, 0x814b23ed00000000, - 0xc280856a00000000, 0x17b307ac00000000, 0x5478a12b00000000, - 0xd0233b7800000000, 0x93e89dff00000000, 0xd8940fdf00000000, - 0x9b5fa95800000000, 0x1f04330b00000000, 0x5ccf958c00000000, - 0x0e57e57300000000, 0x4d9c43f400000000, 0xc9c7d9a700000000, - 0x8a0c7f2000000000, 0xc170ed0000000000, 0x82bb4b8700000000, - 0x06e0d1d400000000, 0x452b775300000000, 0x9018f59500000000, - 0xd3d3531200000000, 0x5788c94100000000, 0x14436fc600000000, - 0x5f3ffde600000000, 0x1cf45b6100000000, 0x98afc13200000000, - 0xdb6467b500000000, 0x73ceb46400000000, 0x300512e300000000, - 0xb45e88b000000000, 0xf7952e3700000000, 0xbce9bc1700000000, - 0xff221a9000000000, 0x7b7980c300000000, 0x38b2264400000000, - 0xed81a48200000000, 0xae4a020500000000, 0x2a11985600000000, - 0x69da3ed100000000, 0x22a6acf100000000, 0x616d0a7600000000, - 0xe536902500000000, 0xa6fd36a200000000, 0xe8cb8cba00000000, - 0xab002a3d00000000, 0x2f5bb06e00000000, 0x6c9016e900000000, - 0x27ec84c900000000, 0x6427224e00000000, 0xe07cb81d00000000, - 0xa3b71e9a00000000, 0x76849c5c00000000, 0x354f3adb00000000, - 0xb114a08800000000, 0xf2df060f00000000, 0xb9a3942f00000000, - 0xfa6832a800000000, 0x7e33a8fb00000000, 0x3df80e7c00000000, - 0x9552ddad00000000, 0xd6997b2a00000000, 0x52c2e17900000000, - 0x110947fe00000000, 0x5a75d5de00000000, 0x19be735900000000, - 0x9de5e90a00000000, 0xde2e4f8d00000000, 0x0b1dcd4b00000000, - 0x48d66bcc00000000, 0xcc8df19f00000000, 0x8f46571800000000, - 0xc43ac53800000000, 0x87f163bf00000000, 0x03aaf9ec00000000, - 0x40615f6b00000000, 0x12f92f9400000000, 0x5132891300000000, - 0xd569134000000000, 0x96a2b5c700000000, 0xddde27e700000000, - 0x9e15816000000000, 0x1a4e1b3300000000, 0x5985bdb400000000, - 0x8cb63f7200000000, 0xcf7d99f500000000, 0x4b2603a600000000, - 0x08eda52100000000, 0x4391370100000000, 0x005a918600000000, - 0x84010bd500000000, 0xc7caad5200000000, 0x6f607e8300000000, - 0x2cabd80400000000, 0xa8f0425700000000, 0xeb3be4d000000000, - 0xa04776f000000000, 0xe38cd07700000000, 0x67d74a2400000000, - 0x241ceca300000000, 0xf12f6e6500000000, 0xb2e4c8e200000000, - 0x36bf52b100000000, 0x7574f43600000000, 0x3e08661600000000, - 0x7dc3c09100000000, 0xf9985ac200000000, 0xba53fc4500000000, - 0x1caecae700000000, 0x5f656c6000000000, 0xdb3ef63300000000, - 0x98f550b400000000, 0xd389c29400000000, 0x9042641300000000, - 0x1419fe4000000000, 0x57d258c700000000, 0x82e1da0100000000, - 0xc12a7c8600000000, 0x4571e6d500000000, 0x06ba405200000000, - 0x4dc6d27200000000, 0x0e0d74f500000000, 0x8a56eea600000000, - 0xc99d482100000000, 0x61379bf000000000, 0x22fc3d7700000000, - 0xa6a7a72400000000, 0xe56c01a300000000, 0xae10938300000000, - 0xeddb350400000000, 0x6980af5700000000, 0x2a4b09d000000000, - 0xff788b1600000000, 0xbcb32d9100000000, 0x38e8b7c200000000, - 0x7b23114500000000, 0x305f836500000000, 0x739425e200000000, - 0xf7cfbfb100000000, 0xb404193600000000, 0xe69c69c900000000, - 0xa557cf4e00000000, 0x210c551d00000000, 0x62c7f39a00000000, - 0x29bb61ba00000000, 0x6a70c73d00000000, 0xee2b5d6e00000000, - 0xade0fbe900000000, 0x78d3792f00000000, 0x3b18dfa800000000, - 0xbf4345fb00000000, 0xfc88e37c00000000, 0xb7f4715c00000000, - 0xf43fd7db00000000, 0x70644d8800000000, 0x33afeb0f00000000, - 0x9b0538de00000000, 0xd8ce9e5900000000, 0x5c95040a00000000, - 0x1f5ea28d00000000, 0x542230ad00000000, 0x17e9962a00000000, - 0x93b20c7900000000, 0xd079aafe00000000, 0x054a283800000000, - 0x46818ebf00000000, 0xc2da14ec00000000, 0x8111b26b00000000, - 0xca6d204b00000000, 0x89a686cc00000000, 0x0dfd1c9f00000000, - 0x4e36ba1800000000}, - {0x0000000000000000, 0xe1b652ef00000000, 0x836bd40500000000, - 0x62dd86ea00000000, 0x06d7a80b00000000, 0xe761fae400000000, - 0x85bc7c0e00000000, 0x640a2ee100000000, 0x0cae511700000000, - 0xed1803f800000000, 0x8fc5851200000000, 0x6e73d7fd00000000, - 0x0a79f91c00000000, 0xebcfabf300000000, 0x89122d1900000000, - 0x68a47ff600000000, 0x185ca32e00000000, 0xf9eaf1c100000000, - 0x9b37772b00000000, 0x7a8125c400000000, 0x1e8b0b2500000000, - 0xff3d59ca00000000, 0x9de0df2000000000, 0x7c568dcf00000000, - 0x14f2f23900000000, 0xf544a0d600000000, 0x9799263c00000000, - 0x762f74d300000000, 0x12255a3200000000, 0xf39308dd00000000, - 0x914e8e3700000000, 0x70f8dcd800000000, 0x30b8465d00000000, - 0xd10e14b200000000, 0xb3d3925800000000, 0x5265c0b700000000, - 0x366fee5600000000, 0xd7d9bcb900000000, 0xb5043a5300000000, - 0x54b268bc00000000, 0x3c16174a00000000, 0xdda045a500000000, - 0xbf7dc34f00000000, 0x5ecb91a000000000, 0x3ac1bf4100000000, - 0xdb77edae00000000, 0xb9aa6b4400000000, 0x581c39ab00000000, - 0x28e4e57300000000, 0xc952b79c00000000, 0xab8f317600000000, - 0x4a39639900000000, 0x2e334d7800000000, 0xcf851f9700000000, - 0xad58997d00000000, 0x4ceecb9200000000, 0x244ab46400000000, - 0xc5fce68b00000000, 0xa721606100000000, 0x4697328e00000000, - 0x229d1c6f00000000, 0xc32b4e8000000000, 0xa1f6c86a00000000, - 0x40409a8500000000, 0x60708dba00000000, 0x81c6df5500000000, - 0xe31b59bf00000000, 0x02ad0b5000000000, 0x66a725b100000000, - 0x8711775e00000000, 0xe5ccf1b400000000, 0x047aa35b00000000, - 0x6cdedcad00000000, 0x8d688e4200000000, 0xefb508a800000000, - 0x0e035a4700000000, 0x6a0974a600000000, 0x8bbf264900000000, - 0xe962a0a300000000, 0x08d4f24c00000000, 0x782c2e9400000000, - 0x999a7c7b00000000, 0xfb47fa9100000000, 0x1af1a87e00000000, - 0x7efb869f00000000, 0x9f4dd47000000000, 0xfd90529a00000000, - 0x1c26007500000000, 0x74827f8300000000, 0x95342d6c00000000, - 0xf7e9ab8600000000, 0x165ff96900000000, 0x7255d78800000000, - 0x93e3856700000000, 0xf13e038d00000000, 0x1088516200000000, - 0x50c8cbe700000000, 0xb17e990800000000, 0xd3a31fe200000000, - 0x32154d0d00000000, 0x561f63ec00000000, 0xb7a9310300000000, - 0xd574b7e900000000, 0x34c2e50600000000, 0x5c669af000000000, - 0xbdd0c81f00000000, 0xdf0d4ef500000000, 0x3ebb1c1a00000000, - 0x5ab132fb00000000, 0xbb07601400000000, 0xd9dae6fe00000000, - 0x386cb41100000000, 0x489468c900000000, 0xa9223a2600000000, - 0xcbffbccc00000000, 0x2a49ee2300000000, 0x4e43c0c200000000, - 0xaff5922d00000000, 0xcd2814c700000000, 0x2c9e462800000000, - 0x443a39de00000000, 0xa58c6b3100000000, 0xc751eddb00000000, - 0x26e7bf3400000000, 0x42ed91d500000000, 0xa35bc33a00000000, - 0xc18645d000000000, 0x2030173f00000000, 0x81e66bae00000000, - 0x6050394100000000, 0x028dbfab00000000, 0xe33bed4400000000, - 0x8731c3a500000000, 0x6687914a00000000, 0x045a17a000000000, - 0xe5ec454f00000000, 0x8d483ab900000000, 0x6cfe685600000000, - 0x0e23eebc00000000, 0xef95bc5300000000, 0x8b9f92b200000000, - 0x6a29c05d00000000, 0x08f446b700000000, 0xe942145800000000, - 0x99bac88000000000, 0x780c9a6f00000000, 0x1ad11c8500000000, - 0xfb674e6a00000000, 0x9f6d608b00000000, 0x7edb326400000000, - 0x1c06b48e00000000, 0xfdb0e66100000000, 0x9514999700000000, - 0x74a2cb7800000000, 0x167f4d9200000000, 0xf7c91f7d00000000, - 0x93c3319c00000000, 0x7275637300000000, 0x10a8e59900000000, - 0xf11eb77600000000, 0xb15e2df300000000, 0x50e87f1c00000000, - 0x3235f9f600000000, 0xd383ab1900000000, 0xb78985f800000000, - 0x563fd71700000000, 0x34e251fd00000000, 0xd554031200000000, - 0xbdf07ce400000000, 0x5c462e0b00000000, 0x3e9ba8e100000000, - 0xdf2dfa0e00000000, 0xbb27d4ef00000000, 0x5a91860000000000, - 0x384c00ea00000000, 0xd9fa520500000000, 0xa9028edd00000000, - 0x48b4dc3200000000, 0x2a695ad800000000, 0xcbdf083700000000, - 0xafd526d600000000, 0x4e63743900000000, 0x2cbef2d300000000, - 0xcd08a03c00000000, 0xa5acdfca00000000, 0x441a8d2500000000, - 0x26c70bcf00000000, 0xc771592000000000, 0xa37b77c100000000, - 0x42cd252e00000000, 0x2010a3c400000000, 0xc1a6f12b00000000, - 0xe196e61400000000, 0x0020b4fb00000000, 0x62fd321100000000, - 0x834b60fe00000000, 0xe7414e1f00000000, 0x06f71cf000000000, - 0x642a9a1a00000000, 0x859cc8f500000000, 0xed38b70300000000, - 0x0c8ee5ec00000000, 0x6e53630600000000, 0x8fe531e900000000, - 0xebef1f0800000000, 0x0a594de700000000, 0x6884cb0d00000000, - 0x893299e200000000, 0xf9ca453a00000000, 0x187c17d500000000, - 0x7aa1913f00000000, 0x9b17c3d000000000, 0xff1ded3100000000, - 0x1eabbfde00000000, 0x7c76393400000000, 0x9dc06bdb00000000, - 0xf564142d00000000, 0x14d246c200000000, 0x760fc02800000000, - 0x97b992c700000000, 0xf3b3bc2600000000, 0x1205eec900000000, - 0x70d8682300000000, 0x916e3acc00000000, 0xd12ea04900000000, - 0x3098f2a600000000, 0x5245744c00000000, 0xb3f326a300000000, - 0xd7f9084200000000, 0x364f5aad00000000, 0x5492dc4700000000, - 0xb5248ea800000000, 0xdd80f15e00000000, 0x3c36a3b100000000, - 0x5eeb255b00000000, 0xbf5d77b400000000, 0xdb57595500000000, - 0x3ae10bba00000000, 0x583c8d5000000000, 0xb98adfbf00000000, - 0xc972036700000000, 0x28c4518800000000, 0x4a19d76200000000, - 0xabaf858d00000000, 0xcfa5ab6c00000000, 0x2e13f98300000000, - 0x4cce7f6900000000, 0xad782d8600000000, 0xc5dc527000000000, - 0x246a009f00000000, 0x46b7867500000000, 0xa701d49a00000000, - 0xc30bfa7b00000000, 0x22bda89400000000, 0x40602e7e00000000, - 0xa1d67c9100000000}, - {0x0000000000000000, 0x5880e2d700000000, 0xf106b47400000000, - 0xa98656a300000000, 0xe20d68e900000000, 0xba8d8a3e00000000, - 0x130bdc9d00000000, 0x4b8b3e4a00000000, 0x851da10900000000, - 0xdd9d43de00000000, 0x741b157d00000000, 0x2c9bf7aa00000000, - 0x6710c9e000000000, 0x3f902b3700000000, 0x96167d9400000000, - 0xce969f4300000000, 0x0a3b421300000000, 0x52bba0c400000000, - 0xfb3df66700000000, 0xa3bd14b000000000, 0xe8362afa00000000, - 0xb0b6c82d00000000, 0x19309e8e00000000, 0x41b07c5900000000, - 0x8f26e31a00000000, 0xd7a601cd00000000, 0x7e20576e00000000, - 0x26a0b5b900000000, 0x6d2b8bf300000000, 0x35ab692400000000, - 0x9c2d3f8700000000, 0xc4addd5000000000, 0x1476842600000000, - 0x4cf666f100000000, 0xe570305200000000, 0xbdf0d28500000000, - 0xf67beccf00000000, 0xaefb0e1800000000, 0x077d58bb00000000, - 0x5ffdba6c00000000, 0x916b252f00000000, 0xc9ebc7f800000000, - 0x606d915b00000000, 0x38ed738c00000000, 0x73664dc600000000, - 0x2be6af1100000000, 0x8260f9b200000000, 0xdae01b6500000000, - 0x1e4dc63500000000, 0x46cd24e200000000, 0xef4b724100000000, - 0xb7cb909600000000, 0xfc40aedc00000000, 0xa4c04c0b00000000, - 0x0d461aa800000000, 0x55c6f87f00000000, 0x9b50673c00000000, - 0xc3d085eb00000000, 0x6a56d34800000000, 0x32d6319f00000000, - 0x795d0fd500000000, 0x21dded0200000000, 0x885bbba100000000, - 0xd0db597600000000, 0x28ec084d00000000, 0x706cea9a00000000, - 0xd9eabc3900000000, 0x816a5eee00000000, 0xcae160a400000000, - 0x9261827300000000, 0x3be7d4d000000000, 0x6367360700000000, - 0xadf1a94400000000, 0xf5714b9300000000, 0x5cf71d3000000000, - 0x0477ffe700000000, 0x4ffcc1ad00000000, 0x177c237a00000000, - 0xbefa75d900000000, 0xe67a970e00000000, 0x22d74a5e00000000, - 0x7a57a88900000000, 0xd3d1fe2a00000000, 0x8b511cfd00000000, - 0xc0da22b700000000, 0x985ac06000000000, 0x31dc96c300000000, - 0x695c741400000000, 0xa7caeb5700000000, 0xff4a098000000000, - 0x56cc5f2300000000, 0x0e4cbdf400000000, 0x45c783be00000000, - 0x1d47616900000000, 0xb4c137ca00000000, 0xec41d51d00000000, - 0x3c9a8c6b00000000, 0x641a6ebc00000000, 0xcd9c381f00000000, - 0x951cdac800000000, 0xde97e48200000000, 0x8617065500000000, - 0x2f9150f600000000, 0x7711b22100000000, 0xb9872d6200000000, - 0xe107cfb500000000, 0x4881991600000000, 0x10017bc100000000, - 0x5b8a458b00000000, 0x030aa75c00000000, 0xaa8cf1ff00000000, - 0xf20c132800000000, 0x36a1ce7800000000, 0x6e212caf00000000, - 0xc7a77a0c00000000, 0x9f2798db00000000, 0xd4aca69100000000, - 0x8c2c444600000000, 0x25aa12e500000000, 0x7d2af03200000000, - 0xb3bc6f7100000000, 0xeb3c8da600000000, 0x42badb0500000000, - 0x1a3a39d200000000, 0x51b1079800000000, 0x0931e54f00000000, - 0xa0b7b3ec00000000, 0xf837513b00000000, 0x50d8119a00000000, - 0x0858f34d00000000, 0xa1dea5ee00000000, 0xf95e473900000000, - 0xb2d5797300000000, 0xea559ba400000000, 0x43d3cd0700000000, - 0x1b532fd000000000, 0xd5c5b09300000000, 0x8d45524400000000, - 0x24c304e700000000, 0x7c43e63000000000, 0x37c8d87a00000000, - 0x6f483aad00000000, 0xc6ce6c0e00000000, 0x9e4e8ed900000000, - 0x5ae3538900000000, 0x0263b15e00000000, 0xabe5e7fd00000000, - 0xf365052a00000000, 0xb8ee3b6000000000, 0xe06ed9b700000000, - 0x49e88f1400000000, 0x11686dc300000000, 0xdffef28000000000, - 0x877e105700000000, 0x2ef846f400000000, 0x7678a42300000000, - 0x3df39a6900000000, 0x657378be00000000, 0xccf52e1d00000000, - 0x9475ccca00000000, 0x44ae95bc00000000, 0x1c2e776b00000000, - 0xb5a821c800000000, 0xed28c31f00000000, 0xa6a3fd5500000000, - 0xfe231f8200000000, 0x57a5492100000000, 0x0f25abf600000000, - 0xc1b334b500000000, 0x9933d66200000000, 0x30b580c100000000, - 0x6835621600000000, 0x23be5c5c00000000, 0x7b3ebe8b00000000, - 0xd2b8e82800000000, 0x8a380aff00000000, 0x4e95d7af00000000, - 0x1615357800000000, 0xbf9363db00000000, 0xe713810c00000000, - 0xac98bf4600000000, 0xf4185d9100000000, 0x5d9e0b3200000000, - 0x051ee9e500000000, 0xcb8876a600000000, 0x9308947100000000, - 0x3a8ec2d200000000, 0x620e200500000000, 0x29851e4f00000000, - 0x7105fc9800000000, 0xd883aa3b00000000, 0x800348ec00000000, - 0x783419d700000000, 0x20b4fb0000000000, 0x8932ada300000000, - 0xd1b24f7400000000, 0x9a39713e00000000, 0xc2b993e900000000, - 0x6b3fc54a00000000, 0x33bf279d00000000, 0xfd29b8de00000000, - 0xa5a95a0900000000, 0x0c2f0caa00000000, 0x54afee7d00000000, - 0x1f24d03700000000, 0x47a432e000000000, 0xee22644300000000, - 0xb6a2869400000000, 0x720f5bc400000000, 0x2a8fb91300000000, - 0x8309efb000000000, 0xdb890d6700000000, 0x9002332d00000000, - 0xc882d1fa00000000, 0x6104875900000000, 0x3984658e00000000, - 0xf712facd00000000, 0xaf92181a00000000, 0x06144eb900000000, - 0x5e94ac6e00000000, 0x151f922400000000, 0x4d9f70f300000000, - 0xe419265000000000, 0xbc99c48700000000, 0x6c429df100000000, - 0x34c27f2600000000, 0x9d44298500000000, 0xc5c4cb5200000000, - 0x8e4ff51800000000, 0xd6cf17cf00000000, 0x7f49416c00000000, - 0x27c9a3bb00000000, 0xe95f3cf800000000, 0xb1dfde2f00000000, - 0x1859888c00000000, 0x40d96a5b00000000, 0x0b52541100000000, - 0x53d2b6c600000000, 0xfa54e06500000000, 0xa2d402b200000000, - 0x6679dfe200000000, 0x3ef93d3500000000, 0x977f6b9600000000, - 0xcfff894100000000, 0x8474b70b00000000, 0xdcf455dc00000000, - 0x7572037f00000000, 0x2df2e1a800000000, 0xe3647eeb00000000, - 0xbbe49c3c00000000, 0x1262ca9f00000000, 0x4ae2284800000000, - 0x0169160200000000, 0x59e9f4d500000000, 0xf06fa27600000000, - 0xa8ef40a100000000}, - {0x0000000000000000, 0x463b676500000000, 0x8c76ceca00000000, - 0xca4da9af00000000, 0x59ebed4e00000000, 0x1fd08a2b00000000, - 0xd59d238400000000, 0x93a644e100000000, 0xb2d6db9d00000000, - 0xf4edbcf800000000, 0x3ea0155700000000, 0x789b723200000000, - 0xeb3d36d300000000, 0xad0651b600000000, 0x674bf81900000000, - 0x21709f7c00000000, 0x25abc6e000000000, 0x6390a18500000000, - 0xa9dd082a00000000, 0xefe66f4f00000000, 0x7c402bae00000000, - 0x3a7b4ccb00000000, 0xf036e56400000000, 0xb60d820100000000, - 0x977d1d7d00000000, 0xd1467a1800000000, 0x1b0bd3b700000000, - 0x5d30b4d200000000, 0xce96f03300000000, 0x88ad975600000000, - 0x42e03ef900000000, 0x04db599c00000000, 0x0b50fc1a00000000, - 0x4d6b9b7f00000000, 0x872632d000000000, 0xc11d55b500000000, - 0x52bb115400000000, 0x1480763100000000, 0xdecddf9e00000000, - 0x98f6b8fb00000000, 0xb986278700000000, 0xffbd40e200000000, - 0x35f0e94d00000000, 0x73cb8e2800000000, 0xe06dcac900000000, - 0xa656adac00000000, 0x6c1b040300000000, 0x2a20636600000000, - 0x2efb3afa00000000, 0x68c05d9f00000000, 0xa28df43000000000, - 0xe4b6935500000000, 0x7710d7b400000000, 0x312bb0d100000000, - 0xfb66197e00000000, 0xbd5d7e1b00000000, 0x9c2de16700000000, - 0xda16860200000000, 0x105b2fad00000000, 0x566048c800000000, - 0xc5c60c2900000000, 0x83fd6b4c00000000, 0x49b0c2e300000000, - 0x0f8ba58600000000, 0x16a0f83500000000, 0x509b9f5000000000, - 0x9ad636ff00000000, 0xdced519a00000000, 0x4f4b157b00000000, - 0x0970721e00000000, 0xc33ddbb100000000, 0x8506bcd400000000, - 0xa47623a800000000, 0xe24d44cd00000000, 0x2800ed6200000000, - 0x6e3b8a0700000000, 0xfd9dcee600000000, 0xbba6a98300000000, - 0x71eb002c00000000, 0x37d0674900000000, 0x330b3ed500000000, - 0x753059b000000000, 0xbf7df01f00000000, 0xf946977a00000000, - 0x6ae0d39b00000000, 0x2cdbb4fe00000000, 0xe6961d5100000000, - 0xa0ad7a3400000000, 0x81dde54800000000, 0xc7e6822d00000000, - 0x0dab2b8200000000, 0x4b904ce700000000, 0xd836080600000000, - 0x9e0d6f6300000000, 0x5440c6cc00000000, 0x127ba1a900000000, - 0x1df0042f00000000, 0x5bcb634a00000000, 0x9186cae500000000, - 0xd7bdad8000000000, 0x441be96100000000, 0x02208e0400000000, - 0xc86d27ab00000000, 0x8e5640ce00000000, 0xaf26dfb200000000, - 0xe91db8d700000000, 0x2350117800000000, 0x656b761d00000000, - 0xf6cd32fc00000000, 0xb0f6559900000000, 0x7abbfc3600000000, - 0x3c809b5300000000, 0x385bc2cf00000000, 0x7e60a5aa00000000, - 0xb42d0c0500000000, 0xf2166b6000000000, 0x61b02f8100000000, - 0x278b48e400000000, 0xedc6e14b00000000, 0xabfd862e00000000, - 0x8a8d195200000000, 0xccb67e3700000000, 0x06fbd79800000000, - 0x40c0b0fd00000000, 0xd366f41c00000000, 0x955d937900000000, - 0x5f103ad600000000, 0x192b5db300000000, 0x2c40f16b00000000, - 0x6a7b960e00000000, 0xa0363fa100000000, 0xe60d58c400000000, - 0x75ab1c2500000000, 0x33907b4000000000, 0xf9ddd2ef00000000, - 0xbfe6b58a00000000, 0x9e962af600000000, 0xd8ad4d9300000000, - 0x12e0e43c00000000, 0x54db835900000000, 0xc77dc7b800000000, - 0x8146a0dd00000000, 0x4b0b097200000000, 0x0d306e1700000000, - 0x09eb378b00000000, 0x4fd050ee00000000, 0x859df94100000000, - 0xc3a69e2400000000, 0x5000dac500000000, 0x163bbda000000000, - 0xdc76140f00000000, 0x9a4d736a00000000, 0xbb3dec1600000000, - 0xfd068b7300000000, 0x374b22dc00000000, 0x717045b900000000, - 0xe2d6015800000000, 0xa4ed663d00000000, 0x6ea0cf9200000000, - 0x289ba8f700000000, 0x27100d7100000000, 0x612b6a1400000000, - 0xab66c3bb00000000, 0xed5da4de00000000, 0x7efbe03f00000000, - 0x38c0875a00000000, 0xf28d2ef500000000, 0xb4b6499000000000, - 0x95c6d6ec00000000, 0xd3fdb18900000000, 0x19b0182600000000, - 0x5f8b7f4300000000, 0xcc2d3ba200000000, 0x8a165cc700000000, - 0x405bf56800000000, 0x0660920d00000000, 0x02bbcb9100000000, - 0x4480acf400000000, 0x8ecd055b00000000, 0xc8f6623e00000000, - 0x5b5026df00000000, 0x1d6b41ba00000000, 0xd726e81500000000, - 0x911d8f7000000000, 0xb06d100c00000000, 0xf656776900000000, - 0x3c1bdec600000000, 0x7a20b9a300000000, 0xe986fd4200000000, - 0xafbd9a2700000000, 0x65f0338800000000, 0x23cb54ed00000000, - 0x3ae0095e00000000, 0x7cdb6e3b00000000, 0xb696c79400000000, - 0xf0ada0f100000000, 0x630be41000000000, 0x2530837500000000, - 0xef7d2ada00000000, 0xa9464dbf00000000, 0x8836d2c300000000, - 0xce0db5a600000000, 0x04401c0900000000, 0x427b7b6c00000000, - 0xd1dd3f8d00000000, 0x97e658e800000000, 0x5dabf14700000000, - 0x1b90962200000000, 0x1f4bcfbe00000000, 0x5970a8db00000000, - 0x933d017400000000, 0xd506661100000000, 0x46a022f000000000, - 0x009b459500000000, 0xcad6ec3a00000000, 0x8ced8b5f00000000, - 0xad9d142300000000, 0xeba6734600000000, 0x21ebdae900000000, - 0x67d0bd8c00000000, 0xf476f96d00000000, 0xb24d9e0800000000, - 0x780037a700000000, 0x3e3b50c200000000, 0x31b0f54400000000, - 0x778b922100000000, 0xbdc63b8e00000000, 0xfbfd5ceb00000000, - 0x685b180a00000000, 0x2e607f6f00000000, 0xe42dd6c000000000, - 0xa216b1a500000000, 0x83662ed900000000, 0xc55d49bc00000000, - 0x0f10e01300000000, 0x492b877600000000, 0xda8dc39700000000, - 0x9cb6a4f200000000, 0x56fb0d5d00000000, 0x10c06a3800000000, - 0x141b33a400000000, 0x522054c100000000, 0x986dfd6e00000000, - 0xde569a0b00000000, 0x4df0deea00000000, 0x0bcbb98f00000000, - 0xc186102000000000, 0x87bd774500000000, 0xa6cde83900000000, - 0xe0f68f5c00000000, 0x2abb26f300000000, 0x6c80419600000000, - 0xff26057700000000, 0xb91d621200000000, 0x7350cbbd00000000, - 0x356bacd800000000}, - {0x0000000000000000, 0x9e83da9f00000000, 0x7d01c4e400000000, - 0xe3821e7b00000000, 0xbb04f91200000000, 0x2587238d00000000, - 0xc6053df600000000, 0x5886e76900000000, 0x7609f22500000000, - 0xe88a28ba00000000, 0x0b0836c100000000, 0x958bec5e00000000, - 0xcd0d0b3700000000, 0x538ed1a800000000, 0xb00ccfd300000000, - 0x2e8f154c00000000, 0xec12e44b00000000, 0x72913ed400000000, - 0x911320af00000000, 0x0f90fa3000000000, 0x57161d5900000000, - 0xc995c7c600000000, 0x2a17d9bd00000000, 0xb494032200000000, - 0x9a1b166e00000000, 0x0498ccf100000000, 0xe71ad28a00000000, - 0x7999081500000000, 0x211fef7c00000000, 0xbf9c35e300000000, - 0x5c1e2b9800000000, 0xc29df10700000000, 0xd825c89700000000, - 0x46a6120800000000, 0xa5240c7300000000, 0x3ba7d6ec00000000, - 0x6321318500000000, 0xfda2eb1a00000000, 0x1e20f56100000000, - 0x80a32ffe00000000, 0xae2c3ab200000000, 0x30afe02d00000000, - 0xd32dfe5600000000, 0x4dae24c900000000, 0x1528c3a000000000, - 0x8bab193f00000000, 0x6829074400000000, 0xf6aadddb00000000, - 0x34372cdc00000000, 0xaab4f64300000000, 0x4936e83800000000, - 0xd7b532a700000000, 0x8f33d5ce00000000, 0x11b00f5100000000, - 0xf232112a00000000, 0x6cb1cbb500000000, 0x423edef900000000, - 0xdcbd046600000000, 0x3f3f1a1d00000000, 0xa1bcc08200000000, - 0xf93a27eb00000000, 0x67b9fd7400000000, 0x843be30f00000000, - 0x1ab8399000000000, 0xf14de1f400000000, 0x6fce3b6b00000000, - 0x8c4c251000000000, 0x12cfff8f00000000, 0x4a4918e600000000, - 0xd4cac27900000000, 0x3748dc0200000000, 0xa9cb069d00000000, - 0x874413d100000000, 0x19c7c94e00000000, 0xfa45d73500000000, - 0x64c60daa00000000, 0x3c40eac300000000, 0xa2c3305c00000000, - 0x41412e2700000000, 0xdfc2f4b800000000, 0x1d5f05bf00000000, - 0x83dcdf2000000000, 0x605ec15b00000000, 0xfedd1bc400000000, - 0xa65bfcad00000000, 0x38d8263200000000, 0xdb5a384900000000, - 0x45d9e2d600000000, 0x6b56f79a00000000, 0xf5d52d0500000000, - 0x1657337e00000000, 0x88d4e9e100000000, 0xd0520e8800000000, - 0x4ed1d41700000000, 0xad53ca6c00000000, 0x33d010f300000000, - 0x2968296300000000, 0xb7ebf3fc00000000, 0x5469ed8700000000, - 0xcaea371800000000, 0x926cd07100000000, 0x0cef0aee00000000, - 0xef6d149500000000, 0x71eece0a00000000, 0x5f61db4600000000, - 0xc1e201d900000000, 0x22601fa200000000, 0xbce3c53d00000000, - 0xe465225400000000, 0x7ae6f8cb00000000, 0x9964e6b000000000, - 0x07e73c2f00000000, 0xc57acd2800000000, 0x5bf917b700000000, - 0xb87b09cc00000000, 0x26f8d35300000000, 0x7e7e343a00000000, - 0xe0fdeea500000000, 0x037ff0de00000000, 0x9dfc2a4100000000, - 0xb3733f0d00000000, 0x2df0e59200000000, 0xce72fbe900000000, - 0x50f1217600000000, 0x0877c61f00000000, 0x96f41c8000000000, - 0x757602fb00000000, 0xebf5d86400000000, 0xa39db33200000000, - 0x3d1e69ad00000000, 0xde9c77d600000000, 0x401fad4900000000, - 0x18994a2000000000, 0x861a90bf00000000, 0x65988ec400000000, - 0xfb1b545b00000000, 0xd594411700000000, 0x4b179b8800000000, - 0xa89585f300000000, 0x36165f6c00000000, 0x6e90b80500000000, - 0xf013629a00000000, 0x13917ce100000000, 0x8d12a67e00000000, - 0x4f8f577900000000, 0xd10c8de600000000, 0x328e939d00000000, - 0xac0d490200000000, 0xf48bae6b00000000, 0x6a0874f400000000, - 0x898a6a8f00000000, 0x1709b01000000000, 0x3986a55c00000000, - 0xa7057fc300000000, 0x448761b800000000, 0xda04bb2700000000, - 0x82825c4e00000000, 0x1c0186d100000000, 0xff8398aa00000000, - 0x6100423500000000, 0x7bb87ba500000000, 0xe53ba13a00000000, - 0x06b9bf4100000000, 0x983a65de00000000, 0xc0bc82b700000000, - 0x5e3f582800000000, 0xbdbd465300000000, 0x233e9ccc00000000, - 0x0db1898000000000, 0x9332531f00000000, 0x70b04d6400000000, - 0xee3397fb00000000, 0xb6b5709200000000, 0x2836aa0d00000000, - 0xcbb4b47600000000, 0x55376ee900000000, 0x97aa9fee00000000, - 0x0929457100000000, 0xeaab5b0a00000000, 0x7428819500000000, - 0x2cae66fc00000000, 0xb22dbc6300000000, 0x51afa21800000000, - 0xcf2c788700000000, 0xe1a36dcb00000000, 0x7f20b75400000000, - 0x9ca2a92f00000000, 0x022173b000000000, 0x5aa794d900000000, - 0xc4244e4600000000, 0x27a6503d00000000, 0xb9258aa200000000, - 0x52d052c600000000, 0xcc53885900000000, 0x2fd1962200000000, - 0xb1524cbd00000000, 0xe9d4abd400000000, 0x7757714b00000000, - 0x94d56f3000000000, 0x0a56b5af00000000, 0x24d9a0e300000000, - 0xba5a7a7c00000000, 0x59d8640700000000, 0xc75bbe9800000000, - 0x9fdd59f100000000, 0x015e836e00000000, 0xe2dc9d1500000000, - 0x7c5f478a00000000, 0xbec2b68d00000000, 0x20416c1200000000, - 0xc3c3726900000000, 0x5d40a8f600000000, 0x05c64f9f00000000, - 0x9b45950000000000, 0x78c78b7b00000000, 0xe64451e400000000, - 0xc8cb44a800000000, 0x56489e3700000000, 0xb5ca804c00000000, - 0x2b495ad300000000, 0x73cfbdba00000000, 0xed4c672500000000, - 0x0ece795e00000000, 0x904da3c100000000, 0x8af59a5100000000, - 0x147640ce00000000, 0xf7f45eb500000000, 0x6977842a00000000, - 0x31f1634300000000, 0xaf72b9dc00000000, 0x4cf0a7a700000000, - 0xd2737d3800000000, 0xfcfc687400000000, 0x627fb2eb00000000, - 0x81fdac9000000000, 0x1f7e760f00000000, 0x47f8916600000000, - 0xd97b4bf900000000, 0x3af9558200000000, 0xa47a8f1d00000000, - 0x66e77e1a00000000, 0xf864a48500000000, 0x1be6bafe00000000, - 0x8565606100000000, 0xdde3870800000000, 0x43605d9700000000, - 0xa0e243ec00000000, 0x3e61997300000000, 0x10ee8c3f00000000, - 0x8e6d56a000000000, 0x6def48db00000000, 0xf36c924400000000, - 0xabea752d00000000, 0x3569afb200000000, 0xd6ebb1c900000000, - 0x48686b5600000000}, - {0x0000000000000000, 0xc064281700000000, 0x80c9502e00000000, - 0x40ad783900000000, 0x0093a15c00000000, 0xc0f7894b00000000, - 0x805af17200000000, 0x403ed96500000000, 0x002643b900000000, - 0xc0426bae00000000, 0x80ef139700000000, 0x408b3b8000000000, - 0x00b5e2e500000000, 0xc0d1caf200000000, 0x807cb2cb00000000, - 0x40189adc00000000, 0x414af7a900000000, 0x812edfbe00000000, - 0xc183a78700000000, 0x01e78f9000000000, 0x41d956f500000000, - 0x81bd7ee200000000, 0xc11006db00000000, 0x01742ecc00000000, - 0x416cb41000000000, 0x81089c0700000000, 0xc1a5e43e00000000, - 0x01c1cc2900000000, 0x41ff154c00000000, 0x819b3d5b00000000, - 0xc136456200000000, 0x01526d7500000000, 0xc3929f8800000000, - 0x03f6b79f00000000, 0x435bcfa600000000, 0x833fe7b100000000, - 0xc3013ed400000000, 0x036516c300000000, 0x43c86efa00000000, - 0x83ac46ed00000000, 0xc3b4dc3100000000, 0x03d0f42600000000, - 0x437d8c1f00000000, 0x8319a40800000000, 0xc3277d6d00000000, - 0x0343557a00000000, 0x43ee2d4300000000, 0x838a055400000000, - 0x82d8682100000000, 0x42bc403600000000, 0x0211380f00000000, - 0xc275101800000000, 0x824bc97d00000000, 0x422fe16a00000000, - 0x0282995300000000, 0xc2e6b14400000000, 0x82fe2b9800000000, - 0x429a038f00000000, 0x02377bb600000000, 0xc25353a100000000, - 0x826d8ac400000000, 0x4209a2d300000000, 0x02a4daea00000000, - 0xc2c0f2fd00000000, 0xc7234eca00000000, 0x074766dd00000000, - 0x47ea1ee400000000, 0x878e36f300000000, 0xc7b0ef9600000000, - 0x07d4c78100000000, 0x4779bfb800000000, 0x871d97af00000000, - 0xc7050d7300000000, 0x0761256400000000, 0x47cc5d5d00000000, - 0x87a8754a00000000, 0xc796ac2f00000000, 0x07f2843800000000, - 0x475ffc0100000000, 0x873bd41600000000, 0x8669b96300000000, - 0x460d917400000000, 0x06a0e94d00000000, 0xc6c4c15a00000000, - 0x86fa183f00000000, 0x469e302800000000, 0x0633481100000000, - 0xc657600600000000, 0x864ffada00000000, 0x462bd2cd00000000, - 0x0686aaf400000000, 0xc6e282e300000000, 0x86dc5b8600000000, - 0x46b8739100000000, 0x06150ba800000000, 0xc67123bf00000000, - 0x04b1d14200000000, 0xc4d5f95500000000, 0x8478816c00000000, - 0x441ca97b00000000, 0x0422701e00000000, 0xc446580900000000, - 0x84eb203000000000, 0x448f082700000000, 0x049792fb00000000, - 0xc4f3baec00000000, 0x845ec2d500000000, 0x443aeac200000000, - 0x040433a700000000, 0xc4601bb000000000, 0x84cd638900000000, - 0x44a94b9e00000000, 0x45fb26eb00000000, 0x859f0efc00000000, - 0xc53276c500000000, 0x05565ed200000000, 0x456887b700000000, - 0x850cafa000000000, 0xc5a1d79900000000, 0x05c5ff8e00000000, - 0x45dd655200000000, 0x85b94d4500000000, 0xc514357c00000000, - 0x05701d6b00000000, 0x454ec40e00000000, 0x852aec1900000000, - 0xc587942000000000, 0x05e3bc3700000000, 0xcf41ed4f00000000, - 0x0f25c55800000000, 0x4f88bd6100000000, 0x8fec957600000000, - 0xcfd24c1300000000, 0x0fb6640400000000, 0x4f1b1c3d00000000, - 0x8f7f342a00000000, 0xcf67aef600000000, 0x0f0386e100000000, - 0x4faefed800000000, 0x8fcad6cf00000000, 0xcff40faa00000000, - 0x0f9027bd00000000, 0x4f3d5f8400000000, 0x8f59779300000000, - 0x8e0b1ae600000000, 0x4e6f32f100000000, 0x0ec24ac800000000, - 0xcea662df00000000, 0x8e98bbba00000000, 0x4efc93ad00000000, - 0x0e51eb9400000000, 0xce35c38300000000, 0x8e2d595f00000000, - 0x4e49714800000000, 0x0ee4097100000000, 0xce80216600000000, - 0x8ebef80300000000, 0x4edad01400000000, 0x0e77a82d00000000, - 0xce13803a00000000, 0x0cd372c700000000, 0xccb75ad000000000, - 0x8c1a22e900000000, 0x4c7e0afe00000000, 0x0c40d39b00000000, - 0xcc24fb8c00000000, 0x8c8983b500000000, 0x4cedaba200000000, - 0x0cf5317e00000000, 0xcc91196900000000, 0x8c3c615000000000, - 0x4c58494700000000, 0x0c66902200000000, 0xcc02b83500000000, - 0x8cafc00c00000000, 0x4ccbe81b00000000, 0x4d99856e00000000, - 0x8dfdad7900000000, 0xcd50d54000000000, 0x0d34fd5700000000, - 0x4d0a243200000000, 0x8d6e0c2500000000, 0xcdc3741c00000000, - 0x0da75c0b00000000, 0x4dbfc6d700000000, 0x8ddbeec000000000, - 0xcd7696f900000000, 0x0d12beee00000000, 0x4d2c678b00000000, - 0x8d484f9c00000000, 0xcde537a500000000, 0x0d811fb200000000, - 0x0862a38500000000, 0xc8068b9200000000, 0x88abf3ab00000000, - 0x48cfdbbc00000000, 0x08f102d900000000, 0xc8952ace00000000, - 0x883852f700000000, 0x485c7ae000000000, 0x0844e03c00000000, - 0xc820c82b00000000, 0x888db01200000000, 0x48e9980500000000, - 0x08d7416000000000, 0xc8b3697700000000, 0x881e114e00000000, - 0x487a395900000000, 0x4928542c00000000, 0x894c7c3b00000000, - 0xc9e1040200000000, 0x09852c1500000000, 0x49bbf57000000000, - 0x89dfdd6700000000, 0xc972a55e00000000, 0x09168d4900000000, - 0x490e179500000000, 0x896a3f8200000000, 0xc9c747bb00000000, - 0x09a36fac00000000, 0x499db6c900000000, 0x89f99ede00000000, - 0xc954e6e700000000, 0x0930cef000000000, 0xcbf03c0d00000000, - 0x0b94141a00000000, 0x4b396c2300000000, 0x8b5d443400000000, - 0xcb639d5100000000, 0x0b07b54600000000, 0x4baacd7f00000000, - 0x8bcee56800000000, 0xcbd67fb400000000, 0x0bb257a300000000, - 0x4b1f2f9a00000000, 0x8b7b078d00000000, 0xcb45dee800000000, - 0x0b21f6ff00000000, 0x4b8c8ec600000000, 0x8be8a6d100000000, - 0x8abacba400000000, 0x4adee3b300000000, 0x0a739b8a00000000, - 0xca17b39d00000000, 0x8a296af800000000, 0x4a4d42ef00000000, - 0x0ae03ad600000000, 0xca8412c100000000, 0x8a9c881d00000000, - 0x4af8a00a00000000, 0x0a55d83300000000, 0xca31f02400000000, - 0x8a0f294100000000, 0x4a6b015600000000, 0x0ac6796f00000000, - 0xcaa2517800000000}, - {0x0000000000000000, 0xd4ea739b00000000, 0xe9d396ed00000000, - 0x3d39e57600000000, 0x93a15c0000000000, 0x474b2f9b00000000, - 0x7a72caed00000000, 0xae98b97600000000, 0x2643b90000000000, - 0xf2a9ca9b00000000, 0xcf902fed00000000, 0x1b7a5c7600000000, - 0xb5e2e50000000000, 0x6108969b00000000, 0x5c3173ed00000000, - 0x88db007600000000, 0x4c86720100000000, 0x986c019a00000000, - 0xa555e4ec00000000, 0x71bf977700000000, 0xdf272e0100000000, - 0x0bcd5d9a00000000, 0x36f4b8ec00000000, 0xe21ecb7700000000, - 0x6ac5cb0100000000, 0xbe2fb89a00000000, 0x83165dec00000000, - 0x57fc2e7700000000, 0xf964970100000000, 0x2d8ee49a00000000, - 0x10b701ec00000000, 0xc45d727700000000, 0x980ce50200000000, - 0x4ce6969900000000, 0x71df73ef00000000, 0xa535007400000000, - 0x0badb90200000000, 0xdf47ca9900000000, 0xe27e2fef00000000, - 0x36945c7400000000, 0xbe4f5c0200000000, 0x6aa52f9900000000, - 0x579ccaef00000000, 0x8376b97400000000, 0x2dee000200000000, - 0xf904739900000000, 0xc43d96ef00000000, 0x10d7e57400000000, - 0xd48a970300000000, 0x0060e49800000000, 0x3d5901ee00000000, - 0xe9b3727500000000, 0x472bcb0300000000, 0x93c1b89800000000, - 0xaef85dee00000000, 0x7a122e7500000000, 0xf2c92e0300000000, - 0x26235d9800000000, 0x1b1ab8ee00000000, 0xcff0cb7500000000, - 0x6168720300000000, 0xb582019800000000, 0x88bbe4ee00000000, - 0x5c51977500000000, 0x3019ca0500000000, 0xe4f3b99e00000000, - 0xd9ca5ce800000000, 0x0d202f7300000000, 0xa3b8960500000000, - 0x7752e59e00000000, 0x4a6b00e800000000, 0x9e81737300000000, - 0x165a730500000000, 0xc2b0009e00000000, 0xff89e5e800000000, - 0x2b63967300000000, 0x85fb2f0500000000, 0x51115c9e00000000, - 0x6c28b9e800000000, 0xb8c2ca7300000000, 0x7c9fb80400000000, - 0xa875cb9f00000000, 0x954c2ee900000000, 0x41a65d7200000000, - 0xef3ee40400000000, 0x3bd4979f00000000, 0x06ed72e900000000, - 0xd207017200000000, 0x5adc010400000000, 0x8e36729f00000000, - 0xb30f97e900000000, 0x67e5e47200000000, 0xc97d5d0400000000, - 0x1d972e9f00000000, 0x20aecbe900000000, 0xf444b87200000000, - 0xa8152f0700000000, 0x7cff5c9c00000000, 0x41c6b9ea00000000, - 0x952cca7100000000, 0x3bb4730700000000, 0xef5e009c00000000, - 0xd267e5ea00000000, 0x068d967100000000, 0x8e56960700000000, - 0x5abce59c00000000, 0x678500ea00000000, 0xb36f737100000000, - 0x1df7ca0700000000, 0xc91db99c00000000, 0xf4245cea00000000, - 0x20ce2f7100000000, 0xe4935d0600000000, 0x30792e9d00000000, - 0x0d40cbeb00000000, 0xd9aab87000000000, 0x7732010600000000, - 0xa3d8729d00000000, 0x9ee197eb00000000, 0x4a0be47000000000, - 0xc2d0e40600000000, 0x163a979d00000000, 0x2b0372eb00000000, - 0xffe9017000000000, 0x5171b80600000000, 0x859bcb9d00000000, - 0xb8a22eeb00000000, 0x6c485d7000000000, 0x6032940b00000000, - 0xb4d8e79000000000, 0x89e102e600000000, 0x5d0b717d00000000, - 0xf393c80b00000000, 0x2779bb9000000000, 0x1a405ee600000000, - 0xceaa2d7d00000000, 0x46712d0b00000000, 0x929b5e9000000000, - 0xafa2bbe600000000, 0x7b48c87d00000000, 0xd5d0710b00000000, - 0x013a029000000000, 0x3c03e7e600000000, 0xe8e9947d00000000, - 0x2cb4e60a00000000, 0xf85e959100000000, 0xc56770e700000000, - 0x118d037c00000000, 0xbf15ba0a00000000, 0x6bffc99100000000, - 0x56c62ce700000000, 0x822c5f7c00000000, 0x0af75f0a00000000, - 0xde1d2c9100000000, 0xe324c9e700000000, 0x37ceba7c00000000, - 0x9956030a00000000, 0x4dbc709100000000, 0x708595e700000000, - 0xa46fe67c00000000, 0xf83e710900000000, 0x2cd4029200000000, - 0x11ede7e400000000, 0xc507947f00000000, 0x6b9f2d0900000000, - 0xbf755e9200000000, 0x824cbbe400000000, 0x56a6c87f00000000, - 0xde7dc80900000000, 0x0a97bb9200000000, 0x37ae5ee400000000, - 0xe3442d7f00000000, 0x4ddc940900000000, 0x9936e79200000000, - 0xa40f02e400000000, 0x70e5717f00000000, 0xb4b8030800000000, - 0x6052709300000000, 0x5d6b95e500000000, 0x8981e67e00000000, - 0x27195f0800000000, 0xf3f32c9300000000, 0xcecac9e500000000, - 0x1a20ba7e00000000, 0x92fbba0800000000, 0x4611c99300000000, - 0x7b282ce500000000, 0xafc25f7e00000000, 0x015ae60800000000, - 0xd5b0959300000000, 0xe88970e500000000, 0x3c63037e00000000, - 0x502b5e0e00000000, 0x84c12d9500000000, 0xb9f8c8e300000000, - 0x6d12bb7800000000, 0xc38a020e00000000, 0x1760719500000000, - 0x2a5994e300000000, 0xfeb3e77800000000, 0x7668e70e00000000, - 0xa282949500000000, 0x9fbb71e300000000, 0x4b51027800000000, - 0xe5c9bb0e00000000, 0x3123c89500000000, 0x0c1a2de300000000, - 0xd8f05e7800000000, 0x1cad2c0f00000000, 0xc8475f9400000000, - 0xf57ebae200000000, 0x2194c97900000000, 0x8f0c700f00000000, - 0x5be6039400000000, 0x66dfe6e200000000, 0xb235957900000000, - 0x3aee950f00000000, 0xee04e69400000000, 0xd33d03e200000000, - 0x07d7707900000000, 0xa94fc90f00000000, 0x7da5ba9400000000, - 0x409c5fe200000000, 0x94762c7900000000, 0xc827bb0c00000000, - 0x1ccdc89700000000, 0x21f42de100000000, 0xf51e5e7a00000000, - 0x5b86e70c00000000, 0x8f6c949700000000, 0xb25571e100000000, - 0x66bf027a00000000, 0xee64020c00000000, 0x3a8e719700000000, - 0x07b794e100000000, 0xd35de77a00000000, 0x7dc55e0c00000000, - 0xa92f2d9700000000, 0x9416c8e100000000, 0x40fcbb7a00000000, - 0x84a1c90d00000000, 0x504bba9600000000, 0x6d725fe000000000, - 0xb9982c7b00000000, 0x1700950d00000000, 0xc3eae69600000000, - 0xfed303e000000000, 0x2a39707b00000000, 0xa2e2700d00000000, - 0x7608039600000000, 0x4b31e6e000000000, 0x9fdb957b00000000, - 0x31432c0d00000000, 0xe5a95f9600000000, 0xd890bae000000000, - 0x0c7ac97b00000000}, - {0x0000000000000000, 0x2765258100000000, 0x0fcc3bd900000000, - 0x28a91e5800000000, 0x5f9e066900000000, 0x78fb23e800000000, - 0x50523db000000000, 0x7737183100000000, 0xbe3c0dd200000000, - 0x9959285300000000, 0xb1f0360b00000000, 0x9695138a00000000, - 0xe1a20bbb00000000, 0xc6c72e3a00000000, 0xee6e306200000000, - 0xc90b15e300000000, 0x3d7f6b7f00000000, 0x1a1a4efe00000000, - 0x32b350a600000000, 0x15d6752700000000, 0x62e16d1600000000, - 0x4584489700000000, 0x6d2d56cf00000000, 0x4a48734e00000000, - 0x834366ad00000000, 0xa426432c00000000, 0x8c8f5d7400000000, - 0xabea78f500000000, 0xdcdd60c400000000, 0xfbb8454500000000, - 0xd3115b1d00000000, 0xf4747e9c00000000, 0x7afed6fe00000000, - 0x5d9bf37f00000000, 0x7532ed2700000000, 0x5257c8a600000000, - 0x2560d09700000000, 0x0205f51600000000, 0x2aaceb4e00000000, - 0x0dc9cecf00000000, 0xc4c2db2c00000000, 0xe3a7fead00000000, - 0xcb0ee0f500000000, 0xec6bc57400000000, 0x9b5cdd4500000000, - 0xbc39f8c400000000, 0x9490e69c00000000, 0xb3f5c31d00000000, - 0x4781bd8100000000, 0x60e4980000000000, 0x484d865800000000, - 0x6f28a3d900000000, 0x181fbbe800000000, 0x3f7a9e6900000000, - 0x17d3803100000000, 0x30b6a5b000000000, 0xf9bdb05300000000, - 0xded895d200000000, 0xf6718b8a00000000, 0xd114ae0b00000000, - 0xa623b63a00000000, 0x814693bb00000000, 0xa9ef8de300000000, - 0x8e8aa86200000000, 0xb5fadc2600000000, 0x929ff9a700000000, - 0xba36e7ff00000000, 0x9d53c27e00000000, 0xea64da4f00000000, - 0xcd01ffce00000000, 0xe5a8e19600000000, 0xc2cdc41700000000, - 0x0bc6d1f400000000, 0x2ca3f47500000000, 0x040aea2d00000000, - 0x236fcfac00000000, 0x5458d79d00000000, 0x733df21c00000000, - 0x5b94ec4400000000, 0x7cf1c9c500000000, 0x8885b75900000000, - 0xafe092d800000000, 0x87498c8000000000, 0xa02ca90100000000, - 0xd71bb13000000000, 0xf07e94b100000000, 0xd8d78ae900000000, - 0xffb2af6800000000, 0x36b9ba8b00000000, 0x11dc9f0a00000000, - 0x3975815200000000, 0x1e10a4d300000000, 0x6927bce200000000, - 0x4e42996300000000, 0x66eb873b00000000, 0x418ea2ba00000000, - 0xcf040ad800000000, 0xe8612f5900000000, 0xc0c8310100000000, - 0xe7ad148000000000, 0x909a0cb100000000, 0xb7ff293000000000, - 0x9f56376800000000, 0xb83312e900000000, 0x7138070a00000000, - 0x565d228b00000000, 0x7ef43cd300000000, 0x5991195200000000, - 0x2ea6016300000000, 0x09c324e200000000, 0x216a3aba00000000, - 0x060f1f3b00000000, 0xf27b61a700000000, 0xd51e442600000000, - 0xfdb75a7e00000000, 0xdad27fff00000000, 0xade567ce00000000, - 0x8a80424f00000000, 0xa2295c1700000000, 0x854c799600000000, - 0x4c476c7500000000, 0x6b2249f400000000, 0x438b57ac00000000, - 0x64ee722d00000000, 0x13d96a1c00000000, 0x34bc4f9d00000000, - 0x1c1551c500000000, 0x3b70744400000000, 0x6af5b94d00000000, - 0x4d909ccc00000000, 0x6539829400000000, 0x425ca71500000000, - 0x356bbf2400000000, 0x120e9aa500000000, 0x3aa784fd00000000, - 0x1dc2a17c00000000, 0xd4c9b49f00000000, 0xf3ac911e00000000, - 0xdb058f4600000000, 0xfc60aac700000000, 0x8b57b2f600000000, - 0xac32977700000000, 0x849b892f00000000, 0xa3feacae00000000, - 0x578ad23200000000, 0x70eff7b300000000, 0x5846e9eb00000000, - 0x7f23cc6a00000000, 0x0814d45b00000000, 0x2f71f1da00000000, - 0x07d8ef8200000000, 0x20bdca0300000000, 0xe9b6dfe000000000, - 0xced3fa6100000000, 0xe67ae43900000000, 0xc11fc1b800000000, - 0xb628d98900000000, 0x914dfc0800000000, 0xb9e4e25000000000, - 0x9e81c7d100000000, 0x100b6fb300000000, 0x376e4a3200000000, - 0x1fc7546a00000000, 0x38a271eb00000000, 0x4f9569da00000000, - 0x68f04c5b00000000, 0x4059520300000000, 0x673c778200000000, - 0xae37626100000000, 0x895247e000000000, 0xa1fb59b800000000, - 0x869e7c3900000000, 0xf1a9640800000000, 0xd6cc418900000000, - 0xfe655fd100000000, 0xd9007a5000000000, 0x2d7404cc00000000, - 0x0a11214d00000000, 0x22b83f1500000000, 0x05dd1a9400000000, - 0x72ea02a500000000, 0x558f272400000000, 0x7d26397c00000000, - 0x5a431cfd00000000, 0x9348091e00000000, 0xb42d2c9f00000000, - 0x9c8432c700000000, 0xbbe1174600000000, 0xccd60f7700000000, - 0xebb32af600000000, 0xc31a34ae00000000, 0xe47f112f00000000, - 0xdf0f656b00000000, 0xf86a40ea00000000, 0xd0c35eb200000000, - 0xf7a67b3300000000, 0x8091630200000000, 0xa7f4468300000000, - 0x8f5d58db00000000, 0xa8387d5a00000000, 0x613368b900000000, - 0x46564d3800000000, 0x6eff536000000000, 0x499a76e100000000, - 0x3ead6ed000000000, 0x19c84b5100000000, 0x3161550900000000, - 0x1604708800000000, 0xe2700e1400000000, 0xc5152b9500000000, - 0xedbc35cd00000000, 0xcad9104c00000000, 0xbdee087d00000000, - 0x9a8b2dfc00000000, 0xb22233a400000000, 0x9547162500000000, - 0x5c4c03c600000000, 0x7b29264700000000, 0x5380381f00000000, - 0x74e51d9e00000000, 0x03d205af00000000, 0x24b7202e00000000, - 0x0c1e3e7600000000, 0x2b7b1bf700000000, 0xa5f1b39500000000, - 0x8294961400000000, 0xaa3d884c00000000, 0x8d58adcd00000000, - 0xfa6fb5fc00000000, 0xdd0a907d00000000, 0xf5a38e2500000000, - 0xd2c6aba400000000, 0x1bcdbe4700000000, 0x3ca89bc600000000, - 0x1401859e00000000, 0x3364a01f00000000, 0x4453b82e00000000, - 0x63369daf00000000, 0x4b9f83f700000000, 0x6cfaa67600000000, - 0x988ed8ea00000000, 0xbfebfd6b00000000, 0x9742e33300000000, - 0xb027c6b200000000, 0xc710de8300000000, 0xe075fb0200000000, - 0xc8dce55a00000000, 0xefb9c0db00000000, 0x26b2d53800000000, - 0x01d7f0b900000000, 0x297eeee100000000, 0x0e1bcb6000000000, - 0x792cd35100000000, 0x5e49f6d000000000, 0x76e0e88800000000, - 0x5185cd0900000000}}; - -#else /* W == 4 */ - -local const z_crc_t FAR crc_braid_table[][256] = { - {0x00000000, 0x9ba54c6f, 0xec3b9e9f, 0x779ed2f0, 0x03063b7f, - 0x98a37710, 0xef3da5e0, 0x7498e98f, 0x060c76fe, 0x9da93a91, - 0xea37e861, 0x7192a40e, 0x050a4d81, 0x9eaf01ee, 0xe931d31e, - 0x72949f71, 0x0c18edfc, 0x97bda193, 0xe0237363, 0x7b863f0c, - 0x0f1ed683, 0x94bb9aec, 0xe325481c, 0x78800473, 0x0a149b02, - 0x91b1d76d, 0xe62f059d, 0x7d8a49f2, 0x0912a07d, 0x92b7ec12, - 0xe5293ee2, 0x7e8c728d, 0x1831dbf8, 0x83949797, 0xf40a4567, - 0x6faf0908, 0x1b37e087, 0x8092ace8, 0xf70c7e18, 0x6ca93277, - 0x1e3dad06, 0x8598e169, 0xf2063399, 0x69a37ff6, 0x1d3b9679, - 0x869eda16, 0xf10008e6, 0x6aa54489, 0x14293604, 0x8f8c7a6b, - 0xf812a89b, 0x63b7e4f4, 0x172f0d7b, 0x8c8a4114, 0xfb1493e4, - 0x60b1df8b, 0x122540fa, 0x89800c95, 0xfe1ede65, 0x65bb920a, - 0x11237b85, 0x8a8637ea, 0xfd18e51a, 0x66bda975, 0x3063b7f0, - 0xabc6fb9f, 0xdc58296f, 0x47fd6500, 0x33658c8f, 0xa8c0c0e0, - 0xdf5e1210, 0x44fb5e7f, 0x366fc10e, 0xadca8d61, 0xda545f91, - 0x41f113fe, 0x3569fa71, 0xaeccb61e, 0xd95264ee, 0x42f72881, - 0x3c7b5a0c, 0xa7de1663, 0xd040c493, 0x4be588fc, 0x3f7d6173, - 0xa4d82d1c, 0xd346ffec, 0x48e3b383, 0x3a772cf2, 0xa1d2609d, - 0xd64cb26d, 0x4de9fe02, 0x3971178d, 0xa2d45be2, 0xd54a8912, - 0x4eefc57d, 0x28526c08, 0xb3f72067, 0xc469f297, 0x5fccbef8, - 0x2b545777, 0xb0f11b18, 0xc76fc9e8, 0x5cca8587, 0x2e5e1af6, - 0xb5fb5699, 0xc2658469, 0x59c0c806, 0x2d582189, 0xb6fd6de6, - 0xc163bf16, 0x5ac6f379, 0x244a81f4, 0xbfefcd9b, 0xc8711f6b, - 0x53d45304, 0x274cba8b, 0xbce9f6e4, 0xcb772414, 0x50d2687b, - 0x2246f70a, 0xb9e3bb65, 0xce7d6995, 0x55d825fa, 0x2140cc75, - 0xbae5801a, 0xcd7b52ea, 0x56de1e85, 0x60c76fe0, 0xfb62238f, - 0x8cfcf17f, 0x1759bd10, 0x63c1549f, 0xf86418f0, 0x8ffaca00, - 0x145f866f, 0x66cb191e, 0xfd6e5571, 0x8af08781, 0x1155cbee, - 0x65cd2261, 0xfe686e0e, 0x89f6bcfe, 0x1253f091, 0x6cdf821c, - 0xf77ace73, 0x80e41c83, 0x1b4150ec, 0x6fd9b963, 0xf47cf50c, - 0x83e227fc, 0x18476b93, 0x6ad3f4e2, 0xf176b88d, 0x86e86a7d, - 0x1d4d2612, 0x69d5cf9d, 0xf27083f2, 0x85ee5102, 0x1e4b1d6d, - 0x78f6b418, 0xe353f877, 0x94cd2a87, 0x0f6866e8, 0x7bf08f67, - 0xe055c308, 0x97cb11f8, 0x0c6e5d97, 0x7efac2e6, 0xe55f8e89, - 0x92c15c79, 0x09641016, 0x7dfcf999, 0xe659b5f6, 0x91c76706, - 0x0a622b69, 0x74ee59e4, 0xef4b158b, 0x98d5c77b, 0x03708b14, - 0x77e8629b, 0xec4d2ef4, 0x9bd3fc04, 0x0076b06b, 0x72e22f1a, - 0xe9476375, 0x9ed9b185, 0x057cfdea, 0x71e41465, 0xea41580a, - 0x9ddf8afa, 0x067ac695, 0x50a4d810, 0xcb01947f, 0xbc9f468f, - 0x273a0ae0, 0x53a2e36f, 0xc807af00, 0xbf997df0, 0x243c319f, - 0x56a8aeee, 0xcd0de281, 0xba933071, 0x21367c1e, 0x55ae9591, - 0xce0bd9fe, 0xb9950b0e, 0x22304761, 0x5cbc35ec, 0xc7197983, - 0xb087ab73, 0x2b22e71c, 0x5fba0e93, 0xc41f42fc, 0xb381900c, - 0x2824dc63, 0x5ab04312, 0xc1150f7d, 0xb68bdd8d, 0x2d2e91e2, - 0x59b6786d, 0xc2133402, 0xb58de6f2, 0x2e28aa9d, 0x489503e8, - 0xd3304f87, 0xa4ae9d77, 0x3f0bd118, 0x4b933897, 0xd03674f8, - 0xa7a8a608, 0x3c0dea67, 0x4e997516, 0xd53c3979, 0xa2a2eb89, - 0x3907a7e6, 0x4d9f4e69, 0xd63a0206, 0xa1a4d0f6, 0x3a019c99, - 0x448dee14, 0xdf28a27b, 0xa8b6708b, 0x33133ce4, 0x478bd56b, - 0xdc2e9904, 0xabb04bf4, 0x3015079b, 0x428198ea, 0xd924d485, - 0xaeba0675, 0x351f4a1a, 0x4187a395, 0xda22effa, 0xadbc3d0a, - 0x36197165}, - {0x00000000, 0xc18edfc0, 0x586cb9c1, 0x99e26601, 0xb0d97382, - 0x7157ac42, 0xe8b5ca43, 0x293b1583, 0xbac3e145, 0x7b4d3e85, - 0xe2af5884, 0x23218744, 0x0a1a92c7, 0xcb944d07, 0x52762b06, - 0x93f8f4c6, 0xaef6c4cb, 0x6f781b0b, 0xf69a7d0a, 0x3714a2ca, - 0x1e2fb749, 0xdfa16889, 0x46430e88, 0x87cdd148, 0x1435258e, - 0xd5bbfa4e, 0x4c599c4f, 0x8dd7438f, 0xa4ec560c, 0x656289cc, - 0xfc80efcd, 0x3d0e300d, 0x869c8fd7, 0x47125017, 0xdef03616, - 0x1f7ee9d6, 0x3645fc55, 0xf7cb2395, 0x6e294594, 0xafa79a54, - 0x3c5f6e92, 0xfdd1b152, 0x6433d753, 0xa5bd0893, 0x8c861d10, - 0x4d08c2d0, 0xd4eaa4d1, 0x15647b11, 0x286a4b1c, 0xe9e494dc, - 0x7006f2dd, 0xb1882d1d, 0x98b3389e, 0x593de75e, 0xc0df815f, - 0x01515e9f, 0x92a9aa59, 0x53277599, 0xcac51398, 0x0b4bcc58, - 0x2270d9db, 0xe3fe061b, 0x7a1c601a, 0xbb92bfda, 0xd64819ef, - 0x17c6c62f, 0x8e24a02e, 0x4faa7fee, 0x66916a6d, 0xa71fb5ad, - 0x3efdd3ac, 0xff730c6c, 0x6c8bf8aa, 0xad05276a, 0x34e7416b, - 0xf5699eab, 0xdc528b28, 0x1ddc54e8, 0x843e32e9, 0x45b0ed29, - 0x78bedd24, 0xb93002e4, 0x20d264e5, 0xe15cbb25, 0xc867aea6, - 0x09e97166, 0x900b1767, 0x5185c8a7, 0xc27d3c61, 0x03f3e3a1, - 0x9a1185a0, 0x5b9f5a60, 0x72a44fe3, 0xb32a9023, 0x2ac8f622, - 0xeb4629e2, 0x50d49638, 0x915a49f8, 0x08b82ff9, 0xc936f039, - 0xe00de5ba, 0x21833a7a, 0xb8615c7b, 0x79ef83bb, 0xea17777d, - 0x2b99a8bd, 0xb27bcebc, 0x73f5117c, 0x5ace04ff, 0x9b40db3f, - 0x02a2bd3e, 0xc32c62fe, 0xfe2252f3, 0x3fac8d33, 0xa64eeb32, - 0x67c034f2, 0x4efb2171, 0x8f75feb1, 0x169798b0, 0xd7194770, - 0x44e1b3b6, 0x856f6c76, 0x1c8d0a77, 0xdd03d5b7, 0xf438c034, - 0x35b61ff4, 0xac5479f5, 0x6ddaa635, 0x77e1359f, 0xb66fea5f, - 0x2f8d8c5e, 0xee03539e, 0xc738461d, 0x06b699dd, 0x9f54ffdc, - 0x5eda201c, 0xcd22d4da, 0x0cac0b1a, 0x954e6d1b, 0x54c0b2db, - 0x7dfba758, 0xbc757898, 0x25971e99, 0xe419c159, 0xd917f154, - 0x18992e94, 0x817b4895, 0x40f59755, 0x69ce82d6, 0xa8405d16, - 0x31a23b17, 0xf02ce4d7, 0x63d41011, 0xa25acfd1, 0x3bb8a9d0, - 0xfa367610, 0xd30d6393, 0x1283bc53, 0x8b61da52, 0x4aef0592, - 0xf17dba48, 0x30f36588, 0xa9110389, 0x689fdc49, 0x41a4c9ca, - 0x802a160a, 0x19c8700b, 0xd846afcb, 0x4bbe5b0d, 0x8a3084cd, - 0x13d2e2cc, 0xd25c3d0c, 0xfb67288f, 0x3ae9f74f, 0xa30b914e, - 0x62854e8e, 0x5f8b7e83, 0x9e05a143, 0x07e7c742, 0xc6691882, - 0xef520d01, 0x2edcd2c1, 0xb73eb4c0, 0x76b06b00, 0xe5489fc6, - 0x24c64006, 0xbd242607, 0x7caaf9c7, 0x5591ec44, 0x941f3384, - 0x0dfd5585, 0xcc738a45, 0xa1a92c70, 0x6027f3b0, 0xf9c595b1, - 0x384b4a71, 0x11705ff2, 0xd0fe8032, 0x491ce633, 0x889239f3, - 0x1b6acd35, 0xdae412f5, 0x430674f4, 0x8288ab34, 0xabb3beb7, - 0x6a3d6177, 0xf3df0776, 0x3251d8b6, 0x0f5fe8bb, 0xced1377b, - 0x5733517a, 0x96bd8eba, 0xbf869b39, 0x7e0844f9, 0xe7ea22f8, - 0x2664fd38, 0xb59c09fe, 0x7412d63e, 0xedf0b03f, 0x2c7e6fff, - 0x05457a7c, 0xc4cba5bc, 0x5d29c3bd, 0x9ca71c7d, 0x2735a3a7, - 0xe6bb7c67, 0x7f591a66, 0xbed7c5a6, 0x97ecd025, 0x56620fe5, - 0xcf8069e4, 0x0e0eb624, 0x9df642e2, 0x5c789d22, 0xc59afb23, - 0x041424e3, 0x2d2f3160, 0xeca1eea0, 0x754388a1, 0xb4cd5761, - 0x89c3676c, 0x484db8ac, 0xd1afdead, 0x1021016d, 0x391a14ee, - 0xf894cb2e, 0x6176ad2f, 0xa0f872ef, 0x33008629, 0xf28e59e9, - 0x6b6c3fe8, 0xaae2e028, 0x83d9f5ab, 0x42572a6b, 0xdbb54c6a, - 0x1a3b93aa}, - {0x00000000, 0xefc26b3e, 0x04f5d03d, 0xeb37bb03, 0x09eba07a, - 0xe629cb44, 0x0d1e7047, 0xe2dc1b79, 0x13d740f4, 0xfc152bca, - 0x172290c9, 0xf8e0fbf7, 0x1a3ce08e, 0xf5fe8bb0, 0x1ec930b3, - 0xf10b5b8d, 0x27ae81e8, 0xc86cead6, 0x235b51d5, 0xcc993aeb, - 0x2e452192, 0xc1874aac, 0x2ab0f1af, 0xc5729a91, 0x3479c11c, - 0xdbbbaa22, 0x308c1121, 0xdf4e7a1f, 0x3d926166, 0xd2500a58, - 0x3967b15b, 0xd6a5da65, 0x4f5d03d0, 0xa09f68ee, 0x4ba8d3ed, - 0xa46ab8d3, 0x46b6a3aa, 0xa974c894, 0x42437397, 0xad8118a9, - 0x5c8a4324, 0xb348281a, 0x587f9319, 0xb7bdf827, 0x5561e35e, - 0xbaa38860, 0x51943363, 0xbe56585d, 0x68f38238, 0x8731e906, - 0x6c065205, 0x83c4393b, 0x61182242, 0x8eda497c, 0x65edf27f, - 0x8a2f9941, 0x7b24c2cc, 0x94e6a9f2, 0x7fd112f1, 0x901379cf, - 0x72cf62b6, 0x9d0d0988, 0x763ab28b, 0x99f8d9b5, 0x9eba07a0, - 0x71786c9e, 0x9a4fd79d, 0x758dbca3, 0x9751a7da, 0x7893cce4, - 0x93a477e7, 0x7c661cd9, 0x8d6d4754, 0x62af2c6a, 0x89989769, - 0x665afc57, 0x8486e72e, 0x6b448c10, 0x80733713, 0x6fb15c2d, - 0xb9148648, 0x56d6ed76, 0xbde15675, 0x52233d4b, 0xb0ff2632, - 0x5f3d4d0c, 0xb40af60f, 0x5bc89d31, 0xaac3c6bc, 0x4501ad82, - 0xae361681, 0x41f47dbf, 0xa32866c6, 0x4cea0df8, 0xa7ddb6fb, - 0x481fddc5, 0xd1e70470, 0x3e256f4e, 0xd512d44d, 0x3ad0bf73, - 0xd80ca40a, 0x37cecf34, 0xdcf97437, 0x333b1f09, 0xc2304484, - 0x2df22fba, 0xc6c594b9, 0x2907ff87, 0xcbdbe4fe, 0x24198fc0, - 0xcf2e34c3, 0x20ec5ffd, 0xf6498598, 0x198beea6, 0xf2bc55a5, - 0x1d7e3e9b, 0xffa225e2, 0x10604edc, 0xfb57f5df, 0x14959ee1, - 0xe59ec56c, 0x0a5cae52, 0xe16b1551, 0x0ea97e6f, 0xec756516, - 0x03b70e28, 0xe880b52b, 0x0742de15, 0xe6050901, 0x09c7623f, - 0xe2f0d93c, 0x0d32b202, 0xefeea97b, 0x002cc245, 0xeb1b7946, - 0x04d91278, 0xf5d249f5, 0x1a1022cb, 0xf12799c8, 0x1ee5f2f6, - 0xfc39e98f, 0x13fb82b1, 0xf8cc39b2, 0x170e528c, 0xc1ab88e9, - 0x2e69e3d7, 0xc55e58d4, 0x2a9c33ea, 0xc8402893, 0x278243ad, - 0xccb5f8ae, 0x23779390, 0xd27cc81d, 0x3dbea323, 0xd6891820, - 0x394b731e, 0xdb976867, 0x34550359, 0xdf62b85a, 0x30a0d364, - 0xa9580ad1, 0x469a61ef, 0xadaddaec, 0x426fb1d2, 0xa0b3aaab, - 0x4f71c195, 0xa4467a96, 0x4b8411a8, 0xba8f4a25, 0x554d211b, - 0xbe7a9a18, 0x51b8f126, 0xb364ea5f, 0x5ca68161, 0xb7913a62, - 0x5853515c, 0x8ef68b39, 0x6134e007, 0x8a035b04, 0x65c1303a, - 0x871d2b43, 0x68df407d, 0x83e8fb7e, 0x6c2a9040, 0x9d21cbcd, - 0x72e3a0f3, 0x99d41bf0, 0x761670ce, 0x94ca6bb7, 0x7b080089, - 0x903fbb8a, 0x7ffdd0b4, 0x78bf0ea1, 0x977d659f, 0x7c4ade9c, - 0x9388b5a2, 0x7154aedb, 0x9e96c5e5, 0x75a17ee6, 0x9a6315d8, - 0x6b684e55, 0x84aa256b, 0x6f9d9e68, 0x805ff556, 0x6283ee2f, - 0x8d418511, 0x66763e12, 0x89b4552c, 0x5f118f49, 0xb0d3e477, - 0x5be45f74, 0xb426344a, 0x56fa2f33, 0xb938440d, 0x520fff0e, - 0xbdcd9430, 0x4cc6cfbd, 0xa304a483, 0x48331f80, 0xa7f174be, - 0x452d6fc7, 0xaaef04f9, 0x41d8bffa, 0xae1ad4c4, 0x37e20d71, - 0xd820664f, 0x3317dd4c, 0xdcd5b672, 0x3e09ad0b, 0xd1cbc635, - 0x3afc7d36, 0xd53e1608, 0x24354d85, 0xcbf726bb, 0x20c09db8, - 0xcf02f686, 0x2ddeedff, 0xc21c86c1, 0x292b3dc2, 0xc6e956fc, - 0x104c8c99, 0xff8ee7a7, 0x14b95ca4, 0xfb7b379a, 0x19a72ce3, - 0xf66547dd, 0x1d52fcde, 0xf29097e0, 0x039bcc6d, 0xec59a753, - 0x076e1c50, 0xe8ac776e, 0x0a706c17, 0xe5b20729, 0x0e85bc2a, - 0xe147d714}, - {0x00000000, 0x177b1443, 0x2ef62886, 0x398d3cc5, 0x5dec510c, - 0x4a97454f, 0x731a798a, 0x64616dc9, 0xbbd8a218, 0xaca3b65b, - 0x952e8a9e, 0x82559edd, 0xe634f314, 0xf14fe757, 0xc8c2db92, - 0xdfb9cfd1, 0xacc04271, 0xbbbb5632, 0x82366af7, 0x954d7eb4, - 0xf12c137d, 0xe657073e, 0xdfda3bfb, 0xc8a12fb8, 0x1718e069, - 0x0063f42a, 0x39eec8ef, 0x2e95dcac, 0x4af4b165, 0x5d8fa526, - 0x640299e3, 0x73798da0, 0x82f182a3, 0x958a96e0, 0xac07aa25, - 0xbb7cbe66, 0xdf1dd3af, 0xc866c7ec, 0xf1ebfb29, 0xe690ef6a, - 0x392920bb, 0x2e5234f8, 0x17df083d, 0x00a41c7e, 0x64c571b7, - 0x73be65f4, 0x4a335931, 0x5d484d72, 0x2e31c0d2, 0x394ad491, - 0x00c7e854, 0x17bcfc17, 0x73dd91de, 0x64a6859d, 0x5d2bb958, - 0x4a50ad1b, 0x95e962ca, 0x82927689, 0xbb1f4a4c, 0xac645e0f, - 0xc80533c6, 0xdf7e2785, 0xe6f31b40, 0xf1880f03, 0xde920307, - 0xc9e91744, 0xf0642b81, 0xe71f3fc2, 0x837e520b, 0x94054648, - 0xad887a8d, 0xbaf36ece, 0x654aa11f, 0x7231b55c, 0x4bbc8999, - 0x5cc79dda, 0x38a6f013, 0x2fdde450, 0x1650d895, 0x012bccd6, - 0x72524176, 0x65295535, 0x5ca469f0, 0x4bdf7db3, 0x2fbe107a, - 0x38c50439, 0x014838fc, 0x16332cbf, 0xc98ae36e, 0xdef1f72d, - 0xe77ccbe8, 0xf007dfab, 0x9466b262, 0x831da621, 0xba909ae4, - 0xadeb8ea7, 0x5c6381a4, 0x4b1895e7, 0x7295a922, 0x65eebd61, - 0x018fd0a8, 0x16f4c4eb, 0x2f79f82e, 0x3802ec6d, 0xe7bb23bc, - 0xf0c037ff, 0xc94d0b3a, 0xde361f79, 0xba5772b0, 0xad2c66f3, - 0x94a15a36, 0x83da4e75, 0xf0a3c3d5, 0xe7d8d796, 0xde55eb53, - 0xc92eff10, 0xad4f92d9, 0xba34869a, 0x83b9ba5f, 0x94c2ae1c, - 0x4b7b61cd, 0x5c00758e, 0x658d494b, 0x72f65d08, 0x169730c1, - 0x01ec2482, 0x38611847, 0x2f1a0c04, 0x6655004f, 0x712e140c, - 0x48a328c9, 0x5fd83c8a, 0x3bb95143, 0x2cc24500, 0x154f79c5, - 0x02346d86, 0xdd8da257, 0xcaf6b614, 0xf37b8ad1, 0xe4009e92, - 0x8061f35b, 0x971ae718, 0xae97dbdd, 0xb9eccf9e, 0xca95423e, - 0xddee567d, 0xe4636ab8, 0xf3187efb, 0x97791332, 0x80020771, - 0xb98f3bb4, 0xaef42ff7, 0x714de026, 0x6636f465, 0x5fbbc8a0, - 0x48c0dce3, 0x2ca1b12a, 0x3bdaa569, 0x025799ac, 0x152c8def, - 0xe4a482ec, 0xf3df96af, 0xca52aa6a, 0xdd29be29, 0xb948d3e0, - 0xae33c7a3, 0x97befb66, 0x80c5ef25, 0x5f7c20f4, 0x480734b7, - 0x718a0872, 0x66f11c31, 0x029071f8, 0x15eb65bb, 0x2c66597e, - 0x3b1d4d3d, 0x4864c09d, 0x5f1fd4de, 0x6692e81b, 0x71e9fc58, - 0x15889191, 0x02f385d2, 0x3b7eb917, 0x2c05ad54, 0xf3bc6285, - 0xe4c776c6, 0xdd4a4a03, 0xca315e40, 0xae503389, 0xb92b27ca, - 0x80a61b0f, 0x97dd0f4c, 0xb8c70348, 0xafbc170b, 0x96312bce, - 0x814a3f8d, 0xe52b5244, 0xf2504607, 0xcbdd7ac2, 0xdca66e81, - 0x031fa150, 0x1464b513, 0x2de989d6, 0x3a929d95, 0x5ef3f05c, - 0x4988e41f, 0x7005d8da, 0x677ecc99, 0x14074139, 0x037c557a, - 0x3af169bf, 0x2d8a7dfc, 0x49eb1035, 0x5e900476, 0x671d38b3, - 0x70662cf0, 0xafdfe321, 0xb8a4f762, 0x8129cba7, 0x9652dfe4, - 0xf233b22d, 0xe548a66e, 0xdcc59aab, 0xcbbe8ee8, 0x3a3681eb, - 0x2d4d95a8, 0x14c0a96d, 0x03bbbd2e, 0x67dad0e7, 0x70a1c4a4, - 0x492cf861, 0x5e57ec22, 0x81ee23f3, 0x969537b0, 0xaf180b75, - 0xb8631f36, 0xdc0272ff, 0xcb7966bc, 0xf2f45a79, 0xe58f4e3a, - 0x96f6c39a, 0x818dd7d9, 0xb800eb1c, 0xaf7bff5f, 0xcb1a9296, - 0xdc6186d5, 0xe5ecba10, 0xf297ae53, 0x2d2e6182, 0x3a5575c1, - 0x03d84904, 0x14a35d47, 0x70c2308e, 0x67b924cd, 0x5e341808, - 0x494f0c4b}}; - -local const z_word_t FAR crc_braid_big_table[][256] = { - {0x00000000, 0x43147b17, 0x8628f62e, 0xc53c8d39, 0x0c51ec5d, - 0x4f45974a, 0x8a791a73, 0xc96d6164, 0x18a2d8bb, 0x5bb6a3ac, - 0x9e8a2e95, 0xdd9e5582, 0x14f334e6, 0x57e74ff1, 0x92dbc2c8, - 0xd1cfb9df, 0x7142c0ac, 0x3256bbbb, 0xf76a3682, 0xb47e4d95, - 0x7d132cf1, 0x3e0757e6, 0xfb3bdadf, 0xb82fa1c8, 0x69e01817, - 0x2af46300, 0xefc8ee39, 0xacdc952e, 0x65b1f44a, 0x26a58f5d, - 0xe3990264, 0xa08d7973, 0xa382f182, 0xe0968a95, 0x25aa07ac, - 0x66be7cbb, 0xafd31ddf, 0xecc766c8, 0x29fbebf1, 0x6aef90e6, - 0xbb202939, 0xf834522e, 0x3d08df17, 0x7e1ca400, 0xb771c564, - 0xf465be73, 0x3159334a, 0x724d485d, 0xd2c0312e, 0x91d44a39, - 0x54e8c700, 0x17fcbc17, 0xde91dd73, 0x9d85a664, 0x58b92b5d, - 0x1bad504a, 0xca62e995, 0x89769282, 0x4c4a1fbb, 0x0f5e64ac, - 0xc63305c8, 0x85277edf, 0x401bf3e6, 0x030f88f1, 0x070392de, - 0x4417e9c9, 0x812b64f0, 0xc23f1fe7, 0x0b527e83, 0x48460594, - 0x8d7a88ad, 0xce6ef3ba, 0x1fa14a65, 0x5cb53172, 0x9989bc4b, - 0xda9dc75c, 0x13f0a638, 0x50e4dd2f, 0x95d85016, 0xd6cc2b01, - 0x76415272, 0x35552965, 0xf069a45c, 0xb37ddf4b, 0x7a10be2f, - 0x3904c538, 0xfc384801, 0xbf2c3316, 0x6ee38ac9, 0x2df7f1de, - 0xe8cb7ce7, 0xabdf07f0, 0x62b26694, 0x21a61d83, 0xe49a90ba, - 0xa78eebad, 0xa481635c, 0xe795184b, 0x22a99572, 0x61bdee65, - 0xa8d08f01, 0xebc4f416, 0x2ef8792f, 0x6dec0238, 0xbc23bbe7, - 0xff37c0f0, 0x3a0b4dc9, 0x791f36de, 0xb07257ba, 0xf3662cad, - 0x365aa194, 0x754eda83, 0xd5c3a3f0, 0x96d7d8e7, 0x53eb55de, - 0x10ff2ec9, 0xd9924fad, 0x9a8634ba, 0x5fbab983, 0x1caec294, - 0xcd617b4b, 0x8e75005c, 0x4b498d65, 0x085df672, 0xc1309716, - 0x8224ec01, 0x47186138, 0x040c1a2f, 0x4f005566, 0x0c142e71, - 0xc928a348, 0x8a3cd85f, 0x4351b93b, 0x0045c22c, 0xc5794f15, - 0x866d3402, 0x57a28ddd, 0x14b6f6ca, 0xd18a7bf3, 0x929e00e4, - 0x5bf36180, 0x18e71a97, 0xdddb97ae, 0x9ecfecb9, 0x3e4295ca, - 0x7d56eedd, 0xb86a63e4, 0xfb7e18f3, 0x32137997, 0x71070280, - 0xb43b8fb9, 0xf72ff4ae, 0x26e04d71, 0x65f43666, 0xa0c8bb5f, - 0xe3dcc048, 0x2ab1a12c, 0x69a5da3b, 0xac995702, 0xef8d2c15, - 0xec82a4e4, 0xaf96dff3, 0x6aaa52ca, 0x29be29dd, 0xe0d348b9, - 0xa3c733ae, 0x66fbbe97, 0x25efc580, 0xf4207c5f, 0xb7340748, - 0x72088a71, 0x311cf166, 0xf8719002, 0xbb65eb15, 0x7e59662c, - 0x3d4d1d3b, 0x9dc06448, 0xded41f5f, 0x1be89266, 0x58fce971, - 0x91918815, 0xd285f302, 0x17b97e3b, 0x54ad052c, 0x8562bcf3, - 0xc676c7e4, 0x034a4add, 0x405e31ca, 0x893350ae, 0xca272bb9, - 0x0f1ba680, 0x4c0fdd97, 0x4803c7b8, 0x0b17bcaf, 0xce2b3196, - 0x8d3f4a81, 0x44522be5, 0x074650f2, 0xc27addcb, 0x816ea6dc, - 0x50a11f03, 0x13b56414, 0xd689e92d, 0x959d923a, 0x5cf0f35e, - 0x1fe48849, 0xdad80570, 0x99cc7e67, 0x39410714, 0x7a557c03, - 0xbf69f13a, 0xfc7d8a2d, 0x3510eb49, 0x7604905e, 0xb3381d67, - 0xf02c6670, 0x21e3dfaf, 0x62f7a4b8, 0xa7cb2981, 0xe4df5296, - 0x2db233f2, 0x6ea648e5, 0xab9ac5dc, 0xe88ebecb, 0xeb81363a, - 0xa8954d2d, 0x6da9c014, 0x2ebdbb03, 0xe7d0da67, 0xa4c4a170, - 0x61f82c49, 0x22ec575e, 0xf323ee81, 0xb0379596, 0x750b18af, - 0x361f63b8, 0xff7202dc, 0xbc6679cb, 0x795af4f2, 0x3a4e8fe5, - 0x9ac3f696, 0xd9d78d81, 0x1ceb00b8, 0x5fff7baf, 0x96921acb, - 0xd58661dc, 0x10baece5, 0x53ae97f2, 0x82612e2d, 0xc175553a, - 0x0449d803, 0x475da314, 0x8e30c270, 0xcd24b967, 0x0818345e, - 0x4b0c4f49}, - {0x00000000, 0x3e6bc2ef, 0x3dd0f504, 0x03bb37eb, 0x7aa0eb09, - 0x44cb29e6, 0x47701e0d, 0x791bdce2, 0xf440d713, 0xca2b15fc, - 0xc9902217, 0xf7fbe0f8, 0x8ee03c1a, 0xb08bfef5, 0xb330c91e, - 0x8d5b0bf1, 0xe881ae27, 0xd6ea6cc8, 0xd5515b23, 0xeb3a99cc, - 0x9221452e, 0xac4a87c1, 0xaff1b02a, 0x919a72c5, 0x1cc17934, - 0x22aabbdb, 0x21118c30, 0x1f7a4edf, 0x6661923d, 0x580a50d2, - 0x5bb16739, 0x65daa5d6, 0xd0035d4f, 0xee689fa0, 0xedd3a84b, - 0xd3b86aa4, 0xaaa3b646, 0x94c874a9, 0x97734342, 0xa91881ad, - 0x24438a5c, 0x1a2848b3, 0x19937f58, 0x27f8bdb7, 0x5ee36155, - 0x6088a3ba, 0x63339451, 0x5d5856be, 0x3882f368, 0x06e93187, - 0x0552066c, 0x3b39c483, 0x42221861, 0x7c49da8e, 0x7ff2ed65, - 0x41992f8a, 0xccc2247b, 0xf2a9e694, 0xf112d17f, 0xcf791390, - 0xb662cf72, 0x88090d9d, 0x8bb23a76, 0xb5d9f899, 0xa007ba9e, - 0x9e6c7871, 0x9dd74f9a, 0xa3bc8d75, 0xdaa75197, 0xe4cc9378, - 0xe777a493, 0xd91c667c, 0x54476d8d, 0x6a2caf62, 0x69979889, - 0x57fc5a66, 0x2ee78684, 0x108c446b, 0x13377380, 0x2d5cb16f, - 0x488614b9, 0x76edd656, 0x7556e1bd, 0x4b3d2352, 0x3226ffb0, - 0x0c4d3d5f, 0x0ff60ab4, 0x319dc85b, 0xbcc6c3aa, 0x82ad0145, - 0x811636ae, 0xbf7df441, 0xc66628a3, 0xf80dea4c, 0xfbb6dda7, - 0xc5dd1f48, 0x7004e7d1, 0x4e6f253e, 0x4dd412d5, 0x73bfd03a, - 0x0aa40cd8, 0x34cfce37, 0x3774f9dc, 0x091f3b33, 0x844430c2, - 0xba2ff22d, 0xb994c5c6, 0x87ff0729, 0xfee4dbcb, 0xc08f1924, - 0xc3342ecf, 0xfd5fec20, 0x988549f6, 0xa6ee8b19, 0xa555bcf2, - 0x9b3e7e1d, 0xe225a2ff, 0xdc4e6010, 0xdff557fb, 0xe19e9514, - 0x6cc59ee5, 0x52ae5c0a, 0x51156be1, 0x6f7ea90e, 0x166575ec, - 0x280eb703, 0x2bb580e8, 0x15de4207, 0x010905e6, 0x3f62c709, - 0x3cd9f0e2, 0x02b2320d, 0x7ba9eeef, 0x45c22c00, 0x46791beb, - 0x7812d904, 0xf549d2f5, 0xcb22101a, 0xc89927f1, 0xf6f2e51e, - 0x8fe939fc, 0xb182fb13, 0xb239ccf8, 0x8c520e17, 0xe988abc1, - 0xd7e3692e, 0xd4585ec5, 0xea339c2a, 0x932840c8, 0xad438227, - 0xaef8b5cc, 0x90937723, 0x1dc87cd2, 0x23a3be3d, 0x201889d6, - 0x1e734b39, 0x676897db, 0x59035534, 0x5ab862df, 0x64d3a030, - 0xd10a58a9, 0xef619a46, 0xecdaadad, 0xd2b16f42, 0xabaab3a0, - 0x95c1714f, 0x967a46a4, 0xa811844b, 0x254a8fba, 0x1b214d55, - 0x189a7abe, 0x26f1b851, 0x5fea64b3, 0x6181a65c, 0x623a91b7, - 0x5c515358, 0x398bf68e, 0x07e03461, 0x045b038a, 0x3a30c165, - 0x432b1d87, 0x7d40df68, 0x7efbe883, 0x40902a6c, 0xcdcb219d, - 0xf3a0e372, 0xf01bd499, 0xce701676, 0xb76bca94, 0x8900087b, - 0x8abb3f90, 0xb4d0fd7f, 0xa10ebf78, 0x9f657d97, 0x9cde4a7c, - 0xa2b58893, 0xdbae5471, 0xe5c5969e, 0xe67ea175, 0xd815639a, - 0x554e686b, 0x6b25aa84, 0x689e9d6f, 0x56f55f80, 0x2fee8362, - 0x1185418d, 0x123e7666, 0x2c55b489, 0x498f115f, 0x77e4d3b0, - 0x745fe45b, 0x4a3426b4, 0x332ffa56, 0x0d4438b9, 0x0eff0f52, - 0x3094cdbd, 0xbdcfc64c, 0x83a404a3, 0x801f3348, 0xbe74f1a7, - 0xc76f2d45, 0xf904efaa, 0xfabfd841, 0xc4d41aae, 0x710de237, - 0x4f6620d8, 0x4cdd1733, 0x72b6d5dc, 0x0bad093e, 0x35c6cbd1, - 0x367dfc3a, 0x08163ed5, 0x854d3524, 0xbb26f7cb, 0xb89dc020, - 0x86f602cf, 0xffedde2d, 0xc1861cc2, 0xc23d2b29, 0xfc56e9c6, - 0x998c4c10, 0xa7e78eff, 0xa45cb914, 0x9a377bfb, 0xe32ca719, - 0xdd4765f6, 0xdefc521d, 0xe09790f2, 0x6dcc9b03, 0x53a759ec, - 0x501c6e07, 0x6e77ace8, 0x176c700a, 0x2907b2e5, 0x2abc850e, - 0x14d747e1}, - {0x00000000, 0xc0df8ec1, 0xc1b96c58, 0x0166e299, 0x8273d9b0, - 0x42ac5771, 0x43cab5e8, 0x83153b29, 0x45e1c3ba, 0x853e4d7b, - 0x8458afe2, 0x44872123, 0xc7921a0a, 0x074d94cb, 0x062b7652, - 0xc6f4f893, 0xcbc4f6ae, 0x0b1b786f, 0x0a7d9af6, 0xcaa21437, - 0x49b72f1e, 0x8968a1df, 0x880e4346, 0x48d1cd87, 0x8e253514, - 0x4efabbd5, 0x4f9c594c, 0x8f43d78d, 0x0c56eca4, 0xcc896265, - 0xcdef80fc, 0x0d300e3d, 0xd78f9c86, 0x17501247, 0x1636f0de, - 0xd6e97e1f, 0x55fc4536, 0x9523cbf7, 0x9445296e, 0x549aa7af, - 0x926e5f3c, 0x52b1d1fd, 0x53d73364, 0x9308bda5, 0x101d868c, - 0xd0c2084d, 0xd1a4ead4, 0x117b6415, 0x1c4b6a28, 0xdc94e4e9, - 0xddf20670, 0x1d2d88b1, 0x9e38b398, 0x5ee73d59, 0x5f81dfc0, - 0x9f5e5101, 0x59aaa992, 0x99752753, 0x9813c5ca, 0x58cc4b0b, - 0xdbd97022, 0x1b06fee3, 0x1a601c7a, 0xdabf92bb, 0xef1948d6, - 0x2fc6c617, 0x2ea0248e, 0xee7faa4f, 0x6d6a9166, 0xadb51fa7, - 0xacd3fd3e, 0x6c0c73ff, 0xaaf88b6c, 0x6a2705ad, 0x6b41e734, - 0xab9e69f5, 0x288b52dc, 0xe854dc1d, 0xe9323e84, 0x29edb045, - 0x24ddbe78, 0xe40230b9, 0xe564d220, 0x25bb5ce1, 0xa6ae67c8, - 0x6671e909, 0x67170b90, 0xa7c88551, 0x613c7dc2, 0xa1e3f303, - 0xa085119a, 0x605a9f5b, 0xe34fa472, 0x23902ab3, 0x22f6c82a, - 0xe22946eb, 0x3896d450, 0xf8495a91, 0xf92fb808, 0x39f036c9, - 0xbae50de0, 0x7a3a8321, 0x7b5c61b8, 0xbb83ef79, 0x7d7717ea, - 0xbda8992b, 0xbcce7bb2, 0x7c11f573, 0xff04ce5a, 0x3fdb409b, - 0x3ebda202, 0xfe622cc3, 0xf35222fe, 0x338dac3f, 0x32eb4ea6, - 0xf234c067, 0x7121fb4e, 0xb1fe758f, 0xb0989716, 0x704719d7, - 0xb6b3e144, 0x766c6f85, 0x770a8d1c, 0xb7d503dd, 0x34c038f4, - 0xf41fb635, 0xf57954ac, 0x35a6da6d, 0x9f35e177, 0x5fea6fb6, - 0x5e8c8d2f, 0x9e5303ee, 0x1d4638c7, 0xdd99b606, 0xdcff549f, - 0x1c20da5e, 0xdad422cd, 0x1a0bac0c, 0x1b6d4e95, 0xdbb2c054, - 0x58a7fb7d, 0x987875bc, 0x991e9725, 0x59c119e4, 0x54f117d9, - 0x942e9918, 0x95487b81, 0x5597f540, 0xd682ce69, 0x165d40a8, - 0x173ba231, 0xd7e42cf0, 0x1110d463, 0xd1cf5aa2, 0xd0a9b83b, - 0x107636fa, 0x93630dd3, 0x53bc8312, 0x52da618b, 0x9205ef4a, - 0x48ba7df1, 0x8865f330, 0x890311a9, 0x49dc9f68, 0xcac9a441, - 0x0a162a80, 0x0b70c819, 0xcbaf46d8, 0x0d5bbe4b, 0xcd84308a, - 0xcce2d213, 0x0c3d5cd2, 0x8f2867fb, 0x4ff7e93a, 0x4e910ba3, - 0x8e4e8562, 0x837e8b5f, 0x43a1059e, 0x42c7e707, 0x821869c6, - 0x010d52ef, 0xc1d2dc2e, 0xc0b43eb7, 0x006bb076, 0xc69f48e5, - 0x0640c624, 0x072624bd, 0xc7f9aa7c, 0x44ec9155, 0x84331f94, - 0x8555fd0d, 0x458a73cc, 0x702ca9a1, 0xb0f32760, 0xb195c5f9, - 0x714a4b38, 0xf25f7011, 0x3280fed0, 0x33e61c49, 0xf3399288, - 0x35cd6a1b, 0xf512e4da, 0xf4740643, 0x34ab8882, 0xb7beb3ab, - 0x77613d6a, 0x7607dff3, 0xb6d85132, 0xbbe85f0f, 0x7b37d1ce, - 0x7a513357, 0xba8ebd96, 0x399b86bf, 0xf944087e, 0xf822eae7, - 0x38fd6426, 0xfe099cb5, 0x3ed61274, 0x3fb0f0ed, 0xff6f7e2c, - 0x7c7a4505, 0xbca5cbc4, 0xbdc3295d, 0x7d1ca79c, 0xa7a33527, - 0x677cbbe6, 0x661a597f, 0xa6c5d7be, 0x25d0ec97, 0xe50f6256, - 0xe46980cf, 0x24b60e0e, 0xe242f69d, 0x229d785c, 0x23fb9ac5, - 0xe3241404, 0x60312f2d, 0xa0eea1ec, 0xa1884375, 0x6157cdb4, - 0x6c67c389, 0xacb84d48, 0xaddeafd1, 0x6d012110, 0xee141a39, - 0x2ecb94f8, 0x2fad7661, 0xef72f8a0, 0x29860033, 0xe9598ef2, - 0xe83f6c6b, 0x28e0e2aa, 0xabf5d983, 0x6b2a5742, 0x6a4cb5db, - 0xaa933b1a}, - {0x00000000, 0x6f4ca59b, 0x9f9e3bec, 0xf0d29e77, 0x7f3b0603, - 0x1077a398, 0xe0a53def, 0x8fe99874, 0xfe760c06, 0x913aa99d, - 0x61e837ea, 0x0ea49271, 0x814d0a05, 0xee01af9e, 0x1ed331e9, - 0x719f9472, 0xfced180c, 0x93a1bd97, 0x637323e0, 0x0c3f867b, - 0x83d61e0f, 0xec9abb94, 0x1c4825e3, 0x73048078, 0x029b140a, - 0x6dd7b191, 0x9d052fe6, 0xf2498a7d, 0x7da01209, 0x12ecb792, - 0xe23e29e5, 0x8d728c7e, 0xf8db3118, 0x97979483, 0x67450af4, - 0x0809af6f, 0x87e0371b, 0xe8ac9280, 0x187e0cf7, 0x7732a96c, - 0x06ad3d1e, 0x69e19885, 0x993306f2, 0xf67fa369, 0x79963b1d, - 0x16da9e86, 0xe60800f1, 0x8944a56a, 0x04362914, 0x6b7a8c8f, - 0x9ba812f8, 0xf4e4b763, 0x7b0d2f17, 0x14418a8c, 0xe49314fb, - 0x8bdfb160, 0xfa402512, 0x950c8089, 0x65de1efe, 0x0a92bb65, - 0x857b2311, 0xea37868a, 0x1ae518fd, 0x75a9bd66, 0xf0b76330, - 0x9ffbc6ab, 0x6f2958dc, 0x0065fd47, 0x8f8c6533, 0xe0c0c0a8, - 0x10125edf, 0x7f5efb44, 0x0ec16f36, 0x618dcaad, 0x915f54da, - 0xfe13f141, 0x71fa6935, 0x1eb6ccae, 0xee6452d9, 0x8128f742, - 0x0c5a7b3c, 0x6316dea7, 0x93c440d0, 0xfc88e54b, 0x73617d3f, - 0x1c2dd8a4, 0xecff46d3, 0x83b3e348, 0xf22c773a, 0x9d60d2a1, - 0x6db24cd6, 0x02fee94d, 0x8d177139, 0xe25bd4a2, 0x12894ad5, - 0x7dc5ef4e, 0x086c5228, 0x6720f7b3, 0x97f269c4, 0xf8becc5f, - 0x7757542b, 0x181bf1b0, 0xe8c96fc7, 0x8785ca5c, 0xf61a5e2e, - 0x9956fbb5, 0x698465c2, 0x06c8c059, 0x8921582d, 0xe66dfdb6, - 0x16bf63c1, 0x79f3c65a, 0xf4814a24, 0x9bcdefbf, 0x6b1f71c8, - 0x0453d453, 0x8bba4c27, 0xe4f6e9bc, 0x142477cb, 0x7b68d250, - 0x0af74622, 0x65bbe3b9, 0x95697dce, 0xfa25d855, 0x75cc4021, - 0x1a80e5ba, 0xea527bcd, 0x851ede56, 0xe06fc760, 0x8f2362fb, - 0x7ff1fc8c, 0x10bd5917, 0x9f54c163, 0xf01864f8, 0x00cafa8f, - 0x6f865f14, 0x1e19cb66, 0x71556efd, 0x8187f08a, 0xeecb5511, - 0x6122cd65, 0x0e6e68fe, 0xfebcf689, 0x91f05312, 0x1c82df6c, - 0x73ce7af7, 0x831ce480, 0xec50411b, 0x63b9d96f, 0x0cf57cf4, - 0xfc27e283, 0x936b4718, 0xe2f4d36a, 0x8db876f1, 0x7d6ae886, - 0x12264d1d, 0x9dcfd569, 0xf28370f2, 0x0251ee85, 0x6d1d4b1e, - 0x18b4f678, 0x77f853e3, 0x872acd94, 0xe866680f, 0x678ff07b, - 0x08c355e0, 0xf811cb97, 0x975d6e0c, 0xe6c2fa7e, 0x898e5fe5, - 0x795cc192, 0x16106409, 0x99f9fc7d, 0xf6b559e6, 0x0667c791, - 0x692b620a, 0xe459ee74, 0x8b154bef, 0x7bc7d598, 0x148b7003, - 0x9b62e877, 0xf42e4dec, 0x04fcd39b, 0x6bb07600, 0x1a2fe272, - 0x756347e9, 0x85b1d99e, 0xeafd7c05, 0x6514e471, 0x0a5841ea, - 0xfa8adf9d, 0x95c67a06, 0x10d8a450, 0x7f9401cb, 0x8f469fbc, - 0xe00a3a27, 0x6fe3a253, 0x00af07c8, 0xf07d99bf, 0x9f313c24, - 0xeeaea856, 0x81e20dcd, 0x713093ba, 0x1e7c3621, 0x9195ae55, - 0xfed90bce, 0x0e0b95b9, 0x61473022, 0xec35bc5c, 0x837919c7, - 0x73ab87b0, 0x1ce7222b, 0x930eba5f, 0xfc421fc4, 0x0c9081b3, - 0x63dc2428, 0x1243b05a, 0x7d0f15c1, 0x8ddd8bb6, 0xe2912e2d, - 0x6d78b659, 0x023413c2, 0xf2e68db5, 0x9daa282e, 0xe8039548, - 0x874f30d3, 0x779daea4, 0x18d10b3f, 0x9738934b, 0xf87436d0, - 0x08a6a8a7, 0x67ea0d3c, 0x1675994e, 0x79393cd5, 0x89eba2a2, - 0xe6a70739, 0x694e9f4d, 0x06023ad6, 0xf6d0a4a1, 0x999c013a, - 0x14ee8d44, 0x7ba228df, 0x8b70b6a8, 0xe43c1333, 0x6bd58b47, - 0x04992edc, 0xf44bb0ab, 0x9b071530, 0xea988142, 0x85d424d9, - 0x7506baae, 0x1a4a1f35, 0x95a38741, 0xfaef22da, 0x0a3dbcad, - 0x65711936}}; - -#endif - -#endif - -#if N == 4 - -#if W == 8 - -local const z_crc_t FAR crc_braid_table[][256] = { - {0x00000000, 0xf1da05aa, 0x38c50d15, 0xc91f08bf, 0x718a1a2a, - 0x80501f80, 0x494f173f, 0xb8951295, 0xe3143454, 0x12ce31fe, - 0xdbd13941, 0x2a0b3ceb, 0x929e2e7e, 0x63442bd4, 0xaa5b236b, - 0x5b8126c1, 0x1d596ee9, 0xec836b43, 0x259c63fc, 0xd4466656, - 0x6cd374c3, 0x9d097169, 0x541679d6, 0xa5cc7c7c, 0xfe4d5abd, - 0x0f975f17, 0xc68857a8, 0x37525202, 0x8fc74097, 0x7e1d453d, - 0xb7024d82, 0x46d84828, 0x3ab2ddd2, 0xcb68d878, 0x0277d0c7, - 0xf3add56d, 0x4b38c7f8, 0xbae2c252, 0x73fdcaed, 0x8227cf47, - 0xd9a6e986, 0x287cec2c, 0xe163e493, 0x10b9e139, 0xa82cf3ac, - 0x59f6f606, 0x90e9feb9, 0x6133fb13, 0x27ebb33b, 0xd631b691, - 0x1f2ebe2e, 0xeef4bb84, 0x5661a911, 0xa7bbacbb, 0x6ea4a404, - 0x9f7ea1ae, 0xc4ff876f, 0x352582c5, 0xfc3a8a7a, 0x0de08fd0, - 0xb5759d45, 0x44af98ef, 0x8db09050, 0x7c6a95fa, 0x7565bba4, - 0x84bfbe0e, 0x4da0b6b1, 0xbc7ab31b, 0x04efa18e, 0xf535a424, - 0x3c2aac9b, 0xcdf0a931, 0x96718ff0, 0x67ab8a5a, 0xaeb482e5, - 0x5f6e874f, 0xe7fb95da, 0x16219070, 0xdf3e98cf, 0x2ee49d65, - 0x683cd54d, 0x99e6d0e7, 0x50f9d858, 0xa123ddf2, 0x19b6cf67, - 0xe86ccacd, 0x2173c272, 0xd0a9c7d8, 0x8b28e119, 0x7af2e4b3, - 0xb3edec0c, 0x4237e9a6, 0xfaa2fb33, 0x0b78fe99, 0xc267f626, - 0x33bdf38c, 0x4fd76676, 0xbe0d63dc, 0x77126b63, 0x86c86ec9, - 0x3e5d7c5c, 0xcf8779f6, 0x06987149, 0xf74274e3, 0xacc35222, - 0x5d195788, 0x94065f37, 0x65dc5a9d, 0xdd494808, 0x2c934da2, - 0xe58c451d, 0x145640b7, 0x528e089f, 0xa3540d35, 0x6a4b058a, - 0x9b910020, 0x230412b5, 0xd2de171f, 0x1bc11fa0, 0xea1b1a0a, - 0xb19a3ccb, 0x40403961, 0x895f31de, 0x78853474, 0xc01026e1, - 0x31ca234b, 0xf8d52bf4, 0x090f2e5e, 0xeacb7748, 0x1b1172e2, - 0xd20e7a5d, 0x23d47ff7, 0x9b416d62, 0x6a9b68c8, 0xa3846077, - 0x525e65dd, 0x09df431c, 0xf80546b6, 0x311a4e09, 0xc0c04ba3, - 0x78555936, 0x898f5c9c, 0x40905423, 0xb14a5189, 0xf79219a1, - 0x06481c0b, 0xcf5714b4, 0x3e8d111e, 0x8618038b, 0x77c20621, - 0xbedd0e9e, 0x4f070b34, 0x14862df5, 0xe55c285f, 0x2c4320e0, - 0xdd99254a, 0x650c37df, 0x94d63275, 0x5dc93aca, 0xac133f60, - 0xd079aa9a, 0x21a3af30, 0xe8bca78f, 0x1966a225, 0xa1f3b0b0, - 0x5029b51a, 0x9936bda5, 0x68ecb80f, 0x336d9ece, 0xc2b79b64, - 0x0ba893db, 0xfa729671, 0x42e784e4, 0xb33d814e, 0x7a2289f1, - 0x8bf88c5b, 0xcd20c473, 0x3cfac1d9, 0xf5e5c966, 0x043fcccc, - 0xbcaade59, 0x4d70dbf3, 0x846fd34c, 0x75b5d6e6, 0x2e34f027, - 0xdfeef58d, 0x16f1fd32, 0xe72bf898, 0x5fbeea0d, 0xae64efa7, - 0x677be718, 0x96a1e2b2, 0x9faeccec, 0x6e74c946, 0xa76bc1f9, - 0x56b1c453, 0xee24d6c6, 0x1ffed36c, 0xd6e1dbd3, 0x273bde79, - 0x7cbaf8b8, 0x8d60fd12, 0x447ff5ad, 0xb5a5f007, 0x0d30e292, - 0xfceae738, 0x35f5ef87, 0xc42fea2d, 0x82f7a205, 0x732da7af, - 0xba32af10, 0x4be8aaba, 0xf37db82f, 0x02a7bd85, 0xcbb8b53a, - 0x3a62b090, 0x61e39651, 0x903993fb, 0x59269b44, 0xa8fc9eee, - 0x10698c7b, 0xe1b389d1, 0x28ac816e, 0xd97684c4, 0xa51c113e, - 0x54c61494, 0x9dd91c2b, 0x6c031981, 0xd4960b14, 0x254c0ebe, - 0xec530601, 0x1d8903ab, 0x4608256a, 0xb7d220c0, 0x7ecd287f, - 0x8f172dd5, 0x37823f40, 0xc6583aea, 0x0f473255, 0xfe9d37ff, - 0xb8457fd7, 0x499f7a7d, 0x808072c2, 0x715a7768, 0xc9cf65fd, - 0x38156057, 0xf10a68e8, 0x00d06d42, 0x5b514b83, 0xaa8b4e29, - 0x63944696, 0x924e433c, 0x2adb51a9, 0xdb015403, 0x121e5cbc, - 0xe3c45916}, - {0x00000000, 0x0ee7e8d1, 0x1dcfd1a2, 0x13283973, 0x3b9fa344, - 0x35784b95, 0x265072e6, 0x28b79a37, 0x773f4688, 0x79d8ae59, - 0x6af0972a, 0x64177ffb, 0x4ca0e5cc, 0x42470d1d, 0x516f346e, - 0x5f88dcbf, 0xee7e8d10, 0xe09965c1, 0xf3b15cb2, 0xfd56b463, - 0xd5e12e54, 0xdb06c685, 0xc82efff6, 0xc6c91727, 0x9941cb98, - 0x97a62349, 0x848e1a3a, 0x8a69f2eb, 0xa2de68dc, 0xac39800d, - 0xbf11b97e, 0xb1f651af, 0x078c1c61, 0x096bf4b0, 0x1a43cdc3, - 0x14a42512, 0x3c13bf25, 0x32f457f4, 0x21dc6e87, 0x2f3b8656, - 0x70b35ae9, 0x7e54b238, 0x6d7c8b4b, 0x639b639a, 0x4b2cf9ad, - 0x45cb117c, 0x56e3280f, 0x5804c0de, 0xe9f29171, 0xe71579a0, - 0xf43d40d3, 0xfadaa802, 0xd26d3235, 0xdc8adae4, 0xcfa2e397, - 0xc1450b46, 0x9ecdd7f9, 0x902a3f28, 0x8302065b, 0x8de5ee8a, - 0xa55274bd, 0xabb59c6c, 0xb89da51f, 0xb67a4dce, 0x0f1838c2, - 0x01ffd013, 0x12d7e960, 0x1c3001b1, 0x34879b86, 0x3a607357, - 0x29484a24, 0x27afa2f5, 0x78277e4a, 0x76c0969b, 0x65e8afe8, - 0x6b0f4739, 0x43b8dd0e, 0x4d5f35df, 0x5e770cac, 0x5090e47d, - 0xe166b5d2, 0xef815d03, 0xfca96470, 0xf24e8ca1, 0xdaf91696, - 0xd41efe47, 0xc736c734, 0xc9d12fe5, 0x9659f35a, 0x98be1b8b, - 0x8b9622f8, 0x8571ca29, 0xadc6501e, 0xa321b8cf, 0xb00981bc, - 0xbeee696d, 0x089424a3, 0x0673cc72, 0x155bf501, 0x1bbc1dd0, - 0x330b87e7, 0x3dec6f36, 0x2ec45645, 0x2023be94, 0x7fab622b, - 0x714c8afa, 0x6264b389, 0x6c835b58, 0x4434c16f, 0x4ad329be, - 0x59fb10cd, 0x571cf81c, 0xe6eaa9b3, 0xe80d4162, 0xfb257811, - 0xf5c290c0, 0xdd750af7, 0xd392e226, 0xc0badb55, 0xce5d3384, - 0x91d5ef3b, 0x9f3207ea, 0x8c1a3e99, 0x82fdd648, 0xaa4a4c7f, - 0xa4ada4ae, 0xb7859ddd, 0xb962750c, 0x1e307184, 0x10d79955, - 0x03ffa026, 0x0d1848f7, 0x25afd2c0, 0x2b483a11, 0x38600362, - 0x3687ebb3, 0x690f370c, 0x67e8dfdd, 0x74c0e6ae, 0x7a270e7f, - 0x52909448, 0x5c777c99, 0x4f5f45ea, 0x41b8ad3b, 0xf04efc94, - 0xfea91445, 0xed812d36, 0xe366c5e7, 0xcbd15fd0, 0xc536b701, - 0xd61e8e72, 0xd8f966a3, 0x8771ba1c, 0x899652cd, 0x9abe6bbe, - 0x9459836f, 0xbcee1958, 0xb209f189, 0xa121c8fa, 0xafc6202b, - 0x19bc6de5, 0x175b8534, 0x0473bc47, 0x0a945496, 0x2223cea1, - 0x2cc42670, 0x3fec1f03, 0x310bf7d2, 0x6e832b6d, 0x6064c3bc, - 0x734cfacf, 0x7dab121e, 0x551c8829, 0x5bfb60f8, 0x48d3598b, - 0x4634b15a, 0xf7c2e0f5, 0xf9250824, 0xea0d3157, 0xe4ead986, - 0xcc5d43b1, 0xc2baab60, 0xd1929213, 0xdf757ac2, 0x80fda67d, - 0x8e1a4eac, 0x9d3277df, 0x93d59f0e, 0xbb620539, 0xb585ede8, - 0xa6add49b, 0xa84a3c4a, 0x11284946, 0x1fcfa197, 0x0ce798e4, - 0x02007035, 0x2ab7ea02, 0x245002d3, 0x37783ba0, 0x399fd371, - 0x66170fce, 0x68f0e71f, 0x7bd8de6c, 0x753f36bd, 0x5d88ac8a, - 0x536f445b, 0x40477d28, 0x4ea095f9, 0xff56c456, 0xf1b12c87, - 0xe29915f4, 0xec7efd25, 0xc4c96712, 0xca2e8fc3, 0xd906b6b0, - 0xd7e15e61, 0x886982de, 0x868e6a0f, 0x95a6537c, 0x9b41bbad, - 0xb3f6219a, 0xbd11c94b, 0xae39f038, 0xa0de18e9, 0x16a45527, - 0x1843bdf6, 0x0b6b8485, 0x058c6c54, 0x2d3bf663, 0x23dc1eb2, - 0x30f427c1, 0x3e13cf10, 0x619b13af, 0x6f7cfb7e, 0x7c54c20d, - 0x72b32adc, 0x5a04b0eb, 0x54e3583a, 0x47cb6149, 0x492c8998, - 0xf8dad837, 0xf63d30e6, 0xe5150995, 0xebf2e144, 0xc3457b73, - 0xcda293a2, 0xde8aaad1, 0xd06d4200, 0x8fe59ebf, 0x8102766e, - 0x922a4f1d, 0x9ccda7cc, 0xb47a3dfb, 0xba9dd52a, 0xa9b5ec59, - 0xa7520488}, - {0x00000000, 0x3c60e308, 0x78c1c610, 0x44a12518, 0xf1838c20, - 0xcde36f28, 0x89424a30, 0xb522a938, 0x38761e01, 0x0416fd09, - 0x40b7d811, 0x7cd73b19, 0xc9f59221, 0xf5957129, 0xb1345431, - 0x8d54b739, 0x70ec3c02, 0x4c8cdf0a, 0x082dfa12, 0x344d191a, - 0x816fb022, 0xbd0f532a, 0xf9ae7632, 0xc5ce953a, 0x489a2203, - 0x74fac10b, 0x305be413, 0x0c3b071b, 0xb919ae23, 0x85794d2b, - 0xc1d86833, 0xfdb88b3b, 0xe1d87804, 0xddb89b0c, 0x9919be14, - 0xa5795d1c, 0x105bf424, 0x2c3b172c, 0x689a3234, 0x54fad13c, - 0xd9ae6605, 0xe5ce850d, 0xa16fa015, 0x9d0f431d, 0x282dea25, - 0x144d092d, 0x50ec2c35, 0x6c8ccf3d, 0x91344406, 0xad54a70e, - 0xe9f58216, 0xd595611e, 0x60b7c826, 0x5cd72b2e, 0x18760e36, - 0x2416ed3e, 0xa9425a07, 0x9522b90f, 0xd1839c17, 0xede37f1f, - 0x58c1d627, 0x64a1352f, 0x20001037, 0x1c60f33f, 0x18c1f649, - 0x24a11541, 0x60003059, 0x5c60d351, 0xe9427a69, 0xd5229961, - 0x9183bc79, 0xade35f71, 0x20b7e848, 0x1cd70b40, 0x58762e58, - 0x6416cd50, 0xd1346468, 0xed548760, 0xa9f5a278, 0x95954170, - 0x682dca4b, 0x544d2943, 0x10ec0c5b, 0x2c8cef53, 0x99ae466b, - 0xa5cea563, 0xe16f807b, 0xdd0f6373, 0x505bd44a, 0x6c3b3742, - 0x289a125a, 0x14faf152, 0xa1d8586a, 0x9db8bb62, 0xd9199e7a, - 0xe5797d72, 0xf9198e4d, 0xc5796d45, 0x81d8485d, 0xbdb8ab55, - 0x089a026d, 0x34fae165, 0x705bc47d, 0x4c3b2775, 0xc16f904c, - 0xfd0f7344, 0xb9ae565c, 0x85ceb554, 0x30ec1c6c, 0x0c8cff64, - 0x482dda7c, 0x744d3974, 0x89f5b24f, 0xb5955147, 0xf134745f, - 0xcd549757, 0x78763e6f, 0x4416dd67, 0x00b7f87f, 0x3cd71b77, - 0xb183ac4e, 0x8de34f46, 0xc9426a5e, 0xf5228956, 0x4000206e, - 0x7c60c366, 0x38c1e67e, 0x04a10576, 0x3183ec92, 0x0de30f9a, - 0x49422a82, 0x7522c98a, 0xc00060b2, 0xfc6083ba, 0xb8c1a6a2, - 0x84a145aa, 0x09f5f293, 0x3595119b, 0x71343483, 0x4d54d78b, - 0xf8767eb3, 0xc4169dbb, 0x80b7b8a3, 0xbcd75bab, 0x416fd090, - 0x7d0f3398, 0x39ae1680, 0x05cef588, 0xb0ec5cb0, 0x8c8cbfb8, - 0xc82d9aa0, 0xf44d79a8, 0x7919ce91, 0x45792d99, 0x01d80881, - 0x3db8eb89, 0x889a42b1, 0xb4faa1b9, 0xf05b84a1, 0xcc3b67a9, - 0xd05b9496, 0xec3b779e, 0xa89a5286, 0x94fab18e, 0x21d818b6, - 0x1db8fbbe, 0x5919dea6, 0x65793dae, 0xe82d8a97, 0xd44d699f, - 0x90ec4c87, 0xac8caf8f, 0x19ae06b7, 0x25cee5bf, 0x616fc0a7, - 0x5d0f23af, 0xa0b7a894, 0x9cd74b9c, 0xd8766e84, 0xe4168d8c, - 0x513424b4, 0x6d54c7bc, 0x29f5e2a4, 0x159501ac, 0x98c1b695, - 0xa4a1559d, 0xe0007085, 0xdc60938d, 0x69423ab5, 0x5522d9bd, - 0x1183fca5, 0x2de31fad, 0x29421adb, 0x1522f9d3, 0x5183dccb, - 0x6de33fc3, 0xd8c196fb, 0xe4a175f3, 0xa00050eb, 0x9c60b3e3, - 0x113404da, 0x2d54e7d2, 0x69f5c2ca, 0x559521c2, 0xe0b788fa, - 0xdcd76bf2, 0x98764eea, 0xa416ade2, 0x59ae26d9, 0x65cec5d1, - 0x216fe0c9, 0x1d0f03c1, 0xa82daaf9, 0x944d49f1, 0xd0ec6ce9, - 0xec8c8fe1, 0x61d838d8, 0x5db8dbd0, 0x1919fec8, 0x25791dc0, - 0x905bb4f8, 0xac3b57f0, 0xe89a72e8, 0xd4fa91e0, 0xc89a62df, - 0xf4fa81d7, 0xb05ba4cf, 0x8c3b47c7, 0x3919eeff, 0x05790df7, - 0x41d828ef, 0x7db8cbe7, 0xf0ec7cde, 0xcc8c9fd6, 0x882dbace, - 0xb44d59c6, 0x016ff0fe, 0x3d0f13f6, 0x79ae36ee, 0x45ced5e6, - 0xb8765edd, 0x8416bdd5, 0xc0b798cd, 0xfcd77bc5, 0x49f5d2fd, - 0x759531f5, 0x313414ed, 0x0d54f7e5, 0x800040dc, 0xbc60a3d4, - 0xf8c186cc, 0xc4a165c4, 0x7183ccfc, 0x4de32ff4, 0x09420aec, - 0x3522e9e4}, - {0x00000000, 0x6307d924, 0xc60fb248, 0xa5086b6c, 0x576e62d1, - 0x3469bbf5, 0x9161d099, 0xf26609bd, 0xaedcc5a2, 0xcddb1c86, - 0x68d377ea, 0x0bd4aece, 0xf9b2a773, 0x9ab57e57, 0x3fbd153b, - 0x5cbacc1f, 0x86c88d05, 0xe5cf5421, 0x40c73f4d, 0x23c0e669, - 0xd1a6efd4, 0xb2a136f0, 0x17a95d9c, 0x74ae84b8, 0x281448a7, - 0x4b139183, 0xee1bfaef, 0x8d1c23cb, 0x7f7a2a76, 0x1c7df352, - 0xb975983e, 0xda72411a, 0xd6e01c4b, 0xb5e7c56f, 0x10efae03, - 0x73e87727, 0x818e7e9a, 0xe289a7be, 0x4781ccd2, 0x248615f6, - 0x783cd9e9, 0x1b3b00cd, 0xbe336ba1, 0xdd34b285, 0x2f52bb38, - 0x4c55621c, 0xe95d0970, 0x8a5ad054, 0x5028914e, 0x332f486a, - 0x96272306, 0xf520fa22, 0x0746f39f, 0x64412abb, 0xc14941d7, - 0xa24e98f3, 0xfef454ec, 0x9df38dc8, 0x38fbe6a4, 0x5bfc3f80, - 0xa99a363d, 0xca9def19, 0x6f958475, 0x0c925d51, 0x76b13ed7, - 0x15b6e7f3, 0xb0be8c9f, 0xd3b955bb, 0x21df5c06, 0x42d88522, - 0xe7d0ee4e, 0x84d7376a, 0xd86dfb75, 0xbb6a2251, 0x1e62493d, - 0x7d659019, 0x8f0399a4, 0xec044080, 0x490c2bec, 0x2a0bf2c8, - 0xf079b3d2, 0x937e6af6, 0x3676019a, 0x5571d8be, 0xa717d103, - 0xc4100827, 0x6118634b, 0x021fba6f, 0x5ea57670, 0x3da2af54, - 0x98aac438, 0xfbad1d1c, 0x09cb14a1, 0x6acccd85, 0xcfc4a6e9, - 0xacc37fcd, 0xa051229c, 0xc356fbb8, 0x665e90d4, 0x055949f0, - 0xf73f404d, 0x94389969, 0x3130f205, 0x52372b21, 0x0e8de73e, - 0x6d8a3e1a, 0xc8825576, 0xab858c52, 0x59e385ef, 0x3ae45ccb, - 0x9fec37a7, 0xfcebee83, 0x2699af99, 0x459e76bd, 0xe0961dd1, - 0x8391c4f5, 0x71f7cd48, 0x12f0146c, 0xb7f87f00, 0xd4ffa624, - 0x88456a3b, 0xeb42b31f, 0x4e4ad873, 0x2d4d0157, 0xdf2b08ea, - 0xbc2cd1ce, 0x1924baa2, 0x7a236386, 0xed627dae, 0x8e65a48a, - 0x2b6dcfe6, 0x486a16c2, 0xba0c1f7f, 0xd90bc65b, 0x7c03ad37, - 0x1f047413, 0x43beb80c, 0x20b96128, 0x85b10a44, 0xe6b6d360, - 0x14d0dadd, 0x77d703f9, 0xd2df6895, 0xb1d8b1b1, 0x6baaf0ab, - 0x08ad298f, 0xada542e3, 0xcea29bc7, 0x3cc4927a, 0x5fc34b5e, - 0xfacb2032, 0x99ccf916, 0xc5763509, 0xa671ec2d, 0x03798741, - 0x607e5e65, 0x921857d8, 0xf11f8efc, 0x5417e590, 0x37103cb4, - 0x3b8261e5, 0x5885b8c1, 0xfd8dd3ad, 0x9e8a0a89, 0x6cec0334, - 0x0febda10, 0xaae3b17c, 0xc9e46858, 0x955ea447, 0xf6597d63, - 0x5351160f, 0x3056cf2b, 0xc230c696, 0xa1371fb2, 0x043f74de, - 0x6738adfa, 0xbd4aece0, 0xde4d35c4, 0x7b455ea8, 0x1842878c, - 0xea248e31, 0x89235715, 0x2c2b3c79, 0x4f2ce55d, 0x13962942, - 0x7091f066, 0xd5999b0a, 0xb69e422e, 0x44f84b93, 0x27ff92b7, - 0x82f7f9db, 0xe1f020ff, 0x9bd34379, 0xf8d49a5d, 0x5ddcf131, - 0x3edb2815, 0xccbd21a8, 0xafbaf88c, 0x0ab293e0, 0x69b54ac4, - 0x350f86db, 0x56085fff, 0xf3003493, 0x9007edb7, 0x6261e40a, - 0x01663d2e, 0xa46e5642, 0xc7698f66, 0x1d1bce7c, 0x7e1c1758, - 0xdb147c34, 0xb813a510, 0x4a75acad, 0x29727589, 0x8c7a1ee5, - 0xef7dc7c1, 0xb3c70bde, 0xd0c0d2fa, 0x75c8b996, 0x16cf60b2, - 0xe4a9690f, 0x87aeb02b, 0x22a6db47, 0x41a10263, 0x4d335f32, - 0x2e348616, 0x8b3ced7a, 0xe83b345e, 0x1a5d3de3, 0x795ae4c7, - 0xdc528fab, 0xbf55568f, 0xe3ef9a90, 0x80e843b4, 0x25e028d8, - 0x46e7f1fc, 0xb481f841, 0xd7862165, 0x728e4a09, 0x1189932d, - 0xcbfbd237, 0xa8fc0b13, 0x0df4607f, 0x6ef3b95b, 0x9c95b0e6, - 0xff9269c2, 0x5a9a02ae, 0x399ddb8a, 0x65271795, 0x0620ceb1, - 0xa328a5dd, 0xc02f7cf9, 0x32497544, 0x514eac60, 0xf446c70c, - 0x97411e28}, - {0x00000000, 0x01b5fd1d, 0x036bfa3a, 0x02de0727, 0x06d7f474, - 0x07620969, 0x05bc0e4e, 0x0409f353, 0x0dafe8e8, 0x0c1a15f5, - 0x0ec412d2, 0x0f71efcf, 0x0b781c9c, 0x0acde181, 0x0813e6a6, - 0x09a61bbb, 0x1b5fd1d0, 0x1aea2ccd, 0x18342bea, 0x1981d6f7, - 0x1d8825a4, 0x1c3dd8b9, 0x1ee3df9e, 0x1f562283, 0x16f03938, - 0x1745c425, 0x159bc302, 0x142e3e1f, 0x1027cd4c, 0x11923051, - 0x134c3776, 0x12f9ca6b, 0x36bfa3a0, 0x370a5ebd, 0x35d4599a, - 0x3461a487, 0x306857d4, 0x31ddaac9, 0x3303adee, 0x32b650f3, - 0x3b104b48, 0x3aa5b655, 0x387bb172, 0x39ce4c6f, 0x3dc7bf3c, - 0x3c724221, 0x3eac4506, 0x3f19b81b, 0x2de07270, 0x2c558f6d, - 0x2e8b884a, 0x2f3e7557, 0x2b378604, 0x2a827b19, 0x285c7c3e, - 0x29e98123, 0x204f9a98, 0x21fa6785, 0x232460a2, 0x22919dbf, - 0x26986eec, 0x272d93f1, 0x25f394d6, 0x244669cb, 0x6d7f4740, - 0x6ccaba5d, 0x6e14bd7a, 0x6fa14067, 0x6ba8b334, 0x6a1d4e29, - 0x68c3490e, 0x6976b413, 0x60d0afa8, 0x616552b5, 0x63bb5592, - 0x620ea88f, 0x66075bdc, 0x67b2a6c1, 0x656ca1e6, 0x64d95cfb, - 0x76209690, 0x77956b8d, 0x754b6caa, 0x74fe91b7, 0x70f762e4, - 0x71429ff9, 0x739c98de, 0x722965c3, 0x7b8f7e78, 0x7a3a8365, - 0x78e48442, 0x7951795f, 0x7d588a0c, 0x7ced7711, 0x7e337036, - 0x7f868d2b, 0x5bc0e4e0, 0x5a7519fd, 0x58ab1eda, 0x591ee3c7, - 0x5d171094, 0x5ca2ed89, 0x5e7ceaae, 0x5fc917b3, 0x566f0c08, - 0x57daf115, 0x5504f632, 0x54b10b2f, 0x50b8f87c, 0x510d0561, - 0x53d30246, 0x5266ff5b, 0x409f3530, 0x412ac82d, 0x43f4cf0a, - 0x42413217, 0x4648c144, 0x47fd3c59, 0x45233b7e, 0x4496c663, - 0x4d30ddd8, 0x4c8520c5, 0x4e5b27e2, 0x4feedaff, 0x4be729ac, - 0x4a52d4b1, 0x488cd396, 0x49392e8b, 0xdafe8e80, 0xdb4b739d, - 0xd99574ba, 0xd82089a7, 0xdc297af4, 0xdd9c87e9, 0xdf4280ce, - 0xdef77dd3, 0xd7516668, 0xd6e49b75, 0xd43a9c52, 0xd58f614f, - 0xd186921c, 0xd0336f01, 0xd2ed6826, 0xd358953b, 0xc1a15f50, - 0xc014a24d, 0xc2caa56a, 0xc37f5877, 0xc776ab24, 0xc6c35639, - 0xc41d511e, 0xc5a8ac03, 0xcc0eb7b8, 0xcdbb4aa5, 0xcf654d82, - 0xced0b09f, 0xcad943cc, 0xcb6cbed1, 0xc9b2b9f6, 0xc80744eb, - 0xec412d20, 0xedf4d03d, 0xef2ad71a, 0xee9f2a07, 0xea96d954, - 0xeb232449, 0xe9fd236e, 0xe848de73, 0xe1eec5c8, 0xe05b38d5, - 0xe2853ff2, 0xe330c2ef, 0xe73931bc, 0xe68ccca1, 0xe452cb86, - 0xe5e7369b, 0xf71efcf0, 0xf6ab01ed, 0xf47506ca, 0xf5c0fbd7, - 0xf1c90884, 0xf07cf599, 0xf2a2f2be, 0xf3170fa3, 0xfab11418, - 0xfb04e905, 0xf9daee22, 0xf86f133f, 0xfc66e06c, 0xfdd31d71, - 0xff0d1a56, 0xfeb8e74b, 0xb781c9c0, 0xb63434dd, 0xb4ea33fa, - 0xb55fcee7, 0xb1563db4, 0xb0e3c0a9, 0xb23dc78e, 0xb3883a93, - 0xba2e2128, 0xbb9bdc35, 0xb945db12, 0xb8f0260f, 0xbcf9d55c, - 0xbd4c2841, 0xbf922f66, 0xbe27d27b, 0xacde1810, 0xad6be50d, - 0xafb5e22a, 0xae001f37, 0xaa09ec64, 0xabbc1179, 0xa962165e, - 0xa8d7eb43, 0xa171f0f8, 0xa0c40de5, 0xa21a0ac2, 0xa3aff7df, - 0xa7a6048c, 0xa613f991, 0xa4cdfeb6, 0xa57803ab, 0x813e6a60, - 0x808b977d, 0x8255905a, 0x83e06d47, 0x87e99e14, 0x865c6309, - 0x8482642e, 0x85379933, 0x8c918288, 0x8d247f95, 0x8ffa78b2, - 0x8e4f85af, 0x8a4676fc, 0x8bf38be1, 0x892d8cc6, 0x889871db, - 0x9a61bbb0, 0x9bd446ad, 0x990a418a, 0x98bfbc97, 0x9cb64fc4, - 0x9d03b2d9, 0x9fddb5fe, 0x9e6848e3, 0x97ce5358, 0x967bae45, - 0x94a5a962, 0x9510547f, 0x9119a72c, 0x90ac5a31, 0x92725d16, - 0x93c7a00b}, - {0x00000000, 0x6e8c1b41, 0xdd183682, 0xb3942dc3, 0x61416b45, - 0x0fcd7004, 0xbc595dc7, 0xd2d54686, 0xc282d68a, 0xac0ecdcb, - 0x1f9ae008, 0x7116fb49, 0xa3c3bdcf, 0xcd4fa68e, 0x7edb8b4d, - 0x1057900c, 0x5e74ab55, 0x30f8b014, 0x836c9dd7, 0xede08696, - 0x3f35c010, 0x51b9db51, 0xe22df692, 0x8ca1edd3, 0x9cf67ddf, - 0xf27a669e, 0x41ee4b5d, 0x2f62501c, 0xfdb7169a, 0x933b0ddb, - 0x20af2018, 0x4e233b59, 0xbce956aa, 0xd2654deb, 0x61f16028, - 0x0f7d7b69, 0xdda83def, 0xb32426ae, 0x00b00b6d, 0x6e3c102c, - 0x7e6b8020, 0x10e79b61, 0xa373b6a2, 0xcdffade3, 0x1f2aeb65, - 0x71a6f024, 0xc232dde7, 0xacbec6a6, 0xe29dfdff, 0x8c11e6be, - 0x3f85cb7d, 0x5109d03c, 0x83dc96ba, 0xed508dfb, 0x5ec4a038, - 0x3048bb79, 0x201f2b75, 0x4e933034, 0xfd071df7, 0x938b06b6, - 0x415e4030, 0x2fd25b71, 0x9c4676b2, 0xf2ca6df3, 0xa2a3ab15, - 0xcc2fb054, 0x7fbb9d97, 0x113786d6, 0xc3e2c050, 0xad6edb11, - 0x1efaf6d2, 0x7076ed93, 0x60217d9f, 0x0ead66de, 0xbd394b1d, - 0xd3b5505c, 0x016016da, 0x6fec0d9b, 0xdc782058, 0xb2f43b19, - 0xfcd70040, 0x925b1b01, 0x21cf36c2, 0x4f432d83, 0x9d966b05, - 0xf31a7044, 0x408e5d87, 0x2e0246c6, 0x3e55d6ca, 0x50d9cd8b, - 0xe34de048, 0x8dc1fb09, 0x5f14bd8f, 0x3198a6ce, 0x820c8b0d, - 0xec80904c, 0x1e4afdbf, 0x70c6e6fe, 0xc352cb3d, 0xadded07c, - 0x7f0b96fa, 0x11878dbb, 0xa213a078, 0xcc9fbb39, 0xdcc82b35, - 0xb2443074, 0x01d01db7, 0x6f5c06f6, 0xbd894070, 0xd3055b31, - 0x609176f2, 0x0e1d6db3, 0x403e56ea, 0x2eb24dab, 0x9d266068, - 0xf3aa7b29, 0x217f3daf, 0x4ff326ee, 0xfc670b2d, 0x92eb106c, - 0x82bc8060, 0xec309b21, 0x5fa4b6e2, 0x3128ada3, 0xe3fdeb25, - 0x8d71f064, 0x3ee5dda7, 0x5069c6e6, 0x9e36506b, 0xf0ba4b2a, - 0x432e66e9, 0x2da27da8, 0xff773b2e, 0x91fb206f, 0x226f0dac, - 0x4ce316ed, 0x5cb486e1, 0x32389da0, 0x81acb063, 0xef20ab22, - 0x3df5eda4, 0x5379f6e5, 0xe0eddb26, 0x8e61c067, 0xc042fb3e, - 0xaecee07f, 0x1d5acdbc, 0x73d6d6fd, 0xa103907b, 0xcf8f8b3a, - 0x7c1ba6f9, 0x1297bdb8, 0x02c02db4, 0x6c4c36f5, 0xdfd81b36, - 0xb1540077, 0x638146f1, 0x0d0d5db0, 0xbe997073, 0xd0156b32, - 0x22df06c1, 0x4c531d80, 0xffc73043, 0x914b2b02, 0x439e6d84, - 0x2d1276c5, 0x9e865b06, 0xf00a4047, 0xe05dd04b, 0x8ed1cb0a, - 0x3d45e6c9, 0x53c9fd88, 0x811cbb0e, 0xef90a04f, 0x5c048d8c, - 0x328896cd, 0x7cabad94, 0x1227b6d5, 0xa1b39b16, 0xcf3f8057, - 0x1deac6d1, 0x7366dd90, 0xc0f2f053, 0xae7eeb12, 0xbe297b1e, - 0xd0a5605f, 0x63314d9c, 0x0dbd56dd, 0xdf68105b, 0xb1e40b1a, - 0x027026d9, 0x6cfc3d98, 0x3c95fb7e, 0x5219e03f, 0xe18dcdfc, - 0x8f01d6bd, 0x5dd4903b, 0x33588b7a, 0x80cca6b9, 0xee40bdf8, - 0xfe172df4, 0x909b36b5, 0x230f1b76, 0x4d830037, 0x9f5646b1, - 0xf1da5df0, 0x424e7033, 0x2cc26b72, 0x62e1502b, 0x0c6d4b6a, - 0xbff966a9, 0xd1757de8, 0x03a03b6e, 0x6d2c202f, 0xdeb80dec, - 0xb03416ad, 0xa06386a1, 0xceef9de0, 0x7d7bb023, 0x13f7ab62, - 0xc122ede4, 0xafaef6a5, 0x1c3adb66, 0x72b6c027, 0x807cadd4, - 0xeef0b695, 0x5d649b56, 0x33e88017, 0xe13dc691, 0x8fb1ddd0, - 0x3c25f013, 0x52a9eb52, 0x42fe7b5e, 0x2c72601f, 0x9fe64ddc, - 0xf16a569d, 0x23bf101b, 0x4d330b5a, 0xfea72699, 0x902b3dd8, - 0xde080681, 0xb0841dc0, 0x03103003, 0x6d9c2b42, 0xbf496dc4, - 0xd1c57685, 0x62515b46, 0x0cdd4007, 0x1c8ad00b, 0x7206cb4a, - 0xc192e689, 0xaf1efdc8, 0x7dcbbb4e, 0x1347a00f, 0xa0d38dcc, - 0xce5f968d}, - {0x00000000, 0xe71da697, 0x154a4b6f, 0xf257edf8, 0x2a9496de, - 0xcd893049, 0x3fdeddb1, 0xd8c37b26, 0x55292dbc, 0xb2348b2b, - 0x406366d3, 0xa77ec044, 0x7fbdbb62, 0x98a01df5, 0x6af7f00d, - 0x8dea569a, 0xaa525b78, 0x4d4ffdef, 0xbf181017, 0x5805b680, - 0x80c6cda6, 0x67db6b31, 0x958c86c9, 0x7291205e, 0xff7b76c4, - 0x1866d053, 0xea313dab, 0x0d2c9b3c, 0xd5efe01a, 0x32f2468d, - 0xc0a5ab75, 0x27b80de2, 0x8fd5b0b1, 0x68c81626, 0x9a9ffbde, - 0x7d825d49, 0xa541266f, 0x425c80f8, 0xb00b6d00, 0x5716cb97, - 0xdafc9d0d, 0x3de13b9a, 0xcfb6d662, 0x28ab70f5, 0xf0680bd3, - 0x1775ad44, 0xe52240bc, 0x023fe62b, 0x2587ebc9, 0xc29a4d5e, - 0x30cda0a6, 0xd7d00631, 0x0f137d17, 0xe80edb80, 0x1a593678, - 0xfd4490ef, 0x70aec675, 0x97b360e2, 0x65e48d1a, 0x82f92b8d, - 0x5a3a50ab, 0xbd27f63c, 0x4f701bc4, 0xa86dbd53, 0xc4da6723, - 0x23c7c1b4, 0xd1902c4c, 0x368d8adb, 0xee4ef1fd, 0x0953576a, - 0xfb04ba92, 0x1c191c05, 0x91f34a9f, 0x76eeec08, 0x84b901f0, - 0x63a4a767, 0xbb67dc41, 0x5c7a7ad6, 0xae2d972e, 0x493031b9, - 0x6e883c5b, 0x89959acc, 0x7bc27734, 0x9cdfd1a3, 0x441caa85, - 0xa3010c12, 0x5156e1ea, 0xb64b477d, 0x3ba111e7, 0xdcbcb770, - 0x2eeb5a88, 0xc9f6fc1f, 0x11358739, 0xf62821ae, 0x047fcc56, - 0xe3626ac1, 0x4b0fd792, 0xac127105, 0x5e459cfd, 0xb9583a6a, - 0x619b414c, 0x8686e7db, 0x74d10a23, 0x93ccacb4, 0x1e26fa2e, - 0xf93b5cb9, 0x0b6cb141, 0xec7117d6, 0x34b26cf0, 0xd3afca67, - 0x21f8279f, 0xc6e58108, 0xe15d8cea, 0x06402a7d, 0xf417c785, - 0x130a6112, 0xcbc91a34, 0x2cd4bca3, 0xde83515b, 0x399ef7cc, - 0xb474a156, 0x536907c1, 0xa13eea39, 0x46234cae, 0x9ee03788, - 0x79fd911f, 0x8baa7ce7, 0x6cb7da70, 0x52c5c807, 0xb5d86e90, - 0x478f8368, 0xa09225ff, 0x78515ed9, 0x9f4cf84e, 0x6d1b15b6, - 0x8a06b321, 0x07ece5bb, 0xe0f1432c, 0x12a6aed4, 0xf5bb0843, - 0x2d787365, 0xca65d5f2, 0x3832380a, 0xdf2f9e9d, 0xf897937f, - 0x1f8a35e8, 0xedddd810, 0x0ac07e87, 0xd20305a1, 0x351ea336, - 0xc7494ece, 0x2054e859, 0xadbebec3, 0x4aa31854, 0xb8f4f5ac, - 0x5fe9533b, 0x872a281d, 0x60378e8a, 0x92606372, 0x757dc5e5, - 0xdd1078b6, 0x3a0dde21, 0xc85a33d9, 0x2f47954e, 0xf784ee68, - 0x109948ff, 0xe2cea507, 0x05d30390, 0x8839550a, 0x6f24f39d, - 0x9d731e65, 0x7a6eb8f2, 0xa2adc3d4, 0x45b06543, 0xb7e788bb, - 0x50fa2e2c, 0x774223ce, 0x905f8559, 0x620868a1, 0x8515ce36, - 0x5dd6b510, 0xbacb1387, 0x489cfe7f, 0xaf8158e8, 0x226b0e72, - 0xc576a8e5, 0x3721451d, 0xd03ce38a, 0x08ff98ac, 0xefe23e3b, - 0x1db5d3c3, 0xfaa87554, 0x961faf24, 0x710209b3, 0x8355e44b, - 0x644842dc, 0xbc8b39fa, 0x5b969f6d, 0xa9c17295, 0x4edcd402, - 0xc3368298, 0x242b240f, 0xd67cc9f7, 0x31616f60, 0xe9a21446, - 0x0ebfb2d1, 0xfce85f29, 0x1bf5f9be, 0x3c4df45c, 0xdb5052cb, - 0x2907bf33, 0xce1a19a4, 0x16d96282, 0xf1c4c415, 0x039329ed, - 0xe48e8f7a, 0x6964d9e0, 0x8e797f77, 0x7c2e928f, 0x9b333418, - 0x43f04f3e, 0xa4ede9a9, 0x56ba0451, 0xb1a7a2c6, 0x19ca1f95, - 0xfed7b902, 0x0c8054fa, 0xeb9df26d, 0x335e894b, 0xd4432fdc, - 0x2614c224, 0xc10964b3, 0x4ce33229, 0xabfe94be, 0x59a97946, - 0xbeb4dfd1, 0x6677a4f7, 0x816a0260, 0x733def98, 0x9420490f, - 0xb39844ed, 0x5485e27a, 0xa6d20f82, 0x41cfa915, 0x990cd233, - 0x7e1174a4, 0x8c46995c, 0x6b5b3fcb, 0xe6b16951, 0x01accfc6, - 0xf3fb223e, 0x14e684a9, 0xcc25ff8f, 0x2b385918, 0xd96fb4e0, - 0x3e721277}, - {0x00000000, 0xa58b900e, 0x9066265d, 0x35edb653, 0xfbbd4afb, - 0x5e36daf5, 0x6bdb6ca6, 0xce50fca8, 0x2c0b93b7, 0x898003b9, - 0xbc6db5ea, 0x19e625e4, 0xd7b6d94c, 0x723d4942, 0x47d0ff11, - 0xe25b6f1f, 0x5817276e, 0xfd9cb760, 0xc8710133, 0x6dfa913d, - 0xa3aa6d95, 0x0621fd9b, 0x33cc4bc8, 0x9647dbc6, 0x741cb4d9, - 0xd19724d7, 0xe47a9284, 0x41f1028a, 0x8fa1fe22, 0x2a2a6e2c, - 0x1fc7d87f, 0xba4c4871, 0xb02e4edc, 0x15a5ded2, 0x20486881, - 0x85c3f88f, 0x4b930427, 0xee189429, 0xdbf5227a, 0x7e7eb274, - 0x9c25dd6b, 0x39ae4d65, 0x0c43fb36, 0xa9c86b38, 0x67989790, - 0xc213079e, 0xf7feb1cd, 0x527521c3, 0xe83969b2, 0x4db2f9bc, - 0x785f4fef, 0xddd4dfe1, 0x13842349, 0xb60fb347, 0x83e20514, - 0x2669951a, 0xc432fa05, 0x61b96a0b, 0x5454dc58, 0xf1df4c56, - 0x3f8fb0fe, 0x9a0420f0, 0xafe996a3, 0x0a6206ad, 0xbb2d9bf9, - 0x1ea60bf7, 0x2b4bbda4, 0x8ec02daa, 0x4090d102, 0xe51b410c, - 0xd0f6f75f, 0x757d6751, 0x9726084e, 0x32ad9840, 0x07402e13, - 0xa2cbbe1d, 0x6c9b42b5, 0xc910d2bb, 0xfcfd64e8, 0x5976f4e6, - 0xe33abc97, 0x46b12c99, 0x735c9aca, 0xd6d70ac4, 0x1887f66c, - 0xbd0c6662, 0x88e1d031, 0x2d6a403f, 0xcf312f20, 0x6ababf2e, - 0x5f57097d, 0xfadc9973, 0x348c65db, 0x9107f5d5, 0xa4ea4386, - 0x0161d388, 0x0b03d525, 0xae88452b, 0x9b65f378, 0x3eee6376, - 0xf0be9fde, 0x55350fd0, 0x60d8b983, 0xc553298d, 0x27084692, - 0x8283d69c, 0xb76e60cf, 0x12e5f0c1, 0xdcb50c69, 0x793e9c67, - 0x4cd32a34, 0xe958ba3a, 0x5314f24b, 0xf69f6245, 0xc372d416, - 0x66f94418, 0xa8a9b8b0, 0x0d2228be, 0x38cf9eed, 0x9d440ee3, - 0x7f1f61fc, 0xda94f1f2, 0xef7947a1, 0x4af2d7af, 0x84a22b07, - 0x2129bb09, 0x14c40d5a, 0xb14f9d54, 0xad2a31b3, 0x08a1a1bd, - 0x3d4c17ee, 0x98c787e0, 0x56977b48, 0xf31ceb46, 0xc6f15d15, - 0x637acd1b, 0x8121a204, 0x24aa320a, 0x11478459, 0xb4cc1457, - 0x7a9ce8ff, 0xdf1778f1, 0xeafacea2, 0x4f715eac, 0xf53d16dd, - 0x50b686d3, 0x655b3080, 0xc0d0a08e, 0x0e805c26, 0xab0bcc28, - 0x9ee67a7b, 0x3b6dea75, 0xd936856a, 0x7cbd1564, 0x4950a337, - 0xecdb3339, 0x228bcf91, 0x87005f9f, 0xb2ede9cc, 0x176679c2, - 0x1d047f6f, 0xb88fef61, 0x8d625932, 0x28e9c93c, 0xe6b93594, - 0x4332a59a, 0x76df13c9, 0xd35483c7, 0x310fecd8, 0x94847cd6, - 0xa169ca85, 0x04e25a8b, 0xcab2a623, 0x6f39362d, 0x5ad4807e, - 0xff5f1070, 0x45135801, 0xe098c80f, 0xd5757e5c, 0x70feee52, - 0xbeae12fa, 0x1b2582f4, 0x2ec834a7, 0x8b43a4a9, 0x6918cbb6, - 0xcc935bb8, 0xf97eedeb, 0x5cf57de5, 0x92a5814d, 0x372e1143, - 0x02c3a710, 0xa748371e, 0x1607aa4a, 0xb38c3a44, 0x86618c17, - 0x23ea1c19, 0xedbae0b1, 0x483170bf, 0x7ddcc6ec, 0xd85756e2, - 0x3a0c39fd, 0x9f87a9f3, 0xaa6a1fa0, 0x0fe18fae, 0xc1b17306, - 0x643ae308, 0x51d7555b, 0xf45cc555, 0x4e108d24, 0xeb9b1d2a, - 0xde76ab79, 0x7bfd3b77, 0xb5adc7df, 0x102657d1, 0x25cbe182, - 0x8040718c, 0x621b1e93, 0xc7908e9d, 0xf27d38ce, 0x57f6a8c0, - 0x99a65468, 0x3c2dc466, 0x09c07235, 0xac4be23b, 0xa629e496, - 0x03a27498, 0x364fc2cb, 0x93c452c5, 0x5d94ae6d, 0xf81f3e63, - 0xcdf28830, 0x6879183e, 0x8a227721, 0x2fa9e72f, 0x1a44517c, - 0xbfcfc172, 0x719f3dda, 0xd414add4, 0xe1f91b87, 0x44728b89, - 0xfe3ec3f8, 0x5bb553f6, 0x6e58e5a5, 0xcbd375ab, 0x05838903, - 0xa008190d, 0x95e5af5e, 0x306e3f50, 0xd235504f, 0x77bec041, - 0x42537612, 0xe7d8e61c, 0x29881ab4, 0x8c038aba, 0xb9ee3ce9, - 0x1c65ace7}}; - -local const z_word_t FAR crc_braid_big_table[][256] = { - {0x0000000000000000, 0x0e908ba500000000, 0x5d26669000000000, - 0x53b6ed3500000000, 0xfb4abdfb00000000, 0xf5da365e00000000, - 0xa66cdb6b00000000, 0xa8fc50ce00000000, 0xb7930b2c00000000, - 0xb903808900000000, 0xeab56dbc00000000, 0xe425e61900000000, - 0x4cd9b6d700000000, 0x42493d7200000000, 0x11ffd04700000000, - 0x1f6f5be200000000, 0x6e27175800000000, 0x60b79cfd00000000, - 0x330171c800000000, 0x3d91fa6d00000000, 0x956daaa300000000, - 0x9bfd210600000000, 0xc84bcc3300000000, 0xc6db479600000000, - 0xd9b41c7400000000, 0xd72497d100000000, 0x84927ae400000000, - 0x8a02f14100000000, 0x22fea18f00000000, 0x2c6e2a2a00000000, - 0x7fd8c71f00000000, 0x71484cba00000000, 0xdc4e2eb000000000, - 0xd2dea51500000000, 0x8168482000000000, 0x8ff8c38500000000, - 0x2704934b00000000, 0x299418ee00000000, 0x7a22f5db00000000, - 0x74b27e7e00000000, 0x6bdd259c00000000, 0x654dae3900000000, - 0x36fb430c00000000, 0x386bc8a900000000, 0x9097986700000000, - 0x9e0713c200000000, 0xcdb1fef700000000, 0xc321755200000000, - 0xb26939e800000000, 0xbcf9b24d00000000, 0xef4f5f7800000000, - 0xe1dfd4dd00000000, 0x4923841300000000, 0x47b30fb600000000, - 0x1405e28300000000, 0x1a95692600000000, 0x05fa32c400000000, - 0x0b6ab96100000000, 0x58dc545400000000, 0x564cdff100000000, - 0xfeb08f3f00000000, 0xf020049a00000000, 0xa396e9af00000000, - 0xad06620a00000000, 0xf99b2dbb00000000, 0xf70ba61e00000000, - 0xa4bd4b2b00000000, 0xaa2dc08e00000000, 0x02d1904000000000, - 0x0c411be500000000, 0x5ff7f6d000000000, 0x51677d7500000000, - 0x4e08269700000000, 0x4098ad3200000000, 0x132e400700000000, - 0x1dbecba200000000, 0xb5429b6c00000000, 0xbbd210c900000000, - 0xe864fdfc00000000, 0xe6f4765900000000, 0x97bc3ae300000000, - 0x992cb14600000000, 0xca9a5c7300000000, 0xc40ad7d600000000, - 0x6cf6871800000000, 0x62660cbd00000000, 0x31d0e18800000000, - 0x3f406a2d00000000, 0x202f31cf00000000, 0x2ebfba6a00000000, - 0x7d09575f00000000, 0x7399dcfa00000000, 0xdb658c3400000000, - 0xd5f5079100000000, 0x8643eaa400000000, 0x88d3610100000000, - 0x25d5030b00000000, 0x2b4588ae00000000, 0x78f3659b00000000, - 0x7663ee3e00000000, 0xde9fbef000000000, 0xd00f355500000000, - 0x83b9d86000000000, 0x8d2953c500000000, 0x9246082700000000, - 0x9cd6838200000000, 0xcf606eb700000000, 0xc1f0e51200000000, - 0x690cb5dc00000000, 0x679c3e7900000000, 0x342ad34c00000000, - 0x3aba58e900000000, 0x4bf2145300000000, 0x45629ff600000000, - 0x16d472c300000000, 0x1844f96600000000, 0xb0b8a9a800000000, - 0xbe28220d00000000, 0xed9ecf3800000000, 0xe30e449d00000000, - 0xfc611f7f00000000, 0xf2f194da00000000, 0xa14779ef00000000, - 0xafd7f24a00000000, 0x072ba28400000000, 0x09bb292100000000, - 0x5a0dc41400000000, 0x549d4fb100000000, 0xb3312aad00000000, - 0xbda1a10800000000, 0xee174c3d00000000, 0xe087c79800000000, - 0x487b975600000000, 0x46eb1cf300000000, 0x155df1c600000000, - 0x1bcd7a6300000000, 0x04a2218100000000, 0x0a32aa2400000000, - 0x5984471100000000, 0x5714ccb400000000, 0xffe89c7a00000000, - 0xf17817df00000000, 0xa2cefaea00000000, 0xac5e714f00000000, - 0xdd163df500000000, 0xd386b65000000000, 0x80305b6500000000, - 0x8ea0d0c000000000, 0x265c800e00000000, 0x28cc0bab00000000, - 0x7b7ae69e00000000, 0x75ea6d3b00000000, 0x6a8536d900000000, - 0x6415bd7c00000000, 0x37a3504900000000, 0x3933dbec00000000, - 0x91cf8b2200000000, 0x9f5f008700000000, 0xcce9edb200000000, - 0xc279661700000000, 0x6f7f041d00000000, 0x61ef8fb800000000, - 0x3259628d00000000, 0x3cc9e92800000000, 0x9435b9e600000000, - 0x9aa5324300000000, 0xc913df7600000000, 0xc78354d300000000, - 0xd8ec0f3100000000, 0xd67c849400000000, 0x85ca69a100000000, - 0x8b5ae20400000000, 0x23a6b2ca00000000, 0x2d36396f00000000, - 0x7e80d45a00000000, 0x70105fff00000000, 0x0158134500000000, - 0x0fc898e000000000, 0x5c7e75d500000000, 0x52eefe7000000000, - 0xfa12aebe00000000, 0xf482251b00000000, 0xa734c82e00000000, - 0xa9a4438b00000000, 0xb6cb186900000000, 0xb85b93cc00000000, - 0xebed7ef900000000, 0xe57df55c00000000, 0x4d81a59200000000, - 0x43112e3700000000, 0x10a7c30200000000, 0x1e3748a700000000, - 0x4aaa071600000000, 0x443a8cb300000000, 0x178c618600000000, - 0x191cea2300000000, 0xb1e0baed00000000, 0xbf70314800000000, - 0xecc6dc7d00000000, 0xe25657d800000000, 0xfd390c3a00000000, - 0xf3a9879f00000000, 0xa01f6aaa00000000, 0xae8fe10f00000000, - 0x0673b1c100000000, 0x08e33a6400000000, 0x5b55d75100000000, - 0x55c55cf400000000, 0x248d104e00000000, 0x2a1d9beb00000000, - 0x79ab76de00000000, 0x773bfd7b00000000, 0xdfc7adb500000000, - 0xd157261000000000, 0x82e1cb2500000000, 0x8c71408000000000, - 0x931e1b6200000000, 0x9d8e90c700000000, 0xce387df200000000, - 0xc0a8f65700000000, 0x6854a69900000000, 0x66c42d3c00000000, - 0x3572c00900000000, 0x3be24bac00000000, 0x96e429a600000000, - 0x9874a20300000000, 0xcbc24f3600000000, 0xc552c49300000000, - 0x6dae945d00000000, 0x633e1ff800000000, 0x3088f2cd00000000, - 0x3e18796800000000, 0x2177228a00000000, 0x2fe7a92f00000000, - 0x7c51441a00000000, 0x72c1cfbf00000000, 0xda3d9f7100000000, - 0xd4ad14d400000000, 0x871bf9e100000000, 0x898b724400000000, - 0xf8c33efe00000000, 0xf653b55b00000000, 0xa5e5586e00000000, - 0xab75d3cb00000000, 0x0389830500000000, 0x0d1908a000000000, - 0x5eafe59500000000, 0x503f6e3000000000, 0x4f5035d200000000, - 0x41c0be7700000000, 0x1276534200000000, 0x1ce6d8e700000000, - 0xb41a882900000000, 0xba8a038c00000000, 0xe93ceeb900000000, - 0xe7ac651c00000000}, - {0x0000000000000000, 0x97a61de700000000, 0x6f4b4a1500000000, - 0xf8ed57f200000000, 0xde96942a00000000, 0x493089cd00000000, - 0xb1ddde3f00000000, 0x267bc3d800000000, 0xbc2d295500000000, - 0x2b8b34b200000000, 0xd366634000000000, 0x44c07ea700000000, - 0x62bbbd7f00000000, 0xf51da09800000000, 0x0df0f76a00000000, - 0x9a56ea8d00000000, 0x785b52aa00000000, 0xeffd4f4d00000000, - 0x171018bf00000000, 0x80b6055800000000, 0xa6cdc68000000000, - 0x316bdb6700000000, 0xc9868c9500000000, 0x5e20917200000000, - 0xc4767bff00000000, 0x53d0661800000000, 0xab3d31ea00000000, - 0x3c9b2c0d00000000, 0x1ae0efd500000000, 0x8d46f23200000000, - 0x75aba5c000000000, 0xe20db82700000000, 0xb1b0d58f00000000, - 0x2616c86800000000, 0xdefb9f9a00000000, 0x495d827d00000000, - 0x6f2641a500000000, 0xf8805c4200000000, 0x006d0bb000000000, - 0x97cb165700000000, 0x0d9dfcda00000000, 0x9a3be13d00000000, - 0x62d6b6cf00000000, 0xf570ab2800000000, 0xd30b68f000000000, - 0x44ad751700000000, 0xbc4022e500000000, 0x2be63f0200000000, - 0xc9eb872500000000, 0x5e4d9ac200000000, 0xa6a0cd3000000000, - 0x3106d0d700000000, 0x177d130f00000000, 0x80db0ee800000000, - 0x7836591a00000000, 0xef9044fd00000000, 0x75c6ae7000000000, - 0xe260b39700000000, 0x1a8de46500000000, 0x8d2bf98200000000, - 0xab503a5a00000000, 0x3cf627bd00000000, 0xc41b704f00000000, - 0x53bd6da800000000, 0x2367dac400000000, 0xb4c1c72300000000, - 0x4c2c90d100000000, 0xdb8a8d3600000000, 0xfdf14eee00000000, - 0x6a57530900000000, 0x92ba04fb00000000, 0x051c191c00000000, - 0x9f4af39100000000, 0x08ecee7600000000, 0xf001b98400000000, - 0x67a7a46300000000, 0x41dc67bb00000000, 0xd67a7a5c00000000, - 0x2e972dae00000000, 0xb931304900000000, 0x5b3c886e00000000, - 0xcc9a958900000000, 0x3477c27b00000000, 0xa3d1df9c00000000, - 0x85aa1c4400000000, 0x120c01a300000000, 0xeae1565100000000, - 0x7d474bb600000000, 0xe711a13b00000000, 0x70b7bcdc00000000, - 0x885aeb2e00000000, 0x1ffcf6c900000000, 0x3987351100000000, - 0xae2128f600000000, 0x56cc7f0400000000, 0xc16a62e300000000, - 0x92d70f4b00000000, 0x057112ac00000000, 0xfd9c455e00000000, - 0x6a3a58b900000000, 0x4c419b6100000000, 0xdbe7868600000000, - 0x230ad17400000000, 0xb4accc9300000000, 0x2efa261e00000000, - 0xb95c3bf900000000, 0x41b16c0b00000000, 0xd61771ec00000000, - 0xf06cb23400000000, 0x67caafd300000000, 0x9f27f82100000000, - 0x0881e5c600000000, 0xea8c5de100000000, 0x7d2a400600000000, - 0x85c717f400000000, 0x12610a1300000000, 0x341ac9cb00000000, - 0xa3bcd42c00000000, 0x5b5183de00000000, 0xccf79e3900000000, - 0x56a174b400000000, 0xc107695300000000, 0x39ea3ea100000000, - 0xae4c234600000000, 0x8837e09e00000000, 0x1f91fd7900000000, - 0xe77caa8b00000000, 0x70dab76c00000000, 0x07c8c55200000000, - 0x906ed8b500000000, 0x68838f4700000000, 0xff2592a000000000, - 0xd95e517800000000, 0x4ef84c9f00000000, 0xb6151b6d00000000, - 0x21b3068a00000000, 0xbbe5ec0700000000, 0x2c43f1e000000000, - 0xd4aea61200000000, 0x4308bbf500000000, 0x6573782d00000000, - 0xf2d565ca00000000, 0x0a38323800000000, 0x9d9e2fdf00000000, - 0x7f9397f800000000, 0xe8358a1f00000000, 0x10d8dded00000000, - 0x877ec00a00000000, 0xa10503d200000000, 0x36a31e3500000000, - 0xce4e49c700000000, 0x59e8542000000000, 0xc3bebead00000000, - 0x5418a34a00000000, 0xacf5f4b800000000, 0x3b53e95f00000000, - 0x1d282a8700000000, 0x8a8e376000000000, 0x7263609200000000, - 0xe5c57d7500000000, 0xb67810dd00000000, 0x21de0d3a00000000, - 0xd9335ac800000000, 0x4e95472f00000000, 0x68ee84f700000000, - 0xff48991000000000, 0x07a5cee200000000, 0x9003d30500000000, - 0x0a55398800000000, 0x9df3246f00000000, 0x651e739d00000000, - 0xf2b86e7a00000000, 0xd4c3ada200000000, 0x4365b04500000000, - 0xbb88e7b700000000, 0x2c2efa5000000000, 0xce23427700000000, - 0x59855f9000000000, 0xa168086200000000, 0x36ce158500000000, - 0x10b5d65d00000000, 0x8713cbba00000000, 0x7ffe9c4800000000, - 0xe85881af00000000, 0x720e6b2200000000, 0xe5a876c500000000, - 0x1d45213700000000, 0x8ae33cd000000000, 0xac98ff0800000000, - 0x3b3ee2ef00000000, 0xc3d3b51d00000000, 0x5475a8fa00000000, - 0x24af1f9600000000, 0xb309027100000000, 0x4be4558300000000, - 0xdc42486400000000, 0xfa398bbc00000000, 0x6d9f965b00000000, - 0x9572c1a900000000, 0x02d4dc4e00000000, 0x988236c300000000, - 0x0f242b2400000000, 0xf7c97cd600000000, 0x606f613100000000, - 0x4614a2e900000000, 0xd1b2bf0e00000000, 0x295fe8fc00000000, - 0xbef9f51b00000000, 0x5cf44d3c00000000, 0xcb5250db00000000, - 0x33bf072900000000, 0xa4191ace00000000, 0x8262d91600000000, - 0x15c4c4f100000000, 0xed29930300000000, 0x7a8f8ee400000000, - 0xe0d9646900000000, 0x777f798e00000000, 0x8f922e7c00000000, - 0x1834339b00000000, 0x3e4ff04300000000, 0xa9e9eda400000000, - 0x5104ba5600000000, 0xc6a2a7b100000000, 0x951fca1900000000, - 0x02b9d7fe00000000, 0xfa54800c00000000, 0x6df29deb00000000, - 0x4b895e3300000000, 0xdc2f43d400000000, 0x24c2142600000000, - 0xb36409c100000000, 0x2932e34c00000000, 0xbe94feab00000000, - 0x4679a95900000000, 0xd1dfb4be00000000, 0xf7a4776600000000, - 0x60026a8100000000, 0x98ef3d7300000000, 0x0f49209400000000, - 0xed4498b300000000, 0x7ae2855400000000, 0x820fd2a600000000, - 0x15a9cf4100000000, 0x33d20c9900000000, 0xa474117e00000000, - 0x5c99468c00000000, 0xcb3f5b6b00000000, 0x5169b1e600000000, - 0xc6cfac0100000000, 0x3e22fbf300000000, 0xa984e61400000000, - 0x8fff25cc00000000, 0x1859382b00000000, 0xe0b46fd900000000, - 0x7712723e00000000}, - {0x0000000000000000, 0x411b8c6e00000000, 0x823618dd00000000, - 0xc32d94b300000000, 0x456b416100000000, 0x0470cd0f00000000, - 0xc75d59bc00000000, 0x8646d5d200000000, 0x8ad682c200000000, - 0xcbcd0eac00000000, 0x08e09a1f00000000, 0x49fb167100000000, - 0xcfbdc3a300000000, 0x8ea64fcd00000000, 0x4d8bdb7e00000000, - 0x0c90571000000000, 0x55ab745e00000000, 0x14b0f83000000000, - 0xd79d6c8300000000, 0x9686e0ed00000000, 0x10c0353f00000000, - 0x51dbb95100000000, 0x92f62de200000000, 0xd3eda18c00000000, - 0xdf7df69c00000000, 0x9e667af200000000, 0x5d4bee4100000000, - 0x1c50622f00000000, 0x9a16b7fd00000000, 0xdb0d3b9300000000, - 0x1820af2000000000, 0x593b234e00000000, 0xaa56e9bc00000000, - 0xeb4d65d200000000, 0x2860f16100000000, 0x697b7d0f00000000, - 0xef3da8dd00000000, 0xae2624b300000000, 0x6d0bb00000000000, - 0x2c103c6e00000000, 0x20806b7e00000000, 0x619be71000000000, - 0xa2b673a300000000, 0xe3adffcd00000000, 0x65eb2a1f00000000, - 0x24f0a67100000000, 0xe7dd32c200000000, 0xa6c6beac00000000, - 0xfffd9de200000000, 0xbee6118c00000000, 0x7dcb853f00000000, - 0x3cd0095100000000, 0xba96dc8300000000, 0xfb8d50ed00000000, - 0x38a0c45e00000000, 0x79bb483000000000, 0x752b1f2000000000, - 0x3430934e00000000, 0xf71d07fd00000000, 0xb6068b9300000000, - 0x30405e4100000000, 0x715bd22f00000000, 0xb276469c00000000, - 0xf36dcaf200000000, 0x15aba3a200000000, 0x54b02fcc00000000, - 0x979dbb7f00000000, 0xd686371100000000, 0x50c0e2c300000000, - 0x11db6ead00000000, 0xd2f6fa1e00000000, 0x93ed767000000000, - 0x9f7d216000000000, 0xde66ad0e00000000, 0x1d4b39bd00000000, - 0x5c50b5d300000000, 0xda16600100000000, 0x9b0dec6f00000000, - 0x582078dc00000000, 0x193bf4b200000000, 0x4000d7fc00000000, - 0x011b5b9200000000, 0xc236cf2100000000, 0x832d434f00000000, - 0x056b969d00000000, 0x44701af300000000, 0x875d8e4000000000, - 0xc646022e00000000, 0xcad6553e00000000, 0x8bcdd95000000000, - 0x48e04de300000000, 0x09fbc18d00000000, 0x8fbd145f00000000, - 0xcea6983100000000, 0x0d8b0c8200000000, 0x4c9080ec00000000, - 0xbffd4a1e00000000, 0xfee6c67000000000, 0x3dcb52c300000000, - 0x7cd0dead00000000, 0xfa960b7f00000000, 0xbb8d871100000000, - 0x78a013a200000000, 0x39bb9fcc00000000, 0x352bc8dc00000000, - 0x743044b200000000, 0xb71dd00100000000, 0xf6065c6f00000000, - 0x704089bd00000000, 0x315b05d300000000, 0xf276916000000000, - 0xb36d1d0e00000000, 0xea563e4000000000, 0xab4db22e00000000, - 0x6860269d00000000, 0x297baaf300000000, 0xaf3d7f2100000000, - 0xee26f34f00000000, 0x2d0b67fc00000000, 0x6c10eb9200000000, - 0x6080bc8200000000, 0x219b30ec00000000, 0xe2b6a45f00000000, - 0xa3ad283100000000, 0x25ebfde300000000, 0x64f0718d00000000, - 0xa7dde53e00000000, 0xe6c6695000000000, 0x6b50369e00000000, - 0x2a4bbaf000000000, 0xe9662e4300000000, 0xa87da22d00000000, - 0x2e3b77ff00000000, 0x6f20fb9100000000, 0xac0d6f2200000000, - 0xed16e34c00000000, 0xe186b45c00000000, 0xa09d383200000000, - 0x63b0ac8100000000, 0x22ab20ef00000000, 0xa4edf53d00000000, - 0xe5f6795300000000, 0x26dbede000000000, 0x67c0618e00000000, - 0x3efb42c000000000, 0x7fe0ceae00000000, 0xbccd5a1d00000000, - 0xfdd6d67300000000, 0x7b9003a100000000, 0x3a8b8fcf00000000, - 0xf9a61b7c00000000, 0xb8bd971200000000, 0xb42dc00200000000, - 0xf5364c6c00000000, 0x361bd8df00000000, 0x770054b100000000, - 0xf146816300000000, 0xb05d0d0d00000000, 0x737099be00000000, - 0x326b15d000000000, 0xc106df2200000000, 0x801d534c00000000, - 0x4330c7ff00000000, 0x022b4b9100000000, 0x846d9e4300000000, - 0xc576122d00000000, 0x065b869e00000000, 0x47400af000000000, - 0x4bd05de000000000, 0x0acbd18e00000000, 0xc9e6453d00000000, - 0x88fdc95300000000, 0x0ebb1c8100000000, 0x4fa090ef00000000, - 0x8c8d045c00000000, 0xcd96883200000000, 0x94adab7c00000000, - 0xd5b6271200000000, 0x169bb3a100000000, 0x57803fcf00000000, - 0xd1c6ea1d00000000, 0x90dd667300000000, 0x53f0f2c000000000, - 0x12eb7eae00000000, 0x1e7b29be00000000, 0x5f60a5d000000000, - 0x9c4d316300000000, 0xdd56bd0d00000000, 0x5b1068df00000000, - 0x1a0be4b100000000, 0xd926700200000000, 0x983dfc6c00000000, - 0x7efb953c00000000, 0x3fe0195200000000, 0xfccd8de100000000, - 0xbdd6018f00000000, 0x3b90d45d00000000, 0x7a8b583300000000, - 0xb9a6cc8000000000, 0xf8bd40ee00000000, 0xf42d17fe00000000, - 0xb5369b9000000000, 0x761b0f2300000000, 0x3700834d00000000, - 0xb146569f00000000, 0xf05ddaf100000000, 0x33704e4200000000, - 0x726bc22c00000000, 0x2b50e16200000000, 0x6a4b6d0c00000000, - 0xa966f9bf00000000, 0xe87d75d100000000, 0x6e3ba00300000000, - 0x2f202c6d00000000, 0xec0db8de00000000, 0xad1634b000000000, - 0xa18663a000000000, 0xe09defce00000000, 0x23b07b7d00000000, - 0x62abf71300000000, 0xe4ed22c100000000, 0xa5f6aeaf00000000, - 0x66db3a1c00000000, 0x27c0b67200000000, 0xd4ad7c8000000000, - 0x95b6f0ee00000000, 0x569b645d00000000, 0x1780e83300000000, - 0x91c63de100000000, 0xd0ddb18f00000000, 0x13f0253c00000000, - 0x52eba95200000000, 0x5e7bfe4200000000, 0x1f60722c00000000, - 0xdc4de69f00000000, 0x9d566af100000000, 0x1b10bf2300000000, - 0x5a0b334d00000000, 0x9926a7fe00000000, 0xd83d2b9000000000, - 0x810608de00000000, 0xc01d84b000000000, 0x0330100300000000, - 0x422b9c6d00000000, 0xc46d49bf00000000, 0x8576c5d100000000, - 0x465b516200000000, 0x0740dd0c00000000, 0x0bd08a1c00000000, - 0x4acb067200000000, 0x89e692c100000000, 0xc8fd1eaf00000000, - 0x4ebbcb7d00000000, 0x0fa0471300000000, 0xcc8dd3a000000000, - 0x8d965fce00000000}, - {0x0000000000000000, 0x1dfdb50100000000, 0x3afa6b0300000000, - 0x2707de0200000000, 0x74f4d70600000000, 0x6909620700000000, - 0x4e0ebc0500000000, 0x53f3090400000000, 0xe8e8af0d00000000, - 0xf5151a0c00000000, 0xd212c40e00000000, 0xcfef710f00000000, - 0x9c1c780b00000000, 0x81e1cd0a00000000, 0xa6e6130800000000, - 0xbb1ba60900000000, 0xd0d15f1b00000000, 0xcd2cea1a00000000, - 0xea2b341800000000, 0xf7d6811900000000, 0xa425881d00000000, - 0xb9d83d1c00000000, 0x9edfe31e00000000, 0x8322561f00000000, - 0x3839f01600000000, 0x25c4451700000000, 0x02c39b1500000000, - 0x1f3e2e1400000000, 0x4ccd271000000000, 0x5130921100000000, - 0x76374c1300000000, 0x6bcaf91200000000, 0xa0a3bf3600000000, - 0xbd5e0a3700000000, 0x9a59d43500000000, 0x87a4613400000000, - 0xd457683000000000, 0xc9aadd3100000000, 0xeead033300000000, - 0xf350b63200000000, 0x484b103b00000000, 0x55b6a53a00000000, - 0x72b17b3800000000, 0x6f4cce3900000000, 0x3cbfc73d00000000, - 0x2142723c00000000, 0x0645ac3e00000000, 0x1bb8193f00000000, - 0x7072e02d00000000, 0x6d8f552c00000000, 0x4a888b2e00000000, - 0x57753e2f00000000, 0x0486372b00000000, 0x197b822a00000000, - 0x3e7c5c2800000000, 0x2381e92900000000, 0x989a4f2000000000, - 0x8567fa2100000000, 0xa260242300000000, 0xbf9d912200000000, - 0xec6e982600000000, 0xf1932d2700000000, 0xd694f32500000000, - 0xcb69462400000000, 0x40477f6d00000000, 0x5dbaca6c00000000, - 0x7abd146e00000000, 0x6740a16f00000000, 0x34b3a86b00000000, - 0x294e1d6a00000000, 0x0e49c36800000000, 0x13b4766900000000, - 0xa8afd06000000000, 0xb552656100000000, 0x9255bb6300000000, - 0x8fa80e6200000000, 0xdc5b076600000000, 0xc1a6b26700000000, - 0xe6a16c6500000000, 0xfb5cd96400000000, 0x9096207600000000, - 0x8d6b957700000000, 0xaa6c4b7500000000, 0xb791fe7400000000, - 0xe462f77000000000, 0xf99f427100000000, 0xde989c7300000000, - 0xc365297200000000, 0x787e8f7b00000000, 0x65833a7a00000000, - 0x4284e47800000000, 0x5f79517900000000, 0x0c8a587d00000000, - 0x1177ed7c00000000, 0x3670337e00000000, 0x2b8d867f00000000, - 0xe0e4c05b00000000, 0xfd19755a00000000, 0xda1eab5800000000, - 0xc7e31e5900000000, 0x9410175d00000000, 0x89eda25c00000000, - 0xaeea7c5e00000000, 0xb317c95f00000000, 0x080c6f5600000000, - 0x15f1da5700000000, 0x32f6045500000000, 0x2f0bb15400000000, - 0x7cf8b85000000000, 0x61050d5100000000, 0x4602d35300000000, - 0x5bff665200000000, 0x30359f4000000000, 0x2dc82a4100000000, - 0x0acff44300000000, 0x1732414200000000, 0x44c1484600000000, - 0x593cfd4700000000, 0x7e3b234500000000, 0x63c6964400000000, - 0xd8dd304d00000000, 0xc520854c00000000, 0xe2275b4e00000000, - 0xffdaee4f00000000, 0xac29e74b00000000, 0xb1d4524a00000000, - 0x96d38c4800000000, 0x8b2e394900000000, 0x808efeda00000000, - 0x9d734bdb00000000, 0xba7495d900000000, 0xa78920d800000000, - 0xf47a29dc00000000, 0xe9879cdd00000000, 0xce8042df00000000, - 0xd37df7de00000000, 0x686651d700000000, 0x759be4d600000000, - 0x529c3ad400000000, 0x4f618fd500000000, 0x1c9286d100000000, - 0x016f33d000000000, 0x2668edd200000000, 0x3b9558d300000000, - 0x505fa1c100000000, 0x4da214c000000000, 0x6aa5cac200000000, - 0x77587fc300000000, 0x24ab76c700000000, 0x3956c3c600000000, - 0x1e511dc400000000, 0x03aca8c500000000, 0xb8b70ecc00000000, - 0xa54abbcd00000000, 0x824d65cf00000000, 0x9fb0d0ce00000000, - 0xcc43d9ca00000000, 0xd1be6ccb00000000, 0xf6b9b2c900000000, - 0xeb4407c800000000, 0x202d41ec00000000, 0x3dd0f4ed00000000, - 0x1ad72aef00000000, 0x072a9fee00000000, 0x54d996ea00000000, - 0x492423eb00000000, 0x6e23fde900000000, 0x73de48e800000000, - 0xc8c5eee100000000, 0xd5385be000000000, 0xf23f85e200000000, - 0xefc230e300000000, 0xbc3139e700000000, 0xa1cc8ce600000000, - 0x86cb52e400000000, 0x9b36e7e500000000, 0xf0fc1ef700000000, - 0xed01abf600000000, 0xca0675f400000000, 0xd7fbc0f500000000, - 0x8408c9f100000000, 0x99f57cf000000000, 0xbef2a2f200000000, - 0xa30f17f300000000, 0x1814b1fa00000000, 0x05e904fb00000000, - 0x22eedaf900000000, 0x3f136ff800000000, 0x6ce066fc00000000, - 0x711dd3fd00000000, 0x561a0dff00000000, 0x4be7b8fe00000000, - 0xc0c981b700000000, 0xdd3434b600000000, 0xfa33eab400000000, - 0xe7ce5fb500000000, 0xb43d56b100000000, 0xa9c0e3b000000000, - 0x8ec73db200000000, 0x933a88b300000000, 0x28212eba00000000, - 0x35dc9bbb00000000, 0x12db45b900000000, 0x0f26f0b800000000, - 0x5cd5f9bc00000000, 0x41284cbd00000000, 0x662f92bf00000000, - 0x7bd227be00000000, 0x1018deac00000000, 0x0de56bad00000000, - 0x2ae2b5af00000000, 0x371f00ae00000000, 0x64ec09aa00000000, - 0x7911bcab00000000, 0x5e1662a900000000, 0x43ebd7a800000000, - 0xf8f071a100000000, 0xe50dc4a000000000, 0xc20a1aa200000000, - 0xdff7afa300000000, 0x8c04a6a700000000, 0x91f913a600000000, - 0xb6fecda400000000, 0xab0378a500000000, 0x606a3e8100000000, - 0x7d978b8000000000, 0x5a90558200000000, 0x476de08300000000, - 0x149ee98700000000, 0x09635c8600000000, 0x2e64828400000000, - 0x3399378500000000, 0x8882918c00000000, 0x957f248d00000000, - 0xb278fa8f00000000, 0xaf854f8e00000000, 0xfc76468a00000000, - 0xe18bf38b00000000, 0xc68c2d8900000000, 0xdb71988800000000, - 0xb0bb619a00000000, 0xad46d49b00000000, 0x8a410a9900000000, - 0x97bcbf9800000000, 0xc44fb69c00000000, 0xd9b2039d00000000, - 0xfeb5dd9f00000000, 0xe348689e00000000, 0x5853ce9700000000, - 0x45ae7b9600000000, 0x62a9a59400000000, 0x7f54109500000000, - 0x2ca7199100000000, 0x315aac9000000000, 0x165d729200000000, - 0x0ba0c79300000000}, - {0x0000000000000000, 0x24d9076300000000, 0x48b20fc600000000, - 0x6c6b08a500000000, 0xd1626e5700000000, 0xf5bb693400000000, - 0x99d0619100000000, 0xbd0966f200000000, 0xa2c5dcae00000000, - 0x861cdbcd00000000, 0xea77d36800000000, 0xceaed40b00000000, - 0x73a7b2f900000000, 0x577eb59a00000000, 0x3b15bd3f00000000, - 0x1fccba5c00000000, 0x058dc88600000000, 0x2154cfe500000000, - 0x4d3fc74000000000, 0x69e6c02300000000, 0xd4efa6d100000000, - 0xf036a1b200000000, 0x9c5da91700000000, 0xb884ae7400000000, - 0xa748142800000000, 0x8391134b00000000, 0xeffa1bee00000000, - 0xcb231c8d00000000, 0x762a7a7f00000000, 0x52f37d1c00000000, - 0x3e9875b900000000, 0x1a4172da00000000, 0x4b1ce0d600000000, - 0x6fc5e7b500000000, 0x03aeef1000000000, 0x2777e87300000000, - 0x9a7e8e8100000000, 0xbea789e200000000, 0xd2cc814700000000, - 0xf615862400000000, 0xe9d93c7800000000, 0xcd003b1b00000000, - 0xa16b33be00000000, 0x85b234dd00000000, 0x38bb522f00000000, - 0x1c62554c00000000, 0x70095de900000000, 0x54d05a8a00000000, - 0x4e91285000000000, 0x6a482f3300000000, 0x0623279600000000, - 0x22fa20f500000000, 0x9ff3460700000000, 0xbb2a416400000000, - 0xd74149c100000000, 0xf3984ea200000000, 0xec54f4fe00000000, - 0xc88df39d00000000, 0xa4e6fb3800000000, 0x803ffc5b00000000, - 0x3d369aa900000000, 0x19ef9dca00000000, 0x7584956f00000000, - 0x515d920c00000000, 0xd73eb17600000000, 0xf3e7b61500000000, - 0x9f8cbeb000000000, 0xbb55b9d300000000, 0x065cdf2100000000, - 0x2285d84200000000, 0x4eeed0e700000000, 0x6a37d78400000000, - 0x75fb6dd800000000, 0x51226abb00000000, 0x3d49621e00000000, - 0x1990657d00000000, 0xa499038f00000000, 0x804004ec00000000, - 0xec2b0c4900000000, 0xc8f20b2a00000000, 0xd2b379f000000000, - 0xf66a7e9300000000, 0x9a01763600000000, 0xbed8715500000000, - 0x03d117a700000000, 0x270810c400000000, 0x4b63186100000000, - 0x6fba1f0200000000, 0x7076a55e00000000, 0x54afa23d00000000, - 0x38c4aa9800000000, 0x1c1dadfb00000000, 0xa114cb0900000000, - 0x85cdcc6a00000000, 0xe9a6c4cf00000000, 0xcd7fc3ac00000000, - 0x9c2251a000000000, 0xb8fb56c300000000, 0xd4905e6600000000, - 0xf049590500000000, 0x4d403ff700000000, 0x6999389400000000, - 0x05f2303100000000, 0x212b375200000000, 0x3ee78d0e00000000, - 0x1a3e8a6d00000000, 0x765582c800000000, 0x528c85ab00000000, - 0xef85e35900000000, 0xcb5ce43a00000000, 0xa737ec9f00000000, - 0x83eeebfc00000000, 0x99af992600000000, 0xbd769e4500000000, - 0xd11d96e000000000, 0xf5c4918300000000, 0x48cdf77100000000, - 0x6c14f01200000000, 0x007ff8b700000000, 0x24a6ffd400000000, - 0x3b6a458800000000, 0x1fb342eb00000000, 0x73d84a4e00000000, - 0x57014d2d00000000, 0xea082bdf00000000, 0xced12cbc00000000, - 0xa2ba241900000000, 0x8663237a00000000, 0xae7d62ed00000000, - 0x8aa4658e00000000, 0xe6cf6d2b00000000, 0xc2166a4800000000, - 0x7f1f0cba00000000, 0x5bc60bd900000000, 0x37ad037c00000000, - 0x1374041f00000000, 0x0cb8be4300000000, 0x2861b92000000000, - 0x440ab18500000000, 0x60d3b6e600000000, 0xdddad01400000000, - 0xf903d77700000000, 0x9568dfd200000000, 0xb1b1d8b100000000, - 0xabf0aa6b00000000, 0x8f29ad0800000000, 0xe342a5ad00000000, - 0xc79ba2ce00000000, 0x7a92c43c00000000, 0x5e4bc35f00000000, - 0x3220cbfa00000000, 0x16f9cc9900000000, 0x093576c500000000, - 0x2dec71a600000000, 0x4187790300000000, 0x655e7e6000000000, - 0xd857189200000000, 0xfc8e1ff100000000, 0x90e5175400000000, - 0xb43c103700000000, 0xe561823b00000000, 0xc1b8855800000000, - 0xadd38dfd00000000, 0x890a8a9e00000000, 0x3403ec6c00000000, - 0x10daeb0f00000000, 0x7cb1e3aa00000000, 0x5868e4c900000000, - 0x47a45e9500000000, 0x637d59f600000000, 0x0f16515300000000, - 0x2bcf563000000000, 0x96c630c200000000, 0xb21f37a100000000, - 0xde743f0400000000, 0xfaad386700000000, 0xe0ec4abd00000000, - 0xc4354dde00000000, 0xa85e457b00000000, 0x8c87421800000000, - 0x318e24ea00000000, 0x1557238900000000, 0x793c2b2c00000000, - 0x5de52c4f00000000, 0x4229961300000000, 0x66f0917000000000, - 0x0a9b99d500000000, 0x2e429eb600000000, 0x934bf84400000000, - 0xb792ff2700000000, 0xdbf9f78200000000, 0xff20f0e100000000, - 0x7943d39b00000000, 0x5d9ad4f800000000, 0x31f1dc5d00000000, - 0x1528db3e00000000, 0xa821bdcc00000000, 0x8cf8baaf00000000, - 0xe093b20a00000000, 0xc44ab56900000000, 0xdb860f3500000000, - 0xff5f085600000000, 0x933400f300000000, 0xb7ed079000000000, - 0x0ae4616200000000, 0x2e3d660100000000, 0x42566ea400000000, - 0x668f69c700000000, 0x7cce1b1d00000000, 0x58171c7e00000000, - 0x347c14db00000000, 0x10a513b800000000, 0xadac754a00000000, - 0x8975722900000000, 0xe51e7a8c00000000, 0xc1c77def00000000, - 0xde0bc7b300000000, 0xfad2c0d000000000, 0x96b9c87500000000, - 0xb260cf1600000000, 0x0f69a9e400000000, 0x2bb0ae8700000000, - 0x47dba62200000000, 0x6302a14100000000, 0x325f334d00000000, - 0x1686342e00000000, 0x7aed3c8b00000000, 0x5e343be800000000, - 0xe33d5d1a00000000, 0xc7e45a7900000000, 0xab8f52dc00000000, - 0x8f5655bf00000000, 0x909aefe300000000, 0xb443e88000000000, - 0xd828e02500000000, 0xfcf1e74600000000, 0x41f881b400000000, - 0x652186d700000000, 0x094a8e7200000000, 0x2d93891100000000, - 0x37d2fbcb00000000, 0x130bfca800000000, 0x7f60f40d00000000, - 0x5bb9f36e00000000, 0xe6b0959c00000000, 0xc26992ff00000000, - 0xae029a5a00000000, 0x8adb9d3900000000, 0x9517276500000000, - 0xb1ce200600000000, 0xdda528a300000000, 0xf97c2fc000000000, - 0x4475493200000000, 0x60ac4e5100000000, 0x0cc746f400000000, - 0x281e419700000000}, - {0x0000000000000000, 0x08e3603c00000000, 0x10c6c17800000000, - 0x1825a14400000000, 0x208c83f100000000, 0x286fe3cd00000000, - 0x304a428900000000, 0x38a922b500000000, 0x011e763800000000, - 0x09fd160400000000, 0x11d8b74000000000, 0x193bd77c00000000, - 0x2192f5c900000000, 0x297195f500000000, 0x315434b100000000, - 0x39b7548d00000000, 0x023cec7000000000, 0x0adf8c4c00000000, - 0x12fa2d0800000000, 0x1a194d3400000000, 0x22b06f8100000000, - 0x2a530fbd00000000, 0x3276aef900000000, 0x3a95cec500000000, - 0x03229a4800000000, 0x0bc1fa7400000000, 0x13e45b3000000000, - 0x1b073b0c00000000, 0x23ae19b900000000, 0x2b4d798500000000, - 0x3368d8c100000000, 0x3b8bb8fd00000000, 0x0478d8e100000000, - 0x0c9bb8dd00000000, 0x14be199900000000, 0x1c5d79a500000000, - 0x24f45b1000000000, 0x2c173b2c00000000, 0x34329a6800000000, - 0x3cd1fa5400000000, 0x0566aed900000000, 0x0d85cee500000000, - 0x15a06fa100000000, 0x1d430f9d00000000, 0x25ea2d2800000000, - 0x2d094d1400000000, 0x352cec5000000000, 0x3dcf8c6c00000000, - 0x0644349100000000, 0x0ea754ad00000000, 0x1682f5e900000000, - 0x1e6195d500000000, 0x26c8b76000000000, 0x2e2bd75c00000000, - 0x360e761800000000, 0x3eed162400000000, 0x075a42a900000000, - 0x0fb9229500000000, 0x179c83d100000000, 0x1f7fe3ed00000000, - 0x27d6c15800000000, 0x2f35a16400000000, 0x3710002000000000, - 0x3ff3601c00000000, 0x49f6c11800000000, 0x4115a12400000000, - 0x5930006000000000, 0x51d3605c00000000, 0x697a42e900000000, - 0x619922d500000000, 0x79bc839100000000, 0x715fe3ad00000000, - 0x48e8b72000000000, 0x400bd71c00000000, 0x582e765800000000, - 0x50cd166400000000, 0x686434d100000000, 0x608754ed00000000, - 0x78a2f5a900000000, 0x7041959500000000, 0x4bca2d6800000000, - 0x43294d5400000000, 0x5b0cec1000000000, 0x53ef8c2c00000000, - 0x6b46ae9900000000, 0x63a5cea500000000, 0x7b806fe100000000, - 0x73630fdd00000000, 0x4ad45b5000000000, 0x42373b6c00000000, - 0x5a129a2800000000, 0x52f1fa1400000000, 0x6a58d8a100000000, - 0x62bbb89d00000000, 0x7a9e19d900000000, 0x727d79e500000000, - 0x4d8e19f900000000, 0x456d79c500000000, 0x5d48d88100000000, - 0x55abb8bd00000000, 0x6d029a0800000000, 0x65e1fa3400000000, - 0x7dc45b7000000000, 0x75273b4c00000000, 0x4c906fc100000000, - 0x44730ffd00000000, 0x5c56aeb900000000, 0x54b5ce8500000000, - 0x6c1cec3000000000, 0x64ff8c0c00000000, 0x7cda2d4800000000, - 0x74394d7400000000, 0x4fb2f58900000000, 0x475195b500000000, - 0x5f7434f100000000, 0x579754cd00000000, 0x6f3e767800000000, - 0x67dd164400000000, 0x7ff8b70000000000, 0x771bd73c00000000, - 0x4eac83b100000000, 0x464fe38d00000000, 0x5e6a42c900000000, - 0x568922f500000000, 0x6e20004000000000, 0x66c3607c00000000, - 0x7ee6c13800000000, 0x7605a10400000000, 0x92ec833100000000, - 0x9a0fe30d00000000, 0x822a424900000000, 0x8ac9227500000000, - 0xb26000c000000000, 0xba8360fc00000000, 0xa2a6c1b800000000, - 0xaa45a18400000000, 0x93f2f50900000000, 0x9b11953500000000, - 0x8334347100000000, 0x8bd7544d00000000, 0xb37e76f800000000, - 0xbb9d16c400000000, 0xa3b8b78000000000, 0xab5bd7bc00000000, - 0x90d06f4100000000, 0x98330f7d00000000, 0x8016ae3900000000, - 0x88f5ce0500000000, 0xb05cecb000000000, 0xb8bf8c8c00000000, - 0xa09a2dc800000000, 0xa8794df400000000, 0x91ce197900000000, - 0x992d794500000000, 0x8108d80100000000, 0x89ebb83d00000000, - 0xb1429a8800000000, 0xb9a1fab400000000, 0xa1845bf000000000, - 0xa9673bcc00000000, 0x96945bd000000000, 0x9e773bec00000000, - 0x86529aa800000000, 0x8eb1fa9400000000, 0xb618d82100000000, - 0xbefbb81d00000000, 0xa6de195900000000, 0xae3d796500000000, - 0x978a2de800000000, 0x9f694dd400000000, 0x874cec9000000000, - 0x8faf8cac00000000, 0xb706ae1900000000, 0xbfe5ce2500000000, - 0xa7c06f6100000000, 0xaf230f5d00000000, 0x94a8b7a000000000, - 0x9c4bd79c00000000, 0x846e76d800000000, 0x8c8d16e400000000, - 0xb424345100000000, 0xbcc7546d00000000, 0xa4e2f52900000000, - 0xac01951500000000, 0x95b6c19800000000, 0x9d55a1a400000000, - 0x857000e000000000, 0x8d9360dc00000000, 0xb53a426900000000, - 0xbdd9225500000000, 0xa5fc831100000000, 0xad1fe32d00000000, - 0xdb1a422900000000, 0xd3f9221500000000, 0xcbdc835100000000, - 0xc33fe36d00000000, 0xfb96c1d800000000, 0xf375a1e400000000, - 0xeb5000a000000000, 0xe3b3609c00000000, 0xda04341100000000, - 0xd2e7542d00000000, 0xcac2f56900000000, 0xc221955500000000, - 0xfa88b7e000000000, 0xf26bd7dc00000000, 0xea4e769800000000, - 0xe2ad16a400000000, 0xd926ae5900000000, 0xd1c5ce6500000000, - 0xc9e06f2100000000, 0xc1030f1d00000000, 0xf9aa2da800000000, - 0xf1494d9400000000, 0xe96cecd000000000, 0xe18f8cec00000000, - 0xd838d86100000000, 0xd0dbb85d00000000, 0xc8fe191900000000, - 0xc01d792500000000, 0xf8b45b9000000000, 0xf0573bac00000000, - 0xe8729ae800000000, 0xe091fad400000000, 0xdf629ac800000000, - 0xd781faf400000000, 0xcfa45bb000000000, 0xc7473b8c00000000, - 0xffee193900000000, 0xf70d790500000000, 0xef28d84100000000, - 0xe7cbb87d00000000, 0xde7cecf000000000, 0xd69f8ccc00000000, - 0xceba2d8800000000, 0xc6594db400000000, 0xfef06f0100000000, - 0xf6130f3d00000000, 0xee36ae7900000000, 0xe6d5ce4500000000, - 0xdd5e76b800000000, 0xd5bd168400000000, 0xcd98b7c000000000, - 0xc57bd7fc00000000, 0xfdd2f54900000000, 0xf531957500000000, - 0xed14343100000000, 0xe5f7540d00000000, 0xdc40008000000000, - 0xd4a360bc00000000, 0xcc86c1f800000000, 0xc465a1c400000000, - 0xfccc837100000000, 0xf42fe34d00000000, 0xec0a420900000000, - 0xe4e9223500000000}, - {0x0000000000000000, 0xd1e8e70e00000000, 0xa2d1cf1d00000000, - 0x7339281300000000, 0x44a39f3b00000000, 0x954b783500000000, - 0xe672502600000000, 0x379ab72800000000, 0x88463f7700000000, - 0x59aed87900000000, 0x2a97f06a00000000, 0xfb7f176400000000, - 0xcce5a04c00000000, 0x1d0d474200000000, 0x6e346f5100000000, - 0xbfdc885f00000000, 0x108d7eee00000000, 0xc16599e000000000, - 0xb25cb1f300000000, 0x63b456fd00000000, 0x542ee1d500000000, - 0x85c606db00000000, 0xf6ff2ec800000000, 0x2717c9c600000000, - 0x98cb419900000000, 0x4923a69700000000, 0x3a1a8e8400000000, - 0xebf2698a00000000, 0xdc68dea200000000, 0x0d8039ac00000000, - 0x7eb911bf00000000, 0xaf51f6b100000000, 0x611c8c0700000000, - 0xb0f46b0900000000, 0xc3cd431a00000000, 0x1225a41400000000, - 0x25bf133c00000000, 0xf457f43200000000, 0x876edc2100000000, - 0x56863b2f00000000, 0xe95ab37000000000, 0x38b2547e00000000, - 0x4b8b7c6d00000000, 0x9a639b6300000000, 0xadf92c4b00000000, - 0x7c11cb4500000000, 0x0f28e35600000000, 0xdec0045800000000, - 0x7191f2e900000000, 0xa07915e700000000, 0xd3403df400000000, - 0x02a8dafa00000000, 0x35326dd200000000, 0xe4da8adc00000000, - 0x97e3a2cf00000000, 0x460b45c100000000, 0xf9d7cd9e00000000, - 0x283f2a9000000000, 0x5b06028300000000, 0x8aeee58d00000000, - 0xbd7452a500000000, 0x6c9cb5ab00000000, 0x1fa59db800000000, - 0xce4d7ab600000000, 0xc238180f00000000, 0x13d0ff0100000000, - 0x60e9d71200000000, 0xb101301c00000000, 0x869b873400000000, - 0x5773603a00000000, 0x244a482900000000, 0xf5a2af2700000000, - 0x4a7e277800000000, 0x9b96c07600000000, 0xe8afe86500000000, - 0x39470f6b00000000, 0x0eddb84300000000, 0xdf355f4d00000000, - 0xac0c775e00000000, 0x7de4905000000000, 0xd2b566e100000000, - 0x035d81ef00000000, 0x7064a9fc00000000, 0xa18c4ef200000000, - 0x9616f9da00000000, 0x47fe1ed400000000, 0x34c736c700000000, - 0xe52fd1c900000000, 0x5af3599600000000, 0x8b1bbe9800000000, - 0xf822968b00000000, 0x29ca718500000000, 0x1e50c6ad00000000, - 0xcfb821a300000000, 0xbc8109b000000000, 0x6d69eebe00000000, - 0xa324940800000000, 0x72cc730600000000, 0x01f55b1500000000, - 0xd01dbc1b00000000, 0xe7870b3300000000, 0x366fec3d00000000, - 0x4556c42e00000000, 0x94be232000000000, 0x2b62ab7f00000000, - 0xfa8a4c7100000000, 0x89b3646200000000, 0x585b836c00000000, - 0x6fc1344400000000, 0xbe29d34a00000000, 0xcd10fb5900000000, - 0x1cf81c5700000000, 0xb3a9eae600000000, 0x62410de800000000, - 0x117825fb00000000, 0xc090c2f500000000, 0xf70a75dd00000000, - 0x26e292d300000000, 0x55dbbac000000000, 0x84335dce00000000, - 0x3befd59100000000, 0xea07329f00000000, 0x993e1a8c00000000, - 0x48d6fd8200000000, 0x7f4c4aaa00000000, 0xaea4ada400000000, - 0xdd9d85b700000000, 0x0c7562b900000000, 0x8471301e00000000, - 0x5599d71000000000, 0x26a0ff0300000000, 0xf748180d00000000, - 0xc0d2af2500000000, 0x113a482b00000000, 0x6203603800000000, - 0xb3eb873600000000, 0x0c370f6900000000, 0xdddfe86700000000, - 0xaee6c07400000000, 0x7f0e277a00000000, 0x4894905200000000, - 0x997c775c00000000, 0xea455f4f00000000, 0x3badb84100000000, - 0x94fc4ef000000000, 0x4514a9fe00000000, 0x362d81ed00000000, - 0xe7c566e300000000, 0xd05fd1cb00000000, 0x01b736c500000000, - 0x728e1ed600000000, 0xa366f9d800000000, 0x1cba718700000000, - 0xcd52968900000000, 0xbe6bbe9a00000000, 0x6f83599400000000, - 0x5819eebc00000000, 0x89f109b200000000, 0xfac821a100000000, - 0x2b20c6af00000000, 0xe56dbc1900000000, 0x34855b1700000000, - 0x47bc730400000000, 0x9654940a00000000, 0xa1ce232200000000, - 0x7026c42c00000000, 0x031fec3f00000000, 0xd2f70b3100000000, - 0x6d2b836e00000000, 0xbcc3646000000000, 0xcffa4c7300000000, - 0x1e12ab7d00000000, 0x29881c5500000000, 0xf860fb5b00000000, - 0x8b59d34800000000, 0x5ab1344600000000, 0xf5e0c2f700000000, - 0x240825f900000000, 0x57310dea00000000, 0x86d9eae400000000, - 0xb1435dcc00000000, 0x60abbac200000000, 0x139292d100000000, - 0xc27a75df00000000, 0x7da6fd8000000000, 0xac4e1a8e00000000, - 0xdf77329d00000000, 0x0e9fd59300000000, 0x390562bb00000000, - 0xe8ed85b500000000, 0x9bd4ada600000000, 0x4a3c4aa800000000, - 0x4649281100000000, 0x97a1cf1f00000000, 0xe498e70c00000000, - 0x3570000200000000, 0x02eab72a00000000, 0xd302502400000000, - 0xa03b783700000000, 0x71d39f3900000000, 0xce0f176600000000, - 0x1fe7f06800000000, 0x6cded87b00000000, 0xbd363f7500000000, - 0x8aac885d00000000, 0x5b446f5300000000, 0x287d474000000000, - 0xf995a04e00000000, 0x56c456ff00000000, 0x872cb1f100000000, - 0xf41599e200000000, 0x25fd7eec00000000, 0x1267c9c400000000, - 0xc38f2eca00000000, 0xb0b606d900000000, 0x615ee1d700000000, - 0xde82698800000000, 0x0f6a8e8600000000, 0x7c53a69500000000, - 0xadbb419b00000000, 0x9a21f6b300000000, 0x4bc911bd00000000, - 0x38f039ae00000000, 0xe918dea000000000, 0x2755a41600000000, - 0xf6bd431800000000, 0x85846b0b00000000, 0x546c8c0500000000, - 0x63f63b2d00000000, 0xb21edc2300000000, 0xc127f43000000000, - 0x10cf133e00000000, 0xaf139b6100000000, 0x7efb7c6f00000000, - 0x0dc2547c00000000, 0xdc2ab37200000000, 0xebb0045a00000000, - 0x3a58e35400000000, 0x4961cb4700000000, 0x98892c4900000000, - 0x37d8daf800000000, 0xe6303df600000000, 0x950915e500000000, - 0x44e1f2eb00000000, 0x737b45c300000000, 0xa293a2cd00000000, - 0xd1aa8ade00000000, 0x00426dd000000000, 0xbf9ee58f00000000, - 0x6e76028100000000, 0x1d4f2a9200000000, 0xcca7cd9c00000000, - 0xfb3d7ab400000000, 0x2ad59dba00000000, 0x59ecb5a900000000, - 0x880452a700000000}, - {0x0000000000000000, 0xaa05daf100000000, 0x150dc53800000000, - 0xbf081fc900000000, 0x2a1a8a7100000000, 0x801f508000000000, - 0x3f174f4900000000, 0x951295b800000000, 0x543414e300000000, - 0xfe31ce1200000000, 0x4139d1db00000000, 0xeb3c0b2a00000000, - 0x7e2e9e9200000000, 0xd42b446300000000, 0x6b235baa00000000, - 0xc126815b00000000, 0xe96e591d00000000, 0x436b83ec00000000, - 0xfc639c2500000000, 0x566646d400000000, 0xc374d36c00000000, - 0x6971099d00000000, 0xd679165400000000, 0x7c7ccca500000000, - 0xbd5a4dfe00000000, 0x175f970f00000000, 0xa85788c600000000, - 0x0252523700000000, 0x9740c78f00000000, 0x3d451d7e00000000, - 0x824d02b700000000, 0x2848d84600000000, 0xd2ddb23a00000000, - 0x78d868cb00000000, 0xc7d0770200000000, 0x6dd5adf300000000, - 0xf8c7384b00000000, 0x52c2e2ba00000000, 0xedcafd7300000000, - 0x47cf278200000000, 0x86e9a6d900000000, 0x2cec7c2800000000, - 0x93e463e100000000, 0x39e1b91000000000, 0xacf32ca800000000, - 0x06f6f65900000000, 0xb9fee99000000000, 0x13fb336100000000, - 0x3bb3eb2700000000, 0x91b631d600000000, 0x2ebe2e1f00000000, - 0x84bbf4ee00000000, 0x11a9615600000000, 0xbbacbba700000000, - 0x04a4a46e00000000, 0xaea17e9f00000000, 0x6f87ffc400000000, - 0xc582253500000000, 0x7a8a3afc00000000, 0xd08fe00d00000000, - 0x459d75b500000000, 0xef98af4400000000, 0x5090b08d00000000, - 0xfa956a7c00000000, 0xa4bb657500000000, 0x0ebebf8400000000, - 0xb1b6a04d00000000, 0x1bb37abc00000000, 0x8ea1ef0400000000, - 0x24a435f500000000, 0x9bac2a3c00000000, 0x31a9f0cd00000000, - 0xf08f719600000000, 0x5a8aab6700000000, 0xe582b4ae00000000, - 0x4f876e5f00000000, 0xda95fbe700000000, 0x7090211600000000, - 0xcf983edf00000000, 0x659de42e00000000, 0x4dd53c6800000000, - 0xe7d0e69900000000, 0x58d8f95000000000, 0xf2dd23a100000000, - 0x67cfb61900000000, 0xcdca6ce800000000, 0x72c2732100000000, - 0xd8c7a9d000000000, 0x19e1288b00000000, 0xb3e4f27a00000000, - 0x0cecedb300000000, 0xa6e9374200000000, 0x33fba2fa00000000, - 0x99fe780b00000000, 0x26f667c200000000, 0x8cf3bd3300000000, - 0x7666d74f00000000, 0xdc630dbe00000000, 0x636b127700000000, - 0xc96ec88600000000, 0x5c7c5d3e00000000, 0xf67987cf00000000, - 0x4971980600000000, 0xe37442f700000000, 0x2252c3ac00000000, - 0x8857195d00000000, 0x375f069400000000, 0x9d5adc6500000000, - 0x084849dd00000000, 0xa24d932c00000000, 0x1d458ce500000000, - 0xb740561400000000, 0x9f088e5200000000, 0x350d54a300000000, - 0x8a054b6a00000000, 0x2000919b00000000, 0xb512042300000000, - 0x1f17ded200000000, 0xa01fc11b00000000, 0x0a1a1bea00000000, - 0xcb3c9ab100000000, 0x6139404000000000, 0xde315f8900000000, - 0x7434857800000000, 0xe12610c000000000, 0x4b23ca3100000000, - 0xf42bd5f800000000, 0x5e2e0f0900000000, 0x4877cbea00000000, - 0xe272111b00000000, 0x5d7a0ed200000000, 0xf77fd42300000000, - 0x626d419b00000000, 0xc8689b6a00000000, 0x776084a300000000, - 0xdd655e5200000000, 0x1c43df0900000000, 0xb64605f800000000, - 0x094e1a3100000000, 0xa34bc0c000000000, 0x3659557800000000, - 0x9c5c8f8900000000, 0x2354904000000000, 0x89514ab100000000, - 0xa11992f700000000, 0x0b1c480600000000, 0xb41457cf00000000, - 0x1e118d3e00000000, 0x8b03188600000000, 0x2106c27700000000, - 0x9e0eddbe00000000, 0x340b074f00000000, 0xf52d861400000000, - 0x5f285ce500000000, 0xe020432c00000000, 0x4a2599dd00000000, - 0xdf370c6500000000, 0x7532d69400000000, 0xca3ac95d00000000, - 0x603f13ac00000000, 0x9aaa79d000000000, 0x30afa32100000000, - 0x8fa7bce800000000, 0x25a2661900000000, 0xb0b0f3a100000000, - 0x1ab5295000000000, 0xa5bd369900000000, 0x0fb8ec6800000000, - 0xce9e6d3300000000, 0x649bb7c200000000, 0xdb93a80b00000000, - 0x719672fa00000000, 0xe484e74200000000, 0x4e813db300000000, - 0xf189227a00000000, 0x5b8cf88b00000000, 0x73c420cd00000000, - 0xd9c1fa3c00000000, 0x66c9e5f500000000, 0xcccc3f0400000000, - 0x59deaabc00000000, 0xf3db704d00000000, 0x4cd36f8400000000, - 0xe6d6b57500000000, 0x27f0342e00000000, 0x8df5eedf00000000, - 0x32fdf11600000000, 0x98f82be700000000, 0x0deabe5f00000000, - 0xa7ef64ae00000000, 0x18e77b6700000000, 0xb2e2a19600000000, - 0xecccae9f00000000, 0x46c9746e00000000, 0xf9c16ba700000000, - 0x53c4b15600000000, 0xc6d624ee00000000, 0x6cd3fe1f00000000, - 0xd3dbe1d600000000, 0x79de3b2700000000, 0xb8f8ba7c00000000, - 0x12fd608d00000000, 0xadf57f4400000000, 0x07f0a5b500000000, - 0x92e2300d00000000, 0x38e7eafc00000000, 0x87eff53500000000, - 0x2dea2fc400000000, 0x05a2f78200000000, 0xafa72d7300000000, - 0x10af32ba00000000, 0xbaaae84b00000000, 0x2fb87df300000000, - 0x85bda70200000000, 0x3ab5b8cb00000000, 0x90b0623a00000000, - 0x5196e36100000000, 0xfb93399000000000, 0x449b265900000000, - 0xee9efca800000000, 0x7b8c691000000000, 0xd189b3e100000000, - 0x6e81ac2800000000, 0xc48476d900000000, 0x3e111ca500000000, - 0x9414c65400000000, 0x2b1cd99d00000000, 0x8119036c00000000, - 0x140b96d400000000, 0xbe0e4c2500000000, 0x010653ec00000000, - 0xab03891d00000000, 0x6a25084600000000, 0xc020d2b700000000, - 0x7f28cd7e00000000, 0xd52d178f00000000, 0x403f823700000000, - 0xea3a58c600000000, 0x5532470f00000000, 0xff379dfe00000000, - 0xd77f45b800000000, 0x7d7a9f4900000000, 0xc272808000000000, - 0x68775a7100000000, 0xfd65cfc900000000, 0x5760153800000000, - 0xe8680af100000000, 0x426dd00000000000, 0x834b515b00000000, - 0x294e8baa00000000, 0x9646946300000000, 0x3c434e9200000000, - 0xa951db2a00000000, 0x035401db00000000, 0xbc5c1e1200000000, - 0x1659c4e300000000}}; - -#else /* W == 4 */ - -local const z_crc_t FAR crc_braid_table[][256] = { - {0x00000000, 0xae689191, 0x87a02563, 0x29c8b4f2, 0xd4314c87, - 0x7a59dd16, 0x539169e4, 0xfdf9f875, 0x73139f4f, 0xdd7b0ede, - 0xf4b3ba2c, 0x5adb2bbd, 0xa722d3c8, 0x094a4259, 0x2082f6ab, - 0x8eea673a, 0xe6273e9e, 0x484faf0f, 0x61871bfd, 0xcfef8a6c, - 0x32167219, 0x9c7ee388, 0xb5b6577a, 0x1bdec6eb, 0x9534a1d1, - 0x3b5c3040, 0x129484b2, 0xbcfc1523, 0x4105ed56, 0xef6d7cc7, - 0xc6a5c835, 0x68cd59a4, 0x173f7b7d, 0xb957eaec, 0x909f5e1e, - 0x3ef7cf8f, 0xc30e37fa, 0x6d66a66b, 0x44ae1299, 0xeac68308, - 0x642ce432, 0xca4475a3, 0xe38cc151, 0x4de450c0, 0xb01da8b5, - 0x1e753924, 0x37bd8dd6, 0x99d51c47, 0xf11845e3, 0x5f70d472, - 0x76b86080, 0xd8d0f111, 0x25290964, 0x8b4198f5, 0xa2892c07, - 0x0ce1bd96, 0x820bdaac, 0x2c634b3d, 0x05abffcf, 0xabc36e5e, - 0x563a962b, 0xf85207ba, 0xd19ab348, 0x7ff222d9, 0x2e7ef6fa, - 0x8016676b, 0xa9ded399, 0x07b64208, 0xfa4fba7d, 0x54272bec, - 0x7def9f1e, 0xd3870e8f, 0x5d6d69b5, 0xf305f824, 0xdacd4cd6, - 0x74a5dd47, 0x895c2532, 0x2734b4a3, 0x0efc0051, 0xa09491c0, - 0xc859c864, 0x663159f5, 0x4ff9ed07, 0xe1917c96, 0x1c6884e3, - 0xb2001572, 0x9bc8a180, 0x35a03011, 0xbb4a572b, 0x1522c6ba, - 0x3cea7248, 0x9282e3d9, 0x6f7b1bac, 0xc1138a3d, 0xe8db3ecf, - 0x46b3af5e, 0x39418d87, 0x97291c16, 0xbee1a8e4, 0x10893975, - 0xed70c100, 0x43185091, 0x6ad0e463, 0xc4b875f2, 0x4a5212c8, - 0xe43a8359, 0xcdf237ab, 0x639aa63a, 0x9e635e4f, 0x300bcfde, - 0x19c37b2c, 0xb7abeabd, 0xdf66b319, 0x710e2288, 0x58c6967a, - 0xf6ae07eb, 0x0b57ff9e, 0xa53f6e0f, 0x8cf7dafd, 0x229f4b6c, - 0xac752c56, 0x021dbdc7, 0x2bd50935, 0x85bd98a4, 0x784460d1, - 0xd62cf140, 0xffe445b2, 0x518cd423, 0x5cfdedf4, 0xf2957c65, - 0xdb5dc897, 0x75355906, 0x88cca173, 0x26a430e2, 0x0f6c8410, - 0xa1041581, 0x2fee72bb, 0x8186e32a, 0xa84e57d8, 0x0626c649, - 0xfbdf3e3c, 0x55b7afad, 0x7c7f1b5f, 0xd2178ace, 0xbadad36a, - 0x14b242fb, 0x3d7af609, 0x93126798, 0x6eeb9fed, 0xc0830e7c, - 0xe94bba8e, 0x47232b1f, 0xc9c94c25, 0x67a1ddb4, 0x4e696946, - 0xe001f8d7, 0x1df800a2, 0xb3909133, 0x9a5825c1, 0x3430b450, - 0x4bc29689, 0xe5aa0718, 0xcc62b3ea, 0x620a227b, 0x9ff3da0e, - 0x319b4b9f, 0x1853ff6d, 0xb63b6efc, 0x38d109c6, 0x96b99857, - 0xbf712ca5, 0x1119bd34, 0xece04541, 0x4288d4d0, 0x6b406022, - 0xc528f1b3, 0xade5a817, 0x038d3986, 0x2a458d74, 0x842d1ce5, - 0x79d4e490, 0xd7bc7501, 0xfe74c1f3, 0x501c5062, 0xdef63758, - 0x709ea6c9, 0x5956123b, 0xf73e83aa, 0x0ac77bdf, 0xa4afea4e, - 0x8d675ebc, 0x230fcf2d, 0x72831b0e, 0xdceb8a9f, 0xf5233e6d, - 0x5b4baffc, 0xa6b25789, 0x08dac618, 0x211272ea, 0x8f7ae37b, - 0x01908441, 0xaff815d0, 0x8630a122, 0x285830b3, 0xd5a1c8c6, - 0x7bc95957, 0x5201eda5, 0xfc697c34, 0x94a42590, 0x3accb401, - 0x130400f3, 0xbd6c9162, 0x40956917, 0xeefdf886, 0xc7354c74, - 0x695ddde5, 0xe7b7badf, 0x49df2b4e, 0x60179fbc, 0xce7f0e2d, - 0x3386f658, 0x9dee67c9, 0xb426d33b, 0x1a4e42aa, 0x65bc6073, - 0xcbd4f1e2, 0xe21c4510, 0x4c74d481, 0xb18d2cf4, 0x1fe5bd65, - 0x362d0997, 0x98459806, 0x16afff3c, 0xb8c76ead, 0x910fda5f, - 0x3f674bce, 0xc29eb3bb, 0x6cf6222a, 0x453e96d8, 0xeb560749, - 0x839b5eed, 0x2df3cf7c, 0x043b7b8e, 0xaa53ea1f, 0x57aa126a, - 0xf9c283fb, 0xd00a3709, 0x7e62a698, 0xf088c1a2, 0x5ee05033, - 0x7728e4c1, 0xd9407550, 0x24b98d25, 0x8ad11cb4, 0xa319a846, - 0x0d7139d7}, - {0x00000000, 0xb9fbdbe8, 0xa886b191, 0x117d6a79, 0x8a7c6563, - 0x3387be8b, 0x22fad4f2, 0x9b010f1a, 0xcf89cc87, 0x7672176f, - 0x670f7d16, 0xdef4a6fe, 0x45f5a9e4, 0xfc0e720c, 0xed731875, - 0x5488c39d, 0x44629f4f, 0xfd9944a7, 0xece42ede, 0x551ff536, - 0xce1efa2c, 0x77e521c4, 0x66984bbd, 0xdf639055, 0x8beb53c8, - 0x32108820, 0x236de259, 0x9a9639b1, 0x019736ab, 0xb86ced43, - 0xa911873a, 0x10ea5cd2, 0x88c53e9e, 0x313ee576, 0x20438f0f, - 0x99b854e7, 0x02b95bfd, 0xbb428015, 0xaa3fea6c, 0x13c43184, - 0x474cf219, 0xfeb729f1, 0xefca4388, 0x56319860, 0xcd30977a, - 0x74cb4c92, 0x65b626eb, 0xdc4dfd03, 0xcca7a1d1, 0x755c7a39, - 0x64211040, 0xdddacba8, 0x46dbc4b2, 0xff201f5a, 0xee5d7523, - 0x57a6aecb, 0x032e6d56, 0xbad5b6be, 0xaba8dcc7, 0x1253072f, - 0x89520835, 0x30a9d3dd, 0x21d4b9a4, 0x982f624c, 0xcafb7b7d, - 0x7300a095, 0x627dcaec, 0xdb861104, 0x40871e1e, 0xf97cc5f6, - 0xe801af8f, 0x51fa7467, 0x0572b7fa, 0xbc896c12, 0xadf4066b, - 0x140fdd83, 0x8f0ed299, 0x36f50971, 0x27886308, 0x9e73b8e0, - 0x8e99e432, 0x37623fda, 0x261f55a3, 0x9fe48e4b, 0x04e58151, - 0xbd1e5ab9, 0xac6330c0, 0x1598eb28, 0x411028b5, 0xf8ebf35d, - 0xe9969924, 0x506d42cc, 0xcb6c4dd6, 0x7297963e, 0x63eafc47, - 0xda1127af, 0x423e45e3, 0xfbc59e0b, 0xeab8f472, 0x53432f9a, - 0xc8422080, 0x71b9fb68, 0x60c49111, 0xd93f4af9, 0x8db78964, - 0x344c528c, 0x253138f5, 0x9ccae31d, 0x07cbec07, 0xbe3037ef, - 0xaf4d5d96, 0x16b6867e, 0x065cdaac, 0xbfa70144, 0xaeda6b3d, - 0x1721b0d5, 0x8c20bfcf, 0x35db6427, 0x24a60e5e, 0x9d5dd5b6, - 0xc9d5162b, 0x702ecdc3, 0x6153a7ba, 0xd8a87c52, 0x43a97348, - 0xfa52a8a0, 0xeb2fc2d9, 0x52d41931, 0x4e87f0bb, 0xf77c2b53, - 0xe601412a, 0x5ffa9ac2, 0xc4fb95d8, 0x7d004e30, 0x6c7d2449, - 0xd586ffa1, 0x810e3c3c, 0x38f5e7d4, 0x29888dad, 0x90735645, - 0x0b72595f, 0xb28982b7, 0xa3f4e8ce, 0x1a0f3326, 0x0ae56ff4, - 0xb31eb41c, 0xa263de65, 0x1b98058d, 0x80990a97, 0x3962d17f, - 0x281fbb06, 0x91e460ee, 0xc56ca373, 0x7c97789b, 0x6dea12e2, - 0xd411c90a, 0x4f10c610, 0xf6eb1df8, 0xe7967781, 0x5e6dac69, - 0xc642ce25, 0x7fb915cd, 0x6ec47fb4, 0xd73fa45c, 0x4c3eab46, - 0xf5c570ae, 0xe4b81ad7, 0x5d43c13f, 0x09cb02a2, 0xb030d94a, - 0xa14db333, 0x18b668db, 0x83b767c1, 0x3a4cbc29, 0x2b31d650, - 0x92ca0db8, 0x8220516a, 0x3bdb8a82, 0x2aa6e0fb, 0x935d3b13, - 0x085c3409, 0xb1a7efe1, 0xa0da8598, 0x19215e70, 0x4da99ded, - 0xf4524605, 0xe52f2c7c, 0x5cd4f794, 0xc7d5f88e, 0x7e2e2366, - 0x6f53491f, 0xd6a892f7, 0x847c8bc6, 0x3d87502e, 0x2cfa3a57, - 0x9501e1bf, 0x0e00eea5, 0xb7fb354d, 0xa6865f34, 0x1f7d84dc, - 0x4bf54741, 0xf20e9ca9, 0xe373f6d0, 0x5a882d38, 0xc1892222, - 0x7872f9ca, 0x690f93b3, 0xd0f4485b, 0xc01e1489, 0x79e5cf61, - 0x6898a518, 0xd1637ef0, 0x4a6271ea, 0xf399aa02, 0xe2e4c07b, - 0x5b1f1b93, 0x0f97d80e, 0xb66c03e6, 0xa711699f, 0x1eeab277, - 0x85ebbd6d, 0x3c106685, 0x2d6d0cfc, 0x9496d714, 0x0cb9b558, - 0xb5426eb0, 0xa43f04c9, 0x1dc4df21, 0x86c5d03b, 0x3f3e0bd3, - 0x2e4361aa, 0x97b8ba42, 0xc33079df, 0x7acba237, 0x6bb6c84e, - 0xd24d13a6, 0x494c1cbc, 0xf0b7c754, 0xe1caad2d, 0x583176c5, - 0x48db2a17, 0xf120f1ff, 0xe05d9b86, 0x59a6406e, 0xc2a74f74, - 0x7b5c949c, 0x6a21fee5, 0xd3da250d, 0x8752e690, 0x3ea93d78, - 0x2fd45701, 0x962f8ce9, 0x0d2e83f3, 0xb4d5581b, 0xa5a83262, - 0x1c53e98a}, - {0x00000000, 0x9d0fe176, 0xe16ec4ad, 0x7c6125db, 0x19ac8f1b, - 0x84a36e6d, 0xf8c24bb6, 0x65cdaac0, 0x33591e36, 0xae56ff40, - 0xd237da9b, 0x4f383bed, 0x2af5912d, 0xb7fa705b, 0xcb9b5580, - 0x5694b4f6, 0x66b23c6c, 0xfbbddd1a, 0x87dcf8c1, 0x1ad319b7, - 0x7f1eb377, 0xe2115201, 0x9e7077da, 0x037f96ac, 0x55eb225a, - 0xc8e4c32c, 0xb485e6f7, 0x298a0781, 0x4c47ad41, 0xd1484c37, - 0xad2969ec, 0x3026889a, 0xcd6478d8, 0x506b99ae, 0x2c0abc75, - 0xb1055d03, 0xd4c8f7c3, 0x49c716b5, 0x35a6336e, 0xa8a9d218, - 0xfe3d66ee, 0x63328798, 0x1f53a243, 0x825c4335, 0xe791e9f5, - 0x7a9e0883, 0x06ff2d58, 0x9bf0cc2e, 0xabd644b4, 0x36d9a5c2, - 0x4ab88019, 0xd7b7616f, 0xb27acbaf, 0x2f752ad9, 0x53140f02, - 0xce1bee74, 0x988f5a82, 0x0580bbf4, 0x79e19e2f, 0xe4ee7f59, - 0x8123d599, 0x1c2c34ef, 0x604d1134, 0xfd42f042, 0x41b9f7f1, - 0xdcb61687, 0xa0d7335c, 0x3dd8d22a, 0x581578ea, 0xc51a999c, - 0xb97bbc47, 0x24745d31, 0x72e0e9c7, 0xefef08b1, 0x938e2d6a, - 0x0e81cc1c, 0x6b4c66dc, 0xf64387aa, 0x8a22a271, 0x172d4307, - 0x270bcb9d, 0xba042aeb, 0xc6650f30, 0x5b6aee46, 0x3ea74486, - 0xa3a8a5f0, 0xdfc9802b, 0x42c6615d, 0x1452d5ab, 0x895d34dd, - 0xf53c1106, 0x6833f070, 0x0dfe5ab0, 0x90f1bbc6, 0xec909e1d, - 0x719f7f6b, 0x8cdd8f29, 0x11d26e5f, 0x6db34b84, 0xf0bcaaf2, - 0x95710032, 0x087ee144, 0x741fc49f, 0xe91025e9, 0xbf84911f, - 0x228b7069, 0x5eea55b2, 0xc3e5b4c4, 0xa6281e04, 0x3b27ff72, - 0x4746daa9, 0xda493bdf, 0xea6fb345, 0x77605233, 0x0b0177e8, - 0x960e969e, 0xf3c33c5e, 0x6eccdd28, 0x12adf8f3, 0x8fa21985, - 0xd936ad73, 0x44394c05, 0x385869de, 0xa55788a8, 0xc09a2268, - 0x5d95c31e, 0x21f4e6c5, 0xbcfb07b3, 0x8373efe2, 0x1e7c0e94, - 0x621d2b4f, 0xff12ca39, 0x9adf60f9, 0x07d0818f, 0x7bb1a454, - 0xe6be4522, 0xb02af1d4, 0x2d2510a2, 0x51443579, 0xcc4bd40f, - 0xa9867ecf, 0x34899fb9, 0x48e8ba62, 0xd5e75b14, 0xe5c1d38e, - 0x78ce32f8, 0x04af1723, 0x99a0f655, 0xfc6d5c95, 0x6162bde3, - 0x1d039838, 0x800c794e, 0xd698cdb8, 0x4b972cce, 0x37f60915, - 0xaaf9e863, 0xcf3442a3, 0x523ba3d5, 0x2e5a860e, 0xb3556778, - 0x4e17973a, 0xd318764c, 0xaf795397, 0x3276b2e1, 0x57bb1821, - 0xcab4f957, 0xb6d5dc8c, 0x2bda3dfa, 0x7d4e890c, 0xe041687a, - 0x9c204da1, 0x012facd7, 0x64e20617, 0xf9ede761, 0x858cc2ba, - 0x188323cc, 0x28a5ab56, 0xb5aa4a20, 0xc9cb6ffb, 0x54c48e8d, - 0x3109244d, 0xac06c53b, 0xd067e0e0, 0x4d680196, 0x1bfcb560, - 0x86f35416, 0xfa9271cd, 0x679d90bb, 0x02503a7b, 0x9f5fdb0d, - 0xe33efed6, 0x7e311fa0, 0xc2ca1813, 0x5fc5f965, 0x23a4dcbe, - 0xbeab3dc8, 0xdb669708, 0x4669767e, 0x3a0853a5, 0xa707b2d3, - 0xf1930625, 0x6c9ce753, 0x10fdc288, 0x8df223fe, 0xe83f893e, - 0x75306848, 0x09514d93, 0x945eace5, 0xa478247f, 0x3977c509, - 0x4516e0d2, 0xd81901a4, 0xbdd4ab64, 0x20db4a12, 0x5cba6fc9, - 0xc1b58ebf, 0x97213a49, 0x0a2edb3f, 0x764ffee4, 0xeb401f92, - 0x8e8db552, 0x13825424, 0x6fe371ff, 0xf2ec9089, 0x0fae60cb, - 0x92a181bd, 0xeec0a466, 0x73cf4510, 0x1602efd0, 0x8b0d0ea6, - 0xf76c2b7d, 0x6a63ca0b, 0x3cf77efd, 0xa1f89f8b, 0xdd99ba50, - 0x40965b26, 0x255bf1e6, 0xb8541090, 0xc435354b, 0x593ad43d, - 0x691c5ca7, 0xf413bdd1, 0x8872980a, 0x157d797c, 0x70b0d3bc, - 0xedbf32ca, 0x91de1711, 0x0cd1f667, 0x5a454291, 0xc74aa3e7, - 0xbb2b863c, 0x2624674a, 0x43e9cd8a, 0xdee62cfc, 0xa2870927, - 0x3f88e851}, - {0x00000000, 0xdd96d985, 0x605cb54b, 0xbdca6cce, 0xc0b96a96, - 0x1d2fb313, 0xa0e5dfdd, 0x7d730658, 0x5a03d36d, 0x87950ae8, - 0x3a5f6626, 0xe7c9bfa3, 0x9abab9fb, 0x472c607e, 0xfae60cb0, - 0x2770d535, 0xb407a6da, 0x69917f5f, 0xd45b1391, 0x09cdca14, - 0x74becc4c, 0xa92815c9, 0x14e27907, 0xc974a082, 0xee0475b7, - 0x3392ac32, 0x8e58c0fc, 0x53ce1979, 0x2ebd1f21, 0xf32bc6a4, - 0x4ee1aa6a, 0x937773ef, 0xb37e4bf5, 0x6ee89270, 0xd322febe, - 0x0eb4273b, 0x73c72163, 0xae51f8e6, 0x139b9428, 0xce0d4dad, - 0xe97d9898, 0x34eb411d, 0x89212dd3, 0x54b7f456, 0x29c4f20e, - 0xf4522b8b, 0x49984745, 0x940e9ec0, 0x0779ed2f, 0xdaef34aa, - 0x67255864, 0xbab381e1, 0xc7c087b9, 0x1a565e3c, 0xa79c32f2, - 0x7a0aeb77, 0x5d7a3e42, 0x80ece7c7, 0x3d268b09, 0xe0b0528c, - 0x9dc354d4, 0x40558d51, 0xfd9fe19f, 0x2009381a, 0xbd8d91ab, - 0x601b482e, 0xddd124e0, 0x0047fd65, 0x7d34fb3d, 0xa0a222b8, - 0x1d684e76, 0xc0fe97f3, 0xe78e42c6, 0x3a189b43, 0x87d2f78d, - 0x5a442e08, 0x27372850, 0xfaa1f1d5, 0x476b9d1b, 0x9afd449e, - 0x098a3771, 0xd41ceef4, 0x69d6823a, 0xb4405bbf, 0xc9335de7, - 0x14a58462, 0xa96fe8ac, 0x74f93129, 0x5389e41c, 0x8e1f3d99, - 0x33d55157, 0xee4388d2, 0x93308e8a, 0x4ea6570f, 0xf36c3bc1, - 0x2efae244, 0x0ef3da5e, 0xd36503db, 0x6eaf6f15, 0xb339b690, - 0xce4ab0c8, 0x13dc694d, 0xae160583, 0x7380dc06, 0x54f00933, - 0x8966d0b6, 0x34acbc78, 0xe93a65fd, 0x944963a5, 0x49dfba20, - 0xf415d6ee, 0x29830f6b, 0xbaf47c84, 0x6762a501, 0xdaa8c9cf, - 0x073e104a, 0x7a4d1612, 0xa7dbcf97, 0x1a11a359, 0xc7877adc, - 0xe0f7afe9, 0x3d61766c, 0x80ab1aa2, 0x5d3dc327, 0x204ec57f, - 0xfdd81cfa, 0x40127034, 0x9d84a9b1, 0xa06a2517, 0x7dfcfc92, - 0xc036905c, 0x1da049d9, 0x60d34f81, 0xbd459604, 0x008ffaca, - 0xdd19234f, 0xfa69f67a, 0x27ff2fff, 0x9a354331, 0x47a39ab4, - 0x3ad09cec, 0xe7464569, 0x5a8c29a7, 0x871af022, 0x146d83cd, - 0xc9fb5a48, 0x74313686, 0xa9a7ef03, 0xd4d4e95b, 0x094230de, - 0xb4885c10, 0x691e8595, 0x4e6e50a0, 0x93f88925, 0x2e32e5eb, - 0xf3a43c6e, 0x8ed73a36, 0x5341e3b3, 0xee8b8f7d, 0x331d56f8, - 0x13146ee2, 0xce82b767, 0x7348dba9, 0xaede022c, 0xd3ad0474, - 0x0e3bddf1, 0xb3f1b13f, 0x6e6768ba, 0x4917bd8f, 0x9481640a, - 0x294b08c4, 0xf4ddd141, 0x89aed719, 0x54380e9c, 0xe9f26252, - 0x3464bbd7, 0xa713c838, 0x7a8511bd, 0xc74f7d73, 0x1ad9a4f6, - 0x67aaa2ae, 0xba3c7b2b, 0x07f617e5, 0xda60ce60, 0xfd101b55, - 0x2086c2d0, 0x9d4cae1e, 0x40da779b, 0x3da971c3, 0xe03fa846, - 0x5df5c488, 0x80631d0d, 0x1de7b4bc, 0xc0716d39, 0x7dbb01f7, - 0xa02dd872, 0xdd5ede2a, 0x00c807af, 0xbd026b61, 0x6094b2e4, - 0x47e467d1, 0x9a72be54, 0x27b8d29a, 0xfa2e0b1f, 0x875d0d47, - 0x5acbd4c2, 0xe701b80c, 0x3a976189, 0xa9e01266, 0x7476cbe3, - 0xc9bca72d, 0x142a7ea8, 0x695978f0, 0xb4cfa175, 0x0905cdbb, - 0xd493143e, 0xf3e3c10b, 0x2e75188e, 0x93bf7440, 0x4e29adc5, - 0x335aab9d, 0xeecc7218, 0x53061ed6, 0x8e90c753, 0xae99ff49, - 0x730f26cc, 0xcec54a02, 0x13539387, 0x6e2095df, 0xb3b64c5a, - 0x0e7c2094, 0xd3eaf911, 0xf49a2c24, 0x290cf5a1, 0x94c6996f, - 0x495040ea, 0x342346b2, 0xe9b59f37, 0x547ff3f9, 0x89e92a7c, - 0x1a9e5993, 0xc7088016, 0x7ac2ecd8, 0xa754355d, 0xda273305, - 0x07b1ea80, 0xba7b864e, 0x67ed5fcb, 0x409d8afe, 0x9d0b537b, - 0x20c13fb5, 0xfd57e630, 0x8024e068, 0x5db239ed, 0xe0785523, - 0x3dee8ca6}}; - -local const z_word_t FAR crc_braid_big_table[][256] = { - {0x00000000, 0x85d996dd, 0x4bb55c60, 0xce6ccabd, 0x966ab9c0, - 0x13b32f1d, 0xdddfe5a0, 0x5806737d, 0x6dd3035a, 0xe80a9587, - 0x26665f3a, 0xa3bfc9e7, 0xfbb9ba9a, 0x7e602c47, 0xb00ce6fa, - 0x35d57027, 0xdaa607b4, 0x5f7f9169, 0x91135bd4, 0x14cacd09, - 0x4cccbe74, 0xc91528a9, 0x0779e214, 0x82a074c9, 0xb77504ee, - 0x32ac9233, 0xfcc0588e, 0x7919ce53, 0x211fbd2e, 0xa4c62bf3, - 0x6aaae14e, 0xef737793, 0xf54b7eb3, 0x7092e86e, 0xbefe22d3, - 0x3b27b40e, 0x6321c773, 0xe6f851ae, 0x28949b13, 0xad4d0dce, - 0x98987de9, 0x1d41eb34, 0xd32d2189, 0x56f4b754, 0x0ef2c429, - 0x8b2b52f4, 0x45479849, 0xc09e0e94, 0x2fed7907, 0xaa34efda, - 0x64582567, 0xe181b3ba, 0xb987c0c7, 0x3c5e561a, 0xf2329ca7, - 0x77eb0a7a, 0x423e7a5d, 0xc7e7ec80, 0x098b263d, 0x8c52b0e0, - 0xd454c39d, 0x518d5540, 0x9fe19ffd, 0x1a380920, 0xab918dbd, - 0x2e481b60, 0xe024d1dd, 0x65fd4700, 0x3dfb347d, 0xb822a2a0, - 0x764e681d, 0xf397fec0, 0xc6428ee7, 0x439b183a, 0x8df7d287, - 0x082e445a, 0x50283727, 0xd5f1a1fa, 0x1b9d6b47, 0x9e44fd9a, - 0x71378a09, 0xf4ee1cd4, 0x3a82d669, 0xbf5b40b4, 0xe75d33c9, - 0x6284a514, 0xace86fa9, 0x2931f974, 0x1ce48953, 0x993d1f8e, - 0x5751d533, 0xd28843ee, 0x8a8e3093, 0x0f57a64e, 0xc13b6cf3, - 0x44e2fa2e, 0x5edaf30e, 0xdb0365d3, 0x156faf6e, 0x90b639b3, - 0xc8b04ace, 0x4d69dc13, 0x830516ae, 0x06dc8073, 0x3309f054, - 0xb6d06689, 0x78bcac34, 0xfd653ae9, 0xa5634994, 0x20badf49, - 0xeed615f4, 0x6b0f8329, 0x847cf4ba, 0x01a56267, 0xcfc9a8da, - 0x4a103e07, 0x12164d7a, 0x97cfdba7, 0x59a3111a, 0xdc7a87c7, - 0xe9aff7e0, 0x6c76613d, 0xa21aab80, 0x27c33d5d, 0x7fc54e20, - 0xfa1cd8fd, 0x34701240, 0xb1a9849d, 0x17256aa0, 0x92fcfc7d, - 0x5c9036c0, 0xd949a01d, 0x814fd360, 0x049645bd, 0xcafa8f00, - 0x4f2319dd, 0x7af669fa, 0xff2fff27, 0x3143359a, 0xb49aa347, - 0xec9cd03a, 0x694546e7, 0xa7298c5a, 0x22f01a87, 0xcd836d14, - 0x485afbc9, 0x86363174, 0x03efa7a9, 0x5be9d4d4, 0xde304209, - 0x105c88b4, 0x95851e69, 0xa0506e4e, 0x2589f893, 0xebe5322e, - 0x6e3ca4f3, 0x363ad78e, 0xb3e34153, 0x7d8f8bee, 0xf8561d33, - 0xe26e1413, 0x67b782ce, 0xa9db4873, 0x2c02deae, 0x7404add3, - 0xf1dd3b0e, 0x3fb1f1b3, 0xba68676e, 0x8fbd1749, 0x0a648194, - 0xc4084b29, 0x41d1ddf4, 0x19d7ae89, 0x9c0e3854, 0x5262f2e9, - 0xd7bb6434, 0x38c813a7, 0xbd11857a, 0x737d4fc7, 0xf6a4d91a, - 0xaea2aa67, 0x2b7b3cba, 0xe517f607, 0x60ce60da, 0x551b10fd, - 0xd0c28620, 0x1eae4c9d, 0x9b77da40, 0xc371a93d, 0x46a83fe0, - 0x88c4f55d, 0x0d1d6380, 0xbcb4e71d, 0x396d71c0, 0xf701bb7d, - 0x72d82da0, 0x2ade5edd, 0xaf07c800, 0x616b02bd, 0xe4b29460, - 0xd167e447, 0x54be729a, 0x9ad2b827, 0x1f0b2efa, 0x470d5d87, - 0xc2d4cb5a, 0x0cb801e7, 0x8961973a, 0x6612e0a9, 0xe3cb7674, - 0x2da7bcc9, 0xa87e2a14, 0xf0785969, 0x75a1cfb4, 0xbbcd0509, - 0x3e1493d4, 0x0bc1e3f3, 0x8e18752e, 0x4074bf93, 0xc5ad294e, - 0x9dab5a33, 0x1872ccee, 0xd61e0653, 0x53c7908e, 0x49ff99ae, - 0xcc260f73, 0x024ac5ce, 0x87935313, 0xdf95206e, 0x5a4cb6b3, - 0x94207c0e, 0x11f9ead3, 0x242c9af4, 0xa1f50c29, 0x6f99c694, - 0xea405049, 0xb2462334, 0x379fb5e9, 0xf9f37f54, 0x7c2ae989, - 0x93599e1a, 0x168008c7, 0xd8ecc27a, 0x5d3554a7, 0x053327da, - 0x80eab107, 0x4e867bba, 0xcb5fed67, 0xfe8a9d40, 0x7b530b9d, - 0xb53fc120, 0x30e657fd, 0x68e02480, 0xed39b25d, 0x235578e0, - 0xa68cee3d}, - {0x00000000, 0x76e10f9d, 0xadc46ee1, 0xdb25617c, 0x1b8fac19, - 0x6d6ea384, 0xb64bc2f8, 0xc0aacd65, 0x361e5933, 0x40ff56ae, - 0x9bda37d2, 0xed3b384f, 0x2d91f52a, 0x5b70fab7, 0x80559bcb, - 0xf6b49456, 0x6c3cb266, 0x1addbdfb, 0xc1f8dc87, 0xb719d31a, - 0x77b31e7f, 0x015211e2, 0xda77709e, 0xac967f03, 0x5a22eb55, - 0x2cc3e4c8, 0xf7e685b4, 0x81078a29, 0x41ad474c, 0x374c48d1, - 0xec6929ad, 0x9a882630, 0xd87864cd, 0xae996b50, 0x75bc0a2c, - 0x035d05b1, 0xc3f7c8d4, 0xb516c749, 0x6e33a635, 0x18d2a9a8, - 0xee663dfe, 0x98873263, 0x43a2531f, 0x35435c82, 0xf5e991e7, - 0x83089e7a, 0x582dff06, 0x2eccf09b, 0xb444d6ab, 0xc2a5d936, - 0x1980b84a, 0x6f61b7d7, 0xafcb7ab2, 0xd92a752f, 0x020f1453, - 0x74ee1bce, 0x825a8f98, 0xf4bb8005, 0x2f9ee179, 0x597feee4, - 0x99d52381, 0xef342c1c, 0x34114d60, 0x42f042fd, 0xf1f7b941, - 0x8716b6dc, 0x5c33d7a0, 0x2ad2d83d, 0xea781558, 0x9c991ac5, - 0x47bc7bb9, 0x315d7424, 0xc7e9e072, 0xb108efef, 0x6a2d8e93, - 0x1ccc810e, 0xdc664c6b, 0xaa8743f6, 0x71a2228a, 0x07432d17, - 0x9dcb0b27, 0xeb2a04ba, 0x300f65c6, 0x46ee6a5b, 0x8644a73e, - 0xf0a5a8a3, 0x2b80c9df, 0x5d61c642, 0xabd55214, 0xdd345d89, - 0x06113cf5, 0x70f03368, 0xb05afe0d, 0xc6bbf190, 0x1d9e90ec, - 0x6b7f9f71, 0x298fdd8c, 0x5f6ed211, 0x844bb36d, 0xf2aabcf0, - 0x32007195, 0x44e17e08, 0x9fc41f74, 0xe92510e9, 0x1f9184bf, - 0x69708b22, 0xb255ea5e, 0xc4b4e5c3, 0x041e28a6, 0x72ff273b, - 0xa9da4647, 0xdf3b49da, 0x45b36fea, 0x33526077, 0xe877010b, - 0x9e960e96, 0x5e3cc3f3, 0x28ddcc6e, 0xf3f8ad12, 0x8519a28f, - 0x73ad36d9, 0x054c3944, 0xde695838, 0xa88857a5, 0x68229ac0, - 0x1ec3955d, 0xc5e6f421, 0xb307fbbc, 0xe2ef7383, 0x940e7c1e, - 0x4f2b1d62, 0x39ca12ff, 0xf960df9a, 0x8f81d007, 0x54a4b17b, - 0x2245bee6, 0xd4f12ab0, 0xa210252d, 0x79354451, 0x0fd44bcc, - 0xcf7e86a9, 0xb99f8934, 0x62bae848, 0x145be7d5, 0x8ed3c1e5, - 0xf832ce78, 0x2317af04, 0x55f6a099, 0x955c6dfc, 0xe3bd6261, - 0x3898031d, 0x4e790c80, 0xb8cd98d6, 0xce2c974b, 0x1509f637, - 0x63e8f9aa, 0xa34234cf, 0xd5a33b52, 0x0e865a2e, 0x786755b3, - 0x3a97174e, 0x4c7618d3, 0x975379af, 0xe1b27632, 0x2118bb57, - 0x57f9b4ca, 0x8cdcd5b6, 0xfa3dda2b, 0x0c894e7d, 0x7a6841e0, - 0xa14d209c, 0xd7ac2f01, 0x1706e264, 0x61e7edf9, 0xbac28c85, - 0xcc238318, 0x56aba528, 0x204aaab5, 0xfb6fcbc9, 0x8d8ec454, - 0x4d240931, 0x3bc506ac, 0xe0e067d0, 0x9601684d, 0x60b5fc1b, - 0x1654f386, 0xcd7192fa, 0xbb909d67, 0x7b3a5002, 0x0ddb5f9f, - 0xd6fe3ee3, 0xa01f317e, 0x1318cac2, 0x65f9c55f, 0xbedca423, - 0xc83dabbe, 0x089766db, 0x7e766946, 0xa553083a, 0xd3b207a7, - 0x250693f1, 0x53e79c6c, 0x88c2fd10, 0xfe23f28d, 0x3e893fe8, - 0x48683075, 0x934d5109, 0xe5ac5e94, 0x7f2478a4, 0x09c57739, - 0xd2e01645, 0xa40119d8, 0x64abd4bd, 0x124adb20, 0xc96fba5c, - 0xbf8eb5c1, 0x493a2197, 0x3fdb2e0a, 0xe4fe4f76, 0x921f40eb, - 0x52b58d8e, 0x24548213, 0xff71e36f, 0x8990ecf2, 0xcb60ae0f, - 0xbd81a192, 0x66a4c0ee, 0x1045cf73, 0xd0ef0216, 0xa60e0d8b, - 0x7d2b6cf7, 0x0bca636a, 0xfd7ef73c, 0x8b9ff8a1, 0x50ba99dd, - 0x265b9640, 0xe6f15b25, 0x901054b8, 0x4b3535c4, 0x3dd43a59, - 0xa75c1c69, 0xd1bd13f4, 0x0a987288, 0x7c797d15, 0xbcd3b070, - 0xca32bfed, 0x1117de91, 0x67f6d10c, 0x9142455a, 0xe7a34ac7, - 0x3c862bbb, 0x4a672426, 0x8acde943, 0xfc2ce6de, 0x270987a2, - 0x51e8883f}, - {0x00000000, 0xe8dbfbb9, 0x91b186a8, 0x796a7d11, 0x63657c8a, - 0x8bbe8733, 0xf2d4fa22, 0x1a0f019b, 0x87cc89cf, 0x6f177276, - 0x167d0f67, 0xfea6f4de, 0xe4a9f545, 0x0c720efc, 0x751873ed, - 0x9dc38854, 0x4f9f6244, 0xa74499fd, 0xde2ee4ec, 0x36f51f55, - 0x2cfa1ece, 0xc421e577, 0xbd4b9866, 0x559063df, 0xc853eb8b, - 0x20881032, 0x59e26d23, 0xb139969a, 0xab369701, 0x43ed6cb8, - 0x3a8711a9, 0xd25cea10, 0x9e3ec588, 0x76e53e31, 0x0f8f4320, - 0xe754b899, 0xfd5bb902, 0x158042bb, 0x6cea3faa, 0x8431c413, - 0x19f24c47, 0xf129b7fe, 0x8843caef, 0x60983156, 0x7a9730cd, - 0x924ccb74, 0xeb26b665, 0x03fd4ddc, 0xd1a1a7cc, 0x397a5c75, - 0x40102164, 0xa8cbdadd, 0xb2c4db46, 0x5a1f20ff, 0x23755dee, - 0xcbaea657, 0x566d2e03, 0xbeb6d5ba, 0xc7dca8ab, 0x2f075312, - 0x35085289, 0xddd3a930, 0xa4b9d421, 0x4c622f98, 0x7d7bfbca, - 0x95a00073, 0xecca7d62, 0x041186db, 0x1e1e8740, 0xf6c57cf9, - 0x8faf01e8, 0x6774fa51, 0xfab77205, 0x126c89bc, 0x6b06f4ad, - 0x83dd0f14, 0x99d20e8f, 0x7109f536, 0x08638827, 0xe0b8739e, - 0x32e4998e, 0xda3f6237, 0xa3551f26, 0x4b8ee49f, 0x5181e504, - 0xb95a1ebd, 0xc03063ac, 0x28eb9815, 0xb5281041, 0x5df3ebf8, - 0x249996e9, 0xcc426d50, 0xd64d6ccb, 0x3e969772, 0x47fcea63, - 0xaf2711da, 0xe3453e42, 0x0b9ec5fb, 0x72f4b8ea, 0x9a2f4353, - 0x802042c8, 0x68fbb971, 0x1191c460, 0xf94a3fd9, 0x6489b78d, - 0x8c524c34, 0xf5383125, 0x1de3ca9c, 0x07eccb07, 0xef3730be, - 0x965d4daf, 0x7e86b616, 0xacda5c06, 0x4401a7bf, 0x3d6bdaae, - 0xd5b02117, 0xcfbf208c, 0x2764db35, 0x5e0ea624, 0xb6d55d9d, - 0x2b16d5c9, 0xc3cd2e70, 0xbaa75361, 0x527ca8d8, 0x4873a943, - 0xa0a852fa, 0xd9c22feb, 0x3119d452, 0xbbf0874e, 0x532b7cf7, - 0x2a4101e6, 0xc29afa5f, 0xd895fbc4, 0x304e007d, 0x49247d6c, - 0xa1ff86d5, 0x3c3c0e81, 0xd4e7f538, 0xad8d8829, 0x45567390, - 0x5f59720b, 0xb78289b2, 0xcee8f4a3, 0x26330f1a, 0xf46fe50a, - 0x1cb41eb3, 0x65de63a2, 0x8d05981b, 0x970a9980, 0x7fd16239, - 0x06bb1f28, 0xee60e491, 0x73a36cc5, 0x9b78977c, 0xe212ea6d, - 0x0ac911d4, 0x10c6104f, 0xf81debf6, 0x817796e7, 0x69ac6d5e, - 0x25ce42c6, 0xcd15b97f, 0xb47fc46e, 0x5ca43fd7, 0x46ab3e4c, - 0xae70c5f5, 0xd71ab8e4, 0x3fc1435d, 0xa202cb09, 0x4ad930b0, - 0x33b34da1, 0xdb68b618, 0xc167b783, 0x29bc4c3a, 0x50d6312b, - 0xb80dca92, 0x6a512082, 0x828adb3b, 0xfbe0a62a, 0x133b5d93, - 0x09345c08, 0xe1efa7b1, 0x9885daa0, 0x705e2119, 0xed9da94d, - 0x054652f4, 0x7c2c2fe5, 0x94f7d45c, 0x8ef8d5c7, 0x66232e7e, - 0x1f49536f, 0xf792a8d6, 0xc68b7c84, 0x2e50873d, 0x573afa2c, - 0xbfe10195, 0xa5ee000e, 0x4d35fbb7, 0x345f86a6, 0xdc847d1f, - 0x4147f54b, 0xa99c0ef2, 0xd0f673e3, 0x382d885a, 0x222289c1, - 0xcaf97278, 0xb3930f69, 0x5b48f4d0, 0x89141ec0, 0x61cfe579, - 0x18a59868, 0xf07e63d1, 0xea71624a, 0x02aa99f3, 0x7bc0e4e2, - 0x931b1f5b, 0x0ed8970f, 0xe6036cb6, 0x9f6911a7, 0x77b2ea1e, - 0x6dbdeb85, 0x8566103c, 0xfc0c6d2d, 0x14d79694, 0x58b5b90c, - 0xb06e42b5, 0xc9043fa4, 0x21dfc41d, 0x3bd0c586, 0xd30b3e3f, - 0xaa61432e, 0x42bab897, 0xdf7930c3, 0x37a2cb7a, 0x4ec8b66b, - 0xa6134dd2, 0xbc1c4c49, 0x54c7b7f0, 0x2dadcae1, 0xc5763158, - 0x172adb48, 0xfff120f1, 0x869b5de0, 0x6e40a659, 0x744fa7c2, - 0x9c945c7b, 0xe5fe216a, 0x0d25dad3, 0x90e65287, 0x783da93e, - 0x0157d42f, 0xe98c2f96, 0xf3832e0d, 0x1b58d5b4, 0x6232a8a5, - 0x8ae9531c}, - {0x00000000, 0x919168ae, 0x6325a087, 0xf2b4c829, 0x874c31d4, - 0x16dd597a, 0xe4699153, 0x75f8f9fd, 0x4f9f1373, 0xde0e7bdd, - 0x2cbab3f4, 0xbd2bdb5a, 0xc8d322a7, 0x59424a09, 0xabf68220, - 0x3a67ea8e, 0x9e3e27e6, 0x0faf4f48, 0xfd1b8761, 0x6c8aefcf, - 0x19721632, 0x88e37e9c, 0x7a57b6b5, 0xebc6de1b, 0xd1a13495, - 0x40305c3b, 0xb2849412, 0x2315fcbc, 0x56ed0541, 0xc77c6def, - 0x35c8a5c6, 0xa459cd68, 0x7d7b3f17, 0xecea57b9, 0x1e5e9f90, - 0x8fcff73e, 0xfa370ec3, 0x6ba6666d, 0x9912ae44, 0x0883c6ea, - 0x32e42c64, 0xa37544ca, 0x51c18ce3, 0xc050e44d, 0xb5a81db0, - 0x2439751e, 0xd68dbd37, 0x471cd599, 0xe34518f1, 0x72d4705f, - 0x8060b876, 0x11f1d0d8, 0x64092925, 0xf598418b, 0x072c89a2, - 0x96bde10c, 0xacda0b82, 0x3d4b632c, 0xcfffab05, 0x5e6ec3ab, - 0x2b963a56, 0xba0752f8, 0x48b39ad1, 0xd922f27f, 0xfaf67e2e, - 0x6b671680, 0x99d3dea9, 0x0842b607, 0x7dba4ffa, 0xec2b2754, - 0x1e9fef7d, 0x8f0e87d3, 0xb5696d5d, 0x24f805f3, 0xd64ccdda, - 0x47dda574, 0x32255c89, 0xa3b43427, 0x5100fc0e, 0xc09194a0, - 0x64c859c8, 0xf5593166, 0x07edf94f, 0x967c91e1, 0xe384681c, - 0x721500b2, 0x80a1c89b, 0x1130a035, 0x2b574abb, 0xbac62215, - 0x4872ea3c, 0xd9e38292, 0xac1b7b6f, 0x3d8a13c1, 0xcf3edbe8, - 0x5eafb346, 0x878d4139, 0x161c2997, 0xe4a8e1be, 0x75398910, - 0x00c170ed, 0x91501843, 0x63e4d06a, 0xf275b8c4, 0xc812524a, - 0x59833ae4, 0xab37f2cd, 0x3aa69a63, 0x4f5e639e, 0xdecf0b30, - 0x2c7bc319, 0xbdeaabb7, 0x19b366df, 0x88220e71, 0x7a96c658, - 0xeb07aef6, 0x9eff570b, 0x0f6e3fa5, 0xfddaf78c, 0x6c4b9f22, - 0x562c75ac, 0xc7bd1d02, 0x3509d52b, 0xa498bd85, 0xd1604478, - 0x40f12cd6, 0xb245e4ff, 0x23d48c51, 0xf4edfd5c, 0x657c95f2, - 0x97c85ddb, 0x06593575, 0x73a1cc88, 0xe230a426, 0x10846c0f, - 0x811504a1, 0xbb72ee2f, 0x2ae38681, 0xd8574ea8, 0x49c62606, - 0x3c3edffb, 0xadafb755, 0x5f1b7f7c, 0xce8a17d2, 0x6ad3daba, - 0xfb42b214, 0x09f67a3d, 0x98671293, 0xed9feb6e, 0x7c0e83c0, - 0x8eba4be9, 0x1f2b2347, 0x254cc9c9, 0xb4dda167, 0x4669694e, - 0xd7f801e0, 0xa200f81d, 0x339190b3, 0xc125589a, 0x50b43034, - 0x8996c24b, 0x1807aae5, 0xeab362cc, 0x7b220a62, 0x0edaf39f, - 0x9f4b9b31, 0x6dff5318, 0xfc6e3bb6, 0xc609d138, 0x5798b996, - 0xa52c71bf, 0x34bd1911, 0x4145e0ec, 0xd0d48842, 0x2260406b, - 0xb3f128c5, 0x17a8e5ad, 0x86398d03, 0x748d452a, 0xe51c2d84, - 0x90e4d479, 0x0175bcd7, 0xf3c174fe, 0x62501c50, 0x5837f6de, - 0xc9a69e70, 0x3b125659, 0xaa833ef7, 0xdf7bc70a, 0x4eeaafa4, - 0xbc5e678d, 0x2dcf0f23, 0x0e1b8372, 0x9f8aebdc, 0x6d3e23f5, - 0xfcaf4b5b, 0x8957b2a6, 0x18c6da08, 0xea721221, 0x7be37a8f, - 0x41849001, 0xd015f8af, 0x22a13086, 0xb3305828, 0xc6c8a1d5, - 0x5759c97b, 0xa5ed0152, 0x347c69fc, 0x9025a494, 0x01b4cc3a, - 0xf3000413, 0x62916cbd, 0x17699540, 0x86f8fdee, 0x744c35c7, - 0xe5dd5d69, 0xdfbab7e7, 0x4e2bdf49, 0xbc9f1760, 0x2d0e7fce, - 0x58f68633, 0xc967ee9d, 0x3bd326b4, 0xaa424e1a, 0x7360bc65, - 0xe2f1d4cb, 0x10451ce2, 0x81d4744c, 0xf42c8db1, 0x65bde51f, - 0x97092d36, 0x06984598, 0x3cffaf16, 0xad6ec7b8, 0x5fda0f91, - 0xce4b673f, 0xbbb39ec2, 0x2a22f66c, 0xd8963e45, 0x490756eb, - 0xed5e9b83, 0x7ccff32d, 0x8e7b3b04, 0x1fea53aa, 0x6a12aa57, - 0xfb83c2f9, 0x09370ad0, 0x98a6627e, 0xa2c188f0, 0x3350e05e, - 0xc1e42877, 0x507540d9, 0x258db924, 0xb41cd18a, 0x46a819a3, - 0xd739710d}}; - -#endif - -#endif - -#if N == 5 - -#if W == 8 - -local const z_crc_t FAR crc_braid_table[][256] = { - {0x00000000, 0xaf449247, 0x85f822cf, 0x2abcb088, 0xd08143df, - 0x7fc5d198, 0x55796110, 0xfa3df357, 0x7a7381ff, 0xd53713b8, - 0xff8ba330, 0x50cf3177, 0xaaf2c220, 0x05b65067, 0x2f0ae0ef, - 0x804e72a8, 0xf4e703fe, 0x5ba391b9, 0x711f2131, 0xde5bb376, - 0x24664021, 0x8b22d266, 0xa19e62ee, 0x0edaf0a9, 0x8e948201, - 0x21d01046, 0x0b6ca0ce, 0xa4283289, 0x5e15c1de, 0xf1515399, - 0xdbede311, 0x74a97156, 0x32bf01bd, 0x9dfb93fa, 0xb7472372, - 0x1803b135, 0xe23e4262, 0x4d7ad025, 0x67c660ad, 0xc882f2ea, - 0x48cc8042, 0xe7881205, 0xcd34a28d, 0x627030ca, 0x984dc39d, - 0x370951da, 0x1db5e152, 0xb2f17315, 0xc6580243, 0x691c9004, - 0x43a0208c, 0xece4b2cb, 0x16d9419c, 0xb99dd3db, 0x93216353, - 0x3c65f114, 0xbc2b83bc, 0x136f11fb, 0x39d3a173, 0x96973334, - 0x6caac063, 0xc3ee5224, 0xe952e2ac, 0x461670eb, 0x657e037a, - 0xca3a913d, 0xe08621b5, 0x4fc2b3f2, 0xb5ff40a5, 0x1abbd2e2, - 0x3007626a, 0x9f43f02d, 0x1f0d8285, 0xb04910c2, 0x9af5a04a, - 0x35b1320d, 0xcf8cc15a, 0x60c8531d, 0x4a74e395, 0xe53071d2, - 0x91990084, 0x3edd92c3, 0x1461224b, 0xbb25b00c, 0x4118435b, - 0xee5cd11c, 0xc4e06194, 0x6ba4f3d3, 0xebea817b, 0x44ae133c, - 0x6e12a3b4, 0xc15631f3, 0x3b6bc2a4, 0x942f50e3, 0xbe93e06b, - 0x11d7722c, 0x57c102c7, 0xf8859080, 0xd2392008, 0x7d7db24f, - 0x87404118, 0x2804d35f, 0x02b863d7, 0xadfcf190, 0x2db28338, - 0x82f6117f, 0xa84aa1f7, 0x070e33b0, 0xfd33c0e7, 0x527752a0, - 0x78cbe228, 0xd78f706f, 0xa3260139, 0x0c62937e, 0x26de23f6, - 0x899ab1b1, 0x73a742e6, 0xdce3d0a1, 0xf65f6029, 0x591bf26e, - 0xd95580c6, 0x76111281, 0x5cada209, 0xf3e9304e, 0x09d4c319, - 0xa690515e, 0x8c2ce1d6, 0x23687391, 0xcafc06f4, 0x65b894b3, - 0x4f04243b, 0xe040b67c, 0x1a7d452b, 0xb539d76c, 0x9f8567e4, - 0x30c1f5a3, 0xb08f870b, 0x1fcb154c, 0x3577a5c4, 0x9a333783, - 0x600ec4d4, 0xcf4a5693, 0xe5f6e61b, 0x4ab2745c, 0x3e1b050a, - 0x915f974d, 0xbbe327c5, 0x14a7b582, 0xee9a46d5, 0x41ded492, - 0x6b62641a, 0xc426f65d, 0x446884f5, 0xeb2c16b2, 0xc190a63a, - 0x6ed4347d, 0x94e9c72a, 0x3bad556d, 0x1111e5e5, 0xbe5577a2, - 0xf8430749, 0x5707950e, 0x7dbb2586, 0xd2ffb7c1, 0x28c24496, - 0x8786d6d1, 0xad3a6659, 0x027ef41e, 0x823086b6, 0x2d7414f1, - 0x07c8a479, 0xa88c363e, 0x52b1c569, 0xfdf5572e, 0xd749e7a6, - 0x780d75e1, 0x0ca404b7, 0xa3e096f0, 0x895c2678, 0x2618b43f, - 0xdc254768, 0x7361d52f, 0x59dd65a7, 0xf699f7e0, 0x76d78548, - 0xd993170f, 0xf32fa787, 0x5c6b35c0, 0xa656c697, 0x091254d0, - 0x23aee458, 0x8cea761f, 0xaf82058e, 0x00c697c9, 0x2a7a2741, - 0x853eb506, 0x7f034651, 0xd047d416, 0xfafb649e, 0x55bff6d9, - 0xd5f18471, 0x7ab51636, 0x5009a6be, 0xff4d34f9, 0x0570c7ae, - 0xaa3455e9, 0x8088e561, 0x2fcc7726, 0x5b650670, 0xf4219437, - 0xde9d24bf, 0x71d9b6f8, 0x8be445af, 0x24a0d7e8, 0x0e1c6760, - 0xa158f527, 0x2116878f, 0x8e5215c8, 0xa4eea540, 0x0baa3707, - 0xf197c450, 0x5ed35617, 0x746fe69f, 0xdb2b74d8, 0x9d3d0433, - 0x32799674, 0x18c526fc, 0xb781b4bb, 0x4dbc47ec, 0xe2f8d5ab, - 0xc8446523, 0x6700f764, 0xe74e85cc, 0x480a178b, 0x62b6a703, - 0xcdf23544, 0x37cfc613, 0x988b5454, 0xb237e4dc, 0x1d73769b, - 0x69da07cd, 0xc69e958a, 0xec222502, 0x4366b745, 0xb95b4412, - 0x161fd655, 0x3ca366dd, 0x93e7f49a, 0x13a98632, 0xbced1475, - 0x9651a4fd, 0x391536ba, 0xc328c5ed, 0x6c6c57aa, 0x46d0e722, - 0xe9947565}, - {0x00000000, 0x4e890ba9, 0x9d121752, 0xd39b1cfb, 0xe15528e5, - 0xafdc234c, 0x7c473fb7, 0x32ce341e, 0x19db578b, 0x57525c22, - 0x84c940d9, 0xca404b70, 0xf88e7f6e, 0xb60774c7, 0x659c683c, - 0x2b156395, 0x33b6af16, 0x7d3fa4bf, 0xaea4b844, 0xe02db3ed, - 0xd2e387f3, 0x9c6a8c5a, 0x4ff190a1, 0x01789b08, 0x2a6df89d, - 0x64e4f334, 0xb77fefcf, 0xf9f6e466, 0xcb38d078, 0x85b1dbd1, - 0x562ac72a, 0x18a3cc83, 0x676d5e2c, 0x29e45585, 0xfa7f497e, - 0xb4f642d7, 0x863876c9, 0xc8b17d60, 0x1b2a619b, 0x55a36a32, - 0x7eb609a7, 0x303f020e, 0xe3a41ef5, 0xad2d155c, 0x9fe32142, - 0xd16a2aeb, 0x02f13610, 0x4c783db9, 0x54dbf13a, 0x1a52fa93, - 0xc9c9e668, 0x8740edc1, 0xb58ed9df, 0xfb07d276, 0x289cce8d, - 0x6615c524, 0x4d00a6b1, 0x0389ad18, 0xd012b1e3, 0x9e9bba4a, - 0xac558e54, 0xe2dc85fd, 0x31479906, 0x7fce92af, 0xcedabc58, - 0x8053b7f1, 0x53c8ab0a, 0x1d41a0a3, 0x2f8f94bd, 0x61069f14, - 0xb29d83ef, 0xfc148846, 0xd701ebd3, 0x9988e07a, 0x4a13fc81, - 0x049af728, 0x3654c336, 0x78ddc89f, 0xab46d464, 0xe5cfdfcd, - 0xfd6c134e, 0xb3e518e7, 0x607e041c, 0x2ef70fb5, 0x1c393bab, - 0x52b03002, 0x812b2cf9, 0xcfa22750, 0xe4b744c5, 0xaa3e4f6c, - 0x79a55397, 0x372c583e, 0x05e26c20, 0x4b6b6789, 0x98f07b72, - 0xd67970db, 0xa9b7e274, 0xe73ee9dd, 0x34a5f526, 0x7a2cfe8f, - 0x48e2ca91, 0x066bc138, 0xd5f0ddc3, 0x9b79d66a, 0xb06cb5ff, - 0xfee5be56, 0x2d7ea2ad, 0x63f7a904, 0x51399d1a, 0x1fb096b3, - 0xcc2b8a48, 0x82a281e1, 0x9a014d62, 0xd48846cb, 0x07135a30, - 0x499a5199, 0x7b546587, 0x35dd6e2e, 0xe64672d5, 0xa8cf797c, - 0x83da1ae9, 0xcd531140, 0x1ec80dbb, 0x50410612, 0x628f320c, - 0x2c0639a5, 0xff9d255e, 0xb1142ef7, 0x46c47ef1, 0x084d7558, - 0xdbd669a3, 0x955f620a, 0xa7915614, 0xe9185dbd, 0x3a834146, - 0x740a4aef, 0x5f1f297a, 0x119622d3, 0xc20d3e28, 0x8c843581, - 0xbe4a019f, 0xf0c30a36, 0x235816cd, 0x6dd11d64, 0x7572d1e7, - 0x3bfbda4e, 0xe860c6b5, 0xa6e9cd1c, 0x9427f902, 0xdaaef2ab, - 0x0935ee50, 0x47bce5f9, 0x6ca9866c, 0x22208dc5, 0xf1bb913e, - 0xbf329a97, 0x8dfcae89, 0xc375a520, 0x10eeb9db, 0x5e67b272, - 0x21a920dd, 0x6f202b74, 0xbcbb378f, 0xf2323c26, 0xc0fc0838, - 0x8e750391, 0x5dee1f6a, 0x136714c3, 0x38727756, 0x76fb7cff, - 0xa5606004, 0xebe96bad, 0xd9275fb3, 0x97ae541a, 0x443548e1, - 0x0abc4348, 0x121f8fcb, 0x5c968462, 0x8f0d9899, 0xc1849330, - 0xf34aa72e, 0xbdc3ac87, 0x6e58b07c, 0x20d1bbd5, 0x0bc4d840, - 0x454dd3e9, 0x96d6cf12, 0xd85fc4bb, 0xea91f0a5, 0xa418fb0c, - 0x7783e7f7, 0x390aec5e, 0x881ec2a9, 0xc697c900, 0x150cd5fb, - 0x5b85de52, 0x694bea4c, 0x27c2e1e5, 0xf459fd1e, 0xbad0f6b7, - 0x91c59522, 0xdf4c9e8b, 0x0cd78270, 0x425e89d9, 0x7090bdc7, - 0x3e19b66e, 0xed82aa95, 0xa30ba13c, 0xbba86dbf, 0xf5216616, - 0x26ba7aed, 0x68337144, 0x5afd455a, 0x14744ef3, 0xc7ef5208, - 0x896659a1, 0xa2733a34, 0xecfa319d, 0x3f612d66, 0x71e826cf, - 0x432612d1, 0x0daf1978, 0xde340583, 0x90bd0e2a, 0xef739c85, - 0xa1fa972c, 0x72618bd7, 0x3ce8807e, 0x0e26b460, 0x40afbfc9, - 0x9334a332, 0xddbda89b, 0xf6a8cb0e, 0xb821c0a7, 0x6bbadc5c, - 0x2533d7f5, 0x17fde3eb, 0x5974e842, 0x8aeff4b9, 0xc466ff10, - 0xdcc53393, 0x924c383a, 0x41d724c1, 0x0f5e2f68, 0x3d901b76, - 0x731910df, 0xa0820c24, 0xee0b078d, 0xc51e6418, 0x8b976fb1, - 0x580c734a, 0x168578e3, 0x244b4cfd, 0x6ac24754, 0xb9595baf, - 0xf7d05006}, - {0x00000000, 0x8d88fde2, 0xc060fd85, 0x4de80067, 0x5bb0fd4b, - 0xd63800a9, 0x9bd000ce, 0x1658fd2c, 0xb761fa96, 0x3ae90774, - 0x77010713, 0xfa89faf1, 0xecd107dd, 0x6159fa3f, 0x2cb1fa58, - 0xa13907ba, 0xb5b2f36d, 0x383a0e8f, 0x75d20ee8, 0xf85af30a, - 0xee020e26, 0x638af3c4, 0x2e62f3a3, 0xa3ea0e41, 0x02d309fb, - 0x8f5bf419, 0xc2b3f47e, 0x4f3b099c, 0x5963f4b0, 0xd4eb0952, - 0x99030935, 0x148bf4d7, 0xb014e09b, 0x3d9c1d79, 0x70741d1e, - 0xfdfce0fc, 0xeba41dd0, 0x662ce032, 0x2bc4e055, 0xa64c1db7, - 0x07751a0d, 0x8afde7ef, 0xc715e788, 0x4a9d1a6a, 0x5cc5e746, - 0xd14d1aa4, 0x9ca51ac3, 0x112de721, 0x05a613f6, 0x882eee14, - 0xc5c6ee73, 0x484e1391, 0x5e16eebd, 0xd39e135f, 0x9e761338, - 0x13feeeda, 0xb2c7e960, 0x3f4f1482, 0x72a714e5, 0xff2fe907, - 0xe977142b, 0x64ffe9c9, 0x2917e9ae, 0xa49f144c, 0xbb58c777, - 0x36d03a95, 0x7b383af2, 0xf6b0c710, 0xe0e83a3c, 0x6d60c7de, - 0x2088c7b9, 0xad003a5b, 0x0c393de1, 0x81b1c003, 0xcc59c064, - 0x41d13d86, 0x5789c0aa, 0xda013d48, 0x97e93d2f, 0x1a61c0cd, - 0x0eea341a, 0x8362c9f8, 0xce8ac99f, 0x4302347d, 0x555ac951, - 0xd8d234b3, 0x953a34d4, 0x18b2c936, 0xb98bce8c, 0x3403336e, - 0x79eb3309, 0xf463ceeb, 0xe23b33c7, 0x6fb3ce25, 0x225bce42, - 0xafd333a0, 0x0b4c27ec, 0x86c4da0e, 0xcb2cda69, 0x46a4278b, - 0x50fcdaa7, 0xdd742745, 0x909c2722, 0x1d14dac0, 0xbc2ddd7a, - 0x31a52098, 0x7c4d20ff, 0xf1c5dd1d, 0xe79d2031, 0x6a15ddd3, - 0x27fdddb4, 0xaa752056, 0xbefed481, 0x33762963, 0x7e9e2904, - 0xf316d4e6, 0xe54e29ca, 0x68c6d428, 0x252ed44f, 0xa8a629ad, - 0x099f2e17, 0x8417d3f5, 0xc9ffd392, 0x44772e70, 0x522fd35c, - 0xdfa72ebe, 0x924f2ed9, 0x1fc7d33b, 0xadc088af, 0x2048754d, - 0x6da0752a, 0xe02888c8, 0xf67075e4, 0x7bf88806, 0x36108861, - 0xbb987583, 0x1aa17239, 0x97298fdb, 0xdac18fbc, 0x5749725e, - 0x41118f72, 0xcc997290, 0x817172f7, 0x0cf98f15, 0x18727bc2, - 0x95fa8620, 0xd8128647, 0x559a7ba5, 0x43c28689, 0xce4a7b6b, - 0x83a27b0c, 0x0e2a86ee, 0xaf138154, 0x229b7cb6, 0x6f737cd1, - 0xe2fb8133, 0xf4a37c1f, 0x792b81fd, 0x34c3819a, 0xb94b7c78, - 0x1dd46834, 0x905c95d6, 0xddb495b1, 0x503c6853, 0x4664957f, - 0xcbec689d, 0x860468fa, 0x0b8c9518, 0xaab592a2, 0x273d6f40, - 0x6ad56f27, 0xe75d92c5, 0xf1056fe9, 0x7c8d920b, 0x3165926c, - 0xbced6f8e, 0xa8669b59, 0x25ee66bb, 0x680666dc, 0xe58e9b3e, - 0xf3d66612, 0x7e5e9bf0, 0x33b69b97, 0xbe3e6675, 0x1f0761cf, - 0x928f9c2d, 0xdf679c4a, 0x52ef61a8, 0x44b79c84, 0xc93f6166, - 0x84d76101, 0x095f9ce3, 0x16984fd8, 0x9b10b23a, 0xd6f8b25d, - 0x5b704fbf, 0x4d28b293, 0xc0a04f71, 0x8d484f16, 0x00c0b2f4, - 0xa1f9b54e, 0x2c7148ac, 0x619948cb, 0xec11b529, 0xfa494805, - 0x77c1b5e7, 0x3a29b580, 0xb7a14862, 0xa32abcb5, 0x2ea24157, - 0x634a4130, 0xeec2bcd2, 0xf89a41fe, 0x7512bc1c, 0x38fabc7b, - 0xb5724199, 0x144b4623, 0x99c3bbc1, 0xd42bbba6, 0x59a34644, - 0x4ffbbb68, 0xc273468a, 0x8f9b46ed, 0x0213bb0f, 0xa68caf43, - 0x2b0452a1, 0x66ec52c6, 0xeb64af24, 0xfd3c5208, 0x70b4afea, - 0x3d5caf8d, 0xb0d4526f, 0x11ed55d5, 0x9c65a837, 0xd18da850, - 0x5c0555b2, 0x4a5da89e, 0xc7d5557c, 0x8a3d551b, 0x07b5a8f9, - 0x133e5c2e, 0x9eb6a1cc, 0xd35ea1ab, 0x5ed65c49, 0x488ea165, - 0xc5065c87, 0x88ee5ce0, 0x0566a102, 0xa45fa6b8, 0x29d75b5a, - 0x643f5b3d, 0xe9b7a6df, 0xffef5bf3, 0x7267a611, 0x3f8fa676, - 0xb2075b94}, - {0x00000000, 0x80f0171f, 0xda91287f, 0x5a613f60, 0x6e5356bf, - 0xeea341a0, 0xb4c27ec0, 0x343269df, 0xdca6ad7e, 0x5c56ba61, - 0x06378501, 0x86c7921e, 0xb2f5fbc1, 0x3205ecde, 0x6864d3be, - 0xe894c4a1, 0x623c5cbd, 0xe2cc4ba2, 0xb8ad74c2, 0x385d63dd, - 0x0c6f0a02, 0x8c9f1d1d, 0xd6fe227d, 0x560e3562, 0xbe9af1c3, - 0x3e6ae6dc, 0x640bd9bc, 0xe4fbcea3, 0xd0c9a77c, 0x5039b063, - 0x0a588f03, 0x8aa8981c, 0xc478b97a, 0x4488ae65, 0x1ee99105, - 0x9e19861a, 0xaa2befc5, 0x2adbf8da, 0x70bac7ba, 0xf04ad0a5, - 0x18de1404, 0x982e031b, 0xc24f3c7b, 0x42bf2b64, 0x768d42bb, - 0xf67d55a4, 0xac1c6ac4, 0x2cec7ddb, 0xa644e5c7, 0x26b4f2d8, - 0x7cd5cdb8, 0xfc25daa7, 0xc817b378, 0x48e7a467, 0x12869b07, - 0x92768c18, 0x7ae248b9, 0xfa125fa6, 0xa07360c6, 0x208377d9, - 0x14b11e06, 0x94410919, 0xce203679, 0x4ed02166, 0x538074b5, - 0xd37063aa, 0x89115cca, 0x09e14bd5, 0x3dd3220a, 0xbd233515, - 0xe7420a75, 0x67b21d6a, 0x8f26d9cb, 0x0fd6ced4, 0x55b7f1b4, - 0xd547e6ab, 0xe1758f74, 0x6185986b, 0x3be4a70b, 0xbb14b014, - 0x31bc2808, 0xb14c3f17, 0xeb2d0077, 0x6bdd1768, 0x5fef7eb7, - 0xdf1f69a8, 0x857e56c8, 0x058e41d7, 0xed1a8576, 0x6dea9269, - 0x378bad09, 0xb77bba16, 0x8349d3c9, 0x03b9c4d6, 0x59d8fbb6, - 0xd928eca9, 0x97f8cdcf, 0x1708dad0, 0x4d69e5b0, 0xcd99f2af, - 0xf9ab9b70, 0x795b8c6f, 0x233ab30f, 0xa3caa410, 0x4b5e60b1, - 0xcbae77ae, 0x91cf48ce, 0x113f5fd1, 0x250d360e, 0xa5fd2111, - 0xff9c1e71, 0x7f6c096e, 0xf5c49172, 0x7534866d, 0x2f55b90d, - 0xafa5ae12, 0x9b97c7cd, 0x1b67d0d2, 0x4106efb2, 0xc1f6f8ad, - 0x29623c0c, 0xa9922b13, 0xf3f31473, 0x7303036c, 0x47316ab3, - 0xc7c17dac, 0x9da042cc, 0x1d5055d3, 0xa700e96a, 0x27f0fe75, - 0x7d91c115, 0xfd61d60a, 0xc953bfd5, 0x49a3a8ca, 0x13c297aa, - 0x933280b5, 0x7ba64414, 0xfb56530b, 0xa1376c6b, 0x21c77b74, - 0x15f512ab, 0x950505b4, 0xcf643ad4, 0x4f942dcb, 0xc53cb5d7, - 0x45cca2c8, 0x1fad9da8, 0x9f5d8ab7, 0xab6fe368, 0x2b9ff477, - 0x71fecb17, 0xf10edc08, 0x199a18a9, 0x996a0fb6, 0xc30b30d6, - 0x43fb27c9, 0x77c94e16, 0xf7395909, 0xad586669, 0x2da87176, - 0x63785010, 0xe388470f, 0xb9e9786f, 0x39196f70, 0x0d2b06af, - 0x8ddb11b0, 0xd7ba2ed0, 0x574a39cf, 0xbfdefd6e, 0x3f2eea71, - 0x654fd511, 0xe5bfc20e, 0xd18dabd1, 0x517dbcce, 0x0b1c83ae, - 0x8bec94b1, 0x01440cad, 0x81b41bb2, 0xdbd524d2, 0x5b2533cd, - 0x6f175a12, 0xefe74d0d, 0xb586726d, 0x35766572, 0xdde2a1d3, - 0x5d12b6cc, 0x077389ac, 0x87839eb3, 0xb3b1f76c, 0x3341e073, - 0x6920df13, 0xe9d0c80c, 0xf4809ddf, 0x74708ac0, 0x2e11b5a0, - 0xaee1a2bf, 0x9ad3cb60, 0x1a23dc7f, 0x4042e31f, 0xc0b2f400, - 0x282630a1, 0xa8d627be, 0xf2b718de, 0x72470fc1, 0x4675661e, - 0xc6857101, 0x9ce44e61, 0x1c14597e, 0x96bcc162, 0x164cd67d, - 0x4c2de91d, 0xccddfe02, 0xf8ef97dd, 0x781f80c2, 0x227ebfa2, - 0xa28ea8bd, 0x4a1a6c1c, 0xcaea7b03, 0x908b4463, 0x107b537c, - 0x24493aa3, 0xa4b92dbc, 0xfed812dc, 0x7e2805c3, 0x30f824a5, - 0xb00833ba, 0xea690cda, 0x6a991bc5, 0x5eab721a, 0xde5b6505, - 0x843a5a65, 0x04ca4d7a, 0xec5e89db, 0x6cae9ec4, 0x36cfa1a4, - 0xb63fb6bb, 0x820ddf64, 0x02fdc87b, 0x589cf71b, 0xd86ce004, - 0x52c47818, 0xd2346f07, 0x88555067, 0x08a54778, 0x3c972ea7, - 0xbc6739b8, 0xe60606d8, 0x66f611c7, 0x8e62d566, 0x0e92c279, - 0x54f3fd19, 0xd403ea06, 0xe03183d9, 0x60c194c6, 0x3aa0aba6, - 0xba50bcb9}, - {0x00000000, 0x9570d495, 0xf190af6b, 0x64e07bfe, 0x38505897, - 0xad208c02, 0xc9c0f7fc, 0x5cb02369, 0x70a0b12e, 0xe5d065bb, - 0x81301e45, 0x1440cad0, 0x48f0e9b9, 0xdd803d2c, 0xb96046d2, - 0x2c109247, 0xe141625c, 0x7431b6c9, 0x10d1cd37, 0x85a119a2, - 0xd9113acb, 0x4c61ee5e, 0x288195a0, 0xbdf14135, 0x91e1d372, - 0x049107e7, 0x60717c19, 0xf501a88c, 0xa9b18be5, 0x3cc15f70, - 0x5821248e, 0xcd51f01b, 0x19f3c2f9, 0x8c83166c, 0xe8636d92, - 0x7d13b907, 0x21a39a6e, 0xb4d34efb, 0xd0333505, 0x4543e190, - 0x695373d7, 0xfc23a742, 0x98c3dcbc, 0x0db30829, 0x51032b40, - 0xc473ffd5, 0xa093842b, 0x35e350be, 0xf8b2a0a5, 0x6dc27430, - 0x09220fce, 0x9c52db5b, 0xc0e2f832, 0x55922ca7, 0x31725759, - 0xa40283cc, 0x8812118b, 0x1d62c51e, 0x7982bee0, 0xecf26a75, - 0xb042491c, 0x25329d89, 0x41d2e677, 0xd4a232e2, 0x33e785f2, - 0xa6975167, 0xc2772a99, 0x5707fe0c, 0x0bb7dd65, 0x9ec709f0, - 0xfa27720e, 0x6f57a69b, 0x434734dc, 0xd637e049, 0xb2d79bb7, - 0x27a74f22, 0x7b176c4b, 0xee67b8de, 0x8a87c320, 0x1ff717b5, - 0xd2a6e7ae, 0x47d6333b, 0x233648c5, 0xb6469c50, 0xeaf6bf39, - 0x7f866bac, 0x1b661052, 0x8e16c4c7, 0xa2065680, 0x37768215, - 0x5396f9eb, 0xc6e62d7e, 0x9a560e17, 0x0f26da82, 0x6bc6a17c, - 0xfeb675e9, 0x2a14470b, 0xbf64939e, 0xdb84e860, 0x4ef43cf5, - 0x12441f9c, 0x8734cb09, 0xe3d4b0f7, 0x76a46462, 0x5ab4f625, - 0xcfc422b0, 0xab24594e, 0x3e548ddb, 0x62e4aeb2, 0xf7947a27, - 0x937401d9, 0x0604d54c, 0xcb552557, 0x5e25f1c2, 0x3ac58a3c, - 0xafb55ea9, 0xf3057dc0, 0x6675a955, 0x0295d2ab, 0x97e5063e, - 0xbbf59479, 0x2e8540ec, 0x4a653b12, 0xdf15ef87, 0x83a5ccee, - 0x16d5187b, 0x72356385, 0xe745b710, 0x67cf0be4, 0xf2bfdf71, - 0x965fa48f, 0x032f701a, 0x5f9f5373, 0xcaef87e6, 0xae0ffc18, - 0x3b7f288d, 0x176fbaca, 0x821f6e5f, 0xe6ff15a1, 0x738fc134, - 0x2f3fe25d, 0xba4f36c8, 0xdeaf4d36, 0x4bdf99a3, 0x868e69b8, - 0x13febd2d, 0x771ec6d3, 0xe26e1246, 0xbede312f, 0x2baee5ba, - 0x4f4e9e44, 0xda3e4ad1, 0xf62ed896, 0x635e0c03, 0x07be77fd, - 0x92cea368, 0xce7e8001, 0x5b0e5494, 0x3fee2f6a, 0xaa9efbff, - 0x7e3cc91d, 0xeb4c1d88, 0x8fac6676, 0x1adcb2e3, 0x466c918a, - 0xd31c451f, 0xb7fc3ee1, 0x228cea74, 0x0e9c7833, 0x9becaca6, - 0xff0cd758, 0x6a7c03cd, 0x36cc20a4, 0xa3bcf431, 0xc75c8fcf, - 0x522c5b5a, 0x9f7dab41, 0x0a0d7fd4, 0x6eed042a, 0xfb9dd0bf, - 0xa72df3d6, 0x325d2743, 0x56bd5cbd, 0xc3cd8828, 0xefdd1a6f, - 0x7aadcefa, 0x1e4db504, 0x8b3d6191, 0xd78d42f8, 0x42fd966d, - 0x261ded93, 0xb36d3906, 0x54288e16, 0xc1585a83, 0xa5b8217d, - 0x30c8f5e8, 0x6c78d681, 0xf9080214, 0x9de879ea, 0x0898ad7f, - 0x24883f38, 0xb1f8ebad, 0xd5189053, 0x406844c6, 0x1cd867af, - 0x89a8b33a, 0xed48c8c4, 0x78381c51, 0xb569ec4a, 0x201938df, - 0x44f94321, 0xd18997b4, 0x8d39b4dd, 0x18496048, 0x7ca91bb6, - 0xe9d9cf23, 0xc5c95d64, 0x50b989f1, 0x3459f20f, 0xa129269a, - 0xfd9905f3, 0x68e9d166, 0x0c09aa98, 0x99797e0d, 0x4ddb4cef, - 0xd8ab987a, 0xbc4be384, 0x293b3711, 0x758b1478, 0xe0fbc0ed, - 0x841bbb13, 0x116b6f86, 0x3d7bfdc1, 0xa80b2954, 0xcceb52aa, - 0x599b863f, 0x052ba556, 0x905b71c3, 0xf4bb0a3d, 0x61cbdea8, - 0xac9a2eb3, 0x39eafa26, 0x5d0a81d8, 0xc87a554d, 0x94ca7624, - 0x01baa2b1, 0x655ad94f, 0xf02a0dda, 0xdc3a9f9d, 0x494a4b08, - 0x2daa30f6, 0xb8dae463, 0xe46ac70a, 0x711a139f, 0x15fa6861, - 0x808abcf4}, - {0x00000000, 0xcf9e17c8, 0x444d29d1, 0x8bd33e19, 0x889a53a2, - 0x4704446a, 0xccd77a73, 0x03496dbb, 0xca45a105, 0x05dbb6cd, - 0x8e0888d4, 0x41969f1c, 0x42dff2a7, 0x8d41e56f, 0x0692db76, - 0xc90cccbe, 0x4ffa444b, 0x80645383, 0x0bb76d9a, 0xc4297a52, - 0xc76017e9, 0x08fe0021, 0x832d3e38, 0x4cb329f0, 0x85bfe54e, - 0x4a21f286, 0xc1f2cc9f, 0x0e6cdb57, 0x0d25b6ec, 0xc2bba124, - 0x49689f3d, 0x86f688f5, 0x9ff48896, 0x506a9f5e, 0xdbb9a147, - 0x1427b68f, 0x176edb34, 0xd8f0ccfc, 0x5323f2e5, 0x9cbde52d, - 0x55b12993, 0x9a2f3e5b, 0x11fc0042, 0xde62178a, 0xdd2b7a31, - 0x12b56df9, 0x996653e0, 0x56f84428, 0xd00eccdd, 0x1f90db15, - 0x9443e50c, 0x5bddf2c4, 0x58949f7f, 0x970a88b7, 0x1cd9b6ae, - 0xd347a166, 0x1a4b6dd8, 0xd5d57a10, 0x5e064409, 0x919853c1, - 0x92d13e7a, 0x5d4f29b2, 0xd69c17ab, 0x19020063, 0xe498176d, - 0x2b0600a5, 0xa0d53ebc, 0x6f4b2974, 0x6c0244cf, 0xa39c5307, - 0x284f6d1e, 0xe7d17ad6, 0x2eddb668, 0xe143a1a0, 0x6a909fb9, - 0xa50e8871, 0xa647e5ca, 0x69d9f202, 0xe20acc1b, 0x2d94dbd3, - 0xab625326, 0x64fc44ee, 0xef2f7af7, 0x20b16d3f, 0x23f80084, - 0xec66174c, 0x67b52955, 0xa82b3e9d, 0x6127f223, 0xaeb9e5eb, - 0x256adbf2, 0xeaf4cc3a, 0xe9bda181, 0x2623b649, 0xadf08850, - 0x626e9f98, 0x7b6c9ffb, 0xb4f28833, 0x3f21b62a, 0xf0bfa1e2, - 0xf3f6cc59, 0x3c68db91, 0xb7bbe588, 0x7825f240, 0xb1293efe, - 0x7eb72936, 0xf564172f, 0x3afa00e7, 0x39b36d5c, 0xf62d7a94, - 0x7dfe448d, 0xb2605345, 0x3496dbb0, 0xfb08cc78, 0x70dbf261, - 0xbf45e5a9, 0xbc0c8812, 0x73929fda, 0xf841a1c3, 0x37dfb60b, - 0xfed37ab5, 0x314d6d7d, 0xba9e5364, 0x750044ac, 0x76492917, - 0xb9d73edf, 0x320400c6, 0xfd9a170e, 0x1241289b, 0xdddf3f53, - 0x560c014a, 0x99921682, 0x9adb7b39, 0x55456cf1, 0xde9652e8, - 0x11084520, 0xd804899e, 0x179a9e56, 0x9c49a04f, 0x53d7b787, - 0x509eda3c, 0x9f00cdf4, 0x14d3f3ed, 0xdb4de425, 0x5dbb6cd0, - 0x92257b18, 0x19f64501, 0xd66852c9, 0xd5213f72, 0x1abf28ba, - 0x916c16a3, 0x5ef2016b, 0x97fecdd5, 0x5860da1d, 0xd3b3e404, - 0x1c2df3cc, 0x1f649e77, 0xd0fa89bf, 0x5b29b7a6, 0x94b7a06e, - 0x8db5a00d, 0x422bb7c5, 0xc9f889dc, 0x06669e14, 0x052ff3af, - 0xcab1e467, 0x4162da7e, 0x8efccdb6, 0x47f00108, 0x886e16c0, - 0x03bd28d9, 0xcc233f11, 0xcf6a52aa, 0x00f44562, 0x8b277b7b, - 0x44b96cb3, 0xc24fe446, 0x0dd1f38e, 0x8602cd97, 0x499cda5f, - 0x4ad5b7e4, 0x854ba02c, 0x0e989e35, 0xc10689fd, 0x080a4543, - 0xc794528b, 0x4c476c92, 0x83d97b5a, 0x809016e1, 0x4f0e0129, - 0xc4dd3f30, 0x0b4328f8, 0xf6d93ff6, 0x3947283e, 0xb2941627, - 0x7d0a01ef, 0x7e436c54, 0xb1dd7b9c, 0x3a0e4585, 0xf590524d, - 0x3c9c9ef3, 0xf302893b, 0x78d1b722, 0xb74fa0ea, 0xb406cd51, - 0x7b98da99, 0xf04be480, 0x3fd5f348, 0xb9237bbd, 0x76bd6c75, - 0xfd6e526c, 0x32f045a4, 0x31b9281f, 0xfe273fd7, 0x75f401ce, - 0xba6a1606, 0x7366dab8, 0xbcf8cd70, 0x372bf369, 0xf8b5e4a1, - 0xfbfc891a, 0x34629ed2, 0xbfb1a0cb, 0x702fb703, 0x692db760, - 0xa6b3a0a8, 0x2d609eb1, 0xe2fe8979, 0xe1b7e4c2, 0x2e29f30a, - 0xa5facd13, 0x6a64dadb, 0xa3681665, 0x6cf601ad, 0xe7253fb4, - 0x28bb287c, 0x2bf245c7, 0xe46c520f, 0x6fbf6c16, 0xa0217bde, - 0x26d7f32b, 0xe949e4e3, 0x629adafa, 0xad04cd32, 0xae4da089, - 0x61d3b741, 0xea008958, 0x259e9e90, 0xec92522e, 0x230c45e6, - 0xa8df7bff, 0x67416c37, 0x6408018c, 0xab961644, 0x2045285d, - 0xefdb3f95}, - {0x00000000, 0x24825136, 0x4904a26c, 0x6d86f35a, 0x920944d8, - 0xb68b15ee, 0xdb0de6b4, 0xff8fb782, 0xff638ff1, 0xdbe1dec7, - 0xb6672d9d, 0x92e57cab, 0x6d6acb29, 0x49e89a1f, 0x246e6945, - 0x00ec3873, 0x25b619a3, 0x01344895, 0x6cb2bbcf, 0x4830eaf9, - 0xb7bf5d7b, 0x933d0c4d, 0xfebbff17, 0xda39ae21, 0xdad59652, - 0xfe57c764, 0x93d1343e, 0xb7536508, 0x48dcd28a, 0x6c5e83bc, - 0x01d870e6, 0x255a21d0, 0x4b6c3346, 0x6fee6270, 0x0268912a, - 0x26eac01c, 0xd965779e, 0xfde726a8, 0x9061d5f2, 0xb4e384c4, - 0xb40fbcb7, 0x908ded81, 0xfd0b1edb, 0xd9894fed, 0x2606f86f, - 0x0284a959, 0x6f025a03, 0x4b800b35, 0x6eda2ae5, 0x4a587bd3, - 0x27de8889, 0x035cd9bf, 0xfcd36e3d, 0xd8513f0b, 0xb5d7cc51, - 0x91559d67, 0x91b9a514, 0xb53bf422, 0xd8bd0778, 0xfc3f564e, - 0x03b0e1cc, 0x2732b0fa, 0x4ab443a0, 0x6e361296, 0x96d8668c, - 0xb25a37ba, 0xdfdcc4e0, 0xfb5e95d6, 0x04d12254, 0x20537362, - 0x4dd58038, 0x6957d10e, 0x69bbe97d, 0x4d39b84b, 0x20bf4b11, - 0x043d1a27, 0xfbb2ada5, 0xdf30fc93, 0xb2b60fc9, 0x96345eff, - 0xb36e7f2f, 0x97ec2e19, 0xfa6add43, 0xdee88c75, 0x21673bf7, - 0x05e56ac1, 0x6863999b, 0x4ce1c8ad, 0x4c0df0de, 0x688fa1e8, - 0x050952b2, 0x218b0384, 0xde04b406, 0xfa86e530, 0x9700166a, - 0xb382475c, 0xddb455ca, 0xf93604fc, 0x94b0f7a6, 0xb032a690, - 0x4fbd1112, 0x6b3f4024, 0x06b9b37e, 0x223be248, 0x22d7da3b, - 0x06558b0d, 0x6bd37857, 0x4f512961, 0xb0de9ee3, 0x945ccfd5, - 0xf9da3c8f, 0xdd586db9, 0xf8024c69, 0xdc801d5f, 0xb106ee05, - 0x9584bf33, 0x6a0b08b1, 0x4e895987, 0x230faadd, 0x078dfbeb, - 0x0761c398, 0x23e392ae, 0x4e6561f4, 0x6ae730c2, 0x95688740, - 0xb1ead676, 0xdc6c252c, 0xf8ee741a, 0xf6c1cb59, 0xd2439a6f, - 0xbfc56935, 0x9b473803, 0x64c88f81, 0x404adeb7, 0x2dcc2ded, - 0x094e7cdb, 0x09a244a8, 0x2d20159e, 0x40a6e6c4, 0x6424b7f2, - 0x9bab0070, 0xbf295146, 0xd2afa21c, 0xf62df32a, 0xd377d2fa, - 0xf7f583cc, 0x9a737096, 0xbef121a0, 0x417e9622, 0x65fcc714, - 0x087a344e, 0x2cf86578, 0x2c145d0b, 0x08960c3d, 0x6510ff67, - 0x4192ae51, 0xbe1d19d3, 0x9a9f48e5, 0xf719bbbf, 0xd39bea89, - 0xbdadf81f, 0x992fa929, 0xf4a95a73, 0xd02b0b45, 0x2fa4bcc7, - 0x0b26edf1, 0x66a01eab, 0x42224f9d, 0x42ce77ee, 0x664c26d8, - 0x0bcad582, 0x2f4884b4, 0xd0c73336, 0xf4456200, 0x99c3915a, - 0xbd41c06c, 0x981be1bc, 0xbc99b08a, 0xd11f43d0, 0xf59d12e6, - 0x0a12a564, 0x2e90f452, 0x43160708, 0x6794563e, 0x67786e4d, - 0x43fa3f7b, 0x2e7ccc21, 0x0afe9d17, 0xf5712a95, 0xd1f37ba3, - 0xbc7588f9, 0x98f7d9cf, 0x6019add5, 0x449bfce3, 0x291d0fb9, - 0x0d9f5e8f, 0xf210e90d, 0xd692b83b, 0xbb144b61, 0x9f961a57, - 0x9f7a2224, 0xbbf87312, 0xd67e8048, 0xf2fcd17e, 0x0d7366fc, - 0x29f137ca, 0x4477c490, 0x60f595a6, 0x45afb476, 0x612de540, - 0x0cab161a, 0x2829472c, 0xd7a6f0ae, 0xf324a198, 0x9ea252c2, - 0xba2003f4, 0xbacc3b87, 0x9e4e6ab1, 0xf3c899eb, 0xd74ac8dd, - 0x28c57f5f, 0x0c472e69, 0x61c1dd33, 0x45438c05, 0x2b759e93, - 0x0ff7cfa5, 0x62713cff, 0x46f36dc9, 0xb97cda4b, 0x9dfe8b7d, - 0xf0787827, 0xd4fa2911, 0xd4161162, 0xf0944054, 0x9d12b30e, - 0xb990e238, 0x461f55ba, 0x629d048c, 0x0f1bf7d6, 0x2b99a6e0, - 0x0ec38730, 0x2a41d606, 0x47c7255c, 0x6345746a, 0x9ccac3e8, - 0xb84892de, 0xd5ce6184, 0xf14c30b2, 0xf1a008c1, 0xd52259f7, - 0xb8a4aaad, 0x9c26fb9b, 0x63a94c19, 0x472b1d2f, 0x2aadee75, - 0x0e2fbf43}, - {0x00000000, 0x36f290f3, 0x6de521e6, 0x5b17b115, 0xdbca43cc, - 0xed38d33f, 0xb62f622a, 0x80ddf2d9, 0x6ce581d9, 0x5a17112a, - 0x0100a03f, 0x37f230cc, 0xb72fc215, 0x81dd52e6, 0xdacae3f3, - 0xec387300, 0xd9cb03b2, 0xef399341, 0xb42e2254, 0x82dcb2a7, - 0x0201407e, 0x34f3d08d, 0x6fe46198, 0x5916f16b, 0xb52e826b, - 0x83dc1298, 0xd8cba38d, 0xee39337e, 0x6ee4c1a7, 0x58165154, - 0x0301e041, 0x35f370b2, 0x68e70125, 0x5e1591d6, 0x050220c3, - 0x33f0b030, 0xb32d42e9, 0x85dfd21a, 0xdec8630f, 0xe83af3fc, - 0x040280fc, 0x32f0100f, 0x69e7a11a, 0x5f1531e9, 0xdfc8c330, - 0xe93a53c3, 0xb22de2d6, 0x84df7225, 0xb12c0297, 0x87de9264, - 0xdcc92371, 0xea3bb382, 0x6ae6415b, 0x5c14d1a8, 0x070360bd, - 0x31f1f04e, 0xddc9834e, 0xeb3b13bd, 0xb02ca2a8, 0x86de325b, - 0x0603c082, 0x30f15071, 0x6be6e164, 0x5d147197, 0xd1ce024a, - 0xe73c92b9, 0xbc2b23ac, 0x8ad9b35f, 0x0a044186, 0x3cf6d175, - 0x67e16060, 0x5113f093, 0xbd2b8393, 0x8bd91360, 0xd0cea275, - 0xe63c3286, 0x66e1c05f, 0x501350ac, 0x0b04e1b9, 0x3df6714a, - 0x080501f8, 0x3ef7910b, 0x65e0201e, 0x5312b0ed, 0xd3cf4234, - 0xe53dd2c7, 0xbe2a63d2, 0x88d8f321, 0x64e08021, 0x521210d2, - 0x0905a1c7, 0x3ff73134, 0xbf2ac3ed, 0x89d8531e, 0xd2cfe20b, - 0xe43d72f8, 0xb929036f, 0x8fdb939c, 0xd4cc2289, 0xe23eb27a, - 0x62e340a3, 0x5411d050, 0x0f066145, 0x39f4f1b6, 0xd5cc82b6, - 0xe33e1245, 0xb829a350, 0x8edb33a3, 0x0e06c17a, 0x38f45189, - 0x63e3e09c, 0x5511706f, 0x60e200dd, 0x5610902e, 0x0d07213b, - 0x3bf5b1c8, 0xbb284311, 0x8ddad3e2, 0xd6cd62f7, 0xe03ff204, - 0x0c078104, 0x3af511f7, 0x61e2a0e2, 0x57103011, 0xd7cdc2c8, - 0xe13f523b, 0xba28e32e, 0x8cda73dd, 0x78ed02d5, 0x4e1f9226, - 0x15082333, 0x23fab3c0, 0xa3274119, 0x95d5d1ea, 0xcec260ff, - 0xf830f00c, 0x1408830c, 0x22fa13ff, 0x79eda2ea, 0x4f1f3219, - 0xcfc2c0c0, 0xf9305033, 0xa227e126, 0x94d571d5, 0xa1260167, - 0x97d49194, 0xccc32081, 0xfa31b072, 0x7aec42ab, 0x4c1ed258, - 0x1709634d, 0x21fbf3be, 0xcdc380be, 0xfb31104d, 0xa026a158, - 0x96d431ab, 0x1609c372, 0x20fb5381, 0x7bece294, 0x4d1e7267, - 0x100a03f0, 0x26f89303, 0x7def2216, 0x4b1db2e5, 0xcbc0403c, - 0xfd32d0cf, 0xa62561da, 0x90d7f129, 0x7cef8229, 0x4a1d12da, - 0x110aa3cf, 0x27f8333c, 0xa725c1e5, 0x91d75116, 0xcac0e003, - 0xfc3270f0, 0xc9c10042, 0xff3390b1, 0xa42421a4, 0x92d6b157, - 0x120b438e, 0x24f9d37d, 0x7fee6268, 0x491cf29b, 0xa524819b, - 0x93d61168, 0xc8c1a07d, 0xfe33308e, 0x7eeec257, 0x481c52a4, - 0x130be3b1, 0x25f97342, 0xa923009f, 0x9fd1906c, 0xc4c62179, - 0xf234b18a, 0x72e94353, 0x441bd3a0, 0x1f0c62b5, 0x29fef246, - 0xc5c68146, 0xf33411b5, 0xa823a0a0, 0x9ed13053, 0x1e0cc28a, - 0x28fe5279, 0x73e9e36c, 0x451b739f, 0x70e8032d, 0x461a93de, - 0x1d0d22cb, 0x2bffb238, 0xab2240e1, 0x9dd0d012, 0xc6c76107, - 0xf035f1f4, 0x1c0d82f4, 0x2aff1207, 0x71e8a312, 0x471a33e1, - 0xc7c7c138, 0xf13551cb, 0xaa22e0de, 0x9cd0702d, 0xc1c401ba, - 0xf7369149, 0xac21205c, 0x9ad3b0af, 0x1a0e4276, 0x2cfcd285, - 0x77eb6390, 0x4119f363, 0xad218063, 0x9bd31090, 0xc0c4a185, - 0xf6363176, 0x76ebc3af, 0x4019535c, 0x1b0ee249, 0x2dfc72ba, - 0x180f0208, 0x2efd92fb, 0x75ea23ee, 0x4318b31d, 0xc3c541c4, - 0xf537d137, 0xae206022, 0x98d2f0d1, 0x74ea83d1, 0x42181322, - 0x190fa237, 0x2ffd32c4, 0xaf20c01d, 0x99d250ee, 0xc2c5e1fb, - 0xf4377108}}; - -local const z_word_t FAR crc_braid_big_table[][256] = { - {0x0000000000000000, 0xf390f23600000000, 0xe621e56d00000000, - 0x15b1175b00000000, 0xcc43cadb00000000, 0x3fd338ed00000000, - 0x2a622fb600000000, 0xd9f2dd8000000000, 0xd981e56c00000000, - 0x2a11175a00000000, 0x3fa0000100000000, 0xcc30f23700000000, - 0x15c22fb700000000, 0xe652dd8100000000, 0xf3e3cada00000000, - 0x007338ec00000000, 0xb203cbd900000000, 0x419339ef00000000, - 0x54222eb400000000, 0xa7b2dc8200000000, 0x7e40010200000000, - 0x8dd0f33400000000, 0x9861e46f00000000, 0x6bf1165900000000, - 0x6b822eb500000000, 0x9812dc8300000000, 0x8da3cbd800000000, - 0x7e3339ee00000000, 0xa7c1e46e00000000, 0x5451165800000000, - 0x41e0010300000000, 0xb270f33500000000, 0x2501e76800000000, - 0xd691155e00000000, 0xc320020500000000, 0x30b0f03300000000, - 0xe9422db300000000, 0x1ad2df8500000000, 0x0f63c8de00000000, - 0xfcf33ae800000000, 0xfc80020400000000, 0x0f10f03200000000, - 0x1aa1e76900000000, 0xe931155f00000000, 0x30c3c8df00000000, - 0xc3533ae900000000, 0xd6e22db200000000, 0x2572df8400000000, - 0x97022cb100000000, 0x6492de8700000000, 0x7123c9dc00000000, - 0x82b33bea00000000, 0x5b41e66a00000000, 0xa8d1145c00000000, - 0xbd60030700000000, 0x4ef0f13100000000, 0x4e83c9dd00000000, - 0xbd133beb00000000, 0xa8a22cb000000000, 0x5b32de8600000000, - 0x82c0030600000000, 0x7150f13000000000, 0x64e1e66b00000000, - 0x9771145d00000000, 0x4a02ced100000000, 0xb9923ce700000000, - 0xac232bbc00000000, 0x5fb3d98a00000000, 0x8641040a00000000, - 0x75d1f63c00000000, 0x6060e16700000000, 0x93f0135100000000, - 0x93832bbd00000000, 0x6013d98b00000000, 0x75a2ced000000000, - 0x86323ce600000000, 0x5fc0e16600000000, 0xac50135000000000, - 0xb9e1040b00000000, 0x4a71f63d00000000, 0xf801050800000000, - 0x0b91f73e00000000, 0x1e20e06500000000, 0xedb0125300000000, - 0x3442cfd300000000, 0xc7d23de500000000, 0xd2632abe00000000, - 0x21f3d88800000000, 0x2180e06400000000, 0xd210125200000000, - 0xc7a1050900000000, 0x3431f73f00000000, 0xedc32abf00000000, - 0x1e53d88900000000, 0x0be2cfd200000000, 0xf8723de400000000, - 0x6f0329b900000000, 0x9c93db8f00000000, 0x8922ccd400000000, - 0x7ab23ee200000000, 0xa340e36200000000, 0x50d0115400000000, - 0x4561060f00000000, 0xb6f1f43900000000, 0xb682ccd500000000, - 0x45123ee300000000, 0x50a329b800000000, 0xa333db8e00000000, - 0x7ac1060e00000000, 0x8951f43800000000, 0x9ce0e36300000000, - 0x6f70115500000000, 0xdd00e26000000000, 0x2e90105600000000, - 0x3b21070d00000000, 0xc8b1f53b00000000, 0x114328bb00000000, - 0xe2d3da8d00000000, 0xf762cdd600000000, 0x04f23fe000000000, - 0x0481070c00000000, 0xf711f53a00000000, 0xe2a0e26100000000, - 0x1130105700000000, 0xc8c2cdd700000000, 0x3b523fe100000000, - 0x2ee328ba00000000, 0xdd73da8c00000000, 0xd502ed7800000000, - 0x26921f4e00000000, 0x3323081500000000, 0xc0b3fa2300000000, - 0x194127a300000000, 0xead1d59500000000, 0xff60c2ce00000000, - 0x0cf030f800000000, 0x0c83081400000000, 0xff13fa2200000000, - 0xeaa2ed7900000000, 0x19321f4f00000000, 0xc0c0c2cf00000000, - 0x335030f900000000, 0x26e127a200000000, 0xd571d59400000000, - 0x670126a100000000, 0x9491d49700000000, 0x8120c3cc00000000, - 0x72b031fa00000000, 0xab42ec7a00000000, 0x58d21e4c00000000, - 0x4d63091700000000, 0xbef3fb2100000000, 0xbe80c3cd00000000, - 0x4d1031fb00000000, 0x58a126a000000000, 0xab31d49600000000, - 0x72c3091600000000, 0x8153fb2000000000, 0x94e2ec7b00000000, - 0x67721e4d00000000, 0xf0030a1000000000, 0x0393f82600000000, - 0x1622ef7d00000000, 0xe5b21d4b00000000, 0x3c40c0cb00000000, - 0xcfd032fd00000000, 0xda6125a600000000, 0x29f1d79000000000, - 0x2982ef7c00000000, 0xda121d4a00000000, 0xcfa30a1100000000, - 0x3c33f82700000000, 0xe5c125a700000000, 0x1651d79100000000, - 0x03e0c0ca00000000, 0xf07032fc00000000, 0x4200c1c900000000, - 0xb19033ff00000000, 0xa42124a400000000, 0x57b1d69200000000, - 0x8e430b1200000000, 0x7dd3f92400000000, 0x6862ee7f00000000, - 0x9bf21c4900000000, 0x9b8124a500000000, 0x6811d69300000000, - 0x7da0c1c800000000, 0x8e3033fe00000000, 0x57c2ee7e00000000, - 0xa4521c4800000000, 0xb1e30b1300000000, 0x4273f92500000000, - 0x9f0023a900000000, 0x6c90d19f00000000, 0x7921c6c400000000, - 0x8ab134f200000000, 0x5343e97200000000, 0xa0d31b4400000000, - 0xb5620c1f00000000, 0x46f2fe2900000000, 0x4681c6c500000000, - 0xb51134f300000000, 0xa0a023a800000000, 0x5330d19e00000000, - 0x8ac20c1e00000000, 0x7952fe2800000000, 0x6ce3e97300000000, - 0x9f731b4500000000, 0x2d03e87000000000, 0xde931a4600000000, - 0xcb220d1d00000000, 0x38b2ff2b00000000, 0xe14022ab00000000, - 0x12d0d09d00000000, 0x0761c7c600000000, 0xf4f135f000000000, - 0xf4820d1c00000000, 0x0712ff2a00000000, 0x12a3e87100000000, - 0xe1331a4700000000, 0x38c1c7c700000000, 0xcb5135f100000000, - 0xdee022aa00000000, 0x2d70d09c00000000, 0xba01c4c100000000, - 0x499136f700000000, 0x5c2021ac00000000, 0xafb0d39a00000000, - 0x76420e1a00000000, 0x85d2fc2c00000000, 0x9063eb7700000000, - 0x63f3194100000000, 0x638021ad00000000, 0x9010d39b00000000, - 0x85a1c4c000000000, 0x763136f600000000, 0xafc3eb7600000000, - 0x5c53194000000000, 0x49e20e1b00000000, 0xba72fc2d00000000, - 0x08020f1800000000, 0xfb92fd2e00000000, 0xee23ea7500000000, - 0x1db3184300000000, 0xc441c5c300000000, 0x37d137f500000000, - 0x226020ae00000000, 0xd1f0d29800000000, 0xd183ea7400000000, - 0x2213184200000000, 0x37a20f1900000000, 0xc432fd2f00000000, - 0x1dc020af00000000, 0xee50d29900000000, 0xfbe1c5c200000000, - 0x087137f400000000}, - {0x0000000000000000, 0x3651822400000000, 0x6ca2044900000000, - 0x5af3866d00000000, 0xd844099200000000, 0xee158bb600000000, - 0xb4e60ddb00000000, 0x82b78fff00000000, 0xf18f63ff00000000, - 0xc7dee1db00000000, 0x9d2d67b600000000, 0xab7ce59200000000, - 0x29cb6a6d00000000, 0x1f9ae84900000000, 0x45696e2400000000, - 0x7338ec0000000000, 0xa319b62500000000, 0x9548340100000000, - 0xcfbbb26c00000000, 0xf9ea304800000000, 0x7b5dbfb700000000, - 0x4d0c3d9300000000, 0x17ffbbfe00000000, 0x21ae39da00000000, - 0x5296d5da00000000, 0x64c757fe00000000, 0x3e34d19300000000, - 0x086553b700000000, 0x8ad2dc4800000000, 0xbc835e6c00000000, - 0xe670d80100000000, 0xd0215a2500000000, 0x46336c4b00000000, - 0x7062ee6f00000000, 0x2a91680200000000, 0x1cc0ea2600000000, - 0x9e7765d900000000, 0xa826e7fd00000000, 0xf2d5619000000000, - 0xc484e3b400000000, 0xb7bc0fb400000000, 0x81ed8d9000000000, - 0xdb1e0bfd00000000, 0xed4f89d900000000, 0x6ff8062600000000, - 0x59a9840200000000, 0x035a026f00000000, 0x350b804b00000000, - 0xe52ada6e00000000, 0xd37b584a00000000, 0x8988de2700000000, - 0xbfd95c0300000000, 0x3d6ed3fc00000000, 0x0b3f51d800000000, - 0x51ccd7b500000000, 0x679d559100000000, 0x14a5b99100000000, - 0x22f43bb500000000, 0x7807bdd800000000, 0x4e563ffc00000000, - 0xcce1b00300000000, 0xfab0322700000000, 0xa043b44a00000000, - 0x9612366e00000000, 0x8c66d89600000000, 0xba375ab200000000, - 0xe0c4dcdf00000000, 0xd6955efb00000000, 0x5422d10400000000, - 0x6273532000000000, 0x3880d54d00000000, 0x0ed1576900000000, - 0x7de9bb6900000000, 0x4bb8394d00000000, 0x114bbf2000000000, - 0x271a3d0400000000, 0xa5adb2fb00000000, 0x93fc30df00000000, - 0xc90fb6b200000000, 0xff5e349600000000, 0x2f7f6eb300000000, - 0x192eec9700000000, 0x43dd6afa00000000, 0x758ce8de00000000, - 0xf73b672100000000, 0xc16ae50500000000, 0x9b99636800000000, - 0xadc8e14c00000000, 0xdef00d4c00000000, 0xe8a18f6800000000, - 0xb252090500000000, 0x84038b2100000000, 0x06b404de00000000, - 0x30e586fa00000000, 0x6a16009700000000, 0x5c4782b300000000, - 0xca55b4dd00000000, 0xfc0436f900000000, 0xa6f7b09400000000, - 0x90a632b000000000, 0x1211bd4f00000000, 0x24403f6b00000000, - 0x7eb3b90600000000, 0x48e23b2200000000, 0x3bdad72200000000, - 0x0d8b550600000000, 0x5778d36b00000000, 0x6129514f00000000, - 0xe39edeb000000000, 0xd5cf5c9400000000, 0x8f3cdaf900000000, - 0xb96d58dd00000000, 0x694c02f800000000, 0x5f1d80dc00000000, - 0x05ee06b100000000, 0x33bf849500000000, 0xb1080b6a00000000, - 0x8759894e00000000, 0xddaa0f2300000000, 0xebfb8d0700000000, - 0x98c3610700000000, 0xae92e32300000000, 0xf461654e00000000, - 0xc230e76a00000000, 0x4087689500000000, 0x76d6eab100000000, - 0x2c256cdc00000000, 0x1a74eef800000000, 0x59cbc1f600000000, - 0x6f9a43d200000000, 0x3569c5bf00000000, 0x0338479b00000000, - 0x818fc86400000000, 0xb7de4a4000000000, 0xed2dcc2d00000000, - 0xdb7c4e0900000000, 0xa844a20900000000, 0x9e15202d00000000, - 0xc4e6a64000000000, 0xf2b7246400000000, 0x7000ab9b00000000, - 0x465129bf00000000, 0x1ca2afd200000000, 0x2af32df600000000, - 0xfad277d300000000, 0xcc83f5f700000000, 0x9670739a00000000, - 0xa021f1be00000000, 0x22967e4100000000, 0x14c7fc6500000000, - 0x4e347a0800000000, 0x7865f82c00000000, 0x0b5d142c00000000, - 0x3d0c960800000000, 0x67ff106500000000, 0x51ae924100000000, - 0xd3191dbe00000000, 0xe5489f9a00000000, 0xbfbb19f700000000, - 0x89ea9bd300000000, 0x1ff8adbd00000000, 0x29a92f9900000000, - 0x735aa9f400000000, 0x450b2bd000000000, 0xc7bca42f00000000, - 0xf1ed260b00000000, 0xab1ea06600000000, 0x9d4f224200000000, - 0xee77ce4200000000, 0xd8264c6600000000, 0x82d5ca0b00000000, - 0xb484482f00000000, 0x3633c7d000000000, 0x006245f400000000, - 0x5a91c39900000000, 0x6cc041bd00000000, 0xbce11b9800000000, - 0x8ab099bc00000000, 0xd0431fd100000000, 0xe6129df500000000, - 0x64a5120a00000000, 0x52f4902e00000000, 0x0807164300000000, - 0x3e56946700000000, 0x4d6e786700000000, 0x7b3ffa4300000000, - 0x21cc7c2e00000000, 0x179dfe0a00000000, 0x952a71f500000000, - 0xa37bf3d100000000, 0xf98875bc00000000, 0xcfd9f79800000000, - 0xd5ad196000000000, 0xe3fc9b4400000000, 0xb90f1d2900000000, - 0x8f5e9f0d00000000, 0x0de910f200000000, 0x3bb892d600000000, - 0x614b14bb00000000, 0x571a969f00000000, 0x24227a9f00000000, - 0x1273f8bb00000000, 0x48807ed600000000, 0x7ed1fcf200000000, - 0xfc66730d00000000, 0xca37f12900000000, 0x90c4774400000000, - 0xa695f56000000000, 0x76b4af4500000000, 0x40e52d6100000000, - 0x1a16ab0c00000000, 0x2c47292800000000, 0xaef0a6d700000000, - 0x98a124f300000000, 0xc252a29e00000000, 0xf40320ba00000000, - 0x873bccba00000000, 0xb16a4e9e00000000, 0xeb99c8f300000000, - 0xddc84ad700000000, 0x5f7fc52800000000, 0x692e470c00000000, - 0x33ddc16100000000, 0x058c434500000000, 0x939e752b00000000, - 0xa5cff70f00000000, 0xff3c716200000000, 0xc96df34600000000, - 0x4bda7cb900000000, 0x7d8bfe9d00000000, 0x277878f000000000, - 0x1129fad400000000, 0x621116d400000000, 0x544094f000000000, - 0x0eb3129d00000000, 0x38e290b900000000, 0xba551f4600000000, - 0x8c049d6200000000, 0xd6f71b0f00000000, 0xe0a6992b00000000, - 0x3087c30e00000000, 0x06d6412a00000000, 0x5c25c74700000000, - 0x6a74456300000000, 0xe8c3ca9c00000000, 0xde9248b800000000, - 0x8461ced500000000, 0xb2304cf100000000, 0xc108a0f100000000, - 0xf75922d500000000, 0xadaaa4b800000000, 0x9bfb269c00000000, - 0x194ca96300000000, 0x2f1d2b4700000000, 0x75eead2a00000000, - 0x43bf2f0e00000000}, - {0x0000000000000000, 0xc8179ecf00000000, 0xd1294d4400000000, - 0x193ed38b00000000, 0xa2539a8800000000, 0x6a44044700000000, - 0x737ad7cc00000000, 0xbb6d490300000000, 0x05a145ca00000000, - 0xcdb6db0500000000, 0xd488088e00000000, 0x1c9f964100000000, - 0xa7f2df4200000000, 0x6fe5418d00000000, 0x76db920600000000, - 0xbecc0cc900000000, 0x4b44fa4f00000000, 0x8353648000000000, - 0x9a6db70b00000000, 0x527a29c400000000, 0xe91760c700000000, - 0x2100fe0800000000, 0x383e2d8300000000, 0xf029b34c00000000, - 0x4ee5bf8500000000, 0x86f2214a00000000, 0x9fccf2c100000000, - 0x57db6c0e00000000, 0xecb6250d00000000, 0x24a1bbc200000000, - 0x3d9f684900000000, 0xf588f68600000000, 0x9688f49f00000000, - 0x5e9f6a5000000000, 0x47a1b9db00000000, 0x8fb6271400000000, - 0x34db6e1700000000, 0xfcccf0d800000000, 0xe5f2235300000000, - 0x2de5bd9c00000000, 0x9329b15500000000, 0x5b3e2f9a00000000, - 0x4200fc1100000000, 0x8a1762de00000000, 0x317a2bdd00000000, - 0xf96db51200000000, 0xe053669900000000, 0x2844f85600000000, - 0xddcc0ed000000000, 0x15db901f00000000, 0x0ce5439400000000, - 0xc4f2dd5b00000000, 0x7f9f945800000000, 0xb7880a9700000000, - 0xaeb6d91c00000000, 0x66a147d300000000, 0xd86d4b1a00000000, - 0x107ad5d500000000, 0x0944065e00000000, 0xc153989100000000, - 0x7a3ed19200000000, 0xb2294f5d00000000, 0xab179cd600000000, - 0x6300021900000000, 0x6d1798e400000000, 0xa500062b00000000, - 0xbc3ed5a000000000, 0x74294b6f00000000, 0xcf44026c00000000, - 0x07539ca300000000, 0x1e6d4f2800000000, 0xd67ad1e700000000, - 0x68b6dd2e00000000, 0xa0a143e100000000, 0xb99f906a00000000, - 0x71880ea500000000, 0xcae547a600000000, 0x02f2d96900000000, - 0x1bcc0ae200000000, 0xd3db942d00000000, 0x265362ab00000000, - 0xee44fc6400000000, 0xf77a2fef00000000, 0x3f6db12000000000, - 0x8400f82300000000, 0x4c1766ec00000000, 0x5529b56700000000, - 0x9d3e2ba800000000, 0x23f2276100000000, 0xebe5b9ae00000000, - 0xf2db6a2500000000, 0x3accf4ea00000000, 0x81a1bde900000000, - 0x49b6232600000000, 0x5088f0ad00000000, 0x989f6e6200000000, - 0xfb9f6c7b00000000, 0x3388f2b400000000, 0x2ab6213f00000000, - 0xe2a1bff000000000, 0x59ccf6f300000000, 0x91db683c00000000, - 0x88e5bbb700000000, 0x40f2257800000000, 0xfe3e29b100000000, - 0x3629b77e00000000, 0x2f1764f500000000, 0xe700fa3a00000000, - 0x5c6db33900000000, 0x947a2df600000000, 0x8d44fe7d00000000, - 0x455360b200000000, 0xb0db963400000000, 0x78cc08fb00000000, - 0x61f2db7000000000, 0xa9e545bf00000000, 0x12880cbc00000000, - 0xda9f927300000000, 0xc3a141f800000000, 0x0bb6df3700000000, - 0xb57ad3fe00000000, 0x7d6d4d3100000000, 0x64539eba00000000, - 0xac44007500000000, 0x1729497600000000, 0xdf3ed7b900000000, - 0xc600043200000000, 0x0e179afd00000000, 0x9b28411200000000, - 0x533fdfdd00000000, 0x4a010c5600000000, 0x8216929900000000, - 0x397bdb9a00000000, 0xf16c455500000000, 0xe85296de00000000, - 0x2045081100000000, 0x9e8904d800000000, 0x569e9a1700000000, - 0x4fa0499c00000000, 0x87b7d75300000000, 0x3cda9e5000000000, - 0xf4cd009f00000000, 0xedf3d31400000000, 0x25e44ddb00000000, - 0xd06cbb5d00000000, 0x187b259200000000, 0x0145f61900000000, - 0xc95268d600000000, 0x723f21d500000000, 0xba28bf1a00000000, - 0xa3166c9100000000, 0x6b01f25e00000000, 0xd5cdfe9700000000, - 0x1dda605800000000, 0x04e4b3d300000000, 0xccf32d1c00000000, - 0x779e641f00000000, 0xbf89fad000000000, 0xa6b7295b00000000, - 0x6ea0b79400000000, 0x0da0b58d00000000, 0xc5b72b4200000000, - 0xdc89f8c900000000, 0x149e660600000000, 0xaff32f0500000000, - 0x67e4b1ca00000000, 0x7eda624100000000, 0xb6cdfc8e00000000, - 0x0801f04700000000, 0xc0166e8800000000, 0xd928bd0300000000, - 0x113f23cc00000000, 0xaa526acf00000000, 0x6245f40000000000, - 0x7b7b278b00000000, 0xb36cb94400000000, 0x46e44fc200000000, - 0x8ef3d10d00000000, 0x97cd028600000000, 0x5fda9c4900000000, - 0xe4b7d54a00000000, 0x2ca04b8500000000, 0x359e980e00000000, - 0xfd8906c100000000, 0x43450a0800000000, 0x8b5294c700000000, - 0x926c474c00000000, 0x5a7bd98300000000, 0xe116908000000000, - 0x29010e4f00000000, 0x303fddc400000000, 0xf828430b00000000, - 0xf63fd9f600000000, 0x3e28473900000000, 0x271694b200000000, - 0xef010a7d00000000, 0x546c437e00000000, 0x9c7bddb100000000, - 0x85450e3a00000000, 0x4d5290f500000000, 0xf39e9c3c00000000, - 0x3b8902f300000000, 0x22b7d17800000000, 0xeaa04fb700000000, - 0x51cd06b400000000, 0x99da987b00000000, 0x80e44bf000000000, - 0x48f3d53f00000000, 0xbd7b23b900000000, 0x756cbd7600000000, - 0x6c526efd00000000, 0xa445f03200000000, 0x1f28b93100000000, - 0xd73f27fe00000000, 0xce01f47500000000, 0x06166aba00000000, - 0xb8da667300000000, 0x70cdf8bc00000000, 0x69f32b3700000000, - 0xa1e4b5f800000000, 0x1a89fcfb00000000, 0xd29e623400000000, - 0xcba0b1bf00000000, 0x03b72f7000000000, 0x60b72d6900000000, - 0xa8a0b3a600000000, 0xb19e602d00000000, 0x7989fee200000000, - 0xc2e4b7e100000000, 0x0af3292e00000000, 0x13cdfaa500000000, - 0xdbda646a00000000, 0x651668a300000000, 0xad01f66c00000000, - 0xb43f25e700000000, 0x7c28bb2800000000, 0xc745f22b00000000, - 0x0f526ce400000000, 0x166cbf6f00000000, 0xde7b21a000000000, - 0x2bf3d72600000000, 0xe3e449e900000000, 0xfada9a6200000000, - 0x32cd04ad00000000, 0x89a04dae00000000, 0x41b7d36100000000, - 0x588900ea00000000, 0x909e9e2500000000, 0x2e5292ec00000000, - 0xe6450c2300000000, 0xff7bdfa800000000, 0x376c416700000000, - 0x8c01086400000000, 0x441696ab00000000, 0x5d28452000000000, - 0x953fdbef00000000}, - {0x0000000000000000, 0x95d4709500000000, 0x6baf90f100000000, - 0xfe7be06400000000, 0x9758503800000000, 0x028c20ad00000000, - 0xfcf7c0c900000000, 0x6923b05c00000000, 0x2eb1a07000000000, - 0xbb65d0e500000000, 0x451e308100000000, 0xd0ca401400000000, - 0xb9e9f04800000000, 0x2c3d80dd00000000, 0xd24660b900000000, - 0x4792102c00000000, 0x5c6241e100000000, 0xc9b6317400000000, - 0x37cdd11000000000, 0xa219a18500000000, 0xcb3a11d900000000, - 0x5eee614c00000000, 0xa095812800000000, 0x3541f1bd00000000, - 0x72d3e19100000000, 0xe707910400000000, 0x197c716000000000, - 0x8ca801f500000000, 0xe58bb1a900000000, 0x705fc13c00000000, - 0x8e24215800000000, 0x1bf051cd00000000, 0xf9c2f31900000000, - 0x6c16838c00000000, 0x926d63e800000000, 0x07b9137d00000000, - 0x6e9aa32100000000, 0xfb4ed3b400000000, 0x053533d000000000, - 0x90e1434500000000, 0xd773536900000000, 0x42a723fc00000000, - 0xbcdcc39800000000, 0x2908b30d00000000, 0x402b035100000000, - 0xd5ff73c400000000, 0x2b8493a000000000, 0xbe50e33500000000, - 0xa5a0b2f800000000, 0x3074c26d00000000, 0xce0f220900000000, - 0x5bdb529c00000000, 0x32f8e2c000000000, 0xa72c925500000000, - 0x5957723100000000, 0xcc8302a400000000, 0x8b11128800000000, - 0x1ec5621d00000000, 0xe0be827900000000, 0x756af2ec00000000, - 0x1c4942b000000000, 0x899d322500000000, 0x77e6d24100000000, - 0xe232a2d400000000, 0xf285e73300000000, 0x675197a600000000, - 0x992a77c200000000, 0x0cfe075700000000, 0x65ddb70b00000000, - 0xf009c79e00000000, 0x0e7227fa00000000, 0x9ba6576f00000000, - 0xdc34474300000000, 0x49e037d600000000, 0xb79bd7b200000000, - 0x224fa72700000000, 0x4b6c177b00000000, 0xdeb867ee00000000, - 0x20c3878a00000000, 0xb517f71f00000000, 0xaee7a6d200000000, - 0x3b33d64700000000, 0xc548362300000000, 0x509c46b600000000, - 0x39bff6ea00000000, 0xac6b867f00000000, 0x5210661b00000000, - 0xc7c4168e00000000, 0x805606a200000000, 0x1582763700000000, - 0xebf9965300000000, 0x7e2de6c600000000, 0x170e569a00000000, - 0x82da260f00000000, 0x7ca1c66b00000000, 0xe975b6fe00000000, - 0x0b47142a00000000, 0x9e9364bf00000000, 0x60e884db00000000, - 0xf53cf44e00000000, 0x9c1f441200000000, 0x09cb348700000000, - 0xf7b0d4e300000000, 0x6264a47600000000, 0x25f6b45a00000000, - 0xb022c4cf00000000, 0x4e5924ab00000000, 0xdb8d543e00000000, - 0xb2aee46200000000, 0x277a94f700000000, 0xd901749300000000, - 0x4cd5040600000000, 0x572555cb00000000, 0xc2f1255e00000000, - 0x3c8ac53a00000000, 0xa95eb5af00000000, 0xc07d05f300000000, - 0x55a9756600000000, 0xabd2950200000000, 0x3e06e59700000000, - 0x7994f5bb00000000, 0xec40852e00000000, 0x123b654a00000000, - 0x87ef15df00000000, 0xeecca58300000000, 0x7b18d51600000000, - 0x8563357200000000, 0x10b745e700000000, 0xe40bcf6700000000, - 0x71dfbff200000000, 0x8fa45f9600000000, 0x1a702f0300000000, - 0x73539f5f00000000, 0xe687efca00000000, 0x18fc0fae00000000, - 0x8d287f3b00000000, 0xcaba6f1700000000, 0x5f6e1f8200000000, - 0xa115ffe600000000, 0x34c18f7300000000, 0x5de23f2f00000000, - 0xc8364fba00000000, 0x364dafde00000000, 0xa399df4b00000000, - 0xb8698e8600000000, 0x2dbdfe1300000000, 0xd3c61e7700000000, - 0x46126ee200000000, 0x2f31debe00000000, 0xbae5ae2b00000000, - 0x449e4e4f00000000, 0xd14a3eda00000000, 0x96d82ef600000000, - 0x030c5e6300000000, 0xfd77be0700000000, 0x68a3ce9200000000, - 0x01807ece00000000, 0x94540e5b00000000, 0x6a2fee3f00000000, - 0xfffb9eaa00000000, 0x1dc93c7e00000000, 0x881d4ceb00000000, - 0x7666ac8f00000000, 0xe3b2dc1a00000000, 0x8a916c4600000000, - 0x1f451cd300000000, 0xe13efcb700000000, 0x74ea8c2200000000, - 0x33789c0e00000000, 0xa6acec9b00000000, 0x58d70cff00000000, - 0xcd037c6a00000000, 0xa420cc3600000000, 0x31f4bca300000000, - 0xcf8f5cc700000000, 0x5a5b2c5200000000, 0x41ab7d9f00000000, - 0xd47f0d0a00000000, 0x2a04ed6e00000000, 0xbfd09dfb00000000, - 0xd6f32da700000000, 0x43275d3200000000, 0xbd5cbd5600000000, - 0x2888cdc300000000, 0x6f1addef00000000, 0xfacead7a00000000, - 0x04b54d1e00000000, 0x91613d8b00000000, 0xf8428dd700000000, - 0x6d96fd4200000000, 0x93ed1d2600000000, 0x06396db300000000, - 0x168e285400000000, 0x835a58c100000000, 0x7d21b8a500000000, - 0xe8f5c83000000000, 0x81d6786c00000000, 0x140208f900000000, - 0xea79e89d00000000, 0x7fad980800000000, 0x383f882400000000, - 0xadebf8b100000000, 0x539018d500000000, 0xc644684000000000, - 0xaf67d81c00000000, 0x3ab3a88900000000, 0xc4c848ed00000000, - 0x511c387800000000, 0x4aec69b500000000, 0xdf38192000000000, - 0x2143f94400000000, 0xb49789d100000000, 0xddb4398d00000000, - 0x4860491800000000, 0xb61ba97c00000000, 0x23cfd9e900000000, - 0x645dc9c500000000, 0xf189b95000000000, 0x0ff2593400000000, - 0x9a2629a100000000, 0xf30599fd00000000, 0x66d1e96800000000, - 0x98aa090c00000000, 0x0d7e799900000000, 0xef4cdb4d00000000, - 0x7a98abd800000000, 0x84e34bbc00000000, 0x11373b2900000000, - 0x78148b7500000000, 0xedc0fbe000000000, 0x13bb1b8400000000, - 0x866f6b1100000000, 0xc1fd7b3d00000000, 0x54290ba800000000, - 0xaa52ebcc00000000, 0x3f869b5900000000, 0x56a52b0500000000, - 0xc3715b9000000000, 0x3d0abbf400000000, 0xa8decb6100000000, - 0xb32e9aac00000000, 0x26faea3900000000, 0xd8810a5d00000000, - 0x4d557ac800000000, 0x2476ca9400000000, 0xb1a2ba0100000000, - 0x4fd95a6500000000, 0xda0d2af000000000, 0x9d9f3adc00000000, - 0x084b4a4900000000, 0xf630aa2d00000000, 0x63e4dab800000000, - 0x0ac76ae400000000, 0x9f131a7100000000, 0x6168fa1500000000, - 0xf4bc8a8000000000}, - {0x0000000000000000, 0x1f17f08000000000, 0x7f2891da00000000, - 0x603f615a00000000, 0xbf56536e00000000, 0xa041a3ee00000000, - 0xc07ec2b400000000, 0xdf69323400000000, 0x7eada6dc00000000, - 0x61ba565c00000000, 0x0185370600000000, 0x1e92c78600000000, - 0xc1fbf5b200000000, 0xdeec053200000000, 0xbed3646800000000, - 0xa1c494e800000000, 0xbd5c3c6200000000, 0xa24bcce200000000, - 0xc274adb800000000, 0xdd635d3800000000, 0x020a6f0c00000000, - 0x1d1d9f8c00000000, 0x7d22fed600000000, 0x62350e5600000000, - 0xc3f19abe00000000, 0xdce66a3e00000000, 0xbcd90b6400000000, - 0xa3cefbe400000000, 0x7ca7c9d000000000, 0x63b0395000000000, - 0x038f580a00000000, 0x1c98a88a00000000, 0x7ab978c400000000, - 0x65ae884400000000, 0x0591e91e00000000, 0x1a86199e00000000, - 0xc5ef2baa00000000, 0xdaf8db2a00000000, 0xbac7ba7000000000, - 0xa5d04af000000000, 0x0414de1800000000, 0x1b032e9800000000, - 0x7b3c4fc200000000, 0x642bbf4200000000, 0xbb428d7600000000, - 0xa4557df600000000, 0xc46a1cac00000000, 0xdb7dec2c00000000, - 0xc7e544a600000000, 0xd8f2b42600000000, 0xb8cdd57c00000000, - 0xa7da25fc00000000, 0x78b317c800000000, 0x67a4e74800000000, - 0x079b861200000000, 0x188c769200000000, 0xb948e27a00000000, - 0xa65f12fa00000000, 0xc66073a000000000, 0xd977832000000000, - 0x061eb11400000000, 0x1909419400000000, 0x793620ce00000000, - 0x6621d04e00000000, 0xb574805300000000, 0xaa6370d300000000, - 0xca5c118900000000, 0xd54be10900000000, 0x0a22d33d00000000, - 0x153523bd00000000, 0x750a42e700000000, 0x6a1db26700000000, - 0xcbd9268f00000000, 0xd4ced60f00000000, 0xb4f1b75500000000, - 0xabe647d500000000, 0x748f75e100000000, 0x6b98856100000000, - 0x0ba7e43b00000000, 0x14b014bb00000000, 0x0828bc3100000000, - 0x173f4cb100000000, 0x77002deb00000000, 0x6817dd6b00000000, - 0xb77eef5f00000000, 0xa8691fdf00000000, 0xc8567e8500000000, - 0xd7418e0500000000, 0x76851aed00000000, 0x6992ea6d00000000, - 0x09ad8b3700000000, 0x16ba7bb700000000, 0xc9d3498300000000, - 0xd6c4b90300000000, 0xb6fbd85900000000, 0xa9ec28d900000000, - 0xcfcdf89700000000, 0xd0da081700000000, 0xb0e5694d00000000, - 0xaff299cd00000000, 0x709babf900000000, 0x6f8c5b7900000000, - 0x0fb33a2300000000, 0x10a4caa300000000, 0xb1605e4b00000000, - 0xae77aecb00000000, 0xce48cf9100000000, 0xd15f3f1100000000, - 0x0e360d2500000000, 0x1121fda500000000, 0x711e9cff00000000, - 0x6e096c7f00000000, 0x7291c4f500000000, 0x6d86347500000000, - 0x0db9552f00000000, 0x12aea5af00000000, 0xcdc7979b00000000, - 0xd2d0671b00000000, 0xb2ef064100000000, 0xadf8f6c100000000, - 0x0c3c622900000000, 0x132b92a900000000, 0x7314f3f300000000, - 0x6c03037300000000, 0xb36a314700000000, 0xac7dc1c700000000, - 0xcc42a09d00000000, 0xd355501d00000000, 0x6ae900a700000000, - 0x75fef02700000000, 0x15c1917d00000000, 0x0ad661fd00000000, - 0xd5bf53c900000000, 0xcaa8a34900000000, 0xaa97c21300000000, - 0xb580329300000000, 0x1444a67b00000000, 0x0b5356fb00000000, - 0x6b6c37a100000000, 0x747bc72100000000, 0xab12f51500000000, - 0xb405059500000000, 0xd43a64cf00000000, 0xcb2d944f00000000, - 0xd7b53cc500000000, 0xc8a2cc4500000000, 0xa89dad1f00000000, - 0xb78a5d9f00000000, 0x68e36fab00000000, 0x77f49f2b00000000, - 0x17cbfe7100000000, 0x08dc0ef100000000, 0xa9189a1900000000, - 0xb60f6a9900000000, 0xd6300bc300000000, 0xc927fb4300000000, - 0x164ec97700000000, 0x095939f700000000, 0x696658ad00000000, - 0x7671a82d00000000, 0x1050786300000000, 0x0f4788e300000000, - 0x6f78e9b900000000, 0x706f193900000000, 0xaf062b0d00000000, - 0xb011db8d00000000, 0xd02ebad700000000, 0xcf394a5700000000, - 0x6efddebf00000000, 0x71ea2e3f00000000, 0x11d54f6500000000, - 0x0ec2bfe500000000, 0xd1ab8dd100000000, 0xcebc7d5100000000, - 0xae831c0b00000000, 0xb194ec8b00000000, 0xad0c440100000000, - 0xb21bb48100000000, 0xd224d5db00000000, 0xcd33255b00000000, - 0x125a176f00000000, 0x0d4de7ef00000000, 0x6d7286b500000000, - 0x7265763500000000, 0xd3a1e2dd00000000, 0xccb6125d00000000, - 0xac89730700000000, 0xb39e838700000000, 0x6cf7b1b300000000, - 0x73e0413300000000, 0x13df206900000000, 0x0cc8d0e900000000, - 0xdf9d80f400000000, 0xc08a707400000000, 0xa0b5112e00000000, - 0xbfa2e1ae00000000, 0x60cbd39a00000000, 0x7fdc231a00000000, - 0x1fe3424000000000, 0x00f4b2c000000000, 0xa130262800000000, - 0xbe27d6a800000000, 0xde18b7f200000000, 0xc10f477200000000, - 0x1e66754600000000, 0x017185c600000000, 0x614ee49c00000000, - 0x7e59141c00000000, 0x62c1bc9600000000, 0x7dd64c1600000000, - 0x1de92d4c00000000, 0x02feddcc00000000, 0xdd97eff800000000, - 0xc2801f7800000000, 0xa2bf7e2200000000, 0xbda88ea200000000, - 0x1c6c1a4a00000000, 0x037beaca00000000, 0x63448b9000000000, - 0x7c537b1000000000, 0xa33a492400000000, 0xbc2db9a400000000, - 0xdc12d8fe00000000, 0xc305287e00000000, 0xa524f83000000000, - 0xba3308b000000000, 0xda0c69ea00000000, 0xc51b996a00000000, - 0x1a72ab5e00000000, 0x05655bde00000000, 0x655a3a8400000000, - 0x7a4dca0400000000, 0xdb895eec00000000, 0xc49eae6c00000000, - 0xa4a1cf3600000000, 0xbbb63fb600000000, 0x64df0d8200000000, - 0x7bc8fd0200000000, 0x1bf79c5800000000, 0x04e06cd800000000, - 0x1878c45200000000, 0x076f34d200000000, 0x6750558800000000, - 0x7847a50800000000, 0xa72e973c00000000, 0xb83967bc00000000, - 0xd80606e600000000, 0xc711f66600000000, 0x66d5628e00000000, - 0x79c2920e00000000, 0x19fdf35400000000, 0x06ea03d400000000, - 0xd98331e000000000, 0xc694c16000000000, 0xa6aba03a00000000, - 0xb9bc50ba00000000}, - {0x0000000000000000, 0xe2fd888d00000000, 0x85fd60c000000000, - 0x6700e84d00000000, 0x4bfdb05b00000000, 0xa90038d600000000, - 0xce00d09b00000000, 0x2cfd581600000000, 0x96fa61b700000000, - 0x7407e93a00000000, 0x1307017700000000, 0xf1fa89fa00000000, - 0xdd07d1ec00000000, 0x3ffa596100000000, 0x58fab12c00000000, - 0xba0739a100000000, 0x6df3b2b500000000, 0x8f0e3a3800000000, - 0xe80ed27500000000, 0x0af35af800000000, 0x260e02ee00000000, - 0xc4f38a6300000000, 0xa3f3622e00000000, 0x410eeaa300000000, - 0xfb09d30200000000, 0x19f45b8f00000000, 0x7ef4b3c200000000, - 0x9c093b4f00000000, 0xb0f4635900000000, 0x5209ebd400000000, - 0x3509039900000000, 0xd7f48b1400000000, 0x9be014b000000000, - 0x791d9c3d00000000, 0x1e1d747000000000, 0xfce0fcfd00000000, - 0xd01da4eb00000000, 0x32e02c6600000000, 0x55e0c42b00000000, - 0xb71d4ca600000000, 0x0d1a750700000000, 0xefe7fd8a00000000, - 0x88e715c700000000, 0x6a1a9d4a00000000, 0x46e7c55c00000000, - 0xa41a4dd100000000, 0xc31aa59c00000000, 0x21e72d1100000000, - 0xf613a60500000000, 0x14ee2e8800000000, 0x73eec6c500000000, - 0x91134e4800000000, 0xbdee165e00000000, 0x5f139ed300000000, - 0x3813769e00000000, 0xdaeefe1300000000, 0x60e9c7b200000000, - 0x82144f3f00000000, 0xe514a77200000000, 0x07e92fff00000000, - 0x2b1477e900000000, 0xc9e9ff6400000000, 0xaee9172900000000, - 0x4c149fa400000000, 0x77c758bb00000000, 0x953ad03600000000, - 0xf23a387b00000000, 0x10c7b0f600000000, 0x3c3ae8e000000000, - 0xdec7606d00000000, 0xb9c7882000000000, 0x5b3a00ad00000000, - 0xe13d390c00000000, 0x03c0b18100000000, 0x64c059cc00000000, - 0x863dd14100000000, 0xaac0895700000000, 0x483d01da00000000, - 0x2f3de99700000000, 0xcdc0611a00000000, 0x1a34ea0e00000000, - 0xf8c9628300000000, 0x9fc98ace00000000, 0x7d34024300000000, - 0x51c95a5500000000, 0xb334d2d800000000, 0xd4343a9500000000, - 0x36c9b21800000000, 0x8cce8bb900000000, 0x6e33033400000000, - 0x0933eb7900000000, 0xebce63f400000000, 0xc7333be200000000, - 0x25ceb36f00000000, 0x42ce5b2200000000, 0xa033d3af00000000, - 0xec274c0b00000000, 0x0edac48600000000, 0x69da2ccb00000000, - 0x8b27a44600000000, 0xa7dafc5000000000, 0x452774dd00000000, - 0x22279c9000000000, 0xc0da141d00000000, 0x7add2dbc00000000, - 0x9820a53100000000, 0xff204d7c00000000, 0x1dddc5f100000000, - 0x31209de700000000, 0xd3dd156a00000000, 0xb4ddfd2700000000, - 0x562075aa00000000, 0x81d4febe00000000, 0x6329763300000000, - 0x04299e7e00000000, 0xe6d416f300000000, 0xca294ee500000000, - 0x28d4c66800000000, 0x4fd42e2500000000, 0xad29a6a800000000, - 0x172e9f0900000000, 0xf5d3178400000000, 0x92d3ffc900000000, - 0x702e774400000000, 0x5cd32f5200000000, 0xbe2ea7df00000000, - 0xd92e4f9200000000, 0x3bd3c71f00000000, 0xaf88c0ad00000000, - 0x4d75482000000000, 0x2a75a06d00000000, 0xc88828e000000000, - 0xe47570f600000000, 0x0688f87b00000000, 0x6188103600000000, - 0x837598bb00000000, 0x3972a11a00000000, 0xdb8f299700000000, - 0xbc8fc1da00000000, 0x5e72495700000000, 0x728f114100000000, - 0x907299cc00000000, 0xf772718100000000, 0x158ff90c00000000, - 0xc27b721800000000, 0x2086fa9500000000, 0x478612d800000000, - 0xa57b9a5500000000, 0x8986c24300000000, 0x6b7b4ace00000000, - 0x0c7ba28300000000, 0xee862a0e00000000, 0x548113af00000000, - 0xb67c9b2200000000, 0xd17c736f00000000, 0x3381fbe200000000, - 0x1f7ca3f400000000, 0xfd812b7900000000, 0x9a81c33400000000, - 0x787c4bb900000000, 0x3468d41d00000000, 0xd6955c9000000000, - 0xb195b4dd00000000, 0x53683c5000000000, 0x7f95644600000000, - 0x9d68eccb00000000, 0xfa68048600000000, 0x18958c0b00000000, - 0xa292b5aa00000000, 0x406f3d2700000000, 0x276fd56a00000000, - 0xc5925de700000000, 0xe96f05f100000000, 0x0b928d7c00000000, - 0x6c92653100000000, 0x8e6fedbc00000000, 0x599b66a800000000, - 0xbb66ee2500000000, 0xdc66066800000000, 0x3e9b8ee500000000, - 0x1266d6f300000000, 0xf09b5e7e00000000, 0x979bb63300000000, - 0x75663ebe00000000, 0xcf61071f00000000, 0x2d9c8f9200000000, - 0x4a9c67df00000000, 0xa861ef5200000000, 0x849cb74400000000, - 0x66613fc900000000, 0x0161d78400000000, 0xe39c5f0900000000, - 0xd84f981600000000, 0x3ab2109b00000000, 0x5db2f8d600000000, - 0xbf4f705b00000000, 0x93b2284d00000000, 0x714fa0c000000000, - 0x164f488d00000000, 0xf4b2c00000000000, 0x4eb5f9a100000000, - 0xac48712c00000000, 0xcb48996100000000, 0x29b511ec00000000, - 0x054849fa00000000, 0xe7b5c17700000000, 0x80b5293a00000000, - 0x6248a1b700000000, 0xb5bc2aa300000000, 0x5741a22e00000000, - 0x30414a6300000000, 0xd2bcc2ee00000000, 0xfe419af800000000, - 0x1cbc127500000000, 0x7bbcfa3800000000, 0x994172b500000000, - 0x23464b1400000000, 0xc1bbc39900000000, 0xa6bb2bd400000000, - 0x4446a35900000000, 0x68bbfb4f00000000, 0x8a4673c200000000, - 0xed469b8f00000000, 0x0fbb130200000000, 0x43af8ca600000000, - 0xa152042b00000000, 0xc652ec6600000000, 0x24af64eb00000000, - 0x08523cfd00000000, 0xeaafb47000000000, 0x8daf5c3d00000000, - 0x6f52d4b000000000, 0xd555ed1100000000, 0x37a8659c00000000, - 0x50a88dd100000000, 0xb255055c00000000, 0x9ea85d4a00000000, - 0x7c55d5c700000000, 0x1b553d8a00000000, 0xf9a8b50700000000, - 0x2e5c3e1300000000, 0xcca1b69e00000000, 0xaba15ed300000000, - 0x495cd65e00000000, 0x65a18e4800000000, 0x875c06c500000000, - 0xe05cee8800000000, 0x02a1660500000000, 0xb8a65fa400000000, - 0x5a5bd72900000000, 0x3d5b3f6400000000, 0xdfa6b7e900000000, - 0xf35befff00000000, 0x11a6677200000000, 0x76a68f3f00000000, - 0x945b07b200000000}, - {0x0000000000000000, 0xa90b894e00000000, 0x5217129d00000000, - 0xfb1c9bd300000000, 0xe52855e100000000, 0x4c23dcaf00000000, - 0xb73f477c00000000, 0x1e34ce3200000000, 0x8b57db1900000000, - 0x225c525700000000, 0xd940c98400000000, 0x704b40ca00000000, - 0x6e7f8ef800000000, 0xc77407b600000000, 0x3c689c6500000000, - 0x9563152b00000000, 0x16afb63300000000, 0xbfa43f7d00000000, - 0x44b8a4ae00000000, 0xedb32de000000000, 0xf387e3d200000000, - 0x5a8c6a9c00000000, 0xa190f14f00000000, 0x089b780100000000, - 0x9df86d2a00000000, 0x34f3e46400000000, 0xcfef7fb700000000, - 0x66e4f6f900000000, 0x78d038cb00000000, 0xd1dbb18500000000, - 0x2ac72a5600000000, 0x83cca31800000000, 0x2c5e6d6700000000, - 0x8555e42900000000, 0x7e497ffa00000000, 0xd742f6b400000000, - 0xc976388600000000, 0x607db1c800000000, 0x9b612a1b00000000, - 0x326aa35500000000, 0xa709b67e00000000, 0x0e023f3000000000, - 0xf51ea4e300000000, 0x5c152dad00000000, 0x4221e39f00000000, - 0xeb2a6ad100000000, 0x1036f10200000000, 0xb93d784c00000000, - 0x3af1db5400000000, 0x93fa521a00000000, 0x68e6c9c900000000, - 0xc1ed408700000000, 0xdfd98eb500000000, 0x76d207fb00000000, - 0x8dce9c2800000000, 0x24c5156600000000, 0xb1a6004d00000000, - 0x18ad890300000000, 0xe3b112d000000000, 0x4aba9b9e00000000, - 0x548e55ac00000000, 0xfd85dce200000000, 0x0699473100000000, - 0xaf92ce7f00000000, 0x58bcdace00000000, 0xf1b7538000000000, - 0x0aabc85300000000, 0xa3a0411d00000000, 0xbd948f2f00000000, - 0x149f066100000000, 0xef839db200000000, 0x468814fc00000000, - 0xd3eb01d700000000, 0x7ae0889900000000, 0x81fc134a00000000, - 0x28f79a0400000000, 0x36c3543600000000, 0x9fc8dd7800000000, - 0x64d446ab00000000, 0xcddfcfe500000000, 0x4e136cfd00000000, - 0xe718e5b300000000, 0x1c047e6000000000, 0xb50ff72e00000000, - 0xab3b391c00000000, 0x0230b05200000000, 0xf92c2b8100000000, - 0x5027a2cf00000000, 0xc544b7e400000000, 0x6c4f3eaa00000000, - 0x9753a57900000000, 0x3e582c3700000000, 0x206ce20500000000, - 0x89676b4b00000000, 0x727bf09800000000, 0xdb7079d600000000, - 0x74e2b7a900000000, 0xdde93ee700000000, 0x26f5a53400000000, - 0x8ffe2c7a00000000, 0x91cae24800000000, 0x38c16b0600000000, - 0xc3ddf0d500000000, 0x6ad6799b00000000, 0xffb56cb000000000, - 0x56bee5fe00000000, 0xada27e2d00000000, 0x04a9f76300000000, - 0x1a9d395100000000, 0xb396b01f00000000, 0x488a2bcc00000000, - 0xe181a28200000000, 0x624d019a00000000, 0xcb4688d400000000, - 0x305a130700000000, 0x99519a4900000000, 0x8765547b00000000, - 0x2e6edd3500000000, 0xd57246e600000000, 0x7c79cfa800000000, - 0xe91ada8300000000, 0x401153cd00000000, 0xbb0dc81e00000000, - 0x1206415000000000, 0x0c328f6200000000, 0xa539062c00000000, - 0x5e259dff00000000, 0xf72e14b100000000, 0xf17ec44600000000, - 0x58754d0800000000, 0xa369d6db00000000, 0x0a625f9500000000, - 0x145691a700000000, 0xbd5d18e900000000, 0x4641833a00000000, - 0xef4a0a7400000000, 0x7a291f5f00000000, 0xd322961100000000, - 0x283e0dc200000000, 0x8135848c00000000, 0x9f014abe00000000, - 0x360ac3f000000000, 0xcd16582300000000, 0x641dd16d00000000, - 0xe7d1727500000000, 0x4edafb3b00000000, 0xb5c660e800000000, - 0x1ccde9a600000000, 0x02f9279400000000, 0xabf2aeda00000000, - 0x50ee350900000000, 0xf9e5bc4700000000, 0x6c86a96c00000000, - 0xc58d202200000000, 0x3e91bbf100000000, 0x979a32bf00000000, - 0x89aefc8d00000000, 0x20a575c300000000, 0xdbb9ee1000000000, - 0x72b2675e00000000, 0xdd20a92100000000, 0x742b206f00000000, - 0x8f37bbbc00000000, 0x263c32f200000000, 0x3808fcc000000000, - 0x9103758e00000000, 0x6a1fee5d00000000, 0xc314671300000000, - 0x5677723800000000, 0xff7cfb7600000000, 0x046060a500000000, - 0xad6be9eb00000000, 0xb35f27d900000000, 0x1a54ae9700000000, - 0xe148354400000000, 0x4843bc0a00000000, 0xcb8f1f1200000000, - 0x6284965c00000000, 0x99980d8f00000000, 0x309384c100000000, - 0x2ea74af300000000, 0x87acc3bd00000000, 0x7cb0586e00000000, - 0xd5bbd12000000000, 0x40d8c40b00000000, 0xe9d34d4500000000, - 0x12cfd69600000000, 0xbbc45fd800000000, 0xa5f091ea00000000, - 0x0cfb18a400000000, 0xf7e7837700000000, 0x5eec0a3900000000, - 0xa9c21e8800000000, 0x00c997c600000000, 0xfbd50c1500000000, - 0x52de855b00000000, 0x4cea4b6900000000, 0xe5e1c22700000000, - 0x1efd59f400000000, 0xb7f6d0ba00000000, 0x2295c59100000000, - 0x8b9e4cdf00000000, 0x7082d70c00000000, 0xd9895e4200000000, - 0xc7bd907000000000, 0x6eb6193e00000000, 0x95aa82ed00000000, - 0x3ca10ba300000000, 0xbf6da8bb00000000, 0x166621f500000000, - 0xed7aba2600000000, 0x4471336800000000, 0x5a45fd5a00000000, - 0xf34e741400000000, 0x0852efc700000000, 0xa159668900000000, - 0x343a73a200000000, 0x9d31faec00000000, 0x662d613f00000000, - 0xcf26e87100000000, 0xd112264300000000, 0x7819af0d00000000, - 0x830534de00000000, 0x2a0ebd9000000000, 0x859c73ef00000000, - 0x2c97faa100000000, 0xd78b617200000000, 0x7e80e83c00000000, - 0x60b4260e00000000, 0xc9bfaf4000000000, 0x32a3349300000000, - 0x9ba8bddd00000000, 0x0ecba8f600000000, 0xa7c021b800000000, - 0x5cdcba6b00000000, 0xf5d7332500000000, 0xebe3fd1700000000, - 0x42e8745900000000, 0xb9f4ef8a00000000, 0x10ff66c400000000, - 0x9333c5dc00000000, 0x3a384c9200000000, 0xc124d74100000000, - 0x682f5e0f00000000, 0x761b903d00000000, 0xdf10197300000000, - 0x240c82a000000000, 0x8d070bee00000000, 0x18641ec500000000, - 0xb16f978b00000000, 0x4a730c5800000000, 0xe378851600000000, - 0xfd4c4b2400000000, 0x5447c26a00000000, 0xaf5b59b900000000, - 0x0650d0f700000000}, - {0x0000000000000000, 0x479244af00000000, 0xcf22f88500000000, - 0x88b0bc2a00000000, 0xdf4381d000000000, 0x98d1c57f00000000, - 0x1061795500000000, 0x57f33dfa00000000, 0xff81737a00000000, - 0xb81337d500000000, 0x30a38bff00000000, 0x7731cf5000000000, - 0x20c2f2aa00000000, 0x6750b60500000000, 0xefe00a2f00000000, - 0xa8724e8000000000, 0xfe03e7f400000000, 0xb991a35b00000000, - 0x31211f7100000000, 0x76b35bde00000000, 0x2140662400000000, - 0x66d2228b00000000, 0xee629ea100000000, 0xa9f0da0e00000000, - 0x0182948e00000000, 0x4610d02100000000, 0xcea06c0b00000000, - 0x893228a400000000, 0xdec1155e00000000, 0x995351f100000000, - 0x11e3eddb00000000, 0x5671a97400000000, 0xbd01bf3200000000, - 0xfa93fb9d00000000, 0x722347b700000000, 0x35b1031800000000, - 0x62423ee200000000, 0x25d07a4d00000000, 0xad60c66700000000, - 0xeaf282c800000000, 0x4280cc4800000000, 0x051288e700000000, - 0x8da234cd00000000, 0xca30706200000000, 0x9dc34d9800000000, - 0xda51093700000000, 0x52e1b51d00000000, 0x1573f1b200000000, - 0x430258c600000000, 0x04901c6900000000, 0x8c20a04300000000, - 0xcbb2e4ec00000000, 0x9c41d91600000000, 0xdbd39db900000000, - 0x5363219300000000, 0x14f1653c00000000, 0xbc832bbc00000000, - 0xfb116f1300000000, 0x73a1d33900000000, 0x3433979600000000, - 0x63c0aa6c00000000, 0x2452eec300000000, 0xace252e900000000, - 0xeb70164600000000, 0x7a037e6500000000, 0x3d913aca00000000, - 0xb52186e000000000, 0xf2b3c24f00000000, 0xa540ffb500000000, - 0xe2d2bb1a00000000, 0x6a62073000000000, 0x2df0439f00000000, - 0x85820d1f00000000, 0xc21049b000000000, 0x4aa0f59a00000000, - 0x0d32b13500000000, 0x5ac18ccf00000000, 0x1d53c86000000000, - 0x95e3744a00000000, 0xd27130e500000000, 0x8400999100000000, - 0xc392dd3e00000000, 0x4b22611400000000, 0x0cb025bb00000000, - 0x5b43184100000000, 0x1cd15cee00000000, 0x9461e0c400000000, - 0xd3f3a46b00000000, 0x7b81eaeb00000000, 0x3c13ae4400000000, - 0xb4a3126e00000000, 0xf33156c100000000, 0xa4c26b3b00000000, - 0xe3502f9400000000, 0x6be093be00000000, 0x2c72d71100000000, - 0xc702c15700000000, 0x809085f800000000, 0x082039d200000000, - 0x4fb27d7d00000000, 0x1841408700000000, 0x5fd3042800000000, - 0xd763b80200000000, 0x90f1fcad00000000, 0x3883b22d00000000, - 0x7f11f68200000000, 0xf7a14aa800000000, 0xb0330e0700000000, - 0xe7c033fd00000000, 0xa052775200000000, 0x28e2cb7800000000, - 0x6f708fd700000000, 0x390126a300000000, 0x7e93620c00000000, - 0xf623de2600000000, 0xb1b19a8900000000, 0xe642a77300000000, - 0xa1d0e3dc00000000, 0x29605ff600000000, 0x6ef21b5900000000, - 0xc68055d900000000, 0x8112117600000000, 0x09a2ad5c00000000, - 0x4e30e9f300000000, 0x19c3d40900000000, 0x5e5190a600000000, - 0xd6e12c8c00000000, 0x9173682300000000, 0xf406fcca00000000, - 0xb394b86500000000, 0x3b24044f00000000, 0x7cb640e000000000, - 0x2b457d1a00000000, 0x6cd739b500000000, 0xe467859f00000000, - 0xa3f5c13000000000, 0x0b878fb000000000, 0x4c15cb1f00000000, - 0xc4a5773500000000, 0x8337339a00000000, 0xd4c40e6000000000, - 0x93564acf00000000, 0x1be6f6e500000000, 0x5c74b24a00000000, - 0x0a051b3e00000000, 0x4d975f9100000000, 0xc527e3bb00000000, - 0x82b5a71400000000, 0xd5469aee00000000, 0x92d4de4100000000, - 0x1a64626b00000000, 0x5df626c400000000, 0xf584684400000000, - 0xb2162ceb00000000, 0x3aa690c100000000, 0x7d34d46e00000000, - 0x2ac7e99400000000, 0x6d55ad3b00000000, 0xe5e5111100000000, - 0xa27755be00000000, 0x490743f800000000, 0x0e95075700000000, - 0x8625bb7d00000000, 0xc1b7ffd200000000, 0x9644c22800000000, - 0xd1d6868700000000, 0x59663aad00000000, 0x1ef47e0200000000, - 0xb686308200000000, 0xf114742d00000000, 0x79a4c80700000000, - 0x3e368ca800000000, 0x69c5b15200000000, 0x2e57f5fd00000000, - 0xa6e749d700000000, 0xe1750d7800000000, 0xb704a40c00000000, - 0xf096e0a300000000, 0x78265c8900000000, 0x3fb4182600000000, - 0x684725dc00000000, 0x2fd5617300000000, 0xa765dd5900000000, - 0xe0f799f600000000, 0x4885d77600000000, 0x0f1793d900000000, - 0x87a72ff300000000, 0xc0356b5c00000000, 0x97c656a600000000, - 0xd054120900000000, 0x58e4ae2300000000, 0x1f76ea8c00000000, - 0x8e0582af00000000, 0xc997c60000000000, 0x41277a2a00000000, - 0x06b53e8500000000, 0x5146037f00000000, 0x16d447d000000000, - 0x9e64fbfa00000000, 0xd9f6bf5500000000, 0x7184f1d500000000, - 0x3616b57a00000000, 0xbea6095000000000, 0xf9344dff00000000, - 0xaec7700500000000, 0xe95534aa00000000, 0x61e5888000000000, - 0x2677cc2f00000000, 0x7006655b00000000, 0x379421f400000000, - 0xbf249dde00000000, 0xf8b6d97100000000, 0xaf45e48b00000000, - 0xe8d7a02400000000, 0x60671c0e00000000, 0x27f558a100000000, - 0x8f87162100000000, 0xc815528e00000000, 0x40a5eea400000000, - 0x0737aa0b00000000, 0x50c497f100000000, 0x1756d35e00000000, - 0x9fe66f7400000000, 0xd8742bdb00000000, 0x33043d9d00000000, - 0x7496793200000000, 0xfc26c51800000000, 0xbbb481b700000000, - 0xec47bc4d00000000, 0xabd5f8e200000000, 0x236544c800000000, - 0x64f7006700000000, 0xcc854ee700000000, 0x8b170a4800000000, - 0x03a7b66200000000, 0x4435f2cd00000000, 0x13c6cf3700000000, - 0x54548b9800000000, 0xdce437b200000000, 0x9b76731d00000000, - 0xcd07da6900000000, 0x8a959ec600000000, 0x022522ec00000000, - 0x45b7664300000000, 0x12445bb900000000, 0x55d61f1600000000, - 0xdd66a33c00000000, 0x9af4e79300000000, 0x3286a91300000000, - 0x7514edbc00000000, 0xfda4519600000000, 0xba36153900000000, - 0xedc528c300000000, 0xaa576c6c00000000, 0x22e7d04600000000, - 0x657594e900000000}}; - -#else /* W == 4 */ - -local const z_crc_t FAR crc_braid_table[][256] = { - {0x00000000, 0x65673b46, 0xcace768c, 0xafa94dca, 0x4eedeb59, - 0x2b8ad01f, 0x84239dd5, 0xe144a693, 0x9ddbd6b2, 0xf8bcedf4, - 0x5715a03e, 0x32729b78, 0xd3363deb, 0xb65106ad, 0x19f84b67, - 0x7c9f7021, 0xe0c6ab25, 0x85a19063, 0x2a08dda9, 0x4f6fe6ef, - 0xae2b407c, 0xcb4c7b3a, 0x64e536f0, 0x01820db6, 0x7d1d7d97, - 0x187a46d1, 0xb7d30b1b, 0xd2b4305d, 0x33f096ce, 0x5697ad88, - 0xf93ee042, 0x9c59db04, 0x1afc500b, 0x7f9b6b4d, 0xd0322687, - 0xb5551dc1, 0x5411bb52, 0x31768014, 0x9edfcdde, 0xfbb8f698, - 0x872786b9, 0xe240bdff, 0x4de9f035, 0x288ecb73, 0xc9ca6de0, - 0xacad56a6, 0x03041b6c, 0x6663202a, 0xfa3afb2e, 0x9f5dc068, - 0x30f48da2, 0x5593b6e4, 0xb4d71077, 0xd1b02b31, 0x7e1966fb, - 0x1b7e5dbd, 0x67e12d9c, 0x028616da, 0xad2f5b10, 0xc8486056, - 0x290cc6c5, 0x4c6bfd83, 0xe3c2b049, 0x86a58b0f, 0x35f8a016, - 0x509f9b50, 0xff36d69a, 0x9a51eddc, 0x7b154b4f, 0x1e727009, - 0xb1db3dc3, 0xd4bc0685, 0xa82376a4, 0xcd444de2, 0x62ed0028, - 0x078a3b6e, 0xe6ce9dfd, 0x83a9a6bb, 0x2c00eb71, 0x4967d037, - 0xd53e0b33, 0xb0593075, 0x1ff07dbf, 0x7a9746f9, 0x9bd3e06a, - 0xfeb4db2c, 0x511d96e6, 0x347aada0, 0x48e5dd81, 0x2d82e6c7, - 0x822bab0d, 0xe74c904b, 0x060836d8, 0x636f0d9e, 0xccc64054, - 0xa9a17b12, 0x2f04f01d, 0x4a63cb5b, 0xe5ca8691, 0x80adbdd7, - 0x61e91b44, 0x048e2002, 0xab276dc8, 0xce40568e, 0xb2df26af, - 0xd7b81de9, 0x78115023, 0x1d766b65, 0xfc32cdf6, 0x9955f6b0, - 0x36fcbb7a, 0x539b803c, 0xcfc25b38, 0xaaa5607e, 0x050c2db4, - 0x606b16f2, 0x812fb061, 0xe4488b27, 0x4be1c6ed, 0x2e86fdab, - 0x52198d8a, 0x377eb6cc, 0x98d7fb06, 0xfdb0c040, 0x1cf466d3, - 0x79935d95, 0xd63a105f, 0xb35d2b19, 0x6bf1402c, 0x0e967b6a, - 0xa13f36a0, 0xc4580de6, 0x251cab75, 0x407b9033, 0xefd2ddf9, - 0x8ab5e6bf, 0xf62a969e, 0x934dadd8, 0x3ce4e012, 0x5983db54, - 0xb8c77dc7, 0xdda04681, 0x72090b4b, 0x176e300d, 0x8b37eb09, - 0xee50d04f, 0x41f99d85, 0x249ea6c3, 0xc5da0050, 0xa0bd3b16, - 0x0f1476dc, 0x6a734d9a, 0x16ec3dbb, 0x738b06fd, 0xdc224b37, - 0xb9457071, 0x5801d6e2, 0x3d66eda4, 0x92cfa06e, 0xf7a89b28, - 0x710d1027, 0x146a2b61, 0xbbc366ab, 0xdea45ded, 0x3fe0fb7e, - 0x5a87c038, 0xf52e8df2, 0x9049b6b4, 0xecd6c695, 0x89b1fdd3, - 0x2618b019, 0x437f8b5f, 0xa23b2dcc, 0xc75c168a, 0x68f55b40, - 0x0d926006, 0x91cbbb02, 0xf4ac8044, 0x5b05cd8e, 0x3e62f6c8, - 0xdf26505b, 0xba416b1d, 0x15e826d7, 0x708f1d91, 0x0c106db0, - 0x697756f6, 0xc6de1b3c, 0xa3b9207a, 0x42fd86e9, 0x279abdaf, - 0x8833f065, 0xed54cb23, 0x5e09e03a, 0x3b6edb7c, 0x94c796b6, - 0xf1a0adf0, 0x10e40b63, 0x75833025, 0xda2a7def, 0xbf4d46a9, - 0xc3d23688, 0xa6b50dce, 0x091c4004, 0x6c7b7b42, 0x8d3fddd1, - 0xe858e697, 0x47f1ab5d, 0x2296901b, 0xbecf4b1f, 0xdba87059, - 0x74013d93, 0x116606d5, 0xf022a046, 0x95459b00, 0x3aecd6ca, - 0x5f8bed8c, 0x23149dad, 0x4673a6eb, 0xe9daeb21, 0x8cbdd067, - 0x6df976f4, 0x089e4db2, 0xa7370078, 0xc2503b3e, 0x44f5b031, - 0x21928b77, 0x8e3bc6bd, 0xeb5cfdfb, 0x0a185b68, 0x6f7f602e, - 0xc0d62de4, 0xa5b116a2, 0xd92e6683, 0xbc495dc5, 0x13e0100f, - 0x76872b49, 0x97c38dda, 0xf2a4b69c, 0x5d0dfb56, 0x386ac010, - 0xa4331b14, 0xc1542052, 0x6efd6d98, 0x0b9a56de, 0xeadef04d, - 0x8fb9cb0b, 0x201086c1, 0x4577bd87, 0x39e8cda6, 0x5c8ff6e0, - 0xf326bb2a, 0x9641806c, 0x770526ff, 0x12621db9, 0xbdcb5073, - 0xd8ac6b35}, - {0x00000000, 0xd7e28058, 0x74b406f1, 0xa35686a9, 0xe9680de2, - 0x3e8a8dba, 0x9ddc0b13, 0x4a3e8b4b, 0x09a11d85, 0xde439ddd, - 0x7d151b74, 0xaaf79b2c, 0xe0c91067, 0x372b903f, 0x947d1696, - 0x439f96ce, 0x13423b0a, 0xc4a0bb52, 0x67f63dfb, 0xb014bda3, - 0xfa2a36e8, 0x2dc8b6b0, 0x8e9e3019, 0x597cb041, 0x1ae3268f, - 0xcd01a6d7, 0x6e57207e, 0xb9b5a026, 0xf38b2b6d, 0x2469ab35, - 0x873f2d9c, 0x50ddadc4, 0x26847614, 0xf166f64c, 0x523070e5, - 0x85d2f0bd, 0xcfec7bf6, 0x180efbae, 0xbb587d07, 0x6cbafd5f, - 0x2f256b91, 0xf8c7ebc9, 0x5b916d60, 0x8c73ed38, 0xc64d6673, - 0x11afe62b, 0xb2f96082, 0x651be0da, 0x35c64d1e, 0xe224cd46, - 0x41724bef, 0x9690cbb7, 0xdcae40fc, 0x0b4cc0a4, 0xa81a460d, - 0x7ff8c655, 0x3c67509b, 0xeb85d0c3, 0x48d3566a, 0x9f31d632, - 0xd50f5d79, 0x02eddd21, 0xa1bb5b88, 0x7659dbd0, 0x4d08ec28, - 0x9aea6c70, 0x39bcead9, 0xee5e6a81, 0xa460e1ca, 0x73826192, - 0xd0d4e73b, 0x07366763, 0x44a9f1ad, 0x934b71f5, 0x301df75c, - 0xe7ff7704, 0xadc1fc4f, 0x7a237c17, 0xd975fabe, 0x0e977ae6, - 0x5e4ad722, 0x89a8577a, 0x2afed1d3, 0xfd1c518b, 0xb722dac0, - 0x60c05a98, 0xc396dc31, 0x14745c69, 0x57ebcaa7, 0x80094aff, - 0x235fcc56, 0xf4bd4c0e, 0xbe83c745, 0x6961471d, 0xca37c1b4, - 0x1dd541ec, 0x6b8c9a3c, 0xbc6e1a64, 0x1f389ccd, 0xc8da1c95, - 0x82e497de, 0x55061786, 0xf650912f, 0x21b21177, 0x622d87b9, - 0xb5cf07e1, 0x16998148, 0xc17b0110, 0x8b458a5b, 0x5ca70a03, - 0xfff18caa, 0x28130cf2, 0x78cea136, 0xaf2c216e, 0x0c7aa7c7, - 0xdb98279f, 0x91a6acd4, 0x46442c8c, 0xe512aa25, 0x32f02a7d, - 0x716fbcb3, 0xa68d3ceb, 0x05dbba42, 0xd2393a1a, 0x9807b151, - 0x4fe53109, 0xecb3b7a0, 0x3b5137f8, 0x9a11d850, 0x4df35808, - 0xeea5dea1, 0x39475ef9, 0x7379d5b2, 0xa49b55ea, 0x07cdd343, - 0xd02f531b, 0x93b0c5d5, 0x4452458d, 0xe704c324, 0x30e6437c, - 0x7ad8c837, 0xad3a486f, 0x0e6ccec6, 0xd98e4e9e, 0x8953e35a, - 0x5eb16302, 0xfde7e5ab, 0x2a0565f3, 0x603beeb8, 0xb7d96ee0, - 0x148fe849, 0xc36d6811, 0x80f2fedf, 0x57107e87, 0xf446f82e, - 0x23a47876, 0x699af33d, 0xbe787365, 0x1d2ef5cc, 0xcacc7594, - 0xbc95ae44, 0x6b772e1c, 0xc821a8b5, 0x1fc328ed, 0x55fda3a6, - 0x821f23fe, 0x2149a557, 0xf6ab250f, 0xb534b3c1, 0x62d63399, - 0xc180b530, 0x16623568, 0x5c5cbe23, 0x8bbe3e7b, 0x28e8b8d2, - 0xff0a388a, 0xafd7954e, 0x78351516, 0xdb6393bf, 0x0c8113e7, - 0x46bf98ac, 0x915d18f4, 0x320b9e5d, 0xe5e91e05, 0xa67688cb, - 0x71940893, 0xd2c28e3a, 0x05200e62, 0x4f1e8529, 0x98fc0571, - 0x3baa83d8, 0xec480380, 0xd7193478, 0x00fbb420, 0xa3ad3289, - 0x744fb2d1, 0x3e71399a, 0xe993b9c2, 0x4ac53f6b, 0x9d27bf33, - 0xdeb829fd, 0x095aa9a5, 0xaa0c2f0c, 0x7deeaf54, 0x37d0241f, - 0xe032a447, 0x436422ee, 0x9486a2b6, 0xc45b0f72, 0x13b98f2a, - 0xb0ef0983, 0x670d89db, 0x2d330290, 0xfad182c8, 0x59870461, - 0x8e658439, 0xcdfa12f7, 0x1a1892af, 0xb94e1406, 0x6eac945e, - 0x24921f15, 0xf3709f4d, 0x502619e4, 0x87c499bc, 0xf19d426c, - 0x267fc234, 0x8529449d, 0x52cbc4c5, 0x18f54f8e, 0xcf17cfd6, - 0x6c41497f, 0xbba3c927, 0xf83c5fe9, 0x2fdedfb1, 0x8c885918, - 0x5b6ad940, 0x1154520b, 0xc6b6d253, 0x65e054fa, 0xb202d4a2, - 0xe2df7966, 0x353df93e, 0x966b7f97, 0x4189ffcf, 0x0bb77484, - 0xdc55f4dc, 0x7f037275, 0xa8e1f22d, 0xeb7e64e3, 0x3c9ce4bb, - 0x9fca6212, 0x4828e24a, 0x02166901, 0xd5f4e959, 0x76a26ff0, - 0xa140efa8}, - {0x00000000, 0xef52b6e1, 0x05d46b83, 0xea86dd62, 0x0ba8d706, - 0xe4fa61e7, 0x0e7cbc85, 0xe12e0a64, 0x1751ae0c, 0xf80318ed, - 0x1285c58f, 0xfdd7736e, 0x1cf9790a, 0xf3abcfeb, 0x192d1289, - 0xf67fa468, 0x2ea35c18, 0xc1f1eaf9, 0x2b77379b, 0xc425817a, - 0x250b8b1e, 0xca593dff, 0x20dfe09d, 0xcf8d567c, 0x39f2f214, - 0xd6a044f5, 0x3c269997, 0xd3742f76, 0x325a2512, 0xdd0893f3, - 0x378e4e91, 0xd8dcf870, 0x5d46b830, 0xb2140ed1, 0x5892d3b3, - 0xb7c06552, 0x56ee6f36, 0xb9bcd9d7, 0x533a04b5, 0xbc68b254, - 0x4a17163c, 0xa545a0dd, 0x4fc37dbf, 0xa091cb5e, 0x41bfc13a, - 0xaeed77db, 0x446baab9, 0xab391c58, 0x73e5e428, 0x9cb752c9, - 0x76318fab, 0x9963394a, 0x784d332e, 0x971f85cf, 0x7d9958ad, - 0x92cbee4c, 0x64b44a24, 0x8be6fcc5, 0x616021a7, 0x8e329746, - 0x6f1c9d22, 0x804e2bc3, 0x6ac8f6a1, 0x859a4040, 0xba8d7060, - 0x55dfc681, 0xbf591be3, 0x500bad02, 0xb125a766, 0x5e771187, - 0xb4f1cce5, 0x5ba37a04, 0xaddcde6c, 0x428e688d, 0xa808b5ef, - 0x475a030e, 0xa674096a, 0x4926bf8b, 0xa3a062e9, 0x4cf2d408, - 0x942e2c78, 0x7b7c9a99, 0x91fa47fb, 0x7ea8f11a, 0x9f86fb7e, - 0x70d44d9f, 0x9a5290fd, 0x7500261c, 0x837f8274, 0x6c2d3495, - 0x86abe9f7, 0x69f95f16, 0x88d75572, 0x6785e393, 0x8d033ef1, - 0x62518810, 0xe7cbc850, 0x08997eb1, 0xe21fa3d3, 0x0d4d1532, - 0xec631f56, 0x0331a9b7, 0xe9b774d5, 0x06e5c234, 0xf09a665c, - 0x1fc8d0bd, 0xf54e0ddf, 0x1a1cbb3e, 0xfb32b15a, 0x146007bb, - 0xfee6dad9, 0x11b46c38, 0xc9689448, 0x263a22a9, 0xccbcffcb, - 0x23ee492a, 0xc2c0434e, 0x2d92f5af, 0xc71428cd, 0x28469e2c, - 0xde393a44, 0x316b8ca5, 0xdbed51c7, 0x34bfe726, 0xd591ed42, - 0x3ac35ba3, 0xd04586c1, 0x3f173020, 0xae6be681, 0x41395060, - 0xabbf8d02, 0x44ed3be3, 0xa5c33187, 0x4a918766, 0xa0175a04, - 0x4f45ece5, 0xb93a488d, 0x5668fe6c, 0xbcee230e, 0x53bc95ef, - 0xb2929f8b, 0x5dc0296a, 0xb746f408, 0x581442e9, 0x80c8ba99, - 0x6f9a0c78, 0x851cd11a, 0x6a4e67fb, 0x8b606d9f, 0x6432db7e, - 0x8eb4061c, 0x61e6b0fd, 0x97991495, 0x78cba274, 0x924d7f16, - 0x7d1fc9f7, 0x9c31c393, 0x73637572, 0x99e5a810, 0x76b71ef1, - 0xf32d5eb1, 0x1c7fe850, 0xf6f93532, 0x19ab83d3, 0xf88589b7, - 0x17d73f56, 0xfd51e234, 0x120354d5, 0xe47cf0bd, 0x0b2e465c, - 0xe1a89b3e, 0x0efa2ddf, 0xefd427bb, 0x0086915a, 0xea004c38, - 0x0552fad9, 0xdd8e02a9, 0x32dcb448, 0xd85a692a, 0x3708dfcb, - 0xd626d5af, 0x3974634e, 0xd3f2be2c, 0x3ca008cd, 0xcadfaca5, - 0x258d1a44, 0xcf0bc726, 0x205971c7, 0xc1777ba3, 0x2e25cd42, - 0xc4a31020, 0x2bf1a6c1, 0x14e696e1, 0xfbb42000, 0x1132fd62, - 0xfe604b83, 0x1f4e41e7, 0xf01cf706, 0x1a9a2a64, 0xf5c89c85, - 0x03b738ed, 0xece58e0c, 0x0663536e, 0xe931e58f, 0x081fefeb, - 0xe74d590a, 0x0dcb8468, 0xe2993289, 0x3a45caf9, 0xd5177c18, - 0x3f91a17a, 0xd0c3179b, 0x31ed1dff, 0xdebfab1e, 0x3439767c, - 0xdb6bc09d, 0x2d1464f5, 0xc246d214, 0x28c00f76, 0xc792b997, - 0x26bcb3f3, 0xc9ee0512, 0x2368d870, 0xcc3a6e91, 0x49a02ed1, - 0xa6f29830, 0x4c744552, 0xa326f3b3, 0x4208f9d7, 0xad5a4f36, - 0x47dc9254, 0xa88e24b5, 0x5ef180dd, 0xb1a3363c, 0x5b25eb5e, - 0xb4775dbf, 0x555957db, 0xba0be13a, 0x508d3c58, 0xbfdf8ab9, - 0x670372c9, 0x8851c428, 0x62d7194a, 0x8d85afab, 0x6caba5cf, - 0x83f9132e, 0x697fce4c, 0x862d78ad, 0x7052dcc5, 0x9f006a24, - 0x7586b746, 0x9ad401a7, 0x7bfa0bc3, 0x94a8bd22, 0x7e2e6040, - 0x917cd6a1}, - {0x00000000, 0x87a6cb43, 0xd43c90c7, 0x539a5b84, 0x730827cf, - 0xf4aeec8c, 0xa734b708, 0x20927c4b, 0xe6104f9e, 0x61b684dd, - 0x322cdf59, 0xb58a141a, 0x95186851, 0x12bea312, 0x4124f896, - 0xc68233d5, 0x1751997d, 0x90f7523e, 0xc36d09ba, 0x44cbc2f9, - 0x6459beb2, 0xe3ff75f1, 0xb0652e75, 0x37c3e536, 0xf141d6e3, - 0x76e71da0, 0x257d4624, 0xa2db8d67, 0x8249f12c, 0x05ef3a6f, - 0x567561eb, 0xd1d3aaa8, 0x2ea332fa, 0xa905f9b9, 0xfa9fa23d, - 0x7d39697e, 0x5dab1535, 0xda0dde76, 0x899785f2, 0x0e314eb1, - 0xc8b37d64, 0x4f15b627, 0x1c8feda3, 0x9b2926e0, 0xbbbb5aab, - 0x3c1d91e8, 0x6f87ca6c, 0xe821012f, 0x39f2ab87, 0xbe5460c4, - 0xedce3b40, 0x6a68f003, 0x4afa8c48, 0xcd5c470b, 0x9ec61c8f, - 0x1960d7cc, 0xdfe2e419, 0x58442f5a, 0x0bde74de, 0x8c78bf9d, - 0xaceac3d6, 0x2b4c0895, 0x78d65311, 0xff709852, 0x5d4665f4, - 0xdae0aeb7, 0x897af533, 0x0edc3e70, 0x2e4e423b, 0xa9e88978, - 0xfa72d2fc, 0x7dd419bf, 0xbb562a6a, 0x3cf0e129, 0x6f6abaad, - 0xe8cc71ee, 0xc85e0da5, 0x4ff8c6e6, 0x1c629d62, 0x9bc45621, - 0x4a17fc89, 0xcdb137ca, 0x9e2b6c4e, 0x198da70d, 0x391fdb46, - 0xbeb91005, 0xed234b81, 0x6a8580c2, 0xac07b317, 0x2ba17854, - 0x783b23d0, 0xff9de893, 0xdf0f94d8, 0x58a95f9b, 0x0b33041f, - 0x8c95cf5c, 0x73e5570e, 0xf4439c4d, 0xa7d9c7c9, 0x207f0c8a, - 0x00ed70c1, 0x874bbb82, 0xd4d1e006, 0x53772b45, 0x95f51890, - 0x1253d3d3, 0x41c98857, 0xc66f4314, 0xe6fd3f5f, 0x615bf41c, - 0x32c1af98, 0xb56764db, 0x64b4ce73, 0xe3120530, 0xb0885eb4, - 0x372e95f7, 0x17bce9bc, 0x901a22ff, 0xc380797b, 0x4426b238, - 0x82a481ed, 0x05024aae, 0x5698112a, 0xd13eda69, 0xf1aca622, - 0x760a6d61, 0x259036e5, 0xa236fda6, 0xba8ccbe8, 0x3d2a00ab, - 0x6eb05b2f, 0xe916906c, 0xc984ec27, 0x4e222764, 0x1db87ce0, - 0x9a1eb7a3, 0x5c9c8476, 0xdb3a4f35, 0x88a014b1, 0x0f06dff2, - 0x2f94a3b9, 0xa83268fa, 0xfba8337e, 0x7c0ef83d, 0xaddd5295, - 0x2a7b99d6, 0x79e1c252, 0xfe470911, 0xded5755a, 0x5973be19, - 0x0ae9e59d, 0x8d4f2ede, 0x4bcd1d0b, 0xcc6bd648, 0x9ff18dcc, - 0x1857468f, 0x38c53ac4, 0xbf63f187, 0xecf9aa03, 0x6b5f6140, - 0x942ff912, 0x13893251, 0x401369d5, 0xc7b5a296, 0xe727dedd, - 0x6081159e, 0x331b4e1a, 0xb4bd8559, 0x723fb68c, 0xf5997dcf, - 0xa603264b, 0x21a5ed08, 0x01379143, 0x86915a00, 0xd50b0184, - 0x52adcac7, 0x837e606f, 0x04d8ab2c, 0x5742f0a8, 0xd0e43beb, - 0xf07647a0, 0x77d08ce3, 0x244ad767, 0xa3ec1c24, 0x656e2ff1, - 0xe2c8e4b2, 0xb152bf36, 0x36f47475, 0x1666083e, 0x91c0c37d, - 0xc25a98f9, 0x45fc53ba, 0xe7caae1c, 0x606c655f, 0x33f63edb, - 0xb450f598, 0x94c289d3, 0x13644290, 0x40fe1914, 0xc758d257, - 0x01dae182, 0x867c2ac1, 0xd5e67145, 0x5240ba06, 0x72d2c64d, - 0xf5740d0e, 0xa6ee568a, 0x21489dc9, 0xf09b3761, 0x773dfc22, - 0x24a7a7a6, 0xa3016ce5, 0x839310ae, 0x0435dbed, 0x57af8069, - 0xd0094b2a, 0x168b78ff, 0x912db3bc, 0xc2b7e838, 0x4511237b, - 0x65835f30, 0xe2259473, 0xb1bfcff7, 0x361904b4, 0xc9699ce6, - 0x4ecf57a5, 0x1d550c21, 0x9af3c762, 0xba61bb29, 0x3dc7706a, - 0x6e5d2bee, 0xe9fbe0ad, 0x2f79d378, 0xa8df183b, 0xfb4543bf, - 0x7ce388fc, 0x5c71f4b7, 0xdbd73ff4, 0x884d6470, 0x0febaf33, - 0xde38059b, 0x599eced8, 0x0a04955c, 0x8da25e1f, 0xad302254, - 0x2a96e917, 0x790cb293, 0xfeaa79d0, 0x38284a05, 0xbf8e8146, - 0xec14dac2, 0x6bb21181, 0x4b206dca, 0xcc86a689, 0x9f1cfd0d, - 0x18ba364e}}; - -local const z_word_t FAR crc_braid_big_table[][256] = { - {0x00000000, 0x43cba687, 0xc7903cd4, 0x845b9a53, 0xcf270873, - 0x8cecaef4, 0x08b734a7, 0x4b7c9220, 0x9e4f10e6, 0xdd84b661, - 0x59df2c32, 0x1a148ab5, 0x51681895, 0x12a3be12, 0x96f82441, - 0xd53382c6, 0x7d995117, 0x3e52f790, 0xba096dc3, 0xf9c2cb44, - 0xb2be5964, 0xf175ffe3, 0x752e65b0, 0x36e5c337, 0xe3d641f1, - 0xa01de776, 0x24467d25, 0x678ddba2, 0x2cf14982, 0x6f3aef05, - 0xeb617556, 0xa8aad3d1, 0xfa32a32e, 0xb9f905a9, 0x3da29ffa, - 0x7e69397d, 0x3515ab5d, 0x76de0dda, 0xf2859789, 0xb14e310e, - 0x647db3c8, 0x27b6154f, 0xa3ed8f1c, 0xe026299b, 0xab5abbbb, - 0xe8911d3c, 0x6cca876f, 0x2f0121e8, 0x87abf239, 0xc46054be, - 0x403bceed, 0x03f0686a, 0x488cfa4a, 0x0b475ccd, 0x8f1cc69e, - 0xccd76019, 0x19e4e2df, 0x5a2f4458, 0xde74de0b, 0x9dbf788c, - 0xd6c3eaac, 0x95084c2b, 0x1153d678, 0x529870ff, 0xf465465d, - 0xb7aee0da, 0x33f57a89, 0x703edc0e, 0x3b424e2e, 0x7889e8a9, - 0xfcd272fa, 0xbf19d47d, 0x6a2a56bb, 0x29e1f03c, 0xadba6a6f, - 0xee71cce8, 0xa50d5ec8, 0xe6c6f84f, 0x629d621c, 0x2156c49b, - 0x89fc174a, 0xca37b1cd, 0x4e6c2b9e, 0x0da78d19, 0x46db1f39, - 0x0510b9be, 0x814b23ed, 0xc280856a, 0x17b307ac, 0x5478a12b, - 0xd0233b78, 0x93e89dff, 0xd8940fdf, 0x9b5fa958, 0x1f04330b, - 0x5ccf958c, 0x0e57e573, 0x4d9c43f4, 0xc9c7d9a7, 0x8a0c7f20, - 0xc170ed00, 0x82bb4b87, 0x06e0d1d4, 0x452b7753, 0x9018f595, - 0xd3d35312, 0x5788c941, 0x14436fc6, 0x5f3ffde6, 0x1cf45b61, - 0x98afc132, 0xdb6467b5, 0x73ceb464, 0x300512e3, 0xb45e88b0, - 0xf7952e37, 0xbce9bc17, 0xff221a90, 0x7b7980c3, 0x38b22644, - 0xed81a482, 0xae4a0205, 0x2a119856, 0x69da3ed1, 0x22a6acf1, - 0x616d0a76, 0xe5369025, 0xa6fd36a2, 0xe8cb8cba, 0xab002a3d, - 0x2f5bb06e, 0x6c9016e9, 0x27ec84c9, 0x6427224e, 0xe07cb81d, - 0xa3b71e9a, 0x76849c5c, 0x354f3adb, 0xb114a088, 0xf2df060f, - 0xb9a3942f, 0xfa6832a8, 0x7e33a8fb, 0x3df80e7c, 0x9552ddad, - 0xd6997b2a, 0x52c2e179, 0x110947fe, 0x5a75d5de, 0x19be7359, - 0x9de5e90a, 0xde2e4f8d, 0x0b1dcd4b, 0x48d66bcc, 0xcc8df19f, - 0x8f465718, 0xc43ac538, 0x87f163bf, 0x03aaf9ec, 0x40615f6b, - 0x12f92f94, 0x51328913, 0xd5691340, 0x96a2b5c7, 0xddde27e7, - 0x9e158160, 0x1a4e1b33, 0x5985bdb4, 0x8cb63f72, 0xcf7d99f5, - 0x4b2603a6, 0x08eda521, 0x43913701, 0x005a9186, 0x84010bd5, - 0xc7caad52, 0x6f607e83, 0x2cabd804, 0xa8f04257, 0xeb3be4d0, - 0xa04776f0, 0xe38cd077, 0x67d74a24, 0x241ceca3, 0xf12f6e65, - 0xb2e4c8e2, 0x36bf52b1, 0x7574f436, 0x3e086616, 0x7dc3c091, - 0xf9985ac2, 0xba53fc45, 0x1caecae7, 0x5f656c60, 0xdb3ef633, - 0x98f550b4, 0xd389c294, 0x90426413, 0x1419fe40, 0x57d258c7, - 0x82e1da01, 0xc12a7c86, 0x4571e6d5, 0x06ba4052, 0x4dc6d272, - 0x0e0d74f5, 0x8a56eea6, 0xc99d4821, 0x61379bf0, 0x22fc3d77, - 0xa6a7a724, 0xe56c01a3, 0xae109383, 0xeddb3504, 0x6980af57, - 0x2a4b09d0, 0xff788b16, 0xbcb32d91, 0x38e8b7c2, 0x7b231145, - 0x305f8365, 0x739425e2, 0xf7cfbfb1, 0xb4041936, 0xe69c69c9, - 0xa557cf4e, 0x210c551d, 0x62c7f39a, 0x29bb61ba, 0x6a70c73d, - 0xee2b5d6e, 0xade0fbe9, 0x78d3792f, 0x3b18dfa8, 0xbf4345fb, - 0xfc88e37c, 0xb7f4715c, 0xf43fd7db, 0x70644d88, 0x33afeb0f, - 0x9b0538de, 0xd8ce9e59, 0x5c95040a, 0x1f5ea28d, 0x542230ad, - 0x17e9962a, 0x93b20c79, 0xd079aafe, 0x054a2838, 0x46818ebf, - 0xc2da14ec, 0x8111b26b, 0xca6d204b, 0x89a686cc, 0x0dfd1c9f, - 0x4e36ba18}, - {0x00000000, 0xe1b652ef, 0x836bd405, 0x62dd86ea, 0x06d7a80b, - 0xe761fae4, 0x85bc7c0e, 0x640a2ee1, 0x0cae5117, 0xed1803f8, - 0x8fc58512, 0x6e73d7fd, 0x0a79f91c, 0xebcfabf3, 0x89122d19, - 0x68a47ff6, 0x185ca32e, 0xf9eaf1c1, 0x9b37772b, 0x7a8125c4, - 0x1e8b0b25, 0xff3d59ca, 0x9de0df20, 0x7c568dcf, 0x14f2f239, - 0xf544a0d6, 0x9799263c, 0x762f74d3, 0x12255a32, 0xf39308dd, - 0x914e8e37, 0x70f8dcd8, 0x30b8465d, 0xd10e14b2, 0xb3d39258, - 0x5265c0b7, 0x366fee56, 0xd7d9bcb9, 0xb5043a53, 0x54b268bc, - 0x3c16174a, 0xdda045a5, 0xbf7dc34f, 0x5ecb91a0, 0x3ac1bf41, - 0xdb77edae, 0xb9aa6b44, 0x581c39ab, 0x28e4e573, 0xc952b79c, - 0xab8f3176, 0x4a396399, 0x2e334d78, 0xcf851f97, 0xad58997d, - 0x4ceecb92, 0x244ab464, 0xc5fce68b, 0xa7216061, 0x4697328e, - 0x229d1c6f, 0xc32b4e80, 0xa1f6c86a, 0x40409a85, 0x60708dba, - 0x81c6df55, 0xe31b59bf, 0x02ad0b50, 0x66a725b1, 0x8711775e, - 0xe5ccf1b4, 0x047aa35b, 0x6cdedcad, 0x8d688e42, 0xefb508a8, - 0x0e035a47, 0x6a0974a6, 0x8bbf2649, 0xe962a0a3, 0x08d4f24c, - 0x782c2e94, 0x999a7c7b, 0xfb47fa91, 0x1af1a87e, 0x7efb869f, - 0x9f4dd470, 0xfd90529a, 0x1c260075, 0x74827f83, 0x95342d6c, - 0xf7e9ab86, 0x165ff969, 0x7255d788, 0x93e38567, 0xf13e038d, - 0x10885162, 0x50c8cbe7, 0xb17e9908, 0xd3a31fe2, 0x32154d0d, - 0x561f63ec, 0xb7a93103, 0xd574b7e9, 0x34c2e506, 0x5c669af0, - 0xbdd0c81f, 0xdf0d4ef5, 0x3ebb1c1a, 0x5ab132fb, 0xbb076014, - 0xd9dae6fe, 0x386cb411, 0x489468c9, 0xa9223a26, 0xcbffbccc, - 0x2a49ee23, 0x4e43c0c2, 0xaff5922d, 0xcd2814c7, 0x2c9e4628, - 0x443a39de, 0xa58c6b31, 0xc751eddb, 0x26e7bf34, 0x42ed91d5, - 0xa35bc33a, 0xc18645d0, 0x2030173f, 0x81e66bae, 0x60503941, - 0x028dbfab, 0xe33bed44, 0x8731c3a5, 0x6687914a, 0x045a17a0, - 0xe5ec454f, 0x8d483ab9, 0x6cfe6856, 0x0e23eebc, 0xef95bc53, - 0x8b9f92b2, 0x6a29c05d, 0x08f446b7, 0xe9421458, 0x99bac880, - 0x780c9a6f, 0x1ad11c85, 0xfb674e6a, 0x9f6d608b, 0x7edb3264, - 0x1c06b48e, 0xfdb0e661, 0x95149997, 0x74a2cb78, 0x167f4d92, - 0xf7c91f7d, 0x93c3319c, 0x72756373, 0x10a8e599, 0xf11eb776, - 0xb15e2df3, 0x50e87f1c, 0x3235f9f6, 0xd383ab19, 0xb78985f8, - 0x563fd717, 0x34e251fd, 0xd5540312, 0xbdf07ce4, 0x5c462e0b, - 0x3e9ba8e1, 0xdf2dfa0e, 0xbb27d4ef, 0x5a918600, 0x384c00ea, - 0xd9fa5205, 0xa9028edd, 0x48b4dc32, 0x2a695ad8, 0xcbdf0837, - 0xafd526d6, 0x4e637439, 0x2cbef2d3, 0xcd08a03c, 0xa5acdfca, - 0x441a8d25, 0x26c70bcf, 0xc7715920, 0xa37b77c1, 0x42cd252e, - 0x2010a3c4, 0xc1a6f12b, 0xe196e614, 0x0020b4fb, 0x62fd3211, - 0x834b60fe, 0xe7414e1f, 0x06f71cf0, 0x642a9a1a, 0x859cc8f5, - 0xed38b703, 0x0c8ee5ec, 0x6e536306, 0x8fe531e9, 0xebef1f08, - 0x0a594de7, 0x6884cb0d, 0x893299e2, 0xf9ca453a, 0x187c17d5, - 0x7aa1913f, 0x9b17c3d0, 0xff1ded31, 0x1eabbfde, 0x7c763934, - 0x9dc06bdb, 0xf564142d, 0x14d246c2, 0x760fc028, 0x97b992c7, - 0xf3b3bc26, 0x1205eec9, 0x70d86823, 0x916e3acc, 0xd12ea049, - 0x3098f2a6, 0x5245744c, 0xb3f326a3, 0xd7f90842, 0x364f5aad, - 0x5492dc47, 0xb5248ea8, 0xdd80f15e, 0x3c36a3b1, 0x5eeb255b, - 0xbf5d77b4, 0xdb575955, 0x3ae10bba, 0x583c8d50, 0xb98adfbf, - 0xc9720367, 0x28c45188, 0x4a19d762, 0xabaf858d, 0xcfa5ab6c, - 0x2e13f983, 0x4cce7f69, 0xad782d86, 0xc5dc5270, 0x246a009f, - 0x46b78675, 0xa701d49a, 0xc30bfa7b, 0x22bda894, 0x40602e7e, - 0xa1d67c91}, - {0x00000000, 0x5880e2d7, 0xf106b474, 0xa98656a3, 0xe20d68e9, - 0xba8d8a3e, 0x130bdc9d, 0x4b8b3e4a, 0x851da109, 0xdd9d43de, - 0x741b157d, 0x2c9bf7aa, 0x6710c9e0, 0x3f902b37, 0x96167d94, - 0xce969f43, 0x0a3b4213, 0x52bba0c4, 0xfb3df667, 0xa3bd14b0, - 0xe8362afa, 0xb0b6c82d, 0x19309e8e, 0x41b07c59, 0x8f26e31a, - 0xd7a601cd, 0x7e20576e, 0x26a0b5b9, 0x6d2b8bf3, 0x35ab6924, - 0x9c2d3f87, 0xc4addd50, 0x14768426, 0x4cf666f1, 0xe5703052, - 0xbdf0d285, 0xf67beccf, 0xaefb0e18, 0x077d58bb, 0x5ffdba6c, - 0x916b252f, 0xc9ebc7f8, 0x606d915b, 0x38ed738c, 0x73664dc6, - 0x2be6af11, 0x8260f9b2, 0xdae01b65, 0x1e4dc635, 0x46cd24e2, - 0xef4b7241, 0xb7cb9096, 0xfc40aedc, 0xa4c04c0b, 0x0d461aa8, - 0x55c6f87f, 0x9b50673c, 0xc3d085eb, 0x6a56d348, 0x32d6319f, - 0x795d0fd5, 0x21dded02, 0x885bbba1, 0xd0db5976, 0x28ec084d, - 0x706cea9a, 0xd9eabc39, 0x816a5eee, 0xcae160a4, 0x92618273, - 0x3be7d4d0, 0x63673607, 0xadf1a944, 0xf5714b93, 0x5cf71d30, - 0x0477ffe7, 0x4ffcc1ad, 0x177c237a, 0xbefa75d9, 0xe67a970e, - 0x22d74a5e, 0x7a57a889, 0xd3d1fe2a, 0x8b511cfd, 0xc0da22b7, - 0x985ac060, 0x31dc96c3, 0x695c7414, 0xa7caeb57, 0xff4a0980, - 0x56cc5f23, 0x0e4cbdf4, 0x45c783be, 0x1d476169, 0xb4c137ca, - 0xec41d51d, 0x3c9a8c6b, 0x641a6ebc, 0xcd9c381f, 0x951cdac8, - 0xde97e482, 0x86170655, 0x2f9150f6, 0x7711b221, 0xb9872d62, - 0xe107cfb5, 0x48819916, 0x10017bc1, 0x5b8a458b, 0x030aa75c, - 0xaa8cf1ff, 0xf20c1328, 0x36a1ce78, 0x6e212caf, 0xc7a77a0c, - 0x9f2798db, 0xd4aca691, 0x8c2c4446, 0x25aa12e5, 0x7d2af032, - 0xb3bc6f71, 0xeb3c8da6, 0x42badb05, 0x1a3a39d2, 0x51b10798, - 0x0931e54f, 0xa0b7b3ec, 0xf837513b, 0x50d8119a, 0x0858f34d, - 0xa1dea5ee, 0xf95e4739, 0xb2d57973, 0xea559ba4, 0x43d3cd07, - 0x1b532fd0, 0xd5c5b093, 0x8d455244, 0x24c304e7, 0x7c43e630, - 0x37c8d87a, 0x6f483aad, 0xc6ce6c0e, 0x9e4e8ed9, 0x5ae35389, - 0x0263b15e, 0xabe5e7fd, 0xf365052a, 0xb8ee3b60, 0xe06ed9b7, - 0x49e88f14, 0x11686dc3, 0xdffef280, 0x877e1057, 0x2ef846f4, - 0x7678a423, 0x3df39a69, 0x657378be, 0xccf52e1d, 0x9475ccca, - 0x44ae95bc, 0x1c2e776b, 0xb5a821c8, 0xed28c31f, 0xa6a3fd55, - 0xfe231f82, 0x57a54921, 0x0f25abf6, 0xc1b334b5, 0x9933d662, - 0x30b580c1, 0x68356216, 0x23be5c5c, 0x7b3ebe8b, 0xd2b8e828, - 0x8a380aff, 0x4e95d7af, 0x16153578, 0xbf9363db, 0xe713810c, - 0xac98bf46, 0xf4185d91, 0x5d9e0b32, 0x051ee9e5, 0xcb8876a6, - 0x93089471, 0x3a8ec2d2, 0x620e2005, 0x29851e4f, 0x7105fc98, - 0xd883aa3b, 0x800348ec, 0x783419d7, 0x20b4fb00, 0x8932ada3, - 0xd1b24f74, 0x9a39713e, 0xc2b993e9, 0x6b3fc54a, 0x33bf279d, - 0xfd29b8de, 0xa5a95a09, 0x0c2f0caa, 0x54afee7d, 0x1f24d037, - 0x47a432e0, 0xee226443, 0xb6a28694, 0x720f5bc4, 0x2a8fb913, - 0x8309efb0, 0xdb890d67, 0x9002332d, 0xc882d1fa, 0x61048759, - 0x3984658e, 0xf712facd, 0xaf92181a, 0x06144eb9, 0x5e94ac6e, - 0x151f9224, 0x4d9f70f3, 0xe4192650, 0xbc99c487, 0x6c429df1, - 0x34c27f26, 0x9d442985, 0xc5c4cb52, 0x8e4ff518, 0xd6cf17cf, - 0x7f49416c, 0x27c9a3bb, 0xe95f3cf8, 0xb1dfde2f, 0x1859888c, - 0x40d96a5b, 0x0b525411, 0x53d2b6c6, 0xfa54e065, 0xa2d402b2, - 0x6679dfe2, 0x3ef93d35, 0x977f6b96, 0xcfff8941, 0x8474b70b, - 0xdcf455dc, 0x7572037f, 0x2df2e1a8, 0xe3647eeb, 0xbbe49c3c, - 0x1262ca9f, 0x4ae22848, 0x01691602, 0x59e9f4d5, 0xf06fa276, - 0xa8ef40a1}, - {0x00000000, 0x463b6765, 0x8c76ceca, 0xca4da9af, 0x59ebed4e, - 0x1fd08a2b, 0xd59d2384, 0x93a644e1, 0xb2d6db9d, 0xf4edbcf8, - 0x3ea01557, 0x789b7232, 0xeb3d36d3, 0xad0651b6, 0x674bf819, - 0x21709f7c, 0x25abc6e0, 0x6390a185, 0xa9dd082a, 0xefe66f4f, - 0x7c402bae, 0x3a7b4ccb, 0xf036e564, 0xb60d8201, 0x977d1d7d, - 0xd1467a18, 0x1b0bd3b7, 0x5d30b4d2, 0xce96f033, 0x88ad9756, - 0x42e03ef9, 0x04db599c, 0x0b50fc1a, 0x4d6b9b7f, 0x872632d0, - 0xc11d55b5, 0x52bb1154, 0x14807631, 0xdecddf9e, 0x98f6b8fb, - 0xb9862787, 0xffbd40e2, 0x35f0e94d, 0x73cb8e28, 0xe06dcac9, - 0xa656adac, 0x6c1b0403, 0x2a206366, 0x2efb3afa, 0x68c05d9f, - 0xa28df430, 0xe4b69355, 0x7710d7b4, 0x312bb0d1, 0xfb66197e, - 0xbd5d7e1b, 0x9c2de167, 0xda168602, 0x105b2fad, 0x566048c8, - 0xc5c60c29, 0x83fd6b4c, 0x49b0c2e3, 0x0f8ba586, 0x16a0f835, - 0x509b9f50, 0x9ad636ff, 0xdced519a, 0x4f4b157b, 0x0970721e, - 0xc33ddbb1, 0x8506bcd4, 0xa47623a8, 0xe24d44cd, 0x2800ed62, - 0x6e3b8a07, 0xfd9dcee6, 0xbba6a983, 0x71eb002c, 0x37d06749, - 0x330b3ed5, 0x753059b0, 0xbf7df01f, 0xf946977a, 0x6ae0d39b, - 0x2cdbb4fe, 0xe6961d51, 0xa0ad7a34, 0x81dde548, 0xc7e6822d, - 0x0dab2b82, 0x4b904ce7, 0xd8360806, 0x9e0d6f63, 0x5440c6cc, - 0x127ba1a9, 0x1df0042f, 0x5bcb634a, 0x9186cae5, 0xd7bdad80, - 0x441be961, 0x02208e04, 0xc86d27ab, 0x8e5640ce, 0xaf26dfb2, - 0xe91db8d7, 0x23501178, 0x656b761d, 0xf6cd32fc, 0xb0f65599, - 0x7abbfc36, 0x3c809b53, 0x385bc2cf, 0x7e60a5aa, 0xb42d0c05, - 0xf2166b60, 0x61b02f81, 0x278b48e4, 0xedc6e14b, 0xabfd862e, - 0x8a8d1952, 0xccb67e37, 0x06fbd798, 0x40c0b0fd, 0xd366f41c, - 0x955d9379, 0x5f103ad6, 0x192b5db3, 0x2c40f16b, 0x6a7b960e, - 0xa0363fa1, 0xe60d58c4, 0x75ab1c25, 0x33907b40, 0xf9ddd2ef, - 0xbfe6b58a, 0x9e962af6, 0xd8ad4d93, 0x12e0e43c, 0x54db8359, - 0xc77dc7b8, 0x8146a0dd, 0x4b0b0972, 0x0d306e17, 0x09eb378b, - 0x4fd050ee, 0x859df941, 0xc3a69e24, 0x5000dac5, 0x163bbda0, - 0xdc76140f, 0x9a4d736a, 0xbb3dec16, 0xfd068b73, 0x374b22dc, - 0x717045b9, 0xe2d60158, 0xa4ed663d, 0x6ea0cf92, 0x289ba8f7, - 0x27100d71, 0x612b6a14, 0xab66c3bb, 0xed5da4de, 0x7efbe03f, - 0x38c0875a, 0xf28d2ef5, 0xb4b64990, 0x95c6d6ec, 0xd3fdb189, - 0x19b01826, 0x5f8b7f43, 0xcc2d3ba2, 0x8a165cc7, 0x405bf568, - 0x0660920d, 0x02bbcb91, 0x4480acf4, 0x8ecd055b, 0xc8f6623e, - 0x5b5026df, 0x1d6b41ba, 0xd726e815, 0x911d8f70, 0xb06d100c, - 0xf6567769, 0x3c1bdec6, 0x7a20b9a3, 0xe986fd42, 0xafbd9a27, - 0x65f03388, 0x23cb54ed, 0x3ae0095e, 0x7cdb6e3b, 0xb696c794, - 0xf0ada0f1, 0x630be410, 0x25308375, 0xef7d2ada, 0xa9464dbf, - 0x8836d2c3, 0xce0db5a6, 0x04401c09, 0x427b7b6c, 0xd1dd3f8d, - 0x97e658e8, 0x5dabf147, 0x1b909622, 0x1f4bcfbe, 0x5970a8db, - 0x933d0174, 0xd5066611, 0x46a022f0, 0x009b4595, 0xcad6ec3a, - 0x8ced8b5f, 0xad9d1423, 0xeba67346, 0x21ebdae9, 0x67d0bd8c, - 0xf476f96d, 0xb24d9e08, 0x780037a7, 0x3e3b50c2, 0x31b0f544, - 0x778b9221, 0xbdc63b8e, 0xfbfd5ceb, 0x685b180a, 0x2e607f6f, - 0xe42dd6c0, 0xa216b1a5, 0x83662ed9, 0xc55d49bc, 0x0f10e013, - 0x492b8776, 0xda8dc397, 0x9cb6a4f2, 0x56fb0d5d, 0x10c06a38, - 0x141b33a4, 0x522054c1, 0x986dfd6e, 0xde569a0b, 0x4df0deea, - 0x0bcbb98f, 0xc1861020, 0x87bd7745, 0xa6cde839, 0xe0f68f5c, - 0x2abb26f3, 0x6c804196, 0xff260577, 0xb91d6212, 0x7350cbbd, - 0x356bacd8}}; - -#endif - -#endif - -#if N == 6 - -#if W == 8 - -local const z_crc_t FAR crc_braid_table[][256] = { - {0x00000000, 0x3db1ecdc, 0x7b63d9b8, 0x46d23564, 0xf6c7b370, - 0xcb765fac, 0x8da46ac8, 0xb0158614, 0x36fe60a1, 0x0b4f8c7d, - 0x4d9db919, 0x702c55c5, 0xc039d3d1, 0xfd883f0d, 0xbb5a0a69, - 0x86ebe6b5, 0x6dfcc142, 0x504d2d9e, 0x169f18fa, 0x2b2ef426, - 0x9b3b7232, 0xa68a9eee, 0xe058ab8a, 0xdde94756, 0x5b02a1e3, - 0x66b34d3f, 0x2061785b, 0x1dd09487, 0xadc51293, 0x9074fe4f, - 0xd6a6cb2b, 0xeb1727f7, 0xdbf98284, 0xe6486e58, 0xa09a5b3c, - 0x9d2bb7e0, 0x2d3e31f4, 0x108fdd28, 0x565de84c, 0x6bec0490, - 0xed07e225, 0xd0b60ef9, 0x96643b9d, 0xabd5d741, 0x1bc05155, - 0x2671bd89, 0x60a388ed, 0x5d126431, 0xb60543c6, 0x8bb4af1a, - 0xcd669a7e, 0xf0d776a2, 0x40c2f0b6, 0x7d731c6a, 0x3ba1290e, - 0x0610c5d2, 0x80fb2367, 0xbd4acfbb, 0xfb98fadf, 0xc6291603, - 0x763c9017, 0x4b8d7ccb, 0x0d5f49af, 0x30eea573, 0x6c820349, - 0x5133ef95, 0x17e1daf1, 0x2a50362d, 0x9a45b039, 0xa7f45ce5, - 0xe1266981, 0xdc97855d, 0x5a7c63e8, 0x67cd8f34, 0x211fba50, - 0x1cae568c, 0xacbbd098, 0x910a3c44, 0xd7d80920, 0xea69e5fc, - 0x017ec20b, 0x3ccf2ed7, 0x7a1d1bb3, 0x47acf76f, 0xf7b9717b, - 0xca089da7, 0x8cdaa8c3, 0xb16b441f, 0x3780a2aa, 0x0a314e76, - 0x4ce37b12, 0x715297ce, 0xc14711da, 0xfcf6fd06, 0xba24c862, - 0x879524be, 0xb77b81cd, 0x8aca6d11, 0xcc185875, 0xf1a9b4a9, - 0x41bc32bd, 0x7c0dde61, 0x3adfeb05, 0x076e07d9, 0x8185e16c, - 0xbc340db0, 0xfae638d4, 0xc757d408, 0x7742521c, 0x4af3bec0, - 0x0c218ba4, 0x31906778, 0xda87408f, 0xe736ac53, 0xa1e49937, - 0x9c5575eb, 0x2c40f3ff, 0x11f11f23, 0x57232a47, 0x6a92c69b, - 0xec79202e, 0xd1c8ccf2, 0x971af996, 0xaaab154a, 0x1abe935e, - 0x270f7f82, 0x61dd4ae6, 0x5c6ca63a, 0xd9040692, 0xe4b5ea4e, - 0xa267df2a, 0x9fd633f6, 0x2fc3b5e2, 0x1272593e, 0x54a06c5a, - 0x69118086, 0xeffa6633, 0xd24b8aef, 0x9499bf8b, 0xa9285357, - 0x193dd543, 0x248c399f, 0x625e0cfb, 0x5fefe027, 0xb4f8c7d0, - 0x89492b0c, 0xcf9b1e68, 0xf22af2b4, 0x423f74a0, 0x7f8e987c, - 0x395cad18, 0x04ed41c4, 0x8206a771, 0xbfb74bad, 0xf9657ec9, - 0xc4d49215, 0x74c11401, 0x4970f8dd, 0x0fa2cdb9, 0x32132165, - 0x02fd8416, 0x3f4c68ca, 0x799e5dae, 0x442fb172, 0xf43a3766, - 0xc98bdbba, 0x8f59eede, 0xb2e80202, 0x3403e4b7, 0x09b2086b, - 0x4f603d0f, 0x72d1d1d3, 0xc2c457c7, 0xff75bb1b, 0xb9a78e7f, - 0x841662a3, 0x6f014554, 0x52b0a988, 0x14629cec, 0x29d37030, - 0x99c6f624, 0xa4771af8, 0xe2a52f9c, 0xdf14c340, 0x59ff25f5, - 0x644ec929, 0x229cfc4d, 0x1f2d1091, 0xaf389685, 0x92897a59, - 0xd45b4f3d, 0xe9eaa3e1, 0xb58605db, 0x8837e907, 0xcee5dc63, - 0xf35430bf, 0x4341b6ab, 0x7ef05a77, 0x38226f13, 0x059383cf, - 0x8378657a, 0xbec989a6, 0xf81bbcc2, 0xc5aa501e, 0x75bfd60a, - 0x480e3ad6, 0x0edc0fb2, 0x336de36e, 0xd87ac499, 0xe5cb2845, - 0xa3191d21, 0x9ea8f1fd, 0x2ebd77e9, 0x130c9b35, 0x55deae51, - 0x686f428d, 0xee84a438, 0xd33548e4, 0x95e77d80, 0xa856915c, - 0x18431748, 0x25f2fb94, 0x6320cef0, 0x5e91222c, 0x6e7f875f, - 0x53ce6b83, 0x151c5ee7, 0x28adb23b, 0x98b8342f, 0xa509d8f3, - 0xe3dbed97, 0xde6a014b, 0x5881e7fe, 0x65300b22, 0x23e23e46, - 0x1e53d29a, 0xae46548e, 0x93f7b852, 0xd5258d36, 0xe89461ea, - 0x0383461d, 0x3e32aac1, 0x78e09fa5, 0x45517379, 0xf544f56d, - 0xc8f519b1, 0x8e272cd5, 0xb396c009, 0x357d26bc, 0x08ccca60, - 0x4e1eff04, 0x73af13d8, 0xc3ba95cc, 0xfe0b7910, 0xb8d94c74, - 0x8568a0a8}, - {0x00000000, 0x69790b65, 0xd2f216ca, 0xbb8b1daf, 0x7e952bd5, - 0x17ec20b0, 0xac673d1f, 0xc51e367a, 0xfd2a57aa, 0x94535ccf, - 0x2fd84160, 0x46a14a05, 0x83bf7c7f, 0xeac6771a, 0x514d6ab5, - 0x383461d0, 0x2125a915, 0x485ca270, 0xf3d7bfdf, 0x9aaeb4ba, - 0x5fb082c0, 0x36c989a5, 0x8d42940a, 0xe43b9f6f, 0xdc0ffebf, - 0xb576f5da, 0x0efde875, 0x6784e310, 0xa29ad56a, 0xcbe3de0f, - 0x7068c3a0, 0x1911c8c5, 0x424b522a, 0x2b32594f, 0x90b944e0, - 0xf9c04f85, 0x3cde79ff, 0x55a7729a, 0xee2c6f35, 0x87556450, - 0xbf610580, 0xd6180ee5, 0x6d93134a, 0x04ea182f, 0xc1f42e55, - 0xa88d2530, 0x1306389f, 0x7a7f33fa, 0x636efb3f, 0x0a17f05a, - 0xb19cedf5, 0xd8e5e690, 0x1dfbd0ea, 0x7482db8f, 0xcf09c620, - 0xa670cd45, 0x9e44ac95, 0xf73da7f0, 0x4cb6ba5f, 0x25cfb13a, - 0xe0d18740, 0x89a88c25, 0x3223918a, 0x5b5a9aef, 0x8496a454, - 0xedefaf31, 0x5664b29e, 0x3f1db9fb, 0xfa038f81, 0x937a84e4, - 0x28f1994b, 0x4188922e, 0x79bcf3fe, 0x10c5f89b, 0xab4ee534, - 0xc237ee51, 0x0729d82b, 0x6e50d34e, 0xd5dbcee1, 0xbca2c584, - 0xa5b30d41, 0xccca0624, 0x77411b8b, 0x1e3810ee, 0xdb262694, - 0xb25f2df1, 0x09d4305e, 0x60ad3b3b, 0x58995aeb, 0x31e0518e, - 0x8a6b4c21, 0xe3124744, 0x260c713e, 0x4f757a5b, 0xf4fe67f4, - 0x9d876c91, 0xc6ddf67e, 0xafa4fd1b, 0x142fe0b4, 0x7d56ebd1, - 0xb848ddab, 0xd131d6ce, 0x6abacb61, 0x03c3c004, 0x3bf7a1d4, - 0x528eaab1, 0xe905b71e, 0x807cbc7b, 0x45628a01, 0x2c1b8164, - 0x97909ccb, 0xfee997ae, 0xe7f85f6b, 0x8e81540e, 0x350a49a1, - 0x5c7342c4, 0x996d74be, 0xf0147fdb, 0x4b9f6274, 0x22e66911, - 0x1ad208c1, 0x73ab03a4, 0xc8201e0b, 0xa159156e, 0x64472314, - 0x0d3e2871, 0xb6b535de, 0xdfcc3ebb, 0xd25c4ee9, 0xbb25458c, - 0x00ae5823, 0x69d75346, 0xacc9653c, 0xc5b06e59, 0x7e3b73f6, - 0x17427893, 0x2f761943, 0x460f1226, 0xfd840f89, 0x94fd04ec, - 0x51e33296, 0x389a39f3, 0x8311245c, 0xea682f39, 0xf379e7fc, - 0x9a00ec99, 0x218bf136, 0x48f2fa53, 0x8deccc29, 0xe495c74c, - 0x5f1edae3, 0x3667d186, 0x0e53b056, 0x672abb33, 0xdca1a69c, - 0xb5d8adf9, 0x70c69b83, 0x19bf90e6, 0xa2348d49, 0xcb4d862c, - 0x90171cc3, 0xf96e17a6, 0x42e50a09, 0x2b9c016c, 0xee823716, - 0x87fb3c73, 0x3c7021dc, 0x55092ab9, 0x6d3d4b69, 0x0444400c, - 0xbfcf5da3, 0xd6b656c6, 0x13a860bc, 0x7ad16bd9, 0xc15a7676, - 0xa8237d13, 0xb132b5d6, 0xd84bbeb3, 0x63c0a31c, 0x0ab9a879, - 0xcfa79e03, 0xa6de9566, 0x1d5588c9, 0x742c83ac, 0x4c18e27c, - 0x2561e919, 0x9eeaf4b6, 0xf793ffd3, 0x328dc9a9, 0x5bf4c2cc, - 0xe07fdf63, 0x8906d406, 0x56caeabd, 0x3fb3e1d8, 0x8438fc77, - 0xed41f712, 0x285fc168, 0x4126ca0d, 0xfaadd7a2, 0x93d4dcc7, - 0xabe0bd17, 0xc299b672, 0x7912abdd, 0x106ba0b8, 0xd57596c2, - 0xbc0c9da7, 0x07878008, 0x6efe8b6d, 0x77ef43a8, 0x1e9648cd, - 0xa51d5562, 0xcc645e07, 0x097a687d, 0x60036318, 0xdb887eb7, - 0xb2f175d2, 0x8ac51402, 0xe3bc1f67, 0x583702c8, 0x314e09ad, - 0xf4503fd7, 0x9d2934b2, 0x26a2291d, 0x4fdb2278, 0x1481b897, - 0x7df8b3f2, 0xc673ae5d, 0xaf0aa538, 0x6a149342, 0x036d9827, - 0xb8e68588, 0xd19f8eed, 0xe9abef3d, 0x80d2e458, 0x3b59f9f7, - 0x5220f292, 0x973ec4e8, 0xfe47cf8d, 0x45ccd222, 0x2cb5d947, - 0x35a41182, 0x5cdd1ae7, 0xe7560748, 0x8e2f0c2d, 0x4b313a57, - 0x22483132, 0x99c32c9d, 0xf0ba27f8, 0xc88e4628, 0xa1f74d4d, - 0x1a7c50e2, 0x73055b87, 0xb61b6dfd, 0xdf626698, 0x64e97b37, - 0x0d907052}, - {0x00000000, 0x7fc99b93, 0xff933726, 0x805aacb5, 0x2457680d, - 0x5b9ef39e, 0xdbc45f2b, 0xa40dc4b8, 0x48aed01a, 0x37674b89, - 0xb73de73c, 0xc8f47caf, 0x6cf9b817, 0x13302384, 0x936a8f31, - 0xeca314a2, 0x915da034, 0xee943ba7, 0x6ece9712, 0x11070c81, - 0xb50ac839, 0xcac353aa, 0x4a99ff1f, 0x3550648c, 0xd9f3702e, - 0xa63aebbd, 0x26604708, 0x59a9dc9b, 0xfda41823, 0x826d83b0, - 0x02372f05, 0x7dfeb496, 0xf9ca4629, 0x8603ddba, 0x0659710f, - 0x7990ea9c, 0xdd9d2e24, 0xa254b5b7, 0x220e1902, 0x5dc78291, - 0xb1649633, 0xcead0da0, 0x4ef7a115, 0x313e3a86, 0x9533fe3e, - 0xeafa65ad, 0x6aa0c918, 0x1569528b, 0x6897e61d, 0x175e7d8e, - 0x9704d13b, 0xe8cd4aa8, 0x4cc08e10, 0x33091583, 0xb353b936, - 0xcc9a22a5, 0x20393607, 0x5ff0ad94, 0xdfaa0121, 0xa0639ab2, - 0x046e5e0a, 0x7ba7c599, 0xfbfd692c, 0x8434f2bf, 0x28e58a13, - 0x572c1180, 0xd776bd35, 0xa8bf26a6, 0x0cb2e21e, 0x737b798d, - 0xf321d538, 0x8ce84eab, 0x604b5a09, 0x1f82c19a, 0x9fd86d2f, - 0xe011f6bc, 0x441c3204, 0x3bd5a997, 0xbb8f0522, 0xc4469eb1, - 0xb9b82a27, 0xc671b1b4, 0x462b1d01, 0x39e28692, 0x9def422a, - 0xe226d9b9, 0x627c750c, 0x1db5ee9f, 0xf116fa3d, 0x8edf61ae, - 0x0e85cd1b, 0x714c5688, 0xd5419230, 0xaa8809a3, 0x2ad2a516, - 0x551b3e85, 0xd12fcc3a, 0xaee657a9, 0x2ebcfb1c, 0x5175608f, - 0xf578a437, 0x8ab13fa4, 0x0aeb9311, 0x75220882, 0x99811c20, - 0xe64887b3, 0x66122b06, 0x19dbb095, 0xbdd6742d, 0xc21fefbe, - 0x4245430b, 0x3d8cd898, 0x40726c0e, 0x3fbbf79d, 0xbfe15b28, - 0xc028c0bb, 0x64250403, 0x1bec9f90, 0x9bb63325, 0xe47fa8b6, - 0x08dcbc14, 0x77152787, 0xf74f8b32, 0x888610a1, 0x2c8bd419, - 0x53424f8a, 0xd318e33f, 0xacd178ac, 0x51cb1426, 0x2e028fb5, - 0xae582300, 0xd191b893, 0x759c7c2b, 0x0a55e7b8, 0x8a0f4b0d, - 0xf5c6d09e, 0x1965c43c, 0x66ac5faf, 0xe6f6f31a, 0x993f6889, - 0x3d32ac31, 0x42fb37a2, 0xc2a19b17, 0xbd680084, 0xc096b412, - 0xbf5f2f81, 0x3f058334, 0x40cc18a7, 0xe4c1dc1f, 0x9b08478c, - 0x1b52eb39, 0x649b70aa, 0x88386408, 0xf7f1ff9b, 0x77ab532e, - 0x0862c8bd, 0xac6f0c05, 0xd3a69796, 0x53fc3b23, 0x2c35a0b0, - 0xa801520f, 0xd7c8c99c, 0x57926529, 0x285bfeba, 0x8c563a02, - 0xf39fa191, 0x73c50d24, 0x0c0c96b7, 0xe0af8215, 0x9f661986, - 0x1f3cb533, 0x60f52ea0, 0xc4f8ea18, 0xbb31718b, 0x3b6bdd3e, - 0x44a246ad, 0x395cf23b, 0x469569a8, 0xc6cfc51d, 0xb9065e8e, - 0x1d0b9a36, 0x62c201a5, 0xe298ad10, 0x9d513683, 0x71f22221, - 0x0e3bb9b2, 0x8e611507, 0xf1a88e94, 0x55a54a2c, 0x2a6cd1bf, - 0xaa367d0a, 0xd5ffe699, 0x792e9e35, 0x06e705a6, 0x86bda913, - 0xf9743280, 0x5d79f638, 0x22b06dab, 0xa2eac11e, 0xdd235a8d, - 0x31804e2f, 0x4e49d5bc, 0xce137909, 0xb1dae29a, 0x15d72622, - 0x6a1ebdb1, 0xea441104, 0x958d8a97, 0xe8733e01, 0x97baa592, - 0x17e00927, 0x682992b4, 0xcc24560c, 0xb3edcd9f, 0x33b7612a, - 0x4c7efab9, 0xa0ddee1b, 0xdf147588, 0x5f4ed93d, 0x208742ae, - 0x848a8616, 0xfb431d85, 0x7b19b130, 0x04d02aa3, 0x80e4d81c, - 0xff2d438f, 0x7f77ef3a, 0x00be74a9, 0xa4b3b011, 0xdb7a2b82, - 0x5b208737, 0x24e91ca4, 0xc84a0806, 0xb7839395, 0x37d93f20, - 0x4810a4b3, 0xec1d600b, 0x93d4fb98, 0x138e572d, 0x6c47ccbe, - 0x11b97828, 0x6e70e3bb, 0xee2a4f0e, 0x91e3d49d, 0x35ee1025, - 0x4a278bb6, 0xca7d2703, 0xb5b4bc90, 0x5917a832, 0x26de33a1, - 0xa6849f14, 0xd94d0487, 0x7d40c03f, 0x02895bac, 0x82d3f719, - 0xfd1a6c8a}, - {0x00000000, 0xa396284c, 0x9c5d56d9, 0x3fcb7e95, 0xe3cbabf3, - 0x405d83bf, 0x7f96fd2a, 0xdc00d566, 0x1ce651a7, 0xbf7079eb, - 0x80bb077e, 0x232d2f32, 0xff2dfa54, 0x5cbbd218, 0x6370ac8d, - 0xc0e684c1, 0x39cca34e, 0x9a5a8b02, 0xa591f597, 0x0607dddb, - 0xda0708bd, 0x799120f1, 0x465a5e64, 0xe5cc7628, 0x252af2e9, - 0x86bcdaa5, 0xb977a430, 0x1ae18c7c, 0xc6e1591a, 0x65777156, - 0x5abc0fc3, 0xf92a278f, 0x7399469c, 0xd00f6ed0, 0xefc41045, - 0x4c523809, 0x9052ed6f, 0x33c4c523, 0x0c0fbbb6, 0xaf9993fa, - 0x6f7f173b, 0xcce93f77, 0xf32241e2, 0x50b469ae, 0x8cb4bcc8, - 0x2f229484, 0x10e9ea11, 0xb37fc25d, 0x4a55e5d2, 0xe9c3cd9e, - 0xd608b30b, 0x759e9b47, 0xa99e4e21, 0x0a08666d, 0x35c318f8, - 0x965530b4, 0x56b3b475, 0xf5259c39, 0xcaeee2ac, 0x6978cae0, - 0xb5781f86, 0x16ee37ca, 0x2925495f, 0x8ab36113, 0xe7328d38, - 0x44a4a574, 0x7b6fdbe1, 0xd8f9f3ad, 0x04f926cb, 0xa76f0e87, - 0x98a47012, 0x3b32585e, 0xfbd4dc9f, 0x5842f4d3, 0x67898a46, - 0xc41fa20a, 0x181f776c, 0xbb895f20, 0x844221b5, 0x27d409f9, - 0xdefe2e76, 0x7d68063a, 0x42a378af, 0xe13550e3, 0x3d358585, - 0x9ea3adc9, 0xa168d35c, 0x02fefb10, 0xc2187fd1, 0x618e579d, - 0x5e452908, 0xfdd30144, 0x21d3d422, 0x8245fc6e, 0xbd8e82fb, - 0x1e18aab7, 0x94abcba4, 0x373de3e8, 0x08f69d7d, 0xab60b531, - 0x77606057, 0xd4f6481b, 0xeb3d368e, 0x48ab1ec2, 0x884d9a03, - 0x2bdbb24f, 0x1410ccda, 0xb786e496, 0x6b8631f0, 0xc81019bc, - 0xf7db6729, 0x544d4f65, 0xad6768ea, 0x0ef140a6, 0x313a3e33, - 0x92ac167f, 0x4eacc319, 0xed3aeb55, 0xd2f195c0, 0x7167bd8c, - 0xb181394d, 0x12171101, 0x2ddc6f94, 0x8e4a47d8, 0x524a92be, - 0xf1dcbaf2, 0xce17c467, 0x6d81ec2b, 0x15141c31, 0xb682347d, - 0x89494ae8, 0x2adf62a4, 0xf6dfb7c2, 0x55499f8e, 0x6a82e11b, - 0xc914c957, 0x09f24d96, 0xaa6465da, 0x95af1b4f, 0x36393303, - 0xea39e665, 0x49afce29, 0x7664b0bc, 0xd5f298f0, 0x2cd8bf7f, - 0x8f4e9733, 0xb085e9a6, 0x1313c1ea, 0xcf13148c, 0x6c853cc0, - 0x534e4255, 0xf0d86a19, 0x303eeed8, 0x93a8c694, 0xac63b801, - 0x0ff5904d, 0xd3f5452b, 0x70636d67, 0x4fa813f2, 0xec3e3bbe, - 0x668d5aad, 0xc51b72e1, 0xfad00c74, 0x59462438, 0x8546f15e, - 0x26d0d912, 0x191ba787, 0xba8d8fcb, 0x7a6b0b0a, 0xd9fd2346, - 0xe6365dd3, 0x45a0759f, 0x99a0a0f9, 0x3a3688b5, 0x05fdf620, - 0xa66bde6c, 0x5f41f9e3, 0xfcd7d1af, 0xc31caf3a, 0x608a8776, - 0xbc8a5210, 0x1f1c7a5c, 0x20d704c9, 0x83412c85, 0x43a7a844, - 0xe0318008, 0xdffafe9d, 0x7c6cd6d1, 0xa06c03b7, 0x03fa2bfb, - 0x3c31556e, 0x9fa77d22, 0xf2269109, 0x51b0b945, 0x6e7bc7d0, - 0xcdedef9c, 0x11ed3afa, 0xb27b12b6, 0x8db06c23, 0x2e26446f, - 0xeec0c0ae, 0x4d56e8e2, 0x729d9677, 0xd10bbe3b, 0x0d0b6b5d, - 0xae9d4311, 0x91563d84, 0x32c015c8, 0xcbea3247, 0x687c1a0b, - 0x57b7649e, 0xf4214cd2, 0x282199b4, 0x8bb7b1f8, 0xb47ccf6d, - 0x17eae721, 0xd70c63e0, 0x749a4bac, 0x4b513539, 0xe8c71d75, - 0x34c7c813, 0x9751e05f, 0xa89a9eca, 0x0b0cb686, 0x81bfd795, - 0x2229ffd9, 0x1de2814c, 0xbe74a900, 0x62747c66, 0xc1e2542a, - 0xfe292abf, 0x5dbf02f3, 0x9d598632, 0x3ecfae7e, 0x0104d0eb, - 0xa292f8a7, 0x7e922dc1, 0xdd04058d, 0xe2cf7b18, 0x41595354, - 0xb87374db, 0x1be55c97, 0x242e2202, 0x87b80a4e, 0x5bb8df28, - 0xf82ef764, 0xc7e589f1, 0x6473a1bd, 0xa495257c, 0x07030d30, - 0x38c873a5, 0x9b5e5be9, 0x475e8e8f, 0xe4c8a6c3, 0xdb03d856, - 0x7895f01a}, - {0x00000000, 0x2a283862, 0x545070c4, 0x7e7848a6, 0xa8a0e188, - 0x8288d9ea, 0xfcf0914c, 0xd6d8a92e, 0x8a30c551, 0xa018fd33, - 0xde60b595, 0xf4488df7, 0x229024d9, 0x08b81cbb, 0x76c0541d, - 0x5ce86c7f, 0xcf108ce3, 0xe538b481, 0x9b40fc27, 0xb168c445, - 0x67b06d6b, 0x4d985509, 0x33e01daf, 0x19c825cd, 0x452049b2, - 0x6f0871d0, 0x11703976, 0x3b580114, 0xed80a83a, 0xc7a89058, - 0xb9d0d8fe, 0x93f8e09c, 0x45501f87, 0x6f7827e5, 0x11006f43, - 0x3b285721, 0xedf0fe0f, 0xc7d8c66d, 0xb9a08ecb, 0x9388b6a9, - 0xcf60dad6, 0xe548e2b4, 0x9b30aa12, 0xb1189270, 0x67c03b5e, - 0x4de8033c, 0x33904b9a, 0x19b873f8, 0x8a409364, 0xa068ab06, - 0xde10e3a0, 0xf438dbc2, 0x22e072ec, 0x08c84a8e, 0x76b00228, - 0x5c983a4a, 0x00705635, 0x2a586e57, 0x542026f1, 0x7e081e93, - 0xa8d0b7bd, 0x82f88fdf, 0xfc80c779, 0xd6a8ff1b, 0x8aa03f0e, - 0xa088076c, 0xdef04fca, 0xf4d877a8, 0x2200de86, 0x0828e6e4, - 0x7650ae42, 0x5c789620, 0x0090fa5f, 0x2ab8c23d, 0x54c08a9b, - 0x7ee8b2f9, 0xa8301bd7, 0x821823b5, 0xfc606b13, 0xd6485371, - 0x45b0b3ed, 0x6f988b8f, 0x11e0c329, 0x3bc8fb4b, 0xed105265, - 0xc7386a07, 0xb94022a1, 0x93681ac3, 0xcf8076bc, 0xe5a84ede, - 0x9bd00678, 0xb1f83e1a, 0x67209734, 0x4d08af56, 0x3370e7f0, - 0x1958df92, 0xcff02089, 0xe5d818eb, 0x9ba0504d, 0xb188682f, - 0x6750c101, 0x4d78f963, 0x3300b1c5, 0x192889a7, 0x45c0e5d8, - 0x6fe8ddba, 0x1190951c, 0x3bb8ad7e, 0xed600450, 0xc7483c32, - 0xb9307494, 0x93184cf6, 0x00e0ac6a, 0x2ac89408, 0x54b0dcae, - 0x7e98e4cc, 0xa8404de2, 0x82687580, 0xfc103d26, 0xd6380544, - 0x8ad0693b, 0xa0f85159, 0xde8019ff, 0xf4a8219d, 0x227088b3, - 0x0858b0d1, 0x7620f877, 0x5c08c015, 0xce31785d, 0xe419403f, - 0x9a610899, 0xb04930fb, 0x669199d5, 0x4cb9a1b7, 0x32c1e911, - 0x18e9d173, 0x4401bd0c, 0x6e29856e, 0x1051cdc8, 0x3a79f5aa, - 0xeca15c84, 0xc68964e6, 0xb8f12c40, 0x92d91422, 0x0121f4be, - 0x2b09ccdc, 0x5571847a, 0x7f59bc18, 0xa9811536, 0x83a92d54, - 0xfdd165f2, 0xd7f95d90, 0x8b1131ef, 0xa139098d, 0xdf41412b, - 0xf5697949, 0x23b1d067, 0x0999e805, 0x77e1a0a3, 0x5dc998c1, - 0x8b6167da, 0xa1495fb8, 0xdf31171e, 0xf5192f7c, 0x23c18652, - 0x09e9be30, 0x7791f696, 0x5db9cef4, 0x0151a28b, 0x2b799ae9, - 0x5501d24f, 0x7f29ea2d, 0xa9f14303, 0x83d97b61, 0xfda133c7, - 0xd7890ba5, 0x4471eb39, 0x6e59d35b, 0x10219bfd, 0x3a09a39f, - 0xecd10ab1, 0xc6f932d3, 0xb8817a75, 0x92a94217, 0xce412e68, - 0xe469160a, 0x9a115eac, 0xb03966ce, 0x66e1cfe0, 0x4cc9f782, - 0x32b1bf24, 0x18998746, 0x44914753, 0x6eb97f31, 0x10c13797, - 0x3ae90ff5, 0xec31a6db, 0xc6199eb9, 0xb861d61f, 0x9249ee7d, - 0xcea18202, 0xe489ba60, 0x9af1f2c6, 0xb0d9caa4, 0x6601638a, - 0x4c295be8, 0x3251134e, 0x18792b2c, 0x8b81cbb0, 0xa1a9f3d2, - 0xdfd1bb74, 0xf5f98316, 0x23212a38, 0x0909125a, 0x77715afc, - 0x5d59629e, 0x01b10ee1, 0x2b993683, 0x55e17e25, 0x7fc94647, - 0xa911ef69, 0x8339d70b, 0xfd419fad, 0xd769a7cf, 0x01c158d4, - 0x2be960b6, 0x55912810, 0x7fb91072, 0xa961b95c, 0x8349813e, - 0xfd31c998, 0xd719f1fa, 0x8bf19d85, 0xa1d9a5e7, 0xdfa1ed41, - 0xf589d523, 0x23517c0d, 0x0979446f, 0x77010cc9, 0x5d2934ab, - 0xced1d437, 0xe4f9ec55, 0x9a81a4f3, 0xb0a99c91, 0x667135bf, - 0x4c590ddd, 0x3221457b, 0x18097d19, 0x44e11166, 0x6ec92904, - 0x10b161a2, 0x3a9959c0, 0xec41f0ee, 0xc669c88c, 0xb811802a, - 0x9239b848}, - {0x00000000, 0x4713f6fb, 0x8e27edf6, 0xc9341b0d, 0xc73eddad, - 0x802d2b56, 0x4919305b, 0x0e0ac6a0, 0x550cbd1b, 0x121f4be0, - 0xdb2b50ed, 0x9c38a616, 0x923260b6, 0xd521964d, 0x1c158d40, - 0x5b067bbb, 0xaa197a36, 0xed0a8ccd, 0x243e97c0, 0x632d613b, - 0x6d27a79b, 0x2a345160, 0xe3004a6d, 0xa413bc96, 0xff15c72d, - 0xb80631d6, 0x71322adb, 0x3621dc20, 0x382b1a80, 0x7f38ec7b, - 0xb60cf776, 0xf11f018d, 0x8f43f22d, 0xc85004d6, 0x01641fdb, - 0x4677e920, 0x487d2f80, 0x0f6ed97b, 0xc65ac276, 0x8149348d, - 0xda4f4f36, 0x9d5cb9cd, 0x5468a2c0, 0x137b543b, 0x1d71929b, - 0x5a626460, 0x93567f6d, 0xd4458996, 0x255a881b, 0x62497ee0, - 0xab7d65ed, 0xec6e9316, 0xe26455b6, 0xa577a34d, 0x6c43b840, - 0x2b504ebb, 0x70563500, 0x3745c3fb, 0xfe71d8f6, 0xb9622e0d, - 0xb768e8ad, 0xf07b1e56, 0x394f055b, 0x7e5cf3a0, 0xc5f6e21b, - 0x82e514e0, 0x4bd10fed, 0x0cc2f916, 0x02c83fb6, 0x45dbc94d, - 0x8cefd240, 0xcbfc24bb, 0x90fa5f00, 0xd7e9a9fb, 0x1eddb2f6, - 0x59ce440d, 0x57c482ad, 0x10d77456, 0xd9e36f5b, 0x9ef099a0, - 0x6fef982d, 0x28fc6ed6, 0xe1c875db, 0xa6db8320, 0xa8d14580, - 0xefc2b37b, 0x26f6a876, 0x61e55e8d, 0x3ae32536, 0x7df0d3cd, - 0xb4c4c8c0, 0xf3d73e3b, 0xfdddf89b, 0xbace0e60, 0x73fa156d, - 0x34e9e396, 0x4ab51036, 0x0da6e6cd, 0xc492fdc0, 0x83810b3b, - 0x8d8bcd9b, 0xca983b60, 0x03ac206d, 0x44bfd696, 0x1fb9ad2d, - 0x58aa5bd6, 0x919e40db, 0xd68db620, 0xd8877080, 0x9f94867b, - 0x56a09d76, 0x11b36b8d, 0xe0ac6a00, 0xa7bf9cfb, 0x6e8b87f6, - 0x2998710d, 0x2792b7ad, 0x60814156, 0xa9b55a5b, 0xeea6aca0, - 0xb5a0d71b, 0xf2b321e0, 0x3b873aed, 0x7c94cc16, 0x729e0ab6, - 0x358dfc4d, 0xfcb9e740, 0xbbaa11bb, 0x509cc277, 0x178f348c, - 0xdebb2f81, 0x99a8d97a, 0x97a21fda, 0xd0b1e921, 0x1985f22c, - 0x5e9604d7, 0x05907f6c, 0x42838997, 0x8bb7929a, 0xcca46461, - 0xc2aea2c1, 0x85bd543a, 0x4c894f37, 0x0b9ab9cc, 0xfa85b841, - 0xbd964eba, 0x74a255b7, 0x33b1a34c, 0x3dbb65ec, 0x7aa89317, - 0xb39c881a, 0xf48f7ee1, 0xaf89055a, 0xe89af3a1, 0x21aee8ac, - 0x66bd1e57, 0x68b7d8f7, 0x2fa42e0c, 0xe6903501, 0xa183c3fa, - 0xdfdf305a, 0x98ccc6a1, 0x51f8ddac, 0x16eb2b57, 0x18e1edf7, - 0x5ff21b0c, 0x96c60001, 0xd1d5f6fa, 0x8ad38d41, 0xcdc07bba, - 0x04f460b7, 0x43e7964c, 0x4ded50ec, 0x0afea617, 0xc3cabd1a, - 0x84d94be1, 0x75c64a6c, 0x32d5bc97, 0xfbe1a79a, 0xbcf25161, - 0xb2f897c1, 0xf5eb613a, 0x3cdf7a37, 0x7bcc8ccc, 0x20caf777, - 0x67d9018c, 0xaeed1a81, 0xe9feec7a, 0xe7f42ada, 0xa0e7dc21, - 0x69d3c72c, 0x2ec031d7, 0x956a206c, 0xd279d697, 0x1b4dcd9a, - 0x5c5e3b61, 0x5254fdc1, 0x15470b3a, 0xdc731037, 0x9b60e6cc, - 0xc0669d77, 0x87756b8c, 0x4e417081, 0x0952867a, 0x075840da, - 0x404bb621, 0x897fad2c, 0xce6c5bd7, 0x3f735a5a, 0x7860aca1, - 0xb154b7ac, 0xf6474157, 0xf84d87f7, 0xbf5e710c, 0x766a6a01, - 0x31799cfa, 0x6a7fe741, 0x2d6c11ba, 0xe4580ab7, 0xa34bfc4c, - 0xad413aec, 0xea52cc17, 0x2366d71a, 0x647521e1, 0x1a29d241, - 0x5d3a24ba, 0x940e3fb7, 0xd31dc94c, 0xdd170fec, 0x9a04f917, - 0x5330e21a, 0x142314e1, 0x4f256f5a, 0x083699a1, 0xc10282ac, - 0x86117457, 0x881bb2f7, 0xcf08440c, 0x063c5f01, 0x412fa9fa, - 0xb030a877, 0xf7235e8c, 0x3e174581, 0x7904b37a, 0x770e75da, - 0x301d8321, 0xf929982c, 0xbe3a6ed7, 0xe53c156c, 0xa22fe397, - 0x6b1bf89a, 0x2c080e61, 0x2202c8c1, 0x65113e3a, 0xac252537, - 0xeb36d3cc}, - {0x00000000, 0xa13984ee, 0x99020f9d, 0x383b8b73, 0xe975197b, - 0x484c9d95, 0x707716e6, 0xd14e9208, 0x099b34b7, 0xa8a2b059, - 0x90993b2a, 0x31a0bfc4, 0xe0ee2dcc, 0x41d7a922, 0x79ec2251, - 0xd8d5a6bf, 0x1336696e, 0xb20fed80, 0x8a3466f3, 0x2b0de21d, - 0xfa437015, 0x5b7af4fb, 0x63417f88, 0xc278fb66, 0x1aad5dd9, - 0xbb94d937, 0x83af5244, 0x2296d6aa, 0xf3d844a2, 0x52e1c04c, - 0x6ada4b3f, 0xcbe3cfd1, 0x266cd2dc, 0x87555632, 0xbf6edd41, - 0x1e5759af, 0xcf19cba7, 0x6e204f49, 0x561bc43a, 0xf72240d4, - 0x2ff7e66b, 0x8ece6285, 0xb6f5e9f6, 0x17cc6d18, 0xc682ff10, - 0x67bb7bfe, 0x5f80f08d, 0xfeb97463, 0x355abbb2, 0x94633f5c, - 0xac58b42f, 0x0d6130c1, 0xdc2fa2c9, 0x7d162627, 0x452dad54, - 0xe41429ba, 0x3cc18f05, 0x9df80beb, 0xa5c38098, 0x04fa0476, - 0xd5b4967e, 0x748d1290, 0x4cb699e3, 0xed8f1d0d, 0x4cd9a5b8, - 0xede02156, 0xd5dbaa25, 0x74e22ecb, 0xa5acbcc3, 0x0495382d, - 0x3caeb35e, 0x9d9737b0, 0x4542910f, 0xe47b15e1, 0xdc409e92, - 0x7d791a7c, 0xac378874, 0x0d0e0c9a, 0x353587e9, 0x940c0307, - 0x5fefccd6, 0xfed64838, 0xc6edc34b, 0x67d447a5, 0xb69ad5ad, - 0x17a35143, 0x2f98da30, 0x8ea15ede, 0x5674f861, 0xf74d7c8f, - 0xcf76f7fc, 0x6e4f7312, 0xbf01e11a, 0x1e3865f4, 0x2603ee87, - 0x873a6a69, 0x6ab57764, 0xcb8cf38a, 0xf3b778f9, 0x528efc17, - 0x83c06e1f, 0x22f9eaf1, 0x1ac26182, 0xbbfbe56c, 0x632e43d3, - 0xc217c73d, 0xfa2c4c4e, 0x5b15c8a0, 0x8a5b5aa8, 0x2b62de46, - 0x13595535, 0xb260d1db, 0x79831e0a, 0xd8ba9ae4, 0xe0811197, - 0x41b89579, 0x90f60771, 0x31cf839f, 0x09f408ec, 0xa8cd8c02, - 0x70182abd, 0xd121ae53, 0xe91a2520, 0x4823a1ce, 0x996d33c6, - 0x3854b728, 0x006f3c5b, 0xa156b8b5, 0x99b34b70, 0x388acf9e, - 0x00b144ed, 0xa188c003, 0x70c6520b, 0xd1ffd6e5, 0xe9c45d96, - 0x48fdd978, 0x90287fc7, 0x3111fb29, 0x092a705a, 0xa813f4b4, - 0x795d66bc, 0xd864e252, 0xe05f6921, 0x4166edcf, 0x8a85221e, - 0x2bbca6f0, 0x13872d83, 0xb2bea96d, 0x63f03b65, 0xc2c9bf8b, - 0xfaf234f8, 0x5bcbb016, 0x831e16a9, 0x22279247, 0x1a1c1934, - 0xbb259dda, 0x6a6b0fd2, 0xcb528b3c, 0xf369004f, 0x525084a1, - 0xbfdf99ac, 0x1ee61d42, 0x26dd9631, 0x87e412df, 0x56aa80d7, - 0xf7930439, 0xcfa88f4a, 0x6e910ba4, 0xb644ad1b, 0x177d29f5, - 0x2f46a286, 0x8e7f2668, 0x5f31b460, 0xfe08308e, 0xc633bbfd, - 0x670a3f13, 0xace9f0c2, 0x0dd0742c, 0x35ebff5f, 0x94d27bb1, - 0x459ce9b9, 0xe4a56d57, 0xdc9ee624, 0x7da762ca, 0xa572c475, - 0x044b409b, 0x3c70cbe8, 0x9d494f06, 0x4c07dd0e, 0xed3e59e0, - 0xd505d293, 0x743c567d, 0xd56aeec8, 0x74536a26, 0x4c68e155, - 0xed5165bb, 0x3c1ff7b3, 0x9d26735d, 0xa51df82e, 0x04247cc0, - 0xdcf1da7f, 0x7dc85e91, 0x45f3d5e2, 0xe4ca510c, 0x3584c304, - 0x94bd47ea, 0xac86cc99, 0x0dbf4877, 0xc65c87a6, 0x67650348, - 0x5f5e883b, 0xfe670cd5, 0x2f299edd, 0x8e101a33, 0xb62b9140, - 0x171215ae, 0xcfc7b311, 0x6efe37ff, 0x56c5bc8c, 0xf7fc3862, - 0x26b2aa6a, 0x878b2e84, 0xbfb0a5f7, 0x1e892119, 0xf3063c14, - 0x523fb8fa, 0x6a043389, 0xcb3db767, 0x1a73256f, 0xbb4aa181, - 0x83712af2, 0x2248ae1c, 0xfa9d08a3, 0x5ba48c4d, 0x639f073e, - 0xc2a683d0, 0x13e811d8, 0xb2d19536, 0x8aea1e45, 0x2bd39aab, - 0xe030557a, 0x4109d194, 0x79325ae7, 0xd80bde09, 0x09454c01, - 0xa87cc8ef, 0x9047439c, 0x317ec772, 0xe9ab61cd, 0x4892e523, - 0x70a96e50, 0xd190eabe, 0x00de78b6, 0xa1e7fc58, 0x99dc772b, - 0x38e5f3c5}, - {0x00000000, 0xe81790a1, 0x0b5e2703, 0xe349b7a2, 0x16bc4e06, - 0xfeabdea7, 0x1de26905, 0xf5f5f9a4, 0x2d789c0c, 0xc56f0cad, - 0x2626bb0f, 0xce312bae, 0x3bc4d20a, 0xd3d342ab, 0x309af509, - 0xd88d65a8, 0x5af13818, 0xb2e6a8b9, 0x51af1f1b, 0xb9b88fba, - 0x4c4d761e, 0xa45ae6bf, 0x4713511d, 0xaf04c1bc, 0x7789a414, - 0x9f9e34b5, 0x7cd78317, 0x94c013b6, 0x6135ea12, 0x89227ab3, - 0x6a6bcd11, 0x827c5db0, 0xb5e27030, 0x5df5e091, 0xbebc5733, - 0x56abc792, 0xa35e3e36, 0x4b49ae97, 0xa8001935, 0x40178994, - 0x989aec3c, 0x708d7c9d, 0x93c4cb3f, 0x7bd35b9e, 0x8e26a23a, - 0x6631329b, 0x85788539, 0x6d6f1598, 0xef134828, 0x0704d889, - 0xe44d6f2b, 0x0c5aff8a, 0xf9af062e, 0x11b8968f, 0xf2f1212d, - 0x1ae6b18c, 0xc26bd424, 0x2a7c4485, 0xc935f327, 0x21226386, - 0xd4d79a22, 0x3cc00a83, 0xdf89bd21, 0x379e2d80, 0xb0b5e621, - 0x58a27680, 0xbbebc122, 0x53fc5183, 0xa609a827, 0x4e1e3886, - 0xad578f24, 0x45401f85, 0x9dcd7a2d, 0x75daea8c, 0x96935d2e, - 0x7e84cd8f, 0x8b71342b, 0x6366a48a, 0x802f1328, 0x68388389, - 0xea44de39, 0x02534e98, 0xe11af93a, 0x090d699b, 0xfcf8903f, - 0x14ef009e, 0xf7a6b73c, 0x1fb1279d, 0xc73c4235, 0x2f2bd294, - 0xcc626536, 0x2475f597, 0xd1800c33, 0x39979c92, 0xdade2b30, - 0x32c9bb91, 0x05579611, 0xed4006b0, 0x0e09b112, 0xe61e21b3, - 0x13ebd817, 0xfbfc48b6, 0x18b5ff14, 0xf0a26fb5, 0x282f0a1d, - 0xc0389abc, 0x23712d1e, 0xcb66bdbf, 0x3e93441b, 0xd684d4ba, - 0x35cd6318, 0xdddaf3b9, 0x5fa6ae09, 0xb7b13ea8, 0x54f8890a, - 0xbcef19ab, 0x491ae00f, 0xa10d70ae, 0x4244c70c, 0xaa5357ad, - 0x72de3205, 0x9ac9a2a4, 0x79801506, 0x919785a7, 0x64627c03, - 0x8c75eca2, 0x6f3c5b00, 0x872bcba1, 0xba1aca03, 0x520d5aa2, - 0xb144ed00, 0x59537da1, 0xaca68405, 0x44b114a4, 0xa7f8a306, - 0x4fef33a7, 0x9762560f, 0x7f75c6ae, 0x9c3c710c, 0x742be1ad, - 0x81de1809, 0x69c988a8, 0x8a803f0a, 0x6297afab, 0xe0ebf21b, - 0x08fc62ba, 0xebb5d518, 0x03a245b9, 0xf657bc1d, 0x1e402cbc, - 0xfd099b1e, 0x151e0bbf, 0xcd936e17, 0x2584feb6, 0xc6cd4914, - 0x2edad9b5, 0xdb2f2011, 0x3338b0b0, 0xd0710712, 0x386697b3, - 0x0ff8ba33, 0xe7ef2a92, 0x04a69d30, 0xecb10d91, 0x1944f435, - 0xf1536494, 0x121ad336, 0xfa0d4397, 0x2280263f, 0xca97b69e, - 0x29de013c, 0xc1c9919d, 0x343c6839, 0xdc2bf898, 0x3f624f3a, - 0xd775df9b, 0x5509822b, 0xbd1e128a, 0x5e57a528, 0xb6403589, - 0x43b5cc2d, 0xaba25c8c, 0x48ebeb2e, 0xa0fc7b8f, 0x78711e27, - 0x90668e86, 0x732f3924, 0x9b38a985, 0x6ecd5021, 0x86dac080, - 0x65937722, 0x8d84e783, 0x0aaf2c22, 0xe2b8bc83, 0x01f10b21, - 0xe9e69b80, 0x1c136224, 0xf404f285, 0x174d4527, 0xff5ad586, - 0x27d7b02e, 0xcfc0208f, 0x2c89972d, 0xc49e078c, 0x316bfe28, - 0xd97c6e89, 0x3a35d92b, 0xd222498a, 0x505e143a, 0xb849849b, - 0x5b003339, 0xb317a398, 0x46e25a3c, 0xaef5ca9d, 0x4dbc7d3f, - 0xa5abed9e, 0x7d268836, 0x95311897, 0x7678af35, 0x9e6f3f94, - 0x6b9ac630, 0x838d5691, 0x60c4e133, 0x88d37192, 0xbf4d5c12, - 0x575accb3, 0xb4137b11, 0x5c04ebb0, 0xa9f11214, 0x41e682b5, - 0xa2af3517, 0x4ab8a5b6, 0x9235c01e, 0x7a2250bf, 0x996be71d, - 0x717c77bc, 0x84898e18, 0x6c9e1eb9, 0x8fd7a91b, 0x67c039ba, - 0xe5bc640a, 0x0dabf4ab, 0xeee24309, 0x06f5d3a8, 0xf3002a0c, - 0x1b17baad, 0xf85e0d0f, 0x10499dae, 0xc8c4f806, 0x20d368a7, - 0xc39adf05, 0x2b8d4fa4, 0xde78b600, 0x366f26a1, 0xd5269103, - 0x3d3101a2}}; - -local const z_word_t FAR crc_braid_big_table[][256] = { - {0x0000000000000000, 0xa19017e800000000, 0x03275e0b00000000, - 0xa2b749e300000000, 0x064ebc1600000000, 0xa7deabfe00000000, - 0x0569e21d00000000, 0xa4f9f5f500000000, 0x0c9c782d00000000, - 0xad0c6fc500000000, 0x0fbb262600000000, 0xae2b31ce00000000, - 0x0ad2c43b00000000, 0xab42d3d300000000, 0x09f59a3000000000, - 0xa8658dd800000000, 0x1838f15a00000000, 0xb9a8e6b200000000, - 0x1b1faf5100000000, 0xba8fb8b900000000, 0x1e764d4c00000000, - 0xbfe65aa400000000, 0x1d51134700000000, 0xbcc104af00000000, - 0x14a4897700000000, 0xb5349e9f00000000, 0x1783d77c00000000, - 0xb613c09400000000, 0x12ea356100000000, 0xb37a228900000000, - 0x11cd6b6a00000000, 0xb05d7c8200000000, 0x3070e2b500000000, - 0x91e0f55d00000000, 0x3357bcbe00000000, 0x92c7ab5600000000, - 0x363e5ea300000000, 0x97ae494b00000000, 0x351900a800000000, - 0x9489174000000000, 0x3cec9a9800000000, 0x9d7c8d7000000000, - 0x3fcbc49300000000, 0x9e5bd37b00000000, 0x3aa2268e00000000, - 0x9b32316600000000, 0x3985788500000000, 0x98156f6d00000000, - 0x284813ef00000000, 0x89d8040700000000, 0x2b6f4de400000000, - 0x8aff5a0c00000000, 0x2e06aff900000000, 0x8f96b81100000000, - 0x2d21f1f200000000, 0x8cb1e61a00000000, 0x24d46bc200000000, - 0x85447c2a00000000, 0x27f335c900000000, 0x8663222100000000, - 0x229ad7d400000000, 0x830ac03c00000000, 0x21bd89df00000000, - 0x802d9e3700000000, 0x21e6b5b000000000, 0x8076a25800000000, - 0x22c1ebbb00000000, 0x8351fc5300000000, 0x27a809a600000000, - 0x86381e4e00000000, 0x248f57ad00000000, 0x851f404500000000, - 0x2d7acd9d00000000, 0x8ceada7500000000, 0x2e5d939600000000, - 0x8fcd847e00000000, 0x2b34718b00000000, 0x8aa4666300000000, - 0x28132f8000000000, 0x8983386800000000, 0x39de44ea00000000, - 0x984e530200000000, 0x3af91ae100000000, 0x9b690d0900000000, - 0x3f90f8fc00000000, 0x9e00ef1400000000, 0x3cb7a6f700000000, - 0x9d27b11f00000000, 0x35423cc700000000, 0x94d22b2f00000000, - 0x366562cc00000000, 0x97f5752400000000, 0x330c80d100000000, - 0x929c973900000000, 0x302bdeda00000000, 0x91bbc93200000000, - 0x1196570500000000, 0xb00640ed00000000, 0x12b1090e00000000, - 0xb3211ee600000000, 0x17d8eb1300000000, 0xb648fcfb00000000, - 0x14ffb51800000000, 0xb56fa2f000000000, 0x1d0a2f2800000000, - 0xbc9a38c000000000, 0x1e2d712300000000, 0xbfbd66cb00000000, - 0x1b44933e00000000, 0xbad484d600000000, 0x1863cd3500000000, - 0xb9f3dadd00000000, 0x09aea65f00000000, 0xa83eb1b700000000, - 0x0a89f85400000000, 0xab19efbc00000000, 0x0fe01a4900000000, - 0xae700da100000000, 0x0cc7444200000000, 0xad5753aa00000000, - 0x0532de7200000000, 0xa4a2c99a00000000, 0x0615807900000000, - 0xa785979100000000, 0x037c626400000000, 0xa2ec758c00000000, - 0x005b3c6f00000000, 0xa1cb2b8700000000, 0x03ca1aba00000000, - 0xa25a0d5200000000, 0x00ed44b100000000, 0xa17d535900000000, - 0x0584a6ac00000000, 0xa414b14400000000, 0x06a3f8a700000000, - 0xa733ef4f00000000, 0x0f56629700000000, 0xaec6757f00000000, - 0x0c713c9c00000000, 0xade12b7400000000, 0x0918de8100000000, - 0xa888c96900000000, 0x0a3f808a00000000, 0xabaf976200000000, - 0x1bf2ebe000000000, 0xba62fc0800000000, 0x18d5b5eb00000000, - 0xb945a20300000000, 0x1dbc57f600000000, 0xbc2c401e00000000, - 0x1e9b09fd00000000, 0xbf0b1e1500000000, 0x176e93cd00000000, - 0xb6fe842500000000, 0x1449cdc600000000, 0xb5d9da2e00000000, - 0x11202fdb00000000, 0xb0b0383300000000, 0x120771d000000000, - 0xb397663800000000, 0x33baf80f00000000, 0x922aefe700000000, - 0x309da60400000000, 0x910db1ec00000000, 0x35f4441900000000, - 0x946453f100000000, 0x36d31a1200000000, 0x97430dfa00000000, - 0x3f26802200000000, 0x9eb697ca00000000, 0x3c01de2900000000, - 0x9d91c9c100000000, 0x39683c3400000000, 0x98f82bdc00000000, - 0x3a4f623f00000000, 0x9bdf75d700000000, 0x2b82095500000000, - 0x8a121ebd00000000, 0x28a5575e00000000, 0x893540b600000000, - 0x2dccb54300000000, 0x8c5ca2ab00000000, 0x2eebeb4800000000, - 0x8f7bfca000000000, 0x271e717800000000, 0x868e669000000000, - 0x24392f7300000000, 0x85a9389b00000000, 0x2150cd6e00000000, - 0x80c0da8600000000, 0x2277936500000000, 0x83e7848d00000000, - 0x222caf0a00000000, 0x83bcb8e200000000, 0x210bf10100000000, - 0x809be6e900000000, 0x2462131c00000000, 0x85f204f400000000, - 0x27454d1700000000, 0x86d55aff00000000, 0x2eb0d72700000000, - 0x8f20c0cf00000000, 0x2d97892c00000000, 0x8c079ec400000000, - 0x28fe6b3100000000, 0x896e7cd900000000, 0x2bd9353a00000000, - 0x8a4922d200000000, 0x3a145e5000000000, 0x9b8449b800000000, - 0x3933005b00000000, 0x98a317b300000000, 0x3c5ae24600000000, - 0x9dcaf5ae00000000, 0x3f7dbc4d00000000, 0x9eedaba500000000, - 0x3688267d00000000, 0x9718319500000000, 0x35af787600000000, - 0x943f6f9e00000000, 0x30c69a6b00000000, 0x91568d8300000000, - 0x33e1c46000000000, 0x9271d38800000000, 0x125c4dbf00000000, - 0xb3cc5a5700000000, 0x117b13b400000000, 0xb0eb045c00000000, - 0x1412f1a900000000, 0xb582e64100000000, 0x1735afa200000000, - 0xb6a5b84a00000000, 0x1ec0359200000000, 0xbf50227a00000000, - 0x1de76b9900000000, 0xbc777c7100000000, 0x188e898400000000, - 0xb91e9e6c00000000, 0x1ba9d78f00000000, 0xba39c06700000000, - 0x0a64bce500000000, 0xabf4ab0d00000000, 0x0943e2ee00000000, - 0xa8d3f50600000000, 0x0c2a00f300000000, 0xadba171b00000000, - 0x0f0d5ef800000000, 0xae9d491000000000, 0x06f8c4c800000000, - 0xa768d32000000000, 0x05df9ac300000000, 0xa44f8d2b00000000, - 0x00b678de00000000, 0xa1266f3600000000, 0x039126d500000000, - 0xa201313d00000000}, - {0x0000000000000000, 0xee8439a100000000, 0x9d0f029900000000, - 0x738b3b3800000000, 0x7b1975e900000000, 0x959d4c4800000000, - 0xe616777000000000, 0x08924ed100000000, 0xb7349b0900000000, - 0x59b0a2a800000000, 0x2a3b999000000000, 0xc4bfa03100000000, - 0xcc2deee000000000, 0x22a9d74100000000, 0x5122ec7900000000, - 0xbfa6d5d800000000, 0x6e69361300000000, 0x80ed0fb200000000, - 0xf366348a00000000, 0x1de20d2b00000000, 0x157043fa00000000, - 0xfbf47a5b00000000, 0x887f416300000000, 0x66fb78c200000000, - 0xd95dad1a00000000, 0x37d994bb00000000, 0x4452af8300000000, - 0xaad6962200000000, 0xa244d8f300000000, 0x4cc0e15200000000, - 0x3f4bda6a00000000, 0xd1cfe3cb00000000, 0xdcd26c2600000000, - 0x3256558700000000, 0x41dd6ebf00000000, 0xaf59571e00000000, - 0xa7cb19cf00000000, 0x494f206e00000000, 0x3ac41b5600000000, - 0xd44022f700000000, 0x6be6f72f00000000, 0x8562ce8e00000000, - 0xf6e9f5b600000000, 0x186dcc1700000000, 0x10ff82c600000000, - 0xfe7bbb6700000000, 0x8df0805f00000000, 0x6374b9fe00000000, - 0xb2bb5a3500000000, 0x5c3f639400000000, 0x2fb458ac00000000, - 0xc130610d00000000, 0xc9a22fdc00000000, 0x2726167d00000000, - 0x54ad2d4500000000, 0xba2914e400000000, 0x058fc13c00000000, - 0xeb0bf89d00000000, 0x9880c3a500000000, 0x7604fa0400000000, - 0x7e96b4d500000000, 0x90128d7400000000, 0xe399b64c00000000, - 0x0d1d8fed00000000, 0xb8a5d94c00000000, 0x5621e0ed00000000, - 0x25aadbd500000000, 0xcb2ee27400000000, 0xc3bcaca500000000, - 0x2d38950400000000, 0x5eb3ae3c00000000, 0xb037979d00000000, - 0x0f91424500000000, 0xe1157be400000000, 0x929e40dc00000000, - 0x7c1a797d00000000, 0x748837ac00000000, 0x9a0c0e0d00000000, - 0xe987353500000000, 0x07030c9400000000, 0xd6ccef5f00000000, - 0x3848d6fe00000000, 0x4bc3edc600000000, 0xa547d46700000000, - 0xadd59ab600000000, 0x4351a31700000000, 0x30da982f00000000, - 0xde5ea18e00000000, 0x61f8745600000000, 0x8f7c4df700000000, - 0xfcf776cf00000000, 0x12734f6e00000000, 0x1ae101bf00000000, - 0xf465381e00000000, 0x87ee032600000000, 0x696a3a8700000000, - 0x6477b56a00000000, 0x8af38ccb00000000, 0xf978b7f300000000, - 0x17fc8e5200000000, 0x1f6ec08300000000, 0xf1eaf92200000000, - 0x8261c21a00000000, 0x6ce5fbbb00000000, 0xd3432e6300000000, - 0x3dc717c200000000, 0x4e4c2cfa00000000, 0xa0c8155b00000000, - 0xa85a5b8a00000000, 0x46de622b00000000, 0x3555591300000000, - 0xdbd160b200000000, 0x0a1e837900000000, 0xe49abad800000000, - 0x971181e000000000, 0x7995b84100000000, 0x7107f69000000000, - 0x9f83cf3100000000, 0xec08f40900000000, 0x028ccda800000000, - 0xbd2a187000000000, 0x53ae21d100000000, 0x20251ae900000000, - 0xcea1234800000000, 0xc6336d9900000000, 0x28b7543800000000, - 0x5b3c6f0000000000, 0xb5b856a100000000, 0x704bb39900000000, - 0x9ecf8a3800000000, 0xed44b10000000000, 0x03c088a100000000, - 0x0b52c67000000000, 0xe5d6ffd100000000, 0x965dc4e900000000, - 0x78d9fd4800000000, 0xc77f289000000000, 0x29fb113100000000, - 0x5a702a0900000000, 0xb4f413a800000000, 0xbc665d7900000000, - 0x52e264d800000000, 0x21695fe000000000, 0xcfed664100000000, - 0x1e22858a00000000, 0xf0a6bc2b00000000, 0x832d871300000000, - 0x6da9beb200000000, 0x653bf06300000000, 0x8bbfc9c200000000, - 0xf834f2fa00000000, 0x16b0cb5b00000000, 0xa9161e8300000000, - 0x4792272200000000, 0x34191c1a00000000, 0xda9d25bb00000000, - 0xd20f6b6a00000000, 0x3c8b52cb00000000, 0x4f0069f300000000, - 0xa184505200000000, 0xac99dfbf00000000, 0x421de61e00000000, - 0x3196dd2600000000, 0xdf12e48700000000, 0xd780aa5600000000, - 0x390493f700000000, 0x4a8fa8cf00000000, 0xa40b916e00000000, - 0x1bad44b600000000, 0xf5297d1700000000, 0x86a2462f00000000, - 0x68267f8e00000000, 0x60b4315f00000000, 0x8e3008fe00000000, - 0xfdbb33c600000000, 0x133f0a6700000000, 0xc2f0e9ac00000000, - 0x2c74d00d00000000, 0x5fffeb3500000000, 0xb17bd29400000000, - 0xb9e99c4500000000, 0x576da5e400000000, 0x24e69edc00000000, - 0xca62a77d00000000, 0x75c472a500000000, 0x9b404b0400000000, - 0xe8cb703c00000000, 0x064f499d00000000, 0x0edd074c00000000, - 0xe0593eed00000000, 0x93d205d500000000, 0x7d563c7400000000, - 0xc8ee6ad500000000, 0x266a537400000000, 0x55e1684c00000000, - 0xbb6551ed00000000, 0xb3f71f3c00000000, 0x5d73269d00000000, - 0x2ef81da500000000, 0xc07c240400000000, 0x7fdaf1dc00000000, - 0x915ec87d00000000, 0xe2d5f34500000000, 0x0c51cae400000000, - 0x04c3843500000000, 0xea47bd9400000000, 0x99cc86ac00000000, - 0x7748bf0d00000000, 0xa6875cc600000000, 0x4803656700000000, - 0x3b885e5f00000000, 0xd50c67fe00000000, 0xdd9e292f00000000, - 0x331a108e00000000, 0x40912bb600000000, 0xae15121700000000, - 0x11b3c7cf00000000, 0xff37fe6e00000000, 0x8cbcc55600000000, - 0x6238fcf700000000, 0x6aaab22600000000, 0x842e8b8700000000, - 0xf7a5b0bf00000000, 0x1921891e00000000, 0x143c06f300000000, - 0xfab83f5200000000, 0x8933046a00000000, 0x67b73dcb00000000, - 0x6f25731a00000000, 0x81a14abb00000000, 0xf22a718300000000, - 0x1cae482200000000, 0xa3089dfa00000000, 0x4d8ca45b00000000, - 0x3e079f6300000000, 0xd083a6c200000000, 0xd811e81300000000, - 0x3695d1b200000000, 0x451eea8a00000000, 0xab9ad32b00000000, - 0x7a5530e000000000, 0x94d1094100000000, 0xe75a327900000000, - 0x09de0bd800000000, 0x014c450900000000, 0xefc87ca800000000, - 0x9c43479000000000, 0x72c77e3100000000, 0xcd61abe900000000, - 0x23e5924800000000, 0x506ea97000000000, 0xbeea90d100000000, - 0xb678de0000000000, 0x58fce7a100000000, 0x2b77dc9900000000, - 0xc5f3e53800000000}, - {0x0000000000000000, 0xfbf6134700000000, 0xf6ed278e00000000, - 0x0d1b34c900000000, 0xaddd3ec700000000, 0x562b2d8000000000, - 0x5b30194900000000, 0xa0c60a0e00000000, 0x1bbd0c5500000000, - 0xe04b1f1200000000, 0xed502bdb00000000, 0x16a6389c00000000, - 0xb660329200000000, 0x4d9621d500000000, 0x408d151c00000000, - 0xbb7b065b00000000, 0x367a19aa00000000, 0xcd8c0aed00000000, - 0xc0973e2400000000, 0x3b612d6300000000, 0x9ba7276d00000000, - 0x6051342a00000000, 0x6d4a00e300000000, 0x96bc13a400000000, - 0x2dc715ff00000000, 0xd63106b800000000, 0xdb2a327100000000, - 0x20dc213600000000, 0x801a2b3800000000, 0x7bec387f00000000, - 0x76f70cb600000000, 0x8d011ff100000000, 0x2df2438f00000000, - 0xd60450c800000000, 0xdb1f640100000000, 0x20e9774600000000, - 0x802f7d4800000000, 0x7bd96e0f00000000, 0x76c25ac600000000, - 0x8d34498100000000, 0x364f4fda00000000, 0xcdb95c9d00000000, - 0xc0a2685400000000, 0x3b547b1300000000, 0x9b92711d00000000, - 0x6064625a00000000, 0x6d7f569300000000, 0x968945d400000000, - 0x1b885a2500000000, 0xe07e496200000000, 0xed657dab00000000, - 0x16936eec00000000, 0xb65564e200000000, 0x4da377a500000000, - 0x40b8436c00000000, 0xbb4e502b00000000, 0x0035567000000000, - 0xfbc3453700000000, 0xf6d871fe00000000, 0x0d2e62b900000000, - 0xade868b700000000, 0x561e7bf000000000, 0x5b054f3900000000, - 0xa0f35c7e00000000, 0x1be2f6c500000000, 0xe014e58200000000, - 0xed0fd14b00000000, 0x16f9c20c00000000, 0xb63fc80200000000, - 0x4dc9db4500000000, 0x40d2ef8c00000000, 0xbb24fccb00000000, - 0x005ffa9000000000, 0xfba9e9d700000000, 0xf6b2dd1e00000000, - 0x0d44ce5900000000, 0xad82c45700000000, 0x5674d71000000000, - 0x5b6fe3d900000000, 0xa099f09e00000000, 0x2d98ef6f00000000, - 0xd66efc2800000000, 0xdb75c8e100000000, 0x2083dba600000000, - 0x8045d1a800000000, 0x7bb3c2ef00000000, 0x76a8f62600000000, - 0x8d5ee56100000000, 0x3625e33a00000000, 0xcdd3f07d00000000, - 0xc0c8c4b400000000, 0x3b3ed7f300000000, 0x9bf8ddfd00000000, - 0x600eceba00000000, 0x6d15fa7300000000, 0x96e3e93400000000, - 0x3610b54a00000000, 0xcde6a60d00000000, 0xc0fd92c400000000, - 0x3b0b818300000000, 0x9bcd8b8d00000000, 0x603b98ca00000000, - 0x6d20ac0300000000, 0x96d6bf4400000000, 0x2dadb91f00000000, - 0xd65baa5800000000, 0xdb409e9100000000, 0x20b68dd600000000, - 0x807087d800000000, 0x7b86949f00000000, 0x769da05600000000, - 0x8d6bb31100000000, 0x006aace000000000, 0xfb9cbfa700000000, - 0xf6878b6e00000000, 0x0d71982900000000, 0xadb7922700000000, - 0x5641816000000000, 0x5b5ab5a900000000, 0xa0aca6ee00000000, - 0x1bd7a0b500000000, 0xe021b3f200000000, 0xed3a873b00000000, - 0x16cc947c00000000, 0xb60a9e7200000000, 0x4dfc8d3500000000, - 0x40e7b9fc00000000, 0xbb11aabb00000000, 0x77c29c5000000000, - 0x8c348f1700000000, 0x812fbbde00000000, 0x7ad9a89900000000, - 0xda1fa29700000000, 0x21e9b1d000000000, 0x2cf2851900000000, - 0xd704965e00000000, 0x6c7f900500000000, 0x9789834200000000, - 0x9a92b78b00000000, 0x6164a4cc00000000, 0xc1a2aec200000000, - 0x3a54bd8500000000, 0x374f894c00000000, 0xccb99a0b00000000, - 0x41b885fa00000000, 0xba4e96bd00000000, 0xb755a27400000000, - 0x4ca3b13300000000, 0xec65bb3d00000000, 0x1793a87a00000000, - 0x1a889cb300000000, 0xe17e8ff400000000, 0x5a0589af00000000, - 0xa1f39ae800000000, 0xace8ae2100000000, 0x571ebd6600000000, - 0xf7d8b76800000000, 0x0c2ea42f00000000, 0x013590e600000000, - 0xfac383a100000000, 0x5a30dfdf00000000, 0xa1c6cc9800000000, - 0xacddf85100000000, 0x572beb1600000000, 0xf7ede11800000000, - 0x0c1bf25f00000000, 0x0100c69600000000, 0xfaf6d5d100000000, - 0x418dd38a00000000, 0xba7bc0cd00000000, 0xb760f40400000000, - 0x4c96e74300000000, 0xec50ed4d00000000, 0x17a6fe0a00000000, - 0x1abdcac300000000, 0xe14bd98400000000, 0x6c4ac67500000000, - 0x97bcd53200000000, 0x9aa7e1fb00000000, 0x6151f2bc00000000, - 0xc197f8b200000000, 0x3a61ebf500000000, 0x377adf3c00000000, - 0xcc8ccc7b00000000, 0x77f7ca2000000000, 0x8c01d96700000000, - 0x811aedae00000000, 0x7aecfee900000000, 0xda2af4e700000000, - 0x21dce7a000000000, 0x2cc7d36900000000, 0xd731c02e00000000, - 0x6c206a9500000000, 0x97d679d200000000, 0x9acd4d1b00000000, - 0x613b5e5c00000000, 0xc1fd545200000000, 0x3a0b471500000000, - 0x371073dc00000000, 0xcce6609b00000000, 0x779d66c000000000, - 0x8c6b758700000000, 0x8170414e00000000, 0x7a86520900000000, - 0xda40580700000000, 0x21b64b4000000000, 0x2cad7f8900000000, - 0xd75b6cce00000000, 0x5a5a733f00000000, 0xa1ac607800000000, - 0xacb754b100000000, 0x574147f600000000, 0xf7874df800000000, - 0x0c715ebf00000000, 0x016a6a7600000000, 0xfa9c793100000000, - 0x41e77f6a00000000, 0xba116c2d00000000, 0xb70a58e400000000, - 0x4cfc4ba300000000, 0xec3a41ad00000000, 0x17cc52ea00000000, - 0x1ad7662300000000, 0xe121756400000000, 0x41d2291a00000000, - 0xba243a5d00000000, 0xb73f0e9400000000, 0x4cc91dd300000000, - 0xec0f17dd00000000, 0x17f9049a00000000, 0x1ae2305300000000, - 0xe114231400000000, 0x5a6f254f00000000, 0xa199360800000000, - 0xac8202c100000000, 0x5774118600000000, 0xf7b21b8800000000, - 0x0c4408cf00000000, 0x015f3c0600000000, 0xfaa92f4100000000, - 0x77a830b000000000, 0x8c5e23f700000000, 0x8145173e00000000, - 0x7ab3047900000000, 0xda750e7700000000, 0x21831d3000000000, - 0x2c9829f900000000, 0xd76e3abe00000000, 0x6c153ce500000000, - 0x97e32fa200000000, 0x9af81b6b00000000, 0x610e082c00000000, - 0xc1c8022200000000, 0x3a3e116500000000, 0x372525ac00000000, - 0xccd336eb00000000}, - {0x0000000000000000, 0x6238282a00000000, 0xc470505400000000, - 0xa648787e00000000, 0x88e1a0a800000000, 0xead9888200000000, - 0x4c91f0fc00000000, 0x2ea9d8d600000000, 0x51c5308a00000000, - 0x33fd18a000000000, 0x95b560de00000000, 0xf78d48f400000000, - 0xd924902200000000, 0xbb1cb80800000000, 0x1d54c07600000000, - 0x7f6ce85c00000000, 0xe38c10cf00000000, 0x81b438e500000000, - 0x27fc409b00000000, 0x45c468b100000000, 0x6b6db06700000000, - 0x0955984d00000000, 0xaf1de03300000000, 0xcd25c81900000000, - 0xb249204500000000, 0xd071086f00000000, 0x7639701100000000, - 0x1401583b00000000, 0x3aa880ed00000000, 0x5890a8c700000000, - 0xfed8d0b900000000, 0x9ce0f89300000000, 0x871f504500000000, - 0xe527786f00000000, 0x436f001100000000, 0x2157283b00000000, - 0x0ffef0ed00000000, 0x6dc6d8c700000000, 0xcb8ea0b900000000, - 0xa9b6889300000000, 0xd6da60cf00000000, 0xb4e248e500000000, - 0x12aa309b00000000, 0x709218b100000000, 0x5e3bc06700000000, - 0x3c03e84d00000000, 0x9a4b903300000000, 0xf873b81900000000, - 0x6493408a00000000, 0x06ab68a000000000, 0xa0e310de00000000, - 0xc2db38f400000000, 0xec72e02200000000, 0x8e4ac80800000000, - 0x2802b07600000000, 0x4a3a985c00000000, 0x3556700000000000, - 0x576e582a00000000, 0xf126205400000000, 0x931e087e00000000, - 0xbdb7d0a800000000, 0xdf8ff88200000000, 0x79c780fc00000000, - 0x1bffa8d600000000, 0x0e3fa08a00000000, 0x6c0788a000000000, - 0xca4ff0de00000000, 0xa877d8f400000000, 0x86de002200000000, - 0xe4e6280800000000, 0x42ae507600000000, 0x2096785c00000000, - 0x5ffa900000000000, 0x3dc2b82a00000000, 0x9b8ac05400000000, - 0xf9b2e87e00000000, 0xd71b30a800000000, 0xb523188200000000, - 0x136b60fc00000000, 0x715348d600000000, 0xedb3b04500000000, - 0x8f8b986f00000000, 0x29c3e01100000000, 0x4bfbc83b00000000, - 0x655210ed00000000, 0x076a38c700000000, 0xa12240b900000000, - 0xc31a689300000000, 0xbc7680cf00000000, 0xde4ea8e500000000, - 0x7806d09b00000000, 0x1a3ef8b100000000, 0x3497206700000000, - 0x56af084d00000000, 0xf0e7703300000000, 0x92df581900000000, - 0x8920f0cf00000000, 0xeb18d8e500000000, 0x4d50a09b00000000, - 0x2f6888b100000000, 0x01c1506700000000, 0x63f9784d00000000, - 0xc5b1003300000000, 0xa789281900000000, 0xd8e5c04500000000, - 0xbadde86f00000000, 0x1c95901100000000, 0x7eadb83b00000000, - 0x500460ed00000000, 0x323c48c700000000, 0x947430b900000000, - 0xf64c189300000000, 0x6aace00000000000, 0x0894c82a00000000, - 0xaedcb05400000000, 0xcce4987e00000000, 0xe24d40a800000000, - 0x8075688200000000, 0x263d10fc00000000, 0x440538d600000000, - 0x3b69d08a00000000, 0x5951f8a000000000, 0xff1980de00000000, - 0x9d21a8f400000000, 0xb388702200000000, 0xd1b0580800000000, - 0x77f8207600000000, 0x15c0085c00000000, 0x5d7831ce00000000, - 0x3f4019e400000000, 0x9908619a00000000, 0xfb3049b000000000, - 0xd599916600000000, 0xb7a1b94c00000000, 0x11e9c13200000000, - 0x73d1e91800000000, 0x0cbd014400000000, 0x6e85296e00000000, - 0xc8cd511000000000, 0xaaf5793a00000000, 0x845ca1ec00000000, - 0xe66489c600000000, 0x402cf1b800000000, 0x2214d99200000000, - 0xbef4210100000000, 0xdccc092b00000000, 0x7a84715500000000, - 0x18bc597f00000000, 0x361581a900000000, 0x542da98300000000, - 0xf265d1fd00000000, 0x905df9d700000000, 0xef31118b00000000, - 0x8d0939a100000000, 0x2b4141df00000000, 0x497969f500000000, - 0x67d0b12300000000, 0x05e8990900000000, 0xa3a0e17700000000, - 0xc198c95d00000000, 0xda67618b00000000, 0xb85f49a100000000, - 0x1e1731df00000000, 0x7c2f19f500000000, 0x5286c12300000000, - 0x30bee90900000000, 0x96f6917700000000, 0xf4ceb95d00000000, - 0x8ba2510100000000, 0xe99a792b00000000, 0x4fd2015500000000, - 0x2dea297f00000000, 0x0343f1a900000000, 0x617bd98300000000, - 0xc733a1fd00000000, 0xa50b89d700000000, 0x39eb714400000000, - 0x5bd3596e00000000, 0xfd9b211000000000, 0x9fa3093a00000000, - 0xb10ad1ec00000000, 0xd332f9c600000000, 0x757a81b800000000, - 0x1742a99200000000, 0x682e41ce00000000, 0x0a1669e400000000, - 0xac5e119a00000000, 0xce6639b000000000, 0xe0cfe16600000000, - 0x82f7c94c00000000, 0x24bfb13200000000, 0x4687991800000000, - 0x5347914400000000, 0x317fb96e00000000, 0x9737c11000000000, - 0xf50fe93a00000000, 0xdba631ec00000000, 0xb99e19c600000000, - 0x1fd661b800000000, 0x7dee499200000000, 0x0282a1ce00000000, - 0x60ba89e400000000, 0xc6f2f19a00000000, 0xa4cad9b000000000, - 0x8a63016600000000, 0xe85b294c00000000, 0x4e13513200000000, - 0x2c2b791800000000, 0xb0cb818b00000000, 0xd2f3a9a100000000, - 0x74bbd1df00000000, 0x1683f9f500000000, 0x382a212300000000, - 0x5a12090900000000, 0xfc5a717700000000, 0x9e62595d00000000, - 0xe10eb10100000000, 0x8336992b00000000, 0x257ee15500000000, - 0x4746c97f00000000, 0x69ef11a900000000, 0x0bd7398300000000, - 0xad9f41fd00000000, 0xcfa769d700000000, 0xd458c10100000000, - 0xb660e92b00000000, 0x1028915500000000, 0x7210b97f00000000, - 0x5cb961a900000000, 0x3e81498300000000, 0x98c931fd00000000, - 0xfaf119d700000000, 0x859df18b00000000, 0xe7a5d9a100000000, - 0x41eda1df00000000, 0x23d589f500000000, 0x0d7c512300000000, - 0x6f44790900000000, 0xc90c017700000000, 0xab34295d00000000, - 0x37d4d1ce00000000, 0x55ecf9e400000000, 0xf3a4819a00000000, - 0x919ca9b000000000, 0xbf35716600000000, 0xdd0d594c00000000, - 0x7b45213200000000, 0x197d091800000000, 0x6611e14400000000, - 0x0429c96e00000000, 0xa261b11000000000, 0xc059993a00000000, - 0xeef041ec00000000, 0x8cc869c600000000, 0x2a8011b800000000, - 0x48b8399200000000}, - {0x0000000000000000, 0x4c2896a300000000, 0xd9565d9c00000000, - 0x957ecb3f00000000, 0xf3abcbe300000000, 0xbf835d4000000000, - 0x2afd967f00000000, 0x66d500dc00000000, 0xa751e61c00000000, - 0xeb7970bf00000000, 0x7e07bb8000000000, 0x322f2d2300000000, - 0x54fa2dff00000000, 0x18d2bb5c00000000, 0x8dac706300000000, - 0xc184e6c000000000, 0x4ea3cc3900000000, 0x028b5a9a00000000, - 0x97f591a500000000, 0xdbdd070600000000, 0xbd0807da00000000, - 0xf120917900000000, 0x645e5a4600000000, 0x2876cce500000000, - 0xe9f22a2500000000, 0xa5dabc8600000000, 0x30a477b900000000, - 0x7c8ce11a00000000, 0x1a59e1c600000000, 0x5671776500000000, - 0xc30fbc5a00000000, 0x8f272af900000000, 0x9c46997300000000, - 0xd06e0fd000000000, 0x4510c4ef00000000, 0x0938524c00000000, - 0x6fed529000000000, 0x23c5c43300000000, 0xb6bb0f0c00000000, - 0xfa9399af00000000, 0x3b177f6f00000000, 0x773fe9cc00000000, - 0xe24122f300000000, 0xae69b45000000000, 0xc8bcb48c00000000, - 0x8494222f00000000, 0x11eae91000000000, 0x5dc27fb300000000, - 0xd2e5554a00000000, 0x9ecdc3e900000000, 0x0bb308d600000000, - 0x479b9e7500000000, 0x214e9ea900000000, 0x6d66080a00000000, - 0xf818c33500000000, 0xb430559600000000, 0x75b4b35600000000, - 0x399c25f500000000, 0xace2eeca00000000, 0xe0ca786900000000, - 0x861f78b500000000, 0xca37ee1600000000, 0x5f49252900000000, - 0x1361b38a00000000, 0x388d32e700000000, 0x74a5a44400000000, - 0xe1db6f7b00000000, 0xadf3f9d800000000, 0xcb26f90400000000, - 0x870e6fa700000000, 0x1270a49800000000, 0x5e58323b00000000, - 0x9fdcd4fb00000000, 0xd3f4425800000000, 0x468a896700000000, - 0x0aa21fc400000000, 0x6c771f1800000000, 0x205f89bb00000000, - 0xb521428400000000, 0xf909d42700000000, 0x762efede00000000, - 0x3a06687d00000000, 0xaf78a34200000000, 0xe35035e100000000, - 0x8585353d00000000, 0xc9ada39e00000000, 0x5cd368a100000000, - 0x10fbfe0200000000, 0xd17f18c200000000, 0x9d578e6100000000, - 0x0829455e00000000, 0x4401d3fd00000000, 0x22d4d32100000000, - 0x6efc458200000000, 0xfb828ebd00000000, 0xb7aa181e00000000, - 0xa4cbab9400000000, 0xe8e33d3700000000, 0x7d9df60800000000, - 0x31b560ab00000000, 0x5760607700000000, 0x1b48f6d400000000, - 0x8e363deb00000000, 0xc21eab4800000000, 0x039a4d8800000000, - 0x4fb2db2b00000000, 0xdacc101400000000, 0x96e486b700000000, - 0xf031866b00000000, 0xbc1910c800000000, 0x2967dbf700000000, - 0x654f4d5400000000, 0xea6867ad00000000, 0xa640f10e00000000, - 0x333e3a3100000000, 0x7f16ac9200000000, 0x19c3ac4e00000000, - 0x55eb3aed00000000, 0xc095f1d200000000, 0x8cbd677100000000, - 0x4d3981b100000000, 0x0111171200000000, 0x946fdc2d00000000, - 0xd8474a8e00000000, 0xbe924a5200000000, 0xf2badcf100000000, - 0x67c417ce00000000, 0x2bec816d00000000, 0x311c141500000000, - 0x7d3482b600000000, 0xe84a498900000000, 0xa462df2a00000000, - 0xc2b7dff600000000, 0x8e9f495500000000, 0x1be1826a00000000, - 0x57c914c900000000, 0x964df20900000000, 0xda6564aa00000000, - 0x4f1baf9500000000, 0x0333393600000000, 0x65e639ea00000000, - 0x29ceaf4900000000, 0xbcb0647600000000, 0xf098f2d500000000, - 0x7fbfd82c00000000, 0x33974e8f00000000, 0xa6e985b000000000, - 0xeac1131300000000, 0x8c1413cf00000000, 0xc03c856c00000000, - 0x55424e5300000000, 0x196ad8f000000000, 0xd8ee3e3000000000, - 0x94c6a89300000000, 0x01b863ac00000000, 0x4d90f50f00000000, - 0x2b45f5d300000000, 0x676d637000000000, 0xf213a84f00000000, - 0xbe3b3eec00000000, 0xad5a8d6600000000, 0xe1721bc500000000, - 0x740cd0fa00000000, 0x3824465900000000, 0x5ef1468500000000, - 0x12d9d02600000000, 0x87a71b1900000000, 0xcb8f8dba00000000, - 0x0a0b6b7a00000000, 0x4623fdd900000000, 0xd35d36e600000000, - 0x9f75a04500000000, 0xf9a0a09900000000, 0xb588363a00000000, - 0x20f6fd0500000000, 0x6cde6ba600000000, 0xe3f9415f00000000, - 0xafd1d7fc00000000, 0x3aaf1cc300000000, 0x76878a6000000000, - 0x10528abc00000000, 0x5c7a1c1f00000000, 0xc904d72000000000, - 0x852c418300000000, 0x44a8a74300000000, 0x088031e000000000, - 0x9dfefadf00000000, 0xd1d66c7c00000000, 0xb7036ca000000000, - 0xfb2bfa0300000000, 0x6e55313c00000000, 0x227da79f00000000, - 0x099126f200000000, 0x45b9b05100000000, 0xd0c77b6e00000000, - 0x9cefedcd00000000, 0xfa3aed1100000000, 0xb6127bb200000000, - 0x236cb08d00000000, 0x6f44262e00000000, 0xaec0c0ee00000000, - 0xe2e8564d00000000, 0x77969d7200000000, 0x3bbe0bd100000000, - 0x5d6b0b0d00000000, 0x11439dae00000000, 0x843d569100000000, - 0xc815c03200000000, 0x4732eacb00000000, 0x0b1a7c6800000000, - 0x9e64b75700000000, 0xd24c21f400000000, 0xb499212800000000, - 0xf8b1b78b00000000, 0x6dcf7cb400000000, 0x21e7ea1700000000, - 0xe0630cd700000000, 0xac4b9a7400000000, 0x3935514b00000000, - 0x751dc7e800000000, 0x13c8c73400000000, 0x5fe0519700000000, - 0xca9e9aa800000000, 0x86b60c0b00000000, 0x95d7bf8100000000, - 0xd9ff292200000000, 0x4c81e21d00000000, 0x00a974be00000000, - 0x667c746200000000, 0x2a54e2c100000000, 0xbf2a29fe00000000, - 0xf302bf5d00000000, 0x3286599d00000000, 0x7eaecf3e00000000, - 0xebd0040100000000, 0xa7f892a200000000, 0xc12d927e00000000, - 0x8d0504dd00000000, 0x187bcfe200000000, 0x5453594100000000, - 0xdb7473b800000000, 0x975ce51b00000000, 0x02222e2400000000, - 0x4e0ab88700000000, 0x28dfb85b00000000, 0x64f72ef800000000, - 0xf189e5c700000000, 0xbda1736400000000, 0x7c2595a400000000, - 0x300d030700000000, 0xa573c83800000000, 0xe95b5e9b00000000, - 0x8f8e5e4700000000, 0xc3a6c8e400000000, 0x56d803db00000000, - 0x1af0957800000000}, - {0x0000000000000000, 0x939bc97f00000000, 0x263793ff00000000, - 0xb5ac5a8000000000, 0x0d68572400000000, 0x9ef39e5b00000000, - 0x2b5fc4db00000000, 0xb8c40da400000000, 0x1ad0ae4800000000, - 0x894b673700000000, 0x3ce73db700000000, 0xaf7cf4c800000000, - 0x17b8f96c00000000, 0x8423301300000000, 0x318f6a9300000000, - 0xa214a3ec00000000, 0x34a05d9100000000, 0xa73b94ee00000000, - 0x1297ce6e00000000, 0x810c071100000000, 0x39c80ab500000000, - 0xaa53c3ca00000000, 0x1fff994a00000000, 0x8c64503500000000, - 0x2e70f3d900000000, 0xbdeb3aa600000000, 0x0847602600000000, - 0x9bdca95900000000, 0x2318a4fd00000000, 0xb0836d8200000000, - 0x052f370200000000, 0x96b4fe7d00000000, 0x2946caf900000000, - 0xbadd038600000000, 0x0f71590600000000, 0x9cea907900000000, - 0x242e9ddd00000000, 0xb7b554a200000000, 0x02190e2200000000, - 0x9182c75d00000000, 0x339664b100000000, 0xa00dadce00000000, - 0x15a1f74e00000000, 0x863a3e3100000000, 0x3efe339500000000, - 0xad65faea00000000, 0x18c9a06a00000000, 0x8b52691500000000, - 0x1de6976800000000, 0x8e7d5e1700000000, 0x3bd1049700000000, - 0xa84acde800000000, 0x108ec04c00000000, 0x8315093300000000, - 0x36b953b300000000, 0xa5229acc00000000, 0x0736392000000000, - 0x94adf05f00000000, 0x2101aadf00000000, 0xb29a63a000000000, - 0x0a5e6e0400000000, 0x99c5a77b00000000, 0x2c69fdfb00000000, - 0xbff2348400000000, 0x138ae52800000000, 0x80112c5700000000, - 0x35bd76d700000000, 0xa626bfa800000000, 0x1ee2b20c00000000, - 0x8d797b7300000000, 0x38d521f300000000, 0xab4ee88c00000000, - 0x095a4b6000000000, 0x9ac1821f00000000, 0x2f6dd89f00000000, - 0xbcf611e000000000, 0x04321c4400000000, 0x97a9d53b00000000, - 0x22058fbb00000000, 0xb19e46c400000000, 0x272ab8b900000000, - 0xb4b171c600000000, 0x011d2b4600000000, 0x9286e23900000000, - 0x2a42ef9d00000000, 0xb9d926e200000000, 0x0c757c6200000000, - 0x9feeb51d00000000, 0x3dfa16f100000000, 0xae61df8e00000000, - 0x1bcd850e00000000, 0x88564c7100000000, 0x309241d500000000, - 0xa30988aa00000000, 0x16a5d22a00000000, 0x853e1b5500000000, - 0x3acc2fd100000000, 0xa957e6ae00000000, 0x1cfbbc2e00000000, - 0x8f60755100000000, 0x37a478f500000000, 0xa43fb18a00000000, - 0x1193eb0a00000000, 0x8208227500000000, 0x201c819900000000, - 0xb38748e600000000, 0x062b126600000000, 0x95b0db1900000000, - 0x2d74d6bd00000000, 0xbeef1fc200000000, 0x0b43454200000000, - 0x98d88c3d00000000, 0x0e6c724000000000, 0x9df7bb3f00000000, - 0x285be1bf00000000, 0xbbc028c000000000, 0x0304256400000000, - 0x909fec1b00000000, 0x2533b69b00000000, 0xb6a87fe400000000, - 0x14bcdc0800000000, 0x8727157700000000, 0x328b4ff700000000, - 0xa110868800000000, 0x19d48b2c00000000, 0x8a4f425300000000, - 0x3fe318d300000000, 0xac78d1ac00000000, 0x2614cb5100000000, - 0xb58f022e00000000, 0x002358ae00000000, 0x93b891d100000000, - 0x2b7c9c7500000000, 0xb8e7550a00000000, 0x0d4b0f8a00000000, - 0x9ed0c6f500000000, 0x3cc4651900000000, 0xaf5fac6600000000, - 0x1af3f6e600000000, 0x89683f9900000000, 0x31ac323d00000000, - 0xa237fb4200000000, 0x179ba1c200000000, 0x840068bd00000000, - 0x12b496c000000000, 0x812f5fbf00000000, 0x3483053f00000000, - 0xa718cc4000000000, 0x1fdcc1e400000000, 0x8c47089b00000000, - 0x39eb521b00000000, 0xaa709b6400000000, 0x0864388800000000, - 0x9bfff1f700000000, 0x2e53ab7700000000, 0xbdc8620800000000, - 0x050c6fac00000000, 0x9697a6d300000000, 0x233bfc5300000000, - 0xb0a0352c00000000, 0x0f5201a800000000, 0x9cc9c8d700000000, - 0x2965925700000000, 0xbafe5b2800000000, 0x023a568c00000000, - 0x91a19ff300000000, 0x240dc57300000000, 0xb7960c0c00000000, - 0x1582afe000000000, 0x8619669f00000000, 0x33b53c1f00000000, - 0xa02ef56000000000, 0x18eaf8c400000000, 0x8b7131bb00000000, - 0x3edd6b3b00000000, 0xad46a24400000000, 0x3bf25c3900000000, - 0xa869954600000000, 0x1dc5cfc600000000, 0x8e5e06b900000000, - 0x369a0b1d00000000, 0xa501c26200000000, 0x10ad98e200000000, - 0x8336519d00000000, 0x2122f27100000000, 0xb2b93b0e00000000, - 0x0715618e00000000, 0x948ea8f100000000, 0x2c4aa55500000000, - 0xbfd16c2a00000000, 0x0a7d36aa00000000, 0x99e6ffd500000000, - 0x359e2e7900000000, 0xa605e70600000000, 0x13a9bd8600000000, - 0x803274f900000000, 0x38f6795d00000000, 0xab6db02200000000, - 0x1ec1eaa200000000, 0x8d5a23dd00000000, 0x2f4e803100000000, - 0xbcd5494e00000000, 0x097913ce00000000, 0x9ae2dab100000000, - 0x2226d71500000000, 0xb1bd1e6a00000000, 0x041144ea00000000, - 0x978a8d9500000000, 0x013e73e800000000, 0x92a5ba9700000000, - 0x2709e01700000000, 0xb492296800000000, 0x0c5624cc00000000, - 0x9fcdedb300000000, 0x2a61b73300000000, 0xb9fa7e4c00000000, - 0x1beedda000000000, 0x887514df00000000, 0x3dd94e5f00000000, - 0xae42872000000000, 0x16868a8400000000, 0x851d43fb00000000, - 0x30b1197b00000000, 0xa32ad00400000000, 0x1cd8e48000000000, - 0x8f432dff00000000, 0x3aef777f00000000, 0xa974be0000000000, - 0x11b0b3a400000000, 0x822b7adb00000000, 0x3787205b00000000, - 0xa41ce92400000000, 0x06084ac800000000, 0x959383b700000000, - 0x203fd93700000000, 0xb3a4104800000000, 0x0b601dec00000000, - 0x98fbd49300000000, 0x2d578e1300000000, 0xbecc476c00000000, - 0x2878b91100000000, 0xbbe3706e00000000, 0x0e4f2aee00000000, - 0x9dd4e39100000000, 0x2510ee3500000000, 0xb68b274a00000000, - 0x03277dca00000000, 0x90bcb4b500000000, 0x32a8175900000000, - 0xa133de2600000000, 0x149f84a600000000, 0x87044dd900000000, - 0x3fc0407d00000000, 0xac5b890200000000, 0x19f7d38200000000, - 0x8a6c1afd00000000}, - {0x0000000000000000, 0x650b796900000000, 0xca16f2d200000000, - 0xaf1d8bbb00000000, 0xd52b957e00000000, 0xb020ec1700000000, - 0x1f3d67ac00000000, 0x7a361ec500000000, 0xaa572afd00000000, - 0xcf5c539400000000, 0x6041d82f00000000, 0x054aa14600000000, - 0x7f7cbf8300000000, 0x1a77c6ea00000000, 0xb56a4d5100000000, - 0xd061343800000000, 0x15a9252100000000, 0x70a25c4800000000, - 0xdfbfd7f300000000, 0xbab4ae9a00000000, 0xc082b05f00000000, - 0xa589c93600000000, 0x0a94428d00000000, 0x6f9f3be400000000, - 0xbffe0fdc00000000, 0xdaf576b500000000, 0x75e8fd0e00000000, - 0x10e3846700000000, 0x6ad59aa200000000, 0x0fdee3cb00000000, - 0xa0c3687000000000, 0xc5c8111900000000, 0x2a524b4200000000, - 0x4f59322b00000000, 0xe044b99000000000, 0x854fc0f900000000, - 0xff79de3c00000000, 0x9a72a75500000000, 0x356f2cee00000000, - 0x5064558700000000, 0x800561bf00000000, 0xe50e18d600000000, - 0x4a13936d00000000, 0x2f18ea0400000000, 0x552ef4c100000000, - 0x30258da800000000, 0x9f38061300000000, 0xfa337f7a00000000, - 0x3ffb6e6300000000, 0x5af0170a00000000, 0xf5ed9cb100000000, - 0x90e6e5d800000000, 0xead0fb1d00000000, 0x8fdb827400000000, - 0x20c609cf00000000, 0x45cd70a600000000, 0x95ac449e00000000, - 0xf0a73df700000000, 0x5fbab64c00000000, 0x3ab1cf2500000000, - 0x4087d1e000000000, 0x258ca88900000000, 0x8a91233200000000, - 0xef9a5a5b00000000, 0x54a4968400000000, 0x31afefed00000000, - 0x9eb2645600000000, 0xfbb91d3f00000000, 0x818f03fa00000000, - 0xe4847a9300000000, 0x4b99f12800000000, 0x2e92884100000000, - 0xfef3bc7900000000, 0x9bf8c51000000000, 0x34e54eab00000000, - 0x51ee37c200000000, 0x2bd8290700000000, 0x4ed3506e00000000, - 0xe1cedbd500000000, 0x84c5a2bc00000000, 0x410db3a500000000, - 0x2406cacc00000000, 0x8b1b417700000000, 0xee10381e00000000, - 0x942626db00000000, 0xf12d5fb200000000, 0x5e30d40900000000, - 0x3b3bad6000000000, 0xeb5a995800000000, 0x8e51e03100000000, - 0x214c6b8a00000000, 0x444712e300000000, 0x3e710c2600000000, - 0x5b7a754f00000000, 0xf467fef400000000, 0x916c879d00000000, - 0x7ef6ddc600000000, 0x1bfda4af00000000, 0xb4e02f1400000000, - 0xd1eb567d00000000, 0xabdd48b800000000, 0xced631d100000000, - 0x61cbba6a00000000, 0x04c0c30300000000, 0xd4a1f73b00000000, - 0xb1aa8e5200000000, 0x1eb705e900000000, 0x7bbc7c8000000000, - 0x018a624500000000, 0x64811b2c00000000, 0xcb9c909700000000, - 0xae97e9fe00000000, 0x6b5ff8e700000000, 0x0e54818e00000000, - 0xa1490a3500000000, 0xc442735c00000000, 0xbe746d9900000000, - 0xdb7f14f000000000, 0x74629f4b00000000, 0x1169e62200000000, - 0xc108d21a00000000, 0xa403ab7300000000, 0x0b1e20c800000000, - 0x6e1559a100000000, 0x1423476400000000, 0x71283e0d00000000, - 0xde35b5b600000000, 0xbb3eccdf00000000, 0xe94e5cd200000000, - 0x8c4525bb00000000, 0x2358ae0000000000, 0x4653d76900000000, - 0x3c65c9ac00000000, 0x596eb0c500000000, 0xf6733b7e00000000, - 0x9378421700000000, 0x4319762f00000000, 0x26120f4600000000, - 0x890f84fd00000000, 0xec04fd9400000000, 0x9632e35100000000, - 0xf3399a3800000000, 0x5c24118300000000, 0x392f68ea00000000, - 0xfce779f300000000, 0x99ec009a00000000, 0x36f18b2100000000, - 0x53faf24800000000, 0x29ccec8d00000000, 0x4cc795e400000000, - 0xe3da1e5f00000000, 0x86d1673600000000, 0x56b0530e00000000, - 0x33bb2a6700000000, 0x9ca6a1dc00000000, 0xf9add8b500000000, - 0x839bc67000000000, 0xe690bf1900000000, 0x498d34a200000000, - 0x2c864dcb00000000, 0xc31c179000000000, 0xa6176ef900000000, - 0x090ae54200000000, 0x6c019c2b00000000, 0x163782ee00000000, - 0x733cfb8700000000, 0xdc21703c00000000, 0xb92a095500000000, - 0x694b3d6d00000000, 0x0c40440400000000, 0xa35dcfbf00000000, - 0xc656b6d600000000, 0xbc60a81300000000, 0xd96bd17a00000000, - 0x76765ac100000000, 0x137d23a800000000, 0xd6b532b100000000, - 0xb3be4bd800000000, 0x1ca3c06300000000, 0x79a8b90a00000000, - 0x039ea7cf00000000, 0x6695dea600000000, 0xc988551d00000000, - 0xac832c7400000000, 0x7ce2184c00000000, 0x19e9612500000000, - 0xb6f4ea9e00000000, 0xd3ff93f700000000, 0xa9c98d3200000000, - 0xccc2f45b00000000, 0x63df7fe000000000, 0x06d4068900000000, - 0xbdeaca5600000000, 0xd8e1b33f00000000, 0x77fc388400000000, - 0x12f741ed00000000, 0x68c15f2800000000, 0x0dca264100000000, - 0xa2d7adfa00000000, 0xc7dcd49300000000, 0x17bde0ab00000000, - 0x72b699c200000000, 0xddab127900000000, 0xb8a06b1000000000, - 0xc29675d500000000, 0xa79d0cbc00000000, 0x0880870700000000, - 0x6d8bfe6e00000000, 0xa843ef7700000000, 0xcd48961e00000000, - 0x62551da500000000, 0x075e64cc00000000, 0x7d687a0900000000, - 0x1863036000000000, 0xb77e88db00000000, 0xd275f1b200000000, - 0x0214c58a00000000, 0x671fbce300000000, 0xc802375800000000, - 0xad094e3100000000, 0xd73f50f400000000, 0xb234299d00000000, - 0x1d29a22600000000, 0x7822db4f00000000, 0x97b8811400000000, - 0xf2b3f87d00000000, 0x5dae73c600000000, 0x38a50aaf00000000, - 0x4293146a00000000, 0x27986d0300000000, 0x8885e6b800000000, - 0xed8e9fd100000000, 0x3defabe900000000, 0x58e4d28000000000, - 0xf7f9593b00000000, 0x92f2205200000000, 0xe8c43e9700000000, - 0x8dcf47fe00000000, 0x22d2cc4500000000, 0x47d9b52c00000000, - 0x8211a43500000000, 0xe71add5c00000000, 0x480756e700000000, - 0x2d0c2f8e00000000, 0x573a314b00000000, 0x3231482200000000, - 0x9d2cc39900000000, 0xf827baf000000000, 0x28468ec800000000, - 0x4d4df7a100000000, 0xe2507c1a00000000, 0x875b057300000000, - 0xfd6d1bb600000000, 0x986662df00000000, 0x377be96400000000, - 0x5270900d00000000}, - {0x0000000000000000, 0xdcecb13d00000000, 0xb8d9637b00000000, - 0x6435d24600000000, 0x70b3c7f600000000, 0xac5f76cb00000000, - 0xc86aa48d00000000, 0x148615b000000000, 0xa160fe3600000000, - 0x7d8c4f0b00000000, 0x19b99d4d00000000, 0xc5552c7000000000, - 0xd1d339c000000000, 0x0d3f88fd00000000, 0x690a5abb00000000, - 0xb5e6eb8600000000, 0x42c1fc6d00000000, 0x9e2d4d5000000000, - 0xfa189f1600000000, 0x26f42e2b00000000, 0x32723b9b00000000, - 0xee9e8aa600000000, 0x8aab58e000000000, 0x5647e9dd00000000, - 0xe3a1025b00000000, 0x3f4db36600000000, 0x5b78612000000000, - 0x8794d01d00000000, 0x9312c5ad00000000, 0x4ffe749000000000, - 0x2bcba6d600000000, 0xf72717eb00000000, 0x8482f9db00000000, - 0x586e48e600000000, 0x3c5b9aa000000000, 0xe0b72b9d00000000, - 0xf4313e2d00000000, 0x28dd8f1000000000, 0x4ce85d5600000000, - 0x9004ec6b00000000, 0x25e207ed00000000, 0xf90eb6d000000000, - 0x9d3b649600000000, 0x41d7d5ab00000000, 0x5551c01b00000000, - 0x89bd712600000000, 0xed88a36000000000, 0x3164125d00000000, - 0xc64305b600000000, 0x1aafb48b00000000, 0x7e9a66cd00000000, - 0xa276d7f000000000, 0xb6f0c24000000000, 0x6a1c737d00000000, - 0x0e29a13b00000000, 0xd2c5100600000000, 0x6723fb8000000000, - 0xbbcf4abd00000000, 0xdffa98fb00000000, 0x031629c600000000, - 0x17903c7600000000, 0xcb7c8d4b00000000, 0xaf495f0d00000000, - 0x73a5ee3000000000, 0x4903826c00000000, 0x95ef335100000000, - 0xf1dae11700000000, 0x2d36502a00000000, 0x39b0459a00000000, - 0xe55cf4a700000000, 0x816926e100000000, 0x5d8597dc00000000, - 0xe8637c5a00000000, 0x348fcd6700000000, 0x50ba1f2100000000, - 0x8c56ae1c00000000, 0x98d0bbac00000000, 0x443c0a9100000000, - 0x2009d8d700000000, 0xfce569ea00000000, 0x0bc27e0100000000, - 0xd72ecf3c00000000, 0xb31b1d7a00000000, 0x6ff7ac4700000000, - 0x7b71b9f700000000, 0xa79d08ca00000000, 0xc3a8da8c00000000, - 0x1f446bb100000000, 0xaaa2803700000000, 0x764e310a00000000, - 0x127be34c00000000, 0xce97527100000000, 0xda1147c100000000, - 0x06fdf6fc00000000, 0x62c824ba00000000, 0xbe24958700000000, - 0xcd817bb700000000, 0x116dca8a00000000, 0x755818cc00000000, - 0xa9b4a9f100000000, 0xbd32bc4100000000, 0x61de0d7c00000000, - 0x05ebdf3a00000000, 0xd9076e0700000000, 0x6ce1858100000000, - 0xb00d34bc00000000, 0xd438e6fa00000000, 0x08d457c700000000, - 0x1c52427700000000, 0xc0bef34a00000000, 0xa48b210c00000000, - 0x7867903100000000, 0x8f4087da00000000, 0x53ac36e700000000, - 0x3799e4a100000000, 0xeb75559c00000000, 0xfff3402c00000000, - 0x231ff11100000000, 0x472a235700000000, 0x9bc6926a00000000, - 0x2e2079ec00000000, 0xf2ccc8d100000000, 0x96f91a9700000000, - 0x4a15abaa00000000, 0x5e93be1a00000000, 0x827f0f2700000000, - 0xe64add6100000000, 0x3aa66c5c00000000, 0x920604d900000000, - 0x4eeab5e400000000, 0x2adf67a200000000, 0xf633d69f00000000, - 0xe2b5c32f00000000, 0x3e59721200000000, 0x5a6ca05400000000, - 0x8680116900000000, 0x3366faef00000000, 0xef8a4bd200000000, - 0x8bbf999400000000, 0x575328a900000000, 0x43d53d1900000000, - 0x9f398c2400000000, 0xfb0c5e6200000000, 0x27e0ef5f00000000, - 0xd0c7f8b400000000, 0x0c2b498900000000, 0x681e9bcf00000000, - 0xb4f22af200000000, 0xa0743f4200000000, 0x7c988e7f00000000, - 0x18ad5c3900000000, 0xc441ed0400000000, 0x71a7068200000000, - 0xad4bb7bf00000000, 0xc97e65f900000000, 0x1592d4c400000000, - 0x0114c17400000000, 0xddf8704900000000, 0xb9cda20f00000000, - 0x6521133200000000, 0x1684fd0200000000, 0xca684c3f00000000, - 0xae5d9e7900000000, 0x72b12f4400000000, 0x66373af400000000, - 0xbadb8bc900000000, 0xdeee598f00000000, 0x0202e8b200000000, - 0xb7e4033400000000, 0x6b08b20900000000, 0x0f3d604f00000000, - 0xd3d1d17200000000, 0xc757c4c200000000, 0x1bbb75ff00000000, - 0x7f8ea7b900000000, 0xa362168400000000, 0x5445016f00000000, - 0x88a9b05200000000, 0xec9c621400000000, 0x3070d32900000000, - 0x24f6c69900000000, 0xf81a77a400000000, 0x9c2fa5e200000000, - 0x40c314df00000000, 0xf525ff5900000000, 0x29c94e6400000000, - 0x4dfc9c2200000000, 0x91102d1f00000000, 0x859638af00000000, - 0x597a899200000000, 0x3d4f5bd400000000, 0xe1a3eae900000000, - 0xdb0586b500000000, 0x07e9378800000000, 0x63dce5ce00000000, - 0xbf3054f300000000, 0xabb6414300000000, 0x775af07e00000000, - 0x136f223800000000, 0xcf83930500000000, 0x7a65788300000000, - 0xa689c9be00000000, 0xc2bc1bf800000000, 0x1e50aac500000000, - 0x0ad6bf7500000000, 0xd63a0e4800000000, 0xb20fdc0e00000000, - 0x6ee36d3300000000, 0x99c47ad800000000, 0x4528cbe500000000, - 0x211d19a300000000, 0xfdf1a89e00000000, 0xe977bd2e00000000, - 0x359b0c1300000000, 0x51aede5500000000, 0x8d426f6800000000, - 0x38a484ee00000000, 0xe44835d300000000, 0x807de79500000000, - 0x5c9156a800000000, 0x4817431800000000, 0x94fbf22500000000, - 0xf0ce206300000000, 0x2c22915e00000000, 0x5f877f6e00000000, - 0x836bce5300000000, 0xe75e1c1500000000, 0x3bb2ad2800000000, - 0x2f34b89800000000, 0xf3d809a500000000, 0x97eddbe300000000, - 0x4b016ade00000000, 0xfee7815800000000, 0x220b306500000000, - 0x463ee22300000000, 0x9ad2531e00000000, 0x8e5446ae00000000, - 0x52b8f79300000000, 0x368d25d500000000, 0xea6194e800000000, - 0x1d46830300000000, 0xc1aa323e00000000, 0xa59fe07800000000, - 0x7973514500000000, 0x6df544f500000000, 0xb119f5c800000000, - 0xd52c278e00000000, 0x09c096b300000000, 0xbc267d3500000000, - 0x60cacc0800000000, 0x04ff1e4e00000000, 0xd813af7300000000, - 0xcc95bac300000000, 0x10790bfe00000000, 0x744cd9b800000000, - 0xa8a0688500000000}}; - -#else /* W == 4 */ - -local const z_crc_t FAR crc_braid_table[][256] = { - {0x00000000, 0x81256527, 0xd93bcc0f, 0x581ea928, 0x69069e5f, - 0xe823fb78, 0xb03d5250, 0x31183777, 0xd20d3cbe, 0x53285999, - 0x0b36f0b1, 0x8a139596, 0xbb0ba2e1, 0x3a2ec7c6, 0x62306eee, - 0xe3150bc9, 0x7f6b7f3d, 0xfe4e1a1a, 0xa650b332, 0x2775d615, - 0x166de162, 0x97488445, 0xcf562d6d, 0x4e73484a, 0xad664383, - 0x2c4326a4, 0x745d8f8c, 0xf578eaab, 0xc460dddc, 0x4545b8fb, - 0x1d5b11d3, 0x9c7e74f4, 0xfed6fe7a, 0x7ff39b5d, 0x27ed3275, - 0xa6c85752, 0x97d06025, 0x16f50502, 0x4eebac2a, 0xcfcec90d, - 0x2cdbc2c4, 0xadfea7e3, 0xf5e00ecb, 0x74c56bec, 0x45dd5c9b, - 0xc4f839bc, 0x9ce69094, 0x1dc3f5b3, 0x81bd8147, 0x0098e460, - 0x58864d48, 0xd9a3286f, 0xe8bb1f18, 0x699e7a3f, 0x3180d317, - 0xb0a5b630, 0x53b0bdf9, 0xd295d8de, 0x8a8b71f6, 0x0bae14d1, - 0x3ab623a6, 0xbb934681, 0xe38defa9, 0x62a88a8e, 0x26dcfab5, - 0xa7f99f92, 0xffe736ba, 0x7ec2539d, 0x4fda64ea, 0xceff01cd, - 0x96e1a8e5, 0x17c4cdc2, 0xf4d1c60b, 0x75f4a32c, 0x2dea0a04, - 0xaccf6f23, 0x9dd75854, 0x1cf23d73, 0x44ec945b, 0xc5c9f17c, - 0x59b78588, 0xd892e0af, 0x808c4987, 0x01a92ca0, 0x30b11bd7, - 0xb1947ef0, 0xe98ad7d8, 0x68afb2ff, 0x8bbab936, 0x0a9fdc11, - 0x52817539, 0xd3a4101e, 0xe2bc2769, 0x6399424e, 0x3b87eb66, - 0xbaa28e41, 0xd80a04cf, 0x592f61e8, 0x0131c8c0, 0x8014ade7, - 0xb10c9a90, 0x3029ffb7, 0x6837569f, 0xe91233b8, 0x0a073871, - 0x8b225d56, 0xd33cf47e, 0x52199159, 0x6301a62e, 0xe224c309, - 0xba3a6a21, 0x3b1f0f06, 0xa7617bf2, 0x26441ed5, 0x7e5ab7fd, - 0xff7fd2da, 0xce67e5ad, 0x4f42808a, 0x175c29a2, 0x96794c85, - 0x756c474c, 0xf449226b, 0xac578b43, 0x2d72ee64, 0x1c6ad913, - 0x9d4fbc34, 0xc551151c, 0x4474703b, 0x4db9f56a, 0xcc9c904d, - 0x94823965, 0x15a75c42, 0x24bf6b35, 0xa59a0e12, 0xfd84a73a, - 0x7ca1c21d, 0x9fb4c9d4, 0x1e91acf3, 0x468f05db, 0xc7aa60fc, - 0xf6b2578b, 0x779732ac, 0x2f899b84, 0xaeacfea3, 0x32d28a57, - 0xb3f7ef70, 0xebe94658, 0x6acc237f, 0x5bd41408, 0xdaf1712f, - 0x82efd807, 0x03cabd20, 0xe0dfb6e9, 0x61fad3ce, 0x39e47ae6, - 0xb8c11fc1, 0x89d928b6, 0x08fc4d91, 0x50e2e4b9, 0xd1c7819e, - 0xb36f0b10, 0x324a6e37, 0x6a54c71f, 0xeb71a238, 0xda69954f, - 0x5b4cf068, 0x03525940, 0x82773c67, 0x616237ae, 0xe0475289, - 0xb859fba1, 0x397c9e86, 0x0864a9f1, 0x8941ccd6, 0xd15f65fe, - 0x507a00d9, 0xcc04742d, 0x4d21110a, 0x153fb822, 0x941add05, - 0xa502ea72, 0x24278f55, 0x7c39267d, 0xfd1c435a, 0x1e094893, - 0x9f2c2db4, 0xc732849c, 0x4617e1bb, 0x770fd6cc, 0xf62ab3eb, - 0xae341ac3, 0x2f117fe4, 0x6b650fdf, 0xea406af8, 0xb25ec3d0, - 0x337ba6f7, 0x02639180, 0x8346f4a7, 0xdb585d8f, 0x5a7d38a8, - 0xb9683361, 0x384d5646, 0x6053ff6e, 0xe1769a49, 0xd06ead3e, - 0x514bc819, 0x09556131, 0x88700416, 0x140e70e2, 0x952b15c5, - 0xcd35bced, 0x4c10d9ca, 0x7d08eebd, 0xfc2d8b9a, 0xa43322b2, - 0x25164795, 0xc6034c5c, 0x4726297b, 0x1f388053, 0x9e1de574, - 0xaf05d203, 0x2e20b724, 0x763e1e0c, 0xf71b7b2b, 0x95b3f1a5, - 0x14969482, 0x4c883daa, 0xcdad588d, 0xfcb56ffa, 0x7d900add, - 0x258ea3f5, 0xa4abc6d2, 0x47becd1b, 0xc69ba83c, 0x9e850114, - 0x1fa06433, 0x2eb85344, 0xaf9d3663, 0xf7839f4b, 0x76a6fa6c, - 0xead88e98, 0x6bfdebbf, 0x33e34297, 0xb2c627b0, 0x83de10c7, - 0x02fb75e0, 0x5ae5dcc8, 0xdbc0b9ef, 0x38d5b226, 0xb9f0d701, - 0xe1ee7e29, 0x60cb1b0e, 0x51d32c79, 0xd0f6495e, 0x88e8e076, - 0x09cd8551}, - {0x00000000, 0x9b73ead4, 0xed96d3e9, 0x76e5393d, 0x005ca193, - 0x9b2f4b47, 0xedca727a, 0x76b998ae, 0x00b94326, 0x9bcaa9f2, - 0xed2f90cf, 0x765c7a1b, 0x00e5e2b5, 0x9b960861, 0xed73315c, - 0x7600db88, 0x0172864c, 0x9a016c98, 0xece455a5, 0x7797bf71, - 0x012e27df, 0x9a5dcd0b, 0xecb8f436, 0x77cb1ee2, 0x01cbc56a, - 0x9ab82fbe, 0xec5d1683, 0x772efc57, 0x019764f9, 0x9ae48e2d, - 0xec01b710, 0x77725dc4, 0x02e50c98, 0x9996e64c, 0xef73df71, - 0x740035a5, 0x02b9ad0b, 0x99ca47df, 0xef2f7ee2, 0x745c9436, - 0x025c4fbe, 0x992fa56a, 0xefca9c57, 0x74b97683, 0x0200ee2d, - 0x997304f9, 0xef963dc4, 0x74e5d710, 0x03978ad4, 0x98e46000, - 0xee01593d, 0x7572b3e9, 0x03cb2b47, 0x98b8c193, 0xee5df8ae, - 0x752e127a, 0x032ec9f2, 0x985d2326, 0xeeb81a1b, 0x75cbf0cf, - 0x03726861, 0x980182b5, 0xeee4bb88, 0x7597515c, 0x05ca1930, - 0x9eb9f3e4, 0xe85ccad9, 0x732f200d, 0x0596b8a3, 0x9ee55277, - 0xe8006b4a, 0x7373819e, 0x05735a16, 0x9e00b0c2, 0xe8e589ff, - 0x7396632b, 0x052ffb85, 0x9e5c1151, 0xe8b9286c, 0x73cac2b8, - 0x04b89f7c, 0x9fcb75a8, 0xe92e4c95, 0x725da641, 0x04e43eef, - 0x9f97d43b, 0xe972ed06, 0x720107d2, 0x0401dc5a, 0x9f72368e, - 0xe9970fb3, 0x72e4e567, 0x045d7dc9, 0x9f2e971d, 0xe9cbae20, - 0x72b844f4, 0x072f15a8, 0x9c5cff7c, 0xeab9c641, 0x71ca2c95, - 0x0773b43b, 0x9c005eef, 0xeae567d2, 0x71968d06, 0x0796568e, - 0x9ce5bc5a, 0xea008567, 0x71736fb3, 0x07caf71d, 0x9cb91dc9, - 0xea5c24f4, 0x712fce20, 0x065d93e4, 0x9d2e7930, 0xebcb400d, - 0x70b8aad9, 0x06013277, 0x9d72d8a3, 0xeb97e19e, 0x70e40b4a, - 0x06e4d0c2, 0x9d973a16, 0xeb72032b, 0x7001e9ff, 0x06b87151, - 0x9dcb9b85, 0xeb2ea2b8, 0x705d486c, 0x0b943260, 0x90e7d8b4, - 0xe602e189, 0x7d710b5d, 0x0bc893f3, 0x90bb7927, 0xe65e401a, - 0x7d2daace, 0x0b2d7146, 0x905e9b92, 0xe6bba2af, 0x7dc8487b, - 0x0b71d0d5, 0x90023a01, 0xe6e7033c, 0x7d94e9e8, 0x0ae6b42c, - 0x91955ef8, 0xe77067c5, 0x7c038d11, 0x0aba15bf, 0x91c9ff6b, - 0xe72cc656, 0x7c5f2c82, 0x0a5ff70a, 0x912c1dde, 0xe7c924e3, - 0x7cbace37, 0x0a035699, 0x9170bc4d, 0xe7958570, 0x7ce66fa4, - 0x09713ef8, 0x9202d42c, 0xe4e7ed11, 0x7f9407c5, 0x092d9f6b, - 0x925e75bf, 0xe4bb4c82, 0x7fc8a656, 0x09c87dde, 0x92bb970a, - 0xe45eae37, 0x7f2d44e3, 0x0994dc4d, 0x92e73699, 0xe4020fa4, - 0x7f71e570, 0x0803b8b4, 0x93705260, 0xe5956b5d, 0x7ee68189, - 0x085f1927, 0x932cf3f3, 0xe5c9cace, 0x7eba201a, 0x08bafb92, - 0x93c91146, 0xe52c287b, 0x7e5fc2af, 0x08e65a01, 0x9395b0d5, - 0xe57089e8, 0x7e03633c, 0x0e5e2b50, 0x952dc184, 0xe3c8f8b9, - 0x78bb126d, 0x0e028ac3, 0x95716017, 0xe394592a, 0x78e7b3fe, - 0x0ee76876, 0x959482a2, 0xe371bb9f, 0x7802514b, 0x0ebbc9e5, - 0x95c82331, 0xe32d1a0c, 0x785ef0d8, 0x0f2cad1c, 0x945f47c8, - 0xe2ba7ef5, 0x79c99421, 0x0f700c8f, 0x9403e65b, 0xe2e6df66, - 0x799535b2, 0x0f95ee3a, 0x94e604ee, 0xe2033dd3, 0x7970d707, - 0x0fc94fa9, 0x94baa57d, 0xe25f9c40, 0x792c7694, 0x0cbb27c8, - 0x97c8cd1c, 0xe12df421, 0x7a5e1ef5, 0x0ce7865b, 0x97946c8f, - 0xe17155b2, 0x7a02bf66, 0x0c0264ee, 0x97718e3a, 0xe194b707, - 0x7ae75dd3, 0x0c5ec57d, 0x972d2fa9, 0xe1c81694, 0x7abbfc40, - 0x0dc9a184, 0x96ba4b50, 0xe05f726d, 0x7b2c98b9, 0x0d950017, - 0x96e6eac3, 0xe003d3fe, 0x7b70392a, 0x0d70e2a2, 0x96030876, - 0xe0e6314b, 0x7b95db9f, 0x0d2c4331, 0x965fa9e5, 0xe0ba90d8, - 0x7bc97a0c}, - {0x00000000, 0x172864c0, 0x2e50c980, 0x3978ad40, 0x5ca19300, - 0x4b89f7c0, 0x72f15a80, 0x65d93e40, 0xb9432600, 0xae6b42c0, - 0x9713ef80, 0x803b8b40, 0xe5e2b500, 0xf2cad1c0, 0xcbb27c80, - 0xdc9a1840, 0xa9f74a41, 0xbedf2e81, 0x87a783c1, 0x908fe701, - 0xf556d941, 0xe27ebd81, 0xdb0610c1, 0xcc2e7401, 0x10b46c41, - 0x079c0881, 0x3ee4a5c1, 0x29ccc101, 0x4c15ff41, 0x5b3d9b81, - 0x624536c1, 0x756d5201, 0x889f92c3, 0x9fb7f603, 0xa6cf5b43, - 0xb1e73f83, 0xd43e01c3, 0xc3166503, 0xfa6ec843, 0xed46ac83, - 0x31dcb4c3, 0x26f4d003, 0x1f8c7d43, 0x08a41983, 0x6d7d27c3, - 0x7a554303, 0x432dee43, 0x54058a83, 0x2168d882, 0x3640bc42, - 0x0f381102, 0x181075c2, 0x7dc94b82, 0x6ae12f42, 0x53998202, - 0x44b1e6c2, 0x982bfe82, 0x8f039a42, 0xb67b3702, 0xa15353c2, - 0xc48a6d82, 0xd3a20942, 0xeadaa402, 0xfdf2c0c2, 0xca4e23c7, - 0xdd664707, 0xe41eea47, 0xf3368e87, 0x96efb0c7, 0x81c7d407, - 0xb8bf7947, 0xaf971d87, 0x730d05c7, 0x64256107, 0x5d5dcc47, - 0x4a75a887, 0x2fac96c7, 0x3884f207, 0x01fc5f47, 0x16d43b87, - 0x63b96986, 0x74910d46, 0x4de9a006, 0x5ac1c4c6, 0x3f18fa86, - 0x28309e46, 0x11483306, 0x066057c6, 0xdafa4f86, 0xcdd22b46, - 0xf4aa8606, 0xe382e2c6, 0x865bdc86, 0x9173b846, 0xa80b1506, - 0xbf2371c6, 0x42d1b104, 0x55f9d5c4, 0x6c817884, 0x7ba91c44, - 0x1e702204, 0x095846c4, 0x3020eb84, 0x27088f44, 0xfb929704, - 0xecbaf3c4, 0xd5c25e84, 0xc2ea3a44, 0xa7330404, 0xb01b60c4, - 0x8963cd84, 0x9e4ba944, 0xeb26fb45, 0xfc0e9f85, 0xc57632c5, - 0xd25e5605, 0xb7876845, 0xa0af0c85, 0x99d7a1c5, 0x8effc505, - 0x5265dd45, 0x454db985, 0x7c3514c5, 0x6b1d7005, 0x0ec44e45, - 0x19ec2a85, 0x209487c5, 0x37bce305, 0x4fed41cf, 0x58c5250f, - 0x61bd884f, 0x7695ec8f, 0x134cd2cf, 0x0464b60f, 0x3d1c1b4f, - 0x2a347f8f, 0xf6ae67cf, 0xe186030f, 0xd8feae4f, 0xcfd6ca8f, - 0xaa0ff4cf, 0xbd27900f, 0x845f3d4f, 0x9377598f, 0xe61a0b8e, - 0xf1326f4e, 0xc84ac20e, 0xdf62a6ce, 0xbabb988e, 0xad93fc4e, - 0x94eb510e, 0x83c335ce, 0x5f592d8e, 0x4871494e, 0x7109e40e, - 0x662180ce, 0x03f8be8e, 0x14d0da4e, 0x2da8770e, 0x3a8013ce, - 0xc772d30c, 0xd05ab7cc, 0xe9221a8c, 0xfe0a7e4c, 0x9bd3400c, - 0x8cfb24cc, 0xb583898c, 0xa2abed4c, 0x7e31f50c, 0x691991cc, - 0x50613c8c, 0x4749584c, 0x2290660c, 0x35b802cc, 0x0cc0af8c, - 0x1be8cb4c, 0x6e85994d, 0x79adfd8d, 0x40d550cd, 0x57fd340d, - 0x32240a4d, 0x250c6e8d, 0x1c74c3cd, 0x0b5ca70d, 0xd7c6bf4d, - 0xc0eedb8d, 0xf99676cd, 0xeebe120d, 0x8b672c4d, 0x9c4f488d, - 0xa537e5cd, 0xb21f810d, 0x85a36208, 0x928b06c8, 0xabf3ab88, - 0xbcdbcf48, 0xd902f108, 0xce2a95c8, 0xf7523888, 0xe07a5c48, - 0x3ce04408, 0x2bc820c8, 0x12b08d88, 0x0598e948, 0x6041d708, - 0x7769b3c8, 0x4e111e88, 0x59397a48, 0x2c542849, 0x3b7c4c89, - 0x0204e1c9, 0x152c8509, 0x70f5bb49, 0x67dddf89, 0x5ea572c9, - 0x498d1609, 0x95170e49, 0x823f6a89, 0xbb47c7c9, 0xac6fa309, - 0xc9b69d49, 0xde9ef989, 0xe7e654c9, 0xf0ce3009, 0x0d3cf0cb, - 0x1a14940b, 0x236c394b, 0x34445d8b, 0x519d63cb, 0x46b5070b, - 0x7fcdaa4b, 0x68e5ce8b, 0xb47fd6cb, 0xa357b20b, 0x9a2f1f4b, - 0x8d077b8b, 0xe8de45cb, 0xfff6210b, 0xc68e8c4b, 0xd1a6e88b, - 0xa4cbba8a, 0xb3e3de4a, 0x8a9b730a, 0x9db317ca, 0xf86a298a, - 0xef424d4a, 0xd63ae00a, 0xc11284ca, 0x1d889c8a, 0x0aa0f84a, - 0x33d8550a, 0x24f031ca, 0x41290f8a, 0x56016b4a, 0x6f79c60a, - 0x7851a2ca}, - {0x00000000, 0x9fda839e, 0xe4c4017d, 0x7b1e82e3, 0x12f904bb, - 0x8d238725, 0xf63d05c6, 0x69e78658, 0x25f20976, 0xba288ae8, - 0xc136080b, 0x5eec8b95, 0x370b0dcd, 0xa8d18e53, 0xd3cf0cb0, - 0x4c158f2e, 0x4be412ec, 0xd43e9172, 0xaf201391, 0x30fa900f, - 0x591d1657, 0xc6c795c9, 0xbdd9172a, 0x220394b4, 0x6e161b9a, - 0xf1cc9804, 0x8ad21ae7, 0x15089979, 0x7cef1f21, 0xe3359cbf, - 0x982b1e5c, 0x07f19dc2, 0x97c825d8, 0x0812a646, 0x730c24a5, - 0xecd6a73b, 0x85312163, 0x1aeba2fd, 0x61f5201e, 0xfe2fa380, - 0xb23a2cae, 0x2de0af30, 0x56fe2dd3, 0xc924ae4d, 0xa0c32815, - 0x3f19ab8b, 0x44072968, 0xdbddaaf6, 0xdc2c3734, 0x43f6b4aa, - 0x38e83649, 0xa732b5d7, 0xced5338f, 0x510fb011, 0x2a1132f2, - 0xb5cbb16c, 0xf9de3e42, 0x6604bddc, 0x1d1a3f3f, 0x82c0bca1, - 0xeb273af9, 0x74fdb967, 0x0fe33b84, 0x9039b81a, 0xf4e14df1, - 0x6b3bce6f, 0x10254c8c, 0x8fffcf12, 0xe618494a, 0x79c2cad4, - 0x02dc4837, 0x9d06cba9, 0xd1134487, 0x4ec9c719, 0x35d745fa, - 0xaa0dc664, 0xc3ea403c, 0x5c30c3a2, 0x272e4141, 0xb8f4c2df, - 0xbf055f1d, 0x20dfdc83, 0x5bc15e60, 0xc41bddfe, 0xadfc5ba6, - 0x3226d838, 0x49385adb, 0xd6e2d945, 0x9af7566b, 0x052dd5f5, - 0x7e335716, 0xe1e9d488, 0x880e52d0, 0x17d4d14e, 0x6cca53ad, - 0xf310d033, 0x63296829, 0xfcf3ebb7, 0x87ed6954, 0x1837eaca, - 0x71d06c92, 0xee0aef0c, 0x95146def, 0x0aceee71, 0x46db615f, - 0xd901e2c1, 0xa21f6022, 0x3dc5e3bc, 0x542265e4, 0xcbf8e67a, - 0xb0e66499, 0x2f3ce707, 0x28cd7ac5, 0xb717f95b, 0xcc097bb8, - 0x53d3f826, 0x3a347e7e, 0xa5eefde0, 0xdef07f03, 0x412afc9d, - 0x0d3f73b3, 0x92e5f02d, 0xe9fb72ce, 0x7621f150, 0x1fc67708, - 0x801cf496, 0xfb027675, 0x64d8f5eb, 0x32b39da3, 0xad691e3d, - 0xd6779cde, 0x49ad1f40, 0x204a9918, 0xbf901a86, 0xc48e9865, - 0x5b541bfb, 0x174194d5, 0x889b174b, 0xf38595a8, 0x6c5f1636, - 0x05b8906e, 0x9a6213f0, 0xe17c9113, 0x7ea6128d, 0x79578f4f, - 0xe68d0cd1, 0x9d938e32, 0x02490dac, 0x6bae8bf4, 0xf474086a, - 0x8f6a8a89, 0x10b00917, 0x5ca58639, 0xc37f05a7, 0xb8618744, - 0x27bb04da, 0x4e5c8282, 0xd186011c, 0xaa9883ff, 0x35420061, - 0xa57bb87b, 0x3aa13be5, 0x41bfb906, 0xde653a98, 0xb782bcc0, - 0x28583f5e, 0x5346bdbd, 0xcc9c3e23, 0x8089b10d, 0x1f533293, - 0x644db070, 0xfb9733ee, 0x9270b5b6, 0x0daa3628, 0x76b4b4cb, - 0xe96e3755, 0xee9faa97, 0x71452909, 0x0a5babea, 0x95812874, - 0xfc66ae2c, 0x63bc2db2, 0x18a2af51, 0x87782ccf, 0xcb6da3e1, - 0x54b7207f, 0x2fa9a29c, 0xb0732102, 0xd994a75a, 0x464e24c4, - 0x3d50a627, 0xa28a25b9, 0xc652d052, 0x598853cc, 0x2296d12f, - 0xbd4c52b1, 0xd4abd4e9, 0x4b715777, 0x306fd594, 0xafb5560a, - 0xe3a0d924, 0x7c7a5aba, 0x0764d859, 0x98be5bc7, 0xf159dd9f, - 0x6e835e01, 0x159ddce2, 0x8a475f7c, 0x8db6c2be, 0x126c4120, - 0x6972c3c3, 0xf6a8405d, 0x9f4fc605, 0x0095459b, 0x7b8bc778, - 0xe45144e6, 0xa844cbc8, 0x379e4856, 0x4c80cab5, 0xd35a492b, - 0xbabdcf73, 0x25674ced, 0x5e79ce0e, 0xc1a34d90, 0x519af58a, - 0xce407614, 0xb55ef4f7, 0x2a847769, 0x4363f131, 0xdcb972af, - 0xa7a7f04c, 0x387d73d2, 0x7468fcfc, 0xebb27f62, 0x90acfd81, - 0x0f767e1f, 0x6691f847, 0xf94b7bd9, 0x8255f93a, 0x1d8f7aa4, - 0x1a7ee766, 0x85a464f8, 0xfebae61b, 0x61606585, 0x0887e3dd, - 0x975d6043, 0xec43e2a0, 0x7399613e, 0x3f8cee10, 0xa0566d8e, - 0xdb48ef6d, 0x44926cf3, 0x2d75eaab, 0xb2af6935, 0xc9b1ebd6, - 0x566b6848}}; - -local const z_word_t FAR crc_braid_big_table[][256] = { - {0x00000000, 0x9e83da9f, 0x7d01c4e4, 0xe3821e7b, 0xbb04f912, - 0x2587238d, 0xc6053df6, 0x5886e769, 0x7609f225, 0xe88a28ba, - 0x0b0836c1, 0x958bec5e, 0xcd0d0b37, 0x538ed1a8, 0xb00ccfd3, - 0x2e8f154c, 0xec12e44b, 0x72913ed4, 0x911320af, 0x0f90fa30, - 0x57161d59, 0xc995c7c6, 0x2a17d9bd, 0xb4940322, 0x9a1b166e, - 0x0498ccf1, 0xe71ad28a, 0x79990815, 0x211fef7c, 0xbf9c35e3, - 0x5c1e2b98, 0xc29df107, 0xd825c897, 0x46a61208, 0xa5240c73, - 0x3ba7d6ec, 0x63213185, 0xfda2eb1a, 0x1e20f561, 0x80a32ffe, - 0xae2c3ab2, 0x30afe02d, 0xd32dfe56, 0x4dae24c9, 0x1528c3a0, - 0x8bab193f, 0x68290744, 0xf6aadddb, 0x34372cdc, 0xaab4f643, - 0x4936e838, 0xd7b532a7, 0x8f33d5ce, 0x11b00f51, 0xf232112a, - 0x6cb1cbb5, 0x423edef9, 0xdcbd0466, 0x3f3f1a1d, 0xa1bcc082, - 0xf93a27eb, 0x67b9fd74, 0x843be30f, 0x1ab83990, 0xf14de1f4, - 0x6fce3b6b, 0x8c4c2510, 0x12cfff8f, 0x4a4918e6, 0xd4cac279, - 0x3748dc02, 0xa9cb069d, 0x874413d1, 0x19c7c94e, 0xfa45d735, - 0x64c60daa, 0x3c40eac3, 0xa2c3305c, 0x41412e27, 0xdfc2f4b8, - 0x1d5f05bf, 0x83dcdf20, 0x605ec15b, 0xfedd1bc4, 0xa65bfcad, - 0x38d82632, 0xdb5a3849, 0x45d9e2d6, 0x6b56f79a, 0xf5d52d05, - 0x1657337e, 0x88d4e9e1, 0xd0520e88, 0x4ed1d417, 0xad53ca6c, - 0x33d010f3, 0x29682963, 0xb7ebf3fc, 0x5469ed87, 0xcaea3718, - 0x926cd071, 0x0cef0aee, 0xef6d1495, 0x71eece0a, 0x5f61db46, - 0xc1e201d9, 0x22601fa2, 0xbce3c53d, 0xe4652254, 0x7ae6f8cb, - 0x9964e6b0, 0x07e73c2f, 0xc57acd28, 0x5bf917b7, 0xb87b09cc, - 0x26f8d353, 0x7e7e343a, 0xe0fdeea5, 0x037ff0de, 0x9dfc2a41, - 0xb3733f0d, 0x2df0e592, 0xce72fbe9, 0x50f12176, 0x0877c61f, - 0x96f41c80, 0x757602fb, 0xebf5d864, 0xa39db332, 0x3d1e69ad, - 0xde9c77d6, 0x401fad49, 0x18994a20, 0x861a90bf, 0x65988ec4, - 0xfb1b545b, 0xd5944117, 0x4b179b88, 0xa89585f3, 0x36165f6c, - 0x6e90b805, 0xf013629a, 0x13917ce1, 0x8d12a67e, 0x4f8f5779, - 0xd10c8de6, 0x328e939d, 0xac0d4902, 0xf48bae6b, 0x6a0874f4, - 0x898a6a8f, 0x1709b010, 0x3986a55c, 0xa7057fc3, 0x448761b8, - 0xda04bb27, 0x82825c4e, 0x1c0186d1, 0xff8398aa, 0x61004235, - 0x7bb87ba5, 0xe53ba13a, 0x06b9bf41, 0x983a65de, 0xc0bc82b7, - 0x5e3f5828, 0xbdbd4653, 0x233e9ccc, 0x0db18980, 0x9332531f, - 0x70b04d64, 0xee3397fb, 0xb6b57092, 0x2836aa0d, 0xcbb4b476, - 0x55376ee9, 0x97aa9fee, 0x09294571, 0xeaab5b0a, 0x74288195, - 0x2cae66fc, 0xb22dbc63, 0x51afa218, 0xcf2c7887, 0xe1a36dcb, - 0x7f20b754, 0x9ca2a92f, 0x022173b0, 0x5aa794d9, 0xc4244e46, - 0x27a6503d, 0xb9258aa2, 0x52d052c6, 0xcc538859, 0x2fd19622, - 0xb1524cbd, 0xe9d4abd4, 0x7757714b, 0x94d56f30, 0x0a56b5af, - 0x24d9a0e3, 0xba5a7a7c, 0x59d86407, 0xc75bbe98, 0x9fdd59f1, - 0x015e836e, 0xe2dc9d15, 0x7c5f478a, 0xbec2b68d, 0x20416c12, - 0xc3c37269, 0x5d40a8f6, 0x05c64f9f, 0x9b459500, 0x78c78b7b, - 0xe64451e4, 0xc8cb44a8, 0x56489e37, 0xb5ca804c, 0x2b495ad3, - 0x73cfbdba, 0xed4c6725, 0x0ece795e, 0x904da3c1, 0x8af59a51, - 0x147640ce, 0xf7f45eb5, 0x6977842a, 0x31f16343, 0xaf72b9dc, - 0x4cf0a7a7, 0xd2737d38, 0xfcfc6874, 0x627fb2eb, 0x81fdac90, - 0x1f7e760f, 0x47f89166, 0xd97b4bf9, 0x3af95582, 0xa47a8f1d, - 0x66e77e1a, 0xf864a485, 0x1be6bafe, 0x85656061, 0xdde38708, - 0x43605d97, 0xa0e243ec, 0x3e619973, 0x10ee8c3f, 0x8e6d56a0, - 0x6def48db, 0xf36c9244, 0xabea752d, 0x3569afb2, 0xd6ebb1c9, - 0x48686b56}, - {0x00000000, 0xc0642817, 0x80c9502e, 0x40ad7839, 0x0093a15c, - 0xc0f7894b, 0x805af172, 0x403ed965, 0x002643b9, 0xc0426bae, - 0x80ef1397, 0x408b3b80, 0x00b5e2e5, 0xc0d1caf2, 0x807cb2cb, - 0x40189adc, 0x414af7a9, 0x812edfbe, 0xc183a787, 0x01e78f90, - 0x41d956f5, 0x81bd7ee2, 0xc11006db, 0x01742ecc, 0x416cb410, - 0x81089c07, 0xc1a5e43e, 0x01c1cc29, 0x41ff154c, 0x819b3d5b, - 0xc1364562, 0x01526d75, 0xc3929f88, 0x03f6b79f, 0x435bcfa6, - 0x833fe7b1, 0xc3013ed4, 0x036516c3, 0x43c86efa, 0x83ac46ed, - 0xc3b4dc31, 0x03d0f426, 0x437d8c1f, 0x8319a408, 0xc3277d6d, - 0x0343557a, 0x43ee2d43, 0x838a0554, 0x82d86821, 0x42bc4036, - 0x0211380f, 0xc2751018, 0x824bc97d, 0x422fe16a, 0x02829953, - 0xc2e6b144, 0x82fe2b98, 0x429a038f, 0x02377bb6, 0xc25353a1, - 0x826d8ac4, 0x4209a2d3, 0x02a4daea, 0xc2c0f2fd, 0xc7234eca, - 0x074766dd, 0x47ea1ee4, 0x878e36f3, 0xc7b0ef96, 0x07d4c781, - 0x4779bfb8, 0x871d97af, 0xc7050d73, 0x07612564, 0x47cc5d5d, - 0x87a8754a, 0xc796ac2f, 0x07f28438, 0x475ffc01, 0x873bd416, - 0x8669b963, 0x460d9174, 0x06a0e94d, 0xc6c4c15a, 0x86fa183f, - 0x469e3028, 0x06334811, 0xc6576006, 0x864ffada, 0x462bd2cd, - 0x0686aaf4, 0xc6e282e3, 0x86dc5b86, 0x46b87391, 0x06150ba8, - 0xc67123bf, 0x04b1d142, 0xc4d5f955, 0x8478816c, 0x441ca97b, - 0x0422701e, 0xc4465809, 0x84eb2030, 0x448f0827, 0x049792fb, - 0xc4f3baec, 0x845ec2d5, 0x443aeac2, 0x040433a7, 0xc4601bb0, - 0x84cd6389, 0x44a94b9e, 0x45fb26eb, 0x859f0efc, 0xc53276c5, - 0x05565ed2, 0x456887b7, 0x850cafa0, 0xc5a1d799, 0x05c5ff8e, - 0x45dd6552, 0x85b94d45, 0xc514357c, 0x05701d6b, 0x454ec40e, - 0x852aec19, 0xc5879420, 0x05e3bc37, 0xcf41ed4f, 0x0f25c558, - 0x4f88bd61, 0x8fec9576, 0xcfd24c13, 0x0fb66404, 0x4f1b1c3d, - 0x8f7f342a, 0xcf67aef6, 0x0f0386e1, 0x4faefed8, 0x8fcad6cf, - 0xcff40faa, 0x0f9027bd, 0x4f3d5f84, 0x8f597793, 0x8e0b1ae6, - 0x4e6f32f1, 0x0ec24ac8, 0xcea662df, 0x8e98bbba, 0x4efc93ad, - 0x0e51eb94, 0xce35c383, 0x8e2d595f, 0x4e497148, 0x0ee40971, - 0xce802166, 0x8ebef803, 0x4edad014, 0x0e77a82d, 0xce13803a, - 0x0cd372c7, 0xccb75ad0, 0x8c1a22e9, 0x4c7e0afe, 0x0c40d39b, - 0xcc24fb8c, 0x8c8983b5, 0x4cedaba2, 0x0cf5317e, 0xcc911969, - 0x8c3c6150, 0x4c584947, 0x0c669022, 0xcc02b835, 0x8cafc00c, - 0x4ccbe81b, 0x4d99856e, 0x8dfdad79, 0xcd50d540, 0x0d34fd57, - 0x4d0a2432, 0x8d6e0c25, 0xcdc3741c, 0x0da75c0b, 0x4dbfc6d7, - 0x8ddbeec0, 0xcd7696f9, 0x0d12beee, 0x4d2c678b, 0x8d484f9c, - 0xcde537a5, 0x0d811fb2, 0x0862a385, 0xc8068b92, 0x88abf3ab, - 0x48cfdbbc, 0x08f102d9, 0xc8952ace, 0x883852f7, 0x485c7ae0, - 0x0844e03c, 0xc820c82b, 0x888db012, 0x48e99805, 0x08d74160, - 0xc8b36977, 0x881e114e, 0x487a3959, 0x4928542c, 0x894c7c3b, - 0xc9e10402, 0x09852c15, 0x49bbf570, 0x89dfdd67, 0xc972a55e, - 0x09168d49, 0x490e1795, 0x896a3f82, 0xc9c747bb, 0x09a36fac, - 0x499db6c9, 0x89f99ede, 0xc954e6e7, 0x0930cef0, 0xcbf03c0d, - 0x0b94141a, 0x4b396c23, 0x8b5d4434, 0xcb639d51, 0x0b07b546, - 0x4baacd7f, 0x8bcee568, 0xcbd67fb4, 0x0bb257a3, 0x4b1f2f9a, - 0x8b7b078d, 0xcb45dee8, 0x0b21f6ff, 0x4b8c8ec6, 0x8be8a6d1, - 0x8abacba4, 0x4adee3b3, 0x0a739b8a, 0xca17b39d, 0x8a296af8, - 0x4a4d42ef, 0x0ae03ad6, 0xca8412c1, 0x8a9c881d, 0x4af8a00a, - 0x0a55d833, 0xca31f024, 0x8a0f2941, 0x4a6b0156, 0x0ac6796f, - 0xcaa25178}, - {0x00000000, 0xd4ea739b, 0xe9d396ed, 0x3d39e576, 0x93a15c00, - 0x474b2f9b, 0x7a72caed, 0xae98b976, 0x2643b900, 0xf2a9ca9b, - 0xcf902fed, 0x1b7a5c76, 0xb5e2e500, 0x6108969b, 0x5c3173ed, - 0x88db0076, 0x4c867201, 0x986c019a, 0xa555e4ec, 0x71bf9777, - 0xdf272e01, 0x0bcd5d9a, 0x36f4b8ec, 0xe21ecb77, 0x6ac5cb01, - 0xbe2fb89a, 0x83165dec, 0x57fc2e77, 0xf9649701, 0x2d8ee49a, - 0x10b701ec, 0xc45d7277, 0x980ce502, 0x4ce69699, 0x71df73ef, - 0xa5350074, 0x0badb902, 0xdf47ca99, 0xe27e2fef, 0x36945c74, - 0xbe4f5c02, 0x6aa52f99, 0x579ccaef, 0x8376b974, 0x2dee0002, - 0xf9047399, 0xc43d96ef, 0x10d7e574, 0xd48a9703, 0x0060e498, - 0x3d5901ee, 0xe9b37275, 0x472bcb03, 0x93c1b898, 0xaef85dee, - 0x7a122e75, 0xf2c92e03, 0x26235d98, 0x1b1ab8ee, 0xcff0cb75, - 0x61687203, 0xb5820198, 0x88bbe4ee, 0x5c519775, 0x3019ca05, - 0xe4f3b99e, 0xd9ca5ce8, 0x0d202f73, 0xa3b89605, 0x7752e59e, - 0x4a6b00e8, 0x9e817373, 0x165a7305, 0xc2b0009e, 0xff89e5e8, - 0x2b639673, 0x85fb2f05, 0x51115c9e, 0x6c28b9e8, 0xb8c2ca73, - 0x7c9fb804, 0xa875cb9f, 0x954c2ee9, 0x41a65d72, 0xef3ee404, - 0x3bd4979f, 0x06ed72e9, 0xd2070172, 0x5adc0104, 0x8e36729f, - 0xb30f97e9, 0x67e5e472, 0xc97d5d04, 0x1d972e9f, 0x20aecbe9, - 0xf444b872, 0xa8152f07, 0x7cff5c9c, 0x41c6b9ea, 0x952cca71, - 0x3bb47307, 0xef5e009c, 0xd267e5ea, 0x068d9671, 0x8e569607, - 0x5abce59c, 0x678500ea, 0xb36f7371, 0x1df7ca07, 0xc91db99c, - 0xf4245cea, 0x20ce2f71, 0xe4935d06, 0x30792e9d, 0x0d40cbeb, - 0xd9aab870, 0x77320106, 0xa3d8729d, 0x9ee197eb, 0x4a0be470, - 0xc2d0e406, 0x163a979d, 0x2b0372eb, 0xffe90170, 0x5171b806, - 0x859bcb9d, 0xb8a22eeb, 0x6c485d70, 0x6032940b, 0xb4d8e790, - 0x89e102e6, 0x5d0b717d, 0xf393c80b, 0x2779bb90, 0x1a405ee6, - 0xceaa2d7d, 0x46712d0b, 0x929b5e90, 0xafa2bbe6, 0x7b48c87d, - 0xd5d0710b, 0x013a0290, 0x3c03e7e6, 0xe8e9947d, 0x2cb4e60a, - 0xf85e9591, 0xc56770e7, 0x118d037c, 0xbf15ba0a, 0x6bffc991, - 0x56c62ce7, 0x822c5f7c, 0x0af75f0a, 0xde1d2c91, 0xe324c9e7, - 0x37ceba7c, 0x9956030a, 0x4dbc7091, 0x708595e7, 0xa46fe67c, - 0xf83e7109, 0x2cd40292, 0x11ede7e4, 0xc507947f, 0x6b9f2d09, - 0xbf755e92, 0x824cbbe4, 0x56a6c87f, 0xde7dc809, 0x0a97bb92, - 0x37ae5ee4, 0xe3442d7f, 0x4ddc9409, 0x9936e792, 0xa40f02e4, - 0x70e5717f, 0xb4b80308, 0x60527093, 0x5d6b95e5, 0x8981e67e, - 0x27195f08, 0xf3f32c93, 0xcecac9e5, 0x1a20ba7e, 0x92fbba08, - 0x4611c993, 0x7b282ce5, 0xafc25f7e, 0x015ae608, 0xd5b09593, - 0xe88970e5, 0x3c63037e, 0x502b5e0e, 0x84c12d95, 0xb9f8c8e3, - 0x6d12bb78, 0xc38a020e, 0x17607195, 0x2a5994e3, 0xfeb3e778, - 0x7668e70e, 0xa2829495, 0x9fbb71e3, 0x4b510278, 0xe5c9bb0e, - 0x3123c895, 0x0c1a2de3, 0xd8f05e78, 0x1cad2c0f, 0xc8475f94, - 0xf57ebae2, 0x2194c979, 0x8f0c700f, 0x5be60394, 0x66dfe6e2, - 0xb2359579, 0x3aee950f, 0xee04e694, 0xd33d03e2, 0x07d77079, - 0xa94fc90f, 0x7da5ba94, 0x409c5fe2, 0x94762c79, 0xc827bb0c, - 0x1ccdc897, 0x21f42de1, 0xf51e5e7a, 0x5b86e70c, 0x8f6c9497, - 0xb25571e1, 0x66bf027a, 0xee64020c, 0x3a8e7197, 0x07b794e1, - 0xd35de77a, 0x7dc55e0c, 0xa92f2d97, 0x9416c8e1, 0x40fcbb7a, - 0x84a1c90d, 0x504bba96, 0x6d725fe0, 0xb9982c7b, 0x1700950d, - 0xc3eae696, 0xfed303e0, 0x2a39707b, 0xa2e2700d, 0x76080396, - 0x4b31e6e0, 0x9fdb957b, 0x31432c0d, 0xe5a95f96, 0xd890bae0, - 0x0c7ac97b}, - {0x00000000, 0x27652581, 0x0fcc3bd9, 0x28a91e58, 0x5f9e0669, - 0x78fb23e8, 0x50523db0, 0x77371831, 0xbe3c0dd2, 0x99592853, - 0xb1f0360b, 0x9695138a, 0xe1a20bbb, 0xc6c72e3a, 0xee6e3062, - 0xc90b15e3, 0x3d7f6b7f, 0x1a1a4efe, 0x32b350a6, 0x15d67527, - 0x62e16d16, 0x45844897, 0x6d2d56cf, 0x4a48734e, 0x834366ad, - 0xa426432c, 0x8c8f5d74, 0xabea78f5, 0xdcdd60c4, 0xfbb84545, - 0xd3115b1d, 0xf4747e9c, 0x7afed6fe, 0x5d9bf37f, 0x7532ed27, - 0x5257c8a6, 0x2560d097, 0x0205f516, 0x2aaceb4e, 0x0dc9cecf, - 0xc4c2db2c, 0xe3a7fead, 0xcb0ee0f5, 0xec6bc574, 0x9b5cdd45, - 0xbc39f8c4, 0x9490e69c, 0xb3f5c31d, 0x4781bd81, 0x60e49800, - 0x484d8658, 0x6f28a3d9, 0x181fbbe8, 0x3f7a9e69, 0x17d38031, - 0x30b6a5b0, 0xf9bdb053, 0xded895d2, 0xf6718b8a, 0xd114ae0b, - 0xa623b63a, 0x814693bb, 0xa9ef8de3, 0x8e8aa862, 0xb5fadc26, - 0x929ff9a7, 0xba36e7ff, 0x9d53c27e, 0xea64da4f, 0xcd01ffce, - 0xe5a8e196, 0xc2cdc417, 0x0bc6d1f4, 0x2ca3f475, 0x040aea2d, - 0x236fcfac, 0x5458d79d, 0x733df21c, 0x5b94ec44, 0x7cf1c9c5, - 0x8885b759, 0xafe092d8, 0x87498c80, 0xa02ca901, 0xd71bb130, - 0xf07e94b1, 0xd8d78ae9, 0xffb2af68, 0x36b9ba8b, 0x11dc9f0a, - 0x39758152, 0x1e10a4d3, 0x6927bce2, 0x4e429963, 0x66eb873b, - 0x418ea2ba, 0xcf040ad8, 0xe8612f59, 0xc0c83101, 0xe7ad1480, - 0x909a0cb1, 0xb7ff2930, 0x9f563768, 0xb83312e9, 0x7138070a, - 0x565d228b, 0x7ef43cd3, 0x59911952, 0x2ea60163, 0x09c324e2, - 0x216a3aba, 0x060f1f3b, 0xf27b61a7, 0xd51e4426, 0xfdb75a7e, - 0xdad27fff, 0xade567ce, 0x8a80424f, 0xa2295c17, 0x854c7996, - 0x4c476c75, 0x6b2249f4, 0x438b57ac, 0x64ee722d, 0x13d96a1c, - 0x34bc4f9d, 0x1c1551c5, 0x3b707444, 0x6af5b94d, 0x4d909ccc, - 0x65398294, 0x425ca715, 0x356bbf24, 0x120e9aa5, 0x3aa784fd, - 0x1dc2a17c, 0xd4c9b49f, 0xf3ac911e, 0xdb058f46, 0xfc60aac7, - 0x8b57b2f6, 0xac329777, 0x849b892f, 0xa3feacae, 0x578ad232, - 0x70eff7b3, 0x5846e9eb, 0x7f23cc6a, 0x0814d45b, 0x2f71f1da, - 0x07d8ef82, 0x20bdca03, 0xe9b6dfe0, 0xced3fa61, 0xe67ae439, - 0xc11fc1b8, 0xb628d989, 0x914dfc08, 0xb9e4e250, 0x9e81c7d1, - 0x100b6fb3, 0x376e4a32, 0x1fc7546a, 0x38a271eb, 0x4f9569da, - 0x68f04c5b, 0x40595203, 0x673c7782, 0xae376261, 0x895247e0, - 0xa1fb59b8, 0x869e7c39, 0xf1a96408, 0xd6cc4189, 0xfe655fd1, - 0xd9007a50, 0x2d7404cc, 0x0a11214d, 0x22b83f15, 0x05dd1a94, - 0x72ea02a5, 0x558f2724, 0x7d26397c, 0x5a431cfd, 0x9348091e, - 0xb42d2c9f, 0x9c8432c7, 0xbbe11746, 0xccd60f77, 0xebb32af6, - 0xc31a34ae, 0xe47f112f, 0xdf0f656b, 0xf86a40ea, 0xd0c35eb2, - 0xf7a67b33, 0x80916302, 0xa7f44683, 0x8f5d58db, 0xa8387d5a, - 0x613368b9, 0x46564d38, 0x6eff5360, 0x499a76e1, 0x3ead6ed0, - 0x19c84b51, 0x31615509, 0x16047088, 0xe2700e14, 0xc5152b95, - 0xedbc35cd, 0xcad9104c, 0xbdee087d, 0x9a8b2dfc, 0xb22233a4, - 0x95471625, 0x5c4c03c6, 0x7b292647, 0x5380381f, 0x74e51d9e, - 0x03d205af, 0x24b7202e, 0x0c1e3e76, 0x2b7b1bf7, 0xa5f1b395, - 0x82949614, 0xaa3d884c, 0x8d58adcd, 0xfa6fb5fc, 0xdd0a907d, - 0xf5a38e25, 0xd2c6aba4, 0x1bcdbe47, 0x3ca89bc6, 0x1401859e, - 0x3364a01f, 0x4453b82e, 0x63369daf, 0x4b9f83f7, 0x6cfaa676, - 0x988ed8ea, 0xbfebfd6b, 0x9742e333, 0xb027c6b2, 0xc710de83, - 0xe075fb02, 0xc8dce55a, 0xefb9c0db, 0x26b2d538, 0x01d7f0b9, - 0x297eeee1, 0x0e1bcb60, 0x792cd351, 0x5e49f6d0, 0x76e0e888, - 0x5185cd09}}; - -#endif - -#endif - -#endif - -local const z_crc_t FAR x2n_table[] = { - 0x40000000, 0x20000000, 0x08000000, 0x00800000, 0x00008000, - 0xedb88320, 0xb1e6b092, 0xa06a2517, 0xed627dae, 0x88d14467, - 0xd7bbfe6a, 0xec447f11, 0x8e7ea170, 0x6427800e, 0x4d47bae0, - 0x09fe548f, 0x83852d0f, 0x30362f1a, 0x7b5a9cc3, 0x31fec169, - 0x9fec022a, 0x6c8dedc4, 0x15d6874d, 0x5fde7a4e, 0xbad90e37, - 0x2e4e5eef, 0x4eaba214, 0xa8a472c0, 0x429a969e, 0x148d302a, - 0xc40ba6d0, 0xc4e22c3c}; diff --git a/phobos/etc/c/zlib/deflate.c b/phobos/etc/c/zlib/deflate.c deleted file mode 100644 index 012ea81..0000000 --- a/phobos/etc/c/zlib/deflate.c +++ /dev/null @@ -1,2139 +0,0 @@ -/* deflate.c -- compress data using the deflation algorithm - * Copyright (C) 1995-2024 Jean-loup Gailly and Mark Adler - * For conditions of distribution and use, see copyright notice in zlib.h - */ - -/* - * ALGORITHM - * - * The "deflation" process depends on being able to identify portions - * of the input text which are identical to earlier input (within a - * sliding window trailing behind the input currently being processed). - * - * The most straightforward technique turns out to be the fastest for - * most input files: try all possible matches and select the longest. - * The key feature of this algorithm is that insertions into the string - * dictionary are very simple and thus fast, and deletions are avoided - * completely. Insertions are performed at each input character, whereas - * string matches are performed only when the previous match ends. So it - * is preferable to spend more time in matches to allow very fast string - * insertions and avoid deletions. The matching algorithm for small - * strings is inspired from that of Rabin & Karp. A brute force approach - * is used to find longer strings when a small match has been found. - * A similar algorithm is used in comic (by Jan-Mark Wams) and freeze - * (by Leonid Broukhis). - * A previous version of this file used a more sophisticated algorithm - * (by Fiala and Greene) which is guaranteed to run in linear amortized - * time, but has a larger average cost, uses more memory and is patented. - * However the F&G algorithm may be faster for some highly redundant - * files if the parameter max_chain_length (described below) is too large. - * - * ACKNOWLEDGEMENTS - * - * The idea of lazy evaluation of matches is due to Jan-Mark Wams, and - * I found it in 'freeze' written by Leonid Broukhis. - * Thanks to many people for bug reports and testing. - * - * REFERENCES - * - * Deutsch, L.P.,"DEFLATE Compressed Data Format Specification". - * Available in http://tools.ietf.org/html/rfc1951 - * - * A description of the Rabin and Karp algorithm is given in the book - * "Algorithms" by R. Sedgewick, Addison-Wesley, p252. - * - * Fiala,E.R., and Greene,D.H. - * Data Compression with Finite Windows, Comm.ACM, 32,4 (1989) 490-595 - * - */ - -/* @(#) $Id$ */ - -#include "deflate.h" - -const char deflate_copyright[] = - " deflate 1.3.1 Copyright 1995-2024 Jean-loup Gailly and Mark Adler "; -/* - If you use the zlib library in a product, an acknowledgment is welcome - in the documentation of your product. If for some reason you cannot - include such an acknowledgment, I would appreciate that you keep this - copyright string in the executable of your product. - */ - -typedef enum { - need_more, /* block not completed, need more input or more output */ - block_done, /* block flush performed */ - finish_started, /* finish started, need only more output at next deflate */ - finish_done /* finish done, accept no more input or output */ -} block_state; - -typedef block_state (*compress_func)(deflate_state *s, int flush); -/* Compression function. Returns the block state after the call. */ - -local block_state deflate_stored(deflate_state *s, int flush); -local block_state deflate_fast(deflate_state *s, int flush); -#ifndef FASTEST -local block_state deflate_slow(deflate_state *s, int flush); -#endif -local block_state deflate_rle(deflate_state *s, int flush); -local block_state deflate_huff(deflate_state *s, int flush); - -/* =========================================================================== - * Local data - */ - -#define NIL 0 -/* Tail of hash chains */ - -#ifndef TOO_FAR -# define TOO_FAR 4096 -#endif -/* Matches of length 3 are discarded if their distance exceeds TOO_FAR */ - -/* Values for max_lazy_match, good_match and max_chain_length, depending on - * the desired pack level (0..9). The values given below have been tuned to - * exclude worst case performance for pathological files. Better values may be - * found for specific files. - */ -typedef struct config_s { - ush good_length; /* reduce lazy search above this match length */ - ush max_lazy; /* do not perform lazy search above this match length */ - ush nice_length; /* quit search above this match length */ - ush max_chain; - compress_func func; -} config; - -#ifdef FASTEST -local const config configuration_table[2] = { -/* good lazy nice chain */ -/* 0 */ {0, 0, 0, 0, deflate_stored}, /* store only */ -/* 1 */ {4, 4, 8, 4, deflate_fast}}; /* max speed, no lazy matches */ -#else -local const config configuration_table[10] = { -/* good lazy nice chain */ -/* 0 */ {0, 0, 0, 0, deflate_stored}, /* store only */ -/* 1 */ {4, 4, 8, 4, deflate_fast}, /* max speed, no lazy matches */ -/* 2 */ {4, 5, 16, 8, deflate_fast}, -/* 3 */ {4, 6, 32, 32, deflate_fast}, - -/* 4 */ {4, 4, 16, 16, deflate_slow}, /* lazy matches */ -/* 5 */ {8, 16, 32, 32, deflate_slow}, -/* 6 */ {8, 16, 128, 128, deflate_slow}, -/* 7 */ {8, 32, 128, 256, deflate_slow}, -/* 8 */ {32, 128, 258, 1024, deflate_slow}, -/* 9 */ {32, 258, 258, 4096, deflate_slow}}; /* max compression */ -#endif - -/* Note: the deflate() code requires max_lazy >= MIN_MATCH and max_chain >= 4 - * For deflate_fast() (levels <= 3) good is ignored and lazy has a different - * meaning. - */ - -/* rank Z_BLOCK between Z_NO_FLUSH and Z_PARTIAL_FLUSH */ -#define RANK(f) (((f) * 2) - ((f) > 4 ? 9 : 0)) - -/* =========================================================================== - * Update a hash value with the given input byte - * IN assertion: all calls to UPDATE_HASH are made with consecutive input - * characters, so that a running hash key can be computed from the previous - * key instead of complete recalculation each time. - */ -#define UPDATE_HASH(s,h,c) (h = (((h) << s->hash_shift) ^ (c)) & s->hash_mask) - - -/* =========================================================================== - * Insert string str in the dictionary and set match_head to the previous head - * of the hash chain (the most recent string with same hash key). Return - * the previous length of the hash chain. - * If this file is compiled with -DFASTEST, the compression level is forced - * to 1, and no hash chains are maintained. - * IN assertion: all calls to INSERT_STRING are made with consecutive input - * characters and the first MIN_MATCH bytes of str are valid (except for - * the last MIN_MATCH-1 bytes of the input file). - */ -#ifdef FASTEST -#define INSERT_STRING(s, str, match_head) \ - (UPDATE_HASH(s, s->ins_h, s->window[(str) + (MIN_MATCH-1)]), \ - match_head = s->head[s->ins_h], \ - s->head[s->ins_h] = (Pos)(str)) -#else -#define INSERT_STRING(s, str, match_head) \ - (UPDATE_HASH(s, s->ins_h, s->window[(str) + (MIN_MATCH-1)]), \ - match_head = s->prev[(str) & s->w_mask] = s->head[s->ins_h], \ - s->head[s->ins_h] = (Pos)(str)) -#endif - -/* =========================================================================== - * Initialize the hash table (avoiding 64K overflow for 16 bit systems). - * prev[] will be initialized on the fly. - */ -#define CLEAR_HASH(s) \ - do { \ - s->head[s->hash_size - 1] = NIL; \ - zmemzero((Bytef *)s->head, \ - (unsigned)(s->hash_size - 1)*sizeof(*s->head)); \ - } while (0) - -/* =========================================================================== - * Slide the hash table when sliding the window down (could be avoided with 32 - * bit values at the expense of memory usage). We slide even when level == 0 to - * keep the hash table consistent if we switch back to level > 0 later. - */ -#if defined(__has_feature) -# if __has_feature(memory_sanitizer) - __attribute__((no_sanitize("memory"))) -# endif -#endif -local void slide_hash(deflate_state *s) { - unsigned n, m; - Posf *p; - uInt wsize = s->w_size; - - n = s->hash_size; - p = &s->head[n]; - do { - m = *--p; - *p = (Pos)(m >= wsize ? m - wsize : NIL); - } while (--n); - n = wsize; -#ifndef FASTEST - p = &s->prev[n]; - do { - m = *--p; - *p = (Pos)(m >= wsize ? m - wsize : NIL); - /* If n is not on any hash chain, prev[n] is garbage but - * its value will never be used. - */ - } while (--n); -#endif -} - -/* =========================================================================== - * Read a new buffer from the current input stream, update the adler32 - * and total number of bytes read. All deflate() input goes through - * this function so some applications may wish to modify it to avoid - * allocating a large strm->next_in buffer and copying from it. - * (See also flush_pending()). - */ -local unsigned read_buf(z_streamp strm, Bytef *buf, unsigned size) { - unsigned len = strm->avail_in; - - if (len > size) len = size; - if (len == 0) return 0; - - strm->avail_in -= len; - - zmemcpy(buf, strm->next_in, len); - if (strm->state->wrap == 1) { - strm->adler = adler32(strm->adler, buf, len); - } -#ifdef GZIP - else if (strm->state->wrap == 2) { - strm->adler = crc32(strm->adler, buf, len); - } -#endif - strm->next_in += len; - strm->total_in += len; - - return len; -} - -/* =========================================================================== - * Fill the window when the lookahead becomes insufficient. - * Updates strstart and lookahead. - * - * IN assertion: lookahead < MIN_LOOKAHEAD - * OUT assertions: strstart <= window_size-MIN_LOOKAHEAD - * At least one byte has been read, or avail_in == 0; reads are - * performed for at least two bytes (required for the zip translate_eol - * option -- not supported here). - */ -local void fill_window(deflate_state *s) { - unsigned n; - unsigned more; /* Amount of free space at the end of the window. */ - uInt wsize = s->w_size; - - Assert(s->lookahead < MIN_LOOKAHEAD, "already enough lookahead"); - - do { - more = (unsigned)(s->window_size -(ulg)s->lookahead -(ulg)s->strstart); - - /* Deal with !@#$% 64K limit: */ - if (sizeof(int) <= 2) { - if (more == 0 && s->strstart == 0 && s->lookahead == 0) { - more = wsize; - - } else if (more == (unsigned)(-1)) { - /* Very unlikely, but possible on 16 bit machine if - * strstart == 0 && lookahead == 1 (input done a byte at time) - */ - more--; - } - } - - /* If the window is almost full and there is insufficient lookahead, - * move the upper half to the lower one to make room in the upper half. - */ - if (s->strstart >= wsize + MAX_DIST(s)) { - - zmemcpy(s->window, s->window + wsize, (unsigned)wsize - more); - s->match_start -= wsize; - s->strstart -= wsize; /* we now have strstart >= MAX_DIST */ - s->block_start -= (long) wsize; - if (s->insert > s->strstart) - s->insert = s->strstart; - slide_hash(s); - more += wsize; - } - if (s->strm->avail_in == 0) break; - - /* If there was no sliding: - * strstart <= WSIZE+MAX_DIST-1 && lookahead <= MIN_LOOKAHEAD - 1 && - * more == window_size - lookahead - strstart - * => more >= window_size - (MIN_LOOKAHEAD-1 + WSIZE + MAX_DIST-1) - * => more >= window_size - 2*WSIZE + 2 - * In the BIG_MEM or MMAP case (not yet supported), - * window_size == input_size + MIN_LOOKAHEAD && - * strstart + s->lookahead <= input_size => more >= MIN_LOOKAHEAD. - * Otherwise, window_size == 2*WSIZE so more >= 2. - * If there was sliding, more >= WSIZE. So in all cases, more >= 2. - */ - Assert(more >= 2, "more < 2"); - - n = read_buf(s->strm, s->window + s->strstart + s->lookahead, more); - s->lookahead += n; - - /* Initialize the hash value now that we have some input: */ - if (s->lookahead + s->insert >= MIN_MATCH) { - uInt str = s->strstart - s->insert; - s->ins_h = s->window[str]; - UPDATE_HASH(s, s->ins_h, s->window[str + 1]); -#if MIN_MATCH != 3 - Call UPDATE_HASH() MIN_MATCH-3 more times -#endif - while (s->insert) { - UPDATE_HASH(s, s->ins_h, s->window[str + MIN_MATCH-1]); -#ifndef FASTEST - s->prev[str & s->w_mask] = s->head[s->ins_h]; -#endif - s->head[s->ins_h] = (Pos)str; - str++; - s->insert--; - if (s->lookahead + s->insert < MIN_MATCH) - break; - } - } - /* If the whole input has less than MIN_MATCH bytes, ins_h is garbage, - * but this is not important since only literal bytes will be emitted. - */ - - } while (s->lookahead < MIN_LOOKAHEAD && s->strm->avail_in != 0); - - /* If the WIN_INIT bytes after the end of the current data have never been - * written, then zero those bytes in order to avoid memory check reports of - * the use of uninitialized (or uninitialised as Julian writes) bytes by - * the longest match routines. Update the high water mark for the next - * time through here. WIN_INIT is set to MAX_MATCH since the longest match - * routines allow scanning to strstart + MAX_MATCH, ignoring lookahead. - */ - if (s->high_water < s->window_size) { - ulg curr = s->strstart + (ulg)(s->lookahead); - ulg init; - - if (s->high_water < curr) { - /* Previous high water mark below current data -- zero WIN_INIT - * bytes or up to end of window, whichever is less. - */ - init = s->window_size - curr; - if (init > WIN_INIT) - init = WIN_INIT; - zmemzero(s->window + curr, (unsigned)init); - s->high_water = curr + init; - } - else if (s->high_water < (ulg)curr + WIN_INIT) { - /* High water mark at or above current data, but below current data - * plus WIN_INIT -- zero out to current data plus WIN_INIT, or up - * to end of window, whichever is less. - */ - init = (ulg)curr + WIN_INIT - s->high_water; - if (init > s->window_size - s->high_water) - init = s->window_size - s->high_water; - zmemzero(s->window + s->high_water, (unsigned)init); - s->high_water += init; - } - } - - Assert((ulg)s->strstart <= s->window_size - MIN_LOOKAHEAD, - "not enough room for search"); -} - -/* ========================================================================= */ -int ZEXPORT deflateInit_(z_streamp strm, int level, const char *version, - int stream_size) { - return deflateInit2_(strm, level, Z_DEFLATED, MAX_WBITS, DEF_MEM_LEVEL, - Z_DEFAULT_STRATEGY, version, stream_size); - /* To do: ignore strm->next_in if we use it as window */ -} - -/* ========================================================================= */ -int ZEXPORT deflateInit2_(z_streamp strm, int level, int method, - int windowBits, int memLevel, int strategy, - const char *version, int stream_size) { - deflate_state *s; - int wrap = 1; - static const char my_version[] = ZLIB_VERSION; - - if (version == Z_NULL || version[0] != my_version[0] || - stream_size != sizeof(z_stream)) { - return Z_VERSION_ERROR; - } - if (strm == Z_NULL) return Z_STREAM_ERROR; - - strm->msg = Z_NULL; - if (strm->zalloc == (alloc_func)0) { -#ifdef Z_SOLO - return Z_STREAM_ERROR; -#else - strm->zalloc = zcalloc; - strm->opaque = (voidpf)0; -#endif - } - if (strm->zfree == (free_func)0) -#ifdef Z_SOLO - return Z_STREAM_ERROR; -#else - strm->zfree = zcfree; -#endif - -#ifdef FASTEST - if (level != 0) level = 1; -#else - if (level == Z_DEFAULT_COMPRESSION) level = 6; -#endif - - if (windowBits < 0) { /* suppress zlib wrapper */ - wrap = 0; - if (windowBits < -15) - return Z_STREAM_ERROR; - windowBits = -windowBits; - } -#ifdef GZIP - else if (windowBits > 15) { - wrap = 2; /* write gzip wrapper instead */ - windowBits -= 16; - } -#endif - if (memLevel < 1 || memLevel > MAX_MEM_LEVEL || method != Z_DEFLATED || - windowBits < 8 || windowBits > 15 || level < 0 || level > 9 || - strategy < 0 || strategy > Z_FIXED || (windowBits == 8 && wrap != 1)) { - return Z_STREAM_ERROR; - } - if (windowBits == 8) windowBits = 9; /* until 256-byte window bug fixed */ - s = (deflate_state *) ZALLOC(strm, 1, sizeof(deflate_state)); - if (s == Z_NULL) return Z_MEM_ERROR; - strm->state = (struct internal_state FAR *)s; - s->strm = strm; - s->status = INIT_STATE; /* to pass state test in deflateReset() */ - - s->wrap = wrap; - s->gzhead = Z_NULL; - s->w_bits = (uInt)windowBits; - s->w_size = 1 << s->w_bits; - s->w_mask = s->w_size - 1; - - s->hash_bits = (uInt)memLevel + 7; - s->hash_size = 1 << s->hash_bits; - s->hash_mask = s->hash_size - 1; - s->hash_shift = ((s->hash_bits + MIN_MATCH-1) / MIN_MATCH); - - s->window = (Bytef *) ZALLOC(strm, s->w_size, 2*sizeof(Byte)); - s->prev = (Posf *) ZALLOC(strm, s->w_size, sizeof(Pos)); - s->head = (Posf *) ZALLOC(strm, s->hash_size, sizeof(Pos)); - - s->high_water = 0; /* nothing written to s->window yet */ - - s->lit_bufsize = 1 << (memLevel + 6); /* 16K elements by default */ - - /* We overlay pending_buf and sym_buf. This works since the average size - * for length/distance pairs over any compressed block is assured to be 31 - * bits or less. - * - * Analysis: The longest fixed codes are a length code of 8 bits plus 5 - * extra bits, for lengths 131 to 257. The longest fixed distance codes are - * 5 bits plus 13 extra bits, for distances 16385 to 32768. The longest - * possible fixed-codes length/distance pair is then 31 bits total. - * - * sym_buf starts one-fourth of the way into pending_buf. So there are - * three bytes in sym_buf for every four bytes in pending_buf. Each symbol - * in sym_buf is three bytes -- two for the distance and one for the - * literal/length. As each symbol is consumed, the pointer to the next - * sym_buf value to read moves forward three bytes. From that symbol, up to - * 31 bits are written to pending_buf. The closest the written pending_buf - * bits gets to the next sym_buf symbol to read is just before the last - * code is written. At that time, 31*(n - 2) bits have been written, just - * after 24*(n - 2) bits have been consumed from sym_buf. sym_buf starts at - * 8*n bits into pending_buf. (Note that the symbol buffer fills when n - 1 - * symbols are written.) The closest the writing gets to what is unread is - * then n + 14 bits. Here n is lit_bufsize, which is 16384 by default, and - * can range from 128 to 32768. - * - * Therefore, at a minimum, there are 142 bits of space between what is - * written and what is read in the overlain buffers, so the symbols cannot - * be overwritten by the compressed data. That space is actually 139 bits, - * due to the three-bit fixed-code block header. - * - * That covers the case where either Z_FIXED is specified, forcing fixed - * codes, or when the use of fixed codes is chosen, because that choice - * results in a smaller compressed block than dynamic codes. That latter - * condition then assures that the above analysis also covers all dynamic - * blocks. A dynamic-code block will only be chosen to be emitted if it has - * fewer bits than a fixed-code block would for the same set of symbols. - * Therefore its average symbol length is assured to be less than 31. So - * the compressed data for a dynamic block also cannot overwrite the - * symbols from which it is being constructed. - */ - - s->pending_buf = (uchf *) ZALLOC(strm, s->lit_bufsize, LIT_BUFS); - s->pending_buf_size = (ulg)s->lit_bufsize * 4; - - if (s->window == Z_NULL || s->prev == Z_NULL || s->head == Z_NULL || - s->pending_buf == Z_NULL) { - s->status = FINISH_STATE; - strm->msg = ERR_MSG(Z_MEM_ERROR); - deflateEnd (strm); - return Z_MEM_ERROR; - } -#ifdef LIT_MEM - s->d_buf = (ushf *)(s->pending_buf + (s->lit_bufsize << 1)); - s->l_buf = s->pending_buf + (s->lit_bufsize << 2); - s->sym_end = s->lit_bufsize - 1; -#else - s->sym_buf = s->pending_buf + s->lit_bufsize; - s->sym_end = (s->lit_bufsize - 1) * 3; -#endif - /* We avoid equality with lit_bufsize*3 because of wraparound at 64K - * on 16 bit machines and because stored blocks are restricted to - * 64K-1 bytes. - */ - - s->level = level; - s->strategy = strategy; - s->method = (Byte)method; - - return deflateReset(strm); -} - -/* ========================================================================= - * Check for a valid deflate stream state. Return 0 if ok, 1 if not. - */ -local int deflateStateCheck(z_streamp strm) { - deflate_state *s; - if (strm == Z_NULL || - strm->zalloc == (alloc_func)0 || strm->zfree == (free_func)0) - return 1; - s = strm->state; - if (s == Z_NULL || s->strm != strm || (s->status != INIT_STATE && -#ifdef GZIP - s->status != GZIP_STATE && -#endif - s->status != EXTRA_STATE && - s->status != NAME_STATE && - s->status != COMMENT_STATE && - s->status != HCRC_STATE && - s->status != BUSY_STATE && - s->status != FINISH_STATE)) - return 1; - return 0; -} - -/* ========================================================================= */ -int ZEXPORT deflateSetDictionary(z_streamp strm, const Bytef *dictionary, - uInt dictLength) { - deflate_state *s; - uInt str, n; - int wrap; - unsigned avail; - z_const unsigned char *next; - - if (deflateStateCheck(strm) || dictionary == Z_NULL) - return Z_STREAM_ERROR; - s = strm->state; - wrap = s->wrap; - if (wrap == 2 || (wrap == 1 && s->status != INIT_STATE) || s->lookahead) - return Z_STREAM_ERROR; - - /* when using zlib wrappers, compute Adler-32 for provided dictionary */ - if (wrap == 1) - strm->adler = adler32(strm->adler, dictionary, dictLength); - s->wrap = 0; /* avoid computing Adler-32 in read_buf */ - - /* if dictionary would fill window, just replace the history */ - if (dictLength >= s->w_size) { - if (wrap == 0) { /* already empty otherwise */ - CLEAR_HASH(s); - s->strstart = 0; - s->block_start = 0L; - s->insert = 0; - } - dictionary += dictLength - s->w_size; /* use the tail */ - dictLength = s->w_size; - } - - /* insert dictionary into window and hash */ - avail = strm->avail_in; - next = strm->next_in; - strm->avail_in = dictLength; - strm->next_in = (z_const Bytef *)dictionary; - fill_window(s); - while (s->lookahead >= MIN_MATCH) { - str = s->strstart; - n = s->lookahead - (MIN_MATCH-1); - do { - UPDATE_HASH(s, s->ins_h, s->window[str + MIN_MATCH-1]); -#ifndef FASTEST - s->prev[str & s->w_mask] = s->head[s->ins_h]; -#endif - s->head[s->ins_h] = (Pos)str; - str++; - } while (--n); - s->strstart = str; - s->lookahead = MIN_MATCH-1; - fill_window(s); - } - s->strstart += s->lookahead; - s->block_start = (long)s->strstart; - s->insert = s->lookahead; - s->lookahead = 0; - s->match_length = s->prev_length = MIN_MATCH-1; - s->match_available = 0; - strm->next_in = next; - strm->avail_in = avail; - s->wrap = wrap; - return Z_OK; -} - -/* ========================================================================= */ -int ZEXPORT deflateGetDictionary(z_streamp strm, Bytef *dictionary, - uInt *dictLength) { - deflate_state *s; - uInt len; - - if (deflateStateCheck(strm)) - return Z_STREAM_ERROR; - s = strm->state; - len = s->strstart + s->lookahead; - if (len > s->w_size) - len = s->w_size; - if (dictionary != Z_NULL && len) - zmemcpy(dictionary, s->window + s->strstart + s->lookahead - len, len); - if (dictLength != Z_NULL) - *dictLength = len; - return Z_OK; -} - -/* ========================================================================= */ -int ZEXPORT deflateResetKeep(z_streamp strm) { - deflate_state *s; - - if (deflateStateCheck(strm)) { - return Z_STREAM_ERROR; - } - - strm->total_in = strm->total_out = 0; - strm->msg = Z_NULL; /* use zfree if we ever allocate msg dynamically */ - strm->data_type = Z_UNKNOWN; - - s = (deflate_state *)strm->state; - s->pending = 0; - s->pending_out = s->pending_buf; - - if (s->wrap < 0) { - s->wrap = -s->wrap; /* was made negative by deflate(..., Z_FINISH); */ - } - s->status = -#ifdef GZIP - s->wrap == 2 ? GZIP_STATE : -#endif - INIT_STATE; - strm->adler = -#ifdef GZIP - s->wrap == 2 ? crc32(0L, Z_NULL, 0) : -#endif - adler32(0L, Z_NULL, 0); - s->last_flush = -2; - - _tr_init(s); - - return Z_OK; -} - -/* =========================================================================== - * Initialize the "longest match" routines for a new zlib stream - */ -local void lm_init(deflate_state *s) { - s->window_size = (ulg)2L*s->w_size; - - CLEAR_HASH(s); - - /* Set the default configuration parameters: - */ - s->max_lazy_match = configuration_table[s->level].max_lazy; - s->good_match = configuration_table[s->level].good_length; - s->nice_match = configuration_table[s->level].nice_length; - s->max_chain_length = configuration_table[s->level].max_chain; - - s->strstart = 0; - s->block_start = 0L; - s->lookahead = 0; - s->insert = 0; - s->match_length = s->prev_length = MIN_MATCH-1; - s->match_available = 0; - s->ins_h = 0; -} - -/* ========================================================================= */ -int ZEXPORT deflateReset(z_streamp strm) { - int ret; - - ret = deflateResetKeep(strm); - if (ret == Z_OK) - lm_init(strm->state); - return ret; -} - -/* ========================================================================= */ -int ZEXPORT deflateSetHeader(z_streamp strm, gz_headerp head) { - if (deflateStateCheck(strm) || strm->state->wrap != 2) - return Z_STREAM_ERROR; - strm->state->gzhead = head; - return Z_OK; -} - -/* ========================================================================= */ -int ZEXPORT deflatePending(z_streamp strm, unsigned *pending, int *bits) { - if (deflateStateCheck(strm)) return Z_STREAM_ERROR; - if (pending != Z_NULL) - *pending = strm->state->pending; - if (bits != Z_NULL) - *bits = strm->state->bi_valid; - return Z_OK; -} - -/* ========================================================================= */ -int ZEXPORT deflatePrime(z_streamp strm, int bits, int value) { - deflate_state *s; - int put; - - if (deflateStateCheck(strm)) return Z_STREAM_ERROR; - s = strm->state; -#ifdef LIT_MEM - if (bits < 0 || bits > 16 || - (uchf *)s->d_buf < s->pending_out + ((Buf_size + 7) >> 3)) - return Z_BUF_ERROR; -#else - if (bits < 0 || bits > 16 || - s->sym_buf < s->pending_out + ((Buf_size + 7) >> 3)) - return Z_BUF_ERROR; -#endif - do { - put = Buf_size - s->bi_valid; - if (put > bits) - put = bits; - s->bi_buf |= (ush)((value & ((1 << put) - 1)) << s->bi_valid); - s->bi_valid += put; - _tr_flush_bits(s); - value >>= put; - bits -= put; - } while (bits); - return Z_OK; -} - -/* ========================================================================= */ -int ZEXPORT deflateParams(z_streamp strm, int level, int strategy) { - deflate_state *s; - compress_func func; - - if (deflateStateCheck(strm)) return Z_STREAM_ERROR; - s = strm->state; - -#ifdef FASTEST - if (level != 0) level = 1; -#else - if (level == Z_DEFAULT_COMPRESSION) level = 6; -#endif - if (level < 0 || level > 9 || strategy < 0 || strategy > Z_FIXED) { - return Z_STREAM_ERROR; - } - func = configuration_table[s->level].func; - - if ((strategy != s->strategy || func != configuration_table[level].func) && - s->last_flush != -2) { - /* Flush the last buffer: */ - int err = deflate(strm, Z_BLOCK); - if (err == Z_STREAM_ERROR) - return err; - if (strm->avail_in || (s->strstart - s->block_start) + s->lookahead) - return Z_BUF_ERROR; - } - if (s->level != level) { - if (s->level == 0 && s->matches != 0) { - if (s->matches == 1) - slide_hash(s); - else - CLEAR_HASH(s); - s->matches = 0; - } - s->level = level; - s->max_lazy_match = configuration_table[level].max_lazy; - s->good_match = configuration_table[level].good_length; - s->nice_match = configuration_table[level].nice_length; - s->max_chain_length = configuration_table[level].max_chain; - } - s->strategy = strategy; - return Z_OK; -} - -/* ========================================================================= */ -int ZEXPORT deflateTune(z_streamp strm, int good_length, int max_lazy, - int nice_length, int max_chain) { - deflate_state *s; - - if (deflateStateCheck(strm)) return Z_STREAM_ERROR; - s = strm->state; - s->good_match = (uInt)good_length; - s->max_lazy_match = (uInt)max_lazy; - s->nice_match = nice_length; - s->max_chain_length = (uInt)max_chain; - return Z_OK; -} - -/* ========================================================================= - * For the default windowBits of 15 and memLevel of 8, this function returns a - * close to exact, as well as small, upper bound on the compressed size. This - * is an expansion of ~0.03%, plus a small constant. - * - * For any setting other than those defaults for windowBits and memLevel, one - * of two worst case bounds is returned. This is at most an expansion of ~4% or - * ~13%, plus a small constant. - * - * Both the 0.03% and 4% derive from the overhead of stored blocks. The first - * one is for stored blocks of 16383 bytes (memLevel == 8), whereas the second - * is for stored blocks of 127 bytes (the worst case memLevel == 1). The - * expansion results from five bytes of header for each stored block. - * - * The larger expansion of 13% results from a window size less than or equal to - * the symbols buffer size (windowBits <= memLevel + 7). In that case some of - * the data being compressed may have slid out of the sliding window, impeding - * a stored block from being emitted. Then the only choice is a fixed or - * dynamic block, where a fixed block limits the maximum expansion to 9 bits - * per 8-bit byte, plus 10 bits for every block. The smallest block size for - * which this can occur is 255 (memLevel == 2). - * - * Shifts are used to approximate divisions, for speed. - */ -uLong ZEXPORT deflateBound(z_streamp strm, uLong sourceLen) { - deflate_state *s; - uLong fixedlen, storelen, wraplen; - - /* upper bound for fixed blocks with 9-bit literals and length 255 - (memLevel == 2, which is the lowest that may not use stored blocks) -- - ~13% overhead plus a small constant */ - fixedlen = sourceLen + (sourceLen >> 3) + (sourceLen >> 8) + - (sourceLen >> 9) + 4; - - /* upper bound for stored blocks with length 127 (memLevel == 1) -- - ~4% overhead plus a small constant */ - storelen = sourceLen + (sourceLen >> 5) + (sourceLen >> 7) + - (sourceLen >> 11) + 7; - - /* if can't get parameters, return larger bound plus a zlib wrapper */ - if (deflateStateCheck(strm)) - return (fixedlen > storelen ? fixedlen : storelen) + 6; - - /* compute wrapper length */ - s = strm->state; - switch (s->wrap) { - case 0: /* raw deflate */ - wraplen = 0; - break; - case 1: /* zlib wrapper */ - wraplen = 6 + (s->strstart ? 4 : 0); - break; -#ifdef GZIP - case 2: /* gzip wrapper */ - wraplen = 18; - if (s->gzhead != Z_NULL) { /* user-supplied gzip header */ - Bytef *str; - if (s->gzhead->extra != Z_NULL) - wraplen += 2 + s->gzhead->extra_len; - str = s->gzhead->name; - if (str != Z_NULL) - do { - wraplen++; - } while (*str++); - str = s->gzhead->comment; - if (str != Z_NULL) - do { - wraplen++; - } while (*str++); - if (s->gzhead->hcrc) - wraplen += 2; - } - break; -#endif - default: /* for compiler happiness */ - wraplen = 6; - } - - /* if not default parameters, return one of the conservative bounds */ - if (s->w_bits != 15 || s->hash_bits != 8 + 7) - return (s->w_bits <= s->hash_bits && s->level ? fixedlen : storelen) + - wraplen; - - /* default settings: return tight bound for that case -- ~0.03% overhead - plus a small constant */ - return sourceLen + (sourceLen >> 12) + (sourceLen >> 14) + - (sourceLen >> 25) + 13 - 6 + wraplen; -} - -/* ========================================================================= - * Put a short in the pending buffer. The 16-bit value is put in MSB order. - * IN assertion: the stream state is correct and there is enough room in - * pending_buf. - */ -local void putShortMSB(deflate_state *s, uInt b) { - put_byte(s, (Byte)(b >> 8)); - put_byte(s, (Byte)(b & 0xff)); -} - -/* ========================================================================= - * Flush as much pending output as possible. All deflate() output, except for - * some deflate_stored() output, goes through this function so some - * applications may wish to modify it to avoid allocating a large - * strm->next_out buffer and copying into it. (See also read_buf()). - */ -local void flush_pending(z_streamp strm) { - unsigned len; - deflate_state *s = strm->state; - - _tr_flush_bits(s); - len = s->pending; - if (len > strm->avail_out) len = strm->avail_out; - if (len == 0) return; - - zmemcpy(strm->next_out, s->pending_out, len); - strm->next_out += len; - s->pending_out += len; - strm->total_out += len; - strm->avail_out -= len; - s->pending -= len; - if (s->pending == 0) { - s->pending_out = s->pending_buf; - } -} - -/* =========================================================================== - * Update the header CRC with the bytes s->pending_buf[beg..s->pending - 1]. - */ -#define HCRC_UPDATE(beg) \ - do { \ - if (s->gzhead->hcrc && s->pending > (beg)) \ - strm->adler = crc32(strm->adler, s->pending_buf + (beg), \ - s->pending - (beg)); \ - } while (0) - -/* ========================================================================= */ -int ZEXPORT deflate(z_streamp strm, int flush) { - int old_flush; /* value of flush param for previous deflate call */ - deflate_state *s; - - if (deflateStateCheck(strm) || flush > Z_BLOCK || flush < 0) { - return Z_STREAM_ERROR; - } - s = strm->state; - - if (strm->next_out == Z_NULL || - (strm->avail_in != 0 && strm->next_in == Z_NULL) || - (s->status == FINISH_STATE && flush != Z_FINISH)) { - ERR_RETURN(strm, Z_STREAM_ERROR); - } - if (strm->avail_out == 0) ERR_RETURN(strm, Z_BUF_ERROR); - - old_flush = s->last_flush; - s->last_flush = flush; - - /* Flush as much pending output as possible */ - if (s->pending != 0) { - flush_pending(strm); - if (strm->avail_out == 0) { - /* Since avail_out is 0, deflate will be called again with - * more output space, but possibly with both pending and - * avail_in equal to zero. There won't be anything to do, - * but this is not an error situation so make sure we - * return OK instead of BUF_ERROR at next call of deflate: - */ - s->last_flush = -1; - return Z_OK; - } - - /* Make sure there is something to do and avoid duplicate consecutive - * flushes. For repeated and useless calls with Z_FINISH, we keep - * returning Z_STREAM_END instead of Z_BUF_ERROR. - */ - } else if (strm->avail_in == 0 && RANK(flush) <= RANK(old_flush) && - flush != Z_FINISH) { - ERR_RETURN(strm, Z_BUF_ERROR); - } - - /* User must not provide more input after the first FINISH: */ - if (s->status == FINISH_STATE && strm->avail_in != 0) { - ERR_RETURN(strm, Z_BUF_ERROR); - } - - /* Write the header */ - if (s->status == INIT_STATE && s->wrap == 0) - s->status = BUSY_STATE; - if (s->status == INIT_STATE) { - /* zlib header */ - uInt header = (Z_DEFLATED + ((s->w_bits - 8) << 4)) << 8; - uInt level_flags; - - if (s->strategy >= Z_HUFFMAN_ONLY || s->level < 2) - level_flags = 0; - else if (s->level < 6) - level_flags = 1; - else if (s->level == 6) - level_flags = 2; - else - level_flags = 3; - header |= (level_flags << 6); - if (s->strstart != 0) header |= PRESET_DICT; - header += 31 - (header % 31); - - putShortMSB(s, header); - - /* Save the adler32 of the preset dictionary: */ - if (s->strstart != 0) { - putShortMSB(s, (uInt)(strm->adler >> 16)); - putShortMSB(s, (uInt)(strm->adler & 0xffff)); - } - strm->adler = adler32(0L, Z_NULL, 0); - s->status = BUSY_STATE; - - /* Compression must start with an empty pending buffer */ - flush_pending(strm); - if (s->pending != 0) { - s->last_flush = -1; - return Z_OK; - } - } -#ifdef GZIP - if (s->status == GZIP_STATE) { - /* gzip header */ - strm->adler = crc32(0L, Z_NULL, 0); - put_byte(s, 31); - put_byte(s, 139); - put_byte(s, 8); - if (s->gzhead == Z_NULL) { - put_byte(s, 0); - put_byte(s, 0); - put_byte(s, 0); - put_byte(s, 0); - put_byte(s, 0); - put_byte(s, s->level == 9 ? 2 : - (s->strategy >= Z_HUFFMAN_ONLY || s->level < 2 ? - 4 : 0)); - put_byte(s, OS_CODE); - s->status = BUSY_STATE; - - /* Compression must start with an empty pending buffer */ - flush_pending(strm); - if (s->pending != 0) { - s->last_flush = -1; - return Z_OK; - } - } - else { - put_byte(s, (s->gzhead->text ? 1 : 0) + - (s->gzhead->hcrc ? 2 : 0) + - (s->gzhead->extra == Z_NULL ? 0 : 4) + - (s->gzhead->name == Z_NULL ? 0 : 8) + - (s->gzhead->comment == Z_NULL ? 0 : 16) - ); - put_byte(s, (Byte)(s->gzhead->time & 0xff)); - put_byte(s, (Byte)((s->gzhead->time >> 8) & 0xff)); - put_byte(s, (Byte)((s->gzhead->time >> 16) & 0xff)); - put_byte(s, (Byte)((s->gzhead->time >> 24) & 0xff)); - put_byte(s, s->level == 9 ? 2 : - (s->strategy >= Z_HUFFMAN_ONLY || s->level < 2 ? - 4 : 0)); - put_byte(s, s->gzhead->os & 0xff); - if (s->gzhead->extra != Z_NULL) { - put_byte(s, s->gzhead->extra_len & 0xff); - put_byte(s, (s->gzhead->extra_len >> 8) & 0xff); - } - if (s->gzhead->hcrc) - strm->adler = crc32(strm->adler, s->pending_buf, - s->pending); - s->gzindex = 0; - s->status = EXTRA_STATE; - } - } - if (s->status == EXTRA_STATE) { - if (s->gzhead->extra != Z_NULL) { - ulg beg = s->pending; /* start of bytes to update crc */ - uInt left = (s->gzhead->extra_len & 0xffff) - s->gzindex; - while (s->pending + left > s->pending_buf_size) { - uInt copy = s->pending_buf_size - s->pending; - zmemcpy(s->pending_buf + s->pending, - s->gzhead->extra + s->gzindex, copy); - s->pending = s->pending_buf_size; - HCRC_UPDATE(beg); - s->gzindex += copy; - flush_pending(strm); - if (s->pending != 0) { - s->last_flush = -1; - return Z_OK; - } - beg = 0; - left -= copy; - } - zmemcpy(s->pending_buf + s->pending, - s->gzhead->extra + s->gzindex, left); - s->pending += left; - HCRC_UPDATE(beg); - s->gzindex = 0; - } - s->status = NAME_STATE; - } - if (s->status == NAME_STATE) { - if (s->gzhead->name != Z_NULL) { - ulg beg = s->pending; /* start of bytes to update crc */ - int val; - do { - if (s->pending == s->pending_buf_size) { - HCRC_UPDATE(beg); - flush_pending(strm); - if (s->pending != 0) { - s->last_flush = -1; - return Z_OK; - } - beg = 0; - } - val = s->gzhead->name[s->gzindex++]; - put_byte(s, val); - } while (val != 0); - HCRC_UPDATE(beg); - s->gzindex = 0; - } - s->status = COMMENT_STATE; - } - if (s->status == COMMENT_STATE) { - if (s->gzhead->comment != Z_NULL) { - ulg beg = s->pending; /* start of bytes to update crc */ - int val; - do { - if (s->pending == s->pending_buf_size) { - HCRC_UPDATE(beg); - flush_pending(strm); - if (s->pending != 0) { - s->last_flush = -1; - return Z_OK; - } - beg = 0; - } - val = s->gzhead->comment[s->gzindex++]; - put_byte(s, val); - } while (val != 0); - HCRC_UPDATE(beg); - } - s->status = HCRC_STATE; - } - if (s->status == HCRC_STATE) { - if (s->gzhead->hcrc) { - if (s->pending + 2 > s->pending_buf_size) { - flush_pending(strm); - if (s->pending != 0) { - s->last_flush = -1; - return Z_OK; - } - } - put_byte(s, (Byte)(strm->adler & 0xff)); - put_byte(s, (Byte)((strm->adler >> 8) & 0xff)); - strm->adler = crc32(0L, Z_NULL, 0); - } - s->status = BUSY_STATE; - - /* Compression must start with an empty pending buffer */ - flush_pending(strm); - if (s->pending != 0) { - s->last_flush = -1; - return Z_OK; - } - } -#endif - - /* Start a new block or continue the current one. - */ - if (strm->avail_in != 0 || s->lookahead != 0 || - (flush != Z_NO_FLUSH && s->status != FINISH_STATE)) { - block_state bstate; - - bstate = s->level == 0 ? deflate_stored(s, flush) : - s->strategy == Z_HUFFMAN_ONLY ? deflate_huff(s, flush) : - s->strategy == Z_RLE ? deflate_rle(s, flush) : - (*(configuration_table[s->level].func))(s, flush); - - if (bstate == finish_started || bstate == finish_done) { - s->status = FINISH_STATE; - } - if (bstate == need_more || bstate == finish_started) { - if (strm->avail_out == 0) { - s->last_flush = -1; /* avoid BUF_ERROR next call, see above */ - } - return Z_OK; - /* If flush != Z_NO_FLUSH && avail_out == 0, the next call - * of deflate should use the same flush parameter to make sure - * that the flush is complete. So we don't have to output an - * empty block here, this will be done at next call. This also - * ensures that for a very small output buffer, we emit at most - * one empty block. - */ - } - if (bstate == block_done) { - if (flush == Z_PARTIAL_FLUSH) { - _tr_align(s); - } else if (flush != Z_BLOCK) { /* FULL_FLUSH or SYNC_FLUSH */ - _tr_stored_block(s, (char*)0, 0L, 0); - /* For a full flush, this empty block will be recognized - * as a special marker by inflate_sync(). - */ - if (flush == Z_FULL_FLUSH) { - CLEAR_HASH(s); /* forget history */ - if (s->lookahead == 0) { - s->strstart = 0; - s->block_start = 0L; - s->insert = 0; - } - } - } - flush_pending(strm); - if (strm->avail_out == 0) { - s->last_flush = -1; /* avoid BUF_ERROR at next call, see above */ - return Z_OK; - } - } - } - - if (flush != Z_FINISH) return Z_OK; - if (s->wrap <= 0) return Z_STREAM_END; - - /* Write the trailer */ -#ifdef GZIP - if (s->wrap == 2) { - put_byte(s, (Byte)(strm->adler & 0xff)); - put_byte(s, (Byte)((strm->adler >> 8) & 0xff)); - put_byte(s, (Byte)((strm->adler >> 16) & 0xff)); - put_byte(s, (Byte)((strm->adler >> 24) & 0xff)); - put_byte(s, (Byte)(strm->total_in & 0xff)); - put_byte(s, (Byte)((strm->total_in >> 8) & 0xff)); - put_byte(s, (Byte)((strm->total_in >> 16) & 0xff)); - put_byte(s, (Byte)((strm->total_in >> 24) & 0xff)); - } - else -#endif - { - putShortMSB(s, (uInt)(strm->adler >> 16)); - putShortMSB(s, (uInt)(strm->adler & 0xffff)); - } - flush_pending(strm); - /* If avail_out is zero, the application will call deflate again - * to flush the rest. - */ - if (s->wrap > 0) s->wrap = -s->wrap; /* write the trailer only once! */ - return s->pending != 0 ? Z_OK : Z_STREAM_END; -} - -/* ========================================================================= */ -int ZEXPORT deflateEnd(z_streamp strm) { - int status; - - if (deflateStateCheck(strm)) return Z_STREAM_ERROR; - - status = strm->state->status; - - /* Deallocate in reverse order of allocations: */ - TRY_FREE(strm, strm->state->pending_buf); - TRY_FREE(strm, strm->state->head); - TRY_FREE(strm, strm->state->prev); - TRY_FREE(strm, strm->state->window); - - ZFREE(strm, strm->state); - strm->state = Z_NULL; - - return status == BUSY_STATE ? Z_DATA_ERROR : Z_OK; -} - -/* ========================================================================= - * Copy the source state to the destination state. - * To simplify the source, this is not supported for 16-bit MSDOS (which - * doesn't have enough memory anyway to duplicate compression states). - */ -int ZEXPORT deflateCopy(z_streamp dest, z_streamp source) { -#ifdef MAXSEG_64K - (void)dest; - (void)source; - return Z_STREAM_ERROR; -#else - deflate_state *ds; - deflate_state *ss; - - - if (deflateStateCheck(source) || dest == Z_NULL) { - return Z_STREAM_ERROR; - } - - ss = source->state; - - zmemcpy((voidpf)dest, (voidpf)source, sizeof(z_stream)); - - ds = (deflate_state *) ZALLOC(dest, 1, sizeof(deflate_state)); - if (ds == Z_NULL) return Z_MEM_ERROR; - dest->state = (struct internal_state FAR *) ds; - zmemcpy((voidpf)ds, (voidpf)ss, sizeof(deflate_state)); - ds->strm = dest; - - ds->window = (Bytef *) ZALLOC(dest, ds->w_size, 2*sizeof(Byte)); - ds->prev = (Posf *) ZALLOC(dest, ds->w_size, sizeof(Pos)); - ds->head = (Posf *) ZALLOC(dest, ds->hash_size, sizeof(Pos)); - ds->pending_buf = (uchf *) ZALLOC(dest, ds->lit_bufsize, LIT_BUFS); - - if (ds->window == Z_NULL || ds->prev == Z_NULL || ds->head == Z_NULL || - ds->pending_buf == Z_NULL) { - deflateEnd (dest); - return Z_MEM_ERROR; - } - /* following zmemcpy do not work for 16-bit MSDOS */ - zmemcpy(ds->window, ss->window, ds->w_size * 2 * sizeof(Byte)); - zmemcpy((voidpf)ds->prev, (voidpf)ss->prev, ds->w_size * sizeof(Pos)); - zmemcpy((voidpf)ds->head, (voidpf)ss->head, ds->hash_size * sizeof(Pos)); - zmemcpy(ds->pending_buf, ss->pending_buf, ds->lit_bufsize * LIT_BUFS); - - ds->pending_out = ds->pending_buf + (ss->pending_out - ss->pending_buf); -#ifdef LIT_MEM - ds->d_buf = (ushf *)(ds->pending_buf + (ds->lit_bufsize << 1)); - ds->l_buf = ds->pending_buf + (ds->lit_bufsize << 2); -#else - ds->sym_buf = ds->pending_buf + ds->lit_bufsize; -#endif - - ds->l_desc.dyn_tree = ds->dyn_ltree; - ds->d_desc.dyn_tree = ds->dyn_dtree; - ds->bl_desc.dyn_tree = ds->bl_tree; - - return Z_OK; -#endif /* MAXSEG_64K */ -} - -#ifndef FASTEST -/* =========================================================================== - * Set match_start to the longest match starting at the given string and - * return its length. Matches shorter or equal to prev_length are discarded, - * in which case the result is equal to prev_length and match_start is - * garbage. - * IN assertions: cur_match is the head of the hash chain for the current - * string (strstart) and its distance is <= MAX_DIST, and prev_length >= 1 - * OUT assertion: the match length is not greater than s->lookahead. - */ -local uInt longest_match(deflate_state *s, IPos cur_match) { - unsigned chain_length = s->max_chain_length;/* max hash chain length */ - register Bytef *scan = s->window + s->strstart; /* current string */ - register Bytef *match; /* matched string */ - register int len; /* length of current match */ - int best_len = (int)s->prev_length; /* best match length so far */ - int nice_match = s->nice_match; /* stop if match long enough */ - IPos limit = s->strstart > (IPos)MAX_DIST(s) ? - s->strstart - (IPos)MAX_DIST(s) : NIL; - /* Stop when cur_match becomes <= limit. To simplify the code, - * we prevent matches with the string of window index 0. - */ - Posf *prev = s->prev; - uInt wmask = s->w_mask; - -#ifdef UNALIGNED_OK - /* Compare two bytes at a time. Note: this is not always beneficial. - * Try with and without -DUNALIGNED_OK to check. - */ - register Bytef *strend = s->window + s->strstart + MAX_MATCH - 1; - register ush scan_start = *(ushf*)scan; - register ush scan_end = *(ushf*)(scan + best_len - 1); -#else - register Bytef *strend = s->window + s->strstart + MAX_MATCH; - register Byte scan_end1 = scan[best_len - 1]; - register Byte scan_end = scan[best_len]; -#endif - - /* The code is optimized for HASH_BITS >= 8 and MAX_MATCH-2 multiple of 16. - * It is easy to get rid of this optimization if necessary. - */ - Assert(s->hash_bits >= 8 && MAX_MATCH == 258, "Code too clever"); - - /* Do not waste too much time if we already have a good match: */ - if (s->prev_length >= s->good_match) { - chain_length >>= 2; - } - /* Do not look for matches beyond the end of the input. This is necessary - * to make deflate deterministic. - */ - if ((uInt)nice_match > s->lookahead) nice_match = (int)s->lookahead; - - Assert((ulg)s->strstart <= s->window_size - MIN_LOOKAHEAD, - "need lookahead"); - - do { - Assert(cur_match < s->strstart, "no future"); - match = s->window + cur_match; - - /* Skip to next match if the match length cannot increase - * or if the match length is less than 2. Note that the checks below - * for insufficient lookahead only occur occasionally for performance - * reasons. Therefore uninitialized memory will be accessed, and - * conditional jumps will be made that depend on those values. - * However the length of the match is limited to the lookahead, so - * the output of deflate is not affected by the uninitialized values. - */ -#if (defined(UNALIGNED_OK) && MAX_MATCH == 258) - /* This code assumes sizeof(unsigned short) == 2. Do not use - * UNALIGNED_OK if your compiler uses a different size. - */ - if (*(ushf*)(match + best_len - 1) != scan_end || - *(ushf*)match != scan_start) continue; - - /* It is not necessary to compare scan[2] and match[2] since they are - * always equal when the other bytes match, given that the hash keys - * are equal and that HASH_BITS >= 8. Compare 2 bytes at a time at - * strstart + 3, + 5, up to strstart + 257. We check for insufficient - * lookahead only every 4th comparison; the 128th check will be made - * at strstart + 257. If MAX_MATCH-2 is not a multiple of 8, it is - * necessary to put more guard bytes at the end of the window, or - * to check more often for insufficient lookahead. - */ - Assert(scan[2] == match[2], "scan[2]?"); - scan++, match++; - do { - } while (*(ushf*)(scan += 2) == *(ushf*)(match += 2) && - *(ushf*)(scan += 2) == *(ushf*)(match += 2) && - *(ushf*)(scan += 2) == *(ushf*)(match += 2) && - *(ushf*)(scan += 2) == *(ushf*)(match += 2) && - scan < strend); - /* The funny "do {}" generates better code on most compilers */ - - /* Here, scan <= window + strstart + 257 */ - Assert(scan <= s->window + (unsigned)(s->window_size - 1), - "wild scan"); - if (*scan == *match) scan++; - - len = (MAX_MATCH - 1) - (int)(strend - scan); - scan = strend - (MAX_MATCH-1); - -#else /* UNALIGNED_OK */ - - if (match[best_len] != scan_end || - match[best_len - 1] != scan_end1 || - *match != *scan || - *++match != scan[1]) continue; - - /* The check at best_len - 1 can be removed because it will be made - * again later. (This heuristic is not always a win.) - * It is not necessary to compare scan[2] and match[2] since they - * are always equal when the other bytes match, given that - * the hash keys are equal and that HASH_BITS >= 8. - */ - scan += 2, match++; - Assert(*scan == *match, "match[2]?"); - - /* We check for insufficient lookahead only every 8th comparison; - * the 256th check will be made at strstart + 258. - */ - do { - } while (*++scan == *++match && *++scan == *++match && - *++scan == *++match && *++scan == *++match && - *++scan == *++match && *++scan == *++match && - *++scan == *++match && *++scan == *++match && - scan < strend); - - Assert(scan <= s->window + (unsigned)(s->window_size - 1), - "wild scan"); - - len = MAX_MATCH - (int)(strend - scan); - scan = strend - MAX_MATCH; - -#endif /* UNALIGNED_OK */ - - if (len > best_len) { - s->match_start = cur_match; - best_len = len; - if (len >= nice_match) break; -#ifdef UNALIGNED_OK - scan_end = *(ushf*)(scan + best_len - 1); -#else - scan_end1 = scan[best_len - 1]; - scan_end = scan[best_len]; -#endif - } - } while ((cur_match = prev[cur_match & wmask]) > limit - && --chain_length != 0); - - if ((uInt)best_len <= s->lookahead) return (uInt)best_len; - return s->lookahead; -} - -#else /* FASTEST */ - -/* --------------------------------------------------------------------------- - * Optimized version for FASTEST only - */ -local uInt longest_match(deflate_state *s, IPos cur_match) { - register Bytef *scan = s->window + s->strstart; /* current string */ - register Bytef *match; /* matched string */ - register int len; /* length of current match */ - register Bytef *strend = s->window + s->strstart + MAX_MATCH; - - /* The code is optimized for HASH_BITS >= 8 and MAX_MATCH-2 multiple of 16. - * It is easy to get rid of this optimization if necessary. - */ - Assert(s->hash_bits >= 8 && MAX_MATCH == 258, "Code too clever"); - - Assert((ulg)s->strstart <= s->window_size - MIN_LOOKAHEAD, - "need lookahead"); - - Assert(cur_match < s->strstart, "no future"); - - match = s->window + cur_match; - - /* Return failure if the match length is less than 2: - */ - if (match[0] != scan[0] || match[1] != scan[1]) return MIN_MATCH-1; - - /* The check at best_len - 1 can be removed because it will be made - * again later. (This heuristic is not always a win.) - * It is not necessary to compare scan[2] and match[2] since they - * are always equal when the other bytes match, given that - * the hash keys are equal and that HASH_BITS >= 8. - */ - scan += 2, match += 2; - Assert(*scan == *match, "match[2]?"); - - /* We check for insufficient lookahead only every 8th comparison; - * the 256th check will be made at strstart + 258. - */ - do { - } while (*++scan == *++match && *++scan == *++match && - *++scan == *++match && *++scan == *++match && - *++scan == *++match && *++scan == *++match && - *++scan == *++match && *++scan == *++match && - scan < strend); - - Assert(scan <= s->window + (unsigned)(s->window_size - 1), "wild scan"); - - len = MAX_MATCH - (int)(strend - scan); - - if (len < MIN_MATCH) return MIN_MATCH - 1; - - s->match_start = cur_match; - return (uInt)len <= s->lookahead ? (uInt)len : s->lookahead; -} - -#endif /* FASTEST */ - -#ifdef ZLIB_DEBUG - -#define EQUAL 0 -/* result of memcmp for equal strings */ - -/* =========================================================================== - * Check that the match at match_start is indeed a match. - */ -local void check_match(deflate_state *s, IPos start, IPos match, int length) { - /* check that the match is indeed a match */ - Bytef *back = s->window + (int)match, *here = s->window + start; - IPos len = length; - if (match == (IPos)-1) { - /* match starts one byte before the current window -- just compare the - subsequent length-1 bytes */ - back++; - here++; - len--; - } - if (zmemcmp(back, here, len) != EQUAL) { - fprintf(stderr, " start %u, match %d, length %d\n", - start, (int)match, length); - do { - fprintf(stderr, "(%02x %02x)", *back++, *here++); - } while (--len != 0); - z_error("invalid match"); - } - if (z_verbose > 1) { - fprintf(stderr,"\\[%d,%d]", start - match, length); - do { putc(s->window[start++], stderr); } while (--length != 0); - } -} -#else -# define check_match(s, start, match, length) -#endif /* ZLIB_DEBUG */ - -/* =========================================================================== - * Flush the current block, with given end-of-file flag. - * IN assertion: strstart is set to the end of the current match. - */ -#define FLUSH_BLOCK_ONLY(s, last) { \ - _tr_flush_block(s, (s->block_start >= 0L ? \ - (charf *)&s->window[(unsigned)s->block_start] : \ - (charf *)Z_NULL), \ - (ulg)((long)s->strstart - s->block_start), \ - (last)); \ - s->block_start = s->strstart; \ - flush_pending(s->strm); \ - Tracev((stderr,"[FLUSH]")); \ -} - -/* Same but force premature exit if necessary. */ -#define FLUSH_BLOCK(s, last) { \ - FLUSH_BLOCK_ONLY(s, last); \ - if (s->strm->avail_out == 0) return (last) ? finish_started : need_more; \ -} - -/* Maximum stored block length in deflate format (not including header). */ -#define MAX_STORED 65535 - -/* Minimum of a and b. */ -#define MIN(a, b) ((a) > (b) ? (b) : (a)) - -/* =========================================================================== - * Copy without compression as much as possible from the input stream, return - * the current block state. - * - * In case deflateParams() is used to later switch to a non-zero compression - * level, s->matches (otherwise unused when storing) keeps track of the number - * of hash table slides to perform. If s->matches is 1, then one hash table - * slide will be done when switching. If s->matches is 2, the maximum value - * allowed here, then the hash table will be cleared, since two or more slides - * is the same as a clear. - * - * deflate_stored() is written to minimize the number of times an input byte is - * copied. It is most efficient with large input and output buffers, which - * maximizes the opportunities to have a single copy from next_in to next_out. - */ -local block_state deflate_stored(deflate_state *s, int flush) { - /* Smallest worthy block size when not flushing or finishing. By default - * this is 32K. This can be as small as 507 bytes for memLevel == 1. For - * large input and output buffers, the stored block size will be larger. - */ - unsigned min_block = MIN(s->pending_buf_size - 5, s->w_size); - - /* Copy as many min_block or larger stored blocks directly to next_out as - * possible. If flushing, copy the remaining available input to next_out as - * stored blocks, if there is enough space. - */ - unsigned len, left, have, last = 0; - unsigned used = s->strm->avail_in; - do { - /* Set len to the maximum size block that we can copy directly with the - * available input data and output space. Set left to how much of that - * would be copied from what's left in the window. - */ - len = MAX_STORED; /* maximum deflate stored block length */ - have = (s->bi_valid + 42) >> 3; /* number of header bytes */ - if (s->strm->avail_out < have) /* need room for header */ - break; - /* maximum stored block length that will fit in avail_out: */ - have = s->strm->avail_out - have; - left = s->strstart - s->block_start; /* bytes left in window */ - if (len > (ulg)left + s->strm->avail_in) - len = left + s->strm->avail_in; /* limit len to the input */ - if (len > have) - len = have; /* limit len to the output */ - - /* If the stored block would be less than min_block in length, or if - * unable to copy all of the available input when flushing, then try - * copying to the window and the pending buffer instead. Also don't - * write an empty block when flushing -- deflate() does that. - */ - if (len < min_block && ((len == 0 && flush != Z_FINISH) || - flush == Z_NO_FLUSH || - len != left + s->strm->avail_in)) - break; - - /* Make a dummy stored block in pending to get the header bytes, - * including any pending bits. This also updates the debugging counts. - */ - last = flush == Z_FINISH && len == left + s->strm->avail_in ? 1 : 0; - _tr_stored_block(s, (char *)0, 0L, last); - - /* Replace the lengths in the dummy stored block with len. */ - s->pending_buf[s->pending - 4] = len; - s->pending_buf[s->pending - 3] = len >> 8; - s->pending_buf[s->pending - 2] = ~len; - s->pending_buf[s->pending - 1] = ~len >> 8; - - /* Write the stored block header bytes. */ - flush_pending(s->strm); - -#ifdef ZLIB_DEBUG - /* Update debugging counts for the data about to be copied. */ - s->compressed_len += len << 3; - s->bits_sent += len << 3; -#endif - - /* Copy uncompressed bytes from the window to next_out. */ - if (left) { - if (left > len) - left = len; - zmemcpy(s->strm->next_out, s->window + s->block_start, left); - s->strm->next_out += left; - s->strm->avail_out -= left; - s->strm->total_out += left; - s->block_start += left; - len -= left; - } - - /* Copy uncompressed bytes directly from next_in to next_out, updating - * the check value. - */ - if (len) { - read_buf(s->strm, s->strm->next_out, len); - s->strm->next_out += len; - s->strm->avail_out -= len; - s->strm->total_out += len; - } - } while (last == 0); - - /* Update the sliding window with the last s->w_size bytes of the copied - * data, or append all of the copied data to the existing window if less - * than s->w_size bytes were copied. Also update the number of bytes to - * insert in the hash tables, in the event that deflateParams() switches to - * a non-zero compression level. - */ - used -= s->strm->avail_in; /* number of input bytes directly copied */ - if (used) { - /* If any input was used, then no unused input remains in the window, - * therefore s->block_start == s->strstart. - */ - if (used >= s->w_size) { /* supplant the previous history */ - s->matches = 2; /* clear hash */ - zmemcpy(s->window, s->strm->next_in - s->w_size, s->w_size); - s->strstart = s->w_size; - s->insert = s->strstart; - } - else { - if (s->window_size - s->strstart <= used) { - /* Slide the window down. */ - s->strstart -= s->w_size; - zmemcpy(s->window, s->window + s->w_size, s->strstart); - if (s->matches < 2) - s->matches++; /* add a pending slide_hash() */ - if (s->insert > s->strstart) - s->insert = s->strstart; - } - zmemcpy(s->window + s->strstart, s->strm->next_in - used, used); - s->strstart += used; - s->insert += MIN(used, s->w_size - s->insert); - } - s->block_start = s->strstart; - } - if (s->high_water < s->strstart) - s->high_water = s->strstart; - - /* If the last block was written to next_out, then done. */ - if (last) - return finish_done; - - /* If flushing and all input has been consumed, then done. */ - if (flush != Z_NO_FLUSH && flush != Z_FINISH && - s->strm->avail_in == 0 && (long)s->strstart == s->block_start) - return block_done; - - /* Fill the window with any remaining input. */ - have = s->window_size - s->strstart; - if (s->strm->avail_in > have && s->block_start >= (long)s->w_size) { - /* Slide the window down. */ - s->block_start -= s->w_size; - s->strstart -= s->w_size; - zmemcpy(s->window, s->window + s->w_size, s->strstart); - if (s->matches < 2) - s->matches++; /* add a pending slide_hash() */ - have += s->w_size; /* more space now */ - if (s->insert > s->strstart) - s->insert = s->strstart; - } - if (have > s->strm->avail_in) - have = s->strm->avail_in; - if (have) { - read_buf(s->strm, s->window + s->strstart, have); - s->strstart += have; - s->insert += MIN(have, s->w_size - s->insert); - } - if (s->high_water < s->strstart) - s->high_water = s->strstart; - - /* There was not enough avail_out to write a complete worthy or flushed - * stored block to next_out. Write a stored block to pending instead, if we - * have enough input for a worthy block, or if flushing and there is enough - * room for the remaining input as a stored block in the pending buffer. - */ - have = (s->bi_valid + 42) >> 3; /* number of header bytes */ - /* maximum stored block length that will fit in pending: */ - have = MIN(s->pending_buf_size - have, MAX_STORED); - min_block = MIN(have, s->w_size); - left = s->strstart - s->block_start; - if (left >= min_block || - ((left || flush == Z_FINISH) && flush != Z_NO_FLUSH && - s->strm->avail_in == 0 && left <= have)) { - len = MIN(left, have); - last = flush == Z_FINISH && s->strm->avail_in == 0 && - len == left ? 1 : 0; - _tr_stored_block(s, (charf *)s->window + s->block_start, len, last); - s->block_start += len; - flush_pending(s->strm); - } - - /* We've done all we can with the available input and output. */ - return last ? finish_started : need_more; -} - -/* =========================================================================== - * Compress as much as possible from the input stream, return the current - * block state. - * This function does not perform lazy evaluation of matches and inserts - * new strings in the dictionary only for unmatched strings or for short - * matches. It is used only for the fast compression options. - */ -local block_state deflate_fast(deflate_state *s, int flush) { - IPos hash_head; /* head of the hash chain */ - int bflush; /* set if current block must be flushed */ - - for (;;) { - /* Make sure that we always have enough lookahead, except - * at the end of the input file. We need MAX_MATCH bytes - * for the next match, plus MIN_MATCH bytes to insert the - * string following the next match. - */ - if (s->lookahead < MIN_LOOKAHEAD) { - fill_window(s); - if (s->lookahead < MIN_LOOKAHEAD && flush == Z_NO_FLUSH) { - return need_more; - } - if (s->lookahead == 0) break; /* flush the current block */ - } - - /* Insert the string window[strstart .. strstart + 2] in the - * dictionary, and set hash_head to the head of the hash chain: - */ - hash_head = NIL; - if (s->lookahead >= MIN_MATCH) { - INSERT_STRING(s, s->strstart, hash_head); - } - - /* Find the longest match, discarding those <= prev_length. - * At this point we have always match_length < MIN_MATCH - */ - if (hash_head != NIL && s->strstart - hash_head <= MAX_DIST(s)) { - /* To simplify the code, we prevent matches with the string - * of window index 0 (in particular we have to avoid a match - * of the string with itself at the start of the input file). - */ - s->match_length = longest_match (s, hash_head); - /* longest_match() sets match_start */ - } - if (s->match_length >= MIN_MATCH) { - check_match(s, s->strstart, s->match_start, s->match_length); - - _tr_tally_dist(s, s->strstart - s->match_start, - s->match_length - MIN_MATCH, bflush); - - s->lookahead -= s->match_length; - - /* Insert new strings in the hash table only if the match length - * is not too large. This saves time but degrades compression. - */ -#ifndef FASTEST - if (s->match_length <= s->max_insert_length && - s->lookahead >= MIN_MATCH) { - s->match_length--; /* string at strstart already in table */ - do { - s->strstart++; - INSERT_STRING(s, s->strstart, hash_head); - /* strstart never exceeds WSIZE-MAX_MATCH, so there are - * always MIN_MATCH bytes ahead. - */ - } while (--s->match_length != 0); - s->strstart++; - } else -#endif - { - s->strstart += s->match_length; - s->match_length = 0; - s->ins_h = s->window[s->strstart]; - UPDATE_HASH(s, s->ins_h, s->window[s->strstart + 1]); -#if MIN_MATCH != 3 - Call UPDATE_HASH() MIN_MATCH-3 more times -#endif - /* If lookahead < MIN_MATCH, ins_h is garbage, but it does not - * matter since it will be recomputed at next deflate call. - */ - } - } else { - /* No match, output a literal byte */ - Tracevv((stderr,"%c", s->window[s->strstart])); - _tr_tally_lit(s, s->window[s->strstart], bflush); - s->lookahead--; - s->strstart++; - } - if (bflush) FLUSH_BLOCK(s, 0); - } - s->insert = s->strstart < MIN_MATCH-1 ? s->strstart : MIN_MATCH-1; - if (flush == Z_FINISH) { - FLUSH_BLOCK(s, 1); - return finish_done; - } - if (s->sym_next) - FLUSH_BLOCK(s, 0); - return block_done; -} - -#ifndef FASTEST -/* =========================================================================== - * Same as above, but achieves better compression. We use a lazy - * evaluation for matches: a match is finally adopted only if there is - * no better match at the next window position. - */ -local block_state deflate_slow(deflate_state *s, int flush) { - IPos hash_head; /* head of hash chain */ - int bflush; /* set if current block must be flushed */ - - /* Process the input block. */ - for (;;) { - /* Make sure that we always have enough lookahead, except - * at the end of the input file. We need MAX_MATCH bytes - * for the next match, plus MIN_MATCH bytes to insert the - * string following the next match. - */ - if (s->lookahead < MIN_LOOKAHEAD) { - fill_window(s); - if (s->lookahead < MIN_LOOKAHEAD && flush == Z_NO_FLUSH) { - return need_more; - } - if (s->lookahead == 0) break; /* flush the current block */ - } - - /* Insert the string window[strstart .. strstart + 2] in the - * dictionary, and set hash_head to the head of the hash chain: - */ - hash_head = NIL; - if (s->lookahead >= MIN_MATCH) { - INSERT_STRING(s, s->strstart, hash_head); - } - - /* Find the longest match, discarding those <= prev_length. - */ - s->prev_length = s->match_length, s->prev_match = s->match_start; - s->match_length = MIN_MATCH-1; - - if (hash_head != NIL && s->prev_length < s->max_lazy_match && - s->strstart - hash_head <= MAX_DIST(s)) { - /* To simplify the code, we prevent matches with the string - * of window index 0 (in particular we have to avoid a match - * of the string with itself at the start of the input file). - */ - s->match_length = longest_match (s, hash_head); - /* longest_match() sets match_start */ - - if (s->match_length <= 5 && (s->strategy == Z_FILTERED -#if TOO_FAR <= 32767 - || (s->match_length == MIN_MATCH && - s->strstart - s->match_start > TOO_FAR) -#endif - )) { - - /* If prev_match is also MIN_MATCH, match_start is garbage - * but we will ignore the current match anyway. - */ - s->match_length = MIN_MATCH-1; - } - } - /* If there was a match at the previous step and the current - * match is not better, output the previous match: - */ - if (s->prev_length >= MIN_MATCH && s->match_length <= s->prev_length) { - uInt max_insert = s->strstart + s->lookahead - MIN_MATCH; - /* Do not insert strings in hash table beyond this. */ - - check_match(s, s->strstart - 1, s->prev_match, s->prev_length); - - _tr_tally_dist(s, s->strstart - 1 - s->prev_match, - s->prev_length - MIN_MATCH, bflush); - - /* Insert in hash table all strings up to the end of the match. - * strstart - 1 and strstart are already inserted. If there is not - * enough lookahead, the last two strings are not inserted in - * the hash table. - */ - s->lookahead -= s->prev_length - 1; - s->prev_length -= 2; - do { - if (++s->strstart <= max_insert) { - INSERT_STRING(s, s->strstart, hash_head); - } - } while (--s->prev_length != 0); - s->match_available = 0; - s->match_length = MIN_MATCH-1; - s->strstart++; - - if (bflush) FLUSH_BLOCK(s, 0); - - } else if (s->match_available) { - /* If there was no match at the previous position, output a - * single literal. If there was a match but the current match - * is longer, truncate the previous match to a single literal. - */ - Tracevv((stderr,"%c", s->window[s->strstart - 1])); - _tr_tally_lit(s, s->window[s->strstart - 1], bflush); - if (bflush) { - FLUSH_BLOCK_ONLY(s, 0); - } - s->strstart++; - s->lookahead--; - if (s->strm->avail_out == 0) return need_more; - } else { - /* There is no previous match to compare with, wait for - * the next step to decide. - */ - s->match_available = 1; - s->strstart++; - s->lookahead--; - } - } - Assert (flush != Z_NO_FLUSH, "no flush?"); - if (s->match_available) { - Tracevv((stderr,"%c", s->window[s->strstart - 1])); - _tr_tally_lit(s, s->window[s->strstart - 1], bflush); - s->match_available = 0; - } - s->insert = s->strstart < MIN_MATCH-1 ? s->strstart : MIN_MATCH-1; - if (flush == Z_FINISH) { - FLUSH_BLOCK(s, 1); - return finish_done; - } - if (s->sym_next) - FLUSH_BLOCK(s, 0); - return block_done; -} -#endif /* FASTEST */ - -/* =========================================================================== - * For Z_RLE, simply look for runs of bytes, generate matches only of distance - * one. Do not maintain a hash table. (It will be regenerated if this run of - * deflate switches away from Z_RLE.) - */ -local block_state deflate_rle(deflate_state *s, int flush) { - int bflush; /* set if current block must be flushed */ - uInt prev; /* byte at distance one to match */ - Bytef *scan, *strend; /* scan goes up to strend for length of run */ - - for (;;) { - /* Make sure that we always have enough lookahead, except - * at the end of the input file. We need MAX_MATCH bytes - * for the longest run, plus one for the unrolled loop. - */ - if (s->lookahead <= MAX_MATCH) { - fill_window(s); - if (s->lookahead <= MAX_MATCH && flush == Z_NO_FLUSH) { - return need_more; - } - if (s->lookahead == 0) break; /* flush the current block */ - } - - /* See how many times the previous byte repeats */ - s->match_length = 0; - if (s->lookahead >= MIN_MATCH && s->strstart > 0) { - scan = s->window + s->strstart - 1; - prev = *scan; - if (prev == *++scan && prev == *++scan && prev == *++scan) { - strend = s->window + s->strstart + MAX_MATCH; - do { - } while (prev == *++scan && prev == *++scan && - prev == *++scan && prev == *++scan && - prev == *++scan && prev == *++scan && - prev == *++scan && prev == *++scan && - scan < strend); - s->match_length = MAX_MATCH - (uInt)(strend - scan); - if (s->match_length > s->lookahead) - s->match_length = s->lookahead; - } - Assert(scan <= s->window + (uInt)(s->window_size - 1), - "wild scan"); - } - - /* Emit match if have run of MIN_MATCH or longer, else emit literal */ - if (s->match_length >= MIN_MATCH) { - check_match(s, s->strstart, s->strstart - 1, s->match_length); - - _tr_tally_dist(s, 1, s->match_length - MIN_MATCH, bflush); - - s->lookahead -= s->match_length; - s->strstart += s->match_length; - s->match_length = 0; - } else { - /* No match, output a literal byte */ - Tracevv((stderr,"%c", s->window[s->strstart])); - _tr_tally_lit(s, s->window[s->strstart], bflush); - s->lookahead--; - s->strstart++; - } - if (bflush) FLUSH_BLOCK(s, 0); - } - s->insert = 0; - if (flush == Z_FINISH) { - FLUSH_BLOCK(s, 1); - return finish_done; - } - if (s->sym_next) - FLUSH_BLOCK(s, 0); - return block_done; -} - -/* =========================================================================== - * For Z_HUFFMAN_ONLY, do not look for matches. Do not maintain a hash table. - * (It will be regenerated if this run of deflate switches away from Huffman.) - */ -local block_state deflate_huff(deflate_state *s, int flush) { - int bflush; /* set if current block must be flushed */ - - for (;;) { - /* Make sure that we have a literal to write. */ - if (s->lookahead == 0) { - fill_window(s); - if (s->lookahead == 0) { - if (flush == Z_NO_FLUSH) - return need_more; - break; /* flush the current block */ - } - } - - /* Output a literal byte */ - s->match_length = 0; - Tracevv((stderr,"%c", s->window[s->strstart])); - _tr_tally_lit(s, s->window[s->strstart], bflush); - s->lookahead--; - s->strstart++; - if (bflush) FLUSH_BLOCK(s, 0); - } - s->insert = 0; - if (flush == Z_FINISH) { - FLUSH_BLOCK(s, 1); - return finish_done; - } - if (s->sym_next) - FLUSH_BLOCK(s, 0); - return block_done; -} diff --git a/phobos/etc/c/zlib/deflate.h b/phobos/etc/c/zlib/deflate.h deleted file mode 100644 index 300c6ad..0000000 --- a/phobos/etc/c/zlib/deflate.h +++ /dev/null @@ -1,377 +0,0 @@ -/* deflate.h -- internal compression state - * Copyright (C) 1995-2024 Jean-loup Gailly - * For conditions of distribution and use, see copyright notice in zlib.h - */ - -/* WARNING: this file should *not* be used by applications. It is - part of the implementation of the compression library and is - subject to change. Applications should only use zlib.h. - */ - -/* @(#) $Id$ */ - -#ifndef DEFLATE_H -#define DEFLATE_H - -#include "zutil.h" - -/* define NO_GZIP when compiling if you want to disable gzip header and - trailer creation by deflate(). NO_GZIP would be used to avoid linking in - the crc code when it is not needed. For shared libraries, gzip encoding - should be left enabled. */ -#ifndef NO_GZIP -# define GZIP -#endif - -/* define LIT_MEM to slightly increase the speed of deflate (order 1% to 2%) at - the cost of a larger memory footprint */ -/* #define LIT_MEM */ - -/* =========================================================================== - * Internal compression state. - */ - -#define LENGTH_CODES 29 -/* number of length codes, not counting the special END_BLOCK code */ - -#define LITERALS 256 -/* number of literal bytes 0..255 */ - -#define L_CODES (LITERALS+1+LENGTH_CODES) -/* number of Literal or Length codes, including the END_BLOCK code */ - -#define D_CODES 30 -/* number of distance codes */ - -#define BL_CODES 19 -/* number of codes used to transfer the bit lengths */ - -#define HEAP_SIZE (2*L_CODES+1) -/* maximum heap size */ - -#define MAX_BITS 15 -/* All codes must not exceed MAX_BITS bits */ - -#define Buf_size 16 -/* size of bit buffer in bi_buf */ - -#define INIT_STATE 42 /* zlib header -> BUSY_STATE */ -#ifdef GZIP -# define GZIP_STATE 57 /* gzip header -> BUSY_STATE | EXTRA_STATE */ -#endif -#define EXTRA_STATE 69 /* gzip extra block -> NAME_STATE */ -#define NAME_STATE 73 /* gzip file name -> COMMENT_STATE */ -#define COMMENT_STATE 91 /* gzip comment -> HCRC_STATE */ -#define HCRC_STATE 103 /* gzip header CRC -> BUSY_STATE */ -#define BUSY_STATE 113 /* deflate -> FINISH_STATE */ -#define FINISH_STATE 666 /* stream complete */ -/* Stream status */ - - -/* Data structure describing a single value and its code string. */ -typedef struct ct_data_s { - union { - ush freq; /* frequency count */ - ush code; /* bit string */ - } fc; - union { - ush dad; /* father node in Huffman tree */ - ush len; /* length of bit string */ - } dl; -} FAR ct_data; - -#define Freq fc.freq -#define Code fc.code -#define Dad dl.dad -#define Len dl.len - -typedef struct static_tree_desc_s static_tree_desc; - -typedef struct tree_desc_s { - ct_data *dyn_tree; /* the dynamic tree */ - int max_code; /* largest code with non zero frequency */ - const static_tree_desc *stat_desc; /* the corresponding static tree */ -} FAR tree_desc; - -typedef ush Pos; -typedef Pos FAR Posf; -typedef unsigned IPos; - -/* A Pos is an index in the character window. We use short instead of int to - * save space in the various tables. IPos is used only for parameter passing. - */ - -typedef struct internal_state { - z_streamp strm; /* pointer back to this zlib stream */ - int status; /* as the name implies */ - Bytef *pending_buf; /* output still pending */ - ulg pending_buf_size; /* size of pending_buf */ - Bytef *pending_out; /* next pending byte to output to the stream */ - ulg pending; /* nb of bytes in the pending buffer */ - int wrap; /* bit 0 true for zlib, bit 1 true for gzip */ - gz_headerp gzhead; /* gzip header information to write */ - ulg gzindex; /* where in extra, name, or comment */ - Byte method; /* can only be DEFLATED */ - int last_flush; /* value of flush param for previous deflate call */ - - /* used by deflate.c: */ - - uInt w_size; /* LZ77 window size (32K by default) */ - uInt w_bits; /* log2(w_size) (8..16) */ - uInt w_mask; /* w_size - 1 */ - - Bytef *window; - /* Sliding window. Input bytes are read into the second half of the window, - * and move to the first half later to keep a dictionary of at least wSize - * bytes. With this organization, matches are limited to a distance of - * wSize-MAX_MATCH bytes, but this ensures that IO is always - * performed with a length multiple of the block size. Also, it limits - * the window size to 64K, which is quite useful on MSDOS. - * To do: use the user input buffer as sliding window. - */ - - ulg window_size; - /* Actual size of window: 2*wSize, except when the user input buffer - * is directly used as sliding window. - */ - - Posf *prev; - /* Link to older string with same hash index. To limit the size of this - * array to 64K, this link is maintained only for the last 32K strings. - * An index in this array is thus a window index modulo 32K. - */ - - Posf *head; /* Heads of the hash chains or NIL. */ - - uInt ins_h; /* hash index of string to be inserted */ - uInt hash_size; /* number of elements in hash table */ - uInt hash_bits; /* log2(hash_size) */ - uInt hash_mask; /* hash_size-1 */ - - uInt hash_shift; - /* Number of bits by which ins_h must be shifted at each input - * step. It must be such that after MIN_MATCH steps, the oldest - * byte no longer takes part in the hash key, that is: - * hash_shift * MIN_MATCH >= hash_bits - */ - - long block_start; - /* Window position at the beginning of the current output block. Gets - * negative when the window is moved backwards. - */ - - uInt match_length; /* length of best match */ - IPos prev_match; /* previous match */ - int match_available; /* set if previous match exists */ - uInt strstart; /* start of string to insert */ - uInt match_start; /* start of matching string */ - uInt lookahead; /* number of valid bytes ahead in window */ - - uInt prev_length; - /* Length of the best match at previous step. Matches not greater than this - * are discarded. This is used in the lazy match evaluation. - */ - - uInt max_chain_length; - /* To speed up deflation, hash chains are never searched beyond this - * length. A higher limit improves compression ratio but degrades the - * speed. - */ - - uInt max_lazy_match; - /* Attempt to find a better match only when the current match is strictly - * smaller than this value. This mechanism is used only for compression - * levels >= 4. - */ -# define max_insert_length max_lazy_match - /* Insert new strings in the hash table only if the match length is not - * greater than this length. This saves time but degrades compression. - * max_insert_length is used only for compression levels <= 3. - */ - - int level; /* compression level (1..9) */ - int strategy; /* favor or force Huffman coding*/ - - uInt good_match; - /* Use a faster search when the previous match is longer than this */ - - int nice_match; /* Stop searching when current match exceeds this */ - - /* used by trees.c: */ - /* Didn't use ct_data typedef below to suppress compiler warning */ - struct ct_data_s dyn_ltree[HEAP_SIZE]; /* literal and length tree */ - struct ct_data_s dyn_dtree[2*D_CODES+1]; /* distance tree */ - struct ct_data_s bl_tree[2*BL_CODES+1]; /* Huffman tree for bit lengths */ - - struct tree_desc_s l_desc; /* desc. for literal tree */ - struct tree_desc_s d_desc; /* desc. for distance tree */ - struct tree_desc_s bl_desc; /* desc. for bit length tree */ - - ush bl_count[MAX_BITS+1]; - /* number of codes at each bit length for an optimal tree */ - - int heap[2*L_CODES+1]; /* heap used to build the Huffman trees */ - int heap_len; /* number of elements in the heap */ - int heap_max; /* element of largest frequency */ - /* The sons of heap[n] are heap[2*n] and heap[2*n+1]. heap[0] is not used. - * The same heap array is used to build all trees. - */ - - uch depth[2*L_CODES+1]; - /* Depth of each subtree used as tie breaker for trees of equal frequency - */ - -#ifdef LIT_MEM -# define LIT_BUFS 5 - ushf *d_buf; /* buffer for distances */ - uchf *l_buf; /* buffer for literals/lengths */ -#else -# define LIT_BUFS 4 - uchf *sym_buf; /* buffer for distances and literals/lengths */ -#endif - - uInt lit_bufsize; - /* Size of match buffer for literals/lengths. There are 4 reasons for - * limiting lit_bufsize to 64K: - * - frequencies can be kept in 16 bit counters - * - if compression is not successful for the first block, all input - * data is still in the window so we can still emit a stored block even - * when input comes from standard input. (This can also be done for - * all blocks if lit_bufsize is not greater than 32K.) - * - if compression is not successful for a file smaller than 64K, we can - * even emit a stored file instead of a stored block (saving 5 bytes). - * This is applicable only for zip (not gzip or zlib). - * - creating new Huffman trees less frequently may not provide fast - * adaptation to changes in the input data statistics. (Take for - * example a binary file with poorly compressible code followed by - * a highly compressible string table.) Smaller buffer sizes give - * fast adaptation but have of course the overhead of transmitting - * trees more frequently. - * - I can't count above 4 - */ - - uInt sym_next; /* running index in symbol buffer */ - uInt sym_end; /* symbol table full when sym_next reaches this */ - - ulg opt_len; /* bit length of current block with optimal trees */ - ulg static_len; /* bit length of current block with static trees */ - uInt matches; /* number of string matches in current block */ - uInt insert; /* bytes at end of window left to insert */ - -#ifdef ZLIB_DEBUG - ulg compressed_len; /* total bit length of compressed file mod 2^32 */ - ulg bits_sent; /* bit length of compressed data sent mod 2^32 */ -#endif - - ush bi_buf; - /* Output buffer. bits are inserted starting at the bottom (least - * significant bits). - */ - int bi_valid; - /* Number of valid bits in bi_buf. All bits above the last valid bit - * are always zero. - */ - - ulg high_water; - /* High water mark offset in window for initialized bytes -- bytes above - * this are set to zero in order to avoid memory check warnings when - * longest match routines access bytes past the input. This is then - * updated to the new high water mark. - */ - -} FAR deflate_state; - -/* Output a byte on the stream. - * IN assertion: there is enough room in pending_buf. - */ -#define put_byte(s, c) {s->pending_buf[s->pending++] = (Bytef)(c);} - - -#define MIN_LOOKAHEAD (MAX_MATCH+MIN_MATCH+1) -/* Minimum amount of lookahead, except at the end of the input file. - * See deflate.c for comments about the MIN_MATCH+1. - */ - -#define MAX_DIST(s) ((s)->w_size-MIN_LOOKAHEAD) -/* In order to simplify the code, particularly on 16 bit machines, match - * distances are limited to MAX_DIST instead of WSIZE. - */ - -#define WIN_INIT MAX_MATCH -/* Number of bytes after end of data in window to initialize in order to avoid - memory checker errors from longest match routines */ - - /* in trees.c */ -void ZLIB_INTERNAL _tr_init(deflate_state *s); -int ZLIB_INTERNAL _tr_tally(deflate_state *s, unsigned dist, unsigned lc); -void ZLIB_INTERNAL _tr_flush_block(deflate_state *s, charf *buf, - ulg stored_len, int last); -void ZLIB_INTERNAL _tr_flush_bits(deflate_state *s); -void ZLIB_INTERNAL _tr_align(deflate_state *s); -void ZLIB_INTERNAL _tr_stored_block(deflate_state *s, charf *buf, - ulg stored_len, int last); - -#define d_code(dist) \ - ((dist) < 256 ? _dist_code[dist] : _dist_code[256+((dist)>>7)]) -/* Mapping from a distance to a distance code. dist is the distance - 1 and - * must not have side effects. _dist_code[256] and _dist_code[257] are never - * used. - */ - -#ifndef ZLIB_DEBUG -/* Inline versions of _tr_tally for speed: */ - -#if defined(GEN_TREES_H) || !defined(STDC) - extern uch ZLIB_INTERNAL _length_code[]; - extern uch ZLIB_INTERNAL _dist_code[]; -#else - extern const uch ZLIB_INTERNAL _length_code[]; - extern const uch ZLIB_INTERNAL _dist_code[]; -#endif - -#ifdef LIT_MEM -# define _tr_tally_lit(s, c, flush) \ - { uch cc = (c); \ - s->d_buf[s->sym_next] = 0; \ - s->l_buf[s->sym_next++] = cc; \ - s->dyn_ltree[cc].Freq++; \ - flush = (s->sym_next == s->sym_end); \ - } -# define _tr_tally_dist(s, distance, length, flush) \ - { uch len = (uch)(length); \ - ush dist = (ush)(distance); \ - s->d_buf[s->sym_next] = dist; \ - s->l_buf[s->sym_next++] = len; \ - dist--; \ - s->dyn_ltree[_length_code[len]+LITERALS+1].Freq++; \ - s->dyn_dtree[d_code(dist)].Freq++; \ - flush = (s->sym_next == s->sym_end); \ - } -#else -# define _tr_tally_lit(s, c, flush) \ - { uch cc = (c); \ - s->sym_buf[s->sym_next++] = 0; \ - s->sym_buf[s->sym_next++] = 0; \ - s->sym_buf[s->sym_next++] = cc; \ - s->dyn_ltree[cc].Freq++; \ - flush = (s->sym_next == s->sym_end); \ - } -# define _tr_tally_dist(s, distance, length, flush) \ - { uch len = (uch)(length); \ - ush dist = (ush)(distance); \ - s->sym_buf[s->sym_next++] = (uch)dist; \ - s->sym_buf[s->sym_next++] = (uch)(dist >> 8); \ - s->sym_buf[s->sym_next++] = len; \ - dist--; \ - s->dyn_ltree[_length_code[len]+LITERALS+1].Freq++; \ - s->dyn_dtree[d_code(dist)].Freq++; \ - flush = (s->sym_next == s->sym_end); \ - } -#endif -#else -# define _tr_tally_lit(s, c, flush) flush = _tr_tally(s, 0, c) -# define _tr_tally_dist(s, distance, length, flush) \ - flush = _tr_tally(s, distance, length) -#endif - -#endif /* DEFLATE_H */ diff --git a/phobos/etc/c/zlib/gzclose.c b/phobos/etc/c/zlib/gzclose.c deleted file mode 100644 index 48d6a86..0000000 --- a/phobos/etc/c/zlib/gzclose.c +++ /dev/null @@ -1,23 +0,0 @@ -/* gzclose.c -- zlib gzclose() function - * Copyright (C) 2004, 2010 Mark Adler - * For conditions of distribution and use, see copyright notice in zlib.h - */ - -#include "gzguts.h" - -/* gzclose() is in a separate file so that it is linked in only if it is used. - That way the other gzclose functions can be used instead to avoid linking in - unneeded compression or decompression routines. */ -int ZEXPORT gzclose(gzFile file) { -#ifndef NO_GZCOMPRESS - gz_statep state; - - if (file == NULL) - return Z_STREAM_ERROR; - state = (gz_statep)file; - - return state->mode == GZ_READ ? gzclose_r(file) : gzclose_w(file); -#else - return gzclose_r(file); -#endif -} diff --git a/phobos/etc/c/zlib/gzguts.h b/phobos/etc/c/zlib/gzguts.h deleted file mode 100644 index eba7208..0000000 --- a/phobos/etc/c/zlib/gzguts.h +++ /dev/null @@ -1,214 +0,0 @@ -/* gzguts.h -- zlib internal header definitions for gz* operations - * Copyright (C) 2004-2024 Mark Adler - * For conditions of distribution and use, see copyright notice in zlib.h - */ - -#ifdef _LARGEFILE64_SOURCE -# ifndef _LARGEFILE_SOURCE -# define _LARGEFILE_SOURCE 1 -# endif -# undef _FILE_OFFSET_BITS -# undef _TIME_BITS -#endif - -#ifdef HAVE_HIDDEN -# define ZLIB_INTERNAL __attribute__((visibility ("hidden"))) -#else -# define ZLIB_INTERNAL -#endif - -#include -#include "zlib.h" -#ifdef STDC -# include -# include -# include -#endif - -#ifndef _POSIX_SOURCE -# define _POSIX_SOURCE -#endif -#include - -#ifdef _WIN32 -# include -#endif - -#if defined(__TURBOC__) || defined(_MSC_VER) || defined(_WIN32) -# include -#endif - -#if defined(_WIN32) -# define WIDECHAR -#endif - -#ifdef WINAPI_FAMILY -# define open _open -# define read _read -# define write _write -# define close _close -#endif - -#ifdef NO_DEFLATE /* for compatibility with old definition */ -# define NO_GZCOMPRESS -#endif - -#if defined(STDC99) || (defined(__TURBOC__) && __TURBOC__ >= 0x550) -# ifndef HAVE_VSNPRINTF -# define HAVE_VSNPRINTF -# endif -#endif - -#if defined(__CYGWIN__) -# ifndef HAVE_VSNPRINTF -# define HAVE_VSNPRINTF -# endif -#endif - -#if defined(MSDOS) && defined(__BORLANDC__) && (BORLANDC > 0x410) -# ifndef HAVE_VSNPRINTF -# define HAVE_VSNPRINTF -# endif -#endif - -#ifndef HAVE_VSNPRINTF -# ifdef MSDOS -/* vsnprintf may exist on some MS-DOS compilers (DJGPP?), - but for now we just assume it doesn't. */ -# define NO_vsnprintf -# endif -# ifdef __TURBOC__ -# define NO_vsnprintf -# endif -# ifdef WIN32 -/* In Win32, vsnprintf is available as the "non-ANSI" _vsnprintf. */ -# if !defined(vsnprintf) && !defined(NO_vsnprintf) -# if !defined(_MSC_VER) || ( defined(_MSC_VER) && _MSC_VER < 1500 ) -# define vsnprintf _vsnprintf -# endif -# endif -# endif -# ifdef __SASC -# define NO_vsnprintf -# endif -# ifdef VMS -# define NO_vsnprintf -# endif -# ifdef __OS400__ -# define NO_vsnprintf -# endif -# ifdef __MVS__ -# define NO_vsnprintf -# endif -#endif - -/* unlike snprintf (which is required in C99), _snprintf does not guarantee - null termination of the result -- however this is only used in gzlib.c where - the result is assured to fit in the space provided */ -#if defined(_MSC_VER) && _MSC_VER < 1900 -# define snprintf _snprintf -#endif - -#ifndef local -# define local static -#endif -/* since "static" is used to mean two completely different things in C, we - define "local" for the non-static meaning of "static", for readability - (compile with -Dlocal if your debugger can't find static symbols) */ - -/* gz* functions always use library allocation functions */ -#ifndef STDC - extern voidp malloc(uInt size); - extern void free(voidpf ptr); -#endif - -/* get errno and strerror definition */ -#if defined UNDER_CE -# include -# define zstrerror() gz_strwinerror((DWORD)GetLastError()) -#else -# ifndef NO_STRERROR -# include -# define zstrerror() strerror(errno) -# else -# define zstrerror() "stdio error (consult errno)" -# endif -#endif - -/* provide prototypes for these when building zlib without LFS */ -#if !defined(_LARGEFILE64_SOURCE) || _LFS64_LARGEFILE-0 == 0 - ZEXTERN gzFile ZEXPORT gzopen64(const char *, const char *); - ZEXTERN z_off64_t ZEXPORT gzseek64(gzFile, z_off64_t, int); - ZEXTERN z_off64_t ZEXPORT gztell64(gzFile); - ZEXTERN z_off64_t ZEXPORT gzoffset64(gzFile); -#endif - -/* default memLevel */ -#if MAX_MEM_LEVEL >= 8 -# define DEF_MEM_LEVEL 8 -#else -# define DEF_MEM_LEVEL MAX_MEM_LEVEL -#endif - -/* default i/o buffer size -- double this for output when reading (this and - twice this must be able to fit in an unsigned type) */ -#define GZBUFSIZE 8192 - -/* gzip modes, also provide a little integrity check on the passed structure */ -#define GZ_NONE 0 -#define GZ_READ 7247 -#define GZ_WRITE 31153 -#define GZ_APPEND 1 /* mode set to GZ_WRITE after the file is opened */ - -/* values for gz_state how */ -#define LOOK 0 /* look for a gzip header */ -#define COPY 1 /* copy input directly */ -#define GZIP 2 /* decompress a gzip stream */ - -/* internal gzip file state data structure */ -typedef struct { - /* exposed contents for gzgetc() macro */ - struct gzFile_s x; /* "x" for exposed */ - /* x.have: number of bytes available at x.next */ - /* x.next: next output data to deliver or write */ - /* x.pos: current position in uncompressed data */ - /* used for both reading and writing */ - int mode; /* see gzip modes above */ - int fd; /* file descriptor */ - char *path; /* path or fd for error messages */ - unsigned size; /* buffer size, zero if not allocated yet */ - unsigned want; /* requested buffer size, default is GZBUFSIZE */ - unsigned char *in; /* input buffer (double-sized when writing) */ - unsigned char *out; /* output buffer (double-sized when reading) */ - int direct; /* 0 if processing gzip, 1 if transparent */ - /* just for reading */ - int how; /* 0: get header, 1: copy, 2: decompress */ - z_off64_t start; /* where the gzip data started, for rewinding */ - int eof; /* true if end of input file reached */ - int past; /* true if read requested past end */ - /* just for writing */ - int level; /* compression level */ - int strategy; /* compression strategy */ - int reset; /* true if a reset is pending after a Z_FINISH */ - /* seek request */ - z_off64_t skip; /* amount to skip (already rewound if backwards) */ - int seek; /* true if seek request pending */ - /* error information */ - int err; /* error code */ - char *msg; /* error message */ - /* zlib inflate or deflate stream */ - z_stream strm; /* stream structure in-place (not a pointer) */ -} gz_state; -typedef gz_state FAR *gz_statep; - -/* shared functions */ -void ZLIB_INTERNAL gz_error(gz_statep, int, const char *); -#if defined UNDER_CE -char ZLIB_INTERNAL *gz_strwinerror(DWORD error); -#endif - -/* GT_OFF(x), where x is an unsigned value, is true if x > maximum z_off64_t - value -- needed when comparing unsigned to z_off64_t, which is signed - (possible z_off64_t types off_t, off64_t, and long are all signed) */ -unsigned ZLIB_INTERNAL gz_intmax(void); -#define GT_OFF(x) (sizeof(int) == sizeof(z_off64_t) && (x) > gz_intmax()) diff --git a/phobos/etc/c/zlib/gzlib.c b/phobos/etc/c/zlib/gzlib.c deleted file mode 100644 index c50a520..0000000 --- a/phobos/etc/c/zlib/gzlib.c +++ /dev/null @@ -1,582 +0,0 @@ -/* gzlib.c -- zlib functions common to reading and writing gzip files - * Copyright (C) 2004-2024 Mark Adler - * For conditions of distribution and use, see copyright notice in zlib.h - */ - -#include "gzguts.h" - -#if defined(_WIN32) && !defined(__BORLANDC__) && !defined(__DMC__) -# define LSEEK _lseeki64 -#else -#if defined(_LARGEFILE64_SOURCE) && _LFS64_LARGEFILE-0 -# define LSEEK lseek64 -#else -# define LSEEK lseek -#endif -#endif - -#if defined UNDER_CE - -/* Map the Windows error number in ERROR to a locale-dependent error message - string and return a pointer to it. Typically, the values for ERROR come - from GetLastError. - - The string pointed to shall not be modified by the application, but may be - overwritten by a subsequent call to gz_strwinerror - - The gz_strwinerror function does not change the current setting of - GetLastError. */ -char ZLIB_INTERNAL *gz_strwinerror(DWORD error) { - static char buf[1024]; - - wchar_t *msgbuf; - DWORD lasterr = GetLastError(); - DWORD chars = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM - | FORMAT_MESSAGE_ALLOCATE_BUFFER, - NULL, - error, - 0, /* Default language */ - (LPVOID)&msgbuf, - 0, - NULL); - if (chars != 0) { - /* If there is an \r\n appended, zap it. */ - if (chars >= 2 - && msgbuf[chars - 2] == '\r' && msgbuf[chars - 1] == '\n') { - chars -= 2; - msgbuf[chars] = 0; - } - - if (chars > sizeof (buf) - 1) { - chars = sizeof (buf) - 1; - msgbuf[chars] = 0; - } - - wcstombs(buf, msgbuf, chars + 1); - LocalFree(msgbuf); - } - else { - sprintf(buf, "unknown win32 error (%ld)", error); - } - - SetLastError(lasterr); - return buf; -} - -#endif /* UNDER_CE */ - -/* Reset gzip file state */ -local void gz_reset(gz_statep state) { - state->x.have = 0; /* no output data available */ - if (state->mode == GZ_READ) { /* for reading ... */ - state->eof = 0; /* not at end of file */ - state->past = 0; /* have not read past end yet */ - state->how = LOOK; /* look for gzip header */ - } - else /* for writing ... */ - state->reset = 0; /* no deflateReset pending */ - state->seek = 0; /* no seek request pending */ - gz_error(state, Z_OK, NULL); /* clear error */ - state->x.pos = 0; /* no uncompressed data yet */ - state->strm.avail_in = 0; /* no input data yet */ -} - -/* Open a gzip file either by name or file descriptor. */ -local gzFile gz_open(const void *path, int fd, const char *mode) { - gz_statep state; - z_size_t len; - int oflag; -#ifdef O_CLOEXEC - int cloexec = 0; -#endif -#ifdef O_EXCL - int exclusive = 0; -#endif - - /* check input */ - if (path == NULL) - return NULL; - - /* allocate gzFile structure to return */ - state = (gz_statep)malloc(sizeof(gz_state)); - if (state == NULL) - return NULL; - state->size = 0; /* no buffers allocated yet */ - state->want = GZBUFSIZE; /* requested buffer size */ - state->msg = NULL; /* no error message yet */ - - /* interpret mode */ - state->mode = GZ_NONE; - state->level = Z_DEFAULT_COMPRESSION; - state->strategy = Z_DEFAULT_STRATEGY; - state->direct = 0; - while (*mode) { - if (*mode >= '0' && *mode <= '9') - state->level = *mode - '0'; - else - switch (*mode) { - case 'r': - state->mode = GZ_READ; - break; -#ifndef NO_GZCOMPRESS - case 'w': - state->mode = GZ_WRITE; - break; - case 'a': - state->mode = GZ_APPEND; - break; -#endif - case '+': /* can't read and write at the same time */ - free(state); - return NULL; - case 'b': /* ignore -- will request binary anyway */ - break; -#ifdef O_CLOEXEC - case 'e': - cloexec = 1; - break; -#endif -#ifdef O_EXCL - case 'x': - exclusive = 1; - break; -#endif - case 'f': - state->strategy = Z_FILTERED; - break; - case 'h': - state->strategy = Z_HUFFMAN_ONLY; - break; - case 'R': - state->strategy = Z_RLE; - break; - case 'F': - state->strategy = Z_FIXED; - break; - case 'T': - state->direct = 1; - break; - default: /* could consider as an error, but just ignore */ - ; - } - mode++; - } - - /* must provide an "r", "w", or "a" */ - if (state->mode == GZ_NONE) { - free(state); - return NULL; - } - - /* can't force transparent read */ - if (state->mode == GZ_READ) { - if (state->direct) { - free(state); - return NULL; - } - state->direct = 1; /* for empty file */ - } - - /* save the path name for error messages */ -#ifdef WIDECHAR - if (fd == -2) { - len = wcstombs(NULL, path, 0); - if (len == (z_size_t)-1) - len = 0; - } - else -#endif - len = strlen((const char *)path); - state->path = (char *)malloc(len + 1); - if (state->path == NULL) { - free(state); - return NULL; - } -#ifdef WIDECHAR - if (fd == -2) - if (len) - wcstombs(state->path, path, len + 1); - else - *(state->path) = 0; - else -#endif -#if !defined(NO_snprintf) && !defined(NO_vsnprintf) - (void)snprintf(state->path, len + 1, "%s", (const char *)path); -#else - strcpy(state->path, path); -#endif - - /* compute the flags for open() */ - oflag = -#ifdef O_LARGEFILE - O_LARGEFILE | -#endif -#ifdef O_BINARY - O_BINARY | -#endif -#ifdef O_CLOEXEC - (cloexec ? O_CLOEXEC : 0) | -#endif - (state->mode == GZ_READ ? - O_RDONLY : - (O_WRONLY | O_CREAT | -#ifdef O_EXCL - (exclusive ? O_EXCL : 0) | -#endif - (state->mode == GZ_WRITE ? - O_TRUNC : - O_APPEND))); - - /* open the file with the appropriate flags (or just use fd) */ - state->fd = fd > -1 ? fd : ( -#ifdef WIDECHAR - fd == -2 ? _wopen(path, oflag, 0666) : -#endif - open((const char *)path, oflag, 0666)); - if (state->fd == -1) { - free(state->path); - free(state); - return NULL; - } - if (state->mode == GZ_APPEND) { - LSEEK(state->fd, 0, SEEK_END); /* so gzoffset() is correct */ - state->mode = GZ_WRITE; /* simplify later checks */ - } - - /* save the current position for rewinding (only if reading) */ - if (state->mode == GZ_READ) { - state->start = LSEEK(state->fd, 0, SEEK_CUR); - if (state->start == -1) state->start = 0; - } - - /* initialize stream */ - gz_reset(state); - - /* return stream */ - return (gzFile)state; -} - -/* -- see zlib.h -- */ -gzFile ZEXPORT gzopen(const char *path, const char *mode) { - return gz_open(path, -1, mode); -} - -/* -- see zlib.h -- */ -gzFile ZEXPORT gzopen64(const char *path, const char *mode) { - return gz_open(path, -1, mode); -} - -/* -- see zlib.h -- */ -gzFile ZEXPORT gzdopen(int fd, const char *mode) { - char *path; /* identifier for error messages */ - gzFile gz; - - if (fd == -1 || (path = (char *)malloc(7 + 3 * sizeof(int))) == NULL) - return NULL; -#if !defined(NO_snprintf) && !defined(NO_vsnprintf) - (void)snprintf(path, 7 + 3 * sizeof(int), "", fd); -#else - sprintf(path, "", fd); /* for debugging */ -#endif - gz = gz_open(path, fd, mode); - free(path); - return gz; -} - -/* -- see zlib.h -- */ -#ifdef WIDECHAR -gzFile ZEXPORT gzopen_w(const wchar_t *path, const char *mode) { - return gz_open(path, -2, mode); -} -#endif - -/* -- see zlib.h -- */ -int ZEXPORT gzbuffer(gzFile file, unsigned size) { - gz_statep state; - - /* get internal structure and check integrity */ - if (file == NULL) - return -1; - state = (gz_statep)file; - if (state->mode != GZ_READ && state->mode != GZ_WRITE) - return -1; - - /* make sure we haven't already allocated memory */ - if (state->size != 0) - return -1; - - /* check and set requested size */ - if ((size << 1) < size) - return -1; /* need to be able to double it */ - if (size < 8) - size = 8; /* needed to behave well with flushing */ - state->want = size; - return 0; -} - -/* -- see zlib.h -- */ -int ZEXPORT gzrewind(gzFile file) { - gz_statep state; - - /* get internal structure */ - if (file == NULL) - return -1; - state = (gz_statep)file; - - /* check that we're reading and that there's no error */ - if (state->mode != GZ_READ || - (state->err != Z_OK && state->err != Z_BUF_ERROR)) - return -1; - - /* back up and start over */ - if (LSEEK(state->fd, state->start, SEEK_SET) == -1) - return -1; - gz_reset(state); - return 0; -} - -/* -- see zlib.h -- */ -z_off64_t ZEXPORT gzseek64(gzFile file, z_off64_t offset, int whence) { - unsigned n; - z_off64_t ret; - gz_statep state; - - /* get internal structure and check integrity */ - if (file == NULL) - return -1; - state = (gz_statep)file; - if (state->mode != GZ_READ && state->mode != GZ_WRITE) - return -1; - - /* check that there's no error */ - if (state->err != Z_OK && state->err != Z_BUF_ERROR) - return -1; - - /* can only seek from start or relative to current position */ - if (whence != SEEK_SET && whence != SEEK_CUR) - return -1; - - /* normalize offset to a SEEK_CUR specification */ - if (whence == SEEK_SET) - offset -= state->x.pos; - else if (state->seek) - offset += state->skip; - state->seek = 0; - - /* if within raw area while reading, just go there */ - if (state->mode == GZ_READ && state->how == COPY && - state->x.pos + offset >= 0) { - ret = LSEEK(state->fd, offset - (z_off64_t)state->x.have, SEEK_CUR); - if (ret == -1) - return -1; - state->x.have = 0; - state->eof = 0; - state->past = 0; - state->seek = 0; - gz_error(state, Z_OK, NULL); - state->strm.avail_in = 0; - state->x.pos += offset; - return state->x.pos; - } - - /* calculate skip amount, rewinding if needed for back seek when reading */ - if (offset < 0) { - if (state->mode != GZ_READ) /* writing -- can't go backwards */ - return -1; - offset += state->x.pos; - if (offset < 0) /* before start of file! */ - return -1; - if (gzrewind(file) == -1) /* rewind, then skip to offset */ - return -1; - } - - /* if reading, skip what's in output buffer (one less gzgetc() check) */ - if (state->mode == GZ_READ) { - n = GT_OFF(state->x.have) || (z_off64_t)state->x.have > offset ? - (unsigned)offset : state->x.have; - state->x.have -= n; - state->x.next += n; - state->x.pos += n; - offset -= n; - } - - /* request skip (if not zero) */ - if (offset) { - state->seek = 1; - state->skip = offset; - } - return state->x.pos + offset; -} - -/* -- see zlib.h -- */ -z_off_t ZEXPORT gzseek(gzFile file, z_off_t offset, int whence) { - z_off64_t ret; - - ret = gzseek64(file, (z_off64_t)offset, whence); - return ret == (z_off_t)ret ? (z_off_t)ret : -1; -} - -/* -- see zlib.h -- */ -z_off64_t ZEXPORT gztell64(gzFile file) { - gz_statep state; - - /* get internal structure and check integrity */ - if (file == NULL) - return -1; - state = (gz_statep)file; - if (state->mode != GZ_READ && state->mode != GZ_WRITE) - return -1; - - /* return position */ - return state->x.pos + (state->seek ? state->skip : 0); -} - -/* -- see zlib.h -- */ -z_off_t ZEXPORT gztell(gzFile file) { - z_off64_t ret; - - ret = gztell64(file); - return ret == (z_off_t)ret ? (z_off_t)ret : -1; -} - -/* -- see zlib.h -- */ -z_off64_t ZEXPORT gzoffset64(gzFile file) { - z_off64_t offset; - gz_statep state; - - /* get internal structure and check integrity */ - if (file == NULL) - return -1; - state = (gz_statep)file; - if (state->mode != GZ_READ && state->mode != GZ_WRITE) - return -1; - - /* compute and return effective offset in file */ - offset = LSEEK(state->fd, 0, SEEK_CUR); - if (offset == -1) - return -1; - if (state->mode == GZ_READ) /* reading */ - offset -= state->strm.avail_in; /* don't count buffered input */ - return offset; -} - -/* -- see zlib.h -- */ -z_off_t ZEXPORT gzoffset(gzFile file) { - z_off64_t ret; - - ret = gzoffset64(file); - return ret == (z_off_t)ret ? (z_off_t)ret : -1; -} - -/* -- see zlib.h -- */ -int ZEXPORT gzeof(gzFile file) { - gz_statep state; - - /* get internal structure and check integrity */ - if (file == NULL) - return 0; - state = (gz_statep)file; - if (state->mode != GZ_READ && state->mode != GZ_WRITE) - return 0; - - /* return end-of-file state */ - return state->mode == GZ_READ ? state->past : 0; -} - -/* -- see zlib.h -- */ -const char * ZEXPORT gzerror(gzFile file, int *errnum) { - gz_statep state; - - /* get internal structure and check integrity */ - if (file == NULL) - return NULL; - state = (gz_statep)file; - if (state->mode != GZ_READ && state->mode != GZ_WRITE) - return NULL; - - /* return error information */ - if (errnum != NULL) - *errnum = state->err; - return state->err == Z_MEM_ERROR ? "out of memory" : - (state->msg == NULL ? "" : state->msg); -} - -/* -- see zlib.h -- */ -void ZEXPORT gzclearerr(gzFile file) { - gz_statep state; - - /* get internal structure and check integrity */ - if (file == NULL) - return; - state = (gz_statep)file; - if (state->mode != GZ_READ && state->mode != GZ_WRITE) - return; - - /* clear error and end-of-file */ - if (state->mode == GZ_READ) { - state->eof = 0; - state->past = 0; - } - gz_error(state, Z_OK, NULL); -} - -/* Create an error message in allocated memory and set state->err and - state->msg accordingly. Free any previous error message already there. Do - not try to free or allocate space if the error is Z_MEM_ERROR (out of - memory). Simply save the error message as a static string. If there is an - allocation failure constructing the error message, then convert the error to - out of memory. */ -void ZLIB_INTERNAL gz_error(gz_statep state, int err, const char *msg) { - /* free previously allocated message and clear */ - if (state->msg != NULL) { - if (state->err != Z_MEM_ERROR) - free(state->msg); - state->msg = NULL; - } - - /* if fatal, set state->x.have to 0 so that the gzgetc() macro fails */ - if (err != Z_OK && err != Z_BUF_ERROR) - state->x.have = 0; - - /* set error code, and if no message, then done */ - state->err = err; - if (msg == NULL) - return; - - /* for an out of memory error, return literal string when requested */ - if (err == Z_MEM_ERROR) - return; - - /* construct error message with path */ - if ((state->msg = (char *)malloc(strlen(state->path) + strlen(msg) + 3)) == - NULL) { - state->err = Z_MEM_ERROR; - return; - } -#if !defined(NO_snprintf) && !defined(NO_vsnprintf) - (void)snprintf(state->msg, strlen(state->path) + strlen(msg) + 3, - "%s%s%s", state->path, ": ", msg); -#else - strcpy(state->msg, state->path); - strcat(state->msg, ": "); - strcat(state->msg, msg); -#endif -} - -/* portably return maximum value for an int (when limits.h presumed not - available) -- we need to do this to cover cases where 2's complement not - used, since C standard permits 1's complement and sign-bit representations, - otherwise we could just use ((unsigned)-1) >> 1 */ -unsigned ZLIB_INTERNAL gz_intmax(void) { -#ifdef INT_MAX - return INT_MAX; -#else - unsigned p = 1, q; - do { - q = p; - p <<= 1; - p++; - } while (p > q); - return q >> 1; -#endif -} diff --git a/phobos/etc/c/zlib/gzread.c b/phobos/etc/c/zlib/gzread.c deleted file mode 100644 index 4168cbc..0000000 --- a/phobos/etc/c/zlib/gzread.c +++ /dev/null @@ -1,602 +0,0 @@ -/* gzread.c -- zlib functions for reading gzip files - * Copyright (C) 2004-2017 Mark Adler - * For conditions of distribution and use, see copyright notice in zlib.h - */ - -#include "gzguts.h" - -/* Use read() to load a buffer -- return -1 on error, otherwise 0. Read from - state->fd, and update state->eof, state->err, and state->msg as appropriate. - This function needs to loop on read(), since read() is not guaranteed to - read the number of bytes requested, depending on the type of descriptor. */ -local int gz_load(gz_statep state, unsigned char *buf, unsigned len, - unsigned *have) { - int ret; - unsigned get, max = ((unsigned)-1 >> 2) + 1; - - *have = 0; - do { - get = len - *have; - if (get > max) - get = max; - ret = read(state->fd, buf + *have, get); - if (ret <= 0) - break; - *have += (unsigned)ret; - } while (*have < len); - if (ret < 0) { - gz_error(state, Z_ERRNO, zstrerror()); - return -1; - } - if (ret == 0) - state->eof = 1; - return 0; -} - -/* Load up input buffer and set eof flag if last data loaded -- return -1 on - error, 0 otherwise. Note that the eof flag is set when the end of the input - file is reached, even though there may be unused data in the buffer. Once - that data has been used, no more attempts will be made to read the file. - If strm->avail_in != 0, then the current data is moved to the beginning of - the input buffer, and then the remainder of the buffer is loaded with the - available data from the input file. */ -local int gz_avail(gz_statep state) { - unsigned got; - z_streamp strm = &(state->strm); - - if (state->err != Z_OK && state->err != Z_BUF_ERROR) - return -1; - if (state->eof == 0) { - if (strm->avail_in) { /* copy what's there to the start */ - unsigned char *p = state->in; - unsigned const char *q = strm->next_in; - unsigned n = strm->avail_in; - do { - *p++ = *q++; - } while (--n); - } - if (gz_load(state, state->in + strm->avail_in, - state->size - strm->avail_in, &got) == -1) - return -1; - strm->avail_in += got; - strm->next_in = state->in; - } - return 0; -} - -/* Look for gzip header, set up for inflate or copy. state->x.have must be 0. - If this is the first time in, allocate required memory. state->how will be - left unchanged if there is no more input data available, will be set to COPY - if there is no gzip header and direct copying will be performed, or it will - be set to GZIP for decompression. If direct copying, then leftover input - data from the input buffer will be copied to the output buffer. In that - case, all further file reads will be directly to either the output buffer or - a user buffer. If decompressing, the inflate state will be initialized. - gz_look() will return 0 on success or -1 on failure. */ -local int gz_look(gz_statep state) { - z_streamp strm = &(state->strm); - - /* allocate read buffers and inflate memory */ - if (state->size == 0) { - /* allocate buffers */ - state->in = (unsigned char *)malloc(state->want); - state->out = (unsigned char *)malloc(state->want << 1); - if (state->in == NULL || state->out == NULL) { - free(state->out); - free(state->in); - gz_error(state, Z_MEM_ERROR, "out of memory"); - return -1; - } - state->size = state->want; - - /* allocate inflate memory */ - state->strm.zalloc = Z_NULL; - state->strm.zfree = Z_NULL; - state->strm.opaque = Z_NULL; - state->strm.avail_in = 0; - state->strm.next_in = Z_NULL; - if (inflateInit2(&(state->strm), 15 + 16) != Z_OK) { /* gunzip */ - free(state->out); - free(state->in); - state->size = 0; - gz_error(state, Z_MEM_ERROR, "out of memory"); - return -1; - } - } - - /* get at least the magic bytes in the input buffer */ - if (strm->avail_in < 2) { - if (gz_avail(state) == -1) - return -1; - if (strm->avail_in == 0) - return 0; - } - - /* look for gzip magic bytes -- if there, do gzip decoding (note: there is - a logical dilemma here when considering the case of a partially written - gzip file, to wit, if a single 31 byte is written, then we cannot tell - whether this is a single-byte file, or just a partially written gzip - file -- for here we assume that if a gzip file is being written, then - the header will be written in a single operation, so that reading a - single byte is sufficient indication that it is not a gzip file) */ - if (strm->avail_in > 1 && - strm->next_in[0] == 31 && strm->next_in[1] == 139) { - inflateReset(strm); - state->how = GZIP; - state->direct = 0; - return 0; - } - - /* no gzip header -- if we were decoding gzip before, then this is trailing - garbage. Ignore the trailing garbage and finish. */ - if (state->direct == 0) { - strm->avail_in = 0; - state->eof = 1; - state->x.have = 0; - return 0; - } - - /* doing raw i/o, copy any leftover input to output -- this assumes that - the output buffer is larger than the input buffer, which also assures - space for gzungetc() */ - state->x.next = state->out; - memcpy(state->x.next, strm->next_in, strm->avail_in); - state->x.have = strm->avail_in; - strm->avail_in = 0; - state->how = COPY; - state->direct = 1; - return 0; -} - -/* Decompress from input to the provided next_out and avail_out in the state. - On return, state->x.have and state->x.next point to the just decompressed - data. If the gzip stream completes, state->how is reset to LOOK to look for - the next gzip stream or raw data, once state->x.have is depleted. Returns 0 - on success, -1 on failure. */ -local int gz_decomp(gz_statep state) { - int ret = Z_OK; - unsigned had; - z_streamp strm = &(state->strm); - - /* fill output buffer up to end of deflate stream */ - had = strm->avail_out; - do { - /* get more input for inflate() */ - if (strm->avail_in == 0 && gz_avail(state) == -1) - return -1; - if (strm->avail_in == 0) { - gz_error(state, Z_BUF_ERROR, "unexpected end of file"); - break; - } - - /* decompress and handle errors */ - ret = inflate(strm, Z_NO_FLUSH); - if (ret == Z_STREAM_ERROR || ret == Z_NEED_DICT) { - gz_error(state, Z_STREAM_ERROR, - "internal error: inflate stream corrupt"); - return -1; - } - if (ret == Z_MEM_ERROR) { - gz_error(state, Z_MEM_ERROR, "out of memory"); - return -1; - } - if (ret == Z_DATA_ERROR) { /* deflate stream invalid */ - gz_error(state, Z_DATA_ERROR, - strm->msg == NULL ? "compressed data error" : strm->msg); - return -1; - } - } while (strm->avail_out && ret != Z_STREAM_END); - - /* update available output */ - state->x.have = had - strm->avail_out; - state->x.next = strm->next_out - state->x.have; - - /* if the gzip stream completed successfully, look for another */ - if (ret == Z_STREAM_END) - state->how = LOOK; - - /* good decompression */ - return 0; -} - -/* Fetch data and put it in the output buffer. Assumes state->x.have is 0. - Data is either copied from the input file or decompressed from the input - file depending on state->how. If state->how is LOOK, then a gzip header is - looked for to determine whether to copy or decompress. Returns -1 on error, - otherwise 0. gz_fetch() will leave state->how as COPY or GZIP unless the - end of the input file has been reached and all data has been processed. */ -local int gz_fetch(gz_statep state) { - z_streamp strm = &(state->strm); - - do { - switch(state->how) { - case LOOK: /* -> LOOK, COPY (only if never GZIP), or GZIP */ - if (gz_look(state) == -1) - return -1; - if (state->how == LOOK) - return 0; - break; - case COPY: /* -> COPY */ - if (gz_load(state, state->out, state->size << 1, &(state->x.have)) - == -1) - return -1; - state->x.next = state->out; - return 0; - case GZIP: /* -> GZIP or LOOK (if end of gzip stream) */ - strm->avail_out = state->size << 1; - strm->next_out = state->out; - if (gz_decomp(state) == -1) - return -1; - } - } while (state->x.have == 0 && (!state->eof || strm->avail_in)); - return 0; -} - -/* Skip len uncompressed bytes of output. Return -1 on error, 0 on success. */ -local int gz_skip(gz_statep state, z_off64_t len) { - unsigned n; - - /* skip over len bytes or reach end-of-file, whichever comes first */ - while (len) - /* skip over whatever is in output buffer */ - if (state->x.have) { - n = GT_OFF(state->x.have) || (z_off64_t)state->x.have > len ? - (unsigned)len : state->x.have; - state->x.have -= n; - state->x.next += n; - state->x.pos += n; - len -= n; - } - - /* output buffer empty -- return if we're at the end of the input */ - else if (state->eof && state->strm.avail_in == 0) - break; - - /* need more data to skip -- load up output buffer */ - else { - /* get more output, looking for header if required */ - if (gz_fetch(state) == -1) - return -1; - } - return 0; -} - -/* Read len bytes into buf from file, or less than len up to the end of the - input. Return the number of bytes read. If zero is returned, either the - end of file was reached, or there was an error. state->err must be - consulted in that case to determine which. */ -local z_size_t gz_read(gz_statep state, voidp buf, z_size_t len) { - z_size_t got; - unsigned n; - - /* if len is zero, avoid unnecessary operations */ - if (len == 0) - return 0; - - /* process a skip request */ - if (state->seek) { - state->seek = 0; - if (gz_skip(state, state->skip) == -1) - return 0; - } - - /* get len bytes to buf, or less than len if at the end */ - got = 0; - do { - /* set n to the maximum amount of len that fits in an unsigned int */ - n = (unsigned)-1; - if (n > len) - n = (unsigned)len; - - /* first just try copying data from the output buffer */ - if (state->x.have) { - if (state->x.have < n) - n = state->x.have; - memcpy(buf, state->x.next, n); - state->x.next += n; - state->x.have -= n; - } - - /* output buffer empty -- return if we're at the end of the input */ - else if (state->eof && state->strm.avail_in == 0) { - state->past = 1; /* tried to read past end */ - break; - } - - /* need output data -- for small len or new stream load up our output - buffer */ - else if (state->how == LOOK || n < (state->size << 1)) { - /* get more output, looking for header if required */ - if (gz_fetch(state) == -1) - return 0; - continue; /* no progress yet -- go back to copy above */ - /* the copy above assures that we will leave with space in the - output buffer, allowing at least one gzungetc() to succeed */ - } - - /* large len -- read directly into user buffer */ - else if (state->how == COPY) { /* read directly */ - if (gz_load(state, (unsigned char *)buf, n, &n) == -1) - return 0; - } - - /* large len -- decompress directly into user buffer */ - else { /* state->how == GZIP */ - state->strm.avail_out = n; - state->strm.next_out = (unsigned char *)buf; - if (gz_decomp(state) == -1) - return 0; - n = state->x.have; - state->x.have = 0; - } - - /* update progress */ - len -= n; - buf = (char *)buf + n; - got += n; - state->x.pos += n; - } while (len); - - /* return number of bytes read into user buffer */ - return got; -} - -/* -- see zlib.h -- */ -int ZEXPORT gzread(gzFile file, voidp buf, unsigned len) { - gz_statep state; - - /* get internal structure */ - if (file == NULL) - return -1; - state = (gz_statep)file; - - /* check that we're reading and that there's no (serious) error */ - if (state->mode != GZ_READ || - (state->err != Z_OK && state->err != Z_BUF_ERROR)) - return -1; - - /* since an int is returned, make sure len fits in one, otherwise return - with an error (this avoids a flaw in the interface) */ - if ((int)len < 0) { - gz_error(state, Z_STREAM_ERROR, "request does not fit in an int"); - return -1; - } - - /* read len or fewer bytes to buf */ - len = (unsigned)gz_read(state, buf, len); - - /* check for an error */ - if (len == 0 && state->err != Z_OK && state->err != Z_BUF_ERROR) - return -1; - - /* return the number of bytes read (this is assured to fit in an int) */ - return (int)len; -} - -/* -- see zlib.h -- */ -z_size_t ZEXPORT gzfread(voidp buf, z_size_t size, z_size_t nitems, gzFile file) { - z_size_t len; - gz_statep state; - - /* get internal structure */ - if (file == NULL) - return 0; - state = (gz_statep)file; - - /* check that we're reading and that there's no (serious) error */ - if (state->mode != GZ_READ || - (state->err != Z_OK && state->err != Z_BUF_ERROR)) - return 0; - - /* compute bytes to read -- error on overflow */ - len = nitems * size; - if (size && len / size != nitems) { - gz_error(state, Z_STREAM_ERROR, "request does not fit in a size_t"); - return 0; - } - - /* read len or fewer bytes to buf, return the number of full items read */ - return len ? gz_read(state, buf, len) / size : 0; -} - -/* -- see zlib.h -- */ -#ifdef Z_PREFIX_SET -# undef z_gzgetc -#else -# undef gzgetc -#endif -int ZEXPORT gzgetc(gzFile file) { - unsigned char buf[1]; - gz_statep state; - - /* get internal structure */ - if (file == NULL) - return -1; - state = (gz_statep)file; - - /* check that we're reading and that there's no (serious) error */ - if (state->mode != GZ_READ || - (state->err != Z_OK && state->err != Z_BUF_ERROR)) - return -1; - - /* try output buffer (no need to check for skip request) */ - if (state->x.have) { - state->x.have--; - state->x.pos++; - return *(state->x.next)++; - } - - /* nothing there -- try gz_read() */ - return gz_read(state, buf, 1) < 1 ? -1 : buf[0]; -} - -int ZEXPORT gzgetc_(gzFile file) { - return gzgetc(file); -} - -/* -- see zlib.h -- */ -int ZEXPORT gzungetc(int c, gzFile file) { - gz_statep state; - - /* get internal structure */ - if (file == NULL) - return -1; - state = (gz_statep)file; - - /* in case this was just opened, set up the input buffer */ - if (state->mode == GZ_READ && state->how == LOOK && state->x.have == 0) - (void)gz_look(state); - - /* check that we're reading and that there's no (serious) error */ - if (state->mode != GZ_READ || - (state->err != Z_OK && state->err != Z_BUF_ERROR)) - return -1; - - /* process a skip request */ - if (state->seek) { - state->seek = 0; - if (gz_skip(state, state->skip) == -1) - return -1; - } - - /* can't push EOF */ - if (c < 0) - return -1; - - /* if output buffer empty, put byte at end (allows more pushing) */ - if (state->x.have == 0) { - state->x.have = 1; - state->x.next = state->out + (state->size << 1) - 1; - state->x.next[0] = (unsigned char)c; - state->x.pos--; - state->past = 0; - return c; - } - - /* if no room, give up (must have already done a gzungetc()) */ - if (state->x.have == (state->size << 1)) { - gz_error(state, Z_DATA_ERROR, "out of room to push characters"); - return -1; - } - - /* slide output data if needed and insert byte before existing data */ - if (state->x.next == state->out) { - unsigned char *src = state->out + state->x.have; - unsigned char *dest = state->out + (state->size << 1); - while (src > state->out) - *--dest = *--src; - state->x.next = dest; - } - state->x.have++; - state->x.next--; - state->x.next[0] = (unsigned char)c; - state->x.pos--; - state->past = 0; - return c; -} - -/* -- see zlib.h -- */ -char * ZEXPORT gzgets(gzFile file, char *buf, int len) { - unsigned left, n; - char *str; - unsigned char *eol; - gz_statep state; - - /* check parameters and get internal structure */ - if (file == NULL || buf == NULL || len < 1) - return NULL; - state = (gz_statep)file; - - /* check that we're reading and that there's no (serious) error */ - if (state->mode != GZ_READ || - (state->err != Z_OK && state->err != Z_BUF_ERROR)) - return NULL; - - /* process a skip request */ - if (state->seek) { - state->seek = 0; - if (gz_skip(state, state->skip) == -1) - return NULL; - } - - /* copy output bytes up to new line or len - 1, whichever comes first -- - append a terminating zero to the string (we don't check for a zero in - the contents, let the user worry about that) */ - str = buf; - left = (unsigned)len - 1; - if (left) do { - /* assure that something is in the output buffer */ - if (state->x.have == 0 && gz_fetch(state) == -1) - return NULL; /* error */ - if (state->x.have == 0) { /* end of file */ - state->past = 1; /* read past end */ - break; /* return what we have */ - } - - /* look for end-of-line in current output buffer */ - n = state->x.have > left ? left : state->x.have; - eol = (unsigned char *)memchr(state->x.next, '\n', n); - if (eol != NULL) - n = (unsigned)(eol - state->x.next) + 1; - - /* copy through end-of-line, or remainder if not found */ - memcpy(buf, state->x.next, n); - state->x.have -= n; - state->x.next += n; - state->x.pos += n; - left -= n; - buf += n; - } while (left && eol == NULL); - - /* return terminated string, or if nothing, end of file */ - if (buf == str) - return NULL; - buf[0] = 0; - return str; -} - -/* -- see zlib.h -- */ -int ZEXPORT gzdirect(gzFile file) { - gz_statep state; - - /* get internal structure */ - if (file == NULL) - return 0; - state = (gz_statep)file; - - /* if the state is not known, but we can find out, then do so (this is - mainly for right after a gzopen() or gzdopen()) */ - if (state->mode == GZ_READ && state->how == LOOK && state->x.have == 0) - (void)gz_look(state); - - /* return 1 if transparent, 0 if processing a gzip stream */ - return state->direct; -} - -/* -- see zlib.h -- */ -int ZEXPORT gzclose_r(gzFile file) { - int ret, err; - gz_statep state; - - /* get internal structure */ - if (file == NULL) - return Z_STREAM_ERROR; - state = (gz_statep)file; - - /* check that we're reading */ - if (state->mode != GZ_READ) - return Z_STREAM_ERROR; - - /* free memory and close file */ - if (state->size) { - inflateEnd(&(state->strm)); - free(state->out); - free(state->in); - } - err = state->err == Z_BUF_ERROR ? Z_BUF_ERROR : Z_OK; - gz_error(state, Z_OK, NULL); - free(state->path); - ret = close(state->fd); - free(state); - return ret ? Z_ERRNO : err; -} diff --git a/phobos/etc/c/zlib/gzwrite.c b/phobos/etc/c/zlib/gzwrite.c deleted file mode 100644 index 2940f5c..0000000 --- a/phobos/etc/c/zlib/gzwrite.c +++ /dev/null @@ -1,636 +0,0 @@ -/* gzwrite.c -- zlib functions for writing gzip files - * Copyright (C) 2004-2019 Mark Adler - * For conditions of distribution and use, see copyright notice in zlib.h - */ - -#include "gzguts.h" - -/* Initialize state for writing a gzip file. Mark initialization by setting - state->size to non-zero. Return -1 on a memory allocation failure, or 0 on - success. */ -local int gz_init(gz_statep state) { - int ret; - z_streamp strm = &(state->strm); - - /* allocate input buffer (double size for gzprintf) */ - state->in = (unsigned char *)malloc(state->want << 1); - if (state->in == NULL) { - gz_error(state, Z_MEM_ERROR, "out of memory"); - return -1; - } - - /* only need output buffer and deflate state if compressing */ - if (!state->direct) { - /* allocate output buffer */ - state->out = (unsigned char *)malloc(state->want); - if (state->out == NULL) { - free(state->in); - gz_error(state, Z_MEM_ERROR, "out of memory"); - return -1; - } - - /* allocate deflate memory, set up for gzip compression */ - strm->zalloc = Z_NULL; - strm->zfree = Z_NULL; - strm->opaque = Z_NULL; - ret = deflateInit2(strm, state->level, Z_DEFLATED, - MAX_WBITS + 16, DEF_MEM_LEVEL, state->strategy); - if (ret != Z_OK) { - free(state->out); - free(state->in); - gz_error(state, Z_MEM_ERROR, "out of memory"); - return -1; - } - strm->next_in = NULL; - } - - /* mark state as initialized */ - state->size = state->want; - - /* initialize write buffer if compressing */ - if (!state->direct) { - strm->avail_out = state->size; - strm->next_out = state->out; - state->x.next = strm->next_out; - } - return 0; -} - -/* Compress whatever is at avail_in and next_in and write to the output file. - Return -1 if there is an error writing to the output file or if gz_init() - fails to allocate memory, otherwise 0. flush is assumed to be a valid - deflate() flush value. If flush is Z_FINISH, then the deflate() state is - reset to start a new gzip stream. If gz->direct is true, then simply write - to the output file without compressing, and ignore flush. */ -local int gz_comp(gz_statep state, int flush) { - int ret, writ; - unsigned have, put, max = ((unsigned)-1 >> 2) + 1; - z_streamp strm = &(state->strm); - - /* allocate memory if this is the first time through */ - if (state->size == 0 && gz_init(state) == -1) - return -1; - - /* write directly if requested */ - if (state->direct) { - while (strm->avail_in) { - put = strm->avail_in > max ? max : strm->avail_in; - writ = write(state->fd, strm->next_in, put); - if (writ < 0) { - gz_error(state, Z_ERRNO, zstrerror()); - return -1; - } - strm->avail_in -= (unsigned)writ; - strm->next_in += writ; - } - return 0; - } - - /* check for a pending reset */ - if (state->reset) { - /* don't start a new gzip member unless there is data to write */ - if (strm->avail_in == 0) - return 0; - deflateReset(strm); - state->reset = 0; - } - - /* run deflate() on provided input until it produces no more output */ - ret = Z_OK; - do { - /* write out current buffer contents if full, or if flushing, but if - doing Z_FINISH then don't write until we get to Z_STREAM_END */ - if (strm->avail_out == 0 || (flush != Z_NO_FLUSH && - (flush != Z_FINISH || ret == Z_STREAM_END))) { - while (strm->next_out > state->x.next) { - put = strm->next_out - state->x.next > (int)max ? max : - (unsigned)(strm->next_out - state->x.next); - writ = write(state->fd, state->x.next, put); - if (writ < 0) { - gz_error(state, Z_ERRNO, zstrerror()); - return -1; - } - state->x.next += writ; - } - if (strm->avail_out == 0) { - strm->avail_out = state->size; - strm->next_out = state->out; - state->x.next = state->out; - } - } - - /* compress */ - have = strm->avail_out; - ret = deflate(strm, flush); - if (ret == Z_STREAM_ERROR) { - gz_error(state, Z_STREAM_ERROR, - "internal error: deflate stream corrupt"); - return -1; - } - have -= strm->avail_out; - } while (have); - - /* if that completed a deflate stream, allow another to start */ - if (flush == Z_FINISH) - state->reset = 1; - - /* all done, no errors */ - return 0; -} - -/* Compress len zeros to output. Return -1 on a write error or memory - allocation failure by gz_comp(), or 0 on success. */ -local int gz_zero(gz_statep state, z_off64_t len) { - int first; - unsigned n; - z_streamp strm = &(state->strm); - - /* consume whatever's left in the input buffer */ - if (strm->avail_in && gz_comp(state, Z_NO_FLUSH) == -1) - return -1; - - /* compress len zeros (len guaranteed > 0) */ - first = 1; - while (len) { - n = GT_OFF(state->size) || (z_off64_t)state->size > len ? - (unsigned)len : state->size; - if (first) { - memset(state->in, 0, n); - first = 0; - } - strm->avail_in = n; - strm->next_in = state->in; - state->x.pos += n; - if (gz_comp(state, Z_NO_FLUSH) == -1) - return -1; - len -= n; - } - return 0; -} - -/* Write len bytes from buf to file. Return the number of bytes written. If - the returned value is less than len, then there was an error. */ -local z_size_t gz_write(gz_statep state, voidpc buf, z_size_t len) { - z_size_t put = len; - - /* if len is zero, avoid unnecessary operations */ - if (len == 0) - return 0; - - /* allocate memory if this is the first time through */ - if (state->size == 0 && gz_init(state) == -1) - return 0; - - /* check for seek request */ - if (state->seek) { - state->seek = 0; - if (gz_zero(state, state->skip) == -1) - return 0; - } - - /* for small len, copy to input buffer, otherwise compress directly */ - if (len < state->size) { - /* copy to input buffer, compress when full */ - do { - unsigned have, copy; - - if (state->strm.avail_in == 0) - state->strm.next_in = state->in; - have = (unsigned)((state->strm.next_in + state->strm.avail_in) - - state->in); - copy = state->size - have; - if (copy > len) - copy = (unsigned)len; - memcpy(state->in + have, buf, copy); - state->strm.avail_in += copy; - state->x.pos += copy; - buf = (const char *)buf + copy; - len -= copy; - if (len && gz_comp(state, Z_NO_FLUSH) == -1) - return 0; - } while (len); - } - else { - /* consume whatever's left in the input buffer */ - if (state->strm.avail_in && gz_comp(state, Z_NO_FLUSH) == -1) - return 0; - - /* directly compress user buffer to file */ - state->strm.next_in = (z_const Bytef *)buf; - do { - unsigned n = (unsigned)-1; - if (n > len) - n = (unsigned)len; - state->strm.avail_in = n; - state->x.pos += n; - if (gz_comp(state, Z_NO_FLUSH) == -1) - return 0; - len -= n; - } while (len); - } - - /* input was all buffered or compressed */ - return put; -} - -/* -- see zlib.h -- */ -int ZEXPORT gzwrite(gzFile file, voidpc buf, unsigned len) { - gz_statep state; - - /* get internal structure */ - if (file == NULL) - return 0; - state = (gz_statep)file; - - /* check that we're writing and that there's no error */ - if (state->mode != GZ_WRITE || state->err != Z_OK) - return 0; - - /* since an int is returned, make sure len fits in one, otherwise return - with an error (this avoids a flaw in the interface) */ - if ((int)len < 0) { - gz_error(state, Z_DATA_ERROR, "requested length does not fit in int"); - return 0; - } - - /* write len bytes from buf (the return value will fit in an int) */ - return (int)gz_write(state, buf, len); -} - -/* -- see zlib.h -- */ -z_size_t ZEXPORT gzfwrite(voidpc buf, z_size_t size, z_size_t nitems, - gzFile file) { - z_size_t len; - gz_statep state; - - /* get internal structure */ - if (file == NULL) - return 0; - state = (gz_statep)file; - - /* check that we're writing and that there's no error */ - if (state->mode != GZ_WRITE || state->err != Z_OK) - return 0; - - /* compute bytes to read -- error on overflow */ - len = nitems * size; - if (size && len / size != nitems) { - gz_error(state, Z_STREAM_ERROR, "request does not fit in a size_t"); - return 0; - } - - /* write len bytes to buf, return the number of full items written */ - return len ? gz_write(state, buf, len) / size : 0; -} - -/* -- see zlib.h -- */ -int ZEXPORT gzputc(gzFile file, int c) { - unsigned have; - unsigned char buf[1]; - gz_statep state; - z_streamp strm; - - /* get internal structure */ - if (file == NULL) - return -1; - state = (gz_statep)file; - strm = &(state->strm); - - /* check that we're writing and that there's no error */ - if (state->mode != GZ_WRITE || state->err != Z_OK) - return -1; - - /* check for seek request */ - if (state->seek) { - state->seek = 0; - if (gz_zero(state, state->skip) == -1) - return -1; - } - - /* try writing to input buffer for speed (state->size == 0 if buffer not - initialized) */ - if (state->size) { - if (strm->avail_in == 0) - strm->next_in = state->in; - have = (unsigned)((strm->next_in + strm->avail_in) - state->in); - if (have < state->size) { - state->in[have] = (unsigned char)c; - strm->avail_in++; - state->x.pos++; - return c & 0xff; - } - } - - /* no room in buffer or not initialized, use gz_write() */ - buf[0] = (unsigned char)c; - if (gz_write(state, buf, 1) != 1) - return -1; - return c & 0xff; -} - -/* -- see zlib.h -- */ -int ZEXPORT gzputs(gzFile file, const char *s) { - z_size_t len, put; - gz_statep state; - - /* get internal structure */ - if (file == NULL) - return -1; - state = (gz_statep)file; - - /* check that we're writing and that there's no error */ - if (state->mode != GZ_WRITE || state->err != Z_OK) - return -1; - - /* write string */ - len = strlen(s); - if ((int)len < 0 || (unsigned)len != len) { - gz_error(state, Z_STREAM_ERROR, "string length does not fit in int"); - return -1; - } - put = gz_write(state, s, len); - return put < len ? -1 : (int)len; -} - -#if defined(STDC) || defined(Z_HAVE_STDARG_H) -#include - -/* -- see zlib.h -- */ -int ZEXPORTVA gzvprintf(gzFile file, const char *format, va_list va) { - int len; - unsigned left; - char *next; - gz_statep state; - z_streamp strm; - - /* get internal structure */ - if (file == NULL) - return Z_STREAM_ERROR; - state = (gz_statep)file; - strm = &(state->strm); - - /* check that we're writing and that there's no error */ - if (state->mode != GZ_WRITE || state->err != Z_OK) - return Z_STREAM_ERROR; - - /* make sure we have some buffer space */ - if (state->size == 0 && gz_init(state) == -1) - return state->err; - - /* check for seek request */ - if (state->seek) { - state->seek = 0; - if (gz_zero(state, state->skip) == -1) - return state->err; - } - - /* do the printf() into the input buffer, put length in len -- the input - buffer is double-sized just for this function, so there is guaranteed to - be state->size bytes available after the current contents */ - if (strm->avail_in == 0) - strm->next_in = state->in; - next = (char *)(state->in + (strm->next_in - state->in) + strm->avail_in); - next[state->size - 1] = 0; -#ifdef NO_vsnprintf -# ifdef HAS_vsprintf_void - (void)vsprintf(next, format, va); - for (len = 0; len < state->size; len++) - if (next[len] == 0) break; -# else - len = vsprintf(next, format, va); -# endif -#else -# ifdef HAS_vsnprintf_void - (void)vsnprintf(next, state->size, format, va); - len = strlen(next); -# else - len = vsnprintf(next, state->size, format, va); -# endif -#endif - - /* check that printf() results fit in buffer */ - if (len == 0 || (unsigned)len >= state->size || next[state->size - 1] != 0) - return 0; - - /* update buffer and position, compress first half if past that */ - strm->avail_in += (unsigned)len; - state->x.pos += len; - if (strm->avail_in >= state->size) { - left = strm->avail_in - state->size; - strm->avail_in = state->size; - if (gz_comp(state, Z_NO_FLUSH) == -1) - return state->err; - memmove(state->in, state->in + state->size, left); - strm->next_in = state->in; - strm->avail_in = left; - } - return len; -} - -int ZEXPORTVA gzprintf(gzFile file, const char *format, ...) { - va_list va; - int ret; - -// needed on Win64 - MS __va_start intrinsic not supported by importC yet -#if __IMPORTC__ - __builtin_va_start(va, format); -#else - va_start(va, format); -#endif - ret = gzvprintf(file, format, va); - va_end(va); - return ret; -} - -#else /* !STDC && !Z_HAVE_STDARG_H */ - -/* -- see zlib.h -- */ -int ZEXPORTVA gzprintf(gzFile file, const char *format, int a1, int a2, int a3, - int a4, int a5, int a6, int a7, int a8, int a9, int a10, - int a11, int a12, int a13, int a14, int a15, int a16, - int a17, int a18, int a19, int a20) { - unsigned len, left; - char *next; - gz_statep state; - z_streamp strm; - - /* get internal structure */ - if (file == NULL) - return Z_STREAM_ERROR; - state = (gz_statep)file; - strm = &(state->strm); - - /* check that can really pass pointer in ints */ - if (sizeof(int) != sizeof(void *)) - return Z_STREAM_ERROR; - - /* check that we're writing and that there's no error */ - if (state->mode != GZ_WRITE || state->err != Z_OK) - return Z_STREAM_ERROR; - - /* make sure we have some buffer space */ - if (state->size == 0 && gz_init(state) == -1) - return state->error; - - /* check for seek request */ - if (state->seek) { - state->seek = 0; - if (gz_zero(state, state->skip) == -1) - return state->error; - } - - /* do the printf() into the input buffer, put length in len -- the input - buffer is double-sized just for this function, so there is guaranteed to - be state->size bytes available after the current contents */ - if (strm->avail_in == 0) - strm->next_in = state->in; - next = (char *)(strm->next_in + strm->avail_in); - next[state->size - 1] = 0; -#ifdef NO_snprintf -# ifdef HAS_sprintf_void - sprintf(next, format, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, - a13, a14, a15, a16, a17, a18, a19, a20); - for (len = 0; len < size; len++) - if (next[len] == 0) - break; -# else - len = sprintf(next, format, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, - a12, a13, a14, a15, a16, a17, a18, a19, a20); -# endif -#else -# ifdef HAS_snprintf_void - snprintf(next, state->size, format, a1, a2, a3, a4, a5, a6, a7, a8, a9, - a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20); - len = strlen(next); -# else - len = snprintf(next, state->size, format, a1, a2, a3, a4, a5, a6, a7, a8, - a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20); -# endif -#endif - - /* check that printf() results fit in buffer */ - if (len == 0 || len >= state->size || next[state->size - 1] != 0) - return 0; - - /* update buffer and position, compress first half if past that */ - strm->avail_in += len; - state->x.pos += len; - if (strm->avail_in >= state->size) { - left = strm->avail_in - state->size; - strm->avail_in = state->size; - if (gz_comp(state, Z_NO_FLUSH) == -1) - return state->err; - memmove(state->in, state->in + state->size, left); - strm->next_in = state->in; - strm->avail_in = left; - } - return (int)len; -} - -#endif - -/* -- see zlib.h -- */ -int ZEXPORT gzflush(gzFile file, int flush) { - gz_statep state; - - /* get internal structure */ - if (file == NULL) - return Z_STREAM_ERROR; - state = (gz_statep)file; - - /* check that we're writing and that there's no error */ - if (state->mode != GZ_WRITE || state->err != Z_OK) - return Z_STREAM_ERROR; - - /* check flush parameter */ - if (flush < 0 || flush > Z_FINISH) - return Z_STREAM_ERROR; - - /* check for seek request */ - if (state->seek) { - state->seek = 0; - if (gz_zero(state, state->skip) == -1) - return state->err; - } - - /* compress remaining data with requested flush */ - (void)gz_comp(state, flush); - return state->err; -} - -/* -- see zlib.h -- */ -int ZEXPORT gzsetparams(gzFile file, int level, int strategy) { - gz_statep state; - z_streamp strm; - - /* get internal structure */ - if (file == NULL) - return Z_STREAM_ERROR; - state = (gz_statep)file; - strm = &(state->strm); - - /* check that we're writing and that there's no error */ - if (state->mode != GZ_WRITE || state->err != Z_OK || state->direct) - return Z_STREAM_ERROR; - - /* if no change is requested, then do nothing */ - if (level == state->level && strategy == state->strategy) - return Z_OK; - - /* check for seek request */ - if (state->seek) { - state->seek = 0; - if (gz_zero(state, state->skip) == -1) - return state->err; - } - - /* change compression parameters for subsequent input */ - if (state->size) { - /* flush previous input with previous parameters before changing */ - if (strm->avail_in && gz_comp(state, Z_BLOCK) == -1) - return state->err; - deflateParams(strm, level, strategy); - } - state->level = level; - state->strategy = strategy; - return Z_OK; -} - -/* -- see zlib.h -- */ -int ZEXPORT gzclose_w(gzFile file) { - int ret = Z_OK; - gz_statep state; - - /* get internal structure */ - if (file == NULL) - return Z_STREAM_ERROR; - state = (gz_statep)file; - - /* check that we're writing */ - if (state->mode != GZ_WRITE) - return Z_STREAM_ERROR; - - /* check for seek request */ - if (state->seek) { - state->seek = 0; - if (gz_zero(state, state->skip) == -1) - ret = state->err; - } - - /* flush, free memory, and close file */ - if (gz_comp(state, Z_FINISH) == -1) - ret = state->err; - if (state->size) { - if (!state->direct) { - (void)deflateEnd(&(state->strm)); - free(state->out); - } - free(state->in); - } - gz_error(state, Z_OK, NULL); - free(state->path); - if (close(state->fd) == -1) - ret = Z_ERRNO; - free(state); - return ret; -} diff --git a/phobos/etc/c/zlib/infback.c b/phobos/etc/c/zlib/infback.c deleted file mode 100644 index e7b25b3..0000000 --- a/phobos/etc/c/zlib/infback.c +++ /dev/null @@ -1,628 +0,0 @@ -/* infback.c -- inflate using a call-back interface - * Copyright (C) 1995-2022 Mark Adler - * For conditions of distribution and use, see copyright notice in zlib.h - */ - -/* - This code is largely copied from inflate.c. Normally either infback.o or - inflate.o would be linked into an application--not both. The interface - with inffast.c is retained so that optimized assembler-coded versions of - inflate_fast() can be used with either inflate.c or infback.c. - */ - -#include "zutil.h" -#include "inftrees.h" -#include "inflate.h" -#include "inffast.h" - -/* - strm provides memory allocation functions in zalloc and zfree, or - Z_NULL to use the library memory allocation functions. - - windowBits is in the range 8..15, and window is a user-supplied - window and output buffer that is 2**windowBits bytes. - */ -int ZEXPORT inflateBackInit_(z_streamp strm, int windowBits, - unsigned char FAR *window, const char *version, - int stream_size) { - struct inflate_state FAR *state; - - if (version == Z_NULL || version[0] != ZLIB_VERSION[0] || - stream_size != (int)(sizeof(z_stream))) - return Z_VERSION_ERROR; - if (strm == Z_NULL || window == Z_NULL || - windowBits < 8 || windowBits > 15) - return Z_STREAM_ERROR; - strm->msg = Z_NULL; /* in case we return an error */ - if (strm->zalloc == (alloc_func)0) { -#ifdef Z_SOLO - return Z_STREAM_ERROR; -#else - strm->zalloc = zcalloc; - strm->opaque = (voidpf)0; -#endif - } - if (strm->zfree == (free_func)0) -#ifdef Z_SOLO - return Z_STREAM_ERROR; -#else - strm->zfree = zcfree; -#endif - state = (struct inflate_state FAR *)ZALLOC(strm, 1, - sizeof(struct inflate_state)); - if (state == Z_NULL) return Z_MEM_ERROR; - Tracev((stderr, "inflate: allocated\n")); - strm->state = (struct internal_state FAR *)state; - state->dmax = 32768U; - state->wbits = (uInt)windowBits; - state->wsize = 1U << windowBits; - state->window = window; - state->wnext = 0; - state->whave = 0; - state->sane = 1; - return Z_OK; -} - -/* - Return state with length and distance decoding tables and index sizes set to - fixed code decoding. Normally this returns fixed tables from inffixed.h. - If BUILDFIXED is defined, then instead this routine builds the tables the - first time it's called, and returns those tables the first time and - thereafter. This reduces the size of the code by about 2K bytes, in - exchange for a little execution time. However, BUILDFIXED should not be - used for threaded applications, since the rewriting of the tables and virgin - may not be thread-safe. - */ -local void fixedtables(struct inflate_state FAR *state) { -#ifdef BUILDFIXED - static int virgin = 1; - static code *lenfix, *distfix; - static code fixed[544]; - - /* build fixed huffman tables if first call (may not be thread safe) */ - if (virgin) { - unsigned sym, bits; - static code *next; - - /* literal/length table */ - sym = 0; - while (sym < 144) state->lens[sym++] = 8; - while (sym < 256) state->lens[sym++] = 9; - while (sym < 280) state->lens[sym++] = 7; - while (sym < 288) state->lens[sym++] = 8; - next = fixed; - lenfix = next; - bits = 9; - inflate_table(LENS, state->lens, 288, &(next), &(bits), state->work); - - /* distance table */ - sym = 0; - while (sym < 32) state->lens[sym++] = 5; - distfix = next; - bits = 5; - inflate_table(DISTS, state->lens, 32, &(next), &(bits), state->work); - - /* do this just once */ - virgin = 0; - } -#else /* !BUILDFIXED */ -# include "inffixed.h" -#endif /* BUILDFIXED */ - state->lencode = lenfix; - state->lenbits = 9; - state->distcode = distfix; - state->distbits = 5; -} - -/* Macros for inflateBack(): */ - -/* Load returned state from inflate_fast() */ -#define LOAD() \ - do { \ - put = strm->next_out; \ - left = strm->avail_out; \ - next = strm->next_in; \ - have = strm->avail_in; \ - hold = state->hold; \ - bits = state->bits; \ - } while (0) - -/* Set state from registers for inflate_fast() */ -#define RESTORE() \ - do { \ - strm->next_out = put; \ - strm->avail_out = left; \ - strm->next_in = next; \ - strm->avail_in = have; \ - state->hold = hold; \ - state->bits = bits; \ - } while (0) - -/* Clear the input bit accumulator */ -#define INITBITS() \ - do { \ - hold = 0; \ - bits = 0; \ - } while (0) - -/* Assure that some input is available. If input is requested, but denied, - then return a Z_BUF_ERROR from inflateBack(). */ -#define PULL() \ - do { \ - if (have == 0) { \ - have = in(in_desc, &next); \ - if (have == 0) { \ - next = Z_NULL; \ - ret = Z_BUF_ERROR; \ - goto inf_leave; \ - } \ - } \ - } while (0) - -/* Get a byte of input into the bit accumulator, or return from inflateBack() - with an error if there is no input available. */ -#define PULLBYTE() \ - do { \ - PULL(); \ - have--; \ - hold += (unsigned long)(*next++) << bits; \ - bits += 8; \ - } while (0) - -/* Assure that there are at least n bits in the bit accumulator. If there is - not enough available input to do that, then return from inflateBack() with - an error. */ -#define NEEDBITS(n) \ - do { \ - while (bits < (unsigned)(n)) \ - PULLBYTE(); \ - } while (0) - -/* Return the low n bits of the bit accumulator (n < 16) */ -#define BITS(n) \ - ((unsigned)hold & ((1U << (n)) - 1)) - -/* Remove n bits from the bit accumulator */ -#define DROPBITS(n) \ - do { \ - hold >>= (n); \ - bits -= (unsigned)(n); \ - } while (0) - -/* Remove zero to seven bits as needed to go to a byte boundary */ -#define BYTEBITS() \ - do { \ - hold >>= bits & 7; \ - bits -= bits & 7; \ - } while (0) - -/* Assure that some output space is available, by writing out the window - if it's full. If the write fails, return from inflateBack() with a - Z_BUF_ERROR. */ -#define ROOM() \ - do { \ - if (left == 0) { \ - put = state->window; \ - left = state->wsize; \ - state->whave = left; \ - if (out(out_desc, put, left)) { \ - ret = Z_BUF_ERROR; \ - goto inf_leave; \ - } \ - } \ - } while (0) - -/* - strm provides the memory allocation functions and window buffer on input, - and provides information on the unused input on return. For Z_DATA_ERROR - returns, strm will also provide an error message. - - in() and out() are the call-back input and output functions. When - inflateBack() needs more input, it calls in(). When inflateBack() has - filled the window with output, or when it completes with data in the - window, it calls out() to write out the data. The application must not - change the provided input until in() is called again or inflateBack() - returns. The application must not change the window/output buffer until - inflateBack() returns. - - in() and out() are called with a descriptor parameter provided in the - inflateBack() call. This parameter can be a structure that provides the - information required to do the read or write, as well as accumulated - information on the input and output such as totals and check values. - - in() should return zero on failure. out() should return non-zero on - failure. If either in() or out() fails, than inflateBack() returns a - Z_BUF_ERROR. strm->next_in can be checked for Z_NULL to see whether it - was in() or out() that caused in the error. Otherwise, inflateBack() - returns Z_STREAM_END on success, Z_DATA_ERROR for an deflate format - error, or Z_MEM_ERROR if it could not allocate memory for the state. - inflateBack() can also return Z_STREAM_ERROR if the input parameters - are not correct, i.e. strm is Z_NULL or the state was not initialized. - */ -int ZEXPORT inflateBack(z_streamp strm, in_func in, void FAR *in_desc, - out_func out, void FAR *out_desc) { - struct inflate_state FAR *state; - z_const unsigned char FAR *next; /* next input */ - unsigned char FAR *put; /* next output */ - unsigned have, left; /* available input and output */ - unsigned long hold; /* bit buffer */ - unsigned bits; /* bits in bit buffer */ - unsigned copy; /* number of stored or match bytes to copy */ - unsigned char FAR *from; /* where to copy match bytes from */ - code here; /* current decoding table entry */ - code last; /* parent table entry */ - unsigned len; /* length to copy for repeats, bits to drop */ - int ret; /* return code */ - static const unsigned short order[19] = /* permutation of code lengths */ - {16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15}; - - /* Check that the strm exists and that the state was initialized */ - if (strm == Z_NULL || strm->state == Z_NULL) - return Z_STREAM_ERROR; - state = (struct inflate_state FAR *)strm->state; - - /* Reset the state */ - strm->msg = Z_NULL; - state->mode = TYPE; - state->last = 0; - state->whave = 0; - next = strm->next_in; - have = next != Z_NULL ? strm->avail_in : 0; - hold = 0; - bits = 0; - put = state->window; - left = state->wsize; - - /* Inflate until end of block marked as last */ - for (;;) - switch (state->mode) { - case TYPE: - /* determine and dispatch block type */ - if (state->last) { - BYTEBITS(); - state->mode = DONE; - break; - } - NEEDBITS(3); - state->last = BITS(1); - DROPBITS(1); - switch (BITS(2)) { - case 0: /* stored block */ - Tracev((stderr, "inflate: stored block%s\n", - state->last ? " (last)" : "")); - state->mode = STORED; - break; - case 1: /* fixed block */ - fixedtables(state); - Tracev((stderr, "inflate: fixed codes block%s\n", - state->last ? " (last)" : "")); - state->mode = LEN; /* decode codes */ - break; - case 2: /* dynamic block */ - Tracev((stderr, "inflate: dynamic codes block%s\n", - state->last ? " (last)" : "")); - state->mode = TABLE; - break; - case 3: - strm->msg = (char *)"invalid block type"; - state->mode = BAD; - } - DROPBITS(2); - break; - - case STORED: - /* get and verify stored block length */ - BYTEBITS(); /* go to byte boundary */ - NEEDBITS(32); - if ((hold & 0xffff) != ((hold >> 16) ^ 0xffff)) { - strm->msg = (char *)"invalid stored block lengths"; - state->mode = BAD; - break; - } - state->length = (unsigned)hold & 0xffff; - Tracev((stderr, "inflate: stored length %u\n", - state->length)); - INITBITS(); - - /* copy stored block from input to output */ - while (state->length != 0) { - copy = state->length; - PULL(); - ROOM(); - if (copy > have) copy = have; - if (copy > left) copy = left; - zmemcpy(put, next, copy); - have -= copy; - next += copy; - left -= copy; - put += copy; - state->length -= copy; - } - Tracev((stderr, "inflate: stored end\n")); - state->mode = TYPE; - break; - - case TABLE: - /* get dynamic table entries descriptor */ - NEEDBITS(14); - state->nlen = BITS(5) + 257; - DROPBITS(5); - state->ndist = BITS(5) + 1; - DROPBITS(5); - state->ncode = BITS(4) + 4; - DROPBITS(4); -#ifndef PKZIP_BUG_WORKAROUND - if (state->nlen > 286 || state->ndist > 30) { - strm->msg = (char *)"too many length or distance symbols"; - state->mode = BAD; - break; - } -#endif - Tracev((stderr, "inflate: table sizes ok\n")); - - /* get code length code lengths (not a typo) */ - state->have = 0; - while (state->have < state->ncode) { - NEEDBITS(3); - state->lens[order[state->have++]] = (unsigned short)BITS(3); - DROPBITS(3); - } - while (state->have < 19) - state->lens[order[state->have++]] = 0; - state->next = state->codes; - state->lencode = (code const FAR *)(state->next); - state->lenbits = 7; - ret = inflate_table(CODES, state->lens, 19, &(state->next), - &(state->lenbits), state->work); - if (ret) { - strm->msg = (char *)"invalid code lengths set"; - state->mode = BAD; - break; - } - Tracev((stderr, "inflate: code lengths ok\n")); - - /* get length and distance code code lengths */ - state->have = 0; - while (state->have < state->nlen + state->ndist) { - for (;;) { - here = state->lencode[BITS(state->lenbits)]; - if ((unsigned)(here.bits) <= bits) break; - PULLBYTE(); - } - if (here.val < 16) { - DROPBITS(here.bits); - state->lens[state->have++] = here.val; - } - else { - if (here.val == 16) { - NEEDBITS(here.bits + 2); - DROPBITS(here.bits); - if (state->have == 0) { - strm->msg = (char *)"invalid bit length repeat"; - state->mode = BAD; - break; - } - len = (unsigned)(state->lens[state->have - 1]); - copy = 3 + BITS(2); - DROPBITS(2); - } - else if (here.val == 17) { - NEEDBITS(here.bits + 3); - DROPBITS(here.bits); - len = 0; - copy = 3 + BITS(3); - DROPBITS(3); - } - else { - NEEDBITS(here.bits + 7); - DROPBITS(here.bits); - len = 0; - copy = 11 + BITS(7); - DROPBITS(7); - } - if (state->have + copy > state->nlen + state->ndist) { - strm->msg = (char *)"invalid bit length repeat"; - state->mode = BAD; - break; - } - while (copy--) - state->lens[state->have++] = (unsigned short)len; - } - } - - /* handle error breaks in while */ - if (state->mode == BAD) break; - - /* check for end-of-block code (better have one) */ - if (state->lens[256] == 0) { - strm->msg = (char *)"invalid code -- missing end-of-block"; - state->mode = BAD; - break; - } - - /* build code tables -- note: do not change the lenbits or distbits - values here (9 and 6) without reading the comments in inftrees.h - concerning the ENOUGH constants, which depend on those values */ - state->next = state->codes; - state->lencode = (code const FAR *)(state->next); - state->lenbits = 9; - ret = inflate_table(LENS, state->lens, state->nlen, &(state->next), - &(state->lenbits), state->work); - if (ret) { - strm->msg = (char *)"invalid literal/lengths set"; - state->mode = BAD; - break; - } - state->distcode = (code const FAR *)(state->next); - state->distbits = 6; - ret = inflate_table(DISTS, state->lens + state->nlen, state->ndist, - &(state->next), &(state->distbits), state->work); - if (ret) { - strm->msg = (char *)"invalid distances set"; - state->mode = BAD; - break; - } - Tracev((stderr, "inflate: codes ok\n")); - state->mode = LEN; - /* fallthrough */ - - case LEN: - /* use inflate_fast() if we have enough input and output */ - if (have >= 6 && left >= 258) { - RESTORE(); - if (state->whave < state->wsize) - state->whave = state->wsize - left; - inflate_fast(strm, state->wsize); - LOAD(); - break; - } - - /* get a literal, length, or end-of-block code */ - for (;;) { - here = state->lencode[BITS(state->lenbits)]; - if ((unsigned)(here.bits) <= bits) break; - PULLBYTE(); - } - if (here.op && (here.op & 0xf0) == 0) { - last = here; - for (;;) { - here = state->lencode[last.val + - (BITS(last.bits + last.op) >> last.bits)]; - if ((unsigned)(last.bits + here.bits) <= bits) break; - PULLBYTE(); - } - DROPBITS(last.bits); - } - DROPBITS(here.bits); - state->length = (unsigned)here.val; - - /* process literal */ - if (here.op == 0) { - Tracevv((stderr, here.val >= 0x20 && here.val < 0x7f ? - "inflate: literal '%c'\n" : - "inflate: literal 0x%02x\n", here.val)); - ROOM(); - *put++ = (unsigned char)(state->length); - left--; - state->mode = LEN; - break; - } - - /* process end of block */ - if (here.op & 32) { - Tracevv((stderr, "inflate: end of block\n")); - state->mode = TYPE; - break; - } - - /* invalid code */ - if (here.op & 64) { - strm->msg = (char *)"invalid literal/length code"; - state->mode = BAD; - break; - } - - /* length code -- get extra bits, if any */ - state->extra = (unsigned)(here.op) & 15; - if (state->extra != 0) { - NEEDBITS(state->extra); - state->length += BITS(state->extra); - DROPBITS(state->extra); - } - Tracevv((stderr, "inflate: length %u\n", state->length)); - - /* get distance code */ - for (;;) { - here = state->distcode[BITS(state->distbits)]; - if ((unsigned)(here.bits) <= bits) break; - PULLBYTE(); - } - if ((here.op & 0xf0) == 0) { - last = here; - for (;;) { - here = state->distcode[last.val + - (BITS(last.bits + last.op) >> last.bits)]; - if ((unsigned)(last.bits + here.bits) <= bits) break; - PULLBYTE(); - } - DROPBITS(last.bits); - } - DROPBITS(here.bits); - if (here.op & 64) { - strm->msg = (char *)"invalid distance code"; - state->mode = BAD; - break; - } - state->offset = (unsigned)here.val; - - /* get distance extra bits, if any */ - state->extra = (unsigned)(here.op) & 15; - if (state->extra != 0) { - NEEDBITS(state->extra); - state->offset += BITS(state->extra); - DROPBITS(state->extra); - } - if (state->offset > state->wsize - (state->whave < state->wsize ? - left : 0)) { - strm->msg = (char *)"invalid distance too far back"; - state->mode = BAD; - break; - } - Tracevv((stderr, "inflate: distance %u\n", state->offset)); - - /* copy match from window to output */ - do { - ROOM(); - copy = state->wsize - state->offset; - if (copy < left) { - from = put + copy; - copy = left - copy; - } - else { - from = put - state->offset; - copy = left; - } - if (copy > state->length) copy = state->length; - state->length -= copy; - left -= copy; - do { - *put++ = *from++; - } while (--copy); - } while (state->length != 0); - break; - - case DONE: - /* inflate stream terminated properly */ - ret = Z_STREAM_END; - goto inf_leave; - - case BAD: - ret = Z_DATA_ERROR; - goto inf_leave; - - default: - /* can't happen, but makes compilers happy */ - ret = Z_STREAM_ERROR; - goto inf_leave; - } - - /* Write leftover output and return unused input */ - inf_leave: - if (left < state->wsize) { - if (out(out_desc, state->window, state->wsize - left) && - ret == Z_STREAM_END) - ret = Z_BUF_ERROR; - } - strm->next_in = next; - strm->avail_in = have; - return ret; -} - -int ZEXPORT inflateBackEnd(z_streamp strm) { - if (strm == Z_NULL || strm->state == Z_NULL || strm->zfree == (free_func)0) - return Z_STREAM_ERROR; - ZFREE(strm, strm->state); - strm->state = Z_NULL; - Tracev((stderr, "inflate: end\n")); - return Z_OK; -} diff --git a/phobos/etc/c/zlib/inffast.c b/phobos/etc/c/zlib/inffast.c deleted file mode 100644 index 9354676..0000000 --- a/phobos/etc/c/zlib/inffast.c +++ /dev/null @@ -1,320 +0,0 @@ -/* inffast.c -- fast decoding - * Copyright (C) 1995-2017 Mark Adler - * For conditions of distribution and use, see copyright notice in zlib.h - */ - -#include "zutil.h" -#include "inftrees.h" -#include "inflate.h" -#include "inffast.h" - -#ifdef ASMINF -# pragma message("Assembler code may have bugs -- use at your own risk") -#else - -/* - Decode literal, length, and distance codes and write out the resulting - literal and match bytes until either not enough input or output is - available, an end-of-block is encountered, or a data error is encountered. - When large enough input and output buffers are supplied to inflate(), for - example, a 16K input buffer and a 64K output buffer, more than 95% of the - inflate execution time is spent in this routine. - - Entry assumptions: - - state->mode == LEN - strm->avail_in >= 6 - strm->avail_out >= 258 - start >= strm->avail_out - state->bits < 8 - - On return, state->mode is one of: - - LEN -- ran out of enough output space or enough available input - TYPE -- reached end of block code, inflate() to interpret next block - BAD -- error in block data - - Notes: - - - The maximum input bits used by a length/distance pair is 15 bits for the - length code, 5 bits for the length extra, 15 bits for the distance code, - and 13 bits for the distance extra. This totals 48 bits, or six bytes. - Therefore if strm->avail_in >= 6, then there is enough input to avoid - checking for available input while decoding. - - - The maximum bytes that a single length/distance pair can output is 258 - bytes, which is the maximum length that can be coded. inflate_fast() - requires strm->avail_out >= 258 for each loop to avoid checking for - output space. - */ -void ZLIB_INTERNAL inflate_fast(z_streamp strm, unsigned start) { - struct inflate_state FAR *state; - z_const unsigned char FAR *in; /* local strm->next_in */ - z_const unsigned char FAR *last; /* have enough input while in < last */ - unsigned char FAR *out; /* local strm->next_out */ - unsigned char FAR *beg; /* inflate()'s initial strm->next_out */ - unsigned char FAR *end; /* while out < end, enough space available */ -#ifdef INFLATE_STRICT - unsigned dmax; /* maximum distance from zlib header */ -#endif - unsigned wsize; /* window size or zero if not using window */ - unsigned whave; /* valid bytes in the window */ - unsigned wnext; /* window write index */ - unsigned char FAR *window; /* allocated sliding window, if wsize != 0 */ - unsigned long hold; /* local strm->hold */ - unsigned bits; /* local strm->bits */ - code const FAR *lcode; /* local strm->lencode */ - code const FAR *dcode; /* local strm->distcode */ - unsigned lmask; /* mask for first level of length codes */ - unsigned dmask; /* mask for first level of distance codes */ - code const *here; /* retrieved table entry */ - unsigned op; /* code bits, operation, extra bits, or */ - /* window position, window bytes to copy */ - unsigned len; /* match length, unused bytes */ - unsigned dist; /* match distance */ - unsigned char FAR *from; /* where to copy match from */ - - /* copy state to local variables */ - state = (struct inflate_state FAR *)strm->state; - in = strm->next_in; - last = in + (strm->avail_in - 5); - out = strm->next_out; - beg = out - (start - strm->avail_out); - end = out + (strm->avail_out - 257); -#ifdef INFLATE_STRICT - dmax = state->dmax; -#endif - wsize = state->wsize; - whave = state->whave; - wnext = state->wnext; - window = state->window; - hold = state->hold; - bits = state->bits; - lcode = state->lencode; - dcode = state->distcode; - lmask = (1U << state->lenbits) - 1; - dmask = (1U << state->distbits) - 1; - - /* decode literals and length/distances until end-of-block or not enough - input data or output space */ - do { - if (bits < 15) { - hold += (unsigned long)(*in++) << bits; - bits += 8; - hold += (unsigned long)(*in++) << bits; - bits += 8; - } - here = lcode + (hold & lmask); - dolen: - op = (unsigned)(here->bits); - hold >>= op; - bits -= op; - op = (unsigned)(here->op); - if (op == 0) { /* literal */ - Tracevv((stderr, here->val >= 0x20 && here->val < 0x7f ? - "inflate: literal '%c'\n" : - "inflate: literal 0x%02x\n", here->val)); - *out++ = (unsigned char)(here->val); - } - else if (op & 16) { /* length base */ - len = (unsigned)(here->val); - op &= 15; /* number of extra bits */ - if (op) { - if (bits < op) { - hold += (unsigned long)(*in++) << bits; - bits += 8; - } - len += (unsigned)hold & ((1U << op) - 1); - hold >>= op; - bits -= op; - } - Tracevv((stderr, "inflate: length %u\n", len)); - if (bits < 15) { - hold += (unsigned long)(*in++) << bits; - bits += 8; - hold += (unsigned long)(*in++) << bits; - bits += 8; - } - here = dcode + (hold & dmask); - dodist: - op = (unsigned)(here->bits); - hold >>= op; - bits -= op; - op = (unsigned)(here->op); - if (op & 16) { /* distance base */ - dist = (unsigned)(here->val); - op &= 15; /* number of extra bits */ - if (bits < op) { - hold += (unsigned long)(*in++) << bits; - bits += 8; - if (bits < op) { - hold += (unsigned long)(*in++) << bits; - bits += 8; - } - } - dist += (unsigned)hold & ((1U << op) - 1); -#ifdef INFLATE_STRICT - if (dist > dmax) { - strm->msg = (char *)"invalid distance too far back"; - state->mode = BAD; - break; - } -#endif - hold >>= op; - bits -= op; - Tracevv((stderr, "inflate: distance %u\n", dist)); - op = (unsigned)(out - beg); /* max distance in output */ - if (dist > op) { /* see if copy from window */ - op = dist - op; /* distance back in window */ - if (op > whave) { - if (state->sane) { - strm->msg = - (char *)"invalid distance too far back"; - state->mode = BAD; - break; - } -#ifdef INFLATE_ALLOW_INVALID_DISTANCE_TOOFAR_ARRR - if (len <= op - whave) { - do { - *out++ = 0; - } while (--len); - continue; - } - len -= op - whave; - do { - *out++ = 0; - } while (--op > whave); - if (op == 0) { - from = out - dist; - do { - *out++ = *from++; - } while (--len); - continue; - } -#endif - } - from = window; - if (wnext == 0) { /* very common case */ - from += wsize - op; - if (op < len) { /* some from window */ - len -= op; - do { - *out++ = *from++; - } while (--op); - from = out - dist; /* rest from output */ - } - } - else if (wnext < op) { /* wrap around window */ - from += wsize + wnext - op; - op -= wnext; - if (op < len) { /* some from end of window */ - len -= op; - do { - *out++ = *from++; - } while (--op); - from = window; - if (wnext < len) { /* some from start of window */ - op = wnext; - len -= op; - do { - *out++ = *from++; - } while (--op); - from = out - dist; /* rest from output */ - } - } - } - else { /* contiguous in window */ - from += wnext - op; - if (op < len) { /* some from window */ - len -= op; - do { - *out++ = *from++; - } while (--op); - from = out - dist; /* rest from output */ - } - } - while (len > 2) { - *out++ = *from++; - *out++ = *from++; - *out++ = *from++; - len -= 3; - } - if (len) { - *out++ = *from++; - if (len > 1) - *out++ = *from++; - } - } - else { - from = out - dist; /* copy direct from output */ - do { /* minimum length is three */ - *out++ = *from++; - *out++ = *from++; - *out++ = *from++; - len -= 3; - } while (len > 2); - if (len) { - *out++ = *from++; - if (len > 1) - *out++ = *from++; - } - } - } - else if ((op & 64) == 0) { /* 2nd level distance code */ - here = dcode + here->val + (hold & ((1U << op) - 1)); - goto dodist; - } - else { - strm->msg = (char *)"invalid distance code"; - state->mode = BAD; - break; - } - } - else if ((op & 64) == 0) { /* 2nd level length code */ - here = lcode + here->val + (hold & ((1U << op) - 1)); - goto dolen; - } - else if (op & 32) { /* end-of-block */ - Tracevv((stderr, "inflate: end of block\n")); - state->mode = TYPE; - break; - } - else { - strm->msg = (char *)"invalid literal/length code"; - state->mode = BAD; - break; - } - } while (in < last && out < end); - - /* return unused bytes (on entry, bits < 8, so in won't go too far back) */ - len = bits >> 3; - in -= len; - bits -= len << 3; - hold &= (1U << bits) - 1; - - /* update state and return */ - strm->next_in = in; - strm->next_out = out; - strm->avail_in = (unsigned)(in < last ? 5 + (last - in) : 5 - (in - last)); - strm->avail_out = (unsigned)(out < end ? - 257 + (end - out) : 257 - (out - end)); - state->hold = hold; - state->bits = bits; - return; -} - -/* - inflate_fast() speedups that turned out slower (on a PowerPC G3 750CXe): - - Using bit fields for code structure - - Different op definition to avoid & for extra bits (do & for table bits) - - Three separate decoding do-loops for direct, window, and wnext == 0 - - Special case for distance > 1 copies to do overlapped load and store copy - - Explicit branch predictions (based on measured branch probabilities) - - Deferring match copy and interspersed it with decoding subsequent codes - - Swapping literal/length else - - Swapping window/direct else - - Larger unrolled copy loops (three is about right) - - Moving len -= 3 statement into middle of loop - */ - -#endif /* !ASMINF */ diff --git a/phobos/etc/c/zlib/inffast.h b/phobos/etc/c/zlib/inffast.h deleted file mode 100644 index 49c6d15..0000000 --- a/phobos/etc/c/zlib/inffast.h +++ /dev/null @@ -1,11 +0,0 @@ -/* inffast.h -- header to use inffast.c - * Copyright (C) 1995-2003, 2010 Mark Adler - * For conditions of distribution and use, see copyright notice in zlib.h - */ - -/* WARNING: this file should *not* be used by applications. It is - part of the implementation of the compression library and is - subject to change. Applications should only use zlib.h. - */ - -void ZLIB_INTERNAL inflate_fast(z_streamp strm, unsigned start); diff --git a/phobos/etc/c/zlib/inffixed.h b/phobos/etc/c/zlib/inffixed.h deleted file mode 100644 index d628327..0000000 --- a/phobos/etc/c/zlib/inffixed.h +++ /dev/null @@ -1,94 +0,0 @@ - /* inffixed.h -- table for decoding fixed codes - * Generated automatically by makefixed(). - */ - - /* WARNING: this file should *not* be used by applications. - It is part of the implementation of this library and is - subject to change. Applications should only use zlib.h. - */ - - static const code lenfix[512] = { - {96,7,0},{0,8,80},{0,8,16},{20,8,115},{18,7,31},{0,8,112},{0,8,48}, - {0,9,192},{16,7,10},{0,8,96},{0,8,32},{0,9,160},{0,8,0},{0,8,128}, - {0,8,64},{0,9,224},{16,7,6},{0,8,88},{0,8,24},{0,9,144},{19,7,59}, - {0,8,120},{0,8,56},{0,9,208},{17,7,17},{0,8,104},{0,8,40},{0,9,176}, - {0,8,8},{0,8,136},{0,8,72},{0,9,240},{16,7,4},{0,8,84},{0,8,20}, - {21,8,227},{19,7,43},{0,8,116},{0,8,52},{0,9,200},{17,7,13},{0,8,100}, - {0,8,36},{0,9,168},{0,8,4},{0,8,132},{0,8,68},{0,9,232},{16,7,8}, - {0,8,92},{0,8,28},{0,9,152},{20,7,83},{0,8,124},{0,8,60},{0,9,216}, - {18,7,23},{0,8,108},{0,8,44},{0,9,184},{0,8,12},{0,8,140},{0,8,76}, - {0,9,248},{16,7,3},{0,8,82},{0,8,18},{21,8,163},{19,7,35},{0,8,114}, - {0,8,50},{0,9,196},{17,7,11},{0,8,98},{0,8,34},{0,9,164},{0,8,2}, - {0,8,130},{0,8,66},{0,9,228},{16,7,7},{0,8,90},{0,8,26},{0,9,148}, - {20,7,67},{0,8,122},{0,8,58},{0,9,212},{18,7,19},{0,8,106},{0,8,42}, - {0,9,180},{0,8,10},{0,8,138},{0,8,74},{0,9,244},{16,7,5},{0,8,86}, - {0,8,22},{64,8,0},{19,7,51},{0,8,118},{0,8,54},{0,9,204},{17,7,15}, - {0,8,102},{0,8,38},{0,9,172},{0,8,6},{0,8,134},{0,8,70},{0,9,236}, - {16,7,9},{0,8,94},{0,8,30},{0,9,156},{20,7,99},{0,8,126},{0,8,62}, - {0,9,220},{18,7,27},{0,8,110},{0,8,46},{0,9,188},{0,8,14},{0,8,142}, - {0,8,78},{0,9,252},{96,7,0},{0,8,81},{0,8,17},{21,8,131},{18,7,31}, - {0,8,113},{0,8,49},{0,9,194},{16,7,10},{0,8,97},{0,8,33},{0,9,162}, - {0,8,1},{0,8,129},{0,8,65},{0,9,226},{16,7,6},{0,8,89},{0,8,25}, - {0,9,146},{19,7,59},{0,8,121},{0,8,57},{0,9,210},{17,7,17},{0,8,105}, - {0,8,41},{0,9,178},{0,8,9},{0,8,137},{0,8,73},{0,9,242},{16,7,4}, - {0,8,85},{0,8,21},{16,8,258},{19,7,43},{0,8,117},{0,8,53},{0,9,202}, - {17,7,13},{0,8,101},{0,8,37},{0,9,170},{0,8,5},{0,8,133},{0,8,69}, - {0,9,234},{16,7,8},{0,8,93},{0,8,29},{0,9,154},{20,7,83},{0,8,125}, - {0,8,61},{0,9,218},{18,7,23},{0,8,109},{0,8,45},{0,9,186},{0,8,13}, - {0,8,141},{0,8,77},{0,9,250},{16,7,3},{0,8,83},{0,8,19},{21,8,195}, - {19,7,35},{0,8,115},{0,8,51},{0,9,198},{17,7,11},{0,8,99},{0,8,35}, - {0,9,166},{0,8,3},{0,8,131},{0,8,67},{0,9,230},{16,7,7},{0,8,91}, - {0,8,27},{0,9,150},{20,7,67},{0,8,123},{0,8,59},{0,9,214},{18,7,19}, - {0,8,107},{0,8,43},{0,9,182},{0,8,11},{0,8,139},{0,8,75},{0,9,246}, - {16,7,5},{0,8,87},{0,8,23},{64,8,0},{19,7,51},{0,8,119},{0,8,55}, - {0,9,206},{17,7,15},{0,8,103},{0,8,39},{0,9,174},{0,8,7},{0,8,135}, - {0,8,71},{0,9,238},{16,7,9},{0,8,95},{0,8,31},{0,9,158},{20,7,99}, - {0,8,127},{0,8,63},{0,9,222},{18,7,27},{0,8,111},{0,8,47},{0,9,190}, - {0,8,15},{0,8,143},{0,8,79},{0,9,254},{96,7,0},{0,8,80},{0,8,16}, - {20,8,115},{18,7,31},{0,8,112},{0,8,48},{0,9,193},{16,7,10},{0,8,96}, - {0,8,32},{0,9,161},{0,8,0},{0,8,128},{0,8,64},{0,9,225},{16,7,6}, - {0,8,88},{0,8,24},{0,9,145},{19,7,59},{0,8,120},{0,8,56},{0,9,209}, - {17,7,17},{0,8,104},{0,8,40},{0,9,177},{0,8,8},{0,8,136},{0,8,72}, - {0,9,241},{16,7,4},{0,8,84},{0,8,20},{21,8,227},{19,7,43},{0,8,116}, - {0,8,52},{0,9,201},{17,7,13},{0,8,100},{0,8,36},{0,9,169},{0,8,4}, - {0,8,132},{0,8,68},{0,9,233},{16,7,8},{0,8,92},{0,8,28},{0,9,153}, - {20,7,83},{0,8,124},{0,8,60},{0,9,217},{18,7,23},{0,8,108},{0,8,44}, - {0,9,185},{0,8,12},{0,8,140},{0,8,76},{0,9,249},{16,7,3},{0,8,82}, - {0,8,18},{21,8,163},{19,7,35},{0,8,114},{0,8,50},{0,9,197},{17,7,11}, - {0,8,98},{0,8,34},{0,9,165},{0,8,2},{0,8,130},{0,8,66},{0,9,229}, - {16,7,7},{0,8,90},{0,8,26},{0,9,149},{20,7,67},{0,8,122},{0,8,58}, - {0,9,213},{18,7,19},{0,8,106},{0,8,42},{0,9,181},{0,8,10},{0,8,138}, - {0,8,74},{0,9,245},{16,7,5},{0,8,86},{0,8,22},{64,8,0},{19,7,51}, - {0,8,118},{0,8,54},{0,9,205},{17,7,15},{0,8,102},{0,8,38},{0,9,173}, - {0,8,6},{0,8,134},{0,8,70},{0,9,237},{16,7,9},{0,8,94},{0,8,30}, - {0,9,157},{20,7,99},{0,8,126},{0,8,62},{0,9,221},{18,7,27},{0,8,110}, - {0,8,46},{0,9,189},{0,8,14},{0,8,142},{0,8,78},{0,9,253},{96,7,0}, - {0,8,81},{0,8,17},{21,8,131},{18,7,31},{0,8,113},{0,8,49},{0,9,195}, - {16,7,10},{0,8,97},{0,8,33},{0,9,163},{0,8,1},{0,8,129},{0,8,65}, - {0,9,227},{16,7,6},{0,8,89},{0,8,25},{0,9,147},{19,7,59},{0,8,121}, - {0,8,57},{0,9,211},{17,7,17},{0,8,105},{0,8,41},{0,9,179},{0,8,9}, - {0,8,137},{0,8,73},{0,9,243},{16,7,4},{0,8,85},{0,8,21},{16,8,258}, - {19,7,43},{0,8,117},{0,8,53},{0,9,203},{17,7,13},{0,8,101},{0,8,37}, - {0,9,171},{0,8,5},{0,8,133},{0,8,69},{0,9,235},{16,7,8},{0,8,93}, - {0,8,29},{0,9,155},{20,7,83},{0,8,125},{0,8,61},{0,9,219},{18,7,23}, - {0,8,109},{0,8,45},{0,9,187},{0,8,13},{0,8,141},{0,8,77},{0,9,251}, - {16,7,3},{0,8,83},{0,8,19},{21,8,195},{19,7,35},{0,8,115},{0,8,51}, - {0,9,199},{17,7,11},{0,8,99},{0,8,35},{0,9,167},{0,8,3},{0,8,131}, - {0,8,67},{0,9,231},{16,7,7},{0,8,91},{0,8,27},{0,9,151},{20,7,67}, - {0,8,123},{0,8,59},{0,9,215},{18,7,19},{0,8,107},{0,8,43},{0,9,183}, - {0,8,11},{0,8,139},{0,8,75},{0,9,247},{16,7,5},{0,8,87},{0,8,23}, - {64,8,0},{19,7,51},{0,8,119},{0,8,55},{0,9,207},{17,7,15},{0,8,103}, - {0,8,39},{0,9,175},{0,8,7},{0,8,135},{0,8,71},{0,9,239},{16,7,9}, - {0,8,95},{0,8,31},{0,9,159},{20,7,99},{0,8,127},{0,8,63},{0,9,223}, - {18,7,27},{0,8,111},{0,8,47},{0,9,191},{0,8,15},{0,8,143},{0,8,79}, - {0,9,255} - }; - - static const code distfix[32] = { - {16,5,1},{23,5,257},{19,5,17},{27,5,4097},{17,5,5},{25,5,1025}, - {21,5,65},{29,5,16385},{16,5,3},{24,5,513},{20,5,33},{28,5,8193}, - {18,5,9},{26,5,2049},{22,5,129},{64,5,0},{16,5,2},{23,5,385}, - {19,5,25},{27,5,6145},{17,5,7},{25,5,1537},{21,5,97},{29,5,24577}, - {16,5,4},{24,5,769},{20,5,49},{28,5,12289},{18,5,13},{26,5,3073}, - {22,5,193},{64,5,0} - }; diff --git a/phobos/etc/c/zlib/inflate.c b/phobos/etc/c/zlib/inflate.c deleted file mode 100644 index 94ecff0..0000000 --- a/phobos/etc/c/zlib/inflate.c +++ /dev/null @@ -1,1526 +0,0 @@ -/* inflate.c -- zlib decompression - * Copyright (C) 1995-2022 Mark Adler - * For conditions of distribution and use, see copyright notice in zlib.h - */ - -/* - * Change history: - * - * 1.2.beta0 24 Nov 2002 - * - First version -- complete rewrite of inflate to simplify code, avoid - * creation of window when not needed, minimize use of window when it is - * needed, make inffast.c even faster, implement gzip decoding, and to - * improve code readability and style over the previous zlib inflate code - * - * 1.2.beta1 25 Nov 2002 - * - Use pointers for available input and output checking in inffast.c - * - Remove input and output counters in inffast.c - * - Change inffast.c entry and loop from avail_in >= 7 to >= 6 - * - Remove unnecessary second byte pull from length extra in inffast.c - * - Unroll direct copy to three copies per loop in inffast.c - * - * 1.2.beta2 4 Dec 2002 - * - Change external routine names to reduce potential conflicts - * - Correct filename to inffixed.h for fixed tables in inflate.c - * - Make hbuf[] unsigned char to match parameter type in inflate.c - * - Change strm->next_out[-state->offset] to *(strm->next_out - state->offset) - * to avoid negation problem on Alphas (64 bit) in inflate.c - * - * 1.2.beta3 22 Dec 2002 - * - Add comments on state->bits assertion in inffast.c - * - Add comments on op field in inftrees.h - * - Fix bug in reuse of allocated window after inflateReset() - * - Remove bit fields--back to byte structure for speed - * - Remove distance extra == 0 check in inflate_fast()--only helps for lengths - * - Change post-increments to pre-increments in inflate_fast(), PPC biased? - * - Add compile time option, POSTINC, to use post-increments instead (Intel?) - * - Make MATCH copy in inflate() much faster for when inflate_fast() not used - * - Use local copies of stream next and avail values, as well as local bit - * buffer and bit count in inflate()--for speed when inflate_fast() not used - * - * 1.2.beta4 1 Jan 2003 - * - Split ptr - 257 statements in inflate_table() to avoid compiler warnings - * - Move a comment on output buffer sizes from inffast.c to inflate.c - * - Add comments in inffast.c to introduce the inflate_fast() routine - * - Rearrange window copies in inflate_fast() for speed and simplification - * - Unroll last copy for window match in inflate_fast() - * - Use local copies of window variables in inflate_fast() for speed - * - Pull out common wnext == 0 case for speed in inflate_fast() - * - Make op and len in inflate_fast() unsigned for consistency - * - Add FAR to lcode and dcode declarations in inflate_fast() - * - Simplified bad distance check in inflate_fast() - * - Added inflateBackInit(), inflateBack(), and inflateBackEnd() in new - * source file infback.c to provide a call-back interface to inflate for - * programs like gzip and unzip -- uses window as output buffer to avoid - * window copying - * - * 1.2.beta5 1 Jan 2003 - * - Improved inflateBack() interface to allow the caller to provide initial - * input in strm. - * - Fixed stored blocks bug in inflateBack() - * - * 1.2.beta6 4 Jan 2003 - * - Added comments in inffast.c on effectiveness of POSTINC - * - Typecasting all around to reduce compiler warnings - * - Changed loops from while (1) or do {} while (1) to for (;;), again to - * make compilers happy - * - Changed type of window in inflateBackInit() to unsigned char * - * - * 1.2.beta7 27 Jan 2003 - * - Changed many types to unsigned or unsigned short to avoid warnings - * - Added inflateCopy() function - * - * 1.2.0 9 Mar 2003 - * - Changed inflateBack() interface to provide separate opaque descriptors - * for the in() and out() functions - * - Changed inflateBack() argument and in_func typedef to swap the length - * and buffer address return values for the input function - * - Check next_in and next_out for Z_NULL on entry to inflate() - * - * The history for versions after 1.2.0 are in ChangeLog in zlib distribution. - */ - -#include "zutil.h" -#include "inftrees.h" -#include "inflate.h" -#include "inffast.h" - -#ifdef MAKEFIXED -# ifndef BUILDFIXED -# define BUILDFIXED -# endif -#endif - -local int inflateStateCheck(z_streamp strm) { - struct inflate_state FAR *state; - if (strm == Z_NULL || - strm->zalloc == (alloc_func)0 || strm->zfree == (free_func)0) - return 1; - state = (struct inflate_state FAR *)strm->state; - if (state == Z_NULL || state->strm != strm || - state->mode < HEAD || state->mode > SYNC) - return 1; - return 0; -} - -int ZEXPORT inflateResetKeep(z_streamp strm) { - struct inflate_state FAR *state; - - if (inflateStateCheck(strm)) return Z_STREAM_ERROR; - state = (struct inflate_state FAR *)strm->state; - strm->total_in = strm->total_out = state->total = 0; - strm->msg = Z_NULL; - if (state->wrap) /* to support ill-conceived Java test suite */ - strm->adler = state->wrap & 1; - state->mode = HEAD; - state->last = 0; - state->havedict = 0; - state->flags = -1; - state->dmax = 32768U; - state->head = Z_NULL; - state->hold = 0; - state->bits = 0; - state->lencode = state->distcode = state->next = state->codes; - state->sane = 1; - state->back = -1; - Tracev((stderr, "inflate: reset\n")); - return Z_OK; -} - -int ZEXPORT inflateReset(z_streamp strm) { - struct inflate_state FAR *state; - - if (inflateStateCheck(strm)) return Z_STREAM_ERROR; - state = (struct inflate_state FAR *)strm->state; - state->wsize = 0; - state->whave = 0; - state->wnext = 0; - return inflateResetKeep(strm); -} - -int ZEXPORT inflateReset2(z_streamp strm, int windowBits) { - int wrap; - struct inflate_state FAR *state; - - /* get the state */ - if (inflateStateCheck(strm)) return Z_STREAM_ERROR; - state = (struct inflate_state FAR *)strm->state; - - /* extract wrap request from windowBits parameter */ - if (windowBits < 0) { - if (windowBits < -15) - return Z_STREAM_ERROR; - wrap = 0; - windowBits = -windowBits; - } - else { - wrap = (windowBits >> 4) + 5; -#ifdef GUNZIP - if (windowBits < 48) - windowBits &= 15; -#endif - } - - /* set number of window bits, free window if different */ - if (windowBits && (windowBits < 8 || windowBits > 15)) - return Z_STREAM_ERROR; - if (state->window != Z_NULL && state->wbits != (unsigned)windowBits) { - ZFREE(strm, state->window); - state->window = Z_NULL; - } - - /* update state and reset the rest of it */ - state->wrap = wrap; - state->wbits = (unsigned)windowBits; - return inflateReset(strm); -} - -int ZEXPORT inflateInit2_(z_streamp strm, int windowBits, - const char *version, int stream_size) { - int ret; - struct inflate_state FAR *state; - - if (version == Z_NULL || version[0] != ZLIB_VERSION[0] || - stream_size != (int)(sizeof(z_stream))) - return Z_VERSION_ERROR; - if (strm == Z_NULL) return Z_STREAM_ERROR; - strm->msg = Z_NULL; /* in case we return an error */ - if (strm->zalloc == (alloc_func)0) { -#ifdef Z_SOLO - return Z_STREAM_ERROR; -#else - strm->zalloc = zcalloc; - strm->opaque = (voidpf)0; -#endif - } - if (strm->zfree == (free_func)0) -#ifdef Z_SOLO - return Z_STREAM_ERROR; -#else - strm->zfree = zcfree; -#endif - state = (struct inflate_state FAR *) - ZALLOC(strm, 1, sizeof(struct inflate_state)); - if (state == Z_NULL) return Z_MEM_ERROR; - Tracev((stderr, "inflate: allocated\n")); - strm->state = (struct internal_state FAR *)state; - state->strm = strm; - state->window = Z_NULL; - state->mode = HEAD; /* to pass state test in inflateReset2() */ - ret = inflateReset2(strm, windowBits); - if (ret != Z_OK) { - ZFREE(strm, state); - strm->state = Z_NULL; - } - return ret; -} - -int ZEXPORT inflateInit_(z_streamp strm, const char *version, - int stream_size) { - return inflateInit2_(strm, DEF_WBITS, version, stream_size); -} - -int ZEXPORT inflatePrime(z_streamp strm, int bits, int value) { - struct inflate_state FAR *state; - - if (inflateStateCheck(strm)) return Z_STREAM_ERROR; - if (bits == 0) - return Z_OK; - state = (struct inflate_state FAR *)strm->state; - if (bits < 0) { - state->hold = 0; - state->bits = 0; - return Z_OK; - } - if (bits > 16 || state->bits + (uInt)bits > 32) return Z_STREAM_ERROR; - value &= (1L << bits) - 1; - state->hold += (unsigned)value << state->bits; - state->bits += (uInt)bits; - return Z_OK; -} - -/* - Return state with length and distance decoding tables and index sizes set to - fixed code decoding. Normally this returns fixed tables from inffixed.h. - If BUILDFIXED is defined, then instead this routine builds the tables the - first time it's called, and returns those tables the first time and - thereafter. This reduces the size of the code by about 2K bytes, in - exchange for a little execution time. However, BUILDFIXED should not be - used for threaded applications, since the rewriting of the tables and virgin - may not be thread-safe. - */ -local void fixedtables(struct inflate_state FAR *state) { -#ifdef BUILDFIXED - static int virgin = 1; - static code *lenfix, *distfix; - static code fixed[544]; - - /* build fixed huffman tables if first call (may not be thread safe) */ - if (virgin) { - unsigned sym, bits; - static code *next; - - /* literal/length table */ - sym = 0; - while (sym < 144) state->lens[sym++] = 8; - while (sym < 256) state->lens[sym++] = 9; - while (sym < 280) state->lens[sym++] = 7; - while (sym < 288) state->lens[sym++] = 8; - next = fixed; - lenfix = next; - bits = 9; - inflate_table(LENS, state->lens, 288, &(next), &(bits), state->work); - - /* distance table */ - sym = 0; - while (sym < 32) state->lens[sym++] = 5; - distfix = next; - bits = 5; - inflate_table(DISTS, state->lens, 32, &(next), &(bits), state->work); - - /* do this just once */ - virgin = 0; - } -#else /* !BUILDFIXED */ -# include "inffixed.h" -#endif /* BUILDFIXED */ - state->lencode = lenfix; - state->lenbits = 9; - state->distcode = distfix; - state->distbits = 5; -} - -#ifdef MAKEFIXED -#include - -/* - Write out the inffixed.h that is #include'd above. Defining MAKEFIXED also - defines BUILDFIXED, so the tables are built on the fly. makefixed() writes - those tables to stdout, which would be piped to inffixed.h. A small program - can simply call makefixed to do this: - - void makefixed(void); - - int main(void) - { - makefixed(); - return 0; - } - - Then that can be linked with zlib built with MAKEFIXED defined and run: - - a.out > inffixed.h - */ -void makefixed(void) -{ - unsigned low, size; - struct inflate_state state; - - fixedtables(&state); - puts(" /* inffixed.h -- table for decoding fixed codes"); - puts(" * Generated automatically by makefixed()."); - puts(" */"); - puts(""); - puts(" /* WARNING: this file should *not* be used by applications."); - puts(" It is part of the implementation of this library and is"); - puts(" subject to change. Applications should only use zlib.h."); - puts(" */"); - puts(""); - size = 1U << 9; - printf(" static const code lenfix[%u] = {", size); - low = 0; - for (;;) { - if ((low % 7) == 0) printf("\n "); - printf("{%u,%u,%d}", (low & 127) == 99 ? 64 : state.lencode[low].op, - state.lencode[low].bits, state.lencode[low].val); - if (++low == size) break; - putchar(','); - } - puts("\n };"); - size = 1U << 5; - printf("\n static const code distfix[%u] = {", size); - low = 0; - for (;;) { - if ((low % 6) == 0) printf("\n "); - printf("{%u,%u,%d}", state.distcode[low].op, state.distcode[low].bits, - state.distcode[low].val); - if (++low == size) break; - putchar(','); - } - puts("\n };"); -} -#endif /* MAKEFIXED */ - -/* - Update the window with the last wsize (normally 32K) bytes written before - returning. If window does not exist yet, create it. This is only called - when a window is already in use, or when output has been written during this - inflate call, but the end of the deflate stream has not been reached yet. - It is also called to create a window for dictionary data when a dictionary - is loaded. - - Providing output buffers larger than 32K to inflate() should provide a speed - advantage, since only the last 32K of output is copied to the sliding window - upon return from inflate(), and since all distances after the first 32K of - output will fall in the output data, making match copies simpler and faster. - The advantage may be dependent on the size of the processor's data caches. - */ -local int updatewindow(z_streamp strm, const Bytef *end, unsigned copy) { - struct inflate_state FAR *state; - unsigned dist; - - state = (struct inflate_state FAR *)strm->state; - - /* if it hasn't been done already, allocate space for the window */ - if (state->window == Z_NULL) { - state->window = (unsigned char FAR *) - ZALLOC(strm, 1U << state->wbits, - sizeof(unsigned char)); - if (state->window == Z_NULL) return 1; - } - - /* if window not in use yet, initialize */ - if (state->wsize == 0) { - state->wsize = 1U << state->wbits; - state->wnext = 0; - state->whave = 0; - } - - /* copy state->wsize or less output bytes into the circular window */ - if (copy >= state->wsize) { - zmemcpy(state->window, end - state->wsize, state->wsize); - state->wnext = 0; - state->whave = state->wsize; - } - else { - dist = state->wsize - state->wnext; - if (dist > copy) dist = copy; - zmemcpy(state->window + state->wnext, end - copy, dist); - copy -= dist; - if (copy) { - zmemcpy(state->window, end - copy, copy); - state->wnext = copy; - state->whave = state->wsize; - } - else { - state->wnext += dist; - if (state->wnext == state->wsize) state->wnext = 0; - if (state->whave < state->wsize) state->whave += dist; - } - } - return 0; -} - -/* Macros for inflate(): */ - -/* check function to use adler32() for zlib or crc32() for gzip */ -#ifdef GUNZIP -# define UPDATE_CHECK(check, buf, len) \ - (state->flags ? crc32(check, buf, len) : adler32(check, buf, len)) -#else -# define UPDATE_CHECK(check, buf, len) adler32(check, buf, len) -#endif - -/* check macros for header crc */ -#ifdef GUNZIP -# define CRC2(check, word) \ - do { \ - hbuf[0] = (unsigned char)(word); \ - hbuf[1] = (unsigned char)((word) >> 8); \ - check = crc32(check, hbuf, 2); \ - } while (0) - -# define CRC4(check, word) \ - do { \ - hbuf[0] = (unsigned char)(word); \ - hbuf[1] = (unsigned char)((word) >> 8); \ - hbuf[2] = (unsigned char)((word) >> 16); \ - hbuf[3] = (unsigned char)((word) >> 24); \ - check = crc32(check, hbuf, 4); \ - } while (0) -#endif - -/* Load registers with state in inflate() for speed */ -#define LOAD() \ - do { \ - put = strm->next_out; \ - left = strm->avail_out; \ - next = strm->next_in; \ - have = strm->avail_in; \ - hold = state->hold; \ - bits = state->bits; \ - } while (0) - -/* Restore state from registers in inflate() */ -#define RESTORE() \ - do { \ - strm->next_out = put; \ - strm->avail_out = left; \ - strm->next_in = next; \ - strm->avail_in = have; \ - state->hold = hold; \ - state->bits = bits; \ - } while (0) - -/* Clear the input bit accumulator */ -#define INITBITS() \ - do { \ - hold = 0; \ - bits = 0; \ - } while (0) - -/* Get a byte of input into the bit accumulator, or return from inflate() - if there is no input available. */ -#define PULLBYTE() \ - do { \ - if (have == 0) goto inf_leave; \ - have--; \ - hold += (unsigned long)(*next++) << bits; \ - bits += 8; \ - } while (0) - -/* Assure that there are at least n bits in the bit accumulator. If there is - not enough available input to do that, then return from inflate(). */ -#define NEEDBITS(n) \ - do { \ - while (bits < (unsigned)(n)) \ - PULLBYTE(); \ - } while (0) - -/* Return the low n bits of the bit accumulator (n < 16) */ -#define BITS(n) \ - ((unsigned)hold & ((1U << (n)) - 1)) - -/* Remove n bits from the bit accumulator */ -#define DROPBITS(n) \ - do { \ - hold >>= (n); \ - bits -= (unsigned)(n); \ - } while (0) - -/* Remove zero to seven bits as needed to go to a byte boundary */ -#define BYTEBITS() \ - do { \ - hold >>= bits & 7; \ - bits -= bits & 7; \ - } while (0) - -/* - inflate() uses a state machine to process as much input data and generate as - much output data as possible before returning. The state machine is - structured roughly as follows: - - for (;;) switch (state) { - ... - case STATEn: - if (not enough input data or output space to make progress) - return; - ... make progress ... - state = STATEm; - break; - ... - } - - so when inflate() is called again, the same case is attempted again, and - if the appropriate resources are provided, the machine proceeds to the - next state. The NEEDBITS() macro is usually the way the state evaluates - whether it can proceed or should return. NEEDBITS() does the return if - the requested bits are not available. The typical use of the BITS macros - is: - - NEEDBITS(n); - ... do something with BITS(n) ... - DROPBITS(n); - - where NEEDBITS(n) either returns from inflate() if there isn't enough - input left to load n bits into the accumulator, or it continues. BITS(n) - gives the low n bits in the accumulator. When done, DROPBITS(n) drops - the low n bits off the accumulator. INITBITS() clears the accumulator - and sets the number of available bits to zero. BYTEBITS() discards just - enough bits to put the accumulator on a byte boundary. After BYTEBITS() - and a NEEDBITS(8), then BITS(8) would return the next byte in the stream. - - NEEDBITS(n) uses PULLBYTE() to get an available byte of input, or to return - if there is no input available. The decoding of variable length codes uses - PULLBYTE() directly in order to pull just enough bytes to decode the next - code, and no more. - - Some states loop until they get enough input, making sure that enough - state information is maintained to continue the loop where it left off - if NEEDBITS() returns in the loop. For example, want, need, and keep - would all have to actually be part of the saved state in case NEEDBITS() - returns: - - case STATEw: - while (want < need) { - NEEDBITS(n); - keep[want++] = BITS(n); - DROPBITS(n); - } - state = STATEx; - case STATEx: - - As shown above, if the next state is also the next case, then the break - is omitted. - - A state may also return if there is not enough output space available to - complete that state. Those states are copying stored data, writing a - literal byte, and copying a matching string. - - When returning, a "goto inf_leave" is used to update the total counters, - update the check value, and determine whether any progress has been made - during that inflate() call in order to return the proper return code. - Progress is defined as a change in either strm->avail_in or strm->avail_out. - When there is a window, goto inf_leave will update the window with the last - output written. If a goto inf_leave occurs in the middle of decompression - and there is no window currently, goto inf_leave will create one and copy - output to the window for the next call of inflate(). - - In this implementation, the flush parameter of inflate() only affects the - return code (per zlib.h). inflate() always writes as much as possible to - strm->next_out, given the space available and the provided input--the effect - documented in zlib.h of Z_SYNC_FLUSH. Furthermore, inflate() always defers - the allocation of and copying into a sliding window until necessary, which - provides the effect documented in zlib.h for Z_FINISH when the entire input - stream available. So the only thing the flush parameter actually does is: - when flush is set to Z_FINISH, inflate() cannot return Z_OK. Instead it - will return Z_BUF_ERROR if it has not reached the end of the stream. - */ - -int ZEXPORT inflate(z_streamp strm, int flush) { - struct inflate_state FAR *state; - z_const unsigned char FAR *next; /* next input */ - unsigned char FAR *put; /* next output */ - unsigned have, left; /* available input and output */ - unsigned long hold; /* bit buffer */ - unsigned bits; /* bits in bit buffer */ - unsigned in, out; /* save starting available input and output */ - unsigned copy; /* number of stored or match bytes to copy */ - unsigned char FAR *from; /* where to copy match bytes from */ - code here; /* current decoding table entry */ - code last; /* parent table entry */ - unsigned len; /* length to copy for repeats, bits to drop */ - int ret; /* return code */ -#ifdef GUNZIP - unsigned char hbuf[4]; /* buffer for gzip header crc calculation */ -#endif - static const unsigned short order[19] = /* permutation of code lengths */ - {16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15}; - - if (inflateStateCheck(strm) || strm->next_out == Z_NULL || - (strm->next_in == Z_NULL && strm->avail_in != 0)) - return Z_STREAM_ERROR; - - state = (struct inflate_state FAR *)strm->state; - if (state->mode == TYPE) state->mode = TYPEDO; /* skip check */ - LOAD(); - in = have; - out = left; - ret = Z_OK; - for (;;) - switch (state->mode) { - case HEAD: - if (state->wrap == 0) { - state->mode = TYPEDO; - break; - } - NEEDBITS(16); -#ifdef GUNZIP - if ((state->wrap & 2) && hold == 0x8b1f) { /* gzip header */ - if (state->wbits == 0) - state->wbits = 15; - state->check = crc32(0L, Z_NULL, 0); - CRC2(state->check, hold); - INITBITS(); - state->mode = FLAGS; - break; - } - if (state->head != Z_NULL) - state->head->done = -1; - if (!(state->wrap & 1) || /* check if zlib header allowed */ -#else - if ( -#endif - ((BITS(8) << 8) + (hold >> 8)) % 31) { - strm->msg = (char *)"incorrect header check"; - state->mode = BAD; - break; - } - if (BITS(4) != Z_DEFLATED) { - strm->msg = (char *)"unknown compression method"; - state->mode = BAD; - break; - } - DROPBITS(4); - len = BITS(4) + 8; - if (state->wbits == 0) - state->wbits = len; - if (len > 15 || len > state->wbits) { - strm->msg = (char *)"invalid window size"; - state->mode = BAD; - break; - } - state->dmax = 1U << len; - state->flags = 0; /* indicate zlib header */ - Tracev((stderr, "inflate: zlib header ok\n")); - strm->adler = state->check = adler32(0L, Z_NULL, 0); - state->mode = hold & 0x200 ? DICTID : TYPE; - INITBITS(); - break; -#ifdef GUNZIP - case FLAGS: - NEEDBITS(16); - state->flags = (int)(hold); - if ((state->flags & 0xff) != Z_DEFLATED) { - strm->msg = (char *)"unknown compression method"; - state->mode = BAD; - break; - } - if (state->flags & 0xe000) { - strm->msg = (char *)"unknown header flags set"; - state->mode = BAD; - break; - } - if (state->head != Z_NULL) - state->head->text = (int)((hold >> 8) & 1); - if ((state->flags & 0x0200) && (state->wrap & 4)) - CRC2(state->check, hold); - INITBITS(); - state->mode = TIME; - /* fallthrough */ - case TIME: - NEEDBITS(32); - if (state->head != Z_NULL) - state->head->time = hold; - if ((state->flags & 0x0200) && (state->wrap & 4)) - CRC4(state->check, hold); - INITBITS(); - state->mode = OS; - /* fallthrough */ - case OS: - NEEDBITS(16); - if (state->head != Z_NULL) { - state->head->xflags = (int)(hold & 0xff); - state->head->os = (int)(hold >> 8); - } - if ((state->flags & 0x0200) && (state->wrap & 4)) - CRC2(state->check, hold); - INITBITS(); - state->mode = EXLEN; - /* fallthrough */ - case EXLEN: - if (state->flags & 0x0400) { - NEEDBITS(16); - state->length = (unsigned)(hold); - if (state->head != Z_NULL) - state->head->extra_len = (unsigned)hold; - if ((state->flags & 0x0200) && (state->wrap & 4)) - CRC2(state->check, hold); - INITBITS(); - } - else if (state->head != Z_NULL) - state->head->extra = Z_NULL; - state->mode = EXTRA; - /* fallthrough */ - case EXTRA: - if (state->flags & 0x0400) { - copy = state->length; - if (copy > have) copy = have; - if (copy) { - if (state->head != Z_NULL && - state->head->extra != Z_NULL && - (len = state->head->extra_len - state->length) < - state->head->extra_max) { - zmemcpy(state->head->extra + len, next, - len + copy > state->head->extra_max ? - state->head->extra_max - len : copy); - } - if ((state->flags & 0x0200) && (state->wrap & 4)) - state->check = crc32(state->check, next, copy); - have -= copy; - next += copy; - state->length -= copy; - } - if (state->length) goto inf_leave; - } - state->length = 0; - state->mode = NAME; - /* fallthrough */ - case NAME: - if (state->flags & 0x0800) { - if (have == 0) goto inf_leave; - copy = 0; - do { - len = (unsigned)(next[copy++]); - if (state->head != Z_NULL && - state->head->name != Z_NULL && - state->length < state->head->name_max) - state->head->name[state->length++] = (Bytef)len; - } while (len && copy < have); - if ((state->flags & 0x0200) && (state->wrap & 4)) - state->check = crc32(state->check, next, copy); - have -= copy; - next += copy; - if (len) goto inf_leave; - } - else if (state->head != Z_NULL) - state->head->name = Z_NULL; - state->length = 0; - state->mode = COMMENT; - /* fallthrough */ - case COMMENT: - if (state->flags & 0x1000) { - if (have == 0) goto inf_leave; - copy = 0; - do { - len = (unsigned)(next[copy++]); - if (state->head != Z_NULL && - state->head->comment != Z_NULL && - state->length < state->head->comm_max) - state->head->comment[state->length++] = (Bytef)len; - } while (len && copy < have); - if ((state->flags & 0x0200) && (state->wrap & 4)) - state->check = crc32(state->check, next, copy); - have -= copy; - next += copy; - if (len) goto inf_leave; - } - else if (state->head != Z_NULL) - state->head->comment = Z_NULL; - state->mode = HCRC; - /* fallthrough */ - case HCRC: - if (state->flags & 0x0200) { - NEEDBITS(16); - if ((state->wrap & 4) && hold != (state->check & 0xffff)) { - strm->msg = (char *)"header crc mismatch"; - state->mode = BAD; - break; - } - INITBITS(); - } - if (state->head != Z_NULL) { - state->head->hcrc = (int)((state->flags >> 9) & 1); - state->head->done = 1; - } - strm->adler = state->check = crc32(0L, Z_NULL, 0); - state->mode = TYPE; - break; -#endif - case DICTID: - NEEDBITS(32); - strm->adler = state->check = ZSWAP32(hold); - INITBITS(); - state->mode = DICT; - /* fallthrough */ - case DICT: - if (state->havedict == 0) { - RESTORE(); - return Z_NEED_DICT; - } - strm->adler = state->check = adler32(0L, Z_NULL, 0); - state->mode = TYPE; - /* fallthrough */ - case TYPE: - if (flush == Z_BLOCK || flush == Z_TREES) goto inf_leave; - /* fallthrough */ - case TYPEDO: - if (state->last) { - BYTEBITS(); - state->mode = CHECK; - break; - } - NEEDBITS(3); - state->last = BITS(1); - DROPBITS(1); - switch (BITS(2)) { - case 0: /* stored block */ - Tracev((stderr, "inflate: stored block%s\n", - state->last ? " (last)" : "")); - state->mode = STORED; - break; - case 1: /* fixed block */ - fixedtables(state); - Tracev((stderr, "inflate: fixed codes block%s\n", - state->last ? " (last)" : "")); - state->mode = LEN_; /* decode codes */ - if (flush == Z_TREES) { - DROPBITS(2); - goto inf_leave; - } - break; - case 2: /* dynamic block */ - Tracev((stderr, "inflate: dynamic codes block%s\n", - state->last ? " (last)" : "")); - state->mode = TABLE; - break; - case 3: - strm->msg = (char *)"invalid block type"; - state->mode = BAD; - } - DROPBITS(2); - break; - case STORED: - BYTEBITS(); /* go to byte boundary */ - NEEDBITS(32); - if ((hold & 0xffff) != ((hold >> 16) ^ 0xffff)) { - strm->msg = (char *)"invalid stored block lengths"; - state->mode = BAD; - break; - } - state->length = (unsigned)hold & 0xffff; - Tracev((stderr, "inflate: stored length %u\n", - state->length)); - INITBITS(); - state->mode = COPY_; - if (flush == Z_TREES) goto inf_leave; - /* fallthrough */ - case COPY_: - state->mode = COPY; - /* fallthrough */ - case COPY: - copy = state->length; - if (copy) { - if (copy > have) copy = have; - if (copy > left) copy = left; - if (copy == 0) goto inf_leave; - zmemcpy(put, next, copy); - have -= copy; - next += copy; - left -= copy; - put += copy; - state->length -= copy; - break; - } - Tracev((stderr, "inflate: stored end\n")); - state->mode = TYPE; - break; - case TABLE: - NEEDBITS(14); - state->nlen = BITS(5) + 257; - DROPBITS(5); - state->ndist = BITS(5) + 1; - DROPBITS(5); - state->ncode = BITS(4) + 4; - DROPBITS(4); -#ifndef PKZIP_BUG_WORKAROUND - if (state->nlen > 286 || state->ndist > 30) { - strm->msg = (char *)"too many length or distance symbols"; - state->mode = BAD; - break; - } -#endif - Tracev((stderr, "inflate: table sizes ok\n")); - state->have = 0; - state->mode = LENLENS; - /* fallthrough */ - case LENLENS: - while (state->have < state->ncode) { - NEEDBITS(3); - state->lens[order[state->have++]] = (unsigned short)BITS(3); - DROPBITS(3); - } - while (state->have < 19) - state->lens[order[state->have++]] = 0; - state->next = state->codes; - state->lencode = (const code FAR *)(state->next); - state->lenbits = 7; - ret = inflate_table(CODES, state->lens, 19, &(state->next), - &(state->lenbits), state->work); - if (ret) { - strm->msg = (char *)"invalid code lengths set"; - state->mode = BAD; - break; - } - Tracev((stderr, "inflate: code lengths ok\n")); - state->have = 0; - state->mode = CODELENS; - /* fallthrough */ - case CODELENS: - while (state->have < state->nlen + state->ndist) { - for (;;) { - here = state->lencode[BITS(state->lenbits)]; - if ((unsigned)(here.bits) <= bits) break; - PULLBYTE(); - } - if (here.val < 16) { - DROPBITS(here.bits); - state->lens[state->have++] = here.val; - } - else { - if (here.val == 16) { - NEEDBITS(here.bits + 2); - DROPBITS(here.bits); - if (state->have == 0) { - strm->msg = (char *)"invalid bit length repeat"; - state->mode = BAD; - break; - } - len = state->lens[state->have - 1]; - copy = 3 + BITS(2); - DROPBITS(2); - } - else if (here.val == 17) { - NEEDBITS(here.bits + 3); - DROPBITS(here.bits); - len = 0; - copy = 3 + BITS(3); - DROPBITS(3); - } - else { - NEEDBITS(here.bits + 7); - DROPBITS(here.bits); - len = 0; - copy = 11 + BITS(7); - DROPBITS(7); - } - if (state->have + copy > state->nlen + state->ndist) { - strm->msg = (char *)"invalid bit length repeat"; - state->mode = BAD; - break; - } - while (copy--) - state->lens[state->have++] = (unsigned short)len; - } - } - - /* handle error breaks in while */ - if (state->mode == BAD) break; - - /* check for end-of-block code (better have one) */ - if (state->lens[256] == 0) { - strm->msg = (char *)"invalid code -- missing end-of-block"; - state->mode = BAD; - break; - } - - /* build code tables -- note: do not change the lenbits or distbits - values here (9 and 6) without reading the comments in inftrees.h - concerning the ENOUGH constants, which depend on those values */ - state->next = state->codes; - state->lencode = (const code FAR *)(state->next); - state->lenbits = 9; - ret = inflate_table(LENS, state->lens, state->nlen, &(state->next), - &(state->lenbits), state->work); - if (ret) { - strm->msg = (char *)"invalid literal/lengths set"; - state->mode = BAD; - break; - } - state->distcode = (const code FAR *)(state->next); - state->distbits = 6; - ret = inflate_table(DISTS, state->lens + state->nlen, state->ndist, - &(state->next), &(state->distbits), state->work); - if (ret) { - strm->msg = (char *)"invalid distances set"; - state->mode = BAD; - break; - } - Tracev((stderr, "inflate: codes ok\n")); - state->mode = LEN_; - if (flush == Z_TREES) goto inf_leave; - /* fallthrough */ - case LEN_: - state->mode = LEN; - /* fallthrough */ - case LEN: - if (have >= 6 && left >= 258) { - RESTORE(); - inflate_fast(strm, out); - LOAD(); - if (state->mode == TYPE) - state->back = -1; - break; - } - state->back = 0; - for (;;) { - here = state->lencode[BITS(state->lenbits)]; - if ((unsigned)(here.bits) <= bits) break; - PULLBYTE(); - } - if (here.op && (here.op & 0xf0) == 0) { - last = here; - for (;;) { - here = state->lencode[last.val + - (BITS(last.bits + last.op) >> last.bits)]; - if ((unsigned)(last.bits + here.bits) <= bits) break; - PULLBYTE(); - } - DROPBITS(last.bits); - state->back += last.bits; - } - DROPBITS(here.bits); - state->back += here.bits; - state->length = (unsigned)here.val; - if ((int)(here.op) == 0) { - Tracevv((stderr, here.val >= 0x20 && here.val < 0x7f ? - "inflate: literal '%c'\n" : - "inflate: literal 0x%02x\n", here.val)); - state->mode = LIT; - break; - } - if (here.op & 32) { - Tracevv((stderr, "inflate: end of block\n")); - state->back = -1; - state->mode = TYPE; - break; - } - if (here.op & 64) { - strm->msg = (char *)"invalid literal/length code"; - state->mode = BAD; - break; - } - state->extra = (unsigned)(here.op) & 15; - state->mode = LENEXT; - /* fallthrough */ - case LENEXT: - if (state->extra) { - NEEDBITS(state->extra); - state->length += BITS(state->extra); - DROPBITS(state->extra); - state->back += state->extra; - } - Tracevv((stderr, "inflate: length %u\n", state->length)); - state->was = state->length; - state->mode = DIST; - /* fallthrough */ - case DIST: - for (;;) { - here = state->distcode[BITS(state->distbits)]; - if ((unsigned)(here.bits) <= bits) break; - PULLBYTE(); - } - if ((here.op & 0xf0) == 0) { - last = here; - for (;;) { - here = state->distcode[last.val + - (BITS(last.bits + last.op) >> last.bits)]; - if ((unsigned)(last.bits + here.bits) <= bits) break; - PULLBYTE(); - } - DROPBITS(last.bits); - state->back += last.bits; - } - DROPBITS(here.bits); - state->back += here.bits; - if (here.op & 64) { - strm->msg = (char *)"invalid distance code"; - state->mode = BAD; - break; - } - state->offset = (unsigned)here.val; - state->extra = (unsigned)(here.op) & 15; - state->mode = DISTEXT; - /* fallthrough */ - case DISTEXT: - if (state->extra) { - NEEDBITS(state->extra); - state->offset += BITS(state->extra); - DROPBITS(state->extra); - state->back += state->extra; - } -#ifdef INFLATE_STRICT - if (state->offset > state->dmax) { - strm->msg = (char *)"invalid distance too far back"; - state->mode = BAD; - break; - } -#endif - Tracevv((stderr, "inflate: distance %u\n", state->offset)); - state->mode = MATCH; - /* fallthrough */ - case MATCH: - if (left == 0) goto inf_leave; - copy = out - left; - if (state->offset > copy) { /* copy from window */ - copy = state->offset - copy; - if (copy > state->whave) { - if (state->sane) { - strm->msg = (char *)"invalid distance too far back"; - state->mode = BAD; - break; - } -#ifdef INFLATE_ALLOW_INVALID_DISTANCE_TOOFAR_ARRR - Trace((stderr, "inflate.c too far\n")); - copy -= state->whave; - if (copy > state->length) copy = state->length; - if (copy > left) copy = left; - left -= copy; - state->length -= copy; - do { - *put++ = 0; - } while (--copy); - if (state->length == 0) state->mode = LEN; - break; -#endif - } - if (copy > state->wnext) { - copy -= state->wnext; - from = state->window + (state->wsize - copy); - } - else - from = state->window + (state->wnext - copy); - if (copy > state->length) copy = state->length; - } - else { /* copy from output */ - from = put - state->offset; - copy = state->length; - } - if (copy > left) copy = left; - left -= copy; - state->length -= copy; - do { - *put++ = *from++; - } while (--copy); - if (state->length == 0) state->mode = LEN; - break; - case LIT: - if (left == 0) goto inf_leave; - *put++ = (unsigned char)(state->length); - left--; - state->mode = LEN; - break; - case CHECK: - if (state->wrap) { - NEEDBITS(32); - out -= left; - strm->total_out += out; - state->total += out; - if ((state->wrap & 4) && out) - strm->adler = state->check = - UPDATE_CHECK(state->check, put - out, out); - out = left; - if ((state->wrap & 4) && ( -#ifdef GUNZIP - state->flags ? hold : -#endif - ZSWAP32(hold)) != state->check) { - strm->msg = (char *)"incorrect data check"; - state->mode = BAD; - break; - } - INITBITS(); - Tracev((stderr, "inflate: check matches trailer\n")); - } -#ifdef GUNZIP - state->mode = LENGTH; - /* fallthrough */ - case LENGTH: - if (state->wrap && state->flags) { - NEEDBITS(32); - if ((state->wrap & 4) && hold != (state->total & 0xffffffff)) { - strm->msg = (char *)"incorrect length check"; - state->mode = BAD; - break; - } - INITBITS(); - Tracev((stderr, "inflate: length matches trailer\n")); - } -#endif - state->mode = DONE; - /* fallthrough */ - case DONE: - ret = Z_STREAM_END; - goto inf_leave; - case BAD: - ret = Z_DATA_ERROR; - goto inf_leave; - case MEM: - return Z_MEM_ERROR; - case SYNC: - /* fallthrough */ - default: - return Z_STREAM_ERROR; - } - - /* - Return from inflate(), updating the total counts and the check value. - If there was no progress during the inflate() call, return a buffer - error. Call updatewindow() to create and/or update the window state. - Note: a memory error from inflate() is non-recoverable. - */ - inf_leave: - RESTORE(); - if (state->wsize || (out != strm->avail_out && state->mode < BAD && - (state->mode < CHECK || flush != Z_FINISH))) - if (updatewindow(strm, strm->next_out, out - strm->avail_out)) { - state->mode = MEM; - return Z_MEM_ERROR; - } - in -= strm->avail_in; - out -= strm->avail_out; - strm->total_in += in; - strm->total_out += out; - state->total += out; - if ((state->wrap & 4) && out) - strm->adler = state->check = - UPDATE_CHECK(state->check, strm->next_out - out, out); - strm->data_type = (int)state->bits + (state->last ? 64 : 0) + - (state->mode == TYPE ? 128 : 0) + - (state->mode == LEN_ || state->mode == COPY_ ? 256 : 0); - if (((in == 0 && out == 0) || flush == Z_FINISH) && ret == Z_OK) - ret = Z_BUF_ERROR; - return ret; -} - -int ZEXPORT inflateEnd(z_streamp strm) { - struct inflate_state FAR *state; - if (inflateStateCheck(strm)) - return Z_STREAM_ERROR; - state = (struct inflate_state FAR *)strm->state; - if (state->window != Z_NULL) ZFREE(strm, state->window); - ZFREE(strm, strm->state); - strm->state = Z_NULL; - Tracev((stderr, "inflate: end\n")); - return Z_OK; -} - -int ZEXPORT inflateGetDictionary(z_streamp strm, Bytef *dictionary, - uInt *dictLength) { - struct inflate_state FAR *state; - - /* check state */ - if (inflateStateCheck(strm)) return Z_STREAM_ERROR; - state = (struct inflate_state FAR *)strm->state; - - /* copy dictionary */ - if (state->whave && dictionary != Z_NULL) { - zmemcpy(dictionary, state->window + state->wnext, - state->whave - state->wnext); - zmemcpy(dictionary + state->whave - state->wnext, - state->window, state->wnext); - } - if (dictLength != Z_NULL) - *dictLength = state->whave; - return Z_OK; -} - -int ZEXPORT inflateSetDictionary(z_streamp strm, const Bytef *dictionary, - uInt dictLength) { - struct inflate_state FAR *state; - unsigned long dictid; - int ret; - - /* check state */ - if (inflateStateCheck(strm)) return Z_STREAM_ERROR; - state = (struct inflate_state FAR *)strm->state; - if (state->wrap != 0 && state->mode != DICT) - return Z_STREAM_ERROR; - - /* check for correct dictionary identifier */ - if (state->mode == DICT) { - dictid = adler32(0L, Z_NULL, 0); - dictid = adler32(dictid, dictionary, dictLength); - if (dictid != state->check) - return Z_DATA_ERROR; - } - - /* copy dictionary to window using updatewindow(), which will amend the - existing dictionary if appropriate */ - ret = updatewindow(strm, dictionary + dictLength, dictLength); - if (ret) { - state->mode = MEM; - return Z_MEM_ERROR; - } - state->havedict = 1; - Tracev((stderr, "inflate: dictionary set\n")); - return Z_OK; -} - -int ZEXPORT inflateGetHeader(z_streamp strm, gz_headerp head) { - struct inflate_state FAR *state; - - /* check state */ - if (inflateStateCheck(strm)) return Z_STREAM_ERROR; - state = (struct inflate_state FAR *)strm->state; - if ((state->wrap & 2) == 0) return Z_STREAM_ERROR; - - /* save header structure */ - state->head = head; - head->done = 0; - return Z_OK; -} - -/* - Search buf[0..len-1] for the pattern: 0, 0, 0xff, 0xff. Return when found - or when out of input. When called, *have is the number of pattern bytes - found in order so far, in 0..3. On return *have is updated to the new - state. If on return *have equals four, then the pattern was found and the - return value is how many bytes were read including the last byte of the - pattern. If *have is less than four, then the pattern has not been found - yet and the return value is len. In the latter case, syncsearch() can be - called again with more data and the *have state. *have is initialized to - zero for the first call. - */ -local unsigned syncsearch(unsigned FAR *have, const unsigned char FAR *buf, - unsigned len) { - unsigned got; - unsigned next; - - got = *have; - next = 0; - while (next < len && got < 4) { - if ((int)(buf[next]) == (got < 2 ? 0 : 0xff)) - got++; - else if (buf[next]) - got = 0; - else - got = 4 - got; - next++; - } - *have = got; - return next; -} - -int ZEXPORT inflateSync(z_streamp strm) { - unsigned len; /* number of bytes to look at or looked at */ - int flags; /* temporary to save header status */ - unsigned long in, out; /* temporary to save total_in and total_out */ - unsigned char buf[4]; /* to restore bit buffer to byte string */ - struct inflate_state FAR *state; - - /* check parameters */ - if (inflateStateCheck(strm)) return Z_STREAM_ERROR; - state = (struct inflate_state FAR *)strm->state; - if (strm->avail_in == 0 && state->bits < 8) return Z_BUF_ERROR; - - /* if first time, start search in bit buffer */ - if (state->mode != SYNC) { - state->mode = SYNC; - state->hold >>= state->bits & 7; - state->bits -= state->bits & 7; - len = 0; - while (state->bits >= 8) { - buf[len++] = (unsigned char)(state->hold); - state->hold >>= 8; - state->bits -= 8; - } - state->have = 0; - syncsearch(&(state->have), buf, len); - } - - /* search available input */ - len = syncsearch(&(state->have), strm->next_in, strm->avail_in); - strm->avail_in -= len; - strm->next_in += len; - strm->total_in += len; - - /* return no joy or set up to restart inflate() on a new block */ - if (state->have != 4) return Z_DATA_ERROR; - if (state->flags == -1) - state->wrap = 0; /* if no header yet, treat as raw */ - else - state->wrap &= ~4; /* no point in computing a check value now */ - flags = state->flags; - in = strm->total_in; out = strm->total_out; - inflateReset(strm); - strm->total_in = in; strm->total_out = out; - state->flags = flags; - state->mode = TYPE; - return Z_OK; -} - -/* - Returns true if inflate is currently at the end of a block generated by - Z_SYNC_FLUSH or Z_FULL_FLUSH. This function is used by one PPP - implementation to provide an additional safety check. PPP uses - Z_SYNC_FLUSH but removes the length bytes of the resulting empty stored - block. When decompressing, PPP checks that at the end of input packet, - inflate is waiting for these length bytes. - */ -int ZEXPORT inflateSyncPoint(z_streamp strm) { - struct inflate_state FAR *state; - - if (inflateStateCheck(strm)) return Z_STREAM_ERROR; - state = (struct inflate_state FAR *)strm->state; - return state->mode == STORED && state->bits == 0; -} - -int ZEXPORT inflateCopy(z_streamp dest, z_streamp source) { - struct inflate_state FAR *state; - struct inflate_state FAR *copy; - unsigned char FAR *window; - unsigned wsize; - - /* check input */ - if (inflateStateCheck(source) || dest == Z_NULL) - return Z_STREAM_ERROR; - state = (struct inflate_state FAR *)source->state; - - /* allocate space */ - copy = (struct inflate_state FAR *) - ZALLOC(source, 1, sizeof(struct inflate_state)); - if (copy == Z_NULL) return Z_MEM_ERROR; - window = Z_NULL; - if (state->window != Z_NULL) { - window = (unsigned char FAR *) - ZALLOC(source, 1U << state->wbits, sizeof(unsigned char)); - if (window == Z_NULL) { - ZFREE(source, copy); - return Z_MEM_ERROR; - } - } - - /* copy state */ - zmemcpy((voidpf)dest, (voidpf)source, sizeof(z_stream)); - zmemcpy((voidpf)copy, (voidpf)state, sizeof(struct inflate_state)); - copy->strm = dest; - if (state->lencode >= state->codes && - state->lencode <= state->codes + ENOUGH - 1) { - copy->lencode = copy->codes + (state->lencode - state->codes); - copy->distcode = copy->codes + (state->distcode - state->codes); - } - copy->next = copy->codes + (state->next - state->codes); - if (window != Z_NULL) { - wsize = 1U << state->wbits; - zmemcpy(window, state->window, wsize); - } - copy->window = window; - dest->state = (struct internal_state FAR *)copy; - return Z_OK; -} - -int ZEXPORT inflateUndermine(z_streamp strm, int subvert) { - struct inflate_state FAR *state; - - if (inflateStateCheck(strm)) return Z_STREAM_ERROR; - state = (struct inflate_state FAR *)strm->state; -#ifdef INFLATE_ALLOW_INVALID_DISTANCE_TOOFAR_ARRR - state->sane = !subvert; - return Z_OK; -#else - (void)subvert; - state->sane = 1; - return Z_DATA_ERROR; -#endif -} - -int ZEXPORT inflateValidate(z_streamp strm, int check) { - struct inflate_state FAR *state; - - if (inflateStateCheck(strm)) return Z_STREAM_ERROR; - state = (struct inflate_state FAR *)strm->state; - if (check && state->wrap) - state->wrap |= 4; - else - state->wrap &= ~4; - return Z_OK; -} - -long ZEXPORT inflateMark(z_streamp strm) { - struct inflate_state FAR *state; - - if (inflateStateCheck(strm)) - return -(1L << 16); - state = (struct inflate_state FAR *)strm->state; - return (long)(((unsigned long)((long)state->back)) << 16) + - (state->mode == COPY ? state->length : - (state->mode == MATCH ? state->was - state->length : 0)); -} - -unsigned long ZEXPORT inflateCodesUsed(z_streamp strm) { - struct inflate_state FAR *state; - if (inflateStateCheck(strm)) return (unsigned long)-1; - state = (struct inflate_state FAR *)strm->state; - return (unsigned long)(state->next - state->codes); -} diff --git a/phobos/etc/c/zlib/inflate.h b/phobos/etc/c/zlib/inflate.h deleted file mode 100644 index f127b6b..0000000 --- a/phobos/etc/c/zlib/inflate.h +++ /dev/null @@ -1,126 +0,0 @@ -/* inflate.h -- internal inflate state definition - * Copyright (C) 1995-2019 Mark Adler - * For conditions of distribution and use, see copyright notice in zlib.h - */ - -/* WARNING: this file should *not* be used by applications. It is - part of the implementation of the compression library and is - subject to change. Applications should only use zlib.h. - */ - -/* define NO_GZIP when compiling if you want to disable gzip header and - trailer decoding by inflate(). NO_GZIP would be used to avoid linking in - the crc code when it is not needed. For shared libraries, gzip decoding - should be left enabled. */ -#ifndef NO_GZIP -# define GUNZIP -#endif - -/* Possible inflate modes between inflate() calls */ -typedef enum { - HEAD = 16180, /* i: waiting for magic header */ - FLAGS, /* i: waiting for method and flags (gzip) */ - TIME, /* i: waiting for modification time (gzip) */ - OS, /* i: waiting for extra flags and operating system (gzip) */ - EXLEN, /* i: waiting for extra length (gzip) */ - EXTRA, /* i: waiting for extra bytes (gzip) */ - NAME, /* i: waiting for end of file name (gzip) */ - COMMENT, /* i: waiting for end of comment (gzip) */ - HCRC, /* i: waiting for header crc (gzip) */ - DICTID, /* i: waiting for dictionary check value */ - DICT, /* waiting for inflateSetDictionary() call */ - TYPE, /* i: waiting for type bits, including last-flag bit */ - TYPEDO, /* i: same, but skip check to exit inflate on new block */ - STORED, /* i: waiting for stored size (length and complement) */ - COPY_, /* i/o: same as COPY below, but only first time in */ - COPY, /* i/o: waiting for input or output to copy stored block */ - TABLE, /* i: waiting for dynamic block table lengths */ - LENLENS, /* i: waiting for code length code lengths */ - CODELENS, /* i: waiting for length/lit and distance code lengths */ - LEN_, /* i: same as LEN below, but only first time in */ - LEN, /* i: waiting for length/lit/eob code */ - LENEXT, /* i: waiting for length extra bits */ - DIST, /* i: waiting for distance code */ - DISTEXT, /* i: waiting for distance extra bits */ - MATCH, /* o: waiting for output space to copy string */ - LIT, /* o: waiting for output space to write literal */ - CHECK, /* i: waiting for 32-bit check value */ - LENGTH, /* i: waiting for 32-bit length (gzip) */ - DONE, /* finished check, done -- remain here until reset */ - BAD, /* got a data error -- remain here until reset */ - MEM, /* got an inflate() memory error -- remain here until reset */ - SYNC /* looking for synchronization bytes to restart inflate() */ -} inflate_mode; - -/* - State transitions between above modes - - - (most modes can go to BAD or MEM on error -- not shown for clarity) - - Process header: - HEAD -> (gzip) or (zlib) or (raw) - (gzip) -> FLAGS -> TIME -> OS -> EXLEN -> EXTRA -> NAME -> COMMENT -> - HCRC -> TYPE - (zlib) -> DICTID or TYPE - DICTID -> DICT -> TYPE - (raw) -> TYPEDO - Read deflate blocks: - TYPE -> TYPEDO -> STORED or TABLE or LEN_ or CHECK - STORED -> COPY_ -> COPY -> TYPE - TABLE -> LENLENS -> CODELENS -> LEN_ - LEN_ -> LEN - Read deflate codes in fixed or dynamic block: - LEN -> LENEXT or LIT or TYPE - LENEXT -> DIST -> DISTEXT -> MATCH -> LEN - LIT -> LEN - Process trailer: - CHECK -> LENGTH -> DONE - */ - -/* State maintained between inflate() calls -- approximately 7K bytes, not - including the allocated sliding window, which is up to 32K bytes. */ -struct inflate_state { - z_streamp strm; /* pointer back to this zlib stream */ - inflate_mode mode; /* current inflate mode */ - int last; /* true if processing last block */ - int wrap; /* bit 0 true for zlib, bit 1 true for gzip, - bit 2 true to validate check value */ - int havedict; /* true if dictionary provided */ - int flags; /* gzip header method and flags, 0 if zlib, or - -1 if raw or no header yet */ - unsigned dmax; /* zlib header max distance (INFLATE_STRICT) */ - unsigned long check; /* protected copy of check value */ - unsigned long total; /* protected copy of output count */ - gz_headerp head; /* where to save gzip header information */ - /* sliding window */ - unsigned wbits; /* log base 2 of requested window size */ - unsigned wsize; /* window size or zero if not using window */ - unsigned whave; /* valid bytes in the window */ - unsigned wnext; /* window write index */ - unsigned char FAR *window; /* allocated sliding window, if needed */ - /* bit accumulator */ - unsigned long hold; /* input bit accumulator */ - unsigned bits; /* number of bits in "in" */ - /* for string and stored block copying */ - unsigned length; /* literal or length of data to copy */ - unsigned offset; /* distance back to copy string from */ - /* for table and code decoding */ - unsigned extra; /* extra bits needed */ - /* fixed and dynamic code tables */ - code const FAR *lencode; /* starting table for length/literal codes */ - code const FAR *distcode; /* starting table for distance codes */ - unsigned lenbits; /* index bits for lencode */ - unsigned distbits; /* index bits for distcode */ - /* dynamic table building */ - unsigned ncode; /* number of code length code lengths */ - unsigned nlen; /* number of length code lengths */ - unsigned ndist; /* number of distance code lengths */ - unsigned have; /* number of code lengths in lens[] */ - code FAR *next; /* next available space in codes[] */ - unsigned short lens[320]; /* temporary storage for code lengths */ - unsigned short work[288]; /* work area for code table building */ - code codes[ENOUGH]; /* space for code tables */ - int sane; /* if false, allow invalid distance too far */ - int back; /* bits back of last unprocessed length/lit */ - unsigned was; /* initial length of match */ -}; diff --git a/phobos/etc/c/zlib/inftrees.c b/phobos/etc/c/zlib/inftrees.c deleted file mode 100644 index 98cfe16..0000000 --- a/phobos/etc/c/zlib/inftrees.c +++ /dev/null @@ -1,299 +0,0 @@ -/* inftrees.c -- generate Huffman trees for efficient decoding - * Copyright (C) 1995-2024 Mark Adler - * For conditions of distribution and use, see copyright notice in zlib.h - */ - -#include "zutil.h" -#include "inftrees.h" - -#define MAXBITS 15 - -const char inflate_copyright[] = - " inflate 1.3.1 Copyright 1995-2024 Mark Adler "; -/* - If you use the zlib library in a product, an acknowledgment is welcome - in the documentation of your product. If for some reason you cannot - include such an acknowledgment, I would appreciate that you keep this - copyright string in the executable of your product. - */ - -/* - Build a set of tables to decode the provided canonical Huffman code. - The code lengths are lens[0..codes-1]. The result starts at *table, - whose indices are 0..2^bits-1. work is a writable array of at least - lens shorts, which is used as a work area. type is the type of code - to be generated, CODES, LENS, or DISTS. On return, zero is success, - -1 is an invalid code, and +1 means that ENOUGH isn't enough. table - on return points to the next available entry's address. bits is the - requested root table index bits, and on return it is the actual root - table index bits. It will differ if the request is greater than the - longest code or if it is less than the shortest code. - */ -int ZLIB_INTERNAL inflate_table(codetype type, unsigned short FAR *lens, - unsigned codes, code FAR * FAR *table, - unsigned FAR *bits, unsigned short FAR *work) { - unsigned len; /* a code's length in bits */ - unsigned sym; /* index of code symbols */ - unsigned min, max; /* minimum and maximum code lengths */ - unsigned root; /* number of index bits for root table */ - unsigned curr; /* number of index bits for current table */ - unsigned drop; /* code bits to drop for sub-table */ - int left; /* number of prefix codes available */ - unsigned used; /* code entries in table used */ - unsigned huff; /* Huffman code */ - unsigned incr; /* for incrementing code, index */ - unsigned fill; /* index for replicating entries */ - unsigned low; /* low bits for current root entry */ - unsigned mask; /* mask for low root bits */ - code here; /* table entry for duplication */ - code FAR *next; /* next available space in table */ - const unsigned short FAR *base; /* base value table to use */ - const unsigned short FAR *extra; /* extra bits table to use */ - unsigned match; /* use base and extra for symbol >= match */ - unsigned short count[MAXBITS+1]; /* number of codes of each length */ - unsigned short offs[MAXBITS+1]; /* offsets in table for each length */ - static const unsigned short lbase[31] = { /* Length codes 257..285 base */ - 3, 4, 5, 6, 7, 8, 9, 10, 11, 13, 15, 17, 19, 23, 27, 31, - 35, 43, 51, 59, 67, 83, 99, 115, 131, 163, 195, 227, 258, 0, 0}; - static const unsigned short lext[31] = { /* Length codes 257..285 extra */ - 16, 16, 16, 16, 16, 16, 16, 16, 17, 17, 17, 17, 18, 18, 18, 18, - 19, 19, 19, 19, 20, 20, 20, 20, 21, 21, 21, 21, 16, 203, 77}; - static const unsigned short dbase[32] = { /* Distance codes 0..29 base */ - 1, 2, 3, 4, 5, 7, 9, 13, 17, 25, 33, 49, 65, 97, 129, 193, - 257, 385, 513, 769, 1025, 1537, 2049, 3073, 4097, 6145, - 8193, 12289, 16385, 24577, 0, 0}; - static const unsigned short dext[32] = { /* Distance codes 0..29 extra */ - 16, 16, 16, 16, 17, 17, 18, 18, 19, 19, 20, 20, 21, 21, 22, 22, - 23, 23, 24, 24, 25, 25, 26, 26, 27, 27, - 28, 28, 29, 29, 64, 64}; - - /* - Process a set of code lengths to create a canonical Huffman code. The - code lengths are lens[0..codes-1]. Each length corresponds to the - symbols 0..codes-1. The Huffman code is generated by first sorting the - symbols by length from short to long, and retaining the symbol order - for codes with equal lengths. Then the code starts with all zero bits - for the first code of the shortest length, and the codes are integer - increments for the same length, and zeros are appended as the length - increases. For the deflate format, these bits are stored backwards - from their more natural integer increment ordering, and so when the - decoding tables are built in the large loop below, the integer codes - are incremented backwards. - - This routine assumes, but does not check, that all of the entries in - lens[] are in the range 0..MAXBITS. The caller must assure this. - 1..MAXBITS is interpreted as that code length. zero means that that - symbol does not occur in this code. - - The codes are sorted by computing a count of codes for each length, - creating from that a table of starting indices for each length in the - sorted table, and then entering the symbols in order in the sorted - table. The sorted table is work[], with that space being provided by - the caller. - - The length counts are used for other purposes as well, i.e. finding - the minimum and maximum length codes, determining if there are any - codes at all, checking for a valid set of lengths, and looking ahead - at length counts to determine sub-table sizes when building the - decoding tables. - */ - - /* accumulate lengths for codes (assumes lens[] all in 0..MAXBITS) */ - for (len = 0; len <= MAXBITS; len++) - count[len] = 0; - for (sym = 0; sym < codes; sym++) - count[lens[sym]]++; - - /* bound code lengths, force root to be within code lengths */ - root = *bits; - for (max = MAXBITS; max >= 1; max--) - if (count[max] != 0) break; - if (root > max) root = max; - if (max == 0) { /* no symbols to code at all */ - here.op = (unsigned char)64; /* invalid code marker */ - here.bits = (unsigned char)1; - here.val = (unsigned short)0; - *(*table)++ = here; /* make a table to force an error */ - *(*table)++ = here; - *bits = 1; - return 0; /* no symbols, but wait for decoding to report error */ - } - for (min = 1; min < max; min++) - if (count[min] != 0) break; - if (root < min) root = min; - - /* check for an over-subscribed or incomplete set of lengths */ - left = 1; - for (len = 1; len <= MAXBITS; len++) { - left <<= 1; - left -= count[len]; - if (left < 0) return -1; /* over-subscribed */ - } - if (left > 0 && (type == CODES || max != 1)) - return -1; /* incomplete set */ - - /* generate offsets into symbol table for each length for sorting */ - offs[1] = 0; - for (len = 1; len < MAXBITS; len++) - offs[len + 1] = offs[len] + count[len]; - - /* sort symbols by length, by symbol order within each length */ - for (sym = 0; sym < codes; sym++) - if (lens[sym] != 0) work[offs[lens[sym]]++] = (unsigned short)sym; - - /* - Create and fill in decoding tables. In this loop, the table being - filled is at next and has curr index bits. The code being used is huff - with length len. That code is converted to an index by dropping drop - bits off of the bottom. For codes where len is less than drop + curr, - those top drop + curr - len bits are incremented through all values to - fill the table with replicated entries. - - root is the number of index bits for the root table. When len exceeds - root, sub-tables are created pointed to by the root entry with an index - of the low root bits of huff. This is saved in low to check for when a - new sub-table should be started. drop is zero when the root table is - being filled, and drop is root when sub-tables are being filled. - - When a new sub-table is needed, it is necessary to look ahead in the - code lengths to determine what size sub-table is needed. The length - counts are used for this, and so count[] is decremented as codes are - entered in the tables. - - used keeps track of how many table entries have been allocated from the - provided *table space. It is checked for LENS and DIST tables against - the constants ENOUGH_LENS and ENOUGH_DISTS to guard against changes in - the initial root table size constants. See the comments in inftrees.h - for more information. - - sym increments through all symbols, and the loop terminates when - all codes of length max, i.e. all codes, have been processed. This - routine permits incomplete codes, so another loop after this one fills - in the rest of the decoding tables with invalid code markers. - */ - - /* set up for code type */ - switch (type) { - case CODES: - base = extra = work; /* dummy value--not used */ - match = 20; - break; - case LENS: - base = lbase; - extra = lext; - match = 257; - break; - default: /* DISTS */ - base = dbase; - extra = dext; - match = 0; - } - - /* initialize state for loop */ - huff = 0; /* starting code */ - sym = 0; /* starting code symbol */ - len = min; /* starting code length */ - next = *table; /* current table to fill in */ - curr = root; /* current table index bits */ - drop = 0; /* current bits to drop from code for index */ - low = (unsigned)(-1); /* trigger new sub-table when len > root */ - used = 1U << root; /* use root table entries */ - mask = used - 1; /* mask for comparing low */ - - /* check available table space */ - if ((type == LENS && used > ENOUGH_LENS) || - (type == DISTS && used > ENOUGH_DISTS)) - return 1; - - /* process all codes and make table entries */ - for (;;) { - /* create table entry */ - here.bits = (unsigned char)(len - drop); - if (work[sym] + 1U < match) { - here.op = (unsigned char)0; - here.val = work[sym]; - } - else if (work[sym] >= match) { - here.op = (unsigned char)(extra[work[sym] - match]); - here.val = base[work[sym] - match]; - } - else { - here.op = (unsigned char)(32 + 64); /* end of block */ - here.val = 0; - } - - /* replicate for those indices with low len bits equal to huff */ - incr = 1U << (len - drop); - fill = 1U << curr; - min = fill; /* save offset to next table */ - do { - fill -= incr; - next[(huff >> drop) + fill] = here; - } while (fill != 0); - - /* backwards increment the len-bit code huff */ - incr = 1U << (len - 1); - while (huff & incr) - incr >>= 1; - if (incr != 0) { - huff &= incr - 1; - huff += incr; - } - else - huff = 0; - - /* go to next symbol, update count, len */ - sym++; - if (--(count[len]) == 0) { - if (len == max) break; - len = lens[work[sym]]; - } - - /* create new sub-table if needed */ - if (len > root && (huff & mask) != low) { - /* if first time, transition to sub-tables */ - if (drop == 0) - drop = root; - - /* increment past last table */ - next += min; /* here min is 1 << curr */ - - /* determine length of next table */ - curr = len - drop; - left = (int)(1 << curr); - while (curr + drop < max) { - left -= count[curr + drop]; - if (left <= 0) break; - curr++; - left <<= 1; - } - - /* check for enough space */ - used += 1U << curr; - if ((type == LENS && used > ENOUGH_LENS) || - (type == DISTS && used > ENOUGH_DISTS)) - return 1; - - /* point entry in root table to sub-table */ - low = huff & mask; - (*table)[low].op = (unsigned char)curr; - (*table)[low].bits = (unsigned char)root; - (*table)[low].val = (unsigned short)(next - *table); - } - } - - /* fill in remaining table entry if code is incomplete (guaranteed to have - at most one remaining entry, since if the code is incomplete, the - maximum code length that was allowed to get this far is one bit) */ - if (huff != 0) { - here.op = (unsigned char)64; /* invalid code marker */ - here.bits = (unsigned char)(len - drop); - here.val = (unsigned short)0; - next[huff] = here; - } - - /* set return parameters */ - *table += used; - *bits = root; - return 0; -} diff --git a/phobos/etc/c/zlib/inftrees.h b/phobos/etc/c/zlib/inftrees.h deleted file mode 100644 index 396f74b..0000000 --- a/phobos/etc/c/zlib/inftrees.h +++ /dev/null @@ -1,62 +0,0 @@ -/* inftrees.h -- header to use inftrees.c - * Copyright (C) 1995-2005, 2010 Mark Adler - * For conditions of distribution and use, see copyright notice in zlib.h - */ - -/* WARNING: this file should *not* be used by applications. It is - part of the implementation of the compression library and is - subject to change. Applications should only use zlib.h. - */ - -/* Structure for decoding tables. Each entry provides either the - information needed to do the operation requested by the code that - indexed that table entry, or it provides a pointer to another - table that indexes more bits of the code. op indicates whether - the entry is a pointer to another table, a literal, a length or - distance, an end-of-block, or an invalid code. For a table - pointer, the low four bits of op is the number of index bits of - that table. For a length or distance, the low four bits of op - is the number of extra bits to get after the code. bits is - the number of bits in this code or part of the code to drop off - of the bit buffer. val is the actual byte to output in the case - of a literal, the base length or distance, or the offset from - the current table to the next table. Each entry is four bytes. */ -typedef struct { - unsigned char op; /* operation, extra bits, table bits */ - unsigned char bits; /* bits in this part of the code */ - unsigned short val; /* offset in table or code value */ -} code; - -/* op values as set by inflate_table(): - 00000000 - literal - 0000tttt - table link, tttt != 0 is the number of table index bits - 0001eeee - length or distance, eeee is the number of extra bits - 01100000 - end of block - 01000000 - invalid code - */ - -/* Maximum size of the dynamic table. The maximum number of code structures is - 1444, which is the sum of 852 for literal/length codes and 592 for distance - codes. These values were found by exhaustive searches using the program - examples/enough.c found in the zlib distribution. The arguments to that - program are the number of symbols, the initial root table size, and the - maximum bit length of a code. "enough 286 9 15" for literal/length codes - returns 852, and "enough 30 6 15" for distance codes returns 592. The - initial root table size (9 or 6) is found in the fifth argument of the - inflate_table() calls in inflate.c and infback.c. If the root table size is - changed, then these maximum sizes would be need to be recalculated and - updated. */ -#define ENOUGH_LENS 852 -#define ENOUGH_DISTS 592 -#define ENOUGH (ENOUGH_LENS+ENOUGH_DISTS) - -/* Type of code to build for inflate_table() */ -typedef enum { - CODES, - LENS, - DISTS -} codetype; - -int ZLIB_INTERNAL inflate_table(codetype type, unsigned short FAR *lens, - unsigned codes, code FAR * FAR *table, - unsigned FAR *bits, unsigned short FAR *work); diff --git a/phobos/etc/c/zlib/test/example.c b/phobos/etc/c/zlib/test/example.c deleted file mode 100644 index c3521dd..0000000 --- a/phobos/etc/c/zlib/test/example.c +++ /dev/null @@ -1,546 +0,0 @@ -/* example.c -- usage example of the zlib compression library - * Copyright (C) 1995-2006, 2011, 2016 Jean-loup Gailly - * For conditions of distribution and use, see copyright notice in zlib.h - */ - -/* @(#) $Id$ */ - -#include "zlib.h" -#include - -#ifdef STDC -# include -# include -#endif - -#if defined(VMS) || defined(RISCOS) -# define TESTFILE "foo-gz" -#else -# define TESTFILE "foo.gz" -#endif - -#define CHECK_ERR(err, msg) { \ - if (err != Z_OK) { \ - fprintf(stderr, "%s error: %d\n", msg, err); \ - exit(1); \ - } \ -} - -static z_const char hello[] = "hello, hello!"; -/* "hello world" would be more standard, but the repeated "hello" - * stresses the compression code better, sorry... - */ - -static const char dictionary[] = "hello"; -static uLong dictId; /* Adler32 value of the dictionary */ - -#ifdef Z_SOLO - -static void *myalloc(void *q, unsigned n, unsigned m) { - (void)q; - return calloc(n, m); -} - -static void myfree(void *q, void *p) { - (void)q; - free(p); -} - -static alloc_func zalloc = myalloc; -static free_func zfree = myfree; - -#else /* !Z_SOLO */ - -static alloc_func zalloc = (alloc_func)0; -static free_func zfree = (free_func)0; - -/* =========================================================================== - * Test compress() and uncompress() - */ -static void test_compress(Byte *compr, uLong comprLen, Byte *uncompr, - uLong uncomprLen) { - int err; - uLong len = (uLong)strlen(hello)+1; - - err = compress(compr, &comprLen, (const Bytef*)hello, len); - CHECK_ERR(err, "compress"); - - strcpy((char*)uncompr, "garbage"); - - err = uncompress(uncompr, &uncomprLen, compr, comprLen); - CHECK_ERR(err, "uncompress"); - - if (strcmp((char*)uncompr, hello)) { - fprintf(stderr, "bad uncompress\n"); - exit(1); - } else { - printf("uncompress(): %s\n", (char *)uncompr); - } -} - -/* =========================================================================== - * Test read/write of .gz files - */ -static void test_gzio(const char *fname, Byte *uncompr, uLong uncomprLen) { -#ifdef NO_GZCOMPRESS - fprintf(stderr, "NO_GZCOMPRESS -- gz* functions cannot compress\n"); -#else - int err; - int len = (int)strlen(hello)+1; - gzFile file; - z_off_t pos; - - file = gzopen(fname, "wb"); - if (file == NULL) { - fprintf(stderr, "gzopen error\n"); - exit(1); - } - gzputc(file, 'h'); - if (gzputs(file, "ello") != 4) { - fprintf(stderr, "gzputs err: %s\n", gzerror(file, &err)); - exit(1); - } - if (gzprintf(file, ", %s!", "hello") != 8) { - fprintf(stderr, "gzprintf err: %s\n", gzerror(file, &err)); - exit(1); - } - gzseek(file, 1L, SEEK_CUR); /* add one zero byte */ - gzclose(file); - - file = gzopen(fname, "rb"); - if (file == NULL) { - fprintf(stderr, "gzopen error\n"); - exit(1); - } - strcpy((char*)uncompr, "garbage"); - - if (gzread(file, uncompr, (unsigned)uncomprLen) != len) { - fprintf(stderr, "gzread err: %s\n", gzerror(file, &err)); - exit(1); - } - if (strcmp((char*)uncompr, hello)) { - fprintf(stderr, "bad gzread: %s\n", (char*)uncompr); - exit(1); - } else { - printf("gzread(): %s\n", (char*)uncompr); - } - - pos = gzseek(file, -8L, SEEK_CUR); - if (pos != 6 || gztell(file) != pos) { - fprintf(stderr, "gzseek error, pos=%ld, gztell=%ld\n", - (long)pos, (long)gztell(file)); - exit(1); - } - - if (gzgetc(file) != ' ') { - fprintf(stderr, "gzgetc error\n"); - exit(1); - } - - if (gzungetc(' ', file) != ' ') { - fprintf(stderr, "gzungetc error\n"); - exit(1); - } - - gzgets(file, (char*)uncompr, (int)uncomprLen); - if (strlen((char*)uncompr) != 7) { /* " hello!" */ - fprintf(stderr, "gzgets err after gzseek: %s\n", gzerror(file, &err)); - exit(1); - } - if (strcmp((char*)uncompr, hello + 6)) { - fprintf(stderr, "bad gzgets after gzseek\n"); - exit(1); - } else { - printf("gzgets() after gzseek: %s\n", (char*)uncompr); - } - - gzclose(file); -#endif -} - -#endif /* Z_SOLO */ - -/* =========================================================================== - * Test deflate() with small buffers - */ -static void test_deflate(Byte *compr, uLong comprLen) { - z_stream c_stream; /* compression stream */ - int err; - uLong len = (uLong)strlen(hello)+1; - - c_stream.zalloc = zalloc; - c_stream.zfree = zfree; - c_stream.opaque = (voidpf)0; - - err = deflateInit(&c_stream, Z_DEFAULT_COMPRESSION); - CHECK_ERR(err, "deflateInit"); - - c_stream.next_in = (z_const unsigned char *)hello; - c_stream.next_out = compr; - - while (c_stream.total_in != len && c_stream.total_out < comprLen) { - c_stream.avail_in = c_stream.avail_out = 1; /* force small buffers */ - err = deflate(&c_stream, Z_NO_FLUSH); - CHECK_ERR(err, "deflate"); - } - /* Finish the stream, still forcing small buffers: */ - for (;;) { - c_stream.avail_out = 1; - err = deflate(&c_stream, Z_FINISH); - if (err == Z_STREAM_END) break; - CHECK_ERR(err, "deflate"); - } - - err = deflateEnd(&c_stream); - CHECK_ERR(err, "deflateEnd"); -} - -/* =========================================================================== - * Test inflate() with small buffers - */ -static void test_inflate(Byte *compr, uLong comprLen, Byte *uncompr, - uLong uncomprLen) { - int err; - z_stream d_stream; /* decompression stream */ - - strcpy((char*)uncompr, "garbage"); - - d_stream.zalloc = zalloc; - d_stream.zfree = zfree; - d_stream.opaque = (voidpf)0; - - d_stream.next_in = compr; - d_stream.avail_in = 0; - d_stream.next_out = uncompr; - - err = inflateInit(&d_stream); - CHECK_ERR(err, "inflateInit"); - - while (d_stream.total_out < uncomprLen && d_stream.total_in < comprLen) { - d_stream.avail_in = d_stream.avail_out = 1; /* force small buffers */ - err = inflate(&d_stream, Z_NO_FLUSH); - if (err == Z_STREAM_END) break; - CHECK_ERR(err, "inflate"); - } - - err = inflateEnd(&d_stream); - CHECK_ERR(err, "inflateEnd"); - - if (strcmp((char*)uncompr, hello)) { - fprintf(stderr, "bad inflate\n"); - exit(1); - } else { - printf("inflate(): %s\n", (char *)uncompr); - } -} - -/* =========================================================================== - * Test deflate() with large buffers and dynamic change of compression level - */ -static void test_large_deflate(Byte *compr, uLong comprLen, Byte *uncompr, - uLong uncomprLen) { - z_stream c_stream; /* compression stream */ - int err; - - c_stream.zalloc = zalloc; - c_stream.zfree = zfree; - c_stream.opaque = (voidpf)0; - - err = deflateInit(&c_stream, Z_BEST_SPEED); - CHECK_ERR(err, "deflateInit"); - - c_stream.next_out = compr; - c_stream.avail_out = (uInt)comprLen; - - /* At this point, uncompr is still mostly zeroes, so it should compress - * very well: - */ - c_stream.next_in = uncompr; - c_stream.avail_in = (uInt)uncomprLen; - err = deflate(&c_stream, Z_NO_FLUSH); - CHECK_ERR(err, "deflate"); - if (c_stream.avail_in != 0) { - fprintf(stderr, "deflate not greedy\n"); - exit(1); - } - - /* Feed in already compressed data and switch to no compression: */ - deflateParams(&c_stream, Z_NO_COMPRESSION, Z_DEFAULT_STRATEGY); - c_stream.next_in = compr; - c_stream.avail_in = (uInt)uncomprLen/2; - err = deflate(&c_stream, Z_NO_FLUSH); - CHECK_ERR(err, "deflate"); - - /* Switch back to compressing mode: */ - deflateParams(&c_stream, Z_BEST_COMPRESSION, Z_FILTERED); - c_stream.next_in = uncompr; - c_stream.avail_in = (uInt)uncomprLen; - err = deflate(&c_stream, Z_NO_FLUSH); - CHECK_ERR(err, "deflate"); - - err = deflate(&c_stream, Z_FINISH); - if (err != Z_STREAM_END) { - fprintf(stderr, "deflate should report Z_STREAM_END\n"); - exit(1); - } - err = deflateEnd(&c_stream); - CHECK_ERR(err, "deflateEnd"); -} - -/* =========================================================================== - * Test inflate() with large buffers - */ -static void test_large_inflate(Byte *compr, uLong comprLen, Byte *uncompr, - uLong uncomprLen) { - int err; - z_stream d_stream; /* decompression stream */ - - strcpy((char*)uncompr, "garbage"); - - d_stream.zalloc = zalloc; - d_stream.zfree = zfree; - d_stream.opaque = (voidpf)0; - - d_stream.next_in = compr; - d_stream.avail_in = (uInt)comprLen; - - err = inflateInit(&d_stream); - CHECK_ERR(err, "inflateInit"); - - for (;;) { - d_stream.next_out = uncompr; /* discard the output */ - d_stream.avail_out = (uInt)uncomprLen; - err = inflate(&d_stream, Z_NO_FLUSH); - if (err == Z_STREAM_END) break; - CHECK_ERR(err, "large inflate"); - } - - err = inflateEnd(&d_stream); - CHECK_ERR(err, "inflateEnd"); - - if (d_stream.total_out != 2*uncomprLen + uncomprLen/2) { - fprintf(stderr, "bad large inflate: %ld\n", d_stream.total_out); - exit(1); - } else { - printf("large_inflate(): OK\n"); - } -} - -/* =========================================================================== - * Test deflate() with full flush - */ -static void test_flush(Byte *compr, uLong *comprLen) { - z_stream c_stream; /* compression stream */ - int err; - uInt len = (uInt)strlen(hello)+1; - - c_stream.zalloc = zalloc; - c_stream.zfree = zfree; - c_stream.opaque = (voidpf)0; - - err = deflateInit(&c_stream, Z_DEFAULT_COMPRESSION); - CHECK_ERR(err, "deflateInit"); - - c_stream.next_in = (z_const unsigned char *)hello; - c_stream.next_out = compr; - c_stream.avail_in = 3; - c_stream.avail_out = (uInt)*comprLen; - err = deflate(&c_stream, Z_FULL_FLUSH); - CHECK_ERR(err, "deflate"); - - compr[3]++; /* force an error in first compressed block */ - c_stream.avail_in = len - 3; - - err = deflate(&c_stream, Z_FINISH); - if (err != Z_STREAM_END) { - CHECK_ERR(err, "deflate"); - } - err = deflateEnd(&c_stream); - CHECK_ERR(err, "deflateEnd"); - - *comprLen = c_stream.total_out; -} - -/* =========================================================================== - * Test inflateSync() - */ -static void test_sync(Byte *compr, uLong comprLen, Byte *uncompr, - uLong uncomprLen) { - int err; - z_stream d_stream; /* decompression stream */ - - strcpy((char*)uncompr, "garbage"); - - d_stream.zalloc = zalloc; - d_stream.zfree = zfree; - d_stream.opaque = (voidpf)0; - - d_stream.next_in = compr; - d_stream.avail_in = 2; /* just read the zlib header */ - - err = inflateInit(&d_stream); - CHECK_ERR(err, "inflateInit"); - - d_stream.next_out = uncompr; - d_stream.avail_out = (uInt)uncomprLen; - - err = inflate(&d_stream, Z_NO_FLUSH); - CHECK_ERR(err, "inflate"); - - d_stream.avail_in = (uInt)comprLen-2; /* read all compressed data */ - err = inflateSync(&d_stream); /* but skip the damaged part */ - CHECK_ERR(err, "inflateSync"); - - err = inflate(&d_stream, Z_FINISH); - if (err != Z_STREAM_END) { - fprintf(stderr, "inflate should report Z_STREAM_END\n"); - exit(1); - } - err = inflateEnd(&d_stream); - CHECK_ERR(err, "inflateEnd"); - - printf("after inflateSync(): hel%s\n", (char *)uncompr); -} - -/* =========================================================================== - * Test deflate() with preset dictionary - */ -static void test_dict_deflate(Byte *compr, uLong comprLen) { - z_stream c_stream; /* compression stream */ - int err; - - c_stream.zalloc = zalloc; - c_stream.zfree = zfree; - c_stream.opaque = (voidpf)0; - - err = deflateInit(&c_stream, Z_BEST_COMPRESSION); - CHECK_ERR(err, "deflateInit"); - - err = deflateSetDictionary(&c_stream, - (const Bytef*)dictionary, (int)sizeof(dictionary)); - CHECK_ERR(err, "deflateSetDictionary"); - - dictId = c_stream.adler; - c_stream.next_out = compr; - c_stream.avail_out = (uInt)comprLen; - - c_stream.next_in = (z_const unsigned char *)hello; - c_stream.avail_in = (uInt)strlen(hello)+1; - - err = deflate(&c_stream, Z_FINISH); - if (err != Z_STREAM_END) { - fprintf(stderr, "deflate should report Z_STREAM_END\n"); - exit(1); - } - err = deflateEnd(&c_stream); - CHECK_ERR(err, "deflateEnd"); -} - -/* =========================================================================== - * Test inflate() with a preset dictionary - */ -static void test_dict_inflate(Byte *compr, uLong comprLen, Byte *uncompr, - uLong uncomprLen) { - int err; - z_stream d_stream; /* decompression stream */ - - strcpy((char*)uncompr, "garbage"); - - d_stream.zalloc = zalloc; - d_stream.zfree = zfree; - d_stream.opaque = (voidpf)0; - - d_stream.next_in = compr; - d_stream.avail_in = (uInt)comprLen; - - err = inflateInit(&d_stream); - CHECK_ERR(err, "inflateInit"); - - d_stream.next_out = uncompr; - d_stream.avail_out = (uInt)uncomprLen; - - for (;;) { - err = inflate(&d_stream, Z_NO_FLUSH); - if (err == Z_STREAM_END) break; - if (err == Z_NEED_DICT) { - if (d_stream.adler != dictId) { - fprintf(stderr, "unexpected dictionary"); - exit(1); - } - err = inflateSetDictionary(&d_stream, (const Bytef*)dictionary, - (int)sizeof(dictionary)); - } - CHECK_ERR(err, "inflate with dict"); - } - - err = inflateEnd(&d_stream); - CHECK_ERR(err, "inflateEnd"); - - if (strcmp((char*)uncompr, hello)) { - fprintf(stderr, "bad inflate with dict\n"); - exit(1); - } else { - printf("inflate with dictionary: %s\n", (char *)uncompr); - } -} - -/* =========================================================================== - * Usage: example [output.gz [input.gz]] - */ - -int main(int argc, char *argv[]) { - Byte *compr, *uncompr; - uLong uncomprLen = 20000; - uLong comprLen = 3 * uncomprLen; - static const char* myVersion = ZLIB_VERSION; - - if (zlibVersion()[0] != myVersion[0]) { - fprintf(stderr, "incompatible zlib version\n"); - exit(1); - - } else if (strcmp(zlibVersion(), ZLIB_VERSION) != 0) { - fprintf(stderr, "warning: different zlib version linked: %s\n", - zlibVersion()); - } - - printf("zlib version %s = 0x%04x, compile flags = 0x%lx\n", - ZLIB_VERSION, ZLIB_VERNUM, zlibCompileFlags()); - - compr = (Byte*)calloc((uInt)comprLen, 1); - uncompr = (Byte*)calloc((uInt)uncomprLen, 1); - /* compr and uncompr are cleared to avoid reading uninitialized - * data and to ensure that uncompr compresses well. - */ - if (compr == Z_NULL || uncompr == Z_NULL) { - printf("out of memory\n"); - exit(1); - } - -#ifdef Z_SOLO - (void)argc; - (void)argv; -#else - test_compress(compr, comprLen, uncompr, uncomprLen); - - test_gzio((argc > 1 ? argv[1] : TESTFILE), - uncompr, uncomprLen); -#endif - - test_deflate(compr, comprLen); - test_inflate(compr, comprLen, uncompr, uncomprLen); - - test_large_deflate(compr, comprLen, uncompr, uncomprLen); - test_large_inflate(compr, comprLen, uncompr, uncomprLen); - - test_flush(compr, &comprLen); - test_sync(compr, comprLen, uncompr, uncomprLen); - comprLen = 3 * uncomprLen; - - test_dict_deflate(compr, comprLen); - test_dict_inflate(compr, comprLen, uncompr, uncomprLen); - - free(compr); - free(uncompr); - - return 0; -} diff --git a/phobos/etc/c/zlib/test/infcover.c b/phobos/etc/c/zlib/test/infcover.c deleted file mode 100644 index 8912c40..0000000 --- a/phobos/etc/c/zlib/test/infcover.c +++ /dev/null @@ -1,672 +0,0 @@ -/* infcover.c -- test zlib's inflate routines with full code coverage - * Copyright (C) 2011, 2016 Mark Adler - * For conditions of distribution and use, see copyright notice in zlib.h - */ - -/* to use, do: ./configure --cover && make cover */ - -#include -#include -#include -#include -#include "zlib.h" - -/* get definition of internal structure so we can mess with it (see pull()), - and so we can call inflate_trees() (see cover5()) */ -#define ZLIB_INTERNAL -#include "inftrees.h" -#include "inflate.h" - -#define local static - -/* -- memory tracking routines -- */ - -/* - These memory tracking routines are provided to zlib and track all of zlib's - allocations and deallocations, check for LIFO operations, keep a current - and high water mark of total bytes requested, optionally set a limit on the - total memory that can be allocated, and when done check for memory leaks. - - They are used as follows: - - z_stream strm; - mem_setup(&strm) initializes the memory tracking and sets the - zalloc, zfree, and opaque members of strm to use - memory tracking for all zlib operations on strm - mem_limit(&strm, limit) sets a limit on the total bytes requested -- a - request that exceeds this limit will result in an - allocation failure (returns NULL) -- setting the - limit to zero means no limit, which is the default - after mem_setup() - mem_used(&strm, "msg") prints to stderr "msg" and the total bytes used - mem_high(&strm, "msg") prints to stderr "msg" and the high water mark - mem_done(&strm, "msg") ends memory tracking, releases all allocations - for the tracking as well as leaked zlib blocks, if - any. If there was anything unusual, such as leaked - blocks, non-FIFO frees, or frees of addresses not - allocated, then "msg" and information about the - problem is printed to stderr. If everything is - normal, nothing is printed. mem_done resets the - strm members to Z_NULL to use the default memory - allocation routines on the next zlib initialization - using strm. - */ - -/* these items are strung together in a linked list, one for each allocation */ -struct mem_item { - void *ptr; /* pointer to allocated memory */ - size_t size; /* requested size of allocation */ - struct mem_item *next; /* pointer to next item in list, or NULL */ -}; - -/* this structure is at the root of the linked list, and tracks statistics */ -struct mem_zone { - struct mem_item *first; /* pointer to first item in list, or NULL */ - size_t total, highwater; /* total allocations, and largest total */ - size_t limit; /* memory allocation limit, or 0 if no limit */ - int notlifo, rogue; /* counts of non-LIFO frees and rogue frees */ -}; - -/* memory allocation routine to pass to zlib */ -local void *mem_alloc(void *mem, unsigned count, unsigned size) -{ - void *ptr; - struct mem_item *item; - struct mem_zone *zone = mem; - size_t len = count * (size_t)size; - - /* induced allocation failure */ - if (zone == NULL || (zone->limit && zone->total + len > zone->limit)) - return NULL; - - /* perform allocation using the standard library, fill memory with a - non-zero value to make sure that the code isn't depending on zeros */ - ptr = malloc(len); - if (ptr == NULL) - return NULL; - memset(ptr, 0xa5, len); - - /* create a new item for the list */ - item = malloc(sizeof(struct mem_item)); - if (item == NULL) { - free(ptr); - return NULL; - } - item->ptr = ptr; - item->size = len; - - /* insert item at the beginning of the list */ - item->next = zone->first; - zone->first = item; - - /* update the statistics */ - zone->total += item->size; - if (zone->total > zone->highwater) - zone->highwater = zone->total; - - /* return the allocated memory */ - return ptr; -} - -/* memory free routine to pass to zlib */ -local void mem_free(void *mem, void *ptr) -{ - struct mem_item *item, *next; - struct mem_zone *zone = mem; - - /* if no zone, just do a free */ - if (zone == NULL) { - free(ptr); - return; - } - - /* point next to the item that matches ptr, or NULL if not found -- remove - the item from the linked list if found */ - next = zone->first; - if (next) { - if (next->ptr == ptr) - zone->first = next->next; /* first one is it, remove from list */ - else { - do { /* search the linked list */ - item = next; - next = item->next; - } while (next != NULL && next->ptr != ptr); - if (next) { /* if found, remove from linked list */ - item->next = next->next; - zone->notlifo++; /* not a LIFO free */ - } - - } - } - - /* if found, update the statistics and free the item */ - if (next) { - zone->total -= next->size; - free(next); - } - - /* if not found, update the rogue count */ - else - zone->rogue++; - - /* in any case, do the requested free with the standard library function */ - free(ptr); -} - -/* set up a controlled memory allocation space for monitoring, set the stream - parameters to the controlled routines, with opaque pointing to the space */ -local void mem_setup(z_stream *strm) -{ - struct mem_zone *zone; - - zone = malloc(sizeof(struct mem_zone)); - assert(zone != NULL); - zone->first = NULL; - zone->total = 0; - zone->highwater = 0; - zone->limit = 0; - zone->notlifo = 0; - zone->rogue = 0; - strm->opaque = zone; - strm->zalloc = mem_alloc; - strm->zfree = mem_free; -} - -/* set a limit on the total memory allocation, or 0 to remove the limit */ -local void mem_limit(z_stream *strm, size_t limit) -{ - struct mem_zone *zone = strm->opaque; - - zone->limit = limit; -} - -/* show the current total requested allocations in bytes */ -local void mem_used(z_stream *strm, char *prefix) -{ - struct mem_zone *zone = strm->opaque; - - fprintf(stderr, "%s: %lu allocated\n", prefix, zone->total); -} - -/* show the high water allocation in bytes */ -local void mem_high(z_stream *strm, char *prefix) -{ - struct mem_zone *zone = strm->opaque; - - fprintf(stderr, "%s: %lu high water mark\n", prefix, zone->highwater); -} - -/* release the memory allocation zone -- if there are any surprises, notify */ -local void mem_done(z_stream *strm, char *prefix) -{ - int count = 0; - struct mem_item *item, *next; - struct mem_zone *zone = strm->opaque; - - /* show high water mark */ - mem_high(strm, prefix); - - /* free leftover allocations and item structures, if any */ - item = zone->first; - while (item != NULL) { - free(item->ptr); - next = item->next; - free(item); - item = next; - count++; - } - - /* issue alerts about anything unexpected */ - if (count || zone->total) - fprintf(stderr, "** %s: %lu bytes in %d blocks not freed\n", - prefix, zone->total, count); - if (zone->notlifo) - fprintf(stderr, "** %s: %d frees not LIFO\n", prefix, zone->notlifo); - if (zone->rogue) - fprintf(stderr, "** %s: %d frees not recognized\n", - prefix, zone->rogue); - - /* free the zone and delete from the stream */ - free(zone); - strm->opaque = Z_NULL; - strm->zalloc = Z_NULL; - strm->zfree = Z_NULL; -} - -/* -- inflate test routines -- */ - -/* Decode a hexadecimal string, set *len to length, in[] to the bytes. This - decodes liberally, in that hex digits can be adjacent, in which case two in - a row writes a byte. Or they can be delimited by any non-hex character, - where the delimiters are ignored except when a single hex digit is followed - by a delimiter, where that single digit writes a byte. The returned data is - allocated and must eventually be freed. NULL is returned if out of memory. - If the length is not needed, then len can be NULL. */ -local unsigned char *h2b(const char *hex, unsigned *len) -{ - unsigned char *in, *re; - unsigned next, val; - - in = malloc((strlen(hex) + 1) >> 1); - if (in == NULL) - return NULL; - next = 0; - val = 1; - do { - if (*hex >= '0' && *hex <= '9') - val = (val << 4) + *hex - '0'; - else if (*hex >= 'A' && *hex <= 'F') - val = (val << 4) + *hex - 'A' + 10; - else if (*hex >= 'a' && *hex <= 'f') - val = (val << 4) + *hex - 'a' + 10; - else if (val != 1 && val < 32) /* one digit followed by delimiter */ - val += 240; /* make it look like two digits */ - if (val > 255) { /* have two digits */ - in[next++] = val & 0xff; /* save the decoded byte */ - val = 1; /* start over */ - } - } while (*hex++); /* go through the loop with the terminating null */ - if (len != NULL) - *len = next; - re = realloc(in, next); - return re == NULL ? in : re; -} - -/* generic inflate() run, where hex is the hexadecimal input data, what is the - text to include in an error message, step is how much input data to feed - inflate() on each call, or zero to feed it all, win is the window bits - parameter to inflateInit2(), len is the size of the output buffer, and err - is the error code expected from the first inflate() call (the second - inflate() call is expected to return Z_STREAM_END). If win is 47, then - header information is collected with inflateGetHeader(). If a zlib stream - is looking for a dictionary, then an empty dictionary is provided. - inflate() is run until all of the input data is consumed. */ -local void inf(char *hex, char *what, unsigned step, int win, unsigned len, - int err) -{ - int ret; - unsigned have; - unsigned char *in, *out; - z_stream strm, copy; - gz_header head; - - mem_setup(&strm); - strm.avail_in = 0; - strm.next_in = Z_NULL; - ret = inflateInit2(&strm, win); - if (ret != Z_OK) { - mem_done(&strm, what); - return; - } - out = malloc(len); assert(out != NULL); - if (win == 47) { - head.extra = out; - head.extra_max = len; - head.name = out; - head.name_max = len; - head.comment = out; - head.comm_max = len; - ret = inflateGetHeader(&strm, &head); assert(ret == Z_OK); - } - in = h2b(hex, &have); assert(in != NULL); - if (step == 0 || step > have) - step = have; - strm.avail_in = step; - have -= step; - strm.next_in = in; - do { - strm.avail_out = len; - strm.next_out = out; - ret = inflate(&strm, Z_NO_FLUSH); assert(err == 9 || ret == err); - if (ret != Z_OK && ret != Z_BUF_ERROR && ret != Z_NEED_DICT) - break; - if (ret == Z_NEED_DICT) { - ret = inflateSetDictionary(&strm, in, 1); - assert(ret == Z_DATA_ERROR); - mem_limit(&strm, 1); - ret = inflateSetDictionary(&strm, out, 0); - assert(ret == Z_MEM_ERROR); - mem_limit(&strm, 0); - ((struct inflate_state *)strm.state)->mode = DICT; - ret = inflateSetDictionary(&strm, out, 0); - assert(ret == Z_OK); - ret = inflate(&strm, Z_NO_FLUSH); assert(ret == Z_BUF_ERROR); - } - ret = inflateCopy(©, &strm); assert(ret == Z_OK); - ret = inflateEnd(©); assert(ret == Z_OK); - err = 9; /* don't care next time around */ - have += strm.avail_in; - strm.avail_in = step > have ? have : step; - have -= strm.avail_in; - } while (strm.avail_in); - free(in); - free(out); - ret = inflateReset2(&strm, -8); assert(ret == Z_OK); - ret = inflateEnd(&strm); assert(ret == Z_OK); - mem_done(&strm, what); -} - -/* cover all of the lines in inflate.c up to inflate() */ -local void cover_support(void) -{ - int ret; - z_stream strm; - - mem_setup(&strm); - strm.avail_in = 0; - strm.next_in = Z_NULL; - ret = inflateInit(&strm); assert(ret == Z_OK); - mem_used(&strm, "inflate init"); - ret = inflatePrime(&strm, 5, 31); assert(ret == Z_OK); - ret = inflatePrime(&strm, -1, 0); assert(ret == Z_OK); - ret = inflateSetDictionary(&strm, Z_NULL, 0); - assert(ret == Z_STREAM_ERROR); - ret = inflateEnd(&strm); assert(ret == Z_OK); - mem_done(&strm, "prime"); - - inf("63 0", "force window allocation", 0, -15, 1, Z_OK); - inf("63 18 5", "force window replacement", 0, -8, 259, Z_OK); - inf("63 18 68 30 d0 0 0", "force split window update", 4, -8, 259, Z_OK); - inf("3 0", "use fixed blocks", 0, -15, 1, Z_STREAM_END); - inf("", "bad window size", 0, 1, 0, Z_STREAM_ERROR); - - mem_setup(&strm); - strm.avail_in = 0; - strm.next_in = Z_NULL; - ret = inflateInit_(&strm, "!", (int)sizeof(z_stream)); - assert(ret == Z_VERSION_ERROR); - mem_done(&strm, "wrong version"); - - strm.avail_in = 0; - strm.next_in = Z_NULL; - ret = inflateInit(&strm); assert(ret == Z_OK); - ret = inflateEnd(&strm); assert(ret == Z_OK); - fputs("inflate built-in memory routines\n", stderr); -} - -/* cover all inflate() header and trailer cases and code after inflate() */ -local void cover_wrap(void) -{ - int ret; - z_stream strm, copy; - unsigned char dict[257]; - - ret = inflate(Z_NULL, 0); assert(ret == Z_STREAM_ERROR); - ret = inflateEnd(Z_NULL); assert(ret == Z_STREAM_ERROR); - ret = inflateCopy(Z_NULL, Z_NULL); assert(ret == Z_STREAM_ERROR); - fputs("inflate bad parameters\n", stderr); - - inf("1f 8b 0 0", "bad gzip method", 0, 31, 0, Z_DATA_ERROR); - inf("1f 8b 8 80", "bad gzip flags", 0, 31, 0, Z_DATA_ERROR); - inf("77 85", "bad zlib method", 0, 15, 0, Z_DATA_ERROR); - inf("8 99", "set window size from header", 0, 0, 0, Z_OK); - inf("78 9c", "bad zlib window size", 0, 8, 0, Z_DATA_ERROR); - inf("78 9c 63 0 0 0 1 0 1", "check adler32", 0, 15, 1, Z_STREAM_END); - inf("1f 8b 8 1e 0 0 0 0 0 0 1 0 0 0 0 0 0", "bad header crc", 0, 47, 1, - Z_DATA_ERROR); - inf("1f 8b 8 2 0 0 0 0 0 0 1d 26 3 0 0 0 0 0 0 0 0 0", "check gzip length", - 0, 47, 0, Z_STREAM_END); - inf("78 90", "bad zlib header check", 0, 47, 0, Z_DATA_ERROR); - inf("8 b8 0 0 0 1", "need dictionary", 0, 8, 0, Z_NEED_DICT); - inf("78 9c 63 0", "compute adler32", 0, 15, 1, Z_OK); - - mem_setup(&strm); - strm.avail_in = 0; - strm.next_in = Z_NULL; - ret = inflateInit2(&strm, -8); - strm.avail_in = 2; - strm.next_in = (void *)"\x63"; - strm.avail_out = 1; - strm.next_out = (void *)&ret; - mem_limit(&strm, 1); - ret = inflate(&strm, Z_NO_FLUSH); assert(ret == Z_MEM_ERROR); - ret = inflate(&strm, Z_NO_FLUSH); assert(ret == Z_MEM_ERROR); - mem_limit(&strm, 0); - memset(dict, 0, 257); - ret = inflateSetDictionary(&strm, dict, 257); - assert(ret == Z_OK); - mem_limit(&strm, (sizeof(struct inflate_state) << 1) + 256); - ret = inflatePrime(&strm, 16, 0); assert(ret == Z_OK); - strm.avail_in = 2; - strm.next_in = (void *)"\x80"; - ret = inflateSync(&strm); assert(ret == Z_DATA_ERROR); - ret = inflate(&strm, Z_NO_FLUSH); assert(ret == Z_STREAM_ERROR); - strm.avail_in = 4; - strm.next_in = (void *)"\0\0\xff\xff"; - ret = inflateSync(&strm); assert(ret == Z_OK); - (void)inflateSyncPoint(&strm); - ret = inflateCopy(©, &strm); assert(ret == Z_MEM_ERROR); - mem_limit(&strm, 0); - ret = inflateUndermine(&strm, 1); assert(ret == Z_DATA_ERROR); - (void)inflateMark(&strm); - ret = inflateEnd(&strm); assert(ret == Z_OK); - mem_done(&strm, "miscellaneous, force memory errors"); -} - -/* input and output functions for inflateBack() */ -local unsigned pull(void *desc, unsigned char **buf) -{ - static unsigned int next = 0; - static unsigned char dat[] = {0x63, 0, 2, 0}; - struct inflate_state *state; - - if (desc == Z_NULL) { - next = 0; - return 0; /* no input (already provided at next_in) */ - } - state = (void *)((z_stream *)desc)->state; - if (state != Z_NULL) - state->mode = SYNC; /* force an otherwise impossible situation */ - return next < sizeof(dat) ? (*buf = dat + next++, 1) : 0; -} - -local int push(void *desc, unsigned char *buf, unsigned len) -{ - (void)buf; - (void)len; - return desc != Z_NULL; /* force error if desc not null */ -} - -/* cover inflateBack() up to common deflate data cases and after those */ -local void cover_back(void) -{ - int ret; - z_stream strm; - unsigned char win[32768]; - - ret = inflateBackInit_(Z_NULL, 0, win, 0, 0); - assert(ret == Z_VERSION_ERROR); - ret = inflateBackInit(Z_NULL, 0, win); assert(ret == Z_STREAM_ERROR); - ret = inflateBack(Z_NULL, Z_NULL, Z_NULL, Z_NULL, Z_NULL); - assert(ret == Z_STREAM_ERROR); - ret = inflateBackEnd(Z_NULL); assert(ret == Z_STREAM_ERROR); - fputs("inflateBack bad parameters\n", stderr); - - mem_setup(&strm); - ret = inflateBackInit(&strm, 15, win); assert(ret == Z_OK); - strm.avail_in = 2; - strm.next_in = (void *)"\x03"; - ret = inflateBack(&strm, pull, Z_NULL, push, Z_NULL); - assert(ret == Z_STREAM_END); - /* force output error */ - strm.avail_in = 3; - strm.next_in = (void *)"\x63\x00"; - ret = inflateBack(&strm, pull, Z_NULL, push, &strm); - assert(ret == Z_BUF_ERROR); - /* force mode error by mucking with state */ - ret = inflateBack(&strm, pull, &strm, push, Z_NULL); - assert(ret == Z_STREAM_ERROR); - ret = inflateBackEnd(&strm); assert(ret == Z_OK); - mem_done(&strm, "inflateBack bad state"); - - ret = inflateBackInit(&strm, 15, win); assert(ret == Z_OK); - ret = inflateBackEnd(&strm); assert(ret == Z_OK); - fputs("inflateBack built-in memory routines\n", stderr); -} - -/* do a raw inflate of data in hexadecimal with both inflate and inflateBack */ -local int try(char *hex, char *id, int err) -{ - int ret; - unsigned len, size; - unsigned char *in, *out, *win; - char *prefix; - z_stream strm; - - /* convert to hex */ - in = h2b(hex, &len); - assert(in != NULL); - - /* allocate work areas */ - size = len << 3; - out = malloc(size); - assert(out != NULL); - win = malloc(32768); - assert(win != NULL); - prefix = malloc(strlen(id) + 6); - assert(prefix != NULL); - - /* first with inflate */ - strcpy(prefix, id); - strcat(prefix, "-late"); - mem_setup(&strm); - strm.avail_in = 0; - strm.next_in = Z_NULL; - ret = inflateInit2(&strm, err < 0 ? 47 : -15); - assert(ret == Z_OK); - strm.avail_in = len; - strm.next_in = in; - do { - strm.avail_out = size; - strm.next_out = out; - ret = inflate(&strm, Z_TREES); - assert(ret != Z_STREAM_ERROR && ret != Z_MEM_ERROR); - if (ret == Z_DATA_ERROR || ret == Z_NEED_DICT) - break; - } while (strm.avail_in || strm.avail_out == 0); - if (err) { - assert(ret == Z_DATA_ERROR); - assert(strcmp(id, strm.msg) == 0); - } - inflateEnd(&strm); - mem_done(&strm, prefix); - - /* then with inflateBack */ - if (err >= 0) { - strcpy(prefix, id); - strcat(prefix, "-back"); - mem_setup(&strm); - ret = inflateBackInit(&strm, 15, win); - assert(ret == Z_OK); - strm.avail_in = len; - strm.next_in = in; - ret = inflateBack(&strm, pull, Z_NULL, push, Z_NULL); - assert(ret != Z_STREAM_ERROR); - if (err) { - assert(ret == Z_DATA_ERROR); - assert(strcmp(id, strm.msg) == 0); - } - inflateBackEnd(&strm); - mem_done(&strm, prefix); - } - - /* clean up */ - free(prefix); - free(win); - free(out); - free(in); - return ret; -} - -/* cover deflate data cases in both inflate() and inflateBack() */ -local void cover_inflate(void) -{ - try("0 0 0 0 0", "invalid stored block lengths", 1); - try("3 0", "fixed", 0); - try("6", "invalid block type", 1); - try("1 1 0 fe ff 0", "stored", 0); - try("fc 0 0", "too many length or distance symbols", 1); - try("4 0 fe ff", "invalid code lengths set", 1); - try("4 0 24 49 0", "invalid bit length repeat", 1); - try("4 0 24 e9 ff ff", "invalid bit length repeat", 1); - try("4 0 24 e9 ff 6d", "invalid code -- missing end-of-block", 1); - try("4 80 49 92 24 49 92 24 71 ff ff 93 11 0", - "invalid literal/lengths set", 1); - try("4 80 49 92 24 49 92 24 f b4 ff ff c3 84", "invalid distances set", 1); - try("4 c0 81 8 0 0 0 0 20 7f eb b 0 0", "invalid literal/length code", 1); - try("2 7e ff ff", "invalid distance code", 1); - try("c c0 81 0 0 0 0 0 90 ff 6b 4 0", "invalid distance too far back", 1); - - /* also trailer mismatch just in inflate() */ - try("1f 8b 8 0 0 0 0 0 0 0 3 0 0 0 0 1", "incorrect data check", -1); - try("1f 8b 8 0 0 0 0 0 0 0 3 0 0 0 0 0 0 0 0 1", - "incorrect length check", -1); - try("5 c0 21 d 0 0 0 80 b0 fe 6d 2f 91 6c", "pull 17", 0); - try("5 e0 81 91 24 cb b2 2c 49 e2 f 2e 8b 9a 47 56 9f fb fe ec d2 ff 1f", - "long code", 0); - try("ed c0 1 1 0 0 0 40 20 ff 57 1b 42 2c 4f", "length extra", 0); - try("ed cf c1 b1 2c 47 10 c4 30 fa 6f 35 1d 1 82 59 3d fb be 2e 2a fc f c", - "long distance and extra", 0); - try("ed c0 81 0 0 0 0 80 a0 fd a9 17 a9 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 0 0 0 0 0 6", "window end", 0); - inf("2 8 20 80 0 3 0", "inflate_fast TYPE return", 0, -15, 258, - Z_STREAM_END); - inf("63 18 5 40 c 0", "window wrap", 3, -8, 300, Z_OK); -} - -/* cover remaining lines in inftrees.c */ -local void cover_trees(void) -{ - int ret; - unsigned bits; - unsigned short lens[16], work[16]; - code *next, table[ENOUGH_DISTS]; - - /* we need to call inflate_table() directly in order to manifest not- - enough errors, since zlib insures that enough is always enough */ - for (bits = 0; bits < 15; bits++) - lens[bits] = (unsigned short)(bits + 1); - lens[15] = 15; - next = table; - bits = 15; - ret = inflate_table(DISTS, lens, 16, &next, &bits, work); - assert(ret == 1); - next = table; - bits = 1; - ret = inflate_table(DISTS, lens, 16, &next, &bits, work); - assert(ret == 1); - fputs("inflate_table not enough errors\n", stderr); -} - -/* cover remaining inffast.c decoding and window copying */ -local void cover_fast(void) -{ - inf("e5 e0 81 ad 6d cb b2 2c c9 01 1e 59 63 ae 7d ee fb 4d fd b5 35 41 68" - " ff 7f 0f 0 0 0", "fast length extra bits", 0, -8, 258, Z_DATA_ERROR); - inf("25 fd 81 b5 6d 59 b6 6a 49 ea af 35 6 34 eb 8c b9 f6 b9 1e ef 67 49" - " 50 fe ff ff 3f 0 0", "fast distance extra bits", 0, -8, 258, - Z_DATA_ERROR); - inf("3 7e 0 0 0 0 0", "fast invalid distance code", 0, -8, 258, - Z_DATA_ERROR); - inf("1b 7 0 0 0 0 0", "fast invalid literal/length code", 0, -8, 258, - Z_DATA_ERROR); - inf("d c7 1 ae eb 38 c 4 41 a0 87 72 de df fb 1f b8 36 b1 38 5d ff ff 0", - "fast 2nd level codes and too far back", 0, -8, 258, Z_DATA_ERROR); - inf("63 18 5 8c 10 8 0 0 0 0", "very common case", 0, -8, 259, Z_OK); - inf("63 60 60 18 c9 0 8 18 18 18 26 c0 28 0 29 0 0 0", - "contiguous and wrap around window", 6, -8, 259, Z_OK); - inf("63 0 3 0 0 0 0 0", "copy direct from output", 0, -8, 259, - Z_STREAM_END); -} - -int main(void) -{ - fprintf(stderr, "%s\n", zlibVersion()); - cover_support(); - cover_wrap(); - cover_back(); - cover_inflate(); - cover_trees(); - cover_fast(); - return 0; -} diff --git a/phobos/etc/c/zlib/test/minigzip.c b/phobos/etc/c/zlib/test/minigzip.c deleted file mode 100644 index 134e10e..0000000 --- a/phobos/etc/c/zlib/test/minigzip.c +++ /dev/null @@ -1,579 +0,0 @@ -/* minigzip.c -- simulate gzip using the zlib compression library - * Copyright (C) 1995-2006, 2010, 2011, 2016 Jean-loup Gailly - * For conditions of distribution and use, see copyright notice in zlib.h - */ - -/* - * minigzip is a minimal implementation of the gzip utility. This is - * only an example of using zlib and isn't meant to replace the - * full-featured gzip. No attempt is made to deal with file systems - * limiting names to 14 or 8+3 characters, etc... Error checking is - * very limited. So use minigzip only for testing; use gzip for the - * real thing. On MSDOS, use only on file names without extension - * or in pipe mode. - */ - -/* @(#) $Id$ */ - -#include "zlib.h" -#include - -#ifdef STDC -# include -# include -#endif - -#ifdef USE_MMAP -# include -# include -# include -#endif - -#if defined(MSDOS) || defined(OS2) || defined(WIN32) || defined(__CYGWIN__) -# include -# include -# ifdef UNDER_CE -# include -# endif -# define SET_BINARY_MODE(file) setmode(fileno(file), O_BINARY) -#else -# define SET_BINARY_MODE(file) -#endif - -#if defined(_MSC_VER) && _MSC_VER < 1900 -# define snprintf _snprintf -#endif - -#ifdef VMS -# define unlink delete -# define GZ_SUFFIX "-gz" -#endif -#ifdef RISCOS -# define unlink remove -# define GZ_SUFFIX "-gz" -# define fileno(file) file->__file -#endif -#if defined(__MWERKS__) && __dest_os != __be_os && __dest_os != __win32_os -# include /* for fileno */ -#endif - -#if !defined(Z_HAVE_UNISTD_H) && !defined(_LARGEFILE64_SOURCE) -#ifndef WIN32 /* unlink already in stdio.h for WIN32 */ - extern int unlink(const char *); -#endif -#endif - -#if defined(UNDER_CE) -# include -# define perror(s) pwinerror(s) - -/* Map the Windows error number in ERROR to a locale-dependent error - message string and return a pointer to it. Typically, the values - for ERROR come from GetLastError. - - The string pointed to shall not be modified by the application, - but may be overwritten by a subsequent call to strwinerror - - The strwinerror function does not change the current setting - of GetLastError. */ - -static char *strwinerror (error) - DWORD error; -{ - static char buf[1024]; - - wchar_t *msgbuf; - DWORD lasterr = GetLastError(); - DWORD chars = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM - | FORMAT_MESSAGE_ALLOCATE_BUFFER, - NULL, - error, - 0, /* Default language */ - (LPVOID)&msgbuf, - 0, - NULL); - if (chars != 0) { - /* If there is an \r\n appended, zap it. */ - if (chars >= 2 - && msgbuf[chars - 2] == '\r' && msgbuf[chars - 1] == '\n') { - chars -= 2; - msgbuf[chars] = 0; - } - - if (chars > sizeof (buf) - 1) { - chars = sizeof (buf) - 1; - msgbuf[chars] = 0; - } - - wcstombs(buf, msgbuf, chars + 1); - LocalFree(msgbuf); - } - else { - sprintf(buf, "unknown win32 error (%ld)", error); - } - - SetLastError(lasterr); - return buf; -} - -static void pwinerror (s) - const char *s; -{ - if (s && *s) - fprintf(stderr, "%s: %s\n", s, strwinerror(GetLastError ())); - else - fprintf(stderr, "%s\n", strwinerror(GetLastError ())); -} - -#endif /* UNDER_CE */ - -#ifndef GZ_SUFFIX -# define GZ_SUFFIX ".gz" -#endif -#define SUFFIX_LEN (sizeof(GZ_SUFFIX)-1) - -#define BUFLEN 16384 -#define MAX_NAME_LEN 1024 - -#ifdef MAXSEG_64K -# define local static - /* Needed for systems with limitation on stack size. */ -#else -# define local -#endif - -#ifdef Z_SOLO -/* for Z_SOLO, create simplified gz* functions using deflate and inflate */ - -#if defined(Z_HAVE_UNISTD_H) || defined(Z_LARGE) -# include /* for unlink() */ -#endif - -static void *myalloc(void *q, unsigned n, unsigned m) { - (void)q; - return calloc(n, m); -} - -static void myfree(void *q, void *p) { - (void)q; - free(p); -} - -typedef struct gzFile_s { - FILE *file; - int write; - int err; - char *msg; - z_stream strm; -} *gzFile; - -static gzFile gz_open(const char *path, int fd, const char *mode) { - gzFile gz; - int ret; - - gz = malloc(sizeof(struct gzFile_s)); - if (gz == NULL) - return NULL; - gz->write = strchr(mode, 'w') != NULL; - gz->strm.zalloc = myalloc; - gz->strm.zfree = myfree; - gz->strm.opaque = Z_NULL; - if (gz->write) - ret = deflateInit2(&(gz->strm), -1, 8, 15 + 16, 8, 0); - else { - gz->strm.next_in = 0; - gz->strm.avail_in = Z_NULL; - ret = inflateInit2(&(gz->strm), 15 + 16); - } - if (ret != Z_OK) { - free(gz); - return NULL; - } - gz->file = path == NULL ? fdopen(fd, gz->write ? "wb" : "rb") : - fopen(path, gz->write ? "wb" : "rb"); - if (gz->file == NULL) { - gz->write ? deflateEnd(&(gz->strm)) : inflateEnd(&(gz->strm)); - free(gz); - return NULL; - } - gz->err = 0; - gz->msg = ""; - return gz; -} - -static gzFile gzopen(const char *path, const char *mode) { - return gz_open(path, -1, mode); -} - -static gzFile gzdopen(int fd, const char *mode) { - return gz_open(NULL, fd, mode); -} - -static int gzwrite(gzFile gz, const void *buf, unsigned len) { - z_stream *strm; - unsigned char out[BUFLEN]; - - if (gz == NULL || !gz->write) - return 0; - strm = &(gz->strm); - strm->next_in = (void *)buf; - strm->avail_in = len; - do { - strm->next_out = out; - strm->avail_out = BUFLEN; - (void)deflate(strm, Z_NO_FLUSH); - fwrite(out, 1, BUFLEN - strm->avail_out, gz->file); - } while (strm->avail_out == 0); - return len; -} - -static int gzread(gzFile gz, void *buf, unsigned len) { - int ret; - unsigned got; - unsigned char in[1]; - z_stream *strm; - - if (gz == NULL || gz->write) - return 0; - if (gz->err) - return 0; - strm = &(gz->strm); - strm->next_out = (void *)buf; - strm->avail_out = len; - do { - got = fread(in, 1, 1, gz->file); - if (got == 0) - break; - strm->next_in = in; - strm->avail_in = 1; - ret = inflate(strm, Z_NO_FLUSH); - if (ret == Z_DATA_ERROR) { - gz->err = Z_DATA_ERROR; - gz->msg = strm->msg; - return 0; - } - if (ret == Z_STREAM_END) - inflateReset(strm); - } while (strm->avail_out); - return len - strm->avail_out; -} - -static int gzclose(gzFile gz) { - z_stream *strm; - unsigned char out[BUFLEN]; - - if (gz == NULL) - return Z_STREAM_ERROR; - strm = &(gz->strm); - if (gz->write) { - strm->next_in = Z_NULL; - strm->avail_in = 0; - do { - strm->next_out = out; - strm->avail_out = BUFLEN; - (void)deflate(strm, Z_FINISH); - fwrite(out, 1, BUFLEN - strm->avail_out, gz->file); - } while (strm->avail_out == 0); - deflateEnd(strm); - } - else - inflateEnd(strm); - fclose(gz->file); - free(gz); - return Z_OK; -} - -static const char *gzerror(gzFile gz, int *err) { - *err = gz->err; - return gz->msg; -} - -#endif - -static char *prog; - -/* =========================================================================== - * Display error message and exit - */ -static void error(const char *msg) { - fprintf(stderr, "%s: %s\n", prog, msg); - exit(1); -} - -#ifdef USE_MMAP /* MMAP version, Miguel Albrecht */ - -/* Try compressing the input file at once using mmap. Return Z_OK if - * success, Z_ERRNO otherwise. - */ -static int gz_compress_mmap(FILE *in, gzFile out) { - int len; - int err; - int ifd = fileno(in); - caddr_t buf; /* mmap'ed buffer for the entire input file */ - off_t buf_len; /* length of the input file */ - struct stat sb; - - /* Determine the size of the file, needed for mmap: */ - if (fstat(ifd, &sb) < 0) return Z_ERRNO; - buf_len = sb.st_size; - if (buf_len <= 0) return Z_ERRNO; - - /* Now do the actual mmap: */ - buf = mmap((caddr_t) 0, buf_len, PROT_READ, MAP_SHARED, ifd, (off_t)0); - if (buf == (caddr_t)(-1)) return Z_ERRNO; - - /* Compress the whole file at once: */ - len = gzwrite(out, (char *)buf, (unsigned)buf_len); - - if (len != (int)buf_len) error(gzerror(out, &err)); - - munmap(buf, buf_len); - fclose(in); - if (gzclose(out) != Z_OK) error("failed gzclose"); - return Z_OK; -} -#endif /* USE_MMAP */ - -/* =========================================================================== - * Compress input to output then close both files. - */ - -static void gz_compress(FILE *in, gzFile out) { - local char buf[BUFLEN]; - int len; - int err; - -#ifdef USE_MMAP - /* Try first compressing with mmap. If mmap fails (minigzip used in a - * pipe), use the normal fread loop. - */ - if (gz_compress_mmap(in, out) == Z_OK) return; -#endif - for (;;) { - len = (int)fread(buf, 1, sizeof(buf), in); - if (ferror(in)) { - perror("fread"); - exit(1); - } - if (len == 0) break; - - if (gzwrite(out, buf, (unsigned)len) != len) error(gzerror(out, &err)); - } - fclose(in); - if (gzclose(out) != Z_OK) error("failed gzclose"); -} - -/* =========================================================================== - * Uncompress input to output then close both files. - */ -static void gz_uncompress(gzFile in, FILE *out) { - local char buf[BUFLEN]; - int len; - int err; - - for (;;) { - len = gzread(in, buf, sizeof(buf)); - if (len < 0) error (gzerror(in, &err)); - if (len == 0) break; - - if ((int)fwrite(buf, 1, (unsigned)len, out) != len) { - error("failed fwrite"); - } - } - if (fclose(out)) error("failed fclose"); - - if (gzclose(in) != Z_OK) error("failed gzclose"); -} - - -/* =========================================================================== - * Compress the given file: create a corresponding .gz file and remove the - * original. - */ -static void file_compress(char *file, char *mode) { - local char outfile[MAX_NAME_LEN]; - FILE *in; - gzFile out; - - if (strlen(file) + strlen(GZ_SUFFIX) >= sizeof(outfile)) { - fprintf(stderr, "%s: filename too long\n", prog); - exit(1); - } - -#if !defined(NO_snprintf) && !defined(NO_vsnprintf) - snprintf(outfile, sizeof(outfile), "%s%s", file, GZ_SUFFIX); -#else - strcpy(outfile, file); - strcat(outfile, GZ_SUFFIX); -#endif - - in = fopen(file, "rb"); - if (in == NULL) { - perror(file); - exit(1); - } - out = gzopen(outfile, mode); - if (out == NULL) { - fprintf(stderr, "%s: can't gzopen %s\n", prog, outfile); - exit(1); - } - gz_compress(in, out); - - unlink(file); -} - - -/* =========================================================================== - * Uncompress the given file and remove the original. - */ -static void file_uncompress(char *file) { - local char buf[MAX_NAME_LEN]; - char *infile, *outfile; - FILE *out; - gzFile in; - z_size_t len = strlen(file); - - if (len + strlen(GZ_SUFFIX) >= sizeof(buf)) { - fprintf(stderr, "%s: filename too long\n", prog); - exit(1); - } - -#if !defined(NO_snprintf) && !defined(NO_vsnprintf) - snprintf(buf, sizeof(buf), "%s", file); -#else - strcpy(buf, file); -#endif - - if (len > SUFFIX_LEN && strcmp(file+len-SUFFIX_LEN, GZ_SUFFIX) == 0) { - infile = file; - outfile = buf; - outfile[len-3] = '\0'; - } else { - outfile = file; - infile = buf; -#if !defined(NO_snprintf) && !defined(NO_vsnprintf) - snprintf(buf + len, sizeof(buf) - len, "%s", GZ_SUFFIX); -#else - strcat(infile, GZ_SUFFIX); -#endif - } - in = gzopen(infile, "rb"); - if (in == NULL) { - fprintf(stderr, "%s: can't gzopen %s\n", prog, infile); - exit(1); - } - out = fopen(outfile, "wb"); - if (out == NULL) { - perror(file); - exit(1); - } - - gz_uncompress(in, out); - - unlink(infile); -} - - -/* =========================================================================== - * Usage: minigzip [-c] [-d] [-f] [-h] [-r] [-1 to -9] [files...] - * -c : write to standard output - * -d : decompress - * -f : compress with Z_FILTERED - * -h : compress with Z_HUFFMAN_ONLY - * -r : compress with Z_RLE - * -1 to -9 : compression level - */ - -int main(int argc, char *argv[]) { - int copyout = 0; - int uncompr = 0; - gzFile file; - char *bname, outmode[20]; - -#if !defined(NO_snprintf) && !defined(NO_vsnprintf) - snprintf(outmode, sizeof(outmode), "%s", "wb6 "); -#else - strcpy(outmode, "wb6 "); -#endif - - prog = argv[0]; - bname = strrchr(argv[0], '/'); - if (bname) - bname++; - else - bname = argv[0]; - argc--, argv++; - - if (!strcmp(bname, "gunzip")) - uncompr = 1; - else if (!strcmp(bname, "zcat")) - copyout = uncompr = 1; - - while (argc > 0) { - if (strcmp(*argv, "-c") == 0) - copyout = 1; - else if (strcmp(*argv, "-d") == 0) - uncompr = 1; - else if (strcmp(*argv, "-f") == 0) - outmode[3] = 'f'; - else if (strcmp(*argv, "-h") == 0) - outmode[3] = 'h'; - else if (strcmp(*argv, "-r") == 0) - outmode[3] = 'R'; - else if ((*argv)[0] == '-' && (*argv)[1] >= '1' && (*argv)[1] <= '9' && - (*argv)[2] == 0) - outmode[2] = (*argv)[1]; - else - break; - argc--, argv++; - } - if (outmode[3] == ' ') - outmode[3] = 0; - if (argc == 0) { - SET_BINARY_MODE(stdin); - SET_BINARY_MODE(stdout); - if (uncompr) { - file = gzdopen(fileno(stdin), "rb"); - if (file == NULL) error("can't gzdopen stdin"); - gz_uncompress(file, stdout); - } else { - file = gzdopen(fileno(stdout), outmode); - if (file == NULL) error("can't gzdopen stdout"); - gz_compress(stdin, file); - } - } else { - if (copyout) { - SET_BINARY_MODE(stdout); - } - do { - if (uncompr) { - if (copyout) { - file = gzopen(*argv, "rb"); - if (file == NULL) - fprintf(stderr, "%s: can't gzopen %s\n", prog, *argv); - else - gz_uncompress(file, stdout); - } else { - file_uncompress(*argv); - } - } else { - if (copyout) { - FILE * in = fopen(*argv, "rb"); - - if (in == NULL) { - perror(*argv); - } else { - file = gzdopen(fileno(stdout), outmode); - if (file == NULL) error("can't gzdopen stdout"); - - gz_compress(in, file); - } - - } else { - file_compress(*argv, outmode); - } - } - } while (argv++, --argc); - } - return 0; -} diff --git a/phobos/etc/c/zlib/trees.c b/phobos/etc/c/zlib/trees.c deleted file mode 100644 index 6a523ef..0000000 --- a/phobos/etc/c/zlib/trees.c +++ /dev/null @@ -1,1117 +0,0 @@ -/* trees.c -- output deflated data using Huffman coding - * Copyright (C) 1995-2024 Jean-loup Gailly - * detect_data_type() function provided freely by Cosmin Truta, 2006 - * For conditions of distribution and use, see copyright notice in zlib.h - */ - -/* - * ALGORITHM - * - * The "deflation" process uses several Huffman trees. The more - * common source values are represented by shorter bit sequences. - * - * Each code tree is stored in a compressed form which is itself - * a Huffman encoding of the lengths of all the code strings (in - * ascending order by source values). The actual code strings are - * reconstructed from the lengths in the inflate process, as described - * in the deflate specification. - * - * REFERENCES - * - * Deutsch, L.P.,"'Deflate' Compressed Data Format Specification". - * Available in ftp.uu.net:/pub/archiving/zip/doc/deflate-1.1.doc - * - * Storer, James A. - * Data Compression: Methods and Theory, pp. 49-50. - * Computer Science Press, 1988. ISBN 0-7167-8156-5. - * - * Sedgewick, R. - * Algorithms, p290. - * Addison-Wesley, 1983. ISBN 0-201-06672-6. - */ - -/* @(#) $Id$ */ - -/* #define GEN_TREES_H */ - -#include "deflate.h" - -#ifdef ZLIB_DEBUG -# include -#endif - -/* =========================================================================== - * Constants - */ - -#define MAX_BL_BITS 7 -/* Bit length codes must not exceed MAX_BL_BITS bits */ - -#define END_BLOCK 256 -/* end of block literal code */ - -#define REP_3_6 16 -/* repeat previous bit length 3-6 times (2 bits of repeat count) */ - -#define REPZ_3_10 17 -/* repeat a zero length 3-10 times (3 bits of repeat count) */ - -#define REPZ_11_138 18 -/* repeat a zero length 11-138 times (7 bits of repeat count) */ - -local const int extra_lbits[LENGTH_CODES] /* extra bits for each length code */ - = {0,0,0,0,0,0,0,0,1,1,1,1,2,2,2,2,3,3,3,3,4,4,4,4,5,5,5,5,0}; - -local const int extra_dbits[D_CODES] /* extra bits for each distance code */ - = {0,0,0,0,1,1,2,2,3,3,4,4,5,5,6,6,7,7,8,8,9,9,10,10,11,11,12,12,13,13}; - -local const int extra_blbits[BL_CODES]/* extra bits for each bit length code */ - = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,3,7}; - -local const uch bl_order[BL_CODES] - = {16,17,18,0,8,7,9,6,10,5,11,4,12,3,13,2,14,1,15}; -/* The lengths of the bit length codes are sent in order of decreasing - * probability, to avoid transmitting the lengths for unused bit length codes. - */ - -/* =========================================================================== - * Local data. These are initialized only once. - */ - -#define DIST_CODE_LEN 512 /* see definition of array dist_code below */ - -#if defined(GEN_TREES_H) || !defined(STDC) -/* non ANSI compilers may not accept trees.h */ - -local ct_data static_ltree[L_CODES+2]; -/* The static literal tree. Since the bit lengths are imposed, there is no - * need for the L_CODES extra codes used during heap construction. However - * The codes 286 and 287 are needed to build a canonical tree (see _tr_init - * below). - */ - -local ct_data static_dtree[D_CODES]; -/* The static distance tree. (Actually a trivial tree since all codes use - * 5 bits.) - */ - -uch _dist_code[DIST_CODE_LEN]; -/* Distance codes. The first 256 values correspond to the distances - * 3 .. 258, the last 256 values correspond to the top 8 bits of - * the 15 bit distances. - */ - -uch _length_code[MAX_MATCH-MIN_MATCH+1]; -/* length code for each normalized match length (0 == MIN_MATCH) */ - -local int base_length[LENGTH_CODES]; -/* First normalized length for each code (0 = MIN_MATCH) */ - -local int base_dist[D_CODES]; -/* First normalized distance for each code (0 = distance of 1) */ - -#else -# include "trees.h" -#endif /* GEN_TREES_H */ - -struct static_tree_desc_s { - const ct_data *static_tree; /* static tree or NULL */ - const intf *extra_bits; /* extra bits for each code or NULL */ - int extra_base; /* base index for extra_bits */ - int elems; /* max number of elements in the tree */ - int max_length; /* max bit length for the codes */ -}; - -#ifdef NO_INIT_GLOBAL_POINTERS -# define TCONST -#else -# define TCONST const -#endif - -local TCONST static_tree_desc static_l_desc = -{static_ltree, extra_lbits, LITERALS+1, L_CODES, MAX_BITS}; - -local TCONST static_tree_desc static_d_desc = -{static_dtree, extra_dbits, 0, D_CODES, MAX_BITS}; - -local TCONST static_tree_desc static_bl_desc = -{(const ct_data *)0, extra_blbits, 0, BL_CODES, MAX_BL_BITS}; - -/* =========================================================================== - * Output a short LSB first on the stream. - * IN assertion: there is enough room in pendingBuf. - */ -#define put_short(s, w) { \ - put_byte(s, (uch)((w) & 0xff)); \ - put_byte(s, (uch)((ush)(w) >> 8)); \ -} - -/* =========================================================================== - * Reverse the first len bits of a code, using straightforward code (a faster - * method would use a table) - * IN assertion: 1 <= len <= 15 - */ -local unsigned bi_reverse(unsigned code, int len) { - register unsigned res = 0; - do { - res |= code & 1; - code >>= 1, res <<= 1; - } while (--len > 0); - return res >> 1; -} - -/* =========================================================================== - * Flush the bit buffer, keeping at most 7 bits in it. - */ -local void bi_flush(deflate_state *s) { - if (s->bi_valid == 16) { - put_short(s, s->bi_buf); - s->bi_buf = 0; - s->bi_valid = 0; - } else if (s->bi_valid >= 8) { - put_byte(s, (Byte)s->bi_buf); - s->bi_buf >>= 8; - s->bi_valid -= 8; - } -} - -/* =========================================================================== - * Flush the bit buffer and align the output on a byte boundary - */ -local void bi_windup(deflate_state *s) { - if (s->bi_valid > 8) { - put_short(s, s->bi_buf); - } else if (s->bi_valid > 0) { - put_byte(s, (Byte)s->bi_buf); - } - s->bi_buf = 0; - s->bi_valid = 0; -#ifdef ZLIB_DEBUG - s->bits_sent = (s->bits_sent + 7) & ~7; -#endif -} - -/* =========================================================================== - * Generate the codes for a given tree and bit counts (which need not be - * optimal). - * IN assertion: the array bl_count contains the bit length statistics for - * the given tree and the field len is set for all tree elements. - * OUT assertion: the field code is set for all tree elements of non - * zero code length. - */ -local void gen_codes(ct_data *tree, int max_code, ushf *bl_count) { - ush next_code[MAX_BITS+1]; /* next code value for each bit length */ - unsigned code = 0; /* running code value */ - int bits; /* bit index */ - int n; /* code index */ - - /* The distribution counts are first used to generate the code values - * without bit reversal. - */ - for (bits = 1; bits <= MAX_BITS; bits++) { - code = (code + bl_count[bits - 1]) << 1; - next_code[bits] = (ush)code; - } - /* Check that the bit counts in bl_count are consistent. The last code - * must be all ones. - */ - Assert (code + bl_count[MAX_BITS] - 1 == (1 << MAX_BITS) - 1, - "inconsistent bit counts"); - Tracev((stderr,"\ngen_codes: max_code %d ", max_code)); - - for (n = 0; n <= max_code; n++) { - int len = tree[n].Len; - if (len == 0) continue; - /* Now reverse the bits */ - tree[n].Code = (ush)bi_reverse(next_code[len]++, len); - - Tracecv(tree != static_ltree, (stderr,"\nn %3d %c l %2d c %4x (%x) ", - n, (isgraph(n) ? n : ' '), len, tree[n].Code, next_code[len] - 1)); - } -} - -#ifdef GEN_TREES_H -local void gen_trees_header(void); -#endif - -#ifndef ZLIB_DEBUG -# define send_code(s, c, tree) send_bits(s, tree[c].Code, tree[c].Len) - /* Send a code of the given tree. c and tree must not have side effects */ - -#else /* !ZLIB_DEBUG */ -# define send_code(s, c, tree) \ - { if (z_verbose>2) fprintf(stderr,"\ncd %3d ",(c)); \ - send_bits(s, tree[c].Code, tree[c].Len); } -#endif - -/* =========================================================================== - * Send a value on a given number of bits. - * IN assertion: length <= 16 and value fits in length bits. - */ -#ifdef ZLIB_DEBUG -local void send_bits(deflate_state *s, int value, int length) { - Tracevv((stderr," l %2d v %4x ", length, value)); - Assert(length > 0 && length <= 15, "invalid length"); - s->bits_sent += (ulg)length; - - /* If not enough room in bi_buf, use (valid) bits from bi_buf and - * (16 - bi_valid) bits from value, leaving (width - (16 - bi_valid)) - * unused bits in value. - */ - if (s->bi_valid > (int)Buf_size - length) { - s->bi_buf |= (ush)value << s->bi_valid; - put_short(s, s->bi_buf); - s->bi_buf = (ush)value >> (Buf_size - s->bi_valid); - s->bi_valid += length - Buf_size; - } else { - s->bi_buf |= (ush)value << s->bi_valid; - s->bi_valid += length; - } -} -#else /* !ZLIB_DEBUG */ - -#define send_bits(s, value, length) \ -{ int len = length;\ - if (s->bi_valid > (int)Buf_size - len) {\ - int val = (int)value;\ - s->bi_buf |= (ush)val << s->bi_valid;\ - put_short(s, s->bi_buf);\ - s->bi_buf = (ush)val >> (Buf_size - s->bi_valid);\ - s->bi_valid += len - Buf_size;\ - } else {\ - s->bi_buf |= (ush)(value) << s->bi_valid;\ - s->bi_valid += len;\ - }\ -} -#endif /* ZLIB_DEBUG */ - - -/* the arguments must not have side effects */ - -/* =========================================================================== - * Initialize the various 'constant' tables. - */ -local void tr_static_init(void) { -#if defined(GEN_TREES_H) || !defined(STDC) - static int static_init_done = 0; - int n; /* iterates over tree elements */ - int bits; /* bit counter */ - int length; /* length value */ - int code; /* code value */ - int dist; /* distance index */ - ush bl_count[MAX_BITS+1]; - /* number of codes at each bit length for an optimal tree */ - - if (static_init_done) return; - - /* For some embedded targets, global variables are not initialized: */ -#ifdef NO_INIT_GLOBAL_POINTERS - static_l_desc.static_tree = static_ltree; - static_l_desc.extra_bits = extra_lbits; - static_d_desc.static_tree = static_dtree; - static_d_desc.extra_bits = extra_dbits; - static_bl_desc.extra_bits = extra_blbits; -#endif - - /* Initialize the mapping length (0..255) -> length code (0..28) */ - length = 0; - for (code = 0; code < LENGTH_CODES-1; code++) { - base_length[code] = length; - for (n = 0; n < (1 << extra_lbits[code]); n++) { - _length_code[length++] = (uch)code; - } - } - Assert (length == 256, "tr_static_init: length != 256"); - /* Note that the length 255 (match length 258) can be represented - * in two different ways: code 284 + 5 bits or code 285, so we - * overwrite length_code[255] to use the best encoding: - */ - _length_code[length - 1] = (uch)code; - - /* Initialize the mapping dist (0..32K) -> dist code (0..29) */ - dist = 0; - for (code = 0 ; code < 16; code++) { - base_dist[code] = dist; - for (n = 0; n < (1 << extra_dbits[code]); n++) { - _dist_code[dist++] = (uch)code; - } - } - Assert (dist == 256, "tr_static_init: dist != 256"); - dist >>= 7; /* from now on, all distances are divided by 128 */ - for ( ; code < D_CODES; code++) { - base_dist[code] = dist << 7; - for (n = 0; n < (1 << (extra_dbits[code] - 7)); n++) { - _dist_code[256 + dist++] = (uch)code; - } - } - Assert (dist == 256, "tr_static_init: 256 + dist != 512"); - - /* Construct the codes of the static literal tree */ - for (bits = 0; bits <= MAX_BITS; bits++) bl_count[bits] = 0; - n = 0; - while (n <= 143) static_ltree[n++].Len = 8, bl_count[8]++; - while (n <= 255) static_ltree[n++].Len = 9, bl_count[9]++; - while (n <= 279) static_ltree[n++].Len = 7, bl_count[7]++; - while (n <= 287) static_ltree[n++].Len = 8, bl_count[8]++; - /* Codes 286 and 287 do not exist, but we must include them in the - * tree construction to get a canonical Huffman tree (longest code - * all ones) - */ - gen_codes((ct_data *)static_ltree, L_CODES+1, bl_count); - - /* The static distance tree is trivial: */ - for (n = 0; n < D_CODES; n++) { - static_dtree[n].Len = 5; - static_dtree[n].Code = bi_reverse((unsigned)n, 5); - } - static_init_done = 1; - -# ifdef GEN_TREES_H - gen_trees_header(); -# endif -#endif /* defined(GEN_TREES_H) || !defined(STDC) */ -} - -/* =========================================================================== - * Generate the file trees.h describing the static trees. - */ -#ifdef GEN_TREES_H -# ifndef ZLIB_DEBUG -# include -# endif - -# define SEPARATOR(i, last, width) \ - ((i) == (last)? "\n};\n\n" : \ - ((i) % (width) == (width) - 1 ? ",\n" : ", ")) - -void gen_trees_header(void) { - FILE *header = fopen("trees.h", "w"); - int i; - - Assert (header != NULL, "Can't open trees.h"); - fprintf(header, - "/* header created automatically with -DGEN_TREES_H */\n\n"); - - fprintf(header, "local const ct_data static_ltree[L_CODES+2] = {\n"); - for (i = 0; i < L_CODES+2; i++) { - fprintf(header, "{{%3u},{%3u}}%s", static_ltree[i].Code, - static_ltree[i].Len, SEPARATOR(i, L_CODES+1, 5)); - } - - fprintf(header, "local const ct_data static_dtree[D_CODES] = {\n"); - for (i = 0; i < D_CODES; i++) { - fprintf(header, "{{%2u},{%2u}}%s", static_dtree[i].Code, - static_dtree[i].Len, SEPARATOR(i, D_CODES-1, 5)); - } - - fprintf(header, "const uch ZLIB_INTERNAL _dist_code[DIST_CODE_LEN] = {\n"); - for (i = 0; i < DIST_CODE_LEN; i++) { - fprintf(header, "%2u%s", _dist_code[i], - SEPARATOR(i, DIST_CODE_LEN-1, 20)); - } - - fprintf(header, - "const uch ZLIB_INTERNAL _length_code[MAX_MATCH-MIN_MATCH+1]= {\n"); - for (i = 0; i < MAX_MATCH-MIN_MATCH+1; i++) { - fprintf(header, "%2u%s", _length_code[i], - SEPARATOR(i, MAX_MATCH-MIN_MATCH, 20)); - } - - fprintf(header, "local const int base_length[LENGTH_CODES] = {\n"); - for (i = 0; i < LENGTH_CODES; i++) { - fprintf(header, "%1u%s", base_length[i], - SEPARATOR(i, LENGTH_CODES-1, 20)); - } - - fprintf(header, "local const int base_dist[D_CODES] = {\n"); - for (i = 0; i < D_CODES; i++) { - fprintf(header, "%5u%s", base_dist[i], - SEPARATOR(i, D_CODES-1, 10)); - } - - fclose(header); -} -#endif /* GEN_TREES_H */ - -/* =========================================================================== - * Initialize a new block. - */ -local void init_block(deflate_state *s) { - int n; /* iterates over tree elements */ - - /* Initialize the trees. */ - for (n = 0; n < L_CODES; n++) s->dyn_ltree[n].Freq = 0; - for (n = 0; n < D_CODES; n++) s->dyn_dtree[n].Freq = 0; - for (n = 0; n < BL_CODES; n++) s->bl_tree[n].Freq = 0; - - s->dyn_ltree[END_BLOCK].Freq = 1; - s->opt_len = s->static_len = 0L; - s->sym_next = s->matches = 0; -} - -/* =========================================================================== - * Initialize the tree data structures for a new zlib stream. - */ -void ZLIB_INTERNAL _tr_init(deflate_state *s) { - tr_static_init(); - - s->l_desc.dyn_tree = s->dyn_ltree; - s->l_desc.stat_desc = &static_l_desc; - - s->d_desc.dyn_tree = s->dyn_dtree; - s->d_desc.stat_desc = &static_d_desc; - - s->bl_desc.dyn_tree = s->bl_tree; - s->bl_desc.stat_desc = &static_bl_desc; - - s->bi_buf = 0; - s->bi_valid = 0; -#ifdef ZLIB_DEBUG - s->compressed_len = 0L; - s->bits_sent = 0L; -#endif - - /* Initialize the first block of the first file: */ - init_block(s); -} - -#define SMALLEST 1 -/* Index within the heap array of least frequent node in the Huffman tree */ - - -/* =========================================================================== - * Remove the smallest element from the heap and recreate the heap with - * one less element. Updates heap and heap_len. - */ -#define pqremove(s, tree, top) \ -{\ - top = s->heap[SMALLEST]; \ - s->heap[SMALLEST] = s->heap[s->heap_len--]; \ - pqdownheap(s, tree, SMALLEST); \ -} - -/* =========================================================================== - * Compares to subtrees, using the tree depth as tie breaker when - * the subtrees have equal frequency. This minimizes the worst case length. - */ -#define smaller(tree, n, m, depth) \ - (tree[n].Freq < tree[m].Freq || \ - (tree[n].Freq == tree[m].Freq && depth[n] <= depth[m])) - -/* =========================================================================== - * Restore the heap property by moving down the tree starting at node k, - * exchanging a node with the smallest of its two sons if necessary, stopping - * when the heap property is re-established (each father smaller than its - * two sons). - */ -local void pqdownheap(deflate_state *s, ct_data *tree, int k) { - int v = s->heap[k]; - int j = k << 1; /* left son of k */ - while (j <= s->heap_len) { - /* Set j to the smallest of the two sons: */ - if (j < s->heap_len && - smaller(tree, s->heap[j + 1], s->heap[j], s->depth)) { - j++; - } - /* Exit if v is smaller than both sons */ - if (smaller(tree, v, s->heap[j], s->depth)) break; - - /* Exchange v with the smallest son */ - s->heap[k] = s->heap[j]; k = j; - - /* And continue down the tree, setting j to the left son of k */ - j <<= 1; - } - s->heap[k] = v; -} - -/* =========================================================================== - * Compute the optimal bit lengths for a tree and update the total bit length - * for the current block. - * IN assertion: the fields freq and dad are set, heap[heap_max] and - * above are the tree nodes sorted by increasing frequency. - * OUT assertions: the field len is set to the optimal bit length, the - * array bl_count contains the frequencies for each bit length. - * The length opt_len is updated; static_len is also updated if stree is - * not null. - */ -local void gen_bitlen(deflate_state *s, tree_desc *desc) { - ct_data *tree = desc->dyn_tree; - int max_code = desc->max_code; - const ct_data *stree = desc->stat_desc->static_tree; - const intf *extra = desc->stat_desc->extra_bits; - int base = desc->stat_desc->extra_base; - int max_length = desc->stat_desc->max_length; - int h; /* heap index */ - int n, m; /* iterate over the tree elements */ - int bits; /* bit length */ - int xbits; /* extra bits */ - ush f; /* frequency */ - int overflow = 0; /* number of elements with bit length too large */ - - for (bits = 0; bits <= MAX_BITS; bits++) s->bl_count[bits] = 0; - - /* In a first pass, compute the optimal bit lengths (which may - * overflow in the case of the bit length tree). - */ - tree[s->heap[s->heap_max]].Len = 0; /* root of the heap */ - - for (h = s->heap_max + 1; h < HEAP_SIZE; h++) { - n = s->heap[h]; - bits = tree[tree[n].Dad].Len + 1; - if (bits > max_length) bits = max_length, overflow++; - tree[n].Len = (ush)bits; - /* We overwrite tree[n].Dad which is no longer needed */ - - if (n > max_code) continue; /* not a leaf node */ - - s->bl_count[bits]++; - xbits = 0; - if (n >= base) xbits = extra[n - base]; - f = tree[n].Freq; - s->opt_len += (ulg)f * (unsigned)(bits + xbits); - if (stree) s->static_len += (ulg)f * (unsigned)(stree[n].Len + xbits); - } - if (overflow == 0) return; - - Tracev((stderr,"\nbit length overflow\n")); - /* This happens for example on obj2 and pic of the Calgary corpus */ - - /* Find the first bit length which could increase: */ - do { - bits = max_length - 1; - while (s->bl_count[bits] == 0) bits--; - s->bl_count[bits]--; /* move one leaf down the tree */ - s->bl_count[bits + 1] += 2; /* move one overflow item as its brother */ - s->bl_count[max_length]--; - /* The brother of the overflow item also moves one step up, - * but this does not affect bl_count[max_length] - */ - overflow -= 2; - } while (overflow > 0); - - /* Now recompute all bit lengths, scanning in increasing frequency. - * h is still equal to HEAP_SIZE. (It is simpler to reconstruct all - * lengths instead of fixing only the wrong ones. This idea is taken - * from 'ar' written by Haruhiko Okumura.) - */ - for (bits = max_length; bits != 0; bits--) { - n = s->bl_count[bits]; - while (n != 0) { - m = s->heap[--h]; - if (m > max_code) continue; - if ((unsigned) tree[m].Len != (unsigned) bits) { - Tracev((stderr,"code %d bits %d->%d\n", m, tree[m].Len, bits)); - s->opt_len += ((ulg)bits - tree[m].Len) * tree[m].Freq; - tree[m].Len = (ush)bits; - } - n--; - } - } -} - -#ifdef DUMP_BL_TREE -# include -#endif - -/* =========================================================================== - * Construct one Huffman tree and assigns the code bit strings and lengths. - * Update the total bit length for the current block. - * IN assertion: the field freq is set for all tree elements. - * OUT assertions: the fields len and code are set to the optimal bit length - * and corresponding code. The length opt_len is updated; static_len is - * also updated if stree is not null. The field max_code is set. - */ -local void build_tree(deflate_state *s, tree_desc *desc) { - ct_data *tree = desc->dyn_tree; - const ct_data *stree = desc->stat_desc->static_tree; - int elems = desc->stat_desc->elems; - int n, m; /* iterate over heap elements */ - int max_code = -1; /* largest code with non zero frequency */ - int node; /* new node being created */ - - /* Construct the initial heap, with least frequent element in - * heap[SMALLEST]. The sons of heap[n] are heap[2*n] and heap[2*n + 1]. - * heap[0] is not used. - */ - s->heap_len = 0, s->heap_max = HEAP_SIZE; - - for (n = 0; n < elems; n++) { - if (tree[n].Freq != 0) { - s->heap[++(s->heap_len)] = max_code = n; - s->depth[n] = 0; - } else { - tree[n].Len = 0; - } - } - - /* The pkzip format requires that at least one distance code exists, - * and that at least one bit should be sent even if there is only one - * possible code. So to avoid special checks later on we force at least - * two codes of non zero frequency. - */ - while (s->heap_len < 2) { - node = s->heap[++(s->heap_len)] = (max_code < 2 ? ++max_code : 0); - tree[node].Freq = 1; - s->depth[node] = 0; - s->opt_len--; if (stree) s->static_len -= stree[node].Len; - /* node is 0 or 1 so it does not have extra bits */ - } - desc->max_code = max_code; - - /* The elements heap[heap_len/2 + 1 .. heap_len] are leaves of the tree, - * establish sub-heaps of increasing lengths: - */ - for (n = s->heap_len/2; n >= 1; n--) pqdownheap(s, tree, n); - - /* Construct the Huffman tree by repeatedly combining the least two - * frequent nodes. - */ - node = elems; /* next internal node of the tree */ - do { - pqremove(s, tree, n); /* n = node of least frequency */ - m = s->heap[SMALLEST]; /* m = node of next least frequency */ - - s->heap[--(s->heap_max)] = n; /* keep the nodes sorted by frequency */ - s->heap[--(s->heap_max)] = m; - - /* Create a new node father of n and m */ - tree[node].Freq = tree[n].Freq + tree[m].Freq; - s->depth[node] = (uch)((s->depth[n] >= s->depth[m] ? - s->depth[n] : s->depth[m]) + 1); - tree[n].Dad = tree[m].Dad = (ush)node; -#ifdef DUMP_BL_TREE - if (tree == s->bl_tree) { - fprintf(stderr,"\nnode %d(%d), sons %d(%d) %d(%d)", - node, tree[node].Freq, n, tree[n].Freq, m, tree[m].Freq); - } -#endif - /* and insert the new node in the heap */ - s->heap[SMALLEST] = node++; - pqdownheap(s, tree, SMALLEST); - - } while (s->heap_len >= 2); - - s->heap[--(s->heap_max)] = s->heap[SMALLEST]; - - /* At this point, the fields freq and dad are set. We can now - * generate the bit lengths. - */ - gen_bitlen(s, (tree_desc *)desc); - - /* The field len is now set, we can generate the bit codes */ - gen_codes ((ct_data *)tree, max_code, s->bl_count); -} - -/* =========================================================================== - * Scan a literal or distance tree to determine the frequencies of the codes - * in the bit length tree. - */ -local void scan_tree(deflate_state *s, ct_data *tree, int max_code) { - int n; /* iterates over all tree elements */ - int prevlen = -1; /* last emitted length */ - int curlen; /* length of current code */ - int nextlen = tree[0].Len; /* length of next code */ - int count = 0; /* repeat count of the current code */ - int max_count = 7; /* max repeat count */ - int min_count = 4; /* min repeat count */ - - if (nextlen == 0) max_count = 138, min_count = 3; - tree[max_code + 1].Len = (ush)0xffff; /* guard */ - - for (n = 0; n <= max_code; n++) { - curlen = nextlen; nextlen = tree[n + 1].Len; - if (++count < max_count && curlen == nextlen) { - continue; - } else if (count < min_count) { - s->bl_tree[curlen].Freq += count; - } else if (curlen != 0) { - if (curlen != prevlen) s->bl_tree[curlen].Freq++; - s->bl_tree[REP_3_6].Freq++; - } else if (count <= 10) { - s->bl_tree[REPZ_3_10].Freq++; - } else { - s->bl_tree[REPZ_11_138].Freq++; - } - count = 0; prevlen = curlen; - if (nextlen == 0) { - max_count = 138, min_count = 3; - } else if (curlen == nextlen) { - max_count = 6, min_count = 3; - } else { - max_count = 7, min_count = 4; - } - } -} - -/* =========================================================================== - * Send a literal or distance tree in compressed form, using the codes in - * bl_tree. - */ -local void send_tree(deflate_state *s, ct_data *tree, int max_code) { - int n; /* iterates over all tree elements */ - int prevlen = -1; /* last emitted length */ - int curlen; /* length of current code */ - int nextlen = tree[0].Len; /* length of next code */ - int count = 0; /* repeat count of the current code */ - int max_count = 7; /* max repeat count */ - int min_count = 4; /* min repeat count */ - - /* tree[max_code + 1].Len = -1; */ /* guard already set */ - if (nextlen == 0) max_count = 138, min_count = 3; - - for (n = 0; n <= max_code; n++) { - curlen = nextlen; nextlen = tree[n + 1].Len; - if (++count < max_count && curlen == nextlen) { - continue; - } else if (count < min_count) { - do { send_code(s, curlen, s->bl_tree); } while (--count != 0); - - } else if (curlen != 0) { - if (curlen != prevlen) { - send_code(s, curlen, s->bl_tree); count--; - } - Assert(count >= 3 && count <= 6, " 3_6?"); - send_code(s, REP_3_6, s->bl_tree); send_bits(s, count - 3, 2); - - } else if (count <= 10) { - send_code(s, REPZ_3_10, s->bl_tree); send_bits(s, count - 3, 3); - - } else { - send_code(s, REPZ_11_138, s->bl_tree); send_bits(s, count - 11, 7); - } - count = 0; prevlen = curlen; - if (nextlen == 0) { - max_count = 138, min_count = 3; - } else if (curlen == nextlen) { - max_count = 6, min_count = 3; - } else { - max_count = 7, min_count = 4; - } - } -} - -/* =========================================================================== - * Construct the Huffman tree for the bit lengths and return the index in - * bl_order of the last bit length code to send. - */ -local int build_bl_tree(deflate_state *s) { - int max_blindex; /* index of last bit length code of non zero freq */ - - /* Determine the bit length frequencies for literal and distance trees */ - scan_tree(s, (ct_data *)s->dyn_ltree, s->l_desc.max_code); - scan_tree(s, (ct_data *)s->dyn_dtree, s->d_desc.max_code); - - /* Build the bit length tree: */ - build_tree(s, (tree_desc *)(&(s->bl_desc))); - /* opt_len now includes the length of the tree representations, except the - * lengths of the bit lengths codes and the 5 + 5 + 4 bits for the counts. - */ - - /* Determine the number of bit length codes to send. The pkzip format - * requires that at least 4 bit length codes be sent. (appnote.txt says - * 3 but the actual value used is 4.) - */ - for (max_blindex = BL_CODES-1; max_blindex >= 3; max_blindex--) { - if (s->bl_tree[bl_order[max_blindex]].Len != 0) break; - } - /* Update opt_len to include the bit length tree and counts */ - s->opt_len += 3*((ulg)max_blindex + 1) + 5 + 5 + 4; - Tracev((stderr, "\ndyn trees: dyn %ld, stat %ld", - s->opt_len, s->static_len)); - - return max_blindex; -} - -/* =========================================================================== - * Send the header for a block using dynamic Huffman trees: the counts, the - * lengths of the bit length codes, the literal tree and the distance tree. - * IN assertion: lcodes >= 257, dcodes >= 1, blcodes >= 4. - */ -local void send_all_trees(deflate_state *s, int lcodes, int dcodes, - int blcodes) { - int rank; /* index in bl_order */ - - Assert (lcodes >= 257 && dcodes >= 1 && blcodes >= 4, "not enough codes"); - Assert (lcodes <= L_CODES && dcodes <= D_CODES && blcodes <= BL_CODES, - "too many codes"); - Tracev((stderr, "\nbl counts: ")); - send_bits(s, lcodes - 257, 5); /* not +255 as stated in appnote.txt */ - send_bits(s, dcodes - 1, 5); - send_bits(s, blcodes - 4, 4); /* not -3 as stated in appnote.txt */ - for (rank = 0; rank < blcodes; rank++) { - Tracev((stderr, "\nbl code %2d ", bl_order[rank])); - send_bits(s, s->bl_tree[bl_order[rank]].Len, 3); - } - Tracev((stderr, "\nbl tree: sent %ld", s->bits_sent)); - - send_tree(s, (ct_data *)s->dyn_ltree, lcodes - 1); /* literal tree */ - Tracev((stderr, "\nlit tree: sent %ld", s->bits_sent)); - - send_tree(s, (ct_data *)s->dyn_dtree, dcodes - 1); /* distance tree */ - Tracev((stderr, "\ndist tree: sent %ld", s->bits_sent)); -} - -/* =========================================================================== - * Send a stored block - */ -void ZLIB_INTERNAL _tr_stored_block(deflate_state *s, charf *buf, - ulg stored_len, int last) { - send_bits(s, (STORED_BLOCK<<1) + last, 3); /* send block type */ - bi_windup(s); /* align on byte boundary */ - put_short(s, (ush)stored_len); - put_short(s, (ush)~stored_len); - if (stored_len) - zmemcpy(s->pending_buf + s->pending, (Bytef *)buf, stored_len); - s->pending += stored_len; -#ifdef ZLIB_DEBUG - s->compressed_len = (s->compressed_len + 3 + 7) & (ulg)~7L; - s->compressed_len += (stored_len + 4) << 3; - s->bits_sent += 2*16; - s->bits_sent += stored_len << 3; -#endif -} - -/* =========================================================================== - * Flush the bits in the bit buffer to pending output (leaves at most 7 bits) - */ -void ZLIB_INTERNAL _tr_flush_bits(deflate_state *s) { - bi_flush(s); -} - -/* =========================================================================== - * Send one empty static block to give enough lookahead for inflate. - * This takes 10 bits, of which 7 may remain in the bit buffer. - */ -void ZLIB_INTERNAL _tr_align(deflate_state *s) { - send_bits(s, STATIC_TREES<<1, 3); - send_code(s, END_BLOCK, static_ltree); -#ifdef ZLIB_DEBUG - s->compressed_len += 10L; /* 3 for block type, 7 for EOB */ -#endif - bi_flush(s); -} - -/* =========================================================================== - * Send the block data compressed using the given Huffman trees - */ -local void compress_block(deflate_state *s, const ct_data *ltree, - const ct_data *dtree) { - unsigned dist; /* distance of matched string */ - int lc; /* match length or unmatched char (if dist == 0) */ - unsigned sx = 0; /* running index in symbol buffers */ - unsigned code; /* the code to send */ - int extra; /* number of extra bits to send */ - - if (s->sym_next != 0) do { -#ifdef LIT_MEM - dist = s->d_buf[sx]; - lc = s->l_buf[sx++]; -#else - dist = s->sym_buf[sx++] & 0xff; - dist += (unsigned)(s->sym_buf[sx++] & 0xff) << 8; - lc = s->sym_buf[sx++]; -#endif - if (dist == 0) { - send_code(s, lc, ltree); /* send a literal byte */ - Tracecv(isgraph(lc), (stderr," '%c' ", lc)); - } else { - /* Here, lc is the match length - MIN_MATCH */ - code = _length_code[lc]; - send_code(s, code + LITERALS + 1, ltree); /* send length code */ - extra = extra_lbits[code]; - if (extra != 0) { - lc -= base_length[code]; - send_bits(s, lc, extra); /* send the extra length bits */ - } - dist--; /* dist is now the match distance - 1 */ - code = d_code(dist); - Assert (code < D_CODES, "bad d_code"); - - send_code(s, code, dtree); /* send the distance code */ - extra = extra_dbits[code]; - if (extra != 0) { - dist -= (unsigned)base_dist[code]; - send_bits(s, dist, extra); /* send the extra distance bits */ - } - } /* literal or match pair ? */ - - /* Check for no overlay of pending_buf on needed symbols */ -#ifdef LIT_MEM - Assert(s->pending < 2 * (s->lit_bufsize + sx), "pendingBuf overflow"); -#else - Assert(s->pending < s->lit_bufsize + sx, "pendingBuf overflow"); -#endif - - } while (sx < s->sym_next); - - send_code(s, END_BLOCK, ltree); -} - -/* =========================================================================== - * Check if the data type is TEXT or BINARY, using the following algorithm: - * - TEXT if the two conditions below are satisfied: - * a) There are no non-portable control characters belonging to the - * "block list" (0..6, 14..25, 28..31). - * b) There is at least one printable character belonging to the - * "allow list" (9 {TAB}, 10 {LF}, 13 {CR}, 32..255). - * - BINARY otherwise. - * - The following partially-portable control characters form a - * "gray list" that is ignored in this detection algorithm: - * (7 {BEL}, 8 {BS}, 11 {VT}, 12 {FF}, 26 {SUB}, 27 {ESC}). - * IN assertion: the fields Freq of dyn_ltree are set. - */ -local int detect_data_type(deflate_state *s) { - /* block_mask is the bit mask of block-listed bytes - * set bits 0..6, 14..25, and 28..31 - * 0xf3ffc07f = binary 11110011111111111100000001111111 - */ - unsigned long block_mask = 0xf3ffc07fUL; - int n; - - /* Check for non-textual ("block-listed") bytes. */ - for (n = 0; n <= 31; n++, block_mask >>= 1) - if ((block_mask & 1) && (s->dyn_ltree[n].Freq != 0)) - return Z_BINARY; - - /* Check for textual ("allow-listed") bytes. */ - if (s->dyn_ltree[9].Freq != 0 || s->dyn_ltree[10].Freq != 0 - || s->dyn_ltree[13].Freq != 0) - return Z_TEXT; - for (n = 32; n < LITERALS; n++) - if (s->dyn_ltree[n].Freq != 0) - return Z_TEXT; - - /* There are no "block-listed" or "allow-listed" bytes: - * this stream either is empty or has tolerated ("gray-listed") bytes only. - */ - return Z_BINARY; -} - -/* =========================================================================== - * Determine the best encoding for the current block: dynamic trees, static - * trees or store, and write out the encoded block. - */ -void ZLIB_INTERNAL _tr_flush_block(deflate_state *s, charf *buf, - ulg stored_len, int last) { - ulg opt_lenb, static_lenb; /* opt_len and static_len in bytes */ - int max_blindex = 0; /* index of last bit length code of non zero freq */ - - /* Build the Huffman trees unless a stored block is forced */ - if (s->level > 0) { - - /* Check if the file is binary or text */ - if (s->strm->data_type == Z_UNKNOWN) - s->strm->data_type = detect_data_type(s); - - /* Construct the literal and distance trees */ - build_tree(s, (tree_desc *)(&(s->l_desc))); - Tracev((stderr, "\nlit data: dyn %ld, stat %ld", s->opt_len, - s->static_len)); - - build_tree(s, (tree_desc *)(&(s->d_desc))); - Tracev((stderr, "\ndist data: dyn %ld, stat %ld", s->opt_len, - s->static_len)); - /* At this point, opt_len and static_len are the total bit lengths of - * the compressed block data, excluding the tree representations. - */ - - /* Build the bit length tree for the above two trees, and get the index - * in bl_order of the last bit length code to send. - */ - max_blindex = build_bl_tree(s); - - /* Determine the best encoding. Compute the block lengths in bytes. */ - opt_lenb = (s->opt_len + 3 + 7) >> 3; - static_lenb = (s->static_len + 3 + 7) >> 3; - - Tracev((stderr, "\nopt %lu(%lu) stat %lu(%lu) stored %lu lit %u ", - opt_lenb, s->opt_len, static_lenb, s->static_len, stored_len, - s->sym_next / 3)); - -#ifndef FORCE_STATIC - if (static_lenb <= opt_lenb || s->strategy == Z_FIXED) -#endif - opt_lenb = static_lenb; - - } else { - Assert(buf != (char*)0, "lost buf"); - opt_lenb = static_lenb = stored_len + 5; /* force a stored block */ - } - -#ifdef FORCE_STORED - if (buf != (char*)0) { /* force stored block */ -#else - if (stored_len + 4 <= opt_lenb && buf != (char*)0) { - /* 4: two words for the lengths */ -#endif - /* The test buf != NULL is only necessary if LIT_BUFSIZE > WSIZE. - * Otherwise we can't have processed more than WSIZE input bytes since - * the last block flush, because compression would have been - * successful. If LIT_BUFSIZE <= WSIZE, it is never too late to - * transform a block into a stored block. - */ - _tr_stored_block(s, buf, stored_len, last); - - } else if (static_lenb == opt_lenb) { - send_bits(s, (STATIC_TREES<<1) + last, 3); - compress_block(s, (const ct_data *)static_ltree, - (const ct_data *)static_dtree); -#ifdef ZLIB_DEBUG - s->compressed_len += 3 + s->static_len; -#endif - } else { - send_bits(s, (DYN_TREES<<1) + last, 3); - send_all_trees(s, s->l_desc.max_code + 1, s->d_desc.max_code + 1, - max_blindex + 1); - compress_block(s, (const ct_data *)s->dyn_ltree, - (const ct_data *)s->dyn_dtree); -#ifdef ZLIB_DEBUG - s->compressed_len += 3 + s->opt_len; -#endif - } - Assert (s->compressed_len == s->bits_sent, "bad compressed size"); - /* The above check is made mod 2^32, for files larger than 512 MB - * and uLong implemented on 32 bits. - */ - init_block(s); - - if (last) { - bi_windup(s); -#ifdef ZLIB_DEBUG - s->compressed_len += 7; /* align on byte boundary */ -#endif - } - Tracev((stderr,"\ncomprlen %lu(%lu) ", s->compressed_len >> 3, - s->compressed_len - 7*last)); -} - -/* =========================================================================== - * Save the match info and tally the frequency counts. Return true if - * the current block must be flushed. - */ -int ZLIB_INTERNAL _tr_tally(deflate_state *s, unsigned dist, unsigned lc) { -#ifdef LIT_MEM - s->d_buf[s->sym_next] = (ush)dist; - s->l_buf[s->sym_next++] = (uch)lc; -#else - s->sym_buf[s->sym_next++] = (uch)dist; - s->sym_buf[s->sym_next++] = (uch)(dist >> 8); - s->sym_buf[s->sym_next++] = (uch)lc; -#endif - if (dist == 0) { - /* lc is the unmatched char */ - s->dyn_ltree[lc].Freq++; - } else { - s->matches++; - /* Here, lc is the match length - MIN_MATCH */ - dist--; /* dist = match distance - 1 */ - Assert((ush)dist < (ush)MAX_DIST(s) && - (ush)lc <= (ush)(MAX_MATCH-MIN_MATCH) && - (ush)d_code(dist) < (ush)D_CODES, "_tr_tally: bad match"); - - s->dyn_ltree[_length_code[lc] + LITERALS + 1].Freq++; - s->dyn_dtree[d_code(dist)].Freq++; - } - return (s->sym_next == s->sym_end); -} diff --git a/phobos/etc/c/zlib/trees.h b/phobos/etc/c/zlib/trees.h deleted file mode 100644 index d35639d..0000000 --- a/phobos/etc/c/zlib/trees.h +++ /dev/null @@ -1,128 +0,0 @@ -/* header created automatically with -DGEN_TREES_H */ - -local const ct_data static_ltree[L_CODES+2] = { -{{ 12},{ 8}}, {{140},{ 8}}, {{ 76},{ 8}}, {{204},{ 8}}, {{ 44},{ 8}}, -{{172},{ 8}}, {{108},{ 8}}, {{236},{ 8}}, {{ 28},{ 8}}, {{156},{ 8}}, -{{ 92},{ 8}}, {{220},{ 8}}, {{ 60},{ 8}}, {{188},{ 8}}, {{124},{ 8}}, -{{252},{ 8}}, {{ 2},{ 8}}, {{130},{ 8}}, {{ 66},{ 8}}, {{194},{ 8}}, -{{ 34},{ 8}}, {{162},{ 8}}, {{ 98},{ 8}}, {{226},{ 8}}, {{ 18},{ 8}}, -{{146},{ 8}}, {{ 82},{ 8}}, {{210},{ 8}}, {{ 50},{ 8}}, {{178},{ 8}}, -{{114},{ 8}}, {{242},{ 8}}, {{ 10},{ 8}}, {{138},{ 8}}, {{ 74},{ 8}}, -{{202},{ 8}}, {{ 42},{ 8}}, {{170},{ 8}}, {{106},{ 8}}, {{234},{ 8}}, -{{ 26},{ 8}}, {{154},{ 8}}, {{ 90},{ 8}}, {{218},{ 8}}, {{ 58},{ 8}}, -{{186},{ 8}}, {{122},{ 8}}, {{250},{ 8}}, {{ 6},{ 8}}, {{134},{ 8}}, -{{ 70},{ 8}}, {{198},{ 8}}, {{ 38},{ 8}}, {{166},{ 8}}, {{102},{ 8}}, -{{230},{ 8}}, {{ 22},{ 8}}, {{150},{ 8}}, {{ 86},{ 8}}, {{214},{ 8}}, -{{ 54},{ 8}}, {{182},{ 8}}, {{118},{ 8}}, {{246},{ 8}}, {{ 14},{ 8}}, -{{142},{ 8}}, {{ 78},{ 8}}, {{206},{ 8}}, {{ 46},{ 8}}, {{174},{ 8}}, -{{110},{ 8}}, {{238},{ 8}}, {{ 30},{ 8}}, {{158},{ 8}}, {{ 94},{ 8}}, -{{222},{ 8}}, {{ 62},{ 8}}, {{190},{ 8}}, {{126},{ 8}}, {{254},{ 8}}, -{{ 1},{ 8}}, {{129},{ 8}}, {{ 65},{ 8}}, {{193},{ 8}}, {{ 33},{ 8}}, -{{161},{ 8}}, {{ 97},{ 8}}, {{225},{ 8}}, {{ 17},{ 8}}, {{145},{ 8}}, -{{ 81},{ 8}}, {{209},{ 8}}, {{ 49},{ 8}}, {{177},{ 8}}, {{113},{ 8}}, -{{241},{ 8}}, {{ 9},{ 8}}, {{137},{ 8}}, {{ 73},{ 8}}, {{201},{ 8}}, -{{ 41},{ 8}}, {{169},{ 8}}, {{105},{ 8}}, {{233},{ 8}}, {{ 25},{ 8}}, -{{153},{ 8}}, {{ 89},{ 8}}, {{217},{ 8}}, {{ 57},{ 8}}, {{185},{ 8}}, -{{121},{ 8}}, {{249},{ 8}}, {{ 5},{ 8}}, {{133},{ 8}}, {{ 69},{ 8}}, -{{197},{ 8}}, {{ 37},{ 8}}, {{165},{ 8}}, {{101},{ 8}}, {{229},{ 8}}, -{{ 21},{ 8}}, {{149},{ 8}}, {{ 85},{ 8}}, {{213},{ 8}}, {{ 53},{ 8}}, -{{181},{ 8}}, {{117},{ 8}}, {{245},{ 8}}, {{ 13},{ 8}}, {{141},{ 8}}, -{{ 77},{ 8}}, {{205},{ 8}}, {{ 45},{ 8}}, {{173},{ 8}}, {{109},{ 8}}, -{{237},{ 8}}, {{ 29},{ 8}}, {{157},{ 8}}, {{ 93},{ 8}}, {{221},{ 8}}, -{{ 61},{ 8}}, {{189},{ 8}}, {{125},{ 8}}, {{253},{ 8}}, {{ 19},{ 9}}, -{{275},{ 9}}, {{147},{ 9}}, {{403},{ 9}}, {{ 83},{ 9}}, {{339},{ 9}}, -{{211},{ 9}}, {{467},{ 9}}, {{ 51},{ 9}}, {{307},{ 9}}, {{179},{ 9}}, -{{435},{ 9}}, {{115},{ 9}}, {{371},{ 9}}, {{243},{ 9}}, {{499},{ 9}}, -{{ 11},{ 9}}, {{267},{ 9}}, {{139},{ 9}}, {{395},{ 9}}, {{ 75},{ 9}}, -{{331},{ 9}}, {{203},{ 9}}, {{459},{ 9}}, {{ 43},{ 9}}, {{299},{ 9}}, -{{171},{ 9}}, {{427},{ 9}}, {{107},{ 9}}, {{363},{ 9}}, {{235},{ 9}}, -{{491},{ 9}}, {{ 27},{ 9}}, {{283},{ 9}}, {{155},{ 9}}, {{411},{ 9}}, -{{ 91},{ 9}}, {{347},{ 9}}, {{219},{ 9}}, {{475},{ 9}}, {{ 59},{ 9}}, -{{315},{ 9}}, {{187},{ 9}}, {{443},{ 9}}, {{123},{ 9}}, {{379},{ 9}}, -{{251},{ 9}}, {{507},{ 9}}, {{ 7},{ 9}}, {{263},{ 9}}, {{135},{ 9}}, -{{391},{ 9}}, {{ 71},{ 9}}, {{327},{ 9}}, {{199},{ 9}}, {{455},{ 9}}, -{{ 39},{ 9}}, {{295},{ 9}}, {{167},{ 9}}, {{423},{ 9}}, {{103},{ 9}}, -{{359},{ 9}}, {{231},{ 9}}, {{487},{ 9}}, {{ 23},{ 9}}, {{279},{ 9}}, -{{151},{ 9}}, {{407},{ 9}}, {{ 87},{ 9}}, {{343},{ 9}}, {{215},{ 9}}, -{{471},{ 9}}, {{ 55},{ 9}}, {{311},{ 9}}, {{183},{ 9}}, {{439},{ 9}}, -{{119},{ 9}}, {{375},{ 9}}, {{247},{ 9}}, {{503},{ 9}}, {{ 15},{ 9}}, -{{271},{ 9}}, {{143},{ 9}}, {{399},{ 9}}, {{ 79},{ 9}}, {{335},{ 9}}, -{{207},{ 9}}, {{463},{ 9}}, {{ 47},{ 9}}, {{303},{ 9}}, {{175},{ 9}}, -{{431},{ 9}}, {{111},{ 9}}, {{367},{ 9}}, {{239},{ 9}}, {{495},{ 9}}, -{{ 31},{ 9}}, {{287},{ 9}}, {{159},{ 9}}, {{415},{ 9}}, {{ 95},{ 9}}, -{{351},{ 9}}, {{223},{ 9}}, {{479},{ 9}}, {{ 63},{ 9}}, {{319},{ 9}}, -{{191},{ 9}}, {{447},{ 9}}, {{127},{ 9}}, {{383},{ 9}}, {{255},{ 9}}, -{{511},{ 9}}, {{ 0},{ 7}}, {{ 64},{ 7}}, {{ 32},{ 7}}, {{ 96},{ 7}}, -{{ 16},{ 7}}, {{ 80},{ 7}}, {{ 48},{ 7}}, {{112},{ 7}}, {{ 8},{ 7}}, -{{ 72},{ 7}}, {{ 40},{ 7}}, {{104},{ 7}}, {{ 24},{ 7}}, {{ 88},{ 7}}, -{{ 56},{ 7}}, {{120},{ 7}}, {{ 4},{ 7}}, {{ 68},{ 7}}, {{ 36},{ 7}}, -{{100},{ 7}}, {{ 20},{ 7}}, {{ 84},{ 7}}, {{ 52},{ 7}}, {{116},{ 7}}, -{{ 3},{ 8}}, {{131},{ 8}}, {{ 67},{ 8}}, {{195},{ 8}}, {{ 35},{ 8}}, -{{163},{ 8}}, {{ 99},{ 8}}, {{227},{ 8}} -}; - -local const ct_data static_dtree[D_CODES] = { -{{ 0},{ 5}}, {{16},{ 5}}, {{ 8},{ 5}}, {{24},{ 5}}, {{ 4},{ 5}}, -{{20},{ 5}}, {{12},{ 5}}, {{28},{ 5}}, {{ 2},{ 5}}, {{18},{ 5}}, -{{10},{ 5}}, {{26},{ 5}}, {{ 6},{ 5}}, {{22},{ 5}}, {{14},{ 5}}, -{{30},{ 5}}, {{ 1},{ 5}}, {{17},{ 5}}, {{ 9},{ 5}}, {{25},{ 5}}, -{{ 5},{ 5}}, {{21},{ 5}}, {{13},{ 5}}, {{29},{ 5}}, {{ 3},{ 5}}, -{{19},{ 5}}, {{11},{ 5}}, {{27},{ 5}}, {{ 7},{ 5}}, {{23},{ 5}} -}; - -const uch ZLIB_INTERNAL _dist_code[DIST_CODE_LEN] = { - 0, 1, 2, 3, 4, 4, 5, 5, 6, 6, 6, 6, 7, 7, 7, 7, 8, 8, 8, 8, - 8, 8, 8, 8, 9, 9, 9, 9, 9, 9, 9, 9, 10, 10, 10, 10, 10, 10, 10, 10, -10, 10, 10, 10, 10, 10, 10, 10, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, -11, 11, 11, 11, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, -12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 13, 13, 13, 13, -13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, -13, 13, 13, 13, 13, 13, 13, 13, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, -14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, -14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, -14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 15, 15, 15, 15, 15, 15, 15, 15, -15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, -15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, -15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 0, 0, 16, 17, -18, 18, 19, 19, 20, 20, 20, 20, 21, 21, 21, 21, 22, 22, 22, 22, 22, 22, 22, 22, -23, 23, 23, 23, 23, 23, 23, 23, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, -24, 24, 24, 24, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, -26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, -26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 27, 27, 27, 27, 27, 27, 27, 27, -27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, -27, 27, 27, 27, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, -28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, -28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, -28, 28, 28, 28, 28, 28, 28, 28, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, -29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, -29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, -29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29 -}; - -const uch ZLIB_INTERNAL _length_code[MAX_MATCH-MIN_MATCH+1]= { - 0, 1, 2, 3, 4, 5, 6, 7, 8, 8, 9, 9, 10, 10, 11, 11, 12, 12, 12, 12, -13, 13, 13, 13, 14, 14, 14, 14, 15, 15, 15, 15, 16, 16, 16, 16, 16, 16, 16, 16, -17, 17, 17, 17, 17, 17, 17, 17, 18, 18, 18, 18, 18, 18, 18, 18, 19, 19, 19, 19, -19, 19, 19, 19, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, -21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 22, 22, 22, 22, -22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 23, 23, 23, 23, 23, 23, 23, 23, -23, 23, 23, 23, 23, 23, 23, 23, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, -24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, -25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, -25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 26, 26, 26, 26, 26, 26, 26, 26, -26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, -26, 26, 26, 26, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, -27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 28 -}; - -local const int base_length[LENGTH_CODES] = { -0, 1, 2, 3, 4, 5, 6, 7, 8, 10, 12, 14, 16, 20, 24, 28, 32, 40, 48, 56, -64, 80, 96, 112, 128, 160, 192, 224, 0 -}; - -local const int base_dist[D_CODES] = { - 0, 1, 2, 3, 4, 6, 8, 12, 16, 24, - 32, 48, 64, 96, 128, 192, 256, 384, 512, 768, - 1024, 1536, 2048, 3072, 4096, 6144, 8192, 12288, 16384, 24576 -}; - diff --git a/phobos/etc/c/zlib/uncompr.c b/phobos/etc/c/zlib/uncompr.c deleted file mode 100644 index 5e25666..0000000 --- a/phobos/etc/c/zlib/uncompr.c +++ /dev/null @@ -1,85 +0,0 @@ -/* uncompr.c -- decompress a memory buffer - * Copyright (C) 1995-2003, 2010, 2014, 2016 Jean-loup Gailly, Mark Adler - * For conditions of distribution and use, see copyright notice in zlib.h - */ - -/* @(#) $Id$ */ - -#define ZLIB_INTERNAL -#include "zlib.h" - -/* =========================================================================== - Decompresses the source buffer into the destination buffer. *sourceLen is - the byte length of the source buffer. Upon entry, *destLen is the total size - of the destination buffer, which must be large enough to hold the entire - uncompressed data. (The size of the uncompressed data must have been saved - previously by the compressor and transmitted to the decompressor by some - mechanism outside the scope of this compression library.) Upon exit, - *destLen is the size of the decompressed data and *sourceLen is the number - of source bytes consumed. Upon return, source + *sourceLen points to the - first unused input byte. - - uncompress returns Z_OK if success, Z_MEM_ERROR if there was not enough - memory, Z_BUF_ERROR if there was not enough room in the output buffer, or - Z_DATA_ERROR if the input data was corrupted, including if the input data is - an incomplete zlib stream. -*/ -int ZEXPORT uncompress2(Bytef *dest, uLongf *destLen, const Bytef *source, - uLong *sourceLen) { - z_stream stream; - int err; - const uInt max = (uInt)-1; - uLong len, left; - Byte buf[1]; /* for detection of incomplete stream when *destLen == 0 */ - - len = *sourceLen; - if (*destLen) { - left = *destLen; - *destLen = 0; - } - else { - left = 1; - dest = buf; - } - - stream.next_in = (z_const Bytef *)source; - stream.avail_in = 0; - stream.zalloc = (alloc_func)0; - stream.zfree = (free_func)0; - stream.opaque = (voidpf)0; - - err = inflateInit(&stream); - if (err != Z_OK) return err; - - stream.next_out = dest; - stream.avail_out = 0; - - do { - if (stream.avail_out == 0) { - stream.avail_out = left > (uLong)max ? max : (uInt)left; - left -= stream.avail_out; - } - if (stream.avail_in == 0) { - stream.avail_in = len > (uLong)max ? max : (uInt)len; - len -= stream.avail_in; - } - err = inflate(&stream, Z_NO_FLUSH); - } while (err == Z_OK); - - *sourceLen -= len + stream.avail_in; - if (dest != buf) - *destLen = stream.total_out; - else if (stream.total_out && err == Z_BUF_ERROR) - left = 1; - - inflateEnd(&stream); - return err == Z_STREAM_END ? Z_OK : - err == Z_NEED_DICT ? Z_DATA_ERROR : - err == Z_BUF_ERROR && left + stream.avail_out ? Z_DATA_ERROR : - err; -} - -int ZEXPORT uncompress(Bytef *dest, uLongf *destLen, const Bytef *source, - uLong sourceLen) { - return uncompress2(dest, destLen, source, &sourceLen); -} diff --git a/phobos/etc/c/zlib/zconf.h b/phobos/etc/c/zlib/zconf.h deleted file mode 100644 index 62adc8d..0000000 --- a/phobos/etc/c/zlib/zconf.h +++ /dev/null @@ -1,543 +0,0 @@ -/* zconf.h -- configuration of the zlib compression library - * Copyright (C) 1995-2024 Jean-loup Gailly, Mark Adler - * For conditions of distribution and use, see copyright notice in zlib.h - */ - -/* @(#) $Id$ */ - -#ifndef ZCONF_H -#define ZCONF_H - -/* - * If you *really* need a unique prefix for all types and library functions, - * compile with -DZ_PREFIX. The "standard" zlib should be compiled without it. - * Even better than compiling with -DZ_PREFIX would be to use configure to set - * this permanently in zconf.h using "./configure --zprefix". - */ -#ifdef Z_PREFIX /* may be set to #if 1 by ./configure */ -# define Z_PREFIX_SET - -/* all linked symbols and init macros */ -# define _dist_code z__dist_code -# define _length_code z__length_code -# define _tr_align z__tr_align -# define _tr_flush_bits z__tr_flush_bits -# define _tr_flush_block z__tr_flush_block -# define _tr_init z__tr_init -# define _tr_stored_block z__tr_stored_block -# define _tr_tally z__tr_tally -# define adler32 z_adler32 -# define adler32_combine z_adler32_combine -# define adler32_combine64 z_adler32_combine64 -# define adler32_z z_adler32_z -# ifndef Z_SOLO -# define compress z_compress -# define compress2 z_compress2 -# define compressBound z_compressBound -# endif -# define crc32 z_crc32 -# define crc32_combine z_crc32_combine -# define crc32_combine64 z_crc32_combine64 -# define crc32_combine_gen z_crc32_combine_gen -# define crc32_combine_gen64 z_crc32_combine_gen64 -# define crc32_combine_op z_crc32_combine_op -# define crc32_z z_crc32_z -# define deflate z_deflate -# define deflateBound z_deflateBound -# define deflateCopy z_deflateCopy -# define deflateEnd z_deflateEnd -# define deflateGetDictionary z_deflateGetDictionary -# define deflateInit z_deflateInit -# define deflateInit2 z_deflateInit2 -# define deflateInit2_ z_deflateInit2_ -# define deflateInit_ z_deflateInit_ -# define deflateParams z_deflateParams -# define deflatePending z_deflatePending -# define deflatePrime z_deflatePrime -# define deflateReset z_deflateReset -# define deflateResetKeep z_deflateResetKeep -# define deflateSetDictionary z_deflateSetDictionary -# define deflateSetHeader z_deflateSetHeader -# define deflateTune z_deflateTune -# define deflate_copyright z_deflate_copyright -# define get_crc_table z_get_crc_table -# ifndef Z_SOLO -# define gz_error z_gz_error -# define gz_intmax z_gz_intmax -# define gz_strwinerror z_gz_strwinerror -# define gzbuffer z_gzbuffer -# define gzclearerr z_gzclearerr -# define gzclose z_gzclose -# define gzclose_r z_gzclose_r -# define gzclose_w z_gzclose_w -# define gzdirect z_gzdirect -# define gzdopen z_gzdopen -# define gzeof z_gzeof -# define gzerror z_gzerror -# define gzflush z_gzflush -# define gzfread z_gzfread -# define gzfwrite z_gzfwrite -# define gzgetc z_gzgetc -# define gzgetc_ z_gzgetc_ -# define gzgets z_gzgets -# define gzoffset z_gzoffset -# define gzoffset64 z_gzoffset64 -# define gzopen z_gzopen -# define gzopen64 z_gzopen64 -# ifdef _WIN32 -# define gzopen_w z_gzopen_w -# endif -# define gzprintf z_gzprintf -# define gzputc z_gzputc -# define gzputs z_gzputs -# define gzread z_gzread -# define gzrewind z_gzrewind -# define gzseek z_gzseek -# define gzseek64 z_gzseek64 -# define gzsetparams z_gzsetparams -# define gztell z_gztell -# define gztell64 z_gztell64 -# define gzungetc z_gzungetc -# define gzvprintf z_gzvprintf -# define gzwrite z_gzwrite -# endif -# define inflate z_inflate -# define inflateBack z_inflateBack -# define inflateBackEnd z_inflateBackEnd -# define inflateBackInit z_inflateBackInit -# define inflateBackInit_ z_inflateBackInit_ -# define inflateCodesUsed z_inflateCodesUsed -# define inflateCopy z_inflateCopy -# define inflateEnd z_inflateEnd -# define inflateGetDictionary z_inflateGetDictionary -# define inflateGetHeader z_inflateGetHeader -# define inflateInit z_inflateInit -# define inflateInit2 z_inflateInit2 -# define inflateInit2_ z_inflateInit2_ -# define inflateInit_ z_inflateInit_ -# define inflateMark z_inflateMark -# define inflatePrime z_inflatePrime -# define inflateReset z_inflateReset -# define inflateReset2 z_inflateReset2 -# define inflateResetKeep z_inflateResetKeep -# define inflateSetDictionary z_inflateSetDictionary -# define inflateSync z_inflateSync -# define inflateSyncPoint z_inflateSyncPoint -# define inflateUndermine z_inflateUndermine -# define inflateValidate z_inflateValidate -# define inflate_copyright z_inflate_copyright -# define inflate_fast z_inflate_fast -# define inflate_table z_inflate_table -# ifndef Z_SOLO -# define uncompress z_uncompress -# define uncompress2 z_uncompress2 -# endif -# define zError z_zError -# ifndef Z_SOLO -# define zcalloc z_zcalloc -# define zcfree z_zcfree -# endif -# define zlibCompileFlags z_zlibCompileFlags -# define zlibVersion z_zlibVersion - -/* all zlib typedefs in zlib.h and zconf.h */ -# define Byte z_Byte -# define Bytef z_Bytef -# define alloc_func z_alloc_func -# define charf z_charf -# define free_func z_free_func -# ifndef Z_SOLO -# define gzFile z_gzFile -# endif -# define gz_header z_gz_header -# define gz_headerp z_gz_headerp -# define in_func z_in_func -# define intf z_intf -# define out_func z_out_func -# define uInt z_uInt -# define uIntf z_uIntf -# define uLong z_uLong -# define uLongf z_uLongf -# define voidp z_voidp -# define voidpc z_voidpc -# define voidpf z_voidpf - -/* all zlib structs in zlib.h and zconf.h */ -# define gz_header_s z_gz_header_s -# define internal_state z_internal_state - -#endif - -#if defined(__MSDOS__) && !defined(MSDOS) -# define MSDOS -#endif -#if (defined(OS_2) || defined(__OS2__)) && !defined(OS2) -# define OS2 -#endif -#if defined(_WINDOWS) && !defined(WINDOWS) -# define WINDOWS -#endif -#if defined(_WIN32) || defined(_WIN32_WCE) || defined(__WIN32__) -# ifndef WIN32 -# define WIN32 -# endif -#endif -#if (defined(MSDOS) || defined(OS2) || defined(WINDOWS)) && !defined(WIN32) -# if !defined(__GNUC__) && !defined(__FLAT__) && !defined(__386__) -# ifndef SYS16BIT -# define SYS16BIT -# endif -# endif -#endif - -/* - * Compile with -DMAXSEG_64K if the alloc function cannot allocate more - * than 64k bytes at a time (needed on systems with 16-bit int). - */ -#ifdef SYS16BIT -# define MAXSEG_64K -#endif -#ifdef MSDOS -# define UNALIGNED_OK -#endif - -#ifdef __STDC_VERSION__ -# ifndef STDC -# define STDC -# endif -# if __STDC_VERSION__ >= 199901L -# ifndef STDC99 -# define STDC99 -# endif -# endif -#endif -#if !defined(STDC) && (defined(__STDC__) || defined(__cplusplus)) -# define STDC -#endif -#if !defined(STDC) && (defined(__GNUC__) || defined(__BORLANDC__)) -# define STDC -#endif -#if !defined(STDC) && (defined(MSDOS) || defined(WINDOWS) || defined(WIN32)) -# define STDC -#endif -#if !defined(STDC) && (defined(OS2) || defined(__HOS_AIX__)) -# define STDC -#endif - -#if defined(__OS400__) && !defined(STDC) /* iSeries (formerly AS/400). */ -# define STDC -#endif - -#ifndef STDC -# ifndef const /* cannot use !defined(STDC) && !defined(const) on Mac */ -# define const /* note: need a more gentle solution here */ -# endif -#endif - -#if defined(ZLIB_CONST) && !defined(z_const) -# define z_const const -#else -# define z_const -#endif - -#ifdef Z_SOLO -# ifdef _WIN64 - typedef unsigned long long z_size_t; -# else - typedef unsigned long z_size_t; -# endif -#else -# define z_longlong long long -# if defined(NO_SIZE_T) - typedef unsigned NO_SIZE_T z_size_t; -# elif defined(STDC) -# include - typedef size_t z_size_t; -# else - typedef unsigned long z_size_t; -# endif -# undef z_longlong -#endif - -/* Maximum value for memLevel in deflateInit2 */ -#ifndef MAX_MEM_LEVEL -# ifdef MAXSEG_64K -# define MAX_MEM_LEVEL 8 -# else -# define MAX_MEM_LEVEL 9 -# endif -#endif - -/* Maximum value for windowBits in deflateInit2 and inflateInit2. - * WARNING: reducing MAX_WBITS makes minigzip unable to extract .gz files - * created by gzip. (Files created by minigzip can still be extracted by - * gzip.) - */ -#ifndef MAX_WBITS -# define MAX_WBITS 15 /* 32K LZ77 window */ -#endif - -/* The memory requirements for deflate are (in bytes): - (1 << (windowBits+2)) + (1 << (memLevel+9)) - that is: 128K for windowBits=15 + 128K for memLevel = 8 (default values) - plus a few kilobytes for small objects. For example, if you want to reduce - the default memory requirements from 256K to 128K, compile with - make CFLAGS="-O -DMAX_WBITS=14 -DMAX_MEM_LEVEL=7" - Of course this will generally degrade compression (there's no free lunch). - - The memory requirements for inflate are (in bytes) 1 << windowBits - that is, 32K for windowBits=15 (default value) plus about 7 kilobytes - for small objects. -*/ - - /* Type declarations */ - -#ifndef OF /* function prototypes */ -# ifdef STDC -# define OF(args) args -# else -# define OF(args) () -# endif -#endif - -/* The following definitions for FAR are needed only for MSDOS mixed - * model programming (small or medium model with some far allocations). - * This was tested only with MSC; for other MSDOS compilers you may have - * to define NO_MEMCPY in zutil.h. If you don't need the mixed model, - * just define FAR to be empty. - */ -#ifdef SYS16BIT -# if defined(M_I86SM) || defined(M_I86MM) - /* MSC small or medium model */ -# define SMALL_MEDIUM -# ifdef _MSC_VER -# define FAR _far -# else -# define FAR far -# endif -# endif -# if (defined(__SMALL__) || defined(__MEDIUM__)) - /* Turbo C small or medium model */ -# define SMALL_MEDIUM -# ifdef __BORLANDC__ -# define FAR _far -# else -# define FAR far -# endif -# endif -#endif - -#if defined(WINDOWS) || defined(WIN32) - /* If building or using zlib as a DLL, define ZLIB_DLL. - * This is not mandatory, but it offers a little performance increase. - */ -# ifdef ZLIB_DLL -# if defined(WIN32) && (!defined(__BORLANDC__) || (__BORLANDC__ >= 0x500)) -# ifdef ZLIB_INTERNAL -# define ZEXTERN extern __declspec(dllexport) -# else -# define ZEXTERN extern __declspec(dllimport) -# endif -# endif -# endif /* ZLIB_DLL */ - /* If building or using zlib with the WINAPI/WINAPIV calling convention, - * define ZLIB_WINAPI. - * Caution: the standard ZLIB1.DLL is NOT compiled using ZLIB_WINAPI. - */ -# ifdef ZLIB_WINAPI -# ifdef FAR -# undef FAR -# endif -# ifndef WIN32_LEAN_AND_MEAN -# define WIN32_LEAN_AND_MEAN -# endif -# include - /* No need for _export, use ZLIB.DEF instead. */ - /* For complete Windows compatibility, use WINAPI, not __stdcall. */ -# define ZEXPORT WINAPI -# ifdef WIN32 -# define ZEXPORTVA WINAPIV -# else -# define ZEXPORTVA FAR CDECL -# endif -# endif -#endif - -#if defined (__BEOS__) -# ifdef ZLIB_DLL -# ifdef ZLIB_INTERNAL -# define ZEXPORT __declspec(dllexport) -# define ZEXPORTVA __declspec(dllexport) -# else -# define ZEXPORT __declspec(dllimport) -# define ZEXPORTVA __declspec(dllimport) -# endif -# endif -#endif - -#ifndef ZEXTERN -# define ZEXTERN extern -#endif -#ifndef ZEXPORT -# define ZEXPORT -#endif -#ifndef ZEXPORTVA -# define ZEXPORTVA -#endif - -#ifndef FAR -# define FAR -#endif - -#if !defined(__MACTYPES__) -typedef unsigned char Byte; /* 8 bits */ -#endif -typedef unsigned int uInt; /* 16 bits or more */ -typedef unsigned long uLong; /* 32 bits or more */ - -#ifdef SMALL_MEDIUM - /* Borland C/C++ and some old MSC versions ignore FAR inside typedef */ -# define Bytef Byte FAR -#else - typedef Byte FAR Bytef; -#endif -typedef char FAR charf; -typedef int FAR intf; -typedef uInt FAR uIntf; -typedef uLong FAR uLongf; - -#ifdef STDC - typedef void const *voidpc; - typedef void FAR *voidpf; - typedef void *voidp; -#else - typedef Byte const *voidpc; - typedef Byte FAR *voidpf; - typedef Byte *voidp; -#endif - -#if !defined(Z_U4) && !defined(Z_SOLO) && defined(STDC) -# include -# if (UINT_MAX == 0xffffffffUL) -# define Z_U4 unsigned -# elif (ULONG_MAX == 0xffffffffUL) -# define Z_U4 unsigned long -# elif (USHRT_MAX == 0xffffffffUL) -# define Z_U4 unsigned short -# endif -#endif - -#ifdef Z_U4 - typedef Z_U4 z_crc_t; -#else - typedef unsigned long z_crc_t; -#endif - -#ifdef HAVE_UNISTD_H /* may be set to #if 1 by ./configure */ -# define Z_HAVE_UNISTD_H -#endif - -#ifdef HAVE_STDARG_H /* may be set to #if 1 by ./configure */ -# define Z_HAVE_STDARG_H -#endif - -#ifdef STDC -# ifndef Z_SOLO -# include /* for off_t */ -# endif -#endif - -#if defined(STDC) || defined(Z_HAVE_STDARG_H) -# ifndef Z_SOLO -# include /* for va_list */ -# endif -#endif - -#ifdef _WIN32 -# ifndef Z_SOLO -# include /* for wchar_t */ -# endif -#endif - -/* a little trick to accommodate both "#define _LARGEFILE64_SOURCE" and - * "#define _LARGEFILE64_SOURCE 1" as requesting 64-bit operations, (even - * though the former does not conform to the LFS document), but considering - * both "#undef _LARGEFILE64_SOURCE" and "#define _LARGEFILE64_SOURCE 0" as - * equivalently requesting no 64-bit operations - */ -#if defined(_LARGEFILE64_SOURCE) && -_LARGEFILE64_SOURCE - -1 == 1 -# undef _LARGEFILE64_SOURCE -#endif - -#ifndef Z_HAVE_UNISTD_H -# ifdef __WATCOMC__ -# define Z_HAVE_UNISTD_H -# endif -#endif -#ifndef Z_HAVE_UNISTD_H -# if defined(_LARGEFILE64_SOURCE) && !defined(_WIN32) -# define Z_HAVE_UNISTD_H -# endif -#endif -#ifndef Z_SOLO -# if defined(Z_HAVE_UNISTD_H) -# include /* for SEEK_*, off_t, and _LFS64_LARGEFILE */ -# ifdef VMS -# include /* for off_t */ -# endif -# ifndef z_off_t -# define z_off_t off_t -# endif -# endif -#endif - -#if defined(_LFS64_LARGEFILE) && _LFS64_LARGEFILE-0 -# define Z_LFS64 -#endif - -#if defined(_LARGEFILE64_SOURCE) && defined(Z_LFS64) -# define Z_LARGE64 -#endif - -#if defined(_FILE_OFFSET_BITS) && _FILE_OFFSET_BITS-0 == 64 && defined(Z_LFS64) -# define Z_WANT64 -#endif - -#if !defined(SEEK_SET) && !defined(Z_SOLO) -# define SEEK_SET 0 /* Seek from beginning of file. */ -# define SEEK_CUR 1 /* Seek from current position. */ -# define SEEK_END 2 /* Set file pointer to EOF plus "offset" */ -#endif - -#ifndef z_off_t -# define z_off_t long -#endif - -#if !defined(_WIN32) && defined(Z_LARGE64) -# define z_off64_t off64_t -#else -# if defined(_WIN32) && !defined(__GNUC__) -# define z_off64_t __int64 -# else -# define z_off64_t z_off_t -# endif -#endif - -/* MVS linker does not support external names larger than 8 bytes */ -#if defined(__MVS__) - #pragma map(deflateInit_,"DEIN") - #pragma map(deflateInit2_,"DEIN2") - #pragma map(deflateEnd,"DEEND") - #pragma map(deflateBound,"DEBND") - #pragma map(inflateInit_,"ININ") - #pragma map(inflateInit2_,"ININ2") - #pragma map(inflateEnd,"INEND") - #pragma map(inflateSync,"INSY") - #pragma map(inflateSetDictionary,"INSEDI") - #pragma map(compressBound,"CMBND") - #pragma map(inflate_table,"INTABL") - #pragma map(inflate_fast,"INFA") - #pragma map(inflate_copyright,"INCOPY") -#endif - -#endif /* ZCONF_H */ diff --git a/phobos/etc/c/zlib/zlib.3 b/phobos/etc/c/zlib/zlib.3 deleted file mode 100644 index c716020..0000000 --- a/phobos/etc/c/zlib/zlib.3 +++ /dev/null @@ -1,149 +0,0 @@ -.TH ZLIB 3 "22 Jan 2024" -.SH NAME -zlib \- compression/decompression library -.SH SYNOPSIS -[see -.I zlib.h -for full description] -.SH DESCRIPTION -The -.I zlib -library is a general purpose data compression library. -The code is thread safe, assuming that the standard library functions -used are thread safe, such as memory allocation routines. -It provides in-memory compression and decompression functions, -including integrity checks of the uncompressed data. -This version of the library supports only one compression method (deflation) -but other algorithms may be added later -with the same stream interface. -.LP -Compression can be done in a single step if the buffers are large enough -or can be done by repeated calls of the compression function. -In the latter case, -the application must provide more input and/or consume the output -(providing more output space) before each call. -.LP -The library also supports reading and writing files in -.IR gzip (1) -(.gz) format -with an interface similar to that of stdio. -.LP -The library does not install any signal handler. -The decoder checks the consistency of the compressed data, -so the library should never crash even in the case of corrupted input. -.LP -All functions of the compression library are documented in the file -.IR zlib.h . -The distribution source includes examples of use of the library -in the files -.I test/example.c -and -.IR test/minigzip.c, -as well as other examples in the -.IR examples/ -directory. -.LP -Changes to this version are documented in the file -.I ChangeLog -that accompanies the source. -.LP -.I zlib -is built in to many languages and operating systems, including but not limited to -Java, Python, .NET, PHP, Perl, Ruby, Swift, and Go. -.LP -An experimental package to read and write files in the .zip format, -written on top of -.I zlib -by Gilles Vollant (info@winimage.com), -is available at: -.IP -http://www.winimage.com/zLibDll/minizip.html -and also in the -.I contrib/minizip -directory of the main -.I zlib -source distribution. -.SH "SEE ALSO" -The -.I zlib -web site can be found at: -.IP -http://zlib.net/ -.LP -The data format used by the -.I zlib -library is described by RFC -(Request for Comments) 1950 to 1952 in the files: -.IP -http://tools.ietf.org/html/rfc1950 (for the zlib header and trailer format) -.br -http://tools.ietf.org/html/rfc1951 (for the deflate compressed data format) -.br -http://tools.ietf.org/html/rfc1952 (for the gzip header and trailer format) -.LP -Mark Nelson wrote an article about -.I zlib -for the Jan. 1997 issue of Dr. Dobb's Journal; -a copy of the article is available at: -.IP -http://marknelson.us/1997/01/01/zlib-engine/ -.SH "REPORTING PROBLEMS" -Before reporting a problem, -please check the -.I zlib -web site to verify that you have the latest version of -.IR zlib ; -otherwise, -obtain the latest version and see if the problem still exists. -Please read the -.I zlib -FAQ at: -.IP -http://zlib.net/zlib_faq.html -.LP -before asking for help. -Send questions and/or comments to zlib@gzip.org, -or (for the Windows DLL version) to Gilles Vollant (info@winimage.com). -.SH AUTHORS AND LICENSE -Version 1.3.1 -.LP -Copyright (C) 1995-2024 Jean-loup Gailly and Mark Adler -.LP -This software is provided 'as-is', without any express or implied -warranty. In no event will the authors be held liable for any damages -arising from the use of this software. -.LP -Permission is granted to anyone to use this software for any purpose, -including commercial applications, and to alter it and redistribute it -freely, subject to the following restrictions: -.LP -.nr step 1 1 -.IP \n[step]. 3 -The origin of this software must not be misrepresented; you must not -claim that you wrote the original software. If you use this software -in a product, an acknowledgment in the product documentation would be -appreciated but is not required. -.IP \n+[step]. -Altered source versions must be plainly marked as such, and must not be -misrepresented as being the original software. -.IP \n+[step]. -This notice may not be removed or altered from any source distribution. -.LP -Jean-loup Gailly Mark Adler -.br -jloup@gzip.org madler@alumni.caltech.edu -.LP -The deflate format used by -.I zlib -was defined by Phil Katz. -The deflate and -.I zlib -specifications were written by L. Peter Deutsch. -Thanks to all the people who reported problems and suggested various -improvements in -.IR zlib ; -who are too numerous to cite here. -.LP -UNIX manual page by R. P. C. Rodgers, -U.S. National Library of Medicine (rodgers@nlm.nih.gov). -.\" end of man page diff --git a/phobos/etc/c/zlib/zlib.h b/phobos/etc/c/zlib/zlib.h deleted file mode 100644 index 8d4b932..0000000 --- a/phobos/etc/c/zlib/zlib.h +++ /dev/null @@ -1,1938 +0,0 @@ -/* zlib.h -- interface of the 'zlib' general purpose compression library - version 1.3.1, January 22nd, 2024 - - Copyright (C) 1995-2024 Jean-loup Gailly and Mark Adler - - This software is provided 'as-is', without any express or implied - warranty. In no event will the authors be held liable for any damages - arising from the use of this software. - - Permission is granted to anyone to use this software for any purpose, - including commercial applications, and to alter it and redistribute it - freely, subject to the following restrictions: - - 1. The origin of this software must not be misrepresented; you must not - claim that you wrote the original software. If you use this software - in a product, an acknowledgment in the product documentation would be - appreciated but is not required. - 2. Altered source versions must be plainly marked as such, and must not be - misrepresented as being the original software. - 3. This notice may not be removed or altered from any source distribution. - - Jean-loup Gailly Mark Adler - jloup@gzip.org madler@alumni.caltech.edu - - - The data format used by the zlib library is described by RFCs (Request for - Comments) 1950 to 1952 in the files http://tools.ietf.org/html/rfc1950 - (zlib format), rfc1951 (deflate format) and rfc1952 (gzip format). -*/ - -#ifndef ZLIB_H -#define ZLIB_H - -#include "zconf.h" - -#ifdef __cplusplus -extern "C" { -#endif - -#define ZLIB_VERSION "1.3.1" -#define ZLIB_VERNUM 0x1310 -#define ZLIB_VER_MAJOR 1 -#define ZLIB_VER_MINOR 3 -#define ZLIB_VER_REVISION 1 -#define ZLIB_VER_SUBREVISION 0 - -/* - The 'zlib' compression library provides in-memory compression and - decompression functions, including integrity checks of the uncompressed data. - This version of the library supports only one compression method (deflation) - but other algorithms will be added later and will have the same stream - interface. - - Compression can be done in a single step if the buffers are large enough, - or can be done by repeated calls of the compression function. In the latter - case, the application must provide more input and/or consume the output - (providing more output space) before each call. - - The compressed data format used by default by the in-memory functions is - the zlib format, which is a zlib wrapper documented in RFC 1950, wrapped - around a deflate stream, which is itself documented in RFC 1951. - - The library also supports reading and writing files in gzip (.gz) format - with an interface similar to that of stdio using the functions that start - with "gz". The gzip format is different from the zlib format. gzip is a - gzip wrapper, documented in RFC 1952, wrapped around a deflate stream. - - This library can optionally read and write gzip and raw deflate streams in - memory as well. - - The zlib format was designed to be compact and fast for use in memory - and on communications channels. The gzip format was designed for single- - file compression on file systems, has a larger header than zlib to maintain - directory information, and uses a different, slower check method than zlib. - - The library does not install any signal handler. The decoder checks - the consistency of the compressed data, so the library should never crash - even in the case of corrupted input. -*/ - -typedef voidpf (*alloc_func)(voidpf opaque, uInt items, uInt size); -typedef void (*free_func)(voidpf opaque, voidpf address); - -struct internal_state; - -typedef struct z_stream_s { - z_const Bytef *next_in; /* next input byte */ - uInt avail_in; /* number of bytes available at next_in */ - uLong total_in; /* total number of input bytes read so far */ - - Bytef *next_out; /* next output byte will go here */ - uInt avail_out; /* remaining free space at next_out */ - uLong total_out; /* total number of bytes output so far */ - - z_const char *msg; /* last error message, NULL if no error */ - struct internal_state FAR *state; /* not visible by applications */ - - alloc_func zalloc; /* used to allocate the internal state */ - free_func zfree; /* used to free the internal state */ - voidpf opaque; /* private data object passed to zalloc and zfree */ - - int data_type; /* best guess about the data type: binary or text - for deflate, or the decoding state for inflate */ - uLong adler; /* Adler-32 or CRC-32 value of the uncompressed data */ - uLong reserved; /* reserved for future use */ -} z_stream; - -typedef z_stream FAR *z_streamp; - -/* - gzip header information passed to and from zlib routines. See RFC 1952 - for more details on the meanings of these fields. -*/ -typedef struct gz_header_s { - int text; /* true if compressed data believed to be text */ - uLong time; /* modification time */ - int xflags; /* extra flags (not used when writing a gzip file) */ - int os; /* operating system */ - Bytef *extra; /* pointer to extra field or Z_NULL if none */ - uInt extra_len; /* extra field length (valid if extra != Z_NULL) */ - uInt extra_max; /* space at extra (only when reading header) */ - Bytef *name; /* pointer to zero-terminated file name or Z_NULL */ - uInt name_max; /* space at name (only when reading header) */ - Bytef *comment; /* pointer to zero-terminated comment or Z_NULL */ - uInt comm_max; /* space at comment (only when reading header) */ - int hcrc; /* true if there was or will be a header crc */ - int done; /* true when done reading gzip header (not used - when writing a gzip file) */ -} gz_header; - -typedef gz_header FAR *gz_headerp; - -/* - The application must update next_in and avail_in when avail_in has dropped - to zero. It must update next_out and avail_out when avail_out has dropped - to zero. The application must initialize zalloc, zfree and opaque before - calling the init function. All other fields are set by the compression - library and must not be updated by the application. - - The opaque value provided by the application will be passed as the first - parameter for calls of zalloc and zfree. This can be useful for custom - memory management. The compression library attaches no meaning to the - opaque value. - - zalloc must return Z_NULL if there is not enough memory for the object. - If zlib is used in a multi-threaded application, zalloc and zfree must be - thread safe. In that case, zlib is thread-safe. When zalloc and zfree are - Z_NULL on entry to the initialization function, they are set to internal - routines that use the standard library functions malloc() and free(). - - On 16-bit systems, the functions zalloc and zfree must be able to allocate - exactly 65536 bytes, but will not be required to allocate more than this if - the symbol MAXSEG_64K is defined (see zconf.h). WARNING: On MSDOS, pointers - returned by zalloc for objects of exactly 65536 bytes *must* have their - offset normalized to zero. The default allocation function provided by this - library ensures this (see zutil.c). To reduce memory requirements and avoid - any allocation of 64K objects, at the expense of compression ratio, compile - the library with -DMAX_WBITS=14 (see zconf.h). - - The fields total_in and total_out can be used for statistics or progress - reports. After compression, total_in holds the total size of the - uncompressed data and may be saved for use by the decompressor (particularly - if the decompressor wants to decompress everything in a single step). -*/ - - /* constants */ - -#define Z_NO_FLUSH 0 -#define Z_PARTIAL_FLUSH 1 -#define Z_SYNC_FLUSH 2 -#define Z_FULL_FLUSH 3 -#define Z_FINISH 4 -#define Z_BLOCK 5 -#define Z_TREES 6 -/* Allowed flush values; see deflate() and inflate() below for details */ - -#define Z_OK 0 -#define Z_STREAM_END 1 -#define Z_NEED_DICT 2 -#define Z_ERRNO (-1) -#define Z_STREAM_ERROR (-2) -#define Z_DATA_ERROR (-3) -#define Z_MEM_ERROR (-4) -#define Z_BUF_ERROR (-5) -#define Z_VERSION_ERROR (-6) -/* Return codes for the compression/decompression functions. Negative values - * are errors, positive values are used for special but normal events. - */ - -#define Z_NO_COMPRESSION 0 -#define Z_BEST_SPEED 1 -#define Z_BEST_COMPRESSION 9 -#define Z_DEFAULT_COMPRESSION (-1) -/* compression levels */ - -#define Z_FILTERED 1 -#define Z_HUFFMAN_ONLY 2 -#define Z_RLE 3 -#define Z_FIXED 4 -#define Z_DEFAULT_STRATEGY 0 -/* compression strategy; see deflateInit2() below for details */ - -#define Z_BINARY 0 -#define Z_TEXT 1 -#define Z_ASCII Z_TEXT /* for compatibility with 1.2.2 and earlier */ -#define Z_UNKNOWN 2 -/* Possible values of the data_type field for deflate() */ - -#define Z_DEFLATED 8 -/* The deflate compression method (the only one supported in this version) */ - -#define Z_NULL 0 /* for initializing zalloc, zfree, opaque */ - -#define zlib_version zlibVersion() -/* for compatibility with versions < 1.0.2 */ - - - /* basic functions */ - -ZEXTERN const char * ZEXPORT zlibVersion(void); -/* The application can compare zlibVersion and ZLIB_VERSION for consistency. - If the first character differs, the library code actually used is not - compatible with the zlib.h header file used by the application. This check - is automatically made by deflateInit and inflateInit. - */ - -/* -ZEXTERN int ZEXPORT deflateInit(z_streamp strm, int level); - - Initializes the internal stream state for compression. The fields - zalloc, zfree and opaque must be initialized before by the caller. If - zalloc and zfree are set to Z_NULL, deflateInit updates them to use default - allocation functions. total_in, total_out, adler, and msg are initialized. - - The compression level must be Z_DEFAULT_COMPRESSION, or between 0 and 9: - 1 gives best speed, 9 gives best compression, 0 gives no compression at all - (the input data is simply copied a block at a time). Z_DEFAULT_COMPRESSION - requests a default compromise between speed and compression (currently - equivalent to level 6). - - deflateInit returns Z_OK if success, Z_MEM_ERROR if there was not enough - memory, Z_STREAM_ERROR if level is not a valid compression level, or - Z_VERSION_ERROR if the zlib library version (zlib_version) is incompatible - with the version assumed by the caller (ZLIB_VERSION). msg is set to null - if there is no error message. deflateInit does not perform any compression: - this will be done by deflate(). -*/ - - -ZEXTERN int ZEXPORT deflate(z_streamp strm, int flush); -/* - deflate compresses as much data as possible, and stops when the input - buffer becomes empty or the output buffer becomes full. It may introduce - some output latency (reading input without producing any output) except when - forced to flush. - - The detailed semantics are as follows. deflate performs one or both of the - following actions: - - - Compress more input starting at next_in and update next_in and avail_in - accordingly. If not all input can be processed (because there is not - enough room in the output buffer), next_in and avail_in are updated and - processing will resume at this point for the next call of deflate(). - - - Generate more output starting at next_out and update next_out and avail_out - accordingly. This action is forced if the parameter flush is non zero. - Forcing flush frequently degrades the compression ratio, so this parameter - should be set only when necessary. Some output may be provided even if - flush is zero. - - Before the call of deflate(), the application should ensure that at least - one of the actions is possible, by providing more input and/or consuming more - output, and updating avail_in or avail_out accordingly; avail_out should - never be zero before the call. The application can consume the compressed - output when it wants, for example when the output buffer is full (avail_out - == 0), or after each call of deflate(). If deflate returns Z_OK and with - zero avail_out, it must be called again after making room in the output - buffer because there might be more output pending. See deflatePending(), - which can be used if desired to determine whether or not there is more output - in that case. - - Normally the parameter flush is set to Z_NO_FLUSH, which allows deflate to - decide how much data to accumulate before producing output, in order to - maximize compression. - - If the parameter flush is set to Z_SYNC_FLUSH, all pending output is - flushed to the output buffer and the output is aligned on a byte boundary, so - that the decompressor can get all input data available so far. (In - particular avail_in is zero after the call if enough output space has been - provided before the call.) Flushing may degrade compression for some - compression algorithms and so it should be used only when necessary. This - completes the current deflate block and follows it with an empty stored block - that is three bits plus filler bits to the next byte, followed by four bytes - (00 00 ff ff). - - If flush is set to Z_PARTIAL_FLUSH, all pending output is flushed to the - output buffer, but the output is not aligned to a byte boundary. All of the - input data so far will be available to the decompressor, as for Z_SYNC_FLUSH. - This completes the current deflate block and follows it with an empty fixed - codes block that is 10 bits long. This assures that enough bytes are output - in order for the decompressor to finish the block before the empty fixed - codes block. - - If flush is set to Z_BLOCK, a deflate block is completed and emitted, as - for Z_SYNC_FLUSH, but the output is not aligned on a byte boundary, and up to - seven bits of the current block are held to be written as the next byte after - the next deflate block is completed. In this case, the decompressor may not - be provided enough bits at this point in order to complete decompression of - the data provided so far to the compressor. It may need to wait for the next - block to be emitted. This is for advanced applications that need to control - the emission of deflate blocks. - - If flush is set to Z_FULL_FLUSH, all output is flushed as with - Z_SYNC_FLUSH, and the compression state is reset so that decompression can - restart from this point if previous compressed data has been damaged or if - random access is desired. Using Z_FULL_FLUSH too often can seriously degrade - compression. - - If deflate returns with avail_out == 0, this function must be called again - with the same value of the flush parameter and more output space (updated - avail_out), until the flush is complete (deflate returns with non-zero - avail_out). In the case of a Z_FULL_FLUSH or Z_SYNC_FLUSH, make sure that - avail_out is greater than six when the flush marker begins, in order to avoid - repeated flush markers upon calling deflate() again when avail_out == 0. - - If the parameter flush is set to Z_FINISH, pending input is processed, - pending output is flushed and deflate returns with Z_STREAM_END if there was - enough output space. If deflate returns with Z_OK or Z_BUF_ERROR, this - function must be called again with Z_FINISH and more output space (updated - avail_out) but no more input data, until it returns with Z_STREAM_END or an - error. After deflate has returned Z_STREAM_END, the only possible operations - on the stream are deflateReset or deflateEnd. - - Z_FINISH can be used in the first deflate call after deflateInit if all the - compression is to be done in a single step. In order to complete in one - call, avail_out must be at least the value returned by deflateBound (see - below). Then deflate is guaranteed to return Z_STREAM_END. If not enough - output space is provided, deflate will not return Z_STREAM_END, and it must - be called again as described above. - - deflate() sets strm->adler to the Adler-32 checksum of all input read - so far (that is, total_in bytes). If a gzip stream is being generated, then - strm->adler will be the CRC-32 checksum of the input read so far. (See - deflateInit2 below.) - - deflate() may update strm->data_type if it can make a good guess about - the input data type (Z_BINARY or Z_TEXT). If in doubt, the data is - considered binary. This field is only for information purposes and does not - affect the compression algorithm in any manner. - - deflate() returns Z_OK if some progress has been made (more input - processed or more output produced), Z_STREAM_END if all input has been - consumed and all output has been produced (only when flush is set to - Z_FINISH), Z_STREAM_ERROR if the stream state was inconsistent (for example - if next_in or next_out was Z_NULL or the state was inadvertently written over - by the application), or Z_BUF_ERROR if no progress is possible (for example - avail_in or avail_out was zero). Note that Z_BUF_ERROR is not fatal, and - deflate() can be called again with more input and more output space to - continue compressing. -*/ - - -ZEXTERN int ZEXPORT deflateEnd(z_streamp strm); -/* - All dynamically allocated data structures for this stream are freed. - This function discards any unprocessed input and does not flush any pending - output. - - deflateEnd returns Z_OK if success, Z_STREAM_ERROR if the - stream state was inconsistent, Z_DATA_ERROR if the stream was freed - prematurely (some input or output was discarded). In the error case, msg - may be set but then points to a static string (which must not be - deallocated). -*/ - - -/* -ZEXTERN int ZEXPORT inflateInit(z_streamp strm); - - Initializes the internal stream state for decompression. The fields - next_in, avail_in, zalloc, zfree and opaque must be initialized before by - the caller. In the current version of inflate, the provided input is not - read or consumed. The allocation of a sliding window will be deferred to - the first call of inflate (if the decompression does not complete on the - first call). If zalloc and zfree are set to Z_NULL, inflateInit updates - them to use default allocation functions. total_in, total_out, adler, and - msg are initialized. - - inflateInit returns Z_OK if success, Z_MEM_ERROR if there was not enough - memory, Z_VERSION_ERROR if the zlib library version is incompatible with the - version assumed by the caller, or Z_STREAM_ERROR if the parameters are - invalid, such as a null pointer to the structure. msg is set to null if - there is no error message. inflateInit does not perform any decompression. - Actual decompression will be done by inflate(). So next_in, and avail_in, - next_out, and avail_out are unused and unchanged. The current - implementation of inflateInit() does not process any header information -- - that is deferred until inflate() is called. -*/ - - -ZEXTERN int ZEXPORT inflate(z_streamp strm, int flush); -/* - inflate decompresses as much data as possible, and stops when the input - buffer becomes empty or the output buffer becomes full. It may introduce - some output latency (reading input without producing any output) except when - forced to flush. - - The detailed semantics are as follows. inflate performs one or both of the - following actions: - - - Decompress more input starting at next_in and update next_in and avail_in - accordingly. If not all input can be processed (because there is not - enough room in the output buffer), then next_in and avail_in are updated - accordingly, and processing will resume at this point for the next call of - inflate(). - - - Generate more output starting at next_out and update next_out and avail_out - accordingly. inflate() provides as much output as possible, until there is - no more input data or no more space in the output buffer (see below about - the flush parameter). - - Before the call of inflate(), the application should ensure that at least - one of the actions is possible, by providing more input and/or consuming more - output, and updating the next_* and avail_* values accordingly. If the - caller of inflate() does not provide both available input and available - output space, it is possible that there will be no progress made. The - application can consume the uncompressed output when it wants, for example - when the output buffer is full (avail_out == 0), or after each call of - inflate(). If inflate returns Z_OK and with zero avail_out, it must be - called again after making room in the output buffer because there might be - more output pending. - - The flush parameter of inflate() can be Z_NO_FLUSH, Z_SYNC_FLUSH, Z_FINISH, - Z_BLOCK, or Z_TREES. Z_SYNC_FLUSH requests that inflate() flush as much - output as possible to the output buffer. Z_BLOCK requests that inflate() - stop if and when it gets to the next deflate block boundary. When decoding - the zlib or gzip format, this will cause inflate() to return immediately - after the header and before the first block. When doing a raw inflate, - inflate() will go ahead and process the first block, and will return when it - gets to the end of that block, or when it runs out of data. - - The Z_BLOCK option assists in appending to or combining deflate streams. - To assist in this, on return inflate() always sets strm->data_type to the - number of unused bits in the last byte taken from strm->next_in, plus 64 if - inflate() is currently decoding the last block in the deflate stream, plus - 128 if inflate() returned immediately after decoding an end-of-block code or - decoding the complete header up to just before the first byte of the deflate - stream. The end-of-block will not be indicated until all of the uncompressed - data from that block has been written to strm->next_out. The number of - unused bits may in general be greater than seven, except when bit 7 of - data_type is set, in which case the number of unused bits will be less than - eight. data_type is set as noted here every time inflate() returns for all - flush options, and so can be used to determine the amount of currently - consumed input in bits. - - The Z_TREES option behaves as Z_BLOCK does, but it also returns when the - end of each deflate block header is reached, before any actual data in that - block is decoded. This allows the caller to determine the length of the - deflate block header for later use in random access within a deflate block. - 256 is added to the value of strm->data_type when inflate() returns - immediately after reaching the end of the deflate block header. - - inflate() should normally be called until it returns Z_STREAM_END or an - error. However if all decompression is to be performed in a single step (a - single call of inflate), the parameter flush should be set to Z_FINISH. In - this case all pending input is processed and all pending output is flushed; - avail_out must be large enough to hold all of the uncompressed data for the - operation to complete. (The size of the uncompressed data may have been - saved by the compressor for this purpose.) The use of Z_FINISH is not - required to perform an inflation in one step. However it may be used to - inform inflate that a faster approach can be used for the single inflate() - call. Z_FINISH also informs inflate to not maintain a sliding window if the - stream completes, which reduces inflate's memory footprint. If the stream - does not complete, either because not all of the stream is provided or not - enough output space is provided, then a sliding window will be allocated and - inflate() can be called again to continue the operation as if Z_NO_FLUSH had - been used. - - In this implementation, inflate() always flushes as much output as - possible to the output buffer, and always uses the faster approach on the - first call. So the effects of the flush parameter in this implementation are - on the return value of inflate() as noted below, when inflate() returns early - when Z_BLOCK or Z_TREES is used, and when inflate() avoids the allocation of - memory for a sliding window when Z_FINISH is used. - - If a preset dictionary is needed after this call (see inflateSetDictionary - below), inflate sets strm->adler to the Adler-32 checksum of the dictionary - chosen by the compressor and returns Z_NEED_DICT; otherwise it sets - strm->adler to the Adler-32 checksum of all output produced so far (that is, - total_out bytes) and returns Z_OK, Z_STREAM_END or an error code as described - below. At the end of the stream, inflate() checks that its computed Adler-32 - checksum is equal to that saved by the compressor and returns Z_STREAM_END - only if the checksum is correct. - - inflate() can decompress and check either zlib-wrapped or gzip-wrapped - deflate data. The header type is detected automatically, if requested when - initializing with inflateInit2(). Any information contained in the gzip - header is not retained unless inflateGetHeader() is used. When processing - gzip-wrapped deflate data, strm->adler32 is set to the CRC-32 of the output - produced so far. The CRC-32 is checked against the gzip trailer, as is the - uncompressed length, modulo 2^32. - - inflate() returns Z_OK if some progress has been made (more input processed - or more output produced), Z_STREAM_END if the end of the compressed data has - been reached and all uncompressed output has been produced, Z_NEED_DICT if a - preset dictionary is needed at this point, Z_DATA_ERROR if the input data was - corrupted (input stream not conforming to the zlib format or incorrect check - value, in which case strm->msg points to a string with a more specific - error), Z_STREAM_ERROR if the stream structure was inconsistent (for example - next_in or next_out was Z_NULL, or the state was inadvertently written over - by the application), Z_MEM_ERROR if there was not enough memory, Z_BUF_ERROR - if no progress was possible or if there was not enough room in the output - buffer when Z_FINISH is used. Note that Z_BUF_ERROR is not fatal, and - inflate() can be called again with more input and more output space to - continue decompressing. If Z_DATA_ERROR is returned, the application may - then call inflateSync() to look for a good compression block if a partial - recovery of the data is to be attempted. -*/ - - -ZEXTERN int ZEXPORT inflateEnd(z_streamp strm); -/* - All dynamically allocated data structures for this stream are freed. - This function discards any unprocessed input and does not flush any pending - output. - - inflateEnd returns Z_OK if success, or Z_STREAM_ERROR if the stream state - was inconsistent. -*/ - - - /* Advanced functions */ - -/* - The following functions are needed only in some special applications. -*/ - -/* -ZEXTERN int ZEXPORT deflateInit2(z_streamp strm, - int level, - int method, - int windowBits, - int memLevel, - int strategy); - - This is another version of deflateInit with more compression options. The - fields zalloc, zfree and opaque must be initialized before by the caller. - - The method parameter is the compression method. It must be Z_DEFLATED in - this version of the library. - - The windowBits parameter is the base two logarithm of the window size - (the size of the history buffer). It should be in the range 8..15 for this - version of the library. Larger values of this parameter result in better - compression at the expense of memory usage. The default value is 15 if - deflateInit is used instead. - - For the current implementation of deflate(), a windowBits value of 8 (a - window size of 256 bytes) is not supported. As a result, a request for 8 - will result in 9 (a 512-byte window). In that case, providing 8 to - inflateInit2() will result in an error when the zlib header with 9 is - checked against the initialization of inflate(). The remedy is to not use 8 - with deflateInit2() with this initialization, or at least in that case use 9 - with inflateInit2(). - - windowBits can also be -8..-15 for raw deflate. In this case, -windowBits - determines the window size. deflate() will then generate raw deflate data - with no zlib header or trailer, and will not compute a check value. - - windowBits can also be greater than 15 for optional gzip encoding. Add - 16 to windowBits to write a simple gzip header and trailer around the - compressed data instead of a zlib wrapper. The gzip header will have no - file name, no extra data, no comment, no modification time (set to zero), no - header crc, and the operating system will be set to the appropriate value, - if the operating system was determined at compile time. If a gzip stream is - being written, strm->adler is a CRC-32 instead of an Adler-32. - - For raw deflate or gzip encoding, a request for a 256-byte window is - rejected as invalid, since only the zlib header provides a means of - transmitting the window size to the decompressor. - - The memLevel parameter specifies how much memory should be allocated - for the internal compression state. memLevel=1 uses minimum memory but is - slow and reduces compression ratio; memLevel=9 uses maximum memory for - optimal speed. The default value is 8. See zconf.h for total memory usage - as a function of windowBits and memLevel. - - The strategy parameter is used to tune the compression algorithm. Use the - value Z_DEFAULT_STRATEGY for normal data, Z_FILTERED for data produced by a - filter (or predictor), Z_HUFFMAN_ONLY to force Huffman encoding only (no - string match), or Z_RLE to limit match distances to one (run-length - encoding). Filtered data consists mostly of small values with a somewhat - random distribution. In this case, the compression algorithm is tuned to - compress them better. The effect of Z_FILTERED is to force more Huffman - coding and less string matching; it is somewhat intermediate between - Z_DEFAULT_STRATEGY and Z_HUFFMAN_ONLY. Z_RLE is designed to be almost as - fast as Z_HUFFMAN_ONLY, but give better compression for PNG image data. The - strategy parameter only affects the compression ratio but not the - correctness of the compressed output even if it is not set appropriately. - Z_FIXED prevents the use of dynamic Huffman codes, allowing for a simpler - decoder for special applications. - - deflateInit2 returns Z_OK if success, Z_MEM_ERROR if there was not enough - memory, Z_STREAM_ERROR if any parameter is invalid (such as an invalid - method), or Z_VERSION_ERROR if the zlib library version (zlib_version) is - incompatible with the version assumed by the caller (ZLIB_VERSION). msg is - set to null if there is no error message. deflateInit2 does not perform any - compression: this will be done by deflate(). -*/ - -ZEXTERN int ZEXPORT deflateSetDictionary(z_streamp strm, - const Bytef *dictionary, - uInt dictLength); -/* - Initializes the compression dictionary from the given byte sequence - without producing any compressed output. When using the zlib format, this - function must be called immediately after deflateInit, deflateInit2 or - deflateReset, and before any call of deflate. When doing raw deflate, this - function must be called either before any call of deflate, or immediately - after the completion of a deflate block, i.e. after all input has been - consumed and all output has been delivered when using any of the flush - options Z_BLOCK, Z_PARTIAL_FLUSH, Z_SYNC_FLUSH, or Z_FULL_FLUSH. The - compressor and decompressor must use exactly the same dictionary (see - inflateSetDictionary). - - The dictionary should consist of strings (byte sequences) that are likely - to be encountered later in the data to be compressed, with the most commonly - used strings preferably put towards the end of the dictionary. Using a - dictionary is most useful when the data to be compressed is short and can be - predicted with good accuracy; the data can then be compressed better than - with the default empty dictionary. - - Depending on the size of the compression data structures selected by - deflateInit or deflateInit2, a part of the dictionary may in effect be - discarded, for example if the dictionary is larger than the window size - provided in deflateInit or deflateInit2. Thus the strings most likely to be - useful should be put at the end of the dictionary, not at the front. In - addition, the current implementation of deflate will use at most the window - size minus 262 bytes of the provided dictionary. - - Upon return of this function, strm->adler is set to the Adler-32 value - of the dictionary; the decompressor may later use this value to determine - which dictionary has been used by the compressor. (The Adler-32 value - applies to the whole dictionary even if only a subset of the dictionary is - actually used by the compressor.) If a raw deflate was requested, then the - Adler-32 value is not computed and strm->adler is not set. - - deflateSetDictionary returns Z_OK if success, or Z_STREAM_ERROR if a - parameter is invalid (e.g. dictionary being Z_NULL) or the stream state is - inconsistent (for example if deflate has already been called for this stream - or if not at a block boundary for raw deflate). deflateSetDictionary does - not perform any compression: this will be done by deflate(). -*/ - -ZEXTERN int ZEXPORT deflateGetDictionary(z_streamp strm, - Bytef *dictionary, - uInt *dictLength); -/* - Returns the sliding dictionary being maintained by deflate. dictLength is - set to the number of bytes in the dictionary, and that many bytes are copied - to dictionary. dictionary must have enough space, where 32768 bytes is - always enough. If deflateGetDictionary() is called with dictionary equal to - Z_NULL, then only the dictionary length is returned, and nothing is copied. - Similarly, if dictLength is Z_NULL, then it is not set. - - deflateGetDictionary() may return a length less than the window size, even - when more than the window size in input has been provided. It may return up - to 258 bytes less in that case, due to how zlib's implementation of deflate - manages the sliding window and lookahead for matches, where matches can be - up to 258 bytes long. If the application needs the last window-size bytes of - input, then that would need to be saved by the application outside of zlib. - - deflateGetDictionary returns Z_OK on success, or Z_STREAM_ERROR if the - stream state is inconsistent. -*/ - -ZEXTERN int ZEXPORT deflateCopy(z_streamp dest, - z_streamp source); -/* - Sets the destination stream as a complete copy of the source stream. - - This function can be useful when several compression strategies will be - tried, for example when there are several ways of pre-processing the input - data with a filter. The streams that will be discarded should then be freed - by calling deflateEnd. Note that deflateCopy duplicates the internal - compression state which can be quite large, so this strategy is slow and can - consume lots of memory. - - deflateCopy returns Z_OK if success, Z_MEM_ERROR if there was not - enough memory, Z_STREAM_ERROR if the source stream state was inconsistent - (such as zalloc being Z_NULL). msg is left unchanged in both source and - destination. -*/ - -ZEXTERN int ZEXPORT deflateReset(z_streamp strm); -/* - This function is equivalent to deflateEnd followed by deflateInit, but - does not free and reallocate the internal compression state. The stream - will leave the compression level and any other attributes that may have been - set unchanged. total_in, total_out, adler, and msg are initialized. - - deflateReset returns Z_OK if success, or Z_STREAM_ERROR if the source - stream state was inconsistent (such as zalloc or state being Z_NULL). -*/ - -ZEXTERN int ZEXPORT deflateParams(z_streamp strm, - int level, - int strategy); -/* - Dynamically update the compression level and compression strategy. The - interpretation of level and strategy is as in deflateInit2(). This can be - used to switch between compression and straight copy of the input data, or - to switch to a different kind of input data requiring a different strategy. - If the compression approach (which is a function of the level) or the - strategy is changed, and if there have been any deflate() calls since the - state was initialized or reset, then the input available so far is - compressed with the old level and strategy using deflate(strm, Z_BLOCK). - There are three approaches for the compression levels 0, 1..3, and 4..9 - respectively. The new level and strategy will take effect at the next call - of deflate(). - - If a deflate(strm, Z_BLOCK) is performed by deflateParams(), and it does - not have enough output space to complete, then the parameter change will not - take effect. In this case, deflateParams() can be called again with the - same parameters and more output space to try again. - - In order to assure a change in the parameters on the first try, the - deflate stream should be flushed using deflate() with Z_BLOCK or other flush - request until strm.avail_out is not zero, before calling deflateParams(). - Then no more input data should be provided before the deflateParams() call. - If this is done, the old level and strategy will be applied to the data - compressed before deflateParams(), and the new level and strategy will be - applied to the data compressed after deflateParams(). - - deflateParams returns Z_OK on success, Z_STREAM_ERROR if the source stream - state was inconsistent or if a parameter was invalid, or Z_BUF_ERROR if - there was not enough output space to complete the compression of the - available input data before a change in the strategy or approach. Note that - in the case of a Z_BUF_ERROR, the parameters are not changed. A return - value of Z_BUF_ERROR is not fatal, in which case deflateParams() can be - retried with more output space. -*/ - -ZEXTERN int ZEXPORT deflateTune(z_streamp strm, - int good_length, - int max_lazy, - int nice_length, - int max_chain); -/* - Fine tune deflate's internal compression parameters. This should only be - used by someone who understands the algorithm used by zlib's deflate for - searching for the best matching string, and even then only by the most - fanatic optimizer trying to squeeze out the last compressed bit for their - specific input data. Read the deflate.c source code for the meaning of the - max_lazy, good_length, nice_length, and max_chain parameters. - - deflateTune() can be called after deflateInit() or deflateInit2(), and - returns Z_OK on success, or Z_STREAM_ERROR for an invalid deflate stream. - */ - -ZEXTERN uLong ZEXPORT deflateBound(z_streamp strm, - uLong sourceLen); -/* - deflateBound() returns an upper bound on the compressed size after - deflation of sourceLen bytes. It must be called after deflateInit() or - deflateInit2(), and after deflateSetHeader(), if used. This would be used - to allocate an output buffer for deflation in a single pass, and so would be - called before deflate(). If that first deflate() call is provided the - sourceLen input bytes, an output buffer allocated to the size returned by - deflateBound(), and the flush value Z_FINISH, then deflate() is guaranteed - to return Z_STREAM_END. Note that it is possible for the compressed size to - be larger than the value returned by deflateBound() if flush options other - than Z_FINISH or Z_NO_FLUSH are used. -*/ - -ZEXTERN int ZEXPORT deflatePending(z_streamp strm, - unsigned *pending, - int *bits); -/* - deflatePending() returns the number of bytes and bits of output that have - been generated, but not yet provided in the available output. The bytes not - provided would be due to the available output space having being consumed. - The number of bits of output not provided are between 0 and 7, where they - await more bits to join them in order to fill out a full byte. If pending - or bits are Z_NULL, then those values are not set. - - deflatePending returns Z_OK if success, or Z_STREAM_ERROR if the source - stream state was inconsistent. - */ - -ZEXTERN int ZEXPORT deflatePrime(z_streamp strm, - int bits, - int value); -/* - deflatePrime() inserts bits in the deflate output stream. The intent - is that this function is used to start off the deflate output with the bits - leftover from a previous deflate stream when appending to it. As such, this - function can only be used for raw deflate, and must be used before the first - deflate() call after a deflateInit2() or deflateReset(). bits must be less - than or equal to 16, and that many of the least significant bits of value - will be inserted in the output. - - deflatePrime returns Z_OK if success, Z_BUF_ERROR if there was not enough - room in the internal buffer to insert the bits, or Z_STREAM_ERROR if the - source stream state was inconsistent. -*/ - -ZEXTERN int ZEXPORT deflateSetHeader(z_streamp strm, - gz_headerp head); -/* - deflateSetHeader() provides gzip header information for when a gzip - stream is requested by deflateInit2(). deflateSetHeader() may be called - after deflateInit2() or deflateReset() and before the first call of - deflate(). The text, time, os, extra field, name, and comment information - in the provided gz_header structure are written to the gzip header (xflag is - ignored -- the extra flags are set according to the compression level). The - caller must assure that, if not Z_NULL, name and comment are terminated with - a zero byte, and that if extra is not Z_NULL, that extra_len bytes are - available there. If hcrc is true, a gzip header crc is included. Note that - the current versions of the command-line version of gzip (up through version - 1.3.x) do not support header crc's, and will report that it is a "multi-part - gzip file" and give up. - - If deflateSetHeader is not used, the default gzip header has text false, - the time set to zero, and os set to the current operating system, with no - extra, name, or comment fields. The gzip header is returned to the default - state by deflateReset(). - - deflateSetHeader returns Z_OK if success, or Z_STREAM_ERROR if the source - stream state was inconsistent. -*/ - -/* -ZEXTERN int ZEXPORT inflateInit2(z_streamp strm, - int windowBits); - - This is another version of inflateInit with an extra parameter. The - fields next_in, avail_in, zalloc, zfree and opaque must be initialized - before by the caller. - - The windowBits parameter is the base two logarithm of the maximum window - size (the size of the history buffer). It should be in the range 8..15 for - this version of the library. The default value is 15 if inflateInit is used - instead. windowBits must be greater than or equal to the windowBits value - provided to deflateInit2() while compressing, or it must be equal to 15 if - deflateInit2() was not used. If a compressed stream with a larger window - size is given as input, inflate() will return with the error code - Z_DATA_ERROR instead of trying to allocate a larger window. - - windowBits can also be zero to request that inflate use the window size in - the zlib header of the compressed stream. - - windowBits can also be -8..-15 for raw inflate. In this case, -windowBits - determines the window size. inflate() will then process raw deflate data, - not looking for a zlib or gzip header, not generating a check value, and not - looking for any check values for comparison at the end of the stream. This - is for use with other formats that use the deflate compressed data format - such as zip. Those formats provide their own check values. If a custom - format is developed using the raw deflate format for compressed data, it is - recommended that a check value such as an Adler-32 or a CRC-32 be applied to - the uncompressed data as is done in the zlib, gzip, and zip formats. For - most applications, the zlib format should be used as is. Note that comments - above on the use in deflateInit2() applies to the magnitude of windowBits. - - windowBits can also be greater than 15 for optional gzip decoding. Add - 32 to windowBits to enable zlib and gzip decoding with automatic header - detection, or add 16 to decode only the gzip format (the zlib format will - return a Z_DATA_ERROR). If a gzip stream is being decoded, strm->adler is a - CRC-32 instead of an Adler-32. Unlike the gunzip utility and gzread() (see - below), inflate() will *not* automatically decode concatenated gzip members. - inflate() will return Z_STREAM_END at the end of the gzip member. The state - would need to be reset to continue decoding a subsequent gzip member. This - *must* be done if there is more data after a gzip member, in order for the - decompression to be compliant with the gzip standard (RFC 1952). - - inflateInit2 returns Z_OK if success, Z_MEM_ERROR if there was not enough - memory, Z_VERSION_ERROR if the zlib library version is incompatible with the - version assumed by the caller, or Z_STREAM_ERROR if the parameters are - invalid, such as a null pointer to the structure. msg is set to null if - there is no error message. inflateInit2 does not perform any decompression - apart from possibly reading the zlib header if present: actual decompression - will be done by inflate(). (So next_in and avail_in may be modified, but - next_out and avail_out are unused and unchanged.) The current implementation - of inflateInit2() does not process any header information -- that is - deferred until inflate() is called. -*/ - -ZEXTERN int ZEXPORT inflateSetDictionary(z_streamp strm, - const Bytef *dictionary, - uInt dictLength); -/* - Initializes the decompression dictionary from the given uncompressed byte - sequence. This function must be called immediately after a call of inflate, - if that call returned Z_NEED_DICT. The dictionary chosen by the compressor - can be determined from the Adler-32 value returned by that call of inflate. - The compressor and decompressor must use exactly the same dictionary (see - deflateSetDictionary). For raw inflate, this function can be called at any - time to set the dictionary. If the provided dictionary is smaller than the - window and there is already data in the window, then the provided dictionary - will amend what's there. The application must insure that the dictionary - that was used for compression is provided. - - inflateSetDictionary returns Z_OK if success, Z_STREAM_ERROR if a - parameter is invalid (e.g. dictionary being Z_NULL) or the stream state is - inconsistent, Z_DATA_ERROR if the given dictionary doesn't match the - expected one (incorrect Adler-32 value). inflateSetDictionary does not - perform any decompression: this will be done by subsequent calls of - inflate(). -*/ - -ZEXTERN int ZEXPORT inflateGetDictionary(z_streamp strm, - Bytef *dictionary, - uInt *dictLength); -/* - Returns the sliding dictionary being maintained by inflate. dictLength is - set to the number of bytes in the dictionary, and that many bytes are copied - to dictionary. dictionary must have enough space, where 32768 bytes is - always enough. If inflateGetDictionary() is called with dictionary equal to - Z_NULL, then only the dictionary length is returned, and nothing is copied. - Similarly, if dictLength is Z_NULL, then it is not set. - - inflateGetDictionary returns Z_OK on success, or Z_STREAM_ERROR if the - stream state is inconsistent. -*/ - -ZEXTERN int ZEXPORT inflateSync(z_streamp strm); -/* - Skips invalid compressed data until a possible full flush point (see above - for the description of deflate with Z_FULL_FLUSH) can be found, or until all - available input is skipped. No output is provided. - - inflateSync searches for a 00 00 FF FF pattern in the compressed data. - All full flush points have this pattern, but not all occurrences of this - pattern are full flush points. - - inflateSync returns Z_OK if a possible full flush point has been found, - Z_BUF_ERROR if no more input was provided, Z_DATA_ERROR if no flush point - has been found, or Z_STREAM_ERROR if the stream structure was inconsistent. - In the success case, the application may save the current value of total_in - which indicates where valid compressed data was found. In the error case, - the application may repeatedly call inflateSync, providing more input each - time, until success or end of the input data. -*/ - -ZEXTERN int ZEXPORT inflateCopy(z_streamp dest, - z_streamp source); -/* - Sets the destination stream as a complete copy of the source stream. - - This function can be useful when randomly accessing a large stream. The - first pass through the stream can periodically record the inflate state, - allowing restarting inflate at those points when randomly accessing the - stream. - - inflateCopy returns Z_OK if success, Z_MEM_ERROR if there was not - enough memory, Z_STREAM_ERROR if the source stream state was inconsistent - (such as zalloc being Z_NULL). msg is left unchanged in both source and - destination. -*/ - -ZEXTERN int ZEXPORT inflateReset(z_streamp strm); -/* - This function is equivalent to inflateEnd followed by inflateInit, - but does not free and reallocate the internal decompression state. The - stream will keep attributes that may have been set by inflateInit2. - total_in, total_out, adler, and msg are initialized. - - inflateReset returns Z_OK if success, or Z_STREAM_ERROR if the source - stream state was inconsistent (such as zalloc or state being Z_NULL). -*/ - -ZEXTERN int ZEXPORT inflateReset2(z_streamp strm, - int windowBits); -/* - This function is the same as inflateReset, but it also permits changing - the wrap and window size requests. The windowBits parameter is interpreted - the same as it is for inflateInit2. If the window size is changed, then the - memory allocated for the window is freed, and the window will be reallocated - by inflate() if needed. - - inflateReset2 returns Z_OK if success, or Z_STREAM_ERROR if the source - stream state was inconsistent (such as zalloc or state being Z_NULL), or if - the windowBits parameter is invalid. -*/ - -ZEXTERN int ZEXPORT inflatePrime(z_streamp strm, - int bits, - int value); -/* - This function inserts bits in the inflate input stream. The intent is - that this function is used to start inflating at a bit position in the - middle of a byte. The provided bits will be used before any bytes are used - from next_in. This function should only be used with raw inflate, and - should be used before the first inflate() call after inflateInit2() or - inflateReset(). bits must be less than or equal to 16, and that many of the - least significant bits of value will be inserted in the input. - - If bits is negative, then the input stream bit buffer is emptied. Then - inflatePrime() can be called again to put bits in the buffer. This is used - to clear out bits leftover after feeding inflate a block description prior - to feeding inflate codes. - - inflatePrime returns Z_OK if success, or Z_STREAM_ERROR if the source - stream state was inconsistent. -*/ - -ZEXTERN long ZEXPORT inflateMark(z_streamp strm); -/* - This function returns two values, one in the lower 16 bits of the return - value, and the other in the remaining upper bits, obtained by shifting the - return value down 16 bits. If the upper value is -1 and the lower value is - zero, then inflate() is currently decoding information outside of a block. - If the upper value is -1 and the lower value is non-zero, then inflate is in - the middle of a stored block, with the lower value equaling the number of - bytes from the input remaining to copy. If the upper value is not -1, then - it is the number of bits back from the current bit position in the input of - the code (literal or length/distance pair) currently being processed. In - that case the lower value is the number of bytes already emitted for that - code. - - A code is being processed if inflate is waiting for more input to complete - decoding of the code, or if it has completed decoding but is waiting for - more output space to write the literal or match data. - - inflateMark() is used to mark locations in the input data for random - access, which may be at bit positions, and to note those cases where the - output of a code may span boundaries of random access blocks. The current - location in the input stream can be determined from avail_in and data_type - as noted in the description for the Z_BLOCK flush parameter for inflate. - - inflateMark returns the value noted above, or -65536 if the provided - source stream state was inconsistent. -*/ - -ZEXTERN int ZEXPORT inflateGetHeader(z_streamp strm, - gz_headerp head); -/* - inflateGetHeader() requests that gzip header information be stored in the - provided gz_header structure. inflateGetHeader() may be called after - inflateInit2() or inflateReset(), and before the first call of inflate(). - As inflate() processes the gzip stream, head->done is zero until the header - is completed, at which time head->done is set to one. If a zlib stream is - being decoded, then head->done is set to -1 to indicate that there will be - no gzip header information forthcoming. Note that Z_BLOCK or Z_TREES can be - used to force inflate() to return immediately after header processing is - complete and before any actual data is decompressed. - - The text, time, xflags, and os fields are filled in with the gzip header - contents. hcrc is set to true if there is a header CRC. (The header CRC - was valid if done is set to one.) If extra is not Z_NULL, then extra_max - contains the maximum number of bytes to write to extra. Once done is true, - extra_len contains the actual extra field length, and extra contains the - extra field, or that field truncated if extra_max is less than extra_len. - If name is not Z_NULL, then up to name_max characters are written there, - terminated with a zero unless the length is greater than name_max. If - comment is not Z_NULL, then up to comm_max characters are written there, - terminated with a zero unless the length is greater than comm_max. When any - of extra, name, or comment are not Z_NULL and the respective field is not - present in the header, then that field is set to Z_NULL to signal its - absence. This allows the use of deflateSetHeader() with the returned - structure to duplicate the header. However if those fields are set to - allocated memory, then the application will need to save those pointers - elsewhere so that they can be eventually freed. - - If inflateGetHeader is not used, then the header information is simply - discarded. The header is always checked for validity, including the header - CRC if present. inflateReset() will reset the process to discard the header - information. The application would need to call inflateGetHeader() again to - retrieve the header from the next gzip stream. - - inflateGetHeader returns Z_OK if success, or Z_STREAM_ERROR if the source - stream state was inconsistent. -*/ - -/* -ZEXTERN int ZEXPORT inflateBackInit(z_streamp strm, int windowBits, - unsigned char FAR *window); - - Initialize the internal stream state for decompression using inflateBack() - calls. The fields zalloc, zfree and opaque in strm must be initialized - before the call. If zalloc and zfree are Z_NULL, then the default library- - derived memory allocation routines are used. windowBits is the base two - logarithm of the window size, in the range 8..15. window is a caller - supplied buffer of that size. Except for special applications where it is - assured that deflate was used with small window sizes, windowBits must be 15 - and a 32K byte window must be supplied to be able to decompress general - deflate streams. - - See inflateBack() for the usage of these routines. - - inflateBackInit will return Z_OK on success, Z_STREAM_ERROR if any of - the parameters are invalid, Z_MEM_ERROR if the internal state could not be - allocated, or Z_VERSION_ERROR if the version of the library does not match - the version of the header file. -*/ - -typedef unsigned (*in_func)(void FAR *, - z_const unsigned char FAR * FAR *); -typedef int (*out_func)(void FAR *, unsigned char FAR *, unsigned); - -ZEXTERN int ZEXPORT inflateBack(z_streamp strm, - in_func in, void FAR *in_desc, - out_func out, void FAR *out_desc); -/* - inflateBack() does a raw inflate with a single call using a call-back - interface for input and output. This is potentially more efficient than - inflate() for file i/o applications, in that it avoids copying between the - output and the sliding window by simply making the window itself the output - buffer. inflate() can be faster on modern CPUs when used with large - buffers. inflateBack() trusts the application to not change the output - buffer passed by the output function, at least until inflateBack() returns. - - inflateBackInit() must be called first to allocate the internal state - and to initialize the state with the user-provided window buffer. - inflateBack() may then be used multiple times to inflate a complete, raw - deflate stream with each call. inflateBackEnd() is then called to free the - allocated state. - - A raw deflate stream is one with no zlib or gzip header or trailer. - This routine would normally be used in a utility that reads zip or gzip - files and writes out uncompressed files. The utility would decode the - header and process the trailer on its own, hence this routine expects only - the raw deflate stream to decompress. This is different from the default - behavior of inflate(), which expects a zlib header and trailer around the - deflate stream. - - inflateBack() uses two subroutines supplied by the caller that are then - called by inflateBack() for input and output. inflateBack() calls those - routines until it reads a complete deflate stream and writes out all of the - uncompressed data, or until it encounters an error. The function's - parameters and return types are defined above in the in_func and out_func - typedefs. inflateBack() will call in(in_desc, &buf) which should return the - number of bytes of provided input, and a pointer to that input in buf. If - there is no input available, in() must return zero -- buf is ignored in that - case -- and inflateBack() will return a buffer error. inflateBack() will - call out(out_desc, buf, len) to write the uncompressed data buf[0..len-1]. - out() should return zero on success, or non-zero on failure. If out() - returns non-zero, inflateBack() will return with an error. Neither in() nor - out() are permitted to change the contents of the window provided to - inflateBackInit(), which is also the buffer that out() uses to write from. - The length written by out() will be at most the window size. Any non-zero - amount of input may be provided by in(). - - For convenience, inflateBack() can be provided input on the first call by - setting strm->next_in and strm->avail_in. If that input is exhausted, then - in() will be called. Therefore strm->next_in must be initialized before - calling inflateBack(). If strm->next_in is Z_NULL, then in() will be called - immediately for input. If strm->next_in is not Z_NULL, then strm->avail_in - must also be initialized, and then if strm->avail_in is not zero, input will - initially be taken from strm->next_in[0 .. strm->avail_in - 1]. - - The in_desc and out_desc parameters of inflateBack() is passed as the - first parameter of in() and out() respectively when they are called. These - descriptors can be optionally used to pass any information that the caller- - supplied in() and out() functions need to do their job. - - On return, inflateBack() will set strm->next_in and strm->avail_in to - pass back any unused input that was provided by the last in() call. The - return values of inflateBack() can be Z_STREAM_END on success, Z_BUF_ERROR - if in() or out() returned an error, Z_DATA_ERROR if there was a format error - in the deflate stream (in which case strm->msg is set to indicate the nature - of the error), or Z_STREAM_ERROR if the stream was not properly initialized. - In the case of Z_BUF_ERROR, an input or output error can be distinguished - using strm->next_in which will be Z_NULL only if in() returned an error. If - strm->next_in is not Z_NULL, then the Z_BUF_ERROR was due to out() returning - non-zero. (in() will always be called before out(), so strm->next_in is - assured to be defined if out() returns non-zero.) Note that inflateBack() - cannot return Z_OK. -*/ - -ZEXTERN int ZEXPORT inflateBackEnd(z_streamp strm); -/* - All memory allocated by inflateBackInit() is freed. - - inflateBackEnd() returns Z_OK on success, or Z_STREAM_ERROR if the stream - state was inconsistent. -*/ - -ZEXTERN uLong ZEXPORT zlibCompileFlags(void); -/* Return flags indicating compile-time options. - - Type sizes, two bits each, 00 = 16 bits, 01 = 32, 10 = 64, 11 = other: - 1.0: size of uInt - 3.2: size of uLong - 5.4: size of voidpf (pointer) - 7.6: size of z_off_t - - Compiler, assembler, and debug options: - 8: ZLIB_DEBUG - 9: ASMV or ASMINF -- use ASM code - 10: ZLIB_WINAPI -- exported functions use the WINAPI calling convention - 11: 0 (reserved) - - One-time table building (smaller code, but not thread-safe if true): - 12: BUILDFIXED -- build static block decoding tables when needed - 13: DYNAMIC_CRC_TABLE -- build CRC calculation tables when needed - 14,15: 0 (reserved) - - Library content (indicates missing functionality): - 16: NO_GZCOMPRESS -- gz* functions cannot compress (to avoid linking - deflate code when not needed) - 17: NO_GZIP -- deflate can't write gzip streams, and inflate can't detect - and decode gzip streams (to avoid linking crc code) - 18-19: 0 (reserved) - - Operation variations (changes in library functionality): - 20: PKZIP_BUG_WORKAROUND -- slightly more permissive inflate - 21: FASTEST -- deflate algorithm with only one, lowest compression level - 22,23: 0 (reserved) - - The sprintf variant used by gzprintf (zero is best): - 24: 0 = vs*, 1 = s* -- 1 means limited to 20 arguments after the format - 25: 0 = *nprintf, 1 = *printf -- 1 means gzprintf() not secure! - 26: 0 = returns value, 1 = void -- 1 means inferred string length returned - - Remainder: - 27-31: 0 (reserved) - */ - -#ifndef Z_SOLO - - /* utility functions */ - -/* - The following utility functions are implemented on top of the basic - stream-oriented functions. To simplify the interface, some default options - are assumed (compression level and memory usage, standard memory allocation - functions). The source code of these utility functions can be modified if - you need special options. -*/ - -ZEXTERN int ZEXPORT compress(Bytef *dest, uLongf *destLen, - const Bytef *source, uLong sourceLen); -/* - Compresses the source buffer into the destination buffer. sourceLen is - the byte length of the source buffer. Upon entry, destLen is the total size - of the destination buffer, which must be at least the value returned by - compressBound(sourceLen). Upon exit, destLen is the actual size of the - compressed data. compress() is equivalent to compress2() with a level - parameter of Z_DEFAULT_COMPRESSION. - - compress returns Z_OK if success, Z_MEM_ERROR if there was not - enough memory, Z_BUF_ERROR if there was not enough room in the output - buffer. -*/ - -ZEXTERN int ZEXPORT compress2(Bytef *dest, uLongf *destLen, - const Bytef *source, uLong sourceLen, - int level); -/* - Compresses the source buffer into the destination buffer. The level - parameter has the same meaning as in deflateInit. sourceLen is the byte - length of the source buffer. Upon entry, destLen is the total size of the - destination buffer, which must be at least the value returned by - compressBound(sourceLen). Upon exit, destLen is the actual size of the - compressed data. - - compress2 returns Z_OK if success, Z_MEM_ERROR if there was not enough - memory, Z_BUF_ERROR if there was not enough room in the output buffer, - Z_STREAM_ERROR if the level parameter is invalid. -*/ - -ZEXTERN uLong ZEXPORT compressBound(uLong sourceLen); -/* - compressBound() returns an upper bound on the compressed size after - compress() or compress2() on sourceLen bytes. It would be used before a - compress() or compress2() call to allocate the destination buffer. -*/ - -ZEXTERN int ZEXPORT uncompress(Bytef *dest, uLongf *destLen, - const Bytef *source, uLong sourceLen); -/* - Decompresses the source buffer into the destination buffer. sourceLen is - the byte length of the source buffer. Upon entry, destLen is the total size - of the destination buffer, which must be large enough to hold the entire - uncompressed data. (The size of the uncompressed data must have been saved - previously by the compressor and transmitted to the decompressor by some - mechanism outside the scope of this compression library.) Upon exit, destLen - is the actual size of the uncompressed data. - - uncompress returns Z_OK if success, Z_MEM_ERROR if there was not - enough memory, Z_BUF_ERROR if there was not enough room in the output - buffer, or Z_DATA_ERROR if the input data was corrupted or incomplete. In - the case where there is not enough room, uncompress() will fill the output - buffer with the uncompressed data up to that point. -*/ - -ZEXTERN int ZEXPORT uncompress2(Bytef *dest, uLongf *destLen, - const Bytef *source, uLong *sourceLen); -/* - Same as uncompress, except that sourceLen is a pointer, where the - length of the source is *sourceLen. On return, *sourceLen is the number of - source bytes consumed. -*/ - - /* gzip file access functions */ - -/* - This library supports reading and writing files in gzip (.gz) format with - an interface similar to that of stdio, using the functions that start with - "gz". The gzip format is different from the zlib format. gzip is a gzip - wrapper, documented in RFC 1952, wrapped around a deflate stream. -*/ - -typedef struct gzFile_s *gzFile; /* semi-opaque gzip file descriptor */ - -/* -ZEXTERN gzFile ZEXPORT gzopen(const char *path, const char *mode); - - Open the gzip (.gz) file at path for reading and decompressing, or - compressing and writing. The mode parameter is as in fopen ("rb" or "wb") - but can also include a compression level ("wb9") or a strategy: 'f' for - filtered data as in "wb6f", 'h' for Huffman-only compression as in "wb1h", - 'R' for run-length encoding as in "wb1R", or 'F' for fixed code compression - as in "wb9F". (See the description of deflateInit2 for more information - about the strategy parameter.) 'T' will request transparent writing or - appending with no compression and not using the gzip format. - - "a" can be used instead of "w" to request that the gzip stream that will - be written be appended to the file. "+" will result in an error, since - reading and writing to the same gzip file is not supported. The addition of - "x" when writing will create the file exclusively, which fails if the file - already exists. On systems that support it, the addition of "e" when - reading or writing will set the flag to close the file on an execve() call. - - These functions, as well as gzip, will read and decode a sequence of gzip - streams in a file. The append function of gzopen() can be used to create - such a file. (Also see gzflush() for another way to do this.) When - appending, gzopen does not test whether the file begins with a gzip stream, - nor does it look for the end of the gzip streams to begin appending. gzopen - will simply append a gzip stream to the existing file. - - gzopen can be used to read a file which is not in gzip format; in this - case gzread will directly read from the file without decompression. When - reading, this will be detected automatically by looking for the magic two- - byte gzip header. - - gzopen returns NULL if the file could not be opened, if there was - insufficient memory to allocate the gzFile state, or if an invalid mode was - specified (an 'r', 'w', or 'a' was not provided, or '+' was provided). - errno can be checked to determine if the reason gzopen failed was that the - file could not be opened. -*/ - -ZEXTERN gzFile ZEXPORT gzdopen(int fd, const char *mode); -/* - Associate a gzFile with the file descriptor fd. File descriptors are - obtained from calls like open, dup, creat, pipe or fileno (if the file has - been previously opened with fopen). The mode parameter is as in gzopen. - - The next call of gzclose on the returned gzFile will also close the file - descriptor fd, just like fclose(fdopen(fd, mode)) closes the file descriptor - fd. If you want to keep fd open, use fd = dup(fd_keep); gz = gzdopen(fd, - mode);. The duplicated descriptor should be saved to avoid a leak, since - gzdopen does not close fd if it fails. If you are using fileno() to get the - file descriptor from a FILE *, then you will have to use dup() to avoid - double-close()ing the file descriptor. Both gzclose() and fclose() will - close the associated file descriptor, so they need to have different file - descriptors. - - gzdopen returns NULL if there was insufficient memory to allocate the - gzFile state, if an invalid mode was specified (an 'r', 'w', or 'a' was not - provided, or '+' was provided), or if fd is -1. The file descriptor is not - used until the next gz* read, write, seek, or close operation, so gzdopen - will not detect if fd is invalid (unless fd is -1). -*/ - -ZEXTERN int ZEXPORT gzbuffer(gzFile file, unsigned size); -/* - Set the internal buffer size used by this library's functions for file to - size. The default buffer size is 8192 bytes. This function must be called - after gzopen() or gzdopen(), and before any other calls that read or write - the file. The buffer memory allocation is always deferred to the first read - or write. Three times that size in buffer space is allocated. A larger - buffer size of, for example, 64K or 128K bytes will noticeably increase the - speed of decompression (reading). - - The new buffer size also affects the maximum length for gzprintf(). - - gzbuffer() returns 0 on success, or -1 on failure, such as being called - too late. -*/ - -ZEXTERN int ZEXPORT gzsetparams(gzFile file, int level, int strategy); -/* - Dynamically update the compression level and strategy for file. See the - description of deflateInit2 for the meaning of these parameters. Previously - provided data is flushed before applying the parameter changes. - - gzsetparams returns Z_OK if success, Z_STREAM_ERROR if the file was not - opened for writing, Z_ERRNO if there is an error writing the flushed data, - or Z_MEM_ERROR if there is a memory allocation error. -*/ - -ZEXTERN int ZEXPORT gzread(gzFile file, voidp buf, unsigned len); -/* - Read and decompress up to len uncompressed bytes from file into buf. If - the input file is not in gzip format, gzread copies the given number of - bytes into the buffer directly from the file. - - After reaching the end of a gzip stream in the input, gzread will continue - to read, looking for another gzip stream. Any number of gzip streams may be - concatenated in the input file, and will all be decompressed by gzread(). - If something other than a gzip stream is encountered after a gzip stream, - that remaining trailing garbage is ignored (and no error is returned). - - gzread can be used to read a gzip file that is being concurrently written. - Upon reaching the end of the input, gzread will return with the available - data. If the error code returned by gzerror is Z_OK or Z_BUF_ERROR, then - gzclearerr can be used to clear the end of file indicator in order to permit - gzread to be tried again. Z_OK indicates that a gzip stream was completed - on the last gzread. Z_BUF_ERROR indicates that the input file ended in the - middle of a gzip stream. Note that gzread does not return -1 in the event - of an incomplete gzip stream. This error is deferred until gzclose(), which - will return Z_BUF_ERROR if the last gzread ended in the middle of a gzip - stream. Alternatively, gzerror can be used before gzclose to detect this - case. - - gzread returns the number of uncompressed bytes actually read, less than - len for end of file, or -1 for error. If len is too large to fit in an int, - then nothing is read, -1 is returned, and the error state is set to - Z_STREAM_ERROR. -*/ - -ZEXTERN z_size_t ZEXPORT gzfread(voidp buf, z_size_t size, z_size_t nitems, - gzFile file); -/* - Read and decompress up to nitems items of size size from file into buf, - otherwise operating as gzread() does. This duplicates the interface of - stdio's fread(), with size_t request and return types. If the library - defines size_t, then z_size_t is identical to size_t. If not, then z_size_t - is an unsigned integer type that can contain a pointer. - - gzfread() returns the number of full items read of size size, or zero if - the end of the file was reached and a full item could not be read, or if - there was an error. gzerror() must be consulted if zero is returned in - order to determine if there was an error. If the multiplication of size and - nitems overflows, i.e. the product does not fit in a z_size_t, then nothing - is read, zero is returned, and the error state is set to Z_STREAM_ERROR. - - In the event that the end of file is reached and only a partial item is - available at the end, i.e. the remaining uncompressed data length is not a - multiple of size, then the final partial item is nevertheless read into buf - and the end-of-file flag is set. The length of the partial item read is not - provided, but could be inferred from the result of gztell(). This behavior - is the same as the behavior of fread() implementations in common libraries, - but it prevents the direct use of gzfread() to read a concurrently written - file, resetting and retrying on end-of-file, when size is not 1. -*/ - -ZEXTERN int ZEXPORT gzwrite(gzFile file, voidpc buf, unsigned len); -/* - Compress and write the len uncompressed bytes at buf to file. gzwrite - returns the number of uncompressed bytes written or 0 in case of error. -*/ - -ZEXTERN z_size_t ZEXPORT gzfwrite(voidpc buf, z_size_t size, - z_size_t nitems, gzFile file); -/* - Compress and write nitems items of size size from buf to file, duplicating - the interface of stdio's fwrite(), with size_t request and return types. If - the library defines size_t, then z_size_t is identical to size_t. If not, - then z_size_t is an unsigned integer type that can contain a pointer. - - gzfwrite() returns the number of full items written of size size, or zero - if there was an error. If the multiplication of size and nitems overflows, - i.e. the product does not fit in a z_size_t, then nothing is written, zero - is returned, and the error state is set to Z_STREAM_ERROR. -*/ - -ZEXTERN int ZEXPORTVA gzprintf(gzFile file, const char *format, ...); -/* - Convert, format, compress, and write the arguments (...) to file under - control of the string format, as in fprintf. gzprintf returns the number of - uncompressed bytes actually written, or a negative zlib error code in case - of error. The number of uncompressed bytes written is limited to 8191, or - one less than the buffer size given to gzbuffer(). The caller should assure - that this limit is not exceeded. If it is exceeded, then gzprintf() will - return an error (0) with nothing written. In this case, there may also be a - buffer overflow with unpredictable consequences, which is possible only if - zlib was compiled with the insecure functions sprintf() or vsprintf(), - because the secure snprintf() or vsnprintf() functions were not available. - This can be determined using zlibCompileFlags(). -*/ - -ZEXTERN int ZEXPORT gzputs(gzFile file, const char *s); -/* - Compress and write the given null-terminated string s to file, excluding - the terminating null character. - - gzputs returns the number of characters written, or -1 in case of error. -*/ - -ZEXTERN char * ZEXPORT gzgets(gzFile file, char *buf, int len); -/* - Read and decompress bytes from file into buf, until len-1 characters are - read, or until a newline character is read and transferred to buf, or an - end-of-file condition is encountered. If any characters are read or if len - is one, the string is terminated with a null character. If no characters - are read due to an end-of-file or len is less than one, then the buffer is - left untouched. - - gzgets returns buf which is a null-terminated string, or it returns NULL - for end-of-file or in case of error. If there was an error, the contents at - buf are indeterminate. -*/ - -ZEXTERN int ZEXPORT gzputc(gzFile file, int c); -/* - Compress and write c, converted to an unsigned char, into file. gzputc - returns the value that was written, or -1 in case of error. -*/ - -ZEXTERN int ZEXPORT gzgetc(gzFile file); -/* - Read and decompress one byte from file. gzgetc returns this byte or -1 - in case of end of file or error. This is implemented as a macro for speed. - As such, it does not do all of the checking the other functions do. I.e. - it does not check to see if file is NULL, nor whether the structure file - points to has been clobbered or not. -*/ - -ZEXTERN int ZEXPORT gzungetc(int c, gzFile file); -/* - Push c back onto the stream for file to be read as the first character on - the next read. At least one character of push-back is always allowed. - gzungetc() returns the character pushed, or -1 on failure. gzungetc() will - fail if c is -1, and may fail if a character has been pushed but not read - yet. If gzungetc is used immediately after gzopen or gzdopen, at least the - output buffer size of pushed characters is allowed. (See gzbuffer above.) - The pushed character will be discarded if the stream is repositioned with - gzseek() or gzrewind(). -*/ - -ZEXTERN int ZEXPORT gzflush(gzFile file, int flush); -/* - Flush all pending output to file. The parameter flush is as in the - deflate() function. The return value is the zlib error number (see function - gzerror below). gzflush is only permitted when writing. - - If the flush parameter is Z_FINISH, the remaining data is written and the - gzip stream is completed in the output. If gzwrite() is called again, a new - gzip stream will be started in the output. gzread() is able to read such - concatenated gzip streams. - - gzflush should be called only when strictly necessary because it will - degrade compression if called too often. -*/ - -/* -ZEXTERN z_off_t ZEXPORT gzseek(gzFile file, - z_off_t offset, int whence); - - Set the starting position to offset relative to whence for the next gzread - or gzwrite on file. The offset represents a number of bytes in the - uncompressed data stream. The whence parameter is defined as in lseek(2); - the value SEEK_END is not supported. - - If the file is opened for reading, this function is emulated but can be - extremely slow. If the file is opened for writing, only forward seeks are - supported; gzseek then compresses a sequence of zeroes up to the new - starting position. - - gzseek returns the resulting offset location as measured in bytes from - the beginning of the uncompressed stream, or -1 in case of error, in - particular if the file is opened for writing and the new starting position - would be before the current position. -*/ - -ZEXTERN int ZEXPORT gzrewind(gzFile file); -/* - Rewind file. This function is supported only for reading. - - gzrewind(file) is equivalent to (int)gzseek(file, 0L, SEEK_SET). -*/ - -/* -ZEXTERN z_off_t ZEXPORT gztell(gzFile file); - - Return the starting position for the next gzread or gzwrite on file. - This position represents a number of bytes in the uncompressed data stream, - and is zero when starting, even if appending or reading a gzip stream from - the middle of a file using gzdopen(). - - gztell(file) is equivalent to gzseek(file, 0L, SEEK_CUR) -*/ - -/* -ZEXTERN z_off_t ZEXPORT gzoffset(gzFile file); - - Return the current compressed (actual) read or write offset of file. This - offset includes the count of bytes that precede the gzip stream, for example - when appending or when using gzdopen() for reading. When reading, the - offset does not include as yet unused buffered input. This information can - be used for a progress indicator. On error, gzoffset() returns -1. -*/ - -ZEXTERN int ZEXPORT gzeof(gzFile file); -/* - Return true (1) if the end-of-file indicator for file has been set while - reading, false (0) otherwise. Note that the end-of-file indicator is set - only if the read tried to go past the end of the input, but came up short. - Therefore, just like feof(), gzeof() may return false even if there is no - more data to read, in the event that the last read request was for the exact - number of bytes remaining in the input file. This will happen if the input - file size is an exact multiple of the buffer size. - - If gzeof() returns true, then the read functions will return no more data, - unless the end-of-file indicator is reset by gzclearerr() and the input file - has grown since the previous end of file was detected. -*/ - -ZEXTERN int ZEXPORT gzdirect(gzFile file); -/* - Return true (1) if file is being copied directly while reading, or false - (0) if file is a gzip stream being decompressed. - - If the input file is empty, gzdirect() will return true, since the input - does not contain a gzip stream. - - If gzdirect() is used immediately after gzopen() or gzdopen() it will - cause buffers to be allocated to allow reading the file to determine if it - is a gzip file. Therefore if gzbuffer() is used, it should be called before - gzdirect(). - - When writing, gzdirect() returns true (1) if transparent writing was - requested ("wT" for the gzopen() mode), or false (0) otherwise. (Note: - gzdirect() is not needed when writing. Transparent writing must be - explicitly requested, so the application already knows the answer. When - linking statically, using gzdirect() will include all of the zlib code for - gzip file reading and decompression, which may not be desired.) -*/ - -ZEXTERN int ZEXPORT gzclose(gzFile file); -/* - Flush all pending output for file, if necessary, close file and - deallocate the (de)compression state. Note that once file is closed, you - cannot call gzerror with file, since its structures have been deallocated. - gzclose must not be called more than once on the same file, just as free - must not be called more than once on the same allocation. - - gzclose will return Z_STREAM_ERROR if file is not valid, Z_ERRNO on a - file operation error, Z_MEM_ERROR if out of memory, Z_BUF_ERROR if the - last read ended in the middle of a gzip stream, or Z_OK on success. -*/ - -ZEXTERN int ZEXPORT gzclose_r(gzFile file); -ZEXTERN int ZEXPORT gzclose_w(gzFile file); -/* - Same as gzclose(), but gzclose_r() is only for use when reading, and - gzclose_w() is only for use when writing or appending. The advantage to - using these instead of gzclose() is that they avoid linking in zlib - compression or decompression code that is not used when only reading or only - writing respectively. If gzclose() is used, then both compression and - decompression code will be included the application when linking to a static - zlib library. -*/ - -ZEXTERN const char * ZEXPORT gzerror(gzFile file, int *errnum); -/* - Return the error message for the last error which occurred on file. - errnum is set to zlib error number. If an error occurred in the file system - and not in the compression library, errnum is set to Z_ERRNO and the - application may consult errno to get the exact error code. - - The application must not modify the returned string. Future calls to - this function may invalidate the previously returned string. If file is - closed, then the string previously returned by gzerror will no longer be - available. - - gzerror() should be used to distinguish errors from end-of-file for those - functions above that do not distinguish those cases in their return values. -*/ - -ZEXTERN void ZEXPORT gzclearerr(gzFile file); -/* - Clear the error and end-of-file flags for file. This is analogous to the - clearerr() function in stdio. This is useful for continuing to read a gzip - file that is being written concurrently. -*/ - -#endif /* !Z_SOLO */ - - /* checksum functions */ - -/* - These functions are not related to compression but are exported - anyway because they might be useful in applications using the compression - library. -*/ - -ZEXTERN uLong ZEXPORT adler32(uLong adler, const Bytef *buf, uInt len); -/* - Update a running Adler-32 checksum with the bytes buf[0..len-1] and - return the updated checksum. An Adler-32 value is in the range of a 32-bit - unsigned integer. If buf is Z_NULL, this function returns the required - initial value for the checksum. - - An Adler-32 checksum is almost as reliable as a CRC-32 but can be computed - much faster. - - Usage example: - - uLong adler = adler32(0L, Z_NULL, 0); - - while (read_buffer(buffer, length) != EOF) { - adler = adler32(adler, buffer, length); - } - if (adler != original_adler) error(); -*/ - -ZEXTERN uLong ZEXPORT adler32_z(uLong adler, const Bytef *buf, - z_size_t len); -/* - Same as adler32(), but with a size_t length. -*/ - -/* -ZEXTERN uLong ZEXPORT adler32_combine(uLong adler1, uLong adler2, - z_off_t len2); - - Combine two Adler-32 checksums into one. For two sequences of bytes, seq1 - and seq2 with lengths len1 and len2, Adler-32 checksums were calculated for - each, adler1 and adler2. adler32_combine() returns the Adler-32 checksum of - seq1 and seq2 concatenated, requiring only adler1, adler2, and len2. Note - that the z_off_t type (like off_t) is a signed integer. If len2 is - negative, the result has no meaning or utility. -*/ - -ZEXTERN uLong ZEXPORT crc32(uLong crc, const Bytef *buf, uInt len); -/* - Update a running CRC-32 with the bytes buf[0..len-1] and return the - updated CRC-32. A CRC-32 value is in the range of a 32-bit unsigned integer. - If buf is Z_NULL, this function returns the required initial value for the - crc. Pre- and post-conditioning (one's complement) is performed within this - function so it shouldn't be done by the application. - - Usage example: - - uLong crc = crc32(0L, Z_NULL, 0); - - while (read_buffer(buffer, length) != EOF) { - crc = crc32(crc, buffer, length); - } - if (crc != original_crc) error(); -*/ - -ZEXTERN uLong ZEXPORT crc32_z(uLong crc, const Bytef *buf, - z_size_t len); -/* - Same as crc32(), but with a size_t length. -*/ - -/* -ZEXTERN uLong ZEXPORT crc32_combine(uLong crc1, uLong crc2, z_off_t len2); - - Combine two CRC-32 check values into one. For two sequences of bytes, - seq1 and seq2 with lengths len1 and len2, CRC-32 check values were - calculated for each, crc1 and crc2. crc32_combine() returns the CRC-32 - check value of seq1 and seq2 concatenated, requiring only crc1, crc2, and - len2. len2 must be non-negative. -*/ - -/* -ZEXTERN uLong ZEXPORT crc32_combine_gen(z_off_t len2); - - Return the operator corresponding to length len2, to be used with - crc32_combine_op(). len2 must be non-negative. -*/ - -ZEXTERN uLong ZEXPORT crc32_combine_op(uLong crc1, uLong crc2, uLong op); -/* - Give the same result as crc32_combine(), using op in place of len2. op is - is generated from len2 by crc32_combine_gen(). This will be faster than - crc32_combine() if the generated op is used more than once. -*/ - - - /* various hacks, don't look :) */ - -/* deflateInit and inflateInit are macros to allow checking the zlib version - * and the compiler's view of z_stream: - */ -ZEXTERN int ZEXPORT deflateInit_(z_streamp strm, int level, - const char *version, int stream_size); -ZEXTERN int ZEXPORT inflateInit_(z_streamp strm, - const char *version, int stream_size); -ZEXTERN int ZEXPORT deflateInit2_(z_streamp strm, int level, int method, - int windowBits, int memLevel, - int strategy, const char *version, - int stream_size); -ZEXTERN int ZEXPORT inflateInit2_(z_streamp strm, int windowBits, - const char *version, int stream_size); -ZEXTERN int ZEXPORT inflateBackInit_(z_streamp strm, int windowBits, - unsigned char FAR *window, - const char *version, - int stream_size); -#ifdef Z_PREFIX_SET -# define z_deflateInit(strm, level) \ - deflateInit_((strm), (level), ZLIB_VERSION, (int)sizeof(z_stream)) -# define z_inflateInit(strm) \ - inflateInit_((strm), ZLIB_VERSION, (int)sizeof(z_stream)) -# define z_deflateInit2(strm, level, method, windowBits, memLevel, strategy) \ - deflateInit2_((strm),(level),(method),(windowBits),(memLevel),\ - (strategy), ZLIB_VERSION, (int)sizeof(z_stream)) -# define z_inflateInit2(strm, windowBits) \ - inflateInit2_((strm), (windowBits), ZLIB_VERSION, \ - (int)sizeof(z_stream)) -# define z_inflateBackInit(strm, windowBits, window) \ - inflateBackInit_((strm), (windowBits), (window), \ - ZLIB_VERSION, (int)sizeof(z_stream)) -#else -# define deflateInit(strm, level) \ - deflateInit_((strm), (level), ZLIB_VERSION, (int)sizeof(z_stream)) -# define inflateInit(strm) \ - inflateInit_((strm), ZLIB_VERSION, (int)sizeof(z_stream)) -# define deflateInit2(strm, level, method, windowBits, memLevel, strategy) \ - deflateInit2_((strm),(level),(method),(windowBits),(memLevel),\ - (strategy), ZLIB_VERSION, (int)sizeof(z_stream)) -# define inflateInit2(strm, windowBits) \ - inflateInit2_((strm), (windowBits), ZLIB_VERSION, \ - (int)sizeof(z_stream)) -# define inflateBackInit(strm, windowBits, window) \ - inflateBackInit_((strm), (windowBits), (window), \ - ZLIB_VERSION, (int)sizeof(z_stream)) -#endif - -#ifndef Z_SOLO - -/* gzgetc() macro and its supporting function and exposed data structure. Note - * that the real internal state is much larger than the exposed structure. - * This abbreviated structure exposes just enough for the gzgetc() macro. The - * user should not mess with these exposed elements, since their names or - * behavior could change in the future, perhaps even capriciously. They can - * only be used by the gzgetc() macro. You have been warned. - */ -struct gzFile_s { - unsigned have; - unsigned char *next; - z_off64_t pos; -}; -ZEXTERN int ZEXPORT gzgetc_(gzFile file); /* backward compatibility */ -#ifdef Z_PREFIX_SET -# undef z_gzgetc -# define z_gzgetc(g) \ - ((g)->have ? ((g)->have--, (g)->pos++, *((g)->next)++) : (gzgetc)(g)) -#else -# define gzgetc(g) \ - ((g)->have ? ((g)->have--, (g)->pos++, *((g)->next)++) : (gzgetc)(g)) -#endif - -/* provide 64-bit offset functions if _LARGEFILE64_SOURCE defined, and/or - * change the regular functions to 64 bits if _FILE_OFFSET_BITS is 64 (if - * both are true, the application gets the *64 functions, and the regular - * functions are changed to 64 bits) -- in case these are set on systems - * without large file support, _LFS64_LARGEFILE must also be true - */ -#ifdef Z_LARGE64 - ZEXTERN gzFile ZEXPORT gzopen64(const char *, const char *); - ZEXTERN z_off64_t ZEXPORT gzseek64(gzFile, z_off64_t, int); - ZEXTERN z_off64_t ZEXPORT gztell64(gzFile); - ZEXTERN z_off64_t ZEXPORT gzoffset64(gzFile); - ZEXTERN uLong ZEXPORT adler32_combine64(uLong, uLong, z_off64_t); - ZEXTERN uLong ZEXPORT crc32_combine64(uLong, uLong, z_off64_t); - ZEXTERN uLong ZEXPORT crc32_combine_gen64(z_off64_t); -#endif - -#if !defined(ZLIB_INTERNAL) && defined(Z_WANT64) -# ifdef Z_PREFIX_SET -# define z_gzopen z_gzopen64 -# define z_gzseek z_gzseek64 -# define z_gztell z_gztell64 -# define z_gzoffset z_gzoffset64 -# define z_adler32_combine z_adler32_combine64 -# define z_crc32_combine z_crc32_combine64 -# define z_crc32_combine_gen z_crc32_combine_gen64 -# else -# define gzopen gzopen64 -# define gzseek gzseek64 -# define gztell gztell64 -# define gzoffset gzoffset64 -# define adler32_combine adler32_combine64 -# define crc32_combine crc32_combine64 -# define crc32_combine_gen crc32_combine_gen64 -# endif -# ifndef Z_LARGE64 - ZEXTERN gzFile ZEXPORT gzopen64(const char *, const char *); - ZEXTERN z_off_t ZEXPORT gzseek64(gzFile, z_off_t, int); - ZEXTERN z_off_t ZEXPORT gztell64(gzFile); - ZEXTERN z_off_t ZEXPORT gzoffset64(gzFile); - ZEXTERN uLong ZEXPORT adler32_combine64(uLong, uLong, z_off_t); - ZEXTERN uLong ZEXPORT crc32_combine64(uLong, uLong, z_off_t); - ZEXTERN uLong ZEXPORT crc32_combine_gen64(z_off_t); -# endif -#else - ZEXTERN gzFile ZEXPORT gzopen(const char *, const char *); - ZEXTERN z_off_t ZEXPORT gzseek(gzFile, z_off_t, int); - ZEXTERN z_off_t ZEXPORT gztell(gzFile); - ZEXTERN z_off_t ZEXPORT gzoffset(gzFile); - ZEXTERN uLong ZEXPORT adler32_combine(uLong, uLong, z_off_t); - ZEXTERN uLong ZEXPORT crc32_combine(uLong, uLong, z_off_t); - ZEXTERN uLong ZEXPORT crc32_combine_gen(z_off_t); -#endif - -#else /* Z_SOLO */ - - ZEXTERN uLong ZEXPORT adler32_combine(uLong, uLong, z_off_t); - ZEXTERN uLong ZEXPORT crc32_combine(uLong, uLong, z_off_t); - ZEXTERN uLong ZEXPORT crc32_combine_gen(z_off_t); - -#endif /* !Z_SOLO */ - -/* undocumented functions */ -ZEXTERN const char * ZEXPORT zError(int); -ZEXTERN int ZEXPORT inflateSyncPoint(z_streamp); -ZEXTERN const z_crc_t FAR * ZEXPORT get_crc_table(void); -ZEXTERN int ZEXPORT inflateUndermine(z_streamp, int); -ZEXTERN int ZEXPORT inflateValidate(z_streamp, int); -ZEXTERN unsigned long ZEXPORT inflateCodesUsed(z_streamp); -ZEXTERN int ZEXPORT inflateResetKeep(z_streamp); -ZEXTERN int ZEXPORT deflateResetKeep(z_streamp); -#if defined(_WIN32) && !defined(Z_SOLO) -ZEXTERN gzFile ZEXPORT gzopen_w(const wchar_t *path, - const char *mode); -#endif -#if defined(STDC) || defined(Z_HAVE_STDARG_H) -# ifndef Z_SOLO -ZEXTERN int ZEXPORTVA gzvprintf(gzFile file, - const char *format, - va_list va); -# endif -#endif - -#ifdef __cplusplus -} -#endif - -#endif /* ZLIB_H */ diff --git a/phobos/etc/c/zlib/zutil.c b/phobos/etc/c/zlib/zutil.c deleted file mode 100644 index b1c5d2d..0000000 --- a/phobos/etc/c/zlib/zutil.c +++ /dev/null @@ -1,299 +0,0 @@ -/* zutil.c -- target dependent utility functions for the compression library - * Copyright (C) 1995-2017 Jean-loup Gailly - * For conditions of distribution and use, see copyright notice in zlib.h - */ - -/* @(#) $Id$ */ - -#include "zutil.h" -#ifndef Z_SOLO -# include "gzguts.h" -#endif - -z_const char * const z_errmsg[10] = { - (z_const char *)"need dictionary", /* Z_NEED_DICT 2 */ - (z_const char *)"stream end", /* Z_STREAM_END 1 */ - (z_const char *)"", /* Z_OK 0 */ - (z_const char *)"file error", /* Z_ERRNO (-1) */ - (z_const char *)"stream error", /* Z_STREAM_ERROR (-2) */ - (z_const char *)"data error", /* Z_DATA_ERROR (-3) */ - (z_const char *)"insufficient memory", /* Z_MEM_ERROR (-4) */ - (z_const char *)"buffer error", /* Z_BUF_ERROR (-5) */ - (z_const char *)"incompatible version",/* Z_VERSION_ERROR (-6) */ - (z_const char *)"" -}; - - -const char * ZEXPORT zlibVersion(void) { - return ZLIB_VERSION; -} - -uLong ZEXPORT zlibCompileFlags(void) { - uLong flags; - - flags = 0; - switch ((int)(sizeof(uInt))) { - case 2: break; - case 4: flags += 1; break; - case 8: flags += 2; break; - default: flags += 3; - } - switch ((int)(sizeof(uLong))) { - case 2: break; - case 4: flags += 1 << 2; break; - case 8: flags += 2 << 2; break; - default: flags += 3 << 2; - } - switch ((int)(sizeof(voidpf))) { - case 2: break; - case 4: flags += 1 << 4; break; - case 8: flags += 2 << 4; break; - default: flags += 3 << 4; - } - switch ((int)(sizeof(z_off_t))) { - case 2: break; - case 4: flags += 1 << 6; break; - case 8: flags += 2 << 6; break; - default: flags += 3 << 6; - } -#ifdef ZLIB_DEBUG - flags += 1 << 8; -#endif - /* -#if defined(ASMV) || defined(ASMINF) - flags += 1 << 9; -#endif - */ -#ifdef ZLIB_WINAPI - flags += 1 << 10; -#endif -#ifdef BUILDFIXED - flags += 1 << 12; -#endif -#ifdef DYNAMIC_CRC_TABLE - flags += 1 << 13; -#endif -#ifdef NO_GZCOMPRESS - flags += 1L << 16; -#endif -#ifdef NO_GZIP - flags += 1L << 17; -#endif -#ifdef PKZIP_BUG_WORKAROUND - flags += 1L << 20; -#endif -#ifdef FASTEST - flags += 1L << 21; -#endif -#if defined(STDC) || defined(Z_HAVE_STDARG_H) -# ifdef NO_vsnprintf - flags += 1L << 25; -# ifdef HAS_vsprintf_void - flags += 1L << 26; -# endif -# else -# ifdef HAS_vsnprintf_void - flags += 1L << 26; -# endif -# endif -#else - flags += 1L << 24; -# ifdef NO_snprintf - flags += 1L << 25; -# ifdef HAS_sprintf_void - flags += 1L << 26; -# endif -# else -# ifdef HAS_snprintf_void - flags += 1L << 26; -# endif -# endif -#endif - return flags; -} - -#ifdef ZLIB_DEBUG -#include -# ifndef verbose -# define verbose 0 -# endif -int ZLIB_INTERNAL z_verbose = verbose; - -void ZLIB_INTERNAL z_error(char *m) { - fprintf(stderr, "%s\n", m); - exit(1); -} -#endif - -/* exported to allow conversion of error code to string for compress() and - * uncompress() - */ -const char * ZEXPORT zError(int err) { - return ERR_MSG(err); -} - -#if defined(_WIN32_WCE) && _WIN32_WCE < 0x800 - /* The older Microsoft C Run-Time Library for Windows CE doesn't have - * errno. We define it as a global variable to simplify porting. - * Its value is always 0 and should not be used. - */ - int errno = 0; -#endif - -#ifndef HAVE_MEMCPY - -void ZLIB_INTERNAL zmemcpy(Bytef* dest, const Bytef* source, uInt len) { - if (len == 0) return; - do { - *dest++ = *source++; /* ??? to be unrolled */ - } while (--len != 0); -} - -int ZLIB_INTERNAL zmemcmp(const Bytef* s1, const Bytef* s2, uInt len) { - uInt j; - - for (j = 0; j < len; j++) { - if (s1[j] != s2[j]) return 2*(s1[j] > s2[j])-1; - } - return 0; -} - -void ZLIB_INTERNAL zmemzero(Bytef* dest, uInt len) { - if (len == 0) return; - do { - *dest++ = 0; /* ??? to be unrolled */ - } while (--len != 0); -} -#endif - -#ifndef Z_SOLO - -#ifdef SYS16BIT - -#ifdef __TURBOC__ -/* Turbo C in 16-bit mode */ - -# define MY_ZCALLOC - -/* Turbo C malloc() does not allow dynamic allocation of 64K bytes - * and farmalloc(64K) returns a pointer with an offset of 8, so we - * must fix the pointer. Warning: the pointer must be put back to its - * original form in order to free it, use zcfree(). - */ - -#define MAX_PTR 10 -/* 10*64K = 640K */ - -local int next_ptr = 0; - -typedef struct ptr_table_s { - voidpf org_ptr; - voidpf new_ptr; -} ptr_table; - -local ptr_table table[MAX_PTR]; -/* This table is used to remember the original form of pointers - * to large buffers (64K). Such pointers are normalized with a zero offset. - * Since MSDOS is not a preemptive multitasking OS, this table is not - * protected from concurrent access. This hack doesn't work anyway on - * a protected system like OS/2. Use Microsoft C instead. - */ - -voidpf ZLIB_INTERNAL zcalloc(voidpf opaque, unsigned items, unsigned size) { - voidpf buf; - ulg bsize = (ulg)items*size; - - (void)opaque; - - /* If we allocate less than 65520 bytes, we assume that farmalloc - * will return a usable pointer which doesn't have to be normalized. - */ - if (bsize < 65520L) { - buf = farmalloc(bsize); - if (*(ush*)&buf != 0) return buf; - } else { - buf = farmalloc(bsize + 16L); - } - if (buf == NULL || next_ptr >= MAX_PTR) return NULL; - table[next_ptr].org_ptr = buf; - - /* Normalize the pointer to seg:0 */ - *((ush*)&buf+1) += ((ush)((uch*)buf-0) + 15) >> 4; - *(ush*)&buf = 0; - table[next_ptr++].new_ptr = buf; - return buf; -} - -void ZLIB_INTERNAL zcfree(voidpf opaque, voidpf ptr) { - int n; - - (void)opaque; - - if (*(ush*)&ptr != 0) { /* object < 64K */ - farfree(ptr); - return; - } - /* Find the original pointer */ - for (n = 0; n < next_ptr; n++) { - if (ptr != table[n].new_ptr) continue; - - farfree(table[n].org_ptr); - while (++n < next_ptr) { - table[n-1] = table[n]; - } - next_ptr--; - return; - } - Assert(0, "zcfree: ptr not found"); -} - -#endif /* __TURBOC__ */ - - -#ifdef M_I86 -/* Microsoft C in 16-bit mode */ - -# define MY_ZCALLOC - -#if (!defined(_MSC_VER) || (_MSC_VER <= 600)) -# define _halloc halloc -# define _hfree hfree -#endif - -voidpf ZLIB_INTERNAL zcalloc(voidpf opaque, uInt items, uInt size) { - (void)opaque; - return _halloc((long)items, size); -} - -void ZLIB_INTERNAL zcfree(voidpf opaque, voidpf ptr) { - (void)opaque; - _hfree(ptr); -} - -#endif /* M_I86 */ - -#endif /* SYS16BIT */ - - -#ifndef MY_ZCALLOC /* Any system without a special alloc function */ - -#ifndef STDC -extern voidp malloc(uInt size); -extern voidp calloc(uInt items, uInt size); -extern void free(voidpf ptr); -#endif - -voidpf ZLIB_INTERNAL zcalloc(voidpf opaque, unsigned items, unsigned size) { - (void)opaque; - return sizeof(uInt) > 2 ? (voidpf)malloc(items * size) : - (voidpf)calloc(items, size); -} - -void ZLIB_INTERNAL zcfree(voidpf opaque, voidpf ptr) { - (void)opaque; - free(ptr); -} - -#endif /* MY_ZCALLOC */ - -#endif /* !Z_SOLO */ diff --git a/phobos/etc/c/zlib/zutil.h b/phobos/etc/c/zlib/zutil.h deleted file mode 100644 index 48dd7fe..0000000 --- a/phobos/etc/c/zlib/zutil.h +++ /dev/null @@ -1,254 +0,0 @@ -/* zutil.h -- internal interface and configuration of the compression library - * Copyright (C) 1995-2024 Jean-loup Gailly, Mark Adler - * For conditions of distribution and use, see copyright notice in zlib.h - */ - -/* WARNING: this file should *not* be used by applications. It is - part of the implementation of the compression library and is - subject to change. Applications should only use zlib.h. - */ - -/* @(#) $Id$ */ - -#ifndef ZUTIL_H -#define ZUTIL_H - -#ifdef HAVE_HIDDEN -# define ZLIB_INTERNAL __attribute__((visibility ("hidden"))) -#else -# define ZLIB_INTERNAL -#endif - -#include "zlib.h" - -#if defined(STDC) && !defined(Z_SOLO) -# if !(defined(_WIN32_WCE) && defined(_MSC_VER)) -# include -# endif -# include -# include -#endif - -#ifndef local -# define local static -#endif -/* since "static" is used to mean two completely different things in C, we - define "local" for the non-static meaning of "static", for readability - (compile with -Dlocal if your debugger can't find static symbols) */ - -typedef unsigned char uch; -typedef uch FAR uchf; -typedef unsigned short ush; -typedef ush FAR ushf; -typedef unsigned long ulg; - -#if !defined(Z_U8) && !defined(Z_SOLO) && defined(STDC) -# include -# if (ULONG_MAX == 0xffffffffffffffff) -# define Z_U8 unsigned long -# elif (ULLONG_MAX == 0xffffffffffffffff) -# define Z_U8 unsigned long long -# elif (UINT_MAX == 0xffffffffffffffff) -# define Z_U8 unsigned -# endif -#endif - -extern z_const char * const z_errmsg[10]; /* indexed by 2-zlib_error */ -/* (size given to avoid silly warnings with Visual C++) */ - -#define ERR_MSG(err) z_errmsg[(err) < -6 || (err) > 2 ? 9 : 2 - (err)] - -#define ERR_RETURN(strm,err) \ - return (strm->msg = ERR_MSG(err), (err)) -/* To be used only when the state is known to be valid */ - - /* common constants */ - -#ifndef DEF_WBITS -# define DEF_WBITS MAX_WBITS -#endif -/* default windowBits for decompression. MAX_WBITS is for compression only */ - -#if MAX_MEM_LEVEL >= 8 -# define DEF_MEM_LEVEL 8 -#else -# define DEF_MEM_LEVEL MAX_MEM_LEVEL -#endif -/* default memLevel */ - -#define STORED_BLOCK 0 -#define STATIC_TREES 1 -#define DYN_TREES 2 -/* The three kinds of block type */ - -#define MIN_MATCH 3 -#define MAX_MATCH 258 -/* The minimum and maximum match lengths */ - -#define PRESET_DICT 0x20 /* preset dictionary flag in zlib header */ - - /* target dependencies */ - -#if defined(MSDOS) || (defined(WINDOWS) && !defined(WIN32)) -# define OS_CODE 0x00 -# ifndef Z_SOLO -# if defined(__TURBOC__) || defined(__BORLANDC__) -# if (__STDC__ == 1) && (defined(__LARGE__) || defined(__COMPACT__)) - /* Allow compilation with ANSI keywords only enabled */ - void _Cdecl farfree( void *block ); - void *_Cdecl farmalloc( unsigned long nbytes ); -# else -# include -# endif -# else /* MSC or DJGPP */ -# include -# endif -# endif -#endif - -#ifdef AMIGA -# define OS_CODE 1 -#endif - -#if defined(VAXC) || defined(VMS) -# define OS_CODE 2 -# define F_OPEN(name, mode) \ - fopen((name), (mode), "mbc=60", "ctx=stm", "rfm=fix", "mrs=512") -#endif - -#ifdef __370__ -# if __TARGET_LIB__ < 0x20000000 -# define OS_CODE 4 -# elif __TARGET_LIB__ < 0x40000000 -# define OS_CODE 11 -# else -# define OS_CODE 8 -# endif -#endif - -#if defined(ATARI) || defined(atarist) -# define OS_CODE 5 -#endif - -#ifdef OS2 -# define OS_CODE 6 -# if defined(M_I86) && !defined(Z_SOLO) -# include -# endif -#endif - -#if defined(MACOS) -# define OS_CODE 7 -#endif - -#ifdef __acorn -# define OS_CODE 13 -#endif - -#if defined(WIN32) && !defined(__CYGWIN__) -# define OS_CODE 10 -#endif - -#ifdef _BEOS_ -# define OS_CODE 16 -#endif - -#ifdef __TOS_OS400__ -# define OS_CODE 18 -#endif - -#ifdef __APPLE__ -# define OS_CODE 19 -#endif - -#if defined(__BORLANDC__) && !defined(MSDOS) - #pragma warn -8004 - #pragma warn -8008 - #pragma warn -8066 -#endif - -/* provide prototypes for these when building zlib without LFS */ -#if !defined(_WIN32) && \ - (!defined(_LARGEFILE64_SOURCE) || _LFS64_LARGEFILE-0 == 0) - ZEXTERN uLong ZEXPORT adler32_combine64(uLong, uLong, z_off_t); - ZEXTERN uLong ZEXPORT crc32_combine64(uLong, uLong, z_off_t); - ZEXTERN uLong ZEXPORT crc32_combine_gen64(z_off_t); -#endif - - /* common defaults */ - -#ifndef OS_CODE -# define OS_CODE 3 /* assume Unix */ -#endif - -#ifndef F_OPEN -# define F_OPEN(name, mode) fopen((name), (mode)) -#endif - - /* functions */ - -#if defined(pyr) || defined(Z_SOLO) -# define NO_MEMCPY -#endif -#if defined(SMALL_MEDIUM) && !defined(_MSC_VER) && !defined(__SC__) - /* Use our own functions for small and medium model with MSC <= 5.0. - * You may have to use the same strategy for Borland C (untested). - * The __SC__ check is for Symantec. - */ -# define NO_MEMCPY -#endif -#if defined(STDC) && !defined(HAVE_MEMCPY) && !defined(NO_MEMCPY) -# define HAVE_MEMCPY -#endif -#ifdef HAVE_MEMCPY -# ifdef SMALL_MEDIUM /* MSDOS small or medium model */ -# define zmemcpy _fmemcpy -# define zmemcmp _fmemcmp -# define zmemzero(dest, len) _fmemset(dest, 0, len) -# else -# define zmemcpy memcpy -# define zmemcmp memcmp -# define zmemzero(dest, len) memset(dest, 0, len) -# endif -#else - void ZLIB_INTERNAL zmemcpy(Bytef* dest, const Bytef* source, uInt len); - int ZLIB_INTERNAL zmemcmp(const Bytef* s1, const Bytef* s2, uInt len); - void ZLIB_INTERNAL zmemzero(Bytef* dest, uInt len); -#endif - -/* Diagnostic functions */ -#ifdef ZLIB_DEBUG -# include - extern int ZLIB_INTERNAL z_verbose; - extern void ZLIB_INTERNAL z_error(char *m); -# define Assert(cond,msg) {if(!(cond)) z_error(msg);} -# define Trace(x) {if (z_verbose>=0) fprintf x ;} -# define Tracev(x) {if (z_verbose>0) fprintf x ;} -# define Tracevv(x) {if (z_verbose>1) fprintf x ;} -# define Tracec(c,x) {if (z_verbose>0 && (c)) fprintf x ;} -# define Tracecv(c,x) {if (z_verbose>1 && (c)) fprintf x ;} -#else -# define Assert(cond,msg) -# define Trace(x) -# define Tracev(x) -# define Tracevv(x) -# define Tracec(c,x) -# define Tracecv(c,x) -#endif - -#ifndef Z_SOLO - voidpf ZLIB_INTERNAL zcalloc(voidpf opaque, unsigned items, - unsigned size); - void ZLIB_INTERNAL zcfree(voidpf opaque, voidpf ptr); -#endif - -#define ZALLOC(strm, items, size) \ - (*((strm)->zalloc))((strm)->opaque, (items), (size)) -#define ZFREE(strm, addr) (*((strm)->zfree))((strm)->opaque, (voidpf)(addr)) -#define TRY_FREE(s, p) {if (p) ZFREE(s, p);} - -/* Reverse the bytes in a 32-bit value */ -#define ZSWAP32(q) ((((q) >> 24) & 0xff) + (((q) >> 8) & 0xff00) + \ - (((q) & 0xff00) << 8) + (((q) & 0xff) << 24)) - -#endif /* ZUTIL_H */ diff --git a/phobos/index.dd b/phobos/index.dd deleted file mode 100644 index 4812225..0000000 --- a/phobos/index.dd +++ /dev/null @@ -1,527 +0,0 @@ -Ddoc - -$(P Phobos is the standard runtime library that comes with the D language -compiler.) - -$(P Generally, the `std` namespace is used for the main modules in the -Phobos standard library. The `etc` namespace is used for external C/C++ -library bindings. The `core` namespace is used for low-level D runtime -functions.) - -$(P The following table is a quick reference guide for which Phobos modules to -use for a given category of functionality. Note that some modules may appear in -more than one category, as some Phobos modules are quite generic and can be -applied in a variety of situations.) - -$(BOOKTABLE , - $(TR - $(TH Modules) - $(TH Description) - ) - $(LEADINGROW Algorithms & ranges) - $(TR - $(TDNW - $(MREF std,algorithm)$(BR) - $(MREF std,range)$(BR) - $(MREF std,range,primitives)$(BR) - $(MREF std,range,interfaces)$(BR) - ) - $(TD Generic algorithms that work with $(MREF_ALTTEXT ranges, std,range) - of any type, including strings, arrays, and other kinds of - sequentially-accessed data. Algorithms include searching, - comparison, iteration, sorting, set operations, and mutation. - ) - ) - $(LEADINGROW Array manipulation) - $(TR - $(TDNW - $(MREF std,array)$(BR) - $(MREF std,algorithm)$(BR) - ) - $(TD Convenient operations commonly used with built-in arrays. - Note that many common array operations are subsets of more generic - algorithms that work with arbitrary ranges, so they are found in - `std.algorithm`. - ) - ) - $(LEADINGROW Containers) - $(TR - $(TDNW - $(MREF std,container,array)$(BR) - $(MREF std,container,binaryheap)$(BR) - $(MREF std,container,dlist)$(BR) - $(MREF std,container,rbtree)$(BR) - $(MREF std,container,slist)$(BR) - ) - $(TD See $(MREF_ALTTEXT std.container.*, std,container) for an - overview. - ) - ) - $(LEADINGROW Data formats) - $(TR - $(TDNW $(MREF std,base64)) - $(TD Encoding / decoding Base64 format.) - ) - $(TR - $(TDNW $(MREF std,csv)) - $(TD Read Comma Separated Values and its variants from an input range of $(CODE dchar).) - ) - $(TR - $(TDNW $(MREF std,json)) - $(TD Read/write data in JSON format.) - ) - $(TR - $(TDNW $(MREF std,zip)) - $(TD Read/write data in the ZIP archive format.) - ) - $(TR - $(TDNW $(MREF std,zlib)) - $(TD Compress/decompress data using the zlib library.) - ) - $(LEADINGROW Data integrity) - $(TR - $(TDNW $(MREF std,checkedint)) - $(TD Checked integral types.) - ) - $(TR - $(TDNW $(MREF std,digest)) - $(TD Compute digests such as md5, sha1 and crc32.) - ) - $(TR - $(TDNW $(MREF std,digest,crc)) - $(TD Cyclic Redundancy Check (32-bit) implementation.) - ) - $(TR - $(TDNW $(MREF std,digest,hmac)) - $(TD Compute HMAC digests of arbitrary data.) - ) - $(TR - $(TDNW $(MREF std,digest,md)) - $(TD Compute MD5 hash of arbitrary data.) - ) - $(TR - $(TDNW $(MREF std,digest,murmurhash)) - $(TD Compute MurmurHash of arbitrary data.) - ) - $(TR - $(TDNW $(MREF std,digest,ripemd)) - $(TD Compute RIPEMD-160 hash of arbitrary data.) - ) - $(TR - $(TDNW $(MREF std,digest,sha)) - $(TD Compute SHA1 and SHA2 hashes of arbitrary data.) - ) - $(LEADINGROW Date & time) - $(TR - $(TDNW $(MREF std,datetime)) - $(TD Provides convenient access to date and time representations.) - ) - $(TR - $(TDNW $(MREF core,time)) - $(TD Implements low-level time primitives.) - ) - $(LEADINGROW Exception handling) - $(TR - $(TDNW $(MREF std,exception)) - $(TD Implements routines related to exceptions.) - ) - $(TR - $(TDNW $(MREF core,exception)) - $(TD Defines built-in exception types and low-level - language hooks required by the compiler.) - ) - $(LEADINGROW External library bindings) - $(TR - $(TDNW $(MREF etc,c,curl)) - $(TD Interface to libcurl C library.) - ) - $(TR - $(TDNW $(MREF etc,c,odbc,sql)) - $(TD Interface to ODBC C library.) - ) - $(TR - $(TDNW $(MREF etc,c,odbc,sqlext)) - ) - $(TR - $(TDNW $(MREF etc,c,odbc,sqltypes)) - ) - $(TR - $(TDNW $(MREF etc,c,odbc,sqlucode)) - ) - $(TR - $(TDNW $(MREF etc,c,sqlite3)) - $(TD Interface to SQLite C library.) - ) - $(TR - $(TDNW $(MREF etc,c,zlib)) - $(TD Interface to zlib C library.) - ) - $(LEADINGROW I/O & File system) - $(TR - $(TDNW $(MREF std,file)) - $(TD Manipulate files and directories.) - ) - $(TR - $(TDNW $(MREF std,path)) - $(TD Manipulate strings that represent filesystem paths.) - ) - $(TR - $(TDNW $(MREF std,stdio)) - $(TD Perform buffered I/O.) - ) - $(LEADINGROW Interoperability) - $(TR - $(TDNW - $(MREF core,stdc,complex)$(BR) - $(MREF core,stdc,ctype)$(BR) - $(MREF core,stdc,errno)$(BR) - $(MREF core,stdc,fenv)$(BR) - $(MREF core,stdc,float_)$(BR) - $(MREF core,stdc,inttypes)$(BR) - $(MREF core,stdc,limits)$(BR) - $(MREF core,stdc,locale)$(BR) - $(MREF core,stdc,math)$(BR) - $(MREF core,stdc,signal)$(BR) - $(MREF core,stdc,stdarg)$(BR) - $(MREF core,stdc,stddef)$(BR) - $(MREF core,stdc,stdint)$(BR) - $(MREF core,stdc,stdio)$(BR) - $(MREF core,stdc,stdlib)$(BR) - $(MREF core,stdc,string)$(BR) - $(MREF core,stdc,tgmath)$(BR) - $(MREF core,stdc,time)$(BR) - $(MREF core,stdc,wchar_)$(BR) - $(MREF core,stdc,wctype)$(BR) - ) - $(TD - D bindings for standard C headers.$(BR)$(BR) - These are mostly undocumented, as documentation - for the functions these declarations provide - bindings to can be found on external resources. - ) - ) - $(LEADINGROW Memory management) - $(TR - $(TDNW $(MREF core,memory)) - $(TD Control the built-in garbage collector.) - ) - $(TR - $(TDNW $(MREF std,typecons)) - $(TD Build scoped variables and reference-counted types.) - ) - $(LEADINGROW Metaprogramming) - $(TR - $(TDNW $(MREF core,attribute)) - $(TD Definitions of special attributes recognized by the compiler.) - ) - $(TR - $(TDNW $(MREF core,demangle)) - $(TD Convert $(I mangled) D symbol identifiers to source representation.) - ) - $(TR - $(TDNW $(MREF std,demangle)) - $(TD A simple wrapper around core.demangle.) - ) - $(TR - $(TDNW $(MREF std,meta)) - $(TD Construct and manipulate template argument lists (aka type lists).) - ) - $(TR - $(TDNW $(MREF std,traits)) - $(TD Extract information about types and symbols at compile time.) - ) - $(TR - $(TDNW $(MREF std,typecons)) - $(TD Construct new, useful general purpose types.) - ) - $(LEADINGROW Multitasking) - $(TR - $(TDNW $(MREF std,concurrency)) - $(TD Low level messaging API for threads.) - ) - $(TR - $(TDNW $(MREF std,parallelism)) - $(TD High level primitives for SMP parallelism.) - ) - $(TR - $(TDNW $(MREF std,process)) - $(TD Starting and manipulating processes.) - ) - $(TR - $(TDNW $(MREF core,atomic)) - $(TD Basic support for lock-free concurrent programming.) - ) - $(TR - $(TDNW $(MREF core,sync,barrier)) - $(TD Synchronize the progress of a group of threads.) - ) - $(TR - $(TDNW $(MREF core,sync,condition)) - $(TD Synchronized condition checking.) - ) - $(TR - $(TDNW $(MREF core,sync,exception)) - $(TD Base class for synchronization exceptions.) - ) - $(TR - $(TDNW $(MREF core,sync,mutex)) - $(TD Mutex for mutually exclusive access.) - ) - $(TR - $(TDNW $(MREF core,sync,rwmutex)) - $(TD Shared read access and mutually exclusive write access.) - ) - $(TR - $(TDNW $(MREF core,sync,semaphore)) - $(TD General use synchronization semaphore.) - ) - $(TR - $(TDNW $(MREF core,thread)) - $(TD Thread creation and management.) - ) - $(LEADINGROW Networking) - $(TR - $(TDNW $(MREF std,socket)) - $(TD Socket primitives.) - ) - $(TR - $(TDNW $(MREF std,net,curl)) - $(TD Networking client functionality as provided by libcurl.) - ) - $(TR - $(TDNW $(MREF std,net,isemail)) - $(TD Validates an email address according to RFCs 5321, 5322 and others.) - ) - $(TR - $(TDNW $(MREF std,uri)) - $(TD Encode and decode Uniform Resource Identifiers (URIs).) - ) - $(TR - $(TDNW $(MREF std,uuid)) - $(TD Universally-unique identifiers for resources in distributed - systems.) - ) - $(LEADINGROW Numeric) - $(TR - $(TDNW $(MREF std,bigint)) - $(TD An arbitrary-precision integer type.) - ) - $(TR - $(TDNW $(MREF std,complex)) - $(TD A complex number type.) - ) - $(TR - $(TDNW $(MREF std,math)) - $(TD Elementary mathematical functions (powers, roots, trigonometry).) - ) - $(TR - $(TDNW $(MREF std,mathspecial)) - $(TD Families of transcendental functions.) - ) - $(TR - $(TDNW $(MREF std,numeric)) - $(TD Floating point numerics functions.) - ) - $(TR - $(TDNW $(MREF std,random)) - $(TD Pseudo-random number generators.) - ) - $(TR - $(TDNW $(MREF core,checkedint)) - $(TD Range-checking integral arithmetic primitives.) - ) - $(TR - $(TDNW $(MREF core,math)) - $(TD Built-in mathematical intrinsics.) - ) - $(LEADINGROW Paradigms) - $(TR - $(TDNW $(MREF std,functional)) - $(TD Functions that manipulate other functions.) - ) - $(TR - $(TDNW $(MREF std,algorithm)) - $(TD Generic algorithms for processing sequences.) - ) - $(TR - $(TDNW $(MREF std,signals)) - $(TD Signal-and-slots framework for event-driven programming.) - ) - $(LEADINGROW Runtime utilities) - $(TR - $(TDNW $(MREF1 object)) - $(TD Core language definitions. Automatically imported.) - ) - $(TR - $(TDNW $(MREF std,getopt)) - $(TD Parsing of command-line arguments.) - ) - $(TR - $(TDNW $(MREF std,compiler)) - $(TD Host compiler vendor string and language version.) - ) - $(TR - $(TDNW $(MREF std,system)) - $(TD Runtime environment, such as OS type and endianness.) - ) - $(TR - $(TDNW $(MREF core,cpuid)) - $(TD Capabilities of the CPU the program is running on.) - ) - $(TR - $(TDNW $(MREF core,memory)) - $(TD Control the built-in garbage collector.) - ) - $(TR - $(TDNW $(MREF core,runtime)) - $(TD Control and configure the D runtime.) - ) - $(LEADINGROW String manipulation) - $(TR - $(TDNW $(MREF std,string)) - $(TD Algorithms that work specifically with strings.) - ) - $(TR - $(TDNW $(MREF std,array)) - $(TD Manipulate builtin arrays.) - ) - $(TR - $(TDNW $(MREF std,algorithm)) - $(TD Generic algorithms for processing sequences.) - ) - $(TR - $(TDNW $(MREF std,uni)) - $(TD Fundamental Unicode algorithms and data structures.) - ) - $(TR - $(TDNW $(MREF std,utf)) - $(TD Encode and decode UTF-8, UTF-16 and UTF-32 strings.) - ) - $(TR - $(TDNW $(MREF std,format)) - $(TD Format data into strings.) - ) - $(TR - $(TDNW $(MREF std,path)) - $(TD Manipulate strings that represent filesystem paths.) - ) - $(TR - $(TDNW $(MREF std,regex)) - $(TD Regular expressions.) - ) - $(TR - $(TDNW $(MREF std,ascii)) - $(TD Routines specific to the ASCII subset of Unicode.) - ) - $(TR - $(TDNW $(MREF std,encoding)) - $(TD Handle and transcode between various text encodings.) - ) - $(TR - $(TDNW $(MREF std,windows,charset)) - $(TD Windows specific character set support.) - ) - $(TR - $(TDNW $(MREF std,outbuffer)) - $(TD Serialize data to $(CODE ubyte) arrays.) - ) - $(LEADINGROW Type manipulations) - $(TR - $(TDNW $(MREF std,conv)) - $(TD Convert types from one type to another.) - ) - $(TR - $(TDNW $(MREF std,typecons)) - $(TD Type constructors for scoped variables, ref counted types, etc.) - ) - $(TR - $(TDNW $(MREF std,bitmanip)) - $(TD High level bit level manipulation, bit arrays, bit fields.) - ) - $(TR - $(TDNW $(MREF std,variant)) - $(TD Dynamically-typed variable that can hold a value of any type.) - ) - $(TR - $(TDNW $(MREF core,bitop)) - $(TD Low level bit manipulation.) - ) - $(TR - $(TDNW $(MREF std,sumtype)) - $(TD Type-safe discriminated union.) - ) - $(LEADINGROW Vector programming) - $(TR - $(TDNW $(MREF core,simd)) - $(TD SIMD intrinsics) - ) - $(LEADINGROW Logging) - $(TR - $(TDNW - $(MREF std,logger)$(BR) - $(MREF std,logger,core)$(BR) - $(MREF std,logger,filelogger)$(BR) - $(MREF std,logger,multilogger)$(BR) - $(MREF std,logger,nulllogger)$(BR) - ) - $(TD - Logging. - ) - ) - -$(COMMENT - $(LEADINGROW Undocumented modules (intentionally omitted).) - $(TR - $(TDNW - $(MREF core,sync,config)$(BR) - $(MREF std,container,util)$(BR) - $(MREF std,regex,internal,backtracking)$(BR) - $(MREF std,regex,internal,generator)$(BR) - $(MREF std,regex,internal,ir)$(BR) - $(MREF std,regex,internal,kickstart)$(BR) - $(MREF std,regex,internal,parser)$(BR) - $(MREF std,regex,internal,tests)$(BR) - $(MREF std,regex,internal,thompson)$(BR) - ) - $(TD - Internal modules. - ) - ) - $(TR - $(TDNW - $(MREF core,vararg)$(BR) - $(MREF std,c,fenv)$(BR) - $(MREF std,c,linux,linux)$(BR) - $(MREF std,c,linux,socket)$(BR) - $(MREF std,c,locale)$(BR) - $(MREF std,c,math)$(BR) - $(MREF std,c,process)$(BR) - $(MREF std,c,stdarg)$(BR) - $(MREF std,c,stddef)$(BR) - $(MREF std,c,stdio)$(BR) - $(MREF std,c,stdlib)$(BR) - $(MREF std,c,string)$(BR) - $(MREF std,c,time)$(BR) - $(MREF std,c,wcharh)$(BR) - $(MREF std,stdint)$(BR) - ) - $(TDN - Redirect modules. - ) - ) - $(TR - $(TDNW - $(MREF std,mmfile)$(BR) - $(MREF std,typetuple)$(BR) - ) - $(TD - Deprecated modules. - ) - ) -) -) - -Macros: - TITLE=Phobos Runtime Library - DDOC_BLANKLINE= - _= diff --git a/phobos/phobos/sys/compiler.d b/phobos/phobos/sys/compiler.d deleted file mode 100644 index 8181c9e..0000000 --- a/phobos/phobos/sys/compiler.d +++ /dev/null @@ -1,58 +0,0 @@ -// Written in the D programming language. - -/** - * Identify the compiler used and its various features. - * - * Copyright: Copyright The D Language Foundation 2000 - 2011. - * License: $(HTTP www.boost.org/LICENSE_1_0.txt, Boost License 1.0). - * Authors: $(HTTP digitalmars.com, Walter Bright), Alex Rønne Petersen - * Source: $(PHOBOSSRC phobos/sys/compiler.d) - */ -/* Copyright The D Language Foundation 2000 - 2011. - * 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 phobos.sys.compiler; - -immutable -{ - /// Vendor specific string naming the compiler, for example: "Digital Mars D". - string name = __VENDOR__; - - /// Master list of D compiler vendors. - enum Vendor - { - unknown = 0, /// Compiler vendor could not be detected - digitalMars = 1, /// Digital Mars D (DMD) - gnu = 2, /// GNU D Compiler (GDC) - llvm = 3, /// LLVM D Compiler (LDC) - dotNET = 4, /// D.NET - sdc = 5, /// Snazzy D Compiler (SDC) - } - - /// Which vendor produced this compiler. - version (StdDdoc) Vendor vendor; - else version (DigitalMars) Vendor vendor = Vendor.digitalMars; - else version (GNU) Vendor vendor = Vendor.gnu; - else version (LDC) Vendor vendor = Vendor.llvm; - else version (D_NET) Vendor vendor = Vendor.dotNET; - else version (SDC) Vendor vendor = Vendor.sdc; - else Vendor vendor = Vendor.unknown; - - - /** - * The vendor specific version number, as in - * version_major.version_minor - */ - uint version_major = __VERSION__ / 1000; - uint version_minor = __VERSION__ % 1000; /// ditto - - - /** - * The version of the D Programming Language Specification - * supported by the compiler. - */ - uint D_major = 2; - uint D_minor = 0; -} diff --git a/phobos/phobos/sys/meta.d b/phobos/phobos/sys/meta.d deleted file mode 100644 index 1c31b67..0000000 --- a/phobos/phobos/sys/meta.d +++ /dev/null @@ -1,1255 +0,0 @@ -// Written in the D programming language -/++ - Templates to manipulate - $(DDSUBLINK spec/template, variadic-templates, template parameter sequences) - (also known as $(I alias sequences)). - - Some operations on alias sequences are built into the language, - such as `S[i]`, which accesses the element at index `i` in the - sequence. `S[low .. high]` returns a new alias - sequence that is a slice of the old one. - - For more information, see - $(DDLINK ctarguments, Compile-time Sequences, Compile-time Sequences). - - One thing that should be noted is that while the templates provided in this - module can be extremely useful, they generally should not be used with lists - of values. The language uses alias sequences for a variety of things - (including both parameter lists and argument lists), so they can contain - types, symbols, values, or a mixture of them all. The ability to manipulate - types and symbols within alias sequences is vital, because that's really - the only way to do it. However, because D has CTFE (Compile-Time Function - Evaluation), making it possible to call many functions at compile time, if - code needs to be able to manipulate values at compile-time, CTFE is - typically much more efficient and easier to do. Instantiating a bunch of - templates to manipulate values is incredibly inefficient in comparison. - - So, while many of the templates in this module will work with values simply - because alias sequences can contain values, most code should restrict - itself to using them for operating on types or symbols - i.e. the stuff - where CTFE can't be used. That being said, there will be times when one can - be used to feed into the other. E.G. - --- - alias Types = AliasSeq!(int, byte, ulong, int[10]); - - enum Sizeof(T) = T.sizeof; - - alias sizesAsAliasSeq = Map!(Sizeof, Types); - static assert(sizesAsAliasSeq == AliasSeq!(4, 1, 8, 40)); - - enum size_t[] sizes = [sizesAsAliasSeq]; - static assert(sizes == [4, 1, 8, 40]); - --- - - Just be aware that if CTFE can be used for a particular task, it's better to - use CTFE than to manipulate alias sequences with the kind of templates - provided by this module. - - $(SCRIPT inhibitQuickIndex = 1;) - $(DIVC quickindex, - $(BOOKTABLE , - $(TR $(TH Category) $(TH Templates)) - $(TR $(TD Building blocks) $(TD - $(LREF Alias) - $(LREF AliasSeq) - )) - $(TR $(TD Alias sequence filtering) $(TD - $(LREF Filter) - $(LREF Stride) - $(LREF Unique) - )) - $(TR $(TD Alias sequence transformation) $(TD - $(LREF Map) - $(LREF Reverse) - )) - $(TR $(TD Alias sequence searching) $(TD - $(LREF all) - $(LREF any) - $(LREF indexOf) - )) - $(TR $(TD Template predicates) $(TD - $(LREF And) - $(LREF Not) - $(LREF Or) - )) - $(TR $(TD Template instantiation) $(TD - $(LREF ApplyLeft) - $(LREF ApplyRight) - $(LREF Instantiate) - )) - ) - - References: - Based on ideas in Table 3.1 from - $(LINK2 http://amazon.com/exec/obidos/ASIN/0201704315/ref=ase_classicempire/102-2957199-2585768, - Modern C++ Design), - Andrei Alexandrescu (Addison-Wesley Professional, 2001) - - Copyright: Copyright The D Language Foundation 2005 - 2024. - License: $(HTTP www.boost.org/LICENSE_1_0.txt, Boost License 1.0). - Authors: $(HTTP digitalmars.com, Walter Bright), - $(HTTP klickverbot.at, David Nadlinger) - $(HTTP jmdavisprog.com, Jonathan M Davis) - Source: $(PHOBOSSRC phobos/sys/meta) -+/ -module phobos.sys.meta; - -// Example for converting types to values from module documentation. -@safe unittest -{ - alias Types = AliasSeq!(int, byte, ulong, int[10]); - - enum Sizeof(T) = T.sizeof; - - alias sizesAsAliasSeq = Map!(Sizeof, Types); - static assert(sizesAsAliasSeq == AliasSeq!(4, 1, 8, 40)); - - enum size_t[] sizes = [sizesAsAliasSeq]; - static assert(sizes == [4, 1, 8, 40]); -} - -/++ - Creates a sequence of zero or more aliases. This is most commonly - used as template parameters or arguments. - +/ -alias AliasSeq(TList...) = TList; - -/// -@safe unittest -{ - alias TL = AliasSeq!(int, double); - - int foo(TL td) // same as int foo(int, double); - { - return td[0] + cast(int) td[1]; - } -} - -/// -@safe unittest -{ - alias TL = AliasSeq!(int, double); - - alias Types = AliasSeq!(TL, char); - static assert(is(Types == AliasSeq!(int, double, char))); -} - -/// -@safe unittest -{ - static char foo(size_t i, string str) - { - return str[i]; - } - - alias vars = AliasSeq!(2, "dlang"); - - assert(foo(vars) == 'a'); -} - -/++ - Allows aliasing of any single symbol, type or compile-time expression. - - Not everything can be directly aliased. An alias cannot be declared - of - for example - a literal: - --- - alias a = 4; //Error - --- - With this template any single entity can be aliased: - --- - alias b = Alias!4; //OK - --- - See_Also: - To alias more than one thing at once, use $(LREF AliasSeq). - +/ -alias Alias(alias a) = a; - -/// Ditto -alias Alias(T) = T; - -/// -@safe unittest -{ - // Without Alias this would fail if Args[0] were e.g. a value and - // some logic would be needed to detect when to use enum instead. - alias Head(Args...) = Alias!(Args[0]); - alias Tail(Args...) = Args[1 .. $]; - - alias Blah = AliasSeq!(3, int, "hello"); - static assert(Head!Blah == 3); - static assert(is(Head!(Tail!Blah) == int)); - static assert((Tail!Blah)[1] == "hello"); -} - -/// -@safe unittest -{ - { - alias a = Alias!123; - static assert(a == 123); - } - { - enum e = 1; - alias a = Alias!e; - static assert(a == 1); - } - { - alias a = Alias!(3 + 4); - static assert(a == 7); - } - { - alias concat = (s0, s1) => s0 ~ s1; - alias a = Alias!(concat("Hello", " World!")); - static assert(a == "Hello World!"); - } - { - alias A = Alias!int; - static assert(is(A == int)); - } - { - alias A = Alias!(AliasSeq!int); - static assert(!is(typeof(A[0]))); // An Alias is not an AliasSeq. - static assert(is(A == int)); - } - { - auto i = 6; - alias a = Alias!i; - ++a; - assert(i == 7); - } -} - -/++ - Filters an $(D AliasSeq) using the given template predicate. - - The result is an $(D AliasSeq) that contains only the elements which satisfy - the predicate. - +/ -template Filter(alias Pred, Args...) -{ - alias Filter = AliasSeq!(); - static foreach (Arg; Args) - { - static if (Pred!Arg) - Filter = AliasSeq!(Filter, Arg); - } -} - -/// -@safe unittest -{ - import phobos.sys.traits : isDynamicArray, isPointer, isUnsignedInteger; - - alias Types = AliasSeq!(string, int, int[], bool[], ulong, double, ubyte); - - static assert(is(Filter!(isDynamicArray, Types) == - AliasSeq!(string, int[], bool[]))); - - static assert(is(Filter!(isUnsignedInteger, Types) == - AliasSeq!(ulong, ubyte))); - - static assert(is(Filter!(isPointer, Types) == AliasSeq!())); -} - -/++ - Evaluates to an $(LREF AliasSeq) which only contains every nth element from - the $(LREF AliasSeq) that was passed in, where $(D n) is stepSize. - - So, if stepSize is $(D 2), then the result contains every other element from - the original. If stepSize is $(D 3), then the result contains every third - element from the original. Etc. - - If stepSize is negative, then the result is equivalent to using - $(LREF Reverse) on the given $(LREF AliasSeq) and then using Stride on it - with the absolute value of that stepSize. - - If stepSize is positive, then the first element in the original - $(LREF AliasSeq) is the first element in the result, whereas if stepSize is - negative, then the last element in the original is the first element in the - result. Each subsequent element is then the element at the index of the - previous element plus stepSize. - +/ -template Stride(int stepSize, Args...) -if (stepSize != 0) -{ - alias Stride = AliasSeq!(); - static if (stepSize > 0) - { - static foreach (i; 0 .. (Args.length + stepSize - 1) / stepSize) - Stride = AliasSeq!(Stride, Args[i * stepSize]); - } - else - { - static foreach (i; 0 .. (Args.length - stepSize - 1) / -stepSize) - Stride = AliasSeq!(Stride, Args[$ - 1 + i * stepSize]); - } -} - -/// -@safe unittest -{ - static assert(is(Stride!(1, short, int, long) == AliasSeq!(short, int, long))); - static assert(is(Stride!(2, short, int, long) == AliasSeq!(short, long))); - static assert(is(Stride!(3, short, int, long) == AliasSeq!short)); - static assert(is(Stride!(100, short, int, long) == AliasSeq!short)); - - static assert(is(Stride!(-1, short, int, long) == AliasSeq!(long, int, short))); - static assert(is(Stride!(-2, short, int, long) == AliasSeq!(long, short))); - static assert(is(Stride!(-3, short, int, long) == AliasSeq!long)); - static assert(is(Stride!(-100, short, int, long) == AliasSeq!long)); - - alias Types = AliasSeq!(short, int, long, ushort, uint, ulong); - static assert(is(Stride!(3, Types) == AliasSeq!(short, ushort))); - static assert(is(Stride!(3, Types[1 .. $]) == AliasSeq!(int, uint))); - static assert(is(Stride!(-3, Types) == AliasSeq!(ulong, long))); - - static assert(is(Stride!(-2, Types) == Stride!(2, Reverse!Types))); - - static assert(is(Stride!1 == AliasSeq!())); - static assert(is(Stride!100 == AliasSeq!())); -} - -@safe unittest -{ - static assert(!__traits(compiles, Stride!(0, int))); - - alias Types = AliasSeq!(bool, byte, ubyte, short, ushort, int, uint, long, ulong, - char, wchar, dchar, float, double, real, Object); - alias Types2 = AliasSeq!(bool, ubyte, ushort, uint, ulong, wchar, float, real); - alias Types3 = AliasSeq!(bool, short, uint, char, float, Object); - alias Types4 = AliasSeq!(bool, ushort, ulong, float); - alias Types5 = AliasSeq!(bool, int, wchar, Object); - alias Types6 = AliasSeq!(bool, uint, float); - alias Types7 = AliasSeq!(bool, long, real); - alias Types8 = AliasSeq!(bool, ulong); - alias Types9 = AliasSeq!(bool, char); - alias Types10 = AliasSeq!(bool, wchar); - - static assert(is(Stride!(1, Types) == Types)); - static assert(is(Stride!(2, Types) == Types2)); - static assert(is(Stride!(3, Types) == Types3)); - static assert(is(Stride!(4, Types) == Types4)); - static assert(is(Stride!(5, Types) == Types5)); - static assert(is(Stride!(6, Types) == Types6)); - static assert(is(Stride!(7, Types) == Types7)); - static assert(is(Stride!(8, Types) == Types8)); - static assert(is(Stride!(9, Types) == Types9)); - static assert(is(Stride!(10, Types) == Types10)); - - static assert(is(Stride!(-1, Types) == Reverse!Types)); - static assert(is(Stride!(-2, Types) == Stride!(2, Reverse!Types))); - static assert(is(Stride!(-3, Types) == Stride!(3, Reverse!Types))); - static assert(is(Stride!(-4, Types) == Stride!(4, Reverse!Types))); - static assert(is(Stride!(-5, Types) == Stride!(5, Reverse!Types))); - static assert(is(Stride!(-6, Types) == Stride!(6, Reverse!Types))); - static assert(is(Stride!(-7, Types) == Stride!(7, Reverse!Types))); - static assert(is(Stride!(-8, Types) == Stride!(8, Reverse!Types))); - static assert(is(Stride!(-9, Types) == Stride!(9, Reverse!Types))); - static assert(is(Stride!(-10, Types) == Stride!(10, Reverse!Types))); -} - -/++ - Evaluates to an $(LREF AliasSeq) which contains no duplicate elements. - - Unique takes a binary template predicate that it uses to compare elements - for equality. If the predicate is $(D true) when an element in the given - $(LREF AliasSeq) is compared with an element with a lower index, then that - element is not included in the result (so if any elements in the - $(LREF AliasSeq) are considered equal per the predicate, then only the - first one is included in the result). - - Note that the binary predicate must be partially instantiable, e.g. - --- - alias PartialCmp = Cmp!(Args[0]); - enum same = PartialCmp!(Args[1]); - --- - Otherwise, it won't work. - - See_Also: - $(REF isSameSymbol, phobos, sys, traits) - $(REF isSameType, phobos, sys, traits) - +/ -template Unique(alias Cmp, Args...) -{ - alias Unique = AliasSeq!(); - static foreach (i, Arg; Args) - { - static if (i == 0) - Unique = AliasSeq!Arg; - else - Unique = AppendIfUnique!(Cmp, Unique, Arg); - } -} - -// Unfortunately, this can't be done in-place in Unique, because then we get -// errors about reassigning Unique after reading it. -private template AppendIfUnique(alias Cmp, Args...) -{ - static if (indexOf!(Cmp!(Args[$ - 1]), Args[0 .. $ - 1]) == -1) - alias AppendIfUnique = Args; - else - alias AppendIfUnique = Args[0 .. $ - 1]; -} - -/// -@safe unittest -{ - import phobos.sys.traits : isSameType; - - alias Types1 = AliasSeq!(int, long, long, int, int, float, int); - - static assert(is(Unique!(isSameType, Types1) == - AliasSeq!(int, long, float))); - - alias Types2 = AliasSeq!(byte, ubyte, short, ushort, int, uint); - static assert(is(Unique!(isSameType, Types2) == Types2)); - - // Empty AliasSeq. - static assert(Unique!isSameType.length == 0); - - // An AliasSeq with a single element works as well. - static assert(Unique!(isSameType, int).length == 1); -} - -/// -@safe unittest -{ - import phobos.sys.traits : isSameSymbol; - - int i; - string s; - real r; - alias Symbols = AliasSeq!(i, s, i, i, s, r, r, i); - - alias Result = Unique!(isSameSymbol, Symbols); - static assert(Result.length == 3); - static assert(__traits(isSame, Result[0], i)); - static assert(__traits(isSame, Result[1], s)); - static assert(__traits(isSame, Result[2], r)); - - // Comparing AliasSeqs for equality with is expressions only works - // if they only contain types. - static assert(!is(Symbols == Result)); -} - -/// -@safe unittest -{ - alias Types = AliasSeq!(int, uint, long, string, short, int*, ushort); - - template sameSize(T) - { - enum sameSize(U) = T.sizeof == U.sizeof; - } - static assert(is(Unique!(sameSize, Types) == - AliasSeq!(int, long, string, short))); - - // The predicate must be partially instantiable. - enum sameSize_fails(T, U) = T.sizeof == U.sizeof; - static assert(!__traits(compiles, Unique!(sameSize_fails, Types))); -} - -/++ - Map takes a template and applies it to every element in the given - $(D AliasSeq), resulting in an $(D AliasSeq) with the transformed elements. - - So, it's equivalent to - `AliasSeq!(Fun!(Args[0]), Fun!(Args[1]), ..., Fun!(Args[$ - 1]))`. - +/ -template Map(alias Fun, Args...) -{ - alias Map = AliasSeq!(); - static foreach (Arg; Args) - Map = AliasSeq!(Map, Fun!Arg); -} - -/// -@safe unittest -{ - import phobos.sys.traits : Unqualified; - - // empty - alias Empty = Map!Unqualified; - static assert(Empty.length == 0); - - // single - alias Single = Map!(Unqualified, const int); - static assert(is(Single == AliasSeq!int)); - - // several - alias Several = Map!(Unqualified, int, const int, immutable int, uint, - ubyte, byte, short, ushort, const long); - static assert(is(Several == AliasSeq!(int, int, int, uint, - ubyte, byte, short, ushort, long))); - - alias ToDynamicArray(T) = T[]; - - alias Arrays = Map!(ToDynamicArray, int, const ubyte, string); - static assert(is(Arrays == AliasSeq!(int[], const(ubyte)[], string[]))); -} - -// @@@ BUG @@@ The test below exposes failure of the straightforward use. -// See @adamdruppe's comment to https://github.com/dlang/phobos/pull/8039 -@safe unittest -{ - template id(alias what) - { - enum id = __traits(identifier, what); - } - - enum A { a } - static assert(Map!(id, A.a) == AliasSeq!"a"); -} - -// regression test for https://issues.dlang.org/show_bug.cgi?id=21088 -@system unittest // typeid opEquals is @system -{ - enum getTypeId(T) = typeid(T); - alias A = Map!(getTypeId, int); - - assert(A == typeid(int)); -} - -/++ - Takes an $(D AliasSeq) and result in an $(D AliasSeq) where the order of - the elements has been reversed. - +/ -template Reverse(Args...) -{ - alias Reverse = AliasSeq!(); - static foreach_reverse (Arg; Args) - Reverse = AliasSeq!(Reverse, Arg); -} - -/// -@safe unittest -{ - static assert(is(Reverse!(int, byte, long, string) == - AliasSeq!(string, long, byte, int))); - - alias Types = AliasSeq!(int, long, long, int, float, - ubyte, short, ushort, uint); - static assert(is(Reverse!Types == AliasSeq!(uint, ushort, short, ubyte, - float, int, long, long, int))); - - static assert(is(Reverse!() == AliasSeq!())); -} - -/++ - Whether the given template predicate is $(D true) for all of the elements in - the given $(D AliasSeq). - - Evaluation is $(I not) short-circuited if a $(D false) result is - encountered; the template predicate must be instantiable with all the - elements. - +/ -version (StdDdoc) template all(alias Pred, Args...) -{ - import core.internal.traits : allSatisfy; - alias all = allSatisfy!(Pred, Args); -} -else -{ - import core.internal.traits : allSatisfy; - alias all = allSatisfy; -} - -/// -@safe unittest -{ - import phobos.sys.traits : isDynamicArray, isInteger; - - static assert(!all!(isInteger, int, double)); - static assert( all!(isInteger, int, long)); - - alias Types = AliasSeq!(string, int[], bool[]); - - static assert( all!(isDynamicArray, Types)); - static assert(!all!(isInteger, Types)); - - static assert( all!isInteger); -} - -/++ - Whether the given template predicate is $(D true) for any of the elements in - the given $(D AliasSeq). - - Evaluation is $(I not) short-circuited if a $(D true) result is - encountered; the template predicate must be instantiable with all the - elements. - +/ -version (StdDdoc) template any(alias Pred, Args...) -{ - import core.internal.traits : anySatisfy; - alias any = anySatisfy!(Pred, Args); -} -else -{ - import core.internal.traits : anySatisfy; - alias any = anySatisfy; -} - -/// -@safe unittest -{ - import phobos.sys.traits : isDynamicArray, isInteger; - - static assert(!any!(isInteger, string, double)); - static assert( any!(isInteger, int, double)); - - alias Types = AliasSeq!(string, int[], bool[], real, bool); - - static assert( any!(isDynamicArray, Types)); - static assert(!any!(isInteger, Types)); - - static assert(!any!isInteger); -} - -/++ - Evaluates to the index of the first element where $(D Pred!(Args[i])) is - $(D true). - - If $(D Pred!(Args[i])) is not $(D true) for any elements, then the result - is $(D -1). - - Evaluation is $(I not) short-circuited if a $(D true) result is - encountered; the template predicate must be instantiable with all the - elements. - +/ -template indexOf(alias Pred, Args...) -{ - enum ptrdiff_t indexOf = - { - static foreach (i; 0 .. Args.length) - { - static if (Pred!(Args[i])) - return i; - } - return -1; - }(); -} - -/// -@safe unittest -{ - import phobos.sys.traits : isInteger, isSameSymbol, isSameType; - - alias Types1 = AliasSeq!(string, int, long, char[], ubyte, int); - alias Types2 = AliasSeq!(float, double, int[], char[], void); - - static assert(indexOf!(isInteger, Types1) == 1); - static assert(indexOf!(isInteger, Types2) == -1); - - static assert(indexOf!(isSameType!ubyte, Types1) == 4); - static assert(indexOf!(isSameType!ubyte, Types2) == -1); - - int i; - int j; - string s; - int foo() { return 0; } - alias Symbols = AliasSeq!(i, j, foo); - static assert(indexOf!(isSameSymbol!j, Symbols) == 1); - static assert(indexOf!(isSameSymbol!s, Symbols) == -1); - - // Empty AliasSeq. - static assert(indexOf!isInteger == -1); - - // The predicate does not compile with all of the arguments, - // so indexOf does not compile. - static assert(!__traits(compiles, indexOf!(isSameType!int, long, int, 42))); -} - -unittest -{ - import phobos.sys.traits : isSameType; - - static assert(indexOf!(isSameType!int, short, int, long) >= 0); - static assert(indexOf!(isSameType!string, short, int, long) < 0); - - // This is to verify that we don't accidentally end up with the type of - // the result differing based on whether it's -1 or not. Not specifying the - // type at all in indexOf results in -1 being int on all systems and the - // other results being whatever size_t is (ulong on most systems at this - // point), which does generally work, but being explicit with the type - // avoids any subtle issues that might come from the type of the result - // varying based on whether the item is found or not. - static assert(is(typeof(indexOf!(isSameType!int, short, int, long)) == - typeof(indexOf!(isSameType!string, short, int, long)))); - - static assert(indexOf!(isSameType!string, string, string, string, string) == 0); - static assert(indexOf!(isSameType!string, int, string, string, string) == 1); - static assert(indexOf!(isSameType!string, int, int, string, string) == 2); - static assert(indexOf!(isSameType!string, int, int, int, string) == 3); - static assert(indexOf!(isSameType!string, int, int, int, int) == -1); -} - -/++ - Combines multiple template predicates into a single template predicate using - logical AND - i.e. for the resulting predicate to be $(D true) with a - particular argument, all of the predicates must be $(D true) with that - argument. - - Evaluation is $(I not) short-circuited if a $(D false) result is - encountered; the template predicate must be instantiable with all the - elements. - - See_Also: - $(LREF Not) - $(LREF Or) - +/ -template And(Preds...) -{ - enum And(Args...) = - { - static foreach (Pred; Preds) - { - static if (!Pred!Args) - return false; - } - return true; - }(); -} - -/// -@safe unittest -{ - import phobos.sys.traits : isNumeric; - - template isSameSize(size_t size) - { - enum isSameSize(T) = T.sizeof == size; - } - - alias is32BitNumeric = And!(isNumeric, isSameSize!4); - - static assert(!is32BitNumeric!short); - static assert( is32BitNumeric!int); - static assert(!is32BitNumeric!long); - static assert( is32BitNumeric!float); - static assert(!is32BitNumeric!double); - static assert(!is32BitNumeric!(int*)); - - // An empty sequence of predicates always yields true. - alias alwaysTrue = And!(); - static assert(alwaysTrue!int); -} - -/++ - Predicates with multiple parameters are also supported. However, the number - of parameters must match. - +/ -@safe unittest -{ - import phobos.sys.traits : isImplicitlyConvertible, isInteger, isSameType; - - alias isOnlyImplicitlyConvertible - = And!(Not!isSameType, isImplicitlyConvertible); - - static assert( isOnlyImplicitlyConvertible!(int, long)); - static assert(!isOnlyImplicitlyConvertible!(int, int)); - static assert(!isOnlyImplicitlyConvertible!(long, int)); - - static assert( isOnlyImplicitlyConvertible!(string, const(char)[])); - static assert(!isOnlyImplicitlyConvertible!(string, string)); - static assert(!isOnlyImplicitlyConvertible!(const(char)[], string)); - - // Mismatched numbers of parameters. - alias doesNotWork = And!(isInteger, isImplicitlyConvertible); - static assert(!__traits(compiles, doesNotWork!int)); - static assert(!__traits(compiles, doesNotWork!(int, long))); -} - -@safe unittest -{ - enum testAlways(Args...) = true; - enum testNever(Args...) = false; - - static assert( Instantiate!(And!(testAlways, testAlways, testAlways), int)); - static assert(!Instantiate!(And!(testAlways, testAlways, testNever), int)); - static assert(!Instantiate!(And!(testAlways, testNever, testNever), int)); - static assert(!Instantiate!(And!(testNever, testNever, testNever), int)); - static assert(!Instantiate!(And!(testNever, testNever, testAlways), int)); - static assert(!Instantiate!(And!(testNever, testAlways, testAlways), int)); - - static assert( Instantiate!(And!(testAlways, testAlways), int)); - static assert(!Instantiate!(And!(testAlways, testNever), int)); - static assert(!Instantiate!(And!(testNever, testAlways), int)); - static assert(!Instantiate!(And!(testNever, testNever), int)); - - static assert( Instantiate!(And!testAlways, int)); - static assert(!Instantiate!(And!testNever, int)); - - // No short-circuiting. - import phobos.sys.traits : isEqual, isFloatingPoint; - static assert(!Instantiate!(And!isFloatingPoint, int)); - static assert(!__traits(compiles, Instantiate!(And!(isFloatingPoint, isEqual), int))); -} - -/++ - Evaluates to a template predicate which negates the given predicate. - - See_Also: - $(LREF And) - $(LREF Or) - +/ -template Not(alias Pred) -{ - enum Not(Args...) = !Pred!Args; -} - -/// -@safe unittest -{ - import phobos.sys.traits : isDynamicArray, isPointer; - - alias isNotPointer = Not!isPointer; - static assert( isNotPointer!int); - static assert(!isNotPointer!(int*)); - static assert( all!(isNotPointer, string, char, float)); - - static assert(!all!(Not!isDynamicArray, string, char[], int[], long)); - static assert( any!(Not!isDynamicArray, string, char[], int[], long)); -} - -/++ - Predicates with multiple parameters are also supported. - +/ -@safe unittest -{ - import phobos.sys.traits : isImplicitlyConvertible, isInteger; - - alias notImplicitlyConvertible = Not!isImplicitlyConvertible; - - static assert( notImplicitlyConvertible!(long, int)); - static assert(!notImplicitlyConvertible!(int, long)); - - static assert( notImplicitlyConvertible!(const(char)[], string)); - static assert(!notImplicitlyConvertible!(string, const(char)[])); -} - -/++ - Combines multiple template predicates into a single template predicate using - logical OR - i.e. for the resulting predicate to be $(D true) with a - particular argument, at least one of the predicates must be $(D true) with - that argument. - - Evaluation is $(I not) short-circuited if a $(D true) result is - encountered; the template predicate must be instantiable with all the - elements. - - See_Also: - $(LREF And) - $(LREF Not) - +/ -template Or(Preds...) -{ - enum Or(Args...) = - { - static foreach (Pred; Preds) - { - static if (Pred!Args) - return true; - } - return false; - }(); -} - -/// -@safe unittest -{ - import phobos.sys.traits : isFloatingPoint, isSignedInteger; - - alias isSignedNumeric = Or!(isFloatingPoint, isSignedInteger); - - static assert( isSignedNumeric!short); - static assert( isSignedNumeric!long); - static assert( isSignedNumeric!double); - static assert(!isSignedNumeric!uint); - static assert(!isSignedNumeric!ulong); - static assert(!isSignedNumeric!string); - static assert(!isSignedNumeric!(int*)); - - // An empty sequence of predicates always yields false. - alias alwaysFalse = Or!(); - static assert(!alwaysFalse!int); -} - -/++ - Predicates with multiple parameters are also supported. However, the number - of parameters must match. - +/ -@safe unittest -{ - import phobos.sys.traits : isImplicitlyConvertible, isInteger; - - enum isSameSize(T, U) = T.sizeof == U.sizeof; - alias convertibleOrSameSize = Or!(isImplicitlyConvertible, isSameSize); - - static assert( convertibleOrSameSize!(int, int)); - static assert( convertibleOrSameSize!(int, long)); - static assert(!convertibleOrSameSize!(long, int)); - - static assert( convertibleOrSameSize!(int, float)); - static assert( convertibleOrSameSize!(float, int)); - static assert(!convertibleOrSameSize!(double, int)); - static assert(!convertibleOrSameSize!(float, long)); - - static assert( convertibleOrSameSize!(int*, string*)); - - // Mismatched numbers of parameters. - alias doesNotWork = Or!(isInteger, isImplicitlyConvertible); - static assert(!__traits(compiles, doesNotWork!int)); - static assert(!__traits(compiles, doesNotWork!(int, long))); -} - -@safe unittest -{ - enum testAlways(Args...) = true; - enum testNever(Args...) = false; - - static assert( Instantiate!(Or!(testAlways, testAlways, testAlways), int)); - static assert( Instantiate!(Or!(testAlways, testAlways, testNever), int)); - static assert( Instantiate!(Or!(testAlways, testNever, testNever), int)); - static assert(!Instantiate!(Or!(testNever, testNever, testNever), int)); - - static assert( Instantiate!(Or!(testAlways, testAlways), int)); - static assert( Instantiate!(Or!(testAlways, testNever), int)); - static assert( Instantiate!(Or!(testNever, testAlways), int)); - static assert(!Instantiate!(Or!(testNever, testNever), int)); - - static assert( Instantiate!(Or!testAlways, int)); - static assert(!Instantiate!(Or!testNever, int)); - - static assert(Instantiate!(Or!testAlways, int)); - static assert(Instantiate!(Or!testAlways, Map)); - static assert(Instantiate!(Or!testAlways, int, Map)); - - // No short-circuiting. - import phobos.sys.traits : isEqual, isInteger; - static assert( Instantiate!(Or!isInteger, int)); - static assert(!__traits(compiles, Instantiate!(Or!(isInteger, isEqual), int))); -} - -/++ - Instantiates the given template with the given arguments and evaluates to - the result of that template. - - This is used to work around some syntactic limitations that D has with - regards to instantiating templates. Essentially, D requires a name for a - template when instantiating it (be it the name of the template itself or an - alias to the template), which causes problems when you don't have that. - - Specifically, if the template is within an $(LREF AliasSeq) - e.g. - $(D Templates[0]!Args) - or it's the result of another template - e.g - $(D Foo!Bar!Baz) - the instantiation is illegal. This leaves two ways to - solve the problem. The first is to create an alias, e.g. - --- - alias Template = Templates[0]; - enum result = Template!Args; - - alias Partial = Foo!Bar; - alias T = Partial!Baz; - --- - The second is to use Instantiate, e.g. - --- - enum result = Instantiate!(Templates[0], Args); - - alias T = Instiantiate!(Foo!Bar, Baz); - --- - - Of course, the downside to this is that it adds an additional template - instantiation, but it avoids creating an alias just to be able to - instantiate a template. So, whether it makes sense to use Instantiate - instead of an alias naturally depends on the situation, but without it, - we'd be forced to create aliases even in situations where that's - problematic. - - See_Also: - $(LREF ApplyLeft) - $(LREF ApplyRight) - +/ -alias Instantiate(alias Template, Args...) = Template!Args; - -/// -@safe unittest -{ - import phobos.sys.traits : ConstOf, isImplicitlyConvertible, isSameType, isInteger; - - alias Templates = AliasSeq!(isImplicitlyConvertible!int, - isSameType!string, - isInteger, - ConstOf); - - // Templates[0]!long does not compile, because the compiler can't parse it. - - static assert( Instantiate!(Templates[0], long)); - static assert(!Instantiate!(Templates[0], string)); - - static assert(!Instantiate!(Templates[1], long)); - static assert( Instantiate!(Templates[1], string)); - - static assert( Instantiate!(Templates[2], long)); - static assert(!Instantiate!(Templates[2], string)); - - static assert(is(Instantiate!(Templates[3], int) == const int)); - static assert(is(Instantiate!(Templates[3], double) == const double)); -} - -/// -@safe unittest -{ - template hasMember(string member) - { - enum hasMember(T) = __traits(hasMember, T, member); - } - - struct S - { - int foo; - } - - // hasMember!"foo"!S does not compile, - // because having multiple ! arguments is not allowed. - - static assert( Instantiate!(hasMember!"foo", S)); - static assert(!Instantiate!(hasMember!"bar", S)); -} - -/++ - Instantiate also allows us to do template instantations via templates that - take other templates as arguments. - +/ -@safe unittest -{ - import phobos.sys.traits : isInteger, isNumeric, isUnsignedInteger; - - alias Results = Map!(ApplyRight!(Instantiate, int), - isInteger, isNumeric, isUnsignedInteger); - - static assert([Results] == [true, true, false]); -} - -/++ - ApplyLeft does a - $(LINK2 http://en.wikipedia.org/wiki/Partial_application, partial application) - of its arguments, providing a way to bind a set of arguments to the given - template while delaying actually instantiating that template until the full - set of arguments is provided. The "Left" in the name indicates that the - initial arguments are one the left-hand side of the argument list - when the given template is instantiated. - - Essentially, ApplyLeft results in a template that stores Template and Args, - and when that intermediate template is instantiated in turn, it instantiates - Template with Args on the left-hand side of the arguments to Template and - with the arguments to the intermediate template on the right-hand side - - i.e. Args is applied to the left when instantiating Template. - - So, if you have - --- - alias Intermediate = ApplyLeft!(MyTemplate, Arg1, Arg2); - alias Result = Intermediate!(ArgA, ArgB); - --- - then that is equivalent to - --- - alias Result = MyTemplate!(Arg1, Arg2, ArgA, ArgB); - --- - with the difference being that you have an intermediate template which can - be stored or passed to other templates (e.g. as a template predicate). - - The only difference between ApplyLeft and $(LREF ApplyRight) is whether - Args is on the left-hand or the right-hand side of the arguments given to - Template when it's instantiated. - - Note that in many cases, the need for ApplyLeft can be eliminated by making - it so that Template can be partially instantiated. E.G. - --- - enum isSameType(T, U) = is(T == U); - - template isSameType(T) - { - enum isSameType(U) = is(T == U); - } - --- - makes it so that both of these work - --- - enum result1 = isSameType!(int, long); - - alias Intermediate = isSameType!int; - enum result2 = Intermediate!long; - --- - whereas if only the two argument version is provided, then ApplyLeft would - be required for the second use case. - --- - enum result1 = isSameType!(int, long); - - alias Intermediate = ApplyLeft!(isSameType, int); - enum result2 = Intermediate!long; - --- - - See_Also: - $(LREF ApplyRight) - $(LREF Instantiate) - +/ -template ApplyLeft(alias Template, Args...) -{ - alias ApplyLeft(Right...) = Template!(Args, Right); -} - -/// -@safe unittest -{ - { - alias Intermediate = ApplyLeft!(AliasSeq, ubyte, ushort, uint); - alias Result = Intermediate!(char, wchar, dchar); - static assert(is(Result == AliasSeq!(ubyte, ushort, uint, char, wchar, dchar))); - } - { - enum isImplicitlyConvertible(T, U) = is(T : U); - - // i.e. isImplicitlyConvertible!(ubyte, T) is what all is checking for - // with each element in the AliasSeq. - static assert(all!(ApplyLeft!(isImplicitlyConvertible, ubyte), - short, ushort, int, uint, long, ulong)); - } - { - enum hasMember(T, string member) = __traits(hasMember, T, member); - - struct S - { - bool foo; - int bar; - string baz; - } - - static assert(all!(ApplyLeft!(hasMember, S), "foo", "bar", "baz")); - } - { - // Either set of arguments can be empty, since the first set is just - // stored to be applied later, and then when the intermediate template - // is instantiated, they're all applied to the given template in the - // requested order. However, whether the code compiles when - // instantiating the intermediate template depends on what kinds of - // arguments the given template requires. - - alias Intermediate1 = ApplyLeft!AliasSeq; - static assert(Intermediate1!().length == 0); - - enum isSameSize(T, U) = T.sizeof == U.sizeof; - - alias Intermediate2 = ApplyLeft!(isSameSize, int); - static assert(Intermediate2!uint); - - alias Intermediate3 = ApplyLeft!(isSameSize, int, uint); - static assert(Intermediate3!()); - - alias Intermediate4 = ApplyLeft!(isSameSize); - static assert(Intermediate4!(int, uint)); - - // isSameSize requires two arguments - alias Intermediate5 = ApplyLeft!isSameSize; - static assert(!__traits(compiles, Intermediate5!())); - static assert(!__traits(compiles, Intermediate5!int)); - static assert(!__traits(compiles, Intermediate5!(int, long, string))); - } -} - -/++ - ApplyRight does a - $(LINK2 http://en.wikipedia.org/wiki/Partial_application, partial application) - of its arguments, providing a way to bind a set of arguments to the given - template while delaying actually instantiating that template until the full - set of arguments is provided. The "Right" in the name indicates that the - initial arguments are one the right-hand side of the argument list - when the given template is instantiated. - - Essentially, ApplyRight results in a template that stores Template and - Args, and when that intermediate template is instantiated in turn, it - instantiates Template with the arguments to the intermediate template on - the left-hand side and with Args on the right-hand side - i.e. Args is - applied to the right when instantiating Template. - - So, if you have - --- - alias Intermediate = ApplyRight!(MyTemplate, Arg1, Arg2); - alias Result = Intermediate!(ArgA, ArgB); - --- - then that is equivalent to - --- - alias Result = MyTemplate!(ArgA, ArgB, Arg1, Arg2); - --- - with the difference being that you have an intermediate template which can - be stored or passed to other templates (e.g. as a template predicate). - - The only difference between $(LREF ApplyLeft) and ApplyRight is whether - Args is on the left-hand or the right-hand side of the arguments given to - Template when it's instantiated. - - See_Also: - $(LREF ApplyLeft) - $(LREF Instantiate) - +/ -template ApplyRight(alias Template, Args...) -{ - alias ApplyRight(Left...) = Template!(Left, Args); -} - -/// -@safe unittest -{ - { - alias Intermediate = ApplyRight!(AliasSeq, ubyte, ushort, uint); - alias Result = Intermediate!(char, wchar, dchar); - static assert(is(Result == AliasSeq!(char, wchar, dchar, ubyte, ushort, uint))); - } - { - enum isImplicitlyConvertible(T, U) = is(T : U); - - // i.e. isImplicitlyConvertible!(T, short) is what Filter is checking - // for with each element in the AliasSeq. - static assert(is(Filter!(ApplyRight!(isImplicitlyConvertible, short), - ubyte, string, short, float, int) == - AliasSeq!(ubyte, short))); - } - { - enum hasMember(T, string member) = __traits(hasMember, T, member); - - struct S1 - { - bool foo; - } - - struct S2 - { - int foo() { return 42; } - } - - static assert(all!(ApplyRight!(hasMember, "foo"), S1, S2)); - } - { - // Either set of arguments can be empty, since the first set is just - // stored to be applied later, and then when the intermediate template - // is instantiated, they're all applied to the given template in the - // requested order. However, whether the code compiles when - // instantiating the intermediate template depends on what kinds of - // arguments the given template requires. - - alias Intermediate1 = ApplyRight!AliasSeq; - static assert(Intermediate1!().length == 0); - - enum isSameSize(T, U) = T.sizeof == U.sizeof; - - alias Intermediate2 = ApplyRight!(isSameSize, int); - static assert(Intermediate2!uint); - - alias Intermediate3 = ApplyRight!(isSameSize, int, uint); - static assert(Intermediate3!()); - - alias Intermediate4 = ApplyRight!(isSameSize); - static assert(Intermediate4!(int, uint)); - - // isSameSize requires two arguments - alias Intermediate5 = ApplyRight!isSameSize; - static assert(!__traits(compiles, Intermediate5!())); - static assert(!__traits(compiles, Intermediate5!int)); - } -} diff --git a/phobos/phobos/sys/system.d b/phobos/phobos/sys/system.d deleted file mode 100644 index 538db6d..0000000 --- a/phobos/phobos/sys/system.d +++ /dev/null @@ -1,158 +0,0 @@ -// Written in the D programming language. - -/** - * Information about the target operating system, environment, and CPU. - * - * Copyright: Copyright The D Language Foundation 2000 - 2011 - * License: $(HTTP www.boost.org/LICENSE_1_0.txt, Boost License 1.0). - * Authors: $(HTTP digitalmars.com, Walter Bright) and - $(HTTP jmdavisprog.com, Jonathan M Davis) - * Source: $(PHOBOSSRC phobos/sys/system.d) - */ -module phobos.sys.system; - -immutable -{ - /++ - Operating system. - - Note: - This is for cases where you need a value representing the OS at - runtime. If you're doing something which should compile differently - on different OSes, then please use `version (Windows)`, - `version (linux)`, etc. - - See_Also: - $(DDSUBLINK spec/version,PredefinedVersions, Predefined Versions) - +/ - enum OS - { - win32 = 1, /// Microsoft 32 bit Windows systems - win64, /// Microsoft 64 bit Windows systems - linux, /// All Linux Systems, except for Android - osx, /// Mac OS X - iOS, /// iOS - tvOS, /// tvOS - watchOS, /// watchOS - freeBSD, /// FreeBSD - netBSD, /// NetBSD - openBSD, /// OpenBSD - dragonFlyBSD, /// DragonFlyBSD - solaris, /// Solaris - android, /// Android - otherPosix, /// Other Posix Systems - unknown, /// Unknown - } - - /// The OS that the program was compiled for. - version (Win32) OS os = OS.win32; - else version (Win64) OS os = OS.win64; - else version (Android) OS os = OS.android; - else version (linux) OS os = OS.linux; - else version (OSX) OS os = OS.osx; - else version (iOS) OS os = OS.iOS; - else version (tvOS) OS os = OS.tvOS; - else version (watchOS) OS os = OS.watchOS; - else version (FreeBSD) OS os = OS.freeBSD; - else version (NetBSD) OS os = OS.netBSD; - else version (OpenBSD) OS os = OS.openBSD; - else version (DragonFlyBSD) OS os = OS.dragonFlyBSD; - else version (Posix) OS os = OS.otherPosix; - else OS os = OS.unknown; - - /++ - Byte order endianness. - - Note: - This is intended for cases where you need to deal with endianness at - runtime. If you're doing something which should compile differently - depending on whether you're compiling on a big endian or little - endian machine, then please use `version (BigEndian)` and - `version (LittleEndian)`. - - See_Also: - $(DDSUBLINK spec/version,PredefinedVersions, Predefined Versions) - +/ - enum Endian - { - bigEndian, /// Big endian byte order - littleEndian /// Little endian byte order - } - - /// The endianness that the program was compiled for. - version (LittleEndian) Endian endian = Endian.littleEndian; - else Endian endian = Endian.bigEndian; - /++ - Instruction Set Architecture. - - Note: - This is intended for cases where you need a value representing the - instruction set architecture at runtime. If you're doing something - which should compile differently depending on instruction set - architecture, then please use `version (X86_64)`, `version (ARM)`, - etc. - - See_Also: - $(DDSUBLINK spec/version,PredefinedVersions, Predefined Versions) - +/ - enum ISA - { - x86, /// Intel and AMD 32-bit processors - x86_64, /// Intel and AMD 64-bit processors - arm, /// The ARM architecture (32-bit) (AArch32 et al) - aarch64, /// The Advanced RISC Machine architecture (64-bit) - asmJS, /// The asm.js intermediate programming language - avr, /// 8-bit Atmel AVR Microcontrollers - epiphany, /// The Epiphany architecture - ppc, /// The PowerPC architecture, 32-bit - ppc64, /// The PowerPC architecture, 64-bit - ia64, /// The Itanium architecture (64-bit) - mips32, /// The MIPS architecture, 32-bit - mips64, /// The MIPS architecture, 64-bit - msp430, /// The MSP430 architecture - nvptx, /// The Nvidia Parallel Thread Execution (PTX) architecture, 32-bit - nvptx64, /// The Nvidia Parallel Thread Execution (PTX) architecture, 64-bit - riscv32, /// The RISC-V architecture, 32-bit - riscv64, /// The RISC-V architecture, 64-bit - sparc, /// The SPARC architecture, 32-bit - sparc64, /// The SPARC architecture, 64-bit - s390, /// The System/390 architecture, 32-bit - systemZ, /// The System Z architecture, 64-bit - hppa, /// The HP PA-RISC architecture, 32-bit - hppa64, /// The HP PA-RISC architecture, 64-bit - sh, /// The SuperH architecture, 32-bit - webAssembly, /// The WebAssembly virtual ISA (instruction set architecture), 32-bit - alpha, /// The Alpha architecture - unknown, /// Unknown - } - - /// The instruction set architecture that the program was compiled for. - version (X86) ISA instructionSetArchitecture = ISA.x86; - else version (X86_64) ISA instructionSetArchitecture = ISA.x86_64; - else version (ARM) ISA instructionSetArchitecture = ISA.arm; - else version (AArch64) ISA instructionSetArchitecture = ISA.aarch64; - else version (AsmJS) ISA instructionSetArchitecture = ISA.asmJS; - else version (AVR) ISA instructionSetArchitecture = ISA.avr; - else version (Epiphany) ISA instructionSetArchitecture = ISA.epiphany; - else version (PPC) ISA instructionSetArchitecture = ISA.ppc; - else version (PPC64) ISA instructionSetArchitecture = ISA.ppc64; - else version (IA64) ISA instructionSetArchitecture = ISA.ia64; - else version (MIPS32) ISA instructionSetArchitecture = ISA.mips32; - else version (MIPS64) ISA instructionSetArchitecture = ISA.mips64; - else version (MSP430) ISA instructionSetArchitecture = ISA.msp430; - else version (NVPTX) ISA instructionSetArchitecture = ISA.nvptx; - else version (NVPTX64) ISA instructionSetArchitecture = ISA.nvptx64; - else version (RISCV32) ISA instructionSetArchitecture = ISA.riscv32; - else version (RISCV64) ISA instructionSetArchitecture = ISA.riscv64; - else version (SPARC) ISA instructionSetArchitecture = ISA.sparc; - else version (SPARC64) ISA instructionSetArchitecture = ISA.sparc64; - else version (S390) ISA instructionSetArchitecture = ISA.s390; - else version (SystemZ) ISA instructionSetArchitecture = ISA.systemZ; - else version (HPPA) ISA instructionSetArchitecture = ISA.hppa; - else version (HPPA64) ISA instructionSetArchitecture = ISA.hppa64; - else version (SH) ISA instructionSetArchitecture = ISA.sh; - else version (WebAssembly) ISA instructionSetArchitecture = ISA.webAssembly; - else version (Alpha) ISA instructionSetArchitecture = ISA.alpha; - else ISA instructionSetArchitecture = ISA.unknown; -} - diff --git a/phobos/phobos/sys/traits.d b/phobos/phobos/sys/traits.d deleted file mode 100644 index 595dc7a..0000000 --- a/phobos/phobos/sys/traits.d +++ /dev/null @@ -1,4002 +0,0 @@ -// Written in the D programming language -/++ - Templates which extract information about types and symbols at compile time. - - In the context of phobos.sys.traits, a "trait" is a template which provides - information about a type or symbol. Most traits evaluate to - $(D true) or $(D false), telling the code using it whether the given - arguments match / have that specific trait (e.g. whether the given type is - a dynamic array or whether the given function is $(D @safe)). However, some - traits may provide other kinds of information about a type (e.g. the trait - could evaluate to the base type for an enum type, or it could strip - $(D const) from the type to provide the mutable version of that type). - - These traits are then used primarily in template constraints so that they - can test that the template arguments meet the criteria required by those - templates, though they can be useful in a variety of compile-time contexts - (e.g. the condition of a $(D static if)). - - Note that unless otherwise specified, the isXXXX and hasXXX traits in this - module are checking for exact matches, so base types (e.g. with enums) and - other implicit conversions do not factor into whether such traits are true - or false. The type itself is being checked, not what it can be converted - to. - - This is because these traits are often used in templated constraints, and - having a type pass a template constraint based on an implicit conversion - but then not have the implicit conversion actually take place (which it - won't unless the template does something to force it internally) can lead - to either compilation errors or subtle behavioral differences - and even - when the conversion is done explicitly within a templated function, since - it's not done at the call site, it can still lead to subtle bugs in some - cases (e.g. if slicing a static array is involved). - - So, it's typically best to be explicit and clear about a template constraint - accepting any kind of implicit conversion rather than having it buried in a - trait where programmers stand a good chance of using the trait without - realizing that enums might pass based on their base type - or that a type - might pass based on some other implicit conversion. - - Regardless of what a trait is testing for, the documentation strives to be - $(I very) clear about what the trait does, and of course, the names do try - to make it clear as well - though obviously, only so much information can - be put into a name, and some folks will misintrepret some symbols no matter - how well they're named. So, please be sure that you clearly understand what - these traits do when using them, since messing up template constraints can - unfortunately be a great way to introduce subtle bugs into your program. - Either way, of course, unit tests are your friends. - - $(SCRIPT inhibitQuickIndex = 1;) - - $(BOOKTABLE , - $(TR $(TH Category) $(TH Templates)) - $(TR $(TD Categories of types) $(TD - $(LREF isAggregateType) - $(LREF isDynamicArray) - $(LREF isFloatingPoint) - $(LREF isInstantiationOf) - $(LREF isInteger) - $(LREF isNumeric) - $(LREF isPointer) - $(LREF isSignedInteger) - $(LREF isStaticArray) - $(LREF isUnsignedInteger) - )) - $(TR $(TD Aggregate Type traits) $(TD - $(LREF EnumMembers) - )) - $(TR $(TD Traits testing for type conversions) $(TD - $(LREF isImplicitlyConvertible) - $(LREF isQualifierConvertible) - )) - $(TR $(TD Traits for comparisons) $(TD - $(LREF isEqual) - $(LREF isSameSymbol) - $(LREF isSameType) - )) - $(TR $(TD Aggregate Type Traits) $(TD - $(LREF FieldNames) - $(LREF FieldSymbols) - $(LREF FieldTypes) - )) - $(TR $(TD General Types) $(TD - $(LREF KeyType) - $(LREF OriginalType) - $(LREF ValueType) - )) - $(TR $(TD Traits for removing type qualfiers) $(TD - $(LREF Unconst) - $(LREF Unshared) - $(LREF Unqualified) - )) - $(TR $(TD Type Constructors) $(TD - $(LREF ConstOf) - $(LREF ImmutableOf) - $(LREF InoutOf) - $(LREF SharedOf) - )) - $(TR $(TD Misc) $(TD - $(LREF lvalueOf) - $(LREF rvalueOf) - )) - ) - - Copyright: Copyright The D Language Foundation 2005 - 2024. - License: $(HTTP www.boost.org/LICENSE_1_0.txt, Boost License 1.0). - Authors: $(HTTP jmdavisprog.com, Jonathan M Davis) - $(HTTP digitalmars.com, Walter Bright), - Tomasz Stachowiak (`isExpressions`), - $(HTTP erdani.org, Andrei Alexandrescu), - Shin Fujishiro, - $(HTTP octarineparrot.com, Robert Clipsham), - $(HTTP klickverbot.at, David Nadlinger), - Kenji Hara, - Shoichi Kato - Source: $(PHOBOSSRC phobos/sys/traits) -+/ -module phobos.sys.traits; - -/++ - Whether the given type is an "aggregate type" - i.e. a struct, class, - interface, or union. - +/ -enum isAggregateType(T) = is(T == struct) || is(T == class) || is(T == interface) || is(T == union); - -@safe unittest -{ - struct S {} - class C {} - interface I {} - union U {} - - static assert( isAggregateType!S); - static assert( isAggregateType!C); - static assert( isAggregateType!I); - static assert( isAggregateType!U); - static assert( isAggregateType!(const S)); - static assert( isAggregateType!(shared C)); - - static assert(!isAggregateType!int); - static assert(!isAggregateType!string); - static assert(!isAggregateType!(S*)); - static assert(!isAggregateType!(C[])); - static assert(!isAggregateType!(I[string])); -} - -/++ - Whether the given type is a dynamic array (or what is sometimes referred to - as a slice, since a dynamic array in D is a slice of memory). - - Note that this does not include implicit conversions or enum types. The - type itself must be a dynamic array. - - Remember that D's dynamic arrays are essentially: - --- - struct DynamicArray(T) - { - size_t length; - T* ptr; - } - --- - where $(D ptr) points to the first element in the array, and $(D length) is - the number of elements in the array. - - A dynamic array is not a pointer (unlike arrays in C/C++), and its elements - do not live inside the dynamic array itself. The dynamic array is simply a - slice of memory and does not own or manage its own memory. It can be a - slice of any piece of memory, including GC-allocated memory, the stack, - malloc-ed memory, etc. (with what kind of memory it is of course being - determined by how the dynamic array was created in the first place) - - though if you do any operations on it which end up requiring allocation - (e.g. appending to it if it doesn't have the capacity to expand in-place, - which it won't if it isn't a slice of GC-allocated memory), then that - reallocation will result in the dynamic array being a slice of newly - allocated, GC-backed memory (regardless of what it was a slice of before), - since it's the GC that deals with those allocations. - - As long as code just accesses the elements or members of the dynamic array - - or reduces its length so that it's a smaller slice - it will continue to - point to whatever block of memory it pointed to originally. And because the - GC makes sure that appending to a dynamic array does not stomp on the - memory of any other dynamic arrays, appending to a dynamic array will not - affect any other dynamic array which is a slice of that same block of - memory whether a reallocation occurs or not. - - Regardless, since what allocated the memory that the dynamic array is a - slice of is irrevelant to the type of the dynamic array, whether a given - type is a dynamic array has nothing to do with the kind of memory that's - backing it. A dynamic array which is a slice of a static array of $(D int) - is the the same type as a dynamic array of $(D int) allocated with $(D new) - - i.e. both are $(D int[]). So, this trait will not tell you anything about - what kind of memory a dynamic array is a slice of. It just tells you - whether the type is a dynamic array or not. - - If for some reason, it matters for a function what kind of memory backs one - of its parameters which is a dynamic array, or it needs to be made clear - whether the function will possibly cause that dynamic array to be - reallocated, then that needs to be indicated by the documentation and - cannot be enforced with a template constraint. A template constraint can - enforce that a type used with a template meets certain criteria (e.g. that - it's a dynamic array), but it cannot enforce anything about how the - template actually uses the type. - - However, it $(D is) possible to enforce that a function doesn't use any - operations on a dynamic array which might cause it to be reallocated by - marking that function as $(D @nogc). - - In most cases though, code can be written to not care what kind of memory - backs a dynamic array, because none of the operations on a dynamic array - actually care what kind of memory it's a slice of. It mostly just matters - when you need to track the lifetime of the memory, because it wasn't - allocated by the GC, or when it matters whether a dynamic array could be - reallocated or not (e.g. because the code needs to have that dynamic array - continue to point to the same block of memory). - - See_Also: - $(LREF isPointer) - $(LREF isStaticArray) - $(DDSUBLINK spec/arrays, , The language spec for arrays) - +/ -enum isDynamicArray(T) = is(T == U[], U); - -/// -@safe unittest -{ - // Some types which are dynamic arrays. - static assert( isDynamicArray!(int[])); - static assert( isDynamicArray!(const int[])); - static assert( isDynamicArray!(inout int[])); - static assert( isDynamicArray!(shared(int)[])); - static assert( isDynamicArray!string); - - static assert( isDynamicArray!(typeof([1, 2, 3]))); - static assert( isDynamicArray!(typeof("dlang"))); - - int[] arr; - static assert( isDynamicArray!(typeof(arr))); - - // Some types which aren't dynamic arrays. - static assert(!isDynamicArray!int); - static assert(!isDynamicArray!(int*)); - static assert(!isDynamicArray!real); - - static struct S - { - int[] arr; - } - static assert(!isDynamicArray!S); - - // The struct itself isn't considered a dynamic array, - // but its member variable is when checked directly. - static assert( isDynamicArray!(typeof(S.arr))); - - // Static arrays. - static assert(!isDynamicArray!(int[5])); - static assert(!isDynamicArray!(const(int)[5])); - - int[2] sArr = [42, 97]; - static assert(!isDynamicArray!(typeof(sArr))); - - // While a static array is not a dynamic array, - // a slice of a static array is a dynamic array. - static assert( isDynamicArray!(typeof(sArr[]))); - - // Dynamic array of static arrays. - static assert( isDynamicArray!(long[3][])); - - // Static array of dynamic arrays. - static assert(!isDynamicArray!(long[][3])); - - // Associative array. - static assert(!isDynamicArray!(int[string])); - - // While typeof(null) gets treated as void[] in some contexts, it is - // distinct from void[] and is not considered to be a dynamic array. - static assert(!isDynamicArray!(typeof(null))); - - // However, naturally, if null is cast to a dynamic array, it's a - // dynamic array, since the cast forces the type. - static assert( isDynamicArray!(typeof(cast(int[]) null))); - - enum E : int[] - { - a = [1, 2, 3], - } - - // Enums do not count. - static assert(!isDynamicArray!E); - - static struct AliasThis - { - int[] arr; - alias this = arr; - } - - // Other implicit conversions do not count. - static assert(!isDynamicArray!AliasThis); -} - -@safe unittest -{ - import phobos.sys.meta : Alias, AliasSeq; - - static struct AliasThis(T) - { - T member; - alias this = member; - } - - foreach (Q; AliasSeq!(Alias, ConstOf, ImmutableOf, SharedOf)) - { - foreach (T; AliasSeq!(int[], char[], string, long[3][], double[string][])) - { - enum E : Q!T { a = Q!T.init } - - static assert( isDynamicArray!(Q!T)); - static assert(!isDynamicArray!E); - static assert(!isDynamicArray!(AliasThis!(Q!T))); - } - - foreach (T; AliasSeq!(int, int[51], int[][2], - char[][int][11], immutable char[13u], - const(real)[1], const(real)[1][1], void[0])) - { - enum E : Q!T { a = Q!T.init } - - static assert(!isDynamicArray!(Q!T)); - static assert(!isDynamicArray!E); - static assert(!isDynamicArray!(AliasThis!(Q!T))); - } - } -} - -/++ - Whether type $(D T) is a static array. - - Note that this does not include implicit conversions or enum types. The - type itself must be a static array. This is in contrast to - $(D __traits(isStaticArray, T)) which is true for enums (but not for other - implict conversions to static arrays). - - As explained in the module documentation, traits like this one are not true - for enums (unlike most of the $(D __traits) traits) in order to avoid - testing for implicit conversions by default with template constraints, - since that tends to lead to subtle bugs when the code isn't carefully - written to take implicit conversions into account. - - See also: - $(DDSUBLINK spec/traits, isStaticArray, $(D __traits(isStaticArray, T))) - $(DDSUBLINK spec/arrays, , The language spec for arrays) - +/ -enum isStaticArray(T) = is(T == U[n], U, size_t n); - -/// -@safe unittest -{ - // Some types which are static arrays. - static assert( isStaticArray!(int[12])); - static assert( isStaticArray!(const int[42])); - static assert( isStaticArray!(inout int[0])); - static assert( isStaticArray!(shared(int)[907])); - static assert( isStaticArray!(immutable(char)[5])); - - // D doesn't have static array literals, but you get the same effect - // by casting a dynamic array literal to a static array, and of course, - // the result is typed as a static array. - static assert( isStaticArray!(typeof(cast(int[3]) [1, 2, 3]))); - - int[2] sArr = [1, 2]; - static assert( isStaticArray!(typeof(sArr))); - - // Some types which are not static arrays. - static assert(!isStaticArray!int); - static assert(!isStaticArray!(int*)); - static assert(!isStaticArray!real); - - static struct S - { - int[4] arr; - } - static assert(!isStaticArray!S); - - // The struct itself isn't considered a static array, - // but its member variable is when checked directly. - static assert( isStaticArray!(typeof(S.arr))); - - // Dynamic arrays. - static assert(!isStaticArray!(int[])); - static assert(!isStaticArray!(const(int)[])); - static assert(!isStaticArray!string); - - int[] arr; - static assert(!isStaticArray!(typeof(arr))); - - // A slice of a static array is of course not a static array, - // because it's a dynamic array. - static assert(!isStaticArray!(typeof(sArr[]))); - - // Static array of dynamic arrays. - static assert( isStaticArray!(long[][3])); - - // Dynamic array of static arrays. - static assert(!isStaticArray!(long[3][])); - - // Associative array. - static assert(!isStaticArray!(int[string])); - - // Of course, null is not considered to be a static array. - static assert(!isStaticArray!(typeof(null))); - - enum E : int[3] - { - a = [1, 2, 3], - } - - // Enums do not count. - static assert(!isStaticArray!E); - - // This is where isStaticArray differs from __traits(isStaticArray, ...) - static assert( __traits(isStaticArray, E)); - - static struct AliasThis - { - int[] arr; - alias this = arr; - } - - // Other implicit conversions do not count. - static assert(!isStaticArray!AliasThis); - - static assert(!__traits(isStaticArray, AliasThis)); -} - -@safe unittest -{ - import phobos.sys.meta : Alias, AliasSeq; - - static struct AliasThis(T) - { - T member; - alias this = member; - } - - foreach (Q; AliasSeq!(Alias, ConstOf, ImmutableOf, SharedOf)) - { - foreach (T; AliasSeq!(int[51], int[][2], - char[][int][11], immutable char[13u], - const(real)[1], const(real)[1][1], void[0])) - { - enum E : Q!T { a = Q!T.init, } - - static assert( isStaticArray!(Q!T)); - static assert(!isStaticArray!E); - static assert(!isStaticArray!(AliasThis!(Q!T))); - } - - foreach (T; AliasSeq!(int, int[], char[], string, long[3][], double[string][])) - { - enum E : Q!T { a = Q!T.init, } - - static assert(!isStaticArray!(Q!T)); - static assert(!isStaticArray!E); - static assert(!isStaticArray!(AliasThis!(Q!T))); - } - } -} - -/++ - Whether the given type is one of the built-in integer types, ignoring all - qualifiers. - - $(TABLE - $(TR $(TH Integer Types)) - $(TR $(TD byte)) - $(TR $(TD ubyte)) - $(TR $(TD short)) - $(TR $(TD ushort)) - $(TR $(TD int)) - $(TR $(TD uint)) - $(TR $(TD long)) - $(TR $(TD ulong)) - ) - - Note that this does not include implicit conversions or enum types. The - type itself must be one of the built-in integer types. - - This trait does have some similarities with $(D __traits(isIntegral, T)), - but $(D isIntegral) accepts a $(I lot) more types than isInteger does. - isInteger is specifically for testing for the built-in integer types, - whereas $(D isIntegral) tests for a whole set of types that are vaguely - integer-like (including $(D bool), the three built-in character types, and - some of the vector types from core.simd). So, for most code, isInteger is - going to be more appropriate, but obviously, it depends on what the code is - trying to do. - - See also: - $(DDSUBLINK spec/traits, isIntegral, $(D __traits(isIntegral, T))) - $(LREF isFloatingPoint) - $(LREF isSignedInteger) - $(LREF isNumeric) - $(LREF isUnsignedInteger) - +/ -enum isInteger(T) = is(immutable T == immutable byte) || - is(immutable T == immutable ubyte) || - is(immutable T == immutable short) || - is(immutable T == immutable ushort) || - is(immutable T == immutable int) || - is(immutable T == immutable uint) || - is(immutable T == immutable long) || - is(immutable T == immutable ulong); - -/// -@safe unittest -{ - // Some types which are integer types. - static assert( isInteger!byte); - static assert( isInteger!ubyte); - static assert( isInteger!short); - static assert( isInteger!ushort); - static assert( isInteger!int); - static assert( isInteger!uint); - static assert( isInteger!long); - static assert( isInteger!ulong); - - static assert( isInteger!(const ubyte)); - static assert( isInteger!(immutable short)); - static assert( isInteger!(inout int)); - static assert( isInteger!(shared uint)); - static assert( isInteger!(const shared ulong)); - - static assert( isInteger!(typeof(42))); - static assert( isInteger!(typeof(1234567890L))); - - int i; - static assert( isInteger!(typeof(i))); - - // Some types which aren't integer types. - static assert(!isInteger!bool); - static assert(!isInteger!char); - static assert(!isInteger!wchar); - static assert(!isInteger!dchar); - static assert(!isInteger!(int[])); - static assert(!isInteger!(ubyte[4])); - static assert(!isInteger!(int*)); - static assert(!isInteger!double); - static assert(!isInteger!string); - - static struct S - { - int i; - } - static assert(!isInteger!S); - - // The struct itself isn't considered an integer, - // but its member variable is when checked directly. - static assert( isInteger!(typeof(S.i))); - - enum E : int - { - a = 42 - } - - // Enums do not count. - static assert(!isInteger!E); - - static struct AliasThis - { - int i; - alias this = i; - } - - // Other implicit conversions do not count. - static assert(!isInteger!AliasThis); -} - -@safe unittest -{ - import phobos.sys.meta : Alias, AliasSeq; - - static struct AliasThis(T) - { - T member; - alias this = member; - } - - // The actual core.simd types available vary from system to system, so we - // have to be a bit creative here. The reason that we're testing these types - // is because __traits(isIntegral, T) accepts them, but isInteger is not - // supposed to. - template SIMDTypes() - { - import core.simd; - - alias SIMDTypes = AliasSeq!(); - static if (is(ubyte16)) - SIMDTypes = AliasSeq!(SIMDTypes, ubyte16); - static if (is(int4)) - SIMDTypes = AliasSeq!(SIMDTypes, int4); - static if (is(double2)) - SIMDTypes = AliasSeq!(SIMDTypes, double2); - static if (is(void16)) - SIMDTypes = AliasSeq!(SIMDTypes, void16); - } - - foreach (Q; AliasSeq!(Alias, ConstOf, ImmutableOf, SharedOf)) - { - foreach (T; AliasSeq!(byte, ubyte, short, ushort, int, uint, long, ulong)) - { - enum E : Q!T { a = Q!T.init } - - static assert( isInteger!(Q!T)); - static assert(!isInteger!E); - static assert(!isInteger!(AliasThis!(Q!T))); - } - - foreach (T; AliasSeq!(bool, char, wchar, dchar, float, double, real, SIMDTypes!(), - int[], ubyte[8], dchar[], void[], long*)) - { - enum E : Q!T { a = Q!T.init } - - static assert(!isInteger!(Q!T)); - static assert(!isInteger!E); - static assert(!isInteger!(AliasThis!(Q!T))); - } - } -} - -/++ - Whether the given type is one of the built-in signed integer types, ignoring - all qualifiers. - - $(TABLE - $(TR $(TH Signed Integer Types)) - $(TR $(TD byte)) - $(TR $(TD short)) - $(TR $(TD int)) - $(TR $(TD long)) - ) - - Note that this does not include implicit conversions or enum types. The - type itself must be one of the built-in signed integer types. - - See also: - $(LREF isFloatingPoint) - $(LREF isInteger) - $(LREF isNumeric) - $(LREF isUnsignedInteger) - +/ -enum isSignedInteger(T) = is(immutable T == immutable byte) || - is(immutable T == immutable short) || - is(immutable T == immutable int) || - is(immutable T == immutable long); - -/// -@safe unittest -{ - // Some types which are signed integer types. - static assert( isSignedInteger!byte); - static assert( isSignedInteger!short); - static assert( isSignedInteger!int); - static assert( isSignedInteger!long); - - static assert( isSignedInteger!(const byte)); - static assert( isSignedInteger!(immutable short)); - static assert( isSignedInteger!(inout int)); - static assert( isSignedInteger!(shared int)); - static assert( isSignedInteger!(const shared long)); - - static assert( isSignedInteger!(typeof(42))); - static assert( isSignedInteger!(typeof(1234567890L))); - - int i; - static assert( isSignedInteger!(typeof(i))); - - // Some types which aren't signed integer types. - static assert(!isSignedInteger!ubyte); - static assert(!isSignedInteger!ushort); - static assert(!isSignedInteger!uint); - static assert(!isSignedInteger!ulong); - - static assert(!isSignedInteger!bool); - static assert(!isSignedInteger!char); - static assert(!isSignedInteger!wchar); - static assert(!isSignedInteger!dchar); - static assert(!isSignedInteger!(int[])); - static assert(!isSignedInteger!(ubyte[4])); - static assert(!isSignedInteger!(int*)); - static assert(!isSignedInteger!double); - static assert(!isSignedInteger!string); - - static struct S - { - int i; - } - static assert(!isSignedInteger!S); - - // The struct itself isn't considered a signed integer, - // but its member variable is when checked directly. - static assert( isSignedInteger!(typeof(S.i))); - - enum E : int - { - a = 42 - } - - // Enums do not count. - static assert(!isSignedInteger!E); - - static struct AliasThis - { - int i; - alias this = i; - } - - // Other implicit conversions do not count. - static assert(!isSignedInteger!AliasThis); -} - -@safe unittest -{ - import phobos.sys.meta : Alias, AliasSeq; - - static struct AliasThis(T) - { - T member; - alias this = member; - } - - // The actual core.simd types available vary from system to system, so we - // have to be a bit creative here. The reason that we're testing these types - // is because __traits(isIntegral, T) accepts them, but isSignedInteger is - // not supposed to. - template SIMDTypes() - { - import core.simd; - - alias SIMDTypes = AliasSeq!(); - static if (is(ubyte16)) - SIMDTypes = AliasSeq!(SIMDTypes, ubyte16); - static if (is(int4)) - SIMDTypes = AliasSeq!(SIMDTypes, int4); - static if (is(double2)) - SIMDTypes = AliasSeq!(SIMDTypes, double2); - static if (is(void16)) - SIMDTypes = AliasSeq!(SIMDTypes, void16); - } - - foreach (Q; AliasSeq!(Alias, ConstOf, ImmutableOf, SharedOf)) - { - foreach (T; AliasSeq!(byte, short, int, long)) - { - enum E : Q!T { a = Q!T.init } - - static assert( isSignedInteger!(Q!T)); - static assert(!isSignedInteger!E); - static assert(!isSignedInteger!(AliasThis!(Q!T))); - } - - foreach (T; AliasSeq!(ubyte, ushort, uint, ulong, - bool, char, wchar, dchar, float, double, real, SIMDTypes!(), - int[], ubyte[8], dchar[], void[], long*)) - { - enum E : Q!T { a = Q!T.init } - - static assert(!isSignedInteger!(Q!T)); - static assert(!isSignedInteger!E); - static assert(!isSignedInteger!(AliasThis!(Q!T))); - } - } -} - -/++ - Whether the given type is one of the built-in unsigned integer types, - ignoring all qualifiers. - - $(TABLE - $(TR $(TH Integer Types)) - $(TR $(TD ubyte)) - $(TR $(TD ushort)) - $(TR $(TD uint)) - $(TR $(TD ulong)) - ) - - Note that this does not include implicit conversions or enum types. The - type itself must be one of the built-in unsigned integer types. - - This trait does have some similarities with $(D __traits(isUnsigned, T)), - but $(D isUnsigned) accepts a $(I lot) more types than isUnsignedInteger - does. isUnsignedInteger is specifically for testing for the built-in - unsigned integer types, whereas $(D isUnsigned) tests for a whole set of - types that are unsigned and vaguely integer-like (including $(D bool), the - three built-in character types, and some of the vector types from - core.simd). So, for most code, isUnsignedInteger is going to be more - appropriate, but obviously, it depends on what the code is trying to do. - - See also: - $(DDSUBLINK spec/traits, isUnsigned, $(D __traits(isUnsigned, T))) - $(LREF isFloatingPoint) - $(LREF isInteger) - $(LREF isSignedInteger) - $(LREF isNumeric) - +/ -enum isUnsignedInteger(T) = is(immutable T == immutable ubyte) || - is(immutable T == immutable ushort) || - is(immutable T == immutable uint) || - is(immutable T == immutable ulong); - -/// -@safe unittest -{ - // Some types which are unsigned integer types. - static assert( isUnsignedInteger!ubyte); - static assert( isUnsignedInteger!ushort); - static assert( isUnsignedInteger!uint); - static assert( isUnsignedInteger!ulong); - - static assert( isUnsignedInteger!(const ubyte)); - static assert( isUnsignedInteger!(immutable ushort)); - static assert( isUnsignedInteger!(inout uint)); - static assert( isUnsignedInteger!(shared uint)); - static assert( isUnsignedInteger!(const shared ulong)); - - static assert( isUnsignedInteger!(typeof(42u))); - static assert( isUnsignedInteger!(typeof(1234567890UL))); - - uint u; - static assert( isUnsignedInteger!(typeof(u))); - - // Some types which aren't unsigned integer types. - static assert(!isUnsignedInteger!byte); - static assert(!isUnsignedInteger!short); - static assert(!isUnsignedInteger!int); - static assert(!isUnsignedInteger!long); - - static assert(!isUnsignedInteger!bool); - static assert(!isUnsignedInteger!char); - static assert(!isUnsignedInteger!wchar); - static assert(!isUnsignedInteger!dchar); - static assert(!isUnsignedInteger!(int[])); - static assert(!isUnsignedInteger!(ubyte[4])); - static assert(!isUnsignedInteger!(int*)); - static assert(!isUnsignedInteger!double); - static assert(!isUnsignedInteger!string); - - static struct S - { - uint u; - } - static assert(!isUnsignedInteger!S); - - // The struct itself isn't considered an unsigned integer, - // but its member variable is when checked directly. - static assert( isUnsignedInteger!(typeof(S.u))); - - enum E : uint - { - a = 42 - } - - // Enums do not count. - static assert(!isUnsignedInteger!E); - - static struct AliasThis - { - uint u; - alias this = u; - } - - // Other implicit conversions do not count. - static assert(!isUnsignedInteger!AliasThis); -} - -@safe unittest -{ - import phobos.sys.meta : Alias, AliasSeq; - - static struct AliasThis(T) - { - T member; - alias this = member; - } - - // The actual core.simd types available vary from system to system, so we - // have to be a bit creative here. The reason that we're testing these types - // is because __traits(isIntegral, T) and __traits(isUnsigned, T) accept - // them, but isUnsignedInteger is not supposed to. - template SIMDTypes() - { - import core.simd; - - alias SIMDTypes = AliasSeq!(); - static if (is(ubyte16)) - SIMDTypes = AliasSeq!(SIMDTypes, ubyte16); - static if (is(int4)) - SIMDTypes = AliasSeq!(SIMDTypes, int4); - static if (is(double2)) - SIMDTypes = AliasSeq!(SIMDTypes, double2); - static if (is(void16)) - SIMDTypes = AliasSeq!(SIMDTypes, void16); - } - - foreach (Q; AliasSeq!(Alias, ConstOf, ImmutableOf, SharedOf)) - { - foreach (T; AliasSeq!(ubyte, ushort, uint, ulong)) - { - enum E : Q!T { a = Q!T.init } - - static assert( isUnsignedInteger!(Q!T)); - static assert(!isUnsignedInteger!E); - static assert(!isUnsignedInteger!(AliasThis!(Q!T))); - } - - foreach (T; AliasSeq!(byte, short, int, long, - bool, char, wchar, dchar, float, double, real, SIMDTypes!(), - int[], ubyte[8], dchar[], void[], long*)) - { - enum E : Q!T { a = Q!T.init } - - static assert(!isUnsignedInteger!(Q!T)); - static assert(!isUnsignedInteger!E); - static assert(!isUnsignedInteger!(AliasThis!(Q!T))); - } - } -} - -/++ - Whether the given type is one of the built-in floating-point types, ignoring - all qualifiers. - - $(TABLE - $(TR $(TH Floating-Point Types)) - $(TR $(TD float)) - $(TR $(TD double)) - $(TR $(TD real)) - ) - - Note that this does not include implicit conversions or enum types. The - type itself must be one of the built-in floating-point types. - - This trait does have some similarities with $(D __traits(isFloating, T)), - but $(D isFloating) accepts more types than isFloatingPoint does. - isFloatingPoint is specifically for testing for the built-in floating-point - types, whereas $(D isFloating) tests for a whole set of types that are - vaguely float-like (including enums with a base type which is a - floating-point type and some of the vector types from core.simd). So, for - most code, isFloatingPoint is going to be more appropriate, but obviously, - it depends on what the code is trying to do. - - See also: - $(DDSUBLINK spec/traits, isFloating, $(D __traits(isFloating, T))) - $(LREF isInteger) - $(LREF isSignedInteger) - $(LREF isNumeric) - $(LREF isUnsignedInteger) - +/ -enum isFloatingPoint(T) = is(immutable T == immutable float) || - is(immutable T == immutable double) || - is(immutable T == immutable real); - -/// -@safe unittest -{ - // Some types which are floating-point types. - static assert( isFloatingPoint!float); - static assert( isFloatingPoint!double); - static assert( isFloatingPoint!real); - - static assert( isFloatingPoint!(const float)); - static assert( isFloatingPoint!(immutable float)); - static assert( isFloatingPoint!(inout double)); - static assert( isFloatingPoint!(shared double)); - static assert( isFloatingPoint!(const shared real)); - - static assert( isFloatingPoint!(typeof(42.0))); - static assert( isFloatingPoint!(typeof(42f))); - static assert( isFloatingPoint!(typeof(1e5))); - static assert( isFloatingPoint!(typeof(97.4L))); - - double d; - static assert( isFloatingPoint!(typeof(d))); - - // Some types which aren't floating-point types. - static assert(!isFloatingPoint!bool); - static assert(!isFloatingPoint!char); - static assert(!isFloatingPoint!dchar); - static assert(!isFloatingPoint!int); - static assert(!isFloatingPoint!long); - static assert(!isFloatingPoint!(float[])); - static assert(!isFloatingPoint!(double[4])); - static assert(!isFloatingPoint!(real*)); - static assert(!isFloatingPoint!string); - - static struct S - { - double d; - } - static assert(!isFloatingPoint!S); - - // The struct itself isn't considered a floating-point type, - // but its member variable is when checked directly. - static assert( isFloatingPoint!(typeof(S.d))); - - enum E : double - { - a = 12.34 - } - - // Enums do not count. - static assert(!isFloatingPoint!E); - - static struct AliasThis - { - double d; - alias this = d; - } - - // Other implicit conversions do not count. - static assert(!isFloatingPoint!AliasThis); -} - -@safe unittest -{ - import phobos.sys.meta : Alias, AliasSeq; - - static struct AliasThis(T) - { - T member; - alias this = member; - } - - // The actual core.simd types available vary from system to system, so we - // have to be a bit creative here. The reason that we're testing these types - // is because __traits(isFloating, T) accepts them, but isFloatingPoint is - // not supposed to. - template SIMDTypes() - { - import core.simd; - - alias SIMDTypes = AliasSeq!(); - static if (is(int4)) - SIMDTypes = AliasSeq!(SIMDTypes, int4); - static if (is(double2)) - SIMDTypes = AliasSeq!(SIMDTypes, double2); - static if (is(void16)) - SIMDTypes = AliasSeq!(SIMDTypes, void16); - } - - foreach (Q; AliasSeq!(Alias, ConstOf, ImmutableOf, SharedOf)) - { - foreach (T; AliasSeq!(float, double, real)) - { - enum E : Q!T { a = Q!T.init } - - static assert( isFloatingPoint!(Q!T)); - static assert(!isFloatingPoint!E); - static assert(!isFloatingPoint!(AliasThis!(Q!T))); - } - - foreach (T; AliasSeq!(bool, char, wchar, dchar, byte, ubyte, short, ushort, - int, uint, long, ulong, SIMDTypes!(), - int[], float[8], real[], void[], double*)) - { - enum E : Q!T { a = Q!T.init } - - static assert(!isFloatingPoint!(Q!T)); - static assert(!isFloatingPoint!E); - static assert(!isFloatingPoint!(AliasThis!(Q!T))); - } - } -} - -/++ - Whether the given type is one of the built-in numeric types, ignoring all - qualifiers. It's equivalent to $(D isInteger!T || isFloatingPoint!T), but - it only involves a single template instantation instead of two. - - $(TABLE - $(TR $(TH Numeric Types)) - $(TR $(TD byte)) - $(TR $(TD ubyte)) - $(TR $(TD short)) - $(TR $(TD ushort)) - $(TR $(TD int)) - $(TR $(TD uint)) - $(TR $(TD long)) - $(TR $(TD ulong)) - $(TR $(TD float)) - $(TR $(TD double)) - $(TR $(TD real)) - ) - - Note that this does not include implicit conversions or enum types. The - type itself must be one of the built-in numeric types. - - See_Also: - $(LREF isFloatingPoint) - $(LREF isInteger) - $(LREF isSignedInteger) - $(LREF isUnsignedInteger) - +/ -enum isNumeric(T) = is(immutable T == immutable byte) || - is(immutable T == immutable ubyte) || - is(immutable T == immutable short) || - is(immutable T == immutable ushort) || - is(immutable T == immutable int) || - is(immutable T == immutable uint) || - is(immutable T == immutable long) || - is(immutable T == immutable ulong) || - is(immutable T == immutable float) || - is(immutable T == immutable double) || - is(immutable T == immutable real); - -/// -@safe unittest -{ - // Some types which are numeric types. - static assert( isNumeric!byte); - static assert( isNumeric!ubyte); - static assert( isNumeric!short); - static assert( isNumeric!ushort); - static assert( isNumeric!int); - static assert( isNumeric!uint); - static assert( isNumeric!long); - static assert( isNumeric!ulong); - static assert( isNumeric!float); - static assert( isNumeric!double); - static assert( isNumeric!real); - - static assert( isNumeric!(const short)); - static assert( isNumeric!(immutable int)); - static assert( isNumeric!(inout uint)); - static assert( isNumeric!(shared long)); - static assert( isNumeric!(const shared real)); - - static assert( isNumeric!(typeof(42))); - static assert( isNumeric!(typeof(1234657890L))); - static assert( isNumeric!(typeof(42.0))); - static assert( isNumeric!(typeof(42f))); - static assert( isNumeric!(typeof(1e5))); - static assert( isNumeric!(typeof(97.4L))); - - int i; - static assert( isNumeric!(typeof(i))); - - // Some types which aren't numeric types. - static assert(!isNumeric!bool); - static assert(!isNumeric!char); - static assert(!isNumeric!dchar); - static assert(!isNumeric!(int[])); - static assert(!isNumeric!(double[4])); - static assert(!isNumeric!(real*)); - static assert(!isNumeric!string); - - static struct S - { - int i; - } - static assert(!isNumeric!S); - - // The struct itself isn't considered a numeric type, - // but its member variable is when checked directly. - static assert( isNumeric!(typeof(S.i))); - - enum E : int - { - a = 42 - } - - // Enums do not count. - static assert(!isNumeric!E); - - static struct AliasThis - { - int i; - alias this = i; - } - - // Other implicit conversions do not count. - static assert(!isNumeric!AliasThis); -} - -@safe unittest -{ - import phobos.sys.meta : Alias, AliasSeq; - - static struct AliasThis(T) - { - T member; - alias this = member; - } - - // The actual core.simd types available vary from system to system, so we - // have to be a bit creative here. The reason that we're testing these types - // is because __traits(isInteger, T) and __traits(isFloating, T) accept - // them, but isNumeric is not supposed to. - template SIMDTypes() - { - import core.simd; - - alias SIMDTypes = AliasSeq!(); - static if (is(int4)) - SIMDTypes = AliasSeq!(SIMDTypes, int4); - static if (is(double2)) - SIMDTypes = AliasSeq!(SIMDTypes, double2); - static if (is(void16)) - SIMDTypes = AliasSeq!(SIMDTypes, void16); - } - - foreach (Q; AliasSeq!(Alias, ConstOf, ImmutableOf, SharedOf)) - { - foreach (T; AliasSeq!(byte, ubyte, short, ushort, int, uint, long, ulong, float, double, real)) - { - enum E : Q!T { a = Q!T.init } - - static assert( isNumeric!(Q!T)); - static assert(!isNumeric!E); - static assert(!isNumeric!(AliasThis!(Q!T))); - } - - foreach (T; AliasSeq!(bool, char, wchar, dchar, SIMDTypes!(), - int[], float[8], real[], void[], double*)) - { - enum E : Q!T { a = Q!T.init } - - static assert(!isNumeric!(Q!T)); - static assert(!isNumeric!E); - static assert(!isNumeric!(AliasThis!(Q!T))); - } - } -} - -/++ - Whether the given type is a pointer. - - Note that this does not include implicit conversions or enum types. The - type itself must be a pointer. - - Also, remember that unlike C/C++, D's arrays are not pointers. Rather, a - dynamic array in D is a slice of memory which has a member which is a - pointer to its first element and another member which is the length of the - array as $(D size_t). So, a dynamic array / slice has a $(D ptr) member - which is a pointer, but the dynamic array itself is not a pointer. - - See_Also: - $(LREF isDynamicArray) - +/ -enum isPointer(T) = is(T == U*, U); - -/// -@system unittest -{ - // Some types which are pointers. - static assert( isPointer!(bool*)); - static assert( isPointer!(int*)); - static assert( isPointer!(int**)); - static assert( isPointer!(real*)); - static assert( isPointer!(string*)); - - static assert( isPointer!(const int*)); - static assert( isPointer!(immutable int*)); - static assert( isPointer!(inout int*)); - static assert( isPointer!(shared int*)); - static assert( isPointer!(const shared int*)); - - static assert( isPointer!(typeof("foobar".ptr))); - - int* ptr; - static assert( isPointer!(typeof(ptr))); - - int i; - static assert( isPointer!(typeof(&i))); - - // Some types which aren't pointers. - static assert(!isPointer!bool); - static assert(!isPointer!int); - static assert(!isPointer!dchar); - static assert(!isPointer!(int[])); - static assert(!isPointer!(double[4])); - static assert(!isPointer!string); - - static struct S - { - int* ptr; - } - static assert(!isPointer!S); - - // The struct itself isn't considered a numeric type, - // but its member variable is when checked directly. - static assert( isPointer!(typeof(S.ptr))); - - enum E : immutable(char*) - { - a = "foobar".ptr - } - - // Enums do not count. - static assert(!isPointer!E); - - static struct AliasThis - { - int* ptr; - alias this = ptr; - } - - // Other implicit conversions do not count. - static assert(!isPointer!AliasThis); -} - -@safe unittest -{ - import phobos.sys.meta : Alias, AliasSeq; - - static struct AliasThis(T) - { - T member; - alias this = member; - } - - static struct S - { - int i; - } - - foreach (Q; AliasSeq!(Alias, ConstOf, ImmutableOf, SharedOf)) - { - foreach (T; AliasSeq!(long*, S*, S**, S***, double[]*)) - { - enum E : Q!T { a = Q!T.init } - - static assert( isPointer!(Q!T)); - static assert(!isPointer!E); - static assert(!isPointer!(AliasThis!(Q!T))); - } - - foreach (T; AliasSeq!(bool, char, wchar, dchar, byte, int, uint, long, - int[], float[8], real[], void[])) - { - enum E : Q!T { a = Q!T.init } - - static assert(!isPointer!(Q!T)); - static assert(!isPointer!E); - static assert(!isPointer!(AliasThis!(Q!T))); - } - } -} - -/++ - Evaluates to $(D true) if the given type or symbol is an instantiation of - the given template. - - The overload which takes $(D T) operates on types and indicates whether an - aggregate type (i.e. struct, class, interface, or union) is an - instantiation of the given template. - - The overload which takes $(D Symbol) operates on function templates, - because unlike with aggregate types, the type of a function does not retain - the fact that it was instantiated from a template. So, for functions, it's - necessary to pass the function itself as a symbol rather than pass the type - of the function. - - The overload which takes $(D Symbol) also works with templates which are - not types or functions. - - The single-argument overload makes it so that it can be partially - instantiated with the first argument, which will often be necessary with - template predicates. - +/ -template isInstantiationOf(alias Template, T) -if (__traits(isTemplate, Template)) -{ - enum isInstantiationOf = is(T == Template!Args, Args...); -} - -/++ Ditto +/ -template isInstantiationOf(alias Template, alias Symbol) -if (__traits(isTemplate, Template)) -{ - enum impl(alias T : Template!Args, Args...) = true; - enum impl(alias T) = false; - enum isInstantiationOf = impl!Symbol; -} - -/++ Ditto +/ -template isInstantiationOf(alias Template) -if (__traits(isTemplate, Template)) -{ - enum isInstantiationOf(T) = is(T == Template!Args, Args...); - - template isInstantiationOf(alias Symbol) - { - enum impl(alias T : Template!Args, Args...) = true; - enum impl(alias T) = false; - enum isInstantiationOf = impl!Symbol; - } -} - -/// Examples of templated types. -@safe unittest -{ - static struct S(T) {} - static class C(T) {} - - static assert( isInstantiationOf!(S, S!int)); - static assert( isInstantiationOf!(S, S!int)); - static assert( isInstantiationOf!(S, S!string)); - static assert( isInstantiationOf!(S, const S!string)); - static assert( isInstantiationOf!(S, shared S!string)); - static assert(!isInstantiationOf!(S, int)); - static assert(!isInstantiationOf!(S, C!int)); - static assert(!isInstantiationOf!(S, C!string)); - static assert(!isInstantiationOf!(S, C!(S!int))); - - static assert( isInstantiationOf!(C, C!int)); - static assert( isInstantiationOf!(C, C!string)); - static assert( isInstantiationOf!(C, const C!string)); - static assert( isInstantiationOf!(C, shared C!string)); - static assert(!isInstantiationOf!(C, int)); - static assert(!isInstantiationOf!(C, S!int)); - static assert(!isInstantiationOf!(C, S!string)); - static assert(!isInstantiationOf!(C, S!(C!int))); - - static struct Variadic(T...) {} - - static assert( isInstantiationOf!(Variadic, Variadic!())); - static assert( isInstantiationOf!(Variadic, Variadic!int)); - static assert( isInstantiationOf!(Variadic, Variadic!(int, string))); - static assert( isInstantiationOf!(Variadic, Variadic!(int, string, int))); - static assert( isInstantiationOf!(Variadic, const Variadic!(int, short))); - static assert( isInstantiationOf!(Variadic, shared Variadic!(int, short))); - static assert(!isInstantiationOf!(Variadic, int)); - static assert(!isInstantiationOf!(Variadic, S!int)); - static assert(!isInstantiationOf!(Variadic, C!int)); - - static struct ValueArg(int i) {} - static assert( isInstantiationOf!(ValueArg, ValueArg!42)); - static assert( isInstantiationOf!(ValueArg, ValueArg!256)); - static assert( isInstantiationOf!(ValueArg, const ValueArg!1024)); - static assert( isInstantiationOf!(ValueArg, shared ValueArg!1024)); - static assert(!isInstantiationOf!(ValueArg, int)); - static assert(!isInstantiationOf!(ValueArg, S!int)); - - int i; - - static struct AliasArg(alias Symbol) {} - static assert( isInstantiationOf!(AliasArg, AliasArg!42)); - static assert( isInstantiationOf!(AliasArg, AliasArg!int)); - static assert( isInstantiationOf!(AliasArg, AliasArg!i)); - static assert( isInstantiationOf!(AliasArg, const AliasArg!i)); - static assert( isInstantiationOf!(AliasArg, shared AliasArg!i)); - static assert(!isInstantiationOf!(AliasArg, int)); - static assert(!isInstantiationOf!(AliasArg, S!int)); - - // An uninstantiated template is not an instance of any template, - // not even itself. - static assert(!isInstantiationOf!(S, S)); - static assert(!isInstantiationOf!(S, C)); - static assert(!isInstantiationOf!(C, C)); - static assert(!isInstantiationOf!(C, S)); - - // Variables of a templated type are not considered instantiations of that - // type. For templated types, the overload which takes a type must be used. - S!int s; - C!string c; - static assert(!isInstantiationOf!(S, s)); - static assert(!isInstantiationOf!(C, c)); -} - -// Examples of templated functions. -@safe unittest -{ - static int foo(T...)() { return 42; } - static void bar(T...)(T var) {} - static void baz(T)(T var) {} - static bool frobozz(alias pred)(int) { return true; } - - static assert( isInstantiationOf!(foo, foo!int)); - static assert( isInstantiationOf!(foo, foo!string)); - static assert( isInstantiationOf!(foo, foo!(int, string))); - static assert(!isInstantiationOf!(foo, bar!int)); - static assert(!isInstantiationOf!(foo, bar!string)); - static assert(!isInstantiationOf!(foo, bar!(int, string))); - - static assert( isInstantiationOf!(bar, bar!int)); - static assert( isInstantiationOf!(bar, bar!string)); - static assert( isInstantiationOf!(bar, bar!(int, string))); - static assert(!isInstantiationOf!(bar, foo!int)); - static assert(!isInstantiationOf!(bar, foo!string)); - static assert(!isInstantiationOf!(bar, foo!(int, string))); - - static assert( isInstantiationOf!(baz, baz!int)); - static assert( isInstantiationOf!(baz, baz!string)); - static assert(!isInstantiationOf!(baz, foo!(int, string))); - - static assert( isInstantiationOf!(frobozz, frobozz!(a => a))); - static assert( isInstantiationOf!(frobozz, frobozz!(a => a > 2))); - static assert(!isInstantiationOf!(frobozz, baz!int)); - - // Unfortunately, the function type is not considered an instantiation of - // the template, because that information is not part of the type, unlike - // with templated structs or classes. - static assert(!isInstantiationOf!(foo, typeof(foo!int))); - static assert(!isInstantiationOf!(bar, typeof(bar!int))); -} - -// Examples of templates which aren't types or functions. -@safe unittest -{ - template SingleArg(T) {} - template Variadic(T...) {} - template ValueArg(string s) {} - template Alias(alias symbol) {} - - static assert( isInstantiationOf!(SingleArg, SingleArg!int)); - static assert( isInstantiationOf!(SingleArg, SingleArg!string)); - static assert(!isInstantiationOf!(SingleArg, int)); - static assert(!isInstantiationOf!(SingleArg, Variadic!int)); - - static assert( isInstantiationOf!(Variadic, Variadic!())); - static assert( isInstantiationOf!(Variadic, Variadic!int)); - static assert( isInstantiationOf!(Variadic, Variadic!string)); - static assert( isInstantiationOf!(Variadic, Variadic!(short, int, long))); - static assert(!isInstantiationOf!(Variadic, int)); - static assert(!isInstantiationOf!(Variadic, SingleArg!int)); - - static assert( isInstantiationOf!(ValueArg, ValueArg!"dlang")); - static assert( isInstantiationOf!(ValueArg, ValueArg!"foobar")); - static assert(!isInstantiationOf!(ValueArg, string)); - static assert(!isInstantiationOf!(ValueArg, Variadic!string)); - - int i; - - static assert( isInstantiationOf!(Alias, Alias!int)); - static assert( isInstantiationOf!(Alias, Alias!42)); - static assert( isInstantiationOf!(Alias, Alias!i)); - static assert(!isInstantiationOf!(Alias, int)); - static assert(!isInstantiationOf!(Alias, SingleArg!int)); -} - -/// Examples of partial instantation. -@safe unittest -{ - static struct SingleArg(T) {} - static struct Variadic(T...) {} - - alias isSingleArg = isInstantiationOf!SingleArg; - alias isVariadic = isInstantiationOf!Variadic; - - static assert( isSingleArg!(SingleArg!int)); - static assert( isSingleArg!(const SingleArg!int)); - static assert(!isSingleArg!int); - static assert(!isSingleArg!(Variadic!int)); - - static assert( isVariadic!(Variadic!())); - static assert( isVariadic!(Variadic!int)); - static assert( isVariadic!(shared Variadic!int)); - static assert( isVariadic!(Variadic!(int, string))); - static assert(!isVariadic!int); - static assert(!isVariadic!(SingleArg!int)); - - T foo(T)(T t) { return t; } - T likeFoo(T)(T t) { return t; } - bool bar(alias pred)(int i) { return pred(i); } - - alias isFoo = isInstantiationOf!foo; - alias isBar = isInstantiationOf!bar; - - static assert( isFoo!(foo!int)); - static assert( isFoo!(foo!string)); - static assert(!isFoo!int); - static assert(!isFoo!(likeFoo!int)); - static assert(!isFoo!(bar!(a => true))); - - static assert( isBar!(bar!(a => true))); - static assert( isBar!(bar!(a => a > 2))); - static assert(!isBar!int); - static assert(!isBar!(foo!int)); - static assert(!isBar!(likeFoo!int)); -} - -/++ - Evaluates to an $(D AliasSeq) containing the members of an enum type. - - The elements of the $(D AliasSeq) are in the same order as they are in the - enum declaration. - - An enum can have multiple members with the same value, so if code needs the - enum values to be unique (e.g. if it's generating a switch statement from - them), then $(REF Unique, phobos, sys, meta) can be used to filter out the - duplicate values - e.g. $(D Unique!(isEqual, EnumMembers!E)). - +/ -template EnumMembers(E) -if (is(E == enum)) -{ - import phobos.sys.meta : AliasSeq; - - alias EnumMembers = AliasSeq!(); - static foreach (member; __traits(allMembers, E)) - EnumMembers = AliasSeq!(EnumMembers, __traits(getMember, E, member)); -} - -/// Create an array of enum values. -@safe unittest -{ - enum Sqrts : real - { - one = 1, - two = 1.41421, - three = 1.73205 - } - auto sqrts = [EnumMembers!Sqrts]; - assert(sqrts == [Sqrts.one, Sqrts.two, Sqrts.three]); -} - -/++ - A generic function $(D rank(v)) in the following example uses this template - for finding a member $(D e) in an enum type $(D E). - +/ -@safe unittest -{ - // Returns i if e is the i-th member of E. - static size_t rank(E)(E e) - if (is(E == enum)) - { - static foreach (i, member; EnumMembers!E) - { - if (e == member) - return i; - } - assert(0, "Not an enum member"); - } - - enum Mode - { - read = 1, - write = 2, - map = 4 - } - assert(rank(Mode.read) == 0); - assert(rank(Mode.write) == 1); - assert(rank(Mode.map) == 2); -} - -/// Use EnumMembers to generate a switch statement using static foreach. -@safe unittest -{ - static class Foo - { - string calledMethod; - void foo() @safe { calledMethod = "foo"; } - void bar() @safe { calledMethod = "bar"; } - void baz() @safe { calledMethod = "baz"; } - } - - enum FuncName : string { foo = "foo", bar = "bar", baz = "baz" } - - auto foo = new Foo; - - s: final switch (FuncName.bar) - { - static foreach (member; EnumMembers!FuncName) - { - // Generate a case for each enum value. - case member: - { - // Call foo.{enum value}(). - __traits(getMember, foo, member)(); - break s; - } - } - } - - // Since we passed FuncName.bar to the switch statement, the bar member - // function was called. - assert(foo.calledMethod == "bar"); -} - -@safe unittest -{ - { - enum A { a } - static assert([EnumMembers!A] == [A.a]); - enum B { a, b, c, d, e } - static assert([EnumMembers!B] == [B.a, B.b, B.c, B.d, B.e]); - } - { - enum A : string { a = "alpha", b = "beta" } - static assert([EnumMembers!A] == [A.a, A.b]); - - static struct S - { - int value; - int opCmp(S rhs) const nothrow { return value - rhs.value; } - } - enum B : S { a = S(1), b = S(2), c = S(3) } - static assert([EnumMembers!B] == [B.a, B.b, B.c]); - } - { - enum A { a = 0, b = 0, c = 1, d = 1, e } - static assert([EnumMembers!A] == [A.a, A.b, A.c, A.d, A.e]); - } - { - enum E { member, a = 0, b = 0 } - - static assert(__traits(isSame, EnumMembers!E[0], E.member)); - static assert(__traits(isSame, EnumMembers!E[1], E.a)); - static assert(__traits(isSame, EnumMembers!E[2], E.b)); - - static assert(__traits(identifier, EnumMembers!E[0]) == "member"); - static assert(__traits(identifier, EnumMembers!E[1]) == "a"); - static assert(__traits(identifier, EnumMembers!E[2]) == "b"); - } -} - -// https://issues.dlang.org/show_bug.cgi?id=14561: huge enums -@safe unittest -{ - static string genEnum() - { - string result = "enum TLAs {"; - foreach (c0; '0' .. '2' + 1) - { - foreach (c1; '0' .. '9' + 1) - { - foreach (c2; '0' .. '9' + 1) - { - foreach (c3; '0' .. '9' + 1) - { - result ~= '_'; - result ~= c0; - result ~= c1; - result ~= c2; - result ~= c3; - result ~= ','; - } - } - } - } - result ~= '}'; - return result; - } - mixin(genEnum); - static assert(EnumMembers!TLAs[0] == TLAs._0000); - static assert(EnumMembers!TLAs[$ - 1] == TLAs._2999); -} - -/++ - Whether the type $(D From) is implicitly convertible to the type $(D To). - - Note that template constraints should be very careful about when they test - for implicit conversions and in general should prefer to either test for an - exact set of types or for types which compile with a particular piece of - code rather than being designed to accept any type which implicitly converts - to a particular type. - - This is because having a type pass a template constraint based on an - implicit conversion but then not have the implicit conversion actually take - place (which it won't unless the template does something to force it - internally) can lead to either compilation errors or subtle behavioral - differences - and even when the conversion is done explicitly within a - templated function, since it's not done at the call site, it can still lead - to subtle bugs in some cases (e.g. if slicing a static array is involved). - - For situations where code needs to verify that a type is implicitly - convertible based solely on its qualifiers, $(LREF isQualifierConvertible) - would be a more appropriate choice than isImplicitlyConvertible. - - Given how trivial the $(D is) expression for isImplicitlyConvertible is - - $(D is(To : From)) - this trait is provided primarily so that it can be - used in conjunction with templates that use a template predicate (such as - many of the templates in phobos.sys.meta). - - The single-argument overload makes it so that it can be partially - instantiated with the first argument, which will often be necessary with - template predicates. - - See_Also: - $(DDSUBLINK dlang.org/spec/type, implicit-conversions, Spec on implicit conversions) - $(DDSUBLINK spec/const3, implicit_qualifier_conversions, Spec for implicit qualifier conversions) - $(LREF isQualifierConvertible) - +/ -enum isImplicitlyConvertible(From, To) = is(From : To); - -/++ Ditto +/ -template isImplicitlyConvertible(From) -{ - enum isImplicitlyConvertible(To) = is(From : To); -} - -/// -@safe unittest -{ - static assert( isImplicitlyConvertible!(byte, long)); - static assert( isImplicitlyConvertible!(ushort, long)); - static assert( isImplicitlyConvertible!(int, long)); - static assert( isImplicitlyConvertible!(long, long)); - static assert( isImplicitlyConvertible!(ulong, long)); - - static assert( isImplicitlyConvertible!(ubyte, int)); - static assert( isImplicitlyConvertible!(short, int)); - static assert( isImplicitlyConvertible!(int, int)); - static assert( isImplicitlyConvertible!(uint, int)); - static assert(!isImplicitlyConvertible!(long, int)); - static assert(!isImplicitlyConvertible!(ulong, int)); - - static assert(!isImplicitlyConvertible!(int, string)); - static assert(!isImplicitlyConvertible!(int, int[])); - static assert(!isImplicitlyConvertible!(int, int*)); - - static assert(!isImplicitlyConvertible!(string, int)); - static assert(!isImplicitlyConvertible!(int[], int)); - static assert(!isImplicitlyConvertible!(int*, int)); - - // For better or worse, bool and the built-in character types will - // implicitly convert to integer or floating-point types if the target type - // is large enough. Sometimes, this is desirable, whereas at other times, - // it can have very surprising results, so it's one reason why code should - // be very careful when testing for implicit conversions. - static assert( isImplicitlyConvertible!(bool, int)); - static assert( isImplicitlyConvertible!(char, int)); - static assert( isImplicitlyConvertible!(wchar, int)); - static assert( isImplicitlyConvertible!(dchar, int)); - - static assert( isImplicitlyConvertible!(bool, ubyte)); - static assert( isImplicitlyConvertible!(char, ubyte)); - static assert(!isImplicitlyConvertible!(wchar, ubyte)); - static assert(!isImplicitlyConvertible!(dchar, ubyte)); - - static assert( isImplicitlyConvertible!(bool, double)); - static assert( isImplicitlyConvertible!(char, double)); - static assert( isImplicitlyConvertible!(wchar, double)); - static assert( isImplicitlyConvertible!(dchar, double)); - - // Value types can be implicitly converted regardless of their qualifiers - // thanks to the fact that they're copied. - static assert( isImplicitlyConvertible!(int, int)); - static assert( isImplicitlyConvertible!(const int, int)); - static assert( isImplicitlyConvertible!(immutable int, int)); - static assert( isImplicitlyConvertible!(inout int, int)); - - static assert( isImplicitlyConvertible!(int, const int)); - static assert( isImplicitlyConvertible!(int, immutable int)); - static assert( isImplicitlyConvertible!(int, inout int)); - - // Reference types are far more restrictive about which implicit conversions - // they allow, because qualifiers in D are transitive. - static assert( isImplicitlyConvertible!(int*, int*)); - static assert(!isImplicitlyConvertible!(const int*, int*)); - static assert(!isImplicitlyConvertible!(immutable int*, int*)); - - static assert( isImplicitlyConvertible!(int*, const int*)); - static assert( isImplicitlyConvertible!(const int*, const int*)); - static assert( isImplicitlyConvertible!(immutable int*, const int*)); - - static assert(!isImplicitlyConvertible!(int*, immutable int*)); - static assert(!isImplicitlyConvertible!(const int*, immutable int*)); - static assert( isImplicitlyConvertible!(immutable int*, immutable int*)); - - // Note that inout gets a bit weird, since it's only used with function - // parameters, and it's a stand-in for whatever mutability qualifiers the - // type actually has. So, a function parameter that's inout accepts any - // mutability, but you can't actually implicitly convert to inout, because - // it's unknown within the function what the actual mutability of the type - // is. It will differ depending on the function arguments of a specific - // call to that function, so the same code has to work with all combinations - // of mutability qualifiers. - static assert(!isImplicitlyConvertible!(int*, inout int*)); - static assert(!isImplicitlyConvertible!(const int*, inout int*)); - static assert(!isImplicitlyConvertible!(immutable int*, inout int*)); - static assert( isImplicitlyConvertible!(inout int*, inout int*)); - - static assert(!isImplicitlyConvertible!(inout int*, int*)); - static assert( isImplicitlyConvertible!(inout int*, const int*)); - static assert(!isImplicitlyConvertible!(inout int*, immutable int*)); - - // Enums implicitly convert to their base type. - enum E : int - { - a = 42 - } - static assert( isImplicitlyConvertible!(E, int)); - static assert( isImplicitlyConvertible!(E, long)); - static assert(!isImplicitlyConvertible!(E, int[])); - - // Structs only implicit convert to another type via declaring an - // alias this. - static struct S - { - int i; - } - static assert(!isImplicitlyConvertible!(S, int)); - static assert(!isImplicitlyConvertible!(S, long)); - static assert(!isImplicitlyConvertible!(S, string)); - - static struct AliasThis - { - int i; - alias this = i; - } - static assert( isImplicitlyConvertible!(AliasThis, int)); - static assert( isImplicitlyConvertible!(AliasThis, long)); - static assert(!isImplicitlyConvertible!(AliasThis, string)); - - static struct AliasThis2 - { - AliasThis at; - alias this = at; - } - static assert( isImplicitlyConvertible!(AliasThis2, AliasThis)); - static assert( isImplicitlyConvertible!(AliasThis2, int)); - static assert( isImplicitlyConvertible!(AliasThis2, long)); - static assert(!isImplicitlyConvertible!(AliasThis2, string)); - - static struct AliasThis3 - { - AliasThis2 at; - alias this = at; - } - static assert( isImplicitlyConvertible!(AliasThis3, AliasThis2)); - static assert( isImplicitlyConvertible!(AliasThis3, AliasThis)); - static assert( isImplicitlyConvertible!(AliasThis3, int)); - static assert( isImplicitlyConvertible!(AliasThis3, long)); - static assert(!isImplicitlyConvertible!(AliasThis3, string)); - - // D does not support implicit conversions via construction. - static struct Cons - { - this(int i) - { - this.i = i; - } - - int i; - } - static assert(!isImplicitlyConvertible!(int, Cons)); - - // Classes support implicit conversion based on their class and - // interface hierarchies. - static interface I1 {} - static class Base : I1 {} - - static interface I2 {} - static class Foo : Base, I2 {} - - static class Bar : Base {} - - static assert( isImplicitlyConvertible!(Base, Base)); - static assert(!isImplicitlyConvertible!(Base, Foo)); - static assert(!isImplicitlyConvertible!(Base, Bar)); - static assert( isImplicitlyConvertible!(Base, I1)); - static assert(!isImplicitlyConvertible!(Base, I2)); - - static assert( isImplicitlyConvertible!(Foo, Base)); - static assert( isImplicitlyConvertible!(Foo, Foo)); - static assert(!isImplicitlyConvertible!(Foo, Bar)); - static assert( isImplicitlyConvertible!(Foo, I1)); - static assert( isImplicitlyConvertible!(Foo, I2)); - - static assert( isImplicitlyConvertible!(Bar, Base)); - static assert(!isImplicitlyConvertible!(Bar, Foo)); - static assert( isImplicitlyConvertible!(Bar, Bar)); - static assert( isImplicitlyConvertible!(Bar, I1)); - static assert(!isImplicitlyConvertible!(Bar, I2)); - - static assert(!isImplicitlyConvertible!(I1, Base)); - static assert(!isImplicitlyConvertible!(I1, Foo)); - static assert(!isImplicitlyConvertible!(I1, Bar)); - static assert( isImplicitlyConvertible!(I1, I1)); - static assert(!isImplicitlyConvertible!(I1, I2)); - - static assert(!isImplicitlyConvertible!(I2, Base)); - static assert(!isImplicitlyConvertible!(I2, Foo)); - static assert(!isImplicitlyConvertible!(I2, Bar)); - static assert(!isImplicitlyConvertible!(I2, I1)); - static assert( isImplicitlyConvertible!(I2, I2)); - - // Note that arrays are not implicitly convertible even when their elements - // are implicitly convertible. - static assert(!isImplicitlyConvertible!(ubyte[], uint[])); - static assert(!isImplicitlyConvertible!(Foo[], Base[])); - static assert(!isImplicitlyConvertible!(Bar[], Base[])); - - // However, like with pointers, dynamic arrays are convertible based on - // constness. - static assert( isImplicitlyConvertible!(Base[], const Base[])); - static assert( isImplicitlyConvertible!(Base[], const(Base)[])); - static assert(!isImplicitlyConvertible!(Base[], immutable(Base)[])); - static assert(!isImplicitlyConvertible!(const Base[], immutable Base[])); - static assert( isImplicitlyConvertible!(const Base[], const Base[])); - static assert(!isImplicitlyConvertible!(const Base[], immutable Base[])); -} - -/++ - isImplicitlyConvertible can be used with partial instantiation so that it - can be passed to a template which takes a unary predicate. - +/ -@safe unittest -{ - import phobos.sys.meta : AliasSeq, all, indexOf; - - // byte is implicitly convertible to byte, short, int, and long. - static assert(all!(isImplicitlyConvertible!byte, short, int, long)); - - // const(char)[] at index 2 is the first type in the AliasSeq which string - // can be implicitly converted to. - alias Types = AliasSeq!(int, char[], const(char)[], string, int*); - static assert(indexOf!(isImplicitlyConvertible!string, Types) == 2); -} - -/++ - Whether $(D From) is - $(DDSUBLINK spec/const3, implicit_qualifier_conversions, qualifier-convertible) - to $(D To). - - This is testing whether $(D From) and $(D To) are the same type - minus the - qualifiers - and whether the qualifiers on $(D From) can be implicitly - converted to the qualifiers on $(D To). No other implicit conversions are - taken into account. - - For instance, $(D const int*) is not implicitly convertible to $(D int*), - because that would violate $(D const). That means that $(D const) is not - qualifier convertible to mutable. And as such, $(I any) $(D const) type - is not qualifier convertible to a mutable type even if it's implicitly - convertible. E.G. $(D const int) is implicitly convertible to $(D int), - because it can be copied to avoid violating $(D const), but it's still not - qualifier convertible, because $(D const) types in general cannot be - implicitly converted to mutable. - - The exact types being tested matter, because they need to be the same - (minus the qualifiers) in order to be considered convertible, but beyond - that, all that matters for the conversion is whether those qualifers would - be convertible regardless of which types they were on. So, if you're having - trouble picturing whether $(D From) would be qualifier convertible to - $(D To), then consider which conversions would be allowed from $(D From[]) - to $(D To[]) (and remember that dynamic arrays are only implicitly - convertible based on their qualifers). - - The $(DDSUBLINK spec/const3, implicit_qualifier_conversions, spec) provides - a table of which qualifiers can be implcitly converted to which other - qualifers (and of course, there a bunch of examples below). - - So, isQualifierConvertible can be used in a case like - $(D isQualifierConvertible!(ReturnType!(typeof(foo(bar))), const char), - which would be testing that the return type of $(D foo(bar)) was $(D char), - $(D const char), or $(D immutable char) (since those are the only types - which are qualifier convertible to $(D const char)). - - This is in contrast to - $(D isImplicitlyConvertible!(ReturnType!(typeof(foo(bar))), const char), - which would be $(D true) for $(I any) type which was implicitly convertible - to $(D const char) rather than just $(D char), $(D const char), and - $(D immutable char). - - The single-argument overload makes it so that it can be partially - instantiated with the first argument, which will often be necessary with - template predicates. - - See_Also: - $(DDSUBLINK spec/const3, implicit_qualifier_conversions, Spec for implicit qualifier conversions) - $(LREF isImplicitlyConvertible) - +/ -enum isQualifierConvertible(From, To) = is(immutable From == immutable To) && is(From* : To*); - -/++ Ditto +/ -template isQualifierConvertible(From) -{ - enum isQualifierConvertible(To) = is(immutable From == immutable To) && is(From* : To*); -} - -/// -@safe unittest -{ - // i.e. char* -> const char* - static assert( isQualifierConvertible!(char, const char)); - - // i.e. const char* -> char* - static assert(!isQualifierConvertible!(const char, char)); - - static assert( isQualifierConvertible!(int, int)); - static assert( isQualifierConvertible!(int, const int)); - static assert(!isQualifierConvertible!(int, immutable int)); - - static assert(!isQualifierConvertible!(const int, int)); - static assert( isQualifierConvertible!(const int, const int)); - static assert(!isQualifierConvertible!(const int, immutable int)); - - static assert(!isQualifierConvertible!(immutable int, int)); - static assert( isQualifierConvertible!(immutable int, const int)); - static assert( isQualifierConvertible!(immutable int, immutable int)); - - // Note that inout gets a bit weird, since it's only used with function - // parameters, and it's a stand-in for whatever mutability qualifiers the - // type actually has. So, a function parameter that's inout accepts any - // mutability, but you can't actually implicitly convert to inout, because - // it's unknown within the function what the actual mutability of the type - // is. It will differ depending on the function arguments of a specific - // call to that function, so the same code has to work with all combinations - // of mutability qualifiers. - static assert(!isQualifierConvertible!(int, inout int)); - static assert(!isQualifierConvertible!(const int, inout int)); - static assert(!isQualifierConvertible!(immutable int, inout int)); - static assert( isQualifierConvertible!(inout int, inout int)); - - static assert(!isQualifierConvertible!(inout int, int)); - static assert( isQualifierConvertible!(inout int, const int)); - static assert(!isQualifierConvertible!(inout int, immutable int)); - - // shared is of course also a qualifier. - static assert(!isQualifierConvertible!(int, shared int)); - static assert(!isQualifierConvertible!(int, const shared int)); - static assert(!isQualifierConvertible!(const int, shared int)); - static assert(!isQualifierConvertible!(const int, const shared int)); - static assert(!isQualifierConvertible!(immutable int, shared int)); - static assert( isQualifierConvertible!(immutable int, const shared int)); - - static assert(!isQualifierConvertible!(shared int, int)); - static assert(!isQualifierConvertible!(shared int, const int)); - static assert(!isQualifierConvertible!(shared int, immutable int)); - static assert( isQualifierConvertible!(shared int, shared int)); - static assert( isQualifierConvertible!(shared int, const shared int)); - - static assert(!isQualifierConvertible!(const shared int, int)); - static assert(!isQualifierConvertible!(const shared int, const int)); - static assert(!isQualifierConvertible!(const shared int, immutable int)); - static assert(!isQualifierConvertible!(const shared int, shared int)); - static assert( isQualifierConvertible!(const shared int, const shared int)); - - // Implicit conversions don't count unless they're based purely on - // qualifiers. - enum E : int - { - a = 1 - } - - static assert(!isQualifierConvertible!(E, int)); - static assert(!isQualifierConvertible!(E, const int)); - static assert( isQualifierConvertible!(E, E)); - static assert( isQualifierConvertible!(E, const E)); - static assert(!isQualifierConvertible!(E, immutable E)); - - static struct AliasThis - { - int i; - alias this = i; - } - - static assert(!isQualifierConvertible!(AliasThis, int)); - static assert(!isQualifierConvertible!(AliasThis, const int)); - static assert( isQualifierConvertible!(AliasThis, AliasThis)); - static assert( isQualifierConvertible!(AliasThis, const AliasThis)); - static assert(!isQualifierConvertible!(AliasThis, immutable AliasThis)); - - // The qualifiers are irrelevant if the types aren't the same when - // stripped of all qualifers. - static assert(!isQualifierConvertible!(int, long)); - static assert(!isQualifierConvertible!(int, const long)); - static assert(!isQualifierConvertible!(string, const(ubyte)[])); -} - -/++ - isQualifierConvertible can be used with partial instantiation so that it - can be passed to a template which takes a unary predicate. - +/ -@safe unittest -{ - import phobos.sys.meta : AliasSeq, all, indexOf; - - // byte is qualifier convertible to byte and const byte. - static assert(all!(isQualifierConvertible!byte, byte, const byte)); - - // const(char[]) at index 2 is the first type in the AliasSeq which string - // is qualifier convertible to. - alias Types = AliasSeq!(int, char[], const(char[]), string, int*); - static assert(indexOf!(isQualifierConvertible!string, Types) == 2); -} - -@safe unittest -{ - import phobos.sys.meta : AliasSeq; - - alias Types = AliasSeq!(int, const int, shared int, inout int, const shared int, - const inout int, inout shared int, const inout shared int, immutable int); - - // https://dlang.org/spec/const3.html#implicit_qualifier_conversions - enum _ = 0; - static immutable bool[Types.length][Types.length] conversions = [ - // m c s i cs ci is cis im - [1, 1, _, _, _, _, _, _, _], // mutable - [_, 1, _, _, _, _, _, _, _], // const - [_, _, 1, _, 1, _, _, _, _], // shared - [_, 1, _, 1, _, 1, _, _, _], // inout - [_, _, _, _, 1, _, _, _, _], // const shared - [_, 1, _, _, _, 1, _, _, _], // const inout - [_, _, _, _, 1, _, 1, 1, _], // inout shared - [_, _, _, _, 1, _, _, 1, _], // const inout shared - [_, 1, _, _, 1, 1, _, 1, 1], // immutable - ]; - - foreach (i, From; Types) - { - foreach (j, To; Types) - { - static assert(isQualifierConvertible!(From, To) == conversions[i][j], - "`isQualifierConvertible!(" ~ From.stringof ~ ", " ~ To.stringof ~ ")`" ~ - " should be `" ~ (conversions[i][j] ? "true`" : "false`")); - } - } -} - -/++ - Whether the given values are equal per $(D ==). - - All this does is $(D lhs == rhs) but in an eponymous template, so most code - shouldn't use it. It's intended to be used in conjunction with templates - that take a template predicate - such as those in phobos.sys.meta. - - The single-argument overload makes it so that it can be partially - instantiated with the first argument, which will often be necessary with - template predicates. - - Note that in most cases, even when comparing values at compile time, using - isEqual makes no sense, because you can use CTFE to just compare two values - (or expressions which evaluate to values), but in rare cases where you need - to compare symbols in an $(D AliasSeq) by value with a template predicate - while still leaving them as symbols in an $(D AliasSeq), then isEqual would - be needed. - - A prime example of this would be $(D Unique!(isEqual, EnumMembers!MyEnum)), - which results in an $(D AliasSeq) containing the list of members of - $(D MyEnum) but without any duplicate values (e.g. to use when doing code - generation to create a final switch). - - Alternatively, code such as $(D [EnumMembers!MyEnum].sort().unique()) could - be used to get a dynamic array of the enum members with no duplicate values - via CTFE, thus avoiding the need for template predicates or anything from - phobos.sys.meta. However, you then have a dynamic array of enum values - rather than an $(D AliasSeq) of symbols for those enum members, which - affects what you can do with type introspection. So, which approach is - better depends on what the code needs to do with the enum members. - - In general, however, if code doesn't need an $(D AliasSeq), and an array of - values will do the trick, then it's more efficient to operate on an array of - values with CTFE and avoid using isEqual or other templates to operate on - the values as an $(D AliasSeq). - - See_Also: - $(LREF isSameSymbol) - $(LREF isSameType) - +/ -enum isEqual(alias lhs, alias rhs) = lhs == rhs; - -/++ Ditto +/ -template isEqual(alias lhs) -{ - enum isEqual(alias rhs) = lhs == rhs; -} - -/// It acts just like ==, but it's a template. -@safe unittest -{ - enum a = 42; - - static assert( isEqual!(a, 42)); - static assert( isEqual!(20, 10 + 10)); - - static assert(!isEqual!(a, 120)); - static assert(!isEqual!(77, 19 * 7 + 2)); - - // b cannot be read at compile time, so it won't work with isEqual. - int b = 99; - static assert(!__traits(compiles, isEqual!(b, 99))); -} - -/++ - Comparing some of the differences between an $(D AliasSeq) of enum members - and an array of enum values created from an $(D AliasSeq) of enum members. - +/ -@safe unittest -{ - import phobos.sys.meta : AliasSeq, Unique; - - enum E - { - a = 0, - b = 22, - c = 33, - d = 0, - e = 256, - f = 33, - g = 7 - } - - alias uniqueMembers = Unique!(isEqual, EnumMembers!E); - static assert(uniqueMembers.length == 5); - - static assert(__traits(isSame, uniqueMembers[0], E.a)); - static assert(__traits(isSame, uniqueMembers[1], E.b)); - static assert(__traits(isSame, uniqueMembers[2], E.c)); - static assert(__traits(isSame, uniqueMembers[3], E.e)); - static assert(__traits(isSame, uniqueMembers[4], E.g)); - - static assert(__traits(identifier, uniqueMembers[0]) == "a"); - static assert(__traits(identifier, uniqueMembers[1]) == "b"); - static assert(__traits(identifier, uniqueMembers[2]) == "c"); - static assert(__traits(identifier, uniqueMembers[3]) == "e"); - static assert(__traits(identifier, uniqueMembers[4]) == "g"); - - // Same value but different symbol. - static assert(uniqueMembers[0] == E.d); - static assert(!__traits(isSame, uniqueMembers[0], E.d)); - - // is expressions compare types, not symbols or values, and these AliasSeqs - // contain the list of symbols for the enum members, not types, so the is - // expression evaluates to false even though the symbols are the same. - static assert(!is(uniqueMembers == AliasSeq!(E.a, E.b, E.c, E.e, E.g))); - - // Once the members are converted to an array, the types are the same, and - // the values are the same, but the symbols are not the same. Instead of - // being the symbols E.a, E.b, etc., they're just values with the type E - // which match the values of E.a, E.b, etc. - enum arr = [uniqueMembers]; - static assert(is(typeof(arr) == E[])); - - static assert(arr == [E.a, E.b, E.c, E.e, E.g]); - static assert(arr == [E.d, E.b, E.f, E.e, E.g]); - - static assert(!__traits(isSame, arr[0], E.a)); - static assert(!__traits(isSame, arr[1], E.b)); - static assert(!__traits(isSame, arr[2], E.c)); - static assert(!__traits(isSame, arr[3], E.e)); - static assert(!__traits(isSame, arr[4], E.g)); - - // Since arr[0] is just a value of type E, it's no longer the symbol, E.a, - // even though its type is E, and its value is the same as that of E.a. And - // unlike the actual members of an enum, an element of an array does not - // have an identifier, so __traits(identifier, ...) doesn't work with it. - static assert(!__traits(compiles, __traits(identifier, arr[0]))); - - // Similarly, once an enum member from the AliasSeq is assigned to a - // variable, __traits(identifer, ...) operates on the variable, not the - // symbol from the AliasSeq or the value of the variable. - auto var = uniqueMembers[0]; - static assert(__traits(identifier, var) == "var"); - - // The same with a manifest constant. - enum constant = uniqueMembers[0]; - static assert(__traits(identifier, constant) == "constant"); -} - -/++ - Whether the given symbols are the same symbol. - - All this does is $(D __traits(isSame, lhs, rhs)), so most code shouldn't - use it. It's intended to be used in conjunction with templates that take a - template predicate - such as those in phobos.sys.meta. - - The single-argument overload makes it so that it can be partially - instantiated with the first argument, which will often be necessary with - template predicates. - - See_Also: - $(DDSUBLINK spec/traits, isSame, $(D __traits(isSame, lhs, rhs))) - $(LREF isEqual) - $(LREF isSameType) - +/ -enum isSameSymbol(alias lhs, alias rhs) = __traits(isSame, lhs, rhs); - -/++ Ditto +/ -template isSameSymbol(alias lhs) -{ - enum isSameSymbol(alias rhs) = __traits(isSame, lhs, rhs); -} - -/// -@safe unittest -{ - int i; - int j; - real r; - - static assert( isSameSymbol!(i, i)); - static assert(!isSameSymbol!(i, j)); - static assert(!isSameSymbol!(i, r)); - - static assert(!isSameSymbol!(j, i)); - static assert( isSameSymbol!(j, j)); - static assert(!isSameSymbol!(j, r)); - - static assert(!isSameSymbol!(r, i)); - static assert(!isSameSymbol!(r, j)); - static assert( isSameSymbol!(r, r)); - - auto foo() { return 0; } - auto bar() { return 0; } - - static assert( isSameSymbol!(foo, foo)); - static assert(!isSameSymbol!(foo, bar)); - static assert(!isSameSymbol!(foo, i)); - - static assert(!isSameSymbol!(bar, foo)); - static assert( isSameSymbol!(bar, bar)); - static assert(!isSameSymbol!(bar, i)); - - // Types are symbols too. However, in most cases, they should be compared - // as types, not symbols (be it with is expressions or with isSameType), - // because the results aren't consistent between scalar types and - // user-defined types with regards to type qualifiers when they're compared - // as symbols. - static assert( isSameSymbol!(double, double)); - static assert(!isSameSymbol!(double, const double)); - static assert(!isSameSymbol!(double, int)); - static assert( isSameSymbol!(Object, Object)); - static assert( isSameSymbol!(Object, const Object)); - - static assert(!isSameSymbol!(i, int)); - static assert( isSameSymbol!(typeof(i), int)); - - // Lambdas can be compared with __traits(isSame, ...), - // so they can be compared with isSameSymbol. - static assert( isSameSymbol!(a => a + 42, a => a + 42)); - static assert(!isSameSymbol!(a => a + 42, a => a + 99)); - - // Partial instantiation allows it to be used with templates that expect - // a predicate that takes only a single argument. - import phobos.sys.meta : AliasSeq, indexOf; - alias Types = AliasSeq!(i, j, r, int, long, foo); - static assert(indexOf!(isSameSymbol!j, Types) == 1); - static assert(indexOf!(isSameSymbol!int, Types) == 3); - static assert(indexOf!(isSameSymbol!bar, Types) == -1); -} - -/++ - Whether the given types are the same type. - - All this does is $(D is(T == U)), so most code shouldn't use it. It's - intended to be used in conjunction with templates that take a template - predicate - such as those in phobos.sys.meta. - - The single-argument overload makes it so that it can be partially - instantiated with the first argument, which will often be necessary with - template predicates. - - See_Also: - $(LREF isEqual) - $(LREF isSameSymbol) - +/ -enum isSameType(T, U) = is(T == U); - -/++ Ditto +/ -template isSameType(T) -{ - enum isSameType(U) = is(T == U); -} - -/// -@safe unittest -{ - static assert( isSameType!(long, long)); - static assert(!isSameType!(long, const long)); - static assert(!isSameType!(long, string)); - static assert( isSameType!(string, string)); - - int i; - real r; - static assert( isSameType!(int, typeof(i))); - static assert(!isSameType!(int, typeof(r))); - - static assert(!isSameType!(real, typeof(i))); - static assert( isSameType!(real, typeof(r))); - - // Partial instantiation allows it to be used with templates that expect - // a predicate that takes only a single argument. - import phobos.sys.meta : AliasSeq, indexOf; - alias Types = AliasSeq!(float, string, int, double); - static assert(indexOf!(isSameType!int, Types) == 2); -} - -/++ - Evaluates to an $(D AliasSeq) of the names (as $(D string)s) of the member - variables of an aggregate type (i.e. a struct, class, interface, or union). - - These are fields which take up memory space within an instance of the type - (i.e. not enums / manifest constants, since they don't take up memory - space, and not static member variables, since they don't take up memory - space within an instance). - - Hidden fields (like the virtual function table pointer or the context - pointer for nested types) are not included. - - For classes, only the direct member variables are included and not those - of any base classes. - - For interfaces, the result of FieldNames is always empty, because - interfaces cannot have member variables. However, because interfaces are - aggregate types, they work with FieldNames for consistency so that code - that's written to work on aggregate types doesn't have to worry about - whether it's dealing with an interface. - - See_Also: - $(LREF FieldSymbols) - $(LREF FieldTypes) - $(DDSUBLINK spec/struct.html, struct_instance_properties, $(D tupleof)) - +/ -template FieldNames(T) -if (isAggregateType!T) -{ - import phobos.sys.meta : AliasSeq; - - static if (is(T == struct) && __traits(isNested, T)) - private alias Fields = AliasSeq!(T.tupleof[0 .. $ - 1]); - else - private alias Fields = T.tupleof; - - alias FieldNames = AliasSeq!(); - static foreach (Field; Fields) - FieldNames = AliasSeq!(FieldNames, Field.stringof); -} - -/// -@safe unittest -{ - import phobos.sys.meta : AliasSeq; - - struct S - { - int x; - float y; - } - static assert(FieldNames!S == AliasSeq!("x", "y")); - - // Since the AliasSeq contains values, all of which are of the same type, - // it can be used to create a dynamic array, which would be more - // efficient than operating on an AliasSeq in the cases where an - // AliasSeq is not necessary. - static assert([FieldNames!S] == ["x", "y"]); - - class C - { - // static variables are not included. - static int var; - - // Manifest constants are not included. - enum lang = "dlang"; - - // Functions are not included, even if they're @property functions. - @property int foo() { return 42; } - - string s; - int i; - int[] arr; - } - static assert(FieldNames!C == AliasSeq!("s", "i", "arr")); - - static assert([FieldNames!C] == ["s", "i", "arr"]); - - // Only direct member variables are included. Member variables from any base - // classes are not. - class D : C - { - real r; - } - static assert(FieldNames!D == AliasSeq!"r"); - - static assert([FieldNames!D] == ["r"]); - - // FieldNames will always be empty for an interface, since it's not legal - // for interfaces to have member variables. - interface I - { - } - static assert(FieldNames!I.length == 0); - - union U - { - int i; - double d; - long l; - S s; - } - static assert(FieldNames!U == AliasSeq!("i", "d", "l", "s")); - - static assert([FieldNames!U] == ["i", "d", "l", "s"]);; - - // FieldNames only operates on aggregate types. - static assert(!__traits(compiles, FieldNames!int)); - static assert(!__traits(compiles, FieldNames!(S*))); - static assert(!__traits(compiles, FieldNames!(C[]))); -} - -@safe unittest -{ - import phobos.sys.meta : AliasSeq; - - { - static struct S0 {} - static assert(FieldNames!S0.length == 0); - - static struct S1 { int a; } - static assert(FieldNames!S1 == AliasSeq!"a"); - - static struct S2 { int a; string b; } - static assert(FieldNames!S2 == AliasSeq!("a", "b")); - - static struct S3 { int a; string b; real c; } - static assert(FieldNames!S3 == AliasSeq!("a", "b", "c")); - } - { - int i; - struct S0 { void foo() { i = 0; }} - static assert(FieldNames!S0.length == 0); - static assert(__traits(isNested, S0)); - - struct S1 { int a; void foo() { i = 0; } } - static assert(FieldNames!S1 == AliasSeq!"a"); - static assert(__traits(isNested, S1)); - - struct S2 { int a; string b; void foo() { i = 0; } } - static assert(FieldNames!S2 == AliasSeq!("a", "b")); - static assert(__traits(isNested, S2)); - - struct S3 { int a; string b; real c; void foo() { i = 0; } } - static assert(FieldNames!S3 == AliasSeq!("a", "b", "c")); - static assert(__traits(isNested, S3)); - } - { - static class C0 {} - static assert(FieldNames!C0.length == 0); - - static class C1 { int a; } - static assert(FieldNames!C1 == AliasSeq!"a"); - - static class C2 { int a; string b; } - static assert(FieldNames!C2 == AliasSeq!("a", "b")); - - static class C3 { int a; string b; real c; } - static assert(FieldNames!C3 == AliasSeq!("a", "b", "c")); - - static class D0 : C3 {} - static assert(FieldNames!D0.length == 0); - - static class D1 : C3 { bool x; } - static assert(FieldNames!D1 == AliasSeq!"x"); - - static class D2 : C3 { bool x; int* y; } - static assert(FieldNames!D2 == AliasSeq!("x", "y")); - - static class D3 : C3 { bool x; int* y; short[] z; } - static assert(FieldNames!D3 == AliasSeq!("x", "y", "z")); - } - { - int i; - class C0 { void foo() { i = 0; }} - static assert(FieldNames!C0.length == 0); - static assert(__traits(isNested, C0)); - - class C1 { int a; void foo() { i = 0; } } - static assert(FieldNames!C1 == AliasSeq!"a"); - static assert(__traits(isNested, C1)); - - class C2 { int a; string b; void foo() { i = 0; } } - static assert(FieldNames!C2 == AliasSeq!("a", "b")); - static assert(__traits(isNested, C2)); - - class C3 { int a; string b; real c; void foo() { i = 0; } } - static assert(FieldNames!C3 == AliasSeq!("a", "b", "c")); - static assert(__traits(isNested, C3)); - - class D0 : C3 {} - static assert(FieldNames!D0.length == 0); - static assert(__traits(isNested, D0)); - - class D1 : C3 { bool x; } - static assert(FieldNames!D1 == AliasSeq!"x"); - static assert(__traits(isNested, D1)); - - class D2 : C3 { bool x; int* y; } - static assert(FieldNames!D2 == AliasSeq!("x", "y")); - static assert(__traits(isNested, D2)); - - class D3 : C3 { bool x; int* y; short[] z; } - static assert(FieldNames!D3 == AliasSeq!("x", "y", "z")); - static assert(__traits(isNested, D3)); - } - { - static union U0 {} - static assert(FieldNames!U0.length == 0); - - static union U1 { int a; } - static assert(FieldNames!U1 == AliasSeq!"a"); - - static union U2 { int a; string b; } - static assert(FieldNames!U2 == AliasSeq!("a", "b")); - - static union U3 { int a; string b; real c; } - static assert(FieldNames!U3 == AliasSeq!("a", "b", "c")); - } - { - static struct S - { - enum e = 42; - static str = "foobar"; - - string name() { return "foo"; } - - int[] arr; - - struct Inner1 { int i; } - - static struct Inner2 { long gnol; } - - union { int a; string b; } - - alias Foo = Inner1; - } - - static assert(FieldNames!S == AliasSeq!("arr", "a", "b")); - static assert(FieldNames!(const S) == AliasSeq!("arr", "a", "b")); - static assert(FieldNames!(S.Inner1) == AliasSeq!"i"); - static assert(FieldNames!(S.Inner2) == AliasSeq!"gnol"); - } -} - -/++ - Evaluates to an $(D AliasSeq) of the symbols for the member variables of an - aggregate type (i.e. a struct, class, interface, or union). - - These are fields which take up memory space within an instance of the type - (i.e. not enums / manifest constants, since they don't take up memory - space, and not static member variables, since they don't take up memory - space within an instance). - - Hidden fields (like the virtual function table pointer or the context - pointer for nested types) are not included. - - For classes, only the direct member variables are included and not those - of any base classes. - - For interfaces, the result of FieldSymbols is always empty, because - interfaces cannot have member variables. However, because interfaces are - aggregate types, they work with FieldSymbols for consistency so that code - that's written to work on aggregate types doesn't have to worry about - whether it's dealing with an interface. - - In most cases, $(D FieldSymbols!T) has the same result as $(D T.tupleof). - The difference is that for nested structs with a context pointer, - $(D T.tupleof) includes the context pointer, whereas $(D FieldSymbols!T) - does not. For non-nested structs, and for classes, interfaces, and unions, - $(D FieldSymbols!T) and $(D T.tupleof) are the same. - - So, for most cases, $(D T.tupleof) is sufficient and avoids instantiating - an additional template, but FieldSymbols is provided so that the code that - needs to avoid including context pointers in the list of fields can do so - without the programmer having to figure how to do that correctly. It also - provides a template that's equivalent to what $(LREF FieldNames) and - $(LREF FieldTypes) do in terms of which fields it gives (the difference of - course then being whether you get the symbols, names, or types for the - fields), whereas the behavior for $(D tupleof) is subtly different. - - See_Also: - $(LREF FieldNames) - $(LREF FieldTypes) - $(DDSUBLINK spec/struct.html, struct_instance_properties, $(D tupleof)) - $(DDSUBLINK spec/traits, isNested, $(D __traits(isNested, ...))). - $(DDSUBLINK spec/traits, isSame, $(D __traits(isSame, ...))). - +/ -template FieldSymbols(T) -if (isAggregateType!T) -{ - static if (is(T == struct) && __traits(isNested, T)) - { - import phobos.sys.meta : AliasSeq; - alias FieldSymbols = AliasSeq!(T.tupleof[0 .. $ - 1]); - } - else - alias FieldSymbols = T.tupleof; -} - -/// -@safe unittest -{ - import phobos.sys.meta : AliasSeq; - - struct S - { - int x; - float y; - } - static assert(__traits(isSame, FieldSymbols!S, AliasSeq!(S.x, S.y))); - - // FieldSymbols!S and S.tupleof are the same, because S is not nested. - static assert(__traits(isSame, FieldSymbols!S, S.tupleof)); - - // Note that type qualifiers _should_ be passed on to the result, but due - // to https://issues.dlang.org/show_bug.cgi?id=24516, they aren't. - // FieldTypes does not have this problem, because it aliases the types - // rather than the symbols, so if you need the types from the symbols, you - // should use either FieldTypes or tupleof until the compiler bug has been - // fixed (and if you use tupleof, you need to avoid aliasing the result - // before getting the types from it). - static assert(is(typeof(FieldSymbols!S[0]) == int)); - - // These currently fail when they shouldn't: - //static assert(is(typeof(FieldSymbols!(const S)[0]) == const int)); - //static assert(is(typeof(FieldSymbols!(shared S)[0]) == shared int)); - - class C - { - // static variables are not included. - static int var; - - // Manifest constants are not included. - enum lang = "dlang"; - - // Functions are not included, even if they're @property functions. - @property int foo() { return 42; } - - string s; - int i; - int[] arr; - } - static assert(__traits(isSame, FieldSymbols!C, AliasSeq!(C.s, C.i, C.arr))); - - // FieldSymbols!C and C.tupleof have the same symbols, because they are - // always the same for classes. - static assert(__traits(isSame, FieldSymbols!C, C.tupleof)); - - // Only direct member variables are included. Member variables from any base - // classes are not. - class D : C - { - real r; - } - static assert(__traits(isSame, FieldSymbols!D, AliasSeq!(D.r))); - static assert(__traits(isSame, FieldSymbols!D, D.tupleof)); - - // FieldSymbols will always be empty for an interface, since it's not legal - // for interfaces to have member variables. - interface I - { - } - static assert(FieldSymbols!I.length == 0); - static assert(I.tupleof.length == 0); - - union U - { - int i; - double d; - long l; - S s; - } - static assert(__traits(isSame, FieldSymbols!U, AliasSeq!(U.i, U.d, U.l, U.s))); - - // FieldSymbols!C and C.tupleof have the same symbols, because they are - // always the same for unions. - static assert(__traits(isSame, FieldSymbols!U, U.tupleof)); - - // FieldSymbols only operates on aggregate types. - static assert(!__traits(compiles, FieldSymbols!int)); - static assert(!__traits(compiles, FieldSymbols!(S*))); - static assert(!__traits(compiles, FieldSymbols!(C[]))); -} - -/// Some examples with nested types. -@safe unittest -{ - import phobos.sys.meta : AliasSeq; - - int outside; - - struct S - { - long l; - string s; - - void foo() { outside = 2; } - } - static assert(__traits(isNested, S)); - static assert(__traits(isSame, FieldSymbols!S, AliasSeq!(S.l, S.s))); - - // FieldSymbols!S and S.tupleof are not the same, because S is nested, and - // the context pointer to the outer scope is included in S.tupleof, whereas - // it is excluded from FieldSymbols!S. - static assert(__traits(isSame, S.tupleof[0 .. $ - 1], AliasSeq!(S.l, S.s))); - static assert(S.tupleof[$ - 1].stringof == "this"); - - class C - { - bool b; - int* ptr; - - void foo() { outside = 7; } - } - static assert(__traits(isNested, C)); - static assert(__traits(isSame, FieldSymbols!C, AliasSeq!(C.b, C.ptr))); - - // FieldSymbols!C and C.tupleof have the same symbols, because they are - // always the same for classes. No context pointer is provided as part of - // tupleof for nested classes. - static assert(__traits(isSame, FieldSymbols!C, C.tupleof)); - - // __traits(isNested, ...) is never true for interfaces or unions, since - // they cannot have a context pointer to an outer scope. So, tupleof and - // FieldSymbols will always be the same for interfaces and unions. -} - -@safe unittest -{ - import phobos.sys.meta : AliasSeq; - - { - static struct S0 {} - static assert(FieldSymbols!S0.length == 0); - - static struct S1 { int a; } - static assert(__traits(isSame, FieldSymbols!S1, AliasSeq!(S1.a))); - - static struct S2 { int a; string b; } - static assert(__traits(isSame, FieldSymbols!S2, AliasSeq!(S2.a, S2.b))); - - static struct S3 { int a; string b; real c; } - static assert(__traits(isSame, FieldSymbols!S3, AliasSeq!(S3.a, S3.b, S3.c))); - } - { - int i; - struct S0 { void foo() { i = 0; }} - static assert(FieldSymbols!S0.length == 0); - static assert(__traits(isNested, S0)); - - struct S1 { int a; void foo() { i = 0; } } - static assert(__traits(isSame, FieldSymbols!S1, AliasSeq!(S1.a))); - static assert(__traits(isNested, S1)); - - struct S2 { int a; string b; void foo() { i = 0; } } - static assert(__traits(isSame, FieldSymbols!S2, AliasSeq!(S2.a, S2.b))); - static assert(__traits(isNested, S2)); - - struct S3 { int a; string b; real c; void foo() { i = 0; } } - static assert(__traits(isSame, FieldSymbols!S3, AliasSeq!(S3.a, S3.b, S3.c))); - static assert(__traits(isNested, S3)); - } - { - static class C0 {} - static assert(FieldSymbols!C0.length == 0); - - static class C1 { int a; } - static assert(__traits(isSame, FieldSymbols!C1, AliasSeq!(C1.a))); - - static class C2 { int a; string b; } - static assert(__traits(isSame, FieldSymbols!C2, AliasSeq!(C2.a, C2.b))); - - static class C3 { int a; string b; real c; } - static assert(__traits(isSame, FieldSymbols!C3, AliasSeq!(C3.a, C3.b, C3.c))); - - static class D0 : C3 {} - static assert(FieldSymbols!D0.length == 0); - - static class D1 : C3 { bool x; } - static assert(__traits(isSame, FieldSymbols!D1, AliasSeq!(D1.x))); - - static class D2 : C3 { bool x; int* y; } - static assert(__traits(isSame, FieldSymbols!D2, AliasSeq!(D2.x, D2.y))); - - static class D3 : C3 { bool x; int* y; short[] z; } - static assert(__traits(isSame, FieldSymbols!D3, AliasSeq!(D3.x, D3.y, D3.z))); - } - { - int i; - class C0 { void foo() { i = 0; }} - static assert(FieldSymbols!C0.length == 0); - static assert(__traits(isNested, C0)); - - class C1 { int a; void foo() { i = 0; } } - static assert(__traits(isSame, FieldSymbols!C1, AliasSeq!(C1.a))); - static assert(__traits(isNested, C1)); - - class C2 { int a; string b; void foo() { i = 0; } } - static assert(__traits(isSame, FieldSymbols!C2, AliasSeq!(C2.a, C2.b))); - static assert(__traits(isNested, C2)); - - class C3 { int a; string b; real c; void foo() { i = 0; } } - static assert(__traits(isSame, FieldSymbols!C3, AliasSeq!(C3.a, C3.b, C3.c))); - static assert(__traits(isNested, C3)); - - class D0 : C3 {} - static assert(FieldSymbols!D0.length == 0); - static assert(__traits(isNested, D0)); - - class D1 : C3 { bool x; } - static assert(__traits(isSame, FieldSymbols!D1, AliasSeq!(D1.x))); - static assert(__traits(isNested, D1)); - - class D2 : C3 { bool x; int* y; } - static assert(__traits(isSame, FieldSymbols!D2, AliasSeq!(D2.x, D2.y))); - static assert(__traits(isNested, D2)); - - class D3 : C3 { bool x; int* y; short[] z; } - static assert(__traits(isSame, FieldSymbols!D3, AliasSeq!(D3.x, D3.y, D3.z))); - static assert(__traits(isNested, D3)); - } - { - static union U0 {} - static assert(FieldSymbols!U0.length == 0); - - static union U1 { int a; } - static assert(__traits(isSame, FieldSymbols!U1, AliasSeq!(U1.a))); - - static union U2 { int a; string b; } - static assert(__traits(isSame, FieldSymbols!U2, AliasSeq!(U2.a, U2.b))); - - static union U3 { int a; string b; real c; } - static assert(__traits(isSame, FieldSymbols!U3, AliasSeq!(U3.a, U3.b, U3.c))); - } - { - static struct S - { - enum e = 42; - static str = "foobar"; - - string name() { return "foo"; } - - int[] arr; - - struct Inner1 { int i; } - - static struct Inner2 { long gnol; } - - union { int a; string b; } - - alias Foo = Inner1; - } - - static assert(__traits(isSame, FieldSymbols!S, AliasSeq!(S.arr, S.a, S.b))); - static assert(__traits(isSame, FieldSymbols!(const S), AliasSeq!(S.arr, S.a, S.b))); - static assert(__traits(isSame, FieldSymbols!(S.Inner1), AliasSeq!(S.Inner1.i))); - static assert(__traits(isSame, FieldSymbols!(S.Inner2), AliasSeq!(S.Inner2.gnol))); - } -} - -/++ - Evaluates to an $(D AliasSeq) of the types of the member variables of an - aggregate type (i.e. a struct, class, interface, or union). - - These are fields which take up memory space within an instance of the type - (i.e. not enums / manifest constants, since they don't take up memory - space, and not static member variables, since they don't take up memory - space within an instance). - - Hidden fields (like the virtual function table pointer or the context - pointer for nested types) are not included. - - For classes, only the direct member variables are included and not those - of any base classes. - - For interfaces, the result of FieldTypes is always empty, because - interfaces cannot have member variables. However, because interfaces are - aggregate types, they work with FieldTypes for consistency so that code - that's written to work on aggregate types doesn't have to worry about - whether it's dealing with an interface. - - See_Also: - $(LREF FieldNames) - $(LREF FieldSymbols) - $(DDSUBLINK spec/struct.html, struct_instance_properties, $(D tupleof)) - +/ -template FieldTypes(T) -if (isAggregateType!T) -{ - static if (is(T == struct) && __traits(isNested, T)) - alias FieldTypes = typeof(T.tupleof[0 .. $ - 1]); - else - alias FieldTypes = typeof(T.tupleof); -} - -/// -@safe unittest -{ - import phobos.sys.meta : AliasSeq; - - struct S - { - int x; - float y; - } - static assert(is(FieldTypes!S == AliasSeq!(int, float))); - - // Type qualifers will be passed on to the result. - static assert(is(FieldTypes!(const S) == AliasSeq!(const int, const float))); - static assert(is(FieldTypes!(shared S) == AliasSeq!(shared int, shared float))); - - class C - { - // static variables are not included. - static int var; - - // Manifest constants are not included. - enum lang = "dlang"; - - // Functions are not included, even if they're @property functions. - @property int foo() { return 42; } - - string s; - int i; - int[] arr; - } - static assert(is(FieldTypes!C == AliasSeq!(string, int, int[]))); - - // Only direct member variables are included. Member variables from any base - // classes are not. - class D : C - { - real r; - } - static assert(is(FieldTypes!D == AliasSeq!real)); - - // FieldTypes will always be empty for an interface, since it's not legal - // for interfaces to have member variables. - interface I - { - } - static assert(FieldTypes!I.length == 0); - - union U - { - int i; - double d; - long l; - S s; - } - static assert(is(FieldTypes!U == AliasSeq!(int, double, long, S))); - - // FieldTypes only operates on aggregate types. - static assert(!__traits(compiles, FieldTypes!int)); - static assert(!__traits(compiles, FieldTypes!(S*))); - static assert(!__traits(compiles, FieldTypes!(C[]))); -} - -@safe unittest -{ - import phobos.sys.meta : AliasSeq; - - { - static struct S0 {} - static assert(FieldTypes!S0.length == 0); - - static struct S1 { int a; } - static assert(is(FieldTypes!S1 == AliasSeq!int)); - - static struct S2 { int a; string b; } - static assert(is(FieldTypes!S2 == AliasSeq!(int, string))); - - static struct S3 { int a; string b; real c; } - static assert(is(FieldTypes!S3 == AliasSeq!(int, string, real))); - } - { - int i; - struct S0 { void foo() { i = 0; }} - static assert(FieldTypes!S0.length == 0); - static assert(__traits(isNested, S0)); - - struct S1 { int a; void foo() { i = 0; } } - static assert(is(FieldTypes!S1 == AliasSeq!int)); - static assert(__traits(isNested, S1)); - - struct S2 { int a; string b; void foo() { i = 0; } } - static assert(is(FieldTypes!S2 == AliasSeq!(int, string))); - static assert(__traits(isNested, S2)); - - struct S3 { int a; string b; real c; void foo() { i = 0; } } - static assert(is(FieldTypes!S3 == AliasSeq!(int, string, real))); - static assert(__traits(isNested, S3)); - } - { - static class C0 {} - static assert(FieldTypes!C0.length == 0); - - static class C1 { int a; } - static assert(is(FieldTypes!C1 == AliasSeq!int)); - - static class C2 { int a; string b; } - static assert(is(FieldTypes!C2 == AliasSeq!(int, string))); - - static class C3 { int a; string b; real c; } - static assert(is(FieldTypes!C3 == AliasSeq!(int, string, real))); - - static class D0 : C3 {} - static assert(FieldTypes!D0.length == 0); - - static class D1 : C3 { bool x; } - static assert(is(FieldTypes!D1 == AliasSeq!bool)); - - static class D2 : C3 { bool x; int* y; } - static assert(is(FieldTypes!D2 == AliasSeq!(bool, int*))); - - static class D3 : C3 { bool x; int* y; short[] z; } - static assert(is(FieldTypes!D3 == AliasSeq!(bool, int*, short[]))); - } - { - int i; - class C0 { void foo() { i = 0; }} - static assert(FieldTypes!C0.length == 0); - static assert(__traits(isNested, C0)); - - class C1 { int a; void foo() { i = 0; } } - static assert(is(FieldTypes!C1 == AliasSeq!int)); - static assert(__traits(isNested, C1)); - - class C2 { int a; string b; void foo() { i = 0; } } - static assert(is(FieldTypes!C2 == AliasSeq!(int, string))); - static assert(__traits(isNested, C2)); - - class C3 { int a; string b; real c; void foo() { i = 0; } } - static assert(is(FieldTypes!C3 == AliasSeq!(int, string, real))); - static assert(__traits(isNested, C3)); - - class D0 : C3 {} - static assert(FieldTypes!D0.length == 0); - static assert(__traits(isNested, D0)); - - class D1 : C3 { bool x; } - static assert(is(FieldTypes!D1 == AliasSeq!bool)); - static assert(__traits(isNested, D1)); - - class D2 : C3 { bool x; int* y; } - static assert(is(FieldTypes!D2 == AliasSeq!(bool, int*))); - static assert(__traits(isNested, D2)); - - class D3 : C3 { bool x; int* y; short[] z; } - static assert(is(FieldTypes!D3 == AliasSeq!(bool, int*, short[]))); - static assert(__traits(isNested, D3)); - } - { - static union U0 {} - static assert(FieldTypes!U0.length == 0); - - static union U1 { int a; } - static assert(is(FieldTypes!U1 == AliasSeq!int)); - - static union U2 { int a; string b; } - static assert(is(FieldTypes!U2 == AliasSeq!(int, string))); - - static union U3 { int a; string b; real c; } - static assert(is(FieldTypes!U3 == AliasSeq!(int, string, real))); - } - { - static struct S - { - enum e = 42; - static str = "foobar"; - - string name() { return "foo"; } - - int[] arr; - - struct Inner1 { int i; } - - static struct Inner2 { long gnol; } - - union { int a; string b; } - - alias Foo = Inner1; - } - - static assert(is(FieldTypes!S == AliasSeq!(int[], int, string))); - static assert(is(FieldTypes!(const S) == AliasSeq!(const(int[]), const int, const string))); - static assert(is(FieldTypes!(S.Inner1) == AliasSeq!int)); - static assert(is(FieldTypes!(S.Inner2) == AliasSeq!long)); - } -} - -/++ - Takes a type which is an associative array and evaluates to the type of the - keys in that associative array. - - See_Also: - $(LREF ValueType) - +/ -alias KeyType(V : V[K], K) = K; - -/// -@safe unittest -{ - static assert(is(KeyType!(int[string]) == string)); - static assert(is(KeyType!(string[int]) == int)); - - static assert(is(KeyType!(string[const int]) == const int)); - static assert(is(KeyType!(const int[string]) == string)); - - struct S - { - int i; - } - - string[S] aa1; - static assert(is(KeyType!(typeof(aa1)) == S)); - - S[string] aa2; - static assert(is(KeyType!(typeof(aa2)) == string)); - - KeyType!(typeof(aa1)) key1 = S(42); - KeyType!(typeof(aa2)) key2 = "foo"; - - // Key types with indirections have their inner layers treated as const - // by the compiler, because the values of keys can't change, or the hash - // value could change, putting the associative array in an invalid state. - static assert(is(KeyType!(bool[string[]]) == const(string)[])); - static assert(is(KeyType!(bool[int*]) == const(int)*)); - - // If the given type is not an AA, then KeyType won't compile. - static assert(!__traits(compiles, KeyType!int)); - static assert(!__traits(compiles, KeyType!(int[]))); -} - -/++ - Takes a type which is an associative array and evaluates to the type of the - values in that associative array. - - See_Also: - $(LREF KeyType) - +/ -alias ValueType(V : V[K], K) = V; - -/// -@safe unittest -{ - static assert(is(ValueType!(int[string]) == int)); - static assert(is(ValueType!(string[int]) == string)); - - static assert(is(ValueType!(string[const int]) == string)); - static assert(is(ValueType!(const int[string]) == const int)); - - struct S - { - int i; - } - - string[S] aa1; - static assert(is(ValueType!(typeof(aa1)) == string)); - - S[string] aa2; - static assert(is(ValueType!(typeof(aa2)) == S)); - - ValueType!(typeof(aa1)) value1 = "foo"; - ValueType!(typeof(aa2)) value2 = S(42); - - // If the given type is not an AA, then ValueType won't compile. - static assert(!__traits(compiles, ValueType!int)); - static assert(!__traits(compiles, ValueType!(int[]))); -} - -/++ - Evaluates to the original / ultimate base type of an enum type - or for - non-enum types, it evaluates to the type that it's given. - - If the base type of the given enum type is not an enum, then the result of - OriginalType is its direct base type. However, if the base type of the - given enum is also an enum, then OriginalType gives the ultimate base type - - that is, it keeps getting the base type for each succesive enum in the - chain until it gets to a base type that isn't an enum, and that's the - result. So, the result will never be an enum type. - - If the given type has any qualifiers, the result will have those same - qualifiers. - +/ -version (StdDdoc) template OriginalType(T) -{ - import core.internal.traits : CoreOriginalType = OriginalType; - alias OriginalType = CoreOriginalType!T; -} -else -{ - import core.internal.traits : CoreOriginalType = OriginalType; - alias OriginalType = CoreOriginalType; -} - -/// -@safe unittest -{ - enum E { a, b, c } - static assert(is(OriginalType!E == int)); - - enum F : E { x = E.a } - static assert(is(OriginalType!F == int)); - - enum G : F { y = F.x } - static assert(is(OriginalType!G == int)); - static assert(is(OriginalType!(const G) == const int)); - static assert(is(OriginalType!(immutable G) == immutable int)); - static assert(is(OriginalType!(shared G) == shared int)); - - enum C : char { a = 'a', b = 'b' } - static assert(is(OriginalType!C == char)); - - enum D : string { d = "dlang" } - static assert(is(OriginalType!D == string)); - - static assert(is(OriginalType!int == int)); - static assert(is(OriginalType!(const long) == const long)); - static assert(is(OriginalType!string == string)); - - // OriginalType gets the base type of enums and for all other types gives - // the same type back. It does nothing special for other types - like - // classes - where one could talk about the type having a base type. - class Base {} - class Derived : Base {} - static assert(is(OriginalType!Base == Base)); - static assert(is(OriginalType!Derived == Derived)); -} - -/++ - Removes the outer layer of $(D const), $(D inout), or $(D immutable) - from type $(D T). - - If none of those qualifiers have been applied to the outer layer of - type $(D T), then the result is $(D T). - - For the built-in scalar types (that is $(D bool), the character types, and - the numeric types), they only have one layer, so $(D const U) simply becomes - $(D U). - - Where the layers come in is pointers and arrays. $(D const(U*)) becomes - $(D const(U)*), and $(D const(U[])), becomes $(D const(U)[]). So, a pointer - goes from being fully $(D const) to being a mutable pointer to $(D const), - and a dynamic array goes from being fully $(D const) to being a mutable - dynamic array of $(D const) elements. And if there are multiple layers of - pointers or arrays, it's just that outer layer which is affected - e.g. - $(D const(U**)) would become $(D const(U*)*). - - For user-defined types, the effect is that $(D const U) becomes $(D U), and - how that affects member variables depends on the type of the member - variable. If a member variable is explicitly marked with any mutability - qualifiers, then it will continue to have those qualifiers even after - Unconst has stripped all mutability qualifiers from the containing type. - However, if a mutability qualifier was on the member variable only because - the containing type had that qualifier, then when Unconst removes the - qualifier from the containing type, it is removed from the member variable - as well. - - Also, Unconst has no effect on what a templated type is instantiated - with, so if a templated type is instantiated with a template argument which - has a mutability qualifier, the template instantiation will not change. - +/ -version (StdDdoc) template Unconst(T) -{ - import core.internal.traits : CoreUnconst = Unconst; - alias Unconst = CoreUnconst!T; -} -else -{ - import core.internal.traits : CoreUnconst = Unconst; - alias Unconst = CoreUnconst; -} - -/// -@safe unittest -{ - static assert(is(Unconst!( int) == int)); - static assert(is(Unconst!( const int) == int)); - static assert(is(Unconst!( inout int) == int)); - static assert(is(Unconst!( inout const int) == int)); - static assert(is(Unconst!(shared int) == shared int)); - static assert(is(Unconst!(shared const int) == shared int)); - static assert(is(Unconst!(shared inout int) == shared int)); - static assert(is(Unconst!(shared inout const int) == shared int)); - static assert(is(Unconst!( immutable int) == int)); - - // Only the outer layer of immutable is removed. - // immutable(int[]) -> immutable(int)[] - alias ImmIntArr = immutable(int[]); - static assert(is(Unconst!ImmIntArr == immutable(int)[])); - - // Only the outer layer of const is removed. - // immutable(int*) -> immutable(int)* - alias ConstIntPtr = const(int*); - static assert(is(Unconst!ConstIntPtr == const(int)*)); - - // const(int)* -> const(int)* - alias PtrToConstInt = const(int)*; - static assert(is(Unconst!PtrToConstInt == const(int)*)); - - static struct S - { - int* ptr; - const int* cPtr; - shared int* sPtr; - } - - const S s; - static assert(is(typeof(s) == const S)); - static assert(is(typeof(typeof(s).ptr) == const int*)); - static assert(is(typeof(typeof(s).cPtr) == const int*)); - static assert(is(typeof(typeof(s).sPtr) == const shared int*)); - - // For user-defined types, all mutability qualifiers that are applied to - // member variables only because the containing type has them are removed, - // but the ones that are directly on those member variables remain. - - // const S -> S - static assert(is(Unconst!(typeof(s)) == S)); - static assert(is(typeof(Unconst!(typeof(s)).ptr) == int*)); - static assert(is(typeof(Unconst!(typeof(s)).cPtr) == const int*)); - static assert(is(typeof(Unconst!(typeof(s)).sPtr) == shared int*)); - - static struct Foo(T) - { - T* ptr; - } - - // The qualifier on the type is removed, but the qualifier on the template - // argument is not. - static assert(is(Unconst!(const(Foo!(const int))) == Foo!(const int))); - static assert(is(Unconst!(Foo!(const int)) == Foo!(const int))); - static assert(is(Unconst!(const(Foo!int)) == Foo!int)); -} - -/++ - Removes the outer layer of $(D shared) from type $(D T). - - If $(D shared) has not been applied to the outer layer of type $(D T), then - the result is $(D T). - - Note that while $(D immutable) is implicitly $(D shared), it is unaffected - by Unshared. Only explicit $(D shared) is removed. - - For the built-in scalar types (that is $(D bool), the character types, and - the numeric types), they only have one layer, so $(D shared U) simply - becomes $(D U). - - Where the layers come in is pointers and arrays. $(D shared(U*)) becomes - $(D shared(U)*), and $(D shared(U[])), becomes $(D shared(U)[]). So, a - pointer goes from being fully $(D shared) to being a mutable pointer to - $(D shared), and a dynamic array goes from being fully $(D shared) to being - a mutable dynamic array of $(D shared) elements. And if there are multiple - layers of pointers or arrays, it's just that outer layer which is affected - - e.g. $(D shared(U**)) would become $(D shared(U*)*). - - For user-defined types, the effect is that $(D shared U) becomes $(D U), - and how that affects member variables depends on the type of the member - variable. If a member variable is explicitly marked with $(D shared), then - it will continue to be $(D shared) even after Unshared has stripped - $(D shared) from the containing type. However, if $(D shared) was on the - member variable only because the containing type was $(D shared), then when - Unshared removes the qualifier from the containing type, it is removed from - the member variable as well. - - Also, Unshared has no effect on what a templated type is instantiated - with, so if a templated type is instantiated with a template argument which - has a type qualifier, the template instantiation will not change. - +/ -template Unshared(T) -{ - static if (is(T == shared U, U)) - alias Unshared = U; - else - alias Unshared = T; -} - -/// -@safe unittest -{ - static assert(is(Unshared!( int) == int)); - static assert(is(Unshared!( const int) == const int)); - static assert(is(Unshared!( inout int) == inout int)); - static assert(is(Unshared!( inout const int) == inout const int)); - static assert(is(Unshared!(shared int) == int)); - static assert(is(Unshared!(shared const int) == const int)); - static assert(is(Unshared!(shared inout int) == inout int)); - static assert(is(Unshared!(shared inout const int) == inout const int)); - static assert(is(Unshared!( immutable int) == immutable int)); - - // Only the outer layer of shared is removed. - // shared(int[]) -> shared(int)[] - alias SharedIntArr = shared(int[]); - static assert(is(Unshared!SharedIntArr == shared(int)[])); - - // Only the outer layer of shared is removed. - // shared(int*) -> shared(int)* - alias SharedIntPtr = shared(int*); - static assert(is(Unshared!SharedIntPtr == shared(int)*)); - - // shared(int)* -> shared(int)* - alias PtrToSharedInt = shared(int)*; - static assert(is(Unshared!PtrToSharedInt == shared(int)*)); - - // immutable is unaffected - alias ImmutableArr = immutable(int[]); - static assert(is(Unshared!ImmutableArr == immutable(int[]))); - - static struct S - { - int* ptr; - const int* cPtr; - shared int* sPtr; - } - - shared S s; - static assert(is(typeof(s) == shared S)); - static assert(is(typeof(typeof(s).ptr) == shared int*)); - static assert(is(typeof(typeof(s).cPtr) == const shared int*)); - static assert(is(typeof(typeof(s).sPtr) == shared int*)); - - // For user-defined types, if shared is applied to a member variable only - // because the containing type is shared, then shared is removed from that - // member variable, but if the member variable is directly marked as shared, - // then it continues to be shared. - - // shared S -> S - static assert(is(Unshared!(typeof(s)) == S)); - static assert(is(typeof(Unshared!(typeof(s)).ptr) == int*)); - static assert(is(typeof(Unshared!(typeof(s)).cPtr) == const int*)); - static assert(is(typeof(Unshared!(typeof(s)).sPtr) == shared int*)); - - static struct Foo(T) - { - T* ptr; - } - - // The qualifier on the type is removed, but the qualifier on the template - // argument is not. - static assert(is(Unshared!(shared(Foo!(shared int))) == Foo!(shared int))); - static assert(is(Unshared!(Foo!(shared int)) == Foo!(shared int))); - static assert(is(Unshared!(shared(Foo!int)) == Foo!int)); -} - -/++ - Removes the outer layer of all type qualifiers from type $(D T) - this - includes $(D shared). - - If no type qualifiers have been applied to the outer layer of type $(D T), - then the result is $(D T). - - For the built-in scalar types (that is $(D bool), the character types, and - the numeric types), they only have one layer, so $(D const U) simply becomes - $(D U). - - Where the layers come in is pointers and arrays. $(D const(U*)) becomes - $(D const(U)*), and $(D const(U[])), becomes $(D const(U)[]). So, a pointer - goes from being fully $(D const) to being a mutable pointer to $(D const), - and a dynamic array goes from being fully $(D const) to being a mutable - dynamic array of $(D const) elements. And if there are multiple layers of - pointers or arrays, it's just that outer layer which is affected - e.g. - $(D shared(U**)) would become $(D shared(U*)*). - - For user-defined types, the effect is that $(D const U) becomes $(D U), and - how that affects member variables depends on the type of the member - variable. If a member variable is explicitly marked with any qualifiers, - then it will continue to have those qualifiers even after Unqualified has - stripped all qualifiers from the containing type. However, if a qualifier - was on the member variable only because the containing type had that - qualifier, then when Unqualified removes the qualifier from the containing - type, it is removed from the member variable as well. - - Also, Unqualified has no effect on what a templated type is instantiated - with, so if a templated type is instantiated with a template argument which - has a type qualifier, the template instantiation will not change. - - Note that in most cases, $(LREF Unconst) or $(LREF Unshared) should be used - rather than Unqualified, because in most cases, code is not designed to - work with $(D shared) and thus doing type checks which remove $(D shared) - will allow $(D shared) types to pass template constraints when they won't - actually work with the code. And when code is designed to work with - $(D shared), it's often the case that the type checks need to take - $(D const) into account in order to avoid accidentally mutating $(D const) - data and violating the type system. - - In particular, historically, a lot of D code has used - $(REF Unqual, std, traits) (which is equivalent to phobos.sys.traits' - Unqualified) when the programmer's intent was to remove $(D const), and - $(D shared) wasn't actually considered at all. And in such cases, the code - really should use $(LREF Unconst) instead. - - But of course, if a template constraint or $(D static if) really needs to - strip off both the mutability qualifiers and $(D shared) for what it's - testing for, then that's what Unqualified is for. It's just that it's best - practice to use $(LREF Unconst) when it's not clear that $(D shared) should - be removed as well. - +/ -version (StdDdoc) template Unqualified(T) -{ - import core.internal.traits : CoreUnqualified = Unqual; - alias Unqualified = CoreUnqualified!(T); -} -else -{ - import core.internal.traits : CoreUnqualified = Unqual; - alias Unqualified = CoreUnqualified; -} - -/// -@safe unittest -{ - static assert(is(Unqualified!( int) == int)); - static assert(is(Unqualified!( const int) == int)); - static assert(is(Unqualified!( inout int) == int)); - static assert(is(Unqualified!( inout const int) == int)); - static assert(is(Unqualified!(shared int) == int)); - static assert(is(Unqualified!(shared const int) == int)); - static assert(is(Unqualified!(shared inout int) == int)); - static assert(is(Unqualified!(shared inout const int) == int)); - static assert(is(Unqualified!( immutable int) == int)); - - // Only the outer layer of immutable is removed. - // immutable(int[]) -> immutable(int)[] - alias ImmIntArr = immutable(int[]); - static assert(is(Unqualified!ImmIntArr == immutable(int)[])); - - // Only the outer layer of const is removed. - // const(int*) -> const(int)* - alias ConstIntPtr = const(int*); - static assert(is(Unqualified!ConstIntPtr == const(int)*)); - - // const(int)* -> const(int)* - alias PtrToConstInt = const(int)*; - static assert(is(Unqualified!PtrToConstInt == const(int)*)); - - // Only the outer layer of shared is removed. - // shared(int*) -> shared(int)* - alias SharedIntPtr = shared(int*); - static assert(is(Unqualified!SharedIntPtr == shared(int)*)); - - // shared(int)* -> shared(int)* - alias PtrToSharedInt = shared(int)*; - static assert(is(Unqualified!PtrToSharedInt == shared(int)*)); - - // Both const and shared are removed from the outer layer. - // shared const int[] -> shared(const(int))[] - alias SharedConstIntArr = shared const(int[]); - static assert(is(Unqualified!SharedConstIntArr == shared(const(int))[])); - - static struct S - { - int* ptr; - const int* cPtr; - shared int* sPtr; - } - - shared const S s; - static assert(is(typeof(s) == shared const S)); - static assert(is(typeof(typeof(s).ptr) == shared const int*)); - static assert(is(typeof(typeof(s).cPtr) == shared const int*)); - static assert(is(typeof(typeof(s).sPtr) == shared const int*)); - - // For user-defined types, all qualifiers that are applied to member - // variables only because the containing type has them are removed, but the - // ones that are directly on those member variables remain. - - // shared const S -> S - static assert(is(Unqualified!(typeof(s)) == S)); - static assert(is(typeof(Unqualified!(typeof(s)).ptr) == int*)); - static assert(is(typeof(Unqualified!(typeof(s)).cPtr) == const int*)); - static assert(is(typeof(Unqualified!(typeof(s)).sPtr) == shared int*)); - - static struct Foo(T) - { - T* ptr; - } - - // The qualifiers on the type are removed, but the qualifiers on the - // template argument are not. - static assert(is(Unqualified!(const(Foo!(const int))) == Foo!(const int))); - static assert(is(Unqualified!(Foo!(const int)) == Foo!(const int))); - static assert(is(Unqualified!(const(Foo!int)) == Foo!int)); -} - -/++ - Applies $(D const) to the given type. - - This is primarily useful in conjunction with templates that take a template - predicate (such as many of the templates in phobos.sys.meta), since while in - most cases, you can simply do $(D const T) or $(D const(T)) to make $(D T) - $(D const), with something like $(REF Map, phobos, sys, meta), you need to - pass a template to be applied. - - See_Also: - $(LREF ImmutableOf) - $(LREF InoutOf) - $(LREF SharedOf) - +/ -alias ConstOf(T) = const T; - -/// -@safe unittest -{ - static assert(is(ConstOf!int == const int)); - static assert(is(ConstOf!(const int) == const int)); - static assert(is(ConstOf!(inout int) == inout const int)); - static assert(is(ConstOf!(shared int) == const shared int)); - - // Note that const has no effect on immutable. - static assert(is(ConstOf!(immutable int) == immutable int)); - - import phobos.sys.meta : AliasSeq, Map; - - alias Types = AliasSeq!(int, long, - bool*, ubyte[], - string, immutable(string)); - alias WithConst = Map!(ConstOf, Types); - static assert(is(WithConst == - AliasSeq!(const int, const long, - const(bool*), const(ubyte[]), - const(string), immutable(string)))); -} - -/++ - Applies $(D immutable) to the given type. - - This is primarily useful in conjunction with templates that take a template - predicate (such as many of the templates in phobos.sys.meta), since while in - most cases, you can simply do $(D immutable T) or $(D immutable(T)) to make - $(D T) $(D immutable), with something like $(REF Map, phobos, sys, meta), - you need to pass a template to be applied. - - See_Also: - $(LREF ConstOf) - $(LREF InoutOf) - $(LREF SharedOf) - +/ -alias ImmutableOf(T) = immutable T; - -/// -@safe unittest -{ - static assert(is(ImmutableOf!int == immutable int)); - - // Note that immutable overrides const and inout. - static assert(is(ImmutableOf!(const int) == immutable int)); - static assert(is(ImmutableOf!(inout int) == immutable int)); - - // Note that immutable overrides shared, since immutable is implicitly - // shared. - static assert(is(ImmutableOf!(shared int) == immutable int)); - - static assert(is(ImmutableOf!(immutable int) == immutable int)); - - import phobos.sys.meta : AliasSeq, Map; - - alias Types = AliasSeq!(int, long, - bool*, ubyte[], - string, immutable(string)); - alias WithImmutable = Map!(ImmutableOf, Types); - static assert(is(WithImmutable == - AliasSeq!(immutable int, immutable long, - immutable(bool*), immutable(ubyte[]), - immutable(string), immutable(string)))); -} - -/++ - Applies $(D inout) to the given type. - - This is primarily useful in conjunction with templates that take a template - predicate (such as many of the templates in phobos.sys.meta), since while in - most cases, you can simply do $(D inout T) or $(D inout(T)) to make $(D T) - $(D inout), with something like $(REF Map, phobos, sys, meta), you need to - pass a template to be applied. - - See_Also: - $(LREF ConstOf) - $(LREF ImmutableOf) - $(LREF SharedOf) - +/ -alias InoutOf(T) = inout T; - -/// -@safe unittest -{ - static assert(is(InoutOf!int == inout int)); - static assert(is(InoutOf!(const int) == inout const int)); - static assert(is(InoutOf!(inout int) == inout int)); - static assert(is(InoutOf!(shared int) == inout shared int)); - - // Note that inout has no effect on immutable. - static assert(is(InoutOf!(immutable int) == immutable int)); - - import phobos.sys.meta : AliasSeq, Map; - - alias Types = AliasSeq!(int, long, - bool*, ubyte[], - string, immutable(string)); - alias WithInout = Map!(InoutOf, Types); - static assert(is(WithInout == - AliasSeq!(inout int, inout long, - inout(bool*), inout(ubyte[]), - inout(string), immutable(string)))); -} - -/++ - Applies $(D shared) to the given type. - - This is primarily useful in conjunction with templates that take a template - predicate (such as many of the templates in phobos.sys.meta), since while in - most cases, you can simply do $(D shared T) or $(D shared(T)) to make $(D T) - $(D shared), with something like $(REF Map, phobos, sys, meta), you need to - pass a template to be applied. - - See_Also: - $(LREF ConstOf) - $(LREF ImmutableOf) - $(LREF InoutOf) - +/ -alias SharedOf(T) = shared T; - -/// -@safe unittest -{ - static assert(is(SharedOf!int == shared int)); - static assert(is(SharedOf!(const int) == const shared int)); - static assert(is(SharedOf!(inout int) == inout shared int)); - static assert(is(SharedOf!(shared int) == shared int)); - - // Note that shared has no effect on immutable, since immutable is - // implicitly shared. - static assert(is(SharedOf!(immutable int) == immutable int)); - - import phobos.sys.meta : AliasSeq, Map; - - alias Types = AliasSeq!(int, long, - bool*, ubyte[], - string, immutable(string)); - alias WithShared = Map!(SharedOf, Types); - static assert(is(WithShared == - AliasSeq!(shared int, shared long, - shared(bool*), shared(ubyte[]), - shared(string), immutable(string)))); -} - -// Needed for rvalueOf/lvalueOf because -// "inout on return means inout must be on a parameter as well" -private struct __InoutWorkaroundStruct {} - -/++ - Creates an lvalue or rvalue of type T to be used in conjunction with - $(D is(typeof(...))) or - $(DDSUBLINK spec/traits, compiles, $(D __traits(compiles, ...))). - - The idea is that some traits or other forms of conditional compilation need - to verify that a particular piece of code compiles with an rvalue or an - lvalue of a specific type, and these $(D @property) functions allow you to - get an rvalue or lvalue of a specific type to use within an expression that - is then tested to see whether it compiles. - - They're $(D @property) functions so that using $(D typeof) on them gives - the return type rather than the type of the function. - - Note that these functions are $(I not) defined, so if they're actually used - outside of type introspection, they'll result in linker errors. They're - entirely for testing that a particular piece of code compiles with an rvalue - or lvalue of the given type. - - The $(D __InoutWorkaroundStruct) parameter is entirely to make it so that - these work when the given type has the $(D inout) qualifier, since the - language requires that a function that returns an $(D inout) type also have - an $(D inout) type as a parameter. It should just be ignored. - +/ -@property T rvalueOf(T)(inout __InoutWorkaroundStruct = __InoutWorkaroundStruct.init); - -/++ Ditto +/ -@property ref T lvalueOf(T)(inout __InoutWorkaroundStruct = __InoutWorkaroundStruct.init); - -/// -@safe unittest -{ - static int foo(int); - static assert(is(typeof(foo(lvalueOf!int)) == int)); - static assert(is(typeof(foo(rvalueOf!int)) == int)); - - static bool bar(ref int); - static assert(is(typeof(bar(lvalueOf!int)) == bool)); - static assert(!is(typeof(bar(rvalueOf!int)))); - - static assert( is(typeof({ lvalueOf!int = 42; }))); - static assert(!is(typeof({ rvalueOf!int = 42; }))); - - static struct S {} - static assert( is(typeof({ lvalueOf!S = S.init; }))); - static assert(!is(typeof({ rvalueOf!S = S.init; }))); - - static struct NoAssign - { - @disable void opAssign(ref NoAssign); - } - static assert(!is(typeof({ lvalueOf!NoAssign = NoAssign.init; }))); - static assert(!is(typeof({ rvalueOf!NoAssign = NoAssign.init; }))); -} - -@system unittest -{ - import phobos.sys.meta : AliasSeq; - - void needLvalue(T)(ref T); - static struct S {} - int i; - struct Nested { void f() { ++i; } } - - static foreach (T; AliasSeq!(int, const int, immutable int, inout int, string, S, Nested, Object)) - { - static assert(!__traits(compiles, needLvalue(rvalueOf!T))); - static assert( __traits(compiles, needLvalue(lvalueOf!T))); - static assert(is(typeof(rvalueOf!T) == T)); - static assert(is(typeof(lvalueOf!T) == T)); - } - - static assert(!__traits(compiles, rvalueOf!int = 1)); - static assert( __traits(compiles, lvalueOf!byte = 127)); - static assert(!__traits(compiles, lvalueOf!byte = 128)); -} diff --git a/phobos/project.ddoc b/phobos/project.ddoc deleted file mode 100644 index dec95ae..0000000 --- a/phobos/project.ddoc +++ /dev/null @@ -1 +0,0 @@ -PROJECT=phobos diff --git a/phobos/std/algorithm/comparison.d b/phobos/std/algorithm/comparison.d deleted file mode 100644 index 5c70960..0000000 --- a/phobos/std/algorithm/comparison.d +++ /dev/null @@ -1,2523 +0,0 @@ -// Written in the D programming language. -/** -This is a submodule of $(MREF std, algorithm). -It contains generic comparison algorithms. - -$(SCRIPT inhibitQuickIndex = 1;) -$(BOOKTABLE Cheat Sheet, -$(TR $(TH Function Name) $(TH Description)) -$(T2 among, - Checks if a value is among a set of values, e.g. - `if (v.among(1, 2, 3)) // `v` is 1, 2 or 3`) -$(T2 castSwitch, - `(new A()).castSwitch((A a)=>1,(B b)=>2)` returns `1`.) -$(T2 clamp, - `clamp(1, 3, 6)` returns `3`. `clamp(4, 3, 6)` returns `4`.) -$(T2 cmp, - `cmp("abc", "abcd")` is `-1`, `cmp("abc", "aba")` is `1`, - and `cmp("abc", "abc")` is `0`.) -$(T2 either, - Return first parameter `p` that passes an `if (p)` test, e.g. - `either(0, 42, 43)` returns `42`.) -$(T2 equal, - Compares ranges for element-by-element equality, e.g. - `equal([1, 2, 3], [1.0, 2.0, 3.0])` returns `true`.) -$(T2 isPermutation, - `isPermutation([1, 2], [2, 1])` returns `true`.) -$(T2 isSameLength, - `isSameLength([1, 2, 3], [4, 5, 6])` returns `true`.) -$(T2 levenshteinDistance, - `levenshteinDistance("kitten", "sitting")` returns `3` by using - the $(LINK2 https://en.wikipedia.org/wiki/Levenshtein_distance, - Levenshtein distance algorithm).) -$(T2 levenshteinDistanceAndPath, - `levenshteinDistanceAndPath("kitten", "sitting")` returns - `tuple(3, "snnnsni")` by using the - $(LINK2 https://en.wikipedia.org/wiki/Levenshtein_distance, - Levenshtein distance algorithm).) -$(T2 max, - `max(3, 4, 2)` returns `4`.) -$(T2 min, - `min(3, 4, 2)` returns `2`.) -$(T2 mismatch, - `mismatch("oh hi", "ohayo")` returns `tuple(" hi", "ayo")`.) -$(T2 predSwitch, - `2.predSwitch(1, "one", 2, "two", 3, "three")` returns `"two"`.) -) - -Copyright: Andrei Alexandrescu 2008-. - -License: $(HTTP boost.org/LICENSE_1_0.txt, Boost License 1.0). - -Authors: $(HTTP erdani.com, Andrei Alexandrescu) - -Source: $(PHOBOSSRC std/algorithm/comparison.d) - -Macros: -T2=$(TR $(TDNW $(LREF $1)) $(TD $+)) - */ -module std.algorithm.comparison; - -import std.functional : unaryFun, binaryFun, lessThan, greaterThan; -import std.range.primitives; -import std.traits; -import std.meta : allSatisfy, anySatisfy; -import std.typecons : tuple, Tuple, Flag, Yes; - -import std.internal.attributes : betterC; - -/** -Find `value` _among `values`, returning the 1-based index -of the first matching value in `values`, or `0` if `value` -is not _among `values`. The predicate `pred` is used to -compare values, and uses equality by default. - -Params: - pred = The predicate used to compare the values. - value = The value to search for. - values = The values to compare the value to. - -Returns: - 0 if value was not found among the values, otherwise the index of the - found value plus one is returned. - -See_Also: -$(REF_ALTTEXT find, find, std,algorithm,searching) and $(REF_ALTTEXT canFind, canFind, std,algorithm,searching) for finding a value in a -range. -*/ -uint among(alias pred = (a, b) => a == b, Value, Values...) - (Value value, Values values) -if (Values.length != 0) -{ - foreach (uint i, ref v; values) - { - import std.functional : binaryFun; - if (binaryFun!pred(value, v)) return i + 1; - } - return 0; -} - -/// Ditto -template among(values...) -if (isExpressionTuple!values) -{ - uint among(Value)(Value value) - if (!is(CommonType!(Value, values) == void)) - { - switch (value) - { - foreach (uint i, v; values) - case v: - return i + 1; - default: - return 0; - } - } -} - -/// -@safe @nogc @betterC unittest -{ - assert(3.among(1, 42, 24, 3, 2)); - - if (auto pos = "bar".among("foo", "bar", "baz")) - assert(pos == 2); - else - assert(false); - - // 42 is larger than 24 - assert(42.among!((lhs, rhs) => lhs > rhs)(43, 24, 100) == 2); -} - -/** -Alternatively, `values` can be passed at compile-time, allowing for a more -efficient search, but one that only supports matching on equality: -*/ -@safe @nogc @betterC unittest -{ - assert(3.among!(2, 3, 4)); - assert("bar".among!("foo", "bar", "baz") == 2); -} - -@safe unittest -{ - import std.meta : AliasSeq; - - if (auto pos = 3.among(1, 2, 3)) - assert(pos == 3); - else - assert(false); - assert(!4.among(1, 2, 3)); - - auto position = "hello".among("hello", "world"); - assert(position); - assert(position == 1); - - alias values = AliasSeq!("foo", "bar", "baz"); - auto arr = [values]; - assert(arr[0 .. "foo".among(values)] == ["foo"]); - assert(arr[0 .. "bar".among(values)] == ["foo", "bar"]); - assert(arr[0 .. "baz".among(values)] == arr); - assert("foobar".among(values) == 0); - - if (auto pos = 3.among!(1, 2, 3)) - assert(pos == 3); - else - assert(false); - assert(!4.among!(1, 2, 3)); - - position = "hello".among!("hello", "world"); - assert(position); - assert(position == 1); - - static assert(!__traits(compiles, "a".among!("a", 42))); - static assert(!__traits(compiles, (Object.init).among!(42, "a"))); -} - -// Used in castSwitch to find the first choice that overshadows the last choice -// in a tuple. -private template indexOfFirstOvershadowingChoiceOnLast(choices...) -{ - alias firstParameterTypes = Parameters!(choices[0]); - alias lastParameterTypes = Parameters!(choices[$ - 1]); - - static if (lastParameterTypes.length == 0) - { - // If the last is null-typed choice, check if the first is null-typed. - enum isOvershadowing = firstParameterTypes.length == 0; - } - else static if (firstParameterTypes.length == 1) - { - // If the both first and last are not null-typed, check for overshadowing. - enum isOvershadowing = - is(firstParameterTypes[0] == Object) // Object overshadows all other classes!(this is needed for interfaces) - || is(lastParameterTypes[0] : firstParameterTypes[0]); - } - else - { - // If the first is null typed and the last is not - the is no overshadowing. - enum isOvershadowing = false; - } - - static if (isOvershadowing) - { - enum indexOfFirstOvershadowingChoiceOnLast = 0; - } - else - { - enum indexOfFirstOvershadowingChoiceOnLast = - 1 + indexOfFirstOvershadowingChoiceOnLast!(choices[1..$]); - } -} - -/** -Executes and returns one of a collection of handlers based on the type of the -switch object. - -The first choice that `switchObject` can be casted to the type -of argument it accepts will be called with `switchObject` casted to that -type, and the value it'll return will be returned by `castSwitch`. - -If a choice's return type is void, the choice must throw an exception, unless -all the choices are void. In that case, castSwitch itself will return void. - -Throws: If none of the choice matches, a `SwitchError` will be thrown. $(D -SwitchError) will also be thrown if not all the choices are void and a void -choice was executed without throwing anything. - -Params: - choices = The `choices` needs to be composed of function or delegate - handlers that accept one argument. There can also be a choice that - accepts zero arguments. That choice will be invoked if the $(D - switchObject) is null. - switchObject = the object against which the tests are being made. - -Returns: - The value of the selected choice. - -Note: `castSwitch` can only be used with object types. -*/ -auto castSwitch(choices...)(Object switchObject) -{ - import core.exception : SwitchError; - import std.format : format; - - // Check to see if all handlers return void. - enum areAllHandlersVoidResult = { - bool result = true; - foreach (index, choice; choices) - { - result &= is(ReturnType!choice : void); // void or noreturn - } - return result; - }(); - - if (switchObject !is null) - { - // Checking for exact matches: - const classInfo = typeid(switchObject); - foreach (index, choice; choices) - { - static assert(isCallable!choice, - "A choice handler must be callable"); - - alias choiceParameterTypes = Parameters!choice; - static assert(choiceParameterTypes.length <= 1, - "A choice handler can not have more than one argument."); - - static if (choiceParameterTypes.length == 1) - { - alias CastClass = choiceParameterTypes[0]; - static assert(is(CastClass == class) || is(CastClass == interface), - "A choice handler can have only class or interface typed argument."); - - // Check for overshadowing: - immutable indexOfOvershadowingChoice = - indexOfFirstOvershadowingChoiceOnLast!(choices[0 .. index + 1]); - static assert(indexOfOvershadowingChoice == index, - "choice number %d(type %s) is overshadowed by choice number %d(type %s)".format( - index + 1, CastClass.stringof, indexOfOvershadowingChoice + 1, - Parameters!(choices[indexOfOvershadowingChoice])[0].stringof)); - - if (classInfo == typeid(CastClass)) - { - static if (is(ReturnType!(choice) == void)) - { - choice(cast(CastClass) switchObject); - static if (areAllHandlersVoidResult) - { - return; - } - else - { - throw new SwitchError("Handlers that return void should throw"); - } - } - else - { - return choice(cast(CastClass) switchObject); - } - } - } - } - - // Checking for derived matches: - foreach (choice; choices) - { - alias choiceParameterTypes = Parameters!choice; - static if (choiceParameterTypes.length == 1) - { - if (auto castedObject = cast(choiceParameterTypes[0]) switchObject) - { - static if (is(ReturnType!(choice) == void)) - { - choice(castedObject); - static if (areAllHandlersVoidResult) - { - return; - } - else - { - throw new SwitchError("Handlers that return void should throw"); - } - } - else - { - return choice(castedObject); - } - } - } - } - } - else // If switchObject is null: - { - // Checking for null matches: - foreach (index, choice; choices) - { - static if (Parameters!(choice).length == 0) - { - immutable indexOfOvershadowingChoice = - indexOfFirstOvershadowingChoiceOnLast!(choices[0 .. index + 1]); - - // Check for overshadowing: - static assert(indexOfOvershadowingChoice == index, - "choice number %d(null reference) is overshadowed by choice number %d(null reference)".format( - index + 1, indexOfOvershadowingChoice + 1)); - - if (switchObject is null) - { - static if (is(ReturnType!(choice) == void)) - { - choice(); - static if (areAllHandlersVoidResult) - { - return; - } - else - { - throw new SwitchError("Handlers that return void should throw"); - } - } - else - { - return choice(); - } - } - } - } - } - - // In case nothing matched: - throw new SwitchError("Input not matched by any choice"); -} - -/// -@system unittest -{ - import std.algorithm.iteration : map; - import std.format : format; - - class A - { - int a; - this(int a) {this.a = a;} - @property int i() { return a; } - } - interface I { } - class B : I { } - - Object[] arr = [new A(1), new B(), null]; - - auto results = arr.map!(castSwitch!( - (A a) => "A with a value of %d".format(a.a), - (I i) => "derived from I", - () => "null reference", - ))(); - - // A is handled directly: - assert(results[0] == "A with a value of 1"); - // B has no handler - it is handled by the handler of I: - assert(results[1] == "derived from I"); - // null is handled by the null handler: - assert(results[2] == "null reference"); -} - -/// Using with void handlers: -@system unittest -{ - import std.exception : assertThrown; - - class A { } - class B { } - // Void handlers are allowed if they throw: - assertThrown!Exception( - new B().castSwitch!( - (A a) => 1, - (B d) { throw new Exception("B is not allowed!"); } - )() - ); - - // Void handlers are also allowed if all the handlers are void: - new A().castSwitch!( - (A a) { }, - (B b) { assert(false); }, - )(); -} - -@system unittest -{ - import core.exception : SwitchError; - import std.exception : assertThrown; - - interface I { } - class A : I { } - class B { } - - // Nothing matches: - assertThrown!SwitchError((new A()).castSwitch!( - (B b) => 1, - () => 2, - )()); - - // Choices with multiple arguments are not allowed: - static assert(!__traits(compiles, - (new A()).castSwitch!( - (A a, B b) => 0, - )())); - - // Only callable handlers allowed: - static assert(!__traits(compiles, - (new A()).castSwitch!( - 1234, - )())); - - // Only object arguments allowed: - static assert(!__traits(compiles, - (new A()).castSwitch!( - (int x) => 0, - )())); - - // Object overshadows regular classes: - static assert(!__traits(compiles, - (new A()).castSwitch!( - (Object o) => 0, - (A a) => 1, - )())); - - // Object overshadows interfaces: - static assert(!__traits(compiles, - (new A()).castSwitch!( - (Object o) => 0, - (I i) => 1, - )())); - - // No multiple null handlers allowed: - static assert(!__traits(compiles, - (new A()).castSwitch!( - () => 0, - () => 1, - )())); - - // No non-throwing void handlers allowed(when there are non-void handlers): - assertThrown!SwitchError((new A()).castSwitch!( - (A a) {}, - (B b) => 2, - )()); - - // All-void handlers work for the null case: - null.castSwitch!( - (Object o) { assert(false); }, - () { }, - )(); - - // Throwing void handlers work for the null case: - assertThrown!Exception(null.castSwitch!( - (Object o) => 1, - () { throw new Exception("null"); }, - )()); -} - -@system unittest -{ - interface I { } - class B : I { } - class C : I { } - - assert((new B()).castSwitch!( - (B b) => "class B", - (I i) => "derived from I", - ) == "class B"); - - assert((new C()).castSwitch!( - (B b) => "class B", - (I i) => "derived from I", - ) == "derived from I"); -} - -// https://issues.dlang.org/show_bug.cgi?id=22384 -@system unittest -{ - // Use explicit methods to enforce return types - static void objectSkip(Object) {} - static void defaultSkip() {} - - static noreturn objectError(Object) { assert(false); } - static noreturn defaultError() { assert(false); } - - { - alias test = castSwitch!(objectSkip, defaultError); - static assert(is(ReturnType!test == void)); - }{ - alias test = castSwitch!(objectError, defaultSkip); - static assert(is(ReturnType!test == void)); - }{ - alias test = castSwitch!(objectError, defaultError); - static assert(is(ReturnType!test == noreturn)); - } - - // Also works with non-void handlers - static int objectValue(Object) { return 1;} - static int defaultValue() { return 2; } - - { - alias test = castSwitch!(objectValue, defaultError); - static assert(is(ReturnType!test == int)); - }{ - alias test = castSwitch!(objectError, defaultValue); - static assert(is(ReturnType!test == int)); - } - - // No confusion w.r.t. void callbacks - alias FP = void function(); - static FP objectFunc(Object) { return &defaultSkip; } - static FP defaultFunc() { return &defaultSkip; } - - { - alias test = castSwitch!(objectFunc, defaultError); - static assert(is(ReturnType!test == FP)); - }{ - alias test = castSwitch!(objectError, defaultFunc); - static assert(is(ReturnType!test == FP)); - } -} - -/** Clamps `val` into the given bounds. Result has the same type as `val`. - -Params: - val = The value to _clamp. - lower = The _lower bound of the _clamp. - upper = The _upper bound of the _clamp. - -Returns: - `lower` if `val` is less than `lower`, `upper` if `val` is greater than - `upper`, and `val` in all other cases. Comparisons are made - correctly (using $(REF lessThan, std,functional) and the return value - is converted to the return type using the standard integer coversion rules - $(REF greaterThan, std,functional)) even if the signedness of `T1`, `T2`, - and `T3` are different. -*/ -T1 clamp(T1, T2, T3)(T1 val, T2 lower, T3 upper) -{ - static assert(is(T2 : T1), "T2 of type '", T2.stringof - , "' must be implicitly convertible to type of T1 '" - , T1.stringof, "'"); - static assert(is(T3 : T1), "T3 of type '", T3.stringof - , "' must be implicitly convertible to type of T1 '" - , T1.stringof, "'"); - - assert(!lower.greaterThan(upper), "Lower can't be greater than upper."); - - // `if (is(typeof(val.lessThan(lower) ? lower : val.greaterThan(upper) ? upper : val) : T1)) - // because of https://issues.dlang.org/show_bug.cgi?id=16235. - // Once that is fixed, we can simply use the ternary in both the template constraint - // and the template body - if (val.lessThan(lower)) - return lower; - else if (val.greaterThan(upper)) - return upper; - return val; -} - -/// -@safe @nogc @betterC unittest -{ - assert(clamp(2, 1, 3) == 2); - assert(clamp(0, 1, 3) == 1); - assert(clamp(4, 1, 3) == 3); - - assert(clamp(1, 1, 1) == 1); - - assert(clamp(5, -1, 2u) == 2); - - auto x = clamp(42, uint.max, uint.max); - static assert(is(typeof(x) == int)); - assert(x == -1); -} - -@safe unittest -{ - int a = 1; - short b = 6; - double c = 2; - static assert(is(typeof(clamp(c,a,b)) == double)); - assert(clamp(c, a, b) == c); - assert(clamp(a-c, a, b) == a); - assert(clamp(b+c, a, b) == b); - // mixed sign - a = -5; - uint f = 5; - static assert(is(typeof(clamp(f, a, b)) == uint)); - assert(clamp(f, a, b) == f); - // similar type deduction for (u)long - static assert(is(typeof(clamp(-1L, -2L, 2UL)) == long)); - - // user-defined types - import std.datetime : Date; - assert(clamp(Date(1982, 1, 4), Date(1012, 12, 21), Date(2012, 12, 21)) == Date(1982, 1, 4)); - assert(clamp(Date(1982, 1, 4), Date.min, Date.max) == Date(1982, 1, 4)); - // UFCS style - assert(Date(1982, 1, 4).clamp(Date.min, Date.max) == Date(1982, 1, 4)); - - // Stability - struct A { - int x, y; - int opCmp(ref const A rhs) const { return (x > rhs.x) - (x < rhs.x); } - } - A x, lo, hi; - x.y = 42; - assert(x.clamp(lo, hi).y == 42); -} - -// https://issues.dlang.org/show_bug.cgi?id=23268 -@safe pure nothrow @nogc unittest -{ - static assert(__traits(compiles, clamp(short.init, short.init, cast(const) short.init))); -} - -// cmp -/********************************** -Performs a lexicographical comparison on two -$(REF_ALTTEXT input ranges, isInputRange, std,range,primitives). -Iterating `r1` and `r2` in lockstep, `cmp` compares each element -`e1` of `r1` with the corresponding element `e2` in `r2`. If one -of the ranges has been finished, `cmp` returns a negative value -if `r1` has fewer elements than `r2`, a positive value if `r1` -has more elements than `r2`, and `0` if the ranges have the same -number of elements. - -If the ranges are strings, `cmp` performs UTF decoding -appropriately and compares the ranges one code point at a time. - -A custom predicate may be specified, in which case `cmp` performs -a three-way lexicographical comparison using `pred`. Otherwise -the elements are compared using `opCmp`. - -Params: - pred = Predicate used for comparison. Without a predicate - specified the ordering implied by `opCmp` is used. - r1 = The first range. - r2 = The second range. - -Returns: - `0` if the ranges compare equal. A negative value if `r1` is a prefix of `r2` or - the first differing element of `r1` is less than the corresponding element of `r2` - according to `pred`. A positive value if `r2` is a prefix of `r1` or the first - differing element of `r2` is less than the corresponding element of `r1` - according to `pred`. - -Note: - An earlier version of the documentation incorrectly stated that `-1` is the - only negative value returned and `1` is the only positive value returned. - Whether that is true depends on the types being compared. -*/ -auto cmp(R1, R2)(R1 r1, R2 r2) -if (isInputRange!R1 && isInputRange!R2) -{ - alias E1 = ElementEncodingType!R1; - alias E2 = ElementEncodingType!R2; - - static if (isDynamicArray!R1 && isDynamicArray!R2 - && __traits(isUnsigned, E1) && __traits(isUnsigned, E2) - && E1.sizeof == 1 && E2.sizeof == 1 - // Both or neither must auto-decode. - && (is(immutable E1 == immutable char) == is(immutable E2 == immutable char))) - { - // dstrcmp algorithm is correct for both ubyte[] and for char[]. - import core.internal.string : dstrcmp; - return dstrcmp(cast(const char[]) r1, cast(const char[]) r2); - } - else static if (!(isSomeString!R1 && isSomeString!R2)) - { - for (;; r1.popFront(), r2.popFront()) - { - static if (is(typeof(r1.front.opCmp(r2.front)) R)) - alias Result = R; - else - alias Result = int; - if (r2.empty) return Result(!r1.empty); - if (r1.empty) return Result(-1); - static if (is(typeof(r1.front.opCmp(r2.front)))) - { - auto c = r1.front.opCmp(r2.front); - if (c != 0) return c; - } - else - { - auto a = r1.front, b = r2.front; - if (auto result = (b < a) - (a < b)) return result; - } - } - } - else - { - static if (typeof(r1[0]).sizeof == typeof(r2[0]).sizeof) - { - return () @trusted - { - auto p1 = r1.ptr, p2 = r2.ptr, - pEnd = p1 + min(r1.length, r2.length); - for (; p1 != pEnd; ++p1, ++p2) - { - if (*p1 != *p2) return cast(int) *p1 - cast(int) *p2; - } - static if (typeof(r1[0]).sizeof >= 2 && size_t.sizeof <= uint.sizeof) - return cast(int) r1.length - cast(int) r2.length; - else - return int(r1.length > r2.length) - int(r1.length < r2.length); - }(); - } - else - { - import std.utf : decode; - - for (size_t i1, i2;;) - { - if (i1 == r1.length) return -int(i2 < r2.length); - if (i2 == r2.length) return int(1); - immutable c1 = decode(r1, i1), - c2 = decode(r2, i2); - if (c1 != c2) return cast(int) c1 - cast(int) c2; - } - } - } -} - -/// ditto -int cmp(alias pred, R1, R2)(R1 r1, R2 r2) -if (isInputRange!R1 && isInputRange!R2) -{ - static if (!(isSomeString!R1 && isSomeString!R2)) - { - for (;; r1.popFront(), r2.popFront()) - { - if (r2.empty) return !r1.empty; - if (r1.empty) return -1; - auto a = r1.front, b = r2.front; - if (binaryFun!pred(a, b)) return -1; - if (binaryFun!pred(b, a)) return 1; - } - } - else - { - import std.utf : decode; - - for (size_t i1, i2;;) - { - if (i1 == r1.length) return -int(i2 < r2.length); - if (i2 == r2.length) return 1; - immutable c1 = decode(r1, i1), - c2 = decode(r2, i2); - if (c1 != c2) - { - if (binaryFun!pred(c2, c1)) return 1; - if (binaryFun!pred(c1, c2)) return -1; - } - } - } -} - -/// -pure @safe unittest -{ - int result; - - result = cmp("abc", "abc"); - assert(result == 0); - result = cmp("", ""); - assert(result == 0); - result = cmp("abc", "abcd"); - assert(result < 0); - result = cmp("abcd", "abc"); - assert(result > 0); - result = cmp("abc"d, "abd"); - assert(result < 0); - result = cmp("bbc", "abc"w); - assert(result > 0); - result = cmp("aaa", "aaaa"d); - assert(result < 0); - result = cmp("aaaa", "aaa"d); - assert(result > 0); - result = cmp("aaa", "aaa"d); - assert(result == 0); - result = cmp("aaa"d, "aaa"d); - assert(result == 0); - result = cmp(cast(int[])[], cast(int[])[]); - assert(result == 0); - result = cmp([1, 2, 3], [1, 2, 3]); - assert(result == 0); - result = cmp([1, 3, 2], [1, 2, 3]); - assert(result > 0); - result = cmp([1, 2, 3], [1L, 2, 3, 4]); - assert(result < 0); - result = cmp([1L, 2, 3], [1, 2]); - assert(result > 0); -} - -/// Example predicate that compares individual elements in reverse lexical order -pure @safe unittest -{ - int result; - - result = cmp!"a > b"("abc", "abc"); - assert(result == 0); - result = cmp!"a > b"("", ""); - assert(result == 0); - result = cmp!"a > b"("abc", "abcd"); - assert(result < 0); - result = cmp!"a > b"("abcd", "abc"); - assert(result > 0); - result = cmp!"a > b"("abc"d, "abd"); - assert(result > 0); - result = cmp!"a > b"("bbc", "abc"w); - assert(result < 0); - result = cmp!"a > b"("aaa", "aaaa"d); - assert(result < 0); - result = cmp!"a > b"("aaaa", "aaa"d); - assert(result > 0); - result = cmp!"a > b"("aaa", "aaa"d); - assert(result == 0); - result = cmp("aaa"d, "aaa"d); - assert(result == 0); - result = cmp!"a > b"(cast(int[])[], cast(int[])[]); - assert(result == 0); - result = cmp!"a > b"([1, 2, 3], [1, 2, 3]); - assert(result == 0); - result = cmp!"a > b"([1, 3, 2], [1, 2, 3]); - assert(result < 0); - result = cmp!"a > b"([1, 2, 3], [1L, 2, 3, 4]); - assert(result < 0); - result = cmp!"a > b"([1L, 2, 3], [1, 2]); - assert(result > 0); -} - -// cmp for string with custom predicate fails if distinct chars can compare equal -// https://issues.dlang.org/show_bug.cgi?id=18286 -@nogc nothrow pure @safe unittest -{ - static bool ltCi(dchar a, dchar b)// less than, case insensitive - { - import std.ascii : toUpper; - return toUpper(a) < toUpper(b); - } - static assert(cmp!ltCi("apple2", "APPLE1") > 0); - static assert(cmp!ltCi("apple1", "APPLE2") < 0); - static assert(cmp!ltCi("apple", "APPLE1") < 0); - static assert(cmp!ltCi("APPLE", "apple1") < 0); - static assert(cmp!ltCi("apple", "APPLE") == 0); -} - -// for non-string ranges check that opCmp is evaluated only once per pair. -// https://issues.dlang.org/show_bug.cgi?id=18280 -@nogc nothrow @safe unittest -{ - static int ctr = 0; - struct S - { - int opCmp(ref const S rhs) const - { - ++ctr; - return 0; - } - bool opEquals(T)(T o) const { return false; } - size_t toHash() const { return 0; } - } - immutable S[4] a; - immutable S[4] b; - immutable result = cmp(a[], b[]); - assert(result == 0, "neither should compare greater than the other!"); - assert(ctr == a.length, "opCmp should be called exactly once per pair of items!"); -} - -nothrow pure @safe @nogc unittest -{ - import std.array : staticArray; - // Test cmp when opCmp returns float. - struct F - { - float value; - float opCmp(const ref F rhs) const - { - return value - rhs.value; - } - bool opEquals(T)(T o) const { return false; } - size_t toHash() const { return 0; } - } - auto result = cmp([F(1), F(2), F(3)].staticArray[], [F(1), F(2), F(3)].staticArray[]); - assert(result == 0); - assert(is(typeof(result) == float)); - result = cmp([F(1), F(3), F(2)].staticArray[], [F(1), F(2), F(3)].staticArray[]); - assert(result > 0); - result = cmp([F(1), F(2), F(3)].staticArray[], [F(1), F(2), F(3), F(4)].staticArray[]); - assert(result < 0); - result = cmp([F(1), F(2), F(3)].staticArray[], [F(1), F(2)].staticArray[]); - assert(result > 0); -} - -nothrow pure @safe unittest -{ - // Parallelism (was broken by inferred return type "immutable int") - import std.parallelism : task; - auto t = task!cmp("foo", "bar"); -} - -// equal -/** -Compares two or more ranges for equality, as defined by predicate `pred` -(which is `==` by default). -*/ -template equal(alias pred = "a == b") -{ - /++ - Compares two or more ranges for equality. The ranges may have - different element types, as long as all are comparable by means of - the `pred`. - Performs $(BIGOH min(rs[0].length, rs[1].length, ...)) evaluations of `pred`. However, if - `equal` is invoked with the default predicate, the implementation may take the liberty - to use faster implementations that have the theoretical worst-case - $(BIGOH max(rs[0].length, rs[1].length, ...)). - - At least one of the ranges must be finite. If one range involved is infinite, the result is - (statically known to be) `false`. - - If the ranges have different kinds of UTF code unit (`char`, `wchar`, or - `dchar`), then they are compared using UTF decoding to avoid - accidentally integer-promoting units. - - Params: - rs = The ranges to be compared. - - Returns: - `true` if and only if all ranges compare _equal element - for element, according to binary predicate `pred`. - +/ - bool equal(Ranges...)(Ranges rs) - if (rs.length > 1 - && allSatisfy!(isInputRange, Ranges) - && !allSatisfy!(isInfinite, Ranges) - && is(typeof(binaryFun!pred(rs[0].front, rs[1].front))) - && (rs.length == 2 || is(typeof(equal!pred(rs[1 .. $])) == bool)) - ) - { - alias ElementEncodingTypes = staticMap!(ElementEncodingType, Ranges); - enum differentSize(T) = T.sizeof != ElementEncodingTypes[0].sizeof; - enum useCodePoint = allSatisfy!(isSomeChar, ElementEncodingTypes) && - anySatisfy!(differentSize, ElementEncodingTypes); - enum bool comparableWithEq(alias r) = is(typeof(rs[0] == r)); - - static if (anySatisfy!(isInfinite, Ranges)) - { - return false; - } - else static if (useCodePoint) - { - import std.utf : byDchar; - static bool allByDchar(size_t done, Ranges...)(auto ref Ranges rs) - { - static if (done == rs.length) - return equalLoop(rs); - else - return allByDchar!(done + 1)(rs[0 .. done], rs[done].byDchar, rs[done + 1 .. $]); - } - return allByDchar!0(rs); - } - else static if (is(typeof(pred) == string) && pred == "a == b" && - allSatisfy!(isArray, Ranges) && allSatisfy!(comparableWithEq, rs)) - { - static foreach (r; rs[1 .. $]) - if (rs[0] != r) - return false; - return true; - } - // if one of the arguments is a string and the other isn't, then auto-decoding - // can be avoided if they have the same ElementEncodingType - // TODO: generalize this - else static if (rs.length == 2 && is(typeof(pred) == string) && pred == "a == b" && - isAutodecodableString!(Ranges[0]) != isAutodecodableString!(Ranges[1]) && - is(immutable ElementEncodingType!(Ranges[0]) == immutable ElementEncodingType!(Ranges[1]))) - { - import std.utf : byCodeUnit; - static if (isAutodecodableString!(Ranges[0])) - return equal(rs[0].byCodeUnit, rs[1]); - else - return equal(rs[1].byCodeUnit, rs[0]); - } - else - { - static foreach (i, R; Ranges) - { - static if (hasLength!R) - { - static if (!is(typeof(firstLength))) - { - // Found the first range that has length - auto firstLength = rs[i].length; - } - else - { - // Compare the length of the current range against the first with length - if (firstLength != rs[i].length) - return false; - } - } - } - return equalLoop(rs); - } - } - - private bool equalLoop(Rs...)(ref Rs rs) - { - for (; !rs[0].empty; rs[0].popFront) - static foreach (r; rs[1 .. $]) - if (r.empty || !binaryFun!pred(rs[0].front, r.front)) - return false; - else - r.popFront; - static foreach (r; rs[1 .. $]) - if (!r.empty) - return false; - return true; - } -} - -/// -@safe @nogc unittest -{ - import std.algorithm.comparison : equal; - import std.math.operations : isClose; - - int[4] a = [ 1, 2, 4, 3 ]; - assert(!equal(a[], a[1..$])); - assert(equal(a[], a[])); - assert(equal!((a, b) => a == b)(a[], a[])); - - // different types - double[4] b = [ 1.0, 2, 4, 3]; - assert(!equal(a[], b[1..$])); - assert(equal(a[], b[])); - - // predicated: ensure that two vectors are approximately equal - double[4] c = [ 1.0000000005, 2, 4, 3]; - assert(equal!isClose(b[], c[])); -} - -@safe @nogc unittest -{ - import std.algorithm.comparison : equal; - import std.math.operations : isClose; - - auto s1 = "abc", s2 = "abc"w; - assert(equal(s1, s2, s2)); - assert(equal(s1, s2, s2, s1)); - assert(!equal(s1, s2, s2[1 .. $])); - - int[4] a = [ 1, 2, 4, 3 ]; - assert(!equal(a[], a[1..$], a[])); - assert(equal(a[], a[], a[])); - assert(equal!((a, b) => a == b)(a[], a[], a[])); - - // different types - double[4] b = [ 1.0, 2, 4, 3]; - assert(!equal(a[], b[1..$], b[])); - assert(equal(a[], b[], a[], b[])); - - // predicated: ensure that two vectors are approximately equal - double[4] c = [ 1.0000000005, 2, 4, 3]; - assert(equal!isClose(b[], c[], b[])); -} - -/++ -Tip: `equal` can itself be used as a predicate to other functions. -This can be very useful when the element type of a range is itself a -range. In particular, `equal` can be its own predicate, allowing -range of range (of range...) comparisons. - +/ -@safe unittest -{ - import std.algorithm.comparison : equal; - import std.range : iota, chunks; - assert(equal!(equal!equal)( - [[[0, 1], [2, 3]], [[4, 5], [6, 7]]], - iota(0, 8).chunks(2).chunks(2) - )); -} - -@safe unittest -{ - import std.algorithm.iteration : map; - import std.internal.test.dummyrange : ReferenceForwardRange, - ReferenceInputRange, ReferenceInfiniteForwardRange; - import std.math.operations : isClose; - - // various strings - assert(equal("Ìøü", "Ìøü")); //UTF8 vs UTF8 - assert(!equal("???", "Ìøü")); //UTF8 vs UTF8 - assert(equal("Ìøü"w, "Ìøü"d)); //UTF16 vs UTF32 - assert(!equal("???"w, "Ìøü"d));//UTF16 vs UTF32 - assert(equal("Ìøü"d, "Ìøü"d)); //UTF32 vs UTF32 - assert(!equal("???"d, "Ìøü"d));//UTF32 vs UTF32 - assert(!equal("hello", "world")); - - // same strings, but "explicit non default" comparison (to test the non optimized array comparison) - assert( equal!("a == b")("Ìøü", "Ìøü")); //UTF8 vs UTF8 - assert(!equal!("a == b")("???", "Ìøü")); //UTF8 vs UTF8 - assert( equal!("a == b")("Ìøü"w, "Ìøü"d)); //UTF16 vs UTF32 - assert(!equal!("a == b")("???"w, "Ìøü"d));//UTF16 vs UTF32 - assert( equal!("a == b")("Ìøü"d, "Ìøü"d)); //UTF32 vs UTF32 - assert(!equal!("a == b")("???"d, "Ìøü"d));//UTF32 vs UTF32 - assert(!equal!("a == b")("hello", "world")); - - //Array of string - assert(equal(["hello", "world"], ["hello", "world"])); - assert(!equal(["hello", "world"], ["hello"])); - assert(!equal(["hello", "world"], ["hello", "Bob!"])); - - //Should not compile, because "string == dstring" is illegal - static assert(!is(typeof(equal(["hello", "world"], ["hello"d, "world"d])))); - //However, arrays of non-matching string can be compared using equal!equal. Neat-o! - equal!equal(["hello", "world"], ["hello"d, "world"d]); - - //Tests, with more fancy map ranges - int[] a = [ 1, 2, 4, 3 ]; - assert(equal([2, 4, 8, 6], map!"a*2"(a))); - double[] b = [ 1.0, 2, 4, 3]; - double[] c = [ 1.0000000005, 2, 4, 3]; - assert(equal!isClose(map!"a*2"(b), map!"a*2"(c))); - assert(!equal([2, 4, 1, 3], map!"a*2"(a))); - assert(!equal([2, 4, 1], map!"a*2"(a))); - assert(!equal!isClose(map!"a*3"(b), map!"a*2"(c))); - - //Tests with some fancy reference ranges. - ReferenceInputRange!int cir = new ReferenceInputRange!int([1, 2, 4, 3]); - ReferenceForwardRange!int cfr = new ReferenceForwardRange!int([1, 2, 4, 3]); - assert(equal(cir, a)); - cir = new ReferenceInputRange!int([1, 2, 4, 3]); - assert(equal(cir, cfr.save)); - assert(equal(cfr.save, cfr.save)); - cir = new ReferenceInputRange!int([1, 2, 8, 1]); - assert(!equal(cir, cfr)); - - //Test with an infinite range - auto ifr = new ReferenceInfiniteForwardRange!int; - assert(!equal(a, ifr)); - assert(!equal(ifr, a)); - //Test InputRange without length - assert(!equal(ifr, cir)); - assert(!equal(cir, ifr)); -} - -@safe @nogc pure unittest -{ - import std.utf : byChar, byDchar, byWchar; - - assert(equal("Ìøü".byChar, "Ìøü")); - assert(equal("Ìøü".byChar, "Ìøü"w)); - assert(equal("Ìøü".byChar, "Ìøü"d)); - assert(equal("Ìøü", "Ìøü".byChar)); - assert(equal("Ìøü"w, "Ìøü".byChar)); - assert(equal("Ìøü"d, "Ìøü".byChar)); - - assert(equal("Ìøü".byWchar, "Ìøü")); - assert(equal("Ìøü".byWchar, "Ìøü"w)); - assert(equal("Ìøü".byWchar, "Ìøü"d)); - assert(equal("Ìøü", "Ìøü".byWchar)); - assert(equal("Ìøü"w, "Ìøü".byWchar)); - assert(equal("Ìøü"d, "Ìøü".byWchar)); - - assert(equal("Ìøü".byDchar, "Ìøü")); - assert(equal("Ìøü".byDchar, "Ìøü"w)); - assert(equal("Ìøü".byDchar, "Ìøü"d)); - assert(equal("Ìøü", "Ìøü".byDchar)); - assert(equal("Ìøü"w, "Ìøü".byDchar)); - assert(equal("Ìøü"d, "Ìøü".byDchar)); -} - -@safe @nogc pure unittest -{ - struct R(bool _empty) { - enum empty = _empty; - @property char front(){assert(0);} - void popFront(){assert(0);} - } - alias I = R!false; - static assert(!__traits(compiles, I().equal(I()))); - // strings have fixed length so don't need to compare elements - assert(!I().equal("foo")); - assert(!"bar".equal(I())); - - alias E = R!true; - assert(E().equal(E())); - assert(E().equal("")); - assert("".equal(E())); - assert(!E().equal("foo")); - assert(!"bar".equal(E())); -} - -// levenshteinDistance -/** -Encodes $(HTTP realityinteractive.com/rgrzywinski/archives/000249.html, -edit operations) necessary to transform one sequence into -another. Given sequences `s` (source) and `t` (target), a -sequence of `EditOp` encodes the steps that need to be taken to -convert `s` into `t`. For example, if `s = "cat"` and $(D -"cars"), the minimal sequence that transforms `s` into `t` is: -skip two characters, replace 't' with 'r', and insert an 's'. Working -with edit operations is useful in applications such as spell-checkers -(to find the closest word to a given misspelled word), approximate -searches, diff-style programs that compute the difference between -files, efficient encoding of patches, DNA sequence analysis, and -plagiarism detection. -*/ - -enum EditOp : char -{ - /** Current items are equal; no editing is necessary. */ - none = 'n', - /** Substitute current item in target with current item in source. */ - substitute = 's', - /** Insert current item from the source into the target. */ - insert = 'i', - /** Remove current item from the target. */ - remove = 'r' -} - -/// -@safe unittest -{ - with(EditOp) - { - assert(levenshteinDistanceAndPath("foo", "foobar")[1] == [none, none, none, insert, insert, insert]); - assert(levenshteinDistanceAndPath("banana", "fazan")[1] == [substitute, none, substitute, none, none, remove]); - } -} - -private struct Levenshtein(Range, alias equals, CostType = size_t) -{ - EditOp[] path() - { - import std.algorithm.mutation : reverse; - - EditOp[] result; - size_t i = rows - 1, j = cols - 1; - // restore the path - while (i || j) - { - auto cIns = j == 0 ? CostType.max : matrix(i,j - 1); - auto cDel = i == 0 ? CostType.max : matrix(i - 1,j); - auto cSub = i == 0 || j == 0 - ? CostType.max - : matrix(i - 1,j - 1); - switch (min_index(cSub, cIns, cDel)) - { - case 0: - result ~= matrix(i - 1,j - 1) == matrix(i,j) - ? EditOp.none - : EditOp.substitute; - --i; - --j; - break; - case 1: - result ~= EditOp.insert; - --j; - break; - default: - result ~= EditOp.remove; - --i; - break; - } - } - reverse(result); - return result; - } - - ~this() { - FreeMatrix(); - } - -private: - CostType _deletionIncrement = 1, - _insertionIncrement = 1, - _substitutionIncrement = 1; - CostType[] _matrix; - size_t rows, cols; - - // Treat _matrix as a rectangular array - ref CostType matrix(size_t row, size_t col) { return _matrix[row * cols + col]; } - - void AllocMatrix(size_t r, size_t c) @trusted { - import core.checkedint : mulu; - bool overflow; - const rc = mulu(r, c, overflow); - assert(!overflow, "Overflow during multiplication to determine number " - ~ " of matrix elements"); - rows = r; - cols = c; - if (_matrix.length < rc) - { - import core.exception : onOutOfMemoryError; - import core.stdc.stdlib : realloc; - const nbytes = mulu(rc, _matrix[0].sizeof, overflow); - assert(!overflow, "Overflow during multiplication to determine " - ~ " number of bytes of matrix"); - auto m = cast(CostType *) realloc(_matrix.ptr, nbytes); - if (!m) - onOutOfMemoryError(); - _matrix = m[0 .. r * c]; - InitMatrix(); - } - } - - void FreeMatrix() @trusted { - import core.stdc.stdlib : free; - - free(_matrix.ptr); - _matrix = null; - } - - void InitMatrix() { - foreach (r; 0 .. rows) - matrix(r,0) = r * _deletionIncrement; - foreach (c; 0 .. cols) - matrix(0,c) = c * _insertionIncrement; - } - - static uint min_index(CostType i0, CostType i1, CostType i2) - { - if (i0 <= i1) - { - return i0 <= i2 ? 0 : 2; - } - else - { - return i1 <= i2 ? 1 : 2; - } - } - - CostType distanceWithPath(Range s, Range t) - { - auto slen = walkLength(s.save), tlen = walkLength(t.save); - AllocMatrix(slen + 1, tlen + 1); - foreach (i; 1 .. rows) - { - auto sfront = s.front; - auto tt = t.save; - foreach (j; 1 .. cols) - { - auto cSub = matrix(i - 1,j - 1) - + (equals(sfront, tt.front) ? 0 : _substitutionIncrement); - tt.popFront(); - auto cIns = matrix(i,j - 1) + _insertionIncrement; - auto cDel = matrix(i - 1,j) + _deletionIncrement; - switch (min_index(cSub, cIns, cDel)) - { - case 0: - matrix(i,j) = cSub; - break; - case 1: - matrix(i,j) = cIns; - break; - default: - matrix(i,j) = cDel; - break; - } - } - s.popFront(); - } - return matrix(slen,tlen); - } - - CostType distanceLowMem(Range s, Range t, CostType slen, CostType tlen) - { - CostType lastdiag, olddiag; - AllocMatrix(slen + 1, 1); - foreach (y; 1 .. slen + 1) - { - _matrix[y] = y; - } - foreach (x; 1 .. tlen + 1) - { - auto tfront = t.front; - auto ss = s.save; - _matrix[0] = x; - lastdiag = x - 1; - foreach (y; 1 .. rows) - { - olddiag = _matrix[y]; - auto cSub = lastdiag + (equals(ss.front, tfront) ? 0 : _substitutionIncrement); - ss.popFront(); - auto cIns = _matrix[y - 1] + _insertionIncrement; - auto cDel = _matrix[y] + _deletionIncrement; - switch (min_index(cSub, cIns, cDel)) - { - case 0: - _matrix[y] = cSub; - break; - case 1: - _matrix[y] = cIns; - break; - default: - _matrix[y] = cDel; - break; - } - lastdiag = olddiag; - } - t.popFront(); - } - return _matrix[slen]; - } -} - -/** -Returns the $(HTTP wikipedia.org/wiki/Levenshtein_distance, Levenshtein -distance) between `s` and `t`. The Levenshtein distance computes -the minimal amount of edit operations necessary to transform `s` -into `t`. Performs $(BIGOH s.length * t.length) evaluations of $(D -equals) and occupies $(BIGOH min(s.length, t.length)) storage. - -Params: - equals = The binary predicate to compare the elements of the two ranges. - s = The original range. - t = The transformation target - -Returns: - The minimal number of edits to transform s into t. - -Does not allocate GC memory. -*/ -size_t levenshteinDistance(alias equals = (a,b) => a == b, Range1, Range2) - (Range1 s, Range2 t) -if (isForwardRange!(Range1) && isForwardRange!(Range2)) -{ - alias eq = binaryFun!(equals); - - for (;;) - { - if (s.empty) return t.walkLength; - if (t.empty) return s.walkLength; - if (eq(s.front, t.front)) - { - s.popFront(); - t.popFront(); - continue; - } - static if (isBidirectionalRange!(Range1) && isBidirectionalRange!(Range2)) - { - if (eq(s.back, t.back)) - { - s.popBack(); - t.popBack(); - continue; - } - } - break; - } - - auto slen = walkLength(s.save); - auto tlen = walkLength(t.save); - - if (slen == 1 && tlen == 1) - { - return eq(s.front, t.front) ? 0 : 1; - } - - if (slen < tlen) - { - Levenshtein!(Range1, eq, size_t) lev; - return lev.distanceLowMem(s, t, slen, tlen); - } - else - { - Levenshtein!(Range2, eq, size_t) lev; - return lev.distanceLowMem(t, s, tlen, slen); - } -} - -/// -@safe unittest -{ - import std.algorithm.iteration : filter; - import std.uni : toUpper; - - assert(levenshteinDistance("cat", "rat") == 1); - assert(levenshteinDistance("parks", "spark") == 2); - assert(levenshteinDistance("abcde", "abcde") == 0); - assert(levenshteinDistance("abcde", "abCde") == 1); - assert(levenshteinDistance("kitten", "sitting") == 3); - assert(levenshteinDistance!((a, b) => toUpper(a) == toUpper(b)) - ("parks", "SPARK") == 2); - assert(levenshteinDistance("parks".filter!"true", "spark".filter!"true") == 2); - assert(levenshteinDistance("ID", "I♥D") == 1); -} - -@safe @nogc nothrow unittest -{ - assert(levenshteinDistance("cat"d, "rat"d) == 1); -} - -/// ditto -size_t levenshteinDistance(alias equals = (a,b) => a == b, Range1, Range2) - (auto ref Range1 s, auto ref Range2 t) -if (isConvertibleToString!Range1 || isConvertibleToString!Range2) -{ - import std.meta : staticMap; - alias Types = staticMap!(convertToString, Range1, Range2); - return levenshteinDistance!(equals, Types)(s, t); -} - -@safe unittest -{ - static struct S { string s; alias s this; } - assert(levenshteinDistance(S("cat"), S("rat")) == 1); - assert(levenshteinDistance("cat", S("rat")) == 1); - assert(levenshteinDistance(S("cat"), "rat") == 1); -} - -@safe @nogc nothrow unittest -{ - static struct S { dstring s; alias s this; } - assert(levenshteinDistance(S("cat"d), S("rat"d)) == 1); - assert(levenshteinDistance("cat"d, S("rat"d)) == 1); - assert(levenshteinDistance(S("cat"d), "rat"d) == 1); -} - -/** -Returns the Levenshtein distance and the edit path between `s` and -`t`. - -Params: - equals = The binary predicate to compare the elements of the two ranges. - s = The original range. - t = The transformation target - -Returns: - Tuple with the first element being the minimal amount of edits to transform s into t and - the second element being the sequence of edits to effect this transformation. - -Allocates GC memory for the returned EditOp[] array. -*/ -Tuple!(size_t, EditOp[]) -levenshteinDistanceAndPath(alias equals = (a,b) => a == b, Range1, Range2) - (Range1 s, Range2 t) -if (isForwardRange!(Range1) && isForwardRange!(Range2)) -{ - Levenshtein!(Range1, binaryFun!(equals)) lev; - auto d = lev.distanceWithPath(s, t); - return tuple(d, lev.path()); -} - -/// -@safe unittest -{ - string a = "Saturday", b = "Sundays"; - auto p = levenshteinDistanceAndPath(a, b); - assert(p[0] == 4); - assert(equal(p[1], "nrrnsnnni")); -} - -@safe unittest -{ - assert(levenshteinDistance("a", "a") == 0); - assert(levenshteinDistance("a", "b") == 1); - assert(levenshteinDistance("aa", "ab") == 1); - assert(levenshteinDistance("aa", "abc") == 2); - assert(levenshteinDistance("Saturday", "Sunday") == 3); - assert(levenshteinDistance("kitten", "sitting") == 3); -} - -/// ditto -Tuple!(size_t, EditOp[]) -levenshteinDistanceAndPath(alias equals = (a,b) => a == b, Range1, Range2) - (auto ref Range1 s, auto ref Range2 t) -if (isConvertibleToString!Range1 || isConvertibleToString!Range2) -{ - import std.meta : staticMap; - alias Types = staticMap!(convertToString, Range1, Range2); - return levenshteinDistanceAndPath!(equals, Types)(s, t); -} - -@safe unittest -{ - static struct S { string s; alias s this; } - assert(levenshteinDistanceAndPath(S("cat"), S("rat"))[0] == 1); - assert(levenshteinDistanceAndPath("cat", S("rat"))[0] == 1); - assert(levenshteinDistanceAndPath(S("cat"), "rat")[0] == 1); -} - - -// max -/** -Iterates the passed arguments and returns the maximum value. - -Params: - args = The values to select the maximum from. At least two arguments must - be passed, and they must be comparable with `<`. - -Returns: - The maximum of the passed-in values. The type of the returned value is - the type among the passed arguments that is able to store the largest value. - If at least one of the arguments is NaN, the result is an unspecified value. - See $(REF maxElement, std,algorithm,searching) for examples on how to cope - with NaNs. - -See_Also: - $(REF maxElement, std,algorithm,searching) -*/ -auto max(T...)(T args) -if (T.length >= 2 && !is(CommonType!T == void)) -{ - // Get left-hand side of the comparison. - static if (T.length == 2) - alias a = args[0]; - else - auto a = max(args[0 .. ($ + 1) / 2]); - alias T0 = typeof(a); - - // Get right-hand side. - static if (T.length <= 3) - alias b = args[$ - 1]; - else - auto b = max(args[($ + 1) / 2 .. $]); - alias T1 = typeof(b); - - static assert(is(typeof(a < b)), - "Invalid arguments: Cannot compare types " ~ T0.stringof ~ - " and " ~ T1.stringof ~ " for ordering."); - - // Compute the returned type. - static if (is(typeof(mostNegative!T0 < mostNegative!T1))) - // Both are numeric (or character or Boolean), so we choose the one with the highest maximum. - // (We use mostNegative for num/bool/char testing purposes even if it's not used otherwise.) - alias Result = Select!(T1.max > T0.max, T1, T0); - else - // At least one is non-numeric, so just go with the common type. - alias Result = CommonType!(T0, T1); - - // Perform the computation. - import std.functional : lessThan; - immutable chooseB = lessThan!(T0, T1)(a, b); - return cast(Result) (chooseB ? b : a); -} - -/// ditto -T max(T, U)(T a, U b) -if (is(T == U) && is(typeof(a < b))) -{ - /* Handle the common case without all the template expansions - * of the general case - */ - return a < b ? b : a; -} - -/// -@safe @betterC @nogc unittest -{ - int a = 5; - short b = 6; - double c = 2; - auto d = max(a, b); - assert(is(typeof(d) == int)); - assert(d == 6); - auto e = min(a, b, c); - assert(is(typeof(e) == double)); - assert(e == 2); -} - -@safe unittest // not @nogc due to `Date` -{ - int a = 5; - short b = 6; - double c = 2; - auto d = max(a, b); - static assert(is(typeof(d) == int)); - assert(d == 6); - auto e = max(a, b, c); - static assert(is(typeof(e) == double)); - assert(e == 6); - // mixed sign - a = -5; - uint f = 5; - static assert(is(typeof(max(a, f)) == uint)); - assert(max(a, f) == 5); - - //Test user-defined types - import std.datetime : Date; - assert(max(Date(2012, 12, 21), Date(1982, 1, 4)) == Date(2012, 12, 21)); - assert(max(Date(1982, 1, 4), Date(2012, 12, 21)) == Date(2012, 12, 21)); - assert(max(Date(1982, 1, 4), Date.min) == Date(1982, 1, 4)); - assert(max(Date.min, Date(1982, 1, 4)) == Date(1982, 1, 4)); - assert(max(Date(1982, 1, 4), Date.max) == Date.max); - assert(max(Date.max, Date(1982, 1, 4)) == Date.max); - assert(max(Date.min, Date.max) == Date.max); - assert(max(Date.max, Date.min) == Date.max); -} - -// min -/** -Iterates the passed arguments and returns the minimum value. - -Params: - args = The values to select the minimum from. At least two arguments must - be passed, and they must be comparable with `<`. - -Returns: - The minimum of the passed-in values. The type of the returned value is - the type among the passed arguments that is able to store the smallest value. - If at least one of the arguments is NaN, the result is an unspecified value. - See $(REF minElement, std,algorithm,searching) for examples on how to cope - with NaNs. - -See_Also: - $(REF minElement, std,algorithm,searching) -*/ -auto min(T...)(T args) -if (T.length >= 2 && !is(CommonType!T == void)) -{ - // Get the left-hand side of the comparison. - static if (T.length <= 2) - alias a = args[0]; - else - auto a = min(args[0 .. ($ + 1) / 2]); - alias T0 = typeof(a); - - // Get the right-hand side. - static if (T.length <= 3) - alias b = args[$ - 1]; - else - auto b = min(args[($ + 1) / 2 .. $]); - alias T1 = typeof(b); - - static assert(is(typeof(a < b)), - "Invalid arguments: Cannot compare types " ~ T0.stringof ~ - " and " ~ T1.stringof ~ " for ordering."); - - // Compute the returned type. - static if (is(typeof(mostNegative!T0 < mostNegative!T1))) - // Both are numeric (or character or Boolean), so we choose the one with the lowest minimum. - // If they have the same minimum, choose the one with the smallest size. - // If both mostNegative and sizeof are equal, go for stability: pick the type of the first one. - alias Result = Select!(mostNegative!T1 < mostNegative!T0 || - mostNegative!T1 == mostNegative!T0 && T1.sizeof < T0.sizeof, - T1, T0); - else - // At least one is non-numeric, so just go with the common type. - alias Result = CommonType!(T0, T1); - - // Engage! - import std.functional : lessThan; - immutable chooseB = lessThan!(T1, T0)(b, a); - return cast(Result) (chooseB ? b : a); -} - -/// ditto -T min(T, U)(T a, U b) -if (is(T == U) && is(typeof(a < b))) -{ - /* Handle the common case without all the template expansions - * of the general case - */ - return b < a ? b : a; -} - - -/// -@safe @nogc @betterC unittest -{ - int a = 5; - short b = 6; - double c = 2; - auto d = min(a, b); - static assert(is(typeof(d) == int)); - assert(d == 5); - auto e = min(a, b, c); - static assert(is(typeof(e) == double)); - assert(e == 2); - ulong f = 0xffff_ffff_ffff; - const uint g = min(f, 0xffff_0000); - assert(g == 0xffff_0000); - dchar h = 100; - uint i = 101; - static assert(is(typeof(min(h, i)) == dchar)); - static assert(is(typeof(min(i, h)) == uint)); - assert(min(h, i) == 100); -} - -/** -With arguments of mixed signedness, the return type is the one that can -store the lowest values. -*/ -@safe @nogc @betterC unittest -{ - int a = -10; - uint f = 10; - static assert(is(typeof(min(a, f)) == int)); - assert(min(a, f) == -10); -} - -/// User-defined types that support comparison with < are supported. -@safe unittest // not @nogc due to `Date` -{ - import std.datetime; - assert(min(Date(2012, 12, 21), Date(1982, 1, 4)) == Date(1982, 1, 4)); - assert(min(Date(1982, 1, 4), Date(2012, 12, 21)) == Date(1982, 1, 4)); - assert(min(Date(1982, 1, 4), Date.min) == Date.min); - assert(min(Date.min, Date(1982, 1, 4)) == Date.min); - assert(min(Date(1982, 1, 4), Date.max) == Date(1982, 1, 4)); - assert(min(Date.max, Date(1982, 1, 4)) == Date(1982, 1, 4)); - assert(min(Date.min, Date.max) == Date.min); - assert(min(Date.max, Date.min) == Date.min); -} - -// min must be stable: when in doubt, return the first argument. -@safe unittest -{ - assert(min(1.0, double.nan) == 1.0); - assert(min(double.nan, 1.0) is double.nan); - static struct A { - int x; - string y; - int opCmp(const A a) const { return int(x > a.x) - int(x < a.x); } - } - assert(min(A(1, "first"), A(1, "second")) == A(1, "first")); -} - -// mismatch -/** -Sequentially compares elements in `rs` in lockstep, and -stops at the first mismatch (according to `pred`, by default -equality). Returns a tuple with the reduced ranges that start with the -two mismatched values. Performs $(BIGOH min(r[0].length, r[1].length, ...)) -evaluations of `pred`. -*/ -Tuple!(Ranges) -mismatch(alias pred = (a, b) => a == b, Ranges...)(Ranges rs) -if (rs.length >= 2 && allSatisfy!(isInputRange, Ranges)) -{ - loop: for (; !rs[0].empty; rs[0].popFront) - { - static foreach (r; rs[1 .. $]) - { - if (r.empty || !binaryFun!pred(rs[0].front, r.front)) - break loop; - r.popFront; - } - } - return tuple(rs); -} - -/// -@safe @nogc unittest -{ - int[6] x = [ 1, 5, 2, 7, 4, 3 ]; - double[6] y = [ 1.0, 5, 2, 7.3, 4, 8 ]; - auto m = mismatch(x[], y[]); - assert(m[0] == x[3 .. $]); - assert(m[1] == y[3 .. $]); - - auto m2 = mismatch(x[], y[], x[], y[]); - assert(m2[0] == x[3 .. $]); - assert(m2[1] == y[3 .. $]); - assert(m2[2] == x[3 .. $]); - assert(m2[3] == y[3 .. $]); -} - -@safe @nogc unittest -{ - import std.range : only; - - int[3] a = [ 1, 2, 3 ]; - int[4] b = [ 1, 2, 4, 5 ]; - auto mm = mismatch(a[], b[]); - assert(equal(mm[0], only(3))); - assert(equal(mm[1], only(4, 5))); -} - -/** -Returns one of a collection of expressions based on the value of the switch -expression. - -`choices` needs to be composed of pairs of test expressions and return -expressions. Each test-expression is compared with `switchExpression` using -`pred`(`switchExpression` is the first argument) and if that yields true - -the return expression is returned. - -Both the test and the return expressions are lazily evaluated. - -Params: - -switchExpression = The first argument for the predicate. - -choices = Pairs of test expressions and return expressions. The test -expressions will be the second argument for the predicate, and the return -expression will be returned if the predicate yields true with $(D -switchExpression) and the test expression as arguments. May also have a -default return expression, that needs to be the last expression without a test -expression before it. A return expression may be of void type only if it -always throws. - -Returns: The return expression associated with the first test expression that -made the predicate yield true, or the default return expression if no test -expression matched. - -Throws: If there is no default return expression and the predicate does not -yield true with any test expression - `SwitchError` is thrown. $(D -SwitchError) is also thrown if a void return expression was executed without -throwing anything. -*/ -auto predSwitch(alias pred = "a == b", T, R ...)(T switchExpression, lazy R choices) -{ - import core.exception : SwitchError; - alias predicate = binaryFun!(pred); - - foreach (index, ChoiceType; R) - { - //The even places in `choices` are for the predicate. - static if (index % 2 == 1) - { - if (predicate(switchExpression, choices[index - 1]())) - { - static if (is(typeof(choices[index]()) == void)) - { - choices[index](); - throw new SwitchError("Choices that return void should throw"); - } - else - { - return choices[index](); - } - } - } - } - - //In case nothing matched: - static if (R.length % 2 == 1) //If there is a default return expression: - { - static if (is(typeof(choices[$ - 1]()) == void)) - { - choices[$ - 1](); - throw new SwitchError("Choices that return void should throw"); - } - else - { - return choices[$ - 1](); - } - } - else //If there is no default return expression: - { - throw new SwitchError("Input not matched by any pattern"); - } -} - -/// -@safe unittest -{ - string res = 2.predSwitch!"a < b"( - 1, "less than 1", - 5, "less than 5", - 10, "less than 10", - "greater or equal to 10"); - - assert(res == "less than 5"); - - //The arguments are lazy, which allows us to use predSwitch to create - //recursive functions: - int factorial(int n) - { - return n.predSwitch!"a <= b"( - -1, {throw new Exception("Can not calculate n! for n < 0");}(), - 0, 1, // 0! = 1 - n * factorial(n - 1) // n! = n * (n - 1)! for n >= 0 - ); - } - assert(factorial(3) == 6); - - //Void return expressions are allowed if they always throw: - import std.exception : assertThrown; - assertThrown!Exception(factorial(-9)); -} - -@system unittest -{ - import core.exception : SwitchError; - import std.exception : assertThrown; - - //Nothing matches - with default return expression: - assert(20.predSwitch!"a < b"( - 1, "less than 1", - 5, "less than 5", - 10, "less than 10", - "greater or equal to 10") == "greater or equal to 10"); - - //Nothing matches - without default return expression: - assertThrown!SwitchError(20.predSwitch!"a < b"( - 1, "less than 1", - 5, "less than 5", - 10, "less than 10", - )); - - //Using the default predicate: - assert(2.predSwitch( - 1, "one", - 2, "two", - 3, "three", - ) == "two"); - - //Void return expressions must always throw: - assertThrown!SwitchError(1.predSwitch( - 0, "zero", - 1, {}(), //A void return expression that doesn't throw - 2, "two", - )); -} - -/** -Checks if two or more ranges have the same number of elements. This function is -optimized to always take advantage of the `length` member of either range -if it exists. - -If all ranges have a `length` member or at least one is infinite, -`_isSameLength`'s complexity is $(BIGOH 1). Otherwise, complexity is -$(BIGOH n), where `n` is the smallest of the lengths of ranges with unknown -length. - -Infinite ranges are considered of the same length. An infinite range has never -the same length as a finite range. - -Params: - rs = two or more $(REF_ALTTEXT input ranges, isInputRange, std,range,primitives) - -Returns: - `true` if both ranges have the same length, `false` otherwise. -*/ -bool isSameLength(Ranges...)(Ranges rs) -if (allSatisfy!(isInputRange, Ranges)) -{ - static if (anySatisfy!(isInfinite, Ranges)) - { - return allSatisfy!(isInfinite, Ranges); - } - else static if (anySatisfy!(hasLength, Ranges)) - { - // Compute the O(1) length - auto baselineLength = size_t.max; - static foreach (i, R; Ranges) - { - static if (hasLength!R) - { - if (baselineLength == size_t.max) - baselineLength = rs[i].length; - else if (rs[i].length != baselineLength) - return false; - } - } - // Iterate all ranges without known length - foreach (_; 0 .. baselineLength) - static foreach (i, R; Ranges) - { - static if (!hasLength!R) - { - // All must be non-empty - if (rs[i].empty) - return false; - rs[i].popFront; - } - } - static foreach (i, R; Ranges) - { - static if (!hasLength!R) - { - // All must be now empty - if (!rs[i].empty) - return false; - } - } - return true; - } - else - { - // All have unknown length, iterate in lockstep - for (;;) - static foreach (i, r; rs) - { - if (r.empty) - { - // One is empty, so all must be empty - static if (i != 0) - { - return false; - } - else - { - static foreach (j, r1; rs[1 .. $]) - if (!r1.empty) - return false; - return true; - } - } - r.popFront; - } - } -} - -/// -@safe nothrow pure unittest -{ - assert(isSameLength([1, 2, 3], [4, 5, 6])); - assert(isSameLength([1, 2, 3], [4, 5, 6], [7, 8, 9])); - assert(isSameLength([0.3, 90.4, 23.7, 119.2], [42.6, 23.6, 95.5, 6.3])); - assert(isSameLength("abc", "xyz")); - assert(isSameLength("abc", "xyz", [1, 2, 3])); - - int[] a; - int[] b; - assert(isSameLength(a, b)); - assert(isSameLength(a, b, a, a, b, b, b)); - - assert(!isSameLength([1, 2, 3], [4, 5])); - assert(!isSameLength([1, 2, 3], [4, 5, 6], [7, 8])); - assert(!isSameLength([0.3, 90.4, 23.7], [42.6, 23.6, 95.5, 6.3])); - assert(!isSameLength("abcd", "xyz")); - assert(!isSameLength("abcd", "xyz", "123")); - assert(!isSameLength("abcd", "xyz", "1234")); -} - -// Test CTFE -@safe @nogc pure @betterC unittest -{ - static assert(isSameLength([1, 2, 3], [4, 5, 6])); - static assert(isSameLength([1, 2, 3], [4, 5, 6], [7, 8, 9])); - static assert(!isSameLength([0.3, 90.4, 23.7], [42.6, 23.6, 95.5, 6.3])); - static assert(!isSameLength([1], [0.3, 90.4], [42])); -} - -@safe @nogc pure unittest -{ - import std.range : only; - assert(isSameLength(only(1, 2, 3), only(4, 5, 6))); - assert(isSameLength(only(1, 2, 3), only(4, 5, 6), only(7, 8, 9))); - assert(isSameLength(only(0.3, 90.4, 23.7, 119.2), only(42.6, 23.6, 95.5, 6.3))); - assert(!isSameLength(only(1, 3, 3), only(4, 5))); - assert(!isSameLength(only(1, 3, 3), only(1, 3, 3), only(4, 5))); - assert(!isSameLength(only(1, 3, 3), only(4, 5), only(1, 3, 3))); -} - -@safe nothrow pure unittest -{ - import std.internal.test.dummyrange; - - auto r1 = new ReferenceInputRange!int([1, 2, 3]); - auto r2 = new ReferenceInputRange!int([4, 5, 6]); - assert(isSameLength(r1, r2)); - - auto r3 = new ReferenceInputRange!int([1, 2, 3, 4, 5, 6, 7, 8, 9, 10]); - DummyRange!(ReturnBy.Reference, Length.Yes, RangeType.Input) r4; - assert(isSameLength(r3, r4)); - - DummyRange!(ReturnBy.Reference, Length.Yes, RangeType.Input) r5; - auto r6 = new ReferenceInputRange!int([1, 2, 3, 4, 5, 6, 7, 8, 9, 10]); - assert(isSameLength(r5, r6)); - - auto r7 = new ReferenceInputRange!int([1, 2]); - auto r8 = new ReferenceInputRange!int([4, 5, 6]); - assert(!isSameLength(r7, r8)); - - auto r9 = new ReferenceInputRange!int([1, 2, 3, 4, 5, 6, 7, 8]); - DummyRange!(ReturnBy.Reference, Length.Yes, RangeType.Input) r10; - assert(!isSameLength(r9, r10)); - - DummyRange!(ReturnBy.Reference, Length.Yes, RangeType.Input) r11; - auto r12 = new ReferenceInputRange!int([1, 2, 3, 4, 5, 6, 7, 8]); - assert(!isSameLength(r11, r12)); - - import std.algorithm.iteration : filter; - - assert(isSameLength(filter!"a >= 1"([1, 2, 3]), [4, 5, 6])); - assert(!isSameLength(filter!"a > 1"([1, 2, 3]), [4, 5, 6])); - - assert(isSameLength(filter!"a > 1"([1, 2, 3]), filter!"a > 4"([4, 5, 6]))); - assert(isSameLength(filter!"a > 1"([1, 2, 3]), - filter!"a > 4"([4, 5, 6]), filter!"a >= 5"([4, 5, 6]))); -} - -// Still functional but not documented anymore. -alias AllocateGC = Flag!"allocateGC"; - -/** -Checks if both ranges are permutations of each other. - -This function can allocate if the `Yes.allocateGC` flag is passed. This has -the benefit of have better complexity than the `Yes.allocateGC` option. However, -this option is only available for ranges whose equality can be determined via each -element's `toHash` method. If customized equality is needed, then the `pred` -template parameter can be passed, and the function will automatically switch to -the non-allocating algorithm. See $(REF binaryFun, std,functional) for more details on -how to define `pred`. - -Non-allocating forward range option: $(BIGOH n^2) -Non-allocating forward range option with custom `pred`: $(BIGOH n^2) -Allocating forward range option: amortized $(BIGOH r1.length) + $(BIGOH r2.length) - -Params: - pred = an optional parameter to change how equality is defined - allocateGC = `Yes.allocateGC`/`No.allocateGC` - r1 = A finite $(REF_ALTTEXT forward range, isForwardRange, std,range,primitives) - r2 = A finite $(REF_ALTTEXT forward range, isForwardRange, std,range,primitives) - -Returns: - `true` if all of the elements in `r1` appear the same number of times in `r2`. - Otherwise, returns `false`. -*/ - -bool isPermutation(Flag!"allocateGC" allocateGC, Range1, Range2) -(Range1 r1, Range2 r2) -if (allocateGC == Yes.allocateGC && - isForwardRange!Range1 && - isForwardRange!Range2 && - !isInfinite!Range1 && - !isInfinite!Range2) -{ - alias E1 = Unqual!(ElementType!Range1); - alias E2 = Unqual!(ElementType!Range2); - - if (!isSameLength(r1.save, r2.save)) - { - return false; - } - - // Skip the elements at the beginning where r1.front == r2.front, - // they are in the same order and don't need to be counted. - while (!r1.empty && !r2.empty && r1.front == r2.front) - { - r1.popFront(); - r2.popFront(); - } - - if (r1.empty && r2.empty) - { - return true; - } - - int[CommonType!(E1, E2)] counts; - - foreach (item; r1) - { - ++counts[item]; - } - - foreach (item; r2) - { - if (--counts[item] < 0) - { - return false; - } - } - - return true; -} - -/// ditto -bool isPermutation(alias pred = "a == b", Range1, Range2) -(Range1 r1, Range2 r2) -if (is(typeof(binaryFun!(pred))) && - isForwardRange!Range1 && - isForwardRange!Range2 && - !isInfinite!Range1 && - !isInfinite!Range2) -{ - import std.algorithm.searching : count; - - alias predEquals = binaryFun!(pred); - alias E1 = Unqual!(ElementType!Range1); - alias E2 = Unqual!(ElementType!Range2); - - if (!isSameLength(r1.save, r2.save)) - { - return false; - } - - // Skip the elements at the beginning where r1.front == r2.front, - // they are in the same order and don't need to be counted. - while (!r1.empty && !r2.empty && predEquals(r1.front, r2.front)) - { - r1.popFront(); - r2.popFront(); - } - - if (r1.empty && r2.empty) - { - return true; - } - - size_t r1_count; - size_t r2_count; - - // At each element item, when computing the count of item, scan it while - // also keeping track of the scanning index. If the first occurrence - // of item in the scanning loop has an index smaller than the current index, - // then you know that the element has been seen before - size_t index; - outloop: for (auto r1s1 = r1.save; !r1s1.empty; r1s1.popFront, index++) - { - auto item = r1s1.front; - r1_count = 0; - r2_count = 0; - - size_t i; - for (auto r1s2 = r1.save; !r1s2.empty; r1s2.popFront, i++) - { - auto e = r1s2.front; - if (predEquals(e, item) && i < index) - { - continue outloop; - } - else if (predEquals(e, item)) - { - ++r1_count; - } - } - - r2_count = r2.save.count!pred(item); - - if (r1_count != r2_count) - { - return false; - } - } - - return true; -} - -/// -@safe pure unittest -{ - import std.typecons : Yes; - - assert(isPermutation([1, 2, 3], [3, 2, 1])); - assert(isPermutation([1.1, 2.3, 3.5], [2.3, 3.5, 1.1])); - assert(isPermutation("abc", "bca")); - - assert(!isPermutation([1, 2], [3, 4])); - assert(!isPermutation([1, 1, 2, 3], [1, 2, 2, 3])); - assert(!isPermutation([1, 1], [1, 1, 1])); - - // Faster, but allocates GC handled memory - assert(isPermutation!(Yes.allocateGC)([1.1, 2.3, 3.5], [2.3, 3.5, 1.1])); - assert(!isPermutation!(Yes.allocateGC)([1, 2], [3, 4])); -} - -// Test @nogc inference -@safe @nogc pure unittest -{ - static immutable arr1 = [1, 2, 3]; - static immutable arr2 = [3, 2, 1]; - assert(isPermutation(arr1, arr2)); - - static immutable arr3 = [1, 1, 2, 3]; - static immutable arr4 = [1, 2, 2, 3]; - assert(!isPermutation(arr3, arr4)); -} - -@safe pure unittest -{ - import std.internal.test.dummyrange; - - auto r1 = new ReferenceForwardRange!int([1, 2, 3, 4]); - auto r2 = new ReferenceForwardRange!int([1, 2, 4, 3]); - assert(isPermutation(r1, r2)); - - auto r3 = new ReferenceForwardRange!int([1, 2, 3, 4]); - auto r4 = new ReferenceForwardRange!int([4, 2, 1, 3]); - assert(isPermutation!(Yes.allocateGC)(r3, r4)); - - auto r5 = new ReferenceForwardRange!int([1, 2, 3]); - auto r6 = new ReferenceForwardRange!int([4, 2, 1, 3]); - assert(!isPermutation(r5, r6)); - - auto r7 = new ReferenceForwardRange!int([4, 2, 1, 3]); - auto r8 = new ReferenceForwardRange!int([1, 2, 3]); - assert(!isPermutation!(Yes.allocateGC)(r7, r8)); - - DummyRange!(ReturnBy.Reference, Length.Yes, RangeType.Random) r9; - DummyRange!(ReturnBy.Reference, Length.Yes, RangeType.Random) r10; - assert(isPermutation(r9, r10)); - - DummyRange!(ReturnBy.Reference, Length.Yes, RangeType.Random) r11; - DummyRange!(ReturnBy.Reference, Length.Yes, RangeType.Random) r12; - assert(isPermutation!(Yes.allocateGC)(r11, r12)); - - alias mytuple = Tuple!(int, int); - - assert(isPermutation!"a[0] == b[0]"( - [mytuple(1, 4), mytuple(2, 5)], - [mytuple(2, 3), mytuple(1, 2)] - )); -} - -/** -Get the _first argument `a` that passes an `if (unaryFun!pred(a))` test. If -no argument passes the test, return the last argument. - -Similar to behaviour of the `or` operator in dynamic languages such as Lisp's -`(or ...)` and Python's `a or b or ...` except that the last argument is -returned upon no match. - -Simplifies logic, for instance, in parsing rules where a set of alternative -matchers are tried. The _first one that matches returns it match result, -typically as an abstract syntax tree (AST). - -Bugs: -Lazy parameters are currently, too restrictively, inferred by DMD to -always throw even though they don't need to be. This makes it impossible to -currently mark `either` as `nothrow`. See issue at $(BUGZILLA 12647). - -Returns: - The _first argument that passes the test `pred`. -*/ -CommonType!(T, Ts) either(alias pred = a => a, T, Ts...)(T first, lazy Ts alternatives) -if (alternatives.length >= 1 && - !is(CommonType!(T, Ts) == void) && - allSatisfy!(ifTestable, T, Ts)) -{ - alias predFun = unaryFun!pred; - - if (predFun(first)) return first; - - foreach (e; alternatives[0 .. $ - 1]) - if (predFun(e)) return e; - - return alternatives[$ - 1]; -} - -/// -@safe pure @betterC unittest -{ - const a = 1; - const b = 2; - auto ab = either(a, b); - static assert(is(typeof(ab) == const(int))); - assert(ab == a); - - auto c = 2; - const d = 3; - auto cd = either!(a => a == 3)(c, d); // use predicate - static assert(is(typeof(cd) == int)); - assert(cd == d); - - auto e = 0; - const f = 2; - auto ef = either(e, f); - static assert(is(typeof(ef) == int)); - assert(ef == f); -} - -/// -@safe pure unittest -{ - immutable p = 1; - immutable q = 2; - auto pq = either(p, q); - static assert(is(typeof(pq) == immutable(int))); - assert(pq == p); - - assert(either(3, 4) == 3); - assert(either(0, 4) == 4); - assert(either(0, 0) == 0); - assert(either("", "a") == ""); -} - -/// -@safe pure unittest -{ - string r = null; - assert(either(r, "a") == "a"); - assert(either("a", "") == "a"); - - immutable s = [1, 2]; - assert(either(s, s) == s); - - assert(either([0, 1], [1, 2]) == [0, 1]); - assert(either([0, 1], [1]) == [0, 1]); - assert(either("a", "b") == "a"); - - static assert(!__traits(compiles, either(1, "a"))); - static assert(!__traits(compiles, either(1.0, "a"))); - static assert(!__traits(compiles, either('a', "a"))); -} diff --git a/phobos/std/algorithm/internal.d b/phobos/std/algorithm/internal.d deleted file mode 100644 index 6b45599..0000000 --- a/phobos/std/algorithm/internal.d +++ /dev/null @@ -1,67 +0,0 @@ -// Written in the D programming language. - -/// Helper functions for std.algorithm package. -module std.algorithm.internal; - - -// Same as std.string.format, but "self-importing". -// Helps reduce code and imports, particularly in static asserts. -// Also helps with missing imports errors. -package template algoFormat() -{ - import std.format : format; - alias algoFormat = format; -} - -// Internal random array generators -version (StdUnittest) -{ - package enum size_t maxArraySize = 50; - package enum size_t minArraySize = maxArraySize - 1; - - package string[] rndstuff(T : string)() - { - import std.random : Xorshift, uniform; - - static rnd = Xorshift(234_567_891); - string[] result = - new string[uniform(minArraySize, maxArraySize, rnd)]; - string alpha = "abcdefghijABCDEFGHIJ"; - foreach (ref s; result) - { - foreach (i; 0 .. uniform(0u, 20u, rnd)) - { - auto j = uniform(0, alpha.length - 1, rnd); - s ~= alpha[j]; - } - } - return result; - } - - package int[] rndstuff(T : int)() - { - import std.random : Xorshift, uniform; - - static rnd = Xorshift(345_678_912); - int[] result = new int[uniform(minArraySize, maxArraySize, rnd)]; - foreach (ref i; result) - { - i = uniform(-100, 100, rnd); - } - return result; - } - - package double[] rndstuff(T : double)() - { - double[] result; - foreach (i; rndstuff!(int)()) - { - result ~= i / 50.0; - } - return result; - } -} - -// Used instead of `&object.member` when `member` may be -// either a field or a @property function. -package(std) T* addressOf(T)(ref T val) { return &val; } diff --git a/phobos/std/algorithm/iteration.d b/phobos/std/algorithm/iteration.d deleted file mode 100644 index 1453d2b..0000000 --- a/phobos/std/algorithm/iteration.d +++ /dev/null @@ -1,8068 +0,0 @@ -// Written in the D programming language. -/** -This is a submodule of $(MREF std, algorithm). -It contains generic iteration algorithms. - -$(SCRIPT inhibitQuickIndex = 1;) -$(BOOKTABLE Cheat Sheet, -$(TR $(TH Function Name) $(TH Description)) -$(T2 cache, - Eagerly evaluates and caches another range's `front`.) -$(T2 cacheBidirectional, - As above, but also provides `back` and `popBack`.) -$(T2 chunkBy, - `chunkBy!((a,b) => a[1] == b[1])([[1, 1], [1, 2], [2, 2], [2, 1]])` - returns a range containing 3 subranges: the first with just - `[1, 1]`; the second with the elements `[1, 2]` and `[2, 2]`; - and the third with just `[2, 1]`.) -$(T2 cumulativeFold, - `cumulativeFold!((a, b) => a + b)([1, 2, 3, 4])` returns a - lazily-evaluated range containing the successive reduced values `1`, - `3`, `6`, `10`.) -$(T2 each, - `each!writeln([1, 2, 3])` eagerly prints the numbers `1`, `2` - and `3` on their own lines.) -$(T2 filter, - `filter!(a => a > 0)([1, -1, 2, 0, -3])` iterates over elements `1` - and `2`.) -$(T2 filterBidirectional, - Similar to `filter`, but also provides `back` and `popBack` at - a small increase in cost.) -$(T2 fold, - `fold!((a, b) => a + b)([1, 2, 3, 4])` returns `10`.) -$(T2 group, - `group([5, 2, 2, 3, 3])` returns a range containing the tuples - `tuple(5, 1)`, `tuple(2, 2)`, and `tuple(3, 2)`.) -$(T2 joiner, - `joiner(["hello", "world!"], "; ")` returns a range that iterates - over the characters `"hello; world!"`. No new string is created - - the existing inputs are iterated.) -$(T2 map, - `map!(a => a * 2)([1, 2, 3])` lazily returns a range with the numbers - `2`, `4`, `6`.) -$(T2 mean, - Colloquially known as the average, `mean([1, 2, 3])` returns `2`.) -$(T2 permutations, - Lazily computes all permutations using Heap's algorithm.) -$(T2 reduce, - `reduce!((a, b) => a + b)([1, 2, 3, 4])` returns `10`. - This is the old implementation of `fold`.) -$(T2 splitWhen, - Lazily splits a range by comparing adjacent elements.) -$(T2 splitter, - Lazily splits a range by a separator.) -$(T2 substitute, - `[1, 2].substitute(1, 0.1)` returns `[0.1, 2]`.) -$(T2 sum, - Same as `fold`, but specialized for accurate summation.) -$(T2 uniq, - Iterates over the unique elements in a range, which is assumed sorted.) -) - -Copyright: Andrei Alexandrescu 2008-. - -License: $(HTTP boost.org/LICENSE_1_0.txt, Boost License 1.0). - -Authors: $(HTTP erdani.com, Andrei Alexandrescu) - -Source: $(PHOBOSSRC std/algorithm/iteration.d) - -Macros: -T2=$(TR $(TDNW $(LREF $1)) $(TD $+)) - */ -module std.algorithm.iteration; - -import std.functional : unaryFun, binaryFun; -import std.range.primitives; -import std.traits; -import std.typecons : Flag, Yes, No; - -/++ -`cache` eagerly evaluates $(REF_ALTTEXT front, front, std,range,primitives) of `range` -on each construction or call to $(REF_ALTTEXT popFront, popFront, std,range,primitives), -to store the result in a _cache. -The result is then directly returned when $(REF_ALTTEXT front, front, std,range,primitives) is called, -rather than re-evaluated. - -This can be a useful function to place in a chain, after functions -that have expensive evaluation, as a lazy alternative to $(REF array, std,array). -In particular, it can be placed after a call to $(LREF map), or before a call -$(REF filter, std,range) or $(REF tee, std,range) - -`cache` may provide -$(REF_ALTTEXT bidirectional range, isBidirectionalRange, std,range,primitives) -iteration if needed, but since this comes at an increased cost, it must be explicitly requested via the -call to `cacheBidirectional`. Furthermore, a bidirectional _cache will -evaluate the "center" element twice, when there is only one element left in -the range. - -`cache` does not provide random access primitives, -as `cache` would be unable to _cache the random accesses. -If `Range` provides slicing primitives, -then `cache` will provide the same slicing primitives, -but `hasSlicing!Cache` will not yield true (as the $(REF hasSlicing, std,range,primitives) -trait also checks for random access). - -Params: - range = an $(REF_ALTTEXT input range, isInputRange, std,range,primitives) - -Returns: - An $(REF_ALTTEXT input range, isInputRange, std,range,primitives) with the cached values of range -+/ -auto cache(Range)(Range range) -if (isInputRange!Range) -{ - return _Cache!(Range, false)(range); -} - -/// ditto -auto cacheBidirectional(Range)(Range range) -if (isBidirectionalRange!Range) -{ - return _Cache!(Range, true)(range); -} - -/// -@safe unittest -{ - import std.algorithm.comparison : equal; - import std.range, std.stdio; - import std.typecons : tuple; - - ulong counter = 0; - double fun(int x) - { - ++counter; - // http://en.wikipedia.org/wiki/Quartic_function - return ( (x + 4.0) * (x + 1.0) * (x - 1.0) * (x - 3.0) ) / 14.0 + 0.5; - } - // Without cache, with array (greedy) - auto result1 = iota(-4, 5).map!(a =>tuple(a, fun(a)))() - .filter!(a => a[1] < 0)() - .map!(a => a[0])() - .array(); - - // the values of x that have a negative y are: - assert(equal(result1, [-3, -2, 2])); - - // Check how many times fun was evaluated. - // As many times as the number of items in both source and result. - assert(counter == iota(-4, 5).length + result1.length); - - counter = 0; - // Without array, with cache (lazy) - auto result2 = iota(-4, 5).map!(a =>tuple(a, fun(a)))() - .cache() - .filter!(a => a[1] < 0)() - .map!(a => a[0])(); - - // the values of x that have a negative y are: - assert(equal(result2, [-3, -2, 2])); - - // Check how many times fun was evaluated. - // Only as many times as the number of items in source. - assert(counter == iota(-4, 5).length); -} - -// https://issues.dlang.org/show_bug.cgi?id=15891 -@safe pure unittest -{ - assert([1].map!(x=>[x].map!(y=>y)).cache.front.front == 1); -} - -/++ -Tip: `cache` is eager when evaluating elements. If calling front on the -underlying range has a side effect, it will be observable before calling -front on the actual cached range. - -Furthermore, care should be taken composing `cache` with $(REF take, std,range). -By placing `take` before `cache`, then `cache` will be "aware" -of when the range ends, and correctly stop caching elements when needed. -If calling front has no side effect though, placing `take` after `cache` -may yield a faster range. - -Either way, the resulting ranges will be equivalent, but maybe not at the -same cost or side effects. -+/ -@safe unittest -{ - import std.algorithm.comparison : equal; - import std.range; - int i = 0; - - auto r = iota(0, 4).tee!((a){i = a;}, No.pipeOnPop); - auto r1 = r.take(3).cache(); - auto r2 = r.cache().take(3); - - assert(equal(r1, [0, 1, 2])); - assert(i == 2); //The last "seen" element was 2. The data in cache has been cleared. - - assert(equal(r2, [0, 1, 2])); - assert(i == 3); //cache has accessed 3. It is still stored internally by cache. -} - -@safe unittest -{ - import std.algorithm.comparison : equal; - import std.range; - auto a = [1, 2, 3, 4]; - assert(equal(a.map!(a => (a - 1) * a)().cache(), [ 0, 2, 6, 12])); - assert(equal(a.map!(a => (a - 1) * a)().cacheBidirectional().retro(), [12, 6, 2, 0])); - auto r1 = [1, 2, 3, 4].cache() [1 .. $]; - auto r2 = [1, 2, 3, 4].cacheBidirectional()[1 .. $]; - assert(equal(r1, [2, 3, 4])); - assert(equal(r2, [2, 3, 4])); -} - -@safe unittest -{ - import std.algorithm.comparison : equal; - - //immutable test - static struct S - { - int i; - this(int i) - { - //this.i = i; - } - } - immutable(S)[] s = [S(1), S(2), S(3)]; - assert(equal(s.cache(), s)); - assert(equal(s.cacheBidirectional(), s)); -} - -@safe pure nothrow unittest -{ - import std.algorithm.comparison : equal; - - //safety etc - auto a = [1, 2, 3, 4]; - assert(equal(a.cache(), a)); - assert(equal(a.cacheBidirectional(), a)); -} - -@safe unittest -{ - char[][] stringbufs = ["hello".dup, "world".dup]; - auto strings = stringbufs.map!((a)=>a.idup)().cache(); - assert(strings.front is strings.front); -} - -@safe unittest -{ - import std.range : cycle; - import std.algorithm.comparison : equal; - - auto c = [1, 2, 3].cycle().cache(); - c = c[1 .. $]; - auto d = c[0 .. 1]; - assert(d.equal([2])); -} - -@safe unittest -{ - static struct Range - { - bool initialized = false; - bool front() @property {return initialized = true;} - void popFront() {initialized = false;} - enum empty = false; - } - auto r = Range().cache(); - assert(r.source.initialized == true); -} - -private struct _Cache(R, bool bidir) -{ - import core.exception : RangeError; - - private - { - import std.algorithm.internal : algoFormat; - import std.meta : AliasSeq; - - alias E = ElementType!R; - alias UE = Unqual!E; - - R source; - - static if (bidir) alias CacheTypes = AliasSeq!(UE, UE); - else alias CacheTypes = AliasSeq!UE; - CacheTypes caches; - - static assert(isAssignable!(UE, E) && is(UE : E), - algoFormat( - "Cannot instantiate range with %s because %s elements are not assignable to %s.", - R.stringof, - E.stringof, - UE.stringof - ) - ); - } - - this(R range) - { - source = range; - if (!range.empty) - { - caches[0] = source.front; - static if (bidir) - caches[1] = source.back; - } - else - { - // needed, because the compiler cannot deduce, that 'caches' is initialized - // see https://issues.dlang.org/show_bug.cgi?id=15891 - caches[0] = UE.init; - static if (bidir) - caches[1] = UE.init; - } - } - - static if (isInfinite!R) - enum empty = false; - else - bool empty() @property - { - return source.empty; - } - - mixin ImplementLength!source; - - E front() @property - { - version (assert) if (empty) throw new RangeError(); - return caches[0]; - } - static if (bidir) E back() @property - { - version (assert) if (empty) throw new RangeError(); - return caches[1]; - } - - void popFront() - { - version (assert) if (empty) throw new RangeError(); - source.popFront(); - if (!source.empty) - caches[0] = source.front; - else - { - // see https://issues.dlang.org/show_bug.cgi?id=15891 - caches[0] = UE.init; - static if (bidir) - caches[1] = UE.init; - } - } - static if (bidir) void popBack() - { - version (assert) if (empty) throw new RangeError(); - source.popBack(); - if (!source.empty) - caches[1] = source.back; - else - { - // see https://issues.dlang.org/show_bug.cgi?id=15891 - caches[0] = UE.init; - caches[1] = UE.init; - } - } - - static if (isForwardRange!R) - { - private this(R source, ref CacheTypes caches) - { - this.source = source; - this.caches = caches; - } - typeof(this) save() @property - { - return typeof(this)(source.save, caches); - } - } - - static if (hasSlicing!R) - { - enum hasEndSlicing = is(typeof(source[size_t.max .. $])); - - static if (hasEndSlicing) - { - private static struct DollarToken{} - enum opDollar = DollarToken.init; - - auto opSlice(size_t low, DollarToken) - { - return typeof(this)(source[low .. $]); - } - } - - static if (!isInfinite!R) - { - typeof(this) opSlice(size_t low, size_t high) - { - return typeof(this)(source[low .. high]); - } - } - else static if (hasEndSlicing) - { - auto opSlice(size_t low, size_t high) - in - { - assert(low <= high, "Bounds error when slicing cache."); - } - do - { - import std.range : takeExactly; - return this[low .. $].takeExactly(high - low); - } - } - } -} - -/** -Implements the homonym function (also known as `transform`) present -in many languages of functional flavor. The call `map!(fun)(range)` -returns a range of which elements are obtained by applying `fun(a)` -left to right for all elements `a` in `range`. The original ranges are -not changed. Evaluation is done lazily. - -Params: - fun = one or more transformation functions - -See_Also: - $(HTTP en.wikipedia.org/wiki/Map_(higher-order_function), Map (higher-order function)) -*/ -template map(fun...) -if (fun.length >= 1) -{ - /** - Params: - r = an $(REF_ALTTEXT input range, isInputRange, std,range,primitives) - Returns: - A range with each fun applied to all the elements. If there is more than one - fun, the element type will be `Tuple` containing one element for each fun. - */ - auto map(Range)(Range r) if (isInputRange!(Unqual!Range)) - { - import std.meta : AliasSeq, staticMap; - - alias RE = ElementType!(Range); - static if (fun.length > 1) - { - import std.functional : adjoin; - import std.meta : staticIndexOf; - - alias _funs = staticMap!(unaryFun, fun); - alias _fun = adjoin!_funs; - - // Once https://issues.dlang.org/show_bug.cgi?id=5710 is fixed - // accross all compilers (as of 2020-04, it wasn't fixed in LDC and GDC), - // this validation loop can be moved into a template. - foreach (f; _funs) - { - static assert(!is(typeof(f(RE.init)) == void), - "Mapping function(s) must not return void: " ~ _funs.stringof); - } - } - else - { - alias _fun = unaryFun!fun; - alias _funs = AliasSeq!(_fun); - - // Do the validation separately for single parameters due to - // https://issues.dlang.org/show_bug.cgi?id=15777. - static assert(!is(typeof(_fun(RE.init)) == void), - "Mapping function(s) must not return void: " ~ _funs.stringof); - } - - return MapResult!(_fun, Range)(r); - } -} - -/// -@safe @nogc unittest -{ - import std.algorithm.comparison : equal; - import std.range : chain, only; - auto squares = - chain(only(1, 2, 3, 4), only(5, 6)).map!(a => a * a); - assert(equal(squares, only(1, 4, 9, 16, 25, 36))); -} - -/** -Multiple functions can be passed to `map`. In that case, the -element type of `map` is a tuple containing one element for each -function. -*/ -@safe unittest -{ - auto sums = [2, 4, 6, 8]; - auto products = [1, 4, 9, 16]; - - size_t i = 0; - foreach (result; [ 1, 2, 3, 4 ].map!("a + a", "a * a")) - { - assert(result[0] == sums[i]); - assert(result[1] == products[i]); - ++i; - } -} - -/** -You may alias `map` with some function(s) to a symbol and use -it separately: -*/ -@safe unittest -{ - import std.algorithm.comparison : equal; - import std.conv : to; - - alias stringize = map!(to!string); - assert(equal(stringize([ 1, 2, 3, 4 ]), [ "1", "2", "3", "4" ])); -} - -// Verify workaround for https://issues.dlang.org/show_bug.cgi?id=15777 -@safe unittest -{ - import std.algorithm.mutation, std.string; - auto foo(string[] args) - { - return args.map!strip; - } -} - -private struct MapResult(alias fun, Range) -{ - alias R = Unqual!Range; - R _input; - - static if (isBidirectionalRange!R) - { - @property auto ref back()() - { - assert(!empty, "Attempting to fetch the back of an empty map."); - return fun(_input.back); - } - - void popBack()() - { - assert(!empty, "Attempting to popBack an empty map."); - _input.popBack(); - } - } - - this(R input) - { - _input = input; - } - - static if (isInfinite!R) - { - // Propagate infinite-ness. - enum bool empty = false; - } - else - { - @property bool empty() - { - return _input.empty; - } - } - - void popFront() - { - assert(!empty, "Attempting to popFront an empty map."); - _input.popFront(); - } - - @property auto ref front() - { - assert(!empty, "Attempting to fetch the front of an empty map."); - return fun(_input.front); - } - - static if (isRandomAccessRange!R) - { - static if (is(typeof(Range.init[ulong.max]))) - private alias opIndex_t = ulong; - else - private alias opIndex_t = uint; - - auto ref opIndex(opIndex_t index) - { - return fun(_input[index]); - } - } - - mixin ImplementLength!_input; - - static if (hasSlicing!R) - { - static if (is(typeof(_input[ulong.max .. ulong.max]))) - private alias opSlice_t = ulong; - else - private alias opSlice_t = uint; - - static if (hasLength!R) - { - auto opSlice(opSlice_t low, opSlice_t high) - { - return typeof(this)(_input[low .. high]); - } - } - else static if (is(typeof(_input[opSlice_t.max .. $]))) - { - struct DollarToken{} - enum opDollar = DollarToken.init; - auto opSlice(opSlice_t low, DollarToken) - { - return typeof(this)(_input[low .. $]); - } - - auto opSlice(opSlice_t low, opSlice_t high) - { - import std.range : takeExactly; - return this[low .. $].takeExactly(high - low); - } - } - } - - static if (isForwardRange!R) - { - @property auto save() - { - return typeof(this)(_input.save); - } - } -} - -@safe unittest -{ - import std.algorithm.comparison : equal; - import std.conv : to; - import std.functional : adjoin; - - alias stringize = map!(to!string); - assert(equal(stringize([ 1, 2, 3, 4 ]), [ "1", "2", "3", "4" ])); - - uint counter; - alias count = map!((a) { return counter++; }); - assert(equal(count([ 10, 2, 30, 4 ]), [ 0, 1, 2, 3 ])); - - counter = 0; - adjoin!((a) { return counter++; }, (a) { return counter++; })(1); - alias countAndSquare = map!((a) { return counter++; }, (a) { return counter++; }); - //assert(equal(countAndSquare([ 10, 2 ]), [ tuple(0u, 100), tuple(1u, 4) ])); -} - -@safe unittest -{ - import std.algorithm.comparison : equal; - import std.ascii : toUpper; - import std.internal.test.dummyrange; - import std.range; - import std.typecons : tuple; - import std.random : uniform, Random = Xorshift; - - int[] arr1 = [ 1, 2, 3, 4 ]; - const int[] arr1Const = arr1; - int[] arr2 = [ 5, 6 ]; - auto squares = map!("a * a")(arr1Const); - assert(squares[$ - 1] == 16); - assert(equal(squares, [ 1, 4, 9, 16 ][])); - assert(equal(map!("a * a")(chain(arr1, arr2)), [ 1, 4, 9, 16, 25, 36 ][])); - - // Test the caching stuff. - assert(squares.back == 16); - auto squares2 = squares.save; - assert(squares2.back == 16); - - assert(squares2.front == 1); - squares2.popFront(); - assert(squares2.front == 4); - squares2.popBack(); - assert(squares2.front == 4); - assert(squares2.back == 9); - - assert(equal(map!("a * a")(chain(arr1, arr2)), [ 1, 4, 9, 16, 25, 36 ][])); - - uint i; - foreach (e; map!("a", "a * a")(arr1)) - { - assert(e[0] == ++i); - assert(e[1] == i * i); - } - - // Test length. - assert(squares.length == 4); - assert(map!"a * a"(chain(arr1, arr2)).length == 6); - - // Test indexing. - assert(squares[0] == 1); - assert(squares[1] == 4); - assert(squares[2] == 9); - assert(squares[3] == 16); - - // Test slicing. - auto squareSlice = squares[1 .. squares.length - 1]; - assert(equal(squareSlice, [4, 9][])); - assert(squareSlice.back == 9); - assert(squareSlice[1] == 9); - - // Test on a forward range to make sure it compiles when all the fancy - // stuff is disabled. - auto fibsSquares = map!"a * a"(recurrence!("a[n-1] + a[n-2]")(1, 1)); - assert(fibsSquares.front == 1); - fibsSquares.popFront(); - fibsSquares.popFront(); - assert(fibsSquares.front == 4); - fibsSquares.popFront(); - assert(fibsSquares.front == 9); - - auto repeatMap = map!"a"(repeat(1)); - auto gen = Random(123_456_789); - auto index = uniform(0, 1024, gen); - static assert(isInfinite!(typeof(repeatMap))); - assert(repeatMap[index] == 1); - - auto intRange = map!"a"([1,2,3]); - static assert(isRandomAccessRange!(typeof(intRange))); - assert(equal(intRange, [1, 2, 3])); - - foreach (DummyType; AllDummyRanges) - { - DummyType d; - auto m = map!"a * a"(d); - - static assert(propagatesRangeType!(typeof(m), DummyType)); - assert(equal(m, [1,4,9,16,25,36,49,64,81,100])); - } - - //Test string access - string s1 = "hello world!"; - dstring s2 = "日本語"; - dstring s3 = "hello world!"d; - auto ms1 = map!(toUpper)(s1); - auto ms2 = map!(toUpper)(s2); - auto ms3 = map!(toUpper)(s3); - static assert(!is(ms1[0])); //narrow strings can't be indexed - assert(ms2[0] == '日'); - assert(ms3[0] == 'H'); - static assert(!is(ms1[0 .. 1])); //narrow strings can't be sliced - assert(equal(ms2[0 .. 2], "日本"w)); - assert(equal(ms3[0 .. 2], "HE")); - - // https://issues.dlang.org/show_bug.cgi?id=5753 - static void voidFun(int) {} - static int nonvoidFun(int) { return 0; } - static assert(!__traits(compiles, map!voidFun([1]))); - static assert(!__traits(compiles, map!(voidFun, voidFun)([1]))); - static assert(!__traits(compiles, map!(nonvoidFun, voidFun)([1]))); - static assert(!__traits(compiles, map!(voidFun, nonvoidFun)([1]))); - static assert(!__traits(compiles, map!(a => voidFun(a))([1]))); - - // https://issues.dlang.org/show_bug.cgi?id=15480 - auto dd = map!(z => z * z, c => c * c * c)([ 1, 2, 3, 4 ]); - assert(dd[0] == tuple(1, 1)); - assert(dd[1] == tuple(4, 8)); - assert(dd[2] == tuple(9, 27)); - assert(dd[3] == tuple(16, 64)); - assert(dd.length == 4); -} - -// Verify fix for: https://issues.dlang.org/show_bug.cgi?id=16034 -@safe unittest -{ - struct One - { - int entry = 1; - @disable this(this); - } - - One[] ones = [One(), One()]; - - import std.algorithm.comparison : equal; - - assert(ones.map!`a.entry + 1`.equal([2, 2])); -} - - -@safe unittest -{ - import std.algorithm.comparison : equal; - import std.range; - auto LL = iota(1L, 4L); - auto m = map!"a*a"(LL); - assert(equal(m, [1L, 4L, 9L])); -} - -@safe unittest -{ - import std.range : iota; - - // https://issues.dlang.org/show_bug.cgi?id=10130 - map of iota with const step. - const step = 2; - assert(map!(i => i)(iota(0, 10, step)).walkLength == 5); - - // Need these to all by const to repro the float case, due to the - // CommonType template used in the float specialization of iota. - const floatBegin = 0.0; - const floatEnd = 1.0; - const floatStep = 0.02; - assert(map!(i => i)(iota(floatBegin, floatEnd, floatStep)).walkLength == 50); -} - -@safe unittest -{ - import std.algorithm.comparison : equal; - import std.range; - //slicing infinites - auto rr = iota(0, 5).cycle().map!"a * a"(); - alias RR = typeof(rr); - static assert(hasSlicing!RR); - rr = rr[6 .. $]; //Advances 1 cycle and 1 unit - assert(equal(rr[0 .. 5], [1, 4, 9, 16, 0])); -} - -@safe unittest -{ - import std.range; - struct S {int* p;} - auto m = immutable(S).init.repeat().map!"a".save; - assert(m.front == immutable(S)(null)); -} - -// Issue 20928 -@safe unittest -{ - struct Always3 - { - enum empty = false; - auto save() { return this; } - long front() { return 3; } - void popFront() {} - long opIndex(ulong i) { return 3; } - long opIndex(ulong i) immutable { return 3; } - } - - import std.algorithm.iteration : map; - Always3.init.map!(e => e)[ulong.max]; -} - -// each -/** -Eagerly iterates over `r` and calls `fun` with _each element. - -If no function to call is specified, `each` defaults to doing nothing but -consuming the entire range. `r.front` will be evaluated, but that can be avoided -by specifying a lambda with a `lazy` parameter. - -`each` also supports `opApply`-based types, so it works with e.g. $(REF -parallel, std,parallelism). - -Normally the entire range is iterated. If partial iteration (early stopping) is -desired, `fun` needs to return a value of type $(REF Flag, -std,typecons)`!"each"` (`Yes.each` to continue iteration, or `No.each` to stop -iteration). - -Params: - fun = function to apply to _each element of the range - r = range or iterable over which `each` iterates - -Returns: `Yes.each` if the entire range was iterated, `No.each` in case of early -stopping. - -See_Also: $(REF tee, std,range) - */ -template each(alias fun = "a") -{ - import std.meta : AliasSeq; - import std.traits : Parameters; - import std.typecons : Flag, Yes, No; - -private: - alias BinaryArgs = AliasSeq!(fun, "i", "a"); - - enum isRangeUnaryIterable(R) = - is(typeof(unaryFun!fun(R.init.front))); - - enum isRangeBinaryIterable(R) = - is(typeof(binaryFun!BinaryArgs(0, R.init.front))); - - enum isRangeIterable(R) = - isInputRange!R && - (isRangeUnaryIterable!R || isRangeBinaryIterable!R); - - enum isForeachUnaryIterable(R) = - is(typeof((R r) { - foreach (ref a; r) - cast(void) unaryFun!fun(a); - })); - - enum isForeachUnaryWithIndexIterable(R) = - is(typeof((R r) { - foreach (i, ref a; r) - cast(void) binaryFun!BinaryArgs(i, a); - })); - - enum isForeachBinaryIterable(R) = - is(typeof((R r) { - foreach (ref a, ref b; r) - cast(void) binaryFun!fun(a, b); - })); - - enum isForeachIterable(R) = - (!isForwardRange!R || isDynamicArray!R) && - (isForeachUnaryIterable!R || isForeachBinaryIterable!R || - isForeachUnaryWithIndexIterable!R); - -public: - /** - Params: - r = range or iterable over which each iterates - */ - Flag!"each" each(Range)(Range r) - if (!isForeachIterable!Range && ( - isRangeIterable!Range || - __traits(compiles, typeof(r.front).length))) - { - static if (isRangeIterable!Range) - { - debug(each) pragma(msg, "Using while for ", Range.stringof); - static if (isRangeUnaryIterable!Range) - { - while (!r.empty) - { - static if (!is(typeof(unaryFun!fun(r.front)) == Flag!"each")) - { - cast(void) unaryFun!fun(r.front); - } - else - { - if (unaryFun!fun(r.front) == No.each) return No.each; - } - - r.popFront(); - } - } - else // if (isRangeBinaryIterable!Range) - { - size_t i = 0; - while (!r.empty) - { - static if (!is(typeof(binaryFun!BinaryArgs(i, r.front)) == Flag!"each")) - { - cast(void) binaryFun!BinaryArgs(i, r.front); - } - else - { - if (binaryFun!BinaryArgs(i, r.front) == No.each) return No.each; - } - r.popFront(); - i++; - } - } - } - else - { - // range interface with >2 parameters. - for (auto range = r; !range.empty; range.popFront()) - { - static if (!is(typeof(fun(r.front.expand)) == Flag!"each")) - { - cast(void) fun(range.front.expand); - } - else - { - if (fun(range.front.expand)) return No.each; - } - } - } - return Yes.each; - } - - /// ditto - Flag!"each" each(Iterable)(auto ref Iterable r) - if (isForeachIterable!Iterable || - __traits(compiles, Parameters!(Parameters!(r.opApply)))) - { - static if (isForeachIterable!Iterable) - { - static if (isForeachUnaryIterable!Iterable) - { - debug(each) pragma(msg, "Using foreach UNARY for ", Iterable.stringof); - { - foreach (ref e; r) - { - static if (!is(typeof(unaryFun!fun(e)) == Flag!"each")) - { - cast(void) unaryFun!fun(e); - } - else - { - if (unaryFun!fun(e) == No.each) return No.each; - } - } - } - } - else static if (isForeachBinaryIterable!Iterable) - { - debug(each) pragma(msg, "Using foreach BINARY for ", Iterable.stringof); - foreach (ref a, ref b; r) - { - static if (!is(typeof(binaryFun!fun(a, b)) == Flag!"each")) - { - cast(void) binaryFun!fun(a, b); - } - else - { - if (binaryFun!fun(a, b) == No.each) return No.each; - } - } - } - else static if (isForeachUnaryWithIndexIterable!Iterable) - { - debug(each) pragma(msg, "Using foreach INDEX for ", Iterable.stringof); - foreach (i, ref e; r) - { - static if (!is(typeof(binaryFun!BinaryArgs(i, e)) == Flag!"each")) - { - cast(void) binaryFun!BinaryArgs(i, e); - } - else - { - if (binaryFun!BinaryArgs(i, e) == No.each) return No.each; - } - } - } - else - { - static assert(0, "Invalid foreach iteratable type " ~ Iterable.stringof ~ " met."); - } - return Yes.each; - } - else - { - // opApply with >2 parameters. count the delegate args. - // only works if it is not templated (otherwise we cannot count the args) - auto result = Yes.each; - auto dg(Parameters!(Parameters!(r.opApply)) params) - { - static if (!is(typeof(binaryFun!BinaryArgs(i, e)) == Flag!"each")) - { - fun(params); - return 0; // tells opApply to continue iteration - } - else - { - result = fun(params); - return result == Yes.each ? 0 : -1; - } - } - r.opApply(&dg); - return result; - } - } -} - -/// -@safe unittest -{ - import std.range : iota; - import std.typecons : No; - - int[] arr; - iota(5).each!(n => arr ~= n); - assert(arr == [0, 1, 2, 3, 4]); - - // stop iterating early - iota(5).each!((n) { arr ~= n; return No.each; }); - assert(arr == [0, 1, 2, 3, 4, 0]); - - // If the range supports it, the value can be mutated in place - arr.each!((ref n) => n++); - assert(arr == [1, 2, 3, 4, 5, 1]); - - arr.each!"a++"; - assert(arr == [2, 3, 4, 5, 6, 2]); - - auto m = arr.map!(n => n); - // by-ref lambdas are not allowed for non-ref ranges - static assert(!__traits(compiles, m.each!((ref n) => n++))); - - // The default predicate consumes the range - (&m).each(); - assert(m.empty); -} - -/// `each` can pass an index variable for iterable objects which support this -@safe unittest -{ - auto arr = new size_t[4]; - - arr.each!"a=i"(); - assert(arr == [0, 1, 2, 3]); - - arr.each!((i, ref e) => e = i * 2); - assert(arr == [0, 2, 4, 6]); -} - -/// opApply iterators work as well -@system unittest -{ - static class S - { - int x; - int opApply(scope int delegate(ref int _x) dg) { return dg(x); } - } - - auto s = new S; - s.each!"a++"; - assert(s.x == 1); -} - -// binary foreach with two ref args -@system unittest -{ - import std.range : lockstep; - - auto a = [ 1, 2, 3 ]; - auto b = [ 2, 3, 4 ]; - - a.lockstep(b).each!((ref x, ref y) { ++x; ++y; }); - - assert(a == [ 2, 3, 4 ]); - assert(b == [ 3, 4, 5 ]); -} - -// https://issues.dlang.org/show_bug.cgi?id=15358 -// application of `each` with >2 args (opApply) -@system unittest -{ - import std.range : lockstep; - auto a = [0,1,2]; - auto b = [3,4,5]; - auto c = [6,7,8]; - - lockstep(a, b, c).each!((ref x, ref y, ref z) { ++x; ++y; ++z; }); - - assert(a == [1,2,3]); - assert(b == [4,5,6]); - assert(c == [7,8,9]); -} - -// https://issues.dlang.org/show_bug.cgi?id=15358 -// application of `each` with >2 args (range interface) -@safe unittest -{ - import std.range : zip; - auto a = [0,1,2]; - auto b = [3,4,5]; - auto c = [6,7,8]; - - int[] res; - - zip(a, b, c).each!((x, y, z) { res ~= x + y + z; }); - - assert(res == [9, 12, 15]); -} - -// https://issues.dlang.org/show_bug.cgi?id=16255 -// `each` on opApply doesn't support ref -@safe unittest -{ - int[] dynamicArray = [1, 2, 3, 4, 5]; - int[5] staticArray = [1, 2, 3, 4, 5]; - - dynamicArray.each!((ref x) => x++); - assert(dynamicArray == [2, 3, 4, 5, 6]); - - staticArray.each!((ref x) => x++); - assert(staticArray == [2, 3, 4, 5, 6]); - - staticArray[].each!((ref x) => x++); - assert(staticArray == [3, 4, 5, 6, 7]); -} - -// https://issues.dlang.org/show_bug.cgi?id=16255 -// `each` on opApply doesn't support ref -@system unittest -{ - struct S - { - int x; - int opApply(int delegate(ref int _x) dg) { return dg(x); } - } - - S s; - foreach (ref a; s) ++a; - assert(s.x == 1); - s.each!"++a"; - assert(s.x == 2); -} - -// https://issues.dlang.org/show_bug.cgi?id=15357 -// `each` should behave similar to foreach -@safe unittest -{ - import std.range : iota; - - auto arr = [1, 2, 3, 4]; - - // 1 ref parameter - arr.each!((ref e) => e = 0); - assert(arr.sum == 0); - - // 1 ref parameter and index - arr.each!((i, ref e) => e = cast(int) i); - assert(arr.sum == 4.iota.sum); -} - -// https://issues.dlang.org/show_bug.cgi?id=15357 -// `each` should behave similar to foreach -@system unittest -{ - import std.range : iota, lockstep; - - // 2 ref parameters and index - auto arrA = [1, 2, 3, 4]; - auto arrB = [5, 6, 7, 8]; - lockstep(arrA, arrB).each!((ref a, ref b) { - a = 0; - b = 1; - }); - assert(arrA.sum == 0); - assert(arrB.sum == 4); - - // 3 ref parameters - auto arrC = [3, 3, 3, 3]; - lockstep(arrA, arrB, arrC).each!((ref a, ref b, ref c) { - a = 1; - b = 2; - c = 3; - }); - assert(arrA.sum == 4); - assert(arrB.sum == 8); - assert(arrC.sum == 12); -} - -// https://issues.dlang.org/show_bug.cgi?id=15357 -// `each` should behave similar to foreach -@system unittest -{ - import std.range : lockstep; - import std.typecons : Tuple; - - auto a = "abc"; - auto b = "def"; - - // print each character with an index - { - alias Element = Tuple!(size_t, "index", dchar, "value"); - Element[] rForeach, rEach; - foreach (i, c ; a) rForeach ~= Element(i, c); - a.each!((i, c) => rEach ~= Element(i, c)); - assert(rForeach == rEach); - assert(rForeach == [Element(0, 'a'), Element(1, 'b'), Element(2, 'c')]); - } - - // print pairs of characters - { - alias Element = Tuple!(dchar, "a", dchar, "b"); - Element[] rForeach, rEach; - foreach (c1, c2 ; a.lockstep(b)) rForeach ~= Element(c1, c2); - a.lockstep(b).each!((c1, c2) => rEach ~= Element(c1, c2)); - assert(rForeach == rEach); - assert(rForeach == [Element('a', 'd'), Element('b', 'e'), Element('c', 'f')]); - } -} - -// filter -/** -`filter!(predicate)(range)` returns a new range containing only elements `x` in `range` for -which `predicate(x)` returns `true`. - -The predicate is passed to $(REF unaryFun, std,functional), and can be either a string, or -any callable that can be executed via `pred(element)`. - -Params: - predicate = Function to apply to each element of range - -Returns: - An input range that contains the filtered elements. If `range` is at least a forward range, the return value of `filter` - will also be a forward range. - -See_Also: - $(HTTP en.wikipedia.org/wiki/Filter_(higher-order_function), Filter (higher-order function)), - $(REF filterBidirectional, std,algorithm,iteration) - */ -template filter(alias predicate) -if (is(typeof(unaryFun!predicate))) -{ - /** - Params: - range = An $(REF_ALTTEXT input range, isInputRange, std,range,primitives) - of elements - Returns: - A range containing only elements `x` in `range` for - which `predicate(x)` returns `true`. - */ - auto filter(Range)(Range range) if (isInputRange!(Unqual!Range)) - { - return FilterResult!(unaryFun!predicate, Range)(range); - } -} - -/// -@safe unittest -{ - import std.algorithm.comparison : equal; - import std.math.operations : isClose; - import std.range; - - int[] arr = [ 1, 2, 3, 4, 5 ]; - - // Filter below 3 - auto small = filter!(a => a < 3)(arr); - assert(equal(small, [ 1, 2 ])); - - // Filter again, but with Uniform Function Call Syntax (UFCS) - auto sum = arr.filter!(a => a < 3); - assert(equal(sum, [ 1, 2 ])); - - // In combination with chain() to span multiple ranges - int[] a = [ 3, -2, 400 ]; - int[] b = [ 100, -101, 102 ]; - auto r = chain(a, b).filter!(a => a > 0); - assert(equal(r, [ 3, 400, 100, 102 ])); - - // Mixing convertible types is fair game, too - double[] c = [ 2.5, 3.0 ]; - auto r1 = chain(c, a, b).filter!(a => cast(int) a != a); - assert(isClose(r1, [ 2.5 ])); -} - -private struct FilterResult(alias pred, Range) -{ - alias R = Unqual!Range; - R _input; - private bool _primed; - - private void prime() - { - if (_primed) return; - while (!_input.empty && !pred(_input.front)) - { - _input.popFront(); - } - _primed = true; - } - - this(R r) - { - _input = r; - } - - private this(R r, bool primed) - { - _input = r; - _primed = primed; - } - - auto opSlice() { return this; } - - static if (isInfinite!Range) - { - enum bool empty = false; - } - else - { - @property bool empty() { prime; return _input.empty; } - } - - void popFront() - { - prime; - do - { - _input.popFront(); - } while (!_input.empty && !pred(_input.front)); - } - - @property auto ref front() - { - prime; - assert(!empty, "Attempting to fetch the front of an empty filter."); - return _input.front; - } - - static if (isForwardRange!R) - { - @property auto save() - { - return typeof(this)(_input.save, _primed); - } - } -} - -@safe unittest -{ - import std.algorithm.comparison : equal; - import std.internal.test.dummyrange; - import std.range; - - auto shouldNotLoop4ever = repeat(1).filter!(x => x % 2 == 0); - static assert(isInfinite!(typeof(shouldNotLoop4ever))); - assert(!shouldNotLoop4ever.empty); - - int[] a = [ 3, 4, 2 ]; - auto r = filter!("a > 3")(a); - static assert(isForwardRange!(typeof(r))); - assert(equal(r, [ 4 ][])); - - a = [ 1, 22, 3, 42, 5 ]; - auto under10 = filter!("a < 10")(a); - assert(equal(under10, [1, 3, 5][])); - static assert(isForwardRange!(typeof(under10))); - under10.front = 4; - assert(equal(under10, [4, 3, 5][])); - under10.front = 40; - assert(equal(under10, [40, 3, 5][])); - under10.front = 1; - - auto infinite = filter!"a > 2"(repeat(3)); - static assert(isInfinite!(typeof(infinite))); - static assert(isForwardRange!(typeof(infinite))); - assert(infinite.front == 3); - - foreach (DummyType; AllDummyRanges) - { - DummyType d; - auto f = filter!"a & 1"(d); - assert(equal(f, [1,3,5,7,9])); - - static if (isForwardRange!DummyType) - { - static assert(isForwardRange!(typeof(f))); - } - } - - // With delegates - int x = 10; - int overX(int a) { return a > x; } - typeof(filter!overX(a)) getFilter() - { - return filter!overX(a); - } - auto r1 = getFilter(); - assert(equal(r1, [22, 42])); - - // With chain - auto nums = [0,1,2,3,4]; - assert(equal(filter!overX(chain(a, nums)), [22, 42])); - - // With copying of inner struct Filter to Map - auto arr = [1,2,3,4,5]; - auto m = map!"a + 1"(filter!"a < 4"(arr)); - assert(equal(m, [2, 3, 4])); -} - -@safe unittest -{ - import std.algorithm.comparison : equal; - - int[] a = [ 3, 4 ]; - const aConst = a; - auto r = filter!("a > 3")(aConst); - assert(equal(r, [ 4 ][])); - - a = [ 1, 22, 3, 42, 5 ]; - auto under10 = filter!("a < 10")(a); - assert(equal(under10, [1, 3, 5][])); - assert(equal(under10.save, [1, 3, 5][])); - assert(equal(under10.save, under10)); -} - -@safe unittest -{ - import std.algorithm.comparison : equal; - import std.functional : compose, pipe; - - assert(equal(compose!(map!"2 * a", filter!"a & 1")([1,2,3,4,5]), - [2,6,10])); - assert(equal(pipe!(filter!"a & 1", map!"2 * a")([1,2,3,4,5]), - [2,6,10])); -} - -@safe unittest -{ - import std.algorithm.comparison : equal; - - int x = 10; - int underX(int a) { return a < x; } - const(int)[] list = [ 1, 2, 10, 11, 3, 4 ]; - assert(equal(filter!underX(list), [ 1, 2, 3, 4 ])); -} - -// https://issues.dlang.org/show_bug.cgi?id=19823 -@safe unittest -{ - import std.algorithm.comparison : equal; - import std.range : dropOne; - - auto a = [1, 2, 3, 4]; - assert(a.filter!(a => a != 1).dropOne.equal([3, 4])); - assert(a.filter!(a => a != 2).dropOne.equal([3, 4])); - assert(a.filter!(a => a != 3).dropOne.equal([2, 4])); - assert(a.filter!(a => a != 4).dropOne.equal([2, 3])); - assert(a.filter!(a => a == 1).dropOne.empty); - assert(a.filter!(a => a == 2).dropOne.empty); - assert(a.filter!(a => a == 3).dropOne.empty); - assert(a.filter!(a => a == 4).dropOne.empty); -} - -/** - * Similar to `filter`, except it defines a - * $(REF_ALTTEXT bidirectional range, isBidirectionalRange, std,range,primitives). - * There is a speed disadvantage - the constructor spends time - * finding the last element in the range that satisfies the filtering - * condition (in addition to finding the first one). The advantage is - * that the filtered range can be spanned from both directions. Also, - * $(REF retro, std,range) can be applied against the filtered range. - * - * The predicate is passed to $(REF unaryFun, std,functional), and can either - * accept a string, or any callable that can be executed via `pred(element)`. - * - * Params: - * pred = Function to apply to each element of range - */ -template filterBidirectional(alias pred) -{ - /** - Params: - r = Bidirectional range of elements - Returns: - A range containing only the elements in `r` for which `pred` returns `true`. - */ - auto filterBidirectional(Range)(Range r) if (isBidirectionalRange!(Unqual!Range)) - { - return FilterBidiResult!(unaryFun!pred, Range)(r); - } -} - -/// -@safe unittest -{ - import std.algorithm.comparison : equal; - import std.range; - - int[] arr = [ 1, 2, 3, 4, 5 ]; - auto small = filterBidirectional!("a < 3")(arr); - static assert(isBidirectionalRange!(typeof(small))); - assert(small.back == 2); - assert(equal(small, [ 1, 2 ])); - assert(equal(retro(small), [ 2, 1 ])); - // In combination with chain() to span multiple ranges - int[] a = [ 3, -2, 400 ]; - int[] b = [ 100, -101, 102 ]; - auto r = filterBidirectional!("a > 0")(chain(a, b)); - assert(r.back == 102); -} - -private struct FilterBidiResult(alias pred, Range) -{ - alias R = Unqual!Range; - R _input; - - this(R r) - { - _input = r; - while (!_input.empty && !pred(_input.front)) _input.popFront(); - while (!_input.empty && !pred(_input.back)) _input.popBack(); - } - - @property bool empty() { return _input.empty; } - - void popFront() - { - do - { - _input.popFront(); - } while (!_input.empty && !pred(_input.front)); - } - - @property auto ref front() - { - assert(!empty, "Attempting to fetch the front of an empty filterBidirectional."); - return _input.front; - } - - void popBack() - { - do - { - _input.popBack(); - } while (!_input.empty && !pred(_input.back)); - } - - @property auto ref back() - { - assert(!empty, "Attempting to fetch the back of an empty filterBidirectional."); - return _input.back; - } - - @property auto save() - { - return typeof(this)(_input.save); - } -} - -/** -Groups consecutively equivalent elements into a single tuple of the element and -the number of its repetitions. - -Similarly to `uniq`, `group` produces a range that iterates over unique -consecutive elements of the given range. Each element of this range is a tuple -of the element and the number of times it is repeated in the original range. -Equivalence of elements is assessed by using the predicate `pred`, which -defaults to `"a == b"`. The predicate is passed to $(REF binaryFun, std,functional), -and can either accept a string, or any callable that can be executed via -`pred(element, element)`. - -Params: - pred = Binary predicate for determining equivalence of two elements. - R = The range type - r = The $(REF_ALTTEXT input range, isInputRange, std,range,primitives) to - iterate over. - -Returns: A range of elements of type `Tuple!(ElementType!R, uint)`, -representing each consecutively unique element and its respective number of -occurrences in that run. This will be an input range if `R` is an input -range, and a forward range in all other cases. - -See_Also: $(LREF chunkBy), which chunks an input range into subranges - of equivalent adjacent elements. -*/ -Group!(pred, Range) group(alias pred = "a == b", Range)(Range r) -{ - return typeof(return)(r); -} - -/// ditto -struct Group(alias pred, R) -if (isInputRange!R) -{ - import std.typecons : Rebindable, tuple, Tuple; - - private alias comp = binaryFun!pred; - - private alias E = ElementType!R; - static if ((is(E == class) || is(E == interface)) && - (is(E == const) || is(E == immutable))) - { - private alias MutableE = Rebindable!E; - } - else static if (is(E : Unqual!E)) - { - private alias MutableE = Unqual!E; - } - else - { - private alias MutableE = E; - } - - private R _input; - private Tuple!(MutableE, uint) _current; - - /// - this(R input) - { - _input = input; - if (!_input.empty) popFront(); - } - - private this(R input, Tuple!(MutableE, uint) current) - { - _input = input; - _current = current; - } - - /// - void popFront() - { - if (_input.empty) - { - _current[1] = 0; - } - else - { - _current = tuple(_input.front, 1u); - _input.popFront(); - while (!_input.empty && comp(_current[0], _input.front)) - { - ++_current[1]; - _input.popFront(); - } - } - } - - static if (isInfinite!R) - { - /// - enum bool empty = false; // Propagate infiniteness. - } - else - { - /// - @property bool empty() - { - return _current[1] == 0; - } - } - - /// Returns: the front of the range - @property auto ref front() - { - assert(!empty, "Attempting to fetch the front of an empty Group."); - return _current; - } - - static if (isForwardRange!R) - { - /// - @property typeof(this) save() - { - return Group(_input.save, _current); - } - } -} - -/// -@safe unittest -{ - import std.algorithm.comparison : equal; - import std.typecons : tuple, Tuple; - - int[] arr = [ 1, 2, 2, 2, 2, 3, 4, 4, 4, 5 ]; - assert(equal(group(arr), [ tuple(1, 1u), tuple(2, 4u), tuple(3, 1u), - tuple(4, 3u), tuple(5, 1u) ][])); -} - -/** - * Using group, an associative array can be easily generated with the count of each - * unique element in the range. - */ -@safe unittest -{ - import std.algorithm.sorting : sort; - import std.array : assocArray; - - uint[string] result; - auto range = ["a", "b", "a", "c", "b", "c", "c", "d", "e"]; - result = range.sort!((a, b) => a < b) - .group - .assocArray; - - assert(result == ["a": 2U, "b": 2U, "c": 3U, "d": 1U, "e": 1U]); -} - -@safe unittest -{ - import std.algorithm.comparison : equal; - import std.internal.test.dummyrange; - import std.typecons : tuple, Tuple; - - int[] arr = [ 1, 2, 2, 2, 2, 3, 4, 4, 4, 5 ]; - assert(equal(group(arr), [ tuple(1, 1u), tuple(2, 4u), tuple(3, 1u), - tuple(4, 3u), tuple(5, 1u) ][])); - static assert(isForwardRange!(typeof(group(arr)))); - - foreach (DummyType; AllDummyRanges) - { - DummyType d; - auto g = group(d); - - static assert(d.rt == RangeType.Input || isForwardRange!(typeof(g))); - - assert(equal(g, [tuple(1, 1u), tuple(2, 1u), tuple(3, 1u), tuple(4, 1u), - tuple(5, 1u), tuple(6, 1u), tuple(7, 1u), tuple(8, 1u), - tuple(9, 1u), tuple(10, 1u)])); - } -} - -@safe unittest -{ - import std.algorithm.comparison : equal; - import std.typecons : tuple; - - // https://issues.dlang.org/show_bug.cgi?id=13857 - immutable(int)[] a1 = [1,1,2,2,2,3,4,4,5,6,6,7,8,9,9,9]; - auto g1 = group(a1); - assert(equal(g1, [ tuple(1, 2u), tuple(2, 3u), tuple(3, 1u), - tuple(4, 2u), tuple(5, 1u), tuple(6, 2u), - tuple(7, 1u), tuple(8, 1u), tuple(9, 3u) - ])); - - // https://issues.dlang.org/show_bug.cgi?id=13162 - immutable(ubyte)[] a2 = [1, 1, 1, 0, 0, 0]; - auto g2 = a2.group; - assert(equal(g2, [ tuple(1, 3u), tuple(0, 3u) ])); - - // https://issues.dlang.org/show_bug.cgi?id=10104 - const a3 = [1, 1, 2, 2]; - auto g3 = a3.group; - assert(equal(g3, [ tuple(1, 2u), tuple(2, 2u) ])); - - interface I {} - static class C : I { override size_t toHash() const nothrow @safe { return 0; } } - const C[] a4 = [new const C()]; - auto g4 = a4.group!"a is b"; - assert(g4.front[1] == 1); - - immutable I[] a5 = [new immutable C()]; - auto g5 = a5.group!"a is b"; - assert(g5.front[1] == 1); - - const(int[][]) a6 = [[1], [1]]; - auto g6 = a6.group; - assert(equal(g6.front[0], [1])); -} - -@safe unittest -{ - import std.algorithm.comparison : equal; - import std.typecons : tuple; - - int[] arr = [ 1, 2, 2, 2, 2, 3, 4, 4, 4, 5 ]; - auto r = arr.group; - assert(r.equal([ tuple(1,1u), tuple(2, 4u), tuple(3, 1u), tuple(4, 3u), tuple(5, 1u) ])); - r.popFront; - assert(r.equal([ tuple(2, 4u), tuple(3, 1u), tuple(4, 3u), tuple(5, 1u) ])); - auto s = r.save; - r.popFrontN(2); - assert(r.equal([ tuple(4, 3u), tuple(5, 1u) ])); - assert(s.equal([ tuple(2, 4u), tuple(3, 1u), tuple(4, 3u), tuple(5, 1u) ])); - s.popFront; - auto t = s.save; - r.popFront; - s.popFront; - assert(r.equal([ tuple(5, 1u) ])); - assert(s.equal([ tuple(4, 3u), tuple(5, 1u) ])); - assert(t.equal([ tuple(3, 1u), tuple(4, 3u), tuple(5, 1u) ])); -} - -// https://issues.dlang.org/show_bug.cgi?id=18657 -pure @safe unittest -{ - import std.algorithm.comparison : equal; - import std.range : refRange; - string s = "foo"; - auto r = refRange(&s).group; - assert(equal(r.save, "foo".group)); - assert(equal(r, "foo".group)); -} - -// Used by implementation of chunkBy for non-forward input ranges. -private struct ChunkByChunkImpl(alias pred, Range) -if (isInputRange!Range && !isForwardRange!Range) -{ - alias fun = binaryFun!pred; - - private Range *r; - private ElementType!Range prev; - - this(ref Range range, ElementType!Range _prev) - { - r = ⦥ - prev = _prev; - } - - @property bool empty() - { - return r.empty || !fun(prev, r.front); - } - - @property ElementType!Range front() - { - assert(!empty, "Attempting to fetch the front of an empty chunkBy chunk."); - return r.front; - } - - void popFront() - { - assert(!empty, "Attempting to popFront an empty chunkBy chunk."); - r.popFront(); - } -} - -private template ChunkByImplIsUnary(alias pred, Range) -{ - alias e = lvalueOf!(ElementType!Range); - - static if (is(typeof(binaryFun!pred(e, e)) : bool)) - enum ChunkByImplIsUnary = false; - else static if (is(typeof(unaryFun!pred(e) == unaryFun!pred(e)) : bool)) - enum ChunkByImplIsUnary = true; - else - static assert(0, "chunkBy expects either a binary predicate or "~ - "a unary predicate on range elements of type: "~ - ElementType!Range.stringof); -} - -// Implementation of chunkBy for non-forward input ranges. -private struct ChunkByImpl(alias pred, Range) -if (isInputRange!Range && !isForwardRange!Range) -{ - enum bool isUnary = ChunkByImplIsUnary!(pred, Range); - - static if (isUnary) - alias eq = binaryFun!((a, b) => unaryFun!pred(a) == unaryFun!pred(b)); - else - alias eq = binaryFun!pred; - - private Range r; - private ElementType!Range _prev; - private bool openChunk = false; - - this(Range _r) - { - r = _r; - if (!empty) - { - // Check reflexivity if predicate is claimed to be an equivalence - // relation. - assert(eq(r.front, r.front), - "predicate is not reflexive"); - - // _prev's type may be a nested struct, so must be initialized - // directly in the constructor (cannot call savePred()). - _prev = r.front; - } - else - { - // We won't use _prev, but must be initialized. - _prev = typeof(_prev).init; - } - } - @property bool empty() { return r.empty && !openChunk; } - - @property auto front() - { - assert(!empty, "Attempting to fetch the front of an empty chunkBy."); - openChunk = true; - static if (isUnary) - { - import std.typecons : tuple; - return tuple(unaryFun!pred(_prev), - ChunkByChunkImpl!(eq, Range)(r, _prev)); - } - else - { - return ChunkByChunkImpl!(eq, Range)(r, _prev); - } - } - - void popFront() - { - assert(!empty, "Attempting to popFront an empty chunkBy."); - openChunk = false; - while (!r.empty) - { - if (!eq(_prev, r.front)) - { - _prev = r.front; - break; - } - r.popFront(); - } - } -} -// Outer range for forward range version of chunkBy -private struct ChunkByOuter(Range, bool eqEquivalenceAssured) -{ - size_t groupNum; - Range current; - Range next; - static if (!eqEquivalenceAssured) - { - bool nextUpdated; - } -} - -// Inner range for forward range version of chunkBy -private struct ChunkByGroup(alias eq, Range, bool eqEquivalenceAssured) -{ - import std.typecons : RefCounted; - - alias OuterRange = ChunkByOuter!(Range, eqEquivalenceAssured); - - private size_t groupNum; - static if (eqEquivalenceAssured) - { - private Range start; - } - private Range current; - - // using union prevents RefCounted destructor from propagating @system to - // user code - union { private RefCounted!(OuterRange) mothership; } - private @trusted ref cargo() { return mothership.refCountedPayload; } - - private this(ref RefCounted!(OuterRange) origin) - { - () @trusted { mothership = origin; }(); - groupNum = cargo.groupNum; - current = cargo.current.save; - assert(!current.empty, "Passed range 'r' must not be empty"); - - static if (eqEquivalenceAssured) - { - start = cargo.current.save; - - // Check for reflexivity. - assert(eq(start.front, current.front), - "predicate is not reflexive"); - } - } - - // Cannot be a copy constructor due to https://issues.dlang.org/show_bug.cgi?id=22239 - this(this) scope @trusted - { - import core.lifetime : emplace; - // since mothership has to be in a union, we have to manually trigger - // an increment to the reference count. - auto temp = mothership; - mothership = temp; - - // prevents the reference count from falling back with brute force - emplace(&temp); - } - - @property bool empty() { return groupNum == size_t.max; } - @property auto ref front() { return current.front; } - - void popFront() - { - static if (!eqEquivalenceAssured) - { - auto prevElement = current.front; - } - - current.popFront(); - - static if (eqEquivalenceAssured) - { - //this requires transitivity from the predicate. - immutable nowEmpty = current.empty || !eq(start.front, current.front); - } - else - { - immutable nowEmpty = current.empty || !eq(prevElement, current.front); - } - - - if (nowEmpty) - { - if (groupNum == cargo.groupNum) - { - // If parent range hasn't moved on yet, help it along by - // saving location of start of next Group. - cargo.next = current.save; - static if (!eqEquivalenceAssured) - { - cargo.nextUpdated = true; - } - } - - groupNum = size_t.max; - } - } - - @property auto save() - { - auto copy = this; - copy.current = current.save; - return copy; - } - - @trusted ~this() - { - mothership.destroy; - } -} - -private enum GroupingOpType{binaryEquivalent, binaryAny, unary} - -// Single-pass implementation of chunkBy for forward ranges. -private struct ChunkByImpl(alias pred, alias eq, GroupingOpType opType, Range) -if (isForwardRange!Range) -{ - import std.typecons : RefCounted; - - enum bool eqEquivalenceAssured = opType != GroupingOpType.binaryAny; - alias OuterRange = ChunkByOuter!(Range, eqEquivalenceAssured); - alias InnerRange = ChunkByGroup!(eq, Range, eqEquivalenceAssured); - - static assert(isForwardRange!InnerRange); - - // using union prevents RefCounted destructor from propagating @system to - // user code - union { private RefCounted!OuterRange _impl; } - private @trusted ref impl() { return _impl; } - private @trusted ref implPL() { return _impl.refCountedPayload; } - - this(Range r) - { - import core.lifetime : move; - - auto savedR = r.save; - - static if (eqEquivalenceAssured) () @trusted - { - _impl = RefCounted!OuterRange(0, r, savedR.move); - }(); - else () @trusted - { - _impl = RefCounted!OuterRange(0, r, savedR.move, false); - }(); - } - - // Cannot be a copy constructor due to https://issues.dlang.org/show_bug.cgi?id=22239 - this(this) scope @trusted - { - import core.lifetime : emplace; - // since _impl has to be in a union, we have to manually trigger - // an increment to the reference count. - auto temp = _impl; - _impl = temp; - - // prevents the reference count from falling back with brute force - emplace(&temp); - } - - @property bool empty() { return implPL.current.empty; } - - static if (opType == GroupingOpType.unary) @property auto front() - { - import std.typecons : tuple; - - return tuple(unaryFun!pred(implPL.current.front), InnerRange(impl)); - } - else @property auto front() - { - return InnerRange(impl); - } - - static if (eqEquivalenceAssured) void popFront() - { - // Scan for next group. If we're lucky, one of our Groups would have - // already set .next to the start of the next group, in which case the - // loop is skipped. - while (!implPL.next.empty && eq(implPL.current.front, implPL.next.front)) - { - implPL.next.popFront(); - } - - implPL.current = implPL.next.save; - - // Indicate to any remaining Groups that we have moved on. - implPL.groupNum++; - } - else void popFront() - { - if (implPL.nextUpdated) - { - implPL.current = implPL.next.save; - } - else while (true) - { - auto prevElement = implPL.current.front; - implPL.current.popFront(); - if (implPL.current.empty) break; - if (!eq(prevElement, implPL.current.front)) break; - } - - implPL.nextUpdated = false; - // Indicate to any remaining Groups that we have moved on. - implPL.groupNum++; - } - - @property auto save() - { - // Note: the new copy of the range will be detached from any existing - // satellite Groups, and will not benefit from the .next acceleration. - return typeof(this)(implPL.current.save); - } - - static assert(isForwardRange!(typeof(this)), typeof(this).stringof - ~ " must be a forward range"); - - @trusted ~this() - { - _impl.destroy; - } -} - -//Test for https://issues.dlang.org/show_bug.cgi?id=14909 -@safe unittest -{ - import std.algorithm.comparison : equal; - import std.typecons : tuple; - import std.stdio; - auto n = 3; - auto s = [1,2,3].chunkBy!(a => a+n); - auto t = s.save.map!(x=>x[0]); - auto u = s.map!(x=>x[1]); - assert(t.equal([4,5,6])); - assert(u.equal!equal([[1],[2],[3]])); -} - -//Testing inferring @system correctly -@safe unittest -{ - struct DeadlySave - { - int front; - @safe void popFront(){front++;} - @safe bool empty(){return front >= 5;} - @system auto save(){return this;} - } - - auto test1() - { - DeadlySave src; - return src.walkLength; - - } - - auto test2() - { - DeadlySave src; - return src.chunkBy!((a,b) => a % 2 == b % 2).walkLength; - } - - static assert(isSafe!test1); - static assert(!isSafe!test2); -} - -//Test for https://issues.dlang.org/show_bug.cgi?id=18751 -@safe unittest -{ - import std.algorithm.comparison : equal; - - string[] data = [ "abc", "abc", "def" ]; - int[] indices = [ 0, 1, 2 ]; - - auto chunks = indices.chunkBy!((i, j) => data[i] == data[j]); - assert(chunks.equal!equal([ [ 0, 1 ], [ 2 ] ])); -} - -//Additional test for fix for issues 14909 and 18751 -@safe unittest -{ - import std.algorithm.comparison : equal; - auto v = [2,4,8,3,6,9,1,5,7]; - auto i = 2; - assert(v.chunkBy!((a,b) => a % i == b % i).equal!equal([[2,4,8],[3],[6],[9,1,5,7]])); -} - -@system unittest -{ - import std.algorithm.comparison : equal; - - size_t popCount = 0; - static class RefFwdRange - { - int[] impl; - size_t* pcount; - - @safe nothrow: - - this(int[] data, size_t* pcount) { impl = data; this.pcount = pcount; } - @property bool empty() { return impl.empty; } - @property auto ref front() { return impl.front; } - void popFront() - { - impl.popFront(); - (*pcount)++; - } - @property auto save() { return new RefFwdRange(impl, pcount); } - } - static assert(isForwardRange!RefFwdRange); - - auto testdata = new RefFwdRange([1, 3, 5, 2, 4, 7, 6, 8, 9], &popCount); - auto groups = testdata.chunkBy!((a,b) => (a % 2) == (b % 2)); - auto outerSave1 = groups.save; - - // Sanity test - assert(groups.equal!equal([[1, 3, 5], [2, 4], [7], [6, 8], [9]])); - assert(groups.empty); - - // Performance test for single-traversal use case: popFront should not have - // been called more times than there are elements if we traversed the - // segmented range exactly once. - assert(popCount == 9); - - // Outer range .save test - groups = outerSave1.save; - assert(!groups.empty); - - // Inner range .save test - auto grp1 = groups.front.save; - auto grp1b = grp1.save; - assert(grp1b.equal([1, 3, 5])); - assert(grp1.save.equal([1, 3, 5])); - - // Inner range should remain consistent after outer range has moved on. - groups.popFront(); - assert(grp1.save.equal([1, 3, 5])); - - // Inner range should not be affected by subsequent inner ranges. - assert(groups.front.equal([2, 4])); - assert(grp1.save.equal([1, 3, 5])); -} - -/** - * Chunks an input range into subranges of equivalent adjacent elements. - * In other languages this is often called `partitionBy`, `groupBy` - * or `sliceWhen`. - * - * Equivalence is defined by the predicate `pred`, which can be either - * binary, which is passed to $(REF binaryFun, std,functional), or unary, which is - * passed to $(REF unaryFun, std,functional). In the binary form, two range elements - * `a` and `b` are considered equivalent if `pred(a,b)` is true. In - * unary form, two elements are considered equivalent if `pred(a) == pred(b)` - * is true. - * - * This predicate must be an equivalence relation, that is, it must be - * reflexive (`pred(x,x)` is always true), symmetric - * (`pred(x,y) == pred(y,x)`), and transitive (`pred(x,y) && pred(y,z)` - * implies `pred(x,z)`). If this is not the case, the range returned by - * chunkBy may assert at runtime or behave erratically. Use $(LREF splitWhen) - * if you want to chunk by a predicate that is not an equivalence relation. - * - * Params: - * pred = Predicate for determining equivalence. - * r = An $(REF_ALTTEXT input range, isInputRange, std,range,primitives) to be chunked. - * - * Returns: With a binary predicate, a range of ranges is returned in which - * all elements in a given subrange are equivalent under the given predicate. - * With a unary predicate, a range of tuples is returned, with the tuple - * consisting of the result of the unary predicate for each subrange, and the - * subrange itself. Copying the range currently has reference semantics, but this may - * change in the future. - * - * Notes: - * - * Equivalent elements separated by an intervening non-equivalent element will - * appear in separate subranges; this function only considers adjacent - * equivalence. Elements in the subranges will always appear in the same order - * they appear in the original range. - * - * See_also: - * $(LREF group), which collapses adjacent equivalent elements into a single - * element. - */ -auto chunkBy(alias pred, Range)(Range r) -if (isInputRange!Range) -{ - static if (ChunkByImplIsUnary!(pred, Range)) - { - enum opType = GroupingOpType.unary; - alias eq = binaryFun!((a, b) => unaryFun!pred(a) == unaryFun!pred(b)); - } - else - { - enum opType = GroupingOpType.binaryEquivalent; - alias eq = binaryFun!pred; - } - static if (isForwardRange!Range) - return ChunkByImpl!(pred, eq, opType, Range)(r); - else - return ChunkByImpl!(pred, Range)(r); -} - -/// Showing usage with binary predicate: -@safe unittest -{ - import std.algorithm.comparison : equal; - - // Grouping by particular attribute of each element: - auto data = [ - [1, 1], - [1, 2], - [2, 2], - [2, 3] - ]; - - auto r1 = data.chunkBy!((a,b) => a[0] == b[0]); - assert(r1.equal!equal([ - [[1, 1], [1, 2]], - [[2, 2], [2, 3]] - ])); - - auto r2 = data.chunkBy!((a,b) => a[1] == b[1]); - assert(r2.equal!equal([ - [[1, 1]], - [[1, 2], [2, 2]], - [[2, 3]] - ])); -} - -/// Showing usage with unary predicate: -/* FIXME: pure nothrow*/ @safe unittest -{ - import std.algorithm.comparison : equal; - import std.range.primitives; - import std.typecons : tuple; - - // Grouping by particular attribute of each element: - auto range = - [ - [1, 1], - [1, 1], - [1, 2], - [2, 2], - [2, 3], - [2, 3], - [3, 3] - ]; - - auto byX = chunkBy!(a => a[0])(range); - auto expected1 = - [ - tuple(1, [[1, 1], [1, 1], [1, 2]]), - tuple(2, [[2, 2], [2, 3], [2, 3]]), - tuple(3, [[3, 3]]) - ]; - foreach (e; byX) - { - assert(!expected1.empty); - assert(e[0] == expected1.front[0]); - assert(e[1].equal(expected1.front[1])); - expected1.popFront(); - } - - auto byY = chunkBy!(a => a[1])(range); - auto expected2 = - [ - tuple(1, [[1, 1], [1, 1]]), - tuple(2, [[1, 2], [2, 2]]), - tuple(3, [[2, 3], [2, 3], [3, 3]]) - ]; - foreach (e; byY) - { - assert(!expected2.empty); - assert(e[0] == expected2.front[0]); - assert(e[1].equal(expected2.front[1])); - expected2.popFront(); - } -} - -/*FIXME: pure @safe nothrow*/ @system unittest -{ - import std.algorithm.comparison : equal; - import std.typecons : tuple; - - struct Item { int x, y; } - - // Force R to have only an input range API with reference semantics, so - // that we're not unknowingly making use of array semantics outside of the - // range API. - class RefInputRange(R) - { - R data; - this(R _data) pure @safe nothrow { data = _data; } - @property bool empty() pure @safe nothrow { return data.empty; } - @property auto front() pure @safe nothrow { assert(!empty); return data.front; } - void popFront() pure @safe nothrow { assert(!empty); data.popFront(); } - } - auto refInputRange(R)(R range) { return new RefInputRange!R(range); } - - // An input range API with value semantics. - struct ValInputRange(R) - { - R data; - this(R _data) pure @safe nothrow { data = _data; } - @property bool empty() pure @safe nothrow { return data.empty; } - @property auto front() pure @safe nothrow { assert(!empty); return data.front; } - void popFront() pure @safe nothrow { assert(!empty); data.popFront(); } - } - auto valInputRange(R)(R range) { return ValInputRange!R(range); } - - { - auto arr = [ Item(1,2), Item(1,3), Item(2,3) ]; - static assert(isForwardRange!(typeof(arr))); - - auto byX = chunkBy!(a => a.x)(arr); - static assert(isForwardRange!(typeof(byX))); - - auto byX_subrange1 = byX.front[1].save; - auto byX_subrange2 = byX.front[1].save; - static assert(isForwardRange!(typeof(byX_subrange1))); - static assert(isForwardRange!(typeof(byX_subrange2))); - - byX.popFront(); - assert(byX_subrange1.equal([ Item(1,2), Item(1,3) ])); - byX_subrange1.popFront(); - assert(byX_subrange1.equal([ Item(1,3) ])); - assert(byX_subrange2.equal([ Item(1,2), Item(1,3) ])); - - auto byY = chunkBy!(a => a.y)(arr); - static assert(isForwardRange!(typeof(byY))); - - auto byY2 = byY.save; - static assert(is(typeof(byY) == typeof(byY2))); - byY.popFront(); - assert(byY.front[0] == 3); - assert(byY.front[1].equal([ Item(1,3), Item(2,3) ])); - assert(byY2.front[0] == 2); - assert(byY2.front[1].equal([ Item(1,2) ])); - } - - // Test non-forward input ranges with reference semantics. - { - auto range = refInputRange([ Item(1,1), Item(1,2), Item(2,2) ]); - auto byX = chunkBy!(a => a.x)(range); - assert(byX.front[0] == 1); - assert(byX.front[1].equal([ Item(1,1), Item(1,2) ])); - byX.popFront(); - assert(byX.front[0] == 2); - assert(byX.front[1].equal([ Item(2,2) ])); - byX.popFront(); - assert(byX.empty); - assert(range.empty); - - range = refInputRange([ Item(1,1), Item(1,2), Item(2,2) ]); - auto byY = chunkBy!(a => a.y)(range); - assert(byY.front[0] == 1); - assert(byY.front[1].equal([ Item(1,1) ])); - byY.popFront(); - assert(byY.front[0] == 2); - assert(byY.front[1].equal([ Item(1,2), Item(2,2) ])); - byY.popFront(); - assert(byY.empty); - assert(range.empty); - } - - // Test non-forward input ranges with value semantics. - { - auto range = valInputRange([ Item(1,1), Item(1,2), Item(2,2) ]); - auto byX = chunkBy!(a => a.x)(range); - assert(byX.front[0] == 1); - assert(byX.front[1].equal([ Item(1,1), Item(1,2) ])); - byX.popFront(); - assert(byX.front[0] == 2); - assert(byX.front[1].equal([ Item(2,2) ])); - byX.popFront(); - assert(byX.empty); - assert(!range.empty); // Opposite of refInputRange test - - range = valInputRange([ Item(1,1), Item(1,2), Item(2,2) ]); - auto byY = chunkBy!(a => a.y)(range); - assert(byY.front[0] == 1); - assert(byY.front[1].equal([ Item(1,1) ])); - byY.popFront(); - assert(byY.front[0] == 2); - assert(byY.front[1].equal([ Item(1,2), Item(2,2) ])); - byY.popFront(); - assert(byY.empty); - assert(!range.empty); // Opposite of refInputRange test - } - - /* https://issues.dlang.org/show_bug.cgi?id=19532 - * General behavior of non-forward input ranges. - * - * - If the same chunk is retrieved multiple times via front, the separate chunk - * instances refer to a shared range segment that advances as a single range. - * - Emptying a chunk via popFront does not implicitly popFront the chunk off - * main range. The chunk is still available via front, it is just empty. - */ - { - import std.algorithm.comparison : equal; - import core.exception : AssertError; - import std.exception : assertThrown; - - auto a = [[0, 0], [0, 1], - [1, 2], [1, 3], [1, 4], - [2, 5], [2, 6], - [3, 7], - [4, 8]]; - - // Value input range - { - auto r = valInputRange(a).chunkBy!((a, b) => a[0] == b[0]); - - size_t numChunks = 0; - while (!r.empty) - { - ++numChunks; - auto chunk = r.front; - while (!chunk.empty) - { - assert(r.front.front[1] == chunk.front[1]); - chunk.popFront; - } - assert(!r.empty); - assert(r.front.empty); - r.popFront; - } - - assert(numChunks == 5); - - // Now front and popFront should assert. - bool thrown = false; - try r.front; - catch (AssertError) thrown = true; - assert(thrown); - - thrown = false; - try r.popFront; - catch (AssertError) thrown = true; - assert(thrown); - } - - // Reference input range - { - auto r = refInputRange(a).chunkBy!((a, b) => a[0] == b[0]); - - size_t numChunks = 0; - while (!r.empty) - { - ++numChunks; - auto chunk = r.front; - while (!chunk.empty) - { - assert(r.front.front[1] == chunk.front[1]); - chunk.popFront; - } - assert(!r.empty); - assert(r.front.empty); - r.popFront; - } - - assert(numChunks == 5); - - // Now front and popFront should assert. - bool thrown = false; - try r.front; - catch (AssertError) thrown = true; - assert(thrown); - - thrown = false; - try r.popFront; - catch (AssertError) thrown = true; - assert(thrown); - } - - // Ensure that starting with an empty range doesn't create an empty chunk. - { - int[] emptyRange = []; - - auto r1 = valInputRange(emptyRange).chunkBy!((a, b) => a == b); - auto r2 = refInputRange(emptyRange).chunkBy!((a, b) => a == b); - - assert(r1.empty); - assert(r2.empty); - - bool thrown = false; - try r1.front; - catch (AssertError) thrown = true; - assert(thrown); - - thrown = false; - try r1.popFront; - catch (AssertError) thrown = true; - assert(thrown); - - thrown = false; - try r2.front; - catch (AssertError) thrown = true; - assert(thrown); - - thrown = false; - try r2.popFront; - catch (AssertError) thrown = true; - assert(thrown); - } - } - - // https://issues.dlang.org/show_bug.cgi?id=19532 - Using roundRobin/chunkBy - { - import std.algorithm.comparison : equal; - import std.range : roundRobin; - - auto a0 = [0, 1, 3, 6]; - auto a1 = [0, 2, 4, 6, 7]; - auto a2 = [1, 2, 4, 6, 8, 8, 9]; - - auto expected = - [[0, 0], [1, 1], [2, 2], [3], [4, 4], [6, 6, 6], [7], [8, 8], [9]]; - - auto r1 = roundRobin(valInputRange(a0), valInputRange(a1), valInputRange(a2)) - .chunkBy!((a, b) => a == b); - assert(r1.equal!equal(expected)); - - auto r2 = roundRobin(refInputRange(a0), refInputRange(a1), refInputRange(a2)) - .chunkBy!((a, b) => a == b); - assert(r2.equal!equal(expected)); - - auto r3 = roundRobin(a0, a1, a2).chunkBy!((a, b) => a == b); - assert(r3.equal!equal(expected)); - } - - // https://issues.dlang.org/show_bug.cgi?id=19532 - Using merge/chunkBy - { - import std.algorithm.comparison : equal; - import std.algorithm.sorting : merge; - - auto a0 = [2, 3, 5]; - auto a1 = [2, 4, 5]; - auto a2 = [1, 2, 4, 5]; - - auto expected = [[1], [2, 2, 2], [3], [4, 4], [5, 5, 5]]; - - auto r1 = merge(valInputRange(a0), valInputRange(a1), valInputRange(a2)) - .chunkBy!((a, b) => a == b); - assert(r1.equal!equal(expected)); - - auto r2 = merge(refInputRange(a0), refInputRange(a1), refInputRange(a2)) - .chunkBy!((a, b) => a == b); - assert(r2.equal!equal(expected)); - - auto r3 = merge(a0, a1, a2).chunkBy!((a, b) => a == b); - assert(r3.equal!equal(expected)); - } - - // https://issues.dlang.org/show_bug.cgi?id=19532 - Using chunkBy/map-fold - { - import std.algorithm.comparison : equal; - import std.algorithm.iteration : fold, map; - - auto a = [0, 0, 1, 1, 1, 2, 2, 3, 3, 4, 4, 5, 6, 6, 6, 7, 8, 8, 9]; - auto expected = [0, 3, 4, 6, 8, 5, 18, 7, 16, 9]; - - auto r1 = a - .chunkBy!((a, b) => a == b) - .map!(c => c.fold!((a, b) => a + b)); - assert(r1.equal(expected)); - - auto r2 = valInputRange(a) - .chunkBy!((a, b) => a == b) - .map!(c => c.fold!((a, b) => a + b)); - assert(r2.equal(expected)); - - auto r3 = refInputRange(a) - .chunkBy!((a, b) => a == b) - .map!(c => c.fold!((a, b) => a + b)); - assert(r3.equal(expected)); - } - - // https://issues.dlang.org/show_bug.cgi?id=16169 - // https://issues.dlang.org/show_bug.cgi?id=17966 - // https://issues.dlang.org/show_bug.cgi?id=19532 - // Using multiwayMerge/chunkBy - { - import std.algorithm.comparison : equal; - import std.algorithm.setops : multiwayMerge; - - { - auto a0 = [2, 3, 5]; - auto a1 = [2, 4, 5]; - auto a2 = [1, 2, 4, 5]; - - auto expected = [[1], [2, 2, 2], [3], [4, 4], [5, 5, 5]]; - auto r = multiwayMerge([a0, a1, a2]).chunkBy!((a, b) => a == b); - assert(r.equal!equal(expected)); - } - { - auto a0 = [2, 3, 5]; - auto a1 = [2, 4, 5]; - auto a2 = [1, 2, 4, 5]; - - auto expected = [[1], [2, 2, 2], [3], [4, 4], [5, 5, 5]]; - auto r = - multiwayMerge([valInputRange(a0), valInputRange(a1), valInputRange(a2)]) - .chunkBy!((a, b) => a == b); - assert(r.equal!equal(expected)); - } - { - auto a0 = [2, 3, 5]; - auto a1 = [2, 4, 5]; - auto a2 = [1, 2, 4, 5]; - - auto expected = [[1], [2, 2, 2], [3], [4, 4], [5, 5, 5]]; - auto r = - multiwayMerge([refInputRange(a0), refInputRange(a1), refInputRange(a2)]) - .chunkBy!((a, b) => a == b); - assert(r.equal!equal(expected)); - } - } - - // https://issues.dlang.org/show_bug.cgi?id=20496 - { - auto r = [1,1,1,2,2,2,3,3,3]; - r.chunkBy!((ref e1, ref e2) => e1 == e2); - } -} - - - -// https://issues.dlang.org/show_bug.cgi?id=13805 -@safe unittest -{ - [""].map!((s) => s).chunkBy!((x, y) => true); -} - -/** -Splits a forward range into subranges in places determined by a binary -predicate. - -When iterating, one element of `r` is compared with `pred` to the next -element. If `pred` return true, a new subrange is started for the next element. -Otherwise, they are part of the same subrange. - -If the elements are compared with an inequality (!=) operator, consider -$(LREF chunkBy) instead, as it's likely faster to execute. - -Params: -pred = Predicate for determining where to split. The earlier element in the -source range is always given as the first argument. -r = A $(REF_ALTTEXT forward range, isForwardRange, std,range,primitives) to be split. -Returns: a range of subranges of `r`, split such that within a given subrange, -calling `pred` with any pair of adjacent elements as arguments returns `false`. -Copying the range currently has reference semantics, but this may change in the future. - -See_also: -$(LREF splitter), which uses elements as splitters instead of element-to-element -relations. -*/ - -auto splitWhen(alias pred, Range)(Range r) -if (isForwardRange!Range) -{ import std.functional : not; - return ChunkByImpl!(not!pred, not!pred, GroupingOpType.binaryAny, Range)(r); -} - -/// -nothrow pure @safe unittest -{ - import std.algorithm.comparison : equal; - import std.range : dropExactly; - auto source = [4, 3, 2, 11, 0, -3, -3, 5, 3, 0]; - - auto result1 = source.splitWhen!((a,b) => a <= b); - assert(result1.save.equal!equal([ - [4, 3, 2], - [11, 0, -3], - [-3], - [5, 3, 0] - ])); - - //splitWhen, like chunkBy, is currently a reference range (this may change - //in future). Remember to call `save` when appropriate. - auto result2 = result1.dropExactly(2); - assert(result1.save.equal!equal([ - [-3], - [5, 3, 0] - ])); -} - -//ensure we don't iterate the underlying range twice -nothrow @safe unittest -{ - import std.algorithm.comparison : equal; - import std.math.algebraic : abs; - - struct SomeRange - { - int[] elements; - static int popfrontsSoFar; - - auto front(){return elements[0];} - nothrow @safe void popFront() - { popfrontsSoFar++; - elements = elements[1 .. $]; - } - auto empty(){return elements.length == 0;} - auto save(){return this;} - } - - auto result = SomeRange([10, 9, 8, 5, 0, 1, 0, 8, 11, 10, 8, 12]) - .splitWhen!((a, b) => abs(a - b) >= 3); - - assert(result.equal!equal([ - [10, 9, 8], - [5], - [0, 1, 0], - [8], - [11, 10, 8], - [12] - ])); - - assert(SomeRange.popfrontsSoFar == 12); -} - -// Issue 13595 -@safe unittest -{ - import std.algorithm.comparison : equal; - auto r = [1, 2, 3, 4, 5, 6, 7, 8, 9].splitWhen!((x, y) => ((x*y) % 3) > 0); - assert(r.equal!equal([ - [1], - [2, 3, 4], - [5, 6, 7], - [8, 9] - ])); -} - -nothrow pure @safe unittest -{ - // Grouping by maximum adjacent difference: - import std.math.algebraic : abs; - import std.algorithm.comparison : equal; - auto r3 = [1, 3, 2, 5, 4, 9, 10].splitWhen!((a, b) => abs(a-b) >= 3); - assert(r3.equal!equal([ - [1, 3, 2], - [5, 4], - [9, 10] - ])); -} - -// empty range splitWhen -@nogc nothrow pure @system unittest -{ - int[1] sliceable; - auto result = sliceable[0 .. 0].splitWhen!((a,b) => a+b > 10); - assert(result.empty); -} - -// joiner -/** -Lazily joins a range of ranges with a separator. The separator itself -is a range. If a separator is not provided, then the ranges are -joined directly without anything in between them (often called `flatten` -in other languages). - -Params: - r = An $(REF_ALTTEXT input range, isInputRange, std,range,primitives) of input - ranges to be joined. - sep = A $(REF_ALTTEXT forward range, isForwardRange, std,range,primitives) of - element(s) to serve as separators in the joined range. - -Returns: -A range of elements in the joined range. This will be a bidirectional range if -both outer and inner ranges of `RoR` are at least bidirectional ranges. Else if -both outer and inner ranges of `RoR` are forward ranges, the returned range will -be likewise. Otherwise it will be only an input range. The -$(REF_ALTTEXT range bidirectionality, isBidirectionalRange, std,range,primitives) -is propagated if no separator is specified. - -See_also: -$(REF chain, std,range), which chains a sequence of ranges with compatible elements -into a single range. - -Note: -When both outer and inner ranges of `RoR` are bidirectional and the joiner is -iterated from the back to the front, the separator will still be consumed from -front to back, even if it is a bidirectional range too. - */ -auto joiner(RoR, Separator)(RoR r, Separator sep) -{ - static assert(isInputRange!RoR, "The type of RoR '", RoR.stringof - , " must be an InputRange (isInputRange!", RoR.stringof, ")."); - static assert(isInputRange!(ElementType!RoR), "The ElementyType of RoR '" - , ElementType!(RoR).stringof, "' must be an InputRange " - , "(isInputRange!(ElementType!(", RoR.stringof , ")))."); - static assert(isForwardRange!Separator, "The type of the Separator '" - , Separator.stringof, "' must be a ForwardRange (isForwardRange!(" - , Separator.stringof, "))."); - static assert(is(ElementType!Separator : ElementType!(ElementType!RoR)) - , "The type of the elements of the separator range does not match " - , "the type of the elements that are joined. Separator type '" - , ElementType!(Separator).stringof, "' is not implicitly" - , "convertible to range element type '" - , ElementType!(ElementType!RoR).stringof, "' (is(ElementType!" - , Separator.stringof, " : ElementType!(ElementType!", RoR.stringof - , ")))."); - - static struct Result - { - private RoR _items; - private ElementType!RoR _current; - bool inputStartsWithEmpty = false; - static if (isBidirectional) - { - private ElementType!RoR _currentBack; - bool inputEndsWithEmpty = false; - } - enum isBidirectional = isBidirectionalRange!RoR && - isBidirectionalRange!(ElementType!RoR); - static if (isRandomAccessRange!Separator) - { - static struct CurrentSep - { - private Separator _sep; - private size_t sepIndex; - private size_t sepLength; // cache the length for performance - auto front() { return _sep[sepIndex]; } - void popFront() { sepIndex++; } - auto empty() { return sepIndex >= sepLength; } - auto save() - { - auto copy = this; - copy._sep = _sep; - return copy; - } - void reset() - { - sepIndex = 0; - } - - void initialize(Separator sep) - { - _sep = sep; - sepIndex = sepLength = _sep.length; - } - } - } - else - { - static struct CurrentSep - { - private Separator _sep; - Separator payload; - - alias payload this; - - auto save() - { - auto copy = this; - copy._sep = _sep; - return copy; - } - - void reset() - { - payload = _sep.save; - } - - void initialize(Separator sep) - { - _sep = sep; - } - } - } - - private CurrentSep _currentSep; - static if (isBidirectional) - { - private CurrentSep _currentBackSep; - } - - private void setItem() - { - if (!_items.empty) - { - // If we're exporting .save, we must not consume any of the - // subranges, since RoR.save does not guarantee that the states - // of the subranges are also saved. - static if (isForwardRange!RoR && - isForwardRange!(ElementType!RoR)) - _current = _items.front.save; - else - _current = _items.front; - } - } - - private void useSeparator() - { - // Separator must always come after an item. - assert(_currentSep.empty, - "Attempting to reset a non-empty separator"); - assert(!_items.empty, - "Attempting to use a separator in an empty joiner"); - _items.popFront(); - - // If there are no more items, we're done, since separators are not - // terminators. - if (_items.empty) return; - - if (_currentSep._sep.empty) - { - // Advance to the next range in the - // input - while (_items.front.empty) - { - _items.popFront(); - if (_items.empty) return; - } - setItem; - } - else - { - _currentSep.reset; - assert(!_currentSep.empty, "separator must not be empty"); - } - } - - this(RoR items, Separator sep) - { - _items = items; - _currentSep.initialize(sep); - static if (isBidirectional) - _currentBackSep.initialize(sep); - - //mixin(useItem); // _current should be initialized in place - if (_items.empty) - { - _current = _current.init; // set invalid state - static if (isBidirectional) - _currentBack = _currentBack.init; - } - else - { - // If we're exporting .save, we must not consume any of the - // subranges, since RoR.save does not guarantee that the states - // of the subranges are also saved. - static if (isForwardRange!RoR && - isForwardRange!(ElementType!RoR)) - _current = _items.front.save; - else - _current = _items.front; - - static if (isBidirectional) - { - _currentBack = _items.back.save; - - if (_currentBack.empty) - { - // No data in the currentBack item - toggle to use - // the separator - inputEndsWithEmpty = true; - } - } - - if (_current.empty) - { - // No data in the current item - toggle to use the separator - inputStartsWithEmpty = true; - - // If RoR contains a single empty element, - // the returned Result will always be empty - import std.range : dropOne; - static if (hasLength!RoR) - { - if (_items.length == 1) - _items.popFront; - } - else static if (isForwardRange!RoR) - { - if (_items.save.dropOne.empty) - _items.popFront; - } - else - { - auto _itemsCopy = _items; - if (_itemsCopy.dropOne.empty) - _items.popFront; - } - } - } - } - - @property auto empty() - { - return _items.empty; - } - - //no data in the first item of the initial range - use the separator - private enum useSepIfFrontIsEmpty = q{ - if (inputStartsWithEmpty) - { - useSeparator(); - inputStartsWithEmpty = false; - } - }; - - @property ElementType!(ElementType!RoR) front() - { - mixin(useSepIfFrontIsEmpty); - if (!_currentSep.empty) return _currentSep.front; - assert(!_current.empty, "Attempting to fetch the front of an empty joiner."); - return _current.front; - } - - void popFront() - { - assert(!_items.empty, "Attempting to popFront an empty joiner."); - // Using separator? - mixin(useSepIfFrontIsEmpty); - - if (!_currentSep.empty) - { - _currentSep.popFront(); - if (_currentSep.empty && !_items.empty) - { - setItem; - if (_current.empty) - { - // No data in the current item - toggle to use the separator - useSeparator(); - } - } - } - else - { - // we're using the range - _current.popFront(); - if (_current.empty) - useSeparator(); - } - } - - static if (isForwardRange!RoR && isForwardRange!(ElementType!RoR)) - { - @property auto save() - { - Result copy = this; - copy._items = _items.save; - copy._current = _current.save; - copy._currentSep = _currentSep.save; - static if (isBidirectional) - { - copy._currentBack = _currentBack; - copy._currentBackSep = _currentBackSep; - } - return copy; - } - } - - static if (isBidirectional) - { - //no data in the last item of the initial range - use the separator - private enum useSepIfBackIsEmpty = q{ - if (inputEndsWithEmpty) - { - useBackSeparator; - inputEndsWithEmpty = false; - } - }; - - private void setBackItem() - { - if (!_items.empty) - { - _currentBack = _items.back.save; - } - } - - private void useBackSeparator() - { - // Separator must always come after an item. - assert(_currentBackSep.empty, - "Attempting to reset a non-empty separator"); - assert(!_items.empty, - "Attempting to use a separator in an empty joiner"); - _items.popBack(); - - // If there are no more items, we're done, since separators are not - // terminators. - if (_items.empty) return; - - if (_currentBackSep._sep.empty) - { - // Advance to the next range in the - // input - while (_items.back.empty) - { - _items.popBack(); - if (_items.empty) return; - } - setBackItem; - } - else - { - _currentBackSep.reset; - assert(!_currentBackSep.empty, "separator must not be empty"); - } - } - - @property ElementType!(ElementType!RoR) back() - { - mixin(useSepIfBackIsEmpty); - - if (!_currentBackSep.empty) return _currentBackSep.front; - assert(!_currentBack.empty, "Attempting to fetch the back of an empty joiner."); - return _currentBack.back; - } - - void popBack() - { - assert(!_items.empty, "Attempting to popBack an empty joiner."); - - mixin(useSepIfBackIsEmpty); - - if (!_currentBackSep.empty) - { - _currentBackSep.popFront(); - if (_currentBackSep.empty && !_items.empty) - { - setBackItem; - if (_currentBack.empty) - { - // No data in the current item - toggle to use the separator - useBackSeparator(); - } - } - } - else - { - // we're using the range - _currentBack.popBack(); - if (_currentBack.empty) - useBackSeparator(); - } - } - } - } - return Result(r, sep); -} - -/// -@safe unittest -{ - import std.algorithm.comparison : equal; - import std.conv : text; - - assert(["abc", "def"].joiner.equal("abcdef")); - assert(["Mary", "has", "a", "little", "lamb"] - .joiner("...") - .equal("Mary...has...a...little...lamb")); - assert(["", "abc"].joiner("xyz").equal("xyzabc")); - assert([""].joiner("xyz").equal("")); - assert(["", ""].joiner("xyz").equal("xyz")); -} - -@safe pure nothrow unittest -{ - //joiner with separator can return a bidirectional range - assert(isBidirectionalRange!(typeof(["abc", "def"].joiner("...")))); -} - -@system unittest -{ - import std.algorithm.comparison : equal; - import std.range.interfaces; - import std.range.primitives; - // joiner() should work for non-forward ranges too. - auto r = inputRangeObject(["abc", "def"]); - assert(equal(joiner(r, "xyz"), "abcxyzdef")); -} - -@system unittest -{ - import std.algorithm.comparison : equal; - import std.range; - - // Related to https://issues.dlang.org/show_bug.cgi?id=8061 - auto r = joiner([ - inputRangeObject("abc"), - inputRangeObject("def"), - ], "-*-"); - - assert(equal(r, "abc-*-def")); - - // Test case where separator is specified but is empty. - auto s = joiner([ - inputRangeObject("abc"), - inputRangeObject("def"), - ], ""); - - assert(equal(s, "abcdef")); - - // Test empty separator with some empty elements - auto t = joiner([ - inputRangeObject("abc"), - inputRangeObject(""), - inputRangeObject("def"), - inputRangeObject(""), - ], ""); - - assert(equal(t, "abcdef")); - - // Test empty elements with non-empty separator - auto u = joiner([ - inputRangeObject(""), - inputRangeObject("abc"), - inputRangeObject(""), - inputRangeObject("def"), - inputRangeObject(""), - ], "+-"); - - assert(equal(u, "+-abc+-+-def+-")); - - // https://issues.dlang.org/show_bug.cgi?id=13441: only(x) as separator - string[][] lines = [null]; - lines - .joiner(only("b")) - .array(); -} - -@safe unittest -{ - import std.algorithm.comparison : equal; - - // Transience correctness test - struct TransientRange - { - @safe: - int[][] src; - int[] buf; - - this(int[][] _src) - { - src = _src; - buf.length = 100; - } - @property bool empty() { return src.empty; } - @property int[] front() - { - assert(src.front.length <= buf.length); - buf[0 .. src.front.length] = src.front[0..$]; - return buf[0 .. src.front.length]; - } - void popFront() { src.popFront(); } - } - - // Test embedded empty elements - auto tr1 = TransientRange([[], [1,2,3], [], [4]]); - assert(equal(joiner(tr1, [0]), [0,1,2,3,0,0,4])); - - // Test trailing empty elements - auto tr2 = TransientRange([[], [1,2,3], []]); - assert(equal(joiner(tr2, [0]), [0,1,2,3,0])); - - // Test no empty elements - auto tr3 = TransientRange([[1,2], [3,4]]); - assert(equal(joiner(tr3, [0,1]), [1,2,0,1,3,4])); - - // Test consecutive empty elements - auto tr4 = TransientRange([[1,2], [], [], [], [3,4]]); - assert(equal(joiner(tr4, [0,1]), [1,2,0,1,0,1,0,1,0,1,3,4])); - - // Test consecutive trailing empty elements - auto tr5 = TransientRange([[1,2], [3,4], [], []]); - assert(equal(joiner(tr5, [0,1]), [1,2,0,1,3,4,0,1,0,1])); -} - -@safe unittest -{ - static assert(isInputRange!(typeof(joiner([""], "")))); - static assert(isForwardRange!(typeof(joiner([""], "")))); -} - -@safe pure unittest -{ - { - import std.algorithm.comparison : equal; - auto r = joiner(["abc", "def", "ghi"], "?!"); - char[] res; - while (!r.empty) - { - res ~= r.back; - r.popBack; - } - assert(res.equal("ihg?!fed?!cba")); - } - - { - wchar[] sep = ['Ș', 'Ț']; - auto r = joiner(["","abc",""],sep); - wchar[] resFront; - wchar[] resBack; - - auto rCopy = r.save; - while (!r.empty) - { - resFront ~= r.front; - r.popFront; - } - - while (!rCopy.empty) - { - resBack ~= rCopy.back; - rCopy.popBack; - } - - import std.algorithm.comparison : equal; - - assert(resFront.equal("ȘȚabcȘȚ")); - assert(resBack.equal("ȘȚcbaȘȚ")); - } - - { - import std.algorithm.comparison : equal; - auto r = [""]; - r.popBack; - assert(r.joiner("AB").equal("")); - } - - { - auto r = ["", "", "", "abc", ""].joiner("../"); - auto rCopy = r.save; - - char[] resFront; - char[] resBack; - - while (!r.empty) - { - resFront ~= r.front; - r.popFront; - } - - while (!rCopy.empty) - { - resBack ~= rCopy.back; - rCopy.popBack; - } - - import std.algorithm.comparison : equal; - - assert(resFront.equal("../../../abc../")); - assert(resBack.equal("../cba../../../")); - } - - { - auto r = ["", "abc", ""].joiner("./"); - auto rCopy = r.save; - r.popBack; - rCopy.popFront; - - auto rRev = r.save; - auto rCopyRev = rCopy.save; - - char[] r1, r2, r3, r4; - - while (!r.empty) - { - r1 ~= r.back; - r.popBack; - } - - while (!rCopy.empty) - { - r2 ~= rCopy.front; - rCopy.popFront; - } - - while (!rRev.empty) - { - r3 ~= rRev.front; - rRev.popFront; - } - - while (!rCopyRev.empty) - { - r4 ~= rCopyRev.back; - rCopyRev.popBack; - } - - import std.algorithm.comparison : equal; - - assert(r1.equal("/cba./")); - assert(r2.equal("/abc./")); - assert(r3.equal("./abc")); - assert(r4.equal("./cba")); - } -} - -@system unittest -{ - import std.range; - import std.algorithm.comparison : equal; - - assert(inputRangeObject([""]).joiner("lz").equal("")); -} - -@safe pure unittest -{ - struct inputRangeStrings - { - private string[] strings; - - string front() - { - return strings[0]; - } - - void popFront() - { - strings = strings[1..$]; - } - - bool empty() const - { - return strings.length == 0; - } - } - - auto arr = inputRangeStrings([""]); - - import std.algorithm.comparison : equal; - - assert(arr.joiner("./").equal("")); -} - -@safe pure unittest -{ - auto r = joiner(["", "", "abc", "", ""], ""); - char[] res; - while (!r.empty) - { - res ~= r.back; - r.popBack; - } - - import std.algorithm.comparison : equal; - - assert(res.equal("cba")); -} - -/// Ditto -auto joiner(RoR)(RoR r) -if (isInputRange!RoR && isInputRange!(Unqual!(ElementType!RoR))) -{ - static struct Result - { - private: - RoR _items; - Unqual!(ElementType!RoR) _current; - enum isBidirectional = isForwardRange!RoR && isForwardRange!(ElementType!RoR) && - isBidirectionalRange!RoR && isBidirectionalRange!(ElementType!RoR); - static if (isBidirectional) - { - Unqual!(ElementType!RoR) _currentBack; - bool reachedFinalElement; - } - - this(RoR items, ElementType!RoR current) - { - _items = items; - _current = current; - static if (isBidirectional && hasNested!Result) - _currentBack = typeof(_currentBack).init; - } - - void replaceCurrent(typeof(_current) current) @trusted - { - import core.lifetime : move; - - current.move(_current); - } - - static if (isBidirectional) - { - void replaceCurrentBack(typeof(_currentBack) currentBack) @trusted - { - import core.lifetime : move; - - currentBack.move(_currentBack); - } - } - - public: - this(RoR r) - { - _items = r; - // field _current must be initialized in constructor, because it is nested struct - _current = typeof(_current).init; - - static if (isBidirectional && hasNested!Result) - _currentBack = typeof(_currentBack).init; - mixin(popFrontEmptyElements); - static if (isBidirectional) - mixin(popBackEmptyElements); - } - static if (isInfinite!RoR) - { - enum bool empty = false; - } - else - { - @property auto empty() - { - return _items.empty; - } - } - @property auto ref front() - { - assert(!empty, "Attempting to fetch the front of an empty joiner."); - return _current.front; - } - void popFront() - { - assert(!_current.empty, "Attempting to popFront an empty joiner."); - _current.popFront(); - if (_current.empty) - { - assert(!_items.empty, "Attempting to popFront an empty joiner."); - _items.popFront(); - mixin(popFrontEmptyElements); - } - } - - private enum popFrontEmptyElements = q{ - // Skip over empty subranges. - while (!_items.empty && _items.front.empty) - { - _items.popFront(); - } - if (!_items.empty) - { - // We cannot export .save method unless we ensure subranges are not - // consumed when a .save'd copy of ourselves is iterated over. So - // we need to .save each subrange we traverse. - static if (isForwardRange!RoR && isForwardRange!(ElementType!RoR)) - replaceCurrent(_items.front.save); - else - replaceCurrent(_items.front); - } - else - { - replaceCurrent(typeof(_current).init); - } - }; - - static if (isForwardRange!RoR && isForwardRange!(ElementType!RoR)) - { - @property auto save() - { - // the null check is important if it is a class range, since null.save will segfault; issue #22359 - // could not just compare x is y here without static if due to a compiler assertion failure - static if (is(typeof(null) : typeof(_current))) - auto r = Result(_items.save, _current is null ? null : _current.save); - else - auto r = Result(_items.save, _current.save); - static if (isBidirectional) - { - static if (is(typeof(null) : typeof(_currentBack))) - r.replaceCurrentBack(_currentBack is null ? null : _currentBack.save); - else - r.replaceCurrentBack(_currentBack.save); - r.reachedFinalElement = reachedFinalElement; - } - return r; - } - } - - static if (hasAssignableElements!(ElementType!RoR)) - { - @property void front(ElementType!(ElementType!RoR) element) - { - assert(!empty, "Attempting to assign to front of an empty joiner."); - _current.front = element; - } - - @property void front(ref ElementType!(ElementType!RoR) element) - { - assert(!empty, "Attempting to assign to front of an empty joiner."); - _current.front = element; - } - } - - static if (isBidirectional) - { - bool checkFinalElement() - { - import std.range : dropOne; - - if (reachedFinalElement) - return true; - - static if (hasLength!(typeof(_items))) - { - if (_items.length == 1) - reachedFinalElement = true; - } - else - { - if (_items.save.dropOne.empty) - reachedFinalElement = true; - } - - return false; - } - - @property auto ref back() - { - assert(!empty, "Attempting to fetch the back of an empty joiner."); - if (reachedFinalElement) - return _current.back; - else - return _currentBack.back; - } - - void popBack() - { - assert(!_current.empty, "Attempting to popBack an empty joiner."); - if (checkFinalElement) - _current.popBack(); - else - _currentBack.popBack(); - - bool isEmpty = reachedFinalElement ? _current.empty : _currentBack.empty; - if (isEmpty) - { - assert(!_items.empty, "Attempting to popBack an empty joiner."); - _items.popBack(); - mixin(popBackEmptyElements); - } - } - - private enum popBackEmptyElements = q{ - // Skip over empty subranges. - while (!_items.empty && _items.back.empty) - { - _items.popBack(); - } - if (!_items.empty) - { - checkFinalElement; - // We cannot export .save method unless we ensure subranges are not - // consumed when a .save'd copy of ourselves is iterated over. So - // we need to .save each subrange we traverse. - static if (isForwardRange!RoR && isForwardRange!(ElementType!RoR)) - { - if (reachedFinalElement) - replaceCurrent(_items.back.save); - else - replaceCurrentBack(_items.back.save); - } - else - { - if (reachedFinalElement) - replaceCurrent(_items.back); - else - replaceCurrentBack(_items.back); - } - } - else - { - replaceCurrent(typeof(_current).init); - replaceCurrentBack(typeof(_currentBack).init); - } - }; - - static if (hasAssignableElements!(ElementType!RoR)) - { - @property void back(ElementType!(ElementType!RoR) element) - { - assert(!empty, "Attempting to assign to back of an empty joiner."); - if (reachedFinalElement) - _current.back = element; - else - _currentBack.back = element; - } - - @property void back(ref ElementType!(ElementType!RoR) element) - { - assert(!empty, "Attempting to assign to back of an empty joiner."); - if (reachedFinalElement) - _current.back = element; - else - _currentBack.back = element; - } - } - } - } - return Result(r); -} - -/// -@safe unittest -{ - import std.algorithm.comparison : equal; - import std.range : repeat; - - assert([""].joiner.equal("")); - assert(["", ""].joiner.equal("")); - assert(["", "abc"].joiner.equal("abc")); - assert(["abc", ""].joiner.equal("abc")); - assert(["abc", "def"].joiner.equal("abcdef")); - assert(["Mary", "has", "a", "little", "lamb"].joiner.equal("Maryhasalittlelamb")); - assert("abc".repeat(3).joiner.equal("abcabcabc")); -} - -/// joiner allows in-place mutation! -@safe unittest -{ - import std.algorithm.comparison : equal; - auto a = [ [1, 2, 3], [42, 43] ]; - auto j = joiner(a); - j.front = 44; - assert(a == [ [44, 2, 3], [42, 43] ]); - assert(equal(j, [44, 2, 3, 42, 43])); -} - -/// insert characters fully lazily into a string -@safe pure unittest -{ - import std.algorithm.comparison : equal; - import std.range : chain, cycle, iota, only, retro, take, zip; - import std.format : format; - - static immutable number = "12345678"; - static immutable delimiter = ","; - auto formatted = number.retro - .zip(3.iota.cycle.take(number.length)) - .map!(z => chain(z[0].only, z[1] == 2 ? delimiter : null)) - .joiner - .retro; - static immutable expected = "12,345,678"; - assert(formatted.equal(expected)); -} - -@safe unittest -{ - import std.range.interfaces : inputRangeObject; - static assert(isInputRange!(typeof(joiner([""])))); - static assert(isForwardRange!(typeof(joiner([""])))); -} - -@system unittest -{ - // this test is system because the virtual interface call to save - // is flexible and thus cannot be inferred safe automatically - - // https://issues.dlang.org/show_bug.cgi?id=22359 - import std.range; - ForwardRange!int bug(int[][] r) - { - import std.range : inputRangeObject; - import std.algorithm.iteration : map, joiner; - - auto range = inputRangeObject(r); - - return range.map!(a =>inputRangeObject(a)).joiner.inputRangeObject; - } - auto f = bug([[]]); - f.save(); // should not segfault -} - -@safe unittest -{ - // Initial version of PR #6115 caused a compilation failure for - // https://github.com/BlackEdder/ggplotd/blob/d4428c08db5ffdc05dfd29690bf7da9073ea1dc5/source/ggplotd/stat.d#L562-L583 - import std.range : zip; - int[] xCoords = [1, 2, 3]; - int[] yCoords = [4, 5, 6]; - auto coords = zip(xCoords, xCoords[1..$]).map!( (xr) { - return zip(yCoords, yCoords[1..$]).map!( (yr) { - return [ - [[xr[0], xr[0], xr[1]], - [yr[0], yr[1], yr[1]]], - [[xr[0], xr[1], xr[1]], - [yr[0], yr[0], yr[1]]] - ]; - }).joiner; - }).joiner; -} - -@system unittest -{ - import std.algorithm.comparison : equal; - import std.range.interfaces : inputRangeObject; - import std.range : retro; - - // https://issues.dlang.org/show_bug.cgi?id=8240 - assert(equal(joiner([inputRangeObject("")]), "")); - assert(equal(joiner([inputRangeObject("")]).retro, "")); - - // https://issues.dlang.org/show_bug.cgi?id=8792 - auto b = [[1], [2], [3]]; - auto jb = joiner(b); - auto js = jb.save; - assert(equal(jb, js)); - - auto js2 = jb.save; - jb.popFront(); - assert(!equal(jb, js)); - assert(equal(js2, js)); - js.popFront(); - assert(equal(jb, js)); - assert(!equal(js2, js)); -} - -// https://issues.dlang.org/show_bug.cgi?id=19213 -@system unittest -{ - auto results = [[1,2], [3,4]].map!(q => q.chunkBy!"a").joiner; - int i = 1; - foreach (ref e; results) - assert(e[0] == i++); -} - -/// joiner can be bidirectional -@safe unittest -{ - import std.algorithm.comparison : equal; - import std.range : retro; - - auto a = [[1, 2, 3], [4, 5]]; - auto j = a.joiner; - j.back = 44; - assert(a == [[1, 2, 3], [4, 44]]); - assert(equal(j.retro, [44, 4, 3, 2, 1])); -} - -// bidirectional joiner: test for filtering empty elements -@safe unittest -{ - import std.algorithm.comparison : equal; - import std.range : retro; - - alias El = (e) => new int(e); - auto a = [null, [null, El(1), null, El(2), null, El(3), null], null, [null, El(4), null, El(5), null]]; - auto j = a.joiner; - - alias deref = a => a is null ? -1 : *a; - auto expected = [-1, 5, -1, 4, -1, -1, 3, -1, 2, -1, 1, -1]; - // works with .save. - assert(j.save.retro.map!deref.equal(expected)); - // and without .save - assert(j.retro.map!deref.equal(expected)); - assert(j.retro.map!deref.equal(expected)); -} - -// bidirectional joiner is @nogc -@safe @nogc unittest -{ - import std.algorithm.comparison : equal; - import std.range : iota, only, retro; - - auto a = only(iota(1, 4), iota(4, 6)); - auto j = a.joiner; - static immutable expected = [5 , 4, 3, 2, 1]; - assert(equal(j.retro, expected)); -} - -// bidirectional joiner supports assignment to the back -@safe unittest -{ - import std.algorithm.comparison : equal; - import std.range : popBackN; - - auto a = [[1, 2, 3], [4, 5]]; - auto j = a.joiner; - j.back = 55; - assert(a == [[1, 2, 3], [4, 55]]); - j.popBackN(2); - j.back = 33; - assert(a == [[1, 2, 33], [4, 55]]); -} - -// bidirectional joiner works with auto-decoding -@safe unittest -{ - import std.algorithm.comparison : equal; - import std.range : retro; - - auto a = ["😀😐", "😠"]; - auto j = a.joiner; - assert(j.retro.equal("😠😐😀")); -} - -// test two-side iteration -@safe unittest -{ - import std.algorithm.comparison : equal; - import std.range : popBackN; - - auto arrs = [ - [[1], [2], [3], [4], [5]], - [[1], [2, 3, 4], [5]], - [[1, 2, 3, 4, 5]], - ]; - foreach (arr; arrs) - { - auto a = arr.joiner; - assert(a.front == 1); - assert(a.back == 5); - a.popFront; - assert(a.front == 2); - assert(a.back == 5); - a.popBack; - assert(a.front == 2); - assert(a.back == 4); - a.popFront; - assert(a.front == 3); - assert(a.back == 4); - a.popBack; - assert(a.front == 3); - assert(a.back == 3); - a.popBack; - assert(a.empty); - } -} - -@safe unittest -{ - import std.algorithm.comparison : equal; - - struct TransientRange - { - @safe: - int[] _buf; - int[][] _values; - this(int[][] values) - { - _values = values; - _buf = new int[128]; - } - @property bool empty() - { - return _values.length == 0; - } - @property auto front() - { - foreach (i; 0 .. _values.front.length) - { - _buf[i] = _values[0][i]; - } - return _buf[0 .. _values.front.length]; - } - void popFront() - { - _values = _values[1 .. $]; - } - } - - auto rr = TransientRange([[1,2], [3,4,5], [], [6,7]]); - - // Can't use array() or equal() directly because they fail with transient - // .front. - int[] result; - foreach (c; rr.joiner()) - { - result ~= c; - } - - assert(equal(result, [1,2,3,4,5,6,7])); -} - -@safe unittest -{ - import std.algorithm.comparison : equal; - import std.algorithm.internal : algoFormat; - - struct TransientRange - { - @safe: - dchar[] _buf; - dstring[] _values; - this(dstring[] values) - { - _buf.length = 128; - _values = values; - } - @property bool empty() - { - return _values.length == 0; - } - @property auto front() - { - foreach (i; 0 .. _values.front.length) - { - _buf[i] = _values[0][i]; - } - return _buf[0 .. _values.front.length]; - } - void popFront() - { - _values = _values[1 .. $]; - } - } - - auto rr = TransientRange(["abc"d, "12"d, "def"d, "34"d]); - - // Can't use array() or equal() directly because they fail with transient - // .front. - dchar[] result; - foreach (c; rr.joiner()) - { - result ~= c; - } - - import std.conv : to; - assert(equal(result, "abc12def34"d), - //Convert to string for assert's message - to!string("Unexpected result: '%s'"d.algoFormat(result))); -} - -// https://issues.dlang.org/show_bug.cgi?id=8061 -@system unittest -{ - import std.conv : to; - import std.range.interfaces; - - auto r = joiner([inputRangeObject("ab"), inputRangeObject("cd")]); - assert(isForwardRange!(typeof(r))); - - auto str = to!string(r); - assert(str == "abcd"); -} - -@safe unittest -{ - import std.range : repeat; - - class AssignableRange - { - @safe: - int element; - @property int front() - { - return element; - } - alias back = front; - - enum empty = false; - - auto save() - { - return this; - } - - void popFront() {} - alias popBack = popFront; - - @property void front(int newValue) - { - element = newValue; - } - alias back = front; - } - - static assert(isInputRange!AssignableRange); - static assert(is(ElementType!AssignableRange == int)); - static assert(hasAssignableElements!AssignableRange); - static assert(!hasLvalueElements!AssignableRange); - - auto range = new AssignableRange(); - assert(range.element == 0); - { - auto joined = joiner(repeat(range)); - joined.front = 5; - assert(range.element == 5); - assert(joined.front == 5); - - joined.popFront; - int byRef = 7; - joined.front = byRef; - assert(range.element == byRef); - assert(joined.front == byRef); - } - { - auto joined = joiner(repeat(range)); - joined.back = 5; - assert(range.element == 5); - assert(joined.back == 5); - - joined.popBack; - int byRef = 7; - joined.back = byRef; - assert(range.element == byRef); - assert(joined.back == byRef); - } -} - -// https://issues.dlang.org/show_bug.cgi?id=19850 -@safe pure unittest -{ - assert([[0]].joiner.save.back == 0); -} - -// https://issues.dlang.org/show_bug.cgi?id=22561 -@safe pure unittest -{ - import std.range : only; - - static immutable struct S { int[] array; } - assert([only(S(null))].joiner.front == S(null)); -} - -// https://issues.dlang.org/show_bug.cgi?id=22785 -@safe unittest -{ - - import std.algorithm.iteration : joiner, map; - import std.array : array; - - static immutable struct S - { - int value; - } - - static immutable struct T - { - S[] arr; - } - - auto range = [T([S(3)]), T([S(4), S(5)])]; - - assert(range.map!"a.arr".joiner.array == [S(3), S(4), S(5)]); -} - -/++ -Implements the homonym function (also known as `accumulate`, $(D -compress), `inject`, or `foldl`) present in various programming -languages of functional flavor. There is also $(LREF fold) which does -the same thing but with the opposite parameter order. -The call `reduce!(fun)(seed, range)` first assigns `seed` to -an internal variable `result`, also called the accumulator. -Then, for each element `x` in `range`, `result = fun(result, x)` -gets evaluated. Finally, `result` is returned. -The one-argument version `reduce!(fun)(range)` -works similarly, but it uses the first element of the range as the -seed (the range must be non-empty). - -Returns: - the accumulated `result` - -Params: - fun = one or more functions - -See_Also: - $(HTTP en.wikipedia.org/wiki/Fold_(higher-order_function), Fold (higher-order function)) - - $(LREF fold) is functionally equivalent to $(LREF _reduce) with the argument - order reversed, and without the need to use $(REF_ALTTEXT `tuple`,tuple,std,typecons) - for multiple seeds. This makes it easier to use in UFCS chains. - - $(LREF sum) is similar to `reduce!((a, b) => a + b)` that offers - pairwise summing of floating point numbers. -+/ -template reduce(fun...) -if (fun.length >= 1) -{ - import std.meta : staticMap; - - alias binfuns = staticMap!(binaryFun, fun); - static if (fun.length > 1) - import std.typecons : tuple, isTuple; - - /++ - No-seed version. The first element of `r` is used as the seed's value. - - For each function `f` in `fun`, the corresponding - seed type `S` is `Unqual!(typeof(f(e, e)))`, where `e` is an - element of `r`: `ElementType!R` for ranges, - and `ForeachType!R` otherwise. - - Once S has been determined, then `S s = e;` and `s = f(s, e);` - must both be legal. - - Params: - r = an iterable value as defined by `isIterable` - - Returns: - the final result of the accumulator applied to the iterable - - Throws: `Exception` if `r` is empty - +/ - auto reduce(R)(R r) - if (isIterable!R) - { - import std.exception : enforce; - alias E = Select!(isInputRange!R, ElementType!R, ForeachType!R); - alias Args = staticMap!(ReduceSeedType!E, binfuns); - - static if (isInputRange!R) - { - // no need to throw if range is statically known to be non-empty - static if (!__traits(compiles, - { - static assert(r.length > 0); - })) - enforce(!r.empty, "Cannot reduce an empty input range w/o an explicit seed value."); - - Args result = r.front; - r.popFront(); - return reduceImpl!false(r, result); - } - else - { - auto result = Args.init; - return reduceImpl!true(r, result); - } - } - - /++ - Seed version. The seed should be a single value if `fun` is a - single function. If `fun` is multiple functions, then `seed` - should be a $(REF Tuple, std,typecons), with one field per function in `f`. - - For convenience, if the seed is const, or has qualified fields, then - `reduce` will operate on an unqualified copy. If this happens - then the returned type will not perfectly match `S`. - - Use `fold` instead of `reduce` to use the seed version in a UFCS chain. - - Params: - seed = the initial value of the accumulator - r = an iterable value as defined by `isIterable` - - Returns: - the final result of the accumulator applied to the iterable - +/ - auto reduce(S, R)(S seed, R r) - if (isIterable!R) - { - static if (fun.length == 1) - return reducePreImpl(r, seed); - else - { - import std.algorithm.internal : algoFormat; - static assert(isTuple!S, algoFormat("Seed %s should be a Tuple", S.stringof)); - return reducePreImpl(r, seed.expand); - } - } - - private auto reducePreImpl(R, Args...)(R r, ref Args args) - { - alias Result = staticMap!(Unqual, Args); - static if (is(Result == Args)) - alias result = args; - else - Result result = args; - return reduceImpl!false(r, result); - } - - private auto reduceImpl(bool mustInitialize, R, Args...)(R r, ref Args args) - if (isIterable!R) - { - import std.algorithm.internal : algoFormat; - static assert(Args.length == fun.length, - algoFormat("Seed %s does not have the correct amount of fields (should be %s)", Args.stringof, fun.length)); - alias E = Select!(isInputRange!R, ElementType!R, ForeachType!R); - - static if (mustInitialize) bool initialized = false; - foreach (/+auto ref+/ E e; r) // https://issues.dlang.org/show_bug.cgi?id=4707 - { - foreach (i, f; binfuns) - { - static assert(!is(typeof(f(args[i], e))) || is(typeof(args[i] = f(args[i], e))), - algoFormat( - "Incompatible function/seed/element: %s/%s/%s", - fullyQualifiedName!f, - Args[i].stringof, - E.stringof - ) - ); - } - - static if (mustInitialize) if (initialized == false) - { - import core.internal.lifetime : emplaceRef; - foreach (i, f; binfuns) - emplaceRef!(Args[i])(args[i], e); - initialized = true; - continue; - } - - foreach (i, f; binfuns) - args[i] = f(args[i], e); - } - static if (mustInitialize) - // no need to throw if range is statically known to be non-empty - static if (!__traits(compiles, - { - static assert(r.length > 0); - })) - { - if (!initialized) - throw new Exception("Cannot reduce an empty iterable w/o an explicit seed value."); - } - - static if (Args.length == 1) - return args[0]; - else - return tuple(args); - } -} - -/** -Many aggregate range operations turn out to be solved with `reduce` -quickly and easily. The example below illustrates `reduce`'s -remarkable power and flexibility. -*/ -@safe unittest -{ - import std.algorithm.comparison : max, min; - import std.math.operations : isClose; - import std.range; - - int[] arr = [ 1, 2, 3, 4, 5 ]; - // Sum all elements - auto sum = reduce!((a,b) => a + b)(0, arr); - assert(sum == 15); - - // Sum again, using a string predicate with "a" and "b" - sum = reduce!"a + b"(0, arr); - assert(sum == 15); - - // Compute the maximum of all elements - auto largest = reduce!(max)(arr); - assert(largest == 5); - - // Max again, but with Uniform Function Call Syntax (UFCS) - largest = arr.reduce!(max); - assert(largest == 5); - - // Compute the number of odd elements - auto odds = reduce!((a,b) => a + (b & 1))(0, arr); - assert(odds == 3); - - // Compute the sum of squares - auto ssquares = reduce!((a,b) => a + b * b)(0, arr); - assert(ssquares == 55); - - // Chain multiple ranges into seed - int[] a = [ 3, 4 ]; - int[] b = [ 100 ]; - auto r = reduce!("a + b")(chain(a, b)); - assert(r == 107); - - // Mixing convertible types is fair game, too - double[] c = [ 2.5, 3.0 ]; - auto r1 = reduce!("a + b")(chain(a, b, c)); - assert(isClose(r1, 112.5)); - - // To minimize nesting of parentheses, Uniform Function Call Syntax can be used - auto r2 = chain(a, b, c).reduce!("a + b"); - assert(isClose(r2, 112.5)); -} - -/** -Sometimes it is very useful to compute multiple aggregates in one pass. -One advantage is that the computation is faster because the looping overhead -is shared. That's why `reduce` accepts multiple functions. -If two or more functions are passed, `reduce` returns a -$(REF Tuple, std,typecons) object with one member per passed-in function. -The number of seeds must be correspondingly increased. -*/ -@safe unittest -{ - import std.algorithm.comparison : max, min; - import std.math.operations : isClose; - import std.math.algebraic : sqrt; - import std.typecons : tuple, Tuple; - - double[] a = [ 3.0, 4, 7, 11, 3, 2, 5 ]; - // Compute minimum and maximum in one pass - auto r = reduce!(min, max)(a); - // The type of r is Tuple!(int, int) - assert(isClose(r[0], 2)); // minimum - assert(isClose(r[1], 11)); // maximum - - // Compute sum and sum of squares in one pass - r = reduce!("a + b", "a + b * b")(tuple(0.0, 0.0), a); - assert(isClose(r[0], 35)); // sum - assert(isClose(r[1], 233)); // sum of squares - // Compute average and standard deviation from the above - auto avg = r[0] / a.length; - assert(avg == 5); - auto stdev = sqrt(r[1] / a.length - avg * avg); - assert(cast(int) stdev == 2); -} - -@safe unittest -{ - import std.algorithm.comparison : max, min; - import std.range : chain; - import std.typecons : tuple, Tuple; - - double[] a = [ 3, 4 ]; - auto r = reduce!("a + b")(0.0, a); - assert(r == 7); - r = reduce!("a + b")(a); - assert(r == 7); - r = reduce!(min)(a); - assert(r == 3); - double[] b = [ 100 ]; - auto r1 = reduce!("a + b")(chain(a, b)); - assert(r1 == 107); - - // two funs - auto r2 = reduce!("a + b", "a - b")(tuple(0.0, 0.0), a); - assert(r2[0] == 7 && r2[1] == -7); - auto r3 = reduce!("a + b", "a - b")(a); - assert(r3[0] == 7 && r3[1] == -1); - - a = [ 1, 2, 3, 4, 5 ]; - // Stringize with commas - string rep = reduce!("a ~ `, ` ~ to!(string)(b)")("", a); - assert(rep[2 .. $] == "1, 2, 3, 4, 5", "["~rep[2 .. $]~"]"); -} - -@safe unittest -{ - import std.algorithm.comparison : max, min; - import std.exception : assertThrown; - import std.range : iota; - import std.typecons : tuple, Tuple; - - // Test the opApply case. - static struct OpApply - { - bool actEmpty; - - int opApply(scope int delegate(ref int) @safe dg) - { - int res; - if (actEmpty) return res; - - foreach (i; 0 .. 100) - { - res = dg(i); - if (res) break; - } - return res; - } - } - - OpApply oa; - auto hundredSum = reduce!"a + b"(iota(100)); - assert(reduce!"a + b"(5, oa) == hundredSum + 5); - assert(reduce!"a + b"(oa) == hundredSum); - assert(reduce!("a + b", max)(oa) == tuple(hundredSum, 99)); - assert(reduce!("a + b", max)(tuple(5, 0), oa) == tuple(hundredSum + 5, 99)); - - // Test for throwing on empty range plus no seed. - assertThrown(reduce!"a + b"([1, 2][0 .. 0])); - - oa.actEmpty = true; - assertThrown(reduce!"a + b"(oa)); -} - -@safe unittest -{ - const float a = 0.0; - const float[] b = [ 1.2, 3, 3.3 ]; - float[] c = [ 1.2, 3, 3.3 ]; - auto r = reduce!"a + b"(a, b); - r = reduce!"a + b"(a, c); - assert(r == 7.5); -} - -@safe unittest -{ - // https://issues.dlang.org/show_bug.cgi?id=10408 - // Two-function reduce of a const array. - import std.algorithm.comparison : max, min; - import std.typecons : tuple, Tuple; - - const numbers = [10, 30, 20]; - immutable m = reduce!(min)(numbers); - assert(m == 10); - immutable minmax = reduce!(min, max)(numbers); - assert(minmax == tuple(10, 30)); -} - -@safe unittest -{ - // https://issues.dlang.org/show_bug.cgi?id=10709 - import std.typecons : tuple, Tuple; - - enum foo = "a + 0.5 * b"; - auto r = [0, 1, 2, 3]; - auto r1 = reduce!foo(r); - auto r2 = reduce!(foo, foo)(r); - assert(r1 == 3); - assert(r2 == tuple(3, 3)); -} - -@safe unittest -{ - static struct OpApply - { - int opApply(int delegate(ref int) @safe dg) - { - int[] a = [1, 2, 3]; - - int res = 0; - foreach (ref e; a) - { - res = dg(e); - if (res) break; - } - return res; - } - } - //test CTFE and functions with context - int fun(int a, int b) @safe {return a + b + 1;} - auto foo() - { - import std.algorithm.comparison : max; - import std.typecons : tuple, Tuple; - - auto a = reduce!(fun)([1, 2, 3]); - auto b = reduce!(fun, fun)([1, 2, 3]); - auto c = reduce!(fun)(0, [1, 2, 3]); - auto d = reduce!(fun, fun)(tuple(0, 0), [1, 2, 3]); - auto e = reduce!(fun)(0, OpApply()); - auto f = reduce!(fun, fun)(tuple(0, 0), OpApply()); - - return max(a, b.expand, c, d.expand, e, f.expand); - } - auto a = foo(); - assert(a == 9); - enum b = foo(); - assert(b == 9); -} - -@safe unittest -{ - import std.algorithm.comparison : max, min; - import std.typecons : tuple, Tuple; - - //http://forum.dlang.org/post/oghtttkopzjshsuflelk@forum.dlang.org - //Seed is tuple of const. - static auto minmaxElement(alias F = min, alias G = max, R)(in R range) - @safe pure nothrow - if (isInputRange!R) - { - return reduce!(F, G)(tuple(ElementType!R.max, - ElementType!R.min), range); - } - assert(minmaxElement([1, 2, 3]) == tuple(1, 3)); -} - -// https://issues.dlang.org/show_bug.cgi?id=12569 -@safe unittest -{ - import std.algorithm.comparison : max, min; - import std.typecons : tuple; - dchar c = 'a'; - reduce!(min, max)(tuple(c, c), "hello"); // OK - static assert(!is(typeof(reduce!(min, max)(tuple(c), "hello")))); - static assert(!is(typeof(reduce!(min, max)(tuple(c, c, c), "hello")))); - - - //"Seed dchar should be a Tuple" - static assert(!is(typeof(reduce!(min, max)(c, "hello")))); - //"Seed (dchar) does not have the correct amount of fields (should be 2)" - static assert(!is(typeof(reduce!(min, max)(tuple(c), "hello")))); - //"Seed (dchar, dchar, dchar) does not have the correct amount of fields (should be 2)" - static assert(!is(typeof(reduce!(min, max)(tuple(c, c, c), "hello")))); - //"Incompatible function/seed/element: all(alias pred = "a")/int/dchar" - static assert(!is(typeof(reduce!all(1, "hello")))); - static assert(!is(typeof(reduce!(all, all)(tuple(1, 1), "hello")))); -} - -// https://issues.dlang.org/show_bug.cgi?id=13304 -@safe unittest -{ - int[] data; - static assert(is(typeof(reduce!((a, b) => a + b)(data)))); - assert(data.length == 0); -} - -// https://issues.dlang.org/show_bug.cgi?id=13880 -// reduce shouldn't throw if the length is statically known -pure nothrow @safe @nogc unittest -{ - import std.algorithm.comparison : min; - int[5] arr; - arr[2] = -1; - assert(arr.reduce!min == -1); - - int[0] arr0; - assert(reduce!min(42, arr0) == 42); -} - -//Helper for Reduce -private template ReduceSeedType(E) -{ - static template ReduceSeedType(alias fun) - { - import std.algorithm.internal : algoFormat; - - alias ReduceSeedType = Unqual!(typeof(fun(lvalueOf!E, lvalueOf!E))); - - //Check the Seed type is useable. - ReduceSeedType s = ReduceSeedType.init; - static assert(is(typeof({ReduceSeedType s = lvalueOf!E;})) && - is(typeof(lvalueOf!ReduceSeedType = fun(lvalueOf!ReduceSeedType, lvalueOf!E))), - algoFormat( - "Unable to deduce an acceptable seed type for %s with element type %s.", - fullyQualifiedName!fun, - E.stringof - ) - ); - } -} - - -/++ -Implements the homonym function (also known as `accumulate`, $(D -compress), `inject`, or `foldl`) present in various programming -languages of functional flavor, iteratively calling one or more predicates. - -$(P Each predicate in `fun` must take two arguments:) -* An accumulator value -* An element of the range `r` -$(P Each predicate must return a value which implicitly converts to the -type of the accumulator.) - -$(P For a single predicate, -the call `fold!(fun)(range, seed)` will:) - -* Use `seed` to initialize an internal variable `result` (also called - the accumulator). -* For each element `e` in $(D range), evaluate `result = fun(result, e)`. -* Return $(D result). - -$(P The one-argument version `fold!(fun)(range)` -works similarly, but it uses the first element of the range as the -seed (the range must be non-empty) and iterates over the remaining -elements.) - -Multiple results are produced when using multiple predicates. - -Params: - fun = the predicate function(s) to apply to the elements - -See_Also: - * $(HTTP en.wikipedia.org/wiki/Fold_(higher-order_function), Fold (higher-order function)) - - * $(LREF sum) is similar to `fold!((a, b) => a + b)` that offers - precise summing of floating point numbers. - - * `fold` is functionally equivalent to $(LREF reduce) with the argument order - reversed, and without the need to use $(REF_ALTTEXT `tuple`,tuple,std,typecons) - for multiple seeds. -+/ -template fold(fun...) -if (fun.length >= 1) -{ - /** - Params: - r = the $(REF_ALTTEXT input range, isInputRange, std,range,primitives) to fold - seeds = the initial values of each accumulator (optional), one for each predicate - Returns: - Either the accumulated result for a single predicate, or a - $(REF_ALTTEXT `Tuple`,Tuple,std,typecons) of results. - */ - auto fold(R, S...)(R r, S seeds) - { - static if (S.length < 2) - { - return reduce!fun(seeds, r); - } - else - { - import std.typecons : tuple; - return reduce!fun(tuple(seeds), r); - } - } -} - -/// -@safe pure unittest -{ - immutable arr = [1, 2, 3, 4, 5]; - - // Sum all elements - assert(arr.fold!((a, e) => a + e) == 15); - - // Sum all elements with explicit seed - assert(arr.fold!((a, e) => a + e)(6) == 21); - - import std.algorithm.comparison : min, max; - import std.typecons : tuple; - - // Compute minimum and maximum at the same time - assert(arr.fold!(min, max) == tuple(1, 5)); - - // Compute minimum and maximum at the same time with seeds - assert(arr.fold!(min, max)(0, 7) == tuple(0, 7)); - - // Can be used in a UFCS chain - assert(arr.map!(a => a + 1).fold!((a, e) => a + e) == 20); - - // Return the last element of any range - assert(arr.fold!((a, e) => e) == 5); -} - -@safe @nogc pure nothrow unittest -{ - int[1] arr; - static assert(!is(typeof(arr.fold!()))); - static assert(!is(typeof(arr.fold!(a => a)))); - static assert(is(typeof(arr.fold!((a, b) => a)))); - static assert(is(typeof(arr.fold!((a, b) => a)(1)))); - assert(arr.length == 1); -} - -/++ -Similar to `fold`, but returns a range containing the successive reduced values. -The call `cumulativeFold!(fun)(range, seed)` first assigns `seed` to an -internal variable `result`, also called the accumulator. -The returned range contains the values `result = fun(result, x)` lazily -evaluated for each element `x` in `range`. Finally, the last element has the -same value as `fold!(fun)(seed, range)`. -The one-argument version `cumulativeFold!(fun)(range)` works similarly, but -it returns the first element unchanged and uses it as seed for the next -elements. -This function is also known as - $(HTTP en.cppreference.com/w/cpp/algorithm/partial_sum, partial_sum), - $(HTTP docs.python.org/3/library/itertools.html#itertools.accumulate, accumulate), - $(HTTP hackage.haskell.org/package/base-4.8.2.0/docs/Prelude.html#v:scanl, scan), - $(HTTP mathworld.wolfram.com/CumulativeSum.html, Cumulative Sum). - -Params: - fun = one or more functions to use as fold operation - -Returns: - The function returns a range containing the consecutive reduced values. If - there is more than one `fun`, the element type will be $(REF Tuple, - std,typecons) containing one element for each `fun`. - -See_Also: - $(HTTP en.wikipedia.org/wiki/Prefix_sum, Prefix Sum) - -Note: - - In functional programming languages this is typically called `scan`, `scanl`, - `scanLeft` or `reductions`. -+/ -template cumulativeFold(fun...) -if (fun.length >= 1) -{ - import std.meta : staticMap; - private alias binfuns = staticMap!(binaryFun, fun); - - /++ - No-seed version. The first element of `r` is used as the seed's value. - For each function `f` in `fun`, the corresponding seed type `S` is - `Unqual!(typeof(f(e, e)))`, where `e` is an element of `r`: - `ElementType!R`. - Once `S` has been determined, then `S s = e;` and `s = f(s, e);` must - both be legal. - - Params: - range = An $(REF_ALTTEXT input range, isInputRange, std,range,primitives) - Returns: - a range containing the consecutive reduced values. - +/ - auto cumulativeFold(R)(R range) - if (isInputRange!(Unqual!R)) - { - return cumulativeFoldImpl(range); - } - - /++ - Seed version. The seed should be a single value if `fun` is a single - function. If `fun` is multiple functions, then `seed` should be a - $(REF Tuple, std,typecons), with one field per function in `f`. - For convenience, if the seed is `const`, or has qualified fields, then - `cumulativeFold` will operate on an unqualified copy. If this happens - then the returned type will not perfectly match `S`. - - Params: - range = An $(REF_ALTTEXT input range, isInputRange, std,range,primitives) - seed = the initial value of the accumulator - Returns: - a range containing the consecutive reduced values. - +/ - auto cumulativeFold(R, S)(R range, S seed) - if (isInputRange!(Unqual!R)) - { - static if (fun.length == 1) - return cumulativeFoldImpl(range, seed); - else - return cumulativeFoldImpl(range, seed.expand); - } - - private auto cumulativeFoldImpl(R, Args...)(R range, ref Args args) - { - import std.algorithm.internal : algoFormat; - - static assert(Args.length == 0 || Args.length == fun.length, - algoFormat("Seed %s does not have the correct amount of fields (should be %s)", - Args.stringof, fun.length)); - - static if (args.length) - alias State = staticMap!(Unqual, Args); - else - alias State = staticMap!(ReduceSeedType!(ElementType!R), binfuns); - - foreach (i, f; binfuns) - { - static assert(!__traits(compiles, f(args[i], e)) || __traits(compiles, - { args[i] = f(args[i], e); }()), - algoFormat("Incompatible function/seed/element: %s/%s/%s", - fullyQualifiedName!f, Args[i].stringof, E.stringof)); - } - - static struct Result - { - private: - R source; - State state; - - this(R range, ref Args args) - { - source = range; - if (source.empty) - return; - - foreach (i, f; binfuns) - { - static if (args.length) - state[i] = f(args[i], source.front); - else - state[i] = source.front; - } - } - - public: - @property bool empty() - { - return source.empty; - } - - @property auto front() - { - assert(!empty, "Attempting to fetch the front of an empty cumulativeFold."); - static if (fun.length > 1) - { - import std.typecons : tuple; - return tuple(state); - } - else - { - return state[0]; - } - } - - void popFront() - { - assert(!empty, "Attempting to popFront an empty cumulativeFold."); - source.popFront; - - if (source.empty) - return; - - foreach (i, f; binfuns) - state[i] = f(state[i], source.front); - } - - static if (isForwardRange!R) - { - @property auto save() - { - auto result = this; - result.source = source.save; - return result; - } - } - - mixin ImplementLength!source; - } - - return Result(range, args); - } -} - -/// -@safe unittest -{ - import std.algorithm.comparison : max, min; - import std.array : array; - import std.math.operations : isClose; - import std.range : chain; - - int[] arr = [1, 2, 3, 4, 5]; - // Partial sum of all elements - auto sum = cumulativeFold!((a, b) => a + b)(arr, 0); - assert(sum.array == [1, 3, 6, 10, 15]); - - // Partial sum again, using a string predicate with "a" and "b" - auto sum2 = cumulativeFold!"a + b"(arr, 0); - assert(sum2.array == [1, 3, 6, 10, 15]); - - // Compute the partial maximum of all elements - auto largest = cumulativeFold!max(arr); - assert(largest.array == [1, 2, 3, 4, 5]); - - // Partial max again, but with Uniform Function Call Syntax (UFCS) - largest = arr.cumulativeFold!max; - assert(largest.array == [1, 2, 3, 4, 5]); - - // Partial count of odd elements - auto odds = arr.cumulativeFold!((a, b) => a + (b & 1))(0); - assert(odds.array == [1, 1, 2, 2, 3]); - - // Compute the partial sum of squares - auto ssquares = arr.cumulativeFold!((a, b) => a + b * b)(0); - assert(ssquares.array == [1, 5, 14, 30, 55]); - - // Chain multiple ranges into seed - int[] a = [3, 4]; - int[] b = [100]; - auto r = cumulativeFold!"a + b"(chain(a, b)); - assert(r.array == [3, 7, 107]); - - // Mixing convertible types is fair game, too - double[] c = [2.5, 3.0]; - auto r1 = cumulativeFold!"a + b"(chain(a, b, c)); - assert(isClose(r1, [3, 7, 107, 109.5, 112.5])); - - // To minimize nesting of parentheses, Uniform Function Call Syntax can be used - auto r2 = chain(a, b, c).cumulativeFold!"a + b"; - assert(isClose(r2, [3, 7, 107, 109.5, 112.5])); -} - -/** -Sometimes it is very useful to compute multiple aggregates in one pass. -One advantage is that the computation is faster because the looping overhead -is shared. That's why `cumulativeFold` accepts multiple functions. -If two or more functions are passed, `cumulativeFold` returns a $(REF Tuple, -std,typecons) object with one member per passed-in function. -The number of seeds must be correspondingly increased. -*/ -@safe unittest -{ - import std.algorithm.comparison : max, min; - import std.algorithm.iteration : map; - import std.math.operations : isClose; - import std.typecons : tuple; - - double[] a = [3.0, 4, 7, 11, 3, 2, 5]; - // Compute minimum and maximum in one pass - auto r = a.cumulativeFold!(min, max); - // The type of r is Tuple!(int, int) - assert(isClose(r.map!"a[0]", [3, 3, 3, 3, 3, 2, 2])); // minimum - assert(isClose(r.map!"a[1]", [3, 4, 7, 11, 11, 11, 11])); // maximum - - // Compute sum and sum of squares in one pass - auto r2 = a.cumulativeFold!("a + b", "a + b * b")(tuple(0.0, 0.0)); - assert(isClose(r2.map!"a[0]", [3, 7, 14, 25, 28, 30, 35])); // sum - assert(isClose(r2.map!"a[1]", [9, 25, 74, 195, 204, 208, 233])); // sum of squares -} - -@safe unittest -{ - import std.algorithm.comparison : equal, max, min; - import std.conv : to; - import std.range : chain; - import std.typecons : tuple; - - double[] a = [3, 4]; - auto r = a.cumulativeFold!("a + b")(0.0); - assert(r.equal([3, 7])); - auto r2 = cumulativeFold!("a + b")(a); - assert(r2.equal([3, 7])); - auto r3 = cumulativeFold!(min)(a); - assert(r3.equal([3, 3])); - double[] b = [100]; - auto r4 = cumulativeFold!("a + b")(chain(a, b)); - assert(r4.equal([3, 7, 107])); - - // two funs - auto r5 = cumulativeFold!("a + b", "a - b")(a, tuple(0.0, 0.0)); - assert(r5.equal([tuple(3, -3), tuple(7, -7)])); - auto r6 = cumulativeFold!("a + b", "a - b")(a); - assert(r6.equal([tuple(3, 3), tuple(7, -1)])); - - a = [1, 2, 3, 4, 5]; - // Stringize with commas - auto rep = cumulativeFold!("a ~ `, ` ~ to!string(b)")(a, ""); - assert(rep.map!"a[2 .. $]".equal(["1", "1, 2", "1, 2, 3", "1, 2, 3, 4", "1, 2, 3, 4, 5"])); - - // Test for empty range - a = []; - assert(a.cumulativeFold!"a + b".empty); - assert(a.cumulativeFold!"a + b"(2.0).empty); -} - -@safe unittest -{ - import std.algorithm.comparison : max, min; - import std.array : array; - import std.math.operations : isClose; - import std.typecons : tuple; - - const float a = 0.0; - const float[] b = [1.2, 3, 3.3]; - float[] c = [1.2, 3, 3.3]; - - auto r = cumulativeFold!"a + b"(b, a); - assert(isClose(r, [1.2, 4.2, 7.5])); - - auto r2 = cumulativeFold!"a + b"(c, a); - assert(isClose(r2, [1.2, 4.2, 7.5])); - - const numbers = [10, 30, 20]; - enum m = numbers.cumulativeFold!(min).array; - assert(m == [10, 10, 10]); - enum minmax = numbers.cumulativeFold!(min, max).array; - assert(minmax == [tuple(10, 10), tuple(10, 30), tuple(10, 30)]); -} - -@safe unittest -{ - import std.math.operations : isClose; - import std.typecons : tuple; - - enum foo = "a + 0.5 * b"; - auto r = [0, 1, 2, 3]; - auto r1 = r.cumulativeFold!foo; - auto r2 = r.cumulativeFold!(foo, foo); - assert(isClose(r1, [0, 0.5, 1.5, 3])); - assert(isClose(r2.map!"a[0]", [0, 0.5, 1.5, 3])); - assert(isClose(r2.map!"a[1]", [0, 0.5, 1.5, 3])); -} - -@safe unittest -{ - import std.algorithm.comparison : equal, max, min; - import std.array : array; - import std.typecons : tuple; - - //Seed is tuple of const. - static auto minmaxElement(alias F = min, alias G = max, R)(in R range) - @safe pure nothrow - if (isInputRange!R) - { - return range.cumulativeFold!(F, G)(tuple(ElementType!R.max, ElementType!R.min)); - } - - assert(minmaxElement([1, 2, 3]).equal([tuple(1, 1), tuple(1, 2), tuple(1, 3)])); -} - -// https://issues.dlang.org/show_bug.cgi?id=12569 -@safe unittest -{ - import std.algorithm.comparison : equal, max, min; - import std.typecons : tuple; - - dchar c = 'a'; - - assert(cumulativeFold!(min, max)("hello", tuple(c, c)).equal([tuple('a', 'h'), - tuple('a', 'h'), tuple('a', 'l'), tuple('a', 'l'), tuple('a', 'o')])); - static assert(!__traits(compiles, cumulativeFold!(min, max)("hello", tuple(c)))); - static assert(!__traits(compiles, cumulativeFold!(min, max)("hello", tuple(c, c, c)))); - - //"Seed dchar should be a Tuple" - static assert(!__traits(compiles, cumulativeFold!(min, max)("hello", c))); - //"Seed (dchar) does not have the correct amount of fields (should be 2)" - static assert(!__traits(compiles, cumulativeFold!(min, max)("hello", tuple(c)))); - //"Seed (dchar, dchar, dchar) does not have the correct amount of fields (should be 2)" - static assert(!__traits(compiles, cumulativeFold!(min, max)("hello", tuple(c, c, c)))); - //"Incompatible function/seed/element: all(alias pred = "a")/int/dchar" - static assert(!__traits(compiles, cumulativeFold!all("hello", 1))); - static assert(!__traits(compiles, cumulativeFold!(all, all)("hello", tuple(1, 1)))); -} - -// https://issues.dlang.org/show_bug.cgi?id=13304 -@safe unittest -{ - int[] data; - assert(data.cumulativeFold!((a, b) => a + b).empty); -} - -@safe unittest -{ - import std.algorithm.comparison : equal; - import std.internal.test.dummyrange : AllDummyRanges, propagatesLength, - propagatesRangeType, RangeType; - - foreach (DummyType; AllDummyRanges) - { - DummyType d; - auto m = d.cumulativeFold!"a * b"; - - static assert(propagatesLength!(typeof(m), DummyType)); - static if (DummyType.rt <= RangeType.Forward) - static assert(propagatesRangeType!(typeof(m), DummyType)); - - assert(m.equal([1, 2, 6, 24, 120, 720, 5040, 40_320, 362_880, 3_628_800])); - } -} - -// splitter -/** -Lazily splits a range using an element or range as a separator. -Separator ranges can be any narrow string type or sliceable range type. - -Two adjacent separators are considered to surround an empty element in -the split range. Use `filter!(a => !a.empty)` on the result to compress -empty elements. - -The predicate is passed to $(REF binaryFun, std,functional) and accepts -any callable function that can be executed via `pred(element, s)`. - -Notes: - If splitting a string on whitespace and token compression is desired, - consider using `splitter` without specifying a separator. - - If no separator is passed, the $(REF_ALTTEXT, unary, unaryFun, std,functional) - predicate `isTerminator` decides whether to accept an element of `r`. - -Params: - pred = The predicate for comparing each element with the separator, - defaulting to `"a == b"`. - r = The $(REF_ALTTEXT input range, isInputRange, std,range,primitives) to be - split. Must support slicing and `.length` or be a narrow string type. - s = The element (or range) to be treated as the separator - between range segments to be split. - isTerminator = The predicate for deciding where to split the range when no separator is passed - keepSeparators = The flag for deciding if the separators are kept - -Constraints: - The predicate `pred` needs to accept an element of `r` and the - separator `s`. - -Returns: - An input range of the subranges of elements between separators. If `r` - is a $(REF_ALTTEXT forward range, isForwardRange, std,range,primitives) - or $(REF_ALTTEXT bidirectional range, isBidirectionalRange, std,range,primitives), - the returned range will be likewise. - When a range is used a separator, bidirectionality isn't possible. - - If keepSeparators is equal to Yes.keepSeparators the output will also contain the - separators. - - If an empty range is given, the result is an empty range. If a range with - one separator is given, the result is a range with two empty elements. - -See_Also: - $(REF _splitter, std,regex) for a version that splits using a regular expression defined separator, - $(REF _split, std,array) for a version that splits eagerly and - $(LREF splitWhen), which compares adjacent elements instead of element against separator. -*/ -auto splitter(alias pred = "a == b", - Flag!"keepSeparators" keepSeparators = No.keepSeparators, - Range, - Separator)(Range r, Separator s) -if (is(typeof(binaryFun!pred(r.front, s)) : bool) - && ((hasSlicing!Range && hasLength!Range) || isNarrowString!Range)) -{ - import std.algorithm.searching : find; - import std.conv : unsigned; - - struct Result - { - private: - Range _input; - Separator _separator; - // Do we need hasLength!Range? popFront uses _input.length... - enum size_t _unComputed = size_t.max - 1, _atEnd = size_t.max; - size_t _frontLength = _unComputed; - size_t _backLength = _unComputed; - - static if (isNarrowString!Range) - { - size_t _separatorLength; - } - else - { - enum _separatorLength = 1; - } - - static if (keepSeparators) - { - bool _wasSeparator = true; - } - - static if (isBidirectionalRange!Range) - { - size_t lastIndexOf(Range haystack, Separator needle) - { - import std.range : retro; - auto r = haystack.retro().find!pred(needle); - return r.retro().length - 1; - } - } - - public: - this(Range input, Separator separator) - { - _input = input; - _separator = separator; - - static if (isNarrowString!Range) - { - import std.utf : codeLength; - - _separatorLength = codeLength!(ElementEncodingType!Range)(separator); - } - if (_input.empty) - _frontLength = _atEnd; - } - - static if (isInfinite!Range) - { - enum bool empty = false; - } - else - { - @property bool empty() - { - return _frontLength == _atEnd; - } - } - - @property Range front() - { - assert(!empty, "Attempting to fetch the front of an empty splitter."); - static if (keepSeparators) - { - if (!_wasSeparator) - { - _frontLength = _separatorLength; - _wasSeparator = true; - } - else if (_frontLength == _unComputed) - { - auto r = _input.find!pred(_separator); - _frontLength = _input.length - r.length; - _wasSeparator = false; - } - } - else - { - if (_frontLength == _unComputed) - { - auto r = _input.find!pred(_separator); - _frontLength = _input.length - r.length; - } - } - return _input[0 .. _frontLength]; - } - - void popFront() - { - assert(!empty, "Attempting to popFront an empty splitter."); - if (_frontLength == _unComputed) - { - front; - } - assert(_frontLength <= _input.length, "The front position must" - ~ " not exceed the input.length"); - static if (keepSeparators) - { - if (_frontLength == _input.length && !_wasSeparator) - { - _frontLength = _atEnd; - - _backLength = _atEnd; - } - else - { - _input = _input[_frontLength .. _input.length]; - _frontLength = _unComputed; - } - } - else - { - if (_frontLength == _input.length) - { - // no more input and need to fetch => done - _frontLength = _atEnd; - - // Probably don't need this, but just for consistency: - _backLength = _atEnd; - } - else - { - _input = _input[_frontLength + _separatorLength .. _input.length]; - _frontLength = _unComputed; - } - } - } - - static if (isForwardRange!Range) - { - @property typeof(this) save() - { - auto ret = this; - ret._input = _input.save; - return ret; - } - } - - static if (isBidirectionalRange!Range) - { - @property Range back() - { - assert(!empty, "Attempting to fetch the back of an empty splitter."); - static if (keepSeparators) - { - if (!_wasSeparator) - { - _backLength = _separatorLength; - _wasSeparator = true; - } - else if (_backLength == _unComputed) - { - immutable lastIndex = lastIndexOf(_input, _separator); - if (lastIndex == -1) - { - _backLength = _input.length; - } - else - { - _backLength = _input.length - lastIndex - 1; - } - _wasSeparator = false; - } - } - else - { - if (_backLength == _unComputed) - { - immutable lastIndex = lastIndexOf(_input, _separator); - if (lastIndex == -1) - { - _backLength = _input.length; - } - else - { - _backLength = _input.length - lastIndex - 1; - } - } - } - return _input[_input.length - _backLength .. _input.length]; - } - - void popBack() - { - assert(!empty, "Attempting to popBack an empty splitter."); - if (_backLength == _unComputed) - { - // evaluate back to make sure it's computed - back; - } - assert(_backLength <= _input.length, "The end index must not" - ~ " exceed the length of the input"); - static if (keepSeparators) - { - if (_backLength == _input.length && !_wasSeparator) - { - _frontLength = _atEnd; - _backLength = _atEnd; - } - else - { - _input = _input[0 .. _input.length - _backLength]; - _backLength = _unComputed; - } - } - else - { - if (_backLength == _input.length) - { - // no more input and need to fetch => done - _frontLength = _atEnd; - _backLength = _atEnd; - } - else - { - _input = _input[0 .. _input.length - _backLength - _separatorLength]; - _backLength = _unComputed; - } - } - } - } - } - - return Result(r, s); -} - -/// Basic splitting with characters and numbers. -@safe unittest -{ - import std.algorithm.comparison : equal; - - assert("a|bc|def".splitter('|').equal([ "a", "bc", "def" ])); - - int[] a = [1, 0, 2, 3, 0, 4, 5, 6]; - int[][] w = [ [1], [2, 3], [4, 5, 6] ]; - assert(a.splitter(0).equal(w)); -} - -/// Basic splitting with characters and numbers and keeping sentinels. -@safe unittest -{ - import std.algorithm.comparison : equal; - import std.typecons : Yes; - - assert("a|bc|def".splitter!("a == b", Yes.keepSeparators)('|') - .equal([ "a", "|", "bc", "|", "def" ])); - - int[] a = [1, 0, 2, 3, 0, 4, 5, 6]; - int[][] w = [ [1], [0], [2, 3], [0], [4, 5, 6] ]; - assert(a.splitter!("a == b", Yes.keepSeparators)(0).equal(w)); -} - -/// Adjacent separators. -@safe unittest -{ - import std.algorithm.comparison : equal; - - assert("|ab|".splitter('|').equal([ "", "ab", "" ])); - assert("ab".splitter('|').equal([ "ab" ])); - - assert("a|b||c".splitter('|').equal([ "a", "b", "", "c" ])); - assert("hello world".splitter(' ').equal([ "hello", "", "world" ])); - - auto a = [ 1, 2, 0, 0, 3, 0, 4, 5, 0 ]; - auto w = [ [1, 2], [], [3], [4, 5], [] ]; - assert(a.splitter(0).equal(w)); -} - -/// Adjacent separators and keeping sentinels. -@safe unittest -{ - import std.algorithm.comparison : equal; - import std.typecons : Yes; - - assert("|ab|".splitter!("a == b", Yes.keepSeparators)('|') - .equal([ "", "|", "ab", "|", "" ])); - assert("ab".splitter!("a == b", Yes.keepSeparators)('|') - .equal([ "ab" ])); - - assert("a|b||c".splitter!("a == b", Yes.keepSeparators)('|') - .equal([ "a", "|", "b", "|", "", "|", "c" ])); - assert("hello world".splitter!("a == b", Yes.keepSeparators)(' ') - .equal([ "hello", " ", "", " ", "world" ])); - - auto a = [ 1, 2, 0, 0, 3, 0, 4, 5, 0 ]; - auto w = [ [1, 2], [0], [], [0], [3], [0], [4, 5], [0], [] ]; - assert(a.splitter!("a == b", Yes.keepSeparators)(0).equal(w)); -} - -/// Empty and separator-only ranges. -@safe unittest -{ - import std.algorithm.comparison : equal; - import std.range : empty; - - assert("".splitter('|').empty); - assert("|".splitter('|').equal([ "", "" ])); - assert("||".splitter('|').equal([ "", "", "" ])); -} - -/// Empty and separator-only ranges and keeping sentinels. -@safe unittest -{ - import std.algorithm.comparison : equal; - import std.typecons : Yes; - import std.range : empty; - - assert("".splitter!("a == b", Yes.keepSeparators)('|').empty); - assert("|".splitter!("a == b", Yes.keepSeparators)('|') - .equal([ "", "|", "" ])); - assert("||".splitter!("a == b", Yes.keepSeparators)('|') - .equal([ "", "|", "", "|", "" ])); -} - -/// Use a range for splitting -@safe unittest -{ - import std.algorithm.comparison : equal; - - assert("a=>bc=>def".splitter("=>").equal([ "a", "bc", "def" ])); - assert("a|b||c".splitter("||").equal([ "a|b", "c" ])); - assert("hello world".splitter(" ").equal([ "hello", "world" ])); - - int[] a = [ 1, 2, 0, 0, 3, 0, 4, 5, 0 ]; - int[][] w = [ [1, 2], [3, 0, 4, 5, 0] ]; - assert(a.splitter([0, 0]).equal(w)); - - a = [ 0, 0 ]; - assert(a.splitter([0, 0]).equal([ (int[]).init, (int[]).init ])); - - a = [ 0, 0, 1 ]; - assert(a.splitter([0, 0]).equal([ [], [1] ])); -} - -/// Use a range for splitting -@safe unittest -{ - import std.algorithm.comparison : equal; - import std.typecons : Yes; - - assert("a=>bc=>def".splitter!("a == b", Yes.keepSeparators)("=>") - .equal([ "a", "=>", "bc", "=>", "def" ])); - assert("a|b||c".splitter!("a == b", Yes.keepSeparators)("||") - .equal([ "a|b", "||", "c" ])); - assert("hello world".splitter!("a == b", Yes.keepSeparators)(" ") - .equal([ "hello", " ", "world" ])); - - int[] a = [ 1, 2, 0, 0, 3, 0, 4, 5, 0 ]; - int[][] w = [ [1, 2], [0, 0], [3, 0, 4, 5, 0] ]; - assert(a.splitter!("a == b", Yes.keepSeparators)([0, 0]).equal(w)); - - a = [ 0, 0 ]; - assert(a.splitter!("a == b", Yes.keepSeparators)([0, 0]) - .equal([ (int[]).init, [0, 0], (int[]).init ])); - - a = [ 0, 0, 1 ]; - assert(a.splitter!("a == b", Yes.keepSeparators)([0, 0]) - .equal([ [], [0, 0], [1] ])); -} - -/// Custom predicate functions. -@safe unittest -{ - import std.algorithm.comparison : equal; - import std.ascii : toLower; - - assert("abXcdxef".splitter!"a.toLower == b"('x').equal( - [ "ab", "cd", "ef" ])); - - auto w = [ [0], [1], [2] ]; - assert(w.splitter!"a.front == b"(1).equal([ [[0]], [[2]] ])); -} - -/// Custom predicate functions. -@safe unittest -{ - import std.algorithm.comparison : equal; - import std.typecons : Yes; - import std.ascii : toLower; - - assert("abXcdxef".splitter!("a.toLower == b", Yes.keepSeparators)('x') - .equal([ "ab", "X", "cd", "x", "ef" ])); - - auto w = [ [0], [1], [2] ]; - assert(w.splitter!("a.front == b", Yes.keepSeparators)(1) - .equal([ [[0]], [[1]], [[2]] ])); -} - -/// Use splitter without a separator -@safe unittest -{ - import std.algorithm.comparison : equal; - import std.range.primitives : front; - - assert(equal(splitter!(a => a == '|')("a|bc|def"), [ "a", "bc", "def" ])); - assert(equal(splitter!(a => a == ' ')("hello world"), [ "hello", "", "world" ])); - - int[] a = [ 1, 2, 0, 0, 3, 0, 4, 5, 0 ]; - int[][] w = [ [1, 2], [], [3], [4, 5], [] ]; - assert(equal(splitter!(a => a == 0)(a), w)); - - a = [ 0 ]; - assert(equal(splitter!(a => a == 0)(a), [ (int[]).init, (int[]).init ])); - - a = [ 0, 1 ]; - assert(equal(splitter!(a => a == 0)(a), [ [], [1] ])); - - w = [ [0], [1], [2] ]; - assert(equal(splitter!(a => a.front == 1)(w), [ [[0]], [[2]] ])); -} - -/// Leading separators, trailing separators, or no separators. -@safe unittest -{ - import std.algorithm.comparison : equal; - - assert("|ab|".splitter('|').equal([ "", "ab", "" ])); - assert("ab".splitter('|').equal([ "ab" ])); -} - -/// Leading separators, trailing separators, or no separators. -@safe unittest -{ - import std.algorithm.comparison : equal; - import std.typecons : Yes; - - assert("|ab|".splitter!("a == b", Yes.keepSeparators)('|') - .equal([ "", "|", "ab", "|", "" ])); - assert("ab".splitter!("a == b", Yes.keepSeparators)('|') - .equal([ "ab" ])); -} - -/// Splitter returns bidirectional ranges if the delimiter is a single element -@safe unittest -{ - import std.algorithm.comparison : equal; - import std.range : retro; - assert("a|bc|def".splitter('|').retro.equal([ "def", "bc", "a" ])); -} - -/// Splitter returns bidirectional ranges if the delimiter is a single element -@safe unittest -{ - import std.algorithm.comparison : equal; - import std.typecons : Yes; - import std.range : retro; - assert("a|bc|def".splitter!("a == b", Yes.keepSeparators)('|') - .retro.equal([ "def", "|", "bc", "|", "a" ])); -} - -/// Splitting by word lazily -@safe unittest -{ - import std.ascii : isWhite; - import std.algorithm.comparison : equal; - import std.algorithm.iteration : splitter; - - string str = "Hello World!"; - assert(str.splitter!(isWhite).equal(["Hello", "World!"])); -} - -@safe unittest -{ - import std.algorithm; - import std.array : array; - import std.internal.test.dummyrange; - import std.range : retro; - - assert(equal(splitter("hello world", ' '), [ "hello", "", "world" ])); - assert(equal(splitter("Ĺžlutoučkýřkůň", 'ř'), [ "ĹžlutoučkĂ˝", "kůň" ])); - int[] a = [ 1, 2, 0, 0, 3, 0, 4, 5, 0 ]; - int[][] w = [ [1, 2], [], [3], [4, 5], [] ]; - static assert(isForwardRange!(typeof(splitter(a, 0)))); - - assert(equal(splitter(a, 0), w)); - a = null; - assert(equal(splitter(a, 0), (int[][]).init)); - a = [ 0 ]; - assert(equal(splitter(a, 0), [ (int[]).init, (int[]).init ][])); - a = [ 0, 1 ]; - assert(equal(splitter(a, 0), [ [], [1] ])); - assert(equal(splitter(a, 0), [ [], [1] ][])); - - // Thoroughly exercise the bidirectional stuff. - auto str = "abc abcd abcde ab abcdefg abcdefghij ab ac ar an at ada"; - assert(equal( - retro(splitter(str, 'a')), - retro(array(splitter(str, 'a'))) - )); - - // Test interleaving front and back. - auto split = splitter(str, 'a'); - assert(split.front == ""); - assert(split.back == ""); - split.popBack(); - assert(split.back == "d"); - split.popFront(); - assert(split.front == "bc "); - assert(split.back == "d"); - split.popFront(); - split.popBack(); - assert(split.back == "t "); - split.popBack(); - split.popBack(); - split.popFront(); - split.popFront(); - assert(split.front == "b "); - assert(split.back == "r "); - - // https://issues.dlang.org/show_bug.cgi?id=4408 - foreach (DummyType; AllDummyRanges) - { - static if (isRandomAccessRange!DummyType) - { - static assert(isBidirectionalRange!DummyType); - DummyType d; - auto s = splitter(d, 5); - assert(equal(s.front, [1,2,3,4])); - assert(equal(s.back, [6,7,8,9,10])); - - auto s2 = splitter(d, [4, 5]); - assert(equal(s2.front, [1,2,3])); - } - } -} -@safe unittest -{ - import std.algorithm; - import std.range; - auto L = retro(iota(1L, 10L)); - auto s = splitter(L, 5L); - assert(equal(s.front, [9L, 8L, 7L, 6L])); - s.popFront(); - assert(equal(s.front, [4L, 3L, 2L, 1L])); - s.popFront(); - assert(s.empty); -} - -// https://issues.dlang.org/show_bug.cgi?id=18470 -@safe unittest -{ - import std.algorithm.comparison : equal; - - const w = [[0], [1], [2]]; - assert(w.splitter!((a, b) => a.front() == b)(1).equal([[[0]], [[2]]])); -} - -// https://issues.dlang.org/show_bug.cgi?id=18470 -@safe unittest -{ - import std.algorithm.comparison : equal; - import std.ascii : toLower; - - assert("abXcdxef".splitter!"a.toLower == b"('x').equal(["ab", "cd", "ef"])); - assert("abXcdxef".splitter!((a, b) => a.toLower == b)('x').equal(["ab", "cd", "ef"])); -} - -/// ditto -auto splitter(alias pred = "a == b", - Flag!"keepSeparators" keepSeparators = No.keepSeparators, - Range, - Separator)(Range r, Separator s) -if (is(typeof(binaryFun!pred(r.front, s.front)) : bool) - && (hasSlicing!Range || isNarrowString!Range) - && isForwardRange!Separator - && (hasLength!Separator || isNarrowString!Separator)) -{ - import std.algorithm.searching : find; - import std.conv : unsigned; - - static struct Result - { - private: - Range _input; - Separator _separator; - // _frontLength == size_t.max means empty - size_t _frontLength = size_t.max; - - static if (keepSeparators) - { - bool _wasSeparator = true; - } - - @property auto separatorLength() { return _separator.length; } - - void ensureFrontLength() - { - if (_frontLength != _frontLength.max) return; - static if (keepSeparators) - { - assert(!_input.empty || _wasSeparator, "The input must not be empty"); - if (_wasSeparator) - { - _frontLength = _input.length - - find!pred(_input, _separator).length; - _wasSeparator = false; - } - else - { - _frontLength = separatorLength(); - _wasSeparator = true; - } - } - else - { - assert(!_input.empty, "The input must not be empty"); - // compute front length - _frontLength = (_separator.empty) ? 1 : - _input.length - find!pred(_input, _separator).length; - } - } - - public: - this(Range input, Separator separator) - { - _input = input; - _separator = separator; - } - - @property Range front() - { - assert(!empty, "Attempting to fetch the front of an empty splitter."); - ensureFrontLength(); - return _input[0 .. _frontLength]; - } - - static if (isInfinite!Range) - { - enum bool empty = false; // Propagate infiniteness - } - else - { - @property bool empty() - { - static if (keepSeparators) - { - return _frontLength == size_t.max && _input.empty && !_wasSeparator; - } - else - { - return _frontLength == size_t.max && _input.empty; - } - } - } - - void popFront() - { - assert(!empty, "Attempting to popFront an empty splitter."); - ensureFrontLength(); - - static if (keepSeparators) - { - _input = _input[_frontLength .. _input.length]; - } - else - { - if (_frontLength == _input.length) - { - // done, there's no separator in sight - _input = _input[_frontLength .. _frontLength]; - _frontLength = _frontLength.max; - return; - } - if (_frontLength + separatorLength == _input.length) - { - // Special case: popping the first-to-last item; there is - // an empty item right after this. - _input = _input[_input.length .. _input.length]; - _frontLength = 0; - return; - } - // Normal case, pop one item and the separator, get ready for - // reading the next item - _input = _input[_frontLength + separatorLength .. _input.length]; - } - // mark _frontLength as uninitialized - _frontLength = _frontLength.max; - } - - static if (isForwardRange!Range) - { - @property typeof(this) save() - { - auto ret = this; - ret._input = _input.save; - return ret; - } - } - } - - return Result(r, s); -} - -@safe unittest -{ - import std.algorithm.comparison : equal; - import std.typecons : Tuple; - - alias C = Tuple!(int, "x", int, "y"); - auto a = [C(1,0), C(2,0), C(3,1), C(4,0)]; - assert(equal(splitter!"a.x == b"(a, [2, 3]), [ [C(1,0)], [C(4,0)] ])); -} - -@safe unittest -{ - import std.algorithm.comparison : equal; - import std.array : split; - import std.conv : text; - - auto s = ",abc, de, fg,hi,"; - auto sp0 = splitter(s, ','); - assert(equal(sp0, ["", "abc", " de", " fg", "hi", ""][])); - - auto s1 = ", abc, de, fg, hi, "; - auto sp1 = splitter(s1, ", "); - assert(equal(sp1, ["", "abc", "de", " fg", "hi", ""][])); - static assert(isForwardRange!(typeof(sp1))); - - int[] a = [ 1, 2, 0, 3, 0, 4, 5, 0 ]; - int[][] w = [ [1, 2], [3], [4, 5], [] ]; - uint i; - foreach (e; splitter(a, 0)) - { - assert(i < w.length); - assert(e == w[i++]); - } - assert(i == w.length); - - wstring names = ",peter,paul,jerry,"; - auto words = split(names, ","); - assert(walkLength(words) == 5, text(walkLength(words))); -} - -@safe unittest -{ - int[][] a = [ [1], [2], [0], [3], [0], [4], [5], [0] ]; - int[][][] w = [ [[1], [2]], [[3]], [[4], [5]], [] ]; - uint i; - foreach (e; splitter!"a.front == 0"(a, 0)) - { - assert(i < w.length); - assert(e == w[i++]); - } - assert(i == w.length); -} - -@safe unittest -{ - import std.algorithm.comparison : equal; - auto s6 = ","; - auto sp6 = splitter(s6, ','); - foreach (e; sp6) {} - assert(equal(sp6, ["", ""][])); -} - -// https://issues.dlang.org/show_bug.cgi?id=10773 -@safe unittest -{ - import std.algorithm.comparison : equal; - - auto s = splitter("abc", ""); - assert(s.equal(["a", "b", "c"])); -} - -@safe unittest -{ - import std.algorithm.comparison : equal; - - // Test by-reference separator - static class RefSep { - @safe: - string _impl; - this(string s) { _impl = s; } - @property empty() { return _impl.empty; } - @property auto front() { return _impl.front; } - void popFront() { _impl = _impl[1..$]; } - @property RefSep save() scope { return new RefSep(_impl); } - @property auto length() { return _impl.length; } - } - auto sep = new RefSep("->"); - auto data = "i->am->pointing"; - auto words = splitter(data, sep); - assert(words.equal([ "i", "am", "pointing" ])); -} - -/// ditto -auto splitter(alias isTerminator, Range)(Range r) -if (isForwardRange!Range && is(typeof(unaryFun!isTerminator(r.front)))) -{ - return SplitterResult!(unaryFun!isTerminator, Range)(r); -} - -private struct SplitterResult(alias isTerminator, Range) -{ - import std.algorithm.searching : find; - enum fullSlicing = (hasLength!Range && hasSlicing!Range) || isSomeString!Range; - - private Range _input; - private size_t _end = 0; - static if (!fullSlicing) - private Range _next; - - private void findTerminator() - { - static if (fullSlicing) - { - auto r = find!isTerminator(_input.save); - _end = _input.length - r.length; - } - else - for ( _end = 0; !_next.empty ; _next.popFront) - { - if (isTerminator(_next.front)) - break; - ++_end; - } - } - - this(Range input) - { - _input = input; - static if (!fullSlicing) - _next = _input.save; - - if (!_input.empty) - findTerminator(); - else - _end = size_t.max; - } - - static if (fullSlicing) - { - private this(Range input, size_t end) - { - _input = input; - _end = end; - } - } - else - { - private this(Range input, size_t end, Range next) - { - _input = input; - _end = end; - _next = next; - } - } - - static if (isInfinite!Range) - { - enum bool empty = false; // Propagate infiniteness. - } - else - { - @property bool empty() - { - return _end == size_t.max; - } - } - - @property auto front() - { - version (assert) - { - import core.exception : RangeError; - if (empty) - throw new RangeError(); - } - static if (fullSlicing) - return _input[0 .. _end]; - else - { - import std.range : takeExactly; - return _input.takeExactly(_end); - } - } - - void popFront() - { - version (assert) - { - import core.exception : RangeError; - if (empty) - throw new RangeError(); - } - - static if (fullSlicing) - { - _input = _input[_end .. _input.length]; - if (_input.empty) - { - _end = size_t.max; - return; - } - _input.popFront(); - } - else - { - if (_next.empty) - { - _input = _next; - _end = size_t.max; - return; - } - _next.popFront(); - _input = _next.save; - } - findTerminator(); - } - - @property typeof(this) save() - { - static if (fullSlicing) - return SplitterResult(_input.save, _end); - else - return SplitterResult(_input.save, _end, _next.save); - } -} - -@safe unittest -{ - import std.algorithm.comparison : equal; - import std.range : iota; - - auto L = iota(1L, 10L); - auto s = splitter(L, [5L, 6L]); - assert(equal(s.front, [1L, 2L, 3L, 4L])); - s.popFront(); - assert(equal(s.front, [7L, 8L, 9L])); - s.popFront(); - assert(s.empty); -} - -@safe unittest -{ - import std.algorithm.comparison : equal; - import std.algorithm.internal : algoFormat; - import std.internal.test.dummyrange; - - void compare(string sentence, string[] witness) - { - auto r = splitter!"a == ' '"(sentence); - assert(equal(r.save, witness), algoFormat("got: %(%s, %) expected: %(%s, %)", r, witness)); - } - - compare(" Mary has a little lamb. ", - ["", "Mary", "", "has", "a", "little", "lamb.", "", "", ""]); - compare("Mary has a little lamb. ", - ["Mary", "", "has", "a", "little", "lamb.", "", "", ""]); - compare("Mary has a little lamb.", - ["Mary", "", "has", "a", "little", "lamb."]); - compare("", (string[]).init); - compare(" ", ["", ""]); - - static assert(isForwardRange!(typeof(splitter!"a == ' '"("ABC")))); - - foreach (DummyType; AllDummyRanges) - { - static if (isRandomAccessRange!DummyType) - { - auto rangeSplit = splitter!"a == 5"(DummyType.init); - assert(equal(rangeSplit.front, [1,2,3,4])); - rangeSplit.popFront(); - assert(equal(rangeSplit.front, [6,7,8,9,10])); - } - } -} - -@safe unittest -{ - import std.algorithm.comparison : equal; - import std.algorithm.internal : algoFormat; - import std.range; - - struct Entry - { - int low; - int high; - int[][] result; - } - Entry[] entries = [ - Entry(0, 0, []), - Entry(0, 1, [[0]]), - Entry(1, 2, [[], []]), - Entry(2, 7, [[2], [4], [6]]), - Entry(1, 8, [[], [2], [4], [6], []]), - ]; - foreach ( entry ; entries ) - { - auto a = iota(entry.low, entry.high).filter!"true"(); - auto b = splitter!"a%2"(a); - assert(equal!equal(b.save, entry.result), algoFormat("got: %(%s, %) expected: %(%s, %)", b, entry.result)); - } -} - -@safe unittest -{ - import std.algorithm.comparison : equal; - import std.uni : isWhite; - - // https://issues.dlang.org/show_bug.cgi?id=6791 - assert(equal( - splitter("lĂ  dove terminava quella valle"), - ["lĂ ", "dove", "terminava", "quella", "valle"] - )); - assert(equal( - splitter!(isWhite)("lĂ  dove terminava quella valle"), - ["lĂ ", "dove", "terminava", "quella", "valle"] - )); - assert(equal(splitter!"a=='本'"("日本語"), ["日", "語"])); -} - -// https://issues.dlang.org/show_bug.cgi?id=18657 -pure @safe unittest -{ - import std.algorithm.comparison : equal; - import std.range : refRange; - string s = "foobar"; - auto r = refRange(&s).splitter!(c => c == 'b'); - assert(equal!equal(r.save, ["foo", "ar"])); - assert(equal!equal(r.save, ["foo", "ar"])); -} - -/++ -Lazily splits the character-based range `s` into words, using whitespace as the -delimiter. - -This function is character-range specific and, contrary to -`splitter!(std.uni.isWhite)`, runs of whitespace will be merged together -(no empty tokens will be produced). - -Params: - s = The character-based range to be split. Must be a string, or a - random-access range of character types. - -Returns: - An $(REF_ALTTEXT input range, isInputRange, std,range,primitives) of slices of - the original range split by whitespace. - +/ -auto splitter(Range)(Range s) -if (isSomeString!Range || - isRandomAccessRange!Range && hasLength!Range && hasSlicing!Range && - !isConvertibleToString!Range && - isSomeChar!(ElementEncodingType!Range)) -{ - import std.algorithm.searching : find; - static struct Result - { - private: - import core.exception : RangeError; - Range _s; - size_t _frontLength; - - void getFirst() - { - import std.uni : isWhite; - import std.traits : Unqual; - - static if (is(immutable ElementEncodingType!Range == immutable wchar) && - is(immutable ElementType!Range == immutable dchar)) - { - // all unicode whitespace characters fit into a wchar. However, - // this range is a wchar array, so we will treat it like a - // wchar array instead of decoding each code point. - _frontLength = _s.length; // default condition, no spaces - foreach (i; 0 .. _s.length) - if (isWhite(_s[i])) - { - _frontLength = i; - break; - } - } - else static if (is(immutable ElementType!Range == immutable dchar) || - is(immutable ElementType!Range == immutable wchar)) - { - // dchar or wchar range, we can just use find. - auto r = find!(isWhite)(_s.save); - _frontLength = _s.length - r.length; - } - else - { - // need to decode the characters until we find a space. This is - // ported from std.string.stripLeft. - static import std.ascii; - static import std.uni; - import std.utf : decodeFront; - - auto input = _s.save; - size_t iLength = input.length; - - while (!input.empty) - { - auto c = input.front; - if (std.ascii.isASCII(c)) - { - if (std.ascii.isWhite(c)) - break; - input.popFront(); - --iLength; - } - else - { - auto dc = decodeFront(input); - if (std.uni.isWhite(dc)) - break; - iLength = input.length; - } - } - - // sanity check - assert(iLength <= _s.length, "The current index must not" - ~ " exceed the length of the input"); - - _frontLength = _s.length - iLength; - } - } - - public: - this(Range s) - { - import std.string : stripLeft; - _s = s.stripLeft(); - getFirst(); - } - - @property auto front() - { - version (assert) if (empty) throw new RangeError(); - return _s[0 .. _frontLength]; - } - - void popFront() - { - import std.string : stripLeft; - version (assert) if (empty) throw new RangeError(); - _s = _s[_frontLength .. $].stripLeft(); - getFirst(); - } - - @property bool empty() const - { - return _s.empty; - } - - @property inout(Result) save() inout @safe pure nothrow - { - return this; - } - } - return Result(s); -} - -/// -@safe pure unittest -{ - import std.algorithm.comparison : equal; - auto a = " a bcd ef gh "; - assert(equal(splitter(a), ["a", "bcd", "ef", "gh"][])); -} - -@safe pure unittest -{ - import std.algorithm.comparison : equal; - import std.meta : AliasSeq; - static foreach (S; AliasSeq!(string, wstring, dstring)) - {{ - import std.conv : to; - S a = " a \u2028 bcd ef gh "; - assert(equal(splitter(a), [to!S("a"), to!S("bcd"), to!S("ef"), to!S("gh")])); - a = ""; - assert(splitter(a).empty); - }} - - immutable string s = " a bcd ef gh "; - assert(equal(splitter(s), ["a", "bcd", "ef", "gh"][])); -} - -@safe unittest -{ - import std.conv : to; - import std.string : strip; - - // TDPL example, page 8 - uint[string] dictionary; - char[][3] lines; - lines[0] = "line one".dup; - lines[1] = "line \ttwo".dup; - lines[2] = "yah last line\ryah".dup; - foreach (line; lines) - { - foreach (word; splitter(strip(line))) - { - if (word in dictionary) continue; // Nothing to do - auto newID = dictionary.length; - dictionary[to!string(word)] = cast(uint) newID; - } - } - assert(dictionary.length == 5); - assert(dictionary["line"]== 0); - assert(dictionary["one"]== 1); - assert(dictionary["two"]== 2); - assert(dictionary["yah"]== 3); - assert(dictionary["last"]== 4); - -} - -@safe unittest -{ - // do it with byCodeUnit - import std.conv : to; - import std.string : strip; - import std.utf : byCodeUnit; - - alias BCU = typeof("abc".byCodeUnit()); - - // TDPL example, page 8 - uint[BCU] dictionary; - BCU[3] lines; - lines[0] = "line one".byCodeUnit; - lines[1] = "line \ttwo".byCodeUnit; - lines[2] = "yah last line\ryah".byCodeUnit; - foreach (line; lines) - { - foreach (word; splitter(strip(line))) - { - static assert(is(typeof(word) == BCU)); - if (word in dictionary) continue; // Nothing to do - auto newID = dictionary.length; - dictionary[word] = cast(uint) newID; - } - } - assert(dictionary.length == 5); - assert(dictionary["line".byCodeUnit]== 0); - assert(dictionary["one".byCodeUnit]== 1); - assert(dictionary["two".byCodeUnit]== 2); - assert(dictionary["yah".byCodeUnit]== 3); - assert(dictionary["last".byCodeUnit]== 4); -} - -// https://issues.dlang.org/show_bug.cgi?id=19238 -@safe pure unittest -{ - import std.utf : byCodeUnit; - import std.algorithm.comparison : equal; - auto range = "hello world".byCodeUnit.splitter; - static assert(is(typeof(range.front()) == typeof("hello".byCodeUnit()))); - assert(range.equal(["hello".byCodeUnit, "world".byCodeUnit])); - - // test other space types, including unicode - auto u = " a\t\v\r bcd\u3000 \u2028\t\nef\U00010001 gh"; - assert(equal(splitter(u), ["a", "bcd", "ef\U00010001", "gh"][])); - assert(equal(splitter(u.byCodeUnit), ["a".byCodeUnit, "bcd".byCodeUnit, - "ef\U00010001".byCodeUnit, "gh".byCodeUnit][])); -} - -@safe unittest -{ - import std.algorithm.comparison : equal; - import std.algorithm.internal : algoFormat; - import std.array : split; - import std.conv : text; - - // Check consistency: - // All flavors of split should produce the same results - foreach (input; [(int[]).init, - [0], - [0, 1, 0], - [1, 1, 0, 0, 1, 1], - ]) - { - foreach (s; [0, 1]) - { - auto result = split(input, s); - - assert(equal(result, split(input, [s])), algoFormat(`"[%(%s,%)]"`, split(input, [s]))); - //assert(equal(result, split(input, [s].filter!"true"()))); //Not yet implemented - assert(equal(result, split!((a) => a == s)(input)), text(split!((a) => a == s)(input))); - - //assert(equal!equal(result, split(input.filter!"true"(), s))); //Not yet implemented - //assert(equal!equal(result, split(input.filter!"true"(), [s]))); //Not yet implemented - //assert(equal!equal(result, split(input.filter!"true"(), [s].filter!"true"()))); //Not yet implemented - assert(equal!equal(result, split!((a) => a == s)(input.filter!"true"()))); - - assert(equal(result, splitter(input, s))); - assert(equal(result, splitter(input, [s]))); - //assert(equal(result, splitter(input, [s].filter!"true"()))); //Not yet implemented - assert(equal(result, splitter!((a) => a == s)(input))); - - //assert(equal!equal(result, splitter(input.filter!"true"(), s))); //Not yet implemented - //assert(equal!equal(result, splitter(input.filter!"true"(), [s]))); //Not yet implemented - //assert(equal!equal(result, splitter(input.filter!"true"(), [s].filter!"true"()))); //Not yet implemented - assert(equal!equal(result, splitter!((a) => a == s)(input.filter!"true"()))); - } - } - foreach (input; [string.init, - " ", - " hello ", - "hello hello", - " hello what heck this ? " - ]) - { - foreach (s; [' ', 'h']) - { - auto result = split(input, s); - - assert(equal(result, split(input, [s]))); - //assert(equal(result, split(input, [s].filter!"true"()))); //Not yet implemented - assert(equal(result, split!((a) => a == s)(input))); - - //assert(equal!equal(result, split(input.filter!"true"(), s))); //Not yet implemented - //assert(equal!equal(result, split(input.filter!"true"(), [s]))); //Not yet implemented - //assert(equal!equal(result, split(input.filter!"true"(), [s].filter!"true"()))); //Not yet implemented - assert(equal!equal(result, split!((a) => a == s)(input.filter!"true"()))); - - assert(equal(result, splitter(input, s))); - assert(equal(result, splitter(input, [s]))); - //assert(equal(result, splitter(input, [s].filter!"true"()))); //Not yet implemented - assert(equal(result, splitter!((a) => a == s)(input))); - - //assert(equal!equal(result, splitter(input.filter!"true"(), s))); //Not yet implemented - //assert(equal!equal(result, splitter(input.filter!"true"(), [s]))); //Not yet implemented - //assert(equal!equal(result, splitter(input.filter!"true"(), [s].filter!"true"()))); //Not yet implemented - assert(equal!equal(result, splitter!((a) => a == s)(input.filter!"true"()))); - } - } -} - -// In same combinations substitute needs to calculate the auto-decoded length -// of its needles -private template hasDifferentAutodecoding(Range, Needles...) -{ - import std.meta : anySatisfy; - /* iff - - the needles needs auto-decoding, but the incoming range doesn't (or vice versa) - - both (range, needle) need auto-decoding and don't share the same common type - */ - enum needlesAreNarrow = anySatisfy!(isNarrowString, Needles); - enum sourceIsNarrow = isNarrowString!Range; - enum hasDifferentAutodecoding = sourceIsNarrow != needlesAreNarrow || - (sourceIsNarrow && needlesAreNarrow && - is(CommonType!(Range, Needles) == void)); -} - -@safe nothrow @nogc pure unittest -{ - import std.meta : AliasSeq; // used for better clarity - - static assert(!hasDifferentAutodecoding!(string, AliasSeq!(string, string))); - static assert(!hasDifferentAutodecoding!(wstring, AliasSeq!(wstring, wstring))); - static assert(!hasDifferentAutodecoding!(dstring, AliasSeq!(dstring, dstring))); - - // the needles needs auto-decoding, but the incoming range doesn't (or vice versa) - static assert(hasDifferentAutodecoding!(string, AliasSeq!(wstring, wstring))); - static assert(hasDifferentAutodecoding!(string, AliasSeq!(dstring, dstring))); - static assert(hasDifferentAutodecoding!(wstring, AliasSeq!(string, string))); - static assert(hasDifferentAutodecoding!(wstring, AliasSeq!(dstring, dstring))); - static assert(hasDifferentAutodecoding!(dstring, AliasSeq!(string, string))); - static assert(hasDifferentAutodecoding!(dstring, AliasSeq!(wstring, wstring))); - - // both (range, needle) need auto-decoding and don't share the same common type - static foreach (T; AliasSeq!(string, wstring, dstring)) - { - static assert(hasDifferentAutodecoding!(T, AliasSeq!(wstring, string))); - static assert(hasDifferentAutodecoding!(T, AliasSeq!(dstring, string))); - static assert(hasDifferentAutodecoding!(T, AliasSeq!(wstring, dstring))); - } -} - -// substitute -/** -Returns a range with all occurrences of `substs` in `r`. -replaced with their substitution. - -Single value replacements (`'Ăś'.substitute!('ä', 'a', 'Ăś', 'o', 'Ăź', 'u)`) are -supported as well and in $(BIGOH 1). - -Params: - r = an $(REF_ALTTEXT input range, isInputRange, std,range,primitives) - value = a single value which can be substituted in $(BIGOH 1) - substs = a set of replacements/substitutions - pred = the equality function to test if element(s) are equal to - a substitution - -Returns: a range with the substitutions replaced. - -See_Also: -$(REF replace, std, array) for an eager replace algorithm or -$(REF translate, std, string), and $(REF tr, std, string) -for string algorithms with translation tables. -*/ -template substitute(substs...) -if (substs.length >= 2 && isExpressions!substs) -{ - import std.range.primitives : ElementType; - import std.traits : CommonType; - - static assert(!(substs.length & 1), "The number of substitution parameters must be even"); - - /** - Substitute single values with compile-time substitution mappings. - Complexity: $(BIGOH 1) due to D's `switch` guaranteeing $(BIGOH 1); - */ - auto substitute(Value)(Value value) - if (isInputRange!Value || !is(CommonType!(Value, typeof(substs[0])) == void)) - { - static if (isInputRange!Value) - { - static if (!is(CommonType!(ElementType!Value, typeof(substs[0])) == void)) - { - // Substitute single range elements with compile-time substitution mappings - return value.map!(a => substitute(a)); - } - else static if (isInputRange!Value && - !is(CommonType!(ElementType!Value, ElementType!(typeof(substs[0]))) == void)) - { - // not implemented yet, fallback to runtime variant for now - return .substitute(value, substs); - } - else - { - static assert(0, `Compile-time substitutions must be elements or ranges of the same type of ` ~ - Value.stringof ~ `.`); - } - } - // Substitute single values with compile-time substitution mappings. - else // static if (!is(CommonType!(Value, typeof(substs[0])) == void)) - { - switch (value) - { - static foreach (i; 0 .. substs.length / 2) - case substs[2 * i]: - return substs[2 * i + 1]; - - default: return value; - } - } - } -} - -/// ditto -auto substitute(alias pred = (a, b) => a == b, R, Substs...)(R r, Substs substs) -if (isInputRange!R && Substs.length >= 2 && !is(CommonType!(Substs) == void)) -{ - import std.range.primitives : ElementType; - import std.meta : allSatisfy; - import std.traits : CommonType; - - static assert(!(Substs.length & 1), "The number of substitution parameters must be even"); - - enum n = Substs.length / 2; - - // Substitute individual elements - static if (!is(CommonType!(ElementType!R, Substs) == void)) - { - import std.functional : binaryFun; - - // Imitate a value closure to be @nogc - static struct ReplaceElement - { - private Substs substs; - - this(Substs substs) - { - this.substs = substs; - } - - auto opCall(E)(E e) - { - static foreach (i; 0 .. n) - if (binaryFun!pred(e, substs[2 * i])) - return substs[2 * i + 1]; - - return e; - } - } - auto er = ReplaceElement(substs); - return r.map!er; - } - // Substitute subranges - else static if (!is(CommonType!(ElementType!R, ElementType!(Substs[0])) == void) && - allSatisfy!(isForwardRange, Substs)) - { - import std.range : choose, take; - import std.meta : Stride; - - auto replaceElement(E)(E e) - { - alias ReturnA = typeof(e[0]); - alias ReturnB = typeof(substs[0 .. 1].take(1)); - - // 1-based index - const auto hitNr = e[1]; - switch (hitNr) - { - // no hit - case 0: - // use choose trick for non-common range - static if (is(CommonType!(ReturnA, ReturnB) == void)) - return choose(1, e[0], ReturnB.init); - else - return e[0]; - - // all replacements - static foreach (i; 0 .. n) - case i + 1: - // use choose trick for non-common ranges - static if (is(CommonType!(ReturnA, ReturnB) == void)) - return choose(0, e[0], substs[2 * i + 1].take(size_t.max)); - else - return substs[2 * i + 1].take(size_t.max); - default: - assert(0, "hitNr should always be found."); - } - } - - alias Ins = Stride!(2, Substs); - - static struct SubstituteSplitter - { - import std.range : drop; - import std.typecons : Tuple; - - private - { - typeof(R.init.drop(0)) rest; - Ins needles; - - typeof(R.init.take(0)) skip; // skip before next hit - alias Hit = size_t; // 0 iff no hit, otherwise hit in needles[index-1] - alias E = Tuple!(typeof(skip), Hit); - Hit hitNr; // hit number: 0 means no hit, otherwise index+1 to needles that matched - bool hasHit; // is there a replacement hit which should be printed? - - enum hasDifferentAutodecoding = .hasDifferentAutodecoding!(typeof(rest), Ins); - - // calculating the needle length for narrow strings might be expensive -> cache it - static if (hasDifferentAutodecoding) - ptrdiff_t[n] needleLengths = -1; - } - - this(R haystack, Ins needles) - { - this.rest = haystack.drop(0); - this.needles = needles; - if (!haystack.empty) - { - hasHit = true; - popFront; - } - static if (hasNested!(typeof(skip))) - skip = rest.take(0); - } - - /* If `skip` is non-empty, it's returned as (skip, 0) tuple - otherwise a similar (, hitNr) tuple is returned. - `replaceElement` maps based on the second item (`hitNr`). - */ - @property auto ref front() - { - assert(!empty, "Attempting to fetch the front of an empty substitute."); - return !skip.empty ? E(skip, 0) : E(typeof(skip).init, hitNr); - } - - static if (isInfinite!R) - enum empty = false; // propagate infiniteness - else - @property bool empty() - { - return skip.empty && !hasHit; - } - - /* If currently in a skipping phase => reset. - Otherwise try to find the next occurrence of `needles` - If valid match - - if there are elements before the match, set skip with these elements - (on the next popFront, the range will be in the skip state once) - - `rest`: advance to the end of the match - - set hasHit - Otherwise skip to the end - */ - void popFront() - { - assert(!empty, "Attempting to popFront an empty substitute."); - if (!skip.empty) - { - skip = typeof(skip).init; // jump over skip - } - else - { - import std.algorithm.searching : countUntil, find; - - auto match = rest.find!pred(needles); - - static if (needles.length >= 2) // variadic version of find (returns a tuple) - { - // find with variadic needles returns a (range, needleNr) tuple - // needleNr is a 1-based index - auto hitValue = match[0]; - hitNr = match[1]; - } - else - { - // find with one needle returns the range - auto hitValue = match; - hitNr = match.empty ? 0 : 1; - } - - if (hitNr == 0) // no more hits - { - skip = rest.take(size_t.max); - hasHit = false; - rest = typeof(rest).init; - } - else - { - auto hitLength = size_t.max; - switchL: switch (hitNr - 1) - { - static foreach (i; 0 .. n) - { - case i: - static if (hasDifferentAutodecoding) - { - import std.utf : codeLength; - - // cache calculated needle length - if (needleLengths[i] != -1) - hitLength = needleLengths[i]; - else - hitLength = needleLengths[i] = codeLength!dchar(needles[i]); - } - else - { - hitLength = needles[i].length; - } - break switchL; - } - default: - assert(0, "hitNr should always be found"); - } - - const pos = rest.countUntil(hitValue); - if (pos > 0) // match not at start of rest - skip = rest.take(pos); - - hasHit = true; - - // iff the source range and the substitutions are narrow strings, - // we can avoid calling the auto-decoding `popFront` (via drop) - static if (isNarrowString!(typeof(hitValue)) && !hasDifferentAutodecoding) - rest = hitValue[hitLength .. $]; - else - rest = hitValue.drop(hitLength); - } - } - } - } - - // extract inputs - Ins ins; - static foreach (i; 0 .. n) - ins[i] = substs[2 * i]; - - return SubstituteSplitter(r, ins) - .map!(a => replaceElement(a)) - .joiner; - } - else - { - static assert(0, "The substitutions must either substitute a single element or a save-able subrange."); - } -} - -/// -@safe pure unittest -{ - import std.algorithm.comparison : equal; - - // substitute single elements - assert("do_it".substitute('_', ' ').equal("do it")); - - // substitute multiple, single elements - assert("do_it".substitute('_', ' ', - 'd', 'g', - 'i', 't', - 't', 'o') - .equal("go to")); - - // substitute subranges - assert("do_it".substitute("_", " ", - "do", "done") - .equal("done it")); - - // substitution works for any ElementType - int[] x = [1, 2, 3]; - auto y = x.substitute(1, 0.1); - assert(y.equal([0.1, 2, 3])); - static assert(is(typeof(y.front) == double)); - - import std.range : retro; - assert([1, 2, 3].substitute(1, 0.1).retro.equal([3, 2, 0.1])); -} - -/// Use the faster compile-time overload -@safe pure unittest -{ - import std.algorithm.comparison : equal; - - // substitute subranges of a range - assert("apple_tree".substitute!("apple", "banana", - "tree", "shrub").equal("banana_shrub")); - - // substitute subranges of a range - assert("apple_tree".substitute!('a', 'b', - 't', 'f').equal("bpple_free")); - - // substitute values - assert('a'.substitute!('a', 'b', 't', 'f') == 'b'); -} - -/// Multiple substitutes -@safe pure unittest -{ - import std.algorithm.comparison : equal; - import std.range.primitives : ElementType; - - int[3] x = [1, 2, 3]; - auto y = x[].substitute(1, 0.1) - .substitute(0.1, 0.2); - static assert(is(typeof(y.front) == double)); - assert(y.equal([0.2, 2, 3])); - - auto z = "42".substitute('2', '3') - .substitute('3', '1'); - static assert(is(ElementType!(typeof(z)) == dchar)); - assert(equal(z, "41")); -} - -// Test the first example with compile-time overloads -@safe pure unittest -{ - import std.algorithm.comparison : equal; - - // substitute single elements - assert("do_it".substitute!('_', ' ').equal("do it")); - - // substitute multiple, single elements - assert("do_it".substitute!('_', ' ', - 'd', 'g', - 'i', 't', - 't', 'o') - .equal(`go to`)); - - // substitute subranges - assert("do_it".substitute!("_", " ", - "do", "done") - .equal("done it")); - - // substitution works for any ElementType - int[3] x = [1, 2, 3]; - auto y = x[].substitute!(1, 0.1); - assert(y.equal([0.1, 2, 3])); - static assert(is(typeof(y.front) == double)); - - import std.range : retro; - assert([1, 2, 3].substitute!(1, 0.1).retro.equal([3, 2, 0.1])); -} - -// test infinite ranges -@safe pure nothrow unittest -{ - import std.algorithm.comparison : equal; - import std.range : cycle, take; - - int[] x = [1, 2, 3]; - assert(x.cycle.substitute!(1, 0.1).take(4).equal([0.1, 2, 3, 0.1])); - assert(x.cycle.substitute(1, 0.1).take(4).equal([0.1, 2, 3, 0.1])); -} - -// test infinite ranges -@safe pure nothrow unittest -{ - import std.algorithm.comparison : equal; - import std.internal.test.dummyrange : AllDummyRanges; - - foreach (R; AllDummyRanges) - { - assert(R.init - .substitute!(2, 22, 3, 33, 5, 55, 9, 99) - .equal([1, 22, 33, 4, 55, 6, 7, 8, 99, 10])); - - assert(R.init - .substitute(2, 22, 3, 33, 5, 55, 9, 99) - .equal([1, 22, 33, 4, 55, 6, 7, 8, 99, 10])); - } -} - -// test multiple replacements -@safe pure unittest -{ - import std.algorithm.comparison : equal; - - assert("alpha.beta.gamma" - .substitute("alpha", "1", - "gamma", "3", - "beta", "2").equal("1.2.3")); - - assert("alpha.beta.gamma." - .substitute("alpha", "1", - "gamma", "3", - "beta", "2").equal("1.2.3.")); - - assert("beta.beta.beta" - .substitute("alpha", "1", - "gamma", "3", - "beta", "2").equal("2.2.2")); - - assert("alpha.alpha.alpha" - .substitute("alpha", "1", - "gamma", "3", - "beta", "2").equal("1.1.1")); -} - -// test combination of subrange + element replacement -@safe pure unittest -{ - import std.algorithm.comparison : equal; - - assert(("abcDe".substitute("a", "AA", - "b", "DD") - .substitute('A', 'y', - 'D', 'x', - 'e', '1')) - .equal("yyxxcx1")); -} - -// test const + immutable storage groups -@safe pure unittest -{ - import std.algorithm.comparison : equal; - - auto xyz_abc(T)(T value) - { - immutable a = "a"; - const b = "b"; - auto c = "c"; - return value.substitute!("x", a, - "y", b, - "z", c); - } - assert(xyz_abc("_x").equal("_a")); - assert(xyz_abc(".y.").equal(".b.")); - assert(xyz_abc("z").equal("c")); - assert(xyz_abc("w").equal("w")); -} - -// test with narrow strings (auto-decoding) and subranges -@safe pure unittest -{ - import std.algorithm.comparison : equal; - - assert("äöü€".substitute("ä", "b", "Ăź", "u").equal("bĂśu€")); - assert("äöü€".substitute!("ä", "b", "Ăź", "u").equal("bĂśu€")); - assert("ä...öü€".substitute("ä", "b", "Ăź", "u").equal("b...Ăśu€")); - - auto expected = "emoticons😄😅.😇😈Rock"; - assert("emoticons😄😅😆😇😈rock" - .substitute("r", "R", "😆", ".").equal(expected)); - assert("emoticons😄😅😆😇😈rock" - .substitute!("r", "R", "😆", ".").equal(expected)); -} - -// test with narrow strings (auto-decoding) and single elements -@safe pure unittest -{ - import std.algorithm.comparison : equal; - - assert("äöü€".substitute('ä', 'b', 'Ăź', 'u').equal("bĂśu€")); - assert("äöü€".substitute!('ä', 'b', 'Ăź', 'u').equal("bĂśu€")); - - auto expected = "emoticons😄😅.😇😈Rock"; - assert("emoticons😄😅😆😇😈rock" - .substitute('r', 'R', '😆', '.').equal(expected)); - assert("emoticons😄😅😆😇😈rock" - .substitute!('r', 'R', '😆', '.').equal(expected)); -} - -// test auto-decoding {n,w,d} strings X {n,w,d} strings -@safe pure unittest -{ - import std.algorithm.comparison : equal; - - assert("ääöü€".substitute("ä", "b", "Ăź", "u").equal("bbĂśu€")); - assert("ääöü€".substitute("ä"w, "b"w, "Ăź"w, "u"w).equal("bbĂśu€")); - assert("ääöü€".substitute("ä"d, "b"d, "Ăź"d, "u"d).equal("bbĂśu€")); - - assert("ääöü€"w.substitute("ä", "b", "Ăź", "u").equal("bbĂśu€")); - assert("ääöü€"w.substitute("ä"w, "b"w, "Ăź"w, "u"w).equal("bbĂśu€")); - assert("ääöü€"w.substitute("ä"d, "b"d, "Ăź"d, "u"d).equal("bbĂśu€")); - - assert("ääöü€"d.substitute("ä", "b", "Ăź", "u").equal("bbĂśu€")); - assert("ääöü€"d.substitute("ä"w, "b"w, "Ăź"w, "u"w).equal("bbĂśu€")); - assert("ääöü€"d.substitute("ä"d, "b"d, "Ăź"d, "u"d).equal("bbĂśu€")); - - // auto-decoding is done before by a different range - assert("ääöü€".filter!(a => true).substitute("ä", "b", "Ăź", "u").equal("bbĂśu€")); - assert("ääöü€".filter!(a => true).substitute("ä"w, "b"w, "Ăź"w, "u"w).equal("bbĂśu€")); - assert("ääöü€".filter!(a => true).substitute("ä"d, "b"d, "Ăź"d, "u"d).equal("bbĂśu€")); -} - -// test repeated replacement -@safe pure nothrow unittest -{ - import std.algorithm.comparison : equal; - - assert([1, 2, 3, 1, 1, 2].substitute(1, 0).equal([0, 2, 3, 0, 0, 2])); - assert([1, 2, 3, 1, 1, 2].substitute!(1, 0).equal([0, 2, 3, 0, 0, 2])); - assert([1, 2, 3, 1, 1, 2].substitute(1, 2, 2, 9).equal([2, 9, 3, 2, 2, 9])); -} - -// test @nogc for single element replacements -@safe @nogc unittest -{ - import std.algorithm.comparison : equal; - - static immutable arr = [1, 2, 3, 1, 1, 2]; - static immutable expected = [0, 2, 3, 0, 0, 2]; - - assert(arr.substitute!(1, 0).equal(expected)); - assert(arr.substitute(1, 0).equal(expected)); -} - -// test different range types -@safe pure nothrow unittest -{ - import std.algorithm.comparison : equal; - import std.internal.test.dummyrange : AllDummyRanges; - - static foreach (DummyType; AllDummyRanges) - {{ - DummyType dummyRange; - - // single substitution - dummyRange.substitute (2, 22).equal([1, 22, 3, 4, 5, 6, 7, 8, 9, 10]); - dummyRange.substitute!(2, 22).equal([1, 22, 3, 4, 5, 6, 7, 8, 9, 10]); - - // multiple substitution - dummyRange.substitute (2, 22, 5, 55, 7, 77).equal([1, 22, 3, 4, 55, 6, 77, 8, 9, 10]); - dummyRange.substitute!(2, 22, 5, 55, 7, 77).equal([1, 22, 3, 4, 55, 6, 77, 8, 9, 10]); - }} -} - -// https://issues.dlang.org/show_bug.cgi?id=19207 -@safe pure nothrow unittest -{ - import std.algorithm.comparison : equal; - assert([1, 2, 3, 4].substitute([1], [7]).equal([7, 2, 3, 4])); - assert([1, 2, 3, 4].substitute([2], [7]).equal([1, 7, 3, 4])); - assert([1, 2, 3, 4].substitute([4], [7]).equal([1, 2, 3, 7])); - assert([1, 2, 3, 4].substitute([2, 3], [7]).equal([1, 7, 4])); - assert([1, 2, 3, 4].substitute([3, 4], [7, 8]).equal([1, 2, 7, 8])); -} - -// tests recognizing empty base ranges -nothrow pure @safe unittest -{ - import std.utf : byCodeUnit; - import std.algorithm.comparison : equal; - - assert("".byCodeUnit.substitute('4', 'A').empty); - assert("".byCodeUnit.substitute('0', 'O', '5', 'S', '1', 'l').empty); - assert("".byCodeUnit.substitute("PKM".byCodeUnit, "PoKeMon".byCodeUnit).empty); - assert("".byCodeUnit.substitute - ( "ding".byCodeUnit, - "dong".byCodeUnit, - "click".byCodeUnit, - "clack".byCodeUnit, - "ping".byCodeUnit, - "latency".byCodeUnit - ).empty); -} - -// sum -/** -Sums elements of `r`, which must be a finite -$(REF_ALTTEXT input range, isInputRange, std,range,primitives). Although -conceptually `sum(r)` is equivalent to $(LREF fold)!((a, b) => a + -b)(r, 0), `sum` uses specialized algorithms to maximize accuracy, -as follows. - -$(UL -$(LI If $(REF ElementType, std,range,primitives)!R is a floating-point -type and `R` is a -$(REF_ALTTEXT random-access range, isRandomAccessRange, std,range,primitives) with -length and slicing, then `sum` uses the -$(HTTP en.wikipedia.org/wiki/Pairwise_summation, pairwise summation) -algorithm.) -$(LI If `ElementType!R` is a floating-point type and `R` is a -finite input range (but not a random-access range with slicing), then -`sum` uses the $(HTTP en.wikipedia.org/wiki/Kahan_summation, -Kahan summation) algorithm.) -$(LI In all other cases, a simple element by element addition is done.) -) - -For floating point inputs, calculations are made in -$(DDLINK spec/type, Types, `real`) -precision for `real` inputs and in `double` precision otherwise -(Note this is a special case that deviates from `fold`'s behavior, -which would have kept `float` precision for a `float` range). -For all other types, the calculations are done in the same type obtained -from from adding two elements of the range, which may be a different -type from the elements themselves (for example, in case of -$(DDSUBLINK spec/type,integer-promotions, integral promotion)). - -A seed may be passed to `sum`. Not only will this seed be used as an initial -value, but its type will override all the above, and determine the algorithm -and precision used for summation. If a seed is not passed, one is created with -the value of `typeof(r.front + r.front)(0)`, or `typeof(r.front + r.front).zero` -if no constructor exists that takes an int. - -Note that these specialized summing algorithms execute more primitive operations -than vanilla summation. Therefore, if in certain cases maximum speed is required -at expense of precision, one can use `fold!((a, b) => a + b)(r, 0)`, which -is not specialized for summation. - -Params: - seed = the initial value of the summation - r = a finite input range - -Returns: - The sum of all the elements in the range r. - */ -auto sum(R)(R r) -if (isInputRange!R && !isInfinite!R && is(typeof(r.front + r.front))) -{ - alias E = Unqual!(ElementType!R); - static if (isFloatingPoint!E) - alias Seed = typeof(E.init + 0.0); //biggest of double/real - else - alias Seed = typeof(r.front + r.front); - static if (is(typeof(Unqual!Seed(0)))) - enum seedValue = Unqual!Seed(0); - else static if (is(typeof({ Unqual!Seed tmp = Seed.zero; }))) - enum Unqual!Seed seedValue = Seed.zero; - else - static assert(false, - "Could not initiate an initial value for " ~ (Unqual!Seed).stringof - ~ ". Please supply an initial value manually."); - return sum(r, seedValue); -} -/// ditto -auto sum(R, E)(R r, E seed) -if (isInputRange!R && !isInfinite!R && is(typeof(seed = seed + r.front))) -{ - static if (isFloatingPoint!E) - { - static if (hasLength!R && hasSlicing!R) - { - if (r.empty) return seed; - return seed + sumPairwise!E(r); - } - else - return sumKahan!E(seed, r); - } - else - { - return reduce!"a + b"(seed, r); - } -} - -/// Ditto -@safe pure nothrow unittest -{ - import std.range; - - //simple integral sumation - assert(sum([ 1, 2, 3, 4]) == 10); - - //with integral promotion - assert(sum([false, true, true, false, true]) == 3); - assert(sum(ubyte.max.repeat(100)) == 25500); - - //The result may overflow - assert(uint.max.repeat(3).sum() == 4294967293U ); - //But a seed can be used to change the sumation primitive - assert(uint.max.repeat(3).sum(ulong.init) == 12884901885UL); - - //Floating point sumation - assert(sum([1.0, 2.0, 3.0, 4.0]) == 10); - - //Floating point operations have double precision minimum - static assert(is(typeof(sum([1F, 2F, 3F, 4F])) == double)); - assert(sum([1F, 2, 3, 4]) == 10); - - //Force pair-wise floating point sumation on large integers - import std.math.operations : isClose; - assert(iota(ulong.max / 2, ulong.max / 2 + 4096).sum(0.0) - .isClose((ulong.max / 2) * 4096.0 + 4096^^2 / 2)); -} - -// Pairwise summation http://en.wikipedia.org/wiki/Pairwise_summation -private auto sumPairwise(F, R)(R data) -if (isInputRange!R && !isInfinite!R) -{ - import core.bitop : bsf; - // Works for r with at least length < 2^^(64 + log2(16)), in keeping with the use of size_t - // elsewhere in std.algorithm and std.range on 64 bit platforms. The 16 in log2(16) comes - // from the manual unrolling in sumPairWise16 - F[64] store = void; - size_t idx = 0; - - void collapseStore(T)(T k) - { - auto lastToKeep = idx - cast(uint) bsf(k+1); - while (idx > lastToKeep) - { - store[idx - 1] += store[idx]; - --idx; - } - } - - static if (hasLength!R) - { - foreach (k; 0 .. data.length / 16) - { - static if (isRandomAccessRange!R && hasSlicing!R) - { - store[idx] = sumPairwise16!F(data); - data = data[16 .. data.length]; - } - else store[idx] = sumPairwiseN!(16, false, F)(data); - - collapseStore(k); - ++idx; - } - - size_t i = 0; - foreach (el; data) - { - store[idx] = el; - collapseStore(i); - ++idx; - ++i; - } - } - else - { - size_t k = 0; - while (!data.empty) - { - store[idx] = sumPairwiseN!(16, true, F)(data); - collapseStore(k); - ++idx; - ++k; - } - } - - F s = store[idx - 1]; - foreach_reverse (j; 0 .. idx - 1) - s += store[j]; - - return s; -} - -private auto sumPairwise16(F, R)(R r) -if (isRandomAccessRange!R) -{ - return (((cast(F) r[ 0] + r[ 1]) + (cast(F) r[ 2] + r[ 3])) - + ((cast(F) r[ 4] + r[ 5]) + (cast(F) r[ 6] + r[ 7]))) - + (((cast(F) r[ 8] + r[ 9]) + (cast(F) r[10] + r[11])) - + ((cast(F) r[12] + r[13]) + (cast(F) r[14] + r[15]))); -} - -private auto sumPair(bool needEmptyChecks, F, R)(ref R r) -if (isForwardRange!R && !isRandomAccessRange!R) -{ - static if (needEmptyChecks) if (r.empty) return F(0); - F s0 = r.front; - r.popFront(); - static if (needEmptyChecks) if (r.empty) return s0; - s0 += r.front; - r.popFront(); - return s0; -} - -private auto sumPairwiseN(size_t N, bool needEmptyChecks, F, R)(ref R r) -if (isForwardRange!R && !isRandomAccessRange!R) -{ - import std.math.traits : isPowerOf2; - static assert(isPowerOf2(N), "N must be a power of 2"); - static if (N == 2) return sumPair!(needEmptyChecks, F)(r); - else return sumPairwiseN!(N/2, needEmptyChecks, F)(r) - + sumPairwiseN!(N/2, needEmptyChecks, F)(r); -} - -// Kahan algo http://en.wikipedia.org/wiki/Kahan_summation_algorithm -private auto sumKahan(Result, R)(Result result, R r) -{ - static assert(isFloatingPoint!Result && isMutable!Result, "The type of" - ~ " Result must be a mutable floating point, not " - ~ Result.stringof); - Result c = 0; - for (; !r.empty; r.popFront()) - { - immutable y = r.front - c; - immutable t = result + y; - c = (t - result) - y; - result = t; - } - return result; -} - -@safe pure nothrow unittest -{ - static assert(is(typeof(sum([cast( byte) 1])) == int)); - static assert(is(typeof(sum([cast(ubyte) 1])) == int)); - static assert(is(typeof(sum([ 1, 2, 3, 4])) == int)); - static assert(is(typeof(sum([ 1U, 2U, 3U, 4U])) == uint)); - static assert(is(typeof(sum([ 1L, 2L, 3L, 4L])) == long)); - static assert(is(typeof(sum([1UL, 2UL, 3UL, 4UL])) == ulong)); - - int[] empty; - assert(sum(empty) == 0); - assert(sum([42]) == 42); - assert(sum([42, 43]) == 42 + 43); - assert(sum([42, 43, 44]) == 42 + 43 + 44); - assert(sum([42, 43, 44, 45]) == 42 + 43 + 44 + 45); -} - -@safe pure nothrow unittest -{ - static assert(is(typeof(sum([1.0, 2.0, 3.0, 4.0])) == double)); - static assert(is(typeof(sum([ 1F, 2F, 3F, 4F])) == double)); - const(float[]) a = [1F, 2F, 3F, 4F]; - assert(sum(a) == 10F); - static assert(is(typeof(sum(a)) == double)); - - double[] empty; - assert(sum(empty) == 0); - assert(sum([42.]) == 42); - assert(sum([42., 43.]) == 42 + 43); - assert(sum([42., 43., 44.]) == 42 + 43 + 44); - assert(sum([42., 43., 44., 45.5]) == 42 + 43 + 44 + 45.5); -} - -@safe pure nothrow unittest -{ - import std.container; - static assert(is(typeof(sum(SList!float()[])) == double)); - static assert(is(typeof(sum(SList!double()[])) == double)); - static assert(is(typeof(sum(SList!real()[])) == real)); - - assert(sum(SList!double()[]) == 0); - assert(sum(SList!double(1)[]) == 1); - assert(sum(SList!double(1, 2)[]) == 1 + 2); - assert(sum(SList!double(1, 2, 3)[]) == 1 + 2 + 3); - assert(sum(SList!double(1, 2, 3, 4)[]) == 10); -} - -// https://issues.dlang.org/show_bug.cgi?id=12434 -@safe pure nothrow unittest -{ - immutable a = [10, 20]; - auto s1 = sum(a); - assert(s1 == 30); - auto s2 = a.map!(x => x).sum; - assert(s2 == 30); -} - -@system unittest -{ - import std.bigint; - import std.range; - - immutable BigInt[] a = BigInt("1_000_000_000_000_000_000").repeat(10).array(); - immutable ulong[] b = (ulong.max/2).repeat(10).array(); - auto sa = a.sum(); - auto sb = b.sum(BigInt(0)); //reduce ulongs into bigint - assert(sa == BigInt("10_000_000_000_000_000_000")); - assert(sb == (BigInt(ulong.max/2) * 10)); -} - -@safe pure nothrow @nogc unittest -{ - import std.range; - foreach (n; iota(50)) - assert(repeat(1.0, n).sum == n); -} - -// Issue 19525 -@safe unittest -{ - import std.datetime : Duration, minutes; - assert([1.minutes].sum() == 1.minutes); -} - -/** -Finds the mean (colloquially known as the average) of a range. - -For built-in numerical types, accurate Knuth & Welford mean calculation -is used. For user-defined types, element by element summation is used. -Additionally an extra parameter `seed` is needed in order to correctly -seed the summation with the equivalent to `0`. - -The first overload of this function will return `T.init` if the range -is empty. However, the second overload will return `seed` on empty ranges. - -This function is $(BIGOH r.length). - -Params: - T = The type of the return value. - r = An $(REF_ALTTEXT input range, isInputRange, std,range,primitives) - seed = For user defined types. Should be equivalent to `0`. - -Returns: - The mean of `r` when `r` is non-empty. -*/ -T mean(T = double, R)(R r) -if (isInputRange!R && - isNumeric!(ElementType!R) && - !isInfinite!R) -{ - if (r.empty) - return T.init; - - Unqual!T meanRes = 0; - size_t i = 1; - - // Knuth & Welford mean calculation - // division per element is slower, but more accurate - for (; !r.empty; r.popFront()) - { - T delta = r.front - meanRes; - meanRes += delta / i++; - } - - return meanRes; -} - -/// ditto -auto mean(R, T)(R r, T seed) -if (isInputRange!R && - !isNumeric!(ElementType!R) && - is(typeof(r.front + seed)) && - is(typeof(r.front / size_t(1))) && - !isInfinite!R) -{ - import std.algorithm.iteration : sum, reduce; - - // per item division vis-a-vis the previous overload is too - // inaccurate for integer division, which the user defined - // types might be representing - static if (hasLength!R) - { - if (r.length == 0) - return seed; - - return sum(r, seed) / r.length; - } - else - { - import std.typecons : tuple; - - if (r.empty) - return seed; - - auto pair = reduce!((a, b) => tuple(a[0] + 1, a[1] + b)) - (tuple(size_t(0), seed), r); - return pair[1] / pair[0]; - } -} - -/// -@safe @nogc pure nothrow unittest -{ - import std.math.operations : isClose; - import std.math.traits : isNaN; - - static immutable arr1 = [1, 2, 3]; - static immutable arr2 = [1.5, 2.5, 12.5]; - - assert(arr1.mean.isClose(2)); - assert(arr2.mean.isClose(5.5)); - - assert(arr1[0 .. 0].mean.isNaN); -} - -@safe pure nothrow unittest -{ - import std.internal.test.dummyrange : ReferenceInputRange; - import std.math.operations : isClose; - - auto r1 = new ReferenceInputRange!int([1, 2, 3]); - assert(r1.mean.isClose(2)); - - auto r2 = new ReferenceInputRange!double([1.5, 2.5, 12.5]); - assert(r2.mean.isClose(5.5)); -} - -// Test user defined types -@system pure unittest -{ - import std.bigint : BigInt; - import std.internal.test.dummyrange : ReferenceInputRange; - import std.math.operations : isClose; - - auto bigint_arr = [BigInt("1"), BigInt("2"), BigInt("3"), BigInt("6")]; - auto bigint_arr2 = new ReferenceInputRange!BigInt([ - BigInt("1"), BigInt("2"), BigInt("3"), BigInt("6") - ]); - assert(bigint_arr.mean(BigInt(0)) == BigInt("3")); - assert(bigint_arr2.mean(BigInt(0)) == BigInt("3")); - - BigInt[] bigint_arr3 = []; - assert(bigint_arr3.mean(BigInt(0)) == BigInt(0)); - - struct MyFancyDouble - { - double v; - alias v this; - } - - // both overloads - auto d_arr = [MyFancyDouble(10), MyFancyDouble(15), MyFancyDouble(30)]; - assert(mean!(double)(cast(double[]) d_arr).isClose(18.33333333)); - assert(mean(d_arr, MyFancyDouble(0)).isClose(18.33333333)); -} - -// uniq -/** -Lazily iterates unique consecutive elements of the given range, which is -assumed to be sorted (functionality akin to the -$(HTTP wikipedia.org/wiki/_Uniq, _uniq) system -utility). Equivalence of elements is assessed by using the predicate -`pred`, by default `"a == b"`. The predicate is passed to -$(REF binaryFun, std,functional), and can either accept a string, or any callable -that can be executed via `pred(element, element)`. If the given range is -bidirectional, `uniq` also yields a -$(REF_ALTTEXT bidirectional range, isBidirectionalRange, std,range,primitives). - -Params: - pred = Predicate for determining equivalence between range elements. - r = An $(REF_ALTTEXT input range, isInputRange, std,range,primitives) of - elements to filter. - -Returns: - An $(REF_ALTTEXT input range, isInputRange, std,range,primitives) of - consecutively unique elements in the original range. If `r` is also a - forward range or bidirectional range, the returned range will be likewise. -*/ -auto uniq(alias pred = "a == b", Range)(Range r) -if (isInputRange!Range && is(typeof(binaryFun!pred(r.front, r.front)) == bool)) -{ - return UniqResult!(binaryFun!pred, Range)(r); -} - -/// -@safe unittest -{ - import std.algorithm.comparison : equal; - import std.algorithm.mutation : copy; - - int[] arr = [ 1, 2, 2, 2, 2, 3, 4, 4, 4, 5 ]; - assert(equal(uniq(arr), [ 1, 2, 3, 4, 5 ][])); - - // Filter duplicates in-place using copy - arr.length -= arr.uniq().copy(arr).length; - assert(arr == [ 1, 2, 3, 4, 5 ]); - - // Note that uniqueness is only determined consecutively; duplicated - // elements separated by an intervening different element will not be - // eliminated: - assert(equal(uniq([ 1, 1, 2, 1, 1, 3, 1]), [1, 2, 1, 3, 1])); -} - -private struct UniqResult(alias pred, Range) -{ - Range _input; - - this(Range input) - { - _input = input; - } - - auto opSlice() - { - return this; - } - - void popFront() - { - assert(!empty, "Attempting to popFront an empty uniq."); - auto last = _input.front; - do - { - _input.popFront(); - } - while (!_input.empty && pred(last, _input.front)); - } - - @property ElementType!Range front() - { - assert(!empty, "Attempting to fetch the front of an empty uniq."); - return _input.front; - } - - static if (isBidirectionalRange!Range) - { - void popBack() - { - assert(!empty, "Attempting to popBack an empty uniq."); - auto last = _input.back; - do - { - _input.popBack(); - } - while (!_input.empty && pred(last, _input.back)); - } - - @property ElementType!Range back() - { - assert(!empty, "Attempting to fetch the back of an empty uniq."); - return _input.back; - } - } - - static if (isInfinite!Range) - { - enum bool empty = false; // Propagate infiniteness. - } - else - { - @property bool empty() { return _input.empty; } - } - - static if (isForwardRange!Range) - { - @property typeof(this) save() { - return typeof(this)(_input.save); - } - } -} - -@safe unittest -{ - import std.algorithm.comparison : equal; - import std.internal.test.dummyrange; - import std.range; - - int[] arr = [ 1, 2, 2, 2, 2, 3, 4, 4, 4, 5 ]; - auto r = uniq(arr); - static assert(isForwardRange!(typeof(r))); - - assert(equal(r, [ 1, 2, 3, 4, 5 ][])); - assert(equal(retro(r), retro([ 1, 2, 3, 4, 5 ][]))); - - foreach (DummyType; AllDummyRanges) - { - DummyType d; - auto u = uniq(d); - assert(equal(u, [1,2,3,4,5,6,7,8,9,10])); - - static assert(d.rt == RangeType.Input || isForwardRange!(typeof(u))); - - static if (d.rt >= RangeType.Bidirectional) - { - assert(equal(retro(u), [10,9,8,7,6,5,4,3,2,1])); - } - } -} - -// https://issues.dlang.org/show_bug.cgi?id=17264 -@safe unittest -{ - import std.algorithm.comparison : equal; - - const(int)[] var = [0, 1, 1, 2]; - assert(var.uniq.equal([0, 1, 2])); -} - -/** -Lazily computes all _permutations of `r` using $(HTTP -en.wikipedia.org/wiki/Heap%27s_algorithm, Heap's algorithm). - -Params: - Range = the range type - r = the $(REF_ALTTEXT random access range, isRandomAccessRange, std,range,primitives) - to find the permutations for. -Returns: - A $(REF_ALTTEXT forward range, isForwardRange, std,range,primitives) - of elements of which are an $(REF indexed, std,range) view into `r`. - -See_Also: -$(REF nextPermutation, std,algorithm,sorting). -*/ -Permutations!Range permutations(Range)(Range r) -{ - static assert(isRandomAccessRange!Range, Range.stringof, - " must be a RandomAccessRange"); - static assert(hasLength!Range, Range.stringof - , " must have a length"); - - return typeof(return)(r); -} - -/// ditto -struct Permutations(Range) -{ - static assert(isRandomAccessRange!Range, Range.stringof, - " must be a RandomAccessRange"); - static assert(hasLength!Range, Range.stringof - , " must have a length"); - - private size_t[] _indices, _state; - private Range _r; - private bool _empty; - - /// - this(Range r) - { - import std.array : array; - import std.range : iota; - - this._r = r; - _state = r.length ? new size_t[r.length-1] : null; - _indices = iota(size_t(r.length)).array; - _empty = r.length == 0; - } - private this(size_t[] indices, size_t[] state, Range r, bool empty_) - { - _indices = indices; - _state = state; - _r = r; - _empty = empty_; - } - /// Returns: `true` if the range is empty, `false` otherwise. - @property bool empty() const pure nothrow @safe @nogc - { - return _empty; - } - - /// Returns: the front of the range - @property auto front() - { - import std.range : indexed; - return _r.indexed(_indices); - } - - /// - void popFront() - { - void next(int n) - { - import std.algorithm.mutation : swap; - - if (n > _indices.length) - { - _empty = true; - return; - } - - if (n % 2 == 1) - swap(_indices[0], _indices[n-1]); - else - swap(_indices[_state[n-2]], _indices[n-1]); - - if (++_state[n-2] == n) - { - _state[n-2] = 0; - next(n+1); - } - } - - next(2); - } - /// Returns: an independent copy of the permutations range. - auto save() - { - return typeof(this)(_indices.dup, _state.dup, _r.save, _empty); - } -} - -/// -@safe unittest -{ - import std.algorithm.comparison : equal; - import std.range : iota; - assert(equal!equal(iota(3).permutations, - [[0, 1, 2], - [1, 0, 2], - [2, 0, 1], - [0, 2, 1], - [1, 2, 0], - [2, 1, 0]])); -} - -@safe unittest -{ - import std.algorithm.comparison : equal; - import std.range : ElementType; - import std.array : array; - auto p = [1, 2, 3].permutations; - auto x = p.save.front; - p.popFront; - auto y = p.front; - assert(x != y); -} diff --git a/phobos/std/algorithm/mutation.d b/phobos/std/algorithm/mutation.d deleted file mode 100644 index fbef28e..0000000 --- a/phobos/std/algorithm/mutation.d +++ /dev/null @@ -1,3321 +0,0 @@ -// Written in the D programming language. -/** -This is a submodule of $(MREF std, algorithm). -It contains generic mutation algorithms. - -$(SCRIPT inhibitQuickIndex = 1;) -$(BOOKTABLE Cheat Sheet, -$(TR $(TH Function Name) $(TH Description)) -$(T2 bringToFront, - If `a = [1, 2, 3]` and `b = [4, 5, 6, 7]`, - `bringToFront(a, b)` leaves `a = [4, 5, 6]` and - `b = [7, 1, 2, 3]`.) -$(T2 copy, - Copies a range to another. If - `a = [1, 2, 3]` and `b = new int[5]`, then `copy(a, b)` - leaves `b = [1, 2, 3, 0, 0]` and returns `b[3 .. $]`.) -$(T2 fill, - Fills a range with a pattern, - e.g., if `a = new int[3]`, then `fill(a, 4)` - leaves `a = [4, 4, 4]` and `fill(a, [3, 4])` leaves - `a = [3, 4, 3]`.) -$(T2 initializeAll, - If `a = [1.2, 3.4]`, then `initializeAll(a)` leaves - `a = [double.init, double.init]`.) -$(T2 move, - `move(a, b)` moves `a` into `b`. `move(a)` reads `a` - destructively when necessary.) -$(T2 moveEmplace, - Similar to `move` but assumes `target` is uninitialized.) -$(T2 moveAll, - Moves all elements from one range to another.) -$(T2 moveEmplaceAll, - Similar to `moveAll` but assumes all elements in `target` are uninitialized.) -$(T2 moveSome, - Moves as many elements as possible from one range to another.) -$(T2 moveEmplaceSome, - Similar to `moveSome` but assumes all elements in `target` are uninitialized.) -$(T2 remove, - Removes elements from a range in-place, and returns the shortened - range.) -$(T2 reverse, - If `a = [1, 2, 3]`, `reverse(a)` changes it to `[3, 2, 1]`.) -$(T2 strip, - Strips all leading and trailing elements equal to a value, or that - satisfy a predicate. - If `a = [1, 1, 0, 1, 1]`, then `strip(a, 1)` and - `strip!(e => e == 1)(a)` returns `[0]`.) -$(T2 stripLeft, - Strips all leading elements equal to a value, or that satisfy a - predicate. If `a = [1, 1, 0, 1, 1]`, then `stripLeft(a, 1)` and - `stripLeft!(e => e == 1)(a)` returns `[0, 1, 1]`.) -$(T2 stripRight, - Strips all trailing elements equal to a value, or that satisfy a - predicate. - If `a = [1, 1, 0, 1, 1]`, then `stripRight(a, 1)` and - `stripRight!(e => e == 1)(a)` returns `[1, 1, 0]`.) -$(T2 swap, - Swaps two values.) -$(T2 swapAt, - Swaps two values by indices.) -$(T2 swapRanges, - Swaps all elements of two ranges.) -$(T2 uninitializedFill, - Fills a range (assumed uninitialized) with a value.) -) - -Copyright: Andrei Alexandrescu 2008-. - -License: $(HTTP boost.org/LICENSE_1_0.txt, Boost License 1.0). - -Authors: $(HTTP erdani.com, Andrei Alexandrescu) - -Source: $(PHOBOSSRC std/algorithm/mutation.d) - -Macros: -T2=$(TR $(TDNW $(LREF $1)) $(TD $+)) - */ -module std.algorithm.mutation; - -import std.range.primitives; -import std.traits : isArray, isAssignable, isBlitAssignable, isNarrowString, - Unqual, isSomeChar, isMutable; -import std.meta : allSatisfy; -import std.typecons : tuple, Tuple; - -// bringToFront -/** -`bringToFront` takes two ranges `front` and `back`, which may -be of different types. Considering the concatenation of `front` and -`back` one unified range, `bringToFront` rotates that unified -range such that all elements in `back` are brought to the beginning -of the unified range. The relative ordering of elements in `front` -and `back`, respectively, remains unchanged. - -The `bringToFront` function treats strings at the code unit -level and it is not concerned with Unicode character integrity. -`bringToFront` is designed as a function for moving elements -in ranges, not as a string function. - -Performs $(BIGOH max(front.length, back.length)) evaluations of $(D -swap). - -The `bringToFront` function can rotate elements in one buffer left or right, swap -buffers of equal length, and even move elements across disjoint -buffers of different types and different lengths. - -Preconditions: - -Either `front` and `back` are disjoint, or `back` is -reachable from `front` and `front` is not reachable from $(D -back). - -Params: - front = an $(REF_ALTTEXT input range, isInputRange, std,range,primitives) - back = a $(REF_ALTTEXT forward range, isForwardRange, std,range,primitives) - -Returns: - The number of elements brought to the front, i.e., the length of `back`. - -See_Also: - $(LINK2 http://en.cppreference.com/w/cpp/algorithm/rotate, STL's `rotate`) -*/ -size_t bringToFront(InputRange, ForwardRange)(InputRange front, ForwardRange back) -if (isInputRange!InputRange && isForwardRange!ForwardRange) -{ - import std.string : representation; - - static if (isNarrowString!InputRange) - { - auto frontW = representation(front); - } - else - { - alias frontW = front; - } - static if (isNarrowString!ForwardRange) - { - auto backW = representation(back); - } - else - { - alias backW = back; - } - - return bringToFrontImpl(frontW, backW); -} - -/** -The simplest use of `bringToFront` is for rotating elements in a -buffer. For example: -*/ -@safe unittest -{ - auto arr = [4, 5, 6, 7, 1, 2, 3]; - auto p = bringToFront(arr[0 .. 4], arr[4 .. $]); - assert(p == arr.length - 4); - assert(arr == [ 1, 2, 3, 4, 5, 6, 7 ]); -} - -/** -The `front` range may actually "step over" the `back` -range. This is very useful with forward ranges that cannot compute -comfortably right-bounded subranges like `arr[0 .. 4]` above. In -the example below, `r2` is a right subrange of `r1`. -*/ -@safe unittest -{ - import std.algorithm.comparison : equal; - import std.container : SList; - import std.range.primitives : popFrontN; - - auto list = SList!(int)(4, 5, 6, 7, 1, 2, 3); - auto r1 = list[]; - auto r2 = list[]; popFrontN(r2, 4); - assert(equal(r2, [ 1, 2, 3 ])); - bringToFront(r1, r2); - assert(equal(list[], [ 1, 2, 3, 4, 5, 6, 7 ])); -} - -/** -Elements can be swapped across ranges of different types: -*/ -@safe unittest -{ - import std.algorithm.comparison : equal; - import std.container : SList; - - auto list = SList!(int)(4, 5, 6, 7); - auto vec = [ 1, 2, 3 ]; - bringToFront(list[], vec); - assert(equal(list[], [ 1, 2, 3, 4 ])); - assert(equal(vec, [ 5, 6, 7 ])); -} - -/** -Unicode integrity is not preserved: -*/ -@safe unittest -{ - import std.string : representation; - auto ar = representation("a".dup); - auto br = representation("ç".dup); - - bringToFront(ar, br); - - auto a = cast(char[]) ar; - auto b = cast(char[]) br; - - // Illegal UTF-8 - assert(a == "\303"); - // Illegal UTF-8 - assert(b == "\247a"); -} - -private size_t bringToFrontImpl(InputRange, ForwardRange)(InputRange front, ForwardRange back) -if (isInputRange!InputRange && isForwardRange!ForwardRange) -{ - import std.array : sameHead; - import std.range : take, Take; - enum bool sameHeadExists = is(typeof(front.sameHead(back))); - size_t result; - - for (bool semidone; !front.empty && !back.empty; ) - { - static if (sameHeadExists) - { - if (front.sameHead(back)) break; // shortcut - } - // Swap elements until front and/or back ends. - auto back0 = back.save; - size_t nswaps; - do - { - static if (sameHeadExists) - { - // Detect the stepping-over condition. - if (front.sameHead(back0)) back0 = back.save; - } - swapFront(front, back); - ++nswaps; - front.popFront(); - back.popFront(); - } - while (!front.empty && !back.empty); - - if (!semidone) result += nswaps; - - // Now deal with the remaining elements. - if (back.empty) - { - if (front.empty) break; - // Right side was shorter, which means that we've brought - // all the back elements to the front. - semidone = true; - // Next pass: bringToFront(front, back0) to adjust the rest. - back = back0; - } - else - { - assert(front.empty, "Expected front to be empty"); - // Left side was shorter. Let's step into the back. - static if (is(InputRange == Take!ForwardRange)) - { - front = take(back0, nswaps); - } - else - { - immutable subresult = bringToFront(take(back0, nswaps), - back); - if (!semidone) result += subresult; - break; // done - } - } - } - return result; -} - -@safe unittest -{ - import std.algorithm.comparison : equal; - import std.conv : text; - import std.random : Random = Xorshift, uniform; - - // a more elaborate test - { - auto rnd = Random(123_456_789); - int[] a = new int[uniform(100, 200, rnd)]; - int[] b = new int[uniform(100, 200, rnd)]; - foreach (ref e; a) e = uniform(-100, 100, rnd); - foreach (ref e; b) e = uniform(-100, 100, rnd); - int[] c = a ~ b; - // writeln("a= ", a); - // writeln("b= ", b); - auto n = bringToFront(c[0 .. a.length], c[a.length .. $]); - //writeln("c= ", c); - assert(n == b.length); - assert(c == b ~ a, text(c, "\n", a, "\n", b)); - } - // different types, moveFront, no sameHead - { - static struct R(T) - { - T[] data; - size_t i; - @property - { - R save() { return this; } - bool empty() { return i >= data.length; } - T front() { return data[i]; } - T front(real e) { return data[i] = cast(T) e; } - } - void popFront() { ++i; } - } - auto a = R!int([1, 2, 3, 4, 5]); - auto b = R!real([6, 7, 8, 9]); - auto n = bringToFront(a, b); - assert(n == 4); - assert(a.data == [6, 7, 8, 9, 1]); - assert(b.data == [2, 3, 4, 5]); - } - // front steps over back - { - int[] arr, r1, r2; - - // back is shorter - arr = [4, 5, 6, 7, 1, 2, 3]; - r1 = arr; - r2 = arr[4 .. $]; - bringToFront(r1, r2) == 3 || assert(0); - assert(equal(arr, [1, 2, 3, 4, 5, 6, 7])); - - // front is shorter - arr = [5, 6, 7, 1, 2, 3, 4]; - r1 = arr; - r2 = arr[3 .. $]; - bringToFront(r1, r2) == 4 || assert(0); - assert(equal(arr, [1, 2, 3, 4, 5, 6, 7])); - } - - // https://issues.dlang.org/show_bug.cgi?id=16959 - auto arr = ['4', '5', '6', '7', '1', '2', '3']; - auto p = bringToFront(arr[0 .. 4], arr[4 .. $]); - - assert(p == arr.length - 4); - assert(arr == ['1', '2', '3', '4', '5', '6', '7']); -} - -// Tests if types are arrays and support slice assign. -private enum bool areCopyCompatibleArrays(T1, T2) = - isArray!T1 && isArray!T2 && is(typeof(T2.init[] = T1.init[])); - -// copy -/** -Copies the content of `source` into `target` and returns the -remaining (unfilled) part of `target`. - -Preconditions: `target` shall have enough room to accommodate -the entirety of `source`. - -Params: - source = an $(REF_ALTTEXT input range, isInputRange, std,range,primitives) - target = an output range - -Returns: - The unfilled part of target - */ -TargetRange copy(SourceRange, TargetRange)(SourceRange source, TargetRange target) -if (isInputRange!SourceRange && isOutputRange!(TargetRange, ElementType!SourceRange)) -{ - static if (areCopyCompatibleArrays!(SourceRange, TargetRange)) - { - const tlen = target.length; - const slen = source.length; - assert(tlen >= slen, - "Cannot copy a source range into a smaller target range."); - - immutable overlaps = () @trusted { - return source.ptr < target.ptr + tlen && - target.ptr < source.ptr + slen; }(); - - if (overlaps) - { - if (source.ptr < target.ptr) - { - foreach_reverse (idx; 0 .. slen) - target[idx] = source[idx]; - } - else - { - foreach (idx; 0 .. slen) - target[idx] = source[idx]; - } - return target[slen .. tlen]; - } - else - { - // Array specialization. This uses optimized memory copying - // routines under the hood and is about 10-20x faster than the - // generic implementation. - target[0 .. slen] = source[]; - return target[slen .. $]; - } - } - else - { - // Specialize for 2 random access ranges. - // Typically 2 random access ranges are faster iterated by common - // index than by x.popFront(), y.popFront() pair - static if (isRandomAccessRange!SourceRange && - hasLength!SourceRange && - hasSlicing!TargetRange && - isRandomAccessRange!TargetRange && - hasLength!TargetRange) - { - auto len = source.length; - foreach (idx; 0 .. len) - target[idx] = source[idx]; - return target[len .. target.length]; - } - else - { - foreach (element; source) - put(target, element); - return target; - } - } -} - -/// -@safe unittest -{ - int[] a = [ 1, 5 ]; - int[] b = [ 9, 8 ]; - int[] buf = new int[](a.length + b.length + 10); - auto rem = a.copy(buf); // copy a into buf - rem = b.copy(rem); // copy b into remainder of buf - assert(buf[0 .. a.length + b.length] == [1, 5, 9, 8]); - assert(rem.length == 10); // unused slots in buf -} - -/** -As long as the target range elements support assignment from source -range elements, different types of ranges are accepted: -*/ -@safe unittest -{ - float[] src = [ 1.0f, 5 ]; - double[] dest = new double[src.length]; - src.copy(dest); -} - -/** -To _copy at most `n` elements from a range, you may want to use -$(REF take, std,range): -*/ -@safe unittest -{ - import std.range; - int[] src = [ 1, 5, 8, 9, 10 ]; - auto dest = new int[](3); - src.take(dest.length).copy(dest); - assert(dest == [ 1, 5, 8 ]); -} - -/** -To _copy just those elements from a range that satisfy a predicate, -use $(LREF filter): -*/ -@safe unittest -{ - import std.algorithm.iteration : filter; - int[] src = [ 1, 5, 8, 9, 10, 1, 2, 0 ]; - auto dest = new int[src.length]; - auto rem = src - .filter!(a => (a & 1) == 1) - .copy(dest); - assert(dest[0 .. $ - rem.length] == [ 1, 5, 9, 1 ]); -} - -/** -$(REF retro, std,range) can be used to achieve behavior similar to -$(LINK2 http://en.cppreference.com/w/cpp/algorithm/copy_backward, STL's `copy_backward`'): -*/ -@safe unittest -{ - import std.algorithm, std.range; - int[] src = [1, 2, 4]; - int[] dest = [0, 0, 0, 0, 0]; - src.retro.copy(dest.retro); - assert(dest == [0, 0, 1, 2, 4]); -} - -// Test CTFE copy. -@safe unittest -{ - enum c = copy([1,2,3], [4,5,6,7]); - assert(c == [7]); -} - - -@safe unittest -{ - import std.algorithm.iteration : filter; - - { - int[] a = [ 1, 5 ]; - int[] b = [ 9, 8 ]; - auto e = copy(filter!("a > 1")(a), b); - assert(b[0] == 5 && e.length == 1); - } - - { - int[] a = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]; - copy(a[5 .. 10], a[4 .. 9]); - assert(a[4 .. 9] == [6, 7, 8, 9, 10]); - } - - // https://issues.dlang.org/show_bug.cgi?id=21724 - { - int[] a = [1, 2, 3, 4]; - copy(a[0 .. 2], a[1 .. 3]); - assert(a == [1, 1, 2, 4]); - } - - // https://issues.dlang.org/show_bug.cgi?id=7898 - { - enum v = - { - import std.algorithm; - int[] arr1 = [10, 20, 30, 40, 50]; - int[] arr2 = arr1.dup; - copy(arr1, arr2); - return 35; - }(); - assert(v == 35); - } -} - -// https://issues.dlang.org/show_bug.cgi?id=13650 -@safe unittest -{ - import std.meta : AliasSeq; - static foreach (Char; AliasSeq!(char, wchar, dchar)) - {{ - Char[3] a1 = "123"; - Char[6] a2 = "456789"; - assert(copy(a1[], a2[]) is a2[3..$]); - assert(a1[] == "123"); - assert(a2[] == "123789"); - }} -} - -// https://issues.dlang.org/show_bug.cgi?id=18804 -@safe unittest -{ - static struct NullSink - { - void put(E)(E) {} - } - int line = 0; - struct R - { - int front; - @property bool empty() { return line == 1; } - void popFront() { line = 1; } - } - R r; - copy(r, NullSink()); - assert(line == 1); -} - -/** -Assigns `value` to each element of input range `range`. - -Alternatively, instead of using a single `value` to fill the `range`, -a `filler` $(REF_ALTTEXT forward range, isForwardRange, std,range,primitives) -can be provided. The length of `filler` and `range` do not need to match, but -`filler` must not be empty. - -Params: - range = An - $(REF_ALTTEXT input range, isInputRange, std,range,primitives) - that exposes references to its elements and has assignable - elements - value = Assigned to each element of range - filler = A - $(REF_ALTTEXT forward range, isForwardRange, std,range,primitives) - representing the _fill pattern. - -Throws: If `filler` is empty. - -See_Also: - $(LREF uninitializedFill) - $(LREF initializeAll) - */ -void fill(Range, Value)(auto ref Range range, auto ref Value value) -if ((isInputRange!Range && is(typeof(range.front = value)) || - isSomeChar!Value && is(typeof(range[] = value)))) -{ - alias T = ElementType!Range; - - static if (is(typeof(range[] = value))) - { - range[] = value; - } - else static if (is(typeof(range[] = T(value)))) - { - range[] = T(value); - } - else - { - for ( ; !range.empty; range.popFront() ) - { - range.front = value; - } - } -} - -/// -@safe unittest -{ - int[] a = [ 1, 2, 3, 4 ]; - fill(a, 5); - assert(a == [ 5, 5, 5, 5 ]); -} - -// test fallback on mutable narrow strings -// https://issues.dlang.org/show_bug.cgi?id=16342 -@safe unittest -{ - char[] chars = ['a', 'b']; - fill(chars, 'c'); - assert(chars == "cc"); - - char[2] chars2 = ['a', 'b']; - fill(chars2, 'c'); - assert(chars2 == "cc"); - - wchar[] wchars = ['a', 'b']; - fill(wchars, wchar('c')); - assert(wchars == "cc"w); - - dchar[] dchars = ['a', 'b']; - fill(dchars, dchar('c')); - assert(dchars == "cc"d); -} - -@nogc @safe unittest -{ - const(char)[] chars; - assert(chars.length == 0); - static assert(!__traits(compiles, fill(chars, 'c'))); - wstring wchars; - assert(wchars.length == 0); - static assert(!__traits(compiles, fill(wchars, wchar('c')))); -} - -@nogc @safe unittest -{ - char[] chars; - fill(chars, 'c'); - assert(chars == ""c); -} - -@safe unittest -{ - shared(char)[] chrs = ['r']; - fill(chrs, 'c'); - assert(chrs == [shared(char)('c')]); -} - -@nogc @safe unittest -{ - struct Str(size_t len) - { - private char[len] _data; - void opIndexAssign(char value) @safe @nogc - {_data[] = value;} - } - Str!2 str; - str.fill(':'); - assert(str._data == "::"); -} - -@safe unittest -{ - char[] chars = ['a','b','c','d']; - chars[1 .. 3].fill(':'); - assert(chars == "a::d"); -} -// end https://issues.dlang.org/show_bug.cgi?id=16342 - -@safe unittest -{ - import std.conv : text; - import std.internal.test.dummyrange; - - int[] a = [ 1, 2, 3 ]; - fill(a, 6); - assert(a == [ 6, 6, 6 ], text(a)); - - void fun0() - { - foreach (i; 0 .. 1000) - { - foreach (ref e; a) e = 6; - } - } - void fun1() { foreach (i; 0 .. 1000) fill(a, 6); } - - // fill should accept InputRange - alias InputRange = DummyRange!(ReturnBy.Reference, Length.No, RangeType.Input); - enum filler = uint.max; - InputRange range; - fill(range, filler); - foreach (value; range.arr) - assert(value == filler); -} - -@safe unittest -{ - //ER8638_1 IS_NOT self assignable - static struct ER8638_1 - { - void opAssign(int){} - } - - //ER8638_1 IS self assignable - static struct ER8638_2 - { - void opAssign(ER8638_2){} - void opAssign(int){} - } - - auto er8638_1 = new ER8638_1[](10); - auto er8638_2 = new ER8638_2[](10); - er8638_1.fill(5); //generic case - er8638_2.fill(5); //opSlice(T.init) case -} - -@safe unittest -{ - { - int[] a = [1, 2, 3]; - immutable(int) b = 0; - a.fill(b); - assert(a == [0, 0, 0]); - } - { - double[] a = [1, 2, 3]; - immutable(int) b = 0; - a.fill(b); - assert(a == [0, 0, 0]); - } -} - -/// ditto -void fill(InputRange, ForwardRange)(InputRange range, ForwardRange filler) -if (isInputRange!InputRange - && (isForwardRange!ForwardRange - || (isInputRange!ForwardRange && isInfinite!ForwardRange)) - && is(typeof(InputRange.init.front = ForwardRange.init.front))) -{ - static if (isInfinite!ForwardRange) - { - //ForwardRange is infinite, no need for bounds checking or saving - static if (hasSlicing!ForwardRange && hasLength!InputRange - && is(typeof(filler[0 .. range.length]))) - { - copy(filler[0 .. range.length], range); - } - else - { - //manual feed - for ( ; !range.empty; range.popFront(), filler.popFront()) - { - range.front = filler.front; - } - } - } - else - { - import std.exception : enforce; - - enforce(!filler.empty, "Cannot fill range with an empty filler"); - - static if (hasLength!InputRange && hasLength!ForwardRange - && is(typeof(range.length > filler.length))) - { - //Case we have access to length - immutable len = filler.length; - //Start by bulk copies - while (range.length > len) - { - range = copy(filler.save, range); - } - - //and finally fill the partial range. No need to save here. - static if (hasSlicing!ForwardRange && is(typeof(filler[0 .. range.length]))) - { - //use a quick copy - auto len2 = range.length; - range = copy(filler[0 .. len2], range); - } - else - { - //iterate. No need to check filler, it's length is longer than range's - for (; !range.empty; range.popFront(), filler.popFront()) - { - range.front = filler.front; - } - } - } - else - { - //Most basic case. - auto bck = filler.save; - for (; !range.empty; range.popFront(), filler.popFront()) - { - if (filler.empty) filler = bck.save; - range.front = filler.front; - } - } - } -} - -/// -@safe unittest -{ - int[] a = [ 1, 2, 3, 4, 5 ]; - int[] b = [ 8, 9 ]; - fill(a, b); - assert(a == [ 8, 9, 8, 9, 8 ]); -} - -@safe unittest -{ - import std.exception : assertThrown; - import std.internal.test.dummyrange; - - int[] a = [ 1, 2, 3, 4, 5 ]; - int[] b = [1, 2]; - fill(a, b); - assert(a == [ 1, 2, 1, 2, 1 ]); - - // fill should accept InputRange - alias InputRange = DummyRange!(ReturnBy.Reference, Length.No, RangeType.Input); - InputRange range; - fill(range,[1,2]); - foreach (i,value;range.arr) - assert(value == (i%2 == 0?1:2)); - - //test with a input being a "reference forward" range - fill(a, new ReferenceForwardRange!int([8, 9])); - assert(a == [8, 9, 8, 9, 8]); - - //test with a input being an "infinite input" range - fill(a, new ReferenceInfiniteInputRange!int()); - assert(a == [0, 1, 2, 3, 4]); - - //empty filler test - assertThrown(fill(a, a[$..$])); -} - -/** -Initializes all elements of `range` with their `.init` value. -Assumes that the elements of the range are uninitialized. - -This function is unavailable if `T` is a `struct` and `T.this()` is annotated -with `@disable`. - -Params: - range = An - $(REF_ALTTEXT input range, isInputRange, std,range,primitives) - that exposes references to its elements and has assignable - elements - -See_Also: - $(LREF fill) - $(LREF uninitializedFill) - */ -void initializeAll(Range)(Range range) -if (isInputRange!Range && hasLvalueElements!Range && hasAssignableElements!Range - && __traits(compiles, { static ElementType!Range _; })) -{ - import core.stdc.string : memset, memcpy; - import std.traits : hasElaborateAssign, isDynamicArray; - - alias T = ElementType!Range; - static if (hasElaborateAssign!T) - { - import std.algorithm.internal : addressOf; - //Elaborate opAssign. Must go the memcpy/memset road. - static if (!__traits(isZeroInit, T)) - { - for ( ; !range.empty ; range.popFront() ) - { - import core.internal.lifetime : emplaceInitializer; - emplaceInitializer(range.front); - } - } - else - static if (isDynamicArray!Range) - memset(range.ptr, 0, range.length * T.sizeof); - else - for ( ; !range.empty ; range.popFront() ) - memset(addressOf(range.front), 0, T.sizeof); - } - else - fill(range, T.init); -} - -/// ditto -void initializeAll(Range)(Range range) -if (is(Range == char[]) || is(Range == wchar[])) -{ - alias T = ElementEncodingType!Range; - range[] = T.init; -} - -/// -@system unittest -{ - import core.stdc.stdlib : malloc, free; - - struct S - { - int a = 10; - } - - auto s = (cast(S*) malloc(5 * S.sizeof))[0 .. 5]; - initializeAll(s); - assert(s == [S(10), S(10), S(10), S(10), S(10)]); - - scope(exit) free(s.ptr); -} - -@system unittest -{ - import std.algorithm.iteration : filter; - import std.meta : AliasSeq; - import std.traits : hasElaborateAssign; - - //Test strings: - //Must work on narrow strings. - //Must reject const - char[3] a = void; - a[].initializeAll(); - assert(a[] == [char.init, char.init, char.init]); - string s; - assert(!__traits(compiles, s.initializeAll())); - assert(!__traits(compiles, s.initializeAll())); - assert(s.empty); - - //Note: Cannot call uninitializedFill on narrow strings - - enum e {e1, e2} - e[3] b1 = void; - b1[].initializeAll(); - assert(b1[] == [e.e1, e.e1, e.e1]); - e[3] b2 = void; - b2[].uninitializedFill(e.e2); - assert(b2[] == [e.e2, e.e2, e.e2]); - - static struct S1 - { - int i; - } - static struct S2 - { - int i = 1; - } - static struct S3 - { - int i; - this(this){} - } - static struct S4 - { - int i = 1; - this(this){} - } - static assert(!hasElaborateAssign!S1); - static assert(!hasElaborateAssign!S2); - static assert( hasElaborateAssign!S3); - static assert( hasElaborateAssign!S4); - assert(!typeid(S1).initializer().ptr); - assert( typeid(S2).initializer().ptr); - assert(!typeid(S3).initializer().ptr); - assert( typeid(S4).initializer().ptr); - - static foreach (S; AliasSeq!(S1, S2, S3, S4)) - { - //initializeAll - { - //Array - S[3] ss1 = void; - ss1[].initializeAll(); - assert(ss1[] == [S.init, S.init, S.init]); - - //Not array - S[3] ss2 = void; - auto sf = ss2[].filter!"true"(); - - sf.initializeAll(); - assert(ss2[] == [S.init, S.init, S.init]); - } - //uninitializedFill - { - //Array - S[3] ss1 = void; - ss1[].uninitializedFill(S(2)); - assert(ss1[] == [S(2), S(2), S(2)]); - - //Not array - S[3] ss2 = void; - auto sf = ss2[].filter!"true"(); - sf.uninitializedFill(S(2)); - assert(ss2[] == [S(2), S(2), S(2)]); - } - } -} - -// test that initializeAll works for arrays of static arrays of structs with -// elaborate assigns. -@system unittest -{ - struct Int { - ~this() {} - int x = 3; - } - Int[2] xs = [Int(1), Int(2)]; - struct R { - bool done; - bool empty() { return done; } - ref Int[2] front() { return xs; } - void popFront() { done = true; } - } - initializeAll(R()); - assert(xs[0].x == 3); - assert(xs[1].x == 3); -} - -// https://issues.dlang.org/show_bug.cgi?id=22105 -@system unittest -{ - struct NoDefaultCtor - { - @disable this(); - } - - NoDefaultCtor[1] array = void; - static assert(!__traits(compiles, array[].initializeAll)); -} - -// move -/** -Moves `source` into `target`, via a destructive copy when necessary. - -If `T` is a struct with a destructor or postblit defined, source is reset -to its `.init` value after it is moved into target, otherwise it is -left unchanged. - -Preconditions: -If source has internal pointers that point to itself and doesn't define -opPostMove, it cannot be moved, and will trigger an assertion failure. - -Params: - source = Data to copy. - target = Where to copy into. The destructor, if any, is invoked before the - copy is performed. -*/ -void move(T)(ref T source, ref T target) -{ - moveImpl(target, source); -} - -/// For non-struct types, `move` just performs `target = source`: -@safe unittest -{ - Object obj1 = new Object; - Object obj2 = obj1; - Object obj3; - - move(obj2, obj3); - assert(obj3 is obj1); - // obj2 unchanged - assert(obj2 is obj1); -} - -/// -pure nothrow @safe @nogc unittest -{ - // Structs without destructors are simply copied - struct S1 - { - int a = 1; - int b = 2; - } - S1 s11 = { 10, 11 }; - S1 s12; - - move(s11, s12); - - assert(s12 == S1(10, 11)); - assert(s11 == s12); - - // But structs with destructors or postblits are reset to their .init value - // after copying to the target. - struct S2 - { - int a = 1; - int b = 2; - - ~this() pure nothrow @safe @nogc { } - } - S2 s21 = { 3, 4 }; - S2 s22; - - move(s21, s22); - - assert(s21 == S2(1, 2)); - assert(s22 == S2(3, 4)); -} - -@safe unittest -{ - import std.exception : assertCTFEable; - import std.traits; - - assertCTFEable!((){ - Object obj1 = new Object; - Object obj2 = obj1; - Object obj3; - move(obj2, obj3); - assert(obj3 is obj1); - - static struct S1 { int a = 1, b = 2; } - S1 s11 = { 10, 11 }; - S1 s12; - move(s11, s12); - assert(s11.a == 10 && s11.b == 11 && s12.a == 10 && s12.b == 11); - - static struct S2 { int a = 1; int * b; } - S2 s21 = { 10, null }; - s21.b = new int; - S2 s22; - move(s21, s22); - assert(s21 == s22); - }); - // https://issues.dlang.org/show_bug.cgi?id=5661 test(1) - static struct S3 - { - static struct X { int n = 0; ~this(){n = 0;} } - X x; - } - static assert(hasElaborateDestructor!S3); - S3 s31, s32; - s31.x.n = 1; - move(s31, s32); - assert(s31.x.n == 0); - assert(s32.x.n == 1); - - // https://issues.dlang.org/show_bug.cgi?id=5661 test(2) - static struct S4 - { - static struct X { int n = 0; this(this){n = 0;} } - X x; - } - static assert(hasElaborateCopyConstructor!S4); - S4 s41, s42; - s41.x.n = 1; - move(s41, s42); - assert(s41.x.n == 0); - assert(s42.x.n == 1); - - // https://issues.dlang.org/show_bug.cgi?id=13990 test - class S5; - - S5 s51; - S5 s52 = s51; - S5 s53; - move(s52, s53); - assert(s53 is s51); -} - -/// Ditto -T move(T)(return scope ref T source) -{ - return moveImpl(source); -} - -/// Non-copyable structs can still be moved: -pure nothrow @safe @nogc unittest -{ - struct S - { - int a = 1; - @disable this(this); - ~this() pure nothrow @safe @nogc {} - } - S s1; - s1.a = 2; - S s2 = move(s1); - assert(s1.a == 1); - assert(s2.a == 2); -} - -/// `opPostMove` will be called if defined: -pure nothrow @safe @nogc unittest -{ - struct S - { - int a; - void opPostMove(const ref S old) - { - assert(a == old.a); - a++; - } - } - S s1; - s1.a = 41; - S s2 = move(s1); - assert(s2.a == 42); -} - -// https://issues.dlang.org/show_bug.cgi?id=20869 -// `move` should propagate the attributes of `opPostMove` -@system unittest -{ - static struct S - { - void opPostMove(const ref S old) nothrow @system - { - __gshared int i; - new int(i++); // Force @gc impure @system - } - } - - alias T = void function() @system nothrow; - static assert(is(typeof({ S s; move(s); }) == T)); - static assert(is(typeof({ S s; move(s, s); }) == T)); -} - -private void moveImpl(T)(ref scope T target, ref return scope T source) -{ - import std.traits : hasElaborateDestructor; - - static if (is(T == struct)) - { - // Unsafe when compiling without -dip1000 - if ((() @trusted => &source == &target)()) return; - - // Destroy target before overwriting it - static if (hasElaborateDestructor!T) target.__xdtor(); - } - // move and emplace source into target - moveEmplaceImpl(target, source); -} - -private T moveImpl(T)(ref return scope T source) -{ - // Properly infer safety from moveEmplaceImpl as the implementation below - // might void-initialize pointers in result and hence needs to be @trusted - if (false) moveEmplaceImpl(source, source); - - return trustedMoveImpl(source); -} - -private T trustedMoveImpl(T)(ref return scope T source) @trusted -{ - T result = void; - moveEmplaceImpl(result, source); - return result; -} - -@safe unittest -{ - import std.exception : assertCTFEable; - import std.traits; - - assertCTFEable!((){ - Object obj1 = new Object; - Object obj2 = obj1; - Object obj3 = move(obj2); - assert(obj3 is obj1); - - static struct S1 { int a = 1, b = 2; } - S1 s11 = { 10, 11 }; - S1 s12 = move(s11); - assert(s11.a == 10 && s11.b == 11 && s12.a == 10 && s12.b == 11); - - static struct S2 { int a = 1; int * b; } - S2 s21 = { 10, null }; - s21.b = new int; - S2 s22 = move(s21); - assert(s21 == s22); - }); - - // https://issues.dlang.org/show_bug.cgi?id=5661 test(1) - static struct S3 - { - static struct X { int n = 0; ~this(){n = 0;} } - X x; - } - static assert(hasElaborateDestructor!S3); - S3 s31; - s31.x.n = 1; - S3 s32 = move(s31); - assert(s31.x.n == 0); - assert(s32.x.n == 1); - - // https://issues.dlang.org/show_bug.cgi?id=5661 test(2) - static struct S4 - { - static struct X { int n = 0; this(this){n = 0;} } - X x; - } - static assert(hasElaborateCopyConstructor!S4); - S4 s41; - s41.x.n = 1; - S4 s42 = move(s41); - assert(s41.x.n == 0); - assert(s42.x.n == 1); - - // https://issues.dlang.org/show_bug.cgi?id=13990 test - class S5; - - S5 s51; - S5 s52 = s51; - S5 s53; - s53 = move(s52); - assert(s53 is s51); -} - -@system unittest -{ - static struct S { int n = 0; ~this() @system { n = 0; } } - S a, b; - static assert(!__traits(compiles, () @safe { move(a, b); })); - static assert(!__traits(compiles, () @safe { move(a); })); - a.n = 1; - () @trusted { move(a, b); }(); - assert(a.n == 0); - a.n = 1; - () @trusted { move(a); }(); - assert(a.n == 0); -} - -// https://issues.dlang.org/show_bug.cgi?id=6217 -@safe unittest -{ - import std.algorithm.iteration : map; - auto x = map!"a"([1,2,3]); - x = move(x); -} - -// https://issues.dlang.org/show_bug.cgi?id=8055 -@safe unittest -{ - static struct S - { - int x; - ~this() - { - assert(x == 0); - } - } - S foo(S s) - { - return move(s); - } - S a; - a.x = 0; - auto b = foo(a); - assert(b.x == 0); -} - -// https://issues.dlang.org/show_bug.cgi?id=8057 -@system unittest -{ - int n = 10; - struct S - { - int x; - ~this() - { - // Access to enclosing scope - assert(n == 10); - } - } - S foo(S s) - { - // Move nested struct - return move(s); - } - S a; - a.x = 1; - auto b = foo(a); - assert(b.x == 1); - - // Regression https://issues.dlang.org/show_bug.cgi?id=8171 - static struct Array(T) - { - // nested struct has no member - struct Payload - { - ~this() {} - } - } - Array!int.Payload x = void; - move(x); - move(x, x); -} - -private void moveEmplaceImpl(T)(ref scope T target, ref return scope T source) -{ - import core.stdc.string : memcpy, memset; - import std.traits : hasAliasing, hasElaborateAssign, - hasElaborateCopyConstructor, hasElaborateDestructor, - hasElaborateMove, - isAssignable, isStaticArray; - - static if (!is(T == class) && hasAliasing!T) if (!__ctfe) - { - import std.exception : doesPointTo; - assert(!(doesPointTo(source, source) && !hasElaborateMove!T), - "Cannot move object of type " ~ T.stringof ~ " with internal pointer unless `opPostMove` is defined."); - } - - static if (is(T == struct)) - { - // Unsafe when compiling without -dip1000 - assert((() @trusted => &source !is &target)(), "source and target must not be identical"); - - static if (hasElaborateAssign!T || !isAssignable!T) - () @trusted { memcpy(&target, &source, T.sizeof); }(); - else - target = source; - - static if (hasElaborateMove!T) - __move_post_blt(target, source); - - // If the source defines a destructor or a postblit hook, we must obliterate the - // object in order to avoid double freeing and undue aliasing - static if (hasElaborateDestructor!T || hasElaborateCopyConstructor!T) - { - // If T is nested struct, keep original context pointer - static if (__traits(isNested, T)) - enum sz = T.sizeof - (void*).sizeof; - else - enum sz = T.sizeof; - - static if (__traits(isZeroInit, T)) - () @trusted { memset(&source, 0, sz); }(); - else - () @trusted { memcpy(&source, __traits(initSymbol, T).ptr, sz); }(); - } - } - else static if (isStaticArray!T) - { - for (size_t i = 0; i < source.length; ++i) - move(source[i], target[i]); - } - else - { - // Primitive data (including pointers and arrays) or class - - // assignment works great - target = source; - } -} - -/** - * Similar to $(LREF move) but assumes `target` is uninitialized. This - * is more efficient because `source` can be blitted over `target` - * without destroying or initializing it first. - * - * Params: - * source = value to be moved into target - * target = uninitialized value to be filled by source - */ -void moveEmplace(T)(ref T source, ref T target) pure @system -{ - moveEmplaceImpl(target, source); -} - -/// -pure nothrow @nogc @system unittest -{ - static struct Foo - { - pure nothrow @nogc: - this(int* ptr) { _ptr = ptr; } - ~this() { if (_ptr) ++*_ptr; } - int* _ptr; - } - - int val; - Foo foo1 = void; // uninitialized - auto foo2 = Foo(&val); // initialized - assert(foo2._ptr is &val); - - // Using `move(foo2, foo1)` would have an undefined effect because it would destroy - // the uninitialized foo1. - // moveEmplace directly overwrites foo1 without destroying or initializing it first. - moveEmplace(foo2, foo1); - assert(foo1._ptr is &val); - assert(foo2._ptr is null); - assert(val == 0); -} - -// https://issues.dlang.org/show_bug.cgi?id=18913 -@safe unittest -{ - static struct NoCopy - { - int payload; - ~this() { } - @disable this(this); - } - - static void f(NoCopy[2]) { } - - NoCopy[2] ncarray = [ NoCopy(1), NoCopy(2) ]; - - static assert(!__traits(compiles, f(ncarray))); - f(move(ncarray)); -} - -// moveAll -/** -Calls `move(a, b)` for each element `a` in `src` and the corresponding -element `b` in `tgt`, in increasing order. - -Preconditions: -`walkLength(src) <= walkLength(tgt)`. -This precondition will be asserted. If you cannot ensure there is enough room in -`tgt` to accommodate all of `src` use $(LREF moveSome) instead. - -Params: - src = An $(REF_ALTTEXT input range, isInputRange, std,range,primitives) with - movable elements. - tgt = An $(REF_ALTTEXT input range, isInputRange, std,range,primitives) with - elements that elements from `src` can be moved into. - -Returns: The leftover portion of `tgt` after all elements from `src` have -been moved. - */ -InputRange2 moveAll(InputRange1, InputRange2)(InputRange1 src, InputRange2 tgt) -if (isInputRange!InputRange1 && isInputRange!InputRange2 - && is(typeof(move(src.front, tgt.front)))) -{ - return moveAllImpl!move(src, tgt); -} - -/// -pure nothrow @safe @nogc unittest -{ - int[3] a = [ 1, 2, 3 ]; - int[5] b; - assert(moveAll(a[], b[]) is b[3 .. $]); - assert(a[] == b[0 .. 3]); - int[3] cmp = [ 1, 2, 3 ]; - assert(a[] == cmp[]); -} - -/** - * Similar to $(LREF moveAll) but assumes all elements in `tgt` are - * uninitialized. Uses $(LREF moveEmplace) to move elements from - * `src` over elements from `tgt`. - */ -InputRange2 moveEmplaceAll(InputRange1, InputRange2)(InputRange1 src, InputRange2 tgt) @system -if (isInputRange!InputRange1 && isInputRange!InputRange2 - && is(typeof(moveEmplace(src.front, tgt.front)))) -{ - return moveAllImpl!moveEmplace(src, tgt); -} - -/// -pure nothrow @nogc @system unittest -{ - static struct Foo - { - ~this() pure nothrow @nogc { if (_ptr) ++*_ptr; } - int* _ptr; - } - int[3] refs = [0, 1, 2]; - Foo[3] src = [Foo(&refs[0]), Foo(&refs[1]), Foo(&refs[2])]; - Foo[5] dst = void; - - auto tail = moveEmplaceAll(src[], dst[]); // move 3 value from src over dst - assert(tail.length == 2); // returns remaining uninitialized values - initializeAll(tail); - - import std.algorithm.searching : all; - assert(src[].all!(e => e._ptr is null)); - assert(dst[0 .. 3].all!(e => e._ptr !is null)); -} - -@system unittest -{ - struct InputRange - { - ref int front() { return data[0]; } - void popFront() { data.popFront; } - bool empty() { return data.empty; } - int[] data; - } - auto a = InputRange([ 1, 2, 3 ]); - auto b = InputRange(new int[5]); - moveAll(a, b); - assert(a.data == b.data[0 .. 3]); - assert(a.data == [ 1, 2, 3 ]); -} - -private InputRange2 moveAllImpl(alias moveOp, InputRange1, InputRange2)( - ref InputRange1 src, ref InputRange2 tgt) -{ - import std.exception : enforce; - - static if (isRandomAccessRange!InputRange1 && hasLength!InputRange1 && hasLength!InputRange2 - && hasSlicing!InputRange2 && isRandomAccessRange!InputRange2) - { - auto toMove = src.length; - assert(toMove <= tgt.length, "Source buffer needs to be smaller or equal to the target buffer."); - foreach (idx; 0 .. toMove) - moveOp(src[idx], tgt[idx]); - return tgt[toMove .. tgt.length]; - } - else - { - for (; !src.empty; src.popFront(), tgt.popFront()) - { - assert(!tgt.empty, "Source buffer needs to be smaller or equal to the target buffer."); - moveOp(src.front, tgt.front); - } - return tgt; - } -} - -// moveSome -/** -Calls `move(a, b)` for each element `a` in `src` and the corresponding -element `b` in `tgt`, in increasing order, stopping when either range has been -exhausted. - -Params: - src = An $(REF_ALTTEXT input range, isInputRange, std,range,primitives) with - movable elements. - tgt = An $(REF_ALTTEXT input range, isInputRange, std,range,primitives) with - elements that elements from `src` can be moved into. - -Returns: The leftover portions of the two ranges after one or the other of the -ranges have been exhausted. - */ -Tuple!(InputRange1, InputRange2) moveSome(InputRange1, InputRange2)(InputRange1 src, InputRange2 tgt) -if (isInputRange!InputRange1 && isInputRange!InputRange2 - && is(typeof(move(src.front, tgt.front)))) -{ - return moveSomeImpl!move(src, tgt); -} - -/// -pure nothrow @safe @nogc unittest -{ - int[5] a = [ 1, 2, 3, 4, 5 ]; - int[3] b; - assert(moveSome(a[], b[])[0] is a[3 .. $]); - assert(a[0 .. 3] == b); - assert(a == [ 1, 2, 3, 4, 5 ]); -} - -/** - * Same as $(LREF moveSome) but assumes all elements in `tgt` are - * uninitialized. Uses $(LREF moveEmplace) to move elements from - * `src` over elements from `tgt`. - */ -Tuple!(InputRange1, InputRange2) moveEmplaceSome(InputRange1, InputRange2)(InputRange1 src, InputRange2 tgt) @system -if (isInputRange!InputRange1 && isInputRange!InputRange2 - && is(typeof(move(src.front, tgt.front)))) -{ - return moveSomeImpl!moveEmplace(src, tgt); -} - -/// -pure nothrow @nogc @system unittest -{ - static struct Foo - { - ~this() pure nothrow @nogc { if (_ptr) ++*_ptr; } - int* _ptr; - } - int[4] refs = [0, 1, 2, 3]; - Foo[4] src = [Foo(&refs[0]), Foo(&refs[1]), Foo(&refs[2]), Foo(&refs[3])]; - Foo[3] dst = void; - - auto res = moveEmplaceSome(src[], dst[]); - assert(res.length == 2); - - import std.algorithm.searching : all; - assert(src[0 .. 3].all!(e => e._ptr is null)); - assert(src[3]._ptr !is null); - assert(dst[].all!(e => e._ptr !is null)); -} - -private Tuple!(InputRange1, InputRange2) moveSomeImpl(alias moveOp, InputRange1, InputRange2)( - ref InputRange1 src, ref InputRange2 tgt) -{ - for (; !src.empty && !tgt.empty; src.popFront(), tgt.popFront()) - moveOp(src.front, tgt.front); - return tuple(src, tgt); - } - - -// SwapStrategy -/** -Defines the swapping strategy for algorithms that need to swap -elements in a range (such as partition and sort). The strategy -concerns the swapping of elements that are not the core concern of the -algorithm. For example, consider an algorithm that sorts $(D [ "abc", -"b", "aBc" ]) according to `toUpper(a) < toUpper(b)`. That -algorithm might choose to swap the two equivalent strings `"abc"` -and `"aBc"`. That does not affect the sorting since both -`["abc", "aBc", "b" ]` and `[ "aBc", "abc", "b" ]` are valid -outcomes. - -Some situations require that the algorithm must NOT ever change the -relative ordering of equivalent elements (in the example above, only -`[ "abc", "aBc", "b" ]` would be the correct result). Such -algorithms are called $(B stable). If the ordering algorithm may swap -equivalent elements discretionarily, the ordering is called $(B -unstable). - -Yet another class of algorithms may choose an intermediate tradeoff by -being stable only on a well-defined subrange of the range. There is no -established terminology for such behavior; this library calls it $(B -semistable). - -Generally, the `stable` ordering strategy may be more costly in -time and/or space than the other two because it imposes additional -constraints. Similarly, `semistable` may be costlier than $(D -unstable). As (semi-)stability is not needed very often, the ordering -algorithms in this module parameterized by `SwapStrategy` all -choose `SwapStrategy.unstable` as the default. -*/ - -enum SwapStrategy -{ - /** - Allows freely swapping of elements as long as the output - satisfies the algorithm's requirements. - */ - unstable, - /** - In algorithms partitioning ranges in two, preserve relative - ordering of elements only to the left of the partition point. - */ - semistable, - /** - Preserve the relative ordering of elements to the largest - extent allowed by the algorithm's requirements. - */ - stable, -} - -/// -@safe unittest -{ - int[] a = [0, 1, 2, 3]; - assert(remove!(SwapStrategy.stable)(a, 1) == [0, 2, 3]); - a = [0, 1, 2, 3]; - assert(remove!(SwapStrategy.unstable)(a, 1) == [0, 3, 2]); -} - -/// -@safe unittest -{ - import std.algorithm.sorting : partition; - - // Put stuff greater than 3 on the left - auto arr = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]; - assert(partition!(a => a > 3, SwapStrategy.stable)(arr) == [1, 2, 3]); - assert(arr == [4, 5, 6, 7, 8, 9, 10, 1, 2, 3]); - - arr = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]; - assert(partition!(a => a > 3, SwapStrategy.semistable)(arr) == [2, 3, 1]); - assert(arr == [4, 5, 6, 7, 8, 9, 10, 2, 3, 1]); - - arr = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]; - assert(partition!(a => a > 3, SwapStrategy.unstable)(arr) == [3, 2, 1]); - assert(arr == [10, 9, 8, 4, 5, 6, 7, 3, 2, 1]); -} - -private template isValidIntegralTuple(T) -{ - import std.traits : isIntegral; - import std.typecons : isTuple; - static if (isTuple!T) - { - enum isValidIntegralTuple = T.length == 2 && - isIntegral!(typeof(T.init[0])) && isIntegral!(typeof(T.init[0])); - } - else - { - enum isValidIntegralTuple = isIntegral!T; - } -} - - -/** -Eliminates elements at given offsets from `range` and returns the shortened -range. - -For example, here is how to remove a single element from an array: - -$(RUNNABLE_EXAMPLE ----- -import std.algorithm.mutation; -string[] a = [ "a", "b", "c", "d" ]; -a = a.remove(1); // remove element at offset 1 -assert(a == [ "a", "c", "d"]); ----- -) - -Note that `remove` does not change the length of the original range directly; -instead, it returns the shortened range. If its return value is not assigned to -the original range, the original range will retain its original length, though -its contents will have changed: - -$(RUNNABLE_EXAMPLE ----- -import std.algorithm.mutation; -int[] a = [ 3, 5, 7, 8 ]; -assert(remove(a, 1) == [ 3, 7, 8 ]); -assert(a == [ 3, 7, 8, 8 ]); ----- -) - -The element at offset `1` has been removed and the rest of the elements have -shifted up to fill its place, however, the original array remains of the same -length. This is because all functions in `std.algorithm` only change $(I -content), not $(I topology). The value `8` is repeated because $(LREF move) was -invoked to rearrange elements, and on integers `move` simply copies the source -to the destination. To replace `a` with the effect of the removal, simply -assign the slice returned by `remove` to it, as shown in the first example. - -$(H3 $(LNAME2 remove-multiple, Removing multiple elements)) - -Multiple indices can be passed into `remove`. In that case, -elements at the respective indices are all removed. The indices must -be passed in increasing order, otherwise an exception occurs. - -$(RUNNABLE_EXAMPLE ----- -import std.algorithm.mutation; -int[] a = [ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 ]; -assert(remove(a, 1, 3, 5) == - [ 0, 2, 4, 6, 7, 8, 9, 10 ]); ----- -) - -Note that all indices refer to slots in the $(I original) array, not -in the array as it is being progressively shortened. - -Tuples of two integral offsets can be supplied to remove a range of indices: - -$(RUNNABLE_EXAMPLE ----- -import std.algorithm.mutation, std.typecons; -int[] a = [ 3, 4, 5, 6, 7]; -// remove elements at indices 1 and 2 -assert(remove(a, tuple(1, 3)) == [ 3, 6, 7 ]); ----- -) - -The tuple passes in a range closed to the left and open to -the right (consistent with built-in slices), e.g. `tuple(1, 3)` -means indices `1` and `2` but not `3`. - -Finally, any combination of integral offsets and tuples composed of two integral -offsets can be passed in: - -$(RUNNABLE_EXAMPLE ----- -import std.algorithm.mutation, std.typecons; -int[] a = [ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 ]; -a = remove(a, 1, tuple(3, 5), 9); -assert(a == [ 0, 2, 5, 6, 7, 8, 10 ]); ----- -) - -In this case, the slots at positions 1, 3, 4, and 9 are removed from -the array. - -$(H3 $(LNAME2 remove-moving, Moving strategy)) - -If the need is to remove some elements in the range but the order of -the remaining elements does not have to be preserved, you may want to -pass `SwapStrategy.unstable` to `remove`. - -$(RUNNABLE_EXAMPLE ----- -import std.algorithm.mutation; -int[] a = [ 0, 1, 2, 3 ]; -assert(remove!(SwapStrategy.unstable)(a, 1) == [ 0, 3, 2 ]); ----- -) - -In the case above, the element at slot `1` is removed, but replaced -with the last element of the range. Taking advantage of the relaxation -of the stability requirement, `remove` moved elements from the end -of the array over the slots to be removed. This way there is less data -movement to be done which improves the execution time of the function. - -`remove` works on bidirectional ranges that have assignable -lvalue elements. The moving strategy is (listed from fastest to slowest): - -$(UL - $(LI If $(D s == SwapStrategy.unstable && isRandomAccessRange!Range && -hasLength!Range && hasLvalueElements!Range), then elements are moved from the -end of the range into the slots to be filled. In this case, the absolute -minimum of moves is performed.) - $(LI Otherwise, if $(D s == -SwapStrategy.unstable && isBidirectionalRange!Range && hasLength!Range -&& hasLvalueElements!Range), then elements are still moved from the -end of the range, but time is spent on advancing between slots by repeated -calls to `range.popFront`.) - $(LI Otherwise, elements are moved -incrementally towards the front of `range`; a given element is never -moved several times, but more elements are moved than in the previous -cases.) -) - -Params: - s = a SwapStrategy to determine if the original order needs to be preserved - range = a $(REF_ALTTEXT bidirectional range, isBidirectionalRange, std,range,primitives) - with a length member - offset = which element(s) to remove - -Returns: - A range containing elements of `range` with 1 or more elements removed. -*/ -Range remove -(SwapStrategy s = SwapStrategy.stable, Range, Offset ...) -(Range range, Offset offset) -if (Offset.length >= 1 && allSatisfy!(isValidIntegralTuple, Offset)) -{ - // Activate this check when the deprecation of non-integral tuples is over - //import std.traits : isIntegral; - //import std.typecons : isTuple; - //static foreach (T; Offset) - //{ - //static if (isTuple!T) - //{ - //static assert(T.length == 2 && - //isIntegral!(typeof(T.init[0])) && isIntegral!(typeof(T.init[0])), - //"Each offset must be an integral or a tuple of two integrals." ~ - //"Use `arr.remove(pos1, pos2)` or `arr.remove(tuple(start, begin))`"); - //} - //else - //{ - //static assert(isIntegral!T, - //"Each offset must be an integral or a tuple of two integrals." ~ - //"Use `arr.remove(pos1, pos2)` or `arr.remove(tuple(start, begin))`"); - //} - //} - return removeImpl!s(range, offset); -} - -/// ditto -deprecated("Use of non-integral tuples is deprecated. Use remove(tuple(start, end).") -Range remove -(SwapStrategy s = SwapStrategy.stable, Range, Offset ...) -(Range range, Offset offset) -if (Offset.length >= 1 && !allSatisfy!(isValidIntegralTuple, Offset)) -{ - return removeImpl!s(range, offset); -} - -/// -@safe pure unittest -{ - import std.typecons : tuple; - - auto a = [ 0, 1, 2, 3, 4, 5 ]; - assert(remove!(SwapStrategy.stable)(a, 1) == [ 0, 2, 3, 4, 5 ]); - a = [ 0, 1, 2, 3, 4, 5 ]; - assert(remove!(SwapStrategy.stable)(a, 1, 3) == [ 0, 2, 4, 5] ); - a = [ 0, 1, 2, 3, 4, 5 ]; - assert(remove!(SwapStrategy.stable)(a, 1, tuple(3, 6)) == [ 0, 2 ]); - - a = [ 0, 1, 2, 3, 4, 5 ]; - assert(remove!(SwapStrategy.unstable)(a, 1) == [0, 5, 2, 3, 4]); - a = [ 0, 1, 2, 3, 4, 5 ]; - assert(remove!(SwapStrategy.unstable)(a, tuple(1, 4)) == [0, 5, 4]); -} - -/// -@safe pure unittest -{ - import std.typecons : tuple; - - // Delete an index - assert([4, 5, 6].remove(1) == [4, 6]); - - // Delete multiple indices - assert([4, 5, 6, 7, 8].remove(1, 3) == [4, 6, 8]); - - // Use an indices range - assert([4, 5, 6, 7, 8].remove(tuple(1, 3)) == [4, 7, 8]); - - // Use an indices range and individual indices - assert([4, 5, 6, 7, 8].remove(0, tuple(1, 3), 4) == [7]); -} - -/// `SwapStrategy.unstable` is faster, but doesn't guarantee the same order of the original array -@safe pure unittest -{ - assert([5, 6, 7, 8].remove!(SwapStrategy.stable)(1) == [5, 7, 8]); - assert([5, 6, 7, 8].remove!(SwapStrategy.unstable)(1) == [5, 8, 7]); -} - -private auto removeImpl(SwapStrategy s, Range, Offset...)(Range range, Offset offset) -{ - static if (isNarrowString!Range) - { - static assert(isMutable!(typeof(range[0])), - "Elements must be mutable to remove"); - static assert(s == SwapStrategy.stable, - "Only stable removing can be done for character arrays"); - return removeStableString(range, offset); - } - else - { - static assert(isBidirectionalRange!Range, - "Range must be bidirectional"); - static assert(hasLvalueElements!Range, - "Range must have Lvalue elements (see std.range.hasLvalueElements)"); - - static if (s == SwapStrategy.unstable) - { - static assert(hasLength!Range, - "Range must have `length` for unstable remove"); - return removeUnstable(range, offset); - } - else static if (s == SwapStrategy.stable) - return removeStable(range, offset); - else - static assert(false, - "Only SwapStrategy.stable and SwapStrategy.unstable are supported"); - } -} - -@safe unittest -{ - import std.exception : assertThrown; - import std.range; - - // https://issues.dlang.org/show_bug.cgi?id=10173 - int[] test = iota(0, 10).array(); - assertThrown(remove!(SwapStrategy.stable)(test, tuple(2, 4), tuple(1, 3))); - assertThrown(remove!(SwapStrategy.unstable)(test, tuple(2, 4), tuple(1, 3))); - assertThrown(remove!(SwapStrategy.stable)(test, 2, 4, 1, 3)); - assertThrown(remove!(SwapStrategy.unstable)(test, 2, 4, 1, 3)); -} - -@safe unittest -{ - import std.range; - int[] a = [ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 ]; - a = [ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 ]; - assert(remove!(SwapStrategy.stable)(a, 1) == - [ 0, 2, 3, 4, 5, 6, 7, 8, 9, 10 ]); - - a = [ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 ]; - assert(remove!(SwapStrategy.unstable)(a, 0, 10) == - [ 9, 1, 2, 3, 4, 5, 6, 7, 8 ]); - - a = [ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 ]; - assert(remove!(SwapStrategy.unstable)(a, 0, tuple(9, 11)) == - [ 8, 1, 2, 3, 4, 5, 6, 7 ]); - // https://issues.dlang.org/show_bug.cgi?id=5224 - a = [ 1, 2, 3, 4 ]; - assert(remove!(SwapStrategy.unstable)(a, 2) == - [ 1, 2, 4 ]); - - a = [ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 ]; - assert(remove!(SwapStrategy.stable)(a, 1, 5) == - [ 0, 2, 3, 4, 6, 7, 8, 9, 10 ]); - - a = [ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 ]; - assert(remove!(SwapStrategy.stable)(a, 1, 3, 5) - == [ 0, 2, 4, 6, 7, 8, 9, 10]); - a = [ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 ]; - assert(remove!(SwapStrategy.stable)(a, 1, tuple(3, 5)) - == [ 0, 2, 5, 6, 7, 8, 9, 10]); - - a = iota(0, 10).array(); - assert(remove!(SwapStrategy.unstable)(a, tuple(1, 4), tuple(6, 7)) - == [0, 9, 8, 7, 4, 5]); -} - -// https://issues.dlang.org/show_bug.cgi?id=11576 -@safe unittest -{ - auto arr = [1,2,3]; - arr = arr.remove!(SwapStrategy.unstable)(2); - assert(arr == [1,2]); - -} - -// https://issues.dlang.org/show_bug.cgi?id=12889 -@safe unittest -{ - import std.range; - int[1][] arr = [[0], [1], [2], [3], [4], [5], [6]]; - auto orig = arr.dup; - foreach (i; iota(arr.length)) - { - assert(orig == arr.remove!(SwapStrategy.unstable)(tuple(i,i))); - assert(orig == arr.remove!(SwapStrategy.stable)(tuple(i,i))); - } -} - -@safe unittest -{ - char[] chars = ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h']; - remove(chars, 4); - assert(chars == ['a', 'b', 'c', 'd', 'f', 'g', 'h', 'h']); - - char[] bigChars = "∑œ∆¬é˚˙ƒé∂ß¡¡".dup; - assert(remove(bigChars, tuple(4, 6), 8) == ("∑œ∆¬˙ƒ∂ß¡¡")); - - import std.exception : assertThrown; - assertThrown(remove(bigChars.dup, 1, 0)); - assertThrown(remove(bigChars.dup, tuple(4, 3))); -} - -private Range removeUnstable(Range, Offset...)(Range range, Offset offset) -{ - Tuple!(size_t, "pos", size_t, "len")[offset.length] blackouts; - foreach (i, v; offset) - { - static if (is(typeof(v[0]) : size_t) && is(typeof(v[1]) : size_t)) - { - blackouts[i].pos = v[0]; - blackouts[i].len = v[1] - v[0]; - } - else - { - static assert(is(typeof(v) : size_t), typeof(v).stringof); - blackouts[i].pos = v; - blackouts[i].len = 1; - } - static if (i > 0) - { - import std.exception : enforce; - - enforce(blackouts[i - 1].pos + blackouts[i - 1].len - <= blackouts[i].pos, - "remove(): incorrect ordering of elements to remove"); - } - } - - size_t left = 0, right = offset.length - 1; - auto tgt = range.save; - size_t tgtPos = 0; - - while (left <= right) - { - // Look for a blackout on the right - if (blackouts[right].pos + blackouts[right].len >= range.length) - { - range.popBackExactly(blackouts[right].len); - - // Since right is unsigned, we must check for this case, otherwise - // we might turn it into size_t.max and the loop condition will not - // fail when it should. - if (right > 0) - { - --right; - continue; - } - else - break; - } - // Advance to next blackout on the left - assert(blackouts[left].pos >= tgtPos, "Next blackout on the left shouldn't appear before the target."); - tgt.popFrontExactly(blackouts[left].pos - tgtPos); - tgtPos = blackouts[left].pos; - - // Number of elements to the right of blackouts[right] - immutable tailLen = range.length - (blackouts[right].pos + blackouts[right].len); - size_t toMove = void; - if (tailLen < blackouts[left].len) - { - toMove = tailLen; - blackouts[left].pos += toMove; - blackouts[left].len -= toMove; - } - else - { - toMove = blackouts[left].len; - ++left; - } - tgtPos += toMove; - foreach (i; 0 .. toMove) - { - move(range.back, tgt.front); - range.popBack(); - tgt.popFront(); - } - } - - return range; -} - -private Range removeStable(Range, Offset...)(Range range, Offset offset) -{ - auto result = range; - auto src = range, tgt = range; - size_t pos; - foreach (pass, i; offset) - { - static if (is(typeof(i[0])) && is(typeof(i[1]))) - { - auto from = i[0], delta = i[1] - i[0]; - } - else - { - auto from = i; - enum delta = 1; - } - - static if (pass > 0) - { - import std.exception : enforce; - enforce(pos <= from, - "remove(): incorrect ordering of elements to remove"); - - for (; pos < from; ++pos, src.popFront(), tgt.popFront()) - { - move(src.front, tgt.front); - } - } - else - { - src.popFrontExactly(from); - tgt.popFrontExactly(from); - pos = from; - } - // now skip source to the "to" position - src.popFrontExactly(delta); - result.popBackExactly(delta); - pos += delta; - } - // leftover move - moveAll(src, tgt); - return result; -} - -private Range removeStableString(Range, Offset...)(Range range, Offset offsets) -{ - import std.utf : stride; - size_t charIdx = 0; - size_t dcharIdx = 0; - size_t charShift = 0; - - void skipOne() - { - charIdx += stride(range[charIdx .. $]); - ++dcharIdx; - } - - void copyBackOne() - { - auto encodedLen = stride(range[charIdx .. $]); - foreach (j; charIdx .. charIdx + encodedLen) - range[j - charShift] = range[j]; - charIdx += encodedLen; - ++dcharIdx; - } - - foreach (pass, i; offsets) - { - static if (is(typeof(i[0])) && is(typeof(i[1]))) - { - auto from = i[0]; - auto delta = i[1] - i[0]; - } - else - { - auto from = i; - enum delta = 1; - } - - import std.exception : enforce; - enforce(dcharIdx <= from && delta >= 0, - "remove(): incorrect ordering of elements to remove"); - - while (dcharIdx < from) - static if (pass == 0) - skipOne(); - else - copyBackOne(); - - auto mark = charIdx; - while (dcharIdx < from + delta) - skipOne(); - charShift += charIdx - mark; - } - - foreach (i; charIdx .. range.length) - range[i - charShift] = range[i]; - - return range[0 .. $ - charShift]; -} - -// Use of dynamic arrays as offsets is too error-prone -// https://issues.dlang.org/show_bug.cgi?id=12086 -// Activate these tests once the deprecation period of remove with non-integral tuples is over -@safe unittest -{ - //static assert(!__traits(compiles, [0, 1, 2, 3, 4].remove([1, 3]) == [0, 3, 4])); - static assert(__traits(compiles, [0, 1, 2, 3, 4].remove(1, 3) == [0, 2, 4])); - //static assert(!__traits(compiles, assert([0, 1, 2, 3, 4].remove([1, 3, 4]) == [0, 3, 4]))); - //static assert(!__traits(compiles, assert([0, 1, 2, 3, 4].remove(tuple(1, 3, 4)) == [0, 3, 4]))); - - import std.range : only; - //static assert(!__traits(compiles, assert([0, 1, 2, 3, 4].remove(only(1, 3)) == [0, 3, 4]))); - static assert(__traits(compiles, assert([0, 1, 2, 3, 4].remove(1, 3) == [0, 2, 4]))); -} - -/** -Reduces the length of the -$(REF_ALTTEXT bidirectional range, isBidirectionalRange, std,range,primitives) `range` by removing -elements that satisfy `pred`. If `s = SwapStrategy.unstable`, -elements are moved from the right end of the range over the elements -to eliminate. If `s = SwapStrategy.stable` (the default), -elements are moved progressively to front such that their relative -order is preserved. Returns the filtered range. - -Params: - range = a bidirectional ranges with lvalue elements - or mutable character arrays - -Returns: - the range with all of the elements where `pred` is `true` - removed -*/ -Range remove(alias pred, SwapStrategy s = SwapStrategy.stable, Range)(Range range) -{ - import std.functional : unaryFun; - alias pred_ = unaryFun!pred; - static if (isNarrowString!Range) - { - static assert(isMutable!(typeof(range[0])), - "Elements must be mutable to remove"); - static assert(s == SwapStrategy.stable, - "Only stable removing can be done for character arrays"); - return removePredString!pred_(range); - } - else - { - static assert(isBidirectionalRange!Range, - "Range must be bidirectional"); - static assert(hasLvalueElements!Range, - "Range must have Lvalue elements (see std.range.hasLvalueElements)"); - static if (s == SwapStrategy.unstable) - return removePredUnstable!pred_(range); - else static if (s == SwapStrategy.stable) - return removePredStable!pred_(range); - else - static assert(false, - "Only SwapStrategy.stable and SwapStrategy.unstable are supported"); - } -} - -/// -@safe unittest -{ - static immutable base = [1, 2, 3, 2, 4, 2, 5, 2]; - - int[] arr = base[].dup; - - // using a string-based predicate - assert(remove!("a == 2")(arr) == [ 1, 3, 4, 5 ]); - - // The original array contents have been modified, - // so we need to reset it to its original state. - // The length is unmodified however. - arr[] = base[]; - - // using a lambda predicate - assert(remove!(a => a == 2)(arr) == [ 1, 3, 4, 5 ]); -} - -@safe unittest -{ - int[] a = [ 1, 2, 3, 2, 3, 4, 5, 2, 5, 6 ]; - assert(remove!("a == 2", SwapStrategy.unstable)(a) == - [ 1, 6, 3, 5, 3, 4, 5 ]); - a = [ 1, 2, 3, 2, 3, 4, 5, 2, 5, 6 ]; - assert(remove!("a == 2", SwapStrategy.stable)(a) == - [ 1, 3, 3, 4, 5, 5, 6 ]); -} - -@nogc @safe unittest -{ - // @nogc test - static int[] arr = [0,1,2,3,4,5,6,7,8,9]; - alias pred = e => e < 5; - - auto r = arr[].remove!(SwapStrategy.unstable)(0); - r = r.remove!(SwapStrategy.stable)(0); - r = r.remove!(pred, SwapStrategy.unstable); - r = r.remove!(pred, SwapStrategy.stable); -} - -@safe unittest -{ - import std.algorithm.comparison : min; - import std.algorithm.searching : all, any; - import std.algorithm.sorting : isStrictlyMonotonic; - import std.array : array; - import std.meta : AliasSeq; - import std.range : iota, only; - import std.typecons : Tuple; - alias E = Tuple!(int, int); - alias S = Tuple!(E); - S[] soffsets; - foreach (start; 0 .. 5) - foreach (end; min(start+1,5) .. 5) - soffsets ~= S(E(start,end)); - alias D = Tuple!(E, E); - D[] doffsets; - foreach (start1; 0 .. 10) - foreach (end1; min(start1+1,10) .. 10) - foreach (start2; end1 .. 10) - foreach (end2; min(start2+1,10) .. 10) - doffsets ~= D(E(start1,end1),E(start2,end2)); - alias T = Tuple!(E, E, E); - T[] toffsets; - foreach (start1; 0 .. 15) - foreach (end1; min(start1+1,15) .. 15) - foreach (start2; end1 .. 15) - foreach (end2; min(start2+1,15) .. 15) - foreach (start3; end2 .. 15) - foreach (end3; min(start3+1,15) .. 15) - toffsets ~= T(E(start1,end1),E(start2,end2),E(start3,end3)); - - static void verify(O...)(int[] r, int len, int removed, bool stable, O offsets) - { - assert(r.length == len - removed); - assert(!stable || r.isStrictlyMonotonic); - assert(r.all!(e => all!(o => e < o[0] || e >= o[1])(offsets.only))); - } - - static foreach (offsets; AliasSeq!(soffsets,doffsets,toffsets)) - foreach (os; offsets) - { - int len = 5*os.length; - auto w = iota(0, len).array; - auto x = w.dup; - auto y = w.dup; - auto z = w.dup; - alias pred = e => any!(o => o[0] <= e && e < o[1])(only(os.expand)); - w = w.remove!(SwapStrategy.unstable)(os.expand); - x = x.remove!(SwapStrategy.stable)(os.expand); - y = y.remove!(pred, SwapStrategy.unstable); - z = z.remove!(pred, SwapStrategy.stable); - int removed; - foreach (o; os) - removed += o[1] - o[0]; - verify(w, len, removed, false, os[]); - verify(x, len, removed, true, os[]); - verify(y, len, removed, false, os[]); - verify(z, len, removed, true, os[]); - assert(w == y); - assert(x == z); - } -} - -@safe unittest -{ - char[] chars = "abcdefg".dup; - assert(chars.remove!(dc => dc == 'c' || dc == 'f') == "abdeg"); - assert(chars == "abdegfg"); - - assert(chars.remove!"a == 'd'" == "abegfg"); - - char[] bigChars = "¥^¨^©é√∆π".dup; - assert(bigChars.remove!(dc => dc == "¨"d[0] || dc == "é"d[0]) == "¥^^©√∆π"); -} - -private Range removePredUnstable(alias pred, Range)(Range range) -{ - auto result = range; - for (;!range.empty;) - { - if (!pred(range.front)) - { - range.popFront(); - continue; - } - move(range.back, range.front); - range.popBack(); - result.popBack(); - } - return result; -} - -private Range removePredStable(alias pred, Range)(Range range) -{ - auto result = range; - auto tgt = range; - for (; !range.empty; range.popFront()) - { - if (pred(range.front)) - { - // yank this guy - result.popBack(); - continue; - } - // keep this guy - move(range.front, tgt.front); - tgt.popFront(); - } - return result; -} - -private Range removePredString(alias pred, SwapStrategy s = SwapStrategy.stable, Range) -(Range range) -{ - import std.utf : decode; - import std.functional : unaryFun; - - alias pred_ = unaryFun!pred; - - size_t charIdx = 0; - size_t charShift = 0; - while (charIdx < range.length) - { - size_t start = charIdx; - if (pred_(decode(range, charIdx))) - { - charShift += charIdx - start; - break; - } - } - while (charIdx < range.length) - { - size_t start = charIdx; - auto doRemove = pred_(decode(range, charIdx)); - auto encodedLen = charIdx - start; - if (doRemove) - charShift += encodedLen; - else - foreach (i; start .. charIdx) - range[i - charShift] = range[i]; - } - - return range[0 .. $ - charShift]; -} - -// reverse -/** -Reverses `r` in-place. Performs `r.length / 2` evaluations of `swap`. -UTF sequences consisting of multiple code units are preserved properly. - -Params: - r = a $(REF_ALTTEXT bidirectional range, isBidirectionalRange, std,range,primitives) - with either swappable elements, a random access range with a length member, - or a narrow string - -Returns: `r` - -Note: - When passing a string with unicode modifiers on characters, such as `\u0301`, - this function will not properly keep the position of the modifier. For example, - reversing `ba\u0301d` ("bád") will result in d\u0301ab ("d́ab") instead of - `da\u0301b` ("dáb"). - -See_Also: $(REF retro, std,range) for a lazy reverse without changing `r` -*/ -Range reverse(Range)(Range r) -if (isBidirectionalRange!Range && - (hasSwappableElements!Range || - (hasAssignableElements!Range && hasLength!Range && isRandomAccessRange!Range) || - (isNarrowString!Range && isAssignable!(ElementType!Range)))) -{ - static if (isRandomAccessRange!Range && hasLength!Range) - { - //swapAt is in fact the only way to swap non lvalue ranges - immutable last = r.length - 1; - immutable steps = r.length / 2; - for (size_t i = 0; i < steps; i++) - { - r.swapAt(i, last - i); - } - return r; - } - else static if (isNarrowString!Range && isAssignable!(ElementType!Range)) - { - import std.string : representation; - import std.utf : stride; - - auto raw = representation(r); - for (size_t i = 0; i < r.length;) - { - immutable step = stride(r, i); - if (step > 1) - { - .reverse(raw[i .. i + step]); - i += step; - } - else - { - ++i; - } - } - reverse(raw); - return r; - } - else - { - while (!r.empty) - { - swap(r.front, r.back); - r.popFront(); - if (r.empty) break; - r.popBack(); - } - return r; - } -} - -/// -@safe unittest -{ - int[] arr = [ 1, 2, 3 ]; - assert(arr.reverse == [ 3, 2, 1 ]); -} - -@safe unittest -{ - int[] range = null; - reverse(range); - range = [ 1 ]; - reverse(range); - assert(range == [1]); - range = [1, 2]; - reverse(range); - assert(range == [2, 1]); - range = [1, 2, 3]; - assert(range.reverse == [3, 2, 1]); -} - -/// -@safe unittest -{ - char[] arr = "hello\U00010143\u0100\U00010143".dup; - assert(arr.reverse == "\U00010143\u0100\U00010143olleh"); -} - -@safe unittest -{ - void test(string a, string b) - { - auto c = a.dup; - reverse(c); - assert(c == b, c ~ " != " ~ b); - } - - test("a", "a"); - test(" ", " "); - test("\u2029", "\u2029"); - test("\u0100", "\u0100"); - test("\u0430", "\u0430"); - test("\U00010143", "\U00010143"); - test("abcdefcdef", "fedcfedcba"); - test("hello\U00010143\u0100\U00010143", "\U00010143\u0100\U00010143olleh"); -} - -/** - The strip group of functions allow stripping of either leading, trailing, - or both leading and trailing elements. - - The `stripLeft` function will strip the `front` of the range, - the `stripRight` function will strip the `back` of the range, - while the `strip` function will strip both the `front` and `back` - of the range. - - Note that the `strip` and `stripRight` functions require the range to - be a $(LREF BidirectionalRange) range. - - All of these functions come in two varieties: one takes a target element, - where the range will be stripped as long as this element can be found. - The other takes a lambda predicate, where the range will be stripped as - long as the predicate returns true. - - Params: - range = a $(REF_ALTTEXT bidirectional range, isBidirectionalRange, std,range,primitives) - or $(REF_ALTTEXT input range, isInputRange, std,range,primitives) - element = the elements to remove - - Returns: - a Range with all of range except element at the start and end -*/ -Range strip(Range, E)(Range range, E element) -if (isBidirectionalRange!Range && is(typeof(range.front == element) : bool)) -{ - return range.stripLeft(element).stripRight(element); -} - -/// ditto -Range strip(alias pred, Range)(Range range) -if (isBidirectionalRange!Range && is(typeof(pred(range.back)) : bool)) -{ - return range.stripLeft!pred().stripRight!pred(); -} - -/// ditto -Range stripLeft(Range, E)(Range range, E element) -if (isInputRange!Range && is(typeof(range.front == element) : bool)) -{ - import std.algorithm.searching : find; - return find!((auto ref a) => a != element)(range); -} - -/// ditto -Range stripLeft(alias pred, Range)(Range range) -if (isInputRange!Range && is(typeof(pred(range.front)) : bool)) -{ - import std.algorithm.searching : find; - import std.functional : not; - - return find!(not!pred)(range); -} - -/// ditto -Range stripRight(Range, E)(Range range, E element) -if (isBidirectionalRange!Range && is(typeof(range.back == element) : bool)) -{ - for (; !range.empty; range.popBack()) - { - if (range.back != element) - break; - } - return range; -} - -/// ditto -Range stripRight(alias pred, Range)(Range range) -if (isBidirectionalRange!Range && is(typeof(pred(range.back)) : bool)) -{ - for (; !range.empty; range.popBack()) - { - if (!pred(range.back)) - break; - } - return range; -} - -/// Strip leading and trailing elements equal to the target element. -@safe pure unittest -{ - assert(" foobar ".strip(' ') == "foobar"); - assert("00223.444500".strip('0') == "223.4445"); - assert("ëëêéüŗōpéêëë".strip('ë') == "êéüŗōpéê"); - assert([1, 1, 0, 1, 1].strip(1) == [0]); - assert([0.0, 0.01, 0.01, 0.0].strip(0).length == 2); -} - -/// Strip leading and trailing elements while the predicate returns true. -@safe pure unittest -{ - assert(" foobar ".strip!(a => a == ' ')() == "foobar"); - assert("00223.444500".strip!(a => a == '0')() == "223.4445"); - assert("ëëêéüŗōpéêëë".strip!(a => a == 'ë')() == "êéüŗōpéê"); - assert([1, 1, 0, 1, 1].strip!(a => a == 1)() == [0]); - assert([0.0, 0.01, 0.5, 0.6, 0.01, 0.0].strip!(a => a < 0.4)().length == 2); -} - -/// Strip leading elements equal to the target element. -@safe pure unittest -{ - assert(" foobar ".stripLeft(' ') == "foobar "); - assert("00223.444500".stripLeft('0') == "223.444500"); - assert("ůůűniçodêéé".stripLeft('ů') == "űniçodêéé"); - assert([1, 1, 0, 1, 1].stripLeft(1) == [0, 1, 1]); - assert([0.0, 0.01, 0.01, 0.0].stripLeft(0).length == 3); -} - -/// Strip leading elements while the predicate returns true. -@safe pure unittest -{ - assert(" foobar ".stripLeft!(a => a == ' ')() == "foobar "); - assert("00223.444500".stripLeft!(a => a == '0')() == "223.444500"); - assert("ůůűniçodêéé".stripLeft!(a => a == 'ů')() == "űniçodêéé"); - assert([1, 1, 0, 1, 1].stripLeft!(a => a == 1)() == [0, 1, 1]); - assert([0.0, 0.01, 0.10, 0.5, 0.6].stripLeft!(a => a < 0.4)().length == 2); -} - -/// Strip trailing elements equal to the target element. -@safe pure unittest -{ - assert(" foobar ".stripRight(' ') == " foobar"); - assert("00223.444500".stripRight('0') == "00223.4445"); - assert("ùniçodêéé".stripRight('é') == "ùniçodê"); - assert([1, 1, 0, 1, 1].stripRight(1) == [1, 1, 0]); - assert([0.0, 0.01, 0.01, 0.0].stripRight(0).length == 3); -} - -/// Strip trailing elements while the predicate returns true. -@safe pure unittest -{ - assert(" foobar ".stripRight!(a => a == ' ')() == " foobar"); - assert("00223.444500".stripRight!(a => a == '0')() == "00223.4445"); - assert("ùniçodêéé".stripRight!(a => a == 'é')() == "ùniçodê"); - assert([1, 1, 0, 1, 1].stripRight!(a => a == 1)() == [1, 1, 0]); - assert([0.0, 0.01, 0.10, 0.5, 0.6].stripRight!(a => a > 0.4)().length == 3); -} - -// swap -/** -Swaps `lhs` and `rhs`. The instances `lhs` and `rhs` are moved in -memory, without ever calling `opAssign`, nor any other function. `T` -need not be assignable at all to be swapped. - -If `lhs` and `rhs` reference the same instance, then nothing is done. - -`lhs` and `rhs` must be mutable. If `T` is a struct or union, then -its fields must also all be (recursively) mutable. - -Params: - lhs = Data to be swapped with `rhs`. - rhs = Data to be swapped with `lhs`. -*/ -void swap(T)(ref T lhs, ref T rhs) @trusted pure nothrow @nogc -if (isBlitAssignable!T && !is(typeof(lhs.proxySwap(rhs)))) -{ - import std.traits : hasAliasing, hasElaborateAssign, isAssignable, - isStaticArray; - static if (hasAliasing!T) if (!__ctfe) - { - import std.exception : doesPointTo; - assert(!doesPointTo(lhs, lhs), "Swap: lhs internal pointer."); - assert(!doesPointTo(rhs, rhs), "Swap: rhs internal pointer."); - assert(!doesPointTo(lhs, rhs), "Swap: lhs points to rhs."); - assert(!doesPointTo(rhs, lhs), "Swap: rhs points to lhs."); - } - - static if (hasElaborateAssign!T || !isAssignable!T) - { - if (&lhs != &rhs) - { - // For structs with non-trivial assignment, move memory directly - ubyte[T.sizeof] t = void; - auto a = (cast(ubyte*) &lhs)[0 .. T.sizeof]; - auto b = (cast(ubyte*) &rhs)[0 .. T.sizeof]; - t[] = a[]; - a[] = b[]; - b[] = t[]; - } - } - else - { - //Avoid assigning overlapping arrays. Dynamic arrays are fine, because - //it's their ptr and length properties which get assigned rather - //than their elements when assigning them, but static arrays are value - //types and therefore all of their elements get copied as part of - //assigning them, which would be assigning overlapping arrays if lhs - //and rhs were the same array. - static if (isStaticArray!T) - { - if (lhs.ptr == rhs.ptr) - return; - } - - // For non-elaborate-assign types, suffice to do the classic swap - static if (__traits(hasCopyConstructor, T)) - { - // don't invoke any elaborate constructors either - T tmp = void; - tmp = lhs; - } - else - auto tmp = lhs; - lhs = rhs; - rhs = tmp; - } -} - -/// -@safe unittest -{ - // Swapping POD (plain old data) types: - int a = 42, b = 34; - swap(a, b); - assert(a == 34 && b == 42); - - // Swapping structs with indirection: - static struct S { int x; char c; int[] y; } - S s1 = { 0, 'z', [ 1, 2 ] }; - S s2 = { 42, 'a', [ 4, 6 ] }; - swap(s1, s2); - assert(s1.x == 42); - assert(s1.c == 'a'); - assert(s1.y == [ 4, 6 ]); - - assert(s2.x == 0); - assert(s2.c == 'z'); - assert(s2.y == [ 1, 2 ]); - - // Immutables cannot be swapped: - immutable int imm1 = 1, imm2 = 2; - static assert(!__traits(compiles, swap(imm1, imm2))); - - int c = imm1 + 0; - int d = imm2 + 0; - swap(c, d); - assert(c == 2); - assert(d == 1); -} - -/// -@safe unittest -{ - // Non-copyable types can still be swapped. - static struct NoCopy - { - this(this) { assert(0); } - int n; - string s; - } - NoCopy nc1, nc2; - nc1.n = 127; nc1.s = "abc"; - nc2.n = 513; nc2.s = "uvwxyz"; - - swap(nc1, nc2); - assert(nc1.n == 513 && nc1.s == "uvwxyz"); - assert(nc2.n == 127 && nc2.s == "abc"); - - swap(nc1, nc1); - swap(nc2, nc2); - assert(nc1.n == 513 && nc1.s == "uvwxyz"); - assert(nc2.n == 127 && nc2.s == "abc"); - - // Types containing non-copyable fields can also be swapped. - static struct NoCopyHolder - { - NoCopy noCopy; - } - NoCopyHolder h1, h2; - h1.noCopy.n = 31; h1.noCopy.s = "abc"; - h2.noCopy.n = 65; h2.noCopy.s = null; - - swap(h1, h2); - assert(h1.noCopy.n == 65 && h1.noCopy.s == null); - assert(h2.noCopy.n == 31 && h2.noCopy.s == "abc"); - - swap(h1, h1); - swap(h2, h2); - assert(h1.noCopy.n == 65 && h1.noCopy.s == null); - assert(h2.noCopy.n == 31 && h2.noCopy.s == "abc"); - - // Const types cannot be swapped. - const NoCopy const1, const2; - assert(const1.n == 0 && const2.n == 0); - static assert(!__traits(compiles, swap(const1, const2))); -} - -// https://issues.dlang.org/show_bug.cgi?id=4789 -@safe unittest -{ - int[1] s = [1]; - swap(s, s); - - int[3] a = [1, 2, 3]; - swap(a[1], a[2]); - assert(a == [1, 3, 2]); -} - -@safe unittest -{ - static struct NoAssign - { - int i; - void opAssign(NoAssign) @disable; - } - auto s1 = NoAssign(1); - auto s2 = NoAssign(2); - swap(s1, s2); - assert(s1.i == 2); - assert(s2.i == 1); -} - -@safe unittest -{ - struct S - { - const int i; - int i2 = 2; - int i3 = 3; - } - S s; - static assert(!__traits(compiles, swap(s, s))); - swap(s.i2, s.i3); - assert(s.i2 == 3); - assert(s.i3 == 2); -} - -// https://issues.dlang.org/show_bug.cgi?id=11853 -@safe unittest -{ - import std.traits : isAssignable; - alias T = Tuple!(int, double); - static assert(isAssignable!T); -} - -// https://issues.dlang.org/show_bug.cgi?id=12024 -@safe unittest -{ - import std.datetime; - SysTime a, b; - swap(a, b); -} - -// https://issues.dlang.org/show_bug.cgi?id=9975 -@system unittest -{ - import std.exception : doesPointTo, mayPointTo; - static struct S2 - { - union - { - size_t sz; - string s; - } - } - S2 a , b; - a.sz = -1; - assert(!doesPointTo(a, b)); - assert( mayPointTo(a, b)); - swap(a, b); - - //Note: we can catch an error here, because there is no RAII in this test - import std.exception : assertThrown; - void* p, pp; - p = &p; - assertThrown!Error(move(p)); - assertThrown!Error(move(p, pp)); - assertThrown!Error(swap(p, pp)); -} - -@system unittest -{ - static struct A - { - int* x; - this(this) { x = new int; } - } - A a1, a2; - swap(a1, a2); - - static struct B - { - int* x; - void opAssign(B) { x = new int; } - } - B b1, b2; - swap(b1, b2); -} - -// https://issues.dlang.org/show_bug.cgi?id=20732 -@safe unittest -{ - static struct A - { - int x; - this(scope ref return const A other) - { - import std.stdio; - x = other.x; - // note, struct functions inside @safe functions infer ALL - // attributes, so the following 3 lines are meant to prevent this. - new int; // prevent @nogc inference - writeln("impure"); // prevent pure inference - throw new Exception(""); // prevent nothrow inference - } - } - - A a1, a2; - swap(a1, a2); - - A[1] a3, a4; - swap(a3, a4); -} - -/// ditto -void swap(T)(ref T lhs, ref T rhs) -if (is(typeof(lhs.proxySwap(rhs)))) -{ - lhs.proxySwap(rhs); -} - -/** -Swaps two elements in-place of a range `r`, -specified by their indices `i1` and `i2`. - -Params: - r = a range with swappable elements - i1 = first index - i2 = second index -*/ -void swapAt(R)(auto ref R r, size_t i1, size_t i2) -{ - static if (is(typeof(&r.swapAt))) - { - r.swapAt(i1, i2); - } - else static if (is(typeof(&r[i1]))) - { - swap(r[i1], r[i2]); - } - else - { - if (i1 == i2) return; - auto t1 = r.moveAt(i1); - auto t2 = r.moveAt(i2); - r[i2] = t1; - r[i1] = t2; - } -} - -/// -pure @safe nothrow unittest -{ - import std.algorithm.comparison : equal; - auto a = [1, 2, 3]; - a.swapAt(1, 2); - assert(a.equal([1, 3, 2])); -} - -pure @safe nothrow unittest -{ - import std.algorithm.comparison : equal; - auto a = [4, 5, 6]; - a.swapAt(1, 1); - assert(a.equal([4, 5, 6])); -} - -pure @safe nothrow unittest -{ - // test non random access ranges - import std.algorithm.comparison : equal; - import std.array : array; - - char[] b = ['a', 'b', 'c']; - b.swapAt(1, 2); - assert(b.equal(['a', 'c', 'b'])); - - int[3] c = [1, 2, 3]; - c.swapAt(1, 2); - assert(c.array.equal([1, 3, 2])); - - // opIndex returns lvalue - struct RandomIndexType(T) - { - T payload; - - @property ref auto opIndex(size_t i) - { - return payload[i]; - } - - } - auto d = RandomIndexType!(int[])([4, 5, 6]); - d.swapAt(1, 2); - assert(d.payload.equal([4, 6, 5])); - - // custom moveAt and opIndexAssign - struct RandomMoveAtType(T) - { - T payload; - - ElementType!T moveAt(size_t i) - { - return payload.moveAt(i); - } - - void opIndexAssign(ElementType!T val, size_t idx) - { - payload[idx] = val; - } - } - auto e = RandomMoveAtType!(int[])([7, 8, 9]); - e.swapAt(1, 2); - assert(e.payload.equal([7, 9, 8])); - - - // custom swapAt - struct RandomSwapAtType(T) - { - T payload; - - void swapAt(size_t i) - { - return payload.swapAt(i); - } - } - auto f = RandomMoveAtType!(int[])([10, 11, 12]); - swapAt(f, 1, 2); - assert(f.payload.equal([10, 12, 11])); -} - -private void swapFront(R1, R2)(R1 r1, R2 r2) -if (isInputRange!R1 && isInputRange!R2) -{ - static if (is(typeof(swap(r1.front, r2.front)))) - { - swap(r1.front, r2.front); - } - else - { - auto t1 = moveFront(r1), t2 = moveFront(r2); - r1.front = move(t2); - r2.front = move(t1); - } -} - -// swapRanges -/** -Swaps all elements of `r1` with successive elements in `r2`. -Returns a tuple containing the remainder portions of `r1` and $(D -r2) that were not swapped (one of them will be empty). The ranges may -be of different types but must have the same element type and support -swapping. - -Params: - r1 = an $(REF_ALTTEXT input range, isInputRange, std,range,primitives) - with swappable elements - r2 = an $(REF_ALTTEXT input range, isInputRange, std,range,primitives) - with swappable elements - -Returns: - Tuple containing the remainder portions of r1 and r2 that were not swapped -*/ -Tuple!(InputRange1, InputRange2) -swapRanges(InputRange1, InputRange2)(InputRange1 r1, InputRange2 r2) -if (hasSwappableElements!InputRange1 && hasSwappableElements!InputRange2 - && is(ElementType!InputRange1 == ElementType!InputRange2)) -{ - for (; !r1.empty && !r2.empty; r1.popFront(), r2.popFront()) - { - swap(r1.front, r2.front); - } - return tuple(r1, r2); -} - -/// -@safe unittest -{ - import std.range : empty; - int[] a = [ 100, 101, 102, 103 ]; - int[] b = [ 0, 1, 2, 3 ]; - auto c = swapRanges(a[1 .. 3], b[2 .. 4]); - assert(c[0].empty && c[1].empty); - assert(a == [ 100, 2, 3, 103 ]); - assert(b == [ 0, 1, 101, 102 ]); -} - -/** -Initializes each element of `range` with `value`. -Assumes that the elements of the range are uninitialized. -This is of interest for structs that -define copy constructors (for all other types, $(LREF fill) and -uninitializedFill are equivalent). - -Params: - range = An - $(REF_ALTTEXT input range, isInputRange, std,range,primitives) - that exposes references to its elements and has assignable - elements - value = Assigned to each element of range - -See_Also: - $(LREF fill) - $(LREF initializeAll) - */ -void uninitializedFill(Range, Value)(Range range, Value value) -if (isInputRange!Range && hasLvalueElements!Range && is(typeof(range.front = value))) -{ - import std.traits : hasElaborateAssign; - - alias T = ElementType!Range; - static if (hasElaborateAssign!T) - { - import core.internal.lifetime : emplaceRef; - - // Must construct stuff by the book - for (; !range.empty; range.popFront()) - emplaceRef!T(range.front, value); - } - else - // Doesn't matter whether fill is initialized or not - return fill(range, value); -} - -/// -nothrow @system unittest -{ - import core.stdc.stdlib : malloc, free; - - auto s = (cast(int*) malloc(5 * int.sizeof))[0 .. 5]; - uninitializedFill(s, 42); - assert(s == [ 42, 42, 42, 42, 42 ]); - - scope(exit) free(s.ptr); -} diff --git a/phobos/std/algorithm/package.d b/phobos/std/algorithm/package.d deleted file mode 100644 index 71bd1d9..0000000 --- a/phobos/std/algorithm/package.d +++ /dev/null @@ -1,202 +0,0 @@ -// Written in the D programming language. - -/** -This package implements generic algorithms oriented towards the processing of -sequences. Sequences processed by these functions define range-based -interfaces. See also $(MREF_ALTTEXT Reference on ranges, std, range) and -$(HTTP ddili.org/ders/d.en/ranges.html, tutorial on ranges). - -$(SCRIPT inhibitQuickIndex = 1;) - -Algorithms are categorized into the following submodules: - -$(DIVC quickindex, -$(BOOKTABLE , -$(TR $(TH Submodule) $(TH Functions) -) -$(TR - $(TDNW $(SUBMODULE Searching, searching)) - $(TD - $(SUBREF searching, all) - $(SUBREF searching, any) - $(SUBREF searching, balancedParens) - $(SUBREF searching, boyerMooreFinder) - $(SUBREF searching, canFind) - $(SUBREF searching, commonPrefix) - $(SUBREF searching, count) - $(SUBREF searching, countUntil) - $(SUBREF searching, endsWith) - $(SUBREF searching, find) - $(SUBREF searching, findAdjacent) - $(SUBREF searching, findAmong) - $(SUBREF searching, findSkip) - $(SUBREF searching, findSplit) - $(SUBREF searching, findSplitAfter) - $(SUBREF searching, findSplitBefore) - $(SUBREF searching, minCount) - $(SUBREF searching, maxCount) - $(SUBREF searching, minElement) - $(SUBREF searching, maxElement) - $(SUBREF searching, minIndex) - $(SUBREF searching, maxIndex) - $(SUBREF searching, minPos) - $(SUBREF searching, maxPos) - $(SUBREF searching, skipOver) - $(SUBREF searching, startsWith) - $(SUBREF searching, until) - ) -) -$(TR - $(TDNW $(SUBMODULE Comparison, comparison)) - $(TD - $(SUBREF comparison, among) - $(SUBREF comparison, castSwitch) - $(SUBREF comparison, clamp) - $(SUBREF comparison, cmp) - $(SUBREF comparison, either) - $(SUBREF comparison, equal) - $(SUBREF comparison, isPermutation) - $(SUBREF comparison, isSameLength) - $(SUBREF comparison, levenshteinDistance) - $(SUBREF comparison, levenshteinDistanceAndPath) - $(SUBREF comparison, max) - $(SUBREF comparison, min) - $(SUBREF comparison, mismatch) - $(SUBREF comparison, predSwitch) - ) -) -$(TR - $(TDNW $(SUBMODULE Iteration, iteration)) - $(TD - $(SUBREF iteration, cache) - $(SUBREF iteration, cacheBidirectional) - $(SUBREF iteration, chunkBy) - $(SUBREF iteration, cumulativeFold) - $(SUBREF iteration, each) - $(SUBREF iteration, filter) - $(SUBREF iteration, filterBidirectional) - $(SUBREF iteration, fold) - $(SUBREF iteration, group) - $(SUBREF iteration, joiner) - $(SUBREF iteration, map) - $(SUBREF iteration, mean) - $(SUBREF iteration, permutations) - $(SUBREF iteration, reduce) - $(SUBREF iteration, splitWhen) - $(SUBREF iteration, splitter) - $(SUBREF iteration, substitute) - $(SUBREF iteration, sum) - $(SUBREF iteration, uniq) - ) -) -$(TR - $(TDNW $(SUBMODULE Sorting, sorting)) - $(TD - $(SUBREF sorting, completeSort) - $(SUBREF sorting, isPartitioned) - $(SUBREF sorting, isSorted) - $(SUBREF sorting, isStrictlyMonotonic) - $(SUBREF sorting, ordered) - $(SUBREF sorting, strictlyOrdered) - $(SUBREF sorting, makeIndex) - $(SUBREF sorting, merge) - $(SUBREF sorting, multiSort) - $(SUBREF sorting, nextEvenPermutation) - $(SUBREF sorting, nextPermutation) - $(SUBREF sorting, nthPermutation) - $(SUBREF sorting, partialSort) - $(SUBREF sorting, partition) - $(SUBREF sorting, partition3) - $(SUBREF sorting, schwartzSort) - $(SUBREF sorting, sort) - $(SUBREF sorting, topN) - $(SUBREF sorting, topNCopy) - $(SUBREF sorting, topNIndex) - ) -) -$(TR - $(TDNW Set operations $(BR)($(SUBMODULE setops, setops))) - $(TD - $(SUBREF setops, cartesianProduct) - $(SUBREF setops, largestPartialIntersection) - $(SUBREF setops, largestPartialIntersectionWeighted) - $(SUBREF setops, multiwayMerge) - $(SUBREF setops, multiwayUnion) - $(SUBREF setops, setDifference) - $(SUBREF setops, setIntersection) - $(SUBREF setops, setSymmetricDifference) - ) -) -$(TR - $(TDNW $(SUBMODULE Mutation, mutation)) - $(TD - $(SUBREF mutation, bringToFront) - $(SUBREF mutation, copy) - $(SUBREF mutation, fill) - $(SUBREF mutation, initializeAll) - $(SUBREF mutation, move) - $(SUBREF mutation, moveAll) - $(SUBREF mutation, moveSome) - $(SUBREF mutation, moveEmplace) - $(SUBREF mutation, moveEmplaceAll) - $(SUBREF mutation, moveEmplaceSome) - $(SUBREF mutation, remove) - $(SUBREF mutation, reverse) - $(SUBREF mutation, strip) - $(SUBREF mutation, stripLeft) - $(SUBREF mutation, stripRight) - $(SUBREF mutation, swap) - $(SUBREF mutation, swapRanges) - $(SUBREF mutation, uninitializedFill) - ) -) -)) - -Many functions in this package are parameterized with a $(GLOSSARY predicate). -The predicate may be any suitable callable type -(a function, a delegate, a $(GLOSSARY functor), or a lambda), or a -compile-time string. The string may consist of $(B any) legal D -expression that uses the symbol `a` (for unary functions) or the -symbols `a` and `b` (for binary functions). These names will NOT -interfere with other homonym symbols in user code because they are -evaluated in a different context. The default for all binary -comparison predicates is `"a == b"` for unordered operations and -`"a < b"` for ordered operations. - -Example: - ----- -int[] a = ...; -static bool greater(int a, int b) -{ - return a > b; -} -sort!greater(a); // predicate as alias -sort!((a, b) => a > b)(a); // predicate as a lambda. -sort!"a > b"(a); // predicate as string - // (no ambiguity with array name) -sort(a); // no predicate, "a < b" is implicit ----- - -Macros: -SUBMODULE = $(MREF_ALTTEXT $1, std, algorithm, $2) -SUBREF = $(REF_ALTTEXT $(TT $2), $2, std, algorithm, $1)$(NBSP) - -Copyright: Andrei Alexandrescu 2008-. - -License: $(HTTP boost.org/LICENSE_1_0.txt, Boost License 1.0). - -Authors: $(HTTP erdani.com, Andrei Alexandrescu) - -Source: $(PHOBOSSRC std/algorithm/package.d) - */ -module std.algorithm; - -public import std.algorithm.comparison; -public import std.algorithm.iteration; -public import std.algorithm.mutation; -public import std.algorithm.searching; -public import std.algorithm.setops; -public import std.algorithm.sorting; - -static import std.functional; diff --git a/phobos/std/algorithm/searching.d b/phobos/std/algorithm/searching.d deleted file mode 100644 index 42a9df5..0000000 --- a/phobos/std/algorithm/searching.d +++ /dev/null @@ -1,5318 +0,0 @@ -// Written in the D programming language. -/** -This is a submodule of $(MREF std, algorithm). -It contains generic searching algorithms. - -$(SCRIPT inhibitQuickIndex = 1;) -$(BOOKTABLE Cheat Sheet, -$(TR $(TH Function Name) $(TH Description)) -$(T2 all, - `all!"a > 0"([1, 2, 3, 4])` returns `true` because all elements - are positive) -$(T2 any, - `any!"a > 0"([1, 2, -3, -4])` returns `true` because at least one - element is positive) -$(T2 balancedParens, - `balancedParens("((1 + 1) / 2)", '(', ')')` returns `true` because the - string has balanced parentheses.) -$(T2 boyerMooreFinder, - `find("hello world", boyerMooreFinder("or"))` returns `"orld"` - using the $(LINK2 https://en.wikipedia.org/wiki/Boyer%E2%80%93Moore_string_search_algorithm, - Boyer-Moore _algorithm).) -$(T2 canFind, - `canFind("hello world", "or")` returns `true`.) -$(T2 count, - Counts all elements or elements matching a predicate, specific element or sub-range.$(BR) - `count([1, 2, 1])` returns `3`, - `count([1, 2, 1], 1)` returns `2` and - `count!"a < 0"([1, -3, 0])` returns `1`.) -$(T2 countUntil, - `countUntil(a, b)` returns the number of steps taken in `a` to - reach `b`; for example, `countUntil("hello!", "o")` returns - `4`.) -$(T2 commonPrefix, - `commonPrefix("parakeet", "parachute")` returns `"para"`.) -$(T2 endsWith, - `endsWith("rocks", "ks")` returns `true`.) -$(T2 find, - `find("hello world", "or")` returns `"orld"` using linear search. - (For binary search refer to $(REF SortedRange, std,range).)) -$(T2 findAdjacent, - `findAdjacent([1, 2, 3, 3, 4])` returns the subrange starting with - two equal adjacent elements, i.e. `[3, 3, 4]`.) -$(T2 findAmong, - `findAmong("abcd", "qcx")` returns `"cd"` because `'c'` is - among `"qcx"`.) -$(T2 findSkip, - If `a = "abcde"`, then `findSkip(a, "x")` returns `false` and - leaves `a` unchanged, whereas `findSkip(a, "c")` advances `a` - to `"de"` and returns `true`.) -$(T2 findSplit, - `findSplit("abcdefg", "de")` returns a tuple of three ranges `"abc"`, - `"de"`, and `"fg"`.) -$(T2 findSplitAfter, -`findSplitAfter("abcdefg", "de")` returns a tuple of two ranges `"abcde"` - and `"fg"`.) -$(T2 findSplitBefore, - `findSplitBefore("abcdefg", "de")` returns a tuple of two ranges `"abc"` - and `"defg"`.) -$(T2 minCount, - `minCount([2, 1, 1, 4, 1])` returns `tuple(1, 3)`.) -$(T2 maxCount, - `maxCount([2, 4, 1, 4, 1])` returns `tuple(4, 2)`.) -$(T2 minElement, - Selects the minimal element of a range. - `minElement([3, 4, 1, 2])` returns `1`.) -$(T2 maxElement, - Selects the maximal element of a range. - `maxElement([3, 4, 1, 2])` returns `4`.) -$(T2 minIndex, - Index of the minimal element of a range. - `minIndex([3, 4, 1, 2])` returns `2`.) -$(T2 maxIndex, - Index of the maximal element of a range. - `maxIndex([3, 4, 1, 2])` returns `1`.) -$(T2 minPos, - `minPos([2, 3, 1, 3, 4, 1])` returns the subrange `[1, 3, 4, 1]`, - i.e., positions the range at the first occurrence of its minimal - element.) -$(T2 maxPos, - `maxPos([2, 3, 1, 3, 4, 1])` returns the subrange `[4, 1]`, - i.e., positions the range at the first occurrence of its maximal - element.) -$(T2 skipOver, - Assume `a = "blah"`. Then `skipOver(a, "bi")` leaves `a` - unchanged and returns `false`, whereas `skipOver(a, "bl")` - advances `a` to refer to `"ah"` and returns `true`.) -$(T2 startsWith, - `startsWith("hello, world", "hello")` returns `true`.) -$(T2 until, - Lazily iterates a range until a specific value is found.) -) - -Copyright: Andrei Alexandrescu 2008-. - -License: $(HTTP boost.org/LICENSE_1_0.txt, Boost License 1.0). - -Authors: $(HTTP erdani.com, Andrei Alexandrescu) - -Source: $(PHOBOSSRC std/algorithm/searching.d) - -Macros: -T2=$(TR $(TDNW $(LREF $1)) $(TD $+)) - */ -module std.algorithm.searching; - -import std.functional : unaryFun, binaryFun; -import std.meta : allSatisfy; -import std.range.primitives; -import std.traits; -import std.typecons : Tuple, Flag, Yes, No, tuple; - -/++ -Checks if $(I _all) of the elements satisfy `pred`. - +/ -template all(alias pred = "a") -{ - /++ - Returns `true` if and only if the input range `range` is empty - or $(I _all) values found in `range` satisfy the predicate `pred`. - Performs (at most) $(BIGOH range.length) evaluations of `pred`. - +/ - bool all(Range)(Range range) - if (isInputRange!Range && - (__traits(isTemplate, pred) || is(typeof(unaryFun!pred(range.front))))) - { - import std.functional : not; - - return find!(not!(unaryFun!pred))(range).empty; - } -} - -/// -@safe unittest -{ - assert( all!"a & 1"([1, 3, 5, 7, 9])); - assert(!all!"a & 1"([1, 2, 3, 5, 7, 9])); -} - -/++ -`all` can also be used without a predicate, if its items can be -evaluated to true or false in a conditional statement. This can be a -convenient way to quickly evaluate that $(I _all) of the elements of a range -are true. - +/ -@safe unittest -{ - int[3] vals = [5, 3, 18]; - assert( all(vals[])); -} - -@safe unittest -{ - int x = 1; - assert(all!(a => a > x)([2, 3])); - assert(all!"a == 0x00c9"("\xc3\x89")); // Test that `all` auto-decodes. -} - -/++ -Checks if $(I _any) of the elements satisfies `pred`. -`!any` can be used to verify that $(I none) of the elements satisfy -`pred`. -This is sometimes called `exists` in other languages. - +/ -template any(alias pred = "a") -{ - /++ - Returns `true` if and only if the input range `range` is non-empty - and $(I _any) value found in `range` satisfies the predicate - `pred`. - Performs (at most) $(BIGOH range.length) evaluations of `pred`. - +/ - bool any(Range)(Range range) - if (isInputRange!Range && - (__traits(isTemplate, pred) || is(typeof(unaryFun!pred(range.front))))) - { - return !find!pred(range).empty; - } -} - -/// -@safe unittest -{ - import std.ascii : isWhite; - assert( all!(any!isWhite)(["a a", "b b"])); - assert(!any!(all!isWhite)(["a a", "b b"])); -} - -/++ -`any` can also be used without a predicate, if its items can be -evaluated to true or false in a conditional statement. `!any` can be a -convenient way to quickly test that $(I none) of the elements of a range -evaluate to true. - +/ -@safe unittest -{ - int[3] vals1 = [0, 0, 0]; - assert(!any(vals1[])); //none of vals1 evaluate to true - - int[3] vals2 = [2, 0, 2]; - assert( any(vals2[])); - assert(!all(vals2[])); - - int[3] vals3 = [3, 3, 3]; - assert( any(vals3[])); - assert( all(vals3[])); -} - -@safe unittest -{ - auto a = [ 1, 2, 0, 4 ]; - assert(any!"a == 2"(a)); - assert(any!"a == 0x3000"("\xe3\x80\x80")); // Test that `any` auto-decodes. -} - -// balancedParens -/** -Checks whether `r` has "balanced parentheses", i.e. all instances -of `lPar` are closed by corresponding instances of `rPar`. The -parameter `maxNestingLevel` controls the nesting level allowed. The -most common uses are the default or `0`. In the latter case, no -nesting is allowed. - -Params: - r = The range to check. - lPar = The element corresponding with a left (opening) parenthesis. - rPar = The element corresponding with a right (closing) parenthesis. - maxNestingLevel = The maximum allowed nesting level. - -Returns: - true if the given range has balanced parenthesis within the given maximum - nesting level; false otherwise. -*/ -bool balancedParens(Range, E)(Range r, E lPar, E rPar, - size_t maxNestingLevel = size_t.max) -if (isInputRange!(Range) && is(typeof(r.front == lPar))) -{ - size_t count; - - static if (is(immutable ElementEncodingType!Range == immutable E) && isNarrowString!Range) - { - import std.utf : byCodeUnit; - auto rn = r.byCodeUnit; - } - else - { - alias rn = r; - } - - for (; !rn.empty; rn.popFront()) - { - if (rn.front == lPar) - { - if (count > maxNestingLevel) return false; - ++count; - } - else if (rn.front == rPar) - { - if (!count) return false; - --count; - } - } - return count == 0; -} - -/// -@safe pure unittest -{ - auto s = "1 + (2 * (3 + 1 / 2)"; - assert(!balancedParens(s, '(', ')')); - s = "1 + (2 * (3 + 1) / 2)"; - assert(balancedParens(s, '(', ')')); - s = "1 + (2 * (3 + 1) / 2)"; - assert(!balancedParens(s, '(', ')', 0)); - s = "1 + (2 * 3 + 1) / (2 - 5)"; - assert(balancedParens(s, '(', ')', 0)); - s = "f(x) = ⌈x⌉"; - assert(balancedParens(s, '⌈', '⌉')); -} - -/** - * Sets up Boyer-Moore matching for use with `find` below. - * By default, elements are compared for equality. - * - * `BoyerMooreFinder` allocates GC memory. - * - * Params: - * pred = Predicate used to compare elements. - * needle = A random-access range with length and slicing. - * - * Returns: - * An instance of `BoyerMooreFinder` that can be used with `find()` to - * invoke the Boyer-Moore matching algorithm for finding of `needle` in a - * given haystack. - */ -struct BoyerMooreFinder(alias pred, Range) -{ -private: - size_t[] skip; // GC allocated - ptrdiff_t[ElementType!(Range)] occ; // GC allocated - Range needle; - - ptrdiff_t occurrence(ElementType!(Range) c) scope - { - auto p = c in occ; - return p ? *p : -1; - } - -/* -This helper function checks whether the last "portion" bytes of -"needle" (which is "nlen" bytes long) exist within the "needle" at -offset "offset" (counted from the end of the string), and whether the -character preceding "offset" is not a match. Notice that the range -being checked may reach beyond the beginning of the string. Such range -is ignored. - */ - static bool needlematch(R)(R needle, - size_t portion, size_t offset) - { - import std.algorithm.comparison : equal; - ptrdiff_t virtual_begin = needle.length - offset - portion; - ptrdiff_t ignore = 0; - if (virtual_begin < 0) - { - ignore = -virtual_begin; - virtual_begin = 0; - } - if (virtual_begin > 0 - && needle[virtual_begin - 1] == needle[$ - portion - 1]) - return 0; - - immutable delta = portion - ignore; - return equal(needle[needle.length - delta .. needle.length], - needle[virtual_begin .. virtual_begin + delta]); - } - -public: - /// - this(Range needle) - { - if (!needle.length) return; - this.needle = needle; - /* Populate table with the analysis of the needle */ - /* But ignoring the last letter */ - foreach (i, n ; needle[0 .. $ - 1]) - { - this.occ[n] = i; - } - /* Preprocess #2: init skip[] */ - /* Note: This step could be made a lot faster. - * A simple implementation is shown here. */ - this.skip = new size_t[needle.length]; - foreach (a; 0 .. needle.length) - { - size_t value = 0; - while (value < needle.length - && !needlematch(needle, a, value)) - { - ++value; - } - this.skip[needle.length - a - 1] = value; - } - } - - /// - Range beFound(Range haystack) scope - { - import std.algorithm.comparison : max; - - if (!needle.length) return haystack; - if (needle.length > haystack.length) return haystack[$ .. $]; - /* Search: */ - immutable limit = haystack.length - needle.length; - for (size_t hpos = 0; hpos <= limit; ) - { - size_t npos = needle.length - 1; - while (pred(needle[npos], haystack[npos+hpos])) - { - if (npos == 0) return haystack[hpos .. $]; - --npos; - } - hpos += max(skip[npos], cast(ptrdiff_t) npos - occurrence(haystack[npos+hpos])); - } - return haystack[$ .. $]; - } - - /// - @property size_t length() - { - return needle.length; - } - - /// - alias opDollar = length; -} - -/// Ditto -BoyerMooreFinder!(binaryFun!(pred), Range) boyerMooreFinder -(alias pred = "a == b", Range) -(Range needle) -if ((isRandomAccessRange!(Range) && hasSlicing!Range) || isSomeString!Range) -{ - return typeof(return)(needle); -} - -/// -@safe pure nothrow unittest -{ - auto bmFinder = boyerMooreFinder("TG"); - - string r = "TAGTGCCTGA"; - // search for the first match in the haystack r - r = bmFinder.beFound(r); - assert(r == "TGCCTGA"); - - // continue search in haystack - r = bmFinder.beFound(r[2 .. $]); - assert(r == "TGA"); -} - -/** -Returns the common prefix of two ranges. - -Params: - pred = The predicate to use in comparing elements for commonality. Defaults - to equality `"a == b"`. - - r1 = A $(REF_ALTTEXT forward range, isForwardRange, std,range,primitives) of - elements. - - r2 = An $(REF_ALTTEXT input range, isInputRange, std,range,primitives) of - elements. - -Returns: -A slice of `r1` which contains the characters that both ranges start with, -if the first argument is a string; otherwise, the same as the result of -`takeExactly(r1, n)`, where `n` is the number of elements in the common -prefix of both ranges. - -See_Also: - $(REF takeExactly, std,range) - */ -auto commonPrefix(alias pred = "a == b", R1, R2)(R1 r1, R2 r2) -if (isForwardRange!R1 && isInputRange!R2 && - !isNarrowString!R1 && - is(typeof(binaryFun!pred(r1.front, r2.front)))) -{ - import std.algorithm.comparison : min; - static if (isRandomAccessRange!R1 && isRandomAccessRange!R2 && - hasLength!R1 && hasLength!R2 && - hasSlicing!R1) - { - immutable limit = min(r1.length, r2.length); - foreach (i; 0 .. limit) - { - if (!binaryFun!pred(r1[i], r2[i])) - { - return r1[0 .. i]; - } - } - return r1[0 .. limit]; - } - else - { - import std.range : takeExactly; - auto result = r1.save; - size_t i = 0; - for (; - !r1.empty && !r2.empty && binaryFun!pred(r1.front, r2.front); - ++i, r1.popFront(), r2.popFront()) - {} - return takeExactly(result, i); - } -} - -/// -@safe unittest -{ - assert(commonPrefix("hello, world", "hello, there") == "hello, "); -} - -/// ditto -auto commonPrefix(alias pred, R1, R2)(R1 r1, R2 r2) -if (isNarrowString!R1 && isInputRange!R2 && - is(typeof(binaryFun!pred(r1.front, r2.front)))) -{ - import std.utf : decode; - - auto result = r1.save; - immutable len = r1.length; - size_t i = 0; - - for (size_t j = 0; i < len && !r2.empty; r2.popFront(), i = j) - { - immutable f = decode(r1, j); - if (!binaryFun!pred(f, r2.front)) - break; - } - - return result[0 .. i]; -} - -/// ditto -auto commonPrefix(R1, R2)(R1 r1, R2 r2) -if (isNarrowString!R1 && isInputRange!R2 && !isNarrowString!R2 && - is(typeof(r1.front == r2.front))) -{ - return commonPrefix!"a == b"(r1, r2); -} - -/// ditto -auto commonPrefix(R1, R2)(R1 r1, R2 r2) -if (isNarrowString!R1 && isNarrowString!R2) -{ - import std.algorithm.comparison : min; - - static if (ElementEncodingType!R1.sizeof == ElementEncodingType!R2.sizeof) - { - import std.utf : stride, UTFException; - - immutable limit = min(r1.length, r2.length); - for (size_t i = 0; i < limit;) - { - immutable codeLen = stride(r1, i); - size_t j = 0; - - for (; j < codeLen && i < limit; ++i, ++j) - { - if (r1[i] != r2[i]) - return r1[0 .. i - j]; - } - - if (i == limit && j < codeLen) - throw new UTFException("Invalid UTF-8 sequence", i); - } - return r1[0 .. limit]; - } - else - return commonPrefix!"a == b"(r1, r2); -} - -@safe unittest -{ - import std.algorithm.comparison : equal; - import std.algorithm.iteration : filter; - import std.conv : to; - import std.exception : assertThrown; - import std.meta : AliasSeq; - import std.range; - import std.utf : UTFException; - - assert(commonPrefix([1, 2, 3], [1, 2, 3, 4, 5]) == [1, 2, 3]); - assert(commonPrefix([1, 2, 3, 4, 5], [1, 2, 3]) == [1, 2, 3]); - assert(commonPrefix([1, 2, 3, 4], [1, 2, 3, 4]) == [1, 2, 3, 4]); - assert(commonPrefix([1, 2, 3], [7, 2, 3, 4, 5]).empty); - assert(commonPrefix([7, 2, 3, 4, 5], [1, 2, 3]).empty); - assert(commonPrefix([1, 2, 3], cast(int[]) null).empty); - assert(commonPrefix(cast(int[]) null, [1, 2, 3]).empty); - assert(commonPrefix(cast(int[]) null, cast(int[]) null).empty); - - static foreach (S; AliasSeq!(char[], const(char)[], string, - wchar[], const(wchar)[], wstring, - dchar[], const(dchar)[], dstring)) - { - static foreach (T; AliasSeq!(string, wstring, dstring)) - { - assert(commonPrefix(to!S(""), to!T("")).empty); - assert(commonPrefix(to!S(""), to!T("hello")).empty); - assert(commonPrefix(to!S("hello"), to!T("")).empty); - assert(commonPrefix(to!S("hello, world"), to!T("hello, there")) == to!S("hello, ")); - assert(commonPrefix(to!S("hello, there"), to!T("hello, world")) == to!S("hello, ")); - assert(commonPrefix(to!S("hello, "), to!T("hello, world")) == to!S("hello, ")); - assert(commonPrefix(to!S("hello, world"), to!T("hello, ")) == to!S("hello, ")); - assert(commonPrefix(to!S("hello, world"), to!T("hello, world")) == to!S("hello, world")); - - // https://issues.dlang.org/show_bug.cgi?id=8890 - assert(commonPrefix(to!S("Пиво"), to!T("Пони"))== to!S("П")); - assert(commonPrefix(to!S("Пони"), to!T("Пиво"))== to!S("П")); - assert(commonPrefix(to!S("Пиво"), to!T("Пиво"))== to!S("Пиво")); - assert(commonPrefix(to!S("\U0010FFFF\U0010FFFB\U0010FFFE"), - to!T("\U0010FFFF\U0010FFFB\U0010FFFC")) == to!S("\U0010FFFF\U0010FFFB")); - assert(commonPrefix(to!S("\U0010FFFF\U0010FFFB\U0010FFFC"), - to!T("\U0010FFFF\U0010FFFB\U0010FFFE")) == to!S("\U0010FFFF\U0010FFFB")); - assert(commonPrefix!"a != b"(to!S("Пиво"), to!T("онво")) == to!S("Пи")); - assert(commonPrefix!"a != b"(to!S("онво"), to!T("Пиво")) == to!S("он")); - } - - static assert(is(typeof(commonPrefix(to!S("Пиво"), filter!"true"("Пони"))) == S)); - assert(equal(commonPrefix(to!S("Пиво"), filter!"true"("Пони")), to!S("П"))); - - static assert(is(typeof(commonPrefix(filter!"true"("Пиво"), to!S("Пони"))) == - typeof(takeExactly(filter!"true"("П"), 1)))); - assert(equal(commonPrefix(filter!"true"("Пиво"), to!S("Пони")), takeExactly(filter!"true"("П"), 1))); - } - - assertThrown!UTFException(commonPrefix("\U0010FFFF\U0010FFFB", "\U0010FFFF\U0010FFFB"[0 .. $ - 1])); - - assert(commonPrefix("12345"d, [49, 50, 51, 60, 60]) == "123"d); - assert(commonPrefix([49, 50, 51, 60, 60], "12345" ) == [49, 50, 51]); - assert(commonPrefix([49, 50, 51, 60, 60], "12345"d) == [49, 50, 51]); - - assert(commonPrefix!"a == ('0' + b)"("12345" , [1, 2, 3, 9, 9]) == "123"); - assert(commonPrefix!"a == ('0' + b)"("12345"d, [1, 2, 3, 9, 9]) == "123"d); - assert(commonPrefix!"('0' + a) == b"([1, 2, 3, 9, 9], "12345" ) == [1, 2, 3]); - assert(commonPrefix!"('0' + a) == b"([1, 2, 3, 9, 9], "12345"d) == [1, 2, 3]); -} - -// count -/** -Counts matches of `needle` in `haystack`. - -The first overload counts each element `e` in `haystack` for -which `pred(e, needle)` is `true`. `pred` defaults to -equality. Performs $(BIGOH haystack.length) evaluations of `pred`. - -The second overload counts the number of times `needle` was matched in -`haystack`. `pred` compares elements in each range. -Throws an exception if `needle.empty` is `true`, as the _count -of the empty range in any range would be infinite. Overlapped counts -are *not* considered, for example `count("aaa", "aa")` is `1`, not -`2`. - -Note: Regardless of the overload, `count` will not accept -infinite ranges for `haystack`. - -Params: - pred = The predicate to compare elements. - haystack = The range to _count. - needle = The element or sub-range to _count in `haystack`. - -Returns: - The number of matches in `haystack`. -*/ -size_t count(alias pred = "a == b", Range, E)(Range haystack, E needle) -if (isInputRange!Range && !isInfinite!Range && - is(typeof(binaryFun!pred(haystack.front, needle)))) -{ - bool pred2(ElementType!Range a) { return binaryFun!pred(a, needle); } - return count!pred2(haystack); -} - -/// -@safe unittest -{ - // count elements in range - int[] a = [ 1, 2, 4, 3, 2, 5, 3, 2, 4 ]; - assert(count(a, 2) == 3); - assert(count!("a > b")(a, 2) == 5); -} - -/// -@safe unittest -{ - import std.uni : toLower; - // count range in range - assert(count("abcadfabf", "ab") == 2); - assert(count("ababab", "abab") == 1); - assert(count("ababab", "abx") == 0); - // fuzzy count range in range - assert(count!((a, b) => toLower(a) == toLower(b))("AbcAdFaBf", "ab") == 2); -} - -@safe unittest -{ - import std.conv : text; - - int[] a = [ 1, 2, 4, 3, 2, 5, 3, 2, 4 ]; - assert(count(a, 2) == 3, text(count(a, 2))); - assert(count!("a > b")(a, 2) == 5, text(count!("a > b")(a, 2))); - - // check strings - assert(count("日本語") == 3); - assert(count("日本語"w) == 3); - assert(count("日本語"d) == 3); - - assert(count!("a == '日'")("日本語") == 1); - assert(count!("a == '本'")("日本語"w) == 1); - assert(count!("a == '語'")("日本語"d) == 1); -} - -@safe unittest -{ - string s = "This is a fofofof list"; - string sub = "fof"; - assert(count(s, sub) == 2); -} - -/// Ditto -size_t count(alias pred = "a == b", R1, R2)(R1 haystack, R2 needle) -if (isForwardRange!R1 && !isInfinite!R1 && - isForwardRange!R2 && - is(typeof(binaryFun!pred(haystack.front, needle.front)))) -{ - assert(!needle.empty, "Cannot count occurrences of an empty range"); - - static if (isInfinite!R2) - { - //Note: This is the special case of looking for an infinite inside a finite... - //"How many instances of the Fibonacci sequence can you count in [1, 2, 3]?" - "None." - return 0; - } - else - { - size_t result; - //Note: haystack is not saved, because findskip is designed to modify it - for ( ; findSkip!pred(haystack, needle.save) ; ++result) - {} - return result; - } -} - -/** -Counts all elements or elements satisfying a predicate in `haystack`. - -The first overload counts each element `e` in `haystack` for which `pred(e)` is $(D -true). Performs $(BIGOH haystack.length) evaluations of `pred`. - -The second overload counts the number of elements in a range. -If the given range has the `length` property, -that is returned right away, otherwise -performs $(BIGOH haystack.length) to walk the range. - -Params: - pred = Optional predicate to find elements. - haystack = The range to _count. - -Returns: - The number of elements in `haystack` (for which `pred` returned true). -*/ -size_t count(alias pred, R)(R haystack) -if (isInputRange!R && !isInfinite!R && - is(typeof(unaryFun!pred(haystack.front)))) -{ - size_t result; - alias T = ElementType!R; //For narrow strings forces dchar iteration - foreach (T elem; haystack) - if (unaryFun!pred(elem)) ++result; - return result; -} - -/// -@safe unittest -{ - // count elements in range - int[] a = [ 1, 2, 4, 3, 2, 5, 3, 2, 4 ]; - assert(count(a) == 9); - // count predicate in range - assert(count!("a > 2")(a) == 5); -} - -/// Ditto -size_t count(R)(R haystack) -if (isInputRange!R && !isInfinite!R) -{ - return walkLength(haystack); -} - -@safe unittest -{ - int[] a = [ 1, 2, 4, 3, 2, 5, 3, 2, 4 ]; - assert(count!("a == 3")(a) == 2); - assert(count("日本語") == 3); -} - -// https://issues.dlang.org/show_bug.cgi?id=11253 -@safe nothrow unittest -{ - assert([1, 2, 3].count([2, 3]) == 1); -} - -// https://issues.dlang.org/show_bug.cgi?id=22582 -@safe unittest -{ - assert([1, 2, 3].count!"a & 1" == 2); -} - -/++ - Counts elements in the given - $(REF_ALTTEXT forward range, isForwardRange, std,range,primitives) - until the given predicate is true for one of the given `needles`. - - Params: - pred = The predicate for determining when to stop counting. - haystack = The - $(REF_ALTTEXT input range, isInputRange, std,range,primitives) to be - counted. - needles = Either a single element, or a - $(REF_ALTTEXT forward range, isForwardRange, std,range,primitives) - of elements, to be evaluated in turn against each - element in `haystack` under the given predicate. - - Returns: The number of elements which must be popped from the front of - `haystack` before reaching an element for which - `startsWith!pred(haystack, needles)` is `true`. If - `startsWith!pred(haystack, needles)` is not `true` for any element in - `haystack`, then `-1` is returned. If only `pred` is provided, - `pred(haystack)` is tested for each element. - - See_Also: $(REF indexOf, std,string) - +/ -ptrdiff_t countUntil(alias pred = "a == b", R, Rs...)(R haystack, Rs needles) -if (isForwardRange!R - && Rs.length > 0 - && isForwardRange!(Rs[0]) == isInputRange!(Rs[0]) - && allSatisfy!(canTestStartsWith!(pred, R), Rs)) -{ - typeof(return) result; - - static if (needles.length == 1) - { - static if (hasLength!R) //Note: Narrow strings don't have length. - { - //We delegate to find because find is very efficient. - //We store the length of the haystack so we don't have to save it. - auto len = haystack.length; - auto r2 = find!pred(haystack, needles[0]); - if (!r2.empty) - return cast(typeof(return)) (len - r2.length); - } - else - { - import std.range : dropOne; - - if (needles[0].empty) - return 0; - - //Default case, slower route doing startsWith iteration - for ( ; !haystack.empty ; ++result ) - { - //We compare the first elements of the ranges here before - //forwarding to startsWith. This avoids making useless saves to - //haystack/needle if they aren't even going to be mutated anyways. - //It also cuts down on the amount of pops on haystack. - if (binaryFun!pred(haystack.front, needles[0].front)) - { - //Here, we need to save the needle before popping it. - //haystack we pop in all paths, so we do that, and then save. - haystack.popFront(); - if (startsWith!pred(haystack.save, needles[0].save.dropOne())) - return result; - } - else - haystack.popFront(); - } - } - } - else - { - foreach (i, Ri; Rs) - { - static if (isForwardRange!Ri) - { - if (needles[i].empty) - return 0; - } - } - Tuple!Rs t; - foreach (i, Ri; Rs) - { - static if (!isForwardRange!Ri) - { - t[i] = needles[i]; - } - } - for (; !haystack.empty ; ++result, haystack.popFront()) - { - foreach (i, Ri; Rs) - { - static if (isForwardRange!Ri) - { - t[i] = needles[i].save; - } - } - if (startsWith!pred(haystack.save, t.expand)) - { - return result; - } - } - } - - // Because of https://issues.dlang.org/show_bug.cgi?id=8804 - // Avoids both "unreachable code" or "no return statement" - static if (isInfinite!R) assert(false, R.stringof ~ " must not be an" - ~ " infinite range"); - else return -1; -} - -/// ditto -ptrdiff_t countUntil(alias pred = "a == b", R, N)(R haystack, N needle) -if (isInputRange!R && - is(typeof(binaryFun!pred(haystack.front, needle)) : bool)) -{ - bool pred2(ElementType!R a) { return binaryFun!pred(a, needle); } - return countUntil!pred2(haystack); -} - -/// -@safe unittest -{ - assert(countUntil("hello world", "world") == 6); - assert(countUntil("hello world", 'r') == 8); - assert(countUntil("hello world", "programming") == -1); - assert(countUntil("日本語", "本語") == 1); - assert(countUntil("日本語", '語') == 2); - assert(countUntil("日本語", "五") == -1); - assert(countUntil("日本語", '五') == -1); - assert(countUntil([0, 7, 12, 22, 9], [12, 22]) == 2); - assert(countUntil([0, 7, 12, 22, 9], 9) == 4); - assert(countUntil!"a > b"([0, 7, 12, 22, 9], 20) == 3); -} - -@safe unittest -{ - import std.algorithm.iteration : filter; - import std.internal.test.dummyrange; - - assert(countUntil("日本語", "") == 0); - assert(countUntil("日本語"d, "") == 0); - - assert(countUntil("", "") == 0); - assert(countUntil("".filter!"true"(), "") == 0); - - auto rf = [0, 20, 12, 22, 9].filter!"true"(); - assert(rf.countUntil!"a > b"((int[]).init) == 0); - assert(rf.countUntil!"a > b"(20) == 3); - assert(rf.countUntil!"a > b"([20, 8]) == 3); - assert(rf.countUntil!"a > b"([20, 10]) == -1); - assert(rf.countUntil!"a > b"([20, 8, 0]) == -1); - - auto r = new ReferenceForwardRange!int([0, 1, 2, 3, 4, 5, 6]); - auto r2 = new ReferenceForwardRange!int([3, 4]); - auto r3 = new ReferenceForwardRange!int([3, 5]); - assert(r.save.countUntil(3) == 3); - assert(r.save.countUntil(r2) == 3); - assert(r.save.countUntil(7) == -1); - assert(r.save.countUntil(r3) == -1); -} - -@safe unittest -{ - assert(countUntil("hello world", "world", "asd") == 6); - assert(countUntil("hello world", "world", "ello") == 1); - assert(countUntil("hello world", "world", "") == 0); - assert(countUntil("hello world", "world", 'l') == 2); -} - -/// ditto -ptrdiff_t countUntil(alias pred, R)(R haystack) -if (isInputRange!R && - is(typeof(unaryFun!pred(haystack.front)) : bool)) -{ - typeof(return) i; - static if (isRandomAccessRange!R) - { - //Optimized RA implementation. Since we want to count *and* iterate at - //the same time, it is more efficient this way. - static if (hasLength!R) - { - immutable len = cast(typeof(return)) haystack.length; - for ( ; i < len ; ++i ) - if (unaryFun!pred(haystack[i])) return i; - } - else //if (isInfinite!R) - { - for ( ; ; ++i ) - if (unaryFun!pred(haystack[i])) return i; - } - } - else static if (hasLength!R) - { - //For those odd ranges that have a length, but aren't RA. - //It is faster to quick find, and then compare the lengths - auto r2 = find!pred(haystack.save); - if (!r2.empty) return cast(typeof(return)) (haystack.length - r2.length); - } - else //Everything else - { - alias T = ElementType!R; //For narrow strings forces dchar iteration - foreach (T elem; haystack) - { - if (unaryFun!pred(elem)) return i; - ++i; - } - } - - // Because of https://issues.dlang.org/show_bug.cgi?id=8804 - // Avoids both "unreachable code" or "no return statement" - static if (isInfinite!R) assert(false, R.stringof ~ " must not be an" - ~ " inifite range"); - else return -1; -} - -/// -@safe unittest -{ - import std.ascii : isDigit; - import std.uni : isWhite; - - assert(countUntil!(isWhite)("hello world") == 5); - assert(countUntil!(isDigit)("hello world") == -1); - assert(countUntil!"a > 20"([0, 7, 12, 22, 9]) == 3); -} - -@safe unittest -{ - import std.internal.test.dummyrange; - - // References - { - // input - ReferenceInputRange!int r; - r = new ReferenceInputRange!int([0, 1, 2, 3, 4, 5, 6]); - assert(r.countUntil(3) == 3); - r = new ReferenceInputRange!int([0, 1, 2, 3, 4, 5, 6]); - assert(r.countUntil(7) == -1); - } - { - // forward - auto r = new ReferenceForwardRange!int([0, 1, 2, 3, 4, 5, 6]); - assert(r.save.countUntil([3, 4]) == 3); - assert(r.save.countUntil(3) == 3); - assert(r.save.countUntil([3, 7]) == -1); - assert(r.save.countUntil(7) == -1); - } - { - // infinite forward - auto r = new ReferenceInfiniteForwardRange!int(0); - assert(r.save.countUntil([3, 4]) == 3); - assert(r.save.countUntil(3) == 3); - } -} - -/** -Checks if the given range ends with (one of) the given needle(s). -The reciprocal of `startsWith`. - -Params: - pred = The predicate to use for comparing elements between the range and - the needle(s). - - doesThisEnd = The - $(REF_ALTTEXT bidirectional range, isBidirectionalRange, std,range,primitives) - to check. - - withOneOfThese = The needles to check against, which may be single - elements, or bidirectional ranges of elements. - - withThis = The single element to check. - -Returns: -0 if the needle(s) do not occur at the end of the given range; -otherwise the position of the matching needle, that is, 1 if the range ends -with `withOneOfThese[0]`, 2 if it ends with `withOneOfThese[1]`, and so -on. - -In the case when no needle parameters are given, return `true` iff back of -`doesThisStart` fulfils predicate `pred`. -*/ -uint endsWith(alias pred = "a == b", Range, Needles...)(Range doesThisEnd, Needles withOneOfThese) -if (isBidirectionalRange!Range && Needles.length > 1 && - allSatisfy!(canTestStartsWith!(pred, Range), Needles)) -{ - alias haystack = doesThisEnd; - alias needles = withOneOfThese; - - // Make one pass looking for empty ranges in needles - foreach (i, Unused; Needles) - { - // Empty range matches everything - static if (!is(typeof(binaryFun!pred(haystack.back, needles[i])) : bool)) - { - if (needles[i].empty) return i + 1; - } - } - - for (; !haystack.empty; haystack.popBack()) - { - foreach (i, Unused; Needles) - { - static if (is(typeof(binaryFun!pred(haystack.back, needles[i])) : bool)) - { - // Single-element - if (binaryFun!pred(haystack.back, needles[i])) - { - // found, but continue to account for one-element - // range matches (consider endsWith("ab", "b", - // 'b') should return 1, not 2). - continue; - } - } - else - { - if (binaryFun!pred(haystack.back, needles[i].back)) - continue; - } - - // This code executed on failure to match - // Out with this guy, check for the others - uint result = endsWith!pred(haystack, needles[0 .. i], needles[i + 1 .. $]); - if (result > i) ++result; - return result; - } - - // If execution reaches this point, then the back matches for all - // needles ranges. What we need to do now is to lop off the back of - // all ranges involved and recurse. - foreach (i, Unused; Needles) - { - static if (is(typeof(binaryFun!pred(haystack.back, needles[i])) : bool)) - { - // Test has passed in the previous loop - return i + 1; - } - else - { - needles[i].popBack(); - if (needles[i].empty) return i + 1; - } - } - } - return 0; -} - -/// Ditto -bool endsWith(alias pred = "a == b", R1, R2)(R1 doesThisEnd, R2 withThis) -if (isBidirectionalRange!R1 && - isBidirectionalRange!R2 && - is(typeof(binaryFun!pred(doesThisEnd.back, withThis.back)) : bool)) -{ - alias haystack = doesThisEnd; - alias needle = withThis; - - static if (is(typeof(pred) : string)) - enum isDefaultPred = pred == "a == b"; - else - enum isDefaultPred = false; - - static if (isDefaultPred && isArray!R1 && isArray!R2 && - is(immutable ElementEncodingType!R1 == immutable ElementEncodingType!R2)) - { - if (haystack.length < needle.length) return false; - - return haystack[$ - needle.length .. $] == needle; - } - else - { - import std.range : retro; - return startsWith!pred(retro(doesThisEnd), retro(withThis)); - } -} - -/// Ditto -bool endsWith(alias pred = "a == b", R, E)(R doesThisEnd, E withThis) -if (isBidirectionalRange!R && - is(typeof(binaryFun!pred(doesThisEnd.back, withThis)) : bool)) -{ - if (doesThisEnd.empty) - return false; - - static if (is(typeof(pred) : string)) - enum isDefaultPred = pred == "a == b"; - else - enum isDefaultPred = false; - - alias predFunc = binaryFun!pred; - - // auto-decoding special case - static if (isNarrowString!R) - { - // statically determine decoding is unnecessary to evaluate pred - static if (isDefaultPred && isSomeChar!E && E.sizeof <= ElementEncodingType!R.sizeof) - return doesThisEnd[$ - 1] == withThis; - // specialize for ASCII as to not change previous behavior - else - { - if (withThis <= 0x7F) - return predFunc(doesThisEnd[$ - 1], withThis); - else - return predFunc(doesThisEnd.back, withThis); - } - } - else - { - return predFunc(doesThisEnd.back, withThis); - } -} - -/// Ditto -bool endsWith(alias pred, R)(R doesThisEnd) -if (isInputRange!R && - ifTestable!(typeof(doesThisEnd.front), unaryFun!pred)) -{ - return !doesThisEnd.empty && unaryFun!pred(doesThisEnd.back); -} - -/// -@safe unittest -{ - import std.ascii : isAlpha; - assert("abc".endsWith!(a => a.isAlpha)); - assert("abc".endsWith!isAlpha); - - assert(!"ab1".endsWith!(a => a.isAlpha)); - - assert(!"ab1".endsWith!isAlpha); - assert(!"".endsWith!(a => a.isAlpha)); - - import std.algorithm.comparison : among; - assert("abc".endsWith!(a => a.among('c', 'd') != 0)); - assert(!"abc".endsWith!(a => a.among('a', 'b') != 0)); - - assert(endsWith("abc", "")); - assert(!endsWith("abc", "b")); - assert(endsWith("abc", "a", 'c') == 2); - assert(endsWith("abc", "c", "a") == 1); - assert(endsWith("abc", "c", "c") == 1); - assert(endsWith("abc", "bc", "c") == 2); - assert(endsWith("abc", "x", "c", "b") == 2); - assert(endsWith("abc", "x", "aa", "bc") == 3); - assert(endsWith("abc", "x", "aaa", "sab") == 0); - assert(endsWith("abc", "x", "aaa", 'c', "sab") == 3); -} - -@safe unittest -{ - import std.algorithm.iteration : filterBidirectional; - import std.conv : to; - import std.meta : AliasSeq; - - static foreach (S; AliasSeq!(char[], wchar[], dchar[], string, wstring, dstring)) - (){ // workaround slow optimizations for large functions - // https://issues.dlang.org/show_bug.cgi?id=2396 - assert(!endsWith(to!S("abc"), 'a')); - assert(endsWith(to!S("abc"), 'a', 'c') == 2); - assert(!endsWith(to!S("abc"), 'x', 'n', 'b')); - assert(endsWith(to!S("abc"), 'x', 'n', 'c') == 3); - assert(endsWith(to!S("abc\uFF28"), 'a', '\uFF28', 'c') == 2); - - static foreach (T; AliasSeq!(char[], wchar[], dchar[], string, wstring, dstring)) - { - //Lots of strings - assert(endsWith(to!S("abc"), to!T(""))); - assert(!endsWith(to!S("abc"), to!T("a"))); - assert(!endsWith(to!S("abc"), to!T("b"))); - assert(endsWith(to!S("abc"), to!T("bc"), 'c') == 2); - assert(endsWith(to!S("abc"), to!T("a"), "c") == 2); - assert(endsWith(to!S("abc"), to!T("c"), "a") == 1); - assert(endsWith(to!S("abc"), to!T("c"), "c") == 1); - assert(endsWith(to!S("abc"), to!T("x"), 'c', "b") == 2); - assert(endsWith(to!S("abc"), 'x', to!T("aa"), "bc") == 3); - assert(endsWith(to!S("abc"), to!T("x"), "aaa", "sab") == 0); - assert(endsWith(to!S("abc"), to!T("x"), "aaa", "c", "sab") == 3); - assert(endsWith(to!S("\uFF28el\uFF4co"), to!T("l\uFF4co"))); - assert(endsWith(to!S("\uFF28el\uFF4co"), to!T("lo"), to!T("l\uFF4co")) == 2); - - //Unicode - assert(endsWith(to!S("\uFF28el\uFF4co"), to!T("l\uFF4co"))); - assert(endsWith(to!S("\uFF28el\uFF4co"), to!T("lo"), to!T("l\uFF4co")) == 2); - assert(endsWith(to!S("日本語"), to!T("本語"))); - assert(endsWith(to!S("日本語"), to!T("日本語"))); - assert(!endsWith(to!S("本語"), to!T("日本語"))); - - //Empty - assert(endsWith(to!S(""), T.init)); - assert(!endsWith(to!S(""), 'a')); - assert(endsWith(to!S("a"), T.init)); - assert(endsWith(to!S("a"), T.init, "") == 1); - assert(endsWith(to!S("a"), T.init, 'a') == 1); - assert(endsWith(to!S("a"), 'a', T.init) == 2); - } - }(); - - static foreach (T; AliasSeq!(int, short)) - {{ - immutable arr = cast(T[])[0, 1, 2, 3, 4, 5]; - - //RA range - assert(endsWith(arr, cast(int[]) null)); - assert(!endsWith(arr, 0)); - assert(!endsWith(arr, 4)); - assert(endsWith(arr, 5)); - assert(endsWith(arr, 0, 4, 5) == 3); - assert(endsWith(arr, [5])); - assert(endsWith(arr, [4, 5])); - assert(endsWith(arr, [4, 5], 7) == 1); - assert(!endsWith(arr, [2, 4, 5])); - assert(endsWith(arr, [2, 4, 5], [3, 4, 5]) == 2); - - //Normal input range - assert(!endsWith(filterBidirectional!"true"(arr), 4)); - assert(endsWith(filterBidirectional!"true"(arr), 5)); - assert(endsWith(filterBidirectional!"true"(arr), [5])); - assert(endsWith(filterBidirectional!"true"(arr), [4, 5])); - assert(endsWith(filterBidirectional!"true"(arr), [4, 5], 7) == 1); - assert(!endsWith(filterBidirectional!"true"(arr), [2, 4, 5])); - assert(endsWith(filterBidirectional!"true"(arr), [2, 4, 5], [3, 4, 5]) == 2); - assert(endsWith(arr, filterBidirectional!"true"([4, 5]))); - assert(endsWith(arr, filterBidirectional!"true"([4, 5]), 7) == 1); - assert(!endsWith(arr, filterBidirectional!"true"([2, 4, 5]))); - assert(endsWith(arr, [2, 4, 5], filterBidirectional!"true"([3, 4, 5])) == 2); - - //Non-default pred - assert(endsWith!("a%10 == b%10")(arr, [14, 15])); - assert(!endsWith!("a%10 == b%10")(arr, [15, 14])); - }} -} - -@safe pure unittest -{ - //example from https://issues.dlang.org/show_bug.cgi?id=19727 - import std.path : asRelativePath; - string[] ext = ["abc", "def", "ghi"]; - string path = "/foo/file.def"; - assert(ext.any!(e => path.asRelativePath("/foo").endsWith(e)) == true); - assert(ext.any!(e => path.asRelativePath("/foo").startsWith(e)) == false); -} - -private enum bool hasConstEmptyMember(T) = is(typeof(((const T* a) => (*a).empty)(null)) : bool); - -/** -Iterates the passed range and selects the extreme element with `less`. -If the extreme element occurs multiple time, the first occurrence will be -returned. - -Params: - map = custom accessor for the comparison key - selector = custom mapping for the extrema selection - r = Range from which the extreme value will be selected - seedElement = custom seed to use as initial element - -Returns: - The extreme value according to `map` and `selector` of the passed-in values. -*/ -private auto extremum(alias map, alias selector = "a < b", Range)(Range r) -if (isInputRange!Range && !isInfinite!Range && - is(typeof(unaryFun!map(ElementType!(Range).init)))) -in -{ - assert(!r.empty, "r is an empty range"); -} -do -{ - import std.typecons : Rebindable2; - - alias Element = ElementType!Range; - auto seed = Rebindable2!Element(r.front); - r.popFront(); - return extremum!(map, selector)(r, seed.get); -} - -private auto extremum(alias map, alias selector = "a < b", Range, - RangeElementType = ElementType!Range) - (Range r, RangeElementType seedElement) -if (isInputRange!Range && !isInfinite!Range && - !is(CommonType!(ElementType!Range, RangeElementType) == void) && - is(typeof(unaryFun!map(ElementType!(Range).init)))) -{ - import std.typecons : Rebindable2; - - alias mapFun = unaryFun!map; - alias selectorFun = binaryFun!selector; - - alias Element = ElementType!Range; - alias CommonElement = CommonType!(Element, RangeElementType); - auto extremeElement = Rebindable2!CommonElement(seedElement); - - // if we only have one statement in the loop, it can be optimized a lot better - static if (__traits(isSame, map, a => a)) - { - // direct access via a random access range is faster - static if (isRandomAccessRange!Range) - { - foreach (const i; 0 .. r.length) - { - if (selectorFun(r[i], extremeElement.get)) - { - extremeElement = r[i]; - } - } - } - else - { - while (!r.empty) - { - if (selectorFun(r.front, extremeElement.get)) - { - extremeElement = r.front; - } - r.popFront(); - } - } - } - else - { - alias MapType = Unqual!(typeof(mapFun(CommonElement.init))); - MapType extremeElementMapped = mapFun(extremeElement.get); - - // direct access via a random access range is faster - static if (isRandomAccessRange!Range) - { - foreach (const i; 0 .. r.length) - { - MapType mapElement = mapFun(r[i]); - if (selectorFun(mapElement, extremeElementMapped)) - { - extremeElement = r[i]; - extremeElementMapped = mapElement; - } - } - } - else - { - while (!r.empty) - { - MapType mapElement = mapFun(r.front); - if (selectorFun(mapElement, extremeElementMapped)) - { - extremeElement = r.front; - extremeElementMapped = mapElement; - } - r.popFront(); - } - } - } - return extremeElement.get; -} - -private auto extremum(alias selector = "a < b", Range)(Range r) -if (isInputRange!Range && !isInfinite!Range && - !is(typeof(unaryFun!selector(ElementType!(Range).init)))) -{ - return extremum!(a => a, selector)(r); -} - -// if we only have one statement in the loop it can be optimized a lot better -private auto extremum(alias selector = "a < b", Range, - RangeElementType = ElementType!Range) - (Range r, RangeElementType seedElement) -if (isInputRange!Range && !isInfinite!Range && - !is(CommonType!(ElementType!Range, RangeElementType) == void) && - !is(typeof(unaryFun!selector(ElementType!(Range).init)))) -{ - return extremum!(a => a, selector)(r, seedElement); -} - -@safe pure unittest -{ - // allows a custom map to select the extremum - assert([[0, 4], [1, 2]].extremum!"a[0]" == [0, 4]); - assert([[0, 4], [1, 2]].extremum!"a[1]" == [1, 2]); - - // allows a custom selector for comparison - assert([[0, 4], [1, 2]].extremum!("a[0]", "a > b") == [1, 2]); - assert([[0, 4], [1, 2]].extremum!("a[1]", "a > b") == [0, 4]); - - // use a custom comparator - import std.math.operations : cmp; - assert([-2., 0, 5].extremum!cmp == 5.0); - assert([-2., 0, 2].extremum!`cmp(a, b) < 0` == -2.0); - - // combine with map - import std.range : enumerate; - assert([-3., 0, 5].enumerate.extremum!(`a.value`, cmp) == tuple(2, 5.0)); - assert([-2., 0, 2].enumerate.extremum!(`a.value`, `cmp(a, b) < 0`) == tuple(0, -2.0)); - - // seed with a custom value - int[] arr; - assert(arr.extremum(1) == 1); -} - -@safe pure nothrow unittest -{ - // 2d seeds - int[][] arr2d; - assert(arr2d.extremum([1]) == [1]); - - // allow seeds of different types (implicit casting) - assert(extremum([2, 3, 4], 1.5) == 1.5); -} - -@safe pure unittest -{ - import std.range : enumerate, iota; - - // forward ranges - assert(iota(1, 5).extremum() == 1); - assert(iota(2, 5).enumerate.extremum!"a.value" == tuple(0, 2)); - - // should work with const - const(int)[] immArr = [2, 1, 3]; - assert(immArr.extremum == 1); - - // should work with immutable - immutable(int)[] immArr2 = [2, 1, 3]; - assert(immArr2.extremum == 1); - - // with strings - assert(["b", "a", "c"].extremum == "a"); - - // with all dummy ranges - import std.internal.test.dummyrange; - foreach (DummyType; AllDummyRanges) - { - DummyType d; - assert(d.extremum == 1); - assert(d.extremum!(a => a) == 1); - assert(d.extremum!`a > b` == 10); - assert(d.extremum!(a => a, `a > b`) == 10); - } - - // compiletime - enum ctExtremum = iota(1, 5).extremum; - assert(ctExtremum == 1); -} - -@nogc @safe nothrow pure unittest -{ - static immutable arr = [7, 3, 4, 2, 1, 8]; - assert(arr.extremum == 1); - - static immutable arr2d = [[1, 9], [3, 1], [4, 2]]; - assert(arr2d.extremum!"a[1]" == arr2d[1]); -} - -// https://issues.dlang.org/show_bug.cgi?id=17982 -@safe unittest -{ - class B - { - int val; - this(int val){ this.val = val; } - } - - const(B) doStuff(const(B)[] v) - { - return v.extremum!"a.val"; - } - assert(doStuff([new B(1), new B(0), new B(2)]).val == 0); - - const(B)[] arr = [new B(0), new B(1)]; - // can't compare directly - https://issues.dlang.org/show_bug.cgi?id=1824 - assert(arr.extremum!"a.val".val == 0); -} - -// https://issues.dlang.org/show_bug.cgi?id=22786 -@nogc @safe nothrow pure unittest -{ - struct S - { - immutable int value; - } - - assert([S(5), S(6)].extremum!"a.value" == S(5)); -} - -// https://issues.dlang.org/show_bug.cgi?id=24027 -@safe nothrow unittest -{ - class A - { - int a; - this(int a) - { - this.a = a; - } - } - - auto test = new A(5); - A[] arr = [test]; - assert(maxElement!"a.a"(arr) is test); -} - -// find -/** -Finds an element `e` of an $(REF_ALTTEXT input range, isInputRange, std,range,primitives) -where `pred(e)` is `true`. -$(P -$(PANEL -$(UL -$(LI `find` behaves similarly to `dropWhile` in other languages.) -$(LI To _find the *last* matching element in a -$(REF_ALTTEXT bidirectional, isBidirectionalRange, std,range,primitives) `haystack`, -call `find!pred(retro(haystack))`. See $(REF retro, std,range).) -))) - -Complexity: - `find` performs $(BIGOH walkLength(haystack)) evaluations of `pred`. - -Params: - - pred = The predicate to match an element. - haystack = The $(REF_ALTTEXT input range, isInputRange, std,range,primitives) - searched in. - -Returns: - `haystack` advanced such that the front element satisfies `pred`. - If no such element exists, returns an empty `haystack`. -*/ -InputRange find(alias pred, InputRange)(InputRange haystack) -if (isInputRange!InputRange) -{ - alias R = InputRange; - alias predFun = unaryFun!pred; - static if (isNarrowString!R) - { - import std.utf : decode; - - immutable len = haystack.length; - size_t i = 0, next = 0; - while (next < len) - { - if (predFun(decode(haystack, next))) - return haystack[i .. $]; - i = next; - } - return haystack[$ .. $]; - } - else - { - //standard range - for ( ; !haystack.empty; haystack.popFront() ) - { - if (predFun(haystack.front)) - break; - } - return haystack; - } -} - -/// -@safe unittest -{ - auto arr = [ 1, 2, 3, 4, 1 ]; - assert(find!("a > 2")(arr) == [ 3, 4, 1 ]); - - // with predicate alias - bool pred(int e) => e + 1 > 1.5; - assert(find!(pred)(arr) == arr); -} - -@safe pure unittest -{ - int[] r = [ 1, 2, 3 ]; - assert(find!(a=>a > 2)(r) == [3]); - bool pred(int x) { return x + 1 > 1.5; } - assert(find!(pred)(r) == r); - - assert(find!(a=>a > 'v')("hello world") == "world"); - assert(find!(a=>a%4 == 0)("日本語") == "本語"); -} - -/** -Finds an individual element in an $(REF_ALTTEXT input range, isInputRange, std,range,primitives). -Elements of `haystack` are compared with `needle` by using predicate -`pred` with `pred(haystack.front, needle)`. -The predicate is passed to $(REF binaryFun, std, functional), and can either accept a -string, or any callable that can be executed via `pred(element, element)`. - -If `haystack` is a $(REF_ALTTEXT forward range, isForwardRange, std,range,primitives), -`needle` can be a $(REF_ALTTEXT forward range, isForwardRange, std,range,primitives) too. -In this case `startsWith!pred(haystack, needle)` is evaluated on each evaluation. - -$(NOTE To find the first element $(I not) matching the needle, use predicate `"a != b"`.) - -Complexity: - `find` performs $(BIGOH walkLength(haystack)) evaluations of `pred`. - There are specializations that improve performance by taking - advantage of $(REF_ALTTEXT bidirectional, isBidirectionalRange, std,range,primitives) - or $(REF_ALTTEXT random access, isRandomAccessRange, std,range,primitives) - ranges (where possible). - -Params: - - pred = The predicate for comparing each element with the needle, defaulting to equality `"a == b"`. - haystack = The $(REF_ALTTEXT input range, isInputRange, std,range,primitives) - searched in. - needle = The element searched for. - -Returns: - `haystack` advanced such that the front element is the one searched for; - that is, until `binaryFun!pred(haystack.front, needle)` is `true`. If no - such position exists, returns an empty `haystack`. - -See_Also: $(LREF findAdjacent), $(LREF findAmong), $(LREF findSkip), $(LREF findSplit), $(LREF startsWith) -*/ -InputRange find(alias pred = "a == b", InputRange, Element)(InputRange haystack, scope Element needle) -if (isInputRange!InputRange && - is (typeof(binaryFun!pred(haystack.front, needle)) : bool) && - !is (typeof(binaryFun!pred(haystack.front, needle.front)) : bool)) -{ - alias R = InputRange; - alias E = Element; - alias predFun = binaryFun!pred; - static if (is(typeof(pred == "a == b"))) - enum isDefaultPred = pred == "a == b"; - else - enum isDefaultPred = false; - enum isIntegralNeedle = isSomeChar!E || isIntegral!E || isBoolean!E; - - alias EType = ElementType!R; - - // If the haystack is a SortedRange we can use binary search to find the needle. - // Works only for the default find predicate and any SortedRange predicate. - // https://issues.dlang.org/show_bug.cgi?id=8829 - import std.range : SortedRange; - static if (is(InputRange : SortedRange!TT, TT) && isDefaultPred) - { - auto lb = haystack.lowerBound(needle); - if (lb.length == haystack.length || haystack[lb.length] != needle) - return haystack[$ .. $]; - - return haystack[lb.length .. $]; - } - else static if (isNarrowString!R) - { - alias EEType = ElementEncodingType!R; - alias UEEType = Unqual!EEType; - - //These are two special cases which can search without decoding the UTF stream. - static if (isDefaultPred && isIntegralNeedle) - { - import std.utf : canSearchInCodeUnits; - - //This special case deals with UTF8 search, when the needle - //is represented by a single code point. - //Note: "needle <= 0x7F" properly handles sign via unsigned promotion - static if (is(UEEType == char)) - { - if (!__ctfe && canSearchInCodeUnits!char(needle)) - { - static inout(R) trustedMemchr(ref return scope inout(R) haystack, - ref const scope E needle) @trusted nothrow pure - { - import core.stdc.string : memchr; - auto ptr = memchr(haystack.ptr, needle, haystack.length); - return ptr ? - haystack[cast(char*) ptr - haystack.ptr .. $] : - haystack[$ .. $]; - } - return trustedMemchr(haystack, needle); - } - } - - //Ditto, but for UTF16 - static if (is(UEEType == wchar)) - { - if (canSearchInCodeUnits!wchar(needle)) - { - foreach (i, ref EEType e; haystack) - { - if (e == needle) - return haystack[i .. $]; - } - return haystack[$ .. $]; - } - } - } - - //Previous conditional optimizations did not succeed. Fallback to - //unconditional implementations - static if (isDefaultPred) - { - import std.utf : encode; - - //In case of default pred, it is faster to do string/string search. - UEEType[is(UEEType == char) ? 4 : 2] buf; - - size_t len = encode(buf, needle); - return find(haystack, buf[0 .. len]); - } - else - { - import std.utf : decode; - - //Explicit pred: we must test each character by the book. - //We choose a manual decoding approach, because it is faster than - //the built-in foreach, or doing a front/popFront for-loop. - immutable len = haystack.length; - size_t i = 0, next = 0; - while (next < len) - { - if (predFun(decode(haystack, next), needle)) - return haystack[i .. $]; - i = next; - } - return haystack[$ .. $]; - } - } - else static if (isArray!R) - { - // https://issues.dlang.org/show_bug.cgi?id=10403 optimization - static if (isDefaultPred && isIntegral!EType && EType.sizeof == 1 && isIntegralNeedle) - { - import std.algorithm.comparison : max, min; - - R findHelper(return scope ref R haystack, ref E needle) @trusted nothrow pure - { - import core.stdc.string : memchr; - - EType* ptr = null; - //Note: we use "min/max" to handle sign mismatch. - if (min(EType.min, needle) == EType.min && - max(EType.max, needle) == EType.max) - { - ptr = cast(EType*) memchr(haystack.ptr, needle, - haystack.length); - } - - return ptr ? - haystack[ptr - haystack.ptr .. $] : - haystack[$ .. $]; - } - - if (!__ctfe) - return findHelper(haystack, needle); - } - - //Default implementation. - foreach (i, ref e; haystack) - if (predFun(e, needle)) - return haystack[i .. $]; - return haystack[$ .. $]; - } - else - { - //Everything else. Walk. - for ( ; !haystack.empty; haystack.popFront() ) - { - if (predFun(haystack.front, needle)) - break; - } - return haystack; - } -} - -/// -@safe unittest -{ - import std.range.primitives; - - auto arr = [1, 2, 4, 4, 4, 4, 5, 6, 9]; - assert(arr.find(4) == [4, 4, 4, 4, 5, 6, 9]); - assert(arr.find(1) == arr); - assert(arr.find(9) == [9]); - assert(arr.find!((e, n) => e > n)(4) == [5, 6, 9]); - assert(arr.find!((e, n) => e < n)(4) == arr); - assert(arr.find(0).empty); - assert(arr.find(10).empty); - assert(arr.find(8).empty); - - assert(find("hello, world", ',') == ", world"); -} - -/// Case-insensitive find of a string -@safe unittest -{ - import std.range.primitives; - import std.uni : toLower; - - string[] s = ["Hello", "world", "!"]; - assert(s.find!((e, n) => toLower(e) == n)("hello") == s); -} - -@safe unittest -{ - import std.algorithm.comparison : equal; - import std.container : SList; - - auto lst = SList!int(1, 2, 5, 7, 3); - assert(lst.front == 1); - auto r = find(lst[], 5); - assert(equal(r, SList!int(5, 7, 3)[])); - assert(find([1, 2, 3, 5], 4).empty); - assert(equal(find!"a > b"("hello", 'k'), "llo")); -} - -@safe pure nothrow unittest -{ - assert(!find ([1, 2, 3], 2).empty); - assert(!find!((a,b)=>a == b)([1, 2, 3], 2).empty); - assert(!find ([1, 2, 3], 2).empty); - assert(!find!((a,b)=>a == b)([1, 2, 3], 2).empty); -} - -@safe pure unittest -{ - import std.meta : AliasSeq; - static foreach (R; AliasSeq!(string, wstring, dstring)) - { - static foreach (E; AliasSeq!(char, wchar, dchar)) - { - assert(find ("hello world", 'w') == "world"); - assert(find!((a,b)=>a == b)("hello world", 'w') == "world"); - assert(find ("日c語", 'c') == "c語"); - assert(find!((a,b)=>a == b)("日c語", 'c') == "c語"); - assert(find ("0123456789", 'A').empty); - static if (E.sizeof >= 2) - { - assert(find ("日本語", '本') == "本語"); - assert(find!((a,b)=>a == b)("日本語", '本') == "本語"); - } - } - } -} - -@safe unittest -{ - //CTFE - static assert(find("abc", 'b') == "bc"); - static assert(find("日b語", 'b') == "b語"); - static assert(find("日本語", '本') == "本語"); - static assert(find([1, 2, 3], 2) == [2, 3]); - - static assert(find ([1, 2, 3], 2)); - static assert(find!((a,b)=>a == b)([1, 2, 3], 2)); - static assert(find ([1, 2, 3], 2)); - static assert(find!((a,b)=>a == b)([1, 2, 3], 2)); -} - -@safe unittest -{ - import std.exception : assertCTFEable; - import std.meta : AliasSeq; - - void dg() @safe pure nothrow - { - byte[] sarr = [1, 2, 3, 4]; - ubyte[] uarr = [1, 2, 3, 4]; - static foreach (arr; AliasSeq!(sarr, uarr)) - { - static foreach (T; AliasSeq!(byte, ubyte, int, uint)) - { - assert(find(arr, cast(T) 3) == arr[2 .. $]); - assert(find(arr, cast(T) 9) == arr[$ .. $]); - } - assert(find(arr, 256) == arr[$ .. $]); - } - } - dg(); - assertCTFEable!dg; -} - -// https://issues.dlang.org/show_bug.cgi?id=11603 -@safe unittest -{ - enum Foo : ubyte { A } - assert([Foo.A].find(Foo.A).empty == false); - - ubyte x = 0; - assert([x].find(x).empty == false); -} - -/// ditto -R1 find(alias pred = "a == b", R1, R2)(R1 haystack, scope R2 needle) -if (isForwardRange!R1 && isForwardRange!R2 - && is(typeof(binaryFun!pred(haystack.front, needle.front)) : bool)) -{ - static if (!isRandomAccessRange!R1) - { - static if (is(typeof(pred == "a == b")) && pred == "a == b" && isSomeString!R1 && isSomeString!R2 - && haystack[0].sizeof == needle[0].sizeof) - { - // return cast(R1) find(representation(haystack), representation(needle)); - // Specialization for simple string search - alias Representation = - Select!(haystack[0].sizeof == 1, ubyte[], - Select!(haystack[0].sizeof == 2, ushort[], uint[])); - // Will use the array specialization - static TO force(TO, T)(inout T r) @trusted { return cast(TO) r; } - return force!R1(.find!(pred, Representation, Representation) - (force!Representation(haystack), force!Representation(needle))); - } - else - { - return simpleMindedFind!pred(haystack, needle); - } - } - else static if (!isBidirectionalRange!R2 || !hasSlicing!R1) - { - static if (!is(ElementType!R1 == ElementType!R2)) - { - return simpleMindedFind!pred(haystack, needle); - } - else - { - // Prepare the search with needle's first element - if (needle.empty) - return haystack; - - haystack = .find!pred(haystack, needle.front); - - static if (hasLength!R1 && hasLength!R2 && is(typeof(takeNone(haystack)) == R1)) - { - if (needle.length > haystack.length) - return takeNone(haystack); - } - else - { - if (haystack.empty) - return haystack; - } - - needle.popFront(); - size_t matchLen = 1; - - // Loop invariant: haystack[0 .. matchLen] matches everything in - // the initial needle that was popped out of needle. - for (;;) - { - // Extend matchLength as much as possible - for (;;) - { - import std.range : takeNone; - - if (needle.empty || haystack.empty) - return haystack; - - static if (hasLength!R1 && is(typeof(takeNone(haystack)) == R1)) - { - if (matchLen == haystack.length) - return takeNone(haystack); - } - - if (!binaryFun!pred(haystack[matchLen], needle.front)) - break; - - ++matchLen; - needle.popFront(); - } - - auto bestMatch = haystack[0 .. matchLen]; - haystack.popFront(); - haystack = .find!pred(haystack, bestMatch); - } - } - } - else // static if (hasSlicing!R1 && isBidirectionalRange!R2) - { - if (needle.empty) return haystack; - - static if (hasLength!R2) - { - immutable needleLength = needle.length; - } - else - { - immutable needleLength = walkLength(needle.save); - } - if (needleLength > haystack.length) - { - return haystack[haystack.length .. haystack.length]; - } - // Optimization in case the ranges are both SortedRanges. - // Binary search can be used to find the first occurence - // of the first element of the needle in haystack. - // When it is found O(walklength(needle)) steps are performed. - // https://issues.dlang.org/show_bug.cgi?id=8829 enhancement - import std.algorithm.comparison : mismatch; - import std.range : SortedRange; - static if (is(R1 == R2) - && is(R1 : SortedRange!TT, TT) - && pred == "a == b") - { - auto needleFirstElem = needle[0]; - auto partitions = haystack.trisect(needleFirstElem); - auto firstElemLen = partitions[1].length; - size_t count = 0; - - if (firstElemLen == 0) - return haystack[$ .. $]; - - while (needle.front() == needleFirstElem) - { - needle.popFront(); - ++count; - - if (count > firstElemLen) - return haystack[$ .. $]; - } - - auto m = mismatch(partitions[2], needle); - - if (m[1].empty) - return haystack[partitions[0].length + partitions[1].length - count .. $]; - } - else static if (isRandomAccessRange!R2) - { - immutable lastIndex = needleLength - 1; - auto last = needle[lastIndex]; - size_t j = lastIndex, skip = 0; - for (; j < haystack.length;) - { - if (!binaryFun!pred(haystack[j], last)) - { - ++j; - continue; - } - immutable k = j - lastIndex; - // last elements match - for (size_t i = 0;; ++i) - { - if (i == lastIndex) - return haystack[k .. haystack.length]; - if (!binaryFun!pred(haystack[k + i], needle[i])) - break; - } - if (skip == 0) - { - skip = 1; - while (skip < needleLength && needle[needleLength - 1 - skip] != needle[needleLength - 1]) - { - ++skip; - } - } - j += skip; - } - } - else - { - // @@@BUG@@@ - // auto needleBack = moveBack(needle); - // Stage 1: find the step - size_t step = 1; - auto needleBack = needle.back; - needle.popBack(); - for (auto i = needle.save; !i.empty && i.back != needleBack; - i.popBack(), ++step) - { - } - // Stage 2: linear find - size_t scout = needleLength - 1; - for (;;) - { - if (scout >= haystack.length) - break; - if (!binaryFun!pred(haystack[scout], needleBack)) - { - ++scout; - continue; - } - // Found a match with the last element in the needle - auto cand = haystack[scout + 1 - needleLength .. haystack.length]; - if (startsWith!pred(cand, needle)) - { - // found - return cand; - } - scout += step; - } - } - return haystack[haystack.length .. haystack.length]; - } -} - -/// -@safe unittest -{ - import std.container : SList; - import std.range.primitives : empty; - import std.typecons : Tuple; - - assert(find("hello, world", "World").empty); - assert(find("hello, world", "wo") == "world"); - assert([1, 2, 3, 4].find(SList!int(2, 3)[]) == [2, 3, 4]); - alias C = Tuple!(int, "x", int, "y"); - auto a = [C(1,0), C(2,0), C(3,1), C(4,0)]; - assert(a.find!"a.x == b"([2, 3]) == [C(2,0), C(3,1), C(4,0)]); - assert(a[1 .. $].find!"a.x == b"([2, 3]) == [C(2,0), C(3,1), C(4,0)]); -} - -@safe unittest -{ - import std.container : SList; - alias C = Tuple!(int, "x", int, "y"); - assert([C(1,0), C(2,0), C(3,1), C(4,0)].find!"a.x == b"(SList!int(2, 3)[]) == [C(2,0), C(3,1), C(4,0)]); -} - -// https://issues.dlang.org/show_bug.cgi?id=12470 -@safe unittest -{ - import std.array : replace; - inout(char)[] sanitize(inout(char)[] p) - { - return p.replace("\0", " "); - } - assert(sanitize("O\x00o") == "O o"); -} - -@safe unittest -{ - import std.algorithm.comparison : equal; - import std.container : SList; - - auto lst = SList!int(1, 2, 5, 7, 3); - static assert(isForwardRange!(int[])); - static assert(isForwardRange!(typeof(lst[]))); - auto r = find(lst[], [2, 5]); - assert(equal(r, SList!int(2, 5, 7, 3)[])); -} - -@safe unittest -{ - import std.range : assumeSorted; - - auto r1 = assumeSorted([1, 2, 3, 3, 3, 4, 5, 6, 7, 8, 8, 8, 10]); - auto r2 = assumeSorted([3, 3, 4, 5, 6, 7, 8, 8]); - auto r3 = assumeSorted([3, 4, 5, 6, 7, 8]); - auto r4 = assumeSorted([4, 5, 6]); - auto r5 = assumeSorted([12, 13]); - auto r6 = assumeSorted([8, 8, 10, 11]); - auto r7 = assumeSorted([3, 3, 3, 3, 3, 3, 3]); - - assert(find(r1, r2) == assumeSorted([3, 3, 4, 5, 6, 7, 8, 8, 8, 10])); - assert(find(r1, r3) == assumeSorted([3, 4, 5, 6, 7, 8, 8, 8, 10])); - assert(find(r1, r4) == assumeSorted([4, 5, 6, 7, 8, 8, 8, 10])); - assert(find(r1, r5).empty()); - assert(find(r1, r6).empty()); - assert(find(r1, r7).empty()); -} - -@safe unittest -{ - import std.algorithm.comparison : equal; - // @@@BUG@@@ removing static below makes unittest fail - static struct BiRange - { - int[] payload; - @property bool empty() { return payload.empty; } - @property BiRange save() { return this; } - @property ref int front() { return payload[0]; } - @property ref int back() { return payload[$ - 1]; } - void popFront() { return payload.popFront(); } - void popBack() { return payload.popBack(); } - } - auto r = BiRange([1, 2, 3, 10, 11, 4]); - assert(equal(find(r, [10, 11]), [10, 11, 4])); -} - -@safe unittest -{ - import std.container : SList; - - assert(find([ 1, 2, 3 ], SList!int(2, 3)[]) == [ 2, 3 ]); - assert(find([ 1, 2, 1, 2, 3, 3 ], SList!int(2, 3)[]) == [ 2, 3, 3 ]); -} - -// https://issues.dlang.org/show_bug.cgi?id=8334 -@safe unittest -{ - import std.algorithm.iteration : filter; - import std.range; - - auto haystack = [1, 2, 3, 4, 1, 9, 12, 42]; - auto needle = [12, 42, 27]; - - //different overload of find, but it's the base case. - assert(find(haystack, needle).empty); - - assert(find(haystack, takeExactly(filter!"true"(needle), 3)).empty); - assert(find(haystack, filter!"true"(needle)).empty); -} - -// https://issues.dlang.org/show_bug.cgi?id=11013 -@safe unittest -{ - assert(find!"a == a"("abc","abc") == "abc"); -} - -// Internally used by some find() overloads above -private R1 simpleMindedFind(alias pred, R1, R2)(R1 haystack, scope R2 needle) -{ - enum estimateNeedleLength = hasLength!R1 && !hasLength!R2; - - static if (hasLength!R1) - { - static if (!hasLength!R2) - size_t estimatedNeedleLength = 0; - else - immutable size_t estimatedNeedleLength = needle.length; - } - - bool haystackTooShort() - { - static if (estimateNeedleLength) - { - return haystack.length < estimatedNeedleLength; - } - else - { - return haystack.empty; - } - } - - searching: - for (;; haystack.popFront()) - { - if (haystackTooShort()) - { - // Failed search - static if (hasLength!R1) - { - static if (is(typeof(haystack[haystack.length .. - haystack.length]) : R1)) - return haystack[haystack.length .. haystack.length]; - else - return R1.init; - } - else - { - assert(haystack.empty, "Haystack must be empty by now"); - return haystack; - } - } - static if (estimateNeedleLength) - size_t matchLength = 0; - for (auto h = haystack.save, n = needle.save; - !n.empty; - h.popFront(), n.popFront()) - { - if (h.empty || !binaryFun!pred(h.front, n.front)) - { - // Failed searching n in h - static if (estimateNeedleLength) - { - if (estimatedNeedleLength < matchLength) - estimatedNeedleLength = matchLength; - } - continue searching; - } - static if (estimateNeedleLength) - ++matchLength; - } - break; - } - return haystack; -} - -@safe unittest -{ - // Test simpleMindedFind for the case where both haystack and needle have - // length. - struct CustomString - { - @safe: - string _impl; - - // This is what triggers https://issues.dlang.org/show_bug.cgi?id=7992. - @property size_t length() const { return _impl.length; } - @property void length(size_t len) { _impl.length = len; } - - // This is for conformance to the forward range API (we deliberately - // make it non-random access so that we will end up in - // simpleMindedFind). - @property bool empty() const { return _impl.empty; } - @property dchar front() const { return _impl.front; } - void popFront() { _impl.popFront(); } - @property CustomString save() { return this; } - } - - // If https://issues.dlang.org/show_bug.cgi?id=7992 occurs, this will throw an exception from calling - // popFront() on an empty range. - auto r = find(CustomString("a"), CustomString("b")); - assert(r.empty); -} - -/** -Finds two or more `needles` into a `haystack`. The predicate $(D -pred) is used throughout to compare elements. By default, elements are -compared for equality. - -Params: - -pred = The predicate to use for comparing elements. - -haystack = The target of the search. Must be an input range. -If any of `needles` is a range with elements comparable to -elements in `haystack`, then `haystack` must be a -$(REF_ALTTEXT forward range, isForwardRange, std,range,primitives) -such that the search can backtrack. - -needles = One or more items to search for. Each of `needles` must -be either comparable to one element in `haystack`, or be itself a -forward range with elements comparable with elements in -`haystack`. - -Returns: - -A tuple containing `haystack` positioned to match one of the -needles and also the 1-based index of the matching element in $(D -needles) (0 if none of `needles` matched, 1 if `needles[0]` -matched, 2 if `needles[1]` matched...). The first needle to be found -will be the one that matches. If multiple needles are found at the -same spot in the range, then the shortest one is the one which matches -(if multiple needles of the same length are found at the same spot (e.g -`"a"` and `'a'`), then the left-most of them in the argument list -matches). - -The relationship between `haystack` and `needles` simply means -that one can e.g. search for individual `int`s or arrays of $(D -int)s in an array of `int`s. In addition, if elements are -individually comparable, searches of heterogeneous types are allowed -as well: a `double[]` can be searched for an `int` or a $(D -short[]), and conversely a `long` can be searched for a `float` -or a `double[]`. This makes for efficient searches without the need -to coerce one side of the comparison into the other's side type. - -The complexity of the search is $(BIGOH haystack.length * -max(needles.length)). (For needles that are individual items, length -is considered to be 1.) The strategy used in searching several -subranges at once maximizes cache usage by moving in `haystack` as -few times as possible. - */ -Tuple!(Range, size_t) find(alias pred = "a == b", Range, Needles...) -(Range haystack, Needles needles) -if (Needles.length > 1 && is(typeof(startsWith!pred(haystack, needles)))) -{ - for (;; haystack.popFront()) - { - size_t r = startsWith!pred(haystack, needles); - if (r || haystack.empty) - { - return tuple(haystack, r); - } - } -} - -/// -@safe unittest -{ - import std.typecons : tuple; - int[] a = [ 1, 4, 2, 3 ]; - assert(find(a, 4) == [ 4, 2, 3 ]); - assert(find(a, [ 1, 4 ]) == [ 1, 4, 2, 3 ]); - assert(find(a, [ 1, 3 ], 4) == tuple([ 4, 2, 3 ], 2)); - // Mixed types allowed if comparable - assert(find(a, 5, [ 1.2, 3.5 ], 2.0) == tuple([ 2, 3 ], 3)); -} - -@safe unittest -{ - auto s1 = "Mary has a little lamb"; - assert(find(s1, "has a", "has an") == tuple("has a little lamb", 1)); - assert(find(s1, 't', "has a", "has an") == tuple("has a little lamb", 2)); - assert(find(s1, 't', "has a", 'y', "has an") == tuple("y has a little lamb", 3)); - assert(find("abc", "bc").length == 2); -} - -@safe unittest -{ - import std.algorithm.internal : rndstuff; - import std.meta : AliasSeq; - import std.uni : toUpper; - - int[] a = [ 1, 2, 3 ]; - assert(find(a, 5).empty); - assert(find(a, 2) == [2, 3]); - - foreach (T; AliasSeq!(int, double)) - { - auto b = rndstuff!(T)(); - if (!b.length) continue; - b[$ / 2] = 200; - b[$ / 4] = 200; - assert(find(b, 200).length == b.length - b.length / 4); - } - - // Case-insensitive find of a string - string[] s = [ "Hello", "world", "!" ]; - assert(find!("toUpper(a) == toUpper(b)")(s, "hello").length == 3); - - static bool f(string a, string b) { return toUpper(a) == toUpper(b); } - assert(find!(f)(s, "hello").length == 3); -} - -@safe unittest -{ - import std.algorithm.comparison : equal; - import std.algorithm.internal : rndstuff; - import std.meta : AliasSeq; - import std.range : retro; - - int[] a = [ 1, 2, 3, 2, 6 ]; - assert(find(retro(a), 5).empty); - assert(equal(find(retro(a), 2), [ 2, 3, 2, 1 ][])); - - foreach (T; AliasSeq!(int, double)) - { - auto b = rndstuff!(T)(); - if (!b.length) continue; - b[$ / 2] = 200; - b[$ / 4] = 200; - assert(find(retro(b), 200).length == - b.length - (b.length - 1) / 2); - } -} - -@safe unittest -{ - import std.algorithm.comparison : equal; - import std.internal.test.dummyrange; - - int[] a = [ -1, 0, 1, 2, 3, 4, 5 ]; - int[] b = [ 1, 2, 3 ]; - assert(find(a, b) == [ 1, 2, 3, 4, 5 ]); - assert(find(b, a).empty); - - foreach (DummyType; AllDummyRanges) - { - DummyType d; - auto findRes = find(d, 5); - assert(equal(findRes, [5,6,7,8,9,10])); - } -} - -/** - * Finds `needle` in `haystack` efficiently using the - * $(LINK2 https://en.wikipedia.org/wiki/Boyer%E2%80%93Moore_string_search_algorithm, - * Boyer-Moore) method. - * - * Params: - * haystack = A random-access range with length and slicing. - * needle = A $(LREF BoyerMooreFinder). - * - * Returns: - * `haystack` advanced such that `needle` is a prefix of it (if no - * such position exists, returns `haystack` advanced to termination). - */ -RandomAccessRange find(RandomAccessRange, alias pred, InputRange)( - RandomAccessRange haystack, scope BoyerMooreFinder!(pred, InputRange) needle) -{ - return needle.beFound(haystack); -} - -@safe unittest -{ - string h = "/homes/aalexand/d/dmd/bin/../lib/libphobos.a(dmain2.o)"~ - "(.gnu.linkonce.tmain+0x74): In function `main' undefined reference"~ - " to `_Dmain':"; - string[] ns = ["libphobos", "function", " undefined", "`", ":"]; - foreach (n ; ns) - { - auto p = find(h, boyerMooreFinder(n)); - assert(!p.empty); - } -} - -/// -@safe unittest -{ - import std.range.primitives : empty; - int[] a = [ -1, 0, 1, 2, 3, 4, 5 ]; - int[] b = [ 1, 2, 3 ]; - - assert(find(a, boyerMooreFinder(b)) == [ 1, 2, 3, 4, 5 ]); - assert(find(b, boyerMooreFinder(a)).empty); -} - -@safe unittest -{ - auto bm = boyerMooreFinder("for"); - auto match = find("Moor", bm); - assert(match.empty); -} - -// canFind -/++ -Convenience function. Like find, but only returns whether or not the search -was successful. - -For more information about `pred` see $(LREF find). - -See_Also: -$(REF among, std,algorithm,comparison) for checking a value against multiple arguments. - +/ -template canFind(alias pred="a == b") -{ - /++ - Returns `true` if and only if `pred(e)` is true for any value `e` in the - input range `range`. - Performs (at most) $(BIGOH haystack.length) evaluations of `pred`. - +/ - bool canFind(Range)(Range haystack) - if (is(typeof(find!pred(haystack)))) - { - return any!pred(haystack); - } - - /++ - Returns `true` if and only if `needle` can be found in $(D - range). Performs $(BIGOH haystack.length) evaluations of `pred`. - +/ - bool canFind(Range, Element)(Range haystack, scope Element needle) - if (is(typeof(find!pred(haystack, needle)))) - { - return !find!pred(haystack, needle).empty; - } - - /++ - Returns the 1-based index of the first needle found in `haystack`. If no - needle is found, then `0` is returned. - - So, if used directly in the condition of an `if` statement or loop, the result - will be `true` if one of the needles is found and `false` if none are - found, whereas if the result is used elsewhere, it can either be cast to - `bool` for the same effect or used to get which needle was found first - without having to deal with the tuple that $(LREF find) returns for the - same operation. - +/ - size_t canFind(Range, Needles...)(Range haystack, scope Needles needles) - if (Needles.length > 1 && - is(typeof(find!pred(haystack, needles)))) - { - return find!pred(haystack, needles)[1]; - } -} - -/// -@safe unittest -{ - const arr = [0, 1, 2, 3]; - assert(canFind(arr, 2)); - assert(!canFind(arr, 4)); - - // find one of several needles - assert(arr.canFind(3, 2)); - assert(arr.canFind(3, 2) == 2); // second needle found - assert(arr.canFind([1, 3], 2) == 2); - - assert(canFind(arr, [1, 2], [2, 3])); - assert(canFind(arr, [1, 2], [2, 3]) == 1); - assert(canFind(arr, [1, 7], [2, 3])); - assert(canFind(arr, [1, 7], [2, 3]) == 2); - assert(!canFind(arr, [1, 3], [2, 4])); - assert(canFind(arr, [1, 3], [2, 4]) == 0); -} - -/** - * Example using a custom predicate. - * Note that the needle appears as the second argument of the predicate. - */ -@safe unittest -{ - auto words = [ - "apple", - "beeswax", - "cardboard" - ]; - assert(!canFind(words, "bees")); - assert( canFind!((string elem, string needle) => elem.startsWith(needle))(words, "bees")); -} - -/// Search for multiple items in an array of items (search for needles in an array of haystacks) -@safe unittest -{ - string s1 = "aaa111aaa"; - string s2 = "aaa222aaa"; - string s3 = "aaa333aaa"; - string s4 = "aaa444aaa"; - const hay = [s1, s2, s3, s4]; - assert(hay.canFind!(e => e.canFind("111", "222"))); -} - -@safe unittest -{ - import std.algorithm.internal : rndstuff; - - auto a = rndstuff!(int)(); - if (a.length) - { - auto b = a[a.length / 2]; - assert(canFind(a, b)); - } -} - -@safe unittest -{ - import std.algorithm.comparison : equal; - assert(equal!(canFind!"a < b")([[1, 2, 3], [7, 8, 9]], [2, 8])); -} - -// findAdjacent -/** -Advances `r` until it finds the first two adjacent elements `a`, -`b` that satisfy `pred(a, b)`. Performs $(BIGOH r.length) -evaluations of `pred`. - -For more information about `pred` see $(LREF find). - -Params: - pred = The predicate to satisfy. - r = A $(REF_ALTTEXT forward range, isForwardRange, std,range,primitives) to - search in. - -Returns: -`r` advanced to the first occurrence of two adjacent elements that satisfy -the given predicate. If there are no such two elements, returns `r` advanced -until empty. - -See_Also: - $(LINK2 http://en.cppreference.com/w/cpp/algorithm/adjacent_find, STL's `adjacent_find`) -*/ -Range findAdjacent(alias pred = "a == b", Range)(Range r) -if (isForwardRange!(Range)) -{ - auto ahead = r.save; - if (!ahead.empty) - { - for (ahead.popFront(); !ahead.empty; r.popFront(), ahead.popFront()) - { - if (binaryFun!(pred)(r.front, ahead.front)) return r; - } - } - static if (!isInfinite!Range) - return ahead; - assert(0); -} - -/// -@safe unittest -{ - int[] a = [ 11, 10, 10, 9, 8, 8, 7, 8, 9 ]; - auto r = findAdjacent(a); - assert(r == [ 10, 10, 9, 8, 8, 7, 8, 9 ]); - auto p = findAdjacent!("a < b")(a); - assert(p == [ 7, 8, 9 ]); - -} - -@safe unittest -{ - import std.algorithm.comparison : equal; - import std.internal.test.dummyrange; - import std.range; - - int[] a = [ 11, 10, 10, 9, 8, 8, 7, 8, 9 ]; - auto p = findAdjacent(a); - assert(p == [10, 10, 9, 8, 8, 7, 8, 9 ]); - p = findAdjacent!("a < b")(a); - assert(p == [7, 8, 9]); - // empty - a = []; - p = findAdjacent(a); - assert(p.empty); - // not found - a = [ 1, 2, 3, 4, 5 ]; - p = findAdjacent(a); - assert(p.empty); - p = findAdjacent!"a > b"(a); - assert(p.empty); - ReferenceForwardRange!int rfr = new ReferenceForwardRange!int([1, 2, 3, 2, 2, 3]); - assert(equal(findAdjacent(rfr), [2, 2, 3])); - - // https://issues.dlang.org/show_bug.cgi?id=9350 - assert(!repeat(1).findAdjacent().empty); -} - -// findAmong -/** -Searches the given range for an element that matches one of the given choices. - -Advances `seq` by calling `seq.popFront` until either -`find!(pred)(choices, seq.front)` is `true`, or `seq` becomes empty. -Performs $(BIGOH seq.length * choices.length) evaluations of `pred`. - -For more information about `pred` see $(LREF find). - -Params: - pred = The predicate to use for determining a match. - seq = The $(REF_ALTTEXT input range, isInputRange, std,range,primitives) to - search. - choices = A $(REF_ALTTEXT forward range, isForwardRange, std,range,primitives) - of possible choices. - -Returns: -`seq` advanced to the first matching element, or until empty if there are no -matching elements. - -See_Also: $(LREF find), $(REF among, std,algorithm,comparison) -*/ -InputRange findAmong(alias pred = "a == b", InputRange, ForwardRange)( - InputRange seq, ForwardRange choices) -if (isInputRange!InputRange && isForwardRange!ForwardRange) -{ - for (; !seq.empty && find!pred(choices.save, seq.front).empty; seq.popFront()) - { - } - return seq; -} - -/// -@safe unittest -{ - int[] a = [ -1, 0, 1, 2, 3, 4, 5 ]; - int[] b = [ 3, 1, 2 ]; - assert(findAmong(a, b) == a[2 .. $]); -} - -@safe unittest -{ - int[] a = [ -1, 0, 2, 1, 2, 3, 4, 5 ]; - int[] b = [ 1, 2, 3 ]; - assert(findAmong(a, b) == [2, 1, 2, 3, 4, 5 ]); - assert(findAmong(b, [ 4, 6, 7 ][]).empty); - assert(findAmong!("a == b")(a, b).length == a.length - 2); - assert(findAmong!("a == b")(b, [ 4, 6, 7 ][]).empty); -} - -// https://issues.dlang.org/show_bug.cgi?id=19765 -@system unittest -{ - import std.range.interfaces : inputRangeObject; - auto choices = inputRangeObject("b"); - auto f = "foobar".findAmong(choices); - assert(f == "bar"); -} - -// findSkip -/** - * Finds `needle` in `haystack` and positions `haystack` - * right after the first occurrence of `needle`. - * - * If no needle is provided, the `haystack` is advanced as long as `pred` - * evaluates to `true`. - * Similarly, the haystack is positioned so as `pred` evaluates to `false` for - * `haystack.front`. - * - * For more information about `pred` see $(LREF find). - - * Params: - * haystack = The - * $(REF_ALTTEXT forward range, isForwardRange, std,range,primitives) to search - * in. - * needle = The - * $(REF_ALTTEXT forward range, isForwardRange, std,range,primitives) to search - * for. - * pred = Custom predicate for comparison of haystack and needle - * - * Returns: `true` if the needle was found, in which case `haystack` is - * positioned after the end of the first occurrence of `needle`; otherwise - * `false`, leaving `haystack` untouched. If no needle is provided, it returns - * the number of times `pred(haystack.front)` returned true. - * - * See_Also: $(LREF find) - */ -bool findSkip(alias pred = "a == b", R1, R2)(ref R1 haystack, R2 needle) -if (isForwardRange!R1 && isForwardRange!R2 - && is(typeof(binaryFun!pred(haystack.front, needle.front)))) -{ - auto parts = findSplit!pred(haystack, needle); - if (parts[1].empty) return false; - // found - haystack = parts[2]; - return true; -} - -/// -@safe unittest -{ - import std.range.primitives : empty; - // Needle is found; s is replaced by the substring following the first - // occurrence of the needle. - string s = "abcdef"; - assert(findSkip(s, "cd") && s == "ef"); - - // Needle is not found; s is left untouched. - s = "abcdef"; - assert(!findSkip(s, "cxd") && s == "abcdef"); - - // If the needle occurs at the end of the range, the range is left empty. - s = "abcdef"; - assert(findSkip(s, "def") && s.empty); -} - -// https://issues.dlang.org/show_bug.cgi?id=19020 -@safe unittest -{ - static struct WrapperRange - { - string _r; - @property auto empty() { return _r.empty(); } - @property auto front() { return _r.front(); } - auto popFront() { return _r.popFront(); } - @property auto save() { return WrapperRange(_r.save); } - } - auto tmp = WrapperRange("there is a bug here: *"); - assert(!tmp.findSkip("*/")); - assert(tmp._r == "there is a bug here: *"); -} - -/// ditto -size_t findSkip(alias pred, R1)(ref R1 haystack) -if (isForwardRange!R1 && ifTestable!(typeof(haystack.front), unaryFun!pred)) -{ - size_t result; - while (!haystack.empty && unaryFun!pred(haystack.front)) - { - result++; - haystack.popFront; - } - return result; -} - -/// -@safe unittest -{ - import std.ascii : isWhite; - string s = " abc"; - assert(findSkip!isWhite(s) && s == "abc"); - assert(!findSkip!isWhite(s) && s == "abc"); - - s = " "; - assert(findSkip!isWhite(s) == 2); -} - -@safe unittest -{ - import std.ascii : isWhite; - - auto s = " "; - assert(findSkip!isWhite(s) == 2); -} - -private struct FindSplitResult(ubyte emptyRangeIndex, Types...) -{ - this(Types vals) - { - asTuple = typeof(asTuple)(vals); - } - void opAssign(typeof(asTuple) rhs) - { - asTuple = rhs; - } - Tuple!Types asTuple; - alias asTuple this; - - static if (hasConstEmptyMember!(typeof(asTuple[emptyRangeIndex]))) - { - bool opCast(T : bool)() const => !asTuple[emptyRangeIndex].empty; - } - else - { - bool opCast(T : bool)() => !asTuple[emptyRangeIndex].empty; - } -} - -/** -These functions find the first occurrence of `needle` in `haystack` and then -split `haystack` as follows. - -$(PANEL -`findSplit` returns a tuple `result` containing $(I three) ranges. -$(UL -$(LI `result[0]` is the portion of `haystack` before `needle`) -$(LI `result[1]` is the portion of -`haystack` that matches `needle`) -$(LI `result[2]` is the portion of `haystack` -after the match.) -) -If `needle` was not found, `result[0]` comprehends `haystack` -entirely and `result[1]` and `result[2]` are empty. - -`findSplitBefore` returns a tuple `result` containing two ranges. -$(UL -$(LI `result[0]` is the portion of `haystack` before `needle`) -$(LI `result[1]` is the balance of `haystack` starting with the match.) -) -If `needle` was not found, `result[0]` -comprehends `haystack` entirely and `result[1]` is empty. - -`findSplitAfter` returns a tuple `result` containing two ranges. -$(UL -$(LI `result[0]` is the portion of `haystack` up to and including the -match) -$(LI `result[1]` is the balance of `haystack` starting -after the match.) -) -If `needle` was not found, `result[0]` is empty -and `result[1]` is `haystack`. -) -$(P -In all cases, the concatenation of the returned ranges spans the -entire `haystack`. - -If `haystack` is a random-access range, all three components of the tuple have -the same type as `haystack`. Otherwise, `haystack` must be a -$(REF_ALTTEXT forward range, isForwardRange, std,range,primitives) and -the type of `result[0]` (and `result[1]` for `findSplit`) is the same as -the result of $(REF takeExactly, std,range). - -For more information about `pred` see $(LREF find). -) -Params: - pred = Predicate to compare 2 elements. - haystack = The forward range to search. - needle = The forward range to look for. - -Returns: - -A sub-type of $(REF Tuple, std, typecons) of the split portions of `haystack` (see above for -details). This sub-type of `Tuple` defines `opCast!bool`, which -returns `true` when the separating `needle` was found and `false` otherwise. - -See_Also: $(LREF find) - */ -auto findSplit(alias pred = "a == b", R1, R2)(R1 haystack, R2 needle) -if (isForwardRange!R1 && isForwardRange!R2) -{ - static if (isSomeString!R1 && isSomeString!R2 - || (isRandomAccessRange!R1 && hasSlicing!R1 && hasLength!R1 && hasLength!R2)) - { - auto balance = find!pred(haystack, needle); - immutable pos1 = haystack.length - balance.length; - immutable pos2 = balance.empty ? pos1 : pos1 + needle.length; - alias Slice = typeof(haystack[0 .. pos1]); - return FindSplitResult!(1, Slice, Slice, Slice)( - haystack[0 .. pos1], haystack[pos1 .. pos2], haystack[pos2 .. haystack.length]); - } - else - { - import std.range : takeExactly; - auto original = haystack.save; - auto h = haystack.save; - auto n = needle.save; - size_t pos1, pos2; - while (!n.empty && !h.empty) - { - if (binaryFun!pred(h.front, n.front)) - { - h.popFront(); - n.popFront(); - ++pos2; - } - else - { - haystack.popFront(); - n = needle.save; - h = haystack.save; - pos2 = ++pos1; - } - } - if (!n.empty) // incomplete match at the end of haystack - { - pos1 = pos2; - } - return FindSplitResult!(1, - typeof(takeExactly(original, pos1)), - typeof(takeExactly(original, pos1)), typeof(h))( - takeExactly(original, pos1), - takeExactly(haystack, pos2 - pos1), h); - } -} - -/// Ditto -auto findSplitBefore(alias pred = "a == b", R1, R2)(R1 haystack, R2 needle) -if (isForwardRange!R1 && isForwardRange!R2) -{ - static if (isSomeString!R1 && isSomeString!R2 - || (isRandomAccessRange!R1 && hasLength!R1 && hasSlicing!R1 && hasLength!R2)) - { - auto balance = find!pred(haystack, needle); - immutable pos = haystack.length - balance.length; - return FindSplitResult!(1, - typeof(haystack[0 .. pos]), typeof(haystack[0 .. pos]))( - haystack[0 .. pos], haystack[pos .. haystack.length]); - } - else - { - import std.range : takeExactly; - auto original = haystack.save; - auto h = haystack.save; - auto n = needle.save; - size_t pos1, pos2; - while (!n.empty && !h.empty) - { - if (binaryFun!pred(h.front, n.front)) - { - h.popFront(); - n.popFront(); - ++pos2; - } - else - { - haystack.popFront(); - n = needle.save; - h = haystack.save; - pos2 = ++pos1; - } - } - if (!n.empty) // incomplete match at the end of haystack - { - pos1 = pos2; - haystack = h; - } - return FindSplitResult!(1, - typeof(takeExactly(original, pos1)), typeof(haystack))( - takeExactly(original, pos1), haystack); - } -} - -/// Ditto -auto findSplitAfter(alias pred = "a == b", R1, R2)(R1 haystack, R2 needle) -if (isForwardRange!R1 && isForwardRange!R2) -{ - static if (isSomeString!R1 && isSomeString!R2 - || isRandomAccessRange!R1 && hasLength!R1 && hasSlicing!R1 && hasLength!R2) - { - auto balance = find!pred(haystack, needle); - immutable pos = balance.empty ? 0 : haystack.length - balance.length + needle.length; - return FindSplitResult!(0, - typeof(haystack[0 .. pos]), typeof(haystack[0 .. pos]))( - haystack[0 .. pos], haystack[pos .. haystack.length]); - } - else - { - import std.range : takeExactly; - alias Res = FindSplitResult!(0, typeof(takeExactly(haystack, 0)), typeof(haystack)); - auto original = haystack.save; - auto h = haystack.save; - auto n = needle.save; - size_t pos1, pos2; - while (!n.empty) - { - if (h.empty) - { - // Failed search - return Res(takeExactly(original, 0), original); - } - if (binaryFun!pred(h.front, n.front)) - { - h.popFront(); - n.popFront(); - ++pos2; - } - else - { - haystack.popFront(); - n = needle.save; - h = haystack.save; - pos2 = ++pos1; - } - } - return Res(takeExactly(original, pos2), h); - } -} - -/// Returning a subtype of $(REF Tuple, std,typecons) enables -/// the following convenient idiom: -@safe pure nothrow unittest -{ - // findSplit returns a triplet - if (auto split = "dlang-rocks".findSplit("-")) - { - assert(split[0] == "dlang"); - assert(split[1] == "-"); - assert(split[2] == "rocks"); - } - else assert(0); - - // findSplitBefore returns 2 ranges - if (const split = [2, 3, 2, 3, 4, 1].findSplitBefore!"a > b"([2, 2])) - { - assert(split[0] == [2, 3, 2]); - // [3, 4] each greater than [2, 2] - assert(split[1] == [3, 4, 1]); - } - else assert(0); -} - -/// -@safe pure nothrow unittest -{ - import std.range.primitives : empty; - - auto a = "Carl Sagan Memorial Station"; - auto r = findSplit(a, "Velikovsky"); - import std.typecons : isTuple; - static assert(isTuple!(typeof(r.asTuple))); - static assert(isTuple!(typeof(r))); - assert(!r); - assert(r[0] == a); - assert(r[1].empty); - assert(r[2].empty); - r = findSplit(a, " "); - assert(r[0] == "Carl"); - assert(r[1] == " "); - assert(r[2] == "Sagan Memorial Station"); - if (const r1 = findSplitBefore(a, "Sagan")) - { - assert(r1); - assert(r1[0] == "Carl "); - assert(r1[1] == "Sagan Memorial Station"); - } - if (const r2 = findSplitAfter(a, "Sagan")) - { - assert(r2); - assert(r2[0] == "Carl Sagan"); - assert(r2[1] == " Memorial Station"); - } -} - -/// Use $(REF only, std,range) to find single elements: -@safe pure nothrow unittest -{ - import std.range : only; - assert([1, 2, 3, 4].findSplitBefore(only(3))[0] == [1, 2]); -} - -@safe pure nothrow unittest -{ - import std.range.primitives : empty; - - immutable a = [ 1, 2, 3, 4, 5, 6, 7, 8 ]; - auto r = findSplit(a, [9, 1]); - assert(!r); - assert(r[0] == a); - assert(r[1].empty); - assert(r[2].empty); - r = findSplit(a, [3]); - assert(r); - assert(r[0] == a[0 .. 2]); - assert(r[1] == a[2 .. 3]); - assert(r[2] == a[3 .. $]); - - { - const r1 = findSplitBefore(a, [9, 1]); - assert(!r1); - assert(r1[0] == a); - assert(r1[1].empty); - } - - if (immutable r1 = findSplitBefore(a, [3, 4])) - { - assert(r1); - assert(r1[0] == a[0 .. 2]); - assert(r1[1] == a[2 .. $]); - } - else assert(0); - - { - const r2 = findSplitAfter(a, [9, 1]); - assert(!r2); - assert(r2[0].empty); - assert(r2[1] == a); - } - - if (immutable r3 = findSplitAfter(a, [3, 4])) - { - assert(r3); - assert(r3[0] == a[0 .. 4]); - assert(r3[1] == a[4 .. $]); - } - else assert(0); -} - -@safe pure nothrow unittest -{ - import std.algorithm.comparison : equal; - import std.algorithm.iteration : filter; - - auto a = [ 1, 2, 3, 4, 5, 6, 7, 8 ]; - auto fwd = filter!"a > 0"(a); - auto r = findSplit(fwd, [9, 1]); - assert(!r); - assert(equal(r[0], a)); - assert(r[1].empty); - assert(r[2].empty); - r = findSplit(fwd, [3]); - assert(r); - assert(equal(r[0], a[0 .. 2])); - assert(equal(r[1], a[2 .. 3])); - assert(equal(r[2], a[3 .. $])); - r = findSplit(fwd, [8, 9]); - assert(!r); - assert(equal(r[0], a)); - assert(r[1].empty); - assert(r[2].empty); - - // auto variable `r2` cannot be `const` because `fwd.front` is mutable - { - auto r1 = findSplitBefore(fwd, [9, 1]); - assert(!r1); - assert(equal(r1[0], a)); - assert(r1[1].empty); - } - - if (auto r1 = findSplitBefore(fwd, [3, 4])) - { - assert(r1); - assert(equal(r1[0], a[0 .. 2])); - assert(equal(r1[1], a[2 .. $])); - } - else assert(0); - - { - auto r1 = findSplitBefore(fwd, [8, 9]); - assert(!r1); - assert(equal(r1[0], a)); - assert(r1[1].empty); - } - - { - auto r2 = findSplitAfter(fwd, [9, 1]); - assert(!r2); - assert(r2[0].empty); - assert(equal(r2[1], a)); - } - - if (auto r2 = findSplitAfter(fwd, [3, 4])) - { - assert(r2); - assert(equal(r2[0], a[0 .. 4])); - assert(equal(r2[1], a[4 .. $])); - } - else assert(0); - - { - auto r2 = findSplitAfter(fwd, [8, 9]); - assert(!r2); - assert(r2[0].empty); - assert(equal(r2[1], a)); - } -} - -@safe pure nothrow @nogc unittest -{ - auto str = "sep,one,sep,two"; - - auto split = str.findSplitAfter(","); - assert(split[0] == "sep,"); - - split = split[1].findSplitAfter(","); - assert(split[0] == "one,"); - - split = split[1].findSplitBefore(","); - assert(split[0] == "sep"); -} - -@safe pure nothrow @nogc unittest -{ - auto str = "sep,one,sep,two"; - - auto split = str.findSplitBefore(",two"); - assert(split[0] == "sep,one,sep"); - assert(split[1] == ",two"); - - split = split[0].findSplitBefore(",sep"); - assert(split[0] == "sep,one"); - assert(split[1] == ",sep"); - - split = split[0].findSplitAfter(","); - assert(split[0] == "sep,"); - assert(split[1] == "one"); -} - -// https://issues.dlang.org/show_bug.cgi?id=11013 -@safe pure unittest -{ - auto var = "abc"; - auto split = var.findSplitBefore!q{a == a}(var); - assert(split[0] == ""); - assert(split[1] == "abc"); -} - -// minCount -/** - -Computes the minimum (respectively maximum) of `range` along with its number of -occurrences. Formally, the minimum is a value `x` in `range` such that $(D -pred(a, x)) is `false` for all values `a` in `range`. Conversely, the maximum is -a value `x` in `range` such that `pred(x, a)` is `false` for all values `a` -in `range` (note the swapped arguments to `pred`). - -These functions may be used for computing arbitrary extrema by choosing `pred` -appropriately. For corrrect functioning, `pred` must be a strict partial order, -i.e. transitive (if `pred(a, b) && pred(b, c)` then `pred(a, c)`) and -irreflexive (`pred(a, a)` is `false`). The $(LUCKY trichotomy property of -inequality) is not required: these algorithms consider elements `a` and `b` equal -(for the purpose of counting) if `pred` puts them in the same equivalence class, -i.e. `!pred(a, b) && !pred(b, a)`. - -Params: - pred = The ordering predicate to use to determine the extremum (minimum - or maximum). - range = The $(REF_ALTTEXT input range, isInputRange, std,range,primitives) to count. - -Returns: The minimum, respectively maximum element of a range together with the -number it occurs in the range. - -Limitations: If at least one of the arguments is NaN, the result is -an unspecified value. See $(REF maxElement, std,algorithm,searching) -for examples on how to cope with NaNs. - -Throws: `Exception` if `range.empty`. - -See_Also: $(REF min, std,algorithm,comparison), $(LREF minIndex), $(LREF minElement), $(LREF minPos) - */ -Tuple!(ElementType!Range, size_t) -minCount(alias pred = "a < b", Range)(Range range) -if (isInputRange!Range && !isInfinite!Range && - is(typeof(binaryFun!pred(range.front, range.front)))) -{ - import std.algorithm.internal : algoFormat; - import std.exception : enforce; - - alias T = ElementType!Range; - alias UT = Unqual!T; - alias RetType = Tuple!(T, size_t); - - static assert(is(typeof(RetType(range.front, 1))), - algoFormat("Error: Cannot call minCount on a %s, because it is not possible "~ - "to copy the result value (a %s) into a Tuple.", Range.stringof, T.stringof)); - - enforce(!range.empty, "Can't count elements from an empty range"); - size_t occurrences = 1; - - static if (isForwardRange!Range) - { - Range least = range.save; - for (range.popFront(); !range.empty; range.popFront()) - { - if (binaryFun!pred(least.front, range.front)) - { - assert(!binaryFun!pred(range.front, least.front), - "min/maxPos: predicate must be a strict partial order."); - continue; - } - if (binaryFun!pred(range.front, least.front)) - { - // change the min - least = range.save; - occurrences = 1; - } - else - ++occurrences; - } - return RetType(least.front, occurrences); - } - else static if (isAssignable!(UT, T) || (!hasElaborateAssign!UT && isAssignable!UT)) - { - UT v = UT.init; - static if (isAssignable!(UT, T)) v = range.front; - else v = cast(UT) range.front; - - for (range.popFront(); !range.empty; range.popFront()) - { - if (binaryFun!pred(*cast(T*)&v, range.front)) continue; - if (binaryFun!pred(range.front, *cast(T*)&v)) - { - // change the min - static if (isAssignable!(UT, T)) v = range.front; - else v = cast(UT) range.front; //Safe because !hasElaborateAssign!UT - occurrences = 1; - } - else - ++occurrences; - } - return RetType(*cast(T*)&v, occurrences); - } - else static if (hasLvalueElements!Range) - { - import std.algorithm.internal : addressOf; - T* p = addressOf(range.front); - for (range.popFront(); !range.empty; range.popFront()) - { - if (binaryFun!pred(*p, range.front)) continue; - if (binaryFun!pred(range.front, *p)) - { - // change the min - p = addressOf(range.front); - occurrences = 1; - } - else - ++occurrences; - } - return RetType(*p, occurrences); - } - else - static assert(false, - algoFormat("Sorry, can't find the minCount of a %s: Don't know how "~ - "to keep track of the smallest %s element.", Range.stringof, T.stringof)); -} - -/// Ditto -Tuple!(ElementType!Range, size_t) -maxCount(alias pred = "a < b", Range)(Range range) -if (isInputRange!Range && !isInfinite!Range && - is(typeof(binaryFun!pred(range.front, range.front)))) -{ - return range.minCount!((a, b) => binaryFun!pred(b, a)); -} - -/// -@safe unittest -{ - import std.conv : text; - import std.typecons : tuple; - - int[] a = [ 2, 3, 4, 1, 2, 4, 1, 1, 2 ]; - // Minimum is 1 and occurs 3 times - assert(a.minCount == tuple(1, 3)); - // Maximum is 4 and occurs 2 times - assert(a.maxCount == tuple(4, 2)); -} - -@system unittest -{ - import std.conv : text; - import std.exception : assertThrown; - import std.internal.test.dummyrange; - - int[][] b = [ [4], [2, 4], [4], [4] ]; - auto c = minCount!("a[0] < b[0]")(b); - assert(c == tuple([2, 4], 1), text(c[0])); - - //Test empty range - assertThrown(minCount(b[$..$])); - - //test with reference ranges. Test both input and forward. - assert(minCount(new ReferenceInputRange!int([1, 2, 1, 0, 2, 0])) == tuple(0, 2)); - assert(minCount(new ReferenceForwardRange!int([1, 2, 1, 0, 2, 0])) == tuple(0, 2)); -} - -@system unittest -{ - import std.conv : text; - import std.meta : AliasSeq; - - static struct R(T) //input range - { - T[] arr; - alias arr this; - } - - immutable a = [ 2, 3, 4, 1, 2, 4, 1, 1, 2 ]; - R!(immutable int) b = R!(immutable int)(a); - - assert(minCount(a) == tuple(1, 3)); - assert(minCount(b) == tuple(1, 3)); - assert(minCount!((ref immutable int a, ref immutable int b) => (a > b))(a) == tuple(4, 2)); - assert(minCount!((ref immutable int a, ref immutable int b) => (a > b))(b) == tuple(4, 2)); - - immutable(int[])[] c = [ [4], [2, 4], [4], [4] ]; - assert(minCount!("a[0] < b[0]")(c) == tuple([2, 4], 1), text(c[0])); - - static struct S1 - { - int i; - } - alias IS1 = immutable(S1); - static assert( isAssignable!S1); - static assert( isAssignable!(S1, IS1)); - - static struct S2 - { - int* p; - this(ref immutable int i) immutable {p = &i;} - this(ref int i) {p = &i;} - @property ref inout(int) i() inout {return *p;} - bool opEquals(const S2 other) const {return i == other.i;} - } - alias IS2 = immutable(S2); - static assert( isAssignable!S2); - static assert(!isAssignable!(S2, IS2)); - static assert(!hasElaborateAssign!S2); - - static struct S3 - { - int i; - void opAssign(ref S3 other) @disable; - } - static assert(!isAssignable!S3); - - static foreach (Type; AliasSeq!(S1, IS1, S2, IS2, S3)) - {{ - static if (is(Type == immutable)) alias V = immutable int; - else alias V = int; - V one = 1, two = 2; - auto r1 = [Type(two), Type(one), Type(one)]; - auto r2 = R!Type(r1); - assert(minCount!"a.i < b.i"(r1) == tuple(Type(one), 2)); - assert(minCount!"a.i < b.i"(r2) == tuple(Type(one), 2)); - assert(one == 1 && two == 2); - }} -} - -/** -Iterates the passed range and returns the minimal element. -A custom mapping function can be passed to `map`. -In other languages this is sometimes called `argmin`. - -Complexity: O(n) - Exactly `n - 1` comparisons are needed. - -Params: - map = custom accessor for the comparison key - r = range from which the minimal element will be selected - seed = custom seed to use as initial element - -Precondition: If a seed is not given, `r` must not be empty. - -Returns: The minimal element of the passed-in range. - -Note: - If at least one of the arguments is NaN, the result is an unspecified value. - - If you want to ignore NaNs, you can use $(REF filter, std,algorithm,iteration) - and $(REF isNaN, std,math) to remove them, before applying minElement. - Add a suitable seed, to avoid error messages if all elements are NaNs: - - --- - .filter!(a=>!a.isNaN).minElement(); - --- - - If you want to get NaN as a result if a NaN is present in the range, - you can use $(REF fold, std,algorithm,iteration) and $(REF isNaN, std,math): - - --- - .fold!((a,b)=>a.isNaN || b.isNaN ? real.nan : a < b ? a : b); - --- - -See_Also: - - $(LREF maxElement), $(REF min, std,algorithm,comparison), $(LREF minCount), - $(LREF minIndex), $(LREF minPos) -*/ -auto minElement(alias map = (a => a), Range)(Range r) -if (isInputRange!Range && !isInfinite!Range) -{ - return extremum!map(r); -} - -/// ditto -auto minElement(alias map = (a => a), Range, RangeElementType = ElementType!Range) - (Range r, RangeElementType seed) -if (isInputRange!Range && !isInfinite!Range && - !is(CommonType!(ElementType!Range, RangeElementType) == void)) -{ - return extremum!map(r, seed); -} - -/// -@safe pure unittest -{ - import std.range : enumerate; - import std.typecons : tuple; - - assert([2, 7, 1, 3].minElement == 1); - - // allows to get the index of an element too - assert([5, 3, 7, 9].enumerate.minElement!"a.value" == tuple(1, 3)); - - // any custom accessor can be passed - assert([[0, 4], [1, 2]].minElement!"a[1]" == [1, 2]); - - // can be seeded - int[] arr; - assert(arr.minElement(1) == 1); -} - -@safe pure unittest -{ - import std.range : enumerate, iota; - // supports mapping - assert([3, 4, 5, 1, 2].enumerate.minElement!"a.value" == tuple(3, 1)); - assert([5, 2, 4].enumerate.minElement!"a.value" == tuple(1, 2)); - - // forward ranges - assert(iota(1, 5).minElement() == 1); - assert(iota(2, 5).enumerate.minElement!"a.value" == tuple(0, 2)); - - // should work with const - const(int)[] immArr = [2, 1, 3]; - assert(immArr.minElement == 1); - - // should work with immutable - immutable(int)[] immArr2 = [2, 1, 3]; - assert(immArr2.minElement == 1); - - // with strings - assert(["b", "a", "c"].minElement == "a"); - - // with all dummy ranges - import std.internal.test.dummyrange; - foreach (DummyType; AllDummyRanges) - { - DummyType d; - assert(d.minElement == 1); - assert(d.minElement!(a => a) == 1); - assert(d.minElement!(a => -a) == 10); - } - - // with empty, but seeded ranges - int[] arr; - assert(arr.minElement(42) == 42); - assert(arr.minElement!(a => a)(42) == 42); -} - -@nogc @safe nothrow pure unittest -{ - static immutable arr = [7, 3, 4, 2, 1, 8]; - assert(arr.minElement == 1); - - static immutable arr2d = [[1, 9], [3, 1], [4, 2]]; - assert(arr2d.minElement!"a[1]" == arr2d[1]); -} - -// https://issues.dlang.org/show_bug.cgi?id=17982 -@safe unittest -{ - struct A - { - int val; - } - - const(A)[] v = [A(0)]; - assert(v.minElement!"a.val" == A(0)); -} - -// https://issues.dlang.org/show_bug.cgi?id=17982 -@safe unittest -{ - class B - { - int val; - this(int val){ this.val = val; } - } - - const(B) doStuff(const(B)[] v) - { - return v.minElement!"a.val"; - } - assert(doStuff([new B(1), new B(0), new B(2)]).val == 0); - - const(B)[] arr = [new B(0), new B(1)]; - // can't compare directly - https://issues.dlang.org/show_bug.cgi?id=1824 - assert(arr.minElement!"a.val".val == 0); -} - -/** -Iterates the passed range and returns the maximal element. -A custom mapping function can be passed to `map`. -In other languages this is sometimes called `argmax`. - -Complexity: O(n) - Exactly `n - 1` comparisons are needed. - -Params: - map = custom accessor for the comparison key - r = range from which the maximum element will be selected - seed = custom seed to use as initial element - -Precondition: If a seed is not given, `r` must not be empty. - -Returns: The maximal element of the passed-in range. - -Note: - If at least one of the arguments is NaN, the result is an unspecified value. - See $(REF minElement, std,algorithm,searching) for examples on how to cope - with NaNs. - -See_Also: - - $(LREF minElement), $(REF max, std,algorithm,comparison), $(LREF maxCount), - $(LREF maxIndex), $(LREF maxPos) -*/ -auto maxElement(alias map = (a => a), Range)(Range r) -if (isInputRange!Range && !isInfinite!Range) -{ - return extremum!(map, "a > b")(r); -} - -/// ditto -auto maxElement(alias map = (a => a), Range, RangeElementType = ElementType!Range) - (Range r, RangeElementType seed) -if (isInputRange!Range && !isInfinite!Range && - !is(CommonType!(ElementType!Range, RangeElementType) == void)) -{ - return extremum!(map, "a > b")(r, seed); -} - -/// -@safe pure unittest -{ - import std.range : enumerate; - import std.typecons : tuple; - assert([2, 1, 4, 3].maxElement == 4); - - // allows to get the index of an element too - assert([2, 1, 4, 3].enumerate.maxElement!"a.value" == tuple(2, 4)); - - // any custom accessor can be passed - assert([[0, 4], [1, 2]].maxElement!"a[1]" == [0, 4]); - - // can be seeded - int[] arr; - assert(arr.minElement(1) == 1); -} - -@safe pure unittest -{ - import std.range : enumerate, iota; - - // supports mapping - assert([3, 4, 5, 1, 2].enumerate.maxElement!"a.value" == tuple(2, 5)); - assert([5, 2, 4].enumerate.maxElement!"a.value" == tuple(0, 5)); - - // forward ranges - assert(iota(1, 5).maxElement() == 4); - assert(iota(2, 5).enumerate.maxElement!"a.value" == tuple(2, 4)); - assert(iota(4, 14).enumerate.maxElement!"a.value" == tuple(9, 13)); - - // should work with const - const(int)[] immArr = [2, 3, 1]; - assert(immArr.maxElement == 3); - - // should work with immutable - immutable(int)[] immArr2 = [2, 3, 1]; - assert(immArr2.maxElement == 3); - - // with strings - assert(["a", "c", "b"].maxElement == "c"); - - // with all dummy ranges - import std.internal.test.dummyrange; - foreach (DummyType; AllDummyRanges) - { - DummyType d; - assert(d.maxElement == 10); - assert(d.maxElement!(a => a) == 10); - assert(d.maxElement!(a => -a) == 1); - } - - // with empty, but seeded ranges - int[] arr; - assert(arr.maxElement(42) == 42); - assert(arr.maxElement!(a => a)(42) == 42); - -} - -@nogc @safe nothrow pure unittest -{ - static immutable arr = [7, 3, 8, 2, 1, 4]; - assert(arr.maxElement == 8); - - static immutable arr2d = [[1, 3], [3, 9], [4, 2]]; - assert(arr2d.maxElement!"a[1]" == arr2d[1]); -} - -// https://issues.dlang.org/show_bug.cgi?id=17982 -@safe unittest -{ - class B - { - int val; - this(int val){ this.val = val; } - } - - const(B) doStuff(const(B)[] v) - { - return v.maxElement!"a.val"; - } - assert(doStuff([new B(1), new B(0), new B(2)]).val == 2); - - const(B)[] arr = [new B(0), new B(1)]; - // can't compare directly - https://issues.dlang.org/show_bug.cgi?id=1824 - assert(arr.maxElement!"a.val".val == 1); -} - -// https://issues.dlang.org/show_bug.cgi?id=23993 -@safe unittest -{ - import std.bigint : BigInt; - - assert([BigInt(2), BigInt(3)].maxElement == BigInt(3)); -} - -// https://issues.dlang.org/show_bug.cgi?id=24596 -@safe unittest -{ - static class A { - int i; - int getI() @safe => i; - this(int i) @safe { this.i = i; } - } - auto arr = [new A(2), new A(3)]; - - arr.maxElement!(a => a.getI); - - assert(arr[0].getI == 2); -} - -// minPos -/** -Computes a subrange of `range` starting at the first occurrence of `range`'s -minimum (respectively maximum) and with the same ending as `range`, or the -empty range if `range` itself is empty. - -Formally, the minimum is a value `x` in `range` such that `pred(a, x)` is -`false` for all values `a` in `range`. Conversely, the maximum is a value `x` in -`range` such that `pred(x, a)` is `false` for all values `a` in `range` (note -the swapped arguments to `pred`). - -These functions may be used for computing arbitrary extrema by choosing `pred` -appropriately. For corrrect functioning, `pred` must be a strict partial order, -i.e. transitive (if `pred(a, b) && pred(b, c)` then `pred(a, c)`) and -irreflexive (`pred(a, a)` is `false`). - -Params: - pred = The ordering predicate to use to determine the extremum (minimum or - maximum) element. - range = The $(REF_ALTTEXT forward range, isForwardRange, std,range,primitives) to search. - -Returns: The position of the minimum (respectively maximum) element of forward -range `range`, i.e. a subrange of `range` starting at the position of its -smallest (respectively largest) element and with the same ending as `range`. - -Limitations: If at least one of the arguments is NaN, the result is -an unspecified value. See $(REF maxElement, std,algorithm,searching) -for examples on how to cope with NaNs. - -See_Also: - $(REF max, std,algorithm,comparison), $(LREF minCount), $(LREF minIndex), $(LREF minElement) -*/ -Range minPos(alias pred = "a < b", Range)(Range range) -if (isForwardRange!Range && !isInfinite!Range && - is(typeof(binaryFun!pred(range.front, range.front)))) -{ - static if (hasSlicing!Range && isRandomAccessRange!Range && hasLength!Range) - { - // Prefer index-based access - size_t pos = 0; - foreach (i; 1 .. range.length) - { - if (binaryFun!pred(range[i], range[pos])) - { - pos = i; - } - } - return range[pos .. range.length]; - } - else - { - auto result = range.save; - if (range.empty) return result; - for (range.popFront(); !range.empty; range.popFront()) - { - // Note: Unlike minCount, we do not care to find equivalence, so a - // single pred call is enough. - if (binaryFun!pred(range.front, result.front)) - { - // change the min - result = range.save; - } - } - return result; - } -} - -/// Ditto -Range maxPos(alias pred = "a < b", Range)(Range range) -if (isForwardRange!Range && !isInfinite!Range && - is(typeof(binaryFun!pred(range.front, range.front)))) -{ - return range.minPos!((a, b) => binaryFun!pred(b, a)); -} - -/// -@safe unittest -{ - int[] a = [ 2, 3, 4, 1, 2, 4, 1, 1, 2 ]; - // Minimum is 1 and first occurs in position 3 - assert(a.minPos == [ 1, 2, 4, 1, 1, 2 ]); - // Maximum is 4 and first occurs in position 2 - assert(a.maxPos == [ 4, 1, 2, 4, 1, 1, 2 ]); -} - -@safe unittest -{ - import std.algorithm.comparison : equal; - import std.internal.test.dummyrange; - - int[] a = [ 2, 3, 4, 1, 2, 4, 1, 1, 2 ]; - //Test that an empty range works - int[] b = a[$..$]; - assert(equal(minPos(b), b)); - - //test with reference range. - assert( equal( minPos(new ReferenceForwardRange!int([1, 2, 1, 0, 2, 0])), [0, 2, 0] ) ); -} - -@system unittest -{ - //Rvalue range - import std.algorithm.comparison : equal; - import std.container : Array; - - assert(Array!int(2, 3, 4, 1, 2, 4, 1, 1, 2) - [] - .minPos() - .equal([ 1, 2, 4, 1, 1, 2 ])); -} - -@safe unittest -{ - //BUG 9299 - immutable a = [ 2, 3, 4, 1, 2, 4, 1, 1, 2 ]; - // Minimum is 1 and first occurs in position 3 - assert(minPos(a) == [ 1, 2, 4, 1, 1, 2 ]); - // Maximum is 4 and first occurs in position 5 - assert(minPos!("a > b")(a) == [ 4, 1, 2, 4, 1, 1, 2 ]); - - immutable(int[])[] b = [ [4], [2, 4], [4], [4] ]; - assert(minPos!("a[0] < b[0]")(b) == [ [2, 4], [4], [4] ]); -} - -/** -Computes the index of the first occurrence of `range`'s minimum element. - -Params: - pred = The ordering predicate to use to determine the minimum element. - range = The $(REF_ALTTEXT input range, isInputRange, std,range,primitives) - to search. - -Complexity: $(BIGOH range.length) - Exactly `range.length - 1` comparisons are needed. - -Returns: - The index of the first encounter of the minimum element in `range`. If the - `range` is empty, -1 is returned. - -Limitations: - If at least one of the arguments is NaN, the result is - an unspecified value. See $(REF maxElement, std,algorithm,searching) - for examples on how to cope with NaNs. - -See_Also: - $(LREF maxIndex), $(REF min, std,algorithm,comparison), $(LREF minCount), $(LREF minElement), $(LREF minPos) - */ -ptrdiff_t minIndex(alias pred = "a < b", Range)(Range range) -if (isInputRange!Range && !isInfinite!Range && - is(typeof(binaryFun!pred(range.front, range.front)))) -{ - if (range.empty) return -1; - - ptrdiff_t minPos = 0; - - static if (isRandomAccessRange!Range && hasLength!Range) - { - foreach (i; 1 .. range.length) - { - if (binaryFun!pred(range[i], range[minPos])) - { - minPos = i; - } - } - } - else - { - ptrdiff_t curPos = 0; - Unqual!(typeof(range.front)) min = range.front; - for (range.popFront(); !range.empty; range.popFront()) - { - ++curPos; - if (binaryFun!pred(range.front, min)) - { - min = range.front; - minPos = curPos; - } - } - } - return minPos; -} - -/// -@safe pure nothrow unittest -{ - int[] a = [2, 3, 4, 1, 2, 4, 1, 1, 2]; - - // Minimum is 1 and first occurs in position 3 - assert(a.minIndex == 3); - // Get maximum index with minIndex - assert(a.minIndex!"a > b" == 2); - - // Range is empty, so return value is -1 - int[] b; - assert(b.minIndex == -1); - - // Works with more custom types - struct Dog { int age; } - Dog[] dogs = [Dog(10), Dog(5), Dog(15)]; - assert(dogs.minIndex!"a.age < b.age" == 1); -} - -@safe pure unittest -{ - // should work with const - const(int)[] immArr = [2, 1, 3]; - assert(immArr.minIndex == 1); - - // Works for const ranges too - const int[] c = [2, 5, 4, 1, 2, 3]; - assert(c.minIndex == 3); - - // should work with immutable - immutable(int)[] immArr2 = [2, 1, 3]; - assert(immArr2.minIndex == 1); - - // with strings - assert(["b", "a", "c"].minIndex == 1); - - // infinite range - import std.range : cycle; - static assert(!__traits(compiles, cycle([1]).minIndex)); - - // with all dummy ranges - import std.internal.test.dummyrange : AllDummyRanges; - foreach (DummyType; AllDummyRanges) - { - static if (isForwardRange!DummyType && !isInfinite!DummyType) - { - DummyType d; - d.arr = [5, 3, 7, 2, 1, 4]; - assert(d.minIndex == 4); - - d.arr = []; - assert(d.minIndex == -1); - } - } -} - -@nogc @safe nothrow pure unittest -{ - static immutable arr = [7, 3, 8, 2, 1, 4]; - assert(arr.minIndex == 4); - - static immutable arr2d = [[1, 3], [3, 9], [4, 2]]; - assert(arr2d.minIndex!"a[1] < b[1]" == 2); -} - -@safe nothrow pure unittest -{ - // InputRange test - - static struct InRange - { - @property int front() - { - return arr[index]; - } - - bool empty() const - { - return arr.length == index; - } - - void popFront() - { - index++; - } - - int[] arr; - size_t index = 0; - } - - static assert(isInputRange!InRange); - - auto arr1 = InRange([5, 2, 3, 4, 5, 3, 6]); - auto arr2 = InRange([7, 3, 8, 2, 1, 4]); - - assert(arr1.minIndex == 1); - assert(arr2.minIndex == 4); -} - -/** -Computes the index of the first occurrence of `range`'s maximum element. - -Complexity: $(BIGOH range) - Exactly `range.length - 1` comparisons are needed. - -Params: - pred = The ordering predicate to use to determine the maximum element. - range = The $(REF_ALTTEXT input range, isInputRange, std,range,primitives) to search. - -Returns: - The index of the first encounter of the maximum in `range`. If the - `range` is empty, -1 is returned. - -Limitations: - If at least one of the arguments is NaN, the result is - an unspecified value. See $(REF maxElement, std,algorithm,searching) - for examples on how to cope with NaNs. - -See_Also: - $(LREF minIndex), $(REF max, std,algorithm,comparison), $(LREF maxCount), $(LREF maxElement), $(LREF maxPos) - */ -ptrdiff_t maxIndex(alias pred = "a < b", Range)(Range range) -if (isInputRange!Range && !isInfinite!Range && - is(typeof(binaryFun!pred(range.front, range.front)))) -{ - return range.minIndex!((a, b) => binaryFun!pred(b, a)); -} - -/// -@safe pure nothrow unittest -{ - // Maximum is 4 and first occurs in position 2 - int[] a = [2, 3, 4, 1, 2, 4, 1, 1, 2]; - assert(a.maxIndex == 2); - - // Empty range - int[] b; - assert(b.maxIndex == -1); - - // Works with more custom types - struct Dog { int age; } - Dog[] dogs = [Dog(10), Dog(15), Dog(5)]; - assert(dogs.maxIndex!"a.age < b.age" == 1); -} - -@safe pure unittest -{ - // should work with const - const(int)[] immArr = [5, 1, 3]; - assert(immArr.maxIndex == 0); - - // Works for const ranges too - const int[] c = [2, 5, 4, 1, 2, 3]; - assert(c.maxIndex == 1); - - - // should work with immutable - immutable(int)[] immArr2 = [2, 1, 3]; - assert(immArr2.maxIndex == 2); - - // with strings - assert(["b", "a", "c"].maxIndex == 2); - - // infinite range - import std.range : cycle; - static assert(!__traits(compiles, cycle([1]).maxIndex)); - - // with all dummy ranges - import std.internal.test.dummyrange : AllDummyRanges; - foreach (DummyType; AllDummyRanges) - { - static if (isForwardRange!DummyType && !isInfinite!DummyType) - { - DummyType d; - - d.arr = [5, 3, 7, 2, 1, 4]; - assert(d.maxIndex == 2); - - d.arr = []; - assert(d.maxIndex == -1); - } - } -} - -@nogc @safe nothrow pure unittest -{ - static immutable arr = [7, 3, 8, 2, 1, 4]; - assert(arr.maxIndex == 2); - - static immutable arr2d = [[1, 3], [3, 9], [4, 2]]; - assert(arr2d.maxIndex!"a[1] < b[1]" == 1); -} - -/** -Skip over the initial portion of the first given range (`haystack`) that matches -any of the additionally given ranges (`needles`) fully, or -if no second range is given skip over the elements that fulfill pred. -Do nothing if there is no match. - -Params: - pred = The predicate that determines whether elements from each respective - range match. Defaults to equality `"a == b"`. -*/ -template skipOver(alias pred = (a, b) => a == b) -{ - enum bool isPredComparable(T) = ifTestable!(T, binaryFun!pred); - - /** - Params: - haystack = The $(REF_ALTTEXT forward range, isForwardRange, std,range,primitives) to - move forward. - needles = The $(REF_ALTTEXT input ranges, isInputRange, std,range,primitives) - representing the prefix of `r1` to skip over. - es = The element to match. - - Returns: - `true` if the prefix of `haystack` matches any range of `needles` fully - or `pred` evaluates to true, and `haystack` has been advanced to the point past this segment; - otherwise false, and `haystack` is left in its original position. - - Note: - By definition, empty ranges are matched fully and if `needles` contains an empty range, - `skipOver` will return `true`. - */ - bool skipOver(Haystack, Needles...)(ref Haystack haystack, Needles needles) - if (is(typeof(binaryFun!pred(haystack.front, needles[0].front))) && - isForwardRange!Haystack && - allSatisfy!(isInputRange, Needles) && - !is(CommonType!(staticMap!(ElementType, staticMap!(Unqual, Needles))) == void)) - { - static if (__traits(isSame, pred, (a, b) => a == b) - && is(typeof(haystack[0 .. $] == needles[0]) : bool) - && is(typeof(haystack = haystack[0 .. $])) - && hasLength!Haystack && allSatisfy!(hasLength, Needles)) - { - ptrdiff_t longestMatch = -1; - static foreach (r2; needles) - { - if (r2.length <= haystack.length && longestMatch < ptrdiff_t(r2.length) - && (haystack[0 .. r2.length] == r2 || r2.length == 0)) - longestMatch = r2.length; - } - if (longestMatch >= 0) - { - if (longestMatch > 0) - haystack = haystack[longestMatch .. $]; - - return true; - } - return false; - } - else - { - import std.algorithm.comparison : min; - auto r = haystack.save; - - static if (hasLength!Haystack && allSatisfy!(hasLength, Needles)) - { - import std.algorithm.iteration : map; - import std.algorithm.searching : minElement; - import std.range : only; - // Shortcut opportunity! - if (needles.only.map!(a => a.length).minElement > haystack.length) - return false; - } - - // compatibility: return true if any range was empty - bool hasEmptyRanges; - static foreach (i, r2; needles) - { - if (r2.empty) - hasEmptyRanges = true; - } - - bool hasNeedleMatch; - size_t inactiveNeedlesLen; - bool[Needles.length] inactiveNeedles; - for (; !r.empty; r.popFront) - { - static foreach (i, r2; needles) - { - if (!r2.empty && !inactiveNeedles[i]) - { - if (binaryFun!pred(r.front, r2.front)) - { - r2.popFront; - if (r2.empty) - { - // we skipped over a new match - hasNeedleMatch = true; - inactiveNeedlesLen++; - // skip over haystack - haystack = r; - } - } - else - { - inactiveNeedles[i] = true; - inactiveNeedlesLen++; - } - } - } - - // are we done? - if (inactiveNeedlesLen == needles.length) - break; - } - - if (hasNeedleMatch) - haystack.popFront; - - return hasNeedleMatch || hasEmptyRanges; - } - } - - /// Ditto - bool skipOver(R)(ref R r1) - if (isForwardRange!R && - ifTestable!(typeof(r1.front), unaryFun!pred)) - { - if (r1.empty || !unaryFun!pred(r1.front)) - return false; - - do - r1.popFront(); - while (!r1.empty && unaryFun!pred(r1.front)); - return true; - } - - /// Ditto - bool skipOver(R, Es...)(ref R r, Es es) - if (isInputRange!R && is(typeof(binaryFun!pred(r.front, es[0])))) - { - if (r.empty) - return false; - - static foreach (e; es) - { - if (binaryFun!pred(r.front, e)) - { - r.popFront(); - return true; - } - } - return false; - } -} - -/// -@safe unittest -{ - import std.algorithm.comparison : equal; - - auto s1 = "Hello world"; - assert(!skipOver(s1, "Ha")); - assert(s1 == "Hello world"); - assert(skipOver(s1, "Hell") && s1 == "o world", s1); - - string[] r1 = ["abc", "def", "hij"]; - dstring[] r2 = ["abc"d]; - assert(!skipOver!((a, b) => a.equal(b))(r1, ["def"d]), r1[0]); - assert(r1 == ["abc", "def", "hij"]); - assert(skipOver!((a, b) => a.equal(b))(r1, r2)); - assert(r1 == ["def", "hij"]); -} - -/// -@safe unittest -{ - import std.ascii : isWhite; - import std.range.primitives : empty; - - auto s2 = "\t\tvalue"; - auto s3 = ""; - auto s4 = "\t\t\t"; - assert(s2.skipOver!isWhite && s2 == "value"); - assert(!s3.skipOver!isWhite); - assert(s4.skipOver!isWhite && s3.empty); -} - -/// Variadic skipOver -@safe unittest -{ - auto s = "Hello world"; - assert(!skipOver(s, "hello", "HellO")); - assert(s == "Hello world"); - - // the range is skipped over the longest matching needle is skipped - assert(skipOver(s, "foo", "hell", "Hello ")); - assert(s == "world"); -} - -/// -@safe unittest -{ - import std.algorithm.comparison : equal; - - auto s1 = "Hello world"; - assert(!skipOver(s1, 'a')); - assert(s1 == "Hello world"); - assert(skipOver(s1, 'H') && s1 == "ello world"); - - string[] r = ["abc", "def", "hij"]; - dstring e = "abc"d; - assert(!skipOver!((a, b) => a.equal(b))(r, "def"d)); - assert(r == ["abc", "def", "hij"]); - assert(skipOver!((a, b) => a.equal(b))(r, e)); - assert(r == ["def", "hij"]); - - auto s2 = ""; - assert(!s2.skipOver('a')); -} - -/// Partial instantiation -@safe unittest -{ - import std.ascii : isWhite; - import std.range.primitives : empty; - - alias whitespaceSkiper = skipOver!isWhite; - - auto s2 = "\t\tvalue"; - auto s3 = ""; - auto s4 = "\t\t\t"; - assert(whitespaceSkiper(s2) && s2 == "value"); - assert(!whitespaceSkiper(s2)); - assert(whitespaceSkiper(s4) && s3.empty); -} - -// variadic skipOver -@safe unittest -{ - auto s = "DLang.rocks"; - assert(!s.skipOver("dlang", "DLF", "DLang ")); - assert(s == "DLang.rocks"); - - assert(s.skipOver("dlang", "DLANG", "DLF", "D", "DL", "DLanpp")); - assert(s == "ang.rocks"); - s = "DLang.rocks"; - - assert(s.skipOver("DLang", "DLANG", "DLF", "D", "DL", "DLang ")); - assert(s == ".rocks"); - s = "DLang.rocks"; - - assert(s.skipOver("dlang", "DLANG", "DLF", "D", "DL", "DLang.")); - assert(s == "rocks"); -} - -// variadic with custom pred -@safe unittest -{ - import std.ascii : toLower; - - auto s = "DLang.rocks"; - assert(!s.skipOver("dlang", "DLF", "DLang ")); - assert(s == "DLang.rocks"); - - assert(s.skipOver!((a, b) => a.toLower == b.toLower)("dlang", "DLF", "DLang ")); - assert(s == ".rocks"); -} - -// variadic skipOver with mixed needles -@safe unittest -{ - auto s = "DLang.rocks"; - assert(!s.skipOver("dlang"d, "DLF", "DLang "w)); - assert(s == "DLang.rocks"); - - assert(s.skipOver("dlang", "DLANG"d, "DLF"w, "D"d, "DL", "DLanp")); - assert(s == "ang.rocks"); - s = "DLang.rocks"; - - assert(s.skipOver("DLang", "DLANG"w, "DLF"d, "D"d, "DL", "DLang ")); - assert(s == ".rocks"); - s = "DLang.rocks"; - - assert(s.skipOver("dlang", "DLANG"w, "DLF", "D"d, "DL"w, "DLang."d)); - assert(s == "rocks"); - - import std.algorithm.iteration : filter; - s = "DLang.rocks"; - assert(s.skipOver("dlang", "DLang".filter!(a => true))); - assert(s == ".rocks"); -} - -// variadic skipOver with auto-decoding -@safe unittest -{ - auto s = "☢☣☠.☺"; - assert(s.skipOver("a", "☢", "☢☣☠")); - assert(s == ".☺"); -} - -// skipOver with @nogc -@safe @nogc pure nothrow unittest -{ - static immutable s = [0, 1, 2]; - immutable(int)[] s2 = s[]; - - static immutable skip1 = [0, 2]; - static immutable skip2 = [0, 1]; - assert(s2.skipOver(skip1, skip2)); - assert(s2 == s[2 .. $]); -} - -// variadic skipOver with single elements -@safe unittest -{ - auto s = "DLang.rocks"; - assert(!s.skipOver('a', 'd', 'e')); - assert(s == "DLang.rocks"); - - assert(s.skipOver('a', 'D', 'd', 'D')); - assert(s == "Lang.rocks"); - s = "DLang.rocks"; - - assert(s.skipOver(wchar('a'), dchar('D'), 'd')); - assert(s == "Lang.rocks"); - - dstring dstr = "+Foo"; - assert(!dstr.skipOver('.', '-')); - assert(dstr == "+Foo"); - - assert(dstr.skipOver('+', '-')); - assert(dstr == "Foo"); -} - -// skipOver with empty ranges must return true (compatibility) -@safe unittest -{ - auto s = "DLang.rocks"; - assert(s.skipOver("")); - assert(s.skipOver("", "")); - assert(s.skipOver("", "foo")); - - auto s2 = "DLang.rocks"d; - assert(s2.skipOver("")); - assert(s2.skipOver("", "")); - assert(s2.skipOver("", "foo")); -} - -// dxml regression -@safe unittest -{ - import std.utf : byCodeUnit; - import std.algorithm.comparison : equal; - - bool stripStartsWith(Text)(ref Text text, string needle) - { - return text.skipOver(needle.byCodeUnit()); - } - auto text = ""d.byCodeUnit; - assert(stripStartsWith(text, "")); - assert(text.equal("")); -} - -/** -Checks whether the given -$(REF_ALTTEXT input range, isInputRange, std,range,primitives) starts with (one -of) the given needle(s) or, if no needles are given, -if its front element fulfils predicate `pred`. - -For more information about `pred` see $(LREF find). - -Params: - - pred = Predicate to use in comparing the elements of the haystack and the - needle(s). Mandatory if no needles are given. - - doesThisStart = The input range to check. - - withOneOfThese = The needles against which the range is to be checked, - which may be individual elements or input ranges of elements. - - withThis = The single needle to check, which may be either a single element - or an input range of elements. - -Returns: - -0 if the needle(s) do not occur at the beginning of the given range; -otherwise the position of the matching needle, that is, 1 if the range starts -with `withOneOfThese[0]`, 2 if it starts with `withOneOfThese[1]`, and so -on. - -In the case where `doesThisStart` starts with multiple of the ranges or -elements in `withOneOfThese`, then the shortest one matches (if there are -two which match which are of the same length (e.g. `"a"` and `'a'`), then -the left-most of them in the argument -list matches). - -In the case when no needle parameters are given, return `true` iff front of -`doesThisStart` fulfils predicate `pred`. - */ -uint startsWith(alias pred = (a, b) => a == b, Range, Needles...)(Range doesThisStart, Needles withOneOfThese) -if (isInputRange!Range && Needles.length > 1 && - allSatisfy!(canTestStartsWith!(pred, Range), Needles)) -{ - template checkType(T) - { - enum checkType = is(immutable ElementEncodingType!Range == immutable T); - } - - // auto-decoding special case - static if (__traits(isSame, binaryFun!pred, (a, b) => a == b) && - isNarrowString!Range && allSatisfy!(checkType, Needles)) - { - import std.utf : byCodeUnit; - auto haystack = doesThisStart.byCodeUnit; - } - else - { - alias haystack = doesThisStart; - } - alias needles = withOneOfThese; - - // Make one pass looking for empty ranges in needles - foreach (i, Unused; Needles) - { - // Empty range matches everything - static if (!is(typeof(binaryFun!pred(haystack.front, needles[i])) : bool)) - { - if (needles[i].empty) return i + 1; - } - } - - for (; !haystack.empty; haystack.popFront()) - { - foreach (i, Unused; Needles) - { - static if (is(typeof(binaryFun!pred(haystack.front, needles[i])) : bool)) - { - // Single-element - if (binaryFun!pred(haystack.front, needles[i])) - { - // found, but instead of returning, we just stop searching. - // This is to account for one-element - // range matches (consider startsWith("ab", "a", - // 'a') should return 1, not 2). - break; - } - } - else - { - if (binaryFun!pred(haystack.front, needles[i].front)) - { - continue; - } - } - - // This code executed on failure to match - // Out with this guy, check for the others - uint result = startsWith!pred(haystack, needles[0 .. i], needles[i + 1 .. $]); - if (result > i) ++result; - return result; - } - - // If execution reaches this point, then the front matches for all - // needle ranges, or a needle element has been matched. - // What we need to do now is iterate, lopping off the front of - // the range and checking if the result is empty, or finding an - // element needle and returning. - // If neither happens, we drop to the end and loop. - foreach (i, Unused; Needles) - { - static if (is(typeof(binaryFun!pred(haystack.front, needles[i])) : bool)) - { - // Test has passed in the previous loop - return i + 1; - } - else - { - needles[i].popFront(); - if (needles[i].empty) return i + 1; - } - } - } - return 0; -} - -/// Ditto -bool startsWith(alias pred = "a == b", R1, R2)(R1 doesThisStart, R2 withThis) -if (isInputRange!R1 && - isInputRange!R2 && - is(typeof(binaryFun!pred(doesThisStart.front, withThis.front)) : bool)) -{ - alias haystack = doesThisStart; - alias needle = withThis; - - static if (is(typeof(pred) : string)) - enum isDefaultPred = pred == "a == b"; - else - enum isDefaultPred = false; - - // Note: Although narrow strings don't have a "true" length, for a narrow string to start with another - // narrow string, it must have *at least* as many code units. - static if ((hasLength!R1 && hasLength!R2) || - ((hasLength!R1 || isNarrowString!R1) && (hasLength!R2 || isNarrowString!R2) - && (ElementEncodingType!R1.sizeof <= ElementEncodingType!R2.sizeof))) - { - if (haystack.length < needle.length) - return false; - } - - static if (isDefaultPred && isArray!R1 && isArray!R2 && - is(immutable ElementEncodingType!R1 == immutable ElementEncodingType!R2)) - { - //Array slice comparison mode - return haystack[0 .. needle.length] == needle; - } - else static if (isRandomAccessRange!R1 && isRandomAccessRange!R2 && hasLength!R2) - { - //RA dual indexing mode - foreach (j; 0 .. needle.length) - { - if (!binaryFun!pred(haystack[j], needle[j])) - // not found - return false; - } - // found! - return true; - } - else - { - //Standard input range mode - if (needle.empty) return true; - static if (hasLength!R1 && hasLength!R2) - { - //We have previously checked that haystack.length > needle.length, - //So no need to check haystack.empty during iteration - for ( ; ; haystack.popFront() ) - { - if (!binaryFun!pred(haystack.front, needle.front)) break; - needle.popFront(); - if (needle.empty) return true; - } - } - else - { - for ( ; !haystack.empty ; haystack.popFront() ) - { - if (!binaryFun!pred(haystack.front, needle.front)) break; - needle.popFront(); - if (needle.empty) return true; - } - } - return false; - } -} - -/// Ditto -bool startsWith(alias pred = "a == b", R, E)(R doesThisStart, E withThis) -if (isInputRange!R && - is(typeof(binaryFun!pred(doesThisStart.front, withThis)) : bool)) -{ - if (doesThisStart.empty) - return false; - - static if (is(typeof(pred) : string)) - enum isDefaultPred = pred == "a == b"; - else - enum isDefaultPred = false; - - alias predFunc = binaryFun!pred; - - // auto-decoding special case - static if (isNarrowString!R) - { - // statically determine decoding is unnecessary to evaluate pred - static if (isDefaultPred && isSomeChar!E && E.sizeof <= ElementEncodingType!R.sizeof) - return doesThisStart[0] == withThis; - // specialize for ASCII as to not change previous behavior - else - { - if (withThis <= 0x7F) - return predFunc(doesThisStart[0], withThis); - else - return predFunc(doesThisStart.front, withThis); - } - } - else - { - return predFunc(doesThisStart.front, withThis); - } -} - -/// Ditto -bool startsWith(alias pred, R)(R doesThisStart) -if (isInputRange!R && - ifTestable!(typeof(doesThisStart.front), unaryFun!pred)) -{ - return !doesThisStart.empty && unaryFun!pred(doesThisStart.front); -} - -/// -@safe unittest -{ - import std.ascii : isAlpha; - - assert("abc".startsWith!(a => a.isAlpha)); - assert("abc".startsWith!isAlpha); - assert(!"1ab".startsWith!(a => a.isAlpha)); - assert(!"".startsWith!(a => a.isAlpha)); - - import std.algorithm.comparison : among; - assert("abc".startsWith!(a => a.among('a', 'b') != 0)); - assert(!"abc".startsWith!(a => a.among('b', 'c') != 0)); - - assert(startsWith("abc", "")); - assert(startsWith("abc", "a")); - assert(!startsWith("abc", "b")); - assert(startsWith("abc", 'a', "b") == 1); - assert(startsWith("abc", "b", "a") == 2); - assert(startsWith("abc", "a", "a") == 1); - assert(startsWith("abc", "ab", "a") == 2); - assert(startsWith("abc", "x", "a", "b") == 2); - assert(startsWith("abc", "x", "aa", "ab") == 3); - assert(startsWith("abc", "x", "aaa", "sab") == 0); - assert(startsWith("abc", "x", "aaa", "a", "sab") == 3); - - import std.typecons : Tuple; - alias C = Tuple!(int, "x", int, "y"); - assert(startsWith!"a.x == b"([ C(1,1), C(1,2), C(2,2) ], [1, 1])); - assert(startsWith!"a.x == b"([ C(1,1), C(2,1), C(2,2) ], [1, 1], [1, 2], [1, 3]) == 2); -} - -@safe unittest -{ - import std.algorithm.iteration : filter; - import std.conv : to; - import std.meta : AliasSeq; - import std.range; - - static foreach (S; AliasSeq!(char[], wchar[], dchar[], string, wstring, dstring)) - (){ // workaround slow optimizations for large functions - // https://issues.dlang.org/show_bug.cgi?id=2396 - assert(!startsWith(to!S("abc"), 'c')); - assert(startsWith(to!S("abc"), 'a', 'c') == 1); - assert(!startsWith(to!S("abc"), 'x', 'n', 'b')); - assert(startsWith(to!S("abc"), 'x', 'n', 'a') == 3); - assert(startsWith(to!S("\uFF28abc"), 'a', '\uFF28', 'c') == 2); - - static foreach (T; AliasSeq!(char[], wchar[], dchar[], string, wstring, dstring)) - { - //Lots of strings - assert(startsWith(to!S("abc"), to!T(""))); - assert(startsWith(to!S("ab"), to!T("a"))); - assert(startsWith(to!S("abc"), to!T("a"))); - assert(!startsWith(to!S("abc"), to!T("b"))); - assert(!startsWith(to!S("abc"), to!T("b"), "bc", "abcd", "xyz")); - assert(startsWith(to!S("abc"), to!T("ab"), 'a') == 2); - assert(startsWith(to!S("abc"), to!T("a"), "b") == 1); - assert(startsWith(to!S("abc"), to!T("b"), "a") == 2); - assert(startsWith(to!S("abc"), to!T("a"), 'a') == 1); - assert(startsWith(to!S("abc"), 'a', to!T("a")) == 1); - assert(startsWith(to!S("abc"), to!T("x"), "a", "b") == 2); - assert(startsWith(to!S("abc"), to!T("x"), "aa", "ab") == 3); - assert(startsWith(to!S("abc"), to!T("x"), "aaa", "sab") == 0); - assert(startsWith(to!S("abc"), 'a')); - assert(!startsWith(to!S("abc"), to!T("sab"))); - assert(startsWith(to!S("abc"), 'x', to!T("aaa"), 'a', "sab") == 3); - - //Unicode - assert(startsWith(to!S("\uFF28el\uFF4co"), to!T("\uFF28el"))); - assert(startsWith(to!S("\uFF28el\uFF4co"), to!T("Hel"), to!T("\uFF28el")) == 2); - assert(startsWith(to!S("日本語"), to!T("日本"))); - assert(startsWith(to!S("日本語"), to!T("日本語"))); - assert(!startsWith(to!S("日本"), to!T("日本語"))); - - //Empty - assert(startsWith(to!S(""), T.init)); - assert(!startsWith(to!S(""), 'a')); - assert(startsWith(to!S("a"), T.init)); - assert(startsWith(to!S("a"), T.init, "") == 1); - assert(startsWith(to!S("a"), T.init, 'a') == 1); - assert(startsWith(to!S("a"), 'a', T.init) == 2); - } - }(); - - //Length but no RA - assert(!startsWith("abc".takeExactly(3), "abcd".takeExactly(4))); - assert(startsWith("abc".takeExactly(3), "abcd".takeExactly(3))); - assert(startsWith("abc".takeExactly(3), "abcd".takeExactly(1))); - - static foreach (T; AliasSeq!(int, short)) - {{ - immutable arr = cast(T[])[0, 1, 2, 3, 4, 5]; - - //RA range - assert(startsWith(arr, cast(int[]) null)); - assert(!startsWith(arr, 5)); - assert(!startsWith(arr, 1)); - assert(startsWith(arr, 0)); - assert(startsWith(arr, 5, 0, 1) == 2); - assert(startsWith(arr, [0])); - assert(startsWith(arr, [0, 1])); - assert(startsWith(arr, [0, 1], 7) == 1); - assert(!startsWith(arr, [0, 1, 7])); - assert(startsWith(arr, [0, 1, 7], [0, 1, 2]) == 2); - - //Normal input range - assert(!startsWith(filter!"true"(arr), 1)); - assert(startsWith(filter!"true"(arr), 0)); - assert(startsWith(filter!"true"(arr), [0])); - assert(startsWith(filter!"true"(arr), [0, 1])); - assert(startsWith(filter!"true"(arr), [0, 1], 7) == 1); - assert(!startsWith(filter!"true"(arr), [0, 1, 7])); - assert(startsWith(filter!"true"(arr), [0, 1, 7], [0, 1, 2]) == 2); - assert(startsWith(arr, filter!"true"([0, 1]))); - assert(startsWith(arr, filter!"true"([0, 1]), 7) == 1); - assert(!startsWith(arr, filter!"true"([0, 1, 7]))); - assert(startsWith(arr, [0, 1, 7], filter!"true"([0, 1, 2])) == 2); - - //Non-default pred - assert(startsWith!("a%10 == b%10")(arr, [10, 11])); - assert(!startsWith!("a%10 == b%10")(arr, [10, 12])); - }} -} - -private template canTestStartsWith(alias pred, Haystack) -{ - enum bool canTestStartsWith(Needle) = is(typeof( - (ref Haystack h, ref Needle n) => startsWith!pred(h, n))); -} - -/* (Not yet documented.) -Consume all elements from `r` that are equal to one of the elements -`es`. - */ -private void skipAll(alias pred = "a == b", R, Es...)(ref R r, Es es) -//if (is(typeof(binaryFun!pred(r1.front, es[0])))) -{ - loop: - for (; !r.empty; r.popFront()) - { - foreach (i, E; Es) - { - if (binaryFun!pred(r.front, es[i])) - { - continue loop; - } - } - break; - } -} - -@safe unittest -{ - auto s1 = "Hello world"; - skipAll(s1, 'H', 'e'); - assert(s1 == "llo world"); -} - -/** -Interval option specifier for `until` (below) and others. - -If set to `OpenRight.yes`, then the interval is open to the right -(last element is not included). - -Otherwise if set to `OpenRight.no`, then the interval is closed to the right -including the entire sentinel. - */ -alias OpenRight = Flag!"openRight"; - -/** -Lazily iterates `range` _until the element `e` for which -`pred(e, sentinel)` is true. - -This is similar to `takeWhile` in other languages. - -Params: - pred = Predicate to determine when to stop. - range = The $(REF_ALTTEXT input range, isInputRange, std,range,primitives) - to iterate over. - sentinel = The element to stop at. - openRight = Determines whether the element for which the given predicate is - true should be included in the resulting range (`No.openRight`), or - not (`Yes.openRight`). - -Returns: - An $(REF_ALTTEXT input range, isInputRange, std,range,primitives) that - iterates over the original range's elements, but ends when the specified - predicate becomes true. If the original range is a - $(REF_ALTTEXT forward range, isForwardRange, std,range,primitives) or - higher, this range will be a forward range. - */ -Until!(pred, Range, Sentinel) -until(alias pred = "a == b", Range, Sentinel) -(Range range, Sentinel sentinel, OpenRight openRight = Yes.openRight) -if (!is(Sentinel == OpenRight)) -{ - return typeof(return)(range, sentinel, openRight); -} - -/// Ditto -Until!(pred, Range, void) -until(alias pred, Range) -(Range range, OpenRight openRight = Yes.openRight) -{ - return typeof(return)(range, openRight); -} - -/// ditto -struct Until(alias pred, Range, Sentinel) -if (isInputRange!Range) -{ - private Range _input; - static if (!is(Sentinel == void)) - private Sentinel _sentinel; - private OpenRight _openRight; - private bool _matchStarted; - private bool _done; - - static if (!is(Sentinel == void)) - { - /// - this(Range input, Sentinel sentinel, - OpenRight openRight = Yes.openRight) - { - _input = input; - _sentinel = sentinel; - _openRight = openRight; - static if (isInputRange!Sentinel - && is(immutable ElementEncodingType!Sentinel == immutable ElementEncodingType!Range)) - { - _matchStarted = predSatisfied(); - _done = _input.empty || _sentinel.empty || openRight && _matchStarted; - if (_matchStarted && !_done && !openRight) - { - _sentinel.popFront; - } - } - else - { - _done = _input.empty || openRight && predSatisfied(); - } - } - private this(Range input, Sentinel sentinel, OpenRight openRight, - bool done) - { - _input = input; - _sentinel = sentinel; - _openRight = openRight; - _done = done; - } - } - else - { - /// - this(Range input, OpenRight openRight = Yes.openRight) - { - _input = input; - _openRight = openRight; - _done = _input.empty || openRight && predSatisfied(); - } - private this(Range input, OpenRight openRight, bool done) - { - _input = input; - _openRight = openRight; - _done = done; - } - } - - /// - @property bool empty() - { - return _done; - } - - /// - @property auto ref front() - { - assert(!empty, "Can not get the front of an empty Until"); - return _input.front; - } - - private bool predSatisfied() - { - static if (is(Sentinel == void)) - return cast(bool) unaryFun!pred(_input.front); - else - return cast(bool) startsWith!pred(_input, _sentinel); - } - - /// - void popFront() - { - assert(!empty, "Can not popFront of an empty Until"); - if (!_openRight) - { - static if (isInputRange!Sentinel - && is(immutable ElementEncodingType!Sentinel == immutable ElementEncodingType!Range)) - { - _input.popFront(); - _done = _input.empty || _sentinel.empty; - if (!_done) - { - if (_matchStarted) - { - _sentinel.popFront; - } - else - { - _matchStarted = predSatisfied(); - if (_matchStarted) - { - _sentinel.popFront; - } - } - } - } - else - { - _done = predSatisfied(); - _input.popFront(); - _done = _done || _input.empty; - } - } - else - { - _input.popFront(); - _done = _input.empty || predSatisfied(); - } - } - - static if (isForwardRange!Range) - { - /// - @property Until save() - { - static if (is(Sentinel == void)) - return Until(_input.save, _openRight, _done); - else - return Until(_input.save, _sentinel, _openRight, _done); - } - } -} - -/// -@safe unittest -{ - import std.algorithm.comparison : equal; - import std.typecons : No; - int[] a = [ 1, 2, 4, 7, 7, 2, 4, 7, 3, 5]; - assert(equal(a.until(7), [1, 2, 4])); - assert(equal(a.until(7, No.openRight), [1, 2, 4, 7])); -} - -@safe unittest -{ - import std.algorithm.comparison : equal; - int[] a = [ 1, 2, 4, 7, 7, 2, 4, 7, 3, 5]; - - static assert(isForwardRange!(typeof(a.until(7)))); - static assert(isForwardRange!(typeof(until!"a == 2"(a, No.openRight)))); - - assert(equal(a.until(7), [1, 2, 4])); - assert(equal(a.until([7, 2]), [1, 2, 4, 7])); - assert(equal(a.until(7, No.openRight), [1, 2, 4, 7])); - assert(equal(until!"a == 2"(a, No.openRight), [1, 2])); -} - -// https://issues.dlang.org/show_bug.cgi?id=13171 -@system unittest -{ - import std.algorithm.comparison : equal; - import std.range; - auto a = [1, 2, 3, 4]; - assert(equal(refRange(&a).until(3, No.openRight), [1, 2, 3])); - assert(a == [4]); -} - -// https://issues.dlang.org/show_bug.cgi?id=10460 -@safe unittest -{ - import std.algorithm.comparison : equal; - auto a = [1, 2, 3, 4]; - foreach (ref e; a.until(3)) - e = 0; - assert(equal(a, [0, 0, 3, 4])); -} - -// https://issues.dlang.org/show_bug.cgi?id=13124 -@safe unittest -{ - import std.algorithm.comparison : among, equal; - auto s = "hello how\nare you"; - assert(equal(s.until!(c => c.among!('\n', '\r')), "hello how")); -} - -// https://issues.dlang.org/show_bug.cgi?id=18657 -pure @safe unittest -{ - import std.algorithm.comparison : equal; - import std.range : refRange; - { - string s = "foobar"; - auto r = refRange(&s).until("bar"); - assert(equal(r.save, "foo")); - assert(equal(r.save, "foo")); - } - { - string s = "foobar"; - auto r = refRange(&s).until!(e => e == 'b'); - assert(equal(r.save, "foo")); - assert(equal(r.save, "foo")); - } -} - -// https://issues.dlang.org/show_bug.cgi?id=14543 -pure @safe unittest -{ - import std.algorithm.comparison : equal; - import std.uni : toUpper; - assert("one two three".until("two").equal("one ")); - assert("one two three".until("two", OpenRight.no).equal("one two")); - - assert("one two three".until("two", No.openRight).equal("one two")); - assert("one two three".until("two", Yes.openRight).equal("one ")); - - assert("one two three".until('t', Yes.openRight).equal("one ")); - assert("one two three".until("", Yes.openRight).equal("")); - assert("one two three".until("", No.openRight).equal("")); - - assert("one two three".until("three", No.openRight).equal("one two three")); - assert("one two three".until("three", Yes.openRight).equal("one two ")); - - assert("one two three".until("one", No.openRight).equal("one")); - assert("one two three".until("one", Yes.openRight).equal("")); - - assert("one two three".until("o", No.openRight).equal("o")); - assert("one two three".until("o", Yes.openRight).equal("")); - - assert("one two three".until("", No.openRight).equal("")); - assert("one two three".until("", Yes.openRight).equal("")); - - assert("one two three".until!((a,b)=>a.toUpper == b)("TWO", No.openRight).equal("one two")); -} - -// https://issues.dlang.org/show_bug.cgi?id=24342 -pure @safe unittest -{ - import std.algorithm.comparison : equal; - assert(["A", "BC", "D"].until("BC", No.openRight).equal(["A", "BC"])); - assert([[1], [2, 3], [4]].until([2, 3], No.openRight).equal([[1], [2, 3]])); -} diff --git a/phobos/std/algorithm/setops.d b/phobos/std/algorithm/setops.d deleted file mode 100644 index 363bd16..0000000 --- a/phobos/std/algorithm/setops.d +++ /dev/null @@ -1,1577 +0,0 @@ -// Written in the D programming language. -/** -This is a submodule of $(MREF std, algorithm). -It contains generic algorithms that implement set operations. - -The functions $(LREF multiwayMerge), $(LREF multiwayUnion), $(LREF setDifference), -$(LREF setIntersection), $(LREF setSymmetricDifference) expect a range of sorted -ranges as input. - -All algorithms are generalized to accept as input not only sets but also -$(LINK2 https://en.wikipedia.org/wiki/Multiset, multisets). Each algorithm -documents behaviour in the presence of duplicated inputs. - -$(SCRIPT inhibitQuickIndex = 1;) -$(BOOKTABLE Cheat Sheet, -$(TR $(TH Function Name) $(TH Description)) -$(T2 cartesianProduct, - Computes Cartesian product of two ranges.) -$(T2 largestPartialIntersection, - Copies out the values that occur most frequently in a range of ranges.) -$(T2 largestPartialIntersectionWeighted, - Copies out the values that occur most frequently (multiplied by - per-value weights) in a range of ranges.) -$(T2 multiwayMerge, - Merges a range of sorted ranges.) -$(T2 multiwayUnion, - Computes the union of a range of sorted ranges.) -$(T2 setDifference, - Lazily computes the set difference of two or more sorted ranges.) -$(T2 setIntersection, - Lazily computes the intersection of two or more sorted ranges.) -$(T2 setSymmetricDifference, - Lazily computes the symmetric set difference of two or more sorted - ranges.) -) - -Copyright: Andrei Alexandrescu 2008-. - -License: $(HTTP boost.org/LICENSE_1_0.txt, Boost License 1.0). - -Authors: $(HTTP erdani.com, Andrei Alexandrescu) - -Source: $(PHOBOSSRC std/algorithm/setops.d) - -Macros: -T2=$(TR $(TDNW $(LREF $1)) $(TD $+)) - */ -module std.algorithm.setops; - -import std.range.primitives; - -import std.functional : unaryFun, binaryFun; -import std.traits; -import std.meta : AliasSeq, staticMap, allSatisfy, anySatisfy; - -import std.algorithm.sorting : Merge; -import std.typecons : No; - -// cartesianProduct -/** -Lazily computes the Cartesian product of two or more ranges. The product is a -range of tuples of elements from each respective range. - -The conditions for the two-range case are as follows: - -If both ranges are finite, then one must be (at least) a -$(REF_ALTTEXT forward range, isForwardRange, std,range,primitives) and the -other an $(REF_ALTTEXT input range, isInputRange, std,range,primitives). - -If one range is infinite and the other finite, then the finite range must -be a forward range, and the infinite range can be an input range. - -If both ranges are infinite, then both must be forward ranges. - -When there are more than two ranges, the above conditions apply to each -adjacent pair of ranges. - -Params: - range1 = The first range - range2 = The second range - ranges = Two or more non-infinite forward ranges - otherRanges = Zero or more non-infinite forward ranges - -Returns: - A forward range of $(REF Tuple, std,typecons) representing elements of the - cartesian product of the given ranges. -*/ -auto cartesianProduct(R1, R2)(R1 range1, R2 range2) -if (!allSatisfy!(isForwardRange, R1, R2) || - anySatisfy!(isInfinite, R1, R2)) -{ - import std.algorithm.iteration : map, joiner; - - static if (isInfinite!R1 && isInfinite!R2) - { - static if (isForwardRange!R1 && isForwardRange!R2) - { - import std.range : zip, repeat, take, chain, sequence; - - // This algorithm traverses the cartesian product by alternately - // covering the right and bottom edges of an increasing square area - // over the infinite table of combinations. This schedule allows us - // to require only forward ranges. - return zip(sequence!"n"(cast(size_t) 0), range1.save, range2.save, - repeat(range1), repeat(range2)) - .map!(function(a) => chain( - zip(repeat(a[1]), take(a[4].save, a[0])), - zip(take(a[3].save, a[0]+1), repeat(a[2])) - ))() - .joiner(); - } - else static assert(0, "cartesianProduct of infinite ranges requires "~ - "forward ranges"); - } - else static if (isInputRange!R1 && isForwardRange!R2 && !isInfinite!R2) - { - import std.range : zip, repeat; - return joiner(map!((ElementType!R1 a) => zip(repeat(a), range2.save)) - (range1)); - } - else static if (isInputRange!R2 && isForwardRange!R1 && !isInfinite!R1) - { - import std.range : zip, repeat; - return joiner(map!((ElementType!R2 a) => zip(range1.save, repeat(a))) - (range2)); - } - else static assert(0, "cartesianProduct involving finite ranges must "~ - "have at least one finite forward range"); -} - -/// -@safe unittest -{ - import std.algorithm.searching : canFind; - import std.range; - import std.typecons : tuple; - - auto N = sequence!"n"(0); // the range of natural numbers - auto N2 = cartesianProduct(N, N); // the range of all pairs of natural numbers - - // Various arbitrary number pairs can be found in the range in finite time. - assert(canFind(N2, tuple(0, 0))); - assert(canFind(N2, tuple(123, 321))); - assert(canFind(N2, tuple(11, 35))); - assert(canFind(N2, tuple(279, 172))); -} - -/// -@safe unittest -{ - import std.algorithm.searching : canFind; - import std.typecons : tuple; - - auto B = [ 1, 2, 3 ]; - auto C = [ 4, 5, 6 ]; - auto BC = cartesianProduct(B, C); - - foreach (n; [[1, 4], [2, 4], [3, 4], [1, 5], [2, 5], [3, 5], [1, 6], - [2, 6], [3, 6]]) - { - assert(canFind(BC, tuple(n[0], n[1]))); - } -} - -@safe unittest -{ - // Test cartesian product of two infinite ranges - import std.algorithm.searching : canFind; - import std.range; - import std.typecons : tuple; - - auto Even = sequence!"2*n"(0); - auto Odd = sequence!"2*n+1"(0); - auto EvenOdd = cartesianProduct(Even, Odd); - - foreach (pair; [[0, 1], [2, 1], [0, 3], [2, 3], [4, 1], [4, 3], [0, 5], - [2, 5], [4, 5], [6, 1], [6, 3], [6, 5]]) - { - assert(canFind(EvenOdd, tuple(pair[0], pair[1]))); - } - - // This should terminate in finite time - assert(canFind(EvenOdd, tuple(124, 73))); - assert(canFind(EvenOdd, tuple(0, 97))); - assert(canFind(EvenOdd, tuple(42, 1))); -} - -@safe unittest -{ - // Test cartesian product of an infinite input range and a finite forward - // range. - import std.algorithm.searching : canFind; - import std.range; - import std.typecons : tuple; - - auto N = sequence!"n"(0); - auto M = [100, 200, 300]; - auto NM = cartesianProduct(N,M); - - foreach (pair; [[0, 100], [0, 200], [0, 300], [1, 100], [1, 200], [1, 300], - [2, 100], [2, 200], [2, 300], [3, 100], [3, 200], - [3, 300]]) - { - assert(canFind(NM, tuple(pair[0], pair[1]))); - } - - // We can't solve the halting problem, so we can only check a finite - // initial segment here. - assert(!canFind(NM.take(100), tuple(100, 0))); - assert(!canFind(NM.take(100), tuple(1, 1))); - assert(!canFind(NM.take(100), tuple(100, 200))); - - auto MN = cartesianProduct(M,N); - foreach (pair; [[100, 0], [200, 0], [300, 0], [100, 1], [200, 1], [300, 1], - [100, 2], [200, 2], [300, 2], [100, 3], [200, 3], - [300, 3]]) - { - assert(canFind(MN, tuple(pair[0], pair[1]))); - } - - // We can't solve the halting problem, so we can only check a finite - // initial segment here. - assert(!canFind(MN.take(100), tuple(0, 100))); - assert(!canFind(MN.take(100), tuple(0, 1))); - assert(!canFind(MN.take(100), tuple(100, 200))); -} - -@safe unittest -{ - import std.algorithm.searching : canFind; - import std.typecons : tuple; - - // Test cartesian product of two finite ranges. - auto X = [1, 2, 3]; - auto Y = [4, 5, 6]; - auto XY = cartesianProduct(X, Y); - auto Expected = [[1, 4], [1, 5], [1, 6], [2, 4], [2, 5], [2, 6], [3, 4], - [3, 5], [3, 6]]; - - // Verify Expected ⊆ XY - foreach (pair; Expected) - { - assert(canFind(XY, tuple(pair[0], pair[1]))); - } - - // Verify XY ⊆ Expected - foreach (pair; XY) - { - assert(canFind(Expected, [pair[0], pair[1]])); - } - - // And therefore, by set comprehension, XY == Expected -} - -@safe unittest -{ - import std.algorithm.comparison : equal; - import std.algorithm.iteration : map; - import std.algorithm.searching : canFind; - import std.typecons : tuple; - - import std.range; - auto N = sequence!"n"(0); - - // To force the template to fall to the second case, we wrap N in a struct - // that doesn't allow bidirectional access. - struct FwdRangeWrapper(R) - { - R impl; - - // Input range API - @property auto front() { return impl.front; } - void popFront() { impl.popFront(); } - static if (isInfinite!R) - enum empty = false; - else - @property bool empty() { return impl.empty; } - - // Forward range API - @property auto save() { return typeof(this)(impl.save); } - } - auto fwdWrap(R)(R range) { return FwdRangeWrapper!R(range); } - - // General test: two infinite bidirectional ranges - auto N2 = cartesianProduct(N, N); - - assert(canFind(N2, tuple(0, 0))); - assert(canFind(N2, tuple(123, 321))); - assert(canFind(N2, tuple(11, 35))); - assert(canFind(N2, tuple(279, 172))); - - // Test first case: forward range with bidirectional range - auto fwdN = fwdWrap(N); - auto N2_a = cartesianProduct(fwdN, N); - - assert(canFind(N2_a, tuple(0, 0))); - assert(canFind(N2_a, tuple(123, 321))); - assert(canFind(N2_a, tuple(11, 35))); - assert(canFind(N2_a, tuple(279, 172))); - - // Test second case: bidirectional range with forward range - auto N2_b = cartesianProduct(N, fwdN); - - assert(canFind(N2_b, tuple(0, 0))); - assert(canFind(N2_b, tuple(123, 321))); - assert(canFind(N2_b, tuple(11, 35))); - assert(canFind(N2_b, tuple(279, 172))); - - // Test third case: finite forward range with (infinite) input range - static struct InpRangeWrapper(R) - { - R impl; - - // Input range API - @property auto front() { return impl.front; } - void popFront() { impl.popFront(); } - static if (isInfinite!R) - enum empty = false; - else - @property bool empty() { return impl.empty; } - } - auto inpWrap(R)(R r) { return InpRangeWrapper!R(r); } - - auto inpN = inpWrap(N); - auto B = [ 1, 2, 3 ]; - auto fwdB = fwdWrap(B); - auto BN = cartesianProduct(fwdB, inpN); - - assert(equal(map!"[a[0],a[1]]"(BN.take(10)), [[1, 0], [2, 0], [3, 0], - [1, 1], [2, 1], [3, 1], [1, 2], [2, 2], [3, 2], [1, 3]])); - - // Test fourth case: (infinite) input range with finite forward range - auto NB = cartesianProduct(inpN, fwdB); - - assert(equal(map!"[a[0],a[1]]"(NB.take(10)), [[0, 1], [0, 2], [0, 3], - [1, 1], [1, 2], [1, 3], [2, 1], [2, 2], [2, 3], [3, 1]])); - - // General finite range case - auto C = [ 4, 5, 6 ]; - auto BC = cartesianProduct(B, C); - - foreach (n; [[1, 4], [2, 4], [3, 4], [1, 5], [2, 5], [3, 5], [1, 6], - [2, 6], [3, 6]]) - { - assert(canFind(BC, tuple(n[0], n[1]))); - } -} - -// https://issues.dlang.org/show_bug.cgi?id=13091 -pure nothrow @safe @nogc unittest -{ - int[1] a = [1]; - foreach (t; cartesianProduct(a[], a[])) {} -} - -/// ditto -auto cartesianProduct(RR...)(RR ranges) -if (ranges.length >= 2 && - allSatisfy!(isForwardRange, RR) && - !anySatisfy!(isInfinite, RR)) -{ - // This overload uses a much less template-heavy implementation when - // all ranges are finite forward ranges, which is the most common use - // case, so that we don't run out of resources too quickly. - // - // For infinite ranges or non-forward ranges, we fall back to the old - // implementation which expands an exponential number of templates. - import std.typecons : tuple; - - static struct Result - { - RR ranges; - RR current; - bool empty = true; - - this(RR _ranges) - { - ranges = _ranges; - empty = false; - foreach (i, r; ranges) - { - current[i] = r.save; - if (current[i].empty) - empty = true; - } - } - @property auto front() - { - import std.algorithm.internal : algoFormat; - import std.range : iota; - return mixin(algoFormat("tuple(%(current[%d].front%|,%))", - iota(0, current.length))); - } - void popFront() scope - { - foreach_reverse (i, ref r; current) - { - r.popFront(); - if (!r.empty) break; - - static if (i == 0) - empty = true; - else - r = ranges[i].save; // rollover - } - } - @property Result save() return scope - { - Result copy = this; - foreach (i, r; ranges) - { - copy.ranges[i] = ranges[i].save; - copy.current[i] = current[i].save; - } - return copy; - } - } - static assert(isForwardRange!Result, Result.stringof ~ " must be a forward" - ~ " range"); - - return Result(ranges); -} - -// cartesian product of empty ranges should be empty -// https://issues.dlang.org/show_bug.cgi?id=10693 -@safe unittest -{ - int[] a, b, c, d, e; - auto cprod = cartesianProduct(a,b,c,d,e); - assert(cprod.empty); - foreach (_; cprod) {} // should not crash - - // Test case where only one of the ranges is empty: the result should still - // be empty. - int[] p=[1], q=[]; - auto cprod2 = cartesianProduct(p,p,p,q,p); - assert(cprod2.empty); - foreach (_; cprod2) {} // should not crash -} - -@safe unittest -{ - // .init value of cartesianProduct should be empty - auto cprod = cartesianProduct([0,0], [1,1], [2,2]); - assert(!cprod.empty); - assert(cprod.init.empty); -} - -// https://issues.dlang.org/show_bug.cgi?id=13393 -@safe unittest -{ - assert(!cartesianProduct([0],[0],[0]).save.empty); -} - -/// ditto -auto cartesianProduct(R1, R2, RR...)(R1 range1, R2 range2, RR otherRanges) -if (!allSatisfy!(isForwardRange, R1, R2, RR) || - anySatisfy!(isInfinite, R1, R2, RR)) -{ - /* We implement the n-ary cartesian product by recursively invoking the - * binary cartesian product. To make the resulting range nicer, we denest - * one level of tuples so that a ternary cartesian product, for example, - * returns 3-element tuples instead of nested 2-element tuples. - */ - import std.algorithm.internal : algoFormat; - import std.algorithm.iteration : map; - import std.range : iota; - - enum string denest = algoFormat("tuple(a[0], %(a[1][%d]%|,%))", - iota(0, otherRanges.length+1)); - return map!denest( - cartesianProduct(range1, cartesianProduct(range2, otherRanges)) - ); -} - -@safe unittest -{ - import std.algorithm.searching : canFind; - import std.range; - import std.typecons : tuple, Tuple; - - auto N = sequence!"n"(0); - auto N3 = cartesianProduct(N, N, N); - - // Check that tuples are properly denested - assert(is(ElementType!(typeof(N3)) == Tuple!(size_t,size_t,size_t))); - - assert(canFind(N3, tuple(0, 27, 7))); - assert(canFind(N3, tuple(50, 23, 11))); - assert(canFind(N3, tuple(9, 3, 0))); -} - -@safe unittest -{ - import std.algorithm.searching : canFind; - import std.range; - import std.typecons : tuple, Tuple; - - auto N = sequence!"n"(0); - auto N4 = cartesianProduct(N, N, N, N); - - // Check that tuples are properly denested - assert(is(ElementType!(typeof(N4)) == Tuple!(size_t,size_t,size_t,size_t))); - - assert(canFind(N4, tuple(1, 2, 3, 4))); - assert(canFind(N4, tuple(4, 3, 2, 1))); - assert(canFind(N4, tuple(10, 3, 1, 2))); -} - -// https://issues.dlang.org/show_bug.cgi?id=9878 -/// -@safe unittest -{ - import std.algorithm.comparison : equal; - import std.typecons : tuple; - - auto A = [ 1, 2, 3 ]; - auto B = [ 'a', 'b', 'c' ]; - auto C = [ "x", "y", "z" ]; - auto ABC = cartesianProduct(A, B, C); - - assert(ABC.equal([ - tuple(1, 'a', "x"), tuple(1, 'a', "y"), tuple(1, 'a', "z"), - tuple(1, 'b', "x"), tuple(1, 'b', "y"), tuple(1, 'b', "z"), - tuple(1, 'c', "x"), tuple(1, 'c', "y"), tuple(1, 'c', "z"), - tuple(2, 'a', "x"), tuple(2, 'a', "y"), tuple(2, 'a', "z"), - tuple(2, 'b', "x"), tuple(2, 'b', "y"), tuple(2, 'b', "z"), - tuple(2, 'c', "x"), tuple(2, 'c', "y"), tuple(2, 'c', "z"), - tuple(3, 'a', "x"), tuple(3, 'a', "y"), tuple(3, 'a', "z"), - tuple(3, 'b', "x"), tuple(3, 'b', "y"), tuple(3, 'b', "z"), - tuple(3, 'c', "x"), tuple(3, 'c', "y"), tuple(3, 'c', "z") - ])); -} - -pure @safe nothrow @nogc unittest -{ - import std.range.primitives : isForwardRange; - int[2] A = [1,2]; - auto C = cartesianProduct(A[], A[], A[]); - assert(isForwardRange!(typeof(C))); - - C.popFront(); - auto front1 = C.front; - auto D = C.save; - C.popFront(); - assert(D.front == front1); -} - -// https://issues.dlang.org/show_bug.cgi?id=13935 -@safe unittest -{ - import std.algorithm.iteration : map; - auto seq = [1, 2].map!(x => x); - foreach (pair; cartesianProduct(seq, seq)) {} -} - -@system unittest -{ - import std.algorithm.comparison : equal; - import std.typecons : tuple; - - static struct SystemRange - { - int[] data; - - int front() @system @property inout - { - return data[0]; - } - - bool empty() @system @property inout - { - return data.length == 0; - } - - void popFront() @system - { - data = data[1 .. $]; - } - - SystemRange save() @system - { - return this; - } - } - - assert(SystemRange([1, 2]).cartesianProduct(SystemRange([3, 4])) - .equal([tuple(1, 3), tuple(1, 4), tuple(2, 3), tuple(2, 4)])); -} - -// largestPartialIntersection -/** -Given a range of sorted $(REF_ALTTEXT forward ranges, isForwardRange, std,range,primitives) -`ror`, copies to `tgt` the elements that are common to most ranges, along with their number -of occurrences. All ranges in `ror` are assumed to be sorted by $(D -less). Only the most frequent `tgt.length` elements are returned. - -Params: - less = The predicate the ranges are sorted by. - ror = A range of forward ranges sorted by `less`. - tgt = The target range to copy common elements to. - sorted = Whether the elements copied should be in sorted order. - -The function `largestPartialIntersection` is useful for -e.g. searching an $(LINK2 https://en.wikipedia.org/wiki/Inverted_index, -inverted index) for the documents most -likely to contain some terms of interest. The complexity of the search -is $(BIGOH n * log(tgt.length)), where `n` is the sum of lengths of -all input ranges. This approach is faster than keeping an associative -array of the occurrences and then selecting its top items, and also -requires less memory (`largestPartialIntersection` builds its -result directly in `tgt` and requires no extra memory). - -If at least one of the ranges is a multiset, then all occurences -of a duplicate element are taken into account. The result is -equivalent to merging all ranges and picking the most frequent -`tgt.length` elements. - -Warning: Because `largestPartialIntersection` does not allocate -extra memory, it will leave `ror` modified. Namely, $(D -largestPartialIntersection) assumes ownership of `ror` and -discretionarily swaps and advances elements of it. If you want $(D -ror) to preserve its contents after the call, you may want to pass a -duplicate to `largestPartialIntersection` (and perhaps cache the -duplicate in between calls). - */ -void largestPartialIntersection -(alias less = "a < b", RangeOfRanges, Range) -(RangeOfRanges ror, Range tgt, SortOutput sorted = No.sortOutput) -{ - struct UnitWeights - { - static int opIndex(ElementType!(ElementType!RangeOfRanges)) { return 1; } - } - return largestPartialIntersectionWeighted!less(ror, tgt, UnitWeights(), - sorted); -} - -/// -@system unittest -{ - import std.typecons : tuple, Tuple; - - // Figure which number can be found in most arrays of the set of - // arrays below. - double[][] a = - [ - [ 1, 4, 7, 8 ], - [ 1, 7 ], - [ 1, 7, 8], - [ 4 ], - [ 7 ], - ]; - auto b = new Tuple!(double, uint)[1]; - // it will modify the input range, hence we need to create a duplicate - largestPartialIntersection(a.dup, b); - // First member is the item, second is the occurrence count - assert(b[0] == tuple(7.0, 4u)); - // 7.0 occurs in 4 out of 5 inputs, more than any other number - - // If more of the top-frequent numbers are needed, just create a larger - // tgt range - auto c = new Tuple!(double, uint)[2]; - largestPartialIntersection(a, c); - assert(c[0] == tuple(1.0, 3u)); - // 1.0 occurs in 3 inputs - - // multiset - double[][] x = - [ - [1, 1, 1, 1, 4, 7, 8], - [1, 7], - [1, 7, 8], - [4, 7], - [7] - ]; - auto y = new Tuple!(double, uint)[2]; - largestPartialIntersection(x.dup, y); - // 7.0 occurs 5 times - assert(y[0] == tuple(7.0, 5u)); - // 1.0 occurs 6 times - assert(y[1] == tuple(1.0, 6u)); -} - -import std.algorithm.sorting : SortOutput; // FIXME - -// largestPartialIntersectionWeighted -/** -Similar to `largestPartialIntersection`, but associates a weight -with each distinct element in the intersection. - -If at least one of the ranges is a multiset, then all occurences -of a duplicate element are taken into account. The result -is equivalent to merging all input ranges and picking the highest -`tgt.length`, weight-based ranking elements. - -Params: - less = The predicate the ranges are sorted by. - ror = A range of $(REF_ALTTEXT forward ranges, isForwardRange, std,range,primitives) - sorted by `less`. - tgt = The target range to copy common elements to. - weights = An associative array mapping elements to weights. - sorted = Whether the elements copied should be in sorted order. - -*/ -void largestPartialIntersectionWeighted -(alias less = "a < b", RangeOfRanges, Range, WeightsAA) -(RangeOfRanges ror, Range tgt, WeightsAA weights, SortOutput sorted = No.sortOutput) -{ - import std.algorithm.iteration : group; - import std.algorithm.sorting : topNCopy; - - if (tgt.empty) return; - alias InfoType = ElementType!Range; - bool heapComp(InfoType a, InfoType b) - { - return weights[a[0]] * a[1] > weights[b[0]] * b[1]; - } - topNCopy!heapComp(group(multiwayMerge!less(ror)), tgt, sorted); -} - -/// -@system unittest -{ - import std.typecons : tuple, Tuple; - - // Figure which number can be found in most arrays of the set of - // arrays below, with specific per-element weights - double[][] a = - [ - [ 1, 4, 7, 8 ], - [ 1, 7 ], - [ 1, 7, 8], - [ 4 ], - [ 7 ], - ]; - auto b = new Tuple!(double, uint)[1]; - double[double] weights = [ 1:1.2, 4:2.3, 7:1.1, 8:1.1 ]; - largestPartialIntersectionWeighted(a, b, weights); - // First member is the item, second is the occurrence count - assert(b[0] == tuple(4.0, 2u)); - // 4.0 occurs 2 times -> 4.6 (2 * 2.3) - // 7.0 occurs 3 times -> 4.4 (3 * 1.1) - - // multiset - double[][] x = - [ - [ 1, 1, 1, 4, 7, 8 ], - [ 1, 7 ], - [ 1, 7, 8], - [ 4 ], - [ 7 ], - ]; - auto y = new Tuple!(double, uint)[1]; - largestPartialIntersectionWeighted(x, y, weights); - assert(y[0] == tuple(1.0, 5u)); - // 1.0 occurs 5 times -> 1.2 * 5 = 6 -} - -@system unittest -{ - import std.conv : text; - import std.typecons : tuple, Tuple, Yes; - - double[][] a = - [ - [ 1, 4, 7, 8 ], - [ 1, 7 ], - [ 1, 7, 8], - [ 4 ], - [ 7 ], - ]; - auto b = new Tuple!(double, uint)[2]; - largestPartialIntersection(a, b, Yes.sortOutput); - assert(b == [ tuple(7.0, 4u), tuple(1.0, 3u) ][], text(b)); - assert(a[0].empty); -} - -@system unittest -{ - import std.conv : text; - import std.typecons : tuple, Tuple, Yes; - - string[][] a = - [ - [ "1", "4", "7", "8" ], - [ "1", "7" ], - [ "1", "7", "8"], - [ "4" ], - [ "7" ], - ]; - auto b = new Tuple!(string, uint)[2]; - largestPartialIntersection(a, b, Yes.sortOutput); - assert(b == [ tuple("7", 4u), tuple("1", 3u) ][], text(b)); -} - -@system unittest -{ - import std.typecons : tuple, Tuple; - - // Figure which number can be found in most arrays of the set of - // arrays below, with specific per-element weights - double[][] a = - [ - [ 1, 4, 7, 8 ], - [ 1, 7 ], - [ 1, 7, 8], - [ 4 ], - [ 7 ], - ]; - auto b = new Tuple!(double, uint)[1]; - double[double] weights = [ 1:1.2, 4:2.3, 7:1.1, 8:1.1 ]; - largestPartialIntersectionWeighted(a, b, weights); - // First member is the item, second is the occurrence count - assert(b[0] == tuple(4.0, 2u)); -} - -@system unittest -{ - import std.container : Array; - import std.typecons : Tuple; - - alias T = Tuple!(uint, uint); - const Array!T arrayOne = Array!T( [ T(1,2), T(3,4) ] ); - const Array!T arrayTwo = Array!T([ T(1,2), T(3,4) ] ); - - assert(arrayOne == arrayTwo); -} - -// MultiwayMerge -/** -Merges multiple sets. The input sets are passed as a -range of ranges and each is assumed to be sorted by $(D -less). Computation is done lazily, one union element at a time. The -complexity of one `popFront` operation is $(BIGOH -log(ror.length)). However, the length of `ror` decreases as ranges -in it are exhausted, so the complexity of a full pass through $(D -MultiwayMerge) is dependent on the distribution of the lengths of ranges -contained within `ror`. If all ranges have the same length `n` -(worst case scenario), the complexity of a full pass through $(D -MultiwayMerge) is $(BIGOH n * ror.length * log(ror.length)), i.e., $(D -log(ror.length)) times worse than just spanning all ranges in -turn. The output comes sorted (unstably) by `less`. - -The length of the resulting range is the sum of all lengths of -the ranges passed as input. This means that all elements (duplicates -included) are transferred to the resulting range. - -For backward compatibility, `multiwayMerge` is available under -the name `nWayUnion` and `MultiwayMerge` under the name of `NWayUnion` . -Future code should use `multiwayMerge` and `MultiwayMerge` as `nWayUnion` -and `NWayUnion` will be deprecated. - -Params: - less = Predicate the given ranges are sorted by. - ror = A range of ranges sorted by `less` to compute the union for. - -Returns: - A range of the union of the ranges in `ror`. - -Warning: Because `MultiwayMerge` does not allocate extra memory, it -will leave `ror` modified. Namely, `MultiwayMerge` assumes ownership -of `ror` and discretionarily swaps and advances elements of it. If -you want `ror` to preserve its contents after the call, you may -want to pass a duplicate to `MultiwayMerge` (and perhaps cache the -duplicate in between calls). - -See_Also: $(REF merge, std,algorithm,sorting) for an analogous function that - takes a static number of ranges of possibly disparate types. - */ -struct MultiwayMerge(alias less, RangeOfRanges) -{ - import std.container : BinaryHeap; - - private alias ElementType = .ElementType!(.ElementType!RangeOfRanges); - private alias comp = binaryFun!less; - private RangeOfRanges _ror; - - /// - static bool compFront(.ElementType!RangeOfRanges a, - .ElementType!RangeOfRanges b) - { - // revert comparison order so we get the smallest elements first - return comp(b.front, a.front); - } - private BinaryHeap!(RangeOfRanges, compFront) _heap; - - /// - this(RangeOfRanges ror) - { - import std.algorithm.mutation : remove, SwapStrategy; - - // Preemptively get rid of all empty ranges in the input - // No need for stability either - _ror = remove!("a.empty", SwapStrategy.unstable)(ror); - //Build the heap across the range - _heap.acquire(_ror); - } - - /// - @property bool empty() { return _ror.empty; } - - /// - @property auto ref front() - { - return _heap.front.front; - } - - /// - void popFront() - { - _heap.removeFront(); - // let's look at the guy just popped - _ror.back.popFront(); - if (_ror.back.empty) - { - _ror.popBack(); - // nothing else to do: the empty range is not in the - // heap and not in _ror - return; - } - // Put the popped range back in the heap - const bool worked = _heap.conditionalInsert(_ror.back); - assert(worked, "Failed to insert item into heap"); - } -} - -/// Ditto -MultiwayMerge!(less, RangeOfRanges) multiwayMerge -(alias less = "a < b", RangeOfRanges) -(RangeOfRanges ror) -{ - return typeof(return)(ror); -} - -/// -@system unittest -{ - import std.algorithm.comparison : equal; - - double[][] a = - [ - [ 1, 4, 7, 8 ], - [ 1, 7 ], - [ 1, 7, 8], - [ 4 ], - [ 7 ], - ]; - auto witness = [ - 1, 1, 1, 4, 4, 7, 7, 7, 7, 8, 8 - ]; - assert(equal(multiwayMerge(a), witness)); - - double[][] b = - [ - // range with duplicates - [ 1, 1, 4, 7, 8 ], - [ 7 ], - [ 1, 7, 8], - [ 4 ], - [ 7 ], - ]; - // duplicates are propagated to the resulting range - assert(equal(multiwayMerge(b), witness)); -} - -alias nWayUnion = multiwayMerge; -alias NWayUnion = MultiwayMerge; - -/** -Computes the union of multiple ranges. The -$(REF_ALTTEXT input ranges, isInputRange, std,range,primitives) are passed -as a range of ranges and each is assumed to be sorted by $(D -less). Computation is done lazily, one union element at a time. -`multiwayUnion(ror)` is functionally equivalent to `multiwayMerge(ror).uniq`. - -"The output of multiwayUnion has no duplicates even when its inputs contain duplicates." - -Params: - less = Predicate the given ranges are sorted by. - ror = A range of ranges sorted by `less` to compute the intersection for. - -Returns: - A range of the union of the ranges in `ror`. - -See also: $(LREF multiwayMerge) - */ -auto multiwayUnion(alias less = "a < b", RangeOfRanges)(RangeOfRanges ror) -{ - import std.algorithm.iteration : uniq; - import std.functional : not; - return ror.multiwayMerge!(less).uniq!(not!less); -} - -/// -@system unittest -{ - import std.algorithm.comparison : equal; - - // sets - double[][] a = - [ - [ 1, 4, 7, 8 ], - [ 1, 7 ], - [ 1, 7, 8], - [ 4 ], - [ 7 ], - ]; - - auto witness = [1, 4, 7, 8]; - assert(equal(multiwayUnion(a), witness)); - - // multisets - double[][] b = - [ - [ 1, 1, 1, 4, 7, 8 ], - [ 1, 7 ], - [ 1, 7, 7, 8], - [ 4 ], - [ 7 ], - ]; - assert(equal(multiwayUnion(b), witness)); - - double[][] c = - [ - [9, 8, 8, 8, 7, 6], - [9, 8, 6], - [9, 8, 5] - ]; - auto witness2 = [9, 8, 7, 6, 5]; - assert(equal(multiwayUnion!"a > b"(c), witness2)); -} - -/** -Lazily computes the difference of `r1` and `r2`. The two ranges -are assumed to be sorted by `less`. The element types of the two -ranges must have a common type. - - -In the case of multisets, considering that element `a` appears `x` -times in `r1` and `y` times and `r2`, the number of occurences -of `a` in the resulting range is going to be `x-y` if x > y or 0 otherwise. - -Params: - less = Predicate the given ranges are sorted by. - r1 = The first range. - r2 = The range to subtract from `r1`. - -Returns: - A range of the difference of `r1` and `r2`. - -See_also: $(LREF setSymmetricDifference) - */ -struct SetDifference(alias less = "a < b", R1, R2) -if (isInputRange!(R1) && isInputRange!(R2)) -{ -private: - R1 r1; - R2 r2; - alias comp = binaryFun!(less); - - void adjustPosition() - { - while (!r1.empty) - { - if (r2.empty || comp(r1.front, r2.front)) break; - if (comp(r2.front, r1.front)) - { - r2.popFront(); - } - else - { - // both are equal - r1.popFront(); - r2.popFront(); - } - } - } - -public: - /// - this(R1 r1, R2 r2) - { - this.r1 = r1; - this.r2 = r2; - // position to the first element - adjustPosition(); - } - - /// - void popFront() - { - r1.popFront(); - adjustPosition(); - } - - /// - @property auto ref front() - { - assert(!empty, "Can not get front of empty SetDifference"); - return r1.front; - } - - static if (isForwardRange!R1 && isForwardRange!R2) - { - /// - @property typeof(this) save() - { - auto ret = this; - ret.r1 = r1.save; - ret.r2 = r2.save; - return ret; - } - } - - /// - @property bool empty() { return r1.empty; } -} - -/// Ditto -SetDifference!(less, R1, R2) setDifference(alias less = "a < b", R1, R2) -(R1 r1, R2 r2) -{ - return typeof(return)(r1, r2); -} - -/// -@safe unittest -{ - import std.algorithm.comparison : equal; - import std.range.primitives : isForwardRange; - - //sets - int[] a = [ 1, 2, 4, 5, 7, 9 ]; - int[] b = [ 0, 1, 2, 4, 7, 8 ]; - assert(equal(setDifference(a, b), [5, 9])); - static assert(isForwardRange!(typeof(setDifference(a, b)))); - - // multisets - int[] x = [1, 1, 1, 2, 3]; - int[] y = [1, 1, 2, 4, 5]; - auto r = setDifference(x, y); - assert(equal(r, [1, 3])); - assert(setDifference(r, x).empty); -} - -// https://issues.dlang.org/show_bug.cgi?id=10460 -@safe unittest -{ - import std.algorithm.comparison : equal; - - int[] a = [1, 2, 3, 4, 5]; - int[] b = [2, 4]; - foreach (ref e; setDifference(a, b)) - e = 0; - assert(equal(a, [0, 2, 0, 4, 0])); -} - -/** -Lazily computes the intersection of two or more -$(REF_ALTTEXT input ranges, isInputRange, std,range,primitives) -`ranges`. The ranges are assumed to be sorted by `less`. The element -types of the ranges must have a common type. - -In the case of multisets, the range with the minimum number of -occurences of a given element, propagates the number of -occurences of this element to the resulting range. - -Params: - less = Predicate the given ranges are sorted by. - ranges = The ranges to compute the intersection for. - -Returns: - A range containing the intersection of the given ranges. - */ -struct SetIntersection(alias less = "a < b", Rs...) -if (Rs.length >= 2 && allSatisfy!(isInputRange, Rs) && - !is(CommonType!(staticMap!(ElementType, Rs)) == void)) -{ -private: - Rs _input; - alias comp = binaryFun!less; - alias ElementType = CommonType!(staticMap!(.ElementType, Rs)); - - // Positions to the first elements that are all equal - void adjustPosition() - { - if (empty) return; - - size_t done = Rs.length; - static if (Rs.length > 1) while (true) - { - foreach (i, ref r; _input) - { - alias next = _input[(i + 1) % Rs.length]; - - if (comp(next.front, r.front)) - { - do - { - next.popFront(); - if (next.empty) return; - } while (comp(next.front, r.front)); - done = Rs.length; - } - if (--done == 0) return; - } - } - } - -public: - /// - this(Rs input) - { - this._input = input; - // position to the first element - adjustPosition(); - } - - /// - @property bool empty() - { - foreach (ref r; _input) - { - if (r.empty) return true; - } - return false; - } - - /// - void popFront() - { - assert(!empty, "Can not popFront of empty SetIntersection"); - static if (Rs.length > 1) foreach (i, ref r; _input) - { - alias next = _input[(i + 1) % Rs.length]; - assert(!comp(r.front, next.front), "Set elements must not" - ~ " contradict the less predicate"); - } - - foreach (ref r; _input) - { - r.popFront(); - } - adjustPosition(); - } - - /// - @property ElementType front() - { - assert(!empty, "Can not get front of empty SetIntersection"); - return _input[0].front; - } - - static if (allSatisfy!(isForwardRange, Rs)) - { - /// - @property SetIntersection save() - { - auto ret = this; - foreach (i, ref r; _input) - { - ret._input[i] = r.save; - } - return ret; - } - } -} - -/// Ditto -SetIntersection!(less, Rs) setIntersection(alias less = "a < b", Rs...)(Rs ranges) -if (Rs.length >= 2 && allSatisfy!(isInputRange, Rs) && - !is(CommonType!(staticMap!(ElementType, Rs)) == void)) -{ - return typeof(return)(ranges); -} - -/// -@safe unittest -{ - import std.algorithm.comparison : equal; - - // sets - int[] a = [ 1, 2, 4, 5, 7, 9 ]; - int[] b = [ 0, 1, 2, 4, 7, 8 ]; - int[] c = [ 0, 1, 4, 5, 7, 8 ]; - assert(equal(setIntersection(a, a), a)); - assert(equal(setIntersection(a, b), [1, 2, 4, 7])); - assert(equal(setIntersection(a, b, c), [1, 4, 7])); - - // multisets - int[] d = [ 1, 1, 2, 2, 7, 7 ]; - int[] e = [ 1, 1, 1, 7]; - assert(equal(setIntersection(a, d), [1, 2, 7])); - assert(equal(setIntersection(d, e), [1, 1, 7])); -} - -@safe unittest -{ - import std.algorithm.comparison : equal; - import std.algorithm.iteration : filter; - - int[] a = [ 1, 2, 4, 5, 7, 9 ]; - int[] b = [ 0, 1, 2, 4, 7, 8 ]; - int[] c = [ 0, 1, 4, 5, 7, 8 ]; - int[] d = [ 1, 3, 4 ]; - int[] e = [ 4, 5 ]; - - assert(equal(setIntersection(a, a), a)); - assert(equal(setIntersection(a, a, a), a)); - assert(equal(setIntersection(a, b), [1, 2, 4, 7])); - assert(equal(setIntersection(a, b, c), [1, 4, 7])); - assert(equal(setIntersection(a, b, c, d), [1, 4])); - assert(equal(setIntersection(a, b, c, d, e), [4])); - - auto inpA = a.filter!(_ => true), inpB = b.filter!(_ => true); - auto inpC = c.filter!(_ => true), inpD = d.filter!(_ => true); - assert(equal(setIntersection(inpA, inpB, inpC, inpD), [1, 4])); - - assert(equal(setIntersection(a, b, b, a), [1, 2, 4, 7])); - assert(equal(setIntersection(a, c, b), [1, 4, 7])); - assert(equal(setIntersection(b, a, c), [1, 4, 7])); - assert(equal(setIntersection(b, c, a), [1, 4, 7])); - assert(equal(setIntersection(c, a, b), [1, 4, 7])); - assert(equal(setIntersection(c, b, a), [1, 4, 7])); -} - -/** -Lazily computes the symmetric difference of `r1` and `r2`, -i.e. the elements that are present in exactly one of `r1` and $(D -r2). The two ranges are assumed to be sorted by `less`, and the -output is also sorted by `less`. The element types of the two -ranges must have a common type. - -If both ranges are sets (without duplicated elements), the resulting -range is going to be a set. If at least one of the ranges is a multiset, -the number of occurences of an element `x` in the resulting range is `abs(a-b)` -where `a` is the number of occurences of `x` in `r1`, `b` is the number of -occurences of `x` in `r2`, and `abs` is the absolute value. - -If both arguments are ranges of L-values of the same type then -`SetSymmetricDifference` will also be a range of L-values of -that type. - -Params: - less = Predicate the given ranges are sorted by. - r1 = The first range. - r2 = The second range. - -Returns: - A range of the symmetric difference between `r1` and `r2`. - -See_also: $(LREF setDifference) - */ -struct SetSymmetricDifference(alias less = "a < b", R1, R2) -if (isInputRange!(R1) && isInputRange!(R2)) -{ -private: - R1 r1; - R2 r2; - //bool usingR2; - alias comp = binaryFun!(less); - - void adjustPosition() - { - while (!r1.empty && !r2.empty) - { - if (comp(r1.front, r2.front) || comp(r2.front, r1.front)) - { - break; - } - // equal, pop both - r1.popFront(); - r2.popFront(); - } - } - -public: - /// - this(R1 r1, R2 r2) - { - this.r1 = r1; - this.r2 = r2; - // position to the first element - adjustPosition(); - } - - /// - void popFront() - { - assert(!empty, "Can not popFront of empty SetSymmetricDifference"); - if (r1.empty) r2.popFront(); - else if (r2.empty) r1.popFront(); - else - { - // neither is empty - if (comp(r1.front, r2.front)) - { - r1.popFront(); - } - else - { - assert(comp(r2.front, r1.front), "Elements of R1 and R2" - ~ " must be different"); - r2.popFront(); - } - } - adjustPosition(); - } - - /// - @property auto ref front() - { - assert(!empty, "Can not get the front of an empty" - ~ " SetSymmetricDifference"); - immutable chooseR1 = r2.empty || !r1.empty && comp(r1.front, r2.front); - assert(chooseR1 || r1.empty || comp(r2.front, r1.front), "Failed to" - ~ " get appropriate front"); - return chooseR1 ? r1.front : r2.front; - } - - static if (isForwardRange!R1 && isForwardRange!R2) - { - /// - @property typeof(this) save() - { - auto ret = this; - ret.r1 = r1.save; - ret.r2 = r2.save; - return ret; - } - } - - /// - ref auto opSlice() { return this; } - - /// - @property bool empty() { return r1.empty && r2.empty; } -} - -/// Ditto -SetSymmetricDifference!(less, R1, R2) -setSymmetricDifference(alias less = "a < b", R1, R2) -(R1 r1, R2 r2) -{ - return typeof(return)(r1, r2); -} - -/// -@safe unittest -{ - import std.algorithm.comparison : equal; - import std.range.primitives : isForwardRange; - - // sets - int[] a = [ 1, 2, 4, 5, 7, 9 ]; - int[] b = [ 0, 1, 2, 4, 7, 8 ]; - assert(equal(setSymmetricDifference(a, b), [0, 5, 8, 9][])); - static assert(isForwardRange!(typeof(setSymmetricDifference(a, b)))); - - //mutisets - int[] c = [1, 1, 1, 1, 2, 2, 2, 4, 5, 6]; - int[] d = [1, 1, 2, 2, 2, 2, 4, 7, 9]; - assert(equal(setSymmetricDifference(c, d), setSymmetricDifference(d, c))); - assert(equal(setSymmetricDifference(c, d), [1, 1, 2, 5, 6, 7, 9])); -} - -// https://issues.dlang.org/show_bug.cgi?id=10460 -@safe unittest -{ - import std.algorithm.comparison : equal; - - int[] a = [1, 2]; - double[] b = [2.0, 3.0]; - int[] c = [2, 3]; - - alias R1 = typeof(setSymmetricDifference(a, b)); - static assert(is(ElementType!R1 == double)); - static assert(!hasLvalueElements!R1); - - alias R2 = typeof(setSymmetricDifference(a, c)); - static assert(is(ElementType!R2 == int)); - static assert(hasLvalueElements!R2); - - assert(equal(setSymmetricDifference(a, b), [1.0, 3.0])); - assert(equal(setSymmetricDifference(a, c), [1, 3])); -} - -/++ -TODO: once SetUnion got deprecated we can provide the usual definition -(= merge + filter after uniqs) -See: https://github.com/dlang/phobos/pull/4249 -/** -Lazily computes the union of two or more ranges `rs`. The ranges -are assumed to be sorted by `less`. Elements in the output are -unique. The element types of all ranges must have a common type. - -Params: - less = Predicate the given ranges are sorted by. - rs = The ranges to compute the union for. - -Returns: - A range containing the unique union of the given ranges. - -See_Also: - $(REF merge, std,algorithm,sorting) - */ -auto setUnion(alias less = "a < b", Rs...) -(Rs rs) -{ - import std.algorithm.iteration : uniq; - import std.algorithm.sorting : merge; - return merge!(less, Rs)(rs).uniq; -} - -/// -@safe pure nothrow unittest - /// -{ - import std.algorithm.comparison : equal; - - int[] a = [1, 3, 5]; - int[] b = [2, 3, 4]; - assert(a.setUnion(b).equal([1, 2, 3, 4, 5])); -} - -@safe pure nothrow unittest -{ - import std.algorithm.comparison : equal; - - int[] a = [ 1, 2, 4, 5, 7, 9 ]; - int[] b = [ 0, 1, 2, 4, 7, 8 ]; - double[] c = [ 10.5 ]; - - assert(equal(setUnion(a, b), [0, 1, 2, 4, 5, 7, 8, 9][])); - assert(equal(setUnion(a, c, b), - [0, 1, 2, 4, 5, 7, 8, 9, 10.5][])); -} - -@safe unittest -{ - // save - import std.range : dropOne; - int[] a = [0, 1, 2]; - int[] b = [0, 3]; - auto arr = a.setUnion(b); - assert(arr.front == 0); - assert(arr.save.dropOne.front == 1); - assert(arr.front == 0); -} - -@nogc @safe pure nothrow unittest -{ - import std.algorithm.comparison : equal; - - static immutable a = [1, 3, 5]; - static immutable b = [2, 4]; - static immutable r = [1, 2, 3, 4, 5]; - assert(a.setUnion(b).equal(r)); -} - -@safe pure nothrow unittest -{ - import std.algorithm.comparison : equal; - import std.internal.test.dummyrange; - import std.range : iota; - - auto dummyResult1 = [1, 1.5, 2, 3, 4, 5, 5.5, 6, 7, 8, 9, 10]; - auto dummyResult2 = iota(1, 11); - foreach (DummyType; AllDummyRanges) - { - DummyType d; - assert(d.setUnion([1, 1.5, 5.5]).equal(dummyResult1)); - assert(d.setUnion(d).equal(dummyResult2)); - } -} -++/ diff --git a/phobos/std/algorithm/sorting.d b/phobos/std/algorithm/sorting.d deleted file mode 100644 index c5b085d..0000000 --- a/phobos/std/algorithm/sorting.d +++ /dev/null @@ -1,5030 +0,0 @@ -// Written in the D programming language. -/** -This is a submodule of $(MREF std, algorithm). -It contains generic sorting algorithms. - -$(SCRIPT inhibitQuickIndex = 1;) -$(BOOKTABLE Cheat Sheet, -$(TR $(TH Function Name) $(TH Description)) -$(T2 completeSort, - If `a = [10, 20, 30]` and `b = [40, 6, 15]`, then - `completeSort(a, b)` leaves `a = [6, 10, 15]` and `b = [20, 30, 40]`. - The range `a` must be sorted prior to the call, and as a result the - combination `$(REF chain, std,range)(a, b)` is sorted.) -$(T2 isPartitioned, - `isPartitioned!"a < 0"([-1, -2, 1, 0, 2])` returns `true` because - the predicate is `true` for a portion of the range and `false` - afterwards.) -$(T2 isSorted, - `isSorted([1, 1, 2, 3])` returns `true`.) -$(T2 isStrictlyMonotonic, - `isStrictlyMonotonic([1, 1, 2, 3])` returns `false`.) -$(T2 ordered, - `ordered(1, 1, 2, 3)` returns `true`.) -$(T2 strictlyOrdered, - `strictlyOrdered(1, 1, 2, 3)` returns `false`.) -$(T2 makeIndex, - Creates a separate index for a range.) -$(T2 merge, - Lazily merges two or more sorted ranges.) -$(T2 multiSort, - Sorts by multiple keys.) -$(T2 nextEvenPermutation, - Computes the next lexicographically greater even permutation of a range - in-place.) -$(T2 nextPermutation, - Computes the next lexicographically greater permutation of a range - in-place.) -$(T2 nthPermutation, - Computes the nth permutation of a range - in-place.) -$(T2 partialSort, - If `a = [5, 4, 3, 2, 1]`, then `partialSort(a, 3)` leaves - `a[0 .. 3] = [1, 2, 3]`. - The other elements of `a` are left in an unspecified order.) -$(T2 partition, - Partitions a range according to a unary predicate.) -$(T2 partition3, - Partitions a range according to a binary predicate in three parts (less - than, equal, greater than the given pivot). Pivot is not given as an - index, but instead as an element independent from the range's content.) -$(T2 pivotPartition, - Partitions a range according to a binary predicate in two parts: less - than or equal, and greater than or equal to the given pivot, passed as - an index in the range.) -$(T2 schwartzSort, - Sorts with the help of the $(LINK2 https://en.wikipedia.org/wiki/Schwartzian_transform, Schwartzian transform).) -$(T2 sort, - Sorts.) -$(T2 topN, - Separates the top elements in a range, akin to $(LINK2 https://en.wikipedia.org/wiki/Quickselect, Quickselect).) -$(T2 topNCopy, - Copies out the top elements of a range.) -$(T2 topNIndex, - Builds an index of the top elements of a range.) -) - -Copyright: Andrei Alexandrescu 2008-. - -License: $(HTTP boost.org/LICENSE_1_0.txt, Boost License 1.0). - -Authors: $(HTTP erdani.com, Andrei Alexandrescu) - -Source: $(PHOBOSSRC std/algorithm/sorting.d) - -Macros: -T2=$(TR $(TDNW $(LREF $1)) $(TD $+)) - */ -module std.algorithm.sorting; - -import std.algorithm.mutation : SwapStrategy; -import std.functional : unaryFun, binaryFun; -import std.range.primitives; -import std.typecons : Flag, No, Yes; -import std.meta : allSatisfy; -import std.range : SortedRange; -import std.traits; - -/** -Specifies whether the output of certain algorithm is desired in sorted -format. - -If set to `SortOutput.no`, the output should not be sorted. - -Otherwise if set to `SortOutput.yes`, the output should be sorted. - */ -alias SortOutput = Flag!"sortOutput"; - -// completeSort -/** -Sorts the random-access range `chain(lhs, rhs)` according to -predicate `less`. - -The left-hand side of the range `lhs` is assumed to be already sorted; -`rhs` is assumed to be unsorted. -The exact strategy chosen depends on the relative sizes of `lhs` and -`rhs`. Performs $(BIGOH lhs.length + rhs.length * log(rhs.length)) -(best case) to $(BIGOH (lhs.length + rhs.length) * log(lhs.length + -rhs.length)) (worst-case) evaluations of $(REF_ALTTEXT swap, swap, std,algorithm,mutation). - -Params: - less = The predicate to sort by. - ss = The swapping strategy to use. - lhs = The sorted, left-hand side of the random access range to be sorted. - rhs = The unsorted, right-hand side of the random access range to be - sorted. -*/ -void completeSort(alias less = "a < b", SwapStrategy ss = SwapStrategy.unstable, - Lhs , Rhs)(SortedRange!(Lhs, less) lhs, Rhs rhs) -if (hasLength!(Rhs) && hasSlicing!(Rhs) - && hasSwappableElements!Lhs && hasSwappableElements!Rhs) -{ - import std.algorithm.mutation : bringToFront; - import std.range : chain, assumeSorted; - // Probably this algorithm can be optimized by using in-place - // merge - auto lhsOriginal = lhs.release(); - foreach (i; 0 .. rhs.length) - { - auto sortedSoFar = chain(lhsOriginal, rhs[0 .. i]); - auto ub = assumeSorted!less(sortedSoFar).upperBound(rhs[i]); - if (!ub.length) continue; - bringToFront(ub.release(), rhs[i .. i + 1]); - } -} - -/// -@safe unittest -{ - import std.range : assumeSorted; - int[] a = [ 1, 2, 3 ]; - int[] b = [ 4, 0, 6, 5 ]; - completeSort(assumeSorted(a), b); - assert(a == [ 0, 1, 2 ]); - assert(b == [ 3, 4, 5, 6 ]); -} - -// isSorted -/** -Checks whether a $(REF_ALTTEXT forward range, isForwardRange, std,range,primitives) -is sorted according to the comparison operation `less`. Performs $(BIGOH r.length) -evaluations of `less`. - -Unlike `isSorted`, `isStrictlyMonotonic` does not allow for equal values, -i.e. values for which both `less(a, b)` and `less(b, a)` are false. - -With either function, the predicate must be a strict ordering just like with -`isSorted`. For example, using `"a <= b"` instead of `"a < b"` is -incorrect and will cause failed assertions. - -Params: - less = Predicate the range should be sorted by. - r = Forward range to check for sortedness. - -Returns: - `true` if the range is sorted, false otherwise. `isSorted` allows - duplicates, `isStrictlyMonotonic` not. -*/ -bool isSorted(alias less = "a < b", Range)(Range r) -if (isForwardRange!(Range)) -{ - if (r.empty) return true; - - static if (isRandomAccessRange!Range && hasLength!Range) - { - immutable limit = r.length - 1; - foreach (i; 0 .. limit) - { - if (!binaryFun!less(r[i + 1], r[i])) continue; - assert( - !binaryFun!less(r[i], r[i + 1]), - "Predicate for isSorted is not antisymmetric. Both" ~ - " pred(a, b) and pred(b, a) are true for certain values."); - return false; - } - } - else - { - auto ahead = r.save; - ahead.popFront(); - size_t i; - - for (; !ahead.empty; ahead.popFront(), r.popFront(), ++i) - { - if (!binaryFun!less(ahead.front, r.front)) continue; - // Check for antisymmetric predicate - assert( - !binaryFun!less(r.front, ahead.front), - "Predicate for isSorted is not antisymmetric. Both" ~ - " pred(a, b) and pred(b, a) are true for certain values."); - return false; - } - } - return true; -} - -/// -@safe unittest -{ - assert([1, 1, 2].isSorted); - // strictly monotonic doesn't allow duplicates - assert(![1, 1, 2].isStrictlyMonotonic); - - int[] arr = [4, 3, 2, 1]; - assert(!isSorted(arr)); - assert(!isStrictlyMonotonic(arr)); - - assert(isSorted!"a > b"(arr)); - assert(isStrictlyMonotonic!"a > b"(arr)); - - sort(arr); - assert(isSorted(arr)); - assert(isStrictlyMonotonic(arr)); -} - -@safe unittest -{ - import std.conv : to; - - // https://issues.dlang.org/show_bug.cgi?id=9457 - auto x = "abcd"; - assert(isSorted(x)); - auto y = "acbd"; - assert(!isSorted(y)); - - int[] a = [1, 2, 3]; - assert(isSorted(a)); - int[] b = [1, 3, 2]; - assert(!isSorted(b)); - - // ignores duplicates - int[] c = [1, 1, 2]; - assert(isSorted(c)); - - dchar[] ds = "コーヒーが好きです"d.dup; - sort(ds); - string s = to!string(ds); - assert(isSorted(ds)); // random-access - assert(isSorted(s)); // bidirectional -} - -@nogc @safe nothrow pure unittest -{ - static immutable a = [1, 2, 3]; - assert(a.isSorted); -} - -/// ditto -bool isStrictlyMonotonic(alias less = "a < b", Range)(Range r) -if (isForwardRange!Range) -{ - import std.algorithm.searching : findAdjacent; - return findAdjacent!((a,b) => !binaryFun!less(a,b))(r).empty; -} - -@safe unittest -{ - import std.conv : to; - - assert("abcd".isStrictlyMonotonic); - assert(!"aacd".isStrictlyMonotonic); - assert(!"acb".isStrictlyMonotonic); - - assert([1, 2, 3].isStrictlyMonotonic); - assert(![1, 3, 2].isStrictlyMonotonic); - assert(![1, 1, 2].isStrictlyMonotonic); - - // ー occurs twice -> can't be strict - dchar[] ds = "コーヒーが好きです"d.dup; - sort(ds); - string s = to!string(ds); - assert(!isStrictlyMonotonic(ds)); // random-access - assert(!isStrictlyMonotonic(s)); // bidirectional - - dchar[] ds2 = "コーヒが好きです"d.dup; - sort(ds2); - string s2 = to!string(ds2); - assert(isStrictlyMonotonic(ds2)); // random-access - assert(isStrictlyMonotonic(s2)); // bidirectional -} - -@nogc @safe nothrow pure unittest -{ - static immutable a = [1, 2, 3]; - assert(a.isStrictlyMonotonic); -} - -/** -Like `isSorted`, returns `true` if the given `values` are ordered -according to the comparison operation `less`. Unlike `isSorted`, takes values -directly instead of structured in a range. - -`ordered` allows repeated values, e.g. `ordered(1, 1, 2)` is `true`. To verify -that the values are ordered strictly monotonically, use `strictlyOrdered`; -`strictlyOrdered(1, 1, 2)` is `false`. - -With either function, the predicate must be a strict ordering. For example, -using `"a <= b"` instead of `"a < b"` is incorrect and will cause failed -assertions. - -Params: - values = The tested value - less = The comparison predicate - -Returns: - `true` if the values are ordered; `ordered` allows for duplicates, - `strictlyOrdered` does not. -*/ - -bool ordered(alias less = "a < b", T...)(T values) -if ((T.length == 2 && is(typeof(binaryFun!less(values[1], values[0])) : bool)) - || - (T.length > 2 && is(typeof(ordered!less(values[0 .. 1 + $ / 2]))) - && is(typeof(ordered!less(values[$ / 2 .. $])))) - ) -{ - foreach (i, _; T[0 .. $ - 1]) - { - if (binaryFun!less(values[i + 1], values[i])) - { - assert(!binaryFun!less(values[i], values[i + 1]), - __FUNCTION__ ~ ": incorrect non-strict predicate."); - return false; - } - } - return true; -} - -/// ditto -bool strictlyOrdered(alias less = "a < b", T...)(T values) -if (is(typeof(ordered!less(values)))) -{ - foreach (i, _; T[0 .. $ - 1]) - { - if (!binaryFun!less(values[i], values[i + 1])) - { - return false; - } - assert(!binaryFun!less(values[i + 1], values[i]), - __FUNCTION__ ~ ": incorrect non-strict predicate."); - } - return true; -} - -/// -@safe unittest -{ - assert(ordered(42, 42, 43)); - assert(!strictlyOrdered(43, 42, 45)); - assert(ordered(42, 42, 43)); - assert(!strictlyOrdered(42, 42, 43)); - assert(!ordered(43, 42, 45)); - // Ordered lexicographically - assert(ordered("Jane", "Jim", "Joe")); - assert(strictlyOrdered("Jane", "Jim", "Joe")); - // Incidentally also ordered by length decreasing - assert(ordered!((a, b) => a.length > b.length)("Jane", "Jim", "Joe")); - // ... but not strictly so: "Jim" and "Joe" have the same length - assert(!strictlyOrdered!((a, b) => a.length > b.length)("Jane", "Jim", "Joe")); -} - -// partition -/** -Partitions a range in two using the given `predicate`. - -Specifically, reorders the range `r = [left, right$(RPAREN)` using $(REF_ALTTEXT swap, swap, std,algorithm,mutation) -such that all elements `i` for which `predicate(i)` is `true` come -before all elements `j` for which `predicate(j)` returns `false`. - -Performs $(BIGOH r.length) (if unstable or semistable) or $(BIGOH -r.length * log(r.length)) (if stable) evaluations of `less` and $(REF_ALTTEXT swap, swap, std,algorithm,mutation). -The unstable version computes the minimum possible evaluations of `swap` -(roughly half of those performed by the semistable version). - -Params: - predicate = The predicate to partition by. - ss = The swapping strategy to employ. - r = The random-access range to partition. - -Returns: - -The right part of `r` after partitioning. - -If `ss == SwapStrategy.stable`, `partition` preserves the relative -ordering of all elements `a`, `b` in `r` for which -`predicate(a) == predicate(b)`. -If `ss == SwapStrategy.semistable`, `partition` preserves -the relative ordering of all elements `a`, `b` in the left part of `r` -for which `predicate(a) == predicate(b)`. -*/ -Range partition(alias predicate, SwapStrategy ss, Range)(Range r) -if (ss == SwapStrategy.stable && isRandomAccessRange!(Range) && hasLength!Range && - hasSlicing!Range && hasSwappableElements!Range) -{ - import std.algorithm.mutation : bringToFront; - - alias pred = unaryFun!(predicate); - if (r.empty) return r; - - if (r.length == 1) - { - if (pred(r.front)) r.popFront(); - return r; - } - const middle = r.length / 2; - alias recurse = .partition!(pred, ss, Range); - auto lower = recurse(r[0 .. middle]); - auto upper = recurse(r[middle .. r.length]); - bringToFront(lower, r[middle .. r.length - upper.length]); - return r[r.length - lower.length - upper.length .. r.length]; -} - -///ditto -Range partition(alias predicate, SwapStrategy ss = SwapStrategy.unstable, Range)(Range r) -if (ss != SwapStrategy.stable && isInputRange!Range && hasSwappableElements!Range) -{ - import std.algorithm.mutation : swap; - alias pred = unaryFun!(predicate); - - static if (ss == SwapStrategy.semistable) - { - if (r.empty) return r; - - for (; !r.empty; r.popFront()) - { - // skip the initial portion of "correct" elements - if (pred(r.front)) continue; - // hit the first "bad" element - auto result = r; - for (r.popFront(); !r.empty; r.popFront()) - { - if (!pred(r.front)) continue; - swap(result.front, r.front); - result.popFront(); - } - return result; - } - - return r; - } - else - { - // Inspired from www.stepanovpapers.com/PAM3-partition_notes.pdf, - // section "Bidirectional Partition Algorithm (Hoare)" - static if (isDynamicArray!Range) - { - import std.algorithm.mutation : swapAt; - // For dynamic arrays prefer index-based manipulation - if (!r.length) return r; - size_t lo = 0, hi = r.length - 1; - for (;;) - { - for (;;) - { - if (lo > hi) return r[lo .. r.length]; - if (!pred(r[lo])) break; - ++lo; - } - // found the left bound - assert(lo <= hi, "lo must be <= hi"); - for (;;) - { - if (lo == hi) return r[lo .. r.length]; - if (pred(r[hi])) break; - --hi; - } - // found the right bound, swap & make progress - r.swapAt(lo++, hi--); - } - } - else - { - import std.algorithm.mutation : swap; - auto result = r; - for (;;) - { - for (;;) - { - if (r.empty) return result; - if (!pred(r.front)) break; - r.popFront(); - result.popFront(); - } - // found the left bound - assert(!r.empty, "r must not be empty"); - for (;;) - { - if (pred(r.back)) break; - r.popBack(); - if (r.empty) return result; - } - // found the right bound, swap & make progress - static if (is(typeof(swap(r.front, r.back)))) - { - swap(r.front, r.back); - } - else - { - auto t1 = r.moveFront(), t2 = r.moveBack(); - r.front = t2; - r.back = t1; - } - r.popFront(); - result.popFront(); - r.popBack(); - } - } - } -} - -/// -@safe unittest -{ - import std.algorithm.mutation : SwapStrategy; - import std.algorithm.searching : count, find; - import std.conv : text; - import std.range.primitives : empty; - - auto Arr = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]; - auto arr = Arr.dup; - static bool even(int a) { return (a & 1) == 0; } - // Partition arr such that even numbers come first - auto r = partition!(even)(arr); - // Now arr is separated in evens and odds. - // Numbers may have become shuffled due to instability - assert(r == arr[5 .. $]); - assert(count!(even)(arr[0 .. 5]) == 5); - assert(find!(even)(r).empty); - - // Can also specify the predicate as a string. - // Use 'a' as the predicate argument name - arr[] = Arr[]; - r = partition!(q{(a & 1) == 0})(arr); - assert(r == arr[5 .. $]); - - // Now for a stable partition: - arr[] = Arr[]; - r = partition!(q{(a & 1) == 0}, SwapStrategy.stable)(arr); - // Now arr is [2 4 6 8 10 1 3 5 7 9], and r points to 1 - assert(arr == [2, 4, 6, 8, 10, 1, 3, 5, 7, 9] && r == arr[5 .. $]); - - // In case the predicate needs to hold its own state, use a delegate: - arr[] = Arr[]; - int x = 3; - // Put stuff greater than 3 on the left - bool fun(int a) { return a > x; } - r = partition!(fun, SwapStrategy.semistable)(arr); - // Now arr is [4 5 6 7 8 9 10 2 3 1] and r points to 2 - assert(arr == [4, 5, 6, 7, 8, 9, 10, 2, 3, 1] && r == arr[7 .. $]); -} - -@safe unittest -{ - import std.algorithm.internal : rndstuff; - static bool even(int a) { return (a & 1) == 0; } - - // test with random data - auto a = rndstuff!int(); - partition!even(a); - assert(isPartitioned!even(a)); - auto b = rndstuff!string(); - partition!`a.length < 5`(b); - assert(isPartitioned!`a.length < 5`(b)); -} - -// pivotPartition -/** -Partitions `r` around `pivot` using comparison function `less`, algorithm akin -to $(LINK2 https://en.wikipedia.org/wiki/Quicksort#Hoare_partition_scheme, -Hoare partition). - -Specifically, permutes elements of `r` and returns -an index `k < r.length` such that: - -$(UL - -$(LI `r[pivot]` is swapped to `r[k]`) - -$(LI All elements `e` in subrange `r[0 .. k]` satisfy `!less(r[k], e)` -(i.e. `r[k]` is greater than or equal to each element to its left according to -predicate `less`)) - -$(LI All elements `e` in subrange `r[k .. $]` satisfy `!less(e, r[k])` -(i.e. `r[k]` is less than or equal to each element to its right -according to predicate `less`))) - -If `r` contains equivalent elements, multiple permutations of `r` satisfy these -constraints. In such cases, `pivotPartition` attempts to distribute equivalent -elements fairly to the left and right of `k` such that `k` stays close to $(D -r.length / 2). - -Params: -less = The predicate used for comparison, modeled as a - $(LINK2 https://en.wikipedia.org/wiki/Weak_ordering#Strict_weak_orderings, - strict weak ordering) (irreflexive, antisymmetric, transitive, and implying a transitive - equivalence) -r = The range being partitioned -pivot = The index of the pivot for partitioning, must be less than `r.length` or -`0` if `r.length` is `0` - -Returns: -The new position of the pivot - -See_Also: -$(HTTP jgrcs.info/index.php/jgrcs/article/view/142, Engineering of a Quicksort -Partitioning Algorithm), D. Abhyankar, Journal of Global Research in Computer -Science, February 2011. $(HTTPS youtube.com/watch?v=AxnotgLql0k, ACCU 2016 -Keynote), Andrei Alexandrescu. -*/ -size_t pivotPartition(alias less = "a < b", Range) -(Range r, size_t pivot) -if (isRandomAccessRange!Range && hasLength!Range && hasSlicing!Range && hasAssignableElements!Range) -{ - assert(pivot < r.length || r.length == 0 && pivot == 0, "pivot must be" - ~ " less than the length of r or r must be empty and pivot zero"); - if (r.length <= 1) return 0; - import std.algorithm.mutation : swapAt, move; - alias lt = binaryFun!less; - - // Pivot at the front - r.swapAt(pivot, 0); - - // Fork implementation depending on nothrow copy, assignment, and - // comparison. If all of these are nothrow, use the specialized - // implementation discussed at https://youtube.com/watch?v=AxnotgLql0k. - static if (is(typeof( - () nothrow { auto x = r.front; x = r.front; return lt(x, x); } - ))) - { - auto p = r[0]; - // Plant the pivot in the end as well as a sentinel - size_t lo = 0, hi = r.length - 1; - auto save = r.moveAt(hi); - r[hi] = p; // Vacancy is in r[$ - 1] now - // Start process - for (;;) - { - // Loop invariant - version (StdUnittest) - { - // this used to import std.algorithm.all, but we want to save - // imports when unittests are enabled if possible. - foreach (x; r[0 .. lo]) - assert(!lt(p, x), "p must not be less than x"); - foreach (x; r[hi+1 .. r.length]) - assert(!lt(x, p), "x must not be less than p"); - } - do ++lo; while (lt(r[lo], p)); - r[hi] = r[lo]; - // Vacancy is now in r[lo] - do --hi; while (lt(p, r[hi])); - if (lo >= hi) break; - r[lo] = r[hi]; - // Vacancy is not in r[hi] - } - // Fixup - assert(lo - hi <= 2, "Following compare not possible"); - assert(!lt(p, r[hi]), "r[hi] must not be less than p"); - if (lo == hi + 2) - { - assert(!lt(r[hi + 1], p), "r[hi + 1] must not be less than p"); - r[lo] = r[hi + 1]; - --lo; - } - r[lo] = save; - if (lt(p, save)) --lo; - assert(!lt(p, r[lo]), "r[lo] must not be less than p"); - } - else - { - size_t lo = 1, hi = r.length - 1; - loop: for (;; lo++, hi--) - { - for (;; ++lo) - { - if (lo > hi) break loop; - if (!lt(r[lo], r[0])) break; - } - // found the left bound: r[lo] >= r[0] - assert(lo <= hi, "lo must be less or equal than hi"); - for (;; --hi) - { - if (lo >= hi) break loop; - if (!lt(r[0], r[hi])) break; - } - // found the right bound: r[hi] <= r[0], swap & make progress - assert(!lt(r[lo], r[hi]), "r[lo] must not be less than r[hi]"); - r.swapAt(lo, hi); - } - --lo; - } - r.swapAt(lo, 0); - return lo; -} - -/// -@safe nothrow unittest -{ - int[] a = [5, 3, 2, 6, 4, 1, 3, 7]; - size_t pivot = pivotPartition(a, a.length / 2); - import std.algorithm.searching : all; - assert(a[0 .. pivot].all!(x => x <= a[pivot])); - assert(a[pivot .. $].all!(x => x >= a[pivot])); -} - -@safe unittest -{ - void test(alias less)() - { - int[] a; - size_t pivot; - - a = [-9, -4, -2, -2, 9]; - pivot = pivotPartition!less(a, a.length / 2); - import std.algorithm.searching : all; - assert(a[0 .. pivot].all!(x => x <= a[pivot])); - assert(a[pivot .. $].all!(x => x >= a[pivot])); - - a = [9, 2, 8, -5, 5, 4, -8, -4, 9]; - pivot = pivotPartition!less(a, a.length / 2); - assert(a[0 .. pivot].all!(x => x <= a[pivot])); - assert(a[pivot .. $].all!(x => x >= a[pivot])); - - a = [ 42 ]; - pivot = pivotPartition!less(a, a.length / 2); - assert(pivot == 0); - assert(a == [ 42 ]); - - a = [ 43, 42 ]; - pivot = pivotPartition!less(a, 0); - assert(pivot == 1); - assert(a == [ 42, 43 ]); - - a = [ 43, 42 ]; - pivot = pivotPartition!less(a, 1); - assert(pivot == 0); - assert(a == [ 42, 43 ]); - - a = [ 42, 42 ]; - pivot = pivotPartition!less(a, 0); - assert(pivot == 0 || pivot == 1); - assert(a == [ 42, 42 ]); - pivot = pivotPartition!less(a, 1); - assert(pivot == 0 || pivot == 1); - assert(a == [ 42, 42 ]); - - import std.algorithm.iteration : map; - import std.array : array; - import std.format : format; - import std.random : Random, uniform, Xorshift; - import std.range : iota; - auto s = 123_456_789; - auto g = Xorshift(s); - a = iota(0, uniform(1, 1000, g)) - .map!(_ => uniform(-1000, 1000, g)) - .array; - pivot = pivotPartition!less(a, a.length / 2); - assert(a[0 .. pivot].all!(x => x <= a[pivot]), "RNG seed: %d".format(s)); - assert(a[pivot .. $].all!(x => x >= a[pivot]), "RNG seed: %d".format(s)); - } - test!"a < b"; - static bool myLess(int a, int b) - { - static bool bogus; - if (bogus) throw new Exception(""); // just to make it no-nothrow - return a < b; - } - test!myLess; -} - -/** -Params: - pred = The predicate that the range should be partitioned by. - r = The range to check. -Returns: `true` if `r` is partitioned according to predicate `pred`. - */ -bool isPartitioned(alias pred, Range)(Range r) -if (isForwardRange!(Range)) -{ - for (; !r.empty; r.popFront()) - { - if (unaryFun!(pred)(r.front)) continue; - for (r.popFront(); !r.empty; r.popFront()) - { - if (unaryFun!(pred)(r.front)) return false; - } - break; - } - return true; -} - -/// -@safe unittest -{ - int[] r = [ 1, 3, 5, 7, 8, 2, 4, ]; - assert(isPartitioned!"a & 1"(r)); -} - -// partition3 -/** -Rearranges elements in `r` in three adjacent ranges and returns -them. - -The first and leftmost range only contains elements in `r` -less than `pivot`. The second and middle range only contains -elements in `r` that are equal to `pivot`. Finally, the third -and rightmost range only contains elements in `r` that are greater -than `pivot`. The less-than test is defined by the binary function -`less`. - -Params: - less = The predicate to use for the rearrangement. - ss = The swapping strategy to use. - r = The random-access range to rearrange. - pivot = The pivot element. - -Returns: - A $(REF Tuple, std,typecons) of the three resulting ranges. These ranges are - slices of the original range. - -BUGS: stable `partition3` has not been implemented yet. - */ -auto partition3(alias less = "a < b", SwapStrategy ss = SwapStrategy.unstable, Range, E) -(Range r, E pivot) -if (ss == SwapStrategy.unstable && isRandomAccessRange!Range - && hasSwappableElements!Range && hasLength!Range && hasSlicing!Range - && is(typeof(binaryFun!less(r.front, pivot)) == bool) - && is(typeof(binaryFun!less(pivot, r.front)) == bool) - && is(typeof(binaryFun!less(r.front, r.front)) == bool)) -{ - // The algorithm is described in "Engineering a sort function" by - // Jon Bentley et al, pp 1257. - - import std.algorithm.comparison : min; - import std.algorithm.mutation : swap, swapAt, swapRanges; - import std.typecons : tuple; - - alias lessFun = binaryFun!less; - size_t i, j, k = r.length, l = k; - - bigloop: - for (;;) - { - for (;; ++j) - { - if (j == k) break bigloop; - assert(j < r.length, "j must be less than r.length"); - if (lessFun(r[j], pivot)) continue; - if (lessFun(pivot, r[j])) break; - r.swapAt(i++, j); - } - assert(j < k, "j must be less than k"); - for (;;) - { - assert(k > 0, "k must be positive"); - if (!lessFun(pivot, r[--k])) - { - if (lessFun(r[k], pivot)) break; - r.swapAt(k, --l); - } - if (j == k) break bigloop; - } - // Here we know r[j] > pivot && r[k] < pivot - r.swapAt(j++, k); - } - - // Swap the equal ranges from the extremes into the middle - auto strictlyLess = j - i, strictlyGreater = l - k; - auto swapLen = min(i, strictlyLess); - swapRanges(r[0 .. swapLen], r[j - swapLen .. j]); - swapLen = min(r.length - l, strictlyGreater); - swapRanges(r[k .. k + swapLen], r[r.length - swapLen .. r.length]); - return tuple(r[0 .. strictlyLess], - r[strictlyLess .. r.length - strictlyGreater], - r[r.length - strictlyGreater .. r.length]); -} - -/// -@safe unittest -{ - auto a = [ 8, 3, 4, 1, 4, 7, 4 ]; - auto pieces = partition3(a, 4); - assert(pieces[0] == [ 1, 3 ]); - assert(pieces[1] == [ 4, 4, 4 ]); - assert(pieces[2] == [ 8, 7 ]); -} - -@safe unittest -{ - import std.random : Random = Xorshift, uniform; - - immutable uint[] seeds = [3923355730, 1927035882]; - foreach (s; seeds) - { - auto r = Random(s); - auto a = new int[](uniform(0, 100, r)); - foreach (ref e; a) - { - e = uniform(0, 50, r); - } - auto pieces = partition3(a, 25); - assert(pieces[0].length + pieces[1].length + pieces[2].length == a.length); - foreach (e; pieces[0]) - { - assert(e < 25); - } - foreach (e; pieces[1]) - { - assert(e == 25); - } - foreach (e; pieces[2]) - { - assert(e > 25); - } - } -} - -// makeIndex -/** -Computes an index for `r` based on the comparison `less`. - -The index is a sorted array of pointers or indices into the original -range. This technique is similar to sorting, but it is more flexible -because (1) it allows "sorting" of immutable collections, (2) allows -binary search even if the original collection does not offer random -access, (3) allows multiple indexes, each on a different predicate, -and (4) may be faster when dealing with large objects. However, using -an index may also be slower under certain circumstances due to the -extra indirection, and is always larger than a sorting-based solution -because it needs space for the index in addition to the original -collection. The complexity is the same as `sort`'s. - -The first overload of `makeIndex` writes to a range containing -pointers, and the second writes to a range containing offsets. The -first overload requires `Range` to be a -$(REF_ALTTEXT forward range, isForwardRange, std,range,primitives), and the -latter requires it to be a random-access range. - -`makeIndex` overwrites its second argument with the result, but -never reallocates it. - -Params: - less = The comparison to use. - ss = The swapping strategy. - r = The range to index. - index = The resulting index. - -Returns: The pointer-based version returns a `SortedRange` wrapper -over index, of type -`SortedRange!(RangeIndex, (a, b) => binaryFun!less(*a, *b))` -thus reflecting the ordering of the -index. The index-based version returns `void` because the ordering -relation involves not only `index` but also `r`. - -Throws: If the second argument's length is less than that of the range -indexed, an exception is thrown. -*/ -SortedRange!(RangeIndex, (a, b) => binaryFun!less(*a, *b)) -makeIndex( - alias less = "a < b", - SwapStrategy ss = SwapStrategy.unstable, - Range, - RangeIndex) -(Range r, RangeIndex index) -if (isForwardRange!(Range) && isRandomAccessRange!(RangeIndex) - && is(ElementType!(RangeIndex) : ElementType!(Range)*) && hasAssignableElements!RangeIndex) -{ - import std.algorithm.internal : addressOf; - import std.exception : enforce; - - // assume collection already ordered - size_t i; - for (; !r.empty; r.popFront(), ++i) - index[i] = addressOf(r.front); - enforce(index.length == i); - // sort the index - sort!((a, b) => binaryFun!less(*a, *b), ss)(index); - return typeof(return)(index); -} - -/// Ditto -void makeIndex( - alias less = "a < b", - SwapStrategy ss = SwapStrategy.unstable, - Range, - RangeIndex) -(Range r, RangeIndex index) -if (isRandomAccessRange!Range && !isInfinite!Range && - isRandomAccessRange!RangeIndex && !isInfinite!RangeIndex && - isIntegral!(ElementType!RangeIndex) && hasAssignableElements!RangeIndex) -{ - import std.conv : to; - import std.exception : enforce; - - alias IndexType = Unqual!(ElementType!RangeIndex); - enforce(r.length == index.length, - "r and index must be same length for makeIndex."); - static if (IndexType.sizeof < size_t.sizeof) - { - enforce(r.length <= size_t(1) + IndexType.max, "Cannot create an index with " ~ - "element type " ~ IndexType.stringof ~ " with length " ~ - to!string(r.length) ~ "."); - } - - // Use size_t as loop index to avoid overflow on ++i, - // e.g. when squeezing 256 elements into a ubyte index. - foreach (size_t i; 0 .. r.length) - index[i] = cast(IndexType) i; - - // sort the index - sort!((a, b) => binaryFun!less(r[cast(size_t) a], r[cast(size_t) b]), ss) - (index); -} - -/// -@system unittest -{ - immutable(int[]) arr = [ 2, 3, 1, 5, 0 ]; - // index using pointers - auto index1 = new immutable(int)*[arr.length]; - makeIndex!("a < b")(arr, index1); - assert(isSorted!("*a < *b")(index1)); - // index using offsets - auto index2 = new size_t[arr.length]; - makeIndex!("a < b")(arr, index2); - assert(isSorted! - ((size_t a, size_t b){ return arr[a] < arr[b];}) - (index2)); -} - -@system unittest -{ - immutable(int)[] arr = [ 2, 3, 1, 5, 0 ]; - // index using pointers - auto index1 = new immutable(int)*[arr.length]; - alias ImmRange = typeof(arr); - alias ImmIndex = typeof(index1); - static assert(isForwardRange!(ImmRange)); - static assert(isRandomAccessRange!(ImmIndex)); - static assert(!isIntegral!(ElementType!(ImmIndex))); - static assert(is(ElementType!(ImmIndex) : ElementType!(ImmRange)*)); - makeIndex!("a < b")(arr, index1); - assert(isSorted!("*a < *b")(index1)); - - // index using offsets - auto index2 = new long[arr.length]; - makeIndex(arr, index2); - assert(isSorted! - ((long a, long b){ - return arr[cast(size_t) a] < arr[cast(size_t) b]; - })(index2)); - - // index strings using offsets - string[] arr1 = ["I", "have", "no", "chocolate"]; - auto index3 = new byte[arr1.length]; - makeIndex(arr1, index3); - assert(isSorted! - ((byte a, byte b){ return arr1[a] < arr1[b];}) - (index3)); -} - -@safe unittest -{ - import std.algorithm.comparison : equal; - import std.range : iota; - - ubyte[256] index = void; - iota(256).makeIndex(index[]); - assert(index[].equal(iota(256))); - byte[128] sindex = void; - iota(128).makeIndex(sindex[]); - assert(sindex[].equal(iota(128))); - - auto index2 = new uint[10]; - 10.iota.makeIndex(index2); - assert(index2.equal(10.iota)); -} - -struct Merge(alias less = "a < b", Rs...) -if (Rs.length >= 2 && - allSatisfy!(isInputRange, Rs) && - !is(CommonType!(staticMap!(ElementType, Rs)) == void)) -{ - public Rs source; - private size_t _lastFrontIndex = size_t.max; - static if (isBidirectional) - { - private size_t _lastBackIndex = size_t.max; // `size_t.max` means uninitialized, - } - - import std.functional : binaryFun; - import std.meta : anySatisfy; - import std.traits : isCopyable; - - private alias comp = binaryFun!less; - private alias ElementType = CommonType!(staticMap!(.ElementType, Rs)); - private enum isBidirectional = allSatisfy!(isBidirectionalRange, staticMap!(Unqual, Rs)); - - debug private enum canCheckSortedness = isCopyable!ElementType && !hasAliasing!ElementType; - - this(Rs source) - { - this.source = source; - this._lastFrontIndex = frontIndex; - } - - static if (anySatisfy!(isInfinite, Rs)) - { - enum bool empty = false; // propagate infiniteness - } - else - { - @property bool empty() - { - return _lastFrontIndex == size_t.max; - } - } - - @property auto ref front() - { - final switch (_lastFrontIndex) - { - foreach (i, _; Rs) - { - case i: - assert(!source[i].empty, "Can not get front of empty Merge"); - return source[i].front; - } - } - } - - private size_t frontIndex() - { - size_t bestIndex = size_t.max; // indicate undefined - Unqual!ElementType bestElement; - foreach (i, _; Rs) - { - if (source[i].empty) continue; - if (bestIndex == size_t.max || // either this is the first or - comp(source[i].front, bestElement)) - { - bestIndex = i; - bestElement = source[i].front; - } - } - return bestIndex; - } - - void popFront() - { - sw: final switch (_lastFrontIndex) - { - foreach (i, R; Rs) - { - case i: - debug static if (canCheckSortedness) - { - ElementType previousFront = source[i].front(); - } - source[i].popFront(); - debug static if (canCheckSortedness) - { - if (!source[i].empty) - { - assert(!comp(source[i].front, previousFront), - "Input " ~ i.stringof ~ " is unsorted"); // @nogc - } - } - break sw; - } - } - _lastFrontIndex = frontIndex; - } - - static if (isBidirectional) - { - @property auto ref back() - { - if (_lastBackIndex == size_t.max) - { - this._lastBackIndex = backIndex; // lazy initialization - } - final switch (_lastBackIndex) - { - foreach (i, _; Rs) - { - case i: - assert(!source[i].empty, "Can not get back of empty Merge"); - return source[i].back; - } - } - } - - private size_t backIndex() - { - size_t bestIndex = size_t.max; // indicate undefined - Unqual!ElementType bestElement; - foreach (i, _; Rs) - { - if (source[i].empty) continue; - if (bestIndex == size_t.max || // either this is the first or - comp(bestElement, source[i].back)) - { - bestIndex = i; - bestElement = source[i].back; - } - } - return bestIndex; - } - - void popBack() - { - if (_lastBackIndex == size_t.max) - { - this._lastBackIndex = backIndex; // lazy initialization - } - sw: final switch (_lastBackIndex) - { - foreach (i, R; Rs) - { - case i: - debug static if (canCheckSortedness) - { - ElementType previousBack = source[i].back(); - } - source[i].popBack(); - debug static if (canCheckSortedness) - { - if (!source[i].empty) - { - assert(!comp(previousBack, source[i].back), - "Input " ~ i.stringof ~ " is unsorted"); // @nogc - } - } - break sw; - } - } - _lastBackIndex = backIndex; - if (_lastBackIndex == size_t.max) // if emptied - { - _lastFrontIndex = size_t.max; - } - } - } - - static if (allSatisfy!(isForwardRange, staticMap!(Unqual, Rs))) - { - @property auto save() - { - auto result = this; - foreach (i, _; Rs) - { - result.source[i] = result.source[i].save; - } - return result; - } - } - - static if (allSatisfy!(hasLength, Rs)) - { - @property size_t length() - { - size_t result; - foreach (i, _; Rs) - { - result += source[i].length; - } - return result; - } - - alias opDollar = length; - } -} - -/** - Merge multiple sorted ranges `rs` with less-than predicate function `pred` - into one single sorted output range containing the sorted union of the - elements of inputs. - - Duplicates are not eliminated, meaning that the total - number of elements in the output is the sum of all elements in the ranges - passed to it; the `length` member is offered if all inputs also have - `length`. The element types of all the inputs must have a common type - `CommonType`. - -Params: - less = Predicate the given ranges are sorted by. - rs = The ranges to compute the union for. - -Returns: - A range containing the union of the given ranges. - -Details: - -All of its inputs are assumed to be sorted. This can mean that inputs are - instances of $(REF SortedRange, std,range). Use the result of $(REF sort, - std,algorithm,sorting), or $(REF assumeSorted, std,range) to merge ranges - known to be sorted (show in the example below). Note that there is currently - no way of ensuring that two or more instances of $(REF SortedRange, - std,range) are sorted using a specific comparison function `pred`. Therefore - no checking is done here to assure that all inputs `rs` are instances of - $(REF SortedRange, std,range). - - This algorithm is lazy, doing work progressively as elements are pulled off - the result. - - Time complexity is proportional to the sum of element counts over all inputs. - - If all inputs have the same element type and offer it by `ref`, output - becomes a range with mutable `front` (and `back` where appropriate) that - reflects in the original inputs. - - If any of the inputs `rs` is infinite so is the result (`empty` being always - `false`). - -See_Also: $(REF multiwayMerge, std,algorithm,setops) for an analogous function - that merges a dynamic number of ranges. -*/ -Merge!(less, Rs) merge(alias less = "a < b", Rs...)(Rs rs) -if (Rs.length >= 2 && - allSatisfy!(isInputRange, Rs) && - !is(CommonType!(staticMap!(ElementType, Rs)) == void)) -{ - return typeof(return)(rs); -} - -/// -@safe pure nothrow unittest -{ - import std.algorithm.comparison : equal; - import std.range : retro; - - int[] a = [1, 3, 5]; - int[] b = [2, 3, 4]; - - assert(a.merge(b).equal([1, 2, 3, 3, 4, 5])); - assert(a.merge(b).retro.equal([5, 4, 3, 3, 2, 1])); -} - -@safe pure nothrow unittest -{ - import std.algorithm.comparison : equal; - - int[] a = [ 1, 2, 4, 5, 7, 9 ]; - int[] b = [ 0, 1, 2, 4, 7, 8 ]; - double[] c = [ 10.5 ]; - - assert(merge(a, b).length == a.length + b.length); - assert(equal(merge(a, b), [0, 1, 1, 2, 2, 4, 4, 5, 7, 7, 8, 9][])); - assert(equal(merge(a, c, b), - [0, 1, 1, 2, 2, 4, 4, 5, 7, 7, 8, 9, 10.5][])); - auto u = merge(a, b); - u.front--; - assert(equal(u, [-1, 1, 1, 2, 2, 4, 4, 5, 7, 7, 8, 9][])); -} - -@safe pure nothrow unittest -{ - // save - import std.range : dropOne; - int[] a = [1, 2]; - int[] b = [0, 3]; - auto arr = a.merge(b); - assert(arr.front == 0); - assert(arr.save.dropOne.front == 1); - assert(arr.front == 0); -} - -@safe pure nothrow unittest -{ - import std.algorithm.comparison : equal; - import std.internal.test.dummyrange; - - auto dummyResult1 = [1, 1, 1.5, 2, 3, 4, 5, 5.5, 6, 7, 8, 9, 10]; - auto dummyResult2 = [1, 1, 2, 2, 3, 3, 4, 4, 5, 5, - 6, 6, 7, 7, 8, 8, 9, 9, 10, 10]; - foreach (DummyType; AllDummyRanges) - { - DummyType d; - assert(d.merge([1, 1.5, 5.5]).equal(dummyResult1)); - assert(d.merge(d).equal(dummyResult2)); - } -} - -@nogc @safe pure nothrow unittest -{ - import std.algorithm.comparison : equal; - - static immutable a = [1, 3, 5]; - static immutable b = [2, 3, 4]; - static immutable r = [1, 2, 3, 3, 4, 5]; - assert(a.merge(b).equal(r)); -} - -/// test bi-directional access and common type -@safe pure nothrow unittest -{ - import std.algorithm.comparison : equal; - import std.range : retro; - import std.traits : CommonType; - - alias S = short; - alias I = int; - alias D = double; - - S[] a = [1, 2, 3]; - I[] b = [50, 60]; - D[] c = [10, 20, 30, 40]; - - auto m = merge(a, b, c); - - static assert(is(typeof(m.front) == CommonType!(S, I, D))); - - assert(equal(m, [1, 2, 3, 10, 20, 30, 40, 50, 60])); - assert(equal(m.retro, [60, 50, 40, 30, 20, 10, 3, 2, 1])); - - m.popFront(); - assert(equal(m, [2, 3, 10, 20, 30, 40, 50, 60])); - m.popBack(); - assert(equal(m, [2, 3, 10, 20, 30, 40, 50])); - m.popFront(); - assert(equal(m, [3, 10, 20, 30, 40, 50])); - m.popBack(); - assert(equal(m, [3, 10, 20, 30, 40])); - m.popFront(); - assert(equal(m, [10, 20, 30, 40])); - m.popBack(); - assert(equal(m, [10, 20, 30])); - m.popFront(); - assert(equal(m, [20, 30])); - m.popBack(); - assert(equal(m, [20])); - m.popFront(); - assert(m.empty); -} - -// Issue 21810: Check for sortedness must not use `==` -@nogc @safe pure nothrow unittest -{ - import std.algorithm.comparison : equal; - import std.typecons : tuple; - - static immutable a = [ - tuple(1, 1), - tuple(3, 1), - tuple(3, 2), - tuple(5, 1), - ]; - static immutable b = [ - tuple(2, 1), - tuple(3, 1), - tuple(4, 1), - tuple(4, 2), - ]; - static immutable r = [ - tuple(1, 1), - tuple(2, 1), - tuple(3, 1), - tuple(3, 2), - tuple(3, 1), - tuple(4, 1), - tuple(4, 2), - tuple(5, 1), - ]; - assert(merge!"a[0] < b[0]"(a, b).equal(r)); -} - -private template validPredicates(E, less...) -{ - static if (less.length == 0) - enum validPredicates = true; - else static if (less.length == 1 && is(typeof(less[0]) == SwapStrategy)) - enum validPredicates = true; - else - enum validPredicates = - is(typeof((E a, E b){ bool r = binaryFun!(less[0])(a, b); })) - && validPredicates!(E, less[1 .. $]); -} - -/** -Sorts a range by multiple keys. - -The call $(D multiSort!("a.id < b.id", -"a.date > b.date")(r)) sorts the range `r` by `id` ascending, -and sorts elements that have the same `id` by `date` -descending. Such a call is equivalent to $(D sort!"a.id != b.id ? a.id -< b.id : a.date > b.date"(r)), but `multiSort` is faster because it -does fewer comparisons (in addition to being more convenient). - -Returns: - The initial range wrapped as a `SortedRange` with its predicates - converted to an equivalent single predicate. - */ -template multiSort(less...) //if (less.length > 1) -{ - auto multiSort(Range)(Range r) - if (validPredicates!(ElementType!Range, less) && hasSwappableElements!Range) - { - import std.meta : AliasSeq; - import std.range : assumeSorted; - static if (is(typeof(less[$ - 1]) == SwapStrategy)) - { - enum ss = less[$ - 1]; - alias funs = less[0 .. $ - 1]; - } - else - { - enum ss = SwapStrategy.unstable; - alias funs = less; - } - - static if (funs.length == 0) - static assert(false, "No sorting predicate provided for multiSort"); - else - static if (funs.length == 1) - return sort!(funs[0], ss, Range)(r); - else - { - multiSortImpl!(Range, ss, funs)(r); - return assumeSorted!(multiSortPredFun!(Range, funs))(r); - } - } -} - -/// -@safe unittest -{ - import std.algorithm.mutation : SwapStrategy; - static struct Point { int x, y; } - auto pts1 = [ Point(0, 0), Point(5, 5), Point(0, 1), Point(0, 2) ]; - auto pts2 = [ Point(0, 0), Point(0, 1), Point(0, 2), Point(5, 5) ]; - multiSort!("a.x < b.x", "a.y < b.y", SwapStrategy.unstable)(pts1); - assert(pts1 == pts2); -} - -private bool multiSortPredFun(Range, funs...)(ElementType!Range a, ElementType!Range b) -{ - foreach (f; funs) - { - alias lessFun = binaryFun!(f); - if (lessFun(a, b)) return true; - if (lessFun(b, a)) return false; - } - return false; -} - -private void multiSortImpl(Range, SwapStrategy ss, funs...)(Range r) -{ - alias lessFun = binaryFun!(funs[0]); - - static if (funs.length > 1) - { - while (r.length > 1) - { - auto p = getPivot!lessFun(r); - auto t = partition3!(funs[0], ss)(r, r[p]); - if (t[0].length <= t[2].length) - { - multiSortImpl!(Range, ss, funs)(t[0]); - multiSortImpl!(Range, ss, funs[1 .. $])(t[1]); - r = t[2]; - } - else - { - multiSortImpl!(Range, ss, funs[1 .. $])(t[1]); - multiSortImpl!(Range, ss, funs)(t[2]); - r = t[0]; - } - } - } - else - { - sort!(lessFun, ss)(r); - } -} - -@safe unittest -{ - import std.algorithm.comparison : equal; - import std.range; - - static struct Point { int x, y; } - auto pts1 = [ Point(5, 6), Point(1, 0), Point(5, 7), Point(1, 1), Point(1, 2), Point(0, 1) ]; - auto pts2 = [ Point(0, 1), Point(1, 0), Point(1, 1), Point(1, 2), Point(5, 6), Point(5, 7) ]; - static assert(validPredicates!(Point, "a.x < b.x", "a.y < b.y")); - multiSort!("a.x < b.x", "a.y < b.y", SwapStrategy.unstable)(pts1); - assert(pts1 == pts2); - - auto pts3 = indexed(pts1, iota(pts1.length)); - assert(pts3.multiSort!("a.x < b.x", "a.y < b.y", SwapStrategy.unstable).release.equal(pts2)); - - auto pts4 = iota(10).array; - assert(pts4.multiSort!("a > b").release.equal(iota(10).retro)); -} - -//https://issues.dlang.org/show_bug.cgi?id=9160 (L-value only comparators) -@safe unittest -{ - static struct A - { - int x; - int y; - } - - static bool byX(const ref A lhs, const ref A rhs) - { - return lhs.x < rhs.x; - } - - static bool byY(const ref A lhs, const ref A rhs) - { - return lhs.y < rhs.y; - } - - auto points = [ A(4, 1), A(2, 4)]; - multiSort!(byX, byY)(points); - assert(points[0] == A(2, 4)); - assert(points[1] == A(4, 1)); -} - -// cannot access frame of function -// https://issues.dlang.org/show_bug.cgi?id=16179 -@safe unittest -{ - auto arr = [[1, 2], [2, 0], [1, 0], [1, 1]]; - int c = 3; - - arr.multiSort!( - (a, b) => a[0] < b[0], - (a, b) => c*a[1] < c*b[1] - ); - assert(arr == [[1, 0], [1, 1], [1, 2], [2, 0]]); -} - -// https://issues.dlang.org/show_bug.cgi?id=16413 - @system comparison function -@system unittest -{ - static @system bool lt(int a, int b) { return a < b; } - auto a = [2, 1]; - a.multiSort!(lt, lt); - assert(a == [1, 2]); -} - -private size_t getPivot(alias less, Range)(Range r) -{ - auto mid = r.length / 2; - if (r.length < 512) - { - if (r.length >= 32) - medianOf!less(r, size_t(0), mid, r.length - 1); - return mid; - } - - // The plan here is to take the median of five by taking five elements in - // the array, segregate around their median, and return the position of the - // third. We choose first, mid, last, and two more in between those. - - auto quarter = r.length / 4; - medianOf!less(r, - size_t(0), mid - quarter, mid, mid + quarter, r.length - 1); - return mid; -} - -/* -Sorting routine that is optimized for short ranges. Note: uses insertion sort -going downward. Benchmarked a similar routine that goes upward, for some reason -it's slower. -*/ -private void shortSort(alias less, Range)(Range r) -{ - import std.algorithm.mutation : swapAt; - alias pred = binaryFun!(less); - - switch (r.length) - { - case 0: case 1: - return; - case 2: - if (pred(r[1], r[0])) r.swapAt(0, 1); - return; - case 3: - if (pred(r[2], r[0])) - { - if (pred(r[0], r[1])) - { - r.swapAt(0, 1); - r.swapAt(0, 2); - } - else - { - r.swapAt(0, 2); - if (pred(r[1], r[0])) r.swapAt(0, 1); - } - } - else - { - if (pred(r[1], r[0])) - { - r.swapAt(0, 1); - } - else - { - if (pred(r[2], r[1])) r.swapAt(1, 2); - } - } - return; - case 4: - if (pred(r[1], r[0])) r.swapAt(0, 1); - if (pred(r[3], r[2])) r.swapAt(2, 3); - if (pred(r[2], r[0])) r.swapAt(0, 2); - if (pred(r[3], r[1])) r.swapAt(1, 3); - if (pred(r[2], r[1])) r.swapAt(1, 2); - return; - default: - sort5!pred(r[r.length - 5 .. r.length]); - if (r.length == 5) return; - break; - } - - assert(r.length >= 6, "r must have more than 5 elements"); - /* The last 5 elements of the range are sorted. Proceed with expanding the - sorted portion downward. */ - immutable maxJ = r.length - 2; - for (size_t i = r.length - 6; ; --i) - { - static if (is(typeof(() nothrow - { - auto t = r[0]; if (pred(t, r[0])) r[0] = r[0]; - }))) // Can we afford to temporarily invalidate the array? - { - import core.lifetime : move; - - size_t j = i + 1; - static if (hasLvalueElements!Range) - auto temp = move(r[i]); - else - auto temp = r[i]; - - if (pred(r[j], temp)) - { - do - { - static if (hasLvalueElements!Range) - trustedMoveEmplace(r[j], r[j - 1]); - else - r[j - 1] = r[j]; - ++j; - } - while (j < r.length && pred(r[j], temp)); - - static if (hasLvalueElements!Range) - trustedMoveEmplace(temp, r[j - 1]); - else - r[j - 1] = move(temp); - } - } - else - { - size_t j = i; - while (pred(r[j + 1], r[j])) - { - r.swapAt(j, j + 1); - if (j == maxJ) break; - ++j; - } - } - if (i == 0) break; - } -} - -/// @trusted wrapper for moveEmplace -private void trustedMoveEmplace(T)(ref T source, ref T target) @trusted -{ - import core.lifetime : moveEmplace; - moveEmplace(source, target); -} - -@safe unittest -{ - import std.random : Random = Xorshift, uniform; - - auto rnd = Random(1); - auto a = new int[uniform(100, 200, rnd)]; - foreach (ref e; a) - { - e = uniform(-100, 100, rnd); - } - - shortSort!(binaryFun!("a < b"), int[])(a); - assert(isSorted(a)); -} - -/* -Sorts the first 5 elements exactly of range r. -*/ -private void sort5(alias lt, Range)(Range r) -{ - assert(r.length >= 5, "r must have more than 4 elements"); - - import std.algorithm.mutation : swapAt; - - // 1. Sort first two pairs - if (lt(r[1], r[0])) r.swapAt(0, 1); - if (lt(r[3], r[2])) r.swapAt(2, 3); - - // 2. Arrange first two pairs by the largest element - if (lt(r[3], r[1])) - { - r.swapAt(0, 2); - r.swapAt(1, 3); - } - assert(!lt(r[1], r[0]) && !lt(r[3], r[1]) && !lt(r[3], r[2]), "unexpected" - ~ " order"); - - // 3. Insert 4 into [0, 1, 3] - if (lt(r[4], r[1])) - { - r.swapAt(3, 4); - r.swapAt(1, 3); - if (lt(r[1], r[0])) - { - r.swapAt(0, 1); - } - } - else if (lt(r[4], r[3])) - { - r.swapAt(3, 4); - } - assert(!lt(r[1], r[0]) && !lt(r[3], r[1]) && !lt(r[4], r[3]), "unexpected" - ~ " order"); - - // 4. Insert 2 into [0, 1, 3, 4] (note: we already know the last is greater) - assert(!lt(r[4], r[2]), "unexpected order"); - if (lt(r[2], r[1])) - { - r.swapAt(1, 2); - if (lt(r[1], r[0])) - { - r.swapAt(0, 1); - } - } - else if (lt(r[3], r[2])) - { - r.swapAt(2, 3); - } - // 7 comparisons, 0-9 swaps -} - -@safe unittest -{ - import std.algorithm.iteration : permutations; - import std.algorithm.mutation : copy; - import std.range : iota; - - int[5] buf; - foreach (per; iota(5).permutations) - { - per.copy(buf[]); - sort5!((a, b) => a < b)(buf[]); - assert(buf[].isSorted); - } -} - -// sort -/** -Sorts a random-access range according to the predicate `less`. - -Performs $(BIGOH r.length * log(r.length)) evaluations of `less`. If `less` involves -expensive computations on the _sort key, it may be worthwhile to use -$(LREF schwartzSort) instead. - -Stable sorting requires `hasAssignableElements!Range` to be true. - -`sort` returns a $(REF SortedRange, std,range) over the original range, -allowing functions that can take advantage of sorted data to know that the -range is sorted and adjust accordingly. The $(REF SortedRange, std,range) is a -wrapper around the original range, so both it and the original range are sorted. -Other functions can't know that the original range has been sorted, but -they $(I can) know that $(REF SortedRange, std,range) has been sorted. - -Preconditions: - -The predicate is expected to satisfy certain rules in order for `sort` to -behave as expected - otherwise, the program may fail on certain inputs (but not -others) when not compiled in release mode, due to the cursory `assumeSorted` -check. Specifically, `sort` expects `less(a,b) && less(b,c)` to imply -`less(a,c)` (transitivity), and, conversely, `!less(a,b) && !less(b,c)` to -imply `!less(a,c)`. Note that the default predicate (`"a < b"`) does not -always satisfy these conditions for floating point types, because the expression -will always be `false` when either `a` or `b` is NaN. -Use $(REF cmp, std,math) instead. - -Params: - less = The predicate to sort by. - ss = The swapping strategy to use. - r = The range to sort. - -Returns: The initial range wrapped as a `SortedRange` with the predicate -`binaryFun!less`. - -Algorithms: $(HTTP en.wikipedia.org/wiki/Introsort, Introsort) is used for unstable sorting and -$(HTTP en.wikipedia.org/wiki/Timsort, Timsort) is used for stable sorting. -Each algorithm has benefits beyond stability. Introsort is generally faster but -Timsort may achieve greater speeds on data with low entropy or if predicate calls -are expensive. Introsort performs no allocations whereas Timsort will perform one -or more allocations per call. Both algorithms have $(BIGOH n log n) worst-case -time complexity. - -See_Also: - $(REF assumeSorted, std,range)$(BR) - $(REF SortedRange, std,range)$(BR) - $(REF SwapStrategy, std,algorithm,mutation)$(BR) - $(REF binaryFun, std,functional) -*/ -SortedRange!(Range, less) -sort(alias less = "a < b", SwapStrategy ss = SwapStrategy.unstable, Range) -(Range r) - /+ Unstable sorting uses the quicksort algorithm, which uses swapAt, - which either uses swap(...), requiring swappable elements, or just - swaps using assignment. - Stable sorting uses TimSort, which needs to copy elements into a buffer, - requiring assignable elements. +/ -{ - import std.range : assumeSorted; - static if (ss == SwapStrategy.unstable) - { - static assert(hasSwappableElements!Range || hasAssignableElements!Range, - "When using SwapStrategy.unstable, the passed Range '" - ~ Range.stringof ~ "' must" - ~ " either fulfill hasSwappableElements, or" - ~ " hasAssignableElements, both were not the case"); - } - else - { - static assert(hasAssignableElements!Range, "When using a SwapStrategy" - ~ " != unstable, the" - ~ " passed Range '" ~ Range.stringof ~ "' must fulfill" - ~ " hasAssignableElements, which it did not"); - } - - static assert(isRandomAccessRange!Range, "The passed Range '" - ~ Range.stringof ~ "' must be a Random AccessRange " - ~ "(isRandomAccessRange)"); - - static assert(hasSlicing!Range, "The passed Range '" - ~ Range.stringof ~ "' must allow Slicing (hasSlicing)"); - - static assert(hasLength!Range, "The passed Range '" - ~ Range.stringof ~ "' must have a length (hasLength)"); - - alias lessFun = binaryFun!(less); - alias LessRet = typeof(lessFun(r.front, r.front)); // instantiate lessFun - - static assert(is(LessRet == bool), "The return type of the template" - ~ " argument 'less' when used with the binaryFun!less template" - ~ " must be a bool. This is not the case, the returned type is '" - ~ LessRet.stringof ~ "'"); - - static if (ss == SwapStrategy.unstable) - quickSortImpl!(lessFun)(r, r.length); - else //use Tim Sort for semistable & stable - TimSortImpl!(lessFun, Range).sort(r, null); - - assert(isSorted!lessFun(r), "Failed to sort range of type " ~ Range.stringof); - return assumeSorted!less(r); -} - -/// -@safe pure nothrow unittest -{ - int[] array = [ 1, 2, 3, 4 ]; - - // sort in descending order - array.sort!("a > b"); - assert(array == [ 4, 3, 2, 1 ]); - - // sort in ascending order - array.sort(); - assert(array == [ 1, 2, 3, 4 ]); - - // sort with reusable comparator and chain - alias myComp = (x, y) => x > y; - assert(array.sort!(myComp).release == [ 4, 3, 2, 1 ]); -} - -/// -@safe unittest -{ - // Showcase stable sorting - import std.algorithm.mutation : SwapStrategy; - string[] words = [ "aBc", "a", "abc", "b", "ABC", "c" ]; - sort!("toUpper(a) < toUpper(b)", SwapStrategy.stable)(words); - assert(words == [ "a", "aBc", "abc", "ABC", "b", "c" ]); -} - -/// -@safe unittest -{ - // Sorting floating-point numbers in presence of NaN - double[] numbers = [-0.0, 3.0, -2.0, double.nan, 0.0, -double.nan]; - - import std.algorithm.comparison : equal; - import std.math.operations : cmp; - import std.math.traits : isIdentical; - - sort!((a, b) => cmp(a, b) < 0)(numbers); - - double[] sorted = [-double.nan, -2.0, -0.0, 0.0, 3.0, double.nan]; - assert(numbers.equal!isIdentical(sorted)); -} - -@safe unittest -{ - // Simple regression benchmark - import std.algorithm.iteration, std.algorithm.mutation; - import std.array : array; - import std.random : Random, uniform; - import std.range : iota; - Random rng; - int[] a = iota(20148).map!(_ => uniform(-1000, 1000, rng)).array; - static uint comps; - static bool less(int a, int b) { ++comps; return a < b; } - sort!less(a); // random numbers - sort!less(a); // sorted ascending - a.reverse(); - sort!less(a); // sorted descending - a[] = 0; - sort!less(a); // all equal - - // This should get smaller with time. On occasion it may go larger, but only - // if there's thorough justification. - debug enum uint watermark = 1676220; - else enum uint watermark = 1676220; - - import std.conv; - assert(comps <= watermark, text("You seem to have pessimized sort! ", - watermark, " < ", comps)); - assert(comps >= watermark, text("You seem to have improved sort!", - " Please update watermark from ", watermark, " to ", comps)); -} - -@safe unittest -{ - import std.algorithm.internal : rndstuff; - import std.algorithm.mutation : swapRanges; - import std.random : Random = Xorshift, uniform; - import std.uni : toUpper; - - // sort using delegate - auto a = new int[100]; - auto rnd = Random(123_456_789); - foreach (ref e; a) - { - e = uniform(-100, 100, rnd); - } - - int i = 0; - bool greater2(int a, int b) @safe { return a + i > b + i; } - auto greater = &greater2; - sort!(greater)(a); - assert(isSorted!(greater)(a)); - - // sort using string - sort!("a < b")(a); - assert(isSorted!("a < b")(a)); - - // sort using function; all elements equal - foreach (ref e; a) - { - e = 5; - } - static bool less(int a, int b) { return a < b; } - sort!(less)(a); - assert(isSorted!(less)(a)); - - string[] words = [ "aBc", "a", "abc", "b", "ABC", "c" ]; - bool lessi(string a, string b) { return toUpper(a) < toUpper(b); } - sort!(lessi, SwapStrategy.stable)(words); - assert(words == [ "a", "aBc", "abc", "ABC", "b", "c" ]); - - // sort using ternary predicate - //sort!("b - a")(a); - //assert(isSorted!(less)(a)); - - a = rndstuff!(int)(); - sort(a); - assert(isSorted(a)); - auto b = rndstuff!(string)(); - sort!("toLower(a) < toLower(b)")(b); - assert(isSorted!("toUpper(a) < toUpper(b)")(b)); - - { - // https://issues.dlang.org/show_bug.cgi?id=10317 - enum E_10317 { a, b } - auto a_10317 = new E_10317[10]; - sort(a_10317); - } - - { - // https://issues.dlang.org/show_bug.cgi?id=7767 - // Unstable sort should complete without an excessive number of predicate calls - // This would suggest it's running in quadratic time - - // Compilation error if predicate is not static, i.e. a nested function - static uint comp; - static bool pred(size_t a, size_t b) - { - ++comp; - return a < b; - } - - size_t[] arr; - arr.length = 1024; - - foreach (k; 0 .. arr.length) arr[k] = k; - swapRanges(arr[0..$/2], arr[$/2..$]); - - sort!(pred, SwapStrategy.unstable)(arr); - assert(comp < 25_000); - } - - { - import std.algorithm.mutation : swap; - - bool proxySwapCalled; - struct S - { - int i; - alias i this; - void proxySwap(ref S other) { swap(i, other.i); proxySwapCalled = true; } - @disable void opAssign(S value); - } - - alias R = S[]; - R r = [S(3), S(2), S(1)]; - static assert(hasSwappableElements!R); - static assert(!hasAssignableElements!R); - r.sort(); - assert(proxySwapCalled); - } - - // https://issues.dlang.org/show_bug.cgi?id=20751 - { - static bool refPred(ref int a, ref int b) - { - return a < b; - } - - auto sortedArr = [5,4,3,2,1].sort!refPred; - sortedArr.equalRange(3); - } -} - -private void quickSortImpl(alias less, Range)(Range r, size_t depth) -{ - import std.algorithm.comparison : min, max; - import std.algorithm.mutation : swap, swapAt; - import std.conv : to; - - alias Elem = ElementType!(Range); - enum size_t shortSortGetsBetter = max(32, 1024 / Elem.sizeof); - static assert(shortSortGetsBetter >= 1, Elem.stringof ~ " " - ~ to!string(Elem.sizeof)); - - // partition - while (r.length > shortSortGetsBetter) - { - if (depth == 0) - { - HeapOps!(less, Range).heapSort(r); - return; - } - depth = depth >= depth.max / 2 ? (depth / 3) * 2 : (depth * 2) / 3; - - const pivotIdx = getPivot!(less)(r); - auto pivot = r[pivotIdx]; - - // partition - r.swapAt(pivotIdx, r.length - 1); - size_t lessI = size_t.max, greaterI = r.length - 1; - - outer: for (;;) - { - alias pred = binaryFun!less; - while (pred(r[++lessI], pivot)) {} - assert(lessI <= greaterI, "sort: invalid comparison function."); - for (;;) - { - if (greaterI == lessI) break outer; - if (!pred(pivot, r[--greaterI])) break; - } - assert(lessI <= greaterI, "sort: invalid comparison function."); - if (lessI == greaterI) break; - r.swapAt(lessI, greaterI); - } - - r.swapAt(r.length - 1, lessI); - auto left = r[0 .. lessI], right = r[lessI + 1 .. r.length]; - if (right.length > left.length) - { - swap(left, right); - } - .quickSortImpl!(less, Range)(right, depth); - r = left; - } - // residual sort - static if (shortSortGetsBetter > 1) - { - shortSort!(less, Range)(r); - } -} - -// Heap operations for random-access ranges -package(std) template HeapOps(alias less, Range) -{ - import std.algorithm.mutation : swapAt; - - static assert(isRandomAccessRange!Range, Range.stringof ~ " must be a" - ~ " RandomAccessRange"); - static assert(hasLength!Range, Range.stringof ~ " must have length"); - static assert(hasSwappableElements!Range || hasAssignableElements!Range, - Range.stringof ~ " must have swappable or assignable Elements"); - - alias lessFun = binaryFun!less; - - //template because of https://issues.dlang.org/show_bug.cgi?id=12410 - void heapSort()(Range r) - { - // If true, there is nothing to do - if (r.length < 2) return; - // Build Heap - buildHeap(r); - // Sort - for (size_t i = r.length - 1; i > 0; --i) - { - r.swapAt(0, i); - percolate(r, 0, i); - } - } - - //template because of https://issues.dlang.org/show_bug.cgi?id=12410 - void buildHeap()(Range r) - { - immutable n = r.length; - for (size_t i = n / 2; i-- > 0; ) - { - siftDown(r, i, n); - } - assert(isHeap(r), "r is not a heap"); - } - - bool isHeap()(Range r) - { - size_t parent = 0; - foreach (child; 1 .. r.length) - { - if (lessFun(r[parent], r[child])) return false; - // Increment parent every other pass - parent += !(child & 1); - } - return true; - } - - // Sifts down r[parent] (which is initially assumed to be messed up) so the - // heap property is restored for r[parent .. end]. - // template because of https://issues.dlang.org/show_bug.cgi?id=12410 - void siftDown()(Range r, size_t parent, immutable size_t end) - { - for (;;) - { - auto child = (parent + 1) * 2; - if (child >= end) - { - // Leftover left child? - if (child == end && lessFun(r[parent], r[--child])) - r.swapAt(parent, child); - break; - } - auto leftChild = child - 1; - if (lessFun(r[child], r[leftChild])) child = leftChild; - if (!lessFun(r[parent], r[child])) break; - r.swapAt(parent, child); - parent = child; - } - } - - // Alternate version of siftDown that performs fewer comparisons, see - // https://en.wikipedia.org/wiki/Heapsort#Bottom-up_heapsort. The percolate - // process first sifts the parent all the way down (without comparing it - // against the leaves), and then a bit up until the heap property is - // restored. So there are more swaps but fewer comparisons. Gains are made - // when the final position is likely to end toward the bottom of the heap, - // so not a lot of sifts back are performed. - //template because of https://issues.dlang.org/show_bug.cgi?id=12410 - void percolate()(Range r, size_t parent, immutable size_t end) - { - immutable root = parent; - - // Sift down - for (;;) - { - auto child = (parent + 1) * 2; - - if (child >= end) - { - if (child == end) - { - // Leftover left node. - --child; - r.swapAt(parent, child); - parent = child; - } - break; - } - - auto leftChild = child - 1; - if (lessFun(r[child], r[leftChild])) child = leftChild; - r.swapAt(parent, child); - parent = child; - } - - // Sift up - for (auto child = parent; child > root; child = parent) - { - parent = (child - 1) / 2; - if (!lessFun(r[parent], r[child])) break; - r.swapAt(parent, child); - } - } -} - -// Tim Sort implementation -private template TimSortImpl(alias pred, R) -{ - import core.bitop : bsr; - import std.array : uninitializedArray; - - static assert(isRandomAccessRange!R, R.stringof ~ " must be a" - ~ " RandomAccessRange"); - static assert(hasLength!R, R.stringof ~ " must have a length"); - static assert(hasSlicing!R, R.stringof ~ " must support slicing"); - static assert(hasAssignableElements!R, R.stringof ~ " must have" - ~ " assignable elements"); - - alias T = ElementType!R; - - alias less = binaryFun!pred; - bool greater()(auto ref T a, auto ref T b) { return less(b, a); } - bool greaterEqual()(auto ref T a, auto ref T b) { return !less(a, b); } - bool lessEqual()(auto ref T a, auto ref T b) { return !less(b, a); } - - enum minimalMerge = 128; - enum minimalGallop = 7; - enum minimalStorage = 256; - enum stackSize = 40; - - struct Slice{ size_t base, length; } - - // Entry point for tim sort - void sort()(R range, T[] temp) - { - import std.algorithm.comparison : min; - import std.format : format; - - // Do insertion sort on small range - if (range.length <= minimalMerge) - { - binaryInsertionSort(range); - return; - } - - immutable minRun = minRunLength(range.length); - immutable minTemp = min(range.length / 2, minimalStorage); - size_t minGallop = minimalGallop; - Slice[stackSize] stack = void; - size_t stackLen = 0; - - // Allocate temporary memory if not provided by user - if (temp.length < minTemp) temp = () @trusted { return uninitializedArray!(T[])(minTemp); }(); - - for (size_t i = 0; i < range.length; ) - { - // Find length of first run in list - size_t runLen = firstRun(range[i .. range.length]); - - // If run has less than minRun elements, extend using insertion sort - if (runLen < minRun) - { - // Do not run farther than the length of the range - immutable force = range.length - i > minRun ? minRun : range.length - i; - binaryInsertionSort(range[i .. i + force], runLen); - runLen = force; - } - - // Push run onto stack - stack[stackLen++] = Slice(i, runLen); - i += runLen; - - // Collapse stack so that (e1 > e2 + e3 && e2 > e3) - // STACK is | ... e1 e2 e3 > - while (stackLen > 1) - { - immutable run4 = stackLen - 1; - immutable run3 = stackLen - 2; - immutable run2 = stackLen - 3; - immutable run1 = stackLen - 4; - - if ( (stackLen > 2 && stack[run2].length <= stack[run3].length + stack[run4].length) || - (stackLen > 3 && stack[run1].length <= stack[run3].length + stack[run2].length) ) - { - immutable at = stack[run2].length < stack[run4].length ? run2 : run3; - mergeAt(range, stack[0 .. stackLen], at, minGallop, temp); - } - else if (stack[run3].length > stack[run4].length) break; - else mergeAt(range, stack[0 .. stackLen], run3, minGallop, temp); - - stackLen -= 1; - } - - // Assert that the code above established the invariant correctly - version (StdUnittest) - { - if (stackLen == 2) - { - assert(stack[0].length > stack[1].length, format! - "stack[0].length %s > stack[1].length %s"( - stack[0].length, stack[1].length - )); - } - else if (stackLen > 2) - { - foreach (k; 2 .. stackLen) - { - assert(stack[k - 2].length > stack[k - 1].length + stack[k].length, - format!"stack[k - 2].length %s > stack[k - 1].length %s + stack[k].length %s"( - stack[k - 2].length, stack[k - 1].length, stack[k].length - )); - assert(stack[k - 1].length > stack[k].length, - format!"stack[k - 1].length %s > stack[k].length %s"( - stack[k - 1].length, stack[k].length - )); - } - } - } - } - - // Force collapse stack until there is only one run left - while (stackLen > 1) - { - immutable run3 = stackLen - 1; - immutable run2 = stackLen - 2; - immutable run1 = stackLen - 3; - immutable at = stackLen >= 3 && stack[run1].length <= stack[run3].length - ? run1 : run2; - mergeAt(range, stack[0 .. stackLen], at, minGallop, temp); - --stackLen; - } - } - - // Calculates optimal value for minRun: - // take first 6 bits of n and add 1 if any lower bits are set - size_t minRunLength()(size_t n) - { - immutable shift = bsr(n)-5; - auto result = (n >> shift) + !!(n & ~((1 << shift)-1)); - return result; - } - - // Returns length of first run in range - size_t firstRun()(R range) - out(ret) - { - assert(ret <= range.length, "ret must be less or equal than" - ~ " range.length"); - } - do - { - import std.algorithm.mutation : reverse; - - if (range.length < 2) return range.length; - - size_t i = 2; - if (lessEqual(range[0], range[1])) - { - while (i < range.length && lessEqual(range[i-1], range[i])) ++i; - } - else - { - while (i < range.length && greater(range[i-1], range[i])) ++i; - reverse(range[0 .. i]); - } - return i; - } - - // A binary insertion sort for building runs up to minRun length - void binaryInsertionSort()(R range, size_t sortedLen = 1) - out - { - if (!__ctfe) assert(isSorted!pred(range), "range must be sorted"); - } - do - { - import std.algorithm.mutation : move; - - for (; sortedLen < range.length; ++sortedLen) - { - T item = range.moveAt(sortedLen); - size_t lower = 0; - size_t upper = sortedLen; - while (upper != lower) - { - size_t center = (lower + upper) / 2; - if (less(item, range[center])) upper = center; - else lower = center + 1; - } - //Currently (DMD 2.061) moveAll+retro is slightly less - //efficient then stright 'for' loop - //11 instructions vs 7 in the innermost loop [checked on Win32] - //moveAll(retro(range[lower .. sortedLen]), - // retro(range[lower+1 .. sortedLen+1])); - for (upper=sortedLen; upper > lower; upper--) - { - static if (hasLvalueElements!R) - move(range[upper -1], range[upper]); - else - range[upper] = range.moveAt(upper - 1); - } - - static if (hasLvalueElements!R) - move(item, range[lower]); - else - range[lower] = move(item); - } - } - - // Merge two runs in stack (at, at + 1) - void mergeAt()(R range, Slice[] stack, immutable size_t at, ref size_t minGallop, ref T[] temp) - in - { - import std.format : format; - assert(stack.length >= 2, "stack be be greater than 1"); - assert(stack.length - at == 2 || stack.length - at == 3, - format!"stack.length - at %s must be 2 or 3"(stack.length - at)); - } - do - { - immutable base = stack[at].base; - immutable mid = stack[at].length; - immutable len = stack[at + 1].length + mid; - - // Pop run from stack - stack[at] = Slice(base, len); - if (stack.length - at == 3) stack[$ - 2] = stack[$ - 1]; - - // Merge runs (at, at + 1) - return merge(range[base .. base + len], mid, minGallop, temp); - } - - // Merge two runs in a range. Mid is the starting index of the second run. - // minGallop and temp are references; The calling function must receive the updated values. - void merge()(R range, size_t mid, ref size_t minGallop, ref T[] temp) - in - { - if (!__ctfe) - { - assert(isSorted!pred(range[0 .. mid]), "range[0 .. mid] must be" - ~ " sorted"); - assert(isSorted!pred(range[mid .. range.length]), "range[mid .." - ~ " range.length] must be sorted"); - } - } - do - { - assert(mid < range.length, "mid must be less than the length of the" - ~ " range"); - - // Reduce range of elements - immutable firstElement = gallopForwardUpper(range[0 .. mid], range[mid]); - immutable lastElement = gallopReverseLower(range[mid .. range.length], range[mid - 1]) + mid; - range = range[firstElement .. lastElement]; - mid -= firstElement; - - if (mid == 0 || mid == range.length) return; - - // Call function which will copy smaller run into temporary memory - if (mid <= range.length / 2) - { - temp = ensureCapacity(mid, temp); - minGallop = mergeLo(range, mid, minGallop, temp); - } - else - { - temp = ensureCapacity(range.length - mid, temp); - minGallop = mergeHi(range, mid, minGallop, temp); - } - } - - // Enlarge size of temporary memory if needed - T[] ensureCapacity()(size_t minCapacity, T[] temp) - out(ret) - { - assert(ret.length >= minCapacity, "ensuring the capacity failed"); - } - do - { - if (temp.length < minCapacity) - { - size_t newSize = 1<<(bsr(minCapacity)+1); - //Test for overflow - if (newSize < minCapacity) newSize = minCapacity; - - // can't use `temp.length` if there's no default constructor - static if (__traits(compiles, { T defaultConstructed; cast(void) defaultConstructed; })) - { - if (__ctfe) temp.length = newSize; - else temp = () @trusted { return uninitializedArray!(T[])(newSize); }(); - } - else - { - temp = () @trusted { return uninitializedArray!(T[])(newSize); }(); - } - } - return temp; - } - - // Merge front to back. Returns new value of minGallop. - // temp must be large enough to store range[0 .. mid] - size_t mergeLo()(R range, immutable size_t mid, size_t minGallop, T[] temp) - out - { - if (!__ctfe) assert(isSorted!pred(range), "the range must be sorted"); - } - do - { - import std.algorithm.mutation : copy; - - assert(mid <= range.length, "mid must be less than the length of the" - ~ " range"); - assert(temp.length >= mid, "temp.length must be greater or equal to mid"); - - // Copy run into temporary memory - temp = temp[0 .. mid]; - copy(range[0 .. mid], temp); - - // Move first element into place - moveEntry(range, mid, range, 0); - - size_t i = 1, lef = 0, rig = mid + 1; - size_t count_lef, count_rig; - immutable lef_end = temp.length - 1; - - if (lef < lef_end && rig < range.length) - outer: while (true) - { - count_lef = 0; - count_rig = 0; - - // Linear merge - while ((count_lef | count_rig) < minGallop) - { - if (lessEqual(temp[lef], range[rig])) - { - moveEntry(temp, lef++, range, i++); - if (lef >= lef_end) break outer; - ++count_lef; - count_rig = 0; - } - else - { - moveEntry(range, rig++, range, i++); - if (rig >= range.length) break outer; - count_lef = 0; - ++count_rig; - } - } - - // Gallop merge - do - { - count_lef = gallopForwardUpper(temp[lef .. $], range[rig]); - foreach (j; 0 .. count_lef) moveEntry(temp, lef++, range, i++); - if (lef >= temp.length) break outer; - - count_rig = gallopForwardLower(range[rig .. range.length], temp[lef]); - foreach (j; 0 .. count_rig) moveEntry(range, rig++, range, i++); - if (rig >= range.length) while (true) - { - moveEntry(temp, lef++, range, i++); - if (lef >= temp.length) break outer; - } - - if (minGallop > 0) --minGallop; - } - while (count_lef >= minimalGallop || count_rig >= minimalGallop); - - minGallop += 2; - } - - // Move remaining elements from right - while (rig < range.length) - moveEntry(range, rig++, range, i++); - - // Move remaining elements from left - while (lef < temp.length) - moveEntry(temp, lef++, range, i++); - - return minGallop > 0 ? minGallop : 1; - } - - // Merge back to front. Returns new value of minGallop. - // temp must be large enough to store range[mid .. range.length] - size_t mergeHi()(R range, immutable size_t mid, size_t minGallop, T[] temp) - out - { - if (!__ctfe) assert(isSorted!pred(range), "the range must be sorted"); - } - do - { - import std.algorithm.mutation : copy; - import std.format : format; - - assert(mid <= range.length, "mid must be less or equal to range.length"); - assert(temp.length >= range.length - mid, format! - "temp.length %s >= range.length %s - mid %s"(temp.length, - range.length, mid)); - - // Copy run into temporary memory - temp = temp[0 .. range.length - mid]; - copy(range[mid .. range.length], temp); - - // Move first element into place - moveEntry(range, mid - 1, range, range.length - 1); - - size_t i = range.length - 2, lef = mid - 2, rig = temp.length - 1; - size_t count_lef, count_rig; - - outer: - while (true) - { - count_lef = 0; - count_rig = 0; - - // Linear merge - while ((count_lef | count_rig) < minGallop) - { - if (greaterEqual(temp[rig], range[lef])) - { - moveEntry(temp, rig, range, i--); - if (rig == 1) - { - // Move remaining elements from left - while (true) - { - moveEntry(range, lef, range, i--); - if (lef == 0) break; - --lef; - } - - // Move last element into place - moveEntry(temp, 0, range, i); - - break outer; - } - --rig; - count_lef = 0; - ++count_rig; - } - else - { - moveEntry(range, lef, range, i--); - if (lef == 0) while (true) - { - moveEntry(temp, rig, range, i--); - if (rig == 0) break outer; - --rig; - } - --lef; - ++count_lef; - count_rig = 0; - } - } - - // Gallop merge - do - { - count_rig = rig - gallopReverseLower(temp[0 .. rig], range[lef]); - foreach (j; 0 .. count_rig) - { - moveEntry(temp, rig, range, i--); - if (rig == 0) break outer; - --rig; - } - - count_lef = lef - gallopReverseUpper(range[0 .. lef], temp[rig]); - foreach (j; 0 .. count_lef) - { - moveEntry(range, lef, range, i--); - if (lef == 0) while (true) - { - moveEntry(temp, rig, range, i--); - if (rig == 0) break outer; - --rig; - } - --lef; - } - - if (minGallop > 0) --minGallop; - } - while (count_lef >= minimalGallop || count_rig >= minimalGallop); - - minGallop += 2; - } - - return minGallop > 0 ? minGallop : 1; - } - - // false = forward / lower, true = reverse / upper - template gallopSearch(bool forwardReverse, bool lowerUpper) - { - // Gallop search on range according to attributes forwardReverse and lowerUpper - size_t gallopSearch(R)(R range, T value) - out(ret) - { - assert(ret <= range.length, "ret must be less or equal to" - ~ " range.length"); - } - do - { - size_t lower = 0, center = 1, upper = range.length; - alias gap = center; - - static if (forwardReverse) - { - static if (!lowerUpper) alias comp = lessEqual; // reverse lower - static if (lowerUpper) alias comp = less; // reverse upper - - // Gallop Search Reverse - while (gap <= upper) - { - if (comp(value, range[upper - gap])) - { - upper -= gap; - gap *= 2; - } - else - { - lower = upper - gap; - break; - } - } - - // Binary Search Reverse - while (upper != lower) - { - center = lower + (upper - lower) / 2; - if (comp(value, range[center])) upper = center; - else lower = center + 1; - } - } - else - { - static if (!lowerUpper) alias comp = greater; // forward lower - static if (lowerUpper) alias comp = greaterEqual; // forward upper - - // Gallop Search Forward - while (lower + gap < upper) - { - if (comp(value, range[lower + gap])) - { - lower += gap; - gap *= 2; - } - else - { - upper = lower + gap; - break; - } - } - - // Binary Search Forward - while (lower != upper) - { - center = lower + (upper - lower) / 2; - if (comp(value, range[center])) lower = center + 1; - else upper = center; - } - } - - return lower; - } - } - - alias gallopForwardLower = gallopSearch!(false, false); - alias gallopForwardUpper = gallopSearch!(false, true); - alias gallopReverseLower = gallopSearch!( true, false); - alias gallopReverseUpper = gallopSearch!( true, true); - - /// Helper method that moves from[fIdx] into to[tIdx] if both are lvalues and - /// uses a plain assignment if not (necessary for backwards compatibility) - void moveEntry(X, Y)(ref X from, const size_t fIdx, ref Y to, const size_t tIdx) - { - // This template is instantiated with different combinations of range (R) and temp (T[]). - // T[] obviously has lvalue-elements, so checking R should be enough here - static if (hasLvalueElements!R) - { - import core.lifetime : move; - move(from[fIdx], to[tIdx]); - } - else - to[tIdx] = from[fIdx]; - } -} - -@safe unittest -{ - import std.random : Random, uniform, randomShuffle; - - // Element type with two fields - static struct E - { - size_t value, index; - } - - // Generates data especially for testing sorting with Timsort - static E[] genSampleData(uint seed) @safe - { - import std.algorithm.mutation : swap, swapRanges; - - auto rnd = Random(seed); - - E[] arr; - arr.length = 64 * 64; - - // We want duplicate values for testing stability - foreach (i, ref v; arr) v.value = i / 64; - - // Swap ranges at random middle point (test large merge operation) - immutable mid = uniform(arr.length / 4, arr.length / 4 * 3, rnd); - swapRanges(arr[0 .. mid], arr[mid .. $]); - - // Shuffle last 1/8 of the array (test insertion sort and linear merge) - randomShuffle(arr[$ / 8 * 7 .. $], rnd); - - // Swap few random elements (test galloping mode) - foreach (i; 0 .. arr.length / 64) - { - immutable a = uniform(0, arr.length, rnd), b = uniform(0, arr.length, rnd); - swap(arr[a], arr[b]); - } - - // Now that our test array is prepped, store original index value - // This will allow us to confirm the array was sorted stably - foreach (i, ref v; arr) v.index = i; - - return arr; - } - - // Tests the Timsort function for correctness and stability - static bool testSort(uint seed) - { - import std.format : format; - auto arr = genSampleData(seed); - - // Now sort the array! - static bool comp(E a, E b) - { - return a.value < b.value; - } - - sort!(comp, SwapStrategy.stable)(arr); - - // Test that the array was sorted correctly - assert(isSorted!comp(arr), "arr must be sorted"); - - // Test that the array was sorted stably - foreach (i; 0 .. arr.length - 1) - { - if (arr[i].value == arr[i + 1].value) - assert(arr[i].index < arr[i + 1].index, format! - "arr[i %s].index %s < arr[i + 1].index %s"( - i, arr[i].index, arr[i + 1].index)); - } - - return true; - } - - enum seed = 310614065; - testSort(seed); - - enum result = testSort(seed); - assert(result == true, "invalid result"); -} - -// https://issues.dlang.org/show_bug.cgi?id=4584 -@safe unittest -{ - assert(isSorted!"a < b"(sort!("a < b", SwapStrategy.stable)( - [83, 42, 85, 86, 87, 22, 89, 30, 91, 46, 93, 94, 95, 6, - 97, 14, 33, 10, 101, 102, 103, 26, 105, 106, 107, 6] - ))); - -} - -@safe unittest -{ - //test stable sort + zip - import std.range; - auto x = [10, 50, 60, 60, 20]; - dchar[] y = "abcde"d.dup; - - sort!("a[0] < b[0]", SwapStrategy.stable)(zip(x, y)); - assert(x == [10, 20, 50, 60, 60]); - assert(y == "aebcd"d); -} - -// https://issues.dlang.org/show_bug.cgi?id=14223 -@safe unittest -{ - import std.array, std.range; - auto arr = chain(iota(0, 384), iota(0, 256), iota(0, 80), iota(0, 64), iota(0, 96)).array; - sort!("a < b", SwapStrategy.stable)(arr); -} - -@safe unittest -{ - static struct NoCopy - { - pure nothrow @nogc @safe: - - int key; - this(scope const ref NoCopy) - { - assert(false, "Tried to copy struct!"); - } - ref opAssign()(scope const auto ref NoCopy other) - { - assert(false, "Tried to copy struct!"); - } - this(this) {} - } - - static NoCopy[] makeArray(const size_t size) - { - NoCopy[] array = new NoCopy[](size); - foreach (const i, ref t; array[0..$/2]) t.key = cast(int) (size - i); - foreach (const i, ref t; array[$/2..$]) t.key = cast(int) i; - return array; - } - - alias cmp = (ref NoCopy a, ref NoCopy b) => a.key < b.key; - enum minMerge = TimSortImpl!(cmp, NoCopy[]).minimalMerge; - - sort!(cmp, SwapStrategy.unstable)(makeArray(20)); - sort!(cmp, SwapStrategy.stable)(makeArray(minMerge - 5)); - sort!(cmp, SwapStrategy.stable)(makeArray(minMerge + 5)); -} - -// https://issues.dlang.org/show_bug.cgi?id=23668 -@safe unittest -{ - static struct S - { - int opCmp(const S) const { return 1; } - @disable this(); - } - S[] array; - array.sort!("a < b", SwapStrategy.stable); -} - -// schwartzSort -/** -Alternative sorting method that should be used when comparing keys involves an -expensive computation. - -Instead of using `less(a, b)` for comparing elements, -`schwartzSort` uses `less(transform(a), transform(b))`. The values of the -`transform` function are precomputed in a temporary array, thus saving on -repeatedly computing it. Conversely, if the cost of `transform` is small -compared to the cost of allocating and filling the precomputed array, `sort` -may be faster and therefore preferable. - -This approach to sorting is akin to the $(HTTP -wikipedia.org/wiki/Schwartzian_transform, Schwartzian transform), also known as -the decorate-sort-undecorate pattern in Python and Lisp. The complexity is the -same as that of the corresponding `sort`, but `schwartzSort` evaluates -`transform` only `r.length` times (less than half when compared to regular -sorting). The usage can be best illustrated with an example. - -Example: ----- -uint hashFun(string) { ... expensive computation ... } -string[] array = ...; -// Sort strings by hash, slow -sort!((a, b) => hashFun(a) < hashFun(b))(array); -// Sort strings by hash, fast (only computes arr.length hashes): -schwartzSort!(hashFun, "a < b")(array); ----- - -The `schwartzSort` function might require less temporary data and -be faster than the Perl idiom or the decorate-sort-undecorate idiom -present in Python and Lisp. This is because sorting is done in-place -and only minimal extra data (one array of transformed elements) is -created. - -To check whether an array was sorted and benefit of the speedup of -Schwartz sorting, a function `schwartzIsSorted` is not provided -because the effect can be achieved by calling $(D -isSorted!less(map!transform(r))). - -Params: - transform = The transformation to apply. Either a unary function - (`unaryFun!transform(element)`), or a binary function - (`binaryFun!transform(element, index)`). - less = The predicate to sort the transformed elements by. - ss = The swapping strategy to use. - r = The range to sort. - -Returns: The initial range wrapped as a `SortedRange` with the -predicate `(a, b) => binaryFun!less(transform(a), transform(b))`. - */ -SortedRange!(R, ((a, b) => binaryFun!less(unaryFun!transform(a), - unaryFun!transform(b)))) -schwartzSort(alias transform, alias less = "a < b", - SwapStrategy ss = SwapStrategy.unstable, R)(R r) -if (isRandomAccessRange!R && hasLength!R && hasSwappableElements!R && - !is(typeof(binaryFun!less) == SwapStrategy)) -{ - import core.lifetime : emplace; - import std.range : zip, SortedRange; - import std.string : representation; - - static if (is(typeof(unaryFun!transform(r.front)))) - { - alias transformFun = unaryFun!transform; - alias TB = typeof(transformFun(r.front)); - enum isBinary = false; - } - else static if (is(typeof(binaryFun!transform(r.front, 0)))) - { - alias transformFun = binaryFun!transform; - alias TB = typeof(transformFun(r.front, 0)); - enum isBinary = true; - } - else - static assert(false, "unsupported `transform` alias"); - - // The `transform` function might return a qualified type, e.g. const(int). - // Strip qualifiers if possible s.t. the temporary array is sortable. - static if (is(TB : Unqual!TB)) - alias T = Unqual!TB; - else - static assert(false, "`transform` returns an unsortable qualified type: " ~ TB.stringof); - - static trustedMalloc()(size_t len) @trusted - { - import core.checkedint : mulu; - import core.memory : pureMalloc; - bool overflow; - const nbytes = mulu(len, T.sizeof, overflow); - if (overflow) assert(false, "multiplication overflowed"); - T[] result = (cast(T*) pureMalloc(nbytes))[0 .. len]; - static if (hasIndirections!T) - { - import core.memory : GC; - GC.addRange(result.ptr, nbytes); - } - return result; - } - auto xform1 = trustedMalloc(r.length); - - size_t length; - scope(exit) - { - static if (hasElaborateDestructor!T) - { - foreach (i; 0 .. length) collectException(destroy(xform1[i])); - } - static void trustedFree()(T[] p) @trusted - { - import core.memory : pureFree; - static if (hasIndirections!T) - { - import core.memory : GC; - GC.removeRange(p.ptr); - } - pureFree(p.ptr); - } - trustedFree(xform1); - } - for (; length != r.length; ++length) - { - static if (isBinary) - emplace(&xform1[length], transformFun(r[length], length)); - else - emplace(&xform1[length], transformFun(r[length])); - } - // Make sure we use ubyte[] and ushort[], not char[] and wchar[] - // for the intermediate array, lest zip gets confused. - static if (isNarrowString!(typeof(xform1))) - { - auto xform = xform1.representation(); - } - else - { - alias xform = xform1; - } - zip(xform, r).sort!((a, b) => binaryFun!less(a[0], b[0]), ss)(); - return typeof(return)(r); -} - -/// ditto -auto schwartzSort(alias transform, SwapStrategy ss, R)(R r) -if (isRandomAccessRange!R && hasLength!R && hasSwappableElements!R) -{ - return schwartzSort!(transform, "a < b", ss, R)(r); -} - -/// -@safe pure unittest -{ - import std.algorithm.iteration : map; - import std.numeric : entropy; - - auto lowEnt = [ 1.0, 0, 0 ], - midEnt = [ 0.1, 0.1, 0.8 ], - highEnt = [ 0.31, 0.29, 0.4 ]; - auto arr = new double[][3]; - arr[0] = midEnt; - arr[1] = lowEnt; - arr[2] = highEnt; - - schwartzSort!(entropy, "a > b")(arr); - - assert(arr[0] == highEnt); - assert(arr[1] == midEnt); - assert(arr[2] == lowEnt); - assert(isSorted!("a > b")(map!(entropy)(arr))); -} - -@safe pure unittest -{ - import std.algorithm.iteration : map; - import std.numeric : entropy; - - auto lowEnt = [ 1.0, 0, 0 ], - midEnt = [ 0.1, 0.1, 0.8 ], - highEnt = [ 0.31, 0.29, 0.4 ]; - auto arr = new double[][3]; - arr[0] = midEnt; - arr[1] = lowEnt; - arr[2] = highEnt; - - schwartzSort!(entropy, "a < b")(arr); - - assert(arr[0] == lowEnt); - assert(arr[1] == midEnt); - assert(arr[2] == highEnt); - assert(isSorted!("a < b")(map!(entropy)(arr))); -} - -@safe pure unittest -{ - // binary transform function - string[] strings = [ "one", "two", "three" ]; - schwartzSort!((element, index) => size_t.max - index)(strings); - assert(strings == [ "three", "two", "one" ]); -} - -// https://issues.dlang.org/show_bug.cgi?id=4909 -@safe pure unittest -{ - import std.typecons : Tuple; - Tuple!(char)[] chars; - schwartzSort!"a[0]"(chars); -} - -// https://issues.dlang.org/show_bug.cgi?id=5924 -@safe pure unittest -{ - import std.typecons : Tuple; - Tuple!(char)[] chars; - schwartzSort!((Tuple!(char) c){ return c[0]; })(chars); -} - -// https://issues.dlang.org/show_bug.cgi?id=13965 -@safe pure unittest -{ - import std.typecons : Tuple; - Tuple!(char)[] chars; - schwartzSort!("a[0]", SwapStrategy.stable)(chars); -} - -// https://issues.dlang.org/show_bug.cgi?id=13965 -@safe pure unittest -{ - import std.algorithm.iteration : map; - import std.numeric : entropy; - - auto lowEnt = [ 1.0, 0, 0 ], - midEnt = [ 0.1, 0.1, 0.8 ], - highEnt = [ 0.31, 0.29, 0.4 ]; - auto arr = new double[][3]; - arr[0] = midEnt; - arr[1] = lowEnt; - arr[2] = highEnt; - - schwartzSort!(entropy, SwapStrategy.stable)(arr); - - assert(arr[0] == lowEnt); - assert(arr[1] == midEnt); - assert(arr[2] == highEnt); - assert(isSorted!("a < b")(map!(entropy)(arr))); -} - -// https://issues.dlang.org/show_bug.cgi?id=20799 -@safe unittest -{ - import std.range : iota, retro; - import std.array : array; - - auto arr = 1_000_000.iota.retro.array; - arr.schwartzSort!( - n => new int(n), - (a, b) => *a < *b - ); - assert(arr.isSorted()); -} - -// https://issues.dlang.org/show_bug.cgi?id=21183 -@safe unittest -{ - static T get(T)(int) { return T.init; } - - // There's no need to actually sort, just checking type interference - if (false) - { - int[] arr; - - // Fine because there are no indirections - arr.schwartzSort!(get!(const int)); - - // Fine because it decays to immutable(int)* - arr.schwartzSort!(get!(immutable int*)); - - // Disallowed because it would require a non-const reference - static assert(!__traits(compiles, arr.schwartzSort!(get!(const Object)))); - - static struct Wrapper - { - int* ptr; - } - - // Disallowed because Wrapper.ptr would become mutable - static assert(!__traits(compiles, arr.schwartzSort!(get!(const Wrapper)))); - } -} - -// partialSort -/** -Reorders the random-access range `r` such that the range `r[0 .. mid]` -is the same as if the entire `r` were sorted, and leaves -the range `r[mid .. r.length]` in no particular order. - -Performs $(BIGOH r.length * log(mid)) evaluations of `pred`. The -implementation simply calls `topN!(less, ss)(r, n)` and then $(D -sort!(less, ss)(r[0 .. n])). - -Params: - less = The predicate to sort by. - ss = The swapping strategy to use. - r = The random-access range to reorder. - n = The length of the initial segment of `r` to sort. -*/ -void partialSort(alias less = "a < b", SwapStrategy ss = SwapStrategy.unstable, - Range)(Range r, size_t n) -if (isRandomAccessRange!(Range) && hasLength!(Range) && hasSlicing!(Range)) -{ - partialSort!(less, ss)(r[0 .. n], r[n .. $]); -} - -/// -@system unittest -{ - int[] a = [ 9, 8, 7, 6, 5, 4, 3, 2, 1, 0 ]; - partialSort(a, 5); - assert(a[0 .. 5] == [ 0, 1, 2, 3, 4 ]); -} - -/** -Stores the smallest elements of the two ranges in the left-hand range in sorted order. - -Params: - less = The predicate to sort by. - ss = The swapping strategy to use. - r1 = The first range. - r2 = The second range. - */ - -void partialSort(alias less = "a < b", SwapStrategy ss = SwapStrategy.unstable, - Range1, Range2)(Range1 r1, Range2 r2) -if (isRandomAccessRange!(Range1) && hasLength!Range1 && - isInputRange!Range2 && is(ElementType!Range1 == ElementType!Range2) && - hasLvalueElements!Range1 && hasLvalueElements!Range2) -{ - topN!(less, ss)(r1, r2); - sort!(less, ss)(r1); -} -/// -@system unittest -{ - int[] a = [5, 7, 2, 6, 7]; - int[] b = [2, 1, 5, 6, 7, 3, 0]; - - partialSort(a, b); - assert(a == [0, 1, 2, 2, 3]); -} - -// topN -/** -Reorders the range `r` using $(REF_ALTTEXT swap, swap, std,algorithm,mutation) -such that `r[nth]` refers to the element that would fall there if the range -were fully sorted. - -It is akin to $(LINK2 https://en.wikipedia.org/wiki/Quickselect, Quickselect), -and partitions `r` such that all elements -`e1` from `r[0]` to `r[nth]` satisfy `!less(r[nth], e1)`, -and all elements `e2` from `r[nth]` to `r[r.length]` satisfy -`!less(e2, r[nth])`. Effectively, it finds the `nth + 1` smallest -(according to `less`) elements in `r`. Performs an expected -$(BIGOH r.length) (if unstable) or $(BIGOH r.length * log(r.length)) -(if stable) evaluations of `less` and $(REF_ALTTEXT swap, swap, std,algorithm,mutation). - -If `n >= r.length`, the algorithm has no effect and returns -`r[0 .. r.length]`. - -Params: - less = The predicate to sort by. - ss = The swapping strategy to use. - r = The random-access range to reorder. - nth = The index of the element that should be in sorted position after the - function is done. - -Returns: a slice from `r[0]` to `r[nth]`, excluding `r[nth]` itself. - -See_Also: - $(LREF topNIndex), - -BUGS: - -Stable topN has not been implemented yet. -*/ -auto topN(alias less = "a < b", - SwapStrategy ss = SwapStrategy.unstable, - Range)(Range r, size_t nth) -if (isRandomAccessRange!(Range) && hasLength!Range && - hasSlicing!Range && hasAssignableElements!Range) -{ - static assert(ss == SwapStrategy.unstable, - "Stable topN not yet implemented"); - if (nth >= r.length) return r[0 .. r.length]; - auto ret = r[0 .. nth]; - if (false) - { - // Workaround for https://issues.dlang.org/show_bug.cgi?id=16528 - // Safety checks: enumerate all potentially unsafe generic primitives - // then use a @trusted implementation. - cast(void) binaryFun!less(r[0], r[r.length - 1]); - import std.algorithm.mutation : swapAt; - r.swapAt(size_t(0), size_t(0)); - static assert(is(typeof(r.length) == size_t), - typeof(r.length).stringof ~ " must be of type size_t"); - pivotPartition!less(r, 0); - } - bool useSampling = true; - topNImpl!(binaryFun!less)(r, nth, useSampling); - return ret; -} - -/// -@safe unittest -{ - int[] v = [ 25, 7, 9, 2, 0, 5, 21 ]; - topN!"a < b"(v, 100); - assert(v == [ 25, 7, 9, 2, 0, 5, 21 ]); - auto n = 4; - topN!((a, b) => a < b)(v, n); - assert(v[n] == 9); -} - -// https://issues.dlang.org/show_bug.cgi?id=8341 -@safe unittest -{ - import std.algorithm.comparison : equal; - import std.range : zip; - import std.typecons : tuple; - auto a = [10, 30, 20]; - auto b = ["c", "b", "a"]; - assert(topN!"a[0] > b[0]"(zip(a, b), 2).equal([tuple(20, "a"), tuple(30, "b")])); -} - -private @trusted -void topNImpl(alias less, R)(R r, size_t n, ref bool useSampling) -{ - for (;;) - { - import std.algorithm.mutation : swapAt; - assert(n < r.length); - size_t pivot = void; - - // Decide strategy for partitioning - if (n == 0) - { - pivot = 0; - foreach (i; 1 .. r.length) - if (less(r[i], r[pivot])) pivot = i; - r.swapAt(n, pivot); - return; - } - if (n + 1 == r.length) - { - pivot = 0; - foreach (i; 1 .. r.length) - if (less(r[pivot], r[i])) pivot = i; - r.swapAt(n, pivot); - return; - } - if (r.length <= 12) - { - pivot = pivotPartition!less(r, r.length / 2); - } - else if (n * 16 <= (r.length - 1) * 7) - { - pivot = topNPartitionOffMedian!(less, No.leanRight) - (r, n, useSampling); - // Quality check - if (useSampling) - { - if (pivot < n) - { - if (pivot * 4 < r.length) - { - useSampling = false; - } - } - else if ((r.length - pivot) * 8 < r.length * 3) - { - useSampling = false; - } - } - } - else if (n * 16 >= (r.length - 1) * 9) - { - pivot = topNPartitionOffMedian!(less, Yes.leanRight) - (r, n, useSampling); - // Quality check - if (useSampling) - { - if (pivot < n) - { - if (pivot * 8 < r.length * 3) - { - useSampling = false; - } - } - else if ((r.length - pivot) * 4 < r.length) - { - useSampling = false; - } - } - } - else - { - pivot = topNPartition!less(r, n, useSampling); - // Quality check - if (useSampling && - (pivot * 9 < r.length * 2 || pivot * 9 > r.length * 7)) - { - // Failed - abort sampling going forward - useSampling = false; - } - } - - assert(pivot != size_t.max, "pivot must be not equal to size_t.max"); - // See how the pivot fares - if (pivot == n) - { - return; - } - if (pivot > n) - { - r = r[0 .. pivot]; - } - else - { - n -= pivot + 1; - r = r[pivot + 1 .. r.length]; - } - } -} - -private size_t topNPartition(alias lp, R)(R r, size_t n, bool useSampling) -{ - import std.format : format; - assert(r.length >= 9 && n < r.length, "length must be longer than 8" - ~ " and n must be less than r.length"); - immutable ninth = r.length / 9; - auto pivot = ninth / 2; - // Position subrange r[lo .. hi] to have length equal to ninth and its upper - // median r[lo .. hi][$ / 2] in exactly the same place as the upper median - // of the entire range r[$ / 2]. This is to improve behavior for searching - // the median in already sorted ranges. - immutable lo = r.length / 2 - pivot, hi = lo + ninth; - // We have either one straggler on the left, one on the right, or none. - assert(lo - (r.length - hi) <= 1 || (r.length - hi) - lo <= 1, - format!"straggler check failed lo %s, r.length %s, hi %s"(lo, r.length, hi)); - assert(lo >= ninth * 4, format!"lo %s >= ninth * 4 %s"(lo, ninth * 4)); - assert(r.length - hi >= ninth * 4, - format!"r.length %s - hi %s >= ninth * 4 %s"(r.length, hi, ninth * 4)); - - // Partition in groups of 3, and the mid tertile again in groups of 3 - if (!useSampling) - p3!lp(r, lo - ninth, hi + ninth); - p3!lp(r, lo, hi); - - // Get the median of medians of medians - // Map the full interval of n to the full interval of the ninth - pivot = (n * (ninth - 1)) / (r.length - 1); - topNImpl!lp(r[lo .. hi], pivot, useSampling); - return expandPartition!lp(r, lo, pivot + lo, hi); -} - -private void p3(alias less, Range)(Range r, size_t lo, immutable size_t hi) -{ - import std.format : format; - assert(lo <= hi && hi < r.length, - format!"lo %s <= hi %s && hi < r.length %s"(lo, hi, r.length)); - immutable ln = hi - lo; - for (; lo < hi; ++lo) - { - assert(lo >= ln, format!"lo %s >= ln %s"(lo, ln)); - assert(lo + ln < r.length, format!"lo %s + ln %s < r.length %s"( - lo, ln, r.length)); - medianOf!less(r, lo - ln, lo, lo + ln); - } -} - -private void p4(alias less, Flag!"leanRight" f, Range) - (Range r, size_t lo, immutable size_t hi) -{ - import std.format : format; - assert(lo <= hi && hi < r.length, format!"lo %s <= hi %s && hi < r.length %s"( - lo, hi, r.length)); - immutable ln = hi - lo, _2ln = ln * 2; - for (; lo < hi; ++lo) - { - assert(lo >= ln, format!"lo %s >= ln %s"(lo, ln)); - assert(lo + ln < r.length, format!"lo %s + ln %s < r.length %s"( - lo, ln, r.length)); - static if (f == Yes.leanRight) - medianOf!(less, f)(r, lo - _2ln, lo - ln, lo, lo + ln); - else - medianOf!(less, f)(r, lo - ln, lo, lo + ln, lo + _2ln); - } -} - -private size_t topNPartitionOffMedian(alias lp, Flag!"leanRight" f, R) - (R r, size_t n, bool useSampling) -{ - assert(r.length >= 12, "The length of r must be greater than 11"); - assert(n < r.length, "n must be less than the length of r"); - immutable _4 = r.length / 4; - static if (f == Yes.leanRight) - immutable leftLimit = 2 * _4; - else - immutable leftLimit = _4; - // Partition in groups of 4, and the left quartile again in groups of 3 - if (!useSampling) - { - p4!(lp, f)(r, leftLimit, leftLimit + _4); - } - immutable _12 = _4 / 3; - immutable lo = leftLimit + _12, hi = lo + _12; - p3!lp(r, lo, hi); - - // Get the median of medians of medians - // Map the full interval of n to the full interval of the ninth - immutable pivot = (n * (_12 - 1)) / (r.length - 1); - topNImpl!lp(r[lo .. hi], pivot, useSampling); - return expandPartition!lp(r, lo, pivot + lo, hi); -} - -/* -Params: -less = predicate -r = range to partition -pivot = pivot to partition around -lo = value such that r[lo .. pivot] already less than r[pivot] -hi = value such that r[pivot .. hi] already greater than r[pivot] - -Returns: new position of pivot -*/ -private -size_t expandPartition(alias lp, R)(R r, size_t lo, size_t pivot, size_t hi) -in -{ - import std.algorithm.searching : all; - assert(lo <= pivot, "lo must be less than or equal pivot"); - assert(pivot < hi, "pivot must be less than hi"); - assert(hi <= r.length, "hi must be less than or equal to the length of r"); - assert(r[lo .. pivot + 1].all!(x => !lp(r[pivot], x)), - "r[lo .. pivot + 1] failed less than test"); - assert(r[pivot + 1 .. hi].all!(x => !lp(x, r[pivot])), - "r[pivot + 1 .. hi] failed less than test"); - } -out -{ - import std.algorithm.searching : all; - assert(r[0 .. pivot + 1].all!(x => !lp(r[pivot], x)), - "r[0 .. pivot + 1] failed less than test"); - assert(r[pivot + 1 .. r.length].all!(x => !lp(x, r[pivot])), - "r[pivot + 1 .. r.length] failed less than test"); -} -do -{ - import std.algorithm.mutation : swapAt; - import std.algorithm.searching : all; - // We work with closed intervals! - --hi; - - size_t left = 0, rite = r.length - 1; - loop: for (;; ++left, --rite) - { - for (;; ++left) - { - if (left == lo) break loop; - if (!lp(r[left], r[pivot])) break; - } - for (;; --rite) - { - if (rite == hi) break loop; - if (!lp(r[pivot], r[rite])) break; - } - r.swapAt(left, rite); - } - - assert(r[lo .. pivot + 1].all!(x => !lp(r[pivot], x)), - "r[lo .. pivot + 1] failed less than test"); - assert(r[pivot + 1 .. hi + 1].all!(x => !lp(x, r[pivot])), - "r[pivot + 1 .. hi + 1] failed less than test"); - assert(r[0 .. left].all!(x => !lp(r[pivot], x)), - "r[0 .. left] failed less than test"); - assert(r[rite + 1 .. r.length].all!(x => !lp(x, r[pivot])), - "r[rite + 1 .. r.length] failed less than test"); - - immutable oldPivot = pivot; - - if (left < lo) - { - // First loop: spend r[lo .. pivot] - for (; lo < pivot; ++left) - { - if (left == lo) goto done; - if (!lp(r[oldPivot], r[left])) continue; - --pivot; - assert(!lp(r[oldPivot], r[pivot]), "less check failed"); - r.swapAt(left, pivot); - } - // Second loop: make left and pivot meet - for (;; ++left) - { - if (left == pivot) goto done; - if (!lp(r[oldPivot], r[left])) continue; - for (;;) - { - if (left == pivot) goto done; - --pivot; - if (lp(r[pivot], r[oldPivot])) - { - r.swapAt(left, pivot); - break; - } - } - } - } - - // First loop: spend r[lo .. pivot] - for (; hi != pivot; --rite) - { - if (rite == hi) goto done; - if (!lp(r[rite], r[oldPivot])) continue; - ++pivot; - assert(!lp(r[pivot], r[oldPivot]), "less check failed"); - r.swapAt(rite, pivot); - } - // Second loop: make left and pivot meet - for (; rite > pivot; --rite) - { - if (!lp(r[rite], r[oldPivot])) continue; - while (rite > pivot) - { - ++pivot; - if (lp(r[oldPivot], r[pivot])) - { - r.swapAt(rite, pivot); - break; - } - } - } - -done: - r.swapAt(oldPivot, pivot); - return pivot; -} - -@safe unittest -{ - auto a = [ 10, 5, 3, 4, 8, 11, 13, 3, 9, 4, 10 ]; - assert(expandPartition!((a, b) => a < b)(a, 4, 5, 6) == 9); - - import std.algorithm.iteration : map; - import std.array : array; - import std.random : uniform; - import std.range : iota; - auto size = uniform(1, 1000); - a = iota(0, size).map!(_ => uniform(0, 1000)).array; - if (a.length == 0) return; - expandPartition!((a, b) => a < b)(a, a.length / 2, a.length / 2, - a.length / 2 + 1); -} - -@safe unittest -{ - import std.algorithm.comparison : max, min; - import std.algorithm.iteration : reduce; - - int[] v = [ 7, 6, 5, 4, 3, 2, 1, 0 ]; - ptrdiff_t n = 3; - topN!("a < b")(v, n); - assert(reduce!max(v[0 .. n]) <= v[n]); - assert(reduce!min(v[n + 1 .. $]) >= v[n]); - // - v = [3, 4, 5, 6, 7, 2, 3, 4, 5, 6, 1, 2, 3, 4, 5]; - n = 3; - topN(v, n); - assert(reduce!max(v[0 .. n]) <= v[n]); - assert(reduce!min(v[n + 1 .. $]) >= v[n]); - // - v = [3, 4, 5, 6, 7, 2, 3, 4, 5, 6, 1, 2, 3, 4, 5]; - n = 1; - topN(v, n); - assert(reduce!max(v[0 .. n]) <= v[n]); - assert(reduce!min(v[n + 1 .. $]) >= v[n]); - // - v = [3, 4, 5, 6, 7, 2, 3, 4, 5, 6, 1, 2, 3, 4, 5]; - n = v.length - 1; - topN(v, n); - assert(v[n] == 7); - // - v = [3, 4, 5, 6, 7, 2, 3, 4, 5, 6, 1, 2, 3, 4, 5]; - n = 0; - topN(v, n); - assert(v[n] == 1); - - double[][] v1 = [[-10, -5], [-10, -3], [-10, -5], [-10, -4], - [-10, -5], [-9, -5], [-9, -3], [-9, -5],]; - - // double[][] v1 = [ [-10, -5], [-10, -4], [-9, -5], [-9, -5], - // [-10, -5], [-10, -3], [-10, -5], [-9, -3],]; - double[]*[] idx = [ &v1[0], &v1[1], &v1[2], &v1[3], &v1[4], &v1[5], &v1[6], - &v1[7], ]; - - auto mid = v1.length / 2; - topN!((a, b){ return (*a)[1] < (*b)[1]; })(idx, mid); - foreach (e; idx[0 .. mid]) assert((*e)[1] <= (*idx[mid])[1]); - foreach (e; idx[mid .. $]) assert((*e)[1] >= (*idx[mid])[1]); -} - -@safe unittest -{ - import std.algorithm.comparison : max, min; - import std.algorithm.iteration : reduce; - import std.random : Random = Xorshift, uniform; - - immutable uint[] seeds = [90027751, 2709791795, 1374631933, 995751648, 3541495258, 984840953]; - foreach (s; seeds) - { - auto r = Random(s); - - int[] a = new int[uniform(1, 10000, r)]; - foreach (ref e; a) e = uniform(-1000, 1000, r); - - auto k = uniform(0, a.length, r); - topN(a, k); - if (k > 0) - { - auto left = reduce!max(a[0 .. k]); - assert(left <= a[k]); - } - if (k + 1 < a.length) - { - auto right = reduce!min(a[k + 1 .. $]); - assert(right >= a[k]); - } - } -} - -// https://issues.dlang.org/show_bug.cgi?id=12987 -@safe unittest -{ - int[] a = [ 25, 7, 9, 2, 0, 5, 21 ]; - auto n = 4; - auto t = topN(a, n); - sort(t); - assert(t == [0, 2, 5, 7]); -} - -/** -Stores the smallest elements of the two ranges in the left-hand range. - -Params: - less = The predicate to sort by. - ss = The swapping strategy to use. - r1 = The first range. - r2 = The second range. - */ -auto topN(alias less = "a < b", - SwapStrategy ss = SwapStrategy.unstable, - Range1, Range2)(Range1 r1, Range2 r2) -if (isRandomAccessRange!(Range1) && hasLength!Range1 && - isInputRange!Range2 && is(ElementType!Range1 == ElementType!Range2) && - hasLvalueElements!Range1 && hasLvalueElements!Range2) -{ - import std.container : BinaryHeap; - - static assert(ss == SwapStrategy.unstable, - "Stable topN not yet implemented"); - - auto heap = BinaryHeap!(Range1, less)(r1); - foreach (ref e; r2) - { - heap.conditionalSwap(e); - } - - return r1; -} - -/// -@system unittest -{ - int[] a = [ 5, 7, 2, 6, 7 ]; - int[] b = [ 2, 1, 5, 6, 7, 3, 0 ]; - topN(a, b); - sort(a); - assert(a == [0, 1, 2, 2, 3]); -} - -// https://issues.dlang.org/show_bug.cgi?id=15421 -@system unittest -{ - import std.algorithm.comparison : equal; - import std.internal.test.dummyrange; - import std.meta : AliasSeq; - - alias RandomRanges = AliasSeq!( - DummyRange!(ReturnBy.Reference, Length.Yes, RangeType.Random) - ); - - alias ReferenceRanges = AliasSeq!( - DummyRange!(ReturnBy.Reference, Length.Yes, RangeType.Forward), - DummyRange!(ReturnBy.Reference, Length.Yes, RangeType.Bidirectional), - DummyRange!(ReturnBy.Reference, Length.Yes, RangeType.Random), - DummyRange!(ReturnBy.Reference, Length.No, RangeType.Forward), - DummyRange!(ReturnBy.Reference, Length.No, RangeType.Bidirectional)); - - foreach (T1; RandomRanges) - { - foreach (T2; ReferenceRanges) - { - import std.array : array; - - T1 A; - T2 B; - - A.reinit(); - B.reinit(); - - topN(A, B); - - // BUG(?): sort doesn't accept DummyRanges (needs Slicing and Length) - auto a = array(A); - auto b = array(B); - sort(a); - sort(b); - - assert(equal(a, [ 1, 1, 2, 2, 3, 3, 4, 4, 5, 5 ])); - assert(equal(b, [ 6, 6, 7, 7, 8, 8, 9, 9, 10, 10 ])); - } - } -} - -// https://issues.dlang.org/show_bug.cgi?id=15421 -@system unittest -{ - auto a = [ 9, 8, 0, 3, 5, 25, 43, 4, 2, 0, 7 ]; - auto b = [ 9, 8, 0, 3, 5, 25, 43, 4, 2, 0, 7 ]; - - topN(a, 4); - topN(b[0 .. 4], b[4 .. $]); - - sort(a[0 .. 4]); - sort(a[4 .. $]); - sort(b[0 .. 4]); - sort(b[4 .. $]); - - assert(a[0 .. 4] == b[0 .. 4]); - assert(a[4 .. $] == b[4 .. $]); - assert(a == b); -} - -// https://issues.dlang.org/show_bug.cgi?id=12987 -@system unittest -{ - int[] a = [ 5, 7, 2, 6, 7 ]; - int[] b = [ 2, 1, 5, 6, 7, 3, 0 ]; - auto t = topN(a, b); - sort(t); - assert(t == [ 0, 1, 2, 2, 3 ]); -} - -// https://issues.dlang.org/show_bug.cgi?id=15420 -@system unittest -{ - int[] a = [ 5, 7, 2, 6, 7 ]; - int[] b = [ 2, 1, 5, 6, 7, 3, 0 ]; - topN!"a > b"(a, b); - sort!"a > b"(a); - assert(a == [ 7, 7, 7, 6, 6 ]); -} - -/** -Copies the top `n` elements of the -$(REF_ALTTEXT input range, isInputRange, std,range,primitives) `source` into the -random-access range `target`, where `n = target.length`. - -Elements of `source` are not touched. If $(D -sorted) is `true`, the target is sorted. Otherwise, the target -respects the $(HTTP en.wikipedia.org/wiki/Binary_heap, heap property). - -Params: - less = The predicate to sort by. - source = The source range. - target = The target range. - sorted = Whether to sort the elements copied into `target`. - -Returns: The slice of `target` containing the copied elements. - */ -TRange topNCopy(alias less = "a < b", SRange, TRange) - (SRange source, TRange target, SortOutput sorted = No.sortOutput) -if (isInputRange!(SRange) && isRandomAccessRange!(TRange) - && hasLength!(TRange) && hasSlicing!(TRange)) -{ - import std.container : BinaryHeap; - - if (target.empty) return target; - auto heap = BinaryHeap!(TRange, less)(target, 0); - foreach (e; source) heap.conditionalInsert(e); - auto result = target[0 .. heap.length]; - if (sorted == Yes.sortOutput) - { - while (!heap.empty) heap.removeFront(); - } - return result; -} - -/// -@system unittest -{ - import std.typecons : Yes; - - int[] a = [ 10, 16, 2, 3, 1, 5, 0 ]; - int[] b = new int[3]; - topNCopy(a, b, Yes.sortOutput); - assert(b == [ 0, 1, 2 ]); -} - -@system unittest -{ - import std.random : Random = Xorshift, uniform, randomShuffle; - import std.typecons : Yes; - - auto r = Random(123_456_789); - ptrdiff_t[] a = new ptrdiff_t[uniform(1, 1000, r)]; - foreach (i, ref e; a) e = i; - randomShuffle(a, r); - auto n = uniform(0, a.length, r); - ptrdiff_t[] b = new ptrdiff_t[n]; - topNCopy!(binaryFun!("a < b"))(a, b, Yes.sortOutput); - assert(isSorted!(binaryFun!("a < b"))(b)); -} - -/** -Given a range of elements, constructs an index of its top $(I n) elements -(i.e., the first $(I n) elements if the range were sorted). - -Similar to $(LREF topN), except that the range is not modified. - -Params: - less = A binary predicate that defines the ordering of range elements. - Defaults to `a < b`. - ss = $(RED (Not implemented yet.)) Specify the swapping strategy. - r = A - $(REF_ALTTEXT random-access range, isRandomAccessRange, std,range,primitives) - of elements to make an index for. - index = A - $(REF_ALTTEXT random-access range, isRandomAccessRange, std,range,primitives) - with assignable elements to build the index in. The length of this range - determines how many top elements to index in `r`. - - This index range can either have integral elements, in which case the - constructed index will consist of zero-based numerical indices into - `r`; or it can have pointers to the element type of `r`, in which - case the constructed index will be pointers to the top elements in - `r`. - sorted = Determines whether to sort the index by the elements they refer - to. - -See_also: $(LREF topN), $(LREF topNCopy). - -BUGS: -The swapping strategy parameter is not implemented yet; currently it is -ignored. -*/ -void topNIndex(alias less = "a < b", SwapStrategy ss = SwapStrategy.unstable, - Range, RangeIndex) - (Range r, RangeIndex index, SortOutput sorted = No.sortOutput) -if (isRandomAccessRange!Range && - isRandomAccessRange!RangeIndex && - hasAssignableElements!RangeIndex) -{ - static assert(ss == SwapStrategy.unstable, - "Stable swap strategy not implemented yet."); - - import std.container.binaryheap : BinaryHeap; - if (index.empty) return; - - static if (isIntegral!(ElementType!(RangeIndex))) - { - import std.exception : enforce; - - enforce(ElementType!(RangeIndex).max >= index.length, - "Index type too small"); - bool indirectLess(ElementType!(RangeIndex) a, ElementType!(RangeIndex) b) - { - return binaryFun!(less)(r[a], r[b]); - } - auto heap = BinaryHeap!(RangeIndex, indirectLess)(index, 0); - foreach (i; 0 .. r.length) - { - heap.conditionalInsert(cast(ElementType!RangeIndex) i); - } - - } - else static if (is(ElementType!(RangeIndex) == ElementType!(Range)*)) - { - static bool indirectLess(const ElementType!(RangeIndex) a, - const ElementType!(RangeIndex) b) - { - return binaryFun!less(*a, *b); - } - auto heap = BinaryHeap!(RangeIndex, indirectLess)(index, 0); - foreach (i; 0 .. r.length) - { - heap.conditionalInsert(&r[i]); - } - } - else static assert(0, "Invalid ElementType"); - - if (sorted == Yes.sortOutput) - { - while (!heap.empty) heap.removeFront(); - } -} - -/// -@system unittest -{ - import std.typecons : Yes; - - // Construct index to top 3 elements using numerical indices: - int[] a = [ 10, 2, 7, 5, 8, 1 ]; - int[] index = new int[3]; - topNIndex(a, index, Yes.sortOutput); - assert(index == [5, 1, 3]); // because a[5]==1, a[1]==2, a[3]==5 - - // Construct index to top 3 elements using pointer indices: - int*[] ptrIndex = new int*[3]; - topNIndex(a, ptrIndex, Yes.sortOutput); - assert(ptrIndex == [ &a[5], &a[1], &a[3] ]); -} - -@system unittest -{ - import std.conv : text; - - { - int[] a = [ 10, 8, 9, 2, 4, 6, 7, 1, 3, 5 ]; - int*[] b = new int*[5]; - topNIndex!("a > b")(a, b, Yes.sortOutput); - assert(b == [ &a[0], &a[2], &a[1], &a[6], &a[5]]); - } - { - int[] a = [ 10, 8, 9, 2, 4, 6, 7, 1, 3, 5 ]; - auto b = new ubyte[5]; - topNIndex!("a > b")(a, b, Yes.sortOutput); - assert(b == [ cast(ubyte) 0, cast(ubyte) 2, cast(ubyte) 1, cast(ubyte) 6, cast(ubyte) 5], text(b)); - } -} - -// medianOf -/* -Private for the time being. - -Computes the median of 2 to 5 arbitrary indexes in random-access range `r` -using hand-written specialized algorithms. The indexes must be distinct (if not, -behavior is implementation-defined). The function also partitions the elements -involved around the median, e.g. `medianOf(r, a, b, c)` not only fills `r[b]` -with the median of `r[a]`, `r[b]`, and `r[c]`, but also puts the minimum in -`r[a]` and the maximum in `r[c]`. - -Params: -less = The comparison predicate used, modeled as a - $(LINK2 https://en.wikipedia.org/wiki/Weak_ordering#Strict_weak_orderings, strict weak ordering) - (irreflexive, antisymmetric, transitive, and implying a transitive equivalence). -flag = Used only for even values of `T.length`. If `No.leanRight`, the median -"leans left", meaning `medianOf(r, a, b, c, d)` puts the lower median of the -four in `r[b]`, the minimum in `r[a]`, and the two others in `r[c]` and `r[d]`. -Conversely, `median!("a < b", Yes.leanRight)(r, a, b, c, d)` puts the upper -median of the four in `r[c]`, the maximum in `r[d]`, and the two others in -`r[a]` and `r[b]`. -r = The range containing the indexes. -i = Two to five indexes inside `r`. -*/ -private void medianOf( - alias less = "a < b", - Flag!"leanRight" flag = No.leanRight, - Range, - Indexes...) - (Range r, Indexes i) -if (isRandomAccessRange!Range && hasLength!Range && - Indexes.length >= 2 && Indexes.length <= 5 && - allSatisfy!(isUnsigned, Indexes)) -{ - assert(r.length >= Indexes.length, "r.length must be greater than or" - ~ " equal to Indexes.length"); - import std.functional : binaryFun; - alias lt = binaryFun!less; - enum k = Indexes.length; - import std.algorithm.mutation : swapAt; - import std.format : format; - - alias a = i[0]; - static assert(is(typeof(a) == size_t), typeof(a).stringof ~ " must be" - ~ " of type size_t"); - static if (k >= 2) - { - alias b = i[1]; - static assert(is(typeof(b) == size_t), typeof(b).stringof ~ " must be" - ~ " of type size_t"); - assert(a != b, "a != b "); - } - static if (k >= 3) - { - alias c = i[2]; - static assert(is(typeof(c) == size_t), typeof(c).stringof ~ " must be" - ~ " of type size_t"); - assert(a != c && b != c, "a != c && b != c"); - } - static if (k >= 4) - { - alias d = i[3]; - static assert(is(typeof(d) == size_t), typeof(d).stringof ~ " must be" - ~ " of type size_t"); - assert(a != d && b != d && c != d, "a != d && b != d && c != d failed"); - } - static if (k >= 5) - { - alias e = i[4]; - static assert(is(typeof(e) == size_t), typeof(e).stringof ~ " must be" - ~ " of type size_t"); - assert(a != e && b != e && c != e && d != e, - "a != e && b != e && c != e && d != e failed"); - } - - static if (k == 2) - { - if (lt(r[b], r[a])) r.swapAt(a, b); - } - else static if (k == 3) - { - if (lt(r[c], r[a])) // c < a - { - if (lt(r[a], r[b])) // c < a < b - { - r.swapAt(a, b); - r.swapAt(a, c); - } - else // c < a, b <= a - { - r.swapAt(a, c); - if (lt(r[b], r[a])) r.swapAt(a, b); - } - } - else // a <= c - { - if (lt(r[b], r[a])) // b < a <= c - { - r.swapAt(a, b); - } - else // a <= c, a <= b - { - if (lt(r[c], r[b])) r.swapAt(b, c); - } - } - assert(!lt(r[b], r[a]), "less than check failed"); - assert(!lt(r[c], r[b]), "less than check failed"); - } - else static if (k == 4) - { - static if (flag == No.leanRight) - { - // Eliminate the rightmost from the competition - if (lt(r[d], r[c])) r.swapAt(c, d); // c <= d - if (lt(r[d], r[b])) r.swapAt(b, d); // b <= d - medianOf!lt(r, a, b, c); - } - else - { - // Eliminate the leftmost from the competition - if (lt(r[b], r[a])) r.swapAt(a, b); // a <= b - if (lt(r[c], r[a])) r.swapAt(a, c); // a <= c - medianOf!lt(r, b, c, d); - } - } - else static if (k == 5) - { - // Credit: Teppo Niinimäki - version (StdUnittest) scope(success) - { - assert(!lt(r[c], r[a]), "less than check failed"); - assert(!lt(r[c], r[b]), "less than check failed"); - assert(!lt(r[d], r[c]), "less than check failed"); - assert(!lt(r[e], r[c]), "less than check failed"); - } - - if (lt(r[c], r[a])) r.swapAt(a, c); - if (lt(r[d], r[b])) r.swapAt(b, d); - if (lt(r[d], r[c])) - { - r.swapAt(c, d); - r.swapAt(a, b); - } - if (lt(r[e], r[b])) r.swapAt(b, e); - if (lt(r[e], r[c])) - { - r.swapAt(c, e); - if (lt(r[c], r[a])) r.swapAt(a, c); - } - else - { - if (lt(r[c], r[b])) r.swapAt(b, c); - } - } -} - -@safe unittest -{ - // Verify medianOf for all permutations of [1, 2, 2, 3, 4]. - int[5] data = [1, 2, 2, 3, 4]; - do - { - int[5] a = data; - medianOf(a[], size_t(0), size_t(1)); - assert(a[0] <= a[1]); - - a[] = data[]; - medianOf(a[], size_t(0), size_t(1), size_t(2)); - assert(ordered(a[0], a[1], a[2])); - - a[] = data[]; - medianOf(a[], size_t(0), size_t(1), size_t(2), size_t(3)); - assert(a[0] <= a[1] && a[1] <= a[2] && a[1] <= a[3]); - - a[] = data[]; - medianOf!("a < b", Yes.leanRight)(a[], size_t(0), size_t(1), - size_t(2), size_t(3)); - assert(a[0] <= a[2] && a[1] <= a[2] && a[2] <= a[3]); - - a[] = data[]; - medianOf(a[], size_t(0), size_t(1), size_t(2), size_t(3), size_t(4)); - assert(a[0] <= a[2] && a[1] <= a[2] && a[2] <= a[3] && a[2] <= a[4]); - } - while (nextPermutation(data[])); -} - -// nextPermutation -/** - * Permutes `range` in-place to the next lexicographically greater - * permutation. - * - * The predicate `less` defines the lexicographical ordering to be used on - * the range. - * - * If the range is currently the lexicographically greatest permutation, it is - * permuted back to the least permutation and false is returned. Otherwise, - * true is returned. One can thus generate all permutations of a range by - * sorting it according to `less`, which produces the lexicographically - * least permutation, and then calling nextPermutation until it returns false. - * This is guaranteed to generate all distinct permutations of the range - * exactly once. If there are $(I N) elements in the range and all of them are - * unique, then $(I N)! permutations will be generated. Otherwise, if there are - * some duplicated elements, fewer permutations will be produced. ----- -// Enumerate all permutations -int[] a = [1,2,3,4,5]; -do -{ - // use the current permutation and - // proceed to the next permutation of the array. -} while (nextPermutation(a)); ----- - * Params: - * less = The ordering to be used to determine lexicographical ordering of the - * permutations. - * range = The range to permute. - * - * Returns: false if the range was lexicographically the greatest, in which - * case the range is reversed back to the lexicographically smallest - * permutation; otherwise returns true. - * See_Also: - * $(REF permutations, std,algorithm,iteration). - */ -bool nextPermutation(alias less="a < b", BidirectionalRange) - (BidirectionalRange range) -if (isBidirectionalRange!BidirectionalRange && - hasSwappableElements!BidirectionalRange) -{ - import std.algorithm.mutation : reverse, swap; - import std.algorithm.searching : find; - import std.range : retro, takeExactly; - // Ranges of 0 or 1 element have no distinct permutations. - if (range.empty) return false; - - auto i = retro(range); - auto last = i.save; - - // Find last occurring increasing pair of elements - size_t n = 1; - for (i.popFront(); !i.empty; i.popFront(), last.popFront(), n++) - { - if (binaryFun!less(i.front, last.front)) - break; - } - - if (i.empty) - { - // Entire range is decreasing: it's lexicographically the greatest. So - // wrap it around. - range.reverse(); - return false; - } - - // Find last element greater than i.front. - auto j = find!((a) => binaryFun!less(i.front, a))( - takeExactly(retro(range), n)); - - assert(!j.empty, "j must not be empty"); // shouldn't happen since i.front < last.front - swap(i.front, j.front); - reverse(takeExactly(retro(range), n)); - - return true; -} - -/// -@safe unittest -{ - // Step through all permutations of a sorted array in lexicographic order - int[] a = [1,2,3]; - assert(nextPermutation(a) == true); - assert(a == [1,3,2]); - assert(nextPermutation(a) == true); - assert(a == [2,1,3]); - assert(nextPermutation(a) == true); - assert(a == [2,3,1]); - assert(nextPermutation(a) == true); - assert(a == [3,1,2]); - assert(nextPermutation(a) == true); - assert(a == [3,2,1]); - assert(nextPermutation(a) == false); - assert(a == [1,2,3]); -} - -/// -@safe unittest -{ - // Step through permutations of an array containing duplicate elements: - int[] a = [1,1,2]; - assert(nextPermutation(a) == true); - assert(a == [1,2,1]); - assert(nextPermutation(a) == true); - assert(a == [2,1,1]); - assert(nextPermutation(a) == false); - assert(a == [1,1,2]); -} - -@safe unittest -{ - // Boundary cases: arrays of 0 or 1 element. - int[] a1 = []; - assert(!nextPermutation(a1)); - assert(a1 == []); - - int[] a2 = [1]; - assert(!nextPermutation(a2)); - assert(a2 == [1]); -} - -@safe unittest -{ - import std.algorithm.comparison : equal; - - auto a1 = [1, 2, 3, 4]; - - assert(nextPermutation(a1)); - assert(equal(a1, [1, 2, 4, 3])); - - assert(nextPermutation(a1)); - assert(equal(a1, [1, 3, 2, 4])); - - assert(nextPermutation(a1)); - assert(equal(a1, [1, 3, 4, 2])); - - assert(nextPermutation(a1)); - assert(equal(a1, [1, 4, 2, 3])); - - assert(nextPermutation(a1)); - assert(equal(a1, [1, 4, 3, 2])); - - assert(nextPermutation(a1)); - assert(equal(a1, [2, 1, 3, 4])); - - assert(nextPermutation(a1)); - assert(equal(a1, [2, 1, 4, 3])); - - assert(nextPermutation(a1)); - assert(equal(a1, [2, 3, 1, 4])); - - assert(nextPermutation(a1)); - assert(equal(a1, [2, 3, 4, 1])); - - assert(nextPermutation(a1)); - assert(equal(a1, [2, 4, 1, 3])); - - assert(nextPermutation(a1)); - assert(equal(a1, [2, 4, 3, 1])); - - assert(nextPermutation(a1)); - assert(equal(a1, [3, 1, 2, 4])); - - assert(nextPermutation(a1)); - assert(equal(a1, [3, 1, 4, 2])); - - assert(nextPermutation(a1)); - assert(equal(a1, [3, 2, 1, 4])); - - assert(nextPermutation(a1)); - assert(equal(a1, [3, 2, 4, 1])); - - assert(nextPermutation(a1)); - assert(equal(a1, [3, 4, 1, 2])); - - assert(nextPermutation(a1)); - assert(equal(a1, [3, 4, 2, 1])); - - assert(nextPermutation(a1)); - assert(equal(a1, [4, 1, 2, 3])); - - assert(nextPermutation(a1)); - assert(equal(a1, [4, 1, 3, 2])); - - assert(nextPermutation(a1)); - assert(equal(a1, [4, 2, 1, 3])); - - assert(nextPermutation(a1)); - assert(equal(a1, [4, 2, 3, 1])); - - assert(nextPermutation(a1)); - assert(equal(a1, [4, 3, 1, 2])); - - assert(nextPermutation(a1)); - assert(equal(a1, [4, 3, 2, 1])); - - assert(!nextPermutation(a1)); - assert(equal(a1, [1, 2, 3, 4])); -} - -@safe unittest -{ - // Test with non-default sorting order - int[] a = [3,2,1]; - assert(nextPermutation!"a > b"(a) == true); - assert(a == [3,1,2]); - assert(nextPermutation!"a > b"(a) == true); - assert(a == [2,3,1]); - assert(nextPermutation!"a > b"(a) == true); - assert(a == [2,1,3]); - assert(nextPermutation!"a > b"(a) == true); - assert(a == [1,3,2]); - assert(nextPermutation!"a > b"(a) == true); - assert(a == [1,2,3]); - assert(nextPermutation!"a > b"(a) == false); - assert(a == [3,2,1]); -} - -// https://issues.dlang.org/show_bug.cgi?id=13594 -@safe unittest -{ - int[3] a = [1,2,3]; - assert(nextPermutation(a[])); - assert(a == [1,3,2]); -} - -// nextEvenPermutation -/** - * Permutes `range` in-place to the next lexicographically greater $(I even) - * permutation. - * - * The predicate `less` defines the lexicographical ordering to be used on - * the range. - * - * An even permutation is one which is produced by swapping an even number of - * pairs of elements in the original range. The set of $(I even) permutations - * is distinct from the set of $(I all) permutations only when there are no - * duplicate elements in the range. If the range has $(I N) unique elements, - * then there are exactly $(I N)!/2 even permutations. - * - * If the range is already the lexicographically greatest even permutation, it - * is permuted back to the least even permutation and false is returned. - * Otherwise, true is returned, and the range is modified in-place to be the - * lexicographically next even permutation. - * - * One can thus generate the even permutations of a range with unique elements - * by starting with the lexicographically smallest permutation, and repeatedly - * calling nextEvenPermutation until it returns false. ----- -// Enumerate even permutations -int[] a = [1,2,3,4,5]; -do -{ - // use the current permutation and - // proceed to the next even permutation of the array. -} while (nextEvenPermutation(a)); ----- - * One can also generate the $(I odd) permutations of a range by noting that - * permutations obey the rule that even + even = even, and odd + even = odd. - * Thus, by swapping the last two elements of a lexicographically least range, - * it is turned into the first odd permutation. Then calling - * nextEvenPermutation on this first odd permutation will generate the next - * even permutation relative to this odd permutation, which is actually the - * next odd permutation of the original range. Thus, by repeatedly calling - * nextEvenPermutation until it returns false, one enumerates the odd - * permutations of the original range. ----- -// Enumerate odd permutations -int[] a = [1,2,3,4,5]; -swap(a[$-2], a[$-1]); // a is now the first odd permutation of [1,2,3,4,5] -do -{ - // use the current permutation and - // proceed to the next odd permutation of the original array - // (which is an even permutation of the first odd permutation). -} while (nextEvenPermutation(a)); ----- - * - * Warning: Since even permutations are only distinct from all permutations - * when the range elements are unique, this function assumes that there are no - * duplicate elements under the specified ordering. If this is not _true, some - * permutations may fail to be generated. When the range has non-unique - * elements, you should use $(MYREF nextPermutation) instead. - * - * Params: - * less = The ordering to be used to determine lexicographical ordering of the - * permutations. - * range = The range to permute. - * - * Returns: false if the range was lexicographically the greatest, in which - * case the range is reversed back to the lexicographically smallest - * permutation; otherwise returns true. - */ -bool nextEvenPermutation(alias less="a < b", BidirectionalRange) - (BidirectionalRange range) -if (isBidirectionalRange!BidirectionalRange && - hasSwappableElements!BidirectionalRange) -{ - import std.algorithm.mutation : reverse, swap; - import std.algorithm.searching : find; - import std.range : retro, takeExactly; - // Ranges of 0 or 1 element have no distinct permutations. - if (range.empty) return false; - - bool oddParity = false; - bool ret = true; - do - { - auto i = retro(range); - auto last = i.save; - - // Find last occurring increasing pair of elements - size_t n = 1; - for (i.popFront(); !i.empty; - i.popFront(), last.popFront(), n++) - { - if (binaryFun!less(i.front, last.front)) - break; - } - - if (!i.empty) - { - // Find last element greater than i.front. - auto j = find!((a) => binaryFun!less(i.front, a))( - takeExactly(retro(range), n)); - - // shouldn't happen since i.front < last.front - assert(!j.empty, "j must not be empty"); - - swap(i.front, j.front); - oddParity = !oddParity; - } - else - { - // Entire range is decreasing: it's lexicographically - // the greatest. - ret = false; - } - - reverse(takeExactly(retro(range), n)); - if ((n / 2) % 2 == 1) - oddParity = !oddParity; - } while (oddParity); - - return ret; -} - -/// -@safe unittest -{ - // Step through even permutations of a sorted array in lexicographic order - int[] a = [1,2,3]; - assert(nextEvenPermutation(a) == true); - assert(a == [2,3,1]); - assert(nextEvenPermutation(a) == true); - assert(a == [3,1,2]); - assert(nextEvenPermutation(a) == false); - assert(a == [1,2,3]); -} - -@safe unittest -{ - auto a3 = [ 1, 2, 3, 4 ]; - int count = 1; - while (nextEvenPermutation(a3)) count++; - assert(count == 12); -} - -@safe unittest -{ - // Test with non-default sorting order - auto a = [ 3, 2, 1 ]; - - assert(nextEvenPermutation!"a > b"(a) == true); - assert(a == [ 2, 1, 3 ]); - assert(nextEvenPermutation!"a > b"(a) == true); - assert(a == [ 1, 3, 2 ]); - assert(nextEvenPermutation!"a > b"(a) == false); - assert(a == [ 3, 2, 1 ]); -} - -@safe unittest -{ - // Test various cases of rollover - auto a = [ 3, 1, 2 ]; - assert(nextEvenPermutation(a) == false); - assert(a == [ 1, 2, 3 ]); - - auto b = [ 3, 2, 1 ]; - assert(nextEvenPermutation(b) == false); - assert(b == [ 1, 3, 2 ]); -} - -// https://issues.dlang.org/show_bug.cgi?id=13594 -@safe unittest -{ - int[3] a = [1,2,3]; - assert(nextEvenPermutation(a[])); - assert(a == [2,3,1]); -} - -/** -Even permutations are useful for generating coordinates of certain geometric -shapes. Here's a non-trivial example: -*/ -@safe unittest -{ - import std.math.algebraic : sqrt; - - // Print the 60 vertices of a uniform truncated icosahedron (soccer ball) - enum real Phi = (1.0 + sqrt(5.0)) / 2.0; // Golden ratio - real[][] seeds = [ - [0.0, 1.0, 3.0*Phi], - [1.0, 2.0+Phi, 2.0*Phi], - [Phi, 2.0, Phi^^3] - ]; - size_t n; - foreach (seed; seeds) - { - // Loop over even permutations of each seed - do - { - // Loop over all sign changes of each permutation - size_t i; - do - { - // Generate all possible sign changes - for (i=0; i < seed.length; i++) - { - if (seed[i] != 0.0) - { - seed[i] = -seed[i]; - if (seed[i] < 0.0) - break; - } - } - n++; - } while (i < seed.length); - } while (nextEvenPermutation(seed)); - } - assert(n == 60); -} - -/** -Permutes `range` into the `perm` permutation. - -The algorithm has a constant runtime complexity with respect to the number of -permutations created. -Due to the number of unique values of `ulong` only the first 21 elements of -`range` can be permuted. The rest of the range will therefore not be -permuted. -This algorithm uses the $(HTTP en.wikipedia.org/wiki/Lehmer_code, Lehmer -Code). - -The algorithm works as follows: -$(D_CODE - auto pem = [4,0,4,1,0,0,0]; // permutation 2982 in factorial - auto src = [0,1,2,3,4,5,6]; // the range to permutate - - auto i = 0; // range index - // range index iterates pem and src in sync - // pem[i] + i is used as index into src - // first src[pem[i] + i] is stored in t - auto t = 4; // tmp value - src = [0,1,2,3,n,5,6]; - - // then the values between i and pem[i] + i are moved one - // to the right - src = [n,0,1,2,3,5,6]; - // at last t is inserted into position i - src = [4,0,1,2,3,5,6]; - // finally i is incremented - ++i; - - // this process is repeated while i < pem.length - - t = 0; - src = [4,n,1,2,3,5,6]; - src = [4,0,1,2,3,5,6]; - ++i; - t = 6; - src = [4,0,1,2,3,5,n]; - src = [4,0,n,1,2,3,5]; - src = [4,0,6,1,2,3,5]; -) - -Returns: - The permuted range. - -Params: - range = The Range to permute. The original ordering will be lost. - perm = The permutation to permutate `range` to. -*/ -auto ref Range nthPermutation(Range) - (auto ref Range range, const ulong perm) -if (isRandomAccessRange!Range && hasLength!Range) -{ - if (!nthPermutationImpl(range, perm)) - { - throw new Exception( - "The range to permutate must not have less" - ~ " elements than the factorial number has digits"); - } - - return range; -} - -/// -pure @safe unittest -{ - auto src = [0, 1, 2, 3, 4, 5, 6]; - auto rslt = [4, 0, 6, 2, 1, 3, 5]; - - src = nthPermutation(src, 2982); - assert(src == rslt); -} - -/** -Returns: `true` in case the permutation worked, `false` in case `perm` had - more digits in the factorial number system than range had elements. - This case must not occur as this would lead to out of range accesses. -*/ -bool nthPermutationImpl(Range) - (auto ref Range range, ulong perm) -if (isRandomAccessRange!Range && hasLength!Range) -{ - import std.range.primitives : ElementType; - import std.numeric : decimalToFactorial; - - // ulong.max has 21 digits in the factorial number system - ubyte[21] fac; - size_t idx = decimalToFactorial(perm, fac); - - if (idx > range.length) - { - return false; - } - - ElementType!Range tmp; - size_t i = 0; - - for (; i < idx; ++i) - { - size_t re = fac[i]; - tmp = range[re + i]; - for (size_t j = re + i; j > i; --j) - { - range[j] = range[j - 1]; - } - range[i] = tmp; - } - - return true; -} - -/// -pure @safe unittest -{ - auto src = [0, 1, 2, 3, 4, 5, 6]; - auto rslt = [4, 0, 6, 2, 1, 3, 5]; - - bool worked = nthPermutationImpl(src, 2982); - assert(worked); - assert(src == rslt); -} - -pure @safe unittest -{ - auto rslt = [4, 0, 6, 2, 1, 3, 5]; - - auto src = nthPermutation([0, 1, 2, 3, 4, 5, 6], 2982); - assert(src == rslt); -} - -pure @safe unittest -{ - auto src = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10]; - auto rslt = [4, 0, 6, 2, 1, 3, 5, 7, 8, 9, 10]; - - src = nthPermutation(src, 2982); - assert(src == rslt); -} - -pure @safe unittest -{ - import std.exception : assertThrown; - - auto src = [0, 1, 2, 3]; - - assertThrown(nthPermutation(src, 2982)); -} - -pure @safe unittest -{ - import std.internal.test.dummyrange; - import std.meta : AliasSeq; - - auto src = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10]; - auto rsl = [4, 0, 6, 2, 1, 3, 5, 7, 8, 9, 10]; - - foreach (T; AliasSeq!( - DummyRange!(ReturnBy.Reference, Length.Yes, RangeType.Random, int[]), - DummyRange!(ReturnBy.Value, Length.Yes, RangeType.Random, int[]))) - { - static assert(isRandomAccessRange!(T)); - static assert(hasLength!(T)); - auto dr = T(src.dup); - dr = nthPermutation(dr, 2982); - - int idx; - foreach (it; dr) - { - assert(it == rsl[idx++]); - } - } -} diff --git a/phobos/std/array.d b/phobos/std/array.d deleted file mode 100644 index 494fa29..0000000 --- a/phobos/std/array.d +++ /dev/null @@ -1,4975 +0,0 @@ -// Written in the D programming language. -/** -Functions and types that manipulate built-in arrays and associative arrays. - -This module provides all kinds of functions to create, manipulate or convert arrays: - -$(SCRIPT inhibitQuickIndex = 1;) -$(DIVC quickindex, -$(BOOKTABLE , -$(TR $(TH Function Name) $(TH Description) -) - $(TR $(TD $(LREF array)) - $(TD Returns a copy of the input in a newly allocated dynamic array. - )) - $(TR $(TD $(LREF appender)) - $(TD Returns a new $(LREF Appender) or $(LREF RefAppender) initialized with a given array. - )) - $(TR $(TD $(LREF assocArray)) - $(TD Returns a newly allocated associative array from a range of key/value tuples. - )) - $(TR $(TD $(LREF byPair)) - $(TD Construct a range iterating over an associative array by key/value tuples. - )) - $(TR $(TD $(LREF insertInPlace)) - $(TD Inserts into an existing array at a given position. - )) - $(TR $(TD $(LREF join)) - $(TD Concatenates a range of ranges into one array. - )) - $(TR $(TD $(LREF minimallyInitializedArray)) - $(TD Returns a new array of type `T`. - )) - $(TR $(TD $(LREF replace)) - $(TD Returns a new array with all occurrences of a certain subrange replaced. - )) - $(TR $(TD $(LREF replaceFirst)) - $(TD Returns a new array with the first occurrence of a certain subrange replaced. - )) - $(TR $(TD $(LREF replaceInPlace)) - $(TD Replaces all occurrences of a certain subrange and puts the result into a given array. - )) - $(TR $(TD $(LREF replaceInto)) - $(TD Replaces all occurrences of a certain subrange and puts the result into an output range. - )) - $(TR $(TD $(LREF replaceLast)) - $(TD Returns a new array with the last occurrence of a certain subrange replaced. - )) - $(TR $(TD $(LREF replaceSlice)) - $(TD Returns a new array with a given slice replaced. - )) - $(TR $(TD $(LREF replicate)) - $(TD Creates a new array out of several copies of an input array or range. - )) - $(TR $(TD $(LREF sameHead)) - $(TD Checks if the initial segments of two arrays refer to the same - place in memory. - )) - $(TR $(TD $(LREF sameTail)) - $(TD Checks if the final segments of two arrays refer to the same place - in memory. - )) - $(TR $(TD $(LREF split)) - $(TD Eagerly split a range or string into an array. - )) - $(TR $(TD $(LREF staticArray)) - $(TD Creates a new static array from given data. - )) - $(TR $(TD $(LREF uninitializedArray)) - $(TD Returns a new array of type `T` without initializing its elements. - )) -)) - -Copyright: Copyright Andrei Alexandrescu 2008- and Jonathan M Davis 2011-. - -License: $(HTTP boost.org/LICENSE_1_0.txt, Boost License 1.0). - -Authors: $(HTTP erdani.org, Andrei Alexandrescu) and - $(HTTP jmdavisprog.com, Jonathan M Davis) - -Source: $(PHOBOSSRC std/array.d) -*/ -module std.array; - -import std.functional; -import std.meta; -import std.traits; - -import std.range.primitives; -public import std.range.primitives : save, empty, popFront, popBack, front, back; - -/** - * Allocates an array and initializes it with copies of the elements - * of range `r`. - * - * Narrow strings are handled as follows: - * - If autodecoding is turned on (default), then they are handled as a separate overload. - * - If autodecoding is turned off, then this is equivalent to duplicating the array. - * - * Params: - * r = range (or aggregate with `opApply` function) whose elements are copied into the allocated array - * Returns: - * allocated and initialized array - */ -ForeachType!Range[] array(Range)(Range r) -if (isIterable!Range && !isAutodecodableString!Range && !isInfinite!Range) -{ - if (__ctfe) - { - // Compile-time version to avoid memcpy calls. - // Also used to infer attributes of array(). - typeof(return) result; - foreach (e; r) - result ~= e; - return result; - } - - alias E = ForeachType!Range; - static if (hasLength!Range) - { - const length = r.length; - if (length == 0) - return null; - - import core.internal.lifetime : emplaceRef; - - auto result = (() @trusted => uninitializedArray!(Unqual!E[])(length))(); - - // Every element of the uninitialized array must be initialized - size_t cnt; //Number of elements that have been initialized - try - { - foreach (e; r) - { - emplaceRef!E(result[cnt], e); - ++cnt; - } - } catch (Exception e) - { - //https://issues.dlang.org/show_bug.cgi?id=22185 - //Make any uninitialized elements safely destructible. - foreach (ref elem; result[cnt..$]) - { - import core.internal.lifetime : emplaceInitializer; - emplaceInitializer(elem); - } - throw e; - } - /* - https://issues.dlang.org/show_bug.cgi?id=22673 - - We preallocated an array, we should ensure that enough range elements - were gathered such that every slot in the array is filled. If not, the GC - will collect the allocated array, leading to the `length - cnt` left over elements - being collected too - despite their contents having no guarantee of destructibility. - */ - assert(length == cnt, - "Range .length property was not equal to the length yielded by the range before becoming empty"); - return (() @trusted => cast(E[]) result)(); - } - else - { - auto a = appender!(E[])(); - foreach (e; r) - { - a.put(e); - } - return a.data; - } -} - -/// ditto -ForeachType!(typeof((*Range).init))[] array(Range)(Range r) -if (is(Range == U*, U) && isIterable!U && !isAutodecodableString!Range && !isInfinite!Range) -{ - return array(*r); -} - -/// -@safe pure nothrow unittest -{ - auto a = array([1, 2, 3, 4, 5][]); - assert(a == [ 1, 2, 3, 4, 5 ]); -} - -@safe pure nothrow unittest -{ - import std.algorithm.comparison : equal; - struct Foo - { - int a; - } - auto a = array([Foo(1), Foo(2), Foo(3), Foo(4), Foo(5)][]); - assert(equal(a, [Foo(1), Foo(2), Foo(3), Foo(4), Foo(5)])); -} - -@safe pure nothrow unittest -{ - struct MyRange - { - enum front = 123; - enum empty = true; - void popFront() {} - } - - auto arr = (new MyRange).array; - assert(arr.empty); -} - -@safe pure nothrow unittest -{ - immutable int[] a = [1, 2, 3, 4]; - auto b = (&a).array; - assert(b == a); -} - -@safe pure nothrow unittest -{ - import std.algorithm.comparison : equal; - struct Foo - { - int a; - noreturn opAssign(Foo) - { - assert(0); - } - auto opEquals(Foo foo) - { - return a == foo.a; - } - } - auto a = array([Foo(1), Foo(2), Foo(3), Foo(4), Foo(5)][]); - assert(equal(a, [Foo(1), Foo(2), Foo(3), Foo(4), Foo(5)])); -} - -// https://issues.dlang.org/show_bug.cgi?id=12315 -@safe pure nothrow unittest -{ - static struct Bug12315 { immutable int i; } - enum bug12315 = [Bug12315(123456789)].array(); - static assert(bug12315[0].i == 123456789); -} - -@safe pure nothrow unittest -{ - import std.range; - static struct S{int* p;} - auto a = array(immutable(S).init.repeat(5)); - assert(a.length == 5); -} - -// https://issues.dlang.org/show_bug.cgi?id=18995 -@system unittest -{ - import core.memory : __delete; - int nAlive = 0; - struct S - { - bool alive; - this(int) { alive = true; ++nAlive; } - this(this) { nAlive += alive; } - ~this() { nAlive -= alive; alive = false; } - } - - import std.algorithm.iteration : map; - import std.range : iota; - - auto arr = iota(3).map!(a => S(a)).array; - assert(nAlive == 3); - - // No good way to ensure the GC frees this, just call the lifetime function - // directly. - __delete(arr); - - assert(nAlive == 0); -} - -@safe pure nothrow @nogc unittest -{ - //Turn down infinity: - static assert(!is(typeof( - repeat(1).array() - ))); -} - -// https://issues.dlang.org/show_bug.cgi?id=20937 -@safe pure nothrow unittest -{ - struct S {int* x;} - struct R - { - immutable(S) front; - bool empty; - @safe pure nothrow void popFront(){empty = true;} - } - R().array; -} - -/** -Convert a narrow autodecoding string to an array type that fully supports -random access. This is handled as a special case and always returns an array -of `dchar` - -NOTE: This function is never used when autodecoding is turned off. - -Params: - str = `isNarrowString` to be converted to an array of `dchar` -Returns: - a `dchar[]`, `const(dchar)[]`, or `immutable(dchar)[]` depending on the constness of - the input. -*/ -CopyTypeQualifiers!(ElementType!String,dchar)[] array(String)(scope String str) -if (isAutodecodableString!String) -{ - import std.utf : toUTF32; - auto temp = str.toUTF32; - /* Unsafe cast. Allowed because toUTF32 makes a new array - and copies all the elements. - */ - return () @trusted { return cast(CopyTypeQualifiers!(ElementType!String, dchar)[]) temp; } (); -} - -/// -@safe pure nothrow unittest -{ - import std.range.primitives : isRandomAccessRange; - import std.traits : isAutodecodableString; - - // note that if autodecoding is turned off, `array` will not transcode these. - static if (isAutodecodableString!string) - assert("Hello D".array == "Hello D"d); - else - assert("Hello D".array == "Hello D"); - - static if (isAutodecodableString!wstring) - assert("Hello D"w.array == "Hello D"d); - else - assert("Hello D"w.array == "Hello D"w); - - static assert(isRandomAccessRange!dstring == true); -} - -@safe unittest -{ - import std.conv : to; - - static struct TestArray { int x; string toString() @safe { return to!string(x); } } - - static struct OpAssign - { - uint num; - this(uint num) { this.num = num; } - - // Templating opAssign to make sure the bugs with opAssign being - // templated are fixed. - void opAssign(T)(T rhs) { this.num = rhs.num; } - } - - static struct OpApply - { - int opApply(scope int delegate(ref int) @safe dg) - { - int res; - foreach (i; 0 .. 10) - { - res = dg(i); - if (res) break; - } - - return res; - } - } - - auto a = array([1, 2, 3, 4, 5][]); - assert(a == [ 1, 2, 3, 4, 5 ]); - - auto b = array([TestArray(1), TestArray(2)][]); - assert(b == [TestArray(1), TestArray(2)]); - - class C - { - int x; - this(int y) { x = y; } - override string toString() const @safe { return to!string(x); } - } - auto c = array([new C(1), new C(2)][]); - assert(c[0].x == 1); - assert(c[1].x == 2); - - auto d = array([1.0, 2.2, 3][]); - assert(is(typeof(d) == double[])); - assert(d == [1.0, 2.2, 3]); - - auto e = [OpAssign(1), OpAssign(2)]; - auto f = array(e); - assert(e == f); - - assert(array(OpApply.init) == [0,1,2,3,4,5,6,7,8,9]); - static if (isAutodecodableString!string) - { - assert(array("ABC") == "ABC"d); - assert(array("ABC".dup) == "ABC"d.dup); - } -} - -// https://issues.dlang.org/show_bug.cgi?id=8233 -@safe pure nothrow unittest -{ - assert(array("hello world"d) == "hello world"d); - immutable a = [1, 2, 3, 4, 5]; - assert(array(a) == a); - const b = a; - assert(array(b) == a); - - //To verify that the opAssign branch doesn't get screwed up by using Unqual. - //EDIT: array no longer calls opAssign. - struct S - { - ref S opAssign(S)(const ref S rhs) - { - assert(0); - } - - int i; - } - - static foreach (T; AliasSeq!(S, const S, immutable S)) - {{ - auto arr = [T(1), T(2), T(3), T(4)]; - assert(array(arr) == arr); - }} -} - -// https://issues.dlang.org/show_bug.cgi?id=9824 -@safe pure nothrow unittest -{ - static struct S - { - @disable void opAssign(S); - int i; - } - auto arr = [S(0), S(1), S(2)]; - arr.array(); -} - -// https://issues.dlang.org/show_bug.cgi?id=10220 -@safe pure nothrow unittest -{ - import std.algorithm.comparison : equal; - import std.exception; - import std.range : repeat; - - static struct S - { - int val; - - @disable this(); - this(int v) { val = v; } - } - assertCTFEable!( - { - auto r = S(1).repeat(2).array(); - assert(equal(r, [S(1), S(1)])); - }); -} -//https://issues.dlang.org/show_bug.cgi?id=22673 -@system unittest -{ - struct LyingRange - { - enum size_t length = 100; - enum theRealLength = 50; - size_t idx = 0; - bool empty() - { - return idx <= theRealLength; - } - void popFront() - { - ++idx; - } - size_t front() - { - return idx; - } - } - static assert(hasLength!LyingRange); - LyingRange rng; - import std.exception : assertThrown; - assertThrown!Error(array(rng)); -} -//https://issues.dlang.org/show_bug.cgi?id=22185 -@system unittest -{ - import std.stdio; - static struct ThrowingCopy - { - int x = 420; - this(ref return scope ThrowingCopy rhs) - { - rhs.x = 420; - // - throw new Exception("This throws"); - } - ~this() - { - /* - Any time this destructor runs, it should be running on "valid" - data. This is is mimicked by having a .init other than 0 (the value the memory - practically will be from the GC). - */ - if (x != 420) - { - //This will only trigger during GC finalization so avoid writefln for now. - printf("Destructor failure in ThrowingCopy(%d) @ %p", x, &this); - assert(x == 420, "unittest destructor failed"); - } - } - } - static struct LyingThrowingRange - { - enum size_t length = 100; - enum size_t evilRealLength = 50; - size_t idx; - ThrowingCopy front() - { - return ThrowingCopy(12); - } - bool empty() - { - return idx == evilRealLength; - } - void popFront() - { - ++idx; - } - } - static assert(hasLength!LyingThrowingRange); - import std.exception : assertThrown; - { - assertThrown(array(LyingThrowingRange())); - } - import core.memory : GC; - /* - Force a collection early. Doesn't always actually finalize the bad objects - but trying to collect soon after the allocation is thrown away means any potential failures - will happen earlier. - */ - GC.collect(); -} - -/** -Returns a newly allocated associative array from a range of key/value tuples -or from a range of keys and a range of values. - -Params: - r = An $(REF_ALTTEXT input range, isInputRange, std,range,primitives) - of tuples of keys and values. - keys = An $(REF_ALTTEXT input range, isInputRange, std,range,primitives) of keys - values = An $(REF_ALTTEXT input range, isInputRange, std,range,primitives) of values - -Returns: - - A newly allocated associative array out of elements of the input - range, which must be a range of tuples (Key, Value) or - a range of keys and a range of values. If given two ranges of unequal - lengths after the elements of the shorter are exhausted the remaining - elements of the longer will not be considered. - Returns a null associative array reference when given an empty range. - Duplicates: Associative arrays have unique keys. If r contains duplicate keys, - then the result will contain the value of the last pair for that key in r. - -See_Also: $(REF Tuple, std,typecons), $(REF zip, std,range) - */ - -auto assocArray(Range)(Range r) -if (isInputRange!Range) -{ - import std.typecons : isTuple; - - alias E = ElementType!Range; - static assert(isTuple!E, "assocArray: argument must be a range of tuples," - ~" but was a range of "~E.stringof); - static assert(E.length == 2, "assocArray: tuple dimension must be 2"); - alias KeyType = E.Types[0]; - alias ValueType = E.Types[1]; - static assert(isMutable!ValueType, "assocArray: value type must be mutable"); - - ValueType[KeyType] aa; - foreach (ref t; r) - aa[t[0]] = t[1]; - return aa; -} - -/// ditto -auto assocArray(Keys, Values)(Keys keys, Values values) -if (isInputRange!Values && isInputRange!Keys) -{ - static if (isDynamicArray!Keys && isDynamicArray!Values - && !isNarrowString!Keys && !isNarrowString!Values) - { - void* aa; - { - // aaLiteral is nothrow when the destructors don't throw - static if (is(typeof(() nothrow - { - import std.range : ElementType; - import std.traits : hasElaborateDestructor; - alias KeyElement = ElementType!Keys; - static if (hasElaborateDestructor!KeyElement) - KeyElement.init.__xdtor(); - - alias ValueElement = ElementType!Values; - static if (hasElaborateDestructor!ValueElement) - ValueElement.init.__xdtor(); - }))) - { - scope(failure) assert(false, "aaLiteral must not throw"); - } - if (values.length > keys.length) - values = values[0 .. keys.length]; - else if (keys.length > values.length) - keys = keys[0 .. values.length]; - aa = aaLiteral(keys, values); - } - alias Key = typeof(keys[0]); - alias Value = typeof(values[0]); - return (() @trusted => cast(Value[Key]) aa)(); - } - else - { - // zip is not always able to infer nothrow - alias Key = ElementType!Keys; - alias Value = ElementType!Values; - static assert(isMutable!Value, "assocArray: value type must be mutable"); - Value[Key] aa; - foreach (key; keys) - { - if (values.empty) break; - - // aa[key] is incorrectly not @safe if the destructor throws - // https://issues.dlang.org/show_bug.cgi?id=18592 - static if (is(typeof(() @safe - { - import std.range : ElementType; - import std.traits : hasElaborateDestructor; - alias KeyElement = ElementType!Keys; - static if (hasElaborateDestructor!KeyElement) - KeyElement.init.__xdtor(); - - alias ValueElement = ElementType!Values; - static if (hasElaborateDestructor!ValueElement) - ValueElement.init.__xdtor(); - }))) - { - () @trusted { - aa[key] = values.front; - }(); - } - else - { - aa[key] = values.front; - } - values.popFront(); - } - return aa; - } -} - -/// -@safe pure /*nothrow*/ unittest -{ - import std.range : repeat, zip; - import std.typecons : tuple; - import std.range.primitives : autodecodeStrings; - auto a = assocArray(zip([0, 1, 2], ["a", "b", "c"])); // aka zipMap - static assert(is(typeof(a) == string[int])); - assert(a == [0:"a", 1:"b", 2:"c"]); - - auto b = assocArray([ tuple("foo", "bar"), tuple("baz", "quux") ]); - static assert(is(typeof(b) == string[string])); - assert(b == ["foo":"bar", "baz":"quux"]); - - static if (autodecodeStrings) - alias achar = dchar; - else - alias achar = immutable(char); - auto c = assocArray("ABCD", true.repeat); - static assert(is(typeof(c) == bool[achar])); - bool[achar] expected = ['D':true, 'A':true, 'B':true, 'C':true]; - assert(c == expected); -} - -// Cannot be version (StdUnittest) - recursive instantiation error -// https://issues.dlang.org/show_bug.cgi?id=11053 -@safe pure nothrow unittest -{ - import std.typecons; - static assert(!__traits(compiles, [ 1, 2, 3 ].assocArray())); - static assert(!__traits(compiles, [ tuple("foo", "bar", "baz") ].assocArray())); - static assert(!__traits(compiles, [ tuple("foo") ].assocArray())); - assert([ tuple("foo", "bar") ].assocArray() == ["foo": "bar"]); -} - -// https://issues.dlang.org/show_bug.cgi?id=13909 -@safe pure nothrow unittest -{ - import std.typecons; - auto a = [tuple!(const string, string)("foo", "bar")]; - auto b = [tuple!(string, const string)("foo", "bar")]; - assert(a == b); - assert(assocArray(a) == [cast(const(string)) "foo": "bar"]); - static assert(!__traits(compiles, assocArray(b))); -} - -// https://issues.dlang.org/show_bug.cgi?id=5502 -@safe pure nothrow unittest -{ - auto a = assocArray([0, 1, 2], ["a", "b", "c"]); - static assert(is(typeof(a) == string[int])); - assert(a == [0:"a", 1:"b", 2:"c"]); - - auto b = assocArray([0, 1, 2], [3L, 4, 5]); - static assert(is(typeof(b) == long[int])); - assert(b == [0: 3L, 1: 4, 2: 5]); -} - -// https://issues.dlang.org/show_bug.cgi?id=5502 -@safe pure unittest -{ - import std.algorithm.iteration : filter, map; - import std.range : enumerate; - import std.range.primitives : autodecodeStrings; - - auto r = "abcde".enumerate.filter!(a => a.index == 2); - auto a = assocArray(r.map!(a => a.value), r.map!(a => a.index)); - static if (autodecodeStrings) - alias achar = dchar; - else - alias achar = immutable(char); - static assert(is(typeof(a) == size_t[achar])); - assert(a == [achar('c'): size_t(2)]); -} - -@safe nothrow pure unittest -{ - import std.range : iota; - auto b = assocArray(3.iota, 3.iota(6)); - static assert(is(typeof(b) == int[int])); - assert(b == [0: 3, 1: 4, 2: 5]); - - b = assocArray([0, 1, 2], [3, 4, 5]); - assert(b == [0: 3, 1: 4, 2: 5]); -} - -@safe unittest -{ - struct ThrowingElement - { - int i; - static bool b; - ~this(){ - if (b) - throw new Exception(""); - } - } - static assert(!__traits(compiles, () nothrow { assocArray([ThrowingElement()], [0]);})); - assert(assocArray([ThrowingElement()], [0]) == [ThrowingElement(): 0]); - - static assert(!__traits(compiles, () nothrow { assocArray([0], [ThrowingElement()]);})); - assert(assocArray([0], [ThrowingElement()]) == [0: ThrowingElement()]); - - import std.range : iota; - static assert(!__traits(compiles, () nothrow { assocArray(1.iota, [ThrowingElement()]);})); - assert(assocArray(1.iota, [ThrowingElement()]) == [0: ThrowingElement()]); -} - -@system unittest -{ - import std.range : iota; - struct UnsafeElement - { - int i; - static bool b; - ~this(){ - int[] arr; - void* p = arr.ptr + 1; // unsafe - } - } - static assert(!__traits(compiles, () @safe { assocArray(1.iota, [UnsafeElement()]);})); - assert(assocArray(1.iota, [UnsafeElement()]) == [0: UnsafeElement()]); -} - -/** -Construct a range iterating over an associative array by key/value tuples. - -Params: - aa = The associative array to iterate over. - -Returns: A $(REF_ALTTEXT forward range, isForwardRange, std,range,primitives) -of Tuple's of key and value pairs from the given associative array. The members -of each pair can be accessed by name (`.key` and `.value`). or by integer -index (0 and 1 respectively). -*/ -auto byPair(AA)(AA aa) -if (isAssociativeArray!AA) -{ - import std.algorithm.iteration : map; - import std.typecons : tuple; - - return aa.byKeyValue - .map!(pair => tuple!("key", "value")(pair.key, pair.value)); -} - -/// -@safe pure nothrow unittest -{ - import std.algorithm.sorting : sort; - import std.typecons : tuple, Tuple; - - auto aa = ["a": 1, "b": 2, "c": 3]; - Tuple!(string, int)[] pairs; - - // Iteration over key/value pairs. - foreach (pair; aa.byPair) - { - if (pair.key == "b") - pairs ~= tuple("B", pair.value); - else - pairs ~= pair; - } - - // Iteration order is implementation-dependent, so we should sort it to get - // a fixed order. - pairs.sort(); - assert(pairs == [ - tuple("B", 2), - tuple("a", 1), - tuple("c", 3) - ]); -} - -@safe pure nothrow unittest -{ - import std.typecons : tuple, Tuple; - import std.meta : AliasSeq; - - auto aa = ["a":2]; - auto pairs = aa.byPair(); - - alias PT = typeof(pairs.front); - static assert(is(PT : Tuple!(string,int))); - static assert(PT.fieldNames == AliasSeq!("key", "value")); - static assert(isForwardRange!(typeof(pairs))); - - assert(!pairs.empty); - assert(pairs.front == tuple("a", 2)); - - auto savedPairs = pairs.save; - - pairs.popFront(); - assert(pairs.empty); - assert(!savedPairs.empty); - assert(savedPairs.front == tuple("a", 2)); -} - -// https://issues.dlang.org/show_bug.cgi?id=17711 -@safe pure nothrow unittest -{ - const(int[string]) aa = [ "abc": 123 ]; - - // Ensure that byKeyValue is usable with a const AA. - auto kv = aa.byKeyValue; - assert(!kv.empty); - assert(kv.front.key == "abc" && kv.front.value == 123); - kv.popFront(); - assert(kv.empty); - - // Ensure byPair is instantiable with const AA. - auto r = aa.byPair; - static assert(isInputRange!(typeof(r))); - assert(!r.empty && r.front[0] == "abc" && r.front[1] == 123); - r.popFront(); - assert(r.empty); -} - -private template blockAttribute(T) -{ - import core.memory; - static if (hasIndirections!(T) || is(T == void)) - { - enum blockAttribute = 0; - } - else - { - enum blockAttribute = GC.BlkAttr.NO_SCAN; - } -} - -@safe unittest -{ - import core.memory : UGC = GC; - static assert(!(blockAttribute!void & UGC.BlkAttr.NO_SCAN)); -} - -// Returns the number of dimensions in an array T. -private template nDimensions(T) -{ - static if (isArray!T) - { - enum nDimensions = 1 + nDimensions!(typeof(T.init[0])); - } - else - { - enum nDimensions = 0; - } -} - -@safe unittest -{ - static assert(nDimensions!(uint[]) == 1); - static assert(nDimensions!(float[][]) == 2); -} - -/++ -Returns a new array of type `T` allocated on the garbage collected heap -without initializing its elements. This can be a useful optimization if every -element will be immediately initialized. `T` may be a multidimensional -array. In this case sizes may be specified for any number of dimensions from 0 -to the number in `T`. - -uninitializedArray is `nothrow` and weakly `pure`. - -uninitializedArray is `@system` if the uninitialized element type has pointers. - -Params: - T = The type of the resulting array elements - sizes = The length dimension(s) of the resulting array -Returns: - An array of `T` with `I.length` dimensions. -+/ -auto uninitializedArray(T, I...)(I sizes) nothrow @system -if (isDynamicArray!T && allSatisfy!(isIntegral, I) && hasIndirections!(ElementEncodingType!T)) -{ - enum isSize_t(E) = is (E : size_t); - alias toSize_t(E) = size_t; - - static assert(allSatisfy!(isSize_t, I), - "Argument types in "~I.stringof~" are not all convertible to size_t: " - ~Filter!(templateNot!(isSize_t), I).stringof); - - //Eagerlly transform non-size_t into size_t to avoid template bloat - alias ST = staticMap!(toSize_t, I); - - return arrayAllocImpl!(false, T, ST)(sizes); -} - -/// ditto -auto uninitializedArray(T, I...)(I sizes) nothrow @trusted -if (isDynamicArray!T && allSatisfy!(isIntegral, I) && !hasIndirections!(ElementEncodingType!T)) -{ - enum isSize_t(E) = is (E : size_t); - alias toSize_t(E) = size_t; - - static assert(allSatisfy!(isSize_t, I), - "Argument types in "~I.stringof~" are not all convertible to size_t: " - ~Filter!(templateNot!(isSize_t), I).stringof); - - //Eagerlly transform non-size_t into size_t to avoid template bloat - alias ST = staticMap!(toSize_t, I); - - return arrayAllocImpl!(false, T, ST)(sizes); -} -/// -@system nothrow pure unittest -{ - double[] arr = uninitializedArray!(double[])(100); - assert(arr.length == 100); - - double[][] matrix = uninitializedArray!(double[][])(42, 31); - assert(matrix.length == 42); - assert(matrix[0].length == 31); - - char*[] ptrs = uninitializedArray!(char*[])(100); - assert(ptrs.length == 100); -} - -/++ -Returns a new array of type `T` allocated on the garbage collected heap. - -Partial initialization is done for types with indirections, for preservation -of memory safety. Note that elements will only be initialized to 0, but not -necessarily the element type's `.init`. - -minimallyInitializedArray is `nothrow` and weakly `pure`. - -Params: - T = The type of the array elements - sizes = The length dimension(s) of the resulting array -Returns: - An array of `T` with `I.length` dimensions. -+/ -auto minimallyInitializedArray(T, I...)(I sizes) nothrow @trusted -if (isDynamicArray!T && allSatisfy!(isIntegral, I)) -{ - enum isSize_t(E) = is (E : size_t); - alias toSize_t(E) = size_t; - - static assert(allSatisfy!(isSize_t, I), - "Argument types in "~I.stringof~" are not all convertible to size_t: " - ~Filter!(templateNot!(isSize_t), I).stringof); - //Eagerlly transform non-size_t into size_t to avoid template bloat - alias ST = staticMap!(toSize_t, I); - - return arrayAllocImpl!(true, T, ST)(sizes); -} - -/// -@safe pure nothrow unittest -{ - import std.algorithm.comparison : equal; - import std.range : repeat; - - auto arr = minimallyInitializedArray!(int[])(42); - assert(arr.length == 42); - - // Elements aren't necessarily initialized to 0, so don't do this: - // assert(arr.equal(0.repeat(42))); - // If that is needed, initialize the array normally instead: - auto arr2 = new int[42]; - assert(arr2.equal(0.repeat(42))); -} - -@safe pure nothrow unittest -{ - cast(void) minimallyInitializedArray!(int[][][][][])(); - double[] arr = minimallyInitializedArray!(double[])(100); - assert(arr.length == 100); - - double[][] matrix = minimallyInitializedArray!(double[][])(42); - assert(matrix.length == 42); - foreach (elem; matrix) - { - assert(elem.ptr is null); - } -} - -// from rt/lifetime.d -private extern(C) void[] _d_newarrayU(const TypeInfo ti, size_t length) pure nothrow; - -// from rt/tracegc.d -version (D_ProfileGC) -private extern (C) void[] _d_newarrayUTrace(string file, size_t line, - string funcname, const scope TypeInfo ti, size_t length) pure nothrow; - -private auto arrayAllocImpl(bool minimallyInitialized, T, I...)(I sizes) nothrow -{ - static assert(I.length <= nDimensions!T, - I.length.stringof~"dimensions specified for a "~nDimensions!T.stringof~" dimensional array."); - - alias E = ElementEncodingType!T; - - E[] ret; - - static if (I.length != 0) - { - static assert(is(I[0] == size_t), "I[0] must be of type size_t not " - ~ I[0].stringof); - alias size = sizes[0]; - } - - static if (I.length == 1) - { - if (__ctfe) - { - static if (__traits(compiles, new E[](size))) - ret = new E[](size); - else static if (__traits(compiles, ret ~= E.init)) - { - try - { - //Issue: if E has an impure postblit, then all of arrayAllocImpl - //Will be impure, even during non CTFE. - foreach (i; 0 .. size) - ret ~= E.init; - } - catch (Exception e) - assert(0, e.msg); - } - else - assert(0, "No postblit nor default init on " ~ E.stringof ~ - ": At least one is required for CTFE."); - } - else - { - import core.stdc.string : memset; - - /+ - NOTES: - _d_newarrayU is part of druntime, and creates an uninitialized - block, just like GC.malloc. However, it also sets the appropriate - bits, and sets up the block as an appendable array of type E[], - which will inform the GC how to destroy the items in the block - when it gets collected. - - _d_newarrayU returns a void[], but with the length set according - to E.sizeof. - +/ - version (D_ProfileGC) - { - // FIXME: file, line, function should be propagated from the - // caller, not here. - *(cast(void[]*)&ret) = _d_newarrayUTrace(__FILE__, __LINE__, - __FUNCTION__, typeid(E[]), size); - } - else - *(cast(void[]*)&ret) = _d_newarrayU(typeid(E[]), size); - static if (minimallyInitialized && hasIndirections!E) - // _d_newarrayU would have asserted if the multiplication below - // had overflowed, so we don't have to check it again. - memset(ret.ptr, 0, E.sizeof * ret.length); - } - } - else static if (I.length > 1) - { - ret = arrayAllocImpl!(false, E[])(size); - foreach (ref elem; ret) - elem = arrayAllocImpl!(minimallyInitialized, E)(sizes[1..$]); - } - - return ret; -} - -@safe nothrow pure unittest -{ - auto s1 = uninitializedArray!(int[])(); - auto s2 = minimallyInitializedArray!(int[])(); - assert(s1.length == 0); - assert(s2.length == 0); -} - -// https://issues.dlang.org/show_bug.cgi?id=9803 -@safe nothrow pure unittest -{ - auto a = minimallyInitializedArray!(int*[])(1); - assert(a[0] == null); - auto b = minimallyInitializedArray!(int[][])(1); - assert(b[0].empty); - auto c = minimallyInitializedArray!(int*[][])(1, 1); - assert(c[0][0] == null); -} - -// https://issues.dlang.org/show_bug.cgi?id=10637 -@safe pure nothrow unittest -{ - static struct S - { - static struct I{int i; alias i this;} - int* p; - this() @disable; - this(int i) - { - p = &(new I(i)).i; - } - this(this) - { - p = &(new I(*p)).i; - } - ~this() - { - // note, this assert is invalid -- a struct should always be able - // to run its dtor on the .init value, I'm leaving it here - // commented out because the original test case had it. I'm not - // sure what it's trying to prove. - // - // What happens now that minimallyInitializedArray adds the - // destructor run to the GC, is that this assert would fire in the - // GC, which triggers an invalid memory operation. - //assert(p != null); - } - } - auto a = minimallyInitializedArray!(S[])(1); - assert(a[0].p == null); - enum b = minimallyInitializedArray!(S[])(1); - assert(b[0].p == null); -} - -@safe pure nothrow unittest -{ - static struct S1 - { - this() @disable; - this(this) @disable; - } - auto a1 = minimallyInitializedArray!(S1[][])(2, 2); - assert(a1); - static struct S2 - { - this() @disable; - //this(this) @disable; - } - auto a2 = minimallyInitializedArray!(S2[][])(2, 2); - assert(a2); - enum b2 = minimallyInitializedArray!(S2[][])(2, 2); - assert(b2 !is null); - static struct S3 - { - //this() @disable; - this(this) @disable; - } - auto a3 = minimallyInitializedArray!(S3[][])(2, 2); - assert(a3); - enum b3 = minimallyInitializedArray!(S3[][])(2, 2); - assert(b3 !is null); -} - -/++ -Returns the overlapping portion, if any, of two arrays. Unlike `equal`, -`overlap` only compares the pointers and lengths in the -ranges, not the values referred by them. If `r1` and `r2` have an -overlapping slice, returns that slice. Otherwise, returns the null -slice. - -Params: - a = The first array to compare - b = The second array to compare -Returns: - The overlapping portion of the two arrays. -+/ -CommonType!(T[], U[]) overlap(T, U)(T[] a, U[] b) @trusted -if (is(typeof(a.ptr < b.ptr) == bool)) -{ - import std.algorithm.comparison : min; - - auto end = min(a.ptr + a.length, b.ptr + b.length); - // CTFE requires pairing pointer comparisons, which forces a - // slightly inefficient implementation. - if (a.ptr <= b.ptr && b.ptr < a.ptr + a.length) - { - return b.ptr[0 .. end - b.ptr]; - } - - if (b.ptr <= a.ptr && a.ptr < b.ptr + b.length) - { - return a.ptr[0 .. end - a.ptr]; - } - - return null; -} - -/// -@safe pure nothrow unittest -{ - int[] a = [ 10, 11, 12, 13, 14 ]; - int[] b = a[1 .. 3]; - assert(overlap(a, b) == [ 11, 12 ]); - b = b.dup; - // overlap disappears even though the content is the same - assert(overlap(a, b).empty); - - static test()() @nogc - { - auto a = "It's three o'clock"d; - auto b = a[5 .. 10]; - return b.overlap(a); - } - - //works at compile-time - static assert(test == "three"d); -} - -@safe pure nothrow unittest -{ - static void test(L, R)(L l, R r) - { - assert(overlap(l, r) == [ 100, 12 ]); - - assert(overlap(l, l[0 .. 2]) is l[0 .. 2]); - assert(overlap(l, l[3 .. 5]) is l[3 .. 5]); - assert(overlap(l[0 .. 2], l) is l[0 .. 2]); - assert(overlap(l[3 .. 5], l) is l[3 .. 5]); - } - - int[] a = [ 10, 11, 12, 13, 14 ]; - int[] b = a[1 .. 3]; - a[1] = 100; - - immutable int[] c = a.idup; - immutable int[] d = c[1 .. 3]; - - test(a, b); - assert(overlap(a, b.dup).empty); - test(c, d); - assert(overlap(c, d.dup.idup).empty); -} - - // https://issues.dlang.org/show_bug.cgi?id=9836 -@safe pure nothrow unittest -{ - // range primitives for array should work with alias this types - struct Wrapper - { - int[] data; - alias data this; - - @property Wrapper save() { return this; } - } - auto w = Wrapper([1,2,3,4]); - std.array.popFront(w); // should work - - static assert(isInputRange!Wrapper); - static assert(isForwardRange!Wrapper); - static assert(isBidirectionalRange!Wrapper); - static assert(isRandomAccessRange!Wrapper); -} - -private void copyBackwards(T)(T[] src, T[] dest) -{ - import core.stdc.string : memmove; - import std.format : format; - - assert(src.length == dest.length, format! - "src.length %s must equal dest.length %s"(src.length, dest.length)); - - if (!__ctfe || hasElaborateCopyConstructor!T) - { - /* insertInPlace relies on dest being uninitialized, so no postblits allowed, - * as this is a MOVE that overwrites the destination, not a COPY. - * BUG: insertInPlace will not work with ctfe and postblits - */ - memmove(dest.ptr, src.ptr, src.length * T.sizeof); - } - else - { - immutable len = src.length; - for (size_t i = len; i-- > 0;) - { - dest[i] = src[i]; - } - } -} - -/++ - Inserts `stuff` (which must be an input range or any number of - implicitly convertible items) in `array` at position `pos`. - - Params: - array = The array that `stuff` will be inserted into. - pos = The position in `array` to insert the `stuff`. - stuff = An $(REF_ALTTEXT input range, isInputRange, std,range,primitives), - or any number of implicitly convertible items to insert into `array`. - +/ -void insertInPlace(T, U...)(ref T[] array, size_t pos, U stuff) -if (!isSomeString!(T[]) - && allSatisfy!(isInputRangeOrConvertible!T, U) && U.length > 0) -{ - static if (allSatisfy!(isInputRangeWithLengthOrConvertible!T, U)) - { - import core.internal.lifetime : emplaceRef; - - immutable oldLen = array.length; - - size_t to_insert = 0; - foreach (i, E; U) - { - static if (is(E : T)) //a single convertible value, not a range - to_insert += 1; - else - to_insert += stuff[i].length; - } - if (to_insert) - { - array.length += to_insert; - - // Takes arguments array, pos, stuff - // Spread apart array[] at pos by moving elements - (() @trusted { copyBackwards(array[pos .. oldLen], array[pos+to_insert..$]); })(); - - // Initialize array[pos .. pos+to_insert] with stuff[] - auto j = 0; - foreach (i, E; U) - { - static if (is(E : T)) - { - emplaceRef!T(array[pos + j++], stuff[i]); - } - else - { - foreach (v; stuff[i]) - { - emplaceRef!T(array[pos + j++], v); - } - } - } - } - } - else - { - // stuff has some InputRanges in it that don't have length - // assume that stuff to be inserted is typically shorter - // then the array that can be arbitrary big - // TODO: needs a better implementation as there is no need to build an _array_ - // a singly-linked list of memory blocks (rope, etc.) will do - auto app = appender!(T[])(); - foreach (i, E; U) - app.put(stuff[i]); - insertInPlace(array, pos, app.data); - } -} - -/// Ditto -void insertInPlace(T, U...)(ref T[] array, size_t pos, U stuff) -if (isSomeString!(T[]) && allSatisfy!(isCharOrStringOrDcharRange, U)) -{ - static if (is(Unqual!T == T) - && allSatisfy!(isInputRangeWithLengthOrConvertible!dchar, U)) - { - import std.utf : codeLength, byDchar; - // mutable, can do in place - //helper function: re-encode dchar to Ts and store at *ptr - static T* putDChar(T* ptr, dchar ch) - { - static if (is(T == dchar)) - { - *ptr++ = ch; - return ptr; - } - else - { - import std.utf : encode; - T[dchar.sizeof/T.sizeof] buf; - immutable len = encode(buf, ch); - final switch (len) - { - static if (T.sizeof == char.sizeof) - { - case 4: - ptr[3] = buf[3]; - goto case; - case 3: - ptr[2] = buf[2]; - goto case; - } - case 2: - ptr[1] = buf[1]; - goto case; - case 1: - ptr[0] = buf[0]; - } - ptr += len; - return ptr; - } - } - size_t to_insert = 0; - //count up the number of *codeunits* to insert - foreach (i, E; U) - to_insert += codeLength!T(stuff[i]); - array.length += to_insert; - - @trusted static void moveToRight(T[] arr, size_t gap) - { - static assert(!hasElaborateCopyConstructor!T, - "T must not have an elaborate copy constructor"); - import core.stdc.string : memmove; - if (__ctfe) - { - for (size_t i = arr.length - gap; i; --i) - arr[gap + i - 1] = arr[i - 1]; - } - else - memmove(arr.ptr + gap, arr.ptr, (arr.length - gap) * T.sizeof); - } - moveToRight(array[pos .. $], to_insert); - auto ptr = array.ptr + pos; - foreach (i, E; U) - { - static if (is(E : dchar)) - { - ptr = putDChar(ptr, stuff[i]); - } - else - { - foreach (ch; stuff[i].byDchar) - ptr = putDChar(ptr, ch); - } - } - assert(ptr == array.ptr + pos + to_insert, "(ptr == array.ptr + pos + to_insert) is false"); - } - else - { - // immutable/const, just construct a new array - auto app = appender!(T[])(); - app.put(array[0 .. pos]); - foreach (i, E; U) - app.put(stuff[i]); - app.put(array[pos..$]); - array = app.data; - } -} - -/// -@safe pure unittest -{ - int[] a = [ 1, 2, 3, 4 ]; - a.insertInPlace(2, [ 1, 2 ]); - assert(a == [ 1, 2, 1, 2, 3, 4 ]); - a.insertInPlace(3, 10u, 11); - assert(a == [ 1, 2, 1, 10, 11, 2, 3, 4]); - - union U - { - float a = 3.0; - int b; - } - - U u1 = { b : 3 }; - U u2 = { b : 4 }; - U u3 = { b : 5 }; - U[] unionArr = [u2, u3]; - unionArr.insertInPlace(2, [u1]); - assert(unionArr == [u2, u3, u1]); - unionArr.insertInPlace(0, [u3, u2]); - assert(unionArr == [u3, u2, u2, u3, u1]); - - static class C - { - int a; - float b; - - this(int a, float b) { this.a = a; this.b = b; } - } - - C c1 = new C(42, 1.0); - C c2 = new C(0, 0.0); - C c3 = new C(int.max, float.init); - - C[] classArr = [c1, c2, c3]; - insertInPlace(classArr, 3, [c2, c3]); - C[5] classArr1 = classArr; - assert(classArr1 == [c1, c2, c3, c2, c3]); - insertInPlace(classArr, 0, c3, c1); - C[7] classArr2 = classArr; - assert(classArr2 == [c3, c1, c1, c2, c3, c2, c3]); -} - -//constraint helpers -private template isInputRangeWithLengthOrConvertible(E) -{ - template isInputRangeWithLengthOrConvertible(R) - { - //hasLength not defined for char[], wchar[] and dchar[] - enum isInputRangeWithLengthOrConvertible = - (isInputRange!R && is(typeof(R.init.length)) - && is(ElementType!R : E)) || is(R : E); - } -} - -//ditto -private template isCharOrStringOrDcharRange(T) -{ - enum isCharOrStringOrDcharRange = isSomeString!T || isSomeChar!T || - (isInputRange!T && is(ElementType!T : dchar)); -} - -//ditto -private template isInputRangeOrConvertible(E) -{ - template isInputRangeOrConvertible(R) - { - enum isInputRangeOrConvertible = - (isInputRange!R && is(ElementType!R : E)) || is(R : E); - } -} - -@system unittest -{ - // @system due to insertInPlace - import core.exception; - import std.algorithm.comparison : equal; - import std.algorithm.iteration : filter; - import std.conv : to; - import std.exception; - - - bool test(T, U, V)(T orig, size_t pos, U toInsert, V result) - { - { - static if (is(T == typeof(T.init.dup))) - auto a = orig.dup; - else - auto a = orig.idup; - - a.insertInPlace(pos, toInsert); - if (!equal(a, result)) - return false; - } - - static if (isInputRange!U) - { - orig.insertInPlace(pos, filter!"true"(toInsert)); - return equal(orig, result); - } - else - return true; - } - - - assert(test([1, 2, 3, 4], 0, [6, 7], [6, 7, 1, 2, 3, 4])); - assert(test([1, 2, 3, 4], 2, [8, 9], [1, 2, 8, 9, 3, 4])); - assert(test([1, 2, 3, 4], 4, [10, 11], [1, 2, 3, 4, 10, 11])); - - assert(test([1, 2, 3, 4], 0, 22, [22, 1, 2, 3, 4])); - assert(test([1, 2, 3, 4], 2, 23, [1, 2, 23, 3, 4])); - assert(test([1, 2, 3, 4], 4, 24, [1, 2, 3, 4, 24])); - - void testStr(T, U)(string file = __FILE__, size_t line = __LINE__) - { - - auto l = to!T("hello"); - auto r = to!U(" વિશ્વ"); - - enforce(test(l, 0, r, " વિશ્વhello"), - new AssertError("testStr failure 1", file, line)); - enforce(test(l, 3, r, "hel વિશ્વlo"), - new AssertError("testStr failure 2", file, line)); - enforce(test(l, l.length, r, "hello વિશ્વ"), - new AssertError("testStr failure 3", file, line)); - } - - static foreach (T; AliasSeq!(char, wchar, dchar, - immutable(char), immutable(wchar), immutable(dchar))) - { - static foreach (U; AliasSeq!(char, wchar, dchar, - immutable(char), immutable(wchar), immutable(dchar))) - { - testStr!(T[], U[])(); - } - - } - - // variadic version - bool testVar(T, U...)(T orig, size_t pos, U args) - { - static if (is(T == typeof(T.init.dup))) - auto a = orig.dup; - else - auto a = orig.idup; - auto result = args[$-1]; - - a.insertInPlace(pos, args[0..$-1]); - if (!equal(a, result)) - return false; - return true; - } - assert(testVar([1, 2, 3, 4], 0, 6, 7u, [6, 7, 1, 2, 3, 4])); - assert(testVar([1L, 2, 3, 4], 2, 8, 9L, [1, 2, 8, 9, 3, 4])); - assert(testVar([1L, 2, 3, 4], 4, 10L, 11, [1, 2, 3, 4, 10, 11])); - assert(testVar([1L, 2, 3, 4], 4, [10, 11], 40L, 42L, - [1, 2, 3, 4, 10, 11, 40, 42])); - assert(testVar([1L, 2, 3, 4], 4, 10, 11, [40L, 42], - [1, 2, 3, 4, 10, 11, 40, 42])); - assert(testVar("t".idup, 1, 'e', 's', 't', "test")); - assert(testVar("!!"w.idup, 1, "\u00e9ll\u00f4", 'x', "TTT"w, 'y', - "!\u00e9ll\u00f4xTTTy!")); - assert(testVar("flipflop"d.idup, 4, '_', - "xyz"w, '\U00010143', '_', "abc"d, "__", - "flip_xyz\U00010143_abc__flop")); -} - -@system unittest -{ - import std.algorithm.comparison : equal; - // insertInPlace interop with postblit - static struct Int - { - int* payload; - this(int k) - { - payload = new int; - *payload = k; - } - this(this) - { - int* np = new int; - *np = *payload; - payload = np; - } - ~this() - { - if (payload) - *payload = 0; //'destroy' it - } - @property int getPayload(){ return *payload; } - alias getPayload this; - } - - Int[] arr = [Int(1), Int(4), Int(5)]; - assert(arr[0] == 1); - insertInPlace(arr, 1, Int(2), Int(3)); - assert(equal(arr, [1, 2, 3, 4, 5])); //check it works with postblit -} - -@safe unittest -{ - import std.exception; - assertCTFEable!( - { - int[] a = [1, 2]; - a.insertInPlace(2, 3); - a.insertInPlace(0, -1, 0); - return a == [-1, 0, 1, 2, 3]; - }); -} - -// https://issues.dlang.org/show_bug.cgi?id=6874 -@system unittest -{ - import core.memory; - // allocate some space - byte[] a; - a.length = 1; - - // fill it - a.length = a.capacity; - - // write beyond - byte[] b = a[$ .. $]; - b.insertInPlace(0, a); - - // make sure that reallocation has happened - assert(GC.addrOf(&b[0]) == GC.addrOf(&b[$-1])); -} - - -/++ - Returns whether the `front`s of `lhs` and `rhs` both refer to the - same place in memory, making one of the arrays a slice of the other which - starts at index `0`. - - Params: - lhs = the first array to compare - rhs = the second array to compare - Returns: - `true` if $(D lhs.ptr == rhs.ptr), `false` otherwise. - +/ -@safe -pure nothrow @nogc bool sameHead(T)(in T[] lhs, in T[] rhs) -{ - return lhs.ptr == rhs.ptr; -} - -/// -@safe pure nothrow unittest -{ - auto a = [1, 2, 3, 4, 5]; - auto b = a[0 .. 2]; - - assert(a.sameHead(b)); -} - - -/++ - Returns whether the `back`s of `lhs` and `rhs` both refer to the - same place in memory, making one of the arrays a slice of the other which - end at index `$`. - - Params: - lhs = the first array to compare - rhs = the second array to compare - Returns: - `true` if both arrays are the same length and $(D lhs.ptr == rhs.ptr), - `false` otherwise. - +/ -@trusted -pure nothrow @nogc bool sameTail(T)(in T[] lhs, in T[] rhs) -{ - return lhs.ptr + lhs.length == rhs.ptr + rhs.length; -} - -/// -@safe pure nothrow unittest -{ - auto a = [1, 2, 3, 4, 5]; - auto b = a[3..$]; - - assert(a.sameTail(b)); -} - -@safe pure nothrow unittest -{ - static foreach (T; AliasSeq!(int[], const(int)[], immutable(int)[], const int[], immutable int[])) - {{ - T a = [1, 2, 3, 4, 5]; - T b = a; - T c = a[1 .. $]; - T d = a[0 .. 1]; - T e = null; - - assert(sameHead(a, a)); - assert(sameHead(a, b)); - assert(!sameHead(a, c)); - assert(sameHead(a, d)); - assert(!sameHead(a, e)); - - assert(sameTail(a, a)); - assert(sameTail(a, b)); - assert(sameTail(a, c)); - assert(!sameTail(a, d)); - assert(!sameTail(a, e)); - - //verifies R-value compatibilty - assert(a.sameHead(a[0 .. 0])); - assert(a.sameTail(a[$ .. $])); - }} -} - -/** -Params: - s = an $(REF_ALTTEXT input range, isInputRange, std,range,primitives) - or a dynamic array - n = number of times to repeat `s` - -Returns: - An array that consists of `s` repeated `n` times. This function allocates, fills, and - returns a new array. - -See_Also: - For a lazy version, refer to $(REF repeat, std,range). - */ -ElementEncodingType!S[] replicate(S)(S s, size_t n) -if (isDynamicArray!S) -{ - alias RetType = ElementEncodingType!S[]; - - // Optimization for return join(std.range.repeat(s, n)); - if (n == 0) - return RetType.init; - if (n == 1) - return cast(RetType) s; - auto r = new Unqual!(typeof(s[0]))[n * s.length]; - if (s.length == 1) - r[] = s[0]; - else - { - immutable len = s.length, nlen = n * len; - for (size_t i = 0; i < nlen; i += len) - { - r[i .. i + len] = s[]; - } - } - return r; -} - -/// ditto -ElementType!S[] replicate(S)(S s, size_t n) -if (isInputRange!S && !isDynamicArray!S) -{ - import std.range : repeat; - return join(std.range.repeat(s, n)); -} - - -/// -@safe unittest -{ - auto a = "abc"; - auto s = replicate(a, 3); - - assert(s == "abcabcabc"); - - auto b = [1, 2, 3]; - auto c = replicate(b, 3); - - assert(c == [1, 2, 3, 1, 2, 3, 1, 2, 3]); - - auto d = replicate(b, 0); - - assert(d == []); -} - -@safe unittest -{ - import std.conv : to; - - static foreach (S; AliasSeq!(string, wstring, dstring, char[], wchar[], dchar[])) - {{ - immutable S t = "abc"; - - assert(replicate(to!S("1234"), 0) is null); - assert(replicate(to!S("1234"), 0) is null); - assert(replicate(to!S("1234"), 1) == "1234"); - assert(replicate(to!S("1234"), 2) == "12341234"); - assert(replicate(to!S("1"), 4) == "1111"); - assert(replicate(t, 3) == "abcabcabc"); - assert(replicate(cast(S) null, 4) is null); - }} -} - -/++ -Eagerly splits `range` into an array, using `sep` as the delimiter. - -When no delimiter is provided, strings are split into an array of words, -using whitespace as delimiter. -Runs of whitespace are merged together (no empty words are produced). - -The `range` must be a $(REF_ALTTEXT forward range, isForwardRange, std,range,primitives). -The separator can be a value of the same type as the elements in `range` -or it can be another forward `range`. - -Params: - s = the string to split by word if no separator is given - range = the range to split - sep = a value of the same type as the elements of `range` or another - isTerminator = a predicate that splits the range when it returns `true`. - -Returns: - An array containing the divided parts of `range` (or the words of `s`). - -See_Also: -$(REF splitter, std,algorithm,iteration) for a lazy version without allocating memory. - -$(REF splitter, std,regex) for a version that splits using a regular -expression defined separator. -+/ -S[] split(S)(S s) @safe pure -if (isSomeString!S) -{ - size_t istart; - bool inword = false; - auto result = appender!(S[]); - - foreach (i, dchar c ; s) - { - import std.uni : isWhite; - if (isWhite(c)) - { - if (inword) - { - put(result, s[istart .. i]); - inword = false; - } - } - else - { - if (!inword) - { - istart = i; - inword = true; - } - } - } - if (inword) - put(result, s[istart .. $]); - return result.data; -} - -/// -@safe unittest -{ - import std.uni : isWhite; - assert("Learning,D,is,fun".split(",") == ["Learning", "D", "is", "fun"]); - assert("Learning D is fun".split!isWhite == ["Learning", "D", "is", "fun"]); - assert("Learning D is fun".split(" D ") == ["Learning", "is fun"]); -} - -/// -@safe unittest -{ - string str = "Hello World!"; - assert(str.split == ["Hello", "World!"]); - - string str2 = "Hello\t\tWorld\t!"; - assert(str2.split == ["Hello", "World", "!"]); -} - -@safe unittest -{ - import std.conv : to; - import std.format : format; - import std.typecons; - - static auto makeEntry(S)(string l, string[] r) - {return tuple(l.to!S(), r.to!(S[])());} - - static foreach (S; AliasSeq!(string, wstring, dstring,)) - {{ - auto entries = - [ - makeEntry!S("", []), - makeEntry!S(" ", []), - makeEntry!S("hello", ["hello"]), - makeEntry!S(" hello ", ["hello"]), - makeEntry!S(" h e l l o ", ["h", "e", "l", "l", "o"]), - makeEntry!S("peter\t\npaul\rjerry", ["peter", "paul", "jerry"]), - makeEntry!S(" \t\npeter paul\tjerry \n", ["peter", "paul", "jerry"]), - makeEntry!S("\u2000日\u202F本\u205F語\u3000", ["日", "本", "語"]), - makeEntry!S("  哈・郎博尔德}    ___一个", ["哈・郎博尔德}", "___一个"]) - ]; - foreach (entry; entries) - assert(entry[0].split() == entry[1], format("got: %s, expected: %s.", entry[0].split(), entry[1])); - }} - - //Just to test that an immutable is split-able - immutable string s = " \t\npeter paul\tjerry \n"; - assert(split(s) == ["peter", "paul", "jerry"]); -} - -@safe unittest //purity, ctfe ... -{ - import std.exception; - void dg() @safe pure { - assert(split("hello world"c) == ["hello"c, "world"c]); - assert(split("hello world"w) == ["hello"w, "world"w]); - assert(split("hello world"d) == ["hello"d, "world"d]); - } - dg(); - assertCTFEable!dg; -} - -/// -@safe unittest -{ - assert(split("hello world") == ["hello","world"]); - assert(split("192.168.0.1", ".") == ["192", "168", "0", "1"]); - - auto a = split([1, 2, 3, 4, 5, 1, 2, 3, 4, 5], [2, 3]); - assert(a == [[1], [4, 5, 1], [4, 5]]); -} - -///ditto -auto split(Range, Separator)(Range range, Separator sep) -if (isForwardRange!Range && ( - is(typeof(ElementType!Range.init == Separator.init)) || - is(typeof(ElementType!Range.init == ElementType!Separator.init)) && isForwardRange!Separator - )) -{ - import std.algorithm.iteration : splitter; - return range.splitter(sep).array; -} -///ditto -auto split(alias isTerminator, Range)(Range range) -if (isForwardRange!Range && is(typeof(unaryFun!isTerminator(range.front)))) -{ - import std.algorithm.iteration : splitter; - return range.splitter!isTerminator.array; -} - -@safe unittest -{ - import std.algorithm.comparison : cmp; - import std.conv; - - static foreach (S; AliasSeq!(string, wstring, dstring, - immutable(string), immutable(wstring), immutable(dstring), - char[], wchar[], dchar[], - const(char)[], const(wchar)[], const(dchar)[], - const(char[]), immutable(char[]))) - {{ - S s = to!S(",peter,paul,jerry,"); - - auto words = split(s, ","); - assert(words.length == 5, text(words.length)); - assert(cmp(words[0], "") == 0); - assert(cmp(words[1], "peter") == 0); - assert(cmp(words[2], "paul") == 0); - assert(cmp(words[3], "jerry") == 0); - assert(cmp(words[4], "") == 0); - - auto s1 = s[0 .. s.length - 1]; // lop off trailing ',' - words = split(s1, ","); - assert(words.length == 4); - assert(cmp(words[3], "jerry") == 0); - - auto s2 = s1[1 .. s1.length]; // lop off leading ',' - words = split(s2, ","); - assert(words.length == 3); - assert(cmp(words[0], "peter") == 0); - - auto s3 = to!S(",,peter,,paul,,jerry,,"); - - words = split(s3, ",,"); - assert(words.length == 5); - assert(cmp(words[0], "") == 0); - assert(cmp(words[1], "peter") == 0); - assert(cmp(words[2], "paul") == 0); - assert(cmp(words[3], "jerry") == 0); - assert(cmp(words[4], "") == 0); - - auto s4 = s3[0 .. s3.length - 2]; // lop off trailing ',,' - words = split(s4, ",,"); - assert(words.length == 4); - assert(cmp(words[3], "jerry") == 0); - - auto s5 = s4[2 .. s4.length]; // lop off leading ',,' - words = split(s5, ",,"); - assert(words.length == 3); - assert(cmp(words[0], "peter") == 0); - }} -} - -/+ - Conservative heuristic to determine if a range can be iterated cheaply. - Used by `join` in decision to do an extra iteration of the range to - compute the resultant length. If iteration is not cheap then precomputing - length could be more expensive than using `Appender`. - - For now, we only assume arrays are cheap to iterate. - +/ -private enum bool hasCheapIteration(R) = isArray!R; - -/++ - Eagerly concatenates all of the ranges in `ror` together (with the GC) - into one array using `sep` as the separator if present. - - Params: - ror = An $(REF_ALTTEXT input range, isInputRange, std,range,primitives) - of input ranges - sep = An input range, or a single element, to join the ranges on - - Returns: - An array of elements - - See_Also: - For a lazy version, see $(REF joiner, std,algorithm,iteration) - +/ -ElementEncodingType!(ElementType!RoR)[] join(RoR, R)(RoR ror, R sep) -if (isInputRange!RoR && - isInputRange!(Unqual!(ElementType!RoR)) && - isInputRange!R && - (is(immutable ElementType!(ElementType!RoR) == immutable ElementType!R) || - (isSomeChar!(ElementType!(ElementType!RoR)) && isSomeChar!(ElementType!R)) - )) -{ - alias RetType = typeof(return); - alias RetTypeElement = Unqual!(ElementEncodingType!RetType); - alias RoRElem = ElementType!RoR; - - if (ror.empty) - return RetType.init; - - // Constraint only requires input range for sep. - // This converts sep to an array (forward range) if it isn't one, - // and makes sure it has the same string encoding for string types. - static if (isSomeString!RetType && - !is(immutable ElementEncodingType!RetType == immutable ElementEncodingType!R)) - { - import std.conv : to; - auto sepArr = to!RetType(sep); - } - else static if (!isArray!R) - auto sepArr = array(sep); - else - alias sepArr = sep; - - static if (hasCheapIteration!RoR && (hasLength!RoRElem || isNarrowString!RoRElem)) - { - import core.internal.lifetime : emplaceRef; - size_t length; // length of result array - size_t rorLength; // length of range ror - foreach (r; ror.save) - { - length += r.length; - ++rorLength; - } - if (!rorLength) - return null; - length += (rorLength - 1) * sepArr.length; - - auto result = (() @trusted => uninitializedArray!(RetTypeElement[])(length))(); - size_t len; - foreach (e; ror.front) - emplaceRef(result[len++], e); - ror.popFront(); - foreach (r; ror) - { - foreach (e; sepArr) - emplaceRef(result[len++], e); - foreach (e; r) - emplaceRef(result[len++], e); - } - assert(len == result.length); - return (() @trusted => cast(RetType) result)(); - } - else - { - auto result = appender!RetType(); - put(result, ror.front); - ror.popFront(); - for (; !ror.empty; ror.popFront()) - { - put(result, sepArr); - put(result, ror.front); - } - return result.data; - } -} - -// https://issues.dlang.org/show_bug.cgi?id=14230 -@safe unittest -{ - string[] ary = ["","aa","bb","cc"]; // leaded by _empty_ element - assert(ary.join(" @") == " @aa @bb @cc"); // OK in 2.067b1 and olders -} - -// https://issues.dlang.org/show_bug.cgi?id=21337 -@system unittest -{ - import std.algorithm.iteration : map; - - static class Once - { - bool empty; - - void popFront() - { - empty = true; - } - - int front() - { - return 0; - } - } - - assert([1, 2].map!"[a]".join(new Once) == [1, 0, 2]); -} - -/// Ditto -ElementEncodingType!(ElementType!RoR)[] join(RoR, E)(RoR ror, scope E sep) -if (isInputRange!RoR && - isInputRange!(Unqual!(ElementType!RoR)) && - ((is(E : ElementType!(ElementType!RoR))) || - (!autodecodeStrings && isSomeChar!(ElementType!(ElementType!RoR)) && - isSomeChar!E))) -{ - alias RetType = typeof(return); - alias RetTypeElement = Unqual!(ElementEncodingType!RetType); - alias RoRElem = ElementType!RoR; - - if (ror.empty) - return RetType.init; - - static if (hasCheapIteration!RoR && (hasLength!RoRElem || isNarrowString!RoRElem)) - { - static if (isSomeChar!E && isSomeChar!RetTypeElement && E.sizeof > RetTypeElement.sizeof) - { - import std.utf : encode; - RetTypeElement[4 / RetTypeElement.sizeof] encodeSpace; - immutable size_t sepArrLength = encode(encodeSpace, sep); - return join(ror, encodeSpace[0 .. sepArrLength]); - } - else - { - import core.internal.lifetime : emplaceRef; - import std.format : format; - size_t length; - size_t rorLength; - foreach (r; ror.save) - { - length += r.length; - ++rorLength; - } - if (!rorLength) - return null; - length += rorLength - 1; - auto result = uninitializedArray!(RetTypeElement[])(length); - - - size_t len; - foreach (e; ror.front) - emplaceRef(result[len++], e); - ror.popFront(); - foreach (r; ror) - { - emplaceRef(result[len++], sep); - foreach (e; r) - emplaceRef(result[len++], e); - } - assert(len == result.length, format! - "len %s must equal result.lenght %s"(len, result.length)); - return (() @trusted => cast(RetType) result)(); - } - } - else - { - auto result = appender!RetType(); - put(result, ror.front); - ror.popFront(); - for (; !ror.empty; ror.popFront()) - { - put(result, sep); - put(result, ror.front); - } - return result.data; - } -} - -// https://issues.dlang.org/show_bug.cgi?id=14230 -@safe unittest -{ - string[] ary = ["","aa","bb","cc"]; - assert(ary.join('@') == "@aa@bb@cc"); -} - -/// Ditto -ElementEncodingType!(ElementType!RoR)[] join(RoR)(RoR ror) -if (isInputRange!RoR && - isInputRange!(Unqual!(ElementType!RoR))) -{ - alias RetType = typeof(return); - alias ConstRetTypeElement = ElementEncodingType!RetType; - static if (isAssignable!(Unqual!ConstRetTypeElement, ConstRetTypeElement)) - { - alias RetTypeElement = Unqual!ConstRetTypeElement; - } - else - { - alias RetTypeElement = ConstRetTypeElement; - } - alias RoRElem = ElementType!RoR; - - if (ror.empty) - return RetType.init; - - static if (hasCheapIteration!RoR && (hasLength!RoRElem || isNarrowString!RoRElem)) - { - import core.internal.lifetime : emplaceRef; - size_t length; - foreach (r; ror.save) - length += r.length; - - auto result = (() @trusted => uninitializedArray!(RetTypeElement[])(length))(); - size_t len; - foreach (r; ror) - foreach (e; r) - emplaceRef!RetTypeElement(result[len++], e); - assert(len == result.length, - "emplaced an unexpected number of elements"); - return (() @trusted => cast(RetType) result)(); - } - else - { - auto result = appender!RetType(); - for (; !ror.empty; ror.popFront()) - put(result, ror.front); - return result.data; - } -} - -/// -@safe pure nothrow unittest -{ - assert(join(["hello", "silly", "world"], " ") == "hello silly world"); - assert(join(["hello", "silly", "world"]) == "hellosillyworld"); - - assert(join([[1, 2, 3], [4, 5]], [72, 73]) == [1, 2, 3, 72, 73, 4, 5]); - assert(join([[1, 2, 3], [4, 5]]) == [1, 2, 3, 4, 5]); - - const string[] arr = ["apple", "banana"]; - assert(arr.join(",") == "apple,banana"); - assert(arr.join() == "applebanana"); -} - -@safe pure unittest -{ - import std.conv : to; - import std.range.primitives : autodecodeStrings; - - static foreach (T; AliasSeq!(string,wstring,dstring)) - {{ - auto arr2 = "Здравствуй Мир Unicode".to!(T); - auto arr = ["Здравствуй", "Мир", "Unicode"].to!(T[]); - assert(join(arr) == "ЗдравствуйМирUnicode"); - static foreach (S; AliasSeq!(char,wchar,dchar)) - {{ - auto jarr = arr.join(to!S(' ')); - static assert(is(typeof(jarr) == T)); - assert(jarr == arr2); - }} - static foreach (S; AliasSeq!(string,wstring,dstring)) - {{ - auto jarr = arr.join(to!S(" ")); - static assert(is(typeof(jarr) == T)); - assert(jarr == arr2); - }} - }} - - static foreach (T; AliasSeq!(string,wstring,dstring)) - {{ - auto arr2 = "Здравствуй\u047CМир\u047CUnicode".to!(T); - auto arr = ["Здравствуй", "Мир", "Unicode"].to!(T[]); - static foreach (S; AliasSeq!(wchar,dchar)) - {{ - auto jarr = arr.join(to!S('\u047C')); - static assert(is(typeof(jarr) == T)); - assert(jarr == arr2); - }} - }} - - const string[] arr = ["apple", "banana"]; - assert(arr.join(',') == "apple,banana"); -} - -@safe unittest -{ - class A { } - - const A[][] array; - auto result = array.join; // can't remove constness, so don't try - - static assert(is(typeof(result) == const(A)[])); -} - -@safe unittest -{ - import std.algorithm; - import std.conv : to; - import std.range; - - static foreach (R; AliasSeq!(string, wstring, dstring)) - {{ - R word1 = "日本語"; - R word2 = "paul"; - R word3 = "jerry"; - R[] words = [word1, word2, word3]; - - auto filteredWord1 = filter!"true"(word1); - auto filteredLenWord1 = takeExactly(filteredWord1, word1.walkLength()); - auto filteredWord2 = filter!"true"(word2); - auto filteredLenWord2 = takeExactly(filteredWord2, word2.walkLength()); - auto filteredWord3 = filter!"true"(word3); - auto filteredLenWord3 = takeExactly(filteredWord3, word3.walkLength()); - auto filteredWordsArr = [filteredWord1, filteredWord2, filteredWord3]; - auto filteredLenWordsArr = [filteredLenWord1, filteredLenWord2, filteredLenWord3]; - auto filteredWords = filter!"true"(filteredWordsArr); - - static foreach (S; AliasSeq!(string, wstring, dstring)) - {{ - assert(join(filteredWords, to!S(", ")) == "日本語, paul, jerry"); - assert(join(filteredWords, to!(ElementType!S)(',')) == "日本語,paul,jerry"); - assert(join(filteredWordsArr, to!(ElementType!(S))(',')) == "日本語,paul,jerry"); - assert(join(filteredWordsArr, to!S(", ")) == "日本語, paul, jerry"); - assert(join(filteredWordsArr, to!(ElementType!(S))(',')) == "日本語,paul,jerry"); - assert(join(filteredLenWordsArr, to!S(", ")) == "日本語, paul, jerry"); - assert(join(filter!"true"(words), to!S(", ")) == "日本語, paul, jerry"); - assert(join(words, to!S(", ")) == "日本語, paul, jerry"); - - assert(join(filteredWords, to!S("")) == "日本語pauljerry"); - assert(join(filteredWordsArr, to!S("")) == "日本語pauljerry"); - assert(join(filteredLenWordsArr, to!S("")) == "日本語pauljerry"); - assert(join(filter!"true"(words), to!S("")) == "日本語pauljerry"); - assert(join(words, to!S("")) == "日本語pauljerry"); - - assert(join(filter!"true"([word1]), to!S(", ")) == "日本語"); - assert(join([filteredWord1], to!S(", ")) == "日本語"); - assert(join([filteredLenWord1], to!S(", ")) == "日本語"); - assert(join(filter!"true"([filteredWord1]), to!S(", ")) == "日本語"); - assert(join([word1], to!S(", ")) == "日本語"); - - assert(join(filteredWords, to!S(word1)) == "日本語日本語paul日本語jerry"); - assert(join(filteredWordsArr, to!S(word1)) == "日本語日本語paul日本語jerry"); - assert(join(filteredLenWordsArr, to!S(word1)) == "日本語日本語paul日本語jerry"); - assert(join(filter!"true"(words), to!S(word1)) == "日本語日本語paul日本語jerry"); - assert(join(words, to!S(word1)) == "日本語日本語paul日本語jerry"); - - auto filterComma = filter!"true"(to!S(", ")); - assert(join(filteredWords, filterComma) == "日本語, paul, jerry"); - assert(join(filteredWordsArr, filterComma) == "日本語, paul, jerry"); - assert(join(filteredLenWordsArr, filterComma) == "日本語, paul, jerry"); - assert(join(filter!"true"(words), filterComma) == "日本語, paul, jerry"); - assert(join(words, filterComma) == "日本語, paul, jerry"); - }} - - assert(join(filteredWords) == "日本語pauljerry"); - assert(join(filteredWordsArr) == "日本語pauljerry"); - assert(join(filteredLenWordsArr) == "日本語pauljerry"); - assert(join(filter!"true"(words)) == "日本語pauljerry"); - assert(join(words) == "日本語pauljerry"); - - assert(join(filteredWords, filter!"true"(", ")) == "日本語, paul, jerry"); - assert(join(filteredWordsArr, filter!"true"(", ")) == "日本語, paul, jerry"); - assert(join(filteredLenWordsArr, filter!"true"(", ")) == "日本語, paul, jerry"); - assert(join(filter!"true"(words), filter!"true"(", ")) == "日本語, paul, jerry"); - assert(join(words, filter!"true"(", ")) == "日本語, paul, jerry"); - - assert(join(filter!"true"(cast(typeof(filteredWordsArr))[]), ", ").empty); - assert(join(cast(typeof(filteredWordsArr))[], ", ").empty); - assert(join(cast(typeof(filteredLenWordsArr))[], ", ").empty); - assert(join(filter!"true"(cast(R[])[]), ", ").empty); - assert(join(cast(R[])[], ", ").empty); - - assert(join(filter!"true"(cast(typeof(filteredWordsArr))[])).empty); - assert(join(cast(typeof(filteredWordsArr))[]).empty); - assert(join(cast(typeof(filteredLenWordsArr))[]).empty); - - assert(join(filter!"true"(cast(R[])[])).empty); - assert(join(cast(R[])[]).empty); - }} - - assert(join([[1, 2], [41, 42]], [5, 6]) == [1, 2, 5, 6, 41, 42]); - assert(join([[1, 2], [41, 42]], cast(int[])[]) == [1, 2, 41, 42]); - assert(join([[1, 2]], [5, 6]) == [1, 2]); - assert(join(cast(int[][])[], [5, 6]).empty); - - assert(join([[1, 2], [41, 42]]) == [1, 2, 41, 42]); - assert(join(cast(int[][])[]).empty); - - alias f = filter!"true"; - assert(join([[1, 2], [41, 42]], [5, 6]) == [1, 2, 5, 6, 41, 42]); - assert(join(f([[1, 2], [41, 42]]), [5, 6]) == [1, 2, 5, 6, 41, 42]); - assert(join([f([1, 2]), f([41, 42])], [5, 6]) == [1, 2, 5, 6, 41, 42]); - assert(join(f([f([1, 2]), f([41, 42])]), [5, 6]) == [1, 2, 5, 6, 41, 42]); - assert(join([[1, 2], [41, 42]], f([5, 6])) == [1, 2, 5, 6, 41, 42]); - assert(join(f([[1, 2], [41, 42]]), f([5, 6])) == [1, 2, 5, 6, 41, 42]); - assert(join([f([1, 2]), f([41, 42])], f([5, 6])) == [1, 2, 5, 6, 41, 42]); - assert(join(f([f([1, 2]), f([41, 42])]), f([5, 6])) == [1, 2, 5, 6, 41, 42]); -} - -// https://issues.dlang.org/show_bug.cgi?id=10683 -@safe unittest -{ - import std.range : join; - import std.typecons : tuple; - assert([[tuple(1)]].join == [tuple(1)]); - assert([[tuple("x")]].join == [tuple("x")]); -} - -// https://issues.dlang.org/show_bug.cgi?id=13877 -@safe unittest -{ - // Test that the range is iterated only once. - import std.algorithm.iteration : map; - int c = 0; - auto j1 = [1, 2, 3].map!(_ => [c++]).join; - assert(c == 3); - assert(j1 == [0, 1, 2]); - - c = 0; - auto j2 = [1, 2, 3].map!(_ => [c++]).join(9); - assert(c == 3); - assert(j2 == [0, 9, 1, 9, 2]); - - c = 0; - auto j3 = [1, 2, 3].map!(_ => [c++]).join([9]); - assert(c == 3); - assert(j3 == [0, 9, 1, 9, 2]); -} - - -/++ - Replace occurrences of `from` with `to` in `subject` in a new array. - - Params: - subject = the array to scan - from = the item to replace - to = the item to replace all instances of `from` with - - Returns: - A new array without changing the contents of `subject`, or the original - array if no match is found. - - See_Also: - $(REF substitute, std,algorithm,iteration) for a lazy replace. - +/ -E[] replace(E, R1, R2)(E[] subject, R1 from, R2 to) -if ((isForwardRange!R1 && isForwardRange!R2 && (hasLength!R2 || isSomeString!R2)) || - is(Unqual!E : Unqual!R1)) -{ - size_t changed = 0; - return replace(subject, from, to, changed); -} - -/// -@safe unittest -{ - assert("Hello Wörld".replace("o Wö", "o Wo") == "Hello World"); - assert("Hello Wörld".replace("l", "h") == "Hehho Wörhd"); -} - -@safe unittest -{ - assert([1, 2, 3, 4, 2].replace([2], [5]) == [1, 5, 3, 4, 5]); - assert([3, 3, 3].replace([3], [0]) == [0, 0, 0]); - assert([3, 3, 4, 3].replace([3, 3], [1, 1, 1]) == [1, 1, 1, 4, 3]); -} - -// https://issues.dlang.org/show_bug.cgi?id=18215 -@safe unittest -{ - auto arr = ["aaa.dd", "b"]; - arr = arr.replace("aaa.dd", "."); - assert(arr == [".", "b"]); - - arr = ["_", "_", "aaa.dd", "b", "c", "aaa.dd", "e"]; - arr = arr.replace("aaa.dd", "."); - assert(arr == ["_", "_", ".", "b", "c", ".", "e"]); -} - -// https://issues.dlang.org/show_bug.cgi?id=18215 -@safe unittest -{ - assert([[0], [1, 2], [0], [3]].replace([0], [4]) == [[4], [1, 2], [4], [3]]); - assert([[0], [1, 2], [0], [3], [1, 2]] - .replace([1, 2], [0]) == [[0], [0], [0], [3], [0]]); - assert([[0], [1, 2], [0], [3], [1, 2], [0], [1, 2]] - .replace([[0], [1, 2]], [[4]]) == [[4], [0], [3], [1, 2], [4]]); -} - -// https://issues.dlang.org/show_bug.cgi?id=10930 -@safe unittest -{ - assert([0, 1, 2].replace(1, 4) == [0, 4, 2]); - assert("äbö".replace('ä', 'a') == "abö"); -} - -// empty array -@safe unittest -{ - int[] arr; - assert(replace(arr, 1, 2) == []); -} - -/++ - Replace occurrences of `from` with `to` in `subject` in a new array. - `changed` counts how many replacements took place. - - Params: - subject = the array to scan - from = the item to replace - to = the item to replace all instances of `from` with - changed = the number of replacements - - Returns: - A new array without changing the contents of `subject`, or the original - array if no match is found. - +/ -E[] replace(E, R1, R2)(E[] subject, R1 from, R2 to, ref size_t changed) -if ((isForwardRange!R1 && isForwardRange!R2 && (hasLength!R2 || isSomeString!R2)) || - is(Unqual!E : Unqual!R1)) -{ - import std.algorithm.searching : find; - import std.range : dropOne; - - static if (isInputRange!R1) - { - if (from.empty) return subject; - alias rSave = a => a.save; - } - else - { - alias rSave = a => a; - } - - auto balance = find(subject, rSave(from)); - if (balance.empty) - return subject; - - auto app = appender!(E[])(); - app.put(subject[0 .. subject.length - balance.length]); - app.put(rSave(to)); - ++changed; - // replacing an element in an array is different to a range replacement - static if (is(Unqual!E : Unqual!R1)) - replaceInto(app, balance.dropOne, from, to, changed); - else - replaceInto(app, balance[from.length .. $], from, to, changed); - - return app.data; -} - -/// -@safe unittest -{ - size_t changed = 0; - assert("Hello Wörld".replace("o Wö", "o Wo", changed) == "Hello World"); - assert(changed == 1); - - changed = 0; - assert("Hello Wörld".replace("l", "h", changed) == "Hehho Wörhd"); - import std.stdio : writeln; - writeln(changed); - assert(changed == 3); -} - -/++ - Replace occurrences of `from` with `to` in `subject` and output the result into - `sink`. - - Params: - sink = an $(REF_ALTTEXT output range, isOutputRange, std,range,primitives) - subject = the array to scan - from = the item to replace - to = the item to replace all instances of `from` with - - See_Also: - $(REF substitute, std,algorithm,iteration) for a lazy replace. - +/ -void replaceInto(E, Sink, R1, R2)(Sink sink, E[] subject, R1 from, R2 to) -if (isOutputRange!(Sink, E) && - ((isForwardRange!R1 && isForwardRange!R2 && (hasLength!R2 || isSomeString!R2)) || - is(Unqual!E : Unqual!R1))) -{ - size_t changed = 0; - replaceInto(sink, subject, from, to, changed); -} - -/// -@safe unittest -{ - auto arr = [1, 2, 3, 4, 5]; - auto from = [2, 3]; - auto to = [4, 6]; - auto sink = appender!(int[])(); - - replaceInto(sink, arr, from, to); - - assert(sink.data == [1, 4, 6, 4, 5]); -} - -// empty array -@safe unittest -{ - auto sink = appender!(int[])(); - int[] arr; - replaceInto(sink, arr, 1, 2); - assert(sink.data == []); -} - -@safe unittest -{ - import std.algorithm.comparison : cmp; - import std.conv : to; - - static foreach (S; AliasSeq!(string, wstring, dstring, char[], wchar[], dchar[])) - { - static foreach (T; AliasSeq!(string, wstring, dstring, char[], wchar[], dchar[])) - {{ - auto s = to!S("This is a foo foo list"); - auto from = to!T("foo"); - auto into = to!S("silly"); - S r; - int i; - - r = replace(s, from, into); - i = cmp(r, "This is a silly silly list"); - assert(i == 0); - - r = replace(s, to!S(""), into); - i = cmp(r, "This is a foo foo list"); - assert(i == 0); - - assert(replace(r, to!S("won't find this"), to!S("whatever")) is r); - }} - } - - immutable s = "This is a foo foo list"; - assert(replace(s, "foo", "silly") == "This is a silly silly list"); -} - -@safe unittest -{ - import std.algorithm.searching : skipOver; - import std.conv : to; - - struct CheckOutput(C) - { - C[] desired; - this(C[] arr){ desired = arr; } - void put(C[] part){ assert(skipOver(desired, part)); } - } - static foreach (S; AliasSeq!(string, wstring, dstring, char[], wchar[], dchar[])) - {{ - alias Char = ElementEncodingType!S; - S s = to!S("yet another dummy text, yet another ..."); - S from = to!S("yet another"); - S into = to!S("some"); - replaceInto(CheckOutput!(Char)(to!S("some dummy text, some ...")) - , s, from, into); - }} -} - -// https://issues.dlang.org/show_bug.cgi?id=10930 -@safe unittest -{ - auto sink = appender!(int[])(); - replaceInto(sink, [0, 1, 2], 1, 5); - assert(sink.data == [0, 5, 2]); - - auto sink2 = appender!(dchar[])(); - replaceInto(sink2, "äbö", 'ä', 'a'); - assert(sink2.data == "abö"); -} - -/++ - Replace occurrences of `from` with `to` in `subject` and output the result into - `sink`. `changed` counts how many replacements took place. - - Params: - sink = an $(REF_ALTTEXT output range, isOutputRange, std,range,primitives) - subject = the array to scan - from = the item to replace - to = the item to replace all instances of `from` with - changed = the number of replacements - +/ -void replaceInto(E, Sink, R1, R2)(Sink sink, E[] subject, R1 from, R2 to, ref size_t changed) -if (isOutputRange!(Sink, E) && - ((isForwardRange!R1 && isForwardRange!R2 && (hasLength!R2 || isSomeString!R2)) || - is(Unqual!E : Unqual!R1))) -{ - import std.algorithm.searching : find; - import std.range : dropOne; - - static if (isInputRange!R1) - { - if (from.empty) - { - sink.put(subject); - return; - } - alias rSave = a => a.save; - } - else - { - alias rSave = a => a; - } - for (;;) - { - auto balance = find(subject, rSave(from)); - if (balance.empty) - { - sink.put(subject); - break; - } - sink.put(subject[0 .. subject.length - balance.length]); - sink.put(rSave(to)); - ++changed; - // replacing an element in an array is different to a range replacement - static if (is(Unqual!E : Unqual!R1)) - subject = balance.dropOne; - else - subject = balance[from.length .. $]; - } -} - -/// -@safe unittest -{ - auto arr = [1, 2, 3, 4, 5]; - auto from = [2, 3]; - auto to = [4, 6]; - auto sink = appender!(int[])(); - - size_t changed = 0; - replaceInto(sink, arr, from, to, changed); - - assert(sink.data == [1, 4, 6, 4, 5]); - assert(changed == 1); -} - -/++ - Replaces elements from `array` with indices ranging from `from` - (inclusive) to `to` (exclusive) with the range `stuff`. - - Params: - subject = the array to scan - from = the starting index - to = the ending index - stuff = the items to replace in-between `from` and `to` - - Returns: - A new array without changing the contents of `subject`. - - See_Also: - $(REF substitute, std,algorithm,iteration) for a lazy replace. - +/ -T[] replace(T, Range)(T[] subject, size_t from, size_t to, Range stuff) -if (isInputRange!Range && - (is(ElementType!Range : T) || - isSomeString!(T[]) && is(ElementType!Range : dchar))) -{ - static if (hasLength!Range && is(ElementEncodingType!Range : T)) - { - import std.algorithm.mutation : copy; - assert(from <= to, "from must be before or equal to to"); - immutable sliceLen = to - from; - auto retval = new Unqual!(T)[](subject.length - sliceLen + stuff.length); - retval[0 .. from] = subject[0 .. from]; - - if (!stuff.empty) - copy(stuff, retval[from .. from + stuff.length]); - - retval[from + stuff.length .. $] = subject[to .. $]; - static if (is(T == const) || is(T == immutable)) - { - return () @trusted { return cast(T[]) retval; } (); - } - else - { - return cast(T[]) retval; - } - } - else - { - auto app = appender!(T[])(); - app.put(subject[0 .. from]); - app.put(stuff); - app.put(subject[to .. $]); - return app.data; - } -} - -/// -@safe unittest -{ - auto a = [ 1, 2, 3, 4 ]; - auto b = a.replace(1, 3, [ 9, 9, 9 ]); - assert(a == [ 1, 2, 3, 4 ]); - assert(b == [ 1, 9, 9, 9, 4 ]); -} - -@system unittest -{ - import core.exception; - import std.algorithm.iteration : filter; - import std.conv : to; - import std.exception; - - - auto a = [ 1, 2, 3, 4 ]; - assert(replace(a, 0, 0, [5, 6, 7]) == [5, 6, 7, 1, 2, 3, 4]); - assert(replace(a, 0, 2, cast(int[])[]) == [3, 4]); - assert(replace(a, 0, 4, [5, 6, 7]) == [5, 6, 7]); - assert(replace(a, 0, 2, [5, 6, 7]) == [5, 6, 7, 3, 4]); - assert(replace(a, 2, 4, [5, 6, 7]) == [1, 2, 5, 6, 7]); - - assert(replace(a, 0, 0, filter!"true"([5, 6, 7])) == [5, 6, 7, 1, 2, 3, 4]); - assert(replace(a, 0, 2, filter!"true"(cast(int[])[])) == [3, 4]); - assert(replace(a, 0, 4, filter!"true"([5, 6, 7])) == [5, 6, 7]); - assert(replace(a, 0, 2, filter!"true"([5, 6, 7])) == [5, 6, 7, 3, 4]); - assert(replace(a, 2, 4, filter!"true"([5, 6, 7])) == [1, 2, 5, 6, 7]); - assert(a == [ 1, 2, 3, 4 ]); - - void testStr(T, U)(string file = __FILE__, size_t line = __LINE__) - { - - auto l = to!T("hello"); - auto r = to!U(" world"); - - enforce(replace(l, 0, 0, r) == " worldhello", - new AssertError("testStr failure 1", file, line)); - enforce(replace(l, 0, 3, r) == " worldlo", - new AssertError("testStr failure 2", file, line)); - enforce(replace(l, 3, l.length, r) == "hel world", - new AssertError("testStr failure 3", file, line)); - enforce(replace(l, 0, l.length, r) == " world", - new AssertError("testStr failure 4", file, line)); - enforce(replace(l, l.length, l.length, r) == "hello world", - new AssertError("testStr failure 5", file, line)); - } - - testStr!(string, string)(); - testStr!(string, wstring)(); - testStr!(string, dstring)(); - testStr!(wstring, string)(); - testStr!(wstring, wstring)(); - testStr!(wstring, dstring)(); - testStr!(dstring, string)(); - testStr!(dstring, wstring)(); - testStr!(dstring, dstring)(); - - enum s = "0123456789"; - enum w = "⁰¹²³⁴⁵⁶⁷⁸⁹"w; - enum d = "⁰¹²³⁴⁵⁶⁷⁸⁹"d; - - assert(replace(s, 0, 0, "***") == "***0123456789"); - assert(replace(s, 10, 10, "***") == "0123456789***"); - assert(replace(s, 3, 8, "1012") == "012101289"); - assert(replace(s, 0, 5, "43210") == "4321056789"); - assert(replace(s, 5, 10, "43210") == "0123443210"); - - assert(replace(w, 0, 0, "***"w) == "***⁰¹²³⁴⁵⁶⁷⁸⁹"w); - assert(replace(w, 10, 10, "***"w) == "⁰¹²³⁴⁵⁶⁷⁸⁹***"w); - assert(replace(w, 3, 8, "¹⁰¹²"w) == "⁰¹²¹⁰¹²⁸⁹"w); - assert(replace(w, 0, 5, "⁴³²¹⁰"w) == "⁴³²¹⁰⁵⁶⁷⁸⁹"w); - assert(replace(w, 5, 10, "⁴³²¹⁰"w) == "⁰¹²³⁴⁴³²¹⁰"w); - - assert(replace(d, 0, 0, "***"d) == "***⁰¹²³⁴⁵⁶⁷⁸⁹"d); - assert(replace(d, 10, 10, "***"d) == "⁰¹²³⁴⁵⁶⁷⁸⁹***"d); - assert(replace(d, 3, 8, "¹⁰¹²"d) == "⁰¹²¹⁰¹²⁸⁹"d); - assert(replace(d, 0, 5, "⁴³²¹⁰"d) == "⁴³²¹⁰⁵⁶⁷⁸⁹"d); - assert(replace(d, 5, 10, "⁴³²¹⁰"d) == "⁰¹²³⁴⁴³²¹⁰"d); -} - -// https://issues.dlang.org/show_bug.cgi?id=18166 -@safe pure unittest -{ - auto str = replace("aaaaa"d, 1, 4, "***"d); - assert(str == "a***a"); -} - -/++ - Replaces elements from `array` with indices ranging from `from` - (inclusive) to `to` (exclusive) with the range `stuff`. Expands or - shrinks the array as needed. - - Params: - array = the array to scan - from = the starting index - to = the ending index - stuff = the items to replace in-between `from` and `to` - +/ -void replaceInPlace(T, Range)(ref T[] array, size_t from, size_t to, Range stuff) -if (is(typeof(replace(array, from, to, stuff)))) -{ - static if (isDynamicArray!Range && - is(Unqual!(ElementEncodingType!Range) == T) && - !isNarrowString!(T[])) - { - // optimized for homogeneous arrays that can be overwritten. - import std.algorithm.mutation : remove; - import std.typecons : tuple; - - if (overlap(array, stuff).length) - { - // use slower/conservative method - array = array[0 .. from] ~ stuff ~ array[to .. $]; - } - else if (stuff.length <= to - from) - { - // replacement reduces length - immutable stuffEnd = from + stuff.length; - array[from .. stuffEnd] = stuff[]; - if (stuffEnd < to) - array = remove(array, tuple(stuffEnd, to)); - } - else - { - // replacement increases length - // @@@TODO@@@: optimize this - immutable replaceLen = to - from; - array[from .. to] = stuff[0 .. replaceLen]; - insertInPlace(array, to, stuff[replaceLen .. $]); - } - } - else - { - // default implementation, just do what replace does. - array = replace(array, from, to, stuff); - } -} - -/// -@safe unittest -{ - int[] a = [1, 4, 5]; - replaceInPlace(a, 1u, 2u, [2, 3, 4]); - assert(a == [1, 2, 3, 4, 5]); - replaceInPlace(a, 1u, 2u, cast(int[])[]); - assert(a == [1, 3, 4, 5]); - replaceInPlace(a, 1u, 3u, a[2 .. 4]); - assert(a == [1, 4, 5, 5]); -} - -// https://issues.dlang.org/show_bug.cgi?id=12889 -@safe unittest -{ - int[1][] arr = [[0], [1], [2], [3], [4], [5], [6]]; - int[1][] stuff = [[0], [1]]; - replaceInPlace(arr, 4, 6, stuff); - assert(arr == [[0], [1], [2], [3], [0], [1], [6]]); -} - -@system unittest -{ - // https://issues.dlang.org/show_bug.cgi?id=14925 - char[] a = "mon texte 1".dup; - char[] b = "abc".dup; - replaceInPlace(a, 4, 9, b); - assert(a == "mon abc 1"); - - // ensure we can replace in place with different encodings - string unicoded = "\U00010437"; - string unicodedLong = "\U00010437aaaaa"; - string base = "abcXXXxyz"; - string result = "abc\U00010437xyz"; - string resultLong = "abc\U00010437aaaaaxyz"; - size_t repstart = 3; - size_t repend = 3 + 3; - - void testStringReplaceInPlace(T, U)() - { - import std.algorithm.comparison : equal; - import std.conv; - auto a = unicoded.to!(U[]); - auto b = unicodedLong.to!(U[]); - - auto test = base.to!(T[]); - - test.replaceInPlace(repstart, repend, a); - assert(equal(test, result), "Failed for types " ~ T.stringof ~ " and " ~ U.stringof); - - test = base.to!(T[]); - - test.replaceInPlace(repstart, repend, b); - assert(equal(test, resultLong), "Failed for types " ~ T.stringof ~ " and " ~ U.stringof); - } - - import std.meta : AliasSeq; - alias allChars = AliasSeq!(char, immutable(char), const(char), - wchar, immutable(wchar), const(wchar), - dchar, immutable(dchar), const(dchar)); - foreach (T; allChars) - foreach (U; allChars) - testStringReplaceInPlace!(T, U)(); - - void testInout(inout(int)[] a) - { - // will be transferred to the 'replace' function - replaceInPlace(a, 1, 2, [1,2,3]); - } -} - -@safe unittest -{ - // the constraint for the first overload used to match this, which wouldn't compile. - import std.algorithm.comparison : equal; - long[] a = [1L, 2, 3]; - int[] b = [4, 5, 6]; - a.replaceInPlace(1, 2, b); - assert(equal(a, [1L, 4, 5, 6, 3])); -} - -@system unittest -{ - import core.exception; - import std.algorithm.comparison : equal; - import std.algorithm.iteration : filter; - import std.conv : to; - import std.exception; - - - bool test(T, U, V)(T orig, size_t from, size_t to, U toReplace, V result) - { - { - static if (is(T == typeof(T.init.dup))) - auto a = orig.dup; - else - auto a = orig.idup; - - a.replaceInPlace(from, to, toReplace); - if (!equal(a, result)) - return false; - } - - static if (isInputRange!U) - { - orig.replaceInPlace(from, to, filter!"true"(toReplace)); - return equal(orig, result); - } - else - return true; - } - - assert(test([1, 2, 3, 4], 0, 0, [5, 6, 7], [5, 6, 7, 1, 2, 3, 4])); - assert(test([1, 2, 3, 4], 0, 2, cast(int[])[], [3, 4])); - assert(test([1, 2, 3, 4], 0, 4, [5, 6, 7], [5, 6, 7])); - assert(test([1, 2, 3, 4], 0, 2, [5, 6, 7], [5, 6, 7, 3, 4])); - assert(test([1, 2, 3, 4], 2, 4, [5, 6, 7], [1, 2, 5, 6, 7])); - - assert(test([1, 2, 3, 4], 0, 0, filter!"true"([5, 6, 7]), [5, 6, 7, 1, 2, 3, 4])); - assert(test([1, 2, 3, 4], 0, 2, filter!"true"(cast(int[])[]), [3, 4])); - assert(test([1, 2, 3, 4], 0, 4, filter!"true"([5, 6, 7]), [5, 6, 7])); - assert(test([1, 2, 3, 4], 0, 2, filter!"true"([5, 6, 7]), [5, 6, 7, 3, 4])); - assert(test([1, 2, 3, 4], 2, 4, filter!"true"([5, 6, 7]), [1, 2, 5, 6, 7])); - - void testStr(T, U)(string file = __FILE__, size_t line = __LINE__) - { - - auto l = to!T("hello"); - auto r = to!U(" world"); - - enforce(test(l, 0, 0, r, " worldhello"), - new AssertError("testStr failure 1", file, line)); - enforce(test(l, 0, 3, r, " worldlo"), - new AssertError("testStr failure 2", file, line)); - enforce(test(l, 3, l.length, r, "hel world"), - new AssertError("testStr failure 3", file, line)); - enforce(test(l, 0, l.length, r, " world"), - new AssertError("testStr failure 4", file, line)); - enforce(test(l, l.length, l.length, r, "hello world"), - new AssertError("testStr failure 5", file, line)); - } - - testStr!(string, string)(); - testStr!(string, wstring)(); - testStr!(string, dstring)(); - testStr!(wstring, string)(); - testStr!(wstring, wstring)(); - testStr!(wstring, dstring)(); - testStr!(dstring, string)(); - testStr!(dstring, wstring)(); - testStr!(dstring, dstring)(); -} - -/++ - Replaces the first occurrence of `from` with `to` in `subject`. - - Params: - subject = the array to scan - from = the item to replace - to = the item to replace `from` with - - Returns: - A new array without changing the contents of `subject`, or the original - array if no match is found. - +/ -E[] replaceFirst(E, R1, R2)(E[] subject, R1 from, R2 to) -if (isDynamicArray!(E[]) && - isForwardRange!R1 && is(typeof(appender!(E[])().put(from[0 .. 1]))) && - isForwardRange!R2 && is(typeof(appender!(E[])().put(to[0 .. 1])))) -{ - if (from.empty) return subject; - static if (isSomeString!(E[])) - { - import std.string : indexOf; - immutable idx = subject.indexOf(from); - } - else - { - import std.algorithm.searching : countUntil; - immutable idx = subject.countUntil(from); - } - if (idx == -1) - return subject; - - auto app = appender!(E[])(); - app.put(subject[0 .. idx]); - app.put(to); - - static if (isSomeString!(E[]) && isSomeString!R1) - { - import std.utf : codeLength; - immutable fromLength = codeLength!(Unqual!E, R1)(from); - } - else - immutable fromLength = from.length; - - app.put(subject[idx + fromLength .. $]); - - return app.data; -} - -/// -@safe unittest -{ - auto a = [1, 2, 2, 3, 4, 5]; - auto b = a.replaceFirst([2], [1337]); - assert(b == [1, 1337, 2, 3, 4, 5]); - - auto s = "This is a foo foo list"; - auto r = s.replaceFirst("foo", "silly"); - assert(r == "This is a silly foo list"); -} - -@safe unittest -{ - import std.algorithm.comparison : cmp; - import std.conv : to; - - static foreach (S; AliasSeq!(string, wstring, dstring, char[], wchar[], dchar[], - const(char[]), immutable(char[]))) - { - static foreach (T; AliasSeq!(string, wstring, dstring, char[], wchar[], dchar[], - const(char[]), immutable(char[]))) - {{ - auto s = to!S("This is a foo foo list"); - auto s2 = to!S("Thüs is a ßöö foo list"); - auto from = to!T("foo"); - auto from2 = to!T("ßöö"); - auto into = to!T("silly"); - auto into2 = to!T("sälly"); - - S r1 = replaceFirst(s, from, into); - assert(cmp(r1, "This is a silly foo list") == 0); - - S r11 = replaceFirst(s2, from2, into2); - assert(cmp(r11, "Thüs is a sälly foo list") == 0, - to!string(r11) ~ " : " ~ S.stringof ~ " " ~ T.stringof); - - S r2 = replaceFirst(r1, from, into); - assert(cmp(r2, "This is a silly silly list") == 0); - - S r3 = replaceFirst(s, to!T(""), into); - assert(cmp(r3, "This is a foo foo list") == 0); - - assert(replaceFirst(r3, to!T("won't find"), to!T("whatever")) is r3); - }} - } -} - -// https://issues.dlang.org/show_bug.cgi?id=8187 -@safe unittest -{ - auto res = ["a", "a"]; - assert(replace(res, "a", "b") == ["b", "b"]); - assert(replaceFirst(res, "a", "b") == ["b", "a"]); -} - -/++ - Replaces the last occurrence of `from` with `to` in `subject`. - - Params: - subject = the array to scan - from = the item to replace - to = the item to replace `from` with - - Returns: - A new array without changing the contents of `subject`, or the original - array if no match is found. - +/ -E[] replaceLast(E, R1, R2)(E[] subject, R1 from , R2 to) -if (isDynamicArray!(E[]) && - isForwardRange!R1 && is(typeof(appender!(E[])().put(from[0 .. 1]))) && - isForwardRange!R2 && is(typeof(appender!(E[])().put(to[0 .. 1])))) -{ - import std.range : retro; - if (from.empty) return subject; - static if (isSomeString!(E[])) - { - import std.string : lastIndexOf; - auto idx = subject.lastIndexOf(from); - } - else - { - import std.algorithm.searching : countUntil; - auto idx = retro(subject).countUntil(retro(from)); - } - - if (idx == -1) - return subject; - - static if (isSomeString!(E[]) && isSomeString!R1) - { - import std.utf : codeLength; - auto fromLength = codeLength!(Unqual!E, R1)(from); - } - else - auto fromLength = from.length; - - auto app = appender!(E[])(); - static if (isSomeString!(E[])) - app.put(subject[0 .. idx]); - else - app.put(subject[0 .. $ - idx - fromLength]); - - app.put(to); - - static if (isSomeString!(E[])) - app.put(subject[idx+fromLength .. $]); - else - app.put(subject[$ - idx .. $]); - - return app.data; -} - -/// -@safe unittest -{ - auto a = [1, 2, 2, 3, 4, 5]; - auto b = a.replaceLast([2], [1337]); - assert(b == [1, 2, 1337, 3, 4, 5]); - - auto s = "This is a foo foo list"; - auto r = s.replaceLast("foo", "silly"); - assert(r == "This is a foo silly list", r); -} - -@safe unittest -{ - import std.algorithm.comparison : cmp; - import std.conv : to; - - static foreach (S; AliasSeq!(string, wstring, dstring, char[], wchar[], dchar[], - const(char[]), immutable(char[]))) - { - static foreach (T; AliasSeq!(string, wstring, dstring, char[], wchar[], dchar[], - const(char[]), immutable(char[]))) - {{ - auto s = to!S("This is a foo foo list"); - auto s2 = to!S("Thüs is a ßöö ßöö list"); - auto from = to!T("foo"); - auto from2 = to!T("ßöö"); - auto into = to!T("silly"); - auto into2 = to!T("sälly"); - - S r1 = replaceLast(s, from, into); - assert(cmp(r1, "This is a foo silly list") == 0, to!string(r1)); - - S r11 = replaceLast(s2, from2, into2); - assert(cmp(r11, "Thüs is a ßöö sälly list") == 0, - to!string(r11) ~ " : " ~ S.stringof ~ " " ~ T.stringof); - - S r2 = replaceLast(r1, from, into); - assert(cmp(r2, "This is a silly silly list") == 0); - - S r3 = replaceLast(s, to!T(""), into); - assert(cmp(r3, "This is a foo foo list") == 0); - - assert(replaceLast(r3, to!T("won't find"), to!T("whatever")) is r3); - }} - } -} - -/++ - Creates a new array such that the items in `slice` are replaced with the - items in `replacement`. `slice` and `replacement` do not need to be the - same length. The result will grow or shrink based on the items given. - - Params: - s = the base of the new array - slice = the slice of `s` to be replaced - replacement = the items to replace `slice` with - - Returns: - A new array that is `s` with `slice` replaced by - `replacement[]`. - - See_Also: - $(REF substitute, std,algorithm,iteration) for a lazy replace. - +/ -inout(T)[] replaceSlice(T)(inout(T)[] s, in T[] slice, in T[] replacement) -in -{ - // Verify that slice[] really is a slice of s[] - assert(overlap(s, slice) is slice, "slice[] is not a subslice of s[]"); -} -do -{ - auto result = new T[s.length - slice.length + replacement.length]; - immutable so = &slice[0] - &s[0]; - result[0 .. so] = s[0 .. so]; - result[so .. so + replacement.length] = replacement[]; - result[so + replacement.length .. result.length] = - s[so + slice.length .. s.length]; - - return () @trusted inout { - return cast(inout(T)[]) result; - }(); -} - -/// -@safe unittest -{ - auto a = [1, 2, 3, 4, 5]; - auto b = replaceSlice(a, a[1 .. 4], [0, 0, 0]); - - assert(b == [1, 0, 0, 0, 5]); -} - -@safe unittest -{ - import std.algorithm.comparison : cmp; - - string s = "hello"; - string slice = s[2 .. 4]; - - auto r = replaceSlice(s, slice, "bar"); - int i; - i = cmp(r, "hebaro"); - assert(i == 0); -} - -/** -Implements an output range that appends data to an array. This is -recommended over $(D array ~= data) when appending many elements because it is more -efficient. `Appender` maintains its own array metadata locally, so it can avoid -the $(DDSUBLINK spec/arrays, capacity-reserve, performance hit of looking up slice `capacity`) -for each append. - -Params: - A = the array type to simulate. - -See_Also: $(LREF appender) - */ -struct Appender(A) -if (isDynamicArray!A) -{ - import core.memory : GC; - - private alias T = ElementEncodingType!A; - - private struct Data - { - size_t capacity; - Unqual!T[] arr; - bool tryExtendBlock = false; - } - - private Data* _data; - - /** - * Constructs an `Appender` with a given array. Note that this does not copy the - * data. If the array has a larger capacity as determined by `arr.capacity`, - * it will be used by the appender. After initializing an appender on an array, - * appending to the original array will reallocate. - */ - this(A arr) @trusted - { - // initialize to a given array. - _data = new Data; - _data.arr = cast(Unqual!T[]) arr; //trusted - - if (__ctfe) - return; - - // We want to use up as much of the block the array is in as possible. - // if we consume all the block that we can, then array appending is - // safe WRT built-in append, and we can use the entire block. - // We only do this for mutable types that can be extended. - static if (isMutable!T && is(typeof(arr.length = size_t.max))) - { - immutable cap = arr.capacity; //trusted - // Replace with "GC.setAttr( Not Appendable )" once pure (and fixed) - if (cap > arr.length) - arr.length = cap; - } - _data.capacity = arr.length; - } - - /** - * Reserve at least newCapacity elements for appending. Note that more elements - * may be reserved than requested. If `newCapacity <= capacity`, then nothing is - * done. - * - * Params: - * newCapacity = the capacity the `Appender` should have - */ - void reserve(size_t newCapacity) - { - if (_data) - { - if (newCapacity > _data.capacity) - ensureAddable(newCapacity - _data.arr.length); - } - else - { - ensureAddable(newCapacity); - } - } - - /** - * Returns: the capacity of the array (the maximum number of elements the - * managed array can accommodate before triggering a reallocation). If any - * appending will reallocate, `0` will be returned. - */ - @property size_t capacity() const - { - return _data ? _data.capacity : 0; - } - - /** - * Use opSlice() from now on. - * Returns: The managed array. - */ - @property inout(T)[] data() inout @trusted - { - return this[]; - } - - /** - * Returns: The managed array. - */ - @property inout(T)[] opSlice() inout @trusted - { - /* @trusted operation: - * casting Unqual!T[] to inout(T)[] - */ - return cast(typeof(return))(_data ? _data.arr : null); - } - - // ensure we can add nelems elements, resizing as necessary - private void ensureAddable(size_t nelems) - { - if (!_data) - _data = new Data; - immutable len = _data.arr.length; - immutable reqlen = len + nelems; - - if (_data.capacity >= reqlen) - return; - - // need to increase capacity - if (__ctfe) - { - static if (__traits(compiles, new Unqual!T[1])) - { - _data.arr.length = reqlen; - } - else - { - // avoid restriction of @disable this() - _data.arr = _data.arr[0 .. _data.capacity]; - foreach (i; _data.capacity .. reqlen) - _data.arr ~= Unqual!T.init; - } - _data.arr = _data.arr[0 .. len]; - _data.capacity = reqlen; - } - else - { - // Time to reallocate. - // We need to almost duplicate what's in druntime, except we - // have better access to the capacity field. - auto newlen = appenderNewCapacity!(T.sizeof)(_data.capacity, reqlen); - // first, try extending the current block - if (_data.tryExtendBlock) - { - immutable u = (() @trusted => GC.extend(_data.arr.ptr, nelems * T.sizeof, (newlen - len) * T.sizeof))(); - if (u) - { - // extend worked, update the capacity - _data.capacity = u / T.sizeof; - return; - } - } - - - // didn't work, must reallocate - import core.checkedint : mulu; - bool overflow; - const nbytes = mulu(newlen, T.sizeof, overflow); - if (overflow) assert(false, "the reallocation would exceed the " - ~ "available pointer range"); - - auto bi = (() @trusted => GC.qalloc(nbytes, blockAttribute!T))(); - _data.capacity = bi.size / T.sizeof; - import core.stdc.string : memcpy; - if (len) - () @trusted { memcpy(bi.base, _data.arr.ptr, len * T.sizeof); }(); - _data.arr = (() @trusted => (cast(Unqual!T*) bi.base)[0 .. len])(); - _data.tryExtendBlock = true; - // leave the old data, for safety reasons - } - } - - private template canPutItem(U) - { - enum bool canPutItem = - is(Unqual!U : Unqual!T) || - isSomeChar!T && isSomeChar!U; - } - private template canPutConstRange(Range) - { - enum bool canPutConstRange = - isInputRange!(Unqual!Range) && - !isInputRange!Range && - is(typeof(Appender.init.put(Range.init.front))); - } - private template canPutRange(Range) - { - enum bool canPutRange = - isInputRange!Range && - is(typeof(Appender.init.put(Range.init.front))); - } - - /** - * Appends `item` to the managed array. Performs encoding for - * `char` types if `A` is a differently typed `char` array. - * - * Params: - * item = the single item to append - */ - void put(U)(U item) if (canPutItem!U) - { - static if (isSomeChar!T && isSomeChar!U && T.sizeof < U.sizeof) - { - /* may throwable operation: - * - std.utf.encode - */ - // must do some transcoding around here - import std.utf : encode; - Unqual!T[T.sizeof == 1 ? 4 : 2] encoded; - auto len = encode(encoded, item); - put(encoded[0 .. len]); - } - else - { - import core.lifetime : emplace; - - ensureAddable(1); - immutable len = _data.arr.length; - - auto bigData = (() @trusted => _data.arr.ptr[0 .. len + 1])(); - auto itemUnqual = (() @trusted => & cast() item)(); - emplace(&bigData[len], *itemUnqual); - //We do this at the end, in case of exceptions - _data.arr = bigData; - } - } - - // Const fixing hack. - void put(Range)(Range items) if (canPutConstRange!Range) - { - alias p = put!(Unqual!Range); - p(items); - } - - /** - * Appends an entire range to the managed array. Performs encoding for - * `char` elements if `A` is a differently typed `char` array. - * - * Params: - * items = the range of items to append - */ - void put(Range)(Range items) if (canPutRange!Range) - { - // note, we disable this branch for appending one type of char to - // another because we can't trust the length portion. - static if (!(isSomeChar!T && isSomeChar!(ElementType!Range) && - !is(immutable Range == immutable T[])) && - is(typeof(items.length) == size_t)) - { - // optimization -- if this type is something other than a string, - // and we are adding exactly one element, call the version for one - // element. - static if (!isSomeChar!T) - { - if (items.length == 1) - { - put(items.front); - return; - } - } - - // make sure we have enough space, then add the items - auto bigDataFun(size_t extra) - { - ensureAddable(extra); - return (() @trusted => _data.arr.ptr[0 .. _data.arr.length + extra])(); - } - auto bigData = bigDataFun(items.length); - - immutable len = _data.arr.length; - immutable newlen = bigData.length; - - alias UT = Unqual!T; - - static if (is(typeof(_data.arr[] = items[])) && - !hasElaborateAssign!UT && isAssignable!(UT, ElementEncodingType!Range)) - { - bigData[len .. newlen] = items[]; - } - else - { - import core.internal.lifetime : emplaceRef; - foreach (ref it ; bigData[len .. newlen]) - { - emplaceRef!T(it, items.front); - items.popFront(); - } - } - - //We do this at the end, in case of exceptions - _data.arr = bigData; - } - else static if (isSomeChar!T && isSomeChar!(ElementType!Range) && - !is(immutable T == immutable ElementType!Range)) - { - // need to decode and encode - import std.utf : decodeFront; - while (!items.empty) - { - auto c = items.decodeFront; - put(c); - } - } - else - { - //pragma(msg, Range.stringof); - // Generic input range - for (; !items.empty; items.popFront()) - { - put(items.front); - } - } - } - - /** - * Appends to the managed array. - * - * See_Also: $(LREF Appender.put) - */ - alias opOpAssign(string op : "~") = put; - - // only allow overwriting data on non-immutable and non-const data - static if (isMutable!T) - { - /** - * Clears the managed array. This allows the elements of the array to be reused - * for appending. - * - * Note: clear is disabled for immutable or const element types, due to the - * possibility that `Appender` might overwrite immutable data. - */ - void clear() @trusted pure nothrow - { - if (_data) - { - _data.arr = _data.arr.ptr[0 .. 0]; - } - } - - /** - * Shrinks the managed array to the given length. - * - * Throws: `Exception` if newlength is greater than the current array length. - * Note: shrinkTo is disabled for immutable or const element types. - */ - void shrinkTo(size_t newlength) @trusted pure - { - import std.exception : enforce; - if (_data) - { - enforce(newlength <= _data.arr.length, "Attempting to shrink Appender with newlength > length"); - _data.arr = _data.arr.ptr[0 .. newlength]; - } - else - enforce(newlength == 0, "Attempting to shrink empty Appender with non-zero newlength"); - } - } - - /** - * Gives a string in the form of `Appender!(A)(data)`. - * - * Params: - * w = A `char` accepting - * $(REF_ALTTEXT output range, isOutputRange, std, range, primitives). - * fmt = A $(REF FormatSpec, std, format) which controls how the array - * is formatted. - * Returns: - * A `string` if `writer` is not set; `void` otherwise. - */ - string toString()() const - { - import std.format.spec : singleSpec; - - auto app = appender!string(); - auto spec = singleSpec("%s"); - immutable len = _data ? _data.arr.length : 0; - // different reserve lengths because each element in a - // non-string-like array uses two extra characters for `, `. - static if (isSomeString!A) - { - app.reserve(len + 25); - } - else - { - // Multiplying by three is a very conservative estimate of - // length, as it assumes each element is only one char - app.reserve((len * 3) + 25); - } - toString(app, spec); - return app.data; - } - - import std.format.spec : FormatSpec; - - /// ditto - template toString(Writer) - if (isOutputRange!(Writer, char)) - { - void toString(ref Writer w, scope const ref FormatSpec!char fmt) const - { - import std.format.write : formatValue; - import std.range.primitives : put; - put(w, Unqual!(typeof(this)).stringof); - put(w, '('); - formatValue(w, data, fmt); - put(w, ')'); - } - } -} - -/// -@safe pure nothrow unittest -{ - auto app = appender!string(); - string b = "abcdefg"; - foreach (char c; b) - app.put(c); - assert(app[] == "abcdefg"); - - int[] a = [ 1, 2 ]; - auto app2 = appender(a); - app2.put(3); - app2.put([ 4, 5, 6 ]); - assert(app2[] == [ 1, 2, 3, 4, 5, 6 ]); -} - -@safe pure unittest -{ - import std.format : format; - import std.format.spec : singleSpec; - - auto app = appender!(int[])(); - app.put(1); - app.put(2); - app.put(3); - assert("%s".format(app) == "Appender!(int[])(%s)".format([1,2,3])); - - auto app2 = appender!string(); - auto spec = singleSpec("%s"); - app.toString(app2, spec); - assert(app2[] == "Appender!(int[])([1, 2, 3])"); - - auto app3 = appender!string(); - spec = singleSpec("%(%04d, %)"); - app.toString(app3, spec); - assert(app3[] == "Appender!(int[])(0001, 0002, 0003)"); -} - -// https://issues.dlang.org/show_bug.cgi?id=17251 -@safe pure nothrow unittest -{ - static struct R - { - int front() const { return 0; } - bool empty() const { return true; } - void popFront() {} - } - - auto app = appender!(R[]); - const(R)[1] r; - app.put(r[0]); - app.put(r[]); -} - -// https://issues.dlang.org/show_bug.cgi?id=13300 -@safe pure nothrow unittest -{ - static test(bool isPurePostblit)() - { - static if (!isPurePostblit) - static int i; - - struct Simple - { - @disable this(); // Without this, it works. - static if (!isPurePostblit) - this(this) { i++; } - else - pure this(this) { } - - private: - this(int tmp) { } - } - - struct Range - { - @property Simple front() { return Simple(0); } - void popFront() { count++; } - @property empty() { return count < 3; } - size_t count; - } - - Range r; - auto a = r.array(); - } - - static assert(__traits(compiles, () pure { test!true(); })); - static assert(!__traits(compiles, () pure { test!false(); })); -} - -// https://issues.dlang.org/show_bug.cgi?id=19572 -@safe pure nothrow unittest -{ - static struct Struct - { - int value; - - int fun() const { return 23; } - - alias fun this; - } - - Appender!(Struct[]) appender; - - appender.put(const(Struct)(42)); - - auto result = appender[][0]; - - assert(result.value != 23); -} - -@safe pure unittest -{ - import std.conv : to; - import std.utf : byCodeUnit; - auto str = "ウェブサイト"; - auto wstr = appender!wstring(); - put(wstr, str.byCodeUnit); - assert(wstr.data == str.to!wstring); -} - -// https://issues.dlang.org/show_bug.cgi?id=21256 -@safe pure unittest -{ - Appender!string app1; - app1.toString(); - - Appender!(int[]) app2; - app2.toString(); -} - -//Calculates an efficient growth scheme based on the old capacity -//of data, and the minimum requested capacity. -//arg curLen: The current length -//arg reqLen: The length as requested by the user -//ret sugLen: A suggested growth. -private size_t appenderNewCapacity(size_t TSizeOf)(size_t curLen, size_t reqLen) -{ - import core.bitop : bsr; - import std.algorithm.comparison : max; - if (curLen == 0) - return max(reqLen,8); - ulong mult = 100 + (1000UL) / (bsr(curLen * TSizeOf) + 1); - // limit to doubling the length, we don't want to grow too much - if (mult > 200) - mult = 200; - auto sugLen = cast(size_t)((curLen * mult + 99) / 100); - return max(reqLen, sugLen); -} - -/** - * A version of $(LREF Appender) that can update an array in-place. - * It forwards all calls to an underlying appender implementation. - * Any calls made to the appender also update the pointer to the - * original array passed in. - * - * Tip: Use the `arrayPtr` overload of $(LREF appender) for construction with type-inference. - * - * Params: - * A = The array type to simulate - */ -struct RefAppender(A) -if (isDynamicArray!A) -{ - private alias T = ElementEncodingType!A; - - private - { - Appender!A impl; - A* arr; - } - - /** - * Constructs a `RefAppender` with a given array reference. This does not copy the - * data. If the array has a larger capacity as determined by `arr.capacity`, it - * will be used by the appender. - * - * Note: Do not use built-in appending (i.e. `~=`) on the original array - * until you are done with the appender, because subsequent calls to the appender - * will reallocate the array data without those appends. - * - * Params: - * arr = Pointer to an array. Must not be _null. - */ - this(A* arr) - { - impl = Appender!A(*arr); - this.arr = arr; - } - - /** Wraps remaining `Appender` methods such as $(LREF put). - * Params: - * fn = Method name to call. - * args = Arguments to pass to the method. - */ - void opDispatch(string fn, Args...)(Args args) - if (__traits(compiles, (Appender!A a) => mixin("a." ~ fn ~ "(args)"))) - { - // we do it this way because we can't cache a void return - scope(exit) *this.arr = impl[]; - mixin("return impl." ~ fn ~ "(args);"); - } - - /** - * Appends `rhs` to the managed array. - * Params: - * rhs = Element or range. - */ - void opOpAssign(string op : "~", U)(U rhs) - if (__traits(compiles, (Appender!A a){ a.put(rhs); })) - { - scope(exit) *this.arr = impl[]; - impl.put(rhs); - } - - /** - * Returns the capacity of the array (the maximum number of elements the - * managed array can accommodate before triggering a reallocation). If any - * appending will reallocate, `capacity` returns `0`. - */ - @property size_t capacity() const - { - return impl.capacity; - } - - /* Use opSlice() instead. - * Returns: the managed array. - */ - @property inout(T)[] data() inout - { - return impl[]; - } - - /** - * Returns: the managed array. - */ - @property inout(ElementEncodingType!A)[] opSlice() inout - { - return impl[]; - } -} - -/// -@safe pure nothrow -unittest -{ - int[] a = [1, 2]; - auto app2 = appender(&a); - assert(app2[] == [1, 2]); - assert(a == [1, 2]); - app2 ~= 3; - app2 ~= [4, 5, 6]; - assert(app2[] == [1, 2, 3, 4, 5, 6]); - assert(a == [1, 2, 3, 4, 5, 6]); - - app2.reserve(5); - assert(app2.capacity >= 5); -} - -/++ - Convenience function that returns an $(LREF Appender) instance, - optionally initialized with `array`. - +/ -Appender!A appender(A)() -if (isDynamicArray!A) -{ - return Appender!A(null); -} -/// ditto -Appender!(E[]) appender(A : E[], E)(auto ref A array) -{ - static assert(!isStaticArray!A || __traits(isRef, array), - "Cannot create Appender from an rvalue static array"); - - return Appender!(E[])(array); -} - -@safe pure nothrow unittest -{ - auto app = appender!(char[])(); - string b = "abcdefg"; - foreach (char c; b) app.put(c); - assert(app[] == "abcdefg"); -} - -@safe pure nothrow unittest -{ - auto app = appender!(char[])(); - string b = "abcdefg"; - foreach (char c; b) app ~= c; - assert(app[] == "abcdefg"); -} - -@safe pure nothrow unittest -{ - int[] a = [ 1, 2 ]; - auto app2 = appender(a); - assert(app2[] == [ 1, 2 ]); - app2.put(3); - app2.put([ 4, 5, 6 ][]); - assert(app2[] == [ 1, 2, 3, 4, 5, 6 ]); - app2.put([ 7 ]); - assert(app2[] == [ 1, 2, 3, 4, 5, 6, 7 ]); -} - -@safe pure nothrow unittest -{ - auto app4 = appender([]); - try // shrinkTo may throw - { - app4.shrinkTo(0); - } - catch (Exception) assert(0); -} - -// https://issues.dlang.org/show_bug.cgi?id=5663 -// https://issues.dlang.org/show_bug.cgi?id=9725 -@safe pure nothrow unittest -{ - import std.exception : assertNotThrown; - - static foreach (S; AliasSeq!(char[], const(char)[], string)) - { - { - Appender!S app5663i; - assertNotThrown(app5663i.put("\xE3")); - assert(app5663i[] == "\xE3"); - - Appender!S app5663c; - assertNotThrown(app5663c.put(cast(const(char)[])"\xE3")); - assert(app5663c[] == "\xE3"); - - Appender!S app5663m; - assertNotThrown(app5663m.put("\xE3".dup)); - assert(app5663m[] == "\xE3"); - } - // ditto for ~= - { - Appender!S app5663i; - assertNotThrown(app5663i ~= "\xE3"); - assert(app5663i[] == "\xE3"); - - Appender!S app5663c; - assertNotThrown(app5663c ~= cast(const(char)[])"\xE3"); - assert(app5663c[] == "\xE3"); - - Appender!S app5663m; - assertNotThrown(app5663m ~= "\xE3".dup); - assert(app5663m[] == "\xE3"); - } - } -} - -// https://issues.dlang.org/show_bug.cgi?id=10122 -@safe pure nothrow unittest -{ - import std.exception : assertCTFEable; - - static struct S10122 - { - int val; - - @disable this(); - this(int v) @safe pure nothrow { val = v; } - } - assertCTFEable!( - { - auto w = appender!(S10122[])(); - w.put(S10122(1)); - assert(w[].length == 1 && w[][0].val == 1); - }); -} - -@safe pure nothrow unittest -{ - import std.exception : assertThrown; - - int[] a = [ 1, 2 ]; - auto app2 = appender(a); - assert(app2[] == [ 1, 2 ]); - app2 ~= 3; - app2 ~= [ 4, 5, 6 ][]; - assert(app2[] == [ 1, 2, 3, 4, 5, 6 ]); - app2 ~= [ 7 ]; - assert(app2[] == [ 1, 2, 3, 4, 5, 6, 7 ]); - - app2.reserve(5); - assert(app2.capacity >= 5); - - try // shrinkTo may throw - { - app2.shrinkTo(3); - } - catch (Exception) assert(0); - assert(app2[] == [ 1, 2, 3 ]); - assertThrown(app2.shrinkTo(5)); - - const app3 = app2; - assert(app3.capacity >= 3); - assert(app3[] == [1, 2, 3]); -} - -/// -@safe pure nothrow -unittest -{ - auto w = appender!string; - // pre-allocate space for at least 10 elements (this avoids costly reallocations) - w.reserve(10); - assert(w.capacity >= 10); - - w.put('a'); // single elements - w.put("bc"); // multiple elements - - // use the append syntax - w ~= 'd'; - w ~= "ef"; - - assert(w[] == "abcdef"); -} - -@safe pure nothrow unittest -{ - auto w = appender!string(); - w.reserve(4); - cast(void) w.capacity; - cast(void) w[]; - try - { - wchar wc = 'a'; - dchar dc = 'a'; - w.put(wc); // decoding may throw - w.put(dc); // decoding may throw - } - catch (Exception) assert(0); -} - -@safe pure nothrow unittest -{ - auto w = appender!(int[])(); - w.reserve(4); - cast(void) w.capacity; - cast(void) w[]; - w.put(10); - w.put([10]); - w.clear(); - try - { - w.shrinkTo(0); - } - catch (Exception) assert(0); - - struct N - { - int payload; - alias payload this; - } - w.put(N(1)); - w.put([N(2)]); - - struct S(T) - { - @property bool empty() { return true; } - @property T front() { return T.init; } - void popFront() {} - } - S!int r; - w.put(r); -} - -// https://issues.dlang.org/show_bug.cgi?id=10690 -@safe pure nothrow unittest -{ - import std.algorithm.iteration : filter; - import std.typecons : tuple; - [tuple(1)].filter!(t => true).array; // No error - [tuple("A")].filter!(t => true).array; // error -} - -@safe pure nothrow unittest -{ - import std.range; - //Coverage for put(Range) - struct S1 - { - } - struct S2 - { - void opAssign(S2){} - } - auto a1 = Appender!(S1[])(); - auto a2 = Appender!(S2[])(); - auto au1 = Appender!(const(S1)[])(); - a1.put(S1().repeat().take(10)); - a2.put(S2().repeat().take(10)); - auto sc1 = const(S1)(); - au1.put(sc1.repeat().take(10)); -} - -@system pure unittest -{ - import std.range; - struct S2 - { - void opAssign(S2){} - } - auto au2 = Appender!(const(S2)[])(); - auto sc2 = const(S2)(); - au2.put(sc2.repeat().take(10)); -} - -@system pure nothrow unittest -{ - struct S - { - int* p; - } - - auto a0 = Appender!(S[])(); - auto a1 = Appender!(const(S)[])(); - auto a2 = Appender!(immutable(S)[])(); - auto s0 = S(null); - auto s1 = const(S)(null); - auto s2 = immutable(S)(null); - a1.put(s0); - a1.put(s1); - a1.put(s2); - a1.put([s0]); - a1.put([s1]); - a1.put([s2]); - a0.put(s0); - static assert(!is(typeof(a0.put(a1)))); - static assert(!is(typeof(a0.put(a2)))); - a0.put([s0]); - static assert(!is(typeof(a0.put([a1])))); - static assert(!is(typeof(a0.put([a2])))); - static assert(!is(typeof(a2.put(a0)))); - static assert(!is(typeof(a2.put(a1)))); - a2.put(s2); - static assert(!is(typeof(a2.put([a0])))); - static assert(!is(typeof(a2.put([a1])))); - a2.put([s2]); -} - -// https://issues.dlang.org/show_bug.cgi?id=9528 -@safe pure nothrow unittest -{ - const(E)[] fastCopy(E)(E[] src) { - auto app = appender!(const(E)[])(); - foreach (i, e; src) - app.put(e); - return app[]; - } - - static class C {} - static struct S { const(C) c; } - S[] s = [ S(new C) ]; - - auto t = fastCopy(s); // Does not compile - assert(t.length == 1); -} - -// https://issues.dlang.org/show_bug.cgi?id=10753 -@safe pure unittest -{ - import std.algorithm.iteration : map; - struct Foo { - immutable dchar d; - } - struct Bar { - immutable int x; - } - "12".map!Foo.array; - [1, 2].map!Bar.array; -} - -@safe pure nothrow unittest -{ - import std.algorithm.comparison : equal; - - //New appender signature tests - alias mutARR = int[]; - alias conARR = const(int)[]; - alias immARR = immutable(int)[]; - - mutARR mut; - conARR con; - immARR imm; - - auto app1 = Appender!mutARR(mut); //Always worked. Should work. Should not create a warning. - app1.put(7); - assert(equal(app1[], [7])); - static assert(!is(typeof(Appender!mutARR(con)))); //Never worked. Should not work. - static assert(!is(typeof(Appender!mutARR(imm)))); //Never worked. Should not work. - - auto app2 = Appender!conARR(mut); //Always worked. Should work. Should not create a warning. - app2.put(7); - assert(equal(app2[], [7])); - auto app3 = Appender!conARR(con); //Didn't work. Now works. Should not create a warning. - app3.put(7); - assert(equal(app3[], [7])); - auto app4 = Appender!conARR(imm); //Didn't work. Now works. Should not create a warning. - app4.put(7); - assert(equal(app4[], [7])); - - //{auto app = Appender!immARR(mut);} //Worked. Will cease to work. Creates warning. - //static assert(!is(typeof(Appender!immARR(mut)))); //Worked. Will cease to work. Uncomment me after full deprecation. - static assert(!is(typeof(Appender!immARR(con)))); //Never worked. Should not work. - auto app5 = Appender!immARR(imm); //Didn't work. Now works. Should not create a warning. - app5.put(7); - assert(equal(app5[], [7])); - - //Deprecated. Please uncomment and make sure this doesn't work: - //char[] cc; - //static assert(!is(typeof(Appender!string(cc)))); - - //This should always work: - auto app6 = appender!string(null); - assert(app6[] == null); - auto app7 = appender!(const(char)[])(null); - assert(app7[] == null); - auto app8 = appender!(char[])(null); - assert(app8[] == null); -} - -@safe pure nothrow unittest //Test large allocations (for GC.extend) -{ - import std.algorithm.comparison : equal; - import std.range; - Appender!(char[]) app; - app.reserve(1); //cover reserve on non-initialized - foreach (_; 0 .. 100_000) - app.put('a'); - assert(equal(app[], 'a'.repeat(100_000))); -} - -@safe pure nothrow unittest -{ - auto reference = new ubyte[](2048 + 1); //a number big enough to have a full page (EG: the GC extends) - auto arr = reference.dup; - auto app = appender(arr[0 .. 0]); - app.reserve(1); //This should not trigger a call to extend - app.put(ubyte(1)); //Don't clobber arr - assert(reference[] == arr[]); -} - -@safe pure nothrow unittest // clear method is supported only for mutable element types -{ - Appender!string app; - app.put("foo"); - static assert(!__traits(compiles, app.clear())); - assert(app[] == "foo"); -} - -@safe pure nothrow unittest -{ - static struct D//dynamic - { - int[] i; - alias i this; - } - static struct S//static - { - int[5] i; - alias i this; - } - static assert(!is(Appender!(char[5]))); - static assert(!is(Appender!D)); - static assert(!is(Appender!S)); - - enum int[5] a = []; - int[5] b; - D d; - S s; - int[5] foo(){return a;} - - static assert(!is(typeof(appender(a)))); - static assert( is(typeof(appender(b)))); - static assert( is(typeof(appender(d)))); - static assert( is(typeof(appender(s)))); - static assert(!is(typeof(appender(foo())))); -} - -@system unittest -{ - // https://issues.dlang.org/show_bug.cgi?id=13077 - static class A {} - - // reduced case - auto w = appender!(shared(A)[])(); - w.put(new shared A()); - - // original case - import std.range; - InputRange!(shared A) foo() - { - return [new shared A].inputRangeObject; - } - auto res = foo.array; - assert(res.length == 1); -} - -/++ - Convenience function that returns a $(LREF RefAppender) instance initialized - with `arrayPtr`. Don't use null for the array pointer, use the other - version of `appender` instead. - +/ -RefAppender!(E[]) appender(P : E[]*, E)(P arrayPtr) -{ - return RefAppender!(E[])(arrayPtr); -} - -/// -@safe pure nothrow -unittest -{ - int[] a = [1, 2]; - auto app2 = appender(&a); - assert(app2[] == [1, 2]); - assert(a == [1, 2]); - app2 ~= 3; - app2 ~= [4, 5, 6]; - assert(app2[] == [1, 2, 3, 4, 5, 6]); - assert(a == [1, 2, 3, 4, 5, 6]); - - app2.reserve(5); - assert(app2.capacity >= 5); -} - -@safe pure nothrow unittest -{ - auto arr = new char[0]; - auto app = appender(&arr); - string b = "abcdefg"; - foreach (char c; b) app.put(c); - assert(app[] == "abcdefg"); - assert(arr == "abcdefg"); -} - -@safe pure nothrow unittest -{ - auto arr = new char[0]; - auto app = appender(&arr); - string b = "abcdefg"; - foreach (char c; b) app ~= c; - assert(app[] == "abcdefg"); - assert(arr == "abcdefg"); -} - -@safe pure nothrow unittest -{ - int[] a = [ 1, 2 ]; - auto app2 = appender(&a); - assert(app2[] == [ 1, 2 ]); - assert(a == [ 1, 2 ]); - app2.put(3); - app2.put([ 4, 5, 6 ][]); - assert(app2[] == [ 1, 2, 3, 4, 5, 6 ]); - assert(a == [ 1, 2, 3, 4, 5, 6 ]); -} - -@safe pure nothrow unittest -{ - import std.exception : assertThrown; - - int[] a = [ 1, 2 ]; - auto app2 = appender(&a); - assert(app2[] == [ 1, 2 ]); - assert(a == [ 1, 2 ]); - app2 ~= 3; - app2 ~= [ 4, 5, 6 ][]; - assert(app2[] == [ 1, 2, 3, 4, 5, 6 ]); - assert(a == [ 1, 2, 3, 4, 5, 6 ]); - - app2.reserve(5); - assert(app2.capacity >= 5); - - try // shrinkTo may throw - { - app2.shrinkTo(3); - } - catch (Exception) assert(0); - assert(app2[] == [ 1, 2, 3 ]); - assertThrown(app2.shrinkTo(5)); - - const app3 = app2; - assert(app3.capacity >= 3); - assert(app3[] == [1, 2, 3]); -} - -// https://issues.dlang.org/show_bug.cgi?id=14605 -@safe pure nothrow unittest -{ - static assert(isOutputRange!(Appender!(int[]), int)); - static assert(isOutputRange!(RefAppender!(int[]), int)); -} - -@safe pure nothrow unittest -{ - Appender!(int[]) app; - short[] range = [1, 2, 3]; - app.put(range); - assert(app[] == [1, 2, 3]); -} - -@safe pure nothrow unittest -{ - string s = "hello".idup; - char[] a = "hello".dup; - auto appS = appender(s); - auto appA = appender(a); - put(appS, 'w'); - put(appA, 'w'); - s ~= 'a'; //Clobbers here? - a ~= 'a'; //Clobbers here? - assert(appS[] == "hellow"); - assert(appA[] == "hellow"); -} - -/++ -Constructs a static array from a dynamic array whose length is known at compile-time. -The element type can be inferred or specified explicitly: - -* $(D [1, 2].staticArray) returns `int[2]` -* $(D [1, 2].staticArray!float) returns `float[2]` - -Note: `staticArray` returns by value, so expressions involving large arrays may be inefficient. - -Params: - a = The input array. - -Returns: A static array constructed from `a`. -+/ -pragma(inline, true) T[n] staticArray(T, size_t n)(auto ref T[n] a) -{ - return a; -} - -/// static array from array literal -nothrow pure @safe @nogc unittest -{ - auto a = [0, 1].staticArray; - static assert(is(typeof(a) == int[2])); - assert(a == [0, 1]); -} - -/// ditto -pragma(inline, true) U[n] staticArray(U, T, size_t n)(auto ref T[n] a) -if (!is(T == U) && is(T : U)) -{ - return a[].staticArray!(U[n]); -} - -/// static array from array with implicit casting of elements -nothrow pure @safe @nogc unittest -{ - auto b = [0, 1].staticArray!long; - static assert(is(typeof(b) == long[2])); - assert(b == [0, 1]); -} - -nothrow pure @safe @nogc unittest -{ - int val = 3; - static immutable gold = [1, 2, 3]; - [1, 2, val].staticArray.checkStaticArray!int([1, 2, 3]); - - @nogc void checkNogc() - { - [1, 2, val].staticArray.checkStaticArray!int(gold); - } - - checkNogc(); - - [1, 2, val].staticArray!double.checkStaticArray!double(gold); - [1, 2, 3].staticArray!int.checkStaticArray!int(gold); - - [1, 2, 3].staticArray!(const(int)).checkStaticArray!(const(int))(gold); - [1, 2, 3].staticArray!(const(double)).checkStaticArray!(const(double))(gold); - { - const(int)[3] a2 = [1, 2, 3].staticArray; - } - - [cast(byte) 1, cast(byte) 129].staticArray.checkStaticArray!byte([1, -127]); -} - -/** -Constructs a static array from a range. -When `a.length` is not known at compile time, the number of elements must be -given as a template argument (e.g. `myrange.staticArray!2`). -Size and type can be combined, if the source range elements are implicitly -convertible to the requested element type (eg: `2.iota.staticArray!(long[2])`). - -When the range `a` is known at compile time, it can be given as a -template argument to avoid having to specify the number of elements -(e.g.: `staticArray!(2.iota)` or `staticArray!(double, 2.iota)`). - -Params: - a = The input range. If there are less elements than the specified length of the static array, - the rest of it is default-initialized. If there are more than specified, the first elements - up to the specified length are used. - rangeLength = Output for the number of elements used from `a`. Optional. -*/ -auto staticArray(size_t n, T)(scope T a) -if (isInputRange!T) -{ - alias U = ElementType!T; - return staticArray!(U[n], U, n)(a); -} - -/// ditto -auto staticArray(size_t n, T)(scope T a, out size_t rangeLength) -if (isInputRange!T) -{ - alias U = ElementType!T; - return staticArray!(U[n], U, n)(a, rangeLength); -} - -/// ditto -auto staticArray(Un : U[n], U, size_t n, T)(scope T a) -if (isInputRange!T && is(ElementType!T : U)) -{ - size_t extraStackSpace; - return staticArray!(Un, U, n)(a, extraStackSpace); -} - -/// ditto -auto staticArray(Un : U[n], U, size_t n, T)(scope T a, out size_t rangeLength) -if (isInputRange!T && is(ElementType!T : U)) -{ - import std.algorithm.mutation : uninitializedFill; - import std.range : take; - import core.internal.lifetime : emplaceRef; - - if (__ctfe) - { - size_t i; - // Compile-time version to avoid unchecked memory access. - Unqual!U[n] ret; - for (auto iter = a.take(n); !iter.empty; iter.popFront()) - { - ret[i] = iter.front; - i++; - } - - rangeLength = i; - return (() @trusted => cast(U[n]) ret)(); - } - - auto ret = (() @trusted - { - Unqual!U[n] theArray = void; - return theArray; - }()); - - size_t i; - if (true) - { - // ret was void-initialized so let's initialize the unfilled part manually. - // also prevents destructors to be called on uninitialized memory if - // an exception is thrown - scope (exit) ret[i .. $].uninitializedFill(U.init); - - for (auto iter = a.take(n); !iter.empty; iter.popFront()) - { - emplaceRef!U(ret[i++], iter.front); - } - } - - rangeLength = i; - return (() @trusted => cast(U[n]) ret)(); -} - -/// static array from range + size -nothrow pure @safe @nogc unittest -{ - import std.range : iota; - - auto input = 3.iota; - auto a = input.staticArray!2; - static assert(is(typeof(a) == int[2])); - assert(a == [0, 1]); - auto b = input.staticArray!(long[4]); - static assert(is(typeof(b) == long[4])); - assert(b == [0, 1, 2, 0]); -} - -// Tests that code compiles when there is an elaborate destructor and exceptions -// are thrown. Unfortunately can't test that memory is initialized -// before having a destructor called on it. -@safe nothrow unittest -{ - // exists only to allow doing something in the destructor. Not tested - // at the end because value appears to depend on implementation of the. - // function. - static int preventersDestroyed = 0; - - static struct CopyPreventer - { - bool on = false; - this(this) - { - if (on) throw new Exception("Thou shalt not copy past me!"); - } - - ~this() - { - preventersDestroyed++; - } - } - auto normalArray = - [ - CopyPreventer(false), - CopyPreventer(false), - CopyPreventer(true), - CopyPreventer(false), - CopyPreventer(true), - ]; - - try - { - auto staticArray = normalArray.staticArray!5; - assert(false); - } - catch (Exception e){} -} - - -nothrow pure @safe @nogc unittest -{ - auto a = [1, 2].staticArray; - assert(is(typeof(a) == int[2]) && a == [1, 2]); - - import std.range : iota; - - 2.iota.staticArray!2.checkStaticArray!int([0, 1]); - 2.iota.staticArray!(double[2]).checkStaticArray!double([0, 1]); - 2.iota.staticArray!(long[2]).checkStaticArray!long([0, 1]); -} - -nothrow pure @safe @nogc unittest -{ - import std.range : iota; - size_t copiedAmount; - 2.iota.staticArray!1(copiedAmount); - assert(copiedAmount == 1); - 2.iota.staticArray!3(copiedAmount); - assert(copiedAmount == 2); -} - -/// ditto -auto staticArray(alias a)() -if (isInputRange!(typeof(a))) -{ - return .staticArray!(size_t(a.length))(a); -} - -/// ditto -auto staticArray(U, alias a)() -if (isInputRange!(typeof(a))) -{ - return .staticArray!(U[size_t(a.length)])(a); -} - -/// static array from CT range -nothrow pure @safe @nogc unittest -{ - import std.range : iota; - - enum a = staticArray!(2.iota); - static assert(is(typeof(a) == int[2])); - assert(a == [0, 1]); - - enum b = staticArray!(long, 2.iota); - static assert(is(typeof(b) == long[2])); - assert(b == [0, 1]); -} - -nothrow pure @safe @nogc unittest -{ - import std.range : iota; - - enum a = staticArray!(2.iota); - staticArray!(2.iota).checkStaticArray!int([0, 1]); - staticArray!(double, 2.iota).checkStaticArray!double([0, 1]); - staticArray!(long, 2.iota).checkStaticArray!long([0, 1]); -} - -version (StdUnittest) private void checkStaticArray(T, T1, T2)(T1 a, T2 b) nothrow @safe pure @nogc -{ - static assert(is(T1 == T[T1.length])); - assert(a == b, "a must be equal to b"); -} diff --git a/phobos/std/ascii.d b/phobos/std/ascii.d deleted file mode 100644 index 3cd723f..0000000 --- a/phobos/std/ascii.d +++ /dev/null @@ -1,805 +0,0 @@ -// Written in the D programming language. - -/++ - Functions which operate on ASCII characters. - - All of the functions in std.ascii accept Unicode characters but - effectively ignore them if they're not ASCII. All `isX` functions return - `false` for non-ASCII characters, and all `toX` functions do nothing - to non-ASCII characters. - - For functions which operate on Unicode characters, see - $(MREF std, uni). - -$(SCRIPT inhibitQuickIndex = 1;) -$(DIVC quickindex, -$(BOOKTABLE, -$(TR $(TH Category) $(TH Functions)) -$(TR $(TD Validation) $(TD - $(LREF isAlpha) - $(LREF isAlphaNum) - $(LREF isASCII) - $(LREF isControl) - $(LREF isDigit) - $(LREF isGraphical) - $(LREF isHexDigit) - $(LREF isOctalDigit) - $(LREF isPrintable) - $(LREF isPunctuation) - $(LREF isUpper) - $(LREF isWhite) -)) -$(TR $(TD Conversions) $(TD - $(LREF toLower) - $(LREF toUpper) -)) -$(TR $(TD Constants) $(TD - $(LREF digits) - $(LREF fullHexDigits) - $(LREF hexDigits) - $(LREF letters) - $(LREF lowercase) - $(LREF lowerHexDigits) - $(LREF newline) - $(LREF octalDigits) - $(LREF uppercase) - $(LREF whitespace) -)) -$(TR $(TD Enums) $(TD - $(LREF ControlChar) - $(LREF LetterCase) -)) -)) - References: - $(LINK2 http://www.digitalmars.com/d/ascii-table.html, ASCII Table), - $(HTTP en.wikipedia.org/wiki/Ascii, Wikipedia) - - License: $(HTTP www.boost.org/LICENSE_1_0.txt, Boost License 1.0). - Authors: $(HTTP digitalmars.com, Walter Bright) and - $(HTTP jmdavisprog.com, Jonathan M Davis) - Source: $(PHOBOSSRC std/ascii.d) - +/ -module std.ascii; - -immutable fullHexDigits = "0123456789ABCDEFabcdef"; /// 0 .. 9A .. Fa .. f -immutable hexDigits = fullHexDigits[0 .. 16]; /// 0 .. 9A .. F -immutable lowerHexDigits = "0123456789abcdef"; /// 0 .. 9a .. f -immutable digits = hexDigits[0 .. 10]; /// 0 .. 9 -immutable octalDigits = digits[0 .. 8]; /// 0 .. 7 -immutable letters = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"; /// A .. Za .. z -immutable uppercase = letters[0 .. 26]; /// A .. Z -immutable lowercase = letters[26 .. 52]; /// a .. z -immutable whitespace = " \t\v\r\n\f"; /// ASCII _whitespace - -/++ - Letter case specifier. - +/ -enum LetterCase : bool -{ - upper, /// Upper case letters - lower /// Lower case letters -} - -/// -@safe unittest -{ - import std.conv : to; - - assert(42.to!string(16, LetterCase.upper) == "2A"); - assert(42.to!string(16, LetterCase.lower) == "2a"); -} - -/// -@safe unittest -{ - import std.digest.hmac : hmac; - import std.digest : toHexString; - import std.digest.sha : SHA1; - import std.string : representation; - - const sha1HMAC = "A very long phrase".representation - .hmac!SHA1("secret".representation) - .toHexString!(LetterCase.lower); - assert(sha1HMAC == "49f2073c7bf58577e8c9ae59fe8cfd37c9ab94e5"); -} - -/++ - All control characters in the ASCII table ($(HTTPS www.asciitable.com, source)). -+/ -enum ControlChar : char -{ - nul = '\x00', /// Null - soh = '\x01', /// Start of heading - stx = '\x02', /// Start of text - etx = '\x03', /// End of text - eot = '\x04', /// End of transmission - enq = '\x05', /// Enquiry - ack = '\x06', /// Acknowledge - bel = '\x07', /// Bell - bs = '\x08', /// Backspace - tab = '\x09', /// Horizontal tab - lf = '\x0A', /// NL line feed, new line - vt = '\x0B', /// Vertical tab - ff = '\x0C', /// NP form feed, new page - cr = '\x0D', /// Carriage return - so = '\x0E', /// Shift out - si = '\x0F', /// Shift in - dle = '\x10', /// Data link escape - dc1 = '\x11', /// Device control 1 - dc2 = '\x12', /// Device control 2 - dc3 = '\x13', /// Device control 3 - dc4 = '\x14', /// Device control 4 - nak = '\x15', /// Negative acknowledge - syn = '\x16', /// Synchronous idle - etb = '\x17', /// End of transmission block - can = '\x18', /// Cancel - em = '\x19', /// End of medium - sub = '\x1A', /// Substitute - esc = '\x1B', /// Escape - fs = '\x1C', /// File separator - gs = '\x1D', /// Group separator - rs = '\x1E', /// Record separator - us = '\x1F', /// Unit separator - del = '\x7F' /// Delete -} - -/// -@safe pure nothrow @nogc unittest -{ - import std.algorithm.comparison, std.algorithm.searching, std.range, std.traits; - - // Because all ASCII characters fit in char, so do these - static assert(ControlChar.ack.sizeof == 1); - - // All control characters except del are in row starting from 0 - static assert(EnumMembers!ControlChar.only.until(ControlChar.del).equal(iota(32))); - - static assert(ControlChar.nul == '\0'); - static assert(ControlChar.bel == '\a'); - static assert(ControlChar.bs == '\b'); - static assert(ControlChar.ff == '\f'); - static assert(ControlChar.lf == '\n'); - static assert(ControlChar.cr == '\r'); - static assert(ControlChar.tab == '\t'); - static assert(ControlChar.vt == '\v'); -} - -/// -@safe pure nothrow unittest -{ - import std.conv; - //Control character table can be used in place of hexcodes. - with (ControlChar) assert(text("Phobos", us, "Deimos", us, "Tango", rs) == "Phobos\x1FDeimos\x1FTango\x1E"); -} - -/// Newline sequence for this system. -version (Windows) - immutable newline = "\r\n"; -else version (Posix) - immutable newline = "\n"; -else version (LDC) // WebAssembly etc. - immutable newline = "\n"; - - -/++ - Params: c = The character to test. - Returns: Whether `c` is a letter or a number (0 .. 9, a .. z, A .. Z). - +/ -bool isAlphaNum(dchar c) @safe pure nothrow @nogc -{ - const hc = c | 0x20; - return ('0' <= c && c <= '9') || ('a' <= hc && hc <= 'z'); -} - -/// -@safe pure nothrow @nogc unittest -{ - assert( isAlphaNum('A')); - assert( isAlphaNum('1')); - assert(!isAlphaNum('#')); - - // N.B.: does not return true for non-ASCII Unicode alphanumerics: - assert(!isAlphaNum('á')); -} - -@safe unittest -{ - import std.range; - foreach (c; chain(digits, octalDigits, fullHexDigits, letters, lowercase, uppercase)) - assert(isAlphaNum(c)); - - foreach (c; whitespace) - assert(!isAlphaNum(c)); -} - - -/++ - Params: c = The character to test. - Returns: Whether `c` is an ASCII letter (A .. Z, a .. z). - +/ -bool isAlpha(dchar c) @safe pure nothrow @nogc -{ - // Optimizer can turn this into a bitmask operation on 64 bit code - return (c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z'); -} - -/// -@safe pure nothrow @nogc unittest -{ - assert( isAlpha('A')); - assert(!isAlpha('1')); - assert(!isAlpha('#')); - - // N.B.: does not return true for non-ASCII Unicode alphabetic characters: - assert(!isAlpha('á')); -} - -@safe unittest -{ - import std.range; - foreach (c; chain(letters, lowercase, uppercase)) - assert(isAlpha(c)); - - foreach (c; chain(digits, octalDigits, whitespace)) - assert(!isAlpha(c)); -} - - -/++ - Params: c = The character to test. - Returns: Whether `c` is a lowercase ASCII letter (a .. z). - +/ -bool isLower(dchar c) @safe pure nothrow @nogc -{ - return c >= 'a' && c <= 'z'; -} - -/// -@safe pure nothrow @nogc unittest -{ - assert( isLower('a')); - assert(!isLower('A')); - assert(!isLower('#')); - - // N.B.: does not return true for non-ASCII Unicode lowercase letters - assert(!isLower('á')); - assert(!isLower('Á')); -} - -@safe unittest -{ - import std.range; - foreach (c; lowercase) - assert(isLower(c)); - - foreach (c; chain(digits, uppercase, whitespace)) - assert(!isLower(c)); -} - - -/++ - Params: c = The character to test. - Returns: Whether `c` is an uppercase ASCII letter (A .. Z). - +/ -bool isUpper(dchar c) @safe pure nothrow @nogc -{ - return c <= 'Z' && 'A' <= c; -} - -/// -@safe pure nothrow @nogc unittest -{ - assert( isUpper('A')); - assert(!isUpper('a')); - assert(!isUpper('#')); - - // N.B.: does not return true for non-ASCII Unicode uppercase letters - assert(!isUpper('á')); - assert(!isUpper('Á')); -} - -@safe unittest -{ - import std.range; - foreach (c; uppercase) - assert(isUpper(c)); - - foreach (c; chain(digits, lowercase, whitespace)) - assert(!isUpper(c)); -} - - -/++ - Params: c = The character to test. - Returns: Whether `c` is a digit (0 .. 9). - +/ -bool isDigit(dchar c) @safe pure nothrow @nogc -{ - return '0' <= c && c <= '9'; -} - -/// -@safe pure nothrow @nogc unittest -{ - assert( isDigit('3')); - assert( isDigit('8')); - assert(!isDigit('B')); - assert(!isDigit('#')); - - // N.B.: does not return true for non-ASCII Unicode numbers - assert(!isDigit('0')); // full-width digit zero (U+FF10) - assert(!isDigit('4')); // full-width digit four (U+FF14) -} - -@safe unittest -{ - import std.range; - foreach (c; digits) - assert(isDigit(c)); - - foreach (c; chain(letters, whitespace)) - assert(!isDigit(c)); -} - - -/++ - Params: c = The character to test. - Returns: Whether `c` is a digit in base 8 (0 .. 7). - +/ -bool isOctalDigit(dchar c) @safe pure nothrow @nogc -{ - return c >= '0' && c <= '7'; -} - -/// -@safe pure nothrow @nogc unittest -{ - assert( isOctalDigit('0')); - assert( isOctalDigit('7')); - assert(!isOctalDigit('8')); - assert(!isOctalDigit('A')); - assert(!isOctalDigit('#')); -} - -@safe unittest -{ - import std.range; - foreach (c; octalDigits) - assert(isOctalDigit(c)); - - foreach (c; chain(letters, ['8', '9'], whitespace)) - assert(!isOctalDigit(c)); -} - - -/++ - Params: c = The character to test. - Returns: Whether `c` is a digit in base 16 (0 .. 9, A .. F, a .. f). - +/ -bool isHexDigit(dchar c) @safe pure nothrow @nogc -{ - const hc = c | 0x20; - return ('0' <= c && c <= '9') || ('a' <= hc && hc <= 'f'); -} - -/// -@safe pure nothrow @nogc unittest -{ - assert( isHexDigit('0')); - assert( isHexDigit('A')); - assert( isHexDigit('f')); // lowercase hex digits are accepted - assert(!isHexDigit('g')); - assert(!isHexDigit('G')); - assert(!isHexDigit('#')); -} - -@safe unittest -{ - import std.range; - foreach (c; fullHexDigits) - assert(isHexDigit(c)); - - foreach (c; chain(lowercase[6 .. $], uppercase[6 .. $], whitespace)) - assert(!isHexDigit(c)); -} - - -/++ - Params: c = The character to test. - Returns: Whether or not `c` is a whitespace character. That includes the - space, tab, vertical tab, form feed, carriage return, and linefeed - characters. - +/ -bool isWhite(dchar c) @safe pure nothrow @nogc -{ - return c == ' ' || (c >= 0x09 && c <= 0x0D); -} - -/// -@safe pure nothrow @nogc unittest -{ - assert( isWhite(' ')); - assert( isWhite('\t')); - assert( isWhite('\n')); - assert(!isWhite('1')); - assert(!isWhite('a')); - assert(!isWhite('#')); - - // N.B.: Does not return true for non-ASCII Unicode whitespace characters. - static import std.uni; - assert(std.uni.isWhite('\u00A0')); - assert(!isWhite('\u00A0')); // std.ascii.isWhite -} - -@safe unittest -{ - import std.range; - foreach (c; whitespace) - assert(isWhite(c)); - - foreach (c; chain(digits, letters)) - assert(!isWhite(c)); -} - - -/++ - Params: c = The character to test. - Returns: Whether `c` is a control character. - +/ -bool isControl(dchar c) @safe pure nothrow @nogc -{ - return c < 0x20 || c == 0x7F; -} - -/// -@safe pure nothrow @nogc unittest -{ - assert( isControl('\0')); - assert( isControl('\022')); - assert( isControl('\n')); // newline is both whitespace and control - assert(!isControl(' ')); - assert(!isControl('1')); - assert(!isControl('a')); - assert(!isControl('#')); - - // N.B.: non-ASCII Unicode control characters are not recognized: - assert(!isControl('\u0080')); - assert(!isControl('\u2028')); - assert(!isControl('\u2029')); -} - -@safe unittest -{ - import std.range; - foreach (dchar c; 0 .. 32) - assert(isControl(c)); - assert(isControl(127)); - - foreach (c; chain(digits, letters, [' '])) - assert(!isControl(c)); -} - - -/++ - Params: c = The character to test. - Returns: Whether or not `c` is a punctuation character. That includes - all ASCII characters which are not control characters, letters, digits, or - whitespace. - +/ -bool isPunctuation(dchar c) @safe pure nothrow @nogc -{ - return c <= '~' && c >= '!' && !isAlphaNum(c); -} - -/// -@safe pure nothrow @nogc unittest -{ - assert( isPunctuation('.')); - assert( isPunctuation(',')); - assert( isPunctuation(':')); - assert( isPunctuation('!')); - assert( isPunctuation('#')); - assert( isPunctuation('~')); - assert( isPunctuation('+')); - assert( isPunctuation('_')); - - assert(!isPunctuation('1')); - assert(!isPunctuation('a')); - assert(!isPunctuation(' ')); - assert(!isPunctuation('\n')); - assert(!isPunctuation('\0')); - - // N.B.: Non-ASCII Unicode punctuation characters are not recognized. - assert(!isPunctuation('\u2012')); // (U+2012 = en-dash) -} - -@safe unittest -{ - foreach (dchar c; 0 .. 128) - { - if (isControl(c) || isAlphaNum(c) || c == ' ') - assert(!isPunctuation(c)); - else - assert(isPunctuation(c)); - } -} - - -/++ - Params: c = The character to test. - Returns: Whether or not `c` is a printable character other than the - space character. - +/ -bool isGraphical(dchar c) @safe pure nothrow @nogc -{ - return '!' <= c && c <= '~'; -} - -/// -@safe pure nothrow @nogc unittest -{ - assert( isGraphical('1')); - assert( isGraphical('a')); - assert( isGraphical('#')); - assert(!isGraphical(' ')); // whitespace is not graphical - assert(!isGraphical('\n')); - assert(!isGraphical('\0')); - - // N.B.: Unicode graphical characters are not regarded as such. - assert(!isGraphical('á')); -} - -@safe unittest -{ - foreach (dchar c; 0 .. 128) - { - if (isControl(c) || c == ' ') - assert(!isGraphical(c)); - else - assert(isGraphical(c)); - } -} - - -/++ - Params: c = The character to test. - Returns: Whether or not `c` is a printable character - including the - space character. - +/ -bool isPrintable(dchar c) @safe pure nothrow @nogc -{ - return c >= ' ' && c <= '~'; -} - -/// -@safe pure nothrow @nogc unittest -{ - assert( isPrintable(' ')); // whitespace is printable - assert( isPrintable('1')); - assert( isPrintable('a')); - assert( isPrintable('#')); - assert(!isPrintable('\0')); // control characters are not printable - - // N.B.: Printable non-ASCII Unicode characters are not recognized. - assert(!isPrintable('á')); -} - -@safe unittest -{ - foreach (dchar c; 0 .. 128) - { - if (isControl(c)) - assert(!isPrintable(c)); - else - assert(isPrintable(c)); - } -} - - -/++ - Params: c = The character to test. - Returns: Whether or not `c` is in the ASCII character set - i.e. in the - range 0 .. 0x7F. - +/ -pragma(inline, true) -bool isASCII(dchar c) @safe pure nothrow @nogc -{ - return c <= 0x7F; -} - -/// -@safe pure nothrow @nogc unittest -{ - assert( isASCII('a')); - assert(!isASCII('á')); -} - -@safe unittest -{ - foreach (dchar c; 0 .. 128) - assert(isASCII(c)); - - assert(!isASCII(128)); -} - - -/++ - Converts an ASCII letter to lowercase. - - Params: c = A character of any type that implicitly converts to `dchar`. - In the case where it's a built-in type, or an enum of a built-in type, - `Unqual!(OriginalType!C)` is returned, whereas if it's a user-defined - type, `dchar` is returned. - - Returns: The corresponding lowercase letter, if `c` is an uppercase - ASCII character, otherwise `c` itself. - +/ -auto toLower(C)(C c) -if (is(C : dchar)) -{ - import std.traits : OriginalType; - - static if (!__traits(isScalar, C)) - alias R = dchar; - else static if (is(immutable OriginalType!C == immutable OC, OC)) - alias R = OC; - - return isUpper(c) ? cast(R)(cast(R) c + 'a' - 'A') : cast(R) c; -} - -/// -@safe pure nothrow @nogc unittest -{ - assert(toLower('a') == 'a'); - assert(toLower('A') == 'a'); - assert(toLower('#') == '#'); - - // N.B.: Non-ASCII Unicode uppercase letters are not converted. - assert(toLower('Á') == 'Á'); -} - -@safe pure nothrow unittest -{ - - import std.meta; - static foreach (C; AliasSeq!(char, wchar, dchar, immutable char, ubyte)) - { - foreach (i, c; uppercase) - assert(toLower(cast(C) c) == lowercase[i]); - - foreach (C c; 0 .. 128) - { - if (c < 'A' || c > 'Z') - assert(toLower(c) == c); - else - assert(toLower(c) != c); - } - - foreach (C c; 128 .. C.max) - assert(toLower(c) == c); - - //CTFE - static assert(toLower(cast(C)'a') == 'a'); - static assert(toLower(cast(C)'A') == 'a'); - } -} - - -/++ - Converts an ASCII letter to uppercase. - - Params: c = Any type which implicitly converts to `dchar`. In the case - where it's a built-in type, or an enum of a built-in type, - `Unqual!(OriginalType!C)` is returned, whereas if it's a user-defined - type, `dchar` is returned. - - Returns: The corresponding uppercase letter, if `c` is a lowercase ASCII - character, otherwise `c` itself. - +/ -auto toUpper(C)(C c) -if (is(C : dchar)) -{ - import std.traits : OriginalType; - - static if (!__traits(isScalar, C)) - alias R = dchar; - else static if (is(immutable OriginalType!C == immutable OC, OC)) - alias R = OC; - - return isLower(c) ? cast(R)(cast(R) c - ('a' - 'A')) : cast(R) c; -} - -/// -@safe pure nothrow @nogc unittest -{ - assert(toUpper('a') == 'A'); - assert(toUpper('A') == 'A'); - assert(toUpper('#') == '#'); - - // N.B.: Non-ASCII Unicode lowercase letters are not converted. - assert(toUpper('á') == 'á'); -} - -@safe pure nothrow unittest -{ - import std.meta; - static foreach (C; AliasSeq!(char, wchar, dchar, immutable char, ubyte)) - { - foreach (i, c; lowercase) - assert(toUpper(cast(C) c) == uppercase[i]); - - foreach (C c; 0 .. 128) - { - if (c < 'a' || c > 'z') - assert(toUpper(c) == c); - else - assert(toUpper(c) != c); - } - - foreach (C c; 128 .. C.max) - assert(toUpper(c) == c); - - //CTFE - static assert(toUpper(cast(C)'a') == 'A'); - static assert(toUpper(cast(C)'A') == 'A'); - } -} - - -@safe unittest //Test both toUpper and toLower with non-builtin -{ - import std.meta; - import std.traits; - - //User Defined [Char|Wchar|Dchar] - static struct UDC { char c; alias c this; } - static struct UDW { wchar c; alias c this; } - static struct UDD { dchar c; alias c this; } - //[Char|Wchar|Dchar] Enum - enum CE : char {a = 'a', A = 'A'} - enum WE : wchar {a = 'a', A = 'A'} - enum DE : dchar {a = 'a', A = 'A'} - //User Defined [Char|Wchar|Dchar] Enum - enum UDCE : UDC {a = UDC('a'), A = UDC('A')} - enum UDWE : UDW {a = UDW('a'), A = UDW('A')} - enum UDDE : UDD {a = UDD('a'), A = UDD('A')} - - //User defined types with implicit cast to dchar test. - static foreach (Char; AliasSeq!(UDC, UDW, UDD)) - { - assert(toLower(Char('a')) == 'a'); - assert(toLower(Char('A')) == 'a'); - static assert(toLower(Char('a')) == 'a'); - static assert(toLower(Char('A')) == 'a'); - static assert(toUpper(Char('a')) == 'A'); - static assert(toUpper(Char('A')) == 'A'); - } - - //Various enum tests. - static foreach (Enum; AliasSeq!(CE, WE, DE, UDCE, UDWE, UDDE)) - { - assert(toLower(Enum.a) == 'a'); - assert(toLower(Enum.A) == 'a'); - assert(toUpper(Enum.a) == 'A'); - assert(toUpper(Enum.A) == 'A'); - static assert(toLower(Enum.a) == 'a'); - static assert(toLower(Enum.A) == 'a'); - static assert(toUpper(Enum.a) == 'A'); - static assert(toUpper(Enum.A) == 'A'); - } - - //Return value type tests for enum of non-UDT. These should be the original type. - static foreach (T; AliasSeq!(CE, WE, DE)) - {{ - alias C = OriginalType!T; - static assert(is(typeof(toLower(T.init)) == C)); - static assert(is(typeof(toUpper(T.init)) == C)); - }} - - //Return value tests for UDT and enum of UDT. These should be dchar - static foreach (T; AliasSeq!(UDC, UDW, UDD, UDCE, UDWE, UDDE)) - { - static assert(is(typeof(toLower(T.init)) == dchar)); - static assert(is(typeof(toUpper(T.init)) == dchar)); - } -} diff --git a/phobos/std/base64.d b/phobos/std/base64.d deleted file mode 100644 index 0fc92ac..0000000 --- a/phobos/std/base64.d +++ /dev/null @@ -1,2189 +0,0 @@ -// Written in the D programming language. - -/** - * Support for Base64 encoding and decoding. - * - * This module provides two default implementations of Base64 encoding, - * $(LREF Base64) with a standard encoding alphabet, and a variant - * $(LREF Base64URL) that has a modified encoding alphabet designed to be - * safe for embedding in URLs and filenames. - * - * Both variants are implemented as instantiations of the template - * $(LREF Base64Impl). Most users will not need to use this template - * directly; however, it can be used to create customized Base64 encodings, - * such as one that omits padding characters, or one that is safe to embed - * inside a regular expression. - * - * Example: - * ----- - * ubyte[] data = [0x14, 0xfb, 0x9c, 0x03, 0xd9, 0x7e]; - * - * const(char)[] encoded = Base64.encode(data); - * assert(encoded == "FPucA9l+"); - * - * ubyte[] decoded = Base64.decode("FPucA9l+"); - * assert(decoded == [0x14, 0xfb, 0x9c, 0x03, 0xd9, 0x7e]); - * ----- - * - * The range API is supported for both encoding and decoding: - * - * Example: - * ----- - * // Create MIME Base64 with CRLF, per line 76. - * File f = File("./text.txt", "r"); - * scope(exit) f.close(); - * - * Appender!string mime64 = appender!string; - * - * foreach (encoded; Base64.encoder(f.byChunk(57))) - * { - * mime64.put(encoded); - * mime64.put("\r\n"); - * } - * - * writeln(mime64.data); - * ----- - * - * References: - * $(LINK2 https://tools.ietf.org/html/rfc4648, RFC 4648 - The Base16, Base32, and Base64 - * Data Encodings) - * - * Copyright: Masahiro Nakagawa 2010-. - * License: $(HTTP boost.org/LICENSE_1_0.txt, Boost License 1.0). - * Authors: Masahiro Nakagawa, Daniel Murphy (Single value Encoder and Decoder) - * Source: $(PHOBOSSRC std/base64.d) - * Macros: - * LREF2=`$2` - */ -module std.base64; - -import std.exception : enforce; -import std.range.primitives : empty, front, isInputRange, isOutputRange, - isForwardRange, ElementType, hasLength, popFront, put, save; -import std.traits : isArray; - -// Make sure module header code examples work correctly. -pure @safe unittest -{ - ubyte[] data = [0x14, 0xfb, 0x9c, 0x03, 0xd9, 0x7e]; - - const(char)[] encoded = Base64.encode(data); - assert(encoded == "FPucA9l+"); - - ubyte[] decoded = Base64.decode("FPucA9l+"); - assert(decoded == [0x14, 0xfb, 0x9c, 0x03, 0xd9, 0x7e]); -} - -/** - * Implementation of standard _Base64 encoding. - * - * See $(LREF Base64Impl) for a description of available methods. - */ -alias Base64 = Base64Impl!('+', '/'); - -/// -pure @safe unittest -{ - ubyte[] data = [0x83, 0xd7, 0x30, 0x7a, 0x01, 0x3f]; - assert(Base64.encode(data) == "g9cwegE/"); - assert(Base64.decode("g9cwegE/") == data); -} - - -/** - * Variation of Base64 encoding that is safe for use in URLs and filenames. - * - * See $(LREF Base64Impl) for a description of available methods. - */ -alias Base64URL = Base64Impl!('-', '_'); - -/// -pure @safe unittest -{ - ubyte[] data = [0x83, 0xd7, 0x30, 0x7a, 0x01, 0x3f]; - assert(Base64URL.encode(data) == "g9cwegE_"); - assert(Base64URL.decode("g9cwegE_") == data); -} - -/** - * Unpadded variation of Base64 encoding that is safe for use in URLs and - * filenames, as used in RFCs 4648 and 7515 (JWS/JWT/JWE). - * - * See $(LREF Base64Impl) for a description of available methods. - */ -alias Base64URLNoPadding = Base64Impl!('-', '_', Base64.NoPadding); - -/// -pure @safe unittest -{ - ubyte[] data = [0x83, 0xd7, 0x30, 0x7b, 0xef]; - assert(Base64URLNoPadding.encode(data) == "g9cwe-8"); - assert(Base64URLNoPadding.decode("g9cwe-8") == data); -} - -/** - * Template for implementing Base64 encoding and decoding. - * - * For most purposes, direct usage of this template is not necessary; instead, - * this module provides default implementations: $(LREF Base64), implementing - * basic Base64 encoding, and $(LREF Base64URL) and $(LREF Base64URLNoPadding), - * that implement the Base64 variant for use in URLs and filenames, with - * and without padding, respectively. - * - * Customized Base64 encoding schemes can be implemented by instantiating this - * template with the appropriate arguments. For example: - * - * ----- - * // Non-standard Base64 format for embedding in regular expressions. - * alias Base64Re = Base64Impl!('!', '=', Base64.NoPadding); - * ----- - * - * NOTE: - * Encoded strings will not have any padding if the `Padding` parameter is - * set to `NoPadding`. - */ -template Base64Impl(char Map62th, char Map63th, char Padding = '=') -{ - enum NoPadding = '\0'; /// represents no-padding encoding - - - // Verify Base64 characters - static assert(Map62th < 'A' || Map62th > 'Z', "Character '" ~ Map62th ~ "' cannot be used twice"); - static assert(Map63th < 'A' || Map63th > 'Z', "Character '" ~ Map63th ~ "' cannot be used twice"); - static assert(Padding < 'A' || Padding > 'Z', "Character '" ~ Padding ~ "' cannot be used twice"); - static assert(Map62th < 'a' || Map62th > 'z', "Character '" ~ Map62th ~ "' cannot be used twice"); - static assert(Map63th < 'a' || Map63th > 'z', "Character '" ~ Map63th ~ "' cannot be used twice"); - static assert(Padding < 'a' || Padding > 'z', "Character '" ~ Padding ~ "' cannot be used twice"); - static assert(Map62th < '0' || Map62th > '9', "Character '" ~ Map62th ~ "' cannot be used twice"); - static assert(Map63th < '0' || Map63th > '9', "Character '" ~ Map63th ~ "' cannot be used twice"); - static assert(Padding < '0' || Padding > '9', "Character '" ~ Padding ~ "' cannot be used twice"); - static assert(Map62th != Map63th, "Character '" ~ Map63th ~ "' cannot be used twice"); - static assert(Map62th != Padding, "Character '" ~ Padding ~ "' cannot be used twice"); - static assert(Map63th != Padding, "Character '" ~ Padding ~ "' cannot be used twice"); - static assert(Map62th != NoPadding, "'\\0' is not a valid Base64character"); - static assert(Map63th != NoPadding, "'\\0' is not a valid Base64character"); - - - /* Encode functions */ - - - private immutable EncodeMap = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789" ~ Map62th ~ Map63th; - - - /** - * Calculates the length needed to store the encoded string corresponding - * to an input of the given length. - * - * Params: - * sourceLength = Length of the source array. - * - * Returns: - * The length of a Base64 encoding of an array of the given length. - */ - @safe @nogc - pure nothrow size_t encodeLength(in size_t sourceLength) - { - static if (Padding == NoPadding) - return (sourceLength / 3) * 4 + (sourceLength % 3 == 0 ? 0 : sourceLength % 3 == 1 ? 2 : 3); - else - return (sourceLength / 3 + (sourceLength % 3 ? 1 : 0)) * 4; - } - - /// - @safe unittest - { - ubyte[] data = [0x1a, 0x2b, 0x3c, 0x4d, 0x5d, 0x6e]; - - // Allocate a buffer large enough to hold the encoded string. - auto buf = new char[Base64.encodeLength(data.length)]; - - Base64.encode(data, buf); - assert(buf == "Gis8TV1u"); - } - - - // ubyte[] to char[] - - - /** - * Encode $(D_PARAM source) into a `char[]` buffer using Base64 - * encoding. - * - * Params: - * source = The $(REF_ALTTEXT input range, isInputRange, std,range,primitives) - * to _encode. - * buffer = The `char[]` buffer to store the encoded result. - * - * Returns: - * The slice of $(D_PARAM buffer) that contains the encoded string. - */ - @trusted - pure char[] encode(R1, R2)(const scope R1 source, return scope R2 buffer) - if (isArray!R1 && is(ElementType!R1 : ubyte) && is(R2 == char[])) - in - { - assert(buffer.length >= encodeLength(source.length), "Insufficient buffer for encoding"); - } - out(result) - { - assert(result.length == encodeLength(source.length), "The length of result is different from Base64"); - } - do - { - immutable srcLen = source.length; - if (srcLen == 0) - return []; - - immutable blocks = srcLen / 3; - immutable remain = srcLen % 3; - auto bufptr = buffer.ptr; - auto srcptr = source.ptr; - - foreach (Unused; 0 .. blocks) - { - immutable val = srcptr[0] << 16 | srcptr[1] << 8 | srcptr[2]; - *bufptr++ = EncodeMap[val >> 18 ]; - *bufptr++ = EncodeMap[val >> 12 & 0x3f]; - *bufptr++ = EncodeMap[val >> 6 & 0x3f]; - *bufptr++ = EncodeMap[val & 0x3f]; - srcptr += 3; - } - - if (remain) - { - immutable val = srcptr[0] << 16 | (remain == 2 ? srcptr[1] << 8 : 0); - *bufptr++ = EncodeMap[val >> 18 ]; - *bufptr++ = EncodeMap[val >> 12 & 0x3f]; - - final switch (remain) - { - case 2: - *bufptr++ = EncodeMap[val >> 6 & 0x3f]; - static if (Padding != NoPadding) - *bufptr++ = Padding; - break; - case 1: - static if (Padding != NoPadding) - { - *bufptr++ = Padding; - *bufptr++ = Padding; - } - break; - } - } - - // encode method can't assume buffer length. So, slice needed. - return buffer[0 .. bufptr - buffer.ptr]; - } - - /// - @nogc nothrow @safe unittest - { - ubyte[6] data = [0x83, 0xd7, 0x30, 0x7a, 0x01, 0x3f]; - char[32] buffer; // much bigger than necessary - - // Just to be sure... - auto encodedLength = Base64.encodeLength(data.length); - assert(buffer.length >= encodedLength); - - // encode() returns a slice to the provided buffer. - auto encoded = Base64.encode(data[], buffer[]); - assert(encoded is buffer[0 .. encodedLength]); - assert(encoded == "g9cwegE/"); - } - - - // InputRange to char[] - - - /** - * ditto - */ - char[] encode(R1, R2)(R1 source, R2 buffer) if (!isArray!R1 && isInputRange!R1 && - is(ElementType!R1 : ubyte) && hasLength!R1 && - is(R2 == char[])) - in - { - assert(buffer.length >= encodeLength(source.length), "Insufficient buffer for encoding"); - } - out(result) - { - // @@@BUG@@@ D's DbC can't caputre an argument of function and store the result of precondition. - //assert(result.length == encodeLength(source.length), "The length of result is different from Base64"); - } - do - { - immutable srcLen = source.length; - if (srcLen == 0) - return []; - - immutable blocks = srcLen / 3; - immutable remain = srcLen % 3; - auto bufptr = buffer.ptr; - - foreach (Unused; 0 .. blocks) - { - immutable v1 = source.front; source.popFront(); - immutable v2 = source.front; source.popFront(); - immutable v3 = source.front; source.popFront(); - immutable val = v1 << 16 | v2 << 8 | v3; - *bufptr++ = EncodeMap[val >> 18 ]; - *bufptr++ = EncodeMap[val >> 12 & 0x3f]; - *bufptr++ = EncodeMap[val >> 6 & 0x3f]; - *bufptr++ = EncodeMap[val & 0x3f]; - } - - if (remain) - { - size_t val = source.front << 16; - if (remain == 2) - { - source.popFront(); - val |= source.front << 8; - } - - *bufptr++ = EncodeMap[val >> 18 ]; - *bufptr++ = EncodeMap[val >> 12 & 0x3f]; - - final switch (remain) - { - case 2: - *bufptr++ = EncodeMap[val >> 6 & 0x3f]; - static if (Padding != NoPadding) - *bufptr++ = Padding; - break; - case 1: - static if (Padding != NoPadding) - { - *bufptr++ = Padding; - *bufptr++ = Padding; - } - break; - } - } - - // @@@BUG@@@ Workaround for DbC problem. See comment on 'out'. - version (StdUnittest) - assert( - bufptr - buffer.ptr == encodeLength(srcLen), - "The length of result is different from Base64" - ); - - // encode method can't assume buffer length. So, slice needed. - return buffer[0 .. bufptr - buffer.ptr]; - } - - - // ubyte[] to OutputRange - - - /** - * Encodes $(D_PARAM source) into an - * $(REF_ALTTEXT output range, isOutputRange, std,range,primitives) using - * Base64 encoding. - * - * Params: - * source = The $(REF_ALTTEXT input range, isInputRange, std,range,primitives) - * to _encode. - * range = The $(REF_ALTTEXT output range, isOutputRange, std,range,primitives) - * to store the encoded result. - * - * Returns: - * The number of times the output range's `put` method was invoked. - */ - size_t encode(E, R)(scope const(E)[] source, auto ref R range) - if (is(E : ubyte) && isOutputRange!(R, char) && !is(R == char[])) - out(result) - { - assert(result == encodeLength(source.length), "The number of put is different from the length of Base64"); - } - do - { - immutable srcLen = source.length; - if (srcLen == 0) - return 0; - - immutable blocks = srcLen / 3; - immutable remain = srcLen % 3; - auto s = source; // copy for out contract length check - size_t pcount; - - foreach (Unused; 0 .. blocks) - { - immutable val = s[0] << 16 | s[1] << 8 | s[2]; - put(range, EncodeMap[val >> 18 ]); - put(range, EncodeMap[val >> 12 & 0x3f]); - put(range, EncodeMap[val >> 6 & 0x3f]); - put(range, EncodeMap[val & 0x3f]); - s = s[3 .. $]; - pcount += 4; - } - - if (remain) - { - immutable val = s[0] << 16 | (remain == 2 ? s[1] << 8 : 0); - put(range, EncodeMap[val >> 18 ]); - put(range, EncodeMap[val >> 12 & 0x3f]); - pcount += 2; - - final switch (remain) - { - case 2: - put(range, EncodeMap[val >> 6 & 0x3f]); - pcount++; - - static if (Padding != NoPadding) - { - put(range, Padding); - pcount++; - } - break; - case 1: - static if (Padding != NoPadding) - { - put(range, Padding); - put(range, Padding); - pcount += 2; - } - break; - } - } - - return pcount; - } - - /// - @safe pure nothrow unittest - { - import std.array : appender; - - auto output = appender!string(); - ubyte[] data = [0x1a, 0x2b, 0x3c, 0x4d, 0x5d, 0x6e]; - - // This overload of encode() returns the number of calls to the output - // range's put method. - assert(Base64.encode(data, output) == 8); - assert(output.data == "Gis8TV1u"); - } - - - // InputRange to OutputRange - - - /** - * ditto - */ - size_t encode(R1, R2)(R1 source, auto ref R2 range) - if (!isArray!R1 && isInputRange!R1 && is(ElementType!R1 : ubyte) && - hasLength!R1 && !is(R2 == char[]) && isOutputRange!(R2, char)) - { - immutable srcLen = source.length; - if (srcLen == 0) - return 0; - - immutable blocks = srcLen / 3; - immutable remain = srcLen % 3; - size_t pcount; - - foreach (Unused; 0 .. blocks) - { - immutable v1 = source.front; source.popFront(); - immutable v2 = source.front; source.popFront(); - immutable v3 = source.front; source.popFront(); - immutable val = v1 << 16 | v2 << 8 | v3; - put(range, EncodeMap[val >> 18 ]); - put(range, EncodeMap[val >> 12 & 0x3f]); - put(range, EncodeMap[val >> 6 & 0x3f]); - put(range, EncodeMap[val & 0x3f]); - pcount += 4; - } - - if (remain) - { - size_t val = source.front << 16; - if (remain == 2) - { - source.popFront(); - val |= source.front << 8; - } - - put(range, EncodeMap[val >> 18 ]); - put(range, EncodeMap[val >> 12 & 0x3f]); - pcount += 2; - - final switch (remain) - { - case 2: - put(range, EncodeMap[val >> 6 & 0x3f]); - pcount++; - - static if (Padding != NoPadding) - { - put(range, Padding); - pcount++; - } - break; - case 1: - static if (Padding != NoPadding) - { - put(range, Padding); - put(range, Padding); - pcount += 2; - } - break; - } - } - - // @@@BUG@@@ Workaround for DbC problem. - version (StdUnittest) - assert( - pcount == encodeLength(srcLen), - "The number of put is different from the length of Base64" - ); - - return pcount; - } - - - /** - * Encodes $(D_PARAM source) to newly-allocated buffer. - * - * This convenience method alleviates the need to manually manage output - * buffers. - * - * Params: - * source = The $(REF_ALTTEXT input range, isInputRange, std,range,primitives) - * to _encode. - * - * Returns: - * A newly-allocated `char[]` buffer containing the encoded string. - */ - @safe - pure char[] encode(Range)(Range source) if (isArray!Range && is(ElementType!Range : ubyte)) - { - return encode(source, new char[encodeLength(source.length)]); - } - - /// - @safe unittest - { - ubyte[] data = [0x1a, 0x2b, 0x3c, 0x4d, 0x5d, 0x6e]; - assert(Base64.encode(data) == "Gis8TV1u"); - } - - - /** - * ditto - */ - char[] encode(Range)(Range source) if (!isArray!Range && isInputRange!Range && - is(ElementType!Range : ubyte) && hasLength!Range) - { - return encode(source, new char[encodeLength(source.length)]); - } - - - /** - * An $(REF_ALTTEXT input range, isInputRange, std,range,primitives) that - * iterates over the respective Base64 encodings of a range of data items. - * - * This range will be a $(REF_ALTTEXT forward range, isForwardRange, std,range,primitives) - * if the underlying data source is at least a forward range. - * - * Note: This struct is not intended to be created in user code directly; - * use the $(LREF encoder) function instead. - */ - struct Encoder(Range) if (isInputRange!Range && (is(ElementType!Range : const(ubyte)[]) || - is(ElementType!Range : const(char)[]))) - { - private: - Range range_; - char[] buffer_, encoded_; - - - public: - this(Range range) - { - range_ = range; - if (!empty) - doEncoding(); - } - - - /** - * Returns: - * true if there is no more encoded data left. - */ - @property @trusted - bool empty() - { - return range_.empty; - } - - - /** - * Returns: The current chunk of encoded data. - */ - @property @safe - nothrow char[] front() - { - return encoded_; - } - - - /** - * Advance the range to the next chunk of encoded data. - * - * Throws: - * `Base64Exception` If invoked when - * $(LREF2 .Base64Impl.Encoder.empty, empty) returns `true`. - */ - void popFront() - { - assert(!empty, "Cannot call popFront on Encoder with no data remaining"); - - range_.popFront(); - - /* - * This check is very ugly. I think this is a Range's flaw. - * I very strongly want the Range guideline for unified implementation. - * - * In this case, Encoder becomes a beautiful implementation if 'front' performs Base64 encoding. - */ - if (!empty) - doEncoding(); - } - - - static if (isForwardRange!Range) - { - /** - * Save the current iteration state of the range. - * - * This method is only available if the underlying range is a - * $(REF_ALTTEXT forward range, isForwardRange, std,range,primitives). - * - * Returns: - * A copy of `this`. - */ - @property - typeof(this) save() - { - typeof(return) encoder; - - encoder.range_ = range_.save; - encoder.buffer_ = buffer_.dup; - encoder.encoded_ = encoder.buffer_[0 .. encoded_.length]; - - return encoder; - } - } - - - private: - void doEncoding() - { - auto data = cast(const(ubyte)[])range_.front; - auto size = encodeLength(data.length); - if (size > buffer_.length) - buffer_.length = size; - - encoded_ = encode(data, buffer_); - } - } - - - /** - * An $(REF_ALTTEXT input range, isInputRange, std,range,primitives) that - * iterates over the encoded bytes of the given source data. - * - * It will be a $(REF_ALTTEXT forward range, isForwardRange, std,range,primitives) - * if the underlying data source is at least a forward range. - * - * Note: This struct is not intended to be created in user code directly; - * use the $(LREF encoder) function instead. - */ - struct Encoder(Range) if (isInputRange!Range && is(ElementType!Range : ubyte)) - { - private: - Range range_; - ubyte first; - int pos, padding; - - - public: - this(Range range) - { - range_ = range; - static if (isForwardRange!Range) - range_ = range_.save; - - if (range_.empty) - pos = -1; - else - popFront(); - } - - - /** - * Returns: - * true if there are no more encoded characters to be iterated. - */ - @property @safe - nothrow bool empty() const - { - static if (Padding == NoPadding) - return pos < 0; - else - return pos < 0 && !padding; - } - - - /** - * Returns: The current encoded character. - */ - @property @safe - nothrow ubyte front() - { - return first; - } - - - /** - * Advance to the next encoded character. - * - * Throws: - * `Base64Exception` If invoked when $(LREF2 .Base64Impl.Encoder.empty.2, - * empty) returns `true`. - */ - void popFront() - { - assert(!empty, "Cannot call popFront on Encoder with no data remaining"); - - static if (Padding != NoPadding) - if (padding) - { - first = Padding; - pos = -1; - padding--; - return; - } - - if (range_.empty) - { - pos = -1; - return; - } - - final switch (pos) - { - case 0: - first = EncodeMap[range_.front >> 2]; - break; - case 1: - immutable t = (range_.front & 0b11) << 4; - range_.popFront(); - - if (range_.empty) - { - first = EncodeMap[t]; - padding = 3; - } - else - { - first = EncodeMap[t | (range_.front >> 4)]; - } - break; - case 2: - immutable t = (range_.front & 0b1111) << 2; - range_.popFront(); - - if (range_.empty) - { - first = EncodeMap[t]; - padding = 2; - } - else - { - first = EncodeMap[t | (range_.front >> 6)]; - } - break; - case 3: - first = EncodeMap[range_.front & 0b111111]; - range_.popFront(); - break; - } - - ++pos %= 4; - } - - - static if (isForwardRange!Range) - { - /** - * Save the current iteration state of the range. - * - * This method is only available if the underlying range is a - * $(REF_ALTTEXT forward range, isForwardRange, std,range,primitives). - * - * Returns: - * A copy of `this`. - */ - @property - typeof(this) save() - { - auto encoder = this; - encoder.range_ = encoder.range_.save; - return encoder; - } - } - } - - - /** - * Construct an `Encoder` that iterates over the Base64 encoding of the - * given $(REF_ALTTEXT input range, isInputRange, std,range,primitives). - * - * Params: - * range = An $(REF_ALTTEXT input range, isInputRange, std,range,primitives) - * over the data to be encoded. - * - * Returns: - * If $(D_PARAM range) is a range of bytes, an `Encoder` that iterates - * over the bytes of the corresponding Base64 encoding. - * - * If $(D_PARAM range) is a range of ranges of bytes, an `Encoder` that - * iterates over the Base64 encoded strings of each element of the range. - * - * In both cases, the returned `Encoder` will be a - * $(REF_ALTTEXT forward range, isForwardRange, std,range,primitives) if the - * given `range` is at least a forward range, otherwise it will be only - * an input range. - * - * Example: - * This example encodes the input one line at a time. - * ----- - * File f = File("text.txt", "r"); - * scope(exit) f.close(); - * - * uint line = 0; - * foreach (encoded; Base64.encoder(f.byLine())) - * { - * writeln(++line, ". ", encoded); - * } - * ----- - * - * Example: - * This example encodes the input data one byte at a time. - * ----- - * ubyte[] data = cast(ubyte[]) "0123456789"; - * - * // The ElementType of data is not aggregation type - * foreach (encoded; Base64.encoder(data)) - * { - * writeln(encoded); - * } - * ----- - */ - Encoder!(Range) encoder(Range)(Range range) if (isInputRange!Range) - { - return typeof(return)(range); - } - - - /* Decode functions */ - - - private immutable int[char.max + 1] DecodeMap = [ - 'A':0b000000, 'B':0b000001, 'C':0b000010, 'D':0b000011, 'E':0b000100, - 'F':0b000101, 'G':0b000110, 'H':0b000111, 'I':0b001000, 'J':0b001001, - 'K':0b001010, 'L':0b001011, 'M':0b001100, 'N':0b001101, 'O':0b001110, - 'P':0b001111, 'Q':0b010000, 'R':0b010001, 'S':0b010010, 'T':0b010011, - 'U':0b010100, 'V':0b010101, 'W':0b010110, 'X':0b010111, 'Y':0b011000, - 'Z':0b011001, 'a':0b011010, 'b':0b011011, 'c':0b011100, 'd':0b011101, - 'e':0b011110, 'f':0b011111, 'g':0b100000, 'h':0b100001, 'i':0b100010, - 'j':0b100011, 'k':0b100100, 'l':0b100101, 'm':0b100110, 'n':0b100111, - 'o':0b101000, 'p':0b101001, 'q':0b101010, 'r':0b101011, 's':0b101100, - 't':0b101101, 'u':0b101110, 'v':0b101111, 'w':0b110000, 'x':0b110001, - 'y':0b110010, 'z':0b110011, '0':0b110100, '1':0b110101, '2':0b110110, - '3':0b110111, '4':0b111000, '5':0b111001, '6':0b111010, '7':0b111011, - '8':0b111100, '9':0b111101, Map62th:0b111110, Map63th:0b111111, Padding:-1 - ]; - - - /** - * Given a Base64 encoded string, calculates the length of the decoded - * string. - * - * Params: - * sourceLength = The length of the Base64 encoding. - * - * Returns: - * The length of the decoded string corresponding to a Base64 encoding of - * length $(D_PARAM sourceLength). - */ - @safe - pure @nogc nothrow size_t decodeLength(in size_t sourceLength) - { - static if (Padding == NoPadding) - return (sourceLength / 4) * 3 + (sourceLength % 4 < 2 ? 0 : sourceLength % 4 == 2 ? 1 : 2); - else - return (sourceLength / 4) * 3; - } - - /// - @safe unittest - { - auto encoded = "Gis8TV1u"; - - // Allocate a sufficiently large buffer to hold to decoded result. - auto buffer = new ubyte[Base64.decodeLength(encoded.length)]; - - Base64.decode(encoded, buffer); - assert(buffer == [0x1a, 0x2b, 0x3c, 0x4d, 0x5d, 0x6e]); - } - - - // Used in decode contracts. Calculates the actual size the decoded - // result should have, taking into account trailing padding. - @safe - pure @nogc nothrow private size_t realDecodeLength(R)(R source) - { - auto expect = decodeLength(source.length); - static if (Padding != NoPadding) - { - if (source.length % 4 == 0) - { - expect -= source.length == 0 ? 0 : - source[$ - 2] == Padding ? 2 : - source[$ - 1] == Padding ? 1 : 0; - } - } - return expect; - } - - - // char[] to ubyte[] - - - /** - * Decodes $(D_PARAM source) into the given buffer. - * - * Params: - * source = The $(REF_ALTTEXT input range, isInputRange, std,range,primitives) - * to _decode. - * buffer = The buffer to store decoded result. - * - * Returns: - * The slice of $(D_PARAM buffer) containing the decoded result. - * - * Throws: - * `Base64Exception` if $(D_PARAM source) contains characters outside the - * base alphabet of the current Base64 encoding scheme. - */ - @trusted - pure ubyte[] decode(R1, R2)(in R1 source, return scope R2 buffer) if (isArray!R1 && is(ElementType!R1 : dchar) && - is(R2 == ubyte[]) && isOutputRange!(R2, ubyte)) - in - { - assert(buffer.length >= realDecodeLength(source), "Insufficient buffer for decoding"); - } - out(result) - { - immutable expect = realDecodeLength(source); - assert(result.length == expect, "The length of result is different from the expected length"); - } - do - { - immutable srcLen = source.length; - if (srcLen == 0) - return []; - static if (Padding != NoPadding) - enforce(srcLen % 4 == 0, new Base64Exception("Invalid length of encoded data")); - - immutable blocks = srcLen / 4; - auto srcptr = source.ptr; - auto bufptr = buffer.ptr; - - foreach (Unused; 0 .. blocks) - { - immutable v1 = decodeChar(*srcptr++); - immutable v2 = decodeChar(*srcptr++); - - *bufptr++ = cast(ubyte)(v1 << 2 | v2 >> 4); - - immutable v3 = decodeChar(*srcptr++); - if (v3 == -1) - break; - - *bufptr++ = cast(ubyte)((v2 << 4 | v3 >> 2) & 0xff); - - immutable v4 = decodeChar(*srcptr++); - if (v4 == -1) - break; - - *bufptr++ = cast(ubyte)((v3 << 6 | v4) & 0xff); - } - - static if (Padding == NoPadding) - { - immutable remain = srcLen % 4; - - if (remain) - { - immutable v1 = decodeChar(*srcptr++); - immutable v2 = decodeChar(*srcptr++); - - *bufptr++ = cast(ubyte)(v1 << 2 | v2 >> 4); - - if (remain == 3) - *bufptr++ = cast(ubyte)((v2 << 4 | decodeChar(*srcptr++) >> 2) & 0xff); - } - } - - return buffer[0 .. bufptr - buffer.ptr]; - } - - /// - @safe unittest - { - auto encoded = "Gis8TV1u"; - ubyte[32] buffer; // much bigger than necessary - - // Just to be sure... - auto decodedLength = Base64.decodeLength(encoded.length); - assert(buffer.length >= decodedLength); - - // decode() returns a slice of the given buffer. - auto decoded = Base64.decode(encoded, buffer[]); - assert(decoded is buffer[0 .. decodedLength]); - assert(decoded == [0x1a, 0x2b, 0x3c, 0x4d, 0x5d, 0x6e]); - } - - // InputRange to ubyte[] - - - /** - * ditto - */ - ubyte[] decode(R1, R2)(R1 source, R2 buffer) if (!isArray!R1 && isInputRange!R1 && - is(ElementType!R1 : dchar) && hasLength!R1 && - is(R2 == ubyte[]) && isOutputRange!(R2, ubyte)) - in - { - assert(buffer.length >= decodeLength(source.length), "Insufficient buffer for decoding"); - } - do - { - immutable srcLen = source.length; - if (srcLen == 0) - return []; - static if (Padding != NoPadding) - enforce(srcLen % 4 == 0, new Base64Exception("Invalid length of encoded data")); - - immutable blocks = srcLen / 4; - auto bufptr = buffer.ptr; - - foreach (Unused; 0 .. blocks) - { - immutable v1 = decodeChar(source.front); source.popFront(); - immutable v2 = decodeChar(source.front); source.popFront(); - - *bufptr++ = cast(ubyte)(v1 << 2 | v2 >> 4); - - immutable v3 = decodeChar(source.front); - if (v3 == -1) - break; - - *bufptr++ = cast(ubyte)((v2 << 4 | v3 >> 2) & 0xff); - source.popFront(); - - immutable v4 = decodeChar(source.front); - if (v4 == -1) - break; - - *bufptr++ = cast(ubyte)((v3 << 6 | v4) & 0xff); - source.popFront(); - } - - static if (Padding == NoPadding) - { - immutable remain = srcLen % 4; - - if (remain) - { - immutable v1 = decodeChar(source.front); source.popFront(); - immutable v2 = decodeChar(source.front); - - *bufptr++ = cast(ubyte)(v1 << 2 | v2 >> 4); - - if (remain == 3) - { - source.popFront(); - *bufptr++ = cast(ubyte)((v2 << 4 | decodeChar(source.front) >> 2) & 0xff); - } - } - } - - // We need to do the check here because we have consumed the length - version (StdUnittest) - assert( - (bufptr - buffer.ptr) >= (decodeLength(srcLen) - 2), - "The length of result is smaller than expected length" - ); - - return buffer[0 .. bufptr - buffer.ptr]; - } - - - // char[] to OutputRange - - - /** - * Decodes $(D_PARAM source) into a given - * $(REF_ALTTEXT output range, isOutputRange, std,range,primitives). - * - * Params: - * source = The $(REF_ALTTEXT input range, isInputRange, std,range,primitives) - * to _decode. - * range = The $(REF_ALTTEXT output range, isOutputRange, std,range,primitives) - * to store the decoded result. - * - * Returns: - * The number of times the output range's `put` method was invoked. - * - * Throws: - * `Base64Exception` if $(D_PARAM source) contains characters outside the - * base alphabet of the current Base64 encoding scheme. - */ - size_t decode(R1, R2)(in R1 source, auto ref R2 range) - if (isArray!R1 && is(ElementType!R1 : dchar) && - !is(R2 == ubyte[]) && isOutputRange!(R2, ubyte)) - out(result) - { - immutable expect = realDecodeLength(source); - assert(result == expect, "The result of decode is different from the expected"); - } - do - { - immutable srcLen = source.length; - if (srcLen == 0) - return 0; - static if (Padding != NoPadding) - enforce(srcLen % 4 == 0, new Base64Exception("Invalid length of encoded data")); - - immutable blocks = srcLen / 4; - auto srcptr = source.ptr; - size_t pcount; - - foreach (Unused; 0 .. blocks) - { - immutable v1 = decodeChar(*srcptr++); - immutable v2 = decodeChar(*srcptr++); - - put(range, cast(ubyte)(v1 << 2 | v2 >> 4)); - pcount++; - - immutable v3 = decodeChar(*srcptr++); - if (v3 == -1) - break; - - put(range, cast(ubyte)((v2 << 4 | v3 >> 2) & 0xff)); - pcount++; - - immutable v4 = decodeChar(*srcptr++); - if (v4 == -1) - break; - - put(range, cast(ubyte)((v3 << 6 | v4) & 0xff)); - pcount++; - } - - static if (Padding == NoPadding) - { - immutable remain = srcLen % 4; - - if (remain) - { - immutable v1 = decodeChar(*srcptr++); - immutable v2 = decodeChar(*srcptr++); - - put(range, cast(ubyte)(v1 << 2 | v2 >> 4)); - pcount++; - - if (remain == 3) - { - put(range, cast(ubyte)((v2 << 4 | decodeChar(*srcptr++) >> 2) & 0xff)); - pcount++; - } - } - } - - return pcount; - } - - /// - @system unittest - { - struct OutputRange - { - ubyte[] result; - void put(ubyte b) { result ~= b; } - } - OutputRange output; - - // This overload of decode() returns the number of calls to put(). - assert(Base64.decode("Gis8TV1u", output) == 6); - assert(output.result == [0x1a, 0x2b, 0x3c, 0x4d, 0x5d, 0x6e]); - } - - - // InputRange to OutputRange - - - /** - * ditto - */ - size_t decode(R1, R2)(R1 source, auto ref R2 range) - if (!isArray!R1 && isInputRange!R1 && is(ElementType!R1 : dchar) && - hasLength!R1 && !is(R2 == ubyte[]) && isOutputRange!(R2, ubyte)) - out(result) - { - // @@@BUG@@@ Workaround for DbC problem. - //immutable expect = decodeLength(source.length) - 2; - //assert(result >= expect, "The length of result is smaller than expected length"); - } - do - { - immutable srcLen = source.length; - if (srcLen == 0) - return 0; - static if (Padding != NoPadding) - enforce(srcLen % 4 == 0, new Base64Exception("Invalid length of encoded data")); - - immutable blocks = srcLen / 4; - size_t pcount; - - foreach (Unused; 0 .. blocks) - { - immutable v1 = decodeChar(source.front); source.popFront(); - immutable v2 = decodeChar(source.front); source.popFront(); - - put(range, cast(ubyte)(v1 << 2 | v2 >> 4)); - pcount++; - - immutable v3 = decodeChar(source.front); - if (v3 == -1) - break; - - put(range, cast(ubyte)((v2 << 4 | v3 >> 2) & 0xff)); - source.popFront(); - pcount++; - - immutable v4 = decodeChar(source.front); - if (v4 == -1) - break; - - put(range, cast(ubyte)((v3 << 6 | v4) & 0xff)); - source.popFront(); - pcount++; - } - - static if (Padding == NoPadding) - { - immutable remain = srcLen % 4; - - if (remain) - { - immutable v1 = decodeChar(source.front); source.popFront(); - immutable v2 = decodeChar(source.front); - - put(range, cast(ubyte)(v1 << 2 | v2 >> 4)); - pcount++; - - if (remain == 3) - { - source.popFront(); - put(range, cast(ubyte)((v2 << 4 | decodeChar(source.front) >> 2) & 0xff)); - pcount++; - } - } - } - - // @@@BUG@@@ Workaround for DbC problem. - version (StdUnittest) - assert( - pcount >= (decodeLength(srcLen) - 2), - "The length of result is smaller than expected length" - ); - - return pcount; - } - - - /** - * Decodes $(D_PARAM source) into newly-allocated buffer. - * - * This convenience method alleviates the need to manually manage decoding - * buffers. - * - * Params: - * source = The $(REF_ALTTEXT input range, isInputRange, std,range,primitives) - * to _decode. - * - * Returns: - * A newly-allocated `ubyte[]` buffer containing the decoded string. - */ - @safe - pure ubyte[] decode(Range)(Range source) if (isArray!Range && is(ElementType!Range : dchar)) - { - return decode(source, new ubyte[decodeLength(source.length)]); - } - - /// - @safe unittest - { - auto data = "Gis8TV1u"; - assert(Base64.decode(data) == [0x1a, 0x2b, 0x3c, 0x4d, 0x5d, 0x6e]); - } - - - /** - * ditto - */ - ubyte[] decode(Range)(Range source) if (!isArray!Range && isInputRange!Range && - is(ElementType!Range : dchar) && hasLength!Range) - { - return decode(source, new ubyte[decodeLength(source.length)]); - } - - - /** - * An $(REF_ALTTEXT input range, isInputRange, std,range,primitives) that - * iterates over the decoded data of a range of Base64 encodings. - * - * This range will be a $(REF_ALTTEXT forward range, isForwardRange, std,range,primitives) - * if the underlying data source is at least a forward range. - * - * Note: This struct is not intended to be created in user code directly; - * use the $(LREF decoder) function instead. - */ - struct Decoder(Range) if (isInputRange!Range && (is(ElementType!Range : const(char)[]) || - is(ElementType!Range : const(ubyte)[]))) - { - private: - Range range_; - ubyte[] buffer_, decoded_; - - - public: - this(Range range) - { - range_ = range; - if (!empty) - doDecoding(); - } - - - /** - * Returns: - * true if there are no more elements to be iterated. - */ - @property @trusted - bool empty() - { - return range_.empty; - } - - - /** - * Returns: The decoding of the current element in the input. - */ - @property @safe - nothrow ubyte[] front() - { - return decoded_; - } - - - /** - * Advance to the next element in the input to be decoded. - * - * Throws: - * `Base64Exception` if invoked when $(LREF2 .Base64Impl.Decoder.empty, - * empty) returns `true`. - */ - void popFront() - { - assert(!empty, "Cannot call popFront on Decoder with no data remaining."); - - range_.popFront(); - - /* - * I mentioned Encoder's popFront. - */ - if (!empty) - doDecoding(); - } - - - static if (isForwardRange!Range) - { - /** - * Saves the current iteration state. - * - * This method is only available if the underlying range is a - * $(REF_ALTTEXT forward range, isForwardRange, std,range,primitives) - * - * Returns: A copy of `this`. - */ - @property - typeof(this) save() - { - typeof(return) decoder; - - decoder.range_ = range_.save; - decoder.buffer_ = buffer_.dup; - decoder.decoded_ = decoder.buffer_[0 .. decoded_.length]; - - return decoder; - } - } - - - private: - void doDecoding() - { - auto data = cast(const(char)[])range_.front; - - static if (Padding == NoPadding) - { - while (data.length % 4 == 1) - { - range_.popFront(); - data ~= cast(const(char)[])range_.front; - } - } - else - { - while (data.length % 4 != 0) - { - range_.popFront(); - enforce(!range_.empty, new Base64Exception("Invalid length of encoded data")); - data ~= cast(const(char)[])range_.front; - } - } - - auto size = decodeLength(data.length); - if (size > buffer_.length) - buffer_.length = size; - - decoded_ = decode(data, buffer_); - } - } - - - /** - * An $(REF_ALTTEXT input range, isInputRange, std,range,primitives) that - * iterates over the bytes of data decoded from a Base64 encoded string. - * - * This range will be a $(REF_ALTTEXT forward range, isForwardRange, std,range,primitives) - * if the underlying data source is at least a forward range. - * - * Note: This struct is not intended to be created in user code directly; - * use the $(LREF decoder) function instead. - */ - struct Decoder(Range) if (isInputRange!Range && is(ElementType!Range : char)) - { - private: - Range range_; - ubyte first; - int pos; - - - public: - this(Range range) - { - range_ = range; - static if (isForwardRange!Range) - range_ = range_.save; - - static if (Padding != NoPadding && hasLength!Range) - enforce(range_.length % 4 == 0, new Base64Exception("Invalid length of encoded data")); - - if (range_.empty) - pos = -1; - else - popFront(); - } - - - /** - * Returns: - * true if there are no more elements to be iterated. - */ - @property @safe - nothrow bool empty() const - { - return pos < 0; - } - - - /** - * Returns: The current decoded byte. - */ - @property @safe - nothrow ubyte front() - { - return first; - } - - - /** - * Advance to the next decoded byte. - * - * Throws: - * `Base64Exception` if invoked when $(LREF2 .Base64Impl.Decoder.empty, - * empty) returns `true`. - */ - void popFront() - { - enforce(!empty, new Base64Exception("Cannot call popFront on Decoder with no data remaining")); - - static if (Padding == NoPadding) - { - bool endCondition() - { - return range_.empty; - } - } - else - { - bool endCondition() - { - enforce(!range_.empty, new Base64Exception("Missing padding")); - return range_.front == Padding; - } - } - - if (range_.empty || range_.front == Padding) - { - pos = -1; - return; - } - - final switch (pos) - { - case 0: - enforce(!endCondition(), new Base64Exception("Premature end of data found")); - - immutable t = DecodeMap[range_.front] << 2; - range_.popFront(); - - enforce(!endCondition(), new Base64Exception("Premature end of data found")); - first = cast(ubyte)(t | (DecodeMap[range_.front] >> 4)); - break; - case 1: - immutable t = (DecodeMap[range_.front] & 0b1111) << 4; - range_.popFront(); - - if (endCondition()) - { - pos = -1; - return; - } - else - { - first = cast(ubyte)(t | (DecodeMap[range_.front] >> 2)); - } - break; - case 2: - immutable t = (DecodeMap[range_.front] & 0b11) << 6; - range_.popFront(); - - if (endCondition()) - { - pos = -1; - return; - } - else - { - first = cast(ubyte)(t | DecodeMap[range_.front]); - } - - range_.popFront(); - break; - } - - ++pos %= 3; - } - - - static if (isForwardRange!Range) - { - /** - * Saves the current iteration state. - * - * This method is only available if the underlying range is a - * $(REF_ALTTEXT forward range, isForwardRange, std,range,primitives) - * - * Returns: A copy of `this`. - */ - @property - typeof(this) save() - { - auto decoder = this; - decoder.range_ = decoder.range_.save; - return decoder; - } - } - } - - - /** - * Construct a `Decoder` that iterates over the decoding of the given - * Base64 encoded data. - * - * Params: - * range = An $(REF_ALTTEXT input range, isInputRange, std,range,primitives) - * over the data to be decoded, or a `char` array. Will not accept - * `wchar[]` nor `dchar[]`. - * - * Returns: - * If $(D_PARAM range) is a range or array of `char`, a `Decoder` that - * iterates over the bytes of the corresponding Base64 decoding. - * - * If $(D_PARAM range) is a range of ranges of characters, a `Decoder` - * that iterates over the decoded strings corresponding to each element of - * the range. - * - * In both cases, the returned `Decoder` will be a - * $(REF_ALTTEXT forward range, isForwardRange, std,range,primitives) if the - * given `range` is at least a forward range, otherwise it will be only - * an input range. - * - * If the input data contains characters not found in the base alphabet of - * the current Base64 encoding scheme, the returned range may throw a - * `Base64Exception`. - * - * Example: - * This example shows decoding over a range of input data lines. - * ----- - * foreach (decoded; Base64.decoder(stdin.byLine())) - * { - * writeln(decoded); - * } - * ----- - * - * This example shows decoding one byte at a time. - * ----- - * auto encoded = Base64.encoder(cast(ubyte[])"0123456789"); - * foreach (n; map!q{a - '0'}(Base64.decoder(encoded))) - * { - * writeln(n); - * } - * ----- - */ - Decoder!(Range) decoder(Range)(Range range) if (isInputRange!Range) - { - return typeof(return)(range); - } - - /// ditto - Decoder!(const(ubyte)[]) decoder()(const(char)[] range) - { - import std.string : representation; - return typeof(return)(range.representation); - } - - /// - @safe pure unittest - { - import std.algorithm.comparison : equal; - string encoded = - "VGhvdSBzaGFsdCBuZXZlciBjb250aW51ZSBhZnRlciBhc3NlcnRpbmcgbnVsbA=="; - - assert(Base64.decoder(encoded) - .equal("Thou shalt never continue after asserting null")); - } - - - private: - @safe - pure int decodeChar()(char chr) - { - immutable val = DecodeMap[chr]; - - // enforce can't be a pure function, so I use trivial check. - if (val == 0 && chr != 'A') - throw new Base64Exception("Invalid character: " ~ chr); - - return val; - } - - - @safe - pure int decodeChar()(dchar chr) - { - // See above comment. - if (chr > 0x7f) - throw new Base64Exception("Base64-encoded character must be a single byte"); - - return decodeChar(cast(char) chr); - } -} - -/// -@safe unittest -{ - import std.string : representation; - - // pre-defined: alias Base64 = Base64Impl!('+', '/'); - ubyte[] emptyArr; - assert(Base64.encode(emptyArr) == ""); - assert(Base64.encode("f".representation) == "Zg=="); - assert(Base64.encode("foo".representation) == "Zm9v"); - - alias Base64Re = Base64Impl!('!', '=', Base64.NoPadding); - assert(Base64Re.encode("f".representation) == "Zg"); - assert(Base64Re.encode("foo".representation) == "Zm9v"); -} - -/** - * Exception thrown upon encountering Base64 encoding or decoding errors. - */ -class Base64Exception : Exception -{ - @safe pure nothrow - this(string s, string fn = __FILE__, size_t ln = __LINE__) - { - super(s, fn, ln); - } -} - -/// -@safe unittest -{ - import std.exception : assertThrown; - assertThrown!Base64Exception(Base64.decode("ab|c")); -} - -@system unittest -{ - import std.algorithm.comparison : equal; - import std.algorithm.sorting : sort; - import std.conv; - import std.exception : assertThrown; - import std.file; - import std.stdio; - - alias Base64Re = Base64Impl!('!', '=', Base64.NoPadding); - - // Test vectors from RFC 4648 - ubyte[][string] tv = [ - "" :cast(ubyte[])"", - "f" :cast(ubyte[])"f", - "fo" :cast(ubyte[])"fo", - "foo" :cast(ubyte[])"foo", - "foob" :cast(ubyte[])"foob", - "fooba" :cast(ubyte[])"fooba", - "foobar":cast(ubyte[])"foobar" - ]; - - { // Base64 - // encode - assert(Base64.encodeLength(tv[""].length) == 0); - assert(Base64.encodeLength(tv["f"].length) == 4); - assert(Base64.encodeLength(tv["fo"].length) == 4); - assert(Base64.encodeLength(tv["foo"].length) == 4); - assert(Base64.encodeLength(tv["foob"].length) == 8); - assert(Base64.encodeLength(tv["fooba"].length) == 8); - assert(Base64.encodeLength(tv["foobar"].length) == 8); - - assert(Base64.encode(tv[""]) == ""); - assert(Base64.encode(tv["f"]) == "Zg=="); - assert(Base64.encode(tv["fo"]) == "Zm8="); - assert(Base64.encode(tv["foo"]) == "Zm9v"); - assert(Base64.encode(tv["foob"]) == "Zm9vYg=="); - assert(Base64.encode(tv["fooba"]) == "Zm9vYmE="); - assert(Base64.encode(tv["foobar"]) == "Zm9vYmFy"); - - // decode - assert(Base64.decodeLength(Base64.encode(tv[""]).length) == 0); - assert(Base64.decodeLength(Base64.encode(tv["f"]).length) == 3); - assert(Base64.decodeLength(Base64.encode(tv["fo"]).length) == 3); - assert(Base64.decodeLength(Base64.encode(tv["foo"]).length) == 3); - assert(Base64.decodeLength(Base64.encode(tv["foob"]).length) == 6); - assert(Base64.decodeLength(Base64.encode(tv["fooba"]).length) == 6); - assert(Base64.decodeLength(Base64.encode(tv["foobar"]).length) == 6); - - assert(Base64.decode(Base64.encode(tv[""])) == tv[""]); - assert(Base64.decode(Base64.encode(tv["f"])) == tv["f"]); - assert(Base64.decode(Base64.encode(tv["fo"])) == tv["fo"]); - assert(Base64.decode(Base64.encode(tv["foo"])) == tv["foo"]); - assert(Base64.decode(Base64.encode(tv["foob"])) == tv["foob"]); - assert(Base64.decode(Base64.encode(tv["fooba"])) == tv["fooba"]); - assert(Base64.decode(Base64.encode(tv["foobar"])) == tv["foobar"]); - - assertThrown!Base64Exception(Base64.decode("ab|c")); - - // Test decoding incomplete strings. RFC does not specify the correct - // behavior, but the code should never throw Errors on invalid input. - - // decodeLength is nothrow - assert(Base64.decodeLength(1) == 0); - assert(Base64.decodeLength(2) <= 1); - assert(Base64.decodeLength(3) <= 2); - - // may throw Exceptions, may not throw Errors - assertThrown!Base64Exception(Base64.decode("Zg")); - assertThrown!Base64Exception(Base64.decode("Zg=")); - assertThrown!Base64Exception(Base64.decode("Zm8")); - assertThrown!Base64Exception(Base64.decode("Zg==;")); - } - - { // No padding - // encode - assert(Base64Re.encodeLength(tv[""].length) == 0); - assert(Base64Re.encodeLength(tv["f"].length) == 2); - assert(Base64Re.encodeLength(tv["fo"].length) == 3); - assert(Base64Re.encodeLength(tv["foo"].length) == 4); - assert(Base64Re.encodeLength(tv["foob"].length) == 6); - assert(Base64Re.encodeLength(tv["fooba"].length) == 7); - assert(Base64Re.encodeLength(tv["foobar"].length) == 8); - - assert(Base64Re.encode(tv[""]) == ""); - assert(Base64Re.encode(tv["f"]) == "Zg"); - assert(Base64Re.encode(tv["fo"]) == "Zm8"); - assert(Base64Re.encode(tv["foo"]) == "Zm9v"); - assert(Base64Re.encode(tv["foob"]) == "Zm9vYg"); - assert(Base64Re.encode(tv["fooba"]) == "Zm9vYmE"); - assert(Base64Re.encode(tv["foobar"]) == "Zm9vYmFy"); - - // decode - assert(Base64Re.decodeLength(Base64Re.encode(tv[""]).length) == 0); - assert(Base64Re.decodeLength(Base64Re.encode(tv["f"]).length) == 1); - assert(Base64Re.decodeLength(Base64Re.encode(tv["fo"]).length) == 2); - assert(Base64Re.decodeLength(Base64Re.encode(tv["foo"]).length) == 3); - assert(Base64Re.decodeLength(Base64Re.encode(tv["foob"]).length) == 4); - assert(Base64Re.decodeLength(Base64Re.encode(tv["fooba"]).length) == 5); - assert(Base64Re.decodeLength(Base64Re.encode(tv["foobar"]).length) == 6); - - assert(Base64Re.decode(Base64Re.encode(tv[""])) == tv[""]); - assert(Base64Re.decode(Base64Re.encode(tv["f"])) == tv["f"]); - assert(Base64Re.decode(Base64Re.encode(tv["fo"])) == tv["fo"]); - assert(Base64Re.decode(Base64Re.encode(tv["foo"])) == tv["foo"]); - assert(Base64Re.decode(Base64Re.encode(tv["foob"])) == tv["foob"]); - assert(Base64Re.decode(Base64Re.encode(tv["fooba"])) == tv["fooba"]); - assert(Base64Re.decode(Base64Re.encode(tv["foobar"])) == tv["foobar"]); - - // decodeLength is nothrow - assert(Base64.decodeLength(1) == 0); - } - - { // with OutputRange - import std.array; - - auto a = Appender!(char[])([]); - auto b = Appender!(ubyte[])([]); - - assert(Base64.encode(tv[""], a) == 0); - assert(Base64.decode(a.data, b) == 0); - assert(tv[""] == b.data); a.clear(); b.clear(); - - assert(Base64.encode(tv["f"], a) == 4); - assert(Base64.decode(a.data, b) == 1); - assert(tv["f"] == b.data); a.clear(); b.clear(); - - assert(Base64.encode(tv["fo"], a) == 4); - assert(Base64.decode(a.data, b) == 2); - assert(tv["fo"] == b.data); a.clear(); b.clear(); - - assert(Base64.encode(tv["foo"], a) == 4); - assert(Base64.decode(a.data, b) == 3); - assert(tv["foo"] == b.data); a.clear(); b.clear(); - - assert(Base64.encode(tv["foob"], a) == 8); - assert(Base64.decode(a.data, b) == 4); - assert(tv["foob"] == b.data); a.clear(); b.clear(); - - assert(Base64.encode(tv["fooba"], a) == 8); - assert(Base64.decode(a.data, b) == 5); - assert(tv["fooba"] == b.data); a.clear(); b.clear(); - - assert(Base64.encode(tv["foobar"], a) == 8); - assert(Base64.decode(a.data, b) == 6); - assert(tv["foobar"] == b.data); a.clear(); b.clear(); - } - - // https://issues.dlang.org/show_bug.cgi?id=9543 - // These tests were disabled because they actually relied on the input range having length. - // The implementation (currently) doesn't support encoding/decoding from a length-less source. - version (none) - { // with InputRange - // InputRange to ubyte[] or char[] - auto encoded = Base64.encode(map!(to!(ubyte))(["20", "251", "156", "3", "217", "126"])); - assert(encoded == "FPucA9l+"); - assert(Base64.decode(map!q{a}(encoded)) == [0x14, 0xfb, 0x9c, 0x03, 0xd9, 0x7e]); - - // InputRange to OutputRange - auto a = Appender!(char[])([]); - auto b = Appender!(ubyte[])([]); - assert(Base64.encode(map!(to!(ubyte))(["20", "251", "156", "3", "217", "126"]), a) == 8); - assert(a.data == "FPucA9l+"); - assert(Base64.decode(map!q{a}(a.data), b) == 6); - assert(b.data == [0x14, 0xfb, 0x9c, 0x03, 0xd9, 0x7e]); - } - - { // Encoder and Decoder - { - string encode_file = std.file.deleteme ~ "-testingEncoder"; - std.file.write(encode_file, "\nf\nfo\nfoo\nfoob\nfooba\nfoobar"); - - auto witness = ["", "Zg==", "Zm8=", "Zm9v", "Zm9vYg==", "Zm9vYmE=", "Zm9vYmFy"]; - auto f = File(encode_file); - scope(exit) - { - f.close(); - assert(!f.isOpen); - std.file.remove(encode_file); - } - - size_t i; - foreach (encoded; Base64.encoder(f.byLine())) - assert(encoded == witness[i++]); - - assert(i == witness.length); - } - - { - string decode_file = std.file.deleteme ~ "-testingDecoder"; - std.file.write(decode_file, "\nZg==\nZm8=\nZm9v\nZm9vYg==\nZm9vYmE=\nZm9vYmFy"); - - auto witness = sort(tv.keys); - auto f = File(decode_file); - scope(exit) - { - f.close(); - assert(!f.isOpen); - std.file.remove(decode_file); - } - - size_t i; - foreach (decoded; Base64.decoder(f.byLine())) - assert(decoded == witness[i++]); - - assert(i == witness.length); - } - - { // ForwardRange - { - auto encoder = Base64.encoder(sort(tv.values)); - auto witness = ["", "Zg==", "Zm8=", "Zm9v", "Zm9vYg==", "Zm9vYmE=", "Zm9vYmFy"]; - size_t i; - - assert(encoder.front == witness[i++]); encoder.popFront(); - assert(encoder.front == witness[i++]); encoder.popFront(); - assert(encoder.front == witness[i++]); encoder.popFront(); - - foreach (encoded; encoder.save) - assert(encoded == witness[i++]); - } - - { - auto decoder = Base64.decoder(["", "Zg==", "Zm8=", "Zm9v", "Zm9vYg==", "Zm9vYmE=", "Zm9vYmFy"]); - auto witness = sort(tv.values); - size_t i; - - assert(decoder.front == witness[i++]); decoder.popFront(); - assert(decoder.front == witness[i++]); decoder.popFront(); - assert(decoder.front == witness[i++]); decoder.popFront(); - - foreach (decoded; decoder.save) - assert(decoded == witness[i++]); - } - } - } - - { // Encoder and Decoder for single character encoding and decoding - alias Base64NoPadding = Base64Impl!('+', '/', Base64.NoPadding); - - auto tests = [ - "" : ["", "", "", ""], - "f" : ["Zg==", "Zg==", "Zg", "Zg"], - "fo" : ["Zm8=", "Zm8=", "Zm8", "Zm8"], - "foo" : ["Zm9v", "Zm9v", "Zm9v", "Zm9v"], - "foob" : ["Zm9vYg==", "Zm9vYg==", "Zm9vYg", "Zm9vYg"], - "fooba" : ["Zm9vYmE=", "Zm9vYmE=", "Zm9vYmE", "Zm9vYmE"], - "foobar" : ["Zm9vYmFy", "Zm9vYmFy", "Zm9vYmFy", "Zm9vYmFy"], - ]; - - foreach (u, e; tests) - { - assert(equal(Base64.encoder(cast(ubyte[]) u), e[0])); - assert(equal(Base64.decoder(Base64.encoder(cast(ubyte[]) u)), u)); - - assert(equal(Base64URL.encoder(cast(ubyte[]) u), e[1])); - assert(equal(Base64URL.decoder(Base64URL.encoder(cast(ubyte[]) u)), u)); - - assert(equal(Base64NoPadding.encoder(cast(ubyte[]) u), e[2])); - assert(equal(Base64NoPadding.decoder(Base64NoPadding.encoder(cast(ubyte[]) u)), u)); - - assert(equal(Base64Re.encoder(cast(ubyte[]) u), e[3])); - assert(equal(Base64Re.decoder(Base64Re.encoder(cast(ubyte[]) u)), u)); - } - } -} - -// Regression control for the output range ref bug in encode. -@safe unittest -{ - struct InputRange - { - ubyte[] impl = [0x1a, 0x2b, 0x3c, 0x4d, 0x5d, 0x6e]; - @property bool empty() { return impl.length == 0; } - @property ubyte front() { return impl[0]; } - void popFront() { impl = impl[1 .. $]; } - @property size_t length() { return impl.length; } - } - - struct OutputRange - { - char[] result; - void put(char b) { result ~= b; } - } - - InputRange ir; - OutputRange or; - assert(Base64.encode(ir, or) == 8); - assert(or.result == "Gis8TV1u"); - - // Verify that any existing workaround that uses & still works. - InputRange ir2; - OutputRange or2; - () @trusted { - assert(Base64.encode(ir2, &or2) == 8); - }(); - assert(or2.result == "Gis8TV1u"); -} - -// Regression control for the output range ref bug in decode. -@safe unittest -{ - struct InputRange - { - const(char)[] impl = "Gis8TV1u"; - @property bool empty() { return impl.length == 0; } - @property dchar front() { return impl[0]; } - void popFront() { impl = impl[1 .. $]; } - @property size_t length() { return impl.length; } - } - - struct OutputRange - { - ubyte[] result; - void put(ubyte b) { result ~= b; } - } - - InputRange ir; - OutputRange or; - assert(Base64.decode(ir, or) == 6); - assert(or.result == [0x1a, 0x2b, 0x3c, 0x4d, 0x5d, 0x6e]); - - // Verify that any existing workaround that uses & still works. - InputRange ir2; - OutputRange or2; - () @trusted { - assert(Base64.decode(ir2, &or2) == 6); - }(); - assert(or2.result == [0x1a, 0x2b, 0x3c, 0x4d, 0x5d, 0x6e]); -} - -// https://issues.dlang.org/show_bug.cgi?id=21679 -// https://issues.dlang.org/show_bug.cgi?id=21706 -@safe unittest -{ - ubyte[][] input; - assert(Base64.encoder(input).empty); - assert(Base64.decoder(input).empty); -} - -@safe unittest -{ - struct InputRange(ubyte[] data) - { - ubyte[] impl = data; - bool empty() { return impl.length == 0; } - ubyte front() { return impl[0]; } - void popFront() { impl = impl[1 .. $]; } - size_t length() { return impl.length; } - } - - struct OutputRange - { - ubyte[] result; - void put(ubyte b) { result ~= b; } - } - - void test_encode(ubyte[] data, string result)() - { - InputRange!data ir; - OutputRange or; - assert(Base64.encode(ir, or) == result.length); - assert(or.result == result); - } - - void test_decode(ubyte[] data, string result)() - { - InputRange!data ir; - OutputRange or; - assert(Base64.decode(ir, or) == result.length); - assert(or.result == result); - } - - test_encode!([], ""); - test_encode!(['x'], "eA=="); - test_encode!([123, 45], "ey0="); - - test_decode!([], ""); - test_decode!(['e', 'A', '=', '='], "x"); - test_decode!(['e', 'y', '0', '='], "{-"); -} - -@system unittest -{ - // checking forward range - auto item = Base64.decoder(Base64.encoder(cast(ubyte[]) "foobar")); - auto copy = item.save(); - item.popFront(); - assert(item.front == 'o'); - assert(copy.front == 'f'); -} - -@system unittest -{ - // checking invalid dchar - dchar[] c = cast(dchar[]) "ääää"; - - import std.exception : assertThrown; - assertThrown!Base64Exception(Base64.decode(c)); -} - -@safe unittest -{ - import std.array : array; - - char[][] input = [['e', 'y'], ['0', '=']]; - assert(Base64.decoder(input).array == [[123, 45]]); -} - -// https://issues.dlang.org/show_bug.cgi?id=21707 -@safe unittest -{ - import std.exception : assertThrown; - - char[][] t1 = [[ 'Z', 'g', '=' ]]; - assertThrown!Base64Exception(Base64.decoder(t1)); - - char[][] t2 = [[ 'e', 'y', '0' ], ['=', '=']]; - assertThrown!Base64Exception(Base64.decoder(t2)); -} diff --git a/phobos/std/bigint.d b/phobos/std/bigint.d deleted file mode 100644 index 0240ea1..0000000 --- a/phobos/std/bigint.d +++ /dev/null @@ -1,2385 +0,0 @@ -/** Arbitrary-precision ('bignum') arithmetic. - * - * Performance is optimized for numbers below ~1000 decimal digits. - * For X86 machines, highly optimised assembly routines are used. - * - * The following algorithms are currently implemented: - * $(UL - * $(LI Karatsuba multiplication) - * $(LI Squaring is optimized independently of multiplication) - * $(LI Divide-and-conquer division) - * $(LI Binary exponentiation) - * ) - * - * For very large numbers, consider using the $(HTTP gmplib.org, GMP library) instead. - * - * License: $(HTTP www.boost.org/LICENSE_1_0.txt, Boost License 1.0). - * Authors: Don Clugston - * Source: $(PHOBOSSRC std/bigint.d) - */ -/* Copyright Don Clugston 2008 - 2010. - * 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.bigint; - -import std.conv : ConvException; - -import std.format.spec : FormatSpec; -import std.format : FormatException; -import std.internal.math.biguintcore; -import std.internal.math.biguintnoasm : BigDigit; -import std.range.primitives; -import std.traits; - -/** A struct representing an arbitrary precision integer. - * - * All arithmetic operations are supported, except unsigned shift right (`>>>`). - * Bitwise operations (`|`, `&`, `^`, `~`) are supported, and behave as if BigInt was - * an infinite length 2's complement number. - * - * BigInt implements value semantics using copy-on-write. This means that - * assignment is cheap, but operations such as x++ will cause heap - * allocation. (But note that for most bigint operations, heap allocation is - * inevitable anyway.) - */ -struct BigInt -{ -private: - BigUint data; // BigInt adds signed arithmetic to BigUint. - bool sign = false; -public: - /** - * Construct a `BigInt` from a decimal or hexadecimal string. The number must - * be in the form of a decimal or hex literal. It may have a leading `+` - * or `-` sign, followed by `0x` or `0X` if hexadecimal. Underscores are - * permitted in any location after the `0x` and/or the sign of the number. - * - * Params: - * s = a finite bidirectional range of any character type - * - * Throws: - * $(REF ConvException, std,conv) if the string doesn't represent a valid number - */ - this(Range)(Range s) if ( - isBidirectionalRange!Range && - isSomeChar!(ElementType!Range) && - !isInfinite!Range && - !isNarrowString!Range) - { - import std.algorithm.iteration : filterBidirectional; - import std.algorithm.searching : startsWith; - import std.conv : ConvException; - import std.exception : enforce; - import std.utf : byChar; - - enforce!ConvException(!s.empty, "Can't initialize BigInt with an empty range"); - - bool neg = false; - bool ok; - - data = 0UL; - - // check for signs and if the string is a hex value - if (s.front == '+') - { - s.popFront(); // skip '+' - } - else if (s.front == '-') - { - neg = true; - s.popFront(); - } - - if (s.save.startsWith("0x".byChar) || - s.save.startsWith("0X".byChar)) - { - s.popFront; - s.popFront; - - if (!s.empty) - ok = data.fromHexString(s.filterBidirectional!(a => a != '_')); - else - ok = false; - } - else - { - ok = data.fromDecimalString(s.filterBidirectional!(a => a != '_')); - } - - enforce!ConvException(ok, "Not a valid numerical string"); - - if (isZero()) - neg = false; - - sign = neg; - } - - /// ditto - this(Range)(Range s) pure - if (isNarrowString!Range) - { - import std.utf : byCodeUnit; - this(s.byCodeUnit); - } - - @safe unittest - { - // system because of the dummy ranges eventually call std.array!string - import std.exception : assertThrown; - import std.internal.test.dummyrange; - - auto r1 = new ReferenceBidirectionalRange!dchar("101"); - auto big1 = BigInt(r1); - assert(big1 == BigInt(101)); - - auto r2 = new ReferenceBidirectionalRange!dchar("1_000"); - auto big2 = BigInt(r2); - assert(big2 == BigInt(1000)); - - auto r3 = new ReferenceBidirectionalRange!dchar("0x0"); - auto big3 = BigInt(r3); - assert(big3 == BigInt(0)); - - auto r4 = new ReferenceBidirectionalRange!dchar("0x"); - assertThrown!ConvException(BigInt(r4)); - } - - /** - * Construct a `BigInt` from a sign and a magnitude. - * - * The magnitude is an $(REF_ALTTEXT input range, isInputRange, std,range,primitives) - * of unsigned integers that satisfies either $(REF hasLength, std,range,primitives) - * or $(REF isForwardRange, std,range,primitives). The first (leftmost) - * element of the magnitude is considered the most significant. - * - * Params: - * isNegative = true for negative, false for non-negative - * (ignored when magnitude is zero) - * magnitude = a finite range of unsigned integers - */ - this(Range)(bool isNegative, Range magnitude) if ( - isInputRange!Range && - isUnsigned!(ElementType!Range) && - (hasLength!Range || isForwardRange!Range) && - !isInfinite!Range) - { - data.fromMagnitude(magnitude); - sign = isNegative && !data.isZero; - } - - /// - pure @safe unittest - { - ubyte[] magnitude = [1, 2, 3, 4, 5, 6]; - auto b1 = BigInt(false, magnitude); - assert(cast(long) b1 == 0x01_02_03_04_05_06L); - auto b2 = BigInt(true, magnitude); - assert(cast(long) b2 == -0x01_02_03_04_05_06L); - } - - /// Construct a `BigInt` from a built-in integral type. - this(T)(T x) pure nothrow @safe if (isIntegral!T) - { - data = data.init; // @@@: Workaround for compiler bug - opAssign(x); - } - - /// - @safe unittest - { - ulong data = 1_000_000_000_000; - auto bigData = BigInt(data); - assert(bigData == BigInt("1_000_000_000_000")); - } - - /// Construct a `BigInt` from another `BigInt`. - this(T)(T x) pure nothrow @safe if (is(immutable T == immutable BigInt)) - { - opAssign(x); - } - - /// - @safe unittest - { - const(BigInt) b1 = BigInt("1_234_567_890"); - BigInt b2 = BigInt(b1); - assert(b2 == BigInt("1_234_567_890")); - } - - /// Assignment from built-in integer types. - BigInt opAssign(T)(T x) pure nothrow @safe if (isIntegral!T) - { - data = cast(ulong) absUnsign(x); - sign = (x < 0); - return this; - } - - /// - @safe unittest - { - auto b = BigInt("123"); - b = 456; - assert(b == BigInt("456")); - } - - /// Assignment from another BigInt. - BigInt opAssign(T:BigInt)(T x) pure @nogc @safe - { - data = x.data; - sign = x.sign; - return this; - } - - /// - @safe unittest - { - auto b1 = BigInt("123"); - auto b2 = BigInt("456"); - b2 = b1; - assert(b2 == BigInt("123")); - } - - /** - * Implements assignment operators from built-in integers of the form - * `BigInt op= integer`. - */ - BigInt opOpAssign(string op, T)(T y) pure nothrow @safe return scope - if ((op=="+" || op=="-" || op=="*" || op=="/" || op=="%" - || op==">>" || op=="<<" || op=="^^" || op=="|" || op=="&" || op=="^") && isIntegral!T) - { - ulong u = absUnsign(y); - - static if (op=="+") - { - data = BigUint.addOrSubInt!ulong(data, u, wantSub: sign != (y<0), sign); - } - else static if (op=="-") - { - data = BigUint.addOrSubInt!ulong(data, u, wantSub: sign == (y<0), sign); - } - else static if (op=="*") - { - if (y == 0) - { - sign = false; - data = 0UL; - } - else - { - sign = ( sign != (y<0) ); - data = BigUint.mulInt(data, u); - } - } - else static if (op=="/") - { - assert(y != 0, "Division by zero"); - static if (T.sizeof <= uint.sizeof) - { - data = BigUint.divInt(data, cast(uint) u); - } - else - { - data = BigUint.divInt(data, u); - } - sign = data.isZero() ? false : sign ^ (y < 0); - } - else static if (op=="%") - { - assert(y != 0, "Division by zero"); - static if (is(immutable(T) == immutable(long)) || is( immutable(T) == immutable(ulong) )) - { - this %= BigInt(y); - } - else - { - data = cast(ulong) BigUint.modInt(data, cast(uint) u); - if (data.isZero()) - sign = false; - } - // x%y always has the same sign as x. - // This is not the same as mathematical mod. - } - else static if (op==">>" || op=="<<") - { - // Do a left shift if y>0 and <<, or - // if y<0 and >>; else do a right shift. - if (y == 0) - return this; - else if ((y > 0) == (op=="<<")) - { - // Sign never changes during left shift - data = data.opBinary!(op)(u); - } - else - { - data = data.opBinary!(op)(u); - if (data.isZero()) - sign = false; - } - } - else static if (op=="^^") - { - sign = (y & 1) ? sign : false; - if (y < 0) - { - checkDivByZero(); - data = cast(ulong) (data == 1); - } - else - { - data = BigUint.pow(data, u); - } - } - else static if (op=="&") - { - if (y >= 0 && (y <= 1 || !sign)) // In these cases we can avoid some allocation. - { - static if (T.sizeof <= uint.sizeof && BigDigit.sizeof <= uint.sizeof) - data = cast(ulong) data.peekUint(0) & y; - else - data = data.peekUlong(0) & y; - sign = false; - } - else - { - BigInt b = y; - opOpAssign!op(b); - } - } - else static if (op=="|" || op=="^") - { - BigInt b = y; - opOpAssign!op(b); - } - else static assert(0, "BigInt " ~ op[0..$-1] ~ "= " ~ T.stringof ~ " is not supported"); - return this; - } - - /// - @safe unittest - { - auto b = BigInt("1_000_000_000"); - - b += 12345; - assert(b == BigInt("1_000_012_345")); - - b /= 5; - assert(b == BigInt("200_002_469")); - } - - // https://issues.dlang.org/show_bug.cgi?id=16264 - @safe unittest - { - auto a = BigInt( - `335690982744637013564796917901053301979460129353374296317539383938630086938` ~ - `465898213033510992292836631752875403891802201862860531801760096359705447768` ~ - `957432600293361240407059207520920532482429912948952142341440301429494694368` ~ - `264560802292927144211230021750155988283029753927847924288850436812178022006` ~ - `408597793414273953252832688620479083497367463977081627995406363446761896298` ~ - `967177607401918269561385622811274398143647535024987050366350585544531063531` ~ - `7118554808325723941557169427279911052268935775`); - - auto b = BigInt( - `207672245542926038535480439528441949928508406405023044025560363701392340829` ~ - `852529131306106648201340460604257466180580583656068555417076345439694125326` ~ - `843947164365500055567495554645796102453565953360564114634705366335703491527` ~ - `429426780005741168078089657359833601261803592920462081364401456331489106355` ~ - `199133982282631108670436696758342051198891939367812305559960349479160308314` ~ - `068518200681530999860641597181672463704794566473241690395901768680673716414` ~ - `243691584391572899147223065906633310537507956952626106509069491302359792769` ~ - `378934570685117202046921464019396759638376362935855896435623442486036961070` ~ - `534574698959398017332214518246531363445309522357827985468581166065335726996` ~ - `711467464306784543112544076165391268106101754253962102479935962248302404638` ~ - `21737237102628470475027851189594709504`); - - BigInt c = a * b; // Crashes - - assert(c == BigInt( - `697137001950904057507249234183127244116872349433141878383548259425589716813` ~ - `135440660252012378417669596912108637127036044977634382385990472429604619344` ~ - `738746224291111527200379708978133071390303850450970292020176369525401803474` ~ - `998613408923490273129022167907826017408385746675184651576154302536663744109` ~ - `111018961065316024005076097634601030334948684412785487182572502394847587887` ~ - `507385831062796361152176364659197432600147716058873232435238712648552844428` ~ - `058885217631715287816333209463171932255049134340904981280717725999710525214` ~ - `161541960645335744430049558161514565159449390036287489478108344584188898872` ~ - `434914159748515512161981956372737022393466624249130107254611846175580584736` ~ - `276213025837422102290580044755202968610542057651282410252208599309841499843` ~ - `672251048622223867183370008181364966502137725166782667358559333222947265344` ~ - `524195551978394625568228658697170315141077913403482061673401937141405425042` ~ - `283546509102861986303306729882186190883772633960389974665467972016939172303` ~ - `653623175801495207204880400522581834672918935651426160175413277309985678579` ~ - `830872397214091472424064274864210953551447463312267310436493480881235642109` ~ - `668498742629676513172286703948381906930297135997498416573231570483993847269` ~ - `479552708416124555462530834668011570929850407031109157206202741051573633443` ~ - `58105600` - )); - } - - // https://issues.dlang.org/show_bug.cgi?id=24028 - @system unittest - { - import std.exception : assertThrown; - import core.exception : AssertError; - - assert(BigInt(100) ^^ -1 == BigInt(0)); - assert(BigInt(1) ^^ -1 == BigInt(1)); - assert(BigInt(-1) ^^ -1 == BigInt(-1)); - assert(BigInt(-1) ^^ -2 == BigInt(1)); - assertThrown!AssertError(BigInt(0) ^^ -1); - } - - /** - * Implements assignment operators of the form `BigInt op= BigInt`. - */ - BigInt opOpAssign(string op, T)(T y) pure nothrow @safe return scope - if ((op=="+" || op== "-" || op=="*" || op=="|" || op=="&" || op=="^" || op=="/" || op=="%") - && is (T: BigInt)) - { - static if (op == "+") - { - data = BigUint.addOrSub(data, y.data, sign != y.sign, sign); - } - else static if (op == "-") - { - data = BigUint.addOrSub(data, y.data, sign == y.sign, sign); - } - else static if (op == "*") - { - data = BigUint.mul(data, y.data); - sign = isZero() ? false : sign ^ y.sign; - } - else static if (op == "/") - { - y.checkDivByZero(); - if (!isZero()) - { - data = BigUint.div(data, y.data); - sign = isZero() ? false : sign ^ y.sign; - } - } - else static if (op == "%") - { - y.checkDivByZero(); - if (!isZero()) - { - data = BigUint.mod(data, y.data); - // x%y always has the same sign as x. - if (isZero()) - sign = false; - } - } - else static if (op == "|" || op == "&" || op == "^") - { - data = BigUint.bitwiseOp!op(data, y.data, sign, y.sign, sign); - } - else static assert(0, "BigInt " ~ op[0..$-1] ~ "= " ~ - T.stringof ~ " is not supported"); - return this; - } - - /// - @safe unittest - { - auto x = BigInt("123"); - auto y = BigInt("321"); - x += y; - assert(x == BigInt("444")); - } - - /** - * Implements binary operators between `BigInt`s. - */ - BigInt opBinary(string op, T)(T y) pure nothrow @safe const return scope - if ((op=="+" || op == "*" || op=="-" || op=="|" || op=="&" || op=="^" || - op=="/" || op=="%") - && is (T: BigInt)) - { - BigInt r = this; - return r.opOpAssign!(op)(y); - } - - /// - @safe unittest - { - auto x = BigInt("123"); - auto y = BigInt("456"); - BigInt z = x * y; - assert(z == BigInt("56088")); - } - - /** - * Implements binary operators between `BigInt`'s and built-in integers. - */ - BigInt opBinary(string op, T)(T y) pure nothrow @safe const return scope - if ((op=="+" || op == "*" || op=="-" || op=="/" || op=="|" || op=="&" || - op=="^"|| op==">>" || op=="<<" || op=="^^") - && isIntegral!T) - { - BigInt r = this; - r.opOpAssign!(op)(y); - return r; - } - - /// - @safe unittest - { - auto x = BigInt("123"); - x *= 300; - assert(x == BigInt("36900")); - } - - /** - Implements a narrowing remainder operation with built-in integer types. - - This binary operator returns a narrower, built-in integer type - where applicable, according to the following table. - - $(TABLE , - $(TR $(TD `BigInt`) $(TD $(CODE_PERCENT)) $(TD `uint`) $(TD $(RARR)) $(TD `long`)) - $(TR $(TD `BigInt`) $(TD $(CODE_PERCENT)) $(TD `long`) $(TD $(RARR)) $(TD `long`)) - $(TR $(TD `BigInt`) $(TD $(CODE_PERCENT)) $(TD `ulong`) $(TD $(RARR)) $(TD `BigInt`)) - $(TR $(TD `BigInt`) $(TD $(CODE_PERCENT)) $(TD other type) $(TD $(RARR)) $(TD `int`)) - ) - */ - auto opBinary(string op, T)(T y) pure nothrow @safe const - if (op == "%" && isIntegral!T) - { - assert(y != 0, "% 0 not allowed"); - - // BigInt % uint => long - // BigInt % long => long - // BigInt % ulong => BigInt - // BigInt % other_type => int - static if (is(immutable T == immutable long) || is(immutable T == immutable ulong)) - { - auto r = this % BigInt(y); - - static if (is(immutable T == immutable long)) - { - return r.toLong(); - } - else - { - // return as-is to avoid overflow - return r; - } - } - else - { - immutable uint u = absUnsign(y); - static if (is(immutable T == immutable uint)) - alias R = long; - else - alias R = int; - R rem = BigUint.modInt(data, u); - // x%y always has the same sign as x. - // This is not the same as mathematical mod. - return sign ? -rem : rem; - } - } - - /// - @safe unittest - { - auto x = BigInt("1_000_000_500"); - long l = 1_000_000L; - ulong ul = 2_000_000UL; - int i = 500_000; - short s = 30_000; - - assert(is(typeof(x % l) == long) && x % l == 500L); - assert(is(typeof(x % ul) == BigInt) && x % ul == BigInt(500)); - assert(is(typeof(x % i) == int) && x % i == 500); - assert(is(typeof(x % s) == int) && x % s == 10500); - } - - /** - Implements operators with built-in integers on the left-hand side and - `BigInt` on the right-hand side. - */ - BigInt opBinaryRight(string op, T)(T y) pure nothrow @safe const - if ((op=="+" || op=="*" || op=="|" || op=="&" || op=="^") && isIntegral!T) - { - return opBinary!(op)(y); - } - - /// - @safe unittest - { - auto x = BigInt("100"); - BigInt y = 123 + x; - assert(y == BigInt("223")); - - BigInt z = 123 - x; - assert(z == BigInt("23")); - - // Dividing a built-in integer type by BigInt always results in - // something that fits in a built-in type, so the built-in type is - // returned, not BigInt. - assert(is(typeof(1000 / x) == int)); - assert(1000 / x == 10); - } - - // BigInt = integer op BigInt - /// ditto - BigInt opBinaryRight(string op, T)(T y) pure nothrow @safe const - if (op == "-" && isIntegral!T) - { - ulong u = absUnsign(y); - BigInt r; - static if (op == "-") - { - r.sign = sign; - r.data = BigUint.addOrSubInt!ulong(data, u, wantSub: sign == (y<0), r.sign); - r.negate(); - } - return r; - } - - // integer = integer op BigInt - /// ditto - T opBinaryRight(string op, T)(T x) pure nothrow @safe const - if ((op=="%" || op=="/") && isIntegral!T) - { - checkDivByZero(); - - static if (op == "%") - { - // x%y always has the same sign as x. - if (data.ulongLength > 1) - return x; - immutable u = absUnsign(x); - immutable rem = u % data.peekUlong(0); - // x%y always has the same sign as x. - return cast(T)((x<0) ? -rem : rem); - } - else static if (op == "/") - { - if (data.ulongLength > 1) - return 0; - return cast(T)(x / data.peekUlong(0)); - } - } - - // const unary operations - /** - Implements `BigInt` unary operators. - */ - BigInt opUnary(string op)() pure nothrow @safe const if (op=="+" || op=="-" || op=="~") - { - static if (op=="-") - { - BigInt r = this; - r.negate(); - return r; - } - else static if (op=="~") - { - return -(this+1); - } - else static if (op=="+") - return this; - } - - // non-const unary operations - /// ditto - BigInt opUnary(string op)() pure nothrow @safe if (op=="++" || op=="--") - { - static if (op=="++") - { - data = BigUint.addOrSubInt!ulong(data, 1UL, wantSub: sign, sign); - return this; - } - else static if (op=="--") - { - data = BigUint.addOrSubInt!ulong(data, 1UL, wantSub: !sign, sign); - return this; - } - } - - /// - @safe unittest - { - auto x = BigInt("1234"); - assert(-x == BigInt("-1234")); - - ++x; - assert(x == BigInt("1235")); - } - - /** - Implements `BigInt` equality test with other `BigInt`'s and built-in - numeric types. - */ - bool opEquals()(auto ref const BigInt y) const pure @nogc @safe - { - return sign == y.sign && y.data == data; - } - - /// ditto - bool opEquals(T)(const T y) const pure nothrow @nogc @safe if (isIntegral!T) - { - if (sign != (y<0)) - return 0; - return data.opEquals(cast(ulong) absUnsign(y)); - } - - /// ditto - bool opEquals(T)(const T y) const pure nothrow @nogc if (isFloatingPoint!T) - { - return 0 == opCmp(y); - } - - /// - @safe unittest - { - // Note that when comparing a BigInt to a float or double the - // full precision of the BigInt is always considered, unlike - // when comparing an int to a float or a long to a double. - assert(BigInt(123456789) != cast(float) 123456789); - } - - @safe unittest - { - auto x = BigInt("12345"); - auto y = BigInt("12340"); - int z = 12345; - int w = 54321; - - assert(x == x); - assert(x != y); - assert(x == y + 5); - assert(x == z); - assert(x != w); - } - - @safe unittest - { - import std.math.operations : nextDown, nextUp; - - const x = BigInt("0x1abc_de80_0000_0000_0000_0000_0000_0000"); - BigInt x1 = x + 1; - BigInt x2 = x - 1; - - const d = 0x1.abcde8p124; - assert(x == d); - assert(x1 != d); - assert(x2 != d); - assert(x != nextUp(d)); - assert(x != nextDown(d)); - assert(x != double.nan); - - const dL = 0x1.abcde8p124L; - assert(x == dL); - assert(x1 != dL); - assert(x2 != dL); - assert(x != nextUp(dL)); - assert(x != nextDown(dL)); - assert(x != real.nan); - - assert(BigInt(0) == 0.0f); - assert(BigInt(0) == 0.0); - assert(BigInt(0) == 0.0L); - assert(BigInt(0) == -0.0f); - assert(BigInt(0) == -0.0); - assert(BigInt(0) == -0.0L); - assert(BigInt("999_999_999_999_999_999_999_999_999_999_999_999_999") != float.infinity); - } - - /** - Implements casting to `bool`. - */ - T opCast(T:bool)() pure nothrow @nogc @safe const - { - return !isZero(); - } - - /// - @safe unittest - { - // Non-zero values are regarded as true - auto x = BigInt("1"); - auto y = BigInt("10"); - assert(x); - assert(y); - - // Zero value is regarded as false - auto z = BigInt("0"); - assert(!z); - } - - /** - Implements casting to integer types. - - Throws: $(REF ConvOverflowException, std,conv) if the number exceeds - the target type's range. - */ - T opCast(T:ulong)() pure @safe const - { - if (isUnsigned!T && sign) - { /* throw */ } - else - if (data.ulongLength == 1) - { - ulong l = data.peekUlong(0); - if (isUnsigned!T || !sign) - { - if (l <= T.max) - return cast(T) l; - } - else - { - if (l <= ulong(T.max)+1) - return cast(T)-long(l); // -long.min == long.min - } - } - - import std.conv : ConvOverflowException; - import std.string : format; - throw new ConvOverflowException( - "BigInt(%s) cannot be represented as a %s" - .format(this.toDecimalString, T.stringof)); - } - - /// - @safe unittest - { - import std.conv : to, ConvOverflowException; - import std.exception : assertThrown; - - assert(BigInt("0").to!int == 0); - - assert(BigInt("0").to!ubyte == 0); - assert(BigInt("255").to!ubyte == 255); - assertThrown!ConvOverflowException(BigInt("256").to!ubyte); - assertThrown!ConvOverflowException(BigInt("-1").to!ubyte); - } - - @safe unittest - { - import std.conv : to, ConvOverflowException; - import std.exception : assertThrown; - - assert(BigInt("-1").to!byte == -1); - assert(BigInt("-128").to!byte == -128); - assert(BigInt("127").to!byte == 127); - assertThrown!ConvOverflowException(BigInt("-129").to!byte); - assertThrown!ConvOverflowException(BigInt("128").to!byte); - - assert(BigInt("0").to!uint == 0); - assert(BigInt("4294967295").to!uint == uint.max); - assertThrown!ConvOverflowException(BigInt("4294967296").to!uint); - assertThrown!ConvOverflowException(BigInt("-1").to!uint); - - assert(BigInt("-1").to!int == -1); - assert(BigInt("-2147483648").to!int == int.min); - assert(BigInt("2147483647").to!int == int.max); - assertThrown!ConvOverflowException(BigInt("-2147483649").to!int); - assertThrown!ConvOverflowException(BigInt("2147483648").to!int); - - assert(BigInt("0").to!ulong == 0); - assert(BigInt("18446744073709551615").to!ulong == ulong.max); - assertThrown!ConvOverflowException(BigInt("18446744073709551616").to!ulong); - assertThrown!ConvOverflowException(BigInt("-1").to!ulong); - - assert(BigInt("-1").to!long == -1); - assert(BigInt("-9223372036854775808").to!long == long.min); - assert(BigInt("9223372036854775807").to!long == long.max); - assertThrown!ConvOverflowException(BigInt("-9223372036854775809").to!long); - assertThrown!ConvOverflowException(BigInt("9223372036854775808").to!long); - } - - /** - Implements casting to floating point types. - */ - T opCast(T)() @safe nothrow @nogc const if (isFloatingPoint!T) - { - return toFloat!(T, "nearest"); - } - - /// - @system unittest - { - assert(cast(float) BigInt("35540592535949172786332045140593475584") - == 35540592535949172786332045140593475584.0f); - assert(cast(double) BigInt("35540601499647381470685035515422441472") - == 35540601499647381470685035515422441472.0); - assert(cast(real) BigInt("35540601499647381470685035515422441472") - == 35540601499647381470685035515422441472.0L); - - assert(cast(float) BigInt("-0x1345_6780_0000_0000_0000_0000_0000") == -0x1.3456_78p+108f ); - assert(cast(double) BigInt("-0x1345_678a_bcde_f000_0000_0000_0000") == -0x1.3456_78ab_cdefp+108 ); - assert(cast(real) BigInt("-0x1345_678a_bcde_f000_0000_0000_0000") == -0x1.3456_78ab_cdefp+108L); - } - - /// Rounding when casting to floating point - @system unittest - { - // BigInts whose values cannot be exactly represented as float/double/real - // are rounded when cast to float/double/real. When cast to float or - // double or 64-bit real the rounding is strictly defined. When cast - // to extended-precision real the rounding rules vary by environment. - - // BigInts that fall somewhere between two non-infinite floats/doubles - // are rounded to the closer value when cast to float/double. - assert(cast(float) BigInt(0x1aaa_aae7) == 0x1.aaa_aaep+28f); - assert(cast(float) BigInt(0x1aaa_aaff) == 0x1.aaa_ab0p+28f); - assert(cast(float) BigInt(-0x1aaa_aae7) == -0x1.aaaaaep+28f); - assert(cast(float) BigInt(-0x1aaa_aaff) == -0x1.aaaab0p+28f); - - assert(cast(double) BigInt(0x1aaa_aaaa_aaaa_aa77) == 0x1.aaa_aaaa_aaaa_aa00p+60); - assert(cast(double) BigInt(0x1aaa_aaaa_aaaa_aaff) == 0x1.aaa_aaaa_aaaa_ab00p+60); - assert(cast(double) BigInt(-0x1aaa_aaaa_aaaa_aa77) == -0x1.aaa_aaaa_aaaa_aa00p+60); - assert(cast(double) BigInt(-0x1aaa_aaaa_aaaa_aaff) == -0x1.aaa_aaaa_aaaa_ab00p+60); - - // BigInts that fall exactly between two non-infinite floats/doubles - // are rounded away from zero when cast to float/double. (Note that - // in most environments this is NOT the same rounding rule rule used - // when casting int/long to float/double.) - assert(cast(float) BigInt(0x1aaa_aaf0) == 0x1.aaa_ab0p+28f); - assert(cast(float) BigInt(-0x1aaa_aaf0) == -0x1.aaaab0p+28f); - - assert(cast(double) BigInt(0x1aaa_aaaa_aaaa_aa80) == 0x1.aaa_aaaa_aaaa_ab00p+60); - assert(cast(double) BigInt(-0x1aaa_aaaa_aaaa_aa80) == -0x1.aaa_aaaa_aaaa_ab00p+60); - - // BigInts that are bounded on one side by the largest positive or - // most negative finite float/double and on the other side by infinity - // or -infinity are rounded as if in place of infinity was the value - // `2^^(T.max_exp)` when cast to float/double. - assert(cast(float) BigInt("999_999_999_999_999_999_999_999_999_999_999_999_999") == float.infinity); - assert(cast(float) BigInt("-999_999_999_999_999_999_999_999_999_999_999_999_999") == -float.infinity); - - assert(cast(double) BigInt("999_999_999_999_999_999_999_999_999_999_999_999_999") < double.infinity); - assert(cast(real) BigInt("999_999_999_999_999_999_999_999_999_999_999_999_999") < real.infinity); - } - - @safe unittest - { - // Test exponent overflow is correct. - assert(cast(float) BigInt(0x1fffffff) == 0x1.000000p+29f); - assert(cast(double) BigInt(0x1fff_ffff_ffff_fff0) == 0x1.000000p+61); - } - - private T toFloat(T, string roundingMode)() @safe nothrow @nogc const - if (__traits(isFloating, T) && (roundingMode == "nearest" || roundingMode == "truncate")) - { - import core.bitop : bsr; - enum performRounding = (roundingMode == "nearest"); - enum performTruncation = (roundingMode == "truncate"); - static assert(performRounding || performTruncation, "unrecognized rounding mode"); - enum int totalNeededBits = T.mant_dig + int(performRounding); - static if (totalNeededBits <= 64) - { - // We need to examine the top two 64-bit words, not just the top one, - // since the top word could have just a single significant bit. - const ulongLength = data.ulongLength; - const ulong w1 = data.peekUlong(ulongLength - 1); - if (w1 == 0) - return T(0); // Special: exponent should be all zero bits, plus bsr(w1) is undefined. - const ulong w2 = ulongLength < 2 ? 0 : data.peekUlong(ulongLength - 2); - const uint w1BitCount = bsr(w1) + 1; - ulong sansExponent = (w1 << (64 - w1BitCount)) | (w2 >>> (w1BitCount)); - size_t exponent = (ulongLength - 1) * 64 + w1BitCount + 1; - static if (performRounding) - { - sansExponent += 1UL << (64 - totalNeededBits); - if (0 <= cast(long) sansExponent) // Use high bit to detect overflow. - { - // Do not bother filling in the high bit of sansExponent - // with 1. It will be discarded by float and double and 80 - // bit real cannot be on this path with rounding enabled. - exponent += 1; - } - } - static if (T.mant_dig == float.mant_dig) - { - if (exponent >= T.max_exp) - return isNegative ? -T.infinity : T.infinity; - uint resultBits = (uint(isNegative) << 31) | // sign bit - ((0xFF & (exponent - float.min_exp)) << 23) | // exponent - cast(uint) ((sansExponent << 1) >>> (64 - 23)); // mantissa. - // TODO: remove @trusted lambda after DIP 1000 is enabled by default. - return (() @trusted => *cast(float*) &resultBits)(); - } - else static if (T.mant_dig == double.mant_dig) - { - if (exponent >= T.max_exp) - return isNegative ? -T.infinity : T.infinity; - ulong resultBits = (ulong(isNegative) << 63) | // sign bit - ((0x7FFUL & (exponent - double.min_exp)) << 52) | // exponent - ((sansExponent << 1) >>> (64 - 52)); // mantissa. - // TODO: remove @trusted lambda after DIP 1000 is enabled by default. - return (() @trusted => *cast(double*) &resultBits)(); - } - else - { - import core.math : ldexp; - return ldexp(isNegative ? -cast(real) sansExponent : cast(real) sansExponent, - cast(int) exponent - 65); - } - } - else - { - import core.math : ldexp; - const ulongLength = data.ulongLength; - if ((ulongLength - 1) * 64L > int.max) - return isNegative ? -T.infinity : T.infinity; - int scale = cast(int) ((ulongLength - 1) * 64); - const ulong w1 = data.peekUlong(ulongLength - 1); - if (w1 == 0) - return T(0); // Special: bsr(w1) is undefined. - int bitsStillNeeded = totalNeededBits - bsr(w1) - 1; - T acc = ldexp(cast(T) w1, scale); - for (ptrdiff_t i = ulongLength - 2; i >= 0 && bitsStillNeeded > 0; i--) - { - ulong w = data.peekUlong(i); - // To round towards zero we must make sure not to use too many bits. - if (bitsStillNeeded >= 64) - { - acc += ldexp(cast(T) w, scale -= 64); - bitsStillNeeded -= 64; - } - else - { - w = (w >>> (64 - bitsStillNeeded)) << (64 - bitsStillNeeded); - acc += ldexp(cast(T) w, scale -= 64); - break; - } - } - if (isNegative) - acc = -acc; - return cast(T) acc; - } - } - - /** - Implements casting to/from qualified `BigInt`'s. - - Warning: Casting to/from `const` or `immutable` may break type - system guarantees. Use with care. - */ - T opCast(T)() pure nothrow @nogc const - if (is(immutable T == immutable BigInt)) - { - return this; - } - - /// - @safe unittest - { - const(BigInt) x = BigInt("123"); - BigInt y = cast() x; // cast away const - assert(y == x); - } - - // Hack to make BigInt's typeinfo.compare work properly. - // Note that this must appear before the other opCmp overloads, otherwise - // DMD won't find it. - /** - Implements 3-way comparisons of `BigInt` with `BigInt` or `BigInt` with - built-in numeric types. - */ - int opCmp(ref const BigInt y) pure nothrow @nogc @safe const - { - // Simply redirect to the "real" opCmp implementation. - return this.opCmp!BigInt(y); - } - - /// ditto - int opCmp(T)(const T y) pure nothrow @nogc @safe const if (isIntegral!T) - { - if (sign != (y<0) ) - return sign ? -1 : 1; - int cmp = data.opCmp(cast(ulong) absUnsign(y)); - return sign? -cmp: cmp; - } - /// ditto - int opCmp(T)(const T y) nothrow @nogc @safe const if (isFloatingPoint!T) - { - import core.bitop : bsr; - import std.math.operations : cmp; - import std.math.traits : isFinite; - - const asFloat = toFloat!(T, "truncate"); - if (asFloat != y) - return cmp(asFloat, y); // handles +/- NaN. - if (!isFinite(y)) - return isNegative ? 1 : -1; - const ulongLength = data.ulongLength; - const w1 = data.peekUlong(ulongLength - 1); - if (w1 == 0) - return 0; // Special: bsr(w1) is undefined. - const numSignificantBits = (ulongLength - 1) * 64 + bsr(w1) + 1; - for (ptrdiff_t bitsRemainingToCheck = numSignificantBits - T.mant_dig, i = 0; - bitsRemainingToCheck > 0; i++, bitsRemainingToCheck -= 64) - { - auto word = data.peekUlong(i); - if (word == 0) - continue; - // Make sure we're only checking digits that are beyond - // the precision of `y`. - if (bitsRemainingToCheck < 64 && (word << (64 - bitsRemainingToCheck)) == 0) - break; // This can only happen on the last loop iteration. - return isNegative ? -1 : 1; - } - return 0; - } - /// ditto - int opCmp(T:BigInt)(const T y) pure nothrow @nogc @safe const - { - if (sign != y.sign) - return sign ? -1 : 1; - immutable cmp = data.opCmp(y.data); - return sign? -cmp: cmp; - } - - /// - @safe unittest - { - auto x = BigInt("100"); - auto y = BigInt("10"); - int z = 50; - const int w = 200; - - assert(y < x); - assert(x > z); - assert(z > y); - assert(x < w); - } - - /// - @safe unittest - { - auto x = BigInt("0x1abc_de80_0000_0000_0000_0000_0000_0000"); - BigInt y = x - 1; - BigInt z = x + 1; - - double d = 0x1.abcde8p124; - assert(y < d); - assert(z > d); - assert(x >= d && x <= d); - - // Note that when comparing a BigInt to a float or double the - // full precision of the BigInt is always considered, unlike - // when comparing an int to a float or a long to a double. - assert(BigInt(123456789) < cast(float) 123456789); - } - - @safe unittest - { - assert(BigInt("999_999_999_999_999_999_999_999_999_999_999_999_999") < float.infinity); - - // Test `real` works. - auto x = BigInt("0x1abc_de80_0000_0000_0000_0000_0000_0000"); - BigInt y = x - 1; - BigInt z = x + 1; - - real d = 0x1.abcde8p124; - assert(y < d); - assert(z > d); - assert(x >= d && x <= d); - - // Test comparison for numbers of 64 bits or fewer. - auto w1 = BigInt(0x1abc_de80_0000_0000); - auto w2 = w1 - 1; - auto w3 = w1 + 1; - assert(w1.ulongLength == 1); - assert(w2.ulongLength == 1); - assert(w3.ulongLength == 1); - - double e = 0x1.abcde8p+60; - assert(w1 >= e && w1 <= e); - assert(w2 < e); - assert(w3 > e); - - real eL = 0x1.abcde8p+60; - assert(w1 >= eL && w1 <= eL); - assert(w2 < eL); - assert(w3 > eL); - } - - /** - Returns: The value of this `BigInt` as a `long`, or `long.max`/`long.min` - if outside the representable range. - */ - long toLong() @safe pure nothrow const @nogc - { - return (sign ? -1 : 1) * - (data.ulongLength == 1 && (data.peekUlong(0) <= sign+cast(ulong)(long.max)) // 1+long.max = |long.min| - ? cast(long)(data.peekUlong(0)) - : long.max); - } - - /// - @safe unittest - { - auto b = BigInt("12345"); - long l = b.toLong(); - assert(l == 12345); - } - - /** - Returns: The value of this `BigInt` as an `int`, or `int.max`/`int.min` if outside - the representable range. - */ - int toInt() @safe pure nothrow @nogc const - { - return (sign ? -1 : 1) * - (data.uintLength == 1 && (data.peekUint(0) <= sign+cast(uint)(int.max)) // 1+int.max = |int.min| - ? cast(int)(data.peekUint(0)) - : int.max); - } - - /// - @safe unittest - { - auto big = BigInt("5_000_000"); - auto i = big.toInt(); - assert(i == 5_000_000); - - // Numbers that are too big to fit into an int will be clamped to int.max. - auto tooBig = BigInt("5_000_000_000"); - i = tooBig.toInt(); - assert(i == int.max); - } - - /// Number of significant `uint`s which are used in storing this number. - /// The absolute value of this `BigInt` is always < 2$(SUPERSCRIPT 32*uintLength) - @property size_t uintLength() @safe pure nothrow @nogc const - { - return data.uintLength; - } - - /// Number of significant `ulong`s which are used in storing this number. - /// The absolute value of this `BigInt` is always < 2$(SUPERSCRIPT 64*ulongLength) - @property size_t ulongLength() @safe pure nothrow @nogc const - { - return data.ulongLength; - } - - /** Convert the `BigInt` to `string`, passing it to the given sink. - * - * Params: - * sink = An OutputRange for accepting possibly piecewise segments of the - * formatted string. - * formatString = A format string specifying the output format. - * - * $(TABLE Available output formats:, - * $(TR $(TD "d") $(TD Decimal)) - * $(TR $(TD "o") $(TD Octal)) - * $(TR $(TD "x") $(TD Hexadecimal, lower case)) - * $(TR $(TD "X") $(TD Hexadecimal, upper case)) - * $(TR $(TD "s") $(TD Default formatting (same as "d") )) - * $(TR $(TD null) $(TD Default formatting (same as "d") )) - * ) - */ - void toString(Writer)(scope ref Writer sink, string formatString) const - { - auto f = FormatSpec!char(formatString); - f.writeUpToNextSpec(sink); - toString!Writer(sink, f); - } - - /// ditto - void toString(Writer)(scope ref Writer sink, scope const ref FormatSpec!char f) const - { - import std.range.primitives : put; - const spec = f.spec; - immutable hex = (spec == 'x' || spec == 'X'); - if (!(spec == 's' || spec == 'd' || spec =='o' || hex)) - throw new FormatException("Format specifier not understood: %" ~ spec); - - char[] buff; - if (spec == 'X') - { - buff = data.toHexString(0, '_', 0, f.flZero ? '0' : ' ', LetterCase.upper); - } - else if (spec == 'x') - { - buff = data.toHexString(0, '_', 0, f.flZero ? '0' : ' ', LetterCase.lower); - } - else if (spec == 'o') - { - buff = data.toOctalString(); - } - else - { - buff = data.toDecimalString(0); - } - assert(buff.length > 0, "Invalid buffer length"); - - char signChar = isNegative ? '-' : 0; - auto minw = buff.length + (signChar ? 1 : 0); - - if (!hex && !signChar && (f.width == 0 || minw < f.width)) - { - if (f.flPlus) - { - signChar = '+'; - ++minw; - } - else if (f.flSpace) - { - signChar = ' '; - ++minw; - } - } - - immutable maxw = minw < f.width ? f.width : minw; - immutable difw = maxw - minw; - - if (!f.flDash && !f.flZero) - foreach (i; 0 .. difw) - put(sink, " "); - - if (signChar) - { - scope char[1] buf = signChar; - put(sink, buf[]); - } - - if (!f.flDash && f.flZero) - foreach (i; 0 .. difw) - put(sink, "0"); - - put(sink, buff); - - if (f.flDash) - foreach (i; 0 .. difw) - put(sink, " "); - } - - /** - `toString` is rarely directly invoked; the usual way of using it is via - $(REF format, std, format): - */ - @safe unittest - { - import std.format : format; - - auto x = BigInt("1_000_000"); - x *= 12345; - - assert(format("%d", x) == "12345000000"); - assert(format("%x", x) == "2_dfd1c040"); - assert(format("%X", x) == "2_DFD1C040"); - assert(format("%o", x) == "133764340100"); - } - - // for backwards compatibility, see unittest below - /// ditto - void toString(scope void delegate(scope const(char)[]) sink, string formatString) const - { - toString!(void delegate(scope const(char)[]))(sink, formatString); - } - - // for backwards compatibility, see unittest below - /// ditto - void toString(scope void delegate(scope const(char)[]) sink, scope const ref FormatSpec!char f) const - { - toString!(void delegate(scope const(char)[]))(sink, f); - } - - // Backwards compatibility test - // BigInt.toString used to only accept a delegate sink function, but this does not work - // well with attributes such as @safe. A template function toString was added that - // works on OutputRanges, but when a delegate was passed in the form of an untyped - // lambda such as `str => dst.put(str)` the parameter type was inferred as `void` and - // the function failed to instantiate. - @system unittest - { - import std.format.spec : FormatSpec; - import std.array : appender; - BigInt num = 503; - auto dst = appender!string(); - num.toString(str => dst.put(str), null); - assert(dst[] == "503"); - num = 504; - auto f = FormatSpec!char(""); - num.toString(str => dst.put(str), f); - assert(dst[] == "503504"); - } - - // Implement toHash so that BigInt works properly as an AA key. - /** - Returns: A unique hash of the `BigInt`'s value suitable for use in a hash - table. - */ - size_t toHash() const @safe pure nothrow @nogc - { - return data.toHash() + sign; - } - - /** - `toHash` is rarely directly invoked; it is implicitly used when - BigInt is used as the key of an associative array. - */ - @safe pure unittest - { - string[BigInt] aa; - aa[BigInt(123)] = "abc"; - aa[BigInt(456)] = "def"; - - assert(aa[BigInt(123)] == "abc"); - assert(aa[BigInt(456)] == "def"); - } - - /** - * Gets the nth number in the underlying representation that makes up the whole - * `BigInt`. - * - * Params: - * T = the type to view the underlying representation as - * n = The nth number to retrieve. Must be less than $(LREF ulongLength) or - * $(LREF uintLength) with respect to `T`. - * Returns: - * The nth `ulong` in the representation of this `BigInt`. - */ - T getDigit(T = ulong)(size_t n) const - if (is(T == ulong) || is(T == uint)) - { - static if (is(T == ulong)) - { - assert(n < ulongLength(), "getDigit index out of bounds"); - return data.peekUlong(n); - } - else - { - assert(n < uintLength(), "getDigit index out of bounds"); - return data.peekUint(n); - } - } - - /// - @safe pure unittest - { - auto a = BigInt("1000"); - assert(a.ulongLength() == 1); - assert(a.getDigit(0) == 1000); - - assert(a.uintLength() == 1); - assert(a.getDigit!uint(0) == 1000); - - auto b = BigInt("2_000_000_000_000_000_000_000_000_000"); - assert(b.ulongLength() == 2); - assert(b.getDigit(0) == 4584946418820579328); - assert(b.getDigit(1) == 108420217); - - assert(b.uintLength() == 3); - assert(b.getDigit!uint(0) == 3489660928); - assert(b.getDigit!uint(1) == 1067516025); - assert(b.getDigit!uint(2) == 108420217); - } - -private: - void negate() @safe pure nothrow @nogc scope - { - if (!data.isZero()) - sign = !sign; - } - bool isZero() pure const nothrow @nogc @safe scope - { - return data.isZero(); - } - alias isNegative = sign; - - // Generate a runtime error if division by zero occurs - void checkDivByZero() pure const nothrow @safe scope - { - assert(!isZero(), "BigInt division by zero"); - } -} - -/// -@safe unittest -{ - BigInt a = "9588669891916142"; - BigInt b = "7452469135154800"; - auto c = a * b; - assert(c == BigInt("71459266416693160362545788781600")); - auto d = b * a; - assert(d == BigInt("71459266416693160362545788781600")); - assert(d == c); - d = c * BigInt("794628672112"); - assert(d == BigInt("56783581982794522489042432639320434378739200")); - auto e = c + d; - assert(e == BigInt("56783581982865981755459125799682980167520800")); - auto f = d + c; - assert(f == e); - auto g = f - c; - assert(g == d); - g = f - d; - assert(g == c); - e = 12345678; - g = c + e; - auto h = g / b; - auto i = g % b; - assert(h == a); - assert(i == e); - BigInt j = "-0x9A56_57f4_7B83_AB78"; - BigInt k = j; - j ^^= 11; - assert(k ^^ 11 == j); -} - -/** -Params: - x = The `BigInt` to convert to a decimal `string`. - -Returns: - A `string` that represents the `BigInt` as a decimal number. - -*/ -string toDecimalString(const(BigInt) x) pure nothrow @safe -{ - auto buff = x.data.toDecimalString(x.isNegative ? 1 : 0); - if (x.isNegative) - buff[0] = '-'; - return buff; -} - -/// -@safe pure unittest -{ - auto x = BigInt("123"); - x *= 1000; - x += 456; - - auto xstr = x.toDecimalString(); - assert(xstr == "123456"); -} - -/** -Params: - x = The `BigInt` to convert to a hexadecimal `string`. - -Returns: - A `string` that represents the `BigInt` as a hexadecimal (base 16) - number in upper case. - -*/ -string toHex(const(BigInt) x) pure @safe -{ - import std.array : appender; - auto outbuff = appender!string(); - x.toString(outbuff, "%X"); - return outbuff[]; -} - -/// -@safe unittest -{ - auto x = BigInt("123"); - x *= 1000; - x += 456; - - auto xstr = x.toHex(); - assert(xstr == "1E240"); -} - -/** Returns the absolute value of x converted to the corresponding unsigned -type. - -Params: - x = The integral value to return the absolute value of. - -Returns: - The absolute value of x. - -*/ -Unsigned!T absUnsign(T)(T x) -if (isIntegral!T) -{ - static if (isSigned!T) - { - import std.conv : unsigned; - /* This returns the correct result even when x = T.min - * on two's complement machines because unsigned(T.min) = |T.min| - * even though -T.min = T.min. - */ - return unsigned((x < 0) ? cast(T)(0-x) : x); - } - else - { - return x; - } -} - -/// -nothrow pure @safe -unittest -{ - assert((-1).absUnsign == 1); - assert(1.absUnsign == 1); -} - -nothrow pure @safe -unittest -{ - BigInt a, b; - a = 1; - b = 2; - auto c = a + b; - assert(c == 3); -} - -nothrow pure @safe -unittest -{ - long a; - BigInt b; - auto c = a + b; - assert(c == 0); - auto d = b + a; - assert(d == 0); -} - -nothrow pure @safe -unittest -{ - BigInt x = 1, y = 2; - assert(x < y); - assert(x <= y); - assert(y >= x); - assert(y > x); - assert(x != y); - - long r1 = x.toLong; - assert(r1 == 1); - - BigInt r2 = 10 % x; - assert(r2 == 0); - - BigInt r3 = 10 / y; - assert(r3 == 5); - - BigInt[] arr = [BigInt(1)]; - auto incr = arr[0]++; - assert(arr == [BigInt(2)]); - assert(incr == BigInt(1)); -} - -@safe unittest -{ - // Radix conversion - assert( toDecimalString(BigInt("-1_234_567_890_123_456_789")) - == "-1234567890123456789"); - assert( toHex(BigInt("0x1234567890123456789")) == "123_45678901_23456789"); - assert( toHex(BigInt("0x00000000000000000000000000000000000A234567890123456789")) - == "A23_45678901_23456789"); - assert( toHex(BigInt("0x000_00_000000_000_000_000000000000_000000_")) == "0"); - - assert(BigInt(-0x12345678).toInt() == -0x12345678); - assert(BigInt(-0x12345678).toLong() == -0x12345678); - assert(BigInt(0x1234_5678_9ABC_5A5AL).ulongLength == 1); - assert(BigInt(0x1234_5678_9ABC_5A5AL).toLong() == 0x1234_5678_9ABC_5A5AL); - assert(BigInt(-0x1234_5678_9ABC_5A5AL).toLong() == -0x1234_5678_9ABC_5A5AL); - assert(BigInt(0xF234_5678_9ABC_5A5AL).toLong() == long.max); - assert(BigInt(-0x123456789ABCL).toInt() == -int.max); - char[] s1 = "123".dup; // https://issues.dlang.org/show_bug.cgi?id=8164 - assert(BigInt(s1) == 123); - char[] s2 = "0xABC".dup; - assert(BigInt(s2) == 2748); - - assert((BigInt(-2) + BigInt(1)) == BigInt(-1)); - BigInt a = ulong.max - 5; - auto b = -long.max % a; - assert( b == -long.max % (ulong.max - 5)); - b = long.max / a; - assert( b == long.max /(ulong.max - 5)); - assert(BigInt(1) - 1 == 0); - assert((-4) % BigInt(5) == -4); // https://issues.dlang.org/show_bug.cgi?id=5928 - assert(BigInt(-4) % BigInt(5) == -4); - assert(BigInt(2)/BigInt(-3) == BigInt(0)); // https://issues.dlang.org/show_bug.cgi?id=8022 - assert(BigInt("-1") > long.min); // https://issues.dlang.org/show_bug.cgi?id=9548 - - assert(toDecimalString(BigInt("0000000000000000000000000000000000000000001234567")) - == "1234567"); -} - -@safe unittest // Minimum signed value bug tests. -{ - assert(BigInt("-0x8000000000000000") == BigInt(long.min)); - assert(BigInt("-0x8000000000000000")+1 > BigInt(long.min)); - assert(BigInt("-0x80000000") == BigInt(int.min)); - assert(BigInt("-0x80000000")+1 > BigInt(int.min)); - assert(BigInt(long.min).toLong() == long.min); // lossy toLong bug for long.min - assert(BigInt(int.min).toInt() == int.min); // lossy toInt bug for int.min - assert(BigInt(long.min).ulongLength == 1); - assert(BigInt(int.min).uintLength == 1); // cast/sign extend bug in opAssign - BigInt a; - a += int.min; - assert(a == BigInt(int.min)); - a = int.min - BigInt(int.min); - assert(a == 0); - a = int.min; - assert(a == BigInt(int.min)); - assert(int.min % (BigInt(int.min)-1) == int.min); - assert((BigInt(int.min)-1)%int.min == -1); -} - - // Recursive division (https://issues.dlang.org/show_bug.cgi?id=5568) -@safe unittest -{ - enum Z = 4843; - BigInt m = (BigInt(1) << (Z*8) ) - 1; - m -= (BigInt(1) << (Z*6)) - 1; - BigInt oldm = m; - - BigInt a = (BigInt(1) << (Z*4) )-1; - BigInt b = m % a; - m /= a; - m *= a; - assert( m + b == oldm); - - m = (BigInt(1) << (4846 + 4843) ) - 1; - a = (BigInt(1) << 4846 ) - 1; - b = (BigInt(1) << (4846*2 + 4843)) - 1; - BigInt c = (BigInt(1) << (4846*2 + 4843*2)) - 1; - BigInt w = c - b + a; - assert(w % m == 0); - - // https://issues.dlang.org/show_bug.cgi?id=6819 - BigInt z1 = BigInt(10)^^64; - BigInt w1 = BigInt(10)^^128; - assert(z1^^2 == w1); - BigInt z2 = BigInt(1)<<64; - BigInt w2 = BigInt(1)<<128; - assert(z2^^2 == w2); - // https://issues.dlang.org/show_bug.cgi?id=7993 - BigInt n7793 = 10; - assert( n7793 / 1 == 10); - // https://issues.dlang.org/show_bug.cgi?id=7973 - auto a7973 = 10_000_000_000_000_000; - const c7973 = 10_000_000_000_000_000; - immutable i7973 = 10_000_000_000_000_000; - BigInt v7973 = 2551700137; - v7973 %= a7973; - assert(v7973 == 2551700137); - v7973 %= c7973; - assert(v7973 == 2551700137); - v7973 %= i7973; - assert(v7973 == 2551700137); - // https://issues.dlang.org/show_bug.cgi?id=8165 - BigInt[2] a8165; - a8165[0] = a8165[1] = 1; -} - -@safe unittest -{ - import std.array; - import std.format.write : formattedWrite; - - immutable string[][] table = [ - /* fmt, +10 -10 */ - ["%d", "10", "-10"], - ["%+d", "+10", "-10"], - ["%-d", "10", "-10"], - ["%+-d", "+10", "-10"], - - ["%4d", " 10", " -10"], - ["%+4d", " +10", " -10"], - ["%-4d", "10 ", "-10 "], - ["%+-4d", "+10 ", "-10 "], - - ["%04d", "0010", "-010"], - ["%+04d", "+010", "-010"], - ["%-04d", "10 ", "-10 "], - ["%+-04d", "+10 ", "-10 "], - - ["% 04d", " 010", "-010"], - ["%+ 04d", "+010", "-010"], - ["%- 04d", " 10 ", "-10 "], - ["%+- 04d", "+10 ", "-10 "], - ]; - - auto w1 = appender!(char[])(); - auto w2 = appender!(char[])(); - - foreach (entry; table) - { - immutable fmt = entry[0]; - - formattedWrite(w1, fmt, BigInt(10)); - formattedWrite(w2, fmt, 10); - assert(w1.data == w2.data); - assert(w1.data == entry[1]); - w1.clear(); - w2.clear(); - - formattedWrite(w1, fmt, BigInt(-10)); - formattedWrite(w2, fmt, -10); - assert(w1.data == w2.data); - assert(w1.data == entry[2]); - w1.clear(); - w2.clear(); - } -} - -@safe unittest -{ - import std.array; - import std.format.write : formattedWrite; - - immutable string[][] table = [ - /* fmt, +10 -10 */ - ["%x", "a", "-a"], - ["%+x", "a", "-a"], - ["%-x", "a", "-a"], - ["%+-x", "a", "-a"], - - ["%4x", " a", " -a"], - ["%+4x", " a", " -a"], - ["%-4x", "a ", "-a "], - ["%+-4x", "a ", "-a "], - - ["%04x", "000a", "-00a"], - ["%+04x", "000a", "-00a"], - ["%-04x", "a ", "-a "], - ["%+-04x", "a ", "-a "], - - ["% 04x", "000a", "-00a"], - ["%+ 04x", "000a", "-00a"], - ["%- 04x", "a ", "-a "], - ["%+- 04x", "a ", "-a "], - ]; - - auto w1 = appender!(char[])(); - auto w2 = appender!(char[])(); - - foreach (entry; table) - { - immutable fmt = entry[0]; - - formattedWrite(w1, fmt, BigInt(10)); - formattedWrite(w2, fmt, 10); - assert(w1.data == w2.data); // Equal only positive BigInt - assert(w1.data == entry[1]); - w1.clear(); - w2.clear(); - - formattedWrite(w1, fmt, BigInt(-10)); - //formattedWrite(w2, fmt, -10); - //assert(w1.data == w2.data); - assert(w1.data == entry[2]); - w1.clear(); - //w2.clear(); - } -} - -@safe unittest -{ - import std.array; - import std.format.write : formattedWrite; - - immutable string[][] table = [ - /* fmt, +10 -10 */ - ["%X", "A", "-A"], - ["%+X", "A", "-A"], - ["%-X", "A", "-A"], - ["%+-X", "A", "-A"], - - ["%4X", " A", " -A"], - ["%+4X", " A", " -A"], - ["%-4X", "A ", "-A "], - ["%+-4X", "A ", "-A "], - - ["%04X", "000A", "-00A"], - ["%+04X", "000A", "-00A"], - ["%-04X", "A ", "-A "], - ["%+-04X", "A ", "-A "], - - ["% 04X", "000A", "-00A"], - ["%+ 04X", "000A", "-00A"], - ["%- 04X", "A ", "-A "], - ["%+- 04X", "A ", "-A "], - ]; - - auto w1 = appender!(char[])(); - auto w2 = appender!(char[])(); - - foreach (entry; table) - { - immutable fmt = entry[0]; - - formattedWrite(w1, fmt, BigInt(10)); - formattedWrite(w2, fmt, 10); - assert(w1.data == w2.data); // Equal only positive BigInt - assert(w1.data == entry[1]); - w1.clear(); - w2.clear(); - - formattedWrite(w1, fmt, BigInt(-10)); - //formattedWrite(w2, fmt, -10); - //assert(w1.data == w2.data); - assert(w1.data == entry[2]); - w1.clear(); - //w2.clear(); - } -} - -// https://issues.dlang.org/show_bug.cgi?id=6448 -@safe unittest -{ - import std.array; - import std.format.write : formattedWrite; - - auto w1 = appender!string(); - auto w2 = appender!string(); - - int x = 100; - formattedWrite(w1, "%010d", x); - BigInt bx = x; - formattedWrite(w2, "%010d", bx); - assert(w1.data == w2.data); - // https://issues.dlang.org/show_bug.cgi?id=8011 - BigInt y = -3; - ++y; - assert(y.toLong() == -2); - y = 1; - --y; - assert(y.toLong() == 0); - --y; - assert(y.toLong() == -1); - --y; - assert(y.toLong() == -2); -} - -@safe unittest -{ - import std.math.algebraic : abs; - auto r = abs(BigInt(-1000)); // https://issues.dlang.org/show_bug.cgi?id=6486 - assert(r == 1000); - - auto r2 = abs(const(BigInt)(-500)); // https://issues.dlang.org/show_bug.cgi?id=11188 - assert(r2 == 500); - auto r3 = abs(immutable(BigInt)(-733)); // https://issues.dlang.org/show_bug.cgi?id=11188 - assert(r3 == 733); - - // opCast!bool - BigInt one = 1, zero; - assert(one && !zero); -} - -// https://issues.dlang.org/show_bug.cgi?id=6850 -@safe unittest -{ - pure long pureTest() { - BigInt a = 1; - BigInt b = 1336; - a += b; - return a.toLong(); - } - - assert(pureTest() == 1337); -} - -// https://issues.dlang.org/show_bug.cgi?id=8435 -// https://issues.dlang.org/show_bug.cgi?id=10118 -@safe unittest -{ - auto i = BigInt(100); - auto j = BigInt(100); - - // Two separate BigInt instances representing same value should have same - // hash. - assert(typeid(i).getHash(&i) == typeid(j).getHash(&j)); - assert(typeid(i).compare(&i, &j) == 0); - - // BigInt AA keys should behave consistently. - int[BigInt] aa; - aa[BigInt(123)] = 123; - assert(BigInt(123) in aa); - - aa[BigInt(123)] = 321; - assert(aa[BigInt(123)] == 321); - - auto keys = aa.byKey; - assert(keys.front == BigInt(123)); - keys.popFront(); - assert(keys.empty); -} - -// https://issues.dlang.org/show_bug.cgi?id=11148 -@safe unittest -{ - void foo(BigInt) {} - const BigInt cbi = 3; - immutable BigInt ibi = 3; - - foo(cbi); - foo(ibi); - - import std.conv : to; - import std.meta : AliasSeq; - - static foreach (T1; AliasSeq!(BigInt, const(BigInt), immutable(BigInt))) - { - static foreach (T2; AliasSeq!(BigInt, const(BigInt), immutable(BigInt))) - {{ - T1 t1 = 2; - T2 t2 = t1; - - T2 t2_1 = to!T2(t1); - T2 t2_2 = cast(T2) t1; - - assert(t2 == t1); - assert(t2 == 2); - - assert(t2_1 == t1); - assert(t2_1 == 2); - - assert(t2_2 == t1); - assert(t2_2 == 2); - }} - } - - BigInt n = 2; - n *= 2; - assert(n == 4); -} - -// https://issues.dlang.org/show_bug.cgi?id=8167 -@safe unittest -{ - BigInt a = BigInt(3); - BigInt b = BigInt(a); - assert(b == 3); -} - -// https://issues.dlang.org/show_bug.cgi?id=9061 -@safe unittest -{ - long l1 = 0x12345678_90ABCDEF; - long l2 = 0xFEDCBA09_87654321; - long l3 = l1 | l2; - long l4 = l1 & l2; - long l5 = l1 ^ l2; - - BigInt b1 = l1; - BigInt b2 = l2; - BigInt b3 = b1 | b2; - BigInt b4 = b1 & b2; - BigInt b5 = b1 ^ b2; - - assert(l3 == b3); - assert(l4 == b4); - assert(l5 == b5); -} - -// https://issues.dlang.org/show_bug.cgi?id=11600 -@safe unittest -{ - import std.conv; - import std.exception : assertThrown; - - // Original bug report - assertThrown!ConvException(to!BigInt("avadakedavra")); - - // Digit string lookalikes that are actually invalid - assertThrown!ConvException(to!BigInt("0123hellothere")); - assertThrown!ConvException(to!BigInt("-hihomarylowe")); - assertThrown!ConvException(to!BigInt("__reallynow__")); - assertThrown!ConvException(to!BigInt("-123four")); -} - -// https://issues.dlang.org/show_bug.cgi?id=11583 -@safe unittest -{ - BigInt x = 0; - assert((x > 0) == false); -} - -// https://issues.dlang.org/show_bug.cgi?id=13391 -@safe unittest -{ - BigInt x1 = "123456789"; - BigInt x2 = "123456789123456789"; - BigInt x3 = "123456789123456789123456789"; - - import std.meta : AliasSeq; - static foreach (T; AliasSeq!(byte, ubyte, short, ushort, int, uint, long, ulong)) - { - assert((x1 * T.max) / T.max == x1); - assert((x2 * T.max) / T.max == x2); - assert((x3 * T.max) / T.max == x3); - } - - assert(x1 / -123456789 == -1); - assert(x1 / 123456789U == 1); - assert(x1 / -123456789L == -1); - assert(x1 / 123456789UL == 1); - assert(x2 / -123456789123456789L == -1); - assert(x2 / 123456789123456789UL == 1); - - assert(x1 / uint.max == 0); - assert(x1 / ulong.max == 0); - assert(x2 / ulong.max == 0); - - x1 /= 123456789UL; - assert(x1 == 1); - x2 /= 123456789123456789UL; - assert(x2 == 1); -} - -// https://issues.dlang.org/show_bug.cgi?id=13963 -@safe unittest -{ - BigInt x = 1; - import std.meta : AliasSeq; - static foreach (Int; AliasSeq!(byte, ubyte, short, ushort, int)) - { - assert(is(typeof(x % Int(1)) == int)); - } - assert(is(typeof(x % 1U) == long)); - assert(is(typeof(x % 1L) == long)); - assert(is(typeof(x % 1UL) == BigInt)); - - auto x0 = BigInt(uint.max - 1); - auto x1 = BigInt(8); - assert(x1 / x == x1); - auto x2 = -BigInt(long.min) + 1; - - // uint - assert( x0 % uint.max == x0 % BigInt(uint.max)); - assert(-x0 % uint.max == -x0 % BigInt(uint.max)); - assert( x0 % uint.max == long(uint.max - 1)); - assert(-x0 % uint.max == -long(uint.max - 1)); - - // long - assert(x1 % 2L == 0L); - assert(-x1 % 2L == 0L); - - assert(x1 % 3L == 2L); - assert(x1 % -3L == 2L); - assert(-x1 % 3L == -2L); - assert(-x1 % -3L == -2L); - - assert(x1 % 11L == 8L); - assert(x1 % -11L == 8L); - assert(-x1 % 11L == -8L); - assert(-x1 % -11L == -8L); - - // ulong - assert(x1 % 2UL == BigInt(0)); - assert(-x1 % 2UL == BigInt(0)); - - assert(x1 % 3UL == BigInt(2)); - assert(-x1 % 3UL == -BigInt(2)); - - assert(x1 % 11UL == BigInt(8)); - assert(-x1 % 11UL == -BigInt(8)); - - assert(x2 % ulong.max == x2); - assert(-x2 % ulong.max == -x2); -} - -// https://issues.dlang.org/show_bug.cgi?id=14124 -@safe unittest -{ - auto x = BigInt(-3); - x %= 3; - assert(!x.isNegative); - assert(x.isZero); - - x = BigInt(-3); - x %= cast(ushort) 3; - assert(!x.isNegative); - assert(x.isZero); - - x = BigInt(-3); - x %= 3L; - assert(!x.isNegative); - assert(x.isZero); - - x = BigInt(3); - x %= -3; - assert(!x.isNegative); - assert(x.isZero); -} - -// https://issues.dlang.org/show_bug.cgi?id=15678 -@safe unittest -{ - import std.exception : assertThrown; - assertThrown!ConvException(BigInt("")); - assertThrown!ConvException(BigInt("0x1234BARF")); - assertThrown!ConvException(BigInt("1234PUKE")); -} - -// https://issues.dlang.org/show_bug.cgi?id=6447 -@safe unittest -{ - import std.algorithm.comparison : equal; - import std.range : iota; - - auto s = BigInt(1_000_000_000_000); - auto e = BigInt(1_000_000_000_003); - auto r = iota(s, e); - assert(r.equal([ - BigInt(1_000_000_000_000), - BigInt(1_000_000_000_001), - BigInt(1_000_000_000_002) - ])); -} - -// https://issues.dlang.org/show_bug.cgi?id=17330 -@safe unittest -{ - auto b = immutable BigInt("123"); - assert(b == 123); -} - -// https://issues.dlang.org/show_bug.cgi?id=14767 -@safe pure unittest -{ - static immutable a = BigInt("340282366920938463463374607431768211455"); - assert(a == BigInt("340282366920938463463374607431768211455")); - - BigInt plusTwo(in BigInt n) - { - return n + 2; - } - - enum BigInt test1 = BigInt(123); - enum BigInt test2 = plusTwo(test1); - assert(test2 == 125); -} - -/** - * Finds the quotient and remainder for the given dividend and divisor in one operation. - * - * Params: - * dividend = the $(LREF BigInt) to divide - * divisor = the $(LREF BigInt) to divide the dividend by - * quotient = is set to the result of the division - * remainder = is set to the remainder of the division - */ -void divMod(const BigInt dividend, const BigInt divisor, out BigInt quotient, out BigInt remainder) pure nothrow @safe -{ - BigUint q, r; - BigUint.divMod(dividend.data, divisor.data, q, r); - quotient.sign = dividend.sign != divisor.sign; - quotient.data = q; - remainder.sign = r.isZero() ? false : dividend.sign; - remainder.data = r; -} - -/// -@safe pure nothrow unittest -{ - auto a = BigInt(123); - auto b = BigInt(25); - BigInt q, r; - - divMod(a, b, q, r); - - assert(q == 4); - assert(r == 23); - assert(q * b + r == a); -} - -// https://issues.dlang.org/show_bug.cgi?id=18086 -@safe pure nothrow unittest -{ - BigInt q = 1; - BigInt r = 1; - BigInt c = 1024; - BigInt d = 100; - - divMod(c, d, q, r); - assert(q == 10); - assert(r == 24); - assert((q * d + r) == c); - - divMod(c, -d, q, r); - assert(q == -10); - assert(r == 24); - assert(q * -d + r == c); - - divMod(-c, -d, q, r); - assert(q == 10); - assert(r == -24); - assert(q * -d + r == -c); - - divMod(-c, d, q, r); - assert(q == -10); - assert(r == -24); - assert(q * d + r == -c); -} - -// https://issues.dlang.org/show_bug.cgi?id=22771 -@safe pure nothrow unittest -{ - BigInt quotient, remainder; - divMod(BigInt(-50), BigInt(1), quotient, remainder); - assert(remainder == 0); -} - -// https://issues.dlang.org/show_bug.cgi?id=19740 -@safe unittest -{ - BigInt a = BigInt( - "241127122100380210001001124020210001001100000200003101000062221012075223052000021042250111300200000000000" ~ - "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"); - BigInt b = BigInt( - "700200000000500418321000401140010110000022007221432000000141020011323301104104060202100200457210001600142" ~ - "000001012245300100001110215200000000120000000000000000000000000000000000000000000000000000000000000000000" ~ - "00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"); - - BigInt c = a * b; - assert(c == BigInt( - "1688372108948068874722901180228375682334987075822938736581472847151834613694489486296103575639363261807341" ~ - "3910091006778604956808730652275328822700182498926542563654351871390166691461743896850906716336187966456064" ~ - "2702007176328110013356024000000000000000000000000000000000000000000000000000000000000000000000000000000000" ~ - "0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" ~ - "0000000000000000000000000000000000000000000000000000000000000000000000000000000000000")); -} - -@safe unittest -{ - auto n = BigInt("1234"d); -} - -/** -Fast power modulus calculation for $(LREF BigInt) operands. -Params: - base = the $(LREF BigInt) is basic operands. - exponent = the $(LREF BigInt) is power exponent of base. - modulus = the $(LREF BigInt) is modules to be modular of base ^ exponent. -Returns: - The power modulus value of (base ^ exponent) % modulus. -*/ -BigInt powmod(BigInt base, BigInt exponent, BigInt modulus) pure nothrow @safe -{ - BigInt result = 1; - - while (exponent) - { - if (exponent.data.peekUint(0) & 1) - { - result = (result * base) % modulus; - } - - auto tmp = base % modulus; - base = (tmp * tmp) % modulus; - exponent >>= 1; - } - - return result; -} - -/// for powmod -@safe unittest -{ - BigInt base = BigInt("123456789012345678901234567890"); - BigInt exponent = BigInt("1234567890123456789012345678901234567"); - BigInt modulus = BigInt("1234567"); - - BigInt result = powmod(base, exponent, modulus); - assert(result == 359079); -} diff --git a/phobos/std/bitmanip.d b/phobos/std/bitmanip.d deleted file mode 100644 index de2ff31..0000000 --- a/phobos/std/bitmanip.d +++ /dev/null @@ -1,4719 +0,0 @@ -// Written in the D programming language. - -/** -Bit-level manipulation facilities. - -$(SCRIPT inhibitQuickIndex = 1;) -$(DIVC quickindex, -$(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 The D Language Foundation 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), - $(HTTP jmdavisprog.com, Jonathan M Davis), - Alex Rønne Petersen, - Damian Ziemba, - Amaury SECHET -Source: $(PHOBOSSRC std/bitmanip.d) -*/ -/* - Copyright The D Language Foundation 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; - -import std.range.primitives; -public import std.system : Endian; -import std.traits; - -private string myToString(ulong n) pure @safe -{ - import core.internal.string : UnsignedStringBuf, unsignedToTempString; - UnsignedStringBuf buf; - auto s = unsignedToTempString(n, buf); - // pure allows implicit cast to string - return s ~ (n > uint.max ? "UL" : "U"); -} - -@safe pure unittest -{ - assert(myToString(5) == "5U"); - assert(myToString(uint.max) == "4294967295U"); - assert(myToString(uint.max + 1UL) == "4294967296UL"); -} - - -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 createAccessors = ""; - } - else static if (len == 0) - { - // Fields of length 0 are always zero - enum createAccessors = "enum "~T.stringof~" "~name~" = 0;\n"; - } - else - { - enum ulong maskAllElse = ((~0uL) >> (64 - len)) << offset; - enum TSize = 8 * T.sizeof; - enum SignShift = TSize - len; - - static if (T.min < 0) - { - enum long minVal = -(1uL << (len - 1)); - enum ulong maxVal = (1uL << (len - 1)) - 1; - enum RightShiftOp = ">>="; - } - else - { - enum ulong minVal = 0; - enum ulong maxVal = (~0uL) >> (64 - len); - enum RightShiftOp = ">>>="; - } - - static if (is(T == bool)) - { - enum createAccessors = - // 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 createAccessors = "@property "~T.stringof~" "~name~"() @safe pure nothrow @nogc const {" - ~ "auto result = cast("~T.stringof~") (" ~ store ~ " >>" ~ myToString(offset) ~ ");" - ~ "result <<= " ~ myToString(SignShift) ~ ";" - ~ "result " ~ RightShiftOp ~ myToString(SignShift) ~ ";" - ~ " return 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 = "_bf"; - 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, not " ~ Size.stringof); - alias StoreType = ulong; // just to avoid another error msg - } - - enum createStorageAndFields - = "private " ~ StoreType.stringof ~ " " ~ Name ~ ";" - ~ createFields!(Name, 0, Ts); -} - -private template createFields(string store, size_t offset, Ts...) -{ - static if (Ts.length > 0) - enum createFields - = createAccessors!(store, Ts[0], Ts[1], Ts[2], offset) - ~ createFields!(store, offset + Ts[2], Ts[3 .. $]); - else - enum createFields = ""; -} - -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 createReferenceAccessor = 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 createTaggedReference - = createReferenceAccessor!(StoreName, T, sizeOfBitField!Ts, name) - ~ createFields!(StoreName, 0, Ts, size_t, "", T.sizeof * 8 - sizeOfBitField!Ts); -} - -/** -Allows creating `bitfields` inside `structs`, `classes` and `unions`. - -A `bitfield` consists of one or more entries with a fixed number of -bits reserved for each of the entries. The types of the entries can -be `bool`s, integral types or enumerated types, arbitrarily mixed. -The most efficient type to store in `bitfields` is `bool`, followed -by unsigned types, followed by signed types. - -Each non-`bool` entry of the `bitfield` will be represented by the -number of bits specified by the user. The minimum and the maximum -numbers that represent this domain can be queried by using the name -of the variable followed by `_min` or `_max`. - -Limitation: The number of bits in a `bitfield` is limited to 8, 16, -32 or 64. If padding is needed, an entry should be explicitly -allocated with an empty name. - -Implementation_details: `Bitfields` are internally stored in an -`ubyte`, `ushort`, `uint` or `ulong` depending on the number of bits -used. The bits are filled in the order given by the parameters, -starting with the lowest significant bit. The name of the (private) -variable used for saving the `bitfield` is created by concatenating -all of the variable names, each preceded by an underscore, and -a suffix `_bf`. - -Params: T = A list of template parameters divided into chunks of 3 - items. Each chunk consists (in this order) of a type, a - name and a number. Together they define an entry - of the `bitfield`: a variable of the given type and name, - which can hold as many bits as the number denotes. - -Returns: A string that can be used in a `mixin` to add the `bitfield`. - -See_Also: $(REF BitFlags, std,typecons) -*/ -string bitfields(T...)() -{ - static assert(T.length % 3 == 0, - "Wrong number of arguments (" ~ T.length.stringof ~ "): Must be a multiple of 3"); - - static foreach (i, ARG; T) - { - static if (i % 3 == 0) - static assert(is (typeof({ARG tmp = cast (ARG)0; if (ARG.min < 0) {} }())), - "Integral type or `bool` expected, found " ~ ARG.stringof); - - // would be nice to check for valid variable names too - static if (i % 3 == 1) - static assert(is (typeof(ARG) : string), - "Variable name expected, found " ~ ARG.stringof); - - static if (i % 3 == 2) - { - static assert(is (typeof({ulong tmp = ARG;}())), - "Integral value expected, found " ~ ARG.stringof); - - static if (T[i-1] != "") - { - static assert(!is (T[i-2] : bool) || ARG <= 1, - "Type `bool` is only allowed for single-bit fields"); - - static assert(ARG >= 0 && ARG <= T[i-2].sizeof * 8, - "Wrong size of bitfield: " ~ ARG.stringof); - } - } - } - - return createStorageAndFields!T; -} - -/** -Create a `bitfield` pack of eight bits, which fit in -one `ubyte`. The `bitfields` are allocated starting from the -least significant bit, i.e. `x` occupies the two least significant bits -of the `bitfields` storage. -*/ -@safe unittest -{ - 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; - - assert(obj.x == 2); - assert(obj.y == 0); - assert(obj.z == 2); - assert(obj.flag == false); -} - -/** -The sum of all bit lengths in one `bitfield` instantiation -must be exactly 8, 16, 32, or 64. If padding is needed, just allocate -one bitfield with an empty name. -*/ -@safe unittest -{ - struct A - { - mixin(bitfields!( - bool, "flag1", 1, - bool, "flag2", 1, - uint, "", 6)); - } - - A a; - assert(a.flag1 == 0); - a.flag1 = 1; - assert(a.flag1 == 1); - a.flag1 = 0; - assert(a.flag1 == 0); -} - -/// enums can be used too -@safe unittest -{ - enum ABC { A, B, C } - struct EnumTest - { - mixin(bitfields!( - ABC, "x", 2, - bool, "y", 1, - ubyte, "z", 5)); - } -} - -@safe pure nothrow @nogc -unittest -{ - // Degenerate bitfields tests mixed with range tests - // https://issues.dlang.org/show_bug.cgi?id=8474 - // https://issues.dlang.org/show_bug.cgi?id=11160 - 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); -} - -// https://issues.dlang.org/show_bug.cgi?id=6686 -@safe unittest -{ - 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); -} - -// https://issues.dlang.org/show_bug.cgi?id=5942 -@safe unittest -{ - 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)); - } - - //https://issues.dlang.org/show_bug.cgi?id=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)); -} - -// https://issues.dlang.org/show_bug.cgi?id=12477 -@system unittest -{ - import core.exception : AssertError; - import std.algorithm.searching : canFind; - - 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); } -} - -// https://issues.dlang.org/show_bug.cgi?id=15305 -@safe unittest -{ - 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); -} - -// https://issues.dlang.org/show_bug.cgi?id=21634 -@safe unittest -{ - struct A - { - mixin(bitfields!(int, "", 1, - int, "gshared", 7)); - } -} - -// https://issues.dlang.org/show_bug.cgi?id=21725 -@safe unittest -{ - struct S - { - mixin(bitfields!( - uint, q{foo}, 4, - uint, null, 4, - )); - } -} - -/** -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 -`uint*` as specified by the first argument, and is named x, as specified by the second -argument. - -Following arguments works the same way as `bitfield`'s. The bitfield must fit into the -bits known to be zero because of the pointer alignment. -*/ - -template taggedPointer(T : T*, string name, Ts...) { - enum taggedPointer = createTaggedReference!(T*, T.alignof, name, Ts); -} - -/// -@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; -} - -@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); -} - -/** -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 `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); -} - -/// -@safe unittest -{ - struct A - { - int a; - mixin(taggedClassRef!( - Object, "o", - uint, "i", 2)); - } - A obj; - obj.o = new Object(); - obj.i = 3; -} - -@system unittest -{ - 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))); -} - -private struct FloatingPointRepresentation(T) -{ - static if (is(T == float)) - { - enum uint bias = 127, fractionBits = 23, exponentBits = 8, signBits = 1; - alias FractionType = uint; - alias ExponentType = ubyte; - } - else - { - enum uint bias = 1023, fractionBits = 52, exponentBits = 11, signBits = 1; - alias FractionType = ulong; - alias ExponentType = ushort; - } - - union - { - T value; - mixin(bitfields!( - FractionType, "fraction", fractionBits, - ExponentType, "exponent", exponentBits, - bool, "sign", signBits)); - } -} - -/** - Allows manipulating the fraction, exponent, and sign parts of a - `float` separately. The definition is: - ----- -struct FloatRep -{ - union - { - float value; - mixin(bitfields!( - uint, "fraction", 23, - ubyte, "exponent", 8, - bool, "sign", 1)); - } - enum uint bias = 127, fractionBits = 23, exponentBits = 8, signBits = 1; -} ----- -*/ -alias FloatRep = FloatingPointRepresentation!float; - -/// -@safe unittest -{ - FloatRep rep = {value: 0}; - assert(rep.fraction == 0); - assert(rep.exponent == 0); - assert(!rep.sign); - - rep.value = 42; - assert(rep.fraction == 2621440); - assert(rep.exponent == 132); - assert(!rep.sign); - - rep.value = 10; - assert(rep.fraction == 2097152); - assert(rep.exponent == 130); -} - -/// -@safe unittest -{ - FloatRep rep = {value: 1}; - assert(rep.fraction == 0); - assert(rep.exponent == 127); - assert(!rep.sign); - - rep.exponent = 126; - assert(rep.value == 0.5); - - rep.exponent = 130; - assert(rep.value == 8); -} - -/// -@safe unittest -{ - FloatRep rep = {value: 1}; - rep.value = -0.5; - assert(rep.fraction == 0); - assert(rep.exponent == 126); - assert(rep.sign); - - rep.value = -1. / 3; - assert(rep.fraction == 2796203); - assert(rep.exponent == 125); - assert(rep.sign); -} - -/** - Allows manipulating the fraction, exponent, and sign parts of a - `double` separately. The definition is: - ----- -struct DoubleRep -{ - union - { - double value; - mixin(bitfields!( - ulong, "fraction", 52, - ushort, "exponent", 11, - bool, "sign", 1)); - } - enum uint bias = 1023, signBits = 1, fractionBits = 52, exponentBits = 11; -} ----- -*/ -alias DoubleRep = FloatingPointRepresentation!double; - -/// -@safe unittest -{ - DoubleRep rep = {value: 0}; - assert(rep.fraction == 0); - assert(rep.exponent == 0); - assert(!rep.sign); - - rep.value = 42; - assert(rep.fraction == 1407374883553280); - assert(rep.exponent == 1028); - assert(!rep.sign); - - rep.value = 10; - assert(rep.fraction == 1125899906842624); - assert(rep.exponent == 1026); -} - -/// -@safe unittest -{ - DoubleRep rep = {value: 1}; - assert(rep.fraction == 0); - assert(rep.exponent == 1023); - assert(!rep.sign); - - rep.exponent = 1022; - assert(rep.value == 0.5); - - rep.exponent = 1026; - assert(rep.value == 8); -} - -/// -@safe unittest -{ - DoubleRep rep = {value: 1}; - rep.value = -0.5; - assert(rep.fraction == 0); - assert(rep.exponent == 1022); - assert(rep.sign); - - rep.value = -1. / 3; - assert(rep.fraction == 1501199875790165); - assert(rep.exponent == 1021); - assert(rep.sign); -} - -/// Reading -@safe unittest -{ - 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); -} - -/// Writing -@safe unittest -{ - DoubleRep x; - x.fraction = 1125899906842624; - x.exponent = 1025; - x.sign = true; - assert(x.value == -5.0); -} - -/** -A dynamic array of bits. Each bit in a `BitArray` can be manipulated individually -or by the standard bitwise operators `&`, `|`, `^`, `~`, `>>`, `<<` and also by -other effective member functions; most of them work relative to the `BitArray`'s -dimension (see $(LREF dim)), instead of its $(LREF length). -*/ -struct BitArray -{ -private: - - import core.bitop : btc, bts, btr, bsf, bt; - import std.format.spec : FormatSpec; - - size_t _len; - size_t* _ptr; - enum bitsPerSizeT = size_t.sizeof * 8; - - @property size_t fullWords() const scope @safe @nogc pure nothrow - { - return _len / bitsPerSizeT; - } - // Number of bits after the last full word - @property size_t endBits() const scope @safe @nogc pure nothrow - { - return _len % bitsPerSizeT; - } - // Bit mask to extract the bits after the last full word - @property size_t endMask() const scope @safe @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: - /** - Creates a `BitArray` from a `bool` array, such that `bool` values read - from left to right correspond to subsequent bits in the `BitArray`. - - Params: ba = Source array of `bool` values. - */ - this(in bool[] ba) nothrow pure - { - length = ba.length; - foreach (i, b; ba) - { - this[i] = b; - } - } - - /// - @system unittest - { - import std.algorithm.comparison : equal; - - bool[] input = [true, false, false, true, true]; - auto a = BitArray(input); - assert(a.length == 5); - assert(a.bitsSet.equal([0, 3, 4])); - - // This also works because an implicit cast to bool[] occurs for this array. - auto b = BitArray([0, 0, 1]); - assert(b.length == 3); - assert(b.bitsSet.equal([2])); - } - - /// - @system unittest - { - import std.algorithm.comparison : equal; - import std.array : array; - import std.range : iota, repeat; - - BitArray a = true.repeat(70).array; - assert(a.length == 70); - assert(a.bitsSet.equal(iota(0, 70))); - } - - /** - Creates a `BitArray` from the raw contents of the source array. The - source array is not copied but simply acts as the underlying array - of bits, which stores data as `size_t` units. - - That means a particular care should be taken when passing an array - of a type different than `size_t`, firstly because its length should - be a multiple of `size_t.sizeof`, and secondly because how the bits - are mapped: - --- - size_t[] source = [1, 2, 3, 3424234, 724398, 230947, 389492]; - enum sbits = size_t.sizeof * 8; - auto ba = BitArray(source, source.length * sbits); - foreach (n; 0 .. source.length * sbits) - { - auto nth_bit = cast(bool) (source[n / sbits] & (1L << (n % sbits))); - assert(ba[n] == nth_bit); - } - --- - The least significant bit in any `size_t` unit is the starting bit of this - unit, and the most significant bit is the last bit of this unit. Therefore, - passing e.g. an array of `int`s may result in a different `BitArray` - depending on the processor's endianness. - - This constructor is the inverse of $(LREF opCast). - - Params: - v = Source array. `v.length` must be a multple of `size_t.sizeof`. - numbits = Number of bits to be mapped from the source array, i.e. - length of the created `BitArray`. - */ - this(void[] v, size_t numbits) @nogc nothrow pure - in - { - assert(numbits <= v.length * 8, - "numbits must be less than or equal to v.length * 8"); - assert(v.length % size_t.sizeof == 0, - "v.length must be a multiple of the size of size_t"); - } - do - { - _ptr = cast(size_t*) v.ptr; - _len = numbits; - } - - /// - @system unittest - { - import std.algorithm.comparison : equal; - - auto a = BitArray([1, 0, 0, 1, 1]); - - // Inverse of the cast. - auto v = cast(void[]) a; - auto b = BitArray(v, a.length); - - assert(b.length == 5); - assert(b.bitsSet.equal([0, 3, 4])); - - // a and b share the underlying data. - a[0] = 0; - assert(b[0] == 0); - assert(a == b); - } - - /// - @system unittest - { - import std.algorithm.comparison : equal; - - size_t[] source = [0b1100, 0b0011]; - enum sbits = size_t.sizeof * 8; - auto ba = BitArray(source, source.length * sbits); - // The least significant bit in each unit is this unit's starting bit. - assert(ba.bitsSet.equal([2, 3, sbits, sbits + 1])); - } - - /// - @system unittest - { - // Example from the doc for this constructor. - static immutable size_t[] sourceData = [1, 0b101, 3, 3424234, 724398, 230947, 389492]; - size_t[] source = sourceData.dup; - enum sbits = size_t.sizeof * 8; - auto ba = BitArray(source, source.length * sbits); - foreach (n; 0 .. source.length * sbits) - { - auto nth_bit = cast(bool) (source[n / sbits] & (1L << (n % sbits))); - assert(ba[n] == nth_bit); - } - - // Example of mapping only part of the array. - import std.algorithm.comparison : equal; - - auto bc = BitArray(source, sbits + 1); - assert(bc.bitsSet.equal([0, sbits])); - // Source array has not been modified. - assert(source == sourceData); - } - - // Deliberately undocumented: raw initialization of bit array. - this(size_t len, size_t* ptr) @nogc nothrow pure - { - _len = len; - _ptr = ptr; - } - - /** - Returns: Dimension i.e. the number of native words backing this `BitArray`. - - Technically, this is the length of the underlying array storing bits, which - is equal to `ceil(length / (size_t.sizeof * 8))`, as bits are packed into - `size_t` units. - */ - @property size_t dim() const @nogc nothrow pure @safe - { - return lenToDim(_len); - } - - /** - Returns: Number of bits in the `BitArray`. - */ - @property size_t length() const @nogc nothrow pure @safe - { - return _len; - } - - /********************************************** - * Sets the amount of bits in the `BitArray`. - * $(RED Warning: increasing length may overwrite bits in - * the final word of the current underlying data regardless - * of whether it is shared between BitArray objects. i.e. D - * dynamic array extension semantics are not followed.) - */ - @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; - } - - auto oldlen = _len; - _len = newlen; - if (oldlen < newlen) - { - auto end = ((oldlen / bitsPerSizeT) + 1) * bitsPerSizeT; - if (end > newlen) - end = newlen; - this[oldlen .. end] = 0; - } - } - return _len; - } - - // https://issues.dlang.org/show_bug.cgi?id=20240 - @system unittest - { - BitArray ba; - - ba.length = 1; - ba[0] = 1; - ba.length = 0; - ba.length = 1; - assert(ba[0] == 0); // OK - - ba.length = 2; - ba[1] = 1; - ba.length = 1; - ba.length = 2; - assert(ba[1] == 0); // Fail - } - - /********************************************** - * Gets the `i`'th bit in the `BitArray`. - */ - bool opIndex(size_t i) const @nogc pure nothrow - in - { - assert(i < _len, "i must be less than the length"); - } - do - { - return cast(bool) bt(_ptr, i); - } - - /// - @system unittest - { - static void fun(const BitArray arr) - { - auto x = arr[0]; - assert(x == 1); - } - BitArray a; - a.length = 3; - a[0] = 1; - fun(a); - } - - /********************************************** - * Sets the `i`'th bit in the `BitArray`. - */ - bool opIndexAssign(bool b, size_t i) @nogc pure nothrow - in - { - assert(i < _len, "i must be less than the length"); - } - do - { - if (b) - bts(_ptr, i); - else - btr(_ptr, i); - return b; - } - - /** - Sets all the values in the `BitArray` to the - value specified by `val`. - */ - void opSliceAssign(bool val) @nogc pure nothrow - { - _ptr[0 .. fullWords] = val ? ~size_t(0) : 0; - if (endBits) - { - if (val) - _ptr[fullWords] |= endMask; - else - _ptr[fullWords] &= ~endMask; - } - } - - /// - @system pure nothrow unittest - { - import std.algorithm.comparison : equal; - - auto b = BitArray([1, 0, 1, 0, 1, 1]); - - b[] = true; - // all bits are set - assert(b.bitsSet.equal([0, 1, 2, 3, 4, 5])); - - b[] = false; - // none of the bits are set - assert(b.bitsSet.empty); - } - - /** - Sets the bits of a slice of `BitArray` starting - at index `start` and ends at index ($D end - 1) - with the values specified by `val`. - */ - void opSliceAssign(bool val, size_t start, size_t end) @nogc pure nothrow - in - { - assert(start <= end, "start must be less or equal to end"); - assert(end <= length, "end must be less or equal to the length"); - } - do - { - size_t startBlock = start / bitsPerSizeT; - size_t endBlock = end / bitsPerSizeT; - size_t startOffset = start % bitsPerSizeT; - size_t endOffset = end % bitsPerSizeT; - - if (startBlock == endBlock) - { - size_t startBlockMask = ~((size_t(1) << startOffset) - 1); - size_t endBlockMask = (size_t(1) << endOffset) - 1; - size_t joinMask = startBlockMask & endBlockMask; - if (val) - _ptr[startBlock] |= joinMask; - else - _ptr[startBlock] &= ~joinMask; - return; - } - - if (startOffset != 0) - { - size_t startBlockMask = ~((size_t(1) << startOffset) - 1); - if (val) - _ptr[startBlock] |= startBlockMask; - else - _ptr[startBlock] &= ~startBlockMask; - ++startBlock; - } - if (endOffset != 0) - { - size_t endBlockMask = (size_t(1) << endOffset) - 1; - if (val) - _ptr[endBlock] |= endBlockMask; - else - _ptr[endBlock] &= ~endBlockMask; - } - _ptr[startBlock .. endBlock] = size_t(0) - size_t(val); - } - - /// - @system pure nothrow unittest - { - import std.algorithm.comparison : equal; - import std.range : iota; - import std.stdio; - - auto b = BitArray([1, 0, 0, 0, 1, 1, 0]); - b[1 .. 3] = true; - assert(b.bitsSet.equal([0, 1, 2, 4, 5])); - - bool[72] bitArray; - auto b1 = BitArray(bitArray); - b1[63 .. 67] = true; - assert(b1.bitsSet.equal([63, 64, 65, 66])); - b1[63 .. 67] = false; - assert(b1.bitsSet.empty); - b1[0 .. 64] = true; - assert(b1.bitsSet.equal(iota(0, 64))); - b1[0 .. 64] = false; - assert(b1.bitsSet.empty); - - bool[256] bitArray2; - auto b2 = BitArray(bitArray2); - b2[3 .. 245] = true; - assert(b2.bitsSet.equal(iota(3, 245))); - b2[3 .. 245] = false; - assert(b2.bitsSet.empty); - } - - /** - Flips all the bits in the `BitArray` - */ - void flip() @nogc pure nothrow - { - foreach (i; 0 .. fullWords) - _ptr[i] = ~_ptr[i]; - - if (endBits) - _ptr[fullWords] = (~_ptr[fullWords]) & endMask; - } - - /// - @system pure nothrow unittest - { - import std.algorithm.comparison : equal; - import std.range : iota; - - // positions 0, 2, 4 are set - auto b = BitArray([1, 0, 1, 0, 1, 0]); - b.flip(); - // after flipping, positions 1, 3, 5 are set - assert(b.bitsSet.equal([1, 3, 5])); - - bool[270] bits; - auto b1 = BitArray(bits); - b1.flip(); - assert(b1.bitsSet.equal(iota(0, 270))); - } - - /** - Flips a single bit, specified by `pos` - */ - void flip(size_t pos) @nogc pure nothrow - { - bt(_ptr, pos) ? btr(_ptr, pos) : bts(_ptr, pos); - } - - /// - @system pure nothrow unittest - { - auto ax = BitArray([1, 0, 0, 1]); - ax.flip(0); - assert(ax[0] == 0); - - bool[200] y; - y[90 .. 130] = true; - auto ay = BitArray(y); - ay.flip(100); - assert(ay[100] == 0); - } - - /********************************************** - * Counts all the set bits in the `BitArray` - */ - size_t count() const scope @safe @nogc pure nothrow - { - if (_ptr) - { - size_t bitCount; - foreach (i; 0 .. fullWords) - bitCount += (() @trusted => countBitsSet(_ptr[i]))(); - if (endBits) - bitCount += (() @trusted => countBitsSet(_ptr[fullWords] & endMask))(); - return bitCount; - } - else - { - return 0; - } - } - - /// - @system pure nothrow unittest - { - auto a = BitArray([0, 1, 1, 0, 0, 1, 1]); - assert(a.count == 4); - - BitArray b; - assert(b.count == 0); - - bool[200] boolArray; - boolArray[45 .. 130] = true; - auto c = BitArray(boolArray); - assert(c.count == 85); - } - - /********************************************** - * Duplicates the `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; - - a.length = 3; - a[0] = 1; a[1] = 0; a[2] = 1; - b = a.dup; - assert(b.length == 3); - foreach (i; 0 .. 3) - assert(b[i] == (((i ^ 1) & 1) ? true : false)); - } - - /********************************************** - * Support for `foreach` loops for `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 - { - 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 `BitArray`. - */ - @property BitArray reverse() @nogc pure nothrow return - out (result) - { - assert(result == this, "the result must be equal to this"); - } - do - { - 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 - { - BitArray b; - bool[5] data = [1,0,1,1,0]; - - b = BitArray(data); - b.reverse; - foreach (i; 0 .. data.length) - assert(b[i] == data[4 - i]); - } - - - /********************************************** - * Sorts the `BitArray`'s elements. - */ - @property BitArray sort() @nogc pure nothrow return - out (result) - { - assert(result == this, "the result must be equal to this"); - } - do - { - 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 - { - size_t x = 0b1100011000; - auto ba = BitArray(10, &x); - ba.sort; - foreach (i; 0 .. 6) - assert(ba[i] == false); - foreach (i; 6 .. 10) - assert(ba[i] == true); - } - - - /*************************************** - * Support for operators == and != for `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 - { - bool[] ba = [1,0,1,0,1]; - bool[] bb = [1,0,1]; - bool[] bc = [1,0,1,0,1,0,1]; - bool[] bd = [1,0,1,1,1]; - bool[] be = [1,0,1,0,1]; - bool[] bf = [1,0,1,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0]; - bool[] bg = [1,0,1,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1]; - - auto a = BitArray(ba); - auto b = BitArray(bb); - auto c = BitArray(bc); - auto d = BitArray(bd); - auto e = BitArray(be); - auto f = BitArray(bf); - auto g = BitArray(bg); - - assert(a != b); - assert(a != c); - assert(a != d); - assert(a == e); - assert(f != g); - } - - /*************************************** - * Supports comparison operators for `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 - { - bool[] ba = [1,0,1,0,1]; - bool[] bb = [1,0,1]; - bool[] bc = [1,0,1,0,1,0,1]; - bool[] bd = [1,0,1,1,1]; - bool[] be = [1,0,1,0,1]; - bool[] bf = [1,0,1,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1]; - bool[] bg = [1,0,1,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0]; - - auto a = BitArray(ba); - auto b = BitArray(bb); - auto c = BitArray(bc); - auto d = BitArray(bd); - auto e = BitArray(be); - auto f = BitArray(bf); - auto g = BitArray(bg); - - assert(a > b); - assert(a >= b); - assert(a < c); - assert(a <= c); - assert(a < d); - assert(a <= d); - assert(a == e); - assert(a <= e); - assert(a >= e); - assert(f < g); - assert(g <= g); - } - - @system unittest - { - 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 `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; - } - - /*************************************** - * Convert to `void[]`. - */ - inout(void)[] opCast(T : const void[])() inout @nogc pure nothrow - { - return cast(inout void[]) _ptr[0 .. dim]; - } - - /*************************************** - * Convert to `size_t[]`. - */ - inout(size_t)[] opCast(T : const size_t[])() inout @nogc pure nothrow - { - return _ptr[0 .. dim]; - } - - /// - @system unittest - { - import std.array : array; - import std.range : repeat, take; - - // bit array with 300 elements - auto a = BitArray(true.repeat.take(300).array); - size_t[] v = cast(size_t[]) a; - const blockSize = size_t.sizeof * 8; - assert(v.length == (a.length + blockSize - 1) / blockSize); - } - - // https://issues.dlang.org/show_bug.cgi?id=20606 - @system unittest - { - import std.meta : AliasSeq; - - static foreach (alias T; AliasSeq!(void, size_t)) - {{ - BitArray m; - T[] ma = cast(T[]) m; - - const BitArray c; - const(T)[] ca = cast(const T[]) c; - - immutable BitArray i; - immutable(T)[] ia = cast(immutable T[]) i; - - // Cross-mutability - ca = cast(const T[]) m; - ca = cast(const T[]) i; - - // Invalid cast don't compile - static assert(!is(typeof(cast(T[]) c))); - static assert(!is(typeof(cast(T[]) i))); - static assert(!is(typeof(cast(immutable T[]) m))); - static assert(!is(typeof(cast(immutable T[]) c))); - }} - } - - /*************************************** - * Support for unary operator ~ for `BitArray`. - */ - BitArray opUnary(string op)() const pure nothrow - if (op == "~") - { - 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 - { - 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 `BitArray`. - */ - BitArray opBinary(string op)(const BitArray e2) const pure nothrow - if (op == "-" || op == "&" || op == "|" || op == "^") - in - { - assert(e2.length == _len, "e2 must have the same length as this"); - } - do - { - 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 - { - 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 - { - bool[] ba = [1,0,1,0,1]; - bool[] bb = [1,0,1,1,0]; - - auto a = BitArray(ba); - auto b = BitArray(bb); - - BitArray c = a | b; - - assert(c[0] == 1); - assert(c[1] == 0); - assert(c[2] == 1); - assert(c[3] == 1); - assert(c[4] == 1); - } - - /// - @system unittest - { - bool[] ba = [1,0,1,0,1]; - bool[] bb = [1,0,1,1,0]; - - auto a = BitArray(ba); - auto b = BitArray(bb); - - BitArray c = a ^ b; - - assert(c[0] == 0); - assert(c[1] == 0); - assert(c[2] == 0); - assert(c[3] == 1); - assert(c[4] == 1); - } - - /// - @system unittest - { - bool[] ba = [1,0,1,0,1]; - bool[] bb = [1,0,1,1,0]; - - auto a = BitArray(ba); - auto b = BitArray(bb); - - BitArray c = a - b; - - 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 `BitArray`. - */ - BitArray opOpAssign(string op)(const BitArray e2) @nogc pure nothrow return scope - if (op == "-" || op == "&" || op == "|" || op == "^") - in - { - assert(e2.length == _len, "e2 must have the same length as this"); - } - do - { - 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 - { - bool[] ba = [1,0,1,0,1,1,0,1,0,1]; - bool[] bb = [1,0,1,1,0]; - auto a = BitArray(ba); - auto b = BitArray(bb); - BitArray c = a; - c.length = 5; - c &= b; - assert(a[5] == 1); - assert(a[6] == 0); - assert(a[7] == 1); - assert(a[8] == 0); - assert(a[9] == 1); - } - - /// - @system unittest - { - bool[] ba = [1,0,1,0,1]; - 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 - { - bool[] ba = [1,0,1,0,1]; - 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 - { - bool[] ba = [1,0,1,0,1]; - 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 - { - bool[] ba = [1,0,1,0,1]; - 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 `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 opOpAssign(string op)(bool b) pure nothrow return scope - if (op == "~") - { - length = _len + 1; - this[_len - 1] = b; - return this; - } - - /// - @system unittest - { - 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 opOpAssign(string op)(BitArray b) pure nothrow return scope - if (op == "~") - { - auto istart = _len; - length = _len + b.length; - for (auto i = istart; i < _len; i++) - this[i] = b[i - istart]; - return this; - } - - /// - @system unittest - { - bool[] ba = [1,0]; - 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 `BitArray`. - */ - BitArray opBinary(string op)(bool b) const pure nothrow - if (op == "~") - { - BitArray r; - - r = this.dup; - r.length = _len + 1; - r[_len] = b; - return r; - } - - /** ditto */ - BitArray opBinaryRight(string op)(bool b) const pure nothrow - if (op == "~") - { - BitArray r; - - r.length = _len + 1; - r[0] = b; - foreach (i; 0 .. _len) - r[1 + i] = this[i]; - return r; - } - - /** ditto */ - BitArray opBinary(string op)(BitArray b) const pure nothrow - if (op == "~") - { - BitArray r; - - r = this.dup; - r ~= b; - return r; - } - - /// - @system unittest - { - bool[] ba = [1,0]; - 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, "nbits must be less than bitsPerSizeT"); - } - do - { - if (nbits == 0) - return lower; - 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, "nbits must be less than bitsPerSizeT"); - } - do - { - if (nbits == 0) - return upper; - 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 `<<=` 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 `>>=` 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) - { - if (bitsToShift == 0) - _ptr[dim - wordsToShift - 1] = _ptr[dim - 1]; - else - { - // Special case: if endBits == 0, then also endMask == 0. - size_t lastWord = (endBits ? (_ptr[fullWords] & endMask) : _ptr[fullWords - 1]); - _ptr[dim - wordsToShift - 1] = rollRight(0, lastWord, bitsToShift); - } - } - - import std.algorithm.comparison : min; - foreach (i; 0 .. min(wordsToShift, dim)) - { - _ptr[dim - i - 1] = 0; - } - } - - // https://issues.dlang.org/show_bug.cgi?id=17467 - @system unittest - { - import std.algorithm.comparison : equal; - import std.range : iota; - - bool[] buf = new bool[64*3]; - buf[0 .. 64] = true; - BitArray b = BitArray(buf); - assert(equal(b.bitsSet, iota(0, 64))); - b <<= 64; - assert(equal(b.bitsSet, iota(64, 128))); - - buf = new bool[64*3]; - buf[64*2 .. 64*3] = true; - b = BitArray(buf); - assert(equal(b.bitsSet, iota(64*2, 64*3))); - b >>= 64; - assert(equal(b.bitsSet, iota(64, 128))); - } - - // https://issues.dlang.org/show_bug.cgi?id=18134 - // shifting right when length is a multiple of 8 * size_t.sizeof. - @system unittest - { - import std.algorithm.comparison : equal; - import std.array : array; - import std.range : repeat, iota; - - immutable r = size_t.sizeof * 8; - - BitArray a = true.repeat(r / 2).array; - a >>= 0; - assert(a.bitsSet.equal(iota(0, r / 2))); - a >>= 1; - assert(a.bitsSet.equal(iota(0, r / 2 - 1))); - - BitArray b = true.repeat(r).array; - b >>= 0; - assert(b.bitsSet.equal(iota(0, r))); - b >>= 1; - assert(b.bitsSet.equal(iota(0, r - 1))); - - BitArray c = true.repeat(2 * r).array; - c >>= 0; - assert(c.bitsSet.equal(iota(0, 2 * r))); - c >>= 10; - assert(c.bitsSet.equal(iota(0, 2 * r - 10))); - } - - /// - @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. - * - * Params: - * sink = A `char` accepting - * $(REF_ALTTEXT output range, isOutputRange, std, range, primitives). - * fmt = A $(REF FormatSpec, std,format) which controls how the data - * is displayed. - */ - void toString(W)(ref W sink, scope const ref FormatSpec!char fmt) const - if (isOutputRange!(W, char)) - { - const spec = fmt.spec; - switch (spec) - { - case 'b': - return formatBitString(sink); - case 's': - return formatBitArray(sink); - default: - throw new Exception("Unknown format specifier: %" ~ spec); - } - } - - /// - @system pure unittest - { - import std.format : format; - - auto b = BitArray([0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 1]); - - auto s1 = format("%s", b); - 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, chain; - - return chain( - iota(fullWords) - .filter!(i => _ptr[i])() - .map!(i => BitsSet!size_t(_ptr[i], i * bitsPerSizeT))() - .joiner(), - iota(fullWords * bitsPerSizeT, _len) - .filter!(i => this[i])() - ); - } - - /// - @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; - - 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))); - } - - // https://issues.dlang.org/show_bug.cgi?id=20241 - @system unittest - { - BitArray ba; - ba.length = 2; - ba[1] = 1; - ba.length = 1; - assert(ba.bitsSet.empty); - } - - private void formatBitString(Writer)(auto ref Writer sink) const - { - if (!length) - return; - - auto leftover = _len % 8; - foreach (idx; 0 .. leftover) - { - put(sink, cast(char)(this[idx] + '0')); - } - - if (leftover && _len > 8) - put(sink, "_"); - - size_t count; - foreach (idx; leftover .. _len) - { - put(sink, cast(char)(this[idx] + '0')); - if (++count == 8 && idx != _len - 1) - { - put(sink, "_"); - count = 0; - } - } - } - - private void formatBitArray(Writer)(auto ref Writer sink) const - { - put(sink, "["); - foreach (idx; 0 .. _len) - { - put(sink, cast(char)(this[idx] + '0')); - if (idx + 1 < _len) - put(sink, ", "); - } - put(sink, "]"); - } - - // https://issues.dlang.org/show_bug.cgi?id=20639 - // Separate @nogc test because public tests use array literals - // (and workarounds needlessly uglify those examples) - @system @nogc unittest - { - size_t[2] buffer; - BitArray b = BitArray(buffer[], buffer.sizeof * 8); - - b[] = true; - b[0 .. 1] = true; - b.flip(); - b.flip(1); - cast(void) b.count(); - } -} - -/// Slicing & bitsSet -@system unittest -{ - import std.algorithm.comparison : equal; - import std.range : iota; - - bool[] buf = new bool[64 * 3]; - buf[0 .. 64] = true; - BitArray b = BitArray(buf); - assert(b.bitsSet.equal(iota(0, 64))); - b <<= 64; - assert(b.bitsSet.equal(iota(64, 128))); -} - -/// Concatenation and appending -@system unittest -{ - import std.algorithm.comparison : equal; - - auto b = BitArray([1, 0]); - b ~= true; - assert(b[2] == 1); - b ~= BitArray([0, 1]); - auto c = BitArray([1, 0, 1, 0, 1]); - assert(b == c); - assert(b.bitsSet.equal([0, 2, 4])); -} - -/// Bit flipping -@system unittest -{ - import std.algorithm.comparison : equal; - - auto b = BitArray([1, 1, 0, 1]); - b &= BitArray([0, 1, 1, 0]); - assert(b.bitsSet.equal([1])); - b.flip; - assert(b.bitsSet.equal([0, 2, 3])); -} - -/// String format of bitarrays -@system unittest -{ - import std.format : format; - auto 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"); -} - -/// -@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"); -} - -@system unittest -{ - BitArray a; - a.length = 5; - foreach (ref bool b; a) - { - assert(b == 0); - b = 1; - } - foreach (bool b; a) - assert(b == 1); -} - -/++ - Swaps the endianness of the given integral value or character. - +/ -T swapEndian(T)(const T val) @safe pure nothrow @nogc -if (isIntegral!T || isSomeChar!T || isBoolean!T) -{ - import core.bitop : bswap, byteswap; - static if (val.sizeof == 1) - return val; - else static if (T.sizeof == 2) - return cast(T) byteswap(cast(ushort) val); - else static if (T.sizeof == 4) - return cast(T) bswap(cast(uint) val); - else static if (T.sizeof == 8) - return cast(T) bswap(cast(ulong) val); - else - static assert(0, T.stringof ~ " unsupported by swapEndian."); -} - -/// -@safe unittest -{ - assert(42.swapEndian == 704643072); - assert(42.swapEndian.swapEndian == 42); // reflexive - assert(1.swapEndian == 16777216); - - assert(true.swapEndian == true); - assert(byte(10).swapEndian == 10); - assert(char(10).swapEndian == 10); - - assert(ushort(10).swapEndian == 2560); - assert(long(10).swapEndian == 720575940379279360); - assert(ulong(10).swapEndian == 720575940379279360); -} - -@safe unittest -{ - import std.meta; - import std.stdio; - static foreach (T; AliasSeq!(bool, byte, ubyte, short, ushort, int, uint, long, ulong, char, wchar, dchar)) - {{ - scope(failure) writeln("Failed type: ", 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); - - // Check CTFE compiles. - static assert(swapEndian(swapEndian(T(1))) is T(1)); - - 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 https://issues.dlang.org/show_bug.cgi?id=6354 - 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) -{ - T value; - ubyte[T.sizeof] array; - - static if (is(immutable FloatingPointTypeOf!(T) == immutable float)) - uint intValue; - else static if (is(immutable FloatingPointTypeOf!(T) == immutable double)) - ulong intValue; - -} - -// Can't use EndianSwapper union during CTFE. -private auto ctfeRead(T)(const ubyte[T.sizeof] array) -if (__traits(isIntegral, T)) -{ - Unqual!T result; - version (LittleEndian) - foreach_reverse (b; array) - result = cast() cast(T) ((result << 8) | b); - else - foreach (b; array) - result = cast() cast(T) ((result << 8) | b); - return cast(T) result; -} - -// Can't use EndianSwapper union during CTFE. -private auto ctfeBytes(T)(const T value) -if (__traits(isIntegral, T)) -{ - ubyte[T.sizeof] result; - Unqual!T tmp = value; - version (LittleEndian) - { - foreach (i; 0 .. T.sizeof) - { - result[i] = cast(ubyte) tmp; - tmp = cast() cast(T) (tmp >>> 8); - } - } - else - { - foreach_reverse (i; 0 .. T.sizeof) - { - result[i] = cast(ubyte) tmp; - tmp = cast()(T) (tmp >>> 8); - } - } - return result; -} - -/++ - Converts the given value from the native endianness to big endian and - returns it as a `ubyte[n]` where `n` is the size of the given type. - - Returning a `ubyte[n]` helps prevent accidentally using a swapped value - as a regular one (and in the case of floating point values, it's necessary, - because the FPU will mess up any swapped floating point values. So, you - can't actually have swapped floating point values as floating point values). - - `real` is not supported, because its size is implementation-dependent - and therefore could vary from machine to machine (which could make it - unusable if you tried to transfer it to another machine). - +/ -auto nativeToBigEndian(T)(const T val) @safe pure nothrow @nogc -if (canSwapEndianness!T) -{ - version (LittleEndian) - return nativeToEndianImpl!true(val); - else - return nativeToEndianImpl!false(val); -} - -/// -@safe unittest -{ - int i = 12345; - ubyte[4] swappedI = nativeToBigEndian(i); - assert(i == bigEndianToNative!int(swappedI)); - - float f = 123.45f; - ubyte[4] swappedF = nativeToBigEndian(f); - assert(f == bigEndianToNative!float(swappedF)); - - const float cf = 123.45f; - ubyte[4] swappedCF = nativeToBigEndian(cf); - assert(cf == bigEndianToNative!float(swappedCF)); - - double d = 123.45; - ubyte[8] swappedD = nativeToBigEndian(d); - assert(d == bigEndianToNative!double(swappedD)); - - const double cd = 123.45; - ubyte[8] swappedCD = nativeToBigEndian(cd); - assert(cd == bigEndianToNative!double(swappedCD)); -} - -private auto nativeToEndianImpl(bool swap, T)(const T val) @safe pure nothrow @nogc -if (__traits(isIntegral, T)) -{ - if (!__ctfe) - { - static if (swap) - return EndianSwapper!T(swapEndian(val)).array; - else - return EndianSwapper!T(val).array; - } - else - { - // Can't use EndianSwapper in CTFE. - static if (swap) - return ctfeBytes(swapEndian(val)); - else - return ctfeBytes(val); - } -} - -@safe unittest -{ - import std.meta; - import std.stdio; - static 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) writeln("Failed type: ", 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); - - //Check CTFE compiles. - static assert(bigEndianToNative!T(nativeToBigEndian(T(1))) is T(1)); - - 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 `ubyte[n]` where `n` is the size - of the target type. You must give the target type as a template argument, - because there are multiple types with the same size and so the type of the - argument is not enough to determine the return type. - - Taking a `ubyte[n]` helps prevent accidentally using a swapped value - as a regular one (and in the case of floating point values, it's necessary, - because the FPU will mess up any swapped floating point values. So, you - can't actually have swapped floating point values as floating point values). - +/ -T bigEndianToNative(T, size_t n)(ubyte[n] val) @safe pure nothrow @nogc -if (canSwapEndianness!T && n == T.sizeof) -{ - version (LittleEndian) - return endianToNativeImpl!(true, T, n)(val); - else - return endianToNativeImpl!(false, 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)); -} - -/++ - Converts the given value from the native endianness to little endian and - returns it as a `ubyte[n]` where `n` is the size of the given type. - - Returning a `ubyte[n]` helps prevent accidentally using a swapped value - as a regular one (and in the case of floating point values, it's necessary, - because the FPU will mess up any swapped floating point values. So, you - can't actually have swapped floating point values as floating point values). - +/ -auto nativeToLittleEndian(T)(const T val) @safe pure nothrow @nogc -if (canSwapEndianness!T) -{ - version (BigEndian) - return nativeToEndianImpl!true(val); - else - return nativeToEndianImpl!false(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)); -} - -@safe unittest -{ - import std.meta; - import std.stdio; - static foreach (T; AliasSeq!(bool, byte, ubyte, short, ushort, int, uint, long, ulong, - char, wchar, dchar/*, - float, double*/)) - {{ - scope(failure) writeln("Failed type: ", 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); - - //Check CTFE compiles. - static assert(littleEndianToNative!T(nativeToLittleEndian(T(1))) is T(1)); - - 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 `ubyte[n]` where `n` is the size - of the target type. You must give the target type as a template argument, - because there are multiple types with the same size and so the type of the - argument is not enough to determine the return type. - - Taking a `ubyte[n]` helps prevent accidentally using a swapped value - as a regular one (and in the case of floating point values, it's necessary, - because the FPU will mess up any swapped floating point values. So, you - can't actually have swapped floating point values as floating point values). - - `real` is not supported, because its size is implementation-dependent - and therefore could vary from machine to machine (which could make it - unusable if you tried to transfer it to another machine). - +/ -T littleEndianToNative(T, size_t n)(ubyte[n] val) @safe pure nothrow @nogc -if (canSwapEndianness!T && n == T.sizeof) -{ - version (BigEndian) - return endianToNativeImpl!(true, T, n)(val); - else - return endianToNativeImpl!(false, T, n)(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 endianToNativeImpl(bool swap, T, size_t n)(ubyte[n] val) @nogc nothrow pure @trusted -if (__traits(isIntegral, T) && n == T.sizeof) -{ - if (!__ctfe) - { - EndianSwapper!T es = { array: val }; - static if (swap) - return swapEndian(es.value); - else - return es.value; - } - else - { - static if (swap) - return swapEndian(ctfeRead!T(val)); - else - return ctfeRead!T(val); - } -} - -private auto nativeToEndianImpl(bool swap, T)(const T val) @trusted pure nothrow @nogc -if (isFloatOrDouble!T) -{ - if (!__ctfe) - { - EndianSwapper!T es = EndianSwapper!T(val); - static if (swap) - es.intValue = swapEndian(es.intValue); - return es.array; - } - else - { - static if (T.sizeof == 4) - uint intValue = *cast(const uint*) &val; - else static if (T.sizeof == 8) - ulong intValue = *cast(const ulong*) & val; - static if (swap) - intValue = swapEndian(intValue); - return ctfeBytes(intValue); - } -} - -private auto endianToNativeImpl(bool swap, T, size_t n)(ubyte[n] val) @trusted pure nothrow @nogc -if (isFloatOrDouble!T && n == T.sizeof) -{ - if (!__ctfe) - { - EndianSwapper!T es = { array: val }; - static if (swap) - es.intValue = swapEndian(es.intValue); - return es.value; - } - else - { - static if (n == 4) - uint x = ctfeRead!uint(val); - else static if (n == 8) - ulong x = ctfeRead!ulong(val); - static if (swap) - x = swapEndian(x); - return *cast(T*) &x; - } -} - -private template isFloatOrDouble(T) -{ - enum isFloatOrDouble = isFloatingPoint!T && - !is(immutable FloatingPointTypeOf!T == immutable real); -} - -@safe unittest -{ - import std.meta; - static 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; - static 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))); - } - - //! - static 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 `ubyte`s and converts the first `T.sizeof` bytes to - `T`. The value returned is converted from the given endianness to the - native endianness. The range is not consumed. - - Params: - T = The integral type to convert the first `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 `hasSlicing!R` is `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, "index must not point to null"); - - 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); -} - -/// -@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); -} - -@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())); - } -} - -/++ - Takes a range of `ubyte`s and converts the first `T.sizeof` bytes to - `T`. The value returned is converted from the given endianness to the - native endianness. The `T.sizeof` bytes which are read are consumed from - the range. - - Params: - T = The integral type to convert the first `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())); - } -} - -// https://issues.dlang.org/show_bug.cgi?id=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; } - alias opDollar = 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 `ubyte`s as a sequence of `T.sizeof` `ubyte`s - starting at index. `hasSlicing!R` must be `true`. - - Params: - T = The integral type to convert the first `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, const 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, const T value, size_t* index) -if (canSwapEndianness!T && - isForwardRange!R && - hasSlicing!R && - is(ElementType!R : ubyte)) -{ - assert(index, "index must not point to null"); - - 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]); -} - -/// -@system unittest -{ - 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]); -} - -/// -@system unittest -{ - 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); -} - -/// bool -@system unittest -{ - 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(8-bit) -@system unittest -{ - 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) -@system unittest -{ - 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) -@system unittest -{ - 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) -@system unittest -{ - 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) -@system unittest -{ - 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 -@system unittest -{ - 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 -@system unittest -{ - 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 -@system unittest -{ - 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 -@system unittest -{ - 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 -@system unittest -{ - 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 `ubyte`s (using `put`) as a sequence of - `T.sizeof` `ubyte`s starting at index. `hasSlicing!R` must be - `true`. - - Params: - T = The integral type to convert the first `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, const 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]); -} - -/// bool -@safe unittest -{ - import std.array : appender; - 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 -@safe unittest -{ - import std.array : appender; - 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 -@safe unittest -{ - import std.array : appender; - 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 -@safe unittest -{ - import std.array : appender; - 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 -@safe unittest -{ - import std.array : appender; - 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 -@safe unittest -{ - import std.array : appender; - 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 -@safe unittest -{ - import std.array : appender; - 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 -@safe unittest -{ - import std.array : appender; - auto buffer = appender!(const ubyte[])(); - - enum Real: real - { - one = 32.0, - two = 25.0 - } - - static assert(!__traits(compiles, buffer.append!Real(Real.one))); -} - -@system unittest -{ - import std.array; - import std.format : format; - import std.meta : AliasSeq; - static foreach (endianness; [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; - static foreach (T; Types) - { - toWrite.append!(T, endianness)(cast(T) values[index++]); - length += T.sizeof; - } - - auto toRead = toWrite.data; - assert(toRead.length == length); - - index = 0; - static 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 `value`. -For signed integers, the sign bit is included in the count. -*/ -private uint countBitsSet(T)(const T value) -if (isIntegral!T) -{ - static if (T.sizeof == 8) - { - import core.bitop : popcnt; - const c = popcnt(cast(ulong) value); - } - else static if (T.sizeof == 4) - { - import core.bitop : popcnt; - const c = popcnt(cast(uint) value); - } - // http://graphics.stanford.edu/~seander/bithacks.html#CountBitsSetParallel - 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; - static 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); - } - // Check CTFE compiles. - static assert(countBitsSet(cast(T) 1) == 1); - } - 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() const - { - 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 BitsSet save() const - { - return this; - } - - @property size_t length() const - { - return countBitsSet(_value); - } - - private T _value; - private size_t _index; -} - -/** -Range that iterates the indices of the set bits in `value`. -Index 0 corresponds to the least significant bit. -For signed integers, the highest index corresponds to the sign bit. -*/ -auto bitsSet(T)(const 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; - static 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])); -} diff --git a/phobos/std/checkedint.d b/phobos/std/checkedint.d deleted file mode 100644 index cec1dc1..0000000 --- a/phobos/std/checkedint.d +++ /dev/null @@ -1,3591 +0,0 @@ -// Written in the D programming language. -/** -$(SCRIPT inhibitQuickIndex = 1;) - -This module defines facilities for efficient checking of integral operations -against overflow, casting with loss of precision, unexpected change of sign, -etc. The checking (and possibly correction) can be done at operation level, for -example $(LREF opChecked)$(D !"+"(x, y, overflow)) adds two integrals `x` and -`y` and sets `overflow` to `true` if an overflow occurred. The flag `overflow` -(a `bool` passed by reference) is not touched if the operation succeeded, so the -same flag can be reused for a sequence of operations and tested at the end. - -Issuing individual checked operations is flexible and efficient but often -tedious. The $(LREF Checked) facility offers encapsulated integral wrappers that -do all checking internally and have configurable behavior upon erroneous -results. For example, `Checked!int` is a type that behaves like `int` but aborts -execution immediately whenever involved in an operation that produces the -arithmetically wrong result. The accompanying convenience function $(LREF -checked) uses type deduction to convert a value `x` of integral type `T` to -`Checked!T` by means of `checked(x)`. For example: - ---- -void main() -{ - import std.checkedint, std.stdio; - writeln((checked(5) + 7).get); // 12 - writeln((checked(10) * 1000 * 1000 * 1000).get); // Overflow -} ---- - -Similarly, $(D checked(-1) > uint(0)) aborts execution (even though the built-in -comparison $(D int(-1) > uint(0)) is surprisingly true due to language's -conversion rules modeled after C). Thus, `Checked!int` is a virtually drop-in -replacement for `int` useable in debug builds, to be replaced by `int` in -release mode if efficiency demands it. - -`Checked` has customizable behavior with the help of a second type parameter, -`Hook`. Depending on what methods `Hook` defines, core operations on the -underlying integral may be verified for overflow or completely redefined. If -`Hook` defines no method at all and carries no state, there is no change in -behavior, i.e. $(D Checked!(int, void)) is a wrapper around `int` that adds no -customization at all. - -This module provides a few predefined hooks (below) that add useful behavior to -`Checked`: - -$(BOOKTABLE , - $(TR $(TD $(LREF Abort)) $(TD - fails every incorrect operation with a message to $(REF - stderr, std, stdio) followed by a call to `assert(0)`. It is the default - second parameter, i.e. `Checked!short` is the same as - $(D Checked!(short, Abort)). - )) - $(TR $(TD $(LREF Throw)) $(TD - fails every incorrect operation by throwing an exception. - )) - $(TR $(TD $(LREF Warn)) $(TD - prints incorrect operations to $(REF stderr, std, stdio) - but otherwise preserves the built-in behavior. - )) - $(TR $(TD $(LREF ProperCompare)) $(TD - fixes the comparison operators `==`, `!=`, `<`, `<=`, `>`, and `>=` - to return correct results in all circumstances, - at a slight cost in efficiency. For example, - $(D Checked!(uint, ProperCompare)(1) > -1) is `true`, - which is not the case for the built-in comparison. Also, comparing - numbers for equality with floating-point numbers only passes if the - integral can be converted to the floating-point number precisely, - so as to preserve transitivity of equality. - )) - $(TR $(TD $(LREF WithNaN)) $(TD - reserves a special "Not a Number" (NaN) value akin to the homonym value - reserved for floating-point values. Once a $(D Checked!(X, WithNaN)) - gets this special value, it preserves and propagates it until - reassigned. $(LREF isNaN) can be used to query whether the object - is not a number. - )) - $(TR $(TD $(LREF Saturate)) $(TD - implements saturating arithmetic, i.e. $(D Checked!(int, Saturate)) - "stops" at `int.max` for all operations that would cause an `int` to - overflow toward infinity, and at `int.min` for all operations that would - correspondingly overflow toward negative infinity. - )) -) - - -These policies may be used alone, e.g. $(D Checked!(uint, WithNaN)) defines a -`uint`-like type that reaches a stable NaN state for all erroneous operations. -They may also be "stacked" on top of each other, owing to the property that a -checked integral emulates an actual integral, which means another checked -integral can be built on top of it. Some combinations of interest include: - -$(BOOKTABLE , - $(TR $(TD $(D Checked!(Checked!int, ProperCompare)))) - $(TR $(TD -defines an `int` with fixed -comparison operators that will fail with `assert(0)` upon overflow. (Recall that -`Abort` is the default policy.) The order in which policies are combined is -important because the outermost policy (`ProperCompare` in this case) has the -first crack at intercepting an operator. The converse combination $(D -Checked!(Checked!(int, ProperCompare))) is meaningless because `Abort` will -intercept comparison and will fail without giving `ProperCompare` a chance to -intervene. - )) - $(TR $(TD)) - $(TR $(TDNW $(D Checked!(Checked!(int, ProperCompare), WithNaN)))) - $(TR $(TD -defines an `int`-like -type that supports a NaN value. For values that are not NaN, comparison works -properly. Again the composition order is important; $(D Checked!(Checked!(int, -WithNaN), ProperCompare)) does not have good semantics because `ProperCompare` -intercepts comparisons before the numbers involved are tested for NaN. - )) -) - -The hook's members are looked up statically in a Design by Introspection manner -and are all optional. The table below illustrates the members that a hook type -may define and their influence over the behavior of the `Checked` type using it. -In the table, `hook` is an alias for `Hook` if the type `Hook` does not -introduce any state, or an object of type `Hook` otherwise. - -$(TABLE , -$(TR $(TH `Hook` member) $(TH Semantics in $(D Checked!(T, Hook))) -) -$(TR $(TD `defaultValue`) $(TD If defined, `Hook.defaultValue!T` is used as the -default initializer of the payload.) -) -$(TR $(TD `min`) $(TD If defined, `Hook.min!T` is used as the minimum value of -the payload.) -) -$(TR $(TD `max`) $(TD If defined, `Hook.max!T` is used as the maximum value of -the payload.) -) -$(TR $(TD `hookOpCast`) $(TD If defined, `hook.hookOpCast!U(get)` is forwarded -to unconditionally when the payload is to be cast to type `U`.) -) -$(TR $(TD `onBadCast`) $(TD If defined and `hookOpCast` is $(I not) defined, -`onBadCast!U(get)` is forwarded to when the payload is to be cast to type `U` -and the cast would lose information or force a change of sign.) -) -$(TR $(TD `hookOpEquals`) $(TD If defined, $(D hook.hookOpEquals(get, rhs)) is -forwarded to unconditionally when the payload is compared for equality against -value `rhs` of integral, floating point, or Boolean type.) -) -$(TR $(TD `hookOpCmp`) $(TD If defined, $(D hook.hookOpCmp(get, rhs)) is -forwarded to unconditionally when the payload is compared for ordering against -value `rhs` of integral, floating point, or Boolean type.) -) -$(TR $(TD `hookOpUnary`) $(TD If defined, `hook.hookOpUnary!op(get)` (where `op` -is the operator symbol) is forwarded to for unary operators `-` and `~`. In -addition, for unary operators `++` and `--`, `hook.hookOpUnary!op(payload)` is -called, where `payload` is a reference to the value wrapped by `Checked` so the -hook can change it.) -) -$(TR $(TD `hookOpBinary`) $(TD If defined, $(D hook.hookOpBinary!op(get, rhs)) -(where `op` is the operator symbol and `rhs` is the right-hand side operand) is -forwarded to unconditionally for binary operators `+`, `-`, `*`, `/`, `%`, -`^^`, `&`, `|`, `^`, `<<`, `>>`, and `>>>`.) -) -$(TR $(TD `hookOpBinaryRight`) $(TD If defined, $(D -hook.hookOpBinaryRight!op(lhs, get)) (where `op` is the operator symbol and -`lhs` is the left-hand side operand) is forwarded to unconditionally for binary -operators `+`, `-`, `*`, `/`, `%`, `^^`, `&`, `|`, `^`, `<<`, `>>`, and `>>>`.) -) -$(TR $(TD `onOverflow`) $(TD If defined, `hook.onOverflow!op(get)` is forwarded -to for unary operators that overflow but only if `hookOpUnary` is not defined. -Unary `~` does not overflow; unary `-` overflows only when the most negative -value of a signed type is negated, and the result of the hook call is returned. -When the increment or decrement operators overflow, the payload is assigned the -result of `hook.onOverflow!op(get)`. When a binary operator overflows, the -result of $(D hook.onOverflow!op(get, rhs)) is returned, but only if `Hook` does -not define `hookOpBinary`.) -) -$(TR $(TD `hookOpOpAssign`) $(TD If defined, $(D hook.hookOpOpAssign!op(payload, -rhs)) (where `op` is the operator symbol and `rhs` is the right-hand side -operand) is forwarded to unconditionally for binary operators `+=`, `-=`, `*=`, `/=`, `%=`, -`^^=`, `&=`, `|=`, `^=`, `<<=`, `>>=`, and `>>>=`.) -) -$(TR $(TD `onLowerBound`) $(TD If defined, $(D hook.onLowerBound(value, bound)) -(where `value` is the value being assigned) is forwarded to when the result of -binary operators `+=`, `-=`, `*=`, `/=`, `%=`, `^^=`, `&=`, `|=`, `^=`, `<<=`, `>>=`, -and `>>>=` is smaller than the smallest value representable by `T`.) -) -$(TR $(TD `onUpperBound`) $(TD If defined, $(D hook.onUpperBound(value, bound)) -(where `value` is the value being assigned) is forwarded to when the result of -binary operators `+=`, `-=`, `*=`, `/=`, `%=`, `^^=`, `&=`, `|=`, `^=`, `<<=`, `>>=`, -and `>>>=` is larger than the largest value representable by `T`.) -) -$(TR $(TD `hookToHash`) $(TD If defined, $(D hook.hookToHash(payload)) -(where `payload` is a reference to the value wrapped by Checked) is forwarded -to when `toHash` is called on a Checked type. Custom hashing can be implemented -in a `Hook`, otherwise the built-in hashing is used.) -) -) - -Source: $(PHOBOSSRC std/checkedint.d) -*/ -module std.checkedint; -import std.traits : isFloatingPoint, isIntegral, isNumeric, isUnsigned, Unqual; - -/// -@safe unittest -{ - int[] concatAndAdd(int[] a, int[] b, int offset) - { - // Aborts on overflow on size computation - auto r = new int[(checked(a.length) + b.length).get]; - // Aborts on overflow on element computation - foreach (i; 0 .. a.length) - r[i] = (a[i] + checked(offset)).get; - foreach (i; 0 .. b.length) - r[i + a.length] = (b[i] + checked(offset)).get; - return r; - } - assert(concatAndAdd([1, 2, 3], [4, 5], -1) == [0, 1, 2, 3, 4]); -} - - -/// `Saturate` stops at an overflow -@safe unittest -{ - auto x = (cast(byte) 127).checked!Saturate; - assert(x == 127); - x++; - assert(x == 127); -} - -/// `WithNaN` has a special "Not a Number" (NaN) value akin to the homonym value reserved for floating-point values -@safe unittest -{ - auto x = 100.checked!WithNaN; - assert(x == 100); - x /= 0; - assert(x.isNaN); -} - -/// `ProperCompare` fixes the comparison operators ==, !=, <, <=, >, and >= to return correct results -@safe unittest -{ - uint x = 1; - auto y = x.checked!ProperCompare; - assert(x < -1); // built-in comparison - assert(y > -1); // ProperCompare -} - -/// `Throw` fails every incorrect operation by throwing an exception -@safe unittest -{ - import std.exception : assertThrown; - auto x = -1.checked!Throw; - assertThrown(x / 0); - assertThrown(x + int.min); - assertThrown(x == uint.max); -} - -/** -Checked integral type wraps an integral `T` and customizes its behavior with the -help of a `Hook` type. The type wrapped must be one of the predefined integrals -(unqualified), or another instance of `Checked`. - -Params: - T = type that is wrapped in the `Checked` type - Hook = hook type that customizes the behavior of the `Checked` type -*/ -struct Checked(T, Hook = Abort) -if (isIntegral!T || is(T == Checked!(U, H), U, H)) -{ - import std.algorithm.comparison : among; - import std.experimental.allocator.common : stateSize; - import std.format.spec : FormatSpec; - import std.range.primitives : isInputRange, ElementType; - import std.traits : hasMember, isSomeChar; - - /** - The type of the integral subject to checking. - */ - alias Representation = T; - - // state { - static if (hasMember!(Hook, "defaultValue")) - private T payload = Hook.defaultValue!T; - else - private T payload; - /** - `hook` is a member variable if it has state, or an alias for `Hook` - otherwise. - */ - static if (stateSize!Hook > 0) Hook hook; - else alias hook = Hook; - // } state - - // get - /** - Returns: - A copy of the underlying value. - */ - auto get() inout { return payload; } - /// - @safe unittest - { - auto x = checked(ubyte(42)); - static assert(is(typeof(x.get()) == ubyte)); - assert(x.get == 42); - const y = checked(ubyte(42)); - static assert(is(typeof(y.get()) == const ubyte)); - assert(y.get == 42); - } - - /** - Defines the minimum and maximum. These values are hookable by defining - `Hook.min` and/or `Hook.max`. - */ - static if (hasMember!(Hook, "min")) - { - enum Checked!(T, Hook) min = Checked!(T, Hook)(Hook.min!T); - /// - @safe unittest - { - assert(Checked!short.min == -32768); - assert(Checked!(short, WithNaN).min == -32767); - assert(Checked!(uint, WithNaN).max == uint.max - 1); - } - } - else - { - /// ditto - enum Checked!(T, Hook) min = Checked(T.min); - } - static if (hasMember!(Hook, "max")) - { - /// ditto - enum Checked!(T, Hook) max = Checked(Hook.max!T); - } - else - { - /// ditto - enum Checked!(T, Hook) max = Checked(T.max); - } - - /** - Constructor taking a value properly convertible to the underlying type. `U` - may be either an integral that can be converted to `T` without a loss, or - another `Checked` instance whose representation may be in turn converted to - `T` without a loss. - */ - this(U)(U rhs) - if (valueConvertible!(U, T) || - !isIntegral!T && is(typeof(T(rhs))) || - is(U == Checked!(V, W), V, W) && - is(typeof(Checked!(T, Hook)(rhs.get)))) - { - static if (isIntegral!U) - payload = rhs; - else - payload = rhs.payload; - } - /// - @safe unittest - { - auto a = checked(42L); - assert(a == 42); - auto b = Checked!long(4242); // convert 4242 to long - assert(b == 4242); - } - - /** - Assignment operator. Has the same constraints as the constructor. - - Params: - rhs = The value to assign - - Returns: - A reference to `this` - */ - ref Checked opAssign(U)(U rhs) return - if (is(typeof(Checked!(T, Hook)(rhs)))) - { - static if (isIntegral!U) - payload = rhs; - else - payload = rhs.payload; - return this; - } - /// - @safe unittest - { - Checked!long a; - a = 42L; - assert(a == 42); - a = 4242; - assert(a == 4242); - } - - /// - @safe unittest - { - Checked!long a, b; - a = b = 3; - assert(a == 3 && b == 3); - } - - /** - Construct from a decimal string. The conversion follows the same rules as - $(REF to, std, conv) converting a string to the wrapped `T` type. - - Params: - str = an $(REF_ALTTEXT input range, isInputRange, std,range,primitives) - of characters - */ - this(Range)(Range str) - if (isInputRange!Range && isSomeChar!(ElementType!Range)) - { - import std.conv : to; - - this(to!T(str)); - } - - /** - $(REF to, std, conv) can convert a string to a `Checked!T`: - */ - @system unittest - { - import std.conv : to; - - const a = to!long("1234"); - const b = to!(Checked!long)("1234"); - assert(a == b); - } - - // opCast - /** - Casting operator to integral, `bool`, or floating point type. - - If a cast to a floating-point type is requested and `Hook` defines - `onBadCast`, the cast is verified by ensuring $(D get == cast(T) - U(get)). If that is not `true`, `hook.onBadCast!U(get)` is returned. - - If a cast to an integral type is requested and `Hook` defines `onBadCast`, - the cast is verified by ensuring `get` and $(D cast(U) - get) are the same arithmetic number. (Note that `int(-1)` and - `uint(1)` are different values arithmetically although they have the same - bitwise representation and compare equal by language rules.) If the numbers - are not arithmetically equal, `hook.onBadCast!U(get)` is - returned. - - Params: - U = The type to cast to - - Returns: - If `Hook` defines `hookOpCast`, the call immediately returns - `hook.hookOpCast!U(get)`. Otherwise, casting to `bool` yields $(D - get != 0) and casting to another integral that can represent all - values of `T` returns `get` promoted to `U`. - */ - U opCast(U, this _)() - if (isIntegral!U || isFloatingPoint!U || is(U == bool)) - { - static if (hasMember!(Hook, "hookOpCast")) - { - return hook.hookOpCast!U(payload); - } - else static if (is(U == bool)) - { - return payload != 0; - } - else static if (valueConvertible!(T, U)) - { - return payload; - } - // may lose bits or precision - else static if (!hasMember!(Hook, "onBadCast")) - { - return cast(U) payload; - } - else - { - if (isUnsigned!T || !isUnsigned!U || - T.sizeof > U.sizeof || payload >= 0) - { - auto result = cast(U) payload; - // If signedness is different, we need additional checks - if (result == payload && - (!isUnsigned!T || isUnsigned!U || result >= 0)) - return result; - } - return hook.onBadCast!U(payload); - } - } - /// - @safe unittest - { - assert(cast(uint) checked(42) == 42); - assert(cast(uint) checked!WithNaN(-42) == uint.max); - } - - // opEquals - /** - Compares `this` against `rhs` for equality. - - If `U` is also an instance of `Checked`, both hooks (left- and right-hand - side) are introspected for the method `hookOpEquals`. If both define it, - priority is given to the left-hand side. - - Params: - rhs = Right-hand side to compare for equality - - Returns: - If `Hook` defines `hookOpEquals`, the function forwards to $(D - hook.hookOpEquals(get, rhs)). Otherwise, the result of the - built-in operation $(D get == rhs) is returned. - - */ - bool opEquals(U, this _)(U rhs) - if (isIntegral!U || isFloatingPoint!U || is(U == bool) || - is(U == Checked!(V, W), V, W) && is(typeof(this == rhs.payload))) - { - static if (is(U == Checked!(V, W), V, W)) - { - alias R = typeof(payload + rhs.payload); - static if (is(Hook == W)) - { - // Use the lhs hook if there - return this == rhs.payload; - } - else static if (valueConvertible!(T, R) && valueConvertible!(V, R)) - { - return payload == rhs.payload; - } - else static if (hasMember!(Hook, "hookOpEquals")) - { - return hook.hookOpEquals(payload, rhs.payload); - } - else static if (hasMember!(W, "hookOpEquals")) - { - return rhs.hook.hookOpEquals(rhs.payload, payload); - } - else - { - return payload == rhs.payload; - } - } - else static if (hasMember!(Hook, "hookOpEquals")) - return hook.hookOpEquals(payload, rhs); - else static if (isIntegral!U || isFloatingPoint!U || is(U == bool)) - return payload == rhs; - } - - /// - static if (is(T == int) && is(Hook == void)) @safe unittest - { - import std.traits : isUnsigned; - - static struct MyHook - { - static bool thereWereErrors; - static bool hookOpEquals(L, R)(L lhs, R rhs) - { - if (lhs != rhs) return false; - static if (isUnsigned!L && !isUnsigned!R) - { - if (lhs > 0 && rhs < 0) thereWereErrors = true; - } - else static if (isUnsigned!R && !isUnsigned!L) - if (lhs < 0 && rhs > 0) thereWereErrors = true; - // Preserve built-in behavior. - return true; - } - } - auto a = checked!MyHook(-42); - assert(a == uint(-42)); - assert(MyHook.thereWereErrors); - MyHook.thereWereErrors = false; - assert(checked!MyHook(uint(-42)) == -42); - assert(MyHook.thereWereErrors); - static struct MyHook2 - { - static bool hookOpEquals(L, R)(L lhs, R rhs) - { - return lhs == rhs; - } - } - MyHook.thereWereErrors = false; - assert(checked!MyHook2(uint(-42)) == a); - // Hook on left hand side takes precedence, so no errors - assert(!MyHook.thereWereErrors); - } - - // toHash - /** - Generates a hash for `this`. If `Hook` defines `hookToHash`, the call - immediately returns `hook.hookToHash(payload)`. If `Hook` does not - implement `hookToHash`, but it has state, a hash will be generated for - the `Hook` using the built-in function and it will be xored with the - hash of the `payload`. - - Returns: - The hash of `this` instance. - - */ - size_t toHash() const nothrow @safe - { - static if (hasMember!(Hook, "hookToHash")) - { - return hook.hookToHash(payload); - } - else static if (stateSize!Hook > 0) - { - static if (hasMember!(typeof(payload), "toHash")) - { - return payload.toHash() ^ hashOf(hook); - } - else - { - return hashOf(payload) ^ hashOf(hook); - } - } - else static if (hasMember!(typeof(payload), "toHash")) - { - return payload.toHash(); - } - else - { - return .hashOf(payload); - } - } - - /// ditto - size_t toHash(this _)() shared const nothrow @safe - { - import core.atomic : atomicLoad, MemoryOrder; - static if (is(typeof(this.payload.atomicLoad!(MemoryOrder.acq)) P)) - { - auto localPayload = __ctfe ? cast(P) this.payload - : this.payload.atomicLoad!(MemoryOrder.acq); - } - else - { - alias localPayload = this.payload; - } - - static if (hasMember!(Hook, "hookToHash")) - { - return hook.hookToHash(localPayload); - } - else static if (stateSize!Hook > 0) - { - static if (hasMember!(typeof(localPayload), "toHash")) - { - return localPayload.toHash() ^ hashOf(hook); - } - else - { - return hashOf(localPayload) ^ hashOf(hook); - } - } - else static if (hasMember!(typeof(localPayload), "toHash")) - { - return localPayload.toHash(); - } - else - { - return .hashOf(localPayload); - } - } - - /** - Writes a string representation of this to a `sink`. - - Params: - sink = A `Char` accepting - $(REF_ALTTEXT output range, isOutputRange, std,range,primitives). - fmt = A $(REF FormatSpec, std, format) which controls how this - is formatted. - */ - void toString(Writer, Char)(scope ref Writer sink, scope const ref FormatSpec!Char fmt) const - { - import std.format.write : formatValue; - if (fmt.spec == 's') - return formatValue(sink, this, fmt); - else - return formatValue(sink, payload, fmt); - } - - /** - `toString` is rarely directly invoked; the usual way of using it is via - $(REF format, std, format): - */ - @system unittest - { - import std.format; - - assert(format("%04d", checked(15)) == "0015"); - assert(format("0x%02x", checked(15)) == "0x0f"); - } - - // opCmp - /** - - Compares `this` against `rhs` for ordering. If `Hook` defines `hookOpCmp`, - the function forwards to $(D hook.hookOpCmp(get, rhs)). Otherwise, the - result of the built-in comparison operation is returned. - - If `U` is also an instance of `Checked`, both hooks (left- and right-hand - side) are introspected for the method `hookOpCmp`. If both define it, - priority is given to the left-hand side. - - Params: - rhs = The right-hand side operand - U = either the type of `rhs` or the underlying type - if `rhs` is a `Checked` instance - Hook1 = If `rhs` is a `Checked` instance, `Hook1` represents - the instance's behavior hook - - Returns: - The result of `hookOpCmp` if `hook` defines `hookOpCmp`. If - `U` is an instance of `Checked` and `hook` does not define - `hookOpCmp`, result of `rhs.hook.hookOpCmp` is returned. - If none of the instances specify the behavior via `hookOpCmp`, - `-1` is returned if `lhs` is lesser than `rhs`, `1` if `lhs` - is greater than `rhs` and `0` on equality. - */ - auto opCmp(U, this _)(const U rhs) //const pure @safe nothrow @nogc - if (isIntegral!U || isFloatingPoint!U || is(U == bool)) - { - static if (hasMember!(Hook, "hookOpCmp")) - { - return hook.hookOpCmp(payload, rhs); - } - else static if (valueConvertible!(T, U) || valueConvertible!(U, T)) - { - return payload < rhs ? -1 : payload > rhs; - } - else static if (isFloatingPoint!U) - { - U lhs = payload; - return lhs < rhs ? U(-1.0) - : lhs > rhs ? U(1.0) - : lhs == rhs ? U(0.0) : U.init; - } - else - { - return payload < rhs ? -1 : payload > rhs; - } - } - - /// ditto - auto opCmp(U, Hook1, this _)(Checked!(U, Hook1) rhs) - { - alias R = typeof(payload + rhs.payload); - static if (valueConvertible!(T, R) && valueConvertible!(U, R)) - { - return payload < rhs.payload ? -1 : payload > rhs.payload; - } - else static if (is(Hook == Hook1)) - { - // Use the lhs hook - return this.opCmp(rhs.payload); - } - else static if (hasMember!(Hook, "hookOpCmp")) - { - return hook.hookOpCmp(get, rhs.get); - } - else static if (hasMember!(Hook1, "hookOpCmp")) - { - return -rhs.hook.hookOpCmp(rhs.payload, get); - } - else - { - return payload < rhs.payload ? -1 : payload > rhs.payload; - } - } - - /// - static if (is(T == int) && is(Hook == void)) @safe unittest - { - import std.traits : isUnsigned; - - static struct MyHook - { - static bool thereWereErrors; - static int hookOpCmp(L, R)(L lhs, R rhs) - { - static if (isUnsigned!L && !isUnsigned!R) - { - if (rhs < 0 && rhs >= lhs) - thereWereErrors = true; - } - else static if (isUnsigned!R && !isUnsigned!L) - { - if (lhs < 0 && lhs >= rhs) - thereWereErrors = true; - } - // Preserve built-in behavior. - return lhs < rhs ? -1 : lhs > rhs; - } - } - auto a = checked!MyHook(-42); - assert(a > uint(42)); - assert(MyHook.thereWereErrors); - static struct MyHook2 - { - static int hookOpCmp(L, R)(L lhs, R rhs) - { - // Default behavior - return lhs < rhs ? -1 : lhs > rhs; - } - } - MyHook.thereWereErrors = false; - assert(Checked!(uint, MyHook2)(uint(-42)) <= a); - //assert(Checked!(uint, MyHook2)(uint(-42)) >= a); - // Hook on left hand side takes precedence, so no errors - assert(!MyHook.thereWereErrors); - assert(a <= Checked!(uint, MyHook2)(uint(-42))); - assert(MyHook.thereWereErrors); - } - - // For coverage - static if (is(T == int) && is(Hook == void)) @safe unittest - { - assert(checked(42) <= checked!void(42)); - assert(checked!void(42) <= checked(42u)); - assert(checked!void(42) <= checked!(void*)(42u)); - } - - // opUnary - /** - - Defines unary operators `+`, `-`, `~`, `++`, and `--`. Unary `+` is not - overridable and always has built-in behavior (returns `this`). For the - others, if `Hook` defines `hookOpUnary`, `opUnary` forwards to $(D - Checked!(typeof(hook.hookOpUnary!op(get)), - Hook)(hook.hookOpUnary!op(get))). - - If `Hook` does not define `hookOpUnary` but defines `onOverflow`, `opUnary` - forwards to `hook.onOverflow!op(get)` in case an overflow occurs. - For `++` and `--`, the payload is assigned from the result of the call to - `onOverflow`. - - Note that unary `-` is considered to overflow if `T` is a signed integral of - 32 or 64 bits and is equal to the most negative value. This is because that - value has no positive negation. - - Params: - op = The unary operator - - Returns: - A `Checked` instance representing the result of the unary - operation - */ - auto opUnary(string op, this _)() - if (op == "+" || op == "-" || op == "~") - { - static if (op == "+") - return Checked(this); // "+" is not hookable - else static if (hasMember!(Hook, "hookOpUnary")) - { - auto r = hook.hookOpUnary!op(payload); - return Checked!(typeof(r), Hook)(r); - } - else static if (op == "-" && isIntegral!T && T.sizeof >= 4 && - !isUnsigned!T && hasMember!(Hook, "onOverflow")) - { - static assert(is(typeof(-payload) == typeof(payload))); - bool overflow; - import core.checkedint : negs; - auto r = negs(payload, overflow); - if (overflow) r = hook.onOverflow!op(payload); - return Checked(r); - } - else - return Checked(mixin(op ~ "payload")); - } - - /// ditto - ref Checked opUnary(string op)() return - if (op == "++" || op == "--") - { - static if (hasMember!(Hook, "hookOpUnary")) - hook.hookOpUnary!op(payload); - else static if (hasMember!(Hook, "onOverflow")) - { - static if (op == "++") - { - if (payload == max.payload) - payload = hook.onOverflow!"++"(payload); - else - ++payload; - } - else - { - if (payload == min.payload) - payload = hook.onOverflow!"--"(payload); - else - --payload; - } - } - else - mixin(op ~ "payload;"); - return this; - } - - /// - static if (is(T == int) && is(Hook == void)) @safe unittest - { - static struct MyHook - { - static bool thereWereErrors; - static L hookOpUnary(string x, L)(L lhs) - { - if (x == "-" && lhs == -lhs) thereWereErrors = true; - return -lhs; - } - } - auto a = checked!MyHook(long.min); - assert(a == -a); - assert(MyHook.thereWereErrors); - auto b = checked!void(42); - assert(++b == 43); - } - - // opBinary - /** - - Defines binary operators `+`, `-`, `*`, `/`, `%`, `^^`, `&`, `|`, `^`, `<<`, `>>`, - and `>>>`. If `Hook` defines `hookOpBinary`, `opBinary` forwards to $(D - Checked!(typeof(hook.hookOpBinary!op(get, rhs)), - Hook)(hook.hookOpBinary!op(get, rhs))). - - If `Hook` does not define `hookOpBinary` but defines `onOverflow`, - `opBinary` forwards to `hook.onOverflow!op(get, rhs)` in case an - overflow occurs. - - If two `Checked` instances are involved in a binary operation and both - define `hookOpBinary`, the left-hand side hook has priority. If both define - `onOverflow`, a compile-time error occurs. - - Params: - op = The binary operator - rhs = The right hand side operand - U = If `rhs` is a `Checked` instance, `U` represents - the underlying instance type - Hook1 = If `rhs` is a `Checked` instance, `Hook1` represents - the instance's behavior hook - - Returns: - A `Checked` instance representing the result of the binary - operation - */ - auto opBinary(string op, Rhs)(const Rhs rhs) - if (isIntegral!Rhs || isFloatingPoint!Rhs || is(Rhs == bool)) - { - return opBinaryImpl!(op, Rhs, typeof(this))(rhs); - } - - /// ditto - auto opBinary(string op, Rhs)(const Rhs rhs) const - if (isIntegral!Rhs || isFloatingPoint!Rhs || is(Rhs == bool)) - { - return opBinaryImpl!(op, Rhs, typeof(this))(rhs); - } - - private auto opBinaryImpl(string op, Rhs, this _)(const Rhs rhs) - { - alias R = typeof(mixin("payload" ~ op ~ "rhs")); - static assert(is(typeof(mixin("payload" ~ op ~ "rhs")) == R)); - static if (isIntegral!R) alias Result = Checked!(R, Hook); - else alias Result = R; - - static if (hasMember!(Hook, "hookOpBinary")) - { - auto r = hook.hookOpBinary!op(payload, rhs); - return Checked!(typeof(r), Hook)(r); - } - else static if (is(Rhs == bool)) - { - return mixin("this" ~ op ~ "ubyte(rhs)"); - } - else static if (isFloatingPoint!Rhs) - { - return mixin("payload" ~ op ~ "rhs"); - } - else static if (hasMember!(Hook, "onOverflow")) - { - bool overflow; - auto r = opChecked!op(payload, rhs, overflow); - if (overflow) r = hook.onOverflow!op(payload, rhs); - return Result(r); - } - else - { - // Default is built-in behavior - return Result(mixin("payload" ~ op ~ "rhs")); - } - } - - /// ditto - auto opBinary(string op, U, Hook1)(Checked!(U, Hook1) rhs) - { - return opBinaryImpl2!(op, U, Hook1, typeof(this))(rhs); - } - - /// ditto - auto opBinary(string op, U, Hook1)(Checked!(U, Hook1) rhs) const - { - return opBinaryImpl2!(op, U, Hook1, typeof(this))(rhs); - } - - private - auto opBinaryImpl2(string op, U, Hook1, this _)(Checked!(U, Hook1) rhs) - { - alias R = typeof(get + rhs.payload); - static if (valueConvertible!(T, R) && valueConvertible!(U, R) || - is(Hook == Hook1)) - { - // Delegate to lhs - return mixin("this" ~ op ~ "rhs.payload"); - } - else static if (hasMember!(Hook, "hookOpBinary")) - { - return hook.hookOpBinary!op(payload, rhs); - } - else static if (hasMember!(Hook1, "hookOpBinary")) - { - // Delegate to rhs - return mixin("this.payload" ~ op ~ "rhs"); - } - else static if (hasMember!(Hook, "onOverflow") && - !hasMember!(Hook1, "onOverflow")) - { - // Delegate to lhs - return mixin("this" ~ op ~ "rhs.payload"); - } - else static if (hasMember!(Hook1, "onOverflow") && - !hasMember!(Hook, "onOverflow")) - { - // Delegate to rhs - return mixin("this.payload" ~ op ~ "rhs"); - } - else - { - static assert(0, "Conflict between lhs and rhs hooks," ~ - " use .get on one side to disambiguate."); - } - } - - static if (is(T == int) && is(Hook == void)) @safe unittest - { - const a = checked(42); - assert(a + 1 == 43); - assert(a + checked(uint(42)) == 84); - assert(checked(42) + checked!void(42u) == 84); - assert(checked!void(42) + checked(42u) == 84); - - static struct MyHook - { - static uint tally; - static auto hookOpBinary(string x, L, R)(L lhs, R rhs) - { - ++tally; - return mixin("lhs" ~ x ~ "rhs"); - } - } - assert(checked!MyHook(42) + checked(42u) == 84); - assert(checked!void(42) + checked!MyHook(42u) == 84); - assert(MyHook.tally == 2); - } - - // opBinaryRight - /** - - Defines binary operators `+`, `-`, `*`, `/`, `%`, `^^`, `&`, `|`, `^`, `<<`, - `>>`, and `>>>` for the case when a built-in numeric or Boolean type is on - the left-hand side, and a `Checked` instance is on the right-hand side. - - Params: - op = The binary operator - lhs = The left hand side operand - - Returns: - A `Checked` instance representing the result of the binary - operation - - */ - auto opBinaryRight(string op, Lhs)(const Lhs lhs) - if (isIntegral!Lhs || isFloatingPoint!Lhs || is(Lhs == bool)) - { - return opBinaryRightImpl!(op, Lhs, typeof(this))(lhs); - } - - /// ditto - auto opBinaryRight(string op, Lhs)(const Lhs lhs) const - if (isIntegral!Lhs || isFloatingPoint!Lhs || is(Lhs == bool)) - { - return opBinaryRightImpl!(op, Lhs, typeof(this))(lhs); - } - - private auto opBinaryRightImpl(string op, Lhs, this _)(const Lhs lhs) - { - static if (hasMember!(Hook, "hookOpBinaryRight")) - { - auto r = hook.hookOpBinaryRight!op(lhs, payload); - return Checked!(typeof(r), Hook)(r); - } - else static if (hasMember!(Hook, "hookOpBinary")) - { - auto r = hook.hookOpBinary!op(lhs, payload); - return Checked!(typeof(r), Hook)(r); - } - else static if (is(Lhs == bool)) - { - return mixin("ubyte(lhs)" ~ op ~ "this"); - } - else static if (isFloatingPoint!Lhs) - { - return mixin("lhs" ~ op ~ "payload"); - } - else static if (hasMember!(Hook, "onOverflow")) - { - bool overflow; - auto r = opChecked!op(lhs, T(payload), overflow); - if (overflow) r = hook.onOverflow!op(lhs, payload); - return Checked!(typeof(r), Hook)(r); - } - else - { - // Default is built-in behavior - auto r = mixin("lhs" ~ op ~ "T(payload)"); - return Checked!(typeof(r), Hook)(r); - } - } - - static if (is(T == int) && is(Hook == void)) @safe unittest - { - assert(1 + checked(1) == 2); - static uint tally; - static struct MyHook - { - static auto hookOpBinaryRight(string x, L, R)(L lhs, R rhs) - { - ++tally; - return mixin("lhs" ~ x ~ "rhs"); - } - } - assert(1 + checked!MyHook(1) == 2); - assert(tally == 1); - - immutable x1 = checked(1); - assert(1 + x1 == 2); - immutable x2 = checked!MyHook(1); - assert(1 + x2 == 2); - assert(tally == 2); - } - - // opOpAssign - /** - - Defines operators `+=`, `-=`, `*=`, `/=`, `%=`, `^^=`, `&=`, `|=`, `^=`, - `<<=`, `>>=`, and `>>>=`. - - If `Hook` defines `hookOpOpAssign`, `opOpAssign` forwards to - `hook.hookOpOpAssign!op(payload, rhs)`, where `payload` is a reference to - the internally held data so the hook can change it. - - Otherwise, the operator first evaluates $(D auto result = - opBinary!op(payload, rhs).payload), which is subject to the hooks in - `opBinary`. Then, if `result` is less than $(D Checked!(T, Hook).min) and if - `Hook` defines `onLowerBound`, the payload is assigned from $(D - hook.onLowerBound(result, min)). If `result` is greater than $(D Checked!(T, - Hook).max) and if `Hook` defines `onUpperBound`, the payload is assigned - from $(D hook.onUpperBound(result, min)). - - If the right-hand side is also a Checked but with a different hook or - underlying type, the hook and underlying type of this Checked takes - precedence. - - In all other cases, the built-in behavior is carried out. - - Params: - op = The operator involved (without the `"="`, e.g. `"+"` for `"+="` etc) - rhs = The right-hand side of the operator (left-hand side is `this`) - - Returns: A reference to `this`. - */ - ref Checked opOpAssign(string op, Rhs)(const Rhs rhs) return - if (isIntegral!Rhs || isFloatingPoint!Rhs || is(Rhs == bool)) - { - static assert(is(typeof(mixin("payload" ~ op ~ "=rhs")) == T)); - - static if (hasMember!(Hook, "hookOpOpAssign")) - { - hook.hookOpOpAssign!op(payload, rhs); - } - else - { - alias R = typeof(get + rhs); - auto r = opBinary!op(rhs).get; - import std.conv : unsigned; - - static if (ProperCompare.hookOpCmp(R.min, min.get) < 0 && - hasMember!(Hook, "onLowerBound")) - { - if (ProperCompare.hookOpCmp(r, min.get) < 0) - { - // Example: Checked!uint(1) += int(-3) - payload = hook.onLowerBound(r, min.get); - return this; - } - } - static if (ProperCompare.hookOpCmp(max.get, R.max) < 0 && - hasMember!(Hook, "onUpperBound")) - { - if (ProperCompare.hookOpCmp(r, max.get) > 0) - { - // Example: Checked!uint(1) += long(uint.max) - payload = hook.onUpperBound(r, max.get); - return this; - } - } - payload = cast(T) r; - } - return this; - } - - /// ditto - ref Checked opOpAssign(string op, Rhs)(const Rhs rhs) return - if (is(Rhs == Checked!(RhsT, RhsHook), RhsT, RhsHook)) - { - return opOpAssign!(op, typeof(rhs.payload))(rhs.payload); - } - - /// - static if (is(T == int) && is(Hook == void)) @safe unittest - { - static struct MyHook - { - static bool thereWereErrors; - static T onLowerBound(Rhs, T)(Rhs rhs, T bound) - { - thereWereErrors = true; - return bound; - } - static T onUpperBound(Rhs, T)(Rhs rhs, T bound) - { - thereWereErrors = true; - return bound; - } - } - auto x = checked!MyHook(byte.min); - x -= 1; - assert(MyHook.thereWereErrors); - MyHook.thereWereErrors = false; - x = byte.max; - x += 1; - assert(MyHook.thereWereErrors); - } -} - -/// -@safe @nogc pure nothrow unittest -{ - // Hook that ignores all problems. - static struct Ignore - { - @nogc nothrow pure @safe static: - Dst onBadCast(Dst, Src)(Src src) { return cast(Dst) src; } - Lhs onLowerBound(Rhs, T)(Rhs rhs, T bound) { return cast(T) rhs; } - T onUpperBound(Rhs, T)(Rhs rhs, T bound) { return cast(T) rhs; } - bool hookOpEquals(Lhs, Rhs)(Lhs lhs, Rhs rhs) { return lhs == rhs; } - int hookOpCmp(Lhs, Rhs)(Lhs lhs, Rhs rhs) { return (lhs > rhs) - (lhs < rhs); } - typeof(~Lhs()) onOverflow(string x, Lhs)(ref Lhs lhs) { return mixin(x ~ "lhs"); } - typeof(Lhs() + Rhs()) onOverflow(string x, Lhs, Rhs)(Lhs lhs, Rhs rhs) - { - static if (x == "/") - return typeof(lhs / rhs).min; - else - return mixin("lhs" ~ x ~ "rhs"); - } - } - - auto x = Checked!(int, Ignore)(5) + 7; -} - - -/** - -Convenience function that turns an integral into the corresponding `Checked` -instance by using template argument deduction. The hook type may be specified -(by default `Abort`). - -Params: - Hook = type that customizes the behavior, by default `Abort` - T = type represetinfg the underlying represantion of the `Checked` instance - value = the actual value of the representation - -Returns: - A `Checked` instance customized by the provided `Hook` and `value` -*/ -Checked!(T, Hook) checked(Hook = Abort, T)(const T value) -if (is(typeof(Checked!(T, Hook)(value)))) -{ - return Checked!(T, Hook)(value); -} - -/// -@safe unittest -{ - static assert(is(typeof(checked(42)) == Checked!int)); - assert(checked(42) == Checked!int(42)); - static assert(is(typeof(checked!WithNaN(42)) == Checked!(int, WithNaN))); - assert(checked!WithNaN(42) == Checked!(int, WithNaN)(42)); -} - -// get -@safe unittest -{ - void test(T)() - { - assert(Checked!(T, void)(ubyte(22)).get == 22); - } - test!ubyte; - test!(const ubyte); - test!(immutable ubyte); -} - -@system unittest -{ - // https://issues.dlang.org/show_bug.cgi?id=21758 - assert(4 * checked(5L) == 20); - assert(20 / checked(5L) == 4); - assert(2 ^^ checked(3L) == 8); - assert(12 % checked(5L) == 2); - assert((0xff & checked(3L)) == 3); - assert((0xf0 | checked(3L)) == 0xf3); - assert((0xff ^ checked(3L)) == 0xfc); -} - -// Abort -/** - -Force all integral errors to fail by printing an error message to `stderr` and -then abort the program. `Abort` is the default second argument for `Checked`. - -*/ -struct Abort -{ -static: - /** - - Called automatically upon a bad cast (one that loses precision or attempts - to convert a negative value to an unsigned type). The source type is `Src` - and the destination type is `Dst`. - - Params: - src = Souce operand - - Returns: - Nominally the result is the desired value of the cast operation, - which will be forwarded as the result of the cast. For `Abort`, the - function never returns because it aborts the program. - */ - Dst onBadCast(Dst, Src)(Src src) - { - Warn.onBadCast!Dst(src); - assert(0); - } - - /** - - Called automatically upon a bounds error. - - Params: - rhs = The right-hand side value in the assignment, after the operator has - been evaluated - bound = The value of the bound being violated - - Returns: Nominally the result is the desired value of the operator, which - will be forwarded as result. For `Abort`, the function never returns because - it aborts the program. - - */ - T onLowerBound(Rhs, T)(Rhs rhs, T bound) - { - Warn.onLowerBound(rhs, bound); - assert(0); - } - /// ditto - T onUpperBound(Rhs, T)(Rhs rhs, T bound) - { - Warn.onUpperBound(rhs, bound); - assert(0); - } - - /** - - Called automatically upon a comparison for equality. In case of a erroneous - comparison (one that would make a signed negative value appear equal to an - unsigned positive value), this hook issues `assert(0)` which terminates the - application. - - Params: - lhs = The first argument of `Checked`, e.g. `int` if the left-hand side of - the operator is `Checked!int` - rhs = The right-hand side type involved in the operator - - Returns: Upon a correct comparison, returns the result of the comparison. - Otherwise, the function terminates the application so it never returns. - - */ - static bool hookOpEquals(Lhs, Rhs)(Lhs lhs, Rhs rhs) - { - bool error; - auto result = opChecked!"=="(lhs, rhs, error); - if (error) - { - Warn.hookOpEquals(lhs, rhs); - assert(0); - } - return result; - } - - /** - - Called automatically upon a comparison for ordering using one of the - operators `<`, `<=`, `>`, or `>=`. In case the comparison is erroneous (i.e. - it would make a signed negative value appear greater than or equal to an - unsigned positive value), then application is terminated with `assert(0)`. - Otherwise, the three-state result is returned (positive if $(D lhs > rhs), - negative if $(D lhs < rhs), `0` otherwise). - - Params: - lhs = The first argument of `Checked`, e.g. `int` if the left-hand side of - the operator is `Checked!int` - rhs = The right-hand side type involved in the operator - - Returns: For correct comparisons, returns a positive integer if $(D lhs > - rhs), a negative integer if $(D lhs < rhs), `0` if the two are equal. Upon - a mistaken comparison such as $(D int(-1) < uint(0)), the function never - returns because it aborts the program. - - */ - int hookOpCmp(Lhs, Rhs)(Lhs lhs, Rhs rhs) - { - bool error; - auto result = opChecked!"cmp"(lhs, rhs, error); - if (error) - { - Warn.hookOpCmp(lhs, rhs); - assert(0); - } - return result; - } - - /** - - Called automatically upon an overflow during a unary or binary operation. - - Params: - x = The operator, e.g. `-` - lhs = The left-hand side (or sole) argument - rhs = The right-hand side type involved in the operator - - Returns: Nominally the result is the desired value of the operator, which - will be forwarded as result. For `Abort`, the function never returns because - it aborts the program. - - */ - typeof(~Lhs()) onOverflow(string x, Lhs)(Lhs lhs) - { - Warn.onOverflow!x(lhs); - assert(0); - } - /// ditto - typeof(Lhs() + Rhs()) onOverflow(string x, Lhs, Rhs)(Lhs lhs, Rhs rhs) - { - Warn.onOverflow!x(lhs, rhs); - assert(0); - } -} - -/// -@safe unittest -{ - void test(T)() - { - Checked!(int, Abort) x; - x = 42; - auto x1 = cast(T) x; - assert(x1 == 42); - //x1 += long(int.max); - } - test!short; - test!(const short); - test!(immutable short); -} - - -// Throw -/** - -Force all integral errors to fail by throwing an exception of type -`Throw.CheckFailure`. The message coming with the error is similar to the one -printed by `Warn`. - -*/ -struct Throw -{ - /** - Exception type thrown upon any failure. - */ - static class CheckFailure : Exception - { - /** - Params: - f = format specifier - vals = actual values for the format specifier - */ - this(T...)(string f, T vals) - { - import std.format : format; - super(format(f, vals)); - } - } - - /** - - Called automatically upon a bad cast (one that loses precision or attempts - to convert a negative value to an unsigned type). The source type is `Src` - and the destination type is `Dst`. - - Params: - src = source operand - - Returns: - Nominally the result is the desired value of the cast operation, - which will be forwarded as the result of the cast. For `Throw`, the - function never returns because it throws an exception. - - Throws: - `CheckFailure` on bad cast - */ - static Dst onBadCast(Dst, Src)(Src src) - { - throw new CheckFailure("Erroneous cast: cast(%s) %s(%s)", - Dst.stringof, Src.stringof, src); - } - - /** - - Called automatically upon a bounds error. - - Params: - rhs = The right-hand side value in the assignment, after the operator has - been evaluated - bound = The value of the bound being violated - - Returns: - Nominally the result is the desired value of the operator, which - will be forwarded as result. For `Throw`, the function never returns because - it throws. - - Throws: - `CheckFailure` on overflow - - */ - static T onLowerBound(Rhs, T)(Rhs rhs, T bound) - { - throw new CheckFailure("Lower bound error: %s(%s) < %s(%s)", - Rhs.stringof, rhs, T.stringof, bound); - } - /// ditto - static T onUpperBound(Rhs, T)(Rhs rhs, T bound) - { - throw new CheckFailure("Upper bound error: %s(%s) > %s(%s)", - Rhs.stringof, rhs, T.stringof, bound); - } - - /** - - Called automatically upon a comparison for equality. Throws upon an - erroneous comparison (one that would make a signed negative value appear - equal to an unsigned positive value). - - Params: - lhs = The first argument of `Checked`, e.g. `int` if the left-hand side of - the operator is `Checked!int` - rhs = The right-hand side type involved in the operator - - Returns: The result of the comparison. - - Throws: `CheckFailure` if the comparison is mathematically erroneous. - - */ - static bool hookOpEquals(L, R)(L lhs, R rhs) - { - bool error; - auto result = opChecked!"=="(lhs, rhs, error); - if (error) - { - throw new CheckFailure("Erroneous comparison: %s(%s) == %s(%s)", - L.stringof, lhs, R.stringof, rhs); - } - return result; - } - - /** - - Called automatically upon a comparison for ordering using one of the - operators `<`, `<=`, `>`, or `>=`. In case the comparison is erroneous (i.e. - it would make a signed negative value appear greater than or equal to an - unsigned positive value), throws a `Throw.CheckFailure` exception. - Otherwise, the three-state result is returned (positive if $(D lhs > rhs), - negative if $(D lhs < rhs), `0` otherwise). - - Params: - lhs = The first argument of `Checked`, e.g. `int` if the left-hand side of - the operator is `Checked!int` - rhs = The right-hand side type involved in the operator - - Returns: For correct comparisons, returns a positive integer if $(D lhs > - rhs), a negative integer if $(D lhs < rhs), `0` if the two are equal. - - Throws: Upon a mistaken comparison such as $(D int(-1) < uint(0)), the - function never returns because it throws a `Throw.CheckedFailure` exception. - - */ - static int hookOpCmp(Lhs, Rhs)(Lhs lhs, Rhs rhs) - { - bool error; - auto result = opChecked!"cmp"(lhs, rhs, error); - if (error) - { - throw new CheckFailure("Erroneous ordering comparison: %s(%s) and %s(%s)", - Lhs.stringof, lhs, Rhs.stringof, rhs); - } - return result; - } - - /** - - Called automatically upon an overflow during a unary or binary operation. - - Params: - x = The operator, e.g. `-` - lhs = The left-hand side (or sole) argument - rhs = The right-hand side type involved in the operator - - Returns: - Nominally the result is the desired value of the operator, which - will be forwarded as result. For `Throw`, the function never returns because - it throws an exception. - - Throws: - `CheckFailure` on overflow - - */ - static typeof(~Lhs()) onOverflow(string x, Lhs)(Lhs lhs) - { - throw new CheckFailure("Overflow on unary operator: %s%s(%s)", - x, Lhs.stringof, lhs); - } - /// ditto - static typeof(Lhs() + Rhs()) onOverflow(string x, Lhs, Rhs)(Lhs lhs, Rhs rhs) - { - throw new CheckFailure("Overflow on binary operator: %s(%s) %s %s(%s)", - Lhs.stringof, lhs, x, Rhs.stringof, rhs); - } -} - -/// -@safe unittest -{ - void test(T)() - { - Checked!(int, Throw) x; - x = 42; - auto x1 = cast(T) x; - assert(x1 == 42); - x = T.max + 1; - import std.exception : assertThrown, assertNotThrown; - assertThrown(cast(T) x); - x = x.max; - assertThrown(x += 42); - assertThrown(x += 42L); - x = x.min; - assertThrown(-x); - assertThrown(x -= 42); - assertThrown(x -= 42L); - x = -1; - assertNotThrown(x == -1); - assertThrown(x == uint(-1)); - assertNotThrown(x <= -1); - assertThrown(x <= uint(-1)); - } - test!short; - test!(const short); - test!(immutable short); -} - -// Warn -/** -Hook that prints to `stderr` a trace of all integral errors, without affecting -default behavior. -*/ -struct Warn -{ - import std.stdio : writefln; -static: - /** - - Called automatically upon a bad cast from `src` to type `Dst` (one that - loses precision or attempts to convert a negative value to an unsigned - type). - - Params: - src = The source of the cast - Dst = The target type of the cast - - Returns: `cast(Dst) src` - - */ - Dst onBadCast(Dst, Src)(Src src) - { - trustedStderr.writefln("Erroneous cast: cast(%s) %s(%s)", - Dst.stringof, Src.stringof, src); - return cast(Dst) src; - } - - /** - - Called automatically upon a bad `opOpAssign` call (one that loses precision - or attempts to convert a negative value to an unsigned type). - - Params: - rhs = The right-hand side value in the assignment, after the operator has - been evaluated - bound = The bound being violated - - Returns: `cast(T) rhs` - */ - T onLowerBound(Rhs, T)(Rhs rhs, T bound) - { - trustedStderr.writefln("Lower bound error: %s(%s) < %s(%s)", - Rhs.stringof, rhs, T.stringof, bound); - return cast(T) rhs; - } - /// ditto - T onUpperBound(Rhs, T)(Rhs rhs, T bound) - { - trustedStderr.writefln("Upper bound error: %s(%s) > %s(%s)", - Rhs.stringof, rhs, T.stringof, bound); - return cast(T) rhs; - } - - /** - - Called automatically upon a comparison for equality. In case of an Erroneous - comparison (one that would make a signed negative value appear equal to an - unsigned positive value), writes a warning message to `stderr` as a side - effect. - - Params: - lhs = The first argument of `Checked`, e.g. `int` if the left-hand side of - the operator is `Checked!int` - rhs = The right-hand side type involved in the operator - - Returns: In all cases the function returns the built-in result of $(D lhs == - rhs). - - */ - bool hookOpEquals(Lhs, Rhs)(Lhs lhs, Rhs rhs) - { - bool error; - auto result = opChecked!"=="(lhs, rhs, error); - if (error) - { - trustedStderr.writefln("Erroneous comparison: %s(%s) == %s(%s)", - Lhs.stringof, lhs, Rhs.stringof, rhs); - return lhs == rhs; - } - return result; - } - - /// - @safe unittest - { - auto x = checked!Warn(-42); - // Passes - assert(x == -42); - // Passes but prints a warning - // assert(x == uint(-42)); - } - - /** - - Called automatically upon a comparison for ordering using one of the - operators `<`, `<=`, `>`, or `>=`. In case the comparison is erroneous (i.e. - it would make a signed negative value appear greater than or equal to an - unsigned positive value), then a warning message is printed to `stderr`. - - Params: - lhs = The first argument of `Checked`, e.g. `int` if the left-hand side of - the operator is `Checked!int` - rhs = The right-hand side type involved in the operator - - Returns: In all cases, returns $(D lhs < rhs ? -1 : lhs > rhs). The result - is not autocorrected in case of an erroneous comparison. - - */ - int hookOpCmp(Lhs, Rhs)(Lhs lhs, Rhs rhs) - { - bool error; - auto result = opChecked!"cmp"(lhs, rhs, error); - if (error) - { - trustedStderr.writefln("Erroneous ordering comparison: %s(%s) and %s(%s)", - Lhs.stringof, lhs, Rhs.stringof, rhs); - return lhs < rhs ? -1 : lhs > rhs; - } - return result; - } - - /// - @safe unittest - { - auto x = checked!Warn(-42); - // Passes - assert(x <= -42); - // Passes but prints a warning - // assert(x <= uint(-42)); - } - - /** - - Called automatically upon an overflow during a unary or binary operation. - - Params: - x = The operator involved - Lhs = The first argument of `Checked`, e.g. `int` if the left-hand side of - the operator is `Checked!int` - Rhs = The right-hand side type involved in the operator - - Returns: - $(D mixin(x ~ "lhs")) for unary, $(D mixin("lhs" ~ x ~ "rhs")) for - binary - - */ - typeof(~Lhs()) onOverflow(string x, Lhs)(ref Lhs lhs) - { - trustedStderr.writefln("Overflow on unary operator: %s%s(%s)", - x, Lhs.stringof, lhs); - return mixin(x ~ "lhs"); - } - /// ditto - typeof(Lhs() + Rhs()) onOverflow(string x, Lhs, Rhs)(Lhs lhs, Rhs rhs) - { - trustedStderr.writefln("Overflow on binary operator: %s(%s) %s %s(%s)", - Lhs.stringof, lhs, x, Rhs.stringof, rhs); - static if (x == "/") // Issue 20743: mixin below would cause SIGFPE on POSIX - return typeof(lhs / rhs).min; // or EXCEPTION_INT_OVERFLOW on Windows - else - return mixin("lhs" ~ x ~ "rhs"); - } - - // This is safe because we do not assign to the reference returned by - // `stderr`. The ability for the caller to do that is why `stderr` is not - // safe in the general case. - private @property auto ref trustedStderr() @trusted - { - import std.stdio : stderr; - - return stderr; - } -} - -/// -@safe unittest -{ - auto x = checked!Warn(42); - short x1 = cast(short) x; - //x += long(int.max); - auto y = checked!Warn(cast(const int) 42); - short y1 = cast(const byte) y; -} - -@system unittest -{ - auto a = checked!Warn(int.min); - auto b = checked!Warn(-1); - auto x = checked!Abort(int.min); - auto y = checked!Abort(-1); - - // Temporarily redirect output to stderr to make sure we get the right output. - import std.file : exists, remove; - import std.process : uniqueTempPath; - import std.stdio : stderr; - auto tmpname = uniqueTempPath; - scope(exit) if (exists(tmpname)) remove(tmpname); - auto t = stderr; - stderr.open(tmpname, "w"); - // Open a new scope to minimize code ran with stderr redirected. - { - scope(exit) stderr = t; - assert(a / b == a * b); - import std.exception : assertThrown; - import core.exception : AssertError; - assertThrown!AssertError(x / y); - } - import std.file : readText; - import std.ascii : newline; - auto witness = readText(tmpname); - auto expected = -"Overflow on binary operator: int(-2147483648) / const(int)(-1)" ~ newline ~ -"Overflow on binary operator: int(-2147483648) * const(int)(-1)" ~ newline ~ -"Overflow on binary operator: int(-2147483648) / const(int)(-1)" ~ newline; - assert(witness == expected, "'" ~ witness ~ "'"); -} - -// https://issues.dlang.org/show_bug.cgi?id=22249 -@safe unittest -{ - alias _ = Warn.onLowerBound!(int, int); -} - -// ProperCompare -/** - -Hook that provides arithmetically correct comparisons for equality and ordering. -Comparing an object of type $(D Checked!(X, ProperCompare)) against another -integral (for equality or ordering) ensures that no surprising conversions from -signed to unsigned integral occur before the comparison. Using $(D Checked!(X, -ProperCompare)) on either side of a comparison for equality against a -floating-point number makes sure the integral can be properly converted to the -floating point type, thus making sure equality is transitive. - -*/ -struct ProperCompare -{ - /** - Hook for `==` and `!=` that ensures comparison against integral values has - the behavior expected by the usual arithmetic rules. The built-in semantics - yield surprising behavior when comparing signed values against unsigned - values for equality, for example $(D uint.max == -1) or $(D -1_294_967_296 == - 3_000_000_000u). The call $(D hookOpEquals(x, y)) returns `true` if and only - if `x` and `y` represent the same arithmetic number. - - If one of the numbers is an integral and the other is a floating-point - number, $(D hookOpEquals(x, y)) returns `true` if and only if the integral - can be converted exactly (without approximation) to the floating-point - number. This is in order to preserve transitivity of equality: if $(D - hookOpEquals(x, y)) and $(D hookOpEquals(y, z)) then $(D hookOpEquals(y, - z)), in case `x`, `y`, and `z` are a mix of integral and floating-point - numbers. - - Params: - lhs = The left-hand side of the comparison for equality - rhs = The right-hand side of the comparison for equality - - Returns: - The result of the comparison, `true` if the values are equal - */ - static bool hookOpEquals(L, R)(L lhs, R rhs) - { - alias C = typeof(lhs + rhs); - static if (isFloatingPoint!C) - { - static if (!isFloatingPoint!L) - { - return hookOpEquals(rhs, lhs); - } - else static if (!isFloatingPoint!R) - { - static assert(isFloatingPoint!L && !isFloatingPoint!R); - auto rhs1 = C(rhs); - return lhs == rhs1 && cast(R) rhs1 == rhs; - } - else - return lhs == rhs; - } - else - { - bool error; - auto result = opChecked!"=="(lhs, rhs, error); - if (error) - { - // Only possible error is a wrong "true" - return false; - } - return result; - } - } - - /** - Hook for `<`, `<=`, `>`, and `>=` that ensures comparison against integral - values has the behavior expected by the usual arithmetic rules. The built-in - semantics yield surprising behavior when comparing signed values against - unsigned values, for example $(D 0u < -1). The call $(D hookOpCmp(x, y)) - returns `-1` if and only if `x` is smaller than `y` in abstract arithmetic - sense. - - If one of the numbers is an integral and the other is a floating-point - number, $(D hookOpEquals(x, y)) returns a floating-point number that is `-1` - if `x < y`, `0` if `x == y`, `1` if `x > y`, and `NaN` if the floating-point - number is `NaN`. - - Params: - lhs = The left-hand side of the comparison for ordering - rhs = The right-hand side of the comparison for ordering - - Returns: - The result of the comparison (negative if $(D lhs < rhs), positive if $(D - lhs > rhs), `0` if the values are equal) - */ - static auto hookOpCmp(L, R)(L lhs, R rhs) - { - alias C = typeof(lhs + rhs); - static if (isFloatingPoint!C) - { - return lhs < rhs - ? C(-1) - : lhs > rhs ? C(1) : lhs == rhs ? C(0) : C.init; - } - else - { - static if (!valueConvertible!(L, C) || !valueConvertible!(R, C)) - { - static assert(isUnsigned!C); - static assert(isUnsigned!L != isUnsigned!R); - if (!isUnsigned!L && lhs < 0) - return -1; - if (!isUnsigned!R && rhs < 0) - return 1; - } - return lhs < rhs ? -1 : lhs > rhs; - } - } -} - -/// -@safe unittest -{ - alias opEqualsProper = ProperCompare.hookOpEquals; - assert(opEqualsProper(42, 42)); - assert(opEqualsProper(42.0, 42.0)); - assert(opEqualsProper(42u, 42)); - assert(opEqualsProper(42, 42u)); - assert(-1 == 4294967295u); - assert(!opEqualsProper(-1, 4294967295u)); - assert(!opEqualsProper(const uint(-1), -1)); - assert(!opEqualsProper(uint(-1), -1.0)); - assert(3_000_000_000U == -1_294_967_296); - assert(!opEqualsProper(3_000_000_000U, -1_294_967_296)); -} - -@safe unittest -{ - alias opCmpProper = ProperCompare.hookOpCmp; - assert(opCmpProper(42, 42) == 0); - assert(opCmpProper(42, 42.0) == 0); - assert(opCmpProper(41, 42.0) < 0); - assert(opCmpProper(42, 41.0) > 0); - import std.math.traits : isNaN; - assert(isNaN(opCmpProper(41, double.init))); - assert(opCmpProper(42u, 42) == 0); - assert(opCmpProper(42, 42u) == 0); - assert(opCmpProper(-1, uint(-1)) < 0); - assert(opCmpProper(uint(-1), -1) > 0); - assert(opCmpProper(-1.0, -1) == 0); -} - -@safe unittest -{ - auto x1 = Checked!(uint, ProperCompare)(42u); - assert(x1.get < -1); - assert(x1 > -1); -} - -// WithNaN -/** - -Hook that reserves a special value as a "Not a Number" representative. For -signed integrals, the reserved value is `T.min`. For signed integrals, the -reserved value is `T.max`. - -The default value of a $(D Checked!(X, WithNaN)) is its NaN value, so care must -be taken that all variables are explicitly initialized. Any arithmetic and logic -operation involving at least on NaN becomes NaN itself. All of $(D a == b), $(D -a < b), $(D a > b), $(D a <= b), $(D a >= b) yield `false` if at least one of -`a` and `b` is NaN. - -*/ -struct WithNaN -{ -static: - /** - The default value used for values not explicitly initialized. It is the NaN - value, i.e. `T.min` for signed integrals and `T.max` for unsigned integrals. - */ - enum T defaultValue(T) = T.min == 0 ? T.max : T.min; - /** - The maximum value representable is `T.max` for signed integrals, $(D - T.max - 1) for unsigned integrals. The minimum value representable is $(D - T.min + 1) for signed integrals, `0` for unsigned integrals. - */ - enum T max(T) = cast(T) (T.min == 0 ? T.max - 1 : T.max); - /// ditto - enum T min(T) = cast(T) (T.min == 0 ? T(0) : T.min + 1); - - /** - If `rhs` is `WithNaN.defaultValue!Rhs`, returns - `WithNaN.defaultValue!Lhs`. Otherwise, returns $(D cast(Lhs) rhs). - - Params: - rhs = the value being cast (`Rhs` is the first argument to `Checked`) - Lhs = the target type of the cast - - Returns: The result of the cast operation. - */ - Lhs hookOpCast(Lhs, Rhs)(Rhs rhs) - { - static if (is(Lhs == bool)) - { - return rhs != defaultValue!Rhs && rhs != 0; - } - else static if (valueConvertible!(Rhs, Lhs)) - { - return rhs != defaultValue!Rhs ? Lhs(rhs) : defaultValue!Lhs; - } - else - { - // Not value convertible, only viable option is rhs fits within the - // bounds of Lhs - static if (ProperCompare.hookOpCmp!(Rhs, Lhs)(lhs: Rhs.min, rhs: Lhs.min) < 0) - { - // Example: hookOpCast!short(int(42)), hookOpCast!uint(int(42)) - if (ProperCompare.hookOpCmp!(Rhs, Lhs)(lhs: rhs, rhs: Lhs.min) < 0) - return defaultValue!Lhs; - } - static if (ProperCompare.hookOpCmp!(Rhs, Lhs)(lhs: Rhs.max, rhs: Lhs.max) > 0) - { - // Example: hookOpCast!int(uint(42)) - if (ProperCompare.hookOpCmp!(Rhs, Lhs)(lhs: rhs, rhs: Lhs.max) > 0) - return defaultValue!Lhs; - } - return cast(Lhs) rhs; - } - } - - /// - @safe unittest - { - auto x = checked!WithNaN(422); - assert((cast(ubyte) x) == 255); - x = checked!WithNaN(-422); - assert((cast(byte) x) == -128); - assert(cast(short) x == -422); - assert(cast(bool) x); - x = x.init; // set back to NaN - assert(x != true); - assert(x != false); - } - - /** - - Returns `false` if $(D lhs == WithNaN.defaultValue!Lhs), $(D lhs == rhs) - otherwise. - - Params: - lhs = The left-hand side of the comparison (`Lhs` is the first argument to - `Checked`) - rhs = The right-hand side of the comparison - - Returns: `lhs != WithNaN.defaultValue!Lhs && lhs == rhs` - */ - bool hookOpEquals(Lhs, Rhs)(Lhs lhs, Rhs rhs) - { - return lhs != defaultValue!Lhs && lhs == rhs; - } - - /** - - If $(D lhs == WithNaN.defaultValue!Lhs), returns `double.init`. Otherwise, - has the same semantics as the default comparison. - - Params: - lhs = The left-hand side of the comparison (`Lhs` is the first argument to - `Checked`) - rhs = The right-hand side of the comparison - - Returns: `double.init` if `lhs == WitnNaN.defaultValue!Lhs`, `-1.0` if $(D - lhs < rhs), `0.0` if $(D lhs == rhs), `1.0` if $(D lhs > rhs). - - */ - double hookOpCmp(Lhs, Rhs)(Lhs lhs, Rhs rhs) - { - if (lhs == defaultValue!Lhs) return double.init; - return lhs < rhs - ? -1.0 - : lhs > rhs ? 1.0 : lhs == rhs ? 0.0 : double.init; - } - - /// - @safe unittest - { - Checked!(int, WithNaN) x; - assert(!(x < 0) && !(x > 0) && !(x == 0)); - x = 1; - assert(x > 0 && !(x < 0) && !(x == 0)); - } - - /** - Defines hooks for unary operators `-`, `~`, `++`, and `--`. - - For `-` and `~`, if $(D v == WithNaN.defaultValue!T), returns - `WithNaN.defaultValue!T`. Otherwise, the semantics is the same as for the - built-in operator. - - For `++` and `--`, if $(D v == WithNaN.defaultValue!Lhs) or the operation - would result in an overflow, sets `v` to `WithNaN.defaultValue!T`. - Otherwise, the semantics is the same as for the built-in operator. - - Params: - x = The operator symbol - v = The left-hand side of the comparison (`T` is the first argument to - `Checked`) - - Returns: $(UL $(LI For $(D x == "-" || x == "~"): If $(D v == - WithNaN.defaultValue!T), the function returns `WithNaN.defaultValue!T`. - Otherwise it returns the normal result of the operator.) $(LI For $(D x == - "++" || x == "--"): The function returns `void`.)) - - */ - auto hookOpUnary(string x, T)(ref T v) - { - static if (x == "-" || x == "~") - { - return v != defaultValue!T ? mixin(x ~ "v") : v; - } - else static if (x == "++") - { - static if (defaultValue!T == T.min) - { - if (v != defaultValue!T) - { - if (v == T.max) v = defaultValue!T; - else ++v; - } - } - else - { - static assert(defaultValue!T == T.max); - if (v != defaultValue!T) ++v; - } - } - else static if (x == "--") - { - if (v != defaultValue!T) --v; - } - } - - /// - @safe unittest - { - Checked!(int, WithNaN) x; - ++x; - assert(x.isNaN); - x = 1; - assert(!x.isNaN); - x = -x; - ++x; - assert(!x.isNaN); - } - - @safe unittest // for coverage - { - Checked!(uint, WithNaN) y; - ++y; - assert(y.isNaN); - } - - /** - Defines hooks for binary operators `+`, `-`, `*`, `/`, `%`, `^^`, `&`, `|`, - `^`, `<<`, `>>`, and `>>>` for cases where a `Checked` object is the - left-hand side operand. If $(D lhs == WithNaN.defaultValue!Lhs), returns - $(D WithNaN.defaultValue!(typeof(lhs + rhs))) without evaluating the - operand. Otherwise, evaluates the operand. If evaluation does not overflow, - returns the result. Otherwise, returns $(D WithNaN.defaultValue!(typeof(lhs + - rhs))). - - Params: - x = The operator symbol - lhs = The left-hand side operand (`Lhs` is the first argument to `Checked`) - rhs = The right-hand side operand - - Returns: If $(D lhs != WithNaN.defaultValue!Lhs) and the operator does not - overflow, the function returns the same result as the built-in operator. In - all other cases, returns $(D WithNaN.defaultValue!(typeof(lhs + rhs))). - */ - auto hookOpBinary(string x, L, R)(L lhs, R rhs) - { - alias Result = typeof(lhs + rhs); - if (lhs != defaultValue!L) - { - bool error; - auto result = opChecked!x(lhs, rhs, error); - if (!error) return result; - } - return defaultValue!Result; - } - - /// - @safe unittest - { - Checked!(int, WithNaN) x; - assert((x + 1).isNaN); - x = 100; - assert(!(x + 1).isNaN); - } - - /** - Defines hooks for binary operators `+`, `-`, `*`, `/`, `%`, `^^`, `&`, `|`, - `^`, `<<`, `>>`, and `>>>` for cases where a `Checked` object is the - right-hand side operand. If $(D rhs == WithNaN.defaultValue!Rhs), returns - $(D WithNaN.defaultValue!(typeof(lhs + rhs))) without evaluating the - operand. Otherwise, evaluates the operand. If evaluation does not overflow, - returns the result. Otherwise, returns $(D WithNaN.defaultValue!(typeof(lhs + - rhs))). - - Params: - x = The operator symbol - lhs = The left-hand side operand - rhs = The right-hand side operand (`Rhs` is the first argument to `Checked`) - - Returns: If $(D rhs != WithNaN.defaultValue!Rhs) and the operator does not - overflow, the function returns the same result as the built-in operator. In - all other cases, returns $(D WithNaN.defaultValue!(typeof(lhs + rhs))). - */ - auto hookOpBinaryRight(string x, L, R)(L lhs, R rhs) - { - alias Result = typeof(lhs + rhs); - if (rhs != defaultValue!R) - { - bool error; - auto result = opChecked!x(lhs, rhs, error); - if (!error) return result; - } - return defaultValue!Result; - } - /// - @safe unittest - { - Checked!(int, WithNaN) x; - assert((1 + x).isNaN); - x = 100; - assert(!(1 + x).isNaN); - } - - /** - - Defines hooks for binary operators `+=`, `-=`, `*=`, `/=`, `%=`, `^^=`, - `&=`, `|=`, `^=`, `<<=`, `>>=`, and `>>>=` for cases where a `Checked` - object is the left-hand side operand. If $(D lhs == - WithNaN.defaultValue!Lhs), no action is carried. Otherwise, evaluates the - operand. If evaluation does not overflow and fits in `Lhs` without loss of - information or change of sign, sets `lhs` to the result. Otherwise, sets - `lhs` to `WithNaN.defaultValue!Lhs`. - - Params: - x = The operator symbol (without the `=`) - lhs = The left-hand side operand (`Lhs` is the first argument to `Checked`) - rhs = The right-hand side operand - - Returns: `void` - */ - void hookOpOpAssign(string x, L, R)(ref L lhs, R rhs) - { - if (lhs == defaultValue!L) - return; - bool error; - auto temp = opChecked!x(lhs, rhs, error); - lhs = error - ? defaultValue!L - : hookOpCast!L(temp); - } - - /// - @safe unittest - { - Checked!(int, WithNaN) x; - x += 4; - assert(x.isNaN); - x = 0; - x += 4; - assert(!x.isNaN); - x += int.max; - assert(x.isNaN); - } -} - -/// -@safe unittest -{ - auto x1 = Checked!(int, WithNaN)(); - assert(x1.isNaN); - assert(x1.get == int.min); - assert(x1 != x1); - assert(!(x1 < x1)); - assert(!(x1 > x1)); - assert(!(x1 == x1)); - ++x1; - assert(x1.isNaN); - assert(x1.get == int.min); - --x1; - assert(x1.isNaN); - assert(x1.get == int.min); - x1 = 42; - assert(!x1.isNaN); - assert(x1 == x1); - assert(x1 <= x1); - assert(x1 >= x1); - static assert(x1.min == int.min + 1); - x1 += long(int.max); -} - -/** -Queries whether a $(D Checked!(T, WithNaN)) object is not a number (NaN). - -Params: - x = the `Checked` instance queried - -Returns: - `true` if `x` is a NaN, `false` otherwise -*/ -bool isNaN(T)(const Checked!(T, WithNaN) x) -{ - return x.get == x.init.get; -} - -/// -@safe unittest -{ - auto x1 = Checked!(int, WithNaN)(); - assert(x1.isNaN); - x1 = 1; - assert(!x1.isNaN); - x1 = x1.init; - assert(x1.isNaN); -} - -@safe unittest -{ - void test1(T)() - { - auto x1 = Checked!(T, WithNaN)(); - assert(x1.isNaN); - assert(x1.get == int.min); - assert(x1 != x1); - assert(!(x1 < x1)); - assert(!(x1 > x1)); - assert(!(x1 == x1)); - assert(x1.get == int.min); - auto x2 = Checked!(T, WithNaN)(42); - assert(!x2.isNaN); - assert(x2 == x2); - assert(x2 <= x2); - assert(x2 >= x2); - static assert(x2.min == T.min + 1); - } - test1!int; - test1!(const int); - test1!(immutable int); - - void test2(T)() - { - auto x1 = Checked!(T, WithNaN)(); - assert(x1.get == T.min); - assert(x1 != x1); - assert(!(x1 < x1)); - assert(!(x1 > x1)); - assert(!(x1 == x1)); - ++x1; - assert(x1.get == T.min); - --x1; - assert(x1.get == T.min); - x1 = 42; - assert(x1 == x1); - assert(x1 <= x1); - assert(x1 >= x1); - static assert(x1.min == T.min + 1); - x1 += long(T.max); - } - test2!int; -} - -@safe unittest -{ - alias Smart(T) = Checked!(Checked!(T, ProperCompare), WithNaN); - Smart!int x1; - assert(x1 != x1); - x1 = -1; - assert(x1 < 1u); - auto x2 = Smart!(const int)(42); -} - -// Saturate -/** - -Hook that implements $(I saturation), i.e. any arithmetic operation that would -overflow leaves the result at its extreme value (`min` or `max` depending on the -direction of the overflow). - -Saturation is not sticky; if a value reaches its saturation value, another -operation may take it back to normal range. - -*/ -struct Saturate -{ -static: - /** - - Implements saturation for operators `+=`, `-=`, `*=`, `/=`, `%=`, `^^=`, `&=`, `|=`, `^=`, `<<=`, `>>=`, - and `>>>=`. This hook is called if the result of the binary operation does - not fit in `Lhs` without loss of information or a change in sign. - - Params: - Rhs = The right-hand side type in the assignment, after the operation has - been computed - bound = The bound being violated - - Returns: `Lhs.max` if $(D rhs >= 0), `Lhs.min` otherwise. - - */ - T onLowerBound(Rhs, T)(Rhs, T bound) - { - return bound; - } - /// ditto - T onUpperBound(Rhs, T)(Rhs, T bound) - { - return bound; - } - /// - @safe unittest - { - auto x = checked!Saturate(short(100)); - x += 33000; - assert(x == short.max); - x -= 70000; - assert(x == short.min); - } - - /** - - Implements saturation for operators `+`, `-` (unary and binary), `*`, `/`, - `%`, `^^`, `&`, `|`, `^`, `<<`, `>>`, and `>>>`. - - For unary `-`, `onOverflow` is called if $(D lhs == Lhs.min) and `Lhs` is a - signed type. The function returns `Lhs.max`. - - For binary operators, the result is as follows: $(UL $(LI `Lhs.max` if the - result overflows in the positive direction, on division by `0`, or on - shifting right by a negative value) $(LI `Lhs.min` if the result overflows - in the negative direction) $(LI `0` if `lhs` is being shifted left by a - negative value, or shifted right by a large positive value)) - - Params: - x = The operator involved in the `opAssign` operation - Lhs = The left-hand side type of the operator (`Lhs` is the first argument to - `Checked`) - Rhs = The right-hand side type in the operator - - Returns: The saturated result of the operator. - - */ - auto onOverflow(string x, Lhs)(Lhs) - { - static assert(x == "-" || x == "++" || x == "--"); - return x == "--" ? Lhs.min : Lhs.max; - } - /// ditto - typeof(Lhs() + Rhs()) onOverflow(string x, Lhs, Rhs)(Lhs lhs, Rhs rhs) - { - static if (x == "+") - return rhs >= 0 ? Lhs.max : Lhs.min; - else static if (x == "*") - return (lhs >= 0) == (rhs >= 0) ? Lhs.max : Lhs.min; - else static if (x == "^^") - return lhs > 0 || !(rhs & 1) ? Lhs.max : Lhs.min; - else static if (x == "-") - return rhs >= 0 ? Lhs.min : Lhs.max; - else static if (x == "/" || x == "%") - return Lhs.max; - else static if (x == "<<") - return rhs >= 0 ? Lhs.max : 0; - else static if (x == ">>" || x == ">>>") - return rhs >= 0 ? 0 : Lhs.max; - else - static assert(false); - } - /// - @safe unittest - { - assert(checked!Saturate(int.max) + 1 == int.max); - assert(checked!Saturate(100) ^^ 10 == int.max); - assert(checked!Saturate(-100) ^^ 10 == int.max); - assert(checked!Saturate(100) / 0 == int.max); - assert(checked!Saturate(100) << -1 == 0); - assert(checked!Saturate(100) << 33 == int.max); - assert(checked!Saturate(100) >> -1 == int.max); - assert(checked!Saturate(100) >> 33 == 0); - } -} - -/// -@safe unittest -{ - auto x = checked!Saturate(int.max); - ++x; - assert(x == int.max); - --x; - assert(x == int.max - 1); - x = int.min; - assert(-x == int.max); - x -= 42; - assert(x == int.min); - assert(x * -2 == int.max); -} - -/* -Yields `true` if `T1` is "value convertible" (by C's "value preserving" rule, -see $(HTTP c-faq.com/expr/preservingrules.html)) to `T2`, where the two are -integral types. That is, all of values in `T1` are also in `T2`. For example -`int` is value convertible to `long` but not to `uint` or `ulong`. -*/ -private enum valueConvertible(T1, T2) = isIntegral!T1 && isIntegral!T2 && - is(T1 : T2) && ( - isUnsigned!T1 == isUnsigned!T2 || // same signedness - !isUnsigned!T2 && T2.sizeof > T1.sizeof // safely convertible - ); - -/** - -Defines binary operations with overflow checking for any two integral types. -The result type obeys the language rules (even when they may be -counterintuitive), and `overflow` is set if an overflow occurs (including -inadvertent change of signedness, e.g. `-1` is converted to `uint`). -Conceptually the behavior is: - -$(OL $(LI Perform the operation in infinite precision) -$(LI If the infinite-precision result fits in the result type, return it and -do not touch `overflow`) -$(LI Otherwise, set `overflow` to `true` and return an unspecified value) -) - -The implementation exploits properties of types and operations to minimize -additional work. - -Params: -x = The binary operator involved, e.g. `/` -lhs = The left-hand side of the operator -rhs = The right-hand side of the operator -overflow = The overflow indicator (assigned `true` in case there's an error) - -Returns: -The result of the operation, which is the same as the built-in operator -*/ -typeof(mixin(x == "cmp" ? "0" : ("L() " ~ x ~ " R()"))) -opChecked(string x, L, R)(const L lhs, const R rhs, ref bool overflow) -if (isIntegral!L && isIntegral!R) -{ - static if (x == "cmp") - alias Result = int; - else - alias Result = typeof(mixin("L() " ~ x ~ " R()")); - - import core.checkedint : addu, adds, subs, muls, subu, mulu; - import std.algorithm.comparison : among; - static if (x == "==") - { - alias C = typeof(lhs + rhs); - static if (valueConvertible!(L, C) && valueConvertible!(R, C)) - { - // Values are converted to R before comparison, cool. - return lhs == rhs; - } - else - { - static assert(isUnsigned!C); - static assert(isUnsigned!L != isUnsigned!R); - if (lhs != rhs) return false; - // R(lhs) and R(rhs) have the same bit pattern, yet may be - // different due to signedness change. - static if (!isUnsigned!R) - { - if (rhs >= 0) - return true; - } - else - { - if (lhs >= 0) - return true; - } - overflow = true; - return true; - } - } - else static if (x == "cmp") - { - alias C = typeof(lhs + rhs); - static if (!valueConvertible!(L, C) || !valueConvertible!(R, C)) - { - static assert(isUnsigned!C); - static assert(isUnsigned!L != isUnsigned!R); - if (!isUnsigned!L && lhs < 0) - { - overflow = true; - return -1; - } - if (!isUnsigned!R && rhs < 0) - { - overflow = true; - return 1; - } - } - return lhs < rhs ? -1 : lhs > rhs; - } - else static if (x.among("<<", ">>", ">>>")) - { - // Handle shift separately from all others. The test below covers - // negative rhs as well. - import std.conv : unsigned; - if (unsigned(rhs) > 8 * Result.sizeof) goto fail; - return mixin("lhs" ~ x ~ "rhs"); - } - else static if (x.among("&", "|", "^")) - { - // Nothing to check - return mixin("lhs" ~ x ~ "rhs"); - } - else static if (x == "^^") - { - // Exponentiation is weird, handle separately - return pow(lhs, rhs, overflow); - } - else static if (valueConvertible!(L, Result) && - valueConvertible!(R, Result)) - { - static if (L.sizeof < Result.sizeof && R.sizeof < Result.sizeof && - x.among("+", "-", "*")) - { - // No checks - both are value converted and result is in range - return mixin("lhs" ~ x ~ "rhs"); - } - else static if (x == "+") - { - static if (isUnsigned!Result) alias impl = addu; - else alias impl = adds; - return impl(Result(lhs), Result(rhs), overflow); - } - else static if (x == "-") - { - static if (isUnsigned!Result) alias impl = subu; - else alias impl = subs; - return impl(Result(lhs), Result(rhs), overflow); - } - else static if (x == "*") - { - static if (!isUnsigned!L && !isUnsigned!R && - is(L == Result)) - { - if (lhs == Result.min && rhs == -1) goto fail; - } - static if (isUnsigned!Result) alias impl = mulu; - else alias impl = muls; - return impl(Result(lhs), Result(rhs), overflow); - } - else static if (x == "/" || x == "%") - { - static if (!isUnsigned!L && !isUnsigned!R && - is(L == Result) && x == "/") - { - if (lhs == Result.min && rhs == -1) goto fail; - } - if (rhs == 0) goto fail; - return mixin("lhs" ~ x ~ "rhs"); - } - else static assert(0, x); - } - else // Mixed signs - { - static assert(isUnsigned!Result); - static assert(isUnsigned!L != isUnsigned!R); - static if (x == "+") - { - static if (!isUnsigned!L) - { - if (lhs < 0) - return subu(Result(rhs), Result(-lhs), overflow); - } - else static if (!isUnsigned!R) - { - if (rhs < 0) - return subu(Result(lhs), Result(-rhs), overflow); - } - return addu(Result(lhs), Result(rhs), overflow); - } - else static if (x == "-") - { - static if (!isUnsigned!L) - { - if (lhs < 0) goto fail; - } - else static if (!isUnsigned!R) - { - if (rhs < 0) - return addu(Result(lhs), Result(-rhs), overflow); - } - return subu(Result(lhs), Result(rhs), overflow); - } - else static if (x == "*") - { - static if (!isUnsigned!L) - { - if (lhs < 0) goto fail; - } - else static if (!isUnsigned!R) - { - if (rhs < 0) goto fail; - } - return mulu(Result(lhs), Result(rhs), overflow); - } - else static if (x == "/" || x == "%") - { - static if (!isUnsigned!L) - { - if (lhs < 0 || rhs == 0) goto fail; - } - else static if (!isUnsigned!R) - { - if (rhs <= 0) goto fail; - } - return mixin("Result(lhs)" ~ x ~ "Result(rhs)"); - } - else static assert(0, x); - } - debug assert(false); -fail: - overflow = true; - return Result(0); -} - -/// -@safe unittest -{ - bool overflow; - assert(opChecked!"+"(const short(1), short(1), overflow) == 2 && !overflow); - assert(opChecked!"+"(1, 1, overflow) == 2 && !overflow); - assert(opChecked!"+"(1, 1u, overflow) == 2 && !overflow); - assert(opChecked!"+"(-1, 1u, overflow) == 0 && !overflow); - assert(opChecked!"+"(1u, -1, overflow) == 0 && !overflow); -} - -/// -@safe unittest -{ - bool overflow; - assert(opChecked!"-"(1, 1, overflow) == 0 && !overflow); - assert(opChecked!"-"(1, 1u, overflow) == 0 && !overflow); - assert(opChecked!"-"(1u, -1, overflow) == 2 && !overflow); - assert(opChecked!"-"(-1, 1u, overflow) == 0 && overflow); -} - -@safe unittest -{ - bool overflow; - assert(opChecked!"*"(2, 3, overflow) == 6 && !overflow); - assert(opChecked!"*"(2, 3u, overflow) == 6 && !overflow); - assert(opChecked!"*"(1u, -1, overflow) == 0 && overflow); - //assert(mul(-1, 1u, overflow) == uint.max - 1 && overflow); -} - -@safe unittest -{ - bool overflow; - assert(opChecked!"/"(6, 3, overflow) == 2 && !overflow); - assert(opChecked!"/"(6, 3, overflow) == 2 && !overflow); - assert(opChecked!"/"(6u, 3, overflow) == 2 && !overflow); - assert(opChecked!"/"(6, 3u, overflow) == 2 && !overflow); - assert(opChecked!"/"(11, 0, overflow) == 0 && overflow); - overflow = false; - assert(opChecked!"/"(6u, 0, overflow) == 0 && overflow); - overflow = false; - assert(opChecked!"/"(-6, 2u, overflow) == 0 && overflow); - overflow = false; - assert(opChecked!"/"(-6, 0u, overflow) == 0 && overflow); - overflow = false; - assert(opChecked!"cmp"(0u, -6, overflow) == 1 && overflow); - overflow = false; - assert(opChecked!"|"(1, 2, overflow) == 3 && !overflow); -} - -/* -Exponentiation function used by the implementation of operator `^^`. -*/ -private pure @safe nothrow @nogc -auto pow(L, R)(const L lhs, const R rhs, ref bool overflow) -if (isIntegral!L && isIntegral!R) -{ - if (rhs <= 1) - { - if (rhs == 0) return 1; - static if (!isUnsigned!R) - return rhs == 1 - ? lhs - : (rhs == -1 && (lhs == 1 || lhs == -1)) ? lhs : 0; - else - return lhs; - } - - typeof(lhs ^^ rhs) b = void; - static if (!isUnsigned!L && isUnsigned!(typeof(b))) - { - // Need to worry about mixed-sign stuff - if (lhs < 0) - { - if (rhs & 1) - { - if (lhs < 0) overflow = true; - return 0; - } - b = -lhs; - } - else - { - b = lhs; - } - } - else - { - b = lhs; - } - if (b == 1) return 1; - if (b == -1) return (rhs & 1) ? -1 : 1; - if (rhs > 63) - { - overflow = true; - return 0; - } - - assert((b > 1 || b < -1) && rhs > 1); - return powImpl(b, cast(uint) rhs, overflow); -} - -// Inspiration: http://www.stepanovpapers.com/PAM.pdf -pure @safe nothrow @nogc -private T powImpl(T)(T b, uint e, ref bool overflow) -if (isIntegral!T && T.sizeof >= 4) -{ - assert(e > 1); - - import core.checkedint : muls, mulu; - static if (isUnsigned!T) alias mul = mulu; - else alias mul = muls; - - T r = b; - --e; - // Loop invariant: r * (b ^^ e) is the actual result - for (;; e /= 2) - { - if (e % 2) - { - r = mul(r, b, overflow); - if (e == 1) break; - } - b = mul(b, b, overflow); - } - return r; -} - -@safe unittest -{ - static void testPow(T)(T x, uint e) - { - bool overflow; - assert(opChecked!"^^"(T(0), 0, overflow) == 1); - assert(opChecked!"^^"(-2, T(0), overflow) == 1); - assert(opChecked!"^^"(-2, T(1), overflow) == -2); - assert(opChecked!"^^"(-1, -1, overflow) == -1); - assert(opChecked!"^^"(-2, 1, overflow) == -2); - assert(opChecked!"^^"(-2, -1, overflow) == 0); - assert(opChecked!"^^"(-2, 4u, overflow) == 16); - assert(!overflow); - assert(opChecked!"^^"(-2, 3u, overflow) == 0); - assert(overflow); - overflow = false; - assert(opChecked!"^^"(3, 64u, overflow) == 0); - assert(overflow); - overflow = false; - foreach (uint i; 0 .. e) - { - assert(opChecked!"^^"(x, i, overflow) == x ^^ i); - assert(!overflow); - } - assert(opChecked!"^^"(x, e, overflow) == x ^^ e); - assert(overflow); - } - - testPow!int(3, 21); - testPow!uint(3, 21); - testPow!long(3, 40); - testPow!ulong(3, 41); -} - -version (StdUnittest) private struct CountOverflows -{ - uint calls; - auto onOverflow(string op, Lhs)(Lhs lhs) - { - ++calls; - return mixin(op ~ "lhs"); - } - auto onOverflow(string op, Lhs, Rhs)(Lhs lhs, Rhs rhs) - { - ++calls; - return mixin("lhs" ~ op ~ "rhs"); - } - T onLowerBound(Rhs, T)(Rhs rhs, T) - { - ++calls; - return cast(T) rhs; - } - T onUpperBound(Rhs, T)(Rhs rhs, T) - { - ++calls; - return cast(T) rhs; - } -} - -// opBinary -@nogc nothrow pure @safe unittest -{ - static struct CountOpBinary - { - uint calls; - auto hookOpBinary(string op, Lhs, Rhs)(Lhs lhs, Rhs rhs) - { - ++calls; - return mixin("lhs" ~ op ~ "rhs"); - } - } - auto x = Checked!(const int, void)(42), y = Checked!(immutable int, void)(142); - assert(x + y == 184); - assert(x + 100 == 142); - assert(y - x == 100); - assert(200 - x == 158); - assert(y * x == 142 * 42); - assert(x / 1 == 42); - assert(x % 20 == 2); - - auto x1 = Checked!(int, CountOverflows)(42); - assert(x1 + 0 == 42); - assert(x1 + false == 42); - assert(is(typeof(x1 + 0.5) == double)); - assert(x1 + 0.5 == 42.5); - assert(x1.hook.calls == 0); - assert(x1 + int.max == int.max + 42); - assert(x1.hook.calls == 1); - assert(x1 * 2 == 84); - assert(x1.hook.calls == 1); - assert(x1 / 2 == 21); - assert(x1.hook.calls == 1); - assert(x1 % 20 == 2); - assert(x1.hook.calls == 1); - assert(x1 << 2 == 42 << 2); - assert(x1.hook.calls == 1); - assert(x1 << 42 == x1.get << x1.get); - assert(x1.hook.calls == 2); - x1 = int.min; - assert(x1 - 1 == int.max); - assert(x1.hook.calls == 3); - - auto x2 = Checked!(int, CountOpBinary)(42); - assert(x2 + 1 == 43); - assert(x2.hook.calls == 1); - - auto x3 = Checked!(uint, CountOverflows)(42u); - assert(x3 + 1 == 43); - assert(x3.hook.calls == 0); - assert(x3 - 1 == 41); - assert(x3.hook.calls == 0); - assert(x3 + (-42) == 0); - assert(x3.hook.calls == 0); - assert(x3 - (-42) == 84); - assert(x3.hook.calls == 0); - assert(x3 * 2 == 84); - assert(x3.hook.calls == 0); - assert(x3 * -2 == -84); - assert(x3.hook.calls == 1); - assert(x3 / 2 == 21); - assert(x3.hook.calls == 1); - assert(x3 / -2 == 0); - assert(x3.hook.calls == 2); - assert(x3 ^^ 2 == 42 * 42); - assert(x3.hook.calls == 2); - - auto x4 = Checked!(int, CountOverflows)(42); - assert(x4 + 1 == 43); - assert(x4.hook.calls == 0); - assert(x4 + 1u == 43); - assert(x4.hook.calls == 0); - assert(x4 - 1 == 41); - assert(x4.hook.calls == 0); - assert(x4 * 2 == 84); - assert(x4.hook.calls == 0); - x4 = -2; - assert(x4 + 2u == 0); - assert(x4.hook.calls == 0); - assert(x4 * 2u == -4); - assert(x4.hook.calls == 1); - - auto x5 = Checked!(int, CountOverflows)(3); - assert(x5 ^^ 0 == 1); - assert(x5 ^^ 1 == 3); - assert(x5 ^^ 2 == 9); - assert(x5 ^^ 3 == 27); - assert(x5 ^^ 4 == 81); - assert(x5 ^^ 5 == 81 * 3); - assert(x5 ^^ 6 == 81 * 9); -} - -// opBinaryRight -@nogc nothrow pure @safe unittest -{ - auto x1 = Checked!(int, CountOverflows)(42); - assert(1 + x1 == 43); - assert(true + x1 == 43); - assert(0.5 + x1 == 42.5); - auto x2 = Checked!(int, void)(42); - assert(x1 + x2 == 84); - assert(x2 + x1 == 84); -} - -// opOpAssign -@safe unittest -{ - auto x1 = Checked!(int, CountOverflows)(3); - assert((x1 += 2) == 5); - x1 *= 2_000_000_000L; - assert(x1.hook.calls == 1); - x1 *= -2_000_000_000L; - assert(x1.hook.calls == 2); - - auto x2 = Checked!(ushort, CountOverflows)(ushort(3)); - assert((x2 += 2) == 5); - assert(x2.hook.calls == 0); - assert((x2 += ushort.max) == cast(ushort) (ushort(5) + ushort.max)); - assert(x2.hook.calls == 1); - - auto x3 = Checked!(uint, CountOverflows)(3u); - x3 *= ulong(2_000_000_000); - assert(x3.hook.calls == 1); -} - -// opAssign -@safe unittest -{ - Checked!(int, void) x; - x = 42; - assert(x.get == 42); - x = x; - assert(x.get == 42); - x = short(43); - assert(x.get == 43); - x = ushort(44); - assert(x.get == 44); -} - -@safe unittest -{ - static assert(!is(typeof(Checked!(short, void)(ushort(42))))); - static assert(!is(typeof(Checked!(int, void)(long(42))))); - static assert(!is(typeof(Checked!(int, void)(ulong(42))))); - assert(Checked!(short, void)(short(42)).get == 42); - assert(Checked!(int, void)(ushort(42)).get == 42); -} - -// opCast -@nogc nothrow pure @safe unittest -{ - static assert(is(typeof(cast(float) Checked!(int, void)(42)) == float)); - assert(cast(float) Checked!(int, void)(42) == 42); - - assert(is(typeof(cast(long) Checked!(int, void)(42)) == long)); - assert(cast(long) Checked!(int, void)(42) == 42); - static assert(is(typeof(cast(long) Checked!(uint, void)(42u)) == long)); - assert(cast(long) Checked!(uint, void)(42u) == 42); - - auto x = Checked!(int, void)(42); - if (x) {} else assert(0); - x = 0; - if (x) assert(0); - - static struct Hook1 - { - uint calls; - Dst hookOpCast(Dst, Src)(Src value) - { - ++calls; - return 42; - } - } - auto y = Checked!(long, Hook1)(long.max); - assert(cast(int) y == 42); - assert(cast(uint) y == 42); - assert(y.hook.calls == 2); - - static struct Hook2 - { - uint calls; - Dst onBadCast(Dst, Src)(Src value) - { - ++calls; - return 42; - } - } - auto x1 = Checked!(uint, Hook2)(100u); - assert(cast(ushort) x1 == 100); - assert(cast(short) x1 == 100); - assert(cast(float) x1 == 100); - assert(cast(double) x1 == 100); - assert(cast(real) x1 == 100); - assert(x1.hook.calls == 0); - assert(cast(int) x1 == 100); - assert(x1.hook.calls == 0); - x1 = uint.max; - assert(cast(int) x1 == 42); - assert(x1.hook.calls == 1); - - auto x2 = Checked!(int, Hook2)(-100); - assert(cast(short) x2 == -100); - assert(cast(ushort) x2 == 42); - assert(cast(uint) x2 == 42); - assert(cast(ulong) x2 == 42); - assert(x2.hook.calls == 3); -} - -// opEquals -@nogc nothrow pure @safe unittest -{ - assert(Checked!(int, void)(42) == 42L); - assert(42UL == Checked!(int, void)(42)); - - static struct Hook1 - { - uint calls; - bool hookOpEquals(Lhs, Rhs)(const Lhs lhs, const Rhs rhs) - { - ++calls; - return lhs != rhs; - } - } - auto x1 = Checked!(int, Hook1)(100); - assert(x1 != Checked!(long, Hook1)(100)); - assert(x1.hook.calls == 1); - assert(x1 != 100u); - assert(x1.hook.calls == 2); - - static struct Hook2 - { - uint calls; - bool hookOpEquals(Lhs, Rhs)(Lhs lhs, Rhs rhs) - { - ++calls; - return false; - } - } - auto x2 = Checked!(int, Hook2)(-100); - assert(x2 != x1); - // For coverage: lhs has no hookOpEquals, rhs does - assert(Checked!(uint, void)(100u) != x2); - // For coverage: different types, neither has a hookOpEquals - assert(Checked!(uint, void)(100u) == Checked!(int, void*)(100)); - assert(x2.hook.calls == 0); - assert(x2 != -100); - assert(x2.hook.calls == 1); - assert(x2 != cast(uint) -100); - assert(x2.hook.calls == 2); - x2 = 100; - assert(x2 != cast(uint) 100); - assert(x2.hook.calls == 3); - x2 = -100; - - auto x3 = Checked!(uint, Hook2)(100u); - assert(x3 != 100); - x3 = uint.max; - assert(x3 != -1); - - assert(x2 != x3); -} - -// opCmp -@nogc nothrow pure @safe unittest -{ - Checked!(int, void) x; - assert(x <= x); - assert(x < 45); - assert(x < 45u); - assert(x > -45); - assert(x < 44.2); - assert(x > -44.2); - assert(!(x < double.init)); - assert(!(x > double.init)); - assert(!(x <= double.init)); - assert(!(x >= double.init)); - - static struct Hook1 - { - uint calls; - int hookOpCmp(Lhs, Rhs)(Lhs lhs, Rhs rhs) - { - ++calls; - return 0; - } - } - auto x1 = Checked!(int, Hook1)(42); - assert(!(x1 < 43u)); - assert(!(43u < x1)); - assert(x1.hook.calls == 2); - - static struct Hook2 - { - uint calls; - int hookOpCmp(Lhs, Rhs)(Lhs lhs, Rhs rhs) - { - ++calls; - return ProperCompare.hookOpCmp(lhs, rhs); - } - } - auto x2 = Checked!(int, Hook2)(-42); - assert(x2 < 43u); - assert(43u > x2); - assert(x2.hook.calls == 2); - x2 = 42; - assert(x2 > 41u); - - auto x3 = Checked!(uint, Hook2)(42u); - assert(x3 > 41); - assert(x3 > -41); -} - -// opUnary -@nogc nothrow pure @safe unittest -{ - auto x = Checked!(int, void)(42); - assert(x == +x); - static assert(is(typeof(-x) == typeof(x))); - assert(-x == Checked!(int, void)(-42)); - static assert(is(typeof(~x) == typeof(x))); - assert(~x == Checked!(int, void)(~42)); - assert(++x == 43); - assert(--x == 42); - - static struct Hook1 - { - uint calls; - auto hookOpUnary(string op, T)(T value) if (op == "-") - { - ++calls; - return T(42); - } - auto hookOpUnary(string op, T)(T value) if (op == "~") - { - ++calls; - return T(43); - } - } - auto x1 = Checked!(int, Hook1)(100); - assert(is(typeof(-x1) == typeof(x1))); - assert(-x1 == Checked!(int, Hook1)(42)); - assert(is(typeof(~x1) == typeof(x1))); - assert(~x1 == Checked!(int, Hook1)(43)); - assert(x1.hook.calls == 2); - - static struct Hook2 - { - uint calls; - void hookOpUnary(string op, T)(ref T value) if (op == "++") - { - ++calls; - --value; - } - void hookOpUnary(string op, T)(ref T value) if (op == "--") - { - ++calls; - ++value; - } - } - auto x2 = Checked!(int, Hook2)(100); - assert(++x2 == 99); - assert(x2 == 99); - assert(--x2 == 100); - assert(x2 == 100); - - auto x3 = Checked!(int, CountOverflows)(int.max - 1); - assert(++x3 == int.max); - assert(x3.hook.calls == 0); - assert(++x3 == int.min); - assert(x3.hook.calls == 1); - assert(-x3 == int.min); - assert(x3.hook.calls == 2); - - x3 = int.min + 1; - assert(--x3 == int.min); - assert(x3.hook.calls == 2); - assert(--x3 == int.max); - assert(x3.hook.calls == 3); -} - -// -@nogc nothrow pure @safe unittest -{ - Checked!(int, void) x; - assert(x == x); - assert(x == +x); - assert(x == -x); - ++x; - assert(x == 1); - x++; - assert(x == 2); - - x = 42; - assert(x == 42); - const short _short = 43; - x = _short; - assert(x == _short); - ushort _ushort = 44; - x = _ushort; - assert(x == _ushort); - assert(x == 44.0); - assert(x != 44.1); - assert(x < 45); - assert(x < 44.2); - assert(x > -45); - assert(x > -44.2); - - assert(cast(long) x == 44); - assert(cast(short) x == 44); - - const Checked!(uint, void) y; - assert(y <= y); - assert(y == 0); - assert(y < x); - x = -1; - assert(x > y); -} - -@nogc nothrow pure @safe unittest -{ - alias cint = Checked!(int, void); - cint a = 1, b = 2; - a += b; - assert(a == cint(3)); - - alias ccint = Checked!(cint, Saturate); - ccint c = 14; - a += c; - assert(a == cint(17)); -} - -// toHash -@safe unittest -{ - assert(checked(42).toHash() == checked(42).toHash()); - assert(checked(12).toHash() != checked(19).toHash()); - - static struct Hook1 - { - static size_t hookToHash(T)(T payload) nothrow @trusted - { - static if (size_t.sizeof == 4) - { - return typeid(payload).getHash(&payload) ^ 0xFFFF_FFFF; - } - else - { - return typeid(payload).getHash(&payload) ^ 0xFFFF_FFFF_FFFF_FFFF; - } - - } - } - - auto a = checked!Hook1(78); - auto b = checked!Hook1(78); - assert(a.toHash() == b.toHash()); - - assert(checked!Hook1(12).toHash() != checked!Hook1(13).toHash()); - - static struct Hook2 - { - static if (size_t.sizeof == 4) - { - static size_t hashMask = 0xFFFF_0000; - } - else - { - static size_t hashMask = 0xFFFF_0000_FFFF_0000; - } - - static size_t hookToHash(T)(T payload) nothrow @trusted - { - return typeid(payload).getHash(&payload) ^ hashMask; - } - } - - auto x = checked!Hook2(1901); - auto y = checked!Hook2(1989); - - assert((() nothrow @safe => x.toHash() == x.toHash())()); - - assert(x.toHash() == x.toHash()); - assert(x.toHash() != y.toHash()); - assert(checked!Hook1(1901).toHash() != x.toHash()); - - immutable z = checked!Hook1(1901); - immutable t = checked!Hook1(1901); - immutable w = checked!Hook2(1901); - - assert(z.toHash() == t.toHash()); - assert(z.toHash() != x.toHash()); - assert(z.toHash() != w.toHash()); - - const long c = 0xF0F0F0F0; - const long d = 0xF0F0F0F0; - - assert(checked!Hook1(c).toHash() != checked!Hook2(c)); - assert(checked!Hook1(c).toHash() != checked!Hook1(d)); - - // Hook with state, does not implement hookToHash - static struct Hook3 - { - ulong var1 = ulong.max; - uint var2 = uint.max; - } - - assert(checked!Hook3(12).toHash() != checked!Hook3(13).toHash()); - assert(checked!Hook3(13).toHash() == checked!Hook3(13).toHash()); - - // Hook with no state and no hookToHash, payload has its own hashing function - auto x1 = Checked!(Checked!int, ProperCompare)(123); - auto x2 = Checked!(Checked!int, ProperCompare)(123); - auto x3 = Checked!(Checked!int, ProperCompare)(144); - - assert(x1.toHash() == x2.toHash()); - assert(x1.toHash() != x3.toHash()); - assert(x2.toHash() != x3.toHash()); - - // Check shared. - { - shared shared0 = checked(12345678); - shared shared1 = checked!Hook1(123456789); - shared shared2 = checked!Hook2(234567891); - shared shared3 = checked!Hook3(345678912); - assert(shared0.toHash() == hashOf(shared0)); - assert(shared1.toHash() == hashOf(shared1)); - assert(shared2.toHash() == hashOf(shared2)); - assert(shared3.toHash() == hashOf(shared3)); - } -} - -/// -@safe unittest -{ - struct MyHook - { - static size_t hookToHash(T)(const T payload) nothrow @trusted - { - return .hashOf(payload); - } - } - - int[Checked!(int, MyHook)] aa; - Checked!(int, MyHook) var = 42; - aa[var] = 100; - - assert(aa[var] == 100); - - int[Checked!(int, Abort)] bb; - Checked!(int, Abort) var2 = 42; - bb[var2] = 100; - - assert(bb[var2] == 100); -} diff --git a/phobos/std/compiler.d b/phobos/std/compiler.d deleted file mode 100644 index 4ea5bd7..0000000 --- a/phobos/std/compiler.d +++ /dev/null @@ -1,58 +0,0 @@ -// Written in the D programming language. - -/** - * Identify the compiler used and its various features. - * - * Copyright: Copyright The D Language Foundation 2000 - 2011. - * License: $(HTTP www.boost.org/LICENSE_1_0.txt, Boost License 1.0). - * Authors: $(HTTP digitalmars.com, Walter Bright), Alex Rønne Petersen - * Source: $(PHOBOSSRC std/compiler.d) - */ -/* Copyright The D Language Foundation 2000 - 2011. - * 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.compiler; - -immutable -{ - /// Vendor specific string naming the compiler, for example: "Digital Mars D". - string name = __VENDOR__; - - /// Master list of D compiler vendors. - enum Vendor - { - unknown = 0, /// Compiler vendor could not be detected - digitalMars = 1, /// Digital Mars D (DMD) - gnu = 2, /// GNU D Compiler (GDC) - llvm = 3, /// LLVM D Compiler (LDC) - dotNET = 4, /// D.NET - sdc = 5, /// Snazzy D Compiler (SDC) - } - - /// Which vendor produced this compiler. - version (StdDdoc) Vendor vendor; - else version (DigitalMars) Vendor vendor = Vendor.digitalMars; - else version (GNU) Vendor vendor = Vendor.gnu; - else version (LDC) Vendor vendor = Vendor.llvm; - else version (D_NET) Vendor vendor = Vendor.dotNET; - else version (SDC) Vendor vendor = Vendor.sdc; - else Vendor vendor = Vendor.unknown; - - - /** - * The vendor specific version number, as in - * version_major.version_minor - */ - uint version_major = __VERSION__ / 1000; - uint version_minor = __VERSION__ % 1000; /// ditto - - - /** - * The version of the D Programming Language Specification - * supported by the compiler. - */ - uint D_major = 2; - uint D_minor = 0; -} diff --git a/phobos/std/complex.d b/phobos/std/complex.d deleted file mode 100644 index 83ff5be..0000000 --- a/phobos/std/complex.d +++ /dev/null @@ -1,1952 +0,0 @@ -// Written in the D programming language. - -/** This module contains the $(LREF Complex) type, which is used to represent - complex numbers, along with related mathematical operations and functions. - - $(LREF Complex) will eventually - $(DDLINK deprecate, Deprecated Features, replace) - the built-in types `cfloat`, `cdouble`, `creal`, `ifloat`, - `idouble`, and `ireal`. - - Macros: - TABLE_SV = - - $0
Special Values
- PLUSMN = ± - NAN = $(RED NAN) - INFIN = ∞ - PI = π - - Authors: Lars Tandle Kyllingstad, Don Clugston - Copyright: Copyright (c) 2010, Lars T. Kyllingstad. - License: $(HTTP boost.org/LICENSE_1_0.txt, Boost License 1.0) - Source: $(PHOBOSSRC std/complex.d) -*/ -module std.complex; - -import std.traits; - -/** Helper function that returns a complex number with the specified - real and imaginary parts. - - Params: - R = (template parameter) type of real part of complex number - I = (template parameter) type of imaginary part of complex number - - re = real part of complex number to be constructed - im = (optional) imaginary part of complex number, 0 if omitted. - - Returns: - `Complex` instance with real and imaginary parts set - to the values provided as input. If neither `re` nor - `im` are floating-point numbers, the return type will - be `Complex!double`. Otherwise, the return type is - deduced using $(D std.traits.CommonType!(R, I)). -*/ -auto complex(R)(const R re) @safe pure nothrow @nogc -if (is(R : double)) -{ - static if (isFloatingPoint!R) - return Complex!R(re, 0); - else - return Complex!double(re, 0); -} - -/// ditto -auto complex(R, I)(const R re, const I im) @safe pure nothrow @nogc -if (is(R : double) && is(I : double)) -{ - static if (isFloatingPoint!R || isFloatingPoint!I) - return Complex!(CommonType!(R, I))(re, im); - else - return Complex!double(re, im); -} - -/// -@safe pure nothrow unittest -{ - auto a = complex(1.0); - static assert(is(typeof(a) == Complex!double)); - assert(a.re == 1.0); - assert(a.im == 0.0); - - auto b = complex(2.0L); - static assert(is(typeof(b) == Complex!real)); - assert(b.re == 2.0L); - assert(b.im == 0.0L); - - auto c = complex(1.0, 2.0); - static assert(is(typeof(c) == Complex!double)); - assert(c.re == 1.0); - assert(c.im == 2.0); - - auto d = complex(3.0, 4.0L); - static assert(is(typeof(d) == Complex!real)); - assert(d.re == 3.0); - assert(d.im == 4.0L); - - auto e = complex(1); - static assert(is(typeof(e) == Complex!double)); - assert(e.re == 1); - assert(e.im == 0); - - auto f = complex(1L, 2); - static assert(is(typeof(f) == Complex!double)); - assert(f.re == 1L); - assert(f.im == 2); - - auto g = complex(3, 4.0L); - static assert(is(typeof(g) == Complex!real)); - assert(g.re == 3); - assert(g.im == 4.0L); -} - - -/** A complex number parametrised by a type `T`, which must be either - `float`, `double` or `real`. -*/ -struct Complex(T) -if (isFloatingPoint!T) -{ - import std.format.spec : FormatSpec; - import std.range.primitives : isOutputRange; - - /** The real part of the number. */ - T re; - - /** The imaginary part of the number. */ - T im; - - /** Converts the complex number to a string representation. - - The second form of this function is usually not called directly; - instead, it is used via $(REF format, std,string), as shown in the examples - below. Supported format characters are 'e', 'f', 'g', 'a', and 's'. - - See the $(MREF std, format) and $(REF format, std,string) - documentation for more information. - */ - string toString() const @safe /* TODO: pure nothrow */ - { - import std.exception : assumeUnique; - char[] buf; - buf.reserve(100); - auto fmt = FormatSpec!char("%s"); - toString((const(char)[] s) { buf ~= s; }, fmt); - static trustedAssumeUnique(T)(T t) @trusted { return assumeUnique(t); } - return trustedAssumeUnique(buf); - } - - static if (is(T == double)) - /// - @safe unittest - { - auto c = complex(1.2, 3.4); - - // Vanilla toString formatting: - assert(c.toString() == "1.2+3.4i"); - - // Formatting with std.string.format specs: the precision and width - // specifiers apply to both the real and imaginary parts of the - // complex number. - import std.format : format; - assert(format("%.2f", c) == "1.20+3.40i"); - assert(format("%4.1f", c) == " 1.2+ 3.4i"); - } - - /// ditto - void toString(Writer, Char)(scope Writer w, scope const ref FormatSpec!Char formatSpec) const - if (isOutputRange!(Writer, const(Char)[])) - { - import std.format.write : formatValue; - import std.math.traits : signbit; - import std.range.primitives : put; - formatValue(w, re, formatSpec); - if (signbit(im) == 0) - put(w, "+"); - formatValue(w, im, formatSpec); - put(w, "i"); - } - -@safe pure nothrow @nogc: - - /** Construct a complex number with the specified real and - imaginary parts. In the case where a single argument is passed - that is not complex, the imaginary part of the result will be - zero. - */ - this(R : T)(Complex!R z) - { - re = z.re; - im = z.im; - } - - /// ditto - this(Rx : T, Ry : T)(const Rx x, const Ry y) - { - re = x; - im = y; - } - - /// ditto - this(R : T)(const R r) - { - re = r; - im = 0; - } - - // ASSIGNMENT OPERATORS - - // this = complex - ref Complex opAssign(R : T)(Complex!R z) - { - re = z.re; - im = z.im; - return this; - } - - // this = numeric - ref Complex opAssign(R : T)(const R r) - { - re = r; - im = 0; - return this; - } - - // COMPARISON OPERATORS - - // this == complex - bool opEquals(R : T)(Complex!R z) const - { - return re == z.re && im == z.im; - } - - // this == numeric - bool opEquals(R : T)(const R r) const - { - return re == r && im == 0; - } - - // UNARY OPERATORS - - // +complex - Complex opUnary(string op)() const - if (op == "+") - { - return this; - } - - // -complex - Complex opUnary(string op)() const - if (op == "-") - { - return Complex(-re, -im); - } - - // BINARY OPERATORS - - // complex op complex - Complex!(CommonType!(T,R)) opBinary(string op, R)(Complex!R z) const - { - alias C = typeof(return); - auto w = C(this.re, this.im); - return w.opOpAssign!(op)(z); - } - - // complex op numeric - Complex!(CommonType!(T,R)) opBinary(string op, R)(const R r) const - if (isNumeric!R) - { - alias C = typeof(return); - auto w = C(this.re, this.im); - return w.opOpAssign!(op)(r); - } - - // numeric + complex, numeric * complex - Complex!(CommonType!(T, R)) opBinaryRight(string op, R)(const R r) const - if ((op == "+" || op == "*") && (isNumeric!R)) - { - return opBinary!(op)(r); - } - - // numeric - complex - Complex!(CommonType!(T, R)) opBinaryRight(string op, R)(const R r) const - if (op == "-" && isNumeric!R) - { - return Complex(r - re, -im); - } - - // numeric / complex - Complex!(CommonType!(T, R)) opBinaryRight(string op, R)(const R r) const - if (op == "/" && isNumeric!R) - { - version (FastMath) - { - // Compute norm(this) - immutable norm = re * re + im * im; - // Compute r * conj(this) - immutable prod_re = r * re; - immutable prod_im = r * -im; - // Divide the product by the norm - typeof(return) w = void; - w.re = prod_re / norm; - w.im = prod_im / norm; - return w; - } - else - { - import core.math : fabs; - typeof(return) w = void; - if (fabs(re) < fabs(im)) - { - immutable ratio = re/im; - immutable rdivd = r/(re*ratio + im); - - w.re = rdivd*ratio; - w.im = -rdivd; - } - else - { - immutable ratio = im/re; - immutable rdivd = r/(re + im*ratio); - - w.re = rdivd; - w.im = -rdivd*ratio; - } - - return w; - } - } - - // numeric ^^ complex - Complex!(CommonType!(T, R)) opBinaryRight(string op, R)(const R lhs) const - if (op == "^^" && isNumeric!R) - { - import core.math : cos, sin; - import std.math.exponential : exp, log; - import std.math.constants : PI; - Unqual!(CommonType!(T, R)) ab = void, ar = void; - - if (lhs >= 0) - { - // r = lhs - // theta = 0 - ab = lhs ^^ this.re; - ar = log(lhs) * this.im; - } - else - { - // r = -lhs - // theta = PI - ab = (-lhs) ^^ this.re * exp(-PI * this.im); - ar = PI * this.re + log(-lhs) * this.im; - } - - return typeof(return)(ab * cos(ar), ab * sin(ar)); - } - - // OP-ASSIGN OPERATORS - - // complex += complex, complex -= complex - ref Complex opOpAssign(string op, C)(const C z) - if ((op == "+" || op == "-") && is(C R == Complex!R)) - { - mixin ("re "~op~"= z.re;"); - mixin ("im "~op~"= z.im;"); - return this; - } - - // complex *= complex - ref Complex opOpAssign(string op, C)(const C z) - if (op == "*" && is(C R == Complex!R)) - { - auto temp = re*z.re - im*z.im; - im = im*z.re + re*z.im; - re = temp; - return this; - } - - // complex /= complex - ref Complex opOpAssign(string op, C)(const C z) - if (op == "/" && is(C R == Complex!R)) - { - version (FastMath) - { - // Compute norm(z) - immutable norm = z.re * z.re + z.im * z.im; - // Compute this * conj(z) - immutable prod_re = re * z.re - im * -z.im; - immutable prod_im = im * z.re + re * -z.im; - // Divide the product by the norm - re = prod_re / norm; - im = prod_im / norm; - return this; - } - else - { - import core.math : fabs; - if (fabs(z.re) < fabs(z.im)) - { - immutable ratio = z.re/z.im; - immutable denom = z.re*ratio + z.im; - - immutable temp = (re*ratio + im)/denom; - im = (im*ratio - re)/denom; - re = temp; - } - else - { - immutable ratio = z.im/z.re; - immutable denom = z.re + z.im*ratio; - - immutable temp = (re + im*ratio)/denom; - im = (im - re*ratio)/denom; - re = temp; - } - return this; - } - } - - // complex ^^= complex - ref Complex opOpAssign(string op, C)(const C z) - if (op == "^^" && is(C R == Complex!R)) - { - import core.math : cos, sin; - import std.math.exponential : exp, log; - immutable r = abs(this); - immutable t = arg(this); - immutable ab = r^^z.re * exp(-t*z.im); - immutable ar = t*z.re + log(r)*z.im; - - re = ab*cos(ar); - im = ab*sin(ar); - return this; - } - - // complex += numeric, complex -= numeric - ref Complex opOpAssign(string op, U : T)(const U a) - if (op == "+" || op == "-") - { - mixin ("re "~op~"= a;"); - return this; - } - - // complex *= numeric, complex /= numeric - ref Complex opOpAssign(string op, U : T)(const U a) - if (op == "*" || op == "/") - { - mixin ("re "~op~"= a;"); - mixin ("im "~op~"= a;"); - return this; - } - - // complex ^^= real - ref Complex opOpAssign(string op, R)(const R r) - if (op == "^^" && isFloatingPoint!R) - { - import core.math : cos, sin; - immutable ab = abs(this)^^r; - immutable ar = arg(this)*r; - re = ab*cos(ar); - im = ab*sin(ar); - return this; - } - - // complex ^^= int - ref Complex opOpAssign(string op, U)(const U i) - if (op == "^^" && isIntegral!U) - { - switch (i) - { - case 0: - re = 1.0; - im = 0.0; - break; - case 1: - // identity; do nothing - break; - case 2: - this *= this; - break; - case 3: - auto z = this; - this *= z; - this *= z; - break; - default: - this ^^= cast(real) i; - } - return this; - } - - /** Returns a complex number instance that correponds in size and in ABI - to the associated C compiler's `_Complex` type. - */ - auto toNative() - { - import core.stdc.config : c_complex_float, c_complex_double, c_complex_real; - static if (is(T == float)) - return c_complex_float(re, im); - else static if (is(T == double)) - return c_complex_double(re, im); - else - return c_complex_real(re, im); - } -} - -@safe pure nothrow unittest -{ - import std.complex; - static import core.math; - import std.math; - - enum EPS = double.epsilon; - auto c1 = complex(1.0, 1.0); - - // Check unary operations. - auto c2 = Complex!double(0.5, 2.0); - - assert(c2 == +c2); - - assert((-c2).re == -(c2.re)); - assert((-c2).im == -(c2.im)); - assert(c2 == -(-c2)); - - // Check complex-complex operations. - auto cpc = c1 + c2; - assert(cpc.re == c1.re + c2.re); - assert(cpc.im == c1.im + c2.im); - - auto cmc = c1 - c2; - assert(cmc.re == c1.re - c2.re); - assert(cmc.im == c1.im - c2.im); - - auto ctc = c1 * c2; - assert(isClose(abs(ctc), abs(c1)*abs(c2), EPS)); - assert(isClose(arg(ctc), arg(c1)+arg(c2), EPS)); - - auto cdc = c1 / c2; - assert(isClose(abs(cdc), abs(c1)/abs(c2), EPS)); - assert(isClose(arg(cdc), arg(c1)-arg(c2), EPS)); - - auto cec = c1^^c2; - assert(isClose(cec.re, 0.1152413197994, 1e-12)); - assert(isClose(cec.im, 0.2187079045274, 1e-12)); - - // Check complex-real operations. - double a = 123.456; - - auto cpr = c1 + a; - assert(cpr.re == c1.re + a); - assert(cpr.im == c1.im); - - auto cmr = c1 - a; - assert(cmr.re == c1.re - a); - assert(cmr.im == c1.im); - - auto ctr = c1 * a; - assert(ctr.re == c1.re*a); - assert(ctr.im == c1.im*a); - - auto cdr = c1 / a; - assert(isClose(abs(cdr), abs(c1)/a, EPS)); - assert(isClose(arg(cdr), arg(c1), EPS)); - - auto cer = c1^^3.0; - assert(isClose(abs(cer), abs(c1)^^3, EPS)); - assert(isClose(arg(cer), arg(c1)*3, EPS)); - - auto rpc = a + c1; - assert(rpc == cpr); - - auto rmc = a - c1; - assert(rmc.re == a-c1.re); - assert(rmc.im == -c1.im); - - auto rtc = a * c1; - assert(rtc == ctr); - - auto rdc = a / c1; - assert(isClose(abs(rdc), a/abs(c1), EPS)); - assert(isClose(arg(rdc), -arg(c1), EPS)); - - rdc = a / c2; - assert(isClose(abs(rdc), a/abs(c2), EPS)); - assert(isClose(arg(rdc), -arg(c2), EPS)); - - auto rec1a = 1.0 ^^ c1; - assert(rec1a.re == 1.0); - assert(rec1a.im == 0.0); - - auto rec2a = 1.0 ^^ c2; - assert(rec2a.re == 1.0); - assert(rec2a.im == 0.0); - - auto rec1b = (-1.0) ^^ c1; - assert(isClose(abs(rec1b), std.math.exp(-PI * c1.im), EPS)); - auto arg1b = arg(rec1b); - /* The argument _should_ be PI, but floating-point rounding error - * means that in fact the imaginary part is very slightly negative. - */ - assert(isClose(arg1b, PI, EPS) || isClose(arg1b, -PI, EPS)); - - auto rec2b = (-1.0) ^^ c2; - assert(isClose(abs(rec2b), std.math.exp(-2 * PI), EPS)); - assert(isClose(arg(rec2b), PI_2, EPS)); - - auto rec3a = 0.79 ^^ complex(6.8, 5.7); - auto rec3b = complex(0.79, 0.0) ^^ complex(6.8, 5.7); - assert(isClose(rec3a.re, rec3b.re, 1e-14)); - assert(isClose(rec3a.im, rec3b.im, 1e-14)); - - auto rec4a = (-0.79) ^^ complex(6.8, 5.7); - auto rec4b = complex(-0.79, 0.0) ^^ complex(6.8, 5.7); - assert(isClose(rec4a.re, rec4b.re, 1e-14)); - assert(isClose(rec4a.im, rec4b.im, 1e-14)); - - auto rer = a ^^ complex(2.0, 0.0); - auto rcheck = a ^^ 2.0; - static assert(is(typeof(rcheck) == double)); - assert(feqrel(rer.re, rcheck) == double.mant_dig); - assert(isIdentical(rer.re, rcheck)); - assert(rer.im == 0.0); - - auto rer2 = (-a) ^^ complex(2.0, 0.0); - rcheck = (-a) ^^ 2.0; - assert(feqrel(rer2.re, rcheck) == double.mant_dig); - assert(isIdentical(rer2.re, rcheck)); - assert(isClose(rer2.im, 0.0, 0.0, 1e-10)); - - auto rer3 = (-a) ^^ complex(-2.0, 0.0); - rcheck = (-a) ^^ (-2.0); - assert(feqrel(rer3.re, rcheck) == double.mant_dig); - assert(isIdentical(rer3.re, rcheck)); - assert(isClose(rer3.im, 0.0, 0.0, EPS)); - - auto rer4 = a ^^ complex(-2.0, 0.0); - rcheck = a ^^ (-2.0); - assert(feqrel(rer4.re, rcheck) == double.mant_dig); - assert(isIdentical(rer4.re, rcheck)); - assert(rer4.im == 0.0); - - // Check Complex-int operations. - foreach (i; 0 .. 6) - { - auto cei = c1^^i; - assert(isClose(abs(cei), abs(c1)^^i, 1e-14)); - // Use cos() here to deal with arguments that go outside - // the (-pi,pi] interval (only an issue for i>3). - assert(isClose(core.math.cos(arg(cei)), core.math.cos(arg(c1)*i), 1e-14)); - } - - // Check operations between different complex types. - auto cf = Complex!float(1.0, 1.0); - auto cr = Complex!real(1.0, 1.0); - auto c1pcf = c1 + cf; - auto c1pcr = c1 + cr; - static assert(is(typeof(c1pcf) == Complex!double)); - static assert(is(typeof(c1pcr) == Complex!real)); - assert(c1pcf.re == c1pcr.re); - assert(c1pcf.im == c1pcr.im); - - auto c1c = c1; - auto c2c = c2; - - c1c /= c1; - assert(isClose(c1c.re, 1.0, EPS)); - assert(isClose(c1c.im, 0.0, 0.0, EPS)); - - c1c = c1; - c1c /= c2; - assert(isClose(c1c.re, 0.5882352941177, 1e-12)); - assert(isClose(c1c.im, -0.3529411764706, 1e-12)); - - c2c /= c1; - assert(isClose(c2c.re, 1.25, EPS)); - assert(isClose(c2c.im, 0.75, EPS)); - - c2c = c2; - c2c /= c2; - assert(isClose(c2c.re, 1.0, EPS)); - assert(isClose(c2c.im, 0.0, 0.0, EPS)); -} - -@safe pure nothrow unittest -{ - // Initialization - Complex!double a = 1; - assert(a.re == 1 && a.im == 0); - Complex!double b = 1.0; - assert(b.re == 1.0 && b.im == 0); - Complex!double c = Complex!real(1.0, 2); - assert(c.re == 1.0 && c.im == 2); -} - -@safe pure nothrow unittest -{ - // Assignments and comparisons - Complex!double z; - - z = 1; - assert(z == 1); - assert(z.re == 1.0 && z.im == 0.0); - - z = 2.0; - assert(z == 2.0); - assert(z.re == 2.0 && z.im == 0.0); - - z = 1.0L; - assert(z == 1.0L); - assert(z.re == 1.0 && z.im == 0.0); - - auto w = Complex!real(1.0, 1.0); - z = w; - assert(z == w); - assert(z.re == 1.0 && z.im == 1.0); - - auto c = Complex!float(2.0, 2.0); - z = c; - assert(z == c); - assert(z.re == 2.0 && z.im == 2.0); -} - - -/* Makes Complex!(Complex!T) fold to Complex!T. - - The rationale for this is that just like the real line is a - subspace of the complex plane, the complex plane is a subspace - of itself. Example of usage: - --- - Complex!T addI(T)(T x) - { - return x + Complex!T(0.0, 1.0); - } - --- - The above will work if T is both real and complex. -*/ -template Complex(T) -if (is(T R == Complex!R)) -{ - alias Complex = T; -} - -@safe pure nothrow unittest -{ - static assert(is(Complex!(Complex!real) == Complex!real)); - - Complex!T addI(T)(T x) - { - return x + Complex!T(0.0, 1.0); - } - - auto z1 = addI(1.0); - assert(z1.re == 1.0 && z1.im == 1.0); - - enum one = Complex!double(1.0, 0.0); - auto z2 = addI(one); - assert(z1 == z2); -} - - -/** - Params: z = A complex number. - Returns: The absolute value (or modulus) of `z`. -*/ -T abs(T)(Complex!T z) @safe pure nothrow @nogc -{ - import std.math.algebraic : hypot; - return hypot(z.re, z.im); -} - -/// -@safe pure nothrow unittest -{ - static import core.math; - assert(abs(complex(1.0)) == 1.0); - assert(abs(complex(0.0, 1.0)) == 1.0); - assert(abs(complex(1.0L, -2.0L)) == core.math.sqrt(5.0L)); -} - -@safe pure nothrow @nogc unittest -{ - static import core.math; - assert(abs(complex(0.0L, -3.2L)) == 3.2L); - assert(abs(complex(0.0L, 71.6L)) == 71.6L); - assert(abs(complex(-1.0L, 1.0L)) == core.math.sqrt(2.0L)); -} - -@safe pure nothrow @nogc unittest -{ - import std.meta : AliasSeq; - static foreach (T; AliasSeq!(float, double, real)) - {{ - static import std.math; - Complex!T a = complex(T(-12), T(3)); - T b = std.math.hypot(a.re, a.im); - assert(std.math.isClose(abs(a), b)); - assert(std.math.isClose(abs(-a), b)); - }} -} - -/++ - Params: - z = A complex number. - x = A real number. - Returns: The squared modulus of `z`. - For genericity, if called on a real number, returns its square. -+/ -T sqAbs(T)(Complex!T z) @safe pure nothrow @nogc -{ - return z.re*z.re + z.im*z.im; -} - -/// -@safe pure nothrow unittest -{ - import std.math.operations : isClose; - assert(sqAbs(complex(0.0)) == 0.0); - assert(sqAbs(complex(1.0)) == 1.0); - assert(sqAbs(complex(0.0, 1.0)) == 1.0); - assert(isClose(sqAbs(complex(1.0L, -2.0L)), 5.0L)); - assert(isClose(sqAbs(complex(-3.0L, 1.0L)), 10.0L)); - assert(isClose(sqAbs(complex(1.0f,-1.0f)), 2.0f)); -} - -/// ditto -T sqAbs(T)(const T x) @safe pure nothrow @nogc -if (isFloatingPoint!T) -{ - return x*x; -} - -@safe pure nothrow unittest -{ - import std.math.operations : isClose; - assert(sqAbs(0.0) == 0.0); - assert(sqAbs(-1.0) == 1.0); - assert(isClose(sqAbs(-3.0L), 9.0L)); - assert(isClose(sqAbs(-5.0f), 25.0f)); -} - - -/** - Params: z = A complex number. - Returns: The argument (or phase) of `z`. - */ -T arg(T)(Complex!T z) @safe pure nothrow @nogc -{ - import std.math.trigonometry : atan2; - return atan2(z.im, z.re); -} - -/// -@safe pure nothrow unittest -{ - import std.math.constants : PI_2, PI_4; - assert(arg(complex(1.0)) == 0.0); - assert(arg(complex(0.0L, 1.0L)) == PI_2); - assert(arg(complex(1.0L, 1.0L)) == PI_4); -} - - -/** - * Extracts the norm of a complex number. - * Params: - * z = A complex number - * Returns: - * The squared magnitude of `z`. - */ -T norm(T)(Complex!T z) @safe pure nothrow @nogc -{ - return z.re * z.re + z.im * z.im; -} - -/// -@safe pure nothrow @nogc unittest -{ - import std.math.operations : isClose; - import std.math.constants : PI; - assert(norm(complex(3.0, 4.0)) == 25.0); - assert(norm(fromPolar(5.0, 0.0)) == 25.0); - assert(isClose(norm(fromPolar(5.0L, PI / 6)), 25.0L)); - assert(isClose(norm(fromPolar(5.0L, 13 * PI / 6)), 25.0L)); -} - - -/** - Params: z = A complex number. - Returns: The complex conjugate of `z`. -*/ -Complex!T conj(T)(Complex!T z) @safe pure nothrow @nogc -{ - return Complex!T(z.re, -z.im); -} - -/// -@safe pure nothrow unittest -{ - assert(conj(complex(1.0)) == complex(1.0)); - assert(conj(complex(1.0, 2.0)) == complex(1.0, -2.0)); -} - -@safe pure nothrow @nogc unittest -{ - import std.meta : AliasSeq; - static foreach (T; AliasSeq!(float, double, real)) - {{ - auto c = Complex!T(7, 3L); - assert(conj(c) == Complex!T(7, -3L)); - auto z = Complex!T(0, -3.2L); - assert(conj(z) == -z); - }} -} - -/** - * Returns the projection of `z` onto the Riemann sphere. - * Params: - * z = A complex number - * Returns: - * The projection of `z` onto the Riemann sphere. - */ -Complex!T proj(T)(Complex!T z) -{ - static import std.math; - - if (std.math.isInfinity(z.re) || std.math.isInfinity(z.im)) - return Complex!T(T.infinity, std.math.copysign(0.0, z.im)); - - return z; -} - -/// -@safe pure nothrow unittest -{ - assert(proj(complex(1.0)) == complex(1.0)); - assert(proj(complex(double.infinity, 5.0)) == complex(double.infinity, 0.0)); - assert(proj(complex(5.0, -double.infinity)) == complex(double.infinity, -0.0)); -} - - -/** - Constructs a complex number given its absolute value and argument. - Params: - modulus = The modulus - argument = The argument - Returns: The complex number with the given modulus and argument. -*/ -Complex!(CommonType!(T, U)) fromPolar(T, U)(const T modulus, const U argument) - @safe pure nothrow @nogc -{ - import core.math : sin, cos; - return Complex!(CommonType!(T,U)) - (modulus*cos(argument), modulus*sin(argument)); -} - -/// -@safe pure nothrow unittest -{ - import core.math; - import std.math.operations : isClose; - import std.math.algebraic : sqrt; - import std.math.constants : PI_4; - auto z = fromPolar(core.math.sqrt(2.0L), PI_4); - assert(isClose(z.re, 1.0L)); - assert(isClose(z.im, 1.0L)); -} - -version (StdUnittest) -{ - // Helper function for comparing two Complex numbers. - int ceqrel(T)(const Complex!T x, const Complex!T y) @safe pure nothrow @nogc - { - import std.math.operations : feqrel; - const r = feqrel(x.re, y.re); - const i = feqrel(x.im, y.im); - return r < i ? r : i; - } -} - -/** - Trigonometric functions on complex numbers. - - Params: z = A complex number. - Returns: The sine, cosine and tangent of `z`, respectively. -*/ -Complex!T sin(T)(Complex!T z) @safe pure nothrow @nogc -{ - auto cs = expi(z.re); - auto csh = coshisinh(z.im); - return typeof(return)(cs.im * csh.re, cs.re * csh.im); -} - -/// -@safe pure nothrow unittest -{ - static import core.math; - assert(sin(complex(0.0)) == 0.0); - assert(sin(complex(2.0, 0)) == core.math.sin(2.0)); -} - -@safe pure nothrow unittest -{ - static import core.math; - assert(ceqrel(sin(complex(2.0L, 0)), complex(core.math.sin(2.0L))) >= real.mant_dig - 1); -} - -/// ditto -Complex!T cos(T)(Complex!T z) @safe pure nothrow @nogc -{ - auto cs = expi(z.re); - auto csh = coshisinh(z.im); - return typeof(return)(cs.re * csh.re, - cs.im * csh.im); -} - -/// -@safe pure nothrow unittest -{ - static import core.math; - static import std.math; - assert(cos(complex(0.0)) == 1.0); - assert(cos(complex(1.3, 0.0)) == core.math.cos(1.3)); - assert(cos(complex(0.0, 5.2)) == std.math.cosh(5.2)); -} - -@safe pure nothrow unittest -{ - static import core.math; - static import std.math; - assert(ceqrel(cos(complex(0, 5.2L)), complex(std.math.cosh(5.2L), 0.0L)) >= real.mant_dig - 1); - assert(ceqrel(cos(complex(1.3L)), complex(core.math.cos(1.3L))) >= real.mant_dig - 1); -} - -/// ditto -Complex!T tan(T)(Complex!T z) @safe pure nothrow @nogc -{ - return sin(z) / cos(z); -} - -/// -@safe pure nothrow @nogc unittest -{ - static import std.math; - - int ceqrel(T)(const Complex!T x, const Complex!T y) @safe pure nothrow @nogc - { - import std.math.operations : feqrel; - const r = feqrel(x.re, y.re); - const i = feqrel(x.im, y.im); - return r < i ? r : i; - } - assert(ceqrel(tan(complex(1.0, 0.0)), complex(std.math.tan(1.0), 0.0)) >= double.mant_dig - 2); - assert(ceqrel(tan(complex(0.0, 1.0)), complex(0.0, std.math.tanh(1.0))) >= double.mant_dig - 2); -} - -/** - Inverse trigonometric functions on complex numbers. - - Params: z = A complex number. - Returns: The arcsine, arccosine and arctangent of `z`, respectively. -*/ -Complex!T asin(T)(Complex!T z) @safe pure nothrow @nogc -{ - auto ash = asinh(Complex!T(-z.im, z.re)); - return Complex!T(ash.im, -ash.re); -} - -/// -@safe pure nothrow unittest -{ - import std.math.operations : isClose; - import std.math.constants : PI; - assert(asin(complex(0.0)) == 0.0); - assert(isClose(asin(complex(0.5L)), PI / 6, 0, 1e-15)); -} - -@safe pure nothrow unittest -{ - import std.math.operations : isClose; - import std.math.constants : PI; - version (DigitalMars) {} else // Disabled because of https://issues.dlang.org/show_bug.cgi?id=21376 - assert(isClose(asin(complex(0.5f)), float(PI) / 6)); -} - -/// ditto -Complex!T acos(T)(Complex!T z) @safe pure nothrow @nogc -{ - static import std.math; - auto as = asin(z); - return Complex!T(T(std.math.PI_2) - as.re, as.im); -} - -/// -@safe pure nothrow unittest -{ - import std.math.operations : isClose; - import std.math.constants : PI; - import std.math.trigonometry : std_math_acos = acos; - assert(acos(complex(0.0)) == std_math_acos(0.0)); - assert(isClose(acos(complex(0.5L)), PI / 3, 0, 1e-15)); -} - -@safe pure nothrow unittest -{ - import std.math.operations : isClose; - import std.math.constants : PI; - version (DigitalMars) {} else // Disabled because of https://issues.dlang.org/show_bug.cgi?id=21376 - assert(isClose(acos(complex(0.5f)), float(PI) / 3)); -} - -/// ditto -Complex!T atan(T)(Complex!T z) @safe pure nothrow @nogc -{ - static import std.math; - const T re2 = z.re * z.re; - const T x = 1 - re2 - z.im * z.im; - - T num = z.im + 1; - T den = z.im - 1; - - num = re2 + num * num; - den = re2 + den * den; - - return Complex!T(T(0.5) * std.math.atan2(2 * z.re, x), - T(0.25) * std.math.log(num / den)); -} - -/// -@safe pure nothrow @nogc unittest -{ - import std.math.operations : isClose; - import std.math.constants : PI; - assert(atan(complex(0.0)) == 0.0); - assert(isClose(atan(sqrt(complex(3.0L))), PI / 3)); - assert(isClose(atan(sqrt(complex(3.0f))), float(PI) / 3)); -} - -/** - Hyperbolic trigonometric functions on complex numbers. - - Params: z = A complex number. - Returns: The hyperbolic sine, cosine and tangent of `z`, respectively. -*/ -Complex!T sinh(T)(Complex!T z) @safe pure nothrow @nogc -{ - static import core.math, std.math; - return Complex!T(std.math.sinh(z.re) * core.math.cos(z.im), - std.math.cosh(z.re) * core.math.sin(z.im)); -} - -/// -@safe pure nothrow unittest -{ - static import std.math; - assert(sinh(complex(0.0)) == 0.0); - assert(sinh(complex(1.0L)) == std.math.sinh(1.0L)); - assert(sinh(complex(1.0f)) == std.math.sinh(1.0f)); -} - -/// ditto -Complex!T cosh(T)(Complex!T z) @safe pure nothrow @nogc -{ - static import core.math, std.math; - return Complex!T(std.math.cosh(z.re) * core.math.cos(z.im), - std.math.sinh(z.re) * core.math.sin(z.im)); -} - -/// -@safe pure nothrow unittest -{ - static import std.math; - assert(cosh(complex(0.0)) == 1.0); - assert(cosh(complex(1.0L)) == std.math.cosh(1.0L)); - assert(cosh(complex(1.0f)) == std.math.cosh(1.0f)); -} - -/// ditto -Complex!T tanh(T)(Complex!T z) @safe pure nothrow @nogc -{ - return sinh(z) / cosh(z); -} - -/// -@safe pure nothrow @nogc unittest -{ - import std.math.operations : isClose; - import std.math.trigonometry : std_math_tanh = tanh; - assert(tanh(complex(0.0)) == 0.0); - assert(isClose(tanh(complex(1.0L)), std_math_tanh(1.0L))); - assert(isClose(tanh(complex(1.0f)), std_math_tanh(1.0f))); -} - -/** - Inverse hyperbolic trigonometric functions on complex numbers. - - Params: z = A complex number. - Returns: The hyperbolic arcsine, arccosine and arctangent of `z`, respectively. -*/ -Complex!T asinh(T)(Complex!T z) @safe pure nothrow @nogc -{ - auto t = Complex!T((z.re - z.im) * (z.re + z.im) + 1, 2 * z.re * z.im); - return log(sqrt(t) + z); -} - -/// -@safe pure nothrow unittest -{ - import std.math.operations : isClose; - import std.math.trigonometry : std_math_asinh = asinh; - assert(asinh(complex(0.0)) == 0.0); - assert(isClose(asinh(complex(1.0L)), std_math_asinh(1.0L))); - assert(isClose(asinh(complex(1.0f)), std_math_asinh(1.0f))); -} - -/// ditto -Complex!T acosh(T)(Complex!T z) @safe pure nothrow @nogc -{ - return 2 * log(sqrt(T(0.5) * (z + 1)) + sqrt(T(0.5) * (z - 1))); -} - -/// -@safe pure nothrow unittest -{ - import std.math.operations : isClose; - import std.math.trigonometry : std_math_acosh = acosh; - assert(acosh(complex(1.0)) == 0.0); - assert(isClose(acosh(complex(3.0L)), std_math_acosh(3.0L))); - assert(isClose(acosh(complex(3.0f)), std_math_acosh(3.0f))); -} - -/// ditto -Complex!T atanh(T)(Complex!T z) @safe pure nothrow @nogc -{ - static import std.math; - const T im2 = z.im * z.im; - const T x = 1 - im2 - z.re * z.re; - - T num = 1 + z.re; - T den = 1 - z.re; - - num = im2 + num * num; - den = im2 + den * den; - - return Complex!T(T(0.25) * (std.math.log(num) - std.math.log(den)), - T(0.5) * std.math.atan2(2 * z.im, x)); -} - -/// -@safe pure nothrow @nogc unittest -{ - import std.math.operations : isClose; - import std.math.trigonometry : std_math_atanh = atanh; - assert(atanh(complex(0.0)) == 0.0); - assert(isClose(atanh(complex(0.5L)), std_math_atanh(0.5L))); - assert(isClose(atanh(complex(0.5f)), std_math_atanh(0.5f))); -} - -/** - Params: y = A real number. - Returns: The value of cos(y) + i sin(y). - - Note: - `expi` is included here for convenience and for easy migration of code. -*/ -Complex!real expi(real y) @trusted pure nothrow @nogc -{ - import core.math : cos, sin; - version (none) // LDC: this is the old asm version from std.math.expi - re-enable? - { - static if (real.mant_dig == 64) // x87 - { - if (!__ctfe) - { - Complex!real r = void; - asm @trusted pure nothrow @nogc - { - "fsincos" : "=st" (r.re), "=st(1)" (r.im) : "st" (y) : "flags"; - } - return r; - } - } - } - return Complex!real(cos(y), sin(y)); -} - -/// -@safe pure nothrow unittest -{ - import core.math : cos, sin; - assert(expi(0.0L) == 1.0L); - assert(expi(1.3e5L) == complex(cos(1.3e5L), sin(1.3e5L))); -} - -/** - Params: y = A real number. - Returns: The value of cosh(y) + i sinh(y) - - Note: - `coshisinh` is included here for convenience and for easy migration of code. -*/ -Complex!real coshisinh(real y) @safe pure nothrow @nogc -{ - static import core.math; - static import std.math; - if (core.math.fabs(y) <= 0.5) - return Complex!real(std.math.cosh(y), std.math.sinh(y)); - else - { - auto z = std.math.exp(y); - auto zi = 0.5 / z; - z = 0.5 * z; - return Complex!real(z + zi, z - zi); - } -} - -/// -@safe pure nothrow @nogc unittest -{ - import std.math.trigonometry : cosh, sinh; - assert(coshisinh(3.0L) == complex(cosh(3.0L), sinh(3.0L))); -} - -/** - Params: z = A complex number. - Returns: The square root of `z`. -*/ -Complex!T sqrt(T)(Complex!T z) @safe pure nothrow @nogc -{ - static import core.math; - typeof(return) c; - real x,y,w,r; - - if (z == 0) - { - c = typeof(return)(0, 0); - } - else - { - real z_re = z.re; - real z_im = z.im; - - x = core.math.fabs(z_re); - y = core.math.fabs(z_im); - if (x >= y) - { - r = y / x; - w = core.math.sqrt(x) - * core.math.sqrt(0.5 * (1 + core.math.sqrt(1 + r * r))); - } - else - { - r = x / y; - w = core.math.sqrt(y) - * core.math.sqrt(0.5 * (r + core.math.sqrt(1 + r * r))); - } - - if (z_re >= 0) - { - c = typeof(return)(w, z_im / (w + w)); - } - else - { - if (z_im < 0) - w = -w; - c = typeof(return)(z_im / (w + w), w); - } - } - return c; -} - -/// -@safe pure nothrow unittest -{ - static import core.math; - assert(sqrt(complex(0.0)) == 0.0); - assert(sqrt(complex(1.0L, 0)) == core.math.sqrt(1.0L)); - assert(sqrt(complex(-1.0L, 0)) == complex(0, 1.0L)); - assert(sqrt(complex(-8.0, -6.0)) == complex(1.0, -3.0)); -} - -@safe pure nothrow unittest -{ - import std.math.operations : isClose; - - auto c1 = complex(1.0, 1.0); - auto c2 = Complex!double(0.5, 2.0); - - auto c1s = sqrt(c1); - assert(isClose(c1s.re, 1.09868411347)); - assert(isClose(c1s.im, 0.455089860562)); - - auto c2s = sqrt(c2); - assert(isClose(c2s.re, 1.13171392428)); - assert(isClose(c2s.im, 0.883615530876)); -} - -// support %f formatting of complex numbers -// https://issues.dlang.org/show_bug.cgi?id=10881 -@safe unittest -{ - import std.format : format; - - auto x = complex(1.2, 3.4); - assert(format("%.2f", x) == "1.20+3.40i"); - - auto y = complex(1.2, -3.4); - assert(format("%.2f", y) == "1.20-3.40i"); -} - -@safe unittest -{ - // Test wide string formatting - import std.format.write : formattedWrite; - wstring wformat(T)(string format, Complex!T c) - { - import std.array : appender; - auto w = appender!wstring(); - auto n = formattedWrite(w, format, c); - return w.data; - } - - auto x = complex(1.2, 3.4); - assert(wformat("%.2f", x) == "1.20+3.40i"w); -} - -@safe unittest -{ - // Test ease of use (vanilla toString() should be supported) - assert(complex(1.2, 3.4).toString() == "1.2+3.4i"); -} - -@safe pure nothrow @nogc unittest -{ - auto c = complex(3.0L, 4.0L); - c = sqrt(c); - assert(c.re == 2.0L); - assert(c.im == 1.0L); -} - -/** - * Calculates e$(SUPERSCRIPT x). - * Params: - * x = A complex number - * Returns: - * The complex base e exponential of `x` - * - * $(TABLE_SV - * $(TR $(TH x) $(TH exp(x))) - * $(TR $(TD ($(PLUSMN)0, +0)) $(TD (1, +0))) - * $(TR $(TD (any, +$(INFIN))) $(TD ($(NAN), $(NAN)))) - * $(TR $(TD (any, $(NAN)) $(TD ($(NAN), $(NAN))))) - * $(TR $(TD (+$(INFIN), +0)) $(TD (+$(INFIN), +0))) - * $(TR $(TD (-$(INFIN), any)) $(TD ($(PLUSMN)0, cis(x.im)))) - * $(TR $(TD (+$(INFIN), any)) $(TD ($(PLUSMN)$(INFIN), cis(x.im)))) - * $(TR $(TD (-$(INFIN), +$(INFIN))) $(TD ($(PLUSMN)0, $(PLUSMN)0))) - * $(TR $(TD (+$(INFIN), +$(INFIN))) $(TD ($(PLUSMN)$(INFIN), $(NAN)))) - * $(TR $(TD (-$(INFIN), $(NAN))) $(TD ($(PLUSMN)0, $(PLUSMN)0))) - * $(TR $(TD (+$(INFIN), $(NAN))) $(TD ($(PLUSMN)$(INFIN), $(NAN)))) - * $(TR $(TD ($(NAN), +0)) $(TD ($(NAN), +0))) - * $(TR $(TD ($(NAN), any)) $(TD ($(NAN), $(NAN)))) - * $(TR $(TD ($(NAN), $(NAN))) $(TD ($(NAN), $(NAN)))) - * ) - */ -Complex!T exp(T)(Complex!T x) @trusted pure nothrow @nogc // TODO: @safe -{ - static import std.math; - - // Handle special cases explicitly here, as fromPolar will otherwise - // cause them to return Complex!T(NaN, NaN), or with the wrong sign. - if (std.math.isInfinity(x.re)) - { - if (std.math.isNaN(x.im)) - { - if (std.math.signbit(x.re)) - return Complex!T(0, std.math.copysign(0, x.im)); - else - return x; - } - if (std.math.isInfinity(x.im)) - { - if (std.math.signbit(x.re)) - return Complex!T(0, std.math.copysign(0, x.im)); - else - return Complex!T(T.infinity, -T.nan); - } - if (x.im == 0.0) - { - if (std.math.signbit(x.re)) - return Complex!T(0.0); - else - return Complex!T(T.infinity); - } - } - if (std.math.isNaN(x.re)) - { - if (std.math.isNaN(x.im) || std.math.isInfinity(x.im)) - return Complex!T(T.nan, T.nan); - if (x.im == 0.0) - return x; - } - if (x.re == 0.0) - { - if (std.math.isNaN(x.im) || std.math.isInfinity(x.im)) - return Complex!T(T.nan, T.nan); - if (x.im == 0.0) - return Complex!T(1.0, 0.0); - } - - return fromPolar!(T, T)(std.math.exp(x.re), x.im); -} - -/// -@safe pure nothrow @nogc unittest -{ - import std.math.operations : isClose; - import std.math.constants : PI; - - assert(exp(complex(0.0, 0.0)) == complex(1.0, 0.0)); - - auto a = complex(2.0, 1.0); - assert(exp(conj(a)) == conj(exp(a))); - - auto b = exp(complex(0.0L, 1.0L) * PI); - assert(isClose(b, -1.0L, 0.0, 1e-15)); -} - -@safe pure nothrow @nogc unittest -{ - import std.math.traits : isNaN, isInfinity; - - auto a = exp(complex(0.0, double.infinity)); - assert(a.re.isNaN && a.im.isNaN); - auto b = exp(complex(0.0, double.infinity)); - assert(b.re.isNaN && b.im.isNaN); - auto c = exp(complex(0.0, double.nan)); - assert(c.re.isNaN && c.im.isNaN); - - auto d = exp(complex(+double.infinity, 0.0)); - assert(d == complex(double.infinity, 0.0)); - auto e = exp(complex(-double.infinity, 0.0)); - assert(e == complex(0.0)); - auto f = exp(complex(-double.infinity, 1.0)); - assert(f == complex(0.0)); - auto g = exp(complex(+double.infinity, 1.0)); - assert(g == complex(double.infinity, double.infinity)); - auto h = exp(complex(-double.infinity, +double.infinity)); - assert(h == complex(0.0)); - auto i = exp(complex(+double.infinity, +double.infinity)); - assert(i.re.isInfinity && i.im.isNaN); - auto j = exp(complex(-double.infinity, double.nan)); - assert(j == complex(0.0)); - auto k = exp(complex(+double.infinity, double.nan)); - assert(k.re.isInfinity && k.im.isNaN); - - auto l = exp(complex(double.nan, 0)); - assert(l.re.isNaN && l.im == 0.0); - auto m = exp(complex(double.nan, 1)); - assert(m.re.isNaN && m.im.isNaN); - auto n = exp(complex(double.nan, double.nan)); - assert(n.re.isNaN && n.im.isNaN); -} - -@safe pure nothrow @nogc unittest -{ - import std.math.constants : PI; - import std.math.operations : isClose; - - auto a = exp(complex(0.0, -PI)); - assert(isClose(a, -1.0, 0.0, 1e-15)); - - auto b = exp(complex(0.0, -2.0 * PI / 3.0)); - assert(isClose(b, complex(-0.5L, -0.866025403784438646763L))); - - auto c = exp(complex(0.0, PI / 3.0)); - assert(isClose(c, complex(0.5L, 0.866025403784438646763L))); - - auto d = exp(complex(0.0, 2.0 * PI / 3.0)); - assert(isClose(d, complex(-0.5L, 0.866025403784438646763L))); - - auto e = exp(complex(0.0, PI)); - assert(isClose(e, -1.0, 0.0, 1e-15)); -} - -/** - * Calculate the natural logarithm of x. - * The branch cut is along the negative axis. - * Params: - * x = A complex number - * Returns: - * The complex natural logarithm of `x` - * - * $(TABLE_SV - * $(TR $(TH x) $(TH log(x))) - * $(TR $(TD (-0, +0)) $(TD (-$(INFIN), $(PI)))) - * $(TR $(TD (+0, +0)) $(TD (-$(INFIN), +0))) - * $(TR $(TD (any, +$(INFIN))) $(TD (+$(INFIN), $(PI)/2))) - * $(TR $(TD (any, $(NAN))) $(TD ($(NAN), $(NAN)))) - * $(TR $(TD (-$(INFIN), any)) $(TD (+$(INFIN), $(PI)))) - * $(TR $(TD (+$(INFIN), any)) $(TD (+$(INFIN), +0))) - * $(TR $(TD (-$(INFIN), +$(INFIN))) $(TD (+$(INFIN), 3$(PI)/4))) - * $(TR $(TD (+$(INFIN), +$(INFIN))) $(TD (+$(INFIN), $(PI)/4))) - * $(TR $(TD ($(PLUSMN)$(INFIN), $(NAN))) $(TD (+$(INFIN), $(NAN)))) - * $(TR $(TD ($(NAN), any)) $(TD ($(NAN), $(NAN)))) - * $(TR $(TD ($(NAN), +$(INFIN))) $(TD (+$(INFIN), $(NAN)))) - * $(TR $(TD ($(NAN), $(NAN))) $(TD ($(NAN), $(NAN)))) - * ) - */ -Complex!T log(T)(Complex!T x) @safe pure nothrow @nogc -{ - static import std.math; - - // Handle special cases explicitly here for better accuracy. - // The order here is important, so that the correct path is chosen. - if (std.math.isNaN(x.re)) - { - if (std.math.isInfinity(x.im)) - return Complex!T(T.infinity, T.nan); - else - return Complex!T(T.nan, T.nan); - } - if (std.math.isInfinity(x.re)) - { - if (std.math.isNaN(x.im)) - return Complex!T(T.infinity, T.nan); - else if (std.math.isInfinity(x.im)) - { - if (std.math.signbit(x.re)) - return Complex!T(T.infinity, std.math.copysign(3.0 * std.math.PI_4, x.im)); - else - return Complex!T(T.infinity, std.math.copysign(std.math.PI_4, x.im)); - } - else - { - if (std.math.signbit(x.re)) - return Complex!T(T.infinity, std.math.copysign(std.math.PI, x.im)); - else - return Complex!T(T.infinity, std.math.copysign(0.0, x.im)); - } - } - if (std.math.isNaN(x.im)) - return Complex!T(T.nan, T.nan); - if (std.math.isInfinity(x.im)) - return Complex!T(T.infinity, std.math.copysign(std.math.PI_2, x.im)); - if (x.re == 0.0 && x.im == 0.0) - { - if (std.math.signbit(x.re)) - return Complex!T(-T.infinity, std.math.copysign(std.math.PI, x.im)); - else - return Complex!T(-T.infinity, std.math.copysign(0.0, x.im)); - } - - return Complex!T(std.math.log(abs(x)), arg(x)); -} - -/// -@safe pure nothrow @nogc unittest -{ - import core.math : sqrt; - import std.math.constants : PI; - import std.math.operations : isClose; - - auto a = complex(2.0, 1.0); - assert(log(conj(a)) == conj(log(a))); - - auto b = 2.0 * log10(complex(0.0, 1.0)); - auto c = 4.0 * log10(complex(sqrt(2.0) / 2, sqrt(2.0) / 2)); - assert(isClose(b, c, 0.0, 1e-15)); - - assert(log(complex(-1.0L, 0.0L)) == complex(0.0L, PI)); - assert(log(complex(-1.0L, -0.0L)) == complex(0.0L, -PI)); -} - -@safe pure nothrow @nogc unittest -{ - import std.math.traits : isNaN, isInfinity; - import std.math.constants : PI, PI_2, PI_4; - - auto a = log(complex(-0.0L, 0.0L)); - assert(a == complex(-real.infinity, PI)); - auto b = log(complex(0.0L, 0.0L)); - assert(b == complex(-real.infinity, +0.0L)); - auto c = log(complex(1.0L, real.infinity)); - assert(c == complex(real.infinity, PI_2)); - auto d = log(complex(1.0L, real.nan)); - assert(d.re.isNaN && d.im.isNaN); - - auto e = log(complex(-real.infinity, 1.0L)); - assert(e == complex(real.infinity, PI)); - auto f = log(complex(real.infinity, 1.0L)); - assert(f == complex(real.infinity, 0.0L)); - auto g = log(complex(-real.infinity, real.infinity)); - assert(g == complex(real.infinity, 3.0 * PI_4)); - auto h = log(complex(real.infinity, real.infinity)); - assert(h == complex(real.infinity, PI_4)); - auto i = log(complex(real.infinity, real.nan)); - assert(i.re.isInfinity && i.im.isNaN); - - auto j = log(complex(real.nan, 1.0L)); - assert(j.re.isNaN && j.im.isNaN); - auto k = log(complex(real.nan, real.infinity)); - assert(k.re.isInfinity && k.im.isNaN); - auto l = log(complex(real.nan, real.nan)); - assert(l.re.isNaN && l.im.isNaN); -} - -@safe pure nothrow @nogc unittest -{ - import std.math.constants : PI; - import std.math.operations : isClose; - - auto a = log(fromPolar(1.0, PI / 6.0)); - assert(isClose(a, complex(0.0L, 0.523598775598298873077L), 0.0, 1e-15)); - - auto b = log(fromPolar(1.0, PI / 3.0)); - assert(isClose(b, complex(0.0L, 1.04719755119659774615L), 0.0, 1e-15)); - - auto c = log(fromPolar(1.0, PI / 2.0)); - assert(isClose(c, complex(0.0L, 1.57079632679489661923L), 0.0, 1e-15)); - - auto d = log(fromPolar(1.0, 2.0 * PI / 3.0)); - assert(isClose(d, complex(0.0L, 2.09439510239319549230L), 0.0, 1e-15)); - - auto e = log(fromPolar(1.0, 5.0 * PI / 6.0)); - assert(isClose(e, complex(0.0L, 2.61799387799149436538L), 0.0, 1e-15)); - - auto f = log(complex(-1.0L, 0.0L)); - assert(isClose(f, complex(0.0L, PI), 0.0, 1e-15)); -} - -/** - * Calculate the base-10 logarithm of x. - * Params: - * x = A complex number - * Returns: - * The complex base 10 logarithm of `x` - */ -Complex!T log10(T)(Complex!T x) @safe pure nothrow @nogc -{ - import std.math.constants : LN10; - - return log(x) / Complex!T(LN10); -} - -/// -@safe pure nothrow @nogc unittest -{ - import core.math : sqrt; - import std.math.constants : LN10, PI; - import std.math.operations : isClose; - - auto a = complex(2.0, 1.0); - assert(log10(a) == log(a) / log(complex(10.0))); - - auto b = log10(complex(0.0, 1.0)) * 2.0; - auto c = log10(complex(sqrt(2.0) / 2, sqrt(2.0) / 2)) * 4.0; - assert(isClose(b, c, 0.0, 1e-15)); -} - -@safe pure nothrow @nogc unittest -{ - import std.math.constants : LN10, PI; - import std.math.operations : isClose; - - auto a = log10(fromPolar(1.0, PI / 6.0)); - assert(isClose(a, complex(0.0L, 0.227396058973640224580L), 0.0, 1e-15)); - - auto b = log10(fromPolar(1.0, PI / 3.0)); - assert(isClose(b, complex(0.0L, 0.454792117947280449161L), 0.0, 1e-15)); - - auto c = log10(fromPolar(1.0, PI / 2.0)); - assert(isClose(c, complex(0.0L, 0.682188176920920673742L), 0.0, 1e-15)); - - auto d = log10(fromPolar(1.0, 2.0 * PI / 3.0)); - assert(isClose(d, complex(0.0L, 0.909584235894560898323L), 0.0, 1e-15)); - - auto e = log10(fromPolar(1.0, 5.0 * PI / 6.0)); - assert(isClose(e, complex(0.0L, 1.13698029486820112290L), 0.0, 1e-15)); - - auto f = log10(complex(-1.0L, 0.0L)); - assert(isClose(f, complex(0.0L, 1.36437635384184134748L), 0.0, 1e-15)); - - assert(ceqrel(log10(complex(-100.0L, 0.0L)), complex(2.0L, PI / LN10)) >= real.mant_dig - 1); - assert(ceqrel(log10(complex(-100.0L, -0.0L)), complex(2.0L, -PI / LN10)) >= real.mant_dig - 1); -} - -/** - * Calculates x$(SUPERSCRIPT n). - * The branch cut is on the negative axis. - * Params: - * x = base - * n = exponent - * Returns: - * `x` raised to the power of `n` - */ -Complex!T pow(T, Int)(Complex!T x, const Int n) @safe pure nothrow @nogc -if (isIntegral!Int) -{ - alias UInt = Unsigned!(Unqual!Int); - - UInt m = (n < 0) ? -cast(UInt) n : n; - Complex!T y = (m % 2) ? x : Complex!T(1); - - while (m >>= 1) - { - x *= x; - if (m % 2) - y *= x; - } - - return (n < 0) ? Complex!T(1) / y : y; -} - -/// -@safe pure nothrow @nogc unittest -{ - import std.math.operations : isClose; - - auto a = complex(1.0, 2.0); - assert(pow(a, 2) == a * a); - assert(pow(a, 3) == a * a * a); - assert(pow(a, -2) == 1.0 / (a * a)); - assert(isClose(pow(a, -3), 1.0 / (a * a * a))); -} - -/// ditto -Complex!T pow(T)(Complex!T x, const T n) @trusted pure nothrow @nogc -{ - static import std.math; - - if (x == 0.0) - return Complex!T(0.0); - - if (x.im == 0 && x.re > 0.0) - return Complex!T(std.math.pow(x.re, n)); - - Complex!T t = log(x); - return fromPolar!(T, T)(std.math.exp(n * t.re), n * t.im); -} - -/// -@safe pure nothrow @nogc unittest -{ - import std.math.operations : isClose; - assert(pow(complex(0.0), 2.0) == complex(0.0)); - assert(pow(complex(5.0), 2.0) == complex(25.0)); - - auto a = pow(complex(-1.0, 0.0), 0.5); - assert(isClose(a, complex(0.0, +1.0), 0.0, 1e-16)); - - auto b = pow(complex(-1.0, -0.0), 0.5); - assert(isClose(b, complex(0.0, -1.0), 0.0, 1e-16)); -} - -/// ditto -Complex!T pow(T)(Complex!T x, Complex!T y) @trusted pure nothrow @nogc -{ - return (x == 0) ? Complex!T(0) : exp(y * log(x)); -} - -/// -@safe pure nothrow @nogc unittest -{ - import std.math.operations : isClose; - import std.math.exponential : exp; - import std.math.constants : PI; - auto a = complex(0.0); - auto b = complex(2.0); - assert(pow(a, b) == complex(0.0)); - - auto c = complex(0.0L, 1.0L); - assert(isClose(pow(c, c), exp((-PI) / 2))); -} - -/// ditto -Complex!T pow(T)(const T x, Complex!T n) @trusted pure nothrow @nogc -{ - static import std.math; - - return (x > 0.0) - ? fromPolar!(T, T)(std.math.pow(x, n.re), n.im * std.math.log(x)) - : pow(Complex!T(x), n); -} - -/// -@safe pure nothrow @nogc unittest -{ - import std.math.operations : isClose; - assert(pow(2.0, complex(0.0)) == complex(1.0)); - assert(pow(2.0, complex(5.0)) == complex(32.0)); - - auto a = pow(-2.0, complex(-1.0)); - assert(isClose(a, complex(-0.5), 0.0, 1e-16)); - - auto b = pow(-0.5, complex(-1.0)); - assert(isClose(b, complex(-2.0), 0.0, 1e-15)); -} - -@safe pure nothrow @nogc unittest -{ - import std.math.constants : PI; - import std.math.operations : isClose; - - auto a = pow(complex(3.0, 4.0), 2); - assert(isClose(a, complex(-7.0, 24.0))); - - auto b = pow(complex(3.0, 4.0), PI); - assert(ceqrel(b, complex(-152.91512205297134, 35.547499631917738)) >= double.mant_dig - 3); - - auto c = pow(complex(3.0, 4.0), complex(-2.0, 1.0)); - assert(ceqrel(c, complex(0.015351734187477306, -0.0038407695456661503)) >= double.mant_dig - 3); - - auto d = pow(PI, complex(2.0, -1.0)); - assert(ceqrel(d, complex(4.0790296880118296, -8.9872469554541869)) >= double.mant_dig - 1); - - auto e = complex(2.0); - assert(ceqrel(pow(e, 3), exp(3 * log(e))) >= double.mant_dig - 1); -} - -@safe pure nothrow @nogc unittest -{ - import std.meta : AliasSeq; - import std.math.traits : floatTraits, RealFormat; - static foreach (T; AliasSeq!(float, double, real)) - {{ - static if (floatTraits!T.realFormat == RealFormat.ibmExtended) - { - /* For IBM real, epsilon is too small (since 1.0 plus any double is - representable) to be able to expect results within epsilon * 100. */ - } - else - { - T eps = T.epsilon * 100; - - T a = -1.0; - T b = 0.5; - Complex!T ref1 = pow(complex(a), complex(b)); - Complex!T res1 = pow(a, complex(b)); - Complex!T res2 = pow(complex(a), b); - assert(abs(ref1 - res1) < eps); - assert(abs(ref1 - res2) < eps); - assert(abs(res1 - res2) < eps); - - T c = -3.2; - T d = 1.4; - Complex!T ref2 = pow(complex(a), complex(b)); - Complex!T res3 = pow(a, complex(b)); - Complex!T res4 = pow(complex(a), b); - assert(abs(ref2 - res3) < eps); - assert(abs(ref2 - res4) < eps); - assert(abs(res3 - res4) < eps); - } - }} -} - -@safe pure nothrow @nogc unittest -{ - import std.meta : AliasSeq; - static foreach (T; AliasSeq!(float, double, real)) - {{ - auto c = Complex!T(123, 456); - auto n = c.toNative(); - assert(c.re == n.re && c.im == n.im); - }} -} diff --git a/phobos/std/concurrency.d b/phobos/std/concurrency.d deleted file mode 100644 index 267f682..0000000 --- a/phobos/std/concurrency.d +++ /dev/null @@ -1,2861 +0,0 @@ -/** - * $(SCRIPT inhibitQuickIndex = 1;) - * $(DIVC quickindex, - * $(BOOKTABLE, - * $(TR $(TH Category) $(TH Symbols)) - * $(TR $(TD Tid) $(TD - * $(MYREF locate) - * $(MYREF ownerTid) - * $(MYREF register) - * $(MYREF spawn) - * $(MYREF spawnLinked) - * $(MYREF thisTid) - * $(MYREF Tid) - * $(MYREF TidMissingException) - * $(MYREF unregister) - * )) - * $(TR $(TD Message passing) $(TD - * $(MYREF prioritySend) - * $(MYREF receive) - * $(MYREF receiveOnly) - * $(MYREF receiveTimeout) - * $(MYREF send) - * $(MYREF setMaxMailboxSize) - * )) - * $(TR $(TD Message-related types) $(TD - * $(MYREF LinkTerminated) - * $(MYREF MailboxFull) - * $(MYREF MessageMismatch) - * $(MYREF OnCrowding) - * $(MYREF OwnerTerminated) - * $(MYREF PriorityMessageException) - * )) - * $(TR $(TD Scheduler) $(TD - * $(MYREF FiberScheduler) - * $(MYREF Generator) - * $(MYREF Scheduler) - * $(MYREF scheduler) - * $(MYREF ThreadInfo) - * $(MYREF ThreadScheduler) - * $(MYREF yield) - * )) - * $(TR $(TD Misc) $(TD - * $(MYREF initOnce) - * )) - * )) - * - * This is a low-level messaging API upon which more structured or restrictive - * APIs may be built. The general idea is that every messageable entity is - * represented by a common handle type called a `Tid`, which allows messages to - * be sent to logical threads that are executing in both the current process - * and in external processes using the same interface. This is an important - * aspect of scalability because it allows the components of a program to be - * spread across available resources with few to no changes to the actual - * implementation. - * - * A logical thread is an execution context that has its own stack and which - * runs asynchronously to other logical threads. These may be preemptively - * scheduled kernel threads, $(MREF_ALTTEXT fibers, core, thread, fiber) - * (cooperative user-space threads), or some other concept with similar behavior. - * - * The type of concurrency used when logical threads are created is determined - * by the $(LREF Scheduler) selected at initialization time. The default behavior is - * currently to create a new kernel thread per call to spawn, but other - * schedulers are available that multiplex fibers across the main thread or - * use some combination of the two approaches. - * - * Copyright: Copyright Sean Kelly 2009 - 2014. - * License: Boost License 1.0. - * Authors: Sean Kelly, Alex Rønne Petersen, Martin Nowak - * Source: $(PHOBOSSRC std/concurrency.d) - */ -/* Copyright Sean Kelly 2009 - 2014. - * 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.concurrency; - -public import std.variant; - -import core.atomic; -import core.sync.condition; -import core.sync.mutex; -import core.thread; -import std.range.primitives; -import std.range.interfaces : InputRange; -import std.traits; - -/// -@system unittest -{ - __gshared string received; - static void spawnedFunc(Tid ownerTid) - { - import std.conv : text; - // Receive a message from the owner thread. - receive((int i){ - received = text("Received the number ", i); - - // Send a message back to the owner thread - // indicating success. - send(ownerTid, true); - }); - } - - // Start spawnedFunc in a new thread. - auto childTid = spawn(&spawnedFunc, thisTid); - - // Send the number 42 to this new thread. - send(childTid, 42); - - // Receive the result code. - auto wasSuccessful = receiveOnly!(bool); - assert(wasSuccessful); - assert(received == "Received the number 42"); -} - -private -{ - bool hasLocalAliasing(Types...)() - { - import std.typecons : Rebindable; - - // Works around "statement is not reachable" - bool doesIt = false; - static foreach (T; Types) - { - static if (is(T == Tid)) - { /* Allowed */ } - else static if (is(T : Rebindable!R, R)) - doesIt |= hasLocalAliasing!R; - else static if (is(T == struct)) - doesIt |= hasLocalAliasing!(typeof(T.tupleof)); - else - doesIt |= std.traits.hasUnsharedAliasing!(T); - } - return doesIt; - } - - @safe unittest - { - static struct Container { Tid t; } - static assert(!hasLocalAliasing!(Tid, Container, int)); - } - - // https://issues.dlang.org/show_bug.cgi?id=20097 - @safe unittest - { - import std.datetime.systime : SysTime; - static struct Container { SysTime time; } - static assert(!hasLocalAliasing!(SysTime, Container)); - } - - enum MsgType - { - standard, - priority, - linkDead, - } - - struct Message - { - MsgType type; - Variant data; - - this(T...)(MsgType t, T vals) if (T.length > 0) - { - static if (T.length == 1) - { - type = t; - data = vals[0]; - } - else - { - import std.typecons : Tuple; - - type = t; - data = Tuple!(T)(vals); - } - } - - @property auto convertsTo(T...)() - { - static if (T.length == 1) - { - return is(T[0] == Variant) || data.convertsTo!(T); - } - else - { - import std.typecons : Tuple; - return data.convertsTo!(Tuple!(T)); - } - } - - @property auto get(T...)() - { - static if (T.length == 1) - { - static if (is(T[0] == Variant)) - return data; - else - return data.get!(T); - } - else - { - import std.typecons : Tuple; - return data.get!(Tuple!(T)); - } - } - - auto map(Op)(Op op) - { - alias Args = Parameters!(Op); - - static if (Args.length == 1) - { - static if (is(Args[0] == Variant)) - return op(data); - else - return op(data.get!(Args)); - } - else - { - import std.typecons : Tuple; - return op(data.get!(Tuple!(Args)).expand); - } - } - } - - void checkops(T...)(T ops) - { - import std.format : format; - - foreach (i, t1; T) - { - static assert(isFunctionPointer!t1 || isDelegate!t1, - format!"T %d is not a function pointer or delegates"(i)); - alias a1 = Parameters!(t1); - alias r1 = ReturnType!(t1); - - static if (i < T.length - 1 && is(r1 == void)) - { - static assert(a1.length != 1 || !is(a1[0] == Variant), - "function with arguments " ~ a1.stringof ~ - " occludes successive function"); - - foreach (t2; T[i + 1 .. $]) - { - alias a2 = Parameters!(t2); - - static assert(!is(a1 == a2), - "function with arguments " ~ a1.stringof ~ " occludes successive function"); - } - } - } - } - - @property ref ThreadInfo thisInfo() nothrow - { - import core.atomic : atomicLoad; - - auto localScheduler = atomicLoad(scheduler); - if (localScheduler is null) - return ThreadInfo.thisInfo; - return localScheduler.thisInfo; - } -} - -static ~this() -{ - thisInfo.cleanup(); -} - -// Exceptions - -/** - * Thrown on calls to $(LREF receiveOnly) if a message other than the type - * the receiving thread expected is sent. - */ -class MessageMismatch : Exception -{ - /// - this(string msg = "Unexpected message type") @safe pure nothrow @nogc - { - super(msg); - } -} - -/** - * Thrown on calls to $(LREF receive) if the thread that spawned the receiving - * thread has terminated and no more messages exist. - */ -class OwnerTerminated : Exception -{ - /// - this(Tid t, string msg = "Owner terminated") @safe pure nothrow @nogc - { - super(msg); - tid = t; - } - - Tid tid; -} - -/** - * Thrown if a linked thread has terminated. - */ -class LinkTerminated : Exception -{ - /// - this(Tid t, string msg = "Link terminated") @safe pure nothrow @nogc - { - super(msg); - tid = t; - } - - Tid tid; -} - -/** - * Thrown if a message was sent to a thread via - * $(REF prioritySend, std,concurrency) and the receiver does not have a handler - * for a message of this type. - */ -class PriorityMessageException : Exception -{ - /// - this(Variant vals) - { - super("Priority message"); - message = vals; - } - - /** - * The message that was sent. - */ - Variant message; -} - -/** - * Thrown on mailbox crowding if the mailbox is configured with - * `OnCrowding.throwException`. - */ -class MailboxFull : Exception -{ - /// - this(Tid t, string msg = "Mailbox full") @safe pure nothrow @nogc - { - super(msg); - tid = t; - } - - Tid tid; -} - -/** - * Thrown when a `Tid` is missing, e.g. when $(LREF ownerTid) doesn't - * find an owner thread. - */ -class TidMissingException : Exception -{ - import std.exception : basicExceptionCtors; - /// - mixin basicExceptionCtors; -} - - -// Thread ID - - -/** - * An opaque type used to represent a logical thread. - */ -struct Tid -{ -private: - this(MessageBox m) @safe pure nothrow @nogc - { - mbox = m; - } - - MessageBox mbox; - -public: - - /** - * Generate a convenient string for identifying this `Tid`. This is only - * useful to see if `Tid`'s that are currently executing are the same or - * different, e.g. for logging and debugging. It is potentially possible - * that a `Tid` executed in the future will have the same `toString()` output - * as another `Tid` that has already terminated. - */ - void toString(W)(ref W w) const - { - import std.format.write : formattedWrite; - auto p = () @trusted { return cast(void*) mbox; }(); - formattedWrite(w, "Tid(%x)", p); - } - -} - -@safe unittest -{ - import std.conv : text; - Tid tid; - assert(text(tid) == "Tid(0)"); - auto tid2 = thisTid; - assert(text(tid2) != "Tid(0)"); - auto tid3 = tid2; - assert(text(tid2) == text(tid3)); -} - -// https://issues.dlang.org/show_bug.cgi?id=21512 -@system unittest -{ - import std.format : format; - - const(Tid) b = spawn(() {}); - assert(format!"%s"(b)[0 .. 4] == "Tid("); -} - -/** - * Returns: The `Tid` of the caller's thread. - */ -@property Tid thisTid() @safe -{ - // TODO: remove when concurrency is safe - static auto trus() @trusted - { - if (thisInfo.ident != Tid.init) - return thisInfo.ident; - thisInfo.ident = Tid(new MessageBox); - return thisInfo.ident; - } - - return trus(); -} - -/** - * Return the `Tid` of the thread which spawned the caller's thread. - * - * Throws: A `TidMissingException` exception if - * there is no owner thread. - */ -@property Tid ownerTid() -{ - import std.exception : enforce; - - enforce!TidMissingException(thisInfo.owner.mbox !is null, "Error: Thread has no owner thread."); - return thisInfo.owner; -} - -@system unittest -{ - import std.exception : assertThrown; - - static void fun() - { - string res = receiveOnly!string(); - assert(res == "Main calling"); - ownerTid.send("Child responding"); - } - - assertThrown!TidMissingException(ownerTid); - auto child = spawn(&fun); - child.send("Main calling"); - string res = receiveOnly!string(); - assert(res == "Child responding"); -} - -// Thread Creation - -private template isSpawnable(F, T...) -{ - template isParamsImplicitlyConvertible(F1, F2, int i = 0) - { - alias param1 = Parameters!F1; - alias param2 = Parameters!F2; - static if (param1.length != param2.length) - enum isParamsImplicitlyConvertible = false; - else static if (param1.length == i) - enum isParamsImplicitlyConvertible = true; - else static if (is(param2[i] : param1[i])) - enum isParamsImplicitlyConvertible = isParamsImplicitlyConvertible!(F1, - F2, i + 1); - else - enum isParamsImplicitlyConvertible = false; - } - - enum isSpawnable = isCallable!F && is(ReturnType!F : void) - && isParamsImplicitlyConvertible!(F, void function(T)) - && (isFunctionPointer!F || !hasUnsharedAliasing!F); -} - -/** - * Starts `fn(args)` in a new logical thread. - * - * Executes the supplied function in a new logical thread represented by - * `Tid`. The calling thread is designated as the owner of the new thread. - * When the owner thread terminates an `OwnerTerminated` message will be - * sent to the new thread, causing an `OwnerTerminated` exception to be - * thrown on `receive()`. - * - * Params: - * fn = The function to execute. - * args = Arguments to the function. - * - * Returns: - * A `Tid` representing the new logical thread. - * - * Notes: - * `args` must not have unshared aliasing. In other words, all arguments - * to `fn` must either be `shared` or `immutable` or have no - * pointer indirection. This is necessary for enforcing isolation among - * threads. - * - * Similarly, if `fn` is a delegate, it must not have unshared aliases, meaning - * `fn` must be either `shared` or `immutable`. */ -Tid spawn(F, T...)(F fn, T args) -if (isSpawnable!(F, T)) -{ - static assert(!hasLocalAliasing!(T), "Aliases to mutable thread-local data not allowed."); - return _spawn(false, fn, args); -} - -/// -@system unittest -{ - static void f(string msg) - { - assert(msg == "Hello World"); - } - - auto tid = spawn(&f, "Hello World"); -} - -/// Fails: char[] has mutable aliasing. -@system unittest -{ - string msg = "Hello, World!"; - - static void f1(string msg) {} - static assert(!__traits(compiles, spawn(&f1, msg.dup))); - static assert( __traits(compiles, spawn(&f1, msg.idup))); - - static void f2(char[] msg) {} - static assert(!__traits(compiles, spawn(&f2, msg.dup))); - static assert(!__traits(compiles, spawn(&f2, msg.idup))); -} - -/// New thread with anonymous function -@system unittest -{ - spawn({ - ownerTid.send("This is so great!"); - }); - assert(receiveOnly!string == "This is so great!"); -} - -@system unittest -{ - import core.thread : thread_joinAll; - - __gshared string receivedMessage; - static void f1(string msg) - { - receivedMessage = msg; - } - - auto tid1 = spawn(&f1, "Hello World"); - thread_joinAll; - assert(receivedMessage == "Hello World"); -} - -/** - * Starts `fn(args)` in a logical thread and will receive a `LinkTerminated` - * message when the operation terminates. - * - * Executes the supplied function in a new logical thread represented by - * `Tid`. This new thread is linked to the calling thread so that if either - * it or the calling thread terminates a `LinkTerminated` message will be sent - * to the other, causing a `LinkTerminated` exception to be thrown on `receive()`. - * The owner relationship from `spawn()` is preserved as well, so if the link - * between threads is broken, owner termination will still result in an - * `OwnerTerminated` exception to be thrown on `receive()`. - * - * Params: - * fn = The function to execute. - * args = Arguments to the function. - * - * Returns: - * A Tid representing the new thread. - */ -Tid spawnLinked(F, T...)(F fn, T args) -if (isSpawnable!(F, T)) -{ - static assert(!hasLocalAliasing!(T), "Aliases to mutable thread-local data not allowed."); - return _spawn(true, fn, args); -} - -/* - * - */ -private Tid _spawn(F, T...)(bool linked, F fn, T args) -if (isSpawnable!(F, T)) -{ - // TODO: MessageList and &exec should be shared. - auto spawnTid = Tid(new MessageBox); - auto ownerTid = thisTid; - - void exec() - { - thisInfo.ident = spawnTid; - thisInfo.owner = ownerTid; - fn(args); - } - - // TODO: MessageList and &exec should be shared. - if (scheduler !is null) - scheduler.spawn(&exec); - else - { - auto t = new Thread(&exec); - t.start(); - } - thisInfo.links[spawnTid] = linked; - return spawnTid; -} - -@system unittest -{ - void function() fn1; - void function(int) fn2; - static assert(__traits(compiles, spawn(fn1))); - static assert(__traits(compiles, spawn(fn2, 2))); - static assert(!__traits(compiles, spawn(fn1, 1))); - static assert(!__traits(compiles, spawn(fn2))); - - void delegate(int) shared dg1; - shared(void delegate(int)) dg2; - shared(void delegate(long) shared) dg3; - shared(void delegate(real, int, long) shared) dg4; - void delegate(int) immutable dg5; - void delegate(int) dg6; - static assert(__traits(compiles, spawn(dg1, 1))); - static assert(__traits(compiles, spawn(dg2, 2))); - static assert(__traits(compiles, spawn(dg3, 3))); - static assert(__traits(compiles, spawn(dg4, 4, 4, 4))); - static assert(__traits(compiles, spawn(dg5, 5))); - static assert(!__traits(compiles, spawn(dg6, 6))); - - auto callable1 = new class{ void opCall(int) shared {} }; - auto callable2 = cast(shared) new class{ void opCall(int) shared {} }; - auto callable3 = new class{ void opCall(int) immutable {} }; - auto callable4 = cast(immutable) new class{ void opCall(int) immutable {} }; - auto callable5 = new class{ void opCall(int) {} }; - auto callable6 = cast(shared) new class{ void opCall(int) immutable {} }; - auto callable7 = cast(immutable) new class{ void opCall(int) shared {} }; - auto callable8 = cast(shared) new class{ void opCall(int) const shared {} }; - auto callable9 = cast(const shared) new class{ void opCall(int) shared {} }; - auto callable10 = cast(const shared) new class{ void opCall(int) const shared {} }; - auto callable11 = cast(immutable) new class{ void opCall(int) const shared {} }; - static assert(!__traits(compiles, spawn(callable1, 1))); - static assert( __traits(compiles, spawn(callable2, 2))); - static assert(!__traits(compiles, spawn(callable3, 3))); - static assert( __traits(compiles, spawn(callable4, 4))); - static assert(!__traits(compiles, spawn(callable5, 5))); - static assert(!__traits(compiles, spawn(callable6, 6))); - static assert(!__traits(compiles, spawn(callable7, 7))); - static assert( __traits(compiles, spawn(callable8, 8))); - static assert(!__traits(compiles, spawn(callable9, 9))); - static assert( __traits(compiles, spawn(callable10, 10))); - static assert( __traits(compiles, spawn(callable11, 11))); -} - -/** - * Places the values as a message at the back of tid's message queue. - * - * Sends the supplied value to the thread represented by tid. As with - * $(REF spawn, std,concurrency), `T` must not have unshared aliasing. - */ -void send(T...)(Tid tid, T vals) -in (tid.mbox !is null) -{ - static assert(!hasLocalAliasing!(T), "Aliases to mutable thread-local data not allowed."); - _send(tid, vals); -} - -/** - * Places the values as a message on the front of tid's message queue. - * - * Send a message to `tid` but place it at the front of `tid`'s message - * queue instead of at the back. This function is typically used for - * out-of-band communication, to signal exceptional conditions, etc. - */ -void prioritySend(T...)(Tid tid, T vals) -in (tid.mbox !is null) -{ - static assert(!hasLocalAliasing!(T), "Aliases to mutable thread-local data not allowed."); - _send(MsgType.priority, tid, vals); -} - -/* - * ditto - */ -private void _send(T...)(Tid tid, T vals) -in (tid.mbox !is null) -{ - _send(MsgType.standard, tid, vals); -} - -/* - * Implementation of send. This allows parameter checking to be different for - * both Tid.send() and .send(). - */ -private void _send(T...)(MsgType type, Tid tid, T vals) -in (tid.mbox !is null) -{ - auto msg = Message(type, vals); - tid.mbox.put(msg); -} - -/** - * Receives a message from another thread. - * - * Receive a message from another thread, or block if no messages of the - * specified types are available. This function works by pattern matching - * a message against a set of delegates and executing the first match found. - * - * If a delegate that accepts a $(REF Variant, std,variant) is included as - * the last argument to `receive`, it will match any message that was not - * matched by an earlier delegate. If more than one argument is sent, - * the `Variant` will contain a $(REF Tuple, std,typecons) of all values - * sent. - * - * Params: - * ops = Variadic list of function pointers and delegates. Entries - * in this list must not occlude later entries. - * - * Throws: $(LREF OwnerTerminated) when the sending thread was terminated. - */ -void receive(T...)( T ops ) -in -{ - assert(thisInfo.ident.mbox !is null, - "Cannot receive a message until a thread was spawned " - ~ "or thisTid was passed to a running thread."); -} -do -{ - checkops( ops ); - - thisInfo.ident.mbox.get( ops ); -} - -/// -@system unittest -{ - import std.variant : Variant; - - auto process = () - { - receive( - (int i) { ownerTid.send(1); }, - (double f) { ownerTid.send(2); }, - (Variant v) { ownerTid.send(3); } - ); - }; - - { - auto tid = spawn(process); - send(tid, 42); - assert(receiveOnly!int == 1); - } - - { - auto tid = spawn(process); - send(tid, 3.14); - assert(receiveOnly!int == 2); - } - - { - auto tid = spawn(process); - send(tid, "something else"); - assert(receiveOnly!int == 3); - } -} - -@safe unittest -{ - static assert( __traits( compiles, - { - receive( (Variant x) {} ); - receive( (int x) {}, (Variant x) {} ); - } ) ); - - static assert( !__traits( compiles, - { - receive( (Variant x) {}, (int x) {} ); - } ) ); - - static assert( !__traits( compiles, - { - receive( (int x) {}, (int x) {} ); - } ) ); -} - -// Make sure receive() works with free functions as well. -version (StdUnittest) -{ - private void receiveFunction(int x) {} -} -@safe unittest -{ - static assert( __traits( compiles, - { - receive( &receiveFunction ); - receive( &receiveFunction, (Variant x) {} ); - } ) ); -} - - -private template receiveOnlyRet(T...) -{ - static if ( T.length == 1 ) - { - alias receiveOnlyRet = T[0]; - } - else - { - import std.typecons : Tuple; - alias receiveOnlyRet = Tuple!(T); - } -} - -/** - * Receives only messages with arguments of the specified types. - * - * Params: - * T = Variadic list of types to be received. - * - * Returns: The received message. If `T` has more than one entry, - * the message will be packed into a $(REF Tuple, std,typecons). - * - * Throws: $(LREF MessageMismatch) if a message of types other than `T` - * is received, - * $(LREF OwnerTerminated) when the sending thread was terminated. - */ -receiveOnlyRet!(T) receiveOnly(T...)() -in -{ - assert(thisInfo.ident.mbox !is null, - "Cannot receive a message until a thread was spawned or thisTid was passed to a running thread."); -} -do -{ - import std.format : format; - import std.meta : allSatisfy; - import std.typecons : Tuple; - - Tuple!(T) ret; - - thisInfo.ident.mbox.get((T val) { - static if (T.length) - { - static if (allSatisfy!(isAssignable, T)) - { - ret.field = val; - } - else - { - import core.lifetime : emplace; - emplace(&ret, val); - } - } - }, - (LinkTerminated e) { throw e; }, - (OwnerTerminated e) { throw e; }, - (Variant val) { - static if (T.length > 1) - string exp = T.stringof; - else - string exp = T[0].stringof; - - throw new MessageMismatch( - format("Unexpected message type: expected '%s', got '%s'", exp, val.type.toString())); - }); - static if (T.length == 1) - return ret[0]; - else - return ret; -} - -/// -@system unittest -{ - auto tid = spawn( - { - assert(receiveOnly!int == 42); - }); - send(tid, 42); -} - -/// -@system unittest -{ - auto tid = spawn( - { - assert(receiveOnly!string == "text"); - }); - send(tid, "text"); -} - -/// -@system unittest -{ - struct Record { string name; int age; } - - auto tid = spawn( - { - auto msg = receiveOnly!(double, Record); - assert(msg[0] == 0.5); - assert(msg[1].name == "Alice"); - assert(msg[1].age == 31); - }); - - send(tid, 0.5, Record("Alice", 31)); -} - -@system unittest -{ - static void t1(Tid mainTid) - { - try - { - receiveOnly!string(); - mainTid.send(""); - } - catch (Throwable th) - { - mainTid.send(th.msg); - } - } - - auto tid = spawn(&t1, thisTid); - tid.send(1); - string result = receiveOnly!string(); - assert(result == "Unexpected message type: expected 'string', got 'int'"); -} - -// https://issues.dlang.org/show_bug.cgi?id=21663 -@safe unittest -{ - alias test = receiveOnly!(string, bool, bool); -} - -/** - * Receives a message from another thread and gives up if no match - * arrives within a specified duration. - * - * Receive a message from another thread, or block until `duration` exceeds, - * if no messages of the specified types are available. This function works - * by pattern matching a message against a set of delegates and executing - * the first match found. - * - * If a delegate that accepts a $(REF Variant, std,variant) is included as - * the last argument, it will match any message that was not - * matched by an earlier delegate. If more than one argument is sent, - * the `Variant` will contain a $(REF Tuple, std,typecons) of all values - * sent. - * - * Params: - * duration = Duration, how long to wait. If `duration` is negative, - * won't wait at all. - * ops = Variadic list of function pointers and delegates. Entries - * in this list must not occlude later entries. - * - * Returns: `true` if it received a message and `false` if it timed out waiting - * for one. - * - * Throws: $(LREF OwnerTerminated) when the sending thread was terminated. - */ -bool receiveTimeout(T...)(Duration duration, T ops) -in -{ - assert(thisInfo.ident.mbox !is null, - "Cannot receive a message until a thread was spawned or thisTid was passed to a running thread."); -} -do -{ - checkops(ops); - - return thisInfo.ident.mbox.get(duration, ops); -} - -@safe unittest -{ - static assert(__traits(compiles, { - receiveTimeout(msecs(0), (Variant x) {}); - receiveTimeout(msecs(0), (int x) {}, (Variant x) {}); - })); - - static assert(!__traits(compiles, { - receiveTimeout(msecs(0), (Variant x) {}, (int x) {}); - })); - - static assert(!__traits(compiles, { - receiveTimeout(msecs(0), (int x) {}, (int x) {}); - })); - - static assert(__traits(compiles, { - receiveTimeout(msecs(10), (int x) {}, (Variant x) {}); - })); -} - -// MessageBox Limits - -/** - * These behaviors may be specified when a mailbox is full. - */ -enum OnCrowding -{ - block, /// Wait until room is available. - throwException, /// Throw a $(LREF MailboxFull) exception. - ignore /// Abort the send and return. -} - -private -{ - bool onCrowdingBlock(Tid tid) @safe pure nothrow @nogc - { - return true; - } - - bool onCrowdingThrow(Tid tid) @safe pure - { - throw new MailboxFull(tid); - } - - bool onCrowdingIgnore(Tid tid) @safe pure nothrow @nogc - { - return false; - } -} - -/** - * Sets a maximum mailbox size. - * - * Sets a limit on the maximum number of user messages allowed in the mailbox. - * If this limit is reached, the caller attempting to add a new message will - * execute the behavior specified by doThis. If messages is zero, the mailbox - * is unbounded. - * - * Params: - * tid = The Tid of the thread for which this limit should be set. - * messages = The maximum number of messages or zero if no limit. - * doThis = The behavior executed when a message is sent to a full - * mailbox. - */ -void setMaxMailboxSize(Tid tid, size_t messages, OnCrowding doThis) @safe pure -in (tid.mbox !is null) -{ - final switch (doThis) - { - case OnCrowding.block: - return tid.mbox.setMaxMsgs(messages, &onCrowdingBlock); - case OnCrowding.throwException: - return tid.mbox.setMaxMsgs(messages, &onCrowdingThrow); - case OnCrowding.ignore: - return tid.mbox.setMaxMsgs(messages, &onCrowdingIgnore); - } -} - -/** - * Sets a maximum mailbox size. - * - * Sets a limit on the maximum number of user messages allowed in the mailbox. - * If this limit is reached, the caller attempting to add a new message will - * execute onCrowdingDoThis. If messages is zero, the mailbox is unbounded. - * - * Params: - * tid = The Tid of the thread for which this limit should be set. - * messages = The maximum number of messages or zero if no limit. - * onCrowdingDoThis = The routine called when a message is sent to a full - * mailbox. - */ -void setMaxMailboxSize(Tid tid, size_t messages, bool function(Tid) onCrowdingDoThis) -in (tid.mbox !is null) -{ - tid.mbox.setMaxMsgs(messages, onCrowdingDoThis); -} - -private -{ - __gshared Tid[string] tidByName; - __gshared string[][Tid] namesByTid; -} - -private @property Mutex registryLock() -{ - __gshared Mutex impl; - initOnce!impl(new Mutex); - return impl; -} - -private void unregisterMe(ref ThreadInfo me) -{ - if (me.ident != Tid.init) - { - synchronized (registryLock) - { - if (auto allNames = me.ident in namesByTid) - { - foreach (name; *allNames) - tidByName.remove(name); - namesByTid.remove(me.ident); - } - } - } -} - -/** - * Associates name with tid. - * - * Associates name with tid in a process-local map. When the thread - * represented by tid terminates, any names associated with it will be - * automatically unregistered. - * - * Params: - * name = The name to associate with tid. - * tid = The tid register by name. - * - * Returns: - * true if the name is available and tid is not known to represent a - * defunct thread. - */ -bool register(string name, Tid tid) -in (tid.mbox !is null) -{ - synchronized (registryLock) - { - if (name in tidByName) - return false; - if (tid.mbox.isClosed) - return false; - namesByTid[tid] ~= name; - tidByName[name] = tid; - return true; - } -} - -/** - * Removes the registered name associated with a tid. - * - * Params: - * name = The name to unregister. - * - * Returns: - * true if the name is registered, false if not. - */ -bool unregister(string name) -{ - import std.algorithm.mutation : remove, SwapStrategy; - import std.algorithm.searching : countUntil; - - synchronized (registryLock) - { - if (auto tid = name in tidByName) - { - auto allNames = *tid in namesByTid; - auto pos = countUntil(*allNames, name); - remove!(SwapStrategy.unstable)(*allNames, pos); - tidByName.remove(name); - return true; - } - return false; - } -} - -/** - * Gets the `Tid` associated with name. - * - * Params: - * name = The name to locate within the registry. - * - * Returns: - * The associated `Tid` or `Tid.init` if name is not registered. - */ -Tid locate(string name) -{ - synchronized (registryLock) - { - if (auto tid = name in tidByName) - return *tid; - return Tid.init; - } -} - -/** - * Encapsulates all implementation-level data needed for scheduling. - * - * When defining a $(LREF Scheduler), an instance of this struct must be associated - * with each logical thread. It contains all implementation-level information - * needed by the internal API. - */ -struct ThreadInfo -{ - Tid ident; - bool[Tid] links; - Tid owner; - - /** - * Gets a thread-local instance of `ThreadInfo`. - * - * Gets a thread-local instance of `ThreadInfo`, which should be used as the - * default instance when info is requested for a thread not created by the - * `Scheduler`. - */ - static @property ref thisInfo() nothrow - { - static ThreadInfo val; - return val; - } - - /** - * Cleans up this ThreadInfo. - * - * This must be called when a scheduled thread terminates. It tears down - * the messaging system for the thread and notifies interested parties of - * the thread's termination. - */ - void cleanup() - { - if (ident.mbox !is null) - ident.mbox.close(); - foreach (tid; links.keys) - _send(MsgType.linkDead, tid, ident); - if (owner != Tid.init) - _send(MsgType.linkDead, owner, ident); - unregisterMe(this); // clean up registry entries - } - - // https://issues.dlang.org/show_bug.cgi?id=20160 - @system unittest - { - register("main_thread", thisTid()); - - ThreadInfo t; - t.cleanup(); - - assert(locate("main_thread") == thisTid()); - } -} - -/** - * A `Scheduler` controls how threading is performed by spawn. - * - * Implementing a `Scheduler` allows the concurrency mechanism used by this - * module to be customized according to different needs. By default, a call - * to spawn will create a new kernel thread that executes the supplied routine - * and terminates when finished. But it is possible to create `Scheduler`s that - * reuse threads, that multiplex `Fiber`s (coroutines) across a single thread, - * or any number of other approaches. By making the choice of `Scheduler` a - * user-level option, `std.concurrency` may be used for far more types of - * application than if this behavior were predefined. - * - * Example: - * --- - * import std.concurrency; - * import std.stdio; - * - * void main() - * { - * scheduler = new FiberScheduler; - * scheduler.start( - * { - * writeln("the rest of main goes here"); - * }); - * } - * --- - * - * Some schedulers have a dispatching loop that must run if they are to work - * properly, so for the sake of consistency, when using a scheduler, `start()` - * must be called within `main()`. This yields control to the scheduler and - * will ensure that any spawned threads are executed in an expected manner. - */ -interface Scheduler -{ - /** - * Spawns the supplied op and starts the `Scheduler`. - * - * This is intended to be called at the start of the program to yield all - * scheduling to the active `Scheduler` instance. This is necessary for - * schedulers that explicitly dispatch threads rather than simply relying - * on the operating system to do so, and so start should always be called - * within `main()` to begin normal program execution. - * - * Params: - * op = A wrapper for whatever the main thread would have done in the - * absence of a custom scheduler. It will be automatically executed - * via a call to spawn by the `Scheduler`. - */ - void start(void delegate() op); - - /** - * Assigns a logical thread to execute the supplied op. - * - * This routine is called by spawn. It is expected to instantiate a new - * logical thread and run the supplied operation. This thread must call - * `thisInfo.cleanup()` when the thread terminates if the scheduled thread - * is not a kernel thread--all kernel threads will have their `ThreadInfo` - * cleaned up automatically by a thread-local destructor. - * - * Params: - * op = The function to execute. This may be the actual function passed - * by the user to spawn itself, or may be a wrapper function. - */ - void spawn(void delegate() op); - - /** - * Yields execution to another logical thread. - * - * This routine is called at various points within concurrency-aware APIs - * to provide a scheduler a chance to yield execution when using some sort - * of cooperative multithreading model. If this is not appropriate, such - * as when each logical thread is backed by a dedicated kernel thread, - * this routine may be a no-op. - */ - void yield() nothrow; - - /** - * Returns an appropriate `ThreadInfo` instance. - * - * Returns an instance of `ThreadInfo` specific to the logical thread that - * is calling this routine or, if the calling thread was not create by - * this scheduler, returns `ThreadInfo.thisInfo` instead. - */ - @property ref ThreadInfo thisInfo() nothrow; - - /** - * Creates a `Condition` variable analog for signaling. - * - * Creates a new `Condition` variable analog which is used to check for and - * to signal the addition of messages to a thread's message queue. Like - * yield, some schedulers may need to define custom behavior so that calls - * to `Condition.wait()` yield to another thread when no new messages are - * available instead of blocking. - * - * Params: - * m = The `Mutex` that will be associated with this condition. It will be - * locked prior to any operation on the condition, and so in some - * cases a `Scheduler` may need to hold this reference and unlock the - * mutex before yielding execution to another logical thread. - */ - Condition newCondition(Mutex m) nothrow; -} - -/** - * An example `Scheduler` using kernel threads. - * - * This is an example `Scheduler` that mirrors the default scheduling behavior - * of creating one kernel thread per call to spawn. It is fully functional - * and may be instantiated and used, but is not a necessary part of the - * default functioning of this module. - */ -class ThreadScheduler : Scheduler -{ - /** - * This simply runs op directly, since no real scheduling is needed by - * this approach. - */ - void start(void delegate() op) - { - op(); - } - - /** - * Creates a new kernel thread and assigns it to run the supplied op. - */ - void spawn(void delegate() op) - { - auto t = new Thread(op); - t.start(); - } - - /** - * This scheduler does no explicit multiplexing, so this is a no-op. - */ - void yield() nothrow - { - // no explicit yield needed - } - - /** - * Returns `ThreadInfo.thisInfo`, since it is a thread-local instance of - * `ThreadInfo`, which is the correct behavior for this scheduler. - */ - @property ref ThreadInfo thisInfo() nothrow - { - return ThreadInfo.thisInfo; - } - - /** - * Creates a new `Condition` variable. No custom behavior is needed here. - */ - Condition newCondition(Mutex m) nothrow - { - return new Condition(m); - } -} - -/** - * An example `Scheduler` using $(MREF_ALTTEXT `Fiber`s, core, thread, fiber). - * - * This is an example scheduler that creates a new `Fiber` per call to spawn - * and multiplexes the execution of all fibers within the main thread. - */ -class FiberScheduler : Scheduler -{ - /** - * This creates a new `Fiber` for the supplied op and then starts the - * dispatcher. - */ - void start(void delegate() op) - { - create(op); - dispatch(); - } - - /** - * This created a new `Fiber` for the supplied op and adds it to the - * dispatch list. - */ - void spawn(void delegate() op) nothrow - { - create(op); - yield(); - } - - /** - * If the caller is a scheduled `Fiber`, this yields execution to another - * scheduled `Fiber`. - */ - void yield() nothrow - { - // NOTE: It's possible that we should test whether the calling Fiber - // is an InfoFiber before yielding, but I think it's reasonable - // that any (non-Generator) fiber should yield here. - if (Fiber.getThis()) - Fiber.yield(); - } - - /** - * Returns an appropriate `ThreadInfo` instance. - * - * Returns a `ThreadInfo` instance specific to the calling `Fiber` if the - * `Fiber` was created by this dispatcher, otherwise it returns - * `ThreadInfo.thisInfo`. - */ - @property ref ThreadInfo thisInfo() nothrow - { - auto f = cast(InfoFiber) Fiber.getThis(); - - if (f !is null) - return f.info; - return ThreadInfo.thisInfo; - } - - /** - * Returns a `Condition` analog that yields when wait or notify is called. - * - * Bug: - * For the default implementation, `notifyAll` will behave like `notify`. - * - * Params: - * m = A `Mutex` to use for locking if the condition needs to be waited on - * or notified from multiple `Thread`s. - * If `null`, no `Mutex` will be used and it is assumed that the - * `Condition` is only waited on/notified from one `Thread`. - */ - Condition newCondition(Mutex m) nothrow - { - return new FiberCondition(m); - } - -protected: - /** - * Creates a new `Fiber` which calls the given delegate. - * - * Params: - * op = The delegate the fiber should call - */ - void create(void delegate() op) nothrow - { - void wrap() - { - scope (exit) - { - thisInfo.cleanup(); - } - op(); - } - - m_fibers ~= new InfoFiber(&wrap); - } - - /** - * `Fiber` which embeds a `ThreadInfo` - */ - static class InfoFiber : Fiber - { - ThreadInfo info; - - this(void delegate() op) nothrow - { - super(op); - } - - this(void delegate() op, size_t sz) nothrow - { - super(op, sz); - } - } - -private: - class FiberCondition : Condition - { - this(Mutex m) nothrow - { - super(m); - notified = false; - } - - override void wait() nothrow - { - scope (exit) notified = false; - - while (!notified) - switchContext(); - } - - override bool wait(Duration period) nothrow - { - import core.time : MonoTime; - - scope (exit) notified = false; - - for (auto limit = MonoTime.currTime + period; - !notified && !period.isNegative; - period = limit - MonoTime.currTime) - { - this.outer.yield(); - } - return notified; - } - - override void notify() nothrow - { - notified = true; - switchContext(); - } - - override void notifyAll() nothrow - { - notified = true; - switchContext(); - } - - private: - void switchContext() nothrow - { - if (mutex_nothrow) mutex_nothrow.unlock_nothrow(); - scope (exit) - if (mutex_nothrow) - mutex_nothrow.lock_nothrow(); - this.outer.yield(); - } - - bool notified; - } - - void dispatch() - { - import std.algorithm.mutation : remove; - - while (m_fibers.length > 0) - { - auto t = m_fibers[m_pos].call(Fiber.Rethrow.no); - if (t !is null && !(cast(OwnerTerminated) t)) - { - throw t; - } - if (m_fibers[m_pos].state == Fiber.State.TERM) - { - if (m_pos >= (m_fibers = remove(m_fibers, m_pos)).length) - m_pos = 0; - } - else if (m_pos++ >= m_fibers.length - 1) - { - m_pos = 0; - } - } - } - - Fiber[] m_fibers; - size_t m_pos; -} - -@system unittest -{ - static void receive(Condition cond, ref size_t received) - { - while (true) - { - synchronized (cond.mutex) - { - cond.wait(); - ++received; - } - } - } - - static void send(Condition cond, ref size_t sent) - { - while (true) - { - synchronized (cond.mutex) - { - ++sent; - cond.notify(); - } - } - } - - auto fs = new FiberScheduler; - auto mtx = new Mutex; - auto cond = fs.newCondition(mtx); - - size_t received, sent; - auto waiter = new Fiber({ receive(cond, received); }), notifier = new Fiber({ send(cond, sent); }); - waiter.call(); - assert(received == 0); - notifier.call(); - assert(sent == 1); - assert(received == 0); - waiter.call(); - assert(received == 1); - waiter.call(); - assert(received == 1); -} - -/** - * Sets the `Scheduler` behavior within the program. - * - * This variable sets the `Scheduler` behavior within this program. Typically, - * when setting a `Scheduler`, `scheduler.start()` should be called in `main`. This - * routine will not return until program execution is complete. - */ -__gshared Scheduler scheduler; - -// Generator - -/** - * If the caller is a `Fiber` and is not a $(LREF Generator), this function will call - * `scheduler.yield()` or `Fiber.yield()`, as appropriate. - */ -void yield() nothrow -{ - auto fiber = Fiber.getThis(); - if (!(cast(IsGenerator) fiber)) - { - if (scheduler is null) - { - if (fiber) - return Fiber.yield(); - } - else - scheduler.yield(); - } -} - -/// Used to determine whether a Generator is running. -private interface IsGenerator {} - - -/** - * A Generator is a $(MREF_ALTTEXT Fiber, core, thread, fiber) - * that periodically returns values of type `T` to the - * caller via `yield`. This is represented as an InputRange. - */ -class Generator(T) : - Fiber, IsGenerator, InputRange!T -{ - /** - * Initializes a generator object which is associated with a static - * D function. The function will be called once to prepare the range - * for iteration. - * - * Params: - * fn = The fiber function. - * - * In: - * fn must not be null. - */ - this(void function() fn) - { - super(fn); - call(); - } - - /** - * Initializes a generator object which is associated with a static - * D function. The function will be called once to prepare the range - * for iteration. - * - * Params: - * fn = The fiber function. - * sz = The stack size for this fiber. - * - * In: - * fn must not be null. - */ - this(void function() fn, size_t sz) - { - super(fn, sz); - call(); - } - - /** - * Initializes a generator object which is associated with a static - * D function. The function will be called once to prepare the range - * for iteration. - * - * Params: - * fn = The fiber function. - * sz = The stack size for this fiber. - * guardPageSize = size of the guard page to trap fiber's stack - * overflows. Refer to $(REF Fiber, core,thread)'s - * documentation for more details. - * - * In: - * fn must not be null. - */ - this(void function() fn, size_t sz, size_t guardPageSize) - { - super(fn, sz, guardPageSize); - call(); - } - - /** - * Initializes a generator object which is associated with a dynamic - * D function. The function will be called once to prepare the range - * for iteration. - * - * Params: - * dg = The fiber function. - * - * In: - * dg must not be null. - */ - this(void delegate() dg) - { - super(dg); - call(); - } - - /** - * Initializes a generator object which is associated with a dynamic - * D function. The function will be called once to prepare the range - * for iteration. - * - * Params: - * dg = The fiber function. - * sz = The stack size for this fiber. - * - * In: - * dg must not be null. - */ - this(void delegate() dg, size_t sz) - { - super(dg, sz); - call(); - } - - /** - * Initializes a generator object which is associated with a dynamic - * D function. The function will be called once to prepare the range - * for iteration. - * - * Params: - * dg = The fiber function. - * sz = The stack size for this fiber. - * guardPageSize = size of the guard page to trap fiber's stack - * overflows. Refer to $(REF Fiber, core,thread)'s - * documentation for more details. - * - * In: - * dg must not be null. - */ - this(void delegate() dg, size_t sz, size_t guardPageSize) - { - super(dg, sz, guardPageSize); - call(); - } - - /** - * Returns true if the generator is empty. - */ - final bool empty() @property - { - return m_value is null || state == State.TERM; - } - - /** - * Obtains the next value from the underlying function. - */ - final void popFront() - { - call(); - } - - /** - * Returns the most recently generated value by shallow copy. - */ - final T front() @property - { - return *m_value; - } - - /** - * Returns the most recently generated value without executing a - * copy contructor. Will not compile for element types defining a - * postblit, because `Generator` does not return by reference. - */ - final T moveFront() - { - static if (!hasElaborateCopyConstructor!T) - { - return front; - } - else - { - static assert(0, - "Fiber front is always rvalue and thus cannot be moved since it defines a postblit."); - } - } - - final int opApply(scope int delegate(T) loopBody) - { - int broken; - for (; !empty; popFront()) - { - broken = loopBody(front); - if (broken) break; - } - return broken; - } - - final int opApply(scope int delegate(size_t, T) loopBody) - { - int broken; - for (size_t i; !empty; ++i, popFront()) - { - broken = loopBody(i, front); - if (broken) break; - } - return broken; - } -private: - T* m_value; -} - -/// -@system unittest -{ - auto tid = spawn({ - int i; - while (i < 9) - i = receiveOnly!int; - - ownerTid.send(i * 2); - }); - - auto r = new Generator!int({ - foreach (i; 1 .. 10) - yield(i); - }); - - foreach (e; r) - tid.send(e); - - assert(receiveOnly!int == 18); -} - -/** - * Yields a value of type T to the caller of the currently executing - * generator. - * - * Params: - * value = The value to yield. - */ -void yield(T)(ref T value) -{ - Generator!T cur = cast(Generator!T) Fiber.getThis(); - if (cur !is null && cur.state == Fiber.State.EXEC) - { - cur.m_value = &value; - return Fiber.yield(); - } - throw new Exception("yield(T) called with no active generator for the supplied type"); -} - -/// ditto -void yield(T)(T value) -{ - yield(value); -} - -@system unittest -{ - import core.exception; - import std.exception; - - auto mainTid = thisTid; - alias testdg = () { - auto tid = spawn( - (Tid mainTid) { - int i; - scope (failure) mainTid.send(false); - try - { - for (i = 1; i < 10; i++) - { - if (receiveOnly!int() != i) - { - mainTid.send(false); - break; - } - } - } - catch (OwnerTerminated e) - { - // i will advance 1 past the last value expected - mainTid.send(i == 4); - } - }, mainTid); - auto r = new Generator!int( - { - assertThrown!Exception(yield(2.0)); - yield(); // ensure this is a no-op - yield(1); - yield(); // also once something has been yielded - yield(2); - yield(3); - }); - - foreach (e; r) - { - tid.send(e); - } - }; - - scheduler = new ThreadScheduler; - scheduler.spawn(testdg); - assert(receiveOnly!bool()); - - scheduler = new FiberScheduler; - scheduler.start(testdg); - assert(receiveOnly!bool()); - scheduler = null; -} -/// -@system unittest -{ - import std.range; - - InputRange!int myIota = iota(10).inputRangeObject; - - myIota.popFront(); - myIota.popFront(); - assert(myIota.moveFront == 2); - assert(myIota.front == 2); - myIota.popFront(); - assert(myIota.front == 3); - - //can be assigned to std.range.interfaces.InputRange directly - myIota = new Generator!int( - { - foreach (i; 0 .. 10) yield(i); - }); - - myIota.popFront(); - myIota.popFront(); - assert(myIota.moveFront == 2); - assert(myIota.front == 2); - myIota.popFront(); - assert(myIota.front == 3); - - size_t[2] counter = [0, 0]; - foreach (i, unused; myIota) counter[] += [1, i]; - - assert(myIota.empty); - assert(counter == [7, 21]); -} - -private -{ - /* - * A MessageBox is a message queue for one thread. Other threads may send - * messages to this owner by calling put(), and the owner receives them by - * calling get(). The put() call is therefore effectively shared and the - * get() call is effectively local. setMaxMsgs may be used by any thread - * to limit the size of the message queue. - */ - class MessageBox - { - this() @trusted nothrow /* TODO: make @safe after relevant druntime PR gets merged */ - { - m_lock = new Mutex; - m_closed = false; - - if (scheduler is null) - { - m_putMsg = new Condition(m_lock); - m_notFull = new Condition(m_lock); - } - else - { - m_putMsg = scheduler.newCondition(m_lock); - m_notFull = scheduler.newCondition(m_lock); - } - } - - /// - final @property bool isClosed() @safe @nogc pure - { - synchronized (m_lock) - { - return m_closed; - } - } - - /* - * Sets a limit on the maximum number of user messages allowed in the - * mailbox. If this limit is reached, the caller attempting to add - * a new message will execute call. If num is zero, there is no limit - * on the message queue. - * - * Params: - * num = The maximum size of the queue or zero if the queue is - * unbounded. - * call = The routine to call when the queue is full. - */ - final void setMaxMsgs(size_t num, bool function(Tid) call) @safe @nogc pure - { - synchronized (m_lock) - { - m_maxMsgs = num; - m_onMaxMsgs = call; - } - } - - /* - * If maxMsgs is not set, the message is added to the queue and the - * owner is notified. If the queue is full, the message will still be - * accepted if it is a control message, otherwise onCrowdingDoThis is - * called. If the routine returns true, this call will block until - * the owner has made space available in the queue. If it returns - * false, this call will abort. - * - * Params: - * msg = The message to put in the queue. - * - * Throws: - * An exception if the queue is full and onCrowdingDoThis throws. - */ - final void put(ref Message msg) - { - synchronized (m_lock) - { - // TODO: Generate an error here if m_closed is true, or maybe - // put a message in the caller's queue? - if (!m_closed) - { - while (true) - { - if (isPriorityMsg(msg)) - { - m_sharedPty.put(msg); - m_putMsg.notify(); - return; - } - if (!mboxFull() || isControlMsg(msg)) - { - m_sharedBox.put(msg); - m_putMsg.notify(); - return; - } - if (m_onMaxMsgs !is null && !m_onMaxMsgs(thisTid)) - { - return; - } - m_putQueue++; - m_notFull.wait(); - m_putQueue--; - } - } - } - } - - /* - * Matches ops against each message in turn until a match is found. - * - * Params: - * ops = The operations to match. Each may return a bool to indicate - * whether a message with a matching type is truly a match. - * - * Returns: - * true if a message was retrieved and false if not (such as if a - * timeout occurred). - * - * Throws: - * LinkTerminated if a linked thread terminated, or OwnerTerminated - * if the owner thread terminates and no existing messages match the - * supplied ops. - */ - bool get(T...)(scope T vals) - { - import std.meta : AliasSeq; - - static assert(T.length, "T must not be empty"); - - static if (is(T[0] : Duration)) - { - alias Ops = AliasSeq!(T[1 .. $]); - alias ops = vals[1 .. $]; - enum timedWait = true; - Duration period = vals[0]; - } - else - { - alias Ops = AliasSeq!(T); - alias ops = vals[0 .. $]; - enum timedWait = false; - } - - bool onStandardMsg(ref Message msg) - { - foreach (i, t; Ops) - { - alias Args = Parameters!(t); - auto op = ops[i]; - - if (msg.convertsTo!(Args)) - { - alias RT = ReturnType!(t); - static if (is(RT == bool)) - { - return msg.map(op); - } - else - { - msg.map(op); - static if (!is(immutable RT == immutable noreturn)) - return true; - } - } - } - return false; - } - - bool onLinkDeadMsg(ref Message msg) - { - assert(msg.convertsTo!(Tid), - "Message could be converted to Tid"); - auto tid = msg.get!(Tid); - - if (bool* pDepends = tid in thisInfo.links) - { - auto depends = *pDepends; - thisInfo.links.remove(tid); - // Give the owner relationship precedence. - if (depends && tid != thisInfo.owner) - { - auto e = new LinkTerminated(tid); - auto m = Message(MsgType.standard, e); - if (onStandardMsg(m)) - return true; - throw e; - } - } - if (tid == thisInfo.owner) - { - thisInfo.owner = Tid.init; - auto e = new OwnerTerminated(tid); - auto m = Message(MsgType.standard, e); - if (onStandardMsg(m)) - return true; - throw e; - } - return false; - } - - bool onControlMsg(ref Message msg) - { - switch (msg.type) - { - case MsgType.linkDead: - return onLinkDeadMsg(msg); - default: - return false; - } - } - - bool scan(ref ListT list) - { - for (auto range = list[]; !range.empty;) - { - // Only the message handler will throw, so if this occurs - // we can be certain that the message was handled. - scope (failure) - list.removeAt(range); - - if (isControlMsg(range.front)) - { - if (onControlMsg(range.front)) - { - // Although the linkDead message is a control message, - // it can be handled by the user. Since the linkDead - // message throws if not handled, if we get here then - // it has been handled and we can return from receive. - // This is a weird special case that will have to be - // handled in a more general way if more are added. - if (!isLinkDeadMsg(range.front)) - { - list.removeAt(range); - continue; - } - list.removeAt(range); - return true; - } - range.popFront(); - continue; - } - else - { - if (onStandardMsg(range.front)) - { - list.removeAt(range); - return true; - } - range.popFront(); - continue; - } - } - return false; - } - - bool pty(ref ListT list) - { - if (!list.empty) - { - auto range = list[]; - - if (onStandardMsg(range.front)) - { - list.removeAt(range); - return true; - } - if (range.front.convertsTo!(Throwable)) - throw range.front.get!(Throwable); - else if (range.front.convertsTo!(shared(Throwable))) - /* Note: a shared type can be caught without the shared qualifier - * so throwing shared will be an error */ - throw cast() range.front.get!(shared(Throwable)); - else - throw new PriorityMessageException(range.front.data); - } - return false; - } - - static if (timedWait) - { - import core.time : MonoTime; - auto limit = MonoTime.currTime + period; - } - - while (true) - { - ListT arrived; - - if (pty(m_localPty) || scan(m_localBox)) - { - return true; - } - yield(); - synchronized (m_lock) - { - updateMsgCount(); - while (m_sharedPty.empty && m_sharedBox.empty) - { - // NOTE: We're notifying all waiters here instead of just - // a few because the onCrowding behavior may have - // changed and we don't want to block sender threads - // unnecessarily if the new behavior is not to block. - // This will admittedly result in spurious wakeups - // in other situations, but what can you do? - if (m_putQueue && !mboxFull()) - m_notFull.notifyAll(); - static if (timedWait) - { - if (period <= Duration.zero || !m_putMsg.wait(period)) - return false; - } - else - { - m_putMsg.wait(); - } - } - m_localPty.put(m_sharedPty); - arrived.put(m_sharedBox); - } - if (m_localPty.empty) - { - scope (exit) m_localBox.put(arrived); - if (scan(arrived)) - { - return true; - } - else - { - static if (timedWait) - { - period = limit - MonoTime.currTime; - } - continue; - } - } - m_localBox.put(arrived); - pty(m_localPty); - return true; - } - } - - /* - * Called on thread termination. This routine processes any remaining - * control messages, clears out message queues, and sets a flag to - * reject any future messages. - */ - final void close() - { - static void onLinkDeadMsg(ref Message msg) - { - assert(msg.convertsTo!(Tid), - "Message could be converted to Tid"); - auto tid = msg.get!(Tid); - - thisInfo.links.remove(tid); - if (tid == thisInfo.owner) - thisInfo.owner = Tid.init; - } - - static void sweep(ref ListT list) - { - for (auto range = list[]; !range.empty; range.popFront()) - { - if (range.front.type == MsgType.linkDead) - onLinkDeadMsg(range.front); - } - } - - ListT arrived; - - sweep(m_localBox); - synchronized (m_lock) - { - arrived.put(m_sharedBox); - m_closed = true; - } - m_localBox.clear(); - sweep(arrived); - } - - private: - // Routines involving local data only, no lock needed. - - bool mboxFull() @safe @nogc pure nothrow - { - return m_maxMsgs && m_maxMsgs <= m_localMsgs + m_sharedBox.length; - } - - void updateMsgCount() @safe @nogc pure nothrow - { - m_localMsgs = m_localBox.length; - } - - bool isControlMsg(ref Message msg) @safe @nogc pure nothrow - { - return msg.type != MsgType.standard && msg.type != MsgType.priority; - } - - bool isPriorityMsg(ref Message msg) @safe @nogc pure nothrow - { - return msg.type == MsgType.priority; - } - - bool isLinkDeadMsg(ref Message msg) @safe @nogc pure nothrow - { - return msg.type == MsgType.linkDead; - } - - alias OnMaxFn = bool function(Tid); - alias ListT = List!(Message); - - ListT m_localBox; - ListT m_localPty; - - Mutex m_lock; - Condition m_putMsg; - Condition m_notFull; - size_t m_putQueue; - ListT m_sharedBox; - ListT m_sharedPty; - OnMaxFn m_onMaxMsgs; - size_t m_localMsgs; - size_t m_maxMsgs; - bool m_closed; - } - - /* - * - */ - struct List(T) - { - struct Range - { - import std.exception : enforce; - - @property bool empty() const - { - return !m_prev.next; - } - - @property ref T front() - { - enforce(m_prev.next, "invalid list node"); - return m_prev.next.val; - } - - @property void front(T val) - { - enforce(m_prev.next, "invalid list node"); - m_prev.next.val = val; - } - - void popFront() - { - enforce(m_prev.next, "invalid list node"); - m_prev = m_prev.next; - } - - private this(Node* p) - { - m_prev = p; - } - - private Node* m_prev; - } - - void put(T val) - { - put(newNode(val)); - } - - void put(ref List!(T) rhs) - { - if (!rhs.empty) - { - put(rhs.m_first); - while (m_last.next !is null) - { - m_last = m_last.next; - m_count++; - } - rhs.m_first = null; - rhs.m_last = null; - rhs.m_count = 0; - } - } - - Range opSlice() - { - return Range(cast(Node*)&m_first); - } - - void removeAt(Range r) - { - import std.exception : enforce; - - assert(m_count, "Can not remove from empty Range"); - Node* n = r.m_prev; - enforce(n && n.next, "attempting to remove invalid list node"); - - if (m_last is m_first) - m_last = null; - else if (m_last is n.next) - m_last = n; // nocoverage - Node* to_free = n.next; - n.next = n.next.next; - freeNode(to_free); - m_count--; - } - - @property size_t length() - { - return m_count; - } - - void clear() - { - m_first = m_last = null; - m_count = 0; - } - - @property bool empty() - { - return m_first is null; - } - - private: - struct Node - { - Node* next; - T val; - - this(T v) - { - val = v; - } - } - - static shared struct SpinLock - { - void lock() { while (!cas(&locked, false, true)) { Thread.yield(); } } - void unlock() { atomicStore!(MemoryOrder.rel)(locked, false); } - bool locked; - } - - static shared SpinLock sm_lock; - static shared Node* sm_head; - - Node* newNode(T v) - { - Node* n; - { - sm_lock.lock(); - scope (exit) sm_lock.unlock(); - - if (sm_head) - { - n = cast(Node*) sm_head; - sm_head = sm_head.next; - } - } - if (n) - { - import core.lifetime : emplace; - emplace!Node(n, v); - } - else - { - n = new Node(v); - } - return n; - } - - void freeNode(Node* n) - { - // destroy val to free any owned GC memory - destroy(n.val); - - sm_lock.lock(); - scope (exit) sm_lock.unlock(); - - auto sn = cast(shared(Node)*) n; - sn.next = sm_head; - sm_head = sn; - } - - void put(Node* n) - { - m_count++; - if (!empty) - { - m_last.next = n; - m_last = n; - return; - } - m_first = n; - m_last = n; - } - - Node* m_first; - Node* m_last; - size_t m_count; - } -} - -@system unittest -{ - import std.typecons : tuple, Tuple; - - static void testfn(Tid tid) - { - receive((float val) { assert(0); }, (int val, int val2) { - assert(val == 42 && val2 == 86); - }); - receive((Tuple!(int, int) val) { assert(val[0] == 42 && val[1] == 86); }); - receive((Variant val) { }); - receive((string val) { - if ("the quick brown fox" != val) - return false; - return true; - }, (string val) { assert(false); }); - prioritySend(tid, "done"); - } - - static void runTest(Tid tid) - { - send(tid, 42, 86); - send(tid, tuple(42, 86)); - send(tid, "hello", "there"); - send(tid, "the quick brown fox"); - receive((string val) { assert(val == "done"); }); - } - - static void simpleTest() - { - auto tid = spawn(&testfn, thisTid); - runTest(tid); - - // Run the test again with a limited mailbox size. - tid = spawn(&testfn, thisTid); - setMaxMailboxSize(tid, 2, OnCrowding.block); - runTest(tid); - } - - simpleTest(); - - scheduler = new ThreadScheduler; - simpleTest(); - scheduler = null; -} - -private @property shared(Mutex) initOnceLock() -{ - static shared Mutex lock; - if (auto mtx = atomicLoad!(MemoryOrder.acq)(lock)) - return mtx; - auto mtx = new shared Mutex; - if (cas(&lock, cast(shared) null, mtx)) - return mtx; - return atomicLoad!(MemoryOrder.acq)(lock); -} - -/** - * Initializes $(D_PARAM var) with the lazy $(D_PARAM init) value in a - * thread-safe manner. - * - * The implementation guarantees that all threads simultaneously calling - * initOnce with the same $(D_PARAM var) argument block until $(D_PARAM var) is - * fully initialized. All side-effects of $(D_PARAM init) are globally visible - * afterwards. - * - * Params: - * var = The variable to initialize - * init = The lazy initializer value - * - * Returns: - * A reference to the initialized variable - */ -auto ref initOnce(alias var)(lazy typeof(var) init) -{ - return initOnce!var(init, initOnceLock); -} - -/// A typical use-case is to perform lazy but thread-safe initialization. -@system unittest -{ - static class MySingleton - { - static MySingleton instance() - { - __gshared MySingleton inst; - return initOnce!inst(new MySingleton); - } - } - - assert(MySingleton.instance !is null); -} - -@system unittest -{ - static class MySingleton - { - static MySingleton instance() - { - __gshared MySingleton inst; - return initOnce!inst(new MySingleton); - } - - private: - this() { val = ++cnt; } - size_t val; - __gshared size_t cnt; - } - - foreach (_; 0 .. 10) - spawn({ ownerTid.send(MySingleton.instance.val); }); - foreach (_; 0 .. 10) - assert(receiveOnly!size_t == MySingleton.instance.val); - assert(MySingleton.cnt == 1); -} - -/** - * Same as above, but takes a separate mutex instead of sharing one among - * all initOnce instances. - * - * This should be used to avoid dead-locks when the $(D_PARAM init) - * expression waits for the result of another thread that might also - * call initOnce. Use with care. - * - * Params: - * var = The variable to initialize - * init = The lazy initializer value - * mutex = A mutex to prevent race conditions - * - * Returns: - * A reference to the initialized variable - */ -auto ref initOnce(alias var)(lazy typeof(var) init, shared Mutex mutex) -{ - // check that var is global, can't take address of a TLS variable - static assert(is(typeof({ __gshared p = &var; })), - "var must be 'static shared' or '__gshared'."); - import core.atomic : atomicLoad, MemoryOrder, atomicStore; - - static shared bool flag; - if (!atomicLoad!(MemoryOrder.acq)(flag)) - { - synchronized (mutex) - { - if (!atomicLoad!(MemoryOrder.raw)(flag)) - { - var = init; - static if (!is(immutable typeof(var) == immutable noreturn)) - atomicStore!(MemoryOrder.rel)(flag, true); - } - } - } - return var; -} - -/// ditto -auto ref initOnce(alias var)(lazy typeof(var) init, Mutex mutex) -{ - return initOnce!var(init, cast(shared) mutex); -} - -/// Use a separate mutex when init blocks on another thread that might also call initOnce. -@system unittest -{ - import core.sync.mutex : Mutex; - - static shared bool varA, varB; - static shared Mutex m; - m = new shared Mutex; - - spawn({ - // use a different mutex for varB to avoid a dead-lock - initOnce!varB(true, m); - ownerTid.send(true); - }); - // init depends on the result of the spawned thread - initOnce!varA(receiveOnly!bool); - assert(varA == true); - assert(varB == true); -} - -@system unittest -{ - static shared bool a; - __gshared bool b; - static bool c; - bool d; - initOnce!a(true); - initOnce!b(true); - static assert(!__traits(compiles, initOnce!c(true))); // TLS - static assert(!__traits(compiles, initOnce!d(true))); // local variable -} - -// test ability to send shared arrays -@system unittest -{ - static shared int[] x = new shared(int)[1]; - auto tid = spawn({ - auto arr = receiveOnly!(shared(int)[]); - arr[0] = 5; - ownerTid.send(true); - }); - tid.send(x); - receiveOnly!(bool); - assert(x[0] == 5); -} - -// https://issues.dlang.org/show_bug.cgi?id=13930 -@system unittest -{ - immutable aa = ["0":0]; - thisTid.send(aa); - receiveOnly!(immutable int[string]); // compile error -} - -// https://issues.dlang.org/show_bug.cgi?id=19345 -@system unittest -{ - static struct Aggregate { const int a; const int[5] b; } - static void t1(Tid mainTid) - { - const sendMe = Aggregate(42, [1, 2, 3, 4, 5]); - mainTid.send(sendMe); - } - - spawn(&t1, thisTid); - auto result1 = receiveOnly!(const Aggregate)(); - immutable expected = Aggregate(42, [1, 2, 3, 4, 5]); - assert(result1 == expected); -} - -// Noreturn support -@system unittest -{ - static noreturn foo(int) { throw new Exception(""); } - - if (false) spawn(&foo, 1); - if (false) spawnLinked(&foo, 1); - - if (false) receive(&foo); - if (false) receiveTimeout(Duration.init, &foo); - - // Wrapped in __traits(compiles) to skip codegen which crashes dmd's backend - static assert(__traits(compiles, receiveOnly!noreturn() )); - static assert(__traits(compiles, send(Tid.init, noreturn.init) )); - static assert(__traits(compiles, prioritySend(Tid.init, noreturn.init) )); - static assert(__traits(compiles, yield(noreturn.init) )); - - static assert(__traits(compiles, { - __gshared noreturn n; - initOnce!n(noreturn.init); - })); -} diff --git a/phobos/std/container/array.d b/phobos/std/container/array.d deleted file mode 100644 index ad120c1..0000000 --- a/phobos/std/container/array.d +++ /dev/null @@ -1,2795 +0,0 @@ -/** - * This module provides an `Array` type with deterministic memory usage not - * reliant on the GC, as an alternative to the built-in arrays. - * - * This module is a submodule of $(MREF std, container). - * - * Source: $(PHOBOSSRC std/container/array.d) - * - * Copyright: 2010- Andrei Alexandrescu. All rights reserved by the respective holders. - * - * License: Distributed under the Boost Software License, Version 1.0. - * (See accompanying file LICENSE_1_0.txt or copy at $(HTTP - * boost.org/LICENSE_1_0.txt)). - * - * Authors: $(HTTP erdani.com, Andrei Alexandrescu) - * - * $(SCRIPT inhibitQuickIndex = 1;) - */ -module std.container.array; - -import core.exception : RangeError; -import std.range.primitives; -import std.traits; - -public import std.container.util; - -pure @system unittest -{ - // We test multiple array lengths in order to ensure that the "a1.capacity == a0.length" test is meaningful - // for the version in which the constructor uses insertBack - // (because for some lengths, the test passes even without reserve). - for (size_t n = 0; n < 100; ++n) - { - float[] a0; - { - import std.range : iota; - import std.array : array; - import std.algorithm.iteration : map; - a0 = iota (0, n).map!(i => i * 1.1f).array; - } - - auto a1 = Array!float(a0); - - // We check that a1 has the same length and contents as a0: - { - assert(a1.length == a0.length); - - // I wish that I could write "assert(a1[] == a0[]);", - // but the compiler complains: "Error: incompatible types for `(a1.opSlice()) == (a0[])`: `RangeT!(Array!float)` and `float[]`". - import std.algorithm.comparison : equal; - assert(equal(a1[], a0[])); - } - - // We check that a1's constructor has called reserve (to maintain good performance): - assert(a1.capacity == a0.length); - } -} - -pure @system unittest -{ - // To avoid bad performance, we check that an Array constructed from an empty range - // does not initialize its RefCountedStore object, even after a call to "reserve(0)". - - { - Array!float a1; - assert(! a1._data.refCountedStore.isInitialized); - a1.reserve(0); - assert(! a1._data.refCountedStore.isInitialized); - } - - { - float[] a0 = []; - Array!float a1 = a0; - // [2021-09-26] TODO: Investigate RefCounted. - //assert(! a1._data.refCountedStore.isInitialized); - a1.reserve(0); - // [2021-09-26] TODO: Investigate RefCounted. - //assert(! a1._data.refCountedStore.isInitialized); - } -} - -/// -pure @system unittest -{ - auto arr = Array!int(0, 2, 3); - assert(arr[0] == 0); - assert(arr.front == 0); - assert(arr.back == 3); - - // reserve space - arr.reserve(1000); - assert(arr.length == 3); - assert(arr.capacity >= 1000); - - // insertion - arr.insertBefore(arr[1..$], 1); - assert(arr.front == 0); - assert(arr.length == 4); - - arr.insertBack(4); - assert(arr.back == 4); - assert(arr.length == 5); - - // set elements - arr[1] *= 42; - assert(arr[1] == 42); -} - -/// -pure @system unittest -{ - import std.algorithm.comparison : equal; - auto arr = Array!int(1, 2, 3); - - // concat - auto b = Array!int(11, 12, 13); - arr ~= b; - assert(arr.length == 6); - - // slicing - assert(arr[1 .. 3].equal([2, 3])); - - // remove - arr.linearRemove(arr[1 .. 3]); - assert(arr[0 .. 2].equal([1, 11])); -} - -/// `Array!bool` packs together values efficiently by allocating one bit per element -pure @system unittest -{ - auto arr = Array!bool([true, true, false, true, false]); - assert(arr.length == 5); -} - -private struct RangeT(A) -{ - /* Workaround for https://issues.dlang.org/show_bug.cgi?id=13629 - See also: http://forum.dlang.org/post/vbmwhzvawhnkoxrhbnyb@forum.dlang.org - */ - private A[1] _outer_; - private @property ref inout(A) _outer() inout { return _outer_[0]; } - - private size_t _a, _b; - - /* E is different from T when A is more restrictively qualified than T: - immutable(Array!int) => T == int, E = immutable(int) */ - alias E = typeof(_outer_[0]._data._payload[0]); - - private this(ref A data, size_t a, size_t b) - { - _outer_ = data; - _a = a; - _b = b; - } - - @property RangeT save() - { - return this; - } - - @property bool empty() @safe pure nothrow const - { - return _a >= _b; - } - - @property size_t length() @safe pure nothrow const - { - return _b - _a; - } - alias opDollar = length; - - @property ref inout(E) front() inout - { - assert(!empty, "Attempting to access the front of an empty Array"); - return _outer[_a]; - } - @property ref inout(E) back() inout - { - assert(!empty, "Attempting to access the back of an empty Array"); - return _outer[_b - 1]; - } - - void popFront() @safe @nogc pure nothrow - { - assert(!empty, "Attempting to popFront an empty Array"); - ++_a; - } - - void popBack() @safe @nogc pure nothrow - { - assert(!empty, "Attempting to popBack an empty Array"); - --_b; - } - - static if (isMutable!A) - { - import std.algorithm.mutation : move; - - E moveFront() - { - assert(!empty, "Attempting to moveFront an empty Array"); - assert(_a < _outer.length, - "Attempting to moveFront using an out of bounds low index on an Array"); - return move(_outer._data._payload[_a]); - } - - E moveBack() - { - assert(!empty, "Attempting to moveBack an empty Array"); - assert(_b - 1 < _outer.length, - "Attempting to moveBack using an out of bounds high index on an Array"); - return move(_outer._data._payload[_b - 1]); - } - - E moveAt(size_t i) - { - assert(_a + i < _b, - "Attempting to moveAt using an out of bounds index on an Array"); - assert(_a + i < _outer.length, - "Cannot move past the end of the array"); - return move(_outer._data._payload[_a + i]); - } - } - - ref inout(E) opIndex(size_t i) inout - { - assert(_a + i < _b, - "Attempting to fetch using an out of bounds index on an Array"); - return _outer[_a + i]; - } - - RangeT opSlice() - { - return typeof(return)(_outer, _a, _b); - } - - RangeT opSlice(size_t i, size_t j) - { - assert(i <= j && _a + j <= _b, - "Attempting to slice using an out of bounds indices on an Array"); - return typeof(return)(_outer, _a + i, _a + j); - } - - RangeT!(const(A)) opSlice() const - { - return typeof(return)(_outer, _a, _b); - } - - RangeT!(const(A)) opSlice(size_t i, size_t j) const - { - assert(i <= j && _a + j <= _b, - "Attempting to slice using an out of bounds indices on an Array"); - return typeof(return)(_outer, _a + i, _a + j); - } - - static if (isMutable!A) - { - void opSliceAssign(E value) - { - assert(_b <= _outer.length, - "Attempting to assign using an out of bounds indices on an Array"); - _outer[_a .. _b] = value; - } - - void opSliceAssign(E value, size_t i, size_t j) - { - assert(_a + j <= _b, - "Attempting to slice assign using an out of bounds indices on an Array"); - _outer[_a + i .. _a + j] = value; - } - - void opSliceUnary(string op)() - if (op == "++" || op == "--") - { - assert(_b <= _outer.length, - "Attempting to slice using an out of bounds indices on an Array"); - mixin(op~"_outer[_a .. _b];"); - } - - void opSliceUnary(string op)(size_t i, size_t j) - if (op == "++" || op == "--") - { - assert(_a + j <= _b, - "Attempting to slice using an out of bounds indices on an Array"); - mixin(op~"_outer[_a + i .. _a + j];"); - } - - void opSliceOpAssign(string op)(E value) - { - assert(_b <= _outer.length, - "Attempting to slice using an out of bounds indices on an Array"); - mixin("_outer[_a .. _b] "~op~"= value;"); - } - - void opSliceOpAssign(string op)(E value, size_t i, size_t j) - { - assert(_a + j <= _b, - "Attempting to slice using an out of bounds indices on an Array"); - mixin("_outer[_a + i .. _a + j] "~op~"= value;"); - } - } -} - -@system unittest -{ - enum : bool { display = false } - static if (display) - { - import std.stdio; - enum { nDigitsForPointer = 2 * size_t.sizeof, nDigitsForNObjects = 4 } - } - - static struct S - { - static size_t s_nConstructed; - static size_t s_nDestroyed; - static void throwIfTooMany() - { - if (s_nConstructed >= 7) throw new Exception ("Ka-boom !"); - } - - uint _i; - - ~this() - { - static if (display) writefln("@%*Xh: Destroying.", nDigitsForPointer, &this); - ++s_nDestroyed; - } - - this(uint i) - { - static if (display) writefln("@%*Xh: Constructing.", nDigitsForPointer, &this); - _i = i; - ++s_nConstructed; - throwIfTooMany(); - } - - this(this) - { - static if (display) writefln("@%*Xh: Copying.", nDigitsForPointer, &this); - ++s_nConstructed; - throwIfTooMany(); - } - } - - try - { - auto a = Array!S (S(0), S(1), S(2), S(3)); - static if (display) writefln("@%*Xh: This is where the array elements are.", nDigitsForPointer, &a [0]); - } - catch (Exception e) - { - static if (display) writefln("Exception caught !"); - } - - static if (display) - { - writefln("s_nConstructed %*Xh.", nDigitsForNObjects, S.s_nConstructed); - writefln("s_nDestroyed %*Xh.", nDigitsForNObjects, S.s_nDestroyed); - writefln("s_nConstructed should be equal to s_nDestroyed."); - writefln(""); - } - - assert(S.s_nDestroyed == S.s_nConstructed); -} - - -/** - * _Array type with deterministic control of memory. The memory allocated - * for the array is reclaimed as soon as possible; there is no reliance - * on the garbage collector. `Array` uses `malloc`, `realloc` and `free` - * for managing its own memory. - * - * This means that pointers to elements of an `Array` will become - * dangling as soon as the element is removed from the `Array`. On the other hand - * the memory allocated by an `Array` will be scanned by the GC and - * GC managed objects referenced from an `Array` will be kept alive. - * - * Note: - * - * When using `Array` with range-based functions like those in `std.algorithm`, - * `Array` must be sliced to get a range (for example, use `array[].map!` - * instead of `array.map!`). The container itself is not a range. - */ -struct Array(T) -if (!is(immutable T == immutable bool)) -{ - import core.memory : free = pureFree; - import std.internal.memory : enforceMalloc, enforceRealloc; - import core.stdc.string : memcpy, memmove, memset; - - import core.memory : GC; - - import std.exception : enforce; - import std.typecons : RefCounted, RefCountedAutoInitialize; - - // This structure is not copyable. - private struct Payload - { - size_t _capacity; - T[] _payload; - - this(T[] p) { _capacity = p.length; _payload = p; } - - // Destructor releases array memory - ~this() - { - // Warning: destroy would destroy also class instances. - // The hasElaborateDestructor protects us here. - static if (hasElaborateDestructor!T) - foreach (ref e; _payload) - .destroy(e); - - static if (hasIndirections!T) - GC.removeRange(cast(void*) _payload.ptr); - - free(cast(void*) _payload.ptr); - } - - this(this) @disable; - - void opAssign(Payload rhs) @disable; - - @property size_t length() const - { - return _payload.length; - } - - @property void length(size_t newLength) - { - if (length >= newLength) - { - // shorten - static if (hasElaborateDestructor!T) - foreach (ref e; _payload.ptr[newLength .. _payload.length]) - .destroy(e); - - _payload = _payload.ptr[0 .. newLength]; - return; - } - - static if (__traits(compiles, { static T _; })) - { - import std.algorithm.mutation : initializeAll; - - immutable startEmplace = length; - reserve(newLength); - initializeAll(_payload.ptr[startEmplace .. newLength]); - _payload = _payload.ptr[0 .. newLength]; - } - else - { - assert(0, "Cannot add elements to array because `" ~ - fullyQualifiedName!T ~ ".this()` is annotated with " ~ - "`@disable`."); - } - } - - @property size_t capacity() const - { - return _capacity; - } - - void reserve(size_t elements) - { - if (elements <= capacity) return; - static if (T.sizeof == 1) - { - const sz = elements; - } - else - { - import core.checkedint : mulu; - bool overflow; - const sz = mulu(elements, T.sizeof, overflow); - if (overflow) - assert(false, "Overflow"); - } - static if (hasIndirections!T) - { - /* Because of the transactional nature of this - * relative to the garbage collector, ensure no - * threading bugs by using malloc/copy/free rather - * than realloc. - */ - immutable oldLength = length; - - auto newPayloadPtr = cast(T*) enforceMalloc(sz); - auto newPayload = newPayloadPtr[0 .. oldLength]; - - // copy old data over to new array - memcpy(cast(void*) newPayload.ptr, cast(void*) _payload.ptr, T.sizeof * oldLength); - // Zero out unused capacity to prevent gc from seeing false pointers - memset( cast(void*) (newPayload.ptr + oldLength), - 0, - (elements - oldLength) * T.sizeof); - GC.addRange(cast(void*) newPayload.ptr, sz); - GC.removeRange(cast(void*) _payload.ptr); - free(cast(void*) _payload.ptr); - _payload = newPayload; - } - else - { - // These can't have pointers, so no need to zero unused region - auto newPayloadPtr = cast(T*) enforceRealloc(_payload.ptr, sz); - auto newPayload = newPayloadPtr[0 .. length]; - _payload = newPayload; - } - _capacity = elements; - } - - // Insert one item - size_t insertBack(Elem)(Elem elem) - if (isImplicitlyConvertible!(Elem, T)) - { - import core.lifetime : emplace; - assert(_capacity >= length); - if (_capacity == length) - { - import core.checkedint : addu; - - bool overflow; - immutable size_t newCapacity = addu(capacity, capacity / 2 + 1, overflow); - if (overflow) - assert(false, "Overflow"); - - reserve(newCapacity); - } - assert(capacity > length && _payload.ptr, - "Failed to reserve memory"); - emplace(_payload.ptr + _payload.length, elem); - _payload = _payload.ptr[0 .. _payload.length + 1]; - return 1; - } - - // Insert a range of items - size_t insertBack(Range)(Range r) - if (isInputRange!Range && isImplicitlyConvertible!(ElementType!Range, T)) - { - immutable size_t oldLength = length; - - static if (hasLength!Range) - { - immutable size_t rLength = r.length; - reserve(oldLength + rLength); - } - - size_t result; - foreach (item; r) - { - insertBack(item); - ++result; - } - - static if (hasLength!Range) - assert(result == rLength, "insertBack: range might have changed length"); - - assert(length == oldLength + result, - "Failed to insertBack range"); - - return result; - } - } - private alias Data = RefCounted!(Payload, RefCountedAutoInitialize.no); - private Data _data; - - /** - * Constructor taking a number of items. - */ - this(U)(U[] values...) - if (isImplicitlyConvertible!(U, T)) - { - // [2021-07-17] Checking to see whether *always* calling ensureInitialized works-around-and/or-is-related-to https://issues.dlang.org/show_bug.cgihttps://issues.dlang.org/show_bug.cgi... - //if (values.length) - { - _data.refCountedStore.ensureInitialized(); - _data.reserve(values.length); - foreach (ref value; values) - { - // We do not simply write "_data.insertBack(value);" - // because that might perform, on each iteration, a now-redundant check of length vs capacity. - // Thanks to @dkorpel (https://github.com/dlang/phobos/pull/8162#discussion_r667479090). - - import core.lifetime : emplace; - emplace(_data._payload.ptr + _data._payload.length, value); - - // We increment the length after each iteration (as opposed to adjusting it just once, after the loop) - // in order to improve error-safety (in case one of the calls to emplace throws). - _data._payload = _data._payload.ptr[0 .. _data._payload.length + 1]; - } - } - - assert(length == values.length); // We check that all values have been inserted. - assert(capacity == values.length); // We check that reserve has been called before the loop. - } - - /// ditto - // needed when T is an array and only one argument is passed - this(T single) { __ctor!T(single); } - - /** - * Constructor taking an $(REF_ALTTEXT input range, isInputRange, std,range,primitives) - */ - this(Range)(Range r) - if (isInputRange!Range && isImplicitlyConvertible!(ElementType!Range, T) && !is(Range == T[])) - { - insertBack(r); - } - - /** - * Comparison for equality. - */ - bool opEquals(const Array rhs) const - { - return opEquals(rhs); - } - - // fix https://issues.dlang.org/show_bug.cgi?23140 - private alias Unshared(T) = T; - private alias Unshared(T: shared U, U) = U; - - /// ditto - bool opEquals(ref const Array rhs) const - { - if (empty) return rhs.empty; - if (rhs.empty) return false; - - return cast(Unshared!(T)[]) _data._payload == cast(Unshared!(T)[]) rhs._data._payload; - } - - /** - * Defines the array's primary range, which is a random-access range. - * - * `ConstRange` is a variant with `const` elements. - * `ImmutableRange` is a variant with `immutable` elements. - */ - alias Range = RangeT!Array; - - /// ditto - alias ConstRange = RangeT!(const Array); - - /// ditto - alias ImmutableRange = RangeT!(immutable Array); - - /** - * Duplicates the array. The elements themselves are not transitively - * duplicated. - * - * Complexity: $(BIGOH length). - */ - @property Array dup() - { - if (!_data.refCountedStore.isInitialized) return this; - return Array(_data._payload); - } - - /** - * Returns: `true` if and only if the array has no elements. - * - * Complexity: $(BIGOH 1) - */ - @property bool empty() const - { - return !_data.refCountedStore.isInitialized || _data._payload.empty; - } - - /** - * Returns: The number of elements in the array. - * - * Complexity: $(BIGOH 1). - */ - @property size_t length() const - { - return _data.refCountedStore.isInitialized ? _data._payload.length : 0; - } - - /// ditto - size_t opDollar() const - { - return length; - } - - /** - * Returns: The maximum number of elements the array can store without - * reallocating memory and invalidating iterators upon insertion. - * - * Complexity: $(BIGOH 1) - */ - @property size_t capacity() - { - return _data.refCountedStore.isInitialized ? _data._capacity : 0; - } - - /** - * Returns: the internal representation of the array. - * - * Complexity: $(BIGOH 1). - */ - - inout(T)[] data() inout @system - { - return _data.refCountedStore.isInitialized ? _data._payload : []; - } - - /** - * Ensures sufficient capacity to accommodate `e` _elements. - * If `e < capacity`, this method does nothing. - * - * Postcondition: `capacity >= e` - * - * Note: If the capacity is increased, one should assume that all - * iterators to the elements are invalidated. - * - * Complexity: at most $(BIGOH length) if `e > capacity`, otherwise $(BIGOH 1). - */ - void reserve(size_t elements) - { - if (elements > capacity) - { - _data.refCountedStore.ensureInitialized(); - _data.reserve(elements); - assert(capacity == elements); // Might need changing to ">=" if implementation of Payload.reserve is changed. - } - } - - /** - * Returns: A range that iterates over elements of the array in - * forward order. - * - * Complexity: $(BIGOH 1) - */ - Range opSlice() - { - return typeof(return)(this, 0, length); - } - - ConstRange opSlice() const - { - return typeof(return)(this, 0, length); - } - - ImmutableRange opSlice() immutable - { - return typeof(return)(this, 0, length); - } - - /** - * Returns: A range that iterates over elements of the array from - * index `i` up to (excluding) index `j`. - * - * Precondition: `i <= j && j <= length` - * - * Complexity: $(BIGOH 1) - */ - Range opSlice(size_t i, size_t j) - { - assert(i <= j && j <= length, "Invalid slice bounds"); - return typeof(return)(this, i, j); - } - - ConstRange opSlice(size_t i, size_t j) const - { - assert(i <= j && j <= length, "Invalid slice bounds"); - return typeof(return)(this, i, j); - } - - ImmutableRange opSlice(size_t i, size_t j) immutable - { - assert(i <= j && j <= length, "Invalid slice bounds"); - return typeof(return)(this, i, j); - } - - /** - * Returns: The first element of the array. - * - * Precondition: `empty == false` - * - * Complexity: $(BIGOH 1) - */ - @property ref inout(T) front() inout - { - assert(_data.refCountedStore.isInitialized, - "Cannot get front of empty range"); - return _data._payload[0]; - } - - /** - * Returns: The last element of the array. - * - * Precondition: `empty == false` - * - * Complexity: $(BIGOH 1) - */ - @property ref inout(T) back() inout - { - assert(_data.refCountedStore.isInitialized, - "Cannot get back of empty range"); - return _data._payload[$ - 1]; - } - - /** - * Returns: The element or a reference to the element at the specified index. - * - * Precondition: `i < length` - * - * Complexity: $(BIGOH 1) - */ - ref inout(T) opIndex(size_t i) inout - { - assert(_data.refCountedStore.isInitialized, - "Cannot index empty range"); - return _data._payload[i]; - } - - /** - * Slicing operators executing the specified operation on the entire slice. - * - * Precondition: `i < j && j < length` - * - * Complexity: $(BIGOH slice.length) - */ - void opSliceAssign(T value) - { - if (!_data.refCountedStore.isInitialized) return; - _data._payload[] = value; - } - - /// ditto - void opSliceAssign(T value, size_t i, size_t j) - { - auto slice = _data.refCountedStore.isInitialized ? - _data._payload : - T[].init; - slice[i .. j] = value; - } - - /// ditto - void opSliceUnary(string op)() - if (op == "++" || op == "--") - { - if (!_data.refCountedStore.isInitialized) return; - mixin(op~"_data._payload[];"); - } - - /// ditto - void opSliceUnary(string op)(size_t i, size_t j) - if (op == "++" || op == "--") - { - auto slice = _data.refCountedStore.isInitialized ? _data._payload : T[].init; - mixin(op~"slice[i .. j];"); - } - - /// ditto - void opSliceOpAssign(string op)(T value) - { - if (!_data.refCountedStore.isInitialized) return; - mixin("_data._payload[] "~op~"= value;"); - } - - /// ditto - void opSliceOpAssign(string op)(T value, size_t i, size_t j) - { - auto slice = _data.refCountedStore.isInitialized ? _data._payload : T[].init; - mixin("slice[i .. j] "~op~"= value;"); - } - - private enum hasSliceWithLength(T) = is(typeof({ T t = T.init; t[].length; })); - - /** - * Returns: A new array which is a concatenation of `this` and its argument. - * - * Complexity: - * $(BIGOH length + m), where `m` is the number of elements in `stuff`. - */ - Array opBinary(string op, Stuff)(Stuff stuff) - if (op == "~") - { - Array result; - - static if (hasLength!Stuff || isNarrowString!Stuff) - result.reserve(length + stuff.length); - else static if (hasSliceWithLength!Stuff) - result.reserve(length + stuff[].length); - else static if (isImplicitlyConvertible!(Stuff, T)) - result.reserve(length + 1); - - result.insertBack(this[]); - result ~= stuff; - return result; - } - - /** - * Forwards to `insertBack`. - */ - void opOpAssign(string op, Stuff)(auto ref Stuff stuff) - if (op == "~") - { - static if (is(typeof(stuff[])) && isImplicitlyConvertible!(typeof(stuff[0]), T)) - { - insertBack(stuff[]); - } - else - { - insertBack(stuff); - } - } - - /** - * Removes all the elements from the array and releases allocated memory. - * - * Postcondition: `empty == true && capacity == 0` - * - * Complexity: $(BIGOH length) - */ - void clear() - { - _data = Data.init; - } - - /** - * Sets the number of elements in the array to `newLength`. If `newLength` - * is greater than `length`, the new elements are added to the end of the - * array and initialized with `T.init`. If `T` is a `struct` whose default - * constructor is annotated with `@disable`, `newLength` must be lower than - * or equal to `length`. - * - * Complexity: - * Guaranteed $(BIGOH abs(length - newLength)) if `capacity >= newLength`. - * If `capacity < newLength` the worst case is $(BIGOH newLength). - * - * Precondition: `__traits(compiles, { static T _; }) || newLength <= length` - * - * Postcondition: `length == newLength` - */ - @property void length(size_t newLength) - { - _data.refCountedStore.ensureInitialized(); - _data.length = newLength; - } - - /** - * Removes the last element from the array and returns it. - * Both stable and non-stable versions behave the same and guarantee - * that ranges iterating over the array are never invalidated. - * - * Precondition: `empty == false` - * - * Returns: The element removed. - * - * Complexity: $(BIGOH 1). - * - * Throws: `Exception` if the array is empty. - */ - T removeAny() - { - auto result = back; - removeBack(); - return result; - } - - /// ditto - alias stableRemoveAny = removeAny; - - /** - * Inserts the specified elements at the back of the array. `stuff` can be - * a value convertible to `T` or a range of objects convertible to `T`. - * - * Returns: The number of elements inserted. - * - * Complexity: - * $(BIGOH length + m) if reallocation takes place, otherwise $(BIGOH m), - * where `m` is the number of elements in `stuff`. - */ - size_t insertBack(Stuff)(Stuff stuff) - if (isImplicitlyConvertible!(Stuff, T) || - isInputRange!Stuff && isImplicitlyConvertible!(ElementType!Stuff, T)) - { - _data.refCountedStore.ensureInitialized(); - return _data.insertBack(stuff); - } - - /// ditto - alias insert = insertBack; - - /** - * Removes the value from the back of the array. Both stable and non-stable - * versions behave the same and guarantee that ranges iterating over the - * array are never invalidated. - * - * Precondition: `empty == false` - * - * Complexity: $(BIGOH 1). - * - * Throws: `Exception` if the array is empty. - */ - void removeBack() - { - assert(!empty); - static if (hasElaborateDestructor!T) - .destroy(_data._payload[$ - 1]); - - _data._payload = _data._payload[0 .. $ - 1]; - } - - /// ditto - alias stableRemoveBack = removeBack; - - /** - * Removes `howMany` values from the back of the array. - * Unlike the unparameterized versions above, these functions - * do not throw if they could not remove `howMany` elements. Instead, - * if `howMany > n`, all elements are removed. The returned value is - * the effective number of elements removed. Both stable and non-stable - * versions behave the same and guarantee that ranges iterating over - * the array are never invalidated. - * - * Returns: The number of elements removed. - * - * Complexity: $(BIGOH howMany). - */ - size_t removeBack(size_t howMany) - { - if (howMany > length) howMany = length; - static if (hasElaborateDestructor!T) - foreach (ref e; _data._payload[$ - howMany .. $]) - .destroy(e); - - _data._payload = _data._payload[0 .. $ - howMany]; - return howMany; - } - - /// ditto - alias stableRemoveBack = removeBack; - - /** - * Inserts `stuff` before, after, or instead range `r`, which must - * be a valid range previously extracted from this array. `stuff` - * can be a value convertible to `T` or a range of objects convertible - * to `T`. Both stable and non-stable version behave the same and - * guarantee that ranges iterating over the array are never invalidated. - * - * Returns: The number of values inserted. - * - * Complexity: $(BIGOH length + m), where `m` is the length of `stuff`. - * - * Throws: `Exception` if `r` is not a range extracted from this array. - */ - size_t insertBefore(Stuff)(Range r, Stuff stuff) - if (isImplicitlyConvertible!(Stuff, T)) - { - import core.lifetime : emplace; - enforce(r._outer._data is _data && r._a <= length); - reserve(length + 1); - assert(_data.refCountedStore.isInitialized, - "Failed to allocate capacity to insertBefore"); - // Move elements over by one slot - memmove(_data._payload.ptr + r._a + 1, - _data._payload.ptr + r._a, - T.sizeof * (length - r._a)); - emplace(_data._payload.ptr + r._a, stuff); - _data._payload = _data._payload.ptr[0 .. _data._payload.length + 1]; - return 1; - } - - /// ditto - size_t insertBefore(Stuff)(Range r, Stuff stuff) - if (isInputRange!Stuff && isImplicitlyConvertible!(ElementType!Stuff, T)) - { - import core.lifetime : emplace; - enforce(r._outer._data is _data && r._a <= length); - static if (isForwardRange!Stuff) - { - // Can find the length in advance - auto extra = walkLength(stuff); - if (!extra) return 0; - reserve(length + extra); - assert(_data.refCountedStore.isInitialized, - "Failed to allocate capacity to insertBefore"); - // Move elements over by extra slots - memmove(_data._payload.ptr + r._a + extra, - _data._payload.ptr + r._a, - T.sizeof * (length - r._a)); - foreach (p; _data._payload.ptr + r._a .. - _data._payload.ptr + r._a + extra) - { - emplace(p, stuff.front); - stuff.popFront(); - } - _data._payload = - _data._payload.ptr[0 .. _data._payload.length + extra]; - return extra; - } - else - { - import std.algorithm.mutation : bringToFront; - enforce(_data); - immutable offset = r._a; - enforce(offset <= length); - auto result = insertBack(stuff); - bringToFront(this[offset .. length - result], - this[length - result .. length]); - return result; - } - } - - /// ditto - alias stableInsertBefore = insertBefore; - - /// ditto - size_t insertAfter(Stuff)(Range r, Stuff stuff) - if (isImplicitlyConvertible!(Stuff, T) || - isInputRange!Stuff && isImplicitlyConvertible!(ElementType!Stuff, T)) - { - import std.algorithm.mutation : bringToFront; - enforce(r._outer._data is _data); - // TODO: optimize - immutable offset = r._b; - enforce(offset <= length); - auto result = insertBack(stuff); - bringToFront(this[offset .. length - result], - this[length - result .. length]); - return result; - } - - /// ditto - size_t replace(Stuff)(Range r, Stuff stuff) - if (isInputRange!Stuff && isImplicitlyConvertible!(ElementType!Stuff, T)) - { - enforce(r._outer._data is _data); - size_t result; - for (; !stuff.empty; stuff.popFront()) - { - if (r.empty) - { - // insert the rest - return result + insertBefore(r, stuff); - } - r.front = stuff.front; - r.popFront(); - ++result; - } - // Remove remaining stuff in r - linearRemove(r); - return result; - } - - /// ditto - size_t replace(Stuff)(Range r, Stuff stuff) - if (isImplicitlyConvertible!(Stuff, T)) - { - enforce(r._outer._data is _data); - if (r.empty) - { - insertBefore(r, stuff); - } - else - { - r.front = stuff; - r.popFront(); - linearRemove(r); - } - return 1; - } - - /** - * Removes all elements belonging to `r`, which must be a range - * obtained originally from this array. - * - * Returns: A range spanning the remaining elements in the array that - * initially were right after `r`. - * - * Complexity: $(BIGOH length) - * - * Throws: `Exception` if `r` is not a valid range extracted from this array. - */ - Range linearRemove(Range r) - { - import std.algorithm.mutation : copy; - - enforce(r._outer._data is _data); - enforce(_data.refCountedStore.isInitialized); - enforce(r._a <= r._b && r._b <= length); - immutable offset1 = r._a; - immutable offset2 = r._b; - immutable tailLength = length - offset2; - // Use copy here, not a[] = b[] because the ranges may overlap - copy(this[offset2 .. length], this[offset1 .. offset1 + tailLength]); - length = offset1 + tailLength; - return this[length - tailLength .. length]; - } -} - -@system unittest -{ - Array!int a; - assert(a.empty); -} - -@system unittest -{ - Array!int a; - a.length = 10; - assert(a.length == 10); - assert(a.capacity >= a.length); -} - -@system unittest -{ - struct Dumb { int x = 5; } - Array!Dumb a; - a.length = 10; - assert(a.length == 10); - assert(a.capacity >= a.length); - immutable cap = a.capacity; - foreach (ref e; a) - e.x = 10; - a.length = 5; - assert(a.length == 5); - // do not realloc if length decreases - assert(a.capacity == cap); - foreach (ref e; a) - assert(e.x == 10); - - a.length = 8; - assert(a.length == 8); - // do not realloc if capacity sufficient - assert(a.capacity == cap); - assert(Dumb.init.x == 5); - foreach (i; 0 .. 5) - assert(a[i].x == 10); - foreach (i; 5 .. a.length) - assert(a[i].x == Dumb.init.x); - - // realloc required, check if values properly copied - a[] = Dumb(1); - a.length = 20; - assert(a.capacity >= 20); - foreach (i; 0 .. 8) - assert(a[i].x == 1); - foreach (i; 8 .. a.length) - assert(a[i].x == Dumb.init.x); - - // check if overlapping elements properly initialized - a.length = 1; - a.length = 20; - assert(a[0].x == 1); - foreach (e; a[1 .. $]) - assert(e.x == Dumb.init.x); -} - -@system unittest -{ - Array!int a = Array!int(1, 2, 3); - //a._data._refCountedDebug = true; - auto b = a.dup; - assert(b == Array!int(1, 2, 3)); - b.front = 42; - assert(b == Array!int(42, 2, 3)); - assert(a == Array!int(1, 2, 3)); -} - -@system unittest -{ - auto a = Array!int(1, 2, 3); - assert(a.length == 3); -} - -@system unittest -{ - const Array!int a = [1, 2]; - - assert(a[0] == 1); - assert(a.front == 1); - assert(a.back == 2); - - static assert(!__traits(compiles, { a[0] = 1; })); - static assert(!__traits(compiles, { a.front = 1; })); - static assert(!__traits(compiles, { a.back = 1; })); - - auto r = a[]; - size_t i; - foreach (e; r) - { - assert(e == i + 1); - i++; - } -} - -@system unittest -{ - import std.algorithm.comparison : equal; - auto a = Array!string("test"); - assert(a[].equal(["test"])); -} - -@safe unittest -{ - // https://issues.dlang.org/show_bug.cgi?id=13621 - import std.container : Array, BinaryHeap; - alias Heap = BinaryHeap!(Array!int); -} - -@system unittest -{ - // https://issues.dlang.org/show_bug.cgi?id=18800 - static struct S { void* p; } - Array!S a; - a.length = 10; -} - -@system unittest -{ - Array!int a; - a.reserve(1000); - assert(a.length == 0); - assert(a.empty); - assert(a.capacity >= 1000); - auto p = a._data._payload.ptr; - foreach (i; 0 .. 1000) - { - a.insertBack(i); - } - assert(p == a._data._payload.ptr); -} - -@system unittest -{ - auto a = Array!int(1, 2, 3); - a[1] *= 42; - assert(a[1] == 84); -} - -@system unittest -{ - auto a = Array!int(1, 2, 3); - auto b = Array!int(11, 12, 13); - auto c = a ~ b; - assert(c == Array!int(1, 2, 3, 11, 12, 13)); - assert(a ~ b[] == Array!int(1, 2, 3, 11, 12, 13)); - assert(a ~ [4,5] == Array!int(1,2,3,4,5)); -} - -@system unittest -{ - auto a = Array!int(1, 2, 3); - auto b = Array!int(11, 12, 13); - a ~= b; - assert(a == Array!int(1, 2, 3, 11, 12, 13)); -} - -@system unittest -{ - auto a = Array!int(1, 2, 3, 4); - assert(a.removeAny() == 4); - assert(a == Array!int(1, 2, 3)); -} - -@system unittest -{ - auto a = Array!int(1, 2, 3, 4, 5); - auto r = a[2 .. a.length]; - assert(a.insertBefore(r, 42) == 1); - assert(a == Array!int(1, 2, 42, 3, 4, 5)); - r = a[2 .. 2]; - assert(a.insertBefore(r, [8, 9]) == 2); - assert(a == Array!int(1, 2, 8, 9, 42, 3, 4, 5)); -} - -@system unittest -{ - auto a = Array!int(0, 1, 2, 3, 4, 5, 6, 7, 8); - a.linearRemove(a[4 .. 6]); - assert(a == Array!int(0, 1, 2, 3, 6, 7, 8)); -} - -// Give the Range object some testing. -@system unittest -{ - import std.algorithm.comparison : equal; - import std.range : retro; - auto a = Array!int(0, 1, 2, 3, 4, 5, 6)[]; - auto b = Array!int(6, 5, 4, 3, 2, 1, 0)[]; - alias A = typeof(a); - - static assert(isRandomAccessRange!A); - static assert(hasSlicing!A); - static assert(hasAssignableElements!A); - static assert(hasMobileElements!A); - - assert(equal(retro(b), a)); - assert(a.length == 7); - assert(equal(a[1 .. 4], [1, 2, 3])); -} - -// https://issues.dlang.org/show_bug.cgi?id=5920 -@system unittest -{ - struct structBug5920 - { - int order; - uint* pDestructionMask; - ~this() - { - if (pDestructionMask) - *pDestructionMask += 1 << order; - } - } - - alias S = structBug5920; - uint dMask; - - auto arr = Array!S(cast(S[])[]); - foreach (i; 0 .. 8) - arr.insertBack(S(i, &dMask)); - // don't check dMask now as S may be copied multiple times (it's ok?) - { - assert(arr.length == 8); - dMask = 0; - arr.length = 6; - assert(arr.length == 6); // make sure shrinking calls the d'tor - assert(dMask == 0b1100_0000); - arr.removeBack(); - assert(arr.length == 5); // make sure removeBack() calls the d'tor - assert(dMask == 0b1110_0000); - arr.removeBack(3); - assert(arr.length == 2); // ditto - assert(dMask == 0b1111_1100); - arr.clear(); - assert(arr.length == 0); // make sure clear() calls the d'tor - assert(dMask == 0b1111_1111); - } - assert(dMask == 0b1111_1111); // make sure the d'tor is called once only. -} - -// Test for https://issues.dlang.org/show_bug.cgi?id=5792 -// (mainly just to check if this piece of code is compilable) -@system unittest -{ - auto a = Array!(int[])([[1,2],[3,4]]); - a.reserve(4); - assert(a.capacity >= 4); - assert(a.length == 2); - assert(a[0] == [1,2]); - assert(a[1] == [3,4]); - a.reserve(16); - assert(a.capacity >= 16); - assert(a.length == 2); - assert(a[0] == [1,2]); - assert(a[1] == [3,4]); -} - -// test replace!Stuff with range Stuff -@system unittest -{ - import std.algorithm.comparison : equal; - auto a = Array!int([1, 42, 5]); - a.replace(a[1 .. 2], [2, 3, 4]); - assert(equal(a[], [1, 2, 3, 4, 5])); -} - -// test insertBefore and replace with empty Arrays -@system unittest -{ - import std.algorithm.comparison : equal; - auto a = Array!int(); - a.insertBefore(a[], 1); - assert(equal(a[], [1])); -} -@system unittest -{ - import std.algorithm.comparison : equal; - auto a = Array!int(); - a.insertBefore(a[], [1, 2]); - assert(equal(a[], [1, 2])); -} -@system unittest -{ - import std.algorithm.comparison : equal; - auto a = Array!int(); - a.replace(a[], [1, 2]); - assert(equal(a[], [1, 2])); -} -@system unittest -{ - import std.algorithm.comparison : equal; - auto a = Array!int(); - a.replace(a[], 1); - assert(equal(a[], [1])); -} -// make sure that Array instances refuse ranges that don't belong to them -@system unittest -{ - import std.exception : assertThrown; - - Array!int a = [1, 2, 3]; - auto r = a.dup[]; - assertThrown(a.insertBefore(r, 42)); - assertThrown(a.insertBefore(r, [42])); - assertThrown(a.insertAfter(r, 42)); - assertThrown(a.replace(r, 42)); - assertThrown(a.replace(r, [42])); - assertThrown(a.linearRemove(r)); -} -@system unittest -{ - auto a = Array!int([1, 1]); - a[1] = 0; //Check Array.opIndexAssign - assert(a[1] == 0); - a[1] += 1; //Check Array.opIndexOpAssign - assert(a[1] == 1); - - //Check Array.opIndexUnary - ++a[0]; - //a[0]++ //op++ doesn't return, so this shouldn't work, even with 5044 fixed - assert(a[0] == 2); - assert(+a[0] == +2); - assert(-a[0] == -2); - assert(~a[0] == ~2); - - auto r = a[]; - r[1] = 0; //Check Array.Range.opIndexAssign - assert(r[1] == 0); - r[1] += 1; //Check Array.Range.opIndexOpAssign - assert(r[1] == 1); - - //Check Array.Range.opIndexUnary - ++r[0]; - //r[0]++ //op++ doesn't return, so this shouldn't work, even with 5044 fixed - assert(r[0] == 3); - assert(+r[0] == +3); - assert(-r[0] == -3); - assert(~r[0] == ~3); -} - -@system unittest -{ - import std.algorithm.comparison : equal; - - //Test "array-wide" operations - auto a = Array!int([0, 1, 2]); //Array - a[] += 5; - assert(a[].equal([5, 6, 7])); - ++a[]; - assert(a[].equal([6, 7, 8])); - a[1 .. 3] *= 5; - assert(a[].equal([6, 35, 40])); - a[0 .. 2] = 0; - assert(a[].equal([0, 0, 40])); - - //Test empty array - auto a2 = Array!int.init; - ++a2[]; - ++a2[0 .. 0]; - a2[] = 0; - a2[0 .. 0] = 0; - a2[] += 0; - a2[0 .. 0] += 0; - - //Test "range-wide" operations - auto r = Array!int([0, 1, 2])[]; //Array.Range - r[] += 5; - assert(r.equal([5, 6, 7])); - ++r[]; - assert(r.equal([6, 7, 8])); - r[1 .. 3] *= 5; - assert(r.equal([6, 35, 40])); - r[0 .. 2] = 0; - assert(r.equal([0, 0, 40])); - - //Test empty Range - auto r2 = Array!int.init[]; - ++r2[]; - ++r2[0 .. 0]; - r2[] = 0; - r2[0 .. 0] = 0; - r2[] += 0; - r2[0 .. 0] += 0; -} - -// Test https://issues.dlang.org/show_bug.cgi?id=11194 -@system unittest -{ - static struct S { - int i = 1337; - void* p; - this(this) { assert(i == 1337); } - ~this() { assert(i == 1337); } - } - Array!S arr; - S s; - arr ~= s; - arr ~= s; -} - -// https://issues.dlang.org/show_bug.cgi?id=11459 -@safe unittest -{ - static struct S - { - bool b; - alias b this; - } - alias A = Array!S; - alias B = Array!(shared bool); -} - -// https://issues.dlang.org/show_bug.cgi?id=11884 -@system unittest -{ - import std.algorithm.iteration : filter; - auto a = Array!int([1, 2, 2].filter!"true"()); -} - -// https://issues.dlang.org/show_bug.cgi?id=8282 -@safe unittest -{ - auto arr = new Array!int; -} - -// https://issues.dlang.org/show_bug.cgi?id=6998 -@system unittest -{ - static int i = 0; - class C - { - int dummy = 1; - this(){++i;} - ~this(){--i;} - } - - assert(i == 0); - auto c = new C(); - assert(i == 1); - - //scope - { - auto arr = Array!C(c); - assert(i == 1); - } - //Array should not have destroyed the class instance - assert(i == 1); - - //Just to make sure the GC doesn't collect before the above test. - assert(c.dummy == 1); -} - -//https://issues.dlang.org/show_bug.cgi?id=6998 (2) -@system unittest -{ - static class C {int i;} - auto c = new C; - c.i = 42; - Array!C a; - a ~= c; - a.clear; - assert(c.i == 42); //fails -} - -@safe unittest -{ - static assert(is(Array!int.Range)); - static assert(is(Array!int.ConstRange)); -} - -@system unittest // const/immutable Array and Ranges -{ - static void test(A, R, E, S)() - { - A a; - R r = a[]; - assert(r.empty); - assert(r.length == 0); - static assert(is(typeof(r.front) == E)); - static assert(is(typeof(r.back) == E)); - static assert(is(typeof(r[0]) == E)); - static assert(is(typeof(r[]) == S)); - static assert(is(typeof(r[0 .. 0]) == S)); - } - - alias A = Array!int; - - test!(A, A.Range, int, A.Range); - test!(A, const A.Range, const int, A.ConstRange); - - test!(const A, A.ConstRange, const int, A.ConstRange); - test!(const A, const A.ConstRange, const int, A.ConstRange); - - test!(immutable A, A.ImmutableRange, immutable int, A.ImmutableRange); - test!(immutable A, const A.ImmutableRange, immutable int, A.ImmutableRange); - test!(immutable A, immutable A.ImmutableRange, immutable int, - A.ImmutableRange); -} - -// ensure @nogc -@nogc @system unittest -{ - Array!int ai; - ai ~= 1; - assert(ai.front == 1); - - ai.reserve(10); - assert(ai.capacity == 10); - - static immutable arr = [1, 2, 3]; - ai.insertBack(arr); -} - -/* - * typeof may give wrong result in case of classes defining `opCall` operator - * https://issues.dlang.org/show_bug.cgi?id=20589 - * - * destructor std.container.array.Array!(MyClass).Array.~this is @system - * so the unittest is @system too - */ -@system unittest -{ - class MyClass - { - T opCall(T)(T p) - { - return p; - } - } - - Array!MyClass arr; -} - -@system unittest -{ - import std.algorithm.comparison : equal; - auto a = Array!int([1,2,3,4,5]); - assert(a.length == 5); - - assert(a.insertAfter(a[0 .. 5], [7, 8]) == 2); - assert(equal(a[], [1,2,3,4,5,7,8])); - - assert(a.insertAfter(a[0 .. 5], 6) == 1); - assert(equal(a[], [1,2,3,4,5,6,7,8])); -} - -// https://issues.dlang.org/show_bug.cgi?id=22105 -@system unittest -{ - import core.exception : AssertError; - import std.exception : assertThrown, assertNotThrown; - - struct NoDefaultCtor - { - int i; - @disable this(); - this(int j) { i = j; } - } - - auto array = Array!NoDefaultCtor([NoDefaultCtor(1), NoDefaultCtor(2)]); - assertNotThrown!AssertError(array.length = 1); - assertThrown!AssertError(array.length = 5); -} - -// https://issues.dlang.org/show_bug.cgi?id=23140 -@system unittest -{ - shared class C - { - } - - Array!C ac; - ac = Array!C([new C]); -} -//////////////////////////////////////////////////////////////////////////////// -// Array!bool -//////////////////////////////////////////////////////////////////////////////// - -/** - * _Array specialized for `bool`. Packs together values efficiently by - * allocating one bit per element. - */ -struct Array(T) -if (is(immutable T == immutable bool)) -{ - import std.exception : enforce; - import std.typecons : RefCounted, RefCountedAutoInitialize; - - static immutable uint bitsPerWord = size_t.sizeof * 8; - private static struct Data - { - Array!size_t.Payload _backend; - size_t _length; - } - private RefCounted!(Data, RefCountedAutoInitialize.no) _store; - - private @property ref size_t[] data() - { - assert(_store.refCountedStore.isInitialized, - "Cannot get data of uninitialized Array"); - return _store._backend._payload; - } - - /** - * Defines the array's primary range. - */ - struct Range - { - private Array _outer; - private size_t _a, _b; - /// Range primitives - @property Range save() - { - version (bug4437) - { - return this; - } - else - { - auto copy = this; - return copy; - } - } - /// Ditto - @property bool empty() - { - return _a >= _b || _outer.length < _b; - } - /// Ditto - @property T front() - { - assert(!empty, "Attempting to access the front of an empty Array"); - return _outer[_a]; - } - /// Ditto - @property void front(bool value) - { - assert(!empty, "Attempting to set the front of an empty Array"); - _outer[_a] = value; - } - /// Ditto - T moveFront() - { - assert(!empty, "Attempting to move the front of an empty Array"); - return _outer.moveAt(_a); - } - /// Ditto - void popFront() - { - assert(!empty, "Attempting to popFront an empty Array"); - ++_a; - } - /// Ditto - @property T back() - { - assert(!empty, "Attempting to access the back of an empty Array"); - return _outer[_b - 1]; - } - /// Ditto - @property void back(bool value) - { - assert(!empty, "Attempting to set the back of an empty Array"); - _outer[_b - 1] = value; - } - /// Ditto - T moveBack() - { - assert(!empty, "Attempting to move the back of an empty Array"); - return _outer.moveAt(_b - 1); - } - /// Ditto - void popBack() - { - assert(!empty, "Attempting to popBack an empty Array"); - --_b; - } - /// Ditto - T opIndex(size_t i) - { - return _outer[_a + i]; - } - /// Ditto - void opIndexAssign(T value, size_t i) - { - _outer[_a + i] = value; - } - /// Ditto - T moveAt(size_t i) - { - return _outer.moveAt(_a + i); - } - /// Ditto - @property size_t length() const - { - assert(_a <= _b, "Invalid bounds"); - return _b - _a; - } - alias opDollar = length; - /// ditto - Range opSlice(size_t low, size_t high) - { - // Note: indexes start at 0, which is equivalent to _a - assert( - low <= high && high <= (_b - _a), - "Using out of bounds indexes on an Array" - ); - return Range(_outer, _a + low, _a + high); - } - } - - /** - * Constructor taking a number of items. - */ - this(U)(U[] values...) - if (isImplicitlyConvertible!(U, T)) - { - reserve(values.length); - foreach (i, v; values) - { - auto rem = i % bitsPerWord; - if (rem) - { - // Fits within the current array - if (v) - { - data[$ - 1] |= (cast(size_t) 1 << rem); - } - else - { - data[$ - 1] &= ~(cast(size_t) 1 << rem); - } - } - else - { - // Need to add more data - _store._backend.insertBack(v); - } - } - _store._length = values.length; - } - - /** - * Constructor taking an $(REF_ALTTEXT input range, isInputRange, std,range,primitives) - */ - this(Range)(Range r) - if (isInputRange!Range && isImplicitlyConvertible!(ElementType!Range, T) && !is(Range == T[])) - { - insertBack(r); - } - - /** - * Property returning `true` if and only if the array has - * no elements. - * - * Complexity: $(BIGOH 1) - */ - @property bool empty() - { - return !length; - } - - /** - * Returns: A duplicate of the array. - * - * Complexity: $(BIGOH length). - */ - @property Array dup() - { - Array result; - result.insertBack(this[]); - return result; - } - - /** - * Returns the number of elements in the array. - * - * Complexity: $(BIGOH 1). - */ - @property size_t length() const - { - return _store.refCountedStore.isInitialized ? _store._length : 0; - } - alias opDollar = length; - - /** - * Returns: The maximum number of elements the array can store without - * reallocating memory and invalidating iterators upon insertion. - * - * Complexity: $(BIGOH 1). - */ - @property size_t capacity() - { - return _store.refCountedStore.isInitialized - ? cast(size_t) bitsPerWord * _store._backend.capacity - : 0; - } - - /** - * Ensures sufficient capacity to accommodate `e` _elements. - * If `e < capacity`, this method does nothing. - * - * Postcondition: `capacity >= e` - * - * Note: If the capacity is increased, one should assume that all - * iterators to the elements are invalidated. - * - * Complexity: at most $(BIGOH length) if `e > capacity`, otherwise $(BIGOH 1). - */ - void reserve(size_t e) - { - import std.conv : to; - _store.refCountedStore.ensureInitialized(); - _store._backend.reserve(to!size_t((e + bitsPerWord - 1) / bitsPerWord)); - } - - /** - * Returns: A range that iterates over all elements of the array in forward order. - * - * Complexity: $(BIGOH 1) - */ - Range opSlice() - { - return Range(this, 0, length); - } - - - /** - * Returns: A range that iterates the array between two specified positions. - * - * Complexity: $(BIGOH 1) - */ - Range opSlice(size_t a, size_t b) - { - enforce(a <= b && b <= length); - return Range(this, a, b); - } - - /** - * Returns: The first element of the array. - * - * Precondition: `empty == false` - * - * Complexity: $(BIGOH 1) - * - * Throws: `Exception` if the array is empty. - */ - @property bool front() - { - assert(!empty); - return data.ptr[0] & 1; - } - - /// Ditto - @property void front(bool value) - { - assert(!empty); - if (value) data.ptr[0] |= 1; - else data.ptr[0] &= ~cast(size_t) 1; - } - - /** - * Returns: The last element of the array. - * - * Precondition: `empty == false` - * - * Complexity: $(BIGOH 1) - * - * Throws: `Exception` if the array is empty. - */ - @property bool back() - { - assert(!empty); - return cast(bool)(data.back & (cast(size_t) 1 << ((_store._length - 1) % bitsPerWord))); - } - - /// Ditto - @property void back(bool value) - { - assert(!empty); - if (value) - { - data.back |= (cast(size_t) 1 << ((_store._length - 1) % bitsPerWord)); - } - else - { - data.back &= - ~(cast(size_t) 1 << ((_store._length - 1) % bitsPerWord)); - } - } - - /** - * Indexing operators yielding or modifyng the value at the specified index. - * - * Precondition: `i < length` - * - * Complexity: $(BIGOH 1) - */ - bool opIndex(size_t i) - { - auto div = cast(size_t) (i / bitsPerWord); - auto rem = i % bitsPerWord; - enforce(div < data.length); - return cast(bool)(data.ptr[div] & (cast(size_t) 1 << rem)); - } - - /// ditto - void opIndexAssign(bool value, size_t i) - { - auto div = cast(size_t) (i / bitsPerWord); - auto rem = i % bitsPerWord; - enforce(div < data.length); - if (value) data.ptr[div] |= (cast(size_t) 1 << rem); - else data.ptr[div] &= ~(cast(size_t) 1 << rem); - } - - /// ditto - void opIndexOpAssign(string op)(bool value, size_t i) - { - auto div = cast(size_t) (i / bitsPerWord); - auto rem = i % bitsPerWord; - enforce(div < data.length); - auto oldValue = cast(bool) (data.ptr[div] & (cast(size_t) 1 << rem)); - // Do the deed - auto newValue = mixin("oldValue "~op~" value"); - // Write back the value - if (newValue != oldValue) - { - if (newValue) data.ptr[div] |= (cast(size_t) 1 << rem); - else data.ptr[div] &= ~(cast(size_t) 1 << rem); - } - } - - /// Ditto - T moveAt(size_t i) - { - return this[i]; - } - - /** - * Returns: A new array which is a concatenation of `this` and its argument. - * - * Complexity: - * $(BIGOH length + m), where `m` is the number of elements in `stuff`. - */ - Array!bool opBinary(string op, Stuff)(Stuff rhs) - if (op == "~") - { - Array!bool result; - - static if (hasLength!Stuff) - result.reserve(length + rhs.length); - else static if (is(typeof(rhs[])) && hasLength!(typeof(rhs[]))) - result.reserve(length + rhs[].length); - else static if (isImplicitlyConvertible!(Stuff, bool)) - result.reserve(length + 1); - - result.insertBack(this[]); - result ~= rhs; - return result; - } - - /** - * Forwards to `insertBack`. - */ - Array!bool opOpAssign(string op, Stuff)(Stuff stuff) - if (op == "~") - { - static if (is(typeof(stuff[]))) insertBack(stuff[]); - else insertBack(stuff); - return this; - } - - /** - * Removes all the elements from the array and releases allocated memory. - * - * Postcondition: `empty == true && capacity == 0` - * - * Complexity: $(BIGOH length) - */ - void clear() - { - this = Array(); - } - - /** - * Sets the number of elements in the array to `newLength`. If `newLength` - * is greater than `length`, the new elements are added to the end of the - * array and initialized with `false`. - * - * Complexity: - * Guaranteed $(BIGOH abs(length - newLength)) if `capacity >= newLength`. - * If `capacity < newLength` the worst case is $(BIGOH newLength). - * - * Postcondition: `length == newLength` - */ - @property void length(size_t newLength) - { - import std.conv : to; - _store.refCountedStore.ensureInitialized(); - auto newDataLength = - to!size_t((newLength + bitsPerWord - 1) / bitsPerWord); - _store._backend.length = newDataLength; - _store._length = newLength; - } - - /** - * Removes the last element from the array and returns it. - * Both stable and non-stable versions behave the same and guarantee - * that ranges iterating over the array are never invalidated. - * - * Precondition: `empty == false` - * - * Returns: The element removed. - * - * Complexity: $(BIGOH 1). - * - * Throws: `Exception` if the array is empty. - */ - T removeAny() - { - auto result = back; - removeBack(); - return result; - } - - /// ditto - alias stableRemoveAny = removeAny; - - /** - * Inserts the specified elements at the back of the array. `stuff` can be - * a value convertible to `bool` or a range of objects convertible to `bool`. - * - * Returns: The number of elements inserted. - * - * Complexity: - * $(BIGOH length + m) if reallocation takes place, otherwise $(BIGOH m), - * where `m` is the number of elements in `stuff`. - */ - size_t insertBack(Stuff)(Stuff stuff) - if (is(Stuff : bool)) - { - _store.refCountedStore.ensureInitialized(); - auto rem = _store._length % bitsPerWord; - if (rem) - { - // Fits within the current array - if (stuff) - { - data[$ - 1] |= (cast(size_t) 1 << rem); - } - else - { - data[$ - 1] &= ~(cast(size_t) 1 << rem); - } - } - else - { - // Need to add more data - _store._backend.insertBack(stuff); - } - ++_store._length; - return 1; - } - - /// ditto - size_t insertBack(Stuff)(Stuff stuff) - if (isInputRange!Stuff && is(ElementType!Stuff : bool)) - { - size_t result; - static if (hasLength!Stuff) - result = stuff.length; - - for (; !stuff.empty; stuff.popFront()) - { - insertBack(stuff.front); - static if (!hasLength!Stuff) ++result; - } - - return result; - } - - /// ditto - alias stableInsertBack = insertBack; - - /// ditto - alias insert = insertBack; - - /// ditto - alias stableInsert = insertBack; - - /// ditto - alias linearInsert = insertBack; - - /// ditto - alias stableLinearInsert = insertBack; - - /** - * Removes the value from the back of the array. Both stable and non-stable - * versions behave the same and guarantee that ranges iterating over the - * array are never invalidated. - * - * Precondition: `empty == false` - * - * Complexity: $(BIGOH 1). - * - * Throws: `Exception` if the array is empty. - */ - void removeBack() - { - enforce(_store._length); - if (_store._length % bitsPerWord) - { - // Cool, just decrease the length - --_store._length; - } - else - { - // Reduce the allocated space - --_store._length; - _store._backend.length = _store._backend.length - 1; - } - } - - /// ditto - alias stableRemoveBack = removeBack; - - /** - * Removes `howMany` values from the back of the array. Unlike the - * unparameterized versions above, these functions do not throw if - * they could not remove `howMany` elements. Instead, if `howMany > n`, - * all elements are removed. The returned value is the effective number - * of elements removed. Both stable and non-stable versions behave the same - * and guarantee that ranges iterating over the array are never invalidated. - * - * Returns: The number of elements removed. - * - * Complexity: $(BIGOH howMany). - */ - size_t removeBack(size_t howMany) - { - if (howMany >= length) - { - howMany = length; - clear(); - } - else - { - length = length - howMany; - } - return howMany; - } - - /// ditto - alias stableRemoveBack = removeBack; - - /** - * Inserts `stuff` before, after, or instead range `r`, which must - * be a valid range previously extracted from this array. `stuff` - * can be a value convertible to `bool` or a range of objects convertible - * to `bool`. Both stable and non-stable version behave the same and - * guarantee that ranges iterating over the array are never invalidated. - * - * Returns: The number of values inserted. - * - * Complexity: $(BIGOH length + m), where `m` is the length of `stuff`. - */ - size_t insertBefore(Stuff)(Range r, Stuff stuff) - { - import std.algorithm.mutation : bringToFront; - // TODO: make this faster, it moves one bit at a time - immutable inserted = stableInsertBack(stuff); - immutable tailLength = length - inserted; - bringToFront( - this[r._a .. tailLength], - this[tailLength .. length]); - return inserted; - } - - /// ditto - alias stableInsertBefore = insertBefore; - - /// ditto - size_t insertAfter(Stuff)(Range r, Stuff stuff) - if (isImplicitlyConvertible!(Stuff, T) || - isInputRange!Stuff && isImplicitlyConvertible!(ElementType!Stuff, T)) - { - import std.algorithm.mutation : bringToFront; - // TODO: make this faster, it moves one bit at a time - immutable inserted = stableInsertBack(stuff); - immutable tailLength = length - inserted; - bringToFront( - this[r._b .. tailLength], - this[tailLength .. length]); - return inserted; - } - - /// ditto - alias stableInsertAfter = insertAfter; - - /// ditto - size_t replace(Stuff)(Range r, Stuff stuff) - if (is(Stuff : bool)) - { - if (!r.empty) - { - // There is room - r.front = stuff; - r.popFront(); - linearRemove(r); - } - else - { - // No room, must insert - insertBefore(r, stuff); - } - return 1; - } - - /// ditto - alias stableReplace = replace; - - /** - * Removes all elements belonging to `r`, which must be a range - * obtained originally from this array. - * - * Returns: A range spanning the remaining elements in the array that - * initially were right after `r`. - * - * Complexity: $(BIGOH length) - */ - Range linearRemove(Range r) - { - import std.algorithm.mutation : copy; - copy(this[r._b .. length], this[r._a .. length]); - length = length - r.length; - return this[r._a .. length]; - } -} - -@system unittest -{ - import std.algorithm.comparison : equal; - - auto a = Array!bool([true, true, false, false, true, false]); - assert(equal(a[], [true, true, false, false, true, false])); -} - -// using Ranges -@system unittest -{ - import std.algorithm.comparison : equal; - import std.range : retro; - bool[] arr = [true, true, false, false, true, false]; - - auto a = Array!bool(retro(arr)); - assert(equal(a[], retro(arr))); -} - -@system unittest -{ - Array!bool a; - assert(a.empty); -} - -@system unittest -{ - auto arr = Array!bool([false, false, false, false]); - assert(arr.front == false); - assert(arr.back == false); - assert(arr[1] == false); - auto slice = arr[]; - slice = arr[0 .. $]; - slice = slice[1 .. $]; - slice.front = true; - slice.back = true; - slice[1] = true; - slice = slice[0 .. $]; // https://issues.dlang.org/show_bug.cgi?id=19171 - assert(slice.front == true); - assert(slice.back == true); - assert(slice[1] == true); - assert(slice.moveFront == true); - assert(slice.moveBack == true); - assert(slice.moveAt(1) == true); -} - -// uncomparable values are valid values for an array -// https://issues.dlang.org/show_bug.cgi?id=16331 -@system unittest -{ - double[] values = [double.nan, double.nan]; - auto arr = Array!double(values); -} - -@nogc @system unittest -{ - auto a = Array!int(0, 1, 2); - int[3] b = [3, 4, 5]; - short[3] ci = [0, 1, 0]; - auto c = Array!short(ci); - assert(Array!int(0, 1, 2, 0, 1, 2) == a ~ a); - assert(Array!int(0, 1, 2, 3, 4, 5) == a ~ b); - assert(Array!int(0, 1, 2, 3) == a ~ 3); - assert(Array!int(0, 1, 2, 0, 1, 0) == a ~ c); -} - -@nogc @system unittest -{ - auto a = Array!char('a', 'b'); - assert(Array!char("abc") == a ~ 'c'); - import std.utf : byCodeUnit; - assert(Array!char("abcd") == a ~ "cd".byCodeUnit); -} - -@nogc @system unittest -{ - auto a = Array!dchar("ąćę"d); - assert(Array!dchar("ąćęϢϖ"d) == a ~ "Ϣϖ"d); - wchar x = 'Ϣ'; - assert(Array!dchar("ąćęϢz"d) == a ~ x ~ 'z'); -} - -@system unittest -{ - Array!bool a; - assert(a.empty); - a.insertBack(false); - assert(!a.empty); -} - -@system unittest -{ - Array!bool a; - assert(a.empty); - auto b = a.dup; - assert(b.empty); - a.insertBack(true); - assert(b.empty); -} - -@system unittest -{ - import std.conv : to; - Array!bool a; - assert(a.length == 0); - a.insert(true); - assert(a.length == 1, to!string(a.length)); -} - -@system unittest -{ - import std.conv : to; - Array!bool a; - assert(a.capacity == 0); - foreach (i; 0 .. 100) - { - a.insert(true); - assert(a.capacity >= a.length, to!string(a.capacity)); - } -} - -@system unittest -{ - Array!bool a; - assert(a.capacity == 0); - a.reserve(15657); - assert(a.capacity >= 15657); - a.reserve(100); - assert(a.capacity >= 15657); -} - -@system unittest -{ - auto a = Array!bool([true, false, true, true]); - assert(a[0 .. 2].length == 2); -} - -@system unittest -{ - auto a = Array!bool([true, false, true, true]); - assert(a[].length == 4); -} - -@system unittest -{ - auto a = Array!bool([true, false, true, true]); - assert(a.front); - a.front = false; - assert(!a.front); -} - -@system unittest -{ - auto a = Array!bool([true, false, true, true]); - assert(a[].length == 4); -} - -@system unittest -{ - auto a = Array!bool([true, false, true, true]); - assert(a.back); - a.back = false; - assert(!a.back); -} - -@system unittest -{ - auto a = Array!bool([true, false, true, true]); - assert(a[0] && !a[1]); - a[0] &= a[1]; - assert(!a[0]); -} - -@system unittest -{ - import std.algorithm.comparison : equal; - auto a = Array!bool([true, false, true, true]); - auto b = Array!bool([true, true, false, true]); - assert(equal((a ~ b)[], - [true, false, true, true, true, true, false, true])); - assert((a ~ [true, false])[].equal([true, false, true, true, true, false])); - Array!bool c; - c.insertBack(true); - assert((c ~ false)[].equal([true, false])); -} -@system unittest -{ - import std.algorithm.comparison : equal; - auto a = Array!bool([true, false, true, true]); - auto b = Array!bool([false, true, false, true, true]); - a ~= b; - assert(equal( - a[], - [true, false, true, true, false, true, false, true, true])); -} - -@system unittest -{ - auto a = Array!bool([true, false, true, true]); - a.clear(); - assert(a.capacity == 0); -} - -@system unittest -{ - Array!bool a; - a.length = 1057; - assert(a.length == 1057); - assert(a.capacity >= a.length); - foreach (e; a) - { - assert(!e); - } - immutable cap = a.capacity; - a.length = 100; - assert(a.length == 100); - // do not realloc if length decreases - assert(a.capacity == cap); -} - -@system unittest -{ - Array!bool a; - a.length = 1057; - assert(!a.removeAny()); - assert(a.length == 1056); - foreach (e; a) - { - assert(!e); - } -} - -@system unittest -{ - Array!bool a; - for (int i = 0; i < 100; ++i) - a.insertBack(true); - foreach (e; a) - assert(e); -} - -@system unittest -{ - Array!bool a; - a.length = 1057; - assert(a.removeBack(1000) == 1000); - assert(a.length == 57); - foreach (e; a) - { - assert(!e); - } -} - -@system unittest -{ - import std.conv : to; - Array!bool a; - version (bugxxxx) - { - a._store.refCountedDebug = true; - } - a.insertBefore(a[], true); - assert(a.length == 1, to!string(a.length)); - a.insertBefore(a[], false); - assert(a.length == 2, to!string(a.length)); - a.insertBefore(a[1 .. $], true); - import std.algorithm.comparison : equal; - assert(a[].equal([false, true, true])); -} - -// https://issues.dlang.org/show_bug.cgi?id=21555 -@system unittest -{ - import std.algorithm.comparison : equal; - Array!bool arr; - size_t len = arr.insertBack([false, true]); - assert(len == 2); -} - -// https://issues.dlang.org/show_bug.cgi?id=21556 -@system unittest -{ - import std.algorithm.comparison : equal; - Array!bool a; - a.insertBack([true, true, false, false, true]); - assert(a.length == 5); - - assert(a.insertAfter(a[0 .. 5], [false, false]) == 2); - assert(equal(a[], [true, true, false, false, true, false, false])); - - assert(a.insertAfter(a[0 .. 5], true) == 1); - assert(equal(a[], [true, true, false, false, true, true, false, false])); -} - -@system unittest -{ - import std.conv : to; - Array!bool a; - a.length = 10; - a.insertAfter(a[0 .. 5], true); - assert(a.length == 11, to!string(a.length)); - assert(a[5]); -} -@system unittest -{ - alias V3 = int[3]; - V3 v = [1, 2, 3]; - Array!V3 arr; - arr ~= v; - assert(arr[0] == [1, 2, 3]); -} -@system unittest -{ - alias V3 = int[3]; - V3[2] v = [[1, 2, 3], [4, 5, 6]]; - Array!V3 arr; - arr ~= v; - assert(arr[0] == [1, 2, 3]); - assert(arr[1] == [4, 5, 6]); -} - -// Change of length reallocates without calling GC. -// https://issues.dlang.org/show_bug.cgi?id=13642 -@system unittest -{ - import core.memory; - class ABC { void func() { int x = 5; } } - - Array!ABC arr; - // Length only allocates if capacity is too low. - arr.reserve(4); - assert(arr.capacity == 4); - - void func() @nogc - { - arr.length = 5; - } - func(); - - foreach (ref b; arr) b = new ABC; - GC.collect(); - arr[1].func(); -} - -@system unittest -{ - - Array!int arr = [1, 2, 4, 5]; - int[] data = arr.data(); - - const Array!int arr2 = [8, 9]; - assert(arr2.data() == [8, 9]); - - data[0] = 0; - assert(arr[0] == 0); - - arr.length = 0; - assert(arr.data == []); - - Array!int empty; - assert(empty.data == []); -} diff --git a/phobos/std/container/binaryheap.d b/phobos/std/container/binaryheap.d deleted file mode 100644 index 0fd3452..0000000 --- a/phobos/std/container/binaryheap.d +++ /dev/null @@ -1,639 +0,0 @@ -/** -This module provides a `BinaryHeap` (aka priority queue) -adaptor that makes a binary heap out of any user-provided random-access range. - -This module is a submodule of $(MREF std, container). - -Source: $(PHOBOSSRC std/container/binaryheap.d) - -Copyright: 2010- Andrei Alexandrescu. All rights reserved by the respective holders. - -License: Distributed under the Boost Software License, Version 1.0. -(See accompanying file LICENSE_1_0.txt or copy at $(HTTP -boost.org/LICENSE_1_0.txt)). - -Authors: $(HTTP erdani.com, Andrei Alexandrescu) -*/ -module std.container.binaryheap; - -import std.range.primitives; -import std.traits; - -public import std.container.util; - -/// -@system unittest -{ - import std.algorithm.comparison : equal; - import std.range : take; - auto maxHeap = heapify([4, 7, 3, 1, 5]); - assert(maxHeap.take(3).equal([7, 5, 4])); - - auto minHeap = heapify!"a > b"([4, 7, 3, 1, 5]); - assert(minHeap.take(3).equal([1, 3, 4])); -} - -// BinaryHeap -/** -Implements a $(HTTP en.wikipedia.org/wiki/Binary_heap, binary heap) -container on top of a given random-access range type (usually $(D -T[])) or a random-access container type (usually `Array!T`). The -documentation of `BinaryHeap` will refer to the underlying range or -container as the $(I store) of the heap. - -The binary heap induces structure over the underlying store such that -accessing the largest element (by using the `front` property) is a -$(BIGOH 1) operation and extracting it (by using the $(D -removeFront()) method) is done fast in $(BIGOH log n) time. - -If `less` is the less-than operator, which is the default option, -then `BinaryHeap` defines a so-called max-heap that optimizes -extraction of the $(I largest) elements. To define a min-heap, -instantiate BinaryHeap with $(D "a > b") as its predicate. - -Simply extracting elements from a `BinaryHeap` container is -tantamount to lazily fetching elements of `Store` in descending -order. Extracting elements from the `BinaryHeap` to completion -leaves the underlying store sorted in ascending order but, again, -yields elements in descending order. - -If `Store` is a range, the `BinaryHeap` cannot grow beyond the -size of that range. If `Store` is a container that supports $(D -insertBack), the `BinaryHeap` may grow by adding elements to the -container. - */ -struct BinaryHeap(Store, alias less = "a < b") -if (isRandomAccessRange!(Store) || isRandomAccessRange!(typeof(Store.init[]))) -{ - import std.algorithm.comparison : min; - import std.algorithm.mutation : move, swapAt; - import std.algorithm.sorting : HeapOps; - import std.exception : enforce; - import std.functional : binaryFun; - import std.typecons : RefCounted, RefCountedAutoInitialize; - - static if (isRandomAccessRange!Store) - alias Range = Store; - else - alias Range = typeof(Store.init[]); - alias percolate = HeapOps!(less, Range).percolate; - alias buildHeap = HeapOps!(less, Range).buildHeap; - -// Really weird @@BUG@@: if you comment out the "private:" label below, -// std.algorithm can't unittest anymore -//private: - - // The payload includes the support store and the effective length - private static struct Data - { - Store _store; - size_t _length; - } - // TODO: migrate to use the SafeRefCounted. The problem is that some member - // functions here become @system with a naive switch. - private RefCounted!(Data, RefCountedAutoInitialize.no) _payload; - // Comparison predicate - private alias comp = binaryFun!(less); - // Convenience accessors - private @property ref Store _store() - { - assert(_payload.refCountedStore.isInitialized, - "BinaryHeap not initialized"); - return _payload._store; - } - private @property ref size_t _length() - { - assert(_payload.refCountedStore.isInitialized, - "BinaryHeap not initialized"); - return _payload._length; - } - - // Asserts that the heap property is respected. - private void assertValid() - { - debug - { - import std.conv : to; - if (!_payload.refCountedStore.isInitialized) return; - if (_length < 2) return; - for (size_t n = _length - 1; n >= 1; --n) - { - auto parentIdx = (n - 1) / 2; - assert(!comp(_store[parentIdx], _store[n]), to!string(n)); - } - } - } - - // @@@BUG@@@: add private here, std.algorithm doesn't unittest anymore - /*private*/ void pop(Store store) - { - assert(!store.empty, "Cannot pop an empty store."); - if (store.length == 1) return; - auto t1 = store[].moveFront(); - auto t2 = store[].moveBack(); - store.front = move(t2); - store.back = move(t1); - percolate(store[], 0, store.length - 1); - } - -public: - - /** - Converts the store `s` into a heap. If `initialSize` is - specified, only the first `initialSize` elements in `s` - are transformed into a heap, after which the heap can grow up - to `r.length` (if `Store` is a range) or indefinitely (if - `Store` is a container with `insertBack`). Performs - $(BIGOH min(r.length, initialSize)) evaluations of `less`. - */ - this(Store s, size_t initialSize = size_t.max) - { - acquire(s, initialSize); - } - -/** -Takes ownership of a store. After this, manipulating `s` may make -the heap work incorrectly. - */ - void acquire(Store s, size_t initialSize = size_t.max) - { - _payload.refCountedStore.ensureInitialized(); - _store = move(s); - _length = min(_store.length, initialSize); - if (_length < 2) return; - buildHeap(_store[]); - assertValid(); - } - -/** -Takes ownership of a store assuming it already was organized as a -heap. - */ - void assume(Store s, size_t initialSize = size_t.max) - { - _payload.refCountedStore.ensureInitialized(); - _store = s; - _length = min(_store.length, initialSize); - assertValid(); - } - -/** -Clears the heap. Returns the portion of the store from `0` up to -`length`, which satisfies the $(LINK2 https://en.wikipedia.org/wiki/Heap_(data_structure), -heap property). - */ - auto release() - { - if (!_payload.refCountedStore.isInitialized) - { - return typeof(_store[0 .. _length]).init; - } - assertValid(); - auto result = _store[0 .. _length]; - _payload = _payload.init; - return result; - } - -/** -Returns `true` if the heap is _empty, `false` otherwise. - */ - @property bool empty() - { - return !length; - } - -/** -Returns a duplicate of the heap. The `dup` method is available only if the -underlying store supports it. - */ - static if (is(typeof((Store s) { return s.dup; }(Store.init)) == Store)) - { - @property BinaryHeap dup() - { - BinaryHeap result; - if (!_payload.refCountedStore.isInitialized) return result; - result.assume(_store.dup, length); - return result; - } - } - -/** -Returns the _length of the heap. - */ - @property size_t length() - { - return _payload.refCountedStore.isInitialized ? _length : 0; - } - -/** -Returns the _capacity of the heap, which is the length of the -underlying store (if the store is a range) or the _capacity of the -underlying store (if the store is a container). - */ - @property size_t capacity() - { - if (!_payload.refCountedStore.isInitialized) return 0; - static if (is(typeof(_store.capacity) : size_t)) - { - return _store.capacity; - } - else - { - return _store.length; - } - } - -/** -Returns a copy of the _front of the heap, which is the largest element -according to `less`. - */ - @property ElementType!Store front() - { - assert(!empty, "Cannot call front on an empty heap."); - return _store.front; - } - -/** -Clears the heap by detaching it from the underlying store. - */ - void clear() - { - _payload = _payload.init; - } - -/** -Inserts `value` into the store. If the underlying store is a range -and $(D length == capacity), throws an exception. - */ - size_t insert(ElementType!Store value) - { - static if (is(typeof(_store.insertBack(value)))) - { - _payload.refCountedStore.ensureInitialized(); - if (length == _store.length) - { - // reallocate - _store.insertBack(value); - } - else - { - // no reallocation - _store[_length] = value; - } - } - else - { - import std.traits : isDynamicArray; - static if (isDynamicArray!Store) - { - if (length == _store.length) - _store.length = (length < 6 ? 8 : length * 3 / 2); - _store[_length] = value; - } - else - { - // can't grow - enforce(length < _store.length, - "Cannot grow a heap created over a range"); - } - } - - // sink down the element - for (size_t n = _length; n; ) - { - auto parentIdx = (n - 1) / 2; - if (!comp(_store[parentIdx], _store[n])) break; // done! - // must swap and continue - _store.swapAt(parentIdx, n); - n = parentIdx; - } - ++_length; - debug(BinaryHeap) assertValid(); - return 1; - } - -/** -Removes the largest element from the heap. - */ - void removeFront() - { - assert(!empty, "Cannot call removeFront on an empty heap."); - if (_length > 1) - { - auto t1 = _store[].moveFront(); - auto t2 = _store[].moveAt(_length - 1); - _store.front = move(t2); - _store[_length - 1] = move(t1); - } - --_length; - percolate(_store[], 0, _length); - } - - /// ditto - alias popFront = removeFront; - -/** -Removes the largest element from the heap and returns a copy of -it. The element still resides in the heap's store. For performance -reasons you may want to use `removeFront` with heaps of objects -that are expensive to copy. - */ - ElementType!Store removeAny() - { - removeFront(); - return _store[_length]; - } - -/** -Replaces the largest element in the store with `value`. - */ - void replaceFront(ElementType!Store value) - { - // must replace the top - assert(!empty, "Cannot call replaceFront on an empty heap."); - _store.front = value; - percolate(_store[], 0, _length); - debug(BinaryHeap) assertValid(); - } - -/** -If the heap has room to grow, inserts `value` into the store and -returns `true`. Otherwise, if $(D less(value, front)), calls $(D -replaceFront(value)) and returns again `true`. Otherwise, leaves -the heap unaffected and returns `false`. This method is useful in -scenarios where the smallest `k` elements of a set of candidates -must be collected. - */ - bool conditionalInsert(ElementType!Store value) - { - _payload.refCountedStore.ensureInitialized(); - if (_length < _store.length) - { - insert(value); - return true; - } - - assert(!_store.empty, "Cannot replace front of an empty heap."); - if (!comp(value, _store.front)) return false; // value >= largest - _store.front = value; - - percolate(_store[], 0, _length); - debug(BinaryHeap) assertValid(); - return true; - } - -/** -Swapping is allowed if the heap is full. If $(D less(value, front)), the -method exchanges store.front and value and returns `true`. Otherwise, it -leaves the heap unaffected and returns `false`. - */ - bool conditionalSwap(ref ElementType!Store value) - { - _payload.refCountedStore.ensureInitialized(); - assert(_length == _store.length, - "length and number of stored items out of sync"); - assert(!_store.empty, "Cannot swap front of an empty heap."); - - if (!comp(value, _store.front)) return false; // value >= largest - - import std.algorithm.mutation : swap; - swap(_store.front, value); - - percolate(_store[], 0, _length); - debug(BinaryHeap) assertValid(); - - return true; - } -} - -/// Example from "Introduction to Algorithms" Cormen et al, p 146 -@system unittest -{ - import std.algorithm.comparison : equal; - int[] a = [ 4, 1, 3, 2, 16, 9, 10, 14, 8, 7 ]; - auto h = heapify(a); - // largest element - assert(h.front == 16); - // a has the heap property - assert(equal(a, [ 16, 14, 10, 8, 7, 9, 3, 2, 4, 1 ])); -} - -/// `BinaryHeap` implements the standard input range interface, allowing -/// lazy iteration of the underlying range in descending order. -@system unittest -{ - import std.algorithm.comparison : equal; - import std.range : take; - int[] a = [4, 1, 3, 2, 16, 9, 10, 14, 8, 7]; - auto top5 = heapify(a).take(5); - assert(top5.equal([16, 14, 10, 9, 8])); -} - -/** -Convenience function that returns a `BinaryHeap!Store` object -initialized with `s` and `initialSize`. - */ -BinaryHeap!(Store, less) heapify(alias less = "a < b", Store)(Store s, - size_t initialSize = size_t.max) -{ - - return BinaryHeap!(Store, less)(s, initialSize); -} - -/// -@system unittest -{ - import std.conv : to; - import std.range.primitives; - { - // example from "Introduction to Algorithms" Cormen et al., p 146 - int[] a = [ 4, 1, 3, 2, 16, 9, 10, 14, 8, 7 ]; - auto h = heapify(a); - h = heapify!"a < b"(a); - assert(h.front == 16); - assert(a == [ 16, 14, 10, 8, 7, 9, 3, 2, 4, 1 ]); - auto witness = [ 16, 14, 10, 9, 8, 7, 4, 3, 2, 1 ]; - for (; !h.empty; h.removeFront(), witness.popFront()) - { - assert(!witness.empty); - assert(witness.front == h.front); - } - assert(witness.empty); - } - { - int[] a = [ 4, 1, 3, 2, 16, 9, 10, 14, 8, 7 ]; - int[] b = new int[a.length]; - BinaryHeap!(int[]) h = BinaryHeap!(int[])(b, 0); - foreach (e; a) - { - h.insert(e); - } - assert(b == [ 16, 14, 10, 8, 7, 3, 9, 1, 4, 2 ], to!string(b)); - } -} - -@system unittest -{ - // Test range interface. - import std.algorithm.comparison : equal; - int[] a = [4, 1, 3, 2, 16, 9, 10, 14, 8, 7]; - auto h = heapify(a); - static assert(isInputRange!(typeof(h))); - assert(h.equal([16, 14, 10, 9, 8, 7, 4, 3, 2, 1])); -} - -// https://issues.dlang.org/show_bug.cgi?id=15675 -@system unittest -{ - import std.container.array : Array; - - Array!int elements = [1, 2, 10, 12]; - auto heap = heapify(elements); - assert(heap.front == 12); -} - -// https://issues.dlang.org/show_bug.cgi?id=16072 -@system unittest -{ - auto q = heapify!"a > b"([2, 4, 5]); - q.insert(1); - q.insert(6); - assert(q.front == 1); - - // test more multiple grows - int[] arr; - auto r = heapify!"a < b"(arr); - foreach (i; 0 .. 100) - r.insert(i); - - assert(r.front == 99); -} - -@system unittest -{ - import std.algorithm.comparison : equal; - int[] a = [4, 1, 3, 2, 16, 9, 10, 14, 8, 7]; - auto heap = heapify(a); - auto dup = heap.dup(); - assert(dup.equal([16, 14, 10, 9, 8, 7, 4, 3, 2, 1])); -} - -@safe unittest -{ - static struct StructWithoutDup - { - int[] a; - @disable StructWithoutDup dup(); - alias a this; - } - - // Assert Binary heap can be created when Store doesn't have dup - // if dup is not used. - assert(__traits(compiles, () - { - auto s = StructWithoutDup([1,2]); - auto h = heapify(s); - })); - - // Assert dup can't be used on BinaryHeaps when Store doesn't have dup - assert(!__traits(compiles, () - { - auto s = StructWithoutDup([1,2]); - auto h = heapify(s); - h.dup(); - })); -} - -@safe unittest -{ - static struct StructWithDup - { - int[] a; - StructWithDup dup() - { - StructWithDup d; - return d; - } - alias a this; - } - - // Assert dup can be used on BinaryHeaps when Store has dup - assert(__traits(compiles, () - { - auto s = StructWithDup([1, 2]); - auto h = heapify(s); - h.dup(); - })); -} - -@system unittest -{ - import std.algorithm.comparison : equal; - import std.internal.test.dummyrange; - - alias RefRange = DummyRange!(ReturnBy.Reference, Length.Yes, RangeType.Random); - - RefRange a; - RefRange b; - a.reinit(); - b.reinit(); - - auto heap = heapify(a); - foreach (ref elem; b) - { - heap.conditionalSwap(elem); - } - - assert(equal(heap, [ 5, 5, 4, 4, 3, 3, 2, 2, 1, 1])); - assert(equal(b, [10, 9, 8, 7, 6, 6, 7, 8, 9, 10])); -} - -// https://issues.dlang.org/show_bug.cgi?id=17314 -@system unittest -{ - import std.algorithm.comparison : equal; - int[] a = [5]; - auto heap = heapify(a); - heap.insert(6); - assert(equal(heap, [6, 5])); -} - -/** -Example for unintuitive behaviour -It is important not to use the Store after a Heap has been instantiated from -it, at least in the cases of Dynamic Arrays. For example, inserting a new element -in a Heap, which is using a Dyamic Array as a Store, will cause a reallocation of -the Store, if the Store is already full. The Heap will not point anymore to the -original Dyamic Array, but point to a new Dynamic Array. - */ - -// https://issues.dlang.org/show_bug.cgi?id=18333 -@system unittest -{ - import std.stdio; - import std.algorithm.comparison : equal; - import std.container.binaryheap; - - int[] a = [ 4, 1, 3, 2, 16, 9, 10, 14, 8, 7 ]; - auto h = heapify(a); - - // Internal representation of Binary Heap tree - assert(a.equal([16, 14, 10, 8, 7, 9, 3, 2, 4, 1])); - - h.replaceFront(30); - // Value 16 was replaced by 30 - assert(a.equal([30, 14, 10, 8, 7, 9, 3, 2, 4, 1])); - - // Making changes to the Store will be seen in the Heap - a[0] = 40; - assert(h.front() == 40); - - // Inserting a new element will reallocate the Store, leaving - // the original Store unchanged. - h.insert(20); - assert(a.equal([40, 14, 10, 8, 7, 9, 3, 2, 4, 1])); - - // Making changes to the original Store will not affect the Heap anymore - a[0] = 60; - assert(h.front() == 40); -} diff --git a/phobos/std/container/dlist.d b/phobos/std/container/dlist.d deleted file mode 100644 index 4fdf13d..0000000 --- a/phobos/std/container/dlist.d +++ /dev/null @@ -1,1150 +0,0 @@ -/** -This module implements a generic doubly-linked list container. -It can be used as a queue, dequeue or stack. - -This module is a submodule of $(MREF std, container). - -Source: $(PHOBOSSRC std/container/dlist.d) - -Copyright: 2010- Andrei Alexandrescu. All rights reserved by the respective holders. - -License: Distributed under the Boost Software License, Version 1.0. -(See accompanying file LICENSE_1_0.txt or copy at $(HTTP -boost.org/LICENSE_1_0.txt)). - -Authors: $(HTTP erdani.com, Andrei Alexandrescu) - -$(SCRIPT inhibitQuickIndex = 1;) -*/ -module std.container.dlist; - -/// -@safe unittest -{ - import std.algorithm.comparison : equal; - import std.container : DList; - - auto s = DList!int(1, 2, 3); - assert(equal(s[], [1, 2, 3])); - - s.removeFront(); - assert(equal(s[], [2, 3])); - s.removeBack(); - assert(equal(s[], [2])); - - s.insertFront([4, 5]); - assert(equal(s[], [4, 5, 2])); - s.insertBack([6, 7]); - assert(equal(s[], [4, 5, 2, 6, 7])); - - // If you want to apply range operations, simply slice it. - import std.algorithm.searching : countUntil; - import std.range : popFrontN, popBackN, walkLength; - - auto sl = DList!int([1, 2, 3, 4, 5]); - assert(countUntil(sl[], 2) == 1); - - auto r = sl[]; - popFrontN(r, 2); - popBackN(r, 2); - assert(r.equal([3])); - assert(walkLength(r) == 1); - - // DList.Range can be used to remove elements from the list it spans - auto nl = DList!int([1, 2, 3, 4, 5]); - for (auto rn = nl[]; !rn.empty;) - if (rn.front % 2 == 0) - nl.popFirstOf(rn); - else - rn.popFront(); - assert(equal(nl[], [1, 3, 5])); - auto rs = nl[]; - rs.popFront(); - nl.remove(rs); - assert(equal(nl[], [1])); -} - -import std.range.primitives; -import std.traits; - -public import std.container.util; - -/+ -A DList Node without payload. Used to handle the sentinel node (henceforth "sentinode"). - -Also used for parts of the code that don't depend on the payload type. - +/ -private struct BaseNode -{ - private BaseNode* _prev = null; - private BaseNode* _next = null; - - /+ - Gets the payload associated with this node. - This is trusted because all nodes are associated with a payload, even - the sentinel node. - It is also not possible to mix Nodes in DLists of different types. - This function is implemented as a member function here, as UFCS does not - work with pointers. - +/ - ref inout(T) getPayload(T)() inout @trusted - { - return (cast(inout(DList!T.PayNode)*)&this)._payload; - } - - // Helper: Given nodes p and n, connects them. - static void connect(BaseNode* p, BaseNode* n) @safe nothrow pure - { - p._next = n; - n._prev = p; - } -} - -/+ -The base DList Range. Contains Range primitives that don't depend on payload type. - +/ -private struct DRange -{ - @safe unittest - { - static assert(isBidirectionalRange!DRange); - static assert(is(ElementType!DRange == BaseNode*)); - } - -nothrow @safe @nogc pure: - private BaseNode* _first; - private BaseNode* _last; - - private this(BaseNode* first, BaseNode* last) - { - assert((first is null) == (last is null), "Dlist.Range.this: Invalid arguments"); - _first = first; - _last = last; - } - private this(BaseNode* n) - { - this(n, n); - } - - @property - bool empty() const scope - { - assert((_first is null) == (_last is null), "DList.Range: Invalidated state"); - return !_first; - } - - @property BaseNode* front() return scope - { - assert(!empty, "DList.Range.front: Range is empty"); - return _first; - } - - void popFront() scope - { - assert(!empty, "DList.Range.popFront: Range is empty"); - if (_first is _last) - { - _first = _last = null; - } - else - { - assert(_first._next && _first is _first._next._prev, "DList.Range: Invalidated state"); - _first = _first._next; - } - } - - @property BaseNode* back() return scope - { - assert(!empty, "DList.Range.front: Range is empty"); - return _last; - } - - void popBack() scope - { - assert(!empty, "DList.Range.popBack: Range is empty"); - if (_first is _last) - { - _first = _last = null; - } - else - { - assert(_last._prev && _last is _last._prev._next, "DList.Range: Invalidated state"); - _last = _last._prev; - } - } - - /// Forward range primitive. - @property DRange save() return scope { return this; } -} - -/** -Implements a doubly-linked list. - -`DList` uses reference semantics. - */ -struct DList(T) -{ - import std.range : Take; - - /* - A Node with a Payload. A PayNode. - */ - struct PayNode - { - BaseNode _base; - alias _base this; - - T _payload = T.init; - - this (BaseNode _base, T _payload) - { - import std.algorithm.mutation : move; - - this._base = _base; - this._payload = move(_payload); - } - - inout(BaseNode)* asBaseNode() inout @trusted - { - return &_base; - } - } - - //The sentinel node - private BaseNode* _root; - - private - { - //Construct as new PayNode, and returns it as a BaseNode. - static BaseNode* createNode(Stuff)(auto ref Stuff arg, BaseNode* prev = null, BaseNode* next = null) - { - import std.algorithm.mutation : move; - - return (new PayNode(BaseNode(prev, next), move(arg))).asBaseNode(); - } - - void initialize() nothrow @safe pure - { - if (_root) return; - //Note: We allocate a PayNode for safety reasons. - _root = (new PayNode()).asBaseNode(); - _root._next = _root._prev = _root; - } - ref inout(BaseNode*) _first() @property @safe nothrow pure inout - { - assert(_root, "Root pointer must not be null"); - return _root._next; - } - ref inout(BaseNode*) _last() @property @safe nothrow pure inout - { - assert(_root, "Root pointer must not be null"); - return _root._prev; - } - } //end private - -/** -Constructor taking a number of nodes - */ - this(U)(U[] values...) if (isImplicitlyConvertible!(U, T)) - { - insertBack(values); - } - -/** -Constructor taking an $(REF_ALTTEXT input range, isInputRange, std,range,primitives) - */ - this(Stuff)(Stuff stuff) - if (isInputRange!Stuff && isImplicitlyConvertible!(ElementType!Stuff, T)) - { - insertBack(stuff); - } - -/** -Comparison for equality. - -Complexity: $(BIGOH min(n, n1)) where `n1` is the number of -elements in `rhs`. - */ - bool opEquals()(ref const DList rhs) const - if (is(typeof(front == front))) - { - const lhs = this; - const lroot = lhs._root; - const rroot = rhs._root; - - if (lroot is rroot) return true; - if (lroot is null) return rroot is rroot._next; - if (rroot is null) return lroot is lroot._next; - - const(BaseNode)* pl = lhs._first; - const(BaseNode)* pr = rhs._first; - while (true) - { - if (pl is lroot) return pr is rroot; - if (pr is rroot) return false; - - // !== because of NaN - if (!(pl.getPayload!T() == pr.getPayload!T())) return false; - - pl = pl._next; - pr = pr._next; - } - } - - /** - Defines the container's primary range, which embodies a bidirectional range. - */ - struct Range - { - static assert(isBidirectionalRange!Range); - - DRange _base; - alias _base this; - - private this(BaseNode* first, BaseNode* last) - { - _base = DRange(first, last); - } - private this(BaseNode* n) - { - this(n, n); - } - - @property ref T front() - { - return _base.front.getPayload!T(); - } - - @property ref T back() - { - return _base.back.getPayload!T(); - } - - //Note: shadows base DRange.save. - //Necessary for static covariance. - @property Range save() { return this; } - } - -/** -Property returning `true` if and only if the container has no -elements. - -Complexity: $(BIGOH 1) - */ - bool empty() @property const nothrow - { - return _root is null || _root is _first; - } - -/** -Removes all contents from the `DList`. - -Postcondition: `empty` - -Complexity: $(BIGOH 1) - */ - void clear() - { - //remove actual elements. - remove(this[]); - } - -/** -Duplicates the container. The elements themselves are not transitively -duplicated. - -Complexity: $(BIGOH n). - */ - @property DList dup() - { - return DList(this[]); - } - -/** -Returns a range that iterates over all elements of the container, in -forward order. - -Complexity: $(BIGOH 1) - */ - Range opSlice() - { - if (empty) - return Range(null, null); - else - return Range(_first, _last); - } - -/** -Forward to `opSlice().front`. - -Complexity: $(BIGOH 1) - */ - @property ref inout(T) front() inout - { - assert(!empty, "DList.front: List is empty"); - return _first.getPayload!T(); - } - -/** -Forward to `opSlice().back`. - -Complexity: $(BIGOH 1) - */ - @property ref inout(T) back() inout - { - assert(!empty, "DList.back: List is empty"); - return _last.getPayload!T(); - } - -/+ ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ +/ -/+ BEGIN CONCAT FUNCTIONS HERE +/ -/+ ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ +/ - -/** -Returns a new `DList` that's the concatenation of `this` and its -argument `rhs`. - */ - DList opBinary(string op, Stuff)(Stuff rhs) - if (op == "~" && is(typeof(insertBack(rhs)))) - { - auto ret = this.dup; - ret.insertBack(rhs); - return ret; - } - -/** -Returns a new `DList` that's the concatenation of the argument `lhs` -and `this`. - */ - DList opBinaryRight(string op, Stuff)(Stuff lhs) - if (op == "~" && is(typeof(insertFront(lhs)))) - { - auto ret = this.dup; - ret.insertFront(lhs); - return ret; - } - -/** -Appends the contents of the argument `rhs` into `this`. - */ - DList opOpAssign(string op, Stuff)(Stuff rhs) - if (op == "~" && is(typeof(insertBack(rhs)))) - { - insertBack(rhs); - return this; - } - -/+ ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ +/ -/+ BEGIN INSERT FUNCTIONS HERE +/ -/+ ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ +/ - -/** -Inserts `stuff` to the front/back of the container. `stuff` can be a -value convertible to `T` or a range of objects convertible to $(D -T). The stable version behaves the same, but guarantees that ranges -iterating over the container are never invalidated. - -Returns: The number of elements inserted - -Complexity: $(BIGOH log(n)) - */ - size_t insertFront(Stuff)(Stuff stuff) - { - initialize(); - return insertAfterNode(_root, stuff); - } - - /// ditto - size_t insertBack(Stuff)(Stuff stuff) - { - initialize(); - return insertBeforeNode(_root, stuff); - } - - /// ditto - alias insert = insertBack; - - /// ditto - alias stableInsert = insert; - - /// ditto - alias stableInsertFront = insertFront; - - /// ditto - alias stableInsertBack = insertBack; - -/** -Inserts `stuff` after range `r`, which must be a non-empty range -previously extracted from this container. - -`stuff` can be a value convertible to `T` or a range of objects -convertible to `T`. The stable version behaves the same, but -guarantees that ranges iterating over the container are never -invalidated. - -Returns: The number of values inserted. - -Complexity: $(BIGOH k + m), where `k` is the number of elements in -`r` and `m` is the length of `stuff`. - */ - size_t insertBefore(Stuff)(Range r, Stuff stuff) - { - if (r._first) - return insertBeforeNode(r._first, stuff); - else - { - initialize(); - return insertAfterNode(_root, stuff); - } - } - - /// ditto - alias stableInsertBefore = insertBefore; - - /// ditto - size_t insertAfter(Stuff)(Range r, Stuff stuff) - { - if (r._last) - return insertAfterNode(r._last, stuff); - else - { - initialize(); - return insertBeforeNode(_root, stuff); - } - } - - /// ditto - alias stableInsertAfter = insertAfter; - -/+ ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ +/ -/+ BEGIN REMOVE FUNCTIONS HERE +/ -/+ ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ +/ - -/** -Picks one value in an unspecified position in the container, removes -it from the container, and returns it. The stable version behaves the same, -but guarantees that ranges iterating over the container are never invalidated. - -Precondition: `!empty` - -Returns: The element removed. - -Complexity: $(BIGOH 1). - */ - T removeAny() - { - import std.algorithm.mutation : move; - - assert(!empty, "DList.removeAny: List is empty"); - auto result = move(back); - removeBack(); - return result; - } - /// ditto - alias stableRemoveAny = removeAny; - -/** -Removes the value at the front/back of the container. The stable version -behaves the same, but guarantees that ranges iterating over the -container are never invalidated. - -Precondition: `!empty` - -Complexity: $(BIGOH 1). - */ - void removeFront() - { - assert(!empty, "DList.removeFront: List is empty"); - assert(_root is _first._prev, "DList: Inconsistent state"); - BaseNode.connect(_root, _first._next); - } - - /// ditto - alias stableRemoveFront = removeFront; - - /// ditto - void removeBack() - { - assert(!empty, "DList.removeBack: List is empty"); - assert(_last._next is _root, "DList: Inconsistent state"); - BaseNode.connect(_last._prev, _root); - } - - /// ditto - alias stableRemoveBack = removeBack; - -/** -Removes `howMany` values at the front or back of the -container. Unlike the unparameterized versions above, these functions -do not throw if they could not remove `howMany` elements. Instead, -if $(D howMany > n), all elements are removed. The returned value is -the effective number of elements removed. The stable version behaves -the same, but guarantees that ranges iterating over the container are -never invalidated. - -Returns: The number of elements removed - -Complexity: $(BIGOH howMany). - */ - size_t removeFront(size_t howMany) - { - if (!_root) return 0; - size_t result; - auto p = _first; - while (p !is _root && result < howMany) - { - p = p._next; - ++result; - } - BaseNode.connect(_root, p); - return result; - } - - /// ditto - alias stableRemoveFront = removeFront; - - /// ditto - size_t removeBack(size_t howMany) - { - if (!_root) return 0; - size_t result; - auto p = _last; - while (p !is _root && result < howMany) - { - p = p._prev; - ++result; - } - BaseNode.connect(p, _root); - return result; - } - - /// ditto - alias stableRemoveBack = removeBack; - -/** -Removes all elements belonging to `r`, which must be a range -obtained originally from this container. - -Returns: A range spanning the remaining elements in the container that -initially were right after `r`. - -Complexity: $(BIGOH 1) - */ - Range remove(Range r) - { - if (r.empty) - return r; - - assert(_root !is null, "Cannot remove from an un-initialized List"); - assert(r._first, "Remove: Range is empty"); - - BaseNode.connect(r._first._prev, r._last._next); - auto after = r._last._next; - if (after is _root) - return Range(null, null); - else - return Range(after, _last); - } - - /// ditto - Range linearRemove(Range r) - { - return remove(r); - } - - /// ditto - alias stableRemove = remove; - -/** -Removes first element of `r`, wich must be a range obtained originally -from this container, from both DList instance and range `r`. - -Compexity: $(BIGOH 1) - */ - void popFirstOf(ref Range r) - { - assert(_root !is null, "Cannot remove from an un-initialized List"); - assert(r._first, "popFirstOf: Range is empty"); - auto prev = r._first._prev; - auto next = r._first._next; - r.popFront(); - BaseNode.connect(prev, next); - } - -/** -Removes last element of `r`, wich must be a range obtained originally -from this container, from both DList instance and range `r`. - -Compexity: $(BIGOH 1) - */ - void popLastOf(ref Range r) - { - assert(_root !is null, "Cannot remove from an un-initialized List"); - assert(r._first, "popLastOf: Range is empty"); - auto prev = r._last._prev; - auto next = r._last._next; - r.popBack(); - BaseNode.connect(prev, next); - } - -/** -`linearRemove` functions as `remove`, but also accepts ranges that are -result the of a `take` operation. This is a convenient way to remove a -fixed amount of elements from the range. - -Complexity: $(BIGOH r.walkLength) - */ - Range linearRemove(Take!Range r) - { - assert(_root !is null, "Cannot remove from an un-initialized List"); - assert(r.source._first, "Remove: Range is empty"); - - BaseNode* first = r.source._first; - BaseNode* last = null; - do - { - last = r.source._first; - r.popFront(); - } while ( !r.empty ); - - return remove(Range(first, last)); - } - - /// ditto - alias stableLinearRemove = linearRemove; - -/** -Removes the first occurence of an element from the list in linear time. - -Returns: True if the element existed and was successfully removed, false otherwise. - -Params: - value = value of the node to be removed - -Complexity: $(BIGOH n) - */ - bool linearRemoveElement(T value) - { - import std.algorithm.mutation : move; - - auto n1 = findNodeByValue(_root, move(value)); - if (n1) - { - auto n2 = n1._next._next; - BaseNode.connect(n1, n2); - return true; - } - - return false; - } - - -private: - - BaseNode* findNodeByValue(BaseNode* n, T value) - { - if (!n) return null; - auto ahead = n._next; - while (ahead && ahead.getPayload!T() != value) - { - n = ahead; - ahead = n._next; - if (ahead == _last._next) return null; - } - return n; - } - - // Helper: Inserts stuff before the node n. - size_t insertBeforeNode(Stuff)(BaseNode* n, ref Stuff stuff) - if (isImplicitlyConvertible!(Stuff, T)) - { - auto p = createNode(stuff, n._prev, n); - n._prev._next = p; - n._prev = p; - return 1; - } - // ditto - size_t insertBeforeNode(Stuff)(BaseNode* n, ref Stuff stuff) - if (isInputRange!Stuff && isImplicitlyConvertible!(ElementType!Stuff, T)) - { - if (stuff.empty) return 0; - size_t result; - Range r = createRange(stuff, result); - BaseNode.connect(n._prev, r._first); - BaseNode.connect(r._last, n); - return result; - } - - // Helper: Inserts stuff after the node n. - size_t insertAfterNode(Stuff)(BaseNode* n, ref Stuff stuff) - if (isImplicitlyConvertible!(Stuff, T)) - { - auto p = createNode(stuff, n, n._next); - n._next._prev = p; - n._next = p; - return 1; - } - // ditto - size_t insertAfterNode(Stuff)(BaseNode* n, ref Stuff stuff) - if (isInputRange!Stuff && isImplicitlyConvertible!(ElementType!Stuff, T)) - { - if (stuff.empty) return 0; - size_t result; - Range r = createRange(stuff, result); - BaseNode.connect(r._last, n._next); - BaseNode.connect(n, r._first); - return result; - } - - // Helper: Creates a chain of nodes from the range stuff. - Range createRange(Stuff)(ref Stuff stuff, ref size_t result) - { - BaseNode* first = createNode(stuff.front); - BaseNode* last = first; - ++result; - for ( stuff.popFront() ; !stuff.empty ; stuff.popFront() ) - { - auto p = createNode(stuff.front, last); - last = last._next = p; - ++result; - } - return Range(first, last); - } -} - -@safe unittest -{ - import std.algorithm.comparison : equal; - - auto e = DList!int(); - auto b = e.linearRemoveElement(1); - assert(b == false); - assert(e.empty()); - auto a = DList!int(-1, 1, 2, 1, 3, 4); - b = a.linearRemoveElement(1); - assert(equal(a[], [-1, 2, 1, 3, 4])); - assert(b == true); - b = a.linearRemoveElement(-1); - assert(b == true); - assert(equal(a[], [2, 1, 3, 4])); - b = a.linearRemoveElement(1); - assert(b == true); - assert(equal(a[], [2, 3, 4])); - b = a.linearRemoveElement(2); - assert(b == true); - b = a.linearRemoveElement(20); - assert(b == false); - assert(equal(a[], [3, 4])); - b = a.linearRemoveElement(4); - assert(b == true); - assert(equal(a[], [3])); - b = a.linearRemoveElement(3); - assert(b == true); - assert(a.empty()); - a.linearRemoveElement(3); -} - -@safe unittest -{ - import std.algorithm.comparison : equal; - - //Tests construction signatures - alias IntList = DList!int; - auto a0 = IntList(); - auto a1 = IntList(0); - auto a2 = IntList(0, 1); - auto a3 = IntList([0]); - auto a4 = IntList([0, 1]); - - assert(a0[].empty); - assert(equal(a1[], [0])); - assert(equal(a2[], [0, 1])); - assert(equal(a3[], [0])); - assert(equal(a4[], [0, 1])); -} - -@safe unittest -{ - import std.algorithm.comparison : equal; - - alias IntList = DList!int; - IntList list = IntList([0,1,2,3]); - assert(equal(list[],[0,1,2,3])); - list.insertBack([4,5,6,7]); - assert(equal(list[],[0,1,2,3,4,5,6,7])); - - list = IntList(); - list.insertFront([0,1,2,3]); - assert(equal(list[],[0,1,2,3])); - list.insertFront([4,5,6,7]); - assert(equal(list[],[4,5,6,7,0,1,2,3])); -} - -@safe unittest -{ - import std.algorithm.comparison : equal; - import std.range : take; - - alias IntList = DList!int; - IntList list = IntList([0,1,2,3]); - auto range = list[]; - for ( ; !range.empty; range.popFront()) - { - int item = range.front; - if (item == 2) - { - list.stableLinearRemove(take(range, 1)); - break; - } - } - assert(equal(list[],[0,1,3])); - - list = IntList([0,1,2,3]); - range = list[]; - for ( ; !range.empty; range.popFront()) - { - int item = range.front; - if (item == 2) - { - list.stableLinearRemove(take(range,2)); - break; - } - } - assert(equal(list[],[0,1])); - - list = IntList([0,1,2,3]); - range = list[]; - for ( ; !range.empty; range.popFront()) - { - int item = range.front; - if (item == 0) - { - list.stableLinearRemove(take(range,2)); - break; - } - } - assert(equal(list[],[2,3])); - - list = IntList([0,1,2,3]); - range = list[]; - for ( ; !range.empty; range.popFront()) - { - int item = range.front; - if (item == 1) - { - list.stableLinearRemove(take(range,2)); - break; - } - } - assert(equal(list[],[0,3])); -} - -@safe unittest -{ - import std.algorithm.comparison : equal; - - auto dl = DList!int([1, 2, 3, 4, 5]); - auto r = dl[]; - r.popFront(); - dl.popFirstOf(r); - assert(equal(dl[], [1, 3, 4, 5])); - assert(equal(r, [3, 4, 5])); - r.popBack(); - dl.popLastOf(r); - assert(equal(dl[], [1, 3, 5])); - assert(equal(r, [3])); - dl = DList!int([0]); - r = dl[]; - dl.popFirstOf(r); - assert(dl.empty); - dl = DList!int([0]); - r = dl[]; - dl.popLastOf(r); - assert(dl.empty); -} - -@safe unittest -{ - import std.algorithm.comparison : equal; - - auto dl = DList!string(["a", "b", "d"]); - dl.insertAfter(dl[], "e"); // insert at the end - assert(equal(dl[], ["a", "b", "d", "e"])); - auto dlr = dl[]; - dlr.popBack(); dlr.popBack(); - dl.insertAfter(dlr, "c"); // insert after "b" - assert(equal(dl[], ["a", "b", "c", "d", "e"])); -} - -@safe unittest -{ - import std.algorithm.comparison : equal; - - auto dl = DList!string(["a", "b", "d"]); - dl.insertBefore(dl[], "e"); // insert at the front - assert(equal(dl[], ["e", "a", "b", "d"])); - auto dlr = dl[]; - dlr.popFront(); dlr.popFront(); - dl.insertBefore(dlr, "c"); // insert before "b" - assert(equal(dl[], ["e", "a", "c", "b", "d"])); -} - -@safe unittest -{ - auto d = DList!int([1, 2, 3]); - d.front = 5; //test frontAssign - assert(d.front == 5); - auto r = d[]; - r.back = 1; - assert(r.back == 1); -} - -// https://issues.dlang.org/show_bug.cgi?id=8895 -@safe unittest -{ - auto a = make!(DList!int)(1,2,3,4); - auto b = make!(DList!int)(1,2,3,4); - auto c = make!(DList!int)(1,2,3,5); - auto d = make!(DList!int)(1,2,3,4,5); - assert(a == b); // this better terminate! - assert(!(a == c)); - assert(!(a == d)); -} - -@safe unittest -{ - auto d = DList!int([1, 2, 3]); - d.front = 5; //test frontAssign - assert(d.front == 5); - auto r = d[]; - r.back = 1; - assert(r.back == 1); -} - -@safe unittest -{ - auto a = DList!int(); - assert(a.removeFront(10) == 0); - a.insert([1, 2, 3]); - assert(a.removeFront(10) == 3); - assert(a[].empty); -} - -@safe unittest -{ - import std.algorithm.comparison : equal; - - //Verify all flavors of ~ - auto a = DList!int(); - auto b = DList!int(); - auto c = DList!int([1, 2, 3]); - auto d = DList!int([4, 5, 6]); - - assert((a ~ b[])[].empty); - assert((c ~ d[])[].equal([1, 2, 3, 4, 5, 6])); - assert(c[].equal([1, 2, 3])); - assert(d[].equal([4, 5, 6])); - - assert((c[] ~ d)[].equal([1, 2, 3, 4, 5, 6])); - assert(c[].equal([1, 2, 3])); - assert(d[].equal([4, 5, 6])); - - a~=c[]; - assert(a[].equal([1, 2, 3])); - assert(c[].equal([1, 2, 3])); - - a~=d[]; - assert(a[].equal([1, 2, 3, 4, 5, 6])); - assert(d[].equal([4, 5, 6])); - - a~=[7, 8, 9]; - assert(a[].equal([1, 2, 3, 4, 5, 6, 7, 8, 9])); - - //trick test: - auto r = c[]; - c.removeFront(); - c.removeBack(); -} - -@safe unittest -{ - import std.algorithm.comparison : equal; - - // https://issues.dlang.org/show_bug.cgi?id=8905 - auto a = DList!int([1, 2, 3, 4]); - auto r = a[]; - a.stableRemoveBack(); - a.stableInsertBack(7); - assert(a[].equal([1, 2, 3, 7])); -} - -// https://issues.dlang.org/show_bug.cgi?id=12566 -@safe unittest -{ - auto dl2 = DList!int([2,7]); - dl2.removeFront(); - assert(dl2[].walkLength == 1); - dl2.removeBack(); - assert(dl2.empty, "not empty?!"); -} - -// https://issues.dlang.org/show_bug.cgi?id=13076 -@safe unittest -{ - DList!int list; - assert(list.empty); - list.clear(); -} - -// https://issues.dlang.org/show_bug.cgi?id=13425 -@safe unittest -{ - import std.range : drop, take; - auto list = DList!int([1,2,3,4,5]); - auto r = list[].drop(4); // r is a view of the last element of list - assert(r.front == 5 && r.walkLength == 1); - r = list.linearRemove(r.take(1)); - assert(r.empty); // fails -} - -// https://issues.dlang.org/show_bug.cgi?id=14300 -@safe unittest -{ - interface ITest {} - static class Test : ITest {} - - DList!ITest().insertBack(new Test()); -} - -// https://issues.dlang.org/show_bug.cgi?id=15263 -@safe unittest -{ - import std.range : iota; - auto a = DList!int(); - a.insertFront(iota(0, 5)); // can insert range with non-ref front - assert(a.front == 0 && a.back == 4); -} - -// https://issues.dlang.org/show_bug.cgi?id=22147 -@safe unittest -{ - import std.algorithm.mutation : move; - - static struct Item - { - @disable this(this); - - int x; - } - - auto list = DList!Item(); - list.insertFront(Item(1)); - assert(list[].walkLength == 1); - assert(list.front.x == 1); - auto item = list.moveFront; - item.x = 2; - list.front = move(item); - assert(list.front.x == 2); - list.removeFront(); - assert(list[].walkLength == 0); -} diff --git a/phobos/std/container/package.d b/phobos/std/container/package.d deleted file mode 100644 index 763da8b..0000000 --- a/phobos/std/container/package.d +++ /dev/null @@ -1,1151 +0,0 @@ -// Written in the D programming language. - -/** -This module defines generic containers. - -Construction: - -To implement the different containers both struct and class based -approaches have been used. $(REF make, std,container,util) allows for -uniform construction with either approach. - ---- -import std.container; -// Construct a red-black tree and an array both containing the values 1, 2, 3. -// RedBlackTree should typically be allocated using `new` -RedBlackTree!int rbTree = new RedBlackTree!int(1, 2, 3); -// But `new` should not be used with Array -Array!int array = Array!int(1, 2, 3); -// `make` hides the differences -RedBlackTree!int rbTree2 = make!(RedBlackTree!int)(1, 2, 3); -Array!int array2 = make!(Array!int)(1, 2, 3); ---- - -Note that `make` can infer the element type from the given arguments. - ---- -import std.container; -auto rbTree = make!RedBlackTree(1, 2, 3); // RedBlackTree!int -auto array = make!Array("1", "2", "3"); // Array!string ---- - -Reference_semantics: - -All containers have reference semantics, which means that after -assignment both variables refer to the same underlying data. - -To make a copy of a container, use the `c.dup` container primitive. ---- -import std.container, std.range; -Array!int originalArray = make!(Array!int)(1, 2, 3); -Array!int secondArray = originalArray; -assert(equal(originalArray[], secondArray[])); - -// changing one instance changes the other one as well! -originalArray[0] = 12; -assert(secondArray[0] == 12); - -// secondArray now refers to an independent copy of originalArray -secondArray = originalArray.dup; -secondArray[0] = 1; -// assert that originalArray has not been affected -assert(originalArray[0] == 12); ---- - -$(B Attention:) If the container is implemented as a class, using an -uninitialized instance can cause a null pointer dereference. - ---- -import std.container; - -RedBlackTree!int rbTree; -rbTree.insert(5); // null pointer dereference ---- - -Using an uninitialized struct-based container will work, because the struct -intializes itself upon use; however, up to this point the container will not -have an identity and assignment does not create two references to the same -data. - ---- -import std.container; - -// create an uninitialized array -Array!int array1; -// array2 does _not_ refer to array1 -Array!int array2 = array1; -array2.insertBack(42); -// thus array1 will not be affected -assert(array1.empty); - -// after initialization reference semantics work as expected -array1 = array2; -// now affects array2 as well -array1.removeBack(); -assert(array2.empty); ---- -It is therefore recommended to always construct containers using -$(REF make, std,container,util). - -This is in fact necessary to put containers into another container. -For example, to construct an `Array` of ten empty `Array`s, use -the following that calls `make` ten times. - ---- -import std.container, std.range; - -auto arrOfArrs = make!Array(generate!(() => make!(Array!int)).take(10)); ---- - -Submodules: - -This module consists of the following submodules: - -$(UL - $(LI - The $(MREF std, container, array) module provides - an array type with deterministic control of memory, not reliant on - the GC unlike built-in arrays. - ) - $(LI - The $(MREF std, container, binaryheap) module - provides a binary heap implementation that can be applied to any - user-provided random-access range. - ) - $(LI - The $(MREF std, container, dlist) module provides - a doubly-linked list implementation. - ) - $(LI - The $(MREF std, container, rbtree) module - implements red-black trees. - ) - $(LI - The $(MREF std, container, slist) module - implements singly-linked lists. - ) - $(LI - The $(MREF std, container, util) module contains - some generic tools commonly used by container implementations. - ) -) - -The_primary_range_of_a_container: - -While some containers offer direct access to their elements e.g. via -`opIndex`, `c.front` or `c.back`, access -and modification of a container's contents is generally done through -its primary $(MREF_ALTTEXT range, std, range) type, -which is aliased as `C.Range`. For example, the primary range type of -`Array!int` is `Array!int.Range`. - -If the documentation of a member function of a container takes -a parameter of type `Range`, then it refers to the primary range type of -this container. Oftentimes `Take!Range` will be used, in which case -the range refers to a span of the elements in the container. Arguments to -these parameters $(B must) be obtained from the same container instance -as the one being worked with. It is important to note that many generic range -algorithms return the same range type as their input range. - ---- -import std.algorithm.comparison : equal; -import std.algorithm.iteration : find; -import std.container; -import std.range : take; - -auto array = make!Array(1, 2, 3); - -// `find` returns an Array!int.Range advanced to the element "2" -array.linearRemove(array[].find(2)); - -assert(array[].equal([1])); - -array = make!Array(1, 2, 3); - -// the range given to `linearRemove` is a Take!(Array!int.Range) -// spanning just the element "2" -array.linearRemove(array[].find(2).take(1)); - -assert(array[].equal([1, 3])); ---- - -When any $(MREF_ALTTEXT range, std, range) can be passed as an argument to -a member function, the documention usually refers to the parameter's templated -type as `Stuff`. - ---- -import std.algorithm.comparison : equal; -import std.container; -import std.range : iota; - -auto array = make!Array(1, 2); - -// the range type returned by `iota` is completely unrelated to Array, -// which is fine for Array.insertBack: -array.insertBack(iota(3, 10)); - -assert(array[].equal([1, 2, 3, 4, 5, 6, 7, 8, 9])); ---- - -Container_primitives: - -Containers do not form a class hierarchy, instead they implement a -common set of primitives (see table below). These primitives each guarantee -a specific worst case complexity and thus allow generic code to be written -independently of the container implementation. - -For example the primitives `c.remove(r)` and `c.linearRemove(r)` both -remove the sequence of elements in range `r` from the container `c`. -The primitive `c.remove(r)` guarantees -$(BIGOH n$(SUBSCRIPT r) log n$(SUBSCRIPT c)) complexity in the worst case and -`c.linearRemove(r)` relaxes this guarantee to $(BIGOH n$(SUBSCRIPT c)). - -Since a sequence of elements can be removed from a $(MREF_ALTTEXT doubly linked list,std,container,dlist) -in constant time, `DList` provides the primitive `c.remove(r)` -as well as `c.linearRemove(r)`. On the other hand -$(MREF_ALTTEXT Array, std,container, array) only offers `c.linearRemove(r)`. - -The following table describes the common set of primitives that containers -implement. A container need not implement all primitives, but if a -primitive is implemented, it must support the syntax described in the $(B -syntax) column with the semantics described in the $(B description) column, and -it must not have a worst-case complexity worse than denoted in big-O notation in -the $(BIGOH ·) column. Below, `C` means a container type, `c` is -a value of container type, $(D n$(SUBSCRIPT x)) represents the effective length of -value `x`, which could be a single element (in which case $(D n$(SUBSCRIPT x)) is -`1`), a container, or a range. - -$(BOOKTABLE Container primitives, -$(TR - $(TH Syntax) - $(TH $(BIGOH ·)) - $(TH Description) -) -$(TR - $(TDNW `C(x)`) - $(TDNW $(D n$(SUBSCRIPT x))) - $(TD Creates a container of type `C` from either another container or a range. - The created container must not be a null reference even if x is empty.) -) -$(TR - $(TDNW `c.dup`) - $(TDNW $(D n$(SUBSCRIPT c))) - $(TD Returns a duplicate of the container.) -) -$(TR - $(TDNW $(D c ~ x)) - $(TDNW $(D n$(SUBSCRIPT c) + n$(SUBSCRIPT x))) - $(TD Returns the concatenation of `c` and `r`. `x` may be a single - element or an input range.) -) -$(TR - $(TDNW $(D x ~ c)) - $(TDNW $(D n$(SUBSCRIPT c) + n$(SUBSCRIPT x))) - $(TD Returns the concatenation of `x` and `c`. `x` may be a - single element or an input range type.) -) -$(LEADINGROWN 3, Iteration -) -$(TR - $(TD `c.Range`) - $(TD) - $(TD The primary range type associated with the container.) -) -$(TR - $(TD `c[]`) - $(TDNW $(D log n$(SUBSCRIPT c))) - $(TD Returns a range - iterating over the entire container, in a container-defined order.) -) -$(TR - $(TDNW $(D c[a .. b])) - $(TDNW $(D log n$(SUBSCRIPT c))) - $(TD Fetches a portion of the container from key `a` to key `b`.) -) -$(LEADINGROWN 3, Capacity -) -$(TR - $(TD `c.empty`) - $(TD `1`) - $(TD Returns `true` if the container has no elements, `false` otherwise.) -) -$(TR - $(TD `c.length`) - $(TDNW $(D log n$(SUBSCRIPT c))) - $(TD Returns the number of elements in the container.) -) -$(TR - $(TDNW $(D c.length = n)) - $(TDNW $(D n$(SUBSCRIPT c) + n)) - $(TD Forces the number of elements in the container to `n`. - If the container ends up growing, the added elements are initialized - in a container-dependent manner (usually with `T.init`).) -) -$(TR - $(TD `c.capacity`) - $(TDNW $(D log n$(SUBSCRIPT c))) - $(TD Returns the maximum number of elements that can be stored in the - container without triggering a reallocation.) -) -$(TR - $(TD `c.reserve(x)`) - $(TD $(D n$(SUBSCRIPT c))) - $(TD Forces `capacity` to at least `x` without reducing it.) -) -$(LEADINGROWN 3, Access -) -$(TR - $(TDNW `c.front`) - $(TDNW $(D log n$(SUBSCRIPT c))) - $(TD Returns the first element of the container, in a container-defined order.) -) -$(TR - $(TDNW `c.moveFront`) - $(TDNW $(D log n$(SUBSCRIPT c))) - $(TD Destructively reads and returns the first element of the - container. The slot is not removed from the container; it is left - initialized with `T.init`. This routine need not be defined if $(D - front) returns a `ref`.) -) -$(TR - $(TDNW $(D c.front = v)) - $(TDNW $(D log n$(SUBSCRIPT c))) - $(TD Assigns `v` to the first element of the container.) -) -$(TR - $(TDNW `c.back`) - $(TDNW $(D log n$(SUBSCRIPT c))) - $(TD Returns the last element of the container, in a container-defined order.) -) -$(TR - $(TDNW `c.moveBack`) - $(TDNW $(D log n$(SUBSCRIPT c))) - $(TD Destructively reads and returns the last element of the - container. The slot is not removed from the container; it is left - initialized with `T.init`. This routine need not be defined if $(D - front) returns a `ref`.) -) -$(TR - $(TDNW $(D c.back = v)) - $(TDNW $(D log n$(SUBSCRIPT c))) - $(TD Assigns `v` to the last element of the container.) -) -$(TR - $(TDNW `c[x]`) - $(TDNW $(D log n$(SUBSCRIPT c))) - $(TD Provides indexed access into the container. The index type is - container-defined. A container may define several index types (and - consequently overloaded indexing).) -) -$(TR - $(TDNW `c.moveAt(x)`) - $(TDNW $(D log n$(SUBSCRIPT c))) - $(TD Destructively reads and returns the value at position `x`. The slot - is not removed from the container; it is left initialized with $(D - T.init).) -) -$(TR - $(TDNW $(D c[x] = v)) - $(TDNW $(D log n$(SUBSCRIPT c))) - $(TD Sets element at specified index into the container.) -) -$(TR - $(TDNW $(D c[x] $(I op)= v)) - $(TDNW $(D log n$(SUBSCRIPT c))) - $(TD Performs read-modify-write operation at specified index into the - container.) -) -$(LEADINGROWN 3, Operations -) -$(TR - $(TDNW $(D e in c)) - $(TDNW $(D log n$(SUBSCRIPT c))) - $(TD Returns nonzero if e is found in `c`.) -) -$(TR - $(TDNW `c.lowerBound(v)`) - $(TDNW $(D log n$(SUBSCRIPT c))) - $(TD Returns a range of all elements strictly less than `v`.) -) -$(TR - $(TDNW `c.upperBound(v)`) - $(TDNW $(D log n$(SUBSCRIPT c))) - $(TD Returns a range of all elements strictly greater than `v`.) -) -$(TR - $(TDNW `c.equalRange(v)`) - $(TDNW $(D log n$(SUBSCRIPT c))) - $(TD Returns a range of all elements in `c` that are equal to `v`.) -) -$(LEADINGROWN 3, Modifiers -) -$(TR - $(TDNW $(D c ~= x)) - $(TDNW $(D n$(SUBSCRIPT c) + n$(SUBSCRIPT x))) - $(TD Appends `x` to `c`. `x` may be a single element or an input range type.) -) -$(TR - $(TDNW `c.clear()`) - $(TDNW $(D n$(SUBSCRIPT c))) - $(TD Removes all elements in `c`.) -) -$(TR - $(TDNW `c.insert(x)`) - $(TDNW $(D n$(SUBSCRIPT x) * log n$(SUBSCRIPT c))) - $(TD Inserts `x` in `c` at a position (or positions) chosen by `c`.) -) -$(TR - $(TDNW `c.stableInsert(x)`) - $(TDNW $(D n$(SUBSCRIPT x) * log n$(SUBSCRIPT c))) - $(TD Same as `c.insert(x)`, but is guaranteed to not invalidate any ranges.) -) -$(TR - $(TDNW `c.linearInsert(v)`) - $(TDNW $(D n$(SUBSCRIPT c))) - $(TD Same as `c.insert(v)` but relaxes complexity to linear.) -) -$(TR - $(TDNW `c.stableLinearInsert(v)`) - $(TDNW $(D n$(SUBSCRIPT c))) - $(TD Same as `c.stableInsert(v)` but relaxes complexity to linear.) -) -$(TR - $(TDNW `c.removeAny()`) - $(TDNW $(D log n$(SUBSCRIPT c))) - $(TD Removes some element from `c` and returns it.) -) -$(TR - $(TDNW `c.stableRemoveAny()`) - $(TDNW $(D log n$(SUBSCRIPT c))) - $(TD Same as `c.removeAny()`, but is guaranteed to not invalidate any - iterators.) -) -$(TR - $(TDNW `c.insertFront(v)`) - $(TDNW $(D log n$(SUBSCRIPT c))) - $(TD Inserts `v` at the front of `c`.) -) -$(TR - $(TDNW `c.stableInsertFront(v)`) - $(TDNW $(D log n$(SUBSCRIPT c))) - $(TD Same as `c.insertFront(v)`, but guarantees no ranges will be - invalidated.) -) -$(TR - $(TDNW `c.insertBack(v)`) - $(TDNW $(D log n$(SUBSCRIPT c))) - $(TD Inserts `v` at the back of `c`.) -) -$(TR - $(TDNW `c.stableInsertBack(v)`) - $(TDNW $(D log n$(SUBSCRIPT c))) - $(TD Same as `c.insertBack(v)`, but guarantees no ranges will be - invalidated.) -) -$(TR - $(TDNW `c.removeFront()`) - $(TDNW $(D log n$(SUBSCRIPT c))) - $(TD Removes the element at the front of `c`.) -) -$(TR - $(TDNW `c.stableRemoveFront()`) - $(TDNW $(D log n$(SUBSCRIPT c))) - $(TD Same as `c.removeFront()`, but guarantees no ranges will be - invalidated.) -) -$(TR - $(TDNW `c.removeBack()`) - $(TDNW $(D log n$(SUBSCRIPT c))) - $(TD Removes the value at the back of `c`.) -) -$(TR - $(TDNW `c.stableRemoveBack()`) - $(TDNW $(D log n$(SUBSCRIPT c))) - $(TD Same as `c.removeBack()`, but guarantees no ranges will be - invalidated.) -) -$(TR - $(TDNW `c.remove(r)`) - $(TDNW $(D n$(SUBSCRIPT r) * log n$(SUBSCRIPT c))) - $(TD Removes range `r` from `c`.) -) -$(TR - $(TDNW `c.stableRemove(r)`) - $(TDNW $(D n$(SUBSCRIPT r) * log n$(SUBSCRIPT c))) - $(TD Same as `c.remove(r)`, but guarantees iterators are not - invalidated.) -) -$(TR - $(TDNW `c.linearRemove(r)`) - $(TDNW $(D n$(SUBSCRIPT c))) - $(TD Removes range `r` from `c`.) -) -$(TR - $(TDNW `c.stableLinearRemove(r)`) - $(TDNW $(D n$(SUBSCRIPT c))) - $(TD Same as `c.linearRemove(r)`, but guarantees iterators are not - invalidated.) -) -$(TR - $(TDNW `c.removeKey(k)`) - $(TDNW $(D log n$(SUBSCRIPT c))) - $(TD Removes an element from `c` by using its key `k`. - The key's type is defined by the container.) -) -) - -Source: $(PHOBOSSRC std/container/package.d) - -Copyright: Red-black tree code copyright (C) 2008- by Steven Schveighoffer. Other code -copyright 2010- Andrei Alexandrescu. All rights reserved by the respective holders. - -License: Distributed under the Boost Software License, Version 1.0. -(See accompanying file LICENSE_1_0.txt or copy at $(HTTP -boost.org/LICENSE_1_0.txt)). - -Authors: Steven Schveighoffer, $(HTTP erdani.com, Andrei Alexandrescu) - */ - -module std.container; - -public import std.container.array; -public import std.container.binaryheap; -public import std.container.dlist; -public import std.container.rbtree; -public import std.container.slist; - -import std.meta; - - -/* The following documentation and type `TotalContainer` are -intended for developers only. - -`TotalContainer` is an unimplemented container that illustrates a -host of primitives that a container may define. It is to some extent -the bottom of the conceptual container hierarchy. A given container -most often will choose to only implement a subset of these primitives, -and define its own additional ones. Adhering to the standard primitive -names below allows generic code to work independently of containers. - -Things to remember: any container must be a reference type, whether -implemented as a `class` or `struct`. No primitive below -requires the container to escape addresses of elements, which means -that compliant containers can be defined to use reference counting or -other deterministic memory management techniques. - -A container may choose to define additional specific operations. The -only requirement is that those operations bear different names than -the ones below, lest user code gets confused. - -Complexity of operations should be interpreted as "at least as good -as". If an operation is required to have $(BIGOH n) complexity, it -could have anything lower than that, e.g. $(BIGOH log(n)). Unless -specified otherwise, `n` inside a $(BIGOH) expression stands for -the number of elements in the container. - */ -struct TotalContainer(T) -{ -/** -If the container has a notion of key-value mapping, `KeyType` -defines the type of the key of the container. - */ - alias KeyType = T; - -/** -If the container has a notion of multikey-value mapping, $(D -KeyTypes[k]), where `k` is a zero-based unsigned number, defines -the type of the `k`th key of the container. - -A container may define both `KeyType` and `KeyTypes`, e.g. in -the case it has the notion of primary/preferred key. - */ - alias KeyTypes = AliasSeq!T; - -/** -If the container has a notion of key-value mapping, `ValueType` -defines the type of the value of the container. Typically, a map-style -container mapping values of type `K` to values of type `V` -defines `KeyType` to be `K` and `ValueType` to be `V`. - */ - alias ValueType = T; - -/** -Defines the container's primary range, which embodies one of the -ranges defined in $(MREF std,range). - -Generally a container may define several types of ranges. - */ - struct Range - { - /++ - Range primitives. - +/ - @property bool empty() - { - assert(0, "Not implemented"); - } - /// Ditto - @property ref T front() //ref return optional - { - assert(0, "Not implemented"); - } - /// Ditto - @property void front(T value) //Only when front does not return by ref - { - assert(0, "Not implemented"); - } - /// Ditto - T moveFront() - { - assert(0, "Not implemented"); - } - /// Ditto - void popFront() - { - assert(0, "Not implemented"); - } - /// Ditto - @property ref T back() //ref return optional - { - assert(0, "Not implemented"); - } - /// Ditto - @property void back(T value) //Only when front does not return by ref - { - assert(0, "Not implemented"); - } - /// Ditto - T moveBack() - { - assert(0, "Not implemented"); - } - /// Ditto - void popBack() - { - assert(0, "Not implemented"); - } - /// Ditto - T opIndex(size_t i) //ref return optional - { - assert(0, "Not implemented"); - } - /// Ditto - void opIndexAssign(size_t i, T value) //Only when front does not return by ref - { - assert(0, "Not implemented"); - } - /// Ditto - T opIndexUnary(string op)(size_t i) //Only when front does not return by ref - { - assert(0, "Not implemented"); - } - /// Ditto - void opIndexOpAssign(string op)(size_t i, T value) //Only when front does not return by ref - { - assert(0, "Not implemented"); - } - /// Ditto - T moveAt(size_t i) - { - assert(0, "Not implemented"); - } - /// Ditto - @property size_t length() - { - assert(0, "Not implemented"); - } - } - -/** -Property returning `true` if and only if the container has no -elements. - -Complexity: $(BIGOH 1) - */ - @property bool empty() - { - assert(0, "Not implemented"); - } - -/** -Returns a duplicate of the container. The elements themselves are not -transitively duplicated. - -Complexity: $(BIGOH n). - */ - @property TotalContainer dup() - { - assert(0, "Not implemented"); - } - -/** -Returns the number of elements in the container. - -Complexity: $(BIGOH log(n)). -*/ - @property size_t length() - { - assert(0, "Not implemented"); - } - -/** -Returns the maximum number of elements the container can store without -(a) allocating memory, (b) invalidating iterators upon insertion. - -Complexity: $(BIGOH log(n)). - */ - @property size_t capacity() - { - assert(0, "Not implemented"); - } - -/** -Ensures sufficient capacity to accommodate `n` elements. - -Postcondition: $(D capacity >= n) - -Complexity: $(BIGOH log(e - capacity)) if $(D e > capacity), otherwise -$(BIGOH 1). - */ - void reserve(size_t e) - { - assert(0, "Not implemented"); - } - -/** -Returns a range that iterates over all elements of the container, in a -container-defined order. The container should choose the most -convenient and fast method of iteration for `opSlice()`. - -Complexity: $(BIGOH log(n)) - */ - Range opSlice() - { - assert(0, "Not implemented"); - } - - /** - Returns a range that iterates the container between two - specified positions. - - Complexity: $(BIGOH log(n)) - */ - Range opSlice(size_t a, size_t b) - { - assert(0, "Not implemented"); - } - -/** -Forward to `opSlice().front` and `opSlice().back`, respectively. - -Complexity: $(BIGOH log(n)) - */ - @property ref T front() //ref return optional - { - assert(0, "Not implemented"); - } - /// Ditto - @property void front(T value) //Only when front does not return by ref - { - assert(0, "Not implemented"); - } - /// Ditto - T moveFront() - { - assert(0, "Not implemented"); - } - /// Ditto - @property ref T back() //ref return optional - { - assert(0, "Not implemented"); - } - /// Ditto - @property void back(T value) //Only when front does not return by ref - { - assert(0, "Not implemented"); - } - /// Ditto - T moveBack() - { - assert(0, "Not implemented"); - } - -/** -Indexing operators yield or modify the value at a specified index. - */ - ref T opIndex(KeyType) //ref return optional - { - assert(0, "Not implemented"); - } - /// ditto - void opIndexAssign(KeyType i, T value) //Only when front does not return by ref - { - assert(0, "Not implemented"); - } - /// ditto - T opIndexUnary(string op)(KeyType i) //Only when front does not return by ref - { - assert(0, "Not implemented"); - } - /// ditto - void opIndexOpAssign(string op)(KeyType i, T value) //Only when front does not return by ref - { - assert(0, "Not implemented"); - } - /// ditto - T moveAt(KeyType i) - { - assert(0, "Not implemented"); - } - -/** -$(D k in container) returns true if the given key is in the container. - */ - bool opBinaryRight(string op)(KeyType k) if (op == "in") - { - assert(0, "Not implemented"); - } - -/** -Returns a range of all elements containing `k` (could be empty or a -singleton range). - */ - Range equalRange(KeyType k) - { - assert(0, "Not implemented"); - } - -/** -Returns a range of all elements with keys less than `k` (could be -empty or a singleton range). Only defined by containers that store -data sorted at all times. - */ - Range lowerBound(KeyType k) - { - assert(0, "Not implemented"); - } - -/** -Returns a range of all elements with keys larger than `k` (could be -empty or a singleton range). Only defined by containers that store -data sorted at all times. - */ - Range upperBound(KeyType k) - { - assert(0, "Not implemented"); - } - -/** -Returns a new container that's the concatenation of `this` and its -argument. `opBinaryRight` is only defined if `Stuff` does not -define `opBinary`. - -Complexity: $(BIGOH n + m), where m is the number of elements in $(D -stuff) - */ - TotalContainer opBinary(string op)(Stuff rhs) if (op == "~") - { - assert(0, "Not implemented"); - } - - /// ditto - TotalContainer opBinaryRight(string op)(Stuff lhs) if (op == "~") - { - assert(0, "Not implemented"); - } - -/** -Forwards to $(D insertAfter(this[], stuff)). - */ - void opOpAssign(string op)(Stuff stuff) if (op == "~") - { - assert(0, "Not implemented"); - } - -/** -Removes all contents from the container. The container decides how $(D -capacity) is affected. - -Postcondition: `empty` - -Complexity: $(BIGOH n) - */ - void clear() - { - assert(0, "Not implemented"); - } - -/** -Sets the number of elements in the container to `newSize`. If $(D -newSize) is greater than `length`, the added elements are added to -unspecified positions in the container and initialized with $(D -.init). - -Complexity: $(BIGOH abs(n - newLength)) - -Postcondition: $(D _length == newLength) - */ - @property void length(size_t newLength) - { - assert(0, "Not implemented"); - } - -/** -Inserts `stuff` in an unspecified position in the -container. Implementations should choose whichever insertion means is -the most advantageous for the container, but document the exact -behavior. `stuff` can be a value convertible to the element type of -the container, or a range of values convertible to it. - -The `stable` version guarantees that ranges iterating over the -container are never invalidated. Client code that counts on -non-invalidating insertion should use `stableInsert`. Such code would -not compile against containers that don't support it. - -Returns: The number of elements added. - -Complexity: $(BIGOH m * log(n)), where `m` is the number of -elements in `stuff` - */ - size_t insert(Stuff)(Stuff stuff) - { - assert(0, "Not implemented"); - } - ///ditto - size_t stableInsert(Stuff)(Stuff stuff) - { - assert(0, "Not implemented"); - } - -/** -Same as `insert(stuff)` and `stableInsert(stuff)` respectively, -but relax the complexity constraint to linear. - */ - size_t linearInsert(Stuff)(Stuff stuff) - { - assert(0, "Not implemented"); - } - ///ditto - size_t stableLinearInsert(Stuff)(Stuff stuff) - { - assert(0, "Not implemented"); - } - -/** -Picks one value in an unspecified position in the container, removes -it from the container, and returns it. Implementations should pick the -value that's the most advantageous for the container. The stable version -behaves the same, but guarantees that ranges iterating over the container -are never invalidated. - -Precondition: `!empty` - -Returns: The element removed. - -Complexity: $(BIGOH log(n)). - */ - T removeAny() - { - assert(0, "Not implemented"); - } - /// ditto - T stableRemoveAny() - { - assert(0, "Not implemented"); - } - -/** -Inserts `value` to the front or back of the container. `stuff` -can be a value convertible to the container's element type or a range -of values convertible to it. The stable version behaves the same, but -guarantees that ranges iterating over the container are never -invalidated. - -Returns: The number of elements inserted - -Complexity: $(BIGOH log(n)). - */ - size_t insertFront(Stuff)(Stuff stuff) - { - assert(0, "Not implemented"); - } - /// ditto - size_t stableInsertFront(Stuff)(Stuff stuff) - { - assert(0, "Not implemented"); - } - /// ditto - size_t insertBack(Stuff)(Stuff stuff) - { - assert(0, "Not implemented"); - } - /// ditto - size_t stableInsertBack(T value) - { - assert(0, "Not implemented"); - } - -/** -Removes the value at the front or back of the container. The stable -version behaves the same, but guarantees that ranges iterating over -the container are never invalidated. The optional parameter $(D -howMany) instructs removal of that many elements. If $(D howMany > n), -all elements are removed and no exception is thrown. - -Precondition: `!empty` - -Complexity: $(BIGOH log(n)). - */ - void removeFront() - { - assert(0, "Not implemented"); - } - /// ditto - void stableRemoveFront() - { - assert(0, "Not implemented"); - } - /// ditto - void removeBack() - { - assert(0, "Not implemented"); - } - /// ditto - void stableRemoveBack() - { - assert(0, "Not implemented"); - } - -/** -Removes `howMany` values at the front or back of the -container. Unlike the unparameterized versions above, these functions -do not throw if they could not remove `howMany` elements. Instead, -if $(D howMany > n), all elements are removed. The returned value is -the effective number of elements removed. The stable version behaves -the same, but guarantees that ranges iterating over the container are -never invalidated. - -Returns: The number of elements removed - -Complexity: $(BIGOH howMany * log(n)). - */ - size_t removeFront(size_t howMany) - { - assert(0, "Not implemented"); - } - /// ditto - size_t stableRemoveFront(size_t howMany) - { - assert(0, "Not implemented"); - } - /// ditto - size_t removeBack(size_t howMany) - { - assert(0, "Not implemented"); - } - /// ditto - size_t stableRemoveBack(size_t howMany) - { - assert(0, "Not implemented"); - } - -/** -Removes all values corresponding to key `k`. - -Complexity: $(BIGOH m * log(n)), where `m` is the number of -elements with the same key. - -Returns: The number of elements removed. - */ - size_t removeKey(KeyType k) - { - assert(0, "Not implemented"); - } - -/** -Inserts `stuff` before, after, or instead range `r`, which must -be a valid range previously extracted from this container. `stuff` -can be a value convertible to the container's element type or a range -of objects convertible to it. The stable version behaves the same, but -guarantees that ranges iterating over the container are never -invalidated. - -Returns: The number of values inserted. - -Complexity: $(BIGOH n + m), where `m` is the length of `stuff` - */ - size_t insertBefore(Stuff)(Range r, Stuff stuff) - { - assert(0, "Not implemented"); - } - /// ditto - size_t stableInsertBefore(Stuff)(Range r, Stuff stuff) - { - assert(0, "Not implemented"); - } - /// ditto - size_t insertAfter(Stuff)(Range r, Stuff stuff) - { - assert(0, "Not implemented"); - } - /// ditto - size_t stableInsertAfter(Stuff)(Range r, Stuff stuff) - { - assert(0, "Not implemented"); - } - /// ditto - size_t replace(Stuff)(Range r, Stuff stuff) - { - assert(0, "Not implemented"); - } - /// ditto - size_t stableReplace(Stuff)(Range r, Stuff stuff) - { - assert(0, "Not implemented"); - } - -/** -Removes all elements belonging to `r`, which must be a range -obtained originally from this container. The stable version behaves the -same, but guarantees that ranges iterating over the container are -never invalidated. - -Returns: A range spanning the remaining elements in the container that -initially were right after `r`. - -Complexity: $(BIGOH m * log(n)), where `m` is the number of -elements in `r` - */ - Range remove(Range r) - { - assert(0, "Not implemented"); - } - /// ditto - Range stableRemove(Range r) - { - assert(0, "Not implemented"); - } - -/** -Same as `remove` above, but has complexity relaxed to linear. - -Returns: A range spanning the remaining elements in the container that -initially were right after `r`. - -Complexity: $(BIGOH n) - */ - Range linearRemove(Range r) - { - assert(0, "Not implemented"); - } - /// ditto - Range stableLinearRemove(Range r) - { - assert(0, "Not implemented"); - } -} - -@safe unittest -{ - TotalContainer!int test; -} diff --git a/phobos/std/container/rbtree.d b/phobos/std/container/rbtree.d deleted file mode 100644 index 9bd8d27..0000000 --- a/phobos/std/container/rbtree.d +++ /dev/null @@ -1,2230 +0,0 @@ -/** -This module implements a red-black tree container. - -This module is a submodule of $(MREF std, container). - -Source: $(PHOBOSSRC std/container/rbtree.d) - -Copyright: Red-black tree code copyright (C) 2008- by Steven Schveighoffer. Other code -copyright 2010- Andrei Alexandrescu. All rights reserved by the respective holders. - -License: Distributed under the Boost Software License, Version 1.0. -(See accompanying file LICENSE_1_0.txt or copy at $(HTTP -boost.org/LICENSE_1_0.txt)). - -Authors: Steven Schveighoffer, $(HTTP erdani.com, Andrei Alexandrescu) -*/ -module std.container.rbtree; - -/// -@safe pure unittest -{ - import std.algorithm.comparison : equal; - import std.container.rbtree; - - auto rbt = redBlackTree(3, 1, 4, 2, 5); - assert(rbt.front == 1); - assert(equal(rbt[], [1, 2, 3, 4, 5])); - - rbt.removeKey(1, 4); - assert(equal(rbt[], [2, 3, 5])); - - rbt.removeFront(); - assert(equal(rbt[], [3, 5])); - - rbt.insert([1, 2, 4]); - assert(equal(rbt[], [1, 2, 3, 4, 5])); - - // Query bounds in O(log(n)) - assert(rbt.lowerBound(3).equal([1, 2])); - assert(rbt.equalRange(3).equal([3])); - assert(rbt.upperBound(3).equal([4, 5])); - - // A Red Black tree with the highest element at front: - import std.range : iota; - auto maxTree = redBlackTree!"a > b"(iota(5)); - assert(equal(maxTree[], [4, 3, 2, 1, 0])); - - // adding duplicates will not add them, but return 0 - auto rbt2 = redBlackTree(1, 3); - assert(rbt2.insert(1) == 0); - assert(equal(rbt2[], [1, 3])); - assert(rbt2.insert(2) == 1); - - // however you can allow duplicates - auto ubt = redBlackTree!true([0, 1, 0, 1]); - assert(equal(ubt[], [0, 0, 1, 1])); -} - -import std.format; -import std.functional : binaryFun; - -public import std.container.util; - -version (StdUnittest) debug = RBDoChecks; - -//debug = RBDoChecks; - -/* - * Implementation for a Red Black node for use in a Red Black Tree (see below) - * - * this implementation assumes we have a marker Node that is the parent of the - * root Node. This marker Node is not a valid Node, but marks the end of the - * collection. The root is the left child of the marker Node, so it is always - * last in the collection. The marker Node is passed in to the setColor - * function, and the Node which has this Node as its parent is assumed to be - * the root Node. - * - * A Red Black tree should have O(lg(n)) insertion, removal, and search time. - */ -struct RBNode(V) -{ - /* - * Convenience alias - */ - alias Node = RBNode*; - - private Node _left; - private Node _right; - private Node _parent; - - /** - * The value held by this node - */ - V value; - - /** - * Enumeration determining what color the node is. Null nodes are assumed - * to be black. - */ - enum Color : byte - { - Red, - Black - } - - /** - * The color of the node. - */ - Color color; - - /** - * Get the left child - */ - @property inout(RBNode)* left() inout return scope - { - return _left; - } - - /** - * Get the right child - */ - @property inout(RBNode)* right() inout return scope - { - return _right; - } - - /** - * Get the parent - */ - @property inout(RBNode)* parent() inout return scope - { - return _parent; - } - - /** - * Set the left child. Also updates the new child's parent node. This - * does not update the previous child. - * - * $(RED Warning: If the node this is called on is a local variable, a stack pointer can be - * escaped through `newNode.parent`. It's marked `@trusted` only for backwards compatibility.) - * - * Returns newNode - */ - @property Node left(return scope Node newNode) @trusted - { - _left = newNode; - if (newNode !is null) - newNode._parent = &this; - return newNode; - } - - /** - * Set the right child. Also updates the new child's parent node. This - * does not update the previous child. - * - * $(RED Warning: If the node this is called on is a local variable, a stack pointer can be - * escaped through `newNode.parent`. It's marked `@trusted` only for backwards compatibility.) - * - * Returns newNode - */ - @property Node right(return scope Node newNode) @trusted - { - _right = newNode; - if (newNode !is null) - newNode._parent = &this; - return newNode; - } - - // assume _left is not null - // - // performs rotate-right operation, where this is T, _right is R, _left is - // L, _parent is P: - // - // P P - // | -> | - // T L - // / \ / \ - // L R a T - // / \ / \ - // a b b R - // - /** - * Rotate right. This performs the following operations: - * - The left child becomes the parent of this node. - * - This node becomes the new parent's right child. - * - The old right child of the new parent becomes the left child of this - * node. - */ - Node rotateR() - in - { - assert(_left !is null, "left node must not be null"); - } - do - { - // sets _left._parent also - if (isLeftNode) - parent.left = _left; - else - parent.right = _left; - Node tmp = _left._right; - - // sets _parent also - _left.right = &this; - - // sets tmp._parent also - left = tmp; - - return &this; - } - - // assumes _right is non null - // - // performs rotate-left operation, where this is T, _right is R, _left is - // L, _parent is P: - // - // P P - // | -> | - // T R - // / \ / \ - // L R T b - // / \ / \ - // a b L a - // - /** - * Rotate left. This performs the following operations: - * - The right child becomes the parent of this node. - * - This node becomes the new parent's left child. - * - The old left child of the new parent becomes the right child of this - * node. - */ - Node rotateL() - in - { - assert(_right !is null, "right node must not be null"); - } - do - { - // sets _right._parent also - if (isLeftNode) - parent.left = _right; - else - parent.right = _right; - Node tmp = _right._left; - - // sets _parent also - _right.left = &this; - - // sets tmp._parent also - right = tmp; - return &this; - } - - - /** - * Returns true if this node is a left child. - * - * Note that this should always return a value because the root has a - * parent which is the marker node. - */ - @property bool isLeftNode() const - in - { - assert(_parent !is null, "parent must not be null"); - } - do - { - return _parent._left is &this; - } - - /** - * Set the color of the node after it is inserted. This performs an - * update to the whole tree, possibly rotating nodes to keep the Red-Black - * properties correct. This is an O(lg(n)) operation, where n is the - * number of nodes in the tree. - * - * end is the marker node, which is the parent of the topmost valid node. - */ - void setColor(Node end) - { - // test against the marker node - if (_parent !is end) - { - if (_parent.color == Color.Red) - { - Node cur = &this; - while (true) - { - // because root is always black, _parent._parent always exists - if (cur._parent.isLeftNode) - { - // parent is left node, y is 'uncle', could be null - Node y = cur._parent._parent._right; - if (y !is null && y.color == Color.Red) - { - cur._parent.color = Color.Black; - y.color = Color.Black; - cur = cur._parent._parent; - if (cur._parent is end) - { - // root node - cur.color = Color.Black; - break; - } - else - { - // not root node - cur.color = Color.Red; - if (cur._parent.color == Color.Black) - // satisfied, exit the loop - break; - } - } - else - { - if (!cur.isLeftNode) - cur = cur._parent.rotateL(); - cur._parent.color = Color.Black; - cur = cur._parent._parent.rotateR(); - cur.color = Color.Red; - // tree should be satisfied now - break; - } - } - else - { - // parent is right node, y is 'uncle' - Node y = cur._parent._parent._left; - if (y !is null && y.color == Color.Red) - { - cur._parent.color = Color.Black; - y.color = Color.Black; - cur = cur._parent._parent; - if (cur._parent is end) - { - // root node - cur.color = Color.Black; - break; - } - else - { - // not root node - cur.color = Color.Red; - if (cur._parent.color == Color.Black) - // satisfied, exit the loop - break; - } - } - else - { - if (cur.isLeftNode) - cur = cur._parent.rotateR(); - cur._parent.color = Color.Black; - cur = cur._parent._parent.rotateL(); - cur.color = Color.Red; - // tree should be satisfied now - break; - } - } - } - - } - } - else - { - // - // this is the root node, color it black - // - color = Color.Black; - } - } - - /** - * Remove this node from the tree. The 'end' node is used as the marker - * which is root's parent. Note that this cannot be null! - * - * Returns the next highest valued node in the tree after this one, or end - * if this was the highest-valued node. - */ - Node remove(Node end) return - { - // - // remove this node from the tree, fixing the color if necessary. - // - Node x; - Node ret = next; - - // if this node has 2 children - if (_left !is null && _right !is null) - { - // - // normally, we can just swap this node's and y's value, but - // because an iterator could be pointing to y and we don't want to - // disturb it, we swap this node and y's structure instead. This - // can also be a benefit if the value of the tree is a large - // struct, which takes a long time to copy. - // - Node yp, yl, yr; - Node y = ret; // y = next - yp = y._parent; - yl = y._left; - yr = y._right; - auto yc = y.color; - auto isyleft = y.isLeftNode; - - // - // replace y's structure with structure of this node. - // - if (isLeftNode) - _parent.left = y; - else - _parent.right = y; - // - // need special case so y doesn't point back to itself - // - y.left = _left; - if (_right is y) - y.right = &this; - else - y.right = _right; - y.color = color; - - // - // replace this node's structure with structure of y. - // - left = yl; - right = yr; - if (_parent !is y) - { - if (isyleft) - yp.left = &this; - else - yp.right = &this; - } - color = yc; - } - - // if this has less than 2 children, remove it - if (_left !is null) - x = _left; - else - x = _right; - - bool deferedUnlink = false; - if (x is null) - { - // pretend this is a null node, defer unlinking the node - x = &this; - deferedUnlink = true; - } - else if (isLeftNode) - _parent.left = x; - else - _parent.right = x; - - // if the color of this is black, then it needs to be fixed - if (color == color.Black) - { - // need to recolor the tree. - while (x._parent !is end && x.color == Node.Color.Black) - { - if (x.isLeftNode) - { - // left node - Node w = x._parent._right; - if (w.color == Node.Color.Red) - { - w.color = Node.Color.Black; - x._parent.color = Node.Color.Red; - x._parent.rotateL(); - w = x._parent._right; - } - Node wl = w.left; - Node wr = w.right; - if ((wl is null || wl.color == Node.Color.Black) && - (wr is null || wr.color == Node.Color.Black)) - { - w.color = Node.Color.Red; - x = x._parent; - } - else - { - if (wr is null || wr.color == Node.Color.Black) - { - // wl cannot be null here - wl.color = Node.Color.Black; - w.color = Node.Color.Red; - w.rotateR(); - w = x._parent._right; - } - - w.color = x._parent.color; - x._parent.color = Node.Color.Black; - w._right.color = Node.Color.Black; - x._parent.rotateL(); - x = end.left; // x = root - } - } - else - { - // right node - Node w = x._parent._left; - if (w.color == Node.Color.Red) - { - w.color = Node.Color.Black; - x._parent.color = Node.Color.Red; - x._parent.rotateR(); - w = x._parent._left; - } - Node wl = w.left; - Node wr = w.right; - if ((wl is null || wl.color == Node.Color.Black) && - (wr is null || wr.color == Node.Color.Black)) - { - w.color = Node.Color.Red; - x = x._parent; - } - else - { - if (wl is null || wl.color == Node.Color.Black) - { - // wr cannot be null here - wr.color = Node.Color.Black; - w.color = Node.Color.Red; - w.rotateL(); - w = x._parent._left; - } - - w.color = x._parent.color; - x._parent.color = Node.Color.Black; - w._left.color = Node.Color.Black; - x._parent.rotateR(); - x = end.left; // x = root - } - } - } - x.color = Node.Color.Black; - } - - if (deferedUnlink) - { - // - // unlink this node from the tree - // - if (isLeftNode) - _parent.left = null; - else - _parent.right = null; - } - - // clean references to help GC - // https://issues.dlang.org/show_bug.cgi?id=12915 - _left = _right = _parent = null; - - return ret; - } - - /** - * Return the leftmost descendant of this node. - */ - @property inout(RBNode)* leftmost() inout return - { - inout(RBNode)* result = &this; - while (result._left !is null) - result = result._left; - return result; - } - - /** - * Return the rightmost descendant of this node - */ - @property inout(RBNode)* rightmost() inout return - { - inout(RBNode)* result = &this; - while (result._right !is null) - result = result._right; - return result; - } - - /** - * Returns the next valued node in the tree. - * - * You should never call this on the marker node, as it is assumed that - * there is a valid next node. - */ - @property inout(RBNode)* next() inout return - { - inout(RBNode)* n = &this; - if (n.right is null) - { - while (!n.isLeftNode) - n = n._parent; - return n._parent; - } - else - return n.right.leftmost; - } - - /** - * Returns the previous valued node in the tree. - * - * You should never call this on the leftmost node of the tree as it is - * assumed that there is a valid previous node. - */ - @property inout(RBNode)* prev() inout return - { - inout(RBNode)* n = &this; - if (n.left is null) - { - while (n.isLeftNode) - n = n._parent; - return n._parent; - } - else - return n.left.rightmost; - } - - Node dup(scope Node delegate(V v) alloc) - { - // - // duplicate this and all child nodes - // - // The recursion should be lg(n), so we shouldn't have to worry about - // stack size. - // - Node copy = alloc(value); - copy.color = color; - if (_left !is null) - copy.left = _left.dup(alloc); - if (_right !is null) - copy.right = _right.dup(alloc); - return copy; - } - - Node dup() - { - Node copy = new RBNode!V(null, null, null, value); - copy.color = color; - if (_left !is null) - copy.left = _left.dup(); - if (_right !is null) - copy.right = _right.dup(); - return copy; - } -} - -//constness checks -@safe pure unittest -{ - const RBNode!int n; - static assert(is(typeof(n.leftmost))); - static assert(is(typeof(n.rightmost))); - static assert(is(typeof(n.next))); - static assert(is(typeof(n.prev))); -} - -private struct RBRange(N) -{ - alias Node = N; - alias Elem = typeof(Node.value); - - private Node _begin; - private Node _end; - - private this(Node b, Node e) - { - _begin = b; - _end = e; - } - - /** - * Returns `true` if the range is _empty - */ - @property bool empty() const - { - return _begin is _end; - } - - /** - * Returns the first element in the range - */ - @property Elem front() - { - return _begin.value; - } - - /** - * Returns the last element in the range - */ - @property Elem back() - { - return _end.prev.value; - } - - /** - * pop the front element from the range - * - * Complexity: amortized $(BIGOH 1) - */ - void popFront() - { - _begin = _begin.next; - } - - /** - * pop the back element from the range - * - * Complexity: amortized $(BIGOH 1) - */ - void popBack() - { - _end = _end.prev; - } - - /** - * Trivial _save implementation, needed for `isForwardRange`. - */ - @property RBRange save() - { - return this; - } -} - -/** - * Implementation of a $(LINK2 https://en.wikipedia.org/wiki/Red%E2%80%93black_tree, - * red-black tree) container. - * - * All inserts, removes, searches, and any function in general has complexity - * of $(BIGOH lg(n)). - * - * To use a different comparison than $(D "a < b"), pass a different operator string - * that can be used by $(REF binaryFun, std,functional), or pass in a - * function, delegate, functor, or any type where $(D less(a, b)) results in a `bool` - * value. - * - * Note that less should produce a strict ordering. That is, for two unequal - * elements `a` and `b`, $(D less(a, b) == !less(b, a)). $(D less(a, a)) should - * always equal `false`. - * - * If `allowDuplicates` is set to `true`, then inserting the same element more than - * once continues to add more elements. If it is `false`, duplicate elements are - * ignored on insertion. If duplicates are allowed, then new elements are - * inserted after all existing duplicate elements. - */ -final class RedBlackTree(T, alias less = "a < b", bool allowDuplicates = false) -if (is(typeof(binaryFun!less(T.init, T.init)))) -{ - import std.meta : allSatisfy; - import std.range : Take; - import std.range.primitives : isInputRange, walkLength; - import std.traits : isIntegral, isDynamicArray, isImplicitlyConvertible; - - alias _less = binaryFun!less; - - version (StdUnittest) - { - static if (is(typeof(less) == string)) - { - private enum doUnittest = is(byte : T) && isIntegral!T && (less == "a < b" || less == "a > b"); - } - else - enum doUnittest = false; - - // note, this must be final so it does not affect the vtable layout - bool arrayEqual(T[] arr) - { - if (walkLength(this[]) == arr.length) - { - foreach (v; arr) - { - if (!(v in this)) - return false; - } - return true; - } - return false; - } - } - else - { - private enum doUnittest = false; - } - - /** - * Element type for the tree - */ - alias Elem = T; - - // used for convenience - private alias RBNode = .RBNode!Elem; - private alias Node = RBNode.Node; - - private Node _end; - private Node _begin; - private size_t _length; - - private void _setup() - { - //Make sure that _setup isn't run more than once. - assert(!_end, "Setup must only be run once"); - _begin = _end = allocate(); - } - - static private Node allocate() - { - return new RBNode; - } - - static private Node allocate(Elem v) - { - return new RBNode(null, null, null, v); - } - - /** - * The range types for `RedBlackTree` - */ - alias Range = RBRange!(RBNode*); - alias ConstRange = RBRange!(const(RBNode)*); /// Ditto - alias ImmutableRange = RBRange!(immutable(RBNode)*); /// Ditto - - static if (doUnittest) @safe pure unittest - { - import std.algorithm.comparison : equal; - import std.range.primitives; - auto ts = new RedBlackTree(1, 2, 3, 4, 5); - assert(ts.length == 5); - auto r = ts[]; - - static if (less == "a < b") - auto vals = [1, 2, 3, 4, 5]; - else - auto vals = [5, 4, 3, 2, 1]; - assert(equal(r, vals)); - - assert(r.front == vals.front); - assert(r.back != r.front); - auto oldfront = r.front; - auto oldback = r.back; - r.popFront(); - r.popBack(); - assert(r.front != r.back); - assert(r.front != oldfront); - assert(r.back != oldback); - assert(ts.length == 5); - } - - // find a node based on an element value - private inout(RBNode)* _find(Elem e) inout - { - static if (allowDuplicates) - { - inout(RBNode)* cur = _end.left; - inout(RBNode)* result = null; - while (cur) - { - if (_less(cur.value, e)) - cur = cur.right; - else if (_less(e, cur.value)) - cur = cur.left; - else - { - // want to find the left-most element - result = cur; - cur = cur.left; - } - } - return result; - } - else - { - inout(RBNode)* cur = _end.left; - while (cur) - { - if (_less(cur.value, e)) - cur = cur.right; - else if (_less(e, cur.value)) - cur = cur.left; - else - return cur; - } - return null; - } - } - - /* add an element to the tree, returns the node added, or the existing node - * if it has already been added and allowDuplicates is false - * Returns: - * true if node was added - */ - private bool _add(return scope Elem n) - { - Node result; - static if (!allowDuplicates) - bool added = true; - - if (!_end.left) - { - result = allocate(n); - (() @trusted { _end.left = _begin = result; }) (); - } - else - { - Node newParent = _end.left; - Node nxt; - while (true) - { - if (_less(n, newParent.value)) - { - nxt = newParent.left; - if (nxt is null) - { - // - // add to right of new parent - // - result = allocate(n); - (() @trusted { newParent.left = result; }) (); - break; - } - } - else - { - static if (!allowDuplicates) - { - if (!_less(newParent.value, n)) - { - result = newParent; - added = false; - break; - } - } - nxt = newParent.right; - if (nxt is null) - { - // - // add to right of new parent - // - result = allocate(n); - (() @trusted { newParent.right = result; }) (); - break; - } - } - newParent = nxt; - } - if (_begin.left) - _begin = _begin.left; - } - static if (allowDuplicates) - { - result.setColor(_end); - debug(RBDoChecks) - check(); - ++_length; - return true; - } - else - { - if (added) - { - ++_length; - result.setColor(_end); - } - debug(RBDoChecks) - check(); - return added; - } - } - - - /** - * Check if any elements exist in the container. Returns `false` if at least - * one element exists. - */ - @property bool empty() const // pure, nothrow, @safe, @nogc: are inferred - { - return _end.left is null; - } - - /++ - Returns the number of elements in the container. - - Complexity: $(BIGOH 1). - +/ - @property size_t length() const - { - return _length; - } - - /** - * Duplicate this container. The resulting container contains a shallow - * copy of the elements. - * - * Complexity: $(BIGOH n) - */ - @property RedBlackTree dup() - { - return new RedBlackTree(_end.dup(), _length); - } - - static if (doUnittest) @safe pure unittest - { - import std.algorithm.comparison : equal; - auto ts = new RedBlackTree(1, 2, 3, 4, 5); - assert(ts.length == 5); - auto ts2 = ts.dup; - assert(ts2.length == 5); - assert(equal(ts[], ts2[])); - ts2.insert(cast(Elem) 6); - assert(!equal(ts[], ts2[])); - assert(ts.length == 5 && ts2.length == 6); - } - - /** - * Fetch a range that spans all the elements in the container. - * - * Complexity: $(BIGOH 1) - */ - Range opSlice() - { - return Range(_begin, _end); - } - - /// Ditto - ConstRange opSlice() const - { - return ConstRange(_begin, _end); - } - - /// Ditto - ImmutableRange opSlice() immutable - { - return ImmutableRange(_begin, _end); - } - - /** - * The front element in the container - * - * Complexity: $(BIGOH 1) - */ - inout(Elem) front() inout - { - return _begin.value; - } - - /** - * The last element in the container - * - * Complexity: $(BIGOH log(n)) - */ - inout(Elem) back() inout - { - return _end.prev.value; - } - - /++ - `in` operator. Check to see if the given element exists in the - container. - - Complexity: $(BIGOH log(n)) - +/ - bool opBinaryRight(string op)(Elem e) const if (op == "in") - { - return _find(e) !is null; - } - - static if (doUnittest) @safe pure unittest - { - auto ts = new RedBlackTree(1, 2, 3, 4, 5); - assert(cast(Elem) 3 in ts); - assert(cast(Elem) 6 !in ts); - } - - /** - * Compares two trees for equality. - * - * Complexity: $(BIGOH n) - */ - override bool opEquals(Object rhs) - { - import std.algorithm.comparison : equal; - - RedBlackTree that = cast(RedBlackTree) rhs; - if (that is null) return false; - - // If there aren't the same number of nodes, we can't be equal. - if (this._length != that._length) return false; - - auto thisRange = this[]; - auto thatRange = that[]; - return equal!((Elem a, Elem b) => !_less(a,b) && !_less(b,a)) - (thisRange, thatRange); - } - - static if (doUnittest) @system unittest - { - auto t1 = new RedBlackTree(1,2,3,4); - auto t2 = new RedBlackTree(1,2,3,4); - auto t3 = new RedBlackTree(1,2,3,5); - auto t4 = new RedBlackTree(1,2,3,4,5); - auto o = new Object(); - - assert(t1 == t1); - assert(t1 == t2); - assert(t1 != t3); - assert(t1 != t4); - assert(t1 != o); // pathological case, must not crash - } - - /** - * Generates a hash for the tree. Note that with a custom comparison function - * it may not hold that if two rbtrees are equal, the hashes of the trees - * will be equal. - */ - override size_t toHash() nothrow @safe - { - size_t hash = cast(size_t) 0x6b63_616c_4264_6552UL; - foreach (ref e; this[]) - // As in boost::hash_combine - // https://www.boost.org/doc/libs/1_55_0/doc/html/hash/reference.html#boost.hash_combine - hash += .hashOf(e) + 0x9e3779b9 + (hash << 6) + (hash >>> 2); - return hash; - } - - static if (doUnittest) @system unittest - { - auto t1 = new RedBlackTree(1,2,3,4); - auto t2 = new RedBlackTree(1,2,3,4); - auto t3 = new RedBlackTree(1,2,3,5); - auto t4 = new RedBlackTree(1,2,3,4,5); - - assert(t1.toHash() == t2.toHash); - - assert(t1.toHash() != t3.toHash); - assert(t2.toHash() != t3.toHash); - - assert(t3.toHash() != t4.toHash); - assert(t1.toHash() != t4.toHash); - - // empty tree - auto t5 = new RedBlackTree(); - auto t6 = new RedBlackTree(); - - assert(t5.toHash() == t6.toHash()); - - auto t7 = new RedBlackTree!string("red", "black"); - auto t8 = new RedBlackTree!string("white", "black"); - auto t9 = new RedBlackTree!string("red", "black"); - - assert(t7.toHash() == t9.toHash()); - assert(t7.toHash() != t8.toHash()); - - static struct MyInt - { - int x; - - @safe: - - this(int init_x) - { - x = init_x; - } - - size_t toHash() const nothrow - { - return typeid(x).getHash(&x) ^ 0xF0F0_F0F0; - } - - int opCmp(const MyInt that) const - { - return (x > that.x) - (x < that.x); - } - - bool opEquals(const MyInt that) const - { - return (this.x == that.x); - } - } - - auto rbt1 = new RedBlackTree!MyInt(MyInt(1), MyInt(2), MyInt(3), MyInt(4)); - auto rbt2 = new RedBlackTree!MyInt(MyInt(1), MyInt(2), MyInt(3), MyInt(4)); - - assert(rbt1.toHash() == rbt2.toHash()); - assert(rbt1.toHash() != t1.toHash()); - - auto rbt3 = new RedBlackTree!MyInt(MyInt(4), MyInt(2), MyInt(3), MyInt(4)); - - assert(rbt1.toHash() != rbt3.toHash()); - - class MyInt2 - { - int x; - - this(int init_x) - { - x = init_x; - } - - override size_t toHash() const @safe nothrow - { - return typeid(x).getHash(&x) ^ 0xF0F0_F0F0; - } - - int opCmp(const MyInt2 that) const - { - return (x > that.x) - (x < that.x); - } - - bool opEquals(const MyInt2 that) const - { - return (this.x == that.x); - } - } - - static bool nullSafeLess(scope const MyInt2 a, scope const MyInt2 b) - { - return a is null ? b !is null : (b !is null && a < b); - } - - auto rbt4 = new RedBlackTree!MyInt2(new MyInt2(1), new MyInt2(9), new MyInt2(3), new MyInt2(42)); - auto rbt5 = new RedBlackTree!MyInt2(new MyInt2(1), new MyInt2(9), new MyInt2(3), new MyInt2(42)); - auto rbt6 = new RedBlackTree!(MyInt2, nullSafeLess)(new MyInt2(9), new MyInt2(3), new MyInt2(42)); - auto rbt7 = new RedBlackTree!(MyInt2, nullSafeLess)(null); - - assert(rbt6.toHash() != rbt5.toHash()); - assert(rbt6.toHash() != rbt4.toHash()); - assert(rbt6.toHash() != rbt7.toHash()); - assert(rbt4.toHash() == rbt5.toHash()); - - auto rbt8 = new RedBlackTree!(MyInt2, nullSafeLess)(null, new MyInt2(9), null, new MyInt2(42)); - auto rbt9 = new RedBlackTree!(MyInt2, nullSafeLess)(null, new MyInt2(9), null, new MyInt2(42)); - auto rbt10 = new RedBlackTree!(MyInt2, nullSafeLess)(new MyInt2(94), null, new MyInt2(147)); - - assert(rbt8.toHash() == rbt9.toHash()); - assert(rbt8.toHash() != rbt10.toHash()); - } - - /** - * Removes all elements from the container. - * - * Complexity: $(BIGOH 1) - */ - void clear() - { - _end.left = null; - _begin = _end; - _length = 0; - } - - static if (doUnittest) @safe pure unittest - { - auto ts = new RedBlackTree(1,2,3,4,5); - assert(ts.length == 5); - ts.clear(); - assert(ts.empty && ts.length == 0); - } - - /** - * Insert a single element in the container. Note that this does not - * invalidate any ranges currently iterating the container. - * - * Returns: The number of elements inserted. - * - * Complexity: $(BIGOH log(n)) - */ - size_t stableInsert(Stuff)(Stuff stuff) if (isImplicitlyConvertible!(Stuff, Elem)) - { - static if (allowDuplicates) - { - _add(stuff); - return 1; - } - else - { - return _add(stuff); - } - } - - /** - * Insert a range of elements in the container. Note that this does not - * invalidate any ranges currently iterating the container. - * - * Returns: The number of elements inserted. - * - * Complexity: $(BIGOH m * log(n)) - */ - size_t stableInsert(Stuff)(scope Stuff stuff) - if (isInputRange!Stuff && - isImplicitlyConvertible!(ElementType!Stuff, Elem)) - { - size_t result = 0; - static if (allowDuplicates) - { - foreach (e; stuff) - { - ++result; - _add(e); - } - } - else - { - foreach (e; stuff) - { - result += _add(e); - } - } - return result; - } - - /// ditto - alias insert = stableInsert; - - static if (doUnittest) @safe pure unittest - { - auto ts = new RedBlackTree(2,1,3,4,5,2,5); - static if (allowDuplicates) - { - assert(ts.length == 7); - assert(ts.stableInsert(cast(Elem[])[7, 8, 6, 9, 10, 8]) == 6); - assert(ts.length == 13); - assert(ts.stableInsert(cast(Elem) 11) == 1 && ts.length == 14); - assert(ts.stableInsert(cast(Elem) 7) == 1 && ts.length == 15); - - static if (less == "a < b") - assert(ts.arrayEqual([1,2,2,3,4,5,5,6,7,7,8,8,9,10,11])); - else - assert(ts.arrayEqual([11,10,9,8,8,7,7,6,5,5,4,3,2,2,1])); - } - else - { - assert(ts.length == 5); - Elem[] elems = [7, 8, 6, 9, 10, 8]; - assert(ts.stableInsert(elems) == 5); - assert(ts.length == 10); - assert(ts.stableInsert(cast(Elem) 11) == 1 && ts.length == 11); - assert(ts.stableInsert(cast(Elem) 7) == 0 && ts.length == 11); - - static if (less == "a < b") - assert(ts.arrayEqual([1,2,3,4,5,6,7,8,9,10,11])); - else - assert(ts.arrayEqual([11,10,9,8,7,6,5,4,3,2,1])); - } - } - - /** - * Remove an element from the container and return its value. - * - * Complexity: $(BIGOH log(n)) - */ - Elem removeAny() - { - scope(success) - --_length; - auto n = _begin; - auto result = n.value; - _begin = n.remove(_end); - debug(RBDoChecks) - check(); - return result; - } - - static if (doUnittest) @safe pure unittest - { - auto ts = new RedBlackTree(1,2,3,4,5); - assert(ts.length == 5); - auto x = ts.removeAny(); - assert(ts.length == 4); - Elem[] arr; - foreach (Elem i; 1 .. 6) - if (i != x) arr ~= i; - assert(ts.arrayEqual(arr)); - } - - /** - * Remove the front element from the container. - * - * Complexity: $(BIGOH log(n)) - */ - void removeFront() - { - scope(success) - --_length; - _begin = _begin.remove(_end); - debug(RBDoChecks) - check(); - } - - /** - * Remove the back element from the container. - * - * Complexity: $(BIGOH log(n)) - */ - void removeBack() - { - scope(success) - --_length; - auto lastnode = _end.prev; - if (lastnode is _begin) - _begin = _begin.remove(_end); - else - lastnode.remove(_end); - debug(RBDoChecks) - check(); - } - - static if (doUnittest) @safe pure unittest - { - auto ts = new RedBlackTree(1,2,3,4,5); - assert(ts.length == 5); - ts.removeBack(); - assert(ts.length == 4); - - static if (less == "a < b") - assert(ts.arrayEqual([1,2,3,4])); - else - assert(ts.arrayEqual([2,3,4,5])); - - ts.removeFront(); - assert(ts.arrayEqual([2,3,4]) && ts.length == 3); - } - - /++ - Removes the given range from the container. - - Returns: A range containing all of the elements that were after the - given range. - - Complexity: $(BIGOH m * log(n)) (where m is the number of elements in - the range) - +/ - Range remove(Range r) - { - auto b = r._begin; - auto e = r._end; - if (_begin is b) - _begin = e; - while (b !is e) - { - b = b.remove(_end); - --_length; - } - debug(RBDoChecks) - check(); - return Range(e, _end); - } - - static if (doUnittest) @safe pure unittest - { - import std.algorithm.comparison : equal; - auto ts = new RedBlackTree(1,2,3,4,5); - assert(ts.length == 5); - auto r = ts[]; - r.popFront(); - r.popBack(); - assert(ts.length == 5); - auto r2 = ts.remove(r); - assert(ts.length == 2); - assert(ts.arrayEqual([1,5])); - - static if (less == "a < b") - assert(equal(r2, [5])); - else - assert(equal(r2, [1])); - } - - /++ - Removes the given `Take!Range` from the container - - Returns: A range containing all of the elements that were after the - given range. - - Complexity: $(BIGOH m * log(n)) (where m is the number of elements in - the range) - +/ - Range remove(Take!Range r) - { - immutable isBegin = (r.source._begin is _begin); - auto b = r.source._begin; - - while (!r.empty) - { - r.popFront(); - b = b.remove(_end); - --_length; - } - - if (isBegin) - _begin = b; - - return Range(b, _end); - } - - static if (doUnittest) @safe pure unittest - { - import std.algorithm.comparison : equal; - import std.range : take; - auto ts = new RedBlackTree(1,2,3,4,5); - auto r = ts[]; - r.popFront(); - assert(ts.length == 5); - auto r2 = ts.remove(take(r, 0)); - - static if (less == "a < b") - { - assert(equal(r2, [2,3,4,5])); - auto r3 = ts.remove(take(r, 2)); - assert(ts.arrayEqual([1,4,5]) && ts.length == 3); - assert(equal(r3, [4,5])); - } - else - { - assert(equal(r2, [4,3,2,1])); - auto r3 = ts.remove(take(r, 2)); - assert(ts.arrayEqual([5,2,1]) && ts.length == 3); - assert(equal(r3, [2,1])); - } - } - - /++ - Removes elements from the container that are equal to the given values - according to the less comparator. One element is removed for each value - given which is in the container. If `allowDuplicates` is true, - duplicates are removed only if duplicate values are given. - - Returns: The number of elements removed. - - Complexity: $(BIGOH m log(n)) (where m is the number of elements to remove) - - Example: --------------------- -auto rbt = redBlackTree!true(0, 1, 1, 1, 4, 5, 7); -rbt.removeKey(1, 4, 7); -assert(equal(rbt[], [0, 1, 1, 5])); -rbt.removeKey(1, 1, 0); -assert(equal(rbt[], [5])); --------------------- - +/ - size_t removeKey(U...)(U elems) - if (allSatisfy!(isImplicitlyConvertibleToElem, U)) - { - Elem[U.length] toRemove = [elems]; - return removeKey(toRemove[]); - } - - /++ Ditto +/ - size_t removeKey(U)(scope U[] elems) - if (isImplicitlyConvertible!(U, Elem)) - { - immutable lenBefore = length; - - foreach (e; elems) - { - auto beg = _firstGreaterEqual(e); - if (beg is _end || _less(e, beg.value)) - // no values are equal - continue; - immutable isBegin = (beg is _begin); - beg = beg.remove(_end); - if (isBegin) - _begin = beg; - --_length; - } - - return lenBefore - length; - } - - /++ Ditto +/ - size_t removeKey(Stuff)(Stuff stuff) - if (isInputRange!Stuff && - isImplicitlyConvertible!(ElementType!Stuff, Elem) && - !isDynamicArray!Stuff) - { - import std.array : array; - //We use array in case stuff is a Range from this RedBlackTree - either - //directly or indirectly. - return removeKey(array(stuff)); - } - - //Helper for removeKey. - private template isImplicitlyConvertibleToElem(U) - { - enum isImplicitlyConvertibleToElem = isImplicitlyConvertible!(U, Elem); - } - - static if (doUnittest) @safe pure unittest - { - import std.algorithm.comparison : equal; - import std.range : take; - auto rbt = new RedBlackTree(5, 4, 3, 7, 2, 1, 7, 6, 2, 19, 45); - - //The cast(Elem) is because these tests are instantiated with a variety - //of numeric types, and the literals are all int, which is not always - //implicitly convertible to Elem (e.g. short). - static if (allowDuplicates) - { - assert(rbt.length == 11); - assert(rbt.removeKey(cast(Elem) 4) == 1 && rbt.length == 10); - assert(rbt.arrayEqual([1,2,2,3,5,6,7,7,19,45]) && rbt.length == 10); - - assert(rbt.removeKey(cast(Elem) 6, cast(Elem) 2, cast(Elem) 1) == 3); - assert(rbt.arrayEqual([2,3,5,7,7,19,45]) && rbt.length == 7); - - assert(rbt.removeKey(cast(Elem)(42)) == 0 && rbt.length == 7); - assert(rbt.removeKey(take(rbt[], 3)) == 3 && rbt.length == 4); - - static if (less == "a < b") - assert(equal(rbt[], [7,7,19,45])); - else - assert(equal(rbt[], [7,5,3,2])); - } - else - { - assert(rbt.length == 9); - assert(rbt.removeKey(cast(Elem) 4) == 1 && rbt.length == 8); - assert(rbt.arrayEqual([1,2,3,5,6,7,19,45])); - - assert(rbt.removeKey(cast(Elem) 6, cast(Elem) 2, cast(Elem) 1) == 3); - assert(rbt.arrayEqual([3,5,7,19,45]) && rbt.length == 5); - - assert(rbt.removeKey(cast(Elem)(42)) == 0 && rbt.length == 5); - assert(rbt.removeKey(take(rbt[], 3)) == 3 && rbt.length == 2); - - static if (less == "a < b") - assert(equal(rbt[], [19,45])); - else - assert(equal(rbt[], [5,3])); - } - } - - // find the first node where the value is > e - private inout(RBNode)* _firstGreater(Elem e) inout - { - // can't use _find, because we cannot return null - auto cur = _end.left; - inout(RBNode)* result = _end; - while (cur) - { - if (_less(e, cur.value)) - { - result = cur; - cur = cur.left; - } - else - cur = cur.right; - } - return result; - } - - // find the first node where the value is >= e - private inout(RBNode)* _firstGreaterEqual(Elem e) inout - { - // can't use _find, because we cannot return null. - auto cur = _end.left; - inout(RBNode)* result = _end; - while (cur) - { - if (_less(cur.value, e)) - cur = cur.right; - else - { - result = cur; - cur = cur.left; - } - - } - return result; - } - - /** - * Get a range from the container with all elements that are > e according - * to the less comparator - * - * Complexity: $(BIGOH log(n)) - */ - Range upperBound(Elem e) - { - return Range(_firstGreater(e), _end); - } - - /// Ditto - ConstRange upperBound(Elem e) const - { - return ConstRange(_firstGreater(e), _end); - } - - /// Ditto - ImmutableRange upperBound(Elem e) immutable - { - return ImmutableRange(_firstGreater(e), _end); - } - - /** - * Get a range from the container with all elements that are < e according - * to the less comparator - * - * Complexity: $(BIGOH log(n)) - */ - Range lowerBound(Elem e) - { - return Range(_begin, _firstGreaterEqual(e)); - } - - /// Ditto - ConstRange lowerBound(Elem e) const - { - return ConstRange(_begin, _firstGreaterEqual(e)); - } - - /// Ditto - ImmutableRange lowerBound(Elem e) immutable - { - return ImmutableRange(_begin, _firstGreaterEqual(e)); - } - - /** - * Get a range from the container with all elements that are == e according - * to the less comparator - * - * Complexity: $(BIGOH log(n)) - */ - auto equalRange(this This)(Elem e) - { - auto beg = _firstGreaterEqual(e); - alias RangeType = RBRange!(typeof(beg)); - if (beg is _end || _less(e, beg.value)) - // no values are equal - return RangeType(beg, beg); - static if (allowDuplicates) - { - return RangeType(beg, _firstGreater(e)); - } - else - { - // no sense in doing a full search, no duplicates are allowed, - // so we just get the next node. - return RangeType(beg, beg.next); - } - } - - static if (doUnittest) @safe pure unittest - { - import std.algorithm.comparison : equal; - auto ts = new RedBlackTree(1, 2, 3, 4, 5); - auto rl = ts.lowerBound(3); - auto ru = ts.upperBound(3); - auto re = ts.equalRange(3); - - static if (less == "a < b") - { - assert(equal(rl, [1,2])); - assert(equal(ru, [4,5])); - } - else - { - assert(equal(rl, [5,4])); - assert(equal(ru, [2,1])); - } - - assert(equal(re, [3])); - } - - debug(RBDoChecks) - { - /* - * Print the tree. This prints a sideways view of the tree in ASCII form, - * with the number of indentations representing the level of the nodes. - * It does not print values, only the tree structure and color of nodes. - */ - void printTree(Node n, int indent = 0) - { - import std.stdio : write, writeln; - if (n !is null) - { - printTree(n.right, indent + 2); - for (int i = 0; i < indent; i++) - write("."); - writeln(n.color == n.color.Black ? "B" : "R"); - printTree(n.left, indent + 2); - } - else - { - for (int i = 0; i < indent; i++) - write("."); - writeln("N"); - } - if (indent is 0) - writeln(); - } - - /* - * Check the tree for validity. This is called after every add or remove. - * This should only be enabled to debug the implementation of the RB Tree. - */ - void check() @trusted - { - // - // check implementation of the tree - // - int recurse(Node n, string path) - { - import std.stdio : writeln; - if (n is null) - return 1; - if (n.parent.left !is n && n.parent.right !is n) - throw new Exception("Node at path " ~ path ~ " has inconsistent pointers"); - Node next = n.next; - static if (allowDuplicates) - { - if (next !is _end && _less(next.value, n.value)) - throw new Exception("ordering invalid at path " ~ path); - } - else - { - if (next !is _end && !_less(n.value, next.value)) - throw new Exception("ordering invalid at path " ~ path); - } - if (n.color == n.color.Red) - { - if ((n.left !is null && n.left.color == n.color.Red) || - (n.right !is null && n.right.color == n.color.Red)) - throw new Exception("Node at path " ~ path ~ " is red with a red child"); - } - - int l = recurse(n.left, path ~ "L"); - int r = recurse(n.right, path ~ "R"); - if (l != r) - { - writeln("bad tree at:"); - debug printTree(n); - throw new Exception( - "Node at path " ~ path ~ " has different number of black nodes on left and right paths" - ); - } - return l + (n.color == n.color.Black ? 1 : 0); - } - - try - { - recurse(_end.left, ""); - } - catch (Exception e) - { - debug printTree(_end.left, 0); - throw e; - } - } - } - - /** - Formats the RedBlackTree into a sink function. For more info see $(D - std.format.formatValue). Note that this only is available when the - element type can be formatted. Otherwise, the default toString from - Object is used. - */ - static if (is(typeof((){FormatSpec!(char) fmt; formatValue((const(char)[]) {}, ConstRange.init, fmt);}))) - { - void toString(scope void delegate(const(char)[]) sink, scope const ref FormatSpec!char fmt) const - { - sink("RedBlackTree("); - sink.formatValue(this[], fmt); - sink(")"); - } - } - - /** - * Constructor. Pass in an array of elements, or individual elements to - * initialize the tree with. - */ - this(Elem[] elems...) - { - _setup(); - stableInsert(elems); - } - - /** - * Constructor. Pass in a range of elements to initialize the tree with. - */ - this(Stuff)(Stuff stuff) if (isInputRange!Stuff && isImplicitlyConvertible!(ElementType!Stuff, Elem)) - { - _setup(); - stableInsert(stuff); - } - - /// - this() - { - _setup(); - } - - private this(Node end, size_t length) - { - _end = end; - _begin = end.leftmost; - _length = length; - } -} - -//Verify Example for removeKey. -@safe pure unittest -{ - import std.algorithm.comparison : equal; - auto rbt = redBlackTree!true(0, 1, 1, 1, 4, 5, 7); - rbt.removeKey(1, 4, 7); - assert(equal(rbt[], [0, 1, 1, 5])); - rbt.removeKey(1, 1, 0); - assert(equal(rbt[], [5])); -} - -//Tests for removeKey -@safe pure unittest -{ - import std.algorithm.comparison : equal; - { - auto rbt = redBlackTree(["hello", "world", "foo", "bar"]); - assert(equal(rbt[], ["bar", "foo", "hello", "world"])); - assert(rbt.removeKey("hello") == 1); - assert(equal(rbt[], ["bar", "foo", "world"])); - assert(rbt.removeKey("hello") == 0); - assert(equal(rbt[], ["bar", "foo", "world"])); - assert(rbt.removeKey("hello", "foo", "bar") == 2); - assert(equal(rbt[], ["world"])); - assert(rbt.removeKey(["", "world", "hello"]) == 1); - assert(rbt.empty); - } - - { - auto rbt = redBlackTree([1, 2, 12, 27, 4, 500]); - assert(equal(rbt[], [1, 2, 4, 12, 27, 500])); - assert(rbt.removeKey(1u) == 1); - assert(equal(rbt[], [2, 4, 12, 27, 500])); - assert(rbt.removeKey(cast(byte) 1) == 0); - assert(equal(rbt[], [2, 4, 12, 27, 500])); - assert(rbt.removeKey(1, 12u, cast(byte) 27) == 2); - assert(equal(rbt[], [2, 4, 500])); - assert(rbt.removeKey([cast(short) 0, cast(short) 500, cast(short) 1]) == 1); - assert(equal(rbt[], [2, 4])); - } -} - -@safe pure unittest -{ - void test(T)() - { - auto rt1 = new RedBlackTree!(T, "a < b", false)(); - auto rt2 = new RedBlackTree!(T, "a < b", true)(); - auto rt3 = new RedBlackTree!(T, "a > b", false)(); - auto rt4 = new RedBlackTree!(T, "a > b", true)(); - } - - test!long(); - test!ulong(); - test!int(); - test!uint(); - test!short(); - test!ushort(); - test!byte(); - test!byte(); -} - -// https://issues.dlang.org/show_bug.cgi?id=19626 -@safe pure unittest -{ - enum T { a, b } - alias t = RedBlackTree!T; -} - -import std.range.primitives : isInputRange, ElementType; -import std.traits : isArray, isSomeString; - -/++ - Convenience function for creating a `RedBlackTree!E` from a list of - values. - - Params: - allowDuplicates = Whether duplicates should be allowed (optional, default: false) - less = predicate to sort by (optional) - elems = elements to insert into the rbtree (variadic arguments) - range = range elements to insert into the rbtree (alternative to elems) - +/ -auto redBlackTree(E)(E[] elems...) -{ - return new RedBlackTree!E(elems); -} - -/++ Ditto +/ -auto redBlackTree(bool allowDuplicates, E)(E[] elems...) -{ - return new RedBlackTree!(E, "a < b", allowDuplicates)(elems); -} - -/++ Ditto +/ -auto redBlackTree(alias less, E)(E[] elems...) -if (is(typeof(binaryFun!less(E.init, E.init)))) -{ - return new RedBlackTree!(E, less)(elems); -} - -/++ Ditto +/ -auto redBlackTree(alias less, bool allowDuplicates, E)(E[] elems...) -if (is(typeof(binaryFun!less(E.init, E.init)))) -{ - //We shouldn't need to instantiate less here, but for some reason, - //dmd can't handle it if we don't (even though the template which - //takes less but not allowDuplicates works just fine). - return new RedBlackTree!(E, binaryFun!less, allowDuplicates)(elems); -} - -/++ Ditto +/ -auto redBlackTree(Stuff)(Stuff range) -if (isInputRange!Stuff && !isArray!(Stuff)) -{ - return new RedBlackTree!(ElementType!Stuff)(range); -} - -/++ Ditto +/ -auto redBlackTree(bool allowDuplicates, Stuff)(Stuff range) -if (isInputRange!Stuff && !isArray!(Stuff)) -{ - return new RedBlackTree!(ElementType!Stuff, "a < b", allowDuplicates)(range); -} - -/++ Ditto +/ -auto redBlackTree(alias less, Stuff)(Stuff range) -if ( is(typeof(binaryFun!less((ElementType!Stuff).init, (ElementType!Stuff).init))) - && isInputRange!Stuff && !isArray!(Stuff)) -{ - return new RedBlackTree!(ElementType!Stuff, less)(range); -} - -/++ Ditto +/ -auto redBlackTree(alias less, bool allowDuplicates, Stuff)(Stuff range) -if ( is(typeof(binaryFun!less((ElementType!Stuff).init, (ElementType!Stuff).init))) - && isInputRange!Stuff && !isArray!(Stuff)) -{ - //We shouldn't need to instantiate less here, but for some reason, - //dmd can't handle it if we don't (even though the template which - //takes less but not allowDuplicates works just fine). - return new RedBlackTree!(ElementType!Stuff, binaryFun!less, allowDuplicates)(range); -} - -/// -@safe pure unittest -{ - import std.range : iota; - - auto rbt1 = redBlackTree(0, 1, 5, 7); - auto rbt2 = redBlackTree!string("hello", "world"); - auto rbt3 = redBlackTree!true(0, 1, 5, 7, 5); - auto rbt4 = redBlackTree!"a > b"(0, 1, 5, 7); - auto rbt5 = redBlackTree!("a > b", true)(0.1, 1.3, 5.9, 7.2, 5.9); - - // also works with ranges - auto rbt6 = redBlackTree(iota(3)); - auto rbt7 = redBlackTree!true(iota(3)); - auto rbt8 = redBlackTree!"a > b"(iota(3)); - auto rbt9 = redBlackTree!("a > b", true)(iota(3)); -} - -//Combinations not in examples. -@system pure unittest -{ - auto rbt1 = redBlackTree!(true, string)("hello", "hello"); - auto rbt2 = redBlackTree!((a, b){return a < b;}, double)(5.1, 2.3); - auto rbt3 = redBlackTree!("a > b", true, string)("hello", "world"); -} - -//Range construction. -@safe pure unittest -{ - import std.algorithm.comparison : equal; - import std.range : iota; - auto rbt = new RedBlackTree!(int, "a > b")(iota(5)); - assert(equal(rbt[], [4, 3, 2, 1, 0])); -} - - -// construction with arrays -@safe pure unittest -{ - import std.algorithm.comparison : equal; - - auto rbt = redBlackTree!"a > b"([0, 1, 2, 3, 4]); - assert(equal(rbt[], [4, 3, 2, 1, 0])); - - auto rbt2 = redBlackTree!"a > b"(["a", "b"]); - assert(equal(rbt2[], ["b", "a"])); - - auto rbt3 = redBlackTree!"a > b"([1, 2]); - assert(equal(rbt3[], [2, 1])); - - auto rbt4 = redBlackTree([0, 1, 7, 5]); - assert(equal(rbt4[], [0, 1, 5, 7])); - - auto rbt5 = redBlackTree(["hello", "world"]); - assert(equal(rbt5[], ["hello", "world"])); - - auto rbt6 = redBlackTree!true([0, 1, 5, 7, 5]); - assert(equal(rbt6[], [0, 1, 5, 5, 7])); - - auto rbt7 = redBlackTree!"a > b"([0, 1, 5, 7]); - assert(equal(rbt7[], [7, 5, 1, 0])); - - auto rbt8 = redBlackTree!("a > b", true)([0.1, 1.3, 5.9, 7.2, 5.9]); - assert(equal(rbt8[], [7.2, 5.9, 5.9, 1.3, 0.1])); -} - -// convenience wrapper range construction -@safe pure unittest -{ - import std.algorithm.comparison : equal; - import std.range : chain, iota; - - auto rbt = redBlackTree(iota(3)); - assert(equal(rbt[], [0, 1, 2])); - - auto rbt2 = redBlackTree!"a > b"(iota(2)); - assert(equal(rbt2[], [1, 0])); - - auto rbt3 = redBlackTree(chain([0, 1], [7, 5])); - assert(equal(rbt3[], [0, 1, 5, 7])); - - auto rbt4 = redBlackTree(chain(["hello"], ["world"])); - assert(equal(rbt4[], ["hello", "world"])); - - auto rbt5 = redBlackTree!true(chain([0, 1], [5, 7, 5])); - assert(equal(rbt5[], [0, 1, 5, 5, 7])); - - auto rbt6 = redBlackTree!("a > b", true)(chain([0.1, 1.3], [5.9, 7.2, 5.9])); - assert(equal(rbt6[], [7.2, 5.9, 5.9, 1.3, 0.1])); -} - -@safe pure unittest -{ - import std.array : array; - - auto rt1 = redBlackTree(5, 4, 3, 2, 1); - assert(rt1.length == 5); - assert(array(rt1[]) == [1, 2, 3, 4, 5]); - - auto rt2 = redBlackTree!"a > b"(1.1, 2.1); - assert(rt2.length == 2); - assert(array(rt2[]) == [2.1, 1.1]); - - auto rt3 = redBlackTree!true(5, 5, 4); - assert(rt3.length == 3); - assert(array(rt3[]) == [4, 5, 5]); - - auto rt4 = redBlackTree!string("hello", "hello"); - assert(rt4.length == 1); - assert(array(rt4[]) == ["hello"]); -} - -@system unittest -{ - import std.conv : to; - - auto rt1 = redBlackTree!string(); - assert(rt1.to!string == "RedBlackTree([])"); - - auto rt2 = redBlackTree!string("hello"); - assert(rt2.to!string == "RedBlackTree([\"hello\"])"); - - auto rt3 = redBlackTree!string("hello", "world", "!"); - assert(rt3.to!string == "RedBlackTree([\"!\", \"hello\", \"world\"])"); - - // type deduction can be done automatically - auto rt4 = redBlackTree(["hello"]); - assert(rt4.to!string == "RedBlackTree([\"hello\"])"); -} - -//constness checks -@safe pure unittest -{ - const rt1 = redBlackTree(5,4,3,2,1); - void allQualifiers() pure nothrow @safe @nogc { - assert(!rt1.empty); - assert(rt1.length == 5); - assert(5 in rt1); - } - allQualifiers(); - - static assert(is(typeof(rt1.upperBound(3).front) == const(int))); - import std.algorithm.comparison : equal; - assert(rt1.upperBound(3).equal([4, 5])); - assert(rt1.lowerBound(3).equal([1, 2])); - assert(rt1.equalRange(3).equal([3])); - assert(rt1[].equal([1, 2, 3, 4, 5])); -} - -//immutable checks -@safe pure unittest -{ - immutable rt1 = redBlackTree(5,4,3,2,1); - static assert(is(typeof(rt1.empty))); - static assert(is(typeof(rt1.length))); - static assert(is(typeof(5 in rt1))); - - static assert(is(typeof(rt1.upperBound(3).front) == immutable(int))); - import std.algorithm.comparison : equal; - assert(rt1.upperBound(2).equal([3, 4, 5])); -} - -// https://issues.dlang.org/show_bug.cgi?id=15941 -@safe pure unittest -{ - class C {} - RedBlackTree!(C, "cast(void*)a < cast(void*) b") tree; -} - -// const/immutable elements (https://issues.dlang.org/show_bug.cgi?id=17519) -@safe pure unittest -{ - RedBlackTree!(immutable int) t1; - RedBlackTree!(const int) t2; - - import std.algorithm.iteration : map; - static struct S { int* p; } - auto t3 = new RedBlackTree!(immutable S, (a, b) => *a.p < *b.p); - t3.insert([1, 2, 3].map!(x => immutable S(new int(x)))); - static assert(!__traits(compiles, *t3.front.p = 4)); - assert(*t3.front.p == 1); -} - -// make sure the comparator can be a delegate -@safe pure unittest -{ - import std.algorithm.comparison : equal; - - auto t = new RedBlackTree!(int, delegate(a, b) => a > b); - t.insert([1, 3, 5, 4, 2]); - assert(t[].equal([5, 4, 3, 2, 1])); -} diff --git a/phobos/std/container/slist.d b/phobos/std/container/slist.d deleted file mode 100644 index 0b504b4..0000000 --- a/phobos/std/container/slist.d +++ /dev/null @@ -1,940 +0,0 @@ -/** -This module implements a singly-linked list container. -It can be used as a stack. - -This module is a submodule of $(MREF std, container). - -Source: $(PHOBOSSRC std/container/slist.d) - -Copyright: 2010- Andrei Alexandrescu. All rights reserved by the respective holders. - -License: Distributed under the Boost Software License, Version 1.0. -(See accompanying file LICENSE_1_0.txt or copy at $(HTTP -boost.org/LICENSE_1_0.txt)). - -Authors: $(HTTP erdani.com, Andrei Alexandrescu) - -$(SCRIPT inhibitQuickIndex = 1;) -*/ -module std.container.slist; - -/// -@safe unittest -{ - import std.algorithm.comparison : equal; - import std.container : SList; - - auto s = SList!int(1, 2, 3); - assert(equal(s[], [1, 2, 3])); - - s.removeFront(); - assert(equal(s[], [2, 3])); - - s.insertFront([5, 6]); - assert(equal(s[], [5, 6, 2, 3])); - - // If you want to apply range operations, simply slice it. - import std.algorithm.searching : countUntil; - import std.range : popFrontN, walkLength; - - auto sl = SList!int(1, 2, 3, 4, 5); - assert(countUntil(sl[], 2) == 1); - - auto r = sl[]; - popFrontN(r, 2); - assert(walkLength(r) == 3); -} - -public import std.container.util; - -/** - Implements a simple and fast singly-linked list. - It can be used as a stack. - - `SList` uses reference semantics. - */ -struct SList(T) -if (!is(T == shared)) -{ - import std.exception : enforce; - import std.range : Take; - import std.range.primitives : isInputRange, isForwardRange, ElementType; - import std.traits : isImplicitlyConvertible; - - private struct Node - { - Node * _next; - T _payload; - } - private struct NodeWithoutPayload - { - Node* _next; - } - static assert(NodeWithoutPayload._next.offsetof == Node._next.offsetof); - - private Node * _root; - - private void initialize() @trusted nothrow pure - { - if (_root) return; - _root = cast (Node*) new NodeWithoutPayload(); - } - - private ref inout(Node*) _first() @property @safe nothrow pure inout - { - assert(_root, "root pointer must not be null"); - return _root._next; - } - - private static Node * findLastNode(Node * n) - { - assert(n, "Node n pointer must not be null"); - auto ahead = n._next; - while (ahead) - { - n = ahead; - ahead = n._next; - } - return n; - } - - private static Node * findLastNode(Node * n, size_t limit) - { - assert(n, "Node n pointer must not be null"); - assert(limit, "limit must be greater than 0"); - auto ahead = n._next; - while (ahead) - { - if (!--limit) break; - n = ahead; - ahead = n._next; - } - return n; - } - - private static Node * findNode(Node * n, Node * findMe) - { - assert(n, "Node n pointer must not be null"); - auto ahead = n._next; - while (ahead != findMe) - { - n = ahead; - enforce(n); - ahead = n._next; - } - return n; - } - - private static Node* findNodeByValue(Node* n, T value) - { - if (!n) return null; - auto ahead = n._next; - while (ahead && ahead._payload != value) - { - n = ahead; - ahead = n._next; - } - return n; - } - - private static auto createNodeChain(Stuff)(Stuff stuff) - if (isImplicitlyConvertible!(Stuff, T)) - { - import std.range : only; - return createNodeChain(only(stuff)); - } - - private static auto createNodeChain(Stuff)(Stuff stuff) - if (isInputRange!Stuff && isImplicitlyConvertible!(ElementType!Stuff, T)) - { - static struct Chain - { - Node* first; - Node* last; - size_t length; - } - - Chain ch; - - foreach (item; stuff) - { - auto newNode = new Node(null, item); - (ch.first ? ch.last._next : ch.first) = newNode; - ch.last = newNode; - ++ch.length; - } - - return ch; - } - - private static size_t insertAfterNode(Stuff)(Node* n, Stuff stuff) - { - auto ch = createNodeChain(stuff); - - if (!ch.length) return 0; - - ch.last._next = n._next; - n._next = ch.first; - - return ch.length; - } - -/** -Constructor taking a number of nodes - */ - this(U)(U[] values...) if (isImplicitlyConvertible!(U, T)) - { - insertFront(values); - } - -/** -Constructor taking an $(REF_ALTTEXT input range, isInputRange, std,range,primitives) - */ - this(Stuff)(Stuff stuff) - if (isInputRange!Stuff - && isImplicitlyConvertible!(ElementType!Stuff, T) - && !is(Stuff == T[])) - { - insertFront(stuff); - } - -/** -Comparison for equality. - -Complexity: $(BIGOH min(n, n1)) where `n1` is the number of -elements in `rhs`. - */ - bool opEquals(const SList rhs) const - { - return opEquals(rhs); - } - - /// ditto - bool opEquals(ref const SList rhs) const - { - if (_root is rhs._root) return true; - if (_root is null) return rhs._root is null || rhs._first is null; - if (rhs._root is null) return _root is null || _first is null; - - const(Node) * n1 = _first, n2 = rhs._first; - - for (;; n1 = n1._next, n2 = n2._next) - { - if (!n1) return !n2; - if (!n2 || n1._payload != n2._payload) return false; - } - } - -/** -Defines the container's primary range, which embodies a forward range. - */ - struct Range - { - private Node * _head; - private this(Node * p) { _head = p; } - - /// Input range primitives. - @property bool empty() const { return !_head; } - - /// ditto - @property ref T front() - { - assert(!empty, "SList.Range.front: Range is empty"); - return _head._payload; - } - - /// ditto - void popFront() - { - assert(!empty, "SList.Range.popFront: Range is empty"); - _head = _head._next; - } - - /// Forward range primitive. - @property Range save() { return this; } - - T moveFront() - { - import std.algorithm.mutation : move; - - assert(!empty, "SList.Range.moveFront: Range is empty"); - return move(_head._payload); - } - - bool sameHead(Range rhs) - { - return _head && _head == rhs._head; - } - } - - @safe unittest - { - static assert(isForwardRange!Range); - } - -/** -Property returning `true` if and only if the container has no -elements. - -Complexity: $(BIGOH 1) - */ - @property bool empty() const - { - return _root is null || _first is null; - } - -/** -Duplicates the container. The elements themselves are not transitively -duplicated. - -Complexity: $(BIGOH n). - */ - @property SList dup() - { - return SList(this[]); - } - -/** -Returns a range that iterates over all elements of the container, in -forward order. - -Complexity: $(BIGOH 1) - */ - Range opSlice() - { - if (empty) - return Range(null); - else - return Range(_first); - } - -/** -Forward to `opSlice().front`. - -Complexity: $(BIGOH 1) - */ - @property ref T front() - { - assert(!empty, "SList.front: List is empty"); - return _first._payload; - } - - @safe unittest - { - auto s = SList!int(1, 2, 3); - s.front = 42; - assert(s == SList!int(42, 2, 3)); - } - -/** -Returns a new `SList` that's the concatenation of `this` and its -argument. `opBinaryRight` is only defined if `Stuff` does not -define `opBinary`. - */ - SList opBinary(string op, Stuff)(Stuff rhs) - if (op == "~" && is(typeof(SList(rhs)))) - { - import std.range : chain, only; - - static if (isInputRange!Stuff) - alias r = rhs; - else - auto r = only(rhs); - - return SList(this[].chain(r)); - } - - /// ditto - SList opBinaryRight(string op, Stuff)(Stuff lhs) - if (op == "~" && !is(typeof(lhs.opBinary!"~"(this))) && is(typeof(SList(lhs)))) - { - import std.range : chain, only; - - static if (isInputRange!Stuff) - alias r = lhs; - else - auto r = only(lhs); - - return SList(r.chain(this[])); - } - -/** -Removes all contents from the `SList`. - -Postcondition: `empty` - -Complexity: $(BIGOH 1) - */ - void clear() - { - if (_root) - _first = null; - } - -/** -Reverses SList in-place. Performs no memory allocation. - -Complexity: $(BIGOH n) - */ - void reverse() - { - if (!empty) - { - Node* prev; - while (_first) - { - auto next = _first._next; - _first._next = prev; - prev = _first; - _first = next; - } - _first = prev; - } - } - -/** -Inserts `stuff` to the front of the container. `stuff` can be a -value convertible to `T` or a range of objects convertible to $(D -T). The stable version behaves the same, but guarantees that ranges -iterating over the container are never invalidated. - -Returns: The number of elements inserted - -Complexity: $(BIGOH m), where `m` is the length of `stuff` - */ - size_t insertFront(Stuff)(Stuff stuff) - if (isInputRange!Stuff || isImplicitlyConvertible!(Stuff, T)) - { - initialize(); - return insertAfterNode(_root, stuff); - } - - /// ditto - alias insert = insertFront; - - /// ditto - alias stableInsert = insert; - - /// ditto - alias stableInsertFront = insertFront; - -/** -Picks one value in an unspecified position in the container, removes -it from the container, and returns it. The stable version behaves the same, -but guarantees that ranges iterating over the container are never invalidated. - -Precondition: `!empty` - -Returns: The element removed. - -Complexity: $(BIGOH 1). - */ - T removeAny() - { - import std.algorithm.mutation : move; - - assert(!empty, "SList.removeAny: List is empty"); - auto result = move(_first._payload); - _first = _first._next; - return result; - } - /// ditto - alias stableRemoveAny = removeAny; - -/** -Removes the value at the front of the container. The stable version -behaves the same, but guarantees that ranges iterating over the -container are never invalidated. - -Precondition: `!empty` - -Complexity: $(BIGOH 1). - */ - void removeFront() - { - assert(!empty, "SList.removeFront: List is empty"); - _first = _first._next; - } - - /// ditto - alias stableRemoveFront = removeFront; - -/** -Removes `howMany` values at the front or back of the -container. Unlike the unparameterized versions above, these functions -do not throw if they could not remove `howMany` elements. Instead, -if $(D howMany > n), all elements are removed. The returned value is -the effective number of elements removed. The stable version behaves -the same, but guarantees that ranges iterating over the container are -never invalidated. - -Returns: The number of elements removed - -Complexity: $(BIGOH howMany * log(n)). - */ - size_t removeFront(size_t howMany) - { - size_t result; - while (_first && result < howMany) - { - _first = _first._next; - ++result; - } - return result; - } - - /// ditto - alias stableRemoveFront = removeFront; - -/** -Inserts `stuff` after range `r`, which must be a range -previously extracted from this container. Given that all ranges for a -list end at the end of the list, this function essentially appends to -the list and uses `r` as a potentially fast way to reach the last -node in the list. Ideally `r` is positioned near or at the last -element of the list. - -`stuff` can be a value convertible to `T` or a range of objects -convertible to `T`. The stable version behaves the same, but -guarantees that ranges iterating over the container are never -invalidated. - -Returns: The number of values inserted. - -Complexity: $(BIGOH k + m), where `k` is the number of elements in -`r` and `m` is the length of `stuff`. - -Example: --------------------- -auto sl = SList!string(["a", "b", "d"]); -sl.insertAfter(sl[], "e"); // insert at the end (slowest) -assert(std.algorithm.equal(sl[], ["a", "b", "d", "e"])); -sl.insertAfter(std.range.take(sl[], 2), "c"); // insert after "b" -assert(std.algorithm.equal(sl[], ["a", "b", "c", "d", "e"])); --------------------- - */ - - size_t insertAfter(Stuff)(Range r, Stuff stuff) - if (isInputRange!Stuff || isImplicitlyConvertible!(Stuff, T)) - { - initialize(); - if (!_first) - { - enforce(!r._head); - return insertFront(stuff); - } - enforce(r._head); - auto n = findLastNode(r._head); - return insertAfterNode(n, stuff); - } - -/** -Similar to `insertAfter` above, but accepts a range bounded in -count. This is important for ensuring fast insertions in the middle of -the list. For fast insertions after a specified position `r`, use -$(D insertAfter(take(r, 1), stuff)). The complexity of that operation -only depends on the number of elements in `stuff`. - -Precondition: $(D r.original.empty || r.maxLength > 0) - -Returns: The number of values inserted. - -Complexity: $(BIGOH k + m), where `k` is the number of elements in -`r` and `m` is the length of `stuff`. - */ - size_t insertAfter(Stuff)(Take!Range r, Stuff stuff) - if (isInputRange!Stuff || isImplicitlyConvertible!(Stuff, T)) - { - auto orig = r.source; - if (!orig._head) - { - // Inserting after a null range counts as insertion to the - // front - return insertFront(stuff); - } - enforce(!r.empty); - // Find the last valid element in the range - foreach (i; 1 .. r.maxLength) - { - if (!orig._head._next) break; - orig.popFront(); - } - // insert here - return insertAfterNode(orig._head, stuff); - } - -/// ditto - alias stableInsertAfter = insertAfter; - -/** -Removes a range from the list in linear time. - -Returns: An empty range. - -Complexity: $(BIGOH n) - */ - Range linearRemove(Range r) - { - if (!_first) - { - enforce(!r._head); - return this[]; - } - auto n = findNode(_root, r._head); - n._next = null; - return Range(null); - } - -/** -Removes a `Take!Range` from the list in linear time. - -Returns: A range comprehending the elements after the removed range. - -Complexity: $(BIGOH n) - */ - Range linearRemove(Take!Range r) - { - auto orig = r.source; - // We have something to remove here - if (orig._head == _first) - { - // remove straight from the head of the list - for (; !r.empty; r.popFront()) - { - removeFront(); - } - return this[]; - } - if (!r.maxLength) - { - // Nothing to remove, return the range itself - return orig; - } - // Remove from somewhere in the middle of the list - enforce(_first); - auto n1 = findNode(_root, orig._head); - auto n2 = findLastNode(orig._head, r.maxLength); - n1._next = n2._next; - return Range(n1._next); - } - -/// ditto - alias stableLinearRemove = linearRemove; - -/** -Removes the first occurence of an element from the list in linear time. - -Returns: True if the element existed and was successfully removed, false otherwise. - -Params: - value = value of the node to be removed - -Complexity: $(BIGOH n) - */ - bool linearRemoveElement(T value) - { - auto n1 = findNodeByValue(_root, value); - - if (n1 && n1._next) - { - n1._next = n1._next._next; - return true; - } - - return false; - } -} - -@safe unittest -{ - import std.algorithm.comparison : equal; - - auto e = SList!int(); - auto b = e.linearRemoveElement(2); - assert(b == false); - assert(e.empty()); - auto a = SList!int(-1, 1, 2, 1, 3, 4); - b = a.linearRemoveElement(1); - assert(equal(a[], [-1, 2, 1, 3, 4])); - assert(b == true); - b = a.linearRemoveElement(-1); - assert(b == true); - assert(equal(a[], [2, 1, 3, 4])); - b = a.linearRemoveElement(1); - assert(b == true); - assert(equal(a[], [2, 3, 4])); - b = a.linearRemoveElement(2); - assert(b == true); - b = a.linearRemoveElement(20); - assert(b == false); - assert(equal(a[], [3, 4])); - b = a.linearRemoveElement(4); - assert(b == true); - assert(equal(a[], [3])); - b = a.linearRemoveElement(3); - assert(b == true); - assert(a.empty()); - a.linearRemoveElement(3); -} - -@safe unittest -{ - import std.algorithm.comparison : equal; - - auto a = SList!int(5); - auto b = a; - auto r = a[]; - a.insertFront(1); - b.insertFront(2); - assert(equal(a[], [2, 1, 5])); - assert(equal(b[], [2, 1, 5])); - r.front = 9; - assert(equal(a[], [2, 1, 9])); - assert(equal(b[], [2, 1, 9])); -} - -@safe unittest -{ - auto s = SList!int(1, 2, 3); - auto n = s.findLastNode(s._root); - assert(n && n._payload == 3); -} - -@safe unittest -{ - import std.range.primitives; - auto s = SList!int(1, 2, 5, 10); - assert(walkLength(s[]) == 4); -} - -@safe unittest -{ - import std.range : take; - auto src = take([0, 1, 2, 3], 3); - auto s = SList!int(src); - assert(s == SList!int(0, 1, 2)); -} - -@safe unittest -{ - auto a = SList!int(); - auto b = SList!int(); - auto c = a ~ b[]; - assert(c.empty); -} - -@safe unittest -{ - auto a = SList!int(1, 2, 3); - auto b = SList!int(4, 5, 6); - auto c = a ~ b[]; - assert(c == SList!int(1, 2, 3, 4, 5, 6)); -} - -@safe unittest -{ - auto a = SList!int(1, 2, 3); - auto b = [4, 5, 6]; - auto c = a ~ b; - assert(c == SList!int(1, 2, 3, 4, 5, 6)); -} - -@safe unittest -{ - auto a = SList!int(1, 2, 3); - auto c = a ~ 4; - assert(c == SList!int(1, 2, 3, 4)); -} - -@safe unittest -{ - auto a = SList!int(2, 3, 4); - auto b = 1 ~ a; - assert(b == SList!int(1, 2, 3, 4)); -} - -@safe unittest -{ - auto a = [1, 2, 3]; - auto b = SList!int(4, 5, 6); - auto c = a ~ b; - assert(c == SList!int(1, 2, 3, 4, 5, 6)); -} - -@safe unittest -{ - auto s = SList!int(1, 2, 3, 4); - s.insertFront([ 42, 43 ]); - assert(s == SList!int(42, 43, 1, 2, 3, 4)); -} - -@safe unittest -{ - auto s = SList!int(1, 2, 3); - assert(s.removeAny() == 1); - assert(s == SList!int(2, 3)); - assert(s.stableRemoveAny() == 2); - assert(s == SList!int(3)); -} - -@safe unittest -{ - import std.algorithm.comparison : equal; - - auto s = SList!int(1, 2, 3); - s.removeFront(); - assert(equal(s[], [2, 3])); - s.stableRemoveFront(); - assert(equal(s[], [3])); -} - -@safe unittest -{ - auto s = SList!int(1, 2, 3, 4, 5, 6, 7); - assert(s.removeFront(3) == 3); - assert(s == SList!int(4, 5, 6, 7)); -} - -@safe unittest -{ - auto a = SList!int(1, 2, 3); - auto b = SList!int(1, 2, 3); - assert(a.insertAfter(a[], b[]) == 3); -} - -@safe unittest -{ - import std.range : take; - auto s = SList!int(1, 2, 3, 4); - auto r = take(s[], 2); - assert(s.insertAfter(r, 5) == 1); - assert(s == SList!int(1, 2, 5, 3, 4)); -} - -@safe unittest -{ - import std.algorithm.comparison : equal; - import std.range : take; - - // insertAfter documentation example - auto sl = SList!string(["a", "b", "d"]); - sl.insertAfter(sl[], "e"); // insert at the end (slowest) - assert(equal(sl[], ["a", "b", "d", "e"])); - sl.insertAfter(take(sl[], 2), "c"); // insert after "b" - assert(equal(sl[], ["a", "b", "c", "d", "e"])); -} - -@safe unittest -{ - import std.range.primitives; - auto s = SList!int(1, 2, 3, 4, 5); - auto r = s[]; - popFrontN(r, 3); - auto r1 = s.linearRemove(r); - assert(s == SList!int(1, 2, 3)); - assert(r1.empty); -} - -@safe unittest -{ - auto s = SList!int(1, 2, 3, 4, 5); - auto r = s[]; - auto r1 = s.linearRemove(r); - assert(s == SList!int()); - assert(r1.empty); -} - -@safe unittest -{ - import std.algorithm.comparison : equal; - import std.range; - - auto s = SList!int(1, 2, 3, 4, 5, 6, 7, 8, 9, 10); - auto r = s[]; - popFrontN(r, 3); - auto r1 = take(r, 4); - assert(equal(r1, [4, 5, 6, 7])); - auto r2 = s.linearRemove(r1); - assert(s == SList!int(1, 2, 3, 8, 9, 10)); - assert(equal(r2, [8, 9, 10])); -} - -@safe unittest -{ - import std.range.primitives; - auto lst = SList!int(1, 5, 42, 9); - assert(!lst.empty); - assert(lst.front == 1); - assert(walkLength(lst[]) == 4); - - auto lst2 = lst ~ [ 1, 2, 3 ]; - assert(walkLength(lst2[]) == 7); - - auto lst3 = lst ~ [ 7 ]; - assert(walkLength(lst3[]) == 5); -} - -@safe unittest -{ - auto s = make!(SList!int)(1, 2, 3); -} - -// https://issues.dlang.org/show_bug.cgi?id=5193 -@safe unittest -{ - static struct Data - { - const int val; - } - SList!Data list; -} - -@safe unittest -{ - auto s = SList!int([1, 2, 3]); - s.front = 5; //test frontAssign - assert(s.front == 5); - auto r = s[]; - r.front = 1; //test frontAssign - assert(r.front == 1); -} - -// https://issues.dlang.org/show_bug.cgi?id=14920 -@safe unittest -{ - SList!int s; - s.insertAfter(s[], 1); - assert(s.front == 1); -} - -// https://issues.dlang.org/show_bug.cgi?id=15659 -@safe unittest -{ - SList!int s; - s.clear(); -} - -@safe unittest -{ - SList!int s; - s.reverse(); -} - -@safe unittest -{ - import std.algorithm.comparison : equal; - - auto s = SList!int([1, 2, 3]); - assert(s[].equal([1, 2, 3])); - - s.reverse(); - assert(s[].equal([3, 2, 1])); -} - -@safe unittest -{ - auto s = SList!int([4, 6, 8, 12, 16]); - auto d = s.dup; - assert(d !is s); - assert(d == s); -} diff --git a/phobos/std/container/util.d b/phobos/std/container/util.d deleted file mode 100644 index cc273a2..0000000 --- a/phobos/std/container/util.d +++ /dev/null @@ -1,189 +0,0 @@ -/** -This module contains some common utilities used by containers. - -This module is a submodule of $(MREF std, container). - -Source: $(PHOBOSSRC std/container/util.d) - -Copyright: 2010- Andrei Alexandrescu. All rights reserved by the respective holders. - -License: Distributed under the Boost Software License, Version 1.0. -(See accompanying file LICENSE_1_0.txt or copy at $(HTTP -boost.org/LICENSE_1_0.txt)). - -Authors: $(HTTP erdani.com, Andrei Alexandrescu) - -$(SCRIPT inhibitQuickIndex = 1;) -*/ -module std.container.util; - -/** -Returns an initialized object. This function is mainly for eliminating -construction differences between structs and classes. It allows code to not -worry about whether the type it's constructing is a struct or a class. - */ -template make(T) -if (is(T == struct) || is(T == class)) -{ - T make(Args...)(Args arguments) - if (is(T == struct) && __traits(compiles, T(arguments))) - { - // constructing an std.container.Array without arguments, - // does not initialize its payload and is equivalent - // to a null reference. We therefore construct an empty container - // by passing an empty array to its constructor. - // https://issues.dlang.org/show_bug.cgi?id=13872. - static if (arguments.length == 0) - { - import std.range.primitives : ElementType; - alias ET = ElementType!(T.Range); - return T(ET[].init); - } - else - return T(arguments); - } - - T make(Args...)(Args arguments) - if (is(T == class) && __traits(compiles, new T(arguments))) - { - return new T(arguments); - } -} - - -/// -@system unittest -{ - import std.algorithm.comparison : equal; - import std.container; - - auto arr = make!(Array!int)([4, 2, 3, 1]); - assert(equal(arr[], [4, 2, 3, 1])); - - auto rbt = make!(RedBlackTree!(int, "a > b"))([4, 2, 3, 1]); - assert(equal(rbt[], [4, 3, 2, 1])); - - alias makeList = make!(SList!int); - auto slist = makeList(1, 2, 3); - assert(equal(slist[], [1, 2, 3])); -} - -@system unittest -{ - import std.algorithm.comparison : equal; - import std.container; - - auto arr1 = make!(Array!dchar)(); - assert(arr1.empty); - auto arr2 = make!(Array!dchar)("hello"d); - assert(equal(arr2[], "hello"d)); - - auto rtb1 = make!(RedBlackTree!dchar)(); - assert(rtb1.empty); - auto rtb2 = make!(RedBlackTree!dchar)('h', 'e', 'l', 'l', 'o'); - assert(equal(rtb2[], "ehlo"d)); -} - -// https://issues.dlang.org/show_bug.cgi?id=8895 -@safe unittest -{ - import std.algorithm.comparison : equal; - import std.container; - - auto a = make!(DList!int)(1,2,3,4); - auto b = make!(DList!int)(1,2,3,4); - auto c = make!(DList!int)(1,2,3,5); - auto d = make!(DList!int)(1,2,3,4,5); - assert(a == b); // this better terminate! - assert(a != c); - assert(a != d); -} - -/** - * Convenience function for constructing a generic container. - */ -template make(alias Container, Args...) -if (!is(Container)) -{ - import std.range : isInputRange, isInfinite; - import std.traits : isDynamicArray; - - auto make(Range)(Range range) - if (!isDynamicArray!Range && isInputRange!Range && !isInfinite!Range) - { - import std.range : ElementType; - return .make!(Container!(ElementType!Range, Args))(range); - } - - auto make(T)(T[] items...) - if (!isInfinite!T) - { - return .make!(Container!(T, Args))(items); - } -} - -/// forbid construction from infinite range -@safe unittest -{ - import std.container.array : Array; - import std.range : only, repeat; - import std.range.primitives : isInfinite; - static assert(__traits(compiles, { auto arr = make!Array(only(5)); })); - static assert(!__traits(compiles, { auto arr = make!Array(repeat(5)); })); -} - -/// -@system unittest -{ - import std.algorithm.comparison : equal; - import std.container.array, std.container.rbtree, std.container.slist; - import std.range : iota; - - auto arr = make!Array(iota(5)); - assert(equal(arr[], [0, 1, 2, 3, 4])); - - auto rbtmax = make!(RedBlackTree, "a > b")(iota(5)); - assert(equal(rbtmax[], [4, 3, 2, 1, 0])); - - auto rbtmin = make!RedBlackTree(4, 1, 3, 2); - assert(equal(rbtmin[], [1, 2, 3, 4])); - - alias makeList = make!SList; - auto list = makeList(1, 7, 42); - assert(equal(list[], [1, 7, 42])); -} - -@safe unittest -{ - import std.algorithm.comparison : equal; - import std.container.rbtree; - - auto rbtmin = make!(RedBlackTree, "a < b", false)(3, 2, 2, 1); - assert(equal(rbtmin[], [1, 2, 3])); -} - -// https://issues.dlang.org/show_bug.cgi?id=13872 -@system unittest -{ - import std.container; - - auto tree1 = make!(RedBlackTree!int)(); - auto refToTree1 = tree1; - refToTree1.insert(1); - assert(1 in tree1); - - auto array1 = make!(Array!int)(); - auto refToArray1 = array1; - refToArray1.insertBack(1); - assert(!array1.empty); - - auto slist = make!(SList!int)(); - auto refToSlist = slist; - refToSlist.insert(1); - assert(!slist.empty); - - auto dlist = make!(DList!int)(); - auto refToDList = dlist; - refToDList.insert(1); - assert(!dlist.empty); -} diff --git a/phobos/std/conv.d b/phobos/std/conv.d deleted file mode 100644 index 3aa73c6..0000000 --- a/phobos/std/conv.d +++ /dev/null @@ -1,6031 +0,0 @@ -// Written in the D programming language. - -/** -A one-stop shop for converting values from one type to another. - -$(SCRIPT inhibitQuickIndex = 1;) -$(DIVC quickindex, -$(BOOKTABLE, -$(TR $(TH Category) $(TH Functions)) -$(TR $(TD Generic) $(TD - $(LREF asOriginalType) - $(LREF castFrom) - $(LREF parse) - $(LREF to) - $(LREF toChars) -)) -$(TR $(TD Strings) $(TD - $(LREF text) - $(LREF wtext) - $(LREF dtext) - $(LREF hexString) -)) -$(TR $(TD Numeric) $(TD - $(LREF octal) - $(LREF roundTo) - $(LREF signed) - $(LREF unsigned) -)) -$(TR $(TD Exceptions) $(TD - $(LREF ConvException) - $(LREF ConvOverflowException) -)) -)) - -Copyright: Copyright The D Language Foundation 2007-. - -License: $(HTTP boost.org/LICENSE_1_0.txt, Boost License 1.0). - -Authors: $(HTTP digitalmars.com, Walter Bright), - $(HTTP erdani.org, Andrei Alexandrescu), - Shin Fujishiro, - Adam D. Ruppe, - Kenji Hara - -Source: $(PHOBOSSRC std/conv.d) - -*/ -module std.conv; - -public import std.ascii : LetterCase; - -import std.meta; -import std.range; -import std.traits; -import std.typecons : Flag, Yes, No, tuple, isTuple; - -// Same as std.string.format, but "self-importing". -// Helps reduce code and imports, particularly in static asserts. -// Also helps with missing imports errors. -package template convFormat() -{ - import std.format : format; - alias convFormat = format; -} - -/* ************* Exceptions *************** */ - -/** - * Thrown on conversion errors. - */ -class ConvException : Exception -{ - import std.exception : basicExceptionCtors; - /// - mixin basicExceptionCtors; -} - -/// -@safe unittest -{ - import std.exception : assertThrown; - assertThrown!ConvException(to!int("abc")); -} - -private auto convError(S, T)(S source, string fn = __FILE__, size_t ln = __LINE__) -{ - string msg; - - if (source.empty) - msg = "Unexpected end of input when converting from type " ~ S.stringof ~ " to type " ~ T.stringof; - else - { - ElementType!S el = source.front; - - if (el == '\n') - msg = text("Unexpected '\\n' when converting from type " ~ S.stringof ~ " to type " ~ T.stringof); - else - msg = text("Unexpected '", el, - "' when converting from type " ~ S.stringof ~ " to type " ~ T.stringof); - } - - return new ConvException(msg, fn, ln); -} - -@safe pure/* nothrow*/ // lazy parameter bug -private auto parseError(lazy string msg, string fn = __FILE__, size_t ln = __LINE__) -{ - return new ConvException(text("Can't parse string: ", msg), fn, ln); -} - -private void parseCheck(alias source)(dchar c, string fn = __FILE__, size_t ln = __LINE__) -{ - if (source.empty) - throw parseError(text("unexpected end of input when expecting \"", c, "\"")); - if (source.front != c) - throw parseError(text("\"", c, "\" is missing"), fn, ln); - source.popFront(); -} - -private -{ - T toStr(T, S)(S src) - if (isSomeString!T) - { - // workaround for https://issues.dlang.org/show_bug.cgi?id=14198 - static if (is(S == bool) && is(typeof({ T s = "string"; }))) - { - return src ? "true" : "false"; - } - else - { - import std.array : appender; - import std.format.spec : FormatSpec; - import std.format.write : formatValue; - - auto w = appender!T(); - FormatSpec!(ElementEncodingType!T) f; - formatValue(w, src, f); - return w.data; - } - } - - template isExactSomeString(T) - { - enum isExactSomeString = isSomeString!T && !is(T == enum); - } - - template isEnumStrToStr(S, T) - { - enum isEnumStrToStr = is(S : T) && - is(S == enum) && isExactSomeString!T; - } - template isNullToStr(S, T) - { - enum isNullToStr = is(S : T) && - (is(immutable S == immutable typeof(null))) && isExactSomeString!T; - } -} - -/** - * Thrown on conversion overflow errors. - */ -class ConvOverflowException : ConvException -{ - @safe pure nothrow - this(string s, string fn = __FILE__, size_t ln = __LINE__) - { - super(s, fn, ln); - } -} - -/// -@safe unittest -{ - import std.exception : assertThrown; - assertThrown!ConvOverflowException(to!ubyte(1_000_000)); -} - -/** -The `to` template converts a value from one type _to another. -The source type is deduced and the target type must be specified, for example the -expression `to!int(42.0)` converts the number 42 from -`double` _to `int`. The conversion is "safe", i.e., -it checks for overflow; `to!int(4.2e10)` would throw the -`ConvOverflowException` exception. Overflow checks are only -inserted when necessary, e.g., `to!double(42)` does not do -any checking because any `int` fits in a `double`. - -Conversions from string _to numeric types differ from the C equivalents -`atoi()` and `atol()` by checking for overflow and not allowing whitespace. - -For conversion of strings _to signed types, the grammar recognized is: -$(PRE $(I Integer): - $(I Sign UnsignedInteger) - $(I UnsignedInteger) -$(I Sign): - $(B +) - $(B -)) - -For conversion _to unsigned types, the grammar recognized is: -$(PRE $(I UnsignedInteger): - $(I DecimalDigit) - $(I DecimalDigit) $(I UnsignedInteger)) - */ -template to(T) -{ - T to(A...)(A args) - if (A.length > 0) - { - return toImpl!T(args); - } - - // Fix https://issues.dlang.org/show_bug.cgi?id=6175 - T to(S)(ref S arg) - if (isStaticArray!S) - { - return toImpl!T(arg); - } - - // Fix https://issues.dlang.org/show_bug.cgi?id=16108 - T to(S)(ref S arg) - if (isAggregateType!S && !isCopyable!S) - { - return toImpl!T(arg); - } -} - -/** - * Converting a value _to its own type (useful mostly for generic code) - * simply returns its argument. - */ -@safe pure unittest -{ - int a = 42; - int b = to!int(a); - double c = to!double(3.14); // c is double with value 3.14 -} - -/** - * Converting among numeric types is a safe way _to cast them around. - * - * Conversions from floating-point types _to integral types allow loss of - * precision (the fractional part of a floating-point number). The - * conversion is truncating towards zero, the same way a cast would - * truncate. (_To round a floating point value when casting _to an - * integral, use `roundTo`.) - */ -@safe pure unittest -{ - import std.exception : assertThrown; - - int a = 420; - assert(to!long(a) == a); - assertThrown!ConvOverflowException(to!byte(a)); - - assert(to!int(4.2e6) == 4200000); - assertThrown!ConvOverflowException(to!uint(-3.14)); - assert(to!uint(3.14) == 3); - assert(to!uint(3.99) == 3); - assert(to!int(-3.99) == -3); -} - -/** - * When converting strings _to numeric types, note that D hexadecimal and binary - * literals are not handled. Neither the prefixes that indicate the base, nor the - * horizontal bar used _to separate groups of digits are recognized. This also - * applies to the suffixes that indicate the type. - * - * _To work around this, you can specify a radix for conversions involving numbers. - */ -@safe pure unittest -{ - auto str = to!string(42, 16); - assert(str == "2A"); - auto i = to!int(str, 16); - assert(i == 42); -} - -/** - * Conversions from integral types _to floating-point types always - * succeed, but might lose accuracy. The largest integers with a - * predecessor representable in floating-point format are `2^24-1` for - * `float`, `2^53-1` for `double`, and `2^64-1` for `real` (when - * `real` is 80-bit, e.g. on Intel machines). - */ -@safe pure unittest -{ - // 2^24 - 1, largest proper integer representable as float - int a = 16_777_215; - assert(to!int(to!float(a)) == a); - assert(to!int(to!float(-a)) == -a); -} - -/** - Conversion from string types to char types enforces the input - to consist of a single code point, and said code point must - fit in the target type. Otherwise, $(LREF ConvException) is thrown. - */ -@safe pure unittest -{ - import std.exception : assertThrown; - - assert(to!char("a") == 'a'); - assertThrown(to!char("Ăą")); // 'Ăą' does not fit into a char - assert(to!wchar("Ăą") == 'Ăą'); - assertThrown(to!wchar("😃")); // '😃' does not fit into a wchar - assert(to!dchar("😃") == '😃'); - - // Using wstring or dstring as source type does not affect the result - assert(to!char("a"w) == 'a'); - assert(to!char("a"d) == 'a'); - - // Two code points cannot be converted to a single one - assertThrown(to!char("ab")); -} - -/** - * Converting an array _to another array type works by converting each - * element in turn. Associative arrays can be converted _to associative - * arrays as long as keys and values can in turn be converted. - */ -@safe pure unittest -{ - import std.string : split; - - int[] a = [1, 2, 3]; - auto b = to!(float[])(a); - assert(b == [1.0f, 2, 3]); - string str = "1 2 3 4 5 6"; - auto numbers = to!(double[])(split(str)); - assert(numbers == [1.0, 2, 3, 4, 5, 6]); - int[string] c; - c["a"] = 1; - c["b"] = 2; - auto d = to!(double[wstring])(c); - assert(d["a"w] == 1 && d["b"w] == 2); -} - -/** - * Conversions operate transitively, meaning that they work on arrays and - * associative arrays of any complexity. - * - * This conversion works because `to!short` applies _to an `int`, `to!wstring` - * applies _to a `string`, `to!string` applies _to a `double`, and - * `to!(double[])` applies _to an `int[]`. The conversion might throw an - * exception because `to!short` might fail the range check. - */ -@safe unittest -{ - int[string][double[int[]]] a; - auto b = to!(short[wstring][string[double[]]])(a); -} - -/** - * Object-to-object conversions by dynamic casting throw exception when - * the source is non-null and the target is null. - */ -@safe pure unittest -{ - import std.exception : assertThrown; - // Testing object conversions - class A {} - class B : A {} - class C : A {} - A a1 = new A, a2 = new B, a3 = new C; - assert(to!B(a2) is a2); - assert(to!C(a3) is a3); - assertThrown!ConvException(to!B(a3)); -} - -/** - * Stringize conversion from all types is supported. - * $(UL - * $(LI String _to string conversion works for any two string types having - * (`char`, `wchar`, `dchar`) character widths and any - * combination of qualifiers (mutable, `const`, or `immutable`).) - * $(LI Converts array (other than strings) _to string. - * Each element is converted by calling `to!T`.) - * $(LI Associative array _to string conversion. - * Each element is converted by calling `to!T`.) - * $(LI Object _to string conversion calls `toString` against the object or - * returns `"null"` if the object is null.) - * $(LI Struct _to string conversion calls `toString` against the struct if - * it is defined.) - * $(LI For structs that do not define `toString`, the conversion _to string - * produces the list of fields.) - * $(LI Enumerated types are converted _to strings as their symbolic names.) - * $(LI Boolean values are converted to `"true"` or `"false"`.) - * $(LI `char`, `wchar`, `dchar` _to a string type.) - * $(LI Unsigned or signed integers _to strings. - * $(DL $(DT [special case]) - * $(DD Convert integral value _to string in $(D_PARAM radix) radix. - * radix must be a value from 2 to 36. - * value is treated as a signed value only if radix is 10. - * The characters A through Z are used to represent values 10 through 36 - * and their case is determined by the $(D_PARAM letterCase) parameter.))) - * $(LI All floating point types _to all string types.) - * $(LI Pointer to string conversions convert the pointer to a `size_t` value. - * If pointer is `char*`, treat it as C-style strings. - * In that case, this function is `@system`.)) - * See $(REF formatValue, std,format) on how `toString` should be defined. - */ -@system pure unittest // @system due to cast and ptr -{ - // Conversion representing dynamic/static array with string - long[] a = [ 1, 3, 5 ]; - assert(to!string(a) == "[1, 3, 5]"); - - // Conversion representing associative array with string - int[string] associativeArray = ["0":1, "1":2]; - assert(to!string(associativeArray) == `["0":1, "1":2]` || - to!string(associativeArray) == `["1":2, "0":1]`); - - // char* to string conversion - assert(to!string(cast(char*) null) == ""); - assert(to!string("foo\0".ptr) == "foo"); - - // Conversion reinterpreting void array to string - auto w = "abcx"w; - const(void)[] b = w; - assert(b.length == 8); - - auto c = to!(wchar[])(b); - assert(c == "abcx"); -} - -/** - * Strings can be converted to enum types. The enum member with the same name as the - * input string is returned. The comparison is case-sensitive. - * - * A $(LREF ConvException) is thrown if the enum does not have the specified member. - */ -@safe pure unittest -{ - import std.exception : assertThrown; - - enum E { a, b, c } - assert(to!E("a") == E.a); - assert(to!E("b") == E.b); - assertThrown!ConvException(to!E("A")); -} - -// Tests for https://issues.dlang.org/show_bug.cgi?id=6175 -@safe pure nothrow unittest -{ - char[9] sarr = "blablabla"; - auto darr = to!(char[])(sarr); - assert(sarr.ptr == darr.ptr); - assert(sarr.length == darr.length); -} - -// Tests for https://issues.dlang.org/show_bug.cgi?id=7348 -@safe pure /+nothrow+/ unittest -{ - assert(to!string(null) == "null"); - assert(text(null) == "null"); -} - -// Test `scope` inference of parameters of `text` -@safe unittest -{ - static struct S - { - int* x; // make S a type with pointers - string toString() const scope - { - return "S"; - } - } - scope S s; - assert(text("a", s) == "aS"); -} - -// Tests for https://issues.dlang.org/show_bug.cgi?id=11390 -@safe pure /+nothrow+/ unittest -{ - const(typeof(null)) ctn; - immutable(typeof(null)) itn; - assert(to!string(ctn) == "null"); - assert(to!string(itn) == "null"); -} - -// Tests for https://issues.dlang.org/show_bug.cgi?id=8729: do NOT skip leading WS -@safe pure unittest -{ - import std.exception; - static foreach (T; AliasSeq!(byte, ubyte, short, ushort, int, uint, long, ulong)) - { - assertThrown!ConvException(to!T(" 0")); - assertThrown!ConvException(to!T(" 0", 8)); - } - static foreach (T; AliasSeq!(float, double, real)) - { - assertThrown!ConvException(to!T(" 0")); - } - - assertThrown!ConvException(to!bool(" true")); - - alias NullType = typeof(null); - assertThrown!ConvException(to!NullType(" null")); - - alias ARR = int[]; - assertThrown!ConvException(to!ARR(" [1]")); - - alias AA = int[int]; - assertThrown!ConvException(to!AA(" [1:1]")); -} - -// https://issues.dlang.org/show_bug.cgi?id=20623 -@safe pure nothrow unittest -{ - // static class C - // { - // override string toString() const - // { - // return "C()"; - // } - // } - - static struct S - { - bool b; - int i; - float f; - int[] a; - int[int] aa; - S* p; - // C c; // TODO: Fails because of hasToString - - void fun() inout - { - static foreach (const idx; 0 .. this.tupleof.length) - { - { - const _ = this.tupleof[idx].to!string(); - } - } - } - } -} - -/** -If the source type is implicitly convertible to the target type, $(D -to) simply performs the implicit conversion. - */ -private T toImpl(T, S)(S value) -if (is(S : T) && - !isEnumStrToStr!(S, T) && !isNullToStr!(S, T)) -{ - template isSignedInt(T) - { - enum isSignedInt = isIntegral!T && isSigned!T; - } - alias isUnsignedInt = isUnsigned; - - // Conversion from integer to integer, and changing its sign - static if (isUnsignedInt!S && isSignedInt!T && S.sizeof == T.sizeof) - { // unsigned to signed & same size - import std.exception : enforce; - enforce(value <= cast(S) T.max, - new ConvOverflowException("Conversion positive overflow")); - } - else static if (isSignedInt!S && isUnsignedInt!T) - { // signed to unsigned - import std.exception : enforce; - enforce(0 <= value, - new ConvOverflowException("Conversion negative overflow")); - } - - return value; -} - -// https://issues.dlang.org/show_bug.cgi?id=9523: Allow identity enum conversion -@safe pure nothrow unittest -{ - enum E { a } - auto e = to!E(E.a); - assert(e == E.a); -} - -@safe pure nothrow unittest -{ - int a = 42; - auto b = to!long(a); - assert(a == b); -} - -// https://issues.dlang.org/show_bug.cgi?id=6377 -@safe pure unittest -{ - import std.exception; - // Conversion between same size - static foreach (S; AliasSeq!(byte, short, int, long)) - {{ - alias U = Unsigned!S; - - static foreach (Sint; AliasSeq!(S, const S, immutable S)) - static foreach (Uint; AliasSeq!(U, const U, immutable U)) - {{ - // positive overflow - Uint un = Uint.max; - assertThrown!ConvOverflowException(to!Sint(un), - text(Sint.stringof, ' ', Uint.stringof, ' ', un)); - - // negative overflow - Sint sn = -1; - assertThrown!ConvOverflowException(to!Uint(sn), - text(Sint.stringof, ' ', Uint.stringof, ' ', un)); - }} - }} - - // Conversion between different size - static foreach (i, S1; AliasSeq!(byte, short, int, long)) - static foreach ( S2; AliasSeq!(byte, short, int, long)[i+1..$]) - {{ - alias U1 = Unsigned!S1; - alias U2 = Unsigned!S2; - - static assert(U1.sizeof < S2.sizeof); - - // small unsigned to big signed - static foreach (Uint; AliasSeq!(U1, const U1, immutable U1)) - static foreach (Sint; AliasSeq!(S2, const S2, immutable S2)) - {{ - Uint un = Uint.max; - assertNotThrown(to!Sint(un)); - assert(to!Sint(un) == un); - }} - - // big unsigned to small signed - static foreach (Uint; AliasSeq!(U2, const U2, immutable U2)) - static foreach (Sint; AliasSeq!(S1, const S1, immutable S1)) - {{ - Uint un = Uint.max; - assertThrown(to!Sint(un)); - }} - - static assert(S1.sizeof < U2.sizeof); - - // small signed to big unsigned - static foreach (Sint; AliasSeq!(S1, const S1, immutable S1)) - static foreach (Uint; AliasSeq!(U2, const U2, immutable U2)) - {{ - Sint sn = -1; - assertThrown!ConvOverflowException(to!Uint(sn)); - }} - - // big signed to small unsigned - static foreach (Sint; AliasSeq!(S2, const S2, immutable S2)) - static foreach (Uint; AliasSeq!(U1, const U1, immutable U1)) - {{ - Sint sn = -1; - assertThrown!ConvOverflowException(to!Uint(sn)); - }} - }} -} - -// https://issues.dlang.org/show_bug.cgi?id=13551 -private T toImpl(T, S)(S value) -if (isTuple!T) -{ - T t; - static foreach (i; 0 .. T.length) - { - t[i] = value[i].to!(typeof(T[i])); - } - return t; -} - -@safe unittest -{ - import std.typecons : Tuple; - - auto test = ["10", "20", "30"]; - assert(test.to!(Tuple!(int, int, int)) == Tuple!(int, int, int)(10, 20, 30)); - - auto test1 = [1, 2]; - assert(test1.to!(Tuple!(int, int)) == Tuple!(int, int)(1, 2)); - - auto test2 = [1.0, 2.0, 3.0]; - assert(test2.to!(Tuple!(int, int, int)) == Tuple!(int, int, int)(1, 2, 3)); -} - -/* - Converting static arrays forwards to their dynamic counterparts. - */ -private T toImpl(T, S)(ref S s) -if (isStaticArray!S) -{ - return toImpl!(T, typeof(s[0])[])(s); -} - -@safe pure nothrow unittest -{ - char[4] test = ['a', 'b', 'c', 'd']; - static assert(!isInputRange!(Unqual!(char[4]))); - assert(to!string(test) == test); -} - -/** -When source type supports member template function opCast, it is used. -*/ -private T toImpl(T, S)(S value) -if (!is(S : T) && - is(typeof(S.init.opCast!T()) : T) && - !isExactSomeString!T && - !is(typeof(T(value)))) -{ - return value.opCast!T(); -} - -@safe pure unittest -{ - static struct Test - { - struct T - { - this(S s) @safe pure { } - } - struct S - { - T opCast(U)() @safe pure { assert(false); } - } - } - cast(void) to!(Test.T)(Test.S()); - - // make sure std.conv.to is doing the same thing as initialization - Test.S s; - Test.T t = s; -} - -@safe pure unittest -{ - class B - { - T opCast(T)() { return 43; } - } - auto b = new B; - assert(to!int(b) == 43); - - struct S - { - T opCast(T)() { return 43; } - } - auto s = S(); - assert(to!int(s) == 43); -} - -/** -When target type supports 'converting construction', it is used. -$(UL $(LI If target type is struct, `T(value)` is used.) - $(LI If target type is class, $(D new T(value)) is used.)) -*/ -private T toImpl(T, S)(S value) -if (!is(S : T) && - is(T == struct) && is(typeof(T(value)))) -{ - return T(value); -} - -// https://issues.dlang.org/show_bug.cgi?id=3961 -@safe pure unittest -{ - struct Int - { - int x; - } - Int i = to!Int(1); - - static struct Int2 - { - int x; - this(int x) @safe pure { this.x = x; } - } - Int2 i2 = to!Int2(1); - - static struct Int3 - { - int x; - static Int3 opCall(int x) @safe pure - { - Int3 i; - i.x = x; - return i; - } - } - Int3 i3 = to!Int3(1); -} - -// https://issues.dlang.org/show_bug.cgi?id=6808 -@safe pure unittest -{ - static struct FakeBigInt - { - this(string s) @safe pure {} - } - - string s = "101"; - auto i3 = to!FakeBigInt(s); -} - -/// ditto -private T toImpl(T, S)(S value) -if (!is(S : T) && - is(T == class) && is(typeof(new T(value)))) -{ - return new T(value); -} - -@safe pure unittest -{ - static struct S - { - int x; - } - static class C - { - int x; - this(int x) @safe pure { this.x = x; } - } - - static class B - { - int value; - this(S src) @safe pure { value = src.x; } - this(C src) @safe pure { value = src.x; } - } - - S s = S(1); - auto b1 = to!B(s); // == new B(s) - assert(b1.value == 1); - - C c = new C(2); - auto b2 = to!B(c); // == new B(c) - assert(b2.value == 2); - - auto c2 = to!C(3); // == new C(3) - assert(c2.x == 3); -} - -@safe pure unittest -{ - struct S - { - class A - { - this(B b) @safe pure {} - } - class B : A - { - this() @safe pure { super(this); } - } - } - - S.B b = new S.B(); - S.A a = to!(S.A)(b); // == cast(S.A) b - // (do not run construction conversion like new S.A(b)) - assert(b is a); - - static class C : Object - { - this() @safe pure {} - this(Object o) @safe pure {} - } - - Object oc = new C(); - C a2 = to!C(oc); // == new C(a) - // Construction conversion overrides down-casting conversion - assert(a2 !is a); // -} - -/** -Object-to-object conversions by dynamic casting throw exception when the source is -non-null and the target is null. - */ -private T toImpl(T, S)(S value) -if (!is(S : T) && - (is(S == class) || is(S == interface)) && !is(typeof(value.opCast!T()) : T) && - (is(T == class) || is(T == interface)) && !is(typeof(new T(value)))) -{ - static if (is(T == immutable)) - { - // immutable <- immutable - enum isModConvertible = is(S == immutable); - } - else static if (is(T == const)) - { - static if (is(T == shared)) - { - // shared const <- shared - // shared const <- shared const - // shared const <- immutable - enum isModConvertible = is(S == shared) || is(S == immutable); - } - else - { - // const <- mutable - // const <- immutable - enum isModConvertible = !is(S == shared); - } - } - else - { - static if (is(T == shared)) - { - // shared <- shared mutable - enum isModConvertible = is(S == shared) && !is(S == const); - } - else - { - // (mutable) <- (mutable) - enum isModConvertible = is(Unqual!S == S); - } - } - static assert(isModConvertible, "Bad modifier conversion: "~S.stringof~" to "~T.stringof); - - auto result = ()@trusted{ return cast(T) value; }(); - if (!result && value) - { - throw new ConvException("Cannot convert object of static type " - ~S.classinfo.name~" and dynamic type "~value.classinfo.name - ~" to type "~T.classinfo.name); - } - return result; -} - -// Unittest for 6288 -@safe pure unittest -{ - import std.exception; - - alias Identity(T) = T; - alias toConst(T) = const T; - alias toShared(T) = shared T; - alias toSharedConst(T) = shared const T; - alias toImmutable(T) = immutable T; - template AddModifier(int n) - if (0 <= n && n < 5) - { - static if (n == 0) alias AddModifier = Identity; - else static if (n == 1) alias AddModifier = toConst; - else static if (n == 2) alias AddModifier = toShared; - else static if (n == 3) alias AddModifier = toSharedConst; - else static if (n == 4) alias AddModifier = toImmutable; - } - - interface I {} - interface J {} - - class A {} - class B : A {} - class C : B, I, J {} - class D : I {} - - static foreach (m1; 0 .. 5) // enumerate modifiers - static foreach (m2; 0 .. 5) // ditto - {{ - alias srcmod = AddModifier!m1; - alias tgtmod = AddModifier!m2; - - // Compile time convertible equals to modifier convertible. - static if (is(srcmod!Object : tgtmod!Object)) - { - // Test runtime conversions: class to class, class to interface, - // interface to class, and interface to interface - - // Check that the runtime conversion to succeed - srcmod!A ac = new srcmod!C(); - srcmod!I ic = new srcmod!C(); - assert(to!(tgtmod!C)(ac) !is null); // A(c) to C - assert(to!(tgtmod!I)(ac) !is null); // A(c) to I - assert(to!(tgtmod!C)(ic) !is null); // I(c) to C - assert(to!(tgtmod!J)(ic) !is null); // I(c) to J - - // Check that the runtime conversion fails - srcmod!A ab = new srcmod!B(); - srcmod!I id = new srcmod!D(); - assertThrown(to!(tgtmod!C)(ab)); // A(b) to C - assertThrown(to!(tgtmod!I)(ab)); // A(b) to I - assertThrown(to!(tgtmod!C)(id)); // I(d) to C - assertThrown(to!(tgtmod!J)(id)); // I(d) to J - } - else - { - // Check that the conversion is rejected statically - static assert(!is(typeof(to!(tgtmod!C)(srcmod!A.init)))); // A to C - static assert(!is(typeof(to!(tgtmod!I)(srcmod!A.init)))); // A to I - static assert(!is(typeof(to!(tgtmod!C)(srcmod!I.init)))); // I to C - static assert(!is(typeof(to!(tgtmod!J)(srcmod!I.init)))); // I to J - } - }} -} - -/** -Handles type _to string conversions -*/ -private T toImpl(T, S)(S value) -if (!(is(S : T) && - !isEnumStrToStr!(S, T) && !isNullToStr!(S, T)) && - !isInfinite!S && isExactSomeString!T) -{ - static if (isExactSomeString!S && value[0].sizeof == ElementEncodingType!T.sizeof) - { - // string-to-string with incompatible qualifier conversion - static if (is(ElementEncodingType!T == immutable)) - { - // conversion (mutable|const) -> immutable - return value.idup; - } - else - { - // conversion (immutable|const) -> mutable - return value.dup; - } - } - else static if (isExactSomeString!S) - { - import std.array : appender; - // other string-to-string - //Use Appender directly instead of toStr, which also uses a formatedWrite - auto w = appender!T(); - w.put(value); - return w.data; - } - else static if (isIntegral!S && !is(S == enum)) - { - // other integral-to-string conversions with default radix - - import core.internal.string : signedToTempString, unsignedToTempString; - - alias EEType = Unqual!(ElementEncodingType!T); - EEType[long.sizeof * 3 + 1] buf = void; - EEType[] t = isSigned!S - ? signedToTempString!(10, false, EEType)(value, buf) - : unsignedToTempString!(10, false, EEType)(value, buf); - return t.dup; - } - else static if (is(S == void[]) || is(S == const(void)[]) || is(S == immutable(void)[])) - { - import core.stdc.string : memcpy; - import std.exception : enforce; - // Converting void array to string - alias Char = Unqual!(ElementEncodingType!T); - auto raw = cast(const(ubyte)[]) value; - enforce(raw.length % Char.sizeof == 0, - new ConvException("Alignment mismatch in converting a " - ~ S.stringof ~ " to a " - ~ T.stringof)); - auto result = new Char[raw.length / Char.sizeof]; - ()@trusted{ memcpy(result.ptr, value.ptr, value.length); }(); - return cast(T) result; - } - else static if (isPointer!S && isSomeChar!(PointerTarget!S)) - { - // This is unsafe because we cannot guarantee that the pointer is null terminated. - return () @system { - static if (is(S : const(char)*)) - import core.stdc.string : strlen; - else - size_t strlen(S s) nothrow - { - S p = s; - while (*p++) {} - return p-s-1; - } - return toImpl!T(value ? value[0 .. strlen(value)].dup : null); - }(); - } - else static if (isSomeString!T && is(S == enum)) - { - static if (isSwitchable!(OriginalType!S) && EnumMembers!S.length <= 50) - { - switch (value) - { - foreach (member; NoDuplicates!(EnumMembers!S)) - { - case member: - return to!T(enumRep!(immutable(T), S, member)); - } - default: - } - } - else - { - foreach (member; EnumMembers!S) - { - if (value == member) - return to!T(enumRep!(immutable(T), S, member)); - } - } - - import std.array : appender; - import std.format.spec : FormatSpec; - import std.format.write : formatValue; - - //Default case, delegate to format - //Note: we don't call toStr directly, to avoid duplicate work. - auto app = appender!T(); - app.put("cast(" ~ S.stringof ~ ")"); - FormatSpec!char f; - formatValue(app, cast(OriginalType!S) value, f); - return app.data; - } - else - { - // other non-string values runs formatting - return toStr!T(value); - } -} - -// https://issues.dlang.org/show_bug.cgi?id=14042 -@system unittest -{ - immutable(char)* ptr = "hello".ptr; - auto result = ptr.to!(char[]); -} -// https://issues.dlang.org/show_bug.cgi?id=8384 -@system unittest -{ - void test1(T)(T lp, string cmp) - { - static foreach (e; AliasSeq!(char, wchar, dchar)) - { - test2!(e[])(lp, cmp); - test2!(const(e)[])(lp, cmp); - test2!(immutable(e)[])(lp, cmp); - } - } - - void test2(D, S)(S lp, string cmp) - { - assert(to!string(to!D(lp)) == cmp); - } - - static foreach (e; AliasSeq!("Hello, world!", "Hello, world!"w, "Hello, world!"d)) - { - test1(e, "Hello, world!"); - test1(e.ptr, "Hello, world!"); - } - static foreach (e; AliasSeq!("", ""w, ""d)) - { - test1(e, ""); - test1(e.ptr, ""); - } -} - -/* - To string conversion for non copy-able structs - */ -private T toImpl(T, S)(ref S value) -if (!(is(S : T) && - !isEnumStrToStr!(S, T) && !isNullToStr!(S, T)) && - !isInfinite!S && isExactSomeString!T && !isCopyable!S && !isStaticArray!S) -{ - import std.array : appender; - import std.format.spec : FormatSpec; - import std.format.write : formatValue; - - auto w = appender!T(); - FormatSpec!(ElementEncodingType!T) f; - formatValue(w, value, f); - return w.data; -} - -// https://issues.dlang.org/show_bug.cgi?id=16108 -@safe unittest -{ - static struct A - { - int val; - bool flag; - - string toString() { return text(val, ":", flag); } - - @disable this(this); - } - - auto a = A(); - assert(to!string(a) == "0:false"); - - static struct B - { - int val; - bool flag; - - @disable this(this); - } - - auto b = B(); - assert(to!string(b) == "B(0, false)"); -} - -// https://issues.dlang.org/show_bug.cgi?id=20070 -@safe unittest -{ - void writeThem(T)(ref inout(T) them) - { - assert(them.to!string == "[1, 2, 3, 4]"); - } - - const(uint)[4] vals = [ 1, 2, 3, 4 ]; - writeThem(vals); -} - -/* - Check whether type `T` can be used in a switch statement. - This is useful for compile-time generation of switch case statements. -*/ -private template isSwitchable(E) -{ - enum bool isSwitchable = is(typeof({ - switch (E.init) { default: } - })); -} - -// -@safe unittest -{ - static assert(isSwitchable!int); - static assert(!isSwitchable!double); - static assert(!isSwitchable!real); -} - -//Static representation of the index I of the enum S, -//In representation T. -//T must be an immutable string (avoids un-necessary initializations). -private template enumRep(T, S, S value) -if (is (T == immutable) && isExactSomeString!T && is(S == enum)) -{ - static T enumRep = toStr!T(value); -} - -@safe pure unittest -{ - import std.exception; - void dg() - { - // string to string conversion - alias Chars = AliasSeq!(char, wchar, dchar); - foreach (LhsC; Chars) - { - alias LhStrings = AliasSeq!(LhsC[], const(LhsC)[], immutable(LhsC)[]); - foreach (Lhs; LhStrings) - { - foreach (RhsC; Chars) - { - alias RhStrings = AliasSeq!(RhsC[], const(RhsC)[], immutable(RhsC)[]); - foreach (Rhs; RhStrings) - { - Lhs s1 = to!Lhs("wyda"); - Rhs s2 = to!Rhs(s1); - //writeln(Lhs.stringof, " -> ", Rhs.stringof); - assert(s1 == to!Lhs(s2)); - } - } - } - } - - foreach (T; Chars) - { - foreach (U; Chars) - { - T[] s1 = to!(T[])("Hello, world!"); - auto s2 = to!(U[])(s1); - assert(s1 == to!(T[])(s2)); - auto s3 = to!(const(U)[])(s1); - assert(s1 == to!(T[])(s3)); - auto s4 = to!(immutable(U)[])(s1); - assert(s1 == to!(T[])(s4)); - } - } - } - dg(); - assertCTFEable!dg; -} - -@safe pure unittest -{ - // Conversion representing bool value with string - bool b; - assert(to!string(b) == "false"); - b = true; - assert(to!string(b) == "true"); -} - -@safe pure unittest -{ - // Conversion representing character value with string - alias AllChars = - AliasSeq!( char, const( char), immutable( char), - wchar, const(wchar), immutable(wchar), - dchar, const(dchar), immutable(dchar)); - foreach (Char1; AllChars) - { - foreach (Char2; AllChars) - { - Char1 c = 'a'; - assert(to!(Char2[])(c)[0] == c); - } - uint x = 4; - assert(to!(Char1[])(x) == "4"); - } - - string s = "foo"; - string s2; - foreach (char c; s) - { - s2 ~= to!string(c); - } - assert(s2 == "foo"); -} - -@safe pure nothrow unittest -{ - import std.exception; - // Conversion representing integer values with string - - static foreach (Int; AliasSeq!(ubyte, ushort, uint, ulong)) - { - assert(to!string(Int(0)) == "0"); - assert(to!string(Int(9)) == "9"); - assert(to!string(Int(123)) == "123"); - } - - static foreach (Int; AliasSeq!(byte, short, int, long)) - { - assert(to!string(Int(0)) == "0"); - assert(to!string(Int(9)) == "9"); - assert(to!string(Int(123)) == "123"); - assert(to!string(Int(-0)) == "0"); - assert(to!string(Int(-9)) == "-9"); - assert(to!string(Int(-123)) == "-123"); - assert(to!string(const(Int)(6)) == "6"); - } - - assert(wtext(int.max) == "2147483647"w); - assert(wtext(int.min) == "-2147483648"w); - assert(to!string(0L) == "0"); - - assertCTFEable!( - { - assert(to!string(1uL << 62) == "4611686018427387904"); - assert(to!string(0x100000000) == "4294967296"); - assert(to!string(-138L) == "-138"); - }); -} - -@safe unittest // sprintf issue -{ - double[2] a = [ 1.5, 2.5 ]; - assert(to!string(a) == "[1.5, 2.5]"); -} - -@safe unittest -{ - // Conversion representing class object with string - class A - { - override string toString() @safe const { return "an A"; } - } - A a; - assert(to!string(a) == "null"); - a = new A; - assert(to!string(a) == "an A"); - - // https://issues.dlang.org/show_bug.cgi?id=7660 - class C { override string toString() @safe const { return "C"; } } - struct S { C c; alias c this; } - S s; s.c = new C(); - assert(to!string(s) == "C"); -} - -@safe unittest -{ - // Conversion representing struct object with string - struct S1 - { - string toString() { return "wyda"; } - } - assert(to!string(S1()) == "wyda"); - - struct S2 - { - int a = 42; - float b = 43.5; - } - S2 s2; - assert(to!string(s2) == "S2(42, 43.5)"); - - // Test for https://issues.dlang.org/show_bug.cgi?id=8080 - struct S8080 - { - short[4] data; - alias data this; - string toString() { return ""; } - } - S8080 s8080; - assert(to!string(s8080) == ""); -} - -@safe unittest -{ - // Conversion representing enum value with string - enum EB : bool { a = true } - enum EU : uint { a = 0, b = 1, c = 2 } // base type is unsigned - // base type is signed (https://issues.dlang.org/show_bug.cgi?id=7909) - enum EI : int { a = -1, b = 0, c = 1 } - enum EF : real { a = 1.414, b = 1.732, c = 2.236 } - enum EC : char { a = 'x', b = 'y' } - enum ES : string { a = "aaa", b = "bbb" } - - static foreach (E; AliasSeq!(EB, EU, EI, EF, EC, ES)) - { - assert(to! string(E.a) == "a"c); - assert(to!wstring(E.a) == "a"w); - assert(to!dstring(E.a) == "a"d); - } - - // Test an value not corresponding to an enum member. - auto o = cast(EU) 5; - assert(to! string(o) == "cast(EU)5"c); - assert(to!wstring(o) == "cast(EU)5"w); - assert(to!dstring(o) == "cast(EU)5"d); -} - -@safe unittest -{ - enum E - { - foo, - doo = foo, // check duplicate switch statements - bar, - } - - //Test regression 12494 - assert(to!string(E.foo) == "foo"); - assert(to!string(E.doo) == "foo"); - assert(to!string(E.bar) == "bar"); - - static foreach (S; AliasSeq!(string, wstring, dstring, const(char[]), const(wchar[]), const(dchar[]))) - {{ - auto s1 = to!S(E.foo); - auto s2 = to!S(E.foo); - assert(s1 == s2); - // ensure we don't allocate when it's unnecessary - assert(s1 is s2); - }} - - static foreach (S; AliasSeq!(char[], wchar[], dchar[])) - {{ - auto s1 = to!S(E.foo); - auto s2 = to!S(E.foo); - assert(s1 == s2); - // ensure each mutable array is unique - assert(s1 !is s2); - }} -} - -// ditto -@trusted pure private T toImpl(T, S)(S value, uint radix, LetterCase letterCase = LetterCase.upper) -if (isIntegral!S && - isExactSomeString!T) -in -{ - assert(radix >= 2 && radix <= 36, "radix must be in range [2,36]"); -} -do -{ - alias EEType = Unqual!(ElementEncodingType!T); - - T toStringRadixConvert(size_t bufLen)(uint runtimeRadix = 0) - { - Unsigned!(Unqual!S) div = void, mValue = unsigned(value); - - size_t index = bufLen; - EEType[bufLen] buffer = void; - char baseChar = letterCase == LetterCase.lower ? 'a' : 'A'; - char mod = void; - - do - { - div = cast(S)(mValue / runtimeRadix ); - mod = cast(ubyte)(mValue % runtimeRadix); - mod += mod < 10 ? '0' : baseChar - 10; - buffer[--index] = cast(char) mod; - mValue = div; - } while (mValue); - - return cast(T) buffer[index .. $].dup; - } - - import std.array : array; - switch (radix) - { - case 10: - // The (value+0) is so integral promotions happen to the type - return toChars!(10, EEType)(value + 0).array; - case 16: - // The unsigned(unsigned(value)+0) is so unsigned integral promotions happen to the type - if (letterCase == letterCase.upper) - return toChars!(16, EEType, LetterCase.upper)(unsigned(unsigned(value) + 0)).array; - else - return toChars!(16, EEType, LetterCase.lower)(unsigned(unsigned(value) + 0)).array; - case 2: - return toChars!(2, EEType)(unsigned(unsigned(value) + 0)).array; - case 8: - return toChars!(8, EEType)(unsigned(unsigned(value) + 0)).array; - - default: - return toStringRadixConvert!(S.sizeof * 6)(radix); - } -} - -@safe pure nothrow unittest -{ - static foreach (Int; AliasSeq!(uint, ulong)) - { - assert(to!string(Int(16), 16) == "10"); - assert(to!string(Int(15), 2u) == "1111"); - assert(to!string(Int(1), 2u) == "1"); - assert(to!string(Int(0x1234AF), 16u) == "1234AF"); - assert(to!string(Int(0x1234BCD), 16u, LetterCase.upper) == "1234BCD"); - assert(to!string(Int(0x1234AF), 16u, LetterCase.lower) == "1234af"); - } - - static foreach (Int; AliasSeq!(int, long)) - { - assert(to!string(Int(-10), 10u) == "-10"); - } - - assert(to!string(byte(-10), 16) == "F6"); - assert(to!string(long.min) == "-9223372036854775808"); - assert(to!string(long.max) == "9223372036854775807"); -} - -/** -Narrowing numeric-numeric conversions throw when the value does not -fit in the narrower type. - */ -private T toImpl(T, S)(S value) -if (!is(S : T) && - (isNumeric!S || isSomeChar!S || isBoolean!S) && - (isNumeric!T || isSomeChar!T || isBoolean!T) && !is(T == enum)) -{ - static if (isFloatingPoint!S && isIntegral!T) - { - import std.math.traits : isNaN; - if (value.isNaN) throw new ConvException("Input was NaN"); - } - - enum sSmallest = mostNegative!S; - enum tSmallest = mostNegative!T; - static if (sSmallest < 0) - { - // possible underflow converting from a signed - static if (tSmallest == 0) - { - immutable good = value >= 0; - } - else - { - static assert(tSmallest < 0, - "minimum value of T must be smaller than 0"); - immutable good = value >= tSmallest; - } - if (!good) - throw new ConvOverflowException("Conversion negative overflow"); - } - static if (S.max > T.max) - { - // possible overflow - if (value > T.max) - throw new ConvOverflowException("Conversion positive overflow"); - } - return (ref value)@trusted{ return cast(T) value; }(value); -} - -@safe pure unittest -{ - import std.exception; - - dchar a = ' '; - assert(to!char(a) == ' '); - a = 300; - assert(collectException(to!char(a))); - - dchar from0 = 'A'; - char to0 = to!char(from0); - - wchar from1 = 'A'; - char to1 = to!char(from1); - - char from2 = 'A'; - char to2 = to!char(from2); - - char from3 = 'A'; - wchar to3 = to!wchar(from3); - - char from4 = 'A'; - dchar to4 = to!dchar(from4); -} - -@safe unittest -{ - import std.exception; - - // Narrowing conversions from enum -> integral should be allowed, but they - // should throw at runtime if the enum value doesn't fit in the target - // type. - enum E1 : ulong { A = 1, B = 1UL << 48, C = 0 } - assert(to!int(E1.A) == 1); - assert(to!bool(E1.A) == true); - assertThrown!ConvOverflowException(to!int(E1.B)); // E1.B overflows int - assertThrown!ConvOverflowException(to!bool(E1.B)); // E1.B overflows bool - assert(to!bool(E1.C) == false); - - enum E2 : long { A = -1L << 48, B = -1 << 31, C = 1 << 31 } - assertThrown!ConvOverflowException(to!int(E2.A)); // E2.A overflows int - assertThrown!ConvOverflowException(to!uint(E2.B)); // E2.B overflows uint - assert(to!int(E2.B) == -1 << 31); // but does not overflow int - assert(to!int(E2.C) == 1 << 31); // E2.C does not overflow int - - enum E3 : int { A = -1, B = 1, C = 255, D = 0 } - assertThrown!ConvOverflowException(to!ubyte(E3.A)); - assertThrown!ConvOverflowException(to!bool(E3.A)); - assert(to!byte(E3.A) == -1); - assert(to!byte(E3.B) == 1); - assert(to!ubyte(E3.C) == 255); - assert(to!bool(E3.B) == true); - assertThrown!ConvOverflowException(to!byte(E3.C)); - assertThrown!ConvOverflowException(to!bool(E3.C)); - assert(to!bool(E3.D) == false); - -} - -@safe unittest -{ - import std.exception; - import std.math.traits : isNaN; - - double d = double.nan; - float f = to!float(d); - assert(f.isNaN); - assert(to!double(f).isNaN); - assertThrown!ConvException(to!int(d)); - assertThrown!ConvException(to!int(f)); - auto ex = collectException(d.to!int); - assert(ex.msg == "Input was NaN"); -} - -/** -Array-to-array conversion (except when target is a string type) -converts each element in turn by using `to`. - */ -private T toImpl(T, S)(scope S value) -if (!is(S : T) && - !isSomeString!S && isDynamicArray!S && - !isExactSomeString!T && isArray!T) -{ - alias E = typeof(T.init[0]); - - static if (isStaticArray!T) - { - import std.exception : enforce; - auto res = to!(E[])(value); - enforce!ConvException(T.length == res.length, - convFormat("Length mismatch when converting to static array: %s vs %s", T.length, res.length)); - return res[0 .. T.length]; - } - else - { - import std.array : appender; - auto w = appender!(E[])(); - w.reserve(value.length); - foreach (ref e; value) - { - w.put(to!E(e)); - } - return w.data; - } -} - -@safe pure unittest -{ - import std.exception; - - // array to array conversions - uint[] a = [ 1u, 2, 3 ]; - auto b = to!(float[])(a); - assert(b == [ 1.0f, 2, 3 ]); - - immutable(int)[3] d = [ 1, 2, 3 ]; - b = to!(float[])(d); - assert(b == [ 1.0f, 2, 3 ]); - - uint[][] e = [ a, a ]; - auto f = to!(float[][])(e); - assert(f[0] == b && f[1] == b); - - // Test for https://issues.dlang.org/show_bug.cgi?id=8264 - struct Wrap - { - string wrap; - alias wrap this; - } - Wrap[] warr = to!(Wrap[])(["foo", "bar"]); // should work - - // https://issues.dlang.org/show_bug.cgi?id=12633 - import std.conv : to; - const s2 = ["10", "20"]; - - immutable int[2] a3 = s2.to!(int[2]); - assert(a3 == [10, 20]); - - // verify length mismatches are caught - immutable s4 = [1, 2, 3, 4]; - foreach (i; [1, 4]) - { - auto ex = collectException(s4[0 .. i].to!(int[2])); - assert(ex && ex.msg == "Length mismatch when converting to static array: 2 vs " ~ [cast(char)(i + '0')], - ex ? ex.msg : "Exception was not thrown!"); - } -} - -@safe unittest -{ - auto b = [ 1.0f, 2, 3 ]; - - auto c = to!(string[])(b); - assert(c[0] == "1" && c[1] == "2" && c[2] == "3"); -} - -/** -Associative array to associative array conversion converts each key -and each value in turn. - */ -private T toImpl(T, S)(S value) -if (!is(S : T) && isAssociativeArray!S && - isAssociativeArray!T && !is(T == enum)) -{ - /* This code is potentially unsafe. - */ - alias K2 = KeyType!T; - alias V2 = ValueType!T; - - // While we are "building" the AA, we need to unqualify its values, and only re-qualify at the end - Unqual!V2[K2] result; - - foreach (k1, v1; value) - { - // Cast values temporarily to Unqual!V2 to store them to result variable - result[to!K2(k1)] = to!(Unqual!V2)(v1); - } - // Cast back to original type - return () @trusted { return cast(T) result; }(); -} - -@safe unittest -{ - // hash to hash conversions - int[string] a; - a["0"] = 1; - a["1"] = 2; - auto b = to!(double[dstring])(a); - assert(b["0"d] == 1 && b["1"d] == 2); -} - -// https://issues.dlang.org/show_bug.cgi?id=8705, from doc -@safe unittest -{ - import std.exception; - int[string][double[int[]]] a; - auto b = to!(short[wstring][string[double[]]])(a); - a = [null:["hello":int.max]]; - assertThrown!ConvOverflowException(to!(short[wstring][string[double[]]])(a)); -} -@system unittest // Extra cases for AA with qualifiers conversion -{ - int[][int[]] a;// = [[], []]; - auto b = to!(immutable(short[])[immutable short[]])(a); - - double[dstring][int[long[]]] c; - auto d = to!(immutable(short[immutable wstring])[immutable string[double[]]])(c); -} - -@safe unittest -{ - import std.algorithm.comparison : equal; - import std.array : byPair; - - int[int] a; - assert(a.to!(int[int]) == a); - assert(a.to!(const(int)[int]).byPair.equal(a.byPair)); -} - -@safe pure unittest -{ - static void testIntegralToFloating(Integral, Floating)() - { - Integral a = 42; - auto b = to!Floating(a); - assert(a == b); - assert(a == to!Integral(b)); - } - static void testFloatingToIntegral(Floating, Integral)() - { - import std.math.traits : floatTraits, RealFormat; - - bool convFails(Source, Target, E)(Source src) - { - try - cast(void) to!Target(src); - catch (E) - return true; - return false; - } - - // convert some value - Floating a = 4.2e1; - auto b = to!Integral(a); - assert(is(typeof(b) == Integral) && b == 42); - // convert some negative value (if applicable) - a = -4.2e1; - static if (Integral.min < 0) - { - b = to!Integral(a); - assert(is(typeof(b) == Integral) && b == -42); - } - else - { - // no go for unsigned types - assert(convFails!(Floating, Integral, ConvOverflowException)(a)); - } - // convert to the smallest integral value - a = 0.0 + Integral.min; - static if (Integral.min < 0) - { - a = -a; // -Integral.min not representable as an Integral - assert(convFails!(Floating, Integral, ConvOverflowException)(a) - || Floating.sizeof <= Integral.sizeof - || floatTraits!Floating.realFormat == RealFormat.ieeeExtended53); - } - a = 0.0 + Integral.min; - assert(to!Integral(a) == Integral.min); - --a; // no more representable as an Integral - assert(convFails!(Floating, Integral, ConvOverflowException)(a) - || Floating.sizeof <= Integral.sizeof - || floatTraits!Floating.realFormat == RealFormat.ieeeExtended53); - a = 0.0 + Integral.max; - assert(to!Integral(a) == Integral.max - || Floating.sizeof <= Integral.sizeof - || floatTraits!Floating.realFormat == RealFormat.ieeeExtended53); - ++a; // no more representable as an Integral - assert(convFails!(Floating, Integral, ConvOverflowException)(a) - || Floating.sizeof <= Integral.sizeof - || floatTraits!Floating.realFormat == RealFormat.ieeeExtended53); - // convert a value with a fractional part - a = 3.14; - assert(to!Integral(a) == 3); - a = 3.99; - assert(to!Integral(a) == 3); - static if (Integral.min < 0) - { - a = -3.14; - assert(to!Integral(a) == -3); - a = -3.99; - assert(to!Integral(a) == -3); - } - } - - alias AllInts = AliasSeq!(byte, ubyte, short, ushort, int, uint, long, ulong); - alias AllFloats = AliasSeq!(float, double, real); - alias AllNumerics = AliasSeq!(AllInts, AllFloats); - // test with same type - { - foreach (T; AllNumerics) - { - T a = 42; - auto b = to!T(a); - assert(is(typeof(a) == typeof(b)) && a == b); - } - } - // test that floating-point numbers convert properly to largest ints - // see http://oregonstate.edu/~peterseb/mth351/docs/351s2001_fp80x87.html - // look for "largest fp integer with a predecessor" - { - // float - int a = 16_777_215; // 2^24 - 1 - assert(to!int(to!float(a)) == a); - assert(to!int(to!float(-a)) == -a); - // double - long b = 9_007_199_254_740_991; // 2^53 - 1 - assert(to!long(to!double(b)) == b); - assert(to!long(to!double(-b)) == -b); - // real - static if (real.mant_dig >= 64) - { - ulong c = 18_446_744_073_709_551_615UL; // 2^64 - 1 - assert(to!ulong(to!real(c)) == c); - } - } - // test conversions floating => integral - { - foreach (Integral; AllInts) - { - foreach (Floating; AllFloats) - { - testFloatingToIntegral!(Floating, Integral)(); - } - } - } - // test conversion integral => floating - { - foreach (Integral; AllInts) - { - foreach (Floating; AllFloats) - { - testIntegralToFloating!(Integral, Floating)(); - } - } - } - // test parsing - { - foreach (T; AllNumerics) - { - // from type immutable(char)[2] - auto a = to!T("42"); - assert(a == 42); - // from type char[] - char[] s1 = "42".dup; - a = to!T(s1); - assert(a == 42); - // from type char[2] - char[2] s2; - s2[] = "42"; - a = to!T(s2); - assert(a == 42); - // from type immutable(wchar)[2] - a = to!T("42"w); - assert(a == 42); - } - } -} - -@safe unittest -{ - alias AllInts = AliasSeq!(byte, ubyte, short, ushort, int, uint, long, ulong); - alias AllFloats = AliasSeq!(float, double, real); - alias AllNumerics = AliasSeq!(AllInts, AllFloats); - // test conversions to string - { - foreach (T; AllNumerics) - { - T a = 42; - string s = to!string(a); - assert(s == "42", s); - wstring ws = to!wstring(a); - assert(ws == "42"w, to!string(ws)); - dstring ds = to!dstring(a); - assert(ds == "42"d, to!string(ds)); - // array test - T[] b = new T[2]; - b[0] = 42; - b[1] = 33; - assert(to!string(b) == "[42, 33]"); - } - } - // test array to string conversion - foreach (T ; AllNumerics) - { - auto a = [to!T(1), 2, 3]; - assert(to!string(a) == "[1, 2, 3]"); - } - // test enum to int conversion - enum Testing { Test1, Test2 } - Testing t; - auto a = to!string(t); - assert(a == "Test1"); -} - - -/** -String, or string-like input range, to non-string conversion runs parsing. -$(UL - $(LI When the source is a wide string, it is first converted to a narrow - string and then parsed.) - $(LI When the source is a narrow string, normal text parsing occurs.)) -*/ -private T toImpl(T, S)(S value) -if (isInputRange!S && isSomeChar!(ElementEncodingType!S) && - !isExactSomeString!T && is(typeof(parse!T(value))) && - // https://issues.dlang.org/show_bug.cgi?id=20539 - !(is(T == enum) && is(typeof(value == OriginalType!T.init)) && !isSomeString!(OriginalType!T))) -{ - scope(success) - { - if (!value.empty) - { - throw convError!(S, T)(value); - } - } - return parse!T(value); -} - -/// ditto -private T toImpl(T, S)(S value, uint radix) -if (isSomeFiniteCharInputRange!S && - isIntegral!T && is(typeof(parse!T(value, radix)))) -{ - scope(success) - { - if (!value.empty) - { - throw convError!(S, T)(value); - } - } - return parse!T(value, radix); -} - -@safe pure unittest -{ - // https://issues.dlang.org/show_bug.cgi?id=6668 - // ensure no collaterals thrown - try { to!uint("-1"); } - catch (ConvException e) { assert(e.next is null); } -} - -@safe pure unittest -{ - static foreach (Str; AliasSeq!(string, wstring, dstring)) - {{ - Str a = "123"; - assert(to!int(a) == 123); - assert(to!double(a) == 123); - }} - - // https://issues.dlang.org/show_bug.cgi?id=6255 - auto n = to!int("FF", 16); - assert(n == 255); -} - -// https://issues.dlang.org/show_bug.cgi?id=15800 -@safe unittest -{ - import std.utf : byCodeUnit, byChar, byWchar, byDchar; - - assert(to!int(byCodeUnit("10")) == 10); - assert(to!int(byCodeUnit("10"), 10) == 10); - assert(to!int(byCodeUnit("10"w)) == 10); - assert(to!int(byCodeUnit("10"w), 10) == 10); - - assert(to!int(byChar("10")) == 10); - assert(to!int(byChar("10"), 10) == 10); - assert(to!int(byWchar("10")) == 10); - assert(to!int(byWchar("10"), 10) == 10); - assert(to!int(byDchar("10")) == 10); - assert(to!int(byDchar("10"), 10) == 10); -} - -/** -String, or string-like input range, to char type not directly -supported by parse parses the first dchar of the source. - -Returns: the first code point of the input range, converted - to type T. - -Throws: ConvException if the input range contains more than - a single code point, or if the code point does not - fit into a code unit of type T. -*/ -private T toImpl(T, S)(S value) -if (isSomeChar!T && !is(typeof(parse!T(value))) && - is(typeof(parse!dchar(value)))) -{ - import std.utf : encode; - - immutable dchar codepoint = parse!dchar(value); - if (!value.empty) - throw new ConvException(convFormat("Cannot convert \"%s\" to %s because it " ~ - "contains more than a single code point.", - value, T.stringof)); - T[dchar.sizeof / T.sizeof] decodedCodepoint; - if (encode(decodedCodepoint, codepoint) != 1) - throw new ConvException(convFormat("First code point '%s' of \"%s\" does not fit into a " ~ - "single %s code unit", codepoint, value, T.stringof)); - return decodedCodepoint[0]; -} - -@safe pure unittest -{ - import std.exception : assertThrown; - - assert(toImpl!wchar("a") == 'a'); - - assert(toImpl!char("a"d) == 'a'); - assert(toImpl!char("a"w) == 'a'); - assert(toImpl!wchar("a"d) == 'a'); - - assertThrown!ConvException(toImpl!wchar("ab")); - assertThrown!ConvException(toImpl!char("😃"d)); -} - -/** -Convert a value that is implicitly convertible to the enum base type -into an Enum value. If the value does not match any enum member values -a ConvException is thrown. -Enums with floating-point or string base types are not supported. -*/ -private T toImpl(T, S)(S value) -if (is(T == enum) && !is(S == enum) - && is(typeof(value == OriginalType!T.init)) - && !isFloatingPoint!(OriginalType!T) && !isSomeString!(OriginalType!T)) -{ - foreach (Member; EnumMembers!T) - { - if (Member == value) - return Member; - } - throw new ConvException(convFormat("Value (%s) does not match any member value of enum '%s'", value, T.stringof)); -} - -@safe pure unittest -{ - import std.exception; - enum En8143 : int { A = 10, B = 20, C = 30, D = 20 } - enum En8143[][] m3 = to!(En8143[][])([[10, 30], [30, 10]]); - static assert(m3 == [[En8143.A, En8143.C], [En8143.C, En8143.A]]); - - En8143 en1 = to!En8143(10); - assert(en1 == En8143.A); - assertThrown!ConvException(to!En8143(5)); // matches none - En8143[][] m1 = to!(En8143[][])([[10, 30], [30, 10]]); - assert(m1 == [[En8143.A, En8143.C], [En8143.C, En8143.A]]); -} - -// https://issues.dlang.org/show_bug.cgi?id=20539 -@safe pure unittest -{ - import std.exception : assertNotThrown; - - // To test that the bug is fixed it is required that the struct is static, - // otherwise, the frame pointer makes the test pass even if the bug is not - // fixed. - - static struct A - { - auto opEquals(U)(U) - { - return true; - } - } - - enum ColorA - { - red = A() - } - - assertNotThrown("xxx".to!ColorA); - - // This is a guard for the future. - - struct B - { - auto opEquals(U)(U) - { - return true; - } - } - - enum ColorB - { - red = B() - } - - assertNotThrown("xxx".to!ColorB); -} - -/*************************************************************** - Rounded conversion from floating point to integral. - -Rounded conversions do not work with non-integral target types. - */ - -template roundTo(Target) -{ - Target roundTo(Source)(Source value) - { - import core.math : abs = fabs; - import std.math.exponential : log2; - import std.math.rounding : trunc; - - static assert(isFloatingPoint!Source); - static assert(isIntegral!Target); - - // If value >= 2 ^^ (real.mant_dig - 1), the number is an integer - // and adding 0.5 won't work, but we allready know, that we do - // not have to round anything. - if (log2(abs(value)) >= real.mant_dig - 1) - return to!Target(value); - - return to!Target(trunc(value + (value < 0 ? -0.5L : 0.5L))); - } -} - -/// -@safe unittest -{ - assert(roundTo!int(3.14) == 3); - assert(roundTo!int(3.49) == 3); - assert(roundTo!int(3.5) == 4); - assert(roundTo!int(3.999) == 4); - assert(roundTo!int(-3.14) == -3); - assert(roundTo!int(-3.49) == -3); - assert(roundTo!int(-3.5) == -4); - assert(roundTo!int(-3.999) == -4); - assert(roundTo!(const int)(to!(const double)(-3.999)) == -4); -} - -@safe unittest -{ - import std.exception; - // boundary values - static foreach (Int; AliasSeq!(byte, ubyte, short, ushort, int, uint)) - { - assert(roundTo!Int(Int.min - 0.4L) == Int.min); - assert(roundTo!Int(Int.max + 0.4L) == Int.max); - assertThrown!ConvOverflowException(roundTo!Int(Int.min - 0.5L)); - assertThrown!ConvOverflowException(roundTo!Int(Int.max + 0.5L)); - } -} - -@safe unittest -{ - import std.exception; - assertThrown!ConvException(roundTo!int(float.init)); - auto ex = collectException(roundTo!int(float.init)); - assert(ex.msg == "Input was NaN"); -} - -// https://issues.dlang.org/show_bug.cgi?id=5232 -@safe pure unittest -{ - static if (real.mant_dig >= 64) - ulong maxOdd = ulong.max; - else - ulong maxOdd = (1UL << real.mant_dig) - 1; - - real r1 = maxOdd; - assert(roundTo!ulong(r1) == maxOdd); - - real r2 = maxOdd - 1; - assert(roundTo!ulong(r2) == maxOdd - 1); - - real r3 = maxOdd / 2; - assert(roundTo!ulong(r3) == maxOdd / 2); - - real r4 = maxOdd / 2 + 1; - assert(roundTo!ulong(r4) == maxOdd / 2 + 1); - - // this is only an issue on computers where real == double - long l = -((1L << double.mant_dig) - 1); - double r5 = l; - assert(roundTo!long(r5) == l); -} - -/** -$(PANEL -The `parse` family of functions works quite like the $(LREF to) -family, except that: -$(OL - $(LI It only works with character ranges as input.) - $(LI It takes the input by reference. This means that rvalues (such - as string literals) are not accepted: use `to` instead.) - $(LI It advances the input to the position following the conversion.) - $(LI It does not throw if it could not convert the entire input.)) -) - -This overload parses a `bool` from a character input range. - -Params: - Target = the boolean type to convert to - source = the lvalue of an $(REF_ALTTEXT input range, isInputRange, std,range,primitives) - doCount = the flag for deciding to report the number of consumed characters - -Returns: -$(UL - $(LI A `bool` if `doCount` is set to `No.doCount`) - $(LI A `tuple` containing a `bool` and a `size_t` if `doCount` is set to `Yes.doCount`)) - -Throws: - A $(LREF ConvException) if the range does not represent a `bool`. - -Note: - All character input range conversions using $(LREF to) are forwarded - to `parse` and do not require lvalues. -*/ -auto parse(Target, Source, Flag!"doCount" doCount = No.doCount)(ref Source source) -if (is(immutable Target == immutable bool) && - isInputRange!Source && - isSomeChar!(ElementType!Source)) -{ - import std.ascii : toLower; - - static if (isNarrowString!Source) - { - import std.string : representation; - auto s = source.representation; - } - else - { - alias s = source; - } - - if (!s.empty) - { - auto c1 = toLower(s.front); - bool result = c1 == 't'; - if (result || c1 == 'f') - { - s.popFront(); - foreach (c; result ? "rue" : "alse") - { - if (s.empty || toLower(s.front) != c) - goto Lerr; - s.popFront(); - } - - static if (isNarrowString!Source) - source = cast(Source) s; - - static if (doCount) - { - if (result) - return tuple!("data", "count")(result, 4); - return tuple!("data", "count")(result, 5); - } - else - { - return result; - } - } - } -Lerr: - throw parseError("bool should be case-insensitive 'true' or 'false'"); -} - -/// -@safe unittest -{ - import std.typecons : Flag, Yes, No; - auto s = "true"; - bool b = parse!bool(s); - assert(b); - auto s2 = "true"; - bool b2 = parse!(bool, string, No.doCount)(s2); - assert(b2); - auto s3 = "true"; - auto b3 = parse!(bool, string, Yes.doCount)(s3); - assert(b3.data && b3.count == 4); - auto s4 = "falSE"; - auto b4 = parse!(bool, string, Yes.doCount)(s4); - assert(!b4.data && b4.count == 5); -} - -@safe unittest -{ - import std.algorithm.comparison : equal; - import std.exception; - struct InputString - { - string _s; - @property auto front() { return _s.front; } - @property bool empty() { return _s.empty; } - void popFront() { _s.popFront(); } - } - - auto s = InputString("trueFALSETrueFalsetRUEfALSE"); - assert(parse!bool(s) == true); - assert(s.equal("FALSETrueFalsetRUEfALSE")); - assert(parse!bool(s) == false); - assert(s.equal("TrueFalsetRUEfALSE")); - assert(parse!bool(s) == true); - assert(s.equal("FalsetRUEfALSE")); - assert(parse!bool(s) == false); - assert(s.equal("tRUEfALSE")); - assert(parse!bool(s) == true); - assert(s.equal("fALSE")); - assert(parse!bool(s) == false); - assert(s.empty); - - foreach (ss; ["tfalse", "ftrue", "t", "f", "tru", "fals", ""]) - { - s = InputString(ss); - assertThrown!ConvException(parse!bool(s)); - } -} - -/** -Parses an integer from a character $(REF_ALTTEXT input range, isInputRange, std,range,primitives). - -Params: - Target = the integral type to convert to - s = the lvalue of an input range - doCount = the flag for deciding to report the number of consumed characters - -Returns: -$(UL - $(LI A number of type `Target` if `doCount` is set to `No.doCount`) - $(LI A `tuple` containing a number of type `Target` and a `size_t` if `doCount` is set to `Yes.doCount`)) - -Throws: - A $(LREF ConvException) If an overflow occurred during conversion or - if no character of the input was meaningfully converted. -*/ -auto parse(Target, Source, Flag!"doCount" doCount = No.doCount)(ref scope Source s) -if (isIntegral!Target && !is(Target == enum) && - isSomeChar!(ElementType!Source)) -{ - static if (Target.sizeof < int.sizeof) - { - // smaller types are handled like integers - auto v = .parse!(Select!(Target.min < 0, int, uint), Source, Yes.doCount)(s); - auto result = (() @trusted => cast (Target) v.data)(); - if (result == v.data) - { - static if (doCount) - { - return tuple!("data", "count")(result, v.count); - } - else - { - return result; - } - } - throw new ConvOverflowException("Overflow in integral conversion"); - } - else - { - // int or larger types - - static if (Target.min < 0) - bool sign = false; - else - enum bool sign = false; - - enum char maxLastDigit = Target.min < 0 ? 7 : 5; - uint c; - - static if (isNarrowString!Source) - { - import std.string : representation; - auto source = s.representation; - } - else - { - alias source = s; - } - - size_t count = 0; - - if (source.empty) - goto Lerr; - - c = source.front; - - static if (Target.min < 0) - { - switch (c) - { - case '-': - sign = true; - goto case '+'; - case '+': - ++count; - source.popFront(); - - if (source.empty) - goto Lerr; - - c = source.front; - - break; - - default: - break; - } - } - c -= '0'; - if (c <= 9) - { - Target v = cast(Target) c; - - ++count; - source.popFront(); - - while (!source.empty) - { - c = cast(typeof(c)) (source.front - '0'); - - if (c > 9) - break; - - if (v >= 0 && (v < Target.max/10 || - (v == Target.max/10 && c <= maxLastDigit + sign))) - { - // Note: `v` can become negative here in case of parsing - // the most negative value: - v = cast(Target) (v * 10 + c); - ++count; - source.popFront(); - } - else - throw new ConvOverflowException("Overflow in integral conversion"); - } - - if (sign) - v = -v; - - static if (isNarrowString!Source) - s = s[$-source.length..$]; - - static if (doCount) - { - return tuple!("data", "count")(v, count); - } - else - { - return v; - } - } -Lerr: - static if (isNarrowString!Source) - throw convError!(Source, Target)(cast(Source) source); - else - throw convError!(Source, Target)(source); - } -} - -/// -@safe pure unittest -{ - import std.typecons : Flag, Yes, No; - string s = "123"; - auto a = parse!int(s); - assert(a == 123); - - string s1 = "123"; - auto a1 = parse!(int, string, Yes.doCount)(s1); - assert(a1.data == 123 && a1.count == 3); - - // parse only accepts lvalues - static assert(!__traits(compiles, parse!int("123"))); -} - -/// -@safe pure unittest -{ - import std.string : tr; - import std.typecons : Flag, Yes, No; - string test = "123 \t 76.14"; - auto a = parse!uint(test); - assert(a == 123); - assert(test == " \t 76.14"); // parse bumps string - test = tr(test, " \t\n\r", "", "d"); // skip ws - assert(test == "76.14"); - auto b = parse!double(test); - assert(b == 76.14); - assert(test == ""); - - string test2 = "123 \t 76.14"; - auto a2 = parse!(uint, string, Yes.doCount)(test2); - assert(a2.data == 123 && a2.count == 3); - assert(test2 == " \t 76.14");// parse bumps string - test2 = tr(test2, " \t\n\r", "", "d"); // skip ws - assert(test2 == "76.14"); - auto b2 = parse!(double, string, Yes.doCount)(test2); - assert(b2.data == 76.14 && b2.count == 5); - assert(test2 == ""); - -} - -@safe pure unittest -{ - static foreach (Int; AliasSeq!(byte, ubyte, short, ushort, int, uint, long, ulong)) - { - { - assert(to!Int("0") == 0); - - static if (isSigned!Int) - { - assert(to!Int("+0") == 0); - assert(to!Int("-0") == 0); - } - } - - static if (Int.sizeof >= byte.sizeof) - { - assert(to!Int("6") == 6); - assert(to!Int("23") == 23); - assert(to!Int("68") == 68); - assert(to!Int("127") == 0x7F); - - static if (isUnsigned!Int) - { - assert(to!Int("255") == 0xFF); - } - static if (isSigned!Int) - { - assert(to!Int("+6") == 6); - assert(to!Int("+23") == 23); - assert(to!Int("+68") == 68); - assert(to!Int("+127") == 0x7F); - - assert(to!Int("-6") == -6); - assert(to!Int("-23") == -23); - assert(to!Int("-68") == -68); - assert(to!Int("-128") == -128); - } - } - - static if (Int.sizeof >= short.sizeof) - { - assert(to!Int("468") == 468); - assert(to!Int("32767") == 0x7FFF); - - static if (isUnsigned!Int) - { - assert(to!Int("65535") == 0xFFFF); - } - static if (isSigned!Int) - { - assert(to!Int("+468") == 468); - assert(to!Int("+32767") == 0x7FFF); - - assert(to!Int("-468") == -468); - assert(to!Int("-32768") == -32768); - } - } - - static if (Int.sizeof >= int.sizeof) - { - assert(to!Int("2147483647") == 0x7FFFFFFF); - - static if (isUnsigned!Int) - { - assert(to!Int("4294967295") == 0xFFFFFFFF); - } - - static if (isSigned!Int) - { - assert(to!Int("+2147483647") == 0x7FFFFFFF); - - assert(to!Int("-2147483648") == -2147483648); - } - } - - static if (Int.sizeof >= long.sizeof) - { - assert(to!Int("9223372036854775807") == 0x7FFFFFFFFFFFFFFF); - - static if (isUnsigned!Int) - { - assert(to!Int("18446744073709551615") == 0xFFFFFFFFFFFFFFFF); - } - - static if (isSigned!Int) - { - assert(to!Int("+9223372036854775807") == 0x7FFFFFFFFFFFFFFF); - - assert(to!Int("-9223372036854775808") == 0x8000000000000000); - } - } - } -} - -@safe pure unittest -{ - import std.exception; - - immutable string[] errors = - [ - "", - "-", - "+", - "-+", - " ", - " 0", - "0 ", - "- 0", - "1-", - "xx", - "123h", - "-+1", - "--1", - "+-1", - "++1", - ]; - - immutable string[] unsignedErrors = - [ - "+5", - "-78", - ]; - - // parsing error check - static foreach (Int; AliasSeq!(byte, ubyte, short, ushort, int, uint, long, ulong)) - { - foreach (j, s; errors) - assertThrown!ConvException(to!Int(s)); - - // parse!SomeUnsigned cannot parse head sign. - static if (isUnsigned!Int) - { - foreach (j, s; unsignedErrors) - assertThrown!ConvException(to!Int(s)); - } - } - - immutable string[] positiveOverflowErrors = - [ - "128", // > byte.max - "256", // > ubyte.max - "32768", // > short.max - "65536", // > ushort.max - "2147483648", // > int.max - "4294967296", // > uint.max - "9223372036854775808", // > long.max - "18446744073709551616", // > ulong.max - ]; - // positive overflow check - static foreach (i, Int; AliasSeq!(byte, ubyte, short, ushort, int, uint, long, ulong)) - { - foreach (j, s; positiveOverflowErrors[i..$]) - assertThrown!ConvOverflowException(to!Int(s)); - } - - immutable string[] negativeOverflowErrors = - [ - "-129", // < byte.min - "-32769", // < short.min - "-2147483649", // < int.min - "-9223372036854775809", // < long.min - ]; - // negative overflow check - static foreach (i, Int; AliasSeq!(byte, short, int, long)) - { - foreach (j, s; negativeOverflowErrors[i..$]) - assertThrown!ConvOverflowException(to!Int(s)); - } -} - -@safe pure unittest -{ - void checkErrMsg(string input, dchar charInMsg, dchar charNotInMsg) - { - try - { - int x = input.to!int(); - assert(false, "Invalid conversion did not throw"); - } - catch (ConvException e) - { - // Ensure error message contains failing character, not the character - // beyond. - import std.algorithm.searching : canFind; - assert( e.msg.canFind(charInMsg) && - !e.msg.canFind(charNotInMsg)); - } - catch (Exception e) - { - assert(false, "Did not throw ConvException"); - } - } - checkErrMsg("@$", '@', '$'); - checkErrMsg("@$123", '@', '$'); - checkErrMsg("1@$23", '@', '$'); - checkErrMsg("1@$", '@', '$'); - checkErrMsg("1@$2", '@', '$'); - checkErrMsg("12@$", '@', '$'); -} - -@safe pure unittest -{ - import std.exception; - assertCTFEable!({ string s = "1234abc"; assert(parse! int(s) == 1234 && s == "abc"); }); - assertCTFEable!({ string s = "-1234abc"; assert(parse! int(s) == -1234 && s == "abc"); }); - assertCTFEable!({ string s = "1234abc"; assert(parse!uint(s) == 1234 && s == "abc"); }); - - assertCTFEable!({ string s = "1234abc"; assert(parse!( int, string, Yes.doCount)(s) == - tuple( 1234, 4) && s == "abc"); }); - assertCTFEable!({ string s = "-1234abc"; assert(parse!( int, string, Yes.doCount)(s) == - tuple(-1234, 5) && s == "abc"); }); - assertCTFEable!({ string s = "1234abc"; assert(parse!(uint, string, Yes.doCount)(s) == - tuple( 1234 ,4) && s == "abc"); }); -} - -// https://issues.dlang.org/show_bug.cgi?id=13931 -@safe pure unittest -{ - import std.exception; - - assertThrown!ConvOverflowException("-21474836480".to!int()); - assertThrown!ConvOverflowException("-92233720368547758080".to!long()); -} - -// https://issues.dlang.org/show_bug.cgi?id=14396 -@safe pure unittest -{ - struct StrInputRange - { - this (string s) { str = s; } - char front() const @property { return str[front_index]; } - char popFront() { return str[front_index++]; } - bool empty() const @property { return str.length <= front_index; } - string str; - size_t front_index = 0; - } - auto input = StrInputRange("777"); - assert(parse!int(input) == 777); - - auto input2 = StrInputRange("777"); - assert(parse!(int, StrInputRange, Yes.doCount)(input2) == tuple(777, 3)); -} - -// https://issues.dlang.org/show_bug.cgi?id=9621 -@safe pure unittest -{ - string s1 = "[ \"\\141\", \"\\0\", \"\\41\", \"\\418\" ]"; - assert(parse!(string[])(s1) == ["a", "\0", "!", "!8"]); - - s1 = "[ \"\\141\", \"\\0\", \"\\41\", \"\\418\" ]"; - auto len = s1.length; - assert(parse!(string[], string, Yes.doCount)(s1) == tuple(["a", "\0", "!", "!8"], len)); -} - -/// ditto -auto parse(Target, Source, Flag!"doCount" doCount = No.doCount)(ref Source source, uint radix) -if (isIntegral!Target && !is(Target == enum) && - isSomeChar!(ElementType!Source)) -in -{ - assert(radix >= 2 && radix <= 36, "radix must be in range [2,36]"); -} -do -{ - import core.checkedint : mulu, addu; - import std.exception : enforce; - - if (radix == 10) - { - return parse!(Target, Source, doCount)(source); - } - - enforce!ConvException(!source.empty, "s must not be empty in integral parse"); - - immutable uint beyond = (radix < 10 ? '0' : 'a'-10) + radix; - Target v = 0; - - static if (isNarrowString!Source) - { - import std.string : representation; - scope s = source.representation; - } - else - { - alias s = source; - } - - size_t count = 0; - auto found = false; - do - { - uint c = s.front; - if (c < '0') - break; - if (radix < 10) - { - if (c >= beyond) - break; - } - else - { - if (c > '9') - { - c |= 0x20;//poorman's tolower - if (c < 'a' || c >= beyond) - break; - c -= 'a'-10-'0'; - } - } - - bool overflow = false; - auto nextv = v.mulu(radix, overflow).addu(c - '0', overflow); - enforce!ConvOverflowException(!overflow && nextv <= Target.max, "Overflow in integral conversion"); - v = cast(Target) nextv; - ++count; - s.popFront(); - found = true; - } while (!s.empty); - - if (!found) - { - static if (isNarrowString!Source) - throw convError!(Source, Target)(cast(Source) source); - else - throw convError!(Source, Target)(source); - } - - static if (isNarrowString!Source) - source = source[$ - s.length .. $]; - - static if (doCount) - { - return tuple!("data", "count")(v, count); - } - else - { - return v; - } -} - -@safe pure unittest -{ - string s; // parse doesn't accept rvalues - foreach (i; 2 .. 37) - { - assert(parse!int(s = "0", i) == 0); - assert(parse!int(s = "1", i) == 1); - assert(parse!byte(s = "10", i) == i); - assert(parse!(int, string, Yes.doCount)(s = "0", i) == tuple(0, 1)); - assert(parse!(int, string, Yes.doCount)(s = "1", i) == tuple(1, 1)); - assert(parse!(byte, string, Yes.doCount)(s = "10", i) == tuple(i, 2)); - } - - assert(parse!int(s = "0011001101101", 2) == 0b0011001101101); - assert(parse!int(s = "765", 8) == octal!765); - assert(parse!int(s = "000135", 8) == octal!"135"); - assert(parse!int(s = "fCDe", 16) == 0xfcde); - - // https://issues.dlang.org/show_bug.cgi?id=6609 - assert(parse!int(s = "-42", 10) == -42); - - assert(parse!ubyte(s = "ff", 16) == 0xFF); -} - -// https://issues.dlang.org/show_bug.cgi?id=7302 -@safe pure unittest -{ - import std.range : cycle; - auto r = cycle("2A!"); - auto u = parse!uint(r, 16); - assert(u == 42); - assert(r.front == '!'); - - auto r2 = cycle("2A!"); - auto u2 = parse!(uint, typeof(r2), Yes.doCount)(r2, 16); - assert(u2.data == 42 && u2.count == 2); - assert(r2.front == '!'); -} - -// https://issues.dlang.org/show_bug.cgi?id=13163 -@safe pure unittest -{ - import std.exception; - foreach (s; ["fff", "123"]) - assertThrown!ConvOverflowException(s.parse!ubyte(16)); -} - -// https://issues.dlang.org/show_bug.cgi?id=17282 -@safe pure unittest -{ - auto str = "0=\x00\x02\x55\x40&\xff\xf0\n\x00\x04\x55\x40\xff\xf0~4+10\n"; - assert(parse!uint(str) == 0); - - str = "0=\x00\x02\x55\x40&\xff\xf0\n\x00\x04\x55\x40\xff\xf0~4+10\n"; - assert(parse!(uint, string, Yes.doCount)(str) == tuple(0, 1)); -} - -// https://issues.dlang.org/show_bug.cgi?id=18248 -@safe pure unittest -{ - import std.exception : assertThrown; - - auto str = ";"; - assertThrown(str.parse!uint(16)); - assertThrown(str.parse!(uint, string, Yes.doCount)(16)); -} - -/** - * Parses an `enum` type from a string representing an enum member name. - * - * Params: - * Target = the `enum` type to convert to - * s = the lvalue of the range to _parse - * doCount = the flag for deciding to report the number of consumed characters - * - * Returns: - $(UL - * $(LI An `enum` of type `Target` if `doCount` is set to `No.doCount`) - * $(LI A `tuple` containing an `enum` of type `Target` and a `size_t` if `doCount` is set to `Yes.doCount`)) - * - * Throws: - * A $(LREF ConvException) if type `Target` does not have a member - * represented by `s`. - */ -auto parse(Target, Source, Flag!"doCount" doCount = No.doCount)(ref Source s) -if (is(Target == enum) && isSomeString!Source && !is(Source == enum)) -{ - import std.algorithm.searching : startsWith; - import std.traits : Unqual, EnumMembers; - - Unqual!Target result; - size_t longest_match = 0; - - foreach (i, e; EnumMembers!Target) - { - auto ident = __traits(allMembers, Target)[i]; - if (longest_match < ident.length && s.startsWith(ident)) - { - result = e; - longest_match = ident.length ; - } - } - - if (longest_match > 0) - { - s = s[longest_match .. $]; - static if (doCount) - { - return tuple!("data", "count")(result, longest_match); - } - else - { - return result; - } - } - - throw new ConvException( - Target.stringof ~ " does not have a member named '" - ~ to!string(s) ~ "'"); -} - -/// -@safe unittest -{ - import std.typecons : Flag, Yes, No, tuple; - enum EnumType : bool { a = true, b = false, c = a } - - auto str = "a"; - assert(parse!EnumType(str) == EnumType.a); - auto str2 = "a"; - assert(parse!(EnumType, string, No.doCount)(str2) == EnumType.a); - auto str3 = "a"; - assert(parse!(EnumType, string, Yes.doCount)(str3) == tuple(EnumType.a, 1)); - -} - -@safe unittest -{ - import std.exception; - - enum EB : bool { a = true, b = false, c = a } - enum EU { a, b, c } - enum EI { a = -1, b = 0, c = 1 } - enum EF : real { a = 1.414, b = 1.732, c = 2.236 } - enum EC : char { a = 'a', b = 'b', c = 'c' } - enum ES : string { a = "aaa", b = "bbb", c = "ccc" } - - static foreach (E; AliasSeq!(EB, EU, EI, EF, EC, ES)) - { - assert(to!E("a"c) == E.a); - assert(to!E("b"w) == E.b); - assert(to!E("c"d) == E.c); - - assert(to!(const E)("a") == E.a); - assert(to!(immutable E)("a") == E.a); - assert(to!(shared E)("a") == E.a); - - assertThrown!ConvException(to!E("d")); - } -} - -// https://issues.dlang.org/show_bug.cgi?id=4744 -@safe pure unittest -{ - enum A { member1, member11, member111 } - assert(to!A("member1" ) == A.member1 ); - assert(to!A("member11" ) == A.member11 ); - assert(to!A("member111") == A.member111); - auto s = "member1111"; - assert(parse!A(s) == A.member111 && s == "1"); - auto s2 = "member1111"; - assert(parse!(A, string, No.doCount)(s2) == A.member111 && s2 == "1"); - auto s3 = "member1111"; - assert(parse!(A, string, Yes.doCount)(s3) == tuple(A.member111, 9) && s3 == "1"); -} - -/** - * Parses a floating point number from a character range. - * - * Params: - * Target = a floating point type - * source = the lvalue of the range to _parse - * doCount = the flag for deciding to report the number of consumed characters - * - * Returns: - $(UL - * $(LI A floating point number of type `Target` if `doCount` is set to `No.doCount`) - * $(LI A `tuple` containing a floating point number of¡type `Target` and a `size_t` - * if `doCount` is set to `Yes.doCount`)) - * - * Throws: - * A $(LREF ConvException) if `source` is empty, if no number could be - * parsed, or if an overflow occurred. - */ -auto parse(Target, Source, Flag!"doCount" doCount = No.doCount)(ref Source source) -if (isFloatingPoint!Target && !is(Target == enum) && - isInputRange!Source && isSomeChar!(ElementType!Source) && !is(Source == enum)) -{ - import std.ascii : isDigit, isAlpha, toLower, toUpper, isHexDigit; - import std.exception : enforce; - - static if (isNarrowString!Source) - { - import std.string : representation; - scope p = source.representation; - } - else - { - alias p = source; - } - - void advanceSource() - { - static if (isNarrowString!Source) - source = source[$ - p.length .. $]; - } - - static immutable real[14] negtab = - [ 1e-4096L,1e-2048L,1e-1024L,1e-512L,1e-256L,1e-128L,1e-64L,1e-32L, - 1e-16L,1e-8L,1e-4L,1e-2L,1e-1L,1.0L ]; - static immutable real[13] postab = - [ 1e+4096L,1e+2048L,1e+1024L,1e+512L,1e+256L,1e+128L,1e+64L,1e+32L, - 1e+16L,1e+8L,1e+4L,1e+2L,1e+1L ]; - - ConvException bailOut()(string msg = null, string fn = __FILE__, size_t ln = __LINE__) - { - if (msg == null) - msg = "Floating point conversion error"; - return new ConvException(text(msg, " for input \"", source, "\"."), fn, ln); - } - - enforce(!p.empty, bailOut()); - - - size_t count = 0; - bool sign = false; - switch (p.front) - { - case '-': - sign = true; - ++count; - p.popFront(); - enforce(!p.empty, bailOut()); - if (toLower(p.front) == 'i') - goto case 'i'; - break; - case '+': - ++count; - p.popFront(); - enforce(!p.empty, bailOut()); - break; - case 'i': case 'I': - // inf - ++count; - p.popFront(); - enforce(!p.empty && toUpper(p.front) == 'N', - bailOut("error converting input to floating point")); - ++count; - p.popFront(); - enforce(!p.empty && toUpper(p.front) == 'F', - bailOut("error converting input to floating point")); - // skip past the last 'f' - ++count; - p.popFront(); - advanceSource(); - static if (doCount) - { - return tuple!("data", "count")(sign ? -Target.infinity : Target.infinity, count); - } - else - { - return sign ? -Target.infinity : Target.infinity; - } - default: {} - } - - bool isHex = false; - bool startsWithZero = p.front == '0'; - if (startsWithZero) - { - ++count; - p.popFront(); - if (p.empty) - { - advanceSource(); - static if (doCount) - { - return tuple!("data", "count")(cast (Target) (sign ? -0.0 : 0.0), count); - } - else - { - return sign ? -0.0 : 0.0; - } - } - - isHex = p.front == 'x' || p.front == 'X'; - if (isHex) - { - ++count; - p.popFront(); - } - } - else if (toLower(p.front) == 'n') - { - // nan - ++count; - p.popFront(); - enforce(!p.empty && toUpper(p.front) == 'A', - bailOut("error converting input to floating point")); - ++count; - p.popFront(); - enforce(!p.empty && toUpper(p.front) == 'N', - bailOut("error converting input to floating point")); - // skip past the last 'n' - ++count; - p.popFront(); - advanceSource(); - static if (doCount) - { - return tuple!("data", "count")(Target.nan, count); - } - else - { - return typeof(return).nan; - } - } - - /* - * The following algorithm consists of 2 steps: - * 1) parseDigits processes the textual input into msdec and possibly - * lsdec/msscale variables, followed by the exponent parser which sets - * exp below. - * Hex: input is 0xaaaaa...p+000... where aaaa is the mantissa in hex - * and 000 is the exponent in decimal format with base 2. - * Decimal: input is 0.00333...p+000... where 0.0033 is the mantissa - * in decimal and 000 is the exponent in decimal format with base 10. - * 2) Convert msdec/lsdec and exp into native real format - */ - - real ldval = 0.0; - char dot = 0; /* if decimal point has been seen */ - int exp = 0; - ulong msdec = 0, lsdec = 0; - ulong msscale = 1; - bool sawDigits; - - enum { hex, decimal } - - // sets msdec, lsdec/msscale, and sawDigits by parsing the mantissa digits - void parseDigits(alias FloatFormat)() - { - static if (FloatFormat == hex) - { - enum uint base = 16; - enum ulong msscaleMax = 0x1000_0000_0000_0000UL; // largest power of 16 a ulong holds - enum ubyte expIter = 4; // iterate the base-2 exponent by 4 for every hex digit - alias checkDigit = isHexDigit; - /* - * convert letter to binary representation: First clear bit - * to convert lower space chars to upperspace, then -('A'-10) - * converts letter A to 10, letter B to 11, ... - */ - alias convertDigit = (int x) => isAlpha(x) ? ((x & ~0x20) - ('A' - 10)) : x - '0'; - sawDigits = false; - } - else static if (FloatFormat == decimal) - { - enum uint base = 10; - enum ulong msscaleMax = 10_000_000_000_000_000_000UL; // largest power of 10 a ulong holds - enum ubyte expIter = 1; // iterate the base-10 exponent once for every decimal digit - alias checkDigit = isDigit; - alias convertDigit = (int x) => x - '0'; - // Used to enforce that any mantissa digits are present - sawDigits = startsWithZero; - } - else - static assert(false, "Unrecognized floating-point format used."); - - while (!p.empty) - { - int i = p.front; - while (checkDigit(i)) - { - sawDigits = true; /* must have at least 1 digit */ - - i = convertDigit(i); - - if (msdec < (ulong.max - base)/base) - { - // For base 16: Y = ... + y3*16^3 + y2*16^2 + y1*16^1 + y0*16^0 - msdec = msdec * base + i; - } - else if (msscale < msscaleMax) - { - lsdec = lsdec * base + i; - msscale *= base; - } - else - { - exp += expIter; - } - exp -= dot; - ++count; - p.popFront(); - if (p.empty) - break; - i = p.front; - if (i == '_') - { - ++count; - p.popFront(); - if (p.empty) - break; - i = p.front; - } - } - if (i == '.' && !dot) - { - ++count; - p.popFront(); - dot += expIter; - } - else - break; - } - - // Have we seen any mantissa digits so far? - enforce(sawDigits, bailOut("no digits seen")); - static if (FloatFormat == hex) - enforce(!p.empty && (p.front == 'p' || p.front == 'P'), - bailOut("Floating point parsing: exponent is required")); - } - - if (isHex) - parseDigits!hex; - else - parseDigits!decimal; - - if (isHex || (!p.empty && (p.front == 'e' || p.front == 'E'))) - { - char sexp = 0; - int e = 0; - - ++count; - p.popFront(); - enforce(!p.empty, new ConvException("Unexpected end of input")); - switch (p.front) - { - case '-': sexp++; - goto case; - case '+': ++count; - p.popFront(); - break; - default: {} - } - sawDigits = false; - while (!p.empty && isDigit(p.front)) - { - if (e < 0x7FFFFFFF / 10 - 10) // prevent integer overflow - { - e = e * 10 + p.front - '0'; - } - ++count; - p.popFront(); - sawDigits = true; - } - exp += (sexp) ? -e : e; - enforce(sawDigits, new ConvException("No digits seen.")); - } - - ldval = msdec; - if (msscale != 1) /* if stuff was accumulated in lsdec */ - ldval = ldval * msscale + lsdec; - if (isHex) - { - import core.math : ldexp; - - // Exponent is power of 2, not power of 10 - ldval = ldexp(ldval,exp); - } - else if (ldval) - { - uint u = 0; - int pow = 4096; - - while (exp > 0) - { - while (exp >= pow) - { - ldval *= postab[u]; - exp -= pow; - } - pow >>= 1; - u++; - } - while (exp < 0) - { - while (exp <= -pow) - { - ldval *= negtab[u]; - enforce(ldval != 0, new ConvException("Range error")); - exp += pow; - } - pow >>= 1; - u++; - } - } - - Target result = cast(Target) (sign ? -ldval : ldval); - - // if overflow occurred - import std.math.traits : isFinite; - enforce(isFinite(result), new ConvException("Range error")); - - advanceSource(); - static if (doCount) - { - return tuple!("data", "count")(result, count); - } - else - { - return result; - } -} - - -/// -@safe unittest -{ - import std.math.operations : isClose; - import std.math.traits : isNaN, isInfinity; - import std.typecons : Flag, Yes, No; - auto str = "123.456"; - assert(parse!double(str).isClose(123.456)); - auto str2 = "123.456"; - assert(parse!(double, string, No.doCount)(str2).isClose(123.456)); - auto str3 = "123.456"; - auto r = parse!(double, string, Yes.doCount)(str3); - assert(r.data.isClose(123.456)); - assert(r.count == 7); - auto str4 = "-123.456"; - r = parse!(double, string, Yes.doCount)(str4); - assert(r.data.isClose(-123.456)); - assert(r.count == 8); - auto str5 = "+123.456"; - r = parse!(double, string, Yes.doCount)(str5); - assert(r.data.isClose(123.456)); - assert(r.count == 8); - auto str6 = "inf0"; - r = parse!(double, string, Yes.doCount)(str6); - assert(isInfinity(r.data) && r.count == 3 && str6 == "0"); - auto str7 = "-0"; - auto r2 = parse!(float, string, Yes.doCount)(str7); - assert(r2.data.isClose(0.0) && r2.count == 2); - auto str8 = "nan"; - auto r3 = parse!(real, string, Yes.doCount)(str8); - assert(isNaN(r3.data) && r3.count == 3); -} - -@safe unittest -{ - import std.exception; - import std.math.traits : isNaN, isInfinity; - import std.math.algebraic : fabs; - - // Compare reals with given precision - bool feq(in real rx, in real ry, in real precision = 0.000001L) - { - if (rx == ry) - return 1; - - if (isNaN(rx)) - return cast(bool) isNaN(ry); - - if (isNaN(ry)) - return 0; - - return cast(bool)(fabs(rx - ry) <= precision); - } - - // Make given typed literal - F Literal(F)(F f) - { - return f; - } - - static foreach (Float; AliasSeq!(float, double, real)) - { - assert(to!Float("123") == Literal!Float(123)); - assert(to!Float("+123") == Literal!Float(+123)); - assert(to!Float("-123") == Literal!Float(-123)); - assert(to!Float("123e2") == Literal!Float(123e2)); - assert(to!Float("123e+2") == Literal!Float(123e+2)); - assert(to!Float("123e-2") == Literal!Float(123e-2L)); - assert(to!Float("123.") == Literal!Float(123.0)); - assert(to!Float(".375") == Literal!Float(.375)); - - assert(to!Float("1.23375E+2") == Literal!Float(1.23375E+2)); - - assert(to!Float("0") is 0.0); - assert(to!Float("-0") is -0.0); - - assert(isNaN(to!Float("nan"))); - - assertThrown!ConvException(to!Float("\x00")); - } - - // min and max - float f = to!float("1.17549e-38"); - assert(feq(cast(real) f, cast(real) 1.17549e-38)); - assert(feq(cast(real) f, cast(real) float.min_normal)); - f = to!float("3.40282e+38"); - assert(to!string(f) == to!string(3.40282e+38)); - - // min and max - double d = to!double("2.22508e-308"); - assert(feq(cast(real) d, cast(real) 2.22508e-308)); - assert(feq(cast(real) d, cast(real) double.min_normal)); - d = to!double("1.79769e+308"); - assert(to!string(d) == to!string(1.79769e+308)); - assert(to!string(d) == to!string(double.max)); - - auto z = real.max / 2L; - static assert(is(typeof(z) == real)); - assert(!isNaN(z)); - assert(!isInfinity(z)); - string a = to!string(z); - real b = to!real(a); - string c = to!string(b); - - assert(c == a, "\n" ~ c ~ "\n" ~ a); - - assert(to!string(to!real(to!string(real.max / 2L))) == to!string(real.max / 2L)); - - // min and max - real r = to!real(to!string(real.min_normal)); - version (NetBSD) - { - // NetBSD notice - // to!string returns 3.3621e-4932L. It is less than real.min_normal and it is subnormal value - // Simple C code - // long double rd = 3.3621e-4932L; - // printf("%Le\n", rd); - // has unexpected result: 1.681050e-4932 - // - // Bug report: http://gnats.netbsd.org/cgi-bin/query-pr-single.pl?number=50937 - } - else - { - assert(to!string(r) == to!string(real.min_normal)); - } - r = to!real(to!string(real.max)); - assert(to!string(r) == to!string(real.max)); - - real pi = 3.1415926535897932384626433832795028841971693993751L; - string fullPrecision = "3.1415926535897932384626433832795028841971693993751"; - assert(feq(parse!real(fullPrecision), pi, 2*real.epsilon)); - string fullPrecision2 = "3.1415926535897932384626433832795028841971693993751"; - assert(feq(parse!(real, string, No.doCount)(fullPrecision2), pi, 2*real.epsilon)); - string fullPrecision3= "3.1415926535897932384626433832795028841971693993751"; - auto len = fullPrecision3.length; - auto res = parse!(real, string, Yes.doCount)(fullPrecision3); - assert(feq(res.data, pi, 2*real.epsilon)); - assert(res.count == len); - - real x = 0x1.FAFAFAFAFAFAFAFAFAFAFAFAFAFAFAFAAFAAFAFAFAFAFAFAFAP-252L; - string full = "0x1.FAFAFAFAFAFAFAFAFAFAFAFAFAFAFAFAAFAAFAFAFAFAFAFAFAP-252"; - assert(parse!real(full) == x); - string full2 = "0x1.FAFAFAFAFAFAFAFAFAFAFAFAFAFAFAFAAFAAFAFAFAFAFAFAFAP-252"; - assert(parse!(real, string, No.doCount)(full2) == x); - string full3 = "0x1.FAFAFAFAFAFAFAFAFAFAFAFAFAFAFAFAAFAAFAFAFAFAFAFAFAP-252"; - auto len2 = full3.length; - assert(parse!(real, string, Yes.doCount)(full3) == tuple(x, len2)); -} - -// Tests for the double implementation -@system unittest -{ - // @system because strtod is not @safe. - import std.math.traits : floatTraits, RealFormat; - - static if (floatTraits!real.realFormat == RealFormat.ieeeDouble) - { - import core.stdc.stdlib, std.exception, std.math; - - //Should be parsed exactly: 53 bit mantissa - string s = "0x1A_BCDE_F012_3456p10"; - auto x = parse!real(s); - assert(x == 0x1A_BCDE_F012_3456p10L); - //1 bit is implicit - assert(((*cast(ulong*)&x) & 0x000F_FFFF_FFFF_FFFF) == 0xA_BCDE_F012_3456); - assert(strtod("0x1ABCDEF0123456p10", null) == x); - - s = "0x1A_BCDE_F012_3456p10"; - auto len = s.length; - assert(parse!(real, string, Yes.doCount)(s) == tuple(x, len)); - - //Should be parsed exactly: 10 bit mantissa - s = "0x3FFp10"; - x = parse!real(s); - assert(x == 0x03FFp10); - //1 bit is implicit - assert(((*cast(ulong*)&x) & 0x000F_FFFF_FFFF_FFFF) == 0x000F_F800_0000_0000); - assert(strtod("0x3FFp10", null) == x); - - //60 bit mantissa, round up - s = "0xFFF_FFFF_FFFF_FFFFp10"; - x = parse!real(s); - assert(isClose(x, 0xFFF_FFFF_FFFF_FFFFp10)); - //1 bit is implicit - assert(((*cast(ulong*)&x) & 0x000F_FFFF_FFFF_FFFF) == 0x0000_0000_0000_0000); - assert(strtod("0xFFFFFFFFFFFFFFFp10", null) == x); - - //60 bit mantissa, round down - s = "0xFFF_FFFF_FFFF_FF90p10"; - x = parse!real(s); - assert(isClose(x, 0xFFF_FFFF_FFFF_FF90p10)); - //1 bit is implicit - assert(((*cast(ulong*)&x) & 0x000F_FFFF_FFFF_FFFF) == 0x000F_FFFF_FFFF_FFFF); - assert(strtod("0xFFFFFFFFFFFFF90p10", null) == x); - - //61 bit mantissa, round up 2 - s = "0x1F0F_FFFF_FFFF_FFFFp10"; - x = parse!real(s); - assert(isClose(x, 0x1F0F_FFFF_FFFF_FFFFp10)); - //1 bit is implicit - assert(((*cast(ulong*)&x) & 0x000F_FFFF_FFFF_FFFF) == 0x000F_1000_0000_0000); - assert(strtod("0x1F0FFFFFFFFFFFFFp10", null) == x); - - //61 bit mantissa, round down 2 - s = "0x1F0F_FFFF_FFFF_FF10p10"; - x = parse!real(s); - assert(isClose(x, 0x1F0F_FFFF_FFFF_FF10p10)); - //1 bit is implicit - assert(((*cast(ulong*)&x) & 0x000F_FFFF_FFFF_FFFF) == 0x000F_0FFF_FFFF_FFFF); - assert(strtod("0x1F0FFFFFFFFFFF10p10", null) == x); - - //Huge exponent - s = "0x1F_FFFF_FFFF_FFFFp900"; - x = parse!real(s); - assert(strtod("0x1FFFFFFFFFFFFFp900", null) == x); - - //exponent too big -> converror - s = ""; - assertThrown!ConvException(x = parse!real(s)); - assert(strtod("0x1FFFFFFFFFFFFFp1024", null) == real.infinity); - - //-exponent too big -> 0 - s = "0x1FFFFFFFFFFFFFp-2000"; - x = parse!real(s); - assert(x == 0); - assert(strtod("0x1FFFFFFFFFFFFFp-2000", null) == x); - - s = "0x1FFFFFFFFFFFFFp-2000"; - len = s.length; - assert(parse!(real, string, Yes.doCount)(s) == tuple(x, len)); - } -} - -@system unittest -{ - import core.stdc.errno; - import core.stdc.stdlib; - import std.math.traits : floatTraits, RealFormat; - - errno = 0; // In case it was set by another unittest in a different module. - struct longdouble - { - static if (floatTraits!real.realFormat == RealFormat.ieeeQuadruple) - { - ushort[8] value; - } - else static if (floatTraits!real.realFormat == RealFormat.ieeeExtended || - floatTraits!real.realFormat == RealFormat.ieeeExtended53) - { - ushort[5] value; - } - else static if (floatTraits!real.realFormat == RealFormat.ieeeDouble) - { - ushort[4] value; - } - else - static assert(false, "Not implemented"); - } - - real ld; - longdouble x; - real ld1; - longdouble x1; - int i; - - static if (floatTraits!real.realFormat == RealFormat.ieeeQuadruple) - enum s = "0x1.FFFFFFFFFFFFFFFFFFFFFFFFFFFFp-16382"; - else static if (floatTraits!real.realFormat == RealFormat.ieeeExtended) - enum s = "0x1.FFFFFFFFFFFFFFFEp-16382"; - else static if (floatTraits!real.realFormat == RealFormat.ieeeExtended53) - enum s = "0x1.FFFFFFFFFFFFFFFEp-16382"; - else static if (floatTraits!real.realFormat == RealFormat.ieeeDouble) - enum s = "0x1.FFFFFFFFFFFFFFFEp-1000"; - else - static assert(false, "Floating point format for real not supported"); - - auto s2 = s.idup; - ld = parse!real(s2); - assert(s2.empty); - x = *cast(longdouble *)&ld; - - static if (floatTraits!real.realFormat == RealFormat.ieeeExtended) - { - version (CRuntime_Microsoft) - ld1 = 0x1.FFFFFFFFFFFFFFFEp-16382L; // strtold currently mapped to strtod - else - ld1 = strtold(s.ptr, null); - } - else static if (floatTraits!real.realFormat == RealFormat.ieeeExtended53) - ld1 = 0x1.FFFFFFFFFFFFFFFEp-16382L; // strtold rounds to 53 bits. - else - ld1 = strtold(s.ptr, null); - - x1 = *cast(longdouble *)&ld1; - assert(x1 == x && ld1 == ld); - - assert(!errno); - - s2 = "1.0e5"; - ld = parse!real(s2); - assert(s2.empty); - x = *cast(longdouble *)&ld; - ld1 = strtold("1.0e5", null); - x1 = *cast(longdouble *)&ld1; -} - -@safe pure unittest -{ - import std.exception; - - // https://issues.dlang.org/show_bug.cgi?id=4959 - { - auto s = "0 "; - auto x = parse!double(s); - assert(s == " "); - assert(x == 0.0); - } - { - auto s = "0 "; - auto x = parse!(double, string, Yes.doCount)(s); - assert(s == " "); - assert(x == tuple(0.0, 1)); - } - - // https://issues.dlang.org/show_bug.cgi?id=3369 - assert(to!float("inf") == float.infinity); - assert(to!float("-inf") == -float.infinity); - - // https://issues.dlang.org/show_bug.cgi?id=6160 - assert(6_5.536e3L == to!real("6_5.536e3")); // 2^16 - assert(0x1000_000_000_p10 == to!real("0x1000_000_000_p10")); // 7.03687e+13 - - // https://issues.dlang.org/show_bug.cgi?id=6258 - assertThrown!ConvException(to!real("-")); - assertThrown!ConvException(to!real("in")); - - // https://issues.dlang.org/show_bug.cgi?id=7055 - assertThrown!ConvException(to!float("INF2")); - - //extra stress testing - auto ssOK = ["1.", "1.1.1", "1.e5", "2e1e", "2a", "2e1_1", "3.4_", - "inf", "-inf", "infa", "-infa", "inf2e2", "-inf2e2", - "nan", "-NAN", "+NaN", "-nAna", "NAn2e2", "-naN2e2"]; - auto ssKO = ["", " ", "2e", "2e+", "2e-", "2ee", "2e++1", "2e--1", "2e_1", - "+inf", "-in", "I", "+N", "-NaD", "0x3.F"]; - foreach (s; ssOK) - parse!double(s); - foreach (s; ssKO) - assertThrown!ConvException(parse!double(s)); -} - -@safe unittest // https://issues.dlang.org/show_bug.cgi?id=22637 -{ - import std.exception : assertThrown, assertNotThrown; - auto src = "9991232549867999698999493543521458974414359998784641646846435132132543645435456345634541999999999999999" - ~ "9999999943321231321311999231345312413646846354354354399999934153465464654646464654134135354199999999996515734999" - ~ "9999999320135273486741354354731567431324134999999999999999999999999999999999999999999999135411.9"; - assertThrown!ConvException(parse!double(src)); - static if (real.max_10_exp > 310) assertNotThrown!ConvException(parse!real(src)); -} - -/** -Parses one character from a character range. - -Params: - Target = the type to convert to - s = the lvalue of an $(REF_ALTTEXT input range, isInputRange, std,range,primitives) - doCount = the flag for deciding to report the number of consumed characters - -Returns: -$(UL - $(LI A character of type `Target` if `doCount` is set to `No.doCount`) - $(LI A `tuple` containing a character of type `Target` and a `size_t` if `doCount` is set to `Yes.doCount`)) - -Throws: - A $(LREF ConvException) if the range is empty. - */ -auto parse(Target, Source, Flag!"doCount" doCount = No.doCount)(ref Source s) -if (staticIndexOf!(immutable Target, immutable dchar, immutable ElementEncodingType!Source) >= 0 && - isSomeString!Source && !is(Source == enum)) -{ - if (s.empty) - throw convError!(Source, Target)(s); - static if (is(immutable Target == immutable dchar)) - { - Target result = s.front; - s.popFront(); - static if (doCount) - { - return tuple!("data", "count")(result, 1); - } - else - { - return result; - } - - } - else - { - // Special case: okay so parse a Char off a Char[] - Target result = s[0]; - s = s[1 .. $]; - static if (doCount) - { - return tuple!("data", "count")(result, 1); - } - else - { - return result; - } - } -} - -@safe pure unittest -{ - static foreach (Str; AliasSeq!(string, wstring, dstring)) - { - static foreach (Char; AliasSeq!(char, wchar, dchar)) - {{ - static if (is(immutable Char == immutable dchar) || - Char.sizeof == ElementEncodingType!Str.sizeof) - { - Str s = "aaa"; - assert(parse!Char(s) == 'a'); - assert(s == "aa"); - assert(parse!(Char, typeof(s), No.doCount)(s) == 'a'); - assert(s == "a"); - assert(parse!(Char, typeof(s), Yes.doCount)(s) == tuple('a', 1) && s == ""); - } - }} - } -} - -/// ditto -auto parse(Target, Source, Flag!"doCount" doCount = No.doCount)(ref Source s) -if (isSomeChar!Target && Target.sizeof >= ElementType!Source.sizeof && !is(Target == enum) && - !isSomeString!Source && isInputRange!Source && isSomeChar!(ElementType!Source)) -{ - if (s.empty) - throw convError!(Source, Target)(s); - Target result = s.front; - s.popFront(); - static if (doCount) - { - return tuple!("data", "count")(result, 1); - } - else - { - return result; - } -} - -/// -@safe pure unittest -{ - import std.typecons : Flag, Yes, No; - auto s = "Hello, World!"; - char first = parse!char(s); - assert(first == 'H'); - assert(s == "ello, World!"); - char second = parse!(char, string, No.doCount)(s); - assert(second == 'e'); - assert(s == "llo, World!"); - auto third = parse!(char, string, Yes.doCount)(s); - assert(third.data == 'l' && third.count == 1); - assert(s == "lo, World!"); -} - - -/* - Tests for to!bool and parse!bool -*/ -@safe pure unittest -{ - import std.exception; - - assert(to!bool("TruE") == true); - assert(to!bool("faLse"d) == false); - assertThrown!ConvException(to!bool("maybe")); - - auto t = "TrueType"; - assert(parse!bool(t) == true); - assert(t == "Type"); - - auto f = "False killer whale"d; - assert(parse!bool(f) == false); - assert(f == " killer whale"d); - - f = "False killer whale"d; - assert(parse!(bool, dstring, Yes.doCount)(f) == tuple(false, 5)); - assert(f == " killer whale"d); - - auto m = "maybe"; - assertThrown!ConvException(parse!bool(m)); - assertThrown!ConvException(parse!(bool, string, Yes.doCount)(m)); - assert(m == "maybe"); // m shouldn't change on failure - - auto s = "true"; - auto b = parse!(const(bool))(s); - assert(b == true); -} - -/** -Parses `typeof(null)` from a character range if the range -spells `"null"`. This function is case insensitive. - -Params: - Target = the type to convert to - s = the lvalue of an $(REF_ALTTEXT input range, isInputRange, std,range,primitives) - doCount = the flag for deciding to report the number of consumed characters - -Returns: -$(UL - $(LI `null` if `doCount` is set to `No.doCount`) - $(LI A `tuple` containing `null` and a `size_t` if `doCount` is set to `Yes.doCount`)) - -Throws: - A $(LREF ConvException) if the range doesn't represent `null`. - */ -auto parse(Target, Source, Flag!"doCount" doCount = No.doCount)(ref Source s) -if (is(immutable Target == immutable typeof(null)) && - isInputRange!Source && - isSomeChar!(ElementType!Source)) -{ - import std.ascii : toLower; - foreach (c; "null") - { - if (s.empty || toLower(s.front) != c) - throw parseError("null should be case-insensitive 'null'"); - s.popFront(); - } - static if (doCount) - { - return tuple!("data", "count")(null, 4); - } - else - { - return null; - } -} - -/// -@safe pure unittest -{ - import std.exception : assertThrown; - import std.typecons : Flag, Yes, No; - - alias NullType = typeof(null); - auto s1 = "null"; - assert(parse!NullType(s1) is null); - assert(s1 == ""); - - auto s2 = "NUll"d; - assert(parse!NullType(s2) is null); - assert(s2 == ""); - - auto s3 = "nuLlNULl"; - assert(parse!(NullType, string, No.doCount)(s3) is null); - auto r = parse!(NullType, string, Yes.doCount)(s3); - assert(r.data is null && r.count == 4); - - auto m = "maybe"; - assertThrown!ConvException(parse!NullType(m)); - assertThrown!ConvException(parse!(NullType, string, Yes.doCount)(m)); - assert(m == "maybe"); // m shouldn't change on failure - - auto s = "NULL"; - assert(parse!(const NullType)(s) is null); -} - -//Used internally by parse Array/AA, to remove ascii whites -package auto skipWS(R, Flag!"doCount" doCount = No.doCount)(ref R r) -{ - import std.ascii : isWhite; - static if (isSomeString!R) - { - //Implementation inspired from stripLeft. - foreach (i, c; r) - { - if (!isWhite(c)) - { - r = r[i .. $]; - static if (doCount) - { - return i; - } - else - { - return; - } - } - } - auto len = r.length; - r = r[0 .. 0]; //Empty string with correct type. - static if (doCount) - { - return len; - } - else - { - return; - } - } - else - { - size_t i = 0; - for (; !r.empty && isWhite(r.front); r.popFront(), ++i) - { } - static if (doCount) - { - return i; - } - } -} - -/** - * Parses an array from a string given the left bracket (default $(D - * '[')), right bracket (default `']'`), and element separator (by - * default `','`). A trailing separator is allowed. - * - * Params: - * s = The string to parse - * lbracket = the character that starts the array - * rbracket = the character that ends the array - * comma = the character that separates the elements of the array - * doCount = the flag for deciding to report the number of consumed characters - * - * Returns: - $(UL - * $(LI An array of type `Target` if `doCount` is set to `No.doCount`) - * $(LI A `tuple` containing an array of type `Target` and a `size_t` if `doCount` is set to `Yes.doCount`)) - */ -auto parse(Target, Source, Flag!"doCount" doCount = No.doCount)(ref Source s, dchar lbracket = '[', - dchar rbracket = ']', dchar comma = ',') -if (isDynamicArray!Target && !is(Target == enum) && - isSomeString!Source && !is(Source == enum)) -{ - import std.array : appender; - - auto result = appender!Target(); - - parseCheck!s(lbracket); - size_t count = 1 + skipWS!(Source, Yes.doCount)(s); - if (s.empty) - throw convError!(Source, Target)(s); - if (s.front == rbracket) - { - s.popFront(); - static if (doCount) - { - return tuple!("data", "count")(result.data, ++count); - } - else - { - return result.data; - } - } - for (;; s.popFront(), count += 1 + skipWS!(Source, Yes.doCount)(s)) - { - if (!s.empty && s.front == rbracket) - break; - auto r = parseElement!(WideElementType!Target, Source, Yes.doCount)(s); - result ~= r.data; - count += r.count + skipWS!(Source, Yes.doCount)(s); - if (s.empty) - throw convError!(Source, Target)(s); - if (s.front != comma) - break; - } - parseCheck!s(rbracket); - static if (doCount) - { - return tuple!("data", "count")(result.data, ++count); - } - else - { - return result.data; - } -} - -/// -@safe pure unittest -{ - import std.typecons : Flag, Yes, No; - auto s1 = `[['h', 'e', 'l', 'l', 'o'], "world"]`; - auto a1 = parse!(string[])(s1); - assert(a1 == ["hello", "world"]); - - auto s2 = `["aaa", "bbb", "ccc"]`; - auto a2 = parse!(string[])(s2); - assert(a2 == ["aaa", "bbb", "ccc"]); - - auto s3 = `[['h', 'e', 'l', 'l', 'o'], "world"]`; - auto len3 = s3.length; - auto a3 = parse!(string[], string, Yes.doCount)(s3); - assert(a3.data == ["hello", "world"]); - assert(a3.count == len3); -} - -// https://issues.dlang.org/show_bug.cgi?id=9615 -@safe unittest -{ - import std.typecons : Flag, Yes, No, tuple; - string s0 = "[1,2, ]"; - string s1 = "[1,2, \t\v\r\n]"; - string s2 = "[1,2]"; - assert(s0.parse!(int[]) == [1,2]); - assert(s1.parse!(int[]) == [1,2]); - assert(s2.parse!(int[]) == [1,2]); - - s0 = "[1,2, ]"; - auto len0 = s0.length; - s1 = "[1,2, \t\v\r\n]"; - auto len1 = s1.length; - s2 = "[1,2]"; - auto len2 = s2.length; - assert(s0.parse!(int[], string, Yes.doCount) == tuple([1,2], len0)); - assert(s1.parse!(int[], string, Yes.doCount) == tuple([1,2], len1)); - assert(s2.parse!(int[], string, Yes.doCount) == tuple([1,2], len2)); - - string s3 = `["a","b",]`; - string s4 = `["a","b"]`; - assert(s3.parse!(string[]) == ["a","b"]); - assert(s4.parse!(string[]) == ["a","b"]); - - s3 = `["a","b",]`; - auto len3 = s3.length; - assert(s3.parse!(string[], string, Yes.doCount) == tuple(["a","b"], len3)); - - s3 = `[ ]`; - assert(tuple([], s3.length) == s3.parse!(string[], string, Yes.doCount)); - - import std.exception : assertThrown; - string s5 = "[,]"; - string s6 = "[, \t,]"; - assertThrown!ConvException(parse!(string[])(s5)); - assertThrown!ConvException(parse!(int[])(s6)); - - s5 = "[,]"; - s6 = "[,¡\t,]"; - assertThrown!ConvException(parse!(string[], string, Yes.doCount)(s5)); - assertThrown!ConvException(parse!(string[], string, Yes.doCount)(s6)); -} - -@safe unittest -{ - int[] a = [1, 2, 3, 4, 5]; - auto s = to!string(a); - assert(to!(int[])(s) == a); -} - -@safe unittest -{ - int[][] a = [ [1, 2] , [3], [4, 5] ]; - auto s = to!string(a); - assert(to!(int[][])(s) == a); -} - -@safe unittest -{ - int[][][] ia = [ [[1,2],[3,4],[5]] , [[6],[],[7,8,9]] , [[]] ]; - - char[] s = to!(char[])(ia); - int[][][] ia2; - - ia2 = to!(typeof(ia2))(s); - assert( ia == ia2); -} - -@safe pure unittest -{ - import std.exception; - import std.typecons : Flag, Yes, No; - - //Check proper failure - auto s = "[ 1 , 2 , 3 ]"; - auto s2 = s.save; - foreach (i ; 0 .. s.length-1) - { - auto ss = s[0 .. i]; - assertThrown!ConvException(parse!(int[])(ss)); - assertThrown!ConvException(parse!(int[], string, Yes.doCount)(ss)); - } - int[] arr = parse!(int[])(s); - auto arr2 = parse!(int[], string, Yes.doCount)(s2); - arr = arr2.data; -} - -@safe pure unittest -{ - //Checks parsing of strings with escaped characters - string s1 = `[ - "Contains a\0null!", - "tab\there", - "line\nbreak", - "backslash \\ slash / question \?", - "number \x35 five", - "unicode \u65E5 sun", - "very long \U000065E5 sun" - ]`; - - //Note: escaped characters purposefully replaced and isolated to guarantee - //there are no typos in the escape syntax - string[] s2 = [ - "Contains a" ~ '\0' ~ "null!", - "tab" ~ '\t' ~ "here", - "line" ~ '\n' ~ "break", - "backslash " ~ '\\' ~ " slash / question ?", - "number 5 five", - "unicode 日 sun", - "very long 日 sun" - ]; - string s3 = s1.save; - assert(s2 == parse!(string[])(s1)); - assert(s1.empty); - assert(tuple(s2, s3.length) == parse!(string[], string, Yes.doCount)(s3)); -} - -/// ditto -auto parse(Target, Source, Flag!"doCount" doCount = No.doCount)(ref Source s, dchar lbracket = '[', - dchar rbracket = ']', dchar comma = ',') -if (isStaticArray!Target && !is(Target == enum) && - isExactSomeString!Source) -{ - static if (hasIndirections!Target) - Target result = Target.init[0].init; - else - Target result = void; - - parseCheck!s(lbracket); - size_t count = 1 + skipWS!(Source, Yes.doCount)(s); - if (s.empty) - throw convError!(Source, Target)(s); - if (s.front == rbracket) - { - static if (result.length != 0) - goto Lmanyerr; - else - { - s.popFront(); - static if (doCount) - { - return tuple!("data", "count")(result, ++count); - } - else - { - return result; - } - } - } - for (size_t i = 0; ; s.popFront(), count += 1 + skipWS!(Source, Yes.doCount)(s)) - { - if (i == result.length) - goto Lmanyerr; - auto r = parseElement!(ElementType!Target, Source, Yes.doCount)(s); - result[i++] = r.data; - count += r.count + skipWS!(Source, Yes.doCount)(s); - if (s.empty) - throw convError!(Source, Target)(s); - if (s.front != comma) - { - if (i != result.length) - goto Lfewerr; - break; - } - } - parseCheck!s(rbracket); - static if (doCount) - { - return tuple!("data", "count")(result, ++count); - } - else - { - return result; - } - - -Lmanyerr: - throw parseError(text("Too many elements in input, ", result.length, " elements expected.")); - -Lfewerr: - throw parseError(text("Too few elements in input, ", result.length, " elements expected.")); -} - -@safe pure unittest -{ - import std.exception; - - auto s1 = "[1,2,3,4]"; - auto sa1 = parse!(int[4])(s1); - assert(sa1 == [1,2,3,4]); - s1 = "[1,2,3,4]"; - assert(tuple([1,2,3,4], s1.length) == parse!(int[4], string, Yes.doCount)(s1)); - - auto s2 = "[[1],[2,3],[4]]"; - auto sa2 = parse!(int[][3])(s2); - assert(sa2 == [[1],[2,3],[4]]); - s2 = "[[1],[2,3],[4]]"; - assert(tuple([[1],[2,3],[4]], s2.length) == parse!(int[][3], string, Yes.doCount)(s2)); - - auto s3 = "[1,2,3]"; - assertThrown!ConvException(parse!(int[4])(s3)); - assertThrown!ConvException(parse!(int[4], string, Yes.doCount)(s3)); - - auto s4 = "[1,2,3,4,5]"; - assertThrown!ConvException(parse!(int[4])(s4)); - assertThrown!ConvException(parse!(int[4], string, Yes.doCount)(s4)); -} - -/** - * Parses an associative array from a string given the left bracket (default $(D - * '[')), right bracket (default `']'`), key-value separator (default $(D - * ':')), and element seprator (by default `','`). - * - * Params: - * s = the string to parse - * lbracket = the character that starts the associative array - * rbracket = the character that ends the associative array - * keyval = the character that associates the key with the value - * comma = the character that separates the elements of the associative array - * doCount = the flag for deciding to report the number of consumed characters - * - * Returns: - $(UL - * $(LI An associative array of type `Target` if `doCount` is set to `No.doCount`) - * $(LI A `tuple` containing an associative array of type `Target` and a `size_t` - * if `doCount` is set to `Yes.doCount`)) - */ -auto parse(Target, Source, Flag!"doCount" doCount = No.doCount)(ref Source s, dchar lbracket = '[', - dchar rbracket = ']', dchar keyval = ':', dchar comma = ',') -if (isAssociativeArray!Target && !is(Target == enum) && - isSomeString!Source && !is(Source == enum)) -{ - alias KeyType = typeof(Target.init.keys[0]); - alias ValType = typeof(Target.init.values[0]); - - Target result; - - parseCheck!s(lbracket); - size_t count = 1 + skipWS!(Source, Yes.doCount)(s); - if (s.empty) - throw convError!(Source, Target)(s); - if (s.front == rbracket) - { - s.popFront(); - static if (doCount) - { - return tuple!("data", "count")(result, ++count); - } - else - { - return result; - } - } - for (;; s.popFront(), count += 1 + skipWS!(Source, Yes.doCount)(s)) - { - auto key = parseElement!(KeyType, Source, Yes.doCount)(s); - count += key.count + skipWS!(Source, Yes.doCount)(s); - parseCheck!s(keyval); - count += 1 + skipWS!(Source, Yes.doCount)(s); - auto val = parseElement!(ValType, Source, Yes.doCount)(s); - count += val.count + skipWS!(Source, Yes.doCount)(s); - result[key.data] = val.data; - if (s.empty) - throw convError!(Source, Target)(s); - if (s.front != comma) - break; - } - parseCheck!s(rbracket); - static if (doCount) - { - return tuple!("data", "count")(result, ++count); - } - else - { - return result; - } -} - -/// -@safe pure unittest -{ - import std.typecons : Flag, Yes, No, tuple; - import std.range.primitives : save; - import std.array : assocArray; - auto s1 = "[1:10, 2:20, 3:30]"; - auto copyS1 = s1.save; - auto aa1 = parse!(int[int])(s1); - assert(aa1 == [1:10, 2:20, 3:30]); - assert(tuple([1:10, 2:20, 3:30], copyS1.length) == parse!(int[int], string, Yes.doCount)(copyS1)); - - auto s2 = `["aaa":10, "bbb":20, "ccc":30]`; - auto copyS2 = s2.save; - auto aa2 = parse!(int[string])(s2); - assert(aa2 == ["aaa":10, "bbb":20, "ccc":30]); - assert(tuple(["aaa":10, "bbb":20, "ccc":30], copyS2.length) == - parse!(int[string], string, Yes.doCount)(copyS2)); - - auto s3 = `["aaa":[1], "bbb":[2,3], "ccc":[4,5,6]]`; - auto copyS3 = s3.save; - auto aa3 = parse!(int[][string])(s3); - assert(aa3 == ["aaa":[1], "bbb":[2,3], "ccc":[4,5,6]]); - assert(tuple(["aaa":[1], "bbb":[2,3], "ccc":[4,5,6]], copyS3.length) == - parse!(int[][string], string, Yes.doCount)(copyS3)); - - auto s4 = `[]`; - int[int] emptyAA; - assert(tuple(emptyAA, s4.length) == parse!(int[int], string, Yes.doCount)(s4)); -} - -@safe pure unittest -{ - import std.exception; - - //Check proper failure - auto s = "[1:10, 2:20, 3:30]"; - auto s2 = s.save; - foreach (i ; 0 .. s.length-1) - { - auto ss = s[0 .. i]; - assertThrown!ConvException(parse!(int[int])(ss)); - assertThrown!ConvException(parse!(int[int], string, Yes.doCount)(ss)); - } - int[int] aa = parse!(int[int])(s); - auto aa2 = parse!(int[int], string, Yes.doCount)(s2); - aa = aa2[0]; - -} - -private auto parseEscape(Source, Flag!"doCount" doCount = No.doCount)(ref Source s) -if (isInputRange!Source && isSomeChar!(ElementType!Source)) -{ - parseCheck!s('\\'); - size_t count = 1; - if (s.empty) - throw parseError("Unterminated escape sequence"); - - // consumes 1 element from Source - dchar getHexDigit()(ref Source s_ = s) // workaround - { - import std.ascii : isAlpha, isHexDigit; - if (s_.empty) - throw parseError("Unterminated escape sequence"); - s_.popFront(); - if (s_.empty) - throw parseError("Unterminated escape sequence"); - dchar c = s_.front; - if (!isHexDigit(c)) - throw parseError("Hex digit is missing"); - return isAlpha(c) ? ((c & ~0x20) - ('A' - 10)) : c - '0'; - } - - // We need to do octals separate, because they need a lookahead to find out, - // where the escape sequence ends. - auto first = s.front; - if (first >= '0' && first <= '7') - { - dchar c1 = s.front; - ++count; - s.popFront(); - if (s.empty) - { - static if (doCount) - { - return tuple!("data", "count")(cast (dchar) (c1 - '0'), count); - } - else - { - return cast (dchar) (c1 - '0'); - } - } - dchar c2 = s.front; - if (c2 < '0' || c2 > '7') - { - static if (doCount) - { - return tuple!("data", "count")(cast (dchar)(c1 - '0'), count); - } - else - { - return cast (dchar)(c1 - '0'); - } - } - ++count; - s.popFront(); - dchar c3 = s.front; - if (c3 < '0' || c3 > '7') - { - static if (doCount) - { - return tuple!("data", "count")(cast (dchar) (8 * (c1 - '0') + (c2 - '0')), count); - } - else - { - return cast (dchar) (8 * (c1 - '0') + (c2 - '0')); - } - } - ++count; - s.popFront(); - if (c1 > '3') - throw parseError("Octal sequence is larger than \\377"); - static if (doCount) - { - return tuple!("data", "count")(cast (dchar) (64 * (c1 - '0') + 8 * (c2 - '0') + (c3 - '0')), count); - } - else - { - return cast (dchar) (64 * (c1 - '0') + 8 * (c2 - '0') + (c3 - '0')); - } - } - - dchar result; - - switch (first) - { - case '"': result = '\"'; break; - case '\'': result = '\''; break; - case '?': result = '\?'; break; - case '\\': result = '\\'; break; - case 'a': result = '\a'; break; - case 'b': result = '\b'; break; - case 'f': result = '\f'; break; - case 'n': result = '\n'; break; - case 'r': result = '\r'; break; - case 't': result = '\t'; break; - case 'v': result = '\v'; break; - case 'x': - result = getHexDigit() << 4; - result |= getHexDigit(); - count += 2; - break; - case 'u': - result = getHexDigit() << 12; - result |= getHexDigit() << 8; - result |= getHexDigit() << 4; - result |= getHexDigit(); - count += 4; - break; - case 'U': - result = getHexDigit() << 28; - result |= getHexDigit() << 24; - result |= getHexDigit() << 20; - result |= getHexDigit() << 16; - result |= getHexDigit() << 12; - result |= getHexDigit() << 8; - result |= getHexDigit() << 4; - result |= getHexDigit(); - count += 8; - break; - default: - throw parseError("Unknown escape character " ~ to!string(s.front)); - } - if (s.empty) - throw parseError("Unterminated escape sequence"); - - s.popFront(); - - static if (doCount) - { - return tuple!("data", "count")(cast (dchar) result, ++count); - } - else - { - return cast (dchar) result; - } -} - -@safe pure unittest -{ - string[] s1 = [ - `\"`, `\'`, `\?`, `\\`, `\a`, `\b`, `\f`, `\n`, `\r`, `\t`, `\v`, //Normal escapes - `\141`, - `\x61`, - `\u65E5`, `\U00012456`, - // https://issues.dlang.org/show_bug.cgi?id=9621 (Named Character Entities) - //`\&`, `\"`, - ]; - string[] copyS1 = s1 ~ s1[0 .. 0]; - - const(dchar)[] s2 = [ - '\"', '\'', '\?', '\\', '\a', '\b', '\f', '\n', '\r', '\t', '\v', //Normal escapes - '\141', - '\x61', - '\u65E5', '\U00012456', - // https://issues.dlang.org/show_bug.cgi?id=9621 (Named Character Entities) - //'\&', '\"', - ]; - - foreach (i ; 0 .. s1.length) - { - assert(s2[i] == parseEscape(s1[i])); - assert(s1[i].empty); - - assert(tuple(s2[i], copyS1[i].length) == parseEscape!(string, Yes.doCount)(copyS1[i])); - assert(copyS1[i].empty); - } -} - -@safe pure unittest -{ - import std.exception; - - string[] ss = [ - `hello!`, //Not an escape - `\`, //Premature termination - `\/`, //Not an escape - `\gggg`, //Not an escape - `\xzz`, //Not an hex - `\x0`, //Premature hex end - `\XB9`, //Not legal hex syntax - `\u!!`, //Not a unicode hex - `\777`, //Octal is larger than a byte - `\80`, //Wrong digit at beginning of octal - `\u123`, //Premature hex end - `\U123123` //Premature hex end - ]; - foreach (s ; ss) - { - assertThrown!ConvException(parseEscape(s)); - assertThrown!ConvException(parseEscape!(string, Yes.doCount)(s)); - } -} - -// Undocumented -auto parseElement(Target, Source, Flag!"doCount" doCount = No.doCount)(ref Source s) -if (isInputRange!Source && isSomeChar!(ElementType!Source) && !is(Source == enum) && - isExactSomeString!Target) -{ - import std.array : appender; - auto result = appender!Target(); - - // parse array of chars - if (s.empty) - throw convError!(Source, Target)(s); - if (s.front == '[') - { - return parse!(Target, Source, doCount)(s); - } - - parseCheck!s('\"'); - size_t count = 1; - if (s.empty) - throw convError!(Source, Target)(s); - if (s.front == '\"') - { - s.popFront(); - static if (doCount) - { - return tuple!("data", "count")(result.data, ++count); - } - else - { - return result.data; - } - - } - while (true) - { - if (s.empty) - throw parseError("Unterminated quoted string"); - switch (s.front) - { - case '\"': - s.popFront(); - static if (doCount) - { - return tuple!("data", "count")(result.data, ++count); - } - else - { - return result.data; - } - case '\\': - auto r = parseEscape!(typeof(s), Yes.doCount)(s); - result.put(r[0]); - count += r[1]; - break; - default: - result.put(s.front); - ++count; - s.popFront(); - break; - } - } - assert(false, "Unexpected fallthrough"); -} - -// ditto -auto parseElement(Target, Source, Flag!"doCount" doCount = No.doCount)(ref Source s) -if (isInputRange!Source && isSomeChar!(ElementType!Source) && !is(Source == enum) && - is(CharTypeOf!Target == dchar) && !is(Target == enum)) -{ - Unqual!Target c; - - parseCheck!s('\''); - size_t count = 1; - if (s.empty) - throw convError!(Source, Target)(s); - ++count; // for the following if-else sequence - if (s.front != '\\') - { - c = s.front; - s.popFront(); - } - else - c = parseEscape(s); - parseCheck!s('\''); - static if (doCount) - { - return tuple!("data", "count")(c, ++count); - } - else - { - return c; - } -} - -// ditto -auto parseElement(Target, Source, Flag!"doCount" doCount = No.doCount)(ref Source s) -if (isInputRange!Source && isSomeChar!(ElementType!Source) && - !isSomeString!Target && !isSomeChar!Target) -{ - return parse!(Target, Source, doCount)(s); -} - -// Use this when parsing a type that will ultimately be appended to a -// string. -package template WideElementType(T) -{ - alias E = ElementType!T; - static if (isSomeChar!E) - alias WideElementType = dchar; - else - alias WideElementType = E; -} - - -/*************************************************************** - * Convenience functions for converting one or more arguments - * of any type into _text (the three character widths). - */ -string text(T...)(T args) -if (T.length > 0) { return textImpl!string(args); } - -///ditto -wstring wtext(T...)(T args) -if (T.length > 0) { return textImpl!wstring(args); } - -///ditto -dstring dtext(T...)(T args) -if (T.length > 0) { return textImpl!dstring(args); } - -/// -@safe unittest -{ - assert( text(42, ' ', 1.5, ": xyz") == "42 1.5: xyz"c); - assert(wtext(42, ' ', 1.5, ": xyz") == "42 1.5: xyz"w); - assert(dtext(42, ' ', 1.5, ": xyz") == "42 1.5: xyz"d); -} - -@safe unittest -{ - char c = 'h'; - wchar w = 'ä˝ '; - dchar d = 'እ'; - - assert( text(c, "ello", ' ', w, "弽 ", d, "ው ሰላም ነው") == "hello 你弽 እው ሰላም ነው"c); - assert(wtext(c, "ello", ' ', w, "弽 ", d, "ው ሰላም ነው") == "hello 你弽 እው ሰላም ነው"w); - assert(dtext(c, "ello", ' ', w, "弽 ", d, "ው ሰላም ነው") == "hello 你弽 እው ሰላም ነው"d); - - string cs = "今日は"; - wstring ws = "여보세요"; - dstring ds = "Здравствуйте"; - - assert( text(cs, ' ', ws, " ", ds) == "今日は 여보세요 Здравствуйте"c); - assert(wtext(cs, ' ', ws, " ", ds) == "今日は 여보세요 Здравствуйте"w); - assert(dtext(cs, ' ', ws, " ", ds) == "今日は 여보세요 Здравствуйте"d); -} - -private S textImpl(S, U...)(U args) -{ - static if (U.length == 0) - { - return null; - } - else static if (U.length == 1) - { - return to!S(args[0]); - } - else - { - import std.array : appender; - import std.traits : isSomeChar, isSomeString; - - auto app = appender!S(); - - // assume that on average, parameters will have less - // than 20 elements - app.reserve(U.length * 20); - // Must be static foreach because of https://issues.dlang.org/show_bug.cgi?id=21209 - static foreach (arg; args) - { - static if ( - isSomeChar!(typeof(arg)) - || isSomeString!(typeof(arg)) - || ( isInputRange!(typeof(arg)) && isSomeChar!(ElementType!(typeof(arg))) ) - ) - app.put(arg); - else static if ( - - is(immutable typeof(arg) == immutable uint) || is(immutable typeof(arg) == immutable ulong) || - is(immutable typeof(arg) == immutable int) || is(immutable typeof(arg) == immutable long) - ) - // https://issues.dlang.org/show_bug.cgi?id=17712#c15 - app.put(textImpl!(S)(arg)); - else - app.put(to!S(arg)); - } - - return app.data; - } -} - - -/*************************************************************** -The `octal` facility provides a means to declare a number in base 8. -Using `octal!177` or `octal!"177"` for 127 represented in octal -(same as 0177 in C). - -The rules for strings are the usual for literals: If it can fit in an -`int`, it is an `int`. Otherwise, it is a `long`. But, if the -user specifically asks for a `long` with the `L` suffix, always -give the `long`. Give an unsigned iff it is asked for with the $(D -U) or `u` suffix. _Octals created from integers preserve the type -of the passed-in integral. - -See_Also: - $(LREF parse) for parsing octal strings at runtime. - */ -template octal(string num) -if (isOctalLiteral(num)) -{ - static if ((octalFitsInInt!num && !literalIsLong!num) && !literalIsUnsigned!num) - enum octal = octal!int(num); - else static if ((!octalFitsInInt!num || literalIsLong!num) && !literalIsUnsigned!num) - enum octal = octal!long(num); - else static if ((octalFitsInInt!num && !literalIsLong!num) && literalIsUnsigned!num) - enum octal = octal!uint(num); - else static if ((!octalFitsInInt!(num) || literalIsLong!(num)) && literalIsUnsigned!(num)) - enum octal = octal!ulong(num); - else - static assert(false, "Unusable input " ~ num); -} - -/// Ditto -template octal(alias decimalInteger) -if (is(typeof(decimalInteger)) && isIntegral!(typeof(decimalInteger))) -{ - enum octal = convertToOctal(decimalInteger); -} - -/// -@safe unittest -{ - // Same as 0177 - auto a = octal!177; - // octal is a compile-time device - enum b = octal!160; - // Create an unsigned octal - auto c = octal!"1_000_000u"; - // Leading zeros are allowed when converting from a string - auto d = octal!"0001_200_000"; -} - -/************************************* - * Convert a decimal integer to an octal integer with the same digits. - * Params: - * i = integer to convert - * Returns: - * octal integer with the same type and same digits - */ -private T convertToOctal(T)(T i) -{ - assert((i % 10) < 8); - return i ? convertToOctal(i / 10) * 8 + i % 10 : 0; -} - -/* - Takes a string, num, which is an octal literal, and returns its - value, in the type T specified. -*/ -private T octal(T)(const string num) -{ - assert(isOctalLiteral(num), num ~ " is not an octal literal"); - - T value = 0; - - foreach (const char s; num) - { - if (s < '0' || s > '7') // we only care about digits; skip the rest - // safe to skip - this is checked out in the assert so these - // are just suffixes - continue; - - value *= 8; - value += s - '0'; - } - - return value; -} - -@safe unittest -{ - int a = octal!int("10"); - assert(a == 8); - - int b = octal!int("000137"); - assert(b == 95); -} - -/* -Take a look at int.max and int.max+1 in octal and the logic for this -function follows directly. - */ -private template octalFitsInInt(string octalNum) -{ - // note it is important to strip the literal of all - // non-numbers. kill the suffix and underscores lest they mess up - // the number of digits here that we depend on. - enum bool octalFitsInInt = strippedOctalLiteral(octalNum).length < 11 || - strippedOctalLiteral(octalNum).length == 11 && - strippedOctalLiteral(octalNum)[0] == '1'; -} - -private string strippedOctalLiteral(string original) -{ - string stripped = ""; - bool leading_zeros = true; - foreach (c; original) - { - if (!('0' <= c && c <= '7')) - continue; - if (c == '0') - { - if (leading_zeros) - continue; - } - else - { - leading_zeros = false; - } - stripped ~= c; - } - if (stripped.length == 0) - { - assert(leading_zeros); - return "0"; - } - return stripped; -} - -@safe unittest -{ - static assert(strippedOctalLiteral("7") == "7"); - static assert(strippedOctalLiteral("123") == "123"); - static assert(strippedOctalLiteral("00123") == "123"); - static assert(strippedOctalLiteral("01230") == "1230"); - static assert(strippedOctalLiteral("0") == "0"); - static assert(strippedOctalLiteral("00_000") == "0"); - static assert(strippedOctalLiteral("000_000_12_300") == "12300"); -} - -private template literalIsLong(string num) -{ - static if (num.length > 1) - // can be xxL or xxLu according to spec - enum literalIsLong = (num[$-1] == 'L' || num[$-2] == 'L'); - else - enum literalIsLong = false; -} - -private template literalIsUnsigned(string num) -{ - static if (num.length > 1) - // can be xxU or xxUL according to spec - enum literalIsUnsigned = (num[$-1] == 'u' || num[$-2] == 'u') - // both cases are allowed too - || (num[$-1] == 'U' || num[$-2] == 'U'); - else - enum literalIsUnsigned = false; -} - -/* -Returns if the given string is a correctly formatted octal literal. - -The format is specified in spec/lex.html. The leading zeros are allowed, -but not required. - */ -@safe pure nothrow @nogc -private bool isOctalLiteral(const string num) -{ - if (num.length == 0) - return false; - - // Must start with a digit. - if (num[0] < '0' || num[0] > '7') - return false; - - foreach (i, c; num) - { - if (('0' <= c && c <= '7') || c == '_') // a legal character - continue; - - if (i < num.length - 2) - return false; - - // gotta check for those suffixes - if (c != 'U' && c != 'u' && c != 'L') - return false; - if (i != num.length - 1) - { - // if we're not the last one, the next one must - // also be a suffix to be valid - char c2 = num[$-1]; - if (c2 != 'U' && c2 != 'u' && c2 != 'L') - return false; // spam at the end of the string - if (c2 == c) - return false; // repeats are disallowed - } - } - - return true; -} - -@safe unittest -{ - // ensure that you get the right types, even with embedded underscores - auto w = octal!"100_000_000_000"; - static assert(!is(typeof(w) == int)); - auto w2 = octal!"1_000_000_000"; - static assert(is(typeof(w2) == int)); - - static assert(octal!"45" == 37); - static assert(octal!"0" == 0); - static assert(octal!"7" == 7); - static assert(octal!"10" == 8); - static assert(octal!"666" == 438); - static assert(octal!"0004001" == 2049); - static assert(octal!"00" == 0); - static assert(octal!"0_0" == 0); - - static assert(octal!45 == 37); - static assert(octal!0 == 0); - static assert(octal!7 == 7); - static assert(octal!10 == 8); - static assert(octal!666 == 438); - - static assert(octal!"66_6" == 438); - static assert(octal!"0_0_66_6" == 438); - - static assert(octal!2520046213 == 356535435); - static assert(octal!"2520046213" == 356535435); - - static assert(octal!17777777777 == int.max); - - static assert(!__traits(compiles, octal!823)); - - static assert(!__traits(compiles, octal!"823")); - - static assert(!__traits(compiles, octal!"_823")); - static assert(!__traits(compiles, octal!"spam")); - static assert(!__traits(compiles, octal!"77%")); - - static assert(is(typeof(octal!"17777777777") == int)); - static assert(octal!"17777777777" == int.max); - - static assert(is(typeof(octal!"20000000000U") == ulong)); // Shouldn't this be uint? - static assert(octal!"20000000000" == uint(int.max) + 1); - - static assert(is(typeof(octal!"777777777777777777777") == long)); - static assert(octal!"777777777777777777777" == long.max); - - static assert(is(typeof(octal!"1000000000000000000000U") == ulong)); - static assert(octal!"1000000000000000000000" == ulong(long.max) + 1); - - int a; - long b; - - // biggest value that should fit in an it - a = octal!"17777777777"; - assert(a == int.max); - // should not fit in the int - static assert(!__traits(compiles, a = octal!"20000000000")); - // ... but should fit in a long - b = octal!"20000000000"; - assert(b == 1L + int.max); - - b = octal!"1L"; - assert(b == 1); - b = octal!1L; - assert(b == 1); -} - -// emplace() used to be here but was moved to druntime -public import core.lifetime : emplace; - -// https://issues.dlang.org/show_bug.cgi?id=9559 -@safe unittest -{ - import std.algorithm.iteration : map; - import std.array : array; - import std.typecons : Nullable; - alias I = Nullable!int; - auto ints = [0, 1, 2].map!(i => i & 1 ? I.init : I(i))(); - auto asArray = array(ints); -} - -@system unittest //http://forum.dlang.org/post/nxbdgtdlmwscocbiypjs@forum.dlang.org -{ - import std.array : array; - import std.datetime : SysTime, UTC; - import std.math.traits : isNaN; - - static struct A - { - double i; - } - - static struct B - { - invariant() - { - if (j == 0) - assert(a.i.isNaN(), "why is 'j' zero?? and i is not NaN?"); - else - assert(!a.i.isNaN()); - } - SysTime when; // comment this line avoid the breakage - int j; - A a; - } - - B b1 = B.init; - assert(&b1); // verify that default eyes invariants are ok; - - auto b2 = B(SysTime(0, UTC()), 1, A(1)); - assert(&b2); - auto b3 = B(SysTime(0, UTC()), 1, A(1)); - assert(&b3); - - auto arr = [b2, b3]; - - assert(arr[0].j == 1); - assert(arr[1].j == 1); - auto a2 = arr.array(); // << bang, invariant is raised, also if b2 and b3 are good -} - -@safe unittest -{ - import std.algorithm.comparison : equal; - import std.algorithm.iteration : map; - // Check fix for https://issues.dlang.org/show_bug.cgi?id=2971 - assert(equal(map!(to!int)(["42", "34", "345"]), [42, 34, 345])); -} - -// Undocumented for the time being -void toTextRange(T, W)(T value, W writer) -if (isIntegral!T && isOutputRange!(W, char)) -{ - import core.internal.string : SignedStringBuf, signedToTempString, - UnsignedStringBuf, unsignedToTempString; - - if (value < 0) - { - SignedStringBuf buf = void; - put(writer, signedToTempString(value, buf)); - } - else - { - UnsignedStringBuf buf = void; - put(writer, unsignedToTempString(value, buf)); - } -} - -@safe unittest -{ - import std.array : appender; - auto result = appender!(char[])(); - toTextRange(-1, result); - assert(result.data == "-1"); -} - - -/** - Returns the corresponding _unsigned value for `x` (e.g. if `x` has type - `int`, it returns $(D cast(uint) x)). The advantage compared to the cast - is that you do not need to rewrite the cast if `x` later changes type - (e.g from `int` to `long`). - - Note that the result is always mutable even if the original type was const - or immutable. In order to retain the constness, use $(REF Unsigned, std,traits). - */ -auto unsigned(T)(T x) -if (isIntegral!T) -{ - return cast() cast(Unsigned!T) x; -} - -/// -@safe unittest -{ - import std.traits : Unsigned; - immutable int s = 42; - auto u1 = unsigned(s); //not qualified - static assert(is(typeof(u1) == uint)); - Unsigned!(typeof(s)) u2 = unsigned(s); //same qualification - static assert(is(typeof(u2) == immutable uint)); - immutable u3 = unsigned(s); //explicitly qualified -} - -/// Ditto -auto unsigned(T)(T x) -if (isSomeChar!T) -{ - // All characters are unsigned - static assert(T.min == 0, T.stringof ~ ".min must be zero"); - return cast() x; -} - -@safe unittest -{ - static foreach (T; AliasSeq!(byte, ubyte)) - { - static assert(is(typeof(unsigned(cast(T) 1)) == ubyte)); - static assert(is(typeof(unsigned(cast(const T) 1)) == ubyte)); - static assert(is(typeof(unsigned(cast(immutable T) 1)) == ubyte)); - } - - static foreach (T; AliasSeq!(short, ushort)) - { - static assert(is(typeof(unsigned(cast(T) 1)) == ushort)); - static assert(is(typeof(unsigned(cast(const T) 1)) == ushort)); - static assert(is(typeof(unsigned(cast(immutable T) 1)) == ushort)); - } - - static foreach (T; AliasSeq!(int, uint)) - { - static assert(is(typeof(unsigned(cast(T) 1)) == uint)); - static assert(is(typeof(unsigned(cast(const T) 1)) == uint)); - static assert(is(typeof(unsigned(cast(immutable T) 1)) == uint)); - } - - static foreach (T; AliasSeq!(long, ulong)) - { - static assert(is(typeof(unsigned(cast(T) 1)) == ulong)); - static assert(is(typeof(unsigned(cast(const T) 1)) == ulong)); - static assert(is(typeof(unsigned(cast(immutable T) 1)) == ulong)); - } -} - -@safe unittest -{ - static foreach (T; AliasSeq!(char, wchar, dchar)) - { - static assert(is(typeof(unsigned(cast(T)'A')) == T)); - static assert(is(typeof(unsigned(cast(const T)'A')) == T)); - static assert(is(typeof(unsigned(cast(immutable T)'A')) == T)); - } -} - - -/** - Returns the corresponding _signed value for `x` (e.g. if `x` has type - `uint`, it returns $(D cast(int) x)). The advantage compared to the cast - is that you do not need to rewrite the cast if `x` later changes type - (e.g from `uint` to `ulong`). - - Note that the result is always mutable even if the original type was const - or immutable. In order to retain the constness, use $(REF Signed, std,traits). - */ -auto signed(T)(T x) -if (isIntegral!T) -{ - return cast() cast(Signed!T) x; -} - -/// -@safe unittest -{ - import std.traits : Signed; - - immutable uint u = 42; - auto s1 = signed(u); //not qualified - static assert(is(typeof(s1) == int)); - Signed!(typeof(u)) s2 = signed(u); //same qualification - static assert(is(typeof(s2) == immutable int)); - immutable s3 = signed(u); //explicitly qualified -} - -@system unittest -{ - static foreach (T; AliasSeq!(byte, ubyte)) - { - static assert(is(typeof(signed(cast(T) 1)) == byte)); - static assert(is(typeof(signed(cast(const T) 1)) == byte)); - static assert(is(typeof(signed(cast(immutable T) 1)) == byte)); - } - - static foreach (T; AliasSeq!(short, ushort)) - { - static assert(is(typeof(signed(cast(T) 1)) == short)); - static assert(is(typeof(signed(cast(const T) 1)) == short)); - static assert(is(typeof(signed(cast(immutable T) 1)) == short)); - } - - static foreach (T; AliasSeq!(int, uint)) - { - static assert(is(typeof(signed(cast(T) 1)) == int)); - static assert(is(typeof(signed(cast(const T) 1)) == int)); - static assert(is(typeof(signed(cast(immutable T) 1)) == int)); - } - - static foreach (T; AliasSeq!(long, ulong)) - { - static assert(is(typeof(signed(cast(T) 1)) == long)); - static assert(is(typeof(signed(cast(const T) 1)) == long)); - static assert(is(typeof(signed(cast(immutable T) 1)) == long)); - } -} - -// https://issues.dlang.org/show_bug.cgi?id=10874 -@safe unittest -{ - enum Test { a = 0 } - ulong l = 0; - auto t = l.to!Test; -} - -// asOriginalType -/** -Returns the representation of an enumerated value, i.e. the value converted to -the base type of the enumeration. -*/ -OriginalType!E asOriginalType(E)(E value) -if (is(E == enum)) -{ - return value; -} - -/// -@safe unittest -{ - enum A { a = 42 } - static assert(is(typeof(A.a.asOriginalType) == int)); - assert(A.a.asOriginalType == 42); - enum B : double { a = 43 } - static assert(is(typeof(B.a.asOriginalType) == double)); - assert(B.a.asOriginalType == 43); -} - -/** - A wrapper on top of the built-in cast operator that allows one to restrict - casting of the original type of the value. - - A common issue with using a raw cast is that it may silently continue to - compile even if the value's type has changed during refactoring, - which breaks the initial assumption about the cast. - - Params: - From = The type to cast from. The programmer must ensure it is legal - to make this cast. - */ -template castFrom(From) -{ - /** - Params: - To = The type _to cast _to. - value = The value _to cast. It must be of type `From`, - otherwise a compile-time error is emitted. - - Returns: - the value after the cast, returned by reference if possible. - */ - auto ref to(To, T)(auto ref T value) @system - { - static assert( - is(From == T), - "the value to cast is not of specified type '" ~ From.stringof ~ - "', it is of type '" ~ T.stringof ~ "'" - ); - - static assert( - is(typeof(cast(To) value)), - "can't cast from '" ~ From.stringof ~ "' to '" ~ To.stringof ~ "'" - ); - - return cast(To) value; - } -} - -/// -@system unittest -{ - // Regular cast, which has been verified to be legal by the programmer: - { - long x; - auto y = cast(int) x; - } - - // However this will still compile if 'x' is changed to be a pointer: - { - long* x; - auto y = cast(int) x; - } - - // castFrom provides a more reliable alternative to casting: - { - long x; - auto y = castFrom!long.to!int(x); - } - - // Changing the type of 'x' will now issue a compiler error, - // allowing bad casts to be caught before it's too late: - { - long* x; - static assert( - !__traits(compiles, castFrom!long.to!int(x)) - ); - - // if cast is still needed, must be changed to: - auto y = castFrom!(long*).to!int(x); - } -} - -// https://issues.dlang.org/show_bug.cgi?id=16667 -@system unittest -{ - ubyte[] a = ['a', 'b', 'c']; - assert(castFrom!(ubyte[]).to!(string)(a) == "abc"); -} - -/** -Check the correctness of a string for `hexString`. -The result is true if and only if the input string is composed of whitespace -characters (\f\n\r\t\v lineSep paraSep nelSep) and -an even number of hexadecimal digits (regardless of the case). -*/ -@safe pure @nogc -private bool isHexLiteral(String)(scope const String hexData) -{ - import std.ascii : isHexDigit; - import std.uni : lineSep, paraSep, nelSep; - size_t i; - foreach (const dchar c; hexData) - { - switch (c) - { - case ' ': - case '\t': - case '\v': - case '\f': - case '\r': - case '\n': - case lineSep: - case paraSep: - case nelSep: - continue; - - default: - break; - } - if (c.isHexDigit) - ++i; - else - return false; - } - return !(i & 1); -} - -@safe unittest -{ - // test all the hex digits - static assert( ("0123456789abcdefABCDEF").isHexLiteral); - // empty or white strings are not valid - static assert( "\r\n\t".isHexLiteral); - // but are accepted if the count of hex digits is even - static assert( "A\r\n\tB".isHexLiteral); -} - -@safe unittest -{ - import std.ascii; - // empty/whites - static assert( "".isHexLiteral); - static assert( " \r".isHexLiteral); - static assert( whitespace.isHexLiteral); - static assert( ""w.isHexLiteral); - static assert( " \r"w.isHexLiteral); - static assert( ""d.isHexLiteral); - static assert( " \r"d.isHexLiteral); - static assert( "\u2028\u2029\u0085"d.isHexLiteral); - // odd x strings - static assert( !("5" ~ whitespace).isHexLiteral); - static assert( !"123".isHexLiteral); - static assert( !"1A3".isHexLiteral); - static assert( !"1 23".isHexLiteral); - static assert( !"\r\n\tC".isHexLiteral); - static assert( !"123"w.isHexLiteral); - static assert( !"1A3"w.isHexLiteral); - static assert( !"1 23"w.isHexLiteral); - static assert( !"\r\n\tC"w.isHexLiteral); - static assert( !"123"d.isHexLiteral); - static assert( !"1A3"d.isHexLiteral); - static assert( !"1 23"d.isHexLiteral); - static assert( !"\r\n\tC"d.isHexLiteral); - // even x strings with invalid charset - static assert( !"12gG".isHexLiteral); - static assert( !"2A 3q".isHexLiteral); - static assert( !"12gG"w.isHexLiteral); - static assert( !"2A 3q"w.isHexLiteral); - static assert( !"12gG"d.isHexLiteral); - static assert( !"2A 3q"d.isHexLiteral); - // valid x strings - static assert( ("5A" ~ whitespace).isHexLiteral); - static assert( ("5A 01A C FF de 1b").isHexLiteral); - static assert( ("0123456789abcdefABCDEF").isHexLiteral); - static assert( (" 012 34 5 6789 abcd ef\rAB\nCDEF").isHexLiteral); - static assert( ("5A 01A C FF de 1b"w).isHexLiteral); - static assert( ("0123456789abcdefABCDEF"w).isHexLiteral); - static assert( (" 012 34 5 6789 abcd ef\rAB\nCDEF"w).isHexLiteral); - static assert( ("5A 01A C FF de 1b"d).isHexLiteral); - static assert( ("0123456789abcdefABCDEF"d).isHexLiteral); - static assert( (" 012 34 5 6789 abcd ef\rAB\nCDEF"d).isHexLiteral); - // library version allows what's pointed by https://issues.dlang.org/show_bug.cgi?id=10454 - static assert( ("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF").isHexLiteral); -} - -/** -Converts a hex literal to a string at compile time. - -Takes a string made of hexadecimal digits and returns -the matching string by converting each pair of digits to a character. -The input string can also include white characters, which can be used -to keep the literal string readable in the source code. - -The function is intended to replace the hexadecimal literal strings -starting with `'x'`, which could be removed to simplify the core language. - -Params: - hexData = string to be converted. - -Returns: - a `string`, a `wstring` or a `dstring`, according to the type of hexData. - */ -template hexString(string hexData) -if (hexData.isHexLiteral) -{ - enum hexString = mixin(hexToString(hexData)); -} - -/// ditto -template hexString(wstring hexData) -if (hexData.isHexLiteral) -{ - enum wstring hexString = mixin(hexToString(hexData)); -} - -/// ditto -template hexString(dstring hexData) -if (hexData.isHexLiteral) -{ - enum dstring hexString = mixin(hexToString(hexData)); -} - -/// -@safe unittest -{ - // conversion at compile time - auto string1 = hexString!"304A314B"; - assert(string1 == "0J1K"); - auto string2 = hexString!"304A314B"w; - assert(string2 == "0J1K"w); - auto string3 = hexString!"304A314B"d; - assert(string3 == "0J1K"d); -} - -@safe nothrow pure private -{ - /* These are meant to be used with CTFE. - * They cause the instantiations of hexStrLiteral() - * to be in Phobos, not user code. - */ - string hexToString(string s) - { - return hexStrLiteral(s); - } - - wstring hexToString(wstring s) - { - return hexStrLiteral(s); - } - - dstring hexToString(dstring s) - { - return hexStrLiteral(s); - } -} - -/* - Turn a hexadecimal string into a regular string literal. - I.e. "dead beef" is transformed into "\xde\xad\xbe\xef" - suitable for use in a mixin. - Params: - hexData is string, wstring, or dstring and validated by isHexLiteral() - */ -@trusted nothrow pure -private auto hexStrLiteral(String)(scope String hexData) -{ - import std.ascii : isHexDigit; - alias C = Unqual!(ElementEncodingType!String); // char, wchar or dchar - C[] result; - result.length = 1 + hexData.length * 2 + 1; // don't forget the " " - /* Use a pointer because we know it won't overrun, - * and this will reduce the size of the function substantially - * by not doing the array bounds checks. - * This is why this function is @trusted. - */ - auto r = result.ptr; - r[0] = '"'; - size_t cnt = 0; - foreach (c; hexData) - { - if (c.isHexDigit) - { - if ((cnt & 1) == 0) - { - r[1 + cnt] = '\\'; - r[1 + cnt + 1] = 'x'; - cnt += 2; - } - r[1 + cnt] = c; - ++cnt; - } - } - r[1 + cnt] = '"'; - result.length = 1 + cnt + 1; // trim off any excess length - return result; -} - - -@safe unittest -{ - // compile time - assert(hexString!"46 47 48 49 4A 4B" == "FGHIJK"); - assert(hexString!"30\r\n\t\f\v31 32 33 32 31 30" == "0123210"); - assert(hexString!"ab cd" == hexString!"ABCD"); -} - - -/** - * Convert integer to a range of characters. - * Intended to be lightweight and fast. - * - * Params: - * radix = 2, 8, 10, 16 - * Char = character type for output - * letterCase = lower for deadbeef, upper for DEADBEEF - * value = integer to convert. Can be ubyte, ushort, uint or ulong. If radix - * is 10, can also be byte, short, int or long. - * Returns: - * Random access range with slicing and everything - */ - -auto toChars(ubyte radix = 10, Char = char, LetterCase letterCase = LetterCase.lower, T)(T value) - pure nothrow @nogc @safe -if ((radix == 2 || radix == 8 || radix == 10 || radix == 16) && - isIntegral!T && (radix == 10 || isUnsigned!T)) -{ - alias UT = Unqual!T; - - static if (radix == 10) - { - /* uint.max is 42_9496_7295 - * int.max is 21_4748_3647 - * ulong.max is 1844_6744_0737_0955_1615 - * long.max is 922_3372_0368_5477_5807 - */ - static struct Result - { - void initialize(UT value) - { - import core.internal.string : signedToTempString, unsignedToTempString; - - char[] t = value < 0 - ? signedToTempString!(10, false, char)(value, buf) - : unsignedToTempString!(10, false, char)(value, buf); - - lwr = cast(uint) (buf.length - t.length); - upr = cast(uint) buf.length; - } - - @property size_t length() { return upr - lwr; } - - alias opDollar = length; - - @property bool empty() { return upr == lwr; } - - @property Char front() { return buf[lwr]; } - - void popFront() { ++lwr; } - - @property Char back() { return buf[upr - 1]; } - - void popBack() { --upr; } - - @property Result save() { return this; } - - Char opIndex(size_t i) { return buf[lwr + i]; } - - Result opSlice(size_t lwr, size_t upr) - { - Result result = void; - result.buf = buf; - result.lwr = cast(uint)(this.lwr + lwr); - result.upr = cast(uint)(this.lwr + upr); - return result; - } - - private: - uint lwr = void, upr = void; - char[(UT.sizeof == 4) ? 10 + isSigned!T : 20] buf = void; - } - - Result result; - result.initialize(value); - return result; - } - else - { - static if (radix == 2) - enum SHIFT = 1; - else static if (radix == 8) - enum SHIFT = 3; - else static if (radix == 16) - enum SHIFT = 4; - else - static assert(false, "radix must be 2, 8, 10, or 16"); - static struct Result - { - this(UT value) - { - this.value = value; - - ubyte len = 1; - while (value >>>= SHIFT) - ++len; - this.len = len; - } - - @property size_t length() { return len; } - - @property bool empty() { return len == 0; } - - @property Char front() { return opIndex(0); } - - void popFront() { --len; } - - @property Char back() { return opIndex(len - 1); } - - void popBack() - { - value >>>= SHIFT; - --len; - } - - @property Result save() { return this; } - - Char opIndex(size_t i) - { - Char c = (value >>> ((len - i - 1) * SHIFT)) & ((1 << SHIFT) - 1); - return cast(Char)((radix < 10 || c < 10) ? c + '0' - : (letterCase == LetterCase.upper ? c + 'A' - 10 - : c + 'a' - 10)); - } - - Result opSlice(size_t lwr, size_t upr) - { - Result result = void; - result.value = value >>> ((len - upr) * SHIFT); - result.len = cast(ubyte)(upr - lwr); - return result; - } - - private: - UT value; - ubyte len; - } - - return Result(value); - } -} - -/// -@safe unittest -{ - import std.algorithm.comparison : equal; - - assert(toChars(1).equal("1")); - assert(toChars(1_000_000).equal("1000000")); - - assert(toChars!(2)(2U).equal("10")); - assert(toChars!(16)(255U).equal("ff")); - assert(toChars!(16, char, LetterCase.upper)(255U).equal("FF")); -} - - -@safe unittest -{ - import std.array; - import std.range; - - assert(toChars(123) == toChars(123)); - - { - assert(toChars!2(ubyte(0)).array == "0"); - assert(toChars!2(ushort(0)).array == "0"); - assert(toChars!2(0u).array == "0"); - assert(toChars!2(0Lu).array == "0"); - assert(toChars!2(ubyte(1)).array == "1"); - assert(toChars!2(ushort(1)).array == "1"); - assert(toChars!2(1u).array == "1"); - assert(toChars!2(1Lu).array == "1"); - - auto r = toChars!2(2u); - assert(r.length == 2); - assert(r[0] == '1'); - assert(r[1 .. 2].array == "0"); - auto s = r.save; - assert(r.array == "10"); - assert(s.retro.array == "01"); - } - { - assert(toChars!8(ubyte(0)).array == "0"); - assert(toChars!8(ushort(0)).array == "0"); - assert(toChars!8(0u).array == "0"); - assert(toChars!8(0Lu).array == "0"); - assert(toChars!8(1u).array == "1"); - assert(toChars!8(1234567Lu).array == "4553207"); - assert(toChars!8(ubyte.max).array == "377"); - assert(toChars!8(ushort.max).array == "177777"); - - auto r = toChars!8(8u); - assert(r.length == 2); - assert(r[0] == '1'); - assert(r[1 .. 2].array == "0"); - auto s = r.save; - assert(r.array == "10"); - assert(s.retro.array == "01"); - } - { - assert(toChars!10(ubyte(0)).array == "0"); - assert(toChars!10(ushort(0)).array == "0"); - assert(toChars!10(0u).array == "0"); - assert(toChars!10(0Lu).array == "0"); - assert(toChars!10(1u).array == "1"); - assert(toChars!10(1234567Lu).array == "1234567"); - assert(toChars!10(ubyte.max).array == "255"); - assert(toChars!10(ushort.max).array == "65535"); - assert(toChars!10(uint.max).array == "4294967295"); - assert(toChars!10(ulong.max).array == "18446744073709551615"); - - auto r = toChars(10u); - assert(r.length == 2); - assert(r[0] == '1'); - assert(r[1 .. 2].array == "0"); - auto s = r.save; - assert(r.array == "10"); - assert(s.retro.array == "01"); - } - { - assert(toChars!10(0).array == "0"); - assert(toChars!10(0L).array == "0"); - assert(toChars!10(1).array == "1"); - assert(toChars!10(1234567L).array == "1234567"); - assert(toChars!10(byte.max).array == "127"); - assert(toChars!10(short.max).array == "32767"); - assert(toChars!10(int.max).array == "2147483647"); - assert(toChars!10(long.max).array == "9223372036854775807"); - assert(toChars!10(-byte.max).array == "-127"); - assert(toChars!10(-short.max).array == "-32767"); - assert(toChars!10(-int.max).array == "-2147483647"); - assert(toChars!10(-long.max).array == "-9223372036854775807"); - assert(toChars!10(byte.min).array == "-128"); - assert(toChars!10(short.min).array == "-32768"); - assert(toChars!10(int.min).array == "-2147483648"); - assert(toChars!10(long.min).array == "-9223372036854775808"); - - auto r = toChars!10(10); - assert(r.length == 2); - assert(r[0] == '1'); - assert(r[1 .. 2].array == "0"); - auto s = r.save; - assert(r.array == "10"); - assert(s.retro.array == "01"); - } - { - assert(toChars!(16)(0u).array == "0"); - assert(toChars!(16)(0Lu).array == "0"); - assert(toChars!(16)(10u).array == "a"); - assert(toChars!(16, char, LetterCase.upper)(0x12AF34567Lu).array == "12AF34567"); - assert(toChars!(16)(ubyte(0)).array == "0"); - assert(toChars!(16)(ushort(0)).array == "0"); - assert(toChars!(16)(ubyte.max).array == "ff"); - assert(toChars!(16)(ushort.max).array == "ffff"); - - auto r = toChars!(16)(16u); - assert(r.length == 2); - assert(r[0] == '1'); - assert(r[1 .. 2].array == "0"); - auto s = r.save; - assert(r.array == "10"); - assert(s.retro.array == "01"); - } -} - -@safe unittest // opSlice (https://issues.dlang.org/show_bug.cgi?id=16192) -{ - import std.meta : AliasSeq; - - static struct Test { ubyte radix; uint number; } - - alias tests = AliasSeq!( - Test(2, 0b1_0110_0111u), - Test(2, 0b10_1100_1110u), - Test(8, octal!123456701u), - Test(8, octal!1234567012u), - Test(10, 123456789u), - Test(10, 1234567890u), - Test(16, 0x789ABCDu), - Test(16, 0x789ABCDEu), - ); - - foreach (test; tests) - { - enum ubyte radix = test.radix; - auto original = toChars!radix(test.number); - - // opSlice vs popFront - auto r = original.save; - size_t i = 0; - for (; !r.empty; r.popFront(), ++i) - { - assert(original[i .. original.length].tupleof == r.tupleof); - // tupleof is used to work around https://issues.dlang.org/show_bug.cgi?id=16216. - } - - // opSlice vs popBack - r = original.save; - i = 0; - for (; !r.empty; r.popBack(), ++i) - { - assert(original[0 .. original.length - i].tupleof == r.tupleof); - } - - // opSlice vs both popFront and popBack - r = original.save; - i = 0; - for (; r.length >= 2; r.popFront(), r.popBack(), ++i) - { - assert(original[i .. original.length - i].tupleof == r.tupleof); - } - } -} - -// Converts an unsigned integer to a compile-time string constant. -package enum toCtString(ulong n) = n.stringof[0 .. $ - "LU".length]; - -// Check that .stringof does what we expect, since it's not guaranteed by the -// language spec. -@safe /*@betterC*/ unittest -{ - assert(toCtString!0 == "0"); - assert(toCtString!123456 == "123456"); -} diff --git a/phobos/std/csv.d b/phobos/std/csv.d deleted file mode 100644 index 9ee9b5d..0000000 --- a/phobos/std/csv.d +++ /dev/null @@ -1,1890 +0,0 @@ -//Written in the D programming language - -/** - * Implements functionality to read Comma Separated Values and its variants - * from an $(REF_ALTTEXT input range, isInputRange, std,range,primitives) of `dchar`. - * - * Comma Separated Values provide a simple means to transfer and store - * tabular data. It has been common for programs to use their own - * variant of the CSV format. This parser will loosely follow the - * $(HTTP tools.ietf.org/html/rfc4180, RFC-4180). CSV input should adhere - * to the following criteria (differences from RFC-4180 in parentheses): - * - * $(UL - * $(LI A record is separated by a new line (CRLF,LF,CR)) - * $(LI A final record may end with a new line) - * $(LI A header may be provided as the first record in input) - * $(LI A record has fields separated by a comma (customizable)) - * $(LI A field containing new lines, commas, or double quotes - * should be enclosed in double quotes (customizable)) - * $(LI Double quotes in a field are escaped with a double quote) - * $(LI Each record should contain the same number of fields) - * ) - * - * Example: - * - * ------- - * import std.algorithm; - * import std.array; - * import std.csv; - * import std.stdio; - * import std.typecons; - * - * void main() - * { - * auto text = "Joe,Carpenter,300000\nFred,Blacksmith,400000\r\n"; - * - * foreach (record; csvReader!(Tuple!(string, string, int))(text)) - * { - * writefln("%s works as a %s and earns $%d per year", - * record[0], record[1], record[2]); - * } - * - * // To read the same string from the file "filename.csv": - * - * auto file = File("filename.csv", "r"); - * foreach (record; - * file.byLine.joiner("\n").csvReader!(Tuple!(string, string, int))) - * { - * writefln("%s works as a %s and earns $%d per year", - * record[0], record[1], record[2]); - * } - } - * } - * ------- - * - * When an input contains a header the `Contents` can be specified as an - * associative array. Passing null to signify that a header is present. - * - * ------- - * auto text = "Name,Occupation,Salary\r" ~ - * "Joe,Carpenter,300000\nFred,Blacksmith,400000\r\n"; - * - * foreach (record; csvReader!(string[string]) - * (text, null)) - * { - * writefln("%s works as a %s and earns $%s per year.", - * record["Name"], record["Occupation"], - * record["Salary"]); - * } - * - * // To read the same string from the file "filename.csv": - * - * auto file = File("filename.csv", "r"); - * - * foreach (record; csvReader!(string[string]) - * (file.byLine.joiner("\n"), null)) - * { - * writefln("%s works as a %s and earns $%s per year.", - * record["Name"], record["Occupation"], - * record["Salary"]); - * } - * ------- - * - * This module allows content to be iterated by record stored in a struct, - * class, associative array, or as a range of fields. Upon detection of an - * error an CSVException is thrown (can be disabled). csvNextToken has been - * made public to allow for attempted recovery. - * - * Disabling exceptions will lift many restrictions specified above. A quote - * can appear in a field if the field was not quoted. If in a quoted field any - * quote by itself, not at the end of a field, will end processing for that - * field. The field is ended when there is no input, even if the quote was not - * closed. - * - * See_Also: - * $(HTTP en.wikipedia.org/wiki/Comma-separated_values, Wikipedia - * Comma-separated values) - * - * Copyright: Copyright 2011 - * License: $(HTTP www.boost.org/LICENSE_1_0.txt, Boost License 1.0). - * Authors: Jesse Phillips - * Source: $(PHOBOSSRC std/csv.d) - */ -module std.csv; - -import std.conv; -import std.exception : basicExceptionCtors; -import std.range.primitives; -import std.traits; - -/** - * Exception containing the row and column for when an exception was thrown. - * - * Numbering of both row and col start at one and corresponds to the location - * in the file rather than any specified header. Special consideration should - * be made when there is failure to match the header see $(LREF - * HeaderMismatchException) for details. - * - * When performing type conversions, $(REF ConvException, std,conv) is stored in - * the `next` field. - */ -class CSVException : Exception -{ - /// - size_t row, col; - - // FIXME: Use std.exception.basicExceptionCtors here once - // https://issues.dlang.org/show_bug.cgi?id=11500 is fixed - - this(string msg, string file = __FILE__, size_t line = __LINE__, - Throwable next = null) @nogc @safe pure nothrow - { - super(msg, file, line, next); - } - - this(string msg, Throwable next, string file = __FILE__, - size_t line = __LINE__) @nogc @safe pure nothrow - { - super(msg, file, line, next); - } - - this(string msg, size_t row, size_t col, Throwable next = null, - string file = __FILE__, size_t line = __LINE__) @nogc @safe pure nothrow - { - super(msg, next, file, line); - this.row = row; - this.col = col; - } - - override string toString() @safe pure const - { - return "(Row: " ~ to!string(row) ~ - ", Col: " ~ to!string(col) ~ ") " ~ msg; - } -} - -/// -@safe unittest -{ - import std.exception : collectException; - import std.algorithm.searching : count; - string text = "a,b,c\nHello,65"; - auto ex = collectException!CSVException(csvReader(text).count); - assert(ex.toString == "(Row: 0, Col: 0) Row 2's length 2 does not match previous length of 3."); -} - -/// -@safe unittest -{ - import std.exception : collectException; - import std.algorithm.searching : count; - import std.typecons : Tuple; - string text = "a,b\nHello,65"; - auto ex = collectException!CSVException(csvReader!(Tuple!(string,int))(text).count); - assert(ex.toString == "(Row: 1, Col: 2) Unexpected 'b' when converting from type string to type int"); -} - -// https://issues.dlang.org/show_bug.cgi?id=24478 -@safe unittest -{ - import std.exception : collectException; - import std.algorithm.searching : count; - string text = "A, B\n1, 2, 3"; - auto ex = collectException!CSVException(csvReader!(string[string])(text, null).count); - assert(ex.toString == "(Row: 1, Col: 3) row contains more values than header"); -} - -@safe pure unittest -{ - import std.string; - auto e1 = new Exception("Foobar"); - auto e2 = new CSVException("args", e1); - assert(e2.next is e1); - - size_t r = 13; - size_t c = 37; - - auto e3 = new CSVException("argv", r, c); - assert(e3.row == r); - assert(e3.col == c); - - auto em = e3.toString(); - assert(em.indexOf("13") != -1); - assert(em.indexOf("37") != -1); -} - -/** - * Exception thrown when a Token is identified to not be completed: a quote is - * found in an unquoted field, data continues after a closing quote, or the - * quoted field was not closed before data was empty. - */ -class IncompleteCellException : CSVException -{ - /** - * Data pulled from input before finding a problem - * - * This field is populated when using $(LREF csvReader) - * but not by $(LREF csvNextToken) as this data will have - * already been fed to the output range. - */ - dstring partialData; - - mixin basicExceptionCtors; -} - -/// -@safe unittest -{ - import std.exception : assertThrown; - string text = "a,\"b,c\nHello,65,2.5"; - assertThrown!IncompleteCellException(text.csvReader(["a","b","c"])); -} - -@safe pure unittest -{ - auto e1 = new Exception("Foobar"); - auto e2 = new IncompleteCellException("args", e1); - assert(e2.next is e1); -} - -/** - * Exception thrown under different conditions based on the type of $(D - * Contents). - * - * Structure, Class, and Associative Array - * $(UL - * $(LI When a header is provided but a matching column is not found) - * ) - * - * Other - * $(UL - * $(LI When a header is provided but a matching column is not found) - * $(LI Order did not match that found in the input) - * ) - * - * Since a row and column is not meaningful when a column specified by the - * header is not found in the data, both row and col will be zero. Otherwise - * row is always one and col is the first instance found in header that - * occurred before the previous starting at one. - */ -class HeaderMismatchException : CSVException -{ - mixin basicExceptionCtors; -} - -/// -@safe unittest -{ - import std.exception : assertThrown; - string text = "a,b,c\nHello,65,2.5"; - assertThrown!HeaderMismatchException(text.csvReader(["b","c","invalid"])); -} - -@safe pure unittest -{ - auto e1 = new Exception("Foobar"); - auto e2 = new HeaderMismatchException("args", e1); - assert(e2.next is e1); -} - -/** - * Determines the behavior for when an error is detected. - * - * Disabling exception will follow these rules: - * $(UL - * $(LI A quote can appear in a field if the field was not quoted.) - * $(LI If in a quoted field any quote by itself, not at the end of a - * field, will end processing for that field.) - * $(LI The field is ended when there is no input, even if the quote was - * not closed.) - * $(LI If the given header does not match the order in the input, the - * content will return as it is found in the input.) - * $(LI If the given header contains columns not found in the input they - * will be ignored.) - * ) -*/ -enum Malformed -{ - ignore, /// No exceptions are thrown due to incorrect CSV. - throwException /// Use exceptions when input has incorrect CSV. -} - -/// -@safe unittest -{ - import std.algorithm.comparison : equal; - import std.algorithm.searching : count; - import std.exception : assertThrown; - - string text = "a,b,c\nHello,65,\"2.5"; - assertThrown!IncompleteCellException(text.csvReader.count); - - // ignore the exceptions and try to handle invalid CSV - auto firstLine = text.csvReader!(string, Malformed.ignore)(null).front; - assert(firstLine.equal(["Hello", "65", "2.5"])); -} - -/** -Returns an $(REF_ALTTEXT input range, isInputRange, std,range,primitives) -for iterating over records found in `input`. - -An optional `header` can be provided. The first record will be read in -as the header. If `Contents` is a struct then the header provided is -expected to correspond to the fields in the struct. When `Contents` is -not a type which can contain the entire record, the `header` must be -provided in the same order as the input or an exception is thrown. - -Returns: - An input range R as defined by - $(REF isInputRange, std,range,primitives). When `Contents` is a - struct, class, or an associative array, the element type of R is - `Contents`, otherwise the element type of R is itself a range with - element type `Contents`. - - If a `header` argument is provided, - the returned range provides a `header` field for accessing the header - from the input in array form. - -Throws: - $(LREF CSVException) When a quote is found in an unquoted field, - data continues after a closing quote, the quoted field was not - closed before data was empty, a conversion failed, or when the row's - length does not match the previous length. - - $(LREF HeaderMismatchException) when a header is provided but a - matching column is not found or the order did not match that found in - the input. Read the exception documentation for specific details of - when the exception is thrown for different types of `Contents`. -*/ -auto csvReader(Contents = string,Malformed ErrorLevel = Malformed.throwException, Range, Separator = char)(Range input, - Separator delimiter = ',', Separator quote = '"', - bool allowInconsistentDelimiterCount = false) -if (isInputRange!Range && is(immutable ElementType!Range == immutable dchar) - && isSomeChar!(Separator) - && !is(Contents T : T[U], U : string)) -{ - return CsvReader!(Contents,ErrorLevel,Range, - Unqual!(ElementType!Range),string[]) - (input, delimiter, quote, allowInconsistentDelimiterCount); -} - -/// ditto -auto csvReader(Contents = string, - Malformed ErrorLevel = Malformed.throwException, - Range, Header, Separator = char) - (Range input, Header header, - Separator delimiter = ',', Separator quote = '"', - bool allowInconsistentDelimiterCount = false) -if (isInputRange!Range && is(immutable ElementType!Range == immutable dchar) - && isSomeChar!(Separator) - && isForwardRange!Header - && isSomeString!(ElementType!Header)) -{ - return CsvReader!(Contents,ErrorLevel,Range, - Unqual!(ElementType!Range),Header) - (input, header, delimiter, quote, allowInconsistentDelimiterCount); -} - -/// ditto -auto csvReader(Contents = string, - Malformed ErrorLevel = Malformed.throwException, - Range, Header, Separator = char) - (Range input, Header header, - Separator delimiter = ',', Separator quote = '"', - bool allowInconsistentDelimiterCount = false) -if (isInputRange!Range && is(immutable ElementType!Range == immutable dchar) - && isSomeChar!(Separator) - && is(Header : typeof(null))) -{ - return CsvReader!(Contents,ErrorLevel,Range, - Unqual!(ElementType!Range),string[]) - (input, cast(string[]) null, delimiter, quote, - allowInconsistentDelimiterCount); -} - - -/** -The `Contents` of the input can be provided if all the records are the -same type such as all integer data: -*/ -@safe unittest -{ - import std.algorithm.comparison : equal; - string text = "76,26,22"; - auto records = text.csvReader!int; - assert(records.equal!equal([ - [76, 26, 22], - ])); -} - -/** -Using a struct with modified delimiter: -*/ -@safe unittest -{ - import std.algorithm.comparison : equal; - string text = "Hello;65;2.5\nWorld;123;7.5"; - struct Layout - { - string name; - int value; - double other; - } - - auto records = text.csvReader!Layout(';'); - assert(records.equal([ - Layout("Hello", 65, 2.5), - Layout("World", 123, 7.5), - ])); -} - -/** -Specifying `ErrorLevel` as $(LREF Malformed.ignore) will lift restrictions -on the format. This example shows that an exception is not thrown when -finding a quote in a field not quoted. -*/ -@safe unittest -{ - string text = "A \" is now part of the data"; - auto records = text.csvReader!(string, Malformed.ignore); - auto record = records.front; - - assert(record.front == text); -} - -/// Read only column "b" -@safe unittest -{ - import std.algorithm.comparison : equal; - string text = "a,b,c\nHello,65,63.63\nWorld,123,3673.562"; - auto records = text.csvReader!int(["b"]); - - assert(records.equal!equal([ - [65], - [123], - ])); -} - -/// Read while rearranging the columns by specifying a header with a different order" -@safe unittest -{ - import std.algorithm.comparison : equal; - string text = "a,b,c\nHello,65,2.5\nWorld,123,7.5"; - struct Layout - { - int value; - double other; - string name; - } - - auto records = text.csvReader!Layout(["b","c","a"]); - assert(records.equal([ - Layout(65, 2.5, "Hello"), - Layout(123, 7.5, "World") - ])); -} - -/** -The header can also be left empty if the input contains a header row -and all columns should be iterated. -The header from the input can always be accessed from the `header` field. -*/ -@safe unittest -{ - string text = "a,b,c\nHello,65,63.63"; - auto records = text.csvReader(null); - - assert(records.header == ["a","b","c"]); -} - -/** -Handcrafted csv files tend to have an variable amount of columns. - -By default `std.csv` will throw if the number of columns on a line -is unequal to the number of columns of the first line. -To allow, or disallow, a variable amount of columns a `bool` can be passed to -all overloads of the `csvReader` function as shown below. -*/ -@safe unittest -{ - import std.algorithm.comparison : equal; - - string text = "76,26,22\n1,2\n3,4,5,6"; - auto records = text.csvReader!int(',', '"', true); - - assert(records.equal!equal([ - [76, 26, 22], - [1, 2], - [3, 4, 5, 6] - ])); -} - -/// ditto -@safe unittest -{ - import std.algorithm.comparison : equal; - - static struct Three - { - int a; - int b; - int c; - } - - string text = "76,26,22\n1,2\n3,4,5,6"; - auto records = text.csvReader!Three(',', '"', true); - - assert(records.equal([ - Three(76, 26, 22), - Three(1, 2, 0), - Three(3, 4, 5) - ])); -} - -/// ditto -@safe unittest -{ - import std.algorithm.comparison : equal; - - auto text = "Name,Occupation,Salary\r" ~ - "Joe,Carpenter,300000\nFred,Blacksmith\r\n"; - - auto r = csvReader!(string[string])(text, null, ',', '"', true); - - assert(r.equal([ - [ "Name" : "Joe", "Occupation" : "Carpenter", "Salary" : "300000" ], - [ "Name" : "Fred", "Occupation" : "Blacksmith" ] - ])); -} - -// Test standard iteration over input. -@safe pure unittest -{ - string str = `one,"two ""quoted"""` ~ "\n\"three\nnew line\",\nfive,six"; - auto records = csvReader(str); - - int count; - foreach (record; records) - { - foreach (cell; record) - { - count++; - } - } - assert(count == 6); -} - -// Test newline on last record -@safe pure unittest -{ - string str = "one,two\nthree,four\n"; - auto records = csvReader(str); - records.popFront(); - records.popFront(); - assert(records.empty); -} - -// Test shorter row length -@safe pure unittest -{ - wstring str = "one,1\ntwo\nthree"w; - struct Layout - { - string name; - int value; - } - - Layout[3] ans; - ans[0].name = "one"; - ans[0].value = 1; - ans[1].name = "two"; - ans[1].value = 0; - ans[2].name = "three"; - ans[2].value = 0; - - auto records = csvReader!(Layout,Malformed.ignore)(str); - - int count; - foreach (record; records) - { - assert(ans[count].name == record.name); - assert(ans[count].value == record.value); - count++; - } -} - -// Test shorter row length exception -@safe pure unittest -{ - import std.exception; - - struct A - { - string a,b,c; - } - - auto strs = ["one,1\ntwo", - "one\ntwo,2,二\nthree,3,三", - "one\ntwo,2\nthree,3", - "one,1\ntwo\nthree,3"]; - - foreach (str; strs) - { - auto records = csvReader!A(str); - assertThrown!CSVException((){foreach (record; records) { }}()); - } -} - - -// Test structure conversion interface with unicode. -@safe pure unittest -{ - import std.math.algebraic : abs; - - wstring str = "\U00010143Hello,65,63.63\nWorld,123,3673.562"w; - struct Layout - { - string name; - int value; - double other; - } - - Layout[2] ans; - ans[0].name = "\U00010143Hello"; - ans[0].value = 65; - ans[0].other = 63.63; - ans[1].name = "World"; - ans[1].value = 123; - ans[1].other = 3673.562; - - auto records = csvReader!Layout(str); - - int count; - foreach (record; records) - { - assert(ans[count].name == record.name); - assert(ans[count].value == record.value); - assert(abs(ans[count].other - record.other) < 0.00001); - count++; - } - assert(count == ans.length); -} - -// Test input conversion interface -@safe pure unittest -{ - import std.algorithm; - string str = `76,26,22`; - int[] ans = [76,26,22]; - auto records = csvReader!int(str); - - foreach (record; records) - { - assert(equal(record, ans)); - } -} - -// Test struct & header interface and same unicode -@safe unittest -{ - import std.math.algebraic : abs; - - string str = "a,b,c\nHello,65,63.63\n➊➋➂❹,123,3673.562"; - struct Layout - { - int value; - double other; - string name; - } - - auto records = csvReader!Layout(str, ["b","c","a"]); - - Layout[2] ans; - ans[0].name = "Hello"; - ans[0].value = 65; - ans[0].other = 63.63; - ans[1].name = "➊➋➂❹"; - ans[1].value = 123; - ans[1].other = 3673.562; - - int count; - foreach (record; records) - { - assert(ans[count].name == record.name); - assert(ans[count].value == record.value); - assert(abs(ans[count].other - record.other) < 0.00001); - count++; - } - assert(count == ans.length); - -} - -// Test header interface -@safe unittest -{ - import std.algorithm; - - string str = "a,b,c\nHello,65,63.63\nWorld,123,3673.562"; - auto records = csvReader!int(str, ["b"]); - - auto ans = [[65],[123]]; - foreach (record; records) - { - assert(equal(record, ans.front)); - ans.popFront(); - } - - try - { - csvReader(str, ["c","b"]); - assert(0); - } - catch (HeaderMismatchException e) - { - assert(e.col == 2); - } - auto records2 = csvReader!(string,Malformed.ignore) - (str, ["b","a"], ',', '"'); - - auto ans2 = [["Hello","65"],["World","123"]]; - foreach (record; records2) - { - assert(equal(record, ans2.front)); - ans2.popFront(); - } - - str = "a,c,e\nJoe,Carpenter,300000\nFred,Fly,4"; - records2 = csvReader!(string,Malformed.ignore) - (str, ["a","b","c","d"], ',', '"'); - - ans2 = [["Joe","Carpenter"],["Fred","Fly"]]; - foreach (record; records2) - { - assert(equal(record, ans2.front)); - ans2.popFront(); - } -} - -// Test null header interface -@safe unittest -{ - string str = "a,b,c\nHello,65,63.63\nWorld,123,3673.562"; - auto records = csvReader(str, ["a"]); - - assert(records.header == ["a","b","c"]); -} - -// Test unchecked read -@safe pure unittest -{ - string str = "one \"quoted\""; - foreach (record; csvReader!(string,Malformed.ignore)(str)) - { - foreach (cell; record) - { - assert(cell == "one \"quoted\""); - } - } - - str = "one \"quoted\",two \"quoted\" end"; - struct Ans - { - string a,b; - } - foreach (record; csvReader!(Ans,Malformed.ignore)(str)) - { - assert(record.a == "one \"quoted\""); - assert(record.b == "two \"quoted\" end"); - } -} - -// Test partial data returned -@safe pure unittest -{ - string str = "\"one\nnew line"; - - try - { - foreach (record; csvReader(str)) - {} - assert(0); - } - catch (IncompleteCellException ice) - { - assert(ice.partialData == "one\nnew line"); - } -} - -// Test Windows line break -@safe pure unittest -{ - string str = "one,two\r\nthree"; - - auto records = csvReader(str); - auto record = records.front; - assert(record.front == "one"); - record.popFront(); - assert(record.front == "two"); - records.popFront(); - record = records.front; - assert(record.front == "three"); -} - - -// Test associative array support with unicode separator -@safe unittest -{ - string str = "1❁2❁3\n34❁65❁63\n34❁65❁63"; - - auto records = csvReader!(string[string])(str,["3","1"],'❁'); - int count; - foreach (record; records) - { - count++; - assert(record["1"] == "34"); - assert(record["3"] == "63"); - } - assert(count == 2); -} - -// Test restricted range -@safe unittest -{ - import std.typecons; - struct InputRange - { - dstring text; - - this(dstring txt) - { - text = txt; - } - - @property auto empty() - { - return text.empty; - } - - void popFront() - { - text.popFront(); - } - - @property dchar front() - { - return text[0]; - } - } - auto ir = InputRange("Name,Occupation,Salary\r"d~ - "Joe,Carpenter,300000\nFred,Blacksmith,400000\r\n"d); - - foreach (record; csvReader(ir, cast(string[]) null)) - foreach (cell; record) {} - foreach (record; csvReader!(Tuple!(string, string, int)) - (ir,cast(string[]) null)) {} - foreach (record; csvReader!(string[string]) - (ir,cast(string[]) null)) {} -} - -@safe unittest // const/immutable dchars -{ - import std.algorithm.iteration : map; - import std.array : array; - const(dchar)[] c = "foo,bar\n"; - assert(csvReader(c).map!array.array == [["foo", "bar"]]); - immutable(dchar)[] i = "foo,bar\n"; - assert(csvReader(i).map!array.array == [["foo", "bar"]]); -} - -/* - * This struct is stored on the heap for when the structures - * are passed around. - */ -private pure struct Input(Range, Malformed ErrorLevel) -{ - Range range; - size_t row, col; - static if (ErrorLevel == Malformed.throwException) - size_t rowLength; -} - -/* - * Range for iterating CSV records. - * - * This range is returned by the $(LREF csvReader) functions. It can be - * created in a similar manner to allow `ErrorLevel` be set to $(LREF - * Malformed).ignore if best guess processing should take place. - */ -private struct CsvReader(Contents, Malformed ErrorLevel, Range, Separator, Header) -if (isSomeChar!Separator && isInputRange!Range - && is(immutable ElementType!Range == immutable dchar) - && isForwardRange!Header && isSomeString!(ElementType!Header)) -{ -private: - Input!(Range, ErrorLevel)* _input; - Separator _separator; - Separator _quote; - size_t[] indices; - bool _empty; - bool _allowInconsistentDelimiterCount; - static if (is(Contents == struct) || is(Contents == class)) - { - Contents recordContent; - CsvRecord!(string, ErrorLevel, Range, Separator) recordRange; - } - else static if (is(Contents T : T[U], U : string)) - { - Contents recordContent; - CsvRecord!(T, ErrorLevel, Range, Separator) recordRange; - } - else - CsvRecord!(Contents, ErrorLevel, Range, Separator) recordRange; -public: - /** - * Header from the input in array form. - * - * ------- - * string str = "a,b,c\nHello,65,63.63"; - * auto records = csvReader(str, ["a"]); - * - * assert(records.header == ["a","b","c"]); - * ------- - */ - string[] header; - - /** - * Constructor to initialize the input, delimiter and quote for input - * without a header. - * - * ------- - * string str = `76;^26^;22`; - * int[] ans = [76,26,22]; - * auto records = CsvReader!(int,Malformed.ignore,string,char,string[]) - * (str, ';', '^'); - * - * foreach (record; records) - * { - * assert(equal(record, ans)); - * } - * ------- - */ - this(Range input, Separator delimiter, Separator quote, - bool allowInconsistentDelimiterCount) - { - _input = new Input!(Range, ErrorLevel)(input); - _separator = delimiter; - _quote = quote; - _allowInconsistentDelimiterCount = allowInconsistentDelimiterCount; - - if (_input.range.empty) - { - _empty = true; - return; - } - - prime(); - } - - /** - * Constructor to initialize the input, delimiter and quote for input - * with a header. - * - * ------- - * string str = `high;mean;low\n76;^26^;22`; - * auto records = CsvReader!(int,Malformed.ignore,string,char,string[]) - * (str, ["high","low"], ';', '^'); - * - * int[] ans = [76,22]; - * foreach (record; records) - * { - * assert(equal(record, ans)); - * } - * ------- - * - * Throws: - * $(LREF HeaderMismatchException) when a header is provided but a - * matching column is not found or the order did not match that found - * in the input (non-struct). - */ - this(Range input, Header colHeaders, Separator delimiter, Separator quote, - bool allowInconsistentDelimiterCount) - { - _input = new Input!(Range, ErrorLevel)(input); - _separator = delimiter; - _quote = quote; - _allowInconsistentDelimiterCount = allowInconsistentDelimiterCount; - - if (_input.range.empty) - { - _empty = true; - return; - } - - size_t[string] colToIndex; - foreach (h; colHeaders) - { - colToIndex[h] = size_t.max; - } - - auto r = CsvRecord!(string, ErrorLevel, Range, Separator) - (_input, _separator, _quote, indices, - _allowInconsistentDelimiterCount); - - size_t colIndex; - foreach (col; r) - { - header ~= col; - auto ptr = col in colToIndex; - if (ptr) - *ptr = colIndex; - colIndex++; - } - // The above loop empties the header row. - recordRange._empty = true; - recordRange._allowInconsistentDelimiterCount = - allowInconsistentDelimiterCount; - - indices.length = colToIndex.length; - int i; - foreach (h; colHeaders) - { - immutable index = colToIndex[h]; - static if (ErrorLevel != Malformed.ignore) - if (index == size_t.max) - throw new HeaderMismatchException - ("Header not found: " ~ to!string(h)); - indices[i++] = index; - } - - static if (!is(Contents == struct) && !is(Contents == class)) - { - static if (is(Contents T : T[U], U : string)) - { - import std.algorithm.sorting : sort; - sort(indices); - } - else static if (ErrorLevel == Malformed.ignore) - { - import std.algorithm.sorting : sort; - sort(indices); - } - else - { - import std.algorithm.searching : findAdjacent; - import std.algorithm.sorting : isSorted; - if (!isSorted(indices)) - { - auto ex = new HeaderMismatchException - ("Header in input does not match specified header."); - findAdjacent!"a > b"(indices); - ex.row = 1; - ex.col = indices.front; - - throw ex; - } - } - } - - popFront(); - } - - /** - * Part of an input range as defined by - * $(REF isInputRange, std,range,primitives). - * - * Returns: - * If `Contents` is a struct, will be filled with record data. - * - * If `Contents` is a class, will be filled with record data. - * - * If `Contents` is a associative array, will be filled - * with record data. - * - * If `Contents` is non-struct, a $(LREF CsvRecord) will be - * returned. - */ - @property auto front() - { - assert(!empty, "Attempting to fetch the front of an empty CsvReader"); - static if (is(Contents == struct) || is(Contents == class)) - { - return recordContent; - } - else static if (is(Contents T : T[U], U : string)) - { - return recordContent; - } - else - { - return recordRange; - } - } - - /** - * Part of an input range as defined by - * $(REF isInputRange, std,range,primitives). - */ - @property bool empty() @safe @nogc pure nothrow const - { - return _empty; - } - - /** - * Part of an input range as defined by - * $(REF isInputRange, std,range,primitives). - * - * Throws: - * $(LREF CSVException) When a quote is found in an unquoted field, - * data continues after a closing quote, the quoted field was not - * closed before data was empty, a conversion failed, or when the - * row's length does not match the previous length. - */ - void popFront() - { - while (!recordRange.empty) - { - recordRange.popFront(); - } - - static if (ErrorLevel == Malformed.throwException) - if (_input.rowLength == 0) - _input.rowLength = _input.col; - - _input.col = 0; - - if (!_input.range.empty) - { - if (_input.range.front == '\r') - { - _input.range.popFront(); - if (!_input.range.empty && _input.range.front == '\n') - _input.range.popFront(); - } - else if (_input.range.front == '\n') - _input.range.popFront(); - } - - if (_input.range.empty) - { - _empty = true; - return; - } - - prime(); - } - - private void prime() - { - if (_empty) - return; - _input.row++; - static if (is(Contents == struct) || is(Contents == class)) - { - recordRange = typeof(recordRange) - (_input, _separator, _quote, null, - _allowInconsistentDelimiterCount); - } - else - { - recordRange = typeof(recordRange) - (_input, _separator, _quote, indices, - _allowInconsistentDelimiterCount); - } - - static if (is(Contents T : T[U], U : string)) - { - T[U] aa; - try - { - for (; !recordRange.empty; recordRange.popFront()) - { - const i = _input.col - 1; - if (i >= header.length) - throw new CSVException("row contains more values than header", _input.row, _input.col); - aa[header[i]] = recordRange.front; - } - } - catch (ConvException e) - { - throw new CSVException(e.msg, _input.row, _input.col, e); - } - - recordContent = aa; - } - else static if (is(Contents == struct) || is(Contents == class)) - { - static if (is(Contents == class)) - recordContent = new typeof(recordContent)(); - else - recordContent = typeof(recordContent).init; - size_t colIndex; - try - { - for (; !recordRange.empty;) - { - auto colData = recordRange.front; - scope(exit) colIndex++; - if (indices.length > 0) - { - foreach (ti, ToType; Fields!(Contents)) - { - if (indices[ti] == colIndex) - { - static if (!isSomeString!ToType) skipWS(colData); - recordContent.tupleof[ti] = to!ToType(colData); - } - } - } - else - { - foreach (ti, ToType; Fields!(Contents)) - { - if (ti == colIndex) - { - static if (!isSomeString!ToType) skipWS(colData); - recordContent.tupleof[ti] = to!ToType(colData); - } - } - } - recordRange.popFront(); - } - } - catch (ConvException e) - { - throw new CSVException(e.msg, _input.row, colIndex, e); - } - } - } -} - -@safe pure unittest -{ - import std.algorithm.comparison : equal; - - string str = `76;^26^;22`; - int[] ans = [76,26,22]; - auto records = CsvReader!(int,Malformed.ignore,string,char,string[]) - (str, ';', '^', false); - - foreach (record; records) - { - assert(equal(record, ans)); - } -} - -// https://issues.dlang.org/show_bug.cgi?id=15545 -// @system due to the catch for Throwable -@system pure unittest -{ - import std.exception : assertNotThrown; - enum failData = - "name, surname, age - Joe, Joker, 99\r"; - auto r = csvReader(failData); - assertNotThrown((){foreach (entry; r){}}()); -} - -/* - * This input range is accessible through $(LREF CsvReader) when the - * requested `Contents` type is neither a structure or an associative array. - */ -private struct CsvRecord(Contents, Malformed ErrorLevel, Range, Separator) -if (!is(Contents == class) && !is(Contents == struct)) -{ - import std.array : appender; -private: - Input!(Range, ErrorLevel)* _input; - Separator _separator; - Separator _quote; - Contents curContentsoken; - typeof(appender!(dchar[])()) _front; - bool _empty; - bool _allowInconsistentDelimiterCount; - size_t[] _popCount; -public: - /* - * Params: - * input = Pointer to a character $(REF_ALTTEXT input range, isInputRange, std,range,primitives) - * delimiter = Separator for each column - * quote = Character used for quotation - * indices = An array containing which columns will be returned. - * If empty, all columns are returned. List must be in order. - */ - this(Input!(Range, ErrorLevel)* input, Separator delimiter, - Separator quote, size_t[] indices, - bool allowInconsistentDelimiterCount) - { - _input = input; - _separator = delimiter; - _quote = quote; - - _front = appender!(dchar[])(); - _popCount = indices.dup; - _allowInconsistentDelimiterCount = allowInconsistentDelimiterCount; - - // If a header was given, each call to popFront will need - // to eliminate so many tokens. This calculates - // how many will be skipped to get to the next header column - size_t normalizer; - foreach (ref c; _popCount) - { - static if (ErrorLevel == Malformed.ignore) - { - // If we are not throwing exceptions - // a header may not exist, indices are sorted - // and will be size_t.max if not found. - if (c == size_t.max) - break; - } - c -= normalizer; - normalizer += c + 1; - } - - prime(); - } - - /** - * Part of an input range as defined by - * $(REF isInputRange, std,range,primitives). - */ - @property Contents front() @safe pure - { - assert(!empty, "Attempting to fetch the front of an empty CsvRecord"); - return curContentsoken; - } - - /** - * Part of an input range as defined by - * $(REF isInputRange, std,range,primitives). - */ - @property bool empty() @safe pure nothrow @nogc const - { - return _empty; - } - - /* - * CsvRecord is complete when input - * is empty or starts with record break - */ - private bool recordEnd() - { - if (_input.range.empty - || _input.range.front == '\n' - || _input.range.front == '\r') - { - return true; - } - return false; - } - - - /** - * Part of an input range as defined by - * $(REF isInputRange, std,range,primitives). - * - * Throws: - * $(LREF CSVException) When a quote is found in an unquoted field, - * data continues after a closing quote, the quoted field was not - * closed before data was empty, a conversion failed, or when the - * row's length does not match the previous length. - */ - void popFront() - { - static if (ErrorLevel == Malformed.throwException) - import std.format : format; - // Skip last of record when header is depleted. - if (_popCount.ptr && _popCount.empty) - while (!recordEnd()) - { - prime(1); - } - - if (recordEnd()) - { - _empty = true; - static if (ErrorLevel == Malformed.throwException) - { - if (_input.rowLength != 0 && _input.col != _input.rowLength - && !_allowInconsistentDelimiterCount) - { - throw new CSVException( - format("Row %s's length %s does not match "~ - "previous length of %s.", _input.row, - _input.col, _input.rowLength)); - } - } - return; - } - else - { - static if (ErrorLevel == Malformed.throwException) - { - if (_input.rowLength != 0 && _input.col > _input.rowLength) - { - if (!_allowInconsistentDelimiterCount) - { - throw new CSVException( - format("Row %s's length %s does not match "~ - "previous length of %s.", _input.row, - _input.col, _input.rowLength)); - } - else - { - _empty = true; - return; - } - } - } - } - - // Separator is left on the end of input from the last call. - // This cannot be moved to after the call to csvNextToken as - // there may be an empty record after it. - if (_input.range.front == _separator) - _input.range.popFront(); - - _front.shrinkTo(0); - - prime(); - } - - /* - * Handles moving to the next skipNum token. - */ - private void prime(size_t skipNum) - { - foreach (i; 0 .. skipNum) - { - _input.col++; - _front.shrinkTo(0); - if (_input.range.front == _separator) - _input.range.popFront(); - - try - csvNextToken!(Range, ErrorLevel, Separator) - (_input.range, _front, _separator, _quote,false); - catch (IncompleteCellException ice) - { - ice.row = _input.row; - ice.col = _input.col; - ice.partialData = _front.data.idup; - throw ice; - } - catch (ConvException e) - { - throw new CSVException(e.msg, _input.row, _input.col, e); - } - } - } - - private void prime() - { - try - { - _input.col++; - csvNextToken!(Range, ErrorLevel, Separator) - (_input.range, _front, _separator, _quote,false); - } - catch (IncompleteCellException ice) - { - ice.row = _input.row; - ice.col = _input.col; - ice.partialData = _front.data.idup; - throw ice; - } - - auto skipNum = _popCount.empty ? 0 : _popCount.front; - if (!_popCount.empty) - _popCount.popFront(); - - if (skipNum == size_t.max) - { - while (!recordEnd()) - prime(1); - _empty = true; - return; - } - - if (skipNum) - prime(skipNum); - - auto data = _front.data; - static if (!isSomeString!Contents) skipWS(data); - try curContentsoken = to!Contents(data); - catch (ConvException e) - { - throw new CSVException(e.msg, _input.row, _input.col, e); - } - } -} - -/** - * Lower level control over parsing CSV - * - * This function consumes the input. After each call the input will - * start with either a delimiter or record break (\n, \r\n, \r) which - * must be removed for subsequent calls. - * - * Params: - * input = Any CSV input - * ans = The first field in the input - * sep = The character to represent a comma in the specification - * quote = The character to represent a quote in the specification - * startQuoted = Whether the input should be considered to already be in - * quotes - * - * Throws: - * $(LREF IncompleteCellException) When a quote is found in an unquoted - * field, data continues after a closing quote, or the quoted field was - * not closed before data was empty. - */ -void csvNextToken(Range, Malformed ErrorLevel = Malformed.throwException, - Separator, Output) - (ref Range input, ref Output ans, - Separator sep, Separator quote, - bool startQuoted = false) -if (isSomeChar!Separator && isInputRange!Range - && is(immutable ElementType!Range == immutable dchar) - && isOutputRange!(Output, dchar)) -{ - bool quoted = startQuoted; - bool escQuote; - if (input.empty) - return; - - if (input.front == '\n') - return; - if (input.front == '\r') - return; - - if (input.front == quote) - { - quoted = true; - input.popFront(); - } - - while (!input.empty) - { - assert(!(quoted && escQuote), - "Invalid quotation state in csvNextToken"); - if (!quoted) - { - // When not quoted the token ends at sep - if (input.front == sep) - break; - if (input.front == '\r') - break; - if (input.front == '\n') - break; - } - if (!quoted && !escQuote) - { - if (input.front == quote) - { - // Not quoted, but quote found - static if (ErrorLevel == Malformed.throwException) - throw new IncompleteCellException( - "Quote located in unquoted token"); - else static if (ErrorLevel == Malformed.ignore) - ans.put(quote); - } - else - { - // Not quoted, non-quote character - ans.put(input.front); - } - } - else - { - if (input.front == quote) - { - // Quoted, quote found - // By turning off quoted and turning on escQuote - // I can tell when to add a quote to the string - // escQuote is turned to false when it escapes a - // quote or is followed by a non-quote (see outside else). - // They are mutually exclusive, but provide different - // information. - if (escQuote) - { - escQuote = false; - quoted = true; - ans.put(quote); - } else - { - escQuote = true; - quoted = false; - } - } - else - { - // Quoted, non-quote character - if (escQuote) - { - static if (ErrorLevel == Malformed.throwException) - throw new IncompleteCellException( - "Content continues after end quote, " ~ - "or needs to be escaped."); - else static if (ErrorLevel == Malformed.ignore) - break; - } - ans.put(input.front); - } - } - input.popFront(); - } - - static if (ErrorLevel == Malformed.throwException) - if (quoted && (input.empty || input.front == '\n' || input.front == '\r')) - throw new IncompleteCellException( - "Data continues on future lines or trailing quote"); - -} - -/// -@safe unittest -{ - import std.array : appender; - import std.range.primitives : popFront; - - string str = "65,63\n123,3673"; - - auto a = appender!(char[])(); - - csvNextToken(str,a,',','"'); - assert(a.data == "65"); - assert(str == ",63\n123,3673"); - - str.popFront(); - a.shrinkTo(0); - csvNextToken(str,a,',','"'); - assert(a.data == "63"); - assert(str == "\n123,3673"); - - str.popFront(); - a.shrinkTo(0); - csvNextToken(str,a,',','"'); - assert(a.data == "123"); - assert(str == ",3673"); -} - -// Test csvNextToken on simplest form and correct format. -@safe pure unittest -{ - import std.array; - - string str = "\U00010143Hello,65,63.63\nWorld,123,3673.562"; - - auto a = appender!(dchar[])(); - csvNextToken!string(str,a,',','"'); - assert(a.data == "\U00010143Hello"); - assert(str == ",65,63.63\nWorld,123,3673.562"); - - str.popFront(); - a.shrinkTo(0); - csvNextToken(str,a,',','"'); - assert(a.data == "65"); - assert(str == ",63.63\nWorld,123,3673.562"); - - str.popFront(); - a.shrinkTo(0); - csvNextToken(str,a,',','"'); - assert(a.data == "63.63"); - assert(str == "\nWorld,123,3673.562"); - - str.popFront(); - a.shrinkTo(0); - csvNextToken(str,a,',','"'); - assert(a.data == "World"); - assert(str == ",123,3673.562"); - - str.popFront(); - a.shrinkTo(0); - csvNextToken(str,a,',','"'); - assert(a.data == "123"); - assert(str == ",3673.562"); - - str.popFront(); - a.shrinkTo(0); - csvNextToken(str,a,',','"'); - assert(a.data == "3673.562"); - assert(str == ""); -} - -// Test quoted tokens -@safe pure unittest -{ - import std.array; - - string str = `one,two,"three ""quoted""","",` ~ "\"five\nnew line\"\nsix"; - - auto a = appender!(dchar[])(); - csvNextToken!string(str,a,',','"'); - assert(a.data == "one"); - assert(str == `,two,"three ""quoted""","",` ~ "\"five\nnew line\"\nsix"); - - str.popFront(); - a.shrinkTo(0); - csvNextToken(str,a,',','"'); - assert(a.data == "two"); - assert(str == `,"three ""quoted""","",` ~ "\"five\nnew line\"\nsix"); - - str.popFront(); - a.shrinkTo(0); - csvNextToken(str,a,',','"'); - assert(a.data == "three \"quoted\""); - assert(str == `,"",` ~ "\"five\nnew line\"\nsix"); - - str.popFront(); - a.shrinkTo(0); - csvNextToken(str,a,',','"'); - assert(a.data == ""); - assert(str == ",\"five\nnew line\"\nsix"); - - str.popFront(); - a.shrinkTo(0); - csvNextToken(str,a,',','"'); - assert(a.data == "five\nnew line"); - assert(str == "\nsix"); - - str.popFront(); - a.shrinkTo(0); - csvNextToken(str,a,',','"'); - assert(a.data == "six"); - assert(str == ""); -} - -// Test empty data is pulled at end of record. -@safe pure unittest -{ - import std.array; - - string str = "one,"; - auto a = appender!(dchar[])(); - csvNextToken(str,a,',','"'); - assert(a.data == "one"); - assert(str == ","); - - a.shrinkTo(0); - csvNextToken(str,a,',','"'); - assert(a.data == ""); -} - -// Test exceptions -@safe pure unittest -{ - import std.array; - - string str = "\"one\nnew line"; - - typeof(appender!(dchar[])()) a; - try - { - a = appender!(dchar[])(); - csvNextToken(str,a,',','"'); - assert(0); - } - catch (IncompleteCellException ice) - { - assert(a.data == "one\nnew line"); - assert(str == ""); - } - - str = "Hello world\""; - - try - { - a = appender!(dchar[])(); - csvNextToken(str,a,',','"'); - assert(0); - } - catch (IncompleteCellException ice) - { - assert(a.data == "Hello world"); - assert(str == "\""); - } - - str = "one, two \"quoted\" end"; - - a = appender!(dchar[])(); - csvNextToken!(string,Malformed.ignore)(str,a,',','"'); - assert(a.data == "one"); - str.popFront(); - a.shrinkTo(0); - csvNextToken!(string,Malformed.ignore)(str,a,',','"'); - assert(a.data == " two \"quoted\" end"); -} - -// Test modifying token delimiter -@safe pure unittest -{ - import std.array; - - string str = `one|two|/three "quoted"/|//`; - - auto a = appender!(dchar[])(); - csvNextToken(str,a, '|','/'); - assert(a.data == "one"d); - assert(str == `|two|/three "quoted"/|//`); - - str.popFront(); - a.shrinkTo(0); - csvNextToken(str,a, '|','/'); - assert(a.data == "two"d); - assert(str == `|/three "quoted"/|//`); - - str.popFront(); - a.shrinkTo(0); - csvNextToken(str,a, '|','/'); - assert(a.data == `three "quoted"`); - assert(str == `|//`); - - str.popFront(); - a.shrinkTo(0); - csvNextToken(str,a, '|','/'); - assert(a.data == ""d); -} - -// https://issues.dlang.org/show_bug.cgi?id=8908 -@safe pure unittest -{ - string csv = ` 1.0, 2.0, 3.0 - 4.0, 5.0, 6.0`; - - static struct Data { real a, b, c; } - size_t i = 0; - foreach (data; csvReader!Data(csv)) with (data) - { - int[] row = [cast(int) a, cast(int) b, cast(int) c]; - if (i == 0) - assert(row == [1, 2, 3]); - else - assert(row == [4, 5, 6]); - ++i; - } - - i = 0; - foreach (data; csvReader!real(csv)) - { - auto a = data.front; data.popFront(); - auto b = data.front; data.popFront(); - auto c = data.front; - int[] row = [cast(int) a, cast(int) b, cast(int) c]; - if (i == 0) - assert(row == [1, 2, 3]); - else - assert(row == [4, 5, 6]); - ++i; - } -} - -// https://issues.dlang.org/show_bug.cgi?id=21629 -@safe pure unittest -{ - import std.typecons : Tuple; - struct Reccord - { - string a; - string b; - } - - auto header = ["a" ,"b"]; - string input = ""; - assert(csvReader!Reccord(input).empty, "This should be empty"); - assert(csvReader!Reccord(input, header).empty, "This should be empty"); - assert(csvReader!(Tuple!(string,string))(input).empty, "This should be empty"); - assert(csvReader!(string[string])(input, header).empty, "This should be empty"); - assert(csvReader!(string[string])(input, null).empty, "This should be empty"); - assert(csvReader!(int)(input, null).empty, "This should be empty"); -} diff --git a/phobos/std/datetime/date.d b/phobos/std/datetime/date.d deleted file mode 100644 index ebdaba4..0000000 --- a/phobos/std/datetime/date.d +++ /dev/null @@ -1,10891 +0,0 @@ -// Written in the D programming language -/++ - -$(SCRIPT inhibitQuickIndex = 1;) -$(DIVC quickindex, -$(BOOKTABLE, -$(TR $(TH Category) $(TH Functions)) -$(TR $(TD Main date types) $(TD - $(LREF Date) - $(LREF DateTime) -)) -$(TR $(TD Other date types) $(TD - $(LREF Month) - $(LREF DayOfWeek) - $(LREF TimeOfDay) -)) -$(TR $(TD Date checking) $(TD - $(LREF valid) - $(LREF validTimeUnits) - $(LREF yearIsLeapYear) - $(LREF isTimePoint) - $(LREF enforceValid) -)) -$(TR $(TD Date conversion) $(TD - $(LREF daysToDayOfWeek) - $(LREF monthsToMonth) -)) -$(TR $(TD Time units) $(TD - $(LREF cmpTimeUnits) - $(LREF timeStrings) -)) -$(TR $(TD Other) $(TD - $(LREF AllowDayOverflow) - $(LREF DateTimeException) -)) -)) - - License: $(HTTP www.boost.org/LICENSE_1_0.txt, Boost License 1.0). - Authors: $(HTTP jmdavisprog.com, Jonathan M Davis) - Source: $(PHOBOSSRC std/datetime/date.d) -+/ -module std.datetime.date; - -import core.time : TimeException; -import std.traits : isSomeString, Unqual; -import std.typecons : Flag; -import std.range.primitives : isOutputRange; - -version (StdUnittest) import std.exception : assertThrown; - -@safe unittest -{ - initializeTests(); -} - - -/++ - Exception type used by std.datetime. It's an alias to - $(REF TimeException,core,time). Either can be caught without concern about - which module it came from. - +/ -alias DateTimeException = TimeException; - - -/++ - Represents the 12 months of the Gregorian year (January is 1). - +/ -enum Month : ubyte -{ - jan = 1, /// - feb, /// - mar, /// - apr, /// - may, /// - jun, /// - jul, /// - aug, /// - sep, /// - oct, /// - nov, /// - dec /// -} - -/// -@safe pure unittest -{ - assert(Date(2018, 10, 1).month == Month.oct); - assert(DateTime(1, 1, 1).month == Month.jan); -} - - -/++ - Represents the 7 days of the Gregorian week (Sunday is 0). - +/ -enum DayOfWeek : ubyte -{ - sun = 0, /// - mon, /// - tue, /// - wed, /// - thu, /// - fri, /// - sat /// -} - -/// -@safe pure unittest -{ - assert(Date(2018, 10, 1).dayOfWeek == DayOfWeek.mon); - assert(DateTime(5, 5, 5).dayOfWeek == DayOfWeek.thu); -} - -/++ - In some date calculations, adding months or years can cause the date to fall - on a day of the month which is not valid (e.g. February 29th 2001 or - June 31st 2000). If overflow is allowed (as is the default), then the month - will be incremented accordingly (so, February 29th 2001 would become - March 1st 2001, and June 31st 2000 would become July 1st 2000). If overflow - is not allowed, then the day will be adjusted to the last valid day in that - month (so, February 29th 2001 would become February 28th 2001 and - June 31st 2000 would become June 30th 2000). - - AllowDayOverflow only applies to calculations involving months or years. - - If set to `AllowDayOverflow.no`, then day overflow is not allowed. - - Otherwise, if set to `AllowDayOverflow.yes`, then day overflow is - allowed. - +/ -alias AllowDayOverflow = Flag!"allowDayOverflow"; - - -/++ - Array of the strings representing time units, starting with the smallest - unit and going to the largest. It does not include `"nsecs"`. - - Includes `"hnsecs"` (hecto-nanoseconds (100 ns)), - `"usecs"` (microseconds), `"msecs"` (milliseconds), `"seconds"`, - `"minutes"`, `"hours"`, `"days"`, `"weeks"`, `"months"`, and - `"years"` - +/ -immutable string[] timeStrings = ["hnsecs", "usecs", "msecs", "seconds", "minutes", - "hours", "days", "weeks", "months", "years"]; - - -/++ - Combines the $(REF Date,std,datetime,date) and - $(REF TimeOfDay,std,datetime,date) structs to give an object which holds - both the date and the time. It is optimized for calendar-based operations - and has no concept of time zone. For an object which is optimized for time - operations based on the system time, use - $(REF SysTime,std,datetime,systime). $(REF SysTime,std,datetime,systime) has - a concept of time zone and has much higher precision (hnsecs). `DateTime` - is intended primarily for calendar-based uses rather than precise time - operations. - +/ -struct DateTime -{ -public: - - /++ - Params: - date = The date portion of $(LREF DateTime). - tod = The time portion of $(LREF DateTime). - +/ - this(Date date, TimeOfDay tod = TimeOfDay.init) @safe pure nothrow @nogc - { - _date = date; - _tod = tod; - } - - @safe unittest - { - { - auto dt = DateTime.init; - assert(dt._date == Date.init); - assert(dt._tod == TimeOfDay.init); - } - - { - auto dt = DateTime(Date(1999, 7 ,6)); - assert(dt._date == Date(1999, 7, 6)); - assert(dt._tod == TimeOfDay.init); - } - - { - auto dt = DateTime(Date(1999, 7 ,6), TimeOfDay(12, 30, 33)); - assert(dt._date == Date(1999, 7, 6)); - assert(dt._tod == TimeOfDay(12, 30, 33)); - } - } - - - /++ - Params: - year = The year portion of the date. - month = The month portion of the date (January is 1). - day = The day portion of the date. - hour = The hour portion of the time; - minute = The minute portion of the time; - second = The second portion of the time; - +/ - this(int year, int month, int day, int hour = 0, int minute = 0, int second = 0) @safe pure - { - _date = Date(year, month, day); - _tod = TimeOfDay(hour, minute, second); - } - - @safe unittest - { - { - auto dt = DateTime(1999, 7 ,6); - assert(dt._date == Date(1999, 7, 6)); - assert(dt._tod == TimeOfDay.init); - } - - { - auto dt = DateTime(1999, 7 ,6, 12, 30, 33); - assert(dt._date == Date(1999, 7, 6)); - assert(dt._tod == TimeOfDay(12, 30, 33)); - } - } - - - /++ - Compares this $(LREF DateTime) with the given `DateTime.`. - - Returns: - $(BOOKTABLE, - $(TR $(TD this < rhs) $(TD < 0)) - $(TR $(TD this == rhs) $(TD 0)) - $(TR $(TD this > rhs) $(TD > 0)) - ) - +/ - int opCmp(DateTime rhs) const @safe pure nothrow @nogc - { - immutable dateResult = _date.opCmp(rhs._date); - - if (dateResult != 0) - return dateResult; - - return _tod.opCmp(rhs._tod); - } - - @safe unittest - { - // Test A.D. - assert(DateTime(Date.init, TimeOfDay.init).opCmp(DateTime.init) == 0); - - assert(DateTime(Date(1999, 1, 1)).opCmp(DateTime(Date(1999, 1, 1))) == 0); - assert(DateTime(Date(1, 7, 1)).opCmp(DateTime(Date(1, 7, 1))) == 0); - assert(DateTime(Date(1, 1, 6)).opCmp(DateTime(Date(1, 1, 6))) == 0); - - assert(DateTime(Date(1999, 7, 1)).opCmp(DateTime(Date(1999, 7, 1))) == 0); - assert(DateTime(Date(1999, 7, 6)).opCmp(DateTime(Date(1999, 7, 6))) == 0); - - assert(DateTime(Date(1, 7, 6)).opCmp(DateTime(Date(1, 7, 6))) == 0); - - assert(DateTime(Date(1999, 7, 6)).opCmp(DateTime(Date(2000, 7, 6))) < 0); - assert(DateTime(Date(2000, 7, 6)).opCmp(DateTime(Date(1999, 7, 6))) > 0); - assert(DateTime(Date(1999, 7, 6)).opCmp(DateTime(Date(1999, 8, 6))) < 0); - assert(DateTime(Date(1999, 8, 6)).opCmp(DateTime(Date(1999, 7, 6))) > 0); - assert(DateTime(Date(1999, 7, 6)).opCmp(DateTime(Date(1999, 7, 7))) < 0); - assert(DateTime(Date(1999, 7, 7)).opCmp(DateTime(Date(1999, 7, 6))) > 0); - - assert(DateTime(Date(1999, 8, 7)).opCmp(DateTime(Date(2000, 7, 6))) < 0); - assert(DateTime(Date(2000, 8, 6)).opCmp(DateTime(Date(1999, 7, 7))) > 0); - assert(DateTime(Date(1999, 7, 7)).opCmp(DateTime(Date(2000, 7, 6))) < 0); - assert(DateTime(Date(2000, 7, 6)).opCmp(DateTime(Date(1999, 7, 7))) > 0); - assert(DateTime(Date(1999, 7, 7)).opCmp(DateTime(Date(1999, 8, 6))) < 0); - assert(DateTime(Date(1999, 8, 6)).opCmp(DateTime(Date(1999, 7, 7))) > 0); - - - assert(DateTime(Date(1999, 7, 6), TimeOfDay(0, 0, 0)).opCmp( - DateTime(Date(1999, 7, 6), TimeOfDay(0, 0, 0))) == 0); - assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 0, 0)).opCmp( - DateTime(Date(1999, 7, 6), TimeOfDay(12, 0, 0))) == 0); - assert(DateTime(Date(1999, 7, 6), TimeOfDay(0, 30, 0)).opCmp( - DateTime(Date(1999, 7, 6), TimeOfDay(0, 30, 0))) == 0); - assert(DateTime(Date(1999, 7, 6), TimeOfDay(0, 0, 33)).opCmp( - DateTime(Date(1999, 7, 6), TimeOfDay(0, 0, 33))) == 0); - - assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 0)).opCmp( - DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 0))) == 0); - assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)).opCmp( - DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33))) == 0); - - assert(DateTime(Date(1999, 7, 6), TimeOfDay(0, 30, 33)).opCmp( - DateTime(Date(1999, 7, 6), TimeOfDay(0, 30, 33))) == 0); - assert(DateTime(Date(1999, 7, 6), TimeOfDay(0, 0, 33)).opCmp( - DateTime(Date(1999, 7, 6), TimeOfDay(0, 0, 33))) == 0); - - assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)).opCmp( - DateTime(Date(1999, 7, 6), TimeOfDay(13, 30, 33))) < 0); - assert(DateTime(Date(1999, 7, 6), TimeOfDay(13, 30, 33)).opCmp( - DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33))) > 0); - assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)).opCmp( - DateTime(Date(1999, 7, 6), TimeOfDay(12, 31, 33))) < 0); - assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 31, 33)).opCmp( - DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33))) > 0); - assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)).opCmp( - DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 34))) < 0); - assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 34)).opCmp( - DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33))) > 0); - - assert(DateTime(Date(1999, 7, 6), TimeOfDay(13, 30, 33)).opCmp( - DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 34))) > 0); - assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 34)).opCmp( - DateTime(Date(1999, 7, 6), TimeOfDay(13, 30, 33))) < 0); - assert(DateTime(Date(1999, 7, 6), TimeOfDay(13, 30, 33)).opCmp( - DateTime(Date(1999, 7, 6), TimeOfDay(12, 31, 33))) > 0); - assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 31, 33)).opCmp( - DateTime(Date(1999, 7, 6), TimeOfDay(13, 30, 33))) < 0); - - assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 31, 33)).opCmp( - DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 34))) > 0); - assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 34)).opCmp( - DateTime(Date(1999, 7, 6), TimeOfDay(12, 31, 33))) < 0); - - assert(DateTime(Date(1999, 7, 6), TimeOfDay(13, 30, 33)).opCmp( - DateTime(Date(2000, 7, 6), TimeOfDay(12, 30, 33))) < 0); - assert(DateTime(Date(2000, 7, 6), TimeOfDay(12, 30, 33)).opCmp( - DateTime(Date(1999, 7, 6), TimeOfDay(13, 30, 33))) > 0); - assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 31, 33)).opCmp( - DateTime(Date(2000, 7, 6), TimeOfDay(12, 30, 33))) < 0); - assert(DateTime(Date(2000, 7, 6), TimeOfDay(12, 30, 33)).opCmp( - DateTime(Date(1999, 7, 6), TimeOfDay(12, 31, 33))) > 0); - assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 34)).opCmp( - DateTime(Date(2000, 7, 6), TimeOfDay(12, 30, 33))) < 0); - assert(DateTime(Date(2000, 7, 6), TimeOfDay(12, 30, 33)).opCmp( - DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 34))) > 0); - - assert(DateTime(Date(1999, 7, 6), TimeOfDay(13, 30, 33)).opCmp( - DateTime(Date(1999, 8, 6), TimeOfDay(12, 30, 33))) < 0); - assert(DateTime(Date(1999, 8, 6), TimeOfDay(12, 30, 33)).opCmp( - DateTime(Date(1999, 7, 6), TimeOfDay(13, 30, 33))) > 0); - assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 31, 33)).opCmp( - DateTime(Date(1999, 8, 6), TimeOfDay(12, 30, 33))) < 0); - assert(DateTime(Date(1999, 8, 6), TimeOfDay(12, 30, 33)).opCmp( - DateTime(Date(1999, 7, 6), TimeOfDay(12, 31, 33))) > 0); - assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 34)).opCmp( - DateTime(Date(1999, 8, 6), TimeOfDay(12, 30, 33))) < 0); - assert(DateTime(Date(1999, 8, 6), TimeOfDay(12, 30, 33)).opCmp( - DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 34))) > 0); - - assert(DateTime(Date(1999, 7, 6), TimeOfDay(13, 30, 33)).opCmp( - DateTime(Date(1999, 7, 7), TimeOfDay(12, 30, 33))) < 0); - assert(DateTime(Date(1999, 7, 7), TimeOfDay(12, 30, 33)).opCmp( - DateTime(Date(1999, 7, 6), TimeOfDay(13, 30, 33))) > 0); - assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 31, 33)).opCmp( - DateTime(Date(1999, 7, 7), TimeOfDay(12, 31, 33))) < 0); - assert(DateTime(Date(1999, 7, 7), TimeOfDay(12, 30, 33)).opCmp( - DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33))) > 0); - assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 34)).opCmp( - DateTime(Date(1999, 7, 7), TimeOfDay(12, 30, 33))) < 0); - assert(DateTime(Date(1999, 7, 7), TimeOfDay(12, 30, 33)).opCmp( - DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 34))) > 0); - - // Test B.C. - assert(DateTime(Date(-1, 1, 1), TimeOfDay(12, 30, 33)).opCmp( - DateTime(Date(-1, 1, 1), TimeOfDay(12, 30, 33))) == 0); - assert(DateTime(Date(-1, 7, 1), TimeOfDay(12, 30, 33)).opCmp( - DateTime(Date(-1, 7, 1), TimeOfDay(12, 30, 33))) == 0); - assert(DateTime(Date(-1, 1, 6), TimeOfDay(12, 30, 33)).opCmp( - DateTime(Date(-1, 1, 6), TimeOfDay(12, 30, 33))) == 0); - - assert(DateTime(Date(-1999, 7, 1), TimeOfDay(12, 30, 33)).opCmp( - DateTime(Date(-1999, 7, 1), TimeOfDay(12, 30, 33))) == 0); - assert(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)).opCmp( - DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33))) == 0); - - assert(DateTime(Date(-1, 7, 6), TimeOfDay(12, 30, 33)).opCmp( - DateTime(Date(-1, 7, 6), TimeOfDay(12, 30, 33))) == 0); - - assert(DateTime(Date(-2000, 7, 6), TimeOfDay(12, 30, 33)).opCmp( - DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33))) < 0); - assert(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)).opCmp( - DateTime(Date(-2000, 7, 6), TimeOfDay(12, 30, 33))) > 0); - assert(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)).opCmp( - DateTime(Date(-1999, 8, 6), TimeOfDay(12, 30, 33))) < 0); - assert(DateTime(Date(-1999, 8, 6), TimeOfDay(12, 30, 33)).opCmp( - DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33))) > 0); - assert(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)).opCmp( - DateTime(Date(-1999, 7, 7), TimeOfDay(12, 30, 33))) < 0); - assert(DateTime(Date(-1999, 7, 7), TimeOfDay(12, 30, 33)).opCmp( - DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33))) > 0); - - assert(DateTime(Date(-2000, 8, 6), TimeOfDay(12, 30, 33)).opCmp( - DateTime(Date(-1999, 7, 7), TimeOfDay(12, 30, 33))) < 0); - assert(DateTime(Date(-1999, 8, 7), TimeOfDay(12, 30, 33)).opCmp( - DateTime(Date(-2000, 7, 6), TimeOfDay(12, 30, 33))) > 0); - assert(DateTime(Date(-2000, 7, 6), TimeOfDay(12, 30, 33)).opCmp( - DateTime(Date(-1999, 7, 7), TimeOfDay(12, 30, 33))) < 0); - assert(DateTime(Date(-1999, 7, 7), TimeOfDay(12, 30, 33)).opCmp( - DateTime(Date(-2000, 7, 6), TimeOfDay(12, 30, 33))) > 0); - assert(DateTime(Date(-1999, 7, 7), TimeOfDay(12, 30, 33)).opCmp( - DateTime(Date(-1999, 8, 6), TimeOfDay(12, 30, 33))) < 0); - assert(DateTime(Date(-1999, 8, 6), TimeOfDay(12, 30, 33)).opCmp( - DateTime(Date(-1999, 7, 7), TimeOfDay(12, 30, 33))) > 0); - - // Test Both - assert(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)).opCmp( - DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33))) < 0); - assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)).opCmp( - DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33))) > 0); - - assert(DateTime(Date(-1999, 8, 6), TimeOfDay(12, 30, 33)).opCmp( - DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33))) < 0); - assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)).opCmp( - DateTime(Date(-1999, 8, 6), TimeOfDay(12, 30, 33))) > 0); - - assert(DateTime(Date(-1999, 7, 7), TimeOfDay(12, 30, 33)).opCmp( - DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33))) < 0); - assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)).opCmp( - DateTime(Date(-1999, 7, 7), TimeOfDay(12, 30, 33))) > 0); - - assert(DateTime(Date(-1999, 8, 7), TimeOfDay(12, 30, 33)).opCmp( - DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33))) < 0); - assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)).opCmp( - DateTime(Date(-1999, 8, 7), TimeOfDay(12, 30, 33))) > 0); - - assert(DateTime(Date(-1999, 8, 6), TimeOfDay(12, 30, 33)).opCmp( - DateTime(Date(1999, 6, 6), TimeOfDay(12, 30, 33))) < 0); - assert(DateTime(Date(1999, 6, 8), TimeOfDay(12, 30, 33)).opCmp( - DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33))) > 0); - - auto dt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 33, 30)); - const cdt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 33, 30)); - immutable idt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 33, 30)); - assert(dt.opCmp(dt) == 0); - assert(dt.opCmp(cdt) == 0); - assert(dt.opCmp(idt) == 0); - assert(cdt.opCmp(dt) == 0); - assert(cdt.opCmp(cdt) == 0); - assert(cdt.opCmp(idt) == 0); - assert(idt.opCmp(dt) == 0); - assert(idt.opCmp(cdt) == 0); - assert(idt.opCmp(idt) == 0); - } - - - /++ - The date portion of $(LREF DateTime). - +/ - @property Date date() const @safe pure nothrow @nogc - { - return _date; - } - - @safe unittest - { - { - auto dt = DateTime.init; - assert(dt.date == Date.init); - } - - { - auto dt = DateTime(Date(1999, 7, 6)); - assert(dt.date == Date(1999, 7, 6)); - } - - const cdt = DateTime(1999, 7, 6); - immutable idt = DateTime(1999, 7, 6); - assert(cdt.date == Date(1999, 7, 6)); - assert(idt.date == Date(1999, 7, 6)); - } - - - /++ - The date portion of $(LREF DateTime). - - Params: - date = The Date to set this $(LREF DateTime)'s date portion to. - +/ - @property void date(Date date) @safe pure nothrow @nogc - { - _date = date; - } - - @safe unittest - { - auto dt = DateTime.init; - dt.date = Date(1999, 7, 6); - assert(dt._date == Date(1999, 7, 6)); - assert(dt._tod == TimeOfDay.init); - - const cdt = DateTime(1999, 7, 6); - immutable idt = DateTime(1999, 7, 6); - static assert(!__traits(compiles, cdt.date = Date(2010, 1, 1))); - static assert(!__traits(compiles, idt.date = Date(2010, 1, 1))); - } - - - /++ - The time portion of $(LREF DateTime). - +/ - @property TimeOfDay timeOfDay() const @safe pure nothrow @nogc - { - return _tod; - } - - @safe unittest - { - { - auto dt = DateTime.init; - assert(dt.timeOfDay == TimeOfDay.init); - } - - { - auto dt = DateTime(Date.init, TimeOfDay(12, 30, 33)); - assert(dt.timeOfDay == TimeOfDay(12, 30, 33)); - } - - const cdt = DateTime(1999, 7, 6, 12, 30, 33); - immutable idt = DateTime(1999, 7, 6, 12, 30, 33); - assert(cdt.timeOfDay == TimeOfDay(12, 30, 33)); - assert(idt.timeOfDay == TimeOfDay(12, 30, 33)); - } - - - /++ - The time portion of $(LREF DateTime). - - Params: - tod = The $(REF TimeOfDay,std,datetime,date) to set this - $(LREF DateTime)'s time portion to. - +/ - @property void timeOfDay(TimeOfDay tod) @safe pure nothrow @nogc - { - _tod = tod; - } - - @safe unittest - { - auto dt = DateTime.init; - dt.timeOfDay = TimeOfDay(12, 30, 33); - assert(dt._date == Date.init); - assert(dt._tod == TimeOfDay(12, 30, 33)); - - const cdt = DateTime(1999, 7, 6, 12, 30, 33); - immutable idt = DateTime(1999, 7, 6, 12, 30, 33); - static assert(!__traits(compiles, cdt.timeOfDay = TimeOfDay(12, 30, 33))); - static assert(!__traits(compiles, idt.timeOfDay = TimeOfDay(12, 30, 33))); - } - - - /++ - Year of the Gregorian Calendar. Positive numbers are A.D. Non-positive - are B.C. - +/ - @property short year() const @safe pure nothrow @nogc - { - return _date.year; - } - - @safe unittest - { - assert(Date.init.year == 1); - assert(Date(1999, 7, 6).year == 1999); - assert(Date(-1999, 7, 6).year == -1999); - - const cdt = DateTime(1999, 7, 6, 12, 30, 33); - immutable idt = DateTime(1999, 7, 6, 12, 30, 33); - assert(idt.year == 1999); - assert(idt.year == 1999); - } - - - /++ - Year of the Gregorian Calendar. Positive numbers are A.D. Non-positive - are B.C. - - Params: - year = The year to set this $(LREF DateTime)'s year to. - - Throws: - $(REF DateTimeException,std,datetime,date) if the new year is not - a leap year and if the resulting date would be on February 29th. - +/ - @property void year(int year) @safe pure - { - _date.year = year; - } - - /// - @safe unittest - { - assert(DateTime(Date(1999, 7, 6), TimeOfDay(9, 7, 5)).year == 1999); - assert(DateTime(Date(2010, 10, 4), TimeOfDay(0, 0, 30)).year == 2010); - assert(DateTime(Date(-7, 4, 5), TimeOfDay(7, 45, 2)).year == -7); - } - - @safe unittest - { - static void testDT(DateTime dt, int year, DateTime expected, size_t line = __LINE__) - { - dt.year = year; - assert(dt == expected); - } - - testDT(DateTime(Date(1, 1, 1), TimeOfDay(12, 30, 33)), - 1999, - DateTime(Date(1999, 1, 1), TimeOfDay(12, 30, 33))); - testDT(DateTime(Date(1, 1, 1), TimeOfDay(12, 30, 33)), - 0, - DateTime(Date(0, 1, 1), TimeOfDay(12, 30, 33))); - testDT(DateTime(Date(1, 1, 1), TimeOfDay(12, 30, 33)), - -1999, - DateTime(Date(-1999, 1, 1), TimeOfDay(12, 30, 33))); - - const cdt = DateTime(1999, 7, 6, 12, 30, 33); - immutable idt = DateTime(1999, 7, 6, 12, 30, 33); - static assert(!__traits(compiles, cdt.year = 7)); - static assert(!__traits(compiles, idt.year = 7)); - } - - - /++ - Year B.C. of the Gregorian Calendar counting year 0 as 1 B.C. - - Throws: - $(REF DateTimeException,std,datetime,date) if `isAD` is true. - +/ - @property short yearBC() const @safe pure - { - return _date.yearBC; - } - - /// - @safe unittest - { - assert(DateTime(Date(0, 1, 1), TimeOfDay(12, 30, 33)).yearBC == 1); - assert(DateTime(Date(-1, 1, 1), TimeOfDay(10, 7, 2)).yearBC == 2); - assert(DateTime(Date(-100, 1, 1), TimeOfDay(4, 59, 0)).yearBC == 101); - } - - @safe unittest - { - assertThrown!DateTimeException((DateTime dt){dt.yearBC;}(DateTime(Date(1, 1, 1)))); - - auto dt = DateTime(1999, 7, 6, 12, 30, 33); - const cdt = DateTime(1999, 7, 6, 12, 30, 33); - immutable idt = DateTime(1999, 7, 6, 12, 30, 33); - dt.yearBC = 12; - assert(dt.yearBC == 12); - static assert(!__traits(compiles, cdt.yearBC = 12)); - static assert(!__traits(compiles, idt.yearBC = 12)); - } - - - /++ - Year B.C. of the Gregorian Calendar counting year 0 as 1 B.C. - - Params: - year = The year B.C. to set this $(LREF DateTime)'s year to. - - Throws: - $(REF DateTimeException,std,datetime,date) if a non-positive value - is given. - +/ - @property void yearBC(int year) @safe pure - { - _date.yearBC = year; - } - - /// - @safe unittest - { - auto dt = DateTime(Date(2010, 1, 1), TimeOfDay(7, 30, 0)); - dt.yearBC = 1; - assert(dt == DateTime(Date(0, 1, 1), TimeOfDay(7, 30, 0))); - - dt.yearBC = 10; - assert(dt == DateTime(Date(-9, 1, 1), TimeOfDay(7, 30, 0))); - } - - @safe unittest - { - assertThrown!DateTimeException((DateTime dt){dt.yearBC = -1;}(DateTime(Date(1, 1, 1)))); - - auto dt = DateTime(1999, 7, 6, 12, 30, 33); - const cdt = DateTime(1999, 7, 6, 12, 30, 33); - immutable idt = DateTime(1999, 7, 6, 12, 30, 33); - dt.yearBC = 12; - assert(dt.yearBC == 12); - static assert(!__traits(compiles, cdt.yearBC = 12)); - static assert(!__traits(compiles, idt.yearBC = 12)); - } - - - /++ - Month of a Gregorian Year. - +/ - @property Month month() const @safe pure nothrow @nogc - { - return _date.month; - } - - /// - @safe unittest - { - assert(DateTime(Date(1999, 7, 6), TimeOfDay(9, 7, 5)).month == 7); - assert(DateTime(Date(2010, 10, 4), TimeOfDay(0, 0, 30)).month == 10); - assert(DateTime(Date(-7, 4, 5), TimeOfDay(7, 45, 2)).month == 4); - } - - @safe unittest - { - assert(DateTime.init.month == 1); - assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)).month == 7); - assert(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)).month == 7); - - const cdt = DateTime(1999, 7, 6, 12, 30, 33); - immutable idt = DateTime(1999, 7, 6, 12, 30, 33); - assert(cdt.month == 7); - assert(idt.month == 7); - } - - - /++ - Month of a Gregorian Year. - - Params: - month = The month to set this $(LREF DateTime)'s month to. - - Throws: - $(REF DateTimeException,std,datetime,date) if the given month is - not a valid month. - +/ - @property void month(Month month) @safe pure - { - _date.month = month; - } - - @safe unittest - { - static void testDT(DateTime dt, Month month, DateTime expected = DateTime.init, size_t line = __LINE__) - { - dt.month = month; - assert(expected != DateTime.init); - assert(dt == expected); - } - - assertThrown!DateTimeException(testDT(DateTime(Date(1, 1, 1), TimeOfDay(12, 30, 33)), cast(Month) 0)); - assertThrown!DateTimeException(testDT(DateTime(Date(1, 1, 1), TimeOfDay(12, 30, 33)), cast(Month) 13)); - - testDT(DateTime(Date(1, 1, 1), TimeOfDay(12, 30, 33)), - cast(Month) 7, - DateTime(Date(1, 7, 1), TimeOfDay(12, 30, 33))); - testDT(DateTime(Date(-1, 1, 1), TimeOfDay(12, 30, 33)), - cast(Month) 7, - DateTime(Date(-1, 7, 1), TimeOfDay(12, 30, 33))); - - const cdt = DateTime(1999, 7, 6, 12, 30, 33); - immutable idt = DateTime(1999, 7, 6, 12, 30, 33); - static assert(!__traits(compiles, cdt.month = 12)); - static assert(!__traits(compiles, idt.month = 12)); - } - - - /++ - Day of a Gregorian Month. - +/ - @property ubyte day() const @safe pure nothrow @nogc - { - return _date.day; - } - - /// - @safe unittest - { - assert(DateTime(Date(1999, 7, 6), TimeOfDay(9, 7, 5)).day == 6); - assert(DateTime(Date(2010, 10, 4), TimeOfDay(0, 0, 30)).day == 4); - assert(DateTime(Date(-7, 4, 5), TimeOfDay(7, 45, 2)).day == 5); - } - - @safe unittest - { - import std.format : format; - import std.range : chain; - - static void test(DateTime dateTime, int expected) - { - assert(dateTime.day == expected, format("Value given: %s", dateTime)); - } - - foreach (year; chain(testYearsBC, testYearsAD)) - { - foreach (md; testMonthDays) - { - foreach (tod; testTODs) - test(DateTime(Date(year, md.month, md.day), tod), md.day); - } - } - - const cdt = DateTime(1999, 7, 6, 12, 30, 33); - immutable idt = DateTime(1999, 7, 6, 12, 30, 33); - assert(cdt.day == 6); - assert(idt.day == 6); - } - - - /++ - Day of a Gregorian Month. - - Params: - day = The day of the month to set this $(LREF DateTime)'s day to. - - Throws: - $(REF DateTimeException,std,datetime,date) if the given day is not - a valid day of the current month. - +/ - @property void day(int day) @safe pure - { - _date.day = day; - } - - @safe unittest - { - import std.exception : assertNotThrown; - - static void testDT(DateTime dt, int day) - { - dt.day = day; - } - - // Test A.D. - assertThrown!DateTimeException(testDT(DateTime(Date(1, 1, 1)), 0)); - assertThrown!DateTimeException(testDT(DateTime(Date(1, 1, 1)), 32)); - assertThrown!DateTimeException(testDT(DateTime(Date(1, 2, 1)), 29)); - assertThrown!DateTimeException(testDT(DateTime(Date(4, 2, 1)), 30)); - assertThrown!DateTimeException(testDT(DateTime(Date(1, 3, 1)), 32)); - assertThrown!DateTimeException(testDT(DateTime(Date(1, 4, 1)), 31)); - assertThrown!DateTimeException(testDT(DateTime(Date(1, 5, 1)), 32)); - assertThrown!DateTimeException(testDT(DateTime(Date(1, 6, 1)), 31)); - assertThrown!DateTimeException(testDT(DateTime(Date(1, 7, 1)), 32)); - assertThrown!DateTimeException(testDT(DateTime(Date(1, 8, 1)), 32)); - assertThrown!DateTimeException(testDT(DateTime(Date(1, 9, 1)), 31)); - assertThrown!DateTimeException(testDT(DateTime(Date(1, 10, 1)), 32)); - assertThrown!DateTimeException(testDT(DateTime(Date(1, 11, 1)), 31)); - assertThrown!DateTimeException(testDT(DateTime(Date(1, 12, 1)), 32)); - - assertNotThrown!DateTimeException(testDT(DateTime(Date(1, 1, 1)), 31)); - assertNotThrown!DateTimeException(testDT(DateTime(Date(1, 2, 1)), 28)); - assertNotThrown!DateTimeException(testDT(DateTime(Date(4, 2, 1)), 29)); - assertNotThrown!DateTimeException(testDT(DateTime(Date(1, 3, 1)), 31)); - assertNotThrown!DateTimeException(testDT(DateTime(Date(1, 4, 1)), 30)); - assertNotThrown!DateTimeException(testDT(DateTime(Date(1, 5, 1)), 31)); - assertNotThrown!DateTimeException(testDT(DateTime(Date(1, 6, 1)), 30)); - assertNotThrown!DateTimeException(testDT(DateTime(Date(1, 7, 1)), 31)); - assertNotThrown!DateTimeException(testDT(DateTime(Date(1, 8, 1)), 31)); - assertNotThrown!DateTimeException(testDT(DateTime(Date(1, 9, 1)), 30)); - assertNotThrown!DateTimeException(testDT(DateTime(Date(1, 10, 1)), 31)); - assertNotThrown!DateTimeException(testDT(DateTime(Date(1, 11, 1)), 30)); - assertNotThrown!DateTimeException(testDT(DateTime(Date(1, 12, 1)), 31)); - - { - auto dt = DateTime(Date(1, 1, 1), TimeOfDay(7, 12, 22)); - dt.day = 6; - assert(dt == DateTime(Date(1, 1, 6), TimeOfDay(7, 12, 22))); - } - - // Test B.C. - assertThrown!DateTimeException(testDT(DateTime(Date(-1, 1, 1)), 0)); - assertThrown!DateTimeException(testDT(DateTime(Date(-1, 1, 1)), 32)); - assertThrown!DateTimeException(testDT(DateTime(Date(-1, 2, 1)), 29)); - assertThrown!DateTimeException(testDT(DateTime(Date(0, 2, 1)), 30)); - assertThrown!DateTimeException(testDT(DateTime(Date(-1, 3, 1)), 32)); - assertThrown!DateTimeException(testDT(DateTime(Date(-1, 4, 1)), 31)); - assertThrown!DateTimeException(testDT(DateTime(Date(-1, 5, 1)), 32)); - assertThrown!DateTimeException(testDT(DateTime(Date(-1, 6, 1)), 31)); - assertThrown!DateTimeException(testDT(DateTime(Date(-1, 7, 1)), 32)); - assertThrown!DateTimeException(testDT(DateTime(Date(-1, 8, 1)), 32)); - assertThrown!DateTimeException(testDT(DateTime(Date(-1, 9, 1)), 31)); - assertThrown!DateTimeException(testDT(DateTime(Date(-1, 10, 1)), 32)); - assertThrown!DateTimeException(testDT(DateTime(Date(-1, 11, 1)), 31)); - assertThrown!DateTimeException(testDT(DateTime(Date(-1, 12, 1)), 32)); - - assertNotThrown!DateTimeException(testDT(DateTime(Date(-1, 1, 1)), 31)); - assertNotThrown!DateTimeException(testDT(DateTime(Date(-1, 2, 1)), 28)); - assertNotThrown!DateTimeException(testDT(DateTime(Date(0, 2, 1)), 29)); - assertNotThrown!DateTimeException(testDT(DateTime(Date(-1, 3, 1)), 31)); - assertNotThrown!DateTimeException(testDT(DateTime(Date(-1, 4, 1)), 30)); - assertNotThrown!DateTimeException(testDT(DateTime(Date(-1, 5, 1)), 31)); - assertNotThrown!DateTimeException(testDT(DateTime(Date(-1, 6, 1)), 30)); - assertNotThrown!DateTimeException(testDT(DateTime(Date(-1, 7, 1)), 31)); - assertNotThrown!DateTimeException(testDT(DateTime(Date(-1, 8, 1)), 31)); - assertNotThrown!DateTimeException(testDT(DateTime(Date(-1, 9, 1)), 30)); - assertNotThrown!DateTimeException(testDT(DateTime(Date(-1, 10, 1)), 31)); - assertNotThrown!DateTimeException(testDT(DateTime(Date(-1, 11, 1)), 30)); - assertNotThrown!DateTimeException(testDT(DateTime(Date(-1, 12, 1)), 31)); - - auto dt = DateTime(Date(-1, 1, 1), TimeOfDay(7, 12, 22)); - dt.day = 6; - assert(dt == DateTime(Date(-1, 1, 6), TimeOfDay(7, 12, 22))); - - const cdt = DateTime(1999, 7, 6, 12, 30, 33); - immutable idt = DateTime(1999, 7, 6, 12, 30, 33); - static assert(!__traits(compiles, cdt.day = 27)); - static assert(!__traits(compiles, idt.day = 27)); - } - - - /++ - Hours past midnight. - +/ - @property ubyte hour() const @safe pure nothrow @nogc - { - return _tod.hour; - } - - @safe unittest - { - assert(DateTime.init.hour == 0); - assert(DateTime(Date.init, TimeOfDay(12, 0, 0)).hour == 12); - - const cdt = DateTime(1999, 7, 6, 12, 30, 33); - immutable idt = DateTime(1999, 7, 6, 12, 30, 33); - assert(cdt.hour == 12); - assert(idt.hour == 12); - } - - - /++ - Hours past midnight. - - Params: - hour = The hour of the day to set this $(LREF DateTime)'s hour to. - - Throws: - $(REF DateTimeException,std,datetime,date) if the given hour would - result in an invalid $(LREF DateTime). - +/ - @property void hour(int hour) @safe pure - { - _tod.hour = hour; - } - - @safe unittest - { - assertThrown!DateTimeException((){DateTime(Date(1999, 7, 6), TimeOfDay(0, 0, 0)).hour = 24;}()); - - auto dt = DateTime.init; - dt.hour = 12; - assert(dt == DateTime(1, 1, 1, 12, 0, 0)); - - const cdt = DateTime(1999, 7, 6, 12, 30, 33); - immutable idt = DateTime(1999, 7, 6, 12, 30, 33); - static assert(!__traits(compiles, cdt.hour = 27)); - static assert(!__traits(compiles, idt.hour = 27)); - } - - - /++ - Minutes past the hour. - +/ - @property ubyte minute() const @safe pure nothrow @nogc - { - return _tod.minute; - } - - @safe unittest - { - assert(DateTime.init.minute == 0); - assert(DateTime(1, 1, 1, 0, 30, 0).minute == 30); - - const cdt = DateTime(1999, 7, 6, 12, 30, 33); - immutable idt = DateTime(1999, 7, 6, 12, 30, 33); - assert(cdt.minute == 30); - assert(idt.minute == 30); - } - - - /++ - Minutes past the hour. - - Params: - minute = The minute to set this $(LREF DateTime)'s minute to. - - Throws: - $(REF DateTimeException,std,datetime,date) if the given minute - would result in an invalid $(LREF DateTime). - +/ - @property void minute(int minute) @safe pure - { - _tod.minute = minute; - } - - @safe unittest - { - assertThrown!DateTimeException((){DateTime.init.minute = 60;}()); - - auto dt = DateTime.init; - dt.minute = 30; - assert(dt == DateTime(1, 1, 1, 0, 30, 0)); - - const cdt = DateTime(1999, 7, 6, 12, 30, 33); - immutable idt = DateTime(1999, 7, 6, 12, 30, 33); - static assert(!__traits(compiles, cdt.minute = 27)); - static assert(!__traits(compiles, idt.minute = 27)); - } - - - /++ - Seconds past the minute. - +/ - @property ubyte second() const @safe pure nothrow @nogc - { - return _tod.second; - } - - @safe unittest - { - assert(DateTime.init.second == 0); - assert(DateTime(1, 1, 1, 0, 0, 33).second == 33); - - const cdt = DateTime(1999, 7, 6, 12, 30, 33); - immutable idt = DateTime(1999, 7, 6, 12, 30, 33); - assert(cdt.second == 33); - assert(idt.second == 33); - } - - - /++ - Seconds past the minute. - - Params: - second = The second to set this $(LREF DateTime)'s second to. - - Throws: - $(REF DateTimeException,std,datetime,date) if the given seconds - would result in an invalid $(LREF DateTime). - +/ - @property void second(int second) @safe pure - { - _tod.second = second; - } - - @safe unittest - { - assertThrown!DateTimeException((){DateTime.init.second = 60;}()); - - auto dt = DateTime.init; - dt.second = 33; - assert(dt == DateTime(1, 1, 1, 0, 0, 33)); - - const cdt = DateTime(1999, 7, 6, 12, 30, 33); - immutable idt = DateTime(1999, 7, 6, 12, 30, 33); - static assert(!__traits(compiles, cdt.second = 27)); - static assert(!__traits(compiles, idt.second = 27)); - } - - - /++ - Adds the given number of years or months to this $(LREF DateTime), - mutating it. A negative number will subtract. - - Note that if day overflow is allowed, and the date with the adjusted - year/month overflows the number of days in the new month, then the month - will be incremented by one, and the day set to the number of days - overflowed. (e.g. if the day were 31 and the new month were June, then - the month would be incremented to July, and the new day would be 1). If - day overflow is not allowed, then the day will be set to the last valid - day in the month (e.g. June 31st would become June 30th). - - Params: - units = The type of units to add ("years" or "months"). - value = The number of months or years to add to this - $(LREF DateTime). - allowOverflow = Whether the days should be allowed to overflow, - causing the month to increment. - - Returns: - A reference to the `DateTime` (`this`). - +/ - ref DateTime add(string units) - (long value, AllowDayOverflow allowOverflow = AllowDayOverflow.yes) @safe pure nothrow @nogc - if (units == "years" || units == "months") - { - _date.add!units(value, allowOverflow); - return this; - } - - /// - @safe unittest - { - auto dt1 = DateTime(2010, 1, 1, 12, 30, 33); - dt1.add!"months"(11); - assert(dt1 == DateTime(2010, 12, 1, 12, 30, 33)); - - auto dt2 = DateTime(2010, 1, 1, 12, 30, 33); - dt2.add!"months"(-11); - assert(dt2 == DateTime(2009, 2, 1, 12, 30, 33)); - - auto dt3 = DateTime(2000, 2, 29, 12, 30, 33); - dt3.add!"years"(1); - assert(dt3 == DateTime(2001, 3, 1, 12, 30, 33)); - - auto dt4 = DateTime(2000, 2, 29, 12, 30, 33); - dt4.add!"years"(1, AllowDayOverflow.no); - assert(dt4 == DateTime(2001, 2, 28, 12, 30, 33)); - } - - @safe unittest - { - auto dt = DateTime(2000, 1, 31); - dt.add!"years"(7).add!"months"(-4); - assert(dt == DateTime(2006, 10, 1)); - - const cdt = DateTime(1999, 7, 6, 12, 30, 33); - immutable idt = DateTime(1999, 7, 6, 12, 30, 33); - static assert(!__traits(compiles, cdt.add!"years"(4))); - static assert(!__traits(compiles, idt.add!"years"(4))); - static assert(!__traits(compiles, cdt.add!"months"(4))); - static assert(!__traits(compiles, idt.add!"months"(4))); - } - - - /++ - Adds the given number of years or months to this $(LREF DateTime), - mutating it. A negative number will subtract. - - The difference between rolling and adding is that rolling does not - affect larger units. Rolling a $(LREF DateTime) 12 months - gets the exact same $(LREF DateTime). However, the days can still be - affected due to the differing number of days in each month. - - Because there are no units larger than years, there is no difference - between adding and rolling years. - - Params: - units = The type of units to add ("years" or "months"). - value = The number of months or years to add to this - $(LREF DateTime). - allowOverflow = Whether the days should be allowed to overflow, - causing the month to increment. - - Returns: - A reference to the `DateTime` (`this`). - +/ - ref DateTime roll(string units) - (long value, AllowDayOverflow allowOverflow = AllowDayOverflow.yes) @safe pure nothrow @nogc - if (units == "years" || units == "months") - { - _date.roll!units(value, allowOverflow); - return this; - } - - /// - @safe unittest - { - auto dt1 = DateTime(2010, 1, 1, 12, 33, 33); - dt1.roll!"months"(1); - assert(dt1 == DateTime(2010, 2, 1, 12, 33, 33)); - - auto dt2 = DateTime(2010, 1, 1, 12, 33, 33); - dt2.roll!"months"(-1); - assert(dt2 == DateTime(2010, 12, 1, 12, 33, 33)); - - auto dt3 = DateTime(1999, 1, 29, 12, 33, 33); - dt3.roll!"months"(1); - assert(dt3 == DateTime(1999, 3, 1, 12, 33, 33)); - - auto dt4 = DateTime(1999, 1, 29, 12, 33, 33); - dt4.roll!"months"(1, AllowDayOverflow.no); - assert(dt4 == DateTime(1999, 2, 28, 12, 33, 33)); - - auto dt5 = DateTime(2000, 2, 29, 12, 30, 33); - dt5.roll!"years"(1); - assert(dt5 == DateTime(2001, 3, 1, 12, 30, 33)); - - auto dt6 = DateTime(2000, 2, 29, 12, 30, 33); - dt6.roll!"years"(1, AllowDayOverflow.no); - assert(dt6 == DateTime(2001, 2, 28, 12, 30, 33)); - } - - @safe unittest - { - auto dt = DateTime(2000, 1, 31); - dt.roll!"years"(7).roll!"months"(-4); - assert(dt == DateTime(2007, 10, 1)); - - const cdt = DateTime(1999, 7, 6, 12, 30, 33); - immutable idt = DateTime(1999, 7, 6, 12, 30, 33); - static assert(!__traits(compiles, cdt.roll!"years"(4))); - static assert(!__traits(compiles, idt.roll!"years"(4))); - static assert(!__traits(compiles, cdt.roll!"months"(4))); - static assert(!__traits(compiles, idt.roll!"months"(4))); - } - - - /++ - Adds the given number of units to this $(LREF DateTime), mutating it. A - negative number will subtract. - - The difference between rolling and adding is that rolling does not - affect larger units. For instance, rolling a $(LREF DateTime) one - year's worth of days gets the exact same $(LREF DateTime). - - Accepted units are `"days"`, `"minutes"`, `"hours"`, - `"minutes"`, and `"seconds"`. - - Params: - units = The units to add. - value = The number of $(D_PARAM units) to add to this - $(LREF DateTime). - - Returns: - A reference to the `DateTime` (`this`). - +/ - ref DateTime roll(string units)(long value) @safe pure nothrow @nogc - if (units == "days") - { - _date.roll!"days"(value); - return this; - } - - /// - @safe unittest - { - auto dt1 = DateTime(2010, 1, 1, 11, 23, 12); - dt1.roll!"days"(1); - assert(dt1 == DateTime(2010, 1, 2, 11, 23, 12)); - dt1.roll!"days"(365); - assert(dt1 == DateTime(2010, 1, 26, 11, 23, 12)); - dt1.roll!"days"(-32); - assert(dt1 == DateTime(2010, 1, 25, 11, 23, 12)); - - auto dt2 = DateTime(2010, 7, 4, 12, 0, 0); - dt2.roll!"hours"(1); - assert(dt2 == DateTime(2010, 7, 4, 13, 0, 0)); - - auto dt3 = DateTime(2010, 1, 1, 0, 0, 0); - dt3.roll!"seconds"(-1); - assert(dt3 == DateTime(2010, 1, 1, 0, 0, 59)); - } - - @safe unittest - { - auto dt = DateTime(2000, 1, 31); - dt.roll!"days"(7).roll!"days"(-4); - assert(dt == DateTime(2000, 1, 3)); - - const cdt = DateTime(1999, 7, 6, 12, 30, 33); - immutable idt = DateTime(1999, 7, 6, 12, 30, 33); - static assert(!__traits(compiles, cdt.roll!"days"(4))); - static assert(!__traits(compiles, idt.roll!"days"(4))); - } - - - /// ditto - ref DateTime roll(string units)(long value) @safe pure nothrow @nogc - if (units == "hours" || - units == "minutes" || - units == "seconds") - { - _tod.roll!units(value); - return this; - } - - // Test roll!"hours"(). - @safe unittest - { - static void testDT(DateTime orig, int hours, DateTime expected, size_t line = __LINE__) - { - orig.roll!"hours"(hours); - assert(orig == expected); - } - - // Test A.D. - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 0, - DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 1, - DateTime(Date(1999, 7, 6), TimeOfDay(13, 30, 33))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 2, - DateTime(Date(1999, 7, 6), TimeOfDay(14, 30, 33))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 3, - DateTime(Date(1999, 7, 6), TimeOfDay(15, 30, 33))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 4, - DateTime(Date(1999, 7, 6), TimeOfDay(16, 30, 33))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 5, - DateTime(Date(1999, 7, 6), TimeOfDay(17, 30, 33))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 6, - DateTime(Date(1999, 7, 6), TimeOfDay(18, 30, 33))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 7, - DateTime(Date(1999, 7, 6), TimeOfDay(19, 30, 33))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 8, - DateTime(Date(1999, 7, 6), TimeOfDay(20, 30, 33))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 9, - DateTime(Date(1999, 7, 6), TimeOfDay(21, 30, 33))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 10, - DateTime(Date(1999, 7, 6), TimeOfDay(22, 30, 33))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 11, - DateTime(Date(1999, 7, 6), TimeOfDay(23, 30, 33))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 12, - DateTime(Date(1999, 7, 6), TimeOfDay(0, 30, 33))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 13, - DateTime(Date(1999, 7, 6), TimeOfDay(1, 30, 33))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 14, - DateTime(Date(1999, 7, 6), TimeOfDay(2, 30, 33))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 15, - DateTime(Date(1999, 7, 6), TimeOfDay(3, 30, 33))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 16, - DateTime(Date(1999, 7, 6), TimeOfDay(4, 30, 33))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 17, - DateTime(Date(1999, 7, 6), TimeOfDay(5, 30, 33))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 18, - DateTime(Date(1999, 7, 6), TimeOfDay(6, 30, 33))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 19, - DateTime(Date(1999, 7, 6), TimeOfDay(7, 30, 33))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 20, - DateTime(Date(1999, 7, 6), TimeOfDay(8, 30, 33))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 21, - DateTime(Date(1999, 7, 6), TimeOfDay(9, 30, 33))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 22, - DateTime(Date(1999, 7, 6), TimeOfDay(10, 30, 33))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 23, - DateTime(Date(1999, 7, 6), TimeOfDay(11, 30, 33))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 24, - DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 25, - DateTime(Date(1999, 7, 6), TimeOfDay(13, 30, 33))); - - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -1, - DateTime(Date(1999, 7, 6), TimeOfDay(11, 30, 33))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -2, - DateTime(Date(1999, 7, 6), TimeOfDay(10, 30, 33))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -3, - DateTime(Date(1999, 7, 6), TimeOfDay(9, 30, 33))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -4, - DateTime(Date(1999, 7, 6), TimeOfDay(8, 30, 33))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -5, - DateTime(Date(1999, 7, 6), TimeOfDay(7, 30, 33))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -6, - DateTime(Date(1999, 7, 6), TimeOfDay(6, 30, 33))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -7, - DateTime(Date(1999, 7, 6), TimeOfDay(5, 30, 33))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -8, - DateTime(Date(1999, 7, 6), TimeOfDay(4, 30, 33))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -9, - DateTime(Date(1999, 7, 6), TimeOfDay(3, 30, 33))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -10, - DateTime(Date(1999, 7, 6), TimeOfDay(2, 30, 33))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -11, - DateTime(Date(1999, 7, 6), TimeOfDay(1, 30, 33))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -12, - DateTime(Date(1999, 7, 6), TimeOfDay(0, 30, 33))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -13, - DateTime(Date(1999, 7, 6), TimeOfDay(23, 30, 33))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -14, - DateTime(Date(1999, 7, 6), TimeOfDay(22, 30, 33))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -15, - DateTime(Date(1999, 7, 6), TimeOfDay(21, 30, 33))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -16, - DateTime(Date(1999, 7, 6), TimeOfDay(20, 30, 33))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -17, - DateTime(Date(1999, 7, 6), TimeOfDay(19, 30, 33))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -18, - DateTime(Date(1999, 7, 6), TimeOfDay(18, 30, 33))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -19, - DateTime(Date(1999, 7, 6), TimeOfDay(17, 30, 33))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -20, - DateTime(Date(1999, 7, 6), TimeOfDay(16, 30, 33))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -21, - DateTime(Date(1999, 7, 6), TimeOfDay(15, 30, 33))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -22, - DateTime(Date(1999, 7, 6), TimeOfDay(14, 30, 33))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -23, - DateTime(Date(1999, 7, 6), TimeOfDay(13, 30, 33))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -24, - DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -25, - DateTime(Date(1999, 7, 6), TimeOfDay(11, 30, 33))); - - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(0, 30, 33)), 1, - DateTime(Date(1999, 7, 6), TimeOfDay(1, 30, 33))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(0, 30, 33)), 0, - DateTime(Date(1999, 7, 6), TimeOfDay(0, 30, 33))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(0, 30, 33)), -1, - DateTime(Date(1999, 7, 6), TimeOfDay(23, 30, 33))); - - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(23, 30, 33)), 1, - DateTime(Date(1999, 7, 6), TimeOfDay(0, 30, 33))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(23, 30, 33)), 0, - DateTime(Date(1999, 7, 6), TimeOfDay(23, 30, 33))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(23, 30, 33)), -1, - DateTime(Date(1999, 7, 6), TimeOfDay(22, 30, 33))); - - testDT(DateTime(Date(1999, 7, 31), TimeOfDay(23, 30, 33)), 1, - DateTime(Date(1999, 7, 31), TimeOfDay(0, 30, 33))); - testDT(DateTime(Date(1999, 8, 1), TimeOfDay(0, 30, 33)), -1, - DateTime(Date(1999, 8, 1), TimeOfDay(23, 30, 33))); - - testDT(DateTime(Date(1999, 12, 31), TimeOfDay(23, 30, 33)), 1, - DateTime(Date(1999, 12, 31), TimeOfDay(0, 30, 33))); - testDT(DateTime(Date(2000, 1, 1), TimeOfDay(0, 30, 33)), -1, - DateTime(Date(2000, 1, 1), TimeOfDay(23, 30, 33))); - - testDT(DateTime(Date(1999, 2, 28), TimeOfDay(23, 30, 33)), 25, - DateTime(Date(1999, 2, 28), TimeOfDay(0, 30, 33))); - testDT(DateTime(Date(1999, 3, 2), TimeOfDay(0, 30, 33)), -25, - DateTime(Date(1999, 3, 2), TimeOfDay(23, 30, 33))); - - testDT(DateTime(Date(2000, 2, 28), TimeOfDay(23, 30, 33)), 25, - DateTime(Date(2000, 2, 28), TimeOfDay(0, 30, 33))); - testDT(DateTime(Date(2000, 3, 1), TimeOfDay(0, 30, 33)), -25, - DateTime(Date(2000, 3, 1), TimeOfDay(23, 30, 33))); - - // Test B.C. - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 0, - DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 1, - DateTime(Date(-1999, 7, 6), TimeOfDay(13, 30, 33))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 2, - DateTime(Date(-1999, 7, 6), TimeOfDay(14, 30, 33))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 3, - DateTime(Date(-1999, 7, 6), TimeOfDay(15, 30, 33))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 4, - DateTime(Date(-1999, 7, 6), TimeOfDay(16, 30, 33))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 5, - DateTime(Date(-1999, 7, 6), TimeOfDay(17, 30, 33))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 6, - DateTime(Date(-1999, 7, 6), TimeOfDay(18, 30, 33))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 7, - DateTime(Date(-1999, 7, 6), TimeOfDay(19, 30, 33))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 8, - DateTime(Date(-1999, 7, 6), TimeOfDay(20, 30, 33))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 9, - DateTime(Date(-1999, 7, 6), TimeOfDay(21, 30, 33))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 10, - DateTime(Date(-1999, 7, 6), TimeOfDay(22, 30, 33))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 11, - DateTime(Date(-1999, 7, 6), TimeOfDay(23, 30, 33))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 12, - DateTime(Date(-1999, 7, 6), TimeOfDay(0, 30, 33))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 13, - DateTime(Date(-1999, 7, 6), TimeOfDay(1, 30, 33))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 14, - DateTime(Date(-1999, 7, 6), TimeOfDay(2, 30, 33))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 15, - DateTime(Date(-1999, 7, 6), TimeOfDay(3, 30, 33))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 16, - DateTime(Date(-1999, 7, 6), TimeOfDay(4, 30, 33))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 17, - DateTime(Date(-1999, 7, 6), TimeOfDay(5, 30, 33))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 18, - DateTime(Date(-1999, 7, 6), TimeOfDay(6, 30, 33))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 19, - DateTime(Date(-1999, 7, 6), TimeOfDay(7, 30, 33))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 20, - DateTime(Date(-1999, 7, 6), TimeOfDay(8, 30, 33))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 21, - DateTime(Date(-1999, 7, 6), TimeOfDay(9, 30, 33))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 22, - DateTime(Date(-1999, 7, 6), TimeOfDay(10, 30, 33))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 23, - DateTime(Date(-1999, 7, 6), TimeOfDay(11, 30, 33))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 24, - DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 25, - DateTime(Date(-1999, 7, 6), TimeOfDay(13, 30, 33))); - - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -1, - DateTime(Date(-1999, 7, 6), TimeOfDay(11, 30, 33))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -2, - DateTime(Date(-1999, 7, 6), TimeOfDay(10, 30, 33))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -3, - DateTime(Date(-1999, 7, 6), TimeOfDay(9, 30, 33))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -4, - DateTime(Date(-1999, 7, 6), TimeOfDay(8, 30, 33))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -5, - DateTime(Date(-1999, 7, 6), TimeOfDay(7, 30, 33))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -6, - DateTime(Date(-1999, 7, 6), TimeOfDay(6, 30, 33))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -7, - DateTime(Date(-1999, 7, 6), TimeOfDay(5, 30, 33))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -8, - DateTime(Date(-1999, 7, 6), TimeOfDay(4, 30, 33))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -9, - DateTime(Date(-1999, 7, 6), TimeOfDay(3, 30, 33))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -10, - DateTime(Date(-1999, 7, 6), TimeOfDay(2, 30, 33))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -11, - DateTime(Date(-1999, 7, 6), TimeOfDay(1, 30, 33))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -12, - DateTime(Date(-1999, 7, 6), TimeOfDay(0, 30, 33))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -13, - DateTime(Date(-1999, 7, 6), TimeOfDay(23, 30, 33))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -14, - DateTime(Date(-1999, 7, 6), TimeOfDay(22, 30, 33))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -15, - DateTime(Date(-1999, 7, 6), TimeOfDay(21, 30, 33))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -16, - DateTime(Date(-1999, 7, 6), TimeOfDay(20, 30, 33))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -17, - DateTime(Date(-1999, 7, 6), TimeOfDay(19, 30, 33))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -18, - DateTime(Date(-1999, 7, 6), TimeOfDay(18, 30, 33))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -19, - DateTime(Date(-1999, 7, 6), TimeOfDay(17, 30, 33))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -20, - DateTime(Date(-1999, 7, 6), TimeOfDay(16, 30, 33))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -21, - DateTime(Date(-1999, 7, 6), TimeOfDay(15, 30, 33))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -22, - DateTime(Date(-1999, 7, 6), TimeOfDay(14, 30, 33))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -23, - DateTime(Date(-1999, 7, 6), TimeOfDay(13, 30, 33))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -24, - DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -25, - DateTime(Date(-1999, 7, 6), TimeOfDay(11, 30, 33))); - - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(0, 30, 33)), 1, - DateTime(Date(-1999, 7, 6), TimeOfDay(1, 30, 33))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(0, 30, 33)), 0, - DateTime(Date(-1999, 7, 6), TimeOfDay(0, 30, 33))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(0, 30, 33)), -1, - DateTime(Date(-1999, 7, 6), TimeOfDay(23, 30, 33))); - - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(23, 30, 33)), 1, - DateTime(Date(-1999, 7, 6), TimeOfDay(0, 30, 33))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(23, 30, 33)), 0, - DateTime(Date(-1999, 7, 6), TimeOfDay(23, 30, 33))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(23, 30, 33)), -1, - DateTime(Date(-1999, 7, 6), TimeOfDay(22, 30, 33))); - - testDT(DateTime(Date(-1999, 7, 31), TimeOfDay(23, 30, 33)), 1, - DateTime(Date(-1999, 7, 31), TimeOfDay(0, 30, 33))); - testDT(DateTime(Date(-1999, 8, 1), TimeOfDay(0, 30, 33)), -1, - DateTime(Date(-1999, 8, 1), TimeOfDay(23, 30, 33))); - - testDT(DateTime(Date(-2001, 12, 31), TimeOfDay(23, 30, 33)), 1, - DateTime(Date(-2001, 12, 31), TimeOfDay(0, 30, 33))); - testDT(DateTime(Date(-2000, 1, 1), TimeOfDay(0, 30, 33)), -1, - DateTime(Date(-2000, 1, 1), TimeOfDay(23, 30, 33))); - - testDT(DateTime(Date(-2001, 2, 28), TimeOfDay(23, 30, 33)), 25, - DateTime(Date(-2001, 2, 28), TimeOfDay(0, 30, 33))); - testDT(DateTime(Date(-2001, 3, 2), TimeOfDay(0, 30, 33)), -25, - DateTime(Date(-2001, 3, 2), TimeOfDay(23, 30, 33))); - - testDT(DateTime(Date(-2000, 2, 28), TimeOfDay(23, 30, 33)), 25, - DateTime(Date(-2000, 2, 28), TimeOfDay(0, 30, 33))); - testDT(DateTime(Date(-2000, 3, 1), TimeOfDay(0, 30, 33)), -25, - DateTime(Date(-2000, 3, 1), TimeOfDay(23, 30, 33))); - - // Test Both - testDT(DateTime(Date(-1, 1, 1), TimeOfDay(11, 30, 33)), 17_546, - DateTime(Date(-1, 1, 1), TimeOfDay(13, 30, 33))); - testDT(DateTime(Date(1, 1, 1), TimeOfDay(13, 30, 33)), -17_546, - DateTime(Date(1, 1, 1), TimeOfDay(11, 30, 33))); - - auto dt = DateTime(2000, 1, 31, 9, 7, 6); - dt.roll!"hours"(27).roll!"hours"(-9); - assert(dt == DateTime(2000, 1, 31, 3, 7, 6)); - - const cdt = DateTime(1999, 7, 6, 12, 30, 33); - immutable idt = DateTime(1999, 7, 6, 12, 30, 33); - static assert(!__traits(compiles, cdt.roll!"hours"(4))); - static assert(!__traits(compiles, idt.roll!"hours"(4))); - } - - // Test roll!"minutes"(). - @safe unittest - { - static void testDT(DateTime orig, int minutes, DateTime expected, size_t line = __LINE__) - { - orig.roll!"minutes"(minutes); - assert(orig == expected); - } - - // Test A.D. - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 0, - DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 1, - DateTime(Date(1999, 7, 6), TimeOfDay(12, 31, 33))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 2, - DateTime(Date(1999, 7, 6), TimeOfDay(12, 32, 33))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 3, - DateTime(Date(1999, 7, 6), TimeOfDay(12, 33, 33))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 4, - DateTime(Date(1999, 7, 6), TimeOfDay(12, 34, 33))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 5, - DateTime(Date(1999, 7, 6), TimeOfDay(12, 35, 33))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 10, - DateTime(Date(1999, 7, 6), TimeOfDay(12, 40, 33))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 15, - DateTime(Date(1999, 7, 6), TimeOfDay(12, 45, 33))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 29, - DateTime(Date(1999, 7, 6), TimeOfDay(12, 59, 33))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 30, - DateTime(Date(1999, 7, 6), TimeOfDay(12, 0, 33))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 45, - DateTime(Date(1999, 7, 6), TimeOfDay(12, 15, 33))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 60, - DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 75, - DateTime(Date(1999, 7, 6), TimeOfDay(12, 45, 33))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 90, - DateTime(Date(1999, 7, 6), TimeOfDay(12, 0, 33))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 100, - DateTime(Date(1999, 7, 6), TimeOfDay(12, 10, 33))); - - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 689, - DateTime(Date(1999, 7, 6), TimeOfDay(12, 59, 33))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 690, - DateTime(Date(1999, 7, 6), TimeOfDay(12, 0, 33))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 691, - DateTime(Date(1999, 7, 6), TimeOfDay(12, 1, 33))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 960, - DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 1439, - DateTime(Date(1999, 7, 6), TimeOfDay(12, 29, 33))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 1440, - DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 1441, - DateTime(Date(1999, 7, 6), TimeOfDay(12, 31, 33))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 2880, - DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33))); - - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -1, - DateTime(Date(1999, 7, 6), TimeOfDay(12, 29, 33))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -2, - DateTime(Date(1999, 7, 6), TimeOfDay(12, 28, 33))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -3, - DateTime(Date(1999, 7, 6), TimeOfDay(12, 27, 33))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -4, - DateTime(Date(1999, 7, 6), TimeOfDay(12, 26, 33))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -5, - DateTime(Date(1999, 7, 6), TimeOfDay(12, 25, 33))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -10, - DateTime(Date(1999, 7, 6), TimeOfDay(12, 20, 33))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -15, - DateTime(Date(1999, 7, 6), TimeOfDay(12, 15, 33))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -29, - DateTime(Date(1999, 7, 6), TimeOfDay(12, 1, 33))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -30, - DateTime(Date(1999, 7, 6), TimeOfDay(12, 0, 33))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -45, - DateTime(Date(1999, 7, 6), TimeOfDay(12, 45, 33))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -60, - DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -75, - DateTime(Date(1999, 7, 6), TimeOfDay(12, 15, 33))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -90, - DateTime(Date(1999, 7, 6), TimeOfDay(12, 0, 33))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -100, - DateTime(Date(1999, 7, 6), TimeOfDay(12, 50, 33))); - - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -749, - DateTime(Date(1999, 7, 6), TimeOfDay(12, 1, 33))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -750, - DateTime(Date(1999, 7, 6), TimeOfDay(12, 0, 33))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -751, - DateTime(Date(1999, 7, 6), TimeOfDay(12, 59, 33))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -960, - DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -1439, - DateTime(Date(1999, 7, 6), TimeOfDay(12, 31, 33))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -1440, - DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -1441, - DateTime(Date(1999, 7, 6), TimeOfDay(12, 29, 33))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -2880, - DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33))); - - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 0, 33)), 1, - DateTime(Date(1999, 7, 6), TimeOfDay(12, 1, 33))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 0, 33)), 0, - DateTime(Date(1999, 7, 6), TimeOfDay(12, 0, 33))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 0, 33)), -1, - DateTime(Date(1999, 7, 6), TimeOfDay(12, 59, 33))); - - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(11, 59, 33)), 1, - DateTime(Date(1999, 7, 6), TimeOfDay(11, 0, 33))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(11, 59, 33)), 0, - DateTime(Date(1999, 7, 6), TimeOfDay(11, 59, 33))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(11, 59, 33)), -1, - DateTime(Date(1999, 7, 6), TimeOfDay(11, 58, 33))); - - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(0, 0, 33)), 1, - DateTime(Date(1999, 7, 6), TimeOfDay(0, 1, 33))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(0, 0, 33)), 0, - DateTime(Date(1999, 7, 6), TimeOfDay(0, 0, 33))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(0, 0, 33)), -1, - DateTime(Date(1999, 7, 6), TimeOfDay(0, 59, 33))); - - testDT(DateTime(Date(1999, 7, 5), TimeOfDay(23, 59, 33)), 1, - DateTime(Date(1999, 7, 5), TimeOfDay(23, 0, 33))); - testDT(DateTime(Date(1999, 7, 5), TimeOfDay(23, 59, 33)), 0, - DateTime(Date(1999, 7, 5), TimeOfDay(23, 59, 33))); - testDT(DateTime(Date(1999, 7, 5), TimeOfDay(23, 59, 33)), -1, - DateTime(Date(1999, 7, 5), TimeOfDay(23, 58, 33))); - - testDT(DateTime(Date(1998, 12, 31), TimeOfDay(23, 59, 33)), 1, - DateTime(Date(1998, 12, 31), TimeOfDay(23, 0, 33))); - testDT(DateTime(Date(1998, 12, 31), TimeOfDay(23, 59, 33)), 0, - DateTime(Date(1998, 12, 31), TimeOfDay(23, 59, 33))); - testDT(DateTime(Date(1998, 12, 31), TimeOfDay(23, 59, 33)), -1, - DateTime(Date(1998, 12, 31), TimeOfDay(23, 58, 33))); - - // Test B.C. - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 0, - DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 1, - DateTime(Date(-1999, 7, 6), TimeOfDay(12, 31, 33))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 2, - DateTime(Date(-1999, 7, 6), TimeOfDay(12, 32, 33))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 3, - DateTime(Date(-1999, 7, 6), TimeOfDay(12, 33, 33))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 4, - DateTime(Date(-1999, 7, 6), TimeOfDay(12, 34, 33))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 5, - DateTime(Date(-1999, 7, 6), TimeOfDay(12, 35, 33))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 10, - DateTime(Date(-1999, 7, 6), TimeOfDay(12, 40, 33))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 15, - DateTime(Date(-1999, 7, 6), TimeOfDay(12, 45, 33))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 29, - DateTime(Date(-1999, 7, 6), TimeOfDay(12, 59, 33))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 30, - DateTime(Date(-1999, 7, 6), TimeOfDay(12, 0, 33))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 45, - DateTime(Date(-1999, 7, 6), TimeOfDay(12, 15, 33))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 60, - DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 75, - DateTime(Date(-1999, 7, 6), TimeOfDay(12, 45, 33))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 90, - DateTime(Date(-1999, 7, 6), TimeOfDay(12, 0, 33))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 100, - DateTime(Date(-1999, 7, 6), TimeOfDay(12, 10, 33))); - - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 689, - DateTime(Date(-1999, 7, 6), TimeOfDay(12, 59, 33))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 690, - DateTime(Date(-1999, 7, 6), TimeOfDay(12, 0, 33))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 691, - DateTime(Date(-1999, 7, 6), TimeOfDay(12, 1, 33))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 960, - DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 1439, - DateTime(Date(-1999, 7, 6), TimeOfDay(12, 29, 33))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 1440, - DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 1441, - DateTime(Date(-1999, 7, 6), TimeOfDay(12, 31, 33))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 2880, - DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33))); - - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -1, - DateTime(Date(-1999, 7, 6), TimeOfDay(12, 29, 33))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -2, - DateTime(Date(-1999, 7, 6), TimeOfDay(12, 28, 33))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -3, - DateTime(Date(-1999, 7, 6), TimeOfDay(12, 27, 33))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -4, - DateTime(Date(-1999, 7, 6), TimeOfDay(12, 26, 33))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -5, - DateTime(Date(-1999, 7, 6), TimeOfDay(12, 25, 33))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -10, - DateTime(Date(-1999, 7, 6), TimeOfDay(12, 20, 33))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -15, - DateTime(Date(-1999, 7, 6), TimeOfDay(12, 15, 33))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -29, - DateTime(Date(-1999, 7, 6), TimeOfDay(12, 1, 33))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -30, - DateTime(Date(-1999, 7, 6), TimeOfDay(12, 0, 33))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -45, - DateTime(Date(-1999, 7, 6), TimeOfDay(12, 45, 33))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -60, - DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -75, - DateTime(Date(-1999, 7, 6), TimeOfDay(12, 15, 33))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -90, - DateTime(Date(-1999, 7, 6), TimeOfDay(12, 0, 33))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -100, - DateTime(Date(-1999, 7, 6), TimeOfDay(12, 50, 33))); - - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -749, - DateTime(Date(-1999, 7, 6), TimeOfDay(12, 1, 33))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -750, - DateTime(Date(-1999, 7, 6), TimeOfDay(12, 0, 33))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -751, - DateTime(Date(-1999, 7, 6), TimeOfDay(12, 59, 33))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -960, - DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -1439, - DateTime(Date(-1999, 7, 6), TimeOfDay(12, 31, 33))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -1440, - DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -1441, - DateTime(Date(-1999, 7, 6), TimeOfDay(12, 29, 33))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -2880, - DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33))); - - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 0, 33)), 1, - DateTime(Date(-1999, 7, 6), TimeOfDay(12, 1, 33))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 0, 33)), 0, - DateTime(Date(-1999, 7, 6), TimeOfDay(12, 0, 33))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 0, 33)), -1, - DateTime(Date(-1999, 7, 6), TimeOfDay(12, 59, 33))); - - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(11, 59, 33)), 1, - DateTime(Date(-1999, 7, 6), TimeOfDay(11, 0, 33))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(11, 59, 33)), 0, - DateTime(Date(-1999, 7, 6), TimeOfDay(11, 59, 33))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(11, 59, 33)), -1, - DateTime(Date(-1999, 7, 6), TimeOfDay(11, 58, 33))); - - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(0, 0, 33)), 1, - DateTime(Date(-1999, 7, 6), TimeOfDay(0, 1, 33))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(0, 0, 33)), 0, - DateTime(Date(-1999, 7, 6), TimeOfDay(0, 0, 33))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(0, 0, 33)), -1, - DateTime(Date(-1999, 7, 6), TimeOfDay(0, 59, 33))); - - testDT(DateTime(Date(-1999, 7, 5), TimeOfDay(23, 59, 33)), 1, - DateTime(Date(-1999, 7, 5), TimeOfDay(23, 0, 33))); - testDT(DateTime(Date(-1999, 7, 5), TimeOfDay(23, 59, 33)), 0, - DateTime(Date(-1999, 7, 5), TimeOfDay(23, 59, 33))); - testDT(DateTime(Date(-1999, 7, 5), TimeOfDay(23, 59, 33)), -1, - DateTime(Date(-1999, 7, 5), TimeOfDay(23, 58, 33))); - - testDT(DateTime(Date(-2000, 12, 31), TimeOfDay(23, 59, 33)), 1, - DateTime(Date(-2000, 12, 31), TimeOfDay(23, 0, 33))); - testDT(DateTime(Date(-2000, 12, 31), TimeOfDay(23, 59, 33)), 0, - DateTime(Date(-2000, 12, 31), TimeOfDay(23, 59, 33))); - testDT(DateTime(Date(-2000, 12, 31), TimeOfDay(23, 59, 33)), -1, - DateTime(Date(-2000, 12, 31), TimeOfDay(23, 58, 33))); - - // Test Both - testDT(DateTime(Date(1, 1, 1), TimeOfDay(0, 0, 0)), -1, - DateTime(Date(1, 1, 1), TimeOfDay(0, 59, 0))); - testDT(DateTime(Date(0, 12, 31), TimeOfDay(23, 59, 0)), 1, - DateTime(Date(0, 12, 31), TimeOfDay(23, 0, 0))); - - testDT(DateTime(Date(0, 1, 1), TimeOfDay(0, 0, 0)), -1, - DateTime(Date(0, 1, 1), TimeOfDay(0, 59, 0))); - testDT(DateTime(Date(-1, 12, 31), TimeOfDay(23, 59, 0)), 1, - DateTime(Date(-1, 12, 31), TimeOfDay(23, 0, 0))); - - testDT(DateTime(Date(-1, 1, 1), TimeOfDay(11, 30, 33)), 1_052_760, - DateTime(Date(-1, 1, 1), TimeOfDay(11, 30, 33))); - testDT(DateTime(Date(1, 1, 1), TimeOfDay(13, 30, 33)), -1_052_760, - DateTime(Date(1, 1, 1), TimeOfDay(13, 30, 33))); - - testDT(DateTime(Date(-1, 1, 1), TimeOfDay(11, 30, 33)), 1_052_782, - DateTime(Date(-1, 1, 1), TimeOfDay(11, 52, 33))); - testDT(DateTime(Date(1, 1, 1), TimeOfDay(13, 52, 33)), -1_052_782, - DateTime(Date(1, 1, 1), TimeOfDay(13, 30, 33))); - - auto dt = DateTime(2000, 1, 31, 9, 7, 6); - dt.roll!"minutes"(92).roll!"minutes"(-292); - assert(dt == DateTime(2000, 1, 31, 9, 47, 6)); - - const cdt = DateTime(1999, 7, 6, 12, 30, 33); - immutable idt = DateTime(1999, 7, 6, 12, 30, 33); - static assert(!__traits(compiles, cdt.roll!"minutes"(4))); - static assert(!__traits(compiles, idt.roll!"minutes"(4))); - } - - // Test roll!"seconds"(). - @safe unittest - { - static void testDT(DateTime orig, int seconds, DateTime expected, size_t line = __LINE__) - { - orig.roll!"seconds"(seconds); - assert(orig == expected); - } - - // Test A.D. - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 0, - DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 1, - DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 34))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 2, - DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 35))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 3, - DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 36))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 4, - DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 37))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 5, - DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 38))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 10, - DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 43))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 15, - DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 48))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 26, - DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 59))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 27, - DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 0))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 30, - DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 3))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 59, - DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 32))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 60, - DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 61, - DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 34))); - - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 1766, - DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 59))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 1767, - DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 0))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 1768, - DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 1))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 2007, - DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 0))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 3599, - DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 32))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 3600, - DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 3601, - DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 34))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 7200, - DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33))); - - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -1, - DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 32))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -2, - DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 31))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -3, - DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 30))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -4, - DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 29))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -5, - DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 28))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -10, - DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 23))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -15, - DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 18))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -33, - DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 0))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -34, - DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 59))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -35, - DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 58))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -59, - DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 34))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -60, - DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -61, - DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 32))); - - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 0)), 1, - DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 1))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 0)), 0, - DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 0))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 0)), -1, - DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 59))); - - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 0, 0)), 1, - DateTime(Date(1999, 7, 6), TimeOfDay(12, 0, 1))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 0, 0)), 0, - DateTime(Date(1999, 7, 6), TimeOfDay(12, 0, 0))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 0, 0)), -1, - DateTime(Date(1999, 7, 6), TimeOfDay(12, 0, 59))); - - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(0, 0, 0)), 1, - DateTime(Date(1999, 7, 6), TimeOfDay(0, 0, 1))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(0, 0, 0)), 0, - DateTime(Date(1999, 7, 6), TimeOfDay(0, 0, 0))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(0, 0, 0)), -1, - DateTime(Date(1999, 7, 6), TimeOfDay(0, 0, 59))); - - testDT(DateTime(Date(1999, 7, 5), TimeOfDay(23, 59, 59)), 1, - DateTime(Date(1999, 7, 5), TimeOfDay(23, 59, 0))); - testDT(DateTime(Date(1999, 7, 5), TimeOfDay(23, 59, 59)), 0, - DateTime(Date(1999, 7, 5), TimeOfDay(23, 59, 59))); - testDT(DateTime(Date(1999, 7, 5), TimeOfDay(23, 59, 59)), -1, - DateTime(Date(1999, 7, 5), TimeOfDay(23, 59, 58))); - - testDT(DateTime(Date(1998, 12, 31), TimeOfDay(23, 59, 59)), 1, - DateTime(Date(1998, 12, 31), TimeOfDay(23, 59, 0))); - testDT(DateTime(Date(1998, 12, 31), TimeOfDay(23, 59, 59)), 0, - DateTime(Date(1998, 12, 31), TimeOfDay(23, 59, 59))); - testDT(DateTime(Date(1998, 12, 31), TimeOfDay(23, 59, 59)), -1, - DateTime(Date(1998, 12, 31), TimeOfDay(23, 59, 58))); - - // Test B.C. - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 0, - DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 1, - DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 34))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 2, - DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 35))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 3, - DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 36))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 4, - DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 37))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 5, - DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 38))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 10, - DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 43))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 15, - DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 48))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 26, - DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 59))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 27, - DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 0))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 30, - DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 3))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 59, - DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 32))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 60, - DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 61, - DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 34))); - - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 1766, - DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 59))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 1767, - DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 0))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 1768, - DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 1))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 2007, - DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 0))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 3599, - DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 32))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 3600, - DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 3601, - DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 34))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 7200, - DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33))); - - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -1, - DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 32))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -2, - DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 31))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -3, - DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 30))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -4, - DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 29))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -5, - DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 28))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -10, - DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 23))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -15, - DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 18))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -33, - DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 0))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -34, - DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 59))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -35, - DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 58))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -59, - DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 34))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -60, - DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -61, - DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 32))); - - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 0)), 1, - DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 1))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 0)), 0, - DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 0))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 0)), -1, - DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 59))); - - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 0, 0)), 1, - DateTime(Date(-1999, 7, 6), TimeOfDay(12, 0, 1))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 0, 0)), 0, - DateTime(Date(-1999, 7, 6), TimeOfDay(12, 0, 0))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 0, 0)), -1, - DateTime(Date(-1999, 7, 6), TimeOfDay(12, 0, 59))); - - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(0, 0, 0)), 1, - DateTime(Date(-1999, 7, 6), TimeOfDay(0, 0, 1))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(0, 0, 0)), 0, - DateTime(Date(-1999, 7, 6), TimeOfDay(0, 0, 0))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(0, 0, 0)), -1, - DateTime(Date(-1999, 7, 6), TimeOfDay(0, 0, 59))); - - testDT(DateTime(Date(-1999, 7, 5), TimeOfDay(23, 59, 59)), 1, - DateTime(Date(-1999, 7, 5), TimeOfDay(23, 59, 0))); - testDT(DateTime(Date(-1999, 7, 5), TimeOfDay(23, 59, 59)), 0, - DateTime(Date(-1999, 7, 5), TimeOfDay(23, 59, 59))); - testDT(DateTime(Date(-1999, 7, 5), TimeOfDay(23, 59, 59)), -1, - DateTime(Date(-1999, 7, 5), TimeOfDay(23, 59, 58))); - - testDT(DateTime(Date(-2000, 12, 31), TimeOfDay(23, 59, 59)), 1, - DateTime(Date(-2000, 12, 31), TimeOfDay(23, 59, 0))); - testDT(DateTime(Date(-2000, 12, 31), TimeOfDay(23, 59, 59)), 0, - DateTime(Date(-2000, 12, 31), TimeOfDay(23, 59, 59))); - testDT(DateTime(Date(-2000, 12, 31), TimeOfDay(23, 59, 59)), -1, - DateTime(Date(-2000, 12, 31), TimeOfDay(23, 59, 58))); - - // Test Both - testDT(DateTime(Date(1, 1, 1), TimeOfDay(0, 0, 0)), -1, - DateTime(Date(1, 1, 1), TimeOfDay(0, 0, 59))); - testDT(DateTime(Date(0, 12, 31), TimeOfDay(23, 59, 59)), 1, - DateTime(Date(0, 12, 31), TimeOfDay(23, 59, 0))); - - testDT(DateTime(Date(0, 1, 1), TimeOfDay(0, 0, 0)), -1, - DateTime(Date(0, 1, 1), TimeOfDay(0, 0, 59))); - testDT(DateTime(Date(-1, 12, 31), TimeOfDay(23, 59, 59)), 1, - DateTime(Date(-1, 12, 31), TimeOfDay(23, 59, 0))); - - testDT(DateTime(Date(-1, 1, 1), TimeOfDay(11, 30, 33)), 63_165_600L, - DateTime(Date(-1, 1, 1), TimeOfDay(11, 30, 33))); - testDT(DateTime(Date(1, 1, 1), TimeOfDay(13, 30, 33)), -63_165_600L, - DateTime(Date(1, 1, 1), TimeOfDay(13, 30, 33))); - - testDT(DateTime(Date(-1, 1, 1), TimeOfDay(11, 30, 33)), 63_165_617L, - DateTime(Date(-1, 1, 1), TimeOfDay(11, 30, 50))); - testDT(DateTime(Date(1, 1, 1), TimeOfDay(13, 30, 50)), -63_165_617L, - DateTime(Date(1, 1, 1), TimeOfDay(13, 30, 33))); - - auto dt = DateTime(2000, 1, 31, 9, 7, 6); - dt.roll!"seconds"(92).roll!"seconds"(-292); - assert(dt == DateTime(2000, 1, 31, 9, 7, 46)); - - const cdt = DateTime(1999, 7, 6, 12, 30, 33); - immutable idt = DateTime(1999, 7, 6, 12, 30, 33); - static assert(!__traits(compiles, cdt.roll!"seconds"(4))); - static assert(!__traits(compiles, idt.roll!"seconds"(4))); - } - - - import core.time : Duration; - /++ - Gives the result of adding or subtracting a $(REF Duration, core,time) - from this $(LREF DateTime). - - The legal types of arithmetic for $(LREF DateTime) using this operator - are - - $(BOOKTABLE, - $(TR $(TD DateTime) $(TD +) $(TD Duration) $(TD -->) $(TD DateTime)) - $(TR $(TD DateTime) $(TD -) $(TD Duration) $(TD -->) $(TD DateTime)) - ) - - Params: - duration = The $(REF Duration, core,time) to add to or subtract from - this $(LREF DateTime). - +/ - DateTime opBinary(string op)(Duration duration) const @safe pure nothrow @nogc - if (op == "+" || op == "-") - { - DateTime retval = this; - immutable seconds = duration.total!"seconds"; - mixin("return retval._addSeconds(" ~ op ~ "seconds);"); - } - - /// - @safe unittest - { - import core.time : hours, seconds; - - assert(DateTime(2015, 12, 31, 23, 59, 59) + seconds(1) == - DateTime(2016, 1, 1, 0, 0, 0)); - - assert(DateTime(2015, 12, 31, 23, 59, 59) + hours(1) == - DateTime(2016, 1, 1, 0, 59, 59)); - - assert(DateTime(2016, 1, 1, 0, 0, 0) - seconds(1) == - DateTime(2015, 12, 31, 23, 59, 59)); - - assert(DateTime(2016, 1, 1, 0, 59, 59) - hours(1) == - DateTime(2015, 12, 31, 23, 59, 59)); - } - - @safe unittest - { - import core.time : dur; - - auto dt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)); - - assert(dt + dur!"weeks"(7) == DateTime(Date(1999, 8, 24), TimeOfDay(12, 30, 33))); - assert(dt + dur!"weeks"(-7) == DateTime(Date(1999, 5, 18), TimeOfDay(12, 30, 33))); - assert(dt + dur!"days"(7) == DateTime(Date(1999, 7, 13), TimeOfDay(12, 30, 33))); - assert(dt + dur!"days"(-7) == DateTime(Date(1999, 6, 29), TimeOfDay(12, 30, 33))); - - assert(dt + dur!"hours"(7) == DateTime(Date(1999, 7, 6), TimeOfDay(19, 30, 33))); - assert(dt + dur!"hours"(-7) == DateTime(Date(1999, 7, 6), TimeOfDay(5, 30, 33))); - assert(dt + dur!"minutes"(7) == DateTime(Date(1999, 7, 6), TimeOfDay(12, 37, 33))); - assert(dt + dur!"minutes"(-7) == DateTime(Date(1999, 7, 6), TimeOfDay(12, 23, 33))); - assert(dt + dur!"seconds"(7) == DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 40))); - assert(dt + dur!"seconds"(-7) == DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 26))); - assert(dt + dur!"msecs"(7_000) == DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 40))); - assert(dt + dur!"msecs"(-7_000) == DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 26))); - assert(dt + dur!"usecs"(7_000_000) == DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 40))); - assert(dt + dur!"usecs"(-7_000_000) == DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 26))); - assert(dt + dur!"hnsecs"(70_000_000) == DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 40))); - assert(dt + dur!"hnsecs"(-70_000_000) == DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 26))); - - assert(dt - dur!"weeks"(-7) == DateTime(Date(1999, 8, 24), TimeOfDay(12, 30, 33))); - assert(dt - dur!"weeks"(7) == DateTime(Date(1999, 5, 18), TimeOfDay(12, 30, 33))); - assert(dt - dur!"days"(-7) == DateTime(Date(1999, 7, 13), TimeOfDay(12, 30, 33))); - assert(dt - dur!"days"(7) == DateTime(Date(1999, 6, 29), TimeOfDay(12, 30, 33))); - - assert(dt - dur!"hours"(-7) == DateTime(Date(1999, 7, 6), TimeOfDay(19, 30, 33))); - assert(dt - dur!"hours"(7) == DateTime(Date(1999, 7, 6), TimeOfDay(5, 30, 33))); - assert(dt - dur!"minutes"(-7) == DateTime(Date(1999, 7, 6), TimeOfDay(12, 37, 33))); - assert(dt - dur!"minutes"(7) == DateTime(Date(1999, 7, 6), TimeOfDay(12, 23, 33))); - assert(dt - dur!"seconds"(-7) == DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 40))); - assert(dt - dur!"seconds"(7) == DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 26))); - assert(dt - dur!"msecs"(-7_000) == DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 40))); - assert(dt - dur!"msecs"(7_000) == DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 26))); - assert(dt - dur!"usecs"(-7_000_000) == DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 40))); - assert(dt - dur!"usecs"(7_000_000) == DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 26))); - assert(dt - dur!"hnsecs"(-70_000_000) == DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 40))); - assert(dt - dur!"hnsecs"(70_000_000) == DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 26))); - - auto duration = dur!"seconds"(12); - const cdt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)); - immutable idt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)); - assert(cdt + duration == DateTime(1999, 7, 6, 12, 30, 45)); - assert(idt + duration == DateTime(1999, 7, 6, 12, 30, 45)); - assert(cdt - duration == DateTime(1999, 7, 6, 12, 30, 21)); - assert(idt - duration == DateTime(1999, 7, 6, 12, 30, 21)); - } - - - /++ - Gives the result of adding or subtracting a duration from this - $(LREF DateTime), as well as assigning the result to this - $(LREF DateTime). - - The legal types of arithmetic for $(LREF DateTime) using this operator - are - - $(BOOKTABLE, - $(TR $(TD DateTime) $(TD +) $(TD duration) $(TD -->) $(TD DateTime)) - $(TR $(TD DateTime) $(TD -) $(TD duration) $(TD -->) $(TD DateTime)) - ) - - Params: - duration = The duration to add to or subtract from this - $(LREF DateTime). - +/ - ref DateTime opOpAssign(string op)(Duration duration) @safe pure nothrow @nogc - if (op == "+" || op == "-") - { - import core.time : convert; - import std.format : format; - - DateTime retval = this; - immutable hnsecs = duration.total!"hnsecs"; - - mixin(format(`return _addSeconds(convert!("hnsecs", "seconds")(%shnsecs));`, op)); - } - - @safe unittest - { - import core.time : dur; - assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)) + dur!"weeks"(7) == - DateTime(Date(1999, 8, 24), TimeOfDay(12, 30, 33))); - assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)) + dur!"weeks"(-7) == - DateTime(Date(1999, 5, 18), TimeOfDay(12, 30, 33))); - assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)) + dur!"days"(7) == - DateTime(Date(1999, 7, 13), TimeOfDay(12, 30, 33))); - assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)) + dur!"days"(-7) == - DateTime(Date(1999, 6, 29), TimeOfDay(12, 30, 33))); - - assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)) + dur!"hours"(7) == - DateTime(Date(1999, 7, 6), TimeOfDay(19, 30, 33))); - assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)) + dur!"hours"(-7) == - DateTime(Date(1999, 7, 6), TimeOfDay(5, 30, 33))); - assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)) + dur!"minutes"(7) == - DateTime(Date(1999, 7, 6), TimeOfDay(12, 37, 33))); - assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)) + dur!"minutes"(-7) == - DateTime(Date(1999, 7, 6), TimeOfDay(12, 23, 33))); - assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)) + dur!"seconds"(7) == - DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 40))); - assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)) + dur!"seconds"(-7) == - DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 26))); - assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)) + dur!"msecs"(7_000) == - DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 40))); - assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)) + dur!"msecs"(-7_000) == - DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 26))); - assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)) + dur!"usecs"(7_000_000) == - DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 40))); - assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)) + dur!"usecs"(-7_000_000) == - DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 26))); - assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)) + dur!"hnsecs"(70_000_000) == - DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 40))); - assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)) + dur!"hnsecs"(-70_000_000) == - DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 26))); - - assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)) - dur!"weeks"(-7) == - DateTime(Date(1999, 8, 24), TimeOfDay(12, 30, 33))); - assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)) - dur!"weeks"(7) == - DateTime(Date(1999, 5, 18), TimeOfDay(12, 30, 33))); - assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)) - dur!"days"(-7) == - DateTime(Date(1999, 7, 13), TimeOfDay(12, 30, 33))); - assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)) - dur!"days"(7) == - DateTime(Date(1999, 6, 29), TimeOfDay(12, 30, 33))); - - assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)) - dur!"hours"(-7) == - DateTime(Date(1999, 7, 6), TimeOfDay(19, 30, 33))); - assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)) - dur!"hours"(7) == - DateTime(Date(1999, 7, 6), TimeOfDay(5, 30, 33))); - assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)) - dur!"minutes"(-7) == - DateTime(Date(1999, 7, 6), TimeOfDay(12, 37, 33))); - assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)) - dur!"minutes"(7) == - DateTime(Date(1999, 7, 6), TimeOfDay(12, 23, 33))); - assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)) - dur!"seconds"(-7) == - DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 40))); - assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)) - dur!"seconds"(7) == - DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 26))); - assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)) - dur!"msecs"(-7_000) == - DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 40))); - assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)) - dur!"msecs"(7_000) == - DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 26))); - assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)) - dur!"usecs"(-7_000_000) == - DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 40))); - assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)) - dur!"usecs"(7_000_000) == - DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 26))); - assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)) - dur!"hnsecs"(-70_000_000) == - DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 40))); - assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)) - dur!"hnsecs"(70_000_000) == - DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 26))); - - auto dt = DateTime(2000, 1, 31, 9, 7, 6); - (dt += dur!"seconds"(92)) -= dur!"days"(-500); - assert(dt == DateTime(2001, 6, 14, 9, 8, 38)); - - auto duration = dur!"seconds"(12); - const cdt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)); - immutable idt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)); - static assert(!__traits(compiles, cdt += duration)); - static assert(!__traits(compiles, idt += duration)); - static assert(!__traits(compiles, cdt -= duration)); - static assert(!__traits(compiles, idt -= duration)); - } - - - /++ - Gives the difference between two $(LREF DateTime)s. - - The legal types of arithmetic for $(LREF DateTime) using this operator are - - $(BOOKTABLE, - $(TR $(TD DateTime) $(TD -) $(TD DateTime) $(TD -->) $(TD duration)) - ) - +/ - Duration opBinary(string op)(DateTime rhs) const @safe pure nothrow @nogc - if (op == "-") - { - immutable dateResult = _date - rhs.date; - immutable todResult = _tod - rhs._tod; - - import core.time : dur; - return dur!"hnsecs"(dateResult.total!"hnsecs" + todResult.total!"hnsecs"); - } - - @safe unittest - { - auto dt = DateTime(1999, 7, 6, 12, 30, 33); - - import core.time : dur; - assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)) - DateTime(Date(1998, 7, 6), TimeOfDay(12, 30, 33)) == - dur!"seconds"(31_536_000)); - assert(DateTime(Date(1998, 7, 6), TimeOfDay(12, 30, 33)) - DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)) == - dur!"seconds"(-31_536_000)); - - assert(DateTime(Date(1999, 8, 6), TimeOfDay(12, 30, 33)) - DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)) == - dur!"seconds"(26_78_400)); - assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)) - DateTime(Date(1999, 8, 6), TimeOfDay(12, 30, 33)) == - dur!"seconds"(-26_78_400)); - - assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)) - DateTime(Date(1999, 7, 5), TimeOfDay(12, 30, 33)) == - dur!"seconds"(86_400)); - assert(DateTime(Date(1999, 7, 5), TimeOfDay(12, 30, 33)) - DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)) == - dur!"seconds"(-86_400)); - - assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)) - DateTime(Date(1999, 7, 6), TimeOfDay(11, 30, 33)) == - dur!"seconds"(3600)); - assert(DateTime(Date(1999, 7, 6), TimeOfDay(11, 30, 33)) - DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)) == - dur!"seconds"(-3600)); - - assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 31, 33)) - DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)) == - dur!"seconds"(60)); - assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)) - DateTime(Date(1999, 7, 6), TimeOfDay(12, 31, 33)) == - dur!"seconds"(-60)); - - assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 34)) - DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)) == - dur!"seconds"(1)); - assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)) - DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 34)) == - dur!"seconds"(-1)); - - assert(DateTime(1, 1, 1, 12, 30, 33) - DateTime(1, 1, 1, 0, 0, 0) == dur!"seconds"(45033)); - assert(DateTime(1, 1, 1, 0, 0, 0) - DateTime(1, 1, 1, 12, 30, 33) == dur!"seconds"(-45033)); - assert(DateTime(0, 12, 31, 12, 30, 33) - DateTime(1, 1, 1, 0, 0, 0) == dur!"seconds"(-41367)); - assert(DateTime(1, 1, 1, 0, 0, 0) - DateTime(0, 12, 31, 12, 30, 33) == dur!"seconds"(41367)); - - const cdt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)); - immutable idt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)); - assert(dt - dt == Duration.zero); - assert(cdt - dt == Duration.zero); - assert(idt - dt == Duration.zero); - - assert(dt - cdt == Duration.zero); - assert(cdt - cdt == Duration.zero); - assert(idt - cdt == Duration.zero); - - assert(dt - idt == Duration.zero); - assert(cdt - idt == Duration.zero); - assert(idt - idt == Duration.zero); - } - - - /++ - Returns the difference between the two $(LREF DateTime)s in months. - - To get the difference in years, subtract the year property - of two $(LREF DateTime)s. To get the difference in days or weeks, - subtract the $(LREF DateTime)s themselves and use the - $(REF Duration, core,time) that results. Because converting between - months and smaller units requires a specific date (which - $(REF Duration, core,time)s don't have), getting the difference in - months requires some math using both the year and month properties, so - this is a convenience function for getting the difference in months. - - Note that the number of days in the months or how far into the month - either date is is irrelevant. It is the difference in the month property - combined with the difference in years * 12. So, for instance, - December 31st and January 1st are one month apart just as December 1st - and January 31st are one month apart. - - Params: - rhs = The $(LREF DateTime) to subtract from this one. - +/ - int diffMonths(DateTime rhs) const @safe pure nothrow @nogc - { - return _date.diffMonths(rhs._date); - } - - /// - @safe unittest - { - assert(DateTime(1999, 2, 1, 12, 2, 3).diffMonths( - DateTime(1999, 1, 31, 23, 59, 59)) == 1); - - assert(DateTime(1999, 1, 31, 0, 0, 0).diffMonths( - DateTime(1999, 2, 1, 12, 3, 42)) == -1); - - assert(DateTime(1999, 3, 1, 5, 30, 0).diffMonths( - DateTime(1999, 1, 1, 2, 4, 7)) == 2); - - assert(DateTime(1999, 1, 1, 7, 2, 4).diffMonths( - DateTime(1999, 3, 31, 0, 30, 58)) == -2); - } - - @safe unittest - { - auto dt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)); - const cdt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)); - immutable idt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)); - assert(dt.diffMonths(dt) == 0); - assert(cdt.diffMonths(dt) == 0); - assert(idt.diffMonths(dt) == 0); - - assert(dt.diffMonths(cdt) == 0); - assert(cdt.diffMonths(cdt) == 0); - assert(idt.diffMonths(cdt) == 0); - - assert(dt.diffMonths(idt) == 0); - assert(cdt.diffMonths(idt) == 0); - assert(idt.diffMonths(idt) == 0); - } - - - /++ - Whether this $(LREF DateTime) is in a leap year. - +/ - @property bool isLeapYear() const @safe pure nothrow @nogc - { - return _date.isLeapYear; - } - - @safe unittest - { - auto dt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)); - const cdt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)); - immutable idt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)); - assert(!dt.isLeapYear); - assert(!cdt.isLeapYear); - assert(!idt.isLeapYear); - } - - - /++ - Day of the week this $(LREF DateTime) is on. - +/ - @property DayOfWeek dayOfWeek() const @safe pure nothrow @nogc - { - return _date.dayOfWeek; - } - - @safe unittest - { - auto dt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)); - const cdt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)); - immutable idt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)); - assert(dt.dayOfWeek == DayOfWeek.tue); - assert(cdt.dayOfWeek == DayOfWeek.tue); - assert(idt.dayOfWeek == DayOfWeek.tue); - } - - - /++ - Day of the year this $(LREF DateTime) is on. - +/ - @property ushort dayOfYear() const @safe pure nothrow @nogc - { - return _date.dayOfYear; - } - - /// - @safe unittest - { - assert(DateTime(Date(1999, 1, 1), TimeOfDay(12, 22, 7)).dayOfYear == 1); - assert(DateTime(Date(1999, 12, 31), TimeOfDay(7, 2, 59)).dayOfYear == 365); - assert(DateTime(Date(2000, 12, 31), TimeOfDay(21, 20, 0)).dayOfYear == 366); - } - - @safe unittest - { - auto dt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)); - const cdt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)); - immutable idt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)); - assert(dt.dayOfYear == 187); - assert(cdt.dayOfYear == 187); - assert(idt.dayOfYear == 187); - } - - - /++ - Day of the year. - - Params: - day = The day of the year to set which day of the year this - $(LREF DateTime) is on. - +/ - @property void dayOfYear(int day) @safe pure - { - _date.dayOfYear = day; - } - - @safe unittest - { - auto dt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)); - const cdt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)); - immutable idt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)); - dt.dayOfYear = 12; - assert(dt.dayOfYear == 12); - static assert(!__traits(compiles, cdt.dayOfYear = 12)); - static assert(!__traits(compiles, idt.dayOfYear = 12)); - } - - - /++ - The Xth day of the Gregorian Calendar that this $(LREF DateTime) is on. - +/ - @property int dayOfGregorianCal() const @safe pure nothrow @nogc - { - return _date.dayOfGregorianCal; - } - - /// - @safe unittest - { - assert(DateTime(Date(1, 1, 1), TimeOfDay(0, 0, 0)).dayOfGregorianCal == 1); - assert(DateTime(Date(1, 12, 31), TimeOfDay(23, 59, 59)).dayOfGregorianCal == 365); - assert(DateTime(Date(2, 1, 1), TimeOfDay(2, 2, 2)).dayOfGregorianCal == 366); - - assert(DateTime(Date(0, 12, 31), TimeOfDay(7, 7, 7)).dayOfGregorianCal == 0); - assert(DateTime(Date(0, 1, 1), TimeOfDay(19, 30, 0)).dayOfGregorianCal == -365); - assert(DateTime(Date(-1, 12, 31), TimeOfDay(4, 7, 0)).dayOfGregorianCal == -366); - - assert(DateTime(Date(2000, 1, 1), TimeOfDay(9, 30, 20)).dayOfGregorianCal == 730_120); - assert(DateTime(Date(2010, 12, 31), TimeOfDay(15, 45, 50)).dayOfGregorianCal == 734_137); - } - - @safe unittest - { - const cdt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)); - immutable idt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)); - assert(cdt.dayOfGregorianCal == 729_941); - assert(idt.dayOfGregorianCal == 729_941); - } - - - /++ - The Xth day of the Gregorian Calendar that this $(LREF DateTime) is on. - Setting this property does not affect the time portion of - $(LREF DateTime). - - Params: - days = The day of the Gregorian Calendar to set this $(LREF DateTime) - to. - +/ - @property void dayOfGregorianCal(int days) @safe pure nothrow @nogc - { - _date.dayOfGregorianCal = days; - } - - /// - @safe unittest - { - auto dt = DateTime(Date.init, TimeOfDay(12, 0, 0)); - dt.dayOfGregorianCal = 1; - assert(dt == DateTime(Date(1, 1, 1), TimeOfDay(12, 0, 0))); - - dt.dayOfGregorianCal = 365; - assert(dt == DateTime(Date(1, 12, 31), TimeOfDay(12, 0, 0))); - - dt.dayOfGregorianCal = 366; - assert(dt == DateTime(Date(2, 1, 1), TimeOfDay(12, 0, 0))); - - dt.dayOfGregorianCal = 0; - assert(dt == DateTime(Date(0, 12, 31), TimeOfDay(12, 0, 0))); - - dt.dayOfGregorianCal = -365; - assert(dt == DateTime(Date(-0, 1, 1), TimeOfDay(12, 0, 0))); - - dt.dayOfGregorianCal = -366; - assert(dt == DateTime(Date(-1, 12, 31), TimeOfDay(12, 0, 0))); - - dt.dayOfGregorianCal = 730_120; - assert(dt == DateTime(Date(2000, 1, 1), TimeOfDay(12, 0, 0))); - - dt.dayOfGregorianCal = 734_137; - assert(dt == DateTime(Date(2010, 12, 31), TimeOfDay(12, 0, 0))); - } - - @safe unittest - { - const cdt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)); - immutable idt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)); - static assert(!__traits(compiles, cdt.dayOfGregorianCal = 7)); - static assert(!__traits(compiles, idt.dayOfGregorianCal = 7)); - } - - - /++ - The ISO 8601 week of the year that this $(LREF DateTime) is in. - - See_Also: - $(HTTP en.wikipedia.org/wiki/ISO_week_date, ISO Week Date) - +/ - @property ubyte isoWeek() const @safe pure nothrow - { - return _date.isoWeek; - } - - @safe unittest - { - auto dt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)); - const cdt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)); - immutable idt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)); - assert(dt.isoWeek == 27); - assert(cdt.isoWeek == 27); - assert(idt.isoWeek == 27); - } - - - /++ - The year of the ISO 8601 week calendar that this $(LREF DateTime) is in. - - See_Also: - $(HTTP en.wikipedia.org/wiki/ISO_week_date, ISO Week Date) - +/ - @property short isoWeekYear() const @safe pure nothrow - { - return _date.isoWeekYear; - } - - - /++ - $(LREF DateTime) for the last day in the month that this - $(LREF DateTime) is in. The time portion of endOfMonth is always - 23:59:59. - +/ - @property DateTime endOfMonth() const @safe pure nothrow - { - try - return DateTime(_date.endOfMonth, TimeOfDay(23, 59, 59)); - catch (Exception e) - assert(0, "DateTime constructor threw."); - } - - /// - @safe unittest - { - assert(DateTime(Date(1999, 1, 6), TimeOfDay(0, 0, 0)).endOfMonth == - DateTime(Date(1999, 1, 31), TimeOfDay(23, 59, 59))); - - assert(DateTime(Date(1999, 2, 7), TimeOfDay(19, 30, 0)).endOfMonth == - DateTime(Date(1999, 2, 28), TimeOfDay(23, 59, 59))); - - assert(DateTime(Date(2000, 2, 7), TimeOfDay(5, 12, 27)).endOfMonth == - DateTime(Date(2000, 2, 29), TimeOfDay(23, 59, 59))); - - assert(DateTime(Date(2000, 6, 4), TimeOfDay(12, 22, 9)).endOfMonth == - DateTime(Date(2000, 6, 30), TimeOfDay(23, 59, 59))); - } - - @safe unittest - { - // Test A.D. - assert(DateTime(1999, 1, 1, 0, 13, 26).endOfMonth == DateTime(1999, 1, 31, 23, 59, 59)); - assert(DateTime(1999, 2, 1, 1, 14, 27).endOfMonth == DateTime(1999, 2, 28, 23, 59, 59)); - assert(DateTime(2000, 2, 1, 2, 15, 28).endOfMonth == DateTime(2000, 2, 29, 23, 59, 59)); - assert(DateTime(1999, 3, 1, 3, 16, 29).endOfMonth == DateTime(1999, 3, 31, 23, 59, 59)); - assert(DateTime(1999, 4, 1, 4, 17, 30).endOfMonth == DateTime(1999, 4, 30, 23, 59, 59)); - assert(DateTime(1999, 5, 1, 5, 18, 31).endOfMonth == DateTime(1999, 5, 31, 23, 59, 59)); - assert(DateTime(1999, 6, 1, 6, 19, 32).endOfMonth == DateTime(1999, 6, 30, 23, 59, 59)); - assert(DateTime(1999, 7, 1, 7, 20, 33).endOfMonth == DateTime(1999, 7, 31, 23, 59, 59)); - assert(DateTime(1999, 8, 1, 8, 21, 34).endOfMonth == DateTime(1999, 8, 31, 23, 59, 59)); - assert(DateTime(1999, 9, 1, 9, 22, 35).endOfMonth == DateTime(1999, 9, 30, 23, 59, 59)); - assert(DateTime(1999, 10, 1, 10, 23, 36).endOfMonth == DateTime(1999, 10, 31, 23, 59, 59)); - assert(DateTime(1999, 11, 1, 11, 24, 37).endOfMonth == DateTime(1999, 11, 30, 23, 59, 59)); - assert(DateTime(1999, 12, 1, 12, 25, 38).endOfMonth == DateTime(1999, 12, 31, 23, 59, 59)); - - // Test B.C. - assert(DateTime(-1999, 1, 1, 0, 13, 26).endOfMonth == DateTime(-1999, 1, 31, 23, 59, 59)); - assert(DateTime(-1999, 2, 1, 1, 14, 27).endOfMonth == DateTime(-1999, 2, 28, 23, 59, 59)); - assert(DateTime(-2000, 2, 1, 2, 15, 28).endOfMonth == DateTime(-2000, 2, 29, 23, 59, 59)); - assert(DateTime(-1999, 3, 1, 3, 16, 29).endOfMonth == DateTime(-1999, 3, 31, 23, 59, 59)); - assert(DateTime(-1999, 4, 1, 4, 17, 30).endOfMonth == DateTime(-1999, 4, 30, 23, 59, 59)); - assert(DateTime(-1999, 5, 1, 5, 18, 31).endOfMonth == DateTime(-1999, 5, 31, 23, 59, 59)); - assert(DateTime(-1999, 6, 1, 6, 19, 32).endOfMonth == DateTime(-1999, 6, 30, 23, 59, 59)); - assert(DateTime(-1999, 7, 1, 7, 20, 33).endOfMonth == DateTime(-1999, 7, 31, 23, 59, 59)); - assert(DateTime(-1999, 8, 1, 8, 21, 34).endOfMonth == DateTime(-1999, 8, 31, 23, 59, 59)); - assert(DateTime(-1999, 9, 1, 9, 22, 35).endOfMonth == DateTime(-1999, 9, 30, 23, 59, 59)); - assert(DateTime(-1999, 10, 1, 10, 23, 36).endOfMonth == DateTime(-1999, 10, 31, 23, 59, 59)); - assert(DateTime(-1999, 11, 1, 11, 24, 37).endOfMonth == DateTime(-1999, 11, 30, 23, 59, 59)); - assert(DateTime(-1999, 12, 1, 12, 25, 38).endOfMonth == DateTime(-1999, 12, 31, 23, 59, 59)); - - const cdt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)); - immutable idt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)); - assert(cdt.endOfMonth == DateTime(1999, 7, 31, 23, 59, 59)); - assert(idt.endOfMonth == DateTime(1999, 7, 31, 23, 59, 59)); - } - - - /++ - The last day in the month that this $(LREF DateTime) is in. - +/ - @property ubyte daysInMonth() const @safe pure nothrow @nogc - { - return _date.daysInMonth; - } - - /// - @safe unittest - { - assert(DateTime(Date(1999, 1, 6), TimeOfDay(0, 0, 0)).daysInMonth == 31); - assert(DateTime(Date(1999, 2, 7), TimeOfDay(19, 30, 0)).daysInMonth == 28); - assert(DateTime(Date(2000, 2, 7), TimeOfDay(5, 12, 27)).daysInMonth == 29); - assert(DateTime(Date(2000, 6, 4), TimeOfDay(12, 22, 9)).daysInMonth == 30); - } - - @safe unittest - { - const cdt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)); - immutable idt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)); - assert(cdt.daysInMonth == 31); - assert(idt.daysInMonth == 31); - } - - - /++ - Whether the current year is a date in A.D. - +/ - @property bool isAD() const @safe pure nothrow @nogc - { - return _date.isAD; - } - - /// - @safe unittest - { - assert(DateTime(Date(1, 1, 1), TimeOfDay(12, 7, 0)).isAD); - assert(DateTime(Date(2010, 12, 31), TimeOfDay(0, 0, 0)).isAD); - assert(!DateTime(Date(0, 12, 31), TimeOfDay(23, 59, 59)).isAD); - assert(!DateTime(Date(-2010, 1, 1), TimeOfDay(2, 2, 2)).isAD); - } - - @safe unittest - { - const cdt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)); - immutable idt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)); - assert(cdt.isAD); - assert(idt.isAD); - } - - - /++ - The $(HTTP en.wikipedia.org/wiki/Julian_day, Julian day) for this - $(LREF DateTime) at the given time. For example, prior to noon, - 1996-03-31 would be the Julian day number 2_450_173, so this function - returns 2_450_173, while from noon onward, the julian day number would - be 2_450_174, so this function returns 2_450_174. - +/ - @property long julianDay() const @safe pure nothrow @nogc - { - if (_tod._hour < 12) - return _date.julianDay - 1; - else - return _date.julianDay; - } - - @safe unittest - { - assert(DateTime(Date(-4713, 11, 24), TimeOfDay(0, 0, 0)).julianDay == -1); - assert(DateTime(Date(-4713, 11, 24), TimeOfDay(12, 0, 0)).julianDay == 0); - - assert(DateTime(Date(0, 12, 31), TimeOfDay(0, 0, 0)).julianDay == 1_721_424); - assert(DateTime(Date(0, 12, 31), TimeOfDay(12, 0, 0)).julianDay == 1_721_425); - - assert(DateTime(Date(1, 1, 1), TimeOfDay(0, 0, 0)).julianDay == 1_721_425); - assert(DateTime(Date(1, 1, 1), TimeOfDay(12, 0, 0)).julianDay == 1_721_426); - - assert(DateTime(Date(1582, 10, 15), TimeOfDay(0, 0, 0)).julianDay == 2_299_160); - assert(DateTime(Date(1582, 10, 15), TimeOfDay(12, 0, 0)).julianDay == 2_299_161); - - assert(DateTime(Date(1858, 11, 17), TimeOfDay(0, 0, 0)).julianDay == 2_400_000); - assert(DateTime(Date(1858, 11, 17), TimeOfDay(12, 0, 0)).julianDay == 2_400_001); - - assert(DateTime(Date(1982, 1, 4), TimeOfDay(0, 0, 0)).julianDay == 2_444_973); - assert(DateTime(Date(1982, 1, 4), TimeOfDay(12, 0, 0)).julianDay == 2_444_974); - - assert(DateTime(Date(1996, 3, 31), TimeOfDay(0, 0, 0)).julianDay == 2_450_173); - assert(DateTime(Date(1996, 3, 31), TimeOfDay(12, 0, 0)).julianDay == 2_450_174); - - assert(DateTime(Date(2010, 8, 24), TimeOfDay(0, 0, 0)).julianDay == 2_455_432); - assert(DateTime(Date(2010, 8, 24), TimeOfDay(12, 0, 0)).julianDay == 2_455_433); - - const cdt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)); - immutable idt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)); - assert(cdt.julianDay == 2_451_366); - assert(idt.julianDay == 2_451_366); - } - - - /++ - The modified $(HTTP en.wikipedia.org/wiki/Julian_day, Julian day) for any - time on this date (since, the modified Julian day changes at midnight). - +/ - @property long modJulianDay() const @safe pure nothrow @nogc - { - return _date.modJulianDay; - } - - @safe unittest - { - assert(DateTime(Date(1858, 11, 17), TimeOfDay(0, 0, 0)).modJulianDay == 0); - assert(DateTime(Date(1858, 11, 17), TimeOfDay(12, 0, 0)).modJulianDay == 0); - - assert(DateTime(Date(2010, 8, 24), TimeOfDay(0, 0, 0)).modJulianDay == 55_432); - assert(DateTime(Date(2010, 8, 24), TimeOfDay(12, 0, 0)).modJulianDay == 55_432); - - const cdt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)); - immutable idt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)); - assert(cdt.modJulianDay == 51_365); - assert(idt.modJulianDay == 51_365); - } - - - /++ - Converts this $(LREF DateTime) to a string with the format `YYYYMMDDTHHMMSS`. - If `writer` is set, the resulting string will be written directly to it. - - Params: - writer = A `char` accepting - $(REF_ALTTEXT output range, isOutputRange, std, range, primitives) - Returns: - A `string` when not using an output range; `void` otherwise. - +/ - string toISOString() const @safe pure nothrow - { - import std.array : appender; - auto w = appender!string(); - w.reserve(18); - try - toISOString(w); - catch (Exception e) - assert(0, "toISOString() threw."); - return w.data; - } - - /// ditto - void toISOString(W)(ref W writer) const - if (isOutputRange!(W, char)) - { - import std.format.write : formattedWrite; - _date.toISOString(writer); - formattedWrite!("T%02d%02d%02d")( - writer, - _tod._hour, - _tod._minute, - _tod._second - ); - } - - /// - @safe unittest - { - assert(DateTime(Date(2010, 7, 4), TimeOfDay(7, 6, 12)).toISOString() == - "20100704T070612"); - - assert(DateTime(Date(1998, 12, 25), TimeOfDay(2, 15, 0)).toISOString() == - "19981225T021500"); - - assert(DateTime(Date(0, 1, 5), TimeOfDay(23, 9, 59)).toISOString() == - "00000105T230959"); - - assert(DateTime(Date(-4, 1, 5), TimeOfDay(0, 0, 2)).toISOString() == - "-00040105T000002"); - } - - @safe unittest - { - // Test A.D. - assert(DateTime(Date(9, 12, 4), TimeOfDay(0, 0, 0)).toISOString() == "00091204T000000"); - assert(DateTime(Date(99, 12, 4), TimeOfDay(5, 6, 12)).toISOString() == "00991204T050612"); - assert(DateTime(Date(999, 12, 4), TimeOfDay(13, 44, 59)).toISOString() == "09991204T134459"); - assert(DateTime(Date(9999, 7, 4), TimeOfDay(23, 59, 59)).toISOString() == "99990704T235959"); - assert(DateTime(Date(10000, 10, 20), TimeOfDay(1, 1, 1)).toISOString() == "+100001020T010101"); - - // Test B.C. - assert(DateTime(Date(0, 12, 4), TimeOfDay(0, 12, 4)).toISOString() == "00001204T001204"); - assert(DateTime(Date(-9, 12, 4), TimeOfDay(0, 0, 0)).toISOString() == "-00091204T000000"); - assert(DateTime(Date(-99, 12, 4), TimeOfDay(5, 6, 12)).toISOString() == "-00991204T050612"); - assert(DateTime(Date(-999, 12, 4), TimeOfDay(13, 44, 59)).toISOString() == "-09991204T134459"); - assert(DateTime(Date(-9999, 7, 4), TimeOfDay(23, 59, 59)).toISOString() == "-99990704T235959"); - assert(DateTime(Date(-10000, 10, 20), TimeOfDay(1, 1, 1)).toISOString() == "-100001020T010101"); - - const cdt = DateTime(1999, 7, 6, 12, 30, 33); - immutable idt = DateTime(1999, 7, 6, 12, 30, 33); - assert(cdt.toISOString() == "19990706T123033"); - assert(idt.toISOString() == "19990706T123033"); - } - - - /++ - Converts this $(LREF DateTime) to a string with the format - `YYYY-MM-DDTHH:MM:SS`. If `writer` is set, the resulting - string will be written directly to it. - - Params: - writer = A `char` accepting - $(REF_ALTTEXT output range, isOutputRange, std, range, primitives) - Returns: - A `string` when not using an output range; `void` otherwise. - +/ - string toISOExtString() const @safe pure nothrow - { - import std.array : appender; - auto w = appender!string(); - w.reserve(20); - try - toISOExtString(w); - catch (Exception e) - assert(0, "toISOExtString() threw."); - return w.data; - } - - /// ditto - void toISOExtString(W)(ref W writer) const - if (isOutputRange!(W, char)) - { - import std.format.write : formattedWrite; - _date.toISOExtString(writer); - formattedWrite!("T%02d:%02d:%02d")( - writer, - _tod._hour, - _tod._minute, - _tod._second - ); - } - - /// - @safe unittest - { - assert(DateTime(Date(2010, 7, 4), TimeOfDay(7, 6, 12)).toISOExtString() == - "2010-07-04T07:06:12"); - - assert(DateTime(Date(1998, 12, 25), TimeOfDay(2, 15, 0)).toISOExtString() == - "1998-12-25T02:15:00"); - - assert(DateTime(Date(0, 1, 5), TimeOfDay(23, 9, 59)).toISOExtString() == - "0000-01-05T23:09:59"); - - assert(DateTime(Date(-4, 1, 5), TimeOfDay(0, 0, 2)).toISOExtString() == - "-0004-01-05T00:00:02"); - } - - @safe unittest - { - // Test A.D. - assert(DateTime(Date(9, 12, 4), TimeOfDay(0, 0, 0)).toISOExtString() == "0009-12-04T00:00:00"); - assert(DateTime(Date(99, 12, 4), TimeOfDay(5, 6, 12)).toISOExtString() == "0099-12-04T05:06:12"); - assert(DateTime(Date(999, 12, 4), TimeOfDay(13, 44, 59)).toISOExtString() == "0999-12-04T13:44:59"); - assert(DateTime(Date(9999, 7, 4), TimeOfDay(23, 59, 59)).toISOExtString() == "9999-07-04T23:59:59"); - assert(DateTime(Date(10000, 10, 20), TimeOfDay(1, 1, 1)).toISOExtString() == "+10000-10-20T01:01:01"); - - // Test B.C. - assert(DateTime(Date(0, 12, 4), TimeOfDay(0, 12, 4)).toISOExtString() == "0000-12-04T00:12:04"); - assert(DateTime(Date(-9, 12, 4), TimeOfDay(0, 0, 0)).toISOExtString() == "-0009-12-04T00:00:00"); - assert(DateTime(Date(-99, 12, 4), TimeOfDay(5, 6, 12)).toISOExtString() == "-0099-12-04T05:06:12"); - assert(DateTime(Date(-999, 12, 4), TimeOfDay(13, 44, 59)).toISOExtString() == "-0999-12-04T13:44:59"); - assert(DateTime(Date(-9999, 7, 4), TimeOfDay(23, 59, 59)).toISOExtString() == "-9999-07-04T23:59:59"); - assert(DateTime(Date(-10000, 10, 20), TimeOfDay(1, 1, 1)).toISOExtString() == "-10000-10-20T01:01:01"); - - const cdt = DateTime(1999, 7, 6, 12, 30, 33); - immutable idt = DateTime(1999, 7, 6, 12, 30, 33); - assert(cdt.toISOExtString() == "1999-07-06T12:30:33"); - assert(idt.toISOExtString() == "1999-07-06T12:30:33"); - } - - /++ - Converts this $(LREF DateTime) to a string with the format - `YYYY-Mon-DD HH:MM:SS`. If `writer` is set, the resulting - string will be written directly to it. - - Params: - writer = A `char` accepting - $(REF_ALTTEXT output range, isOutputRange, std, range, primitives) - Returns: - A `string` when not using an output range; `void` otherwise. - +/ - string toSimpleString() const @safe pure nothrow - { - import std.array : appender; - auto w = appender!string(); - w.reserve(22); - try - toSimpleString(w); - catch (Exception e) - assert(0, "toSimpleString() threw."); - return w.data; - } - - /// ditto - void toSimpleString(W)(ref W writer) const - if (isOutputRange!(W, char)) - { - import std.format.write : formattedWrite; - _date.toSimpleString(writer); - formattedWrite!(" %02d:%02d:%02d")( - writer, - _tod._hour, - _tod._minute, - _tod._second - ); - } - - /// - @safe unittest - { - assert(DateTime(Date(2010, 7, 4), TimeOfDay(7, 6, 12)).toSimpleString() == - "2010-Jul-04 07:06:12"); - - assert(DateTime(Date(1998, 12, 25), TimeOfDay(2, 15, 0)).toSimpleString() == - "1998-Dec-25 02:15:00"); - - assert(DateTime(Date(0, 1, 5), TimeOfDay(23, 9, 59)).toSimpleString() == - "0000-Jan-05 23:09:59"); - - assert(DateTime(Date(-4, 1, 5), TimeOfDay(0, 0, 2)).toSimpleString() == - "-0004-Jan-05 00:00:02"); - } - - @safe unittest - { - // Test A.D. - assert(DateTime(Date(9, 12, 4), TimeOfDay(0, 0, 0)).toSimpleString() == "0009-Dec-04 00:00:00"); - assert(DateTime(Date(99, 12, 4), TimeOfDay(5, 6, 12)).toSimpleString() == "0099-Dec-04 05:06:12"); - assert(DateTime(Date(999, 12, 4), TimeOfDay(13, 44, 59)).toSimpleString() == "0999-Dec-04 13:44:59"); - assert(DateTime(Date(9999, 7, 4), TimeOfDay(23, 59, 59)).toSimpleString() == "9999-Jul-04 23:59:59"); - assert(DateTime(Date(10000, 10, 20), TimeOfDay(1, 1, 1)).toSimpleString() == "+10000-Oct-20 01:01:01"); - - // Test B.C. - assert(DateTime(Date(0, 12, 4), TimeOfDay(0, 12, 4)).toSimpleString() == "0000-Dec-04 00:12:04"); - assert(DateTime(Date(-9, 12, 4), TimeOfDay(0, 0, 0)).toSimpleString() == "-0009-Dec-04 00:00:00"); - assert(DateTime(Date(-99, 12, 4), TimeOfDay(5, 6, 12)).toSimpleString() == "-0099-Dec-04 05:06:12"); - assert(DateTime(Date(-999, 12, 4), TimeOfDay(13, 44, 59)).toSimpleString() == "-0999-Dec-04 13:44:59"); - assert(DateTime(Date(-9999, 7, 4), TimeOfDay(23, 59, 59)).toSimpleString() == "-9999-Jul-04 23:59:59"); - assert(DateTime(Date(-10000, 10, 20), TimeOfDay(1, 1, 1)).toSimpleString() == "-10000-Oct-20 01:01:01"); - - const cdt = DateTime(1999, 7, 6, 12, 30, 33); - immutable idt = DateTime(1999, 7, 6, 12, 30, 33); - assert(cdt.toSimpleString() == "1999-Jul-06 12:30:33"); - assert(idt.toSimpleString() == "1999-Jul-06 12:30:33"); - } - - - /++ - Converts this $(LREF DateTime) to a string. - - This function exists to make it easy to convert a $(LREF DateTime) to a - string for code that does not care what the exact format is - just that - it presents the information in a clear manner. It also makes it easy to - simply convert a $(LREF DateTime) to a string when using functions such - as `to!string`, `format`, or `writeln` which use toString to convert - user-defined types. So, it is unlikely that much code will call - toString directly. - - The format of the string is purposefully unspecified, and code that - cares about the format of the string should use `toISOString`, - `toISOExtString`, `toSimpleString`, or some other custom formatting - function that explicitly generates the format that the code needs. The - reason is that the code is then clear about what format it's using, - making it less error-prone to maintain the code and interact with other - software that consumes the generated strings. It's for this same reason - that $(LREF DateTime) has no `fromString` function, whereas it does have - `fromISOString`, `fromISOExtString`, and `fromSimpleString`. - - The format returned by toString may or may not change in the future. - +/ - string toString() const @safe pure nothrow - { - return toSimpleString(); - } - - @safe unittest - { - auto dt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)); - const cdt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)); - immutable idt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)); - assert(dt.toString()); - assert(cdt.toString()); - assert(idt.toString()); - } - - /// ditto - void toString(W)(ref W writer) const - if (isOutputRange!(W, char)) - { - toSimpleString(writer); - } - - /++ - Creates a $(LREF DateTime) from a string with the format YYYYMMDDTHHMMSS. - Whitespace is stripped from the given string. - - Params: - isoString = A string formatted in the ISO format for dates and times. - - Throws: - $(REF DateTimeException,std,datetime,date) if the given string is - not in the ISO format or if the resulting $(LREF DateTime) would not - be valid. - +/ - static DateTime fromISOString(S)(scope const S isoString) @safe pure - if (isSomeString!S) - { - import std.algorithm.searching : countUntil; - import std.exception : enforce; - import std.format : format; - import std.string : strip; - import std.utf : byCodeUnit; - - auto str = strip(isoString); - - enforce(str.length >= 15, new DateTimeException(format("Invalid ISO String: %s", isoString))); - auto t = str.byCodeUnit.countUntil('T'); - - enforce(t != -1, new DateTimeException(format("Invalid ISO String: %s", isoString))); - - immutable date = Date.fromISOString(str[0 .. t]); - immutable tod = TimeOfDay.fromISOString(str[t+1 .. $]); - - return DateTime(date, tod); - } - - /// - @safe unittest - { - assert(DateTime.fromISOString("20100704T070612") == - DateTime(Date(2010, 7, 4), TimeOfDay(7, 6, 12))); - - assert(DateTime.fromISOString("19981225T021500") == - DateTime(Date(1998, 12, 25), TimeOfDay(2, 15, 0))); - - assert(DateTime.fromISOString("00000105T230959") == - DateTime(Date(0, 1, 5), TimeOfDay(23, 9, 59))); - - assert(DateTime.fromISOString("-00040105T000002") == - DateTime(Date(-4, 1, 5), TimeOfDay(0, 0, 2))); - - assert(DateTime.fromISOString(" 20100704T070612 ") == - DateTime(Date(2010, 7, 4), TimeOfDay(7, 6, 12))); - } - - @safe unittest - { - assertThrown!DateTimeException(DateTime.fromISOString("")); - assertThrown!DateTimeException(DateTime.fromISOString("20100704000000")); - assertThrown!DateTimeException(DateTime.fromISOString("20100704 000000")); - assertThrown!DateTimeException(DateTime.fromISOString("20100704t000000")); - assertThrown!DateTimeException(DateTime.fromISOString("20100704T000000.")); - assertThrown!DateTimeException(DateTime.fromISOString("20100704T000000.0")); - - assertThrown!DateTimeException(DateTime.fromISOString("2010-07-0400:00:00")); - assertThrown!DateTimeException(DateTime.fromISOString("2010-07-04 00:00:00")); - assertThrown!DateTimeException(DateTime.fromISOString("2010-07-04t00:00:00")); - assertThrown!DateTimeException(DateTime.fromISOString("2010-07-04T00:00:00.")); - assertThrown!DateTimeException(DateTime.fromISOString("2010-07-04T00:00:00.0")); - - assertThrown!DateTimeException(DateTime.fromISOString("2010-Jul-0400:00:00")); - assertThrown!DateTimeException(DateTime.fromISOString("2010-Jul-04 00:00:00")); - assertThrown!DateTimeException(DateTime.fromISOString("2010-Jul-04t00:00:00")); - assertThrown!DateTimeException(DateTime.fromISOString("2010-Jul-04T00:00:00")); - assertThrown!DateTimeException(DateTime.fromISOString("2010-Jul-04 00:00:00.")); - assertThrown!DateTimeException(DateTime.fromISOString("2010-Jul-04 00:00:00.0")); - - assertThrown!DateTimeException(DateTime.fromISOString("2010-12-22T172201")); - assertThrown!DateTimeException(DateTime.fromISOString("2010-Dec-22 17:22:01")); - - assert(DateTime.fromISOString("20101222T172201") == DateTime(Date(2010, 12, 22), TimeOfDay(17, 22, 1))); - assert(DateTime.fromISOString("19990706T123033") == DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33))); - assert(DateTime.fromISOString("-19990706T123033") == DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33))); - assert(DateTime.fromISOString("+019990706T123033") == DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33))); - assert(DateTime.fromISOString("19990706T123033 ") == DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33))); - assert(DateTime.fromISOString(" 19990706T123033") == DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33))); - assert(DateTime.fromISOString(" 19990706T123033 ") == DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33))); - } - - // https://issues.dlang.org/show_bug.cgi?id=17801 - @safe unittest - { - import std.conv : to; - import std.meta : AliasSeq; - static foreach (C; AliasSeq!(char, wchar, dchar)) - { - static foreach (S; AliasSeq!(C[], const(C)[], immutable(C)[])) - assert(DateTime.fromISOString(to!S("20121221T141516")) == DateTime(2012, 12, 21, 14, 15, 16)); - } - } - - - /++ - Creates a $(LREF DateTime) from a string with the format - YYYY-MM-DDTHH:MM:SS. Whitespace is stripped from the given string. - - Params: - isoExtString = A string formatted in the ISO Extended format for dates - and times. - - Throws: - $(REF DateTimeException,std,datetime,date) if the given string is - not in the ISO Extended format or if the resulting $(LREF DateTime) - would not be valid. - +/ - static DateTime fromISOExtString(S)(scope const S isoExtString) @safe pure - if (isSomeString!(S)) - { - import std.algorithm.searching : countUntil; - import std.exception : enforce; - import std.format : format; - import std.string : strip; - import std.utf : byCodeUnit; - - auto str = strip(isoExtString); - - enforce(str.length >= 15, new DateTimeException(format("Invalid ISO Extended String: %s", isoExtString))); - auto t = str.byCodeUnit.countUntil('T'); - - enforce(t != -1, new DateTimeException(format("Invalid ISO Extended String: %s", isoExtString))); - - immutable date = Date.fromISOExtString(str[0 .. t]); - immutable tod = TimeOfDay.fromISOExtString(str[t+1 .. $]); - - return DateTime(date, tod); - } - - /// - @safe unittest - { - assert(DateTime.fromISOExtString("2010-07-04T07:06:12") == - DateTime(Date(2010, 7, 4), TimeOfDay(7, 6, 12))); - - assert(DateTime.fromISOExtString("1998-12-25T02:15:00") == - DateTime(Date(1998, 12, 25), TimeOfDay(2, 15, 0))); - - assert(DateTime.fromISOExtString("0000-01-05T23:09:59") == - DateTime(Date(0, 1, 5), TimeOfDay(23, 9, 59))); - - assert(DateTime.fromISOExtString("-0004-01-05T00:00:02") == - DateTime(Date(-4, 1, 5), TimeOfDay(0, 0, 2))); - - assert(DateTime.fromISOExtString(" 2010-07-04T07:06:12 ") == - DateTime(Date(2010, 7, 4), TimeOfDay(7, 6, 12))); - } - - @safe unittest - { - assertThrown!DateTimeException(DateTime.fromISOExtString("")); - assertThrown!DateTimeException(DateTime.fromISOExtString("20100704000000")); - assertThrown!DateTimeException(DateTime.fromISOExtString("20100704 000000")); - assertThrown!DateTimeException(DateTime.fromISOExtString("20100704t000000")); - assertThrown!DateTimeException(DateTime.fromISOExtString("20100704T000000.")); - assertThrown!DateTimeException(DateTime.fromISOExtString("20100704T000000.0")); - - assertThrown!DateTimeException(DateTime.fromISOExtString("2010-07:0400:00:00")); - assertThrown!DateTimeException(DateTime.fromISOExtString("2010-07-04 00:00:00")); - assertThrown!DateTimeException(DateTime.fromISOExtString("2010-07-04 00:00:00")); - assertThrown!DateTimeException(DateTime.fromISOExtString("2010-07-04t00:00:00")); - assertThrown!DateTimeException(DateTime.fromISOExtString("2010-07-04T00:00:00.")); - assertThrown!DateTimeException(DateTime.fromISOExtString("2010-07-04T00:00:00.0")); - - assertThrown!DateTimeException(DateTime.fromISOExtString("2010-Jul-0400:00:00")); - assertThrown!DateTimeException(DateTime.fromISOExtString("2010-Jul-04t00:00:00")); - assertThrown!DateTimeException(DateTime.fromISOExtString("2010-Jul-04 00:00:00.")); - assertThrown!DateTimeException(DateTime.fromISOExtString("2010-Jul-04 00:00:00.0")); - - assertThrown!DateTimeException(DateTime.fromISOExtString("20101222T172201")); - assertThrown!DateTimeException(DateTime.fromISOExtString("2010-Dec-22 17:22:01")); - - assert(DateTime.fromISOExtString("2010-12-22T17:22:01") == DateTime(Date(2010, 12, 22), TimeOfDay(17, 22, 1))); - assert(DateTime.fromISOExtString("1999-07-06T12:30:33") == DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33))); - assert(DateTime.fromISOExtString("-1999-07-06T12:30:33") == DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33))); - assert(DateTime.fromISOExtString("+01999-07-06T12:30:33") == DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33))); - assert(DateTime.fromISOExtString("1999-07-06T12:30:33 ") == DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33))); - assert(DateTime.fromISOExtString(" 1999-07-06T12:30:33") == DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33))); - assert(DateTime.fromISOExtString(" 1999-07-06T12:30:33 ") == DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33))); - } - - // https://issues.dlang.org/show_bug.cgi?id=17801 - @safe unittest - { - import std.conv : to; - import std.meta : AliasSeq; - static foreach (C; AliasSeq!(char, wchar, dchar)) - { - static foreach (S; AliasSeq!(C[], const(C)[], immutable(C)[])) - assert(DateTime.fromISOExtString(to!S("2012-12-21T14:15:16")) == DateTime(2012, 12, 21, 14, 15, 16)); - } - } - - - /++ - Creates a $(LREF DateTime) from a string with the format - YYYY-Mon-DD HH:MM:SS. Whitespace is stripped from the given string. - - Params: - simpleString = A string formatted in the way that toSimpleString - formats dates and times. - - Throws: - $(REF DateTimeException,std,datetime,date) if the given string is - not in the correct format or if the resulting $(LREF DateTime) - would not be valid. - +/ - static DateTime fromSimpleString(S)(scope const S simpleString) @safe pure - if (isSomeString!(S)) - { - import std.algorithm.searching : countUntil; - import std.exception : enforce; - import std.format : format; - import std.string : strip; - import std.utf : byCodeUnit; - - auto str = strip(simpleString); - - enforce(str.length >= 15, new DateTimeException(format("Invalid string format: %s", simpleString))); - auto t = str.byCodeUnit.countUntil(' '); - - enforce(t != -1, new DateTimeException(format("Invalid string format: %s", simpleString))); - - immutable date = Date.fromSimpleString(str[0 .. t]); - immutable tod = TimeOfDay.fromISOExtString(str[t+1 .. $]); - - return DateTime(date, tod); - } - - /// - @safe unittest - { - assert(DateTime.fromSimpleString("2010-Jul-04 07:06:12") == - DateTime(Date(2010, 7, 4), TimeOfDay(7, 6, 12))); - assert(DateTime.fromSimpleString("1998-Dec-25 02:15:00") == - DateTime(Date(1998, 12, 25), TimeOfDay(2, 15, 0))); - assert(DateTime.fromSimpleString("0000-Jan-05 23:09:59") == - DateTime(Date(0, 1, 5), TimeOfDay(23, 9, 59))); - assert(DateTime.fromSimpleString("-0004-Jan-05 00:00:02") == - DateTime(Date(-4, 1, 5), TimeOfDay(0, 0, 2))); - assert(DateTime.fromSimpleString(" 2010-Jul-04 07:06:12 ") == - DateTime(Date(2010, 7, 4), TimeOfDay(7, 6, 12))); - } - - @safe unittest - { - assertThrown!DateTimeException(DateTime.fromISOString("")); - assertThrown!DateTimeException(DateTime.fromISOString("20100704000000")); - assertThrown!DateTimeException(DateTime.fromISOString("20100704 000000")); - assertThrown!DateTimeException(DateTime.fromISOString("20100704t000000")); - assertThrown!DateTimeException(DateTime.fromISOString("20100704T000000.")); - assertThrown!DateTimeException(DateTime.fromISOString("20100704T000000.0")); - - assertThrown!DateTimeException(DateTime.fromISOString("2010-07-0400:00:00")); - assertThrown!DateTimeException(DateTime.fromISOString("2010-07-04 00:00:00")); - assertThrown!DateTimeException(DateTime.fromISOString("2010-07-04t00:00:00")); - assertThrown!DateTimeException(DateTime.fromISOString("2010-07-04T00:00:00.")); - assertThrown!DateTimeException(DateTime.fromISOString("2010-07-04T00:00:00.0")); - - assertThrown!DateTimeException(DateTime.fromISOString("2010-Jul-0400:00:00")); - assertThrown!DateTimeException(DateTime.fromISOString("2010-Jul-04 00:00:00")); - assertThrown!DateTimeException(DateTime.fromISOString("2010-Jul-04t00:00:00")); - assertThrown!DateTimeException(DateTime.fromISOString("2010-Jul-04T00:00:00")); - assertThrown!DateTimeException(DateTime.fromISOString("2010-Jul-04 00:00:00.")); - assertThrown!DateTimeException(DateTime.fromISOString("2010-Jul-04 00:00:00.0")); - - assertThrown!DateTimeException(DateTime.fromSimpleString("20101222T172201")); - assertThrown!DateTimeException(DateTime.fromSimpleString("2010-12-22T172201")); - - assert(DateTime.fromSimpleString("2010-Dec-22 17:22:01") == - DateTime(Date(2010, 12, 22), TimeOfDay(17, 22, 1))); - assert(DateTime.fromSimpleString("1999-Jul-06 12:30:33") == - DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33))); - assert(DateTime.fromSimpleString("-1999-Jul-06 12:30:33") == - DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33))); - assert(DateTime.fromSimpleString("+01999-Jul-06 12:30:33") == - DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33))); - assert(DateTime.fromSimpleString("1999-Jul-06 12:30:33 ") == - DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33))); - assert(DateTime.fromSimpleString(" 1999-Jul-06 12:30:33") == - DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33))); - assert(DateTime.fromSimpleString(" 1999-Jul-06 12:30:33 ") == - DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33))); - } - - // https://issues.dlang.org/show_bug.cgi?id=17801 - @safe unittest - { - import std.conv : to; - import std.meta : AliasSeq; - static foreach (C; AliasSeq!(char, wchar, dchar)) - { - static foreach (S; AliasSeq!(C[], const(C)[], immutable(C)[])) - assert(DateTime.fromSimpleString(to!S("2012-Dec-21 14:15:16")) == DateTime(2012, 12, 21, 14, 15, 16)); - } - } - - - /++ - Returns the $(LREF DateTime) farthest in the past which is representable - by $(LREF DateTime). - +/ - @property static DateTime min() @safe pure nothrow @nogc - out(result) - { - assert(result._date == Date.min); - assert(result._tod == TimeOfDay.min); - } - do - { - auto dt = DateTime.init; - dt._date._year = short.min; - dt._date._month = Month.jan; - dt._date._day = 1; - - return dt; - } - - @safe unittest - { - assert(DateTime.min.year < 0); - assert(DateTime.min < DateTime.max); - } - - - /++ - Returns the $(LREF DateTime) farthest in the future which is - representable by $(LREF DateTime). - +/ - @property static DateTime max() @safe pure nothrow @nogc - out(result) - { - assert(result._date == Date.max); - assert(result._tod == TimeOfDay.max); - } - do - { - auto dt = DateTime.init; - dt._date._year = short.max; - dt._date._month = Month.dec; - dt._date._day = 31; - dt._tod._hour = TimeOfDay.maxHour; - dt._tod._minute = TimeOfDay.maxMinute; - dt._tod._second = TimeOfDay.maxSecond; - - return dt; - } - - @safe unittest - { - assert(DateTime.max.year > 0); - assert(DateTime.max > DateTime.min); - } - - -private: - - /+ - Add seconds to the time of day. Negative values will subtract. If the - number of seconds overflows (or underflows), then the seconds will wrap, - increasing (or decreasing) the number of minutes accordingly. The - same goes for any larger units. - - Params: - seconds = The number of seconds to add to this $(LREF DateTime). - +/ - ref DateTime _addSeconds(long seconds) return @safe pure nothrow @nogc - { - import core.time : convert; - long hnsecs = convert!("seconds", "hnsecs")(seconds); - hnsecs += convert!("hours", "hnsecs")(_tod._hour); - hnsecs += convert!("minutes", "hnsecs")(_tod._minute); - hnsecs += convert!("seconds", "hnsecs")(_tod._second); - - auto days = splitUnitsFromHNSecs!"days"(hnsecs); - - if (hnsecs < 0) - { - hnsecs += convert!("days", "hnsecs")(1); - --days; - } - - _date._addDays(days); - - immutable newHours = splitUnitsFromHNSecs!"hours"(hnsecs); - immutable newMinutes = splitUnitsFromHNSecs!"minutes"(hnsecs); - immutable newSeconds = splitUnitsFromHNSecs!"seconds"(hnsecs); - - _tod._hour = cast(ubyte) newHours; - _tod._minute = cast(ubyte) newMinutes; - _tod._second = cast(ubyte) newSeconds; - - return this; - } - - @safe unittest - { - static void testDT(DateTime orig, int seconds, DateTime expected, size_t line = __LINE__) - { - orig._addSeconds(seconds); - assert(orig == expected); - } - - // Test A.D. - testDT(DateTime(1999, 7, 6, 12, 30, 33), 0, DateTime(1999, 7, 6, 12, 30, 33)); - testDT(DateTime(1999, 7, 6, 12, 30, 33), 1, DateTime(1999, 7, 6, 12, 30, 34)); - testDT(DateTime(1999, 7, 6, 12, 30, 33), 2, DateTime(1999, 7, 6, 12, 30, 35)); - testDT(DateTime(1999, 7, 6, 12, 30, 33), 3, DateTime(1999, 7, 6, 12, 30, 36)); - testDT(DateTime(1999, 7, 6, 12, 30, 33), 4, DateTime(1999, 7, 6, 12, 30, 37)); - testDT(DateTime(1999, 7, 6, 12, 30, 33), 5, DateTime(1999, 7, 6, 12, 30, 38)); - testDT(DateTime(1999, 7, 6, 12, 30, 33), 10, DateTime(1999, 7, 6, 12, 30, 43)); - testDT(DateTime(1999, 7, 6, 12, 30, 33), 15, DateTime(1999, 7, 6, 12, 30, 48)); - testDT(DateTime(1999, 7, 6, 12, 30, 33), 26, DateTime(1999, 7, 6, 12, 30, 59)); - testDT(DateTime(1999, 7, 6, 12, 30, 33), 27, DateTime(1999, 7, 6, 12, 31, 0)); - testDT(DateTime(1999, 7, 6, 12, 30, 33), 30, DateTime(1999, 7, 6, 12, 31, 3)); - testDT(DateTime(1999, 7, 6, 12, 30, 33), 59, DateTime(1999, 7, 6, 12, 31, 32)); - testDT(DateTime(1999, 7, 6, 12, 30, 33), 60, DateTime(1999, 7, 6, 12, 31, 33)); - testDT(DateTime(1999, 7, 6, 12, 30, 33), 61, DateTime(1999, 7, 6, 12, 31, 34)); - - testDT(DateTime(1999, 7, 6, 12, 30, 33), 1766, DateTime(1999, 7, 6, 12, 59, 59)); - testDT(DateTime(1999, 7, 6, 12, 30, 33), 1767, DateTime(1999, 7, 6, 13, 0, 0)); - testDT(DateTime(1999, 7, 6, 12, 30, 33), 1768, DateTime(1999, 7, 6, 13, 0, 1)); - testDT(DateTime(1999, 7, 6, 12, 30, 33), 2007, DateTime(1999, 7, 6, 13, 4, 0)); - testDT(DateTime(1999, 7, 6, 12, 30, 33), 3599, DateTime(1999, 7, 6, 13, 30, 32)); - testDT(DateTime(1999, 7, 6, 12, 30, 33), 3600, DateTime(1999, 7, 6, 13, 30, 33)); - testDT(DateTime(1999, 7, 6, 12, 30, 33), 3601, DateTime(1999, 7, 6, 13, 30, 34)); - testDT(DateTime(1999, 7, 6, 12, 30, 33), 7200, DateTime(1999, 7, 6, 14, 30, 33)); - testDT(DateTime(1999, 7, 6, 23, 0, 0), 432_123, DateTime(1999, 7, 11, 23, 2, 3)); - - testDT(DateTime(1999, 7, 6, 12, 30, 33), -1, DateTime(1999, 7, 6, 12, 30, 32)); - testDT(DateTime(1999, 7, 6, 12, 30, 33), -2, DateTime(1999, 7, 6, 12, 30, 31)); - testDT(DateTime(1999, 7, 6, 12, 30, 33), -3, DateTime(1999, 7, 6, 12, 30, 30)); - testDT(DateTime(1999, 7, 6, 12, 30, 33), -4, DateTime(1999, 7, 6, 12, 30, 29)); - testDT(DateTime(1999, 7, 6, 12, 30, 33), -5, DateTime(1999, 7, 6, 12, 30, 28)); - testDT(DateTime(1999, 7, 6, 12, 30, 33), -10, DateTime(1999, 7, 6, 12, 30, 23)); - testDT(DateTime(1999, 7, 6, 12, 30, 33), -15, DateTime(1999, 7, 6, 12, 30, 18)); - testDT(DateTime(1999, 7, 6, 12, 30, 33), -33, DateTime(1999, 7, 6, 12, 30, 0)); - testDT(DateTime(1999, 7, 6, 12, 30, 33), -34, DateTime(1999, 7, 6, 12, 29, 59)); - testDT(DateTime(1999, 7, 6, 12, 30, 33), -35, DateTime(1999, 7, 6, 12, 29, 58)); - testDT(DateTime(1999, 7, 6, 12, 30, 33), -59, DateTime(1999, 7, 6, 12, 29, 34)); - testDT(DateTime(1999, 7, 6, 12, 30, 33), -60, DateTime(1999, 7, 6, 12, 29, 33)); - testDT(DateTime(1999, 7, 6, 12, 30, 33), -61, DateTime(1999, 7, 6, 12, 29, 32)); - - testDT(DateTime(1999, 7, 6, 12, 30, 33), -1833, DateTime(1999, 7, 6, 12, 0, 0)); - testDT(DateTime(1999, 7, 6, 12, 30, 33), -1834, DateTime(1999, 7, 6, 11, 59, 59)); - testDT(DateTime(1999, 7, 6, 12, 30, 33), -3600, DateTime(1999, 7, 6, 11, 30, 33)); - testDT(DateTime(1999, 7, 6, 12, 30, 33), -3601, DateTime(1999, 7, 6, 11, 30, 32)); - testDT(DateTime(1999, 7, 6, 12, 30, 33), -5134, DateTime(1999, 7, 6, 11, 4, 59)); - testDT(DateTime(1999, 7, 6, 23, 0, 0), -432_123, DateTime(1999, 7, 1, 22, 57, 57)); - - testDT(DateTime(1999, 7, 6, 12, 30, 0), 1, DateTime(1999, 7, 6, 12, 30, 1)); - testDT(DateTime(1999, 7, 6, 12, 30, 0), 0, DateTime(1999, 7, 6, 12, 30, 0)); - testDT(DateTime(1999, 7, 6, 12, 30, 0), -1, DateTime(1999, 7, 6, 12, 29, 59)); - - testDT(DateTime(1999, 7, 6, 12, 0, 0), 1, DateTime(1999, 7, 6, 12, 0, 1)); - testDT(DateTime(1999, 7, 6, 12, 0, 0), 0, DateTime(1999, 7, 6, 12, 0, 0)); - testDT(DateTime(1999, 7, 6, 12, 0, 0), -1, DateTime(1999, 7, 6, 11, 59, 59)); - - testDT(DateTime(1999, 7, 6, 0, 0, 0), 1, DateTime(1999, 7, 6, 0, 0, 1)); - testDT(DateTime(1999, 7, 6, 0, 0, 0), 0, DateTime(1999, 7, 6, 0, 0, 0)); - testDT(DateTime(1999, 7, 6, 0, 0, 0), -1, DateTime(1999, 7, 5, 23, 59, 59)); - - testDT(DateTime(1999, 7, 5, 23, 59, 59), 1, DateTime(1999, 7, 6, 0, 0, 0)); - testDT(DateTime(1999, 7, 5, 23, 59, 59), 0, DateTime(1999, 7, 5, 23, 59, 59)); - testDT(DateTime(1999, 7, 5, 23, 59, 59), -1, DateTime(1999, 7, 5, 23, 59, 58)); - - testDT(DateTime(1998, 12, 31, 23, 59, 59), 1, DateTime(1999, 1, 1, 0, 0, 0)); - testDT(DateTime(1998, 12, 31, 23, 59, 59), 0, DateTime(1998, 12, 31, 23, 59, 59)); - testDT(DateTime(1998, 12, 31, 23, 59, 59), -1, DateTime(1998, 12, 31, 23, 59, 58)); - - testDT(DateTime(1998, 1, 1, 0, 0, 0), 1, DateTime(1998, 1, 1, 0, 0, 1)); - testDT(DateTime(1998, 1, 1, 0, 0, 0), 0, DateTime(1998, 1, 1, 0, 0, 0)); - testDT(DateTime(1998, 1, 1, 0, 0, 0), -1, DateTime(1997, 12, 31, 23, 59, 59)); - - // Test B.C. - testDT(DateTime(-1999, 7, 6, 12, 30, 33), 0, DateTime(-1999, 7, 6, 12, 30, 33)); - testDT(DateTime(-1999, 7, 6, 12, 30, 33), 1, DateTime(-1999, 7, 6, 12, 30, 34)); - testDT(DateTime(-1999, 7, 6, 12, 30, 33), 2, DateTime(-1999, 7, 6, 12, 30, 35)); - testDT(DateTime(-1999, 7, 6, 12, 30, 33), 3, DateTime(-1999, 7, 6, 12, 30, 36)); - testDT(DateTime(-1999, 7, 6, 12, 30, 33), 4, DateTime(-1999, 7, 6, 12, 30, 37)); - testDT(DateTime(-1999, 7, 6, 12, 30, 33), 5, DateTime(-1999, 7, 6, 12, 30, 38)); - testDT(DateTime(-1999, 7, 6, 12, 30, 33), 10, DateTime(-1999, 7, 6, 12, 30, 43)); - testDT(DateTime(-1999, 7, 6, 12, 30, 33), 15, DateTime(-1999, 7, 6, 12, 30, 48)); - testDT(DateTime(-1999, 7, 6, 12, 30, 33), 26, DateTime(-1999, 7, 6, 12, 30, 59)); - testDT(DateTime(-1999, 7, 6, 12, 30, 33), 27, DateTime(-1999, 7, 6, 12, 31, 0)); - testDT(DateTime(-1999, 7, 6, 12, 30, 33), 30, DateTime(-1999, 7, 6, 12, 31, 3)); - testDT(DateTime(-1999, 7, 6, 12, 30, 33), 59, DateTime(-1999, 7, 6, 12, 31, 32)); - testDT(DateTime(-1999, 7, 6, 12, 30, 33), 60, DateTime(-1999, 7, 6, 12, 31, 33)); - testDT(DateTime(-1999, 7, 6, 12, 30, 33), 61, DateTime(-1999, 7, 6, 12, 31, 34)); - - testDT(DateTime(-1999, 7, 6, 12, 30, 33), 1766, DateTime(-1999, 7, 6, 12, 59, 59)); - testDT(DateTime(-1999, 7, 6, 12, 30, 33), 1767, DateTime(-1999, 7, 6, 13, 0, 0)); - testDT(DateTime(-1999, 7, 6, 12, 30, 33), 1768, DateTime(-1999, 7, 6, 13, 0, 1)); - testDT(DateTime(-1999, 7, 6, 12, 30, 33), 2007, DateTime(-1999, 7, 6, 13, 4, 0)); - testDT(DateTime(-1999, 7, 6, 12, 30, 33), 3599, DateTime(-1999, 7, 6, 13, 30, 32)); - testDT(DateTime(-1999, 7, 6, 12, 30, 33), 3600, DateTime(-1999, 7, 6, 13, 30, 33)); - testDT(DateTime(-1999, 7, 6, 12, 30, 33), 3601, DateTime(-1999, 7, 6, 13, 30, 34)); - testDT(DateTime(-1999, 7, 6, 12, 30, 33), 7200, DateTime(-1999, 7, 6, 14, 30, 33)); - testDT(DateTime(-1999, 7, 6, 23, 0, 0), 432_123, DateTime(-1999, 7, 11, 23, 2, 3)); - - testDT(DateTime(-1999, 7, 6, 12, 30, 33), -1, DateTime(-1999, 7, 6, 12, 30, 32)); - testDT(DateTime(-1999, 7, 6, 12, 30, 33), -2, DateTime(-1999, 7, 6, 12, 30, 31)); - testDT(DateTime(-1999, 7, 6, 12, 30, 33), -3, DateTime(-1999, 7, 6, 12, 30, 30)); - testDT(DateTime(-1999, 7, 6, 12, 30, 33), -4, DateTime(-1999, 7, 6, 12, 30, 29)); - testDT(DateTime(-1999, 7, 6, 12, 30, 33), -5, DateTime(-1999, 7, 6, 12, 30, 28)); - testDT(DateTime(-1999, 7, 6, 12, 30, 33), -10, DateTime(-1999, 7, 6, 12, 30, 23)); - testDT(DateTime(-1999, 7, 6, 12, 30, 33), -15, DateTime(-1999, 7, 6, 12, 30, 18)); - testDT(DateTime(-1999, 7, 6, 12, 30, 33), -33, DateTime(-1999, 7, 6, 12, 30, 0)); - testDT(DateTime(-1999, 7, 6, 12, 30, 33), -34, DateTime(-1999, 7, 6, 12, 29, 59)); - testDT(DateTime(-1999, 7, 6, 12, 30, 33), -35, DateTime(-1999, 7, 6, 12, 29, 58)); - testDT(DateTime(-1999, 7, 6, 12, 30, 33), -59, DateTime(-1999, 7, 6, 12, 29, 34)); - testDT(DateTime(-1999, 7, 6, 12, 30, 33), -60, DateTime(-1999, 7, 6, 12, 29, 33)); - testDT(DateTime(-1999, 7, 6, 12, 30, 33), -61, DateTime(-1999, 7, 6, 12, 29, 32)); - - testDT(DateTime(-1999, 7, 6, 12, 30, 33), -1833, DateTime(-1999, 7, 6, 12, 0, 0)); - testDT(DateTime(-1999, 7, 6, 12, 30, 33), -1834, DateTime(-1999, 7, 6, 11, 59, 59)); - testDT(DateTime(-1999, 7, 6, 12, 30, 33), -3600, DateTime(-1999, 7, 6, 11, 30, 33)); - testDT(DateTime(-1999, 7, 6, 12, 30, 33), -3601, DateTime(-1999, 7, 6, 11, 30, 32)); - testDT(DateTime(-1999, 7, 6, 12, 30, 33), -5134, DateTime(-1999, 7, 6, 11, 4, 59)); - testDT(DateTime(-1999, 7, 6, 12, 30, 33), -7200, DateTime(-1999, 7, 6, 10, 30, 33)); - testDT(DateTime(-1999, 7, 6, 23, 0, 0), -432_123, DateTime(-1999, 7, 1, 22, 57, 57)); - - testDT(DateTime(-1999, 7, 6, 12, 30, 0), 1, DateTime(-1999, 7, 6, 12, 30, 1)); - testDT(DateTime(-1999, 7, 6, 12, 30, 0), 0, DateTime(-1999, 7, 6, 12, 30, 0)); - testDT(DateTime(-1999, 7, 6, 12, 30, 0), -1, DateTime(-1999, 7, 6, 12, 29, 59)); - - testDT(DateTime(-1999, 7, 6, 12, 0, 0), 1, DateTime(-1999, 7, 6, 12, 0, 1)); - testDT(DateTime(-1999, 7, 6, 12, 0, 0), 0, DateTime(-1999, 7, 6, 12, 0, 0)); - testDT(DateTime(-1999, 7, 6, 12, 0, 0), -1, DateTime(-1999, 7, 6, 11, 59, 59)); - - testDT(DateTime(-1999, 7, 6, 0, 0, 0), 1, DateTime(-1999, 7, 6, 0, 0, 1)); - testDT(DateTime(-1999, 7, 6, 0, 0, 0), 0, DateTime(-1999, 7, 6, 0, 0, 0)); - testDT(DateTime(-1999, 7, 6, 0, 0, 0), -1, DateTime(-1999, 7, 5, 23, 59, 59)); - - testDT(DateTime(-1999, 7, 5, 23, 59, 59), 1, DateTime(-1999, 7, 6, 0, 0, 0)); - testDT(DateTime(-1999, 7, 5, 23, 59, 59), 0, DateTime(-1999, 7, 5, 23, 59, 59)); - testDT(DateTime(-1999, 7, 5, 23, 59, 59), -1, DateTime(-1999, 7, 5, 23, 59, 58)); - - testDT(DateTime(-2000, 12, 31, 23, 59, 59), 1, DateTime(-1999, 1, 1, 0, 0, 0)); - testDT(DateTime(-2000, 12, 31, 23, 59, 59), 0, DateTime(-2000, 12, 31, 23, 59, 59)); - testDT(DateTime(-2000, 12, 31, 23, 59, 59), -1, DateTime(-2000, 12, 31, 23, 59, 58)); - - testDT(DateTime(-2000, 1, 1, 0, 0, 0), 1, DateTime(-2000, 1, 1, 0, 0, 1)); - testDT(DateTime(-2000, 1, 1, 0, 0, 0), 0, DateTime(-2000, 1, 1, 0, 0, 0)); - testDT(DateTime(-2000, 1, 1, 0, 0, 0), -1, DateTime(-2001, 12, 31, 23, 59, 59)); - - // Test Both - testDT(DateTime(1, 1, 1, 0, 0, 0), -1, DateTime(0, 12, 31, 23, 59, 59)); - testDT(DateTime(0, 12, 31, 23, 59, 59), 1, DateTime(1, 1, 1, 0, 0, 0)); - - testDT(DateTime(0, 1, 1, 0, 0, 0), -1, DateTime(-1, 12, 31, 23, 59, 59)); - testDT(DateTime(-1, 12, 31, 23, 59, 59), 1, DateTime(0, 1, 1, 0, 0, 0)); - - testDT(DateTime(-1, 1, 1, 11, 30, 33), 63_165_600L, DateTime(1, 1, 1, 13, 30, 33)); - testDT(DateTime(1, 1, 1, 13, 30, 33), -63_165_600L, DateTime(-1, 1, 1, 11, 30, 33)); - - testDT(DateTime(-1, 1, 1, 11, 30, 33), 63_165_617L, DateTime(1, 1, 1, 13, 30, 50)); - testDT(DateTime(1, 1, 1, 13, 30, 50), -63_165_617L, DateTime(-1, 1, 1, 11, 30, 33)); - - const cdt = DateTime(1999, 7, 6, 12, 30, 33); - immutable idt = DateTime(1999, 7, 6, 12, 30, 33); - static assert(!__traits(compiles, cdt._addSeconds(4))); - static assert(!__traits(compiles, idt._addSeconds(4))); - } - - - Date _date; - TimeOfDay _tod; -} - -/// -@safe pure unittest -{ - import core.time : days, seconds; - - auto dt = DateTime(2000, 6, 1, 10, 30, 0); - - assert(dt.date == Date(2000, 6, 1)); - assert(dt.timeOfDay == TimeOfDay(10, 30, 0)); - assert(dt.dayOfYear == 153); - assert(dt.dayOfWeek == DayOfWeek.thu); - - dt += 10.days + 100.seconds; - assert(dt == DateTime(2000, 6, 11, 10, 31, 40)); - - assert(dt.toISOExtString() == "2000-06-11T10:31:40"); - assert(dt.toISOString() == "20000611T103140"); - assert(dt.toSimpleString() == "2000-Jun-11 10:31:40"); - - assert(DateTime.fromISOExtString("2018-01-01T12:00:00") == DateTime(2018, 1, 1, 12, 0, 0)); - assert(DateTime.fromISOString("20180101T120000") == DateTime(2018, 1, 1, 12, 0, 0)); - assert(DateTime.fromSimpleString("2018-Jan-01 12:00:00") == DateTime(2018, 1, 1, 12, 0, 0)); -} - -/++ - Represents a date in the - $(HTTP en.wikipedia.org/wiki/Proleptic_Gregorian_calendar, Proleptic - Gregorian Calendar) ranging from 32,768 B.C. to 32,767 A.D. Positive years - are A.D. Non-positive years are B.C. - - Year, month, and day are kept separately internally so that `Date` is - optimized for calendar-based operations. - - `Date` uses the Proleptic Gregorian Calendar, so it assumes the Gregorian - leap year calculations for its entire length. As per - $(HTTP en.wikipedia.org/wiki/ISO_8601, ISO 8601), it treats 1 B.C. as - year 0, i.e. 1 B.C. is 0, 2 B.C. is -1, etc. Use $(LREF yearBC) to use B.C. - as a positive integer with 1 B.C. being the year prior to 1 A.D. - - Year 0 is a leap year. - +/ -struct Date -{ -public: - - /++ - Throws: - $(REF DateTimeException,std,datetime,date) if the resulting - $(LREF Date) would not be valid. - - Params: - year = Year of the Gregorian Calendar. Positive values are A.D. - Non-positive values are B.C. with year 0 being the year - prior to 1 A.D. - month = Month of the year (January is 1). - day = Day of the month. - +/ - this(int year, int month, int day) @safe pure - { - enforceValid!"months"(cast(Month) month); - enforceValid!"days"(year, cast(Month) month, day); - - _year = cast(short) year; - _month = cast(Month) month; - _day = cast(ubyte) day; - } - - @safe unittest - { - import std.exception : assertNotThrown; - assert(Date(1, 1, 1) == Date.init); - - static void testDate(Date date, int year, int month, int day) - { - assert(date._year == year); - assert(date._month == month); - assert(date._day == day); - } - - testDate(Date(1999, 1 , 1), 1999, Month.jan, 1); - testDate(Date(1999, 7 , 1), 1999, Month.jul, 1); - testDate(Date(1999, 7 , 6), 1999, Month.jul, 6); - - // Test A.D. - assertThrown!DateTimeException(Date(1, 0, 1)); - assertThrown!DateTimeException(Date(1, 1, 0)); - assertThrown!DateTimeException(Date(1999, 13, 1)); - assertThrown!DateTimeException(Date(1999, 1, 32)); - assertThrown!DateTimeException(Date(1999, 2, 29)); - assertThrown!DateTimeException(Date(2000, 2, 30)); - assertThrown!DateTimeException(Date(1999, 3, 32)); - assertThrown!DateTimeException(Date(1999, 4, 31)); - assertThrown!DateTimeException(Date(1999, 5, 32)); - assertThrown!DateTimeException(Date(1999, 6, 31)); - assertThrown!DateTimeException(Date(1999, 7, 32)); - assertThrown!DateTimeException(Date(1999, 8, 32)); - assertThrown!DateTimeException(Date(1999, 9, 31)); - assertThrown!DateTimeException(Date(1999, 10, 32)); - assertThrown!DateTimeException(Date(1999, 11, 31)); - assertThrown!DateTimeException(Date(1999, 12, 32)); - - assertNotThrown!DateTimeException(Date(1999, 1, 31)); - assertNotThrown!DateTimeException(Date(1999, 2, 28)); - assertNotThrown!DateTimeException(Date(2000, 2, 29)); - assertNotThrown!DateTimeException(Date(1999, 3, 31)); - assertNotThrown!DateTimeException(Date(1999, 4, 30)); - assertNotThrown!DateTimeException(Date(1999, 5, 31)); - assertNotThrown!DateTimeException(Date(1999, 6, 30)); - assertNotThrown!DateTimeException(Date(1999, 7, 31)); - assertNotThrown!DateTimeException(Date(1999, 8, 31)); - assertNotThrown!DateTimeException(Date(1999, 9, 30)); - assertNotThrown!DateTimeException(Date(1999, 10, 31)); - assertNotThrown!DateTimeException(Date(1999, 11, 30)); - assertNotThrown!DateTimeException(Date(1999, 12, 31)); - - // Test B.C. - assertNotThrown!DateTimeException(Date(0, 1, 1)); - assertNotThrown!DateTimeException(Date(-1, 1, 1)); - assertNotThrown!DateTimeException(Date(-1, 12, 31)); - assertNotThrown!DateTimeException(Date(-1, 2, 28)); - assertNotThrown!DateTimeException(Date(-4, 2, 29)); - - assertThrown!DateTimeException(Date(-1, 2, 29)); - assertThrown!DateTimeException(Date(-2, 2, 29)); - assertThrown!DateTimeException(Date(-3, 2, 29)); - } - - - /++ - Params: - day = The Xth day of the Gregorian Calendar that the constructed - $(LREF Date) will be for. - +/ - this(int day) @safe pure nothrow @nogc - { - if (day > 0) - { - int years = (day / daysIn400Years) * 400 + 1; - day %= daysIn400Years; - - { - immutable tempYears = day / daysIn100Years; - - if (tempYears == 4) - { - years += 300; - day -= daysIn100Years * 3; - } - else - { - years += tempYears * 100; - day %= daysIn100Years; - } - } - - years += (day / daysIn4Years) * 4; - day %= daysIn4Years; - - { - immutable tempYears = day / daysInYear; - - if (tempYears == 4) - { - years += 3; - day -= daysInYear * 3; - } - else - { - years += tempYears; - day %= daysInYear; - } - } - - if (day == 0) - { - _year = cast(short)(years - 1); - _month = Month.dec; - _day = 31; - } - else - { - _year = cast(short) years; - - setDayOfYear(day); - } - } - else if (day <= 0 && -day < daysInLeapYear) - { - _year = 0; - - setDayOfYear(daysInLeapYear + day); - } - else - { - day += daysInLeapYear - 1; - int years = (day / daysIn400Years) * 400 - 1; - day %= daysIn400Years; - - { - immutable tempYears = day / daysIn100Years; - - if (tempYears == -4) - { - years -= 300; - day += daysIn100Years * 3; - } - else - { - years += tempYears * 100; - day %= daysIn100Years; - } - } - - years += (day / daysIn4Years) * 4; - day %= daysIn4Years; - - { - immutable tempYears = day / daysInYear; - - if (tempYears == -4) - { - years -= 3; - day += daysInYear * 3; - } - else - { - years += tempYears; - day %= daysInYear; - } - } - - if (day == 0) - { - _year = cast(short)(years + 1); - _month = Month.jan; - _day = 1; - } - else - { - _year = cast(short) years; - immutable newDoY = (yearIsLeapYear(_year) ? daysInLeapYear : daysInYear) + day + 1; - - setDayOfYear(newDoY); - } - } - } - - @safe unittest - { - import std.range : chain; - - // Test A.D. - foreach (gd; chain(testGregDaysBC, testGregDaysAD)) - assert(Date(gd.day) == gd.date); - } - - - /++ - Compares this $(LREF Date) with the given $(LREF Date). - - Returns: - $(BOOKTABLE, - $(TR $(TD this < rhs) $(TD < 0)) - $(TR $(TD this == rhs) $(TD 0)) - $(TR $(TD this > rhs) $(TD > 0)) - ) - +/ - int opCmp(Date rhs) const @safe pure nothrow @nogc - { - if (_year < rhs._year) - return -1; - if (_year > rhs._year) - return 1; - - if (_month < rhs._month) - return -1; - if (_month > rhs._month) - return 1; - - if (_day < rhs._day) - return -1; - if (_day > rhs._day) - return 1; - - return 0; - } - - @safe unittest - { - // Test A.D. - assert(Date(1, 1, 1).opCmp(Date.init) == 0); - - assert(Date(1999, 1, 1).opCmp(Date(1999, 1, 1)) == 0); - assert(Date(1, 7, 1).opCmp(Date(1, 7, 1)) == 0); - assert(Date(1, 1, 6).opCmp(Date(1, 1, 6)) == 0); - - assert(Date(1999, 7, 1).opCmp(Date(1999, 7, 1)) == 0); - assert(Date(1999, 7, 6).opCmp(Date(1999, 7, 6)) == 0); - - assert(Date(1, 7, 6).opCmp(Date(1, 7, 6)) == 0); - - assert(Date(1999, 7, 6).opCmp(Date(2000, 7, 6)) < 0); - assert(Date(2000, 7, 6).opCmp(Date(1999, 7, 6)) > 0); - assert(Date(1999, 7, 6).opCmp(Date(1999, 8, 6)) < 0); - assert(Date(1999, 8, 6).opCmp(Date(1999, 7, 6)) > 0); - assert(Date(1999, 7, 6).opCmp(Date(1999, 7, 7)) < 0); - assert(Date(1999, 7, 7).opCmp(Date(1999, 7, 6)) > 0); - - assert(Date(1999, 8, 7).opCmp(Date(2000, 7, 6)) < 0); - assert(Date(2000, 8, 6).opCmp(Date(1999, 7, 7)) > 0); - assert(Date(1999, 7, 7).opCmp(Date(2000, 7, 6)) < 0); - assert(Date(2000, 7, 6).opCmp(Date(1999, 7, 7)) > 0); - assert(Date(1999, 7, 7).opCmp(Date(1999, 8, 6)) < 0); - assert(Date(1999, 8, 6).opCmp(Date(1999, 7, 7)) > 0); - - // Test B.C. - assert(Date(0, 1, 1).opCmp(Date(0, 1, 1)) == 0); - assert(Date(-1, 1, 1).opCmp(Date(-1, 1, 1)) == 0); - assert(Date(-1, 7, 1).opCmp(Date(-1, 7, 1)) == 0); - assert(Date(-1, 1, 6).opCmp(Date(-1, 1, 6)) == 0); - - assert(Date(-1999, 7, 1).opCmp(Date(-1999, 7, 1)) == 0); - assert(Date(-1999, 7, 6).opCmp(Date(-1999, 7, 6)) == 0); - - assert(Date(-1, 7, 6).opCmp(Date(-1, 7, 6)) == 0); - - assert(Date(-2000, 7, 6).opCmp(Date(-1999, 7, 6)) < 0); - assert(Date(-1999, 7, 6).opCmp(Date(-2000, 7, 6)) > 0); - assert(Date(-1999, 7, 6).opCmp(Date(-1999, 8, 6)) < 0); - assert(Date(-1999, 8, 6).opCmp(Date(-1999, 7, 6)) > 0); - assert(Date(-1999, 7, 6).opCmp(Date(-1999, 7, 7)) < 0); - assert(Date(-1999, 7, 7).opCmp(Date(-1999, 7, 6)) > 0); - - assert(Date(-2000, 8, 6).opCmp(Date(-1999, 7, 7)) < 0); - assert(Date(-1999, 8, 7).opCmp(Date(-2000, 7, 6)) > 0); - assert(Date(-2000, 7, 6).opCmp(Date(-1999, 7, 7)) < 0); - assert(Date(-1999, 7, 7).opCmp(Date(-2000, 7, 6)) > 0); - assert(Date(-1999, 7, 7).opCmp(Date(-1999, 8, 6)) < 0); - assert(Date(-1999, 8, 6).opCmp(Date(-1999, 7, 7)) > 0); - - // Test Both - assert(Date(-1999, 7, 6).opCmp(Date(1999, 7, 6)) < 0); - assert(Date(1999, 7, 6).opCmp(Date(-1999, 7, 6)) > 0); - - assert(Date(-1999, 8, 6).opCmp(Date(1999, 7, 6)) < 0); - assert(Date(1999, 7, 6).opCmp(Date(-1999, 8, 6)) > 0); - - assert(Date(-1999, 7, 7).opCmp(Date(1999, 7, 6)) < 0); - assert(Date(1999, 7, 6).opCmp(Date(-1999, 7, 7)) > 0); - - assert(Date(-1999, 8, 7).opCmp(Date(1999, 7, 6)) < 0); - assert(Date(1999, 7, 6).opCmp(Date(-1999, 8, 7)) > 0); - - assert(Date(-1999, 8, 6).opCmp(Date(1999, 6, 6)) < 0); - assert(Date(1999, 6, 8).opCmp(Date(-1999, 7, 6)) > 0); - - auto date = Date(1999, 7, 6); - const cdate = Date(1999, 7, 6); - immutable idate = Date(1999, 7, 6); - assert(date.opCmp(date) == 0); - assert(date.opCmp(cdate) == 0); - assert(date.opCmp(idate) == 0); - assert(cdate.opCmp(date) == 0); - assert(cdate.opCmp(cdate) == 0); - assert(cdate.opCmp(idate) == 0); - assert(idate.opCmp(date) == 0); - assert(idate.opCmp(cdate) == 0); - assert(idate.opCmp(idate) == 0); - } - - - /++ - Year of the Gregorian Calendar. Positive numbers are A.D. Non-positive - are B.C. - +/ - @property short year() const @safe pure nothrow @nogc - { - return _year; - } - - /// - @safe unittest - { - assert(Date(1999, 7, 6).year == 1999); - assert(Date(2010, 10, 4).year == 2010); - assert(Date(-7, 4, 5).year == -7); - } - - @safe unittest - { - assert(Date.init.year == 1); - assert(Date(1999, 7, 6).year == 1999); - assert(Date(-1999, 7, 6).year == -1999); - - const cdate = Date(1999, 7, 6); - immutable idate = Date(1999, 7, 6); - assert(cdate.year == 1999); - assert(idate.year == 1999); - } - - /++ - Year of the Gregorian Calendar. Positive numbers are A.D. Non-positive - are B.C. - - Params: - year = The year to set this Date's year to. - - Throws: - $(REF DateTimeException,std,datetime,date) if the new year is not - a leap year and the resulting date would be on February 29th. - +/ - @property void year(int year) @safe pure - { - enforceValid!"days"(year, _month, _day); - _year = cast(short) year; - } - - /// - @safe unittest - { - assert(Date(1999, 7, 6).year == 1999); - assert(Date(2010, 10, 4).year == 2010); - assert(Date(-7, 4, 5).year == -7); - } - - @safe unittest - { - static void testDateInvalid(Date date, int year) - { - date.year = year; - } - - static void testDate(Date date, int year, Date expected) - { - date.year = year; - assert(date == expected); - } - - assertThrown!DateTimeException(testDateInvalid(Date(4, 2, 29), 1)); - - testDate(Date(1, 1, 1), 1999, Date(1999, 1, 1)); - testDate(Date(1, 1, 1), 0, Date(0, 1, 1)); - testDate(Date(1, 1, 1), -1999, Date(-1999, 1, 1)); - - const cdate = Date(1999, 7, 6); - immutable idate = Date(1999, 7, 6); - static assert(!__traits(compiles, cdate.year = 1999)); - static assert(!__traits(compiles, idate.year = 1999)); - } - - - /++ - Year B.C. of the Gregorian Calendar counting year 0 as 1 B.C. - - Throws: - $(REF DateTimeException,std,datetime,date) if `isAD` is true. - +/ - @property ushort yearBC() const @safe pure - { - import std.format : format; - - if (isAD) - throw new DateTimeException(format("Year %s is A.D.", _year)); - return cast(ushort)((_year * -1) + 1); - } - - /// - @safe unittest - { - assert(Date(0, 1, 1).yearBC == 1); - assert(Date(-1, 1, 1).yearBC == 2); - assert(Date(-100, 1, 1).yearBC == 101); - } - - @safe unittest - { - assertThrown!DateTimeException((Date date){date.yearBC;}(Date(1, 1, 1))); - - auto date = Date(0, 7, 6); - const cdate = Date(0, 7, 6); - immutable idate = Date(0, 7, 6); - assert(date.yearBC == 1); - assert(cdate.yearBC == 1); - assert(idate.yearBC == 1); - } - - - /++ - Year B.C. of the Gregorian Calendar counting year 0 as 1 B.C. - - Params: - year = The year B.C. to set this $(LREF Date)'s year to. - - Throws: - $(REF DateTimeException,std,datetime,date) if a non-positive value - is given. - +/ - @property void yearBC(int year) @safe pure - { - if (year <= 0) - throw new DateTimeException("The given year is not a year B.C."); - _year = cast(short)((year - 1) * -1); - } - - /// - @safe unittest - { - auto date = Date(2010, 1, 1); - date.yearBC = 1; - assert(date == Date(0, 1, 1)); - - date.yearBC = 10; - assert(date == Date(-9, 1, 1)); - } - - @safe unittest - { - assertThrown!DateTimeException((Date date){date.yearBC = -1;}(Date(1, 1, 1))); - - auto date = Date(0, 7, 6); - const cdate = Date(0, 7, 6); - immutable idate = Date(0, 7, 6); - date.yearBC = 7; - assert(date.yearBC == 7); - static assert(!__traits(compiles, cdate.yearBC = 7)); - static assert(!__traits(compiles, idate.yearBC = 7)); - } - - - /++ - Month of a Gregorian Year. - +/ - @property Month month() const @safe pure nothrow @nogc - { - return _month; - } - - /// - @safe unittest - { - assert(Date(1999, 7, 6).month == 7); - assert(Date(2010, 10, 4).month == 10); - assert(Date(-7, 4, 5).month == 4); - } - - @safe unittest - { - assert(Date.init.month == 1); - assert(Date(1999, 7, 6).month == 7); - assert(Date(-1999, 7, 6).month == 7); - - const cdate = Date(1999, 7, 6); - immutable idate = Date(1999, 7, 6); - assert(cdate.month == 7); - assert(idate.month == 7); - } - - /++ - Month of a Gregorian Year. - - Params: - month = The month to set this $(LREF Date)'s month to. - - Throws: - $(REF DateTimeException,std,datetime,date) if the given month is - not a valid month or if the current day would not be valid in the - given month. - +/ - @property void month(Month month) @safe pure - { - enforceValid!"months"(month); - enforceValid!"days"(_year, month, _day); - _month = cast(Month) month; - } - - @safe unittest - { - static void testDate(Date date, Month month, Date expected = Date.init) - { - date.month = month; - assert(expected != Date.init); - assert(date == expected); - } - - assertThrown!DateTimeException(testDate(Date(1, 1, 1), cast(Month) 0)); - assertThrown!DateTimeException(testDate(Date(1, 1, 1), cast(Month) 13)); - assertThrown!DateTimeException(testDate(Date(1, 1, 29), cast(Month) 2)); - assertThrown!DateTimeException(testDate(Date(0, 1, 30), cast(Month) 2)); - - testDate(Date(1, 1, 1), cast(Month) 7, Date(1, 7, 1)); - testDate(Date(-1, 1, 1), cast(Month) 7, Date(-1, 7, 1)); - - const cdate = Date(1999, 7, 6); - immutable idate = Date(1999, 7, 6); - static assert(!__traits(compiles, cdate.month = 7)); - static assert(!__traits(compiles, idate.month = 7)); - } - - - /++ - Day of a Gregorian Month. - +/ - @property ubyte day() const @safe pure nothrow @nogc - { - return _day; - } - - /// - @safe unittest - { - assert(Date(1999, 7, 6).day == 6); - assert(Date(2010, 10, 4).day == 4); - assert(Date(-7, 4, 5).day == 5); - } - - @safe unittest - { - import std.format : format; - import std.range : chain; - - static void test(Date date, int expected) - { - assert(date.day == expected, format("Value given: %s", date)); - } - - foreach (year; chain(testYearsBC, testYearsAD)) - { - foreach (md; testMonthDays) - test(Date(year, md.month, md.day), md.day); - } - - const cdate = Date(1999, 7, 6); - immutable idate = Date(1999, 7, 6); - assert(cdate.day == 6); - assert(idate.day == 6); - } - - /++ - Day of a Gregorian Month. - - Params: - day = The day of the month to set this $(LREF Date)'s day to. - - Throws: - $(REF DateTimeException,std,datetime,date) if the given day is not - a valid day of the current month. - +/ - @property void day(int day) @safe pure - { - enforceValid!"days"(_year, _month, day); - _day = cast(ubyte) day; - } - - @safe unittest - { - import std.exception : assertNotThrown; - - static void testDate(Date date, int day) - { - date.day = day; - } - - // Test A.D. - assertThrown!DateTimeException(testDate(Date(1, 1, 1), 0)); - assertThrown!DateTimeException(testDate(Date(1, 1, 1), 32)); - assertThrown!DateTimeException(testDate(Date(1, 2, 1), 29)); - assertThrown!DateTimeException(testDate(Date(4, 2, 1), 30)); - assertThrown!DateTimeException(testDate(Date(1, 3, 1), 32)); - assertThrown!DateTimeException(testDate(Date(1, 4, 1), 31)); - assertThrown!DateTimeException(testDate(Date(1, 5, 1), 32)); - assertThrown!DateTimeException(testDate(Date(1, 6, 1), 31)); - assertThrown!DateTimeException(testDate(Date(1, 7, 1), 32)); - assertThrown!DateTimeException(testDate(Date(1, 8, 1), 32)); - assertThrown!DateTimeException(testDate(Date(1, 9, 1), 31)); - assertThrown!DateTimeException(testDate(Date(1, 10, 1), 32)); - assertThrown!DateTimeException(testDate(Date(1, 11, 1), 31)); - assertThrown!DateTimeException(testDate(Date(1, 12, 1), 32)); - - assertNotThrown!DateTimeException(testDate(Date(1, 1, 1), 31)); - assertNotThrown!DateTimeException(testDate(Date(1, 2, 1), 28)); - assertNotThrown!DateTimeException(testDate(Date(4, 2, 1), 29)); - assertNotThrown!DateTimeException(testDate(Date(1, 3, 1), 31)); - assertNotThrown!DateTimeException(testDate(Date(1, 4, 1), 30)); - assertNotThrown!DateTimeException(testDate(Date(1, 5, 1), 31)); - assertNotThrown!DateTimeException(testDate(Date(1, 6, 1), 30)); - assertNotThrown!DateTimeException(testDate(Date(1, 7, 1), 31)); - assertNotThrown!DateTimeException(testDate(Date(1, 8, 1), 31)); - assertNotThrown!DateTimeException(testDate(Date(1, 9, 1), 30)); - assertNotThrown!DateTimeException(testDate(Date(1, 10, 1), 31)); - assertNotThrown!DateTimeException(testDate(Date(1, 11, 1), 30)); - assertNotThrown!DateTimeException(testDate(Date(1, 12, 1), 31)); - - { - auto date = Date(1, 1, 1); - date.day = 6; - assert(date == Date(1, 1, 6)); - } - - // Test B.C. - assertThrown!DateTimeException(testDate(Date(-1, 1, 1), 0)); - assertThrown!DateTimeException(testDate(Date(-1, 1, 1), 32)); - assertThrown!DateTimeException(testDate(Date(-1, 2, 1), 29)); - assertThrown!DateTimeException(testDate(Date(0, 2, 1), 30)); - assertThrown!DateTimeException(testDate(Date(-1, 3, 1), 32)); - assertThrown!DateTimeException(testDate(Date(-1, 4, 1), 31)); - assertThrown!DateTimeException(testDate(Date(-1, 5, 1), 32)); - assertThrown!DateTimeException(testDate(Date(-1, 6, 1), 31)); - assertThrown!DateTimeException(testDate(Date(-1, 7, 1), 32)); - assertThrown!DateTimeException(testDate(Date(-1, 8, 1), 32)); - assertThrown!DateTimeException(testDate(Date(-1, 9, 1), 31)); - assertThrown!DateTimeException(testDate(Date(-1, 10, 1), 32)); - assertThrown!DateTimeException(testDate(Date(-1, 11, 1), 31)); - assertThrown!DateTimeException(testDate(Date(-1, 12, 1), 32)); - - assertNotThrown!DateTimeException(testDate(Date(-1, 1, 1), 31)); - assertNotThrown!DateTimeException(testDate(Date(-1, 2, 1), 28)); - assertNotThrown!DateTimeException(testDate(Date(0, 2, 1), 29)); - assertNotThrown!DateTimeException(testDate(Date(-1, 3, 1), 31)); - assertNotThrown!DateTimeException(testDate(Date(-1, 4, 1), 30)); - assertNotThrown!DateTimeException(testDate(Date(-1, 5, 1), 31)); - assertNotThrown!DateTimeException(testDate(Date(-1, 6, 1), 30)); - assertNotThrown!DateTimeException(testDate(Date(-1, 7, 1), 31)); - assertNotThrown!DateTimeException(testDate(Date(-1, 8, 1), 31)); - assertNotThrown!DateTimeException(testDate(Date(-1, 9, 1), 30)); - assertNotThrown!DateTimeException(testDate(Date(-1, 10, 1), 31)); - assertNotThrown!DateTimeException(testDate(Date(-1, 11, 1), 30)); - assertNotThrown!DateTimeException(testDate(Date(-1, 12, 1), 31)); - - { - auto date = Date(-1, 1, 1); - date.day = 6; - assert(date == Date(-1, 1, 6)); - } - - const cdate = Date(1999, 7, 6); - immutable idate = Date(1999, 7, 6); - static assert(!__traits(compiles, cdate.day = 6)); - static assert(!__traits(compiles, idate.day = 6)); - } - - - /++ - Adds the given number of years or months to this $(LREF Date), mutating - it. A negative number will subtract. - - Note that if day overflow is allowed, and the date with the adjusted - year/month overflows the number of days in the new month, then the month - will be incremented by one, and the day set to the number of days - overflowed. (e.g. if the day were 31 and the new month were June, then - the month would be incremented to July, and the new day would be 1). If - day overflow is not allowed, then the day will be set to the last valid - day in the month (e.g. June 31st would become June 30th). - - Params: - units = The type of units to add ("years" or "months"). - value = The number of months or years to add to this - $(LREF Date). - allowOverflow = Whether the day should be allowed to overflow, - causing the month to increment. - - Returns: - A reference to the `Date` (`this`). - +/ - @safe pure nothrow @nogc - ref Date add(string units)(long value, AllowDayOverflow allowOverflow = AllowDayOverflow.yes) - if (units == "years") - { - _year += value; - - if (_month == Month.feb && _day == 29 && !yearIsLeapYear(_year)) - { - if (allowOverflow == AllowDayOverflow.yes) - { - _month = Month.mar; - _day = 1; - } - else - _day = 28; - } - - return this; - } - - /// - @safe unittest - { - auto d1 = Date(2010, 1, 1); - d1.add!"months"(11); - assert(d1 == Date(2010, 12, 1)); - - auto d2 = Date(2010, 1, 1); - d2.add!"months"(-11); - assert(d2 == Date(2009, 2, 1)); - - auto d3 = Date(2000, 2, 29); - d3.add!"years"(1); - assert(d3 == Date(2001, 3, 1)); - - auto d4 = Date(2000, 2, 29); - d4.add!"years"(1, AllowDayOverflow.no); - assert(d4 == Date(2001, 2, 28)); - } - - // Test add!"years"() with AllowDayOverflow.yes - @safe unittest - { - // Test A.D. - { - auto date = Date(1999, 7, 6); - date.add!"years"(7); - assert(date == Date(2006, 7, 6)); - date.add!"years"(-9); - assert(date == Date(1997, 7, 6)); - } - - { - auto date = Date(1999, 2, 28); - date.add!"years"(1); - assert(date == Date(2000, 2, 28)); - } - - { - auto date = Date(2000, 2, 29); - date.add!"years"(-1); - assert(date == Date(1999, 3, 1)); - } - - // Test B.C. - { - auto date = Date(-1999, 7, 6); - date.add!"years"(-7); - assert(date == Date(-2006, 7, 6)); - date.add!"years"(9); - assert(date == Date(-1997, 7, 6)); - } - - { - auto date = Date(-1999, 2, 28); - date.add!"years"(-1); - assert(date == Date(-2000, 2, 28)); - } - - { - auto date = Date(-2000, 2, 29); - date.add!"years"(1); - assert(date == Date(-1999, 3, 1)); - } - - // Test Both - { - auto date = Date(4, 7, 6); - date.add!"years"(-5); - assert(date == Date(-1, 7, 6)); - date.add!"years"(5); - assert(date == Date(4, 7, 6)); - } - - { - auto date = Date(-4, 7, 6); - date.add!"years"(5); - assert(date == Date(1, 7, 6)); - date.add!"years"(-5); - assert(date == Date(-4, 7, 6)); - } - - { - auto date = Date(4, 7, 6); - date.add!"years"(-8); - assert(date == Date(-4, 7, 6)); - date.add!"years"(8); - assert(date == Date(4, 7, 6)); - } - - { - auto date = Date(-4, 7, 6); - date.add!"years"(8); - assert(date == Date(4, 7, 6)); - date.add!"years"(-8); - assert(date == Date(-4, 7, 6)); - } - - { - auto date = Date(-4, 2, 29); - date.add!"years"(5); - assert(date == Date(1, 3, 1)); - } - - { - auto date = Date(4, 2, 29); - date.add!"years"(-5); - assert(date == Date(-1, 3, 1)); - } - - { - auto date = Date(4, 2, 29); - date.add!"years"(-5).add!"years"(7); - assert(date == Date(6, 3, 1)); - } - - const cdate = Date(1999, 7, 6); - immutable idate = Date(1999, 7, 6); - static assert(!__traits(compiles, cdate.add!"years"(7))); - static assert(!__traits(compiles, idate.add!"years"(7))); - } - - // Test add!"years"() with AllowDayOverflow.no - @safe unittest - { - // Test A.D. - { - auto date = Date(1999, 7, 6); - date.add!"years"(7, AllowDayOverflow.no); - assert(date == Date(2006, 7, 6)); - date.add!"years"(-9, AllowDayOverflow.no); - assert(date == Date(1997, 7, 6)); - } - - { - auto date = Date(1999, 2, 28); - date.add!"years"(1, AllowDayOverflow.no); - assert(date == Date(2000, 2, 28)); - } - - { - auto date = Date(2000, 2, 29); - date.add!"years"(-1, AllowDayOverflow.no); - assert(date == Date(1999, 2, 28)); - } - - // Test B.C. - { - auto date = Date(-1999, 7, 6); - date.add!"years"(-7, AllowDayOverflow.no); - assert(date == Date(-2006, 7, 6)); - date.add!"years"(9, AllowDayOverflow.no); - assert(date == Date(-1997, 7, 6)); - } - - { - auto date = Date(-1999, 2, 28); - date.add!"years"(-1, AllowDayOverflow.no); - assert(date == Date(-2000, 2, 28)); - } - - { - auto date = Date(-2000, 2, 29); - date.add!"years"(1, AllowDayOverflow.no); - assert(date == Date(-1999, 2, 28)); - } - - // Test Both - { - auto date = Date(4, 7, 6); - date.add!"years"(-5, AllowDayOverflow.no); - assert(date == Date(-1, 7, 6)); - date.add!"years"(5, AllowDayOverflow.no); - assert(date == Date(4, 7, 6)); - } - - { - auto date = Date(-4, 7, 6); - date.add!"years"(5, AllowDayOverflow.no); - assert(date == Date(1, 7, 6)); - date.add!"years"(-5, AllowDayOverflow.no); - assert(date == Date(-4, 7, 6)); - } - - { - auto date = Date(4, 7, 6); - date.add!"years"(-8, AllowDayOverflow.no); - assert(date == Date(-4, 7, 6)); - date.add!"years"(8, AllowDayOverflow.no); - assert(date == Date(4, 7, 6)); - } - - { - auto date = Date(-4, 7, 6); - date.add!"years"(8, AllowDayOverflow.no); - assert(date == Date(4, 7, 6)); - date.add!"years"(-8, AllowDayOverflow.no); - assert(date == Date(-4, 7, 6)); - } - - { - auto date = Date(-4, 2, 29); - date.add!"years"(5, AllowDayOverflow.no); - assert(date == Date(1, 2, 28)); - } - - { - auto date = Date(4, 2, 29); - date.add!"years"(-5, AllowDayOverflow.no); - assert(date == Date(-1, 2, 28)); - } - - { - auto date = Date(4, 2, 29); - date.add!"years"(-5, AllowDayOverflow.no).add!"years"(7, AllowDayOverflow.no); - assert(date == Date(6, 2, 28)); - } - } - - - // Shares documentation with "years" version. - @safe pure nothrow @nogc - ref Date add(string units)(long months, AllowDayOverflow allowOverflow = AllowDayOverflow.yes) - if (units == "months") - { - auto years = months / 12; - months %= 12; - auto newMonth = _month + months; - - if (months < 0) - { - if (newMonth < 1) - { - newMonth += 12; - --years; - } - } - else if (newMonth > 12) - { - newMonth -= 12; - ++years; - } - - _year += years; - _month = cast(Month) newMonth; - - immutable currMaxDay = maxDay(_year, _month); - immutable overflow = _day - currMaxDay; - - if (overflow > 0) - { - if (allowOverflow == AllowDayOverflow.yes) - { - ++_month; - _day = cast(ubyte) overflow; - } - else - _day = cast(ubyte) currMaxDay; - } - - return this; - } - - // Test add!"months"() with AllowDayOverflow.yes - @safe unittest - { - // Test A.D. - { - auto date = Date(1999, 7, 6); - date.add!"months"(3); - assert(date == Date(1999, 10, 6)); - date.add!"months"(-4); - assert(date == Date(1999, 6, 6)); - } - - { - auto date = Date(1999, 7, 6); - date.add!"months"(6); - assert(date == Date(2000, 1, 6)); - date.add!"months"(-6); - assert(date == Date(1999, 7, 6)); - } - - { - auto date = Date(1999, 7, 6); - date.add!"months"(27); - assert(date == Date(2001, 10, 6)); - date.add!"months"(-28); - assert(date == Date(1999, 6, 6)); - } - - { - auto date = Date(1999, 5, 31); - date.add!"months"(1); - assert(date == Date(1999, 7, 1)); - } - - { - auto date = Date(1999, 5, 31); - date.add!"months"(-1); - assert(date == Date(1999, 5, 1)); - } - - { - auto date = Date(1999, 2, 28); - date.add!"months"(12); - assert(date == Date(2000, 2, 28)); - } - - { - auto date = Date(2000, 2, 29); - date.add!"months"(12); - assert(date == Date(2001, 3, 1)); - } - - { - auto date = Date(1999, 7, 31); - date.add!"months"(1); - assert(date == Date(1999, 8, 31)); - date.add!"months"(1); - assert(date == Date(1999, 10, 1)); - } - - { - auto date = Date(1998, 8, 31); - date.add!"months"(13); - assert(date == Date(1999, 10, 1)); - date.add!"months"(-13); - assert(date == Date(1998, 9, 1)); - } - - { - auto date = Date(1997, 12, 31); - date.add!"months"(13); - assert(date == Date(1999, 1, 31)); - date.add!"months"(-13); - assert(date == Date(1997, 12, 31)); - } - - { - auto date = Date(1997, 12, 31); - date.add!"months"(14); - assert(date == Date(1999, 3, 3)); - date.add!"months"(-14); - assert(date == Date(1998, 1, 3)); - } - - { - auto date = Date(1998, 12, 31); - date.add!"months"(14); - assert(date == Date(2000, 3, 2)); - date.add!"months"(-14); - assert(date == Date(1999, 1, 2)); - } - - { - auto date = Date(1999, 12, 31); - date.add!"months"(14); - assert(date == Date(2001, 3, 3)); - date.add!"months"(-14); - assert(date == Date(2000, 1, 3)); - } - - // Test B.C. - { - auto date = Date(-1999, 7, 6); - date.add!"months"(3); - assert(date == Date(-1999, 10, 6)); - date.add!"months"(-4); - assert(date == Date(-1999, 6, 6)); - } - - { - auto date = Date(-1999, 7, 6); - date.add!"months"(6); - assert(date == Date(-1998, 1, 6)); - date.add!"months"(-6); - assert(date == Date(-1999, 7, 6)); - } - - { - auto date = Date(-1999, 7, 6); - date.add!"months"(-27); - assert(date == Date(-2001, 4, 6)); - date.add!"months"(28); - assert(date == Date(-1999, 8, 6)); - } - - { - auto date = Date(-1999, 5, 31); - date.add!"months"(1); - assert(date == Date(-1999, 7, 1)); - } - - { - auto date = Date(-1999, 5, 31); - date.add!"months"(-1); - assert(date == Date(-1999, 5, 1)); - } - - { - auto date = Date(-1999, 2, 28); - date.add!"months"(-12); - assert(date == Date(-2000, 2, 28)); - } - - { - auto date = Date(-2000, 2, 29); - date.add!"months"(-12); - assert(date == Date(-2001, 3, 1)); - } - - { - auto date = Date(-1999, 7, 31); - date.add!"months"(1); - assert(date == Date(-1999, 8, 31)); - date.add!"months"(1); - assert(date == Date(-1999, 10, 1)); - } - - { - auto date = Date(-1998, 8, 31); - date.add!"months"(13); - assert(date == Date(-1997, 10, 1)); - date.add!"months"(-13); - assert(date == Date(-1998, 9, 1)); - } - - { - auto date = Date(-1997, 12, 31); - date.add!"months"(13); - assert(date == Date(-1995, 1, 31)); - date.add!"months"(-13); - assert(date == Date(-1997, 12, 31)); - } - - { - auto date = Date(-1997, 12, 31); - date.add!"months"(14); - assert(date == Date(-1995, 3, 3)); - date.add!"months"(-14); - assert(date == Date(-1996, 1, 3)); - } - - { - auto date = Date(-2002, 12, 31); - date.add!"months"(14); - assert(date == Date(-2000, 3, 2)); - date.add!"months"(-14); - assert(date == Date(-2001, 1, 2)); - } - - { - auto date = Date(-2001, 12, 31); - date.add!"months"(14); - assert(date == Date(-1999, 3, 3)); - date.add!"months"(-14); - assert(date == Date(-2000, 1, 3)); - } - - // Test Both - { - auto date = Date(1, 1, 1); - date.add!"months"(-1); - assert(date == Date(0, 12, 1)); - date.add!"months"(1); - assert(date == Date(1, 1, 1)); - } - - { - auto date = Date(4, 1, 1); - date.add!"months"(-48); - assert(date == Date(0, 1, 1)); - date.add!"months"(48); - assert(date == Date(4, 1, 1)); - } - - { - auto date = Date(4, 3, 31); - date.add!"months"(-49); - assert(date == Date(0, 3, 2)); - date.add!"months"(49); - assert(date == Date(4, 4, 2)); - } - - { - auto date = Date(4, 3, 31); - date.add!"months"(-85); - assert(date == Date(-3, 3, 3)); - date.add!"months"(85); - assert(date == Date(4, 4, 3)); - } - - { - auto date = Date(-3, 3, 31); - date.add!"months"(85).add!"months"(-83); - assert(date == Date(-3, 6, 1)); - } - - const cdate = Date(1999, 7, 6); - immutable idate = Date(1999, 7, 6); - static assert(!__traits(compiles, cdate.add!"months"(3))); - static assert(!__traits(compiles, idate.add!"months"(3))); - } - - // Test add!"months"() with AllowDayOverflow.no - @safe unittest - { - // Test A.D. - { - auto date = Date(1999, 7, 6); - date.add!"months"(3, AllowDayOverflow.no); - assert(date == Date(1999, 10, 6)); - date.add!"months"(-4, AllowDayOverflow.no); - assert(date == Date(1999, 6, 6)); - } - - { - auto date = Date(1999, 7, 6); - date.add!"months"(6, AllowDayOverflow.no); - assert(date == Date(2000, 1, 6)); - date.add!"months"(-6, AllowDayOverflow.no); - assert(date == Date(1999, 7, 6)); - } - - { - auto date = Date(1999, 7, 6); - date.add!"months"(27, AllowDayOverflow.no); - assert(date == Date(2001, 10, 6)); - date.add!"months"(-28, AllowDayOverflow.no); - assert(date == Date(1999, 6, 6)); - } - - { - auto date = Date(1999, 5, 31); - date.add!"months"(1, AllowDayOverflow.no); - assert(date == Date(1999, 6, 30)); - } - - { - auto date = Date(1999, 5, 31); - date.add!"months"(-1, AllowDayOverflow.no); - assert(date == Date(1999, 4, 30)); - } - - { - auto date = Date(1999, 2, 28); - date.add!"months"(12, AllowDayOverflow.no); - assert(date == Date(2000, 2, 28)); - } - - { - auto date = Date(2000, 2, 29); - date.add!"months"(12, AllowDayOverflow.no); - assert(date == Date(2001, 2, 28)); - } - - { - auto date = Date(1999, 7, 31); - date.add!"months"(1, AllowDayOverflow.no); - assert(date == Date(1999, 8, 31)); - date.add!"months"(1, AllowDayOverflow.no); - assert(date == Date(1999, 9, 30)); - } - - { - auto date = Date(1998, 8, 31); - date.add!"months"(13, AllowDayOverflow.no); - assert(date == Date(1999, 9, 30)); - date.add!"months"(-13, AllowDayOverflow.no); - assert(date == Date(1998, 8, 30)); - } - - { - auto date = Date(1997, 12, 31); - date.add!"months"(13, AllowDayOverflow.no); - assert(date == Date(1999, 1, 31)); - date.add!"months"(-13, AllowDayOverflow.no); - assert(date == Date(1997, 12, 31)); - } - - { - auto date = Date(1997, 12, 31); - date.add!"months"(14, AllowDayOverflow.no); - assert(date == Date(1999, 2, 28)); - date.add!"months"(-14, AllowDayOverflow.no); - assert(date == Date(1997, 12, 28)); - } - - { - auto date = Date(1998, 12, 31); - date.add!"months"(14, AllowDayOverflow.no); - assert(date == Date(2000, 2, 29)); - date.add!"months"(-14, AllowDayOverflow.no); - assert(date == Date(1998, 12, 29)); - } - - { - auto date = Date(1999, 12, 31); - date.add!"months"(14, AllowDayOverflow.no); - assert(date == Date(2001, 2, 28)); - date.add!"months"(-14, AllowDayOverflow.no); - assert(date == Date(1999, 12, 28)); - } - - // Test B.C. - { - auto date = Date(-1999, 7, 6); - date.add!"months"(3, AllowDayOverflow.no); - assert(date == Date(-1999, 10, 6)); - date.add!"months"(-4, AllowDayOverflow.no); - assert(date == Date(-1999, 6, 6)); - } - - { - auto date = Date(-1999, 7, 6); - date.add!"months"(6, AllowDayOverflow.no); - assert(date == Date(-1998, 1, 6)); - date.add!"months"(-6, AllowDayOverflow.no); - assert(date == Date(-1999, 7, 6)); - } - - { - auto date = Date(-1999, 7, 6); - date.add!"months"(-27, AllowDayOverflow.no); - assert(date == Date(-2001, 4, 6)); - date.add!"months"(28, AllowDayOverflow.no); - assert(date == Date(-1999, 8, 6)); - } - - { - auto date = Date(-1999, 5, 31); - date.add!"months"(1, AllowDayOverflow.no); - assert(date == Date(-1999, 6, 30)); - } - - { - auto date = Date(-1999, 5, 31); - date.add!"months"(-1, AllowDayOverflow.no); - assert(date == Date(-1999, 4, 30)); - } - - { - auto date = Date(-1999, 2, 28); - date.add!"months"(-12, AllowDayOverflow.no); - assert(date == Date(-2000, 2, 28)); - } - - { - auto date = Date(-2000, 2, 29); - date.add!"months"(-12, AllowDayOverflow.no); - assert(date == Date(-2001, 2, 28)); - } - - { - auto date = Date(-1999, 7, 31); - date.add!"months"(1, AllowDayOverflow.no); - assert(date == Date(-1999, 8, 31)); - date.add!"months"(1, AllowDayOverflow.no); - assert(date == Date(-1999, 9, 30)); - } - - { - auto date = Date(-1998, 8, 31); - date.add!"months"(13, AllowDayOverflow.no); - assert(date == Date(-1997, 9, 30)); - date.add!"months"(-13, AllowDayOverflow.no); - assert(date == Date(-1998, 8, 30)); - } - - { - auto date = Date(-1997, 12, 31); - date.add!"months"(13, AllowDayOverflow.no); - assert(date == Date(-1995, 1, 31)); - date.add!"months"(-13, AllowDayOverflow.no); - assert(date == Date(-1997, 12, 31)); - } - - { - auto date = Date(-1997, 12, 31); - date.add!"months"(14, AllowDayOverflow.no); - assert(date == Date(-1995, 2, 28)); - date.add!"months"(-14, AllowDayOverflow.no); - assert(date == Date(-1997, 12, 28)); - } - - { - auto date = Date(-2002, 12, 31); - date.add!"months"(14, AllowDayOverflow.no); - assert(date == Date(-2000, 2, 29)); - date.add!"months"(-14, AllowDayOverflow.no); - assert(date == Date(-2002, 12, 29)); - } - - { - auto date = Date(-2001, 12, 31); - date.add!"months"(14, AllowDayOverflow.no); - assert(date == Date(-1999, 2, 28)); - date.add!"months"(-14, AllowDayOverflow.no); - assert(date == Date(-2001, 12, 28)); - } - - // Test Both - { - auto date = Date(1, 1, 1); - date.add!"months"(-1, AllowDayOverflow.no); - assert(date == Date(0, 12, 1)); - date.add!"months"(1, AllowDayOverflow.no); - assert(date == Date(1, 1, 1)); - } - - { - auto date = Date(4, 1, 1); - date.add!"months"(-48, AllowDayOverflow.no); - assert(date == Date(0, 1, 1)); - date.add!"months"(48, AllowDayOverflow.no); - assert(date == Date(4, 1, 1)); - } - - { - auto date = Date(4, 3, 31); - date.add!"months"(-49, AllowDayOverflow.no); - assert(date == Date(0, 2, 29)); - date.add!"months"(49, AllowDayOverflow.no); - assert(date == Date(4, 3, 29)); - } - - { - auto date = Date(4, 3, 31); - date.add!"months"(-85, AllowDayOverflow.no); - assert(date == Date(-3, 2, 28)); - date.add!"months"(85, AllowDayOverflow.no); - assert(date == Date(4, 3, 28)); - } - - { - auto date = Date(-3, 3, 31); - date.add!"months"(85, AllowDayOverflow.no).add!"months"(-83, AllowDayOverflow.no); - assert(date == Date(-3, 5, 30)); - } - } - - - /++ - Adds the given number of years or months to this $(LREF Date), mutating - it. A negative number will subtract. - - The difference between rolling and adding is that rolling does not - affect larger units. Rolling a $(LREF Date) 12 months gets - the exact same $(LREF Date). However, the days can still be affected due - to the differing number of days in each month. - - Because there are no units larger than years, there is no difference - between adding and rolling years. - - Params: - units = The type of units to add ("years" or "months"). - value = The number of months or years to add to this - $(LREF Date). - allowOverflow = Whether the day should be allowed to overflow, - causing the month to increment. - - Returns: - A reference to the `Date` (`this`). - +/ - @safe pure nothrow @nogc - ref Date roll(string units)(long value, AllowDayOverflow allowOverflow = AllowDayOverflow.yes) - if (units == "years") - { - return add!"years"(value, allowOverflow); - } - - /// - @safe unittest - { - auto d1 = Date(2010, 1, 1); - d1.roll!"months"(1); - assert(d1 == Date(2010, 2, 1)); - - auto d2 = Date(2010, 1, 1); - d2.roll!"months"(-1); - assert(d2 == Date(2010, 12, 1)); - - auto d3 = Date(1999, 1, 29); - d3.roll!"months"(1); - assert(d3 == Date(1999, 3, 1)); - - auto d4 = Date(1999, 1, 29); - d4.roll!"months"(1, AllowDayOverflow.no); - assert(d4 == Date(1999, 2, 28)); - - auto d5 = Date(2000, 2, 29); - d5.roll!"years"(1); - assert(d5 == Date(2001, 3, 1)); - - auto d6 = Date(2000, 2, 29); - d6.roll!"years"(1, AllowDayOverflow.no); - assert(d6 == Date(2001, 2, 28)); - } - - @safe unittest - { - const cdate = Date(1999, 7, 6); - immutable idate = Date(1999, 7, 6); - static assert(!__traits(compiles, cdate.roll!"years"(3))); - static assert(!__traits(compiles, idate.rolYears(3))); - } - - - // Shares documentation with "years" version. - @safe pure nothrow @nogc - ref Date roll(string units)(long months, AllowDayOverflow allowOverflow = AllowDayOverflow.yes) - if (units == "months") - { - months %= 12; - auto newMonth = _month + months; - - if (months < 0) - { - if (newMonth < 1) - newMonth += 12; - } - else - { - if (newMonth > 12) - newMonth -= 12; - } - - _month = cast(Month) newMonth; - - immutable currMaxDay = maxDay(_year, _month); - immutable overflow = _day - currMaxDay; - - if (overflow > 0) - { - if (allowOverflow == AllowDayOverflow.yes) - { - ++_month; - _day = cast(ubyte) overflow; - } - else - _day = cast(ubyte) currMaxDay; - } - - return this; - } - - // Test roll!"months"() with AllowDayOverflow.yes - @safe unittest - { - // Test A.D. - { - auto date = Date(1999, 7, 6); - date.roll!"months"(3); - assert(date == Date(1999, 10, 6)); - date.roll!"months"(-4); - assert(date == Date(1999, 6, 6)); - } - - { - auto date = Date(1999, 7, 6); - date.roll!"months"(6); - assert(date == Date(1999, 1, 6)); - date.roll!"months"(-6); - assert(date == Date(1999, 7, 6)); - } - - { - auto date = Date(1999, 7, 6); - date.roll!"months"(27); - assert(date == Date(1999, 10, 6)); - date.roll!"months"(-28); - assert(date == Date(1999, 6, 6)); - } - - { - auto date = Date(1999, 5, 31); - date.roll!"months"(1); - assert(date == Date(1999, 7, 1)); - } - - { - auto date = Date(1999, 5, 31); - date.roll!"months"(-1); - assert(date == Date(1999, 5, 1)); - } - - { - auto date = Date(1999, 2, 28); - date.roll!"months"(12); - assert(date == Date(1999, 2, 28)); - } - - { - auto date = Date(2000, 2, 29); - date.roll!"months"(12); - assert(date == Date(2000, 2, 29)); - } - - { - auto date = Date(1999, 7, 31); - date.roll!"months"(1); - assert(date == Date(1999, 8, 31)); - date.roll!"months"(1); - assert(date == Date(1999, 10, 1)); - } - - { - auto date = Date(1998, 8, 31); - date.roll!"months"(13); - assert(date == Date(1998, 10, 1)); - date.roll!"months"(-13); - assert(date == Date(1998, 9, 1)); - } - - { - auto date = Date(1997, 12, 31); - date.roll!"months"(13); - assert(date == Date(1997, 1, 31)); - date.roll!"months"(-13); - assert(date == Date(1997, 12, 31)); - } - - { - auto date = Date(1997, 12, 31); - date.roll!"months"(14); - assert(date == Date(1997, 3, 3)); - date.roll!"months"(-14); - assert(date == Date(1997, 1, 3)); - } - - { - auto date = Date(1998, 12, 31); - date.roll!"months"(14); - assert(date == Date(1998, 3, 3)); - date.roll!"months"(-14); - assert(date == Date(1998, 1, 3)); - } - - { - auto date = Date(1999, 12, 31); - date.roll!"months"(14); - assert(date == Date(1999, 3, 3)); - date.roll!"months"(-14); - assert(date == Date(1999, 1, 3)); - } - - // Test B.C. - { - auto date = Date(-1999, 7, 6); - date.roll!"months"(3); - assert(date == Date(-1999, 10, 6)); - date.roll!"months"(-4); - assert(date == Date(-1999, 6, 6)); - } - - { - auto date = Date(-1999, 7, 6); - date.roll!"months"(6); - assert(date == Date(-1999, 1, 6)); - date.roll!"months"(-6); - assert(date == Date(-1999, 7, 6)); - } - - { - auto date = Date(-1999, 7, 6); - date.roll!"months"(-27); - assert(date == Date(-1999, 4, 6)); - date.roll!"months"(28); - assert(date == Date(-1999, 8, 6)); - } - - { - auto date = Date(-1999, 5, 31); - date.roll!"months"(1); - assert(date == Date(-1999, 7, 1)); - } - - { - auto date = Date(-1999, 5, 31); - date.roll!"months"(-1); - assert(date == Date(-1999, 5, 1)); - } - - { - auto date = Date(-1999, 2, 28); - date.roll!"months"(-12); - assert(date == Date(-1999, 2, 28)); - } - - { - auto date = Date(-2000, 2, 29); - date.roll!"months"(-12); - assert(date == Date(-2000, 2, 29)); - } - - { - auto date = Date(-1999, 7, 31); - date.roll!"months"(1); - assert(date == Date(-1999, 8, 31)); - date.roll!"months"(1); - assert(date == Date(-1999, 10, 1)); - } - - { - auto date = Date(-1998, 8, 31); - date.roll!"months"(13); - assert(date == Date(-1998, 10, 1)); - date.roll!"months"(-13); - assert(date == Date(-1998, 9, 1)); - } - - { - auto date = Date(-1997, 12, 31); - date.roll!"months"(13); - assert(date == Date(-1997, 1, 31)); - date.roll!"months"(-13); - assert(date == Date(-1997, 12, 31)); - } - - { - auto date = Date(-1997, 12, 31); - date.roll!"months"(14); - assert(date == Date(-1997, 3, 3)); - date.roll!"months"(-14); - assert(date == Date(-1997, 1, 3)); - } - - { - auto date = Date(-2002, 12, 31); - date.roll!"months"(14); - assert(date == Date(-2002, 3, 3)); - date.roll!"months"(-14); - assert(date == Date(-2002, 1, 3)); - } - - { - auto date = Date(-2001, 12, 31); - date.roll!"months"(14); - assert(date == Date(-2001, 3, 3)); - date.roll!"months"(-14); - assert(date == Date(-2001, 1, 3)); - } - - // Test Both - { - auto date = Date(1, 1, 1); - date.roll!"months"(-1); - assert(date == Date(1, 12, 1)); - date.roll!"months"(1); - assert(date == Date(1, 1, 1)); - } - - { - auto date = Date(4, 1, 1); - date.roll!"months"(-48); - assert(date == Date(4, 1, 1)); - date.roll!"months"(48); - assert(date == Date(4, 1, 1)); - } - - { - auto date = Date(4, 3, 31); - date.roll!"months"(-49); - assert(date == Date(4, 3, 2)); - date.roll!"months"(49); - assert(date == Date(4, 4, 2)); - } - - { - auto date = Date(4, 3, 31); - date.roll!"months"(-85); - assert(date == Date(4, 3, 2)); - date.roll!"months"(85); - assert(date == Date(4, 4, 2)); - } - - { - auto date = Date(-1, 1, 1); - date.roll!"months"(-1); - assert(date == Date(-1, 12, 1)); - date.roll!"months"(1); - assert(date == Date(-1, 1, 1)); - } - - { - auto date = Date(-4, 1, 1); - date.roll!"months"(-48); - assert(date == Date(-4, 1, 1)); - date.roll!"months"(48); - assert(date == Date(-4, 1, 1)); - } - - { - auto date = Date(-4, 3, 31); - date.roll!"months"(-49); - assert(date == Date(-4, 3, 2)); - date.roll!"months"(49); - assert(date == Date(-4, 4, 2)); - } - - { - auto date = Date(-4, 3, 31); - date.roll!"months"(-85); - assert(date == Date(-4, 3, 2)); - date.roll!"months"(85); - assert(date == Date(-4, 4, 2)); - } - - { - auto date = Date(-3, 3, 31); - date.roll!"months"(85).roll!"months"(-83); - assert(date == Date(-3, 6, 1)); - } - - const cdate = Date(1999, 7, 6); - immutable idate = Date(1999, 7, 6); - static assert(!__traits(compiles, cdate.roll!"months"(3))); - static assert(!__traits(compiles, idate.roll!"months"(3))); - } - - // Test roll!"months"() with AllowDayOverflow.no - @safe unittest - { - // Test A.D. - { - auto date = Date(1999, 7, 6); - date.roll!"months"(3, AllowDayOverflow.no); - assert(date == Date(1999, 10, 6)); - date.roll!"months"(-4, AllowDayOverflow.no); - assert(date == Date(1999, 6, 6)); - } - - { - auto date = Date(1999, 7, 6); - date.roll!"months"(6, AllowDayOverflow.no); - assert(date == Date(1999, 1, 6)); - date.roll!"months"(-6, AllowDayOverflow.no); - assert(date == Date(1999, 7, 6)); - } - - { - auto date = Date(1999, 7, 6); - date.roll!"months"(27, AllowDayOverflow.no); - assert(date == Date(1999, 10, 6)); - date.roll!"months"(-28, AllowDayOverflow.no); - assert(date == Date(1999, 6, 6)); - } - - { - auto date = Date(1999, 5, 31); - date.roll!"months"(1, AllowDayOverflow.no); - assert(date == Date(1999, 6, 30)); - } - - { - auto date = Date(1999, 5, 31); - date.roll!"months"(-1, AllowDayOverflow.no); - assert(date == Date(1999, 4, 30)); - } - - { - auto date = Date(1999, 2, 28); - date.roll!"months"(12, AllowDayOverflow.no); - assert(date == Date(1999, 2, 28)); - } - - { - auto date = Date(2000, 2, 29); - date.roll!"months"(12, AllowDayOverflow.no); - assert(date == Date(2000, 2, 29)); - } - - { - auto date = Date(1999, 7, 31); - date.roll!"months"(1, AllowDayOverflow.no); - assert(date == Date(1999, 8, 31)); - date.roll!"months"(1, AllowDayOverflow.no); - assert(date == Date(1999, 9, 30)); - } - - { - auto date = Date(1998, 8, 31); - date.roll!"months"(13, AllowDayOverflow.no); - assert(date == Date(1998, 9, 30)); - date.roll!"months"(-13, AllowDayOverflow.no); - assert(date == Date(1998, 8, 30)); - } - - { - auto date = Date(1997, 12, 31); - date.roll!"months"(13, AllowDayOverflow.no); - assert(date == Date(1997, 1, 31)); - date.roll!"months"(-13, AllowDayOverflow.no); - assert(date == Date(1997, 12, 31)); - } - - { - auto date = Date(1997, 12, 31); - date.roll!"months"(14, AllowDayOverflow.no); - assert(date == Date(1997, 2, 28)); - date.roll!"months"(-14, AllowDayOverflow.no); - assert(date == Date(1997, 12, 28)); - } - - { - auto date = Date(1998, 12, 31); - date.roll!"months"(14, AllowDayOverflow.no); - assert(date == Date(1998, 2, 28)); - date.roll!"months"(-14, AllowDayOverflow.no); - assert(date == Date(1998, 12, 28)); - } - - { - auto date = Date(1999, 12, 31); - date.roll!"months"(14, AllowDayOverflow.no); - assert(date == Date(1999, 2, 28)); - date.roll!"months"(-14, AllowDayOverflow.no); - assert(date == Date(1999, 12, 28)); - } - - // Test B.C. - { - auto date = Date(-1999, 7, 6); - date.roll!"months"(3, AllowDayOverflow.no); - assert(date == Date(-1999, 10, 6)); - date.roll!"months"(-4, AllowDayOverflow.no); - assert(date == Date(-1999, 6, 6)); - } - - { - auto date = Date(-1999, 7, 6); - date.roll!"months"(6, AllowDayOverflow.no); - assert(date == Date(-1999, 1, 6)); - date.roll!"months"(-6, AllowDayOverflow.no); - assert(date == Date(-1999, 7, 6)); - } - - { - auto date = Date(-1999, 7, 6); - date.roll!"months"(-27, AllowDayOverflow.no); - assert(date == Date(-1999, 4, 6)); - date.roll!"months"(28, AllowDayOverflow.no); - assert(date == Date(-1999, 8, 6)); - } - - { - auto date = Date(-1999, 5, 31); - date.roll!"months"(1, AllowDayOverflow.no); - assert(date == Date(-1999, 6, 30)); - } - - { - auto date = Date(-1999, 5, 31); - date.roll!"months"(-1, AllowDayOverflow.no); - assert(date == Date(-1999, 4, 30)); - } - - { - auto date = Date(-1999, 2, 28); - date.roll!"months"(-12, AllowDayOverflow.no); - assert(date == Date(-1999, 2, 28)); - } - - { - auto date = Date(-2000, 2, 29); - date.roll!"months"(-12, AllowDayOverflow.no); - assert(date == Date(-2000, 2, 29)); - } - - { - auto date = Date(-1999, 7, 31); - date.roll!"months"(1, AllowDayOverflow.no); - assert(date == Date(-1999, 8, 31)); - date.roll!"months"(1, AllowDayOverflow.no); - assert(date == Date(-1999, 9, 30)); - } - - { - auto date = Date(-1998, 8, 31); - date.roll!"months"(13, AllowDayOverflow.no); - assert(date == Date(-1998, 9, 30)); - date.roll!"months"(-13, AllowDayOverflow.no); - assert(date == Date(-1998, 8, 30)); - } - - { - auto date = Date(-1997, 12, 31); - date.roll!"months"(13, AllowDayOverflow.no); - assert(date == Date(-1997, 1, 31)); - date.roll!"months"(-13, AllowDayOverflow.no); - assert(date == Date(-1997, 12, 31)); - } - - { - auto date = Date(-1997, 12, 31); - date.roll!"months"(14, AllowDayOverflow.no); - assert(date == Date(-1997, 2, 28)); - date.roll!"months"(-14, AllowDayOverflow.no); - assert(date == Date(-1997, 12, 28)); - } - - { - auto date = Date(-2002, 12, 31); - date.roll!"months"(14, AllowDayOverflow.no); - assert(date == Date(-2002, 2, 28)); - date.roll!"months"(-14, AllowDayOverflow.no); - assert(date == Date(-2002, 12, 28)); - } - - { - auto date = Date(-2001, 12, 31); - date.roll!"months"(14, AllowDayOverflow.no); - assert(date == Date(-2001, 2, 28)); - date.roll!"months"(-14, AllowDayOverflow.no); - assert(date == Date(-2001, 12, 28)); - } - - // Test Both - { - auto date = Date(1, 1, 1); - date.roll!"months"(-1, AllowDayOverflow.no); - assert(date == Date(1, 12, 1)); - date.roll!"months"(1, AllowDayOverflow.no); - assert(date == Date(1, 1, 1)); - } - - { - auto date = Date(4, 1, 1); - date.roll!"months"(-48, AllowDayOverflow.no); - assert(date == Date(4, 1, 1)); - date.roll!"months"(48, AllowDayOverflow.no); - assert(date == Date(4, 1, 1)); - } - - { - auto date = Date(4, 3, 31); - date.roll!"months"(-49, AllowDayOverflow.no); - assert(date == Date(4, 2, 29)); - date.roll!"months"(49, AllowDayOverflow.no); - assert(date == Date(4, 3, 29)); - } - - { - auto date = Date(4, 3, 31); - date.roll!"months"(-85, AllowDayOverflow.no); - assert(date == Date(4, 2, 29)); - date.roll!"months"(85, AllowDayOverflow.no); - assert(date == Date(4, 3, 29)); - } - - { - auto date = Date(-1, 1, 1); - date.roll!"months"(-1, AllowDayOverflow.no); - assert(date == Date(-1, 12, 1)); - date.roll!"months"(1, AllowDayOverflow.no); - assert(date == Date(-1, 1, 1)); - } - - { - auto date = Date(-4, 1, 1); - date.roll!"months"(-48, AllowDayOverflow.no); - assert(date == Date(-4, 1, 1)); - date.roll!"months"(48, AllowDayOverflow.no); - assert(date == Date(-4, 1, 1)); - } - - { - auto date = Date(-4, 3, 31); - date.roll!"months"(-49, AllowDayOverflow.no); - assert(date == Date(-4, 2, 29)); - date.roll!"months"(49, AllowDayOverflow.no); - assert(date == Date(-4, 3, 29)); - } - - { - auto date = Date(-4, 3, 31); - date.roll!"months"(-85, AllowDayOverflow.no); - assert(date == Date(-4, 2, 29)); - date.roll!"months"(85, AllowDayOverflow.no); - assert(date == Date(-4, 3, 29)); - } - - { - auto date = Date(-3, 3, 31); - date.roll!"months"(85, AllowDayOverflow.no).roll!"months"(-83, AllowDayOverflow.no); - assert(date == Date(-3, 5, 30)); - } - } - - - /++ - Adds the given number of units to this $(LREF Date), mutating it. A - negative number will subtract. - - The difference between rolling and adding is that rolling does not - affect larger units. For instance, rolling a $(LREF Date) one - year's worth of days gets the exact same $(LREF Date). - - The only accepted units are `"days"`. - - Params: - units = The units to add. Must be `"days"`. - days = The number of days to add to this $(LREF Date). - - Returns: - A reference to the `Date` (`this`). - +/ - ref Date roll(string units)(long days) @safe pure nothrow @nogc - if (units == "days") - { - immutable limit = maxDay(_year, _month); - days %= limit; - auto newDay = _day + days; - - if (days < 0) - { - if (newDay < 1) - newDay += limit; - } - else if (newDay > limit) - newDay -= limit; - - _day = cast(ubyte) newDay; - return this; - } - - /// - @safe unittest - { - auto d = Date(2010, 1, 1); - d.roll!"days"(1); - assert(d == Date(2010, 1, 2)); - d.roll!"days"(365); - assert(d == Date(2010, 1, 26)); - d.roll!"days"(-32); - assert(d == Date(2010, 1, 25)); - } - - @safe unittest - { - // Test A.D. - { - auto date = Date(1999, 2, 28); - date.roll!"days"(1); - assert(date == Date(1999, 2, 1)); - date.roll!"days"(-1); - assert(date == Date(1999, 2, 28)); - } - - { - auto date = Date(2000, 2, 28); - date.roll!"days"(1); - assert(date == Date(2000, 2, 29)); - date.roll!"days"(1); - assert(date == Date(2000, 2, 1)); - date.roll!"days"(-1); - assert(date == Date(2000, 2, 29)); - } - - { - auto date = Date(1999, 6, 30); - date.roll!"days"(1); - assert(date == Date(1999, 6, 1)); - date.roll!"days"(-1); - assert(date == Date(1999, 6, 30)); - } - - { - auto date = Date(1999, 7, 31); - date.roll!"days"(1); - assert(date == Date(1999, 7, 1)); - date.roll!"days"(-1); - assert(date == Date(1999, 7, 31)); - } - - { - auto date = Date(1999, 1, 1); - date.roll!"days"(-1); - assert(date == Date(1999, 1, 31)); - date.roll!"days"(1); - assert(date == Date(1999, 1, 1)); - } - - { - auto date = Date(1999, 7, 6); - date.roll!"days"(9); - assert(date == Date(1999, 7, 15)); - date.roll!"days"(-11); - assert(date == Date(1999, 7, 4)); - date.roll!"days"(30); - assert(date == Date(1999, 7, 3)); - date.roll!"days"(-3); - assert(date == Date(1999, 7, 31)); - } - - { - auto date = Date(1999, 7, 6); - date.roll!"days"(365); - assert(date == Date(1999, 7, 30)); - date.roll!"days"(-365); - assert(date == Date(1999, 7, 6)); - date.roll!"days"(366); - assert(date == Date(1999, 7, 31)); - date.roll!"days"(730); - assert(date == Date(1999, 7, 17)); - date.roll!"days"(-1096); - assert(date == Date(1999, 7, 6)); - } - - { - auto date = Date(1999, 2, 6); - date.roll!"days"(365); - assert(date == Date(1999, 2, 7)); - date.roll!"days"(-365); - assert(date == Date(1999, 2, 6)); - date.roll!"days"(366); - assert(date == Date(1999, 2, 8)); - date.roll!"days"(730); - assert(date == Date(1999, 2, 10)); - date.roll!"days"(-1096); - assert(date == Date(1999, 2, 6)); - } - - // Test B.C. - { - auto date = Date(-1999, 2, 28); - date.roll!"days"(1); - assert(date == Date(-1999, 2, 1)); - date.roll!"days"(-1); - assert(date == Date(-1999, 2, 28)); - } - - { - auto date = Date(-2000, 2, 28); - date.roll!"days"(1); - assert(date == Date(-2000, 2, 29)); - date.roll!"days"(1); - assert(date == Date(-2000, 2, 1)); - date.roll!"days"(-1); - assert(date == Date(-2000, 2, 29)); - } - - { - auto date = Date(-1999, 6, 30); - date.roll!"days"(1); - assert(date == Date(-1999, 6, 1)); - date.roll!"days"(-1); - assert(date == Date(-1999, 6, 30)); - } - - { - auto date = Date(-1999, 7, 31); - date.roll!"days"(1); - assert(date == Date(-1999, 7, 1)); - date.roll!"days"(-1); - assert(date == Date(-1999, 7, 31)); - } - - { - auto date = Date(-1999, 1, 1); - date.roll!"days"(-1); - assert(date == Date(-1999, 1, 31)); - date.roll!"days"(1); - assert(date == Date(-1999, 1, 1)); - } - - { - auto date = Date(-1999, 7, 6); - date.roll!"days"(9); - assert(date == Date(-1999, 7, 15)); - date.roll!"days"(-11); - assert(date == Date(-1999, 7, 4)); - date.roll!"days"(30); - assert(date == Date(-1999, 7, 3)); - date.roll!"days"(-3); - assert(date == Date(-1999, 7, 31)); - } - - { - auto date = Date(-1999, 7, 6); - date.roll!"days"(365); - assert(date == Date(-1999, 7, 30)); - date.roll!"days"(-365); - assert(date == Date(-1999, 7, 6)); - date.roll!"days"(366); - assert(date == Date(-1999, 7, 31)); - date.roll!"days"(730); - assert(date == Date(-1999, 7, 17)); - date.roll!"days"(-1096); - assert(date == Date(-1999, 7, 6)); - } - - // Test Both - { - auto date = Date(1, 7, 6); - date.roll!"days"(-365); - assert(date == Date(1, 7, 13)); - date.roll!"days"(365); - assert(date == Date(1, 7, 6)); - date.roll!"days"(-731); - assert(date == Date(1, 7, 19)); - date.roll!"days"(730); - assert(date == Date(1, 7, 5)); - } - - { - auto date = Date(0, 7, 6); - date.roll!"days"(-365); - assert(date == Date(0, 7, 13)); - date.roll!"days"(365); - assert(date == Date(0, 7, 6)); - date.roll!"days"(-731); - assert(date == Date(0, 7, 19)); - date.roll!"days"(730); - assert(date == Date(0, 7, 5)); - } - - { - auto date = Date(0, 7, 6); - date.roll!"days"(-365).roll!"days"(362).roll!"days"(-12).roll!"days"(730); - assert(date == Date(0, 7, 8)); - } - - const cdate = Date(1999, 7, 6); - immutable idate = Date(1999, 7, 6); - static assert(!__traits(compiles, cdate.roll!"days"(12))); - static assert(!__traits(compiles, idate.roll!"days"(12))); - } - - import core.time : Duration; - /++ - Gives the result of adding or subtracting a $(REF Duration, core,time) - from - - The legal types of arithmetic for $(LREF Date) using this operator are - - $(BOOKTABLE, - $(TR $(TD Date) $(TD +) $(TD Duration) $(TD -->) $(TD Date)) - $(TR $(TD Date) $(TD -) $(TD Duration) $(TD -->) $(TD Date)) - ) - - Params: - duration = The $(REF Duration, core,time) to add to or subtract from - this $(LREF Date). - +/ - Date opBinary(string op)(Duration duration) const @safe pure nothrow @nogc - if (op == "+" || op == "-") - { - Date retval = this; - immutable days = duration.total!"days"; - mixin("return retval._addDays(" ~ op ~ "days);"); - } - - /// - @safe unittest - { - import core.time : days; - - assert(Date(2015, 12, 31) + days(1) == Date(2016, 1, 1)); - assert(Date(2004, 2, 26) + days(4) == Date(2004, 3, 1)); - - assert(Date(2016, 1, 1) - days(1) == Date(2015, 12, 31)); - assert(Date(2004, 3, 1) - days(4) == Date(2004, 2, 26)); - } - - @safe unittest - { - auto date = Date(1999, 7, 6); - - import core.time : dur; - assert(date + dur!"weeks"(7) == Date(1999, 8, 24)); - assert(date + dur!"weeks"(-7) == Date(1999, 5, 18)); - assert(date + dur!"days"(7) == Date(1999, 7, 13)); - assert(date + dur!"days"(-7) == Date(1999, 6, 29)); - - assert(date + dur!"hours"(24) == Date(1999, 7, 7)); - assert(date + dur!"hours"(-24) == Date(1999, 7, 5)); - assert(date + dur!"minutes"(1440) == Date(1999, 7, 7)); - assert(date + dur!"minutes"(-1440) == Date(1999, 7, 5)); - assert(date + dur!"seconds"(86_400) == Date(1999, 7, 7)); - assert(date + dur!"seconds"(-86_400) == Date(1999, 7, 5)); - assert(date + dur!"msecs"(86_400_000) == Date(1999, 7, 7)); - assert(date + dur!"msecs"(-86_400_000) == Date(1999, 7, 5)); - assert(date + dur!"usecs"(86_400_000_000) == Date(1999, 7, 7)); - assert(date + dur!"usecs"(-86_400_000_000) == Date(1999, 7, 5)); - assert(date + dur!"hnsecs"(864_000_000_000) == Date(1999, 7, 7)); - assert(date + dur!"hnsecs"(-864_000_000_000) == Date(1999, 7, 5)); - - assert(date - dur!"weeks"(-7) == Date(1999, 8, 24)); - assert(date - dur!"weeks"(7) == Date(1999, 5, 18)); - assert(date - dur!"days"(-7) == Date(1999, 7, 13)); - assert(date - dur!"days"(7) == Date(1999, 6, 29)); - - assert(date - dur!"hours"(-24) == Date(1999, 7, 7)); - assert(date - dur!"hours"(24) == Date(1999, 7, 5)); - assert(date - dur!"minutes"(-1440) == Date(1999, 7, 7)); - assert(date - dur!"minutes"(1440) == Date(1999, 7, 5)); - assert(date - dur!"seconds"(-86_400) == Date(1999, 7, 7)); - assert(date - dur!"seconds"(86_400) == Date(1999, 7, 5)); - assert(date - dur!"msecs"(-86_400_000) == Date(1999, 7, 7)); - assert(date - dur!"msecs"(86_400_000) == Date(1999, 7, 5)); - assert(date - dur!"usecs"(-86_400_000_000) == Date(1999, 7, 7)); - assert(date - dur!"usecs"(86_400_000_000) == Date(1999, 7, 5)); - assert(date - dur!"hnsecs"(-864_000_000_000) == Date(1999, 7, 7)); - assert(date - dur!"hnsecs"(864_000_000_000) == Date(1999, 7, 5)); - - auto duration = dur!"days"(12); - const cdate = Date(1999, 7, 6); - immutable idate = Date(1999, 7, 6); - assert(date + duration == Date(1999, 7, 18)); - assert(cdate + duration == Date(1999, 7, 18)); - assert(idate + duration == Date(1999, 7, 18)); - - assert(date - duration == Date(1999, 6, 24)); - assert(cdate - duration == Date(1999, 6, 24)); - assert(idate - duration == Date(1999, 6, 24)); - } - - - /++ - Gives the result of adding or subtracting a $(REF Duration, core,time) - from this $(LREF Date), as well as assigning the result to this - $(LREF Date). - - The legal types of arithmetic for $(LREF Date) using this operator are - - $(BOOKTABLE, - $(TR $(TD Date) $(TD +) $(TD Duration) $(TD -->) $(TD Date)) - $(TR $(TD Date) $(TD -) $(TD Duration) $(TD -->) $(TD Date)) - ) - - Params: - duration = The $(REF Duration, core,time) to add to or subtract from - this $(LREF Date). - +/ - ref Date opOpAssign(string op)(Duration duration) @safe pure nothrow @nogc - if (op == "+" || op == "-") - { - immutable days = duration.total!"days"; - mixin("return _addDays(" ~ op ~ "days);"); - } - - @safe unittest - { - import core.time : dur; - assert(Date(1999, 7, 6) + dur!"weeks"(7) == Date(1999, 8, 24)); - assert(Date(1999, 7, 6) + dur!"weeks"(-7) == Date(1999, 5, 18)); - assert(Date(1999, 7, 6) + dur!"days"(7) == Date(1999, 7, 13)); - assert(Date(1999, 7, 6) + dur!"days"(-7) == Date(1999, 6, 29)); - - assert(Date(1999, 7, 6) + dur!"hours"(24) == Date(1999, 7, 7)); - assert(Date(1999, 7, 6) + dur!"hours"(-24) == Date(1999, 7, 5)); - assert(Date(1999, 7, 6) + dur!"minutes"(1440) == Date(1999, 7, 7)); - assert(Date(1999, 7, 6) + dur!"minutes"(-1440) == Date(1999, 7, 5)); - assert(Date(1999, 7, 6) + dur!"seconds"(86_400) == Date(1999, 7, 7)); - assert(Date(1999, 7, 6) + dur!"seconds"(-86_400) == Date(1999, 7, 5)); - assert(Date(1999, 7, 6) + dur!"msecs"(86_400_000) == Date(1999, 7, 7)); - assert(Date(1999, 7, 6) + dur!"msecs"(-86_400_000) == Date(1999, 7, 5)); - assert(Date(1999, 7, 6) + dur!"usecs"(86_400_000_000) == Date(1999, 7, 7)); - assert(Date(1999, 7, 6) + dur!"usecs"(-86_400_000_000) == Date(1999, 7, 5)); - assert(Date(1999, 7, 6) + dur!"hnsecs"(864_000_000_000) == Date(1999, 7, 7)); - assert(Date(1999, 7, 6) + dur!"hnsecs"(-864_000_000_000) == Date(1999, 7, 5)); - - assert(Date(1999, 7, 6) - dur!"weeks"(-7) == Date(1999, 8, 24)); - assert(Date(1999, 7, 6) - dur!"weeks"(7) == Date(1999, 5, 18)); - assert(Date(1999, 7, 6) - dur!"days"(-7) == Date(1999, 7, 13)); - assert(Date(1999, 7, 6) - dur!"days"(7) == Date(1999, 6, 29)); - - assert(Date(1999, 7, 6) - dur!"hours"(-24) == Date(1999, 7, 7)); - assert(Date(1999, 7, 6) - dur!"hours"(24) == Date(1999, 7, 5)); - assert(Date(1999, 7, 6) - dur!"minutes"(-1440) == Date(1999, 7, 7)); - assert(Date(1999, 7, 6) - dur!"minutes"(1440) == Date(1999, 7, 5)); - assert(Date(1999, 7, 6) - dur!"seconds"(-86_400) == Date(1999, 7, 7)); - assert(Date(1999, 7, 6) - dur!"seconds"(86_400) == Date(1999, 7, 5)); - assert(Date(1999, 7, 6) - dur!"msecs"(-86_400_000) == Date(1999, 7, 7)); - assert(Date(1999, 7, 6) - dur!"msecs"(86_400_000) == Date(1999, 7, 5)); - assert(Date(1999, 7, 6) - dur!"usecs"(-86_400_000_000) == Date(1999, 7, 7)); - assert(Date(1999, 7, 6) - dur!"usecs"(86_400_000_000) == Date(1999, 7, 5)); - assert(Date(1999, 7, 6) - dur!"hnsecs"(-864_000_000_000) == Date(1999, 7, 7)); - assert(Date(1999, 7, 6) - dur!"hnsecs"(864_000_000_000) == Date(1999, 7, 5)); - - { - auto date = Date(0, 1, 31); - (date += dur!"days"(507)) += dur!"days"(-2); - assert(date == Date(1, 6, 19)); - } - - auto duration = dur!"days"(12); - auto date = Date(1999, 7, 6); - const cdate = Date(1999, 7, 6); - immutable idate = Date(1999, 7, 6); - date += duration; - static assert(!__traits(compiles, cdate += duration)); - static assert(!__traits(compiles, idate += duration)); - - date -= duration; - static assert(!__traits(compiles, cdate -= duration)); - static assert(!__traits(compiles, idate -= duration)); - } - - - /++ - Gives the difference between two $(LREF Date)s. - - The legal types of arithmetic for $(LREF Date) using this operator are - - $(BOOKTABLE, - $(TR $(TD Date) $(TD -) $(TD Date) $(TD -->) $(TD duration)) - ) - +/ - Duration opBinary(string op)(Date rhs) const @safe pure nothrow @nogc - if (op == "-") - { - import core.time : dur; - return dur!"days"(this.dayOfGregorianCal - rhs.dayOfGregorianCal); - } - - @safe unittest - { - auto date = Date(1999, 7, 6); - - import core.time : dur; - assert(Date(1999, 7, 6) - Date(1998, 7, 6) == dur!"days"(365)); - assert(Date(1998, 7, 6) - Date(1999, 7, 6) == dur!"days"(-365)); - assert(Date(1999, 6, 6) - Date(1999, 5, 6) == dur!"days"(31)); - assert(Date(1999, 5, 6) - Date(1999, 6, 6) == dur!"days"(-31)); - assert(Date(1999, 1, 1) - Date(1998, 12, 31) == dur!"days"(1)); - assert(Date(1998, 12, 31) - Date(1999, 1, 1) == dur!"days"(-1)); - - const cdate = Date(1999, 7, 6); - immutable idate = Date(1999, 7, 6); - assert(date - date == Duration.zero); - assert(cdate - date == Duration.zero); - assert(idate - date == Duration.zero); - - assert(date - cdate == Duration.zero); - assert(cdate - cdate == Duration.zero); - assert(idate - cdate == Duration.zero); - - assert(date - idate == Duration.zero); - assert(cdate - idate == Duration.zero); - assert(idate - idate == Duration.zero); - } - - - /++ - Returns the difference between the two $(LREF Date)s in months. - - To get the difference in years, subtract the year property - of two $(LREF Date)s. To get the difference in days or weeks, - subtract the $(LREF Date)s themselves and use the - $(REF Duration, core,time) that results. Because converting between - months and smaller units requires a specific date (which - $(REF Duration, core,time)s don't have), getting the difference in - months requires some math using both the year and month properties, so - this is a convenience function for getting the difference in months. - - Note that the number of days in the months or how far into the month - either $(LREF Date) is is irrelevant. It is the difference in the month - property combined with the difference in years * 12. So, for instance, - December 31st and January 1st are one month apart just as December 1st - and January 31st are one month apart. - - Params: - rhs = The $(LREF Date) to subtract from this one. - +/ - int diffMonths(Date rhs) const @safe pure nothrow @nogc - { - immutable yearDiff = _year - rhs._year; - immutable monthDiff = _month - rhs._month; - - return yearDiff * 12 + monthDiff; - } - - /// - @safe unittest - { - assert(Date(1999, 2, 1).diffMonths(Date(1999, 1, 31)) == 1); - assert(Date(1999, 1, 31).diffMonths(Date(1999, 2, 1)) == -1); - assert(Date(1999, 3, 1).diffMonths(Date(1999, 1, 1)) == 2); - assert(Date(1999, 1, 1).diffMonths(Date(1999, 3, 31)) == -2); - } - - @safe unittest - { - auto date = Date(1999, 7, 6); - - // Test A.D. - assert(date.diffMonths(Date(1998, 6, 5)) == 13); - assert(date.diffMonths(Date(1998, 7, 5)) == 12); - assert(date.diffMonths(Date(1998, 8, 5)) == 11); - assert(date.diffMonths(Date(1998, 9, 5)) == 10); - assert(date.diffMonths(Date(1998, 10, 5)) == 9); - assert(date.diffMonths(Date(1998, 11, 5)) == 8); - assert(date.diffMonths(Date(1998, 12, 5)) == 7); - assert(date.diffMonths(Date(1999, 1, 5)) == 6); - assert(date.diffMonths(Date(1999, 2, 6)) == 5); - assert(date.diffMonths(Date(1999, 3, 6)) == 4); - assert(date.diffMonths(Date(1999, 4, 6)) == 3); - assert(date.diffMonths(Date(1999, 5, 6)) == 2); - assert(date.diffMonths(Date(1999, 6, 6)) == 1); - assert(date.diffMonths(date) == 0); - assert(date.diffMonths(Date(1999, 8, 6)) == -1); - assert(date.diffMonths(Date(1999, 9, 6)) == -2); - assert(date.diffMonths(Date(1999, 10, 6)) == -3); - assert(date.diffMonths(Date(1999, 11, 6)) == -4); - assert(date.diffMonths(Date(1999, 12, 6)) == -5); - assert(date.diffMonths(Date(2000, 1, 6)) == -6); - assert(date.diffMonths(Date(2000, 2, 6)) == -7); - assert(date.diffMonths(Date(2000, 3, 6)) == -8); - assert(date.diffMonths(Date(2000, 4, 6)) == -9); - assert(date.diffMonths(Date(2000, 5, 6)) == -10); - assert(date.diffMonths(Date(2000, 6, 6)) == -11); - assert(date.diffMonths(Date(2000, 7, 6)) == -12); - assert(date.diffMonths(Date(2000, 8, 6)) == -13); - - assert(Date(1998, 6, 5).diffMonths(date) == -13); - assert(Date(1998, 7, 5).diffMonths(date) == -12); - assert(Date(1998, 8, 5).diffMonths(date) == -11); - assert(Date(1998, 9, 5).diffMonths(date) == -10); - assert(Date(1998, 10, 5).diffMonths(date) == -9); - assert(Date(1998, 11, 5).diffMonths(date) == -8); - assert(Date(1998, 12, 5).diffMonths(date) == -7); - assert(Date(1999, 1, 5).diffMonths(date) == -6); - assert(Date(1999, 2, 6).diffMonths(date) == -5); - assert(Date(1999, 3, 6).diffMonths(date) == -4); - assert(Date(1999, 4, 6).diffMonths(date) == -3); - assert(Date(1999, 5, 6).diffMonths(date) == -2); - assert(Date(1999, 6, 6).diffMonths(date) == -1); - assert(Date(1999, 8, 6).diffMonths(date) == 1); - assert(Date(1999, 9, 6).diffMonths(date) == 2); - assert(Date(1999, 10, 6).diffMonths(date) == 3); - assert(Date(1999, 11, 6).diffMonths(date) == 4); - assert(Date(1999, 12, 6).diffMonths(date) == 5); - assert(Date(2000, 1, 6).diffMonths(date) == 6); - assert(Date(2000, 2, 6).diffMonths(date) == 7); - assert(Date(2000, 3, 6).diffMonths(date) == 8); - assert(Date(2000, 4, 6).diffMonths(date) == 9); - assert(Date(2000, 5, 6).diffMonths(date) == 10); - assert(Date(2000, 6, 6).diffMonths(date) == 11); - assert(Date(2000, 7, 6).diffMonths(date) == 12); - assert(Date(2000, 8, 6).diffMonths(date) == 13); - - assert(date.diffMonths(Date(1999, 6, 30)) == 1); - assert(date.diffMonths(Date(1999, 7, 1)) == 0); - assert(date.diffMonths(Date(1999, 7, 6)) == 0); - assert(date.diffMonths(Date(1999, 7, 11)) == 0); - assert(date.diffMonths(Date(1999, 7, 16)) == 0); - assert(date.diffMonths(Date(1999, 7, 21)) == 0); - assert(date.diffMonths(Date(1999, 7, 26)) == 0); - assert(date.diffMonths(Date(1999, 7, 31)) == 0); - assert(date.diffMonths(Date(1999, 8, 1)) == -1); - - assert(date.diffMonths(Date(1990, 6, 30)) == 109); - assert(date.diffMonths(Date(1990, 7, 1)) == 108); - assert(date.diffMonths(Date(1990, 7, 6)) == 108); - assert(date.diffMonths(Date(1990, 7, 11)) == 108); - assert(date.diffMonths(Date(1990, 7, 16)) == 108); - assert(date.diffMonths(Date(1990, 7, 21)) == 108); - assert(date.diffMonths(Date(1990, 7, 26)) == 108); - assert(date.diffMonths(Date(1990, 7, 31)) == 108); - assert(date.diffMonths(Date(1990, 8, 1)) == 107); - - assert(Date(1999, 6, 30).diffMonths(date) == -1); - assert(Date(1999, 7, 1).diffMonths(date) == 0); - assert(Date(1999, 7, 6).diffMonths(date) == 0); - assert(Date(1999, 7, 11).diffMonths(date) == 0); - assert(Date(1999, 7, 16).diffMonths(date) == 0); - assert(Date(1999, 7, 21).diffMonths(date) == 0); - assert(Date(1999, 7, 26).diffMonths(date) == 0); - assert(Date(1999, 7, 31).diffMonths(date) == 0); - assert(Date(1999, 8, 1).diffMonths(date) == 1); - - assert(Date(1990, 6, 30).diffMonths(date) == -109); - assert(Date(1990, 7, 1).diffMonths(date) == -108); - assert(Date(1990, 7, 6).diffMonths(date) == -108); - assert(Date(1990, 7, 11).diffMonths(date) == -108); - assert(Date(1990, 7, 16).diffMonths(date) == -108); - assert(Date(1990, 7, 21).diffMonths(date) == -108); - assert(Date(1990, 7, 26).diffMonths(date) == -108); - assert(Date(1990, 7, 31).diffMonths(date) == -108); - assert(Date(1990, 8, 1).diffMonths(date) == -107); - - // Test B.C. - auto dateBC = Date(-1999, 7, 6); - - assert(dateBC.diffMonths(Date(-2000, 6, 5)) == 13); - assert(dateBC.diffMonths(Date(-2000, 7, 5)) == 12); - assert(dateBC.diffMonths(Date(-2000, 8, 5)) == 11); - assert(dateBC.diffMonths(Date(-2000, 9, 5)) == 10); - assert(dateBC.diffMonths(Date(-2000, 10, 5)) == 9); - assert(dateBC.diffMonths(Date(-2000, 11, 5)) == 8); - assert(dateBC.diffMonths(Date(-2000, 12, 5)) == 7); - assert(dateBC.diffMonths(Date(-1999, 1, 5)) == 6); - assert(dateBC.diffMonths(Date(-1999, 2, 6)) == 5); - assert(dateBC.diffMonths(Date(-1999, 3, 6)) == 4); - assert(dateBC.diffMonths(Date(-1999, 4, 6)) == 3); - assert(dateBC.diffMonths(Date(-1999, 5, 6)) == 2); - assert(dateBC.diffMonths(Date(-1999, 6, 6)) == 1); - assert(dateBC.diffMonths(dateBC) == 0); - assert(dateBC.diffMonths(Date(-1999, 8, 6)) == -1); - assert(dateBC.diffMonths(Date(-1999, 9, 6)) == -2); - assert(dateBC.diffMonths(Date(-1999, 10, 6)) == -3); - assert(dateBC.diffMonths(Date(-1999, 11, 6)) == -4); - assert(dateBC.diffMonths(Date(-1999, 12, 6)) == -5); - assert(dateBC.diffMonths(Date(-1998, 1, 6)) == -6); - assert(dateBC.diffMonths(Date(-1998, 2, 6)) == -7); - assert(dateBC.diffMonths(Date(-1998, 3, 6)) == -8); - assert(dateBC.diffMonths(Date(-1998, 4, 6)) == -9); - assert(dateBC.diffMonths(Date(-1998, 5, 6)) == -10); - assert(dateBC.diffMonths(Date(-1998, 6, 6)) == -11); - assert(dateBC.diffMonths(Date(-1998, 7, 6)) == -12); - assert(dateBC.diffMonths(Date(-1998, 8, 6)) == -13); - - assert(Date(-2000, 6, 5).diffMonths(dateBC) == -13); - assert(Date(-2000, 7, 5).diffMonths(dateBC) == -12); - assert(Date(-2000, 8, 5).diffMonths(dateBC) == -11); - assert(Date(-2000, 9, 5).diffMonths(dateBC) == -10); - assert(Date(-2000, 10, 5).diffMonths(dateBC) == -9); - assert(Date(-2000, 11, 5).diffMonths(dateBC) == -8); - assert(Date(-2000, 12, 5).diffMonths(dateBC) == -7); - assert(Date(-1999, 1, 5).diffMonths(dateBC) == -6); - assert(Date(-1999, 2, 6).diffMonths(dateBC) == -5); - assert(Date(-1999, 3, 6).diffMonths(dateBC) == -4); - assert(Date(-1999, 4, 6).diffMonths(dateBC) == -3); - assert(Date(-1999, 5, 6).diffMonths(dateBC) == -2); - assert(Date(-1999, 6, 6).diffMonths(dateBC) == -1); - assert(Date(-1999, 8, 6).diffMonths(dateBC) == 1); - assert(Date(-1999, 9, 6).diffMonths(dateBC) == 2); - assert(Date(-1999, 10, 6).diffMonths(dateBC) == 3); - assert(Date(-1999, 11, 6).diffMonths(dateBC) == 4); - assert(Date(-1999, 12, 6).diffMonths(dateBC) == 5); - assert(Date(-1998, 1, 6).diffMonths(dateBC) == 6); - assert(Date(-1998, 2, 6).diffMonths(dateBC) == 7); - assert(Date(-1998, 3, 6).diffMonths(dateBC) == 8); - assert(Date(-1998, 4, 6).diffMonths(dateBC) == 9); - assert(Date(-1998, 5, 6).diffMonths(dateBC) == 10); - assert(Date(-1998, 6, 6).diffMonths(dateBC) == 11); - assert(Date(-1998, 7, 6).diffMonths(dateBC) == 12); - assert(Date(-1998, 8, 6).diffMonths(dateBC) == 13); - - assert(dateBC.diffMonths(Date(-1999, 6, 30)) == 1); - assert(dateBC.diffMonths(Date(-1999, 7, 1)) == 0); - assert(dateBC.diffMonths(Date(-1999, 7, 6)) == 0); - assert(dateBC.diffMonths(Date(-1999, 7, 11)) == 0); - assert(dateBC.diffMonths(Date(-1999, 7, 16)) == 0); - assert(dateBC.diffMonths(Date(-1999, 7, 21)) == 0); - assert(dateBC.diffMonths(Date(-1999, 7, 26)) == 0); - assert(dateBC.diffMonths(Date(-1999, 7, 31)) == 0); - assert(dateBC.diffMonths(Date(-1999, 8, 1)) == -1); - - assert(dateBC.diffMonths(Date(-2008, 6, 30)) == 109); - assert(dateBC.diffMonths(Date(-2008, 7, 1)) == 108); - assert(dateBC.diffMonths(Date(-2008, 7, 6)) == 108); - assert(dateBC.diffMonths(Date(-2008, 7, 11)) == 108); - assert(dateBC.diffMonths(Date(-2008, 7, 16)) == 108); - assert(dateBC.diffMonths(Date(-2008, 7, 21)) == 108); - assert(dateBC.diffMonths(Date(-2008, 7, 26)) == 108); - assert(dateBC.diffMonths(Date(-2008, 7, 31)) == 108); - assert(dateBC.diffMonths(Date(-2008, 8, 1)) == 107); - - assert(Date(-1999, 6, 30).diffMonths(dateBC) == -1); - assert(Date(-1999, 7, 1).diffMonths(dateBC) == 0); - assert(Date(-1999, 7, 6).diffMonths(dateBC) == 0); - assert(Date(-1999, 7, 11).diffMonths(dateBC) == 0); - assert(Date(-1999, 7, 16).diffMonths(dateBC) == 0); - assert(Date(-1999, 7, 21).diffMonths(dateBC) == 0); - assert(Date(-1999, 7, 26).diffMonths(dateBC) == 0); - assert(Date(-1999, 7, 31).diffMonths(dateBC) == 0); - assert(Date(-1999, 8, 1).diffMonths(dateBC) == 1); - - assert(Date(-2008, 6, 30).diffMonths(dateBC) == -109); - assert(Date(-2008, 7, 1).diffMonths(dateBC) == -108); - assert(Date(-2008, 7, 6).diffMonths(dateBC) == -108); - assert(Date(-2008, 7, 11).diffMonths(dateBC) == -108); - assert(Date(-2008, 7, 16).diffMonths(dateBC) == -108); - assert(Date(-2008, 7, 21).diffMonths(dateBC) == -108); - assert(Date(-2008, 7, 26).diffMonths(dateBC) == -108); - assert(Date(-2008, 7, 31).diffMonths(dateBC) == -108); - assert(Date(-2008, 8, 1).diffMonths(dateBC) == -107); - - // Test Both - assert(Date(3, 3, 3).diffMonths(Date(-5, 5, 5)) == 94); - assert(Date(-5, 5, 5).diffMonths(Date(3, 3, 3)) == -94); - - const cdate = Date(1999, 7, 6); - immutable idate = Date(1999, 7, 6); - assert(date.diffMonths(date) == 0); - assert(cdate.diffMonths(date) == 0); - assert(idate.diffMonths(date) == 0); - - assert(date.diffMonths(cdate) == 0); - assert(cdate.diffMonths(cdate) == 0); - assert(idate.diffMonths(cdate) == 0); - - assert(date.diffMonths(idate) == 0); - assert(cdate.diffMonths(idate) == 0); - assert(idate.diffMonths(idate) == 0); - } - - - /++ - Whether this $(LREF Date) is in a leap year. - +/ - @property bool isLeapYear() const @safe pure nothrow @nogc - { - return yearIsLeapYear(_year); - } - - @safe unittest - { - auto date = Date(1999, 7, 6); - const cdate = Date(1999, 7, 6); - immutable idate = Date(1999, 7, 6); - static assert(!__traits(compiles, date.isLeapYear = true)); - static assert(!__traits(compiles, cdate.isLeapYear = true)); - static assert(!__traits(compiles, idate.isLeapYear = true)); - } - - - /++ - Day of the week this $(LREF Date) is on. - +/ - @property DayOfWeek dayOfWeek() const @safe pure nothrow @nogc - { - return getDayOfWeek(dayOfGregorianCal); - } - - @safe unittest - { - const cdate = Date(1999, 7, 6); - immutable idate = Date(1999, 7, 6); - assert(cdate.dayOfWeek == DayOfWeek.tue); - static assert(!__traits(compiles, cdate.dayOfWeek = DayOfWeek.sun)); - assert(idate.dayOfWeek == DayOfWeek.tue); - static assert(!__traits(compiles, idate.dayOfWeek = DayOfWeek.sun)); - } - - - /++ - Day of the year this $(LREF Date) is on. - +/ - @property ushort dayOfYear() const @safe pure nothrow @nogc - { - if (_month >= Month.jan && _month <= Month.dec) - { - immutable int[] lastDay = isLeapYear ? lastDayLeap : lastDayNonLeap; - auto monthIndex = _month - Month.jan; - - return cast(ushort)(lastDay[monthIndex] + _day); - } - assert(0, "Invalid month."); - } - - /// - @safe unittest - { - assert(Date(1999, 1, 1).dayOfYear == 1); - assert(Date(1999, 12, 31).dayOfYear == 365); - assert(Date(2000, 12, 31).dayOfYear == 366); - } - - @safe unittest - { - import std.algorithm.iteration : filter; - import std.range : chain; - - foreach (year; filter!((a){return !yearIsLeapYear(a);})(chain(testYearsBC, testYearsAD))) - { - foreach (doy; testDaysOfYear) - assert(Date(year, doy.md.month, doy.md.day).dayOfYear == doy.day); - } - - foreach (year; filter!((a){return yearIsLeapYear(a);})(chain(testYearsBC, testYearsAD))) - { - foreach (doy; testDaysOfLeapYear) - assert(Date(year, doy.md.month, doy.md.day).dayOfYear == doy.day); - } - - const cdate = Date(1999, 7, 6); - immutable idate = Date(1999, 7, 6); - assert(cdate.dayOfYear == 187); - assert(idate.dayOfYear == 187); - } - - /++ - Day of the year. - - Params: - day = The day of the year to set which day of the year this - $(LREF Date) is on. - - Throws: - $(REF DateTimeException,std,datetime,date) if the given day is an - invalid day of the year. - +/ - @property void dayOfYear(int day) @safe pure - { - setDayOfYear!true(day); - } - - private void setDayOfYear(bool useExceptions = false)(int day) - { - immutable int[] lastDay = isLeapYear ? lastDayLeap : lastDayNonLeap; - - bool dayOutOfRange = day <= 0 || day > (isLeapYear ? daysInLeapYear : daysInYear); - enum errorMsg = "Invalid day of the year."; - - static if (useExceptions) - { - if (dayOutOfRange) throw new DateTimeException(errorMsg); - } - else - { - assert(!dayOutOfRange, errorMsg); - } - - foreach (i; 1 .. lastDay.length) - { - if (day <= lastDay[i]) - { - _month = cast(Month)(cast(int) Month.jan + i - 1); - _day = cast(ubyte)(day - lastDay[i - 1]); - return; - } - } - assert(0, "Invalid day of the year."); - } - - @safe unittest - { - static void test(Date date, int day, MonthDay expected, size_t line = __LINE__) - { - date.dayOfYear = day; - assert(date.month == expected.month); - assert(date.day == expected.day); - } - - foreach (doy; testDaysOfYear) - { - test(Date(1999, 1, 1), doy.day, doy.md); - test(Date(-1, 1, 1), doy.day, doy.md); - } - - foreach (doy; testDaysOfLeapYear) - { - test(Date(2000, 1, 1), doy.day, doy.md); - test(Date(-4, 1, 1), doy.day, doy.md); - } - - const cdate = Date(1999, 7, 6); - immutable idate = Date(1999, 7, 6); - static assert(!__traits(compiles, cdate.dayOfYear = 187)); - static assert(!__traits(compiles, idate.dayOfYear = 187)); - } - - - /++ - The Xth day of the Gregorian Calendar that this $(LREF Date) is on. - +/ - @property int dayOfGregorianCal() const @safe pure nothrow @nogc - { - if (isAD) - { - if (_year == 1) - return dayOfYear; - - int years = _year - 1; - auto days = (years / 400) * daysIn400Years; - years %= 400; - - days += (years / 100) * daysIn100Years; - years %= 100; - - days += (years / 4) * daysIn4Years; - years %= 4; - - days += years * daysInYear; - - days += dayOfYear; - - return days; - } - else if (_year == 0) - return dayOfYear - daysInLeapYear; - else - { - int years = _year; - auto days = (years / 400) * daysIn400Years; - years %= 400; - - days += (years / 100) * daysIn100Years; - years %= 100; - - days += (years / 4) * daysIn4Years; - years %= 4; - - if (years < 0) - { - days -= daysInLeapYear; - ++years; - - days += years * daysInYear; - - days -= daysInYear - dayOfYear; - } - else - days -= daysInLeapYear - dayOfYear; - - return days; - } - } - - /// - @safe unittest - { - assert(Date(1, 1, 1).dayOfGregorianCal == 1); - assert(Date(1, 12, 31).dayOfGregorianCal == 365); - assert(Date(2, 1, 1).dayOfGregorianCal == 366); - - assert(Date(0, 12, 31).dayOfGregorianCal == 0); - assert(Date(0, 1, 1).dayOfGregorianCal == -365); - assert(Date(-1, 12, 31).dayOfGregorianCal == -366); - - assert(Date(2000, 1, 1).dayOfGregorianCal == 730_120); - assert(Date(2010, 12, 31).dayOfGregorianCal == 734_137); - } - - @safe unittest - { - import std.range : chain; - - foreach (gd; chain(testGregDaysBC, testGregDaysAD)) - assert(gd.date.dayOfGregorianCal == gd.day); - - auto date = Date(1999, 7, 6); - const cdate = Date(1999, 7, 6); - immutable idate = Date(1999, 7, 6); - assert(date.dayOfGregorianCal == 729_941); - assert(cdate.dayOfGregorianCal == 729_941); - assert(idate.dayOfGregorianCal == 729_941); - } - - /++ - The Xth day of the Gregorian Calendar that this $(LREF Date) is on. - - Params: - day = The day of the Gregorian Calendar to set this $(LREF Date) to. - +/ - @property void dayOfGregorianCal(int day) @safe pure nothrow @nogc - { - this = Date(day); - } - - /// - @safe unittest - { - auto date = Date.init; - date.dayOfGregorianCal = 1; - assert(date == Date(1, 1, 1)); - - date.dayOfGregorianCal = 365; - assert(date == Date(1, 12, 31)); - - date.dayOfGregorianCal = 366; - assert(date == Date(2, 1, 1)); - - date.dayOfGregorianCal = 0; - assert(date == Date(0, 12, 31)); - - date.dayOfGregorianCal = -365; - assert(date == Date(-0, 1, 1)); - - date.dayOfGregorianCal = -366; - assert(date == Date(-1, 12, 31)); - - date.dayOfGregorianCal = 730_120; - assert(date == Date(2000, 1, 1)); - - date.dayOfGregorianCal = 734_137; - assert(date == Date(2010, 12, 31)); - } - - @safe unittest - { - auto date = Date(1999, 7, 6); - const cdate = Date(1999, 7, 6); - immutable idate = Date(1999, 7, 6); - date.dayOfGregorianCal = 187; - assert(date.dayOfGregorianCal == 187); - static assert(!__traits(compiles, cdate.dayOfGregorianCal = 187)); - static assert(!__traits(compiles, idate.dayOfGregorianCal = 187)); - } - - - /++ - The ISO 8601 week and year of the year that this $(LREF Date) is in. - - Returns: - An anonymous struct with the members $(D isoWeekYear) for the - resulting year and $(D isoWeek) for the resulting ISO week. - - See_Also: - $(HTTP en.wikipedia.org/wiki/ISO_week_date, ISO Week Date) - +/ - @property auto isoWeekAndYear() const @safe pure nothrow - { - struct ISOWeekAndYear { short isoWeekYear; ubyte isoWeek; } - - immutable weekday = dayOfWeek; - immutable adjustedWeekday = weekday == DayOfWeek.sun ? 7 : weekday; - immutable week = (dayOfYear - adjustedWeekday + 10) / 7; - - try - { - if (week == 53) - { - switch (Date(_year + 1, 1, 1).dayOfWeek) - { - case DayOfWeek.mon: - case DayOfWeek.tue: - case DayOfWeek.wed: - case DayOfWeek.thu: - return ISOWeekAndYear(cast(short) (_year + 1), 1); - case DayOfWeek.fri: - case DayOfWeek.sat: - case DayOfWeek.sun: - return ISOWeekAndYear(_year, 53); - default: - assert(0, "Invalid ISO Week"); - } - } - else if (week > 0) - return ISOWeekAndYear(_year, cast(ubyte) week); - else - return Date(_year - 1, 12, 31).isoWeekAndYear; - } - catch (Exception e) - assert(0, "Date's constructor threw."); - } - - /++ - The ISO 8601 week of the year that this $(LREF Date) is in. - - See_Also: - $(HTTP en.wikipedia.org/wiki/ISO_week_date, ISO Week Date) - +/ - @property ubyte isoWeek() const @safe pure nothrow - { - return isoWeekAndYear().isoWeek; - } - - @safe unittest - { - // Test A.D. - assert(Date(2009, 12, 28).isoWeek == 53); - assert(Date(2009, 12, 29).isoWeek == 53); - assert(Date(2009, 12, 30).isoWeek == 53); - assert(Date(2009, 12, 31).isoWeek == 53); - assert(Date(2010, 1, 1).isoWeek == 53); - assert(Date(2010, 1, 2).isoWeek == 53); - assert(Date(2010, 1, 3).isoWeek == 53); - assert(Date(2010, 1, 4).isoWeek == 1); - assert(Date(2010, 1, 5).isoWeek == 1); - assert(Date(2010, 1, 6).isoWeek == 1); - assert(Date(2010, 1, 7).isoWeek == 1); - assert(Date(2010, 1, 8).isoWeek == 1); - assert(Date(2010, 1, 9).isoWeek == 1); - assert(Date(2010, 1, 10).isoWeek == 1); - assert(Date(2010, 1, 11).isoWeek == 2); - assert(Date(2010, 12, 31).isoWeek == 52); - - assert(Date(2004, 12, 26).isoWeek == 52); - assert(Date(2004, 12, 27).isoWeek == 53); - assert(Date(2004, 12, 28).isoWeek == 53); - assert(Date(2004, 12, 29).isoWeek == 53); - assert(Date(2004, 12, 30).isoWeek == 53); - assert(Date(2004, 12, 31).isoWeek == 53); - assert(Date(2005, 1, 1).isoWeek == 53); - assert(Date(2005, 1, 2).isoWeek == 53); - - assert(Date(2005, 12, 31).isoWeek == 52); - assert(Date(2007, 1, 1).isoWeek == 1); - - assert(Date(2007, 12, 30).isoWeek == 52); - assert(Date(2007, 12, 31).isoWeek == 1); - assert(Date(2008, 1, 1).isoWeek == 1); - - assert(Date(2008, 12, 28).isoWeek == 52); - assert(Date(2008, 12, 29).isoWeek == 1); - assert(Date(2008, 12, 30).isoWeek == 1); - assert(Date(2008, 12, 31).isoWeek == 1); - assert(Date(2009, 1, 1).isoWeek == 1); - assert(Date(2009, 1, 2).isoWeek == 1); - assert(Date(2009, 1, 3).isoWeek == 1); - assert(Date(2009, 1, 4).isoWeek == 1); - - // Test B.C. - // The algorithm should work identically for both A.D. and B.C. since - // it doesn't really take the year into account, so B.C. testing - // probably isn't really needed. - assert(Date(0, 12, 31).isoWeek == 52); - assert(Date(0, 1, 4).isoWeek == 1); - assert(Date(0, 1, 1).isoWeek == 52); - - const cdate = Date(1999, 7, 6); - immutable idate = Date(1999, 7, 6); - assert(cdate.isoWeek == 27); - static assert(!__traits(compiles, cdate.isoWeek = 3)); - assert(idate.isoWeek == 27); - static assert(!__traits(compiles, idate.isoWeek = 3)); - } - - /++ - The year inside the ISO 8601 week calendar that this $(LREF Date) is in. - - May differ from $(LREF year) between 28 December and 4 January. - - See_Also: - $(HTTP en.wikipedia.org/wiki/ISO_week_date, ISO Week Date) - +/ - @property short isoWeekYear() const @safe pure nothrow - { - return isoWeekAndYear().isoWeekYear; - } - - @safe unittest - { - // Test A.D. - assert(Date(2009, 12, 28).isoWeekYear == 2009); - assert(Date(2009, 12, 29).isoWeekYear == 2009); - assert(Date(2009, 12, 30).isoWeekYear == 2009); - assert(Date(2009, 12, 31).isoWeekYear == 2009); - assert(Date(2010, 1, 1).isoWeekYear == 2009); - assert(Date(2010, 1, 2).isoWeekYear == 2009); - assert(Date(2010, 1, 3).isoWeekYear == 2009); - assert(Date(2010, 1, 4).isoWeekYear == 2010); - assert(Date(2010, 1, 5).isoWeekYear == 2010); - assert(Date(2010, 1, 6).isoWeekYear == 2010); - assert(Date(2010, 1, 7).isoWeekYear == 2010); - assert(Date(2010, 1, 8).isoWeekYear == 2010); - assert(Date(2010, 1, 9).isoWeekYear == 2010); - assert(Date(2010, 1, 10).isoWeekYear == 2010); - assert(Date(2010, 1, 11).isoWeekYear == 2010); - assert(Date(2010, 12, 31).isoWeekYear == 2010); - - assert(Date(2004, 12, 26).isoWeekYear == 2004); - assert(Date(2004, 12, 27).isoWeekYear == 2004); - assert(Date(2004, 12, 28).isoWeekYear == 2004); - assert(Date(2004, 12, 29).isoWeekYear == 2004); - assert(Date(2004, 12, 30).isoWeekYear == 2004); - assert(Date(2004, 12, 31).isoWeekYear == 2004); - assert(Date(2005, 1, 1).isoWeekYear == 2004); - assert(Date(2005, 1, 2).isoWeekYear == 2004); - assert(Date(2005, 1, 3).isoWeekYear == 2005); - - assert(Date(2005, 12, 31).isoWeekYear == 2005); - assert(Date(2007, 1, 1).isoWeekYear == 2007); - - assert(Date(2007, 12, 30).isoWeekYear == 2007); - assert(Date(2007, 12, 31).isoWeekYear == 2008); - assert(Date(2008, 1, 1).isoWeekYear == 2008); - - assert(Date(2008, 12, 28).isoWeekYear == 2008); - assert(Date(2008, 12, 29).isoWeekYear == 2009); - assert(Date(2008, 12, 30).isoWeekYear == 2009); - assert(Date(2008, 12, 31).isoWeekYear == 2009); - assert(Date(2009, 1, 1).isoWeekYear == 2009); - assert(Date(2009, 1, 2).isoWeekYear == 2009); - assert(Date(2009, 1, 3).isoWeekYear == 2009); - assert(Date(2009, 1, 4).isoWeekYear == 2009); - - // Test B.C. - assert(Date(0, 12, 31).isoWeekYear == 0); - assert(Date(0, 1, 4).isoWeekYear == 0); - assert(Date(0, 1, 1).isoWeekYear == -1); - - const cdate = Date(1999, 7, 6); - immutable idate = Date(1999, 7, 6); - assert(cdate.isoWeekYear == 1999); - assert(idate.isoWeekYear == 1999); - } - - static Date fromISOWeek(short isoWeekYear, ubyte isoWeek, DayOfWeek weekday) @safe pure nothrow @nogc - { - immutable adjustedWeekday = weekday == DayOfWeek.sun ? 7 : weekday; - immutable dayOffset = (isoWeek - 1) * 7 + adjustedWeekday; - - Date date; - date._year = isoWeekYear; - date._month = Month.jan; - date._day = 3; - immutable startOfYear = date.dayOfWeek; - return date._addDays(dayOffset - startOfYear); - } - - @safe unittest - { - // Test -30000 days to 30000 days for matching construction <-> deconstruction - Date date = Date(1, 1, 1); - date._addDays(-30_000); - foreach (day; 0 .. 60_000) - { - const year = date.isoWeekYear; - const dow = date.dayOfWeek; - const isoWeek = date.isoWeek; - const reversed = Date.fromISOWeek(year, isoWeek, dow); - assert(reversed == date, date.toISOExtString ~ " != " ~ reversed.toISOExtString); - date = date._addDays(1); - } - } - - - /++ - $(LREF Date) for the last day in the month that this $(LREF Date) is in. - +/ - @property Date endOfMonth() const @safe pure nothrow - { - try - return Date(_year, _month, maxDay(_year, _month)); - catch (Exception e) - assert(0, "Date's constructor threw."); - } - - /// - @safe unittest - { - assert(Date(1999, 1, 6).endOfMonth == Date(1999, 1, 31)); - assert(Date(1999, 2, 7).endOfMonth == Date(1999, 2, 28)); - assert(Date(2000, 2, 7).endOfMonth == Date(2000, 2, 29)); - assert(Date(2000, 6, 4).endOfMonth == Date(2000, 6, 30)); - } - - @safe unittest - { - // Test A.D. - assert(Date(1999, 1, 1).endOfMonth == Date(1999, 1, 31)); - assert(Date(1999, 2, 1).endOfMonth == Date(1999, 2, 28)); - assert(Date(2000, 2, 1).endOfMonth == Date(2000, 2, 29)); - assert(Date(1999, 3, 1).endOfMonth == Date(1999, 3, 31)); - assert(Date(1999, 4, 1).endOfMonth == Date(1999, 4, 30)); - assert(Date(1999, 5, 1).endOfMonth == Date(1999, 5, 31)); - assert(Date(1999, 6, 1).endOfMonth == Date(1999, 6, 30)); - assert(Date(1999, 7, 1).endOfMonth == Date(1999, 7, 31)); - assert(Date(1999, 8, 1).endOfMonth == Date(1999, 8, 31)); - assert(Date(1999, 9, 1).endOfMonth == Date(1999, 9, 30)); - assert(Date(1999, 10, 1).endOfMonth == Date(1999, 10, 31)); - assert(Date(1999, 11, 1).endOfMonth == Date(1999, 11, 30)); - assert(Date(1999, 12, 1).endOfMonth == Date(1999, 12, 31)); - - // Test B.C. - assert(Date(-1999, 1, 1).endOfMonth == Date(-1999, 1, 31)); - assert(Date(-1999, 2, 1).endOfMonth == Date(-1999, 2, 28)); - assert(Date(-2000, 2, 1).endOfMonth == Date(-2000, 2, 29)); - assert(Date(-1999, 3, 1).endOfMonth == Date(-1999, 3, 31)); - assert(Date(-1999, 4, 1).endOfMonth == Date(-1999, 4, 30)); - assert(Date(-1999, 5, 1).endOfMonth == Date(-1999, 5, 31)); - assert(Date(-1999, 6, 1).endOfMonth == Date(-1999, 6, 30)); - assert(Date(-1999, 7, 1).endOfMonth == Date(-1999, 7, 31)); - assert(Date(-1999, 8, 1).endOfMonth == Date(-1999, 8, 31)); - assert(Date(-1999, 9, 1).endOfMonth == Date(-1999, 9, 30)); - assert(Date(-1999, 10, 1).endOfMonth == Date(-1999, 10, 31)); - assert(Date(-1999, 11, 1).endOfMonth == Date(-1999, 11, 30)); - assert(Date(-1999, 12, 1).endOfMonth == Date(-1999, 12, 31)); - - const cdate = Date(1999, 7, 6); - immutable idate = Date(1999, 7, 6); - static assert(!__traits(compiles, cdate.endOfMonth = Date(1999, 7, 30))); - static assert(!__traits(compiles, idate.endOfMonth = Date(1999, 7, 30))); - } - - - /++ - The last day in the month that this $(LREF Date) is in. - +/ - @property ubyte daysInMonth() const @safe pure nothrow @nogc - { - return maxDay(_year, _month); - } - - /// - @safe unittest - { - assert(Date(1999, 1, 6).daysInMonth == 31); - assert(Date(1999, 2, 7).daysInMonth == 28); - assert(Date(2000, 2, 7).daysInMonth == 29); - assert(Date(2000, 6, 4).daysInMonth == 30); - } - - @safe unittest - { - // Test A.D. - assert(Date(1999, 1, 1).daysInMonth == 31); - assert(Date(1999, 2, 1).daysInMonth == 28); - assert(Date(2000, 2, 1).daysInMonth == 29); - assert(Date(1999, 3, 1).daysInMonth == 31); - assert(Date(1999, 4, 1).daysInMonth == 30); - assert(Date(1999, 5, 1).daysInMonth == 31); - assert(Date(1999, 6, 1).daysInMonth == 30); - assert(Date(1999, 7, 1).daysInMonth == 31); - assert(Date(1999, 8, 1).daysInMonth == 31); - assert(Date(1999, 9, 1).daysInMonth == 30); - assert(Date(1999, 10, 1).daysInMonth == 31); - assert(Date(1999, 11, 1).daysInMonth == 30); - assert(Date(1999, 12, 1).daysInMonth == 31); - - // Test B.C. - assert(Date(-1999, 1, 1).daysInMonth == 31); - assert(Date(-1999, 2, 1).daysInMonth == 28); - assert(Date(-2000, 2, 1).daysInMonth == 29); - assert(Date(-1999, 3, 1).daysInMonth == 31); - assert(Date(-1999, 4, 1).daysInMonth == 30); - assert(Date(-1999, 5, 1).daysInMonth == 31); - assert(Date(-1999, 6, 1).daysInMonth == 30); - assert(Date(-1999, 7, 1).daysInMonth == 31); - assert(Date(-1999, 8, 1).daysInMonth == 31); - assert(Date(-1999, 9, 1).daysInMonth == 30); - assert(Date(-1999, 10, 1).daysInMonth == 31); - assert(Date(-1999, 11, 1).daysInMonth == 30); - assert(Date(-1999, 12, 1).daysInMonth == 31); - - const cdate = Date(1999, 7, 6); - immutable idate = Date(1999, 7, 6); - static assert(!__traits(compiles, cdate.daysInMonth = 30)); - static assert(!__traits(compiles, idate.daysInMonth = 30)); - } - - - /++ - Whether the current year is a date in A.D. - +/ - @property bool isAD() const @safe pure nothrow @nogc - { - return _year > 0; - } - - /// - @safe unittest - { - assert(Date(1, 1, 1).isAD); - assert(Date(2010, 12, 31).isAD); - assert(!Date(0, 12, 31).isAD); - assert(!Date(-2010, 1, 1).isAD); - } - - @safe unittest - { - assert(Date(2010, 7, 4).isAD); - assert(Date(1, 1, 1).isAD); - assert(!Date(0, 1, 1).isAD); - assert(!Date(-1, 1, 1).isAD); - assert(!Date(-2010, 7, 4).isAD); - - const cdate = Date(1999, 7, 6); - immutable idate = Date(1999, 7, 6); - assert(cdate.isAD); - assert(idate.isAD); - } - - - /++ - The $(HTTP en.wikipedia.org/wiki/Julian_day, Julian day) for this - $(LREF Date) at noon (since the Julian day changes at noon). - +/ - @property long julianDay() const @safe pure nothrow @nogc - { - return dayOfGregorianCal + 1_721_425; - } - - @safe unittest - { - assert(Date(-4713, 11, 24).julianDay == 0); - assert(Date(0, 12, 31).julianDay == 1_721_425); - assert(Date(1, 1, 1).julianDay == 1_721_426); - assert(Date(1582, 10, 15).julianDay == 2_299_161); - assert(Date(1858, 11, 17).julianDay == 2_400_001); - assert(Date(1982, 1, 4).julianDay == 2_444_974); - assert(Date(1996, 3, 31).julianDay == 2_450_174); - assert(Date(2010, 8, 24).julianDay == 2_455_433); - - const cdate = Date(1999, 7, 6); - immutable idate = Date(1999, 7, 6); - assert(cdate.julianDay == 2_451_366); - assert(idate.julianDay == 2_451_366); - } - - - /++ - The modified $(HTTP en.wikipedia.org/wiki/Julian_day, Julian day) for - any time on this date (since, the modified Julian day changes at - midnight). - +/ - @property long modJulianDay() const @safe pure nothrow @nogc - { - return julianDay - 2_400_001; - } - - @safe unittest - { - assert(Date(1858, 11, 17).modJulianDay == 0); - assert(Date(2010, 8, 24).modJulianDay == 55_432); - - const cdate = Date(1999, 7, 6); - immutable idate = Date(1999, 7, 6); - assert(cdate.modJulianDay == 51_365); - assert(idate.modJulianDay == 51_365); - } - - - /++ - Converts this $(LREF Date) to a string with the format `YYYYMMDD`. - If `writer` is set, the resulting string will be written directly - to it. - - Params: - writer = A `char` accepting $(REF_ALTTEXT output range, isOutputRange, std, range, primitives) - Returns: - A `string` when not using an output range; `void` otherwise. - +/ - string toISOString() const @safe pure nothrow - { - import std.array : appender; - auto w = appender!string(); - w.reserve(8); - try - toISOString(w); - catch (Exception e) - assert(0, "toISOString() threw."); - return w.data; - } - - /// - @safe unittest - { - assert(Date(2010, 7, 4).toISOString() == "20100704"); - assert(Date(1998, 12, 25).toISOString() == "19981225"); - assert(Date(0, 1, 5).toISOString() == "00000105"); - assert(Date(-4, 1, 5).toISOString() == "-00040105"); - } - - @safe unittest - { - // Test A.D. - assert(Date(9, 12, 4).toISOString() == "00091204"); - assert(Date(99, 12, 4).toISOString() == "00991204"); - assert(Date(999, 12, 4).toISOString() == "09991204"); - assert(Date(9999, 7, 4).toISOString() == "99990704"); - assert(Date(10000, 10, 20).toISOString() == "+100001020"); - - // Test B.C. - assert(Date(0, 12, 4).toISOString() == "00001204"); - assert(Date(-9, 12, 4).toISOString() == "-00091204"); - assert(Date(-99, 12, 4).toISOString() == "-00991204"); - assert(Date(-999, 12, 4).toISOString() == "-09991204"); - assert(Date(-9999, 7, 4).toISOString() == "-99990704"); - assert(Date(-10000, 10, 20).toISOString() == "-100001020"); - - const cdate = Date(1999, 7, 6); - immutable idate = Date(1999, 7, 6); - assert(cdate.toISOString() == "19990706"); - assert(idate.toISOString() == "19990706"); - } - - /// ditto - void toISOString(W)(ref W writer) const - if (isOutputRange!(W, char)) - { - import std.format.write : formattedWrite; - if (_year >= 0) - { - if (_year < 10_000) - formattedWrite(writer, "%04d%02d%02d", _year, _month, _day); - else - formattedWrite(writer, "+%05d%02d%02d", _year, _month, _day); - } - else if (_year > -10_000) - formattedWrite(writer, "%05d%02d%02d", _year, _month, _day); - else - formattedWrite(writer, "%06d%02d%02d", _year, _month, _day); - } - - @safe pure unittest - { - import std.array : appender; - - auto w = appender!(char[])(); - Date(2010, 7, 4).toISOString(w); - assert(w.data == "20100704"); - w.clear(); - Date(1998, 12, 25).toISOString(w); - assert(w.data == "19981225"); - } - - /++ - Converts this $(LREF Date) to a string with the format `YYYY-MM-DD`. - If `writer` is set, the resulting string will be written directly - to it. - - Params: - writer = A `char` accepting $(REF_ALTTEXT output range, isOutputRange, std, range, primitives) - Returns: - A `string` when not using an output range; `void` otherwise. - +/ - string toISOExtString() const @safe pure nothrow - { - import std.array : appender; - auto w = appender!string(); - w.reserve(10); - try - toISOExtString(w); - catch (Exception e) - assert(0, "toISOExtString() threw."); - return w.data; - } - - /// - @safe unittest - { - assert(Date(2010, 7, 4).toISOExtString() == "2010-07-04"); - assert(Date(1998, 12, 25).toISOExtString() == "1998-12-25"); - assert(Date(0, 1, 5).toISOExtString() == "0000-01-05"); - assert(Date(-4, 1, 5).toISOExtString() == "-0004-01-05"); - } - - @safe unittest - { - // Test A.D. - assert(Date(9, 12, 4).toISOExtString() == "0009-12-04"); - assert(Date(99, 12, 4).toISOExtString() == "0099-12-04"); - assert(Date(999, 12, 4).toISOExtString() == "0999-12-04"); - assert(Date(9999, 7, 4).toISOExtString() == "9999-07-04"); - assert(Date(10000, 10, 20).toISOExtString() == "+10000-10-20"); - - // Test B.C. - assert(Date(0, 12, 4).toISOExtString() == "0000-12-04"); - assert(Date(-9, 12, 4).toISOExtString() == "-0009-12-04"); - assert(Date(-99, 12, 4).toISOExtString() == "-0099-12-04"); - assert(Date(-999, 12, 4).toISOExtString() == "-0999-12-04"); - assert(Date(-9999, 7, 4).toISOExtString() == "-9999-07-04"); - assert(Date(-10000, 10, 20).toISOExtString() == "-10000-10-20"); - - const cdate = Date(1999, 7, 6); - immutable idate = Date(1999, 7, 6); - assert(cdate.toISOExtString() == "1999-07-06"); - assert(idate.toISOExtString() == "1999-07-06"); - } - - /// ditto - void toISOExtString(W)(ref W writer) const - if (isOutputRange!(W, char)) - { - import std.format.write : formattedWrite; - if (_year >= 0) - { - if (_year < 10_000) - formattedWrite(writer, "%04d-%02d-%02d", _year, _month, _day); - else - formattedWrite(writer, "+%05d-%02d-%02d", _year, _month, _day); - } - else if (_year > -10_000) - formattedWrite(writer, "%05d-%02d-%02d", _year, _month, _day); - else - formattedWrite(writer, "%06d-%02d-%02d", _year, _month, _day); - } - - @safe pure unittest - { - import std.array : appender; - - auto w = appender!(char[])(); - Date(2010, 7, 4).toISOExtString(w); - assert(w.data == "2010-07-04"); - w.clear(); - Date(-4, 1, 5).toISOExtString(w); - assert(w.data == "-0004-01-05"); - } - - /++ - Converts this $(LREF Date) to a string with the format `YYYY-Mon-DD`. - If `writer` is set, the resulting string will be written directly - to it. - - Params: - writer = A `char` accepting $(REF_ALTTEXT output range, isOutputRange, std, range, primitives) - Returns: - A `string` when not using an output range; `void` otherwise. - +/ - string toSimpleString() const @safe pure nothrow - { - import std.array : appender; - auto w = appender!string(); - w.reserve(11); - try - toSimpleString(w); - catch (Exception e) - assert(0, "toSimpleString() threw."); - return w.data; - } - - /// - @safe unittest - { - assert(Date(2010, 7, 4).toSimpleString() == "2010-Jul-04"); - assert(Date(1998, 12, 25).toSimpleString() == "1998-Dec-25"); - assert(Date(0, 1, 5).toSimpleString() == "0000-Jan-05"); - assert(Date(-4, 1, 5).toSimpleString() == "-0004-Jan-05"); - } - - @safe unittest - { - // Test A.D. - assert(Date(9, 12, 4).toSimpleString() == "0009-Dec-04"); - assert(Date(99, 12, 4).toSimpleString() == "0099-Dec-04"); - assert(Date(999, 12, 4).toSimpleString() == "0999-Dec-04"); - assert(Date(9999, 7, 4).toSimpleString() == "9999-Jul-04"); - assert(Date(10000, 10, 20).toSimpleString() == "+10000-Oct-20"); - - // Test B.C. - assert(Date(0, 12, 4).toSimpleString() == "0000-Dec-04"); - assert(Date(-9, 12, 4).toSimpleString() == "-0009-Dec-04"); - assert(Date(-99, 12, 4).toSimpleString() == "-0099-Dec-04"); - assert(Date(-999, 12, 4).toSimpleString() == "-0999-Dec-04"); - assert(Date(-9999, 7, 4).toSimpleString() == "-9999-Jul-04"); - assert(Date(-10000, 10, 20).toSimpleString() == "-10000-Oct-20"); - - const cdate = Date(1999, 7, 6); - immutable idate = Date(1999, 7, 6); - assert(cdate.toSimpleString() == "1999-Jul-06"); - assert(idate.toSimpleString() == "1999-Jul-06"); - } - - /// ditto - void toSimpleString(W)(ref W writer) const - if (isOutputRange!(W, char)) - { - import std.format.write : formattedWrite; - if (_year >= 0) - { - if (_year < 10_000) - formattedWrite(writer, "%04d-%s-%02d", _year, monthToString(_month), _day); - else - formattedWrite(writer, "+%05d-%s-%02d", _year, monthToString(_month), _day); - } - else if (_year > -10_000) - formattedWrite(writer, "%05d-%s-%02d", _year, monthToString(_month), _day); - else - formattedWrite(writer, "%06d-%s-%02d", _year, monthToString(_month), _day); - } - - @safe pure unittest - { - import std.array : appender; - - auto w = appender!(char[])(); - Date(9, 12, 4).toSimpleString(w); - assert(w.data == "0009-Dec-04"); - w.clear(); - Date(-10000, 10, 20).toSimpleString(w); - assert(w.data == "-10000-Oct-20"); - } - - /++ - Converts this $(LREF Date) to a string. - - This function exists to make it easy to convert a $(LREF Date) to a - string for code that does not care what the exact format is - just that - it presents the information in a clear manner. It also makes it easy to - simply convert a $(LREF Date) to a string when using functions such as - `to!string`, `format`, or `writeln` which use toString to convert - user-defined types. So, it is unlikely that much code will call - toString directly. - - The format of the string is purposefully unspecified, and code that - cares about the format of the string should use `toISOString`, - `toISOExtString`, `toSimpleString`, or some other custom formatting - function that explicitly generates the format that the code needs. The - reason is that the code is then clear about what format it's using, - making it less error-prone to maintain the code and interact with other - software that consumes the generated strings. It's for this same reason - $(LREF Date) has no `fromString` function, whereas it does have - `fromISOString`, `fromISOExtString`, and `fromSimpleString`. - - The format returned by toString may or may not change in the future. - +/ - string toString() const @safe pure nothrow - { - return toSimpleString(); - } - - @safe unittest - { - auto date = Date(1999, 7, 6); - const cdate = Date(1999, 7, 6); - immutable idate = Date(1999, 7, 6); - assert(date.toString()); - assert(cdate.toString()); - assert(idate.toString()); - } - - /// ditto - void toString(W)(ref W writer) const - if (isOutputRange!(W, char)) - { - toSimpleString(writer); - } - - /++ - Creates a $(LREF Date) from a string with the format YYYYMMDD. Whitespace - is stripped from the given string. - - Params: - isoString = A string formatted in the ISO format for dates. - - Throws: - $(REF DateTimeException,std,datetime,date) if the given string is - not in the ISO format or if the resulting $(LREF Date) would not be - valid. - +/ - static Date fromISOString(S)(scope const S isoString) @safe pure - if (isSomeString!S) - { - import std.algorithm.searching : startsWith; - import std.conv : to, text, ConvException; - import std.exception : enforce; - import std.string : strip; - - auto str = isoString.strip; - - enforce!DateTimeException(str.length >= 8, text("Invalid ISO String: ", isoString)); - - int day, month, year; - auto yearStr = str[0 .. $ - 4]; - - try - { - // using conversion to uint plus cast because it checks for +/- - // for us quickly while throwing ConvException - day = cast(int) to!uint(str[$ - 2 .. $]); - month = cast(int) to!uint(str[$ - 4 .. $ - 2]); - - if (yearStr.length > 4) - { - enforce!DateTimeException(yearStr.startsWith('-', '+'), - text("Invalid ISO String: ", isoString)); - year = to!int(yearStr); - } - else - { - year = cast(int) to!uint(yearStr); - } - } - catch (ConvException) - { - throw new DateTimeException(text("Invalid ISO String: ", isoString)); - } - - return Date(year, month, day); - } - - /// - @safe unittest - { - assert(Date.fromISOString("20100704") == Date(2010, 7, 4)); - assert(Date.fromISOString("19981225") == Date(1998, 12, 25)); - assert(Date.fromISOString("00000105") == Date(0, 1, 5)); - assert(Date.fromISOString("-00040105") == Date(-4, 1, 5)); - assert(Date.fromISOString(" 20100704 ") == Date(2010, 7, 4)); - } - - @safe unittest - { - assertThrown!DateTimeException(Date.fromISOString("")); - assertThrown!DateTimeException(Date.fromISOString("990704")); - assertThrown!DateTimeException(Date.fromISOString("0100704")); - assertThrown!DateTimeException(Date.fromISOString("2010070")); - assertThrown!DateTimeException(Date.fromISOString("2010070 ")); - assertThrown!DateTimeException(Date.fromISOString("120100704")); - assertThrown!DateTimeException(Date.fromISOString("-0100704")); - assertThrown!DateTimeException(Date.fromISOString("+0100704")); - assertThrown!DateTimeException(Date.fromISOString("2010070a")); - assertThrown!DateTimeException(Date.fromISOString("20100a04")); - assertThrown!DateTimeException(Date.fromISOString("2010a704")); - - assertThrown!DateTimeException(Date.fromISOString("99-07-04")); - assertThrown!DateTimeException(Date.fromISOString("010-07-04")); - assertThrown!DateTimeException(Date.fromISOString("2010-07-0")); - assertThrown!DateTimeException(Date.fromISOString("2010-07-0 ")); - assertThrown!DateTimeException(Date.fromISOString("12010-07-04")); - assertThrown!DateTimeException(Date.fromISOString("-010-07-04")); - assertThrown!DateTimeException(Date.fromISOString("+010-07-04")); - assertThrown!DateTimeException(Date.fromISOString("2010-07-0a")); - assertThrown!DateTimeException(Date.fromISOString("2010-0a-04")); - assertThrown!DateTimeException(Date.fromISOString("2010-a7-04")); - assertThrown!DateTimeException(Date.fromISOString("2010/07/04")); - assertThrown!DateTimeException(Date.fromISOString("2010/7/04")); - assertThrown!DateTimeException(Date.fromISOString("2010/7/4")); - assertThrown!DateTimeException(Date.fromISOString("2010/07/4")); - assertThrown!DateTimeException(Date.fromISOString("2010-7-04")); - assertThrown!DateTimeException(Date.fromISOString("2010-7-4")); - assertThrown!DateTimeException(Date.fromISOString("2010-07-4")); - - assertThrown!DateTimeException(Date.fromISOString("99Jul04")); - assertThrown!DateTimeException(Date.fromISOString("010Jul04")); - assertThrown!DateTimeException(Date.fromISOString("2010Jul0")); - assertThrown!DateTimeException(Date.fromISOString("2010Jul0 ")); - assertThrown!DateTimeException(Date.fromISOString("12010Jul04")); - assertThrown!DateTimeException(Date.fromISOString("-010Jul04")); - assertThrown!DateTimeException(Date.fromISOString("+010Jul04")); - assertThrown!DateTimeException(Date.fromISOString("2010Jul0a")); - assertThrown!DateTimeException(Date.fromISOString("2010Jua04")); - assertThrown!DateTimeException(Date.fromISOString("2010aul04")); - - assertThrown!DateTimeException(Date.fromISOString("99-Jul-04")); - assertThrown!DateTimeException(Date.fromISOString("010-Jul-04")); - assertThrown!DateTimeException(Date.fromISOString("2010-Jul-0")); - assertThrown!DateTimeException(Date.fromISOString("2010-Jul-0 ")); - assertThrown!DateTimeException(Date.fromISOString("12010-Jul-04")); - assertThrown!DateTimeException(Date.fromISOString("-010-Jul-04")); - assertThrown!DateTimeException(Date.fromISOString("+010-Jul-04")); - assertThrown!DateTimeException(Date.fromISOString("2010-Jul-0a")); - assertThrown!DateTimeException(Date.fromISOString("2010-Jua-04")); - assertThrown!DateTimeException(Date.fromISOString("2010-Jal-04")); - assertThrown!DateTimeException(Date.fromISOString("2010-aul-04")); - - assertThrown!DateTimeException(Date.fromISOString("2010-07-04")); - assertThrown!DateTimeException(Date.fromISOString("2010-Jul-04")); - - assert(Date.fromISOString("19990706") == Date(1999, 7, 6)); - assert(Date.fromISOString("-19990706") == Date(-1999, 7, 6)); - assert(Date.fromISOString("+019990706") == Date(1999, 7, 6)); - assert(Date.fromISOString("19990706 ") == Date(1999, 7, 6)); - assert(Date.fromISOString(" 19990706") == Date(1999, 7, 6)); - assert(Date.fromISOString(" 19990706 ") == Date(1999, 7, 6)); - } - - // https://issues.dlang.org/show_bug.cgi?id=17801 - @safe unittest - { - import std.conv : to; - import std.meta : AliasSeq; - static foreach (C; AliasSeq!(char, wchar, dchar)) - { - static foreach (S; AliasSeq!(C[], const(C)[], immutable(C)[])) - assert(Date.fromISOString(to!S("20121221")) == Date(2012, 12, 21)); - } - } - - - /++ - Creates a $(LREF Date) from a string with the format YYYY-MM-DD. - Whitespace is stripped from the given string. - - Params: - isoExtString = A string formatted in the ISO Extended format for - dates. - - Throws: - $(REF DateTimeException,std,datetime,date) if the given string is - not in the ISO Extended format or if the resulting $(LREF Date) - would not be valid. - +/ - static Date fromISOExtString(S)(scope const S isoExtString) @safe pure - if (isSomeString!(S)) - { - import std.algorithm.searching : startsWith; - import std.conv : to, ConvException; - import std.format : format; - import std.string : strip; - - auto str = strip(isoExtString); - short year; - ubyte month, day; - - if (str.length < 10 || str[$-3] != '-' || str[$-6] != '-') - throw new DateTimeException(format("Invalid ISO Extended String: %s", isoExtString)); - - auto yearStr = str[0 .. $-6]; - auto signAtBegining = cast(bool) yearStr.startsWith('-', '+'); - if ((yearStr.length > 4) != signAtBegining) - { - throw new DateTimeException(format("Invalid ISO Extended String: %s", isoExtString)); - } - - try - { - day = to!ubyte(str[$-2 .. $]); - month = to!ubyte(str[$-5 .. $-3]); - year = to!short(yearStr); - } - catch (ConvException) - { - throw new DateTimeException(format("Invalid ISO Extended String: %s", isoExtString)); - } - - return Date(year, month, day); - } - - /// - @safe unittest - { - assert(Date.fromISOExtString("2010-07-04") == Date(2010, 7, 4)); - assert(Date.fromISOExtString("1998-12-25") == Date(1998, 12, 25)); - assert(Date.fromISOExtString("0000-01-05") == Date(0, 1, 5)); - assert(Date.fromISOExtString("-0004-01-05") == Date(-4, 1, 5)); - assert(Date.fromISOExtString(" 2010-07-04 ") == Date(2010, 7, 4)); - } - - @safe unittest - { - assertThrown!DateTimeException(Date.fromISOExtString("")); - assertThrown!DateTimeException(Date.fromISOExtString("990704")); - assertThrown!DateTimeException(Date.fromISOExtString("0100704")); - assertThrown!DateTimeException(Date.fromISOExtString("2010070")); - assertThrown!DateTimeException(Date.fromISOExtString("2010070 ")); - assertThrown!DateTimeException(Date.fromISOExtString("120100704")); - assertThrown!DateTimeException(Date.fromISOExtString("-0100704")); - assertThrown!DateTimeException(Date.fromISOExtString("+0100704")); - assertThrown!DateTimeException(Date.fromISOExtString("2010070a")); - assertThrown!DateTimeException(Date.fromISOExtString("20100a04")); - assertThrown!DateTimeException(Date.fromISOExtString("2010a704")); - - assertThrown!DateTimeException(Date.fromISOExtString("99-07-04")); - assertThrown!DateTimeException(Date.fromISOExtString("010-07-04")); - assertThrown!DateTimeException(Date.fromISOExtString("2010-07-0")); - assertThrown!DateTimeException(Date.fromISOExtString("2010-07-0 ")); - assertThrown!DateTimeException(Date.fromISOExtString("12010-07-04")); - assertThrown!DateTimeException(Date.fromISOExtString("-010-07-04")); - assertThrown!DateTimeException(Date.fromISOExtString("+010-07-04")); - assertThrown!DateTimeException(Date.fromISOExtString("2010-07-0a")); - assertThrown!DateTimeException(Date.fromISOExtString("2010-0a-04")); - assertThrown!DateTimeException(Date.fromISOExtString("2010-a7-04")); - assertThrown!DateTimeException(Date.fromISOExtString("2010/07/04")); - assertThrown!DateTimeException(Date.fromISOExtString("2010/7/04")); - assertThrown!DateTimeException(Date.fromISOExtString("2010/7/4")); - assertThrown!DateTimeException(Date.fromISOExtString("2010/07/4")); - assertThrown!DateTimeException(Date.fromISOExtString("2010-7-04")); - assertThrown!DateTimeException(Date.fromISOExtString("2010-7-4")); - assertThrown!DateTimeException(Date.fromISOExtString("2010-07-4")); - - assertThrown!DateTimeException(Date.fromISOExtString("99Jul04")); - assertThrown!DateTimeException(Date.fromISOExtString("010Jul04")); - assertThrown!DateTimeException(Date.fromISOExtString("2010Jul0")); - assertThrown!DateTimeException(Date.fromISOExtString("2010-Jul-0 ")); - assertThrown!DateTimeException(Date.fromISOExtString("12010Jul04")); - assertThrown!DateTimeException(Date.fromISOExtString("-010Jul04")); - assertThrown!DateTimeException(Date.fromISOExtString("+010Jul04")); - assertThrown!DateTimeException(Date.fromISOExtString("2010Jul0a")); - assertThrown!DateTimeException(Date.fromISOExtString("2010Jua04")); - assertThrown!DateTimeException(Date.fromISOExtString("2010aul04")); - - assertThrown!DateTimeException(Date.fromISOExtString("99-Jul-04")); - assertThrown!DateTimeException(Date.fromISOExtString("010-Jul-04")); - assertThrown!DateTimeException(Date.fromISOExtString("2010-Jul-0")); - assertThrown!DateTimeException(Date.fromISOExtString("2010Jul0 ")); - assertThrown!DateTimeException(Date.fromISOExtString("12010-Jul-04")); - assertThrown!DateTimeException(Date.fromISOExtString("-010-Jul-04")); - assertThrown!DateTimeException(Date.fromISOExtString("+010-Jul-04")); - assertThrown!DateTimeException(Date.fromISOExtString("2010-Jul-0a")); - assertThrown!DateTimeException(Date.fromISOExtString("2010-Jua-04")); - assertThrown!DateTimeException(Date.fromISOExtString("2010-Jal-04")); - assertThrown!DateTimeException(Date.fromISOExtString("2010-aul-04")); - - assertThrown!DateTimeException(Date.fromISOExtString("20100704")); - assertThrown!DateTimeException(Date.fromISOExtString("2010-Jul-04")); - - assert(Date.fromISOExtString("1999-07-06") == Date(1999, 7, 6)); - assert(Date.fromISOExtString("-1999-07-06") == Date(-1999, 7, 6)); - assert(Date.fromISOExtString("+01999-07-06") == Date(1999, 7, 6)); - assert(Date.fromISOExtString("1999-07-06 ") == Date(1999, 7, 6)); - assert(Date.fromISOExtString(" 1999-07-06") == Date(1999, 7, 6)); - assert(Date.fromISOExtString(" 1999-07-06 ") == Date(1999, 7, 6)); - } - - // https://issues.dlang.org/show_bug.cgi?id=17801 - @safe unittest - { - import std.conv : to; - import std.meta : AliasSeq; - static foreach (C; AliasSeq!(char, wchar, dchar)) - { - static foreach (S; AliasSeq!(C[], const(C)[], immutable(C)[])) - assert(Date.fromISOExtString(to!S("2012-12-21")) == Date(2012, 12, 21)); - } - } - - - /++ - Creates a $(LREF Date) from a string with the format YYYY-Mon-DD. - Whitespace is stripped from the given string. - - Params: - simpleString = A string formatted in the way that toSimpleString - formats dates. - - Throws: - $(REF DateTimeException,std,datetime,date) if the given string is - not in the correct format or if the resulting $(LREF Date) would not - be valid. - +/ - static Date fromSimpleString(S)(scope const S simpleString) @safe pure - if (isSomeString!(S)) - { - import std.algorithm.searching : startsWith; - import std.conv : to, ConvException; - import std.format : format; - import std.string : strip; - - auto str = strip(simpleString); - - if (str.length < 11 || str[$-3] != '-' || str[$-7] != '-') - throw new DateTimeException(format!"Invalid string format: %s"(simpleString)); - - int year; - uint day; - auto month = monthFromString(str[$ - 6 .. $ - 3]); - auto yearStr = str[0 .. $ - 7]; - auto signAtBegining = cast(bool) yearStr.startsWith('-', '+'); - if ((yearStr.length > 4) != signAtBegining) - { - throw new DateTimeException(format!"Invalid string format: %s"(simpleString)); - } - - try - { - day = to!uint(str[$ - 2 .. $]); - year = to!int(yearStr); - } - catch (ConvException) - { - throw new DateTimeException(format!"Invalid string format: %s"(simpleString)); - } - - return Date(year, month, day); - } - - /// - @safe unittest - { - assert(Date.fromSimpleString("2010-Jul-04") == Date(2010, 7, 4)); - assert(Date.fromSimpleString("1998-Dec-25") == Date(1998, 12, 25)); - assert(Date.fromSimpleString("0000-Jan-05") == Date(0, 1, 5)); - assert(Date.fromSimpleString("-0004-Jan-05") == Date(-4, 1, 5)); - assert(Date.fromSimpleString(" 2010-Jul-04 ") == Date(2010, 7, 4)); - } - - @safe unittest - { - assertThrown!DateTimeException(Date.fromSimpleString("")); - assertThrown!DateTimeException(Date.fromSimpleString("990704")); - assertThrown!DateTimeException(Date.fromSimpleString("0100704")); - assertThrown!DateTimeException(Date.fromSimpleString("2010070")); - assertThrown!DateTimeException(Date.fromSimpleString("2010070 ")); - assertThrown!DateTimeException(Date.fromSimpleString("120100704")); - assertThrown!DateTimeException(Date.fromSimpleString("-0100704")); - assertThrown!DateTimeException(Date.fromSimpleString("+0100704")); - assertThrown!DateTimeException(Date.fromSimpleString("2010070a")); - assertThrown!DateTimeException(Date.fromSimpleString("20100a04")); - assertThrown!DateTimeException(Date.fromSimpleString("2010a704")); - - assertThrown!DateTimeException(Date.fromSimpleString("99-07-04")); - assertThrown!DateTimeException(Date.fromSimpleString("010-07-04")); - assertThrown!DateTimeException(Date.fromSimpleString("2010-07-0")); - assertThrown!DateTimeException(Date.fromSimpleString("2010-07-0 ")); - assertThrown!DateTimeException(Date.fromSimpleString("12010-07-04")); - assertThrown!DateTimeException(Date.fromSimpleString("-010-07-04")); - assertThrown!DateTimeException(Date.fromSimpleString("+010-07-04")); - assertThrown!DateTimeException(Date.fromSimpleString("2010-07-0a")); - assertThrown!DateTimeException(Date.fromSimpleString("2010-0a-04")); - assertThrown!DateTimeException(Date.fromSimpleString("2010-a7-04")); - assertThrown!DateTimeException(Date.fromSimpleString("2010/07/04")); - assertThrown!DateTimeException(Date.fromSimpleString("2010/7/04")); - assertThrown!DateTimeException(Date.fromSimpleString("2010/7/4")); - assertThrown!DateTimeException(Date.fromSimpleString("2010/07/4")); - assertThrown!DateTimeException(Date.fromSimpleString("2010-7-04")); - assertThrown!DateTimeException(Date.fromSimpleString("2010-7-4")); - assertThrown!DateTimeException(Date.fromSimpleString("2010-07-4")); - - assertThrown!DateTimeException(Date.fromSimpleString("99Jul04")); - assertThrown!DateTimeException(Date.fromSimpleString("010Jul04")); - assertThrown!DateTimeException(Date.fromSimpleString("2010Jul0")); - assertThrown!DateTimeException(Date.fromSimpleString("2010Jul0 ")); - assertThrown!DateTimeException(Date.fromSimpleString("12010Jul04")); - assertThrown!DateTimeException(Date.fromSimpleString("-010Jul04")); - assertThrown!DateTimeException(Date.fromSimpleString("+010Jul04")); - assertThrown!DateTimeException(Date.fromSimpleString("2010Jul0a")); - assertThrown!DateTimeException(Date.fromSimpleString("2010Jua04")); - assertThrown!DateTimeException(Date.fromSimpleString("2010aul04")); - - assertThrown!DateTimeException(Date.fromSimpleString("99-Jul-04")); - assertThrown!DateTimeException(Date.fromSimpleString("010-Jul-04")); - assertThrown!DateTimeException(Date.fromSimpleString("2010-Jul-0")); - assertThrown!DateTimeException(Date.fromSimpleString("2010-Jul-0 ")); - assertThrown!DateTimeException(Date.fromSimpleString("12010-Jul-04")); - assertThrown!DateTimeException(Date.fromSimpleString("-010-Jul-04")); - assertThrown!DateTimeException(Date.fromSimpleString("+010-Jul-04")); - assertThrown!DateTimeException(Date.fromSimpleString("2010-Jul-0a")); - assertThrown!DateTimeException(Date.fromSimpleString("2010-Jua-04")); - assertThrown!DateTimeException(Date.fromSimpleString("2010-Jal-04")); - assertThrown!DateTimeException(Date.fromSimpleString("2010-aul-04")); - - assertThrown!DateTimeException(Date.fromSimpleString("20100704")); - assertThrown!DateTimeException(Date.fromSimpleString("2010-07-04")); - - assert(Date.fromSimpleString("1999-Jul-06") == Date(1999, 7, 6)); - assert(Date.fromSimpleString("-1999-Jul-06") == Date(-1999, 7, 6)); - assert(Date.fromSimpleString("+01999-Jul-06") == Date(1999, 7, 6)); - assert(Date.fromSimpleString("1999-Jul-06 ") == Date(1999, 7, 6)); - assert(Date.fromSimpleString(" 1999-Jul-06") == Date(1999, 7, 6)); - assert(Date.fromSimpleString(" 1999-Jul-06 ") == Date(1999, 7, 6)); - } - - // https://issues.dlang.org/show_bug.cgi?id=17801 - @safe unittest - { - import std.conv : to; - import std.meta : AliasSeq; - static foreach (C; AliasSeq!(char, wchar, dchar)) - { - static foreach (S; AliasSeq!(C[], const(C)[], immutable(C)[])) - assert(Date.fromSimpleString(to!S("2012-Dec-21")) == Date(2012, 12, 21)); - } - } - - - /++ - Returns the $(LREF Date) farthest in the past which is representable by - $(LREF Date). - +/ - @property static Date min() @safe pure nothrow @nogc - { - auto date = Date.init; - date._year = short.min; - date._month = Month.jan; - date._day = 1; - - return date; - } - - @safe unittest - { - assert(Date.min.year < 0); - assert(Date.min < Date.max); - } - - - /++ - Returns the $(LREF Date) farthest in the future which is representable - by $(LREF Date). - +/ - @property static Date max() @safe pure nothrow @nogc - { - auto date = Date.init; - date._year = short.max; - date._month = Month.dec; - date._day = 31; - - return date; - } - - @safe unittest - { - assert(Date.max.year > 0); - assert(Date.max > Date.min); - } - - -private: - - /+ - Whether the given values form a valid date. - - Params: - year = The year to test. - month = The month of the Gregorian Calendar to test. - day = The day of the month to test. - +/ - static bool _valid(int year, int month, int day) @safe pure nothrow @nogc - { - if (!valid!"months"(month)) - return false; - return valid!"days"(year, month, day); - } - - -package: - - /+ - Adds the given number of days to this $(LREF Date). A negative number - will subtract. - - The month will be adjusted along with the day if the number of days - added (or subtracted) would overflow (or underflow) the current month. - The year will be adjusted along with the month if the increase (or - decrease) to the month would cause it to overflow (or underflow) the - current year. - - `_addDays(numDays)` is effectively equivalent to - $(D date.dayOfGregorianCal = date.dayOfGregorianCal + days). - - Params: - days = The number of days to add to this Date. - +/ - ref Date _addDays(long days) return @safe pure nothrow @nogc - { - dayOfGregorianCal = cast(int)(dayOfGregorianCal + days); - return this; - } - - @safe unittest - { - // Test A.D. - { - auto date = Date(1999, 2, 28); - date._addDays(1); - assert(date == Date(1999, 3, 1)); - date._addDays(-1); - assert(date == Date(1999, 2, 28)); - } - - { - auto date = Date(2000, 2, 28); - date._addDays(1); - assert(date == Date(2000, 2, 29)); - date._addDays(1); - assert(date == Date(2000, 3, 1)); - date._addDays(-1); - assert(date == Date(2000, 2, 29)); - } - - { - auto date = Date(1999, 6, 30); - date._addDays(1); - assert(date == Date(1999, 7, 1)); - date._addDays(-1); - assert(date == Date(1999, 6, 30)); - } - - { - auto date = Date(1999, 7, 31); - date._addDays(1); - assert(date == Date(1999, 8, 1)); - date._addDays(-1); - assert(date == Date(1999, 7, 31)); - } - - { - auto date = Date(1999, 1, 1); - date._addDays(-1); - assert(date == Date(1998, 12, 31)); - date._addDays(1); - assert(date == Date(1999, 1, 1)); - } - - { - auto date = Date(1999, 7, 6); - date._addDays(9); - assert(date == Date(1999, 7, 15)); - date._addDays(-11); - assert(date == Date(1999, 7, 4)); - date._addDays(30); - assert(date == Date(1999, 8, 3)); - date._addDays(-3); - assert(date == Date(1999, 7, 31)); - } - - { - auto date = Date(1999, 7, 6); - date._addDays(365); - assert(date == Date(2000, 7, 5)); - date._addDays(-365); - assert(date == Date(1999, 7, 6)); - date._addDays(366); - assert(date == Date(2000, 7, 6)); - date._addDays(730); - assert(date == Date(2002, 7, 6)); - date._addDays(-1096); - assert(date == Date(1999, 7, 6)); - } - - // Test B.C. - { - auto date = Date(-1999, 2, 28); - date._addDays(1); - assert(date == Date(-1999, 3, 1)); - date._addDays(-1); - assert(date == Date(-1999, 2, 28)); - } - - { - auto date = Date(-2000, 2, 28); - date._addDays(1); - assert(date == Date(-2000, 2, 29)); - date._addDays(1); - assert(date == Date(-2000, 3, 1)); - date._addDays(-1); - assert(date == Date(-2000, 2, 29)); - } - - { - auto date = Date(-1999, 6, 30); - date._addDays(1); - assert(date == Date(-1999, 7, 1)); - date._addDays(-1); - assert(date == Date(-1999, 6, 30)); - } - - { - auto date = Date(-1999, 7, 31); - date._addDays(1); - assert(date == Date(-1999, 8, 1)); - date._addDays(-1); - assert(date == Date(-1999, 7, 31)); - } - - { - auto date = Date(-1999, 1, 1); - date._addDays(-1); - assert(date == Date(-2000, 12, 31)); - date._addDays(1); - assert(date == Date(-1999, 1, 1)); - } - - { - auto date = Date(-1999, 7, 6); - date._addDays(9); - assert(date == Date(-1999, 7, 15)); - date._addDays(-11); - assert(date == Date(-1999, 7, 4)); - date._addDays(30); - assert(date == Date(-1999, 8, 3)); - date._addDays(-3); - } - - { - auto date = Date(-1999, 7, 6); - date._addDays(365); - assert(date == Date(-1998, 7, 6)); - date._addDays(-365); - assert(date == Date(-1999, 7, 6)); - date._addDays(366); - assert(date == Date(-1998, 7, 7)); - date._addDays(730); - assert(date == Date(-1996, 7, 6)); - date._addDays(-1096); - assert(date == Date(-1999, 7, 6)); - } - - // Test Both - { - auto date = Date(1, 7, 6); - date._addDays(-365); - assert(date == Date(0, 7, 6)); - date._addDays(365); - assert(date == Date(1, 7, 6)); - date._addDays(-731); - assert(date == Date(-1, 7, 6)); - date._addDays(730); - assert(date == Date(1, 7, 5)); - } - - const cdate = Date(1999, 7, 6); - immutable idate = Date(1999, 7, 6); - static assert(!__traits(compiles, cdate._addDays(12))); - static assert(!__traits(compiles, idate._addDays(12))); - } - - - @safe pure invariant() - { - import std.format : format; - assert(valid!"months"(_month), - format("Invariant Failure: year [%s] month [%s] day [%s]", _year, _month, _day)); - assert(valid!"days"(_year, _month, _day), - format("Invariant Failure: year [%s] month [%s] day [%s]", _year, _month, _day)); - } - - short _year = 1; - Month _month = Month.jan; - ubyte _day = 1; -} - -/// -@safe pure unittest -{ - import core.time : days; - - auto d = Date(2000, 6, 1); - - assert(d.dayOfYear == 153); - assert(d.dayOfWeek == DayOfWeek.thu); - - d += 10.days; - assert(d == Date(2000, 6, 11)); - - assert(d.toISOExtString() == "2000-06-11"); - assert(d.toISOString() == "20000611"); - assert(d.toSimpleString() == "2000-Jun-11"); - - assert(Date.fromISOExtString("2018-01-01") == Date(2018, 1, 1)); - assert(Date.fromISOString("20180101") == Date(2018, 1, 1)); - assert(Date.fromSimpleString("2018-Jan-01") == Date(2018, 1, 1)); -} - - -/++ - Represents a time of day with hours, minutes, and seconds. It uses 24 hour - time. -+/ -struct TimeOfDay -{ -public: - - /++ - Params: - hour = Hour of the day [0 - 24$(RPAREN). - minute = Minute of the hour [0 - 60$(RPAREN). - second = Second of the minute [0 - 60$(RPAREN). - - Throws: - $(REF DateTimeException,std,datetime,date) if the resulting - $(LREF TimeOfDay) would be not be valid. - +/ - this(int hour, int minute, int second = 0) @safe pure - { - enforceValid!"hours"(hour); - enforceValid!"minutes"(minute); - enforceValid!"seconds"(second); - - _hour = cast(ubyte) hour; - _minute = cast(ubyte) minute; - _second = cast(ubyte) second; - } - - @safe unittest - { - assert(TimeOfDay(0, 0) == TimeOfDay.init); - - { - auto tod = TimeOfDay(0, 0); - assert(tod._hour == 0); - assert(tod._minute == 0); - assert(tod._second == 0); - } - - { - auto tod = TimeOfDay(12, 30, 33); - assert(tod._hour == 12); - assert(tod._minute == 30); - assert(tod._second == 33); - } - - { - auto tod = TimeOfDay(23, 59, 59); - assert(tod._hour == 23); - assert(tod._minute == 59); - assert(tod._second == 59); - } - - assertThrown!DateTimeException(TimeOfDay(24, 0, 0)); - assertThrown!DateTimeException(TimeOfDay(0, 60, 0)); - assertThrown!DateTimeException(TimeOfDay(0, 0, 60)); - } - - - /++ - Compares this $(LREF TimeOfDay) with the given $(LREF TimeOfDay). - - Returns: - $(BOOKTABLE, - $(TR $(TD this < rhs) $(TD < 0)) - $(TR $(TD this == rhs) $(TD 0)) - $(TR $(TD this > rhs) $(TD > 0)) - ) - +/ - int opCmp(TimeOfDay rhs) const @safe pure nothrow @nogc - { - if (_hour < rhs._hour) - return -1; - if (_hour > rhs._hour) - return 1; - - if (_minute < rhs._minute) - return -1; - if (_minute > rhs._minute) - return 1; - - if (_second < rhs._second) - return -1; - if (_second > rhs._second) - return 1; - - return 0; - } - - @safe unittest - { - assert(TimeOfDay(0, 0, 0).opCmp(TimeOfDay.init) == 0); - - assert(TimeOfDay(0, 0, 0).opCmp(TimeOfDay(0, 0, 0)) == 0); - assert(TimeOfDay(12, 0, 0).opCmp(TimeOfDay(12, 0, 0)) == 0); - assert(TimeOfDay(0, 30, 0).opCmp(TimeOfDay(0, 30, 0)) == 0); - assert(TimeOfDay(0, 0, 33).opCmp(TimeOfDay(0, 0, 33)) == 0); - - assert(TimeOfDay(12, 30, 0).opCmp(TimeOfDay(12, 30, 0)) == 0); - assert(TimeOfDay(12, 30, 33).opCmp(TimeOfDay(12, 30, 33)) == 0); - - assert(TimeOfDay(0, 30, 33).opCmp(TimeOfDay(0, 30, 33)) == 0); - assert(TimeOfDay(0, 0, 33).opCmp(TimeOfDay(0, 0, 33)) == 0); - - assert(TimeOfDay(12, 30, 33).opCmp(TimeOfDay(13, 30, 33)) < 0); - assert(TimeOfDay(13, 30, 33).opCmp(TimeOfDay(12, 30, 33)) > 0); - assert(TimeOfDay(12, 30, 33).opCmp(TimeOfDay(12, 31, 33)) < 0); - assert(TimeOfDay(12, 31, 33).opCmp(TimeOfDay(12, 30, 33)) > 0); - assert(TimeOfDay(12, 30, 33).opCmp(TimeOfDay(12, 30, 34)) < 0); - assert(TimeOfDay(12, 30, 34).opCmp(TimeOfDay(12, 30, 33)) > 0); - - assert(TimeOfDay(13, 30, 33).opCmp(TimeOfDay(12, 30, 34)) > 0); - assert(TimeOfDay(12, 30, 34).opCmp(TimeOfDay(13, 30, 33)) < 0); - assert(TimeOfDay(13, 30, 33).opCmp(TimeOfDay(12, 31, 33)) > 0); - assert(TimeOfDay(12, 31, 33).opCmp(TimeOfDay(13, 30, 33)) < 0); - - assert(TimeOfDay(12, 31, 33).opCmp(TimeOfDay(12, 30, 34)) > 0); - assert(TimeOfDay(12, 30, 34).opCmp(TimeOfDay(12, 31, 33)) < 0); - - const ctod = TimeOfDay(12, 30, 33); - immutable itod = TimeOfDay(12, 30, 33); - assert(ctod.opCmp(itod) == 0); - assert(itod.opCmp(ctod) == 0); - } - - - /++ - Hours past midnight. - +/ - @property ubyte hour() const @safe pure nothrow @nogc - { - return _hour; - } - - @safe unittest - { - assert(TimeOfDay.init.hour == 0); - assert(TimeOfDay(12, 0, 0).hour == 12); - - const ctod = TimeOfDay(12, 0, 0); - immutable itod = TimeOfDay(12, 0, 0); - assert(ctod.hour == 12); - assert(itod.hour == 12); - } - - - /++ - Hours past midnight. - - Params: - hour = The hour of the day to set this $(LREF TimeOfDay)'s hour to. - - Throws: - $(REF DateTimeException,std,datetime,date) if the given hour would - result in an invalid $(LREF TimeOfDay). - +/ - @property void hour(int hour) @safe pure - { - enforceValid!"hours"(hour); - _hour = cast(ubyte) hour; - } - - @safe unittest - { - assertThrown!DateTimeException((){TimeOfDay(0, 0, 0).hour = 24;}()); - - auto tod = TimeOfDay(0, 0, 0); - tod.hour = 12; - assert(tod == TimeOfDay(12, 0, 0)); - - const ctod = TimeOfDay(0, 0, 0); - immutable itod = TimeOfDay(0, 0, 0); - static assert(!__traits(compiles, ctod.hour = 12)); - static assert(!__traits(compiles, itod.hour = 12)); - } - - - /++ - Minutes past the hour. - +/ - @property ubyte minute() const @safe pure nothrow @nogc - { - return _minute; - } - - @safe unittest - { - assert(TimeOfDay.init.minute == 0); - assert(TimeOfDay(0, 30, 0).minute == 30); - - const ctod = TimeOfDay(0, 30, 0); - immutable itod = TimeOfDay(0, 30, 0); - assert(ctod.minute == 30); - assert(itod.minute == 30); - } - - - /++ - Minutes past the hour. - - Params: - minute = The minute to set this $(LREF TimeOfDay)'s minute to. - - Throws: - $(REF DateTimeException,std,datetime,date) if the given minute - would result in an invalid $(LREF TimeOfDay). - +/ - @property void minute(int minute) @safe pure - { - enforceValid!"minutes"(minute); - _minute = cast(ubyte) minute; - } - - @safe unittest - { - assertThrown!DateTimeException((){TimeOfDay(0, 0, 0).minute = 60;}()); - - auto tod = TimeOfDay(0, 0, 0); - tod.minute = 30; - assert(tod == TimeOfDay(0, 30, 0)); - - const ctod = TimeOfDay(0, 0, 0); - immutable itod = TimeOfDay(0, 0, 0); - static assert(!__traits(compiles, ctod.minute = 30)); - static assert(!__traits(compiles, itod.minute = 30)); - } - - - /++ - Seconds past the minute. - +/ - @property ubyte second() const @safe pure nothrow @nogc - { - return _second; - } - - @safe unittest - { - assert(TimeOfDay.init.second == 0); - assert(TimeOfDay(0, 0, 33).second == 33); - - const ctod = TimeOfDay(0, 0, 33); - immutable itod = TimeOfDay(0, 0, 33); - assert(ctod.second == 33); - assert(itod.second == 33); - } - - - /++ - Seconds past the minute. - - Params: - second = The second to set this $(LREF TimeOfDay)'s second to. - - Throws: - $(REF DateTimeException,std,datetime,date) if the given second - would result in an invalid $(LREF TimeOfDay). - +/ - @property void second(int second) @safe pure - { - enforceValid!"seconds"(second); - _second = cast(ubyte) second; - } - - @safe unittest - { - assertThrown!DateTimeException((){TimeOfDay(0, 0, 0).second = 60;}()); - - auto tod = TimeOfDay(0, 0, 0); - tod.second = 33; - assert(tod == TimeOfDay(0, 0, 33)); - - const ctod = TimeOfDay(0, 0, 0); - immutable itod = TimeOfDay(0, 0, 0); - static assert(!__traits(compiles, ctod.second = 33)); - static assert(!__traits(compiles, itod.second = 33)); - } - - - /++ - Adds the given number of units to this $(LREF TimeOfDay), mutating it. A - negative number will subtract. - - The difference between rolling and adding is that rolling does not - affect larger units. For instance, rolling a $(LREF TimeOfDay) - one hours's worth of minutes gets the exact same - $(LREF TimeOfDay). - - Accepted units are `"hours"`, `"minutes"`, and `"seconds"`. - - Params: - units = The units to add. - value = The number of $(D_PARAM units) to add to this - $(LREF TimeOfDay). - - Returns: - A reference to the `TimeOfDay` (`this`). - +/ - ref TimeOfDay roll(string units)(long value) @safe pure nothrow @nogc - if (units == "hours") - { - import core.time : dur; - return this += dur!"hours"(value); - } - - /// - @safe unittest - { - auto tod1 = TimeOfDay(7, 12, 0); - tod1.roll!"hours"(1); - assert(tod1 == TimeOfDay(8, 12, 0)); - - auto tod2 = TimeOfDay(7, 12, 0); - tod2.roll!"hours"(-1); - assert(tod2 == TimeOfDay(6, 12, 0)); - - auto tod3 = TimeOfDay(23, 59, 0); - tod3.roll!"minutes"(1); - assert(tod3 == TimeOfDay(23, 0, 0)); - - auto tod4 = TimeOfDay(0, 0, 0); - tod4.roll!"minutes"(-1); - assert(tod4 == TimeOfDay(0, 59, 0)); - - auto tod5 = TimeOfDay(23, 59, 59); - tod5.roll!"seconds"(1); - assert(tod5 == TimeOfDay(23, 59, 0)); - - auto tod6 = TimeOfDay(0, 0, 0); - tod6.roll!"seconds"(-1); - assert(tod6 == TimeOfDay(0, 0, 59)); - } - - @safe unittest - { - auto tod = TimeOfDay(12, 27, 2); - tod.roll!"hours"(22).roll!"hours"(-7); - assert(tod == TimeOfDay(3, 27, 2)); - - const ctod = TimeOfDay(0, 0, 0); - immutable itod = TimeOfDay(0, 0, 0); - static assert(!__traits(compiles, ctod.roll!"hours"(53))); - static assert(!__traits(compiles, itod.roll!"hours"(53))); - } - - - /// ditto - ref TimeOfDay roll(string units)(long value) @safe pure nothrow @nogc - if (units == "minutes" || units == "seconds") - { - import std.format : format; - - enum memberVarStr = units[0 .. $ - 1]; - value %= 60; - mixin(format("auto newVal = cast(ubyte)(_%s) + value;", memberVarStr)); - - if (value < 0) - { - if (newVal < 0) - newVal += 60; - } - else if (newVal >= 60) - newVal -= 60; - - mixin(format("_%s = cast(ubyte) newVal;", memberVarStr)); - return this; - } - - // Test roll!"minutes"(). - @safe unittest - { - static void testTOD(TimeOfDay orig, int minutes, TimeOfDay expected, size_t line = __LINE__) - { - orig.roll!"minutes"(minutes); - assert(orig == expected); - } - - testTOD(TimeOfDay(12, 30, 33), 0, TimeOfDay(12, 30, 33)); - testTOD(TimeOfDay(12, 30, 33), 1, TimeOfDay(12, 31, 33)); - testTOD(TimeOfDay(12, 30, 33), 2, TimeOfDay(12, 32, 33)); - testTOD(TimeOfDay(12, 30, 33), 3, TimeOfDay(12, 33, 33)); - testTOD(TimeOfDay(12, 30, 33), 4, TimeOfDay(12, 34, 33)); - testTOD(TimeOfDay(12, 30, 33), 5, TimeOfDay(12, 35, 33)); - testTOD(TimeOfDay(12, 30, 33), 10, TimeOfDay(12, 40, 33)); - testTOD(TimeOfDay(12, 30, 33), 15, TimeOfDay(12, 45, 33)); - testTOD(TimeOfDay(12, 30, 33), 29, TimeOfDay(12, 59, 33)); - testTOD(TimeOfDay(12, 30, 33), 30, TimeOfDay(12, 0, 33)); - testTOD(TimeOfDay(12, 30, 33), 45, TimeOfDay(12, 15, 33)); - testTOD(TimeOfDay(12, 30, 33), 60, TimeOfDay(12, 30, 33)); - testTOD(TimeOfDay(12, 30, 33), 75, TimeOfDay(12, 45, 33)); - testTOD(TimeOfDay(12, 30, 33), 90, TimeOfDay(12, 0, 33)); - testTOD(TimeOfDay(12, 30, 33), 100, TimeOfDay(12, 10, 33)); - - testTOD(TimeOfDay(12, 30, 33), 689, TimeOfDay(12, 59, 33)); - testTOD(TimeOfDay(12, 30, 33), 690, TimeOfDay(12, 0, 33)); - testTOD(TimeOfDay(12, 30, 33), 691, TimeOfDay(12, 1, 33)); - testTOD(TimeOfDay(12, 30, 33), 960, TimeOfDay(12, 30, 33)); - testTOD(TimeOfDay(12, 30, 33), 1439, TimeOfDay(12, 29, 33)); - testTOD(TimeOfDay(12, 30, 33), 1440, TimeOfDay(12, 30, 33)); - testTOD(TimeOfDay(12, 30, 33), 1441, TimeOfDay(12, 31, 33)); - testTOD(TimeOfDay(12, 30, 33), 2880, TimeOfDay(12, 30, 33)); - - testTOD(TimeOfDay(12, 30, 33), -1, TimeOfDay(12, 29, 33)); - testTOD(TimeOfDay(12, 30, 33), -2, TimeOfDay(12, 28, 33)); - testTOD(TimeOfDay(12, 30, 33), -3, TimeOfDay(12, 27, 33)); - testTOD(TimeOfDay(12, 30, 33), -4, TimeOfDay(12, 26, 33)); - testTOD(TimeOfDay(12, 30, 33), -5, TimeOfDay(12, 25, 33)); - testTOD(TimeOfDay(12, 30, 33), -10, TimeOfDay(12, 20, 33)); - testTOD(TimeOfDay(12, 30, 33), -15, TimeOfDay(12, 15, 33)); - testTOD(TimeOfDay(12, 30, 33), -29, TimeOfDay(12, 1, 33)); - testTOD(TimeOfDay(12, 30, 33), -30, TimeOfDay(12, 0, 33)); - testTOD(TimeOfDay(12, 30, 33), -45, TimeOfDay(12, 45, 33)); - testTOD(TimeOfDay(12, 30, 33), -60, TimeOfDay(12, 30, 33)); - testTOD(TimeOfDay(12, 30, 33), -75, TimeOfDay(12, 15, 33)); - testTOD(TimeOfDay(12, 30, 33), -90, TimeOfDay(12, 0, 33)); - testTOD(TimeOfDay(12, 30, 33), -100, TimeOfDay(12, 50, 33)); - - testTOD(TimeOfDay(12, 30, 33), -749, TimeOfDay(12, 1, 33)); - testTOD(TimeOfDay(12, 30, 33), -750, TimeOfDay(12, 0, 33)); - testTOD(TimeOfDay(12, 30, 33), -751, TimeOfDay(12, 59, 33)); - testTOD(TimeOfDay(12, 30, 33), -960, TimeOfDay(12, 30, 33)); - testTOD(TimeOfDay(12, 30, 33), -1439, TimeOfDay(12, 31, 33)); - testTOD(TimeOfDay(12, 30, 33), -1440, TimeOfDay(12, 30, 33)); - testTOD(TimeOfDay(12, 30, 33), -1441, TimeOfDay(12, 29, 33)); - testTOD(TimeOfDay(12, 30, 33), -2880, TimeOfDay(12, 30, 33)); - - testTOD(TimeOfDay(12, 0, 33), 1, TimeOfDay(12, 1, 33)); - testTOD(TimeOfDay(12, 0, 33), 0, TimeOfDay(12, 0, 33)); - testTOD(TimeOfDay(12, 0, 33), -1, TimeOfDay(12, 59, 33)); - - testTOD(TimeOfDay(11, 59, 33), 1, TimeOfDay(11, 0, 33)); - testTOD(TimeOfDay(11, 59, 33), 0, TimeOfDay(11, 59, 33)); - testTOD(TimeOfDay(11, 59, 33), -1, TimeOfDay(11, 58, 33)); - - testTOD(TimeOfDay(0, 0, 33), 1, TimeOfDay(0, 1, 33)); - testTOD(TimeOfDay(0, 0, 33), 0, TimeOfDay(0, 0, 33)); - testTOD(TimeOfDay(0, 0, 33), -1, TimeOfDay(0, 59, 33)); - - testTOD(TimeOfDay(23, 59, 33), 1, TimeOfDay(23, 0, 33)); - testTOD(TimeOfDay(23, 59, 33), 0, TimeOfDay(23, 59, 33)); - testTOD(TimeOfDay(23, 59, 33), -1, TimeOfDay(23, 58, 33)); - - auto tod = TimeOfDay(12, 27, 2); - tod.roll!"minutes"(97).roll!"minutes"(-102); - assert(tod == TimeOfDay(12, 22, 2)); - - const ctod = TimeOfDay(0, 0, 0); - immutable itod = TimeOfDay(0, 0, 0); - static assert(!__traits(compiles, ctod.roll!"minutes"(7))); - static assert(!__traits(compiles, itod.roll!"minutes"(7))); - } - - // Test roll!"seconds"(). - @safe unittest - { - static void testTOD(TimeOfDay orig, int seconds, TimeOfDay expected, size_t line = __LINE__) - { - orig.roll!"seconds"(seconds); - assert(orig == expected); - } - - testTOD(TimeOfDay(12, 30, 33), 0, TimeOfDay(12, 30, 33)); - testTOD(TimeOfDay(12, 30, 33), 1, TimeOfDay(12, 30, 34)); - testTOD(TimeOfDay(12, 30, 33), 2, TimeOfDay(12, 30, 35)); - testTOD(TimeOfDay(12, 30, 33), 3, TimeOfDay(12, 30, 36)); - testTOD(TimeOfDay(12, 30, 33), 4, TimeOfDay(12, 30, 37)); - testTOD(TimeOfDay(12, 30, 33), 5, TimeOfDay(12, 30, 38)); - testTOD(TimeOfDay(12, 30, 33), 10, TimeOfDay(12, 30, 43)); - testTOD(TimeOfDay(12, 30, 33), 15, TimeOfDay(12, 30, 48)); - testTOD(TimeOfDay(12, 30, 33), 26, TimeOfDay(12, 30, 59)); - testTOD(TimeOfDay(12, 30, 33), 27, TimeOfDay(12, 30, 0)); - testTOD(TimeOfDay(12, 30, 33), 30, TimeOfDay(12, 30, 3)); - testTOD(TimeOfDay(12, 30, 33), 59, TimeOfDay(12, 30, 32)); - testTOD(TimeOfDay(12, 30, 33), 60, TimeOfDay(12, 30, 33)); - testTOD(TimeOfDay(12, 30, 33), 61, TimeOfDay(12, 30, 34)); - - testTOD(TimeOfDay(12, 30, 33), 1766, TimeOfDay(12, 30, 59)); - testTOD(TimeOfDay(12, 30, 33), 1767, TimeOfDay(12, 30, 0)); - testTOD(TimeOfDay(12, 30, 33), 1768, TimeOfDay(12, 30, 1)); - testTOD(TimeOfDay(12, 30, 33), 2007, TimeOfDay(12, 30, 0)); - testTOD(TimeOfDay(12, 30, 33), 3599, TimeOfDay(12, 30, 32)); - testTOD(TimeOfDay(12, 30, 33), 3600, TimeOfDay(12, 30, 33)); - testTOD(TimeOfDay(12, 30, 33), 3601, TimeOfDay(12, 30, 34)); - testTOD(TimeOfDay(12, 30, 33), 7200, TimeOfDay(12, 30, 33)); - - testTOD(TimeOfDay(12, 30, 33), -1, TimeOfDay(12, 30, 32)); - testTOD(TimeOfDay(12, 30, 33), -2, TimeOfDay(12, 30, 31)); - testTOD(TimeOfDay(12, 30, 33), -3, TimeOfDay(12, 30, 30)); - testTOD(TimeOfDay(12, 30, 33), -4, TimeOfDay(12, 30, 29)); - testTOD(TimeOfDay(12, 30, 33), -5, TimeOfDay(12, 30, 28)); - testTOD(TimeOfDay(12, 30, 33), -10, TimeOfDay(12, 30, 23)); - testTOD(TimeOfDay(12, 30, 33), -15, TimeOfDay(12, 30, 18)); - testTOD(TimeOfDay(12, 30, 33), -33, TimeOfDay(12, 30, 0)); - testTOD(TimeOfDay(12, 30, 33), -34, TimeOfDay(12, 30, 59)); - testTOD(TimeOfDay(12, 30, 33), -35, TimeOfDay(12, 30, 58)); - testTOD(TimeOfDay(12, 30, 33), -59, TimeOfDay(12, 30, 34)); - testTOD(TimeOfDay(12, 30, 33), -60, TimeOfDay(12, 30, 33)); - testTOD(TimeOfDay(12, 30, 33), -61, TimeOfDay(12, 30, 32)); - - testTOD(TimeOfDay(12, 30, 0), 1, TimeOfDay(12, 30, 1)); - testTOD(TimeOfDay(12, 30, 0), 0, TimeOfDay(12, 30, 0)); - testTOD(TimeOfDay(12, 30, 0), -1, TimeOfDay(12, 30, 59)); - - testTOD(TimeOfDay(12, 0, 0), 1, TimeOfDay(12, 0, 1)); - testTOD(TimeOfDay(12, 0, 0), 0, TimeOfDay(12, 0, 0)); - testTOD(TimeOfDay(12, 0, 0), -1, TimeOfDay(12, 0, 59)); - - testTOD(TimeOfDay(0, 0, 0), 1, TimeOfDay(0, 0, 1)); - testTOD(TimeOfDay(0, 0, 0), 0, TimeOfDay(0, 0, 0)); - testTOD(TimeOfDay(0, 0, 0), -1, TimeOfDay(0, 0, 59)); - - testTOD(TimeOfDay(23, 59, 59), 1, TimeOfDay(23, 59, 0)); - testTOD(TimeOfDay(23, 59, 59), 0, TimeOfDay(23, 59, 59)); - testTOD(TimeOfDay(23, 59, 59), -1, TimeOfDay(23, 59, 58)); - - auto tod = TimeOfDay(12, 27, 2); - tod.roll!"seconds"(105).roll!"seconds"(-77); - assert(tod == TimeOfDay(12, 27, 30)); - - const ctod = TimeOfDay(0, 0, 0); - immutable itod = TimeOfDay(0, 0, 0); - static assert(!__traits(compiles, ctod.roll!"seconds"(7))); - static assert(!__traits(compiles, itod.roll!"seconds"(7))); - } - - - import core.time : Duration; - /++ - Gives the result of adding or subtracting a $(REF Duration, core,time) - from this $(LREF TimeOfDay). - - The legal types of arithmetic for $(LREF TimeOfDay) using this operator - are - - $(BOOKTABLE, - $(TR $(TD TimeOfDay) $(TD +) $(TD Duration) $(TD -->) $(TD TimeOfDay)) - $(TR $(TD TimeOfDay) $(TD -) $(TD Duration) $(TD -->) $(TD TimeOfDay)) - ) - - Params: - duration = The $(REF Duration, core,time) to add to or subtract from - this $(LREF TimeOfDay). - +/ - TimeOfDay opBinary(string op)(Duration duration) const @safe pure nothrow @nogc - if (op == "+" || op == "-") - { - TimeOfDay retval = this; - immutable seconds = duration.total!"seconds"; - mixin("return retval._addSeconds(" ~ op ~ "seconds);"); - } - - /// - @safe unittest - { - import core.time : hours, minutes, seconds; - - assert(TimeOfDay(12, 12, 12) + seconds(1) == TimeOfDay(12, 12, 13)); - assert(TimeOfDay(12, 12, 12) + minutes(1) == TimeOfDay(12, 13, 12)); - assert(TimeOfDay(12, 12, 12) + hours(1) == TimeOfDay(13, 12, 12)); - assert(TimeOfDay(23, 59, 59) + seconds(1) == TimeOfDay(0, 0, 0)); - - assert(TimeOfDay(12, 12, 12) - seconds(1) == TimeOfDay(12, 12, 11)); - assert(TimeOfDay(12, 12, 12) - minutes(1) == TimeOfDay(12, 11, 12)); - assert(TimeOfDay(12, 12, 12) - hours(1) == TimeOfDay(11, 12, 12)); - assert(TimeOfDay(0, 0, 0) - seconds(1) == TimeOfDay(23, 59, 59)); - } - - @safe unittest - { - auto tod = TimeOfDay(12, 30, 33); - - import core.time : dur; - assert(tod + dur!"hours"(7) == TimeOfDay(19, 30, 33)); - assert(tod + dur!"hours"(-7) == TimeOfDay(5, 30, 33)); - assert(tod + dur!"minutes"(7) == TimeOfDay(12, 37, 33)); - assert(tod + dur!"minutes"(-7) == TimeOfDay(12, 23, 33)); - assert(tod + dur!"seconds"(7) == TimeOfDay(12, 30, 40)); - assert(tod + dur!"seconds"(-7) == TimeOfDay(12, 30, 26)); - - assert(tod + dur!"msecs"(7000) == TimeOfDay(12, 30, 40)); - assert(tod + dur!"msecs"(-7000) == TimeOfDay(12, 30, 26)); - assert(tod + dur!"usecs"(7_000_000) == TimeOfDay(12, 30, 40)); - assert(tod + dur!"usecs"(-7_000_000) == TimeOfDay(12, 30, 26)); - assert(tod + dur!"hnsecs"(70_000_000) == TimeOfDay(12, 30, 40)); - assert(tod + dur!"hnsecs"(-70_000_000) == TimeOfDay(12, 30, 26)); - - assert(tod - dur!"hours"(-7) == TimeOfDay(19, 30, 33)); - assert(tod - dur!"hours"(7) == TimeOfDay(5, 30, 33)); - assert(tod - dur!"minutes"(-7) == TimeOfDay(12, 37, 33)); - assert(tod - dur!"minutes"(7) == TimeOfDay(12, 23, 33)); - assert(tod - dur!"seconds"(-7) == TimeOfDay(12, 30, 40)); - assert(tod - dur!"seconds"(7) == TimeOfDay(12, 30, 26)); - - assert(tod - dur!"msecs"(-7000) == TimeOfDay(12, 30, 40)); - assert(tod - dur!"msecs"(7000) == TimeOfDay(12, 30, 26)); - assert(tod - dur!"usecs"(-7_000_000) == TimeOfDay(12, 30, 40)); - assert(tod - dur!"usecs"(7_000_000) == TimeOfDay(12, 30, 26)); - assert(tod - dur!"hnsecs"(-70_000_000) == TimeOfDay(12, 30, 40)); - assert(tod - dur!"hnsecs"(70_000_000) == TimeOfDay(12, 30, 26)); - - auto duration = dur!"hours"(11); - const ctod = TimeOfDay(12, 30, 33); - immutable itod = TimeOfDay(12, 30, 33); - assert(tod + duration == TimeOfDay(23, 30, 33)); - assert(ctod + duration == TimeOfDay(23, 30, 33)); - assert(itod + duration == TimeOfDay(23, 30, 33)); - - assert(tod - duration == TimeOfDay(1, 30, 33)); - assert(ctod - duration == TimeOfDay(1, 30, 33)); - assert(itod - duration == TimeOfDay(1, 30, 33)); - } - - - /++ - Gives the result of adding or subtracting a $(REF Duration, core,time) - from this $(LREF TimeOfDay), as well as assigning the result to this - $(LREF TimeOfDay). - - The legal types of arithmetic for $(LREF TimeOfDay) using this operator - are - - $(BOOKTABLE, - $(TR $(TD TimeOfDay) $(TD +) $(TD Duration) $(TD -->) $(TD TimeOfDay)) - $(TR $(TD TimeOfDay) $(TD -) $(TD Duration) $(TD -->) $(TD TimeOfDay)) - ) - - Params: - duration = The $(REF Duration, core,time) to add to or subtract from - this $(LREF TimeOfDay). - +/ - ref TimeOfDay opOpAssign(string op)(Duration duration) @safe pure nothrow @nogc - if (op == "+" || op == "-") - { - immutable seconds = duration.total!"seconds"; - mixin("return _addSeconds(" ~ op ~ "seconds);"); - } - - @safe unittest - { - import core.time : dur; - auto duration = dur!"hours"(12); - - assert(TimeOfDay(12, 30, 33) + dur!"hours"(7) == TimeOfDay(19, 30, 33)); - assert(TimeOfDay(12, 30, 33) + dur!"hours"(-7) == TimeOfDay(5, 30, 33)); - assert(TimeOfDay(12, 30, 33) + dur!"minutes"(7) == TimeOfDay(12, 37, 33)); - assert(TimeOfDay(12, 30, 33) + dur!"minutes"(-7) == TimeOfDay(12, 23, 33)); - assert(TimeOfDay(12, 30, 33) + dur!"seconds"(7) == TimeOfDay(12, 30, 40)); - assert(TimeOfDay(12, 30, 33) + dur!"seconds"(-7) == TimeOfDay(12, 30, 26)); - - assert(TimeOfDay(12, 30, 33) + dur!"msecs"(7000) == TimeOfDay(12, 30, 40)); - assert(TimeOfDay(12, 30, 33) + dur!"msecs"(-7000) == TimeOfDay(12, 30, 26)); - assert(TimeOfDay(12, 30, 33) + dur!"usecs"(7_000_000) == TimeOfDay(12, 30, 40)); - assert(TimeOfDay(12, 30, 33) + dur!"usecs"(-7_000_000) == TimeOfDay(12, 30, 26)); - assert(TimeOfDay(12, 30, 33) + dur!"hnsecs"(70_000_000) == TimeOfDay(12, 30, 40)); - assert(TimeOfDay(12, 30, 33) + dur!"hnsecs"(-70_000_000) == TimeOfDay(12, 30, 26)); - - assert(TimeOfDay(12, 30, 33) - dur!"hours"(-7) == TimeOfDay(19, 30, 33)); - assert(TimeOfDay(12, 30, 33) - dur!"hours"(7) == TimeOfDay(5, 30, 33)); - assert(TimeOfDay(12, 30, 33) - dur!"minutes"(-7) == TimeOfDay(12, 37, 33)); - assert(TimeOfDay(12, 30, 33) - dur!"minutes"(7) == TimeOfDay(12, 23, 33)); - assert(TimeOfDay(12, 30, 33) - dur!"seconds"(-7) == TimeOfDay(12, 30, 40)); - assert(TimeOfDay(12, 30, 33) - dur!"seconds"(7) == TimeOfDay(12, 30, 26)); - - assert(TimeOfDay(12, 30, 33) - dur!"msecs"(-7000) == TimeOfDay(12, 30, 40)); - assert(TimeOfDay(12, 30, 33) - dur!"msecs"(7000) == TimeOfDay(12, 30, 26)); - assert(TimeOfDay(12, 30, 33) - dur!"usecs"(-7_000_000) == TimeOfDay(12, 30, 40)); - assert(TimeOfDay(12, 30, 33) - dur!"usecs"(7_000_000) == TimeOfDay(12, 30, 26)); - assert(TimeOfDay(12, 30, 33) - dur!"hnsecs"(-70_000_000) == TimeOfDay(12, 30, 40)); - assert(TimeOfDay(12, 30, 33) - dur!"hnsecs"(70_000_000) == TimeOfDay(12, 30, 26)); - - auto tod = TimeOfDay(19, 17, 22); - (tod += dur!"seconds"(9)) += dur!"seconds"(-7292); - assert(tod == TimeOfDay(17, 15, 59)); - - const ctod = TimeOfDay(12, 33, 30); - immutable itod = TimeOfDay(12, 33, 30); - static assert(!__traits(compiles, ctod += duration)); - static assert(!__traits(compiles, itod += duration)); - static assert(!__traits(compiles, ctod -= duration)); - static assert(!__traits(compiles, itod -= duration)); - } - - - /++ - Gives the difference between two $(LREF TimeOfDay)s. - - The legal types of arithmetic for $(LREF TimeOfDay) using this operator - are - - $(BOOKTABLE, - $(TR $(TD TimeOfDay) $(TD -) $(TD TimeOfDay) $(TD -->) $(TD duration)) - ) - - Params: - rhs = The $(LREF TimeOfDay) to subtract from this one. - +/ - Duration opBinary(string op)(TimeOfDay rhs) const @safe pure nothrow @nogc - if (op == "-") - { - immutable lhsSec = _hour * 3600 + _minute * 60 + _second; - immutable rhsSec = rhs._hour * 3600 + rhs._minute * 60 + rhs._second; - - import core.time : dur; - return dur!"seconds"(lhsSec - rhsSec); - } - - @safe unittest - { - auto tod = TimeOfDay(12, 30, 33); - - import core.time : dur; - assert(TimeOfDay(7, 12, 52) - TimeOfDay(12, 30, 33) == dur!"seconds"(-19_061)); - assert(TimeOfDay(12, 30, 33) - TimeOfDay(7, 12, 52) == dur!"seconds"(19_061)); - assert(TimeOfDay(12, 30, 33) - TimeOfDay(14, 30, 33) == dur!"seconds"(-7200)); - assert(TimeOfDay(14, 30, 33) - TimeOfDay(12, 30, 33) == dur!"seconds"(7200)); - assert(TimeOfDay(12, 30, 33) - TimeOfDay(12, 34, 33) == dur!"seconds"(-240)); - assert(TimeOfDay(12, 34, 33) - TimeOfDay(12, 30, 33) == dur!"seconds"(240)); - assert(TimeOfDay(12, 30, 33) - TimeOfDay(12, 30, 34) == dur!"seconds"(-1)); - assert(TimeOfDay(12, 30, 34) - TimeOfDay(12, 30, 33) == dur!"seconds"(1)); - - const ctod = TimeOfDay(12, 30, 33); - immutable itod = TimeOfDay(12, 30, 33); - assert(tod - tod == Duration.zero); - assert(ctod - tod == Duration.zero); - assert(itod - tod == Duration.zero); - - assert(tod - ctod == Duration.zero); - assert(ctod - ctod == Duration.zero); - assert(itod - ctod == Duration.zero); - - assert(tod - itod == Duration.zero); - assert(ctod - itod == Duration.zero); - assert(itod - itod == Duration.zero); - } - - - /++ - Converts this $(LREF TimeOfDay) to a string with the format `HHMMSS`. - If `writer` is set, the resulting string will be written directly to it. - - Params: - writer = A `char` accepting $(REF_ALTTEXT output range, isOutputRange, std, range, primitives) - Returns: - A `string` when not using an output range; `void` otherwise. - +/ - string toISOString() const @safe pure nothrow - { - import std.array : appender; - auto w = appender!string(); - w.reserve(6); - try - toISOString(w); - catch (Exception e) - assert(0, "toISOString() threw."); - return w.data; - } - - /// ditto - void toISOString(W)(ref W writer) const - if (isOutputRange!(W, char)) - { - import std.format.write : formattedWrite; - formattedWrite(writer, "%02d%02d%02d", _hour, _minute, _second); - } - - /// - @safe unittest - { - assert(TimeOfDay(0, 0, 0).toISOString() == "000000"); - assert(TimeOfDay(12, 30, 33).toISOString() == "123033"); - } - - @safe unittest - { - auto tod = TimeOfDay(12, 30, 33); - const ctod = TimeOfDay(12, 30, 33); - immutable itod = TimeOfDay(12, 30, 33); - assert(tod.toISOString() == "123033"); - assert(ctod.toISOString() == "123033"); - assert(itod.toISOString() == "123033"); - } - - - /++ - Converts this $(LREF TimeOfDay) to a string with the format `HH:MM:SS`. - If `writer` is set, the resulting string will be written directly to it. - - Params: - writer = A `char` accepting $(REF_ALTTEXT output range, isOutputRange, std, range, primitives) - Returns: - A `string` when not using an output range; `void` otherwise. - +/ - string toISOExtString() const @safe pure nothrow - { - import std.array : appender; - auto w = appender!string(); - w.reserve(8); - try - toISOExtString(w); - catch (Exception e) - assert(0, "toISOExtString() threw."); - return w.data; - } - - /// ditto - void toISOExtString(W)(ref W writer) const - if (isOutputRange!(W, char)) - { - import std.format.write : formattedWrite; - formattedWrite(writer, "%02d:%02d:%02d", _hour, _minute, _second); - } - - /// - @safe unittest - { - assert(TimeOfDay(0, 0, 0).toISOExtString() == "00:00:00"); - assert(TimeOfDay(12, 30, 33).toISOExtString() == "12:30:33"); - } - - @safe unittest - { - auto tod = TimeOfDay(12, 30, 33); - const ctod = TimeOfDay(12, 30, 33); - immutable itod = TimeOfDay(12, 30, 33); - assert(tod.toISOExtString() == "12:30:33"); - assert(ctod.toISOExtString() == "12:30:33"); - assert(itod.toISOExtString() == "12:30:33"); - } - - - /++ - Converts this TimeOfDay to a string. - - This function exists to make it easy to convert a $(LREF TimeOfDay) to a - string for code that does not care what the exact format is - just that - it presents the information in a clear manner. It also makes it easy to - simply convert a $(LREF TimeOfDay) to a string when using functions such - as `to!string`, `format`, or `writeln` which use toString to convert - user-defined types. So, it is unlikely that much code will call - toString directly. - - The format of the string is purposefully unspecified, and code that - cares about the format of the string should use `toISOString`, - `toISOExtString`, or some other custom formatting function that - explicitly generates the format that the code needs. The reason is that - the code is then clear about what format it's using, making it less - error-prone to maintain the code and interact with other software that - consumes the generated strings. It's for this same reason that - $(LREF TimeOfDay) has no `fromString` function, whereas it does have - `fromISOString` and `fromISOExtString`. - - The format returned by toString may or may not change in the future. - - Params: - writer = A `char` accepting $(REF_ALTTEXT output range, isOutputRange, std, range, primitives) - Returns: - A `string` when not using an output range; `void` otherwise. - +/ - string toString() const @safe pure nothrow - { - return toISOExtString(); - } - - /// ditto - void toString(W)(ref W writer) const - if (isOutputRange!(W, char)) - { - toISOExtString(writer); - } - - @safe unittest - { - auto tod = TimeOfDay(12, 30, 33); - const ctod = TimeOfDay(12, 30, 33); - immutable itod = TimeOfDay(12, 30, 33); - assert(tod.toString()); - assert(ctod.toString()); - assert(itod.toString()); - } - - - /++ - Creates a $(LREF TimeOfDay) from a string with the format HHMMSS. - Whitespace is stripped from the given string. - - Params: - isoString = A string formatted in the ISO format for times. - - Throws: - $(REF DateTimeException,std,datetime,date) if the given string is - not in the ISO format or if the resulting $(LREF TimeOfDay) would - not be valid. - +/ - static TimeOfDay fromISOString(S)(scope const S isoString) @safe pure - if (isSomeString!S) - { - import std.conv : to, text, ConvException; - import std.exception : enforce; - import std.string : strip; - - int hours, minutes, seconds; - auto str = strip(isoString); - - enforce!DateTimeException(str.length == 6, text("Invalid ISO String: ", isoString)); - - try - { - // cast to int from uint is used because it checks for - // non digits without extra loops - hours = cast(int) to!uint(str[0 .. 2]); - minutes = cast(int) to!uint(str[2 .. 4]); - seconds = cast(int) to!uint(str[4 .. $]); - } - catch (ConvException) - { - throw new DateTimeException(text("Invalid ISO String: ", isoString)); - } - - return TimeOfDay(hours, minutes, seconds); - } - - /// - @safe unittest - { - assert(TimeOfDay.fromISOString("000000") == TimeOfDay(0, 0, 0)); - assert(TimeOfDay.fromISOString("123033") == TimeOfDay(12, 30, 33)); - assert(TimeOfDay.fromISOString(" 123033 ") == TimeOfDay(12, 30, 33)); - } - - @safe unittest - { - assertThrown!DateTimeException(TimeOfDay.fromISOString("")); - assertThrown!DateTimeException(TimeOfDay.fromISOString("0")); - assertThrown!DateTimeException(TimeOfDay.fromISOString("00")); - assertThrown!DateTimeException(TimeOfDay.fromISOString("000")); - assertThrown!DateTimeException(TimeOfDay.fromISOString("0000")); - assertThrown!DateTimeException(TimeOfDay.fromISOString("00000")); - assertThrown!DateTimeException(TimeOfDay.fromISOString("13033")); - assertThrown!DateTimeException(TimeOfDay.fromISOString("1277")); - assertThrown!DateTimeException(TimeOfDay.fromISOString("12707")); - assertThrown!DateTimeException(TimeOfDay.fromISOString("12070")); - assertThrown!DateTimeException(TimeOfDay.fromISOString("12303a")); - assertThrown!DateTimeException(TimeOfDay.fromISOString("1230a3")); - assertThrown!DateTimeException(TimeOfDay.fromISOString("123a33")); - assertThrown!DateTimeException(TimeOfDay.fromISOString("12a033")); - assertThrown!DateTimeException(TimeOfDay.fromISOString("1a0033")); - assertThrown!DateTimeException(TimeOfDay.fromISOString("a20033")); - assertThrown!DateTimeException(TimeOfDay.fromISOString("1200330")); - assertThrown!DateTimeException(TimeOfDay.fromISOString("0120033")); - assertThrown!DateTimeException(TimeOfDay.fromISOString("-120033")); - assertThrown!DateTimeException(TimeOfDay.fromISOString("+120033")); - assertThrown!DateTimeException(TimeOfDay.fromISOString("120033am")); - assertThrown!DateTimeException(TimeOfDay.fromISOString("120033pm")); - - assertThrown!DateTimeException(TimeOfDay.fromISOString("0::")); - assertThrown!DateTimeException(TimeOfDay.fromISOString(":0:")); - assertThrown!DateTimeException(TimeOfDay.fromISOString("::0")); - assertThrown!DateTimeException(TimeOfDay.fromISOString("0:0:0")); - assertThrown!DateTimeException(TimeOfDay.fromISOString("0:0:00")); - assertThrown!DateTimeException(TimeOfDay.fromISOString("0:00:0")); - assertThrown!DateTimeException(TimeOfDay.fromISOString("00:0:0")); - assertThrown!DateTimeException(TimeOfDay.fromISOString("00:00:0")); - assertThrown!DateTimeException(TimeOfDay.fromISOString("00:0:00")); - assertThrown!DateTimeException(TimeOfDay.fromISOString("13:0:33")); - assertThrown!DateTimeException(TimeOfDay.fromISOString("12:7:7")); - assertThrown!DateTimeException(TimeOfDay.fromISOString("12:7:07")); - assertThrown!DateTimeException(TimeOfDay.fromISOString("12:07:0")); - assertThrown!DateTimeException(TimeOfDay.fromISOString("12:30:3a")); - assertThrown!DateTimeException(TimeOfDay.fromISOString("12:30:a3")); - assertThrown!DateTimeException(TimeOfDay.fromISOString("12:3a:33")); - assertThrown!DateTimeException(TimeOfDay.fromISOString("12:a0:33")); - assertThrown!DateTimeException(TimeOfDay.fromISOString("1a:00:33")); - assertThrown!DateTimeException(TimeOfDay.fromISOString("a2:00:33")); - assertThrown!DateTimeException(TimeOfDay.fromISOString("12:003:30")); - assertThrown!DateTimeException(TimeOfDay.fromISOString("120:03:30")); - assertThrown!DateTimeException(TimeOfDay.fromISOString("012:00:33")); - assertThrown!DateTimeException(TimeOfDay.fromISOString("01:200:33")); - assertThrown!DateTimeException(TimeOfDay.fromISOString("-12:00:33")); - assertThrown!DateTimeException(TimeOfDay.fromISOString("+12:00:33")); - assertThrown!DateTimeException(TimeOfDay.fromISOString("12:00:33am")); - assertThrown!DateTimeException(TimeOfDay.fromISOString("12:00:33pm")); - - assertThrown!DateTimeException(TimeOfDay.fromISOString("12:00:33")); - - assert(TimeOfDay.fromISOString("011217") == TimeOfDay(1, 12, 17)); - assert(TimeOfDay.fromISOString("001412") == TimeOfDay(0, 14, 12)); - assert(TimeOfDay.fromISOString("000007") == TimeOfDay(0, 0, 7)); - assert(TimeOfDay.fromISOString("011217 ") == TimeOfDay(1, 12, 17)); - assert(TimeOfDay.fromISOString(" 011217") == TimeOfDay(1, 12, 17)); - assert(TimeOfDay.fromISOString(" 011217 ") == TimeOfDay(1, 12, 17)); - } - - // https://issues.dlang.org/show_bug.cgi?id=17801 - @safe unittest - { - import std.conv : to; - import std.meta : AliasSeq; - static foreach (C; AliasSeq!(char, wchar, dchar)) - { - static foreach (S; AliasSeq!(C[], const(C)[], immutable(C)[])) - assert(TimeOfDay.fromISOString(to!S("141516")) == TimeOfDay(14, 15, 16)); - } - } - - - /++ - Creates a $(LREF TimeOfDay) from a string with the format HH:MM:SS. - Whitespace is stripped from the given string. - - Params: - isoExtString = A string formatted in the ISO Extended format for - times. - - Throws: - $(REF DateTimeException,std,datetime,date) if the given string is - not in the ISO Extended format or if the resulting $(LREF TimeOfDay) - would not be valid. - +/ - static TimeOfDay fromISOExtString(S)(scope const S isoExtString) @safe pure - if (isSomeString!S) - { - import std.conv : ConvException, text, to; - import std.string : strip; - - auto str = strip(isoExtString); - int hours, minutes, seconds; - - if (str.length != 8 || str[2] != ':' || str[5] != ':') - throw new DateTimeException(text("Invalid ISO Extended String: ", isoExtString)); - - try - { - // cast to int from uint is used because it checks for - // non digits without extra loops - hours = cast(int) to!uint(str[0 .. 2]); - minutes = cast(int) to!uint(str[3 .. 5]); - seconds = cast(int) to!uint(str[6 .. $]); - } - catch (ConvException) - { - throw new DateTimeException(text("Invalid ISO Extended String: ", isoExtString)); - } - - return TimeOfDay(hours, minutes, seconds); - } - - /// - @safe unittest - { - assert(TimeOfDay.fromISOExtString("00:00:00") == TimeOfDay(0, 0, 0)); - assert(TimeOfDay.fromISOExtString("12:30:33") == TimeOfDay(12, 30, 33)); - assert(TimeOfDay.fromISOExtString(" 12:30:33 ") == TimeOfDay(12, 30, 33)); - } - - @safe unittest - { - assertThrown!DateTimeException(TimeOfDay.fromISOExtString("")); - assertThrown!DateTimeException(TimeOfDay.fromISOExtString("0")); - assertThrown!DateTimeException(TimeOfDay.fromISOExtString("00")); - assertThrown!DateTimeException(TimeOfDay.fromISOExtString("000")); - assertThrown!DateTimeException(TimeOfDay.fromISOExtString("0000")); - assertThrown!DateTimeException(TimeOfDay.fromISOExtString("00000")); - assertThrown!DateTimeException(TimeOfDay.fromISOExtString("13033")); - assertThrown!DateTimeException(TimeOfDay.fromISOExtString("1277")); - assertThrown!DateTimeException(TimeOfDay.fromISOExtString("12707")); - assertThrown!DateTimeException(TimeOfDay.fromISOExtString("12070")); - assertThrown!DateTimeException(TimeOfDay.fromISOExtString("12303a")); - assertThrown!DateTimeException(TimeOfDay.fromISOExtString("1230a3")); - assertThrown!DateTimeException(TimeOfDay.fromISOExtString("123a33")); - assertThrown!DateTimeException(TimeOfDay.fromISOExtString("12a033")); - assertThrown!DateTimeException(TimeOfDay.fromISOExtString("1a0033")); - assertThrown!DateTimeException(TimeOfDay.fromISOExtString("a20033")); - assertThrown!DateTimeException(TimeOfDay.fromISOExtString("1200330")); - assertThrown!DateTimeException(TimeOfDay.fromISOExtString("0120033")); - assertThrown!DateTimeException(TimeOfDay.fromISOExtString("-120033")); - assertThrown!DateTimeException(TimeOfDay.fromISOExtString("+120033")); - assertThrown!DateTimeException(TimeOfDay.fromISOExtString("120033am")); - assertThrown!DateTimeException(TimeOfDay.fromISOExtString("120033pm")); - - assertThrown!DateTimeException(TimeOfDay.fromISOExtString("0::")); - assertThrown!DateTimeException(TimeOfDay.fromISOExtString(":0:")); - assertThrown!DateTimeException(TimeOfDay.fromISOExtString("::0")); - assertThrown!DateTimeException(TimeOfDay.fromISOExtString("0:0:0")); - assertThrown!DateTimeException(TimeOfDay.fromISOExtString("0:0:00")); - assertThrown!DateTimeException(TimeOfDay.fromISOExtString("0:00:0")); - assertThrown!DateTimeException(TimeOfDay.fromISOExtString("00:0:0")); - assertThrown!DateTimeException(TimeOfDay.fromISOExtString("00:00:0")); - assertThrown!DateTimeException(TimeOfDay.fromISOExtString("00:0:00")); - assertThrown!DateTimeException(TimeOfDay.fromISOExtString("13:0:33")); - assertThrown!DateTimeException(TimeOfDay.fromISOExtString("12:7:7")); - assertThrown!DateTimeException(TimeOfDay.fromISOExtString("12:7:07")); - assertThrown!DateTimeException(TimeOfDay.fromISOExtString("12:07:0")); - assertThrown!DateTimeException(TimeOfDay.fromISOExtString("12:30:3a")); - assertThrown!DateTimeException(TimeOfDay.fromISOExtString("12:30:a3")); - assertThrown!DateTimeException(TimeOfDay.fromISOExtString("12:3a:33")); - assertThrown!DateTimeException(TimeOfDay.fromISOExtString("12:a0:33")); - assertThrown!DateTimeException(TimeOfDay.fromISOExtString("1a:00:33")); - assertThrown!DateTimeException(TimeOfDay.fromISOExtString("a2:00:33")); - assertThrown!DateTimeException(TimeOfDay.fromISOExtString("12:003:30")); - assertThrown!DateTimeException(TimeOfDay.fromISOExtString("120:03:30")); - assertThrown!DateTimeException(TimeOfDay.fromISOExtString("012:00:33")); - assertThrown!DateTimeException(TimeOfDay.fromISOExtString("01:200:33")); - assertThrown!DateTimeException(TimeOfDay.fromISOExtString("-12:00:33")); - assertThrown!DateTimeException(TimeOfDay.fromISOExtString("+12:00:33")); - assertThrown!DateTimeException(TimeOfDay.fromISOExtString("12:00:33am")); - assertThrown!DateTimeException(TimeOfDay.fromISOExtString("12:00:33pm")); - - assertThrown!DateTimeException(TimeOfDay.fromISOExtString("120033")); - - assert(TimeOfDay.fromISOExtString("01:12:17") == TimeOfDay(1, 12, 17)); - assert(TimeOfDay.fromISOExtString("00:14:12") == TimeOfDay(0, 14, 12)); - assert(TimeOfDay.fromISOExtString("00:00:07") == TimeOfDay(0, 0, 7)); - assert(TimeOfDay.fromISOExtString("01:12:17 ") == TimeOfDay(1, 12, 17)); - assert(TimeOfDay.fromISOExtString(" 01:12:17") == TimeOfDay(1, 12, 17)); - assert(TimeOfDay.fromISOExtString(" 01:12:17 ") == TimeOfDay(1, 12, 17)); - } - - // https://issues.dlang.org/show_bug.cgi?id=17801 - @safe unittest - { - import std.conv : to; - import std.meta : AliasSeq; - static foreach (C; AliasSeq!(char, wchar, dchar)) - { - static foreach (S; AliasSeq!(C[], const(C)[], immutable(C)[])) - assert(TimeOfDay.fromISOExtString(to!S("14:15:16")) == TimeOfDay(14, 15, 16)); - } - } - - - /++ - Returns midnight. - +/ - @property static TimeOfDay min() @safe pure nothrow @nogc - { - return TimeOfDay.init; - } - - @safe unittest - { - assert(TimeOfDay.min.hour == 0); - assert(TimeOfDay.min.minute == 0); - assert(TimeOfDay.min.second == 0); - assert(TimeOfDay.min < TimeOfDay.max); - } - - - /++ - Returns one second short of midnight. - +/ - @property static TimeOfDay max() @safe pure nothrow @nogc - { - auto tod = TimeOfDay.init; - tod._hour = maxHour; - tod._minute = maxMinute; - tod._second = maxSecond; - - return tod; - } - - @safe unittest - { - assert(TimeOfDay.max.hour == 23); - assert(TimeOfDay.max.minute == 59); - assert(TimeOfDay.max.second == 59); - assert(TimeOfDay.max > TimeOfDay.min); - } - - -private: - - /+ - Add seconds to the time of day. Negative values will subtract. If the - number of seconds overflows (or underflows), then the seconds will wrap, - increasing (or decreasing) the number of minutes accordingly. If the - number of minutes overflows (or underflows), then the minutes will wrap. - If the number of minutes overflows(or underflows), then the hour will - wrap. (e.g. adding 90 seconds to 23:59:00 would result in 00:00:30). - - Params: - seconds = The number of seconds to add to this TimeOfDay. - +/ - ref TimeOfDay _addSeconds(long seconds) return @safe pure nothrow @nogc - { - import core.time : convert; - long hnsecs = convert!("seconds", "hnsecs")(seconds); - hnsecs += convert!("hours", "hnsecs")(_hour); - hnsecs += convert!("minutes", "hnsecs")(_minute); - hnsecs += convert!("seconds", "hnsecs")(_second); - - hnsecs %= convert!("days", "hnsecs")(1); - - if (hnsecs < 0) - hnsecs += convert!("days", "hnsecs")(1); - - immutable newHours = splitUnitsFromHNSecs!"hours"(hnsecs); - immutable newMinutes = splitUnitsFromHNSecs!"minutes"(hnsecs); - immutable newSeconds = splitUnitsFromHNSecs!"seconds"(hnsecs); - - _hour = cast(ubyte) newHours; - _minute = cast(ubyte) newMinutes; - _second = cast(ubyte) newSeconds; - - return this; - } - - @safe unittest - { - static void testTOD(TimeOfDay orig, int seconds, TimeOfDay expected, size_t line = __LINE__) - { - orig._addSeconds(seconds); - assert(orig == expected); - } - - testTOD(TimeOfDay(12, 30, 33), 0, TimeOfDay(12, 30, 33)); - testTOD(TimeOfDay(12, 30, 33), 1, TimeOfDay(12, 30, 34)); - testTOD(TimeOfDay(12, 30, 33), 2, TimeOfDay(12, 30, 35)); - testTOD(TimeOfDay(12, 30, 33), 3, TimeOfDay(12, 30, 36)); - testTOD(TimeOfDay(12, 30, 33), 4, TimeOfDay(12, 30, 37)); - testTOD(TimeOfDay(12, 30, 33), 5, TimeOfDay(12, 30, 38)); - testTOD(TimeOfDay(12, 30, 33), 10, TimeOfDay(12, 30, 43)); - testTOD(TimeOfDay(12, 30, 33), 15, TimeOfDay(12, 30, 48)); - testTOD(TimeOfDay(12, 30, 33), 26, TimeOfDay(12, 30, 59)); - testTOD(TimeOfDay(12, 30, 33), 27, TimeOfDay(12, 31, 0)); - testTOD(TimeOfDay(12, 30, 33), 30, TimeOfDay(12, 31, 3)); - testTOD(TimeOfDay(12, 30, 33), 59, TimeOfDay(12, 31, 32)); - testTOD(TimeOfDay(12, 30, 33), 60, TimeOfDay(12, 31, 33)); - testTOD(TimeOfDay(12, 30, 33), 61, TimeOfDay(12, 31, 34)); - - testTOD(TimeOfDay(12, 30, 33), 1766, TimeOfDay(12, 59, 59)); - testTOD(TimeOfDay(12, 30, 33), 1767, TimeOfDay(13, 0, 0)); - testTOD(TimeOfDay(12, 30, 33), 1768, TimeOfDay(13, 0, 1)); - testTOD(TimeOfDay(12, 30, 33), 2007, TimeOfDay(13, 4, 0)); - testTOD(TimeOfDay(12, 30, 33), 3599, TimeOfDay(13, 30, 32)); - testTOD(TimeOfDay(12, 30, 33), 3600, TimeOfDay(13, 30, 33)); - testTOD(TimeOfDay(12, 30, 33), 3601, TimeOfDay(13, 30, 34)); - testTOD(TimeOfDay(12, 30, 33), 7200, TimeOfDay(14, 30, 33)); - - testTOD(TimeOfDay(12, 30, 33), -1, TimeOfDay(12, 30, 32)); - testTOD(TimeOfDay(12, 30, 33), -2, TimeOfDay(12, 30, 31)); - testTOD(TimeOfDay(12, 30, 33), -3, TimeOfDay(12, 30, 30)); - testTOD(TimeOfDay(12, 30, 33), -4, TimeOfDay(12, 30, 29)); - testTOD(TimeOfDay(12, 30, 33), -5, TimeOfDay(12, 30, 28)); - testTOD(TimeOfDay(12, 30, 33), -10, TimeOfDay(12, 30, 23)); - testTOD(TimeOfDay(12, 30, 33), -15, TimeOfDay(12, 30, 18)); - testTOD(TimeOfDay(12, 30, 33), -33, TimeOfDay(12, 30, 0)); - testTOD(TimeOfDay(12, 30, 33), -34, TimeOfDay(12, 29, 59)); - testTOD(TimeOfDay(12, 30, 33), -35, TimeOfDay(12, 29, 58)); - testTOD(TimeOfDay(12, 30, 33), -59, TimeOfDay(12, 29, 34)); - testTOD(TimeOfDay(12, 30, 33), -60, TimeOfDay(12, 29, 33)); - testTOD(TimeOfDay(12, 30, 33), -61, TimeOfDay(12, 29, 32)); - - testTOD(TimeOfDay(12, 30, 33), -1833, TimeOfDay(12, 0, 0)); - testTOD(TimeOfDay(12, 30, 33), -1834, TimeOfDay(11, 59, 59)); - testTOD(TimeOfDay(12, 30, 33), -3600, TimeOfDay(11, 30, 33)); - testTOD(TimeOfDay(12, 30, 33), -3601, TimeOfDay(11, 30, 32)); - testTOD(TimeOfDay(12, 30, 33), -5134, TimeOfDay(11, 4, 59)); - testTOD(TimeOfDay(12, 30, 33), -7200, TimeOfDay(10, 30, 33)); - - testTOD(TimeOfDay(12, 30, 0), 1, TimeOfDay(12, 30, 1)); - testTOD(TimeOfDay(12, 30, 0), 0, TimeOfDay(12, 30, 0)); - testTOD(TimeOfDay(12, 30, 0), -1, TimeOfDay(12, 29, 59)); - - testTOD(TimeOfDay(12, 0, 0), 1, TimeOfDay(12, 0, 1)); - testTOD(TimeOfDay(12, 0, 0), 0, TimeOfDay(12, 0, 0)); - testTOD(TimeOfDay(12, 0, 0), -1, TimeOfDay(11, 59, 59)); - - testTOD(TimeOfDay(0, 0, 0), 1, TimeOfDay(0, 0, 1)); - testTOD(TimeOfDay(0, 0, 0), 0, TimeOfDay(0, 0, 0)); - testTOD(TimeOfDay(0, 0, 0), -1, TimeOfDay(23, 59, 59)); - - testTOD(TimeOfDay(23, 59, 59), 1, TimeOfDay(0, 0, 0)); - testTOD(TimeOfDay(23, 59, 59), 0, TimeOfDay(23, 59, 59)); - testTOD(TimeOfDay(23, 59, 59), -1, TimeOfDay(23, 59, 58)); - - const ctod = TimeOfDay(0, 0, 0); - immutable itod = TimeOfDay(0, 0, 0); - static assert(!__traits(compiles, ctod._addSeconds(7))); - static assert(!__traits(compiles, itod._addSeconds(7))); - } - - - /+ - Whether the given values form a valid $(LREF TimeOfDay). - +/ - static bool _valid(int hour, int minute, int second) @safe pure nothrow @nogc - { - return valid!"hours"(hour) && valid!"minutes"(minute) && valid!"seconds"(second); - } - - - @safe pure invariant() - { - import std.format : format; - assert(_valid(_hour, _minute, _second), - format("Invariant Failure: hour [%s] minute [%s] second [%s]", _hour, _minute, _second)); - } - - -package: - - ubyte _hour; - ubyte _minute; - ubyte _second; - - enum ubyte maxHour = 24 - 1; - enum ubyte maxMinute = 60 - 1; - enum ubyte maxSecond = 60 - 1; -} - -/// -@safe pure unittest -{ - import core.time : minutes, seconds; - - auto t = TimeOfDay(12, 30, 0); - - t += 10.minutes + 100.seconds; - assert(t == TimeOfDay(12, 41, 40)); - - assert(t.toISOExtString() == "12:41:40"); - assert(t.toISOString() == "124140"); - - assert(TimeOfDay.fromISOExtString("15:00:00") == TimeOfDay(15, 0, 0)); - assert(TimeOfDay.fromISOString("015000") == TimeOfDay(1, 50, 0)); -} - -/++ - Returns whether the given value is valid for the given unit type when in a - time point. Naturally, a duration is not held to a particular range, but - the values in a time point are (e.g. a month must be in the range of - 1 - 12 inclusive). - - Params: - units = The units of time to validate. - value = The number to validate. - +/ -bool valid(string units)(int value) @safe pure nothrow @nogc -if (units == "months" || - units == "hours" || - units == "minutes" || - units == "seconds") -{ - static if (units == "months") - return value >= Month.jan && value <= Month.dec; - else static if (units == "hours") - return value >= 0 && value <= 23; - else static if (units == "minutes") - return value >= 0 && value <= 59; - else static if (units == "seconds") - return value >= 0 && value <= 59; -} - -/// -@safe unittest -{ - assert(valid!"hours"(12)); - assert(!valid!"hours"(32)); - assert(valid!"months"(12)); - assert(!valid!"months"(13)); -} - -/++ - Returns whether the given day is valid for the given year and month. - - Params: - units = The units of time to validate. - year = The year of the day to validate. - month = The month of the day to validate (January is 1). - day = The day to validate. - +/ -bool valid(string units)(int year, int month, int day) @safe pure nothrow @nogc -if (units == "days") -{ - return day > 0 && day <= maxDay(year, month); -} - -/// -@safe pure nothrow @nogc unittest -{ - assert(valid!"days"(2016, 2, 29)); - assert(!valid!"days"(2016, 2, 30)); - assert(valid!"days"(2017, 2, 20)); - assert(!valid!"days"(2017, 2, 29)); -} - - -/++ - Params: - units = The units of time to validate. - value = The number to validate. - file = The file that the $(LREF DateTimeException) will list if thrown. - line = The line number that the $(LREF DateTimeException) will list if - thrown. - - Throws: - $(LREF DateTimeException) if `valid!units(value)` is false. - +/ -void enforceValid(string units)(int value, string file = __FILE__, size_t line = __LINE__) @safe pure -if (units == "months" || - units == "hours" || - units == "minutes" || - units == "seconds") -{ - import std.format : format; - - static if (units == "months") - { - if (!valid!units(value)) - throw new DateTimeException(format("%s is not a valid month of the year.", value), file, line); - } - else static if (units == "hours") - { - if (!valid!units(value)) - throw new DateTimeException(format("%s is not a valid hour of the day.", value), file, line); - } - else static if (units == "minutes") - { - if (!valid!units(value)) - throw new DateTimeException(format("%s is not a valid minute of an hour.", value), file, line); - } - else static if (units == "seconds") - { - if (!valid!units(value)) - throw new DateTimeException(format("%s is not a valid second of a minute.", value), file, line); - } -} - -/// -@safe pure unittest -{ - import std.exception : assertThrown, assertNotThrown; - - assertNotThrown(enforceValid!"months"(10)); - assertNotThrown(enforceValid!"seconds"(40)); - - assertThrown!DateTimeException(enforceValid!"months"(0)); - assertThrown!DateTimeException(enforceValid!"hours"(24)); - assertThrown!DateTimeException(enforceValid!"minutes"(60)); - assertThrown!DateTimeException(enforceValid!"seconds"(60)); -} - - -/++ - Because the validity of the day number depends on both on the year - and month of which the day is occurring, take all three variables - to validate the day. - - Params: - units = The units of time to validate. - year = The year of the day to validate. - month = The month of the day to validate. - day = The day to validate. - file = The file that the $(LREF DateTimeException) will list if thrown. - line = The line number that the $(LREF DateTimeException) will list if - thrown. - - Throws: - $(LREF DateTimeException) if $(D valid!"days"(year, month, day)) is false. - +/ -void enforceValid(string units) - (int year, Month month, int day, string file = __FILE__, size_t line = __LINE__) @safe pure -if (units == "days") -{ - import std.format : format; - if (!valid!"days"(year, month, day)) - throw new DateTimeException(format("%s is not a valid day in %s in %s", day, month, year), file, line); -} - -/// -@safe pure unittest -{ - import std.exception : assertThrown, assertNotThrown; - - assertNotThrown(enforceValid!"days"(2000, Month.jan, 1)); - // leap year - assertNotThrown(enforceValid!"days"(2000, Month.feb, 29)); - - assertThrown!DateTimeException(enforceValid!"days"(2001, Month.feb, 29)); - assertThrown!DateTimeException(enforceValid!"days"(2000, Month.jan, 32)); - assertThrown!DateTimeException(enforceValid!"days"(2000, Month.apr, 31)); -} - - -/++ - Returns the number of days from the current day of the week to the given - day of the week. If they are the same, then the result is 0. - - Params: - currDoW = The current day of the week. - dow = The day of the week to get the number of days to. - +/ -int daysToDayOfWeek(DayOfWeek currDoW, DayOfWeek dow) @safe pure nothrow @nogc -{ - if (currDoW == dow) - return 0; - if (currDoW < dow) - return dow - currDoW; - return DayOfWeek.sat - currDoW + dow + 1; -} - -/// -@safe pure nothrow @nogc unittest -{ - assert(daysToDayOfWeek(DayOfWeek.mon, DayOfWeek.mon) == 0); - assert(daysToDayOfWeek(DayOfWeek.mon, DayOfWeek.sun) == 6); - assert(daysToDayOfWeek(DayOfWeek.mon, DayOfWeek.wed) == 2); -} - -@safe unittest -{ - assert(daysToDayOfWeek(DayOfWeek.sun, DayOfWeek.sun) == 0); - assert(daysToDayOfWeek(DayOfWeek.sun, DayOfWeek.mon) == 1); - assert(daysToDayOfWeek(DayOfWeek.sun, DayOfWeek.tue) == 2); - assert(daysToDayOfWeek(DayOfWeek.sun, DayOfWeek.wed) == 3); - assert(daysToDayOfWeek(DayOfWeek.sun, DayOfWeek.thu) == 4); - assert(daysToDayOfWeek(DayOfWeek.sun, DayOfWeek.fri) == 5); - assert(daysToDayOfWeek(DayOfWeek.sun, DayOfWeek.sat) == 6); - - assert(daysToDayOfWeek(DayOfWeek.mon, DayOfWeek.sun) == 6); - assert(daysToDayOfWeek(DayOfWeek.mon, DayOfWeek.mon) == 0); - assert(daysToDayOfWeek(DayOfWeek.mon, DayOfWeek.tue) == 1); - assert(daysToDayOfWeek(DayOfWeek.mon, DayOfWeek.wed) == 2); - assert(daysToDayOfWeek(DayOfWeek.mon, DayOfWeek.thu) == 3); - assert(daysToDayOfWeek(DayOfWeek.mon, DayOfWeek.fri) == 4); - assert(daysToDayOfWeek(DayOfWeek.mon, DayOfWeek.sat) == 5); - - assert(daysToDayOfWeek(DayOfWeek.tue, DayOfWeek.sun) == 5); - assert(daysToDayOfWeek(DayOfWeek.tue, DayOfWeek.mon) == 6); - assert(daysToDayOfWeek(DayOfWeek.tue, DayOfWeek.tue) == 0); - assert(daysToDayOfWeek(DayOfWeek.tue, DayOfWeek.wed) == 1); - assert(daysToDayOfWeek(DayOfWeek.tue, DayOfWeek.thu) == 2); - assert(daysToDayOfWeek(DayOfWeek.tue, DayOfWeek.fri) == 3); - assert(daysToDayOfWeek(DayOfWeek.tue, DayOfWeek.sat) == 4); - - assert(daysToDayOfWeek(DayOfWeek.wed, DayOfWeek.sun) == 4); - assert(daysToDayOfWeek(DayOfWeek.wed, DayOfWeek.mon) == 5); - assert(daysToDayOfWeek(DayOfWeek.wed, DayOfWeek.tue) == 6); - assert(daysToDayOfWeek(DayOfWeek.wed, DayOfWeek.wed) == 0); - assert(daysToDayOfWeek(DayOfWeek.wed, DayOfWeek.thu) == 1); - assert(daysToDayOfWeek(DayOfWeek.wed, DayOfWeek.fri) == 2); - assert(daysToDayOfWeek(DayOfWeek.wed, DayOfWeek.sat) == 3); - - assert(daysToDayOfWeek(DayOfWeek.thu, DayOfWeek.sun) == 3); - assert(daysToDayOfWeek(DayOfWeek.thu, DayOfWeek.mon) == 4); - assert(daysToDayOfWeek(DayOfWeek.thu, DayOfWeek.tue) == 5); - assert(daysToDayOfWeek(DayOfWeek.thu, DayOfWeek.wed) == 6); - assert(daysToDayOfWeek(DayOfWeek.thu, DayOfWeek.thu) == 0); - assert(daysToDayOfWeek(DayOfWeek.thu, DayOfWeek.fri) == 1); - assert(daysToDayOfWeek(DayOfWeek.thu, DayOfWeek.sat) == 2); - - assert(daysToDayOfWeek(DayOfWeek.fri, DayOfWeek.sun) == 2); - assert(daysToDayOfWeek(DayOfWeek.fri, DayOfWeek.mon) == 3); - assert(daysToDayOfWeek(DayOfWeek.fri, DayOfWeek.tue) == 4); - assert(daysToDayOfWeek(DayOfWeek.fri, DayOfWeek.wed) == 5); - assert(daysToDayOfWeek(DayOfWeek.fri, DayOfWeek.thu) == 6); - assert(daysToDayOfWeek(DayOfWeek.fri, DayOfWeek.fri) == 0); - assert(daysToDayOfWeek(DayOfWeek.fri, DayOfWeek.sat) == 1); - - assert(daysToDayOfWeek(DayOfWeek.sat, DayOfWeek.sun) == 1); - assert(daysToDayOfWeek(DayOfWeek.sat, DayOfWeek.mon) == 2); - assert(daysToDayOfWeek(DayOfWeek.sat, DayOfWeek.tue) == 3); - assert(daysToDayOfWeek(DayOfWeek.sat, DayOfWeek.wed) == 4); - assert(daysToDayOfWeek(DayOfWeek.sat, DayOfWeek.thu) == 5); - assert(daysToDayOfWeek(DayOfWeek.sat, DayOfWeek.fri) == 6); - assert(daysToDayOfWeek(DayOfWeek.sat, DayOfWeek.sat) == 0); -} - - -/++ - Returns the number of months from the current months of the year to the - given month of the year. If they are the same, then the result is 0. - - Params: - currMonth = The current month of the year. - month = The month of the year to get the number of months to. - +/ -int monthsToMonth(int currMonth, int month) @safe pure -{ - enforceValid!"months"(currMonth); - enforceValid!"months"(month); - - if (currMonth == month) - return 0; - if (currMonth < month) - return month - currMonth; - return Month.dec - currMonth + month; -} - -/// -@safe pure unittest -{ - assert(monthsToMonth(Month.jan, Month.jan) == 0); - assert(monthsToMonth(Month.jan, Month.dec) == 11); - assert(monthsToMonth(Month.jul, Month.oct) == 3); -} - -@safe unittest -{ - assert(monthsToMonth(Month.jan, Month.jan) == 0); - assert(monthsToMonth(Month.jan, Month.feb) == 1); - assert(monthsToMonth(Month.jan, Month.mar) == 2); - assert(monthsToMonth(Month.jan, Month.apr) == 3); - assert(monthsToMonth(Month.jan, Month.may) == 4); - assert(monthsToMonth(Month.jan, Month.jun) == 5); - assert(monthsToMonth(Month.jan, Month.jul) == 6); - assert(monthsToMonth(Month.jan, Month.aug) == 7); - assert(monthsToMonth(Month.jan, Month.sep) == 8); - assert(monthsToMonth(Month.jan, Month.oct) == 9); - assert(monthsToMonth(Month.jan, Month.nov) == 10); - assert(monthsToMonth(Month.jan, Month.dec) == 11); - - assert(monthsToMonth(Month.may, Month.jan) == 8); - assert(monthsToMonth(Month.may, Month.feb) == 9); - assert(monthsToMonth(Month.may, Month.mar) == 10); - assert(monthsToMonth(Month.may, Month.apr) == 11); - assert(monthsToMonth(Month.may, Month.may) == 0); - assert(monthsToMonth(Month.may, Month.jun) == 1); - assert(monthsToMonth(Month.may, Month.jul) == 2); - assert(monthsToMonth(Month.may, Month.aug) == 3); - assert(monthsToMonth(Month.may, Month.sep) == 4); - assert(monthsToMonth(Month.may, Month.oct) == 5); - assert(monthsToMonth(Month.may, Month.nov) == 6); - assert(monthsToMonth(Month.may, Month.dec) == 7); - - assert(monthsToMonth(Month.oct, Month.jan) == 3); - assert(monthsToMonth(Month.oct, Month.feb) == 4); - assert(monthsToMonth(Month.oct, Month.mar) == 5); - assert(monthsToMonth(Month.oct, Month.apr) == 6); - assert(monthsToMonth(Month.oct, Month.may) == 7); - assert(monthsToMonth(Month.oct, Month.jun) == 8); - assert(monthsToMonth(Month.oct, Month.jul) == 9); - assert(monthsToMonth(Month.oct, Month.aug) == 10); - assert(monthsToMonth(Month.oct, Month.sep) == 11); - assert(monthsToMonth(Month.oct, Month.oct) == 0); - assert(monthsToMonth(Month.oct, Month.nov) == 1); - assert(monthsToMonth(Month.oct, Month.dec) == 2); - - assert(monthsToMonth(Month.dec, Month.jan) == 1); - assert(monthsToMonth(Month.dec, Month.feb) == 2); - assert(monthsToMonth(Month.dec, Month.mar) == 3); - assert(monthsToMonth(Month.dec, Month.apr) == 4); - assert(monthsToMonth(Month.dec, Month.may) == 5); - assert(monthsToMonth(Month.dec, Month.jun) == 6); - assert(monthsToMonth(Month.dec, Month.jul) == 7); - assert(monthsToMonth(Month.dec, Month.aug) == 8); - assert(monthsToMonth(Month.dec, Month.sep) == 9); - assert(monthsToMonth(Month.dec, Month.oct) == 10); - assert(monthsToMonth(Month.dec, Month.nov) == 11); - assert(monthsToMonth(Month.dec, Month.dec) == 0); -} - - -/++ - Whether the given Gregorian Year is a leap year. - - Params: - year = The year to to be tested. - +/ -bool yearIsLeapYear(int year) @safe pure nothrow @nogc -{ - if (year % 400 == 0) - return true; - if (year % 100 == 0) - return false; - return year % 4 == 0; -} - -/// -@safe unittest -{ - foreach (year; [1, 2, 100, 2001, 2002, 2003, 2005, 2006, 2007, 2009, 2010]) - { - assert(!yearIsLeapYear(year)); - assert(!yearIsLeapYear(-year)); - } - - foreach (year; [0, 4, 8, 400, 800, 1600, 1996, 2000, 2004, 2008, 2012]) - { - assert(yearIsLeapYear(year)); - assert(yearIsLeapYear(-year)); - } -} - -@safe unittest -{ - import std.format : format; - foreach (year; [1, 2, 3, 5, 6, 7, 100, 200, 300, 500, 600, 700, 1998, 1999, - 2001, 2002, 2003, 2005, 2006, 2007, 2009, 2010, 2011]) - { - assert(!yearIsLeapYear(year), format("year: %s.", year)); - assert(!yearIsLeapYear(-year), format("year: %s.", year)); - } - - foreach (year; [0, 4, 8, 400, 800, 1600, 1996, 2000, 2004, 2008, 2012]) - { - assert(yearIsLeapYear(year), format("year: %s.", year)); - assert(yearIsLeapYear(-year), format("year: %s.", year)); - } -} - - -/++ - Whether the given type defines all of the necessary functions for it to - function as a time point. - - 1. `T` must define a static property named `min` which is the smallest - value of `T` as `Unqual!T`. - - 2. `T` must define a static property named `max` which is the largest - value of `T` as `Unqual!T`. - - 3. `T` must define an `opBinary` for addition and subtraction that - accepts $(REF Duration, core,time) and returns `Unqual!T`. - - 4. `T` must define an `opOpAssign` for addition and subtraction that - accepts $(REF Duration, core,time) and returns $(D ref Unqual!T). - - 5. `T` must define a `opBinary` for subtraction which accepts `T` - and returns $(REF Duration, core,time). - +/ -template isTimePoint(T) -{ - import core.time : Duration; - import std.traits : FunctionAttribute, functionAttributes, Unqual; - - enum isTimePoint = hasMin && - hasMax && - hasOverloadedOpBinaryWithDuration && - hasOverloadedOpAssignWithDuration && - hasOverloadedOpBinaryWithSelf && - !is(U == Duration); - -private: - - alias U = Unqual!T; - - enum hasMin = __traits(hasMember, T, "min") && - is(typeof(T.min) == U) && - is(typeof({static assert(__traits(isStaticFunction, T.min));})); - - enum hasMax = __traits(hasMember, T, "max") && - is(typeof(T.max) == U) && - is(typeof({static assert(__traits(isStaticFunction, T.max));})); - - enum hasOverloadedOpBinaryWithDuration = is(typeof(T.init + Duration.init) == U) && - is(typeof(T.init - Duration.init) == U); - - enum hasOverloadedOpAssignWithDuration = is(typeof(U.init += Duration.init) == U) && - is(typeof(U.init -= Duration.init) == U) && - is(typeof( - { - alias add = U.opOpAssign!"+"; - alias sub = U.opOpAssign!"-"; - alias FA = FunctionAttribute; - static assert((functionAttributes!add & FA.ref_) != 0); - static assert((functionAttributes!sub & FA.ref_) != 0); - })); - - enum hasOverloadedOpBinaryWithSelf = is(typeof(T.init - T.init) == Duration); -} - -/// -@safe unittest -{ - import core.time : Duration; - import std.datetime.interval : Interval; - import std.datetime.systime : SysTime; - - static assert(isTimePoint!Date); - static assert(isTimePoint!DateTime); - static assert(isTimePoint!SysTime); - static assert(isTimePoint!TimeOfDay); - - static assert(!isTimePoint!int); - static assert(!isTimePoint!Duration); - static assert(!isTimePoint!(Interval!SysTime)); -} - -@safe unittest -{ - import core.time; - import std.datetime.interval; - import std.datetime.systime; - import std.meta : AliasSeq; - - static foreach (TP; AliasSeq!(Date, DateTime, SysTime, TimeOfDay)) - { - static assert(isTimePoint!(const TP), TP.stringof); - static assert(isTimePoint!(immutable TP), TP.stringof); - } - - static foreach (T; AliasSeq!(float, string, Duration, Interval!Date, PosInfInterval!Date, NegInfInterval!Date)) - static assert(!isTimePoint!T, T.stringof); -} - - -/++ - Whether all of the given strings are valid units of time. - - `"nsecs"` is not considered a valid unit of time. Nothing in std.datetime - can handle precision greater than hnsecs, and the few functions in core.time - which deal with "nsecs" deal with it explicitly. - +/ -bool validTimeUnits(string[] units...) @safe pure nothrow @nogc -{ - import std.algorithm.searching : canFind; - foreach (str; units) - { - if (!canFind(timeStrings[], str)) - return false; - } - return true; -} - -/// -@safe @nogc nothrow unittest -{ - assert(validTimeUnits("msecs", "seconds", "minutes")); - assert(validTimeUnits("days", "weeks", "months")); - assert(!validTimeUnits("ms", "seconds", "minutes")); -} - - -/++ - Compares two time unit strings. `"years"` are the largest units and - `"hnsecs"` are the smallest. - - Returns: - $(BOOKTABLE, - $(TR $(TD this < rhs) $(TD < 0)) - $(TR $(TD this == rhs) $(TD 0)) - $(TR $(TD this > rhs) $(TD > 0)) - ) - - Throws: - $(LREF DateTimeException) if either of the given strings is not a valid - time unit string. - +/ -int cmpTimeUnits(string lhs, string rhs) @safe pure -{ - import std.algorithm.searching : countUntil; - import std.exception : enforce; - import std.format : format; - - immutable indexOfLHS = countUntil(timeStrings, lhs); - immutable indexOfRHS = countUntil(timeStrings, rhs); - - enforce!DateTimeException(indexOfLHS != -1, format("%s is not a valid TimeString", lhs)); - enforce!DateTimeException(indexOfRHS != -1, format("%s is not a valid TimeString", rhs)); - - if (indexOfLHS < indexOfRHS) - return -1; - if (indexOfLHS > indexOfRHS) - return 1; - - return 0; -} - -/// -@safe pure unittest -{ - import std.exception : assertThrown; - - assert(cmpTimeUnits("hours", "hours") == 0); - assert(cmpTimeUnits("hours", "weeks") < 0); - assert(cmpTimeUnits("months", "seconds") > 0); - - assertThrown!DateTimeException(cmpTimeUnits("month", "second")); -} - -@safe unittest -{ - foreach (i, outerUnits; timeStrings) - { - assert(cmpTimeUnits(outerUnits, outerUnits) == 0); - - // For some reason, $ won't compile. - foreach (innerUnits; timeStrings[i + 1 .. timeStrings.length]) - assert(cmpTimeUnits(outerUnits, innerUnits) == -1); - } - - foreach (i, outerUnits; timeStrings) - { - foreach (innerUnits; timeStrings[0 .. i]) - assert(cmpTimeUnits(outerUnits, innerUnits) == 1); - } -} - - -/++ - Compares two time unit strings at compile time. `"years"` are the largest - units and `"hnsecs"` are the smallest. - - This template is used instead of `cmpTimeUnits` because exceptions - can't be thrown at compile time and `cmpTimeUnits` must enforce that - the strings it's given are valid time unit strings. This template uses a - template constraint instead. - - Returns: - $(BOOKTABLE, - $(TR $(TD this < rhs) $(TD < 0)) - $(TR $(TD this == rhs) $(TD 0)) - $(TR $(TD this > rhs) $(TD > 0)) - ) - +/ -template CmpTimeUnits(string lhs, string rhs) -if (validTimeUnits(lhs, rhs)) -{ - enum CmpTimeUnits = cmpTimeUnitsCTFE(lhs, rhs); -} - -/// -@safe pure unittest -{ - static assert(CmpTimeUnits!("years", "weeks") > 0); - static assert(CmpTimeUnits!("days", "days") == 0); - static assert(CmpTimeUnits!("seconds", "hours") < 0); -} - -// Helper function for CmpTimeUnits. -private int cmpTimeUnitsCTFE(string lhs, string rhs) @safe pure nothrow @nogc -{ - import std.algorithm.searching : countUntil; - auto tstrings = timeStrings; - immutable indexOfLHS = countUntil(tstrings, lhs); - immutable indexOfRHS = countUntil(tstrings, rhs); - - if (indexOfLHS < indexOfRHS) - return -1; - if (indexOfLHS > indexOfRHS) - return 1; - - return 0; -} - -@safe unittest -{ - static foreach (i; 0 .. timeStrings.length) - { - static assert(CmpTimeUnits!(timeStrings[i], timeStrings[i]) == 0); - - static foreach (next; timeStrings[i + 1 .. $]) - static assert(CmpTimeUnits!(timeStrings[i], next) == -1); - - static foreach (prev; timeStrings[0 .. i]) - static assert(CmpTimeUnits!(timeStrings[i], prev) == 1); - } -} - - -package: - - -/+ - Array of the short (three letter) names of each month. - +/ -immutable string[12] _monthNames = ["Jan", - "Feb", - "Mar", - "Apr", - "May", - "Jun", - "Jul", - "Aug", - "Sep", - "Oct", - "Nov", - "Dec"]; - -/+ - The maximum valid Day in the given month in the given year. - - Params: - year = The year to get the day for. - month = The month of the Gregorian Calendar to get the day for. - +/ -ubyte maxDay(int year, int month) @safe pure nothrow @nogc -in -{ - assert(valid!"months"(month)); -} -do -{ - switch (month) - { - case Month.jan, Month.mar, Month.may, Month.jul, Month.aug, Month.oct, Month.dec: - return 31; - case Month.feb: - return yearIsLeapYear(year) ? 29 : 28; - case Month.apr, Month.jun, Month.sep, Month.nov: - return 30; - default: - assert(0, "Invalid month."); - } -} - -@safe unittest -{ - // Test A.D. - assert(maxDay(1999, 1) == 31); - assert(maxDay(1999, 2) == 28); - assert(maxDay(1999, 3) == 31); - assert(maxDay(1999, 4) == 30); - assert(maxDay(1999, 5) == 31); - assert(maxDay(1999, 6) == 30); - assert(maxDay(1999, 7) == 31); - assert(maxDay(1999, 8) == 31); - assert(maxDay(1999, 9) == 30); - assert(maxDay(1999, 10) == 31); - assert(maxDay(1999, 11) == 30); - assert(maxDay(1999, 12) == 31); - - assert(maxDay(2000, 1) == 31); - assert(maxDay(2000, 2) == 29); - assert(maxDay(2000, 3) == 31); - assert(maxDay(2000, 4) == 30); - assert(maxDay(2000, 5) == 31); - assert(maxDay(2000, 6) == 30); - assert(maxDay(2000, 7) == 31); - assert(maxDay(2000, 8) == 31); - assert(maxDay(2000, 9) == 30); - assert(maxDay(2000, 10) == 31); - assert(maxDay(2000, 11) == 30); - assert(maxDay(2000, 12) == 31); - - // Test B.C. - assert(maxDay(-1999, 1) == 31); - assert(maxDay(-1999, 2) == 28); - assert(maxDay(-1999, 3) == 31); - assert(maxDay(-1999, 4) == 30); - assert(maxDay(-1999, 5) == 31); - assert(maxDay(-1999, 6) == 30); - assert(maxDay(-1999, 7) == 31); - assert(maxDay(-1999, 8) == 31); - assert(maxDay(-1999, 9) == 30); - assert(maxDay(-1999, 10) == 31); - assert(maxDay(-1999, 11) == 30); - assert(maxDay(-1999, 12) == 31); - - assert(maxDay(-2000, 1) == 31); - assert(maxDay(-2000, 2) == 29); - assert(maxDay(-2000, 3) == 31); - assert(maxDay(-2000, 4) == 30); - assert(maxDay(-2000, 5) == 31); - assert(maxDay(-2000, 6) == 30); - assert(maxDay(-2000, 7) == 31); - assert(maxDay(-2000, 8) == 31); - assert(maxDay(-2000, 9) == 30); - assert(maxDay(-2000, 10) == 31); - assert(maxDay(-2000, 11) == 30); - assert(maxDay(-2000, 12) == 31); -} - -/+ - Splits out a particular unit from hnsecs and gives the value for that - unit and the remaining hnsecs. It really shouldn't be used unless unless - all units larger than the given units have already been split out. - - Params: - units = The units to split out. - hnsecs = The current total hnsecs. Upon returning, it is the hnsecs left - after splitting out the given units. - - Returns: - The number of the given units from converting hnsecs to those units. - +/ -long splitUnitsFromHNSecs(string units)(ref long hnsecs) @safe pure nothrow @nogc -if (validTimeUnits(units) && CmpTimeUnits!(units, "months") < 0) -{ - import core.time : convert; - immutable value = convert!("hnsecs", units)(hnsecs); - hnsecs -= convert!(units, "hnsecs")(value); - return value; -} - -@safe unittest -{ - auto hnsecs = 2595000000007L; - immutable days = splitUnitsFromHNSecs!"days"(hnsecs); - assert(days == 3); - assert(hnsecs == 3000000007); - - immutable minutes = splitUnitsFromHNSecs!"minutes"(hnsecs); - assert(minutes == 5); - assert(hnsecs == 7); -} - - -/+ - Returns the day of the week for the given day of the Gregorian Calendar. - - Params: - day = The day of the Gregorian Calendar for which to get the day of - the week. - +/ -DayOfWeek getDayOfWeek(int day) @safe pure nothrow @nogc -{ - // January 1st, 1 A.D. was a Monday - if (day >= 0) - return cast(DayOfWeek)(day % 7); - else - { - immutable dow = cast(DayOfWeek)((day % 7) + 7); - - if (dow == 7) - return DayOfWeek.sun; - else - return dow; - } -} - -@safe unittest -{ - import std.datetime.systime : SysTime; - - // Test A.D. - assert(getDayOfWeek(SysTime(Date(1, 1, 1)).dayOfGregorianCal) == DayOfWeek.mon); - assert(getDayOfWeek(SysTime(Date(1, 1, 2)).dayOfGregorianCal) == DayOfWeek.tue); - assert(getDayOfWeek(SysTime(Date(1, 1, 3)).dayOfGregorianCal) == DayOfWeek.wed); - assert(getDayOfWeek(SysTime(Date(1, 1, 4)).dayOfGregorianCal) == DayOfWeek.thu); - assert(getDayOfWeek(SysTime(Date(1, 1, 5)).dayOfGregorianCal) == DayOfWeek.fri); - assert(getDayOfWeek(SysTime(Date(1, 1, 6)).dayOfGregorianCal) == DayOfWeek.sat); - assert(getDayOfWeek(SysTime(Date(1, 1, 7)).dayOfGregorianCal) == DayOfWeek.sun); - assert(getDayOfWeek(SysTime(Date(1, 1, 8)).dayOfGregorianCal) == DayOfWeek.mon); - assert(getDayOfWeek(SysTime(Date(1, 1, 9)).dayOfGregorianCal) == DayOfWeek.tue); - assert(getDayOfWeek(SysTime(Date(2, 1, 1)).dayOfGregorianCal) == DayOfWeek.tue); - assert(getDayOfWeek(SysTime(Date(3, 1, 1)).dayOfGregorianCal) == DayOfWeek.wed); - assert(getDayOfWeek(SysTime(Date(4, 1, 1)).dayOfGregorianCal) == DayOfWeek.thu); - assert(getDayOfWeek(SysTime(Date(5, 1, 1)).dayOfGregorianCal) == DayOfWeek.sat); - assert(getDayOfWeek(SysTime(Date(2000, 1, 1)).dayOfGregorianCal) == DayOfWeek.sat); - assert(getDayOfWeek(SysTime(Date(2010, 8, 22)).dayOfGregorianCal) == DayOfWeek.sun); - assert(getDayOfWeek(SysTime(Date(2010, 8, 23)).dayOfGregorianCal) == DayOfWeek.mon); - assert(getDayOfWeek(SysTime(Date(2010, 8, 24)).dayOfGregorianCal) == DayOfWeek.tue); - assert(getDayOfWeek(SysTime(Date(2010, 8, 25)).dayOfGregorianCal) == DayOfWeek.wed); - assert(getDayOfWeek(SysTime(Date(2010, 8, 26)).dayOfGregorianCal) == DayOfWeek.thu); - assert(getDayOfWeek(SysTime(Date(2010, 8, 27)).dayOfGregorianCal) == DayOfWeek.fri); - assert(getDayOfWeek(SysTime(Date(2010, 8, 28)).dayOfGregorianCal) == DayOfWeek.sat); - assert(getDayOfWeek(SysTime(Date(2010, 8, 29)).dayOfGregorianCal) == DayOfWeek.sun); - - // Test B.C. - assert(getDayOfWeek(SysTime(Date(0, 12, 31)).dayOfGregorianCal) == DayOfWeek.sun); - assert(getDayOfWeek(SysTime(Date(0, 12, 30)).dayOfGregorianCal) == DayOfWeek.sat); - assert(getDayOfWeek(SysTime(Date(0, 12, 29)).dayOfGregorianCal) == DayOfWeek.fri); - assert(getDayOfWeek(SysTime(Date(0, 12, 28)).dayOfGregorianCal) == DayOfWeek.thu); - assert(getDayOfWeek(SysTime(Date(0, 12, 27)).dayOfGregorianCal) == DayOfWeek.wed); - assert(getDayOfWeek(SysTime(Date(0, 12, 26)).dayOfGregorianCal) == DayOfWeek.tue); - assert(getDayOfWeek(SysTime(Date(0, 12, 25)).dayOfGregorianCal) == DayOfWeek.mon); - assert(getDayOfWeek(SysTime(Date(0, 12, 24)).dayOfGregorianCal) == DayOfWeek.sun); - assert(getDayOfWeek(SysTime(Date(0, 12, 23)).dayOfGregorianCal) == DayOfWeek.sat); -} - - -private: - -enum daysInYear = 365; // The number of days in a non-leap year. -enum daysInLeapYear = 366; // The numbef or days in a leap year. -enum daysIn4Years = daysInYear * 3 + daysInLeapYear; // Number of days in 4 years. -enum daysIn100Years = daysIn4Years * 25 - 1; // The number of days in 100 years. -enum daysIn400Years = daysIn100Years * 4 + 1; // The number of days in 400 years. - -/+ - Array of integers representing the last days of each month in a year. - +/ -immutable int[13] lastDayNonLeap = [0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365]; - -/+ - Array of integers representing the last days of each month in a leap year. - +/ -immutable int[13] lastDayLeap = [0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335, 366]; - - -/+ - Returns the string representation of the given month. - +/ -string monthToString(Month month) @safe pure -{ - import std.format : format; - assert(month >= Month.jan && month <= Month.dec, format("Invalid month: %s", month)); - return _monthNames[month - Month.jan]; -} - -@safe unittest -{ - assert(monthToString(Month.jan) == "Jan"); - assert(monthToString(Month.feb) == "Feb"); - assert(monthToString(Month.mar) == "Mar"); - assert(monthToString(Month.apr) == "Apr"); - assert(monthToString(Month.may) == "May"); - assert(monthToString(Month.jun) == "Jun"); - assert(monthToString(Month.jul) == "Jul"); - assert(monthToString(Month.aug) == "Aug"); - assert(monthToString(Month.sep) == "Sep"); - assert(monthToString(Month.oct) == "Oct"); - assert(monthToString(Month.nov) == "Nov"); - assert(monthToString(Month.dec) == "Dec"); -} - - -/+ - Returns the Month corresponding to the given string. - - Params: - monthStr = The string representation of the month to get the Month for. - - Throws: - $(REF DateTimeException,std,datetime,date) if the given month is not a - valid month string. - +/ -Month monthFromString(T)(T monthStr) @safe pure -if (isSomeString!T) -{ - import std.format : format; - switch (monthStr) - { - case "Jan": - return Month.jan; - case "Feb": - return Month.feb; - case "Mar": - return Month.mar; - case "Apr": - return Month.apr; - case "May": - return Month.may; - case "Jun": - return Month.jun; - case "Jul": - return Month.jul; - case "Aug": - return Month.aug; - case "Sep": - return Month.sep; - case "Oct": - return Month.oct; - case "Nov": - return Month.nov; - case "Dec": - return Month.dec; - default: - throw new DateTimeException(format!"Invalid month %s"(monthStr)); - } -} - -@safe unittest -{ - import std.conv : to; - import std.traits : EnumMembers; - foreach (badStr; ["Ja", "Janu", "Januar", "Januarys", "JJanuary", "JANUARY", - "JAN", "january", "jaNuary", "jaN", "jaNuaRy", "jAn"]) - { - assertThrown!DateTimeException(monthFromString(badStr), badStr); - } - - foreach (month; EnumMembers!Month) - { - assert(monthFromString(monthToString(month)) == month, month.to!string); - } -} - - -// NOTE: all the non-simple array literals are wrapped in functions, because -// otherwise importing causes re-evaluation of the static initializers using -// CTFE with unittests enabled -version (StdUnittest) -{ -private @safe: - // All of these helper arrays are sorted in ascending order. - auto testYearsBC = [-1999, -1200, -600, -4, -1, 0]; - auto testYearsAD = [1, 4, 1000, 1999, 2000, 2012]; - - // I'd use a Tuple, but I get forward reference errors if I try. - struct MonthDay - { - Month month; - short day; - - this(int m, short d) - { - month = cast(Month) m; - day = d; - } - } - - MonthDay[] testMonthDays() - { - static MonthDay[] result = [MonthDay(1, 1), - MonthDay(1, 2), - MonthDay(3, 17), - MonthDay(7, 4), - MonthDay(10, 27), - MonthDay(12, 30), - MonthDay(12, 31)]; - return result; - } - - auto testDays = [1, 2, 9, 10, 16, 20, 25, 28, 29, 30, 31]; - - TimeOfDay[] testTODs() - { - static result = [TimeOfDay(0, 0, 0), - TimeOfDay(0, 0, 1), - TimeOfDay(0, 1, 0), - TimeOfDay(1, 0, 0), - TimeOfDay(13, 13, 13), - TimeOfDay(23, 59, 59)]; - return result; - } - - auto testHours = [0, 1, 12, 22, 23]; - auto testMinSecs = [0, 1, 30, 58, 59]; - - // Throwing exceptions is incredibly expensive, so we want to use a smaller - // set of values for tests using assertThrown. - TimeOfDay[] testTODsThrown() - { - static result = [TimeOfDay(0, 0, 0), - TimeOfDay(13, 13, 13), - TimeOfDay(23, 59, 59)]; - return result; - } - - Date[] testDatesBC; - Date[] testDatesAD; - - DateTime[] testDateTimesBC; - DateTime[] testDateTimesAD; - - // I'd use a Tuple, but I get forward reference errors if I try. - struct GregDay { int day; Date date; } - GregDay[] testGregDaysBC() - { - static result = [GregDay(-1_373_427, Date(-3760, 9, 7)), // Start of the Hebrew Calendar - GregDay(-735_233, Date(-2012, 1, 1)), - GregDay(-735_202, Date(-2012, 2, 1)), - GregDay(-735_175, Date(-2012, 2, 28)), - GregDay(-735_174, Date(-2012, 2, 29)), - GregDay(-735_173, Date(-2012, 3, 1)), - GregDay(-734_502, Date(-2010, 1, 1)), - GregDay(-734_472, Date(-2010, 1, 31)), - GregDay(-734_471, Date(-2010, 2, 1)), - GregDay(-734_444, Date(-2010, 2, 28)), - GregDay(-734_443, Date(-2010, 3, 1)), - GregDay(-734_413, Date(-2010, 3, 31)), - GregDay(-734_412, Date(-2010, 4, 1)), - GregDay(-734_383, Date(-2010, 4, 30)), - GregDay(-734_382, Date(-2010, 5, 1)), - GregDay(-734_352, Date(-2010, 5, 31)), - GregDay(-734_351, Date(-2010, 6, 1)), - GregDay(-734_322, Date(-2010, 6, 30)), - GregDay(-734_321, Date(-2010, 7, 1)), - GregDay(-734_291, Date(-2010, 7, 31)), - GregDay(-734_290, Date(-2010, 8, 1)), - GregDay(-734_260, Date(-2010, 8, 31)), - GregDay(-734_259, Date(-2010, 9, 1)), - GregDay(-734_230, Date(-2010, 9, 30)), - GregDay(-734_229, Date(-2010, 10, 1)), - GregDay(-734_199, Date(-2010, 10, 31)), - GregDay(-734_198, Date(-2010, 11, 1)), - GregDay(-734_169, Date(-2010, 11, 30)), - GregDay(-734_168, Date(-2010, 12, 1)), - GregDay(-734_139, Date(-2010, 12, 30)), - GregDay(-734_138, Date(-2010, 12, 31)), - GregDay(-731_215, Date(-2001, 1, 1)), - GregDay(-730_850, Date(-2000, 1, 1)), - GregDay(-730_849, Date(-2000, 1, 2)), - GregDay(-730_486, Date(-2000, 12, 30)), - GregDay(-730_485, Date(-2000, 12, 31)), - GregDay(-730_484, Date(-1999, 1, 1)), - GregDay(-694_690, Date(-1901, 1, 1)), - GregDay(-694_325, Date(-1900, 1, 1)), - GregDay(-585_118, Date(-1601, 1, 1)), - GregDay(-584_753, Date(-1600, 1, 1)), - GregDay(-584_388, Date(-1600, 12, 31)), - GregDay(-584_387, Date(-1599, 1, 1)), - GregDay(-365_972, Date(-1001, 1, 1)), - GregDay(-365_607, Date(-1000, 1, 1)), - GregDay(-183_351, Date(-501, 1, 1)), - GregDay(-182_986, Date(-500, 1, 1)), - GregDay(-182_621, Date(-499, 1, 1)), - GregDay(-146_827, Date(-401, 1, 1)), - GregDay(-146_462, Date(-400, 1, 1)), - GregDay(-146_097, Date(-400, 12, 31)), - GregDay(-110_302, Date(-301, 1, 1)), - GregDay(-109_937, Date(-300, 1, 1)), - GregDay(-73_778, Date(-201, 1, 1)), - GregDay(-73_413, Date(-200, 1, 1)), - GregDay(-38_715, Date(-105, 1, 1)), - GregDay(-37_254, Date(-101, 1, 1)), - GregDay(-36_889, Date(-100, 1, 1)), - GregDay(-36_524, Date(-99, 1, 1)), - GregDay(-36_160, Date(-99, 12, 31)), - GregDay(-35_794, Date(-97, 1, 1)), - GregDay(-18_627, Date(-50, 1, 1)), - GregDay(-18_262, Date(-49, 1, 1)), - GregDay(-3652, Date(-9, 1, 1)), - GregDay(-2191, Date(-5, 1, 1)), - GregDay(-1827, Date(-5, 12, 31)), - GregDay(-1826, Date(-4, 1, 1)), - GregDay(-1825, Date(-4, 1, 2)), - GregDay(-1462, Date(-4, 12, 30)), - GregDay(-1461, Date(-4, 12, 31)), - GregDay(-1460, Date(-3, 1, 1)), - GregDay(-1096, Date(-3, 12, 31)), - GregDay(-1095, Date(-2, 1, 1)), - GregDay(-731, Date(-2, 12, 31)), - GregDay(-730, Date(-1, 1, 1)), - GregDay(-367, Date(-1, 12, 30)), - GregDay(-366, Date(-1, 12, 31)), - GregDay(-365, Date(0, 1, 1)), - GregDay(-31, Date(0, 11, 30)), - GregDay(-30, Date(0, 12, 1)), - GregDay(-1, Date(0, 12, 30)), - GregDay(0, Date(0, 12, 31))]; - return result; - } - - GregDay[] testGregDaysAD() - { - static result = [GregDay(1, Date(1, 1, 1)), - GregDay(2, Date(1, 1, 2)), - GregDay(32, Date(1, 2, 1)), - GregDay(365, Date(1, 12, 31)), - GregDay(366, Date(2, 1, 1)), - GregDay(731, Date(3, 1, 1)), - GregDay(1096, Date(4, 1, 1)), - GregDay(1097, Date(4, 1, 2)), - GregDay(1460, Date(4, 12, 30)), - GregDay(1461, Date(4, 12, 31)), - GregDay(1462, Date(5, 1, 1)), - GregDay(17_898, Date(50, 1, 1)), - GregDay(35_065, Date(97, 1, 1)), - GregDay(36_160, Date(100, 1, 1)), - GregDay(36_525, Date(101, 1, 1)), - GregDay(37_986, Date(105, 1, 1)), - GregDay(72_684, Date(200, 1, 1)), - GregDay(73_049, Date(201, 1, 1)), - GregDay(109_208, Date(300, 1, 1)), - GregDay(109_573, Date(301, 1, 1)), - GregDay(145_732, Date(400, 1, 1)), - GregDay(146_098, Date(401, 1, 1)), - GregDay(182_257, Date(500, 1, 1)), - GregDay(182_622, Date(501, 1, 1)), - GregDay(364_878, Date(1000, 1, 1)), - GregDay(365_243, Date(1001, 1, 1)), - GregDay(584_023, Date(1600, 1, 1)), - GregDay(584_389, Date(1601, 1, 1)), - GregDay(693_596, Date(1900, 1, 1)), - GregDay(693_961, Date(1901, 1, 1)), - GregDay(729_755, Date(1999, 1, 1)), - GregDay(730_120, Date(2000, 1, 1)), - GregDay(730_121, Date(2000, 1, 2)), - GregDay(730_484, Date(2000, 12, 30)), - GregDay(730_485, Date(2000, 12, 31)), - GregDay(730_486, Date(2001, 1, 1)), - GregDay(733_773, Date(2010, 1, 1)), - GregDay(733_774, Date(2010, 1, 2)), - GregDay(733_803, Date(2010, 1, 31)), - GregDay(733_804, Date(2010, 2, 1)), - GregDay(733_831, Date(2010, 2, 28)), - GregDay(733_832, Date(2010, 3, 1)), - GregDay(733_862, Date(2010, 3, 31)), - GregDay(733_863, Date(2010, 4, 1)), - GregDay(733_892, Date(2010, 4, 30)), - GregDay(733_893, Date(2010, 5, 1)), - GregDay(733_923, Date(2010, 5, 31)), - GregDay(733_924, Date(2010, 6, 1)), - GregDay(733_953, Date(2010, 6, 30)), - GregDay(733_954, Date(2010, 7, 1)), - GregDay(733_984, Date(2010, 7, 31)), - GregDay(733_985, Date(2010, 8, 1)), - GregDay(734_015, Date(2010, 8, 31)), - GregDay(734_016, Date(2010, 9, 1)), - GregDay(734_045, Date(2010, 9, 30)), - GregDay(734_046, Date(2010, 10, 1)), - GregDay(734_076, Date(2010, 10, 31)), - GregDay(734_077, Date(2010, 11, 1)), - GregDay(734_106, Date(2010, 11, 30)), - GregDay(734_107, Date(2010, 12, 1)), - GregDay(734_136, Date(2010, 12, 30)), - GregDay(734_137, Date(2010, 12, 31)), - GregDay(734_503, Date(2012, 1, 1)), - GregDay(734_534, Date(2012, 2, 1)), - GregDay(734_561, Date(2012, 2, 28)), - GregDay(734_562, Date(2012, 2, 29)), - GregDay(734_563, Date(2012, 3, 1)), - GregDay(734_858, Date(2012, 12, 21))]; - return result; - } - - // I'd use a Tuple, but I get forward reference errors if I try. - struct DayOfYear { int day; MonthDay md; } - DayOfYear[] testDaysOfYear() - { - static result = [DayOfYear(1, MonthDay(1, 1)), - DayOfYear(2, MonthDay(1, 2)), - DayOfYear(3, MonthDay(1, 3)), - DayOfYear(31, MonthDay(1, 31)), - DayOfYear(32, MonthDay(2, 1)), - DayOfYear(59, MonthDay(2, 28)), - DayOfYear(60, MonthDay(3, 1)), - DayOfYear(90, MonthDay(3, 31)), - DayOfYear(91, MonthDay(4, 1)), - DayOfYear(120, MonthDay(4, 30)), - DayOfYear(121, MonthDay(5, 1)), - DayOfYear(151, MonthDay(5, 31)), - DayOfYear(152, MonthDay(6, 1)), - DayOfYear(181, MonthDay(6, 30)), - DayOfYear(182, MonthDay(7, 1)), - DayOfYear(212, MonthDay(7, 31)), - DayOfYear(213, MonthDay(8, 1)), - DayOfYear(243, MonthDay(8, 31)), - DayOfYear(244, MonthDay(9, 1)), - DayOfYear(273, MonthDay(9, 30)), - DayOfYear(274, MonthDay(10, 1)), - DayOfYear(304, MonthDay(10, 31)), - DayOfYear(305, MonthDay(11, 1)), - DayOfYear(334, MonthDay(11, 30)), - DayOfYear(335, MonthDay(12, 1)), - DayOfYear(363, MonthDay(12, 29)), - DayOfYear(364, MonthDay(12, 30)), - DayOfYear(365, MonthDay(12, 31))]; - return result; - } - - DayOfYear[] testDaysOfLeapYear() - { - static result = [DayOfYear(1, MonthDay(1, 1)), - DayOfYear(2, MonthDay(1, 2)), - DayOfYear(3, MonthDay(1, 3)), - DayOfYear(31, MonthDay(1, 31)), - DayOfYear(32, MonthDay(2, 1)), - DayOfYear(59, MonthDay(2, 28)), - DayOfYear(60, MonthDay(2, 29)), - DayOfYear(61, MonthDay(3, 1)), - DayOfYear(91, MonthDay(3, 31)), - DayOfYear(92, MonthDay(4, 1)), - DayOfYear(121, MonthDay(4, 30)), - DayOfYear(122, MonthDay(5, 1)), - DayOfYear(152, MonthDay(5, 31)), - DayOfYear(153, MonthDay(6, 1)), - DayOfYear(182, MonthDay(6, 30)), - DayOfYear(183, MonthDay(7, 1)), - DayOfYear(213, MonthDay(7, 31)), - DayOfYear(214, MonthDay(8, 1)), - DayOfYear(244, MonthDay(8, 31)), - DayOfYear(245, MonthDay(9, 1)), - DayOfYear(274, MonthDay(9, 30)), - DayOfYear(275, MonthDay(10, 1)), - DayOfYear(305, MonthDay(10, 31)), - DayOfYear(306, MonthDay(11, 1)), - DayOfYear(335, MonthDay(11, 30)), - DayOfYear(336, MonthDay(12, 1)), - DayOfYear(364, MonthDay(12, 29)), - DayOfYear(365, MonthDay(12, 30)), - DayOfYear(366, MonthDay(12, 31))]; - return result; - } - - void initializeTests() - { - foreach (year; testYearsBC) - { - foreach (md; testMonthDays) - testDatesBC ~= Date(year, md.month, md.day); - } - - foreach (year; testYearsAD) - { - foreach (md; testMonthDays) - testDatesAD ~= Date(year, md.month, md.day); - } - - foreach (dt; testDatesBC) - { - foreach (tod; testTODs) - testDateTimesBC ~= DateTime(dt, tod); - } - - foreach (dt; testDatesAD) - { - foreach (tod; testTODs) - testDateTimesAD ~= DateTime(dt, tod); - } - } -} diff --git a/phobos/std/datetime/interval.d b/phobos/std/datetime/interval.d deleted file mode 100644 index d787e3a..0000000 --- a/phobos/std/datetime/interval.d +++ /dev/null @@ -1,9158 +0,0 @@ -// Written in the D programming language - -/++ -$(SCRIPT inhibitQuickIndex = 1;) -$(DIVC quickindex, -$(BOOKTABLE, -$(TR $(TH Category) $(TH Functions)) -$(TR $(TD Main types) $(TD - $(LREF Interval) - $(LREF Direction) -)) -$(TR $(TD Special intervals) $(TD - $(LREF everyDayOfWeek) - $(LREF everyMonth) - $(LREF everyDuration) -)) -$(TR $(TD Special intervals) $(TD - $(LREF NegInfInterval) - $(LREF PosInfInterval) -)) -$(TR $(TD Underlying ranges) $(TD - $(LREF IntervalRange) - $(LREF NegInfIntervalRange) - $(LREF PosInfIntervalRange) -)) -$(TR $(TD Flags) $(TD - $(LREF PopFirst) -)) -)) - - License: $(HTTP www.boost.org/LICENSE_1_0.txt, Boost License 1.0). - Authors: $(HTTP jmdavisprog.com, Jonathan M Davis) - Source: $(PHOBOSSRC std/datetime/interval.d) -+/ -module std.datetime.interval; - -import core.time : Duration, dur; -import std.datetime.date : AllowDayOverflow, DateTimeException, daysToDayOfWeek, - DayOfWeek, isTimePoint, Month; -import std.exception : enforce; -import std.range.primitives : isOutputRange; -import std.traits : isIntegral; -import std.typecons : Flag; - -version (StdUnittest) import std.exception : assertThrown; - - -/++ - Indicates a direction in time. One example of its use is $(LREF Interval)'s - $(LREF expand) function which uses it to indicate whether the interval - should be expanded backwards (into the past), forwards (into the future), or - both. - +/ -enum Direction -{ - /// Backward. - bwd, - - /// Forward. - fwd, - - /// Both backward and forward. - both -} - - -/++ - Used to indicate whether `popFront` should be called immediately upon - creating a range. The idea is that for some functions used to generate a - range for an interval, `front` is not necessarily a time point which - would ever be generated by the range (e.g. if the range were every Sunday - within an interval, but the interval started on a Monday), so there needs - to be a way to deal with that. To get the first time point in the range to - match what the function generates, then use `PopFirst.yes` to indicate - that the range should have `popFront` called on it before the range is - returned so that `front` is a time point which the function would - generate. To let the first time point not match the generator function, - use `PopFront.no`. - - For instance, if the function used to generate a range of time points - generated successive Easters (i.e. you're iterating over all of the Easters - within the interval), the initial date probably isn't an Easter. Using - `PopFirst.yes` would tell the function which returned the range that - `popFront` was to be called so that front would then be an Easter - the - next one generated by the function (which when iterating forward would be - the Easter following the original `front`, while when iterating backward, - it would be the Easter prior to the original `front`). If - `PopFirst.no` were used, then `front` would remain the original time - point and it would not necessarily be a time point which would be generated - by the range-generating function (which in many cases is exactly what is - desired - e.g. if iterating over every day starting at the beginning of the - interval). - - If set to `PopFirst.no`, then popFront is not called before returning - the range. - - Otherwise, if set to `PopFirst.yes`, then popFront is called before - returning the range. - +/ -alias PopFirst = Flag!"popFirst"; - - -/++ - Represents an interval of time. - - An `Interval` has a starting point and an end point. The interval of time - is therefore the time starting at the starting point up to, but not - including, the end point. e.g. - - $(BOOKTABLE, - $(TR $(TD [January 5th, 2010 - March 10th, 2010$(RPAREN))) - $(TR $(TD [05:00:30 - 12:00:00$(RPAREN))) - $(TR $(TD [1982-01-04T08:59:00 - 2010-07-04T12:00:00$(RPAREN))) - ) - - A range can be obtained from an `Interval`, allowing iteration over - that interval, with the exact time points which are iterated over depending - on the function which generates the range. - +/ -struct Interval(TP) -{ -public: - - /++ - Params: - begin = The time point which begins the interval. - end = The time point which ends (but is not included in) the - interval. - - Throws: - $(REF DateTimeException,std,datetime,date) if $(D_PARAM end) is - before $(D_PARAM begin). - - Example: - -------------------- - Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)); - -------------------- - +/ - this(U)(scope const TP begin, scope const U end) pure - if (is(immutable TP == immutable U)) - { - if (!_valid(begin, end)) - throw new DateTimeException("Arguments would result in an invalid Interval."); - _begin = cast(TP) begin; - _end = cast(TP) end; - } - - - /++ - Params: - begin = The time point which begins the interval. - duration = The duration from the starting point to the end point. - - Throws: - $(REF DateTimeException,std,datetime,date) if the resulting - `end` is before `begin`. - - Example: - -------------------- - assert(Interval!Date(Date(1996, 1, 2), dur!"days"(3)) == - Interval!Date(Date(1996, 1, 2), Date(1996, 1, 5))); - -------------------- - +/ - this(D)(scope const TP begin, scope const D duration) pure - if (__traits(compiles, begin + duration)) - { - _begin = cast(TP) begin; - _end = begin + duration; - if (!_valid(_begin, _end)) - throw new DateTimeException("Arguments would result in an invalid Interval."); - } - - - /++ - Params: - rhs = The $(LREF Interval) to assign to this one. - +/ - ref Interval opAssign(const ref Interval rhs) pure nothrow - { - _begin = cast(TP) rhs._begin; - _end = cast(TP) rhs._end; - return this; - } - - - /++ - Params: - rhs = The $(LREF Interval) to assign to this one. - +/ - ref Interval opAssign(Interval rhs) pure nothrow - { - _begin = cast(TP) rhs._begin; - _end = cast(TP) rhs._end; - return this; - } - - - /++ - The starting point of the interval. It is included in the interval. - - Example: - -------------------- - assert(Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).begin == - Date(1996, 1, 2)); - -------------------- - +/ - @property TP begin() const pure nothrow - { - return cast(TP) _begin; - } - - - /++ - The starting point of the interval. It is included in the interval. - - Params: - timePoint = The time point to set `begin` to. - - Throws: - $(REF DateTimeException,std,datetime,date) if the resulting - interval would be invalid. - +/ - @property void begin(TP timePoint) pure - { - if (!_valid(timePoint, _end)) - throw new DateTimeException("Arguments would result in an invalid Interval."); - _begin = timePoint; - } - - - /++ - The end point of the interval. It is excluded from the interval. - - Example: - -------------------- - assert(Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).end == - Date(2012, 3, 1)); - -------------------- - +/ - @property TP end() const pure nothrow - { - return cast(TP) _end; - } - - - /++ - The end point of the interval. It is excluded from the interval. - - Params: - timePoint = The time point to set end to. - - Throws: - $(REF DateTimeException,std,datetime,date) if the resulting - interval would be invalid. - +/ - @property void end(TP timePoint) pure - { - if (!_valid(_begin, timePoint)) - throw new DateTimeException("Arguments would result in an invalid Interval."); - _end = timePoint; - } - - - /++ - Returns the duration between `begin` and `end`. - - Example: - -------------------- - assert(Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).length == - dur!"days"(5903)); - -------------------- - +/ - @property auto length() const pure nothrow - { - return _end - _begin; - } - - - /++ - Whether the interval's length is 0, that is, whether $(D begin == end). - - Example: - -------------------- - assert(Interval!Date(Date(1996, 1, 2), Date(1996, 1, 2)).empty); - assert(!Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).empty); - -------------------- - +/ - @property bool empty() const pure nothrow - { - return _begin == _end; - } - - - /++ - Whether the given time point is within this interval. - - Params: - timePoint = The time point to check for inclusion in this interval. - - Throws: - $(REF DateTimeException,std,datetime,date) if this interval is - empty. - - Example: - -------------------- - assert(!Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).contains( - Date(1994, 12, 24))); - - assert(Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).contains( - Date(2000, 1, 5))); - assert(!Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).contains( - Date(2012, 3, 1))); - -------------------- - +/ - bool contains(scope const TP timePoint) const pure - { - _enforceNotEmpty(); - return timePoint >= _begin && timePoint < _end; - } - - - /++ - Whether the given interval is completely within this interval. - - Params: - interval = The interval to check for inclusion in this interval. - - Throws: - $(REF DateTimeException,std,datetime,date) if either interval is - empty. - - Example: - -------------------- - assert(!Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).contains( - Interval!Date(Date(1990, 7, 6), Date(2000, 8, 2)))); - - assert(Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).contains( - Interval!Date(Date(1999, 1, 12), Date(2011, 9, 17)))); - - assert(!Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).contains( - Interval!Date(Date(1998, 2, 28), Date(2013, 5, 1)))); - -------------------- - +/ - bool contains(scope const Interval interval) const pure - { - _enforceNotEmpty(); - interval._enforceNotEmpty(); - return interval._begin >= _begin && - interval._begin < _end && - interval._end <= _end; - } - - - /++ - Whether the given interval is completely within this interval. - - Always returns false (unless this interval is empty), because an - interval going to positive infinity can never be contained in a finite - interval. - - Params: - interval = The interval to check for inclusion in this interval. - - Throws: - $(REF DateTimeException,std,datetime,date) if this interval is - empty. - - Example: - -------------------- - assert(!Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).contains( - PosInfInterval!Date(Date(1999, 5, 4)))); - -------------------- - +/ - bool contains(scope const PosInfInterval!TP interval) const pure - { - _enforceNotEmpty(); - return false; - } - - - /++ - Whether the given interval is completely within this interval. - - Always returns false (unless this interval is empty), because an - interval beginning at negative infinity can never be contained in a - finite interval. - - Params: - interval = The interval to check for inclusion in this interval. - - Throws: - $(REF DateTimeException,std,datetime,date) if this interval is - empty. - - Example: - -------------------- - assert(!Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).contains( - NegInfInterval!Date(Date(1996, 5, 4)))); - -------------------- - +/ - bool contains(scope const NegInfInterval!TP interval) const pure - { - _enforceNotEmpty(); - return false; - } - - - /++ - Whether this interval is before the given time point. - - Params: - timePoint = The time point to check whether this interval is before - it. - - Throws: - $(REF DateTimeException,std,datetime,date) if this interval is - empty. - - Example: - -------------------- - assert(!Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).isBefore( - Date(1994, 12, 24))); - - assert(!Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).isBefore( - Date(2000, 1, 5))); - - assert(Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).isBefore( - Date(2012, 3, 1))); - -------------------- - +/ - bool isBefore(scope const TP timePoint) const pure - { - _enforceNotEmpty(); - return _end <= timePoint; - } - - - /++ - Whether this interval is before the given interval and does not - intersect with it. - - Params: - interval = The interval to check for against this interval. - - Throws: - $(REF DateTimeException,std,datetime,date) if either interval is - empty. - - Example: - -------------------- - assert(!Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).isBefore( - Interval!Date(Date(1990, 7, 6), Date(2000, 8, 2)))); - - assert(!Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).isBefore( - Interval!Date(Date(1999, 1, 12), Date(2011, 9, 17)))); - - assert(Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).isBefore( - Interval!Date(Date(2012, 3, 1), Date(2013, 5, 1)))); - -------------------- - +/ - bool isBefore(scope const Interval interval) const pure - { - _enforceNotEmpty(); - interval._enforceNotEmpty(); - return _end <= interval._begin; - } - - - /++ - Whether this interval is before the given interval and does not - intersect with it. - - Params: - interval = The interval to check for against this interval. - - Throws: - $(REF DateTimeException,std,datetime,date) if this interval is - empty. - - Example: - -------------------- - assert(!Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).isBefore( - PosInfInterval!Date(Date(1999, 5, 4)))); - - assert(Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).isBefore( - PosInfInterval!Date(Date(2013, 3, 7)))); - -------------------- - +/ - bool isBefore(scope const PosInfInterval!TP interval) const pure - { - _enforceNotEmpty(); - return _end <= interval._begin; - } - - - /++ - Whether this interval is before the given interval and does not - intersect with it. - - Always returns false (unless this interval is empty) because a finite - interval can never be before an interval beginning at negative infinity. - - Params: - interval = The interval to check for against this interval. - - Throws: - $(REF DateTimeException,std,datetime,date) if this interval is - empty. - - Example: - -------------------- - assert(!Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).isBefore( - NegInfInterval!Date(Date(1996, 5, 4)))); - -------------------- - +/ - bool isBefore(scope const NegInfInterval!TP interval) const pure - { - _enforceNotEmpty(); - return false; - } - - - /++ - Whether this interval is after the given time point. - - Params: - timePoint = The time point to check whether this interval is after - it. - - Throws: - $(REF DateTimeException,std,datetime,date) if this interval is - empty. - - Example: - -------------------- - assert(Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).isAfter( - Date(1994, 12, 24))); - - assert(!Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).isAfter( - Date(2000, 1, 5))); - - assert(!Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).isAfter( - Date(2012, 3, 1))); - -------------------- - +/ - bool isAfter(scope const TP timePoint) const pure - { - _enforceNotEmpty(); - return timePoint < _begin; - } - - - /++ - Whether this interval is after the given interval and does not intersect - it. - - Params: - interval = The interval to check against this interval. - - Throws: - $(REF DateTimeException,std,datetime,date) if either interval is - empty. - - Example: - -------------------- - assert(!Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).isAfter( - Interval!Date(Date(1990, 7, 6), Date(2000, 8, 2)))); - - assert(!Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).isAfter( - Interval!Date(Date(1999, 1, 12), Date(2011, 9, 17)))); - - assert(Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).isAfter( - Interval!Date(Date(1989, 3, 1), Date(1996, 1, 2)))); - -------------------- - +/ - bool isAfter(scope const Interval interval) const pure - { - _enforceNotEmpty(); - interval._enforceNotEmpty(); - return _begin >= interval._end; - } - - - /++ - Whether this interval is after the given interval and does not intersect - it. - - Always returns false (unless this interval is empty) because a finite - interval can never be after an interval going to positive infinity. - - Params: - interval = The interval to check against this interval. - - Throws: - $(REF DateTimeException,std,datetime,date) if this interval is - empty. - - Example: - -------------------- - assert(!Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).isAfter( - PosInfInterval!Date(Date(1999, 5, 4)))); - -------------------- - +/ - bool isAfter(scope const PosInfInterval!TP interval) const pure - { - _enforceNotEmpty(); - return false; - } - - - /++ - Whether this interval is after the given interval and does not intersect - it. - - Params: - interval = The interval to check against this interval. - - Throws: - $(REF DateTimeException,std,datetime,date) if this interval is - empty. - - Example: - -------------------- - assert(Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).isAfter( - NegInfInterval!Date(Date(1996, 1, 2)))); - -------------------- - +/ - bool isAfter(scope const NegInfInterval!TP interval) const pure - { - _enforceNotEmpty(); - return _begin >= interval._end; - } - - - /++ - Whether the given interval overlaps this interval. - - Params: - interval = The interval to check for intersection with this interval. - - Throws: - $(REF DateTimeException,std,datetime,date) if either interval is - empty. - - Example: - -------------------- - assert(Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).intersects( - Interval!Date(Date(1990, 7, 6), Date(2000, 8, 2)))); - - assert(Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).intersects( - Interval!Date(Date(1999, 1, 12), Date(2011, 9, 17)))); - - assert(!Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).intersects( - Interval!Date(Date(1989, 3, 1), Date(1996, 1, 2)))); - -------------------- - +/ - bool intersects(scope const Interval interval) const pure - { - _enforceNotEmpty(); - interval._enforceNotEmpty(); - return interval._begin < _end && interval._end > _begin; - } - - - /++ - Whether the given interval overlaps this interval. - - Params: - interval = The interval to check for intersection with this interval. - - Throws: - $(REF DateTimeException,std,datetime,date) if this interval is - empty. - - Example: - -------------------- - assert(Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).intersects( - PosInfInterval!Date(Date(1999, 5, 4)))); - - assert(!Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).intersects( - PosInfInterval!Date(Date(2012, 3, 1)))); - -------------------- - +/ - bool intersects(scope const PosInfInterval!TP interval) const pure - { - _enforceNotEmpty(); - return _end > interval._begin; - } - - - /++ - Whether the given interval overlaps this interval. - - Params: - interval = The interval to check for intersection with this interval. - - Throws: - $(REF DateTimeException,std,datetime,date) if this interval is - empty. - - Example: - -------------------- - assert(!Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).intersects( - NegInfInterval!Date(Date(1996, 1, 2)))); - - assert(Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).intersects( - NegInfInterval!Date(Date(2000, 1, 2)))); - -------------------- - +/ - bool intersects(scope const NegInfInterval!TP interval) const pure - { - _enforceNotEmpty(); - return _begin < interval._end; - } - - - /++ - Returns the intersection of two intervals - - Params: - interval = The interval to intersect with this interval. - - Throws: - $(REF DateTimeException,std,datetime,date) if the two intervals do - not intersect or if either interval is empty. - - Example: - -------------------- - assert(Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).intersection( - Interval!Date(Date(1990, 7, 6), Date(2000, 8, 2))) == - Interval!Date(Date(1996, 1 , 2), Date(2000, 8, 2))); - - assert(Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).intersection( - Interval!Date(Date(1999, 1, 12), Date(2011, 9, 17))) == - Interval!Date(Date(1999, 1 , 12), Date(2011, 9, 17))); - -------------------- - +/ - Interval intersection(scope const Interval interval) const - { - import std.format : format; - - enforce(this.intersects(interval), - new DateTimeException(format("%s and %s do not intersect.", this, interval))); - - auto begin = _begin > interval._begin ? _begin : interval._begin; - auto end = _end < interval._end ? _end : interval._end; - - return Interval(begin, end); - } - - - /++ - Returns the intersection of two intervals - - Params: - interval = The interval to intersect with this interval. - - Throws: - $(REF DateTimeException,std,datetime,date) if the two intervals do - not intersect or if this interval is empty. - - Example: - -------------------- - assert(Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).intersection( - PosInfInterval!Date(Date(1990, 7, 6))) == - Interval!Date(Date(1996, 1 , 2), Date(2012, 3, 1))); - - assert(Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).intersection( - PosInfInterval!Date(Date(1999, 1, 12))) == - Interval!Date(Date(1999, 1 , 12), Date(2012, 3, 1))); - -------------------- - +/ - Interval intersection(scope const PosInfInterval!TP interval) const - { - import std.format : format; - - enforce(this.intersects(interval), - new DateTimeException(format("%s and %s do not intersect.", this, interval))); - - return Interval(_begin > interval._begin ? _begin : interval._begin, _end); - } - - - /++ - Returns the intersection of two intervals - - Params: - interval = The interval to intersect with this interval. - - Throws: - $(REF DateTimeException,std,datetime,date) if the two intervals do - not intersect or if this interval is empty. - - Example: - -------------------- - assert(Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).intersection( - NegInfInterval!Date(Date(1999, 7, 6))) == - Interval!Date(Date(1996, 1 , 2), Date(1999, 7, 6))); - - assert(Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).intersection( - NegInfInterval!Date(Date(2013, 1, 12))) == - Interval!Date(Date(1996, 1 , 2), Date(2012, 3, 1))); - -------------------- - +/ - Interval intersection(scope const NegInfInterval!TP interval) const - { - import std.format : format; - - enforce(this.intersects(interval), - new DateTimeException(format("%s and %s do not intersect.", this, interval))); - - return Interval(_begin, _end < interval._end ? _end : interval._end); - } - - - /++ - Whether the given interval is adjacent to this interval. - - Params: - interval = The interval to check whether its adjecent to this - interval. - - Throws: - $(REF DateTimeException,std,datetime,date) if either interval is - empty. - - Example: - -------------------- - assert(Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).isAdjacent( - Interval!Date(Date(1990, 7, 6), Date(1996, 1, 2)))); - - assert(Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).isAdjacent( - Interval!Date(Date(2012, 3, 1), Date(2013, 9, 17)))); - - assert(!Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).isAdjacent( - Interval!Date(Date(1989, 3, 1), Date(2012, 3, 1)))); - -------------------- - +/ - bool isAdjacent(scope const Interval interval) const pure - { - _enforceNotEmpty(); - interval._enforceNotEmpty(); - return _begin == interval._end || _end == interval._begin; - } - - - /++ - Whether the given interval is adjacent to this interval. - - Params: - interval = The interval to check whether its adjecent to this - interval. - - Throws: - $(REF DateTimeException,std,datetime,date) if this interval is - empty. - - Example: - -------------------- - assert(!Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).isAdjacent( - PosInfInterval!Date(Date(1999, 5, 4)))); - - assert(Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).isAdjacent( - PosInfInterval!Date(Date(2012, 3, 1)))); - -------------------- - +/ - bool isAdjacent(scope const PosInfInterval!TP interval) const pure - { - _enforceNotEmpty(); - return _end == interval._begin; - } - - - /++ - Whether the given interval is adjacent to this interval. - - Params: - interval = The interval to check whether its adjecent to this - interval. - - Throws: - $(REF DateTimeException,std,datetime,date) if this interval is - empty. - - Example: - -------------------- - assert(Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).isAdjacent( - NegInfInterval!Date(Date(1996, 1, 2)))); - - assert(!Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).isAdjacent( - NegInfInterval!Date(Date(2000, 1, 2)))); - -------------------- - +/ - bool isAdjacent(scope const NegInfInterval!TP interval) const pure - { - _enforceNotEmpty(); - return _begin == interval._end; - } - - - /++ - Returns the union of two intervals - - Params: - interval = The interval to merge with this interval. - - Throws: - $(REF DateTimeException,std,datetime,date) if the two intervals do - not intersect and are not adjacent or if either interval is empty. - - Example: - -------------------- - assert(Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).merge( - Interval!Date(Date(1990, 7, 6), Date(2000, 8, 2))) == - Interval!Date(Date(1990, 7 , 6), Date(2012, 3, 1))); - - assert(Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).merge( - Interval!Date(Date(2012, 3, 1), Date(2013, 5, 7))) == - Interval!Date(Date(1996, 1 , 2), Date(2013, 5, 7))); - -------------------- - +/ - Interval merge(scope const Interval interval) const - { - import std.format : format; - - enforce(this.isAdjacent(interval) || this.intersects(interval), - new DateTimeException(format("%s and %s are not adjacent and do not intersect.", this, interval))); - - auto begin = _begin < interval._begin ? _begin : interval._begin; - auto end = _end > interval._end ? _end : interval._end; - - return Interval(begin, end); - } - - - /++ - Returns the union of two intervals - - Params: - interval = The interval to merge with this interval. - - Throws: - $(REF DateTimeException,std,datetime,date) if the two intervals do - not intersect and are not adjacent or if this interval is empty. - - Example: - -------------------- - assert(Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).merge( - PosInfInterval!Date(Date(1990, 7, 6))) == - PosInfInterval!Date(Date(1990, 7 , 6))); - - assert(Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).merge( - PosInfInterval!Date(Date(2012, 3, 1))) == - PosInfInterval!Date(Date(1996, 1 , 2))); - -------------------- - +/ - PosInfInterval!TP merge(scope const PosInfInterval!TP interval) const - { - import std.format : format; - - enforce(this.isAdjacent(interval) || this.intersects(interval), - new DateTimeException(format("%s and %s are not adjacent and do not intersect.", this, interval))); - - return PosInfInterval!TP(_begin < interval._begin ? _begin : interval._begin); - } - - - /++ - Returns the union of two intervals - - Params: - interval = The interval to merge with this interval. - - Throws: - $(REF DateTimeException,std,datetime,date) if the two intervals do - not intersect and are not adjacent or if this interval is empty. - - Example: - -------------------- - assert(Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).merge( - NegInfInterval!Date(Date(1996, 1, 2))) == - NegInfInterval!Date(Date(2012, 3 , 1))); - - assert(Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).merge( - NegInfInterval!Date(Date(2013, 1, 12))) == - NegInfInterval!Date(Date(2013, 1 , 12))); - -------------------- - +/ - NegInfInterval!TP merge(scope const NegInfInterval!TP interval) const - { - import std.format : format; - - enforce(this.isAdjacent(interval) || this.intersects(interval), - new DateTimeException(format("%s and %s are not adjacent and do not intersect.", this, interval))); - - return NegInfInterval!TP(_end > interval._end ? _end : interval._end); - } - - - /++ - Returns an interval that covers from the earliest time point of two - intervals up to (but not including) the latest time point of two - intervals. - - Params: - interval = The interval to create a span together with this interval. - - Throws: - $(REF DateTimeException,std,datetime,date) if either interval is - empty. - - Example: - -------------------- - assert(Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).span( - Interval!Date(Date(1990, 7, 6), Date(1991, 1, 8))) == - Interval!Date(Date(1990, 7 , 6), Date(2012, 3, 1))); - - assert(Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).span( - Interval!Date(Date(2012, 3, 1), Date(2013, 5, 7))) == - Interval!Date(Date(1996, 1 , 2), Date(2013, 5, 7))); - -------------------- - +/ - Interval span(scope const Interval interval) const pure - { - _enforceNotEmpty(); - interval._enforceNotEmpty(); - - auto begin = _begin < interval._begin ? _begin : interval._begin; - auto end = _end > interval._end ? _end : interval._end; - - return Interval(begin, end); - } - - - /++ - Returns an interval that covers from the earliest time point of two - intervals up to (but not including) the latest time point of two - intervals. - - Params: - interval = The interval to create a span together with this interval. - - Throws: - $(REF DateTimeException,std,datetime,date) if this interval is - empty. - - Example: - -------------------- - assert(Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).span( - PosInfInterval!Date(Date(1990, 7, 6))) == - PosInfInterval!Date(Date(1990, 7 , 6))); - - assert(Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).span( - PosInfInterval!Date(Date(2050, 1, 1))) == - PosInfInterval!Date(Date(1996, 1 , 2))); - -------------------- - +/ - PosInfInterval!TP span(scope const PosInfInterval!TP interval) const pure - { - _enforceNotEmpty(); - return PosInfInterval!TP(_begin < interval._begin ? _begin : interval._begin); - } - - - /++ - Returns an interval that covers from the earliest time point of two - intervals up to (but not including) the latest time point of two - intervals. - - Params: - interval = The interval to create a span together with this interval. - - Throws: - $(REF DateTimeException,std,datetime,date) if this interval is - empty. - - Example: - -------------------- - assert(Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).span( - NegInfInterval!Date(Date(1602, 5, 21))) == - NegInfInterval!Date(Date(2012, 3 , 1))); - - assert(Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).span( - NegInfInterval!Date(Date(2013, 1, 12))) == - NegInfInterval!Date(Date(2013, 1 , 12))); - -------------------- - +/ - NegInfInterval!TP span(scope const NegInfInterval!TP interval) const pure - { - _enforceNotEmpty(); - return NegInfInterval!TP(_end > interval._end ? _end : interval._end); - } - - - /++ - Shifts the interval forward or backwards in time by the given duration - (a positive duration shifts the interval forward; a negative duration - shifts it backward). Effectively, it does $(D begin += duration) and - $(D end += duration). - - Params: - duration = The duration to shift the interval by. - - Throws: - $(REF DateTimeException,std,datetime,date) this interval is empty - or if the resulting interval would be invalid. - - Example: - -------------------- - auto interval1 = Interval!Date(Date(1996, 1, 2), Date(2012, 4, 5)); - auto interval2 = Interval!Date(Date(1996, 1, 2), Date(2012, 4, 5)); - - interval1.shift(dur!"days"(50)); - assert(interval1 == Interval!Date(Date(1996, 2, 21), Date(2012, 5, 25))); - - interval2.shift(dur!"days"(-50)); - assert(interval2 == Interval!Date(Date(1995, 11, 13), Date(2012, 2, 15))); - -------------------- - +/ - void shift(D)(D duration) pure - if (__traits(compiles, begin + duration)) - { - _enforceNotEmpty(); - - auto begin = _begin + duration; - auto end = _end + duration; - - if (!_valid(begin, end)) - throw new DateTimeException("Argument would result in an invalid Interval."); - - _begin = begin; - _end = end; - } - - - static if (__traits(compiles, begin.add!"months"(1)) && - __traits(compiles, begin.add!"years"(1))) - { - /++ - Shifts the interval forward or backwards in time by the given number - of years and/or months (a positive number of years and months shifts - the interval forward; a negative number shifts it backward). - It adds the years the given years and months to both begin and end. - It effectively calls `add!"years"()` and then `add!"months"()` - on begin and end with the given number of years and months. - - Params: - years = The number of years to shift the interval by. - months = The number of months to shift the interval by. - allowOverflow = Whether the days should be allowed to overflow - on `begin` and `end`, causing their month - to increment. - - Throws: - $(REF DateTimeException,std,datetime,date) if this interval is - empty or if the resulting interval would be invalid. - - Example: - -------------------- - auto interval1 = Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)); - auto interval2 = Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)); - - interval1.shift(2); - assert(interval1 == Interval!Date(Date(1998, 1, 2), Date(2014, 3, 1))); - - interval2.shift(-2); - assert(interval2 == Interval!Date(Date(1994, 1, 2), Date(2010, 3, 1))); - -------------------- - +/ - void shift(T)(T years, T months = 0, AllowDayOverflow allowOverflow = AllowDayOverflow.yes) - if (isIntegral!T) - { - _enforceNotEmpty(); - - auto begin = _begin; - auto end = _end; - - begin.add!"years"(years, allowOverflow); - begin.add!"months"(months, allowOverflow); - end.add!"years"(years, allowOverflow); - end.add!"months"(months, allowOverflow); - - enforce(_valid(begin, end), new DateTimeException("Argument would result in an invalid Interval.")); - - _begin = begin; - _end = end; - } - } - - - /++ - Expands the interval forwards and/or backwards in time. Effectively, - it does $(D begin -= duration) and/or $(D end += duration). Whether - it expands forwards and/or backwards in time is determined by - $(D_PARAM dir). - - Params: - duration = The duration to expand the interval by. - dir = The direction in time to expand the interval. - - Throws: - $(REF DateTimeException,std,datetime,date) this interval is empty - or if the resulting interval would be invalid. - - Example: - -------------------- - auto interval1 = Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)); - auto interval2 = Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)); - - interval1.expand(2); - assert(interval1 == Interval!Date(Date(1994, 1, 2), Date(2014, 3, 1))); - - interval2.expand(-2); - assert(interval2 == Interval!Date(Date(1998, 1, 2), Date(2010, 3, 1))); - -------------------- - +/ - void expand(D)(D duration, Direction dir = Direction.both) pure - if (__traits(compiles, begin + duration)) - { - _enforceNotEmpty(); - - switch (dir) - { - case Direction.both: - { - auto begin = _begin - duration; - auto end = _end + duration; - - if (!_valid(begin, end)) - throw new DateTimeException("Argument would result in an invalid Interval."); - - _begin = begin; - _end = end; - - return; - } - case Direction.fwd: - { - auto end = _end + duration; - - if (!_valid(_begin, end)) - throw new DateTimeException("Argument would result in an invalid Interval."); - _end = end; - - return; - } - case Direction.bwd: - { - auto begin = _begin - duration; - - if (!_valid(begin, _end)) - throw new DateTimeException("Argument would result in an invalid Interval."); - _begin = begin; - - return; - } - default: - assert(0, "Invalid Direction."); - } - } - - static if (__traits(compiles, begin.add!"months"(1)) && - __traits(compiles, begin.add!"years"(1))) - { - /++ - Expands the interval forwards and/or backwards in time. Effectively, - it subtracts the given number of months/years from `begin` and - adds them to `end`. Whether it expands forwards and/or backwards - in time is determined by $(D_PARAM dir). - - Params: - years = The number of years to expand the interval by. - months = The number of months to expand the interval by. - allowOverflow = Whether the days should be allowed to overflow - on `begin` and `end`, causing their month - to increment. - dir = The direction in time to expand the interval. - - Throws: - $(REF DateTimeException,std,datetime,date) if this interval is - empty or if the resulting interval would be invalid. - - Example: - -------------------- - auto interval1 = Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)); - auto interval2 = Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)); - - interval1.expand(2); - assert(interval1 == Interval!Date(Date(1994, 1, 2), Date(2014, 3, 1))); - - interval2.expand(-2); - assert(interval2 == Interval!Date(Date(1998, 1, 2), Date(2010, 3, 1))); - -------------------- - +/ - void expand(T)(T years, - T months = 0, - AllowDayOverflow allowOverflow = AllowDayOverflow.yes, - Direction dir = Direction.both) - if (isIntegral!T) - { - _enforceNotEmpty(); - - switch (dir) - { - case Direction.both: - { - auto begin = _begin; - auto end = _end; - - begin.add!"years"(-years, allowOverflow); - begin.add!"months"(-months, allowOverflow); - end.add!"years"(years, allowOverflow); - end.add!"months"(months, allowOverflow); - - enforce(_valid(begin, end), new DateTimeException("Argument would result in an invalid Interval.")); - _begin = begin; - _end = end; - - return; - } - case Direction.fwd: - { - auto end = _end; - - end.add!"years"(years, allowOverflow); - end.add!"months"(months, allowOverflow); - - enforce(_valid(_begin, end), - new DateTimeException("Argument would result in an invalid Interval.")); - _end = end; - - return; - } - case Direction.bwd: - { - auto begin = _begin; - - begin.add!"years"(-years, allowOverflow); - begin.add!"months"(-months, allowOverflow); - - enforce(_valid(begin, _end), - new DateTimeException("Argument would result in an invalid Interval.")); - _begin = begin; - - return; - } - default: - assert(0, "Invalid Direction."); - } - } - } - - - /++ - Returns a range which iterates forward over the interval, starting - at `begin`, using $(D_PARAM func) to generate each successive time - point. - - The range's `front` is the interval's `begin`. $(D_PARAM func) is - used to generate the next `front` when `popFront` is called. If - $(D_PARAM popFirst) is `PopFirst.yes`, then `popFront` is called - before the range is returned (so that `front` is a time point which - $(D_PARAM func) would generate). - - If $(D_PARAM func) ever generates a time point less than or equal to the - current `front` of the range, then a - $(REF DateTimeException,std,datetime,date) will be thrown. The range - will be empty and iteration complete when $(D_PARAM func) generates a - time point equal to or beyond the `end` of the interval. - - There are helper functions in this module which generate common - delegates to pass to `fwdRange`. Their documentation starts with - "Range-generating function," making them easily searchable. - - Params: - func = The function used to generate the time points of the - range over the interval. - popFirst = Whether `popFront` should be called on the range - before returning it. - - Throws: - $(REF DateTimeException,std,datetime,date) if this interval is - empty. - - Warning: - $(D_PARAM func) must be logically pure. Ideally, $(D_PARAM func) - would be a function pointer to a pure function, but forcing - $(D_PARAM func) to be pure is far too restrictive to be useful, and - in order to have the ease of use of having functions which generate - functions to pass to `fwdRange`, $(D_PARAM func) must be a - delegate. - - If $(D_PARAM func) retains state which changes as it is called, then - some algorithms will not work correctly, because the range's - `save` will have failed to have really saved the range's state. - To avoid such bugs, don't pass a delegate which is - not logically pure to `fwdRange`. If $(D_PARAM func) is given the - same time point with two different calls, it must return the same - result both times. - - Of course, none of the functions in this module have this problem, - so it's only relevant if when creating a custom delegate. - - Example: - -------------------- - auto interval = Interval!Date(Date(2010, 9, 1), Date(2010, 9, 9)); - auto func = delegate (scope const Date date) // For iterating over even-numbered days. - { - if ((date.day & 1) == 0) - return date + dur!"days"(2); - - return date + dur!"days"(1); - }; - auto range = interval.fwdRange(func); - - // An odd day. Using PopFirst.yes would have made this Date(2010, 9, 2). - assert(range.front == Date(2010, 9, 1)); - - range.popFront(); - assert(range.front == Date(2010, 9, 2)); - - range.popFront(); - assert(range.front == Date(2010, 9, 4)); - - range.popFront(); - assert(range.front == Date(2010, 9, 6)); - - range.popFront(); - assert(range.front == Date(2010, 9, 8)); - - range.popFront(); - assert(range.empty); - -------------------- - +/ - IntervalRange!(TP, Direction.fwd) fwdRange(TP delegate(scope const TP) func, PopFirst popFirst = PopFirst.no) const - { - _enforceNotEmpty(); - - auto range = IntervalRange!(TP, Direction.fwd)(this, func); - - if (popFirst == PopFirst.yes) - range.popFront(); - - return range; - } - - - /++ - Returns a range which iterates backwards over the interval, starting - at `end`, using $(D_PARAM func) to generate each successive time - point. - - The range's `front` is the interval's `end`. $(D_PARAM func) is - used to generate the next `front` when `popFront` is called. If - $(D_PARAM popFirst) is `PopFirst.yes`, then `popFront` is called - before the range is returned (so that `front` is a time point which - $(D_PARAM func) would generate). - - If $(D_PARAM func) ever generates a time point greater than or equal to - the current `front` of the range, then a - $(REF DateTimeException,std,datetime,date) will be thrown. The range - will be empty and iteration complete when $(D_PARAM func) generates a - time point equal to or less than the `begin` of the interval. - - There are helper functions in this module which generate common - delegates to pass to `bwdRange`. Their documentation starts with - "Range-generating function," making them easily searchable. - - Params: - func = The function used to generate the time points of the - range over the interval. - popFirst = Whether `popFront` should be called on the range - before returning it. - - Throws: - $(REF DateTimeException,std,datetime,date) if this interval is - empty. - - Warning: - $(D_PARAM func) must be logically pure. Ideally, $(D_PARAM func) - would be a function pointer to a pure function, but forcing - $(D_PARAM func) to be pure is far too restrictive to be useful, and - in order to have the ease of use of having functions which generate - functions to pass to `fwdRange`, $(D_PARAM func) must be a - delegate. - - If $(D_PARAM func) retains state which changes as it is called, then - some algorithms will not work correctly, because the range's - `save` will have failed to have really saved the range's state. - To avoid such bugs, don't pass a delegate which is - not logically pure to `fwdRange`. If $(D_PARAM func) is given the - same time point with two different calls, it must return the same - result both times. - - Of course, none of the functions in this module have this problem, - so it's only relevant for custom delegates. - - Example: - -------------------- - auto interval = Interval!Date(Date(2010, 9, 1), Date(2010, 9, 9)); - auto func = delegate (scope const Date date) // For iterating over even-numbered days. - { - if ((date.day & 1) == 0) - return date - dur!"days"(2); - - return date - dur!"days"(1); - }; - auto range = interval.bwdRange(func); - - // An odd day. Using PopFirst.yes would have made this Date(2010, 9, 8). - assert(range.front == Date(2010, 9, 9)); - - range.popFront(); - assert(range.front == Date(2010, 9, 8)); - - range.popFront(); - assert(range.front == Date(2010, 9, 6)); - - range.popFront(); - assert(range.front == Date(2010, 9, 4)); - - range.popFront(); - assert(range.front == Date(2010, 9, 2)); - - range.popFront(); - assert(range.empty); - -------------------- - +/ - IntervalRange!(TP, Direction.bwd) bwdRange(TP delegate(scope const TP) func, PopFirst popFirst = PopFirst.no) const - { - _enforceNotEmpty(); - - auto range = IntervalRange!(TP, Direction.bwd)(this, func); - - if (popFirst == PopFirst.yes) - range.popFront(); - - return range; - } - - /++ - Converts this interval to a string. - Params: - w = A `char` accepting - $(REF_ALTTEXT output range, isOutputRange, std, range, primitives) - Returns: - A `string` when not using an output range; `void` otherwise. - +/ - string toString() const @safe nothrow - { - import std.array : appender; - auto app = appender!string(); - try - toString(app); - catch (Exception e) - assert(0, "toString() threw."); - return app.data; - } - - /// ditto - void toString(Writer)(ref Writer w) const - if (isOutputRange!(Writer, char)) - { - import std.range.primitives : put; - put(w, '['); - _begin.toString(w); - put(w, " - "); - _end.toString(w); - put(w, ')'); - } - -private: - /+ - Throws: - $(REF DateTimeException,std,datetime,date) if this interval is - empty. - +/ - void _enforceNotEmpty(size_t line = __LINE__) const pure - { - if (empty) - throw new DateTimeException("Invalid operation for an empty Interval.", __FILE__, line); - } - - - /+ - Whether the given values form a valid time interval. - - Params: - begin = The starting point of the interval. - end = The end point of the interval. - +/ - static bool _valid(scope const TP begin, scope const TP end) pure nothrow @trusted - { - return begin <= end; - } - - - pure invariant() - { - assert(_valid(_begin, _end), "Invariant Failure: begin is not before or equal to end."); - } - - - TP _begin; - TP _end; -} - -// Test Interval's constructors. -@safe unittest -{ - import std.datetime.date; - import std.datetime.systime; - - assertThrown!DateTimeException(Interval!Date(Date(2010, 1, 1), Date(1, 1, 1))); - - Interval!Date(Date.init, Date.init); - Interval!TimeOfDay(TimeOfDay.init, TimeOfDay.init); - Interval!DateTime(DateTime.init, DateTime.init); - Interval!SysTime(SysTime(0), SysTime(0)); - - Interval!DateTime(DateTime.init, dur!"days"(7)); - - // Verify Examples. - Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)); - assert(Interval!Date(Date(1996, 1, 2), dur!"weeks"(3)) == Interval!Date(Date(1996, 1, 2), Date(1996, 1, 23))); - assert(Interval!Date(Date(1996, 1, 2), dur!"days"(3)) == Interval!Date(Date(1996, 1, 2), Date(1996, 1, 5))); - assert(Interval!DateTime(DateTime(1996, 1, 2, 12, 0, 0), dur!"hours"(3)) == - Interval!DateTime(DateTime(1996, 1, 2, 12, 0, 0), DateTime(1996, 1, 2, 15, 0, 0))); - assert(Interval!DateTime(DateTime(1996, 1, 2, 12, 0, 0), dur!"minutes"(3)) == - Interval!DateTime(DateTime(1996, 1, 2, 12, 0, 0), DateTime(1996, 1, 2, 12, 3, 0))); - assert(Interval!DateTime(DateTime(1996, 1, 2, 12, 0, 0), dur!"seconds"(3)) == - Interval!DateTime(DateTime(1996, 1, 2, 12, 0, 0), DateTime(1996, 1, 2, 12, 0, 3))); - assert(Interval!DateTime(DateTime(1996, 1, 2, 12, 0, 0), dur!"msecs"(3000)) == - Interval!DateTime(DateTime(1996, 1, 2, 12, 0, 0), DateTime(1996, 1, 2, 12, 0, 3))); -} - -// Test Interval's begin. -@safe unittest -{ - import std.datetime.date; - - assert(Interval!Date(Date(1, 1, 1), Date(2010, 1, 1)).begin == Date(1, 1, 1)); - assert(Interval!Date(Date(2010, 1, 1), Date(2010, 1, 1)).begin == Date(2010, 1, 1)); - assert(Interval!Date(Date(1997, 12, 31), Date(1998, 1, 1)).begin == Date(1997, 12, 31)); - - const cInterval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); - immutable iInterval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); - assert(cInterval.begin == Date(2010, 7, 4)); - assert(iInterval.begin == Date(2010, 7, 4)); - - // Verify Examples. - assert(Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).begin == Date(1996, 1, 2)); -} - -// Test Interval's end. -@safe unittest -{ - import std.datetime.date; - - assert(Interval!Date(Date(1, 1, 1), Date(2010, 1, 1)).end == Date(2010, 1, 1)); - assert(Interval!Date(Date(2010, 1, 1), Date(2010, 1, 1)).end == Date(2010, 1, 1)); - assert(Interval!Date(Date(1997, 12, 31), Date(1998, 1, 1)).end == Date(1998, 1, 1)); - - const cInterval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); - immutable iInterval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); - assert(cInterval.end == Date(2012, 1, 7)); - assert(iInterval.end == Date(2012, 1, 7)); - - // Verify Examples. - assert(Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).end == Date(2012, 3, 1)); -} - -// Test Interval's length. -@safe unittest -{ - import std.datetime.date; - import std.datetime.systime; - - assert(Interval!Date(Date(2010, 1, 1), Date(2010, 1, 1)).length == dur!"days"(0)); - assert(Interval!Date(Date(2010, 1, 1), Date(2010, 4, 1)).length == dur!"days"(90)); - assert(Interval!TimeOfDay(TimeOfDay(0, 30, 0), TimeOfDay(12, 22, 7)).length == dur!"seconds"(42_727)); - assert(Interval!DateTime(DateTime(2010, 1, 1, 0, 30, 0), DateTime(2010, 1, 2, 12, 22, 7)).length == - dur!"seconds"(129_127)); - assert(Interval!SysTime(SysTime(DateTime(2010, 1, 1, 0, 30, 0)),SysTime(DateTime(2010, 1, 2, 12, 22, 7))).length == - dur!"seconds"(129_127)); - - const cInterval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); - immutable iInterval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); - assert(cInterval.length != Duration.zero); - assert(iInterval.length != Duration.zero); - - // Verify Examples. - assert(Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).length == dur!"days"(5903)); -} - -// Test Interval's empty. -@safe unittest -{ - import std.datetime.date; - import std.datetime.systime; - - assert(Interval!Date(Date(2010, 1, 1), Date(2010, 1, 1)).empty); - assert(!Interval!Date(Date(2010, 1, 1), Date(2010, 4, 1)).empty); - assert(!Interval!TimeOfDay(TimeOfDay(0, 30, 0), TimeOfDay(12, 22, 7)).empty); - assert(!Interval!DateTime(DateTime(2010, 1, 1, 0, 30, 0), DateTime(2010, 1, 2, 12, 22, 7)).empty); - assert(!Interval!SysTime(SysTime(DateTime(2010, 1, 1, 0, 30, 0)), SysTime(DateTime(2010, 1, 2, 12, 22, 7))).empty); - - const cInterval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); - immutable iInterval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); - assert(!cInterval.empty); - assert(!iInterval.empty); - - // Verify Examples. - assert(Interval!Date(Date(1996, 1, 2), Date(1996, 1, 2)).empty); - assert(!Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).empty); -} - -// Test Interval's contains(time point). -@safe unittest -{ - import std.datetime.date; - - auto interval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); - - assertThrown!DateTimeException(Interval!Date(Date(2010, 7, 4), dur!"days"(0)).contains(Date(2010, 7, 4))); - - assert(!interval.contains(Date(2009, 7, 4))); - assert(!interval.contains(Date(2010, 7, 3))); - assert(interval.contains(Date(2010, 7, 4))); - assert(interval.contains(Date(2010, 7, 5))); - assert(interval.contains(Date(2011, 7, 1))); - assert(interval.contains(Date(2012, 1, 6))); - assert(!interval.contains(Date(2012, 1, 7))); - assert(!interval.contains(Date(2012, 1, 8))); - assert(!interval.contains(Date(2013, 1, 7))); - - const cdate = Date(2010, 7, 6); - const cInterval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); - immutable iInterval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); - assert(interval.contains(cdate)); - assert(cInterval.contains(cdate)); - assert(iInterval.contains(cdate)); - - // Verify Examples. - assert(!Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).contains(Date(1994, 12, 24))); - assert(Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).contains(Date(2000, 1, 5))); - assert(!Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).contains(Date(2012, 3, 1))); -} - -// Test Interval's contains(Interval). -@safe unittest -{ - import std.datetime.date; - - auto interval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); - - assertThrown!DateTimeException(interval.contains(Interval!Date(Date(2010, 7, 4), dur!"days"(0)))); - assertThrown!DateTimeException(Interval!Date(Date(2010, 7, 4), dur!"days"(0)).contains(interval)); - assertThrown!DateTimeException(Interval!Date(Date(2010, 7, 4),dur!"days"(0)).contains( - Interval!Date(Date(2010, 7, 4), dur!"days"(0)))); - - assert(interval.contains(interval)); - assert(!interval.contains(Interval!Date(Date(2010, 7, 1), Date(2010, 7, 3)))); - assert(!interval.contains(Interval!Date(Date(2010, 7, 1), Date(2013, 7, 3)))); - assert(!interval.contains(Interval!Date(Date(2010, 7, 3), Date(2010, 7, 4)))); - assert(!interval.contains(Interval!Date(Date(2010, 7, 3), Date(2010, 7, 5)))); - assert(!interval.contains(Interval!Date(Date(2010, 7, 3), Date(2012, 1, 7)))); - assert(!interval.contains(Interval!Date(Date(2010, 7, 3), Date(2012, 1, 8)))); - assert(interval.contains(Interval!Date(Date(2010, 7, 5), Date(2012, 1, 6)))); - assert(interval.contains(Interval!Date(Date(2010, 7, 5), Date(2012, 1, 7)))); - assert(interval.contains(Interval!Date(Date(2012, 1, 6), Date(2012, 1, 7)))); - assert(!interval.contains(Interval!Date(Date(2012, 1, 6), Date(2012, 1, 8)))); - assert(!interval.contains(Interval!Date(Date(2012, 1, 7), Date(2012, 1, 8)))); - assert(!interval.contains(Interval!Date(Date(2012, 1, 8), Date(2012, 1, 9)))); - - assert(!Interval!Date(Date(2010, 7, 1), Date(2010, 7, 3)).contains(interval)); - assert(Interval!Date(Date(2010, 7, 1), Date(2013, 7, 3)).contains(interval)); - assert(!Interval!Date(Date(2010, 7, 3), Date(2010, 7, 4)).contains(interval)); - assert(!Interval!Date(Date(2010, 7, 3), Date(2010, 7, 5)).contains(interval)); - assert(Interval!Date(Date(2010, 7, 3), Date(2012, 1, 7)).contains(interval)); - assert(Interval!Date(Date(2010, 7, 3), Date(2012, 1, 8)).contains(interval)); - assert(!Interval!Date(Date(2010, 7, 5), Date(2012, 1, 6)).contains(interval)); - assert(!Interval!Date(Date(2010, 7, 5), Date(2012, 1, 7)).contains(interval)); - assert(!Interval!Date(Date(2012, 1, 6), Date(2012, 1, 7)).contains(interval)); - assert(!Interval!Date(Date(2012, 1, 6), Date(2012, 1, 8)).contains(interval)); - assert(!Interval!Date(Date(2012, 1, 7), Date(2012, 1, 8)).contains(interval)); - assert(!Interval!Date(Date(2012, 1, 8), Date(2012, 1, 9)).contains(interval)); - - assert(!interval.contains(PosInfInterval!Date(Date(2010, 7, 3)))); - assert(!interval.contains(PosInfInterval!Date(Date(2010, 7, 4)))); - assert(!interval.contains(PosInfInterval!Date(Date(2010, 7, 5)))); - assert(!interval.contains(PosInfInterval!Date(Date(2012, 1, 6)))); - assert(!interval.contains(PosInfInterval!Date(Date(2012, 1, 7)))); - assert(!interval.contains(PosInfInterval!Date(Date(2012, 1, 8)))); - - assert(!interval.contains(NegInfInterval!Date(Date(2010, 7, 3)))); - assert(!interval.contains(NegInfInterval!Date(Date(2010, 7, 4)))); - assert(!interval.contains(NegInfInterval!Date(Date(2010, 7, 5)))); - assert(!interval.contains(NegInfInterval!Date(Date(2012, 1, 6)))); - assert(!interval.contains(NegInfInterval!Date(Date(2012, 1, 7)))); - assert(!interval.contains(NegInfInterval!Date(Date(2012, 1, 8)))); - - const cInterval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); - immutable iInterval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); - auto posInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); - const cPosInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); - immutable iPosInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); - auto negInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); - const cNegInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); - immutable iNegInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); - assert(interval.contains(interval)); - assert(interval.contains(cInterval)); - assert(interval.contains(iInterval)); - assert(!interval.contains(posInfInterval)); - assert(!interval.contains(cPosInfInterval)); - assert(!interval.contains(iPosInfInterval)); - assert(!interval.contains(negInfInterval)); - assert(!interval.contains(cNegInfInterval)); - assert(!interval.contains(iNegInfInterval)); - assert(cInterval.contains(interval)); - assert(cInterval.contains(cInterval)); - assert(cInterval.contains(iInterval)); - assert(!cInterval.contains(posInfInterval)); - assert(!cInterval.contains(cPosInfInterval)); - assert(!cInterval.contains(iPosInfInterval)); - assert(!cInterval.contains(negInfInterval)); - assert(!cInterval.contains(cNegInfInterval)); - assert(!cInterval.contains(iNegInfInterval)); - assert(iInterval.contains(interval)); - assert(iInterval.contains(cInterval)); - assert(iInterval.contains(iInterval)); - assert(!iInterval.contains(posInfInterval)); - assert(!iInterval.contains(cPosInfInterval)); - assert(!iInterval.contains(iPosInfInterval)); - assert(!iInterval.contains(negInfInterval)); - assert(!iInterval.contains(cNegInfInterval)); - assert(!iInterval.contains(iNegInfInterval)); - - // Verify Examples. - assert(!Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).contains( - Interval!Date(Date(1990, 7, 6), Date(2000, 8, 2)))); - assert(Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).contains( - Interval!Date(Date(1999, 1, 12), Date(2011, 9, 17)))); - assert(!Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).contains( - Interval!Date(Date(1998, 2, 28), Date(2013, 5, 1)))); - - assert(!Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).contains(PosInfInterval!Date(Date(1999, 5, 4)))); - - assert(!Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).contains(NegInfInterval!Date(Date(1996, 5, 4)))); -} - -// Test Interval's isBefore(time point). -@safe unittest -{ - import std.datetime.date; - - auto interval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); - - assertThrown!DateTimeException(Interval!Date(Date(2010, 7, 4), dur!"days"(0)).isBefore(Date(2010, 7, 4))); - - assert(!interval.isBefore(Date(2009, 7, 3))); - assert(!interval.isBefore(Date(2010, 7, 3))); - assert(!interval.isBefore(Date(2010, 7, 4))); - assert(!interval.isBefore(Date(2010, 7, 5))); - assert(!interval.isBefore(Date(2011, 7, 1))); - assert(!interval.isBefore(Date(2012, 1, 6))); - assert(interval.isBefore(Date(2012, 1, 7))); - assert(interval.isBefore(Date(2012, 1, 8))); - assert(interval.isBefore(Date(2013, 1, 7))); - - const cdate = Date(2010, 7, 6); - const cInterval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); - immutable iInterval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); - assert(!interval.isBefore(cdate)); - assert(!cInterval.isBefore(cdate)); - assert(!iInterval.isBefore(cdate)); - - // Verify Examples. - assert(!Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).isBefore(Date(1994, 12, 24))); - assert(!Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).isBefore(Date(2000, 1, 5))); - assert(Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).isBefore(Date(2012, 3, 1))); -} - -// Test Interval's isBefore(Interval). -@safe unittest -{ - import std.datetime.date; - - auto interval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); - - assertThrown!DateTimeException(interval.isBefore(Interval!Date(Date(2010, 7, 4), dur!"days"(0)))); - assertThrown!DateTimeException(Interval!Date(Date(2010, 7, 4), dur!"days"(0)).isBefore(interval)); - assertThrown!DateTimeException(Interval!Date(Date(2010, 7, 4), dur!"days"(0)).isBefore( - Interval!Date(Date(2010, 7, 4), dur!"days"(0)))); - - assert(!interval.isBefore(interval)); - assert(!interval.isBefore(Interval!Date(Date(2010, 7, 1), Date(2010, 7, 3)))); - assert(!interval.isBefore(Interval!Date(Date(2010, 7, 1), Date(2013, 7, 3)))); - assert(!interval.isBefore(Interval!Date(Date(2010, 7, 3), Date(2010, 7, 4)))); - assert(!interval.isBefore(Interval!Date(Date(2010, 7, 3), Date(2010, 7, 5)))); - assert(!interval.isBefore(Interval!Date(Date(2010, 7, 3), Date(2012, 1, 7)))); - assert(!interval.isBefore(Interval!Date(Date(2010, 7, 3), Date(2012, 1, 8)))); - assert(!interval.isBefore(Interval!Date(Date(2010, 7, 5), Date(2012, 1, 6)))); - assert(!interval.isBefore(Interval!Date(Date(2010, 7, 5), Date(2012, 1, 7)))); - assert(!interval.isBefore(Interval!Date(Date(2012, 1, 6), Date(2012, 1, 7)))); - assert(!interval.isBefore(Interval!Date(Date(2012, 1, 6), Date(2012, 1, 8)))); - assert(interval.isBefore(Interval!Date(Date(2012, 1, 7), Date(2012, 1, 8)))); - assert(interval.isBefore(Interval!Date(Date(2012, 1, 8), Date(2012, 1, 9)))); - - assert(Interval!Date(Date(2010, 7, 1), Date(2010, 7, 3)).isBefore(interval)); - assert(!Interval!Date(Date(2010, 7, 1), Date(2013, 7, 3)).isBefore(interval)); - assert(Interval!Date(Date(2010, 7, 3), Date(2010, 7, 4)).isBefore(interval)); - assert(!Interval!Date(Date(2010, 7, 3), Date(2010, 7, 5)).isBefore(interval)); - assert(!Interval!Date(Date(2010, 7, 3), Date(2012, 1, 7)).isBefore(interval)); - assert(!Interval!Date(Date(2010, 7, 3), Date(2012, 1, 8)).isBefore(interval)); - assert(!Interval!Date(Date(2010, 7, 5), Date(2012, 1, 6)).isBefore(interval)); - assert(!Interval!Date(Date(2010, 7, 5), Date(2012, 1, 7)).isBefore(interval)); - assert(!Interval!Date(Date(2012, 1, 6), Date(2012, 1, 7)).isBefore(interval)); - assert(!Interval!Date(Date(2012, 1, 6), Date(2012, 1, 8)).isBefore(interval)); - assert(!Interval!Date(Date(2012, 1, 7), Date(2012, 1, 8)).isBefore(interval)); - assert(!Interval!Date(Date(2012, 1, 8), Date(2012, 1, 9)).isBefore(interval)); - - assert(!interval.isBefore(PosInfInterval!Date(Date(2010, 7, 3)))); - assert(!interval.isBefore(PosInfInterval!Date(Date(2010, 7, 4)))); - assert(!interval.isBefore(PosInfInterval!Date(Date(2010, 7, 5)))); - assert(!interval.isBefore(PosInfInterval!Date(Date(2012, 1, 6)))); - assert(interval.isBefore(PosInfInterval!Date(Date(2012, 1, 7)))); - assert(interval.isBefore(PosInfInterval!Date(Date(2012, 1, 8)))); - - assert(!interval.isBefore(NegInfInterval!Date(Date(2010, 7, 3)))); - assert(!interval.isBefore(NegInfInterval!Date(Date(2010, 7, 4)))); - assert(!interval.isBefore(NegInfInterval!Date(Date(2010, 7, 5)))); - assert(!interval.isBefore(NegInfInterval!Date(Date(2012, 1, 6)))); - assert(!interval.isBefore(NegInfInterval!Date(Date(2012, 1, 7)))); - assert(!interval.isBefore(NegInfInterval!Date(Date(2012, 1, 8)))); - - const cInterval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); - immutable iInterval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); - auto posInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); - const cPosInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); - immutable iPosInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); - auto negInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); - const cNegInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); - immutable iNegInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); - assert(!interval.isBefore(interval)); - assert(!interval.isBefore(cInterval)); - assert(!interval.isBefore(iInterval)); - assert(!interval.isBefore(posInfInterval)); - assert(!interval.isBefore(cPosInfInterval)); - assert(!interval.isBefore(iPosInfInterval)); - assert(!interval.isBefore(negInfInterval)); - assert(!interval.isBefore(cNegInfInterval)); - assert(!interval.isBefore(iNegInfInterval)); - assert(!cInterval.isBefore(interval)); - assert(!cInterval.isBefore(cInterval)); - assert(!cInterval.isBefore(iInterval)); - assert(!cInterval.isBefore(posInfInterval)); - assert(!cInterval.isBefore(cPosInfInterval)); - assert(!cInterval.isBefore(iPosInfInterval)); - assert(!cInterval.isBefore(negInfInterval)); - assert(!cInterval.isBefore(cNegInfInterval)); - assert(!cInterval.isBefore(iNegInfInterval)); - assert(!iInterval.isBefore(interval)); - assert(!iInterval.isBefore(cInterval)); - assert(!iInterval.isBefore(iInterval)); - assert(!iInterval.isBefore(posInfInterval)); - assert(!iInterval.isBefore(cPosInfInterval)); - assert(!iInterval.isBefore(iPosInfInterval)); - assert(!iInterval.isBefore(negInfInterval)); - assert(!iInterval.isBefore(cNegInfInterval)); - assert(!iInterval.isBefore(iNegInfInterval)); - - // Verify Examples. - assert(!Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).isBefore( - Interval!Date(Date(1990, 7, 6), Date(2000, 8, 2)))); - assert(!Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).isBefore( - Interval!Date(Date(1999, 1, 12), Date(2011, 9, 17)))); - assert(Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).isBefore( - Interval!Date(Date(2012, 3, 1), Date(2013, 5, 1)))); - - assert(!Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).isBefore(PosInfInterval!Date(Date(1999, 5, 4)))); - assert(Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).isBefore(PosInfInterval!Date(Date(2013, 3, 7)))); - - assert(!Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).isBefore(NegInfInterval!Date(Date(1996, 5, 4)))); -} - -// Test Interval's isAfter(time point). -@safe unittest -{ - import std.datetime.date; - - auto interval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); - - assertThrown!DateTimeException(Interval!Date(Date(2010, 7, 4), dur!"days"(0)).isAfter(Date(2010, 7, 4))); - - assert(interval.isAfter(Date(2009, 7, 4))); - assert(interval.isAfter(Date(2010, 7, 3))); - assert(!interval.isAfter(Date(2010, 7, 4))); - assert(!interval.isAfter(Date(2010, 7, 5))); - assert(!interval.isAfter(Date(2011, 7, 1))); - assert(!interval.isAfter(Date(2012, 1, 6))); - assert(!interval.isAfter(Date(2012, 1, 7))); - assert(!interval.isAfter(Date(2012, 1, 8))); - assert(!interval.isAfter(Date(2013, 1, 7))); - - const cdate = Date(2010, 7, 6); - const cInterval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); - immutable iInterval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); - assert(!interval.isAfter(cdate)); - assert(!cInterval.isAfter(cdate)); - assert(!iInterval.isAfter(cdate)); - - // Verify Examples. - assert(Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).isAfter(Date(1994, 12, 24))); - assert(!Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).isAfter(Date(2000, 1, 5))); - assert(!Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).isAfter(Date(2012, 3, 1))); -} - -// Test Interval's isAfter(Interval). -@safe unittest -{ - import std.datetime.date; - - auto interval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); - - assertThrown!DateTimeException(interval.isAfter(Interval!Date(Date(2010, 7, 4), dur!"days"(0)))); - assertThrown!DateTimeException(Interval!Date(Date(2010, 7, 4), dur!"days"(0)).isAfter(interval)); - assertThrown!DateTimeException(Interval!Date(Date(2010, 7, 4), dur!"days"(0)).isAfter( - Interval!Date(Date(2010, 7, 4), dur!"days"(0)))); - - assert(!interval.isAfter(interval)); - assert(interval.isAfter(Interval!Date(Date(2010, 7, 1), Date(2010, 7, 3)))); - assert(!interval.isAfter(Interval!Date(Date(2010, 7, 1), Date(2013, 7, 3)))); - assert(interval.isAfter(Interval!Date(Date(2010, 7, 3), Date(2010, 7, 4)))); - assert(!interval.isAfter(Interval!Date(Date(2010, 7, 3), Date(2010, 7, 5)))); - assert(!interval.isAfter(Interval!Date(Date(2010, 7, 3), Date(2012, 1, 7)))); - assert(!interval.isAfter(Interval!Date(Date(2010, 7, 3), Date(2012, 1, 8)))); - assert(!interval.isAfter(Interval!Date(Date(2010, 7, 5), Date(2012, 1, 6)))); - assert(!interval.isAfter(Interval!Date(Date(2010, 7, 5), Date(2012, 1, 7)))); - assert(!interval.isAfter(Interval!Date(Date(2012, 1, 6), Date(2012, 1, 7)))); - assert(!interval.isAfter(Interval!Date(Date(2012, 1, 6), Date(2012, 1, 8)))); - assert(!interval.isAfter(Interval!Date(Date(2012, 1, 7), Date(2012, 1, 8)))); - assert(!interval.isAfter(Interval!Date(Date(2012, 1, 8), Date(2012, 1, 9)))); - - assert(!Interval!Date(Date(2010, 7, 1), Date(2010, 7, 3)).isAfter(interval)); - assert(!Interval!Date(Date(2010, 7, 1), Date(2013, 7, 3)).isAfter(interval)); - assert(!Interval!Date(Date(2010, 7, 3), Date(2010, 7, 4)).isAfter(interval)); - assert(!Interval!Date(Date(2010, 7, 3), Date(2010, 7, 5)).isAfter(interval)); - assert(!Interval!Date(Date(2010, 7, 3), Date(2012, 1, 7)).isAfter(interval)); - assert(!Interval!Date(Date(2010, 7, 3), Date(2012, 1, 8)).isAfter(interval)); - assert(!Interval!Date(Date(2010, 7, 5), Date(2012, 1, 6)).isAfter(interval)); - assert(!Interval!Date(Date(2010, 7, 5), Date(2012, 1, 7)).isAfter(interval)); - assert(!Interval!Date(Date(2012, 1, 6), Date(2012, 1, 7)).isAfter(interval)); - assert(!Interval!Date(Date(2012, 1, 6), Date(2012, 1, 8)).isAfter(interval)); - assert(Interval!Date(Date(2012, 1, 7), Date(2012, 1, 8)).isAfter(interval)); - assert(Interval!Date(Date(2012, 1, 8), Date(2012, 1, 9)).isAfter(interval)); - - assert(!interval.isAfter(PosInfInterval!Date(Date(2010, 7, 3)))); - assert(!interval.isAfter(PosInfInterval!Date(Date(2010, 7, 4)))); - assert(!interval.isAfter(PosInfInterval!Date(Date(2010, 7, 5)))); - assert(!interval.isAfter(PosInfInterval!Date(Date(2012, 1, 6)))); - assert(!interval.isAfter(PosInfInterval!Date(Date(2012, 1, 7)))); - assert(!interval.isAfter(PosInfInterval!Date(Date(2012, 1, 8)))); - - assert(interval.isAfter(NegInfInterval!Date(Date(2010, 7, 3)))); - assert(interval.isAfter(NegInfInterval!Date(Date(2010, 7, 4)))); - assert(!interval.isAfter(NegInfInterval!Date(Date(2010, 7, 5)))); - assert(!interval.isAfter(NegInfInterval!Date(Date(2012, 1, 6)))); - assert(!interval.isAfter(NegInfInterval!Date(Date(2012, 1, 7)))); - assert(!interval.isAfter(NegInfInterval!Date(Date(2012, 1, 8)))); - - const cInterval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); - immutable iInterval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); - auto posInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); - const cPosInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); - immutable iPosInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); - auto negInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); - const cNegInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); - immutable iNegInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); - assert(!interval.isAfter(interval)); - assert(!interval.isAfter(cInterval)); - assert(!interval.isAfter(iInterval)); - assert(!interval.isAfter(posInfInterval)); - assert(!interval.isAfter(cPosInfInterval)); - assert(!interval.isAfter(iPosInfInterval)); - assert(!interval.isAfter(negInfInterval)); - assert(!interval.isAfter(cNegInfInterval)); - assert(!interval.isAfter(iNegInfInterval)); - assert(!cInterval.isAfter(interval)); - assert(!cInterval.isAfter(cInterval)); - assert(!cInterval.isAfter(iInterval)); - assert(!cInterval.isAfter(posInfInterval)); - assert(!cInterval.isAfter(cPosInfInterval)); - assert(!cInterval.isAfter(iPosInfInterval)); - assert(!cInterval.isAfter(negInfInterval)); - assert(!cInterval.isAfter(cNegInfInterval)); - assert(!cInterval.isAfter(iNegInfInterval)); - assert(!iInterval.isAfter(interval)); - assert(!iInterval.isAfter(cInterval)); - assert(!iInterval.isAfter(iInterval)); - assert(!iInterval.isAfter(posInfInterval)); - assert(!iInterval.isAfter(cPosInfInterval)); - assert(!iInterval.isAfter(iPosInfInterval)); - assert(!iInterval.isAfter(negInfInterval)); - assert(!iInterval.isAfter(cNegInfInterval)); - assert(!iInterval.isAfter(iNegInfInterval)); - - // Verify Examples. - assert(!Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).isAfter( - Interval!Date(Date(1990, 7, 6), Date(2000, 8, 2)))); - assert(!Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).isAfter( - Interval!Date(Date(1999, 1, 12), Date(2011, 9, 17)))); - assert(Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).isAfter( - Interval!Date(Date(1989, 3, 1), Date(1996, 1, 2)))); - - assert(!Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).isAfter(PosInfInterval!Date(Date(1999, 5, 4)))); - - assert(Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).isAfter(NegInfInterval!Date(Date(1996, 1, 2)))); -} - -// Test Interval's intersects(). -@safe unittest -{ - import std.datetime.date; - - auto interval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); - - assertThrown!DateTimeException(interval.intersects(Interval!Date(Date(2010, 7, 4), dur!"days"(0)))); - assertThrown!DateTimeException(Interval!Date(Date(2010, 7, 4), dur!"days"(0)).intersects(interval)); - assertThrown!DateTimeException(Interval!Date(Date(2010, 7, 4), dur!"days"(0)).intersects( - Interval!Date(Date(2010, 7, 4), dur!"days"(0)))); - - assert(interval.intersects(interval)); - assert(!interval.intersects(Interval!Date(Date(2010, 7, 1), Date(2010, 7, 3)))); - assert(interval.intersects(Interval!Date(Date(2010, 7, 1), Date(2013, 7, 3)))); - assert(!interval.intersects(Interval!Date(Date(2010, 7, 3), Date(2010, 7, 4)))); - assert(interval.intersects(Interval!Date(Date(2010, 7, 3), Date(2010, 7, 5)))); - assert(interval.intersects(Interval!Date(Date(2010, 7, 3), Date(2012, 1, 7)))); - assert(interval.intersects(Interval!Date(Date(2010, 7, 3), Date(2012, 1, 8)))); - assert(interval.intersects(Interval!Date(Date(2010, 7, 5), Date(2012, 1, 6)))); - assert(interval.intersects(Interval!Date(Date(2010, 7, 5), Date(2012, 1, 7)))); - assert(interval.intersects(Interval!Date(Date(2012, 1, 6), Date(2012, 1, 7)))); - assert(interval.intersects(Interval!Date(Date(2012, 1, 6), Date(2012, 1, 8)))); - assert(!interval.intersects(Interval!Date(Date(2012, 1, 7), Date(2012, 1, 8)))); - assert(!interval.intersects(Interval!Date(Date(2012, 1, 8), Date(2012, 1, 9)))); - - assert(!Interval!Date(Date(2010, 7, 1), Date(2010, 7, 3)).intersects(interval)); - assert(Interval!Date(Date(2010, 7, 1), Date(2013, 7, 3)).intersects(interval)); - assert(!Interval!Date(Date(2010, 7, 3), Date(2010, 7, 4)).intersects(interval)); - assert(Interval!Date(Date(2010, 7, 3), Date(2010, 7, 5)).intersects(interval)); - assert(Interval!Date(Date(2010, 7, 3), Date(2012, 1, 7)).intersects(interval)); - assert(Interval!Date(Date(2010, 7, 3), Date(2012, 1, 8)).intersects(interval)); - assert(Interval!Date(Date(2010, 7, 5), Date(2012, 1, 6)).intersects(interval)); - assert(Interval!Date(Date(2010, 7, 5), Date(2012, 1, 7)).intersects(interval)); - assert(Interval!Date(Date(2012, 1, 6), Date(2012, 1, 7)).intersects(interval)); - assert(Interval!Date(Date(2012, 1, 6), Date(2012, 1, 8)).intersects(interval)); - assert(!Interval!Date(Date(2012, 1, 7), Date(2012, 1, 8)).intersects(interval)); - assert(!Interval!Date(Date(2012, 1, 8), Date(2012, 1, 9)).intersects(interval)); - - assert(interval.intersects(PosInfInterval!Date(Date(2010, 7, 3)))); - assert(interval.intersects(PosInfInterval!Date(Date(2010, 7, 4)))); - assert(interval.intersects(PosInfInterval!Date(Date(2010, 7, 5)))); - assert(interval.intersects(PosInfInterval!Date(Date(2012, 1, 6)))); - assert(!interval.intersects(PosInfInterval!Date(Date(2012, 1, 7)))); - assert(!interval.intersects(PosInfInterval!Date(Date(2012, 1, 8)))); - - assert(!interval.intersects(NegInfInterval!Date(Date(2010, 7, 3)))); - assert(!interval.intersects(NegInfInterval!Date(Date(2010, 7, 4)))); - assert(interval.intersects(NegInfInterval!Date(Date(2010, 7, 5)))); - assert(interval.intersects(NegInfInterval!Date(Date(2012, 1, 6)))); - assert(interval.intersects(NegInfInterval!Date(Date(2012, 1, 7)))); - assert(interval.intersects(NegInfInterval!Date(Date(2012, 1, 8)))); - - const cInterval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); - immutable iInterval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); - auto posInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); - const cPosInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); - immutable iPosInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); - auto negInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); - const cNegInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); - immutable iNegInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); - assert(interval.intersects(interval)); - assert(interval.intersects(cInterval)); - assert(interval.intersects(iInterval)); - assert(interval.intersects(posInfInterval)); - assert(interval.intersects(cPosInfInterval)); - assert(interval.intersects(iPosInfInterval)); - assert(interval.intersects(negInfInterval)); - assert(interval.intersects(cNegInfInterval)); - assert(interval.intersects(iNegInfInterval)); - assert(cInterval.intersects(interval)); - assert(cInterval.intersects(cInterval)); - assert(cInterval.intersects(iInterval)); - assert(cInterval.intersects(posInfInterval)); - assert(cInterval.intersects(cPosInfInterval)); - assert(cInterval.intersects(iPosInfInterval)); - assert(cInterval.intersects(negInfInterval)); - assert(cInterval.intersects(cNegInfInterval)); - assert(cInterval.intersects(iNegInfInterval)); - assert(iInterval.intersects(interval)); - assert(iInterval.intersects(cInterval)); - assert(iInterval.intersects(iInterval)); - assert(iInterval.intersects(posInfInterval)); - assert(iInterval.intersects(cPosInfInterval)); - assert(iInterval.intersects(iPosInfInterval)); - assert(iInterval.intersects(negInfInterval)); - assert(iInterval.intersects(cNegInfInterval)); - assert(iInterval.intersects(iNegInfInterval)); - - // Verify Examples. - assert(Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).intersects( - Interval!Date(Date(1990, 7, 6), Date(2000, 8, 2)))); - assert(Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).intersects( - Interval!Date(Date(1999, 1, 12), Date(2011, 9, 17)))); - assert(!Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).intersects( - Interval!Date(Date(1989, 3, 1), Date(1996, 1, 2)))); - - assert(Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).intersects(PosInfInterval!Date(Date(1999, 5, 4)))); - assert(!Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).intersects(PosInfInterval!Date(Date(2012, 3, 1)))); - - assert(!Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).intersects(NegInfInterval!Date(Date(1996, 1, 2)))); - assert(Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).intersects(NegInfInterval!Date(Date(2000, 1, 2)))); -} - -// Test Interval's intersection(). -@safe unittest -{ - import std.datetime.date; - - auto interval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); - - assertThrown!DateTimeException(interval.intersection(Interval!Date(Date(2010, 7, 4), dur!"days"(0)))); - assertThrown!DateTimeException(Interval!Date(Date(2010, 7, 4), dur!"days"(0)).intersection(interval)); - assertThrown!DateTimeException(Interval!Date(Date(2010, 7, 4), dur!"days"(0)).intersection( - Interval!Date(Date(2010, 7, 4), dur!"days"(0)))); - - assertThrown!DateTimeException(interval.intersection(Interval!Date(Date(2010, 7, 1), Date(2010, 7, 3)))); - assertThrown!DateTimeException(interval.intersection(Interval!Date(Date(2010, 7, 3), Date(2010, 7, 4)))); - assertThrown!DateTimeException(interval.intersection(Interval!Date(Date(2012, 1, 7), Date(2012, 1, 8)))); - assertThrown!DateTimeException(interval.intersection(Interval!Date(Date(2012, 1, 8), Date(2012, 1, 9)))); - - assertThrown!DateTimeException(Interval!Date(Date(2010, 7, 1), Date(2010, 7, 3)).intersection(interval)); - assertThrown!DateTimeException(Interval!Date(Date(2010, 7, 3), Date(2010, 7, 4)).intersection(interval)); - assertThrown!DateTimeException(Interval!Date(Date(2012, 1, 7), Date(2012, 1, 8)).intersection(interval)); - assertThrown!DateTimeException(Interval!Date(Date(2012, 1, 8), Date(2012, 1, 9)).intersection(interval)); - - assertThrown!DateTimeException(interval.intersection(PosInfInterval!Date(Date(2012, 1, 7)))); - assertThrown!DateTimeException(interval.intersection(PosInfInterval!Date(Date(2012, 1, 8)))); - - assertThrown!DateTimeException(interval.intersection(NegInfInterval!Date(Date(2010, 7, 3)))); - assertThrown!DateTimeException(interval.intersection(NegInfInterval!Date(Date(2010, 7, 4)))); - - assert(interval.intersection(interval) == interval); - assert(interval.intersection(Interval!Date(Date(2010, 7, 1), Date(2013, 7, 3))) == - Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7))); - assert(interval.intersection(Interval!Date(Date(2010, 7, 3), Date(2010, 7, 5))) == - Interval!Date(Date(2010, 7, 4), Date(2010, 7, 5))); - assert(interval.intersection(Interval!Date(Date(2010, 7, 3), Date(2012, 1, 7))) == - Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7))); - assert(interval.intersection(Interval!Date(Date(2010, 7, 3), Date(2012, 1, 8))) == - Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7))); - assert(interval.intersection(Interval!Date(Date(2010, 7, 5), Date(2012, 1, 6))) == - Interval!Date(Date(2010, 7, 5), Date(2012, 1, 6))); - assert(interval.intersection(Interval!Date(Date(2010, 7, 5), Date(2012, 1, 7))) == - Interval!Date(Date(2010, 7, 5), Date(2012, 1, 7))); - assert(interval.intersection(Interval!Date(Date(2012, 1, 6), Date(2012, 1, 7))) == - Interval!Date(Date(2012, 1, 6), Date(2012, 1, 7))); - assert(interval.intersection(Interval!Date(Date(2012, 1, 6), Date(2012, 1, 8))) == - Interval!Date(Date(2012, 1, 6), Date(2012, 1, 7))); - - assert(Interval!Date(Date(2010, 7, 1), Date(2013, 7, 3)).intersection(interval) == - Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7))); - assert(Interval!Date(Date(2010, 7, 3), Date(2010, 7, 5)).intersection(interval) == - Interval!Date(Date(2010, 7, 4), Date(2010, 7, 5))); - assert(Interval!Date(Date(2010, 7, 3), Date(2012, 1, 7)).intersection(interval) == - Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7))); - assert(Interval!Date(Date(2010, 7, 3), Date(2012, 1, 8)).intersection(interval) == - Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7))); - assert(Interval!Date(Date(2010, 7, 5), Date(2012, 1, 6)).intersection(interval) == - Interval!Date(Date(2010, 7, 5), Date(2012, 1, 6))); - assert(Interval!Date(Date(2010, 7, 5), Date(2012, 1, 7)).intersection(interval) == - Interval!Date(Date(2010, 7, 5), Date(2012, 1, 7))); - assert(Interval!Date(Date(2012, 1, 6), Date(2012, 1, 7)).intersection(interval) == - Interval!Date(Date(2012, 1, 6), Date(2012, 1, 7))); - assert(Interval!Date(Date(2012, 1, 6), Date(2012, 1, 8)).intersection(interval) == - Interval!Date(Date(2012, 1, 6), Date(2012, 1, 7))); - - assert(interval.intersection(PosInfInterval!Date(Date(2010, 7, 3))) == - Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7))); - assert(interval.intersection(PosInfInterval!Date(Date(2010, 7, 4))) == - Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7))); - assert(interval.intersection(PosInfInterval!Date(Date(2010, 7, 5))) == - Interval!Date(Date(2010, 7, 5), Date(2012, 1, 7))); - assert(interval.intersection(PosInfInterval!Date(Date(2012, 1, 6))) == - Interval!Date(Date(2012, 1, 6), Date(2012, 1, 7))); - - assert(interval.intersection(NegInfInterval!Date(Date(2010, 7, 5))) == - Interval!Date(Date(2010, 7, 4), Date(2010, 7, 5))); - assert(interval.intersection(NegInfInterval!Date(Date(2012, 1, 6))) == - Interval!Date(Date(2010, 7, 4), Date(2012, 1, 6))); - assert(interval.intersection(NegInfInterval!Date(Date(2012, 1, 7))) == - Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7))); - assert(interval.intersection(NegInfInterval!Date(Date(2012, 1, 8))) == - Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7))); - - const cInterval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); - immutable iInterval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); - auto posInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); - const cPosInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); - immutable iPosInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); - auto negInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); - const cNegInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); - immutable iNegInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); - assert(!interval.intersection(interval).empty); - assert(!interval.intersection(cInterval).empty); - assert(!interval.intersection(iInterval).empty); - assert(!interval.intersection(posInfInterval).empty); - assert(!interval.intersection(cPosInfInterval).empty); - assert(!interval.intersection(iPosInfInterval).empty); - assert(!interval.intersection(negInfInterval).empty); - assert(!interval.intersection(cNegInfInterval).empty); - assert(!interval.intersection(iNegInfInterval).empty); - assert(!cInterval.intersection(interval).empty); - assert(!cInterval.intersection(cInterval).empty); - assert(!cInterval.intersection(iInterval).empty); - assert(!cInterval.intersection(posInfInterval).empty); - assert(!cInterval.intersection(cPosInfInterval).empty); - assert(!cInterval.intersection(iPosInfInterval).empty); - assert(!cInterval.intersection(negInfInterval).empty); - assert(!cInterval.intersection(cNegInfInterval).empty); - assert(!cInterval.intersection(iNegInfInterval).empty); - assert(!iInterval.intersection(interval).empty); - assert(!iInterval.intersection(cInterval).empty); - assert(!iInterval.intersection(iInterval).empty); - assert(!iInterval.intersection(posInfInterval).empty); - assert(!iInterval.intersection(cPosInfInterval).empty); - assert(!iInterval.intersection(iPosInfInterval).empty); - assert(!iInterval.intersection(negInfInterval).empty); - assert(!iInterval.intersection(cNegInfInterval).empty); - assert(!iInterval.intersection(iNegInfInterval).empty); - - // Verify Examples. - assert(Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).intersection( - Interval!Date(Date(1990, 7, 6), Date(2000, 8, 2))) == - Interval!Date(Date(1996, 1 , 2), Date(2000, 8, 2))); - assert(Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).intersection( - Interval!Date(Date(1999, 1, 12),Date(2011, 9, 17))) == - Interval!Date(Date(1999, 1 , 12), Date(2011, 9, 17))); - - assert(Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).intersection( - PosInfInterval!Date(Date(1990, 7, 6))) == - Interval!Date(Date(1996, 1 , 2), Date(2012, 3, 1))); - assert(Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).intersection( - PosInfInterval!Date(Date(1999, 1, 12))) == - Interval!Date(Date(1999, 1 , 12), Date(2012, 3, 1))); - - assert(Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).intersection( - NegInfInterval!Date(Date(1999, 7, 6))) == - Interval!Date(Date(1996, 1 , 2), Date(1999, 7, 6))); - assert(Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).intersection( - NegInfInterval!Date(Date(2013, 1, 12))) == - Interval!Date(Date(1996, 1 , 2), Date(2012, 3, 1))); -} - -// Test Interval's isAdjacent(). -@safe unittest -{ - import std.datetime.date; - - auto interval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); - - static void testInterval(scope const Interval!Date interval1, scope const Interval!Date interval2) - { - interval1.isAdjacent(interval2); - } - - assertThrown!DateTimeException(testInterval(interval, Interval!Date(Date(2010, 7, 4), dur!"days"(0)))); - assertThrown!DateTimeException(testInterval(Interval!Date(Date(2010, 7, 4), dur!"days"(0)), interval)); - assertThrown!DateTimeException(testInterval(Interval!Date(Date(2010, 7, 4), dur!"days"(0)), - Interval!Date(Date(2010, 7, 4), dur!"days"(0)))); - - assert(!interval.isAdjacent(interval)); - assert(!interval.isAdjacent(Interval!Date(Date(2010, 7, 1), Date(2010, 7, 3)))); - assert(!interval.isAdjacent(Interval!Date(Date(2010, 7, 1), Date(2013, 7, 3)))); - assert(interval.isAdjacent(Interval!Date(Date(2010, 7, 3), Date(2010, 7, 4)))); - assert(!interval.isAdjacent(Interval!Date(Date(2010, 7, 3), Date(2010, 7, 5)))); - assert(!interval.isAdjacent(Interval!Date(Date(2010, 7, 3), Date(2012, 1, 7)))); - assert(!interval.isAdjacent(Interval!Date(Date(2010, 7, 3), Date(2012, 1, 8)))); - assert(!interval.isAdjacent(Interval!Date(Date(2010, 7, 5), Date(2012, 1, 6)))); - assert(!interval.isAdjacent(Interval!Date(Date(2010, 7, 5), Date(2012, 1, 7)))); - assert(!interval.isAdjacent(Interval!Date(Date(2012, 1, 6), Date(2012, 1, 7)))); - assert(!interval.isAdjacent(Interval!Date(Date(2012, 1, 6), Date(2012, 1, 8)))); - assert(interval.isAdjacent(Interval!Date(Date(2012, 1, 7), Date(2012, 1, 8)))); - assert(!interval.isAdjacent(Interval!Date(Date(2012, 1, 8), Date(2012, 1, 9)))); - - assert(!Interval!Date(Date(2010, 7, 1), Date(2010, 7, 3)).isAdjacent(interval)); - assert(!Interval!Date(Date(2010, 7, 1), Date(2013, 7, 3)).isAdjacent(interval)); - assert(Interval!Date(Date(2010, 7, 3), Date(2010, 7, 4)).isAdjacent(interval)); - assert(!Interval!Date(Date(2010, 7, 3), Date(2010, 7, 5)).isAdjacent(interval)); - assert(!Interval!Date(Date(2010, 7, 3), Date(2012, 1, 7)).isAdjacent(interval)); - assert(!Interval!Date(Date(2010, 7, 3), Date(2012, 1, 8)).isAdjacent(interval)); - assert(!Interval!Date(Date(2010, 7, 5), Date(2012, 1, 6)).isAdjacent(interval)); - assert(!Interval!Date(Date(2010, 7, 5), Date(2012, 1, 7)).isAdjacent(interval)); - assert(!Interval!Date(Date(2012, 1, 6), Date(2012, 1, 7)).isAdjacent(interval)); - assert(!Interval!Date(Date(2012, 1, 6), Date(2012, 1, 8)).isAdjacent(interval)); - assert(Interval!Date(Date(2012, 1, 7), Date(2012, 1, 8)).isAdjacent(interval)); - assert(!Interval!Date(Date(2012, 1, 8), Date(2012, 1, 9)).isAdjacent(interval)); - - assert(!interval.isAdjacent(PosInfInterval!Date(Date(2010, 7, 3)))); - assert(!interval.isAdjacent(PosInfInterval!Date(Date(2010, 7, 4)))); - assert(!interval.isAdjacent(PosInfInterval!Date(Date(2010, 7, 5)))); - assert(!interval.isAdjacent(PosInfInterval!Date(Date(2012, 1, 6)))); - assert(interval.isAdjacent(PosInfInterval!Date(Date(2012, 1, 7)))); - assert(!interval.isAdjacent(PosInfInterval!Date(Date(2012, 1, 8)))); - - assert(!interval.isAdjacent(NegInfInterval!Date(Date(2010, 7, 3)))); - assert(interval.isAdjacent(NegInfInterval!Date(Date(2010, 7, 4)))); - assert(!interval.isAdjacent(NegInfInterval!Date(Date(2010, 7, 5)))); - assert(!interval.isAdjacent(NegInfInterval!Date(Date(2012, 1, 6)))); - assert(!interval.isAdjacent(NegInfInterval!Date(Date(2012, 1, 7)))); - assert(!interval.isAdjacent(NegInfInterval!Date(Date(2012, 1, 8)))); - - const cInterval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); - immutable iInterval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); - auto posInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); - const cPosInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); - immutable iPosInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); - auto negInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); - const cNegInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); - immutable iNegInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); - assert(!interval.isAdjacent(interval)); - assert(!interval.isAdjacent(cInterval)); - assert(!interval.isAdjacent(iInterval)); - assert(!interval.isAdjacent(posInfInterval)); - assert(!interval.isAdjacent(cPosInfInterval)); - assert(!interval.isAdjacent(iPosInfInterval)); - assert(!interval.isAdjacent(negInfInterval)); - assert(!interval.isAdjacent(cNegInfInterval)); - assert(!interval.isAdjacent(iNegInfInterval)); - assert(!cInterval.isAdjacent(interval)); - assert(!cInterval.isAdjacent(cInterval)); - assert(!cInterval.isAdjacent(iInterval)); - assert(!cInterval.isAdjacent(posInfInterval)); - assert(!cInterval.isAdjacent(cPosInfInterval)); - assert(!cInterval.isAdjacent(iPosInfInterval)); - assert(!cInterval.isAdjacent(negInfInterval)); - assert(!cInterval.isAdjacent(cNegInfInterval)); - assert(!cInterval.isAdjacent(iNegInfInterval)); - assert(!iInterval.isAdjacent(interval)); - assert(!iInterval.isAdjacent(cInterval)); - assert(!iInterval.isAdjacent(iInterval)); - assert(!iInterval.isAdjacent(posInfInterval)); - assert(!iInterval.isAdjacent(cPosInfInterval)); - assert(!iInterval.isAdjacent(iPosInfInterval)); - assert(!iInterval.isAdjacent(negInfInterval)); - assert(!iInterval.isAdjacent(cNegInfInterval)); - assert(!iInterval.isAdjacent(iNegInfInterval)); - - // Verify Examples. - assert(Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).isAdjacent( - Interval!Date(Date(1990, 7, 6), Date(1996, 1, 2)))); - assert(Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).isAdjacent( - Interval!Date(Date(2012, 3, 1), Date(2013, 9, 17)))); - assert(!Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).isAdjacent( - Interval!Date(Date(1989, 3, 1), Date(2012, 3, 1)))); - - assert(!Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).isAdjacent(PosInfInterval!Date(Date(1999, 5, 4)))); - assert(Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).isAdjacent(PosInfInterval!Date(Date(2012, 3, 1)))); - - assert(Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).isAdjacent(NegInfInterval!Date(Date(1996, 1, 2)))); - assert(!Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)) .isAdjacent(NegInfInterval!Date(Date(2000, 1, 2)))); -} - -// Test Interval's merge(). -@safe unittest -{ - import std.datetime.date; - - auto interval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); - - static void testInterval(I)(scope const Interval!Date interval1, scope const I interval2) - { - interval1.merge(interval2); - } - - assertThrown!DateTimeException(testInterval(interval, Interval!Date(Date(2010, 7, 4), dur!"days"(0)))); - assertThrown!DateTimeException(testInterval(Interval!Date(Date(2010, 7, 4), dur!"days"(0)), interval)); - assertThrown!DateTimeException(testInterval(Interval!Date(Date(2010, 7, 4), dur!"days"(0)), - Interval!Date(Date(2010, 7, 4), dur!"days"(0)))); - - assertThrown!DateTimeException(testInterval(interval, Interval!Date(Date(2010, 7, 1), Date(2010, 7, 3)))); - assertThrown!DateTimeException(testInterval(interval, Interval!Date(Date(2012, 1, 8), Date(2012, 1, 9)))); - - assertThrown!DateTimeException(testInterval(Interval!Date(Date(2010, 7, 1), Date(2010, 7, 3)), interval)); - assertThrown!DateTimeException(testInterval(Interval!Date(Date(2012, 1, 8), Date(2012, 1, 9)), interval)); - - assertThrown!DateTimeException(testInterval(interval, PosInfInterval!Date(Date(2012, 1, 8)))); - - assertThrown!DateTimeException(testInterval(interval, NegInfInterval!Date(Date(2010, 7, 3)))); - - assert(interval.merge(interval) == interval); - assert(interval.merge(Interval!Date(Date(2010, 7, 1), Date(2013, 7, 3))) == - Interval!Date(Date(2010, 7, 1), Date(2013, 7, 3))); - assert(interval.merge(Interval!Date(Date(2010, 7, 3), Date(2010, 7, 4))) == - Interval!Date(Date(2010, 7, 3), Date(2012, 1, 7))); - assert(interval.merge(Interval!Date(Date(2010, 7, 3), Date(2010, 7, 5))) == - Interval!Date(Date(2010, 7, 3), Date(2012, 1, 7))); - assert(interval.merge(Interval!Date(Date(2010, 7, 3), Date(2012, 1, 7))) == - Interval!Date(Date(2010, 7, 3), Date(2012, 1, 7))); - assert(interval.merge(Interval!Date(Date(2010, 7, 3), Date(2012, 1, 8))) == - Interval!Date(Date(2010, 7, 3), Date(2012, 1, 8))); - assert(interval.merge(Interval!Date(Date(2010, 7, 5), Date(2012, 1, 6))) == - Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7))); - assert(interval.merge(Interval!Date(Date(2010, 7, 5), Date(2012, 1, 7))) == - Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7))); - assert(interval.merge(Interval!Date(Date(2012, 1, 6), Date(2012, 1, 7))) == - Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7))); - assert(interval.merge(Interval!Date(Date(2012, 1, 6), Date(2012, 1, 8))) == - Interval!Date(Date(2010, 7, 4), Date(2012, 1, 8))); - assert(interval.merge(Interval!Date(Date(2012, 1, 7), Date(2012, 1, 8))) == - Interval!Date(Date(2010, 7, 4), Date(2012, 1, 8))); - - assert(Interval!Date(Date(2010, 7, 1), Date(2013, 7, 3)).merge(interval) == - Interval!Date(Date(2010, 7, 1), Date(2013, 7, 3))); - assert(Interval!Date(Date(2010, 7, 3), Date(2010, 7, 4)).merge(interval) == - Interval!Date(Date(2010, 7, 3), Date(2012, 1, 7))); - assert(Interval!Date(Date(2010, 7, 3), Date(2010, 7, 5)).merge(interval) == - Interval!Date(Date(2010, 7, 3), Date(2012, 1, 7))); - assert(Interval!Date(Date(2010, 7, 3), Date(2012, 1, 7)).merge(interval) == - Interval!Date(Date(2010, 7, 3), Date(2012, 1, 7))); - assert(Interval!Date(Date(2010, 7, 3), Date(2012, 1, 8)).merge(interval) == - Interval!Date(Date(2010, 7, 3), Date(2012, 1, 8))); - assert(Interval!Date(Date(2010, 7, 5), Date(2012, 1, 6)).merge(interval) == - Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7))); - assert(Interval!Date(Date(2010, 7, 5), Date(2012, 1, 7)).merge(interval) == - Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7))); - assert(Interval!Date(Date(2012, 1, 6), Date(2012, 1, 7)).merge(interval) == - Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7))); - assert(Interval!Date(Date(2012, 1, 6), Date(2012, 1, 8)).merge(interval) == - Interval!Date(Date(2010, 7, 4), Date(2012, 1, 8))); - assert(Interval!Date(Date(2012, 1, 7), Date(2012, 1, 8)).merge(interval) == - Interval!Date(Date(2010, 7, 4), Date(2012, 1, 8))); - - assert(interval.merge(PosInfInterval!Date(Date(2010, 7, 3))) == - PosInfInterval!Date(Date(2010, 7, 3))); - assert(interval.merge(PosInfInterval!Date(Date(2010, 7, 4))) == - PosInfInterval!Date(Date(2010, 7, 4))); - assert(interval.merge(PosInfInterval!Date(Date(2010, 7, 5))) == - PosInfInterval!Date(Date(2010, 7, 4))); - assert(interval.merge(PosInfInterval!Date(Date(2012, 1, 6))) == - PosInfInterval!Date(Date(2010, 7, 4))); - assert(interval.merge(PosInfInterval!Date(Date(2012, 1, 7))) == - PosInfInterval!Date(Date(2010, 7, 4))); - - assert(interval.merge(NegInfInterval!Date(Date(2010, 7, 4))) == - NegInfInterval!Date(Date(2012, 1, 7))); - assert(interval.merge(NegInfInterval!Date(Date(2010, 7, 5))) == - NegInfInterval!Date(Date(2012, 1, 7))); - assert(interval.merge(NegInfInterval!Date(Date(2012, 1, 6))) == - NegInfInterval!Date(Date(2012, 1, 7))); - assert(interval.merge(NegInfInterval!Date(Date(2012, 1, 7))) == - NegInfInterval!Date(Date(2012, 1, 7))); - assert(interval.merge(NegInfInterval!Date(Date(2012, 1, 8))) == - NegInfInterval!Date(Date(2012, 1, 8))); - - const cInterval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); - immutable iInterval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); - auto posInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); - const cPosInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); - immutable iPosInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); - auto negInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); - const cNegInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); - immutable iNegInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); - assert(!interval.merge(interval).empty); - assert(!interval.merge(cInterval).empty); - assert(!interval.merge(iInterval).empty); - assert(!interval.merge(posInfInterval).empty); - assert(!interval.merge(cPosInfInterval).empty); - assert(!interval.merge(iPosInfInterval).empty); - assert(!interval.merge(negInfInterval).empty); - assert(!interval.merge(cNegInfInterval).empty); - assert(!interval.merge(iNegInfInterval).empty); - assert(!cInterval.merge(interval).empty); - assert(!cInterval.merge(cInterval).empty); - assert(!cInterval.merge(iInterval).empty); - assert(!cInterval.merge(posInfInterval).empty); - assert(!cInterval.merge(cPosInfInterval).empty); - assert(!cInterval.merge(iPosInfInterval).empty); - assert(!cInterval.merge(negInfInterval).empty); - assert(!cInterval.merge(cNegInfInterval).empty); - assert(!cInterval.merge(iNegInfInterval).empty); - assert(!iInterval.merge(interval).empty); - assert(!iInterval.merge(cInterval).empty); - assert(!iInterval.merge(iInterval).empty); - assert(!iInterval.merge(posInfInterval).empty); - assert(!iInterval.merge(cPosInfInterval).empty); - assert(!iInterval.merge(iPosInfInterval).empty); - assert(!iInterval.merge(negInfInterval).empty); - assert(!iInterval.merge(cNegInfInterval).empty); - assert(!iInterval.merge(iNegInfInterval).empty); - - // Verify Examples. - assert(Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).merge(Interval!Date(Date(1990, 7, 6), Date(2000, 8, 2))) == - Interval!Date(Date(1990, 7 , 6), Date(2012, 3, 1))); - assert(Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).merge(Interval!Date(Date(2012, 3, 1), Date(2013, 5, 7))) == - Interval!Date(Date(1996, 1 , 2), Date(2013, 5, 7))); - - assert(Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).merge(PosInfInterval!Date(Date(1990, 7, 6))) == - PosInfInterval!Date(Date(1990, 7 , 6))); - assert(Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).merge(PosInfInterval!Date(Date(2012, 3, 1))) == - PosInfInterval!Date(Date(1996, 1 , 2))); - - assert(Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).merge(NegInfInterval!Date(Date(1996, 1, 2))) == - NegInfInterval!Date(Date(2012, 3 , 1))); - assert(Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).merge(NegInfInterval!Date(Date(2013, 1, 12))) == - NegInfInterval!Date(Date(2013, 1 , 12))); -} - -// Test Interval's span(). -@safe unittest -{ - import std.datetime.date; - - auto interval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); - - static void testInterval(scope const Interval!Date interval1, scope const Interval!Date interval2) - { - interval1.span(interval2); - } - - assertThrown!DateTimeException(testInterval(interval, Interval!Date(Date(2010, 7, 4), dur!"days"(0)))); - assertThrown!DateTimeException(testInterval(Interval!Date(Date(2010, 7, 4), dur!"days"(0)),interval)); - assertThrown!DateTimeException(testInterval(Interval!Date(Date(2010, 7, 4), dur!"days"(0)), - Interval!Date(Date(2010, 7, 4), dur!"days"(0)))); - - assert(interval.span(interval) == interval); - assert(interval.span(Interval!Date(Date(2010, 7, 1), Date(2010, 7, 3))) == - Interval!Date(Date(2010, 7, 1), Date(2012, 1, 7))); - assert(interval.span(Interval!Date(Date(2010, 7, 1), Date(2013, 7, 3))) == - Interval!Date(Date(2010, 7, 1), Date(2013, 7, 3))); - assert(interval.span(Interval!Date(Date(2010, 7, 3), Date(2010, 7, 4))) == - Interval!Date(Date(2010, 7, 3), Date(2012, 1, 7))); - assert(interval.span(Interval!Date(Date(2010, 7, 3), Date(2010, 7, 5))) == - Interval!Date(Date(2010, 7, 3), Date(2012, 1, 7))); - assert(interval.span(Interval!Date(Date(2010, 7, 3), Date(2012, 1, 7))) == - Interval!Date(Date(2010, 7, 3), Date(2012, 1, 7))); - assert(interval.span(Interval!Date(Date(2010, 7, 3), Date(2012, 1, 8))) == - Interval!Date(Date(2010, 7, 3), Date(2012, 1, 8))); - assert(interval.span(Interval!Date(Date(2010, 7, 5), Date(2012, 1, 6))) == - Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7))); - assert(interval.span(Interval!Date(Date(2010, 7, 5), Date(2012, 1, 7))) == - Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7))); - assert(interval.span(Interval!Date(Date(2012, 1, 6), Date(2012, 1, 7))) == - Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7))); - assert(interval.span(Interval!Date(Date(2012, 1, 6), Date(2012, 1, 8))) == - Interval!Date(Date(2010, 7, 4), Date(2012, 1, 8))); - assert(interval.span(Interval!Date(Date(2012, 1, 7), Date(2012, 1, 8))) == - Interval!Date(Date(2010, 7, 4), Date(2012, 1, 8))); - assert(interval.span(Interval!Date(Date(2012, 1, 8), Date(2012, 1, 9))) == - Interval!Date(Date(2010, 7, 4), Date(2012, 1, 9))); - - assert(Interval!Date(Date(2010, 7, 1), Date(2010, 7, 3)).span(interval) == - Interval!Date(Date(2010, 7, 1), Date(2012, 1, 7))); - assert(Interval!Date(Date(2010, 7, 1), Date(2013, 7, 3)).span(interval) == - Interval!Date(Date(2010, 7, 1), Date(2013, 7, 3))); - assert(Interval!Date(Date(2010, 7, 3), Date(2010, 7, 4)).span(interval) == - Interval!Date(Date(2010, 7, 3), Date(2012, 1, 7))); - assert(Interval!Date(Date(2010, 7, 3), Date(2010, 7, 5)).span(interval) == - Interval!Date(Date(2010, 7, 3), Date(2012, 1, 7))); - assert(Interval!Date(Date(2010, 7, 3), Date(2012, 1, 7)).span(interval) == - Interval!Date(Date(2010, 7, 3), Date(2012, 1, 7))); - assert(Interval!Date(Date(2010, 7, 3), Date(2012, 1, 8)).span(interval) == - Interval!Date(Date(2010, 7, 3), Date(2012, 1, 8))); - assert(Interval!Date(Date(2010, 7, 5), Date(2012, 1, 6)).span(interval) == - Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7))); - assert(Interval!Date(Date(2010, 7, 5), Date(2012, 1, 7)).span(interval) == - Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7))); - assert(Interval!Date(Date(2012, 1, 6), Date(2012, 1, 7)).span(interval) == - Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7))); - assert(Interval!Date(Date(2012, 1, 6), Date(2012, 1, 8)).span(interval) == - Interval!Date(Date(2010, 7, 4), Date(2012, 1, 8))); - assert(Interval!Date(Date(2012, 1, 7), Date(2012, 1, 8)).span(interval) == - Interval!Date(Date(2010, 7, 4), Date(2012, 1, 8))); - assert(Interval!Date(Date(2012, 1, 8), Date(2012, 1, 9)).span(interval) == - Interval!Date(Date(2010, 7, 4), Date(2012, 1, 9))); - - assert(interval.span(PosInfInterval!Date(Date(2010, 7, 3))) == PosInfInterval!Date(Date(2010, 7, 3))); - assert(interval.span(PosInfInterval!Date(Date(2010, 7, 4))) == PosInfInterval!Date(Date(2010, 7, 4))); - assert(interval.span(PosInfInterval!Date(Date(2010, 7, 5))) == PosInfInterval!Date(Date(2010, 7, 4))); - assert(interval.span(PosInfInterval!Date(Date(2012, 1, 6))) == PosInfInterval!Date(Date(2010, 7, 4))); - assert(interval.span(PosInfInterval!Date(Date(2012, 1, 7))) == PosInfInterval!Date(Date(2010, 7, 4))); - assert(interval.span(PosInfInterval!Date(Date(2012, 1, 8))) == PosInfInterval!Date(Date(2010, 7, 4))); - - assert(interval.span(NegInfInterval!Date(Date(2010, 7, 3))) == NegInfInterval!Date(Date(2012, 1, 7))); - assert(interval.span(NegInfInterval!Date(Date(2010, 7, 4))) == NegInfInterval!Date(Date(2012, 1, 7))); - assert(interval.span(NegInfInterval!Date(Date(2010, 7, 5))) == NegInfInterval!Date(Date(2012, 1, 7))); - assert(interval.span(NegInfInterval!Date(Date(2012, 1, 6))) == NegInfInterval!Date(Date(2012, 1, 7))); - assert(interval.span(NegInfInterval!Date(Date(2012, 1, 7))) == NegInfInterval!Date(Date(2012, 1, 7))); - assert(interval.span(NegInfInterval!Date(Date(2012, 1, 8))) == NegInfInterval!Date(Date(2012, 1, 8))); - - const cInterval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); - immutable iInterval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); - auto posInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); - const cPosInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); - immutable iPosInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); - auto negInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); - const cNegInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); - immutable iNegInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); - assert(!interval.span(interval).empty); - assert(!interval.span(cInterval).empty); - assert(!interval.span(iInterval).empty); - assert(!interval.span(posInfInterval).empty); - assert(!interval.span(cPosInfInterval).empty); - assert(!interval.span(iPosInfInterval).empty); - assert(!interval.span(negInfInterval).empty); - assert(!interval.span(cNegInfInterval).empty); - assert(!interval.span(iNegInfInterval).empty); - assert(!cInterval.span(interval).empty); - assert(!cInterval.span(cInterval).empty); - assert(!cInterval.span(iInterval).empty); - assert(!cInterval.span(posInfInterval).empty); - assert(!cInterval.span(cPosInfInterval).empty); - assert(!cInterval.span(iPosInfInterval).empty); - assert(!cInterval.span(negInfInterval).empty); - assert(!cInterval.span(cNegInfInterval).empty); - assert(!cInterval.span(iNegInfInterval).empty); - assert(!iInterval.span(interval).empty); - assert(!iInterval.span(cInterval).empty); - assert(!iInterval.span(iInterval).empty); - assert(!iInterval.span(posInfInterval).empty); - assert(!iInterval.span(cPosInfInterval).empty); - assert(!iInterval.span(iPosInfInterval).empty); - assert(!iInterval.span(negInfInterval).empty); - assert(!iInterval.span(cNegInfInterval).empty); - assert(!iInterval.span(iNegInfInterval).empty); - - // Verify Examples. - assert(Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).span(Interval!Date(Date(1990, 7, 6), Date(1991, 1, 8))) == - Interval!Date(Date(1990, 7 , 6), Date(2012, 3, 1))); - assert(Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).span(Interval!Date(Date(2012, 3, 1), Date(2013, 5, 7))) == - Interval!Date(Date(1996, 1 , 2), Date(2013, 5, 7))); - - assert(Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).span(PosInfInterval!Date(Date(1990, 7, 6))) == - PosInfInterval!Date(Date(1990, 7 , 6))); - assert(Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).span(PosInfInterval!Date(Date(2050, 1, 1))) == - PosInfInterval!Date(Date(1996, 1 , 2))); - - assert(Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).span(NegInfInterval!Date(Date(1602, 5, 21))) == - NegInfInterval!Date(Date(2012, 3 , 1))); - assert(Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).span(NegInfInterval!Date(Date(2013, 1, 12))) == - NegInfInterval!Date(Date(2013, 1 , 12))); -} - -// Test Interval's shift(duration). -@safe unittest -{ - import std.datetime.date; - - auto interval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); - - static void testIntervalFail(Interval!Date interval, scope const Duration duration) - { - interval.shift(duration); - } - - assertThrown!DateTimeException(testIntervalFail(Interval!Date(Date(2010, 7, 4), dur!"days"(0)), dur!"days"(1))); - - static void testInterval(I)(I interval, scope const Duration duration, - scope const I expected, size_t line = __LINE__) - { - interval.shift(duration); - assert(interval == expected); - } - - testInterval(interval, dur!"days"(22), Interval!Date(Date(2010, 7, 26), Date(2012, 1, 29))); - testInterval(interval, dur!"days"(-22), Interval!Date(Date(2010, 6, 12), Date(2011, 12, 16))); - - const cInterval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); - immutable iInterval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); - static assert(!__traits(compiles, cInterval.shift(dur!"days"(5)))); - static assert(!__traits(compiles, iInterval.shift(dur!"days"(5)))); - - // Verify Examples. - auto interval1 = Interval!Date(Date(1996, 1, 2), Date(2012, 4, 5)); - auto interval2 = Interval!Date(Date(1996, 1, 2), Date(2012, 4, 5)); - - interval1.shift(dur!"days"(50)); - assert(interval1 == Interval!Date(Date(1996, 2, 21), Date(2012, 5, 25))); - - interval2.shift(dur!"days"(-50)); - assert(interval2 == Interval!Date(Date(1995, 11, 13), Date(2012, 2, 15))); -} - -// Test Interval's shift(int, int, AllowDayOverflow). -@safe unittest -{ - import std.datetime.date; - - { - auto interval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); - - static void testIntervalFail(Interval!Date interval, int years, int months) - { - interval.shift(years, months); - } - - assertThrown!DateTimeException(testIntervalFail(Interval!Date(Date(2010, 7, 4), dur!"days"(0)), 1, 0)); - - static void testInterval(I)(I interval, int years, int months, AllowDayOverflow allow, - in I expected, size_t line = __LINE__) - { - interval.shift(years, months, allow); - assert(interval == expected); - } - - testInterval(interval, 5, 0, AllowDayOverflow.yes, Interval!Date(Date(2015, 7, 4), Date(2017, 1, 7))); - testInterval(interval, -5, 0, AllowDayOverflow.yes, Interval!Date(Date(2005, 7, 4), Date(2007, 1, 7))); - - auto interval2 = Interval!Date(Date(2000, 1, 29), Date(2010, 5, 31)); - - testInterval(interval2, 1, 1, AllowDayOverflow.yes, Interval!Date(Date(2001, 3, 1), Date(2011, 7, 1))); - testInterval(interval2, 1, -1, AllowDayOverflow.yes, Interval!Date(Date(2000, 12, 29), Date(2011, 5, 1))); - testInterval(interval2, -1, -1, AllowDayOverflow.yes, Interval!Date(Date(1998, 12, 29), Date(2009, 5, 1))); - testInterval(interval2, -1, 1, AllowDayOverflow.yes, Interval!Date(Date(1999, 3, 1), Date(2009, 7, 1))); - - testInterval(interval2, 1, 1, AllowDayOverflow.no, Interval!Date(Date(2001, 2, 28), Date(2011, 6, 30))); - testInterval(interval2, 1, -1, AllowDayOverflow.no, Interval!Date(Date(2000, 12, 29), Date(2011, 4, 30))); - testInterval(interval2, -1, -1, AllowDayOverflow.no, Interval!Date(Date(1998, 12, 29), Date(2009, 4, 30))); - testInterval(interval2, -1, 1, AllowDayOverflow.no, Interval!Date(Date(1999, 2, 28), Date(2009, 6, 30))); - } - - const cInterval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); - immutable iInterval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); - static assert(!__traits(compiles, cInterval.shift(5))); - static assert(!__traits(compiles, iInterval.shift(5))); - - // Verify Examples. - auto interval1 = Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)); - auto interval2 = Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)); - - interval1.shift(2); - assert(interval1 == Interval!Date(Date(1998, 1, 2), Date(2014, 3, 1))); - - interval2.shift(-2); - assert(interval2 == Interval!Date(Date(1994, 1, 2), Date(2010, 3, 1))); -} - -// Test Interval's expand(Duration). -@safe unittest -{ - import std.datetime.date; - - auto interval = Interval!Date(Date(2000, 7, 4), Date(2012, 1, 7)); - - static void testIntervalFail(I)(I interval, scope const Duration duration) - { - interval.expand(duration); - } - - assertThrown!DateTimeException(testIntervalFail(Interval!Date(Date(2010, 7, 4), dur!"days"(0)), dur!"days"(1))); - assertThrown!DateTimeException(testIntervalFail(Interval!Date(Date(2010, 7, 4), Date(2010, 7, 5)), dur!"days"(-5))); - - static void testInterval(I)(I interval, scope const Duration duration, - scope const I expected, size_t line = __LINE__) - { - interval.expand(duration); - assert(interval == expected); - } - - testInterval(interval, dur!"days"(22), Interval!Date(Date(2000, 6, 12), Date(2012, 1, 29))); - testInterval(interval, dur!"days"(-22), Interval!Date(Date(2000, 7, 26), Date(2011, 12, 16))); - - const cInterval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); - immutable iInterval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); - static assert(!__traits(compiles, cInterval.expand(dur!"days"(5)))); - static assert(!__traits(compiles, iInterval.expand(dur!"days"(5)))); - - // Verify Examples. - auto interval1 = Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)); - auto interval2 = Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)); - - interval1.expand(dur!"days"(2)); - assert(interval1 == Interval!Date(Date(1995, 12, 31), Date(2012, 3, 3))); - - interval2.expand(dur!"days"(-2)); - assert(interval2 == Interval!Date(Date(1996, 1, 4), Date(2012, 2, 28))); -} - -// Test Interval's expand(int, int, AllowDayOverflow, Direction) -@safe unittest -{ - import std.datetime.date; - - { - auto interval = Interval!Date(Date(2000, 7, 4), Date(2012, 1, 7)); - - static void testIntervalFail(Interval!Date interval, int years, int months) - { - interval.expand(years, months); - } - - assertThrown!DateTimeException(testIntervalFail(Interval!Date(Date(2010, 7, 4), dur!"days"(0)), 1, 0)); - assertThrown!DateTimeException(testIntervalFail(Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)), -5, 0)); - - static void testInterval(I)(I interval, int years, int months, AllowDayOverflow allow, Direction dir, - in I expected, size_t line = __LINE__) - { - interval.expand(years, months, allow, dir); - assert(interval == expected); - } - - testInterval(interval, 5, 0, AllowDayOverflow.yes, Direction.both, - Interval!Date(Date(1995, 7, 4), Date(2017, 1, 7))); - testInterval(interval, -5, 0, AllowDayOverflow.yes, Direction.both, - Interval!Date(Date(2005, 7, 4), Date(2007, 1, 7))); - - testInterval(interval, 5, 0, AllowDayOverflow.yes, Direction.fwd, - Interval!Date(Date(2000, 7, 4), Date(2017, 1, 7))); - testInterval(interval, -5, 0, AllowDayOverflow.yes, Direction.fwd, - Interval!Date(Date(2000, 7, 4), Date(2007, 1, 7))); - - testInterval(interval, 5, 0, AllowDayOverflow.yes, Direction.bwd, - Interval!Date(Date(1995, 7, 4), Date(2012, 1, 7))); - testInterval(interval, -5, 0, AllowDayOverflow.yes, Direction.bwd, - Interval!Date(Date(2005, 7, 4), Date(2012, 1, 7))); - - auto interval2 = Interval!Date(Date(2000, 1, 29), Date(2010, 5, 31)); - - testInterval(interval2, 1, 1, AllowDayOverflow.yes, Direction.both, - Interval!Date(Date(1998, 12, 29), Date(2011, 7, 1))); - testInterval(interval2, 1, -1, AllowDayOverflow.yes, Direction.both, - Interval!Date(Date(1999, 3, 1), Date(2011, 5, 1))); - testInterval(interval2, -1, -1, AllowDayOverflow.yes, Direction.both, - Interval!Date(Date(2001, 3, 1), Date(2009, 5, 1))); - testInterval(interval2, -1, 1, AllowDayOverflow.yes, Direction.both, - Interval!Date(Date(2000, 12, 29), Date(2009, 7, 1))); - - testInterval(interval2, 1, 1, AllowDayOverflow.no, Direction.both, - Interval!Date(Date(1998, 12, 29), Date(2011, 6, 30))); - testInterval(interval2, 1, -1, AllowDayOverflow.no, Direction.both, - Interval!Date(Date(1999, 2, 28), Date(2011, 4, 30))); - testInterval(interval2, -1, -1, AllowDayOverflow.no, Direction.both, - Interval!Date(Date(2001, 2, 28), Date(2009, 4, 30))); - testInterval(interval2, -1, 1, AllowDayOverflow.no, Direction.both, - Interval!Date(Date(2000, 12, 29), Date(2009, 6, 30))); - - testInterval(interval2, 1, 1, AllowDayOverflow.yes, Direction.fwd, - Interval!Date(Date(2000, 1, 29), Date(2011, 7, 1))); - testInterval(interval2, 1, -1, AllowDayOverflow.yes, Direction.fwd, - Interval!Date(Date(2000, 1, 29), Date(2011, 5, 1))); - testInterval(interval2, -1, -1, AllowDayOverflow.yes, Direction.fwd, - Interval!Date(Date(2000, 1, 29), Date(2009, 5, 1))); - testInterval(interval2, -1, 1, AllowDayOverflow.yes, Direction.fwd, - Interval!Date(Date(2000, 1, 29), Date(2009, 7, 1))); - - testInterval(interval2, 1, 1, AllowDayOverflow.no, Direction.fwd, - Interval!Date(Date(2000, 1, 29), Date(2011, 6, 30))); - testInterval(interval2, 1, -1, AllowDayOverflow.no, Direction.fwd, - Interval!Date(Date(2000, 1, 29), Date(2011, 4, 30))); - testInterval(interval2, -1, -1, AllowDayOverflow.no, Direction.fwd, - Interval!Date(Date(2000, 1, 29), Date(2009, 4, 30))); - testInterval(interval2, -1, 1, AllowDayOverflow.no, Direction.fwd, - Interval!Date(Date(2000, 1, 29), Date(2009, 6, 30))); - - testInterval(interval2, 1, 1, AllowDayOverflow.yes, Direction.bwd, - Interval!Date(Date(1998, 12, 29), Date(2010, 5, 31))); - testInterval(interval2, 1, -1, AllowDayOverflow.yes, Direction.bwd, - Interval!Date(Date(1999, 3, 1), Date(2010, 5, 31))); - testInterval(interval2, -1, -1, AllowDayOverflow.yes, Direction.bwd, - Interval!Date(Date(2001, 3, 1), Date(2010, 5, 31))); - testInterval(interval2, -1, 1, AllowDayOverflow.yes, Direction.bwd, - Interval!Date(Date(2000, 12, 29), Date(2010, 5, 31))); - - testInterval(interval2, 1, 1, AllowDayOverflow.no, Direction.bwd, - Interval!Date(Date(1998, 12, 29), Date(2010, 5, 31))); - testInterval(interval2, 1, -1, AllowDayOverflow.no, Direction.bwd, - Interval!Date(Date(1999, 2, 28), Date(2010, 5, 31))); - testInterval(interval2, -1, -1, AllowDayOverflow.no, Direction.bwd, - Interval!Date(Date(2001, 2, 28), Date(2010, 5, 31))); - testInterval(interval2, -1, 1, AllowDayOverflow.no, Direction.bwd, - Interval!Date(Date(2000, 12, 29), Date(2010, 5, 31))); - } - - const cInterval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); - immutable iInterval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); - static assert(!__traits(compiles, cInterval.expand(5))); - static assert(!__traits(compiles, iInterval.expand(5))); - - // Verify Examples. - auto interval1 = Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)); - auto interval2 = Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)); - - interval1.expand(2); - assert(interval1 == Interval!Date(Date(1994, 1, 2), Date(2014, 3, 1))); - - interval2.expand(-2); - assert(interval2 == Interval!Date(Date(1998, 1, 2), Date(2010, 3, 1))); -} - -// Test Interval's fwdRange. -@system unittest -{ - import std.datetime.date; - - { - auto interval = Interval!Date(Date(2010, 9, 19), Date(2010, 9, 21)); - - static void testInterval1(Interval!Date interval) - { - interval.fwdRange(everyDayOfWeek!Date(DayOfWeek.fri)); - } - - assertThrown!DateTimeException(testInterval1(Interval!Date(Date(2010, 7, 4), dur!"days"(0)))); - - static void testInterval2(Interval!Date interval) - { - interval.fwdRange(everyDayOfWeek!(Date, Direction.bwd)(DayOfWeek.fri)).popFront(); - } - - assertThrown!DateTimeException(testInterval2(interval)); - - assert(!interval.fwdRange(everyDayOfWeek!Date(DayOfWeek.fri)).empty); - assert(interval.fwdRange(everyDayOfWeek!Date(DayOfWeek.fri), PopFirst.yes).empty); - - assert(Interval!Date(Date(2010, 9, 12), Date(2010, 10, 1)).fwdRange(everyDayOfWeek!Date(DayOfWeek.fri)).front == - Date(2010, 9, 12)); - - assert(Interval!Date(Date(2010, 9, 12), Date(2010, 10, 1)).fwdRange( - everyDayOfWeek!Date(DayOfWeek.fri), PopFirst.yes).front == Date(2010, 9, 17)); - } - - // Verify Examples. - { - auto interval = Interval!Date(Date(2010, 9, 1), Date(2010, 9, 9)); - auto func = delegate (scope const Date date) - { - if ((date.day & 1) == 0) - return date + dur!"days"(2); - return date + dur!"days"(1); - }; - auto range = interval.fwdRange(func); - - // An odd day. Using PopFirst.yes would have made this Date(2010, 9, 2). - assert(range.front == Date(2010, 9, 1)); - - range.popFront(); - assert(range.front == Date(2010, 9, 2)); - - range.popFront(); - assert(range.front == Date(2010, 9, 4)); - - range.popFront(); - assert(range.front == Date(2010, 9, 6)); - - range.popFront(); - assert(range.front == Date(2010, 9, 8)); - - range.popFront(); - assert(range.empty); - } - - const cInterval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); - immutable iInterval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); - assert(!cInterval.fwdRange(everyDayOfWeek!Date(DayOfWeek.fri)).empty); - assert(!iInterval.fwdRange(everyDayOfWeek!Date(DayOfWeek.fri)).empty); -} - -// Test Interval's bwdRange. -@system unittest -{ - import std.datetime.date; - - { - auto interval = Interval!Date(Date(2010, 9, 19), Date(2010, 9, 21)); - - static void testInterval1(Interval!Date interval) - { - interval.bwdRange(everyDayOfWeek!(Date, Direction.bwd)(DayOfWeek.fri)); - } - - assertThrown!DateTimeException(testInterval1(Interval!Date(Date(2010, 7, 4), dur!"days"(0)))); - - static void testInterval2(Interval!Date interval) - { - interval.bwdRange(everyDayOfWeek!(Date, Direction.fwd)(DayOfWeek.fri)).popFront(); - } - - assertThrown!DateTimeException(testInterval2(interval)); - - assert(!interval.bwdRange(everyDayOfWeek!(Date, Direction.bwd)(DayOfWeek.fri)).empty); - assert(interval.bwdRange(everyDayOfWeek!(Date, Direction.bwd)(DayOfWeek.fri), PopFirst.yes).empty); - - assert(Interval!Date(Date(2010, 9, 19), Date(2010, 10, 1)).bwdRange( - everyDayOfWeek!(Date, Direction.bwd)(DayOfWeek.fri)).front == Date(2010, 10, 1)); - - assert(Interval!Date(Date(2010, 9, 19), Date(2010, 10, 1)).bwdRange( - everyDayOfWeek!(Date, Direction.bwd)(DayOfWeek.fri), PopFirst.yes).front == Date(2010, 9, 24)); - } - - // Verify Examples. - { - auto interval = Interval!Date(Date(2010, 9, 1), Date(2010, 9, 9)); - auto func = delegate (scope const Date date) - { - if ((date.day & 1) == 0) - return date - dur!"days"(2); - return date - dur!"days"(1); - }; - auto range = interval.bwdRange(func); - - // An odd day. Using PopFirst.yes would have made this Date(2010, 9, 8). - assert(range.front == Date(2010, 9, 9)); - - range.popFront(); - assert(range.front == Date(2010, 9, 8)); - - range.popFront(); - assert(range.front == Date(2010, 9, 6)); - - range.popFront(); - assert(range.front == Date(2010, 9, 4)); - - range.popFront(); - assert(range.front == Date(2010, 9, 2)); - - range.popFront(); - assert(range.empty); - } - - const cInterval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); - immutable iInterval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); - assert(!cInterval.bwdRange(everyDayOfWeek!Date(DayOfWeek.fri)).empty); - assert(!iInterval.bwdRange(everyDayOfWeek!Date(DayOfWeek.fri)).empty); -} - -// Test Interval's toString(). -@safe unittest -{ - import std.datetime.date; - - assert(Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)).toString() == "[2010-Jul-04 - 2012-Jan-07)"); - - const cInterval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); - immutable iInterval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); - assert(cInterval.toString()); - assert(iInterval.toString()); -} - - -/++ - Represents an interval of time which has positive infinity as its end point. - - Any ranges which iterate over a `PosInfInterval` are infinite. So, the - main purpose of using `PosInfInterval` is to create an infinite range - which starts at a fixed point in time and goes to positive infinity. - +/ -struct PosInfInterval(TP) -{ -public: - - /++ - Params: - begin = The time point which begins the interval. - - Example: --------------------- -auto interval = PosInfInterval!Date(Date(1996, 1, 2)); --------------------- - +/ - this(scope const TP begin) pure nothrow - { - _begin = cast(TP) begin; - } - - - /++ - Params: - rhs = The `PosInfInterval` to assign to this one. - +/ - ref PosInfInterval opAssign(const ref PosInfInterval rhs) pure nothrow - { - _begin = cast(TP) rhs._begin; - return this; - } - - - /++ - Params: - rhs = The `PosInfInterval` to assign to this one. - +/ - ref PosInfInterval opAssign(PosInfInterval rhs) pure nothrow - { - _begin = cast(TP) rhs._begin; - return this; - } - - - /++ - The starting point of the interval. It is included in the interval. - - Example: --------------------- -assert(PosInfInterval!Date(Date(1996, 1, 2)).begin == Date(1996, 1, 2)); --------------------- - +/ - @property TP begin() const pure nothrow - { - return cast(TP)_begin; - } - - - /++ - The starting point of the interval. It is included in the interval. - - Params: - timePoint = The time point to set `begin` to. - +/ - @property void begin(TP timePoint) pure nothrow - { - _begin = timePoint; - } - - - /++ - Whether the interval's length is 0. Always returns false. - - Example: --------------------- -assert(!PosInfInterval!Date(Date(1996, 1, 2)).empty); --------------------- - +/ - enum bool empty = false; - - - /++ - Whether the given time point is within this interval. - - Params: - timePoint = The time point to check for inclusion in this interval. - - Example: --------------------- -assert(!PosInfInterval!Date(Date(1996, 1, 2)).contains(Date(1994, 12, 24))); -assert(PosInfInterval!Date(Date(1996, 1, 2)).contains(Date(2000, 1, 5))); --------------------- - +/ - bool contains(TP timePoint) const pure nothrow - { - return timePoint >= _begin; - } - - - /++ - Whether the given interval is completely within this interval. - - Params: - interval = The interval to check for inclusion in this interval. - - Throws: - $(REF DateTimeException,std,datetime,date) if the given interval - is empty. - - Example: --------------------- -assert(!PosInfInterval!Date(Date(1996, 1, 2)).contains( - Interval!Date(Date(1990, 7, 6), Date(2000, 8, 2)))); - -assert(PosInfInterval!Date(Date(1996, 1, 2)).contains( - Interval!Date(Date(1999, 1, 12), Date(2011, 9, 17)))); - -assert(PosInfInterval!Date(Date(1996, 1, 2)).contains( - Interval!Date(Date(1998, 2, 28), Date(2013, 5, 1)))); --------------------- - +/ - bool contains(scope const Interval!TP interval) const pure - { - interval._enforceNotEmpty(); - return interval._begin >= _begin; - } - - - /++ - Whether the given interval is completely within this interval. - - Params: - interval = The interval to check for inclusion in this interval. - - Example: --------------------- -assert(PosInfInterval!Date(Date(1996, 1, 2)).contains( - PosInfInterval!Date(Date(1999, 5, 4)))); - -assert(!PosInfInterval!Date(Date(1996, 1, 2)).contains( - PosInfInterval!Date(Date(1995, 7, 2)))); --------------------- - +/ - bool contains(scope const PosInfInterval interval) const pure nothrow - { - return interval._begin >= _begin; - } - - - /++ - Whether the given interval is completely within this interval. - - Always returns false because an interval going to positive infinity - can never contain an interval beginning at negative infinity. - - Params: - interval = The interval to check for inclusion in this interval. - - Example: --------------------- -assert(!PosInfInterval!Date(Date(1996, 1, 2)).contains( - NegInfInterval!Date(Date(1996, 5, 4)))); --------------------- - +/ - bool contains(scope const NegInfInterval!TP interval) const pure nothrow - { - return false; - } - - - /++ - Whether this interval is before the given time point. - - Always returns false because an interval going to positive infinity - can never be before any time point. - - Params: - timePoint = The time point to check whether this interval is before - it. - - Example: --------------------- -assert(!PosInfInterval!Date(Date(1996, 1, 2)).isBefore(Date(1994, 12, 24))); -assert(!PosInfInterval!Date(Date(1996, 1, 2)).isBefore(Date(2000, 1, 5))); --------------------- - +/ - bool isBefore(scope const TP timePoint) const pure nothrow - { - return false; - } - - - /++ - Whether this interval is before the given interval and does not - intersect it. - - Always returns false (unless the given interval is empty) because an - interval going to positive infinity can never be before any other - interval. - - Params: - interval = The interval to check for against this interval. - - Throws: - $(REF DateTimeException,std,datetime,date) if the given interval - is empty. - - Example: --------------------- -assert(!PosInfInterval!Date(Date(1996, 1, 2)).isBefore( - Interval!Date(Date(1990, 7, 6), Date(2000, 8, 2)))); - -assert(!PosInfInterval!Date(Date(1996, 1, 2)).isBefore( - Interval!Date(Date(1999, 1, 12), Date(2011, 9, 17)))); --------------------- - +/ - bool isBefore(scope const Interval!TP interval) const pure - { - interval._enforceNotEmpty(); - return false; - } - - - /++ - Whether this interval is before the given interval and does not - intersect it. - - Always returns false because an interval going to positive infinity can - never be before any other interval. - - Params: - interval = The interval to check for against this interval. - - Example: --------------------- -assert(!PosInfInterval!Date(Date(1996, 1, 2)).isBefore( - PosInfInterval!Date(Date(1992, 5, 4)))); - -assert(!PosInfInterval!Date(Date(1996, 1, 2)).isBefore( - PosInfInterval!Date(Date(2013, 3, 7)))); --------------------- - +/ - bool isBefore(scope const PosInfInterval interval) const pure nothrow - { - return false; - } - - - /++ - Whether this interval is before the given interval and does not - intersect it. - - Always returns false because an interval going to positive infinity can - never be before any other interval. - - Params: - interval = The interval to check for against this interval. - - Example: --------------------- -assert(!PosInfInterval!Date(Date(1996, 1, 2)).isBefore( - NegInfInterval!Date(Date(1996, 5, 4)))); --------------------- - +/ - bool isBefore(scope const NegInfInterval!TP interval) const pure nothrow - { - return false; - } - - - /++ - Whether this interval is after the given time point. - - Params: - timePoint = The time point to check whether this interval is after - it. - - Example: --------------------- -assert(PosInfInterval!Date(Date(1996, 1, 2)).isAfter(Date(1994, 12, 24))); -assert(!PosInfInterval!Date(Date(1996, 1, 2)).isAfter(Date(2000, 1, 5))); --------------------- - +/ - bool isAfter(scope const TP timePoint) const pure nothrow - { - return timePoint < _begin; - } - - - /++ - Whether this interval is after the given interval and does not intersect - it. - - Params: - interval = The interval to check against this interval. - - Throws: - $(REF DateTimeException,std,datetime,date) if the given interval - is empty. - - Example: --------------------- -assert(!PosInfInterval!Date(Date(1996, 1, 2)).isAfter( - Interval!Date(Date(1990, 7, 6), Date(2000, 8, 2)))); - -assert(!PosInfInterval!Date(Date(1996, 1, 2)).isAfter( - Interval!Date(Date(1999, 1, 12), Date(2011, 9, 17)))); - -assert(PosInfInterval!Date(Date(1996, 1, 2)).isAfter( - Interval!Date(Date(1989, 3, 1), Date(1996, 1, 2)))); --------------------- - +/ - bool isAfter(scope const Interval!TP interval) const pure - { - interval._enforceNotEmpty(); - return _begin >= interval._end; - } - - - /++ - Whether this interval is after the given interval and does not intersect - it. - - Always returns false because an interval going to positive infinity can - never be after another interval going to positive infinity. - - Params: - interval = The interval to check against this interval. - - Example: --------------------- -assert(!PosInfInterval!Date(Date(1996, 1, 2)).isAfter( - PosInfInterval!Date(Date(1990, 1, 7)))); - -assert(!PosInfInterval!Date(Date(1996, 1, 2)).isAfter( - PosInfInterval!Date(Date(1999, 5, 4)))); --------------------- - +/ - bool isAfter(scope const PosInfInterval interval) const pure nothrow - { - return false; - } - - - /++ - Whether this interval is after the given interval and does not intersect - it. - - Params: - interval = The interval to check against this interval. - - Example: --------------------- -assert(PosInfInterval!Date(Date(1996, 1, 2)).isAfter( - NegInfInterval!Date(Date(1996, 1, 2)))); - -assert(!PosInfInterval!Date(Date(1996, 1, 2)).isAfter( - NegInfInterval!Date(Date(2000, 7, 1)))); --------------------- - +/ - bool isAfter(scope const NegInfInterval!TP interval) const pure nothrow - { - return _begin >= interval._end; - } - - - /++ - Whether the given interval overlaps this interval. - - Params: - interval = The interval to check for intersection with this interval. - - Throws: - $(REF DateTimeException,std,datetime,date) if the given interval - is empty. - - Example: --------------------- -assert(PosInfInterval!Date(Date(1996, 1, 2)).intersects( - Interval!Date(Date(1990, 7, 6), Date(2000, 8, 2)))); - -assert(PosInfInterval!Date(Date(1996, 1, 2)).intersects( - Interval!Date(Date(1999, 1, 12), Date(2011, 9, 17)))); - -assert(!PosInfInterval!Date(Date(1996, 1, 2)).intersects( - Interval!Date(Date(1989, 3, 1), Date(1996, 1, 2)))); --------------------- - +/ - bool intersects(scope const Interval!TP interval) const pure - { - interval._enforceNotEmpty(); - return interval._end > _begin; - } - - - /++ - Whether the given interval overlaps this interval. - - Always returns true because two intervals going to positive infinity - always overlap. - - Params: - interval = The interval to check for intersection with this - interval. - - Example: --------------------- -assert(PosInfInterval!Date(Date(1996, 1, 2)).intersects( - PosInfInterval!Date(Date(1990, 1, 7)))); - -assert(PosInfInterval!Date(Date(1996, 1, 2)).intersects( - PosInfInterval!Date(Date(1999, 5, 4)))); --------------------- - +/ - bool intersects(scope const PosInfInterval interval) const pure nothrow - { - return true; - } - - - /++ - Whether the given interval overlaps this interval. - - Params: - interval = The interval to check for intersection with this - interval. - - Example: --------------------- -assert(!PosInfInterval!Date(Date(1996, 1, 2)).intersects( - NegInfInterval!Date(Date(1996, 1, 2)))); - -assert(PosInfInterval!Date(Date(1996, 1, 2)).intersects( - NegInfInterval!Date(Date(2000, 7, 1)))); --------------------- - +/ - bool intersects(scope const NegInfInterval!TP interval) const pure nothrow - { - return _begin < interval._end; - } - - - /++ - Returns the intersection of two intervals - - Params: - interval = The interval to intersect with this interval. - - Throws: - $(REF DateTimeException,std,datetime,date) if the two intervals do - not intersect or if the given interval is empty. - - Example: --------------------- -assert(PosInfInterval!Date(Date(1996, 1, 2)).intersection( - Interval!Date(Date(1990, 7, 6), Date(2000, 8, 2))) == - Interval!Date(Date(1996, 1 , 2), Date(2000, 8, 2))); - -assert(PosInfInterval!Date(Date(1996, 1, 2)).intersection( - Interval!Date(Date(1999, 1, 12), Date(2011, 9, 17))) == - Interval!Date(Date(1999, 1 , 12), Date(2011, 9, 17))); --------------------- - +/ - Interval!TP intersection(scope const Interval!TP interval) const - { - import std.format : format; - - enforce(this.intersects(interval), - new DateTimeException(format("%s and %s do not intersect.", this, interval))); - - auto begin = _begin > interval._begin ? _begin : interval._begin; - - return Interval!TP(begin, interval._end); - } - - - /++ - Returns the intersection of two intervals - - Params: - interval = The interval to intersect with this interval. - - Example: --------------------- -assert(PosInfInterval!Date(Date(1996, 1, 2)).intersection( - PosInfInterval!Date(Date(1990, 7, 6))) == - PosInfInterval!Date(Date(1996, 1 , 2))); - -assert(PosInfInterval!Date(Date(1996, 1, 2)).intersection( - PosInfInterval!Date(Date(1999, 1, 12))) == - PosInfInterval!Date(Date(1999, 1 , 12))); --------------------- - +/ - PosInfInterval intersection(scope const PosInfInterval interval) const pure nothrow - { - return PosInfInterval(_begin < interval._begin ? interval._begin : _begin); - } - - - /++ - Returns the intersection of two intervals - - Params: - interval = The interval to intersect with this interval. - - Throws: - $(REF DateTimeException,std,datetime,date) if the two intervals do - not intersect. - - Example: --------------------- -assert(PosInfInterval!Date(Date(1996, 1, 2)).intersection( - NegInfInterval!Date(Date(1999, 7, 6))) == - Interval!Date(Date(1996, 1 , 2), Date(1999, 7, 6))); - -assert(PosInfInterval!Date(Date(1996, 1, 2)).intersection( - NegInfInterval!Date(Date(2013, 1, 12))) == - Interval!Date(Date(1996, 1 , 2), Date(2013, 1, 12))); --------------------- - +/ - Interval!TP intersection(scope const NegInfInterval!TP interval) const - { - import std.format : format; - - enforce(this.intersects(interval), - new DateTimeException(format("%s and %s do not intersect.", this, interval))); - - return Interval!TP(_begin, interval._end); - } - - - /++ - Whether the given interval is adjacent to this interval. - - Params: - interval = The interval to check whether its adjecent to this - interval. - - Throws: - $(REF DateTimeException,std,datetime,date) if the given interval - is empty. - - Example: --------------------- -assert(PosInfInterval!Date(Date(1996, 1, 2)).isAdjacent( - Interval!Date(Date(1989, 3, 1), Date(1996, 1, 2)))); - -assert(!PosInfInterval!Date(Date(1999, 1, 12)).isAdjacent( - Interval!Date(Date(1999, 1, 12), Date(2011, 9, 17)))); --------------------- - +/ - bool isAdjacent(scope const Interval!TP interval) const pure - { - interval._enforceNotEmpty(); - return _begin == interval._end; - } - - - /++ - Whether the given interval is adjacent to this interval. - - Always returns false because two intervals going to positive infinity - can never be adjacent to one another. - - Params: - interval = The interval to check whether its adjecent to this - interval. - - Example: --------------------- -assert(!PosInfInterval!Date(Date(1996, 1, 2)).isAdjacent( - PosInfInterval!Date(Date(1990, 1, 7)))); - -assert(!PosInfInterval!Date(Date(1996, 1, 2)).isAdjacent( - PosInfInterval!Date(Date(1996, 1, 2)))); --------------------- - +/ - bool isAdjacent(scope const PosInfInterval interval) const pure nothrow - { - return false; - } - - - /++ - Whether the given interval is adjacent to this interval. - - Params: - interval = The interval to check whether its adjecent to this - interval. - - Example: --------------------- -assert(PosInfInterval!Date(Date(1996, 1, 2)).isAdjacent( - NegInfInterval!Date(Date(1996, 1, 2)))); - -assert(!PosInfInterval!Date(Date(1996, 1, 2)).isAdjacent( - NegInfInterval!Date(Date(2000, 7, 1)))); --------------------- - +/ - bool isAdjacent(scope const NegInfInterval!TP interval) const pure nothrow - { - return _begin == interval._end; - } - - - /++ - Returns the union of two intervals - - Params: - interval = The interval to merge with this interval. - - Throws: - $(REF DateTimeException,std,datetime,date) if the two intervals do - not intersect and are not adjacent or if the given interval is - empty. - - Note: - There is no overload for `merge` which takes a - `NegInfInterval`, because an interval - going from negative infinity to positive infinity - is not possible. - - Example: --------------------- -assert(PosInfInterval!Date(Date(1996, 1, 2)).merge( - Interval!Date(Date(1990, 7, 6), Date(2000, 8, 2))) == - PosInfInterval!Date(Date(1990, 7 , 6))); - -assert(PosInfInterval!Date(Date(1996, 1, 2)).merge( - Interval!Date(Date(1999, 1, 12), Date(2011, 9, 17))) == - PosInfInterval!Date(Date(1996, 1 , 2))); --------------------- - +/ - PosInfInterval merge(scope const Interval!TP interval) const - { - import std.format : format; - - enforce(this.isAdjacent(interval) || this.intersects(interval), - new DateTimeException(format("%s and %s are not adjacent and do not intersect.", this, interval))); - - return PosInfInterval(_begin < interval._begin ? _begin : interval._begin); - } - - - /++ - Returns the union of two intervals - - Params: - interval = The interval to merge with this interval. - - Note: - There is no overload for `merge` which takes a - `NegInfInterval`, because an interval - going from negative infinity to positive infinity - is not possible. - - Example: --------------------- -assert(PosInfInterval!Date(Date(1996, 1, 2)).merge( - PosInfInterval!Date(Date(1990, 7, 6))) == - PosInfInterval!Date(Date(1990, 7 , 6))); - -assert(PosInfInterval!Date(Date(1996, 1, 2)).merge( - PosInfInterval!Date(Date(1999, 1, 12))) == - PosInfInterval!Date(Date(1996, 1 , 2))); --------------------- - +/ - PosInfInterval merge(scope const PosInfInterval interval) const pure nothrow - { - return PosInfInterval(_begin < interval._begin ? _begin : interval._begin); - } - - - /++ - Returns an interval that covers from the earliest time point of two - intervals up to (but not including) the latest time point of two - intervals. - - Params: - interval = The interval to create a span together with this - interval. - - Throws: - $(REF DateTimeException,std,datetime,date) if the given interval - is empty. - - Note: - There is no overload for `span` which takes a - `NegInfInterval`, because an interval - going from negative infinity to positive infinity - is not possible. - - Example: --------------------- -assert(PosInfInterval!Date(Date(1996, 1, 2)).span( - Interval!Date(Date(500, 8, 9), Date(1602, 1, 31))) == - PosInfInterval!Date(Date(500, 8, 9))); - -assert(PosInfInterval!Date(Date(1996, 1, 2)).span( - Interval!Date(Date(1990, 7, 6), Date(2000, 8, 2))) == - PosInfInterval!Date(Date(1990, 7 , 6))); - -assert(PosInfInterval!Date(Date(1996, 1, 2)).span( - Interval!Date(Date(1999, 1, 12), Date(2011, 9, 17))) == - PosInfInterval!Date(Date(1996, 1 , 2))); --------------------- - +/ - PosInfInterval span(scope const Interval!TP interval) const pure - { - interval._enforceNotEmpty(); - return PosInfInterval(_begin < interval._begin ? _begin : interval._begin); - } - - - /++ - Returns an interval that covers from the earliest time point of two - intervals up to (but not including) the latest time point of two - intervals. - - Params: - interval = The interval to create a span together with this - interval. - - Note: - There is no overload for `span` which takes a - `NegInfInterval`, because an interval - going from negative infinity to positive infinity - is not possible. - - Example: --------------------- -assert(PosInfInterval!Date(Date(1996, 1, 2)).span( - PosInfInterval!Date(Date(1990, 7, 6))) == - PosInfInterval!Date(Date(1990, 7 , 6))); - -assert(PosInfInterval!Date(Date(1996, 1, 2)).span( - PosInfInterval!Date(Date(1999, 1, 12))) == - PosInfInterval!Date(Date(1996, 1 , 2))); --------------------- - +/ - PosInfInterval span(scope const PosInfInterval interval) const pure nothrow - { - return PosInfInterval(_begin < interval._begin ? _begin : interval._begin); - } - - - /++ - Shifts the `begin` of this interval forward or backwards in time by - the given duration (a positive duration shifts the interval forward; a - negative duration shifts it backward). Effectively, it does - $(D begin += duration). - - Params: - duration = The duration to shift the interval by. - - Example: --------------------- -auto interval1 = PosInfInterval!Date(Date(1996, 1, 2)); -auto interval2 = PosInfInterval!Date(Date(1996, 1, 2)); - -interval1.shift(dur!"days"(50)); -assert(interval1 == PosInfInterval!Date(Date(1996, 2, 21))); - -interval2.shift(dur!"days"(-50)); -assert(interval2 == PosInfInterval!Date(Date(1995, 11, 13))); --------------------- - +/ - void shift(D)(D duration) pure nothrow - if (__traits(compiles, begin + duration)) - { - _begin += duration; - } - - - static if (__traits(compiles, begin.add!"months"(1)) && - __traits(compiles, begin.add!"years"(1))) - { - /++ - Shifts the `begin` of this interval forward or backwards in time - by the given number of years and/or months (a positive number of - years and months shifts the interval forward; a negative number - shifts it backward). It adds the years the given years and months to - `begin`. It effectively calls `add!"years"()` and then - `add!"months"()` on `begin` with the given number of years and - months. - - Params: - years = The number of years to shift the interval by. - months = The number of months to shift the interval by. - allowOverflow = Whether the days should be allowed to overflow - on `begin`, causing its month to increment. - - Throws: - $(REF DateTimeException,std,datetime,date) if this interval is - empty or if the resulting interval would be invalid. - - Example: --------------------- -auto interval1 = PosInfInterval!Date(Date(1996, 1, 2)); -auto interval2 = PosInfInterval!Date(Date(1996, 1, 2)); - -interval1.shift(dur!"days"(50)); -assert(interval1 == PosInfInterval!Date(Date(1996, 2, 21))); - -interval2.shift(dur!"days"(-50)); -assert(interval2 == PosInfInterval!Date(Date(1995, 11, 13))); --------------------- - +/ - void shift(T)(T years, T months = 0, AllowDayOverflow allowOverflow = AllowDayOverflow.yes) - if (isIntegral!T) - { - auto begin = _begin; - - begin.add!"years"(years, allowOverflow); - begin.add!"months"(months, allowOverflow); - - _begin = begin; - } - } - - - /++ - Expands the interval backwards in time. Effectively, it does - $(D begin -= duration). - - Params: - duration = The duration to expand the interval by. - - Example: --------------------- -auto interval1 = PosInfInterval!Date(Date(1996, 1, 2)); -auto interval2 = PosInfInterval!Date(Date(1996, 1, 2)); - -interval1.expand(dur!"days"(2)); -assert(interval1 == PosInfInterval!Date(Date(1995, 12, 31))); - -interval2.expand(dur!"days"(-2)); -assert(interval2 == PosInfInterval!Date(Date(1996, 1, 4))); --------------------- - +/ - void expand(D)(D duration) pure nothrow - if (__traits(compiles, begin + duration)) - { - _begin -= duration; - } - - - static if (__traits(compiles, begin.add!"months"(1)) && - __traits(compiles, begin.add!"years"(1))) - { - /++ - Expands the interval forwards and/or backwards in time. Effectively, - it subtracts the given number of months/years from `begin`. - - Params: - years = The number of years to expand the interval by. - months = The number of months to expand the interval by. - allowOverflow = Whether the days should be allowed to overflow - on `begin`, causing its month to increment. - - Throws: - $(REF DateTimeException,std,datetime,date) if this interval is - empty or if the resulting interval would be invalid. - - Example: --------------------- -auto interval1 = PosInfInterval!Date(Date(1996, 1, 2)); -auto interval2 = PosInfInterval!Date(Date(1996, 1, 2)); - -interval1.expand(2); -assert(interval1 == PosInfInterval!Date(Date(1994, 1, 2))); - -interval2.expand(-2); -assert(interval2 == PosInfInterval!Date(Date(1998, 1, 2))); --------------------- - +/ - void expand(T)(T years, T months = 0, AllowDayOverflow allowOverflow = AllowDayOverflow.yes) - if (isIntegral!T) - { - auto begin = _begin; - - begin.add!"years"(-years, allowOverflow); - begin.add!"months"(-months, allowOverflow); - - _begin = begin; - } - } - - - /++ - Returns a range which iterates forward over the interval, starting - at `begin`, using $(D_PARAM func) to generate each successive time - point. - - The range's `front` is the interval's `begin`. $(D_PARAM func) is - used to generate the next `front` when `popFront` is called. If - $(D_PARAM popFirst) is `PopFirst.yes`, then `popFront` is called - before the range is returned (so that `front` is a time point which - $(D_PARAM func) would generate). - - If $(D_PARAM func) ever generates a time point less than or equal to the - current `front` of the range, then a - $(REF DateTimeException,std,datetime,date) will be thrown. - - There are helper functions in this module which generate common - delegates to pass to `fwdRange`. Their documentation starts with - "Range-generating function," to make them easily searchable. - - Params: - func = The function used to generate the time points of the - range over the interval. - popFirst = Whether `popFront` should be called on the range - before returning it. - - Throws: - $(REF DateTimeException,std,datetime,date) if this interval is - empty. - - Warning: - $(D_PARAM func) must be logically pure. Ideally, $(D_PARAM func) - would be a function pointer to a pure function, but forcing - $(D_PARAM func) to be pure is far too restrictive to be useful, and - in order to have the ease of use of having functions which generate - functions to pass to `fwdRange`, $(D_PARAM func) must be a - delegate. - - If $(D_PARAM func) retains state which changes as it is called, then - some algorithms will not work correctly, because the range's - `save` will have failed to have really saved the range's state. - To avoid such bugs, don't pass a delegate which is - not logically pure to `fwdRange`. If $(D_PARAM func) is given the - same time point with two different calls, it must return the same - result both times. - - Of course, none of the functions in this module have this problem, - so it's only relevant for custom delegates. - - Example: --------------------- -auto interval = PosInfInterval!Date(Date(2010, 9, 1)); -auto func = delegate (scope const Date date) //For iterating over even-numbered days. - { - if ((date.day & 1) == 0) - return date + dur!"days"(2); - - return date + dur!"days"(1); - }; -auto range = interval.fwdRange(func); - -//An odd day. Using PopFirst.yes would have made this Date(2010, 9, 2). -assert(range.front == Date(2010, 9, 1)); - -range.popFront(); -assert(range.front == Date(2010, 9, 2)); - -range.popFront(); -assert(range.front == Date(2010, 9, 4)); - -range.popFront(); -assert(range.front == Date(2010, 9, 6)); - -range.popFront(); -assert(range.front == Date(2010, 9, 8)); - -range.popFront(); -assert(!range.empty); --------------------- - +/ - PosInfIntervalRange!(TP) fwdRange(TP delegate(scope const TP) func, PopFirst popFirst = PopFirst.no) const - { - auto range = PosInfIntervalRange!(TP)(this, func); - - if (popFirst == PopFirst.yes) - range.popFront(); - - return range; - } - - - /+ - Converts this interval to a string. - +/ - // Due to bug https://issues.dlang.org/show_bug.cgi?id=3715 , we can't - // have versions of toString() with extra modifiers, - // so we define one version with modifiers and one without. - string toString() - { - return _toStringImpl(); - } - - - /++ - Converts this interval to a string. - +/ - // Due to bug https://issues.dlang.org/show_bug.cgi?id=3715 , we can't - // have versions of toString() with extra modifiers, - // so we define one version with modifiers and one without. - string toString() const nothrow - { - return _toStringImpl(); - } - -private: - - /+ - Since we have two versions of toString(), we have _toStringImpl() - so that they can share implementations. - +/ - string _toStringImpl() const nothrow - { - import std.format : format; - try - return format("[%s - ∞)", _begin); - catch (Exception e) - assert(0, "format() threw."); - } - - - TP _begin; -} - -//Test PosInfInterval's constructor. -@safe unittest -{ - import std.datetime.date; - import std.datetime.systime; - - PosInfInterval!Date(Date.init); - PosInfInterval!TimeOfDay(TimeOfDay.init); - PosInfInterval!DateTime(DateTime.init); - PosInfInterval!SysTime(SysTime(0)); - - //Verify Examples. - auto interval = PosInfInterval!Date(Date(1996, 1, 2)); -} - -//Test PosInfInterval's begin. -@safe unittest -{ - import std.datetime.date; - - assert(PosInfInterval!Date(Date(1, 1, 1)).begin == Date(1, 1, 1)); - assert(PosInfInterval!Date(Date(2010, 1, 1)).begin == Date(2010, 1, 1)); - assert(PosInfInterval!Date(Date(1997, 12, 31)).begin == Date(1997, 12, 31)); - - const cPosInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); - immutable iPosInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); - assert(cPosInfInterval.begin != Date.init); - assert(iPosInfInterval.begin != Date.init); - - //Verify Examples. - assert(PosInfInterval!Date(Date(1996, 1, 2)).begin == Date(1996, 1, 2)); -} - -//Test PosInfInterval's empty. -@safe unittest -{ - import std.datetime.date; - import std.datetime.systime; - - assert(!PosInfInterval!Date(Date(2010, 1, 1)).empty); - assert(!PosInfInterval!TimeOfDay(TimeOfDay(0, 30, 0)).empty); - assert(!PosInfInterval!DateTime(DateTime(2010, 1, 1, 0, 30, 0)).empty); - assert(!PosInfInterval!SysTime(SysTime(DateTime(2010, 1, 1, 0, 30, 0))).empty); - - const cPosInfInterval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); - immutable iPosInfInterval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); - assert(!cPosInfInterval.empty); - assert(!iPosInfInterval.empty); - - //Verify Examples. - assert(!PosInfInterval!Date(Date(1996, 1, 2)).empty); -} - -//Test PosInfInterval's contains(time point). -@safe unittest -{ - import std.datetime.date; - - auto posInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); - - assert(!posInfInterval.contains(Date(2009, 7, 4))); - assert(!posInfInterval.contains(Date(2010, 7, 3))); - assert(posInfInterval.contains(Date(2010, 7, 4))); - assert(posInfInterval.contains(Date(2010, 7, 5))); - assert(posInfInterval.contains(Date(2011, 7, 1))); - assert(posInfInterval.contains(Date(2012, 1, 6))); - assert(posInfInterval.contains(Date(2012, 1, 7))); - assert(posInfInterval.contains(Date(2012, 1, 8))); - assert(posInfInterval.contains(Date(2013, 1, 7))); - - const cdate = Date(2010, 7, 6); - const cPosInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); - immutable iPosInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); - assert(posInfInterval.contains(cdate)); - assert(cPosInfInterval.contains(cdate)); - assert(iPosInfInterval.contains(cdate)); - - //Verify Examples. - assert(!PosInfInterval!Date(Date(1996, 1, 2)).contains(Date(1994, 12, 24))); - assert(PosInfInterval!Date(Date(1996, 1, 2)).contains(Date(2000, 1, 5))); -} - -//Test PosInfInterval's contains(Interval). -@safe unittest -{ - import std.datetime.date; - - auto posInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); - - static void testInterval(scope const PosInfInterval!Date posInfInterval, scope const Interval!Date interval) - { - posInfInterval.contains(interval); - } - - assertThrown!DateTimeException(testInterval(posInfInterval, Interval!Date(Date(2010, 7, 4), dur!"days"(0)))); - - assert(posInfInterval.contains(posInfInterval)); - assert(!posInfInterval.contains(Interval!Date(Date(2010, 7, 1), Date(2010, 7, 3)))); - assert(!posInfInterval.contains(Interval!Date(Date(2010, 7, 1), Date(2013, 7, 3)))); - assert(!posInfInterval.contains(Interval!Date(Date(2010, 7, 3), Date(2010, 7, 4)))); - assert(!posInfInterval.contains(Interval!Date(Date(2010, 7, 3), Date(2010, 7, 5)))); - assert(!posInfInterval.contains(Interval!Date(Date(2010, 7, 3), Date(2012, 1, 7)))); - assert(!posInfInterval.contains(Interval!Date(Date(2010, 7, 3), Date(2012, 1, 8)))); - assert(posInfInterval.contains(Interval!Date(Date(2010, 7, 5), Date(2012, 1, 6)))); - assert(posInfInterval.contains(Interval!Date(Date(2010, 7, 5), Date(2012, 1, 7)))); - assert(posInfInterval.contains(Interval!Date(Date(2012, 1, 6), Date(2012, 1, 7)))); - assert(posInfInterval.contains(Interval!Date(Date(2012, 1, 6), Date(2012, 1, 8)))); - assert(posInfInterval.contains(Interval!Date(Date(2012, 1, 7), Date(2012, 1, 8)))); - assert(posInfInterval.contains(Interval!Date(Date(2012, 1, 8), Date(2012, 1, 9)))); - - assert(!posInfInterval.contains(PosInfInterval!Date(Date(2010, 7, 3)))); - assert(posInfInterval.contains(PosInfInterval!Date(Date(2010, 7, 4)))); - assert(posInfInterval.contains(PosInfInterval!Date(Date(2010, 7, 5)))); - assert(posInfInterval.contains(PosInfInterval!Date(Date(2012, 1, 6)))); - assert(posInfInterval.contains(PosInfInterval!Date(Date(2012, 1, 7)))); - assert(posInfInterval.contains(PosInfInterval!Date(Date(2012, 1, 8)))); - - assert(PosInfInterval!Date(Date(2010, 7, 3)).contains(posInfInterval)); - assert(PosInfInterval!Date(Date(2010, 7, 4)).contains(posInfInterval)); - assert(!PosInfInterval!Date(Date(2010, 7, 5)).contains(posInfInterval)); - assert(!PosInfInterval!Date(Date(2012, 1, 6)).contains(posInfInterval)); - assert(!PosInfInterval!Date(Date(2012, 1, 7)).contains(posInfInterval)); - assert(!PosInfInterval!Date(Date(2012, 1, 8)).contains(posInfInterval)); - - assert(!posInfInterval.contains(NegInfInterval!Date(Date(2010, 7, 3)))); - assert(!posInfInterval.contains(NegInfInterval!Date(Date(2010, 7, 4)))); - assert(!posInfInterval.contains(NegInfInterval!Date(Date(2010, 7, 5)))); - assert(!posInfInterval.contains(NegInfInterval!Date(Date(2012, 1, 6)))); - assert(!posInfInterval.contains(NegInfInterval!Date(Date(2012, 1, 7)))); - assert(!posInfInterval.contains(NegInfInterval!Date(Date(2012, 1, 8)))); - - auto interval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); - const cInterval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); - immutable iInterval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); - const cPosInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); - immutable iPosInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); - auto negInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); - const cNegInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); - immutable iNegInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); - assert(posInfInterval.contains(interval)); - assert(posInfInterval.contains(cInterval)); - assert(posInfInterval.contains(iInterval)); - assert(posInfInterval.contains(posInfInterval)); - assert(posInfInterval.contains(cPosInfInterval)); - assert(posInfInterval.contains(iPosInfInterval)); - assert(!posInfInterval.contains(negInfInterval)); - assert(!posInfInterval.contains(cNegInfInterval)); - assert(!posInfInterval.contains(iNegInfInterval)); - assert(cPosInfInterval.contains(interval)); - assert(cPosInfInterval.contains(cInterval)); - assert(cPosInfInterval.contains(iInterval)); - assert(cPosInfInterval.contains(posInfInterval)); - assert(cPosInfInterval.contains(cPosInfInterval)); - assert(cPosInfInterval.contains(iPosInfInterval)); - assert(!cPosInfInterval.contains(negInfInterval)); - assert(!cPosInfInterval.contains(cNegInfInterval)); - assert(!cPosInfInterval.contains(iNegInfInterval)); - assert(iPosInfInterval.contains(interval)); - assert(iPosInfInterval.contains(cInterval)); - assert(iPosInfInterval.contains(iInterval)); - assert(iPosInfInterval.contains(posInfInterval)); - assert(iPosInfInterval.contains(cPosInfInterval)); - assert(iPosInfInterval.contains(iPosInfInterval)); - assert(!iPosInfInterval.contains(negInfInterval)); - assert(!iPosInfInterval.contains(cNegInfInterval)); - assert(!iPosInfInterval.contains(iNegInfInterval)); - - //Verify Examples. - assert(!PosInfInterval!Date(Date(1996, 1, 2)).contains(Interval!Date(Date(1990, 7, 6), Date(2000, 8, 2)))); - assert(PosInfInterval!Date(Date(1996, 1, 2)).contains(Interval!Date(Date(1999, 1, 12), Date(2011, 9, 17)))); - assert(PosInfInterval!Date(Date(1996, 1, 2)).contains(Interval!Date(Date(1998, 2, 28), Date(2013, 5, 1)))); - - assert(PosInfInterval!Date(Date(1996, 1, 2)).contains(PosInfInterval!Date(Date(1999, 5, 4)))); - assert(!PosInfInterval!Date(Date(1996, 1, 2)).contains(PosInfInterval!Date(Date(1995, 7, 2)))); - - assert(!PosInfInterval!Date(Date(1996, 1, 2)).contains(NegInfInterval!Date(Date(1996, 5, 4)))); -} - -//Test PosInfInterval's isBefore(time point). -@safe unittest -{ - import std.datetime.date; - - auto posInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); - - assert(!posInfInterval.isBefore(Date(2009, 7, 3))); - assert(!posInfInterval.isBefore(Date(2010, 7, 3))); - assert(!posInfInterval.isBefore(Date(2010, 7, 4))); - assert(!posInfInterval.isBefore(Date(2010, 7, 5))); - assert(!posInfInterval.isBefore(Date(2011, 7, 1))); - assert(!posInfInterval.isBefore(Date(2012, 1, 6))); - assert(!posInfInterval.isBefore(Date(2012, 1, 7))); - assert(!posInfInterval.isBefore(Date(2012, 1, 8))); - assert(!posInfInterval.isBefore(Date(2013, 1, 7))); - - const cdate = Date(2010, 7, 6); - const cPosInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); - immutable iPosInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); - assert(!posInfInterval.isBefore(cdate)); - assert(!cPosInfInterval.isBefore(cdate)); - assert(!iPosInfInterval.isBefore(cdate)); - - //Verify Examples. - assert(!PosInfInterval!Date(Date(1996, 1, 2)).isBefore(Date(1994, 12, 24))); - assert(!PosInfInterval!Date(Date(1996, 1, 2)).isBefore(Date(2000, 1, 5))); -} - -//Test PosInfInterval's isBefore(Interval). -@safe unittest -{ - import std.datetime.date; - - auto posInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); - - static void testInterval(scope const PosInfInterval!Date posInfInterval, scope const Interval!Date interval) - { - posInfInterval.isBefore(interval); - } - - assertThrown!DateTimeException(testInterval(posInfInterval, Interval!Date(Date(2010, 7, 4), dur!"days"(0)))); - - assert(!posInfInterval.isBefore(posInfInterval)); - assert(!posInfInterval.isBefore(Interval!Date(Date(2010, 7, 1), Date(2010, 7, 3)))); - assert(!posInfInterval.isBefore(Interval!Date(Date(2010, 7, 1), Date(2013, 7, 3)))); - assert(!posInfInterval.isBefore(Interval!Date(Date(2010, 7, 3), Date(2010, 7, 4)))); - assert(!posInfInterval.isBefore(Interval!Date(Date(2010, 7, 3), Date(2010, 7, 5)))); - assert(!posInfInterval.isBefore(Interval!Date(Date(2010, 7, 3), Date(2012, 1, 7)))); - assert(!posInfInterval.isBefore(Interval!Date(Date(2010, 7, 3), Date(2012, 1, 8)))); - assert(!posInfInterval.isBefore(Interval!Date(Date(2010, 7, 5), Date(2012, 1, 6)))); - assert(!posInfInterval.isBefore(Interval!Date(Date(2010, 7, 5), Date(2012, 1, 7)))); - assert(!posInfInterval.isBefore(Interval!Date(Date(2012, 1, 6), Date(2012, 1, 7)))); - assert(!posInfInterval.isBefore(Interval!Date(Date(2012, 1, 6), Date(2012, 1, 8)))); - assert(!posInfInterval.isBefore(Interval!Date(Date(2012, 1, 7), Date(2012, 1, 8)))); - assert(!posInfInterval.isBefore(Interval!Date(Date(2012, 1, 8), Date(2012, 1, 9)))); - - assert(!posInfInterval.isBefore(PosInfInterval!Date(Date(2010, 7, 3)))); - assert(!posInfInterval.isBefore(PosInfInterval!Date(Date(2010, 7, 4)))); - assert(!posInfInterval.isBefore(PosInfInterval!Date(Date(2010, 7, 5)))); - assert(!posInfInterval.isBefore(PosInfInterval!Date(Date(2012, 1, 6)))); - assert(!posInfInterval.isBefore(PosInfInterval!Date(Date(2012, 1, 7)))); - assert(!posInfInterval.isBefore(PosInfInterval!Date(Date(2012, 1, 8)))); - - assert(!PosInfInterval!Date(Date(2010, 7, 3)).isBefore(posInfInterval)); - assert(!PosInfInterval!Date(Date(2010, 7, 4)).isBefore(posInfInterval)); - assert(!PosInfInterval!Date(Date(2010, 7, 5)).isBefore(posInfInterval)); - assert(!PosInfInterval!Date(Date(2012, 1, 6)).isBefore(posInfInterval)); - assert(!PosInfInterval!Date(Date(2012, 1, 7)).isBefore(posInfInterval)); - assert(!PosInfInterval!Date(Date(2012, 1, 8)).isBefore(posInfInterval)); - - assert(!posInfInterval.isBefore(NegInfInterval!Date(Date(2010, 7, 3)))); - assert(!posInfInterval.isBefore(NegInfInterval!Date(Date(2010, 7, 4)))); - assert(!posInfInterval.isBefore(NegInfInterval!Date(Date(2010, 7, 5)))); - assert(!posInfInterval.isBefore(NegInfInterval!Date(Date(2012, 1, 6)))); - assert(!posInfInterval.isBefore(NegInfInterval!Date(Date(2012, 1, 7)))); - assert(!posInfInterval.isBefore(NegInfInterval!Date(Date(2012, 1, 8)))); - - auto interval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); - const cInterval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); - immutable iInterval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); - const cPosInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); - immutable iPosInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); - auto negInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); - const cNegInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); - immutable iNegInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); - assert(!posInfInterval.isBefore(interval)); - assert(!posInfInterval.isBefore(cInterval)); - assert(!posInfInterval.isBefore(iInterval)); - assert(!posInfInterval.isBefore(posInfInterval)); - assert(!posInfInterval.isBefore(cPosInfInterval)); - assert(!posInfInterval.isBefore(iPosInfInterval)); - assert(!posInfInterval.isBefore(negInfInterval)); - assert(!posInfInterval.isBefore(cNegInfInterval)); - assert(!posInfInterval.isBefore(iNegInfInterval)); - assert(!cPosInfInterval.isBefore(interval)); - assert(!cPosInfInterval.isBefore(cInterval)); - assert(!cPosInfInterval.isBefore(iInterval)); - assert(!cPosInfInterval.isBefore(posInfInterval)); - assert(!cPosInfInterval.isBefore(cPosInfInterval)); - assert(!cPosInfInterval.isBefore(iPosInfInterval)); - assert(!cPosInfInterval.isBefore(negInfInterval)); - assert(!cPosInfInterval.isBefore(cNegInfInterval)); - assert(!cPosInfInterval.isBefore(iNegInfInterval)); - assert(!iPosInfInterval.isBefore(interval)); - assert(!iPosInfInterval.isBefore(cInterval)); - assert(!iPosInfInterval.isBefore(iInterval)); - assert(!iPosInfInterval.isBefore(posInfInterval)); - assert(!iPosInfInterval.isBefore(cPosInfInterval)); - assert(!iPosInfInterval.isBefore(iPosInfInterval)); - assert(!iPosInfInterval.isBefore(negInfInterval)); - assert(!iPosInfInterval.isBefore(cNegInfInterval)); - assert(!iPosInfInterval.isBefore(iNegInfInterval)); - - //Verify Examples. - assert(!PosInfInterval!Date(Date(1996, 1, 2)).isBefore(Interval!Date(Date(1990, 7, 6), Date(2000, 8, 2)))); - assert(!PosInfInterval!Date(Date(1996, 1, 2)).isBefore(Interval!Date(Date(1999, 1, 12), Date(2011, 9, 17)))); - - assert(!PosInfInterval!Date(Date(1996, 1, 2)).isBefore(PosInfInterval!Date(Date(1992, 5, 4)))); - assert(!PosInfInterval!Date(Date(1996, 1, 2)).isBefore(PosInfInterval!Date(Date(2013, 3, 7)))); - - assert(!PosInfInterval!Date(Date(1996, 1, 2)).isBefore(NegInfInterval!Date(Date(1996, 5, 4)))); -} - -//Test PosInfInterval's isAfter(time point). -@safe unittest -{ - import std.datetime.date; - - auto posInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); - - assert(posInfInterval.isAfter(Date(2009, 7, 3))); - assert(posInfInterval.isAfter(Date(2010, 7, 3))); - assert(!posInfInterval.isAfter(Date(2010, 7, 4))); - assert(!posInfInterval.isAfter(Date(2010, 7, 5))); - assert(!posInfInterval.isAfter(Date(2011, 7, 1))); - assert(!posInfInterval.isAfter(Date(2012, 1, 6))); - assert(!posInfInterval.isAfter(Date(2012, 1, 7))); - assert(!posInfInterval.isAfter(Date(2012, 1, 8))); - assert(!posInfInterval.isAfter(Date(2013, 1, 7))); - - const cdate = Date(2010, 7, 6); - const cPosInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); - immutable iPosInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); - assert(!posInfInterval.isAfter(cdate)); - assert(!cPosInfInterval.isAfter(cdate)); - assert(!iPosInfInterval.isAfter(cdate)); - - //Verify Examples. - assert(PosInfInterval!Date(Date(1996, 1, 2)).isAfter(Date(1994, 12, 24))); - assert(!PosInfInterval!Date(Date(1996, 1, 2)).isAfter(Date(2000, 1, 5))); -} - -//Test PosInfInterval's isAfter(Interval). -@safe unittest -{ - import std.datetime.date; - - auto posInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); - - static void testInterval(scope const PosInfInterval!Date posInfInterval, scope const Interval!Date interval) - { - posInfInterval.isAfter(interval); - } - - assertThrown!DateTimeException(testInterval(posInfInterval, Interval!Date(Date(2010, 7, 4), dur!"days"(0)))); - - assert(!posInfInterval.isAfter(posInfInterval)); - assert(posInfInterval.isAfter(Interval!Date(Date(2010, 7, 1), Date(2010, 7, 3)))); - assert(!posInfInterval.isAfter(Interval!Date(Date(2010, 7, 1), Date(2013, 7, 3)))); - assert(posInfInterval.isAfter(Interval!Date(Date(2010, 7, 3), Date(2010, 7, 4)))); - assert(!posInfInterval.isAfter(Interval!Date(Date(2010, 7, 3), Date(2010, 7, 5)))); - assert(!posInfInterval.isAfter(Interval!Date(Date(2010, 7, 3), Date(2012, 1, 7)))); - assert(!posInfInterval.isAfter(Interval!Date(Date(2010, 7, 3), Date(2012, 1, 8)))); - assert(!posInfInterval.isAfter(Interval!Date(Date(2010, 7, 5), Date(2012, 1, 6)))); - assert(!posInfInterval.isAfter(Interval!Date(Date(2010, 7, 5), Date(2012, 1, 7)))); - assert(!posInfInterval.isAfter(Interval!Date(Date(2012, 1, 6), Date(2012, 1, 7)))); - assert(!posInfInterval.isAfter(Interval!Date(Date(2012, 1, 6), Date(2012, 1, 8)))); - assert(!posInfInterval.isAfter(Interval!Date(Date(2012, 1, 7), Date(2012, 1, 8)))); - assert(!posInfInterval.isAfter(Interval!Date(Date(2012, 1, 8), Date(2012, 1, 9)))); - - assert(!posInfInterval.isAfter(PosInfInterval!Date(Date(2010, 7, 3)))); - assert(!posInfInterval.isAfter(PosInfInterval!Date(Date(2010, 7, 4)))); - assert(!posInfInterval.isAfter(PosInfInterval!Date(Date(2010, 7, 5)))); - assert(!posInfInterval.isAfter(PosInfInterval!Date(Date(2012, 1, 6)))); - assert(!posInfInterval.isAfter(PosInfInterval!Date(Date(2012, 1, 7)))); - assert(!posInfInterval.isAfter(PosInfInterval!Date(Date(2012, 1, 8)))); - - assert(!PosInfInterval!Date(Date(2010, 7, 3)).isAfter(posInfInterval)); - assert(!PosInfInterval!Date(Date(2010, 7, 4)).isAfter(posInfInterval)); - assert(!PosInfInterval!Date(Date(2010, 7, 5)).isAfter(posInfInterval)); - assert(!PosInfInterval!Date(Date(2012, 1, 6)).isAfter(posInfInterval)); - assert(!PosInfInterval!Date(Date(2012, 1, 7)).isAfter(posInfInterval)); - assert(!PosInfInterval!Date(Date(2012, 1, 8)).isAfter(posInfInterval)); - - assert(posInfInterval.isAfter(NegInfInterval!Date(Date(2010, 7, 3)))); - assert(posInfInterval.isAfter(NegInfInterval!Date(Date(2010, 7, 4)))); - assert(!posInfInterval.isAfter(NegInfInterval!Date(Date(2010, 7, 5)))); - assert(!posInfInterval.isAfter(NegInfInterval!Date(Date(2012, 1, 6)))); - assert(!posInfInterval.isAfter(NegInfInterval!Date(Date(2012, 1, 7)))); - assert(!posInfInterval.isAfter(NegInfInterval!Date(Date(2012, 1, 8)))); - - auto interval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); - const cInterval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); - immutable iInterval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); - const cPosInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); - immutable iPosInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); - auto negInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); - const cNegInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); - immutable iNegInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); - assert(!posInfInterval.isAfter(interval)); - assert(!posInfInterval.isAfter(cInterval)); - assert(!posInfInterval.isAfter(iInterval)); - assert(!posInfInterval.isAfter(posInfInterval)); - assert(!posInfInterval.isAfter(cPosInfInterval)); - assert(!posInfInterval.isAfter(iPosInfInterval)); - assert(!posInfInterval.isAfter(negInfInterval)); - assert(!posInfInterval.isAfter(cNegInfInterval)); - assert(!posInfInterval.isAfter(iNegInfInterval)); - assert(!cPosInfInterval.isAfter(interval)); - assert(!cPosInfInterval.isAfter(cInterval)); - assert(!cPosInfInterval.isAfter(iInterval)); - assert(!cPosInfInterval.isAfter(posInfInterval)); - assert(!cPosInfInterval.isAfter(cPosInfInterval)); - assert(!cPosInfInterval.isAfter(iPosInfInterval)); - assert(!cPosInfInterval.isAfter(negInfInterval)); - assert(!cPosInfInterval.isAfter(cNegInfInterval)); - assert(!cPosInfInterval.isAfter(iNegInfInterval)); - assert(!iPosInfInterval.isAfter(interval)); - assert(!iPosInfInterval.isAfter(cInterval)); - assert(!iPosInfInterval.isAfter(iInterval)); - assert(!iPosInfInterval.isAfter(posInfInterval)); - assert(!iPosInfInterval.isAfter(cPosInfInterval)); - assert(!iPosInfInterval.isAfter(iPosInfInterval)); - assert(!iPosInfInterval.isAfter(negInfInterval)); - assert(!iPosInfInterval.isAfter(cNegInfInterval)); - assert(!iPosInfInterval.isAfter(iNegInfInterval)); - - //Verify Examples. - assert(!PosInfInterval!Date(Date(1996, 1, 2)).isAfter(Interval!Date(Date(1990, 7, 6), Date(2000, 8, 2)))); - assert(!PosInfInterval!Date(Date(1996, 1, 2)).isAfter(Interval!Date(Date(1999, 1, 12), Date(2011, 9, 17)))); - assert(PosInfInterval!Date(Date(1996, 1, 2)).isAfter(Interval!Date(Date(1989, 3, 1), Date(1996, 1, 2)))); - - assert(!PosInfInterval!Date(Date(1996, 1, 2)).isAfter(PosInfInterval!Date(Date(1990, 1, 7)))); - assert(!PosInfInterval!Date(Date(1996, 1, 2)).isAfter(PosInfInterval!Date(Date(1999, 5, 4)))); - - assert(PosInfInterval!Date(Date(1996, 1, 2)).isAfter(NegInfInterval!Date(Date(1996, 1, 2)))); - assert(!PosInfInterval!Date(Date(1996, 1, 2)).isAfter(NegInfInterval!Date(Date(2000, 7, 1)))); -} - -//Test PosInfInterval's intersects(). -@safe unittest -{ - import std.datetime.date; - - auto posInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); - - static void testInterval(scope const PosInfInterval!Date posInfInterval, scope const Interval!Date interval) - { - posInfInterval.intersects(interval); - } - - assertThrown!DateTimeException(testInterval(posInfInterval, Interval!Date(Date(2010, 7, 4), dur!"days"(0)))); - - assert(posInfInterval.intersects(posInfInterval)); - assert(!posInfInterval.intersects(Interval!Date(Date(2010, 7, 1), Date(2010, 7, 3)))); - assert(posInfInterval.intersects(Interval!Date(Date(2010, 7, 1), Date(2013, 7, 3)))); - assert(!posInfInterval.intersects(Interval!Date(Date(2010, 7, 3), Date(2010, 7, 4)))); - assert(posInfInterval.intersects(Interval!Date(Date(2010, 7, 3), Date(2010, 7, 5)))); - assert(posInfInterval.intersects(Interval!Date(Date(2010, 7, 3), Date(2012, 1, 7)))); - assert(posInfInterval.intersects(Interval!Date(Date(2010, 7, 3), Date(2012, 1, 8)))); - assert(posInfInterval.intersects(Interval!Date(Date(2010, 7, 5), Date(2012, 1, 6)))); - assert(posInfInterval.intersects(Interval!Date(Date(2010, 7, 5), Date(2012, 1, 7)))); - assert(posInfInterval.intersects(Interval!Date(Date(2012, 1, 6), Date(2012, 1, 7)))); - assert(posInfInterval.intersects(Interval!Date(Date(2012, 1, 6), Date(2012, 1, 8)))); - assert(posInfInterval.intersects(Interval!Date(Date(2012, 1, 7), Date(2012, 1, 8)))); - assert(posInfInterval.intersects(Interval!Date(Date(2012, 1, 8), Date(2012, 1, 9)))); - - assert(posInfInterval.intersects(PosInfInterval!Date(Date(2010, 7, 3)))); - assert(posInfInterval.intersects(PosInfInterval!Date(Date(2010, 7, 4)))); - assert(posInfInterval.intersects(PosInfInterval!Date(Date(2010, 7, 5)))); - assert(posInfInterval.intersects(PosInfInterval!Date(Date(2012, 1, 6)))); - assert(posInfInterval.intersects(PosInfInterval!Date(Date(2012, 1, 7)))); - assert(posInfInterval.intersects(PosInfInterval!Date(Date(2012, 1, 8)))); - - assert(PosInfInterval!Date(Date(2010, 7, 3)).intersects(posInfInterval)); - assert(PosInfInterval!Date(Date(2010, 7, 4)).intersects(posInfInterval)); - assert(PosInfInterval!Date(Date(2010, 7, 5)).intersects(posInfInterval)); - assert(PosInfInterval!Date(Date(2012, 1, 6)).intersects(posInfInterval)); - assert(PosInfInterval!Date(Date(2012, 1, 7)).intersects(posInfInterval)); - assert(PosInfInterval!Date(Date(2012, 1, 8)).intersects(posInfInterval)); - - assert(!posInfInterval.intersects(NegInfInterval!Date(Date(2010, 7, 3)))); - assert(!posInfInterval.intersects(NegInfInterval!Date(Date(2010, 7, 4)))); - assert(posInfInterval.intersects(NegInfInterval!Date(Date(2010, 7, 5)))); - assert(posInfInterval.intersects(NegInfInterval!Date(Date(2012, 1, 6)))); - assert(posInfInterval.intersects(NegInfInterval!Date(Date(2012, 1, 7)))); - assert(posInfInterval.intersects(NegInfInterval!Date(Date(2012, 1, 8)))); - - auto interval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); - const cInterval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); - immutable iInterval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); - const cPosInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); - immutable iPosInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); - auto negInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); - const cNegInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); - immutable iNegInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); - assert(posInfInterval.intersects(interval)); - assert(posInfInterval.intersects(cInterval)); - assert(posInfInterval.intersects(iInterval)); - assert(posInfInterval.intersects(posInfInterval)); - assert(posInfInterval.intersects(cPosInfInterval)); - assert(posInfInterval.intersects(iPosInfInterval)); - assert(posInfInterval.intersects(negInfInterval)); - assert(posInfInterval.intersects(cNegInfInterval)); - assert(posInfInterval.intersects(iNegInfInterval)); - assert(cPosInfInterval.intersects(interval)); - assert(cPosInfInterval.intersects(cInterval)); - assert(cPosInfInterval.intersects(iInterval)); - assert(cPosInfInterval.intersects(posInfInterval)); - assert(cPosInfInterval.intersects(cPosInfInterval)); - assert(cPosInfInterval.intersects(iPosInfInterval)); - assert(cPosInfInterval.intersects(negInfInterval)); - assert(cPosInfInterval.intersects(cNegInfInterval)); - assert(cPosInfInterval.intersects(iNegInfInterval)); - assert(iPosInfInterval.intersects(interval)); - assert(iPosInfInterval.intersects(cInterval)); - assert(iPosInfInterval.intersects(iInterval)); - assert(iPosInfInterval.intersects(posInfInterval)); - assert(iPosInfInterval.intersects(cPosInfInterval)); - assert(iPosInfInterval.intersects(iPosInfInterval)); - assert(iPosInfInterval.intersects(negInfInterval)); - assert(iPosInfInterval.intersects(cNegInfInterval)); - assert(iPosInfInterval.intersects(iNegInfInterval)); - - //Verify Examples. - assert(PosInfInterval!Date(Date(1996, 1, 2)).intersects(Interval!Date(Date(1990, 7, 6), Date(2000, 8, 2)))); - assert(PosInfInterval!Date(Date(1996, 1, 2)).intersects(Interval!Date(Date(1999, 1, 12), Date(2011, 9, 17)))); - assert(!PosInfInterval!Date(Date(1996, 1, 2)).intersects(Interval!Date(Date(1989, 3, 1), Date(1996, 1, 2)))); - - assert(PosInfInterval!Date(Date(1996, 1, 2)).intersects(PosInfInterval!Date(Date(1990, 1, 7)))); - assert(PosInfInterval!Date(Date(1996, 1, 2)).intersects(PosInfInterval!Date(Date(1999, 5, 4)))); - - assert(!PosInfInterval!Date(Date(1996, 1, 2)).intersects(NegInfInterval!Date(Date(1996, 1, 2)))); - assert(PosInfInterval!Date(Date(1996, 1, 2)).intersects(NegInfInterval!Date(Date(2000, 7, 1)))); -} - -//Test PosInfInterval's intersection(). -@safe unittest -{ - import std.datetime.date; - - auto posInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); - - static void testInterval(I, J)(scope const I interval1, scope const J interval2) - { - interval1.intersection(interval2); - } - - assertThrown!DateTimeException(testInterval(posInfInterval, Interval!Date(Date(2010, 7, 4), dur!"days"(0)))); - - assertThrown!DateTimeException(testInterval(posInfInterval, Interval!Date(Date(2010, 7, 1), Date(2010, 7, 3)))); - assertThrown!DateTimeException(testInterval(posInfInterval, Interval!Date(Date(2010, 7, 3), Date(2010, 7, 4)))); - - assertThrown!DateTimeException(testInterval(posInfInterval, NegInfInterval!Date(Date(2010, 7, 3)))); - assertThrown!DateTimeException(testInterval(posInfInterval, NegInfInterval!Date(Date(2010, 7, 4)))); - - assert(posInfInterval.intersection(posInfInterval) == posInfInterval); - assert(posInfInterval.intersection(Interval!Date(Date(2010, 7, 1), Date(2013, 7, 3))) == - Interval!Date(Date(2010, 7, 4), Date(2013, 7, 3))); - assert(posInfInterval.intersection(Interval!Date(Date(2010, 7, 3), Date(2010, 7, 5))) == - Interval!Date(Date(2010, 7, 4), Date(2010, 7, 5))); - assert(posInfInterval.intersection(Interval!Date(Date(2010, 7, 3), Date(2012, 1, 7))) == - Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7))); - assert(posInfInterval.intersection(Interval!Date(Date(2010, 7, 3), Date(2012, 1, 8))) == - Interval!Date(Date(2010, 7, 4), Date(2012, 1, 8))); - assert(posInfInterval.intersection(Interval!Date(Date(2010, 7, 5), Date(2012, 1, 6))) == - Interval!Date(Date(2010, 7, 5), Date(2012, 1, 6))); - assert(posInfInterval.intersection(Interval!Date(Date(2010, 7, 5), Date(2012, 1, 7))) == - Interval!Date(Date(2010, 7, 5), Date(2012, 1, 7))); - assert(posInfInterval.intersection(Interval!Date(Date(2012, 1, 6), Date(2012, 1, 7))) == - Interval!Date(Date(2012, 1, 6), Date(2012, 1, 7))); - assert(posInfInterval.intersection(Interval!Date(Date(2012, 1, 6), Date(2012, 1, 8))) == - Interval!Date(Date(2012, 1, 6), Date(2012, 1, 8))); - assert(posInfInterval.intersection(Interval!Date(Date(2012, 1, 7), Date(2012, 1, 8))) == - Interval!Date(Date(2012, 1, 7), Date(2012, 1, 8))); - assert(posInfInterval.intersection(Interval!Date(Date(2012, 1, 8), Date(2012, 1, 9))) == - Interval!Date(Date(2012, 1, 8), Date(2012, 1, 9))); - - assert(posInfInterval.intersection(PosInfInterval!Date(Date(2010, 7, 3))) == PosInfInterval!Date(Date(2010, 7, 4))); - assert(posInfInterval.intersection(PosInfInterval!Date(Date(2010, 7, 4))) == PosInfInterval!Date(Date(2010, 7, 4))); - assert(posInfInterval.intersection(PosInfInterval!Date(Date(2010, 7, 5))) == PosInfInterval!Date(Date(2010, 7, 5))); - assert(posInfInterval.intersection(PosInfInterval!Date(Date(2012, 1, 6))) == PosInfInterval!Date(Date(2012, 1, 6))); - assert(posInfInterval.intersection(PosInfInterval!Date(Date(2012, 1, 7))) == PosInfInterval!Date(Date(2012, 1, 7))); - assert(posInfInterval.intersection(PosInfInterval!Date(Date(2012, 1, 8))) == PosInfInterval!Date(Date(2012, 1, 8))); - - assert(PosInfInterval!Date(Date(2010, 7, 3)).intersection(posInfInterval) == PosInfInterval!Date(Date(2010, 7, 4))); - assert(PosInfInterval!Date(Date(2010, 7, 4)).intersection(posInfInterval) == PosInfInterval!Date(Date(2010, 7, 4))); - assert(PosInfInterval!Date(Date(2010, 7, 5)).intersection(posInfInterval) == PosInfInterval!Date(Date(2010, 7, 5))); - assert(PosInfInterval!Date(Date(2012, 1, 6)).intersection(posInfInterval) == PosInfInterval!Date(Date(2012, 1, 6))); - assert(PosInfInterval!Date(Date(2012, 1, 7)).intersection(posInfInterval) == PosInfInterval!Date(Date(2012, 1, 7))); - assert(PosInfInterval!Date(Date(2012, 1, 8)).intersection(posInfInterval) == PosInfInterval!Date(Date(2012, 1, 8))); - - assert(posInfInterval.intersection(NegInfInterval!Date(Date(2010, 7, 5))) == - Interval!Date(Date(2010, 7, 4), Date(2010, 7, 5))); - assert(posInfInterval.intersection(NegInfInterval!Date(Date(2012, 1, 6))) == - Interval!Date(Date(2010, 7, 4), Date(2012, 1, 6))); - assert(posInfInterval.intersection(NegInfInterval!Date(Date(2012, 1, 7))) == - Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7))); - assert(posInfInterval.intersection(NegInfInterval!Date(Date(2012, 1, 8))) == - Interval!Date(Date(2010, 7, 4), Date(2012, 1, 8))); - - auto interval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); - const cInterval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); - immutable iInterval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); - const cPosInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); - immutable iPosInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); - auto negInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); - const cNegInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); - immutable iNegInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); - assert(!posInfInterval.intersection(interval).empty); - assert(!posInfInterval.intersection(cInterval).empty); - assert(!posInfInterval.intersection(iInterval).empty); - assert(!posInfInterval.intersection(posInfInterval).empty); - assert(!posInfInterval.intersection(cPosInfInterval).empty); - assert(!posInfInterval.intersection(iPosInfInterval).empty); - assert(!posInfInterval.intersection(negInfInterval).empty); - assert(!posInfInterval.intersection(cNegInfInterval).empty); - assert(!posInfInterval.intersection(iNegInfInterval).empty); - assert(!cPosInfInterval.intersection(interval).empty); - assert(!cPosInfInterval.intersection(cInterval).empty); - assert(!cPosInfInterval.intersection(iInterval).empty); - assert(!cPosInfInterval.intersection(posInfInterval).empty); - assert(!cPosInfInterval.intersection(cPosInfInterval).empty); - assert(!cPosInfInterval.intersection(iPosInfInterval).empty); - assert(!cPosInfInterval.intersection(negInfInterval).empty); - assert(!cPosInfInterval.intersection(cNegInfInterval).empty); - assert(!cPosInfInterval.intersection(iNegInfInterval).empty); - assert(!iPosInfInterval.intersection(interval).empty); - assert(!iPosInfInterval.intersection(cInterval).empty); - assert(!iPosInfInterval.intersection(iInterval).empty); - assert(!iPosInfInterval.intersection(posInfInterval).empty); - assert(!iPosInfInterval.intersection(cPosInfInterval).empty); - assert(!iPosInfInterval.intersection(iPosInfInterval).empty); - assert(!iPosInfInterval.intersection(negInfInterval).empty); - assert(!iPosInfInterval.intersection(cNegInfInterval).empty); - assert(!iPosInfInterval.intersection(iNegInfInterval).empty); - - //Verify Examples. - assert(PosInfInterval!Date(Date(1996, 1, 2)).intersection(Interval!Date(Date(1990, 7, 6), Date(2000, 8, 2))) == - Interval!Date(Date(1996, 1, 2), Date(2000, 8, 2))); - assert(PosInfInterval!Date(Date(1996, 1, 2)).intersection(Interval!Date(Date(1999, 1, 12), Date(2011, 9, 17))) == - Interval!Date(Date(1999, 1, 12), Date(2011, 9, 17))); - - assert(PosInfInterval!Date(Date(1996, 1, 2)).intersection(PosInfInterval!Date(Date(1990, 7, 6))) == - PosInfInterval!Date(Date(1996, 1, 2))); - assert(PosInfInterval!Date(Date(1996, 1, 2)).intersection(PosInfInterval!Date(Date(1999, 1, 12))) == - PosInfInterval!Date(Date(1999, 1, 12))); - - assert(PosInfInterval!Date(Date(1996, 1, 2)).intersection(NegInfInterval!Date(Date(1999, 7, 6))) == - Interval!Date(Date(1996, 1, 2), Date(1999, 7, 6))); - assert(PosInfInterval!Date(Date(1996, 1, 2)).intersection(NegInfInterval!Date(Date(2013, 1, 12))) == - Interval!Date(Date(1996, 1, 2), Date(2013, 1, 12))); -} - -//Test PosInfInterval's isAdjacent(). -@safe unittest -{ - import std.datetime.date; - - auto posInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); - - static void testInterval(scope const PosInfInterval!Date posInfInterval, scope const Interval!Date interval) - { - posInfInterval.isAdjacent(interval); - } - - assertThrown!DateTimeException(testInterval(posInfInterval, Interval!Date(Date(2010, 7, 4), dur!"days"(0)))); - - assert(!posInfInterval.isAdjacent(posInfInterval)); - assert(!posInfInterval.isAdjacent(Interval!Date(Date(2010, 7, 1), Date(2010, 7, 3)))); - assert(!posInfInterval.isAdjacent(Interval!Date(Date(2010, 7, 1), Date(2013, 7, 3)))); - assert(posInfInterval.isAdjacent(Interval!Date(Date(2010, 7, 3), Date(2010, 7, 4)))); - assert(!posInfInterval.isAdjacent(Interval!Date(Date(2010, 7, 3), Date(2010, 7, 5)))); - assert(!posInfInterval.isAdjacent(Interval!Date(Date(2010, 7, 3), Date(2012, 1, 7)))); - assert(!posInfInterval.isAdjacent(Interval!Date(Date(2010, 7, 3), Date(2012, 1, 8)))); - assert(!posInfInterval.isAdjacent(Interval!Date(Date(2010, 7, 5), Date(2012, 1, 6)))); - assert(!posInfInterval.isAdjacent(Interval!Date(Date(2010, 7, 5), Date(2012, 1, 7)))); - assert(!posInfInterval.isAdjacent(Interval!Date(Date(2012, 1, 6), Date(2012, 1, 7)))); - assert(!posInfInterval.isAdjacent(Interval!Date(Date(2012, 1, 6), Date(2012, 1, 8)))); - assert(!posInfInterval.isAdjacent(Interval!Date(Date(2012, 1, 7), Date(2012, 1, 8)))); - assert(!posInfInterval.isAdjacent(Interval!Date(Date(2012, 1, 8), Date(2012, 1, 9)))); - - assert(!posInfInterval.isAdjacent(PosInfInterval!Date(Date(2010, 7, 3)))); - assert(!posInfInterval.isAdjacent(PosInfInterval!Date(Date(2010, 7, 4)))); - assert(!posInfInterval.isAdjacent(PosInfInterval!Date(Date(2010, 7, 5)))); - assert(!posInfInterval.isAdjacent(PosInfInterval!Date(Date(2012, 1, 6)))); - assert(!posInfInterval.isAdjacent(PosInfInterval!Date(Date(2012, 1, 7)))); - assert(!posInfInterval.isAdjacent(PosInfInterval!Date(Date(2012, 1, 8)))); - - assert(!PosInfInterval!Date(Date(2010, 7, 3)).isAdjacent(posInfInterval)); - assert(!PosInfInterval!Date(Date(2010, 7, 4)).isAdjacent(posInfInterval)); - assert(!PosInfInterval!Date(Date(2010, 7, 5)).isAdjacent(posInfInterval)); - assert(!PosInfInterval!Date(Date(2012, 1, 6)).isAdjacent(posInfInterval)); - assert(!PosInfInterval!Date(Date(2012, 1, 7)).isAdjacent(posInfInterval)); - assert(!PosInfInterval!Date(Date(2012, 1, 8)).isAdjacent(posInfInterval)); - - assert(!posInfInterval.isAdjacent(NegInfInterval!Date(Date(2010, 7, 3)))); - assert(posInfInterval.isAdjacent(NegInfInterval!Date(Date(2010, 7, 4)))); - assert(!posInfInterval.isAdjacent(NegInfInterval!Date(Date(2010, 7, 5)))); - assert(!posInfInterval.isAdjacent(NegInfInterval!Date(Date(2012, 1, 6)))); - assert(!posInfInterval.isAdjacent(NegInfInterval!Date(Date(2012, 1, 7)))); - assert(!posInfInterval.isAdjacent(NegInfInterval!Date(Date(2012, 1, 8)))); - - auto interval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); - const cInterval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); - immutable iInterval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); - const cPosInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); - immutable iPosInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); - auto negInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); - const cNegInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); - immutable iNegInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); - assert(!posInfInterval.isAdjacent(interval)); - assert(!posInfInterval.isAdjacent(cInterval)); - assert(!posInfInterval.isAdjacent(iInterval)); - assert(!posInfInterval.isAdjacent(posInfInterval)); - assert(!posInfInterval.isAdjacent(cPosInfInterval)); - assert(!posInfInterval.isAdjacent(iPosInfInterval)); - assert(!posInfInterval.isAdjacent(negInfInterval)); - assert(!posInfInterval.isAdjacent(cNegInfInterval)); - assert(!posInfInterval.isAdjacent(iNegInfInterval)); - assert(!cPosInfInterval.isAdjacent(interval)); - assert(!cPosInfInterval.isAdjacent(cInterval)); - assert(!cPosInfInterval.isAdjacent(iInterval)); - assert(!cPosInfInterval.isAdjacent(posInfInterval)); - assert(!cPosInfInterval.isAdjacent(cPosInfInterval)); - assert(!cPosInfInterval.isAdjacent(iPosInfInterval)); - assert(!cPosInfInterval.isAdjacent(negInfInterval)); - assert(!cPosInfInterval.isAdjacent(cNegInfInterval)); - assert(!cPosInfInterval.isAdjacent(iNegInfInterval)); - assert(!iPosInfInterval.isAdjacent(interval)); - assert(!iPosInfInterval.isAdjacent(cInterval)); - assert(!iPosInfInterval.isAdjacent(iInterval)); - assert(!iPosInfInterval.isAdjacent(posInfInterval)); - assert(!iPosInfInterval.isAdjacent(cPosInfInterval)); - assert(!iPosInfInterval.isAdjacent(iPosInfInterval)); - assert(!iPosInfInterval.isAdjacent(negInfInterval)); - assert(!iPosInfInterval.isAdjacent(cNegInfInterval)); - assert(!iPosInfInterval.isAdjacent(iNegInfInterval)); - - //Verify Examples. - assert(PosInfInterval!Date(Date(1996, 1, 2)).isAdjacent(Interval!Date(Date(1989, 3, 1), Date(1996, 1, 2)))); - assert(!PosInfInterval!Date(Date(1999, 1, 12)).isAdjacent(Interval!Date(Date(1999, 1, 12), Date(2011, 9, 17)))); - - assert(!PosInfInterval!Date(Date(1996, 1, 2)).isAdjacent(PosInfInterval!Date(Date(1990, 1, 7)))); - assert(!PosInfInterval!Date(Date(1996, 1, 2)).isAdjacent(PosInfInterval!Date(Date(1996, 1, 2)))); - - assert(PosInfInterval!Date(Date(1996, 1, 2)).isAdjacent(NegInfInterval!Date(Date(1996, 1, 2)))); - assert(!PosInfInterval!Date(Date(1996, 1, 2)).isAdjacent(NegInfInterval!Date(Date(2000, 7, 1)))); -} - -//Test PosInfInterval's merge(). -@safe unittest -{ - import std.datetime.date; - - auto posInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); - - static void testInterval(scope const PosInfInterval!Date posInfInterval, scope const Interval!Date interval) - { - posInfInterval.merge(interval); - } - - assertThrown!DateTimeException(testInterval(posInfInterval, Interval!Date(Date(2010, 7, 4), dur!"days"(0)))); - - assertThrown!DateTimeException(testInterval(posInfInterval, Interval!Date(Date(2010, 7, 1), Date(2010, 7, 3)))); - - assert(posInfInterval.merge(posInfInterval) == posInfInterval); - assert(posInfInterval.merge(Interval!Date(Date(2010, 7, 1), Date(2013, 7, 3))) == - PosInfInterval!Date(Date(2010, 7, 1))); - assert(posInfInterval.merge(Interval!Date(Date(2010, 7, 3), Date(2010, 7, 4))) == - PosInfInterval!Date(Date(2010, 7, 3))); - assert(posInfInterval.merge(Interval!Date(Date(2010, 7, 3), Date(2010, 7, 5))) == - PosInfInterval!Date(Date(2010, 7, 3))); - assert(posInfInterval.merge(Interval!Date(Date(2010, 7, 3), Date(2012, 1, 7))) == - PosInfInterval!Date(Date(2010, 7, 3))); - assert(posInfInterval.merge(Interval!Date(Date(2010, 7, 3), Date(2012, 1, 8))) == - PosInfInterval!Date(Date(2010, 7, 3))); - assert(posInfInterval.merge(Interval!Date(Date(2010, 7, 5), Date(2012, 1, 6))) == - PosInfInterval!Date(Date(2010, 7, 4))); - assert(posInfInterval.merge(Interval!Date(Date(2010, 7, 5), Date(2012, 1, 7))) == - PosInfInterval!Date(Date(2010, 7, 4))); - assert(posInfInterval.merge(Interval!Date(Date(2012, 1, 6), Date(2012, 1, 7))) == - PosInfInterval!Date(Date(2010, 7, 4))); - assert(posInfInterval.merge(Interval!Date(Date(2012, 1, 6), Date(2012, 1, 8))) == - PosInfInterval!Date(Date(2010, 7, 4))); - assert(posInfInterval.merge(Interval!Date(Date(2012, 1, 7), Date(2012, 1, 8))) == - PosInfInterval!Date(Date(2010, 7, 4))); - assert(posInfInterval.merge(Interval!Date(Date(2012, 1, 8), Date(2012, 1, 9))) == - PosInfInterval!Date(Date(2010, 7, 4))); - - assert(posInfInterval.merge(PosInfInterval!Date(Date(2010, 7, 3))) == PosInfInterval!Date(Date(2010, 7, 3))); - assert(posInfInterval.merge(PosInfInterval!Date(Date(2010, 7, 4))) == PosInfInterval!Date(Date(2010, 7, 4))); - assert(posInfInterval.merge(PosInfInterval!Date(Date(2010, 7, 5))) == PosInfInterval!Date(Date(2010, 7, 4))); - assert(posInfInterval.merge(PosInfInterval!Date(Date(2012, 1, 6))) == PosInfInterval!Date(Date(2010, 7, 4))); - assert(posInfInterval.merge(PosInfInterval!Date(Date(2012, 1, 7))) == PosInfInterval!Date(Date(2010, 7, 4))); - assert(posInfInterval.merge(PosInfInterval!Date(Date(2012, 1, 8))) == PosInfInterval!Date(Date(2010, 7, 4))); - - assert(PosInfInterval!Date(Date(2010, 7, 3)).merge(posInfInterval) == PosInfInterval!Date(Date(2010, 7, 3))); - assert(PosInfInterval!Date(Date(2010, 7, 4)).merge(posInfInterval) == PosInfInterval!Date(Date(2010, 7, 4))); - assert(PosInfInterval!Date(Date(2010, 7, 5)).merge(posInfInterval) == PosInfInterval!Date(Date(2010, 7, 4))); - assert(PosInfInterval!Date(Date(2012, 1, 6)).merge(posInfInterval) == PosInfInterval!Date(Date(2010, 7, 4))); - assert(PosInfInterval!Date(Date(2012, 1, 7)).merge(posInfInterval) == PosInfInterval!Date(Date(2010, 7, 4))); - assert(PosInfInterval!Date(Date(2012, 1, 8)).merge(posInfInterval) == PosInfInterval!Date(Date(2010, 7, 4))); - - static assert(!__traits(compiles, posInfInterval.merge(NegInfInterval!Date(Date(2010, 7, 3))))); - static assert(!__traits(compiles, posInfInterval.merge(NegInfInterval!Date(Date(2010, 7, 4))))); - static assert(!__traits(compiles, posInfInterval.merge(NegInfInterval!Date(Date(2010, 7, 5))))); - static assert(!__traits(compiles, posInfInterval.merge(NegInfInterval!Date(Date(2012, 1, 6))))); - static assert(!__traits(compiles, posInfInterval.merge(NegInfInterval!Date(Date(2012, 1, 7))))); - static assert(!__traits(compiles, posInfInterval.merge(NegInfInterval!Date(Date(2012, 1, 8))))); - - auto interval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); - const cInterval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); - immutable iInterval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); - const cPosInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); - immutable iPosInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); - auto negInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); - const cNegInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); - immutable iNegInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); - assert(!posInfInterval.merge(interval).empty); - assert(!posInfInterval.merge(cInterval).empty); - assert(!posInfInterval.merge(iInterval).empty); - assert(!posInfInterval.merge(posInfInterval).empty); - assert(!posInfInterval.merge(cPosInfInterval).empty); - assert(!posInfInterval.merge(iPosInfInterval).empty); - static assert(!__traits(compiles, posInfInterval.merge(negInfInterval))); - static assert(!__traits(compiles, posInfInterval.merge(cNegInfInterval))); - static assert(!__traits(compiles, posInfInterval.merge(iNegInfInterval))); - assert(!cPosInfInterval.merge(interval).empty); - assert(!cPosInfInterval.merge(cInterval).empty); - assert(!cPosInfInterval.merge(iInterval).empty); - assert(!cPosInfInterval.merge(posInfInterval).empty); - assert(!cPosInfInterval.merge(cPosInfInterval).empty); - assert(!cPosInfInterval.merge(iPosInfInterval).empty); - static assert(!__traits(compiles, cPosInfInterval.merge(negInfInterval))); - static assert(!__traits(compiles, cPosInfInterval.merge(cNegInfInterval))); - static assert(!__traits(compiles, cPosInfInterval.merge(iNegInfInterval))); - assert(!iPosInfInterval.merge(interval).empty); - assert(!iPosInfInterval.merge(cInterval).empty); - assert(!iPosInfInterval.merge(iInterval).empty); - assert(!iPosInfInterval.merge(posInfInterval).empty); - assert(!iPosInfInterval.merge(cPosInfInterval).empty); - assert(!iPosInfInterval.merge(iPosInfInterval).empty); - static assert(!__traits(compiles, iPosInfInterval.merge(negInfInterval))); - static assert(!__traits(compiles, iPosInfInterval.merge(cNegInfInterval))); - static assert(!__traits(compiles, iPosInfInterval.merge(iNegInfInterval))); - - //Verify Examples. - assert(PosInfInterval!Date(Date(1996, 1, 2)).merge(Interval!Date(Date(1990, 7, 6), Date(2000, 8, 2))) == - PosInfInterval!Date(Date(1990, 7, 6))); - assert(PosInfInterval!Date(Date(1996, 1, 2)).merge(Interval!Date(Date(1999, 1, 12), Date(2011, 9, 17))) == - PosInfInterval!Date(Date(1996, 1, 2))); - - assert(PosInfInterval!Date(Date(1996, 1, 2)).merge(PosInfInterval!Date(Date(1990, 7, 6))) == - PosInfInterval!Date(Date(1990, 7, 6))); - assert(PosInfInterval!Date(Date(1996, 1, 2)).merge(PosInfInterval!Date(Date(1999, 1, 12))) == - PosInfInterval!Date(Date(1996, 1, 2))); -} - -//Test PosInfInterval's span(). -@safe unittest -{ - import std.datetime.date; - - auto posInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); - - static void testInterval(scope const PosInfInterval!Date posInfInterval, scope const Interval!Date interval) - { - posInfInterval.span(interval); - } - - assertThrown!DateTimeException(testInterval(posInfInterval, Interval!Date(Date(2010, 7, 4), dur!"days"(0)))); - - assert(posInfInterval.span(posInfInterval) == posInfInterval); - assert(posInfInterval.span(Interval!Date(Date(2010, 7, 1), Date(2010, 7, 3))) == - PosInfInterval!Date(Date(2010, 7, 1))); - assert(posInfInterval.span(Interval!Date(Date(2010, 7, 1), Date(2013, 7, 3))) == - PosInfInterval!Date(Date(2010, 7, 1))); - assert(posInfInterval.span(Interval!Date(Date(2010, 7, 3), Date(2010, 7, 4))) == - PosInfInterval!Date(Date(2010, 7, 3))); - assert(posInfInterval.span(Interval!Date(Date(2010, 7, 3), Date(2010, 7, 5))) == - PosInfInterval!Date(Date(2010, 7, 3))); - assert(posInfInterval.span(Interval!Date(Date(2010, 7, 3), Date(2012, 1, 7))) == - PosInfInterval!Date(Date(2010, 7, 3))); - assert(posInfInterval.span(Interval!Date(Date(2010, 7, 3), Date(2012, 1, 8))) == - PosInfInterval!Date(Date(2010, 7, 3))); - assert(posInfInterval.span(Interval!Date(Date(2010, 7, 5), Date(2012, 1, 6))) == - PosInfInterval!Date(Date(2010, 7, 4))); - assert(posInfInterval.span(Interval!Date(Date(2010, 7, 5), Date(2012, 1, 7))) == - PosInfInterval!Date(Date(2010, 7, 4))); - assert(posInfInterval.span(Interval!Date(Date(2012, 1, 6), Date(2012, 1, 7))) == - PosInfInterval!Date(Date(2010, 7, 4))); - assert(posInfInterval.span(Interval!Date(Date(2012, 1, 6), Date(2012, 1, 8))) == - PosInfInterval!Date(Date(2010, 7, 4))); - assert(posInfInterval.span(Interval!Date(Date(2012, 1, 7), Date(2012, 1, 8))) == - PosInfInterval!Date(Date(2010, 7, 4))); - assert(posInfInterval.span(Interval!Date(Date(2012, 1, 8), Date(2012, 1, 9))) == - PosInfInterval!Date(Date(2010, 7, 4))); - - assert(posInfInterval.span(PosInfInterval!Date(Date(2010, 7, 3))) == PosInfInterval!Date(Date(2010, 7, 3))); - assert(posInfInterval.span(PosInfInterval!Date(Date(2010, 7, 4))) == PosInfInterval!Date(Date(2010, 7, 4))); - assert(posInfInterval.span(PosInfInterval!Date(Date(2010, 7, 5))) == PosInfInterval!Date(Date(2010, 7, 4))); - assert(posInfInterval.span(PosInfInterval!Date(Date(2012, 1, 6))) == PosInfInterval!Date(Date(2010, 7, 4))); - assert(posInfInterval.span(PosInfInterval!Date(Date(2012, 1, 7))) == PosInfInterval!Date(Date(2010, 7, 4))); - assert(posInfInterval.span(PosInfInterval!Date(Date(2012, 1, 8))) == PosInfInterval!Date(Date(2010, 7, 4))); - - assert(PosInfInterval!Date(Date(2010, 7, 3)).span(posInfInterval) == PosInfInterval!Date(Date(2010, 7, 3))); - assert(PosInfInterval!Date(Date(2010, 7, 4)).span(posInfInterval) == PosInfInterval!Date(Date(2010, 7, 4))); - assert(PosInfInterval!Date(Date(2010, 7, 5)).span(posInfInterval) == PosInfInterval!Date(Date(2010, 7, 4))); - assert(PosInfInterval!Date(Date(2012, 1, 6)).span(posInfInterval) == PosInfInterval!Date(Date(2010, 7, 4))); - assert(PosInfInterval!Date(Date(2012, 1, 7)).span(posInfInterval) == PosInfInterval!Date(Date(2010, 7, 4))); - assert(PosInfInterval!Date(Date(2012, 1, 8)).span(posInfInterval) == PosInfInterval!Date(Date(2010, 7, 4))); - - static assert(!__traits(compiles, posInfInterval.span(NegInfInterval!Date(Date(2010, 7, 3))))); - static assert(!__traits(compiles, posInfInterval.span(NegInfInterval!Date(Date(2010, 7, 4))))); - static assert(!__traits(compiles, posInfInterval.span(NegInfInterval!Date(Date(2010, 7, 5))))); - static assert(!__traits(compiles, posInfInterval.span(NegInfInterval!Date(Date(2012, 1, 6))))); - static assert(!__traits(compiles, posInfInterval.span(NegInfInterval!Date(Date(2012, 1, 7))))); - static assert(!__traits(compiles, posInfInterval.span(NegInfInterval!Date(Date(2012, 1, 8))))); - - auto interval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); - const cInterval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); - immutable iInterval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); - const cPosInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); - immutable iPosInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); - auto negInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); - const cNegInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); - immutable iNegInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); - assert(!posInfInterval.span(interval).empty); - assert(!posInfInterval.span(cInterval).empty); - assert(!posInfInterval.span(iInterval).empty); - assert(!posInfInterval.span(posInfInterval).empty); - assert(!posInfInterval.span(cPosInfInterval).empty); - assert(!posInfInterval.span(iPosInfInterval).empty); - static assert(!__traits(compiles, posInfInterval.span(negInfInterval))); - static assert(!__traits(compiles, posInfInterval.span(cNegInfInterval))); - static assert(!__traits(compiles, posInfInterval.span(iNegInfInterval))); - assert(!cPosInfInterval.span(interval).empty); - assert(!cPosInfInterval.span(cInterval).empty); - assert(!cPosInfInterval.span(iInterval).empty); - assert(!cPosInfInterval.span(posInfInterval).empty); - assert(!cPosInfInterval.span(cPosInfInterval).empty); - assert(!cPosInfInterval.span(iPosInfInterval).empty); - static assert(!__traits(compiles, cPosInfInterval.span(negInfInterval))); - static assert(!__traits(compiles, cPosInfInterval.span(cNegInfInterval))); - static assert(!__traits(compiles, cPosInfInterval.span(iNegInfInterval))); - assert(!iPosInfInterval.span(interval).empty); - assert(!iPosInfInterval.span(cInterval).empty); - assert(!iPosInfInterval.span(iInterval).empty); - assert(!iPosInfInterval.span(posInfInterval).empty); - assert(!iPosInfInterval.span(cPosInfInterval).empty); - assert(!iPosInfInterval.span(iPosInfInterval).empty); - static assert(!__traits(compiles, iPosInfInterval.span(negInfInterval))); - static assert(!__traits(compiles, iPosInfInterval.span(cNegInfInterval))); - static assert(!__traits(compiles, iPosInfInterval.span(iNegInfInterval))); - - //Verify Examples. - assert(PosInfInterval!Date(Date(1996, 1, 2)).span(Interval!Date(Date(500, 8, 9), Date(1602, 1, 31))) == - PosInfInterval!Date(Date(500, 8, 9))); - assert(PosInfInterval!Date(Date(1996, 1, 2)).span(Interval!Date(Date(1990, 7, 6), Date(2000, 8, 2))) == - PosInfInterval!Date(Date(1990, 7, 6))); - assert(PosInfInterval!Date(Date(1996, 1, 2)).span(Interval!Date(Date(1999, 1, 12), Date(2011, 9, 17))) == - PosInfInterval!Date(Date(1996, 1, 2))); - - assert(PosInfInterval!Date(Date(1996, 1, 2)).span(PosInfInterval!Date(Date(1990, 7, 6))) == - PosInfInterval!Date(Date(1990, 7, 6))); - assert(PosInfInterval!Date(Date(1996, 1, 2)).span(PosInfInterval!Date(Date(1999, 1, 12))) == - PosInfInterval!Date(Date(1996, 1, 2))); -} - -//Test PosInfInterval's shift(). -@safe unittest -{ - import std.datetime.date; - - auto interval = PosInfInterval!Date(Date(2010, 7, 4)); - - static void testInterval(I)(I interval, scope const Duration duration, - scope const I expected, size_t line = __LINE__) - { - interval.shift(duration); - assert(interval == expected); - } - - testInterval(interval, dur!"days"(22), PosInfInterval!Date(Date(2010, 7, 26))); - testInterval(interval, dur!"days"(-22), PosInfInterval!Date(Date(2010, 6, 12))); - - const cInterval = PosInfInterval!Date(Date(2010, 7, 4)); - immutable iInterval = PosInfInterval!Date(Date(2010, 7, 4)); - static assert(!__traits(compiles, cInterval.shift(dur!"days"(5)))); - static assert(!__traits(compiles, iInterval.shift(dur!"days"(5)))); - - //Verify Examples. - auto interval1 = PosInfInterval!Date(Date(1996, 1, 2)); - auto interval2 = PosInfInterval!Date(Date(1996, 1, 2)); - - interval1.shift(dur!"days"(50)); - assert(interval1 == PosInfInterval!Date(Date(1996, 2, 21))); - - interval2.shift(dur!"days"(-50)); - assert(interval2 == PosInfInterval!Date(Date(1995, 11, 13))); -} - -//Test PosInfInterval's shift(int, int, AllowDayOverflow). -@safe unittest -{ - import std.datetime.date; - - { - auto interval = PosInfInterval!Date(Date(2010, 7, 4)); - - static void testInterval(I)(I interval, int years, int months, AllowDayOverflow allow, - in I expected, size_t line = __LINE__) - { - interval.shift(years, months, allow); - assert(interval == expected); - } - - testInterval(interval, 5, 0, AllowDayOverflow.yes, PosInfInterval!Date(Date(2015, 7, 4))); - testInterval(interval, -5, 0, AllowDayOverflow.yes, PosInfInterval!Date(Date(2005, 7, 4))); - - auto interval2 = PosInfInterval!Date(Date(2000, 1, 29)); - - testInterval(interval2, 1, 1, AllowDayOverflow.yes, PosInfInterval!Date(Date(2001, 3, 1))); - testInterval(interval2, 1, -1, AllowDayOverflow.yes, PosInfInterval!Date(Date(2000, 12, 29))); - testInterval(interval2, -1, -1, AllowDayOverflow.yes, PosInfInterval!Date(Date(1998, 12, 29))); - testInterval(interval2, -1, 1, AllowDayOverflow.yes, PosInfInterval!Date(Date(1999, 3, 1))); - - testInterval(interval2, 1, 1, AllowDayOverflow.no, PosInfInterval!Date(Date(2001, 2, 28))); - testInterval(interval2, 1, -1, AllowDayOverflow.no, PosInfInterval!Date(Date(2000, 12, 29))); - testInterval(interval2, -1, -1, AllowDayOverflow.no, PosInfInterval!Date(Date(1998, 12, 29))); - testInterval(interval2, -1, 1, AllowDayOverflow.no, PosInfInterval!Date(Date(1999, 2, 28))); - } - - const cPosInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); - immutable iPosInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); - static assert(!__traits(compiles, cPosInfInterval.shift(1))); - static assert(!__traits(compiles, iPosInfInterval.shift(1))); - - //Verify Examples. - auto interval1 = PosInfInterval!Date(Date(1996, 1, 2)); - auto interval2 = PosInfInterval!Date(Date(1996, 1, 2)); - - interval1.shift(2); - assert(interval1 == PosInfInterval!Date(Date(1998, 1, 2))); - - interval2.shift(-2); - assert(interval2 == PosInfInterval!Date(Date(1994, 1, 2))); -} - -//Test PosInfInterval's expand(). -@safe unittest -{ - import std.datetime.date; - - auto interval = PosInfInterval!Date(Date(2000, 7, 4)); - - static void testInterval(I)(I interval, scope const Duration duration, - scope const I expected, size_t line = __LINE__) - { - interval.expand(duration); - assert(interval == expected); - } - - testInterval(interval, dur!"days"(22), PosInfInterval!Date(Date(2000, 6, 12))); - testInterval(interval, dur!"days"(-22), PosInfInterval!Date(Date(2000, 7, 26))); - - const cInterval = PosInfInterval!Date(Date(2010, 7, 4)); - immutable iInterval = PosInfInterval!Date(Date(2010, 7, 4)); - static assert(!__traits(compiles, cInterval.expand(dur!"days"(5)))); - static assert(!__traits(compiles, iInterval.expand(dur!"days"(5)))); - - //Verify Examples. - auto interval1 = PosInfInterval!Date(Date(1996, 1, 2)); - auto interval2 = PosInfInterval!Date(Date(1996, 1, 2)); - - interval1.expand(dur!"days"(2)); - assert(interval1 == PosInfInterval!Date(Date(1995, 12, 31))); - - interval2.expand(dur!"days"(-2)); - assert(interval2 == PosInfInterval!Date(Date(1996, 1, 4))); -} - -//Test PosInfInterval's expand(int, int, AllowDayOverflow). -@safe unittest -{ - import std.datetime.date; - - { - auto interval = PosInfInterval!Date(Date(2000, 7, 4)); - - static void testInterval(I)(I interval, int years, int months, AllowDayOverflow allow, - in I expected, size_t line = __LINE__) - { - interval.expand(years, months, allow); - assert(interval == expected); - } - - testInterval(interval, 5, 0, AllowDayOverflow.yes, PosInfInterval!Date(Date(1995, 7, 4))); - testInterval(interval, -5, 0, AllowDayOverflow.yes, PosInfInterval!Date(Date(2005, 7, 4))); - - auto interval2 = PosInfInterval!Date(Date(2000, 1, 29)); - - testInterval(interval2, 1, 1, AllowDayOverflow.yes, PosInfInterval!Date(Date(1998, 12, 29))); - testInterval(interval2, 1, -1, AllowDayOverflow.yes, PosInfInterval!Date(Date(1999, 3, 1))); - testInterval(interval2, -1, -1, AllowDayOverflow.yes, PosInfInterval!Date(Date(2001, 3, 1))); - testInterval(interval2, -1, 1, AllowDayOverflow.yes, PosInfInterval!Date(Date(2000, 12, 29))); - - testInterval(interval2, 1, 1, AllowDayOverflow.no, PosInfInterval!Date(Date(1998, 12, 29))); - testInterval(interval2, 1, -1, AllowDayOverflow.no, PosInfInterval!Date(Date(1999, 2, 28))); - testInterval(interval2, -1, -1, AllowDayOverflow.no, PosInfInterval!Date(Date(2001, 2, 28))); - testInterval(interval2, -1, 1, AllowDayOverflow.no, PosInfInterval!Date(Date(2000, 12, 29))); - } - - const cPosInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); - immutable iPosInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); - static assert(!__traits(compiles, cPosInfInterval.expand(1))); - static assert(!__traits(compiles, iPosInfInterval.expand(1))); - - //Verify Examples. - auto interval1 = PosInfInterval!Date(Date(1996, 1, 2)); - auto interval2 = PosInfInterval!Date(Date(1996, 1, 2)); - - interval1.expand(2); - assert(interval1 == PosInfInterval!Date(Date(1994, 1, 2))); - - interval2.expand(-2); - assert(interval2 == PosInfInterval!Date(Date(1998, 1, 2))); -} - -//Test PosInfInterval's fwdRange(). -@system unittest -{ - import std.datetime.date; - - auto posInfInterval = PosInfInterval!Date(Date(2010, 9, 19)); - - static void testInterval(PosInfInterval!Date posInfInterval) - { - posInfInterval.fwdRange(everyDayOfWeek!(Date, Direction.bwd)(DayOfWeek.fri)).popFront(); - } - - assertThrown!DateTimeException(testInterval(posInfInterval)); - - assert(PosInfInterval!Date(Date(2010, 9, 12)).fwdRange(everyDayOfWeek!Date(DayOfWeek.fri)).front == - Date(2010, 9, 12)); - - assert(PosInfInterval!Date(Date(2010, 9, 12)).fwdRange(everyDayOfWeek!Date(DayOfWeek.fri), PopFirst.yes).front == - Date(2010, 9, 17)); - - //Verify Examples. - auto interval = PosInfInterval!Date(Date(2010, 9, 1)); - auto func = delegate (scope const Date date) - { - if ((date.day & 1) == 0) - return date + dur!"days"(2); - return date + dur!"days"(1); - }; - auto range = interval.fwdRange(func); - - assert(range.front == Date(2010, 9, 1)); //An odd day. Using PopFirst.yes would have made this Date(2010, 9, 2). - - range.popFront(); - assert(range.front == Date(2010, 9, 2)); - - range.popFront(); - assert(range.front == Date(2010, 9, 4)); - - range.popFront(); - assert(range.front == Date(2010, 9, 6)); - - range.popFront(); - assert(range.front == Date(2010, 9, 8)); - - range.popFront(); - assert(!range.empty); - - const cPosInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); - immutable iPosInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); - assert(!cPosInfInterval.fwdRange(everyDayOfWeek!Date(DayOfWeek.fri)).empty); - assert(!iPosInfInterval.fwdRange(everyDayOfWeek!Date(DayOfWeek.fri)).empty); -} - -//Test PosInfInterval's toString(). -@safe unittest -{ - import std.datetime.date; - assert(PosInfInterval!Date(Date(2010, 7, 4)).toString() == "[2010-Jul-04 - ∞)"); - - const cPosInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); - immutable iPosInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); - assert(cPosInfInterval.toString()); - assert(iPosInfInterval.toString()); -} - - -/++ - Represents an interval of time which has negative infinity as its starting - point. - - Any ranges which iterate over a `NegInfInterval` are infinite. So, the - main purpose of using `NegInfInterval` is to create an infinite range - which starts at negative infinity and goes to a fixed end point. - Iterate over it in reverse. - +/ -struct NegInfInterval(TP) -{ -public: - - /++ - Params: - end = The time point which ends the interval. - - Example: --------------------- -auto interval = PosInfInterval!Date(Date(1996, 1, 2)); --------------------- - +/ - this(scope const TP end) pure nothrow - { - _end = cast(TP) end; - } - - - /++ - Params: - rhs = The `NegInfInterval` to assign to this one. - +/ - ref NegInfInterval opAssign(const ref NegInfInterval rhs) pure nothrow - { - _end = cast(TP) rhs._end; - return this; - } - - - /++ - Params: - rhs = The `NegInfInterval` to assign to this one. - +/ - ref NegInfInterval opAssign(NegInfInterval rhs) pure nothrow - { - _end = cast(TP) rhs._end; - return this; - } - - - /++ - The end point of the interval. It is excluded from the interval. - - Example: --------------------- -assert(NegInfInterval!Date(Date(2012, 3, 1)).end == Date(2012, 3, 1)); --------------------- - +/ - @property TP end() const pure nothrow - { - return cast(TP)_end; - } - - - /++ - The end point of the interval. It is excluded from the interval. - - Params: - timePoint = The time point to set end to. - +/ - @property void end(TP timePoint) pure nothrow - { - _end = timePoint; - } - - - /++ - Whether the interval's length is 0. Always returns false. - - Example: --------------------- -assert(!NegInfInterval!Date(Date(1996, 1, 2)).empty); --------------------- - +/ - enum bool empty = false; - - - /++ - Whether the given time point is within this interval. - - Params: - timePoint = The time point to check for inclusion in this interval. - - Example: --------------------- -assert(NegInfInterval!Date(Date(2012, 3, 1)).contains(Date(1994, 12, 24))); -assert(NegInfInterval!Date(Date(2012, 3, 1)).contains(Date(2000, 1, 5))); -assert(!NegInfInterval!Date(Date(2012, 3, 1)).contains(Date(2012, 3, 1))); --------------------- - +/ - bool contains(TP timePoint) const pure nothrow - { - return timePoint < _end; - } - - - /++ - Whether the given interval is completely within this interval. - - Params: - interval = The interval to check for inclusion in this interval. - - Throws: - $(REF DateTimeException,std,datetime,date) if the given interval - is empty. - - Example: --------------------- -assert(NegInfInterval!Date(Date(2012, 3, 1)).contains( - Interval!Date(Date(1990, 7, 6), Date(2000, 8, 2)))); - -assert(NegInfInterval!Date(Date(2012, 3, 1)).contains( - Interval!Date(Date(1999, 1, 12), Date(2011, 9, 17)))); - -assert(!NegInfInterval!Date(Date(2012, 3, 1)).contains( - Interval!Date(Date(1998, 2, 28), Date(2013, 5, 1)))); --------------------- - +/ - bool contains(scope const Interval!TP interval) const pure - { - interval._enforceNotEmpty(); - return interval._end <= _end; - } - - - /++ - Whether the given interval is completely within this interval. - - Always returns false because an interval beginning at negative - infinity can never contain an interval going to positive infinity. - - Params: - interval = The interval to check for inclusion in this interval. - - Example: --------------------- -assert(!NegInfInterval!Date(Date(2012, 3, 1)).contains( - PosInfInterval!Date(Date(1999, 5, 4)))); --------------------- - +/ - bool contains(scope const PosInfInterval!TP interval) const pure nothrow - { - return false; - } - - - /++ - Whether the given interval is completely within this interval. - - Params: - interval = The interval to check for inclusion in this interval. - - Example: --------------------- -assert(NegInfInterval!Date(Date(2012, 3, 1)).contains( - NegInfInterval!Date(Date(1996, 5, 4)))); - -assert(!NegInfInterval!Date(Date(2012, 3, 1)).contains( - NegInfInterval!Date(Date(2013, 7, 9)))); --------------------- - +/ - bool contains(scope const NegInfInterval interval) const pure nothrow - { - return interval._end <= _end; - } - - - /++ - Whether this interval is before the given time point. - - Params: - timePoint = The time point to check whether this interval is - before it. - - Example: --------------------- -assert(!NegInfInterval!Date(Date(2012, 3, 1)).isBefore(Date(1994, 12, 24))); -assert(!NegInfInterval!Date(Date(2012, 3, 1)).isBefore(Date(2000, 1, 5))); -assert(NegInfInterval!Date(Date(2012, 3, 1)).isBefore(Date(2012, 3, 1))); --------------------- - +/ - bool isBefore(scope const TP timePoint) const pure nothrow - { - return timePoint >= _end; - } - - - /++ - Whether this interval is before the given interval and does not - intersect it. - - Params: - interval = The interval to check for against this interval. - - Throws: - $(REF DateTimeException,std,datetime,date) if the given interval - is empty - - Example: --------------------- -assert(!NegInfInterval!Date(Date(2012, 3, 1)).isBefore( - Interval!Date(Date(1990, 7, 6), Date(2000, 8, 2)))); - -assert(!NegInfInterval!Date(Date(2012, 3, 1)).isBefore( - Interval!Date(Date(1999, 1, 12), Date(2011, 9, 17)))); - -assert(NegInfInterval!Date(Date(2012, 3, 1)).isBefore( - Interval!Date(Date(2022, 10, 19), Date(2027, 6, 3)))); --------------------- - +/ - bool isBefore(scope const Interval!TP interval) const pure - { - interval._enforceNotEmpty(); - return _end <= interval._begin; - } - - - /++ - Whether this interval is before the given interval and does not - intersect it. - - Params: - interval = The interval to check for against this interval. - - Example: --------------------- -assert(!NegInfInterval!Date(Date(2012, 3, 1)).isBefore( - PosInfInterval!Date(Date(1999, 5, 4)))); - -assert(NegInfInterval!Date(Date(2012, 3, 1)).isBefore( - PosInfInterval!Date(Date(2012, 3, 1)))); --------------------- - +/ - bool isBefore(scope const PosInfInterval!TP interval) const pure nothrow - { - return _end <= interval._begin; - } - - - /++ - Whether this interval is before the given interval and does not - intersect it. - - Always returns false because an interval beginning at negative - infinity can never be before another interval beginning at negative - infinity. - - Params: - interval = The interval to check for against this interval. - - Example: --------------------- -assert(!NegInfInterval!Date(Date(2012, 3, 1)).isBefore( - NegInfInterval!Date(Date(1996, 5, 4)))); - -assert(!NegInfInterval!Date(Date(2012, 3, 1)).isBefore( - NegInfInterval!Date(Date(2013, 7, 9)))); --------------------- - +/ - bool isBefore(scope const NegInfInterval interval) const pure nothrow - { - return false; - } - - - /++ - Whether this interval is after the given time point. - - Always returns false because an interval beginning at negative infinity - can never be after any time point. - - Params: - timePoint = The time point to check whether this interval is after - it. - - Example: --------------------- -assert(!NegInfInterval!Date(Date(2012, 3, 1)).isAfter(Date(1994, 12, 24))); -assert(!NegInfInterval!Date(Date(2012, 3, 1)).isAfter(Date(2000, 1, 5))); -assert(!NegInfInterval!Date(Date(2012, 3, 1)).isAfter(Date(2012, 3, 1))); --------------------- - +/ - bool isAfter(scope const TP timePoint) const pure nothrow - { - return false; - } - - - /++ - Whether this interval is after the given interval and does not - intersect it. - - Always returns false (unless the given interval is empty) because an - interval beginning at negative infinity can never be after any other - interval. - - Params: - interval = The interval to check against this interval. - - Throws: - $(REF DateTimeException,std,datetime,date) if the given interval - is empty. - - Example: --------------------- -assert(!NegInfInterval!Date(Date(2012, 3, 1)).isAfter( - Interval!Date(Date(1990, 7, 6), Date(2000, 8, 2)))); - -assert(!NegInfInterval!Date(Date(2012, 3, 1)).isAfter( - Interval!Date(Date(1999, 1, 12), Date(2011, 9, 17)))); - -assert(!NegInfInterval!Date(Date(2012, 3, 1)).isAfter( - Interval!Date(Date(2022, 10, 19), Date(2027, 6, 3)))); --------------------- - +/ - bool isAfter(scope const Interval!TP interval) const pure - { - interval._enforceNotEmpty(); - return false; - } - - - /++ - Whether this interval is after the given interval and does not intersect - it. - - Always returns false because an interval beginning at negative infinity - can never be after any other interval. - - Params: - interval = The interval to check against this interval. - - Example: --------------------- -assert(!NegInfInterval!Date(Date(2012, 3, 1)).isAfter( - PosInfInterval!Date(Date(1999, 5, 4)))); - -assert(!NegInfInterval!Date(Date(2012, 3, 1)).isAfter( - PosInfInterval!Date(Date(2012, 3, 1)))); --------------------- - +/ - bool isAfter(scope const PosInfInterval!TP interval) const pure nothrow - { - return false; - } - - - /++ - Whether this interval is after the given interval and does not intersect - it. - - Always returns false because an interval beginning at negative infinity - can never be after any other interval. - - Params: - interval = The interval to check against this interval. - - Example: --------------------- -assert(!NegInfInterval!Date(Date(2012, 3, 1)).isAfter( - NegInfInterval!Date(Date(1996, 5, 4)))); - -assert(!NegInfInterval!Date(Date(2012, 3, 1)).isAfter( - NegInfInterval!Date(Date(2013, 7, 9)))); --------------------- - +/ - bool isAfter(scope const NegInfInterval interval) const pure nothrow - { - return false; - } - - - /++ - Whether the given interval overlaps this interval. - - Params: - interval = The interval to check for intersection with this interval. - - Throws: - $(REF DateTimeException,std,datetime,date) if the given interval - is empty. - - Example: --------------------- -assert(NegInfInterval!Date(Date(2012, 3, 1)).intersects( - Interval!Date(Date(1990, 7, 6), Date(2000, 8, 2)))); - -assert(NegInfInterval!Date(Date(2012, 3, 1)).intersects( - Interval!Date(Date(1999, 1, 12), Date(2011, 9, 17)))); - -assert(!NegInfInterval!Date(Date(2012, 3, 1)).intersects( - Interval!Date(Date(2022, 10, 19), Date(2027, 6, 3)))); --------------------- - +/ - bool intersects(scope const Interval!TP interval) const pure - { - interval._enforceNotEmpty(); - return interval._begin < _end; - } - - - /++ - Whether the given interval overlaps this interval. - - Params: - interval = The interval to check for intersection with this - interval. - - Example: --------------------- -assert(NegInfInterval!Date(Date(2012, 3, 1)).intersects( - PosInfInterval!Date(Date(1999, 5, 4)))); - -assert(!NegInfInterval!Date(Date(2012, 3, 1)).intersects( - PosInfInterval!Date(Date(2012, 3, 1)))); --------------------- - +/ - bool intersects(scope const PosInfInterval!TP interval) const pure nothrow - { - return interval._begin < _end; - } - - - /++ - Whether the given interval overlaps this interval. - - Always returns true because two intervals beginning at negative infinity - always overlap. - - Params: - interval = The interval to check for intersection with this interval. - - Example: --------------------- -assert(NegInfInterval!Date(Date(2012, 3, 1)).intersects( - NegInfInterval!Date(Date(1996, 5, 4)))); - -assert(NegInfInterval!Date(Date(2012, 3, 1)).intersects( - NegInfInterval!Date(Date(2013, 7, 9)))); --------------------- - +/ - bool intersects(scope const NegInfInterval!TP interval) const pure nothrow - { - return true; - } - - - /++ - Returns the intersection of two intervals - - Params: - interval = The interval to intersect with this interval. - - Throws: - $(REF DateTimeException,std,datetime,date) if the two intervals do - not intersect or if the given interval is empty. - - Example: --------------------- -assert(NegInfInterval!Date(Date(2012, 3, 1)).intersection( - Interval!Date(Date(1990, 7, 6), Date(2000, 8, 2))) == - Interval!Date(Date(1990, 7 , 6), Date(2000, 8, 2))); - -assert(NegInfInterval!Date(Date(2012, 3, 1)).intersection( - Interval!Date(Date(1999, 1, 12), Date(2015, 9, 2))) == - Interval!Date(Date(1999, 1 , 12), Date(2012, 3, 1))); --------------------- - +/ - Interval!TP intersection(scope const Interval!TP interval) const - { - import std.format : format; - - enforce(this.intersects(interval), - new DateTimeException(format("%s and %s do not intersect.", this, interval))); - - auto end = _end < interval._end ? _end : interval._end; - - return Interval!TP(interval._begin, end); - } - - - /++ - Returns the intersection of two intervals - - Params: - interval = The interval to intersect with this interval. - - Throws: - $(REF DateTimeException,std,datetime,date) if the two intervals do - not intersect. - - Example: --------------------- -assert(NegInfInterval!Date(Date(2012, 3, 1)).intersection( - PosInfInterval!Date(Date(1990, 7, 6))) == - Interval!Date(Date(1990, 7 , 6), Date(2012, 3, 1))); - -assert(NegInfInterval!Date(Date(2012, 3, 1)).intersection( - PosInfInterval!Date(Date(1999, 1, 12))) == - Interval!Date(Date(1999, 1 , 12), Date(2012, 3, 1))); --------------------- - +/ - Interval!TP intersection(scope const PosInfInterval!TP interval) const - { - import std.format : format; - - enforce(this.intersects(interval), - new DateTimeException(format("%s and %s do not intersect.", this, interval))); - - return Interval!TP(interval._begin, _end); - } - - - /++ - Returns the intersection of two intervals - - Params: - interval = The interval to intersect with this interval. - - Example: --------------------- -assert(NegInfInterval!Date(Date(2012, 3, 1)).intersection( - NegInfInterval!Date(Date(1999, 7, 6))) == - NegInfInterval!Date(Date(1999, 7 , 6))); - -assert(NegInfInterval!Date(Date(2012, 3, 1)).intersection( - NegInfInterval!Date(Date(2013, 1, 12))) == - NegInfInterval!Date(Date(2012, 3 , 1))); --------------------- - +/ - NegInfInterval intersection(scope const NegInfInterval interval) const nothrow - { - return NegInfInterval(_end < interval._end ? _end : interval._end); - } - - - /++ - Whether the given interval is adjacent to this interval. - - Params: - interval = The interval to check whether its adjecent to this - interval. - - Throws: - $(REF DateTimeException,std,datetime,date) if the given interval - is empty. - - Example: --------------------- -assert(!NegInfInterval!Date(Date(2012, 3, 1)).isAdjacent( - Interval!Date(Date(1990, 7, 6), Date(2000, 8, 2)))); - -assert(!NegInfInterval!Date(Date(2012, 3, 1)).isAdjacent( - Interval!Date(Date(1999, 1, 12), Date(2012, 3, 1)))); - -assert(NegInfInterval!Date(Date(2012, 3, 1)).isAdjacent( - Interval!Date(Date(2012, 3, 1), Date(2019, 2, 2)))); - -assert(!NegInfInterval!Date(Date(2012, 3, 1)).isAdjacent( - Interval!Date(Date(2022, 10, 19), Date(2027, 6, 3)))); --------------------- - +/ - bool isAdjacent(scope const Interval!TP interval) const pure - { - interval._enforceNotEmpty(); - return interval._begin == _end; - } - - - /++ - Whether the given interval is adjacent to this interval. - - Params: - interval = The interval to check whether its adjecent to this - interval. - - Example: --------------------- -assert(!NegInfInterval!Date(Date(2012, 3, 1)).isAdjacent( - PosInfInterval!Date(Date(1999, 5, 4)))); - -assert(NegInfInterval!Date(Date(2012, 3, 1)).isAdjacent( - PosInfInterval!Date(Date(2012, 3, 1)))); --------------------- - +/ - bool isAdjacent(scope const PosInfInterval!TP interval) const pure nothrow - { - return interval._begin == _end; - } - - - /++ - Whether the given interval is adjacent to this interval. - - Always returns false because two intervals beginning at negative - infinity can never be adjacent to one another. - - Params: - interval = The interval to check whether its adjecent to this - interval. - - Example: --------------------- -assert(!NegInfInterval!Date(Date(2012, 3, 1)).isAdjacent( - NegInfInterval!Date(Date(1996, 5, 4)))); - -assert(!NegInfInterval!Date(Date(2012, 3, 1)).isAdjacent( - NegInfInterval!Date(Date(2012, 3, 1)))); --------------------- - +/ - bool isAdjacent(scope const NegInfInterval interval) const pure nothrow - { - return false; - } - - - /++ - Returns the union of two intervals - - Params: - interval = The interval to merge with this interval. - - Throws: - $(REF DateTimeException,std,datetime,date) if the two intervals do - not intersect and are not adjacent or if the given interval is empty. - - Note: - There is no overload for `merge` which takes a - `PosInfInterval`, because an interval - going from negative infinity to positive infinity - is not possible. - - Example: --------------------- -assert(NegInfInterval!Date(Date(2012, 3, 1)).merge( - Interval!Date(Date(1990, 7, 6), Date(2000, 8, 2))) == - NegInfInterval!Date(Date(2012, 3 , 1))); - -assert(NegInfInterval!Date(Date(2012, 3, 1)).merge( - Interval!Date(Date(1999, 1, 12), Date(2015, 9, 2))) == - NegInfInterval!Date(Date(2015, 9 , 2))); --------------------- - +/ - NegInfInterval merge(scope const Interval!TP interval) const - { - import std.format : format; - - enforce(this.isAdjacent(interval) || this.intersects(interval), - new DateTimeException(format("%s and %s are not adjacent and do not intersect.", this, interval))); - - return NegInfInterval(_end > interval._end ? _end : interval._end); - } - - - /++ - Returns the union of two intervals - - Params: - interval = The interval to merge with this interval. - - Note: - There is no overload for `merge` which takes a - `PosInfInterval`, because an interval - going from negative infinity to positive infinity - is not possible. - - Example: --------------------- -assert(NegInfInterval!Date(Date(2012, 3, 1)).merge( - NegInfInterval!Date(Date(1999, 7, 6))) == - NegInfInterval!Date(Date(2012, 3 , 1))); - -assert(NegInfInterval!Date(Date(2012, 3, 1)).merge( - NegInfInterval!Date(Date(2013, 1, 12))) == - NegInfInterval!Date(Date(2013, 1 , 12))); --------------------- - +/ - NegInfInterval merge(scope const NegInfInterval interval) const pure nothrow - { - return NegInfInterval(_end > interval._end ? _end : interval._end); - } - - - /++ - Returns an interval that covers from the earliest time point of two - intervals up to (but not including) the latest time point of two - intervals. - - Params: - interval = The interval to create a span together with this - interval. - - Throws: - $(REF DateTimeException,std,datetime,date) if the given interval - is empty. - - Note: - There is no overload for `span` which takes a - `PosInfInterval`, because an interval - going from negative infinity to positive infinity - is not possible. - - Example: --------------------- -assert(NegInfInterval!Date(Date(2012, 3, 1)).span( - Interval!Date(Date(1990, 7, 6), Date(2000, 8, 2))) == - NegInfInterval!Date(Date(2012, 3 , 1))); - -assert(NegInfInterval!Date(Date(2012, 3, 1)).span( - Interval!Date(Date(1999, 1, 12), Date(2015, 9, 2))) == - NegInfInterval!Date(Date(2015, 9 , 2))); - -assert(NegInfInterval!Date(Date(1600, 1, 7)).span( - Interval!Date(Date(2012, 3, 11), Date(2017, 7, 1))) == - NegInfInterval!Date(Date(2017, 7 , 1))); --------------------- - +/ - NegInfInterval span(scope const Interval!TP interval) const pure - { - interval._enforceNotEmpty(); - return NegInfInterval(_end > interval._end ? _end : interval._end); - } - - - /++ - Returns an interval that covers from the earliest time point of two - intervals up to (but not including) the latest time point of two - intervals. - - Params: - interval = The interval to create a span together with this - interval. - - Note: - There is no overload for `span` which takes a - `PosInfInterval`, because an interval - going from negative infinity to positive infinity - is not possible. - - Example: --------------------- -assert(NegInfInterval!Date(Date(2012, 3, 1)).span( - NegInfInterval!Date(Date(1999, 7, 6))) == - NegInfInterval!Date(Date(2012, 3 , 1))); - -assert(NegInfInterval!Date(Date(2012, 3, 1)).span( - NegInfInterval!Date(Date(2013, 1, 12))) == - NegInfInterval!Date(Date(2013, 1 , 12))); --------------------- - +/ - NegInfInterval span(scope const NegInfInterval interval) const pure nothrow - { - return NegInfInterval(_end > interval._end ? _end : interval._end); - } - - - /++ - Shifts the `end` of this interval forward or backwards in time by the - given duration (a positive duration shifts the interval forward; a - negative duration shifts it backward). Effectively, it does - $(D end += duration). - - Params: - duration = The duration to shift the interval by. - - Example: --------------------- -auto interval1 = NegInfInterval!Date(Date(2012, 4, 5)); -auto interval2 = NegInfInterval!Date(Date(2012, 4, 5)); - -interval1.shift(dur!"days"(50)); -assert(interval1 == NegInfInterval!Date(Date(2012, 5, 25))); - -interval2.shift(dur!"days"(-50)); -assert(interval2 == NegInfInterval!Date( Date(2012, 2, 15))); --------------------- - +/ - void shift(D)(D duration) pure nothrow - if (__traits(compiles, end + duration)) - { - _end += duration; - } - - - static if (__traits(compiles, end.add!"months"(1)) && - __traits(compiles, end.add!"years"(1))) - { - /++ - Shifts the `end` of this interval forward or backwards in time by - the given number of years and/or months (a positive number of years - and months shifts the interval forward; a negative number shifts it - backward). It adds the years the given years and months to end. It - effectively calls `add!"years"()` and then `add!"months"()` - on end with the given number of years and months. - - Params: - years = The number of years to shift the interval by. - months = The number of months to shift the interval by. - allowOverflow = Whether the days should be allowed to overflow - on `end`, causing its month to increment. - - Throws: - $(REF DateTimeException,std,datetime,date) if empty is true or - if the resulting interval would be invalid. - - Example: --------------------- -auto interval1 = NegInfInterval!Date(Date(2012, 3, 1)); -auto interval2 = NegInfInterval!Date(Date(2012, 3, 1)); - -interval1.shift(2); -assert(interval1 == NegInfInterval!Date(Date(2014, 3, 1))); - -interval2.shift(-2); -assert(interval2 == NegInfInterval!Date(Date(2010, 3, 1))); --------------------- - +/ - void shift(T)(T years, T months = 0, AllowDayOverflow allowOverflow = AllowDayOverflow.yes) - if (isIntegral!T) - { - auto end = _end; - - end.add!"years"(years, allowOverflow); - end.add!"months"(months, allowOverflow); - - _end = end; - } - } - - - /++ - Expands the interval forwards in time. Effectively, it does - $(D end += duration). - - Params: - duration = The duration to expand the interval by. - - Example: --------------------- -auto interval1 = NegInfInterval!Date(Date(2012, 3, 1)); -auto interval2 = NegInfInterval!Date(Date(2012, 3, 1)); - -interval1.expand(dur!"days"(2)); -assert(interval1 == NegInfInterval!Date(Date(2012, 3, 3))); - -interval2.expand(dur!"days"(-2)); -assert(interval2 == NegInfInterval!Date(Date(2012, 2, 28))); --------------------- - +/ - void expand(D)(D duration) pure nothrow - if (__traits(compiles, end + duration)) - { - _end += duration; - } - - - static if (__traits(compiles, end.add!"months"(1)) && - __traits(compiles, end.add!"years"(1))) - { - /++ - Expands the interval forwards and/or backwards in time. Effectively, - it adds the given number of months/years to end. - - Params: - years = The number of years to expand the interval by. - months = The number of months to expand the interval by. - allowOverflow = Whether the days should be allowed to overflow - on `end`, causing their month to increment. - - Throws: - $(REF DateTimeException,std,datetime,date) if empty is true or - if the resulting interval would be invalid. - - Example: --------------------- -auto interval1 = NegInfInterval!Date(Date(2012, 3, 1)); -auto interval2 = NegInfInterval!Date(Date(2012, 3, 1)); - -interval1.expand(2); -assert(interval1 == NegInfInterval!Date(Date(2014, 3, 1))); - -interval2.expand(-2); -assert(interval2 == NegInfInterval!Date(Date(2010, 3, 1))); --------------------- - +/ - void expand(T)(T years, T months = 0, AllowDayOverflow allowOverflow = AllowDayOverflow.yes) - if (isIntegral!T) - { - auto end = _end; - - end.add!"years"(years, allowOverflow); - end.add!"months"(months, allowOverflow); - - _end = end; - } - } - - - /++ - Returns a range which iterates backwards over the interval, starting - at `end`, using $(D_PARAM func) to generate each successive time - point. - - The range's `front` is the interval's `end`. $(D_PARAM func) is - used to generate the next `front` when `popFront` is called. If - $(D_PARAM popFirst) is `PopFirst.yes`, then `popFront` is called - before the range is returned (so that `front` is a time point which - $(D_PARAM func) would generate). - - If $(D_PARAM func) ever generates a time point greater than or equal to - the current `front` of the range, then a - $(REF DateTimeException,std,datetime,date) will be thrown. - - There are helper functions in this module which generate common - delegates to pass to `bwdRange`. Their documentation starts with - "Range-generating function," to make them easily searchable. - - Params: - func = The function used to generate the time points of the - range over the interval. - popFirst = Whether `popFront` should be called on the range - before returning it. - - Throws: - $(REF DateTimeException,std,datetime,date) if this interval is - empty. - - Warning: - $(D_PARAM func) must be logically pure. Ideally, $(D_PARAM func) - would be a function pointer to a pure function, but forcing - $(D_PARAM func) to be pure is far too restrictive to be useful, and - in order to have the ease of use of having functions which generate - functions to pass to `fwdRange`, $(D_PARAM func) must be a - delegate. - - If $(D_PARAM func) retains state which changes as it is called, then - some algorithms will not work correctly, because the range's - `save` will have failed to have really saved the range's state. - To avoid such bugs, don't pass a delegate which is - not logically pure to `fwdRange`. If $(D_PARAM func) is given the - same time point with two different calls, it must return the same - result both times. - - Of course, none of the functions in this module have this problem, - so it's only relevant for custom delegates. - - Example: --------------------- -auto interval = NegInfInterval!Date(Date(2010, 9, 9)); -auto func = delegate (scope const Date date) //For iterating over even-numbered days. - { - if ((date.day & 1) == 0) - return date - dur!"days"(2); - - return date - dur!"days"(1); - }; -auto range = interval.bwdRange(func); - -assert(range.front == Date(2010, 9, 9)); //An odd day. Using PopFirst.yes would have made this Date(2010, 9, 8). - -range.popFront(); -assert(range.front == Date(2010, 9, 8)); - -range.popFront(); -assert(range.front == Date(2010, 9, 6)); - -range.popFront(); -assert(range.front == Date(2010, 9, 4)); - -range.popFront(); -assert(range.front == Date(2010, 9, 2)); - -range.popFront(); -assert(!range.empty); --------------------- - +/ - NegInfIntervalRange!(TP) bwdRange(TP delegate(scope const TP) func, PopFirst popFirst = PopFirst.no) const - { - auto range = NegInfIntervalRange!(TP)(this, func); - - if (popFirst == PopFirst.yes) - range.popFront(); - - return range; - } - - - /+ - Converts this interval to a string. - +/ - // Due to bug https://issues.dlang.org/show_bug.cgi?id=3715 , we can't - // have versions of toString() with extra modifiers, - // so we define one version with modifiers and one without. - string toString() - { - return _toStringImpl(); - } - - - /++ - Converts this interval to a string. - +/ - // Due to bug https://issues.dlang.org/show_bug.cgi?id=3715 , we can't - // have versions of toString() with extra modifiers, - // so we define one version with modifiers and one without. - string toString() const nothrow - { - return _toStringImpl(); - } - -private: - - /+ - Since we have two versions of toString(), we have _toStringImpl() - so that they can share implementations. - +/ - string _toStringImpl() const nothrow - { - import std.format : format; - try - return format("[-∞ - %s)", _end); - catch (Exception e) - assert(0, "format() threw."); - } - - - TP _end; -} - -//Test NegInfInterval's constructor. -@safe unittest -{ - import std.datetime.date; - import std.datetime.systime; - - NegInfInterval!Date(Date.init); - NegInfInterval!TimeOfDay(TimeOfDay.init); - NegInfInterval!DateTime(DateTime.init); - NegInfInterval!SysTime(SysTime(0)); -} - -//Test NegInfInterval's end. -@safe unittest -{ - import std.datetime.date; - - assert(NegInfInterval!Date(Date(2010, 1, 1)).end == Date(2010, 1, 1)); - assert(NegInfInterval!Date(Date(2010, 1, 1)).end == Date(2010, 1, 1)); - assert(NegInfInterval!Date(Date(1998, 1, 1)).end == Date(1998, 1, 1)); - - const cNegInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); - immutable iNegInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); - assert(cNegInfInterval.end != Date.init); - assert(iNegInfInterval.end != Date.init); - - //Verify Examples. - assert(NegInfInterval!Date(Date(2012, 3, 1)).end == Date(2012, 3, 1)); -} - -//Test NegInfInterval's empty. -@safe unittest -{ - import std.datetime.date; - import std.datetime.systime; - - assert(!NegInfInterval!Date(Date(2010, 1, 1)).empty); - assert(!NegInfInterval!TimeOfDay(TimeOfDay(0, 30, 0)).empty); - assert(!NegInfInterval!DateTime(DateTime(2010, 1, 1, 0, 30, 0)).empty); - assert(!NegInfInterval!SysTime(SysTime(DateTime(2010, 1, 1, 0, 30, 0))).empty); - - const cNegInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); - immutable iNegInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); - assert(!cNegInfInterval.empty); - assert(!iNegInfInterval.empty); - - //Verify Examples. - assert(!NegInfInterval!Date(Date(1996, 1, 2)).empty); -} - -//Test NegInfInterval's contains(time point). -@safe unittest -{ - import std.datetime.date; - - auto negInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); - - assert(negInfInterval.contains(Date(2009, 7, 4))); - assert(negInfInterval.contains(Date(2010, 7, 3))); - assert(negInfInterval.contains(Date(2010, 7, 4))); - assert(negInfInterval.contains(Date(2010, 7, 5))); - assert(negInfInterval.contains(Date(2011, 7, 1))); - assert(negInfInterval.contains(Date(2012, 1, 6))); - assert(!negInfInterval.contains(Date(2012, 1, 7))); - assert(!negInfInterval.contains(Date(2012, 1, 8))); - assert(!negInfInterval.contains(Date(2013, 1, 7))); - - const cdate = Date(2010, 7, 6); - const cNegInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); - immutable iNegInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); - assert(negInfInterval.contains(cdate)); - assert(cNegInfInterval.contains(cdate)); - assert(iNegInfInterval.contains(cdate)); - - //Verify Examples. - assert(NegInfInterval!Date(Date(2012, 3, 1)).contains(Date(1994, 12, 24))); - assert(NegInfInterval!Date(Date(2012, 3, 1)).contains(Date(2000, 1, 5))); - assert(!NegInfInterval!Date(Date(2012, 3, 1)).contains(Date(2012, 3, 1))); -} - -//Test NegInfInterval's contains(Interval). -@safe unittest -{ - import std.datetime.date; - - auto negInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); - - static void testInterval(scope const NegInfInterval!Date negInfInterval, scope const Interval!Date interval) - { - negInfInterval.contains(interval); - } - - assertThrown!DateTimeException(testInterval(negInfInterval, Interval!Date(Date(2010, 7, 4), dur!"days"(0)))); - - assert(negInfInterval.contains(negInfInterval)); - assert(negInfInterval.contains(Interval!Date(Date(2010, 7, 1), Date(2010, 7, 3)))); - assert(!negInfInterval.contains(Interval!Date(Date(2010, 7, 1), Date(2013, 7, 3)))); - assert(negInfInterval.contains(Interval!Date(Date(2010, 7, 3), Date(2010, 7, 4)))); - assert(negInfInterval.contains(Interval!Date(Date(2010, 7, 3), Date(2010, 7, 5)))); - assert(negInfInterval.contains(Interval!Date(Date(2010, 7, 3), Date(2012, 1, 7)))); - assert(!negInfInterval.contains(Interval!Date(Date(2010, 7, 3), Date(2012, 1, 8)))); - assert(negInfInterval.contains(Interval!Date(Date(2010, 7, 5), Date(2012, 1, 6)))); - assert(negInfInterval.contains(Interval!Date(Date(2010, 7, 5), Date(2012, 1, 7)))); - assert(negInfInterval.contains(Interval!Date(Date(2012, 1, 6), Date(2012, 1, 7)))); - assert(!negInfInterval.contains(Interval!Date(Date(2012, 1, 6), Date(2012, 1, 8)))); - assert(!negInfInterval.contains(Interval!Date(Date(2012, 1, 7), Date(2012, 1, 8)))); - assert(!negInfInterval.contains(Interval!Date(Date(2012, 1, 8), Date(2012, 1, 9)))); - - assert(negInfInterval.contains(NegInfInterval!Date(Date(2010, 7, 3)))); - assert(negInfInterval.contains(NegInfInterval!Date(Date(2010, 7, 4)))); - assert(negInfInterval.contains(NegInfInterval!Date(Date(2010, 7, 5)))); - assert(negInfInterval.contains(NegInfInterval!Date(Date(2012, 1, 6)))); - assert(negInfInterval.contains(NegInfInterval!Date(Date(2012, 1, 7)))); - assert(!negInfInterval.contains(NegInfInterval!Date(Date(2012, 1, 8)))); - - assert(!NegInfInterval!Date(Date(2010, 7, 3)).contains(negInfInterval)); - assert(!NegInfInterval!Date(Date(2010, 7, 4)).contains(negInfInterval)); - assert(!NegInfInterval!Date(Date(2010, 7, 5)).contains(negInfInterval)); - assert(!NegInfInterval!Date(Date(2012, 1, 6)).contains(negInfInterval)); - assert(NegInfInterval!Date(Date(2012, 1, 7)).contains(negInfInterval)); - assert(NegInfInterval!Date(Date(2012, 1, 8)).contains(negInfInterval)); - - assert(!negInfInterval.contains(PosInfInterval!Date(Date(2010, 7, 3)))); - assert(!negInfInterval.contains(PosInfInterval!Date(Date(2010, 7, 4)))); - assert(!negInfInterval.contains(PosInfInterval!Date(Date(2010, 7, 5)))); - assert(!negInfInterval.contains(PosInfInterval!Date(Date(2012, 1, 6)))); - assert(!negInfInterval.contains(PosInfInterval!Date(Date(2012, 1, 7)))); - assert(!negInfInterval.contains(PosInfInterval!Date(Date(2012, 1, 8)))); - - auto interval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); - const cInterval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); - immutable iInterval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); - auto posInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); - const cPosInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); - immutable iPosInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); - const cNegInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); - immutable iNegInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); - assert(negInfInterval.contains(interval)); - assert(negInfInterval.contains(cInterval)); - assert(negInfInterval.contains(iInterval)); - assert(!negInfInterval.contains(posInfInterval)); - assert(!negInfInterval.contains(cPosInfInterval)); - assert(!negInfInterval.contains(iPosInfInterval)); - assert(negInfInterval.contains(negInfInterval)); - assert(negInfInterval.contains(cNegInfInterval)); - assert(negInfInterval.contains(iNegInfInterval)); - assert(cNegInfInterval.contains(interval)); - assert(cNegInfInterval.contains(cInterval)); - assert(cNegInfInterval.contains(iInterval)); - assert(!cNegInfInterval.contains(posInfInterval)); - assert(!cNegInfInterval.contains(cPosInfInterval)); - assert(!cNegInfInterval.contains(iPosInfInterval)); - assert(cNegInfInterval.contains(negInfInterval)); - assert(cNegInfInterval.contains(cNegInfInterval)); - assert(cNegInfInterval.contains(iNegInfInterval)); - assert(iNegInfInterval.contains(interval)); - assert(iNegInfInterval.contains(cInterval)); - assert(iNegInfInterval.contains(iInterval)); - assert(!iNegInfInterval.contains(posInfInterval)); - assert(!iNegInfInterval.contains(cPosInfInterval)); - assert(!iNegInfInterval.contains(iPosInfInterval)); - assert(iNegInfInterval.contains(negInfInterval)); - assert(iNegInfInterval.contains(cNegInfInterval)); - assert(iNegInfInterval.contains(iNegInfInterval)); - - //Verify Examples. - assert(NegInfInterval!Date(Date(2012, 3, 1)).contains(Interval!Date(Date(1990, 7, 6), Date(2000, 8, 2)))); - assert(NegInfInterval!Date(Date(2012, 3, 1)).contains(Interval!Date(Date(1999, 1, 12), Date(2011, 9, 17)))); - assert(!NegInfInterval!Date(Date(2012, 3, 1)).contains(Interval!Date(Date(1998, 2, 28), Date(2013, 5, 1)))); - - assert(!NegInfInterval!Date(Date(2012, 3, 1)).contains(PosInfInterval!Date(Date(1999, 5, 4)))); - - assert(NegInfInterval!Date(Date(2012, 3, 1)).contains(NegInfInterval!Date(Date(1996, 5, 4)))); - assert(!NegInfInterval!Date(Date(2012, 3, 1)).contains(NegInfInterval!Date(Date(2013, 7, 9)))); -} - -//Test NegInfInterval's isBefore(time point). -@safe unittest -{ - import std.datetime.date; - - auto negInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); - - assert(!negInfInterval.isBefore(Date(2009, 7, 4))); - assert(!negInfInterval.isBefore(Date(2010, 7, 3))); - assert(!negInfInterval.isBefore(Date(2010, 7, 4))); - assert(!negInfInterval.isBefore(Date(2010, 7, 5))); - assert(!negInfInterval.isBefore(Date(2011, 7, 1))); - assert(!negInfInterval.isBefore(Date(2012, 1, 6))); - assert(negInfInterval.isBefore(Date(2012, 1, 7))); - assert(negInfInterval.isBefore(Date(2012, 1, 8))); - assert(negInfInterval.isBefore(Date(2013, 1, 7))); - - const cdate = Date(2010, 7, 6); - const cNegInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); - immutable iNegInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); - assert(!negInfInterval.isBefore(cdate)); - assert(!cNegInfInterval.isBefore(cdate)); - assert(!iNegInfInterval.isBefore(cdate)); - - //Verify Examples. - assert(!NegInfInterval!Date(Date(2012, 3, 1)).isBefore(Date(1994, 12, 24))); - assert(!NegInfInterval!Date(Date(2012, 3, 1)).isBefore(Date(2000, 1, 5))); - assert(NegInfInterval!Date(Date(2012, 3, 1)).isBefore(Date(2012, 3, 1))); -} - -//Test NegInfInterval's isBefore(Interval). -@safe unittest -{ - import std.datetime.date; - - auto negInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); - - static void testInterval(scope const NegInfInterval!Date negInfInterval, scope const Interval!Date interval) - { - negInfInterval.isBefore(interval); - } - - assertThrown!DateTimeException(testInterval(negInfInterval, Interval!Date(Date(2010, 7, 4), dur!"days"(0)))); - - assert(!negInfInterval.isBefore(negInfInterval)); - assert(!negInfInterval.isBefore(Interval!Date(Date(2010, 7, 1), Date(2010, 7, 3)))); - assert(!negInfInterval.isBefore(Interval!Date(Date(2010, 7, 1), Date(2013, 7, 3)))); - assert(!negInfInterval.isBefore(Interval!Date(Date(2010, 7, 3), Date(2010, 7, 4)))); - assert(!negInfInterval.isBefore(Interval!Date(Date(2010, 7, 3), Date(2010, 7, 5)))); - assert(!negInfInterval.isBefore(Interval!Date(Date(2010, 7, 3), Date(2012, 1, 7)))); - assert(!negInfInterval.isBefore(Interval!Date(Date(2010, 7, 3), Date(2012, 1, 8)))); - assert(!negInfInterval.isBefore(Interval!Date(Date(2010, 7, 5), Date(2012, 1, 6)))); - assert(!negInfInterval.isBefore(Interval!Date(Date(2010, 7, 5), Date(2012, 1, 7)))); - assert(!negInfInterval.isBefore(Interval!Date(Date(2012, 1, 6), Date(2012, 1, 7)))); - assert(!negInfInterval.isBefore(Interval!Date(Date(2012, 1, 6), Date(2012, 1, 8)))); - assert(negInfInterval.isBefore(Interval!Date(Date(2012, 1, 7), Date(2012, 1, 8)))); - assert(negInfInterval.isBefore(Interval!Date(Date(2012, 1, 8), Date(2012, 1, 9)))); - - assert(!negInfInterval.isBefore(NegInfInterval!Date(Date(2010, 7, 3)))); - assert(!negInfInterval.isBefore(NegInfInterval!Date(Date(2010, 7, 4)))); - assert(!negInfInterval.isBefore(NegInfInterval!Date(Date(2010, 7, 5)))); - assert(!negInfInterval.isBefore(NegInfInterval!Date(Date(2012, 1, 6)))); - assert(!negInfInterval.isBefore(NegInfInterval!Date(Date(2012, 1, 7)))); - assert(!negInfInterval.isBefore(NegInfInterval!Date(Date(2012, 1, 8)))); - - assert(!NegInfInterval!Date(Date(2010, 7, 3)).isBefore(negInfInterval)); - assert(!NegInfInterval!Date(Date(2010, 7, 4)).isBefore(negInfInterval)); - assert(!NegInfInterval!Date(Date(2010, 7, 5)).isBefore(negInfInterval)); - assert(!NegInfInterval!Date(Date(2012, 1, 6)).isBefore(negInfInterval)); - assert(!NegInfInterval!Date(Date(2012, 1, 7)).isBefore(negInfInterval)); - assert(!NegInfInterval!Date(Date(2012, 1, 8)).isBefore(negInfInterval)); - - assert(!negInfInterval.isBefore(PosInfInterval!Date(Date(2010, 7, 3)))); - assert(!negInfInterval.isBefore(PosInfInterval!Date(Date(2010, 7, 4)))); - assert(!negInfInterval.isBefore(PosInfInterval!Date(Date(2010, 7, 5)))); - assert(!negInfInterval.isBefore(PosInfInterval!Date(Date(2012, 1, 6)))); - assert(negInfInterval.isBefore(PosInfInterval!Date(Date(2012, 1, 7)))); - assert(negInfInterval.isBefore(PosInfInterval!Date(Date(2012, 1, 8)))); - - auto interval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); - const cInterval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); - immutable iInterval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); - auto posInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); - const cPosInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); - immutable iPosInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); - const cNegInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); - immutable iNegInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); - assert(!negInfInterval.isBefore(interval)); - assert(!negInfInterval.isBefore(cInterval)); - assert(!negInfInterval.isBefore(iInterval)); - assert(!negInfInterval.isBefore(posInfInterval)); - assert(!negInfInterval.isBefore(cPosInfInterval)); - assert(!negInfInterval.isBefore(iPosInfInterval)); - assert(!negInfInterval.isBefore(negInfInterval)); - assert(!negInfInterval.isBefore(cNegInfInterval)); - assert(!negInfInterval.isBefore(iNegInfInterval)); - assert(!cNegInfInterval.isBefore(interval)); - assert(!cNegInfInterval.isBefore(cInterval)); - assert(!cNegInfInterval.isBefore(iInterval)); - assert(!cNegInfInterval.isBefore(posInfInterval)); - assert(!cNegInfInterval.isBefore(cPosInfInterval)); - assert(!cNegInfInterval.isBefore(iPosInfInterval)); - assert(!cNegInfInterval.isBefore(negInfInterval)); - assert(!cNegInfInterval.isBefore(cNegInfInterval)); - assert(!cNegInfInterval.isBefore(iNegInfInterval)); - assert(!iNegInfInterval.isBefore(interval)); - assert(!iNegInfInterval.isBefore(cInterval)); - assert(!iNegInfInterval.isBefore(iInterval)); - assert(!iNegInfInterval.isBefore(posInfInterval)); - assert(!iNegInfInterval.isBefore(cPosInfInterval)); - assert(!iNegInfInterval.isBefore(iPosInfInterval)); - assert(!iNegInfInterval.isBefore(negInfInterval)); - assert(!iNegInfInterval.isBefore(cNegInfInterval)); - assert(!iNegInfInterval.isBefore(iNegInfInterval)); - - //Verify Examples. - assert(!NegInfInterval!Date(Date(2012, 3, 1)).isBefore(Interval!Date(Date(1990, 7, 6), Date(2000, 8, 2)))); - assert(!NegInfInterval!Date(Date(2012, 3, 1)).isBefore(Interval!Date(Date(1999, 1, 12), Date(2011, 9, 17)))); - assert(NegInfInterval!Date(Date(2012, 3, 1)).isBefore(Interval!Date(Date(2022, 10, 19), Date(2027, 6, 3)))); - - assert(!NegInfInterval!Date(Date(2012, 3, 1)).isBefore(PosInfInterval!Date(Date(1999, 5, 4)))); - assert(NegInfInterval!Date(Date(2012, 3, 1)).isBefore(PosInfInterval!Date(Date(2012, 3, 1)))); - - assert(!NegInfInterval!Date(Date(2012, 3, 1)).isBefore(NegInfInterval!Date(Date(1996, 5, 4)))); - assert(!NegInfInterval!Date(Date(2012, 3, 1)).isBefore(NegInfInterval!Date(Date(2013, 7, 9)))); -} - -//Test NegInfInterval's isAfter(time point). -@safe unittest -{ - import std.datetime.date; - - auto negInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); - - assert(!negInfInterval.isAfter(Date(2009, 7, 4))); - assert(!negInfInterval.isAfter(Date(2010, 7, 3))); - assert(!negInfInterval.isAfter(Date(2010, 7, 4))); - assert(!negInfInterval.isAfter(Date(2010, 7, 5))); - assert(!negInfInterval.isAfter(Date(2011, 7, 1))); - assert(!negInfInterval.isAfter(Date(2012, 1, 6))); - assert(!negInfInterval.isAfter(Date(2012, 1, 7))); - assert(!negInfInterval.isAfter(Date(2012, 1, 8))); - assert(!negInfInterval.isAfter(Date(2013, 1, 7))); - - const cdate = Date(2010, 7, 6); - const cNegInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); - immutable iNegInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); - assert(!negInfInterval.isAfter(cdate)); - assert(!cNegInfInterval.isAfter(cdate)); - assert(!iNegInfInterval.isAfter(cdate)); -} - -//Test NegInfInterval's isAfter(Interval). -@safe unittest -{ - import std.datetime.date; - - auto negInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); - - static void testInterval(scope const NegInfInterval!Date negInfInterval, scope const Interval!Date interval) - { - negInfInterval.isAfter(interval); - } - - assertThrown!DateTimeException(testInterval(negInfInterval, Interval!Date(Date(2010, 7, 4), dur!"days"(0)))); - - assert(!negInfInterval.isAfter(negInfInterval)); - assert(!negInfInterval.isAfter(Interval!Date(Date(2010, 7, 1), Date(2010, 7, 3)))); - assert(!negInfInterval.isAfter(Interval!Date(Date(2010, 7, 1), Date(2013, 7, 3)))); - assert(!negInfInterval.isAfter(Interval!Date(Date(2010, 7, 3), Date(2010, 7, 4)))); - assert(!negInfInterval.isAfter(Interval!Date(Date(2010, 7, 3), Date(2010, 7, 5)))); - assert(!negInfInterval.isAfter(Interval!Date(Date(2010, 7, 3), Date(2012, 1, 7)))); - assert(!negInfInterval.isAfter(Interval!Date(Date(2010, 7, 3), Date(2012, 1, 8)))); - assert(!negInfInterval.isAfter(Interval!Date(Date(2010, 7, 5), Date(2012, 1, 6)))); - assert(!negInfInterval.isAfter(Interval!Date(Date(2010, 7, 5), Date(2012, 1, 7)))); - assert(!negInfInterval.isAfter(Interval!Date(Date(2012, 1, 6), Date(2012, 1, 7)))); - assert(!negInfInterval.isAfter(Interval!Date(Date(2012, 1, 6), Date(2012, 1, 8)))); - assert(!negInfInterval.isAfter(Interval!Date(Date(2012, 1, 7), Date(2012, 1, 8)))); - assert(!negInfInterval.isAfter(Interval!Date(Date(2012, 1, 8), Date(2012, 1, 9)))); - - assert(!negInfInterval.isAfter(NegInfInterval!Date(Date(2010, 7, 3)))); - assert(!negInfInterval.isAfter(NegInfInterval!Date(Date(2010, 7, 4)))); - assert(!negInfInterval.isAfter(NegInfInterval!Date(Date(2010, 7, 5)))); - assert(!negInfInterval.isAfter(NegInfInterval!Date(Date(2012, 1, 6)))); - assert(!negInfInterval.isAfter(NegInfInterval!Date(Date(2012, 1, 7)))); - assert(!negInfInterval.isAfter(NegInfInterval!Date(Date(2012, 1, 8)))); - - assert(!NegInfInterval!Date(Date(2010, 7, 3)).isAfter(negInfInterval)); - assert(!NegInfInterval!Date(Date(2010, 7, 4)).isAfter(negInfInterval)); - assert(!NegInfInterval!Date(Date(2010, 7, 5)).isAfter(negInfInterval)); - assert(!NegInfInterval!Date(Date(2012, 1, 6)).isAfter(negInfInterval)); - assert(!NegInfInterval!Date(Date(2012, 1, 7)).isAfter(negInfInterval)); - assert(!NegInfInterval!Date(Date(2012, 1, 8)).isAfter(negInfInterval)); - - assert(!negInfInterval.isAfter(PosInfInterval!Date(Date(2010, 7, 3)))); - assert(!negInfInterval.isAfter(PosInfInterval!Date(Date(2010, 7, 4)))); - assert(!negInfInterval.isAfter(PosInfInterval!Date(Date(2010, 7, 5)))); - assert(!negInfInterval.isAfter(PosInfInterval!Date(Date(2012, 1, 6)))); - assert(!negInfInterval.isAfter(PosInfInterval!Date(Date(2012, 1, 7)))); - assert(!negInfInterval.isAfter(PosInfInterval!Date(Date(2012, 1, 8)))); - - auto interval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); - const cInterval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); - immutable iInterval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); - auto posInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); - const cPosInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); - immutable iPosInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); - const cNegInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); - immutable iNegInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); - assert(!negInfInterval.isAfter(interval)); - assert(!negInfInterval.isAfter(cInterval)); - assert(!negInfInterval.isAfter(iInterval)); - assert(!negInfInterval.isAfter(posInfInterval)); - assert(!negInfInterval.isAfter(cPosInfInterval)); - assert(!negInfInterval.isAfter(iPosInfInterval)); - assert(!negInfInterval.isAfter(negInfInterval)); - assert(!negInfInterval.isAfter(cNegInfInterval)); - assert(!negInfInterval.isAfter(iNegInfInterval)); - assert(!cNegInfInterval.isAfter(interval)); - assert(!cNegInfInterval.isAfter(cInterval)); - assert(!cNegInfInterval.isAfter(iInterval)); - assert(!cNegInfInterval.isAfter(posInfInterval)); - assert(!cNegInfInterval.isAfter(cPosInfInterval)); - assert(!cNegInfInterval.isAfter(iPosInfInterval)); - assert(!cNegInfInterval.isAfter(negInfInterval)); - assert(!cNegInfInterval.isAfter(cNegInfInterval)); - assert(!cNegInfInterval.isAfter(iNegInfInterval)); - assert(!iNegInfInterval.isAfter(interval)); - assert(!iNegInfInterval.isAfter(cInterval)); - assert(!iNegInfInterval.isAfter(iInterval)); - assert(!iNegInfInterval.isAfter(posInfInterval)); - assert(!iNegInfInterval.isAfter(cPosInfInterval)); - assert(!iNegInfInterval.isAfter(iPosInfInterval)); - assert(!iNegInfInterval.isAfter(negInfInterval)); - assert(!iNegInfInterval.isAfter(cNegInfInterval)); - assert(!iNegInfInterval.isAfter(iNegInfInterval)); - - //Verify Examples. - assert(!NegInfInterval!Date(Date(2012, 3, 1)).isAfter(Date(1994, 12, 24))); - assert(!NegInfInterval!Date(Date(2012, 3, 1)).isAfter(Date(2000, 1, 5))); - assert(!NegInfInterval!Date(Date(2012, 3, 1)).isAfter(Date(2012, 3, 1))); - - assert(!NegInfInterval!Date(Date(2012, 3, 1)).isAfter(Interval!Date(Date(1990, 7, 6), Date(2000, 8, 2)))); - assert(!NegInfInterval!Date(Date(2012, 3, 1)).isAfter(Interval!Date(Date(1999, 1, 12), Date(2011, 9, 17)))); - assert(!NegInfInterval!Date(Date(2012, 3, 1)).isAfter(Interval!Date(Date(2022, 10, 19), Date(2027, 6, 3)))); - - assert(!NegInfInterval!Date(Date(2012, 3, 1)).isAfter(PosInfInterval!Date(Date(1999, 5, 4)))); - assert(!NegInfInterval!Date(Date(2012, 3, 1)).isAfter(PosInfInterval!Date(Date(2012, 3, 1)))); - - assert(!NegInfInterval!Date(Date(2012, 3, 1)).isAfter(NegInfInterval!Date(Date(1996, 5, 4)))); - assert(!NegInfInterval!Date(Date(2012, 3, 1)).isAfter(NegInfInterval!Date(Date(2013, 7, 9)))); -} - -//Test NegInfInterval's intersects(). -@safe unittest -{ - import std.datetime.date; - - auto negInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); - - static void testInterval(scope const NegInfInterval!Date negInfInterval, scope const Interval!Date interval) - { - negInfInterval.intersects(interval); - } - - assertThrown!DateTimeException(testInterval(negInfInterval, Interval!Date(Date(2010, 7, 4), dur!"days"(0)))); - - assert(negInfInterval.intersects(negInfInterval)); - assert(negInfInterval.intersects(Interval!Date(Date(2010, 7, 1), Date(2010, 7, 3)))); - assert(negInfInterval.intersects(Interval!Date(Date(2010, 7, 1), Date(2013, 7, 3)))); - assert(negInfInterval.intersects(Interval!Date(Date(2010, 7, 3), Date(2010, 7, 4)))); - assert(negInfInterval.intersects(Interval!Date(Date(2010, 7, 3), Date(2010, 7, 5)))); - assert(negInfInterval.intersects(Interval!Date(Date(2010, 7, 3), Date(2012, 1, 7)))); - assert(negInfInterval.intersects(Interval!Date(Date(2010, 7, 3), Date(2012, 1, 8)))); - assert(negInfInterval.intersects(Interval!Date(Date(2010, 7, 5), Date(2012, 1, 6)))); - assert(negInfInterval.intersects(Interval!Date(Date(2010, 7, 5), Date(2012, 1, 7)))); - assert(negInfInterval.intersects(Interval!Date(Date(2012, 1, 6), Date(2012, 1, 7)))); - assert(negInfInterval.intersects(Interval!Date(Date(2012, 1, 6), Date(2012, 1, 8)))); - assert(!negInfInterval.intersects(Interval!Date(Date(2012, 1, 7), Date(2012, 1, 8)))); - assert(!negInfInterval.intersects(Interval!Date(Date(2012, 1, 8), Date(2012, 1, 9)))); - - assert(negInfInterval.intersects(NegInfInterval!Date(Date(2010, 7, 3)))); - assert(negInfInterval.intersects(NegInfInterval!Date(Date(2010, 7, 4)))); - assert(negInfInterval.intersects(NegInfInterval!Date(Date(2010, 7, 5)))); - assert(negInfInterval.intersects(NegInfInterval!Date(Date(2012, 1, 6)))); - assert(negInfInterval.intersects(NegInfInterval!Date(Date(2012, 1, 7)))); - assert(negInfInterval.intersects(NegInfInterval!Date(Date(2012, 1, 8)))); - - assert(NegInfInterval!Date(Date(2010, 7, 3)).intersects(negInfInterval)); - assert(NegInfInterval!Date(Date(2010, 7, 4)).intersects(negInfInterval)); - assert(NegInfInterval!Date(Date(2010, 7, 5)).intersects(negInfInterval)); - assert(NegInfInterval!Date(Date(2012, 1, 6)).intersects(negInfInterval)); - assert(NegInfInterval!Date(Date(2012, 1, 7)).intersects(negInfInterval)); - assert(NegInfInterval!Date(Date(2012, 1, 8)).intersects(negInfInterval)); - - assert(negInfInterval.intersects(PosInfInterval!Date(Date(2010, 7, 3)))); - assert(negInfInterval.intersects(PosInfInterval!Date(Date(2010, 7, 4)))); - assert(negInfInterval.intersects(PosInfInterval!Date(Date(2010, 7, 5)))); - assert(negInfInterval.intersects(PosInfInterval!Date(Date(2012, 1, 6)))); - assert(!negInfInterval.intersects(PosInfInterval!Date(Date(2012, 1, 7)))); - assert(!negInfInterval.intersects(PosInfInterval!Date(Date(2012, 1, 8)))); - - auto interval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); - const cInterval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); - immutable iInterval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); - auto posInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); - const cPosInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); - immutable iPosInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); - const cNegInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); - immutable iNegInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); - assert(negInfInterval.intersects(interval)); - assert(negInfInterval.intersects(cInterval)); - assert(negInfInterval.intersects(iInterval)); - assert(negInfInterval.intersects(posInfInterval)); - assert(negInfInterval.intersects(cPosInfInterval)); - assert(negInfInterval.intersects(iPosInfInterval)); - assert(negInfInterval.intersects(negInfInterval)); - assert(negInfInterval.intersects(cNegInfInterval)); - assert(negInfInterval.intersects(iNegInfInterval)); - assert(cNegInfInterval.intersects(interval)); - assert(cNegInfInterval.intersects(cInterval)); - assert(cNegInfInterval.intersects(iInterval)); - assert(cNegInfInterval.intersects(posInfInterval)); - assert(cNegInfInterval.intersects(cPosInfInterval)); - assert(cNegInfInterval.intersects(iPosInfInterval)); - assert(cNegInfInterval.intersects(negInfInterval)); - assert(cNegInfInterval.intersects(cNegInfInterval)); - assert(cNegInfInterval.intersects(iNegInfInterval)); - assert(iNegInfInterval.intersects(interval)); - assert(iNegInfInterval.intersects(cInterval)); - assert(iNegInfInterval.intersects(iInterval)); - assert(iNegInfInterval.intersects(posInfInterval)); - assert(iNegInfInterval.intersects(cPosInfInterval)); - assert(iNegInfInterval.intersects(iPosInfInterval)); - assert(iNegInfInterval.intersects(negInfInterval)); - assert(iNegInfInterval.intersects(cNegInfInterval)); - assert(iNegInfInterval.intersects(iNegInfInterval)); - - //Verify Examples. - assert(NegInfInterval!Date(Date(2012, 3, 1)).intersects(Interval!Date(Date(1990, 7, 6), Date(2000, 8, 2)))); - assert(NegInfInterval!Date(Date(2012, 3, 1)).intersects(Interval!Date(Date(1999, 1, 12), Date(2011, 9, 17)))); - assert(!NegInfInterval!Date(Date(2012, 3, 1)).intersects(Interval!Date(Date(2022, 10, 19), Date(2027, 6, 3)))); - - assert(NegInfInterval!Date(Date(2012, 3, 1)).intersects(PosInfInterval!Date(Date(1999, 5, 4)))); - assert(!NegInfInterval!Date(Date(2012, 3, 1)).intersects(PosInfInterval!Date(Date(2012, 3, 1)))); - - assert(NegInfInterval!Date(Date(2012, 3, 1)).intersects(NegInfInterval!Date(Date(1996, 5, 4)))); - assert(NegInfInterval!Date(Date(2012, 3, 1)).intersects(NegInfInterval!Date(Date(2013, 7, 9)))); -} - -//Test NegInfInterval's intersection(). -@safe unittest -{ - import std.datetime.date; - - auto negInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); - - static void testInterval(I, J)(scope const I interval1, scope const J interval2) - { - interval1.intersection(interval2); - } - - assertThrown!DateTimeException(testInterval(negInfInterval, Interval!Date(Date(2010, 7, 4), dur!"days"(0)))); - - assertThrown!DateTimeException(testInterval(negInfInterval, Interval!Date(Date(2012, 1, 7), Date(2012, 1, 8)))); - assertThrown!DateTimeException(testInterval(negInfInterval, Interval!Date(Date(2012, 1, 8), Date(2012, 1, 9)))); - - assertThrown!DateTimeException(testInterval(negInfInterval, PosInfInterval!Date(Date(2012, 1, 7)))); - assertThrown!DateTimeException(testInterval(negInfInterval, PosInfInterval!Date(Date(2012, 1, 8)))); - - assert(negInfInterval.intersection(negInfInterval) == negInfInterval); - assert(negInfInterval.intersection(Interval!Date(Date(2010, 7, 1), Date(2010, 7, 3))) == - Interval!Date(Date(2010, 7, 1), Date(2010, 7, 3))); - assert(negInfInterval.intersection(Interval!Date(Date(2010, 7, 1), Date(2013, 7, 3))) == - Interval!Date(Date(2010, 7, 1), Date(2012, 1, 7))); - assert(negInfInterval.intersection(Interval!Date(Date(2010, 7, 3), Date(2010, 7, 4))) == - Interval!Date(Date(2010, 7, 3), Date(2010, 7, 4))); - assert(negInfInterval.intersection(Interval!Date(Date(2010, 7, 3), Date(2010, 7, 5))) == - Interval!Date(Date(2010, 7, 3), Date(2010, 7, 5))); - assert(negInfInterval.intersection(Interval!Date(Date(2010, 7, 3), Date(2012, 1, 7))) == - Interval!Date(Date(2010, 7, 3), Date(2012, 1, 7))); - assert(negInfInterval.intersection(Interval!Date(Date(2010, 7, 3), Date(2012, 1, 8))) == - Interval!Date(Date(2010, 7, 3), Date(2012, 1, 7))); - assert(negInfInterval.intersection(Interval!Date(Date(2010, 7, 5), Date(2012, 1, 6))) == - Interval!Date(Date(2010, 7, 5), Date(2012, 1, 6))); - assert(negInfInterval.intersection(Interval!Date(Date(2010, 7, 5), Date(2012, 1, 7))) == - Interval!Date(Date(2010, 7, 5), Date(2012, 1, 7))); - assert(negInfInterval.intersection(Interval!Date(Date(2012, 1, 6), Date(2012, 1, 7))) == - Interval!Date(Date(2012, 1, 6), Date(2012, 1, 7))); - assert(negInfInterval.intersection(Interval!Date(Date(2012, 1, 6), Date(2012, 1, 8))) == - Interval!Date(Date(2012, 1, 6), Date(2012, 1, 7))); - - assert(negInfInterval.intersection(NegInfInterval!Date(Date(2010, 7, 3))) == NegInfInterval!Date(Date(2010, 7, 3))); - assert(negInfInterval.intersection(NegInfInterval!Date(Date(2010, 7, 4))) == NegInfInterval!Date(Date(2010, 7, 4))); - assert(negInfInterval.intersection(NegInfInterval!Date(Date(2010, 7, 5))) == NegInfInterval!Date(Date(2010, 7, 5))); - assert(negInfInterval.intersection(NegInfInterval!Date(Date(2012, 1, 6))) == NegInfInterval!Date(Date(2012, 1, 6))); - assert(negInfInterval.intersection(NegInfInterval!Date(Date(2012, 1, 7))) == NegInfInterval!Date(Date(2012, 1, 7))); - assert(negInfInterval.intersection(NegInfInterval!Date(Date(2012, 1, 8))) == NegInfInterval!Date(Date(2012, 1, 7))); - - assert(NegInfInterval!Date(Date(2010, 7, 3)).intersection(negInfInterval) == NegInfInterval!Date(Date(2010, 7, 3))); - assert(NegInfInterval!Date(Date(2010, 7, 4)).intersection(negInfInterval) == NegInfInterval!Date(Date(2010, 7, 4))); - assert(NegInfInterval!Date(Date(2010, 7, 5)).intersection(negInfInterval) == NegInfInterval!Date(Date(2010, 7, 5))); - assert(NegInfInterval!Date(Date(2012, 1, 6)).intersection(negInfInterval) == NegInfInterval!Date(Date(2012, 1, 6))); - assert(NegInfInterval!Date(Date(2012, 1, 7)).intersection(negInfInterval) == NegInfInterval!Date(Date(2012, 1, 7))); - assert(NegInfInterval!Date(Date(2012, 1, 8)).intersection(negInfInterval) == NegInfInterval!Date(Date(2012, 1, 7))); - - assert(negInfInterval.intersection(PosInfInterval!Date(Date(2010, 7, 3))) == - Interval!Date(Date(2010, 7, 3), Date(2012, 1 ,7))); - assert(negInfInterval.intersection(PosInfInterval!Date(Date(2010, 7, 4))) == - Interval!Date(Date(2010, 7, 4), Date(2012, 1 ,7))); - assert(negInfInterval.intersection(PosInfInterval!Date(Date(2010, 7, 5))) == - Interval!Date(Date(2010, 7, 5), Date(2012, 1 ,7))); - assert(negInfInterval.intersection(PosInfInterval!Date(Date(2012, 1, 6))) == - Interval!Date(Date(2012, 1, 6), Date(2012, 1 ,7))); - - auto interval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); - const cInterval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); - immutable iInterval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); - auto posInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); - const cPosInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); - immutable iPosInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); - const cNegInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); - immutable iNegInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); - assert(!negInfInterval.intersection(interval).empty); - assert(!negInfInterval.intersection(cInterval).empty); - assert(!negInfInterval.intersection(iInterval).empty); - assert(!negInfInterval.intersection(posInfInterval).empty); - assert(!negInfInterval.intersection(cPosInfInterval).empty); - assert(!negInfInterval.intersection(iPosInfInterval).empty); - assert(!negInfInterval.intersection(negInfInterval).empty); - assert(!negInfInterval.intersection(cNegInfInterval).empty); - assert(!negInfInterval.intersection(iNegInfInterval).empty); - assert(!cNegInfInterval.intersection(interval).empty); - assert(!cNegInfInterval.intersection(cInterval).empty); - assert(!cNegInfInterval.intersection(iInterval).empty); - assert(!cNegInfInterval.intersection(posInfInterval).empty); - assert(!cNegInfInterval.intersection(cPosInfInterval).empty); - assert(!cNegInfInterval.intersection(iPosInfInterval).empty); - assert(!cNegInfInterval.intersection(negInfInterval).empty); - assert(!cNegInfInterval.intersection(cNegInfInterval).empty); - assert(!cNegInfInterval.intersection(iNegInfInterval).empty); - assert(!iNegInfInterval.intersection(interval).empty); - assert(!iNegInfInterval.intersection(cInterval).empty); - assert(!iNegInfInterval.intersection(iInterval).empty); - assert(!iNegInfInterval.intersection(posInfInterval).empty); - assert(!iNegInfInterval.intersection(cPosInfInterval).empty); - assert(!iNegInfInterval.intersection(iPosInfInterval).empty); - assert(!iNegInfInterval.intersection(negInfInterval).empty); - assert(!iNegInfInterval.intersection(cNegInfInterval).empty); - assert(!iNegInfInterval.intersection(iNegInfInterval).empty); - - //Verify Examples. - assert(NegInfInterval!Date(Date(2012, 3, 1)).intersection(Interval!Date(Date(1990, 7, 6), Date(2000, 8, 2))) == - Interval!Date(Date(1990, 7, 6), Date(2000, 8, 2))); - assert(NegInfInterval!Date(Date(2012, 3, 1)).intersection(Interval!Date(Date(1999, 1, 12), Date(2015, 9, 2))) == - Interval!Date(Date(1999, 1, 12), Date(2012, 3, 1))); - - assert(NegInfInterval!Date(Date(2012, 3, 1)).intersection(PosInfInterval!Date(Date(1990, 7, 6))) == - Interval!Date(Date(1990, 7, 6), Date(2012, 3, 1))); - assert(NegInfInterval!Date(Date(2012, 3, 1)).intersection(PosInfInterval!Date(Date(1999, 1, 12))) == - Interval!Date(Date(1999, 1, 12), Date(2012, 3, 1))); - - assert(NegInfInterval!Date(Date(2012, 3, 1)).intersection(NegInfInterval!Date(Date(1999, 7, 6))) == - NegInfInterval!Date(Date(1999, 7, 6))); - assert(NegInfInterval!Date(Date(2012, 3, 1)).intersection(NegInfInterval!Date(Date(2013, 1, 12))) == - NegInfInterval!Date(Date(2012, 3, 1))); -} - -//Test NegInfInterval's isAdjacent(). -@safe unittest -{ - import std.datetime.date; - - auto negInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); - - static void testInterval(scope const NegInfInterval!Date negInfInterval, scope const Interval!Date interval) - { - negInfInterval.isAdjacent(interval); - } - - assertThrown!DateTimeException(testInterval(negInfInterval, Interval!Date(Date(2010, 7, 4), dur!"days"(0)))); - - assert(!negInfInterval.isAdjacent(negInfInterval)); - assert(!negInfInterval.isAdjacent(Interval!Date(Date(2010, 7, 1), Date(2010, 7, 3)))); - assert(!negInfInterval.isAdjacent(Interval!Date(Date(2010, 7, 1), Date(2013, 7, 3)))); - assert(!negInfInterval.isAdjacent(Interval!Date(Date(2010, 7, 3), Date(2010, 7, 4)))); - assert(!negInfInterval.isAdjacent(Interval!Date(Date(2010, 7, 3), Date(2010, 7, 5)))); - assert(!negInfInterval.isAdjacent(Interval!Date(Date(2010, 7, 3), Date(2012, 1, 7)))); - assert(!negInfInterval.isAdjacent(Interval!Date(Date(2010, 7, 3), Date(2012, 1, 8)))); - assert(!negInfInterval.isAdjacent(Interval!Date(Date(2010, 7, 5), Date(2012, 1, 6)))); - assert(!negInfInterval.isAdjacent(Interval!Date(Date(2010, 7, 5), Date(2012, 1, 7)))); - assert(!negInfInterval.isAdjacent(Interval!Date(Date(2012, 1, 6), Date(2012, 1, 7)))); - assert(!negInfInterval.isAdjacent(Interval!Date(Date(2012, 1, 6), Date(2012, 1, 8)))); - assert(negInfInterval.isAdjacent(Interval!Date(Date(2012, 1, 7), Date(2012, 1, 8)))); - assert(!negInfInterval.isAdjacent(Interval!Date(Date(2012, 1, 8), Date(2012, 1, 9)))); - - assert(!negInfInterval.isAdjacent(NegInfInterval!Date(Date(2010, 7, 3)))); - assert(!negInfInterval.isAdjacent(NegInfInterval!Date(Date(2010, 7, 4)))); - assert(!negInfInterval.isAdjacent(NegInfInterval!Date(Date(2010, 7, 5)))); - assert(!negInfInterval.isAdjacent(NegInfInterval!Date(Date(2012, 1, 6)))); - assert(!negInfInterval.isAdjacent(NegInfInterval!Date(Date(2012, 1, 7)))); - assert(!negInfInterval.isAdjacent(NegInfInterval!Date(Date(2012, 1, 8)))); - - assert(!NegInfInterval!Date(Date(2010, 7, 3)).isAdjacent(negInfInterval)); - assert(!NegInfInterval!Date(Date(2010, 7, 4)).isAdjacent(negInfInterval)); - assert(!NegInfInterval!Date(Date(2010, 7, 5)).isAdjacent(negInfInterval)); - assert(!NegInfInterval!Date(Date(2012, 1, 6)).isAdjacent(negInfInterval)); - assert(!NegInfInterval!Date(Date(2012, 1, 7)).isAdjacent(negInfInterval)); - assert(!NegInfInterval!Date(Date(2012, 1, 8)).isAdjacent(negInfInterval)); - - assert(!negInfInterval.isAdjacent(PosInfInterval!Date(Date(2010, 7, 3)))); - assert(!negInfInterval.isAdjacent(PosInfInterval!Date(Date(2010, 7, 4)))); - assert(!negInfInterval.isAdjacent(PosInfInterval!Date(Date(2010, 7, 5)))); - assert(!negInfInterval.isAdjacent(PosInfInterval!Date(Date(2012, 1, 6)))); - assert(negInfInterval.isAdjacent(PosInfInterval!Date(Date(2012, 1, 7)))); - assert(!negInfInterval.isAdjacent(PosInfInterval!Date(Date(2012, 1, 8)))); - - auto interval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); - const cInterval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); - immutable iInterval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); - auto posInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); - const cPosInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); - immutable iPosInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); - const cNegInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); - immutable iNegInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); - assert(!negInfInterval.isAdjacent(interval)); - assert(!negInfInterval.isAdjacent(cInterval)); - assert(!negInfInterval.isAdjacent(iInterval)); - assert(!negInfInterval.isAdjacent(posInfInterval)); - assert(!negInfInterval.isAdjacent(cPosInfInterval)); - assert(!negInfInterval.isAdjacent(iPosInfInterval)); - assert(!negInfInterval.isAdjacent(negInfInterval)); - assert(!negInfInterval.isAdjacent(cNegInfInterval)); - assert(!negInfInterval.isAdjacent(iNegInfInterval)); - assert(!cNegInfInterval.isAdjacent(interval)); - assert(!cNegInfInterval.isAdjacent(cInterval)); - assert(!cNegInfInterval.isAdjacent(iInterval)); - assert(!cNegInfInterval.isAdjacent(posInfInterval)); - assert(!cNegInfInterval.isAdjacent(cPosInfInterval)); - assert(!cNegInfInterval.isAdjacent(iPosInfInterval)); - assert(!cNegInfInterval.isAdjacent(negInfInterval)); - assert(!cNegInfInterval.isAdjacent(cNegInfInterval)); - assert(!cNegInfInterval.isAdjacent(iNegInfInterval)); - assert(!iNegInfInterval.isAdjacent(interval)); - assert(!iNegInfInterval.isAdjacent(cInterval)); - assert(!iNegInfInterval.isAdjacent(iInterval)); - assert(!iNegInfInterval.isAdjacent(posInfInterval)); - assert(!iNegInfInterval.isAdjacent(cPosInfInterval)); - assert(!iNegInfInterval.isAdjacent(iPosInfInterval)); - assert(!iNegInfInterval.isAdjacent(negInfInterval)); - assert(!iNegInfInterval.isAdjacent(cNegInfInterval)); - assert(!iNegInfInterval.isAdjacent(iNegInfInterval)); - - //Verify Examples. - assert(!NegInfInterval!Date(Date(2012, 3, 1)).isAdjacent(Interval!Date(Date(1990, 7, 6), Date(2000, 8, 2)))); - assert(!NegInfInterval!Date(Date(2012, 3, 1)).isAdjacent(Interval!Date(Date(1999, 1, 12), Date(2012, 3, 1)))); - assert(NegInfInterval!Date(Date(2012, 3, 1)).isAdjacent(Interval!Date(Date(2012, 3, 1), Date(2019, 2, 2)))); - assert(!NegInfInterval!Date(Date(2012, 3, 1)).isAdjacent(Interval!Date(Date(2022, 10, 19), Date(2027, 6, 3)))); - - assert(!NegInfInterval!Date(Date(2012, 3, 1)).isAdjacent(PosInfInterval!Date(Date(1999, 5, 4)))); - assert(NegInfInterval!Date(Date(2012, 3, 1)).isAdjacent(PosInfInterval!Date(Date(2012, 3, 1)))); - - assert(!NegInfInterval!Date(Date(2012, 3, 1)).isAdjacent(NegInfInterval!Date(Date(1996, 5, 4)))); - assert(!NegInfInterval!Date(Date(2012, 3, 1)).isAdjacent(NegInfInterval!Date(Date(2012, 3, 1)))); -} - -//Test NegInfInterval's merge(). -@safe unittest -{ - import std.datetime.date; - - auto negInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); - - static void testInterval(I, J)(scope const I interval1, scope const J interval2) - { - interval1.merge(interval2); - } - - assertThrown!DateTimeException(testInterval(negInfInterval, Interval!Date(Date(2010, 7, 4), dur!"days"(0)))); - - assertThrown!DateTimeException(testInterval(negInfInterval, Interval!Date(Date(2012, 1, 8), Date(2012, 1, 9)))); - - assert(negInfInterval.merge(negInfInterval) == negInfInterval); - assert(negInfInterval.merge(Interval!Date(Date(2010, 7, 1), Date(2010, 7, 3))) == - NegInfInterval!Date(Date(2012, 1, 7))); - assert(negInfInterval.merge(Interval!Date(Date(2010, 7, 1), Date(2013, 7, 3))) == - NegInfInterval!Date(Date(2013, 7, 3))); - assert(negInfInterval.merge(Interval!Date(Date(2010, 7, 3), Date(2010, 7, 4))) == - NegInfInterval!Date(Date(2012, 1, 7))); - assert(negInfInterval.merge(Interval!Date(Date(2010, 7, 3), Date(2010, 7, 5))) == - NegInfInterval!Date(Date(2012, 1, 7))); - assert(negInfInterval.merge(Interval!Date(Date(2010, 7, 3), Date(2012, 1, 7))) == - NegInfInterval!Date(Date(2012, 1, 7))); - assert(negInfInterval.merge(Interval!Date(Date(2010, 7, 3), Date(2012, 1, 8))) == - NegInfInterval!Date(Date(2012, 1, 8))); - assert(negInfInterval.merge(Interval!Date(Date(2010, 7, 5), Date(2012, 1, 6))) == - NegInfInterval!Date(Date(2012, 1, 7))); - assert(negInfInterval.merge(Interval!Date(Date(2010, 7, 5), Date(2012, 1, 7))) == - NegInfInterval!Date(Date(2012, 1, 7))); - assert(negInfInterval.merge(Interval!Date(Date(2012, 1, 6), Date(2012, 1, 7))) == - NegInfInterval!Date(Date(2012, 1, 7))); - assert(negInfInterval.merge(Interval!Date(Date(2012, 1, 6), Date(2012, 1, 8))) == - NegInfInterval!Date(Date(2012, 1, 8))); - assert(negInfInterval.merge(Interval!Date(Date(2012, 1, 7), Date(2012, 1, 8))) == - NegInfInterval!Date(Date(2012, 1, 8))); - - assert(negInfInterval.merge(NegInfInterval!Date(Date(2010, 7, 3))) == NegInfInterval!Date(Date(2012, 1, 7))); - assert(negInfInterval.merge(NegInfInterval!Date(Date(2010, 7, 4))) == NegInfInterval!Date(Date(2012, 1, 7))); - assert(negInfInterval.merge(NegInfInterval!Date(Date(2010, 7, 5))) == NegInfInterval!Date(Date(2012, 1, 7))); - assert(negInfInterval.merge(NegInfInterval!Date(Date(2012, 1, 6))) == NegInfInterval!Date(Date(2012, 1, 7))); - assert(negInfInterval.merge(NegInfInterval!Date(Date(2012, 1, 7))) == NegInfInterval!Date(Date(2012, 1, 7))); - assert(negInfInterval.merge(NegInfInterval!Date(Date(2012, 1, 8))) == NegInfInterval!Date(Date(2012, 1, 8))); - - assert(NegInfInterval!Date(Date(2010, 7, 3)).merge(negInfInterval) == NegInfInterval!Date(Date(2012, 1, 7))); - assert(NegInfInterval!Date(Date(2010, 7, 4)).merge(negInfInterval) == NegInfInterval!Date(Date(2012, 1, 7))); - assert(NegInfInterval!Date(Date(2010, 7, 5)).merge(negInfInterval) == NegInfInterval!Date(Date(2012, 1, 7))); - assert(NegInfInterval!Date(Date(2012, 1, 6)).merge(negInfInterval) == NegInfInterval!Date(Date(2012, 1, 7))); - assert(NegInfInterval!Date(Date(2012, 1, 7)).merge(negInfInterval) == NegInfInterval!Date(Date(2012, 1, 7))); - assert(NegInfInterval!Date(Date(2012, 1, 8)).merge(negInfInterval) == NegInfInterval!Date(Date(2012, 1, 8))); - - static assert(!__traits(compiles, negInfInterval.merge(PosInfInterval!Date(Date(2010, 7, 3))))); - static assert(!__traits(compiles, negInfInterval.merge(PosInfInterval!Date(Date(2010, 7, 4))))); - static assert(!__traits(compiles, negInfInterval.merge(PosInfInterval!Date(Date(2010, 7, 5))))); - static assert(!__traits(compiles, negInfInterval.merge(PosInfInterval!Date(Date(2012, 1, 6))))); - static assert(!__traits(compiles, negInfInterval.merge(PosInfInterval!Date(Date(2012, 1, 7))))); - static assert(!__traits(compiles, negInfInterval.merge(PosInfInterval!Date(Date(2012, 1, 8))))); - - auto interval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); - const cInterval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); - immutable iInterval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); - auto posInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); - const cPosInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); - immutable iPosInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); - const cNegInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); - immutable iNegInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); - assert(!negInfInterval.merge(interval).empty); - assert(!negInfInterval.merge(cInterval).empty); - assert(!negInfInterval.merge(iInterval).empty); - static assert(!__traits(compiles, negInfInterval.merge(posInfInterval))); - static assert(!__traits(compiles, negInfInterval.merge(cPosInfInterval))); - static assert(!__traits(compiles, negInfInterval.merge(iPosInfInterval))); - assert(!negInfInterval.merge(negInfInterval).empty); - assert(!negInfInterval.merge(cNegInfInterval).empty); - assert(!negInfInterval.merge(iNegInfInterval).empty); - assert(!cNegInfInterval.merge(interval).empty); - assert(!cNegInfInterval.merge(cInterval).empty); - assert(!cNegInfInterval.merge(iInterval).empty); - static assert(!__traits(compiles, cNegInfInterval.merge(posInfInterval))); - static assert(!__traits(compiles, cNegInfInterval.merge(cPosInfInterval))); - static assert(!__traits(compiles, cNegInfInterval.merge(iPosInfInterval))); - assert(!cNegInfInterval.merge(negInfInterval).empty); - assert(!cNegInfInterval.merge(cNegInfInterval).empty); - assert(!cNegInfInterval.merge(iNegInfInterval).empty); - assert(!iNegInfInterval.merge(interval).empty); - assert(!iNegInfInterval.merge(cInterval).empty); - assert(!iNegInfInterval.merge(iInterval).empty); - static assert(!__traits(compiles, iNegInfInterval.merge(posInfInterval))); - static assert(!__traits(compiles, iNegInfInterval.merge(cPosInfInterval))); - static assert(!__traits(compiles, iNegInfInterval.merge(iPosInfInterval))); - assert(!iNegInfInterval.merge(negInfInterval).empty); - assert(!iNegInfInterval.merge(cNegInfInterval).empty); - assert(!iNegInfInterval.merge(iNegInfInterval).empty); - - //Verify Examples. - assert(NegInfInterval!Date(Date(2012, 3, 1)).merge(Interval!Date(Date(1990, 7, 6), Date(2000, 8, 2))) == - NegInfInterval!Date(Date(2012, 3, 1))); - assert(NegInfInterval!Date(Date(2012, 3, 1)).merge(Interval!Date(Date(1999, 1, 12), Date(2015, 9, 2))) == - NegInfInterval!Date(Date(2015, 9, 2))); - - assert(NegInfInterval!Date(Date(2012, 3, 1)).merge(NegInfInterval!Date(Date(1999, 7, 6))) == - NegInfInterval!Date(Date(2012, 3, 1))); - assert(NegInfInterval!Date(Date(2012, 3, 1)).merge(NegInfInterval!Date(Date(2013, 1, 12))) == - NegInfInterval!Date(Date(2013, 1, 12))); -} - -//Test NegInfInterval's span(). -@safe unittest -{ - import std.datetime.date; - - auto negInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); - - static void testInterval(I, J)(scope const I interval1, scope const J interval2) - { - interval1.span(interval2); - } - - assertThrown!DateTimeException(testInterval(negInfInterval, Interval!Date(Date(2010, 7, 4), dur!"days"(0)))); - - assert(negInfInterval.span(negInfInterval) == negInfInterval); - assert(negInfInterval.span(Interval!Date(Date(2010, 7, 1), Date(2010, 7, 3))) == - NegInfInterval!Date(Date(2012, 1, 7))); - assert(negInfInterval.span(Interval!Date(Date(2010, 7, 1), Date(2013, 7, 3))) == - NegInfInterval!Date(Date(2013, 7, 3))); - assert(negInfInterval.span(Interval!Date(Date(2010, 7, 3), Date(2010, 7, 4))) == - NegInfInterval!Date(Date(2012, 1, 7))); - assert(negInfInterval.span(Interval!Date(Date(2010, 7, 3), Date(2010, 7, 5))) == - NegInfInterval!Date(Date(2012, 1, 7))); - assert(negInfInterval.span(Interval!Date(Date(2010, 7, 3), Date(2012, 1, 7))) == - NegInfInterval!Date(Date(2012, 1, 7))); - assert(negInfInterval.span(Interval!Date(Date(2010, 7, 3), Date(2012, 1, 8))) == - NegInfInterval!Date(Date(2012, 1, 8))); - assert(negInfInterval.span(Interval!Date(Date(2010, 7, 5), Date(2012, 1, 6))) == - NegInfInterval!Date(Date(2012, 1, 7))); - assert(negInfInterval.span(Interval!Date(Date(2010, 7, 5), Date(2012, 1, 7))) == - NegInfInterval!Date(Date(2012, 1, 7))); - assert(negInfInterval.span(Interval!Date(Date(2012, 1, 6), Date(2012, 1, 7))) == - NegInfInterval!Date(Date(2012, 1, 7))); - assert(negInfInterval.span(Interval!Date(Date(2012, 1, 6), Date(2012, 1, 8))) == - NegInfInterval!Date(Date(2012, 1, 8))); - assert(negInfInterval.span(Interval!Date(Date(2012, 1, 7), Date(2012, 1, 8))) == - NegInfInterval!Date(Date(2012, 1, 8))); - assert(negInfInterval.span(Interval!Date(Date(2012, 1, 8), Date(2012, 1, 9))) == - NegInfInterval!Date(Date(2012, 1, 9))); - - assert(negInfInterval.span(NegInfInterval!Date(Date(2010, 7, 3))) == NegInfInterval!Date(Date(2012, 1, 7))); - assert(negInfInterval.span(NegInfInterval!Date(Date(2010, 7, 4))) == NegInfInterval!Date(Date(2012, 1, 7))); - assert(negInfInterval.span(NegInfInterval!Date(Date(2010, 7, 5))) == NegInfInterval!Date(Date(2012, 1, 7))); - assert(negInfInterval.span(NegInfInterval!Date(Date(2012, 1, 6))) == NegInfInterval!Date(Date(2012, 1, 7))); - assert(negInfInterval.span(NegInfInterval!Date(Date(2012, 1, 7))) == NegInfInterval!Date(Date(2012, 1, 7))); - assert(negInfInterval.span(NegInfInterval!Date(Date(2012, 1, 8))) == NegInfInterval!Date(Date(2012, 1, 8))); - - assert(NegInfInterval!Date(Date(2010, 7, 3)).span(negInfInterval) == NegInfInterval!Date(Date(2012, 1, 7))); - assert(NegInfInterval!Date(Date(2010, 7, 4)).span(negInfInterval) == NegInfInterval!Date(Date(2012, 1, 7))); - assert(NegInfInterval!Date(Date(2010, 7, 5)).span(negInfInterval) == NegInfInterval!Date(Date(2012, 1, 7))); - assert(NegInfInterval!Date(Date(2012, 1, 6)).span(negInfInterval) == NegInfInterval!Date(Date(2012, 1, 7))); - assert(NegInfInterval!Date(Date(2012, 1, 7)).span(negInfInterval) == NegInfInterval!Date(Date(2012, 1, 7))); - assert(NegInfInterval!Date(Date(2012, 1, 8)).span(negInfInterval) == NegInfInterval!Date(Date(2012, 1, 8))); - - static assert(!__traits(compiles, negInfInterval.span(PosInfInterval!Date(Date(2010, 7, 3))))); - static assert(!__traits(compiles, negInfInterval.span(PosInfInterval!Date(Date(2010, 7, 4))))); - static assert(!__traits(compiles, negInfInterval.span(PosInfInterval!Date(Date(2010, 7, 5))))); - static assert(!__traits(compiles, negInfInterval.span(PosInfInterval!Date(Date(2012, 1, 6))))); - static assert(!__traits(compiles, negInfInterval.span(PosInfInterval!Date(Date(2012, 1, 7))))); - static assert(!__traits(compiles, negInfInterval.span(PosInfInterval!Date(Date(2012, 1, 8))))); - - auto interval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); - const cInterval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); - immutable iInterval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); - auto posInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); - const cPosInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); - immutable iPosInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); - const cNegInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); - immutable iNegInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); - assert(!negInfInterval.span(interval).empty); - assert(!negInfInterval.span(cInterval).empty); - assert(!negInfInterval.span(iInterval).empty); - static assert(!__traits(compiles, negInfInterval.span(posInfInterval))); - static assert(!__traits(compiles, negInfInterval.span(cPosInfInterval))); - static assert(!__traits(compiles, negInfInterval.span(iPosInfInterval))); - assert(!negInfInterval.span(negInfInterval).empty); - assert(!negInfInterval.span(cNegInfInterval).empty); - assert(!negInfInterval.span(iNegInfInterval).empty); - assert(!cNegInfInterval.span(interval).empty); - assert(!cNegInfInterval.span(cInterval).empty); - assert(!cNegInfInterval.span(iInterval).empty); - static assert(!__traits(compiles, cNegInfInterval.span(posInfInterval))); - static assert(!__traits(compiles, cNegInfInterval.span(cPosInfInterval))); - static assert(!__traits(compiles, cNegInfInterval.span(iPosInfInterval))); - assert(!cNegInfInterval.span(negInfInterval).empty); - assert(!cNegInfInterval.span(cNegInfInterval).empty); - assert(!cNegInfInterval.span(iNegInfInterval).empty); - assert(!iNegInfInterval.span(interval).empty); - assert(!iNegInfInterval.span(cInterval).empty); - assert(!iNegInfInterval.span(iInterval).empty); - static assert(!__traits(compiles, iNegInfInterval.span(posInfInterval))); - static assert(!__traits(compiles, iNegInfInterval.span(cPosInfInterval))); - static assert(!__traits(compiles, iNegInfInterval.span(iPosInfInterval))); - assert(!iNegInfInterval.span(negInfInterval).empty); - assert(!iNegInfInterval.span(cNegInfInterval).empty); - assert(!iNegInfInterval.span(iNegInfInterval).empty); - - //Verify Examples. - assert(NegInfInterval!Date(Date(2012, 3, 1)).span(Interval!Date(Date(1990, 7, 6), Date(2000, 8, 2))) == - NegInfInterval!Date(Date(2012, 3, 1))); - assert(NegInfInterval!Date(Date(2012, 3, 1)).span(Interval!Date(Date(1999, 1, 12), Date(2015, 9, 2))) == - NegInfInterval!Date(Date(2015, 9, 2))); - assert(NegInfInterval!Date(Date(1600, 1, 7)).span(Interval!Date(Date(2012, 3, 11), Date(2017, 7, 1))) == - NegInfInterval!Date(Date(2017, 7, 1))); - - assert(NegInfInterval!Date(Date(2012, 3, 1)).span(NegInfInterval!Date(Date(1999, 7, 6))) == - NegInfInterval!Date(Date(2012, 3, 1))); - assert(NegInfInterval!Date(Date(2012, 3, 1)).span(NegInfInterval!Date(Date(2013, 1, 12))) == - NegInfInterval!Date(Date(2013, 1, 12))); -} - -//Test NegInfInterval's shift(). -@safe unittest -{ - import std.datetime.date; - - auto interval = NegInfInterval!Date(Date(2012, 1, 7)); - - static void testInterval(I)(I interval, scope const Duration duration, - scope const I expected, size_t line = __LINE__) - { - interval.shift(duration); - assert(interval == expected); - } - - testInterval(interval, dur!"days"(22), NegInfInterval!Date(Date(2012, 1, 29))); - testInterval(interval, dur!"days"(-22), NegInfInterval!Date(Date(2011, 12, 16))); - - const cInterval = NegInfInterval!Date(Date(2012, 1, 7)); - immutable iInterval = NegInfInterval!Date(Date(2012, 1, 7)); - static assert(!__traits(compiles, cInterval.shift(dur!"days"(5)))); - static assert(!__traits(compiles, iInterval.shift(dur!"days"(5)))); - - //Verify Examples. - auto interval1 = NegInfInterval!Date(Date(2012, 4, 5)); - auto interval2 = NegInfInterval!Date(Date(2012, 4, 5)); - - interval1.shift(dur!"days"(50)); - assert(interval1 == NegInfInterval!Date(Date(2012, 5, 25))); - - interval2.shift(dur!"days"(-50)); - assert(interval2 == NegInfInterval!Date( Date(2012, 2, 15))); -} - -//Test NegInfInterval's shift(int, int, AllowDayOverflow). -@safe unittest -{ - import std.datetime.date; - - { - auto interval = NegInfInterval!Date(Date(2012, 1, 7)); - - static void testIntervalFail(I)(I interval, int years, int months) - { - interval.shift(years, months); - } - - static void testInterval(I)(I interval, int years, int months, AllowDayOverflow allow, - in I expected, size_t line = __LINE__) - { - interval.shift(years, months, allow); - assert(interval == expected); - } - - testInterval(interval, 5, 0, AllowDayOverflow.yes, NegInfInterval!Date(Date(2017, 1, 7))); - testInterval(interval, -5, 0, AllowDayOverflow.yes, NegInfInterval!Date(Date(2007, 1, 7))); - - auto interval2 = NegInfInterval!Date(Date(2010, 5, 31)); - - testInterval(interval2, 1, 1, AllowDayOverflow.yes, NegInfInterval!Date(Date(2011, 7, 1))); - testInterval(interval2, 1, -1, AllowDayOverflow.yes, NegInfInterval!Date(Date(2011, 5, 1))); - testInterval(interval2, -1, -1, AllowDayOverflow.yes, NegInfInterval!Date(Date(2009, 5, 1))); - testInterval(interval2, -1, 1, AllowDayOverflow.yes, NegInfInterval!Date(Date(2009, 7, 1))); - - testInterval(interval2, 1, 1, AllowDayOverflow.no, NegInfInterval!Date(Date(2011, 6, 30))); - testInterval(interval2, 1, -1, AllowDayOverflow.no, NegInfInterval!Date(Date(2011, 4, 30))); - testInterval(interval2, -1, -1, AllowDayOverflow.no, NegInfInterval!Date(Date(2009, 4, 30))); - testInterval(interval2, -1, 1, AllowDayOverflow.no, NegInfInterval!Date(Date(2009, 6, 30))); - } - - const cNegInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); - immutable iNegInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); - static assert(!__traits(compiles, cNegInfInterval.shift(1))); - static assert(!__traits(compiles, iNegInfInterval.shift(1))); - - //Verify Examples. - auto interval1 = NegInfInterval!Date(Date(2012, 3, 1)); - auto interval2 = NegInfInterval!Date(Date(2012, 3, 1)); - - interval1.shift(2); - assert(interval1 == NegInfInterval!Date(Date(2014, 3, 1))); - - interval2.shift(-2); - assert(interval2 == NegInfInterval!Date(Date(2010, 3, 1))); -} - -//Test NegInfInterval's expand(). -@safe unittest -{ - import std.datetime.date; - - auto interval = NegInfInterval!Date(Date(2012, 1, 7)); - - static void testInterval(I)(I interval, - scope const Duration duration, scope const I expected, size_t line = __LINE__) - { - interval.expand(duration); - assert(interval == expected); - } - - testInterval(interval, dur!"days"(22), NegInfInterval!Date(Date(2012, 1, 29))); - testInterval(interval, dur!"days"(-22), NegInfInterval!Date(Date(2011, 12, 16))); - - const cInterval = NegInfInterval!Date(Date(2012, 1, 7)); - immutable iInterval = NegInfInterval!Date(Date(2012, 1, 7)); - static assert(!__traits(compiles, cInterval.expand(dur!"days"(5)))); - static assert(!__traits(compiles, iInterval.expand(dur!"days"(5)))); - - //Verify Examples. - auto interval1 = NegInfInterval!Date(Date(2012, 3, 1)); - auto interval2 = NegInfInterval!Date(Date(2012, 3, 1)); - - interval1.expand(dur!"days"(2)); - assert(interval1 == NegInfInterval!Date(Date(2012, 3, 3))); - - interval2.expand(dur!"days"(-2)); - assert(interval2 == NegInfInterval!Date(Date(2012, 2, 28))); -} - -//Test NegInfInterval's expand(int, int, AllowDayOverflow). -@safe unittest -{ - import std.datetime.date; - - { - auto interval = NegInfInterval!Date(Date(2012, 1, 7)); - - static void testInterval(I)(I interval, int years, int months, AllowDayOverflow allow, - in I expected, size_t line = __LINE__) - { - interval.expand(years, months, allow); - assert(interval == expected); - } - - testInterval(interval, 5, 0, AllowDayOverflow.yes, NegInfInterval!Date(Date(2017, 1, 7))); - testInterval(interval, -5, 0, AllowDayOverflow.yes, NegInfInterval!Date(Date(2007, 1, 7))); - - auto interval2 = NegInfInterval!Date(Date(2010, 5, 31)); - - testInterval(interval2, 1, 1, AllowDayOverflow.yes, NegInfInterval!Date(Date(2011, 7, 1))); - testInterval(interval2, 1, -1, AllowDayOverflow.yes, NegInfInterval!Date(Date(2011, 5, 1))); - testInterval(interval2, -1, -1, AllowDayOverflow.yes, NegInfInterval!Date(Date(2009, 5, 1))); - testInterval(interval2, -1, 1, AllowDayOverflow.yes, NegInfInterval!Date(Date(2009, 7, 1))); - - testInterval(interval2, 1, 1, AllowDayOverflow.no, NegInfInterval!Date(Date(2011, 6, 30))); - testInterval(interval2, 1, -1, AllowDayOverflow.no, NegInfInterval!Date(Date(2011, 4, 30))); - testInterval(interval2, -1, -1, AllowDayOverflow.no, NegInfInterval!Date(Date(2009, 4, 30))); - testInterval(interval2, -1, 1, AllowDayOverflow.no, NegInfInterval!Date( Date(2009, 6, 30))); - } - - const cNegInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); - immutable iNegInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); - static assert(!__traits(compiles, cNegInfInterval.expand(1))); - static assert(!__traits(compiles, iNegInfInterval.expand(1))); - - //Verify Examples. - auto interval1 = NegInfInterval!Date(Date(2012, 3, 1)); - auto interval2 = NegInfInterval!Date(Date(2012, 3, 1)); - - interval1.expand(2); - assert(interval1 == NegInfInterval!Date(Date(2014, 3, 1))); - - interval2.expand(-2); - assert(interval2 == NegInfInterval!Date(Date(2010, 3, 1))); -} - -//Test NegInfInterval's bwdRange(). -@system unittest -{ - import std.datetime.date; - - auto negInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); - - static void testInterval(NegInfInterval!Date negInfInterval) - { - negInfInterval.bwdRange(everyDayOfWeek!(Date, Direction.fwd)(DayOfWeek.fri)).popFront(); - } - - assertThrown!DateTimeException(testInterval(negInfInterval)); - - assert(NegInfInterval!Date(Date(2010, 10, 1)).bwdRange(everyDayOfWeek!(Date, Direction.bwd)(DayOfWeek.fri)).front == - Date(2010, 10, 1)); - - assert(NegInfInterval!Date(Date(2010, 10, 1)).bwdRange( - everyDayOfWeek!(Date, Direction.bwd)(DayOfWeek.fri), PopFirst.yes).front == Date(2010, 9, 24)); - - //Verify Examples. - auto interval = NegInfInterval!Date(Date(2010, 9, 9)); - auto func = delegate (scope const Date date) - { - if ((date.day & 1) == 0) - return date - dur!"days"(2); - return date - dur!"days"(1); - }; - auto range = interval.bwdRange(func); - - //An odd day. Using PopFirst.yes would have made this Date(2010, 9, 8). - assert(range.front == Date(2010, 9, 9)); - - range.popFront(); - assert(range.front == Date(2010, 9, 8)); - - range.popFront(); - assert(range.front == Date(2010, 9, 6)); - - range.popFront(); - assert(range.front == Date(2010, 9, 4)); - - range.popFront(); - assert(range.front == Date(2010, 9, 2)); - - range.popFront(); - assert(!range.empty); - - const cNegInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); - immutable iNegInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); - assert(!cNegInfInterval.bwdRange(everyDayOfWeek!(Date, Direction.bwd)(DayOfWeek.fri)).empty); - assert(!iNegInfInterval.bwdRange(everyDayOfWeek!(Date, Direction.bwd)(DayOfWeek.fri)).empty); -} - -//Test NegInfInterval's toString(). -@safe unittest -{ - import std.datetime.date; - - assert(NegInfInterval!Date(Date(2012, 1, 7)).toString() == "[-∞ - 2012-Jan-07)"); - - const cNegInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); - immutable iNegInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); - assert(cNegInfInterval.toString()); - assert(iNegInfInterval.toString()); -} - - -/++ - Range-generating function. - - Returns a delegate which returns the next time point with the given - `DayOfWeek` in a range. - - Using this delegate allows iteration over successive time points which - are all the same day of the week. e.g. passing `DayOfWeek.mon` to - `everyDayOfWeek` would result in a delegate which could be used to - iterate over all of the Mondays in a range. - - Params: - dir = The direction to iterate in. If passing the return value to - `fwdRange`, use `Direction.fwd`. If passing it to - `bwdRange`, use `Direction.bwd`. - dayOfWeek = The week that each time point in the range will be. - +/ -TP delegate(scope const TP) everyDayOfWeek(TP, Direction dir = Direction.fwd)(DayOfWeek dayOfWeek) nothrow -if (isTimePoint!TP && - (dir == Direction.fwd || dir == Direction.bwd) && - __traits(hasMember, TP, "dayOfWeek") && - !__traits(isStaticFunction, TP.dayOfWeek) && - is(typeof(TP.dayOfWeek) == DayOfWeek)) -{ - TP func(scope const TP tp) - { - TP retval = cast(TP) tp; - immutable days = daysToDayOfWeek(retval.dayOfWeek, dayOfWeek); - - static if (dir == Direction.fwd) - immutable adjustedDays = days == 0 ? 7 : days; - else - immutable adjustedDays = days == 0 ? -7 : days - 7; - - return retval += dur!"days"(adjustedDays); - } - - return &func; -} - -/// -@system unittest -{ - import std.datetime.date : Date, DayOfWeek; - - auto interval = Interval!Date(Date(2010, 9, 2), Date(2010, 9, 27)); - auto func = everyDayOfWeek!Date(DayOfWeek.mon); - auto range = interval.fwdRange(func); - - // A Thursday. Using PopFirst.yes would have made this Date(2010, 9, 6). - assert(range.front == Date(2010, 9, 2)); - - range.popFront(); - assert(range.front == Date(2010, 9, 6)); - - range.popFront(); - assert(range.front == Date(2010, 9, 13)); - - range.popFront(); - assert(range.front == Date(2010, 9, 20)); - - range.popFront(); - assert(range.empty); -} - -@system unittest -{ - import std.datetime.date; - import std.datetime.systime; - - auto funcFwd = everyDayOfWeek!Date(DayOfWeek.mon); - auto funcBwd = everyDayOfWeek!(Date, Direction.bwd)(DayOfWeek.mon); - - assert(funcFwd(Date(2010, 8, 28)) == Date(2010, 8, 30)); - assert(funcFwd(Date(2010, 8, 29)) == Date(2010, 8, 30)); - assert(funcFwd(Date(2010, 8, 30)) == Date(2010, 9, 6)); - assert(funcFwd(Date(2010, 8, 31)) == Date(2010, 9, 6)); - assert(funcFwd(Date(2010, 9, 1)) == Date(2010, 9, 6)); - assert(funcFwd(Date(2010, 9, 2)) == Date(2010, 9, 6)); - assert(funcFwd(Date(2010, 9, 3)) == Date(2010, 9, 6)); - assert(funcFwd(Date(2010, 9, 4)) == Date(2010, 9, 6)); - assert(funcFwd(Date(2010, 9, 5)) == Date(2010, 9, 6)); - assert(funcFwd(Date(2010, 9, 6)) == Date(2010, 9, 13)); - assert(funcFwd(Date(2010, 9, 7)) == Date(2010, 9, 13)); - - assert(funcBwd(Date(2010, 8, 28)) == Date(2010, 8, 23)); - assert(funcBwd(Date(2010, 8, 29)) == Date(2010, 8, 23)); - assert(funcBwd(Date(2010, 8, 30)) == Date(2010, 8, 23)); - assert(funcBwd(Date(2010, 8, 31)) == Date(2010, 8, 30)); - assert(funcBwd(Date(2010, 9, 1)) == Date(2010, 8, 30)); - assert(funcBwd(Date(2010, 9, 2)) == Date(2010, 8, 30)); - assert(funcBwd(Date(2010, 9, 3)) == Date(2010, 8, 30)); - assert(funcBwd(Date(2010, 9, 4)) == Date(2010, 8, 30)); - assert(funcBwd(Date(2010, 9, 5)) == Date(2010, 8, 30)); - assert(funcBwd(Date(2010, 9, 6)) == Date(2010, 8, 30)); - assert(funcBwd(Date(2010, 9, 7)) == Date(2010, 9, 6)); - - static assert(!__traits(compiles, everyDayOfWeek!TimeOfDay(DayOfWeek.mon))); - assert(everyDayOfWeek!DateTime(DayOfWeek.mon) !is null); - assert(everyDayOfWeek!SysTime(DayOfWeek.mon) !is null); -} - - -/++ - Range-generating function. - - Returns a delegate which returns the next time point with the given month - which would be reached by adding months to the given time point. - - So, using this delegate allows iteration over successive time points - which are in the same month but different years. For example, - iterate over each successive December 25th in an interval by starting with a - date which had the 25th as its day and passed `Month.dec` to - `everyMonth` to create the delegate. - - Since it wouldn't really make sense to be iterating over a specific month - and end up with some of the time points in the succeeding month or two years - after the previous time point, `AllowDayOverflow.no` is always used when - calculating the next time point. - - Params: - dir = The direction to iterate in. If passing the return value to - `fwdRange`, use `Direction.fwd`. If passing it to - `bwdRange`, use `Direction.bwd`. - month = The month that each time point in the range will be in - (January is 1). - +/ -TP delegate(scope const TP) everyMonth(TP, Direction dir = Direction.fwd)(int month) -if (isTimePoint!TP && - (dir == Direction.fwd || dir == Direction.bwd) && - __traits(hasMember, TP, "month") && - !__traits(isStaticFunction, TP.month) && - is(typeof(TP.month) == Month)) -{ - import std.datetime.date : enforceValid, monthsToMonth; - - enforceValid!"months"(month); - - TP func(scope const TP tp) - { - TP retval = cast(TP) tp; - immutable months = monthsToMonth(retval.month, month); - - static if (dir == Direction.fwd) - immutable adjustedMonths = months == 0 ? 12 : months; - else - immutable adjustedMonths = months == 0 ? -12 : months - 12; - - retval.add!"months"(adjustedMonths, AllowDayOverflow.no); - - if (retval.month != month) - { - retval.add!"months"(-1); - assert(retval.month == month); - } - - return retval; - } - - return &func; -} - -/// -@system unittest -{ - import std.datetime.date : Date, Month; - - auto interval = Interval!Date(Date(2000, 1, 30), Date(2004, 8, 5)); - auto func = everyMonth!Date(Month.feb); - auto range = interval.fwdRange(func); - - // Using PopFirst.yes would have made this Date(2010, 2, 29). - assert(range.front == Date(2000, 1, 30)); - - range.popFront(); - assert(range.front == Date(2000, 2, 29)); - - range.popFront(); - assert(range.front == Date(2001, 2, 28)); - - range.popFront(); - assert(range.front == Date(2002, 2, 28)); - - range.popFront(); - assert(range.front == Date(2003, 2, 28)); - - range.popFront(); - assert(range.front == Date(2004, 2, 28)); - - range.popFront(); - assert(range.empty); -} - -@system unittest -{ - import std.datetime.date; - import std.datetime.systime; - - auto funcFwd = everyMonth!Date(Month.jun); - auto funcBwd = everyMonth!(Date, Direction.bwd)(Month.jun); - - assert(funcFwd(Date(2010, 5, 31)) == Date(2010, 6, 30)); - assert(funcFwd(Date(2010, 6, 30)) == Date(2011, 6, 30)); - assert(funcFwd(Date(2010, 7, 31)) == Date(2011, 6, 30)); - assert(funcFwd(Date(2010, 8, 31)) == Date(2011, 6, 30)); - assert(funcFwd(Date(2010, 9, 30)) == Date(2011, 6, 30)); - assert(funcFwd(Date(2010, 10, 31)) == Date(2011, 6, 30)); - assert(funcFwd(Date(2010, 11, 30)) == Date(2011, 6, 30)); - assert(funcFwd(Date(2010, 12, 31)) == Date(2011, 6, 30)); - assert(funcFwd(Date(2011, 1, 31)) == Date(2011, 6, 30)); - assert(funcFwd(Date(2011, 2, 28)) == Date(2011, 6, 28)); - assert(funcFwd(Date(2011, 3, 31)) == Date(2011, 6, 30)); - assert(funcFwd(Date(2011, 4, 30)) == Date(2011, 6, 30)); - assert(funcFwd(Date(2011, 5, 31)) == Date(2011, 6, 30)); - assert(funcFwd(Date(2011, 6, 30)) == Date(2012, 6, 30)); - assert(funcFwd(Date(2011, 7, 31)) == Date(2012, 6, 30)); - - assert(funcBwd(Date(2010, 5, 31)) == Date(2009, 6, 30)); - assert(funcBwd(Date(2010, 6, 30)) == Date(2009, 6, 30)); - assert(funcBwd(Date(2010, 7, 31)) == Date(2010, 6, 30)); - assert(funcBwd(Date(2010, 8, 31)) == Date(2010, 6, 30)); - assert(funcBwd(Date(2010, 9, 30)) == Date(2010, 6, 30)); - assert(funcBwd(Date(2010, 10, 31)) == Date(2010, 6, 30)); - assert(funcBwd(Date(2010, 11, 30)) == Date(2010, 6, 30)); - assert(funcBwd(Date(2010, 12, 31)) == Date(2010, 6, 30)); - assert(funcBwd(Date(2011, 1, 31)) == Date(2010, 6, 30)); - assert(funcBwd(Date(2011, 2, 28)) == Date(2010, 6, 28)); - assert(funcBwd(Date(2011, 3, 31)) == Date(2010, 6, 30)); - assert(funcBwd(Date(2011, 4, 30)) == Date(2010, 6, 30)); - assert(funcBwd(Date(2011, 5, 31)) == Date(2010, 6, 30)); - assert(funcBwd(Date(2011, 6, 30)) == Date(2010, 6, 30)); - assert(funcBwd(Date(2011, 7, 30)) == Date(2011, 6, 30)); - - static assert(!__traits(compiles, everyMonth!TimeOfDay(Month.jan))); - assert(everyMonth!DateTime(Month.jan) !is null); - assert(everyMonth!SysTime(Month.jan) !is null); -} - - -/++ - Range-generating function. - - Returns a delegate which returns the next time point which is the given - duration later. - - Using this delegate allows iteration over successive time points which - are apart by the given duration e.g. passing `dur!"days"(3)` to - `everyDuration` would result in a delegate which could be used to iterate - over a range of days which are each 3 days apart. - - Params: - dir = The direction to iterate in. If passing the return value to - `fwdRange`, use `Direction.fwd`. If passing it to - `bwdRange`, use `Direction.bwd`. - duration = The duration which separates each successive time point in - the range. - +/ -TP delegate(return scope const TP) everyDuration(TP, Direction dir = Direction.fwd, D)(D duration) nothrow -if (isTimePoint!TP && - __traits(compiles, TP.init + duration) && - (dir == Direction.fwd || dir == Direction.bwd)) -{ - TP func(return scope const TP tp) - { - static if (dir == Direction.fwd) - return tp + duration; - else - return tp - duration; - } - - return &func; -} - -/// -@system unittest -{ - import core.time : dur; - import std.datetime.date : Date; - - auto interval = Interval!Date(Date(2010, 9, 2), Date(2010, 9, 27)); - auto func = everyDuration!Date(dur!"days"(8)); - auto range = interval.fwdRange(func); - - // Using PopFirst.yes would have made this Date(2010, 9, 10). - assert(range.front == Date(2010, 9, 2)); - - range.popFront(); - assert(range.front == Date(2010, 9, 10)); - - range.popFront(); - assert(range.front == Date(2010, 9, 18)); - - range.popFront(); - assert(range.front == Date(2010, 9, 26)); - - range.popFront(); - assert(range.empty); -} - -@system unittest -{ - import std.datetime.date; - import std.datetime.systime; - - auto funcFwd = everyDuration!Date(dur!"days"(27)); - auto funcBwd = everyDuration!(Date, Direction.bwd)(dur!"days"(27)); - - assert(funcFwd(Date(2009, 12, 25)) == Date(2010, 1, 21)); - assert(funcFwd(Date(2009, 12, 26)) == Date(2010, 1, 22)); - assert(funcFwd(Date(2009, 12, 27)) == Date(2010, 1, 23)); - assert(funcFwd(Date(2009, 12, 28)) == Date(2010, 1, 24)); - - assert(funcBwd(Date(2010, 1, 21)) == Date(2009, 12, 25)); - assert(funcBwd(Date(2010, 1, 22)) == Date(2009, 12, 26)); - assert(funcBwd(Date(2010, 1, 23)) == Date(2009, 12, 27)); - assert(funcBwd(Date(2010, 1, 24)) == Date(2009, 12, 28)); - - assert(everyDuration!Date(dur!"hnsecs"(1)) !is null); - assert(everyDuration!TimeOfDay(dur!"hnsecs"(1)) !is null); - assert(everyDuration!DateTime(dur!"hnsecs"(1)) !is null); - assert(everyDuration!SysTime(dur!"hnsecs"(1)) !is null); -} - - -/++ - Range-generating function. - - Returns a delegate which returns the next time point which is the given - number of years, month, and duration later. - - The difference between this version of `everyDuration` and the version - which just takes a $(REF Duration, core,time) is that this one also takes - the number of years and months (along with an `AllowDayOverflow` to - indicate whether adding years and months should allow the days to overflow). - - Note that if iterating forward, `add!"years"()` is called on the given - time point, then `add!"months"()`, and finally the duration is added - to it. However, if iterating backwards, the duration is added first, then - `add!"months"()` is called, and finally `add!"years"()` is called. - That way, going backwards generates close to the same time points that - iterating forward does, but since adding years and months is not entirely - reversible (due to possible day overflow, regardless of whether - `AllowDayOverflow.yes` or `AllowDayOverflow.no` is used), it can't be - guaranteed that iterating backwards will give the same time points as - iterating forward would have (even assuming that the end of the range is a - time point which would be returned by the delegate when iterating forward - from `begin`). - - Params: - dir = The direction to iterate in. If passing the return - value to `fwdRange`, use `Direction.fwd`. If - passing it to `bwdRange`, use `Direction.bwd`. - years = The number of years to add to the time point passed to - the delegate. - months = The number of months to add to the time point passed to - the delegate. - allowOverflow = Whether the days should be allowed to overflow on - `begin` and `end`, causing their month to - increment. - duration = The duration to add to the time point passed to the - delegate. - +/ -TP delegate(scope const TP) everyDuration(TP, Direction dir = Direction.fwd, D) - (int years, - int months = 0, - AllowDayOverflow allowOverflow = AllowDayOverflow.yes, - D duration = dur!"days"(0)) nothrow -if (isTimePoint!TP && - __traits(compiles, TP.init + duration) && - __traits(compiles, TP.init.add!"years"(years)) && - __traits(compiles, TP.init.add!"months"(months)) && - (dir == Direction.fwd || dir == Direction.bwd)) -{ - TP func(scope const TP tp) - { - static if (dir == Direction.fwd) - { - TP retval = cast(TP) tp; - - retval.add!"years"(years, allowOverflow); - retval.add!"months"(months, allowOverflow); - - return retval + duration; - } - else - { - TP retval = tp - duration; - - retval.add!"months"(-months, allowOverflow); - retval.add!"years"(-years, allowOverflow); - - return retval; - } - } - - return &func; -} - -/// -@system unittest -{ - import core.time : dur; - import std.datetime.date : AllowDayOverflow, Date; - - auto interval = Interval!Date(Date(2010, 9, 2), Date(2025, 9, 27)); - auto func = everyDuration!Date(4, 1, AllowDayOverflow.yes, dur!"days"(2)); - auto range = interval.fwdRange(func); - - // Using PopFirst.yes would have made this Date(2014, 10, 12). - assert(range.front == Date(2010, 9, 2)); - - range.popFront(); - assert(range.front == Date(2014, 10, 4)); - - range.popFront(); - assert(range.front == Date(2018, 11, 6)); - - range.popFront(); - assert(range.front == Date(2022, 12, 8)); - - range.popFront(); - assert(range.empty); -} - -@system unittest -{ - import std.datetime.date; - import std.datetime.systime; - - { - auto funcFwd = everyDuration!Date(1, 2, AllowDayOverflow.yes, dur!"days"(3)); - auto funcBwd = everyDuration!(Date, Direction.bwd)(1, 2, AllowDayOverflow.yes, dur!"days"(3)); - - assert(funcFwd(Date(2009, 12, 25)) == Date(2011, 2, 28)); - assert(funcFwd(Date(2009, 12, 26)) == Date(2011, 3, 1)); - assert(funcFwd(Date(2009, 12, 27)) == Date(2011, 3, 2)); - assert(funcFwd(Date(2009, 12, 28)) == Date(2011, 3, 3)); - assert(funcFwd(Date(2009, 12, 29)) == Date(2011, 3, 4)); - - assert(funcBwd(Date(2011, 2, 28)) == Date(2009, 12, 25)); - assert(funcBwd(Date(2011, 3, 1)) == Date(2009, 12, 26)); - assert(funcBwd(Date(2011, 3, 2)) == Date(2009, 12, 27)); - assert(funcBwd(Date(2011, 3, 3)) == Date(2009, 12, 28)); - assert(funcBwd(Date(2011, 3, 4)) == Date(2010, 1, 1)); - } - - { - auto funcFwd = everyDuration!Date(1, 2, AllowDayOverflow.no, dur!"days"(3)); - auto funcBwd = everyDuration!(Date, Direction.bwd)(1, 2, AllowDayOverflow.yes, dur!"days"(3)); - - assert(funcFwd(Date(2009, 12, 25)) == Date(2011, 2, 28)); - assert(funcFwd(Date(2009, 12, 26)) == Date(2011, 3, 1)); - assert(funcFwd(Date(2009, 12, 27)) == Date(2011, 3, 2)); - assert(funcFwd(Date(2009, 12, 28)) == Date(2011, 3, 3)); - assert(funcFwd(Date(2009, 12, 29)) == Date(2011, 3, 3)); - - assert(funcBwd(Date(2011, 2, 28)) == Date(2009, 12, 25)); - assert(funcBwd(Date(2011, 3, 1)) == Date(2009, 12, 26)); - assert(funcBwd(Date(2011, 3, 2)) == Date(2009, 12, 27)); - assert(funcBwd(Date(2011, 3, 3)) == Date(2009, 12, 28)); - assert(funcBwd(Date(2011, 3, 4)) == Date(2010, 1, 1)); - } - - assert(everyDuration!Date(1, 2, AllowDayOverflow.yes, dur!"hnsecs"(1)) !is null); - static assert(!__traits(compiles, everyDuration!TimeOfDay(1, 2, AllowDayOverflow.yes, dur!"hnsecs"(1)))); - assert(everyDuration!DateTime(1, 2, AllowDayOverflow.yes, dur!"hnsecs"(1)) !is null); - assert(everyDuration!SysTime(1, 2, AllowDayOverflow.yes, dur!"hnsecs"(1)) !is null); -} - - -/++ - A range over an $(LREF Interval). - - `IntervalRange` is only ever constructed by $(LREF Interval). However, when - it is constructed, it is given a function, `func`, which is used to - generate the time points which are iterated over. `func` takes a time - point and returns a time point of the same type. For instance, - to iterate over all of the days in - the interval `Interval!Date`, pass a function to $(LREF Interval)'s - `fwdRange` where that function took a $(REF Date,std,datetime,date) and - returned a $(REF Date,std,datetime,date) which was one day later. That - function would then be used by `IntervalRange`'s `popFront` to iterate - over the $(REF Date,std,datetime,date)s in the interval. - - If $(D dir == Direction.fwd), then a range iterates forward in time, whereas - if $(D dir == Direction.bwd), then it iterates backwards in time. So, if - $(D dir == Direction.fwd) then $(D front == interval.begin), whereas if - $(D dir == Direction.bwd) then $(D front == interval.end). `func` must - generate a time point going in the proper direction of iteration, or a - $(REF DateTimeException,std,datetime,date) will be thrown. So, to iterate - forward in time, the time point that `func` generates must be later in - time than the one passed to it. If it's either identical or earlier in time, - then a $(REF DateTimeException,std,datetime,date) will be thrown. To - iterate backwards, then the generated time point must be before the time - point which was passed in. - - If the generated time point is ever passed the edge of the range in the - proper direction, then the edge of that range will be used instead. So, if - iterating forward, and the generated time point is past the interval's - `end`, then `front` becomes `end`. If iterating backwards, and the - generated time point is before `begin`, then `front` becomes - `begin`. In either case, the range would then be empty. - - Also note that while normally the `begin` of an interval is included in - it and its `end` is excluded from it, if $(D dir == Direction.bwd), then - `begin` is treated as excluded and `end` is treated as included. This - allows for the same behavior in both directions. This works because none of - $(LREF Interval)'s functions which care about whether `begin` or `end` - is included or excluded are ever called by `IntervalRange`. `interval` - returns a normal interval, regardless of whether $(D dir == Direction.fwd) - or if $(D dir == Direction.bwd), so any $(LREF Interval) functions which are - called on it which care about whether `begin` or `end` are included or - excluded will treat `begin` as included and `end` as excluded. - +/ -struct IntervalRange(TP, Direction dir) -if (isTimePoint!TP && dir != Direction.both) -{ -public: - - /++ - Params: - rhs = The `IntervalRange` to assign to this one. - +/ - ref IntervalRange opAssign(ref IntervalRange rhs) pure nothrow - { - _interval = rhs._interval; - _func = rhs._func; - return this; - } - - - /++ Ditto +/ - ref IntervalRange opAssign(IntervalRange rhs) pure nothrow - { - return this = rhs; - } - - - /++ - Whether this `IntervalRange` is empty. - +/ - @property bool empty() const pure nothrow - { - return _interval.empty; - } - - - /++ - The first time point in the range. - - Throws: - $(REF DateTimeException,std,datetime,date) if the range is empty. - +/ - @property TP front() const pure - { - _enforceNotEmpty(); - - static if (dir == Direction.fwd) - return _interval.begin; - else - return _interval.end; - } - - - /++ - Pops `front` from the range, using `func` to generate the next - time point in the range. If the generated time point is beyond the edge - of the range, then `front` is set to that edge, and the range is then - empty. So, if iterating forwards, and the generated time point is - greater than the interval's `end`, then `front` is set to - `end`. If iterating backwards, and the generated time point is less - than the interval's `begin`, then `front` is set to `begin`. - - Throws: - $(REF DateTimeException,std,datetime,date) if the range is empty - or if the generated time point is in the wrong direction (i.e. if - iterating forward and the generated time point is before `front`, - or if iterating backwards and the generated time point is after - `front`). - +/ - void popFront() - { - _enforceNotEmpty(); - - static if (dir == Direction.fwd) - { - auto begin = _func(_interval.begin); - - if (begin > _interval.end) - begin = _interval.end; - - _enforceCorrectDirection(begin); - - _interval.begin = begin; - } - else - { - auto end = _func(_interval.end); - - if (end < _interval.begin) - end = _interval.begin; - - _enforceCorrectDirection(end); - - _interval.end = end; - } - } - - - /++ - Returns a copy of `this`. - +/ - @property IntervalRange save() pure nothrow - { - return this; - } - - - /++ - The interval that this `IntervalRange` currently covers. - +/ - @property Interval!TP interval() const pure nothrow - { - return cast(Interval!TP)_interval; - } - - - /++ - The function used to generate the next time point in the range. - +/ - TP delegate(scope const TP) func() pure nothrow @property - { - return _func; - } - - - /++ - The `Direction` that this range iterates in. - +/ - @property Direction direction() const pure nothrow - { - return dir; - } - - -private: - - /+ - Params: - interval = The interval that this range covers. - func = The function used to generate the time points which are - iterated over. - +/ - this(const Interval!TP interval, TP delegate(scope const TP) func) pure nothrow @safe - { - _func = func; - _interval = interval; - } - - - /+ - Throws: - $(REF DateTimeException,std,datetime,date) if this interval is - empty. - +/ - void _enforceNotEmpty(size_t line = __LINE__) const pure - { - if (empty) - throw new DateTimeException("Invalid operation for an empty IntervalRange.", __FILE__, line); - } - - - /+ - Throws: - $(REF DateTimeException,std,datetime,date) if $(D_PARAM newTP) is - in the wrong direction. - +/ - void _enforceCorrectDirection(scope const TP newTP, size_t line = __LINE__) const - { - import std.format : format; - - static if (dir == Direction.fwd) - { - enforce(newTP > _interval._begin, - new DateTimeException(format("Generated time point is before previous begin: prev [%s] new [%s]", - interval._begin, - newTP), - __FILE__, - line)); - } - else - { - enforce(newTP < _interval._end, - new DateTimeException(format("Generated time point is after previous end: prev [%s] new [%s]", - interval._end, - newTP), - __FILE__, - line)); - } - } - - - Interval!TP _interval; - TP delegate(scope const TP) _func; -} - -//Test that IntervalRange satisfies the range predicates that it's supposed to satisfy. -@safe unittest -{ - import std.datetime.date; - import std.datetime.systime; - import std.range.primitives; - - static assert(isInputRange!(IntervalRange!(Date, Direction.fwd))); - static assert(isForwardRange!(IntervalRange!(Date, Direction.fwd))); - - // Commented out due to bug https://issues.dlang.org/show_bug.cgi?id=4895 - // static assert(!isOutputRange!(IntervalRange!(Date, Direction.fwd), Date)); - - static assert(!isBidirectionalRange!(IntervalRange!(Date, Direction.fwd))); - static assert(!isRandomAccessRange!(IntervalRange!(Date, Direction.fwd))); - static assert(!hasSwappableElements!(IntervalRange!(Date, Direction.fwd))); - static assert(!hasAssignableElements!(IntervalRange!(Date, Direction.fwd))); - static assert(!hasLength!(IntervalRange!(Date, Direction.fwd))); - static assert(!isInfinite!(IntervalRange!(Date, Direction.fwd))); - static assert(!hasSlicing!(IntervalRange!(Date, Direction.fwd))); - - static assert(is(ElementType!(IntervalRange!(Date, Direction.fwd)) == Date)); - static assert(is(ElementType!(IntervalRange!(TimeOfDay, Direction.fwd)) == TimeOfDay)); - static assert(is(ElementType!(IntervalRange!(DateTime, Direction.fwd)) == DateTime)); - static assert(is(ElementType!(IntervalRange!(SysTime, Direction.fwd)) == SysTime)); -} - -//Test construction of IntervalRange. -@safe unittest -{ - import std.datetime.date; - import std.datetime.systime; - - { - Date dateFunc(scope const Date date) { return date; } - auto interval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); - auto ir = IntervalRange!(Date, Direction.fwd)(interval, &dateFunc); - } - - { - TimeOfDay todFunc(scope const TimeOfDay tod) { return tod; } - auto interval = Interval!TimeOfDay(TimeOfDay(12, 1, 7), TimeOfDay(14, 0, 0)); - auto ir = IntervalRange!(TimeOfDay, Direction.fwd)(interval, &todFunc); - } - - { - DateTime dtFunc(scope const DateTime dt) { return dt; } - auto interval = Interval!DateTime(DateTime(2010, 7, 4, 12, 1, 7), DateTime(2012, 1, 7, 14, 0, 0)); - auto ir = IntervalRange!(DateTime, Direction.fwd)(interval, &dtFunc); - } - - { - SysTime stFunc(scope const SysTime st) { return SysTime.init; } - auto interval = Interval!SysTime(SysTime(DateTime(2010, 7, 4, 12, 1, 7)), - SysTime(DateTime(2012, 1, 7, 14, 0, 0))); - auto ir = IntervalRange!(SysTime, Direction.fwd)(interval, &stFunc); - } -} - -//Test IntervalRange's empty(). -@system unittest -{ - import std.datetime.date; - - //fwd - { - auto range = Interval!Date(Date(2010, 9, 19), Date(2010, 9, 21)).fwdRange(everyDayOfWeek!Date(DayOfWeek.fri)); - - assert(!range.empty); - range.popFront(); - assert(range.empty); - - const cRange = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)).fwdRange(everyDayOfWeek!Date(DayOfWeek.fri)); - assert(!cRange.empty); - - //Apparently, creating an immutable IntervalRange!Date doesn't work, so we can't test if - //empty works with it. However, since an immutable range is pretty useless, it's no great loss. - } - - //bwd - { - auto range = Interval!Date(Date(2010, 9, 19), Date(2010, 9, 21)).bwdRange( - everyDayOfWeek!(Date, Direction.bwd)(DayOfWeek.fri)); - - assert(!range.empty); - range.popFront(); - assert(range.empty); - - const cRange = Interval!Date( Date(2010, 7, 4), Date(2012, 1, 7)).bwdRange( - everyDayOfWeek!(Date, Direction.bwd)(DayOfWeek.fri)); - assert(!cRange.empty); - - //Apparently, creating an immutable IntervalRange!Date doesn't work, so we can't test if - //empty works with it. However, since an immutable range is pretty useless, it's no great loss. - } -} - -//Test IntervalRange's front. -@system unittest -{ - import std.datetime.date; - - //fwd - { - auto emptyRange = Interval!Date(Date(2010, 9, 19), Date(2010, 9, 20)).fwdRange( - everyDayOfWeek!Date(DayOfWeek.wed), PopFirst.yes); - assertThrown!DateTimeException( - (scope const IntervalRange!(Date, Direction.fwd) range){range.front;}(emptyRange)); - - auto range = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)).fwdRange(everyDayOfWeek!Date(DayOfWeek.wed)); - assert(range.front == Date(2010, 7, 4)); - - auto poppedRange = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)).fwdRange( - everyDayOfWeek!Date(DayOfWeek.wed), PopFirst.yes); - assert(poppedRange.front == Date(2010, 7, 7)); - - const cRange = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)).fwdRange(everyDayOfWeek!Date(DayOfWeek.fri)); - assert(cRange.front != Date.init); - } - - //bwd - { - auto emptyRange = Interval!Date(Date(2010, 9, 19), Date(2010, 9, 20)).bwdRange( - everyDayOfWeek!(Date, Direction.bwd)(DayOfWeek.wed), PopFirst.yes); - assertThrown!DateTimeException( - (scope const IntervalRange!(Date, Direction.bwd) range){range.front;}(emptyRange)); - - auto range = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)).bwdRange( - everyDayOfWeek!(Date, Direction.bwd)(DayOfWeek.wed)); - assert(range.front == Date(2012, 1, 7)); - - auto poppedRange = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)).bwdRange( - everyDayOfWeek!(Date, Direction.bwd)(DayOfWeek.wed), PopFirst.yes); - assert(poppedRange.front == Date(2012, 1, 4)); - - const cRange = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)).bwdRange( - everyDayOfWeek!(Date, Direction.bwd)(DayOfWeek.fri)); - assert(cRange.front != Date.init); - } -} - -//Test IntervalRange's popFront(). -@system unittest -{ - import std.datetime.date; - import std.range.primitives : walkLength; - - //fwd - { - auto emptyRange = Interval!Date(Date(2010, 9, 19), Date(2010, 9, 20)).fwdRange( - everyDayOfWeek!Date(DayOfWeek.wed), PopFirst.yes); - assertThrown!DateTimeException((IntervalRange!(Date, Direction.fwd) range){range.popFront();}(emptyRange)); - - auto range = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)).fwdRange( - everyDayOfWeek!Date(DayOfWeek.wed), PopFirst.yes); - auto expected = range.front; - - foreach (date; range) - { - assert(date == expected); - expected += dur!"days"(7); - } - - assert(walkLength(range) == 79); - - const cRange = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)).fwdRange(everyDayOfWeek!Date(DayOfWeek.fri)); - assert(cRange.front != Date.init); - } - - //bwd - { - auto emptyRange = Interval!Date(Date(2010, 9, 19), Date(2010, 9, 20)).bwdRange( - everyDayOfWeek!(Date, Direction.bwd)(DayOfWeek.wed), PopFirst.yes); - assertThrown!DateTimeException((IntervalRange!(Date, Direction.bwd) range){range.popFront();}(emptyRange)); - - auto range = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)).bwdRange( - everyDayOfWeek!(Date, Direction.bwd)(DayOfWeek.wed), PopFirst.yes); - auto expected = range.front; - - foreach (date; range) - { - assert(date == expected); - expected += dur!"days"(-7); - } - - assert(walkLength(range) == 79); - - const cRange = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)).bwdRange( - everyDayOfWeek!(Date, Direction.bwd)(DayOfWeek.fri)); - static assert(!__traits(compiles, cRange.popFront())); - } -} - -//Test IntervalRange's save. -@system unittest -{ - import std.datetime.date; - - //fwd - { - auto interval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); - auto func = everyDayOfWeek!Date(DayOfWeek.fri); - auto range = interval.fwdRange(func); - - assert(range.save == range); - } - - //bwd - { - auto interval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); - auto func = everyDayOfWeek!(Date, Direction.bwd)(DayOfWeek.fri); - auto range = interval.bwdRange(func); - - assert(range.save == range); - } -} - -//Test IntervalRange's interval. -@system unittest -{ - import std.datetime.date; - - //fwd - { - auto interval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); - auto func = everyDayOfWeek!Date(DayOfWeek.fri); - auto range = interval.fwdRange(func); - - assert(range.interval == interval); - - const cRange = range; - assert(!cRange.interval.empty); - } - - //bwd - { - auto interval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); - auto func = everyDayOfWeek!(Date, Direction.bwd)(DayOfWeek.fri); - auto range = interval.bwdRange(func); - - assert(range.interval == interval); - - const cRange = range; - assert(!cRange.interval.empty); - } -} - -//Test IntervalRange's func. -@system unittest -{ - import std.datetime.date; - - //fwd - { - auto interval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); - auto func = everyDayOfWeek!Date(DayOfWeek.fri); - auto range = interval.fwdRange(func); - - assert(range.func == func); - } - - //bwd - { - auto interval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); - auto func = everyDayOfWeek!(Date, Direction.bwd)(DayOfWeek.fri); - auto range = interval.bwdRange(func); - - assert(range.func == func); - } -} - -//Test IntervalRange's direction. -@system unittest -{ - import std.datetime.date; - - //fwd - { - auto interval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); - auto func = everyDayOfWeek!Date(DayOfWeek.fri); - auto range = interval.fwdRange(func); - - assert(range.direction == Direction.fwd); - - const cRange = range; - assert(cRange.direction == Direction.fwd); - } - - //bwd - { - auto interval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); - auto func = everyDayOfWeek!(Date, Direction.bwd)(DayOfWeek.fri); - auto range = interval.bwdRange(func); - - assert(range.direction == Direction.bwd); - - const cRange = range; - assert(cRange.direction == Direction.bwd); - } -} - - -/++ - A range over a `PosInfInterval`. It is an infinite range. - - `PosInfIntervalRange` is only ever constructed by `PosInfInterval`. - However, when it is constructed, it is given a function, `func`, which - is used to generate the time points which are iterated over. `func` - takes a time point and returns a time point of the same type. For - instance, to iterate - over all of the days in the interval `PosInfInterval!Date`, pass a - function to `PosInfInterval`'s `fwdRange` where that function took a - $(REF Date,std,datetime,date) and returned a $(REF Date,std,datetime,date) - which was one day later. That function would then be used by - `PosInfIntervalRange`'s `popFront` to iterate over the - $(REF Date,std,datetime,date)s in the interval - though obviously, since the - range is infinite, use a function such as `std.range.take` with it rather - than iterating over $(I all) of the dates. - - As the interval goes to positive infinity, the range is always iterated over - forwards, never backwards. `func` must generate a time point going in - the proper direction of iteration, or a - $(REF DateTimeException,std,datetime,date) will be thrown. So, the time - points that `func` generates must be later in time than the one passed to - it. If it's either identical or earlier in time, then a - $(REF DateTimeException,std,datetime,date) will be thrown. - +/ -struct PosInfIntervalRange(TP) -if (isTimePoint!TP) -{ -public: - - /++ - Params: - rhs = The `PosInfIntervalRange` to assign to this one. - +/ - ref PosInfIntervalRange opAssign(ref PosInfIntervalRange rhs) pure nothrow - { - _interval = rhs._interval; - _func = rhs._func; - return this; - } - - - /++ Ditto +/ - ref PosInfIntervalRange opAssign(PosInfIntervalRange rhs) pure nothrow - { - return this = rhs; - } - - - /++ - This is an infinite range, so it is never empty. - +/ - enum bool empty = false; - - - /++ - The first time point in the range. - +/ - @property TP front() const pure nothrow - { - return _interval.begin; - } - - - /++ - Pops `front` from the range, using `func` to generate the next - time point in the range. - - Throws: - $(REF DateTimeException,std,datetime,date) if the generated time - point is less than `front`. - +/ - void popFront() - { - auto begin = _func(_interval.begin); - _enforceCorrectDirection(begin); - _interval.begin = begin; - } - - - /++ - Returns a copy of `this`. - +/ - @property PosInfIntervalRange save() pure nothrow - { - return this; - } - - - /++ - The interval that this range currently covers. - +/ - @property PosInfInterval!TP interval() const pure nothrow - { - return cast(PosInfInterval!TP)_interval; - } - - - /++ - The function used to generate the next time point in the range. - +/ - TP delegate(scope const TP) func() pure nothrow @property - { - return _func; - } - - -private: - - /+ - Params: - interval = The interval that this range covers. - func = The function used to generate the time points which are - iterated over. - +/ - this(const PosInfInterval!TP interval, TP delegate(scope const TP) func) pure nothrow - { - _func = func; - _interval = interval; - } - - - /+ - Throws: - $(REF DateTimeException,std,datetime,date) if $(D_PARAM newTP) is - in the wrong direction. - +/ - void _enforceCorrectDirection(scope const TP newTP, size_t line = __LINE__) const - { - import std.format : format; - - enforce(newTP > _interval._begin, - new DateTimeException(format("Generated time point is before previous begin: prev [%s] new [%s]", - interval._begin, - newTP), - __FILE__, - line)); - } - - - PosInfInterval!TP _interval; - TP delegate(scope const TP) _func; -} - -//Test that PosInfIntervalRange satisfies the range predicates that it's supposed to satisfy. -@safe unittest -{ - import std.datetime.date; - import std.datetime.systime; - import std.range.primitives; - - static assert(isInputRange!(PosInfIntervalRange!Date)); - static assert(isForwardRange!(PosInfIntervalRange!Date)); - static assert(isInfinite!(PosInfIntervalRange!Date)); - - // Commented out due to bug https://issues.dlang.org/show_bug.cgi?id=4895 - // static assert(!isOutputRange!(PosInfIntervalRange!Date, Date)); - static assert(!isBidirectionalRange!(PosInfIntervalRange!Date)); - static assert(!isRandomAccessRange!(PosInfIntervalRange!Date)); - static assert(!hasSwappableElements!(PosInfIntervalRange!Date)); - static assert(!hasAssignableElements!(PosInfIntervalRange!Date)); - static assert(!hasLength!(PosInfIntervalRange!Date)); - static assert(!hasSlicing!(PosInfIntervalRange!Date)); - - static assert(is(ElementType!(PosInfIntervalRange!Date) == Date)); - static assert(is(ElementType!(PosInfIntervalRange!TimeOfDay) == TimeOfDay)); - static assert(is(ElementType!(PosInfIntervalRange!DateTime) == DateTime)); - static assert(is(ElementType!(PosInfIntervalRange!SysTime) == SysTime)); -} - -//Test construction of PosInfIntervalRange. -@safe unittest -{ - import std.datetime.date; - import std.datetime.systime; - - { - Date dateFunc(scope const Date date) { return date; } - auto posInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); - auto ir = PosInfIntervalRange!Date(posInfInterval, &dateFunc); - } - - { - TimeOfDay todFunc(scope const TimeOfDay tod) { return tod; } - auto posInfInterval = PosInfInterval!TimeOfDay(TimeOfDay(12, 1, 7)); - auto ir = PosInfIntervalRange!(TimeOfDay)(posInfInterval, &todFunc); - } - - { - DateTime dtFunc(scope const DateTime dt) { return dt; } - auto posInfInterval = PosInfInterval!DateTime(DateTime(2010, 7, 4, 12, 1, 7)); - auto ir = PosInfIntervalRange!(DateTime)(posInfInterval, &dtFunc); - } - - { - SysTime stFunc(scope const SysTime st) { return SysTime.init; } - auto posInfInterval = PosInfInterval!SysTime(SysTime(DateTime(2010, 7, 4, 12, 1, 7))); - auto ir = PosInfIntervalRange!SysTime(posInfInterval, &stFunc); - } -} - -//Test PosInfIntervalRange's front. -@system unittest -{ - import std.datetime.date; - - auto range = PosInfInterval!Date(Date(2010, 7, 4)).fwdRange(everyDayOfWeek!Date(DayOfWeek.wed)); - assert(range.front == Date(2010, 7, 4)); - - auto poppedRange = PosInfInterval!Date(Date(2010, 7, 4)).fwdRange(everyDayOfWeek!Date(DayOfWeek.wed), PopFirst.yes); - assert(poppedRange.front == Date(2010, 7, 7)); - - const cRange = PosInfInterval!Date(Date(2010, 7, 4)).fwdRange(everyDayOfWeek!Date(DayOfWeek.fri)); - assert(cRange.front != Date.init); -} - -//Test PosInfIntervalRange's popFront(). -@system unittest -{ - import std.datetime.date; - import std.range : take; - - auto range = PosInfInterval!Date(Date(2010, 7, 4)).fwdRange(everyDayOfWeek!Date(DayOfWeek.wed), PopFirst.yes); - auto expected = range.front; - - foreach (date; take(range, 79)) - { - assert(date == expected); - expected += dur!"days"(7); - } - - const cRange = PosInfInterval!Date(Date(2010, 7, 4)).fwdRange(everyDayOfWeek!Date(DayOfWeek.fri)); - static assert(!__traits(compiles, cRange.popFront())); -} - -//Test PosInfIntervalRange's save. -@system unittest -{ - import std.datetime.date; - - auto interval = PosInfInterval!Date(Date(2010, 7, 4)); - auto func = everyDayOfWeek!Date(DayOfWeek.fri); - auto range = interval.fwdRange(func); - - assert(range.save == range); -} - -//Test PosInfIntervalRange's interval. -@system unittest -{ - import std.datetime.date; - - auto interval = PosInfInterval!Date(Date(2010, 7, 4)); - auto func = everyDayOfWeek!Date(DayOfWeek.fri); - auto range = interval.fwdRange(func); - - assert(range.interval == interval); - - const cRange = range; - assert(!cRange.interval.empty); -} - -//Test PosInfIntervalRange's func. -@system unittest -{ - import std.datetime.date; - - auto interval = PosInfInterval!Date(Date(2010, 7, 4)); - auto func = everyDayOfWeek!Date(DayOfWeek.fri); - auto range = interval.fwdRange(func); - - assert(range.func == func); -} - - -/++ - A range over a `NegInfInterval`. It is an infinite range. - - `NegInfIntervalRange` is only ever constructed by `NegInfInterval`. - However, when it is constructed, it is given a function, `func`, which - is used to generate the time points which are iterated over. `func` - takes a time point and returns a time point of the same type. For - instance, to iterate over all of the days in the interval - `NegInfInterval!Date`, pass a function to `NegInfInterval`'s - `bwdRange` where that function took a $(REF Date,std,datetime,date) and - returned a $(REF Date,std,datetime,date) which was one day earlier. That - function would then be used by `NegInfIntervalRange`'s `popFront` to - iterate over the $(REF Date,std,datetime,date)s in the interval - though - obviously, since the range is infinite, use a function such as - `std.range.take` with it rather than iterating over $(I all) of the dates. - - As the interval goes to negative infinity, the range is always iterated over - backwards, never forwards. `func` must generate a time point going in - the proper direction of iteration, or a - $(REF DateTimeException,std,datetime,date) will be thrown. So, the time - points that `func` generates must be earlier in time than the one passed - to it. If it's either identical or later in time, then a - $(REF DateTimeException,std,datetime,date) will be thrown. - - Also note that while normally the `end` of an interval is excluded from - it, `NegInfIntervalRange` treats it as if it were included. This allows - for the same behavior as with `PosInfIntervalRange`. This works - because none of `NegInfInterval`'s functions which care about whether - `end` is included or excluded are ever called by - `NegInfIntervalRange`. `interval` returns a normal interval, so any - `NegInfInterval` functions which are called on it which care about - whether `end` is included or excluded will treat `end` as excluded. - +/ -struct NegInfIntervalRange(TP) -if (isTimePoint!TP) -{ -public: - - /++ - Params: - rhs = The `NegInfIntervalRange` to assign to this one. - +/ - ref NegInfIntervalRange opAssign(ref NegInfIntervalRange rhs) pure nothrow - { - _interval = rhs._interval; - _func = rhs._func; - - return this; - } - - - /++ Ditto +/ - ref NegInfIntervalRange opAssign(NegInfIntervalRange rhs) pure nothrow - { - return this = rhs; - } - - - /++ - This is an infinite range, so it is never empty. - +/ - enum bool empty = false; - - - /++ - The first time point in the range. - +/ - @property TP front() const pure nothrow - { - return _interval.end; - } - - - /++ - Pops `front` from the range, using `func` to generate the next - time point in the range. - - Throws: - $(REF DateTimeException,std,datetime,date) if the generated time - point is greater than `front`. - +/ - void popFront() - { - auto end = _func(_interval.end); - _enforceCorrectDirection(end); - _interval.end = end; - } - - - /++ - Returns a copy of `this`. - +/ - @property NegInfIntervalRange save() pure nothrow - { - return this; - } - - - /++ - The interval that this range currently covers. - +/ - @property NegInfInterval!TP interval() const pure nothrow - { - return cast(NegInfInterval!TP)_interval; - } - - - /++ - The function used to generate the next time point in the range. - +/ - TP delegate(scope const TP) func() pure nothrow @property - { - return _func; - } - - -private: - - /+ - Params: - interval = The interval that this range covers. - func = The function used to generate the time points which are - iterated over. - +/ - this(const NegInfInterval!TP interval, TP delegate(scope const TP) func) pure nothrow - { - _func = func; - _interval = interval; - } - - - /+ - Throws: - $(REF DateTimeException,std,datetime,date) if $(D_PARAM newTP) is - in the wrong direction. - +/ - void _enforceCorrectDirection(scope const TP newTP, size_t line = __LINE__) const - { - import std.format : format; - - enforce(newTP < _interval._end, - new DateTimeException(format("Generated time point is before previous end: prev [%s] new [%s]", - interval._end, - newTP), - __FILE__, - line)); - } - - - NegInfInterval!TP _interval; - TP delegate(scope const TP) _func; -} - -//Test that NegInfIntervalRange satisfies the range predicates that it's supposed to satisfy. -@safe unittest -{ - import std.datetime.date; - import std.range.primitives; - - static assert(isInputRange!(NegInfIntervalRange!Date)); - static assert(isForwardRange!(NegInfIntervalRange!Date)); - static assert(isInfinite!(NegInfIntervalRange!Date)); - - // Commented out due to bug https://issues.dlang.org/show_bug.cgi?id=4895 - // static assert(!isOutputRange!(NegInfIntervalRange!Date, Date)); - static assert(!isBidirectionalRange!(NegInfIntervalRange!Date)); - static assert(!isRandomAccessRange!(NegInfIntervalRange!Date)); - static assert(!hasSwappableElements!(NegInfIntervalRange!Date)); - static assert(!hasAssignableElements!(NegInfIntervalRange!Date)); - static assert(!hasLength!(NegInfIntervalRange!Date)); - static assert(!hasSlicing!(NegInfIntervalRange!Date)); - - static assert(is(ElementType!(NegInfIntervalRange!Date) == Date)); - static assert(is(ElementType!(NegInfIntervalRange!TimeOfDay) == TimeOfDay)); - static assert(is(ElementType!(NegInfIntervalRange!DateTime) == DateTime)); -} - -//Test construction of NegInfIntervalRange. -@safe unittest -{ - import std.datetime.date; - import std.datetime.systime; - - { - Date dateFunc(scope const Date date) { return date; } - auto negInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); - auto ir = NegInfIntervalRange!Date(negInfInterval, &dateFunc); - } - - { - TimeOfDay todFunc(scope const TimeOfDay tod) { return tod; } - auto negInfInterval = NegInfInterval!TimeOfDay(TimeOfDay(14, 0, 0)); - auto ir = NegInfIntervalRange!(TimeOfDay)(negInfInterval, &todFunc); - } - - { - DateTime dtFunc(scope const DateTime dt) { return dt; } - auto negInfInterval = NegInfInterval!DateTime(DateTime(2012, 1, 7, 14, 0, 0)); - auto ir = NegInfIntervalRange!(DateTime)(negInfInterval, &dtFunc); - } - - { - SysTime stFunc(scope const SysTime st) { return SysTime.init; } - auto negInfInterval = NegInfInterval!SysTime(SysTime(DateTime(2012, 1, 7, 14, 0, 0))); - auto ir = NegInfIntervalRange!(SysTime)(negInfInterval, &stFunc); - } -} - -//Test NegInfIntervalRange's front. -@system unittest -{ - import std.datetime.date; - - auto range = NegInfInterval!Date(Date(2012, 1, 7)).bwdRange(everyDayOfWeek!(Date, Direction.bwd)(DayOfWeek.wed)); - assert(range.front == Date(2012, 1, 7)); - - auto poppedRange = NegInfInterval!Date(Date(2012, 1, 7)).bwdRange( - everyDayOfWeek!(Date, Direction.bwd)(DayOfWeek.wed), PopFirst.yes); - assert(poppedRange.front == Date(2012, 1, 4)); - - const cRange = NegInfInterval!Date(Date(2012, 1, 7)).bwdRange(everyDayOfWeek!(Date, Direction.bwd)(DayOfWeek.fri)); - assert(cRange.front != Date.init); -} - -//Test NegInfIntervalRange's popFront(). -@system unittest -{ - import std.datetime.date; - import std.range : take; - - auto range = NegInfInterval!Date(Date(2012, 1, 7)).bwdRange( - everyDayOfWeek!(Date, Direction.bwd)(DayOfWeek.wed), PopFirst.yes); - auto expected = range.front; - - foreach (date; take(range, 79)) - { - assert(date == expected); - expected += dur!"days"(-7); - } - - const cRange = NegInfInterval!Date(Date(2012, 1, 7)).bwdRange(everyDayOfWeek!(Date, Direction.bwd)(DayOfWeek.fri)); - static assert(!__traits(compiles, cRange.popFront())); -} - -//Test NegInfIntervalRange's save. -@system unittest -{ - import std.datetime.date; - - auto interval = NegInfInterval!Date(Date(2012, 1, 7)); - auto func = everyDayOfWeek!(Date, Direction.bwd)(DayOfWeek.fri); - auto range = interval.bwdRange(func); - - assert(range.save == range); -} - -//Test NegInfIntervalRange's interval. -@system unittest -{ - import std.datetime.date; - - auto interval = NegInfInterval!Date(Date(2012, 1, 7)); - auto func = everyDayOfWeek!(Date, Direction.bwd)(DayOfWeek.fri); - auto range = interval.bwdRange(func); - - assert(range.interval == interval); - - const cRange = range; - assert(!cRange.interval.empty); -} - -//Test NegInfIntervalRange's func. -@system unittest -{ - import std.datetime.date; - - auto interval = NegInfInterval!Date(Date(2012, 1, 7)); - auto func = everyDayOfWeek!(Date, Direction.bwd)(DayOfWeek.fri); - auto range = interval.bwdRange(func); - - assert(range.func == func); -} diff --git a/phobos/std/datetime/package.d b/phobos/std/datetime/package.d deleted file mode 100644 index 58e71e5..0000000 --- a/phobos/std/datetime/package.d +++ /dev/null @@ -1,167 +0,0 @@ -// Written in the D programming language - -/++ - $(SCRIPT inhibitQuickIndex = 1;) - - Phobos provides the following functionality for time: - - $(DIVC quickindex, - $(BOOKTABLE , - $(TR $(TH Functionality) $(TH Symbols) - ) - $(TR - $(TD Points in Time) - $(TD - $(REF_ALTTEXT Clock, Clock, std, datetime, systime)$(NBSP) - $(REF_ALTTEXT Date, Date, std, datetime, date)$(NBSP) - $(REF_ALTTEXT TimeOfDay, TimeOfDay, std, datetime, date)$(NBSP) - $(REF_ALTTEXT DateTime, DateTime, std, datetime, date)$(NBSP) - $(REF_ALTTEXT SysTime, SysTime, std, datetime, systime)$(NBSP) - ) - ) - $(TR - $(TD $(MREF_ALTTEXT Timezones, std, datetime, timezone)) - $(TD - $(REF_ALTTEXT TimeZone, TimeZone, std, datetime, timezone)$(NBSP) - $(REF_ALTTEXT UTC, UTC, std, datetime, timezone)$(NBSP) - $(REF_ALTTEXT LocalTime, LocalTime, std, datetime, timezone)$(NBSP) - $(REF_ALTTEXT PosixTimeZone, PosixTimeZone, std, datetime, timezone)$(NBSP) - $(REF_ALTTEXT WindowsTimeZone, WindowsTimeZone, std, datetime, timezone)$(NBSP) - $(REF_ALTTEXT SimpleTimeZone, SimpleTimeZone, std, datetime, timezone)$(NBSP) - ) - ) - $(TR - $(TD Intervals and Ranges of Time) - $(TD - $(REF_ALTTEXT Interval, Interval, std, datetime, interval)$(NBSP) - $(REF_ALTTEXT PosInfInterval, PosInfInterval, std, datetime, interval)$(NBSP) - $(REF_ALTTEXT NegInfInterval, NegInfInterval, std, datetime, interval)$(NBSP) - ) - ) - $(TR - $(TD $(MREF_ALTTEXT Durations of Time, core, time)) - $(TD - $(REF_ALTTEXT Duration, Duration, core, time)$(NBSP) - $(REF_ALTTEXT weeks, weeks, core, time)$(NBSP) - $(REF_ALTTEXT days, days, core, time)$(NBSP) - $(REF_ALTTEXT hours, hours, core, time)$(NBSP) - $(REF_ALTTEXT minutes, minutes, core, time)$(NBSP) - $(REF_ALTTEXT seconds, seconds, core, time)$(NBSP) - $(REF_ALTTEXT msecs, msecs, core, time)$(NBSP) - $(REF_ALTTEXT usecs, usecs, core, time)$(NBSP) - $(REF_ALTTEXT hnsecs, hnsecs, core, time)$(NBSP) - $(REF_ALTTEXT nsecs, nsecs, core, time)$(NBSP) - ) - ) - $(TR - $(TD Time Measurement and Benchmarking) - $(TD - $(REF_ALTTEXT MonoTime, MonoTime, core, time)$(NBSP) - $(REF_ALTTEXT StopWatch, StopWatch, std, datetime, stopwatch)$(NBSP) - $(REF_ALTTEXT benchmark, benchmark, std, datetime, stopwatch)$(NBSP) - ) - ) - )) - - This functionality is separated into the following modules: - - $(UL - $(LI $(MREF std, datetime, date) for points in time without timezones.) - $(LI $(MREF std, datetime, timezone) for classes which represent timezones.) - $(LI $(MREF std, datetime, systime) for a point in time with a timezone.) - $(LI $(MREF std, datetime, interval) for types which represent series of points in time.) - $(LI $(MREF std, datetime, stopwatch) for measuring time.) - ) - - See_Also: - $(MREF core, time)$(BR) - $(DDLINK intro-to-datetime, Introduction to std.datetime, - Introduction to std.datetime)
- $(HTTP en.wikipedia.org/wiki/ISO_8601, ISO 8601)
- $(HTTP en.wikipedia.org/wiki/Tz_database, - Wikipedia entry on TZ Database)
- $(HTTP en.wikipedia.org/wiki/List_of_tz_database_time_zones, - List of Time Zones)
- - License: $(HTTP www.boost.org/LICENSE_1_0.txt, Boost License 1.0). - Authors: $(HTTP jmdavisprog.com, Jonathan M Davis) and Kato Shoichi - Source: $(PHOBOSSRC std/datetime/package.d) -+/ -module std.datetime; - -/// Get the current time from the system clock. -@safe unittest -{ - import std.datetime.systime : SysTime, Clock; - - SysTime currentTime = Clock.currTime(); -} - -/** -Construct a specific point in time without timezone information -and get its ISO string. - */ -@safe unittest -{ - import std.datetime.date : DateTime; - - auto dt = DateTime(2018, 1, 1, 12, 30, 10); - assert(dt.toISOString() == "20180101T123010"); - assert(dt.toISOExtString() == "2018-01-01T12:30:10"); -} - -/** -Construct a specific point in time in the UTC timezone and -add two days. - */ -@safe unittest -{ - import std.datetime.systime : SysTime; - import std.datetime.timezone : UTC; - import core.time : days; - - auto st = SysTime(DateTime(2018, 1, 1, 12, 30, 10), UTC()); - assert(st.toISOExtString() == "2018-01-01T12:30:10Z"); - st += 2.days; - assert(st.toISOExtString() == "2018-01-03T12:30:10Z"); -} - -public import core.time; -public import std.datetime.date; -public import std.datetime.interval; -public import std.datetime.systime; -public import std.datetime.timezone; - -import core.exception : AssertError; -import std.functional : unaryFun; -import std.traits; -import std.typecons : Flag, Yes, No; - - -// Verify module example. -@safe unittest -{ - auto currentTime = Clock.currTime(); - auto timeString = currentTime.toISOExtString(); - auto restoredTime = SysTime.fromISOExtString(timeString); -} - -// Verify Examples for core.time.Duration which couldn't be in core.time. -@safe unittest -{ - assert(std.datetime.Date(2010, 9, 7) + dur!"days"(5) == - std.datetime.Date(2010, 9, 12)); - - assert(std.datetime.Date(2010, 9, 7) - std.datetime.Date(2010, 10, 3) == - dur!"days"(-26)); -} - -@safe unittest -{ - import std.traits : hasUnsharedAliasing; - /* https://issues.dlang.org/show_bug.cgi?id=6642 */ - static assert(!hasUnsharedAliasing!Date); - static assert(!hasUnsharedAliasing!TimeOfDay); - static assert(!hasUnsharedAliasing!DateTime); - static assert(!hasUnsharedAliasing!SysTime); -} diff --git a/phobos/std/datetime/stopwatch.d b/phobos/std/datetime/stopwatch.d deleted file mode 100644 index 3c99f79..0000000 --- a/phobos/std/datetime/stopwatch.d +++ /dev/null @@ -1,481 +0,0 @@ -// Written in the D programming language - -/++ - Module containing some basic benchmarking and timing functionality. - - For convenience, this module publicly imports $(MREF core,time). - -$(SCRIPT inhibitQuickIndex = 1;) -$(DIVC quickindex, -$(BOOKTABLE, -$(TR $(TH Category) $(TH Functions)) -$(TR $(TD Main functionality) $(TD - $(LREF StopWatch) - $(LREF benchmark) -)) -$(TR $(TD Flags) $(TD - $(LREF AutoStart) -)) -)) - - $(RED Unlike the other modules in std.datetime, this module is not currently - publicly imported in std.datetime.package, because the old - versions of this functionality which use - $(REF TickDuration,core,time) are in std.datetime.package and would - conflict with the symbols in this module. After the old symbols have - gone through the deprecation cycle and have been fully removed, then - this module will be publicly imported in std.datetime.package. The - old, deprecated symbols has been removed from the documentation in - December 2019 and currently scheduled to be fully removed from Phobos - after 2.094.) - - So, for now, when using std.datetime.stopwatch, if other modules from - std.datetime are needed, then either import them individually rather than - importing std.datetime, or use selective or static imports to import - std.datetime.stopwatch. e.g. - - ---------------------------------------------------------------------------- - import std.datetime; - import std.datetime.stopwatch : benchmark, StopWatch; - ---------------------------------------------------------------------------- - - The compiler will then know to use the symbols from std.datetime.stopwatch - rather than the deprecated ones from std.datetime.package. - - License: $(HTTP www.boost.org/LICENSE_1_0.txt, Boost License 1.0). - Authors: $(HTTP jmdavisprog.com, Jonathan M Davis) and Kato Shoichi - Source: $(PHOBOSSRC std/datetime/stopwatch.d) -+/ -module std.datetime.stopwatch; - -public import core.time; -import std.typecons : Flag; -version (LDC) import ldc.attributes; - -/++ - Used by StopWatch to indicate whether it should start immediately upon - construction. - - If set to `AutoStart.no`, then the StopWatch is not started when it is - constructed. - - Otherwise, if set to `AutoStart.yes`, then the StopWatch is started when - it is constructed. - +/ -alias AutoStart = Flag!"autoStart"; - - -/++ - StopWatch is used to measure time just like one would do with a physical - stopwatch, including stopping, restarting, and/or resetting it. - - $(REF MonoTime,core,time) is used to hold the time, and it uses the system's - monotonic clock, which is high precision and never counts backwards (unlike - the wall clock time, which $(I can) count backwards, which is why - $(REF SysTime,std,datetime,systime) should not be used for timing). - - Note that the precision of StopWatch differs from system to system. It is - impossible for it to be the same for all systems, since the precision of the - system clock and other system-dependent and situation-dependent factors - (such as the overhead of a context switch between threads) varies from - system to system and can affect StopWatch's accuracy. - +/ -struct StopWatch -{ -public: - - /++ - Constructs a StopWatch. Whether it starts immediately depends on the - $(LREF AutoStart) argument. - - If `StopWatch.init` is used, then the constructed StopWatch isn't - running (and can't be, since no constructor ran). - +/ - this(AutoStart autostart) @safe nothrow @nogc - { - if (autostart) - start(); - } - - /// - @system nothrow @nogc unittest - { - import core.thread : Thread; - - { - auto sw = StopWatch(AutoStart.yes); - assert(sw.running); - Thread.sleep(usecs(1)); - assert(sw.peek() > Duration.zero); - } - { - auto sw = StopWatch(AutoStart.no); - assert(!sw.running); - Thread.sleep(usecs(1)); - assert(sw.peek() == Duration.zero); - } - { - StopWatch sw; - assert(!sw.running); - Thread.sleep(usecs(1)); - assert(sw.peek() == Duration.zero); - } - - assert(StopWatch.init == StopWatch(AutoStart.no)); - assert(StopWatch.init != StopWatch(AutoStart.yes)); - } - - - /++ - Resets the StopWatch. - - The StopWatch can be reset while it's running, and resetting it while - it's running will not cause it to stop. - +/ - void reset() @safe nothrow @nogc - { - if (_running) - _timeStarted = MonoTime.currTime; - _ticksElapsed = 0; - } - - /// - @system nothrow @nogc unittest - { - import core.thread : Thread; - - auto sw = StopWatch(AutoStart.yes); - Thread.sleep(usecs(1)); - sw.stop(); - assert(sw.peek() > Duration.zero); - sw.reset(); - assert(sw.peek() == Duration.zero); - } - - @system nothrow @nogc unittest - { - import core.thread : Thread; - - auto sw = StopWatch(AutoStart.yes); - Thread.sleep(msecs(1)); - assert(sw.peek() > msecs(1)); - immutable before = MonoTime.currTime; - - // Just in case the system clock is slow enough or the system is fast - // enough for the call to MonoTime.currTime inside of reset to get - // the same that we just got by calling MonoTime.currTime. - Thread.sleep(usecs(1)); - - sw.reset(); - assert(sw.peek() < msecs(1)); - assert(sw._timeStarted > before); - assert(sw._timeStarted <= MonoTime.currTime); - } - - - /++ - Starts the StopWatch. - - start should not be called if the StopWatch is already running. - +/ - void start() @safe nothrow @nogc - in { assert(!_running, "start was called when the StopWatch was already running."); } - do - { - _running = true; - _timeStarted = MonoTime.currTime; - } - - /// - @system nothrow @nogc unittest - { - import core.thread : Thread; - - StopWatch sw; - assert(!sw.running); - assert(sw.peek() == Duration.zero); - sw.start(); - assert(sw.running); - Thread.sleep(usecs(1)); - assert(sw.peek() > Duration.zero); - } - - - /++ - Stops the StopWatch. - - stop should not be called if the StopWatch is not running. - +/ - void stop() @safe nothrow @nogc - in { assert(_running, "stop was called when the StopWatch was not running."); } - do - { - _running = false; - _ticksElapsed += MonoTime.currTime.ticks - _timeStarted.ticks; - } - - /// - @system nothrow @nogc unittest - { - import core.thread : Thread; - - auto sw = StopWatch(AutoStart.yes); - assert(sw.running); - Thread.sleep(usecs(1)); - immutable t1 = sw.peek(); - assert(t1 > Duration.zero); - - sw.stop(); - assert(!sw.running); - immutable t2 = sw.peek(); - assert(t2 >= t1); - immutable t3 = sw.peek(); - assert(t2 == t3); - } - - - /++ - Peek at the amount of time that the StopWatch has been running. - - This does not include any time during which the StopWatch was stopped but - does include $(I all) of the time that it was running and not just the - time since it was started last. - - Calling $(LREF reset) will reset this to `Duration.zero`. - +/ - Duration peek() @safe const nothrow @nogc - { - enum hnsecsPerSecond = convert!("seconds", "hnsecs")(1); - immutable hnsecsMeasured = convClockFreq(_ticksElapsed, MonoTime.ticksPerSecond, hnsecsPerSecond); - return _running ? MonoTime.currTime - _timeStarted + hnsecs(hnsecsMeasured) - : hnsecs(hnsecsMeasured); - } - - /// - @system nothrow @nogc unittest - { - import core.thread : Thread; - - auto sw = StopWatch(AutoStart.no); - assert(sw.peek() == Duration.zero); - sw.start(); - - Thread.sleep(usecs(1)); - assert(sw.peek() >= usecs(1)); - - Thread.sleep(usecs(1)); - assert(sw.peek() >= usecs(2)); - - sw.stop(); - immutable stopped = sw.peek(); - Thread.sleep(usecs(1)); - assert(sw.peek() == stopped); - - sw.start(); - Thread.sleep(usecs(1)); - assert(sw.peek() > stopped); - } - - @safe nothrow @nogc unittest - { - assert(StopWatch.init.peek() == Duration.zero); - } - - - /++ - Sets the total time which the StopWatch has been running (i.e. what peek - returns). - - The StopWatch does not have to be stopped for setTimeElapsed to be - called, nor will calling it cause the StopWatch to stop. - +/ - void setTimeElapsed(Duration timeElapsed) @safe nothrow @nogc - { - enum hnsecsPerSecond = convert!("seconds", "hnsecs")(1); - _ticksElapsed = convClockFreq(timeElapsed.total!"hnsecs", hnsecsPerSecond, MonoTime.ticksPerSecond); - _timeStarted = MonoTime.currTime; - } - - /// - @system nothrow @nogc unittest - { - import core.thread : Thread; - - StopWatch sw; - sw.setTimeElapsed(hours(1)); - - // As discussed in MonoTime's documentation, converting between - // Duration and ticks is not exact, though it will be close. - // How exact it is depends on the frequency/resolution of the - // system's monotonic clock. - assert(abs(sw.peek() - hours(1)) < usecs(1)); - - sw.start(); - Thread.sleep(usecs(1)); - assert(sw.peek() > hours(1) + usecs(1)); - } - - - /++ - Returns whether this StopWatch is currently running. - +/ - @property bool running() @safe const pure nothrow @nogc - { - return _running; - } - - /// - @safe nothrow @nogc unittest - { - StopWatch sw; - assert(!sw.running); - sw.start(); - assert(sw.running); - sw.stop(); - assert(!sw.running); - } - - -private: - - // We track the ticks for the elapsed time rather than a Duration so that we - // don't lose any precision. - - bool _running = false; // Whether the StopWatch is currently running - MonoTime _timeStarted; // The time the StopWatch started measuring (i.e. when it was started or reset). - long _ticksElapsed; // Total time that the StopWatch ran before it was stopped last. -} - -/// Measure a time in milliseconds, microseconds, or nanoseconds -@safe nothrow @nogc unittest -{ - auto sw = StopWatch(AutoStart.no); - sw.start(); - // ... Insert operations to be timed here ... - sw.stop(); - - long msecs = sw.peek.total!"msecs"; - long usecs = sw.peek.total!"usecs"; - long nsecs = sw.peek.total!"nsecs"; - - assert(usecs >= msecs * 1000); - assert(nsecs >= usecs * 1000); -} - -/// -@system nothrow @nogc unittest -{ - import core.thread : Thread; - - auto sw = StopWatch(AutoStart.yes); - - Duration t1 = sw.peek(); - Thread.sleep(usecs(1)); - Duration t2 = sw.peek(); - assert(t2 > t1); - - Thread.sleep(usecs(1)); - sw.stop(); - - Duration t3 = sw.peek(); - assert(t3 > t2); - Duration t4 = sw.peek(); - assert(t3 == t4); - - sw.start(); - Thread.sleep(usecs(1)); - - Duration t5 = sw.peek(); - assert(t5 > t4); - - // If stopping or resetting the StopWatch is not required, then - // MonoTime can easily be used by itself without StopWatch. - auto before = MonoTime.currTime; - // do stuff... - auto timeElapsed = MonoTime.currTime - before; -} - - -/++ - Benchmarks code for speed assessment and comparison. - - Params: - fun = aliases of callable objects (e.g. function names). Each callable - object should take no arguments. - n = The number of times each function is to be executed. - - Returns: - The amount of time (as a $(REF Duration,core,time)) that it took to call - each function `n` times. The first value is the length of time that - it took to call `fun[0]` `n` times. The second value is the length - of time it took to call `fun[1]` `n` times. Etc. - +/ -Duration[fun.length] benchmark(fun...)(uint n) -{ - Duration[fun.length] result; - auto sw = StopWatch(AutoStart.yes); - - foreach (i, unused; fun) - { - sw.reset(); - foreach (_; 0 .. n) - fun[i](); - result[i] = sw.peek(); - } - - return result; -} - -/// -@safe unittest -{ - import std.conv : to; - - int a; - void f0() {} - void f1() { auto b = a; } - void f2() { auto b = to!string(a); } - auto r = benchmark!(f0, f1, f2)(10_000); - Duration f0Result = r[0]; // time f0 took to run 10,000 times - Duration f1Result = r[1]; // time f1 took to run 10,000 times - Duration f2Result = r[2]; // time f2 took to run 10,000 times -} - -@safe nothrow unittest -{ - import std.conv : to; - - int a; - @optStrategy("none") // LDC - void f0() nothrow {} - void f1() nothrow @trusted { - // do not allow any optimizer to optimize this function away - import core.thread : getpid; - import core.stdc.stdio : printf; - auto b = getpid.to!string; - if (getpid == 1) // never happens, but prevents optimization - printf("%p", &b); - } - - auto sw = StopWatch(AutoStart.yes); - auto r = benchmark!(f0, f1)(1000); - auto total = sw.peek(); - assert(r[0] >= Duration.zero); - assert(r[1] >= Duration.zero); - assert(r[0] <= total); - assert(r[1] <= total); -} - -@safe nothrow @nogc unittest -{ - int f0Count; - int f1Count; - int f2Count; - void f0() nothrow @nogc { ++f0Count; } - void f1() nothrow @nogc { ++f1Count; } - void f2() nothrow @nogc { ++f2Count; } - auto r = benchmark!(f0, f1, f2)(552); - assert(f0Count == 552); - assert(f1Count == 552); - assert(f2Count == 552); -} diff --git a/phobos/std/datetime/systime.d b/phobos/std/datetime/systime.d deleted file mode 100644 index a1d8ef3..0000000 --- a/phobos/std/datetime/systime.d +++ /dev/null @@ -1,11915 +0,0 @@ -// Written in the D programming language - -/++ - -$(DIVC quickindex, -$(BOOKTABLE, -$(TR $(TH Category) $(TH Functions)) -$(TR $(TD Types) $(TD - $(LREF Clock) - $(LREF SysTime) - $(LREF DosFileTime) -)) -$(TR $(TD Conversion) $(TD - $(LREF parseRFC822DateTime) - $(LREF DosFileTimeToSysTime) - $(LREF FILETIMEToStdTime) - $(LREF FILETIMEToSysTime) - $(LREF stdTimeToFILETIME) - $(LREF stdTimeToUnixTime) - $(LREF SYSTEMTIMEToSysTime) - $(LREF SysTimeToDosFileTime) - $(LREF SysTimeToFILETIME) - $(LREF SysTimeToSYSTEMTIME) - $(LREF unixTimeToStdTime) -)) -)) - - License: $(HTTP www.boost.org/LICENSE_1_0.txt, Boost License 1.0). - Authors: $(HTTP jmdavisprog.com, Jonathan M Davis) - Source: $(PHOBOSSRC std/datetime/systime.d) -+/ -module std.datetime.systime; - -version (OSX) - version = Darwin; -else version (iOS) - version = Darwin; -else version (TVOS) - version = Darwin; -else version (WatchOS) - version = Darwin; - -/// Get the current time as a $(LREF SysTime) -@safe unittest -{ - import std.datetime.timezone : LocalTime; - SysTime today = Clock.currTime(); - assert(today.timezone is LocalTime()); -} - -/// Construct a $(LREF SysTime) from a ISO time string -@safe unittest -{ - import std.datetime.date : DateTime; - import std.datetime.timezone : UTC; - - auto st = SysTime.fromISOExtString("2018-01-01T10:30:00Z"); - assert(st == SysTime(DateTime(2018, 1, 1, 10, 30, 0), UTC())); -} - -/// Make a specific point in time in the New York timezone -@safe unittest -{ - import core.time : hours; - import std.datetime.date : DateTime; - import std.datetime.timezone : SimpleTimeZone; - - auto ny = SysTime( - DateTime(2018, 1, 1, 10, 30, 0), - new immutable SimpleTimeZone(-5.hours, "America/New_York") - ); - - // ISO standard time strings - assert(ny.toISOString() == "20180101T103000-05:00"); - assert(ny.toISOExtString() == "2018-01-01T10:30:00-05:00"); -} - -// Note: reconsider using specific imports below after -// https://issues.dlang.org/show_bug.cgi?id=17630 has been fixed -import core.time;// : ClockType, convert, dur, Duration, seconds, TimeException; -import std.datetime.date;// : _monthNames, AllowDayOverflow, CmpTimeUnits, Date, - //DateTime, DateTimeException, DayOfWeek, enforceValid, getDayOfWeek, maxDay, - //Month, splitUnitsFromHNSecs, TimeOfDay, validTimeUnits, yearIsLeapYear; -import std.datetime.timezone;// : LocalTime, SimpleTimeZone, TimeZone, UTC; -import std.exception : enforce; -import std.format : format; -import std.range.primitives; -import std.traits : isIntegral, isSigned, isSomeString, isNarrowString; - -version (Windows) -{ - import core.stdc.time : time_t; - import core.sys.windows.winbase; - import core.sys.windows.winnt; - import core.sys.windows.winsock2; -} -else version (Posix) -{ - import core.sys.posix.signal : timespec; - import core.sys.posix.sys.types : time_t; -} - -version (StdUnittest) -{ - import core.exception : AssertError; - import std.exception : assertThrown; -} - - -@safe unittest -{ - initializeTests(); -} - -version (unittest) private bool clockSupported(ClockType c) -{ - // Skip unsupported clocks on older linux kernels, assume that only - // CLOCK_MONOTONIC and CLOCK_REALTIME exist, as that is the lowest - // common denominator supported by all versions of Linux pre-2.6.12. - version (Linux_Pre_2639) - return c == ClockType.normal || c == ClockType.precise; - else - return true; -} - -/++ - Effectively a namespace to make it clear that the methods it contains are - getting the time from the system clock. It cannot be instantiated. - +/ -final class Clock -{ -public: - - /++ - Returns the current time in the given time zone. - - Params: - clockType = The $(REF ClockType, core,time) indicates which system - clock to use to get the current time. Very few programs - need to use anything other than the default. - tz = The time zone for the SysTime that's returned. - - Throws: - $(REF DateTimeException,std,datetime,date) if it fails to get the - time. - +/ - static SysTime currTime(ClockType clockType = ClockType.normal)(immutable TimeZone tz = LocalTime()) @safe - { - return SysTime(currStdTime!clockType, tz); - } - - @safe unittest - { - import std.format : format; - import core.time; - assert(currTime().timezone is LocalTime()); - assert(currTime(UTC()).timezone is UTC()); - - // core.stdc.time.time does not always use unix time on Windows systems. - // In particular, dmc does not use unix time. If we can guarantee that - // the MS runtime uses unix time, then we may be able run this test - // then, but for now, we're just not going to run this test on Windows. - version (Posix) - { - static import core.stdc.time; - static import std.math; - immutable unixTimeD = currTime().toUnixTime(); - immutable unixTimeC = core.stdc.time.time(null); - assert(std.math.abs(unixTimeC - unixTimeD) <= 2); - } - - auto norm1 = Clock.currTime; - auto norm2 = Clock.currTime(UTC()); - assert(norm1 <= norm2, format("%s %s", norm1, norm2)); - assert(abs(norm1 - norm2) <= seconds(2)); - - import std.meta : AliasSeq; - static foreach (ct; AliasSeq!(ClockType.coarse, ClockType.precise, ClockType.second)) - {{ - static if (clockSupported(ct)) - { - auto value1 = Clock.currTime!ct; - auto value2 = Clock.currTime!ct(UTC()); - assert(value1 <= value2, format("%s %s (ClockType: %s)", value1, value2, ct)); - assert(abs(value1 - value2) <= seconds(2), format("ClockType.%s", ct)); - } - }} - } - - - /++ - Returns the number of hnsecs since midnight, January 1st, 1 A.D. for the - current time. - - Params: - clockType = The $(REF ClockType, core,time) indicates which system - clock to use to get the current time. Very few programs - need to use anything other than the default. - - Throws: - $(REF DateTimeException,std,datetime,date) if it fails to get the - time. - +/ - static @property long currStdTime(ClockType clockType = ClockType.normal)() @trusted - { - static if (clockType != ClockType.coarse && - clockType != ClockType.normal && - clockType != ClockType.precise && - clockType != ClockType.second) - { - static assert(0, format("ClockType.%s is not supported by Clock.currTime or Clock.currStdTime", clockType)); - } - - version (Windows) - { - FILETIME fileTime; - GetSystemTimeAsFileTime(&fileTime); - immutable result = FILETIMEToStdTime(&fileTime); - static if (clockType == ClockType.second) - { - // Ideally, this would use core.std.time.time, but the C runtime - // has to be using unix time for that to work, and that's not - // guaranteed on Windows. Digital Mars does not use unix time. - // MS may or may not. If it does, then this can be made to use - // core.stdc.time for MS, but for now, we'll leave it like this. - return convert!("seconds", "hnsecs")(convert!("hnsecs", "seconds")(result)); - } - else - return result; - } - else version (Posix) - { - static import core.stdc.time; - enum hnsecsToUnixEpoch = unixTimeToStdTime(0); - - version (Darwin) - { - static if (clockType == ClockType.second) - return unixTimeToStdTime(core.stdc.time.time(null)); - else - { - import core.sys.posix.sys.time : gettimeofday, timeval; - timeval tv = void; - // Posix gettimeofday called with a valid timeval address - // and a null second parameter doesn't fail. - gettimeofday(&tv, null); - return convert!("seconds", "hnsecs")(tv.tv_sec) + - tv.tv_usec * 10 + - hnsecsToUnixEpoch; - } - } - else version (linux) - { - static if (clockType == ClockType.second) - return unixTimeToStdTime(core.stdc.time.time(null)); - else - { - import core.sys.linux.time : CLOCK_REALTIME_COARSE; - import core.sys.posix.time : clock_gettime, CLOCK_REALTIME; - static if (clockType == ClockType.coarse) alias clockArg = CLOCK_REALTIME_COARSE; - else static if (clockType == ClockType.normal) alias clockArg = CLOCK_REALTIME; - else static if (clockType == ClockType.precise) alias clockArg = CLOCK_REALTIME; - else static assert(0, "Previous static if is wrong."); - timespec ts = void; - immutable error = clock_gettime(clockArg, &ts); - // Posix clock_gettime called with a valid address and valid clock_id is only - // permitted to fail if the number of seconds does not fit in time_t. If tv_sec - // is long or larger overflow won't happen before 292 billion years A.D. - static if (ts.tv_sec.max < long.max) - { - if (error) - throw new TimeException("Call to clock_gettime() failed"); - } - return convert!("seconds", "hnsecs")(ts.tv_sec) + - ts.tv_nsec / 100 + - hnsecsToUnixEpoch; - } - } - else version (FreeBSD) - { - import core.sys.freebsd.time : clock_gettime, CLOCK_REALTIME, - CLOCK_REALTIME_FAST, CLOCK_REALTIME_PRECISE, CLOCK_SECOND; - static if (clockType == ClockType.coarse) alias clockArg = CLOCK_REALTIME_FAST; - else static if (clockType == ClockType.normal) alias clockArg = CLOCK_REALTIME; - else static if (clockType == ClockType.precise) alias clockArg = CLOCK_REALTIME_PRECISE; - else static if (clockType == ClockType.second) alias clockArg = CLOCK_SECOND; - else static assert(0, "Previous static if is wrong."); - timespec ts = void; - immutable error = clock_gettime(clockArg, &ts); - // Posix clock_gettime called with a valid address and valid clock_id is only - // permitted to fail if the number of seconds does not fit in time_t. If tv_sec - // is long or larger overflow won't happen before 292 billion years A.D. - static if (ts.tv_sec.max < long.max) - { - if (error) - throw new TimeException("Call to clock_gettime() failed"); - } - return convert!("seconds", "hnsecs")(ts.tv_sec) + - ts.tv_nsec / 100 + - hnsecsToUnixEpoch; - } - else version (NetBSD) - { - static if (clockType == ClockType.second) - return unixTimeToStdTime(core.stdc.time.time(null)); - else - { - import core.sys.netbsd.time : clock_gettime, CLOCK_REALTIME; - timespec ts = void; - immutable error = clock_gettime(CLOCK_REALTIME, &ts); - // Posix clock_gettime called with a valid address and valid clock_id is only - // permitted to fail if the number of seconds does not fit in time_t. If tv_sec - // is long or larger overflow won't happen before 292 billion years A.D. - static if (ts.tv_sec.max < long.max) - { - if (error) - throw new TimeException("Call to clock_gettime() failed"); - } - return convert!("seconds", "hnsecs")(ts.tv_sec) + - ts.tv_nsec / 100 + - hnsecsToUnixEpoch; - } - } - else version (OpenBSD) - { - static if (clockType == ClockType.second) - return unixTimeToStdTime(core.stdc.time.time(null)); - else - { - import core.sys.openbsd.time : clock_gettime, CLOCK_REALTIME; - static if (clockType == ClockType.coarse) alias clockArg = CLOCK_REALTIME; - else static if (clockType == ClockType.normal) alias clockArg = CLOCK_REALTIME; - else static if (clockType == ClockType.precise) alias clockArg = CLOCK_REALTIME; - else static assert(0, "Previous static if is wrong."); - timespec ts; - if (clock_gettime(clockArg, &ts) != 0) - throw new TimeException("Call to clock_gettime() failed"); - return convert!("seconds", "hnsecs")(ts.tv_sec) + - ts.tv_nsec / 100 + - hnsecsToUnixEpoch; - } - } - else version (DragonFlyBSD) - { - import core.sys.dragonflybsd.time : clock_gettime, CLOCK_REALTIME, - CLOCK_REALTIME_FAST, CLOCK_REALTIME_PRECISE, CLOCK_SECOND; - static if (clockType == ClockType.coarse) alias clockArg = CLOCK_REALTIME_FAST; - else static if (clockType == ClockType.normal) alias clockArg = CLOCK_REALTIME; - else static if (clockType == ClockType.precise) alias clockArg = CLOCK_REALTIME_PRECISE; - else static if (clockType == ClockType.second) alias clockArg = CLOCK_SECOND; - else static assert(0, "Previous static if is wrong."); - timespec ts = void; - immutable error = clock_gettime(clockArg, &ts); - // Posix clock_gettime called with a valid address and valid clock_id is only - // permitted to fail if the number of seconds does not fit in time_t. If tv_sec - // is long or larger overflow won't happen before 292 billion years A.D. - static if (ts.tv_sec.max < long.max) - { - if (error) - throw new TimeException("Call to clock_gettime() failed"); - } - return convert!("seconds", "hnsecs")(ts.tv_sec) + - ts.tv_nsec / 100 + - hnsecsToUnixEpoch; - } - else version (Solaris) - { - static if (clockType == ClockType.second) - return unixTimeToStdTime(core.stdc.time.time(null)); - else - { - import core.sys.solaris.time : clock_gettime, CLOCK_REALTIME; - static if (clockType == ClockType.coarse) alias clockArg = CLOCK_REALTIME; - else static if (clockType == ClockType.normal) alias clockArg = CLOCK_REALTIME; - else static if (clockType == ClockType.precise) alias clockArg = CLOCK_REALTIME; - else static assert(0, "Previous static if is wrong."); - timespec ts = void; - immutable error = clock_gettime(clockArg, &ts); - // Posix clock_gettime called with a valid address and valid clock_id is only - // permitted to fail if the number of seconds does not fit in time_t. If tv_sec - // is long or larger overflow won't happen before 292 billion years A.D. - static if (ts.tv_sec.max < long.max) - { - if (error) - throw new TimeException("Call to clock_gettime() failed"); - } - return convert!("seconds", "hnsecs")(ts.tv_sec) + - ts.tv_nsec / 100 + - hnsecsToUnixEpoch; - } - } - else version (Hurd) - { - static if (clockType == ClockType.second) - return unixTimeToStdTime(core.stdc.time.time(null)); - else - { - import core.sys.hurd.time : CLOCK_REALTIME_COARSE; - import core.sys.posix.time : clock_gettime, CLOCK_REALTIME; - static if (clockType == ClockType.coarse) alias clockArg = CLOCK_REALTIME_COARSE; - else static if (clockType == ClockType.normal) alias clockArg = CLOCK_REALTIME; - else static if (clockType == ClockType.precise) alias clockArg = CLOCK_REALTIME; - else static assert(0, "Previous static if is wrong."); - timespec ts = void; - immutable error = clock_gettime(clockArg, &ts); - // Posix clock_gettime called with a valid address and valid clock_id is only - // permitted to fail if the number of seconds does not fit in time_t. If tv_sec - // is long or larger overflow won't happen before 292 billion years A.D. - static if (ts.tv_sec.max < long.max) - { - if (error) - throw new TimeException("Call to clock_gettime() failed"); - } - return convert!("seconds", "hnsecs")(ts.tv_sec) + - ts.tv_nsec / 100 + - hnsecsToUnixEpoch; - } - } - else static assert(0, "Unsupported OS"); - } - else static assert(0, "Unsupported OS"); - } - - @safe unittest - { - import std.format : format; - import std.math.algebraic : abs; - import std.meta : AliasSeq; - enum limit = convert!("seconds", "hnsecs")(2); - - auto norm1 = Clock.currStdTime; - auto norm2 = Clock.currStdTime; - assert(norm1 <= norm2, format("%s %s", norm1, norm2)); - assert(abs(norm1 - norm2) <= limit); - - static foreach (ct; AliasSeq!(ClockType.coarse, ClockType.precise, ClockType.second)) - {{ - static if (clockSupported(ct)) - { - auto value1 = Clock.currStdTime!ct; - auto value2 = Clock.currStdTime!ct; - assert(value1 <= value2, format("%s %s (ClockType: %s)", value1, value2, ct)); - assert(abs(value1 - value2) <= limit); - } - }} - } - - -private: - - @disable this(); -} - -/// Get the current time as a $(LREF SysTime) -@safe unittest -{ - import std.datetime.timezone : LocalTime; - SysTime today = Clock.currTime(); - assert(today.timezone is LocalTime()); -} - - -/++ - `SysTime` is the type used to get the current time from the - system or doing anything that involves time zones. Unlike - $(REF DateTime,std,datetime,date), the time zone is an integral part of - `SysTime` (though for local time applications, time zones can be ignored - and it will work, since it defaults to using the local time zone). It holds - its internal time in std time (hnsecs since midnight, January 1st, 1 A.D. - UTC), so it interfaces well with the system time. - - An $(I hnsec) (hecto-nanosecond) is 100 nanoseconds. There are 10,000,000 hnsecs in a second. - -$(PANEL - Unlike $(REF_SHORT DateTime,std,datetime,date), `SysTime` is not optimized for - calendar-based operations, and getting individual units from it such as - years or days is going to involve conversions and be less efficient. - - For calendar-based operations that don't - care about time zones, then $(REF_SHORT DateTime,std,datetime,date) would be - the type to use. For system time, use `SysTime`. -) -$(P - Casting a `SysTime` to one of the following types will perform a conversion: -) - * $(REF Date,std,datetime,date) - * $(REF_SHORT DateTime,std,datetime,date) - * $(REF_SHORT TimeOfDay,std,datetime,date) -$(P - To convert a - $(REF_SHORT Date,std,datetime,date) or $(REF_SHORT DateTime,std,datetime,date) to a - `SysTime`, use `SysTime`'s constructor, and pass in the intended time - zone with it (or don't pass in a $(REF TimeZone,std,datetime,timezone), and - the local time zone will be used). Be aware, however, that converting from a - $(REF_SHORT DateTime,std,datetime,date) to a `SysTime` will not necessarily - be 100% accurate due to DST (one hour of the year doesn't exist and another - occurs twice). To not risk any conversion errors, keep times as - `SysTime`s. Aside from DST though, there shouldn't be any conversion - problems. -) -$(PANEL - For using time zones other than local time or UTC, use - $(REF PosixTimeZone,std,datetime,timezone) on Posix systems (or on Windows, - if providing the TZ Database files), and use - $(REF WindowsTimeZone,std,datetime,timezone) on Windows systems. The time in - `SysTime` is kept internally in hnsecs from midnight, January 1st, 1 A.D. - UTC. Conversion error cannot happen when changing the time zone of a - `SysTime`. $(REF LocalTime,std,datetime,timezone) is the - $(REF_SHORT TimeZone,std,datetime,timezone) class which represents the local time, - and `UTC` is the $(REF_SHORT TimeZone,std,datetime,timezone) class which - represents UTC. `SysTime` uses $(REF_SHORT LocalTime,std,datetime,timezone) if - no $(REF_SHORT TimeZone,std,datetime,timezone) is provided. For more details on - time zones, see the documentation for $(REF_SHORT TimeZone,std,datetime,timezone), - $(REF_SHORT PosixTimeZone,std,datetime,timezone), and - $(REF_SHORT WindowsTimeZone,std,datetime,timezone). -) -$(P - `SysTime`'s range is from approximately 29,000 B.C. to approximately - 29,000 A.D. -) -See_Also: - $(RELATIVE_LINK2 .Clock.currTime, `Clock.currTime`) will return the current time as a `SysTime`. - +/ -struct SysTime -{ - import core.stdc.time : tm; - version (Posix) import core.sys.posix.sys.time : timeval; - import std.typecons : Rebindable; - -public: - - /++ - Params: - dateTime = The $(REF DateTime,std,datetime,date) to use to set - this $(LREF SysTime)'s internal std time. As - $(REF DateTime,std,datetime,date) has no concept of - time zone, tz is used as its time zone. - tz = The $(REF TimeZone,std,datetime,timezone) to use for this - $(LREF SysTime). If null, - $(REF LocalTime,std,datetime,timezone) will be used. The - given $(REF DateTime,std,datetime,date) is assumed to - be in the given time zone. - +/ - this(DateTime dateTime, return scope immutable TimeZone tz = null) return scope @safe nothrow - { - try - this(dateTime, Duration.zero, tz); - catch (Exception e) - assert(0, "SysTime's constructor threw when it shouldn't have."); - } - - @safe unittest - { - static void test(DateTime dt, immutable TimeZone tz, long expected) - { - auto sysTime = SysTime(dt, tz); - assert(sysTime._stdTime == expected); - assert(sysTime._timezone is (tz is null ? LocalTime() : tz), format("Given DateTime: %s", dt)); - } - - test(DateTime.init, UTC(), 0); - test(DateTime(1, 1, 1, 12, 30, 33), UTC(), 450_330_000_000L); - test(DateTime(0, 12, 31, 12, 30, 33), UTC(), -413_670_000_000L); - test(DateTime(1, 1, 1, 0, 0, 0), UTC(), 0); - test(DateTime(1, 1, 1, 0, 0, 1), UTC(), 10_000_000L); - test(DateTime(0, 12, 31, 23, 59, 59), UTC(), -10_000_000L); - - test(DateTime(1, 1, 1, 0, 0, 0), new immutable SimpleTimeZone(dur!"minutes"(-60)), 36_000_000_000L); - test(DateTime(1, 1, 1, 0, 0, 0), new immutable SimpleTimeZone(Duration.zero), 0); - test(DateTime(1, 1, 1, 0, 0, 0), new immutable SimpleTimeZone(dur!"minutes"(60)), -36_000_000_000L); - - static void testScope(scope ref DateTime dt) @safe - { - auto st = SysTime(dt); - } - } - - /++ - Params: - dateTime = The $(REF DateTime,std,datetime,date) to use to set - this $(LREF SysTime)'s internal std time. As - $(REF DateTime,std,datetime,date) has no concept of - time zone, tz is used as its time zone. - fracSecs = The fractional seconds portion of the time. - tz = The $(REF TimeZone,std,datetime,timezone) to use for this - $(LREF SysTime). If null, - $(REF LocalTime,std,datetime,timezone) will be used. The - given $(REF DateTime,std,datetime,date) is assumed to - be in the given time zone. - - Throws: - $(REF DateTimeException,std,datetime,date) if `fracSecs` is negative or if it's - greater than or equal to one second. - +/ - this(DateTime dateTime, Duration fracSecs, return scope immutable TimeZone tz = null) return scope @safe - { - enforce(fracSecs >= Duration.zero, new DateTimeException("A SysTime cannot have negative fractional seconds.")); - enforce(fracSecs < seconds(1), new DateTimeException("Fractional seconds must be less than one second.")); - auto nonNullTZ = tz is null ? LocalTime() : tz; - - immutable dateDiff = dateTime.date - Date.init; - immutable todDiff = dateTime.timeOfDay - TimeOfDay.init; - - immutable adjustedTime = dateDiff + todDiff + fracSecs; - immutable standardTime = nonNullTZ.tzToUTC(adjustedTime.total!"hnsecs"); - - this(standardTime, nonNullTZ); - } - - @safe unittest - { - import core.time; - static void test(DateTime dt, Duration fracSecs, immutable TimeZone tz, long expected) - { - auto sysTime = SysTime(dt, fracSecs, tz); - assert(sysTime._stdTime == expected); - assert(sysTime._timezone is (tz is null ? LocalTime() : tz), - format("Given DateTime: %s, Given Duration: %s", dt, fracSecs)); - } - - test(DateTime.init, Duration.zero, UTC(), 0); - test(DateTime(1, 1, 1, 12, 30, 33), Duration.zero, UTC(), 450_330_000_000L); - test(DateTime(0, 12, 31, 12, 30, 33), Duration.zero, UTC(), -413_670_000_000L); - test(DateTime(1, 1, 1, 0, 0, 0), msecs(1), UTC(), 10_000L); - test(DateTime(0, 12, 31, 23, 59, 59), msecs(999), UTC(), -10_000L); - - test(DateTime(0, 12, 31, 23, 59, 59), hnsecs(9_999_999), UTC(), -1); - test(DateTime(0, 12, 31, 23, 59, 59), hnsecs(1), UTC(), -9_999_999); - test(DateTime(0, 12, 31, 23, 59, 59), Duration.zero, UTC(), -10_000_000); - - assertThrown!DateTimeException(SysTime(DateTime.init, hnsecs(-1), UTC())); - assertThrown!DateTimeException(SysTime(DateTime.init, seconds(1), UTC())); - - static void testScope(scope ref DateTime dt, scope ref Duration d) @safe - { - auto st = SysTime(dt, d); - } - } - - /++ - Params: - date = The $(REF Date,std,datetime,date) to use to set this - $(LREF SysTime)'s internal std time. As - $(REF Date,std,datetime,date) has no concept of time zone, tz - is used as its time zone. - tz = The $(REF TimeZone,std,datetime,timezone) to use for this - $(LREF SysTime). If null, - $(REF LocalTime,std,datetime,timezone) will be used. The - given $(REF Date,std,datetime,date) is assumed to be in the - given time zone. - +/ - this(Date date, return scope immutable TimeZone tz = null) return scope @safe nothrow - { - _timezone = tz is null ? LocalTime() : tz; - - try - { - immutable adjustedTime = (date - Date(1, 1, 1)).total!"hnsecs"; - immutable standardTime = _timezone.tzToUTC(adjustedTime); - - this(standardTime, _timezone); - } - catch (Exception e) - assert(0, "Date's constructor through when it shouldn't have."); - } - - @safe unittest - { - static void test(Date d, immutable TimeZone tz, long expected) - { - auto sysTime = SysTime(d, tz); - assert(sysTime._stdTime == expected); - assert(sysTime._timezone is (tz is null ? LocalTime() : tz), format("Given Date: %s", d)); - } - - test(Date.init, UTC(), 0); - test(Date(1, 1, 1), UTC(), 0); - test(Date(1, 1, 2), UTC(), 864000000000); - test(Date(0, 12, 31), UTC(), -864000000000); - - static void testScope(scope ref Date d) @safe - { - auto st = SysTime(d); - } - } - - /++ - Note: - Whereas the other constructors take in the given date/time, assume - that it's in the given time zone, and convert it to hnsecs in UTC - since midnight, January 1st, 1 A.D. UTC - i.e. std time - this - constructor takes a std time, which is specifically already in UTC, - so no conversion takes place. Of course, the various getter - properties and functions will use the given time zone's conversion - function to convert the results to that time zone, but no conversion - of the arguments to this constructor takes place. - - Params: - stdTime = The number of hnsecs since midnight, January 1st, 1 A.D. - UTC. - tz = The $(REF TimeZone,std,datetime,timezone) to use for this - $(LREF SysTime). If null, - $(REF LocalTime,std,datetime,timezone) will be used. - +/ - this(long stdTime, return scope immutable TimeZone tz = null) return scope @safe pure nothrow - { - _stdTime = stdTime; - _timezone = tz is null ? LocalTime() : tz; - } - - @safe unittest - { - static void test(long stdTime, immutable TimeZone tz) - { - auto sysTime = SysTime(stdTime, tz); - assert(sysTime._stdTime == stdTime); - assert(sysTime._timezone is (tz is null ? LocalTime() : tz), format("Given stdTime: %s", stdTime)); - } - - foreach (stdTime; [-1234567890L, -250, 0, 250, 1235657390L]) - { - foreach (tz; testTZs) - test(stdTime, tz); - } - } - - - /++ - Params: - rhs = The $(LREF SysTime) to assign to this one. - - Returns: The `this` of this `SysTime`. - +/ - ref SysTime opAssign()(auto ref const(SysTime) rhs) scope return @safe pure nothrow - { - _stdTime = rhs._stdTime; - _timezone = rhs._timezone; - return this; - } - - @safe unittest - { - SysTime st; - st = SysTime(DateTime(2012, 12, 21, 1, 2, 3), UTC()); - assert(st == SysTime(DateTime(2012, 12, 21, 1, 2, 3), UTC())); - - const other = SysTime(DateTime(19, 1, 7, 13, 14, 15), LocalTime()); - st = other; - assert(st == other); - - version (none) // https://issues.dlang.org/show_bug.cgi?id=21175 - static void testScope(scope ref SysTime left, const scope SysTime right) @safe - { - left = right; - } - } - - - /++ - Checks for equality between this $(LREF SysTime) and the given - $(LREF SysTime). - - Note that the time zone is ignored. Only the internal - std times (which are in UTC) are compared. - +/ - bool opEquals()(auto ref const(SysTime) rhs) @safe const pure nothrow scope - { - return _stdTime == rhs._stdTime; - } - - @safe unittest - { - import std.range : chain; - - assert(SysTime(DateTime.init, UTC()) == SysTime(0, UTC())); - assert(SysTime(DateTime.init, UTC()) == SysTime(0)); - assert(SysTime(Date.init, UTC()) == SysTime(0)); - assert(SysTime(0) == SysTime(0)); - - static void test(DateTime dt, immutable TimeZone tz1, immutable TimeZone tz2) - { - auto st1 = SysTime(dt); - st1.timezone = tz1; - - auto st2 = SysTime(dt); - st2.timezone = tz2; - - assert(st1 == st2); - } - - foreach (tz1; testTZs) - { - foreach (tz2; testTZs) - { - foreach (dt; chain(testDateTimesBC, testDateTimesAD)) - test(dt, tz1, tz2); - } - } - - auto st = SysTime(DateTime(1999, 7, 6, 12, 33, 30)); - const cst = SysTime(DateTime(1999, 7, 6, 12, 33, 30)); - immutable ist = SysTime(DateTime(1999, 7, 6, 12, 33, 30)); - assert(st == st); - assert(st == cst); - assert(st == ist); - assert(cst == st); - assert(cst == cst); - assert(cst == ist); - assert(ist == st); - assert(ist == cst); - assert(ist == ist); - - static void testScope(scope ref SysTime left, const scope SysTime right) @safe - { - assert(left == right); - assert(right == left); - } - } - - - /++ - Compares this $(LREF SysTime) with the given $(LREF SysTime). - - Time zone is irrelevant when comparing $(LREF SysTime)s. - - Returns: - $(BOOKTABLE, - $(TR $(TD this < rhs) $(TD < 0)) - $(TR $(TD this == rhs) $(TD 0)) - $(TR $(TD this > rhs) $(TD > 0)) - ) - +/ - int opCmp()(auto ref const(SysTime) rhs) @safe const pure nothrow scope - { - if (_stdTime < rhs._stdTime) - return -1; - if (_stdTime > rhs._stdTime) - return 1; - return 0; - } - - @safe unittest - { - import std.algorithm.iteration : map; - import std.array : array; - import std.range : chain; - - assert(SysTime(DateTime.init, UTC()).opCmp(SysTime(0, UTC())) == 0); - assert(SysTime(DateTime.init, UTC()).opCmp(SysTime(0)) == 0); - assert(SysTime(Date.init, UTC()).opCmp(SysTime(0)) == 0); - assert(SysTime(0).opCmp(SysTime(0)) == 0); - - static void testEqual(SysTime st, immutable TimeZone tz1, immutable TimeZone tz2) - { - auto st1 = st; - st1.timezone = tz1; - - auto st2 = st; - st2.timezone = tz2; - - assert(st1.opCmp(st2) == 0); - } - - auto sts = array(map!SysTime(chain(testDateTimesBC, testDateTimesAD))); - - foreach (st; sts) - { - foreach (tz1; testTZs) - { - foreach (tz2; testTZs) - testEqual(st, tz1, tz2); - } - } - - static void testCmp(SysTime st1, immutable TimeZone tz1, SysTime st2, immutable TimeZone tz2) - { - st1.timezone = tz1; - st2.timezone = tz2; - assert(st1.opCmp(st2) < 0); - assert(st2.opCmp(st1) > 0); - } - - foreach (si, st1; sts) - { - foreach (st2; sts[si + 1 .. $]) - { - foreach (tz1; testTZs) - { - foreach (tz2; testTZs) - testCmp(st1, tz1, st2, tz2); - } - } - } - - auto st = SysTime(DateTime(1999, 7, 6, 12, 33, 30)); - const cst = SysTime(DateTime(1999, 7, 6, 12, 33, 30)); - immutable ist = SysTime(DateTime(1999, 7, 6, 12, 33, 30)); - assert(st.opCmp(st) == 0); - assert(st.opCmp(cst) == 0); - assert(st.opCmp(ist) == 0); - assert(cst.opCmp(st) == 0); - assert(cst.opCmp(cst) == 0); - assert(cst.opCmp(ist) == 0); - assert(ist.opCmp(st) == 0); - assert(ist.opCmp(cst) == 0); - assert(ist.opCmp(ist) == 0); - - static void testScope(scope ref SysTime left, const scope SysTime right) @safe - { - assert(left < right); - assert(right > left); - } - } - - - /++ - Returns: A hash of the $(LREF SysTime). - +/ - size_t toHash() const @nogc pure nothrow @safe scope - { - static if (is(size_t == ulong)) - return _stdTime; - else - { - // MurmurHash2 - enum ulong m = 0xc6a4a7935bd1e995UL; - enum ulong n = m * 16; - enum uint r = 47; - - ulong k = _stdTime; - k *= m; - k ^= k >> r; - k *= m; - - ulong h = n; - h ^= k; - h *= m; - - return cast(size_t) h; - } - } - - @safe unittest - { - assert(SysTime(0).toHash == SysTime(0).toHash); - assert(SysTime(DateTime(2000, 1, 1)).toHash == SysTime(DateTime(2000, 1, 1)).toHash); - assert(SysTime(DateTime(2000, 1, 1)).toHash != SysTime(DateTime(2000, 1, 2)).toHash); - - // test that timezones aren't taken into account - assert(SysTime(0, LocalTime()).toHash == SysTime(0, LocalTime()).toHash); - assert(SysTime(0, LocalTime()).toHash == SysTime(0, UTC()).toHash); - assert(SysTime(DateTime(2000, 1, 1), LocalTime()).toHash == SysTime(DateTime(2000, 1, 1), LocalTime()).toHash); - immutable zone = new SimpleTimeZone(dur!"minutes"(60)); - assert(SysTime(DateTime(2000, 1, 1, 1), zone).toHash == SysTime(DateTime(2000, 1, 1), UTC()).toHash); - assert(SysTime(DateTime(2000, 1, 1), zone).toHash != SysTime(DateTime(2000, 1, 1), UTC()).toHash); - - static void testScope(scope ref SysTime st) @safe - { - auto result = st.toHash(); - } - } - - - /++ - Year of the Gregorian Calendar. Positive numbers are A.D. Non-positive - are B.C. - +/ - @property short year() @safe const nothrow scope - { - return (cast(Date) this).year; - } - - @safe unittest - { - import std.range : chain; - static void test(SysTime sysTime, long expected) - { - assert(sysTime.year == expected, format("Value given: %s", sysTime)); - } - - test(SysTime(0, UTC()), 1); - test(SysTime(1, UTC()), 1); - test(SysTime(-1, UTC()), 0); - - foreach (year; chain(testYearsBC, testYearsAD)) - { - foreach (md; testMonthDays) - { - foreach (tod; testTODs) - { - auto dt = DateTime(Date(year, md.month, md.day), tod); - foreach (tz; testTZs) - { - foreach (fs; testFracSecs) - test(SysTime(dt, fs, tz), year); - } - } - } - } - - const cst = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); - immutable ist = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); - assert(cst.year == 1999); - assert(ist.year == 1999); - - static void testScope(scope ref SysTime st) @safe - { - auto result = st.year; - } - } - - /++ - Year of the Gregorian Calendar. Positive numbers are A.D. Non-positive - are B.C. - - Params: - year = The year to set this $(LREF SysTime)'s year to. - - Throws: - $(REF DateTimeException,std,datetime,date) if the new year is not - a leap year and the resulting date would be on February 29th. - +/ - @property void year(int year) @safe scope - { - auto hnsecs = adjTime; - auto days = splitUnitsFromHNSecs!"days"(hnsecs) + 1; - - if (hnsecs < 0) - { - hnsecs += convert!("hours", "hnsecs")(24); - --days; - } - - auto date = Date(cast(int) days); - date.year = year; - - immutable newDaysHNSecs = convert!("days", "hnsecs")(date.dayOfGregorianCal - 1); - adjTime = newDaysHNSecs + hnsecs; - } - - /// - @safe unittest - { - import std.datetime.date : DateTime; - - assert(SysTime(DateTime(1999, 7, 6, 9, 7, 5)).year == 1999); - assert(SysTime(DateTime(2010, 10, 4, 0, 0, 30)).year == 2010); - assert(SysTime(DateTime(-7, 4, 5, 7, 45, 2)).year == -7); - } - - @safe unittest - { - import std.range : chain; - - static void test(SysTime st, int year, SysTime expected) - { - st.year = year; - assert(st == expected); - } - - foreach (st; chain(testSysTimesBC, testSysTimesAD)) - { - auto dt = cast(DateTime) st; - - foreach (year; chain(testYearsBC, testYearsAD)) - { - auto e = SysTime(DateTime(year, dt.month, dt.day, dt.hour, dt.minute, dt.second), - st.fracSecs, - st.timezone); - test(st, year, e); - } - } - - foreach (fs; testFracSecs) - { - foreach (tz; testTZs) - { - foreach (tod; testTODs) - { - test(SysTime(DateTime(Date(1999, 2, 28), tod), fs, tz), 2000, - SysTime(DateTime(Date(2000, 2, 28), tod), fs, tz)); - test(SysTime(DateTime(Date(2000, 2, 28), tod), fs, tz), 1999, - SysTime(DateTime(Date(1999, 2, 28), tod), fs, tz)); - } - - foreach (tod; testTODsThrown) - { - auto st = SysTime(DateTime(Date(2000, 2, 29), tod), fs, tz); - assertThrown!DateTimeException(st.year = 1999); - } - } - } - - const cst = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); - immutable ist = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); - static assert(!__traits(compiles, cst.year = 7)); - static assert(!__traits(compiles, ist.year = 7)); - - static void testScope(scope ref SysTime st) @safe - { - st.year = 42; - } - } - - /++ - Year B.C. of the Gregorian Calendar counting year 0 as 1 B.C. - - Throws: - $(REF DateTimeException,std,datetime,date) if `isAD` is true. - +/ - @property ushort yearBC() @safe const scope - { - return (cast(Date) this).yearBC; - } - - /// - @safe unittest - { - import std.datetime.date : DateTime; - - assert(SysTime(DateTime(0, 1, 1, 12, 30, 33)).yearBC == 1); - assert(SysTime(DateTime(-1, 1, 1, 10, 7, 2)).yearBC == 2); - assert(SysTime(DateTime(-100, 1, 1, 4, 59, 0)).yearBC == 101); - } - - @safe unittest - { - import std.exception : assertNotThrown; - foreach (st; testSysTimesBC) - { - auto msg = format("SysTime: %s", st); - assertNotThrown!DateTimeException(st.yearBC, msg); - assert(st.yearBC == (st.year * -1) + 1, msg); - } - - foreach (st; [testSysTimesAD[0], testSysTimesAD[$/2], testSysTimesAD[$-1]]) - assertThrown!DateTimeException(st.yearBC, format("SysTime: %s", st)); - - auto st = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); - const cst = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); - immutable ist = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); - st.year = 12; - assert(st.year == 12); - static assert(!__traits(compiles, cst.year = 12)); - static assert(!__traits(compiles, ist.year = 12)); - - static void testScope(scope ref SysTime st) @safe - { - auto result = st.yearBC; - } - } - - - /++ - Year B.C. of the Gregorian Calendar counting year 0 as 1 B.C. - - Params: - year = The year B.C. to set this $(LREF SysTime)'s year to. - - Throws: - $(REF DateTimeException,std,datetime,date) if a non-positive value - is given. - +/ - @property void yearBC(int year) @safe scope - { - auto hnsecs = adjTime; - auto days = splitUnitsFromHNSecs!"days"(hnsecs) + 1; - - if (hnsecs < 0) - { - hnsecs += convert!("hours", "hnsecs")(24); - --days; - } - - auto date = Date(cast(int) days); - date.yearBC = year; - - immutable newDaysHNSecs = convert!("days", "hnsecs")(date.dayOfGregorianCal - 1); - adjTime = newDaysHNSecs + hnsecs; - } - - @safe unittest - { - auto st = SysTime(DateTime(2010, 1, 1, 7, 30, 0)); - st.yearBC = 1; - assert(st == SysTime(DateTime(0, 1, 1, 7, 30, 0))); - - st.yearBC = 10; - assert(st == SysTime(DateTime(-9, 1, 1, 7, 30, 0))); - } - - @safe unittest - { - import std.range : chain; - static void test(SysTime st, int year, SysTime expected) - { - st.yearBC = year; - assert(st == expected, format("SysTime: %s", st)); - } - - foreach (st; chain(testSysTimesBC, testSysTimesAD)) - { - auto dt = cast(DateTime) st; - - foreach (year; testYearsBC) - { - auto e = SysTime(DateTime(year, dt.month, dt.day, dt.hour, dt.minute, dt.second), - st.fracSecs, - st.timezone); - test(st, (year * -1) + 1, e); - } - } - - foreach (st; [testSysTimesBC[0], testSysTimesBC[$ - 1], testSysTimesAD[0], testSysTimesAD[$ - 1]]) - { - foreach (year; testYearsBC) - assertThrown!DateTimeException(st.yearBC = year); - } - - foreach (fs; testFracSecs) - { - foreach (tz; testTZs) - { - foreach (tod; testTODs) - { - test(SysTime(DateTime(Date(-1999, 2, 28), tod), fs, tz), 2001, - SysTime(DateTime(Date(-2000, 2, 28), tod), fs, tz)); - test(SysTime(DateTime(Date(-2000, 2, 28), tod), fs, tz), 2000, - SysTime(DateTime(Date(-1999, 2, 28), tod), fs, tz)); - } - - foreach (tod; testTODsThrown) - { - auto st = SysTime(DateTime(Date(-2000, 2, 29), tod), fs, tz); - assertThrown!DateTimeException(st.year = -1999); - } - } - } - - auto st = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); - const cst = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); - immutable ist = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); - st.yearBC = 12; - assert(st.yearBC == 12); - static assert(!__traits(compiles, cst.yearBC = 12)); - static assert(!__traits(compiles, ist.yearBC = 12)); - - static void testScope(scope ref SysTime st) @safe - { - st.yearBC = 42; - } - } - - - /++ - Month of a Gregorian Year. - +/ - @property Month month() @safe const nothrow scope - { - return (cast(Date) this).month; - } - - /// - @safe unittest - { - import std.datetime.date : DateTime; - - assert(SysTime(DateTime(1999, 7, 6, 9, 7, 5)).month == 7); - assert(SysTime(DateTime(2010, 10, 4, 0, 0, 30)).month == 10); - assert(SysTime(DateTime(-7, 4, 5, 7, 45, 2)).month == 4); - } - - @safe unittest - { - import std.range : chain; - - static void test(SysTime sysTime, Month expected) - { - assert(sysTime.month == expected, format("Value given: %s", sysTime)); - } - - test(SysTime(0, UTC()), Month.jan); - test(SysTime(1, UTC()), Month.jan); - test(SysTime(-1, UTC()), Month.dec); - - foreach (year; chain(testYearsBC, testYearsAD)) - { - foreach (md; testMonthDays) - { - foreach (tod; testTODs) - { - auto dt = DateTime(Date(year, md.month, md.day), tod); - foreach (fs; testFracSecs) - { - foreach (tz; testTZs) - test(SysTime(dt, fs, tz), md.month); - } - } - } - } - - const cst = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); - immutable ist = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); - assert(cst.month == 7); - assert(ist.month == 7); - - static void testScope(scope ref SysTime st) @safe - { - auto result = st.month; - } - } - - - /++ - Month of a Gregorian Year. - - Params: - month = The month to set this $(LREF SysTime)'s month to. - - Throws: - $(REF DateTimeException,std,datetime,date) if the given month is - not a valid month. - +/ - @property void month(Month month) @safe scope - { - auto hnsecs = adjTime; - auto days = splitUnitsFromHNSecs!"days"(hnsecs) + 1; - - if (hnsecs < 0) - { - hnsecs += convert!("hours", "hnsecs")(24); - --days; - } - - auto date = Date(cast(int) days); - date.month = month; - - immutable newDaysHNSecs = convert!("days", "hnsecs")(date.dayOfGregorianCal - 1); - adjTime = newDaysHNSecs + hnsecs; - } - - @safe unittest - { - import std.algorithm.iteration : filter; - import std.range : chain; - - static void test(SysTime st, Month month, SysTime expected) - { - st.month = cast(Month) month; - assert(st == expected); - } - - foreach (st; chain(testSysTimesBC, testSysTimesAD)) - { - auto dt = cast(DateTime) st; - - foreach (md; testMonthDays) - { - if (st.day > maxDay(dt.year, md.month)) - continue; - auto e = SysTime(DateTime(dt.year, md.month, dt.day, dt.hour, dt.minute, dt.second), - st.fracSecs, - st.timezone); - test(st, md.month, e); - } - } - - foreach (fs; testFracSecs) - { - foreach (tz; testTZs) - { - foreach (tod; testTODs) - { - foreach (year; filter!((a){return yearIsLeapYear(a);}) (chain(testYearsBC, testYearsAD))) - { - test(SysTime(DateTime(Date(year, 1, 29), tod), fs, tz), - Month.feb, - SysTime(DateTime(Date(year, 2, 29), tod), fs, tz)); - } - - foreach (year; chain(testYearsBC, testYearsAD)) - { - test(SysTime(DateTime(Date(year, 1, 28), tod), fs, tz), - Month.feb, - SysTime(DateTime(Date(year, 2, 28), tod), fs, tz)); - test(SysTime(DateTime(Date(year, 7, 30), tod), fs, tz), - Month.jun, - SysTime(DateTime(Date(year, 6, 30), tod), fs, tz)); - } - } - } - } - - foreach (fs; [testFracSecs[0], testFracSecs[$-1]]) - { - foreach (tz; testTZs) - { - foreach (tod; testTODsThrown) - { - foreach (year; [testYearsBC[$-3], testYearsBC[$-2], - testYearsBC[$-2], testYearsAD[0], - testYearsAD[$-2], testYearsAD[$-1]]) - { - auto day = yearIsLeapYear(year) ? 30 : 29; - auto st1 = SysTime(DateTime(Date(year, 1, day), tod), fs, tz); - assertThrown!DateTimeException(st1.month = Month.feb); - - auto st2 = SysTime(DateTime(Date(year, 7, 31), tod), fs, tz); - assertThrown!DateTimeException(st2.month = Month.jun); - } - } - } - } - - const cst = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); - immutable ist = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); - static assert(!__traits(compiles, cst.month = Month.dec)); - static assert(!__traits(compiles, ist.month = Month.dec)); - - static void testScope(scope ref SysTime st) @safe - { - st.month = Month.dec; - } - } - - /++ - Day of a Gregorian Month. - +/ - @property ubyte day() @safe const nothrow scope - { - return (cast(Date) this).day; - } - - /// - @safe unittest - { - import std.datetime.date : DateTime; - - assert(SysTime(DateTime(1999, 7, 6, 9, 7, 5)).day == 6); - assert(SysTime(DateTime(2010, 10, 4, 0, 0, 30)).day == 4); - assert(SysTime(DateTime(-7, 4, 5, 7, 45, 2)).day == 5); - } - - @safe unittest - { - import std.range : chain; - - static void test(SysTime sysTime, int expected) - { - assert(sysTime.day == expected, format("Value given: %s", sysTime)); - } - - test(SysTime(0, UTC()), 1); - test(SysTime(1, UTC()), 1); - test(SysTime(-1, UTC()), 31); - - foreach (year; chain(testYearsBC, testYearsAD)) - { - foreach (md; testMonthDays) - { - foreach (tod; testTODs) - { - auto dt = DateTime(Date(year, md.month, md.day), tod); - - foreach (tz; testTZs) - { - foreach (fs; testFracSecs) - test(SysTime(dt, fs, tz), md.day); - } - } - } - } - - const cst = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); - immutable ist = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); - assert(cst.day == 6); - assert(ist.day == 6); - - static void testScope(scope ref SysTime st) @safe - { - auto result = st.day; - } - } - - - /++ - Day of a Gregorian Month. - - Params: - day = The day of the month to set this $(LREF SysTime)'s day to. - - Throws: - $(REF DateTimeException,std,datetime,date) if the given day is not - a valid day of the current month. - +/ - @property void day(int day) @safe scope - { - auto hnsecs = adjTime; - auto days = splitUnitsFromHNSecs!"days"(hnsecs) + 1; - - if (hnsecs < 0) - { - hnsecs += convert!("hours", "hnsecs")(24); - --days; - } - - auto date = Date(cast(int) days); - date.day = day; - - immutable newDaysHNSecs = convert!("days", "hnsecs")(date.dayOfGregorianCal - 1); - adjTime = newDaysHNSecs + hnsecs; - } - - @safe unittest - { - import std.range : chain; - import std.traits : EnumMembers; - - foreach (day; chain(testDays)) - { - foreach (st; chain(testSysTimesBC, testSysTimesAD)) - { - auto dt = cast(DateTime) st; - - if (day > maxDay(dt.year, dt.month)) - continue; - auto expected = SysTime(DateTime(dt.year, dt.month, day, dt.hour, dt.minute, dt.second), - st.fracSecs, - st.timezone); - st.day = day; - assert(st == expected, format("[%s] [%s]", st, expected)); - } - } - - foreach (tz; testTZs) - { - foreach (tod; testTODs) - { - foreach (fs; testFracSecs) - { - foreach (year; chain(testYearsBC, testYearsAD)) - { - foreach (month; EnumMembers!Month) - { - auto st = SysTime(DateTime(Date(year, month, 1), tod), fs, tz); - immutable max = maxDay(year, month); - auto expected = SysTime(DateTime(Date(year, month, max), tod), fs, tz); - - st.day = max; - assert(st == expected, format("[%s] [%s]", st, expected)); - } - } - } - } - } - - foreach (tz; testTZs) - { - foreach (tod; testTODsThrown) - { - foreach (fs; [testFracSecs[0], testFracSecs[$-1]]) - { - foreach (year; [testYearsBC[$-3], testYearsBC[$-2], - testYearsBC[$-2], testYearsAD[0], - testYearsAD[$-2], testYearsAD[$-1]]) - { - foreach (month; EnumMembers!Month) - { - auto st = SysTime(DateTime(Date(year, month, 1), tod), fs, tz); - immutable max = maxDay(year, month); - - assertThrown!DateTimeException(st.day = max + 1); - } - } - } - } - } - - const cst = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); - immutable ist = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); - static assert(!__traits(compiles, cst.day = 27)); - static assert(!__traits(compiles, ist.day = 27)); - - static void testScope(scope ref SysTime st) @safe - { - st.day = 12; - } - } - - - /++ - Hours past midnight. - +/ - @property ubyte hour() @safe const nothrow scope - { - auto hnsecs = adjTime; - auto days = splitUnitsFromHNSecs!"days"(hnsecs) + 1; - - if (hnsecs < 0) - { - hnsecs += convert!("hours", "hnsecs")(24); - --days; - } - - return cast(ubyte) getUnitsFromHNSecs!"hours"(hnsecs); - } - - @safe unittest - { - import std.range : chain; - - static void test(SysTime sysTime, int expected) - { - assert(sysTime.hour == expected, format("Value given: %s", sysTime)); - } - - test(SysTime(0, UTC()), 0); - test(SysTime(1, UTC()), 0); - test(SysTime(-1, UTC()), 23); - - foreach (tz; testTZs) - { - foreach (year; chain(testYearsBC, testYearsAD)) - { - foreach (md; testMonthDays) - { - foreach (hour; testHours) - { - foreach (minute; testMinSecs) - { - foreach (second; testMinSecs) - { - auto dt = DateTime(Date(year, md.month, md.day), TimeOfDay(hour, minute, second)); - foreach (fs; testFracSecs) - test(SysTime(dt, fs, tz), hour); - } - } - } - } - } - } - - const cst = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); - immutable ist = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); - assert(cst.hour == 12); - assert(ist.hour == 12); - - static void testScope(scope ref SysTime st) @safe - { - auto result = st.hour; - } - } - - - /++ - Hours past midnight. - - Params: - hour = The hours to set this $(LREF SysTime)'s hour to. - - Throws: - $(REF DateTimeException,std,datetime,date) if the given hour are - not a valid hour of the day. - +/ - @property void hour(int hour) @safe scope - { - enforceValid!"hours"(hour); - - auto hnsecs = adjTime; - auto days = splitUnitsFromHNSecs!"days"(hnsecs); - immutable daysHNSecs = convert!("days", "hnsecs")(days); - immutable negative = hnsecs < 0; - - if (negative) - hnsecs += convert!("hours", "hnsecs")(24); - - hnsecs = removeUnitsFromHNSecs!"hours"(hnsecs); - hnsecs += convert!("hours", "hnsecs")(hour); - - if (negative) - hnsecs -= convert!("hours", "hnsecs")(24); - - adjTime = daysHNSecs + hnsecs; - } - - @safe unittest - { - import std.range : chain; - - foreach (hour; chain(testHours)) - { - foreach (st; chain(testSysTimesBC, testSysTimesAD)) - { - auto dt = cast(DateTime) st; - auto expected = SysTime(DateTime(dt.year, dt.month, dt.day, hour, dt.minute, dt.second), - st.fracSecs, - st.timezone); - st.hour = hour; - assert(st == expected, format("[%s] [%s]", st, expected)); - } - } - - auto st = testSysTimesAD[0]; - assertThrown!DateTimeException(st.hour = -1); - assertThrown!DateTimeException(st.hour = 60); - - const cst = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); - immutable ist = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); - static assert(!__traits(compiles, cst.hour = 27)); - static assert(!__traits(compiles, ist.hour = 27)); - - static void testScope(scope ref SysTime st) @safe - { - st.hour = 12; - } - } - - - /++ - Minutes past the current hour. - +/ - @property ubyte minute() @safe const nothrow scope - { - auto hnsecs = adjTime; - auto days = splitUnitsFromHNSecs!"days"(hnsecs) + 1; - - if (hnsecs < 0) - { - hnsecs += convert!("hours", "hnsecs")(24); - --days; - } - - hnsecs = removeUnitsFromHNSecs!"hours"(hnsecs); - - return cast(ubyte) getUnitsFromHNSecs!"minutes"(hnsecs); - } - - @safe unittest - { - import std.range : chain; - - static void test(SysTime sysTime, int expected) - { - assert(sysTime.minute == expected, format("Value given: %s", sysTime)); - } - - test(SysTime(0, UTC()), 0); - test(SysTime(1, UTC()), 0); - test(SysTime(-1, UTC()), 59); - - foreach (tz; testTZs) - { - foreach (year; chain(testYearsBC, testYearsAD)) - { - foreach (md; testMonthDays) - { - foreach (hour; testHours) - { - foreach (minute; testMinSecs) - { - foreach (second; testMinSecs) - { - auto dt = DateTime(Date(year, md.month, md.day), TimeOfDay(hour, minute, second)); - foreach (fs; testFracSecs) - test(SysTime(dt, fs, tz), minute); - } - } - } - } - } - } - - const cst = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); - immutable ist = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); - assert(cst.minute == 30); - assert(ist.minute == 30); - - static void testScope(scope ref SysTime st) @safe - { - auto result = st.minute; - } - } - - - /++ - Minutes past the current hour. - - Params: - minute = The minute to set this $(LREF SysTime)'s minute to. - - Throws: - $(REF DateTimeException,std,datetime,date) if the given minute are - not a valid minute of an hour. - +/ - @property void minute(int minute) @safe scope - { - enforceValid!"minutes"(minute); - - auto hnsecs = adjTime; - auto days = splitUnitsFromHNSecs!"days"(hnsecs); - immutable daysHNSecs = convert!("days", "hnsecs")(days); - immutable negative = hnsecs < 0; - - if (negative) - hnsecs += convert!("hours", "hnsecs")(24); - - immutable hour = splitUnitsFromHNSecs!"hours"(hnsecs); - hnsecs = removeUnitsFromHNSecs!"minutes"(hnsecs); - - hnsecs += convert!("hours", "hnsecs")(hour); - hnsecs += convert!("minutes", "hnsecs")(minute); - - if (negative) - hnsecs -= convert!("hours", "hnsecs")(24); - - adjTime = daysHNSecs + hnsecs; - } - - @safe unittest - { - import std.range : chain; - - foreach (minute; testMinSecs) - { - foreach (st; chain(testSysTimesBC, testSysTimesAD)) - { - auto dt = cast(DateTime) st; - auto expected = SysTime(DateTime(dt.year, dt.month, dt.day, dt.hour, minute, dt.second), - st.fracSecs, - st.timezone); - st.minute = minute; - assert(st == expected, format("[%s] [%s]", st, expected)); - } - } - - auto st = testSysTimesAD[0]; - assertThrown!DateTimeException(st.minute = -1); - assertThrown!DateTimeException(st.minute = 60); - - const cst = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); - immutable ist = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); - static assert(!__traits(compiles, cst.minute = 27)); - static assert(!__traits(compiles, ist.minute = 27)); - - static void testScope(scope ref SysTime st) @safe - { - st.minute = 12; - } - } - - - /++ - Seconds past the current minute. - +/ - @property ubyte second() @safe const nothrow scope - { - auto hnsecs = adjTime; - auto days = splitUnitsFromHNSecs!"days"(hnsecs) + 1; - - if (hnsecs < 0) - { - hnsecs += convert!("hours", "hnsecs")(24); - --days; - } - - hnsecs = removeUnitsFromHNSecs!"hours"(hnsecs); - hnsecs = removeUnitsFromHNSecs!"minutes"(hnsecs); - - return cast(ubyte) getUnitsFromHNSecs!"seconds"(hnsecs); - } - - @safe unittest - { - import std.range : chain; - - static void test(SysTime sysTime, int expected) - { - assert(sysTime.second == expected, format("Value given: %s", sysTime)); - } - - test(SysTime(0, UTC()), 0); - test(SysTime(1, UTC()), 0); - test(SysTime(-1, UTC()), 59); - - foreach (tz; testTZs) - { - foreach (year; chain(testYearsBC, testYearsAD)) - { - foreach (md; testMonthDays) - { - foreach (hour; testHours) - { - foreach (minute; testMinSecs) - { - foreach (second; testMinSecs) - { - auto dt = DateTime(Date(year, md.month, md.day), TimeOfDay(hour, minute, second)); - foreach (fs; testFracSecs) - test(SysTime(dt, fs, tz), second); - } - } - } - } - } - } - - const cst = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); - immutable ist = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); - assert(cst.second == 33); - assert(ist.second == 33); - - static void testScope(scope ref SysTime st) @safe - { - auto result = st.second; - } - } - - - /++ - Seconds past the current minute. - - Params: - second = The second to set this $(LREF SysTime)'s second to. - - Throws: - $(REF DateTimeException,std,datetime,date) if the given second are - not a valid second of a minute. - +/ - @property void second(int second) @safe scope - { - enforceValid!"seconds"(second); - - auto hnsecs = adjTime; - auto days = splitUnitsFromHNSecs!"days"(hnsecs); - immutable daysHNSecs = convert!("days", "hnsecs")(days); - immutable negative = hnsecs < 0; - - if (negative) - hnsecs += convert!("hours", "hnsecs")(24); - - immutable hour = splitUnitsFromHNSecs!"hours"(hnsecs); - immutable minute = splitUnitsFromHNSecs!"minutes"(hnsecs); - hnsecs = removeUnitsFromHNSecs!"seconds"(hnsecs); - - hnsecs += convert!("hours", "hnsecs")(hour); - hnsecs += convert!("minutes", "hnsecs")(minute); - hnsecs += convert!("seconds", "hnsecs")(second); - - if (negative) - hnsecs -= convert!("hours", "hnsecs")(24); - - adjTime = daysHNSecs + hnsecs; - } - - @safe unittest - { - import std.range : chain; - - foreach (second; testMinSecs) - { - foreach (st; chain(testSysTimesBC, testSysTimesAD)) - { - auto dt = cast(DateTime) st; - auto expected = SysTime(DateTime(dt.year, dt.month, dt.day, dt.hour, dt.minute, second), - st.fracSecs, - st.timezone); - st.second = second; - assert(st == expected, format("[%s] [%s]", st, expected)); - } - } - - auto st = testSysTimesAD[0]; - assertThrown!DateTimeException(st.second = -1); - assertThrown!DateTimeException(st.second = 60); - - const cst = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); - immutable ist = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); - static assert(!__traits(compiles, cst.seconds = 27)); - static assert(!__traits(compiles, ist.seconds = 27)); - - static void testScope(scope ref SysTime st) @safe - { - st.second = 12; - } - } - - - /++ - Fractional seconds past the second (i.e. the portion of a - $(LREF SysTime) which is less than a second). - +/ - @property Duration fracSecs() @safe const nothrow scope - { - auto hnsecs = removeUnitsFromHNSecs!"days"(adjTime); - - if (hnsecs < 0) - hnsecs += convert!("hours", "hnsecs")(24); - - return dur!"hnsecs"(removeUnitsFromHNSecs!"seconds"(hnsecs)); - } - - /// - @safe unittest - { - import core.time : msecs, usecs, hnsecs, nsecs; - import std.datetime.date : DateTime; - - auto dt = DateTime(1982, 4, 1, 20, 59, 22); - assert(SysTime(dt, msecs(213)).fracSecs == msecs(213)); - assert(SysTime(dt, usecs(5202)).fracSecs == usecs(5202)); - assert(SysTime(dt, hnsecs(1234567)).fracSecs == hnsecs(1234567)); - - // SysTime and Duration both have a precision of hnsecs (100 ns), - // so nsecs are going to be truncated. - assert(SysTime(dt, nsecs(123456789)).fracSecs == nsecs(123456700)); - } - - @safe unittest - { - import std.range : chain; - import core.time; - - assert(SysTime(0, UTC()).fracSecs == Duration.zero); - assert(SysTime(1, UTC()).fracSecs == hnsecs(1)); - assert(SysTime(-1, UTC()).fracSecs == hnsecs(9_999_999)); - - foreach (tz; testTZs) - { - foreach (year; chain(testYearsBC, testYearsAD)) - { - foreach (md; testMonthDays) - { - foreach (hour; testHours) - { - foreach (minute; testMinSecs) - { - foreach (second; testMinSecs) - { - auto dt = DateTime(Date(year, md.month, md.day), TimeOfDay(hour, minute, second)); - foreach (fs; testFracSecs) - assert(SysTime(dt, fs, tz).fracSecs == fs); - } - } - } - } - } - } - - const cst = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); - immutable ist = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); - assert(cst.fracSecs == Duration.zero); - assert(ist.fracSecs == Duration.zero); - - static void testScope(scope ref SysTime st) @safe - { - auto result = st.fracSecs; - } - } - - - /++ - Fractional seconds past the second (i.e. the portion of a - $(LREF SysTime) which is less than a second). - - Params: - fracSecs = The duration to set this $(LREF SysTime)'s fractional - seconds to. - - Throws: - $(REF DateTimeException,std,datetime,date) if the given duration - is negative or if it's greater than or equal to one second. - +/ - @property void fracSecs(Duration fracSecs) @safe scope - { - enforce(fracSecs >= Duration.zero, new DateTimeException("A SysTime cannot have negative fractional seconds.")); - enforce(fracSecs < seconds(1), new DateTimeException("Fractional seconds must be less than one second.")); - - auto oldHNSecs = adjTime; - auto days = splitUnitsFromHNSecs!"days"(oldHNSecs); - immutable daysHNSecs = convert!("days", "hnsecs")(days); - immutable negative = oldHNSecs < 0; - - if (negative) - oldHNSecs += convert!("hours", "hnsecs")(24); - - immutable seconds = splitUnitsFromHNSecs!"seconds"(oldHNSecs); - immutable secondsHNSecs = convert!("seconds", "hnsecs")(seconds); - auto newHNSecs = fracSecs.total!"hnsecs" + secondsHNSecs; - - if (negative) - newHNSecs -= convert!("hours", "hnsecs")(24); - - adjTime = daysHNSecs + newHNSecs; - } - - /// - @safe unittest - { - import core.time : Duration, msecs, hnsecs, nsecs; - import std.datetime.date : DateTime; - - auto st = SysTime(DateTime(1982, 4, 1, 20, 59, 22)); - assert(st.fracSecs == Duration.zero); - - st.fracSecs = msecs(213); - assert(st.fracSecs == msecs(213)); - - st.fracSecs = hnsecs(1234567); - assert(st.fracSecs == hnsecs(1234567)); - - // SysTime has a precision of hnsecs (100 ns), so nsecs are - // going to be truncated. - st.fracSecs = nsecs(123456789); - assert(st.fracSecs == hnsecs(1234567)); - } - - @safe unittest - { - import std.range : chain; - import core.time; - - foreach (fracSec; testFracSecs) - { - foreach (st; chain(testSysTimesBC, testSysTimesAD)) - { - auto dt = cast(DateTime) st; - auto expected = SysTime(dt, fracSec, st.timezone); - st.fracSecs = fracSec; - assert(st == expected, format("[%s] [%s]", st, expected)); - } - } - - auto st = testSysTimesAD[0]; - assertThrown!DateTimeException(st.fracSecs = hnsecs(-1)); - assertThrown!DateTimeException(st.fracSecs = seconds(1)); - - const cst = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); - immutable ist = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); - static assert(!__traits(compiles, cst.fracSecs = msecs(7))); - static assert(!__traits(compiles, ist.fracSecs = msecs(7))); - - static void testScope(scope ref SysTime st) @safe - { - st.fracSecs = Duration.zero; - } - } - - - /++ - The total hnsecs from midnight, January 1st, 1 A.D. UTC. This is the - internal representation of $(LREF SysTime). - +/ - @property long stdTime() @safe const pure nothrow scope @nogc - { - return _stdTime; - } - - @safe unittest - { - import core.time; - assert(SysTime(0).stdTime == 0); - assert(SysTime(1).stdTime == 1); - assert(SysTime(-1).stdTime == -1); - assert(SysTime(DateTime(1, 1, 1, 0, 0, 33), hnsecs(502), UTC()).stdTime == 330_000_502L); - assert(SysTime(DateTime(1970, 1, 1, 0, 0, 0), UTC()).stdTime == 621_355_968_000_000_000L); - - const cst = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); - immutable ist = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); - assert(cst.stdTime > 0); - assert(ist.stdTime > 0); - - static void testScope(scope ref SysTime st) @safe - { - auto result = st.stdTime; - } - } - - - /++ - The total hnsecs from midnight, January 1st, 1 A.D. UTC. This is the - internal representation of $(LREF SysTime). - - Params: - stdTime = The number of hnsecs since January 1st, 1 A.D. UTC. - +/ - @property void stdTime(long stdTime) @safe pure nothrow scope - { - _stdTime = stdTime; - } - - @safe unittest - { - import core.time; - static void test(long stdTime, SysTime expected, size_t line = __LINE__) - { - auto st = SysTime(0, UTC()); - st.stdTime = stdTime; - assert(st == expected); - } - - test(0, SysTime(Date(1, 1, 1), UTC())); - test(1, SysTime(DateTime(1, 1, 1, 0, 0, 0), hnsecs(1), UTC())); - test(-1, SysTime(DateTime(0, 12, 31, 23, 59, 59), hnsecs(9_999_999), UTC())); - test(330_000_502L, SysTime(DateTime(1, 1, 1, 0, 0, 33), hnsecs(502), UTC())); - test(621_355_968_000_000_000L, SysTime(DateTime(1970, 1, 1, 0, 0, 0), UTC())); - - const cst = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); - immutable ist = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); - static assert(!__traits(compiles, cst.stdTime = 27)); - static assert(!__traits(compiles, ist.stdTime = 27)); - - static void testScope(scope ref SysTime st) @safe - { - st.stdTime = 42; - } - } - - - /++ - The current time zone of this $(LREF SysTime). Its internal time is - always kept in UTC, so there are no conversion issues between time zones - due to DST. Functions which return all or part of the time - such as - hours - adjust the time to this $(LREF SysTime)'s time zone before - returning. - +/ - @property immutable(TimeZone) timezone() @safe const pure nothrow return scope - { - return _timezone; - } - - @safe unittest - { - assert(SysTime.init.timezone is InitTimeZone()); - assert(SysTime(DateTime.init, UTC()).timezone is UTC()); - - static void testScope(scope ref SysTime st) @safe - { - auto result = st.timezone; - } - } - - - /++ - The current time zone of this $(LREF SysTime). It's internal time is - always kept in UTC, so there are no conversion issues between time zones - due to DST. Functions which return all or part of the time - such as - hours - adjust the time to this $(LREF SysTime)'s time zone before - returning. - - Params: - timezone = The $(REF _TimeZone,std,datetime,_timezone) to set this - $(LREF SysTime)'s time zone to. - +/ - @property void timezone(immutable TimeZone timezone) @safe pure nothrow scope - { - if (timezone is null) - _timezone = LocalTime(); - else - _timezone = timezone; - } - - @safe unittest - { - SysTime st; - st.timezone = null; - assert(st.timezone is LocalTime()); - st.timezone = UTC(); - assert(st.timezone is UTC()); - - static void testScope(scope ref SysTime st) @safe - { - st.timezone = UTC(); - } - } - - - /++ - Returns whether DST is in effect for this $(LREF SysTime). - +/ - @property bool dstInEffect() @safe const nothrow return scope - { - return _timezone.dstInEffect(_stdTime); - } - - // This function's full unit testing is done in the time zone classes, but - // this verifies that SysTime.init works correctly, since historically, it - // has segfaulted due to a null _timezone. - @safe unittest - { - assert(!SysTime.init.dstInEffect); - - static void testScope(scope ref SysTime st) @safe - { - auto result = st.dstInEffect; - } - } - - - /++ - Returns what the offset from UTC is for this $(LREF SysTime). - It includes the DST offset in effect at that time (if any). - +/ - @property Duration utcOffset() @safe const nothrow return scope - { - return _timezone.utcOffsetAt(_stdTime); - } - - // This function's full unit testing is done in the time zone classes, but - // this verifies that SysTime.init works correctly, since historically, it - // has segfaulted due to a null _timezone. - @safe unittest - { - assert(SysTime.init.utcOffset == Duration.zero); - - static void testScope(scope ref SysTime st) @safe - { - auto result = st.utcOffset; - } - } - - - /++ - Returns a $(LREF SysTime) with the same std time as this one, but with - $(REF LocalTime,std,datetime,timezone) as its time zone. - +/ - SysTime toLocalTime() @safe const pure nothrow scope - { - return SysTime(_stdTime, LocalTime()); - } - - @safe unittest - { - import core.time; - { - auto sysTime = SysTime(DateTime(1982, 1, 4, 8, 59, 7), hnsecs(27)); - assert(sysTime == sysTime.toLocalTime()); - assert(sysTime._stdTime == sysTime.toLocalTime()._stdTime); - assert(sysTime.toLocalTime().timezone is LocalTime()); - assert(sysTime.toLocalTime().timezone is sysTime.timezone); - assert(sysTime.toLocalTime().timezone !is UTC()); - } - - { - auto stz = new immutable SimpleTimeZone(dur!"minutes"(-3 * 60)); - auto sysTime = SysTime(DateTime(1982, 1, 4, 8, 59, 7), hnsecs(27), stz); - assert(sysTime == sysTime.toLocalTime()); - assert(sysTime._stdTime == sysTime.toLocalTime()._stdTime); - assert(sysTime.toLocalTime().timezone is LocalTime()); - assert(sysTime.toLocalTime().timezone !is UTC()); - assert(sysTime.toLocalTime().timezone !is stz); - } - - static void testScope(scope ref SysTime st) @safe - { - auto result = st.toLocalTime(); - } - } - - - /++ - Returns a $(LREF SysTime) with the same std time as this one, but with - `UTC` as its time zone. - +/ - SysTime toUTC() @safe const pure nothrow scope - { - return SysTime(_stdTime, UTC()); - } - - @safe unittest - { - import core.time; - auto sysTime = SysTime(DateTime(1982, 1, 4, 8, 59, 7), hnsecs(27)); - assert(sysTime == sysTime.toUTC()); - assert(sysTime._stdTime == sysTime.toUTC()._stdTime); - assert(sysTime.toUTC().timezone is UTC()); - assert(sysTime.toUTC().timezone !is LocalTime()); - assert(sysTime.toUTC().timezone !is sysTime.timezone); - - static void testScope(scope ref SysTime st) @safe - { - auto result = st.toUTC(); - } - } - - - /++ - Returns a $(LREF SysTime) with the same std time as this one, but with - given time zone as its time zone. - +/ - SysTime toOtherTZ(immutable TimeZone tz) @safe const pure nothrow scope - { - if (tz is null) - return SysTime(_stdTime, LocalTime()); - else - return SysTime(_stdTime, tz); - } - - @safe unittest - { - import core.time; - auto stz = new immutable SimpleTimeZone(dur!"minutes"(11 * 60)); - auto sysTime = SysTime(DateTime(1982, 1, 4, 8, 59, 7), hnsecs(27)); - assert(sysTime == sysTime.toOtherTZ(stz)); - assert(sysTime._stdTime == sysTime.toOtherTZ(stz)._stdTime); - assert(sysTime.toOtherTZ(stz).timezone is stz); - assert(sysTime.toOtherTZ(stz).timezone !is LocalTime()); - assert(sysTime.toOtherTZ(stz).timezone !is UTC()); - assert(sysTime.toOtherTZ(null).timezone is LocalTime()); - - static void testScope(scope ref SysTime st) @safe - { - auto result = st.toOtherTZ(null); - } - } - - - /++ - Converts this $(LREF SysTime) to unix time (i.e. seconds from midnight, - January 1st, 1970 in UTC). - - The C standard does not specify the representation of time_t, so it is - implementation defined. On POSIX systems, unix time is equivalent to - time_t, but that's not necessarily true on other systems (e.g. it is - not true for the Digital Mars C runtime). So, be careful when using unix - time with C functions on non-POSIX systems. - - By default, the return type is time_t (which is normally an alias for - int on 32-bit systems and long on 64-bit systems), but if a different - size is required than either int or long can be passed as a template - argument to get the desired size. - - If the return type is int, and the result can't fit in an int, then the - closest value that can be held in 32 bits will be used (so `int.max` - if it goes over and `int.min` if it goes under). However, no attempt - is made to deal with integer overflow if the return type is long. - - Params: - T = The return type (int or long). It defaults to time_t, which is - normally 32 bits on a 32-bit system and 64 bits on a 64-bit - system. - - Returns: - A signed integer representing the unix time which is equivalent to - this SysTime. - +/ - T toUnixTime(T = time_t)() @safe const pure nothrow scope - if (is(T == int) || is(T == long)) - { - return stdTimeToUnixTime!T(_stdTime); - } - - /// - @safe unittest - { - import core.time : hours; - import std.datetime.date : DateTime; - import std.datetime.timezone : SimpleTimeZone, UTC; - - assert(SysTime(DateTime(1970, 1, 1), UTC()).toUnixTime() == 0); - - auto pst = new immutable SimpleTimeZone(hours(-8)); - assert(SysTime(DateTime(1970, 1, 1), pst).toUnixTime() == 28800); - - auto utc = SysTime(DateTime(2007, 12, 22, 8, 14, 45), UTC()); - assert(utc.toUnixTime() == 1_198_311_285); - - auto ca = SysTime(DateTime(2007, 12, 22, 8, 14, 45), pst); - assert(ca.toUnixTime() == 1_198_340_085); - - static void testScope(scope ref SysTime st) @safe - { - auto result = st.toUnixTime(); - } - } - - @safe unittest - { - import std.meta : AliasSeq; - import core.time; - assert(SysTime(DateTime(1970, 1, 1), UTC()).toUnixTime() == 0); - static foreach (units; ["hnsecs", "usecs", "msecs"]) - assert(SysTime(DateTime(1970, 1, 1, 0, 0, 0), dur!units(1), UTC()).toUnixTime() == 0); - assert(SysTime(DateTime(1970, 1, 1, 0, 0, 1), UTC()).toUnixTime() == 1); - assert(SysTime(DateTime(1969, 12, 31, 23, 59, 59), hnsecs(9_999_999), UTC()).toUnixTime() == 0); - assert(SysTime(DateTime(1969, 12, 31, 23, 59, 59), usecs(999_999), UTC()).toUnixTime() == 0); - assert(SysTime(DateTime(1969, 12, 31, 23, 59, 59), msecs(999), UTC()).toUnixTime() == 0); - assert(SysTime(DateTime(1969, 12, 31, 23, 59, 59), UTC()).toUnixTime() == -1); - } - - - /++ - Converts from unix time (i.e. seconds from midnight, January 1st, 1970 - in UTC) to a $(LREF SysTime). - - The C standard does not specify the representation of time_t, so it is - implementation defined. On POSIX systems, unix time is equivalent to - time_t, but that's not necessarily true on other systems (e.g. it is - not true for the Digital Mars C runtime). So, be careful when using unix - time with C functions on non-POSIX systems. - - Params: - unixTime = Seconds from midnight, January 1st, 1970 in UTC. - tz = The time zone for the SysTime that's returned. - +/ - static SysTime fromUnixTime(long unixTime, immutable TimeZone tz = LocalTime()) @safe pure nothrow - { - return SysTime(unixTimeToStdTime(unixTime), tz); - } - - /// - @safe unittest - { - import core.time : hours; - import std.datetime.date : DateTime; - import std.datetime.timezone : SimpleTimeZone, UTC; - - assert(SysTime.fromUnixTime(0) == - SysTime(DateTime(1970, 1, 1), UTC())); - - auto pst = new immutable SimpleTimeZone(hours(-8)); - assert(SysTime.fromUnixTime(28800) == - SysTime(DateTime(1970, 1, 1), pst)); - - auto st1 = SysTime.fromUnixTime(1_198_311_285, UTC()); - assert(st1 == SysTime(DateTime(2007, 12, 22, 8, 14, 45), UTC())); - assert(st1.timezone is UTC()); - assert(st1 == SysTime(DateTime(2007, 12, 22, 0, 14, 45), pst)); - - auto st2 = SysTime.fromUnixTime(1_198_311_285, pst); - assert(st2 == SysTime(DateTime(2007, 12, 22, 8, 14, 45), UTC())); - assert(st2.timezone is pst); - assert(st2 == SysTime(DateTime(2007, 12, 22, 0, 14, 45), pst)); - } - - @safe unittest - { - import core.time; - assert(SysTime.fromUnixTime(0) == SysTime(DateTime(1970, 1, 1), UTC())); - assert(SysTime.fromUnixTime(1) == SysTime(DateTime(1970, 1, 1, 0, 0, 1), UTC())); - assert(SysTime.fromUnixTime(-1) == SysTime(DateTime(1969, 12, 31, 23, 59, 59), UTC())); - - auto st = SysTime.fromUnixTime(0); - auto dt = cast(DateTime) st; - assert(dt <= DateTime(1970, 2, 1) && dt >= DateTime(1969, 12, 31)); - assert(st.timezone is LocalTime()); - - auto aest = new immutable SimpleTimeZone(hours(10)); - assert(SysTime.fromUnixTime(-36000) == SysTime(DateTime(1970, 1, 1), aest)); - } - - - /++ - Returns a `timeval` which represents this $(LREF SysTime). - - Note that like all conversions in std.datetime, this is a truncating - conversion. - - If `timeval.tv_sec` is int, and the result can't fit in an int, then - the closest value that can be held in 32 bits will be used for - `tv_sec`. (so `int.max` if it goes over and `int.min` if it - goes under). - +/ - timeval toTimeVal() @safe const pure nothrow scope - { - immutable tv_sec = toUnixTime!(typeof(timeval.tv_sec))(); - immutable fracHNSecs = removeUnitsFromHNSecs!"seconds"(_stdTime - 621_355_968_000_000_000L); - immutable tv_usec = cast(typeof(timeval.tv_usec))convert!("hnsecs", "usecs")(fracHNSecs); - return timeval(tv_sec, tv_usec); - } - - @safe unittest - { - import core.time; - assert(SysTime(DateTime(1970, 1, 1), UTC()).toTimeVal() == timeval(0, 0)); - assert(SysTime(DateTime(1970, 1, 1), hnsecs(9), UTC()).toTimeVal() == timeval(0, 0)); - assert(SysTime(DateTime(1970, 1, 1), hnsecs(10), UTC()).toTimeVal() == timeval(0, 1)); - assert(SysTime(DateTime(1970, 1, 1), usecs(7), UTC()).toTimeVal() == timeval(0, 7)); - - assert(SysTime(DateTime(1970, 1, 1, 0, 0, 1), UTC()).toTimeVal() == timeval(1, 0)); - assert(SysTime(DateTime(1970, 1, 1, 0, 0, 1), hnsecs(9), UTC()).toTimeVal() == timeval(1, 0)); - assert(SysTime(DateTime(1970, 1, 1, 0, 0, 1), hnsecs(10), UTC()).toTimeVal() == timeval(1, 1)); - assert(SysTime(DateTime(1970, 1, 1, 0, 0, 1), usecs(7), UTC()).toTimeVal() == timeval(1, 7)); - - assert(SysTime(DateTime(1969, 12, 31, 23, 59, 59), hnsecs(9_999_999), UTC()).toTimeVal() == timeval(0, 0)); - assert(SysTime(DateTime(1969, 12, 31, 23, 59, 59), hnsecs(9_999_990), UTC()).toTimeVal() == timeval(0, -1)); - - assert(SysTime(DateTime(1969, 12, 31, 23, 59, 59), usecs(999_999), UTC()).toTimeVal() == timeval(0, -1)); - assert(SysTime(DateTime(1969, 12, 31, 23, 59, 59), usecs(999), UTC()).toTimeVal() == timeval(0, -999_001)); - assert(SysTime(DateTime(1969, 12, 31, 23, 59, 59), msecs(999), UTC()).toTimeVal() == timeval(0, -1000)); - assert(SysTime(DateTime(1969, 12, 31, 23, 59, 59), UTC()).toTimeVal() == timeval(-1, 0)); - assert(SysTime(DateTime(1969, 12, 31, 23, 59, 58), usecs(17), UTC()).toTimeVal() == timeval(-1, -999_983)); - - static void testScope(scope ref SysTime st) @safe - { - auto result = st.toTimeVal(); - } - } - - - version (StdDdoc) - { - version (Windows) private struct timespec {} - /++ - Returns a `timespec` which represents this $(LREF SysTime). - - $(BLUE This function is Posix-Only.) - +/ - timespec toTimeSpec() @safe const pure nothrow scope; - } - else version (Posix) - { - timespec toTimeSpec() @safe const pure nothrow scope - { - immutable tv_sec = toUnixTime!(typeof(timespec.tv_sec))(); - immutable fracHNSecs = removeUnitsFromHNSecs!"seconds"(_stdTime - 621_355_968_000_000_000L); - immutable tv_nsec = cast(typeof(timespec.tv_nsec))convert!("hnsecs", "nsecs")(fracHNSecs); - return timespec(tv_sec, tv_nsec); - } - - @safe unittest - { - import core.time; - assert(SysTime(DateTime(1970, 1, 1), UTC()).toTimeSpec() == timespec(0, 0)); - assert(SysTime(DateTime(1970, 1, 1), hnsecs(9), UTC()).toTimeSpec() == timespec(0, 900)); - assert(SysTime(DateTime(1970, 1, 1), hnsecs(10), UTC()).toTimeSpec() == timespec(0, 1000)); - assert(SysTime(DateTime(1970, 1, 1), usecs(7), UTC()).toTimeSpec() == timespec(0, 7000)); - - assert(SysTime(DateTime(1970, 1, 1, 0, 0, 1), UTC()).toTimeSpec() == timespec(1, 0)); - assert(SysTime(DateTime(1970, 1, 1, 0, 0, 1), hnsecs(9), UTC()).toTimeSpec() == timespec(1, 900)); - assert(SysTime(DateTime(1970, 1, 1, 0, 0, 1), hnsecs(10), UTC()).toTimeSpec() == timespec(1, 1000)); - assert(SysTime(DateTime(1970, 1, 1, 0, 0, 1), usecs(7), UTC()).toTimeSpec() == timespec(1, 7000)); - - assert(SysTime(DateTime(1969, 12, 31, 23, 59, 59), hnsecs(9_999_999), UTC()).toTimeSpec() == - timespec(0, -100)); - assert(SysTime(DateTime(1969, 12, 31, 23, 59, 59), hnsecs(9_999_990), UTC()).toTimeSpec() == - timespec(0, -1000)); - - assert(SysTime(DateTime(1969, 12, 31, 23, 59, 59), usecs(999_999), UTC()).toTimeSpec() == - timespec(0, -1_000)); - assert(SysTime(DateTime(1969, 12, 31, 23, 59, 59), usecs(999), UTC()).toTimeSpec() == - timespec(0, -999_001_000)); - assert(SysTime(DateTime(1969, 12, 31, 23, 59, 59), msecs(999), UTC()).toTimeSpec() == - timespec(0, -1_000_000)); - assert(SysTime(DateTime(1969, 12, 31, 23, 59, 59), UTC()).toTimeSpec() == - timespec(-1, 0)); - assert(SysTime(DateTime(1969, 12, 31, 23, 59, 58), usecs(17), UTC()).toTimeSpec() == - timespec(-1, -999_983_000)); - - static void testScope(scope ref SysTime st) @safe - { - auto result = st.toTimeSpec(); - } - } - } - - /++ - Returns a `tm` which represents this $(LREF SysTime). - +/ - tm toTM() @safe const nothrow scope - { - auto dateTime = cast(DateTime) this; - tm timeInfo; - - timeInfo.tm_sec = dateTime.second; - timeInfo.tm_min = dateTime.minute; - timeInfo.tm_hour = dateTime.hour; - timeInfo.tm_mday = dateTime.day; - timeInfo.tm_mon = dateTime.month - 1; - timeInfo.tm_year = dateTime.year - 1900; - timeInfo.tm_wday = dateTime.dayOfWeek; - timeInfo.tm_yday = dateTime.dayOfYear - 1; - timeInfo.tm_isdst = _timezone.dstInEffect(_stdTime); - - version (Posix) - { - import std.utf : toUTFz; - timeInfo.tm_gmtoff = cast(int) convert!("hnsecs", "seconds")(adjTime - _stdTime); - auto zone = timeInfo.tm_isdst ? _timezone.dstName : _timezone.stdName; - timeInfo.tm_zone = zone.toUTFz!(char*)(); - } - - return timeInfo; - } - - @system unittest - { - import std.conv : to; - import core.time; - - version (Posix) - { - import std.datetime.timezone : clearTZEnvVar, setTZEnvVar; - setTZEnvVar("America/Los_Angeles"); - scope(exit) clearTZEnvVar(); - } - - { - auto timeInfo = SysTime(DateTime(1970, 1, 1)).toTM(); - - assert(timeInfo.tm_sec == 0); - assert(timeInfo.tm_min == 0); - assert(timeInfo.tm_hour == 0); - assert(timeInfo.tm_mday == 1); - assert(timeInfo.tm_mon == 0); - assert(timeInfo.tm_year == 70); - assert(timeInfo.tm_wday == 4); - assert(timeInfo.tm_yday == 0); - - version (Posix) - assert(timeInfo.tm_isdst == 0); - else version (Windows) - assert(timeInfo.tm_isdst == 0 || timeInfo.tm_isdst == 1); - - version (Posix) - { - assert(timeInfo.tm_gmtoff == -8 * 60 * 60); - assert(to!string(timeInfo.tm_zone) == "PST"); - } - } - - { - auto timeInfo = SysTime(DateTime(2010, 7, 4, 12, 15, 7), hnsecs(15)).toTM(); - - assert(timeInfo.tm_sec == 7); - assert(timeInfo.tm_min == 15); - assert(timeInfo.tm_hour == 12); - assert(timeInfo.tm_mday == 4); - assert(timeInfo.tm_mon == 6); - assert(timeInfo.tm_year == 110); - assert(timeInfo.tm_wday == 0); - assert(timeInfo.tm_yday == 184); - - version (Posix) - assert(timeInfo.tm_isdst == 1); - else version (Windows) - assert(timeInfo.tm_isdst == 0 || timeInfo.tm_isdst == 1); - - version (Posix) - { - assert(timeInfo.tm_gmtoff == -7 * 60 * 60); - assert(to!string(timeInfo.tm_zone) == "PDT"); - } - } - - // This is more to verify that SysTime.init.toTM() doesn't segfault and - // does something sane rather than that the value is anything - // particularly useful. - { - auto timeInfo = SysTime.init.toTM(); - - assert(timeInfo.tm_sec == 0); - assert(timeInfo.tm_min == 0); - assert(timeInfo.tm_hour == 0); - assert(timeInfo.tm_mday == 1); - assert(timeInfo.tm_mon == 0); - assert(timeInfo.tm_year == -1899); - assert(timeInfo.tm_wday == 1); - assert(timeInfo.tm_yday == 0); - assert(timeInfo.tm_isdst == 0); - - version (Posix) - { - assert(timeInfo.tm_gmtoff == 0); - assert(to!string(timeInfo.tm_zone) == "SysTime.init's timezone"); - } - } - - static void testScope(scope ref SysTime st) @safe - { - auto result = st.toTM(); - } - } - - - /++ - Adds the given number of years or months to this $(LREF SysTime). A - negative number will subtract. - - Note that if day overflow is allowed, and the date with the adjusted - year/month overflows the number of days in the new month, then the month - will be incremented by one, and the day set to the number of days - overflowed. (e.g. if the day were 31 and the new month were June, then - the month would be incremented to July, and the new day would be 1). If - day overflow is not allowed, then the day will be set to the last valid - day in the month (e.g. June 31st would become June 30th). - - Params: - units = The type of units to add ("years" or "months"). - value = The number of months or years to add to this - $(LREF SysTime). - allowOverflow = Whether the days should be allowed to overflow, - causing the month to increment. - +/ - ref SysTime add(string units)(long value, AllowDayOverflow allowOverflow = AllowDayOverflow.yes) @safe nothrow scope - if (units == "years" || units == "months") - { - auto hnsecs = adjTime; - auto days = splitUnitsFromHNSecs!"days"(hnsecs) + 1; - - if (hnsecs < 0) - { - hnsecs += convert!("hours", "hnsecs")(24); - --days; - } - - auto date = Date(cast(int) days); - date.add!units(value, allowOverflow); - days = date.dayOfGregorianCal - 1; - - if (days < 0) - { - hnsecs -= convert!("hours", "hnsecs")(24); - ++days; - } - - immutable newDaysHNSecs = convert!("days", "hnsecs")(days); - - adjTime = newDaysHNSecs + hnsecs; - - return this; - } - - @safe unittest - { - auto st1 = SysTime(DateTime(2010, 1, 1, 12, 30, 33)); - st1.add!"months"(11); - assert(st1 == SysTime(DateTime(2010, 12, 1, 12, 30, 33))); - - auto st2 = SysTime(DateTime(2010, 1, 1, 12, 30, 33)); - st2.add!"months"(-11); - assert(st2 == SysTime(DateTime(2009, 2, 1, 12, 30, 33))); - - auto st3 = SysTime(DateTime(2000, 2, 29, 12, 30, 33)); - st3.add!"years"(1); - assert(st3 == SysTime(DateTime(2001, 3, 1, 12, 30, 33))); - - auto st4 = SysTime(DateTime(2000, 2, 29, 12, 30, 33)); - st4.add!"years"(1, AllowDayOverflow.no); - assert(st4 == SysTime(DateTime(2001, 2, 28, 12, 30, 33))); - } - - // Test add!"years"() with AllowDayOverflow.yes - @safe unittest - { - import core.time; - // Test A.D. - { - auto sysTime = SysTime(Date(1999, 7, 6)); - sysTime.add!"years"(7); - assert(sysTime == SysTime(Date(2006, 7, 6))); - sysTime.add!"years"(-9); - assert(sysTime == SysTime(Date(1997, 7, 6))); - } - - { - auto sysTime = SysTime(Date(1999, 2, 28)); - sysTime.add!"years"(1); - assert(sysTime == SysTime(Date(2000, 2, 28))); - } - - { - auto sysTime = SysTime(Date(2000, 2, 29)); - sysTime.add!"years"(-1); - assert(sysTime == SysTime(Date(1999, 3, 1))); - } - - { - auto sysTime = SysTime(DateTime(1999, 7, 6, 12, 7, 3), msecs(234)); - sysTime.add!"years"(7); - assert(sysTime == SysTime(DateTime(2006, 7, 6, 12, 7, 3), msecs(234))); - sysTime.add!"years"(-9); - assert(sysTime == SysTime(DateTime(1997, 7, 6, 12, 7, 3), msecs(234))); - } - - { - auto sysTime = SysTime(DateTime(1999, 2, 28, 0, 7, 2), usecs(1207)); - sysTime.add!"years"(1); - assert(sysTime == SysTime(DateTime(2000, 2, 28, 0, 7, 2), usecs(1207))); - } - - { - auto sysTime = SysTime(DateTime(2000, 2, 29, 0, 7, 2), usecs(1207)); - sysTime.add!"years"(-1); - assert(sysTime == SysTime(DateTime(1999, 3, 1, 0, 7, 2), usecs(1207))); - } - - // Test B.C. - { - auto sysTime = SysTime(Date(-1999, 7, 6)); - sysTime.add!"years"(-7); - assert(sysTime == SysTime(Date(-2006, 7, 6))); - sysTime.add!"years"(9); - assert(sysTime == SysTime(Date(-1997, 7, 6))); - } - - { - auto sysTime = SysTime(Date(-1999, 2, 28)); - sysTime.add!"years"(-1); - assert(sysTime == SysTime(Date(-2000, 2, 28))); - } - - { - auto sysTime = SysTime(Date(-2000, 2, 29)); - sysTime.add!"years"(1); - assert(sysTime == SysTime(Date(-1999, 3, 1))); - } - - { - auto sysTime = SysTime(DateTime(-1999, 7, 6, 12, 7, 3), msecs(234)); - sysTime.add!"years"(-7); - assert(sysTime == SysTime(DateTime(-2006, 7, 6, 12, 7, 3), msecs(234))); - sysTime.add!"years"(9); - assert(sysTime == SysTime(DateTime(-1997, 7, 6, 12, 7, 3), msecs(234))); - } - - { - auto sysTime = SysTime(DateTime(-1999, 2, 28, 3, 3, 3), hnsecs(3)); - sysTime.add!"years"(-1); - assert(sysTime == SysTime(DateTime(-2000, 2, 28, 3, 3, 3), hnsecs(3))); - } - - { - auto sysTime = SysTime(DateTime(-2000, 2, 29, 3, 3, 3), hnsecs(3)); - sysTime.add!"years"(1); - assert(sysTime == SysTime(DateTime(-1999, 3, 1, 3, 3, 3), hnsecs(3))); - } - - // Test Both - { - auto sysTime = SysTime(Date(4, 7, 6)); - sysTime.add!"years"(-5); - assert(sysTime == SysTime(Date(-1, 7, 6))); - sysTime.add!"years"(5); - assert(sysTime == SysTime(Date(4, 7, 6))); - } - - { - auto sysTime = SysTime(Date(-4, 7, 6)); - sysTime.add!"years"(5); - assert(sysTime == SysTime(Date(1, 7, 6))); - sysTime.add!"years"(-5); - assert(sysTime == SysTime(Date(-4, 7, 6))); - } - - { - auto sysTime = SysTime(Date(4, 7, 6)); - sysTime.add!"years"(-8); - assert(sysTime == SysTime(Date(-4, 7, 6))); - sysTime.add!"years"(8); - assert(sysTime == SysTime(Date(4, 7, 6))); - } - - { - auto sysTime = SysTime(Date(-4, 7, 6)); - sysTime.add!"years"(8); - assert(sysTime == SysTime(Date(4, 7, 6))); - sysTime.add!"years"(-8); - assert(sysTime == SysTime(Date(-4, 7, 6))); - } - - { - auto sysTime = SysTime(Date(-4, 2, 29)); - sysTime.add!"years"(5); - assert(sysTime == SysTime(Date(1, 3, 1))); - } - - { - auto sysTime = SysTime(Date(4, 2, 29)); - sysTime.add!"years"(-5); - assert(sysTime == SysTime(Date(-1, 3, 1))); - } - - { - auto sysTime = SysTime(DateTime(1, 1, 1, 0, 0, 0)); - sysTime.add!"years"(-1); - assert(sysTime == SysTime(DateTime(0, 1, 1, 0, 0, 0))); - sysTime.add!"years"(1); - assert(sysTime == SysTime(DateTime(1, 1, 1, 0, 0, 0))); - } - - { - auto sysTime = SysTime(DateTime(1, 1, 1, 23, 59, 59), hnsecs(9_999_999)); - sysTime.add!"years"(-1); - assert(sysTime == SysTime(DateTime(0, 1, 1, 23, 59, 59), hnsecs(9_999_999))); - sysTime.add!"years"(1); - assert(sysTime == SysTime(DateTime(1, 1, 1, 23, 59, 59), hnsecs(9_999_999))); - } - - { - auto sysTime = SysTime(DateTime(0, 1, 1, 0, 0, 0)); - sysTime.add!"years"(1); - assert(sysTime == SysTime(DateTime(1, 1, 1, 0, 0, 0))); - sysTime.add!"years"(-1); - assert(sysTime == SysTime(DateTime(0, 1, 1, 0, 0, 0))); - } - - { - auto sysTime = SysTime(DateTime(0, 1, 1, 23, 59, 59), hnsecs(9_999_999)); - sysTime.add!"years"(1); - assert(sysTime == SysTime(DateTime(1, 1, 1, 23, 59, 59), hnsecs(9_999_999))); - sysTime.add!"years"(-1); - assert(sysTime == SysTime(DateTime(0, 1, 1, 23, 59, 59), hnsecs(9_999_999))); - } - - { - auto sysTime = SysTime(DateTime(4, 7, 6, 14, 7, 1), usecs(54329)); - sysTime.add!"years"(-5); - assert(sysTime == SysTime(DateTime(-1, 7, 6, 14, 7, 1), usecs(54329))); - sysTime.add!"years"(5); - assert(sysTime == SysTime(DateTime(4, 7, 6, 14, 7, 1), usecs(54329))); - } - - { - auto sysTime = SysTime(DateTime(-4, 7, 6, 14, 7, 1), usecs(54329)); - sysTime.add!"years"(5); - assert(sysTime == SysTime(DateTime(1, 7, 6, 14, 7, 1), usecs(54329))); - sysTime.add!"years"(-5); - assert(sysTime == SysTime(DateTime(-4, 7, 6, 14, 7, 1), usecs(54329))); - } - - { - auto sysTime = SysTime(DateTime(-4, 2, 29, 5, 5, 5), msecs(555)); - sysTime.add!"years"(5); - assert(sysTime == SysTime(DateTime(1, 3, 1, 5, 5, 5), msecs(555))); - } - - { - auto sysTime = SysTime(DateTime(4, 2, 29, 5, 5, 5), msecs(555)); - sysTime.add!"years"(-5); - assert(sysTime == SysTime(DateTime(-1, 3, 1, 5, 5, 5), msecs(555))); - } - - { - auto sysTime = SysTime(DateTime(4, 2, 29, 5, 5, 5), msecs(555)); - sysTime.add!"years"(-5).add!"years"(7); - assert(sysTime == SysTime(DateTime(6, 3, 1, 5, 5, 5), msecs(555))); - } - - const cst = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); - immutable ist = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); - static assert(!__traits(compiles, cst.add!"years"(4))); - static assert(!__traits(compiles, ist.add!"years"(4))); - - static void testScope(scope ref SysTime st) @safe - { - auto result = st.add!"years"(42); - } - } - - // Test add!"years"() with AllowDayOverflow.no - @safe unittest - { - import core.time; - // Test A.D. - { - auto sysTime = SysTime(Date(1999, 7, 6)); - sysTime.add!"years"(7, AllowDayOverflow.no); - assert(sysTime == SysTime(Date(2006, 7, 6))); - sysTime.add!"years"(-9, AllowDayOverflow.no); - assert(sysTime == SysTime(Date(1997, 7, 6))); - } - - { - auto sysTime = SysTime(Date(1999, 2, 28)); - sysTime.add!"years"(1, AllowDayOverflow.no); - assert(sysTime == SysTime(Date(2000, 2, 28))); - } - - { - auto sysTime = SysTime(Date(2000, 2, 29)); - sysTime.add!"years"(-1, AllowDayOverflow.no); - assert(sysTime == SysTime(Date(1999, 2, 28))); - } - - { - auto sysTime = SysTime(DateTime(1999, 7, 6, 12, 7, 3), msecs(234)); - sysTime.add!"years"(7, AllowDayOverflow.no); - assert(sysTime == SysTime(DateTime(2006, 7, 6, 12, 7, 3), msecs(234))); - sysTime.add!"years"(-9, AllowDayOverflow.no); - assert(sysTime == SysTime(DateTime(1997, 7, 6, 12, 7, 3), msecs(234))); - } - - { - auto sysTime = SysTime(DateTime(1999, 2, 28, 0, 7, 2), usecs(1207)); - sysTime.add!"years"(1, AllowDayOverflow.no); - assert(sysTime == SysTime(DateTime(2000, 2, 28, 0, 7, 2), usecs(1207))); - } - - { - auto sysTime = SysTime(DateTime(2000, 2, 29, 0, 7, 2), usecs(1207)); - sysTime.add!"years"(-1, AllowDayOverflow.no); - assert(sysTime == SysTime(DateTime(1999, 2, 28, 0, 7, 2), usecs(1207))); - } - - // Test B.C. - { - auto sysTime = SysTime(Date(-1999, 7, 6)); - sysTime.add!"years"(-7, AllowDayOverflow.no); - assert(sysTime == SysTime(Date(-2006, 7, 6))); - sysTime.add!"years"(9, AllowDayOverflow.no); - assert(sysTime == SysTime(Date(-1997, 7, 6))); - } - - { - auto sysTime = SysTime(Date(-1999, 2, 28)); - sysTime.add!"years"(-1, AllowDayOverflow.no); - assert(sysTime == SysTime(Date(-2000, 2, 28))); - } - - { - auto sysTime = SysTime(Date(-2000, 2, 29)); - sysTime.add!"years"(1, AllowDayOverflow.no); - assert(sysTime == SysTime(Date(-1999, 2, 28))); - } - - { - auto sysTime = SysTime(DateTime(-1999, 7, 6, 12, 7, 3), msecs(234)); - sysTime.add!"years"(-7, AllowDayOverflow.no); - assert(sysTime == SysTime(DateTime(-2006, 7, 6, 12, 7, 3), msecs(234))); - sysTime.add!"years"(9, AllowDayOverflow.no); - assert(sysTime == SysTime(DateTime(-1997, 7, 6, 12, 7, 3), msecs(234))); - } - - { - auto sysTime = SysTime(DateTime(-1999, 2, 28, 3, 3, 3), hnsecs(3)); - sysTime.add!"years"(-1, AllowDayOverflow.no); - assert(sysTime == SysTime(DateTime(-2000, 2, 28, 3, 3, 3), hnsecs(3))); - } - - { - auto sysTime = SysTime(DateTime(-2000, 2, 29, 3, 3, 3), hnsecs(3)); - sysTime.add!"years"(1, AllowDayOverflow.no); - assert(sysTime == SysTime(DateTime(-1999, 2, 28, 3, 3, 3), hnsecs(3))); - } - - // Test Both - { - auto sysTime = SysTime(Date(4, 7, 6)); - sysTime.add!"years"(-5, AllowDayOverflow.no); - assert(sysTime == SysTime(Date(-1, 7, 6))); - sysTime.add!"years"(5, AllowDayOverflow.no); - assert(sysTime == SysTime(Date(4, 7, 6))); - } - - { - auto sysTime = SysTime(Date(-4, 7, 6)); - sysTime.add!"years"(5, AllowDayOverflow.no); - assert(sysTime == SysTime(Date(1, 7, 6))); - sysTime.add!"years"(-5, AllowDayOverflow.no); - assert(sysTime == SysTime(Date(-4, 7, 6))); - } - - { - auto sysTime = SysTime(Date(4, 7, 6)); - sysTime.add!"years"(-8, AllowDayOverflow.no); - assert(sysTime == SysTime(Date(-4, 7, 6))); - sysTime.add!"years"(8, AllowDayOverflow.no); - assert(sysTime == SysTime(Date(4, 7, 6))); - } - - { - auto sysTime = SysTime(Date(-4, 7, 6)); - sysTime.add!"years"(8, AllowDayOverflow.no); - assert(sysTime == SysTime(Date(4, 7, 6))); - sysTime.add!"years"(-8, AllowDayOverflow.no); - assert(sysTime == SysTime(Date(-4, 7, 6))); - } - - { - auto sysTime = SysTime(Date(-4, 2, 29)); - sysTime.add!"years"(5, AllowDayOverflow.no); - assert(sysTime == SysTime(Date(1, 2, 28))); - } - - { - auto sysTime = SysTime(Date(4, 2, 29)); - sysTime.add!"years"(-5, AllowDayOverflow.no); - assert(sysTime == SysTime(Date(-1, 2, 28))); - } - - { - auto sysTime = SysTime(DateTime(1, 1, 1, 0, 0, 0)); - sysTime.add!"years"(-1, AllowDayOverflow.no); - assert(sysTime == SysTime(DateTime(0, 1, 1, 0, 0, 0))); - sysTime.add!"years"(1, AllowDayOverflow.no); - assert(sysTime == SysTime(DateTime(1, 1, 1, 0, 0, 0))); - } - - { - auto sysTime = SysTime(DateTime(1, 1, 1, 23, 59, 59), hnsecs(9_999_999)); - sysTime.add!"years"(-1, AllowDayOverflow.no); - assert(sysTime == SysTime(DateTime(0, 1, 1, 23, 59, 59), hnsecs(9_999_999))); - sysTime.add!"years"(1, AllowDayOverflow.no); - assert(sysTime == SysTime(DateTime(1, 1, 1, 23, 59, 59), hnsecs(9_999_999))); - } - - { - auto sysTime = SysTime(DateTime(0, 1, 1, 0, 0, 0)); - sysTime.add!"years"(1, AllowDayOverflow.no); - assert(sysTime == SysTime(DateTime(1, 1, 1, 0, 0, 0))); - sysTime.add!"years"(-1, AllowDayOverflow.no); - assert(sysTime == SysTime(DateTime(0, 1, 1, 0, 0, 0))); - } - - { - auto sysTime = SysTime(DateTime(0, 1, 1, 23, 59, 59), hnsecs(9_999_999)); - sysTime.add!"years"(1, AllowDayOverflow.no); - assert(sysTime == SysTime(DateTime(1, 1, 1, 23, 59, 59), hnsecs(9_999_999))); - sysTime.add!"years"(-1, AllowDayOverflow.no); - assert(sysTime == SysTime(DateTime(0, 1, 1, 23, 59, 59), hnsecs(9_999_999))); - } - - { - auto sysTime = SysTime(DateTime(4, 7, 6, 14, 7, 1), usecs(54329)); - sysTime.add!"years"(-5); - assert(sysTime == SysTime(DateTime(-1, 7, 6, 14, 7, 1), usecs(54329))); - sysTime.add!"years"(5); - assert(sysTime == SysTime(DateTime(4, 7, 6, 14, 7, 1), usecs(54329))); - } - - { - auto sysTime = SysTime(DateTime(4, 7, 6, 14, 7, 1), usecs(54329)); - sysTime.add!"years"(-5, AllowDayOverflow.no); - assert(sysTime == SysTime(DateTime(-1, 7, 6, 14, 7, 1), usecs(54329))); - sysTime.add!"years"(5, AllowDayOverflow.no); - assert(sysTime == SysTime(DateTime(4, 7, 6, 14, 7, 1), usecs(54329))); - } - - { - auto sysTime = SysTime(DateTime(-4, 7, 6, 14, 7, 1), usecs(54329)); - sysTime.add!"years"(5, AllowDayOverflow.no); - assert(sysTime == SysTime(DateTime(1, 7, 6, 14, 7, 1), usecs(54329))); - sysTime.add!"years"(-5, AllowDayOverflow.no); - assert(sysTime == SysTime(DateTime(-4, 7, 6, 14, 7, 1), usecs(54329))); - } - - { - auto sysTime = SysTime(DateTime(-4, 2, 29, 5, 5, 5), msecs(555)); - sysTime.add!"years"(5, AllowDayOverflow.no); - assert(sysTime == SysTime(DateTime(1, 2, 28, 5, 5, 5), msecs(555))); - } - - { - auto sysTime = SysTime(DateTime(4, 2, 29, 5, 5, 5), msecs(555)); - sysTime.add!"years"(-5, AllowDayOverflow.no); - assert(sysTime == SysTime(DateTime(-1, 2, 28, 5, 5, 5), msecs(555))); - } - - { - auto sysTime = SysTime(DateTime(4, 2, 29, 5, 5, 5), msecs(555)); - sysTime.add!"years"(-5, AllowDayOverflow.no).add!"years"(7, AllowDayOverflow.no); - assert(sysTime == SysTime(DateTime(6, 2, 28, 5, 5, 5), msecs(555))); - } - } - - // Test add!"months"() with AllowDayOverflow.yes - @safe unittest - { - import core.time; - // Test A.D. - { - auto sysTime = SysTime(Date(1999, 7, 6)); - sysTime.add!"months"(3); - assert(sysTime == SysTime(Date(1999, 10, 6))); - sysTime.add!"months"(-4); - assert(sysTime == SysTime(Date(1999, 6, 6))); - } - - { - auto sysTime = SysTime(Date(1999, 7, 6)); - sysTime.add!"months"(6); - assert(sysTime == SysTime(Date(2000, 1, 6))); - sysTime.add!"months"(-6); - assert(sysTime == SysTime(Date(1999, 7, 6))); - } - - { - auto sysTime = SysTime(Date(1999, 7, 6)); - sysTime.add!"months"(27); - assert(sysTime == SysTime(Date(2001, 10, 6))); - sysTime.add!"months"(-28); - assert(sysTime == SysTime(Date(1999, 6, 6))); - } - - { - auto sysTime = SysTime(Date(1999, 5, 31)); - sysTime.add!"months"(1); - assert(sysTime == SysTime(Date(1999, 7, 1))); - } - - { - auto sysTime = SysTime(Date(1999, 5, 31)); - sysTime.add!"months"(-1); - assert(sysTime == SysTime(Date(1999, 5, 1))); - } - - { - auto sysTime = SysTime(Date(1999, 2, 28)); - sysTime.add!"months"(12); - assert(sysTime == SysTime(Date(2000, 2, 28))); - } - - { - auto sysTime = SysTime(Date(2000, 2, 29)); - sysTime.add!"months"(12); - assert(sysTime == SysTime(Date(2001, 3, 1))); - } - - { - auto sysTime = SysTime(Date(1999, 7, 31)); - sysTime.add!"months"(1); - assert(sysTime == SysTime(Date(1999, 8, 31))); - sysTime.add!"months"(1); - assert(sysTime == SysTime(Date(1999, 10, 1))); - } - - { - auto sysTime = SysTime(Date(1998, 8, 31)); - sysTime.add!"months"(13); - assert(sysTime == SysTime(Date(1999, 10, 1))); - sysTime.add!"months"(-13); - assert(sysTime == SysTime(Date(1998, 9, 1))); - } - - { - auto sysTime = SysTime(Date(1997, 12, 31)); - sysTime.add!"months"(13); - assert(sysTime == SysTime(Date(1999, 1, 31))); - sysTime.add!"months"(-13); - assert(sysTime == SysTime(Date(1997, 12, 31))); - } - - { - auto sysTime = SysTime(Date(1997, 12, 31)); - sysTime.add!"months"(14); - assert(sysTime == SysTime(Date(1999, 3, 3))); - sysTime.add!"months"(-14); - assert(sysTime == SysTime(Date(1998, 1, 3))); - } - - { - auto sysTime = SysTime(Date(1998, 12, 31)); - sysTime.add!"months"(14); - assert(sysTime == SysTime(Date(2000, 3, 2))); - sysTime.add!"months"(-14); - assert(sysTime == SysTime(Date(1999, 1, 2))); - } - - { - auto sysTime = SysTime(Date(1999, 12, 31)); - sysTime.add!"months"(14); - assert(sysTime == SysTime(Date(2001, 3, 3))); - sysTime.add!"months"(-14); - assert(sysTime == SysTime(Date(2000, 1, 3))); - } - - { - auto sysTime = SysTime(DateTime(1999, 7, 6, 12, 2, 7), usecs(5007)); - sysTime.add!"months"(3); - assert(sysTime == SysTime(DateTime(1999, 10, 6, 12, 2, 7), usecs(5007))); - sysTime.add!"months"(-4); - assert(sysTime == SysTime(DateTime(1999, 6, 6, 12, 2, 7), usecs(5007))); - } - - { - auto sysTime = SysTime(DateTime(1998, 12, 31, 7, 7, 7), hnsecs(422202)); - sysTime.add!"months"(14); - assert(sysTime == SysTime(DateTime(2000, 3, 2, 7, 7, 7), hnsecs(422202))); - sysTime.add!"months"(-14); - assert(sysTime == SysTime(DateTime(1999, 1, 2, 7, 7, 7), hnsecs(422202))); - } - - { - auto sysTime = SysTime(DateTime(1999, 12, 31, 7, 7, 7), hnsecs(422202)); - sysTime.add!"months"(14); - assert(sysTime == SysTime(DateTime(2001, 3, 3, 7, 7, 7), hnsecs(422202))); - sysTime.add!"months"(-14); - assert(sysTime == SysTime(DateTime(2000, 1, 3, 7, 7, 7), hnsecs(422202))); - } - - // Test B.C. - { - auto sysTime = SysTime(Date(-1999, 7, 6)); - sysTime.add!"months"(3); - assert(sysTime == SysTime(Date(-1999, 10, 6))); - sysTime.add!"months"(-4); - assert(sysTime == SysTime(Date(-1999, 6, 6))); - } - - { - auto sysTime = SysTime(Date(-1999, 7, 6)); - sysTime.add!"months"(6); - assert(sysTime == SysTime(Date(-1998, 1, 6))); - sysTime.add!"months"(-6); - assert(sysTime == SysTime(Date(-1999, 7, 6))); - } - - { - auto sysTime = SysTime(Date(-1999, 7, 6)); - sysTime.add!"months"(-27); - assert(sysTime == SysTime(Date(-2001, 4, 6))); - sysTime.add!"months"(28); - assert(sysTime == SysTime(Date(-1999, 8, 6))); - } - - { - auto sysTime = SysTime(Date(-1999, 5, 31)); - sysTime.add!"months"(1); - assert(sysTime == SysTime(Date(-1999, 7, 1))); - } - - { - auto sysTime = SysTime(Date(-1999, 5, 31)); - sysTime.add!"months"(-1); - assert(sysTime == SysTime(Date(-1999, 5, 1))); - } - - { - auto sysTime = SysTime(Date(-1999, 2, 28)); - sysTime.add!"months"(-12); - assert(sysTime == SysTime(Date(-2000, 2, 28))); - } - - { - auto sysTime = SysTime(Date(-2000, 2, 29)); - sysTime.add!"months"(-12); - assert(sysTime == SysTime(Date(-2001, 3, 1))); - } - - { - auto sysTime = SysTime(Date(-1999, 7, 31)); - sysTime.add!"months"(1); - assert(sysTime == SysTime(Date(-1999, 8, 31))); - sysTime.add!"months"(1); - assert(sysTime == SysTime(Date(-1999, 10, 1))); - } - - { - auto sysTime = SysTime(Date(-1998, 8, 31)); - sysTime.add!"months"(13); - assert(sysTime == SysTime(Date(-1997, 10, 1))); - sysTime.add!"months"(-13); - assert(sysTime == SysTime(Date(-1998, 9, 1))); - } - - { - auto sysTime = SysTime(Date(-1997, 12, 31)); - sysTime.add!"months"(13); - assert(sysTime == SysTime(Date(-1995, 1, 31))); - sysTime.add!"months"(-13); - assert(sysTime == SysTime(Date(-1997, 12, 31))); - } - - { - auto sysTime = SysTime(Date(-1997, 12, 31)); - sysTime.add!"months"(14); - assert(sysTime == SysTime(Date(-1995, 3, 3))); - sysTime.add!"months"(-14); - assert(sysTime == SysTime(Date(-1996, 1, 3))); - } - - { - auto sysTime = SysTime(Date(-2002, 12, 31)); - sysTime.add!"months"(14); - assert(sysTime == SysTime(Date(-2000, 3, 2))); - sysTime.add!"months"(-14); - assert(sysTime == SysTime(Date(-2001, 1, 2))); - } - - { - auto sysTime = SysTime(Date(-2001, 12, 31)); - sysTime.add!"months"(14); - assert(sysTime == SysTime(Date(-1999, 3, 3))); - sysTime.add!"months"(-14); - assert(sysTime == SysTime(Date(-2000, 1, 3))); - } - - { - auto sysTime = SysTime(DateTime(-1999, 7, 6, 12, 2, 7), usecs(5007)); - sysTime.add!"months"(3); - assert(sysTime == SysTime(DateTime(-1999, 10, 6, 12, 2, 7), usecs(5007))); - sysTime.add!"months"(-4); - assert(sysTime == SysTime(DateTime(-1999, 6, 6, 12, 2, 7), usecs(5007))); - } - - { - auto sysTime = SysTime(DateTime(-2002, 12, 31, 7, 7, 7), hnsecs(422202)); - sysTime.add!"months"(14); - assert(sysTime == SysTime(DateTime(-2000, 3, 2, 7, 7, 7), hnsecs(422202))); - sysTime.add!"months"(-14); - assert(sysTime == SysTime(DateTime(-2001, 1, 2, 7, 7, 7), hnsecs(422202))); - } - - { - auto sysTime = SysTime(DateTime(-2001, 12, 31, 7, 7, 7), hnsecs(422202)); - sysTime.add!"months"(14); - assert(sysTime == SysTime(DateTime(-1999, 3, 3, 7, 7, 7), hnsecs(422202))); - sysTime.add!"months"(-14); - assert(sysTime == SysTime(DateTime(-2000, 1, 3, 7, 7, 7), hnsecs(422202))); - } - - // Test Both - { - auto sysTime = SysTime(Date(1, 1, 1)); - sysTime.add!"months"(-1); - assert(sysTime == SysTime(Date(0, 12, 1))); - sysTime.add!"months"(1); - assert(sysTime == SysTime(Date(1, 1, 1))); - } - - { - auto sysTime = SysTime(Date(4, 1, 1)); - sysTime.add!"months"(-48); - assert(sysTime == SysTime(Date(0, 1, 1))); - sysTime.add!"months"(48); - assert(sysTime == SysTime(Date(4, 1, 1))); - } - - { - auto sysTime = SysTime(Date(4, 3, 31)); - sysTime.add!"months"(-49); - assert(sysTime == SysTime(Date(0, 3, 2))); - sysTime.add!"months"(49); - assert(sysTime == SysTime(Date(4, 4, 2))); - } - - { - auto sysTime = SysTime(Date(4, 3, 31)); - sysTime.add!"months"(-85); - assert(sysTime == SysTime(Date(-3, 3, 3))); - sysTime.add!"months"(85); - assert(sysTime == SysTime(Date(4, 4, 3))); - } - - { - auto sysTime = SysTime(DateTime(1, 1, 1, 0, 0, 0)); - sysTime.add!"months"(-1); - assert(sysTime == SysTime(DateTime(0, 12, 1, 0, 0, 0))); - sysTime.add!"months"(1); - assert(sysTime == SysTime(DateTime(1, 1, 1, 0, 0, 0))); - } - - { - auto sysTime = SysTime(DateTime(1, 1, 1, 23, 59, 59), hnsecs(9_999_999)); - sysTime.add!"months"(-1); - assert(sysTime == SysTime(DateTime(0, 12, 1, 23, 59, 59), hnsecs(9_999_999))); - sysTime.add!"months"(1); - assert(sysTime == SysTime(DateTime(1, 1, 1, 23, 59, 59), hnsecs(9_999_999))); - } - - { - auto sysTime = SysTime(DateTime(0, 12, 1, 0, 0, 0)); - sysTime.add!"months"(1); - assert(sysTime == SysTime(DateTime(1, 1, 1, 0, 0, 0))); - sysTime.add!"months"(-1); - assert(sysTime == SysTime(DateTime(0, 12, 1, 0, 0, 0))); - } - - { - auto sysTime = SysTime(DateTime(0, 12, 1, 23, 59, 59), hnsecs(9_999_999)); - sysTime.add!"months"(1); - assert(sysTime == SysTime(DateTime(1, 1, 1, 23, 59, 59), hnsecs(9_999_999))); - sysTime.add!"months"(-1); - assert(sysTime == SysTime(DateTime(0, 12, 1, 23, 59, 59), hnsecs(9_999_999))); - } - - { - auto sysTime = SysTime(DateTime(1, 1, 1, 0, 7, 9), hnsecs(17)); - sysTime.add!"months"(-1); - assert(sysTime == SysTime(DateTime(0, 12, 1, 0, 7, 9), hnsecs(17))); - sysTime.add!"months"(1); - assert(sysTime == SysTime(DateTime(1, 1, 1, 0, 7, 9), hnsecs(17))); - } - - { - auto sysTime = SysTime(DateTime(4, 3, 31, 12, 11, 10), msecs(9)); - sysTime.add!"months"(-85); - assert(sysTime == SysTime(DateTime(-3, 3, 3, 12, 11, 10), msecs(9))); - sysTime.add!"months"(85); - assert(sysTime == SysTime(DateTime(4, 4, 3, 12, 11, 10), msecs(9))); - } - - { - auto sysTime = SysTime(DateTime(-3, 3, 31, 12, 11, 10), msecs(9)); - sysTime.add!"months"(85); - assert(sysTime == SysTime(DateTime(4, 5, 1, 12, 11, 10), msecs(9))); - sysTime.add!"months"(-85); - assert(sysTime == SysTime(DateTime(-3, 4, 1, 12, 11, 10), msecs(9))); - } - - { - auto sysTime = SysTime(DateTime(-3, 3, 31, 12, 11, 10), msecs(9)); - sysTime.add!"months"(85).add!"months"(-83); - assert(sysTime == SysTime(DateTime(-3, 6, 1, 12, 11, 10), msecs(9))); - } - - const cst = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); - immutable ist = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); - static assert(!__traits(compiles, cst.add!"months"(4))); - static assert(!__traits(compiles, ist.add!"months"(4))); - - static void testScope(scope ref SysTime st) @safe - { - auto result = st.add!"months"(42); - } - } - - // Test add!"months"() with AllowDayOverflow.no - @safe unittest - { - import core.time; - // Test A.D. - { - auto sysTime = SysTime(Date(1999, 7, 6)); - sysTime.add!"months"(3, AllowDayOverflow.no); - assert(sysTime == SysTime(Date(1999, 10, 6))); - sysTime.add!"months"(-4, AllowDayOverflow.no); - assert(sysTime == SysTime(Date(1999, 6, 6))); - } - - { - auto sysTime = SysTime(Date(1999, 7, 6)); - sysTime.add!"months"(6, AllowDayOverflow.no); - assert(sysTime == SysTime(Date(2000, 1, 6))); - sysTime.add!"months"(-6, AllowDayOverflow.no); - assert(sysTime == SysTime(Date(1999, 7, 6))); - } - - { - auto sysTime = SysTime(Date(1999, 7, 6)); - sysTime.add!"months"(27, AllowDayOverflow.no); - assert(sysTime == SysTime(Date(2001, 10, 6))); - sysTime.add!"months"(-28, AllowDayOverflow.no); - assert(sysTime == SysTime(Date(1999, 6, 6))); - } - - { - auto sysTime = SysTime(Date(1999, 5, 31)); - sysTime.add!"months"(1, AllowDayOverflow.no); - assert(sysTime == SysTime(Date(1999, 6, 30))); - } - - { - auto sysTime = SysTime(Date(1999, 5, 31)); - sysTime.add!"months"(-1, AllowDayOverflow.no); - assert(sysTime == SysTime(Date(1999, 4, 30))); - } - - { - auto sysTime = SysTime(Date(1999, 2, 28)); - sysTime.add!"months"(12, AllowDayOverflow.no); - assert(sysTime == SysTime(Date(2000, 2, 28))); - } - - { - auto sysTime = SysTime(Date(2000, 2, 29)); - sysTime.add!"months"(12, AllowDayOverflow.no); - assert(sysTime == SysTime(Date(2001, 2, 28))); - } - - { - auto sysTime = SysTime(Date(1999, 7, 31)); - sysTime.add!"months"(1, AllowDayOverflow.no); - assert(sysTime == SysTime(Date(1999, 8, 31))); - sysTime.add!"months"(1, AllowDayOverflow.no); - assert(sysTime == SysTime(Date(1999, 9, 30))); - } - - { - auto sysTime = SysTime(Date(1998, 8, 31)); - sysTime.add!"months"(13, AllowDayOverflow.no); - assert(sysTime == SysTime(Date(1999, 9, 30))); - sysTime.add!"months"(-13, AllowDayOverflow.no); - assert(sysTime == SysTime(Date(1998, 8, 30))); - } - - { - auto sysTime = SysTime(Date(1997, 12, 31)); - sysTime.add!"months"(13, AllowDayOverflow.no); - assert(sysTime == SysTime(Date(1999, 1, 31))); - sysTime.add!"months"(-13, AllowDayOverflow.no); - assert(sysTime == SysTime(Date(1997, 12, 31))); - } - - { - auto sysTime = SysTime(Date(1997, 12, 31)); - sysTime.add!"months"(14, AllowDayOverflow.no); - assert(sysTime == SysTime(Date(1999, 2, 28))); - sysTime.add!"months"(-14, AllowDayOverflow.no); - assert(sysTime == SysTime(Date(1997, 12, 28))); - } - - { - auto sysTime = SysTime(Date(1998, 12, 31)); - sysTime.add!"months"(14, AllowDayOverflow.no); - assert(sysTime == SysTime(Date(2000, 2, 29))); - sysTime.add!"months"(-14, AllowDayOverflow.no); - assert(sysTime == SysTime(Date(1998, 12, 29))); - } - - { - auto sysTime = SysTime(Date(1999, 12, 31)); - sysTime.add!"months"(14, AllowDayOverflow.no); - assert(sysTime == SysTime(Date(2001, 2, 28))); - sysTime.add!"months"(-14, AllowDayOverflow.no); - assert(sysTime == SysTime(Date(1999, 12, 28))); - } - - { - auto sysTime = SysTime(DateTime(1999, 7, 6, 12, 2, 7), usecs(5007)); - sysTime.add!"months"(3, AllowDayOverflow.no); - assert(sysTime == SysTime(DateTime(1999, 10, 6, 12, 2, 7), usecs(5007))); - sysTime.add!"months"(-4, AllowDayOverflow.no); - assert(sysTime == SysTime(DateTime(1999, 6, 6, 12, 2, 7), usecs(5007))); - } - - { - auto sysTime = SysTime(DateTime(1998, 12, 31, 7, 7, 7), hnsecs(422202)); - sysTime.add!"months"(14, AllowDayOverflow.no); - assert(sysTime == SysTime(DateTime(2000, 2, 29, 7, 7, 7), hnsecs(422202))); - sysTime.add!"months"(-14, AllowDayOverflow.no); - assert(sysTime == SysTime(DateTime(1998, 12, 29, 7, 7, 7), hnsecs(422202))); - } - - { - auto sysTime = SysTime(DateTime(1999, 12, 31, 7, 7, 7), hnsecs(422202)); - sysTime.add!"months"(14, AllowDayOverflow.no); - assert(sysTime == SysTime(DateTime(2001, 2, 28, 7, 7, 7), hnsecs(422202))); - sysTime.add!"months"(-14, AllowDayOverflow.no); - assert(sysTime == SysTime(DateTime(1999, 12, 28, 7, 7, 7), hnsecs(422202))); - } - - // Test B.C. - { - auto sysTime = SysTime(Date(-1999, 7, 6)); - sysTime.add!"months"(3, AllowDayOverflow.no); - assert(sysTime == SysTime(Date(-1999, 10, 6))); - sysTime.add!"months"(-4, AllowDayOverflow.no); - assert(sysTime == SysTime(Date(-1999, 6, 6))); - } - - { - auto sysTime = SysTime(Date(-1999, 7, 6)); - sysTime.add!"months"(6, AllowDayOverflow.no); - assert(sysTime == SysTime(Date(-1998, 1, 6))); - sysTime.add!"months"(-6, AllowDayOverflow.no); - assert(sysTime == SysTime(Date(-1999, 7, 6))); - } - - { - auto sysTime = SysTime(Date(-1999, 7, 6)); - sysTime.add!"months"(-27, AllowDayOverflow.no); - assert(sysTime == SysTime(Date(-2001, 4, 6))); - sysTime.add!"months"(28, AllowDayOverflow.no); - assert(sysTime == SysTime(Date(-1999, 8, 6))); - } - - { - auto sysTime = SysTime(Date(-1999, 5, 31)); - sysTime.add!"months"(1, AllowDayOverflow.no); - assert(sysTime == SysTime(Date(-1999, 6, 30))); - } - - { - auto sysTime = SysTime(Date(-1999, 5, 31)); - sysTime.add!"months"(-1, AllowDayOverflow.no); - assert(sysTime == SysTime(Date(-1999, 4, 30))); - } - - { - auto sysTime = SysTime(Date(-1999, 2, 28)); - sysTime.add!"months"(-12, AllowDayOverflow.no); - assert(sysTime == SysTime(Date(-2000, 2, 28))); - } - - { - auto sysTime = SysTime(Date(-2000, 2, 29)); - sysTime.add!"months"(-12, AllowDayOverflow.no); - assert(sysTime == SysTime(Date(-2001, 2, 28))); - } - - { - auto sysTime = SysTime(Date(-1999, 7, 31)); - sysTime.add!"months"(1, AllowDayOverflow.no); - assert(sysTime == SysTime(Date(-1999, 8, 31))); - sysTime.add!"months"(1, AllowDayOverflow.no); - assert(sysTime == SysTime(Date(-1999, 9, 30))); - } - - { - auto sysTime = SysTime(Date(-1998, 8, 31)); - sysTime.add!"months"(13, AllowDayOverflow.no); - assert(sysTime == SysTime(Date(-1997, 9, 30))); - sysTime.add!"months"(-13, AllowDayOverflow.no); - assert(sysTime == SysTime(Date(-1998, 8, 30))); - } - - { - auto sysTime = SysTime(Date(-1997, 12, 31)); - sysTime.add!"months"(13, AllowDayOverflow.no); - assert(sysTime == SysTime(Date(-1995, 1, 31))); - sysTime.add!"months"(-13, AllowDayOverflow.no); - assert(sysTime == SysTime(Date(-1997, 12, 31))); - } - - { - auto sysTime = SysTime(Date(-1997, 12, 31)); - sysTime.add!"months"(14, AllowDayOverflow.no); - assert(sysTime == SysTime(Date(-1995, 2, 28))); - sysTime.add!"months"(-14, AllowDayOverflow.no); - assert(sysTime == SysTime(Date(-1997, 12, 28))); - } - - { - auto sysTime = SysTime(Date(-2002, 12, 31)); - sysTime.add!"months"(14, AllowDayOverflow.no); - assert(sysTime == SysTime(Date(-2000, 2, 29))); - sysTime.add!"months"(-14, AllowDayOverflow.no); - assert(sysTime == SysTime(Date(-2002, 12, 29))); - } - - { - auto sysTime = SysTime(Date(-2001, 12, 31)); - sysTime.add!"months"(14, AllowDayOverflow.no); - assert(sysTime == SysTime(Date(-1999, 2, 28))); - sysTime.add!"months"(-14, AllowDayOverflow.no); - assert(sysTime == SysTime(Date(-2001, 12, 28))); - } - - { - auto sysTime = SysTime(DateTime(-1999, 7, 6, 12, 2, 7), usecs(5007)); - sysTime.add!"months"(3, AllowDayOverflow.no); - assert(sysTime == SysTime(DateTime(-1999, 10, 6, 12, 2, 7), usecs(5007))); - sysTime.add!"months"(-4, AllowDayOverflow.no); - assert(sysTime == SysTime(DateTime(-1999, 6, 6, 12, 2, 7), usecs(5007))); - } - - { - auto sysTime = SysTime(DateTime(-2002, 12, 31, 7, 7, 7), hnsecs(422202)); - sysTime.add!"months"(14, AllowDayOverflow.no); - assert(sysTime == SysTime(DateTime(-2000, 2, 29, 7, 7, 7), hnsecs(422202))); - sysTime.add!"months"(-14, AllowDayOverflow.no); - assert(sysTime == SysTime(DateTime(-2002, 12, 29, 7, 7, 7), hnsecs(422202))); - } - - { - auto sysTime = SysTime(DateTime(-2001, 12, 31, 7, 7, 7), hnsecs(422202)); - sysTime.add!"months"(14, AllowDayOverflow.no); - assert(sysTime == SysTime(DateTime(-1999, 2, 28, 7, 7, 7), hnsecs(422202))); - sysTime.add!"months"(-14, AllowDayOverflow.no); - assert(sysTime == SysTime(DateTime(-2001, 12, 28, 7, 7, 7), hnsecs(422202))); - } - - // Test Both - { - auto sysTime = SysTime(Date(1, 1, 1)); - sysTime.add!"months"(-1, AllowDayOverflow.no); - assert(sysTime == SysTime(Date(0, 12, 1))); - sysTime.add!"months"(1, AllowDayOverflow.no); - assert(sysTime == SysTime(Date(1, 1, 1))); - } - - { - auto sysTime = SysTime(Date(4, 1, 1)); - sysTime.add!"months"(-48, AllowDayOverflow.no); - assert(sysTime == SysTime(Date(0, 1, 1))); - sysTime.add!"months"(48, AllowDayOverflow.no); - assert(sysTime == SysTime(Date(4, 1, 1))); - } - - { - auto sysTime = SysTime(Date(4, 3, 31)); - sysTime.add!"months"(-49, AllowDayOverflow.no); - assert(sysTime == SysTime(Date(0, 2, 29))); - sysTime.add!"months"(49, AllowDayOverflow.no); - assert(sysTime == SysTime(Date(4, 3, 29))); - } - - { - auto sysTime = SysTime(Date(4, 3, 31)); - sysTime.add!"months"(-85, AllowDayOverflow.no); - assert(sysTime == SysTime(Date(-3, 2, 28))); - sysTime.add!"months"(85, AllowDayOverflow.no); - assert(sysTime == SysTime(Date(4, 3, 28))); - } - - { - auto sysTime = SysTime(DateTime(1, 1, 1, 0, 0, 0)); - sysTime.add!"months"(-1, AllowDayOverflow.no); - assert(sysTime == SysTime(DateTime(0, 12, 1, 0, 0, 0))); - sysTime.add!"months"(1, AllowDayOverflow.no); - assert(sysTime == SysTime(DateTime(1, 1, 1, 0, 0, 0))); - } - - { - auto sysTime = SysTime(DateTime(1, 1, 1, 23, 59, 59), hnsecs(9_999_999)); - sysTime.add!"months"(-1, AllowDayOverflow.no); - assert(sysTime == SysTime(DateTime(0, 12, 1, 23, 59, 59), hnsecs(9_999_999))); - sysTime.add!"months"(1, AllowDayOverflow.no); - assert(sysTime == SysTime(DateTime(1, 1, 1, 23, 59, 59), hnsecs(9_999_999))); - } - - { - auto sysTime = SysTime(DateTime(0, 12, 1, 0, 0, 0)); - sysTime.add!"months"(1, AllowDayOverflow.no); - assert(sysTime == SysTime(DateTime(1, 1, 1, 0, 0, 0))); - sysTime.add!"months"(-1, AllowDayOverflow.no); - assert(sysTime == SysTime(DateTime(0, 12, 1, 0, 0, 0))); - } - - { - auto sysTime = SysTime(DateTime(0, 12, 1, 23, 59, 59), hnsecs(9_999_999)); - sysTime.add!"months"(1, AllowDayOverflow.no); - assert(sysTime == SysTime(DateTime(1, 1, 1, 23, 59, 59), hnsecs(9_999_999))); - sysTime.add!"months"(-1, AllowDayOverflow.no); - assert(sysTime == SysTime(DateTime(0, 12, 1, 23, 59, 59), hnsecs(9_999_999))); - } - - { - auto sysTime = SysTime(DateTime(1, 1, 1, 0, 7, 9), hnsecs(17)); - sysTime.add!"months"(-1, AllowDayOverflow.no); - assert(sysTime == SysTime(DateTime(0, 12, 1, 0, 7, 9), hnsecs(17))); - sysTime.add!"months"(1, AllowDayOverflow.no); - assert(sysTime == SysTime(DateTime(1, 1, 1, 0, 7, 9), hnsecs(17))); - } - - { - auto sysTime = SysTime(DateTime(4, 3, 31, 12, 11, 10), msecs(9)); - sysTime.add!"months"(-85, AllowDayOverflow.no); - assert(sysTime == SysTime(DateTime(-3, 2, 28, 12, 11, 10), msecs(9))); - sysTime.add!"months"(85, AllowDayOverflow.no); - assert(sysTime == SysTime(DateTime(4, 3, 28, 12, 11, 10), msecs(9))); - } - - { - auto sysTime = SysTime(DateTime(-3, 3, 31, 12, 11, 10), msecs(9)); - sysTime.add!"months"(85, AllowDayOverflow.no); - assert(sysTime == SysTime(DateTime(4, 4, 30, 12, 11, 10), msecs(9))); - sysTime.add!"months"(-85, AllowDayOverflow.no); - assert(sysTime == SysTime(DateTime(-3, 3, 30, 12, 11, 10), msecs(9))); - } - - { - auto sysTime = SysTime(DateTime(-3, 3, 31, 12, 11, 10), msecs(9)); - sysTime.add!"months"(85, AllowDayOverflow.no).add!"months"(-83, AllowDayOverflow.no); - assert(sysTime == SysTime(DateTime(-3, 5, 30, 12, 11, 10), msecs(9))); - } - } - - - /++ - Adds the given number of years or months to this $(LREF SysTime). A - negative number will subtract. - - The difference between rolling and adding is that rolling does not - affect larger units. Rolling a $(LREF SysTime) 12 months - gets the exact same $(LREF SysTime). However, the days can still be - affected due to the differing number of days in each month. - - Because there are no units larger than years, there is no difference - between adding and rolling years. - - Params: - units = The type of units to add ("years" or "months"). - value = The number of months or years to add to this - $(LREF SysTime). - allowOverflow = Whether the days should be allowed to overflow, - causing the month to increment. - +/ - ref SysTime roll(string units) - (long value, AllowDayOverflow allowOverflow = AllowDayOverflow.yes) @safe nothrow scope - if (units == "years") - { - return add!"years"(value, allowOverflow); - } - - /// - @safe unittest - { - import std.datetime.date : AllowDayOverflow, DateTime; - - auto st1 = SysTime(DateTime(2010, 1, 1, 12, 33, 33)); - st1.roll!"months"(1); - assert(st1 == SysTime(DateTime(2010, 2, 1, 12, 33, 33))); - - auto st2 = SysTime(DateTime(2010, 1, 1, 12, 33, 33)); - st2.roll!"months"(-1); - assert(st2 == SysTime(DateTime(2010, 12, 1, 12, 33, 33))); - - auto st3 = SysTime(DateTime(1999, 1, 29, 12, 33, 33)); - st3.roll!"months"(1); - assert(st3 == SysTime(DateTime(1999, 3, 1, 12, 33, 33))); - - auto st4 = SysTime(DateTime(1999, 1, 29, 12, 33, 33)); - st4.roll!"months"(1, AllowDayOverflow.no); - assert(st4 == SysTime(DateTime(1999, 2, 28, 12, 33, 33))); - - auto st5 = SysTime(DateTime(2000, 2, 29, 12, 30, 33)); - st5.roll!"years"(1); - assert(st5 == SysTime(DateTime(2001, 3, 1, 12, 30, 33))); - - auto st6 = SysTime(DateTime(2000, 2, 29, 12, 30, 33)); - st6.roll!"years"(1, AllowDayOverflow.no); - assert(st6 == SysTime(DateTime(2001, 2, 28, 12, 30, 33))); - } - - @safe unittest - { - auto st = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); - const cst = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); - immutable ist = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); - st.roll!"years"(4); - static assert(!__traits(compiles, cst.roll!"years"(4))); - static assert(!__traits(compiles, ist.roll!"years"(4))); - - static void testScope(scope ref SysTime st) @safe - { - auto result = st.roll!"years"(42); - } - } - - - // Shares documentation with "years" overload. - ref SysTime roll(string units) - (long value, AllowDayOverflow allowOverflow = AllowDayOverflow.yes) @safe nothrow scope - if (units == "months") - { - auto hnsecs = adjTime; - auto days = splitUnitsFromHNSecs!"days"(hnsecs) + 1; - - if (hnsecs < 0) - { - hnsecs += convert!("hours", "hnsecs")(24); - --days; - } - - auto date = Date(cast(int) days); - date.roll!"months"(value, allowOverflow); - days = date.dayOfGregorianCal - 1; - - if (days < 0) - { - hnsecs -= convert!("hours", "hnsecs")(24); - ++days; - } - - immutable newDaysHNSecs = convert!("days", "hnsecs")(days); - adjTime = newDaysHNSecs + hnsecs; - return this; - } - - // Test roll!"months"() with AllowDayOverflow.yes - @safe unittest - { - import core.time; - // Test A.D. - { - auto sysTime = SysTime(Date(1999, 7, 6)); - sysTime.roll!"months"(3); - assert(sysTime == SysTime(Date(1999, 10, 6))); - sysTime.roll!"months"(-4); - assert(sysTime == SysTime(Date(1999, 6, 6))); - } - - { - auto sysTime = SysTime(Date(1999, 7, 6)); - sysTime.roll!"months"(6); - assert(sysTime == SysTime(Date(1999, 1, 6))); - sysTime.roll!"months"(-6); - assert(sysTime == SysTime(Date(1999, 7, 6))); - } - - { - auto sysTime = SysTime(Date(1999, 7, 6)); - sysTime.roll!"months"(27); - assert(sysTime == SysTime(Date(1999, 10, 6))); - sysTime.roll!"months"(-28); - assert(sysTime == SysTime(Date(1999, 6, 6))); - } - - { - auto sysTime = SysTime(Date(1999, 5, 31)); - sysTime.roll!"months"(1); - assert(sysTime == SysTime(Date(1999, 7, 1))); - } - - { - auto sysTime = SysTime(Date(1999, 5, 31)); - sysTime.roll!"months"(-1); - assert(sysTime == SysTime(Date(1999, 5, 1))); - } - - { - auto sysTime = SysTime(Date(1999, 2, 28)); - sysTime.roll!"months"(12); - assert(sysTime == SysTime(Date(1999, 2, 28))); - } - - { - auto sysTime = SysTime(Date(2000, 2, 29)); - sysTime.roll!"months"(12); - assert(sysTime == SysTime(Date(2000, 2, 29))); - } - - { - auto sysTime = SysTime(Date(1999, 7, 31)); - sysTime.roll!"months"(1); - assert(sysTime == SysTime(Date(1999, 8, 31))); - sysTime.roll!"months"(1); - assert(sysTime == SysTime(Date(1999, 10, 1))); - } - - { - auto sysTime = SysTime(Date(1998, 8, 31)); - sysTime.roll!"months"(13); - assert(sysTime == SysTime(Date(1998, 10, 1))); - sysTime.roll!"months"(-13); - assert(sysTime == SysTime(Date(1998, 9, 1))); - } - - { - auto sysTime = SysTime(Date(1997, 12, 31)); - sysTime.roll!"months"(13); - assert(sysTime == SysTime(Date(1997, 1, 31))); - sysTime.roll!"months"(-13); - assert(sysTime == SysTime(Date(1997, 12, 31))); - } - - { - auto sysTime = SysTime(Date(1997, 12, 31)); - sysTime.roll!"months"(14); - assert(sysTime == SysTime(Date(1997, 3, 3))); - sysTime.roll!"months"(-14); - assert(sysTime == SysTime(Date(1997, 1, 3))); - } - - { - auto sysTime = SysTime(Date(1998, 12, 31)); - sysTime.roll!"months"(14); - assert(sysTime == SysTime(Date(1998, 3, 3))); - sysTime.roll!"months"(-14); - assert(sysTime == SysTime(Date(1998, 1, 3))); - } - - { - auto sysTime = SysTime(Date(1999, 12, 31)); - sysTime.roll!"months"(14); - assert(sysTime == SysTime(Date(1999, 3, 3))); - sysTime.roll!"months"(-14); - assert(sysTime == SysTime(Date(1999, 1, 3))); - } - - { - auto sysTime = SysTime(DateTime(1999, 7, 6, 12, 2, 7), usecs(5007)); - sysTime.roll!"months"(3); - assert(sysTime == SysTime(DateTime(1999, 10, 6, 12, 2, 7), usecs(5007))); - sysTime.roll!"months"(-4); - assert(sysTime == SysTime(DateTime(1999, 6, 6, 12, 2, 7), usecs(5007))); - } - - { - auto sysTime = SysTime(DateTime(1998, 12, 31, 7, 7, 7), hnsecs(422202)); - sysTime.roll!"months"(14); - assert(sysTime == SysTime(DateTime(1998, 3, 3, 7, 7, 7), hnsecs(422202))); - sysTime.roll!"months"(-14); - assert(sysTime == SysTime(DateTime(1998, 1, 3, 7, 7, 7), hnsecs(422202))); - } - - { - auto sysTime = SysTime(DateTime(1999, 12, 31, 7, 7, 7), hnsecs(422202)); - sysTime.roll!"months"(14); - assert(sysTime == SysTime(DateTime(1999, 3, 3, 7, 7, 7), hnsecs(422202))); - sysTime.roll!"months"(-14); - assert(sysTime == SysTime(DateTime(1999, 1, 3, 7, 7, 7), hnsecs(422202))); - } - - // Test B.C. - { - auto sysTime = SysTime(Date(-1999, 7, 6)); - sysTime.roll!"months"(3); - assert(sysTime == SysTime(Date(-1999, 10, 6))); - sysTime.roll!"months"(-4); - assert(sysTime == SysTime(Date(-1999, 6, 6))); - } - - { - auto sysTime = SysTime(Date(-1999, 7, 6)); - sysTime.roll!"months"(6); - assert(sysTime == SysTime(Date(-1999, 1, 6))); - sysTime.roll!"months"(-6); - assert(sysTime == SysTime(Date(-1999, 7, 6))); - } - - { - auto sysTime = SysTime(Date(-1999, 7, 6)); - sysTime.roll!"months"(-27); - assert(sysTime == SysTime(Date(-1999, 4, 6))); - sysTime.roll!"months"(28); - assert(sysTime == SysTime(Date(-1999, 8, 6))); - } - - { - auto sysTime = SysTime(Date(-1999, 5, 31)); - sysTime.roll!"months"(1); - assert(sysTime == SysTime(Date(-1999, 7, 1))); - } - - { - auto sysTime = SysTime(Date(-1999, 5, 31)); - sysTime.roll!"months"(-1); - assert(sysTime == SysTime(Date(-1999, 5, 1))); - } - - { - auto sysTime = SysTime(Date(-1999, 2, 28)); - sysTime.roll!"months"(-12); - assert(sysTime == SysTime(Date(-1999, 2, 28))); - } - - { - auto sysTime = SysTime(Date(-2000, 2, 29)); - sysTime.roll!"months"(-12); - assert(sysTime == SysTime(Date(-2000, 2, 29))); - } - - { - auto sysTime = SysTime(Date(-1999, 7, 31)); - sysTime.roll!"months"(1); - assert(sysTime == SysTime(Date(-1999, 8, 31))); - sysTime.roll!"months"(1); - assert(sysTime == SysTime(Date(-1999, 10, 1))); - } - - { - auto sysTime = SysTime(Date(-1998, 8, 31)); - sysTime.roll!"months"(13); - assert(sysTime == SysTime(Date(-1998, 10, 1))); - sysTime.roll!"months"(-13); - assert(sysTime == SysTime(Date(-1998, 9, 1))); - } - - { - auto sysTime = SysTime(Date(-1997, 12, 31)); - sysTime.roll!"months"(13); - assert(sysTime == SysTime(Date(-1997, 1, 31))); - sysTime.roll!"months"(-13); - assert(sysTime == SysTime(Date(-1997, 12, 31))); - } - - { - auto sysTime = SysTime(Date(-1997, 12, 31)); - sysTime.roll!"months"(14); - assert(sysTime == SysTime(Date(-1997, 3, 3))); - sysTime.roll!"months"(-14); - assert(sysTime == SysTime(Date(-1997, 1, 3))); - } - - { - auto sysTime = SysTime(Date(-2002, 12, 31)); - sysTime.roll!"months"(14); - assert(sysTime == SysTime(Date(-2002, 3, 3))); - sysTime.roll!"months"(-14); - assert(sysTime == SysTime(Date(-2002, 1, 3))); - } - - { - auto sysTime = SysTime(Date(-2001, 12, 31)); - sysTime.roll!"months"(14); - assert(sysTime == SysTime(Date(-2001, 3, 3))); - sysTime.roll!"months"(-14); - assert(sysTime == SysTime(Date(-2001, 1, 3))); - } - - { - auto sysTime = SysTime(DateTime(1, 1, 1, 0, 0, 0)); - sysTime.roll!"months"(-1); - assert(sysTime == SysTime(DateTime(1, 12, 1, 0, 0, 0))); - sysTime.roll!"months"(1); - assert(sysTime == SysTime(DateTime(1, 1, 1, 0, 0, 0))); - } - - { - auto sysTime = SysTime(DateTime(1, 1, 1, 23, 59, 59), hnsecs(9_999_999)); - sysTime.roll!"months"(-1); - assert(sysTime == SysTime(DateTime(1, 12, 1, 23, 59, 59), hnsecs(9_999_999))); - sysTime.roll!"months"(1); - assert(sysTime == SysTime(DateTime(1, 1, 1, 23, 59, 59), hnsecs(9_999_999))); - } - - { - auto sysTime = SysTime(DateTime(0, 12, 1, 0, 0, 0)); - sysTime.roll!"months"(1); - assert(sysTime == SysTime(DateTime(0, 1, 1, 0, 0, 0))); - sysTime.roll!"months"(-1); - assert(sysTime == SysTime(DateTime(0, 12, 1, 0, 0, 0))); - } - - { - auto sysTime = SysTime(DateTime(0, 12, 1, 23, 59, 59), hnsecs(9_999_999)); - sysTime.roll!"months"(1); - assert(sysTime == SysTime(DateTime(0, 1, 1, 23, 59, 59), hnsecs(9_999_999))); - sysTime.roll!"months"(-1); - assert(sysTime == SysTime(DateTime(0, 12, 1, 23, 59, 59), hnsecs(9_999_999))); - } - - { - auto sysTime = SysTime(DateTime(-1999, 7, 6, 12, 2, 7), hnsecs(5007)); - sysTime.roll!"months"(3); - assert(sysTime == SysTime(DateTime(-1999, 10, 6, 12, 2, 7), hnsecs(5007))); - sysTime.roll!"months"(-4); - assert(sysTime == SysTime(DateTime(-1999, 6, 6, 12, 2, 7), hnsecs(5007))); - } - - { - auto sysTime = SysTime(DateTime(-2002, 12, 31, 7, 7, 7), hnsecs(422202)); - sysTime.roll!"months"(14); - assert(sysTime == SysTime(DateTime(-2002, 3, 3, 7, 7, 7), hnsecs(422202))); - sysTime.roll!"months"(-14); - assert(sysTime == SysTime(DateTime(-2002, 1, 3, 7, 7, 7), hnsecs(422202))); - } - - { - auto sysTime = SysTime(DateTime(-2001, 12, 31, 7, 7, 7), hnsecs(422202)); - sysTime.roll!"months"(14); - assert(sysTime == SysTime(DateTime(-2001, 3, 3, 7, 7, 7), hnsecs(422202))); - sysTime.roll!"months"(-14); - assert(sysTime == SysTime(DateTime(-2001, 1, 3, 7, 7, 7), hnsecs(422202))); - } - - // Test Both - { - auto sysTime = SysTime(Date(1, 1, 1)); - sysTime.roll!"months"(-1); - assert(sysTime == SysTime(Date(1, 12, 1))); - sysTime.roll!"months"(1); - assert(sysTime == SysTime(Date(1, 1, 1))); - } - - { - auto sysTime = SysTime(Date(4, 1, 1)); - sysTime.roll!"months"(-48); - assert(sysTime == SysTime(Date(4, 1, 1))); - sysTime.roll!"months"(48); - assert(sysTime == SysTime(Date(4, 1, 1))); - } - - { - auto sysTime = SysTime(Date(4, 3, 31)); - sysTime.roll!"months"(-49); - assert(sysTime == SysTime(Date(4, 3, 2))); - sysTime.roll!"months"(49); - assert(sysTime == SysTime(Date(4, 4, 2))); - } - - { - auto sysTime = SysTime(Date(4, 3, 31)); - sysTime.roll!"months"(-85); - assert(sysTime == SysTime(Date(4, 3, 2))); - sysTime.roll!"months"(85); - assert(sysTime == SysTime(Date(4, 4, 2))); - } - - { - auto sysTime = SysTime(Date(-1, 1, 1)); - sysTime.roll!"months"(-1); - assert(sysTime == SysTime(Date(-1, 12, 1))); - sysTime.roll!"months"(1); - assert(sysTime == SysTime(Date(-1, 1, 1))); - } - - { - auto sysTime = SysTime(Date(-4, 1, 1)); - sysTime.roll!"months"(-48); - assert(sysTime == SysTime(Date(-4, 1, 1))); - sysTime.roll!"months"(48); - assert(sysTime == SysTime(Date(-4, 1, 1))); - } - - { - auto sysTime = SysTime(Date(-4, 3, 31)); - sysTime.roll!"months"(-49); - assert(sysTime == SysTime(Date(-4, 3, 2))); - sysTime.roll!"months"(49); - assert(sysTime == SysTime(Date(-4, 4, 2))); - } - - { - auto sysTime = SysTime(Date(-4, 3, 31)); - sysTime.roll!"months"(-85); - assert(sysTime == SysTime(Date(-4, 3, 2))); - sysTime.roll!"months"(85); - assert(sysTime == SysTime(Date(-4, 4, 2))); - } - - { - auto sysTime = SysTime(DateTime(1, 1, 1, 0, 7, 9), hnsecs(17)); - sysTime.roll!"months"(-1); - assert(sysTime == SysTime(DateTime(1, 12, 1, 0, 7, 9), hnsecs(17))); - sysTime.roll!"months"(1); - assert(sysTime == SysTime(DateTime(1, 1, 1, 0, 7, 9), hnsecs(17))); - } - - { - auto sysTime = SysTime(DateTime(4, 3, 31, 12, 11, 10), msecs(9)); - sysTime.roll!"months"(-85); - assert(sysTime == SysTime(DateTime(4, 3, 2, 12, 11, 10), msecs(9))); - sysTime.roll!"months"(85); - assert(sysTime == SysTime(DateTime(4, 4, 2, 12, 11, 10), msecs(9))); - } - - { - auto sysTime = SysTime(DateTime(-3, 3, 31, 12, 11, 10), msecs(9)); - sysTime.roll!"months"(85); - assert(sysTime == SysTime(DateTime(-3, 5, 1, 12, 11, 10), msecs(9))); - sysTime.roll!"months"(-85); - assert(sysTime == SysTime(DateTime(-3, 4, 1, 12, 11, 10), msecs(9))); - } - - { - auto sysTime = SysTime(DateTime(-3, 3, 31, 12, 11, 10), msecs(9)); - sysTime.roll!"months"(85).roll!"months"(-83); - assert(sysTime == SysTime(DateTime(-3, 6, 1, 12, 11, 10), msecs(9))); - } - - const cst = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); - immutable ist = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); - static assert(!__traits(compiles, cst.roll!"months"(4))); - static assert(!__traits(compiles, ist.roll!"months"(4))); - - static void testScope(scope ref SysTime st) @safe - { - auto result = st.roll!"months"(42); - } - } - - // Test roll!"months"() with AllowDayOverflow.no - @safe unittest - { - import core.time; - // Test A.D. - { - auto sysTime = SysTime(Date(1999, 7, 6)); - sysTime.roll!"months"(3, AllowDayOverflow.no); - assert(sysTime == SysTime(Date(1999, 10, 6))); - sysTime.roll!"months"(-4, AllowDayOverflow.no); - assert(sysTime == SysTime(Date(1999, 6, 6))); - } - - { - auto sysTime = SysTime(Date(1999, 7, 6)); - sysTime.roll!"months"(6, AllowDayOverflow.no); - assert(sysTime == SysTime(Date(1999, 1, 6))); - sysTime.roll!"months"(-6, AllowDayOverflow.no); - assert(sysTime == SysTime(Date(1999, 7, 6))); - } - - { - auto sysTime = SysTime(Date(1999, 7, 6)); - sysTime.roll!"months"(27, AllowDayOverflow.no); - assert(sysTime == SysTime(Date(1999, 10, 6))); - sysTime.roll!"months"(-28, AllowDayOverflow.no); - assert(sysTime == SysTime(Date(1999, 6, 6))); - } - - { - auto sysTime = SysTime(Date(1999, 5, 31)); - sysTime.roll!"months"(1, AllowDayOverflow.no); - assert(sysTime == SysTime(Date(1999, 6, 30))); - } - - { - auto sysTime = SysTime(Date(1999, 5, 31)); - sysTime.roll!"months"(-1, AllowDayOverflow.no); - assert(sysTime == SysTime(Date(1999, 4, 30))); - } - - { - auto sysTime = SysTime(Date(1999, 2, 28)); - sysTime.roll!"months"(12, AllowDayOverflow.no); - assert(sysTime == SysTime(Date(1999, 2, 28))); - } - - { - auto sysTime = SysTime(Date(2000, 2, 29)); - sysTime.roll!"months"(12, AllowDayOverflow.no); - assert(sysTime == SysTime(Date(2000, 2, 29))); - } - - { - auto sysTime = SysTime(Date(1999, 7, 31)); - sysTime.roll!"months"(1, AllowDayOverflow.no); - assert(sysTime == SysTime(Date(1999, 8, 31))); - sysTime.roll!"months"(1, AllowDayOverflow.no); - assert(sysTime == SysTime(Date(1999, 9, 30))); - } - - { - auto sysTime = SysTime(Date(1998, 8, 31)); - sysTime.roll!"months"(13, AllowDayOverflow.no); - assert(sysTime == SysTime(Date(1998, 9, 30))); - sysTime.roll!"months"(-13, AllowDayOverflow.no); - assert(sysTime == SysTime(Date(1998, 8, 30))); - } - - { - auto sysTime = SysTime(Date(1997, 12, 31)); - sysTime.roll!"months"(13, AllowDayOverflow.no); - assert(sysTime == SysTime(Date(1997, 1, 31))); - sysTime.roll!"months"(-13, AllowDayOverflow.no); - assert(sysTime == SysTime(Date(1997, 12, 31))); - } - - { - auto sysTime = SysTime(Date(1997, 12, 31)); - sysTime.roll!"months"(14, AllowDayOverflow.no); - assert(sysTime == SysTime(Date(1997, 2, 28))); - sysTime.roll!"months"(-14, AllowDayOverflow.no); - assert(sysTime == SysTime(Date(1997, 12, 28))); - } - - { - auto sysTime = SysTime(Date(1998, 12, 31)); - sysTime.roll!"months"(14, AllowDayOverflow.no); - assert(sysTime == SysTime(Date(1998, 2, 28))); - sysTime.roll!"months"(-14, AllowDayOverflow.no); - assert(sysTime == SysTime(Date(1998, 12, 28))); - } - - { - auto sysTime = SysTime(Date(1999, 12, 31)); - sysTime.roll!"months"(14, AllowDayOverflow.no); - assert(sysTime == SysTime(Date(1999, 2, 28))); - sysTime.roll!"months"(-14, AllowDayOverflow.no); - assert(sysTime == SysTime(Date(1999, 12, 28))); - } - - { - auto sysTime = SysTime(DateTime(1999, 7, 6, 12, 2, 7), usecs(5007)); - sysTime.roll!"months"(3, AllowDayOverflow.no); - assert(sysTime == SysTime(DateTime(1999, 10, 6, 12, 2, 7), usecs(5007))); - sysTime.roll!"months"(-4, AllowDayOverflow.no); - assert(sysTime == SysTime(DateTime(1999, 6, 6, 12, 2, 7), usecs(5007))); - } - - { - auto sysTime = SysTime(DateTime(1998, 12, 31, 7, 7, 7), hnsecs(422202)); - sysTime.roll!"months"(14, AllowDayOverflow.no); - assert(sysTime == SysTime(DateTime(1998, 2, 28, 7, 7, 7), hnsecs(422202))); - sysTime.roll!"months"(-14, AllowDayOverflow.no); - assert(sysTime == SysTime(DateTime(1998, 12, 28, 7, 7, 7), hnsecs(422202))); - } - - { - auto sysTime = SysTime(DateTime(1999, 12, 31, 7, 7, 7), hnsecs(422202)); - sysTime.roll!"months"(14, AllowDayOverflow.no); - assert(sysTime == SysTime(DateTime(1999, 2, 28, 7, 7, 7), hnsecs(422202))); - sysTime.roll!"months"(-14, AllowDayOverflow.no); - assert(sysTime == SysTime(DateTime(1999, 12, 28, 7, 7, 7), hnsecs(422202))); - } - - // Test B.C. - { - auto sysTime = SysTime(Date(-1999, 7, 6)); - sysTime.roll!"months"(3, AllowDayOverflow.no); - assert(sysTime == SysTime(Date(-1999, 10, 6))); - sysTime.roll!"months"(-4, AllowDayOverflow.no); - assert(sysTime == SysTime(Date(-1999, 6, 6))); - } - - { - auto sysTime = SysTime(Date(-1999, 7, 6)); - sysTime.roll!"months"(6, AllowDayOverflow.no); - assert(sysTime == SysTime(Date(-1999, 1, 6))); - sysTime.roll!"months"(-6, AllowDayOverflow.no); - assert(sysTime == SysTime(Date(-1999, 7, 6))); - } - - { - auto sysTime = SysTime(Date(-1999, 7, 6)); - sysTime.roll!"months"(-27, AllowDayOverflow.no); - assert(sysTime == SysTime(Date(-1999, 4, 6))); - sysTime.roll!"months"(28, AllowDayOverflow.no); - assert(sysTime == SysTime(Date(-1999, 8, 6))); - } - - { - auto sysTime = SysTime(Date(-1999, 5, 31)); - sysTime.roll!"months"(1, AllowDayOverflow.no); - assert(sysTime == SysTime(Date(-1999, 6, 30))); - } - - { - auto sysTime = SysTime(Date(-1999, 5, 31)); - sysTime.roll!"months"(-1, AllowDayOverflow.no); - assert(sysTime == SysTime(Date(-1999, 4, 30))); - } - - { - auto sysTime = SysTime(Date(-1999, 2, 28)); - sysTime.roll!"months"(-12, AllowDayOverflow.no); - assert(sysTime == SysTime(Date(-1999, 2, 28))); - } - - { - auto sysTime = SysTime(Date(-2000, 2, 29)); - sysTime.roll!"months"(-12, AllowDayOverflow.no); - assert(sysTime == SysTime(Date(-2000, 2, 29))); - } - - { - auto sysTime = SysTime(Date(-1999, 7, 31)); - sysTime.roll!"months"(1, AllowDayOverflow.no); - assert(sysTime == SysTime(Date(-1999, 8, 31))); - sysTime.roll!"months"(1, AllowDayOverflow.no); - assert(sysTime == SysTime(Date(-1999, 9, 30))); - } - - { - auto sysTime = SysTime(Date(-1998, 8, 31)); - sysTime.roll!"months"(13, AllowDayOverflow.no); - assert(sysTime == SysTime(Date(-1998, 9, 30))); - sysTime.roll!"months"(-13, AllowDayOverflow.no); - assert(sysTime == SysTime(Date(-1998, 8, 30))); - } - - { - auto sysTime = SysTime(Date(-1997, 12, 31)); - sysTime.roll!"months"(13, AllowDayOverflow.no); - assert(sysTime == SysTime(Date(-1997, 1, 31))); - sysTime.roll!"months"(-13, AllowDayOverflow.no); - assert(sysTime == SysTime(Date(-1997, 12, 31))); - } - - { - auto sysTime = SysTime(Date(-1997, 12, 31)); - sysTime.roll!"months"(14, AllowDayOverflow.no); - assert(sysTime == SysTime(Date(-1997, 2, 28))); - sysTime.roll!"months"(-14, AllowDayOverflow.no); - assert(sysTime == SysTime(Date(-1997, 12, 28))); - } - - { - auto sysTime = SysTime(Date(-2002, 12, 31)); - sysTime.roll!"months"(14, AllowDayOverflow.no); - assert(sysTime == SysTime(Date(-2002, 2, 28))); - sysTime.roll!"months"(-14, AllowDayOverflow.no); - assert(sysTime == SysTime(Date(-2002, 12, 28))); - } - - { - auto sysTime = SysTime(Date(-2001, 12, 31)); - sysTime.roll!"months"(14, AllowDayOverflow.no); - assert(sysTime == SysTime(Date(-2001, 2, 28))); - sysTime.roll!"months"(-14, AllowDayOverflow.no); - assert(sysTime == SysTime(Date(-2001, 12, 28))); - } - - { - auto sysTime = SysTime(DateTime(-1999, 7, 6, 12, 2, 7), usecs(5007)); - sysTime.roll!"months"(3, AllowDayOverflow.no); - assert(sysTime == SysTime(DateTime(-1999, 10, 6, 12, 2, 7), usecs(5007))); - sysTime.roll!"months"(-4, AllowDayOverflow.no); - assert(sysTime == SysTime(DateTime(-1999, 6, 6, 12, 2, 7), usecs(5007))); - } - - { - auto sysTime = SysTime(DateTime(-2002, 12, 31, 7, 7, 7), hnsecs(422202)); - sysTime.roll!"months"(14, AllowDayOverflow.no); - assert(sysTime == SysTime(DateTime(-2002, 2, 28, 7, 7, 7), hnsecs(422202))); - sysTime.roll!"months"(-14, AllowDayOverflow.no); - assert(sysTime == SysTime(DateTime(-2002, 12, 28, 7, 7, 7), hnsecs(422202))); - } - - { - auto sysTime = SysTime(DateTime(-2001, 12, 31, 7, 7, 7), hnsecs(422202)); - sysTime.roll!"months"(14, AllowDayOverflow.no); - assert(sysTime == SysTime(DateTime(-2001, 2, 28, 7, 7, 7), hnsecs(422202))); - sysTime.roll!"months"(-14, AllowDayOverflow.no); - assert(sysTime == SysTime(DateTime(-2001, 12, 28, 7, 7, 7), hnsecs(422202))); - } - - // Test Both - { - auto sysTime = SysTime(Date(1, 1, 1)); - sysTime.roll!"months"(-1, AllowDayOverflow.no); - assert(sysTime == SysTime(Date(1, 12, 1))); - sysTime.roll!"months"(1, AllowDayOverflow.no); - assert(sysTime == SysTime(Date(1, 1, 1))); - } - - { - auto sysTime = SysTime(Date(4, 1, 1)); - sysTime.roll!"months"(-48, AllowDayOverflow.no); - assert(sysTime == SysTime(Date(4, 1, 1))); - sysTime.roll!"months"(48, AllowDayOverflow.no); - assert(sysTime == SysTime(Date(4, 1, 1))); - } - - { - auto sysTime = SysTime(Date(4, 3, 31)); - sysTime.roll!"months"(-49, AllowDayOverflow.no); - assert(sysTime == SysTime(Date(4, 2, 29))); - sysTime.roll!"months"(49, AllowDayOverflow.no); - assert(sysTime == SysTime(Date(4, 3, 29))); - } - - { - auto sysTime = SysTime(Date(4, 3, 31)); - sysTime.roll!"months"(-85, AllowDayOverflow.no); - assert(sysTime == SysTime(Date(4, 2, 29))); - sysTime.roll!"months"(85, AllowDayOverflow.no); - assert(sysTime == SysTime(Date(4, 3, 29))); - } - - { - auto sysTime = SysTime(Date(-1, 1, 1)); - sysTime.roll!"months"(-1, AllowDayOverflow.no); - assert(sysTime == SysTime(Date(-1, 12, 1))); - sysTime.roll!"months"(1, AllowDayOverflow.no); - assert(sysTime == SysTime(Date(-1, 1, 1))); - } - - { - auto sysTime = SysTime(Date(-4, 1, 1)); - sysTime.roll!"months"(-48, AllowDayOverflow.no); - assert(sysTime == SysTime(Date(-4, 1, 1))); - sysTime.roll!"months"(48, AllowDayOverflow.no); - assert(sysTime == SysTime(Date(-4, 1, 1))); - } - - { - auto sysTime = SysTime(Date(-4, 3, 31)); - sysTime.roll!"months"(-49, AllowDayOverflow.no); - assert(sysTime == SysTime(Date(-4, 2, 29))); - sysTime.roll!"months"(49, AllowDayOverflow.no); - assert(sysTime == SysTime(Date(-4, 3, 29))); - } - - { - auto sysTime = SysTime(Date(-4, 3, 31)); - sysTime.roll!"months"(-85, AllowDayOverflow.no); - assert(sysTime == SysTime(Date(-4, 2, 29))); - sysTime.roll!"months"(85, AllowDayOverflow.no); - assert(sysTime == SysTime(Date(-4, 3, 29))); - } - - { - auto sysTime = SysTime(DateTime(1, 1, 1, 0, 0, 0)); - sysTime.roll!"months"(-1, AllowDayOverflow.no); - assert(sysTime == SysTime(DateTime(1, 12, 1, 0, 0, 0))); - sysTime.roll!"months"(1, AllowDayOverflow.no); - assert(sysTime == SysTime(DateTime(1, 1, 1, 0, 0, 0))); - } - - { - auto sysTime = SysTime(DateTime(1, 1, 1, 23, 59, 59), hnsecs(9_999_999)); - sysTime.roll!"months"(-1, AllowDayOverflow.no); - assert(sysTime == SysTime(DateTime(1, 12, 1, 23, 59, 59), hnsecs(9_999_999))); - sysTime.roll!"months"(1, AllowDayOverflow.no); - assert(sysTime == SysTime(DateTime(1, 1, 1, 23, 59, 59), hnsecs(9_999_999))); - } - - { - auto sysTime = SysTime(DateTime(0, 12, 1, 0, 0, 0)); - sysTime.roll!"months"(1, AllowDayOverflow.no); - assert(sysTime == SysTime(DateTime(0, 1, 1, 0, 0, 0))); - sysTime.roll!"months"(-1, AllowDayOverflow.no); - assert(sysTime == SysTime(DateTime(0, 12, 1, 0, 0, 0))); - } - - { - auto sysTime = SysTime(DateTime(0, 12, 1, 23, 59, 59), hnsecs(9_999_999)); - sysTime.roll!"months"(1, AllowDayOverflow.no); - assert(sysTime == SysTime(DateTime(0, 1, 1, 23, 59, 59), hnsecs(9_999_999))); - sysTime.roll!"months"(-1, AllowDayOverflow.no); - assert(sysTime == SysTime(DateTime(0, 12, 1, 23, 59, 59), hnsecs(9_999_999))); - } - - { - auto sysTime = SysTime(DateTime(1, 1, 1, 0, 7, 9), hnsecs(17)); - sysTime.roll!"months"(-1, AllowDayOverflow.no); - assert(sysTime == SysTime(DateTime(1, 12, 1, 0, 7, 9), hnsecs(17))); - sysTime.roll!"months"(1, AllowDayOverflow.no); - assert(sysTime == SysTime(DateTime(1, 1, 1, 0, 7, 9), hnsecs(17))); - } - - { - auto sysTime = SysTime(DateTime(4, 3, 31, 12, 11, 10), msecs(9)); - sysTime.roll!"months"(-85, AllowDayOverflow.no); - assert(sysTime == SysTime(DateTime(4, 2, 29, 12, 11, 10), msecs(9))); - sysTime.roll!"months"(85, AllowDayOverflow.no); - assert(sysTime == SysTime(DateTime(4, 3, 29, 12, 11, 10), msecs(9))); - } - - { - auto sysTime = SysTime(DateTime(-3, 3, 31, 12, 11, 10), msecs(9)); - sysTime.roll!"months"(85, AllowDayOverflow.no); - assert(sysTime == SysTime(DateTime(-3, 4, 30, 12, 11, 10), msecs(9))); - sysTime.roll!"months"(-85, AllowDayOverflow.no); - assert(sysTime == SysTime(DateTime(-3, 3, 30, 12, 11, 10), msecs(9))); - } - - { - auto sysTime = SysTime(DateTime(-3, 3, 31, 12, 11, 10), msecs(9)); - sysTime.roll!"months"(85, AllowDayOverflow.no).roll!"months"(-83, AllowDayOverflow.no); - assert(sysTime == SysTime(DateTime(-3, 5, 30, 12, 11, 10), msecs(9))); - } - } - - - /++ - Adds the given number of units to this $(LREF SysTime). A negative number - will subtract. - - The difference between rolling and adding is that rolling does not - affect larger units. For instance, rolling a $(LREF SysTime) one - year's worth of days gets the exact same $(LREF SysTime). - - Accepted units are `"days"`, `"minutes"`, `"hours"`, - `"minutes"`, `"seconds"`, `"msecs"`, `"usecs"`, and - `"hnsecs"`. - - Note that when rolling msecs, usecs or hnsecs, they all add up to a - second. So, for example, rolling 1000 msecs is exactly the same as - rolling 100,000 usecs. - - Params: - units = The units to add. - value = The number of $(D_PARAM units) to add to this - $(LREF SysTime). - +/ - ref SysTime roll(string units)(long value) @safe nothrow scope - if (units == "days") - { - auto hnsecs = adjTime; - auto gdays = splitUnitsFromHNSecs!"days"(hnsecs) + 1; - - if (hnsecs < 0) - { - hnsecs += convert!("hours", "hnsecs")(24); - --gdays; - } - - auto date = Date(cast(int) gdays); - date.roll!"days"(value); - gdays = date.dayOfGregorianCal - 1; - - if (gdays < 0) - { - hnsecs -= convert!("hours", "hnsecs")(24); - ++gdays; - } - - immutable newDaysHNSecs = convert!("days", "hnsecs")(gdays); - adjTime = newDaysHNSecs + hnsecs; - return this; - } - - /// - @safe unittest - { - import core.time : msecs, hnsecs; - import std.datetime.date : DateTime; - - auto st1 = SysTime(DateTime(2010, 1, 1, 11, 23, 12)); - st1.roll!"days"(1); - assert(st1 == SysTime(DateTime(2010, 1, 2, 11, 23, 12))); - st1.roll!"days"(365); - assert(st1 == SysTime(DateTime(2010, 1, 26, 11, 23, 12))); - st1.roll!"days"(-32); - assert(st1 == SysTime(DateTime(2010, 1, 25, 11, 23, 12))); - - auto st2 = SysTime(DateTime(2010, 7, 4, 12, 0, 0)); - st2.roll!"hours"(1); - assert(st2 == SysTime(DateTime(2010, 7, 4, 13, 0, 0))); - - auto st3 = SysTime(DateTime(2010, 2, 12, 12, 0, 0)); - st3.roll!"hours"(-1); - assert(st3 == SysTime(DateTime(2010, 2, 12, 11, 0, 0))); - - auto st4 = SysTime(DateTime(2009, 12, 31, 0, 0, 0)); - st4.roll!"minutes"(1); - assert(st4 == SysTime(DateTime(2009, 12, 31, 0, 1, 0))); - - auto st5 = SysTime(DateTime(2010, 1, 1, 0, 0, 0)); - st5.roll!"minutes"(-1); - assert(st5 == SysTime(DateTime(2010, 1, 1, 0, 59, 0))); - - auto st6 = SysTime(DateTime(2009, 12, 31, 0, 0, 0)); - st6.roll!"seconds"(1); - assert(st6 == SysTime(DateTime(2009, 12, 31, 0, 0, 1))); - - auto st7 = SysTime(DateTime(2010, 1, 1, 0, 0, 0)); - st7.roll!"seconds"(-1); - assert(st7 == SysTime(DateTime(2010, 1, 1, 0, 0, 59))); - - auto dt = DateTime(2010, 1, 1, 0, 0, 0); - auto st8 = SysTime(dt); - st8.roll!"msecs"(1); - assert(st8 == SysTime(dt, msecs(1))); - - auto st9 = SysTime(dt); - st9.roll!"msecs"(-1); - assert(st9 == SysTime(dt, msecs(999))); - - auto st10 = SysTime(dt); - st10.roll!"hnsecs"(1); - assert(st10 == SysTime(dt, hnsecs(1))); - - auto st11 = SysTime(dt); - st11.roll!"hnsecs"(-1); - assert(st11 == SysTime(dt, hnsecs(9_999_999))); - } - - @safe unittest - { - import core.time; - // Test A.D. - { - auto sysTime = SysTime(Date(1999, 2, 28)); - sysTime.roll!"days"(1); - assert(sysTime == SysTime(Date(1999, 2, 1))); - sysTime.roll!"days"(-1); - assert(sysTime == SysTime(Date(1999, 2, 28))); - } - - { - auto sysTime = SysTime(Date(2000, 2, 28)); - sysTime.roll!"days"(1); - assert(sysTime == SysTime(Date(2000, 2, 29))); - sysTime.roll!"days"(1); - assert(sysTime == SysTime(Date(2000, 2, 1))); - sysTime.roll!"days"(-1); - assert(sysTime == SysTime(Date(2000, 2, 29))); - } - - { - auto sysTime = SysTime(Date(1999, 6, 30)); - sysTime.roll!"days"(1); - assert(sysTime == SysTime(Date(1999, 6, 1))); - sysTime.roll!"days"(-1); - assert(sysTime == SysTime(Date(1999, 6, 30))); - } - - { - auto sysTime = SysTime(Date(1999, 7, 31)); - sysTime.roll!"days"(1); - assert(sysTime == SysTime(Date(1999, 7, 1))); - sysTime.roll!"days"(-1); - assert(sysTime == SysTime(Date(1999, 7, 31))); - } - - { - auto sysTime = SysTime(Date(1999, 1, 1)); - sysTime.roll!"days"(-1); - assert(sysTime == SysTime(Date(1999, 1, 31))); - sysTime.roll!"days"(1); - assert(sysTime == SysTime(Date(1999, 1, 1))); - } - - { - auto sysTime = SysTime(Date(1999, 7, 6)); - sysTime.roll!"days"(9); - assert(sysTime == SysTime(Date(1999, 7, 15))); - sysTime.roll!"days"(-11); - assert(sysTime == SysTime(Date(1999, 7, 4))); - sysTime.roll!"days"(30); - assert(sysTime == SysTime(Date(1999, 7, 3))); - sysTime.roll!"days"(-3); - assert(sysTime == SysTime(Date(1999, 7, 31))); - } - - { - auto sysTime = SysTime(Date(1999, 7, 6)); - sysTime.roll!"days"(365); - assert(sysTime == SysTime(Date(1999, 7, 30))); - sysTime.roll!"days"(-365); - assert(sysTime == SysTime(Date(1999, 7, 6))); - sysTime.roll!"days"(366); - assert(sysTime == SysTime(Date(1999, 7, 31))); - sysTime.roll!"days"(730); - assert(sysTime == SysTime(Date(1999, 7, 17))); - sysTime.roll!"days"(-1096); - assert(sysTime == SysTime(Date(1999, 7, 6))); - } - - { - auto sysTime = SysTime(Date(1999, 2, 6)); - sysTime.roll!"days"(365); - assert(sysTime == SysTime(Date(1999, 2, 7))); - sysTime.roll!"days"(-365); - assert(sysTime == SysTime(Date(1999, 2, 6))); - sysTime.roll!"days"(366); - assert(sysTime == SysTime(Date(1999, 2, 8))); - sysTime.roll!"days"(730); - assert(sysTime == SysTime(Date(1999, 2, 10))); - sysTime.roll!"days"(-1096); - assert(sysTime == SysTime(Date(1999, 2, 6))); - } - - { - auto sysTime = SysTime(DateTime(1999, 2, 28, 7, 9, 2), usecs(234578)); - sysTime.roll!"days"(1); - assert(sysTime == SysTime(DateTime(1999, 2, 1, 7, 9, 2), usecs(234578))); - sysTime.roll!"days"(-1); - assert(sysTime == SysTime(DateTime(1999, 2, 28, 7, 9, 2), usecs(234578))); - } - - { - auto sysTime = SysTime(DateTime(1999, 7, 6, 7, 9, 2), usecs(234578)); - sysTime.roll!"days"(9); - assert(sysTime == SysTime(DateTime(1999, 7, 15, 7, 9, 2), usecs(234578))); - sysTime.roll!"days"(-11); - assert(sysTime == SysTime(DateTime(1999, 7, 4, 7, 9, 2), usecs(234578))); - sysTime.roll!"days"(30); - assert(sysTime == SysTime(DateTime(1999, 7, 3, 7, 9, 2), usecs(234578))); - sysTime.roll!"days"(-3); - assert(sysTime == SysTime(DateTime(1999, 7, 31, 7, 9, 2), usecs(234578))); - } - - // Test B.C. - { - auto sysTime = SysTime(Date(-1999, 2, 28)); - sysTime.roll!"days"(1); - assert(sysTime == SysTime(Date(-1999, 2, 1))); - sysTime.roll!"days"(-1); - assert(sysTime == SysTime(Date(-1999, 2, 28))); - } - - { - auto sysTime = SysTime(Date(-2000, 2, 28)); - sysTime.roll!"days"(1); - assert(sysTime == SysTime(Date(-2000, 2, 29))); - sysTime.roll!"days"(1); - assert(sysTime == SysTime(Date(-2000, 2, 1))); - sysTime.roll!"days"(-1); - assert(sysTime == SysTime(Date(-2000, 2, 29))); - } - - { - auto sysTime = SysTime(Date(-1999, 6, 30)); - sysTime.roll!"days"(1); - assert(sysTime == SysTime(Date(-1999, 6, 1))); - sysTime.roll!"days"(-1); - assert(sysTime == SysTime(Date(-1999, 6, 30))); - } - - { - auto sysTime = SysTime(Date(-1999, 7, 31)); - sysTime.roll!"days"(1); - assert(sysTime == SysTime(Date(-1999, 7, 1))); - sysTime.roll!"days"(-1); - assert(sysTime == SysTime(Date(-1999, 7, 31))); - } - - { - auto sysTime = SysTime(Date(-1999, 1, 1)); - sysTime.roll!"days"(-1); - assert(sysTime == SysTime(Date(-1999, 1, 31))); - sysTime.roll!"days"(1); - assert(sysTime == SysTime(Date(-1999, 1, 1))); - } - - { - auto sysTime = SysTime(Date(-1999, 7, 6)); - sysTime.roll!"days"(9); - assert(sysTime == SysTime(Date(-1999, 7, 15))); - sysTime.roll!"days"(-11); - assert(sysTime == SysTime(Date(-1999, 7, 4))); - sysTime.roll!"days"(30); - assert(sysTime == SysTime(Date(-1999, 7, 3))); - sysTime.roll!"days"(-3); - assert(sysTime == SysTime(Date(-1999, 7, 31))); - } - - { - auto sysTime = SysTime(Date(-1999, 7, 6)); - sysTime.roll!"days"(365); - assert(sysTime == SysTime(Date(-1999, 7, 30))); - sysTime.roll!"days"(-365); - assert(sysTime == SysTime(Date(-1999, 7, 6))); - sysTime.roll!"days"(366); - assert(sysTime == SysTime(Date(-1999, 7, 31))); - sysTime.roll!"days"(730); - assert(sysTime == SysTime(Date(-1999, 7, 17))); - sysTime.roll!"days"(-1096); - assert(sysTime == SysTime(Date(-1999, 7, 6))); - } - - { - auto sysTime = SysTime(DateTime(-1999, 2, 28, 7, 9, 2), usecs(234578)); - sysTime.roll!"days"(1); - assert(sysTime == SysTime(DateTime(-1999, 2, 1, 7, 9, 2), usecs(234578))); - sysTime.roll!"days"(-1); - assert(sysTime == SysTime(DateTime(-1999, 2, 28, 7, 9, 2), usecs(234578))); - } - - { - auto sysTime = SysTime(DateTime(-1999, 7, 6, 7, 9, 2), usecs(234578)); - sysTime.roll!"days"(9); - assert(sysTime == SysTime(DateTime(-1999, 7, 15, 7, 9, 2), usecs(234578))); - sysTime.roll!"days"(-11); - assert(sysTime == SysTime(DateTime(-1999, 7, 4, 7, 9, 2), usecs(234578))); - sysTime.roll!"days"(30); - assert(sysTime == SysTime(DateTime(-1999, 7, 3, 7, 9, 2), usecs(234578))); - sysTime.roll!"days"(-3); - } - - // Test Both - { - auto sysTime = SysTime(Date(1, 7, 6)); - sysTime.roll!"days"(-365); - assert(sysTime == SysTime(Date(1, 7, 13))); - sysTime.roll!"days"(365); - assert(sysTime == SysTime(Date(1, 7, 6))); - sysTime.roll!"days"(-731); - assert(sysTime == SysTime(Date(1, 7, 19))); - sysTime.roll!"days"(730); - assert(sysTime == SysTime(Date(1, 7, 5))); - } - - { - auto sysTime = SysTime(DateTime(1, 1, 1, 0, 0, 0)); - sysTime.roll!"days"(-1); - assert(sysTime == SysTime(DateTime(1, 1, 31, 0, 0, 0))); - sysTime.roll!"days"(1); - assert(sysTime == SysTime(DateTime(1, 1, 1, 0, 0, 0))); - } - - { - auto sysTime = SysTime(DateTime(1, 1, 1, 23, 59, 59), hnsecs(9_999_999)); - sysTime.roll!"days"(-1); - assert(sysTime == SysTime(DateTime(1, 1, 31, 23, 59, 59), hnsecs(9_999_999))); - sysTime.roll!"days"(1); - assert(sysTime == SysTime(DateTime(1, 1, 1, 23, 59, 59), hnsecs(9_999_999))); - } - - { - auto sysTime = SysTime(DateTime(0, 12, 31, 0, 0, 0)); - sysTime.roll!"days"(1); - assert(sysTime == SysTime(DateTime(0, 12, 1, 0, 0, 0))); - sysTime.roll!"days"(-1); - assert(sysTime == SysTime(DateTime(0, 12, 31, 0, 0, 0))); - } - - { - auto sysTime = SysTime(DateTime(0, 12, 31, 23, 59, 59), hnsecs(9_999_999)); - sysTime.roll!"days"(1); - assert(sysTime == SysTime(DateTime(0, 12, 1, 23, 59, 59), hnsecs(9_999_999))); - sysTime.roll!"days"(-1); - assert(sysTime == SysTime(DateTime(0, 12, 31, 23, 59, 59), hnsecs(9_999_999))); - } - - { - auto sysTime = SysTime(DateTime(1, 7, 6, 13, 13, 9), msecs(22)); - sysTime.roll!"days"(-365); - assert(sysTime == SysTime(DateTime(1, 7, 13, 13, 13, 9), msecs(22))); - sysTime.roll!"days"(365); - assert(sysTime == SysTime(DateTime(1, 7, 6, 13, 13, 9), msecs(22))); - sysTime.roll!"days"(-731); - assert(sysTime == SysTime(DateTime(1, 7, 19, 13, 13, 9), msecs(22))); - sysTime.roll!"days"(730); - assert(sysTime == SysTime(DateTime(1, 7, 5, 13, 13, 9), msecs(22))); - } - - { - auto sysTime = SysTime(DateTime(0, 7, 6, 13, 13, 9), msecs(22)); - sysTime.roll!"days"(-365); - assert(sysTime == SysTime(DateTime(0, 7, 13, 13, 13, 9), msecs(22))); - sysTime.roll!"days"(365); - assert(sysTime == SysTime(DateTime(0, 7, 6, 13, 13, 9), msecs(22))); - sysTime.roll!"days"(-731); - assert(sysTime == SysTime(DateTime(0, 7, 19, 13, 13, 9), msecs(22))); - sysTime.roll!"days"(730); - assert(sysTime == SysTime(DateTime(0, 7, 5, 13, 13, 9), msecs(22))); - } - - { - auto sysTime = SysTime(DateTime(0, 7, 6, 13, 13, 9), msecs(22)); - sysTime.roll!"days"(-365).roll!"days"(362).roll!"days"(-12).roll!"days"(730); - assert(sysTime == SysTime(DateTime(0, 7, 8, 13, 13, 9), msecs(22))); - } - - const cst = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); - immutable ist = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); - static assert(!__traits(compiles, cst.roll!"days"(4))); - static assert(!__traits(compiles, ist.roll!"days"(4))); - - static void testScope(scope ref SysTime st) @safe - { - auto result = st.roll!"days"(42); - } - } - - - // Shares documentation with "days" version. - ref SysTime roll(string units)(long value) @safe nothrow scope - if (units == "hours" || units == "minutes" || units == "seconds") - { - try - { - auto hnsecs = adjTime; - auto days = splitUnitsFromHNSecs!"days"(hnsecs) + 1; - - if (hnsecs < 0) - { - hnsecs += convert!("hours", "hnsecs")(24); - --days; - } - - immutable hour = splitUnitsFromHNSecs!"hours"(hnsecs); - immutable minute = splitUnitsFromHNSecs!"minutes"(hnsecs); - immutable second = splitUnitsFromHNSecs!"seconds"(hnsecs); - - auto dateTime = DateTime(Date(cast(int) days), TimeOfDay(cast(int) hour, - cast(int) minute, cast(int) second)); - dateTime.roll!units(value); - --days; - - hnsecs += convert!("hours", "hnsecs")(dateTime.hour); - hnsecs += convert!("minutes", "hnsecs")(dateTime.minute); - hnsecs += convert!("seconds", "hnsecs")(dateTime.second); - - if (days < 0) - { - hnsecs -= convert!("hours", "hnsecs")(24); - ++days; - } - - immutable newDaysHNSecs = convert!("days", "hnsecs")(days); - adjTime = newDaysHNSecs + hnsecs; - return this; - } - catch (Exception e) - assert(0, "Either DateTime's constructor or TimeOfDay's constructor threw."); - } - - // Test roll!"hours"(). - @safe unittest - { - import core.time; - static void testST(SysTime orig, int hours, SysTime expected, size_t line = __LINE__) @safe - { - orig.roll!"hours"(hours); - if (orig != expected) - throw new AssertError(format("Failed. actual [%s] != expected [%s]", orig, expected), __FILE__, line); - } - - // Test A.D. - immutable d = msecs(45); - auto beforeAD = SysTime(DateTime(1999, 7, 6, 12, 30, 33), d); - testST(beforeAD, 0, SysTime(DateTime(1999, 7, 6, 12, 30, 33), d)); - testST(beforeAD, 1, SysTime(DateTime(1999, 7, 6, 13, 30, 33), d)); - testST(beforeAD, 2, SysTime(DateTime(1999, 7, 6, 14, 30, 33), d)); - testST(beforeAD, 3, SysTime(DateTime(1999, 7, 6, 15, 30, 33), d)); - testST(beforeAD, 4, SysTime(DateTime(1999, 7, 6, 16, 30, 33), d)); - testST(beforeAD, 5, SysTime(DateTime(1999, 7, 6, 17, 30, 33), d)); - testST(beforeAD, 6, SysTime(DateTime(1999, 7, 6, 18, 30, 33), d)); - testST(beforeAD, 7, SysTime(DateTime(1999, 7, 6, 19, 30, 33), d)); - testST(beforeAD, 8, SysTime(DateTime(1999, 7, 6, 20, 30, 33), d)); - testST(beforeAD, 9, SysTime(DateTime(1999, 7, 6, 21, 30, 33), d)); - testST(beforeAD, 10, SysTime(DateTime(1999, 7, 6, 22, 30, 33), d)); - testST(beforeAD, 11, SysTime(DateTime(1999, 7, 6, 23, 30, 33), d)); - testST(beforeAD, 12, SysTime(DateTime(1999, 7, 6, 0, 30, 33), d)); - testST(beforeAD, 13, SysTime(DateTime(1999, 7, 6, 1, 30, 33), d)); - testST(beforeAD, 14, SysTime(DateTime(1999, 7, 6, 2, 30, 33), d)); - testST(beforeAD, 15, SysTime(DateTime(1999, 7, 6, 3, 30, 33), d)); - testST(beforeAD, 16, SysTime(DateTime(1999, 7, 6, 4, 30, 33), d)); - testST(beforeAD, 17, SysTime(DateTime(1999, 7, 6, 5, 30, 33), d)); - testST(beforeAD, 18, SysTime(DateTime(1999, 7, 6, 6, 30, 33), d)); - testST(beforeAD, 19, SysTime(DateTime(1999, 7, 6, 7, 30, 33), d)); - testST(beforeAD, 20, SysTime(DateTime(1999, 7, 6, 8, 30, 33), d)); - testST(beforeAD, 21, SysTime(DateTime(1999, 7, 6, 9, 30, 33), d)); - testST(beforeAD, 22, SysTime(DateTime(1999, 7, 6, 10, 30, 33), d)); - testST(beforeAD, 23, SysTime(DateTime(1999, 7, 6, 11, 30, 33), d)); - testST(beforeAD, 24, SysTime(DateTime(1999, 7, 6, 12, 30, 33), d)); - testST(beforeAD, 25, SysTime(DateTime(1999, 7, 6, 13, 30, 33), d)); - testST(beforeAD, 50, SysTime(DateTime(1999, 7, 6, 14, 30, 33), d)); - testST(beforeAD, 10_000, SysTime(DateTime(1999, 7, 6, 4, 30, 33), d)); - - testST(beforeAD, -1, SysTime(DateTime(1999, 7, 6, 11, 30, 33), d)); - testST(beforeAD, -2, SysTime(DateTime(1999, 7, 6, 10, 30, 33), d)); - testST(beforeAD, -3, SysTime(DateTime(1999, 7, 6, 9, 30, 33), d)); - testST(beforeAD, -4, SysTime(DateTime(1999, 7, 6, 8, 30, 33), d)); - testST(beforeAD, -5, SysTime(DateTime(1999, 7, 6, 7, 30, 33), d)); - testST(beforeAD, -6, SysTime(DateTime(1999, 7, 6, 6, 30, 33), d)); - testST(beforeAD, -7, SysTime(DateTime(1999, 7, 6, 5, 30, 33), d)); - testST(beforeAD, -8, SysTime(DateTime(1999, 7, 6, 4, 30, 33), d)); - testST(beforeAD, -9, SysTime(DateTime(1999, 7, 6, 3, 30, 33), d)); - testST(beforeAD, -10, SysTime(DateTime(1999, 7, 6, 2, 30, 33), d)); - testST(beforeAD, -11, SysTime(DateTime(1999, 7, 6, 1, 30, 33), d)); - testST(beforeAD, -12, SysTime(DateTime(1999, 7, 6, 0, 30, 33), d)); - testST(beforeAD, -13, SysTime(DateTime(1999, 7, 6, 23, 30, 33), d)); - testST(beforeAD, -14, SysTime(DateTime(1999, 7, 6, 22, 30, 33), d)); - testST(beforeAD, -15, SysTime(DateTime(1999, 7, 6, 21, 30, 33), d)); - testST(beforeAD, -16, SysTime(DateTime(1999, 7, 6, 20, 30, 33), d)); - testST(beforeAD, -17, SysTime(DateTime(1999, 7, 6, 19, 30, 33), d)); - testST(beforeAD, -18, SysTime(DateTime(1999, 7, 6, 18, 30, 33), d)); - testST(beforeAD, -19, SysTime(DateTime(1999, 7, 6, 17, 30, 33), d)); - testST(beforeAD, -20, SysTime(DateTime(1999, 7, 6, 16, 30, 33), d)); - testST(beforeAD, -21, SysTime(DateTime(1999, 7, 6, 15, 30, 33), d)); - testST(beforeAD, -22, SysTime(DateTime(1999, 7, 6, 14, 30, 33), d)); - testST(beforeAD, -23, SysTime(DateTime(1999, 7, 6, 13, 30, 33), d)); - testST(beforeAD, -24, SysTime(DateTime(1999, 7, 6, 12, 30, 33), d)); - testST(beforeAD, -25, SysTime(DateTime(1999, 7, 6, 11, 30, 33), d)); - testST(beforeAD, -50, SysTime(DateTime(1999, 7, 6, 10, 30, 33), d)); - testST(beforeAD, -10_000, SysTime(DateTime(1999, 7, 6, 20, 30, 33), d)); - - testST(SysTime(DateTime(1999, 7, 6, 0, 30, 33), d), 1, SysTime(DateTime(1999, 7, 6, 1, 30, 33), d)); - testST(SysTime(DateTime(1999, 7, 6, 0, 30, 33), d), 0, SysTime(DateTime(1999, 7, 6, 0, 30, 33), d)); - testST(SysTime(DateTime(1999, 7, 6, 0, 30, 33), d), -1, SysTime(DateTime(1999, 7, 6, 23, 30, 33), d)); - - testST(SysTime(DateTime(1999, 7, 6, 23, 30, 33), d), 1, SysTime(DateTime(1999, 7, 6, 0, 30, 33), d)); - testST(SysTime(DateTime(1999, 7, 6, 23, 30, 33), d), 0, SysTime(DateTime(1999, 7, 6, 23, 30, 33), d)); - testST(SysTime(DateTime(1999, 7, 6, 23, 30, 33), d), -1, SysTime(DateTime(1999, 7, 6, 22, 30, 33), d)); - - testST(SysTime(DateTime(1999, 7, 31, 23, 30, 33), d), 1, SysTime(DateTime(1999, 7, 31, 0, 30, 33), d)); - testST(SysTime(DateTime(1999, 8, 1, 0, 30, 33), d), -1, SysTime(DateTime(1999, 8, 1, 23, 30, 33), d)); - - testST(SysTime(DateTime(1999, 12, 31, 23, 30, 33), d), 1, SysTime(DateTime(1999, 12, 31, 0, 30, 33), d)); - testST(SysTime(DateTime(2000, 1, 1, 0, 30, 33), d), -1, SysTime(DateTime(2000, 1, 1, 23, 30, 33), d)); - - testST(SysTime(DateTime(1999, 2, 28, 23, 30, 33), d), 25, SysTime(DateTime(1999, 2, 28, 0, 30, 33), d)); - testST(SysTime(DateTime(1999, 3, 2, 0, 30, 33), d), -25, SysTime(DateTime(1999, 3, 2, 23, 30, 33), d)); - - testST(SysTime(DateTime(2000, 2, 28, 23, 30, 33), d), 25, SysTime(DateTime(2000, 2, 28, 0, 30, 33), d)); - testST(SysTime(DateTime(2000, 3, 1, 0, 30, 33), d), -25, SysTime(DateTime(2000, 3, 1, 23, 30, 33), d)); - - // Test B.C. - auto beforeBC = SysTime(DateTime(-1999, 7, 6, 12, 30, 33), d); - testST(beforeBC, 0, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), d)); - testST(beforeBC, 1, SysTime(DateTime(-1999, 7, 6, 13, 30, 33), d)); - testST(beforeBC, 2, SysTime(DateTime(-1999, 7, 6, 14, 30, 33), d)); - testST(beforeBC, 3, SysTime(DateTime(-1999, 7, 6, 15, 30, 33), d)); - testST(beforeBC, 4, SysTime(DateTime(-1999, 7, 6, 16, 30, 33), d)); - testST(beforeBC, 5, SysTime(DateTime(-1999, 7, 6, 17, 30, 33), d)); - testST(beforeBC, 6, SysTime(DateTime(-1999, 7, 6, 18, 30, 33), d)); - testST(beforeBC, 7, SysTime(DateTime(-1999, 7, 6, 19, 30, 33), d)); - testST(beforeBC, 8, SysTime(DateTime(-1999, 7, 6, 20, 30, 33), d)); - testST(beforeBC, 9, SysTime(DateTime(-1999, 7, 6, 21, 30, 33), d)); - testST(beforeBC, 10, SysTime(DateTime(-1999, 7, 6, 22, 30, 33), d)); - testST(beforeBC, 11, SysTime(DateTime(-1999, 7, 6, 23, 30, 33), d)); - testST(beforeBC, 12, SysTime(DateTime(-1999, 7, 6, 0, 30, 33), d)); - testST(beforeBC, 13, SysTime(DateTime(-1999, 7, 6, 1, 30, 33), d)); - testST(beforeBC, 14, SysTime(DateTime(-1999, 7, 6, 2, 30, 33), d)); - testST(beforeBC, 15, SysTime(DateTime(-1999, 7, 6, 3, 30, 33), d)); - testST(beforeBC, 16, SysTime(DateTime(-1999, 7, 6, 4, 30, 33), d)); - testST(beforeBC, 17, SysTime(DateTime(-1999, 7, 6, 5, 30, 33), d)); - testST(beforeBC, 18, SysTime(DateTime(-1999, 7, 6, 6, 30, 33), d)); - testST(beforeBC, 19, SysTime(DateTime(-1999, 7, 6, 7, 30, 33), d)); - testST(beforeBC, 20, SysTime(DateTime(-1999, 7, 6, 8, 30, 33), d)); - testST(beforeBC, 21, SysTime(DateTime(-1999, 7, 6, 9, 30, 33), d)); - testST(beforeBC, 22, SysTime(DateTime(-1999, 7, 6, 10, 30, 33), d)); - testST(beforeBC, 23, SysTime(DateTime(-1999, 7, 6, 11, 30, 33), d)); - testST(beforeBC, 24, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), d)); - testST(beforeBC, 25, SysTime(DateTime(-1999, 7, 6, 13, 30, 33), d)); - testST(beforeBC, 50, SysTime(DateTime(-1999, 7, 6, 14, 30, 33), d)); - testST(beforeBC, 10_000, SysTime(DateTime(-1999, 7, 6, 4, 30, 33), d)); - - testST(beforeBC, -1, SysTime(DateTime(-1999, 7, 6, 11, 30, 33), d)); - testST(beforeBC, -2, SysTime(DateTime(-1999, 7, 6, 10, 30, 33), d)); - testST(beforeBC, -3, SysTime(DateTime(-1999, 7, 6, 9, 30, 33), d)); - testST(beforeBC, -4, SysTime(DateTime(-1999, 7, 6, 8, 30, 33), d)); - testST(beforeBC, -5, SysTime(DateTime(-1999, 7, 6, 7, 30, 33), d)); - testST(beforeBC, -6, SysTime(DateTime(-1999, 7, 6, 6, 30, 33), d)); - testST(beforeBC, -7, SysTime(DateTime(-1999, 7, 6, 5, 30, 33), d)); - testST(beforeBC, -8, SysTime(DateTime(-1999, 7, 6, 4, 30, 33), d)); - testST(beforeBC, -9, SysTime(DateTime(-1999, 7, 6, 3, 30, 33), d)); - testST(beforeBC, -10, SysTime(DateTime(-1999, 7, 6, 2, 30, 33), d)); - testST(beforeBC, -11, SysTime(DateTime(-1999, 7, 6, 1, 30, 33), d)); - testST(beforeBC, -12, SysTime(DateTime(-1999, 7, 6, 0, 30, 33), d)); - testST(beforeBC, -13, SysTime(DateTime(-1999, 7, 6, 23, 30, 33), d)); - testST(beforeBC, -14, SysTime(DateTime(-1999, 7, 6, 22, 30, 33), d)); - testST(beforeBC, -15, SysTime(DateTime(-1999, 7, 6, 21, 30, 33), d)); - testST(beforeBC, -16, SysTime(DateTime(-1999, 7, 6, 20, 30, 33), d)); - testST(beforeBC, -17, SysTime(DateTime(-1999, 7, 6, 19, 30, 33), d)); - testST(beforeBC, -18, SysTime(DateTime(-1999, 7, 6, 18, 30, 33), d)); - testST(beforeBC, -19, SysTime(DateTime(-1999, 7, 6, 17, 30, 33), d)); - testST(beforeBC, -20, SysTime(DateTime(-1999, 7, 6, 16, 30, 33), d)); - testST(beforeBC, -21, SysTime(DateTime(-1999, 7, 6, 15, 30, 33), d)); - testST(beforeBC, -22, SysTime(DateTime(-1999, 7, 6, 14, 30, 33), d)); - testST(beforeBC, -23, SysTime(DateTime(-1999, 7, 6, 13, 30, 33), d)); - testST(beforeBC, -24, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), d)); - testST(beforeBC, -25, SysTime(DateTime(-1999, 7, 6, 11, 30, 33), d)); - testST(beforeBC, -50, SysTime(DateTime(-1999, 7, 6, 10, 30, 33), d)); - testST(beforeBC, -10_000, SysTime(DateTime(-1999, 7, 6, 20, 30, 33), d)); - - testST(SysTime(DateTime(-1999, 7, 6, 0, 30, 33), d), 1, SysTime(DateTime(-1999, 7, 6, 1, 30, 33), d)); - testST(SysTime(DateTime(-1999, 7, 6, 0, 30, 33), d), 0, SysTime(DateTime(-1999, 7, 6, 0, 30, 33), d)); - testST(SysTime(DateTime(-1999, 7, 6, 0, 30, 33), d), -1, SysTime(DateTime(-1999, 7, 6, 23, 30, 33), d)); - - testST(SysTime(DateTime(-1999, 7, 6, 23, 30, 33), d), 1, SysTime(DateTime(-1999, 7, 6, 0, 30, 33), d)); - testST(SysTime(DateTime(-1999, 7, 6, 23, 30, 33), d), 0, SysTime(DateTime(-1999, 7, 6, 23, 30, 33), d)); - testST(SysTime(DateTime(-1999, 7, 6, 23, 30, 33), d), -1, SysTime(DateTime(-1999, 7, 6, 22, 30, 33), d)); - - testST(SysTime(DateTime(-1999, 7, 31, 23, 30, 33), d), 1, SysTime(DateTime(-1999, 7, 31, 0, 30, 33), d)); - testST(SysTime(DateTime(-1999, 8, 1, 0, 30, 33), d), -1, SysTime(DateTime(-1999, 8, 1, 23, 30, 33), d)); - - testST(SysTime(DateTime(-2001, 12, 31, 23, 30, 33), d), 1, SysTime(DateTime(-2001, 12, 31, 0, 30, 33), d)); - testST(SysTime(DateTime(-2000, 1, 1, 0, 30, 33), d), -1, SysTime(DateTime(-2000, 1, 1, 23, 30, 33), d)); - - testST(SysTime(DateTime(-2001, 2, 28, 23, 30, 33), d), 25, SysTime(DateTime(-2001, 2, 28, 0, 30, 33), d)); - testST(SysTime(DateTime(-2001, 3, 2, 0, 30, 33), d), -25, SysTime(DateTime(-2001, 3, 2, 23, 30, 33), d)); - - testST(SysTime(DateTime(-2000, 2, 28, 23, 30, 33), d), 25, SysTime(DateTime(-2000, 2, 28, 0, 30, 33), d)); - testST(SysTime(DateTime(-2000, 3, 1, 0, 30, 33), d), -25, SysTime(DateTime(-2000, 3, 1, 23, 30, 33), d)); - - // Test Both - testST(SysTime(DateTime(-1, 1, 1, 11, 30, 33), d), 17_546, SysTime(DateTime(-1, 1, 1, 13, 30, 33), d)); - testST(SysTime(DateTime(1, 1, 1, 13, 30, 33), d), -17_546, SysTime(DateTime(1, 1, 1, 11, 30, 33), d)); - - { - auto sysTime = SysTime(DateTime(1, 1, 1, 0, 0, 0)); - sysTime.roll!"hours"(-1); - assert(sysTime == SysTime(DateTime(1, 1, 1, 23, 0, 0))); - sysTime.roll!"hours"(1); - assert(sysTime == SysTime(DateTime(1, 1, 1, 0, 0, 0))); - } - - { - auto sysTime = SysTime(DateTime(1, 1, 1, 0, 59, 59), hnsecs(9_999_999)); - sysTime.roll!"hours"(-1); - assert(sysTime == SysTime(DateTime(1, 1, 1, 23, 59, 59), hnsecs(9_999_999))); - sysTime.roll!"hours"(1); - assert(sysTime == SysTime(DateTime(1, 1, 1, 0, 59, 59), hnsecs(9_999_999))); - } - - { - auto sysTime = SysTime(DateTime(0, 12, 31, 23, 0, 0)); - sysTime.roll!"hours"(1); - assert(sysTime == SysTime(DateTime(0, 12, 31, 0, 0, 0))); - sysTime.roll!"hours"(-1); - assert(sysTime == SysTime(DateTime(0, 12, 31, 23, 0, 0))); - } - - { - auto sysTime = SysTime(DateTime(0, 12, 31, 23, 59, 59), hnsecs(9_999_999)); - sysTime.roll!"hours"(1); - assert(sysTime == SysTime(DateTime(0, 12, 31, 0, 59, 59), hnsecs(9_999_999))); - sysTime.roll!"hours"(-1); - assert(sysTime == SysTime(DateTime(0, 12, 31, 23, 59, 59), hnsecs(9_999_999))); - } - - { - auto sysTime = SysTime(DateTime(0, 12, 31, 23, 59, 59), hnsecs(9_999_999)); - sysTime.roll!"hours"(1).roll!"hours"(-67); - assert(sysTime == SysTime(DateTime(0, 12, 31, 5, 59, 59), hnsecs(9_999_999))); - } - - const cst = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); - immutable ist = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); - static assert(!__traits(compiles, cst.roll!"hours"(4))); - static assert(!__traits(compiles, ist.roll!"hours"(4))); - - static void testScope(scope ref SysTime st) @safe - { - auto result = st.roll!"hours"(42); - } - } - - // Test roll!"minutes"(). - @safe unittest - { - import core.time; - static void testST(SysTime orig, int minutes, SysTime expected, size_t line = __LINE__) @safe - { - orig.roll!"minutes"(minutes); - if (orig != expected) - throw new AssertError(format("Failed. actual [%s] != expected [%s]", orig, expected), __FILE__, line); - } - - // Test A.D. - immutable d = usecs(7203); - auto beforeAD = SysTime(DateTime(1999, 7, 6, 12, 30, 33), d); - testST(beforeAD, 0, SysTime(DateTime(1999, 7, 6, 12, 30, 33), d)); - testST(beforeAD, 1, SysTime(DateTime(1999, 7, 6, 12, 31, 33), d)); - testST(beforeAD, 2, SysTime(DateTime(1999, 7, 6, 12, 32, 33), d)); - testST(beforeAD, 3, SysTime(DateTime(1999, 7, 6, 12, 33, 33), d)); - testST(beforeAD, 4, SysTime(DateTime(1999, 7, 6, 12, 34, 33), d)); - testST(beforeAD, 5, SysTime(DateTime(1999, 7, 6, 12, 35, 33), d)); - testST(beforeAD, 10, SysTime(DateTime(1999, 7, 6, 12, 40, 33), d)); - testST(beforeAD, 15, SysTime(DateTime(1999, 7, 6, 12, 45, 33), d)); - testST(beforeAD, 29, SysTime(DateTime(1999, 7, 6, 12, 59, 33), d)); - testST(beforeAD, 30, SysTime(DateTime(1999, 7, 6, 12, 0, 33), d)); - testST(beforeAD, 45, SysTime(DateTime(1999, 7, 6, 12, 15, 33), d)); - testST(beforeAD, 60, SysTime(DateTime(1999, 7, 6, 12, 30, 33), d)); - testST(beforeAD, 75, SysTime(DateTime(1999, 7, 6, 12, 45, 33), d)); - testST(beforeAD, 90, SysTime(DateTime(1999, 7, 6, 12, 0, 33), d)); - testST(beforeAD, 100, SysTime(DateTime(1999, 7, 6, 12, 10, 33), d)); - - testST(beforeAD, 689, SysTime(DateTime(1999, 7, 6, 12, 59, 33), d)); - testST(beforeAD, 690, SysTime(DateTime(1999, 7, 6, 12, 0, 33), d)); - testST(beforeAD, 691, SysTime(DateTime(1999, 7, 6, 12, 1, 33), d)); - testST(beforeAD, 960, SysTime(DateTime(1999, 7, 6, 12, 30, 33), d)); - testST(beforeAD, 1439, SysTime(DateTime(1999, 7, 6, 12, 29, 33), d)); - testST(beforeAD, 1440, SysTime(DateTime(1999, 7, 6, 12, 30, 33), d)); - testST(beforeAD, 1441, SysTime(DateTime(1999, 7, 6, 12, 31, 33), d)); - testST(beforeAD, 2880, SysTime(DateTime(1999, 7, 6, 12, 30, 33), d)); - - testST(beforeAD, -1, SysTime(DateTime(1999, 7, 6, 12, 29, 33), d)); - testST(beforeAD, -2, SysTime(DateTime(1999, 7, 6, 12, 28, 33), d)); - testST(beforeAD, -3, SysTime(DateTime(1999, 7, 6, 12, 27, 33), d)); - testST(beforeAD, -4, SysTime(DateTime(1999, 7, 6, 12, 26, 33), d)); - testST(beforeAD, -5, SysTime(DateTime(1999, 7, 6, 12, 25, 33), d)); - testST(beforeAD, -10, SysTime(DateTime(1999, 7, 6, 12, 20, 33), d)); - testST(beforeAD, -15, SysTime(DateTime(1999, 7, 6, 12, 15, 33), d)); - testST(beforeAD, -29, SysTime(DateTime(1999, 7, 6, 12, 1, 33), d)); - testST(beforeAD, -30, SysTime(DateTime(1999, 7, 6, 12, 0, 33), d)); - testST(beforeAD, -45, SysTime(DateTime(1999, 7, 6, 12, 45, 33), d)); - testST(beforeAD, -60, SysTime(DateTime(1999, 7, 6, 12, 30, 33), d)); - testST(beforeAD, -75, SysTime(DateTime(1999, 7, 6, 12, 15, 33), d)); - testST(beforeAD, -90, SysTime(DateTime(1999, 7, 6, 12, 0, 33), d)); - testST(beforeAD, -100, SysTime(DateTime(1999, 7, 6, 12, 50, 33), d)); - - testST(beforeAD, -749, SysTime(DateTime(1999, 7, 6, 12, 1, 33), d)); - testST(beforeAD, -750, SysTime(DateTime(1999, 7, 6, 12, 0, 33), d)); - testST(beforeAD, -751, SysTime(DateTime(1999, 7, 6, 12, 59, 33), d)); - testST(beforeAD, -960, SysTime(DateTime(1999, 7, 6, 12, 30, 33), d)); - testST(beforeAD, -1439, SysTime(DateTime(1999, 7, 6, 12, 31, 33), d)); - testST(beforeAD, -1440, SysTime(DateTime(1999, 7, 6, 12, 30, 33), d)); - testST(beforeAD, -1441, SysTime(DateTime(1999, 7, 6, 12, 29, 33), d)); - testST(beforeAD, -2880, SysTime(DateTime(1999, 7, 6, 12, 30, 33), d)); - - testST(SysTime(DateTime(1999, 7, 6, 12, 0, 33), d), 1, SysTime(DateTime(1999, 7, 6, 12, 1, 33), d)); - testST(SysTime(DateTime(1999, 7, 6, 12, 0, 33), d), 0, SysTime(DateTime(1999, 7, 6, 12, 0, 33), d)); - testST(SysTime(DateTime(1999, 7, 6, 12, 0, 33), d), -1, SysTime(DateTime(1999, 7, 6, 12, 59, 33), d)); - - testST(SysTime(DateTime(1999, 7, 6, 11, 59, 33), d), 1, SysTime(DateTime(1999, 7, 6, 11, 0, 33), d)); - testST(SysTime(DateTime(1999, 7, 6, 11, 59, 33), d), 0, SysTime(DateTime(1999, 7, 6, 11, 59, 33), d)); - testST(SysTime(DateTime(1999, 7, 6, 11, 59, 33), d), -1, SysTime(DateTime(1999, 7, 6, 11, 58, 33), d)); - - testST(SysTime(DateTime(1999, 7, 6, 0, 0, 33), d), 1, SysTime(DateTime(1999, 7, 6, 0, 1, 33), d)); - testST(SysTime(DateTime(1999, 7, 6, 0, 0, 33), d), 0, SysTime(DateTime(1999, 7, 6, 0, 0, 33), d)); - testST(SysTime(DateTime(1999, 7, 6, 0, 0, 33), d), -1, SysTime(DateTime(1999, 7, 6, 0, 59, 33), d)); - - testST(SysTime(DateTime(1999, 7, 5, 23, 59, 33), d), 1, SysTime(DateTime(1999, 7, 5, 23, 0, 33), d)); - testST(SysTime(DateTime(1999, 7, 5, 23, 59, 33), d), 0, SysTime(DateTime(1999, 7, 5, 23, 59, 33), d)); - testST(SysTime(DateTime(1999, 7, 5, 23, 59, 33), d), -1, SysTime(DateTime(1999, 7, 5, 23, 58, 33), d)); - - testST(SysTime(DateTime(1998, 12, 31, 23, 59, 33), d), 1, SysTime(DateTime(1998, 12, 31, 23, 0, 33), d)); - testST(SysTime(DateTime(1998, 12, 31, 23, 59, 33), d), 0, SysTime(DateTime(1998, 12, 31, 23, 59, 33), d)); - testST(SysTime(DateTime(1998, 12, 31, 23, 59, 33), d), -1, SysTime(DateTime(1998, 12, 31, 23, 58, 33), d)); - - // Test B.C. - auto beforeBC = SysTime(DateTime(-1999, 7, 6, 12, 30, 33), d); - testST(beforeBC, 0, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), d)); - testST(beforeBC, 1, SysTime(DateTime(-1999, 7, 6, 12, 31, 33), d)); - testST(beforeBC, 2, SysTime(DateTime(-1999, 7, 6, 12, 32, 33), d)); - testST(beforeBC, 3, SysTime(DateTime(-1999, 7, 6, 12, 33, 33), d)); - testST(beforeBC, 4, SysTime(DateTime(-1999, 7, 6, 12, 34, 33), d)); - testST(beforeBC, 5, SysTime(DateTime(-1999, 7, 6, 12, 35, 33), d)); - testST(beforeBC, 10, SysTime(DateTime(-1999, 7, 6, 12, 40, 33), d)); - testST(beforeBC, 15, SysTime(DateTime(-1999, 7, 6, 12, 45, 33), d)); - testST(beforeBC, 29, SysTime(DateTime(-1999, 7, 6, 12, 59, 33), d)); - testST(beforeBC, 30, SysTime(DateTime(-1999, 7, 6, 12, 0, 33), d)); - testST(beforeBC, 45, SysTime(DateTime(-1999, 7, 6, 12, 15, 33), d)); - testST(beforeBC, 60, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), d)); - testST(beforeBC, 75, SysTime(DateTime(-1999, 7, 6, 12, 45, 33), d)); - testST(beforeBC, 90, SysTime(DateTime(-1999, 7, 6, 12, 0, 33), d)); - testST(beforeBC, 100, SysTime(DateTime(-1999, 7, 6, 12, 10, 33), d)); - - testST(beforeBC, 689, SysTime(DateTime(-1999, 7, 6, 12, 59, 33), d)); - testST(beforeBC, 690, SysTime(DateTime(-1999, 7, 6, 12, 0, 33), d)); - testST(beforeBC, 691, SysTime(DateTime(-1999, 7, 6, 12, 1, 33), d)); - testST(beforeBC, 960, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), d)); - testST(beforeBC, 1439, SysTime(DateTime(-1999, 7, 6, 12, 29, 33), d)); - testST(beforeBC, 1440, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), d)); - testST(beforeBC, 1441, SysTime(DateTime(-1999, 7, 6, 12, 31, 33), d)); - testST(beforeBC, 2880, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), d)); - - testST(beforeBC, -1, SysTime(DateTime(-1999, 7, 6, 12, 29, 33), d)); - testST(beforeBC, -2, SysTime(DateTime(-1999, 7, 6, 12, 28, 33), d)); - testST(beforeBC, -3, SysTime(DateTime(-1999, 7, 6, 12, 27, 33), d)); - testST(beforeBC, -4, SysTime(DateTime(-1999, 7, 6, 12, 26, 33), d)); - testST(beforeBC, -5, SysTime(DateTime(-1999, 7, 6, 12, 25, 33), d)); - testST(beforeBC, -10, SysTime(DateTime(-1999, 7, 6, 12, 20, 33), d)); - testST(beforeBC, -15, SysTime(DateTime(-1999, 7, 6, 12, 15, 33), d)); - testST(beforeBC, -29, SysTime(DateTime(-1999, 7, 6, 12, 1, 33), d)); - testST(beforeBC, -30, SysTime(DateTime(-1999, 7, 6, 12, 0, 33), d)); - testST(beforeBC, -45, SysTime(DateTime(-1999, 7, 6, 12, 45, 33), d)); - testST(beforeBC, -60, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), d)); - testST(beforeBC, -75, SysTime(DateTime(-1999, 7, 6, 12, 15, 33), d)); - testST(beforeBC, -90, SysTime(DateTime(-1999, 7, 6, 12, 0, 33), d)); - testST(beforeBC, -100, SysTime(DateTime(-1999, 7, 6, 12, 50, 33), d)); - - testST(beforeBC, -749, SysTime(DateTime(-1999, 7, 6, 12, 1, 33), d)); - testST(beforeBC, -750, SysTime(DateTime(-1999, 7, 6, 12, 0, 33), d)); - testST(beforeBC, -751, SysTime(DateTime(-1999, 7, 6, 12, 59, 33), d)); - testST(beforeBC, -960, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), d)); - testST(beforeBC, -1439, SysTime(DateTime(-1999, 7, 6, 12, 31, 33), d)); - testST(beforeBC, -1440, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), d)); - testST(beforeBC, -1441, SysTime(DateTime(-1999, 7, 6, 12, 29, 33), d)); - testST(beforeBC, -2880, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), d)); - - testST(SysTime(DateTime(-1999, 7, 6, 12, 0, 33), d), 1, SysTime(DateTime(-1999, 7, 6, 12, 1, 33), d)); - testST(SysTime(DateTime(-1999, 7, 6, 12, 0, 33), d), 0, SysTime(DateTime(-1999, 7, 6, 12, 0, 33), d)); - testST(SysTime(DateTime(-1999, 7, 6, 12, 0, 33), d), -1, SysTime(DateTime(-1999, 7, 6, 12, 59, 33), d)); - - testST(SysTime(DateTime(-1999, 7, 6, 11, 59, 33), d), 1, SysTime(DateTime(-1999, 7, 6, 11, 0, 33), d)); - testST(SysTime(DateTime(-1999, 7, 6, 11, 59, 33), d), 0, SysTime(DateTime(-1999, 7, 6, 11, 59, 33), d)); - testST(SysTime(DateTime(-1999, 7, 6, 11, 59, 33), d), -1, SysTime(DateTime(-1999, 7, 6, 11, 58, 33), d)); - - testST(SysTime(DateTime(-1999, 7, 6, 0, 0, 33), d), 1, SysTime(DateTime(-1999, 7, 6, 0, 1, 33), d)); - testST(SysTime(DateTime(-1999, 7, 6, 0, 0, 33), d), 0, SysTime(DateTime(-1999, 7, 6, 0, 0, 33), d)); - testST(SysTime(DateTime(-1999, 7, 6, 0, 0, 33), d), -1, SysTime(DateTime(-1999, 7, 6, 0, 59, 33), d)); - - testST(SysTime(DateTime(-1999, 7, 5, 23, 59, 33), d), 1, SysTime(DateTime(-1999, 7, 5, 23, 0, 33), d)); - testST(SysTime(DateTime(-1999, 7, 5, 23, 59, 33), d), 0, SysTime(DateTime(-1999, 7, 5, 23, 59, 33), d)); - testST(SysTime(DateTime(-1999, 7, 5, 23, 59, 33), d), -1, SysTime(DateTime(-1999, 7, 5, 23, 58, 33), d)); - - testST(SysTime(DateTime(-2000, 12, 31, 23, 59, 33), d), 1, SysTime(DateTime(-2000, 12, 31, 23, 0, 33), d)); - testST(SysTime(DateTime(-2000, 12, 31, 23, 59, 33), d), 0, SysTime(DateTime(-2000, 12, 31, 23, 59, 33), d)); - testST(SysTime(DateTime(-2000, 12, 31, 23, 59, 33), d), -1, SysTime(DateTime(-2000, 12, 31, 23, 58, 33), d)); - - // Test Both - testST(SysTime(DateTime(1, 1, 1, 0, 0, 0)), -1, SysTime(DateTime(1, 1, 1, 0, 59, 0))); - testST(SysTime(DateTime(0, 12, 31, 23, 59, 0)), 1, SysTime(DateTime(0, 12, 31, 23, 0, 0))); - - testST(SysTime(DateTime(0, 1, 1, 0, 0, 0)), -1, SysTime(DateTime(0, 1, 1, 0, 59, 0))); - testST(SysTime(DateTime(-1, 12, 31, 23, 59, 0)), 1, SysTime(DateTime(-1, 12, 31, 23, 0, 0))); - - testST(SysTime(DateTime(-1, 1, 1, 11, 30, 33), d), 1_052_760, SysTime(DateTime(-1, 1, 1, 11, 30, 33), d)); - testST(SysTime(DateTime(1, 1, 1, 13, 30, 33), d), -1_052_760, SysTime(DateTime(1, 1, 1, 13, 30, 33), d)); - - testST(SysTime(DateTime(-1, 1, 1, 11, 30, 33), d), 1_052_782, SysTime(DateTime(-1, 1, 1, 11, 52, 33), d)); - testST(SysTime(DateTime(1, 1, 1, 13, 52, 33), d), -1_052_782, SysTime(DateTime(1, 1, 1, 13, 30, 33), d)); - - { - auto sysTime = SysTime(DateTime(1, 1, 1, 0, 0, 0)); - sysTime.roll!"minutes"(-1); - assert(sysTime == SysTime(DateTime(1, 1, 1, 0, 59, 0))); - sysTime.roll!"minutes"(1); - assert(sysTime == SysTime(DateTime(1, 1, 1, 0, 0, 0))); - } - - { - auto sysTime = SysTime(DateTime(1, 1, 1, 0, 0, 59), hnsecs(9_999_999)); - sysTime.roll!"minutes"(-1); - assert(sysTime == SysTime(DateTime(1, 1, 1, 0, 59, 59), hnsecs(9_999_999))); - sysTime.roll!"minutes"(1); - assert(sysTime == SysTime(DateTime(1, 1, 1, 0, 0, 59), hnsecs(9_999_999))); - } - - { - auto sysTime = SysTime(DateTime(0, 12, 31, 23, 59, 0)); - sysTime.roll!"minutes"(1); - assert(sysTime == SysTime(DateTime(0, 12, 31, 23, 0, 0))); - sysTime.roll!"minutes"(-1); - assert(sysTime == SysTime(DateTime(0, 12, 31, 23, 59, 0))); - } - - { - auto sysTime = SysTime(DateTime(0, 12, 31, 23, 59, 59), hnsecs(9_999_999)); - sysTime.roll!"minutes"(1); - assert(sysTime == SysTime(DateTime(0, 12, 31, 23, 0, 59), hnsecs(9_999_999))); - sysTime.roll!"minutes"(-1); - assert(sysTime == SysTime(DateTime(0, 12, 31, 23, 59, 59), hnsecs(9_999_999))); - } - - { - auto sysTime = SysTime(DateTime(0, 12, 31, 23, 59, 59), hnsecs(9_999_999)); - sysTime.roll!"minutes"(1).roll!"minutes"(-79); - assert(sysTime == SysTime(DateTime(0, 12, 31, 23, 41, 59), hnsecs(9_999_999))); - } - - const cst = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); - immutable ist = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); - static assert(!__traits(compiles, cst.roll!"minutes"(4))); - static assert(!__traits(compiles, ist.roll!"minutes"(4))); - - static void testScope(scope ref SysTime st) @safe - { - auto result = st.roll!"minutes"(42); - } - } - - // Test roll!"seconds"(). - @safe unittest - { - import core.time; - static void testST(SysTime orig, int seconds, SysTime expected, size_t line = __LINE__) @safe - { - orig.roll!"seconds"(seconds); - if (orig != expected) - throw new AssertError(format("Failed. actual [%s] != expected [%s]", orig, expected), __FILE__, line); - } - - // Test A.D. - immutable d = msecs(274); - auto beforeAD = SysTime(DateTime(1999, 7, 6, 12, 30, 33), d); - testST(beforeAD, 0, SysTime(DateTime(1999, 7, 6, 12, 30, 33), d)); - testST(beforeAD, 1, SysTime(DateTime(1999, 7, 6, 12, 30, 34), d)); - testST(beforeAD, 2, SysTime(DateTime(1999, 7, 6, 12, 30, 35), d)); - testST(beforeAD, 3, SysTime(DateTime(1999, 7, 6, 12, 30, 36), d)); - testST(beforeAD, 4, SysTime(DateTime(1999, 7, 6, 12, 30, 37), d)); - testST(beforeAD, 5, SysTime(DateTime(1999, 7, 6, 12, 30, 38), d)); - testST(beforeAD, 10, SysTime(DateTime(1999, 7, 6, 12, 30, 43), d)); - testST(beforeAD, 15, SysTime(DateTime(1999, 7, 6, 12, 30, 48), d)); - testST(beforeAD, 26, SysTime(DateTime(1999, 7, 6, 12, 30, 59), d)); - testST(beforeAD, 27, SysTime(DateTime(1999, 7, 6, 12, 30, 0), d)); - testST(beforeAD, 30, SysTime(DateTime(1999, 7, 6, 12, 30, 3), d)); - testST(beforeAD, 59, SysTime(DateTime(1999, 7, 6, 12, 30, 32), d)); - testST(beforeAD, 60, SysTime(DateTime(1999, 7, 6, 12, 30, 33), d)); - testST(beforeAD, 61, SysTime(DateTime(1999, 7, 6, 12, 30, 34), d)); - - testST(beforeAD, 1766, SysTime(DateTime(1999, 7, 6, 12, 30, 59), d)); - testST(beforeAD, 1767, SysTime(DateTime(1999, 7, 6, 12, 30, 0), d)); - testST(beforeAD, 1768, SysTime(DateTime(1999, 7, 6, 12, 30, 1), d)); - testST(beforeAD, 2007, SysTime(DateTime(1999, 7, 6, 12, 30, 0), d)); - testST(beforeAD, 3599, SysTime(DateTime(1999, 7, 6, 12, 30, 32), d)); - testST(beforeAD, 3600, SysTime(DateTime(1999, 7, 6, 12, 30, 33), d)); - testST(beforeAD, 3601, SysTime(DateTime(1999, 7, 6, 12, 30, 34), d)); - testST(beforeAD, 7200, SysTime(DateTime(1999, 7, 6, 12, 30, 33), d)); - - testST(beforeAD, -1, SysTime(DateTime(1999, 7, 6, 12, 30, 32), d)); - testST(beforeAD, -2, SysTime(DateTime(1999, 7, 6, 12, 30, 31), d)); - testST(beforeAD, -3, SysTime(DateTime(1999, 7, 6, 12, 30, 30), d)); - testST(beforeAD, -4, SysTime(DateTime(1999, 7, 6, 12, 30, 29), d)); - testST(beforeAD, -5, SysTime(DateTime(1999, 7, 6, 12, 30, 28), d)); - testST(beforeAD, -10, SysTime(DateTime(1999, 7, 6, 12, 30, 23), d)); - testST(beforeAD, -15, SysTime(DateTime(1999, 7, 6, 12, 30, 18), d)); - testST(beforeAD, -33, SysTime(DateTime(1999, 7, 6, 12, 30, 0), d)); - testST(beforeAD, -34, SysTime(DateTime(1999, 7, 6, 12, 30, 59), d)); - testST(beforeAD, -35, SysTime(DateTime(1999, 7, 6, 12, 30, 58), d)); - testST(beforeAD, -59, SysTime(DateTime(1999, 7, 6, 12, 30, 34), d)); - testST(beforeAD, -60, SysTime(DateTime(1999, 7, 6, 12, 30, 33), d)); - testST(beforeAD, -61, SysTime(DateTime(1999, 7, 6, 12, 30, 32), d)); - - testST(SysTime(DateTime(1999, 7, 6, 12, 30, 0), d), 1, SysTime(DateTime(1999, 7, 6, 12, 30, 1), d)); - testST(SysTime(DateTime(1999, 7, 6, 12, 30, 0), d), 0, SysTime(DateTime(1999, 7, 6, 12, 30, 0), d)); - testST(SysTime(DateTime(1999, 7, 6, 12, 30, 0), d), -1, SysTime(DateTime(1999, 7, 6, 12, 30, 59), d)); - - testST(SysTime(DateTime(1999, 7, 6, 12, 0, 0), d), 1, SysTime(DateTime(1999, 7, 6, 12, 0, 1), d)); - testST(SysTime(DateTime(1999, 7, 6, 12, 0, 0), d), 0, SysTime(DateTime(1999, 7, 6, 12, 0, 0), d)); - testST(SysTime(DateTime(1999, 7, 6, 12, 0, 0), d), -1, SysTime(DateTime(1999, 7, 6, 12, 0, 59), d)); - - testST(SysTime(DateTime(1999, 7, 6, 0, 0, 0), d), 1, SysTime(DateTime(1999, 7, 6, 0, 0, 1), d)); - testST(SysTime(DateTime(1999, 7, 6, 0, 0, 0), d), 0, SysTime(DateTime(1999, 7, 6, 0, 0, 0), d)); - testST(SysTime(DateTime(1999, 7, 6, 0, 0, 0), d), -1, SysTime(DateTime(1999, 7, 6, 0, 0, 59), d)); - - testST(SysTime(DateTime(1999, 7, 5, 23, 59, 59), d), 1, SysTime(DateTime(1999, 7, 5, 23, 59, 0), d)); - testST(SysTime(DateTime(1999, 7, 5, 23, 59, 59), d), 0, SysTime(DateTime(1999, 7, 5, 23, 59, 59), d)); - testST(SysTime(DateTime(1999, 7, 5, 23, 59, 59), d), -1, SysTime(DateTime(1999, 7, 5, 23, 59, 58), d)); - - testST(SysTime(DateTime(1998, 12, 31, 23, 59, 59), d), 1, SysTime(DateTime(1998, 12, 31, 23, 59, 0), d)); - testST(SysTime(DateTime(1998, 12, 31, 23, 59, 59), d), 0, SysTime(DateTime(1998, 12, 31, 23, 59, 59), d)); - testST(SysTime(DateTime(1998, 12, 31, 23, 59, 59), d), -1, SysTime(DateTime(1998, 12, 31, 23, 59, 58), d)); - - // Test B.C. - auto beforeBC = SysTime(DateTime(-1999, 7, 6, 12, 30, 33), d); - testST(beforeBC, 0, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), d)); - testST(beforeBC, 1, SysTime(DateTime(-1999, 7, 6, 12, 30, 34), d)); - testST(beforeBC, 2, SysTime(DateTime(-1999, 7, 6, 12, 30, 35), d)); - testST(beforeBC, 3, SysTime(DateTime(-1999, 7, 6, 12, 30, 36), d)); - testST(beforeBC, 4, SysTime(DateTime(-1999, 7, 6, 12, 30, 37), d)); - testST(beforeBC, 5, SysTime(DateTime(-1999, 7, 6, 12, 30, 38), d)); - testST(beforeBC, 10, SysTime(DateTime(-1999, 7, 6, 12, 30, 43), d)); - testST(beforeBC, 15, SysTime(DateTime(-1999, 7, 6, 12, 30, 48), d)); - testST(beforeBC, 26, SysTime(DateTime(-1999, 7, 6, 12, 30, 59), d)); - testST(beforeBC, 27, SysTime(DateTime(-1999, 7, 6, 12, 30, 0), d)); - testST(beforeBC, 30, SysTime(DateTime(-1999, 7, 6, 12, 30, 3), d)); - testST(beforeBC, 59, SysTime(DateTime(-1999, 7, 6, 12, 30, 32), d)); - testST(beforeBC, 60, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), d)); - testST(beforeBC, 61, SysTime(DateTime(-1999, 7, 6, 12, 30, 34), d)); - - testST(beforeBC, 1766, SysTime(DateTime(-1999, 7, 6, 12, 30, 59), d)); - testST(beforeBC, 1767, SysTime(DateTime(-1999, 7, 6, 12, 30, 0), d)); - testST(beforeBC, 1768, SysTime(DateTime(-1999, 7, 6, 12, 30, 1), d)); - testST(beforeBC, 2007, SysTime(DateTime(-1999, 7, 6, 12, 30, 0), d)); - testST(beforeBC, 3599, SysTime(DateTime(-1999, 7, 6, 12, 30, 32), d)); - testST(beforeBC, 3600, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), d)); - testST(beforeBC, 3601, SysTime(DateTime(-1999, 7, 6, 12, 30, 34), d)); - testST(beforeBC, 7200, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), d)); - - testST(beforeBC, -1, SysTime(DateTime(-1999, 7, 6, 12, 30, 32), d)); - testST(beforeBC, -2, SysTime(DateTime(-1999, 7, 6, 12, 30, 31), d)); - testST(beforeBC, -3, SysTime(DateTime(-1999, 7, 6, 12, 30, 30), d)); - testST(beforeBC, -4, SysTime(DateTime(-1999, 7, 6, 12, 30, 29), d)); - testST(beforeBC, -5, SysTime(DateTime(-1999, 7, 6, 12, 30, 28), d)); - testST(beforeBC, -10, SysTime(DateTime(-1999, 7, 6, 12, 30, 23), d)); - testST(beforeBC, -15, SysTime(DateTime(-1999, 7, 6, 12, 30, 18), d)); - testST(beforeBC, -33, SysTime(DateTime(-1999, 7, 6, 12, 30, 0), d)); - testST(beforeBC, -34, SysTime(DateTime(-1999, 7, 6, 12, 30, 59), d)); - testST(beforeBC, -35, SysTime(DateTime(-1999, 7, 6, 12, 30, 58), d)); - testST(beforeBC, -59, SysTime(DateTime(-1999, 7, 6, 12, 30, 34), d)); - testST(beforeBC, -60, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), d)); - testST(beforeBC, -61, SysTime(DateTime(-1999, 7, 6, 12, 30, 32), d)); - - testST(SysTime(DateTime(-1999, 7, 6, 12, 30, 0), d), 1, SysTime(DateTime(-1999, 7, 6, 12, 30, 1), d)); - testST(SysTime(DateTime(-1999, 7, 6, 12, 30, 0), d), 0, SysTime(DateTime(-1999, 7, 6, 12, 30, 0), d)); - testST(SysTime(DateTime(-1999, 7, 6, 12, 30, 0), d), -1, SysTime(DateTime(-1999, 7, 6, 12, 30, 59), d)); - - testST(SysTime(DateTime(-1999, 7, 6, 12, 0, 0), d), 1, SysTime(DateTime(-1999, 7, 6, 12, 0, 1), d)); - testST(SysTime(DateTime(-1999, 7, 6, 12, 0, 0), d), 0, SysTime(DateTime(-1999, 7, 6, 12, 0, 0), d)); - testST(SysTime(DateTime(-1999, 7, 6, 12, 0, 0), d), -1, SysTime(DateTime(-1999, 7, 6, 12, 0, 59), d)); - - testST(SysTime(DateTime(-1999, 7, 6, 0, 0, 0), d), 1, SysTime(DateTime(-1999, 7, 6, 0, 0, 1), d)); - testST(SysTime(DateTime(-1999, 7, 6, 0, 0, 0), d), 0, SysTime(DateTime(-1999, 7, 6, 0, 0, 0), d)); - testST(SysTime(DateTime(-1999, 7, 6, 0, 0, 0), d), -1, SysTime(DateTime(-1999, 7, 6, 0, 0, 59), d)); - - testST(SysTime(DateTime(-1999, 7, 5, 23, 59, 59), d), 1, SysTime(DateTime(-1999, 7, 5, 23, 59, 0), d)); - testST(SysTime(DateTime(-1999, 7, 5, 23, 59, 59), d), 0, SysTime(DateTime(-1999, 7, 5, 23, 59, 59), d)); - testST(SysTime(DateTime(-1999, 7, 5, 23, 59, 59), d), -1, SysTime(DateTime(-1999, 7, 5, 23, 59, 58), d)); - - testST(SysTime(DateTime(-2000, 12, 31, 23, 59, 59), d), 1, SysTime(DateTime(-2000, 12, 31, 23, 59, 0), d)); - testST(SysTime(DateTime(-2000, 12, 31, 23, 59, 59), d), 0, SysTime(DateTime(-2000, 12, 31, 23, 59, 59), d)); - testST(SysTime(DateTime(-2000, 12, 31, 23, 59, 59), d), -1, SysTime(DateTime(-2000, 12, 31, 23, 59, 58), d)); - - // Test Both - testST(SysTime(DateTime(1, 1, 1, 0, 0, 0), d), -1, SysTime(DateTime(1, 1, 1, 0, 0, 59), d)); - testST(SysTime(DateTime(0, 12, 31, 23, 59, 59), d), 1, SysTime(DateTime(0, 12, 31, 23, 59, 0), d)); - - testST(SysTime(DateTime(0, 1, 1, 0, 0, 0), d), -1, SysTime(DateTime(0, 1, 1, 0, 0, 59), d)); - testST(SysTime(DateTime(-1, 12, 31, 23, 59, 59), d), 1, SysTime(DateTime(-1, 12, 31, 23, 59, 0), d)); - - testST(SysTime(DateTime(-1, 1, 1, 11, 30, 33), d), 63_165_600L, SysTime(DateTime(-1, 1, 1, 11, 30, 33), d)); - testST(SysTime(DateTime(1, 1, 1, 13, 30, 33), d), -63_165_600L, SysTime(DateTime(1, 1, 1, 13, 30, 33), d)); - - testST(SysTime(DateTime(-1, 1, 1, 11, 30, 33), d), 63_165_617L, SysTime(DateTime(-1, 1, 1, 11, 30, 50), d)); - testST(SysTime(DateTime(1, 1, 1, 13, 30, 50), d), -63_165_617L, SysTime(DateTime(1, 1, 1, 13, 30, 33), d)); - - { - auto sysTime = SysTime(DateTime(1, 1, 1, 0, 0, 0)); - sysTime.roll!"seconds"(-1); - assert(sysTime == SysTime(DateTime(1, 1, 1, 0, 0, 59))); - sysTime.roll!"seconds"(1); - assert(sysTime == SysTime(DateTime(1, 1, 1, 0, 0, 0))); - } - - { - auto sysTime = SysTime(DateTime(1, 1, 1, 0, 0, 0), hnsecs(9_999_999)); - sysTime.roll!"seconds"(-1); - assert(sysTime == SysTime(DateTime(1, 1, 1, 0, 0, 59), hnsecs(9_999_999))); - sysTime.roll!"seconds"(1); - assert(sysTime == SysTime(DateTime(1, 1, 1, 0, 0, 0), hnsecs(9_999_999))); - } - - { - auto sysTime = SysTime(DateTime(0, 12, 31, 23, 59, 59)); - sysTime.roll!"seconds"(1); - assert(sysTime == SysTime(DateTime(0, 12, 31, 23, 59, 0))); - sysTime.roll!"seconds"(-1); - assert(sysTime == SysTime(DateTime(0, 12, 31, 23, 59, 59))); - } - - { - auto sysTime = SysTime(DateTime(0, 12, 31, 23, 59, 59), hnsecs(9_999_999)); - sysTime.roll!"seconds"(1); - assert(sysTime == SysTime(DateTime(0, 12, 31, 23, 59, 0), hnsecs(9_999_999))); - sysTime.roll!"seconds"(-1); - assert(sysTime == SysTime(DateTime(0, 12, 31, 23, 59, 59), hnsecs(9_999_999))); - } - - { - auto sysTime = SysTime(DateTime(0, 12, 31, 23, 59, 59), hnsecs(9_999_999)); - sysTime.roll!"seconds"(1).roll!"seconds"(-102); - assert(sysTime == SysTime(DateTime(0, 12, 31, 23, 59, 18), hnsecs(9_999_999))); - } - - const cst = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); - immutable ist = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); - static assert(!__traits(compiles, cst.roll!"seconds"(4))); - static assert(!__traits(compiles, ist.roll!"seconds"(4))); - - static void testScope(scope ref SysTime st) @safe - { - auto result = st.roll!"seconds"(42); - } - } - - - // Shares documentation with "days" version. - ref SysTime roll(string units)(long value) @safe nothrow scope - if (units == "msecs" || units == "usecs" || units == "hnsecs") - { - auto hnsecs = adjTime; - immutable days = splitUnitsFromHNSecs!"days"(hnsecs); - immutable negative = hnsecs < 0; - - if (negative) - hnsecs += convert!("hours", "hnsecs")(24); - - immutable seconds = splitUnitsFromHNSecs!"seconds"(hnsecs); - hnsecs += convert!(units, "hnsecs")(value); - hnsecs %= convert!("seconds", "hnsecs")(1); - - if (hnsecs < 0) - hnsecs += convert!("seconds", "hnsecs")(1); - hnsecs += convert!("seconds", "hnsecs")(seconds); - - if (negative) - hnsecs -= convert!("hours", "hnsecs")(24); - - immutable newDaysHNSecs = convert!("days", "hnsecs")(days); - adjTime = newDaysHNSecs + hnsecs; - return this; - } - - - // Test roll!"msecs"(). - @safe unittest - { - import core.time; - static void testST(SysTime orig, int milliseconds, SysTime expected, size_t line = __LINE__) @safe - { - orig.roll!"msecs"(milliseconds); - if (orig != expected) - throw new AssertError(format("Failed. actual [%s] != expected [%s]", orig, expected), __FILE__, line); - } - - // Test A.D. - auto beforeAD = SysTime(DateTime(1999, 7, 6, 12, 30, 33), msecs(274)); - testST(beforeAD, 0, SysTime(DateTime(1999, 7, 6, 12, 30, 33), msecs(274))); - testST(beforeAD, 1, SysTime(DateTime(1999, 7, 6, 12, 30, 33), msecs(275))); - testST(beforeAD, 2, SysTime(DateTime(1999, 7, 6, 12, 30, 33), msecs(276))); - testST(beforeAD, 10, SysTime(DateTime(1999, 7, 6, 12, 30, 33), msecs(284))); - testST(beforeAD, 100, SysTime(DateTime(1999, 7, 6, 12, 30, 33), msecs(374))); - testST(beforeAD, 725, SysTime(DateTime(1999, 7, 6, 12, 30, 33), msecs(999))); - testST(beforeAD, 726, SysTime(DateTime(1999, 7, 6, 12, 30, 33))); - testST(beforeAD, 1000, SysTime(DateTime(1999, 7, 6, 12, 30, 33), msecs(274))); - testST(beforeAD, 1001, SysTime(DateTime(1999, 7, 6, 12, 30, 33), msecs(275))); - testST(beforeAD, 2000, SysTime(DateTime(1999, 7, 6, 12, 30, 33), msecs(274))); - testST(beforeAD, 26_725, SysTime(DateTime(1999, 7, 6, 12, 30, 33), msecs(999))); - testST(beforeAD, 26_726, SysTime(DateTime(1999, 7, 6, 12, 30, 33))); - testST(beforeAD, 26_727, SysTime(DateTime(1999, 7, 6, 12, 30, 33), msecs(1))); - testST(beforeAD, 1_766_725, SysTime(DateTime(1999, 7, 6, 12, 30, 33), msecs(999))); - testST(beforeAD, 1_766_726, SysTime(DateTime(1999, 7, 6, 12, 30, 33))); - - testST(beforeAD, -1, SysTime(DateTime(1999, 7, 6, 12, 30, 33), msecs(273))); - testST(beforeAD, -2, SysTime(DateTime(1999, 7, 6, 12, 30, 33), msecs(272))); - testST(beforeAD, -10, SysTime(DateTime(1999, 7, 6, 12, 30, 33), msecs(264))); - testST(beforeAD, -100, SysTime(DateTime(1999, 7, 6, 12, 30, 33), msecs(174))); - testST(beforeAD, -274, SysTime(DateTime(1999, 7, 6, 12, 30, 33))); - testST(beforeAD, -275, SysTime(DateTime(1999, 7, 6, 12, 30, 33), msecs(999))); - testST(beforeAD, -1000, SysTime(DateTime(1999, 7, 6, 12, 30, 33), msecs(274))); - testST(beforeAD, -1001, SysTime(DateTime(1999, 7, 6, 12, 30, 33), msecs(273))); - testST(beforeAD, -2000, SysTime(DateTime(1999, 7, 6, 12, 30, 33), msecs(274))); - testST(beforeAD, -33_274, SysTime(DateTime(1999, 7, 6, 12, 30, 33))); - testST(beforeAD, -33_275, SysTime(DateTime(1999, 7, 6, 12, 30, 33), msecs(999))); - testST(beforeAD, -1_833_274, SysTime(DateTime(1999, 7, 6, 12, 30, 33))); - testST(beforeAD, -1_833_275, SysTime(DateTime(1999, 7, 6, 12, 30, 33), msecs(999))); - - // Test B.C. - auto beforeBC = SysTime(DateTime(-1999, 7, 6, 12, 30, 33), msecs(274)); - testST(beforeBC, 0, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), msecs(274))); - testST(beforeBC, 1, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), msecs(275))); - testST(beforeBC, 2, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), msecs(276))); - testST(beforeBC, 10, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), msecs(284))); - testST(beforeBC, 100, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), msecs(374))); - testST(beforeBC, 725, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), msecs(999))); - testST(beforeBC, 726, SysTime(DateTime(-1999, 7, 6, 12, 30, 33))); - testST(beforeBC, 1000, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), msecs(274))); - testST(beforeBC, 1001, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), msecs(275))); - testST(beforeBC, 2000, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), msecs(274))); - testST(beforeBC, 26_725, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), msecs(999))); - testST(beforeBC, 26_726, SysTime(DateTime(-1999, 7, 6, 12, 30, 33))); - testST(beforeBC, 26_727, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), msecs(1))); - testST(beforeBC, 1_766_725, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), msecs(999))); - testST(beforeBC, 1_766_726, SysTime(DateTime(-1999, 7, 6, 12, 30, 33))); - - testST(beforeBC, -1, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), msecs(273))); - testST(beforeBC, -2, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), msecs(272))); - testST(beforeBC, -10, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), msecs(264))); - testST(beforeBC, -100, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), msecs(174))); - testST(beforeBC, -274, SysTime(DateTime(-1999, 7, 6, 12, 30, 33))); - testST(beforeBC, -275, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), msecs(999))); - testST(beforeBC, -1000, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), msecs(274))); - testST(beforeBC, -1001, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), msecs(273))); - testST(beforeBC, -2000, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), msecs(274))); - testST(beforeBC, -33_274, SysTime(DateTime(-1999, 7, 6, 12, 30, 33))); - testST(beforeBC, -33_275, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), msecs(999))); - testST(beforeBC, -1_833_274, SysTime(DateTime(-1999, 7, 6, 12, 30, 33))); - testST(beforeBC, -1_833_275, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), msecs(999))); - - // Test Both - auto beforeBoth1 = SysTime(DateTime(1, 1, 1, 0, 0, 0)); - testST(beforeBoth1, 1, SysTime(DateTime(1, 1, 1, 0, 0, 0), msecs(1))); - testST(beforeBoth1, 0, SysTime(DateTime(1, 1, 1, 0, 0, 0))); - testST(beforeBoth1, -1, SysTime(DateTime(1, 1, 1, 0, 0, 0), msecs(999))); - testST(beforeBoth1, -2, SysTime(DateTime(1, 1, 1, 0, 0, 0), msecs(998))); - testST(beforeBoth1, -1000, SysTime(DateTime(1, 1, 1, 0, 0, 0))); - testST(beforeBoth1, -2000, SysTime(DateTime(1, 1, 1, 0, 0, 0))); - testST(beforeBoth1, -2555, SysTime(DateTime(1, 1, 1, 0, 0, 0), msecs(445))); - - auto beforeBoth2 = SysTime(DateTime(0, 12, 31, 23, 59, 59), hnsecs(9_999_999)); - testST(beforeBoth2, -1, SysTime(DateTime(0, 12, 31, 23, 59, 59), hnsecs(9_989_999))); - testST(beforeBoth2, 0, SysTime(DateTime(0, 12, 31, 23, 59, 59), hnsecs(9_999_999))); - testST(beforeBoth2, 1, SysTime(DateTime(0, 12, 31, 23, 59, 59), hnsecs(9999))); - testST(beforeBoth2, 2, SysTime(DateTime(0, 12, 31, 23, 59, 59), hnsecs(19_999))); - testST(beforeBoth2, 1000, SysTime(DateTime(0, 12, 31, 23, 59, 59), hnsecs(9_999_999))); - testST(beforeBoth2, 2000, SysTime(DateTime(0, 12, 31, 23, 59, 59), hnsecs(9_999_999))); - testST(beforeBoth2, 2555, SysTime(DateTime(0, 12, 31, 23, 59, 59), hnsecs(5_549_999))); - - { - auto st = SysTime(DateTime(0, 12, 31, 23, 59, 59), hnsecs(9_999_999)); - st.roll!"msecs"(1202).roll!"msecs"(-703); - assert(st == SysTime(DateTime(0, 12, 31, 23, 59, 59), hnsecs(4_989_999))); - } - - const cst = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); - immutable ist = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); - static assert(!__traits(compiles, cst.roll!"msecs"(4))); - static assert(!__traits(compiles, ist.roll!"msecs"(4))); - - static void testScope(scope ref SysTime st) @safe - { - auto result = st.roll!"msecs"(42); - } - } - - // Test roll!"usecs"(). - @safe unittest - { - import core.time; - static void testST(SysTime orig, long microseconds, SysTime expected, size_t line = __LINE__) @safe - { - orig.roll!"usecs"(microseconds); - if (orig != expected) - throw new AssertError(format("Failed. actual [%s] != expected [%s]", orig, expected), __FILE__, line); - } - - // Test A.D. - auto beforeAD = SysTime(DateTime(1999, 7, 6, 12, 30, 33), usecs(274)); - testST(beforeAD, 0, SysTime(DateTime(1999, 7, 6, 12, 30, 33), usecs(274))); - testST(beforeAD, 1, SysTime(DateTime(1999, 7, 6, 12, 30, 33), usecs(275))); - testST(beforeAD, 2, SysTime(DateTime(1999, 7, 6, 12, 30, 33), usecs(276))); - testST(beforeAD, 10, SysTime(DateTime(1999, 7, 6, 12, 30, 33), usecs(284))); - testST(beforeAD, 100, SysTime(DateTime(1999, 7, 6, 12, 30, 33), usecs(374))); - testST(beforeAD, 725, SysTime(DateTime(1999, 7, 6, 12, 30, 33), usecs(999))); - testST(beforeAD, 726, SysTime(DateTime(1999, 7, 6, 12, 30, 33), usecs(1000))); - testST(beforeAD, 1000, SysTime(DateTime(1999, 7, 6, 12, 30, 33), usecs(1274))); - testST(beforeAD, 1001, SysTime(DateTime(1999, 7, 6, 12, 30, 33), usecs(1275))); - testST(beforeAD, 2000, SysTime(DateTime(1999, 7, 6, 12, 30, 33), usecs(2274))); - testST(beforeAD, 26_725, SysTime(DateTime(1999, 7, 6, 12, 30, 33), usecs(26_999))); - testST(beforeAD, 26_726, SysTime(DateTime(1999, 7, 6, 12, 30, 33), usecs(27_000))); - testST(beforeAD, 26_727, SysTime(DateTime(1999, 7, 6, 12, 30, 33), usecs(27_001))); - testST(beforeAD, 1_766_725, SysTime(DateTime(1999, 7, 6, 12, 30, 33), usecs(766_999))); - testST(beforeAD, 1_766_726, SysTime(DateTime(1999, 7, 6, 12, 30, 33), usecs(767_000))); - testST(beforeAD, 1_000_000, SysTime(DateTime(1999, 7, 6, 12, 30, 33), usecs(274))); - testST(beforeAD, 60_000_000L, SysTime(DateTime(1999, 7, 6, 12, 30, 33), usecs(274))); - testST(beforeAD, 3_600_000_000L, SysTime(DateTime(1999, 7, 6, 12, 30, 33), usecs(274))); - - testST(beforeAD, -1, SysTime(DateTime(1999, 7, 6, 12, 30, 33), usecs(273))); - testST(beforeAD, -2, SysTime(DateTime(1999, 7, 6, 12, 30, 33), usecs(272))); - testST(beforeAD, -10, SysTime(DateTime(1999, 7, 6, 12, 30, 33), usecs(264))); - testST(beforeAD, -100, SysTime(DateTime(1999, 7, 6, 12, 30, 33), usecs(174))); - testST(beforeAD, -274, SysTime(DateTime(1999, 7, 6, 12, 30, 33))); - testST(beforeAD, -275, SysTime(DateTime(1999, 7, 6, 12, 30, 33), usecs(999_999))); - testST(beforeAD, -1000, SysTime(DateTime(1999, 7, 6, 12, 30, 33), usecs(999_274))); - testST(beforeAD, -1001, SysTime(DateTime(1999, 7, 6, 12, 30, 33), usecs(999_273))); - testST(beforeAD, -2000, SysTime(DateTime(1999, 7, 6, 12, 30, 33), usecs(998_274))); - testST(beforeAD, -33_274, SysTime(DateTime(1999, 7, 6, 12, 30, 33), usecs(967_000))); - testST(beforeAD, -33_275, SysTime(DateTime(1999, 7, 6, 12, 30, 33), usecs(966_999))); - testST(beforeAD, -1_833_274, SysTime(DateTime(1999, 7, 6, 12, 30, 33), usecs(167_000))); - testST(beforeAD, -1_833_275, SysTime(DateTime(1999, 7, 6, 12, 30, 33), usecs(166_999))); - testST(beforeAD, -1_000_000, SysTime(DateTime(1999, 7, 6, 12, 30, 33), usecs(274))); - testST(beforeAD, -60_000_000L, SysTime(DateTime(1999, 7, 6, 12, 30, 33), usecs(274))); - testST(beforeAD, -3_600_000_000L, SysTime(DateTime(1999, 7, 6, 12, 30, 33), usecs(274))); - - // Test B.C. - auto beforeBC = SysTime(DateTime(-1999, 7, 6, 12, 30, 33), usecs(274)); - testST(beforeBC, 0, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), usecs(274))); - testST(beforeBC, 1, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), usecs(275))); - testST(beforeBC, 2, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), usecs(276))); - testST(beforeBC, 10, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), usecs(284))); - testST(beforeBC, 100, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), usecs(374))); - testST(beforeBC, 725, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), usecs(999))); - testST(beforeBC, 726, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), usecs(1000))); - testST(beforeBC, 1000, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), usecs(1274))); - testST(beforeBC, 1001, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), usecs(1275))); - testST(beforeBC, 2000, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), usecs(2274))); - testST(beforeBC, 26_725, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), usecs(26_999))); - testST(beforeBC, 26_726, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), usecs(27_000))); - testST(beforeBC, 26_727, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), usecs(27_001))); - testST(beforeBC, 1_766_725, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), usecs(766_999))); - testST(beforeBC, 1_766_726, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), usecs(767_000))); - testST(beforeBC, 1_000_000, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), usecs(274))); - testST(beforeBC, 60_000_000L, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), usecs(274))); - testST(beforeBC, 3_600_000_000L, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), usecs(274))); - - testST(beforeBC, -1, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), usecs(273))); - testST(beforeBC, -2, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), usecs(272))); - testST(beforeBC, -10, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), usecs(264))); - testST(beforeBC, -100, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), usecs(174))); - testST(beforeBC, -274, SysTime(DateTime(-1999, 7, 6, 12, 30, 33))); - testST(beforeBC, -275, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), usecs(999_999))); - testST(beforeBC, -1000, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), usecs(999_274))); - testST(beforeBC, -1001, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), usecs(999_273))); - testST(beforeBC, -2000, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), usecs(998_274))); - testST(beforeBC, -33_274, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), usecs(967_000))); - testST(beforeBC, -33_275, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), usecs(966_999))); - testST(beforeBC, -1_833_274, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), usecs(167_000))); - testST(beforeBC, -1_833_275, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), usecs(166_999))); - testST(beforeBC, -1_000_000, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), usecs(274))); - testST(beforeBC, -60_000_000L, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), usecs(274))); - testST(beforeBC, -3_600_000_000L, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), usecs(274))); - - // Test Both - auto beforeBoth1 = SysTime(DateTime(1, 1, 1, 0, 0, 0)); - testST(beforeBoth1, 1, SysTime(DateTime(1, 1, 1, 0, 0, 0), usecs(1))); - testST(beforeBoth1, 0, SysTime(DateTime(1, 1, 1, 0, 0, 0))); - testST(beforeBoth1, -1, SysTime(DateTime(1, 1, 1, 0, 0, 0), usecs(999_999))); - testST(beforeBoth1, -2, SysTime(DateTime(1, 1, 1, 0, 0, 0), usecs(999_998))); - testST(beforeBoth1, -1000, SysTime(DateTime(1, 1, 1, 0, 0, 0), usecs(999_000))); - testST(beforeBoth1, -2000, SysTime(DateTime(1, 1, 1, 0, 0, 0), usecs(998_000))); - testST(beforeBoth1, -2555, SysTime(DateTime(1, 1, 1, 0, 0, 0), usecs(997_445))); - testST(beforeBoth1, -1_000_000, SysTime(DateTime(1, 1, 1, 0, 0, 0))); - testST(beforeBoth1, -2_000_000, SysTime(DateTime(1, 1, 1, 0, 0, 0))); - testST(beforeBoth1, -2_333_333, SysTime(DateTime(1, 1, 1, 0, 0, 0), usecs(666_667))); - - auto beforeBoth2 = SysTime(DateTime(0, 12, 31, 23, 59, 59), hnsecs(9_999_999)); - testST(beforeBoth2, -1, SysTime(DateTime(0, 12, 31, 23, 59, 59), hnsecs(9_999_989))); - testST(beforeBoth2, 0, SysTime(DateTime(0, 12, 31, 23, 59, 59), hnsecs(9_999_999))); - testST(beforeBoth2, 1, SysTime(DateTime(0, 12, 31, 23, 59, 59), hnsecs(9))); - testST(beforeBoth2, 2, SysTime(DateTime(0, 12, 31, 23, 59, 59), hnsecs(19))); - testST(beforeBoth2, 1000, SysTime(DateTime(0, 12, 31, 23, 59, 59), hnsecs(9999))); - testST(beforeBoth2, 2000, SysTime(DateTime(0, 12, 31, 23, 59, 59), hnsecs(19_999))); - testST(beforeBoth2, 2555, SysTime(DateTime(0, 12, 31, 23, 59, 59), hnsecs(25_549))); - testST(beforeBoth2, 1_000_000, SysTime(DateTime(0, 12, 31, 23, 59, 59), hnsecs(9_999_999))); - testST(beforeBoth2, 2_000_000, SysTime(DateTime(0, 12, 31, 23, 59, 59), hnsecs(9_999_999))); - testST(beforeBoth2, 2_333_333, SysTime(DateTime(0, 12, 31, 23, 59, 59), hnsecs(3_333_329))); - - { - auto st = SysTime(DateTime(0, 12, 31, 23, 59, 59), hnsecs(9_999_999)); - st.roll!"usecs"(9_020_027); - assert(st == SysTime(DateTime(0, 12, 31, 23, 59, 59), hnsecs(200_269))); - } - - { - auto st = SysTime(DateTime(0, 12, 31, 23, 59, 59), hnsecs(9_999_999)); - st.roll!"usecs"(9_020_027).roll!"usecs"(-70_034); - assert(st == SysTime(DateTime(0, 12, 31, 23, 59, 59), hnsecs(9_499_929))); - } - - const cst = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); - immutable ist = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); - static assert(!__traits(compiles, cst.roll!"usecs"(4))); - static assert(!__traits(compiles, ist.roll!"usecs"(4))); - - static void testScope(scope ref SysTime st) @safe - { - auto result = st.roll!"usecs"(42); - } - } - - // Test roll!"hnsecs"(). - @safe unittest - { - import core.time; - static void testST(SysTime orig, long hnsecs, SysTime expected, size_t line = __LINE__) @safe - { - orig.roll!"hnsecs"(hnsecs); - if (orig != expected) - throw new AssertError(format("Failed. actual [%s] != expected [%s]", orig, expected), __FILE__, line); - } - - // Test A.D. - auto dtAD = DateTime(1999, 7, 6, 12, 30, 33); - auto beforeAD = SysTime(dtAD, hnsecs(274)); - testST(beforeAD, 0, SysTime(dtAD, hnsecs(274))); - testST(beforeAD, 1, SysTime(dtAD, hnsecs(275))); - testST(beforeAD, 2, SysTime(dtAD, hnsecs(276))); - testST(beforeAD, 10, SysTime(dtAD, hnsecs(284))); - testST(beforeAD, 100, SysTime(dtAD, hnsecs(374))); - testST(beforeAD, 725, SysTime(dtAD, hnsecs(999))); - testST(beforeAD, 726, SysTime(dtAD, hnsecs(1000))); - testST(beforeAD, 1000, SysTime(dtAD, hnsecs(1274))); - testST(beforeAD, 1001, SysTime(dtAD, hnsecs(1275))); - testST(beforeAD, 2000, SysTime(dtAD, hnsecs(2274))); - testST(beforeAD, 26_725, SysTime(dtAD, hnsecs(26_999))); - testST(beforeAD, 26_726, SysTime(dtAD, hnsecs(27_000))); - testST(beforeAD, 26_727, SysTime(dtAD, hnsecs(27_001))); - testST(beforeAD, 1_766_725, SysTime(dtAD, hnsecs(1_766_999))); - testST(beforeAD, 1_766_726, SysTime(dtAD, hnsecs(1_767_000))); - testST(beforeAD, 1_000_000, SysTime(dtAD, hnsecs(1_000_274))); - testST(beforeAD, 60_000_000L, SysTime(dtAD, hnsecs(274))); - testST(beforeAD, 3_600_000_000L, SysTime(dtAD, hnsecs(274))); - testST(beforeAD, 600_000_000L, SysTime(dtAD, hnsecs(274))); - testST(beforeAD, 36_000_000_000L, SysTime(dtAD, hnsecs(274))); - - testST(beforeAD, -1, SysTime(dtAD, hnsecs(273))); - testST(beforeAD, -2, SysTime(dtAD, hnsecs(272))); - testST(beforeAD, -10, SysTime(dtAD, hnsecs(264))); - testST(beforeAD, -100, SysTime(dtAD, hnsecs(174))); - testST(beforeAD, -274, SysTime(dtAD)); - testST(beforeAD, -275, SysTime(dtAD, hnsecs(9_999_999))); - testST(beforeAD, -1000, SysTime(dtAD, hnsecs(9_999_274))); - testST(beforeAD, -1001, SysTime(dtAD, hnsecs(9_999_273))); - testST(beforeAD, -2000, SysTime(dtAD, hnsecs(9_998_274))); - testST(beforeAD, -33_274, SysTime(dtAD, hnsecs(9_967_000))); - testST(beforeAD, -33_275, SysTime(dtAD, hnsecs(9_966_999))); - testST(beforeAD, -1_833_274, SysTime(dtAD, hnsecs(8_167_000))); - testST(beforeAD, -1_833_275, SysTime(dtAD, hnsecs(8_166_999))); - testST(beforeAD, -1_000_000, SysTime(dtAD, hnsecs(9_000_274))); - testST(beforeAD, -60_000_000L, SysTime(dtAD, hnsecs(274))); - testST(beforeAD, -3_600_000_000L, SysTime(dtAD, hnsecs(274))); - testST(beforeAD, -600_000_000L, SysTime(dtAD, hnsecs(274))); - testST(beforeAD, -36_000_000_000L, SysTime(dtAD, hnsecs(274))); - - // Test B.C. - auto dtBC = DateTime(-1999, 7, 6, 12, 30, 33); - auto beforeBC = SysTime(dtBC, hnsecs(274)); - testST(beforeBC, 0, SysTime(dtBC, hnsecs(274))); - testST(beforeBC, 1, SysTime(dtBC, hnsecs(275))); - testST(beforeBC, 2, SysTime(dtBC, hnsecs(276))); - testST(beforeBC, 10, SysTime(dtBC, hnsecs(284))); - testST(beforeBC, 100, SysTime(dtBC, hnsecs(374))); - testST(beforeBC, 725, SysTime(dtBC, hnsecs(999))); - testST(beforeBC, 726, SysTime(dtBC, hnsecs(1000))); - testST(beforeBC, 1000, SysTime(dtBC, hnsecs(1274))); - testST(beforeBC, 1001, SysTime(dtBC, hnsecs(1275))); - testST(beforeBC, 2000, SysTime(dtBC, hnsecs(2274))); - testST(beforeBC, 26_725, SysTime(dtBC, hnsecs(26_999))); - testST(beforeBC, 26_726, SysTime(dtBC, hnsecs(27_000))); - testST(beforeBC, 26_727, SysTime(dtBC, hnsecs(27_001))); - testST(beforeBC, 1_766_725, SysTime(dtBC, hnsecs(1_766_999))); - testST(beforeBC, 1_766_726, SysTime(dtBC, hnsecs(1_767_000))); - testST(beforeBC, 1_000_000, SysTime(dtBC, hnsecs(1_000_274))); - testST(beforeBC, 60_000_000L, SysTime(dtBC, hnsecs(274))); - testST(beforeBC, 3_600_000_000L, SysTime(dtBC, hnsecs(274))); - testST(beforeBC, 600_000_000L, SysTime(dtBC, hnsecs(274))); - testST(beforeBC, 36_000_000_000L, SysTime(dtBC, hnsecs(274))); - - testST(beforeBC, -1, SysTime(dtBC, hnsecs(273))); - testST(beforeBC, -2, SysTime(dtBC, hnsecs(272))); - testST(beforeBC, -10, SysTime(dtBC, hnsecs(264))); - testST(beforeBC, -100, SysTime(dtBC, hnsecs(174))); - testST(beforeBC, -274, SysTime(dtBC)); - testST(beforeBC, -275, SysTime(dtBC, hnsecs(9_999_999))); - testST(beforeBC, -1000, SysTime(dtBC, hnsecs(9_999_274))); - testST(beforeBC, -1001, SysTime(dtBC, hnsecs(9_999_273))); - testST(beforeBC, -2000, SysTime(dtBC, hnsecs(9_998_274))); - testST(beforeBC, -33_274, SysTime(dtBC, hnsecs(9_967_000))); - testST(beforeBC, -33_275, SysTime(dtBC, hnsecs(9_966_999))); - testST(beforeBC, -1_833_274, SysTime(dtBC, hnsecs(8_167_000))); - testST(beforeBC, -1_833_275, SysTime(dtBC, hnsecs(8_166_999))); - testST(beforeBC, -1_000_000, SysTime(dtBC, hnsecs(9_000_274))); - testST(beforeBC, -60_000_000L, SysTime(dtBC, hnsecs(274))); - testST(beforeBC, -3_600_000_000L, SysTime(dtBC, hnsecs(274))); - testST(beforeBC, -600_000_000L, SysTime(dtBC, hnsecs(274))); - testST(beforeBC, -36_000_000_000L, SysTime(dtBC, hnsecs(274))); - - // Test Both - auto dtBoth1 = DateTime(1, 1, 1, 0, 0, 0); - auto beforeBoth1 = SysTime(dtBoth1); - testST(beforeBoth1, 1, SysTime(dtBoth1, hnsecs(1))); - testST(beforeBoth1, 0, SysTime(dtBoth1)); - testST(beforeBoth1, -1, SysTime(dtBoth1, hnsecs(9_999_999))); - testST(beforeBoth1, -2, SysTime(dtBoth1, hnsecs(9_999_998))); - testST(beforeBoth1, -1000, SysTime(dtBoth1, hnsecs(9_999_000))); - testST(beforeBoth1, -2000, SysTime(dtBoth1, hnsecs(9_998_000))); - testST(beforeBoth1, -2555, SysTime(dtBoth1, hnsecs(9_997_445))); - testST(beforeBoth1, -1_000_000, SysTime(dtBoth1, hnsecs(9_000_000))); - testST(beforeBoth1, -2_000_000, SysTime(dtBoth1, hnsecs(8_000_000))); - testST(beforeBoth1, -2_333_333, SysTime(dtBoth1, hnsecs(7_666_667))); - testST(beforeBoth1, -10_000_000, SysTime(dtBoth1)); - testST(beforeBoth1, -20_000_000, SysTime(dtBoth1)); - testST(beforeBoth1, -20_888_888, SysTime(dtBoth1, hnsecs(9_111_112))); - - auto dtBoth2 = DateTime(0, 12, 31, 23, 59, 59); - auto beforeBoth2 = SysTime(dtBoth2, hnsecs(9_999_999)); - testST(beforeBoth2, -1, SysTime(dtBoth2, hnsecs(9_999_998))); - testST(beforeBoth2, 0, SysTime(dtBoth2, hnsecs(9_999_999))); - testST(beforeBoth2, 1, SysTime(dtBoth2)); - testST(beforeBoth2, 2, SysTime(dtBoth2, hnsecs(1))); - testST(beforeBoth2, 1000, SysTime(dtBoth2, hnsecs(999))); - testST(beforeBoth2, 2000, SysTime(dtBoth2, hnsecs(1999))); - testST(beforeBoth2, 2555, SysTime(dtBoth2, hnsecs(2554))); - testST(beforeBoth2, 1_000_000, SysTime(dtBoth2, hnsecs(999_999))); - testST(beforeBoth2, 2_000_000, SysTime(dtBoth2, hnsecs(1_999_999))); - testST(beforeBoth2, 2_333_333, SysTime(dtBoth2, hnsecs(2_333_332))); - testST(beforeBoth2, 10_000_000, SysTime(dtBoth2, hnsecs(9_999_999))); - testST(beforeBoth2, 20_000_000, SysTime(dtBoth2, hnsecs(9_999_999))); - testST(beforeBoth2, 20_888_888, SysTime(dtBoth2, hnsecs(888_887))); - - { - auto st = SysTime(dtBoth2, hnsecs(9_999_999)); - st.roll!"hnsecs"(70_777_222).roll!"hnsecs"(-222_555_292); - assert(st == SysTime(dtBoth2, hnsecs(8_221_929))); - } - - const cst = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); - immutable ist = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); - static assert(!__traits(compiles, cst.roll!"hnsecs"(4))); - static assert(!__traits(compiles, ist.roll!"hnsecs"(4))); - - static void testScope(scope ref SysTime st) @safe - { - auto result = st.roll!"hnsecs"(42); - } - } - - - /++ - Gives the result of adding or subtracting a $(REF Duration, core,time) - from this $(LREF SysTime). - - The legal types of arithmetic for $(LREF SysTime) using this operator - are - - $(BOOKTABLE, - $(TR $(TD SysTime) $(TD +) $(TD Duration) $(TD -->) $(TD SysTime)) - $(TR $(TD SysTime) $(TD -) $(TD Duration) $(TD -->) $(TD SysTime)) - ) - - Params: - duration = The $(REF Duration, core,time) to add to or subtract from - this $(LREF SysTime). - +/ - SysTime opBinary(string op)(Duration duration) @safe const pure nothrow return scope - if (op == "+" || op == "-") - { - SysTime retval = SysTime(this._stdTime, this._timezone); - immutable hnsecs = duration.total!"hnsecs"; - mixin("retval._stdTime " ~ op ~ "= hnsecs;"); - return retval; - } - - /// - @safe unittest - { - import core.time : hours, seconds; - import std.datetime.date : DateTime; - - assert(SysTime(DateTime(2015, 12, 31, 23, 59, 59)) + seconds(1) == - SysTime(DateTime(2016, 1, 1, 0, 0, 0))); - - assert(SysTime(DateTime(2015, 12, 31, 23, 59, 59)) + hours(1) == - SysTime(DateTime(2016, 1, 1, 0, 59, 59))); - - assert(SysTime(DateTime(2016, 1, 1, 0, 0, 0)) - seconds(1) == - SysTime(DateTime(2015, 12, 31, 23, 59, 59))); - - assert(SysTime(DateTime(2016, 1, 1, 0, 59, 59)) - hours(1) == - SysTime(DateTime(2015, 12, 31, 23, 59, 59))); - } - - @safe unittest - { - import core.time; - auto st = SysTime(DateTime(1999, 7, 6, 12, 30, 33), hnsecs(2_345_678)); - - assert(st + dur!"weeks"(7) == SysTime(DateTime(1999, 8, 24, 12, 30, 33), hnsecs(2_345_678))); - assert(st + dur!"weeks"(-7) == SysTime(DateTime(1999, 5, 18, 12, 30, 33), hnsecs(2_345_678))); - assert(st + dur!"days"(7) == SysTime(DateTime(1999, 7, 13, 12, 30, 33), hnsecs(2_345_678))); - assert(st + dur!"days"(-7) == SysTime(DateTime(1999, 6, 29, 12, 30, 33), hnsecs(2_345_678))); - assert(st + dur!"hours"(7) == SysTime(DateTime(1999, 7, 6, 19, 30, 33), hnsecs(2_345_678))); - assert(st + dur!"hours"(-7) == SysTime(DateTime(1999, 7, 6, 5, 30, 33), hnsecs(2_345_678))); - assert(st + dur!"minutes"(7) == SysTime(DateTime(1999, 7, 6, 12, 37, 33), hnsecs(2_345_678))); - assert(st + dur!"minutes"(-7) == SysTime(DateTime(1999, 7, 6, 12, 23, 33), hnsecs(2_345_678))); - assert(st + dur!"seconds"(7) == SysTime(DateTime(1999, 7, 6, 12, 30, 40), hnsecs(2_345_678))); - assert(st + dur!"seconds"(-7) == SysTime(DateTime(1999, 7, 6, 12, 30, 26), hnsecs(2_345_678))); - assert(st + dur!"msecs"(7) == SysTime(DateTime(1999, 7, 6, 12, 30, 33), hnsecs(2_415_678))); - assert(st + dur!"msecs"(-7) == SysTime(DateTime(1999, 7, 6, 12, 30, 33), hnsecs(2_275_678))); - assert(st + dur!"usecs"(7) == SysTime(DateTime(1999, 7, 6, 12, 30, 33), hnsecs(2_345_748))); - assert(st + dur!"usecs"(-7) == SysTime(DateTime(1999, 7, 6, 12, 30, 33), hnsecs(2_345_608))); - assert(st + dur!"hnsecs"(7) == SysTime(DateTime(1999, 7, 6, 12, 30, 33), hnsecs(2_345_685))); - assert(st + dur!"hnsecs"(-7) == SysTime(DateTime(1999, 7, 6, 12, 30, 33), hnsecs(2_345_671))); - - assert(st - dur!"weeks"(-7) == SysTime(DateTime(1999, 8, 24, 12, 30, 33), hnsecs(2_345_678))); - assert(st - dur!"weeks"(7) == SysTime(DateTime(1999, 5, 18, 12, 30, 33), hnsecs(2_345_678))); - assert(st - dur!"days"(-7) == SysTime(DateTime(1999, 7, 13, 12, 30, 33), hnsecs(2_345_678))); - assert(st - dur!"days"(7) == SysTime(DateTime(1999, 6, 29, 12, 30, 33), hnsecs(2_345_678))); - assert(st - dur!"hours"(-7) == SysTime(DateTime(1999, 7, 6, 19, 30, 33), hnsecs(2_345_678))); - assert(st - dur!"hours"(7) == SysTime(DateTime(1999, 7, 6, 5, 30, 33), hnsecs(2_345_678))); - assert(st - dur!"minutes"(-7) == SysTime(DateTime(1999, 7, 6, 12, 37, 33), hnsecs(2_345_678))); - assert(st - dur!"minutes"(7) == SysTime(DateTime(1999, 7, 6, 12, 23, 33), hnsecs(2_345_678))); - assert(st - dur!"seconds"(-7) == SysTime(DateTime(1999, 7, 6, 12, 30, 40), hnsecs(2_345_678))); - assert(st - dur!"seconds"(7) == SysTime(DateTime(1999, 7, 6, 12, 30, 26), hnsecs(2_345_678))); - assert(st - dur!"msecs"(-7) == SysTime(DateTime(1999, 7, 6, 12, 30, 33), hnsecs(2_415_678))); - assert(st - dur!"msecs"(7) == SysTime(DateTime(1999, 7, 6, 12, 30, 33), hnsecs(2_275_678))); - assert(st - dur!"usecs"(-7) == SysTime(DateTime(1999, 7, 6, 12, 30, 33), hnsecs(2_345_748))); - assert(st - dur!"usecs"(7) == SysTime(DateTime(1999, 7, 6, 12, 30, 33), hnsecs(2_345_608))); - assert(st - dur!"hnsecs"(-7) == SysTime(DateTime(1999, 7, 6, 12, 30, 33), hnsecs(2_345_685))); - assert(st - dur!"hnsecs"(7) == SysTime(DateTime(1999, 7, 6, 12, 30, 33), hnsecs(2_345_671))); - - static void testST(SysTime orig, long hnsecs, SysTime expected, size_t line = __LINE__) @safe - { - auto result = orig + dur!"hnsecs"(hnsecs); - if (result != expected) - throw new AssertError(format("Failed. actual [%s] != expected [%s]", result, expected), __FILE__, line); - } - - // Test A.D. - auto beforeAD = SysTime(DateTime(1999, 7, 6, 12, 30, 33), hnsecs(274)); - testST(beforeAD, 0, SysTime(DateTime(1999, 7, 6, 12, 30, 33), hnsecs(274))); - testST(beforeAD, 1, SysTime(DateTime(1999, 7, 6, 12, 30, 33), hnsecs(275))); - testST(beforeAD, 2, SysTime(DateTime(1999, 7, 6, 12, 30, 33), hnsecs(276))); - testST(beforeAD, 10, SysTime(DateTime(1999, 7, 6, 12, 30, 33), hnsecs(284))); - testST(beforeAD, 100, SysTime(DateTime(1999, 7, 6, 12, 30, 33), hnsecs(374))); - testST(beforeAD, 725, SysTime(DateTime(1999, 7, 6, 12, 30, 33), hnsecs(999))); - testST(beforeAD, 726, SysTime(DateTime(1999, 7, 6, 12, 30, 33), hnsecs(1000))); - testST(beforeAD, 1000, SysTime(DateTime(1999, 7, 6, 12, 30, 33), hnsecs(1274))); - testST(beforeAD, 1001, SysTime(DateTime(1999, 7, 6, 12, 30, 33), hnsecs(1275))); - testST(beforeAD, 2000, SysTime(DateTime(1999, 7, 6, 12, 30, 33), hnsecs(2274))); - testST(beforeAD, 26_725, SysTime(DateTime(1999, 7, 6, 12, 30, 33), hnsecs(26_999))); - testST(beforeAD, 26_726, SysTime(DateTime(1999, 7, 6, 12, 30, 33), hnsecs(27_000))); - testST(beforeAD, 26_727, SysTime(DateTime(1999, 7, 6, 12, 30, 33), hnsecs(27_001))); - testST(beforeAD, 1_766_725, SysTime(DateTime(1999, 7, 6, 12, 30, 33), hnsecs(1_766_999))); - testST(beforeAD, 1_766_726, SysTime(DateTime(1999, 7, 6, 12, 30, 33), hnsecs(1_767_000))); - testST(beforeAD, 1_000_000, SysTime(DateTime(1999, 7, 6, 12, 30, 33), hnsecs(1_000_274))); - testST(beforeAD, 60_000_000L, SysTime(DateTime(1999, 7, 6, 12, 30, 39), hnsecs(274))); - testST(beforeAD, 3_600_000_000L, SysTime(DateTime(1999, 7, 6, 12, 36, 33), hnsecs(274))); - testST(beforeAD, 600_000_000L, SysTime(DateTime(1999, 7, 6, 12, 31, 33), hnsecs(274))); - testST(beforeAD, 36_000_000_000L, SysTime(DateTime(1999, 7, 6, 13, 30, 33), hnsecs(274))); - - testST(beforeAD, -1, SysTime(DateTime(1999, 7, 6, 12, 30, 33), hnsecs(273))); - testST(beforeAD, -2, SysTime(DateTime(1999, 7, 6, 12, 30, 33), hnsecs(272))); - testST(beforeAD, -10, SysTime(DateTime(1999, 7, 6, 12, 30, 33), hnsecs(264))); - testST(beforeAD, -100, SysTime(DateTime(1999, 7, 6, 12, 30, 33), hnsecs(174))); - testST(beforeAD, -274, SysTime(DateTime(1999, 7, 6, 12, 30, 33))); - testST(beforeAD, -275, SysTime(DateTime(1999, 7, 6, 12, 30, 32), hnsecs(9_999_999))); - testST(beforeAD, -1000, SysTime(DateTime(1999, 7, 6, 12, 30, 32), hnsecs(9_999_274))); - testST(beforeAD, -1001, SysTime(DateTime(1999, 7, 6, 12, 30, 32), hnsecs(9_999_273))); - testST(beforeAD, -2000, SysTime(DateTime(1999, 7, 6, 12, 30, 32), hnsecs(9_998_274))); - testST(beforeAD, -33_274, SysTime(DateTime(1999, 7, 6, 12, 30, 32), hnsecs(9_967_000))); - testST(beforeAD, -33_275, SysTime(DateTime(1999, 7, 6, 12, 30, 32), hnsecs(9_966_999))); - testST(beforeAD, -1_833_274, SysTime(DateTime(1999, 7, 6, 12, 30, 32), hnsecs(8_167_000))); - testST(beforeAD, -1_833_275, SysTime(DateTime(1999, 7, 6, 12, 30, 32), hnsecs(8_166_999))); - testST(beforeAD, -1_000_000, SysTime(DateTime(1999, 7, 6, 12, 30, 32), hnsecs(9_000_274))); - testST(beforeAD, -60_000_000L, SysTime(DateTime(1999, 7, 6, 12, 30, 27), hnsecs(274))); - testST(beforeAD, -3_600_000_000L, SysTime(DateTime(1999, 7, 6, 12, 24, 33), hnsecs(274))); - testST(beforeAD, -600_000_000L, SysTime(DateTime(1999, 7, 6, 12, 29, 33), hnsecs(274))); - testST(beforeAD, -36_000_000_000L, SysTime(DateTime(1999, 7, 6, 11, 30, 33), hnsecs(274))); - - // Test B.C. - auto beforeBC = SysTime(DateTime(-1999, 7, 6, 12, 30, 33), hnsecs(274)); - testST(beforeBC, 0, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), hnsecs(274))); - testST(beforeBC, 1, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), hnsecs(275))); - testST(beforeBC, 2, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), hnsecs(276))); - testST(beforeBC, 10, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), hnsecs(284))); - testST(beforeBC, 100, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), hnsecs(374))); - testST(beforeBC, 725, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), hnsecs(999))); - testST(beforeBC, 726, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), hnsecs(1000))); - testST(beforeBC, 1000, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), hnsecs(1274))); - testST(beforeBC, 1001, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), hnsecs(1275))); - testST(beforeBC, 2000, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), hnsecs(2274))); - testST(beforeBC, 26_725, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), hnsecs(26_999))); - testST(beforeBC, 26_726, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), hnsecs(27_000))); - testST(beforeBC, 26_727, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), hnsecs(27_001))); - testST(beforeBC, 1_766_725, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), hnsecs(1_766_999))); - testST(beforeBC, 1_766_726, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), hnsecs(1_767_000))); - testST(beforeBC, 1_000_000, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), hnsecs(1_000_274))); - testST(beforeBC, 60_000_000L, SysTime(DateTime(-1999, 7, 6, 12, 30, 39), hnsecs(274))); - testST(beforeBC, 3_600_000_000L, SysTime(DateTime(-1999, 7, 6, 12, 36, 33), hnsecs(274))); - testST(beforeBC, 600_000_000L, SysTime(DateTime(-1999, 7, 6, 12, 31, 33), hnsecs(274))); - testST(beforeBC, 36_000_000_000L, SysTime(DateTime(-1999, 7, 6, 13, 30, 33), hnsecs(274))); - - testST(beforeBC, -1, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), hnsecs(273))); - testST(beforeBC, -2, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), hnsecs(272))); - testST(beforeBC, -10, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), hnsecs(264))); - testST(beforeBC, -100, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), hnsecs(174))); - testST(beforeBC, -274, SysTime(DateTime(-1999, 7, 6, 12, 30, 33))); - testST(beforeBC, -275, SysTime(DateTime(-1999, 7, 6, 12, 30, 32), hnsecs(9_999_999))); - testST(beforeBC, -1000, SysTime(DateTime(-1999, 7, 6, 12, 30, 32), hnsecs(9_999_274))); - testST(beforeBC, -1001, SysTime(DateTime(-1999, 7, 6, 12, 30, 32), hnsecs(9_999_273))); - testST(beforeBC, -2000, SysTime(DateTime(-1999, 7, 6, 12, 30, 32), hnsecs(9_998_274))); - testST(beforeBC, -33_274, SysTime(DateTime(-1999, 7, 6, 12, 30, 32), hnsecs(9_967_000))); - testST(beforeBC, -33_275, SysTime(DateTime(-1999, 7, 6, 12, 30, 32), hnsecs(9_966_999))); - testST(beforeBC, -1_833_274, SysTime(DateTime(-1999, 7, 6, 12, 30, 32), hnsecs(8_167_000))); - testST(beforeBC, -1_833_275, SysTime(DateTime(-1999, 7, 6, 12, 30, 32), hnsecs(8_166_999))); - testST(beforeBC, -1_000_000, SysTime(DateTime(-1999, 7, 6, 12, 30, 32), hnsecs(9_000_274))); - testST(beforeBC, -60_000_000L, SysTime(DateTime(-1999, 7, 6, 12, 30, 27), hnsecs(274))); - testST(beforeBC, -3_600_000_000L, SysTime(DateTime(-1999, 7, 6, 12, 24, 33), hnsecs(274))); - testST(beforeBC, -600_000_000L, SysTime(DateTime(-1999, 7, 6, 12, 29, 33), hnsecs(274))); - testST(beforeBC, -36_000_000_000L, SysTime(DateTime(-1999, 7, 6, 11, 30, 33), hnsecs(274))); - - // Test Both - auto beforeBoth1 = SysTime(DateTime(1, 1, 1, 0, 0, 0)); - testST(beforeBoth1, 1, SysTime(DateTime(1, 1, 1, 0, 0, 0), hnsecs(1))); - testST(beforeBoth1, 0, SysTime(DateTime(1, 1, 1, 0, 0, 0))); - testST(beforeBoth1, -1, SysTime(DateTime(0, 12, 31, 23, 59, 59), hnsecs(9_999_999))); - testST(beforeBoth1, -2, SysTime(DateTime(0, 12, 31, 23, 59, 59), hnsecs(9_999_998))); - testST(beforeBoth1, -1000, SysTime(DateTime(0, 12, 31, 23, 59, 59), hnsecs(9_999_000))); - testST(beforeBoth1, -2000, SysTime(DateTime(0, 12, 31, 23, 59, 59), hnsecs(9_998_000))); - testST(beforeBoth1, -2555, SysTime(DateTime(0, 12, 31, 23, 59, 59), hnsecs(9_997_445))); - testST(beforeBoth1, -1_000_000, SysTime(DateTime(0, 12, 31, 23, 59, 59), hnsecs(9_000_000))); - testST(beforeBoth1, -2_000_000, SysTime(DateTime(0, 12, 31, 23, 59, 59), hnsecs(8_000_000))); - testST(beforeBoth1, -2_333_333, SysTime(DateTime(0, 12, 31, 23, 59, 59), hnsecs(7_666_667))); - testST(beforeBoth1, -10_000_000, SysTime(DateTime(0, 12, 31, 23, 59, 59))); - testST(beforeBoth1, -20_000_000, SysTime(DateTime(0, 12, 31, 23, 59, 58))); - testST(beforeBoth1, -20_888_888, SysTime(DateTime(0, 12, 31, 23, 59, 57), hnsecs(9_111_112))); - - auto beforeBoth2 = SysTime(DateTime(0, 12, 31, 23, 59, 59), hnsecs(9_999_999)); - testST(beforeBoth2, -1, SysTime(DateTime(0, 12, 31, 23, 59, 59), hnsecs(9_999_998))); - testST(beforeBoth2, 0, SysTime(DateTime(0, 12, 31, 23, 59, 59), hnsecs(9_999_999))); - testST(beforeBoth2, 1, SysTime(DateTime(1, 1, 1, 0, 0, 0))); - testST(beforeBoth2, 2, SysTime(DateTime(1, 1, 1, 0, 0, 0), hnsecs(1))); - testST(beforeBoth2, 1000, SysTime(DateTime(1, 1, 1, 0, 0, 0), hnsecs(999))); - testST(beforeBoth2, 2000, SysTime(DateTime(1, 1, 1, 0, 0, 0), hnsecs(1999))); - testST(beforeBoth2, 2555, SysTime(DateTime(1, 1, 1, 0, 0, 0), hnsecs(2554))); - testST(beforeBoth2, 1_000_000, SysTime(DateTime(1, 1, 1, 0, 0, 0), hnsecs(999_999))); - testST(beforeBoth2, 2_000_000, SysTime(DateTime(1, 1, 1, 0, 0, 0), hnsecs(1_999_999))); - testST(beforeBoth2, 2_333_333, SysTime(DateTime(1, 1, 1, 0, 0, 0), hnsecs(2_333_332))); - testST(beforeBoth2, 10_000_000, SysTime(DateTime(1, 1, 1, 0, 0, 0), hnsecs(9_999_999))); - testST(beforeBoth2, 20_000_000, SysTime(DateTime(1, 1, 1, 0, 0, 1), hnsecs(9_999_999))); - testST(beforeBoth2, 20_888_888, SysTime(DateTime(1, 1, 1, 0, 0, 2), hnsecs(888_887))); - - auto duration = dur!"seconds"(12); - const cst = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); - immutable ist = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); - assert(cst + duration == SysTime(DateTime(1999, 7, 6, 12, 30, 45))); - assert(ist + duration == SysTime(DateTime(1999, 7, 6, 12, 30, 45))); - assert(cst - duration == SysTime(DateTime(1999, 7, 6, 12, 30, 21))); - assert(ist - duration == SysTime(DateTime(1999, 7, 6, 12, 30, 21))); - - static void testScope(scope ref SysTime st, scope ref Duration d) @safe - { - auto result = st + d; - } - } - - - /++ - Gives the result of adding or subtracting a $(REF Duration, core,time) from - this $(LREF SysTime), as well as assigning the result to this - $(LREF SysTime). - - The legal types of arithmetic for $(LREF SysTime) using this operator are - - $(BOOKTABLE, - $(TR $(TD SysTime) $(TD +) $(TD Duration) $(TD -->) $(TD SysTime)) - $(TR $(TD SysTime) $(TD -) $(TD Duration) $(TD -->) $(TD SysTime)) - ) - - Params: - duration = The $(REF Duration, core,time) to add to or subtract from - this $(LREF SysTime). - +/ - ref SysTime opOpAssign(string op)(Duration duration) @safe pure nothrow scope - if (op == "+" || op == "-") - { - immutable hnsecs = duration.total!"hnsecs"; - mixin("_stdTime " ~ op ~ "= hnsecs;"); - return this; - } - - @safe unittest - { - import core.time; - auto before = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); - assert(before + dur!"weeks"(7) == SysTime(DateTime(1999, 8, 24, 12, 30, 33))); - assert(before + dur!"weeks"(-7) == SysTime(DateTime(1999, 5, 18, 12, 30, 33))); - assert(before + dur!"days"(7) == SysTime(DateTime(1999, 7, 13, 12, 30, 33))); - assert(before + dur!"days"(-7) == SysTime(DateTime(1999, 6, 29, 12, 30, 33))); - - assert(before + dur!"hours"(7) == SysTime(DateTime(1999, 7, 6, 19, 30, 33))); - assert(before + dur!"hours"(-7) == SysTime(DateTime(1999, 7, 6, 5, 30, 33))); - assert(before + dur!"minutes"(7) == SysTime(DateTime(1999, 7, 6, 12, 37, 33))); - assert(before + dur!"minutes"(-7) == SysTime(DateTime(1999, 7, 6, 12, 23, 33))); - assert(before + dur!"seconds"(7) == SysTime(DateTime(1999, 7, 6, 12, 30, 40))); - assert(before + dur!"seconds"(-7) == SysTime(DateTime(1999, 7, 6, 12, 30, 26))); - assert(before + dur!"msecs"(7) == SysTime(DateTime(1999, 7, 6, 12, 30, 33), msecs(7))); - assert(before + dur!"msecs"(-7) == SysTime(DateTime(1999, 7, 6, 12, 30, 32), msecs(993))); - assert(before + dur!"usecs"(7) == SysTime(DateTime(1999, 7, 6, 12, 30, 33), usecs(7))); - assert(before + dur!"usecs"(-7) == SysTime(DateTime(1999, 7, 6, 12, 30, 32), usecs(999_993))); - assert(before + dur!"hnsecs"(7) == SysTime(DateTime(1999, 7, 6, 12, 30, 33), hnsecs(7))); - assert(before + dur!"hnsecs"(-7) == SysTime(DateTime(1999, 7, 6, 12, 30, 32), hnsecs(9_999_993))); - - assert(before - dur!"weeks"(-7) == SysTime(DateTime(1999, 8, 24, 12, 30, 33))); - assert(before - dur!"weeks"(7) == SysTime(DateTime(1999, 5, 18, 12, 30, 33))); - assert(before - dur!"days"(-7) == SysTime(DateTime(1999, 7, 13, 12, 30, 33))); - assert(before - dur!"days"(7) == SysTime(DateTime(1999, 6, 29, 12, 30, 33))); - - assert(before - dur!"hours"(-7) == SysTime(DateTime(1999, 7, 6, 19, 30, 33))); - assert(before - dur!"hours"(7) == SysTime(DateTime(1999, 7, 6, 5, 30, 33))); - assert(before - dur!"minutes"(-7) == SysTime(DateTime(1999, 7, 6, 12, 37, 33))); - assert(before - dur!"minutes"(7) == SysTime(DateTime(1999, 7, 6, 12, 23, 33))); - assert(before - dur!"seconds"(-7) == SysTime(DateTime(1999, 7, 6, 12, 30, 40))); - assert(before - dur!"seconds"(7) == SysTime(DateTime(1999, 7, 6, 12, 30, 26))); - assert(before - dur!"msecs"(-7) == SysTime(DateTime(1999, 7, 6, 12, 30, 33), msecs(7))); - assert(before - dur!"msecs"(7) == SysTime(DateTime(1999, 7, 6, 12, 30, 32), msecs(993))); - assert(before - dur!"usecs"(-7) == SysTime(DateTime(1999, 7, 6, 12, 30, 33), usecs(7))); - assert(before - dur!"usecs"(7) == SysTime(DateTime(1999, 7, 6, 12, 30, 32), usecs(999_993))); - assert(before - dur!"hnsecs"(-7) == SysTime(DateTime(1999, 7, 6, 12, 30, 33), hnsecs(7))); - assert(before - dur!"hnsecs"(7) == SysTime(DateTime(1999, 7, 6, 12, 30, 32), hnsecs(9_999_993))); - - static void testST(SysTime orig, long hnsecs, SysTime expected, size_t line = __LINE__) @safe - { - auto r = orig += dur!"hnsecs"(hnsecs); - if (orig != expected) - throw new AssertError(format("Failed 1. actual [%s] != expected [%s]", orig, expected), __FILE__, line); - if (r != expected) - throw new AssertError(format("Failed 2. actual [%s] != expected [%s]", r, expected), __FILE__, line); - } - - // Test A.D. - auto beforeAD = SysTime(DateTime(1999, 7, 6, 12, 30, 33), hnsecs(274)); - testST(beforeAD, 0, SysTime(DateTime(1999, 7, 6, 12, 30, 33), hnsecs(274))); - testST(beforeAD, 1, SysTime(DateTime(1999, 7, 6, 12, 30, 33), hnsecs(275))); - testST(beforeAD, 2, SysTime(DateTime(1999, 7, 6, 12, 30, 33), hnsecs(276))); - testST(beforeAD, 10, SysTime(DateTime(1999, 7, 6, 12, 30, 33), hnsecs(284))); - testST(beforeAD, 100, SysTime(DateTime(1999, 7, 6, 12, 30, 33), hnsecs(374))); - testST(beforeAD, 725, SysTime(DateTime(1999, 7, 6, 12, 30, 33), hnsecs(999))); - testST(beforeAD, 726, SysTime(DateTime(1999, 7, 6, 12, 30, 33), hnsecs(1000))); - testST(beforeAD, 1000, SysTime(DateTime(1999, 7, 6, 12, 30, 33), hnsecs(1274))); - testST(beforeAD, 1001, SysTime(DateTime(1999, 7, 6, 12, 30, 33), hnsecs(1275))); - testST(beforeAD, 2000, SysTime(DateTime(1999, 7, 6, 12, 30, 33), hnsecs(2274))); - testST(beforeAD, 26_725, SysTime(DateTime(1999, 7, 6, 12, 30, 33), hnsecs(26_999))); - testST(beforeAD, 26_726, SysTime(DateTime(1999, 7, 6, 12, 30, 33), hnsecs(27_000))); - testST(beforeAD, 26_727, SysTime(DateTime(1999, 7, 6, 12, 30, 33), hnsecs(27_001))); - testST(beforeAD, 1_766_725, SysTime(DateTime(1999, 7, 6, 12, 30, 33), hnsecs(1_766_999))); - testST(beforeAD, 1_766_726, SysTime(DateTime(1999, 7, 6, 12, 30, 33), hnsecs(1_767_000))); - testST(beforeAD, 1_000_000, SysTime(DateTime(1999, 7, 6, 12, 30, 33), hnsecs(1_000_274))); - testST(beforeAD, 60_000_000L, SysTime(DateTime(1999, 7, 6, 12, 30, 39), hnsecs(274))); - testST(beforeAD, 3_600_000_000L, SysTime(DateTime(1999, 7, 6, 12, 36, 33), hnsecs(274))); - testST(beforeAD, 600_000_000L, SysTime(DateTime(1999, 7, 6, 12, 31, 33), hnsecs(274))); - testST(beforeAD, 36_000_000_000L, SysTime(DateTime(1999, 7, 6, 13, 30, 33), hnsecs(274))); - - testST(beforeAD, -1, SysTime(DateTime(1999, 7, 6, 12, 30, 33), hnsecs(273))); - testST(beforeAD, -2, SysTime(DateTime(1999, 7, 6, 12, 30, 33), hnsecs(272))); - testST(beforeAD, -10, SysTime(DateTime(1999, 7, 6, 12, 30, 33), hnsecs(264))); - testST(beforeAD, -100, SysTime(DateTime(1999, 7, 6, 12, 30, 33), hnsecs(174))); - testST(beforeAD, -274, SysTime(DateTime(1999, 7, 6, 12, 30, 33))); - testST(beforeAD, -275, SysTime(DateTime(1999, 7, 6, 12, 30, 32), hnsecs(9_999_999))); - testST(beforeAD, -1000, SysTime(DateTime(1999, 7, 6, 12, 30, 32), hnsecs(9_999_274))); - testST(beforeAD, -1001, SysTime(DateTime(1999, 7, 6, 12, 30, 32), hnsecs(9_999_273))); - testST(beforeAD, -2000, SysTime(DateTime(1999, 7, 6, 12, 30, 32), hnsecs(9_998_274))); - testST(beforeAD, -33_274, SysTime(DateTime(1999, 7, 6, 12, 30, 32), hnsecs(9_967_000))); - testST(beforeAD, -33_275, SysTime(DateTime(1999, 7, 6, 12, 30, 32), hnsecs(9_966_999))); - testST(beforeAD, -1_833_274, SysTime(DateTime(1999, 7, 6, 12, 30, 32), hnsecs(8_167_000))); - testST(beforeAD, -1_833_275, SysTime(DateTime(1999, 7, 6, 12, 30, 32), hnsecs(8_166_999))); - testST(beforeAD, -1_000_000, SysTime(DateTime(1999, 7, 6, 12, 30, 32), hnsecs(9_000_274))); - testST(beforeAD, -60_000_000L, SysTime(DateTime(1999, 7, 6, 12, 30, 27), hnsecs(274))); - testST(beforeAD, -3_600_000_000L, SysTime(DateTime(1999, 7, 6, 12, 24, 33), hnsecs(274))); - testST(beforeAD, -600_000_000L, SysTime(DateTime(1999, 7, 6, 12, 29, 33), hnsecs(274))); - testST(beforeAD, -36_000_000_000L, SysTime(DateTime(1999, 7, 6, 11, 30, 33), hnsecs(274))); - - // Test B.C. - auto beforeBC = SysTime(DateTime(-1999, 7, 6, 12, 30, 33), hnsecs(274)); - testST(beforeBC, 0, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), hnsecs(274))); - testST(beforeBC, 1, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), hnsecs(275))); - testST(beforeBC, 2, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), hnsecs(276))); - testST(beforeBC, 10, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), hnsecs(284))); - testST(beforeBC, 100, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), hnsecs(374))); - testST(beforeBC, 725, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), hnsecs(999))); - testST(beforeBC, 726, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), hnsecs(1000))); - testST(beforeBC, 1000, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), hnsecs(1274))); - testST(beforeBC, 1001, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), hnsecs(1275))); - testST(beforeBC, 2000, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), hnsecs(2274))); - testST(beforeBC, 26_725, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), hnsecs(26_999))); - testST(beforeBC, 26_726, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), hnsecs(27_000))); - testST(beforeBC, 26_727, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), hnsecs(27_001))); - testST(beforeBC, 1_766_725, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), hnsecs(1_766_999))); - testST(beforeBC, 1_766_726, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), hnsecs(1_767_000))); - testST(beforeBC, 1_000_000, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), hnsecs(1_000_274))); - testST(beforeBC, 60_000_000L, SysTime(DateTime(-1999, 7, 6, 12, 30, 39), hnsecs(274))); - testST(beforeBC, 3_600_000_000L, SysTime(DateTime(-1999, 7, 6, 12, 36, 33), hnsecs(274))); - testST(beforeBC, 600_000_000L, SysTime(DateTime(-1999, 7, 6, 12, 31, 33), hnsecs(274))); - testST(beforeBC, 36_000_000_000L, SysTime(DateTime(-1999, 7, 6, 13, 30, 33), hnsecs(274))); - - testST(beforeBC, -1, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), hnsecs(273))); - testST(beforeBC, -2, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), hnsecs(272))); - testST(beforeBC, -10, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), hnsecs(264))); - testST(beforeBC, -100, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), hnsecs(174))); - testST(beforeBC, -274, SysTime(DateTime(-1999, 7, 6, 12, 30, 33))); - testST(beforeBC, -275, SysTime(DateTime(-1999, 7, 6, 12, 30, 32), hnsecs(9_999_999))); - testST(beforeBC, -1000, SysTime(DateTime(-1999, 7, 6, 12, 30, 32), hnsecs(9_999_274))); - testST(beforeBC, -1001, SysTime(DateTime(-1999, 7, 6, 12, 30, 32), hnsecs(9_999_273))); - testST(beforeBC, -2000, SysTime(DateTime(-1999, 7, 6, 12, 30, 32), hnsecs(9_998_274))); - testST(beforeBC, -33_274, SysTime(DateTime(-1999, 7, 6, 12, 30, 32), hnsecs(9_967_000))); - testST(beforeBC, -33_275, SysTime(DateTime(-1999, 7, 6, 12, 30, 32), hnsecs(9_966_999))); - testST(beforeBC, -1_833_274, SysTime(DateTime(-1999, 7, 6, 12, 30, 32), hnsecs(8_167_000))); - testST(beforeBC, -1_833_275, SysTime(DateTime(-1999, 7, 6, 12, 30, 32), hnsecs(8_166_999))); - testST(beforeBC, -1_000_000, SysTime(DateTime(-1999, 7, 6, 12, 30, 32), hnsecs(9_000_274))); - testST(beforeBC, -60_000_000L, SysTime(DateTime(-1999, 7, 6, 12, 30, 27), hnsecs(274))); - testST(beforeBC, -3_600_000_000L, SysTime(DateTime(-1999, 7, 6, 12, 24, 33), hnsecs(274))); - testST(beforeBC, -600_000_000L, SysTime(DateTime(-1999, 7, 6, 12, 29, 33), hnsecs(274))); - testST(beforeBC, -36_000_000_000L, SysTime(DateTime(-1999, 7, 6, 11, 30, 33), hnsecs(274))); - - // Test Both - auto beforeBoth1 = SysTime(DateTime(1, 1, 1, 0, 0, 0)); - testST(beforeBoth1, 1, SysTime(DateTime(1, 1, 1, 0, 0, 0), hnsecs(1))); - testST(beforeBoth1, 0, SysTime(DateTime(1, 1, 1, 0, 0, 0))); - testST(beforeBoth1, -1, SysTime(DateTime(0, 12, 31, 23, 59, 59), hnsecs(9_999_999))); - testST(beforeBoth1, -2, SysTime(DateTime(0, 12, 31, 23, 59, 59), hnsecs(9_999_998))); - testST(beforeBoth1, -1000, SysTime(DateTime(0, 12, 31, 23, 59, 59), hnsecs(9_999_000))); - testST(beforeBoth1, -2000, SysTime(DateTime(0, 12, 31, 23, 59, 59), hnsecs(9_998_000))); - testST(beforeBoth1, -2555, SysTime(DateTime(0, 12, 31, 23, 59, 59), hnsecs(9_997_445))); - testST(beforeBoth1, -1_000_000, SysTime(DateTime(0, 12, 31, 23, 59, 59), hnsecs(9_000_000))); - testST(beforeBoth1, -2_000_000, SysTime(DateTime(0, 12, 31, 23, 59, 59), hnsecs(8_000_000))); - testST(beforeBoth1, -2_333_333, SysTime(DateTime(0, 12, 31, 23, 59, 59), hnsecs(7_666_667))); - testST(beforeBoth1, -10_000_000, SysTime(DateTime(0, 12, 31, 23, 59, 59))); - testST(beforeBoth1, -20_000_000, SysTime(DateTime(0, 12, 31, 23, 59, 58))); - testST(beforeBoth1, -20_888_888, SysTime(DateTime(0, 12, 31, 23, 59, 57), hnsecs(9_111_112))); - - auto beforeBoth2 = SysTime(DateTime(0, 12, 31, 23, 59, 59), hnsecs(9_999_999)); - testST(beforeBoth2, -1, SysTime(DateTime(0, 12, 31, 23, 59, 59), hnsecs(9_999_998))); - testST(beforeBoth2, 0, SysTime(DateTime(0, 12, 31, 23, 59, 59), hnsecs(9_999_999))); - testST(beforeBoth2, 1, SysTime(DateTime(1, 1, 1, 0, 0, 0))); - testST(beforeBoth2, 2, SysTime(DateTime(1, 1, 1, 0, 0, 0), hnsecs(1))); - testST(beforeBoth2, 1000, SysTime(DateTime(1, 1, 1, 0, 0, 0), hnsecs(999))); - testST(beforeBoth2, 2000, SysTime(DateTime(1, 1, 1, 0, 0, 0), hnsecs(1999))); - testST(beforeBoth2, 2555, SysTime(DateTime(1, 1, 1, 0, 0, 0), hnsecs(2554))); - testST(beforeBoth2, 1_000_000, SysTime(DateTime(1, 1, 1, 0, 0, 0), hnsecs(999_999))); - testST(beforeBoth2, 2_000_000, SysTime(DateTime(1, 1, 1, 0, 0, 0), hnsecs(1_999_999))); - testST(beforeBoth2, 2_333_333, SysTime(DateTime(1, 1, 1, 0, 0, 0), hnsecs(2_333_332))); - testST(beforeBoth2, 10_000_000, SysTime(DateTime(1, 1, 1, 0, 0, 0), hnsecs(9_999_999))); - testST(beforeBoth2, 20_000_000, SysTime(DateTime(1, 1, 1, 0, 0, 1), hnsecs(9_999_999))); - testST(beforeBoth2, 20_888_888, SysTime(DateTime(1, 1, 1, 0, 0, 2), hnsecs(888_887))); - - { - auto st = SysTime(DateTime(0, 12, 31, 23, 59, 59), hnsecs(9_999_999)); - (st += dur!"hnsecs"(52)) += dur!"seconds"(-907); - assert(st == SysTime(DateTime(0, 12, 31, 23, 44, 53), hnsecs(51))); - } - - auto duration = dur!"seconds"(12); - const cst = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); - immutable ist = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); - static assert(!__traits(compiles, cst += duration)); - static assert(!__traits(compiles, ist += duration)); - static assert(!__traits(compiles, cst -= duration)); - static assert(!__traits(compiles, ist -= duration)); - - static void testScope(scope ref SysTime st, scope ref Duration d) @safe - { - auto result1 = st += d; - auto result2 = st -= d; - } - } - - - /++ - Gives the difference between two $(LREF SysTime)s. - - The legal types of arithmetic for $(LREF SysTime) using this operator - are - - $(BOOKTABLE, - $(TR $(TD SysTime) $(TD -) $(TD SysTime) $(TD -->) $(TD duration)) - ) - +/ - Duration opBinary(string op)(SysTime rhs) @safe const pure nothrow scope - if (op == "-") - { - return dur!"hnsecs"(_stdTime - rhs._stdTime); - } - - @safe unittest - { - import core.time; - assert(SysTime(DateTime(1999, 7, 6, 12, 30, 33)) - SysTime(DateTime(1998, 7, 6, 12, 30, 33)) == - dur!"seconds"(31_536_000)); - assert(SysTime(DateTime(1998, 7, 6, 12, 30, 33)) - SysTime(DateTime(1999, 7, 6, 12, 30, 33)) == - dur!"seconds"(-31_536_000)); - - assert(SysTime(DateTime(1999, 8, 6, 12, 30, 33)) - SysTime(DateTime(1999, 7, 6, 12, 30, 33)) == - dur!"seconds"(26_78_400)); - assert(SysTime(DateTime(1999, 7, 6, 12, 30, 33)) - SysTime(DateTime(1999, 8, 6, 12, 30, 33)) == - dur!"seconds"(-26_78_400)); - - assert(SysTime(DateTime(1999, 7, 6, 12, 30, 33)) - SysTime(DateTime(1999, 7, 5, 12, 30, 33)) == - dur!"seconds"(86_400)); - assert(SysTime(DateTime(1999, 7, 5, 12, 30, 33)) - SysTime(DateTime(1999, 7, 6, 12, 30, 33)) == - dur!"seconds"(-86_400)); - - assert(SysTime(DateTime(1999, 7, 6, 12, 30, 33)) - SysTime(DateTime(1999, 7, 6, 11, 30, 33)) == - dur!"seconds"(3600)); - assert(SysTime(DateTime(1999, 7, 6, 11, 30, 33)) - SysTime(DateTime(1999, 7, 6, 12, 30, 33)) == - dur!"seconds"(-3600)); - - assert(SysTime(DateTime(1999, 7, 6, 12, 31, 33)) - SysTime(DateTime(1999, 7, 6, 12, 30, 33)) == - dur!"seconds"(60)); - assert(SysTime(DateTime(1999, 7, 6, 12, 30, 33)) - SysTime(DateTime(1999, 7, 6, 12, 31, 33)) == - dur!"seconds"(-60)); - - assert(SysTime(DateTime(1999, 7, 6, 12, 30, 34)) - SysTime(DateTime(1999, 7, 6, 12, 30, 33)) == - dur!"seconds"(1)); - assert(SysTime(DateTime(1999, 7, 6, 12, 30, 33)) - SysTime(DateTime(1999, 7, 6, 12, 30, 34)) == - dur!"seconds"(-1)); - - { - auto dt = DateTime(1999, 7, 6, 12, 30, 33); - assert(SysTime(dt, msecs(532)) - SysTime(dt) == msecs(532)); - assert(SysTime(dt) - SysTime(dt, msecs(532)) == msecs(-532)); - - assert(SysTime(dt, usecs(333_347)) - SysTime(dt) == usecs(333_347)); - assert(SysTime(dt) - SysTime(dt, usecs(333_347)) == usecs(-333_347)); - - assert(SysTime(dt, hnsecs(1_234_567)) - SysTime(dt) == hnsecs(1_234_567)); - assert(SysTime(dt) - SysTime(dt, hnsecs(1_234_567)) == hnsecs(-1_234_567)); - } - - assert(SysTime(DateTime(1, 1, 1, 12, 30, 33)) - SysTime(DateTime(1, 1, 1, 0, 0, 0)) == dur!"seconds"(45033)); - assert(SysTime(DateTime(1, 1, 1, 0, 0, 0)) - SysTime(DateTime(1, 1, 1, 12, 30, 33)) == dur!"seconds"(-45033)); - assert(SysTime(DateTime(0, 12, 31, 12, 30, 33)) - SysTime(DateTime(1, 1, 1, 0, 0, 0)) == dur!"seconds"(-41367)); - assert(SysTime(DateTime(1, 1, 1, 0, 0, 0)) - SysTime(DateTime(0, 12, 31, 12, 30, 33)) == dur!"seconds"(41367)); - - assert(SysTime(DateTime(1, 1, 1, 0, 0, 0)) - SysTime(DateTime(0, 12, 31, 23, 59, 59), hnsecs(9_999_999)) == - dur!"hnsecs"(1)); - assert(SysTime(DateTime(0, 12, 31, 23, 59, 59), hnsecs(9_999_999)) - SysTime(DateTime(1, 1, 1, 0, 0, 0)) == - dur!"hnsecs"(-1)); - - version (Posix) - { - import std.datetime.timezone : PosixTimeZone; - immutable tz = PosixTimeZone.getTimeZone("America/Los_Angeles"); - } - else version (Windows) - { - import std.datetime.timezone : WindowsTimeZone; - immutable tz = WindowsTimeZone.getTimeZone("Pacific Standard Time"); - } - - { - auto dt = DateTime(2011, 1, 13, 8, 17, 2); - auto d = msecs(296); - assert(SysTime(dt, d, tz) - SysTime(dt, d, tz) == Duration.zero); - assert(SysTime(dt, d, tz) - SysTime(dt, d, UTC()) == hours(8)); - assert(SysTime(dt, d, UTC()) - SysTime(dt, d, tz) == hours(-8)); - } - - auto st = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); - const cst = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); - immutable ist = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); - assert(st - st == Duration.zero); - assert(cst - st == Duration.zero); - assert(ist - st == Duration.zero); - - assert(st - cst == Duration.zero); - assert(cst - cst == Duration.zero); - assert(ist - cst == Duration.zero); - - assert(st - ist == Duration.zero); - assert(cst - ist == Duration.zero); - assert(ist - ist == Duration.zero); - - static void testScope(scope ref SysTime left, scope ref SysTime right) @safe - { - auto result = left - right; - } - } - - - /++ - Returns the difference between the two $(LREF SysTime)s in months. - - To get the difference in years, subtract the year property - of two $(LREF SysTime)s. To get the difference in days or weeks, - subtract the $(LREF SysTime)s themselves and use the - $(REF Duration, core,time) that results. Because converting between - months and smaller units requires a specific date (which - $(REF Duration, core,time)s don't have), getting the difference in - months requires some math using both the year and month properties, so - this is a convenience function for getting the difference in months. - - Note that the number of days in the months or how far into the month - either date is is irrelevant. It is the difference in the month property - combined with the difference in years * 12. So, for instance, - December 31st and January 1st are one month apart just as December 1st - and January 31st are one month apart. - - Params: - rhs = The $(LREF SysTime) to subtract from this one. - +/ - int diffMonths(scope SysTime rhs) @safe const nothrow scope - { - return (cast(Date) this).diffMonths(cast(Date) rhs); - } - - /// - @safe unittest - { - import core.time; - import std.datetime.date : Date; - - assert(SysTime(Date(1999, 2, 1)).diffMonths( - SysTime(Date(1999, 1, 31))) == 1); - - assert(SysTime(Date(1999, 1, 31)).diffMonths( - SysTime(Date(1999, 2, 1))) == -1); - - assert(SysTime(Date(1999, 3, 1)).diffMonths( - SysTime(Date(1999, 1, 1))) == 2); - - assert(SysTime(Date(1999, 1, 1)).diffMonths( - SysTime(Date(1999, 3, 31))) == -2); - } - - @safe unittest - { - import core.time; - auto st = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); - const cst = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); - immutable ist = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); - assert(st.diffMonths(st) == 0); - assert(cst.diffMonths(st) == 0); - assert(ist.diffMonths(st) == 0); - - assert(st.diffMonths(cst) == 0); - assert(cst.diffMonths(cst) == 0); - assert(ist.diffMonths(cst) == 0); - - assert(st.diffMonths(ist) == 0); - assert(cst.diffMonths(ist) == 0); - assert(ist.diffMonths(ist) == 0); - - static void testScope(scope ref SysTime left, scope ref SysTime right) @safe - { - auto result = left.diffMonths(right); - } - } - - - /++ - Whether this $(LREF SysTime) is in a leap year. - +/ - @property bool isLeapYear() @safe const nothrow scope - { - return (cast(Date) this).isLeapYear; - } - - @safe unittest - { - import core.time; - auto st = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); - const cst = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); - immutable ist = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); - assert(!st.isLeapYear); - assert(!cst.isLeapYear); - assert(!ist.isLeapYear); - - static void testScope(scope ref SysTime st) @safe - { - auto result = st.isLeapYear; - } - } - - - /++ - Day of the week this $(LREF SysTime) is on. - +/ - @property DayOfWeek dayOfWeek() @safe const nothrow scope - { - return getDayOfWeek(dayOfGregorianCal); - } - - @safe unittest - { - import core.time; - auto st = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); - const cst = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); - immutable ist = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); - assert(st.dayOfWeek == DayOfWeek.tue); - assert(cst.dayOfWeek == DayOfWeek.tue); - assert(ist.dayOfWeek == DayOfWeek.tue); - - static void testScope(scope ref SysTime st) @safe - { - auto result = st.dayOfWeek; - } - } - - - /++ - Day of the year this $(LREF SysTime) is on. - +/ - @property ushort dayOfYear() @safe const nothrow scope - { - return (cast(Date) this).dayOfYear; - } - - /// - @safe unittest - { - import core.time; - import std.datetime.date : DateTime; - - assert(SysTime(DateTime(1999, 1, 1, 12, 22, 7)).dayOfYear == 1); - assert(SysTime(DateTime(1999, 12, 31, 7, 2, 59)).dayOfYear == 365); - assert(SysTime(DateTime(2000, 12, 31, 21, 20, 0)).dayOfYear == 366); - } - - @safe unittest - { - import core.time; - auto st = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); - const cst = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); - immutable ist = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); - assert(st.dayOfYear == 187); - assert(cst.dayOfYear == 187); - assert(ist.dayOfYear == 187); - - static void testScope(scope ref SysTime st) @safe - { - auto result = st.dayOfYear; - } - } - - - /++ - Day of the year. - - Params: - day = The day of the year to set which day of the year this - $(LREF SysTime) is on. - +/ - @property void dayOfYear(int day) @safe scope - { - immutable hnsecs = adjTime; - immutable days = convert!("hnsecs", "days")(hnsecs); - immutable theRest = hnsecs - convert!("days", "hnsecs")(days); - - auto date = Date(cast(int) days); - date.dayOfYear = day; - - immutable newDaysHNSecs = convert!("days", "hnsecs")(date.dayOfGregorianCal - 1); - - adjTime = newDaysHNSecs + theRest; - } - - @safe unittest - { - import core.time; - auto st = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); - const cst = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); - immutable ist = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); - st.dayOfYear = 12; - assert(st.dayOfYear == 12); - static assert(!__traits(compiles, cst.dayOfYear = 12)); - static assert(!__traits(compiles, ist.dayOfYear = 12)); - - static void testScope(scope ref SysTime st) @safe - { - st.dayOfYear = 42; - } - } - - - /++ - The Xth day of the Gregorian Calendar that this $(LREF SysTime) is on. - +/ - @property int dayOfGregorianCal() @safe const nothrow scope - { - immutable adjustedTime = adjTime; - - // We have to add one because 0 would be midnight, January 1st, 1 A.D., - // which would be the 1st day of the Gregorian Calendar, not the 0th. So, - // simply casting to days is one day off. - if (adjustedTime > 0) - return cast(int) getUnitsFromHNSecs!"days"(adjustedTime) + 1; - - long hnsecs = adjustedTime; - immutable days = cast(int) splitUnitsFromHNSecs!"days"(hnsecs); - - return hnsecs == 0 ? days + 1 : days; - } - - /// - @safe unittest - { - import core.time; - import std.datetime.date : DateTime; - - assert(SysTime(DateTime(1, 1, 1, 0, 0, 0)).dayOfGregorianCal == 1); - assert(SysTime(DateTime(1, 12, 31, 23, 59, 59)).dayOfGregorianCal == 365); - assert(SysTime(DateTime(2, 1, 1, 2, 2, 2)).dayOfGregorianCal == 366); - - assert(SysTime(DateTime(0, 12, 31, 7, 7, 7)).dayOfGregorianCal == 0); - assert(SysTime(DateTime(0, 1, 1, 19, 30, 0)).dayOfGregorianCal == -365); - assert(SysTime(DateTime(-1, 12, 31, 4, 7, 0)).dayOfGregorianCal == -366); - - assert(SysTime(DateTime(2000, 1, 1, 9, 30, 20)).dayOfGregorianCal == 730_120); - assert(SysTime(DateTime(2010, 12, 31, 15, 45, 50)).dayOfGregorianCal == 734_137); - } - - @safe unittest - { - import core.time; - // Test A.D. - assert(SysTime(DateTime(1, 1, 1, 0, 0, 0)).dayOfGregorianCal == 1); - assert(SysTime(DateTime(1, 1, 1, 0, 0, 0), hnsecs(1)).dayOfGregorianCal == 1); - assert(SysTime(DateTime(1, 1, 1, 23, 59, 59), hnsecs(9_999_999)).dayOfGregorianCal == 1); - - assert(SysTime(DateTime(1, 1, 1, 12, 2, 9), msecs(212)).dayOfGregorianCal == 1); - assert(SysTime(DateTime(1, 1, 2, 12, 2, 9), msecs(212)).dayOfGregorianCal == 2); - assert(SysTime(DateTime(1, 2, 1, 12, 2, 9), msecs(212)).dayOfGregorianCal == 32); - assert(SysTime(DateTime(2, 1, 1, 12, 2, 9), msecs(212)).dayOfGregorianCal == 366); - assert(SysTime(DateTime(3, 1, 1, 12, 2, 9), msecs(212)).dayOfGregorianCal == 731); - assert(SysTime(DateTime(4, 1, 1, 12, 2, 9), msecs(212)).dayOfGregorianCal == 1096); - assert(SysTime(DateTime(5, 1, 1, 12, 2, 9), msecs(212)).dayOfGregorianCal == 1462); - assert(SysTime(DateTime(50, 1, 1, 12, 2, 9), msecs(212)).dayOfGregorianCal == 17_898); - assert(SysTime(DateTime(97, 1, 1, 12, 2, 9), msecs(212)).dayOfGregorianCal == 35_065); - assert(SysTime(DateTime(100, 1, 1, 12, 2, 9), msecs(212)).dayOfGregorianCal == 36_160); - assert(SysTime(DateTime(101, 1, 1, 12, 2, 9), msecs(212)).dayOfGregorianCal == 36_525); - assert(SysTime(DateTime(105, 1, 1, 12, 2, 9), msecs(212)).dayOfGregorianCal == 37_986); - assert(SysTime(DateTime(200, 1, 1, 12, 2, 9), msecs(212)).dayOfGregorianCal == 72_684); - assert(SysTime(DateTime(201, 1, 1, 12, 2, 9), msecs(212)).dayOfGregorianCal == 73_049); - assert(SysTime(DateTime(300, 1, 1, 12, 2, 9), msecs(212)).dayOfGregorianCal == 109_208); - assert(SysTime(DateTime(301, 1, 1, 12, 2, 9), msecs(212)).dayOfGregorianCal == 109_573); - assert(SysTime(DateTime(400, 1, 1, 12, 2, 9), msecs(212)).dayOfGregorianCal == 145_732); - assert(SysTime(DateTime(401, 1, 1, 12, 2, 9), msecs(212)).dayOfGregorianCal == 146_098); - assert(SysTime(DateTime(500, 1, 1, 12, 2, 9), msecs(212)).dayOfGregorianCal == 182_257); - assert(SysTime(DateTime(501, 1, 1, 12, 2, 9), msecs(212)).dayOfGregorianCal == 182_622); - assert(SysTime(DateTime(1000, 1, 1, 12, 2, 9), msecs(212)).dayOfGregorianCal == 364_878); - assert(SysTime(DateTime(1001, 1, 1, 12, 2, 9), msecs(212)).dayOfGregorianCal == 365_243); - assert(SysTime(DateTime(1600, 1, 1, 12, 2, 9), msecs(212)).dayOfGregorianCal == 584_023); - assert(SysTime(DateTime(1601, 1, 1, 12, 2, 9), msecs(212)).dayOfGregorianCal == 584_389); - assert(SysTime(DateTime(1900, 1, 1, 12, 2, 9), msecs(212)).dayOfGregorianCal == 693_596); - assert(SysTime(DateTime(1901, 1, 1, 12, 2, 9), msecs(212)).dayOfGregorianCal == 693_961); - assert(SysTime(DateTime(1945, 11, 12, 12, 2, 9), msecs(212)).dayOfGregorianCal == 710_347); - assert(SysTime(DateTime(1999, 1, 1, 12, 2, 9), msecs(212)).dayOfGregorianCal == 729_755); - assert(SysTime(DateTime(2000, 1, 1, 12, 2, 9), msecs(212)).dayOfGregorianCal == 730_120); - assert(SysTime(DateTime(2001, 1, 1, 12, 2, 9), msecs(212)).dayOfGregorianCal == 730_486); - - assert(SysTime(DateTime(2010, 1, 1, 23, 59, 59), msecs(999)).dayOfGregorianCal == 733_773); - assert(SysTime(DateTime(2010, 1, 31, 23, 59, 59), msecs(999)).dayOfGregorianCal == 733_803); - assert(SysTime(DateTime(2010, 2, 1, 23, 59, 59), msecs(999)).dayOfGregorianCal == 733_804); - assert(SysTime(DateTime(2010, 2, 28, 23, 59, 59), msecs(999)).dayOfGregorianCal == 733_831); - assert(SysTime(DateTime(2010, 3, 1, 23, 59, 59), msecs(999)).dayOfGregorianCal == 733_832); - assert(SysTime(DateTime(2010, 3, 31, 23, 59, 59), msecs(999)).dayOfGregorianCal == 733_862); - assert(SysTime(DateTime(2010, 4, 1, 23, 59, 59), msecs(999)).dayOfGregorianCal == 733_863); - assert(SysTime(DateTime(2010, 4, 30, 23, 59, 59), msecs(999)).dayOfGregorianCal == 733_892); - assert(SysTime(DateTime(2010, 5, 1, 23, 59, 59), msecs(999)).dayOfGregorianCal == 733_893); - assert(SysTime(DateTime(2010, 5, 31, 23, 59, 59), msecs(999)).dayOfGregorianCal == 733_923); - assert(SysTime(DateTime(2010, 6, 1, 23, 59, 59), msecs(999)).dayOfGregorianCal == 733_924); - assert(SysTime(DateTime(2010, 6, 30, 23, 59, 59), msecs(999)).dayOfGregorianCal == 733_953); - assert(SysTime(DateTime(2010, 7, 1, 23, 59, 59), msecs(999)).dayOfGregorianCal == 733_954); - assert(SysTime(DateTime(2010, 7, 31, 23, 59, 59), msecs(999)).dayOfGregorianCal == 733_984); - assert(SysTime(DateTime(2010, 8, 1, 23, 59, 59), msecs(999)).dayOfGregorianCal == 733_985); - assert(SysTime(DateTime(2010, 8, 31, 23, 59, 59), msecs(999)).dayOfGregorianCal == 734_015); - assert(SysTime(DateTime(2010, 9, 1, 23, 59, 59), msecs(999)).dayOfGregorianCal == 734_016); - assert(SysTime(DateTime(2010, 9, 30, 23, 59, 59), msecs(999)).dayOfGregorianCal == 734_045); - assert(SysTime(DateTime(2010, 10, 1, 23, 59, 59), msecs(999)).dayOfGregorianCal == 734_046); - assert(SysTime(DateTime(2010, 10, 31, 23, 59, 59), msecs(999)).dayOfGregorianCal == 734_076); - assert(SysTime(DateTime(2010, 11, 1, 23, 59, 59), msecs(999)).dayOfGregorianCal == 734_077); - assert(SysTime(DateTime(2010, 11, 30, 23, 59, 59), msecs(999)).dayOfGregorianCal == 734_106); - assert(SysTime(DateTime(2010, 12, 1, 23, 59, 59), msecs(999)).dayOfGregorianCal == 734_107); - assert(SysTime(DateTime(2010, 12, 31, 23, 59, 59), msecs(999)).dayOfGregorianCal == 734_137); - - assert(SysTime(DateTime(2012, 2, 1, 0, 0, 0)).dayOfGregorianCal == 734_534); - assert(SysTime(DateTime(2012, 2, 28, 0, 0, 0)).dayOfGregorianCal == 734_561); - assert(SysTime(DateTime(2012, 2, 29, 0, 0, 0)).dayOfGregorianCal == 734_562); - assert(SysTime(DateTime(2012, 3, 1, 0, 0, 0)).dayOfGregorianCal == 734_563); - - // Test B.C. - assert(SysTime(DateTime(0, 12, 31, 23, 59, 59), hnsecs(9_999_999)).dayOfGregorianCal == 0); - assert(SysTime(DateTime(0, 12, 31, 23, 59, 59), hnsecs(9_999_998)).dayOfGregorianCal == 0); - assert(SysTime(DateTime(0, 12, 31, 23, 59, 59)).dayOfGregorianCal == 0); - assert(SysTime(DateTime(0, 12, 31, 0, 0, 0), hnsecs(1)).dayOfGregorianCal == 0); - assert(SysTime(DateTime(0, 12, 31, 0, 0, 0)).dayOfGregorianCal == 0); - - assert(SysTime(DateTime(-1, 12, 31, 23, 59, 59), hnsecs(9_999_999)).dayOfGregorianCal == -366); - assert(SysTime(DateTime(-1, 12, 31, 23, 59, 59), hnsecs(9_999_998)).dayOfGregorianCal == -366); - assert(SysTime(DateTime(-1, 12, 31, 23, 59, 59)).dayOfGregorianCal == -366); - assert(SysTime(DateTime(-1, 12, 31, 0, 0, 0)).dayOfGregorianCal == -366); - - assert(SysTime(DateTime(0, 12, 31, 12, 2, 9), msecs(212)).dayOfGregorianCal == 0); - assert(SysTime(DateTime(0, 12, 30, 12, 2, 9), msecs(212)).dayOfGregorianCal == -1); - assert(SysTime(DateTime(0, 12, 1, 12, 2, 9), msecs(212)).dayOfGregorianCal == -30); - assert(SysTime(DateTime(0, 11, 30, 12, 2, 9), msecs(212)).dayOfGregorianCal == -31); - - assert(SysTime(DateTime(-1, 12, 31, 12, 2, 9), msecs(212)).dayOfGregorianCal == -366); - assert(SysTime(DateTime(-1, 12, 30, 12, 2, 9), msecs(212)).dayOfGregorianCal == -367); - assert(SysTime(DateTime(-1, 1, 1, 12, 2, 9), msecs(212)).dayOfGregorianCal == -730); - assert(SysTime(DateTime(-2, 12, 31, 12, 2, 9), msecs(212)).dayOfGregorianCal == -731); - assert(SysTime(DateTime(-2, 1, 1, 12, 2, 9), msecs(212)).dayOfGregorianCal == -1095); - assert(SysTime(DateTime(-3, 12, 31, 12, 2, 9), msecs(212)).dayOfGregorianCal == -1096); - assert(SysTime(DateTime(-3, 1, 1, 12, 2, 9), msecs(212)).dayOfGregorianCal == -1460); - assert(SysTime(DateTime(-4, 12, 31, 12, 2, 9), msecs(212)).dayOfGregorianCal == -1461); - assert(SysTime(DateTime(-4, 1, 1, 12, 2, 9), msecs(212)).dayOfGregorianCal == -1826); - assert(SysTime(DateTime(-5, 12, 31, 12, 2, 9), msecs(212)).dayOfGregorianCal == -1827); - assert(SysTime(DateTime(-5, 1, 1, 12, 2, 9), msecs(212)).dayOfGregorianCal == -2191); - assert(SysTime(DateTime(-9, 1, 1, 12, 2, 9), msecs(212)).dayOfGregorianCal == -3652); - - assert(SysTime(DateTime(-49, 1, 1, 12, 2, 9), msecs(212)).dayOfGregorianCal == -18_262); - assert(SysTime(DateTime(-50, 1, 1, 12, 2, 9), msecs(212)).dayOfGregorianCal == -18_627); - assert(SysTime(DateTime(-97, 1, 1, 12, 2, 9), msecs(212)).dayOfGregorianCal == -35_794); - assert(SysTime(DateTime(-99, 12, 31, 12, 2, 9), msecs(212)).dayOfGregorianCal == -36_160); - assert(SysTime(DateTime(-99, 1, 1, 12, 2, 9), msecs(212)).dayOfGregorianCal == -36_524); - assert(SysTime(DateTime(-100, 1, 1, 12, 2, 9), msecs(212)).dayOfGregorianCal == -36_889); - assert(SysTime(DateTime(-101, 1, 1, 12, 2, 9), msecs(212)).dayOfGregorianCal == -37_254); - assert(SysTime(DateTime(-105, 1, 1, 12, 2, 9), msecs(212)).dayOfGregorianCal == -38_715); - assert(SysTime(DateTime(-200, 1, 1, 12, 2, 9), msecs(212)).dayOfGregorianCal == -73_413); - assert(SysTime(DateTime(-201, 1, 1, 12, 2, 9), msecs(212)).dayOfGregorianCal == -73_778); - assert(SysTime(DateTime(-300, 1, 1, 12, 2, 9), msecs(212)).dayOfGregorianCal == -109_937); - assert(SysTime(DateTime(-301, 1, 1, 12, 2, 9), msecs(212)).dayOfGregorianCal == -110_302); - assert(SysTime(DateTime(-400, 12, 31, 12, 2, 9), msecs(212)).dayOfGregorianCal == -146_097); - assert(SysTime(DateTime(-400, 1, 1, 12, 2, 9), msecs(212)).dayOfGregorianCal == -146_462); - assert(SysTime(DateTime(-401, 1, 1, 12, 2, 9), msecs(212)).dayOfGregorianCal == -146_827); - assert(SysTime(DateTime(-499, 1, 1, 12, 2, 9), msecs(212)).dayOfGregorianCal == -182_621); - assert(SysTime(DateTime(-500, 1, 1, 12, 2, 9), msecs(212)).dayOfGregorianCal == -182_986); - assert(SysTime(DateTime(-501, 1, 1, 12, 2, 9), msecs(212)).dayOfGregorianCal == -183_351); - assert(SysTime(DateTime(-1000, 1, 1, 12, 2, 9), msecs(212)).dayOfGregorianCal == -365_607); - assert(SysTime(DateTime(-1001, 1, 1, 12, 2, 9), msecs(212)).dayOfGregorianCal == -365_972); - assert(SysTime(DateTime(-1599, 1, 1, 12, 2, 9), msecs(212)).dayOfGregorianCal == -584_387); - assert(SysTime(DateTime(-1600, 12, 31, 12, 2, 9), msecs(212)).dayOfGregorianCal == -584_388); - assert(SysTime(DateTime(-1600, 1, 1, 12, 2, 9), msecs(212)).dayOfGregorianCal == -584_753); - assert(SysTime(DateTime(-1601, 1, 1, 12, 2, 9), msecs(212)).dayOfGregorianCal == -585_118); - assert(SysTime(DateTime(-1900, 1, 1, 12, 2, 9), msecs(212)).dayOfGregorianCal == -694_325); - assert(SysTime(DateTime(-1901, 1, 1, 12, 2, 9), msecs(212)).dayOfGregorianCal == -694_690); - assert(SysTime(DateTime(-1999, 1, 1, 12, 2, 9), msecs(212)).dayOfGregorianCal == -730_484); - assert(SysTime(DateTime(-2000, 12, 31, 12, 2, 9), msecs(212)).dayOfGregorianCal == -730_485); - assert(SysTime(DateTime(-2000, 1, 1, 12, 2, 9), msecs(212)).dayOfGregorianCal == -730_850); - assert(SysTime(DateTime(-2001, 1, 1, 12, 2, 9), msecs(212)).dayOfGregorianCal == -731_215); - - assert(SysTime(DateTime(-2010, 1, 1, 23, 59, 59), msecs(999)).dayOfGregorianCal == -734_502); - assert(SysTime(DateTime(-2010, 1, 31, 23, 59, 59), msecs(999)).dayOfGregorianCal == -734_472); - assert(SysTime(DateTime(-2010, 2, 1, 23, 59, 59), msecs(999)).dayOfGregorianCal == -734_471); - assert(SysTime(DateTime(-2010, 2, 28, 23, 59, 59), msecs(999)).dayOfGregorianCal == -734_444); - assert(SysTime(DateTime(-2010, 3, 1, 23, 59, 59), msecs(999)).dayOfGregorianCal == -734_443); - assert(SysTime(DateTime(-2010, 3, 31, 23, 59, 59), msecs(999)).dayOfGregorianCal == -734_413); - assert(SysTime(DateTime(-2010, 4, 1, 23, 59, 59), msecs(999)).dayOfGregorianCal == -734_412); - assert(SysTime(DateTime(-2010, 4, 30, 23, 59, 59), msecs(999)).dayOfGregorianCal == -734_383); - assert(SysTime(DateTime(-2010, 5, 1, 23, 59, 59), msecs(999)).dayOfGregorianCal == -734_382); - assert(SysTime(DateTime(-2010, 5, 31, 23, 59, 59), msecs(999)).dayOfGregorianCal == -734_352); - assert(SysTime(DateTime(-2010, 6, 1, 23, 59, 59), msecs(999)).dayOfGregorianCal == -734_351); - assert(SysTime(DateTime(-2010, 6, 30, 23, 59, 59), msecs(999)).dayOfGregorianCal == -734_322); - assert(SysTime(DateTime(-2010, 7, 1, 23, 59, 59), msecs(999)).dayOfGregorianCal == -734_321); - assert(SysTime(DateTime(-2010, 7, 31, 23, 59, 59), msecs(999)).dayOfGregorianCal == -734_291); - assert(SysTime(DateTime(-2010, 8, 1, 23, 59, 59), msecs(999)).dayOfGregorianCal == -734_290); - assert(SysTime(DateTime(-2010, 8, 31, 23, 59, 59), msecs(999)).dayOfGregorianCal == -734_260); - assert(SysTime(DateTime(-2010, 9, 1, 23, 59, 59), msecs(999)).dayOfGregorianCal == -734_259); - assert(SysTime(DateTime(-2010, 9, 30, 23, 59, 59), msecs(999)).dayOfGregorianCal == -734_230); - assert(SysTime(DateTime(-2010, 10, 1, 23, 59, 59), msecs(999)).dayOfGregorianCal == -734_229); - assert(SysTime(DateTime(-2010, 10, 31, 23, 59, 59), msecs(999)).dayOfGregorianCal == -734_199); - assert(SysTime(DateTime(-2010, 11, 1, 23, 59, 59), msecs(999)).dayOfGregorianCal == -734_198); - assert(SysTime(DateTime(-2010, 11, 30, 23, 59, 59), msecs(999)).dayOfGregorianCal == -734_169); - assert(SysTime(DateTime(-2010, 12, 1, 23, 59, 59), msecs(999)).dayOfGregorianCal == -734_168); - assert(SysTime(DateTime(-2010, 12, 31, 23, 59, 59), msecs(999)).dayOfGregorianCal == -734_138); - - assert(SysTime(DateTime(-2012, 2, 1, 0, 0, 0)).dayOfGregorianCal == -735_202); - assert(SysTime(DateTime(-2012, 2, 28, 0, 0, 0)).dayOfGregorianCal == -735_175); - assert(SysTime(DateTime(-2012, 2, 29, 0, 0, 0)).dayOfGregorianCal == -735_174); - assert(SysTime(DateTime(-2012, 3, 1, 0, 0, 0)).dayOfGregorianCal == -735_173); - - // Start of Hebrew Calendar - assert(SysTime(DateTime(-3760, 9, 7, 0, 0, 0)).dayOfGregorianCal == -1_373_427); - - const cst = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); - immutable ist = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); - assert(cst.dayOfGregorianCal == 729_941); - assert(ist.dayOfGregorianCal == 729_941); - - static void testScope(scope ref SysTime st) @safe - { - auto result = st.dayOfGregorianCal; - } - } - - - // Test that the logic for the day of the Gregorian Calendar is consistent - // between Date and SysTime. - @safe unittest - { - import core.time; - void test(Date date, SysTime st, size_t line = __LINE__) - { - if (date.dayOfGregorianCal != st.dayOfGregorianCal) - { - throw new AssertError(format("Date [%s] SysTime [%s]", date.dayOfGregorianCal, st.dayOfGregorianCal), - __FILE__, line); - } - } - - // Test A.D. - test(Date(1, 1, 1), SysTime(DateTime(1, 1, 1, 0, 0, 0))); - test(Date(1, 1, 2), SysTime(DateTime(1, 1, 2, 0, 0, 0), hnsecs(500))); - test(Date(1, 2, 1), SysTime(DateTime(1, 2, 1, 0, 0, 0), hnsecs(50_000))); - test(Date(2, 1, 1), SysTime(DateTime(2, 1, 1, 0, 0, 0), hnsecs(9_999_999))); - test(Date(3, 1, 1), SysTime(DateTime(3, 1, 1, 12, 13, 14))); - test(Date(4, 1, 1), SysTime(DateTime(4, 1, 1, 12, 13, 14), hnsecs(500))); - test(Date(5, 1, 1), SysTime(DateTime(5, 1, 1, 12, 13, 14), hnsecs(50_000))); - test(Date(50, 1, 1), SysTime(DateTime(50, 1, 1, 12, 13, 14), hnsecs(9_999_999))); - test(Date(97, 1, 1), SysTime(DateTime(97, 1, 1, 23, 59, 59))); - test(Date(100, 1, 1), SysTime(DateTime(100, 1, 1, 23, 59, 59), hnsecs(500))); - test(Date(101, 1, 1), SysTime(DateTime(101, 1, 1, 23, 59, 59), hnsecs(50_000))); - test(Date(105, 1, 1), SysTime(DateTime(105, 1, 1, 23, 59, 59), hnsecs(9_999_999))); - test(Date(200, 1, 1), SysTime(DateTime(200, 1, 1, 0, 0, 0))); - test(Date(201, 1, 1), SysTime(DateTime(201, 1, 1, 0, 0, 0), hnsecs(500))); - test(Date(300, 1, 1), SysTime(DateTime(300, 1, 1, 0, 0, 0), hnsecs(50_000))); - test(Date(301, 1, 1), SysTime(DateTime(301, 1, 1, 0, 0, 0), hnsecs(9_999_999))); - test(Date(400, 1, 1), SysTime(DateTime(400, 1, 1, 12, 13, 14))); - test(Date(401, 1, 1), SysTime(DateTime(401, 1, 1, 12, 13, 14), hnsecs(500))); - test(Date(500, 1, 1), SysTime(DateTime(500, 1, 1, 12, 13, 14), hnsecs(50_000))); - test(Date(501, 1, 1), SysTime(DateTime(501, 1, 1, 12, 13, 14), hnsecs(9_999_999))); - test(Date(1000, 1, 1), SysTime(DateTime(1000, 1, 1, 23, 59, 59))); - test(Date(1001, 1, 1), SysTime(DateTime(1001, 1, 1, 23, 59, 59), hnsecs(500))); - test(Date(1600, 1, 1), SysTime(DateTime(1600, 1, 1, 23, 59, 59), hnsecs(50_000))); - test(Date(1601, 1, 1), SysTime(DateTime(1601, 1, 1, 23, 59, 59), hnsecs(9_999_999))); - test(Date(1900, 1, 1), SysTime(DateTime(1900, 1, 1, 0, 0, 0))); - test(Date(1901, 1, 1), SysTime(DateTime(1901, 1, 1, 0, 0, 0), hnsecs(500))); - test(Date(1945, 11, 12), SysTime(DateTime(1945, 11, 12, 0, 0, 0), hnsecs(50_000))); - test(Date(1999, 1, 1), SysTime(DateTime(1999, 1, 1, 0, 0, 0), hnsecs(9_999_999))); - test(Date(1999, 7, 6), SysTime(DateTime(1999, 7, 6, 12, 13, 14))); - test(Date(2000, 1, 1), SysTime(DateTime(2000, 1, 1, 12, 13, 14), hnsecs(500))); - test(Date(2001, 1, 1), SysTime(DateTime(2001, 1, 1, 12, 13, 14), hnsecs(50_000))); - - test(Date(2010, 1, 1), SysTime(DateTime(2010, 1, 1, 12, 13, 14), hnsecs(9_999_999))); - test(Date(2010, 1, 31), SysTime(DateTime(2010, 1, 31, 23, 0, 0))); - test(Date(2010, 2, 1), SysTime(DateTime(2010, 2, 1, 23, 59, 59), hnsecs(500))); - test(Date(2010, 2, 28), SysTime(DateTime(2010, 2, 28, 23, 59, 59), hnsecs(50_000))); - test(Date(2010, 3, 1), SysTime(DateTime(2010, 3, 1, 23, 59, 59), hnsecs(9_999_999))); - test(Date(2010, 3, 31), SysTime(DateTime(2010, 3, 31, 0, 0, 0))); - test(Date(2010, 4, 1), SysTime(DateTime(2010, 4, 1, 0, 0, 0), hnsecs(500))); - test(Date(2010, 4, 30), SysTime(DateTime(2010, 4, 30, 0, 0, 0), hnsecs(50_000))); - test(Date(2010, 5, 1), SysTime(DateTime(2010, 5, 1, 0, 0, 0), hnsecs(9_999_999))); - test(Date(2010, 5, 31), SysTime(DateTime(2010, 5, 31, 12, 13, 14))); - test(Date(2010, 6, 1), SysTime(DateTime(2010, 6, 1, 12, 13, 14), hnsecs(500))); - test(Date(2010, 6, 30), SysTime(DateTime(2010, 6, 30, 12, 13, 14), hnsecs(50_000))); - test(Date(2010, 7, 1), SysTime(DateTime(2010, 7, 1, 12, 13, 14), hnsecs(9_999_999))); - test(Date(2010, 7, 31), SysTime(DateTime(2010, 7, 31, 23, 59, 59))); - test(Date(2010, 8, 1), SysTime(DateTime(2010, 8, 1, 23, 59, 59), hnsecs(500))); - test(Date(2010, 8, 31), SysTime(DateTime(2010, 8, 31, 23, 59, 59), hnsecs(50_000))); - test(Date(2010, 9, 1), SysTime(DateTime(2010, 9, 1, 23, 59, 59), hnsecs(9_999_999))); - test(Date(2010, 9, 30), SysTime(DateTime(2010, 9, 30, 12, 0, 0))); - test(Date(2010, 10, 1), SysTime(DateTime(2010, 10, 1, 0, 12, 0), hnsecs(500))); - test(Date(2010, 10, 31), SysTime(DateTime(2010, 10, 31, 0, 0, 12), hnsecs(50_000))); - test(Date(2010, 11, 1), SysTime(DateTime(2010, 11, 1, 23, 0, 0), hnsecs(9_999_999))); - test(Date(2010, 11, 30), SysTime(DateTime(2010, 11, 30, 0, 59, 0))); - test(Date(2010, 12, 1), SysTime(DateTime(2010, 12, 1, 0, 0, 59), hnsecs(500))); - test(Date(2010, 12, 31), SysTime(DateTime(2010, 12, 31, 0, 59, 59), hnsecs(50_000))); - - test(Date(2012, 2, 1), SysTime(DateTime(2012, 2, 1, 23, 0, 59), hnsecs(9_999_999))); - test(Date(2012, 2, 28), SysTime(DateTime(2012, 2, 28, 23, 59, 0))); - test(Date(2012, 2, 29), SysTime(DateTime(2012, 2, 29, 7, 7, 7), hnsecs(7))); - test(Date(2012, 3, 1), SysTime(DateTime(2012, 3, 1, 7, 7, 7), hnsecs(7))); - - // Test B.C. - test(Date(0, 12, 31), SysTime(DateTime(0, 12, 31, 0, 0, 0))); - test(Date(0, 12, 30), SysTime(DateTime(0, 12, 30, 0, 0, 0), hnsecs(500))); - test(Date(0, 12, 1), SysTime(DateTime(0, 12, 1, 0, 0, 0), hnsecs(50_000))); - test(Date(0, 11, 30), SysTime(DateTime(0, 11, 30, 0, 0, 0), hnsecs(9_999_999))); - - test(Date(-1, 12, 31), SysTime(DateTime(-1, 12, 31, 12, 13, 14))); - test(Date(-1, 12, 30), SysTime(DateTime(-1, 12, 30, 12, 13, 14), hnsecs(500))); - test(Date(-1, 1, 1), SysTime(DateTime(-1, 1, 1, 12, 13, 14), hnsecs(50_000))); - test(Date(-2, 12, 31), SysTime(DateTime(-2, 12, 31, 12, 13, 14), hnsecs(9_999_999))); - test(Date(-2, 1, 1), SysTime(DateTime(-2, 1, 1, 23, 59, 59))); - test(Date(-3, 12, 31), SysTime(DateTime(-3, 12, 31, 23, 59, 59), hnsecs(500))); - test(Date(-3, 1, 1), SysTime(DateTime(-3, 1, 1, 23, 59, 59), hnsecs(50_000))); - test(Date(-4, 12, 31), SysTime(DateTime(-4, 12, 31, 23, 59, 59), hnsecs(9_999_999))); - test(Date(-4, 1, 1), SysTime(DateTime(-4, 1, 1, 0, 0, 0))); - test(Date(-5, 12, 31), SysTime(DateTime(-5, 12, 31, 0, 0, 0), hnsecs(500))); - test(Date(-5, 1, 1), SysTime(DateTime(-5, 1, 1, 0, 0, 0), hnsecs(50_000))); - test(Date(-9, 1, 1), SysTime(DateTime(-9, 1, 1, 0, 0, 0), hnsecs(9_999_999))); - - test(Date(-49, 1, 1), SysTime(DateTime(-49, 1, 1, 12, 13, 14))); - test(Date(-50, 1, 1), SysTime(DateTime(-50, 1, 1, 12, 13, 14), hnsecs(500))); - test(Date(-97, 1, 1), SysTime(DateTime(-97, 1, 1, 12, 13, 14), hnsecs(50_000))); - test(Date(-99, 12, 31), SysTime(DateTime(-99, 12, 31, 12, 13, 14), hnsecs(9_999_999))); - test(Date(-99, 1, 1), SysTime(DateTime(-99, 1, 1, 23, 59, 59))); - test(Date(-100, 1, 1), SysTime(DateTime(-100, 1, 1, 23, 59, 59), hnsecs(500))); - test(Date(-101, 1, 1), SysTime(DateTime(-101, 1, 1, 23, 59, 59), hnsecs(50_000))); - test(Date(-105, 1, 1), SysTime(DateTime(-105, 1, 1, 23, 59, 59), hnsecs(9_999_999))); - test(Date(-200, 1, 1), SysTime(DateTime(-200, 1, 1, 0, 0, 0))); - test(Date(-201, 1, 1), SysTime(DateTime(-201, 1, 1, 0, 0, 0), hnsecs(500))); - test(Date(-300, 1, 1), SysTime(DateTime(-300, 1, 1, 0, 0, 0), hnsecs(50_000))); - test(Date(-301, 1, 1), SysTime(DateTime(-301, 1, 1, 0, 0, 0), hnsecs(9_999_999))); - test(Date(-400, 12, 31), SysTime(DateTime(-400, 12, 31, 12, 13, 14))); - test(Date(-400, 1, 1), SysTime(DateTime(-400, 1, 1, 12, 13, 14), hnsecs(500))); - test(Date(-401, 1, 1), SysTime(DateTime(-401, 1, 1, 12, 13, 14), hnsecs(50_000))); - test(Date(-499, 1, 1), SysTime(DateTime(-499, 1, 1, 12, 13, 14), hnsecs(9_999_999))); - test(Date(-500, 1, 1), SysTime(DateTime(-500, 1, 1, 23, 59, 59))); - test(Date(-501, 1, 1), SysTime(DateTime(-501, 1, 1, 23, 59, 59), hnsecs(500))); - test(Date(-1000, 1, 1), SysTime(DateTime(-1000, 1, 1, 23, 59, 59), hnsecs(50_000))); - test(Date(-1001, 1, 1), SysTime(DateTime(-1001, 1, 1, 23, 59, 59), hnsecs(9_999_999))); - test(Date(-1599, 1, 1), SysTime(DateTime(-1599, 1, 1, 0, 0, 0))); - test(Date(-1600, 12, 31), SysTime(DateTime(-1600, 12, 31, 0, 0, 0), hnsecs(500))); - test(Date(-1600, 1, 1), SysTime(DateTime(-1600, 1, 1, 0, 0, 0), hnsecs(50_000))); - test(Date(-1601, 1, 1), SysTime(DateTime(-1601, 1, 1, 0, 0, 0), hnsecs(9_999_999))); - test(Date(-1900, 1, 1), SysTime(DateTime(-1900, 1, 1, 12, 13, 14))); - test(Date(-1901, 1, 1), SysTime(DateTime(-1901, 1, 1, 12, 13, 14), hnsecs(500))); - test(Date(-1999, 1, 1), SysTime(DateTime(-1999, 1, 1, 12, 13, 14), hnsecs(50_000))); - test(Date(-1999, 7, 6), SysTime(DateTime(-1999, 7, 6, 12, 13, 14), hnsecs(9_999_999))); - test(Date(-2000, 12, 31), SysTime(DateTime(-2000, 12, 31, 23, 59, 59))); - test(Date(-2000, 1, 1), SysTime(DateTime(-2000, 1, 1, 23, 59, 59), hnsecs(500))); - test(Date(-2001, 1, 1), SysTime(DateTime(-2001, 1, 1, 23, 59, 59), hnsecs(50_000))); - - test(Date(-2010, 1, 1), SysTime(DateTime(-2010, 1, 1, 23, 59, 59), hnsecs(9_999_999))); - test(Date(-2010, 1, 31), SysTime(DateTime(-2010, 1, 31, 0, 0, 0))); - test(Date(-2010, 2, 1), SysTime(DateTime(-2010, 2, 1, 0, 0, 0), hnsecs(500))); - test(Date(-2010, 2, 28), SysTime(DateTime(-2010, 2, 28, 0, 0, 0), hnsecs(50_000))); - test(Date(-2010, 3, 1), SysTime(DateTime(-2010, 3, 1, 0, 0, 0), hnsecs(9_999_999))); - test(Date(-2010, 3, 31), SysTime(DateTime(-2010, 3, 31, 12, 13, 14))); - test(Date(-2010, 4, 1), SysTime(DateTime(-2010, 4, 1, 12, 13, 14), hnsecs(500))); - test(Date(-2010, 4, 30), SysTime(DateTime(-2010, 4, 30, 12, 13, 14), hnsecs(50_000))); - test(Date(-2010, 5, 1), SysTime(DateTime(-2010, 5, 1, 12, 13, 14), hnsecs(9_999_999))); - test(Date(-2010, 5, 31), SysTime(DateTime(-2010, 5, 31, 23, 59, 59))); - test(Date(-2010, 6, 1), SysTime(DateTime(-2010, 6, 1, 23, 59, 59), hnsecs(500))); - test(Date(-2010, 6, 30), SysTime(DateTime(-2010, 6, 30, 23, 59, 59), hnsecs(50_000))); - test(Date(-2010, 7, 1), SysTime(DateTime(-2010, 7, 1, 23, 59, 59), hnsecs(9_999_999))); - test(Date(-2010, 7, 31), SysTime(DateTime(-2010, 7, 31, 0, 0, 0))); - test(Date(-2010, 8, 1), SysTime(DateTime(-2010, 8, 1, 0, 0, 0), hnsecs(500))); - test(Date(-2010, 8, 31), SysTime(DateTime(-2010, 8, 31, 0, 0, 0), hnsecs(50_000))); - test(Date(-2010, 9, 1), SysTime(DateTime(-2010, 9, 1, 0, 0, 0), hnsecs(9_999_999))); - test(Date(-2010, 9, 30), SysTime(DateTime(-2010, 9, 30, 12, 0, 0))); - test(Date(-2010, 10, 1), SysTime(DateTime(-2010, 10, 1, 0, 12, 0), hnsecs(500))); - test(Date(-2010, 10, 31), SysTime(DateTime(-2010, 10, 31, 0, 0, 12), hnsecs(50_000))); - test(Date(-2010, 11, 1), SysTime(DateTime(-2010, 11, 1, 23, 0, 0), hnsecs(9_999_999))); - test(Date(-2010, 11, 30), SysTime(DateTime(-2010, 11, 30, 0, 59, 0))); - test(Date(-2010, 12, 1), SysTime(DateTime(-2010, 12, 1, 0, 0, 59), hnsecs(500))); - test(Date(-2010, 12, 31), SysTime(DateTime(-2010, 12, 31, 0, 59, 59), hnsecs(50_000))); - - test(Date(-2012, 2, 1), SysTime(DateTime(-2012, 2, 1, 23, 0, 59), hnsecs(9_999_999))); - test(Date(-2012, 2, 28), SysTime(DateTime(-2012, 2, 28, 23, 59, 0))); - test(Date(-2012, 2, 29), SysTime(DateTime(-2012, 2, 29, 7, 7, 7), hnsecs(7))); - test(Date(-2012, 3, 1), SysTime(DateTime(-2012, 3, 1, 7, 7, 7), hnsecs(7))); - - test(Date(-3760, 9, 7), SysTime(DateTime(-3760, 9, 7, 0, 0, 0))); - } - - - /++ - The Xth day of the Gregorian Calendar that this $(LREF SysTime) is on. - Setting this property does not affect the time portion of $(LREF SysTime). - - Params: - days = The day of the Gregorian Calendar to set this $(LREF SysTime) - to. - +/ - @property void dayOfGregorianCal(int days) @safe nothrow scope - { - auto hnsecs = adjTime; - hnsecs = removeUnitsFromHNSecs!"days"(hnsecs); - - if (hnsecs < 0) - hnsecs += convert!("hours", "hnsecs")(24); - - if (--days < 0) - { - hnsecs -= convert!("hours", "hnsecs")(24); - ++days; - } - - immutable newDaysHNSecs = convert!("days", "hnsecs")(days); - - adjTime = newDaysHNSecs + hnsecs; - } - - /// - @safe unittest - { - import core.time; - import std.datetime.date : DateTime; - - auto st = SysTime(DateTime(0, 1, 1, 12, 0, 0)); - st.dayOfGregorianCal = 1; - assert(st == SysTime(DateTime(1, 1, 1, 12, 0, 0))); - - st.dayOfGregorianCal = 365; - assert(st == SysTime(DateTime(1, 12, 31, 12, 0, 0))); - - st.dayOfGregorianCal = 366; - assert(st == SysTime(DateTime(2, 1, 1, 12, 0, 0))); - - st.dayOfGregorianCal = 0; - assert(st == SysTime(DateTime(0, 12, 31, 12, 0, 0))); - - st.dayOfGregorianCal = -365; - assert(st == SysTime(DateTime(-0, 1, 1, 12, 0, 0))); - - st.dayOfGregorianCal = -366; - assert(st == SysTime(DateTime(-1, 12, 31, 12, 0, 0))); - - st.dayOfGregorianCal = 730_120; - assert(st == SysTime(DateTime(2000, 1, 1, 12, 0, 0))); - - st.dayOfGregorianCal = 734_137; - assert(st == SysTime(DateTime(2010, 12, 31, 12, 0, 0))); - } - - @safe unittest - { - import core.time; - void testST(SysTime orig, int day, SysTime expected, size_t line = __LINE__) @safe - { - orig.dayOfGregorianCal = day; - if (orig != expected) - throw new AssertError(format("Failed. actual [%s] != expected [%s]", orig, expected), __FILE__, line); - } - - // Test A.D. - testST(SysTime(DateTime(1, 1, 1, 0, 0, 0)), 1, SysTime(DateTime(1, 1, 1, 0, 0, 0))); - testST(SysTime(DateTime(1, 1, 1, 0, 0, 0), hnsecs(1)), 1, SysTime(DateTime(1, 1, 1, 0, 0, 0), hnsecs(1))); - testST(SysTime(DateTime(1, 1, 1, 23, 59, 59), hnsecs(9_999_999)), 1, - SysTime(DateTime(1, 1, 1, 23, 59, 59), hnsecs(9_999_999))); - - // Test B.C. - testST(SysTime(DateTime(0, 1, 1, 0, 0, 0)), 0, SysTime(DateTime(0, 12, 31, 0, 0, 0))); - testST(SysTime(DateTime(0, 1, 1, 23, 59, 59), hnsecs(9_999_999)), 0, - SysTime(DateTime(0, 12, 31, 23, 59, 59), hnsecs(9_999_999))); - testST(SysTime(DateTime(0, 1, 1, 23, 59, 59), hnsecs(1)), 0, - SysTime(DateTime(0, 12, 31, 23, 59, 59), hnsecs(1))); - testST(SysTime(DateTime(0, 1, 1, 23, 59, 59)), 0, SysTime(DateTime(0, 12, 31, 23, 59, 59))); - - // Test Both. - testST(SysTime(DateTime(-512, 7, 20, 0, 0, 0)), 1, SysTime(DateTime(1, 1, 1, 0, 0, 0))); - testST(SysTime(DateTime(-513, 6, 6, 0, 0, 0), hnsecs(1)), 1, SysTime(DateTime(1, 1, 1, 0, 0, 0), hnsecs(1))); - testST(SysTime(DateTime(-511, 5, 7, 23, 59, 59), hnsecs(9_999_999)), 1, - SysTime(DateTime(1, 1, 1, 23, 59, 59), hnsecs(9_999_999))); - - testST(SysTime(DateTime(1607, 4, 8, 0, 0, 0)), 0, SysTime(DateTime(0, 12, 31, 0, 0, 0))); - testST(SysTime(DateTime(1500, 3, 9, 23, 59, 59), hnsecs(9_999_999)), 0, - SysTime(DateTime(0, 12, 31, 23, 59, 59), hnsecs(9_999_999))); - testST(SysTime(DateTime(999, 2, 10, 23, 59, 59), hnsecs(1)), 0, - SysTime(DateTime(0, 12, 31, 23, 59, 59), hnsecs(1))); - testST(SysTime(DateTime(2007, 12, 11, 23, 59, 59)), 0, SysTime(DateTime(0, 12, 31, 23, 59, 59))); - - - auto st = SysTime(DateTime(1, 1, 1, 12, 2, 9), msecs(212)); - - void testST2(int day, SysTime expected, size_t line = __LINE__) @safe - { - st.dayOfGregorianCal = day; - if (st != expected) - throw new AssertError(format("Failed. actual [%s] != expected [%s]", st, expected), __FILE__, line); - } - - // Test A.D. - testST2(1, SysTime(DateTime(1, 1, 1, 12, 2, 9), msecs(212))); - testST2(2, SysTime(DateTime(1, 1, 2, 12, 2, 9), msecs(212))); - testST2(32, SysTime(DateTime(1, 2, 1, 12, 2, 9), msecs(212))); - testST2(366, SysTime(DateTime(2, 1, 1, 12, 2, 9), msecs(212))); - testST2(731, SysTime(DateTime(3, 1, 1, 12, 2, 9), msecs(212))); - testST2(1096, SysTime(DateTime(4, 1, 1, 12, 2, 9), msecs(212))); - testST2(1462, SysTime(DateTime(5, 1, 1, 12, 2, 9), msecs(212))); - testST2(17_898, SysTime(DateTime(50, 1, 1, 12, 2, 9), msecs(212))); - testST2(35_065, SysTime(DateTime(97, 1, 1, 12, 2, 9), msecs(212))); - testST2(36_160, SysTime(DateTime(100, 1, 1, 12, 2, 9), msecs(212))); - testST2(36_525, SysTime(DateTime(101, 1, 1, 12, 2, 9), msecs(212))); - testST2(37_986, SysTime(DateTime(105, 1, 1, 12, 2, 9), msecs(212))); - testST2(72_684, SysTime(DateTime(200, 1, 1, 12, 2, 9), msecs(212))); - testST2(73_049, SysTime(DateTime(201, 1, 1, 12, 2, 9), msecs(212))); - testST2(109_208, SysTime(DateTime(300, 1, 1, 12, 2, 9), msecs(212))); - testST2(109_573, SysTime(DateTime(301, 1, 1, 12, 2, 9), msecs(212))); - testST2(145_732, SysTime(DateTime(400, 1, 1, 12, 2, 9), msecs(212))); - testST2(146_098, SysTime(DateTime(401, 1, 1, 12, 2, 9), msecs(212))); - testST2(182_257, SysTime(DateTime(500, 1, 1, 12, 2, 9), msecs(212))); - testST2(182_622, SysTime(DateTime(501, 1, 1, 12, 2, 9), msecs(212))); - testST2(364_878, SysTime(DateTime(1000, 1, 1, 12, 2, 9), msecs(212))); - testST2(365_243, SysTime(DateTime(1001, 1, 1, 12, 2, 9), msecs(212))); - testST2(584_023, SysTime(DateTime(1600, 1, 1, 12, 2, 9), msecs(212))); - testST2(584_389, SysTime(DateTime(1601, 1, 1, 12, 2, 9), msecs(212))); - testST2(693_596, SysTime(DateTime(1900, 1, 1, 12, 2, 9), msecs(212))); - testST2(693_961, SysTime(DateTime(1901, 1, 1, 12, 2, 9), msecs(212))); - testST2(729_755, SysTime(DateTime(1999, 1, 1, 12, 2, 9), msecs(212))); - testST2(730_120, SysTime(DateTime(2000, 1, 1, 12, 2, 9), msecs(212))); - testST2(730_486, SysTime(DateTime(2001, 1, 1, 12, 2, 9), msecs(212))); - - testST2(733_773, SysTime(DateTime(2010, 1, 1, 12, 2, 9), msecs(212))); - testST2(733_803, SysTime(DateTime(2010, 1, 31, 12, 2, 9), msecs(212))); - testST2(733_804, SysTime(DateTime(2010, 2, 1, 12, 2, 9), msecs(212))); - testST2(733_831, SysTime(DateTime(2010, 2, 28, 12, 2, 9), msecs(212))); - testST2(733_832, SysTime(DateTime(2010, 3, 1, 12, 2, 9), msecs(212))); - testST2(733_862, SysTime(DateTime(2010, 3, 31, 12, 2, 9), msecs(212))); - testST2(733_863, SysTime(DateTime(2010, 4, 1, 12, 2, 9), msecs(212))); - testST2(733_892, SysTime(DateTime(2010, 4, 30, 12, 2, 9), msecs(212))); - testST2(733_893, SysTime(DateTime(2010, 5, 1, 12, 2, 9), msecs(212))); - testST2(733_923, SysTime(DateTime(2010, 5, 31, 12, 2, 9), msecs(212))); - testST2(733_924, SysTime(DateTime(2010, 6, 1, 12, 2, 9), msecs(212))); - testST2(733_953, SysTime(DateTime(2010, 6, 30, 12, 2, 9), msecs(212))); - testST2(733_954, SysTime(DateTime(2010, 7, 1, 12, 2, 9), msecs(212))); - testST2(733_984, SysTime(DateTime(2010, 7, 31, 12, 2, 9), msecs(212))); - testST2(733_985, SysTime(DateTime(2010, 8, 1, 12, 2, 9), msecs(212))); - testST2(734_015, SysTime(DateTime(2010, 8, 31, 12, 2, 9), msecs(212))); - testST2(734_016, SysTime(DateTime(2010, 9, 1, 12, 2, 9), msecs(212))); - testST2(734_045, SysTime(DateTime(2010, 9, 30, 12, 2, 9), msecs(212))); - testST2(734_046, SysTime(DateTime(2010, 10, 1, 12, 2, 9), msecs(212))); - testST2(734_076, SysTime(DateTime(2010, 10, 31, 12, 2, 9), msecs(212))); - testST2(734_077, SysTime(DateTime(2010, 11, 1, 12, 2, 9), msecs(212))); - testST2(734_106, SysTime(DateTime(2010, 11, 30, 12, 2, 9), msecs(212))); - testST2(734_107, SysTime(DateTime(2010, 12, 1, 12, 2, 9), msecs(212))); - testST2(734_137, SysTime(DateTime(2010, 12, 31, 12, 2, 9), msecs(212))); - - testST2(734_534, SysTime(DateTime(2012, 2, 1, 12, 2, 9), msecs(212))); - testST2(734_561, SysTime(DateTime(2012, 2, 28, 12, 2, 9), msecs(212))); - testST2(734_562, SysTime(DateTime(2012, 2, 29, 12, 2, 9), msecs(212))); - testST2(734_563, SysTime(DateTime(2012, 3, 1, 12, 2, 9), msecs(212))); - - testST2(734_534, SysTime(DateTime(2012, 2, 1, 12, 2, 9), msecs(212))); - - testST2(734_561, SysTime(DateTime(2012, 2, 28, 12, 2, 9), msecs(212))); - testST2(734_562, SysTime(DateTime(2012, 2, 29, 12, 2, 9), msecs(212))); - testST2(734_563, SysTime(DateTime(2012, 3, 1, 12, 2, 9), msecs(212))); - - // Test B.C. - testST2(0, SysTime(DateTime(0, 12, 31, 12, 2, 9), msecs(212))); - testST2(-1, SysTime(DateTime(0, 12, 30, 12, 2, 9), msecs(212))); - testST2(-30, SysTime(DateTime(0, 12, 1, 12, 2, 9), msecs(212))); - testST2(-31, SysTime(DateTime(0, 11, 30, 12, 2, 9), msecs(212))); - - testST2(-366, SysTime(DateTime(-1, 12, 31, 12, 2, 9), msecs(212))); - testST2(-367, SysTime(DateTime(-1, 12, 30, 12, 2, 9), msecs(212))); - testST2(-730, SysTime(DateTime(-1, 1, 1, 12, 2, 9), msecs(212))); - testST2(-731, SysTime(DateTime(-2, 12, 31, 12, 2, 9), msecs(212))); - testST2(-1095, SysTime(DateTime(-2, 1, 1, 12, 2, 9), msecs(212))); - testST2(-1096, SysTime(DateTime(-3, 12, 31, 12, 2, 9), msecs(212))); - testST2(-1460, SysTime(DateTime(-3, 1, 1, 12, 2, 9), msecs(212))); - testST2(-1461, SysTime(DateTime(-4, 12, 31, 12, 2, 9), msecs(212))); - testST2(-1826, SysTime(DateTime(-4, 1, 1, 12, 2, 9), msecs(212))); - testST2(-1827, SysTime(DateTime(-5, 12, 31, 12, 2, 9), msecs(212))); - testST2(-2191, SysTime(DateTime(-5, 1, 1, 12, 2, 9), msecs(212))); - testST2(-3652, SysTime(DateTime(-9, 1, 1, 12, 2, 9), msecs(212))); - - testST2(-18_262, SysTime(DateTime(-49, 1, 1, 12, 2, 9), msecs(212))); - testST2(-18_627, SysTime(DateTime(-50, 1, 1, 12, 2, 9), msecs(212))); - testST2(-35_794, SysTime(DateTime(-97, 1, 1, 12, 2, 9), msecs(212))); - testST2(-36_160, SysTime(DateTime(-99, 12, 31, 12, 2, 9), msecs(212))); - testST2(-36_524, SysTime(DateTime(-99, 1, 1, 12, 2, 9), msecs(212))); - testST2(-36_889, SysTime(DateTime(-100, 1, 1, 12, 2, 9), msecs(212))); - testST2(-37_254, SysTime(DateTime(-101, 1, 1, 12, 2, 9), msecs(212))); - testST2(-38_715, SysTime(DateTime(-105, 1, 1, 12, 2, 9), msecs(212))); - testST2(-73_413, SysTime(DateTime(-200, 1, 1, 12, 2, 9), msecs(212))); - testST2(-73_778, SysTime(DateTime(-201, 1, 1, 12, 2, 9), msecs(212))); - testST2(-109_937, SysTime(DateTime(-300, 1, 1, 12, 2, 9), msecs(212))); - testST2(-110_302, SysTime(DateTime(-301, 1, 1, 12, 2, 9), msecs(212))); - testST2(-146_097, SysTime(DateTime(-400, 12, 31, 12, 2, 9), msecs(212))); - testST2(-146_462, SysTime(DateTime(-400, 1, 1, 12, 2, 9), msecs(212))); - testST2(-146_827, SysTime(DateTime(-401, 1, 1, 12, 2, 9), msecs(212))); - testST2(-182_621, SysTime(DateTime(-499, 1, 1, 12, 2, 9), msecs(212))); - testST2(-182_986, SysTime(DateTime(-500, 1, 1, 12, 2, 9), msecs(212))); - testST2(-183_351, SysTime(DateTime(-501, 1, 1, 12, 2, 9), msecs(212))); - testST2(-365_607, SysTime(DateTime(-1000, 1, 1, 12, 2, 9), msecs(212))); - testST2(-365_972, SysTime(DateTime(-1001, 1, 1, 12, 2, 9), msecs(212))); - testST2(-584_387, SysTime(DateTime(-1599, 1, 1, 12, 2, 9), msecs(212))); - testST2(-584_388, SysTime(DateTime(-1600, 12, 31, 12, 2, 9), msecs(212))); - testST2(-584_753, SysTime(DateTime(-1600, 1, 1, 12, 2, 9), msecs(212))); - testST2(-585_118, SysTime(DateTime(-1601, 1, 1, 12, 2, 9), msecs(212))); - testST2(-694_325, SysTime(DateTime(-1900, 1, 1, 12, 2, 9), msecs(212))); - testST2(-694_690, SysTime(DateTime(-1901, 1, 1, 12, 2, 9), msecs(212))); - testST2(-730_484, SysTime(DateTime(-1999, 1, 1, 12, 2, 9), msecs(212))); - testST2(-730_485, SysTime(DateTime(-2000, 12, 31, 12, 2, 9), msecs(212))); - testST2(-730_850, SysTime(DateTime(-2000, 1, 1, 12, 2, 9), msecs(212))); - testST2(-731_215, SysTime(DateTime(-2001, 1, 1, 12, 2, 9), msecs(212))); - - testST2(-734_502, SysTime(DateTime(-2010, 1, 1, 12, 2, 9), msecs(212))); - testST2(-734_472, SysTime(DateTime(-2010, 1, 31, 12, 2, 9), msecs(212))); - testST2(-734_471, SysTime(DateTime(-2010, 2, 1, 12, 2, 9), msecs(212))); - testST2(-734_444, SysTime(DateTime(-2010, 2, 28, 12, 2, 9), msecs(212))); - testST2(-734_443, SysTime(DateTime(-2010, 3, 1, 12, 2, 9), msecs(212))); - testST2(-734_413, SysTime(DateTime(-2010, 3, 31, 12, 2, 9), msecs(212))); - testST2(-734_412, SysTime(DateTime(-2010, 4, 1, 12, 2, 9), msecs(212))); - testST2(-734_383, SysTime(DateTime(-2010, 4, 30, 12, 2, 9), msecs(212))); - testST2(-734_382, SysTime(DateTime(-2010, 5, 1, 12, 2, 9), msecs(212))); - testST2(-734_352, SysTime(DateTime(-2010, 5, 31, 12, 2, 9), msecs(212))); - testST2(-734_351, SysTime(DateTime(-2010, 6, 1, 12, 2, 9), msecs(212))); - testST2(-734_322, SysTime(DateTime(-2010, 6, 30, 12, 2, 9), msecs(212))); - testST2(-734_321, SysTime(DateTime(-2010, 7, 1, 12, 2, 9), msecs(212))); - testST2(-734_291, SysTime(DateTime(-2010, 7, 31, 12, 2, 9), msecs(212))); - testST2(-734_290, SysTime(DateTime(-2010, 8, 1, 12, 2, 9), msecs(212))); - testST2(-734_260, SysTime(DateTime(-2010, 8, 31, 12, 2, 9), msecs(212))); - testST2(-734_259, SysTime(DateTime(-2010, 9, 1, 12, 2, 9), msecs(212))); - testST2(-734_230, SysTime(DateTime(-2010, 9, 30, 12, 2, 9), msecs(212))); - testST2(-734_229, SysTime(DateTime(-2010, 10, 1, 12, 2, 9), msecs(212))); - testST2(-734_199, SysTime(DateTime(-2010, 10, 31, 12, 2, 9), msecs(212))); - testST2(-734_198, SysTime(DateTime(-2010, 11, 1, 12, 2, 9), msecs(212))); - testST2(-734_169, SysTime(DateTime(-2010, 11, 30, 12, 2, 9), msecs(212))); - testST2(-734_168, SysTime(DateTime(-2010, 12, 1, 12, 2, 9), msecs(212))); - testST2(-734_138, SysTime(DateTime(-2010, 12, 31, 12, 2, 9), msecs(212))); - - testST2(-735_202, SysTime(DateTime(-2012, 2, 1, 12, 2, 9), msecs(212))); - testST2(-735_175, SysTime(DateTime(-2012, 2, 28, 12, 2, 9), msecs(212))); - testST2(-735_174, SysTime(DateTime(-2012, 2, 29, 12, 2, 9), msecs(212))); - testST2(-735_173, SysTime(DateTime(-2012, 3, 1, 12, 2, 9), msecs(212))); - - const cst = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); - immutable ist = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); - static assert(!__traits(compiles, cst.dayOfGregorianCal = 7)); - static assert(!__traits(compiles, ist.dayOfGregorianCal = 7)); - - static void testScope(scope ref SysTime st) @safe - { - st.dayOfGregorianCal = 42; - } - } - - - /++ - The ISO 8601 week of the year that this $(LREF SysTime) is in. - - See_Also: - $(HTTP en.wikipedia.org/wiki/ISO_week_date, ISO Week Date). - +/ - @property ubyte isoWeek() @safe const nothrow scope - { - return (cast(Date) this).isoWeek; - } - - /// - @safe unittest - { - import core.time; - import std.datetime.date : Date; - - auto st = SysTime(Date(1999, 7, 6)); - const cst = SysTime(Date(2010, 5, 1)); - immutable ist = SysTime(Date(2015, 10, 10)); - - assert(st.isoWeek == 27); - assert(cst.isoWeek == 17); - assert(ist.isoWeek == 41); - } - - @safe unittest - { - static void testScope(scope ref SysTime st) @safe - { - auto result = st.isoWeek; - } - } - - - /++ - $(LREF SysTime) for the last day in the month that this Date is in. - The time portion of endOfMonth is always 23:59:59.9999999. - +/ - @property SysTime endOfMonth() @safe const nothrow return scope - { - immutable hnsecs = adjTime; - immutable days = getUnitsFromHNSecs!"days"(hnsecs); - - auto date = Date(cast(int) days + 1).endOfMonth; - auto newDays = date.dayOfGregorianCal - 1; - long theTimeHNSecs; - - if (newDays < 0) - { - theTimeHNSecs = -1; - ++newDays; - } - else - theTimeHNSecs = convert!("days", "hnsecs")(1) - 1; - - immutable newDaysHNSecs = convert!("days", "hnsecs")(newDays); - - auto retval = SysTime(this._stdTime, this._timezone); - retval.adjTime = newDaysHNSecs + theTimeHNSecs; - - return retval; - } - - /// - @safe unittest - { - import core.time : msecs, usecs, hnsecs; - import std.datetime.date : DateTime; - - assert(SysTime(DateTime(1999, 1, 6, 0, 0, 0)).endOfMonth == - SysTime(DateTime(1999, 1, 31, 23, 59, 59), hnsecs(9_999_999))); - - assert(SysTime(DateTime(1999, 2, 7, 19, 30, 0), msecs(24)).endOfMonth == - SysTime(DateTime(1999, 2, 28, 23, 59, 59), hnsecs(9_999_999))); - - assert(SysTime(DateTime(2000, 2, 7, 5, 12, 27), usecs(5203)).endOfMonth == - SysTime(DateTime(2000, 2, 29, 23, 59, 59), hnsecs(9_999_999))); - - assert(SysTime(DateTime(2000, 6, 4, 12, 22, 9), hnsecs(12345)).endOfMonth == - SysTime(DateTime(2000, 6, 30, 23, 59, 59), hnsecs(9_999_999))); - } - - @safe unittest - { - import core.time; - // Test A.D. - assert(SysTime(Date(1999, 1, 1)).endOfMonth == SysTime(DateTime(1999, 1, 31, 23, 59, 59), hnsecs(9_999_999))); - assert(SysTime(Date(1999, 2, 1)).endOfMonth == SysTime(DateTime(1999, 2, 28, 23, 59, 59), hnsecs(9_999_999))); - assert(SysTime(Date(2000, 2, 1)).endOfMonth == SysTime(DateTime(2000, 2, 29, 23, 59, 59), hnsecs(9_999_999))); - assert(SysTime(Date(1999, 3, 1)).endOfMonth == SysTime(DateTime(1999, 3, 31, 23, 59, 59), hnsecs(9_999_999))); - assert(SysTime(Date(1999, 4, 1)).endOfMonth == SysTime(DateTime(1999, 4, 30, 23, 59, 59), hnsecs(9_999_999))); - assert(SysTime(Date(1999, 5, 1)).endOfMonth == SysTime(DateTime(1999, 5, 31, 23, 59, 59), hnsecs(9_999_999))); - assert(SysTime(Date(1999, 6, 1)).endOfMonth == SysTime(DateTime(1999, 6, 30, 23, 59, 59), hnsecs(9_999_999))); - assert(SysTime(Date(1999, 7, 1)).endOfMonth == SysTime(DateTime(1999, 7, 31, 23, 59, 59), hnsecs(9_999_999))); - assert(SysTime(Date(1999, 8, 1)).endOfMonth == SysTime(DateTime(1999, 8, 31, 23, 59, 59), hnsecs(9_999_999))); - assert(SysTime(Date(1999, 9, 1)).endOfMonth == SysTime(DateTime(1999, 9, 30, 23, 59, 59), hnsecs(9_999_999))); - assert(SysTime(Date(1999, 10, 1)).endOfMonth == SysTime(DateTime(1999, 10, 31, 23, 59, 59), hnsecs(9_999_999))); - assert(SysTime(Date(1999, 11, 1)).endOfMonth == SysTime(DateTime(1999, 11, 30, 23, 59, 59), hnsecs(9_999_999))); - assert(SysTime(Date(1999, 12, 1)).endOfMonth == SysTime(DateTime(1999, 12, 31, 23, 59, 59), hnsecs(9_999_999))); - - // Test B.C. - assert(SysTime(Date(-1999, 1, 1)).endOfMonth == SysTime(DateTime(-1999, 1, 31, 23, 59, 59), hnsecs(9_999_999))); - assert(SysTime(Date(-1999, 2, 1)).endOfMonth == SysTime(DateTime(-1999, 2, 28, 23, 59, 59), hnsecs(9_999_999))); - assert(SysTime(Date(-2000, 2, 1)).endOfMonth == SysTime(DateTime(-2000, 2, 29, 23, 59, 59), hnsecs(9_999_999))); - assert(SysTime(Date(-1999, 3, 1)).endOfMonth == SysTime(DateTime(-1999, 3, 31, 23, 59, 59), hnsecs(9_999_999))); - assert(SysTime(Date(-1999, 4, 1)).endOfMonth == SysTime(DateTime(-1999, 4, 30, 23, 59, 59), hnsecs(9_999_999))); - assert(SysTime(Date(-1999, 5, 1)).endOfMonth == SysTime(DateTime(-1999, 5, 31, 23, 59, 59), hnsecs(9_999_999))); - assert(SysTime(Date(-1999, 6, 1)).endOfMonth == SysTime(DateTime(-1999, 6, 30, 23, 59, 59), hnsecs(9_999_999))); - assert(SysTime(Date(-1999, 7, 1)).endOfMonth == SysTime(DateTime(-1999, 7, 31, 23, 59, 59), hnsecs(9_999_999))); - assert(SysTime(Date(-1999, 8, 1)).endOfMonth == SysTime(DateTime(-1999, 8, 31, 23, 59, 59), hnsecs(9_999_999))); - assert(SysTime(Date(-1999, 9, 1)).endOfMonth == SysTime(DateTime(-1999, 9, 30, 23, 59, 59), hnsecs(9_999_999))); - assert(SysTime(Date(-1999, 10, 1)).endOfMonth == - SysTime(DateTime(-1999, 10, 31, 23, 59, 59), hnsecs(9_999_999))); - assert(SysTime(Date(-1999, 11, 1)).endOfMonth == - SysTime(DateTime(-1999, 11, 30, 23, 59, 59), hnsecs(9_999_999))); - assert(SysTime(Date(-1999, 12, 1)).endOfMonth == - SysTime(DateTime(-1999, 12, 31, 23, 59, 59), hnsecs(9_999_999))); - - const cst = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); - immutable ist = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); - assert(cst.endOfMonth == SysTime(DateTime(1999, 7, 31, 23, 59, 59), hnsecs(9_999_999))); - assert(ist.endOfMonth == SysTime(DateTime(1999, 7, 31, 23, 59, 59), hnsecs(9_999_999))); - - static void testScope(scope ref SysTime st) @safe - { - auto result = st.endOfMonth; - } - } - - - /++ - The last day in the month that this $(LREF SysTime) is in. - +/ - @property ubyte daysInMonth() @safe const nothrow scope - { - return Date(dayOfGregorianCal).daysInMonth; - } - - /// - @safe unittest - { - import core.time; - import std.datetime.date : DateTime; - - assert(SysTime(DateTime(1999, 1, 6, 0, 0, 0)).daysInMonth == 31); - assert(SysTime(DateTime(1999, 2, 7, 19, 30, 0)).daysInMonth == 28); - assert(SysTime(DateTime(2000, 2, 7, 5, 12, 27)).daysInMonth == 29); - assert(SysTime(DateTime(2000, 6, 4, 12, 22, 9)).daysInMonth == 30); - } - - @safe unittest - { - import core.time; - // Test A.D. - assert(SysTime(DateTime(1999, 1, 1, 12, 1, 13)).daysInMonth == 31); - assert(SysTime(DateTime(1999, 2, 1, 17, 13, 12)).daysInMonth == 28); - assert(SysTime(DateTime(2000, 2, 1, 13, 2, 12)).daysInMonth == 29); - assert(SysTime(DateTime(1999, 3, 1, 12, 13, 12)).daysInMonth == 31); - assert(SysTime(DateTime(1999, 4, 1, 12, 6, 13)).daysInMonth == 30); - assert(SysTime(DateTime(1999, 5, 1, 15, 13, 12)).daysInMonth == 31); - assert(SysTime(DateTime(1999, 6, 1, 13, 7, 12)).daysInMonth == 30); - assert(SysTime(DateTime(1999, 7, 1, 12, 13, 17)).daysInMonth == 31); - assert(SysTime(DateTime(1999, 8, 1, 12, 3, 13)).daysInMonth == 31); - assert(SysTime(DateTime(1999, 9, 1, 12, 13, 12)).daysInMonth == 30); - assert(SysTime(DateTime(1999, 10, 1, 13, 19, 12)).daysInMonth == 31); - assert(SysTime(DateTime(1999, 11, 1, 12, 13, 17)).daysInMonth == 30); - assert(SysTime(DateTime(1999, 12, 1, 12, 52, 13)).daysInMonth == 31); - - // Test B.C. - assert(SysTime(DateTime(-1999, 1, 1, 12, 1, 13)).daysInMonth == 31); - assert(SysTime(DateTime(-1999, 2, 1, 7, 13, 12)).daysInMonth == 28); - assert(SysTime(DateTime(-2000, 2, 1, 13, 2, 12)).daysInMonth == 29); - assert(SysTime(DateTime(-1999, 3, 1, 12, 13, 12)).daysInMonth == 31); - assert(SysTime(DateTime(-1999, 4, 1, 12, 6, 13)).daysInMonth == 30); - assert(SysTime(DateTime(-1999, 5, 1, 5, 13, 12)).daysInMonth == 31); - assert(SysTime(DateTime(-1999, 6, 1, 13, 7, 12)).daysInMonth == 30); - assert(SysTime(DateTime(-1999, 7, 1, 12, 13, 17)).daysInMonth == 31); - assert(SysTime(DateTime(-1999, 8, 1, 12, 3, 13)).daysInMonth == 31); - assert(SysTime(DateTime(-1999, 9, 1, 12, 13, 12)).daysInMonth == 30); - assert(SysTime(DateTime(-1999, 10, 1, 13, 19, 12)).daysInMonth == 31); - assert(SysTime(DateTime(-1999, 11, 1, 12, 13, 17)).daysInMonth == 30); - assert(SysTime(DateTime(-1999, 12, 1, 12, 52, 13)).daysInMonth == 31); - - const cst = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); - immutable ist = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); - assert(cst.daysInMonth == 31); - assert(ist.daysInMonth == 31); - - static void testScope(scope ref SysTime st) @safe - { - auto result = st.daysInMonth; - } - } - - - /++ - Whether the current year is a date in A.D. - +/ - @property bool isAD() @safe const nothrow scope - { - return adjTime >= 0; - } - - /// - @safe unittest - { - import core.time; - import std.datetime.date : DateTime; - - assert(SysTime(DateTime(1, 1, 1, 12, 7, 0)).isAD); - assert(SysTime(DateTime(2010, 12, 31, 0, 0, 0)).isAD); - assert(!SysTime(DateTime(0, 12, 31, 23, 59, 59)).isAD); - assert(!SysTime(DateTime(-2010, 1, 1, 2, 2, 2)).isAD); - } - - @safe unittest - { - import core.time; - assert(SysTime(DateTime(2010, 7, 4, 12, 0, 9)).isAD); - assert(SysTime(DateTime(1, 1, 1, 0, 0, 0)).isAD); - assert(!SysTime(DateTime(0, 12, 31, 23, 59, 59)).isAD); - assert(!SysTime(DateTime(0, 1, 1, 23, 59, 59)).isAD); - assert(!SysTime(DateTime(-1, 1, 1, 23 ,59 ,59)).isAD); - assert(!SysTime(DateTime(-2010, 7, 4, 12, 2, 2)).isAD); - - const cst = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); - immutable ist = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); - assert(cst.isAD); - assert(ist.isAD); - - static void testScope(scope ref SysTime st) @safe - { - auto result = st.isAD; - } - } - - - /++ - The $(HTTP en.wikipedia.org/wiki/Julian_day, Julian day) - for this $(LREF SysTime) at the given time. For example, - prior to noon, 1996-03-31 would be the Julian day number 2_450_173, so - this function returns 2_450_173, while from noon onward, the Julian - day number would be 2_450_174, so this function returns 2_450_174. - +/ - @property long julianDay() @safe const nothrow scope - { - immutable jd = dayOfGregorianCal + 1_721_425; - return hour < 12 ? jd - 1 : jd; - } - - @safe unittest - { - import core.time; - assert(SysTime(DateTime(-4713, 11, 24, 0, 0, 0)).julianDay == -1); - assert(SysTime(DateTime(-4713, 11, 24, 12, 0, 0)).julianDay == 0); - - assert(SysTime(DateTime(0, 12, 31, 0, 0, 0)).julianDay == 1_721_424); - assert(SysTime(DateTime(0, 12, 31, 12, 0, 0)).julianDay == 1_721_425); - - assert(SysTime(DateTime(1, 1, 1, 0, 0, 0)).julianDay == 1_721_425); - assert(SysTime(DateTime(1, 1, 1, 12, 0, 0)).julianDay == 1_721_426); - - assert(SysTime(DateTime(1582, 10, 15, 0, 0, 0)).julianDay == 2_299_160); - assert(SysTime(DateTime(1582, 10, 15, 12, 0, 0)).julianDay == 2_299_161); - - assert(SysTime(DateTime(1858, 11, 17, 0, 0, 0)).julianDay == 2_400_000); - assert(SysTime(DateTime(1858, 11, 17, 12, 0, 0)).julianDay == 2_400_001); - - assert(SysTime(DateTime(1982, 1, 4, 0, 0, 0)).julianDay == 2_444_973); - assert(SysTime(DateTime(1982, 1, 4, 12, 0, 0)).julianDay == 2_444_974); - - assert(SysTime(DateTime(1996, 3, 31, 0, 0, 0)).julianDay == 2_450_173); - assert(SysTime(DateTime(1996, 3, 31, 12, 0, 0)).julianDay == 2_450_174); - - assert(SysTime(DateTime(2010, 8, 24, 0, 0, 0)).julianDay == 2_455_432); - assert(SysTime(DateTime(2010, 8, 24, 12, 0, 0)).julianDay == 2_455_433); - - const cst = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); - immutable ist = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); - assert(cst.julianDay == 2_451_366); - assert(ist.julianDay == 2_451_366); - - static void testScope(scope ref SysTime st) @safe - { - auto result = st.julianDay; - } - } - - - /++ - The modified $(HTTP en.wikipedia.org/wiki/Julian_day, Julian day) for - any time on this date (since, the modified Julian day changes at - midnight). - +/ - @property long modJulianDay() @safe const nothrow scope - { - return dayOfGregorianCal + 1_721_425 - 2_400_001; - } - - @safe unittest - { - import core.time; - assert(SysTime(DateTime(1858, 11, 17, 0, 0, 0)).modJulianDay == 0); - assert(SysTime(DateTime(1858, 11, 17, 12, 0, 0)).modJulianDay == 0); - - assert(SysTime(DateTime(2010, 8, 24, 0, 0, 0)).modJulianDay == 55_432); - assert(SysTime(DateTime(2010, 8, 24, 12, 0, 0)).modJulianDay == 55_432); - - const cst = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); - immutable ist = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); - assert(cst.modJulianDay == 51_365); - assert(ist.modJulianDay == 51_365); - - static void testScope(scope ref SysTime st) @safe - { - auto result = st.modJulianDay; - } - } - - - /++ - Returns a $(REF Date,std,datetime,date) equivalent to this $(LREF SysTime). - +/ - Date opCast(T)() @safe const nothrow scope - if (is(immutable T == immutable Date)) - { - return Date(dayOfGregorianCal); - } - - @safe unittest - { - import core.time; - assert(cast(Date) SysTime(Date(1999, 7, 6)) == Date(1999, 7, 6)); - assert(cast(Date) SysTime(Date(2000, 12, 31)) == Date(2000, 12, 31)); - assert(cast(Date) SysTime(Date(2001, 1, 1)) == Date(2001, 1, 1)); - - assert(cast(Date) SysTime(DateTime(1999, 7, 6, 12, 10, 9)) == Date(1999, 7, 6)); - assert(cast(Date) SysTime(DateTime(2000, 12, 31, 13, 11, 10)) == Date(2000, 12, 31)); - assert(cast(Date) SysTime(DateTime(2001, 1, 1, 14, 12, 11)) == Date(2001, 1, 1)); - - assert(cast(Date) SysTime(Date(-1999, 7, 6)) == Date(-1999, 7, 6)); - assert(cast(Date) SysTime(Date(-2000, 12, 31)) == Date(-2000, 12, 31)); - assert(cast(Date) SysTime(Date(-2001, 1, 1)) == Date(-2001, 1, 1)); - - assert(cast(Date) SysTime(DateTime(-1999, 7, 6, 12, 10, 9)) == Date(-1999, 7, 6)); - assert(cast(Date) SysTime(DateTime(-2000, 12, 31, 13, 11, 10)) == Date(-2000, 12, 31)); - assert(cast(Date) SysTime(DateTime(-2001, 1, 1, 14, 12, 11)) == Date(-2001, 1, 1)); - - const cst = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); - immutable ist = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); - assert(cast(Date) cst != Date.init); - assert(cast(Date) ist != Date.init); - - static void testScope(scope ref SysTime st) @safe - { - auto result = cast(Date) st; - } - } - - - /++ - Returns a $(REF DateTime,std,datetime,date) equivalent to this - $(LREF SysTime). - +/ - DateTime opCast(T)() @safe const nothrow scope - if (is(immutable T == immutable DateTime)) - { - try - { - auto hnsecs = adjTime; - auto days = splitUnitsFromHNSecs!"days"(hnsecs) + 1; - - if (hnsecs < 0) - { - hnsecs += convert!("hours", "hnsecs")(24); - --days; - } - - immutable hour = splitUnitsFromHNSecs!"hours"(hnsecs); - immutable minute = splitUnitsFromHNSecs!"minutes"(hnsecs); - immutable second = getUnitsFromHNSecs!"seconds"(hnsecs); - - return DateTime(Date(cast(int) days), TimeOfDay(cast(int) hour, cast(int) minute, cast(int) second)); - } - catch (Exception e) - assert(0, "Either DateTime's constructor or TimeOfDay's constructor threw."); - } - - @safe unittest - { - import core.time; - assert(cast(DateTime) SysTime(DateTime(1, 1, 6, 7, 12, 22)) == DateTime(1, 1, 6, 7, 12, 22)); - assert(cast(DateTime) SysTime(DateTime(1, 1, 6, 7, 12, 22), msecs(22)) == DateTime(1, 1, 6, 7, 12, 22)); - assert(cast(DateTime) SysTime(Date(1999, 7, 6)) == DateTime(1999, 7, 6, 0, 0, 0)); - assert(cast(DateTime) SysTime(Date(2000, 12, 31)) == DateTime(2000, 12, 31, 0, 0, 0)); - assert(cast(DateTime) SysTime(Date(2001, 1, 1)) == DateTime(2001, 1, 1, 0, 0, 0)); - - assert(cast(DateTime) SysTime(DateTime(1999, 7, 6, 12, 10, 9)) == DateTime(1999, 7, 6, 12, 10, 9)); - assert(cast(DateTime) SysTime(DateTime(2000, 12, 31, 13, 11, 10)) == DateTime(2000, 12, 31, 13, 11, 10)); - assert(cast(DateTime) SysTime(DateTime(2001, 1, 1, 14, 12, 11)) == DateTime(2001, 1, 1, 14, 12, 11)); - - assert(cast(DateTime) SysTime(DateTime(-1, 1, 6, 7, 12, 22)) == DateTime(-1, 1, 6, 7, 12, 22)); - assert(cast(DateTime) SysTime(DateTime(-1, 1, 6, 7, 12, 22), msecs(22)) == DateTime(-1, 1, 6, 7, 12, 22)); - assert(cast(DateTime) SysTime(Date(-1999, 7, 6)) == DateTime(-1999, 7, 6, 0, 0, 0)); - assert(cast(DateTime) SysTime(Date(-2000, 12, 31)) == DateTime(-2000, 12, 31, 0, 0, 0)); - assert(cast(DateTime) SysTime(Date(-2001, 1, 1)) == DateTime(-2001, 1, 1, 0, 0, 0)); - - assert(cast(DateTime) SysTime(DateTime(-1999, 7, 6, 12, 10, 9)) == DateTime(-1999, 7, 6, 12, 10, 9)); - assert(cast(DateTime) SysTime(DateTime(-2000, 12, 31, 13, 11, 10)) == DateTime(-2000, 12, 31, 13, 11, 10)); - assert(cast(DateTime) SysTime(DateTime(-2001, 1, 1, 14, 12, 11)) == DateTime(-2001, 1, 1, 14, 12, 11)); - - assert(cast(DateTime) SysTime(DateTime(2011, 1, 13, 8, 17, 2), msecs(296), LocalTime()) == - DateTime(2011, 1, 13, 8, 17, 2)); - - const cst = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); - immutable ist = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); - assert(cast(DateTime) cst != DateTime.init); - assert(cast(DateTime) ist != DateTime.init); - - static void testScope(scope ref SysTime st) @safe - { - auto result = cast(DateTime) st; - } - } - - - /++ - Returns a $(REF TimeOfDay,std,datetime,date) equivalent to this - $(LREF SysTime). - +/ - TimeOfDay opCast(T)() @safe const nothrow scope - if (is(immutable T == immutable TimeOfDay)) - { - try - { - auto hnsecs = adjTime; - hnsecs = removeUnitsFromHNSecs!"days"(hnsecs); - - if (hnsecs < 0) - hnsecs += convert!("hours", "hnsecs")(24); - - immutable hour = splitUnitsFromHNSecs!"hours"(hnsecs); - immutable minute = splitUnitsFromHNSecs!"minutes"(hnsecs); - immutable second = getUnitsFromHNSecs!"seconds"(hnsecs); - - return TimeOfDay(cast(int) hour, cast(int) minute, cast(int) second); - } - catch (Exception e) - assert(0, "TimeOfDay's constructor threw."); - } - - @safe unittest - { - import core.time; - assert(cast(TimeOfDay) SysTime(Date(1999, 7, 6)) == TimeOfDay(0, 0, 0)); - assert(cast(TimeOfDay) SysTime(Date(2000, 12, 31)) == TimeOfDay(0, 0, 0)); - assert(cast(TimeOfDay) SysTime(Date(2001, 1, 1)) == TimeOfDay(0, 0, 0)); - - assert(cast(TimeOfDay) SysTime(DateTime(1999, 7, 6, 12, 10, 9)) == TimeOfDay(12, 10, 9)); - assert(cast(TimeOfDay) SysTime(DateTime(2000, 12, 31, 13, 11, 10)) == TimeOfDay(13, 11, 10)); - assert(cast(TimeOfDay) SysTime(DateTime(2001, 1, 1, 14, 12, 11)) == TimeOfDay(14, 12, 11)); - - assert(cast(TimeOfDay) SysTime(Date(-1999, 7, 6)) == TimeOfDay(0, 0, 0)); - assert(cast(TimeOfDay) SysTime(Date(-2000, 12, 31)) == TimeOfDay(0, 0, 0)); - assert(cast(TimeOfDay) SysTime(Date(-2001, 1, 1)) == TimeOfDay(0, 0, 0)); - - assert(cast(TimeOfDay) SysTime(DateTime(-1999, 7, 6, 12, 10, 9)) == TimeOfDay(12, 10, 9)); - assert(cast(TimeOfDay) SysTime(DateTime(-2000, 12, 31, 13, 11, 10)) == TimeOfDay(13, 11, 10)); - assert(cast(TimeOfDay) SysTime(DateTime(-2001, 1, 1, 14, 12, 11)) == TimeOfDay(14, 12, 11)); - - const cst = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); - immutable ist = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); - assert(cast(TimeOfDay) cst != TimeOfDay.init); - assert(cast(TimeOfDay) ist != TimeOfDay.init); - - static void testScope(scope ref SysTime st) @safe - { - auto result = cast(TimeOfDay) st; - } - } - - - // Temporary hack until bug https://issues.dlang.org/show_bug.cgi?id=4867 is fixed. - // This allows assignment from const(SysTime) to SysTime. - // It may be a good idea to keep it though, since casting from a type to itself - // should be allowed, and it doesn't work without this opCast() since opCast() - // has already been defined for other types. - SysTime opCast(T)() @safe const pure nothrow scope - if (is(immutable T == immutable SysTime)) - { - return SysTime(_stdTime, _timezone); - } - - @safe unittest - { - static void testScope(scope ref SysTime st) @safe - { - auto result = cast(SysTime) st; - } - } - - - /++ - Converts this $(LREF SysTime) to a string with the format - YYYYMMDDTHHMMSS.FFFFFFFTZ (where F is fractional seconds and TZ is time - zone). - - Note that the number of digits in the fractional seconds varies with the - number of fractional seconds. It's a maximum of 7 (which would be - hnsecs), but only has as many as are necessary to hold the correct value - (so no trailing zeroes), and if there are no fractional seconds, then - there is no decimal point. - - If this $(LREF SysTime)'s time zone is - $(REF LocalTime,std,datetime,timezone), then TZ is empty. If its time - zone is `UTC`, then it is "Z". Otherwise, it is the offset from UTC - (e.g. +0100 or -0700). Note that the offset from UTC is $(I not) enough - to uniquely identify the time zone. - - Time zone offsets will be in the form +HHMM or -HHMM. - - $(RED Warning: - Previously, toISOString did the same as $(LREF toISOExtString) and - generated +HH:MM or -HH:MM for the time zone when it was not - $(REF LocalTime,std,datetime,timezone) or - $(REF UTC,std,datetime,timezone), which is not in conformance with - ISO 8601 for the non-extended string format. This has now been - fixed. However, for now, fromISOString will continue to accept the - extended format for the time zone so that any code which has been - writing out the result of toISOString to read in later will continue - to work. The current behavior will be kept until July 2019 at which - point, fromISOString will be fixed to be standards compliant.) - - Params: - writer = A `char` accepting - $(REF_ALTTEXT output range, isOutputRange, std, range, primitives) - Returns: - A `string` when not using an output range; `void` otherwise. - +/ - string toISOString() @safe const nothrow scope - { - import std.array : appender; - auto app = appender!string(); - app.reserve(30); - try - toISOString(app); - catch (Exception e) - assert(0, "toISOString() threw."); - return app.data; - } - - /// ditto - void toISOString(W)(ref W writer) const scope - if (isOutputRange!(W, char)) - { - immutable adjustedTime = adjTime; - long hnsecs = adjustedTime; - - auto days = splitUnitsFromHNSecs!"days"(hnsecs) + 1; - - if (hnsecs < 0) - { - hnsecs += convert!("hours", "hnsecs")(24); - --days; - } - - immutable hour = splitUnitsFromHNSecs!"hours"(hnsecs); - immutable minute = splitUnitsFromHNSecs!"minutes"(hnsecs); - immutable second = splitUnitsFromHNSecs!"seconds"(hnsecs); - - auto dateTime = DateTime(Date(cast(int) days), TimeOfDay(cast(int) hour, - cast(int) minute, cast(int) second)); - - if (_timezone is LocalTime()) - { - dateTime.toISOString(writer); - fracSecsToISOString(writer, cast(int) hnsecs); - return; - } - - if (_timezone is UTC()) - { - dateTime.toISOString(writer); - fracSecsToISOString(writer, cast(int) hnsecs); - put(writer, 'Z'); - return; - } - - immutable utcOffset = dur!"hnsecs"(adjustedTime - stdTime); - - dateTime.toISOString(writer); - fracSecsToISOString(writer, cast(int) hnsecs); - SimpleTimeZone.toISOExtString(writer, utcOffset); - } - - /// - @safe unittest - { - import core.time : msecs, hnsecs; - import std.datetime.date : DateTime; - - assert(SysTime(DateTime(2010, 7, 4, 7, 6, 12)).toISOString() == - "20100704T070612"); - - assert(SysTime(DateTime(1998, 12, 25, 2, 15, 0), msecs(24)).toISOString() == - "19981225T021500.024"); - - assert(SysTime(DateTime(0, 1, 5, 23, 9, 59)).toISOString() == - "00000105T230959"); - - assert(SysTime(DateTime(-4, 1, 5, 0, 0, 2), hnsecs(520_920)).toISOString() == - "-00040105T000002.052092"); - } - - @safe unittest - { - import core.time; - // Test A.D. - assert(SysTime(DateTime.init, UTC()).toISOString() == "00010101T000000Z"); - assert(SysTime(DateTime(1, 1, 1, 0, 0, 0), hnsecs(1), UTC()).toISOString() == "00010101T000000.0000001Z"); - - assert(SysTime(DateTime(9, 12, 4, 0, 0, 0)).toISOString() == "00091204T000000"); - assert(SysTime(DateTime(99, 12, 4, 5, 6, 12)).toISOString() == "00991204T050612"); - assert(SysTime(DateTime(999, 12, 4, 13, 44, 59)).toISOString() == "09991204T134459"); - assert(SysTime(DateTime(9999, 7, 4, 23, 59, 59)).toISOString() == "99990704T235959"); - assert(SysTime(DateTime(10000, 10, 20, 1, 1, 1)).toISOString() == "+100001020T010101"); - - assert(SysTime(DateTime(9, 12, 4, 0, 0, 0), msecs(42)).toISOString() == "00091204T000000.042"); - assert(SysTime(DateTime(99, 12, 4, 5, 6, 12), msecs(100)).toISOString() == "00991204T050612.1"); - assert(SysTime(DateTime(999, 12, 4, 13, 44, 59), usecs(45020)).toISOString() == "09991204T134459.04502"); - assert(SysTime(DateTime(9999, 7, 4, 23, 59, 59), hnsecs(12)).toISOString() == "99990704T235959.0000012"); - assert(SysTime(DateTime(10000, 10, 20, 1, 1, 1), hnsecs(507890)).toISOString() == "+100001020T010101.050789"); - - assert(SysTime(DateTime(2012, 12, 21, 12, 12, 12), - new immutable SimpleTimeZone(dur!"minutes"(-360))).toISOString() == - "20121221T121212-06:00"); - - assert(SysTime(DateTime(2012, 12, 21, 12, 12, 12), - new immutable SimpleTimeZone(dur!"minutes"(420))).toISOString() == - "20121221T121212+07:00"); - - // Test B.C. - assert(SysTime(DateTime(0, 12, 31, 23, 59, 59), hnsecs(9_999_999), UTC()).toISOString() == - "00001231T235959.9999999Z"); - assert(SysTime(DateTime(0, 12, 31, 23, 59, 59), hnsecs(1), UTC()).toISOString() == "00001231T235959.0000001Z"); - assert(SysTime(DateTime(0, 12, 31, 23, 59, 59), UTC()).toISOString() == "00001231T235959Z"); - - assert(SysTime(DateTime(0, 12, 4, 0, 12, 4)).toISOString() == "00001204T001204"); - assert(SysTime(DateTime(-9, 12, 4, 0, 0, 0)).toISOString() == "-00091204T000000"); - assert(SysTime(DateTime(-99, 12, 4, 5, 6, 12)).toISOString() == "-00991204T050612"); - assert(SysTime(DateTime(-999, 12, 4, 13, 44, 59)).toISOString() == "-09991204T134459"); - assert(SysTime(DateTime(-9999, 7, 4, 23, 59, 59)).toISOString() == "-99990704T235959"); - assert(SysTime(DateTime(-10000, 10, 20, 1, 1, 1)).toISOString() == "-100001020T010101"); - - assert(SysTime(DateTime(0, 12, 4, 0, 0, 0), msecs(7)).toISOString() == "00001204T000000.007"); - assert(SysTime(DateTime(-9, 12, 4, 0, 0, 0), msecs(42)).toISOString() == "-00091204T000000.042"); - assert(SysTime(DateTime(-99, 12, 4, 5, 6, 12), msecs(100)).toISOString() == "-00991204T050612.1"); - assert(SysTime(DateTime(-999, 12, 4, 13, 44, 59), usecs(45020)).toISOString() == "-09991204T134459.04502"); - assert(SysTime(DateTime(-9999, 7, 4, 23, 59, 59), hnsecs(12)).toISOString() == "-99990704T235959.0000012"); - assert(SysTime(DateTime(-10000, 10, 20, 1, 1, 1), hnsecs(507890)).toISOString() == "-100001020T010101.050789"); - - const cst = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); - immutable ist = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); - assert(cst.toISOString() == "19990706T123033"); - assert(ist.toISOString() == "19990706T123033"); - - static void testScope(scope ref SysTime st) @safe - { - auto result = st.toISOString(); - } - } - - - - /++ - Converts this $(LREF SysTime) to a string with the format - YYYY-MM-DDTHH:MM:SS.FFFFFFFTZ (where F is fractional seconds and TZ - is the time zone). - - Default behaviour: - Note that the number of digits in the fractional seconds varies with the - number of fractional seconds. It's a maximum of 7 (which would be - hnsecs), but only has as many as are necessary to hold the correct value - (so no trailing zeroes), and if there are no fractional seconds, then - there is no decimal point. - - The optional parameter "prec" allows to change the default behavior by - specifying the precision of the fractional seconds. The accepted values - are in the range [-1, 7], where -1 represents the default behavior. - - If this $(LREF SysTime)'s time zone is - $(REF LocalTime,std,datetime,timezone), then TZ is empty. If its time - zone is `UTC`, then it is "Z". Otherwise, it is the offset from UTC - (e.g. +01:00 or -07:00). Note that the offset from UTC is $(I not) - enough to uniquely identify the time zone. - - Time zone offsets will be in the form +HH:MM or -HH:MM. - - Params: - writer = A `char` accepting - $(REF_ALTTEXT output range, isOutputRange, std, range, primitives) - prec = An `int` representing the desired precision. Acceptable values range from -1 to 7, where -1 represents the default behavior. - Returns: - A `string` when not using an output range; `void` otherwise. - +/ - string toISOExtString(int prec = -1) @safe const nothrow scope - { - assert(prec >= -1 && prec <= 7, "Precision must be in the range [-1, 7]"); - - import std.array : appender; - auto app = appender!string(); - app.reserve(35); - try - toISOExtString(app, prec); - catch (Exception e) - assert(0, "toISOExtString() threw."); - return app.data; - } - - /// ditto - void toISOExtString(W)(ref W writer, int prec = -1) const scope - if (isOutputRange!(W, char)) - { - assert(prec >= -1 && prec <= 7, "Precision must be in the range [-1, 7]"); - - immutable adjustedTime = adjTime; - long hnsecs = adjustedTime; - - auto days = splitUnitsFromHNSecs!"days"(hnsecs) + 1; - - if (hnsecs < 0) - { - hnsecs += convert!("hours", "hnsecs")(24); - --days; - } - - immutable hour = splitUnitsFromHNSecs!"hours"(hnsecs); - immutable minute = splitUnitsFromHNSecs!"minutes"(hnsecs); - immutable second = splitUnitsFromHNSecs!"seconds"(hnsecs); - - immutable dateTime = DateTime(Date(cast(int) days), TimeOfDay(cast(int) hour, - cast(int) minute, cast(int) second)); - - if (_timezone is LocalTime()) - { - dateTime.toISOExtString(writer); - fracSecsToISOString(writer, cast(int) hnsecs, prec); - return; - } - - if (_timezone is UTC()) - { - dateTime.toISOExtString(writer); - fracSecsToISOString(writer, cast(int) hnsecs, prec); - put(writer, 'Z'); - return; - } - - immutable utcOffset = dur!"hnsecs"(adjustedTime - stdTime); - - dateTime.toISOExtString(writer); - fracSecsToISOString(writer, cast(int) hnsecs, prec); - SimpleTimeZone.toISOExtString(writer, utcOffset); - } - - /// - @safe unittest - { - import core.time : msecs, hnsecs; - import std.datetime.date : DateTime; - - assert(SysTime(DateTime(2010, 7, 4, 7, 6, 12)).toISOExtString() == - "2010-07-04T07:06:12"); - - assert(SysTime(DateTime(1998, 12, 25, 2, 15, 0), msecs(24)).toISOExtString() == - "1998-12-25T02:15:00.024"); - - assert(SysTime(DateTime(0, 1, 5, 23, 9, 59)).toISOExtString() == - "0000-01-05T23:09:59"); - - assert(SysTime(DateTime(-4, 1, 5, 0, 0, 2), hnsecs(520_920)).toISOExtString() == - "-0004-01-05T00:00:02.052092"); - - assert(SysTime(DateTime(-4, 1, 5, 0, 0, 2), hnsecs(520_920)).toISOExtString(4) == - "-0004-01-05T00:00:02.0520"); - - assert(SysTime(DateTime(-4, 1, 5, 0, 0, 2), hnsecs(520_920)).toISOExtString(2) == - "-0004-01-05T00:00:02.05"); - - assert(SysTime(DateTime(-4, 1, 5, 0, 0, 2), hnsecs(520_920)).toISOExtString(7) == - "-0004-01-05T00:00:02.0520920"); - } - - @safe unittest - { - import core.time; - // Test A.D. - assert(SysTime(DateTime.init, UTC()).toISOExtString() == "0001-01-01T00:00:00Z"); - assert(SysTime(DateTime(1, 1, 1, 0, 0, 0), hnsecs(1), UTC()).toISOExtString() == - "0001-01-01T00:00:00.0000001Z"); - - assert(SysTime(DateTime(9, 12, 4, 0, 0, 0)).toISOExtString() == "0009-12-04T00:00:00"); - assert(SysTime(DateTime(99, 12, 4, 5, 6, 12)).toISOExtString() == "0099-12-04T05:06:12"); - assert(SysTime(DateTime(999, 12, 4, 13, 44, 59)).toISOExtString() == "0999-12-04T13:44:59"); - assert(SysTime(DateTime(9999, 7, 4, 23, 59, 59)).toISOExtString() == "9999-07-04T23:59:59"); - assert(SysTime(DateTime(10000, 10, 20, 1, 1, 1)).toISOExtString() == "+10000-10-20T01:01:01"); - - assert(SysTime(DateTime(9, 12, 4, 0, 0, 0), msecs(42)).toISOExtString() == "0009-12-04T00:00:00.042"); - assert(SysTime(DateTime(99, 12, 4, 5, 6, 12), msecs(100)).toISOExtString() == "0099-12-04T05:06:12.1"); - assert(SysTime(DateTime(999, 12, 4, 13, 44, 59), usecs(45020)).toISOExtString() == "0999-12-04T13:44:59.04502"); - assert(SysTime(DateTime(9999, 7, 4, 23, 59, 59), hnsecs(12)).toISOExtString() == "9999-07-04T23:59:59.0000012"); - assert(SysTime(DateTime(10000, 10, 20, 1, 1, 1), hnsecs(507890)).toISOExtString() == - "+10000-10-20T01:01:01.050789"); - - assert(SysTime(DateTime(2012, 12, 21, 12, 12, 12), - new immutable SimpleTimeZone(dur!"minutes"(-360))).toISOExtString() == - "2012-12-21T12:12:12-06:00"); - - assert(SysTime(DateTime(2012, 12, 21, 12, 12, 12), - new immutable SimpleTimeZone(dur!"minutes"(420))).toISOExtString() == - "2012-12-21T12:12:12+07:00"); - - // Test B.C. - assert(SysTime(DateTime(0, 12, 31, 23, 59, 59), hnsecs(9_999_999), UTC()).toISOExtString() == - "0000-12-31T23:59:59.9999999Z"); - assert(SysTime(DateTime(0, 12, 31, 23, 59, 59), hnsecs(1), UTC()).toISOExtString() == - "0000-12-31T23:59:59.0000001Z"); - assert(SysTime(DateTime(0, 12, 31, 23, 59, 59), UTC()).toISOExtString() == "0000-12-31T23:59:59Z"); - - assert(SysTime(DateTime(0, 12, 4, 0, 12, 4)).toISOExtString() == "0000-12-04T00:12:04"); - assert(SysTime(DateTime(-9, 12, 4, 0, 0, 0)).toISOExtString() == "-0009-12-04T00:00:00"); - assert(SysTime(DateTime(-99, 12, 4, 5, 6, 12)).toISOExtString() == "-0099-12-04T05:06:12"); - assert(SysTime(DateTime(-999, 12, 4, 13, 44, 59)).toISOExtString() == "-0999-12-04T13:44:59"); - assert(SysTime(DateTime(-9999, 7, 4, 23, 59, 59)).toISOExtString() == "-9999-07-04T23:59:59"); - assert(SysTime(DateTime(-10000, 10, 20, 1, 1, 1)).toISOExtString() == "-10000-10-20T01:01:01"); - - assert(SysTime(DateTime(0, 12, 4, 0, 0, 0), msecs(7)).toISOExtString() == "0000-12-04T00:00:00.007"); - assert(SysTime(DateTime(-9, 12, 4, 0, 0, 0), msecs(42)).toISOExtString() == "-0009-12-04T00:00:00.042"); - assert(SysTime(DateTime(-99, 12, 4, 5, 6, 12), msecs(100)).toISOExtString() == "-0099-12-04T05:06:12.1"); - assert(SysTime(DateTime(-999, 12, 4, 13, 44, 59), usecs(45020)).toISOExtString() == - "-0999-12-04T13:44:59.04502"); - assert(SysTime(DateTime(-9999, 7, 4, 23, 59, 59), hnsecs(12)).toISOExtString() == - "-9999-07-04T23:59:59.0000012"); - assert(SysTime(DateTime(-10000, 10, 20, 1, 1, 1), hnsecs(507890)).toISOExtString() == - "-10000-10-20T01:01:01.050789"); - - const cst = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); - immutable ist = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); - assert(cst.toISOExtString() == "1999-07-06T12:30:33"); - assert(ist.toISOExtString() == "1999-07-06T12:30:33"); - - static void testScope(scope ref SysTime st) @safe - { - auto result = st.toISOExtString(); - } - } - - /++ - Converts this $(LREF SysTime) to a string with the format - YYYY-Mon-DD HH:MM:SS.FFFFFFFTZ (where F is fractional seconds and TZ - is the time zone). - - Note that the number of digits in the fractional seconds varies with the - number of fractional seconds. It's a maximum of 7 (which would be - hnsecs), but only has as many as are necessary to hold the correct value - (so no trailing zeroes), and if there are no fractional seconds, then - there is no decimal point. - - If this $(LREF SysTime)'s time zone is - $(REF LocalTime,std,datetime,timezone), then TZ is empty. If its time - zone is `UTC`, then it is "Z". Otherwise, it is the offset from UTC - (e.g. +01:00 or -07:00). Note that the offset from UTC is $(I not) - enough to uniquely identify the time zone. - - Time zone offsets will be in the form +HH:MM or -HH:MM. - - Params: - writer = A `char` accepting - $(REF_ALTTEXT output range, isOutputRange, std, range, primitives) - Returns: - A `string` when not using an output range; `void` otherwise. - +/ - string toSimpleString() @safe const nothrow scope - { - import std.array : appender; - auto app = appender!string(); - app.reserve(35); - try - toSimpleString(app); - catch (Exception e) - assert(0, "toSimpleString() threw."); - return app.data; - } - - /// ditto - void toSimpleString(W)(ref W writer) const scope - if (isOutputRange!(W, char)) - { - immutable adjustedTime = adjTime; - long hnsecs = adjustedTime; - - auto days = splitUnitsFromHNSecs!"days"(hnsecs) + 1; - - if (hnsecs < 0) - { - hnsecs += convert!("hours", "hnsecs")(24); - --days; - } - - immutable hour = splitUnitsFromHNSecs!"hours"(hnsecs); - immutable minute = splitUnitsFromHNSecs!"minutes"(hnsecs); - immutable second = splitUnitsFromHNSecs!"seconds"(hnsecs); - - immutable dateTime = DateTime(Date(cast(int) days), TimeOfDay(cast(int) hour, - cast(int) minute, cast(int) second)); - - if (_timezone is LocalTime()) - { - dateTime.toSimpleString(writer); - fracSecsToISOString(writer, cast(int) hnsecs); - return; - } - - if (_timezone is UTC()) - { - dateTime.toSimpleString(writer); - fracSecsToISOString(writer, cast(int) hnsecs); - put(writer, 'Z'); - return; - } - - immutable utcOffset = dur!"hnsecs"(adjustedTime - stdTime); - - dateTime.toSimpleString(writer); - fracSecsToISOString(writer, cast(int) hnsecs); - SimpleTimeZone.toISOExtString(writer, utcOffset); - } - - /// - @safe unittest - { - import core.time : msecs, hnsecs; - import std.datetime.date : DateTime; - - assert(SysTime(DateTime(2010, 7, 4, 7, 6, 12)).toSimpleString() == - "2010-Jul-04 07:06:12"); - - assert(SysTime(DateTime(1998, 12, 25, 2, 15, 0), msecs(24)).toSimpleString() == - "1998-Dec-25 02:15:00.024"); - - assert(SysTime(DateTime(0, 1, 5, 23, 9, 59)).toSimpleString() == - "0000-Jan-05 23:09:59"); - - assert(SysTime(DateTime(-4, 1, 5, 0, 0, 2), hnsecs(520_920)).toSimpleString() == - "-0004-Jan-05 00:00:02.052092"); - } - - @safe unittest - { - import core.time; - // Test A.D. - assert(SysTime(DateTime.init, UTC()).toString() == "0001-Jan-01 00:00:00Z"); - assert(SysTime(DateTime(1, 1, 1, 0, 0, 0), hnsecs(1), UTC()).toString() == "0001-Jan-01 00:00:00.0000001Z"); - - assert(SysTime(DateTime(9, 12, 4, 0, 0, 0)).toSimpleString() == "0009-Dec-04 00:00:00"); - assert(SysTime(DateTime(99, 12, 4, 5, 6, 12)).toSimpleString() == "0099-Dec-04 05:06:12"); - assert(SysTime(DateTime(999, 12, 4, 13, 44, 59)).toSimpleString() == "0999-Dec-04 13:44:59"); - assert(SysTime(DateTime(9999, 7, 4, 23, 59, 59)).toSimpleString() == "9999-Jul-04 23:59:59"); - assert(SysTime(DateTime(10000, 10, 20, 1, 1, 1)).toSimpleString() == "+10000-Oct-20 01:01:01"); - - assert(SysTime(DateTime(9, 12, 4, 0, 0, 0), msecs(42)).toSimpleString() == "0009-Dec-04 00:00:00.042"); - assert(SysTime(DateTime(99, 12, 4, 5, 6, 12), msecs(100)).toSimpleString() == "0099-Dec-04 05:06:12.1"); - assert(SysTime(DateTime(999, 12, 4, 13, 44, 59), usecs(45020)).toSimpleString() == - "0999-Dec-04 13:44:59.04502"); - assert(SysTime(DateTime(9999, 7, 4, 23, 59, 59), hnsecs(12)).toSimpleString() == - "9999-Jul-04 23:59:59.0000012"); - assert(SysTime(DateTime(10000, 10, 20, 1, 1, 1), hnsecs(507890)).toSimpleString() == - "+10000-Oct-20 01:01:01.050789"); - - assert(SysTime(DateTime(2012, 12, 21, 12, 12, 12), - new immutable SimpleTimeZone(dur!"minutes"(-360))).toSimpleString() == - "2012-Dec-21 12:12:12-06:00"); - - assert(SysTime(DateTime(2012, 12, 21, 12, 12, 12), - new immutable SimpleTimeZone(dur!"minutes"(420))).toSimpleString() == - "2012-Dec-21 12:12:12+07:00"); - - // Test B.C. - assert(SysTime(DateTime(0, 12, 31, 23, 59, 59), hnsecs(9_999_999), UTC()).toSimpleString() == - "0000-Dec-31 23:59:59.9999999Z"); - assert(SysTime(DateTime(0, 12, 31, 23, 59, 59), hnsecs(1), UTC()).toSimpleString() == - "0000-Dec-31 23:59:59.0000001Z"); - assert(SysTime(DateTime(0, 12, 31, 23, 59, 59), UTC()).toSimpleString() == "0000-Dec-31 23:59:59Z"); - - assert(SysTime(DateTime(0, 12, 4, 0, 12, 4)).toSimpleString() == "0000-Dec-04 00:12:04"); - assert(SysTime(DateTime(-9, 12, 4, 0, 0, 0)).toSimpleString() == "-0009-Dec-04 00:00:00"); - assert(SysTime(DateTime(-99, 12, 4, 5, 6, 12)).toSimpleString() == "-0099-Dec-04 05:06:12"); - assert(SysTime(DateTime(-999, 12, 4, 13, 44, 59)).toSimpleString() == "-0999-Dec-04 13:44:59"); - assert(SysTime(DateTime(-9999, 7, 4, 23, 59, 59)).toSimpleString() == "-9999-Jul-04 23:59:59"); - assert(SysTime(DateTime(-10000, 10, 20, 1, 1, 1)).toSimpleString() == "-10000-Oct-20 01:01:01"); - - assert(SysTime(DateTime(0, 12, 4, 0, 0, 0), msecs(7)).toSimpleString() == "0000-Dec-04 00:00:00.007"); - assert(SysTime(DateTime(-9, 12, 4, 0, 0, 0), msecs(42)).toSimpleString() == "-0009-Dec-04 00:00:00.042"); - assert(SysTime(DateTime(-99, 12, 4, 5, 6, 12), msecs(100)).toSimpleString() == "-0099-Dec-04 05:06:12.1"); - assert(SysTime(DateTime(-999, 12, 4, 13, 44, 59), usecs(45020)).toSimpleString() == - "-0999-Dec-04 13:44:59.04502"); - assert(SysTime(DateTime(-9999, 7, 4, 23, 59, 59), hnsecs(12)).toSimpleString() == - "-9999-Jul-04 23:59:59.0000012"); - assert(SysTime(DateTime(-10000, 10, 20, 1, 1, 1), hnsecs(507890)).toSimpleString() == - "-10000-Oct-20 01:01:01.050789"); - - const cst = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); - immutable ist = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); - assert(cst.toSimpleString() == "1999-Jul-06 12:30:33"); - assert(ist.toSimpleString() == "1999-Jul-06 12:30:33"); - - static void testScope(scope ref SysTime st) @safe - { - auto result = st.toSimpleString(); - } - } - - - /++ - Converts this $(LREF SysTime) to a string. - - This function exists to make it easy to convert a $(LREF SysTime) to a - string for code that does not care what the exact format is - just that - it presents the information in a clear manner. It also makes it easy to - simply convert a $(LREF SysTime) to a string when using functions such - as `to!string`, `format`, or `writeln` which use toString to convert - user-defined types. So, it is unlikely that much code will call - toString directly. - - The format of the string is purposefully unspecified, and code that - cares about the format of the string should use `toISOString`, - `toISOExtString`, `toSimpleString`, or some other custom formatting - function that explicitly generates the format that the code needs. The - reason is that the code is then clear about what format it's using, - making it less error-prone to maintain the code and interact with other - software that consumes the generated strings. It's for this same reason - that $(LREF SysTime) has no `fromString` function, whereas it does have - `fromISOString`, `fromISOExtString`, and `fromSimpleString`. - - The format returned by toString may or may not change in the future. - - Params: - writer = A `char` accepting - $(REF_ALTTEXT output range, isOutputRange, std, range, primitives) - Returns: - A `string` when not using an output range; `void` otherwise. - +/ - string toString() @safe const nothrow scope - { - return toSimpleString(); - } - - /// ditto - void toString(W)(ref W writer) const scope - if (isOutputRange!(W, char)) - { - toSimpleString(writer); - } - - @safe unittest - { - import core.time; - auto st = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); - const cst = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); - immutable ist = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); - static assert(__traits(compiles, st.toString())); - static assert(__traits(compiles, cst.toString())); - static assert(__traits(compiles, ist.toString())); - - static void testScope(scope ref SysTime st) @safe - { - auto result = st.toString(); - } - } - - - /++ - Creates a $(LREF SysTime) from a string with the format - YYYYMMDDTHHMMSS.FFFFFFFTZ (where F is fractional seconds and TZ - is the time zone). Whitespace is stripped from the given string. - - The exact format is exactly as described in $(LREF toISOString) except - that trailing zeroes are permitted - including having fractional seconds - with all zeroes. The time zone and fractional seconds are optional, - however, a decimal point with nothing following it is invalid. - Also, while $(LREF toISOString) will never generate a string - with more than 7 digits in the fractional seconds (because that's the - limit with hecto-nanosecond precision), it will allow more than 7 digits - in order to read strings from other sources that have higher precision - (however, any digits beyond 7 will be truncated). - - If there is no time zone in the string, then - $(REF LocalTime,std,datetime,timezone) is used. If the time zone is "Z", - then `UTC` is used. Otherwise, a - $(REF SimpleTimeZone,std,datetime,timezone) which corresponds to the - given offset from UTC is used. To get the returned $(LREF SysTime) to be - a particular time zone, pass in that time zone and the $(LREF SysTime) - to be returned will be converted to that time zone (though it will still - be read in as whatever time zone is in its string). - - The accepted formats for time zone offsets are +HH, -HH, +HHMM, and - -HHMM. - - $(RED Warning: - Previously, $(LREF toISOString) did the same as - $(LREF toISOExtString) and generated +HH:MM or -HH:MM for the time - zone when it was not $(REF LocalTime,std,datetime,timezone) or - $(REF UTC,std,datetime,timezone), which is not in conformance with - ISO 8601 for the non-extended string format. This has now been - fixed. However, for now, fromISOString will continue to accept the - extended format for the time zone so that any code which has been - writing out the result of toISOString to read in later will continue - to work. The current behavior will be kept until July 2019 at which - point, fromISOString will be fixed to be standards compliant.) - - Params: - isoString = A string formatted in the ISO format for dates and times. - tz = The time zone to convert the given time to (no - conversion occurs if null). - - Throws: - $(REF DateTimeException,std,datetime,date) if the given string is - not in the ISO format or if the resulting $(LREF SysTime) would not - be valid. - +/ - static SysTime fromISOString(S)(scope const S isoString, immutable TimeZone tz = null) @safe - if (isSomeString!S) - { - import std.algorithm.searching : startsWith, find; - import std.conv : to; - import std.string : strip; - import std.utf : byCodeUnit; - - auto str = strip(isoString); - immutable skipFirst = str.startsWith('+', '-'); - - auto found = (skipFirst ? str[1..$] : str).byCodeUnit.find('.', 'Z', '+', '-'); - auto dateTimeStr = str[0 .. $ - found[0].length]; - - typeof(str.byCodeUnit) foundTZ; // needs to have longer lifetime than zoneStr - typeof(str) fracSecStr; - typeof(str) zoneStr; - - if (found[1] != 0) - { - if (found[1] == 1) - { - foundTZ = found[0].find('Z', '+', '-')[0]; - - if (foundTZ.length != 0) - { - static if (isNarrowString!S) - { - fracSecStr = found[0][0 .. $ - foundTZ.length].source; - zoneStr = foundTZ.source; - } - else - { - fracSecStr = found[0][0 .. $ - foundTZ.length]; - zoneStr = foundTZ; - } - } - else - { - static if (isNarrowString!S) - fracSecStr = found[0].source; - else - fracSecStr = found[0]; - } - } - else - { - static if (isNarrowString!S) - zoneStr = found[0].source; - else - zoneStr = found[0]; - } - } - - try - { - auto dateTime = DateTime.fromISOString(dateTimeStr); - auto fracSec = fracSecsFromISOString(fracSecStr); - - Rebindable!(immutable TimeZone) parsedZone; - - if (zoneStr.empty) - parsedZone = LocalTime(); - else if (zoneStr == "Z") - parsedZone = UTC(); - else - { - try - parsedZone = SimpleTimeZone.fromISOString(zoneStr); - catch (DateTimeException dte) - parsedZone = SimpleTimeZone.fromISOExtString(zoneStr); - } - - auto retval = SysTime(dateTime, fracSec, parsedZone); - - if (tz !is null) - retval.timezone = tz; - - return retval; - } - catch (DateTimeException dte) - throw new DateTimeException(format("Invalid ISO String: %s", isoString)); - } - - /// - @safe unittest - { - import core.time : hours, msecs, usecs, hnsecs; - import std.datetime.date : DateTime; - import std.datetime.timezone : SimpleTimeZone, UTC; - - assert(SysTime.fromISOString("20100704T070612") == - SysTime(DateTime(2010, 7, 4, 7, 6, 12))); - - assert(SysTime.fromISOString("19981225T021500.007") == - SysTime(DateTime(1998, 12, 25, 2, 15, 0), msecs(7))); - - assert(SysTime.fromISOString("00000105T230959.00002") == - SysTime(DateTime(0, 1, 5, 23, 9, 59), usecs(20))); - - assert(SysTime.fromISOString("20130207T043937.000050392") == - SysTime(DateTime(2013, 2, 7, 4, 39, 37), hnsecs(503))); - - assert(SysTime.fromISOString("-00040105T000002") == - SysTime(DateTime(-4, 1, 5, 0, 0, 2))); - - assert(SysTime.fromISOString(" 20100704T070612 ") == - SysTime(DateTime(2010, 7, 4, 7, 6, 12))); - - assert(SysTime.fromISOString("20100704T070612Z") == - SysTime(DateTime(2010, 7, 4, 7, 6, 12), UTC())); - - assert(SysTime.fromISOString("20100704T070612-0800") == - SysTime(DateTime(2010, 7, 4, 7, 6, 12), - new immutable SimpleTimeZone(hours(-8)))); - - assert(SysTime.fromISOString("20100704T070612+0800") == - SysTime(DateTime(2010, 7, 4, 7, 6, 12), - new immutable SimpleTimeZone(hours(8)))); - } - - @safe unittest - { - import core.time; - foreach (str; ["", "20100704000000", "20100704 000000", "20100704t000000", - "20100704T000000.", "20100704T000000.A", "20100704T000000.Z", - "20100704T000000.0000000A", "20100704T000000.00000000A", - "20100704T000000+", "20100704T000000-", "20100704T000000:", - "20100704T000000-:", "20100704T000000+:", "20100704T000000-1:", - "20100704T000000+1:", "20100704T000000+1:0", - "20100704T000000-12.00", "20100704T000000+12.00", - "20100704T000000-8", "20100704T000000+8", - "20100704T000000-800", "20100704T000000+800", - "20100704T000000-080", "20100704T000000+080", - "20100704T000000-2400", "20100704T000000+2400", - "20100704T000000-1260", "20100704T000000+1260", - "20100704T000000.0-8", "20100704T000000.0+8", - "20100704T000000.0-800", "20100704T000000.0+800", - "20100704T000000.0-080", "20100704T000000.0+080", - "20100704T000000.0-2400", "20100704T000000.0+2400", - "20100704T000000.0-1260", "20100704T000000.0+1260", - "20100704T000000-8:00", "20100704T000000+8:00", - "20100704T000000-08:0", "20100704T000000+08:0", - "20100704T000000-24:00", "20100704T000000+24:00", - "20100704T000000-12:60", "20100704T000000+12:60", - "20100704T000000.0-8:00", "20100704T000000.0+8:00", - "20100704T000000.0-08:0", "20100704T000000.0+08:0", - "20100704T000000.0-24:00", "20100704T000000.0+24:00", - "20100704T000000.0-12:60", "20100704T000000.0+12:60", - "2010-07-0400:00:00", "2010-07-04 00:00:00", - "2010-07-04t00:00:00", "2010-07-04T00:00:00.", - "2010-Jul-0400:00:00", "2010-Jul-04 00:00:00", "2010-Jul-04t00:00:00", - "2010-Jul-04T00:00:00", "2010-Jul-04 00:00:00.", - "2010-12-22T172201", "2010-Dec-22 17:22:01"]) - { - assertThrown!DateTimeException(SysTime.fromISOString(str), format("[%s]", str)); - } - - static void test(string str, SysTime st, size_t line = __LINE__) - { - if (SysTime.fromISOString(str) != st) - throw new AssertError("unittest failure", __FILE__, line); - } - - test("20101222T172201", SysTime(DateTime(2010, 12, 22, 17, 22, 1))); - test("19990706T123033", SysTime(DateTime(1999, 7, 6, 12, 30, 33))); - test("-19990706T123033", SysTime(DateTime(-1999, 7, 6, 12, 30, 33))); - test("+019990706T123033", SysTime(DateTime(1999, 7, 6, 12, 30, 33))); - test("19990706T123033 ", SysTime(DateTime(1999, 7, 6, 12, 30, 33))); - test(" 19990706T123033", SysTime(DateTime(1999, 7, 6, 12, 30, 33))); - test(" 19990706T123033 ", SysTime(DateTime(1999, 7, 6, 12, 30, 33))); - - test("19070707T121212.0", SysTime(DateTime(1907, 7, 7, 12, 12, 12))); - test("19070707T121212.0000000", SysTime(DateTime(1907, 7, 7, 12, 12, 12))); - test("19070707T121212.0000001", SysTime(DateTime(1907, 7, 7, 12, 12, 12), hnsecs(1))); - test("20100704T000000.00000000", SysTime(Date(2010, 7, 4))); - test("20100704T000000.00000009", SysTime(Date(2010, 7, 4))); - test("20100704T000000.00000019", SysTime(DateTime(2010, 7, 4), hnsecs(1))); - test("19070707T121212.000001", SysTime(DateTime(1907, 7, 7, 12, 12, 12), usecs(1))); - test("19070707T121212.0000010", SysTime(DateTime(1907, 7, 7, 12, 12, 12), usecs(1))); - test("19070707T121212.001", SysTime(DateTime(1907, 7, 7, 12, 12, 12), msecs(1))); - test("19070707T121212.0010000", SysTime(DateTime(1907, 7, 7, 12, 12, 12), msecs(1))); - - auto west60 = new immutable SimpleTimeZone(hours(-1)); - auto west90 = new immutable SimpleTimeZone(minutes(-90)); - auto west480 = new immutable SimpleTimeZone(hours(-8)); - auto east60 = new immutable SimpleTimeZone(hours(1)); - auto east90 = new immutable SimpleTimeZone(minutes(90)); - auto east480 = new immutable SimpleTimeZone(hours(8)); - - test("20101222T172201Z", SysTime(DateTime(2010, 12, 22, 17, 22, 1), UTC())); - test("20101222T172201-0100", SysTime(DateTime(2010, 12, 22, 17, 22, 1), west60)); - test("20101222T172201-01", SysTime(DateTime(2010, 12, 22, 17, 22, 1), west60)); - test("20101222T172201-0130", SysTime(DateTime(2010, 12, 22, 17, 22, 1), west90)); - test("20101222T172201-0800", SysTime(DateTime(2010, 12, 22, 17, 22, 1), west480)); - test("20101222T172201+0100", SysTime(DateTime(2010, 12, 22, 17, 22, 1), east60)); - test("20101222T172201+01", SysTime(DateTime(2010, 12, 22, 17, 22, 1), east60)); - test("20101222T172201+0130", SysTime(DateTime(2010, 12, 22, 17, 22, 1), east90)); - test("20101222T172201+0800", SysTime(DateTime(2010, 12, 22, 17, 22, 1), east480)); - - test("20101103T065106.57159Z", SysTime(DateTime(2010, 11, 3, 6, 51, 6), hnsecs(5715900), UTC())); - test("20101222T172201.23412Z", SysTime(DateTime(2010, 12, 22, 17, 22, 1), hnsecs(2_341_200), UTC())); - test("20101222T172201.23112-0100", SysTime(DateTime(2010, 12, 22, 17, 22, 1), hnsecs(2_311_200), west60)); - test("20101222T172201.45-01", SysTime(DateTime(2010, 12, 22, 17, 22, 1), hnsecs(4_500_000), west60)); - test("20101222T172201.1-0130", SysTime(DateTime(2010, 12, 22, 17, 22, 1), hnsecs(1_000_000), west90)); - test("20101222T172201.55-0800", SysTime(DateTime(2010, 12, 22, 17, 22, 1), hnsecs(5_500_000), west480)); - test("20101222T172201.1234567+0100", SysTime(DateTime(2010, 12, 22, 17, 22, 1), hnsecs(1_234_567), east60)); - test("20101222T172201.0+01", SysTime(DateTime(2010, 12, 22, 17, 22, 1), east60)); - test("20101222T172201.0000000+0130", SysTime(DateTime(2010, 12, 22, 17, 22, 1), east90)); - test("20101222T172201.45+0800", SysTime(DateTime(2010, 12, 22, 17, 22, 1), hnsecs(4_500_000), east480)); - - // for dstring coverage - assert(SysTime.fromISOString("20101222T172201.23112-0100"d) == SysTime( - DateTime(2010, 12, 22, 17, 22, 1), hnsecs(2_311_200), west60)); - assert(SysTime.fromISOString("19070707T121212.0010000"d) == SysTime( - DateTime(1907, 7, 7, 12, 12, 12), msecs(1))); - - // @@@DEPRECATED_2019-07@@@ - // This isn't deprecated per se, but that text will make it so that it - // pops up when deprecations are moved along around July 2019. At that - // time, we will update fromISOString so that it is conformant with ISO - // 8601, and it will no longer accept ISO extended time zones (it does - // currently because of https://issues.dlang.org/show_bug.cgi?id=15654 - // toISOString used to incorrectly use the ISO extended time zone format). - // These tests will then start failing will need to be updated accordingly. - // Also, the notes about this issue in toISOString and fromISOString's - // documentation will need to be removed. - test("20101222T172201-01:00", SysTime(DateTime(2010, 12, 22, 17, 22, 1), west60)); - test("20101222T172201-01:30", SysTime(DateTime(2010, 12, 22, 17, 22, 1), west90)); - test("20101222T172201-08:00", SysTime(DateTime(2010, 12, 22, 17, 22, 1), west480)); - test("20101222T172201+01:00", SysTime(DateTime(2010, 12, 22, 17, 22, 1), east60)); - test("20101222T172201+01:30", SysTime(DateTime(2010, 12, 22, 17, 22, 1), east90)); - test("20101222T172201+08:00", SysTime(DateTime(2010, 12, 22, 17, 22, 1), east480)); - - test("20101222T172201.23112-01:00", SysTime(DateTime(2010, 12, 22, 17, 22, 1), hnsecs(2_311_200), west60)); - test("20101222T172201.1-01:30", SysTime(DateTime(2010, 12, 22, 17, 22, 1), hnsecs(1_000_000), west90)); - test("20101222T172201.55-08:00", SysTime(DateTime(2010, 12, 22, 17, 22, 1), hnsecs(5_500_000), west480)); - test("20101222T172201.1234567+01:00", SysTime(DateTime(2010, 12, 22, 17, 22, 1), hnsecs(1_234_567), east60)); - test("20101222T172201.0000000+01:30", SysTime(DateTime(2010, 12, 22, 17, 22, 1), east90)); - test("20101222T172201.45+08:00", SysTime(DateTime(2010, 12, 22, 17, 22, 1), hnsecs(4_500_000), east480)); - - static void testScope(scope ref string str) @safe - { - auto result = SysTime.fromISOString(str); - } - } - - // https://issues.dlang.org/show_bug.cgi?id=17801 - @safe unittest - { - import std.conv : to; - import std.meta : AliasSeq; - static foreach (C; AliasSeq!(char, wchar, dchar)) - { - static foreach (S; AliasSeq!(C[], const(C)[], immutable(C)[])) - { - assert(SysTime.fromISOString(to!S("20121221T141516Z")) == - SysTime(DateTime(2012, 12, 21, 14, 15, 16), UTC())); - } - } - } - - - /++ - Creates a $(LREF SysTime) from a string with the format - YYYY-MM-DDTHH:MM:SS.FFFFFFFTZ (where F is fractional seconds and TZ - is the time zone). Whitespace is stripped from the given string. - - The exact format is exactly as described in $(LREF toISOExtString) - except that trailing zeroes are permitted - including having fractional - seconds with all zeroes. The time zone and fractional seconds are - optional, however, a decimal point with nothing following it is invalid. - Also, while $(LREF toISOExtString) will never generate a - string with more than 7 digits in the fractional seconds (because that's - the limit with hecto-nanosecond precision), it will allow more than 7 - digits in order to read strings from other sources that have higher - precision (however, any digits beyond 7 will be truncated). - - If there is no time zone in the string, then - $(REF LocalTime,std,datetime,timezone) is used. If the time zone is "Z", - then `UTC` is used. Otherwise, a - $(REF SimpleTimeZone,std,datetime,timezone) which corresponds to the - given offset from UTC is used. To get the returned $(LREF SysTime) to be - a particular time zone, pass in that time zone and the $(LREF SysTime) - to be returned will be converted to that time zone (though it will still - be read in as whatever time zone is in its string). - - The accepted formats for time zone offsets are +HH, -HH, +HH:MM, and - -HH:MM. - - Params: - isoExtString = A string formatted in the ISO Extended format for - dates and times. - tz = The time zone to convert the given time to (no - conversion occurs if null). - - Throws: - $(REF DateTimeException,std,datetime,date) if the given string is - not in the ISO format or if the resulting $(LREF SysTime) would not - be valid. - +/ - static SysTime fromISOExtString(S)(scope const S isoExtString, immutable TimeZone tz = null) @safe - if (isSomeString!(S)) - { - import std.algorithm.searching : countUntil, find; - import std.conv : to; - import std.string : strip, indexOf; - - auto str = strip(isoExtString); - - auto tIndex = str.indexOf('T'); - enforce(tIndex != -1, new DateTimeException(format("Invalid ISO Extended String: %s", isoExtString))); - - auto found = str[tIndex + 1 .. $].find('.', 'Z', '+', '-'); - auto dateTimeStr = str[0 .. $ - found[0].length]; - - typeof(str) foundTZ; // needs to have longer lifetime than zoneStr - typeof(str) fracSecStr; - typeof(str) zoneStr; - - if (found[1] != 0) - { - if (found[1] == 1) - { - foundTZ = found[0].find('Z', '+', '-')[0]; - - if (foundTZ.length != 0) - { - fracSecStr = found[0][0 .. $ - foundTZ.length]; - zoneStr = foundTZ; - } - else - fracSecStr = found[0]; - } - else - zoneStr = found[0]; - } - - try - { - auto dateTime = DateTime.fromISOExtString(dateTimeStr); - auto fracSec = fracSecsFromISOString(fracSecStr); - Rebindable!(immutable TimeZone) parsedZone; - - if (zoneStr.empty) - parsedZone = LocalTime(); - else if (zoneStr == "Z") - parsedZone = UTC(); - else - parsedZone = SimpleTimeZone.fromISOExtString(zoneStr); - - auto retval = SysTime(dateTime, fracSec, parsedZone); - - if (tz !is null) - retval.timezone = tz; - - return retval; - } - catch (DateTimeException dte) - throw new DateTimeException(format("Invalid ISO Extended String: %s", isoExtString)); - } - - /// - @safe unittest - { - import core.time : hours, msecs, usecs, hnsecs; - import std.datetime.date : DateTime; - import std.datetime.timezone : SimpleTimeZone, UTC; - - assert(SysTime.fromISOExtString("2010-07-04T07:06:12") == - SysTime(DateTime(2010, 7, 4, 7, 6, 12))); - - assert(SysTime.fromISOExtString("1998-12-25T02:15:00.007") == - SysTime(DateTime(1998, 12, 25, 2, 15, 0), msecs(7))); - - assert(SysTime.fromISOExtString("0000-01-05T23:09:59.00002") == - SysTime(DateTime(0, 1, 5, 23, 9, 59), usecs(20))); - - assert(SysTime.fromISOExtString("2013-02-07T04:39:37.000050392") == - SysTime(DateTime(2013, 2, 7, 4, 39, 37), hnsecs(503))); - - assert(SysTime.fromISOExtString("-0004-01-05T00:00:02") == - SysTime(DateTime(-4, 1, 5, 0, 0, 2))); - - assert(SysTime.fromISOExtString(" 2010-07-04T07:06:12 ") == - SysTime(DateTime(2010, 7, 4, 7, 6, 12))); - - assert(SysTime.fromISOExtString("2010-07-04T07:06:12Z") == - SysTime(DateTime(2010, 7, 4, 7, 6, 12), UTC())); - - assert(SysTime.fromISOExtString("2010-07-04T07:06:12-08:00") == - SysTime(DateTime(2010, 7, 4, 7, 6, 12), - new immutable SimpleTimeZone(hours(-8)))); - assert(SysTime.fromISOExtString("2010-07-04T07:06:12+08:00") == - SysTime(DateTime(2010, 7, 4, 7, 6, 12), - new immutable SimpleTimeZone(hours(8)))); - } - - @safe unittest - { - import core.time; - foreach (str; ["", "20100704000000", "20100704 000000", - "20100704t000000", "20100704T000000.", "20100704T000000.0", - "2010-07:0400:00:00", "2010-07-04 00:00:00", - "2010-07-04 00:00:00", "2010-07-04t00:00:00", - "2010-07-04T00:00:00.", "2010-07-04T00:00:00.A", "2010-07-04T00:00:00.Z", - "2010-07-04T00:00:00.0000000A", "2010-07-04T00:00:00.00000000A", - "2010-07-04T00:00:00+", "2010-07-04T00:00:00-", - "2010-07-04T00:00:00:", "2010-07-04T00:00:00-:", "2010-07-04T00:00:00+:", - "2010-07-04T00:00:00-1:", "2010-07-04T00:00:00+1:", "2010-07-04T00:00:00+1:0", - "2010-07-04T00:00:00-12.00", "2010-07-04T00:00:00+12.00", - "2010-07-04T00:00:00-8", "2010-07-04T00:00:00+8", - "20100704T000000-800", "20100704T000000+800", - "20100704T000000-080", "20100704T000000+080", - "20100704T000000-2400", "20100704T000000+2400", - "20100704T000000-1260", "20100704T000000+1260", - "20100704T000000.0-800", "20100704T000000.0+800", - "20100704T000000.0-8", "20100704T000000.0+8", - "20100704T000000.0-080", "20100704T000000.0+080", - "20100704T000000.0-2400", "20100704T000000.0+2400", - "20100704T000000.0-1260", "20100704T000000.0+1260", - "2010-07-04T00:00:00-8:00", "2010-07-04T00:00:00+8:00", - "2010-07-04T00:00:00-24:00", "2010-07-04T00:00:00+24:00", - "2010-07-04T00:00:00-12:60", "2010-07-04T00:00:00+12:60", - "2010-07-04T00:00:00.0-8:00", "2010-07-04T00:00:00.0+8:00", - "2010-07-04T00:00:00.0-8", "2010-07-04T00:00:00.0+8", - "2010-07-04T00:00:00.0-24:00", "2010-07-04T00:00:00.0+24:00", - "2010-07-04T00:00:00.0-12:60", "2010-07-04T00:00:00.0+12:60", - "2010-Jul-0400:00:00", "2010-Jul-04t00:00:00", - "2010-Jul-04 00:00:00.", "2010-Jul-04 00:00:00.0", - "20101222T172201", "2010-Dec-22 17:22:01"]) - { - assertThrown!DateTimeException(SysTime.fromISOExtString(str), format("[%s]", str)); - } - - static void test(string str, SysTime st, size_t line = __LINE__) - { - if (SysTime.fromISOExtString(str) != st) - throw new AssertError("unittest failure", __FILE__, line); - } - - test("2010-12-22T17:22:01", SysTime(DateTime(2010, 12, 22, 17, 22, 1))); - test("1999-07-06T12:30:33", SysTime(DateTime(1999, 7, 6, 12, 30, 33))); - test("-1999-07-06T12:30:33", SysTime(DateTime(-1999, 7, 6, 12, 30, 33))); - test("+01999-07-06T12:30:33", SysTime(DateTime(1999, 7, 6, 12, 30, 33))); - test("1999-07-06T12:30:33 ", SysTime(DateTime(1999, 7, 6, 12, 30, 33))); - test(" 1999-07-06T12:30:33", SysTime(DateTime(1999, 7, 6, 12, 30, 33))); - test(" 1999-07-06T12:30:33 ", SysTime(DateTime(1999, 7, 6, 12, 30, 33))); - - test("1907-07-07T12:12:12.0", SysTime(DateTime(1907, 7, 7, 12, 12, 12))); - test("1907-07-07T12:12:12.0000000", SysTime(DateTime(1907, 7, 7, 12, 12, 12))); - test("1907-07-07T12:12:12.0000001", SysTime(DateTime(1907, 7, 7, 12, 12, 12), hnsecs(1))); - test("2010-07-04T00:00:00.00000000", SysTime(Date(2010, 7, 4))); - test("2010-07-04T00:00:00.00000009", SysTime(Date(2010, 7, 4))); - test("2010-07-04T00:00:00.00000019", SysTime(DateTime(2010, 7, 4), hnsecs(1))); - test("1907-07-07T12:12:12.000001", SysTime(DateTime(1907, 7, 7, 12, 12, 12), usecs(1))); - test("1907-07-07T12:12:12.0000010", SysTime(DateTime(1907, 7, 7, 12, 12, 12), usecs(1))); - test("1907-07-07T12:12:12.001", SysTime(DateTime(1907, 7, 7, 12, 12, 12), msecs(1))); - test("1907-07-07T12:12:12.0010000", SysTime(DateTime(1907, 7, 7, 12, 12, 12), msecs(1))); - - auto west60 = new immutable SimpleTimeZone(hours(-1)); - auto west90 = new immutable SimpleTimeZone(minutes(-90)); - auto west480 = new immutable SimpleTimeZone(hours(-8)); - auto east60 = new immutable SimpleTimeZone(hours(1)); - auto east90 = new immutable SimpleTimeZone(minutes(90)); - auto east480 = new immutable SimpleTimeZone(hours(8)); - - test("2010-12-22T17:22:01Z", SysTime(DateTime(2010, 12, 22, 17, 22, 1), UTC())); - test("2010-12-22T17:22:01-01:00", SysTime(DateTime(2010, 12, 22, 17, 22, 1), west60)); - test("2010-12-22T17:22:01-01", SysTime(DateTime(2010, 12, 22, 17, 22, 1), west60)); - test("2010-12-22T17:22:01-01:30", SysTime(DateTime(2010, 12, 22, 17, 22, 1), west90)); - test("2010-12-22T17:22:01-08:00", SysTime(DateTime(2010, 12, 22, 17, 22, 1), west480)); - test("2010-12-22T17:22:01+01:00", SysTime(DateTime(2010, 12, 22, 17, 22, 1), east60)); - test("2010-12-22T17:22:01+01", SysTime(DateTime(2010, 12, 22, 17, 22, 1), east60)); - test("2010-12-22T17:22:01+01:30", SysTime(DateTime(2010, 12, 22, 17, 22, 1), east90)); - test("2010-12-22T17:22:01+08:00", SysTime(DateTime(2010, 12, 22, 17, 22, 1), east480)); - - test("2010-11-03T06:51:06.57159Z", SysTime(DateTime(2010, 11, 3, 6, 51, 6), hnsecs(5715900), UTC())); - test("2010-12-22T17:22:01.23412Z", SysTime(DateTime(2010, 12, 22, 17, 22, 1), hnsecs(2_341_200), UTC())); - test("2010-12-22T17:22:01.23112-01:00", - SysTime(DateTime(2010, 12, 22, 17, 22, 1), hnsecs(2_311_200), west60)); - test("2010-12-22T17:22:01.45-01", SysTime(DateTime(2010, 12, 22, 17, 22, 1), hnsecs(4_500_000), west60)); - test("2010-12-22T17:22:01.1-01:30", SysTime(DateTime(2010, 12, 22, 17, 22, 1), hnsecs(1_000_000), west90)); - test("2010-12-22T17:22:01.55-08:00", SysTime(DateTime(2010, 12, 22, 17, 22, 1), hnsecs(5_500_000), west480)); - test("2010-12-22T17:22:01.1234567+01:00", - SysTime(DateTime(2010, 12, 22, 17, 22, 1), hnsecs(1_234_567), east60)); - test("2010-12-22T17:22:01.0+01", SysTime(DateTime(2010, 12, 22, 17, 22, 1), east60)); - test("2010-12-22T17:22:01.0000000+01:30", SysTime(DateTime(2010, 12, 22, 17, 22, 1), east90)); - test("2010-12-22T17:22:01.45+08:00", SysTime(DateTime(2010, 12, 22, 17, 22, 1), hnsecs(4_500_000), east480)); - - static void testScope(scope ref string str) @safe - { - auto result = SysTime.fromISOExtString(str); - } - } - - // https://issues.dlang.org/show_bug.cgi?id=17801 - @safe unittest - { - import core.time; - import std.conv : to; - import std.meta : AliasSeq; - static foreach (C; AliasSeq!(char, wchar, dchar)) - { - static foreach (S; AliasSeq!(C[], const(C)[], immutable(C)[])) - { - assert(SysTime.fromISOExtString(to!S("2012-12-21T14:15:16Z")) == - SysTime(DateTime(2012, 12, 21, 14, 15, 16), UTC())); - } - } - } - - - /++ - Creates a $(LREF SysTime) from a string with the format - YYYY-Mon-DD HH:MM:SS.FFFFFFFTZ (where F is fractional seconds and TZ - is the time zone). Whitespace is stripped from the given string. - - The exact format is exactly as described in $(LREF toSimpleString) except - that trailing zeroes are permitted - including having fractional seconds - with all zeroes. The time zone and fractional seconds are optional, - however, a decimal point with nothing following it is invalid. - Also, while $(LREF toSimpleString) will never generate a - string with more than 7 digits in the fractional seconds (because that's - the limit with hecto-nanosecond precision), it will allow more than 7 - digits in order to read strings from other sources that have higher - precision (however, any digits beyond 7 will be truncated). - - If there is no time zone in the string, then - $(REF LocalTime,std,datetime,timezone) is used. If the time zone is "Z", - then `UTC` is used. Otherwise, a - $(REF SimpleTimeZone,std,datetime,timezone) which corresponds to the - given offset from UTC is used. To get the returned $(LREF SysTime) to be - a particular time zone, pass in that time zone and the $(LREF SysTime) - to be returned will be converted to that time zone (though it will still - be read in as whatever time zone is in its string). - - The accepted formats for time zone offsets are +HH, -HH, +HH:MM, and - -HH:MM. - - Params: - simpleString = A string formatted in the way that - `toSimpleString` formats dates and times. - tz = The time zone to convert the given time to (no - conversion occurs if null). - - Throws: - $(REF DateTimeException,std,datetime,date) if the given string is - not in the ISO format or if the resulting $(LREF SysTime) would not - be valid. - +/ - static SysTime fromSimpleString(S)(scope const S simpleString, immutable TimeZone tz = null) @safe - if (isSomeString!(S)) - { - import std.algorithm.searching : find; - import std.conv : to; - import std.string : strip, indexOf; - - auto str = strip(simpleString); - - auto spaceIndex = str.indexOf(' '); - enforce(spaceIndex != -1, new DateTimeException(format("Invalid Simple String: %s", simpleString))); - - auto found = str[spaceIndex + 1 .. $].find('.', 'Z', '+', '-'); - auto dateTimeStr = str[0 .. $ - found[0].length]; - - typeof(str) foundTZ; // needs to have longer lifetime than zoneStr - typeof(str) fracSecStr; - typeof(str) zoneStr; - - if (found[1] != 0) - { - if (found[1] == 1) - { - foundTZ = found[0].find('Z', '+', '-')[0]; - - if (foundTZ.length != 0) - { - fracSecStr = found[0][0 .. $ - foundTZ.length]; - zoneStr = foundTZ; - } - else - fracSecStr = found[0]; - } - else - zoneStr = found[0]; - } - - try - { - auto dateTime = DateTime.fromSimpleString(dateTimeStr); - auto fracSec = fracSecsFromISOString(fracSecStr); - Rebindable!(immutable TimeZone) parsedZone; - - if (zoneStr.empty) - parsedZone = LocalTime(); - else if (zoneStr == "Z") - parsedZone = UTC(); - else - parsedZone = SimpleTimeZone.fromISOExtString(zoneStr); - - auto retval = SysTime(dateTime, fracSec, parsedZone); - - if (tz !is null) - retval.timezone = tz; - - return retval; - } - catch (DateTimeException dte) - throw new DateTimeException(format("Invalid Simple String: %s", simpleString)); - } - - /// - @safe unittest - { - import core.time : hours, msecs, usecs, hnsecs; - import std.datetime.date : DateTime; - import std.datetime.timezone : SimpleTimeZone, UTC; - - assert(SysTime.fromSimpleString("2010-Jul-04 07:06:12") == - SysTime(DateTime(2010, 7, 4, 7, 6, 12))); - - assert(SysTime.fromSimpleString("1998-Dec-25 02:15:00.007") == - SysTime(DateTime(1998, 12, 25, 2, 15, 0), msecs(7))); - - assert(SysTime.fromSimpleString("0000-Jan-05 23:09:59.00002") == - SysTime(DateTime(0, 1, 5, 23, 9, 59), usecs(20))); - - assert(SysTime.fromSimpleString("2013-Feb-07 04:39:37.000050392") == - SysTime(DateTime(2013, 2, 7, 4, 39, 37), hnsecs(503))); - - assert(SysTime.fromSimpleString("-0004-Jan-05 00:00:02") == - SysTime(DateTime(-4, 1, 5, 0, 0, 2))); - - assert(SysTime.fromSimpleString(" 2010-Jul-04 07:06:12 ") == - SysTime(DateTime(2010, 7, 4, 7, 6, 12))); - - assert(SysTime.fromSimpleString("2010-Jul-04 07:06:12Z") == - SysTime(DateTime(2010, 7, 4, 7, 6, 12), UTC())); - - assert(SysTime.fromSimpleString("2010-Jul-04 07:06:12-08:00") == - SysTime(DateTime(2010, 7, 4, 7, 6, 12), - new immutable SimpleTimeZone(hours(-8)))); - - assert(SysTime.fromSimpleString("2010-Jul-04 07:06:12+08:00") == - SysTime(DateTime(2010, 7, 4, 7, 6, 12), - new immutable SimpleTimeZone(hours(8)))); - } - - @safe unittest - { - import core.time; - foreach (str; ["", "20100704000000", "20100704 000000", - "20100704t000000", "20100704T000000.", "20100704T000000.0", - "2010-07-0400:00:00", "2010-07-04 00:00:00", "2010-07-04t00:00:00", - "2010-07-04T00:00:00.", "2010-07-04T00:00:00.0", - "2010-Jul-0400:00:00", "2010-Jul-04t00:00:00", "2010-Jul-04T00:00:00", - "2010-Jul-04 00:00:00.", "2010-Jul-04 00:00:00.A", "2010-Jul-04 00:00:00.Z", - "2010-Jul-04 00:00:00.0000000A", "2010-Jul-04 00:00:00.00000000A", - "2010-Jul-04 00:00:00+", "2010-Jul-04 00:00:00-", - "2010-Jul-04 00:00:00:", "2010-Jul-04 00:00:00-:", - "2010-Jul-04 00:00:00+:", "2010-Jul-04 00:00:00-1:", - "2010-Jul-04 00:00:00+1:", "2010-Jul-04 00:00:00+1:0", - "2010-Jul-04 00:00:00-12.00", "2010-Jul-04 00:00:00+12.00", - "2010-Jul-04 00:00:00-8", "2010-Jul-04 00:00:00+8", - "20100704T000000-800", "20100704T000000+800", - "20100704T000000-080", "20100704T000000+080", - "20100704T000000-2400", "20100704T000000+2400", - "20100704T000000-1260", "20100704T000000+1260", - "20100704T000000.0-800", "20100704T000000.0+800", - "20100704T000000.0-8", "20100704T000000.0+8", - "20100704T000000.0-080", "20100704T000000.0+080", - "20100704T000000.0-2400", "20100704T000000.0+2400", - "20100704T000000.0-1260", "20100704T000000.0+1260", - "2010-Jul-04 00:00:00-8:00", "2010-Jul-04 00:00:00+8:00", - "2010-Jul-04 00:00:00-08:0", "2010-Jul-04 00:00:00+08:0", - "2010-Jul-04 00:00:00-24:00", "2010-Jul-04 00:00:00+24:00", - "2010-Jul-04 00:00:00-12:60", "2010-Jul-04 00:00:00+24:60", - "2010-Jul-04 00:00:00.0-8:00", "2010-Jul-04 00:00:00+8:00", - "2010-Jul-04 00:00:00.0-8", "2010-Jul-04 00:00:00.0+8", - "2010-Jul-04 00:00:00.0-08:0", "2010-Jul-04 00:00:00.0+08:0", - "2010-Jul-04 00:00:00.0-24:00", "2010-Jul-04 00:00:00.0+24:00", - "2010-Jul-04 00:00:00.0-12:60", "2010-Jul-04 00:00:00.0+24:60", - "20101222T172201", "2010-12-22T172201"]) - { - assertThrown!DateTimeException(SysTime.fromSimpleString(str), format("[%s]", str)); - } - - static void test(string str, SysTime st, size_t line = __LINE__) - { - if (SysTime.fromSimpleString(str) != st) - throw new AssertError("unittest failure", __FILE__, line); - } - - test("2010-Dec-22 17:22:01", SysTime(DateTime(2010, 12, 22, 17, 22, 1))); - test("1999-Jul-06 12:30:33", SysTime(DateTime(1999, 7, 6, 12, 30, 33))); - test("-1999-Jul-06 12:30:33", SysTime(DateTime(-1999, 7, 6, 12, 30, 33))); - test("+01999-Jul-06 12:30:33", SysTime(DateTime(1999, 7, 6, 12, 30, 33))); - test("1999-Jul-06 12:30:33 ", SysTime(DateTime(1999, 7, 6, 12, 30, 33))); - test(" 1999-Jul-06 12:30:33", SysTime(DateTime(1999, 7, 6, 12, 30, 33))); - test(" 1999-Jul-06 12:30:33 ", SysTime(DateTime(1999, 7, 6, 12, 30, 33))); - - test("1907-Jul-07 12:12:12.0", SysTime(DateTime(1907, 7, 7, 12, 12, 12))); - test("1907-Jul-07 12:12:12.0000000", SysTime(DateTime(1907, 7, 7, 12, 12, 12))); - test("2010-Jul-04 00:00:00.00000000", SysTime(Date(2010, 7, 4))); - test("2010-Jul-04 00:00:00.00000009", SysTime(Date(2010, 7, 4))); - test("2010-Jul-04 00:00:00.00000019", SysTime(DateTime(2010, 7, 4), hnsecs(1))); - test("1907-Jul-07 12:12:12.0000001", SysTime(DateTime(1907, 7, 7, 12, 12, 12), hnsecs(1))); - test("1907-Jul-07 12:12:12.000001", SysTime(DateTime(1907, 7, 7, 12, 12, 12), usecs(1))); - test("1907-Jul-07 12:12:12.0000010", SysTime(DateTime(1907, 7, 7, 12, 12, 12), usecs(1))); - test("1907-Jul-07 12:12:12.001", SysTime(DateTime(1907, 7, 7, 12, 12, 12), msecs(1))); - test("1907-Jul-07 12:12:12.0010000", SysTime(DateTime(1907, 7, 7, 12, 12, 12), msecs(1))); - - auto west60 = new immutable SimpleTimeZone(hours(-1)); - auto west90 = new immutable SimpleTimeZone(minutes(-90)); - auto west480 = new immutable SimpleTimeZone(hours(-8)); - auto east60 = new immutable SimpleTimeZone(hours(1)); - auto east90 = new immutable SimpleTimeZone(minutes(90)); - auto east480 = new immutable SimpleTimeZone(hours(8)); - - test("2010-Dec-22 17:22:01Z", SysTime(DateTime(2010, 12, 22, 17, 22, 1), UTC())); - test("2010-Dec-22 17:22:01-01:00", SysTime(DateTime(2010, 12, 22, 17, 22, 1), west60)); - test("2010-Dec-22 17:22:01-01", SysTime(DateTime(2010, 12, 22, 17, 22, 1), west60)); - test("2010-Dec-22 17:22:01-01:30", SysTime(DateTime(2010, 12, 22, 17, 22, 1), west90)); - test("2010-Dec-22 17:22:01-08:00", SysTime(DateTime(2010, 12, 22, 17, 22, 1), west480)); - test("2010-Dec-22 17:22:01+01:00", SysTime(DateTime(2010, 12, 22, 17, 22, 1), east60)); - test("2010-Dec-22 17:22:01+01", SysTime(DateTime(2010, 12, 22, 17, 22, 1), east60)); - test("2010-Dec-22 17:22:01+01:30", SysTime(DateTime(2010, 12, 22, 17, 22, 1), east90)); - test("2010-Dec-22 17:22:01+08:00", SysTime(DateTime(2010, 12, 22, 17, 22, 1), east480)); - - test("2010-Nov-03 06:51:06.57159Z", SysTime(DateTime(2010, 11, 3, 6, 51, 6), hnsecs(5715900), UTC())); - test("2010-Dec-22 17:22:01.23412Z", SysTime(DateTime(2010, 12, 22, 17, 22, 1), hnsecs(2_341_200), UTC())); - test("2010-Dec-22 17:22:01.23112-01:00", - SysTime(DateTime(2010, 12, 22, 17, 22, 1), hnsecs(2_311_200), west60)); - test("2010-Dec-22 17:22:01.45-01", SysTime(DateTime(2010, 12, 22, 17, 22, 1), hnsecs(4_500_000), west60)); - test("2010-Dec-22 17:22:01.1-01:30", SysTime(DateTime(2010, 12, 22, 17, 22, 1), hnsecs(1_000_000), west90)); - test("2010-Dec-22 17:22:01.55-08:00", SysTime(DateTime(2010, 12, 22, 17, 22, 1), hnsecs(5_500_000), west480)); - test("2010-Dec-22 17:22:01.1234567+01:00", - SysTime(DateTime(2010, 12, 22, 17, 22, 1), hnsecs(1_234_567), east60)); - test("2010-Dec-22 17:22:01.0+01", SysTime(DateTime(2010, 12, 22, 17, 22, 1), east60)); - test("2010-Dec-22 17:22:01.0000000+01:30", SysTime(DateTime(2010, 12, 22, 17, 22, 1), east90)); - test("2010-Dec-22 17:22:01.45+08:00", SysTime(DateTime(2010, 12, 22, 17, 22, 1), hnsecs(4_500_000), east480)); - - static void testScope(scope ref string str) @safe - { - auto result = SysTime.fromSimpleString(str); - } - } - - // https://issues.dlang.org/show_bug.cgi?id=17801 - @safe unittest - { - import core.time; - import std.conv : to; - import std.meta : AliasSeq; - static foreach (C; AliasSeq!(char, wchar, dchar)) - { - static foreach (S; AliasSeq!(C[], const(C)[], immutable(C)[])) - { - assert(SysTime.fromSimpleString(to!S("2012-Dec-21 14:15:16Z")) == - SysTime(DateTime(2012, 12, 21, 14, 15, 16), UTC())); - } - } - } - - - /++ - Returns the $(LREF SysTime) farthest in the past which is representable - by $(LREF SysTime). - - The $(LREF SysTime) which is returned is in UTC. - +/ - @property static SysTime min() @safe pure nothrow - { - return SysTime(long.min, UTC()); - } - - @safe unittest - { - assert(SysTime.min.year < 0); - assert(SysTime.min < SysTime.max); - } - - - /++ - Returns the $(LREF SysTime) farthest in the future which is representable - by $(LREF SysTime). - - The $(LREF SysTime) which is returned is in UTC. - +/ - @property static SysTime max() @safe pure nothrow - { - return SysTime(long.max, UTC()); - } - - @safe unittest - { - assert(SysTime.max.year > 0); - assert(SysTime.max > SysTime.min); - } - - -private: - - /+ - Returns `stdTime` converted to $(LREF SysTime)'s time zone. - +/ - @property long adjTime() @safe const nothrow scope - { - return _timezone.utcToTZ(_stdTime); - } - - - /+ - Converts the given hnsecs from $(LREF SysTime)'s time zone to std time. - +/ - @property void adjTime(long adjTime) @safe nothrow scope - { - _stdTime = _timezone.tzToUTC(adjTime); - } - - - final class InitTimeZone : TimeZone - { - public: - - static immutable(InitTimeZone) opCall() @safe pure nothrow @nogc { return _initTimeZone; } - - @property override bool hasDST() @safe const nothrow @nogc { return false; } - - override bool dstInEffect(long stdTime) @safe const scope nothrow @nogc { return false; } - - override long utcToTZ(long stdTime) @safe const scope nothrow @nogc { return 0; } - - override long tzToUTC(long adjTime) @safe const scope nothrow @nogc { return 0; } - - override Duration utcOffsetAt(long stdTime) @safe const scope nothrow @nogc { return Duration.zero; } - - private: - - this() @safe immutable pure - { - super("SysTime.init's timezone", "SysTime.init's timezone", "SysTime.init's timezone"); - } - - static immutable InitTimeZone _initTimeZone = new immutable(InitTimeZone); - } - - // https://issues.dlang.org/show_bug.cgi?id=17732 - @safe unittest - { - assert(SysTime.init.timezone is InitTimeZone()); - assert(SysTime.init.toISOString() == "00010101T000000+00:00"); - assert(SysTime.init.toISOExtString() == "0001-01-01T00:00:00+00:00"); - assert(SysTime.init.toSimpleString() == "0001-Jan-01 00:00:00+00:00"); - assert(SysTime.init.toString() == "0001-Jan-01 00:00:00+00:00"); - } - - // Assigning a value to _timezone in SysTime.init currently doesn't work due - // to https://issues.dlang.org/show_bug.cgi?id=17740. So, to hack around - // that problem, these accessors have been added so that we can insert a - // runtime check for null and then use InitTimeZone for SysTime.init (which - // which is the only case where _timezone would be null). This thus fixes - // the problem with segfaulting when using SysTime.init but at the cost of - // what should be an unnecessary null check. Once 17740 has finally been - // fixed, _timezoneStorage should be removed, these accessors should be - // removed, and the _timezone variable declaration should be restored. - pragma(inline, true) @property _timezone() @safe const pure nothrow @nogc - { - return _timezoneStorage is null ? InitTimeZone() : _timezoneStorage; - } - - pragma(inline, true) @property void _timezone(return scope immutable TimeZone tz) @safe pure nothrow @nogc scope - { - _timezoneStorage = tz; - } - - - long _stdTime; - Rebindable!(immutable TimeZone) _timezoneStorage; - //Rebindable!(immutable TimeZone) _timezone = InitTimeZone(); -} - -/// -@safe unittest -{ - import core.time : days, hours, seconds; - import std.datetime.date : Date, DateTime; - import std.datetime.timezone : SimpleTimeZone, UTC; - - const dt = DateTime(2018, 1, 1, 10, 30, 0); - // make a specific point in time in the UTC timezone - auto st = SysTime(dt, UTC()); - assert(st.year == 2018); - assert(st.hour == 10); - - // cast to convert - assert(cast(DateTime) st == dt); - assert(cast(Date) st == Date(2018, 1, 1)); - - // make a specific point in time in the New York timezone - const ny = SysTime(dt, - new immutable SimpleTimeZone(-5.hours, "America/New_York") - ); - assert(ny != st); - assert(ny.hour == 10); - - // ISO standard time strings - assert(st.toISOString() == "20180101T103000Z"); - assert(st.toISOExtString() == "2018-01-01T10:30:00Z"); - - // add two days and 30 seconds - st += 2.days + 30.seconds; - assert(st.toISOExtString() == "2018-01-03T10:30:30Z"); -} - - -/++ - Converts from unix time (which uses midnight, January 1st, 1970 UTC as its - epoch and seconds as its units) to "std time" (which uses midnight, - January 1st, 1 A.D. UTC and hnsecs as its units). - - The C standard does not specify the representation of time_t, so it is - implementation defined. On POSIX systems, unix time is equivalent to - time_t, but that's not necessarily true on other systems (e.g. it is - not true for the Digital Mars C runtime). So, be careful when using unix - time with C functions on non-POSIX systems. - - "std time"'s epoch is based on the Proleptic Gregorian Calendar per ISO - 8601 and is what $(LREF SysTime) uses internally. However, holding the time - as an integer in hnsecs since that epoch technically isn't actually part of - the standard, much as it's based on it, so the name "std time" isn't - particularly good, but there isn't an official name for it. C# uses "ticks" - for the same thing, but they aren't actually clock ticks, and the term - "ticks" $(I is) used for actual clock ticks for $(REF MonoTime, core,time), - so it didn't make sense to use the term ticks here. So, for better or worse, - std.datetime uses the term "std time" for this. - - Params: - unixTime = The unix time to convert. - - See_Also: - SysTime.fromUnixTime - +/ -long unixTimeToStdTime(long unixTime) @safe pure nothrow @nogc -{ - return 621_355_968_000_000_000L + convert!("seconds", "hnsecs")(unixTime); -} - -/// -@safe unittest -{ - import std.datetime.date : DateTime; - import std.datetime.timezone : UTC; - - // Midnight, January 1st, 1970 - assert(unixTimeToStdTime(0) == 621_355_968_000_000_000L); - assert(SysTime(unixTimeToStdTime(0)) == - SysTime(DateTime(1970, 1, 1), UTC())); - - assert(unixTimeToStdTime(int.max) == 642_830_804_470_000_000L); - assert(SysTime(unixTimeToStdTime(int.max)) == - SysTime(DateTime(2038, 1, 19, 3, 14, 7), UTC())); - - assert(unixTimeToStdTime(-127_127) == 621_354_696_730_000_000L); - assert(SysTime(unixTimeToStdTime(-127_127)) == - SysTime(DateTime(1969, 12, 30, 12, 41, 13), UTC())); -} - -@safe unittest -{ - // Midnight, January 2nd, 1970 - assert(unixTimeToStdTime(86_400) == 621_355_968_000_000_000L + 864_000_000_000L); - // Midnight, December 31st, 1969 - assert(unixTimeToStdTime(-86_400) == 621_355_968_000_000_000L - 864_000_000_000L); - - assert(unixTimeToStdTime(0) == (Date(1970, 1, 1) - Date(1, 1, 1)).total!"hnsecs"); - assert(unixTimeToStdTime(0) == (DateTime(1970, 1, 1) - DateTime(1, 1, 1)).total!"hnsecs"); - - foreach (dt; [DateTime(2010, 11, 1, 19, 5, 22), DateTime(1952, 7, 6, 2, 17, 9)]) - assert(unixTimeToStdTime((dt - DateTime(1970, 1, 1)).total!"seconds") == (dt - DateTime.init).total!"hnsecs"); -} - - -/++ - Converts std time (which uses midnight, January 1st, 1 A.D. UTC as its epoch - and hnsecs as its units) to unix time (which uses midnight, January 1st, - 1970 UTC as its epoch and seconds as its units). - - The C standard does not specify the representation of time_t, so it is - implementation defined. On POSIX systems, unix time is equivalent to - time_t, but that's not necessarily true on other systems (e.g. it is - not true for the Digital Mars C runtime). So, be careful when using unix - time with C functions on non-POSIX systems. - - "std time"'s epoch is based on the Proleptic Gregorian Calendar per ISO - 8601 and is what $(LREF SysTime) uses internally. However, holding the time - as an integer in hnsecs since that epoch technically isn't actually part of - the standard, much as it's based on it, so the name "std time" isn't - particularly good, but there isn't an official name for it. C# uses "ticks" - for the same thing, but they aren't actually clock ticks, and the term - "ticks" $(I is) used for actual clock ticks for $(REF MonoTime, core,time), - so it didn't make sense to use the term ticks here. So, for better or worse, - std.datetime uses the term "std time" for this. - - By default, the return type is time_t (which is normally an alias for - int on 32-bit systems and long on 64-bit systems), but if a different - size is required than either int or long can be passed as a template - argument to get the desired size. - - If the return type is int, and the result can't fit in an int, then the - closest value that can be held in 32 bits will be used (so `int.max` - if it goes over and `int.min` if it goes under). However, no attempt - is made to deal with integer overflow if the return type is long. - - Params: - T = The return type (int or long). It defaults to time_t, which is - normally 32 bits on a 32-bit system and 64 bits on a 64-bit - system. - stdTime = The std time to convert. - - Returns: - A signed integer representing the unix time which is equivalent to - the given std time. - - See_Also: - SysTime.toUnixTime - +/ -T stdTimeToUnixTime(T = time_t)(long stdTime) @safe pure nothrow -if (is(T == int) || is(T == long)) -{ - immutable unixTime = convert!("hnsecs", "seconds")(stdTime - 621_355_968_000_000_000L); - - static assert(is(time_t == int) || is(time_t == long), - "Currently, std.datetime only supports systems where time_t is int or long"); - - static if (is(T == long)) - return unixTime; - else static if (is(T == int)) - { - if (unixTime > int.max) - return int.max; - return unixTime < int.min ? int.min : cast(int) unixTime; - } - else - static assert(0, "Bug in template constraint. Only int and long allowed."); -} - -/// -@safe unittest -{ - // Midnight, January 1st, 1970 UTC - assert(stdTimeToUnixTime(621_355_968_000_000_000L) == 0); - - // 2038-01-19 03:14:07 UTC - assert(stdTimeToUnixTime(642_830_804_470_000_000L) == int.max); -} - -@safe unittest -{ - enum unixEpochAsStdTime = (Date(1970, 1, 1) - Date.init).total!"hnsecs"; - - assert(stdTimeToUnixTime(unixEpochAsStdTime) == 0); // Midnight, January 1st, 1970 - assert(stdTimeToUnixTime(unixEpochAsStdTime + 864_000_000_000L) == 86_400); // Midnight, January 2nd, 1970 - assert(stdTimeToUnixTime(unixEpochAsStdTime - 864_000_000_000L) == -86_400); // Midnight, December 31st, 1969 - - assert(stdTimeToUnixTime((Date(1970, 1, 1) - Date(1, 1, 1)).total!"hnsecs") == 0); - assert(stdTimeToUnixTime((DateTime(1970, 1, 1) - DateTime(1, 1, 1)).total!"hnsecs") == 0); - - foreach (dt; [DateTime(2010, 11, 1, 19, 5, 22), DateTime(1952, 7, 6, 2, 17, 9)]) - assert(stdTimeToUnixTime((dt - DateTime.init).total!"hnsecs") == (dt - DateTime(1970, 1, 1)).total!"seconds"); - - enum max = convert!("seconds", "hnsecs")(int.max); - enum min = convert!("seconds", "hnsecs")(int.min); - enum one = convert!("seconds", "hnsecs")(1); - - assert(stdTimeToUnixTime!long(unixEpochAsStdTime + max) == int.max); - assert(stdTimeToUnixTime!int(unixEpochAsStdTime + max) == int.max); - - assert(stdTimeToUnixTime!long(unixEpochAsStdTime + max + one) == int.max + 1L); - assert(stdTimeToUnixTime!int(unixEpochAsStdTime + max + one) == int.max); - assert(stdTimeToUnixTime!long(unixEpochAsStdTime + max + 9_999_999) == int.max); - assert(stdTimeToUnixTime!int(unixEpochAsStdTime + max + 9_999_999) == int.max); - - assert(stdTimeToUnixTime!long(unixEpochAsStdTime + min) == int.min); - assert(stdTimeToUnixTime!int(unixEpochAsStdTime + min) == int.min); - - assert(stdTimeToUnixTime!long(unixEpochAsStdTime + min - one) == int.min - 1L); - assert(stdTimeToUnixTime!int(unixEpochAsStdTime + min - one) == int.min); - assert(stdTimeToUnixTime!long(unixEpochAsStdTime + min - 9_999_999) == int.min); - assert(stdTimeToUnixTime!int(unixEpochAsStdTime + min - 9_999_999) == int.min); -} - - -version (StdDdoc) -{ - version (Windows) - {} - else - { - alias SYSTEMTIME = void*; - alias FILETIME = void*; - } - - /++ - $(BLUE This function is Windows-Only.) - - Converts a `SYSTEMTIME` struct to a $(LREF SysTime). - - Params: - st = The `SYSTEMTIME` struct to convert. - tz = The time zone that the time in the `SYSTEMTIME` struct is - assumed to be (if the `SYSTEMTIME` was supplied by a Windows - system call, the `SYSTEMTIME` will either be in local time - or UTC, depending on the call). - - Throws: - $(REF DateTimeException,std,datetime,date) if the given - `SYSTEMTIME` will not fit in a $(LREF SysTime), which is highly - unlikely to happen given that `SysTime.max` is in 29,228 A.D. and - the maximum `SYSTEMTIME` is in 30,827 A.D. - +/ - SysTime SYSTEMTIMEToSysTime(const scope SYSTEMTIME* st, immutable TimeZone tz = LocalTime()) @safe; - - - /++ - $(BLUE This function is Windows-Only.) - - Converts a $(LREF SysTime) to a `SYSTEMTIME` struct. - - The `SYSTEMTIME` which is returned will be set using the given - $(LREF SysTime)'s time zone, so to get the `SYSTEMTIME` in - UTC, set the $(LREF SysTime)'s time zone to UTC. - - Params: - sysTime = The $(LREF SysTime) to convert. - - Throws: - $(REF DateTimeException,std,datetime,date) if the given - $(LREF SysTime) will not fit in a `SYSTEMTIME`. This will only - happen if the $(LREF SysTime)'s date is prior to 1601 A.D. - +/ - SYSTEMTIME SysTimeToSYSTEMTIME(scope SysTime sysTime) @safe; - - - /++ - $(BLUE This function is Windows-Only.) - - Converts a `FILETIME` struct to the number of hnsecs since midnight, - January 1st, 1 A.D. - - Params: - ft = The `FILETIME` struct to convert. - - Throws: - $(REF DateTimeException,std,datetime,date) if the given - `FILETIME` cannot be represented as the return value. - +/ - long FILETIMEToStdTime(scope const FILETIME* ft) @safe; - - - /++ - $(BLUE This function is Windows-Only.) - - Converts a `FILETIME` struct to a $(LREF SysTime). - - Params: - ft = The `FILETIME` struct to convert. - tz = The time zone that the $(LREF SysTime) will be in - (`FILETIME`s are in UTC). - - Throws: - $(REF DateTimeException,std,datetime,date) if the given - `FILETIME` will not fit in a $(LREF SysTime). - +/ - SysTime FILETIMEToSysTime(scope const FILETIME* ft, immutable TimeZone tz = LocalTime()) @safe; - - - /++ - $(BLUE This function is Windows-Only.) - - Converts a number of hnsecs since midnight, January 1st, 1 A.D. to a - `FILETIME` struct. - - Params: - stdTime = The number of hnsecs since midnight, January 1st, 1 A.D. - UTC. - - Throws: - $(REF DateTimeException,std,datetime,date) if the given value will - not fit in a `FILETIME`. - +/ - FILETIME stdTimeToFILETIME(long stdTime) @safe; - - - /++ - $(BLUE This function is Windows-Only.) - - Converts a $(LREF SysTime) to a `FILETIME` struct. - - `FILETIME`s are always in UTC. - - Params: - sysTime = The $(LREF SysTime) to convert. - - Throws: - $(REF DateTimeException,std,datetime,date) if the given - $(LREF SysTime) will not fit in a `FILETIME`. - +/ - FILETIME SysTimeToFILETIME(scope SysTime sysTime) @safe; -} -else version (Windows) -{ - SysTime SYSTEMTIMEToSysTime(const scope SYSTEMTIME* st, immutable TimeZone tz = LocalTime()) @safe - { - const max = SysTime.max; - - static void throwLaterThanMax() - { - throw new DateTimeException("The given SYSTEMTIME is for a date greater than SysTime.max."); - } - - if (st.wYear > max.year) - throwLaterThanMax(); - else if (st.wYear == max.year) - { - if (st.wMonth > max.month) - throwLaterThanMax(); - else if (st.wMonth == max.month) - { - if (st.wDay > max.day) - throwLaterThanMax(); - else if (st.wDay == max.day) - { - if (st.wHour > max.hour) - throwLaterThanMax(); - else if (st.wHour == max.hour) - { - if (st.wMinute > max.minute) - throwLaterThanMax(); - else if (st.wMinute == max.minute) - { - if (st.wSecond > max.second) - throwLaterThanMax(); - else if (st.wSecond == max.second) - { - if (st.wMilliseconds > max.fracSecs.total!"msecs") - throwLaterThanMax(); - } - } - } - } - } - } - - auto dt = DateTime(st.wYear, st.wMonth, st.wDay, st.wHour, st.wMinute, st.wSecond); - - import core.time : msecs; - return SysTime(dt, msecs(st.wMilliseconds), tz); - } - - @system unittest - { - auto sysTime = Clock.currTime(UTC()); - SYSTEMTIME st = void; - GetSystemTime(&st); - auto converted = SYSTEMTIMEToSysTime(&st, UTC()); - import core.time : abs; - assert(abs((converted - sysTime)) <= dur!"seconds"(2)); - - static void testScope(scope SYSTEMTIME* st) @safe - { - auto result = SYSTEMTIMEToSysTime(st); - } - } - - - SYSTEMTIME SysTimeToSYSTEMTIME(scope SysTime sysTime) @safe - { - immutable dt = cast(DateTime) sysTime; - - if (dt.year < 1601) - throw new DateTimeException("SYSTEMTIME cannot hold dates prior to the year 1601."); - - SYSTEMTIME st; - - st.wYear = dt.year; - st.wMonth = dt.month; - st.wDayOfWeek = dt.dayOfWeek; - st.wDay = dt.day; - st.wHour = dt.hour; - st.wMinute = dt.minute; - st.wSecond = dt.second; - st.wMilliseconds = cast(ushort) sysTime.fracSecs.total!"msecs"; - - return st; - } - - @system unittest - { - SYSTEMTIME st = void; - GetSystemTime(&st); - auto sysTime = SYSTEMTIMEToSysTime(&st, UTC()); - - SYSTEMTIME result = SysTimeToSYSTEMTIME(sysTime); - - assert(st.wYear == result.wYear); - assert(st.wMonth == result.wMonth); - assert(st.wDayOfWeek == result.wDayOfWeek); - assert(st.wDay == result.wDay); - assert(st.wHour == result.wHour); - assert(st.wMinute == result.wMinute); - assert(st.wSecond == result.wSecond); - assert(st.wMilliseconds == result.wMilliseconds); - - static void testScope(scope ref SysTime st) @safe - { - auto localResult = SysTimeToSYSTEMTIME(st); - } - } - - private enum hnsecsFrom1601 = 504_911_232_000_000_000L; - - long FILETIMEToStdTime(scope const FILETIME* ft) @safe - { - ULARGE_INTEGER ul; - ul.HighPart = ft.dwHighDateTime; - ul.LowPart = ft.dwLowDateTime; - ulong tempHNSecs = ul.QuadPart; - - if (tempHNSecs > long.max - hnsecsFrom1601) - throw new DateTimeException("The given FILETIME cannot be represented as a stdTime value."); - - return cast(long) tempHNSecs + hnsecsFrom1601; - } - - SysTime FILETIMEToSysTime(scope const FILETIME* ft, immutable TimeZone tz = LocalTime()) @safe - { - auto sysTime = SysTime(FILETIMEToStdTime(ft), UTC()); - sysTime.timezone = tz; - return sysTime; - } - - @system unittest - { - auto sysTime = Clock.currTime(UTC()); - SYSTEMTIME st = void; - GetSystemTime(&st); - - FILETIME ft = void; - SystemTimeToFileTime(&st, &ft); - - auto converted = FILETIMEToSysTime(&ft); - - import core.time : abs; - assert(abs((converted - sysTime)) <= dur!"seconds"(2)); - - static void testScope(scope FILETIME* ft) @safe - { - auto result = FILETIMEToSysTime(ft); - } - } - - - FILETIME stdTimeToFILETIME(long stdTime) @safe - { - if (stdTime < hnsecsFrom1601) - throw new DateTimeException("The given stdTime value cannot be represented as a FILETIME."); - - ULARGE_INTEGER ul; - ul.QuadPart = cast(ulong) stdTime - hnsecsFrom1601; - - FILETIME ft; - ft.dwHighDateTime = ul.HighPart; - ft.dwLowDateTime = ul.LowPart; - - return ft; - } - - FILETIME SysTimeToFILETIME(scope SysTime sysTime) @safe - { - return stdTimeToFILETIME(sysTime.stdTime); - } - - @system unittest - { - SYSTEMTIME st = void; - GetSystemTime(&st); - - FILETIME ft = void; - SystemTimeToFileTime(&st, &ft); - auto sysTime = FILETIMEToSysTime(&ft, UTC()); - - FILETIME result = SysTimeToFILETIME(sysTime); - - assert(ft.dwLowDateTime == result.dwLowDateTime); - assert(ft.dwHighDateTime == result.dwHighDateTime); - - static void testScope(scope ref SysTime st) @safe - { - auto local_result = SysTimeToFILETIME(st); - } - } -} - - -/++ - Type representing the DOS file date/time format. - +/ -alias DosFileTime = uint; - -/++ - Converts from DOS file date/time to $(LREF SysTime). - - Params: - dft = The DOS file time to convert. - tz = The time zone which the DOS file time is assumed to be in. - - Throws: - $(REF DateTimeException,std,datetime,date) if the `DosFileTime` is - invalid. - +/ -SysTime DosFileTimeToSysTime(DosFileTime dft, immutable TimeZone tz = LocalTime()) @safe -{ - uint dt = cast(uint) dft; - - if (dt == 0) - throw new DateTimeException("Invalid DosFileTime."); - - int year = ((dt >> 25) & 0x7F) + 1980; - int month = ((dt >> 21) & 0x0F); // 1 .. 12 - int dayOfMonth = ((dt >> 16) & 0x1F); // 1 .. 31 - int hour = (dt >> 11) & 0x1F; // 0 .. 23 - int minute = (dt >> 5) & 0x3F; // 0 .. 59 - int second = (dt << 1) & 0x3E; // 0 .. 58 (in 2 second increments) - - try - return SysTime(DateTime(year, month, dayOfMonth, hour, minute, second), tz); - catch (DateTimeException dte) - throw new DateTimeException("Invalid DosFileTime", __FILE__, __LINE__, dte); -} - -/// -@safe unittest -{ - import std.datetime.date : DateTime; - - assert(DosFileTimeToSysTime(0b00000000001000010000000000000000) == SysTime(DateTime(1980, 1, 1, 0, 0, 0))); - assert(DosFileTimeToSysTime(0b11111111100111111011111101111101) == SysTime(DateTime(2107, 12, 31, 23, 59, 58))); - assert(DosFileTimeToSysTime(0x3E3F8456) == SysTime(DateTime(2011, 1, 31, 16, 34, 44))); -} - -@safe unittest -{ - static void testScope(scope ref DosFileTime dft) @safe - { - auto result = DosFileTimeToSysTime(dft); - } -} - - -/++ - Converts from $(LREF SysTime) to DOS file date/time. - - Params: - sysTime = The $(LREF SysTime) to convert. - - Throws: - $(REF DateTimeException,std,datetime,date) if the given - $(LREF SysTime) cannot be converted to a `DosFileTime`. - +/ -DosFileTime SysTimeToDosFileTime(scope SysTime sysTime) @safe -{ - auto dateTime = cast(DateTime) sysTime; - - if (dateTime.year < 1980) - throw new DateTimeException("DOS File Times cannot hold dates prior to 1980."); - - if (dateTime.year > 2107) - throw new DateTimeException("DOS File Times cannot hold dates past 2107."); - - uint retval = 0; - retval = (dateTime.year - 1980) << 25; - retval |= (dateTime.month & 0x0F) << 21; - retval |= (dateTime.day & 0x1F) << 16; - retval |= (dateTime.hour & 0x1F) << 11; - retval |= (dateTime.minute & 0x3F) << 5; - retval |= (dateTime.second >> 1) & 0x1F; - - return cast(DosFileTime) retval; -} - -/// -@safe unittest -{ - import std.datetime.date : DateTime; - - assert(SysTimeToDosFileTime(SysTime(DateTime(1980, 1, 1, 0, 0, 0))) == 0b00000000001000010000000000000000); - assert(SysTimeToDosFileTime(SysTime(DateTime(2107, 12, 31, 23, 59, 58))) == 0b11111111100111111011111101111101); - assert(SysTimeToDosFileTime(SysTime(DateTime(2011, 1, 31, 16, 34, 44))) == 0x3E3F8456); -} - -@safe unittest -{ - static void testScope(scope ref SysTime st) @safe - { - auto result = SysTimeToDosFileTime(st); - } -} - - -/++ - The given array of `char` or random-access range of `char` or - `ubyte` is expected to be in the format specified in - $(HTTP tools.ietf.org/html/rfc5322, RFC 5322) section 3.3 with the - grammar rule $(I date-time). It is the date-time format commonly used in - internet messages such as e-mail and HTTP. The corresponding - $(LREF SysTime) will be returned. - - RFC 822 was the original spec (hence the function's name), whereas RFC 5322 - is the current spec. - - The day of the week is ignored beyond verifying that it's a valid day of the - week, as the day of the week can be inferred from the date. It is not - checked whether the given day of the week matches the actual day of the week - of the given date (though it is technically invalid per the spec if the - day of the week doesn't match the actual day of the week of the given date). - - If the time zone is `"-0000"` (or considered to be equivalent to - `"-0000"` by section 4.3 of the spec), a - $(REF SimpleTimeZone,std,datetime,timezone) with a utc offset of `0` is - used rather than $(REF UTC,std,datetime,timezone), whereas `"+0000"` uses - $(REF UTC,std,datetime,timezone). - - Note that because $(LREF SysTime) does not currently support having a second - value of 60 (as is sometimes done for leap seconds), if the date-time value - does have a value of 60 for the seconds, it is treated as 59. - - The one area in which this function violates RFC 5322 is that it accepts - `"\n"` in folding whitespace in the place of `"\r\n"`, because the - HTTP spec requires it. - - Throws: - $(REF DateTimeException,std,datetime,date) if the given string doesn't - follow the grammar for a date-time field or if the resulting - $(LREF SysTime) is invalid. - +/ -SysTime parseRFC822DateTime()(scope const char[] value) @safe -{ - import std.string : representation; - return parseRFC822DateTime(value.representation); -} - -/++ Ditto +/ -SysTime parseRFC822DateTime(R)(scope R value) -if (isRandomAccessRange!R && hasSlicing!R && hasLength!R && - (is(immutable ElementType!R == immutable char) || is(immutable ElementType!R == immutable ubyte))) -{ - import std.algorithm.searching : find, all; - import std.ascii : isDigit, isAlpha, isPrintable; - import std.conv : to; - import std.functional : not; - import std.string : capitalize, format; - import std.traits : EnumMembers, isArray; - import std.typecons : Rebindable; - - void stripAndCheckLen(R valueBefore, size_t minLen, size_t line = __LINE__) - { - value = _stripCFWS(valueBefore); - if (value.length < minLen) - throw new DateTimeException("date-time value too short", __FILE__, line); - } - stripAndCheckLen(value, "7Dec1200:00A".length); - - static if (isArray!R && (is(ElementEncodingType!R == char) || is(ElementEncodingType!R == ubyte))) - { - static string sliceAsString(R str) @trusted - { - return cast(string) str; - } - } - else - { - char[4] temp; - char[] sliceAsString(R str) @trusted - { - size_t i = 0; - foreach (c; str) - temp[i++] = cast(char) c; - return temp[0 .. str.length]; - } - } - - // day-of-week - if (isAlpha(value[0])) - { - auto dowStr = sliceAsString(value[0 .. 3]); - switch (dowStr) - { - foreach (dow; EnumMembers!DayOfWeek) - { - enum dowC = capitalize(to!string(dow)); - case dowC: - goto afterDoW; - } - default: throw new DateTimeException(format("Invalid day-of-week: %s", dowStr)); - } -afterDoW: stripAndCheckLen(value[3 .. value.length], ",7Dec1200:00A".length); - if (value[0] != ',') - throw new DateTimeException("day-of-week missing comma"); - stripAndCheckLen(value[1 .. value.length], "7Dec1200:00A".length); - } - - // day - immutable digits = isDigit(value[1]) ? 2 : 1; - immutable day = _convDigits!short(value[0 .. digits]); - if (day == -1) - throw new DateTimeException("Invalid day"); - stripAndCheckLen(value[digits .. value.length], "Dec1200:00A".length); - - // month - Month month; - { - auto monStr = sliceAsString(value[0 .. 3]); - switch (monStr) - { - foreach (mon; EnumMembers!Month) - { - enum monC = capitalize(to!string(mon)); - case monC: - { - month = mon; - goto afterMon; - } - } - default: throw new DateTimeException(format("Invalid month: %s", monStr)); - } -afterMon: stripAndCheckLen(value[3 .. value.length], "1200:00A".length); - } - - // year - auto found = value[2 .. value.length].find!(not!(isDigit))(); - size_t yearLen = value.length - found.length; - if (found.length == 0) - throw new DateTimeException("Invalid year"); - if (found[0] == ':') - yearLen -= 2; - auto year = _convDigits!short(value[0 .. yearLen]); - if (year < 1900) - { - if (year == -1) - throw new DateTimeException("Invalid year"); - if (yearLen < 4) - { - if (yearLen == 3) - year += 1900; - else if (yearLen == 2) - year += year < 50 ? 2000 : 1900; - else - throw new DateTimeException("Invalid year. Too few digits."); - } - else - throw new DateTimeException("Invalid year. Cannot be earlier than 1900."); - } - stripAndCheckLen(value[yearLen .. value.length], "00:00A".length); - - // hour - immutable hour = _convDigits!short(value[0 .. 2]); - stripAndCheckLen(value[2 .. value.length], ":00A".length); - if (value[0] != ':') - throw new DateTimeException("Invalid hour"); - stripAndCheckLen(value[1 .. value.length], "00A".length); - - // minute - immutable minute = _convDigits!short(value[0 .. 2]); - stripAndCheckLen(value[2 .. value.length], "A".length); - - // second - short second; - if (value[0] == ':') - { - stripAndCheckLen(value[1 .. value.length], "00A".length); - second = _convDigits!short(value[0 .. 2]); - // this is just if/until SysTime is sorted out to fully support leap seconds - if (second == 60) - second = 59; - stripAndCheckLen(value[2 .. value.length], "A".length); - } - - immutable(TimeZone) parseTZ(int sign) - { - if (value.length < 5) - throw new DateTimeException("Invalid timezone"); - immutable zoneHours = _convDigits!short(value[1 .. 3]); - immutable zoneMinutes = _convDigits!short(value[3 .. 5]); - if (zoneHours == -1 || zoneMinutes == -1 || zoneMinutes > 59) - throw new DateTimeException("Invalid timezone"); - value = value[5 .. value.length]; - immutable utcOffset = (dur!"hours"(zoneHours) + dur!"minutes"(zoneMinutes)) * sign; - if (utcOffset == Duration.zero) - { - return sign == 1 ? cast(immutable(TimeZone))UTC() - : cast(immutable(TimeZone))new immutable SimpleTimeZone(Duration.zero); - } - return new immutable(SimpleTimeZone)(utcOffset); - } - - // zone - Rebindable!(immutable TimeZone) tz; - if (value[0] == '-') - tz = parseTZ(-1); - else if (value[0] == '+') - tz = parseTZ(1); - else - { - // obs-zone - immutable tzLen = value.length - find(value, ' ', '\t', '(')[0].length; - switch (sliceAsString(value[0 .. tzLen <= 4 ? tzLen : 4])) - { - case "UT": case "GMT": tz = UTC(); break; - case "EST": tz = new immutable SimpleTimeZone(dur!"hours"(-5)); break; - case "EDT": tz = new immutable SimpleTimeZone(dur!"hours"(-4)); break; - case "CST": tz = new immutable SimpleTimeZone(dur!"hours"(-6)); break; - case "CDT": tz = new immutable SimpleTimeZone(dur!"hours"(-5)); break; - case "MST": tz = new immutable SimpleTimeZone(dur!"hours"(-7)); break; - case "MDT": tz = new immutable SimpleTimeZone(dur!"hours"(-6)); break; - case "PST": tz = new immutable SimpleTimeZone(dur!"hours"(-8)); break; - case "PDT": tz = new immutable SimpleTimeZone(dur!"hours"(-7)); break; - case "J": case "j": throw new DateTimeException("Invalid timezone"); - default: - { - if (all!(isAlpha)(value[0 .. tzLen])) - { - tz = new immutable SimpleTimeZone(Duration.zero); - break; - } - throw new DateTimeException("Invalid timezone"); - } - } - value = value[tzLen .. value.length]; - } - - // This is kind of arbitrary. Technically, nothing but CFWS is legal past - // the end of the timezone, but we don't want to be picky about that in a - // function that's just parsing rather than validating. So, the idea here is - // that if the next character is printable (and not part of CFWS), then it - // might be part of the timezone and thus affect what the timezone was - // supposed to be, so we'll throw, but otherwise, we'll just ignore it. - if (!value.empty && isPrintable(value[0]) && value[0] != ' ' && value[0] != '(') - throw new DateTimeException("Invalid timezone"); - - try - return SysTime(DateTime(year, month, day, hour, minute, second), tz); - catch (DateTimeException dte) - throw new DateTimeException("date-time format is correct, but the resulting SysTime is invalid.", dte); -} - -/// -@safe unittest -{ - import core.time : hours; - import std.datetime.date : DateTime, DateTimeException; - import std.datetime.timezone : SimpleTimeZone, UTC; - import std.exception : assertThrown; - - auto tz = new immutable SimpleTimeZone(hours(-8)); - assert(parseRFC822DateTime("Sat, 6 Jan 1990 12:14:19 -0800") == - SysTime(DateTime(1990, 1, 6, 12, 14, 19), tz)); - - assert(parseRFC822DateTime("9 Jul 2002 13:11 +0000") == - SysTime(DateTime(2002, 7, 9, 13, 11, 0), UTC())); - - auto badStr = "29 Feb 2001 12:17:16 +0200"; - assertThrown!DateTimeException(parseRFC822DateTime(badStr)); -} - -version (StdUnittest) private void testParse822(alias cr)(string str, SysTime expected, size_t line = __LINE__) -{ - import std.format : format; - auto value = cr(str); - auto result = parseRFC822DateTime(value); - if (result != expected) - throw new AssertError(format("wrong result. expected [%s], actual[%s]", expected, result), __FILE__, line); -} - -version (StdUnittest) private void testBadParse822(alias cr)(string str, size_t line = __LINE__) -{ - try - parseRFC822DateTime(cr(str)); - catch (DateTimeException) - return; - throw new AssertError("No DateTimeException was thrown", __FILE__, line); -} - -@system unittest -{ - import core.time; - import std.algorithm.iteration : filter, map; - import std.algorithm.searching : canFind; - import std.array : array; - import std.ascii : letters; - import std.format : format; - import std.meta : AliasSeq; - import std.range : chain, iota, take; - import std.stdio : writefln, writeln; - import std.string : representation; - - static struct Rand3Letters - { - enum empty = false; - @property auto front() { return _mon; } - void popFront() - { - import std.exception : assumeUnique; - import std.random : rndGen; - _mon = rndGen.map!(a => letters[a % letters.length])().take(3).array().assumeUnique(); - } - string _mon; - static auto start() { Rand3Letters retval; retval.popFront(); return retval; } - } - - static foreach (cr; AliasSeq!(function(string a){return cast(char[]) a;}, - function(string a){return cast(ubyte[]) a;}, - function(string a){return a;}, - function(string a){return map!(b => cast(char) b)(a.representation);})) - {(){ // workaround slow optimizations for large functions - // https://issues.dlang.org/show_bug.cgi?id=2396 - scope(failure) writeln(typeof(cr).stringof); - alias test = testParse822!cr; - alias testBad = testBadParse822!cr; - - immutable std1 = DateTime(2012, 12, 21, 13, 14, 15); - immutable std2 = DateTime(2012, 12, 21, 13, 14, 0); - immutable dst1 = DateTime(1976, 7, 4, 5, 4, 22); - immutable dst2 = DateTime(1976, 7, 4, 5, 4, 0); - - test("21 Dec 2012 13:14:15 +0000", SysTime(std1, UTC())); - test("21 Dec 2012 13:14 +0000", SysTime(std2, UTC())); - test("Fri, 21 Dec 2012 13:14 +0000", SysTime(std2, UTC())); - test("Fri, 21 Dec 2012 13:14:15 +0000", SysTime(std1, UTC())); - - test("04 Jul 1976 05:04:22 +0000", SysTime(dst1, UTC())); - test("04 Jul 1976 05:04 +0000", SysTime(dst2, UTC())); - test("Sun, 04 Jul 1976 05:04 +0000", SysTime(dst2, UTC())); - test("Sun, 04 Jul 1976 05:04:22 +0000", SysTime(dst1, UTC())); - - test("4 Jul 1976 05:04:22 +0000", SysTime(dst1, UTC())); - test("4 Jul 1976 05:04 +0000", SysTime(dst2, UTC())); - test("Sun, 4 Jul 1976 05:04 +0000", SysTime(dst2, UTC())); - test("Sun, 4 Jul 1976 05:04:22 +0000", SysTime(dst1, UTC())); - - auto badTZ = new immutable SimpleTimeZone(Duration.zero); - test("21 Dec 2012 13:14:15 -0000", SysTime(std1, badTZ)); - test("21 Dec 2012 13:14 -0000", SysTime(std2, badTZ)); - test("Fri, 21 Dec 2012 13:14 -0000", SysTime(std2, badTZ)); - test("Fri, 21 Dec 2012 13:14:15 -0000", SysTime(std1, badTZ)); - - test("04 Jul 1976 05:04:22 -0000", SysTime(dst1, badTZ)); - test("04 Jul 1976 05:04 -0000", SysTime(dst2, badTZ)); - test("Sun, 04 Jul 1976 05:04 -0000", SysTime(dst2, badTZ)); - test("Sun, 04 Jul 1976 05:04:22 -0000", SysTime(dst1, badTZ)); - - test("4 Jul 1976 05:04:22 -0000", SysTime(dst1, badTZ)); - test("4 Jul 1976 05:04 -0000", SysTime(dst2, badTZ)); - test("Sun, 4 Jul 1976 05:04 -0000", SysTime(dst2, badTZ)); - test("Sun, 4 Jul 1976 05:04:22 -0000", SysTime(dst1, badTZ)); - - auto pst = new immutable SimpleTimeZone(dur!"hours"(-8)); - auto pdt = new immutable SimpleTimeZone(dur!"hours"(-7)); - test("21 Dec 2012 13:14:15 -0800", SysTime(std1, pst)); - test("21 Dec 2012 13:14 -0800", SysTime(std2, pst)); - test("Fri, 21 Dec 2012 13:14 -0800", SysTime(std2, pst)); - test("Fri, 21 Dec 2012 13:14:15 -0800", SysTime(std1, pst)); - - test("04 Jul 1976 05:04:22 -0700", SysTime(dst1, pdt)); - test("04 Jul 1976 05:04 -0700", SysTime(dst2, pdt)); - test("Sun, 04 Jul 1976 05:04 -0700", SysTime(dst2, pdt)); - test("Sun, 04 Jul 1976 05:04:22 -0700", SysTime(dst1, pdt)); - - test("4 Jul 1976 05:04:22 -0700", SysTime(dst1, pdt)); - test("4 Jul 1976 05:04 -0700", SysTime(dst2, pdt)); - test("Sun, 4 Jul 1976 05:04 -0700", SysTime(dst2, pdt)); - test("Sun, 4 Jul 1976 05:04:22 -0700", SysTime(dst1, pdt)); - - auto cet = new immutable SimpleTimeZone(dur!"hours"(1)); - auto cest = new immutable SimpleTimeZone(dur!"hours"(2)); - test("21 Dec 2012 13:14:15 +0100", SysTime(std1, cet)); - test("21 Dec 2012 13:14 +0100", SysTime(std2, cet)); - test("Fri, 21 Dec 2012 13:14 +0100", SysTime(std2, cet)); - test("Fri, 21 Dec 2012 13:14:15 +0100", SysTime(std1, cet)); - - test("04 Jul 1976 05:04:22 +0200", SysTime(dst1, cest)); - test("04 Jul 1976 05:04 +0200", SysTime(dst2, cest)); - test("Sun, 04 Jul 1976 05:04 +0200", SysTime(dst2, cest)); - test("Sun, 04 Jul 1976 05:04:22 +0200", SysTime(dst1, cest)); - - test("4 Jul 1976 05:04:22 +0200", SysTime(dst1, cest)); - test("4 Jul 1976 05:04 +0200", SysTime(dst2, cest)); - test("Sun, 4 Jul 1976 05:04 +0200", SysTime(dst2, cest)); - test("Sun, 4 Jul 1976 05:04:22 +0200", SysTime(dst1, cest)); - - // dst and std times are switched in the Southern Hemisphere which is why the - // time zone names and DateTime variables don't match. - auto cstStd = new immutable SimpleTimeZone(dur!"hours"(9) + dur!"minutes"(30)); - auto cstDST = new immutable SimpleTimeZone(dur!"hours"(10) + dur!"minutes"(30)); - test("21 Dec 2012 13:14:15 +1030", SysTime(std1, cstDST)); - test("21 Dec 2012 13:14 +1030", SysTime(std2, cstDST)); - test("Fri, 21 Dec 2012 13:14 +1030", SysTime(std2, cstDST)); - test("Fri, 21 Dec 2012 13:14:15 +1030", SysTime(std1, cstDST)); - - test("04 Jul 1976 05:04:22 +0930", SysTime(dst1, cstStd)); - test("04 Jul 1976 05:04 +0930", SysTime(dst2, cstStd)); - test("Sun, 04 Jul 1976 05:04 +0930", SysTime(dst2, cstStd)); - test("Sun, 04 Jul 1976 05:04:22 +0930", SysTime(dst1, cstStd)); - - test("4 Jul 1976 05:04:22 +0930", SysTime(dst1, cstStd)); - test("4 Jul 1976 05:04 +0930", SysTime(dst2, cstStd)); - test("Sun, 4 Jul 1976 05:04 +0930", SysTime(dst2, cstStd)); - test("Sun, 4 Jul 1976 05:04:22 +0930", SysTime(dst1, cstStd)); - - foreach (int i, mon; _monthNames) - { - test(format("17 %s 2012 00:05:02 +0000", mon), SysTime(DateTime(2012, i + 1, 17, 0, 5, 2), UTC())); - test(format("17 %s 2012 00:05 +0000", mon), SysTime(DateTime(2012, i + 1, 17, 0, 5, 0), UTC())); - } - - import std.uni : toLower, toUpper; - foreach (mon; chain(_monthNames[].map!(a => toLower(a))(), - _monthNames[].map!(a => toUpper(a))(), - ["Jam", "Jen", "Fec", "Fdb", "Mas", "Mbr", "Aps", "Aqr", "Mai", "Miy", - "Jum", "Jbn", "Jup", "Jal", "Aur", "Apg", "Sem", "Sap", "Ocm", "Odt", - "Nom", "Nav", "Dem", "Dac"], - Rand3Letters.start().filter!(a => !_monthNames[].canFind(a)).take(20))) - { - scope(failure) writefln("Month: %s", mon); - testBad(format("17 %s 2012 00:05:02 +0000", mon)); - testBad(format("17 %s 2012 00:05 +0000", mon)); - } - - immutable string[7] daysOfWeekNames = ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"]; - - { - auto start = SysTime(DateTime(2012, 11, 11, 9, 42, 0), UTC()); - int day = 11; - - foreach (int i, dow; daysOfWeekNames) - { - auto curr = start + dur!"days"(i); - test(format("%s, %s Nov 2012 09:42:00 +0000", dow, day), curr); - test(format("%s, %s Nov 2012 09:42 +0000", dow, day++), curr); - - // Whether the day of the week matches the date is ignored. - test(format("%s, 11 Nov 2012 09:42:00 +0000", dow), start); - test(format("%s, 11 Nov 2012 09:42 +0000", dow), start); - } - } - - foreach (dow; chain(daysOfWeekNames[].map!(a => toLower(a))(), - daysOfWeekNames[].map!(a => toUpper(a))(), - ["Sum", "Spn", "Mom", "Man", "Tuf", "Tae", "Wem", "Wdd", "The", "Tur", - "Fro", "Fai", "San", "Sut"], - Rand3Letters.start().filter!(a => !daysOfWeekNames[].canFind(a)).take(20))) - { - scope(failure) writefln("Day of Week: %s", dow); - testBad(format("%s, 11 Nov 2012 09:42:00 +0000", dow)); - testBad(format("%s, 11 Nov 2012 09:42 +0000", dow)); - } - - testBad("31 Dec 1899 23:59:59 +0000"); - test("01 Jan 1900 00:00:00 +0000", SysTime(Date(1900, 1, 1), UTC())); - test("01 Jan 1900 00:00:00 -0000", SysTime(Date(1900, 1, 1), - new immutable SimpleTimeZone(Duration.zero))); - test("01 Jan 1900 00:00:00 -0700", SysTime(Date(1900, 1, 1), - new immutable SimpleTimeZone(dur!"hours"(-7)))); - - { - auto st1 = SysTime(Date(1900, 1, 1), UTC()); - auto st2 = SysTime(Date(1900, 1, 1), new immutable SimpleTimeZone(dur!"hours"(-11))); - foreach (i; 1900 .. 2102) - { - test(format("1 Jan %05d 00:00 +0000", i), st1); - test(format("1 Jan %05d 00:00 -1100", i), st2); - st1.add!"years"(1); - st2.add!"years"(1); - } - st1.year = 9998; - st2.year = 9998; - foreach (i; 9998 .. 11_002) - { - test(format("1 Jan %05d 00:00 +0000", i), st1); - test(format("1 Jan %05d 00:00 -1100", i), st2); - st1.add!"years"(1); - st2.add!"years"(1); - } - } - - testBad("12 Feb 1907 23:17:09 0000"); - testBad("12 Feb 1907 23:17:09 +000"); - testBad("12 Feb 1907 23:17:09 -000"); - testBad("12 Feb 1907 23:17:09 +00000"); - testBad("12 Feb 1907 23:17:09 -00000"); - testBad("12 Feb 1907 23:17:09 +A"); - testBad("12 Feb 1907 23:17:09 +PST"); - testBad("12 Feb 1907 23:17:09 -A"); - testBad("12 Feb 1907 23:17:09 -PST"); - - // test trailing stuff that gets ignored - { - foreach (c; chain(iota(0, 33), ['('], iota(127, ubyte.max + 1))) - { - scope(failure) writefln("c: %d", c); - test(format("21 Dec 2012 13:14:15 +0000%c", cast(char) c), SysTime(std1, UTC())); - test(format("21 Dec 2012 13:14:15 +0000%c ", cast(char) c), SysTime(std1, UTC())); - test(format("21 Dec 2012 13:14:15 +0000%chello", cast(char) c), SysTime(std1, UTC())); - } - } - - // test trailing stuff that doesn't get ignored - { - foreach (c; chain(iota(33, '('), iota('(' + 1, 127))) - { - scope(failure) writefln("c: %d", c); - testBad(format("21 Dec 2012 13:14:15 +0000%c", cast(char) c)); - testBad(format("21 Dec 2012 13:14:15 +0000%c ", cast(char) c)); - testBad(format("21 Dec 2012 13:14:15 +0000%chello", cast(char) c)); - } - } - - testBad("32 Jan 2012 12:13:14 -0800"); - testBad("31 Jan 2012 24:13:14 -0800"); - testBad("31 Jan 2012 12:60:14 -0800"); - testBad("31 Jan 2012 12:13:61 -0800"); - testBad("31 Jan 2012 12:13:14 -0860"); - test("31 Jan 2012 12:13:14 -0859", - SysTime(DateTime(2012, 1, 31, 12, 13, 14), - new immutable SimpleTimeZone(dur!"hours"(-8) + dur!"minutes"(-59)))); - - // leap-seconds - test("21 Dec 2012 15:59:60 -0800", SysTime(DateTime(2012, 12, 21, 15, 59, 59), pst)); - - // FWS - test("Sun,4 Jul 1976 05:04 +0930", SysTime(dst2, cstStd)); - test("Sun,4 Jul 1976 05:04:22 +0930", SysTime(dst1, cstStd)); - test("Sun,4 Jul 1976 05:04 +0930 (foo)", SysTime(dst2, cstStd)); - test("Sun,4 Jul 1976 05:04:22 +0930 (foo)", SysTime(dst1, cstStd)); - test("Sun,4 \r\n Jul \r\n 1976 \r\n 05:04 \r\n +0930 \r\n (foo)", SysTime(dst2, cstStd)); - test("Sun,4 \r\n Jul \r\n 1976 \r\n 05:04:22 \r\n +0930 \r\n (foo)", SysTime(dst1, cstStd)); - - auto str = "01 Jan 2012 12:13:14 -0800 "; - test(str, SysTime(DateTime(2012, 1, 1, 12, 13, 14), new immutable SimpleTimeZone(hours(-8)))); - foreach (i; 0 .. str.length) - { - auto currStr = str.dup; - currStr[i] = 'x'; - scope(failure) writefln("failed: %s", currStr); - testBad(cast(string) currStr); - } - foreach (i; 2 .. str.length) - { - auto currStr = str[0 .. $ - i]; - scope(failure) writefln("failed: %s", currStr); - testBad(cast(string) currStr); - testBad((cast(string) currStr) ~ " "); - } - }();} - - static void testScope(scope ref string str) @safe - { - auto result = parseRFC822DateTime(str); - } -} - -// Obsolete Format per section 4.3 of RFC 5322. -@system unittest -{ - import std.algorithm.iteration : filter, map; - import std.ascii : letters; - import std.exception : collectExceptionMsg; - import std.format : format; - import std.meta : AliasSeq; - import std.range : chain, iota; - import std.stdio : writefln, writeln; - import std.string : representation; - - auto std1 = SysTime(DateTime(2012, 12, 21, 13, 14, 15), UTC()); - auto std2 = SysTime(DateTime(2012, 12, 21, 13, 14, 0), UTC()); - auto std3 = SysTime(DateTime(1912, 12, 21, 13, 14, 15), UTC()); - auto std4 = SysTime(DateTime(1912, 12, 21, 13, 14, 0), UTC()); - auto dst1 = SysTime(DateTime(1976, 7, 4, 5, 4, 22), UTC()); - auto dst2 = SysTime(DateTime(1976, 7, 4, 5, 4, 0), UTC()); - auto tooLate1 = SysTime(Date(10_000, 1, 1), UTC()); - auto tooLate2 = SysTime(DateTime(12_007, 12, 31, 12, 22, 19), UTC()); - - static foreach (cr; AliasSeq!(function(string a){return cast(char[]) a;}, - function(string a){return cast(ubyte[]) a;}, - function(string a){return a;}, - function(string a){return map!(b => cast(char) b)(a.representation);})) - {(){ // workaround slow optimizations for large functions - // https://issues.dlang.org/show_bug.cgi?id=2396 - scope(failure) writeln(typeof(cr).stringof); - alias test = testParse822!cr; - { - auto list = ["", " ", " \r\n\t", "\t\r\n (hello world( frien(dog)) silly \r\n ) \t\t \r\n ()", - " \n ", "\t\n\t", " \n\t (foo) \n (bar) \r\n (baz) \n "]; - - foreach (i, cfws; list) - { - scope(failure) writefln("i: %s", i); - - test(format("%1$s21%1$sDec%1$s2012%1$s13:14:15%1$s+0000%1$s", cfws), std1); - test(format("%1$s21%1$sDec%1$s2012%1$s13:14%1$s+0000%1$s", cfws), std2); - test(format("%1$sFri%1$s,%1$s21%1$sDec%1$s2012%1$s13:14%1$s+0000%1$s", cfws), std2); - test(format("%1$sFri%1$s,%1$s21%1$sDec%1$s2012%1$s13:14:15%1$s+0000%1$s", cfws), std1); - - test(format("%1$s04%1$sJul%1$s1976%1$s05:04:22%1$s+0000%1$s", cfws), dst1); - test(format("%1$s04%1$sJul%1$s1976%1$s05:04%1$s+0000%1$s", cfws), dst2); - test(format("%1$sSun%1$s,%1$s04%1$sJul%1$s1976%1$s05:04%1$s+0000%1$s", cfws), dst2); - test(format("%1$sSun%1$s,%1$s04%1$sJul%1$s1976%1$s05:04:22 +0000%1$s", cfws), dst1); - - test(format("%1$s4%1$sJul%1$s1976%1$s05:04:22%1$s+0000%1$s", cfws), dst1); - test(format("%1$s4%1$sJul%1$s1976%1$s05:04%1$s+0000%1$s", cfws), dst2); - test(format("%1$sSun%1$s,%1$s4%1$sJul%1$s1976%1$s05:04%1$s+0000%1$s", cfws), dst2); - test(format("%1$sSun%1$s,%1$s4%1$sJul%1$s1976%1$s05:04:22%1$s+0000%1$s", cfws), dst1); - - test(format("%1$s21%1$sDec%1$s12%1$s13:14:15%1$s+0000%1$s", cfws), std1); - test(format("%1$s21%1$sDec%1$s12%1$s13:14%1$s+0000%1$s", cfws), std2); - test(format("%1$sFri%1$s,%1$s21%1$sDec%1$s12%1$s13:14%1$s+0000%1$s", cfws), std2); - test(format("%1$sFri%1$s,%1$s21%1$sDec%1$s12%1$s13:14:15%1$s+0000%1$s", cfws), std1); - - test(format("%1$s04%1$sJul%1$s76%1$s05:04:22%1$s+0000%1$s", cfws), dst1); - test(format("%1$s04%1$sJul%1$s76%1$s05:04%1$s+0000%1$s", cfws), dst2); - test(format("%1$sSun%1$s,%1$s04%1$sJul%1$s76%1$s05:04%1$s+0000%1$s", cfws), dst2); - test(format("%1$sSun%1$s,%1$s04%1$sJul%1$s76%1$s05:04:22%1$s+0000%1$s", cfws), dst1); - - test(format("%1$s4%1$sJul%1$s76 05:04:22%1$s+0000%1$s", cfws), dst1); - test(format("%1$s4%1$sJul%1$s76 05:04%1$s+0000%1$s", cfws), dst2); - test(format("%1$sSun%1$s,%1$s4%1$sJul%1$s76%1$s05:04%1$s+0000%1$s", cfws), dst2); - test(format("%1$sSun%1$s,%1$s4%1$sJul%1$s76%1$s05:04:22%1$s+0000%1$s", cfws), dst1); - - test(format("%1$s21%1$sDec%1$s012%1$s13:14:15%1$s+0000%1$s", cfws), std3); - test(format("%1$s21%1$sDec%1$s012%1$s13:14%1$s+0000%1$s", cfws), std4); - test(format("%1$sFri%1$s,%1$s21%1$sDec%1$s012%1$s13:14%1$s+0000%1$s", cfws), std4); - test(format("%1$sFri%1$s,%1$s21%1$sDec%1$s012%1$s13:14:15%1$s+0000%1$s", cfws), std3); - - test(format("%1$s04%1$sJul%1$s076%1$s05:04:22%1$s+0000%1$s", cfws), dst1); - test(format("%1$s04%1$sJul%1$s076%1$s05:04%1$s+0000%1$s", cfws), dst2); - test(format("%1$sSun%1$s,%1$s04%1$sJul%1$s076%1$s05:04%1$s+0000%1$s", cfws), dst2); - test(format("%1$sSun%1$s,%1$s04%1$sJul%1$s076%1$s05:04:22%1$s+0000%1$s", cfws), dst1); - - test(format("%1$s4%1$sJul%1$s076%1$s05:04:22%1$s+0000%1$s", cfws), dst1); - test(format("%1$s4%1$sJul%1$s076%1$s05:04%1$s+0000%1$s", cfws), dst2); - test(format("%1$sSun%1$s,%1$s4%1$sJul%1$s076%1$s05:04%1$s+0000%1$s", cfws), dst2); - test(format("%1$sSun%1$s,%1$s4%1$sJul%1$s076%1$s05:04:22%1$s+0000%1$s", cfws), dst1); - - test(format("%1$s1%1$sJan%1$s10000%1$s00:00:00%1$s+0000%1$s", cfws), tooLate1); - test(format("%1$s31%1$sDec%1$s12007%1$s12:22:19%1$s+0000%1$s", cfws), tooLate2); - test(format("%1$sSat%1$s,%1$s1%1$sJan%1$s10000%1$s00:00:00%1$s+0000%1$s", cfws), tooLate1); - test(format("%1$sSun%1$s,%1$s31%1$sDec%1$s12007%1$s12:22:19%1$s+0000%1$s", cfws), tooLate2); - } - } - - // test years of 1, 2, and 3 digits. - { - auto st1 = SysTime(Date(2000, 1, 1), UTC()); - auto st2 = SysTime(Date(2000, 1, 1), new immutable SimpleTimeZone(dur!"hours"(-12))); - foreach (i; 0 .. 50) - { - test(format("1 Jan %02d 00:00 GMT", i), st1); - test(format("1 Jan %02d 00:00 -1200", i), st2); - st1.add!"years"(1); - st2.add!"years"(1); - } - } - - { - auto st1 = SysTime(Date(1950, 1, 1), UTC()); - auto st2 = SysTime(Date(1950, 1, 1), new immutable SimpleTimeZone(dur!"hours"(-12))); - foreach (i; 50 .. 100) - { - test(format("1 Jan %02d 00:00 GMT", i), st1); - test(format("1 Jan %02d 00:00 -1200", i), st2); - st1.add!"years"(1); - st2.add!"years"(1); - } - } - - { - auto st1 = SysTime(Date(1900, 1, 1), UTC()); - auto st2 = SysTime(Date(1900, 1, 1), new immutable SimpleTimeZone(dur!"hours"(-11))); - foreach (i; 0 .. 1000) - { - test(format("1 Jan %03d 00:00 GMT", i), st1); - test(format("1 Jan %03d 00:00 -1100", i), st2); - st1.add!"years"(1); - st2.add!"years"(1); - } - } - - foreach (i; 0 .. 10) - { - auto str1 = cr(format("1 Jan %d 00:00 GMT", i)); - auto str2 = cr(format("1 Jan %d 00:00 -1200", i)); - assertThrown!DateTimeException(parseRFC822DateTime(str1)); - assertThrown!DateTimeException(parseRFC822DateTime(str1)); - } - - // test time zones - { - auto dt = DateTime(1982, 5, 3, 12, 22, 4); - test("Wed, 03 May 1982 12:22:04 UT", SysTime(dt, UTC())); - test("Wed, 03 May 1982 12:22:04 GMT", SysTime(dt, UTC())); - test("Wed, 03 May 1982 12:22:04 EST", SysTime(dt, new immutable SimpleTimeZone(dur!"hours"(-5)))); - test("Wed, 03 May 1982 12:22:04 EDT", SysTime(dt, new immutable SimpleTimeZone(dur!"hours"(-4)))); - test("Wed, 03 May 1982 12:22:04 CST", SysTime(dt, new immutable SimpleTimeZone(dur!"hours"(-6)))); - test("Wed, 03 May 1982 12:22:04 CDT", SysTime(dt, new immutable SimpleTimeZone(dur!"hours"(-5)))); - test("Wed, 03 May 1982 12:22:04 MST", SysTime(dt, new immutable SimpleTimeZone(dur!"hours"(-7)))); - test("Wed, 03 May 1982 12:22:04 MDT", SysTime(dt, new immutable SimpleTimeZone(dur!"hours"(-6)))); - test("Wed, 03 May 1982 12:22:04 PST", SysTime(dt, new immutable SimpleTimeZone(dur!"hours"(-8)))); - test("Wed, 03 May 1982 12:22:04 PDT", SysTime(dt, new immutable SimpleTimeZone(dur!"hours"(-7)))); - - auto badTZ = new immutable SimpleTimeZone(Duration.zero); - foreach (dchar c; filter!(a => a != 'j' && a != 'J')(letters)) - { - scope(failure) writefln("c: %s", c); - test(format("Wed, 03 May 1982 12:22:04 %s", c), SysTime(dt, badTZ)); - test(format("Wed, 03 May 1982 12:22:04%s", c), SysTime(dt, badTZ)); - } - - foreach (dchar c; ['j', 'J']) - { - scope(failure) writefln("c: %s", c); - assertThrown!DateTimeException(parseRFC822DateTime(cr(format("Wed, 03 May 1982 12:22:04 %s", c)))); - assertThrown!DateTimeException(parseRFC822DateTime(cr(format("Wed, 03 May 1982 12:22:04%s", c)))); - } - - foreach (string s; ["AAA", "GQW", "DDT", "PDA", "GT", "GM"]) - { - scope(failure) writefln("s: %s", s); - test(format("Wed, 03 May 1982 12:22:04 %s", s), SysTime(dt, badTZ)); - } - - // test trailing stuff that gets ignored - { - foreach (c; chain(iota(0, 33), ['('], iota(127, ubyte.max + 1))) - { - scope(failure) writefln("c: %d", c); - test(format("21Dec1213:14:15+0000%c", cast(char) c), std1); - test(format("21Dec1213:14:15+0000%c ", cast(char) c), std1); - test(format("21Dec1213:14:15+0000%chello", cast(char) c), std1); - } - } - - // test trailing stuff that doesn't get ignored - { - foreach (c; chain(iota(33, '('), iota('(' + 1, 127))) - { - scope(failure) writefln("c: %d", c); - assertThrown!DateTimeException( - parseRFC822DateTime(cr(format("21Dec1213:14:15+0000%c", cast(char) c)))); - assertThrown!DateTimeException( - parseRFC822DateTime(cr(format("21Dec1213:14:15+0000%c ", cast(char) c)))); - assertThrown!DateTimeException( - parseRFC822DateTime(cr(format("21Dec1213:14:15+0000%chello", cast(char) c)))); - } - } - } - - // test that the checks for minimum length work correctly and avoid - // any RangeErrors. - test("7Dec1200:00A", SysTime(DateTime(2012, 12, 7, 0, 0, 0), - new immutable SimpleTimeZone(Duration.zero))); - test("Fri,7Dec1200:00A", SysTime(DateTime(2012, 12, 7, 0, 0, 0), - new immutable SimpleTimeZone(Duration.zero))); - test("7Dec1200:00:00A", SysTime(DateTime(2012, 12, 7, 0, 0, 0), - new immutable SimpleTimeZone(Duration.zero))); - test("Fri,7Dec1200:00:00A", SysTime(DateTime(2012, 12, 7, 0, 0, 0), - new immutable SimpleTimeZone(Duration.zero))); - - auto tooShortMsg = collectExceptionMsg!DateTimeException(parseRFC822DateTime("")); - foreach (str; ["Fri,7Dec1200:00:00", "7Dec1200:00:00"]) - { - foreach (i; 0 .. str.length) - { - auto value = str[0 .. $ - i]; - scope(failure) writeln(value); - assert(collectExceptionMsg!DateTimeException(parseRFC822DateTime(value)) == tooShortMsg); - } - } - }();} -} - - -private: - -/+ - Returns the given hnsecs as an ISO string of fractional seconds. - +/ -string fracSecsToISOString(int hnsecs, int prec = -1) @safe pure nothrow -{ - import std.array : appender; - auto w = appender!string(); - try - fracSecsToISOString(w, hnsecs, prec); - catch (Exception e) - assert(0, "fracSecsToISOString() threw."); - return w.data; -} - -void fracSecsToISOString(W)(ref W writer, int hnsecs, int prec = -1) -{ - import std.conv : toChars; - import std.range : padLeft; - - assert(hnsecs >= 0); - - if (prec == 0) - return; - - if (hnsecs == 0) - return; - - put(writer, '.'); - auto chars = hnsecs.toChars.padLeft('0', 7); - - if (prec == -1) - { - while (chars.back == '0') - chars.popBack(); - put(writer, chars); - } - else - put(writer, chars[0 .. prec]); -} - -@safe unittest -{ - assert(fracSecsToISOString(0) == ""); - assert(fracSecsToISOString(1) == ".0000001"); - assert(fracSecsToISOString(10) == ".000001"); - assert(fracSecsToISOString(100) == ".00001"); - assert(fracSecsToISOString(1000) == ".0001"); - assert(fracSecsToISOString(10_000) == ".001"); - assert(fracSecsToISOString(100_000) == ".01"); - assert(fracSecsToISOString(1_000_000) == ".1"); - assert(fracSecsToISOString(1_000_001) == ".1000001"); - assert(fracSecsToISOString(1_001_001) == ".1001001"); - assert(fracSecsToISOString(1_071_601) == ".1071601"); - assert(fracSecsToISOString(1_271_641) == ".1271641"); - assert(fracSecsToISOString(9_999_999) == ".9999999"); - assert(fracSecsToISOString(9_999_990) == ".999999"); - assert(fracSecsToISOString(9_999_900) == ".99999"); - assert(fracSecsToISOString(9_999_000) == ".9999"); - assert(fracSecsToISOString(9_990_000) == ".999"); - assert(fracSecsToISOString(9_900_000) == ".99"); - assert(fracSecsToISOString(9_000_000) == ".9"); - assert(fracSecsToISOString(999) == ".0000999"); - assert(fracSecsToISOString(9990) == ".000999"); - assert(fracSecsToISOString(99_900) == ".00999"); - assert(fracSecsToISOString(999_000) == ".0999"); -} - - -/+ - Returns a Duration corresponding to to the given ISO string of - fractional seconds. - +/ -static Duration fracSecsFromISOString(S)(scope const S isoString) @safe pure -if (isSomeString!S) -{ - import std.algorithm.searching : all; - import std.ascii : isDigit; - import std.conv : to; - import std.string : representation; - - if (isoString.empty) - return Duration.zero; - - auto str = isoString.representation; - - enforce(str[0] == '.', new DateTimeException("Invalid ISO String")); - str.popFront(); - - enforce(!str.empty && all!isDigit(str), new DateTimeException("Invalid ISO String")); - - dchar[7] fullISOString = void; - foreach (i, ref dchar c; fullISOString) - { - if (i < str.length) - c = str[i]; - else - c = '0'; - } - - return hnsecs(to!int(fullISOString[])); -} - -@safe unittest -{ - import core.time; - static void testFSInvalid(string isoString) - { - fracSecsFromISOString(isoString); - } - - assertThrown!DateTimeException(testFSInvalid(".")); - assertThrown!DateTimeException(testFSInvalid("0.")); - assertThrown!DateTimeException(testFSInvalid("0")); - assertThrown!DateTimeException(testFSInvalid("0000000")); - assertThrown!DateTimeException(testFSInvalid("T")); - assertThrown!DateTimeException(testFSInvalid("T.")); - assertThrown!DateTimeException(testFSInvalid(".T")); - assertThrown!DateTimeException(testFSInvalid(".00000Q0")); - assertThrown!DateTimeException(testFSInvalid(".000000Q")); - assertThrown!DateTimeException(testFSInvalid(".0000000Q")); - assertThrown!DateTimeException(testFSInvalid(".0000000000Q")); - - assert(fracSecsFromISOString("") == Duration.zero); - assert(fracSecsFromISOString(".0000001") == hnsecs(1)); - assert(fracSecsFromISOString(".000001") == hnsecs(10)); - assert(fracSecsFromISOString(".00001") == hnsecs(100)); - assert(fracSecsFromISOString(".0001") == hnsecs(1000)); - assert(fracSecsFromISOString(".001") == hnsecs(10_000)); - assert(fracSecsFromISOString(".01") == hnsecs(100_000)); - assert(fracSecsFromISOString(".1") == hnsecs(1_000_000)); - assert(fracSecsFromISOString(".1000001") == hnsecs(1_000_001)); - assert(fracSecsFromISOString(".1001001") == hnsecs(1_001_001)); - assert(fracSecsFromISOString(".1071601") == hnsecs(1_071_601)); - assert(fracSecsFromISOString(".1271641") == hnsecs(1_271_641)); - assert(fracSecsFromISOString(".9999999") == hnsecs(9_999_999)); - assert(fracSecsFromISOString(".9999990") == hnsecs(9_999_990)); - assert(fracSecsFromISOString(".999999") == hnsecs(9_999_990)); - assert(fracSecsFromISOString(".9999900") == hnsecs(9_999_900)); - assert(fracSecsFromISOString(".99999") == hnsecs(9_999_900)); - assert(fracSecsFromISOString(".9999000") == hnsecs(9_999_000)); - assert(fracSecsFromISOString(".9999") == hnsecs(9_999_000)); - assert(fracSecsFromISOString(".9990000") == hnsecs(9_990_000)); - assert(fracSecsFromISOString(".999") == hnsecs(9_990_000)); - assert(fracSecsFromISOString(".9900000") == hnsecs(9_900_000)); - assert(fracSecsFromISOString(".9900") == hnsecs(9_900_000)); - assert(fracSecsFromISOString(".99") == hnsecs(9_900_000)); - assert(fracSecsFromISOString(".9000000") == hnsecs(9_000_000)); - assert(fracSecsFromISOString(".9") == hnsecs(9_000_000)); - assert(fracSecsFromISOString(".0000999") == hnsecs(999)); - assert(fracSecsFromISOString(".0009990") == hnsecs(9990)); - assert(fracSecsFromISOString(".000999") == hnsecs(9990)); - assert(fracSecsFromISOString(".0099900") == hnsecs(99_900)); - assert(fracSecsFromISOString(".00999") == hnsecs(99_900)); - assert(fracSecsFromISOString(".0999000") == hnsecs(999_000)); - assert(fracSecsFromISOString(".0999") == hnsecs(999_000)); - assert(fracSecsFromISOString(".00000000") == Duration.zero); - assert(fracSecsFromISOString(".00000001") == Duration.zero); - assert(fracSecsFromISOString(".00000009") == Duration.zero); - assert(fracSecsFromISOString(".1234567890") == hnsecs(1_234_567)); - assert(fracSecsFromISOString(".12345678901234567890") == hnsecs(1_234_567)); -} - - -/+ - This function is used to split out the units without getting the remaining - hnsecs. - - Params: - units = The units to split out. - hnsecs = The current total hnsecs. - - Returns: - The split out value. - +/ -long getUnitsFromHNSecs(string units)(long hnsecs) @safe pure nothrow -if (validTimeUnits(units) && - CmpTimeUnits!(units, "months") < 0) -{ - return convert!("hnsecs", units)(hnsecs); -} - -@safe unittest -{ - auto hnsecs = 2595000000007L; - immutable days = getUnitsFromHNSecs!"days"(hnsecs); - assert(days == 3); - assert(hnsecs == 2595000000007L); -} - - -/+ - This function is used to split out the units without getting the units but - just the remaining hnsecs. - - Params: - units = The units to split out. - hnsecs = The current total hnsecs. - - Returns: - The remaining hnsecs. - +/ -long removeUnitsFromHNSecs(string units)(long hnsecs) @safe pure nothrow -if (validTimeUnits(units) && - CmpTimeUnits!(units, "months") < 0) -{ - immutable value = convert!("hnsecs", units)(hnsecs); - return hnsecs - convert!(units, "hnsecs")(value); -} - -@safe unittest -{ - auto hnsecs = 2595000000007L; - auto returned = removeUnitsFromHNSecs!"days"(hnsecs); - assert(returned == 3000000007); - assert(hnsecs == 2595000000007L); -} - - -/+ - Strips what RFC 5322, section 3.2.2 refers to as CFWS from the left-hand - side of the given range (it strips comments delimited by $(D '(') and - `'`') as well as folding whitespace). - - It is assumed that the given range contains the value of a header field and - no terminating CRLF for the line (though the CRLF for folding whitespace is - of course expected and stripped) and thus that the only case of CR or LF is - in folding whitespace. - - If a comment does not terminate correctly (e.g. mismatched parens) or if the - the FWS is malformed, then the range will be empty when stripCWFS is done. - However, only minimal validation of the content is done (e.g. quoted pairs - within a comment aren't validated beyond \$LPAREN or \$RPAREN, because - they're inside a comment, and thus their value doesn't matter anyway). It's - only when the content does not conform to the grammar rules for FWS and thus - literally cannot be parsed that content is considered invalid, and an empty - range is returned. - - Note that _stripCFWS is eager, not lazy. It does not create a new range. - Rather, it pops off the CFWS from the range and returns it. - +/ -R _stripCFWS(R)(R range) -if (isRandomAccessRange!R && hasSlicing!R && hasLength!R && - (is(immutable ElementType!R == immutable char) || is(immutable ElementType!R == immutable ubyte))) -{ - immutable e = range.length; - outer: for (size_t i = 0; i < e; ) - { - switch (range[i]) - { - case ' ': case '\t': - { - ++i; - break; - } - case '\r': - { - if (i + 2 < e && range[i + 1] == '\n' && (range[i + 2] == ' ' || range[i + 2] == '\t')) - { - i += 3; - break; - } - break outer; - } - case '\n': - { - if (i + 1 < e && (range[i + 1] == ' ' || range[i + 1] == '\t')) - { - i += 2; - break; - } - break outer; - } - case '(': - { - ++i; - size_t commentLevel = 1; - while (i < e) - { - if (range[i] == '(') - ++commentLevel; - else if (range[i] == ')') - { - ++i; - if (--commentLevel == 0) - continue outer; - continue; - } - else if (range[i] == '\\') - { - if (++i == e) - break outer; - } - ++i; - } - break outer; - } - default: return range[i .. e]; - } - } - return range[e .. e]; -} - -@system unittest -{ - import std.algorithm.comparison : equal; - import std.algorithm.iteration : map; - import std.meta : AliasSeq; - import std.stdio : writeln; - import std.string : representation; - - static foreach (cr; AliasSeq!(function(string a){return cast(ubyte[]) a;}, - function(string a){return map!(b => cast(char) b)(a.representation);})) - { - scope(failure) writeln(typeof(cr).stringof); - - assert(_stripCFWS(cr("")).empty); - assert(_stripCFWS(cr("\r")).empty); - assert(_stripCFWS(cr("\r\n")).empty); - assert(_stripCFWS(cr("\r\n ")).empty); - assert(_stripCFWS(cr(" \t\r\n")).empty); - assert(equal(_stripCFWS(cr(" \t\r\n hello")), cr("hello"))); - assert(_stripCFWS(cr(" \t\r\nhello")).empty); - assert(_stripCFWS(cr(" \t\r\n\v")).empty); - assert(equal(_stripCFWS(cr("\v \t\r\n\v")), cr("\v \t\r\n\v"))); - assert(_stripCFWS(cr("()")).empty); - assert(_stripCFWS(cr("(hello world)")).empty); - assert(_stripCFWS(cr("(hello world)(hello world)")).empty); - assert(_stripCFWS(cr("(hello world\r\n foo\r where's\nwaldo)")).empty); - assert(_stripCFWS(cr(" \t (hello \tworld\r\n foo\r where's\nwaldo)\t\t ")).empty); - assert(_stripCFWS(cr(" ")).empty); - assert(_stripCFWS(cr("\t\t\t")).empty); - assert(_stripCFWS(cr("\t \r\n\r \n")).empty); - assert(_stripCFWS(cr("(hello world) (can't find waldo) (he's lost)")).empty); - assert(_stripCFWS(cr("(hello\\) world) (can't \\(find waldo) (he's \\(\\)lost)")).empty); - assert(_stripCFWS(cr("(((((")).empty); - assert(_stripCFWS(cr("(((()))")).empty); - assert(_stripCFWS(cr("(((())))")).empty); - assert(equal(_stripCFWS(cr("(((()))))")), cr(")"))); - assert(equal(_stripCFWS(cr(")))))")), cr(")))))"))); - assert(equal(_stripCFWS(cr("()))))")), cr("))))"))); - assert(equal(_stripCFWS(cr(" hello hello ")), cr("hello hello "))); - assert(equal(_stripCFWS(cr("\thello (world)")), cr("hello (world)"))); - assert(equal(_stripCFWS(cr(" \r\n \\((\\)) foo")), cr("\\((\\)) foo"))); - assert(equal(_stripCFWS(cr(" \r\n (\\((\\))) foo")), cr("foo"))); - assert(equal(_stripCFWS(cr(" \r\n (\\(())) foo")), cr(") foo"))); - assert(_stripCFWS(cr(" \r\n (((\\))) foo")).empty); - - assert(_stripCFWS(cr("(hello)(hello)")).empty); - assert(_stripCFWS(cr(" \r\n (hello)\r\n (hello)")).empty); - assert(_stripCFWS(cr(" \r\n (hello) \r\n (hello) \r\n ")).empty); - assert(_stripCFWS(cr("\t\t\t\t(hello)\t\t\t\t(hello)\t\t\t\t")).empty); - assert(equal(_stripCFWS(cr(" \r\n (hello)\r\n (hello) \r\n hello")), cr("hello"))); - assert(equal(_stripCFWS(cr(" \r\n (hello) \r\n (hello) \r\n hello")), cr("hello"))); - assert(equal(_stripCFWS(cr("\t\r\n\t(hello)\r\n\t(hello)\t\r\n hello")), cr("hello"))); - assert(equal(_stripCFWS(cr("\t\r\n\t(hello)\t\r\n\t(hello)\t\r\n hello")), cr("hello"))); - assert(equal(_stripCFWS(cr(" \r\n (hello) \r\n \r\n (hello) \r\n hello")), cr("hello"))); - assert(equal(_stripCFWS(cr(" \r\n (hello) \r\n (hello) \r\n \r\n hello")), cr("hello"))); - assert(equal(_stripCFWS(cr(" \r\n \r\n (hello)\t\r\n (hello) \r\n hello")), cr("hello"))); - assert(equal(_stripCFWS(cr(" \r\n\t\r\n\t(hello)\t\r\n (hello) \r\n hello")), cr("hello"))); - - assert(equal(_stripCFWS(cr(" (\r\n ( \r\n ) \r\n ) foo")), cr("foo"))); - assert(equal(_stripCFWS(cr(" ( \r\n ( \r\n ) \r\n ) foo")), cr("foo"))); - assert(equal(_stripCFWS(cr(" (\t\r\n ( \r\n ) \r\n ) foo")), cr("foo"))); - assert(equal(_stripCFWS(cr(" (\r\n\t( \r\n ) \r\n ) foo")), cr("foo"))); - assert(equal(_stripCFWS(cr(" ( \r\n (\t\r\n ) \r\n ) foo")), cr("foo"))); - assert(equal(_stripCFWS(cr(" ( \r\n (\r\n ) \r\n ) foo")), cr("foo"))); - assert(equal(_stripCFWS(cr(" ( \r\n (\r\n\t) \r\n ) foo")), cr("foo"))); - assert(equal(_stripCFWS(cr(" ( \r\n ( \r\n) \r\n ) foo")), cr("foo"))); - assert(equal(_stripCFWS(cr(" ( \r\n ( \r\n )\t\r\n ) foo")), cr("foo"))); - assert(equal(_stripCFWS(cr(" ( \r\n ( \r\n )\r\n ) foo")), cr("foo"))); - assert(equal(_stripCFWS(cr(" ( \r\n ( \r\n ) \r\n) foo")), cr("foo"))); - assert(equal(_stripCFWS(cr(" ( \r\n ( \r\n ) \r\n\t) foo")), cr("foo"))); - assert(equal(_stripCFWS(cr(" ( \r\n ( \r\n ) \r\n ) \r\n foo")), cr("foo"))); - assert(equal(_stripCFWS(cr(" ( \r\n ( \r\n ) \r\n )\t\r\n foo")), cr("foo"))); - assert(equal(_stripCFWS(cr(" ( \r\n ( \r\n ) \r\n )\r\n foo")), cr("foo"))); - - assert(equal(_stripCFWS(cr(" ( \r\n \r\n ( \r\n \r\n ) \r\n ) foo")), cr("foo"))); - assert(equal(_stripCFWS(cr(" ( \r\n \r\n ( \r\n \r\n ) \r\n ) foo")), cr("foo"))); - assert(equal(_stripCFWS(cr(" (\t\r\n \r\n ( \r\n \r\n ) \r\n ) foo")), cr("foo"))); - assert(equal(_stripCFWS(cr(" (\r\n \r\n\t( \r\n \r\n ) \r\n ) foo")), cr("foo"))); - assert(equal(_stripCFWS(cr(" (\r\n \r\n( \r\n \r\n ) \r\n ) foo")), cr("foo"))); - assert(equal(_stripCFWS(cr(" (\r\n \r\n ( \r\n \r\n\t) \r\n ) foo")), cr("foo"))); - assert(equal(_stripCFWS(cr(" (\r\n \r\n ( \r\n \r\n )\t\r\n ) foo")), cr("foo"))); - assert(equal(_stripCFWS(cr(" (\r\n \r\n ( \r\n \r\n )\r\n ) foo")), cr("foo"))); - - assert(equal(_stripCFWS(cr(" ( \r\n bar \r\n ( \r\n bar \r\n ) \r\n ) foo")), cr("foo"))); - assert(equal(_stripCFWS(cr(" ( \r\n () \r\n ( \r\n () \r\n ) \r\n ) foo")), cr("foo"))); - assert(equal(_stripCFWS(cr(" ( \r\n \\\\ \r\n ( \r\n \\\\ \r\n ) \r\n ) foo")), cr("foo"))); - - assert(_stripCFWS(cr("(hello)(hello)")).empty); - assert(_stripCFWS(cr(" \n (hello)\n (hello) \n ")).empty); - assert(_stripCFWS(cr(" \n (hello) \n (hello) \n ")).empty); - assert(equal(_stripCFWS(cr(" \n (hello)\n (hello) \n hello")), cr("hello"))); - assert(equal(_stripCFWS(cr(" \n (hello) \n (hello) \n hello")), cr("hello"))); - assert(equal(_stripCFWS(cr("\t\n\t(hello)\n\t(hello)\t\n hello")), cr("hello"))); - assert(equal(_stripCFWS(cr("\t\n\t(hello)\t\n\t(hello)\t\n hello")), cr("hello"))); - assert(equal(_stripCFWS(cr(" \n (hello) \n \n (hello) \n hello")), cr("hello"))); - assert(equal(_stripCFWS(cr(" \n (hello) \n (hello) \n \n hello")), cr("hello"))); - assert(equal(_stripCFWS(cr(" \n \n (hello)\t\n (hello) \n hello")), cr("hello"))); - assert(equal(_stripCFWS(cr(" \n\t\n\t(hello)\t\n (hello) \n hello")), cr("hello"))); - } -} - -// This is so that we don't have to worry about std.conv.to throwing. It also -// doesn't have to worry about quite as many cases as std.conv.to, since it -// doesn't have to worry about a sign on the value or about whether it fits. -T _convDigits(T, R)(R str) -if (isIntegral!T && isSigned!T) // The constraints on R were already covered by parseRFC822DateTime. -{ - import std.ascii : isDigit; - - assert(!str.empty); - T num = 0; - foreach (i; 0 .. str.length) - { - if (i != 0) - num *= 10; - if (!isDigit(str[i])) - return -1; - num += str[i] - '0'; - } - return num; -} - -@safe unittest -{ - import std.conv : to; - import std.range : chain, iota; - foreach (i; chain(iota(0, 101), [250, 999, 1000, 1001, 2345, 9999])) - { - assert(_convDigits!int(to!string(i)) == i, i.to!string); - } - foreach (str; ["-42", "+42", "1a", "1 ", " ", " 42 "]) - { - assert(_convDigits!int(str) == -1, str); - } -} - - -// NOTE: all the non-simple array literals are wrapped in functions, because -// otherwise importing causes re-evaluation of the static initializers using -// CTFE with unittests enabled -version (StdUnittest) -{ -private @safe: - // Variables to help in testing. - Duration currLocalDiffFromUTC; - immutable (TimeZone)[] testTZs; - - // All of these helper arrays are sorted in ascending order. - auto testYearsBC = [-1999, -1200, -600, -4, -1, 0]; - auto testYearsAD = [1, 4, 1000, 1999, 2000, 2012]; - - // I'd use a Tuple, but I get forward reference errors if I try. - struct MonthDay - { - Month month; - short day; - - this(int m, short d) - { - month = cast(Month) m; - day = d; - } - } - - MonthDay[] testMonthDays() - { - static result = [MonthDay(1, 1), - MonthDay(1, 2), - MonthDay(3, 17), - MonthDay(7, 4), - MonthDay(10, 27), - MonthDay(12, 30), - MonthDay(12, 31)]; - return result; - } - - auto testDays = [1, 2, 9, 10, 16, 20, 25, 28, 29, 30, 31]; - - TimeOfDay[] testTODs() - { - static result = [TimeOfDay(0, 0, 0), - TimeOfDay(0, 0, 1), - TimeOfDay(0, 1, 0), - TimeOfDay(1, 0, 0), - TimeOfDay(13, 13, 13), - TimeOfDay(23, 59, 59)]; - return result; - } - - auto testHours = [0, 1, 12, 22, 23]; - auto testMinSecs = [0, 1, 30, 58, 59]; - - // Throwing exceptions is incredibly expensive, so we want to use a smaller - // set of values for tests using assertThrown. - TimeOfDay[] testTODsThrown() - { - static result = [TimeOfDay(0, 0, 0), - TimeOfDay(13, 13, 13), - TimeOfDay(23, 59, 59)]; - return result; - } - - Date[] testDatesBC; - Date[] testDatesAD; - - DateTime[] testDateTimesBC; - DateTime[] testDateTimesAD; - - Duration[] testFracSecs; - - SysTime[] testSysTimesBC; - SysTime[] testSysTimesAD; - - // I'd use a Tuple, but I get forward reference errors if I try. - struct GregDay { int day; Date date; } - GregDay[] testGregDaysBC() - { - static result = [GregDay(-1_373_427, Date(-3760, 9, 7)), // Start of the Hebrew Calendar - GregDay(-735_233, Date(-2012, 1, 1)), - GregDay(-735_202, Date(-2012, 2, 1)), - GregDay(-735_175, Date(-2012, 2, 28)), - GregDay(-735_174, Date(-2012, 2, 29)), - GregDay(-735_173, Date(-2012, 3, 1)), - GregDay(-734_502, Date(-2010, 1, 1)), - GregDay(-734_472, Date(-2010, 1, 31)), - GregDay(-734_471, Date(-2010, 2, 1)), - GregDay(-734_444, Date(-2010, 2, 28)), - GregDay(-734_443, Date(-2010, 3, 1)), - GregDay(-734_413, Date(-2010, 3, 31)), - GregDay(-734_412, Date(-2010, 4, 1)), - GregDay(-734_383, Date(-2010, 4, 30)), - GregDay(-734_382, Date(-2010, 5, 1)), - GregDay(-734_352, Date(-2010, 5, 31)), - GregDay(-734_351, Date(-2010, 6, 1)), - GregDay(-734_322, Date(-2010, 6, 30)), - GregDay(-734_321, Date(-2010, 7, 1)), - GregDay(-734_291, Date(-2010, 7, 31)), - GregDay(-734_290, Date(-2010, 8, 1)), - GregDay(-734_260, Date(-2010, 8, 31)), - GregDay(-734_259, Date(-2010, 9, 1)), - GregDay(-734_230, Date(-2010, 9, 30)), - GregDay(-734_229, Date(-2010, 10, 1)), - GregDay(-734_199, Date(-2010, 10, 31)), - GregDay(-734_198, Date(-2010, 11, 1)), - GregDay(-734_169, Date(-2010, 11, 30)), - GregDay(-734_168, Date(-2010, 12, 1)), - GregDay(-734_139, Date(-2010, 12, 30)), - GregDay(-734_138, Date(-2010, 12, 31)), - GregDay(-731_215, Date(-2001, 1, 1)), - GregDay(-730_850, Date(-2000, 1, 1)), - GregDay(-730_849, Date(-2000, 1, 2)), - GregDay(-730_486, Date(-2000, 12, 30)), - GregDay(-730_485, Date(-2000, 12, 31)), - GregDay(-730_484, Date(-1999, 1, 1)), - GregDay(-694_690, Date(-1901, 1, 1)), - GregDay(-694_325, Date(-1900, 1, 1)), - GregDay(-585_118, Date(-1601, 1, 1)), - GregDay(-584_753, Date(-1600, 1, 1)), - GregDay(-584_388, Date(-1600, 12, 31)), - GregDay(-584_387, Date(-1599, 1, 1)), - GregDay(-365_972, Date(-1001, 1, 1)), - GregDay(-365_607, Date(-1000, 1, 1)), - GregDay(-183_351, Date(-501, 1, 1)), - GregDay(-182_986, Date(-500, 1, 1)), - GregDay(-182_621, Date(-499, 1, 1)), - GregDay(-146_827, Date(-401, 1, 1)), - GregDay(-146_462, Date(-400, 1, 1)), - GregDay(-146_097, Date(-400, 12, 31)), - GregDay(-110_302, Date(-301, 1, 1)), - GregDay(-109_937, Date(-300, 1, 1)), - GregDay(-73_778, Date(-201, 1, 1)), - GregDay(-73_413, Date(-200, 1, 1)), - GregDay(-38_715, Date(-105, 1, 1)), - GregDay(-37_254, Date(-101, 1, 1)), - GregDay(-36_889, Date(-100, 1, 1)), - GregDay(-36_524, Date(-99, 1, 1)), - GregDay(-36_160, Date(-99, 12, 31)), - GregDay(-35_794, Date(-97, 1, 1)), - GregDay(-18_627, Date(-50, 1, 1)), - GregDay(-18_262, Date(-49, 1, 1)), - GregDay(-3652, Date(-9, 1, 1)), - GregDay(-2191, Date(-5, 1, 1)), - GregDay(-1827, Date(-5, 12, 31)), - GregDay(-1826, Date(-4, 1, 1)), - GregDay(-1825, Date(-4, 1, 2)), - GregDay(-1462, Date(-4, 12, 30)), - GregDay(-1461, Date(-4, 12, 31)), - GregDay(-1460, Date(-3, 1, 1)), - GregDay(-1096, Date(-3, 12, 31)), - GregDay(-1095, Date(-2, 1, 1)), - GregDay(-731, Date(-2, 12, 31)), - GregDay(-730, Date(-1, 1, 1)), - GregDay(-367, Date(-1, 12, 30)), - GregDay(-366, Date(-1, 12, 31)), - GregDay(-365, Date(0, 1, 1)), - GregDay(-31, Date(0, 11, 30)), - GregDay(-30, Date(0, 12, 1)), - GregDay(-1, Date(0, 12, 30)), - GregDay(0, Date(0, 12, 31))]; - return result; - } - - GregDay[] testGregDaysAD() - { - static result = [GregDay(1, Date(1, 1, 1)), - GregDay(2, Date(1, 1, 2)), - GregDay(32, Date(1, 2, 1)), - GregDay(365, Date(1, 12, 31)), - GregDay(366, Date(2, 1, 1)), - GregDay(731, Date(3, 1, 1)), - GregDay(1096, Date(4, 1, 1)), - GregDay(1097, Date(4, 1, 2)), - GregDay(1460, Date(4, 12, 30)), - GregDay(1461, Date(4, 12, 31)), - GregDay(1462, Date(5, 1, 1)), - GregDay(17_898, Date(50, 1, 1)), - GregDay(35_065, Date(97, 1, 1)), - GregDay(36_160, Date(100, 1, 1)), - GregDay(36_525, Date(101, 1, 1)), - GregDay(37_986, Date(105, 1, 1)), - GregDay(72_684, Date(200, 1, 1)), - GregDay(73_049, Date(201, 1, 1)), - GregDay(109_208, Date(300, 1, 1)), - GregDay(109_573, Date(301, 1, 1)), - GregDay(145_732, Date(400, 1, 1)), - GregDay(146_098, Date(401, 1, 1)), - GregDay(182_257, Date(500, 1, 1)), - GregDay(182_622, Date(501, 1, 1)), - GregDay(364_878, Date(1000, 1, 1)), - GregDay(365_243, Date(1001, 1, 1)), - GregDay(584_023, Date(1600, 1, 1)), - GregDay(584_389, Date(1601, 1, 1)), - GregDay(693_596, Date(1900, 1, 1)), - GregDay(693_961, Date(1901, 1, 1)), - GregDay(729_755, Date(1999, 1, 1)), - GregDay(730_120, Date(2000, 1, 1)), - GregDay(730_121, Date(2000, 1, 2)), - GregDay(730_484, Date(2000, 12, 30)), - GregDay(730_485, Date(2000, 12, 31)), - GregDay(730_486, Date(2001, 1, 1)), - GregDay(733_773, Date(2010, 1, 1)), - GregDay(733_774, Date(2010, 1, 2)), - GregDay(733_803, Date(2010, 1, 31)), - GregDay(733_804, Date(2010, 2, 1)), - GregDay(733_831, Date(2010, 2, 28)), - GregDay(733_832, Date(2010, 3, 1)), - GregDay(733_862, Date(2010, 3, 31)), - GregDay(733_863, Date(2010, 4, 1)), - GregDay(733_892, Date(2010, 4, 30)), - GregDay(733_893, Date(2010, 5, 1)), - GregDay(733_923, Date(2010, 5, 31)), - GregDay(733_924, Date(2010, 6, 1)), - GregDay(733_953, Date(2010, 6, 30)), - GregDay(733_954, Date(2010, 7, 1)), - GregDay(733_984, Date(2010, 7, 31)), - GregDay(733_985, Date(2010, 8, 1)), - GregDay(734_015, Date(2010, 8, 31)), - GregDay(734_016, Date(2010, 9, 1)), - GregDay(734_045, Date(2010, 9, 30)), - GregDay(734_046, Date(2010, 10, 1)), - GregDay(734_076, Date(2010, 10, 31)), - GregDay(734_077, Date(2010, 11, 1)), - GregDay(734_106, Date(2010, 11, 30)), - GregDay(734_107, Date(2010, 12, 1)), - GregDay(734_136, Date(2010, 12, 30)), - GregDay(734_137, Date(2010, 12, 31)), - GregDay(734_503, Date(2012, 1, 1)), - GregDay(734_534, Date(2012, 2, 1)), - GregDay(734_561, Date(2012, 2, 28)), - GregDay(734_562, Date(2012, 2, 29)), - GregDay(734_563, Date(2012, 3, 1)), - GregDay(734_858, Date(2012, 12, 21))]; - return result; - } - - // I'd use a Tuple, but I get forward reference errors if I try. - struct DayOfYear { int day; MonthDay md; } - DayOfYear[] testDaysOfYear() - { - static result = [DayOfYear(1, MonthDay(1, 1)), - DayOfYear(2, MonthDay(1, 2)), - DayOfYear(3, MonthDay(1, 3)), - DayOfYear(31, MonthDay(1, 31)), - DayOfYear(32, MonthDay(2, 1)), - DayOfYear(59, MonthDay(2, 28)), - DayOfYear(60, MonthDay(3, 1)), - DayOfYear(90, MonthDay(3, 31)), - DayOfYear(91, MonthDay(4, 1)), - DayOfYear(120, MonthDay(4, 30)), - DayOfYear(121, MonthDay(5, 1)), - DayOfYear(151, MonthDay(5, 31)), - DayOfYear(152, MonthDay(6, 1)), - DayOfYear(181, MonthDay(6, 30)), - DayOfYear(182, MonthDay(7, 1)), - DayOfYear(212, MonthDay(7, 31)), - DayOfYear(213, MonthDay(8, 1)), - DayOfYear(243, MonthDay(8, 31)), - DayOfYear(244, MonthDay(9, 1)), - DayOfYear(273, MonthDay(9, 30)), - DayOfYear(274, MonthDay(10, 1)), - DayOfYear(304, MonthDay(10, 31)), - DayOfYear(305, MonthDay(11, 1)), - DayOfYear(334, MonthDay(11, 30)), - DayOfYear(335, MonthDay(12, 1)), - DayOfYear(363, MonthDay(12, 29)), - DayOfYear(364, MonthDay(12, 30)), - DayOfYear(365, MonthDay(12, 31))]; - return result; - } - - DayOfYear[] testDaysOfLeapYear() - { - static result = [DayOfYear(1, MonthDay(1, 1)), - DayOfYear(2, MonthDay(1, 2)), - DayOfYear(3, MonthDay(1, 3)), - DayOfYear(31, MonthDay(1, 31)), - DayOfYear(32, MonthDay(2, 1)), - DayOfYear(59, MonthDay(2, 28)), - DayOfYear(60, MonthDay(2, 29)), - DayOfYear(61, MonthDay(3, 1)), - DayOfYear(91, MonthDay(3, 31)), - DayOfYear(92, MonthDay(4, 1)), - DayOfYear(121, MonthDay(4, 30)), - DayOfYear(122, MonthDay(5, 1)), - DayOfYear(152, MonthDay(5, 31)), - DayOfYear(153, MonthDay(6, 1)), - DayOfYear(182, MonthDay(6, 30)), - DayOfYear(183, MonthDay(7, 1)), - DayOfYear(213, MonthDay(7, 31)), - DayOfYear(214, MonthDay(8, 1)), - DayOfYear(244, MonthDay(8, 31)), - DayOfYear(245, MonthDay(9, 1)), - DayOfYear(274, MonthDay(9, 30)), - DayOfYear(275, MonthDay(10, 1)), - DayOfYear(305, MonthDay(10, 31)), - DayOfYear(306, MonthDay(11, 1)), - DayOfYear(335, MonthDay(11, 30)), - DayOfYear(336, MonthDay(12, 1)), - DayOfYear(364, MonthDay(12, 29)), - DayOfYear(365, MonthDay(12, 30)), - DayOfYear(366, MonthDay(12, 31))]; - return result; - } - - void initializeTests() - { - import std.algorithm.sorting : sort; - import std.typecons : Rebindable; - immutable lt = LocalTime().utcToTZ(0); - currLocalDiffFromUTC = dur!"hnsecs"(lt); - - version (Posix) - { - import std.datetime.timezone : PosixTimeZone; - immutable otherTZ = lt < 0 ? PosixTimeZone.getTimeZone("Australia/Sydney") - : PosixTimeZone.getTimeZone("America/Denver"); - } - else version (Windows) - { - import std.datetime.timezone : WindowsTimeZone; - immutable otherTZ = lt < 0 ? WindowsTimeZone.getTimeZone("AUS Eastern Standard Time") - : WindowsTimeZone.getTimeZone("Mountain Standard Time"); - } - - immutable ot = otherTZ.utcToTZ(0); - - auto diffs = [0L, lt, ot]; - auto diffAA = [0L : Rebindable!(immutable TimeZone)(UTC())]; - diffAA[lt] = Rebindable!(immutable TimeZone)(LocalTime()); - diffAA[ot] = Rebindable!(immutable TimeZone)(otherTZ); - - sort(diffs); - testTZs = [diffAA[diffs[0]], diffAA[diffs[1]], diffAA[diffs[2]]]; - - testFracSecs = [Duration.zero, hnsecs(1), hnsecs(5007), hnsecs(9_999_999)]; - - foreach (year; testYearsBC) - { - foreach (md; testMonthDays) - testDatesBC ~= Date(year, md.month, md.day); - } - - foreach (year; testYearsAD) - { - foreach (md; testMonthDays) - testDatesAD ~= Date(year, md.month, md.day); - } - - foreach (dt; testDatesBC) - { - foreach (tod; testTODs) - testDateTimesBC ~= DateTime(dt, tod); - } - - foreach (dt; testDatesAD) - { - foreach (tod; testTODs) - testDateTimesAD ~= DateTime(dt, tod); - } - - foreach (dt; testDateTimesBC) - { - foreach (tz; testTZs) - { - foreach (fs; testFracSecs) - testSysTimesBC ~= SysTime(dt, fs, tz); - } - } - - foreach (dt; testDateTimesAD) - { - foreach (tz; testTZs) - { - foreach (fs; testFracSecs) - testSysTimesAD ~= SysTime(dt, fs, tz); - } - } - } -} diff --git a/phobos/std/datetime/timezone.d b/phobos/std/datetime/timezone.d deleted file mode 100644 index b238918..0000000 --- a/phobos/std/datetime/timezone.d +++ /dev/null @@ -1,3536 +0,0 @@ -// Written in the D programming language - -/++ - -$(SCRIPT inhibitQuickIndex = 1;) -$(DIVC quickindex, -$(BOOKTABLE, -$(TR $(TH Category) $(TH Functions)) -$(TR $(TD Time zones) $(TD - $(LREF TimeZone) - $(LREF UTC) - $(LREF LocalTime) - $(LREF PosixTimeZone) - $(LREF WindowsTimeZone) - $(LREF SimpleTimeZone) -)) -$(TR $(TD Utilities) $(TD - $(LREF clearTZEnvVar) - $(LREF parseTZConversions) - $(LREF setTZEnvVar) - $(LREF TZConversions) -)) -)) - - License: $(HTTP www.boost.org/LICENSE_1_0.txt, Boost License 1.0). - Authors: $(HTTP jmdavisprog.com, Jonathan M Davis) - Source: $(PHOBOSSRC std/datetime/timezone.d) -+/ -module std.datetime.timezone; - -import core.time : abs, convert, dur, Duration, hours, minutes; -import std.datetime.systime : Clock, stdTimeToUnixTime, SysTime; -import std.range.primitives : back, empty, front, isOutputRange, popFront; -import std.traits : isIntegral, isSomeString; - -version (OSX) - version = Darwin; -else version (iOS) - version = Darwin; -else version (TVOS) - version = Darwin; -else version (WatchOS) - version = Darwin; - -version (Windows) -{ - import core.stdc.time : time_t; - import core.sys.windows.winbase; - import core.sys.windows.winsock2; - import std.windows.registry; - - // Uncomment and run unittests to print missing Windows TZ translations. - // Please subscribe to Microsoft Daylight Saving Time & Time Zone Blog - // (https://blogs.technet.microsoft.com/dst2007/) if you feel responsible - // for updating the translations. - // version = UpdateWindowsTZTranslations; -} -else version (Posix) -{ - import core.sys.posix.signal : timespec; - import core.sys.posix.sys.types : time_t; -} - -version (StdUnittest) import std.exception : assertThrown; - - -/++ - Represents a time zone. It is used with $(REF SysTime,std,datetime,systime) - to indicate the time zone of a $(REF SysTime,std,datetime,systime). - +/ -abstract class TimeZone -{ -public: - - /++ - The name of the time zone. Exactly how the time zone name is formatted - depends on the derived class. In the case of $(LREF PosixTimeZone), it's - the TZ Database name, whereas with $(LREF WindowsTimeZone), it's the - name that Windows chose to give the registry key for that time zone - (typically the name that they give $(LREF stdTime) if the OS is in - English). For other time zone types, what it is depends on how they're - implemented. - - See_Also: - $(HTTP en.wikipedia.org/wiki/Tz_database, Wikipedia entry on TZ - Database)
- $(HTTP en.wikipedia.org/wiki/List_of_tz_database_time_zones, List of - Time Zones) - +/ - @property string name() @safe const nothrow - { - return _name; - } - - - /++ - Typically, the abbreviation (generally 3 or 4 letters) for the time zone - when DST is $(I not) in effect (e.g. PST). It is not necessarily unique. - - However, on Windows, it may be the unabbreviated name (e.g. Pacific - Standard Time). Regardless, it is not the same as name. - +/ - @property string stdName() @safe const scope nothrow - { - return _stdName; - } - - - /++ - Typically, the abbreviation (generally 3 or 4 letters) for the time zone - when DST $(I is) in effect (e.g. PDT). It is not necessarily unique. - - However, on Windows, it may be the unabbreviated name (e.g. Pacific - Daylight Time). Regardless, it is not the same as name. - +/ - @property string dstName() @safe const scope nothrow - { - return _dstName; - } - - - /++ - Whether this time zone has Daylight Savings Time at any point in time. - Note that for some time zone types it may not have DST for current dates - but will still return true for `hasDST` because the time zone did at - some point have DST. - +/ - @property abstract bool hasDST() @safe const nothrow; - - - /++ - Takes the number of hnsecs (100 ns) since midnight, January 1st, 1 A.D. - in UTC time (i.e. std time) and returns whether DST is effect in this - time zone at the given point in time. - - Params: - stdTime = The UTC time that needs to be checked for DST in this time - zone. - +/ - abstract bool dstInEffect(long stdTime) @safe const scope nothrow; - - - /++ - Takes the number of hnsecs (100 ns) since midnight, January 1st, 1 A.D. - in UTC time (i.e. std time) and converts it to this time zone's time. - - Params: - stdTime = The UTC time that needs to be adjusted to this time zone's - time. - +/ - abstract long utcToTZ(long stdTime) @safe const scope nothrow; - - - /++ - Takes the number of hnsecs (100 ns) since midnight, January 1st, 1 A.D. - in this time zone's time and converts it to UTC (i.e. std time). - - Params: - adjTime = The time in this time zone that needs to be adjusted to - UTC time. - +/ - abstract long tzToUTC(long adjTime) @safe const scope nothrow; - - - /++ - Returns what the offset from UTC is at the given std time. - It includes the DST offset in effect at that time (if any). - - Params: - stdTime = The UTC time for which to get the offset from UTC for this - time zone. - +/ - Duration utcOffsetAt(long stdTime) @safe const scope nothrow - { - return dur!"hnsecs"(utcToTZ(stdTime) - stdTime); - } - - // The purpose of this is to handle the case where a Windows time zone is - // new and exists on an up-to-date Windows box but does not exist on Windows - // boxes which have not been properly updated. The "date added" is included - // on the theory that we'll be able to remove them at some point in the - // the future once enough time has passed, and that way, we know how much - // time has passed. - private static string _getOldName(string windowsTZName) @safe pure nothrow - { - switch (windowsTZName) - { - case "Belarus Standard Time": return "Kaliningrad Standard Time"; // Added 2014-10-08 - case "Russia Time Zone 10": return "Magadan Standard Time"; // Added 2014-10-08 - case "Russia Time Zone 11": return "Magadan Standard Time"; // Added 2014-10-08 - case "Russia Time Zone 3": return "Russian Standard Time"; // Added 2014-10-08 - default: return null; - } - } - - // Since reading in the time zone files could be expensive, most unit tests - // are consolidated into this one unittest block which minimizes how often - // it reads a time zone file. - @system unittest - { - import core.exception : AssertError; - import std.conv : to; - import std.file : exists, isFile; - import std.format : format; - import std.path : chainPath; - import std.stdio : writefln; - import std.typecons : tuple; - - version (Posix) alias getTimeZone = PosixTimeZone.getTimeZone; - else version (Windows) alias getTimeZone = WindowsTimeZone.getTimeZone; - - version (Posix) scope(exit) clearTZEnvVar(); - - static immutable(TimeZone) testTZ(string tzName, - string stdName, - string dstName, - Duration utcOffset, - Duration dstOffset, - bool north = true) - { - scope(failure) writefln("Failed time zone: %s", tzName); - - version (Posix) - { - immutable tz = PosixTimeZone.getTimeZone(tzName); - assert(tz.name == tzName); - } - else version (Windows) - { - immutable tz = WindowsTimeZone.getTimeZone(tzName); - assert(tz.name == stdName); - } - - immutable hasDST = dstOffset != Duration.zero; - - //assert(tz.stdName == stdName); //Locale-dependent - //assert(tz.dstName == dstName); //Locale-dependent - assert(tz.hasDST == hasDST); - - import std.datetime.date : DateTime; - immutable stdDate = DateTime(2010, north ? 1 : 7, 1, 6, 0, 0); - immutable dstDate = DateTime(2010, north ? 7 : 1, 1, 6, 0, 0); - auto std = SysTime(stdDate, tz); - auto dst = SysTime(dstDate, tz); - auto stdUTC = SysTime(stdDate - utcOffset, UTC()); - auto dstUTC = SysTime(stdDate - utcOffset + dstOffset, UTC()); - - assert(!std.dstInEffect); - assert(dst.dstInEffect == hasDST); - assert(tz.utcOffsetAt(std.stdTime) == utcOffset); - assert(tz.utcOffsetAt(dst.stdTime) == utcOffset + dstOffset); - - assert(cast(DateTime) std == stdDate); - assert(cast(DateTime) dst == dstDate); - assert(std == stdUTC); - - version (Posix) - { - setTZEnvVar(tzName); - - static void testTM(scope const SysTime st) - { - import core.stdc.time : tm; - import core.sys.posix.time : localtime_r; - - time_t unixTime = st.toUnixTime(); - tm osTimeInfo = void; - localtime_r(&unixTime, &osTimeInfo); - tm ourTimeInfo = st.toTM(); - - assert(ourTimeInfo.tm_sec == osTimeInfo.tm_sec); - assert(ourTimeInfo.tm_min == osTimeInfo.tm_min); - assert(ourTimeInfo.tm_hour == osTimeInfo.tm_hour); - assert(ourTimeInfo.tm_mday == osTimeInfo.tm_mday); - assert(ourTimeInfo.tm_mon == osTimeInfo.tm_mon); - assert(ourTimeInfo.tm_year == osTimeInfo.tm_year); - assert(ourTimeInfo.tm_wday == osTimeInfo.tm_wday); - assert(ourTimeInfo.tm_yday == osTimeInfo.tm_yday); - assert(ourTimeInfo.tm_isdst == osTimeInfo.tm_isdst); - assert(ourTimeInfo.tm_gmtoff == osTimeInfo.tm_gmtoff); - assert(to!string(ourTimeInfo.tm_zone) == to!string(osTimeInfo.tm_zone)); - } - - testTM(std); - testTM(dst); - - // Apparently, right/ does not exist on Mac OS X. I don't know - // whether or not it exists on FreeBSD. It's rather pointless - // normally, since the Posix standard requires that leap seconds - // be ignored, so it does make some sense that right/ wouldn't - // be there, but since PosixTimeZone _does_ use leap seconds if - // the time zone file does, we'll test that functionality if the - // appropriate files exist. - if (chainPath(PosixTimeZone.defaultTZDatabaseDir, "right", tzName).exists) - { - auto leapTZ = PosixTimeZone.getTimeZone("right/" ~ tzName); - - assert(leapTZ.name == "right/" ~ tzName); - //assert(leapTZ.stdName == stdName); //Locale-dependent - //assert(leapTZ.dstName == dstName); //Locale-dependent - assert(leapTZ.hasDST == hasDST); - - auto leapSTD = SysTime(std.stdTime, leapTZ); - auto leapDST = SysTime(dst.stdTime, leapTZ); - - assert(!leapSTD.dstInEffect); - assert(leapDST.dstInEffect == hasDST); - - assert(leapSTD.stdTime == std.stdTime); - assert(leapDST.stdTime == dst.stdTime); - - // Whenever a leap second is added/removed, - // this will have to be adjusted. - //enum leapDiff = convert!("seconds", "hnsecs")(25); - //assert(leapSTD.adjTime - leapDiff == std.adjTime); - //assert(leapDST.adjTime - leapDiff == dst.adjTime); - } - } - - return tz; - } - - import std.datetime.date : DateTime; - auto dstSwitches = [/+America/Los_Angeles+/ tuple(DateTime(2012, 3, 11), DateTime(2012, 11, 4), 2, 2), - /+America/New_York+/ tuple(DateTime(2012, 3, 11), DateTime(2012, 11, 4), 2, 2), - ///+America/Santiago+/ tuple(DateTime(2011, 8, 21), DateTime(2011, 5, 8), 0, 0), - /+Europe/London+/ tuple(DateTime(2012, 3, 25), DateTime(2012, 10, 28), 1, 2), - /+Europe/Paris+/ tuple(DateTime(2012, 3, 25), DateTime(2012, 10, 28), 2, 3), - /+Australia/Adelaide+/ tuple(DateTime(2012, 10, 7), DateTime(2012, 4, 1), 2, 3)]; - - import std.datetime.date : DateTimeException; - version (Posix) - { - version (FreeBSD) enum utcZone = "Etc/UTC"; - else version (OpenBSD) enum utcZone = "UTC"; - else version (NetBSD) enum utcZone = "UTC"; - else version (DragonFlyBSD) enum utcZone = "UTC"; - else version (linux) enum utcZone = "UTC"; - else version (Darwin) enum utcZone = "UTC"; - else version (Solaris) enum utcZone = "UTC"; - else static assert(0, "The location of the UTC timezone file on this Posix platform must be set."); - - auto tzs = [testTZ("America/Los_Angeles", "PST", "PDT", dur!"hours"(-8), dur!"hours"(1)), - testTZ("America/New_York", "EST", "EDT", dur!"hours"(-5), dur!"hours"(1)), - //testTZ("America/Santiago", "CLT", "CLST", dur!"hours"(-4), dur!"hours"(1), false), - testTZ("Europe/London", "GMT", "BST", dur!"hours"(0), dur!"hours"(1)), - testTZ("Europe/Paris", "CET", "CEST", dur!"hours"(1), dur!"hours"(1)), - // Per www.timeanddate.com, it should be "CST" and "CDT", - // but the OS insists that it's "CST" for both. We should - // probably figure out how to report an error in the TZ - // database and report it. - testTZ("Australia/Adelaide", "CST", "CST", - dur!"hours"(9) + dur!"minutes"(30), dur!"hours"(1), false)]; - - testTZ(utcZone, "UTC", "UTC", dur!"hours"(0), dur!"hours"(0)); - assertThrown!DateTimeException(PosixTimeZone.getTimeZone("hello_world")); - } - else version (Windows) - { - auto tzs = [testTZ("Pacific Standard Time", "Pacific Standard Time", - "Pacific Daylight Time", dur!"hours"(-8), dur!"hours"(1)), - testTZ("Eastern Standard Time", "Eastern Standard Time", - "Eastern Daylight Time", dur!"hours"(-5), dur!"hours"(1)), - //testTZ("Pacific SA Standard Time", "Pacific SA Standard Time", - //"Pacific SA Daylight Time", dur!"hours"(-4), dur!"hours"(1), false), - testTZ("GMT Standard Time", "GMT Standard Time", - "GMT Daylight Time", dur!"hours"(0), dur!"hours"(1)), - testTZ("Romance Standard Time", "Romance Standard Time", - "Romance Daylight Time", dur!"hours"(1), dur!"hours"(1)), - testTZ("Cen. Australia Standard Time", "Cen. Australia Standard Time", - "Cen. Australia Daylight Time", - dur!"hours"(9) + dur!"minutes"(30), dur!"hours"(1), false)]; - - testTZ("Greenwich Standard Time", "Greenwich Standard Time", - "Greenwich Daylight Time", dur!"hours"(0), dur!"hours"(0)); - assertThrown!DateTimeException(WindowsTimeZone.getTimeZone("hello_world")); - } - else - assert(0, "OS not supported."); - - foreach (i; 0 .. tzs.length) - { - auto tz = tzs[i]; - immutable spring = dstSwitches[i][2]; - immutable fall = dstSwitches[i][3]; - auto stdOffset = SysTime(dstSwitches[i][0] + dur!"days"(-1), tz).utcOffset; - auto dstOffset = stdOffset + dur!"hours"(1); - - // Verify that creating a SysTime in the given time zone results - // in a SysTime with the correct std time during and surrounding - // a DST switch. - foreach (hour; -12 .. 13) - { - import std.exception : enforce; - auto st = SysTime(dstSwitches[i][0] + dur!"hours"(hour), tz); - immutable targetHour = hour < 0 ? hour + 24 : hour; - - static void testHour(SysTime st, int hour, string tzName, size_t line = __LINE__) - { - enforce(st.hour == hour, - new AssertError(format("[%s] [%s]: [%s] [%s]", st, tzName, st.hour, hour), - __FILE__, line)); - } - - void testOffset1(Duration offset, bool dstInEffect, size_t line = __LINE__) - { - AssertError msg(string tag) - { - return new AssertError(format("%s [%s] [%s]: [%s] [%s] [%s]", - tag, st, tz.name, st.utcOffset, stdOffset, dstOffset), - __FILE__, line); - } - - enforce(st.dstInEffect == dstInEffect, msg("1")); - enforce(st.utcOffset == offset, msg("2")); - enforce((st + dur!"minutes"(1)).utcOffset == offset, msg("3")); - } - - if (hour == spring) - { - testHour(st, spring + 1, tz.name); - testHour(st + dur!"minutes"(1), spring + 1, tz.name); - } - else - { - testHour(st, targetHour, tz.name); - testHour(st + dur!"minutes"(1), targetHour, tz.name); - } - - if (hour < spring) - testOffset1(stdOffset, false); - else - testOffset1(dstOffset, true); - - st = SysTime(dstSwitches[i][1] + dur!"hours"(hour), tz); - testHour(st, targetHour, tz.name); - - // Verify that 01:00 is the first 01:00 (or whatever hour before the switch is). - if (hour == fall - 1) - testHour(st + dur!"hours"(1), targetHour, tz.name); - - if (hour < fall) - testOffset1(dstOffset, true); - else - testOffset1(stdOffset, false); - } - - // Verify that converting a time in UTC to a time in another - // time zone results in the correct time during and surrounding - // a DST switch. - bool first = true; - auto springSwitch = SysTime(dstSwitches[i][0] + dur!"hours"(spring), UTC()) - stdOffset; - auto fallSwitch = SysTime(dstSwitches[i][1] + dur!"hours"(fall), UTC()) - dstOffset; - // https://issues.dlang.org/show_bug.cgi?id=3659 makes this necessary. - auto fallSwitchMinus1 = fallSwitch - dur!"hours"(1); - - foreach (hour; -24 .. 25) - { - auto utc = SysTime(dstSwitches[i][0] + dur!"hours"(hour), UTC()); - auto local = utc.toOtherTZ(tz); - - void testOffset2(Duration offset, size_t line = __LINE__) - { - AssertError msg(string tag) - { - return new AssertError(format("%s [%s] [%s]: [%s] [%s]", tag, hour, tz.name, utc, local), - __FILE__, line); - } - - import std.exception : enforce; - enforce((utc + offset).hour == local.hour, msg("1")); - enforce((utc + offset + dur!"minutes"(1)).hour == local.hour, msg("2")); - } - - if (utc < springSwitch) - testOffset2(stdOffset); - else - testOffset2(dstOffset); - - utc = SysTime(dstSwitches[i][1] + dur!"hours"(hour), UTC()); - local = utc.toOtherTZ(tz); - - if (utc == fallSwitch || utc == fallSwitchMinus1) - { - if (first) - { - testOffset2(dstOffset); - first = false; - } - else - testOffset2(stdOffset); - } - else if (utc > fallSwitch) - testOffset2(stdOffset); - else - testOffset2(dstOffset); - } - } - } - - -protected: - - /++ - Params: - name = The name of the time zone. - stdName = The abbreviation for the time zone during std time. - dstName = The abbreviation for the time zone during DST. - +/ - this(string name, string stdName, string dstName) @safe immutable pure - { - _name = name; - _stdName = stdName; - _dstName = dstName; - } - - -private: - - immutable string _name; - immutable string _stdName; - immutable string _dstName; -} - - -/++ - A TimeZone which represents the current local time zone on - the system running your program. - - This uses the underlying C calls to adjust the time rather than using - specific D code based off of system settings to calculate the time such as - $(LREF PosixTimeZone) and $(LREF WindowsTimeZone) do. That also means that - it will use whatever the current time zone is on the system, even if the - system's time zone changes while the program is running. - +/ -final class LocalTime : TimeZone -{ -public: - - /++ - $(LREF LocalTime) is a singleton class. $(LREF LocalTime) returns its - only instance. - +/ - static immutable(LocalTime) opCall() @trusted pure nothrow - { - alias FuncType = immutable(LocalTime) function() @safe pure nothrow; - return (cast(FuncType)&singleton)(); - } - - - version (StdDdoc) - { - /++ - In principle, this is the name of the local time zone. However, - this always returns the empty string. This is because time zones - cannot be uniquely identified by the attributes given by the - OS (such as the `stdName` and `dstName`), and neither Posix systems - nor Windows systems provide an easy way to get the TZ Database name - of the local time zone. - - See_Also: - $(HTTP en.wikipedia.org/wiki/Tz_database, Wikipedia entry on TZ - Database)
- $(HTTP en.wikipedia.org/wiki/List_of_tz_database_time_zones, List - of Time Zones) - +/ - @property override string name() @safe const nothrow; - } - - - /++ - Typically, the abbreviation (generally 3 or 4 letters) for the time zone - when DST is $(I not) in effect (e.g. PST). It is not necessarily unique. - - However, on Windows, it may be the unabbreviated name (e.g. Pacific - Standard Time). Regardless, it is not the same as name. - - This property is overridden because the local time of the system could - change while the program is running and we need to determine it - dynamically rather than it being fixed like it would be with most time - zones. - +/ - @property override string stdName() @trusted const scope nothrow - { - version (Posix) - { - import core.stdc.time : tzname; - import std.conv : to; - try - return to!string(tzname[0]); - catch (Exception e) - assert(0, "to!string(tzname[0]) failed."); - } - else version (Windows) - { - TIME_ZONE_INFORMATION tzInfo; - GetTimeZoneInformation(&tzInfo); - - // Cannot use to!string() like this should, probably due to bug - // https://issues.dlang.org/show_bug.cgi?id=5016 - //return to!string(tzInfo.StandardName); - - wchar[32] str; - - foreach (i, ref wchar c; str) - c = tzInfo.StandardName[i]; - - string retval; - - try - { - foreach (dchar c; str) - { - if (c == '\0') - break; - - retval ~= c; - } - - return retval; - } - catch (Exception e) - assert(0, "GetTimeZoneInformation() returned invalid UTF-16."); - } - } - - @safe unittest - { - version (FreeBSD) - { - // A bug on FreeBSD 9+ makes it so that this test fails. - // https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=168862 - } - else version (NetBSD) - { - // The same bug on NetBSD 7+ - } - else - { - assert(LocalTime().stdName !is null); - - version (Posix) - { - scope(exit) clearTZEnvVar(); - - setTZEnvVar("America/Los_Angeles"); - assert(LocalTime().stdName == "PST"); - - setTZEnvVar("America/New_York"); - assert(LocalTime().stdName == "EST"); - } - } - } - - - /++ - Typically, the abbreviation (generally 3 or 4 letters) for the time zone - when DST $(I is) in effect (e.g. PDT). It is not necessarily unique. - - However, on Windows, it may be the unabbreviated name (e.g. Pacific - Daylight Time). Regardless, it is not the same as name. - - This property is overridden because the local time of the system could - change while the program is running and we need to determine it - dynamically rather than it being fixed like it would be with most time - zones. - +/ - @property override string dstName() @trusted const scope nothrow - { - version (Posix) - { - import core.stdc.time : tzname; - import std.conv : to; - try - return to!string(tzname[1]); - catch (Exception e) - assert(0, "to!string(tzname[1]) failed."); - } - else version (Windows) - { - TIME_ZONE_INFORMATION tzInfo; - GetTimeZoneInformation(&tzInfo); - - // Cannot use to!string() like this should, probably due to bug - // https://issues.dlang.org/show_bug.cgi?id=5016 - //return to!string(tzInfo.DaylightName); - - wchar[32] str; - - foreach (i, ref wchar c; str) - c = tzInfo.DaylightName[i]; - - string retval; - - try - { - foreach (dchar c; str) - { - if (c == '\0') - break; - - retval ~= c; - } - - return retval; - } - catch (Exception e) - assert(0, "GetTimeZoneInformation() returned invalid UTF-16."); - } - } - - @safe unittest - { - // tzname, called from dstName, isn't set by default for Musl. - version (CRuntime_Musl) - assert(LocalTime().dstName is null); - else - assert(LocalTime().dstName !is null); - - version (Posix) - { - scope(exit) clearTZEnvVar(); - - version (FreeBSD) - { - // A bug on FreeBSD 9+ makes it so that this test fails. - // https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=168862 - } - else version (NetBSD) - { - // The same bug on NetBSD 7+ - } - else - { - setTZEnvVar("America/Los_Angeles"); - assert(LocalTime().dstName == "PDT"); - - setTZEnvVar("America/New_York"); - assert(LocalTime().dstName == "EDT"); - } - } - } - - - /++ - Whether this time zone has Daylight Savings Time at any point in time. - Note that for some time zone types it may not have DST for current - dates but will still return true for `hasDST` because the time zone - did at some point have DST. - +/ - @property override bool hasDST() @trusted const nothrow - { - version (Posix) - { - static if (is(typeof(daylight))) - return cast(bool)(daylight); - else - { - try - { - import std.datetime.date : Date; - auto currYear = (cast(Date) Clock.currTime()).year; - auto janOffset = SysTime(Date(currYear, 1, 4), cast(immutable) this).stdTime - - SysTime(Date(currYear, 1, 4), UTC()).stdTime; - auto julyOffset = SysTime(Date(currYear, 7, 4), cast(immutable) this).stdTime - - SysTime(Date(currYear, 7, 4), UTC()).stdTime; - - return janOffset != julyOffset; - } - catch (Exception e) - assert(0, "Clock.currTime() threw."); - } - } - else version (Windows) - { - TIME_ZONE_INFORMATION tzInfo; - GetTimeZoneInformation(&tzInfo); - - return tzInfo.DaylightDate.wMonth != 0; - } - } - - @safe unittest - { - LocalTime().hasDST; - - version (Posix) - { - scope(exit) clearTZEnvVar(); - - setTZEnvVar("America/Los_Angeles"); - assert(LocalTime().hasDST); - - setTZEnvVar("America/New_York"); - assert(LocalTime().hasDST); - - setTZEnvVar("UTC"); - assert(!LocalTime().hasDST); - } - } - - - /++ - Takes the number of hnsecs (100 ns) since midnight, January 1st, 1 A.D. - in UTC time (i.e. std time) and returns whether DST is in effect in this - time zone at the given point in time. - - Params: - stdTime = The UTC time that needs to be checked for DST in this time - zone. - +/ - override bool dstInEffect(long stdTime) @trusted const scope nothrow - { - import core.stdc.time : tm; - - time_t unixTime = stdTimeToUnixTime(stdTime); - - version (Posix) - { - import core.sys.posix.time : localtime_r; - - tm timeInfo = void; - localtime_r(&unixTime, &timeInfo); - - return cast(bool)(timeInfo.tm_isdst); - } - else version (Windows) - { - import core.stdc.time : localtime; - - // Apparently Windows isn't smart enough to deal with negative time_t. - if (unixTime >= 0) - { - tm* timeInfo = localtime(&unixTime); - - if (timeInfo) - return cast(bool)(timeInfo.tm_isdst); - } - - TIME_ZONE_INFORMATION tzInfo; - GetTimeZoneInformation(&tzInfo); - - return WindowsTimeZone._dstInEffect(&tzInfo, stdTime); - } - } - - @safe unittest - { - auto currTime = Clock.currStdTime; - LocalTime().dstInEffect(currTime); - } - - - /++ - Returns hnsecs in the local time zone using the standard C function - calls on Posix systems and the standard Windows system calls on Windows - systems to adjust the time to the appropriate time zone from std time. - - Params: - stdTime = The UTC time that needs to be adjusted to this time zone's - time. - - See_Also: - `TimeZone.utcToTZ` - +/ - override long utcToTZ(long stdTime) @trusted const scope nothrow - { - version (Solaris) - return stdTime + convert!("seconds", "hnsecs")(tm_gmtoff(stdTime)); - else version (Posix) - { - import core.stdc.time : tm; - import core.sys.posix.time : localtime_r; - time_t unixTime = stdTimeToUnixTime(stdTime); - tm timeInfo = void; - localtime_r(&unixTime, &timeInfo); - - return stdTime + convert!("seconds", "hnsecs")(timeInfo.tm_gmtoff); - } - else version (Windows) - { - TIME_ZONE_INFORMATION tzInfo; - GetTimeZoneInformation(&tzInfo); - - return WindowsTimeZone._utcToTZ(&tzInfo, stdTime, hasDST); - } - } - - @safe unittest - { - LocalTime().utcToTZ(0); - } - - - /++ - Returns std time using the standard C function calls on Posix systems - and the standard Windows system calls on Windows systems to adjust the - time to UTC from the appropriate time zone. - - See_Also: - `TimeZone.tzToUTC` - - Params: - adjTime = The time in this time zone that needs to be adjusted to - UTC time. - +/ - override long tzToUTC(long adjTime) @trusted const scope nothrow - { - version (Posix) - { - import core.stdc.time : tm; - import core.sys.posix.time : localtime_r; - time_t unixTime = stdTimeToUnixTime(adjTime); - - immutable past = unixTime - cast(time_t) convert!("days", "seconds")(1); - tm timeInfo = void; - localtime_r(past < unixTime ? &past : &unixTime, &timeInfo); - immutable pastOffset = timeInfo.tm_gmtoff; - - immutable future = unixTime + cast(time_t) convert!("days", "seconds")(1); - localtime_r(future > unixTime ? &future : &unixTime, &timeInfo); - immutable futureOffset = timeInfo.tm_gmtoff; - - if (pastOffset == futureOffset) - return adjTime - convert!("seconds", "hnsecs")(pastOffset); - - if (pastOffset < futureOffset) - unixTime -= cast(time_t) convert!("hours", "seconds")(1); - - unixTime -= pastOffset; - localtime_r(&unixTime, &timeInfo); - - return adjTime - convert!("seconds", "hnsecs")(timeInfo.tm_gmtoff); - } - else version (Windows) - { - TIME_ZONE_INFORMATION tzInfo; - GetTimeZoneInformation(&tzInfo); - - return WindowsTimeZone._tzToUTC(&tzInfo, adjTime, hasDST); - } - } - - @safe unittest - { - import core.exception : AssertError; - import std.format : format; - import std.typecons : tuple; - - assert(LocalTime().tzToUTC(LocalTime().utcToTZ(0)) == 0); - assert(LocalTime().utcToTZ(LocalTime().tzToUTC(0)) == 0); - - assert(LocalTime().tzToUTC(LocalTime().utcToTZ(0)) == 0); - assert(LocalTime().utcToTZ(LocalTime().tzToUTC(0)) == 0); - - version (Posix) - { - scope(exit) clearTZEnvVar(); - - import std.datetime.date : DateTime; - auto tzInfos = [tuple("America/Los_Angeles", DateTime(2012, 3, 11), DateTime(2012, 11, 4), 2, 2), - tuple("America/New_York", DateTime(2012, 3, 11), DateTime(2012, 11, 4), 2, 2), - //tuple("America/Santiago", DateTime(2011, 8, 21), DateTime(2011, 5, 8), 0, 0), - tuple("Atlantic/Azores", DateTime(2011, 3, 27), DateTime(2011, 10, 30), 0, 1), - tuple("Europe/London", DateTime(2012, 3, 25), DateTime(2012, 10, 28), 1, 2), - tuple("Europe/Paris", DateTime(2012, 3, 25), DateTime(2012, 10, 28), 2, 3), - tuple("Australia/Adelaide", DateTime(2012, 10, 7), DateTime(2012, 4, 1), 2, 3)]; - - foreach (i; 0 .. tzInfos.length) - { - import std.exception : enforce; - auto tzName = tzInfos[i][0]; - setTZEnvVar(tzName); - immutable spring = tzInfos[i][3]; - immutable fall = tzInfos[i][4]; - auto stdOffset = SysTime(tzInfos[i][1] + dur!"hours"(-12)).utcOffset; - auto dstOffset = stdOffset + dur!"hours"(1); - - // Verify that creating a SysTime in the given time zone results - // in a SysTime with the correct std time during and surrounding - // a DST switch. - foreach (hour; -12 .. 13) - { - auto st = SysTime(tzInfos[i][1] + dur!"hours"(hour)); - immutable targetHour = hour < 0 ? hour + 24 : hour; - - static void testHour(SysTime st, int hour, string tzName, size_t line = __LINE__) - { - enforce(st.hour == hour, - new AssertError(format("[%s] [%s]: [%s] [%s]", st, tzName, st.hour, hour), - __FILE__, line)); - } - - void testOffset1(Duration offset, bool dstInEffect, size_t line = __LINE__) - { - AssertError msg(string tag) - { - return new AssertError(format("%s [%s] [%s]: [%s] [%s] [%s]", - tag, st, tzName, st.utcOffset, stdOffset, dstOffset), - __FILE__, line); - } - - enforce(st.dstInEffect == dstInEffect, msg("1")); - enforce(st.utcOffset == offset, msg("2")); - enforce((st + dur!"minutes"(1)).utcOffset == offset, msg("3")); - } - - if (hour == spring) - { - testHour(st, spring + 1, tzName); - testHour(st + dur!"minutes"(1), spring + 1, tzName); - } - else - { - testHour(st, targetHour, tzName); - testHour(st + dur!"minutes"(1), targetHour, tzName); - } - - if (hour < spring) - testOffset1(stdOffset, false); - else - testOffset1(dstOffset, true); - - st = SysTime(tzInfos[i][2] + dur!"hours"(hour)); - testHour(st, targetHour, tzName); - - // Verify that 01:00 is the first 01:00 (or whatever hour before the switch is). - if (hour == fall - 1) - testHour(st + dur!"hours"(1), targetHour, tzName); - - if (hour < fall) - testOffset1(dstOffset, true); - else - testOffset1(stdOffset, false); - } - - // Verify that converting a time in UTC to a time in another - // time zone results in the correct time during and surrounding - // a DST switch. - bool first = true; - auto springSwitch = SysTime(tzInfos[i][1] + dur!"hours"(spring), UTC()) - stdOffset; - auto fallSwitch = SysTime(tzInfos[i][2] + dur!"hours"(fall), UTC()) - dstOffset; - // https://issues.dlang.org/show_bug.cgi?id=3659 makes this necessary. - auto fallSwitchMinus1 = fallSwitch - dur!"hours"(1); - - foreach (hour; -24 .. 25) - { - auto utc = SysTime(tzInfos[i][1] + dur!"hours"(hour), UTC()); - auto local = utc.toLocalTime(); - - void testOffset2(Duration offset, size_t line = __LINE__) - { - AssertError msg(string tag) - { - return new AssertError(format("%s [%s] [%s]: [%s] [%s]", tag, hour, tzName, utc, local), - __FILE__, line); - } - - enforce((utc + offset).hour == local.hour, msg("1")); - enforce((utc + offset + dur!"minutes"(1)).hour == local.hour, msg("2")); - } - - if (utc < springSwitch) - testOffset2(stdOffset); - else - testOffset2(dstOffset); - - utc = SysTime(tzInfos[i][2] + dur!"hours"(hour), UTC()); - local = utc.toLocalTime(); - - if (utc == fallSwitch || utc == fallSwitchMinus1) - { - if (first) - { - testOffset2(dstOffset); - first = false; - } - else - testOffset2(stdOffset); - } - else if (utc > fallSwitch) - testOffset2(stdOffset); - else - testOffset2(dstOffset); - } - } - } - } - - -private: - - this() @safe immutable pure - { - super("", "", ""); - } - - - // This is done so that we can maintain purity in spite of doing an impure - // operation the first time that LocalTime() is called. - static immutable(LocalTime) singleton() @trusted - { - import core.stdc.time : tzset; - import std.concurrency : initOnce; - static instance = new immutable(LocalTime)(); - static shared bool guard; - initOnce!guard({tzset(); return true;}()); - return instance; - } - - - // The Solaris version of struct tm has no tm_gmtoff field, so do it here - version (Solaris) - { - long tm_gmtoff(long stdTime) @trusted const nothrow - { - import core.stdc.time : tm; - import core.sys.posix.time : localtime_r, gmtime_r; - - time_t unixTime = stdTimeToUnixTime(stdTime); - tm timeInfo = void; - localtime_r(&unixTime, &timeInfo); - tm timeInfoGmt = void; - gmtime_r(&unixTime, &timeInfoGmt); - - return timeInfo.tm_sec - timeInfoGmt.tm_sec + - convert!("minutes", "seconds")(timeInfo.tm_min - timeInfoGmt.tm_min) + - convert!("hours", "seconds")(timeInfo.tm_hour - timeInfoGmt.tm_hour); - } - } -} - - -/++ - A $(LREF TimeZone) which represents UTC. - +/ -final class UTC : TimeZone -{ -public: - - /++ - `UTC` is a singleton class. `UTC` returns its only instance. - +/ - static immutable(UTC) opCall() @safe pure nothrow - { - return _utc; - } - - - /++ - Always returns false. - +/ - @property override bool hasDST() @safe const nothrow - { - return false; - } - - - /++ - Always returns false. - +/ - override bool dstInEffect(long stdTime) @safe const scope nothrow - { - return false; - } - - - /++ - Returns the given hnsecs without changing them at all. - - Params: - stdTime = The UTC time that needs to be adjusted to this time zone's - time. - - See_Also: - `TimeZone.utcToTZ` - +/ - override long utcToTZ(long stdTime) @safe const scope nothrow - { - return stdTime; - } - - @safe unittest - { - assert(UTC().utcToTZ(0) == 0); - - version (Posix) - { - scope(exit) clearTZEnvVar(); - - setTZEnvVar("UTC"); - import std.datetime.date : Date; - auto std = SysTime(Date(2010, 1, 1)); - auto dst = SysTime(Date(2010, 7, 1)); - assert(UTC().utcToTZ(std.stdTime) == std.stdTime); - assert(UTC().utcToTZ(dst.stdTime) == dst.stdTime); - } - } - - - /++ - Returns the given hnsecs without changing them at all. - - See_Also: - `TimeZone.tzToUTC` - - Params: - adjTime = The time in this time zone that needs to be adjusted to - UTC time. - +/ - override long tzToUTC(long adjTime) @safe const scope nothrow - { - return adjTime; - } - - @safe unittest - { - assert(UTC().tzToUTC(0) == 0); - - version (Posix) - { - scope(exit) clearTZEnvVar(); - - setTZEnvVar("UTC"); - import std.datetime.date : Date; - auto std = SysTime(Date(2010, 1, 1)); - auto dst = SysTime(Date(2010, 7, 1)); - assert(UTC().tzToUTC(std.stdTime) == std.stdTime); - assert(UTC().tzToUTC(dst.stdTime) == dst.stdTime); - } - } - - - /++ - Returns a $(REF Duration, core,time) of 0. - - Params: - stdTime = The UTC time for which to get the offset from UTC for this - time zone. - +/ - override Duration utcOffsetAt(long stdTime) @safe const scope nothrow - { - return dur!"hnsecs"(0); - } - - -private: - - this() @safe immutable pure - { - super("UTC", "UTC", "UTC"); - } - - - static immutable UTC _utc = new immutable(UTC)(); -} - - -/++ - Represents a time zone with an offset (in minutes, west is negative) from - UTC but no DST. - - It's primarily used as the time zone in the result of - $(REF SysTime,std,datetime,systime)'s `fromISOString`, - `fromISOExtString`, and `fromSimpleString`. - - `name` and `dstName` are always the empty string since this time zone - has no DST, and while it may be meant to represent a time zone which is in - the TZ Database, obviously it's not likely to be following the exact rules - of any of the time zones in the TZ Database, so it makes no sense to set it. - +/ -final class SimpleTimeZone : TimeZone -{ -public: - - /++ - Always returns false. - +/ - @property override bool hasDST() @safe const nothrow - { - return false; - } - - - /++ - Always returns false. - +/ - override bool dstInEffect(long stdTime) @safe const scope nothrow - { - return false; - } - - - /++ - Takes the number of hnsecs (100 ns) since midnight, January 1st, 1 A.D. - in UTC time (i.e. std time) and converts it to this time zone's time. - - Params: - stdTime = The UTC time that needs to be adjusted to this time zone's - time. - +/ - override long utcToTZ(long stdTime) @safe const scope nothrow - { - return stdTime + _utcOffset.total!"hnsecs"; - } - - @safe unittest - { - auto west = new immutable SimpleTimeZone(dur!"hours"(-8)); - auto east = new immutable SimpleTimeZone(dur!"hours"(8)); - - assert(west.utcToTZ(0) == -288_000_000_000L); - assert(east.utcToTZ(0) == 288_000_000_000L); - assert(west.utcToTZ(54_321_234_567_890L) == 54_033_234_567_890L); - - const cstz = west; - assert(cstz.utcToTZ(50002) == west.utcToTZ(50002)); - } - - - /++ - Takes the number of hnsecs (100 ns) since midnight, January 1st, 1 A.D. - in this time zone's time and converts it to UTC (i.e. std time). - - Params: - adjTime = The time in this time zone that needs to be adjusted to - UTC time. - +/ - override long tzToUTC(long adjTime) @safe const scope nothrow - { - return adjTime - _utcOffset.total!"hnsecs"; - } - - @safe unittest - { - auto west = new immutable SimpleTimeZone(dur!"hours"(-8)); - auto east = new immutable SimpleTimeZone(dur!"hours"(8)); - - assert(west.tzToUTC(-288_000_000_000L) == 0); - assert(east.tzToUTC(288_000_000_000L) == 0); - assert(west.tzToUTC(54_033_234_567_890L) == 54_321_234_567_890L); - - const cstz = west; - assert(cstz.tzToUTC(20005) == west.tzToUTC(20005)); - } - - - /++ - Returns utcOffset as a $(REF Duration, core,time). - - Params: - stdTime = The UTC time for which to get the offset from UTC for this - time zone. - +/ - override Duration utcOffsetAt(long stdTime) @safe const scope nothrow - { - return _utcOffset; - } - - - /++ - Params: - utcOffset = This time zone's offset from UTC with west of UTC being - negative (it is added to UTC to get the adjusted time). - stdName = The `stdName` for this time zone. - +/ - this(Duration utcOffset, string stdName = "") @safe immutable pure - { - // FIXME This probably needs to be changed to something like (-12 - 13). - import std.datetime.date : DateTimeException; - import std.exception : enforce; - enforce!DateTimeException(abs(utcOffset) < dur!"minutes"(1440), - "Offset from UTC must be within range (-24:00 - 24:00)."); - super("", stdName, ""); - this._utcOffset = utcOffset; - } - - @safe unittest - { - auto stz = new immutable SimpleTimeZone(dur!"hours"(-8), "PST"); - assert(stz.name == ""); - assert(stz.stdName == "PST"); - assert(stz.dstName == ""); - assert(stz.utcOffset == dur!"hours"(-8)); - } - - - /++ - The amount of time the offset from UTC is (negative is west of UTC, - positive is east). - +/ - @property Duration utcOffset() @safe const pure nothrow - { - return _utcOffset; - } - - -package: - - /+ - Returns a time zone as a string with an offset from UTC. - - Time zone offsets will be in the form +HHMM or -HHMM. - - Params: - utcOffset = The number of minutes offset from UTC (negative means - west). - +/ - static string toISOString(Duration utcOffset) @safe pure - { - import std.array : appender; - auto w = appender!string(); - w.reserve(5); - toISOString(w, utcOffset); - return w.data; - } - - // ditto - static void toISOString(W)(ref W writer, Duration utcOffset) - if (isOutputRange!(W, char)) - { - import std.datetime.date : DateTimeException; - import std.exception : enforce; - import std.format.write : formattedWrite; - immutable absOffset = abs(utcOffset); - enforce!DateTimeException(absOffset < dur!"minutes"(1440), - "Offset from UTC must be within range (-24:00 - 24:00)."); - int hours; - int minutes; - absOffset.split!("hours", "minutes")(hours, minutes); - formattedWrite( - writer, - utcOffset < Duration.zero ? "-%02d%02d" : "+%02d%02d", - hours, - minutes - ); - } - - @safe unittest - { - static string testSTZInvalid(Duration offset) - { - return SimpleTimeZone.toISOString(offset); - } - - import std.datetime.date : DateTimeException; - assertThrown!DateTimeException(testSTZInvalid(dur!"minutes"(1440))); - assertThrown!DateTimeException(testSTZInvalid(dur!"minutes"(-1440))); - - assert(toISOString(dur!"minutes"(0)) == "+0000"); - assert(toISOString(dur!"minutes"(1)) == "+0001"); - assert(toISOString(dur!"minutes"(10)) == "+0010"); - assert(toISOString(dur!"minutes"(59)) == "+0059"); - assert(toISOString(dur!"minutes"(60)) == "+0100"); - assert(toISOString(dur!"minutes"(90)) == "+0130"); - assert(toISOString(dur!"minutes"(120)) == "+0200"); - assert(toISOString(dur!"minutes"(480)) == "+0800"); - assert(toISOString(dur!"minutes"(1439)) == "+2359"); - - assert(toISOString(dur!"minutes"(-1)) == "-0001"); - assert(toISOString(dur!"minutes"(-10)) == "-0010"); - assert(toISOString(dur!"minutes"(-59)) == "-0059"); - assert(toISOString(dur!"minutes"(-60)) == "-0100"); - assert(toISOString(dur!"minutes"(-90)) == "-0130"); - assert(toISOString(dur!"minutes"(-120)) == "-0200"); - assert(toISOString(dur!"minutes"(-480)) == "-0800"); - assert(toISOString(dur!"minutes"(-1439)) == "-2359"); - } - - - /+ - Returns a time zone as a string with an offset from UTC. - - Time zone offsets will be in the form +HH:MM or -HH:MM. - - Params: - utcOffset = The number of minutes offset from UTC (negative means - west). - +/ - static string toISOExtString(Duration utcOffset) @safe pure - { - import std.array : appender; - auto w = appender!string(); - w.reserve(6); - toISOExtString(w, utcOffset); - return w.data; - } - - // ditto - static void toISOExtString(W)(ref W writer, Duration utcOffset) - { - import std.datetime.date : DateTimeException; - import std.format.write : formattedWrite; - import std.exception : enforce; - - immutable absOffset = abs(utcOffset); - enforce!DateTimeException(absOffset < dur!"minutes"(1440), - "Offset from UTC must be within range (-24:00 - 24:00)."); - int hours; - int minutes; - absOffset.split!("hours", "minutes")(hours, minutes); - formattedWrite( - writer, - utcOffset < Duration.zero ? "-%02d:%02d" : "+%02d:%02d", - hours, - minutes - ); - } - - @safe unittest - { - static string testSTZInvalid(Duration offset) - { - return SimpleTimeZone.toISOExtString(offset); - } - - import std.datetime.date : DateTimeException; - assertThrown!DateTimeException(testSTZInvalid(dur!"minutes"(1440))); - assertThrown!DateTimeException(testSTZInvalid(dur!"minutes"(-1440))); - - assert(toISOExtString(dur!"minutes"(0)) == "+00:00"); - assert(toISOExtString(dur!"minutes"(1)) == "+00:01"); - assert(toISOExtString(dur!"minutes"(10)) == "+00:10"); - assert(toISOExtString(dur!"minutes"(59)) == "+00:59"); - assert(toISOExtString(dur!"minutes"(60)) == "+01:00"); - assert(toISOExtString(dur!"minutes"(90)) == "+01:30"); - assert(toISOExtString(dur!"minutes"(120)) == "+02:00"); - assert(toISOExtString(dur!"minutes"(480)) == "+08:00"); - assert(toISOExtString(dur!"minutes"(1439)) == "+23:59"); - - assert(toISOExtString(dur!"minutes"(-1)) == "-00:01"); - assert(toISOExtString(dur!"minutes"(-10)) == "-00:10"); - assert(toISOExtString(dur!"minutes"(-59)) == "-00:59"); - assert(toISOExtString(dur!"minutes"(-60)) == "-01:00"); - assert(toISOExtString(dur!"minutes"(-90)) == "-01:30"); - assert(toISOExtString(dur!"minutes"(-120)) == "-02:00"); - assert(toISOExtString(dur!"minutes"(-480)) == "-08:00"); - assert(toISOExtString(dur!"minutes"(-1439)) == "-23:59"); - } - - - /+ - Takes a time zone as a string with an offset from UTC and returns a - $(LREF SimpleTimeZone) which matches. - - The accepted formats for time zone offsets are +HH, -HH, +HHMM, and - -HHMM. - - Params: - isoString = A string which represents a time zone in the ISO format. - +/ - static immutable(SimpleTimeZone) fromISOString(S)(S isoString) @safe pure - if (isSomeString!S) - { - import std.algorithm.searching : startsWith; - import std.conv : text, to, ConvException; - import std.datetime.date : DateTimeException; - import std.exception : enforce; - - auto whichSign = isoString.startsWith('-', '+'); - enforce!DateTimeException(whichSign > 0, text("Invalid ISO String ", isoString)); - - isoString = isoString[1 .. $]; - auto sign = whichSign == 1 ? -1 : 1; - int hours; - int minutes; - - try - { - // cast to int from uint is used because it checks for - // non digits without extra loops - if (isoString.length == 2) - { - hours = cast(int) to!uint(isoString); - } - else if (isoString.length == 4) - { - hours = cast(int) to!uint(isoString[0 .. 2]); - minutes = cast(int) to!uint(isoString[2 .. 4]); - } - else - { - throw new DateTimeException(text("Invalid ISO String ", isoString)); - } - } - catch (ConvException) - { - throw new DateTimeException(text("Invalid ISO String ", isoString)); - } - - enforce!DateTimeException(hours < 24 && minutes < 60, text("Invalid ISO String ", isoString)); - - return new immutable SimpleTimeZone(sign * (dur!"hours"(hours) + dur!"minutes"(minutes))); - } - - @safe unittest - { - import core.exception : AssertError; - import std.format : format; - - foreach (str; ["", "Z", "-", "+", "-:", "+:", "-1:", "+1:", "+1", "-1", - "-24:00", "+24:00", "-24", "+24", "-2400", "+2400", - "1", "+1", "-1", "+9", "-9", - "+1:0", "+01:0", "+1:00", "+01:000", "+01:60", - "-1:0", "-01:0", "-1:00", "-01:000", "-01:60", - "000", "00000", "0160", "-0160", - " +08:00", "+ 08:00", "+08 :00", "+08: 00", "+08:00 ", - " -08:00", "- 08:00", "-08 :00", "-08: 00", "-08:00 ", - " +0800", "+ 0800", "+08 00", "+08 00", "+0800 ", - " -0800", "- 0800", "-08 00", "-08 00", "-0800 ", - "+ab:cd", "+abcd", "+0Z:00", "+Z", "+00Z", - "-ab:cd", "+abcd", "-0Z:00", "-Z", "-00Z", - "01:00", "12:00", "23:59"]) - { - import std.datetime.date : DateTimeException; - assertThrown!DateTimeException(SimpleTimeZone.fromISOString(str), format("[%s]", str)); - } - - static void test(string str, Duration utcOffset, size_t line = __LINE__) - { - if (SimpleTimeZone.fromISOString(str).utcOffset != (new immutable SimpleTimeZone(utcOffset)).utcOffset) - throw new AssertError("unittest failure", __FILE__, line); - } - - test("+0000", Duration.zero); - test("+0001", minutes(1)); - test("+0010", minutes(10)); - test("+0059", minutes(59)); - test("+0100", hours(1)); - test("+0130", hours(1) + minutes(30)); - test("+0200", hours(2)); - test("+0800", hours(8)); - test("+2359", hours(23) + minutes(59)); - - test("-0001", minutes(-1)); - test("-0010", minutes(-10)); - test("-0059", minutes(-59)); - test("-0100", hours(-1)); - test("-0130", hours(-1) - minutes(30)); - test("-0200", hours(-2)); - test("-0800", hours(-8)); - test("-2359", hours(-23) - minutes(59)); - - test("+00", Duration.zero); - test("+01", hours(1)); - test("+02", hours(2)); - test("+12", hours(12)); - test("+23", hours(23)); - - test("-00", Duration.zero); - test("-01", hours(-1)); - test("-02", hours(-2)); - test("-12", hours(-12)); - test("-23", hours(-23)); - } - - @safe unittest - { - import core.exception : AssertError; - import std.format : format; - - static void test(scope const string isoString, int expectedOffset, size_t line = __LINE__) - { - auto stz = SimpleTimeZone.fromISOExtString(isoString); - if (stz.utcOffset != dur!"minutes"(expectedOffset)) - throw new AssertError(format("unittest failure: wrong offset [%s]", stz.utcOffset), __FILE__, line); - - auto result = SimpleTimeZone.toISOExtString(stz.utcOffset); - if (result != isoString) - throw new AssertError(format("unittest failure: [%s] != [%s]", result, isoString), __FILE__, line); - } - - test("+00:00", 0); - test("+00:01", 1); - test("+00:10", 10); - test("+00:59", 59); - test("+01:00", 60); - test("+01:30", 90); - test("+02:00", 120); - test("+08:00", 480); - test("+08:00", 480); - test("+23:59", 1439); - - test("-00:01", -1); - test("-00:10", -10); - test("-00:59", -59); - test("-01:00", -60); - test("-01:30", -90); - test("-02:00", -120); - test("-08:00", -480); - test("-08:00", -480); - test("-23:59", -1439); - } - - - /+ - Takes a time zone as a string with an offset from UTC and returns a - $(LREF SimpleTimeZone) which matches. - - The accepted formats for time zone offsets are +HH, -HH, +HH:MM, and - -HH:MM. - - Params: - isoExtString = A string which represents a time zone in the ISO format. - +/ - static immutable(SimpleTimeZone) fromISOExtString(S)(scope S isoExtString) @safe pure - if (isSomeString!S) - { - import std.algorithm.searching : startsWith; - import std.conv : ConvException, to; - import std.datetime.date : DateTimeException; - import std.exception : enforce; - import std.format : format; - import std.string : indexOf; - - auto whichSign = isoExtString.startsWith('-', '+'); - enforce!DateTimeException(whichSign > 0, format("Invalid ISO String: %s", isoExtString)); - auto sign = whichSign == 1 ? -1 : 1; - - isoExtString = isoExtString[1 .. $]; - enforce!DateTimeException(!isoExtString.empty, format("Invalid ISO String: %s", isoExtString)); - - immutable colon = isoExtString.indexOf(':'); - S hoursStr; - S minutesStr; - int hours, minutes; - - if (colon != -1) - { - hoursStr = isoExtString[0 .. colon]; - minutesStr = isoExtString[colon + 1 .. $]; - enforce!DateTimeException(minutesStr.length == 2, format("Invalid ISO String: %s", isoExtString)); - } - else - { - hoursStr = isoExtString; - } - - enforce!DateTimeException(hoursStr.length == 2, format("Invalid ISO String: %s", isoExtString)); - - try - { - // cast to int from uint is used because it checks for - // non digits without extra loops - hours = cast(int) to!uint(hoursStr); - minutes = cast(int) (minutesStr.empty ? 0 : to!uint(minutesStr)); - } - catch (ConvException) - { - throw new DateTimeException(format("Invalid ISO String: %s", isoExtString)); - } - - enforce!DateTimeException(hours < 24 && minutes < 60, format("Invalid ISO String: %s", isoExtString)); - - return new immutable SimpleTimeZone(sign * (dur!"hours"(hours) + dur!"minutes"(minutes))); - } - - @safe unittest - { - import core.exception : AssertError; - import std.format : format; - - foreach (str; ["", "Z", "-", "+", "-:", "+:", "-1:", "+1:", "+1", "-1", - "-24:00", "+24:00", "-24", "+24", "-2400", "-2400", - "1", "+1", "-1", "+9", "-9", - "+1:0", "+01:0", "+1:00", "+01:000", "+01:60", - "-1:0", "-01:0", "-1:00", "-01:000", "-01:60", - "000", "00000", "0160", "-0160", - " +08:00", "+ 08:00", "+08 :00", "+08: 00", "+08:00 ", - " -08:00", "- 08:00", "-08 :00", "-08: 00", "-08:00 ", - " +0800", "+ 0800", "+08 00", "+08 00", "+0800 ", - " -0800", "- 0800", "-08 00", "-08 00", "-0800 ", - "+ab:cd", "abcd", "+0Z:00", "+Z", "+00Z", - "-ab:cd", "abcd", "-0Z:00", "-Z", "-00Z", - "0100", "1200", "2359"]) - { - import std.datetime.date : DateTimeException; - assertThrown!DateTimeException(SimpleTimeZone.fromISOExtString(str), format("[%s]", str)); - } - - static void test(string str, Duration utcOffset, size_t line = __LINE__) - { - if (SimpleTimeZone.fromISOExtString(str).utcOffset != (new immutable SimpleTimeZone(utcOffset)).utcOffset) - throw new AssertError("unittest failure", __FILE__, line); - } - - test("+00:00", Duration.zero); - test("+00:01", minutes(1)); - test("+00:10", minutes(10)); - test("+00:59", minutes(59)); - test("+01:00", hours(1)); - test("+01:30", hours(1) + minutes(30)); - test("+02:00", hours(2)); - test("+08:00", hours(8)); - test("+23:59", hours(23) + minutes(59)); - - test("-00:01", minutes(-1)); - test("-00:10", minutes(-10)); - test("-00:59", minutes(-59)); - test("-01:00", hours(-1)); - test("-01:30", hours(-1) - minutes(30)); - test("-02:00", hours(-2)); - test("-08:00", hours(-8)); - test("-23:59", hours(-23) - minutes(59)); - - test("+00", Duration.zero); - test("+01", hours(1)); - test("+02", hours(2)); - test("+12", hours(12)); - test("+23", hours(23)); - - test("-00", Duration.zero); - test("-01", hours(-1)); - test("-02", hours(-2)); - test("-12", hours(-12)); - test("-23", hours(-23)); - } - - @safe unittest - { - import core.exception : AssertError; - import std.format : format; - - static void test(scope const string isoExtString, int expectedOffset, size_t line = __LINE__) - { - auto stz = SimpleTimeZone.fromISOExtString(isoExtString); - if (stz.utcOffset != dur!"minutes"(expectedOffset)) - throw new AssertError(format("unittest failure: wrong offset [%s]", stz.utcOffset), __FILE__, line); - - auto result = SimpleTimeZone.toISOExtString(stz.utcOffset); - if (result != isoExtString) - throw new AssertError(format("unittest failure: [%s] != [%s]", result, isoExtString), __FILE__, line); - } - - test("+00:00", 0); - test("+00:01", 1); - test("+00:10", 10); - test("+00:59", 59); - test("+01:00", 60); - test("+01:30", 90); - test("+02:00", 120); - test("+08:00", 480); - test("+08:00", 480); - test("+23:59", 1439); - - test("-00:01", -1); - test("-00:10", -10); - test("-00:59", -59); - test("-01:00", -60); - test("-01:30", -90); - test("-02:00", -120); - test("-08:00", -480); - test("-08:00", -480); - test("-23:59", -1439); - } - - -private: - - immutable Duration _utcOffset; -} - - -/++ - Represents a time zone from a TZ Database time zone file. Files from the TZ - Database are how Posix systems hold their time zone information. - Unfortunately, Windows does not use the TZ Database. To use the TZ Database, - use `PosixTimeZone` (which reads its information from the TZ Database - files on disk) on Windows by providing the TZ Database files and telling - `PosixTimeZone.getTimeZone` where the directory holding them is. - - To get a `PosixTimeZone`, call `PosixTimeZone.getTimeZone` - (which allows specifying the location the time zone files). - - Note: - Unless your system's local time zone deals with leap seconds (which is - highly unlikely), then the only way to get a time zone which - takes leap seconds into account is to use `PosixTimeZone` with a - time zone whose name starts with "right/". Those time zone files do - include leap seconds, and `PosixTimeZone` will take them into account - (though posix systems which use a "right/" time zone as their local time - zone will $(I not) take leap seconds into account even though they're - in the file). - - See_Also: - $(HTTP www.iana.org/time-zones, Home of the TZ Database files)
- $(HTTP en.wikipedia.org/wiki/Tz_database, Wikipedia entry on TZ Database)
- $(HTTP en.wikipedia.org/wiki/List_of_tz_database_time_zones, List of Time - Zones) - +/ -final class PosixTimeZone : TimeZone -{ - import std.algorithm.searching : countUntil, canFind, startsWith; - import std.file : isDir, isFile, exists, dirEntries, SpanMode, DirEntry; - import std.path : extension; - import std.stdio : File; - import std.string : strip, representation; - import std.traits : isArray, isSomeChar; -public: - - /++ - Whether this time zone has Daylight Savings Time at any point in time. - Note that for some time zone types it may not have DST for current - dates but will still return true for `hasDST` because the time zone - did at some point have DST. - +/ - @property override bool hasDST() @safe const nothrow - { - return _hasDST; - } - - - /++ - Takes the number of hnsecs (100 ns) since midnight, January 1st, 1 A.D. - in UTC time (i.e. std time) and returns whether DST is in effect in this - time zone at the given point in time. - - Params: - stdTime = The UTC time that needs to be checked for DST in this time - zone. - +/ - override bool dstInEffect(long stdTime) @safe const scope nothrow - { - assert(!_transitions.empty); - - immutable unixTime = stdTimeToUnixTime(stdTime); - immutable found = countUntil!"b < a.timeT"(_transitions, unixTime); - - if (found == -1) - return _transitions.back.ttInfo.isDST; - - immutable transition = found == 0 ? _transitions[0] : _transitions[found - 1]; - - return transition.ttInfo.isDST; - } - - - /++ - Takes the number of hnsecs (100 ns) since midnight, January 1st, 1 A.D. - in UTC time (i.e. std time) and converts it to this time zone's time. - - Params: - stdTime = The UTC time that needs to be adjusted to this time zone's - time. - +/ - override long utcToTZ(long stdTime) @safe const scope nothrow - { - assert(!_transitions.empty); - - immutable leapSecs = calculateLeapSeconds(stdTime); - immutable unixTime = stdTimeToUnixTime(stdTime); - immutable found = countUntil!"b < a.timeT"(_transitions, unixTime); - - if (found == -1) - return stdTime + convert!("seconds", "hnsecs")(_transitions.back.ttInfo.utcOffset + leapSecs); - - immutable transition = found == 0 ? _transitions[0] : _transitions[found - 1]; - - return stdTime + convert!("seconds", "hnsecs")(transition.ttInfo.utcOffset + leapSecs); - } - - - /++ - Takes the number of hnsecs (100 ns) since midnight, January 1st, 1 A.D. - in this time zone's time and converts it to UTC (i.e. std time). - - Params: - adjTime = The time in this time zone that needs to be adjusted to - UTC time. - +/ - override long tzToUTC(long adjTime) @safe const scope nothrow - { - assert(!_transitions.empty, "UTC offset's not available"); - - immutable leapSecs = calculateLeapSeconds(adjTime); - time_t unixTime = stdTimeToUnixTime(adjTime); - immutable past = unixTime - convert!("days", "seconds")(1); - immutable future = unixTime + convert!("days", "seconds")(1); - - immutable pastFound = countUntil!"b < a.timeT"(_transitions, past); - - if (pastFound == -1) - return adjTime - convert!("seconds", "hnsecs")(_transitions.back.ttInfo.utcOffset + leapSecs); - - immutable futureFound = countUntil!"b < a.timeT"(_transitions[pastFound .. $], future); - immutable pastTrans = pastFound == 0 ? _transitions[0] : _transitions[pastFound - 1]; - - if (futureFound == 0) - return adjTime - convert!("seconds", "hnsecs")(pastTrans.ttInfo.utcOffset + leapSecs); - - immutable futureTrans = futureFound == -1 ? _transitions.back - : _transitions[pastFound + futureFound - 1]; - immutable pastOffset = pastTrans.ttInfo.utcOffset; - - if (pastOffset < futureTrans.ttInfo.utcOffset) - unixTime -= convert!("hours", "seconds")(1); - - immutable found = countUntil!"b < a.timeT"(_transitions[pastFound .. $], unixTime - pastOffset); - - if (found == -1) - return adjTime - convert!("seconds", "hnsecs")(_transitions.back.ttInfo.utcOffset + leapSecs); - - immutable transition = found == 0 ? pastTrans : _transitions[pastFound + found - 1]; - - return adjTime - convert!("seconds", "hnsecs")(transition.ttInfo.utcOffset + leapSecs); - } - - - version (StdDdoc) - { - /++ - The default directory where the TZ Database files are stored. It's - empty for Windows, since Windows doesn't have them. You can also use - the TZDatabaseDir version to pass an arbitrary path at compile-time, - rather than hard-coding it here. Android concatenates all time zone - data into a single file called tzdata and stores it in the directory - below. - +/ - enum defaultTZDatabaseDir = ""; - } - else version (TZDatabaseDir) - { - import std.string : strip; - enum defaultTZDatabaseDir = strip(import("TZDatabaseDirFile")); - } - else version (Android) - { - enum defaultTZDatabaseDir = "/system/usr/share/zoneinfo/"; - } - else version (Solaris) - { - enum defaultTZDatabaseDir = "/usr/share/lib/zoneinfo/"; - } - else version (Posix) - { - enum defaultTZDatabaseDir = "/usr/share/zoneinfo/"; - } - else version (Windows) - { - enum defaultTZDatabaseDir = ""; - } - - - /++ - Returns a $(LREF TimeZone) with the give name per the TZ Database. The - time zone information is fetched from the TZ Database time zone files in - the given directory. - - See_Also: - $(HTTP en.wikipedia.org/wiki/Tz_database, Wikipedia entry on TZ - Database)
- $(HTTP en.wikipedia.org/wiki/List_of_tz_database_time_zones, List of - Time Zones) - - Params: - name = The TZ Database name of the desired time zone - tzDatabaseDir = The directory where the TZ Database files are - located. Because these files are not located on - Windows systems, provide them - and give their location here to - use $(LREF PosixTimeZone)s. - - Throws: - $(REF DateTimeException,std,datetime,date) if the given time zone - could not be found or `FileException` if the TZ Database file - could not be opened. - +/ - // TODO make it possible for tzDatabaseDir to be gzipped tar file rather than an uncompressed - // directory. - static immutable(PosixTimeZone) getTimeZone(string name, string tzDatabaseDir = defaultTZDatabaseDir) @trusted - { - import std.algorithm.sorting : sort; - import std.conv : to; - import std.datetime.date : DateTimeException; - import std.exception : enforce; - import std.format : format; - import std.path : asNormalizedPath, chainPath; - import std.range : retro; - - name = strip(name); - - enforce(tzDatabaseDir.exists(), new DateTimeException(format("Directory %s does not exist.", tzDatabaseDir))); - enforce(tzDatabaseDir.isDir, new DateTimeException(format("%s is not a directory.", tzDatabaseDir))); - - version (Android) - { - auto tzfileOffset = name in tzdataIndex(tzDatabaseDir); - enforce(tzfileOffset, new DateTimeException(format("The time zone %s is not listed.", name))); - string tzFilename = separate_index ? "zoneinfo.dat" : "tzdata"; - const file = asNormalizedPath(chainPath(tzDatabaseDir, tzFilename)).to!string; - } - else - const file = asNormalizedPath(chainPath(tzDatabaseDir, name)).to!string; - - enforce(file.exists(), new DateTimeException(format("File %s does not exist.", file))); - enforce(file.isFile, new DateTimeException(format("%s is not a file.", file))); - - auto tzFile = File(file); - version (Android) tzFile.seek(*tzfileOffset); - immutable gmtZone = name.representation().canFind("GMT"); - - import std.datetime.date : DateTimeException; - try - { - _enforceValidTZFile(readVal!(char[])(tzFile, 4) == "TZif"); - - immutable char tzFileVersion = readVal!char(tzFile); - _enforceValidTZFile(tzFileVersion == '\0' || tzFileVersion == '2' || tzFileVersion == '3'); - - { - auto zeroBlock = readVal!(ubyte[])(tzFile, 15); - bool allZeroes = true; - - foreach (val; zeroBlock) - { - if (val != 0) - { - allZeroes = false; - break; - } - } - - _enforceValidTZFile(allZeroes); - } - - - // The number of UTC/local indicators stored in the file. - auto tzh_ttisgmtcnt = readVal!int(tzFile); - - // The number of standard/wall indicators stored in the file. - auto tzh_ttisstdcnt = readVal!int(tzFile); - - // The number of leap seconds for which data is stored in the file. - auto tzh_leapcnt = readVal!int(tzFile); - - // The number of "transition times" for which data is stored in the file. - auto tzh_timecnt = readVal!int(tzFile); - - // The number of "local time types" for which data is stored in the file (must not be zero). - auto tzh_typecnt = readVal!int(tzFile); - _enforceValidTZFile(tzh_typecnt != 0); - - // The number of characters of "timezone abbreviation strings" stored in the file. - auto tzh_charcnt = readVal!int(tzFile); - - // time_ts where DST transitions occur. - auto transitionTimeTs = new long[](tzh_timecnt); - foreach (ref transition; transitionTimeTs) - transition = readVal!int(tzFile); - - // Indices into ttinfo structs indicating the changes - // to be made at the corresponding DST transition. - auto ttInfoIndices = new ubyte[](tzh_timecnt); - foreach (ref ttInfoIndex; ttInfoIndices) - ttInfoIndex = readVal!ubyte(tzFile); - - // ttinfos which give info on DST transitions. - auto tempTTInfos = new TempTTInfo[](tzh_typecnt); - foreach (ref ttInfo; tempTTInfos) - ttInfo = readVal!TempTTInfo(tzFile); - - // The array of time zone abbreviation characters. - auto tzAbbrevChars = readVal!(char[])(tzFile, tzh_charcnt); - - auto leapSeconds = new LeapSecond[](tzh_leapcnt); - foreach (ref leapSecond; leapSeconds) - { - // The time_t when the leap second occurs. - auto timeT = readVal!int(tzFile); - - // The total number of leap seconds to be applied after - // the corresponding leap second. - auto total = readVal!int(tzFile); - - leapSecond = LeapSecond(timeT, total); - } - - // Indicate whether each corresponding DST transition were specified - // in standard time or wall clock time. - auto transitionIsStd = new bool[](tzh_ttisstdcnt); - foreach (ref isStd; transitionIsStd) - isStd = readVal!bool(tzFile); - - // Indicate whether each corresponding DST transition associated with - // local time types are specified in UTC or local time. - auto transitionInUTC = new bool[](tzh_ttisgmtcnt); - foreach (ref inUTC; transitionInUTC) - inUTC = readVal!bool(tzFile); - - _enforceValidTZFile(!tzFile.eof); - - // If version 2 or 3, the information is duplicated in 64-bit. - if (tzFileVersion == '2' || tzFileVersion == '3') - { - _enforceValidTZFile(readVal!(char[])(tzFile, 4) == "TZif"); - - immutable char tzFileVersion2 = readVal!(char)(tzFile); - _enforceValidTZFile(tzFileVersion2 == '2' || tzFileVersion2 == '3'); - - { - auto zeroBlock = readVal!(ubyte[])(tzFile, 15); - bool allZeroes = true; - - foreach (val; zeroBlock) - { - if (val != 0) - { - allZeroes = false; - break; - } - } - - _enforceValidTZFile(allZeroes); - } - - - // The number of UTC/local indicators stored in the file. - tzh_ttisgmtcnt = readVal!int(tzFile); - - // The number of standard/wall indicators stored in the file. - tzh_ttisstdcnt = readVal!int(tzFile); - - // The number of leap seconds for which data is stored in the file. - tzh_leapcnt = readVal!int(tzFile); - - // The number of "transition times" for which data is stored in the file. - tzh_timecnt = readVal!int(tzFile); - - // The number of "local time types" for which data is stored in the file (must not be zero). - tzh_typecnt = readVal!int(tzFile); - _enforceValidTZFile(tzh_typecnt != 0); - - // The number of characters of "timezone abbreviation strings" stored in the file. - tzh_charcnt = readVal!int(tzFile); - - // time_ts where DST transitions occur. - transitionTimeTs = new long[](tzh_timecnt); - foreach (ref transition; transitionTimeTs) - transition = readVal!long(tzFile); - - // Indices into ttinfo structs indicating the changes - // to be made at the corresponding DST transition. - ttInfoIndices = new ubyte[](tzh_timecnt); - foreach (ref ttInfoIndex; ttInfoIndices) - ttInfoIndex = readVal!ubyte(tzFile); - - // ttinfos which give info on DST transitions. - tempTTInfos = new TempTTInfo[](tzh_typecnt); - foreach (ref ttInfo; tempTTInfos) - ttInfo = readVal!TempTTInfo(tzFile); - - // The array of time zone abbreviation characters. - tzAbbrevChars = readVal!(char[])(tzFile, tzh_charcnt); - - leapSeconds = new LeapSecond[](tzh_leapcnt); - foreach (ref leapSecond; leapSeconds) - { - // The time_t when the leap second occurs. - auto timeT = readVal!long(tzFile); - - // The total number of leap seconds to be applied after - // the corresponding leap second. - auto total = readVal!int(tzFile); - - leapSecond = LeapSecond(timeT, total); - } - - // Indicate whether each corresponding DST transition were specified - // in standard time or wall clock time. - transitionIsStd = new bool[](tzh_ttisstdcnt); - foreach (ref isStd; transitionIsStd) - isStd = readVal!bool(tzFile); - - // Indicate whether each corresponding DST transition associated with - // local time types are specified in UTC or local time. - transitionInUTC = new bool[](tzh_ttisgmtcnt); - foreach (ref inUTC; transitionInUTC) - inUTC = readVal!bool(tzFile); - } - - _enforceValidTZFile(tzFile.readln().strip().empty); - - cast(void) tzFile.readln(); - - version (Android) - { - // Android uses a single file for all timezone data, so the file - // doesn't end here. - } - else - { - _enforceValidTZFile(tzFile.readln().strip().empty); - _enforceValidTZFile(tzFile.eof); - } - - - auto transitionTypes = new TransitionType*[](tempTTInfos.length); - - foreach (i, ref ttype; transitionTypes) - { - bool isStd = false; - - if (i < transitionIsStd.length && !transitionIsStd.empty) - isStd = transitionIsStd[i]; - - bool inUTC = false; - - if (i < transitionInUTC.length && !transitionInUTC.empty) - inUTC = transitionInUTC[i]; - - ttype = new TransitionType(isStd, inUTC); - } - - auto ttInfos = new immutable(TTInfo)*[](tempTTInfos.length); - foreach (i, ref ttInfo; ttInfos) - { - auto tempTTInfo = tempTTInfos[i]; - - if (gmtZone) - tempTTInfo.tt_gmtoff = -tempTTInfo.tt_gmtoff; - - auto abbrevChars = tzAbbrevChars[tempTTInfo.tt_abbrind .. $]; - string abbrev = abbrevChars[0 .. abbrevChars.countUntil('\0')].idup; - - ttInfo = new immutable(TTInfo)(tempTTInfos[i], abbrev); - } - - auto tempTransitions = new TempTransition[](transitionTimeTs.length); - foreach (i, ref tempTransition; tempTransitions) - { - immutable ttiIndex = ttInfoIndices[i]; - auto transitionTimeT = transitionTimeTs[i]; - auto ttype = transitionTypes[ttiIndex]; - auto ttInfo = ttInfos[ttiIndex]; - - tempTransition = TempTransition(transitionTimeT, ttInfo, ttype); - } - - if (tempTransitions.empty) - { - _enforceValidTZFile(ttInfos.length == 1 && transitionTypes.length == 1); - tempTransitions ~= TempTransition(0, ttInfos[0], transitionTypes[0]); - } - - sort!"a.timeT < b.timeT"(tempTransitions); - sort!"a.timeT < b.timeT"(leapSeconds); - - auto transitions = new Transition[](tempTransitions.length); - foreach (i, ref transition; transitions) - { - auto tempTransition = tempTransitions[i]; - auto transitionTimeT = tempTransition.timeT; - auto ttInfo = tempTransition.ttInfo; - - _enforceValidTZFile(i == 0 || transitionTimeT > tempTransitions[i - 1].timeT); - - transition = Transition(transitionTimeT, ttInfo); - } - - string stdName; - string dstName; - bool hasDST = false; - - foreach (transition; retro(transitions)) - { - auto ttInfo = transition.ttInfo; - - if (ttInfo.isDST) - { - if (dstName.empty) - dstName = ttInfo.abbrev; - hasDST = true; - } - else - { - if (stdName.empty) - stdName = ttInfo.abbrev; - } - - if (!stdName.empty && !dstName.empty) - break; - } - - return new immutable PosixTimeZone(transitions.idup, leapSeconds.idup, name, stdName, dstName, hasDST); - } - catch (DateTimeException dte) - throw dte; - catch (Exception e) - throw new DateTimeException("Not a valid TZ data file", __FILE__, __LINE__, e); - } - - /// - @safe unittest - { - version (Posix) - { - auto tz = PosixTimeZone.getTimeZone("America/Los_Angeles"); - - assert(tz.name == "America/Los_Angeles"); - assert(tz.stdName == "PST"); - assert(tz.dstName == "PDT"); - } - } - - /++ - Returns a list of the names of the time zones installed on the system. - - Providing a sub-name narrows down the list of time zones (which - can number in the thousands). For example, - passing in "America" as the sub-name returns only the time zones which - begin with "America". - - Params: - subName = The first part of the desired time zones. - tzDatabaseDir = The directory where the TZ Database files are - located. - - Throws: - `FileException` if it fails to read from disk. - +/ - static string[] getInstalledTZNames(string subName = "", string tzDatabaseDir = defaultTZDatabaseDir) @safe - { - import std.algorithm.sorting : sort; - import std.array : appender; - import std.exception : enforce; - import std.format : format; - - version (Posix) - subName = strip(subName); - else version (Windows) - { - import std.array : replace; - import std.path : dirSeparator; - subName = replace(strip(subName), "/", dirSeparator); - } - - import std.datetime.date : DateTimeException; - enforce(tzDatabaseDir.exists(), new DateTimeException(format("Directory %s does not exist.", tzDatabaseDir))); - enforce(tzDatabaseDir.isDir, new DateTimeException(format("%s is not a directory.", tzDatabaseDir))); - - auto timezones = appender!(string[])(); - - version (Android) - { - import std.algorithm.iteration : filter; - import std.algorithm.mutation : copy; - - const index = () @trusted { return tzdataIndex(tzDatabaseDir); }(); - index.byKey.filter!(a => a.startsWith(subName)).copy(timezones); - } - else - { - import std.path : baseName; - // dirEntries is @system because it uses a DirIterator with a - // RefCounted variable, but here, no references to the payload is - // escaped to the outside, so this should be @trusted - () @trusted { - foreach (DirEntry de; dirEntries(tzDatabaseDir, SpanMode.depth)) - { - if (de.isFile) - { - auto tzName = de.name[tzDatabaseDir.length .. $]; - - if (!tzName.extension().empty || - !tzName.startsWith(subName) || - baseName(tzName) == "leapseconds" || - tzName == "+VERSION" || - tzName == "SECURITY") - { - continue; - } - - timezones.put(tzName); - } - } - }(); - } - - sort(timezones.data); - - return timezones.data; - } - - version (Posix) @system unittest - { - import std.exception : assertNotThrown; - import std.stdio : writefln; - static void testPTZSuccess(string tzName) - { - scope(failure) writefln("TZName which threw: %s", tzName); - - PosixTimeZone.getTimeZone(tzName); - } - - static void testPTZFailure(string tzName) - { - scope(success) writefln("TZName which was supposed to throw: %s", tzName); - - PosixTimeZone.getTimeZone(tzName); - } - - auto tzNames = getInstalledTZNames(); - - import std.datetime.date : DateTimeException; - foreach (tzName; tzNames) - assertNotThrown!DateTimeException(testPTZSuccess(tzName)); - - // No timezone directories on Android, just a single tzdata file - version (Android) - {} - else - { - foreach (DirEntry de; dirEntries(defaultTZDatabaseDir, SpanMode.depth)) - { - if (de.isFile) - { - auto tzName = de.name[defaultTZDatabaseDir.length .. $]; - - if (!canFind(tzNames, tzName)) - assertThrown!DateTimeException(testPTZFailure(tzName)); - } - } - } - } - - -private: - - /+ - Holds information on when a time transition occures (usually a - transition to or from DST) as well as a pointer to the `TTInfo` which - holds information on the utc offset past the transition. - +/ - struct Transition - { - this(long timeT, immutable (TTInfo)* ttInfo) @safe pure - { - this.timeT = timeT; - this.ttInfo = ttInfo; - } - - long timeT; - immutable (TTInfo)* ttInfo; - } - - - /+ - Holds information on when a leap second occurs. - +/ - struct LeapSecond - { - this(long timeT, int total) @safe pure - { - this.timeT = timeT; - this.total = total; - } - - long timeT; - int total; - } - - /+ - Holds information on the utc offset after a transition as well as - whether DST is in effect after that transition. - +/ - struct TTInfo - { - this(scope const TempTTInfo tempTTInfo, string abbrev) @safe immutable pure - { - utcOffset = tempTTInfo.tt_gmtoff; - isDST = tempTTInfo.tt_isdst; - this.abbrev = abbrev; - } - - immutable int utcOffset; // Offset from UTC. - immutable bool isDST; // Whether DST is in effect. - immutable string abbrev; // The current abbreviation for the time zone. - } - - - /+ - Struct used to hold information relating to `TTInfo` while organizing - the time zone information prior to putting it in its final form. - +/ - struct TempTTInfo - { - this(int gmtOff, bool isDST, ubyte abbrInd) @safe pure - { - tt_gmtoff = gmtOff; - tt_isdst = isDST; - tt_abbrind = abbrInd; - } - - int tt_gmtoff; - bool tt_isdst; - ubyte tt_abbrind; - } - - - /+ - Struct used to hold information relating to `Transition` while - organizing the time zone information prior to putting it in its final - form. - +/ - struct TempTransition - { - this(long timeT, immutable (TTInfo)* ttInfo, TransitionType* ttype) @safe pure - { - this.timeT = timeT; - this.ttInfo = ttInfo; - this.ttype = ttype; - } - - long timeT; - immutable (TTInfo)* ttInfo; - TransitionType* ttype; - } - - - /+ - Struct used to hold information relating to `Transition` and - `TTInfo` while organizing the time zone information prior to putting - it in its final form. - +/ - struct TransitionType - { - this(bool isStd, bool inUTC) @safe pure - { - this.isStd = isStd; - this.inUTC = inUTC; - } - - // Whether the transition is in std time (as opposed to wall clock time). - bool isStd; - - // Whether the transition is in UTC (as opposed to local time). - bool inUTC; - } - - - /+ - Reads an int from a TZ file. - +/ - static T readVal(T)(ref File tzFile) @trusted - if ((isIntegral!T || isSomeChar!T) || is(immutable T == immutable bool)) - { - import std.bitmanip : bigEndianToNative; - T[1] buff; - - _enforceValidTZFile(!tzFile.eof); - tzFile.rawRead(buff); - - return bigEndianToNative!T(cast(ubyte[T.sizeof]) buff); - } - - /+ - Reads an array of values from a TZ file. - +/ - static T readVal(T)(ref File tzFile, size_t length) @trusted - if (isArray!T) - { - auto buff = new T(length); - - _enforceValidTZFile(!tzFile.eof); - tzFile.rawRead(buff); - - return buff; - } - - - /+ - Reads a `TempTTInfo` from a TZ file. - +/ - static T readVal(T)(ref File tzFile) @safe - if (is(T == TempTTInfo)) - { - return TempTTInfo(readVal!int(tzFile), - readVal!bool(tzFile), - readVal!ubyte(tzFile)); - } - - - /+ - Throws: - $(REF DateTimeException,std,datetime,date) if `result` is false. - +/ - static void _enforceValidTZFile(bool result, size_t line = __LINE__) @safe pure - { - import std.datetime.date : DateTimeException; - if (!result) - throw new DateTimeException("Not a valid tzdata file.", __FILE__, line); - } - - - int calculateLeapSeconds(long stdTime) @safe const scope pure nothrow - { - if (_leapSeconds.empty) - return 0; - - immutable unixTime = stdTimeToUnixTime(stdTime); - - if (_leapSeconds.front.timeT >= unixTime) - return 0; - - immutable found = countUntil!"b < a.timeT"(_leapSeconds, unixTime); - - if (found == -1) - return _leapSeconds.back.total; - - immutable leapSecond = found == 0 ? _leapSeconds[0] : _leapSeconds[found - 1]; - - return leapSecond.total; - } - - - this(immutable Transition[] transitions, - immutable LeapSecond[] leapSeconds, - string name, - string stdName, - string dstName, - bool hasDST) @safe immutable pure - { - if (dstName.empty && !stdName.empty) - dstName = stdName; - else if (stdName.empty && !dstName.empty) - stdName = dstName; - - super(name, stdName, dstName); - - if (!transitions.empty) - { - foreach (i, transition; transitions[0 .. $-1]) - _enforceValidTZFile(transition.timeT < transitions[i + 1].timeT); - } - - foreach (i, leapSecond; leapSeconds) - _enforceValidTZFile(i == leapSeconds.length - 1 || leapSecond.timeT < leapSeconds[i + 1].timeT); - - _transitions = transitions; - _leapSeconds = leapSeconds; - _hasDST = hasDST; - } - - // Android concatenates the usual timezone directories into a single file, - // tzdata, along with an index to jump to each timezone's offset. In older - // versions of Android, the index was stored in a separate file, zoneinfo.idx, - // whereas now it's stored at the beginning of tzdata. - version (Android) - { - // Keep track of whether there's a separate index, zoneinfo.idx. Only - // check this after calling tzdataIndex, as it's initialized there. - static shared bool separate_index; - - // Extracts the name of each time zone and the offset where its data is - // located in the tzdata file from the index and caches it for later. - static const(uint[string]) tzdataIndex(string tzDir) - { - import std.concurrency : initOnce; - - __gshared uint[string] _tzIndex; - - // _tzIndex is initialized once and then shared across all threads. - initOnce!_tzIndex( - { - import std.conv : to; - import std.datetime.date : DateTimeException; - import std.format : format; - import std.path : asNormalizedPath, chainPath; - - enum indexEntrySize = 52; - const combinedFile = asNormalizedPath(chainPath(tzDir, "tzdata")).to!string; - const indexFile = asNormalizedPath(chainPath(tzDir, "zoneinfo.idx")).to!string; - File tzFile; - uint indexEntries, dataOffset; - uint[string] initIndex; - - // Check for the combined file tzdata, which stores the index - // and the time zone data together. - if (combinedFile.exists() && combinedFile.isFile) - { - tzFile = File(combinedFile); - _enforceValidTZFile(readVal!(char[])(tzFile, 6) == "tzdata"); - auto tzDataVersion = readVal!(char[])(tzFile, 6); - _enforceValidTZFile(tzDataVersion[5] == '\0'); - - uint indexOffset = readVal!uint(tzFile); - dataOffset = readVal!uint(tzFile); - readVal!uint(tzFile); - - indexEntries = (dataOffset - indexOffset) / indexEntrySize; - separate_index = false; - } - else if (indexFile.exists() && indexFile.isFile) - { - tzFile = File(indexFile); - indexEntries = to!uint(tzFile.size/indexEntrySize); - separate_index = true; - } - else - { - throw new DateTimeException(format("Both timezone files %s and %s do not exist.", - combinedFile, indexFile)); - } - - foreach (_; 0 .. indexEntries) - { - string tzName = to!string(readVal!(char[])(tzFile, 40).ptr); - uint tzOffset = readVal!uint(tzFile); - readVal!(uint[])(tzFile, 2); - initIndex[tzName] = dataOffset + tzOffset; - } - initIndex.rehash; - return initIndex; - }()); - return _tzIndex; - } - } - - // List of times when the utc offset changes. - immutable Transition[] _transitions; - - // List of leap second occurrences. - immutable LeapSecond[] _leapSeconds; - - // Whether DST is in effect for this time zone at any point in time. - immutable bool _hasDST; -} - - -version (StdDdoc) -{ - /++ - $(BLUE This class is Windows-Only.) - - Represents a time zone from the Windows registry. Unfortunately, Windows - does not use the TZ Database. To use the TZ Database, use - $(LREF PosixTimeZone) (which reads its information from the TZ Database - files on disk) on Windows by providing the TZ Database files and telling - `PosixTimeZone.getTimeZone` where the directory holding them is. - - The TZ Database files and Windows' time zone information frequently - do not match. Windows has many errors with regards to when DST switches - occur (especially for historical dates). Also, the TZ Database files - include far more time zones than Windows does. So, for accurate - time zone information, use the TZ Database files with - $(LREF PosixTimeZone) rather than `WindowsTimeZone`. However, because - `WindowsTimeZone` uses Windows system calls to deal with the time, - it's far more likely to match the behavior of other Windows programs. - Be aware of the differences when selecting a method. - - `WindowsTimeZone` does not exist on Posix systems. - - To get a `WindowsTimeZone`, call `WindowsTimeZone.getTimeZone`. - - See_Also: - $(HTTP www.iana.org/time-zones, Home of the TZ Database files) - +/ - final class WindowsTimeZone : TimeZone - { - public: - - /++ - Whether this time zone has Daylight Savings Time at any point in - time. Note that for some time zone types it may not have DST for - current dates but will still return true for `hasDST` because the - time zone did at some point have DST. - +/ - @property override bool hasDST() @safe const scope nothrow; - - - /++ - Takes the number of hnsecs (100 ns) since midnight, January 1st, - 1 A.D. in UTC time (i.e. std time) and returns whether DST is in - effect in this time zone at the given point in time. - - Params: - stdTime = The UTC time that needs to be checked for DST in this - time zone. - +/ - override bool dstInEffect(long stdTime) @safe const scope nothrow; - - - /++ - Takes the number of hnsecs (100 ns) since midnight, January 1st, - 1 A.D. in UTC time (i.e. std time) and converts it to this time - zone's time. - - Params: - stdTime = The UTC time that needs to be adjusted to this time - zone's time. - +/ - override long utcToTZ(long stdTime) @safe const scope nothrow; - - - /++ - Takes the number of hnsecs (100 ns) since midnight, January 1st, - 1 A.D. in this time zone's time and converts it to UTC (i.e. std - time). - - Params: - adjTime = The time in this time zone that needs to be adjusted - to UTC time. - +/ - override long tzToUTC(long adjTime) @safe const scope nothrow; - - - /++ - Returns a $(LREF TimeZone) with the given name per the Windows time - zone names. The time zone information is fetched from the Windows - registry. - - See_Also: - $(HTTP en.wikipedia.org/wiki/Tz_database, Wikipedia entry on TZ - Database)
- $(HTTP en.wikipedia.org/wiki/List_of_tz_database_time_zones, List - of Time Zones) - - Params: - name = The TZ Database name of the desired time zone. - - Throws: - $(REF DateTimeException,std,datetime,date) if the given time - zone could not be found. - - Example: - -------------------- - auto tz = WindowsTimeZone.getTimeZone("Pacific Standard Time"); - -------------------- - +/ - static immutable(WindowsTimeZone) getTimeZone(string name) @safe; - - - /++ - Returns a list of the names of the time zones installed on the - system. The list returned by WindowsTimeZone contains the Windows - TZ names, not the TZ Database names. However, - `TimeZone.getinstalledTZNames` will return the TZ Database names - which are equivalent to the Windows TZ names. - +/ - static string[] getInstalledTZNames() @safe; - - private: - - version (Windows) - {} - else - alias TIME_ZONE_INFORMATION = void*; - - static bool _dstInEffect(const scope TIME_ZONE_INFORMATION* tzInfo, long stdTime) nothrow; - static long _utcToTZ(const scope TIME_ZONE_INFORMATION* tzInfo, long stdTime, bool hasDST) nothrow; - static long _tzToUTC(const scope TIME_ZONE_INFORMATION* tzInfo, long adjTime, bool hasDST) nothrow; - - this() immutable pure - { - super("", "", ""); - } - } - -} -else version (Windows) -{ - final class WindowsTimeZone : TimeZone - { - import std.algorithm.sorting : sort; - import std.array : appender; - import std.conv : to; - import std.format : format; - - public: - - @property override bool hasDST() @safe const scope nothrow - { - return _tzInfo.DaylightDate.wMonth != 0; - } - - - override bool dstInEffect(long stdTime) @safe const scope nothrow - { - return _dstInEffect(&_tzInfo, stdTime); - } - - - override long utcToTZ(long stdTime) @safe const scope nothrow - { - return _utcToTZ(&_tzInfo, stdTime, hasDST); - } - - - override long tzToUTC(long adjTime) @safe const scope nothrow - { - return _tzToUTC(&_tzInfo, adjTime, hasDST); - } - - - static immutable(WindowsTimeZone) getTimeZone(string name) @trusted - { - scope baseKey = Registry.localMachine.getKey(`Software\Microsoft\Windows NT\CurrentVersion\Time Zones`); - - foreach (tzKeyName; baseKey.keyNames) - { - if (tzKeyName != name) - continue; - - scope tzKey = baseKey.getKey(tzKeyName); - - scope stdVal = tzKey.getValue("Std"); - auto stdName = stdVal.value_SZ; - - scope dstVal = tzKey.getValue("Dlt"); - auto dstName = dstVal.value_SZ; - - scope tziVal = tzKey.getValue("TZI"); - auto binVal = tziVal.value_BINARY; - assert(binVal.length == REG_TZI_FORMAT.sizeof, - "Unexpected size while getTimeZone with name " ~ name); - auto tziFmt = cast(REG_TZI_FORMAT*) binVal.ptr; - - TIME_ZONE_INFORMATION tzInfo; - - auto wstdName = stdName.to!wstring; - auto wdstName = dstName.to!wstring; - auto wstdNameLen = wstdName.length > 32 ? 32 : wstdName.length; - auto wdstNameLen = wdstName.length > 32 ? 32 : wdstName.length; - - tzInfo.Bias = tziFmt.Bias; - tzInfo.StandardName[0 .. wstdNameLen] = wstdName[0 .. wstdNameLen]; - tzInfo.StandardName[wstdNameLen .. $] = '\0'; - tzInfo.StandardDate = tziFmt.StandardDate; - tzInfo.StandardBias = tziFmt.StandardBias; - tzInfo.DaylightName[0 .. wdstNameLen] = wdstName[0 .. wdstNameLen]; - tzInfo.DaylightName[wdstNameLen .. $] = '\0'; - tzInfo.DaylightDate = tziFmt.DaylightDate; - tzInfo.DaylightBias = tziFmt.DaylightBias; - - return new immutable WindowsTimeZone(name, tzInfo); - } - import std.datetime.date : DateTimeException; - throw new DateTimeException(format("Failed to find time zone: %s", name)); - } - - static string[] getInstalledTZNames() @trusted - { - auto timezones = appender!(string[])(); - - scope baseKey = Registry.localMachine.getKey(`Software\Microsoft\Windows NT\CurrentVersion\Time Zones`); - - foreach (tzKeyName; baseKey.keyNames) - timezones.put(tzKeyName); - sort(timezones.data); - - return timezones.data; - } - - @safe unittest - { - import std.exception : assertNotThrown; - import std.stdio : writefln; - static void testWTZSuccess(string tzName) - { - scope(failure) writefln("TZName which threw: %s", tzName); - - WindowsTimeZone.getTimeZone(tzName); - } - - auto tzNames = getInstalledTZNames(); - - import std.datetime.date : DateTimeException; - foreach (tzName; tzNames) - assertNotThrown!DateTimeException(testWTZSuccess(tzName)); - } - - - private: - - static bool _dstInEffect(const scope TIME_ZONE_INFORMATION* tzInfo, long stdTime) @trusted nothrow - { - try - { - if (tzInfo.DaylightDate.wMonth == 0) - return false; - - import std.datetime.date : DateTime, Month; - auto utcDateTime = cast(DateTime) SysTime(stdTime, UTC()); - - //The limits of what SystemTimeToTzSpecificLocalTime will accept. - if (utcDateTime.year < 1601) - { - import std.datetime.date : Month; - if (utcDateTime.month == Month.feb && utcDateTime.day == 29) - utcDateTime.day = 28; - utcDateTime.year = 1601; - } - else if (utcDateTime.year > 30_827) - { - if (utcDateTime.month == Month.feb && utcDateTime.day == 29) - utcDateTime.day = 28; - utcDateTime.year = 30_827; - } - - //SystemTimeToTzSpecificLocalTime doesn't act correctly at the - //beginning or end of the year (bleh). Unless some bizarre time - //zone changes DST on January 1st or December 31st, this should - //fix the problem. - if (utcDateTime.month == Month.jan) - { - if (utcDateTime.day == 1) - utcDateTime.day = 2; - } - else if (utcDateTime.month == Month.dec && utcDateTime.day == 31) - utcDateTime.day = 30; - - SYSTEMTIME utcTime = void; - SYSTEMTIME otherTime = void; - - utcTime.wYear = utcDateTime.year; - utcTime.wMonth = utcDateTime.month; - utcTime.wDay = utcDateTime.day; - utcTime.wHour = utcDateTime.hour; - utcTime.wMinute = utcDateTime.minute; - utcTime.wSecond = utcDateTime.second; - utcTime.wMilliseconds = 0; - - immutable result = SystemTimeToTzSpecificLocalTime(cast(TIME_ZONE_INFORMATION*) tzInfo, - &utcTime, - &otherTime); - assert(result, "Failed to create SystemTimeToTzSpecificLocalTime"); - - immutable otherDateTime = DateTime(otherTime.wYear, - otherTime.wMonth, - otherTime.wDay, - otherTime.wHour, - otherTime.wMinute, - otherTime.wSecond); - immutable diff = utcDateTime - otherDateTime; - immutable minutes = diff.total!"minutes" - tzInfo.Bias; - - if (minutes == tzInfo.DaylightBias) - return true; - - assert(minutes == tzInfo.StandardBias, "Unexpected difference"); - - return false; - } - catch (Exception e) - assert(0, "DateTime's constructor threw."); - } - - @system unittest - { - TIME_ZONE_INFORMATION tzInfo; - GetTimeZoneInformation(&tzInfo); - - import std.datetime.date : DateTime; - foreach (year; [1600, 1601, 30_827, 30_828]) - WindowsTimeZone._dstInEffect(&tzInfo, SysTime(DateTime(year, 1, 1)).stdTime); - } - - - static long _utcToTZ(const scope TIME_ZONE_INFORMATION* tzInfo, long stdTime, bool hasDST) @safe nothrow - { - if (hasDST && WindowsTimeZone._dstInEffect(tzInfo, stdTime)) - return stdTime - convert!("minutes", "hnsecs")(tzInfo.Bias + tzInfo.DaylightBias); - - return stdTime - convert!("minutes", "hnsecs")(tzInfo.Bias + tzInfo.StandardBias); - } - - - static long _tzToUTC(const scope TIME_ZONE_INFORMATION* tzInfo, long adjTime, bool hasDST) @trusted nothrow - { - if (hasDST) - { - try - { - import std.datetime.date : DateTime, Month; - bool dstInEffectForLocalDateTime(DateTime localDateTime) - { - // The limits of what SystemTimeToTzSpecificLocalTime will accept. - if (localDateTime.year < 1601) - { - if (localDateTime.month == Month.feb && localDateTime.day == 29) - localDateTime.day = 28; - - localDateTime.year = 1601; - } - else if (localDateTime.year > 30_827) - { - if (localDateTime.month == Month.feb && localDateTime.day == 29) - localDateTime.day = 28; - - localDateTime.year = 30_827; - } - - // SystemTimeToTzSpecificLocalTime doesn't act correctly at the - // beginning or end of the year (bleh). Unless some bizarre time - // zone changes DST on January 1st or December 31st, this should - // fix the problem. - if (localDateTime.month == Month.jan) - { - if (localDateTime.day == 1) - localDateTime.day = 2; - } - else if (localDateTime.month == Month.dec && localDateTime.day == 31) - localDateTime.day = 30; - - SYSTEMTIME utcTime = void; - SYSTEMTIME localTime = void; - - localTime.wYear = localDateTime.year; - localTime.wMonth = localDateTime.month; - localTime.wDay = localDateTime.day; - localTime.wHour = localDateTime.hour; - localTime.wMinute = localDateTime.minute; - localTime.wSecond = localDateTime.second; - localTime.wMilliseconds = 0; - - immutable result = TzSpecificLocalTimeToSystemTime(cast(TIME_ZONE_INFORMATION*) tzInfo, - &localTime, - &utcTime); - assert(result); - assert(result, "Failed to create _tzToUTC"); - - immutable utcDateTime = DateTime(utcTime.wYear, - utcTime.wMonth, - utcTime.wDay, - utcTime.wHour, - utcTime.wMinute, - utcTime.wSecond); - - immutable diff = localDateTime - utcDateTime; - immutable minutes = -tzInfo.Bias - diff.total!"minutes"; - - if (minutes == tzInfo.DaylightBias) - return true; - - assert(minutes == tzInfo.StandardBias, "Unexpected difference"); - - return false; - } - - import std.datetime.date : DateTime; - auto localDateTime = cast(DateTime) SysTime(adjTime, UTC()); - auto localDateTimeBefore = localDateTime - dur!"hours"(1); - auto localDateTimeAfter = localDateTime + dur!"hours"(1); - - auto dstInEffectNow = dstInEffectForLocalDateTime(localDateTime); - auto dstInEffectBefore = dstInEffectForLocalDateTime(localDateTimeBefore); - auto dstInEffectAfter = dstInEffectForLocalDateTime(localDateTimeAfter); - - bool isDST; - - if (dstInEffectBefore && dstInEffectNow && dstInEffectAfter) - isDST = true; - else if (!dstInEffectBefore && !dstInEffectNow && !dstInEffectAfter) - isDST = false; - else if (!dstInEffectBefore && dstInEffectAfter) - isDST = false; - else if (dstInEffectBefore && !dstInEffectAfter) - isDST = dstInEffectNow; - else - assert(0, "Bad Logic."); - - if (isDST) - return adjTime + convert!("minutes", "hnsecs")(tzInfo.Bias + tzInfo.DaylightBias); - } - catch (Exception e) - assert(0, "SysTime's constructor threw."); - } - - return adjTime + convert!("minutes", "hnsecs")(tzInfo.Bias + tzInfo.StandardBias); - } - - - this(string name, TIME_ZONE_INFORMATION tzInfo) @trusted immutable pure - { - super(name, to!string(tzInfo.StandardName.ptr), to!string(tzInfo.DaylightName.ptr)); - _tzInfo = tzInfo; - } - - - TIME_ZONE_INFORMATION _tzInfo; - } -} - - -version (StdDdoc) -{ - /++ - $(BLUE This function is Posix-Only.) - - Sets the local time zone on Posix systems with the TZ - Database name by setting the TZ environment variable. - - Unfortunately, there is no way to do it on Windows using the TZ - Database name, so this function only exists on Posix systems. - +/ - void setTZEnvVar(string tzDatabaseName) @safe nothrow; - - - /++ - $(BLUE This function is Posix-Only.) - - Clears the TZ environment variable. - +/ - void clearTZEnvVar() @safe nothrow; -} -else version (Posix) -{ - void setTZEnvVar(string tzDatabaseName) @trusted nothrow - { - import core.stdc.time : tzset; - import core.sys.posix.stdlib : setenv; - import std.internal.cstring : tempCString; - import std.path : asNormalizedPath, chainPath; - - version (Android) - auto value = asNormalizedPath(tzDatabaseName); - else - auto value = asNormalizedPath(chainPath(PosixTimeZone.defaultTZDatabaseDir, tzDatabaseName)); - setenv("TZ", value.tempCString(), 1); - tzset(); - } - - - void clearTZEnvVar() @trusted nothrow - { - import core.stdc.time : tzset; - import core.sys.posix.stdlib : unsetenv; - - unsetenv("TZ"); - tzset(); - } -} - - -/++ - Provides the conversions between the IANA time zone database time zone names - (which POSIX systems use) and the time zone names that Windows uses. - - Windows uses a different set of time zone names than the IANA time zone - database does, and how they correspond to one another changes over time - (particularly when Microsoft updates Windows). - $(HTTP unicode.org/cldr/data/common/supplemental/windowsZones.xml, windowsZones.xml) - provides the current conversions (which may or may not match up with what's - on a particular Windows box depending on how up-to-date it is), and - parseTZConversions reads in those conversions from windowsZones.xml so that - a D program can use those conversions. - - However, it should be noted that the time zone information on Windows is - frequently less accurate than that in the IANA time zone database, and if - someone really wants accurate time zone information, they should use the - IANA time zone database files with $(LREF PosixTimeZone) on Windows rather - than $(LREF WindowsTimeZone), whereas $(LREF WindowsTimeZone) makes more - sense when trying to match what Windows will think the time is in a specific - time zone. - - Also, the IANA time zone database has a lot more time zones than Windows - does. - - Params: - windowsZonesXMLText = The text from - $(HTTP unicode.org/cldr/data/common/supplemental/windowsZones.xml, windowsZones.xml) - - Throws: - Exception if there is an error while parsing the given XML. - --------------------- - // Parse the conversions from a local file. - auto text = std.file.readText("path/to/windowsZones.xml"); - auto conversions = parseTZConversions(text); - - // Alternatively, grab the XML file from the web at runtime - // and parse it so that it's guaranteed to be up-to-date, though - // that has the downside that the code needs to worry about the - // site being down or unicode.org changing the URL. - auto url = "http://unicode.org/cldr/data/common/supplemental/windowsZones.xml"; - auto conversions2 = parseTZConversions(std.net.curl.get(url)); --------------------- - +/ -struct TZConversions -{ - /++ - The key is the Windows time zone name, and the value is a list of - IANA TZ database names which are close (currently only ever one, but - it allows for multiple in case it's ever necessary). - +/ - string[][string] toWindows; - - /++ - The key is the IANA time zone database name, and the value is a list of - Windows time zone names which are close (usually only one, but it could - be multiple). - +/ - string[][string] fromWindows; -} - -/++ ditto +/ -TZConversions parseTZConversions(string windowsZonesXMLText) @safe pure -{ - // This is a bit hacky, since it doesn't properly read XML, but it avoids - // needing to pull in an xml parsing module. - import std.algorithm.iteration : uniq; - import std.algorithm.searching : find; - import std.algorithm.sorting : sort; - import std.array : array, split; - import std.string : lineSplitter; - - string[][string] win2Nix; - string[][string] nix2Win; - - immutable f1 = ` - - line = line.find(f1); - if (line.empty) - continue; - line = line[f1.length .. $]; - auto next = line.find('"'); - enforce(!next.empty, "Error parsing. Text does not appear to be from windowsZones.xml"); - auto win = line[0 .. $ - next.length]; - line = next.find(f2); - enforce(!line.empty, "Error parsing. Text does not appear to be from windowsZones.xml"); - line = line[f2.length .. $]; - next = line.find('"'); - enforce(!next.empty, "Error parsing. Text does not appear to be from windowsZones.xml"); - auto nixes = line[0 .. $ - next.length].split(); - - if (auto n = win in win2Nix) - *n ~= nixes; - else - win2Nix[win] = nixes; - - foreach (nix; nixes) - { - if (auto w = nix in nix2Win) - *w ~= win; - else - nix2Win[nix] = [win]; - } - } - - foreach (key, ref value; nix2Win) - value = value.sort().uniq().array(); - foreach (key, ref value; win2Nix) - value = value.sort().uniq().array(); - - return TZConversions(nix2Win, win2Nix); -} - -@safe unittest -{ - import std.algorithm.comparison : equal; - import std.algorithm.iteration : uniq; - import std.algorithm.sorting : isSorted; - - // Reduced text from http://unicode.org/cldr/data/common/supplemental/windowsZones.xml - auto sampleFileText = -` - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -`; - - auto tzConversions = parseTZConversions(sampleFileText); - assert(tzConversions.toWindows.length == 15); - assert(tzConversions.toWindows["America/Anchorage"] == ["Alaskan Standard Time"]); - assert(tzConversions.toWindows["America/Juneau"] == ["Alaskan Standard Time"]); - assert(tzConversions.toWindows["America/Nome"] == ["Alaskan Standard Time"]); - assert(tzConversions.toWindows["America/Sitka"] == ["Alaskan Standard Time"]); - assert(tzConversions.toWindows["America/Yakutat"] == ["Alaskan Standard Time"]); - assert(tzConversions.toWindows["Etc/GMT+10"] == ["Hawaiian Standard Time"]); - assert(tzConversions.toWindows["Etc/GMT+11"] == ["UTC-11"]); - assert(tzConversions.toWindows["Etc/GMT+12"] == ["Dateline Standard Time"]); - assert(tzConversions.toWindows["Pacific/Honolulu"] == ["Hawaiian Standard Time"]); - assert(tzConversions.toWindows["Pacific/Johnston"] == ["Hawaiian Standard Time"]); - assert(tzConversions.toWindows["Pacific/Midway"] == ["UTC-11"]); - assert(tzConversions.toWindows["Pacific/Niue"] == ["UTC-11"]); - assert(tzConversions.toWindows["Pacific/Pago_Pago"] == ["UTC-11"]); - assert(tzConversions.toWindows["Pacific/Rarotonga"] == ["Hawaiian Standard Time"]); - assert(tzConversions.toWindows["Pacific/Tahiti"] == ["Hawaiian Standard Time"]); - - assert(tzConversions.fromWindows.length == 4); - assert(tzConversions.fromWindows["Alaskan Standard Time"] == - ["America/Anchorage", "America/Juneau", "America/Nome", "America/Sitka", "America/Yakutat"]); - assert(tzConversions.fromWindows["Dateline Standard Time"] == ["Etc/GMT+12"]); - assert(tzConversions.fromWindows["Hawaiian Standard Time"] == - ["Etc/GMT+10", "Pacific/Honolulu", "Pacific/Johnston", "Pacific/Rarotonga", "Pacific/Tahiti"]); - assert(tzConversions.fromWindows["UTC-11"] == - ["Etc/GMT+11", "Pacific/Midway", "Pacific/Niue", "Pacific/Pago_Pago"]); - - foreach (key, value; tzConversions.fromWindows) - { - assert(value.isSorted, key); - assert(equal(value.uniq(), value), key); - } -} diff --git a/phobos/std/demangle.d b/phobos/std/demangle.d deleted file mode 100644 index a78d3d3..0000000 --- a/phobos/std/demangle.d +++ /dev/null @@ -1,69 +0,0 @@ -// Written in the D programming language. - -/** - * Demangle D mangled names. - * - * Copyright: Copyright The D Language Foundation 2000 - 2009. - * License: $(HTTP www.boost.org/LICENSE_1_0.txt, Boost License 1.0). - * Authors: $(HTTP digitalmars.com, Walter Bright), - * Thomas K$(UUML)hne, Frits van Bommel - * Source: $(PHOBOSSRC std/demangle.d) - * $(SCRIPT inhibitQuickIndex = 1;) - */ -/* - * Copyright The D Language Foundation 2000 - 2009. - * 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.demangle; - -/** -Demangle D mangled names. - -Params: - name = the mangled name -Returns: - A `string`. If it is not a D mangled name, it returns its argument name. - */ -string demangle(string name) @safe pure nothrow -{ - import core.demangle : demangle; - import std.exception : assumeUnique; - auto ret = demangle(name); - return () @trusted { return ret.assumeUnique; } (); -} - -/// -@safe pure unittest -{ - // int b in module a - assert(demangle("_D1a1bi") == "int a.b"); - // char array foo in module test - assert(demangle("_D4test3fooAa") == "char[] test.foo"); -} - -/** -This program reads standard in and writes it to standard out, -pretty-printing any found D mangled names. - */ -@system unittest -{ - import std.ascii : isAlphaNum; - import std.algorithm.iteration : chunkBy, joiner, map; - import std.algorithm.mutation : copy; - import std.conv : to; - import std.demangle : demangle; - import std.functional : pipe; - import std.stdio : stdin, stdout; - - void main() - { - stdin.byLineCopy - .map!( - l => l.chunkBy!(a => isAlphaNum(a) || a == '_') - .map!(a => a[1].pipe!(to!string, demangle)).joiner - ) - .copy(stdout.lockingTextWriter); - } -} diff --git a/phobos/std/digest/crc.d b/phobos/std/digest/crc.d deleted file mode 100644 index b7922bb..0000000 --- a/phobos/std/digest/crc.d +++ /dev/null @@ -1,730 +0,0 @@ -/** -Cyclic Redundancy Check (32-bit) implementation. - -$(SCRIPT inhibitQuickIndex = 1;) - -$(DIVC quickindex, -$(BOOKTABLE , -$(TR $(TH Category) $(TH Functions) -) -$(TR $(TDNW Template API) $(TD $(MYREF CRC) $(MYREF CRC32) $(MYREF CRC64ECMA) $(MYREF CRC64ISO) -) -) -$(TR $(TDNW OOP API) $(TD $(MYREF CRC32Digest) $(MYREF CRC64ECMADigest) $(MYREF CRC64ISODigest)) -) -$(TR $(TDNW Helpers) $(TD $(MYREF crcHexString) $(MYREF crc32Of) $(MYREF crc64ECMAOf) $(MYREF crc64ISOOf)) -) -) -) - - * - * This module conforms to the APIs defined in `std.digest`. To understand the - * differences between the template and the OOP API, see $(MREF std, digest). - * - * This module publicly imports $(MREF std, digest) and can be used as a stand-alone - * module. - * - * Note: - * CRCs are usually printed with the MSB first. When using - * $(REF toHexString, std,digest) the result will be in an unexpected - * order. Use $(REF toHexString, std,digest)'s optional order parameter - * to specify decreasing order for the correct result. The $(LREF crcHexString) - * alias can also be used for this purpose. - * - * License: $(HTTP www.boost.org/LICENSE_1_0.txt, Boost License 1.0). - * - * Authors: Pavel "EvilOne" Minayev, Alex Rønne Petersen, Johannes Pfau - * - * References: - * $(LINK2 http://en.wikipedia.org/wiki/Cyclic_redundancy_check, Wikipedia on CRC) - * - * Source: $(PHOBOSSRC std/digest/crc.d) - * - * Standards: - * Implements the 'common' IEEE CRC32 variant - * (LSB-first order, Initial value uint.max, complement result) - * - * CTFE: - * Digests do not work in CTFE - */ -/* - * Copyright (c) 2001 - 2002 - * Pavel "EvilOne" Minayev - * Copyright (c) 2012 - * Alex Rønne Petersen - * 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.digest.crc; - -public import std.digest; - -/// -@safe unittest -{ - //Template API - import std.digest.crc; - - ubyte[4] hash = crc32Of("The quick brown fox jumps over the lazy dog"); - assert(crcHexString(hash) == "414FA339"); - - //Feeding data - ubyte[1024] data; - CRC32 crc; - crc.put(data[]); - crc.start(); //Start again - crc.put(data[]); - hash = crc.finish(); -} - -/// -@safe unittest -{ - //OOP API - import std.digest.crc; - - auto crc = new CRC32Digest(); - ubyte[] hash = crc.digest("The quick brown fox jumps over the lazy dog"); - assert(crcHexString(hash) == "414FA339"); //352441c2 - - //Feeding data - ubyte[1024] data; - crc.put(data[]); - crc.reset(); //Start again - crc.put(data[]); - hash = crc.finish(); -} - -private T[256][8] genTables(T)(T polynomial) -{ - T[256][8] res = void; - - foreach (i; 0 .. 0x100) - { - T crc = i; - foreach (_; 0 .. 8) - crc = (crc >> 1) ^ (-int(crc & 1) & polynomial); - res[0][i] = crc; - } - - foreach (i; 0 .. 0x100) - { - res[1][i] = (res[0][i] >> 8) ^ res[0][res[0][i] & 0xFF]; - res[2][i] = (res[1][i] >> 8) ^ res[0][res[1][i] & 0xFF]; - res[3][i] = (res[2][i] >> 8) ^ res[0][res[2][i] & 0xFF]; - res[4][i] = (res[3][i] >> 8) ^ res[0][res[3][i] & 0xFF]; - res[5][i] = (res[4][i] >> 8) ^ res[0][res[4][i] & 0xFF]; - res[6][i] = (res[5][i] >> 8) ^ res[0][res[5][i] & 0xFF]; - res[7][i] = (res[6][i] >> 8) ^ res[0][res[6][i] & 0xFF]; - } - return res; -} - -@system unittest -{ - auto tables = genTables(0xEDB88320); - assert(tables[0][0] == 0x00000000 && tables[0][$ - 1] == 0x2d02ef8d && tables[7][$ - 1] == 0x264b06e6); -} - -/** - * Template API CRC32 implementation. - * See `std.digest` for differences between template and OOP API. - */ -alias CRC32 = CRC!(32, 0xEDB88320); - -/** - * Template API CRC64-ECMA implementation. - * See `std.digest` for differences between template and OOP API. - */ -alias CRC64ECMA = CRC!(64, 0xC96C5795D7870F42); - -/** - * Template API CRC64-ISO implementation. - * See `std.digest` for differences between template and OOP API. - */ -alias CRC64ISO = CRC!(64, 0xD800000000000000); - -/** - * Generic Template API used for CRC32 and CRC64 implementations. - * - * The N parameter indicate the size of the hash in bits. - * The parameter P specify the polynomial to be used for reduction. - * - * You may want to use the CRC32, CRC65ECMA and CRC64ISO aliases - * for convenience. - * - * See `std.digest` for differences between template and OOP API. - */ -struct CRC(uint N, ulong P) -if (N == 32 || N == 64) -{ - private: - static if (N == 32) - { - alias T = uint; - } - else - { - alias T = ulong; - } - - static immutable T[256][8] tables = genTables!T(P); - - /** - * Type of the finished CRC hash. - * ubyte[4] if N is 32, ubyte[8] if N is 64. - */ - alias R = ubyte[T.sizeof]; - - // magic initialization constants - T _state = T.max; - - public: - /** - * Use this to feed the digest with data. - * Also implements the $(REF isOutputRange, std,range,primitives) - * interface for `ubyte` and `const(ubyte)[]`. - */ - void put(scope const(ubyte)[] data...) @trusted pure nothrow @nogc - { - T crc = _state; - // process eight bytes at once - while (data.length >= 8) - { - // Use byte-wise reads to support architectures without HW support - // for unaligned reads. This can be optimized by compilers to a single - // 32-bit read if unaligned reads are supported. - // DMD is not able to do this optimization though, so explicitly - // do unaligned reads for DMD's architectures. - version (X86) - enum hasLittleEndianUnalignedReads = true; - else version (X86_64) - enum hasLittleEndianUnalignedReads = true; - else - enum hasLittleEndianUnalignedReads = false; // leave decision to optimizer - - uint one = void; - uint two = void; - - if (!__ctfe && hasLittleEndianUnalignedReads) - { - one = (cast(uint*) data.ptr)[0]; - two = (cast(uint*) data.ptr)[1]; - } - else - { - one = (data.ptr[3] << 24 | data.ptr[2] << 16 | data.ptr[1] << 8 | data.ptr[0]); - two = (data.ptr[7] << 24 | data.ptr[6] << 16 | data.ptr[5] << 8 | data.ptr[4]); - } - - static if (N == 32) - { - one ^= crc; - } - else - { - one ^= (crc & 0xffffffff); - two ^= (crc >> 32); - } - - crc = - tables[0][two >> 24] ^ - tables[1][(two >> 16) & 0xFF] ^ - tables[2][(two >> 8) & 0xFF] ^ - tables[3][two & 0xFF] ^ - tables[4][one >> 24] ^ - tables[5][(one >> 16) & 0xFF] ^ - tables[6][(one >> 8) & 0xFF] ^ - tables[7][one & 0xFF]; - - data = data[8 .. $]; - } - // remaining 1 to 7 bytes - foreach (d; data) - crc = (crc >> 8) ^ tables[0][(crc & 0xFF) ^ d]; - _state = crc; - } - - /** - * Used to initialize the CRC32 digest. - * - * Note: - * For this CRC32 Digest implementation calling start after default construction - * is not necessary. Calling start is only necessary to reset the Digest. - * - * Generic code which deals with different Digest types should always call start though. - */ - void start() @safe pure nothrow @nogc - { - this = CRC.init; - } - - /** - * Returns the finished CRC hash. This also calls $(LREF start) to - * reset the internal state. - */ - R finish() @safe pure nothrow @nogc - { - auto tmp = peek(); - start(); - return tmp; - } - - /** - * Works like `finish` but does not reset the internal state, so it's possible - * to continue putting data into this CRC after a call to peek. - */ - R peek() const @safe pure nothrow @nogc - { - import std.bitmanip : nativeToLittleEndian; - //Complement, LSB first / Little Endian, see http://rosettacode.org/wiki/CRC-32 - return nativeToLittleEndian(~_state); - } -} - -@safe unittest -{ - // https://issues.dlang.org/show_bug.cgi?id=13471 - static ubyte[4] foo(string str) - { - ubyte[4] result = str.crc32Of(); - if (result == (ubyte[4]).init) - throw new Exception("this should not be thrown"); - return result; - } - enum buggy1 = foo("Hello World!"); - enum buggy2 = crc32Of("Hello World!"); - assert(buggy1 == buggy2); - assert(buggy1 == "Hello World!".crc32Of()); -} - -/// -@safe unittest -{ - //Simple example, hashing a string using crc32Of helper function - ubyte[4] hash32 = crc32Of("abc"); - //Let's get a hash string - assert(crcHexString(hash32) == "352441C2"); - // Repeat for CRC64 - ubyte[8] hash64ecma = crc64ECMAOf("abc"); - assert(crcHexString(hash64ecma) == "2CD8094A1A277627"); - ubyte[8] hash64iso = crc64ISOOf("abc"); - assert(crcHexString(hash64iso) == "3776C42000000000"); -} - -/// -@safe unittest -{ - ubyte[1024] data; - //Using the basic API - CRC32 hash32; - CRC64ECMA hash64ecma; - CRC64ISO hash64iso; - //Initialize data here... - hash32.put(data); - ubyte[4] result32 = hash32.finish(); - hash64ecma.put(data); - ubyte[8] result64ecma = hash64ecma.finish(); - hash64iso.put(data); - ubyte[8] result64iso = hash64iso.finish(); -} - -/// -@safe unittest -{ - //Let's use the template features: - //Note: When passing a CRC32 to a function, it must be passed by reference! - void doSomething(T)(ref T hash) - if (isDigest!T) - { - hash.put(cast(ubyte) 0); - } - CRC32 crc32; - crc32.start(); - doSomething(crc32); - assert(crcHexString(crc32.finish()) == "D202EF8D"); - // repeat for CRC64 - CRC64ECMA crc64ecma; - crc64ecma.start(); - doSomething(crc64ecma); - assert(crcHexString(crc64ecma.finish()) == "1FADA17364673F59"); - CRC64ISO crc64iso; - crc64iso.start(); - doSomething(crc64iso); - assert(crcHexString(crc64iso.finish()) == "6F90000000000000"); -} - -@safe unittest -{ - assert(isDigest!CRC32); - assert(isDigest!CRC64ECMA); - assert(isDigest!CRC64ISO); -} - -@system unittest -{ - import std.conv : hexString; - ubyte[4] digest; - - CRC32 crc; - crc.put(cast(ubyte[])"abcdefghijklmnopqrstuvwxyz"); - assert(crc.peek() == cast(ubyte[]) hexString!"bd50274c"); - crc.start(); - crc.put(cast(ubyte[])""); - assert(crc.finish() == cast(ubyte[]) hexString!"00000000"); - - digest = crc32Of(""); - assert(digest == cast(ubyte[]) hexString!"00000000"); - - //Test vector from http://rosettacode.org/wiki/CRC-32 - assert(crcHexString(crc32Of("The quick brown fox jumps over the lazy dog")) == "414FA339"); - - digest = crc32Of("a"); - assert(digest == cast(ubyte[]) hexString!"43beb7e8"); - - digest = crc32Of("abc"); - assert(digest == cast(ubyte[]) hexString!"c2412435"); - - digest = crc32Of("abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq"); - assert(digest == cast(ubyte[]) hexString!"5f3f1a17"); - - digest = crc32Of("message digest"); - assert(digest == cast(ubyte[]) hexString!"7f9d1520"); - - digest = crc32Of("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"); - assert(digest == cast(ubyte[]) hexString!"d2e6c21f"); - - digest = crc32Of("1234567890123456789012345678901234567890"~ - "1234567890123456789012345678901234567890"); - assert(digest == cast(ubyte[]) hexString!"724aa97c"); - - enum ubyte[4] input = cast(ubyte[4]) hexString!"c3fcd3d7"; - assert(crcHexString(input) == "D7D3FCC3"); -} - -@system unittest -{ - import std.conv : hexString; - ubyte[8] digest; - - CRC64ECMA crc; - crc.put(cast(ubyte[])"abcdefghijklmnopqrstuvwxyz"); - assert(crc.peek() == cast(ubyte[]) hexString!"2f121b7575789626"); - crc.start(); - crc.put(cast(ubyte[])""); - assert(crc.finish() == cast(ubyte[]) hexString!"0000000000000000"); - digest = crc64ECMAOf(""); - assert(digest == cast(ubyte[]) hexString!"0000000000000000"); - - //Test vector from http://rosettacode.org/wiki/CRC-32 - assert(crcHexString(crc64ECMAOf("The quick brown fox jumps over the lazy dog")) == "5B5EB8C2E54AA1C4"); - - digest = crc64ECMAOf("a"); - assert(digest == cast(ubyte[]) hexString!"052b652e77840233"); - - digest = crc64ECMAOf("abc"); - assert(digest == cast(ubyte[]) hexString!"2776271a4a09d82c"); - - digest = crc64ECMAOf("abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq"); - assert(digest == cast(ubyte[]) hexString!"4b7cdce3746c449f"); - - digest = crc64ECMAOf("message digest"); - assert(digest == cast(ubyte[]) hexString!"6f9b8a3156c9bc5d"); - - digest = crc64ECMAOf("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"); - assert(digest == cast(ubyte[]) hexString!"2656b716e1bf0503"); - - digest = crc64ECMAOf("1234567890123456789012345678901234567890"~ - "1234567890123456789012345678901234567890"); - assert(digest == cast(ubyte[]) hexString!"bd3eb7765d0a22ae"); - - enum ubyte[8] input = cast(ubyte[8]) hexString!"c3fcd3d7efbeadde"; - assert(crcHexString(input) == "DEADBEEFD7D3FCC3"); -} - -@system unittest -{ - import std.conv : hexString; - ubyte[8] digest; - - CRC64ISO crc; - crc.put(cast(ubyte[])"abcdefghijklmnopqrstuvwxyz"); - assert(crc.peek() == cast(ubyte[]) hexString!"f0494ab780989b42"); - crc.start(); - crc.put(cast(ubyte[])""); - assert(crc.finish() == cast(ubyte[]) hexString!"0000000000000000"); - digest = crc64ISOOf(""); - assert(digest == cast(ubyte[]) hexString!"0000000000000000"); - - //Test vector from http://rosettacode.org/wiki/CRC-32 - assert(crcHexString(crc64ISOOf("The quick brown fox jumps over the lazy dog")) == "4EF14E19F4C6E28E"); - - digest = crc64ISOOf("a"); - assert(digest == cast(ubyte[]) hexString!"0000000000002034"); - - digest = crc64ISOOf("abc"); - assert(digest == cast(ubyte[]) hexString!"0000000020c47637"); - - digest = crc64ISOOf("abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq"); - assert(digest == cast(ubyte[]) hexString!"5173f717971365e5"); - - digest = crc64ISOOf("message digest"); - assert(digest == cast(ubyte[]) hexString!"a2c355bbc0b93f86"); - - digest = crc64ISOOf("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"); - assert(digest == cast(ubyte[]) hexString!"598B258292E40084"); - - digest = crc64ISOOf("1234567890123456789012345678901234567890"~ - "1234567890123456789012345678901234567890"); - assert(digest == cast(ubyte[]) hexString!"760cd2d3588bf809"); - - enum ubyte[8] input = cast(ubyte[8]) hexString!"c3fcd3d7efbeadde"; - assert(crcHexString(input) == "DEADBEEFD7D3FCC3"); -} - -/** - * This is a convenience alias for $(REF digest, std,digest) using the - * CRC32 implementation. - * - * Params: - * data = `InputRange` of `ElementType` implicitly convertible to - * `ubyte`, `ubyte[]` or `ubyte[num]` or one or more arrays - * of any type. - * - * Returns: - * CRC32 of data - */ -//simple alias doesn't work here, hope this gets inlined... -ubyte[4] crc32Of(T...)(T data) -{ - return digest!(CRC32, T)(data); -} - -/// -@system unittest -{ - ubyte[] data = [4,5,7,25]; - assert(data.crc32Of == [167, 180, 199, 131]); - - import std.utf : byChar; - assert("hello"d.byChar.crc32Of == [134, 166, 16, 54]); - - ubyte[4] hash = "abc".crc32Of(); - assert(hash == digest!CRC32("ab", "c")); - - import std.range : iota; - enum ubyte S = 5, F = 66; - assert(iota(S, F).crc32Of == [59, 140, 234, 154]); -} - -/** - * This is a convenience alias for $(REF digest, std,digest) using the - * CRC64-ECMA implementation. - * - * Params: - * data = `InputRange` of `ElementType` implicitly convertible to - * `ubyte`, `ubyte[]` or `ubyte[num]` or one or more arrays - * of any type. - * - * Returns: - * CRC64-ECMA of data - */ -//simple alias doesn't work here, hope this gets inlined... -ubyte[8] crc64ECMAOf(T...)(T data) -{ - return digest!(CRC64ECMA, T)(data); -} - -/// -@system unittest -{ - ubyte[] data = [4,5,7,25]; - assert(data.crc64ECMAOf == [58, 142, 220, 214, 118, 98, 105, 69]); - - import std.utf : byChar; - assert("hello"d.byChar.crc64ECMAOf == [177, 55, 185, 219, 229, 218, 30, 155]); - - ubyte[8] hash = "abc".crc64ECMAOf(); - assert("abc".crc64ECMAOf == [39, 118, 39, 26, 74, 9, 216, 44]); - assert(hash == digest!CRC64ECMA("ab", "c")); - - import std.range : iota; - enum ubyte S = 5, F = 66; - assert(iota(S, F).crc64ECMAOf == [6, 184, 91, 238, 46, 213, 127, 188]); -} - -/** - * This is a convenience alias for $(REF digest, std,digest) using the - * CRC64-ISO implementation. - * - * Params: - * data = `InputRange` of `ElementType` implicitly convertible to - * `ubyte`, `ubyte[]` or `ubyte[num]` or one or more arrays - * of any type. - * - * Returns: - * CRC64-ISO of data - */ -//simple alias doesn't work here, hope this gets inlined... -ubyte[8] crc64ISOOf(T...)(T data) -{ - return digest!(CRC64ISO, T)(data); -} - -/// -@system unittest -{ - ubyte[] data = [4,5,7,25]; - assert(data.crc64ISOOf == [0, 0, 0, 80, 137, 232, 203, 120]); - - import std.utf : byChar; - assert("hello"d.byChar.crc64ISOOf == [0, 0, 16, 216, 226, 238, 62, 60]); - - ubyte[8] hash = "abc".crc64ISOOf(); - assert("abc".crc64ISOOf == [0, 0, 0, 0, 32, 196, 118, 55]); - assert(hash == digest!CRC64ISO("ab", "c")); - - import std.range : iota; - enum ubyte S = 5, F = 66; - - assert(iota(S, F).crc64ISOOf == [21, 185, 116, 95, 219, 11, 54, 7]); -} - -/** - * producing the usual CRC32 string output. - */ -public alias crcHexString = toHexString!(Order.decreasing); -///ditto -public alias crcHexString = toHexString!(Order.decreasing, 16); - -/** - * OOP API CRC32 implementation. - * See `std.digest` for differences between template and OOP API. - * - * This is an alias for $(D $(REF WrapperDigest, std,digest)!CRC32), see - * there for more information. - */ -alias CRC32Digest = WrapperDigest!CRC32; - -/** - * OOP API CRC64-ECMA implementation. - * See `std.digest` for differences between template and OOP API. - * - * This is an alias for $(D $(REF WrapperDigest, std,digest)!CRC64ECMA), - * see there for more information. - */ -alias CRC64ECMADigest = WrapperDigest!CRC64ECMA; - -/** - * OOP API CRC64-ISO implementation. - * See `std.digest` for differences between template and OOP API. - * - * This is an alias for $(D $(REF WrapperDigest, std,digest)!CRC64ISO), - * see there for more information. - */ -alias CRC64ISODigest = WrapperDigest!CRC64ISO; - -/// -@safe unittest -{ - //Simple example, hashing a string using CRC32Digest.digest helper function - auto crc = new CRC32Digest(); - ubyte[] hash = crc.digest("abc"); - //Let's get a hash string - assert(crcHexString(hash) == "352441C2"); -} - -/// -@system unittest -{ - //Let's use the OOP features: - void test(Digest dig) - { - dig.put(cast(ubyte) 0); - } - auto crc = new CRC32Digest(); - test(crc); - - //Let's use a custom buffer: - ubyte[4] buf; - ubyte[] result = crc.finish(buf[]); - assert(crcHexString(result) == "D202EF8D"); -} - -/// -@safe unittest -{ - //Simple example - auto hash = new CRC32Digest(); - hash.put(cast(ubyte) 0); - ubyte[] result = hash.finish(); -} - -/// -@system unittest -{ - //using a supplied buffer - ubyte[4] buf; - auto hash = new CRC32Digest(); - hash.put(cast(ubyte) 0); - ubyte[] result = hash.finish(buf[]); - //The result is now in result (and in buf. If you pass a buffer which is bigger than - //necessary, result will have the correct length, but buf will still have it's original - //length) -} - -@system unittest -{ - import std.conv : hexString; - import std.range; - import std.exception; - - auto crc = new CRC32Digest(); - - crc.put(cast(ubyte[])"abcdefghijklmnopqrstuvwxyz"); - assert(crc.peek() == cast(ubyte[]) hexString!"bd50274c"); - crc.reset(); - crc.put(cast(ubyte[])""); - assert(crc.finish() == cast(ubyte[]) hexString!"00000000"); - - crc.put(cast(ubyte[])"abcdefghijklmnopqrstuvwxyz"); - ubyte[20] result; - auto result2 = crc.finish(result[]); - assert(result[0 .. 4] == result2 && result2 == cast(ubyte[]) hexString!"bd50274c"); - - debug - assertThrown!Error(crc.finish(result[0 .. 3])); - - assert(crc.length == 4); - - assert(crc.digest("") == cast(ubyte[]) hexString!"00000000"); - - assert(crc.digest("a") == cast(ubyte[]) hexString!"43beb7e8"); - - assert(crc.digest("abc") == cast(ubyte[]) hexString!"c2412435"); - - assert(crc.digest("abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq") - == cast(ubyte[]) hexString!"5f3f1a17"); - - assert(crc.digest("message digest") == cast(ubyte[]) hexString!"7f9d1520"); - - assert(crc.digest("abcdefghijklmnopqrstuvwxyz") - == cast(ubyte[]) hexString!"bd50274c"); - - assert(crc.digest("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789") - == cast(ubyte[]) hexString!"d2e6c21f"); - - assert(crc.digest("1234567890123456789012345678901234567890", - "1234567890123456789012345678901234567890") - == cast(ubyte[]) hexString!"724aa97c"); - - ubyte[] onemilliona = new ubyte[1000000]; - onemilliona[] = 'a'; - auto digest = crc32Of(onemilliona); - assert(digest == cast(ubyte[]) hexString!"BCBF25DC"); - - auto oneMillionRange = repeat!ubyte(cast(ubyte)'a', 1000000); - digest = crc32Of(oneMillionRange); - assert(digest == cast(ubyte[]) hexString!"BCBF25DC"); -} diff --git a/phobos/std/digest/hmac.d b/phobos/std/digest/hmac.d deleted file mode 100644 index 6688ba7..0000000 --- a/phobos/std/digest/hmac.d +++ /dev/null @@ -1,354 +0,0 @@ -// Written in the D programming language. - -/** -This package implements the hash-based message authentication code (_HMAC) -algorithm as defined in $(HTTP tools.ietf.org/html/rfc2104, RFC2104). See also -the corresponding $(HTTP en.wikipedia.org/wiki/Hash-based_message_authentication_code, Wikipedia article). - -$(SCRIPT inhibitQuickIndex = 1;) - -Macros: - -License: $(HTTP boost.org/LICENSE_1_0.txt, Boost License 1.0). - -Source: $(PHOBOSSRC std/digest/hmac.d) - */ - -module std.digest.hmac; - -import std.digest : isDigest, hasBlockSize, isDigestibleRange, DigestType; -import std.meta : allSatisfy; - -@safe: - -/** - * Template API HMAC implementation. - * - * This implements an _HMAC over the digest H. If H doesn't provide - * information about the block size, it can be supplied explicitly using - * the second overload. - * - * This type conforms to $(REF isDigest, std,digest). - */ - -/// Compute HMAC over an input string -@safe unittest -{ - import std.ascii : LetterCase; - import std.digest : toHexString; - import std.digest.sha : SHA1; - import std.string : representation; - - auto secret = "secret".representation; - assert("The quick brown fox jumps over the lazy dog" - .representation - .hmac!SHA1(secret) - .toHexString!(LetterCase.lower) == "198ea1ea04c435c1246b586a06d5cf11c3ffcda6"); -} - -template HMAC(H) -if (isDigest!H && hasBlockSize!H) -{ - alias HMAC = HMAC!(H, H.blockSize); -} - -/** - * Overload of HMAC to be used if H doesn't provide information about its - * block size. - */ - -struct HMAC(H, size_t hashBlockSize) -if (hashBlockSize % 8 == 0) -{ - enum blockSize = hashBlockSize; - - private H digest; - private ubyte[blockSize / 8] key; - - /** - * Constructs the HMAC digest using the specified secret. - */ - - this(scope const(ubyte)[] secret) - { - // if secret is too long, shorten it by computing its hash - typeof(digest.finish()) buffer = void; - typeof(secret) secretBytes = secret; - - if (secret.length > blockSize / 8) - { - digest.start(); - digest.put(secret); - buffer = digest.finish(); - secretBytes = buffer[]; - } - - // if secret is too short, it will be padded with zeroes - // (the key buffer is already zero-initialized) - import std.algorithm.mutation : copy; - secretBytes.copy(key[]); - - start(); - } - - /// - @safe pure nothrow @nogc unittest - { - import std.digest.sha : SHA1; - import std.string : representation; - auto hmac = HMAC!SHA1("My s3cR3T keY".representation); - hmac.put("Hello, world".representation); - static immutable expected = [ - 130, 32, 235, 44, 208, 141, - 150, 232, 211, 214, 162, 195, - 188, 127, 52, 89, 100, 68, 90, 216]; - assert(hmac.finish() == expected); - } - - /** - * Reinitializes the digest, making it ready for reuse. - * - * Note: - * The constructor leaves the digest in an initialized state, so that this - * method only needs to be called if an unfinished digest is to be reused. - * - * Returns: - * A reference to the digest for convenient chaining. - */ - - ref HMAC!(H, blockSize) start() return - { - ubyte[blockSize / 8] ipad = void; - foreach (immutable i; 0 .. blockSize / 8) - ipad[i] = key[i] ^ 0x36; - - digest.start(); - digest.put(ipad[]); - - return this; - } - - /// - @safe pure nothrow @nogc unittest - { - import std.digest.sha : SHA1; - import std.string : representation; - string data1 = "Hello, world", data2 = "Hola mundo"; - auto hmac = HMAC!SHA1("My s3cR3T keY".representation); - hmac.put(data1.representation); - hmac.start(); // reset digest - hmac.put(data2.representation); // start over - static immutable expected = [ - 122, 151, 232, 240, 249, 80, - 19, 178, 186, 77, 110, 23, 208, - 52, 11, 88, 34, 151, 192, 255]; - assert(hmac.finish() == expected); - } - - /** - * Feeds a piece of data into the hash computation. This method allows the - * type to be used as an $(REF OutputRange, std,range). - * - * Returns: - * A reference to the digest for convenient chaining. - */ - - ref HMAC!(H, blockSize) put(in ubyte[] data...) return - { - digest.put(data); - return this; - } - - /// - @safe pure nothrow @nogc unittest - { - import std.digest.hmac, std.digest.sha; - import std.string : representation; - string data1 = "Hello, world", data2 = "Hola mundo"; - auto hmac = HMAC!SHA1("My s3cR3T keY".representation); - hmac.put(data1.representation) - .put(data2.representation); - static immutable expected = [ - 197, 57, 52, 3, 13, 194, 13, - 36, 117, 228, 8, 11, 111, 51, - 165, 3, 123, 31, 251, 113]; - assert(hmac.finish() == expected); - } - - /** - * Resets the digest and returns the finished hash. - */ - - DigestType!H finish() - { - ubyte[blockSize / 8] opad = void; - foreach (immutable i; 0 .. blockSize / 8) - opad[i] = key[i] ^ 0x5c; - - auto tmp = digest.finish(); - - digest.start(); - digest.put(opad[]); - digest.put(tmp); - auto result = digest.finish(); - start(); // reset the digest - return result; - } - - /// - @safe pure nothrow @nogc unittest - { - import std.digest.sha : SHA1; - import std.string : representation; - string data1 = "Hello, world", data2 = "Hola mundo"; - auto hmac = HMAC!SHA1("My s3cR3T keY".representation); - auto testDigest = hmac.put(data1.representation) - .put(data2.representation) - .finish(); - static immutable expected = [ - 197, 57, 52, 3, 13, 194, 13, - 36, 117, 228, 8, 11, 111, 51, - 165, 3, 123, 31, 251, 113]; - assert(testDigest == expected); - } -} - -/// ditto -template hmac(H) -if (isDigest!H && hasBlockSize!H) -{ - alias hmac = hmac!(H, H.blockSize); -} - -/// ditto -template hmac(H, size_t blockSize) -if (isDigest!H) -{ - /** - * Constructs an HMAC digest with the specified secret. - * - * Returns: - * An instance of HMAC that can be fed data as desired, and finished - * to compute the final hash when done. - */ - auto hmac(scope const(ubyte)[] secret) - { - return HMAC!(H, blockSize)(secret); - } - - /// - @safe pure nothrow @nogc unittest - { - import std.digest.sha : SHA1; - import std.string : representation; - string data1 = "Hello, world", data2 = "Hola mundo"; - auto digest = hmac!SHA1("My s3cR3T keY".representation) - .put(data1.representation) - .put(data2.representation) - .finish(); - static immutable expected = [ - 197, 57, 52, 3, 13, 194, 13, 36, - 117, 228, 8, 11, 111, 51, 165, - 3, 123, 31, 251, 113]; - assert(digest == expected); - } - - /** - * Computes an _HMAC digest over the given range of data with the - * specified secret. - * - * Returns: - * The final _HMAC hash. - */ - DigestType!H hmac(T...)(scope T data, scope const(ubyte)[] secret) - if (allSatisfy!(isDigestibleRange, typeof(data))) - { - import std.range.primitives : put; - auto hash = HMAC!(H, blockSize)(secret); - foreach (datum; data) - put(hash, datum); - return hash.finish(); - } - - /// - @safe pure nothrow @nogc unittest - { - import std.algorithm.iteration : map; - import std.digest.sha : SHA1; - import std.string : representation; - string data = "Hello, world"; - auto digest = data.representation - .map!(a => cast(ubyte)(a+1)) - .hmac!SHA1("My s3cR3T keY".representation); - static assert(is(typeof(digest) == ubyte[20])); - static immutable expected = [ - 163, 208, 118, 179, 216, 93, - 17, 10, 84, 200, 87, 104, 244, - 111, 136, 214, 167, 210, 58, 10]; - assert(digest == expected); - } -} - -/// -@safe pure nothrow @nogc unittest -{ - import std.digest.sha : SHA1; - import std.string : representation; - string data1 = "Hello, world", data2 = "Hola mundo"; - auto hmac = HMAC!SHA1("My s3cR3T keY".representation); - auto digest = hmac.put(data1.representation) - .put(data2.representation) - .finish(); - static immutable expected = [ - 197, 57, 52, 3, 13, 194, 13, - 36, 117, 228, 8, 11, 111, 51, - 165, 3, 123, 31, 251, 113]; - assert(digest == expected); -} - -@safe pure nothrow @nogc -unittest -{ - import std.digest.md : MD5; - import std.range : isOutputRange; - static assert(isOutputRange!(HMAC!MD5, ubyte)); - static assert(isDigest!(HMAC!MD5)); - static assert(hasBlockSize!(HMAC!MD5) && HMAC!MD5.blockSize == MD5.blockSize); -} - -@safe pure nothrow -unittest -{ - import std.digest.md : MD5; - import std.digest.sha : SHA1, SHA256; - - // Note, can't be UFCS because we don't want to import inside - // version (StdUnittest). - import std.digest : toHexString, LetterCase; - alias hex = toHexString!(LetterCase.lower); - - ubyte[] nada; - assert(hex(hmac!MD5 (nada, nada)) == "74e6f7298a9c2d168935f58c001bad88"); - assert(hex(hmac!SHA1 (nada, nada)) == "fbdb1d1b18aa6c08324b7d64b71fb76370690e1d"); - assert(hex(hmac!SHA256(nada, nada)) == "b613679a0814d9ec772f95d778c35fc5ff1697c493715653c6c712144292c5ad"); - - import std.string : representation; - auto key = "key".representation, - long_key = ("012345678901234567890123456789012345678901" - ~"234567890123456789012345678901234567890123456789").representation, - data1 = "The quick brown fox ".representation, - data2 = "jumps over the lazy dog".representation, - data = data1 ~ data2; - - assert(hex(data.hmac!MD5 (key)) == "80070713463e7749b90c2dc24911e275"); - assert(hex(data.hmac!SHA1 (key)) == "de7c9b85b8b78aa6bc8a7a36f70a90701c9db4d9"); - assert(hex(data.hmac!SHA256(key)) == "f7bc83f430538424b13298e6aa6fb143ef4d59a14946175997479dbc2d1a3cd8"); - - assert(hex(data.hmac!MD5 (long_key)) == "e1728d68e05beae186ea768561963778"); - assert(hex(data.hmac!SHA1 (long_key)) == "560d3cd77316e57ab4bba0c186966200d2b37ba3"); - assert(hex(data.hmac!SHA256(long_key)) == "a1b0065a5d1edd93152c677e1bc1b1e3bc70d3a76619842e7f733f02b8135c04"); - - assert(hmac!MD5 (key).put(data1).put(data2).finish == data.hmac!MD5 (key)); - assert(hmac!SHA1 (key).put(data1).put(data2).finish == data.hmac!SHA1 (key)); - assert(hmac!SHA256(key).put(data1).put(data2).finish == data.hmac!SHA256(key)); -} diff --git a/phobos/std/digest/md.d b/phobos/std/digest/md.d deleted file mode 100644 index f4d6200..0000000 --- a/phobos/std/digest/md.d +++ /dev/null @@ -1,587 +0,0 @@ -/** - * Computes MD5 hashes of arbitrary data. MD5 hashes are 16 byte quantities that are like a - * checksum or CRC, but are more robust. - * -$(SCRIPT inhibitQuickIndex = 1;) - -$(DIVC quickindex, -$(BOOKTABLE , -$(TR $(TH Category) $(TH Functions) -) -$(TR $(TDNW Template API) $(TD $(MYREF MD5) -) -) -$(TR $(TDNW OOP API) $(TD $(MYREF MD5Digest)) -) -$(TR $(TDNW Helpers) $(TD $(MYREF md5Of)) -) -) -) - - * This module conforms to the APIs defined in `std.digest`. To understand the - * differences between the template and the OOP API, see $(MREF std, digest). - * - * This module publicly imports $(MREF std, digest) and can be used as a stand-alone - * module. - * - * License: $(HTTP www.boost.org/LICENSE_1_0.txt, Boost License 1.0). - * - * CTFE: - * Digests do not work in CTFE - * - * Authors: - * Piotr Szturmaj, Kai Nacke, Johannes Pfau $(BR) - * The routines and algorithms are derived from the $(I RSA Data Security, Inc. MD5 Message-Digest Algorithm). - * - * References: - * $(LINK2 http://en.wikipedia.org/wiki/Md5, Wikipedia on MD5) - * - * Source: $(PHOBOSSRC std/digest/md.d) - * - */ - -/* md5.d - RSA Data Security, Inc., MD5 message-digest algorithm - * Derived from the RSA Data Security, Inc. MD5 Message-Digest Algorithm. - */ -module std.digest.md; - -public import std.digest; - -/// -@safe unittest -{ - //Template API - import std.digest.md; - - //Feeding data - ubyte[1024] data; - MD5 md5; - md5.start(); - md5.put(data[]); - md5.start(); //Start again - md5.put(data[]); - auto hash = md5.finish(); -} - -/// -@safe unittest -{ - //OOP API - import std.digest.md; - - auto md5 = new MD5Digest(); - ubyte[] hash = md5.digest("abc"); - assert(toHexString(hash) == "900150983CD24FB0D6963F7D28E17F72"); - - //Feeding data - ubyte[1024] data; - md5.put(data[]); - md5.reset(); //Start again - md5.put(data[]); - hash = md5.finish(); -} - -/** - * Template API MD5 implementation. - * See `std.digest` for differences between template and OOP API. - */ -struct MD5 -{ - import core.bitop : rol; - private: - // magic initialization constants - uint[4] _state = [0x67452301,0xefcdab89,0x98badcfe,0x10325476]; // state (ABCD) - ulong _count; //number of bits, modulo 2^64 - ubyte[64] _buffer; // input buffer - - static immutable ubyte[64] _padding = - [ - 0x80, 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, 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, 0, 0, 0, 0, 0, 0, 0, 0, 0 - ]; - - // F, G, H and I are basic MD5 functions - static @safe pure nothrow @nogc - { - uint F(uint x, uint y, uint z) { return (x & y) | (~x & z); } - uint G(uint x, uint y, uint z) { return (x & z) | (y & ~z); } - uint H(uint x, uint y, uint z) { return x ^ y ^ z; } - uint I(uint x, uint y, uint z) { return y ^ (x | ~z); } - } - - - /* - * FF, GG, HH, and II transformations for rounds 1, 2, 3, and 4. - * Rotation is separate from addition to prevent recomputation. - */ - static void FF(ref uint a, uint b, uint c, uint d, uint x, uint s, uint ac) - @safe pure nothrow @nogc - { - a += F (b, c, d) + x + ac; - a = rol(a, s); - a += b; - } - - static void GG(ref uint a, uint b, uint c, uint d, uint x, uint s, uint ac) - @safe pure nothrow @nogc - { - a += G (b, c, d) + x + ac; - a = rol(a, s); - a += b; - } - - static void HH(ref uint a, uint b, uint c, uint d, uint x, uint s, uint ac) - @safe pure nothrow @nogc - { - a += H (b, c, d) + x + ac; - a = rol(a, s); - a += b; - } - - static void II(ref uint a, uint b, uint c, uint d, uint x, uint s, uint ac) - @safe pure nothrow @nogc - { - a += I (b, c, d) + x + ac; - a = rol(a, s); - a += b; - } - - /* - * MD5 basic transformation. Transforms state based on block. - */ - - //Constants for MD5Transform routine. - enum - { - S11 = 7, - S12 = 12, - S13 = 17, - S14 = 22, - S21 = 5, - S22 = 9, - S23 = 14, - S24 = 20, - S31 = 4, - S32 = 11, - S33 = 16, - S34 = 23, - S41 = 6, - S42 = 10, - S43 = 15, - S44 = 21, - } - - private void transform(const(ubyte[64])* block) pure nothrow @nogc - { - uint a = _state[0], - b = _state[1], - c = _state[2], - d = _state[3]; - - uint[16] x = void; - - version (BigEndian) - { - import std.bitmanip : littleEndianToNative; - - for (size_t i = 0; i < 16; i++) - { - x[i] = littleEndianToNative!uint(*cast(ubyte[4]*)&(*block)[i*4]); - } - } - else - { - (cast(ubyte*) x.ptr)[0 .. 64] = (cast(ubyte*) block)[0 .. 64]; - } - - //Round 1 - FF (a, b, c, d, x[ 0], S11, 0xd76aa478); /* 1 */ - FF (d, a, b, c, x[ 1], S12, 0xe8c7b756); /* 2 */ - FF (c, d, a, b, x[ 2], S13, 0x242070db); /* 3 */ - FF (b, c, d, a, x[ 3], S14, 0xc1bdceee); /* 4 */ - FF (a, b, c, d, x[ 4], S11, 0xf57c0faf); /* 5 */ - FF (d, a, b, c, x[ 5], S12, 0x4787c62a); /* 6 */ - FF (c, d, a, b, x[ 6], S13, 0xa8304613); /* 7 */ - FF (b, c, d, a, x[ 7], S14, 0xfd469501); /* 8 */ - FF (a, b, c, d, x[ 8], S11, 0x698098d8); /* 9 */ - FF (d, a, b, c, x[ 9], S12, 0x8b44f7af); /* 10 */ - FF (c, d, a, b, x[10], S13, 0xffff5bb1); /* 11 */ - FF (b, c, d, a, x[11], S14, 0x895cd7be); /* 12 */ - FF (a, b, c, d, x[12], S11, 0x6b901122); /* 13 */ - FF (d, a, b, c, x[13], S12, 0xfd987193); /* 14 */ - FF (c, d, a, b, x[14], S13, 0xa679438e); /* 15 */ - FF (b, c, d, a, x[15], S14, 0x49b40821); /* 16 */ - - //Round 2 - GG (a, b, c, d, x[ 1], S21, 0xf61e2562); /* 17 */ - GG (d, a, b, c, x[ 6], S22, 0xc040b340); /* 18 */ - GG (c, d, a, b, x[11], S23, 0x265e5a51); /* 19 */ - GG (b, c, d, a, x[ 0], S24, 0xe9b6c7aa); /* 20 */ - GG (a, b, c, d, x[ 5], S21, 0xd62f105d); /* 21 */ - GG (d, a, b, c, x[10], S22, 0x2441453); /* 22 */ - GG (c, d, a, b, x[15], S23, 0xd8a1e681); /* 23 */ - GG (b, c, d, a, x[ 4], S24, 0xe7d3fbc8); /* 24 */ - GG (a, b, c, d, x[ 9], S21, 0x21e1cde6); /* 25 */ - GG (d, a, b, c, x[14], S22, 0xc33707d6); /* 26 */ - GG (c, d, a, b, x[ 3], S23, 0xf4d50d87); /* 27 */ - GG (b, c, d, a, x[ 8], S24, 0x455a14ed); /* 28 */ - GG (a, b, c, d, x[13], S21, 0xa9e3e905); /* 29 */ - GG (d, a, b, c, x[ 2], S22, 0xfcefa3f8); /* 30 */ - GG (c, d, a, b, x[ 7], S23, 0x676f02d9); /* 31 */ - GG (b, c, d, a, x[12], S24, 0x8d2a4c8a); /* 32 */ - - //Round 3 - HH (a, b, c, d, x[ 5], S31, 0xfffa3942); /* 33 */ - HH (d, a, b, c, x[ 8], S32, 0x8771f681); /* 34 */ - HH (c, d, a, b, x[11], S33, 0x6d9d6122); /* 35 */ - HH (b, c, d, a, x[14], S34, 0xfde5380c); /* 36 */ - HH (a, b, c, d, x[ 1], S31, 0xa4beea44); /* 37 */ - HH (d, a, b, c, x[ 4], S32, 0x4bdecfa9); /* 38 */ - HH (c, d, a, b, x[ 7], S33, 0xf6bb4b60); /* 39 */ - HH (b, c, d, a, x[10], S34, 0xbebfbc70); /* 40 */ - HH (a, b, c, d, x[13], S31, 0x289b7ec6); /* 41 */ - HH (d, a, b, c, x[ 0], S32, 0xeaa127fa); /* 42 */ - HH (c, d, a, b, x[ 3], S33, 0xd4ef3085); /* 43 */ - HH (b, c, d, a, x[ 6], S34, 0x4881d05); /* 44 */ - HH (a, b, c, d, x[ 9], S31, 0xd9d4d039); /* 45 */ - HH (d, a, b, c, x[12], S32, 0xe6db99e5); /* 46 */ - HH (c, d, a, b, x[15], S33, 0x1fa27cf8); /* 47 */ - HH (b, c, d, a, x[ 2], S34, 0xc4ac5665); /* 48 */ - - //Round 4 - II (a, b, c, d, x[ 0], S41, 0xf4292244); /* 49 */ - II (d, a, b, c, x[ 7], S42, 0x432aff97); /* 50 */ - II (c, d, a, b, x[14], S43, 0xab9423a7); /* 51 */ - II (b, c, d, a, x[ 5], S44, 0xfc93a039); /* 52 */ - II (a, b, c, d, x[12], S41, 0x655b59c3); /* 53 */ - II (d, a, b, c, x[ 3], S42, 0x8f0ccc92); /* 54 */ - II (c, d, a, b, x[10], S43, 0xffeff47d); /* 55 */ - II (b, c, d, a, x[ 1], S44, 0x85845dd1); /* 56 */ - II (a, b, c, d, x[ 8], S41, 0x6fa87e4f); /* 57 */ - II (d, a, b, c, x[15], S42, 0xfe2ce6e0); /* 58 */ - II (c, d, a, b, x[ 6], S43, 0xa3014314); /* 59 */ - II (b, c, d, a, x[13], S44, 0x4e0811a1); /* 60 */ - II (a, b, c, d, x[ 4], S41, 0xf7537e82); /* 61 */ - II (d, a, b, c, x[11], S42, 0xbd3af235); /* 62 */ - II (c, d, a, b, x[ 2], S43, 0x2ad7d2bb); /* 63 */ - II (b, c, d, a, x[ 9], S44, 0xeb86d391); /* 64 */ - - _state[0] += a; - _state[1] += b; - _state[2] += c; - _state[3] += d; - - //Zeroize sensitive information. - x[] = 0; - } - - public: - enum blockSize = 512; - - /** - * Use this to feed the digest with data. - * Also implements the $(REF isOutputRange, std,range,primitives) - * interface for `ubyte` and `const(ubyte)[]`. - * - * Example: - * ---- - * MD5 dig; - * dig.put(cast(ubyte) 0); //single ubyte - * dig.put(cast(ubyte) 0, cast(ubyte) 0); //variadic - * ubyte[10] buf; - * dig.put(buf); //buffer - * ---- - */ - void put(scope const(ubyte)[] data...) @trusted pure nothrow @nogc - { - size_t i; - uint index, partLen; - auto inputLen = data.length; - - //Compute number of bytes mod 64 - index = (cast(uint)_count >> 3) & (64 - 1); - - //Update number of bits - _count += inputLen * 8; - - partLen = 64 - index; - - //Transform as many times as possible - if (inputLen >= partLen) - { - (&_buffer[index])[0 .. partLen] = data.ptr[0 .. partLen]; - transform(&_buffer); - - for (i = partLen; i + 63 < inputLen; i += 64) - { - transform(cast(const(ubyte[64])*)(data[i .. i + 64].ptr)); - } - - index = 0; - } - else - { - i = 0; - } - - /* Buffer remaining input */ - if (inputLen - i) - (&_buffer[index])[0 .. inputLen-i] = (&data[i])[0 .. inputLen-i]; - } - - /** - * Used to (re)initialize the MD5 digest. - * - * Note: - * For this MD5 Digest implementation calling start after default construction - * is not necessary. Calling start is only necessary to reset the Digest. - * - * Generic code which deals with different Digest types should always call start though. - * - * Example: - * -------- - * MD5 digest; - * //digest.start(); //Not necessary - * digest.put(0); - * -------- - */ - void start() @safe pure nothrow @nogc - { - this = MD5.init; - } - - /** - * Returns the finished MD5 hash. This also calls $(LREF start) to - * reset the internal state. - */ - ubyte[16] finish() @trusted pure nothrow @nogc - { - import std.bitmanip : nativeToLittleEndian; - - ubyte[16] data = void; - ubyte[8] bits = void; - uint index, padLen; - - //Save number of bits - bits[0 .. 8] = nativeToLittleEndian(_count)[]; - - //Pad out to 56 mod 64 - index = (cast(uint)_count >> 3) & (64 - 1); - padLen = (index < 56) ? (56 - index) : (120 - index); - put(_padding[0 .. padLen]); - - //Append length (before padding) - put(bits); - - //Store state in digest - data[0 .. 4] = nativeToLittleEndian(_state[0])[]; - data[4 .. 8] = nativeToLittleEndian(_state[1])[]; - data[8 .. 12] = nativeToLittleEndian(_state[2])[]; - data[12 .. 16] = nativeToLittleEndian(_state[3])[]; - - /* Zeroize sensitive information. */ - start(); - return data; - } - /// - @safe unittest - { - //Simple example - MD5 hash; - hash.start(); - hash.put(cast(ubyte) 0); - ubyte[16] result = hash.finish(); - } -} - -/// -@safe unittest -{ - //Simple example, hashing a string using md5Of helper function - ubyte[16] hash = md5Of("abc"); - //Let's get a hash string - assert(toHexString(hash) == "900150983CD24FB0D6963F7D28E17F72"); -} - -/// -@safe unittest -{ - //Using the basic API - MD5 hash; - hash.start(); - ubyte[1024] data; - //Initialize data here... - hash.put(data); - ubyte[16] result = hash.finish(); -} - -/// -@safe unittest -{ - //Let's use the template features: - void doSomething(T)(ref T hash) - if (isDigest!T) - { - hash.put(cast(ubyte) 0); - } - MD5 md5; - md5.start(); - doSomething(md5); - assert(toHexString(md5.finish()) == "93B885ADFE0DA089CDF634904FD59F71"); -} - -@safe unittest -{ - assert(isDigest!MD5); -} - -@system unittest -{ - import std.range; - import std.conv : hexString; - - ubyte[16] digest; - - MD5 md5; - md5.put(cast(ubyte[])"abcdef"); - md5.start(); - md5.put(cast(ubyte[])""); - assert(md5.finish() == cast(ubyte[]) hexString!"d41d8cd98f00b204e9800998ecf8427e"); - - digest = md5Of(""); - assert(digest == cast(ubyte[]) hexString!"d41d8cd98f00b204e9800998ecf8427e"); - - digest = md5Of("a"); - assert(digest == cast(ubyte[]) hexString!"0cc175b9c0f1b6a831c399e269772661"); - - digest = md5Of("abc"); - assert(digest == cast(ubyte[]) hexString!"900150983cd24fb0d6963f7d28e17f72"); - - digest = md5Of("abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq"); - assert(digest == cast(ubyte[]) hexString!"8215ef0796a20bcaaae116d3876c664a"); - - digest = md5Of("message digest"); - assert(digest == cast(ubyte[]) hexString!"f96b697d7cb7938d525a2f31aaf161d0"); - - digest = md5Of("abcdefghijklmnopqrstuvwxyz"); - assert(digest == cast(ubyte[]) hexString!"c3fcd3d76192e4007dfb496cca67e13b"); - - digest = md5Of("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"); - assert(digest == cast(ubyte[]) hexString!"d174ab98d277d9f5a5611c2c9f419d9f"); - - digest = md5Of("1234567890123456789012345678901234567890"~ - "1234567890123456789012345678901234567890"); - assert(digest == cast(ubyte[]) hexString!"57edf4a22be3c955ac49da2e2107b67a"); - - enum ubyte[16] input = cast(ubyte[16]) hexString!"c3fcd3d76192e4007dfb496cca67e13b"; - assert(toHexString(input) - == "C3FCD3D76192E4007DFB496CCA67E13B"); - - ubyte[] onemilliona = new ubyte[1000000]; - onemilliona[] = 'a'; - digest = md5Of(onemilliona); - assert(digest == cast(ubyte[]) hexString!"7707D6AE4E027C70EEA2A935C2296F21"); - - auto oneMillionRange = repeat!ubyte(cast(ubyte)'a', 1000000); - digest = md5Of(oneMillionRange); - assert(digest == cast(ubyte[]) hexString!"7707D6AE4E027C70EEA2A935C2296F21"); -} - -/** - * This is a convenience alias for $(REF digest, std,digest) using the - * MD5 implementation. - */ -//simple alias doesn't work here, hope this gets inlined... -auto md5Of(T...)(T data) -{ - return digest!(MD5, T)(data); -} - -/// -@safe unittest -{ - ubyte[16] hash = md5Of("abc"); - assert(hash == digest!MD5("abc")); -} - -/** - * OOP API MD5 implementation. - * See `std.digest` for differences between template and OOP API. - * - * This is an alias for $(D $(REF WrapperDigest, std,digest)!MD5), see - * there for more information. - */ -alias MD5Digest = WrapperDigest!MD5; - -/// -@safe unittest -{ - //Simple example, hashing a string using Digest.digest helper function - auto md5 = new MD5Digest(); - ubyte[] hash = md5.digest("abc"); - //Let's get a hash string - assert(toHexString(hash) == "900150983CD24FB0D6963F7D28E17F72"); -} - -/// -@system unittest -{ - //Let's use the OOP features: - void test(Digest dig) - { - dig.put(cast(ubyte) 0); - } - auto md5 = new MD5Digest(); - test(md5); - - //Let's use a custom buffer: - ubyte[16] buf; - ubyte[] result = md5.finish(buf[]); - assert(toHexString(result) == "93B885ADFE0DA089CDF634904FD59F71"); -} - -@system unittest -{ - import std.conv : hexString; - auto md5 = new MD5Digest(); - - md5.put(cast(ubyte[])"abcdef"); - md5.reset(); - md5.put(cast(ubyte[])""); - assert(md5.finish() == cast(ubyte[]) hexString!"d41d8cd98f00b204e9800998ecf8427e"); - - md5.put(cast(ubyte[])"abcdefghijklmnopqrstuvwxyz"); - ubyte[20] result; - auto result2 = md5.finish(result[]); - assert(result[0 .. 16] == result2 && result2 == cast(ubyte[]) hexString!"c3fcd3d76192e4007dfb496cca67e13b"); - - debug - { - import std.exception; - assertThrown!Error(md5.finish(result[0 .. 15])); - } - - assert(md5.length == 16); - - assert(md5.digest("") == cast(ubyte[]) hexString!"d41d8cd98f00b204e9800998ecf8427e"); - - assert(md5.digest("a") == cast(ubyte[]) hexString!"0cc175b9c0f1b6a831c399e269772661"); - - assert(md5.digest("abc") == cast(ubyte[]) hexString!"900150983cd24fb0d6963f7d28e17f72"); - - assert(md5.digest("abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq") - == cast(ubyte[]) hexString!"8215ef0796a20bcaaae116d3876c664a"); - - assert(md5.digest("message digest") == cast(ubyte[]) hexString!"f96b697d7cb7938d525a2f31aaf161d0"); - - assert(md5.digest("abcdefghijklmnopqrstuvwxyz") - == cast(ubyte[]) hexString!"c3fcd3d76192e4007dfb496cca67e13b"); - - assert(md5.digest("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789") - == cast(ubyte[]) hexString!"d174ab98d277d9f5a5611c2c9f419d9f"); - - assert(md5.digest("1234567890123456789012345678901234567890", - "1234567890123456789012345678901234567890") - == cast(ubyte[]) hexString!"57edf4a22be3c955ac49da2e2107b67a"); -} diff --git a/phobos/std/digest/murmurhash.d b/phobos/std/digest/murmurhash.d deleted file mode 100644 index 4ae3443..0000000 --- a/phobos/std/digest/murmurhash.d +++ /dev/null @@ -1,843 +0,0 @@ -/** -Computes $(LINK2 https://en.wikipedia.org/wiki/MurmurHash, MurmurHash) hashes -of arbitrary data. MurmurHash is a non-cryptographic hash function suitable -for general hash-based lookup. It is optimized for x86 but can be used on -all architectures. - -The current version is MurmurHash3, which yields a 32-bit or 128-bit hash value. -The older MurmurHash 1 and 2 are currently not supported. - -MurmurHash3 comes in three flavors, listed in increasing order of throughput: -$(UL -$(LI `MurmurHash3!32` produces a 32-bit value and is optimized for 32-bit architectures) -$(LI $(D MurmurHash3!(128, 32)) produces a 128-bit value and is optimized for 32-bit architectures) -$(LI $(D MurmurHash3!(128, 64)) produces a 128-bit value and is optimized for 64-bit architectures) -) - -Note: -$(UL -$(LI $(D MurmurHash3!(128, 32)) and $(D MurmurHash3!(128, 64)) produce different values.) -$(LI The current implementation is optimized for little endian architectures. - It will exhibit different results on big endian architectures and a slightly - less uniform distribution.) -) - -This module conforms to the APIs defined in $(MREF std, digest). - -This module publicly imports $(MREF std, digest) and can be used as a stand-alone module. - -Source: $(PHOBOSSRC std/digest/murmurhash.d) -License: $(HTTP www.boost.org/LICENSE_1_0.txt, Boost License 1.0). -Authors: Guillaume Chatelet -References: $(LINK2 https://github.com/aappleby/smhasher, Reference implementation) -$(BR) $(LINK2 https://en.wikipedia.org/wiki/MurmurHash, Wikipedia) -*/ -/* Copyright Guillaume Chatelet 2016. - * Distributed under the Boost Software License, Version 1.0. - * (See LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) - */ -module std.digest.murmurhash; - -/// -@safe unittest -{ - // MurmurHash3!32, MurmurHash3!(128, 32) and MurmurHash3!(128, 64) implement - // the std.digest Template API. - static assert(isDigest!(MurmurHash3!32)); - // The convenient digest template allows for quick hashing of any data. - ubyte[4] hashed = digest!(MurmurHash3!32)([1, 2, 3, 4]); - assert(hashed == [0, 173, 69, 68]); -} - -/// -@safe unittest -{ - // One can also hash ubyte data piecewise by instanciating a hasher and call - // the 'put' method. - const(ubyte)[] data1 = [1, 2, 3]; - const(ubyte)[] data2 = [4, 5, 6, 7]; - // The incoming data will be buffered and hashed element by element. - MurmurHash3!32 hasher; - hasher.put(data1); - hasher.put(data2); - // The call to 'finish' ensures: - // - the remaining bits are processed - // - the hash gets finalized - auto hashed = hasher.finish(); - assert(hashed == [181, 151, 88, 252]); -} - -/// -@safe unittest -{ - // Using `putElements`, `putRemainder` and `finalize` you gain full - // control over which part of the algorithm to run. - // This allows for maximum throughput but needs extra care. - - // Data type must be the same as the hasher's element type: - // - uint for MurmurHash3!32 - // - uint[4] for MurmurHash3!(128, 32) - // - ulong[2] for MurmurHash3!(128, 64) - const(uint)[] data = [1, 2, 3, 4]; - // Note the hasher starts with 'Fast'. - MurmurHash3!32 hasher; - // Push as many array of elements as you need. The less calls the better. - hasher.putElements(data); - // Put remainder bytes if needed. This method can be called only once. - hasher.putRemainder(ubyte(1), ubyte(1), ubyte(1)); - // Call finalize to incorporate data length in the hash. - hasher.finalize(); - // Finally get the hashed value. - auto hashed = hasher.getBytes(); - assert(hashed == [188, 165, 108, 2]); -} - -version (X86) - version = HaveUnalignedLoads; -else version (X86_64) - version = HaveUnalignedLoads; - -public import std.digest; - -@safe: - -/* -Performance notes: - - To help a bit with the performance when compiling with DMD some - functions have been rewritten to pass by value instead of by reference. - - GDC and LDC are on par with their C++ counterpart. - - DMD is typically between 20% to 50% of the GCC version. -*/ - -/++ - + Implements the MurmurHash3 functions. You can specify the `size` of the - + hash in bit. For 128 bit hashes you can specify whether to optimize for 32 - + or 64 bit architectures. If you don't specify the `opt` value it will select - + the fastest version of the host platform. - + - + This hasher is compatible with the `Digest` API: - + $(UL - + $(LI `void start()`) - + $(LI `void put(scope const(ubyte)[] data...)`) - + $(LI `ubyte[Element.sizeof] finish()`) - + ) - + - + It also provides a faster, low level API working with data of size - + `Element.sizeof`: - + $(UL - + $(LI `void putElements(scope const(Element[]) elements...)`) - + $(LI `void putRemainder(scope const(ubyte[]) data...)`) - + $(LI `void finalize()`) - + $(LI `Element get()`) - + $(LI `ubyte[Element.sizeof] getBytes()`) - + ) - +/ -struct MurmurHash3(uint size /* 32 or 128 */ , uint opt = size_t.sizeof == 8 ? 64 : 32) -{ - enum blockSize = size; // Number of bits of the hashed value. - size_t element_count; // The number of full elements pushed, this is used for finalization. - - static if (size == 32) - { - private enum uint c1 = 0xcc9e2d51; - private enum uint c2 = 0x1b873593; - private uint h1; - alias Element = uint; /// The element type for 32-bit implementation. - - this(uint seed) - { - h1 = seed; - } - /++ - Adds a single Element of data without increasing `element_count`. - Make sure to increase `element_count` by `Element.sizeof` for each call to `putElement`. - +/ - void putElement(uint block) pure nothrow @nogc - { - h1 = update(h1, block, 0, c1, c2, 15, 13, 0xe6546b64U); - } - - /// Put remainder bytes. This must be called only once after `putElement` and before `finalize`. - void putRemainder(scope const(ubyte[]) data...) pure nothrow @nogc - { - assert(data.length < Element.sizeof); - assert(data.length >= 0); - element_count += data.length; - uint k1 = 0; - final switch (data.length & 3) - { - case 3: - k1 ^= data[2] << 16; - goto case; - case 2: - k1 ^= data[1] << 8; - goto case; - case 1: - k1 ^= data[0]; - h1 ^= shuffle(k1, c1, c2, 15); - goto case; - case 0: - } - } - - /// Incorporate `element_count` and finalizes the hash. - void finalize() pure nothrow @nogc - { - h1 ^= element_count; - h1 = fmix(h1); - } - - /// Returns the hash as an uint value. - Element get() pure nothrow @nogc - { - return h1; - } - - /// Returns the current hashed value as an ubyte array. - ubyte[4] getBytes() pure nothrow @nogc - { - return cast(typeof(return)) cast(uint[1])[get()]; - } - } - else static if (size == 128 && opt == 32) - { - private enum uint c1 = 0x239b961b; - private enum uint c2 = 0xab0e9789; - private enum uint c3 = 0x38b34ae5; - private enum uint c4 = 0xa1e38b93; - private uint h4, h3, h2, h1; - - alias Element = uint[4]; /// The element type for 128-bit implementation. - - this(uint seed4, uint seed3, uint seed2, uint seed1) pure nothrow @nogc - { - h4 = seed4; - h3 = seed3; - h2 = seed2; - h1 = seed1; - } - - this(uint seed) pure nothrow @nogc - { - h4 = h3 = h2 = h1 = seed; - } - - /++ - Adds a single Element of data without increasing element_count. - Make sure to increase `element_count` by `Element.sizeof` for each call to `putElement`. - +/ - void putElement(Element block) pure nothrow @nogc - { - h1 = update(h1, block[0], h2, c1, c2, 15, 19, 0x561ccd1bU); - h2 = update(h2, block[1], h3, c2, c3, 16, 17, 0x0bcaa747U); - h3 = update(h3, block[2], h4, c3, c4, 17, 15, 0x96cd1c35U); - h4 = update(h4, block[3], h1, c4, c1, 18, 13, 0x32ac3b17U); - } - - /// Put remainder bytes. This must be called only once after `putElement` and before `finalize`. - void putRemainder(scope const(ubyte[]) data...) pure nothrow @nogc - { - assert(data.length < Element.sizeof); - assert(data.length >= 0); - element_count += data.length; - uint k1 = 0; - uint k2 = 0; - uint k3 = 0; - uint k4 = 0; - - final switch (data.length & 15) - { - case 15: - k4 ^= data[14] << 16; - goto case; - case 14: - k4 ^= data[13] << 8; - goto case; - case 13: - k4 ^= data[12] << 0; - h4 ^= shuffle(k4, c4, c1, 18); - goto case; - case 12: - k3 ^= data[11] << 24; - goto case; - case 11: - k3 ^= data[10] << 16; - goto case; - case 10: - k3 ^= data[9] << 8; - goto case; - case 9: - k3 ^= data[8] << 0; - h3 ^= shuffle(k3, c3, c4, 17); - goto case; - case 8: - k2 ^= data[7] << 24; - goto case; - case 7: - k2 ^= data[6] << 16; - goto case; - case 6: - k2 ^= data[5] << 8; - goto case; - case 5: - k2 ^= data[4] << 0; - h2 ^= shuffle(k2, c2, c3, 16); - goto case; - case 4: - k1 ^= data[3] << 24; - goto case; - case 3: - k1 ^= data[2] << 16; - goto case; - case 2: - k1 ^= data[1] << 8; - goto case; - case 1: - k1 ^= data[0] << 0; - h1 ^= shuffle(k1, c1, c2, 15); - goto case; - case 0: - } - } - - /// Incorporate `element_count` and finalizes the hash. - void finalize() pure nothrow @nogc - { - h1 ^= element_count; - h2 ^= element_count; - h3 ^= element_count; - h4 ^= element_count; - - h1 += h2; - h1 += h3; - h1 += h4; - h2 += h1; - h3 += h1; - h4 += h1; - - h1 = fmix(h1); - h2 = fmix(h2); - h3 = fmix(h3); - h4 = fmix(h4); - - h1 += h2; - h1 += h3; - h1 += h4; - h2 += h1; - h3 += h1; - h4 += h1; - } - - /// Returns the hash as an uint[4] value. - Element get() pure nothrow @nogc - { - return [h1, h2, h3, h4]; - } - - /// Returns the current hashed value as an ubyte array. - ubyte[16] getBytes() pure nothrow @nogc - { - return cast(typeof(return)) get(); - } - } - else static if (size == 128 && opt == 64) - { - private enum ulong c1 = 0x87c37b91114253d5; - private enum ulong c2 = 0x4cf5ad432745937f; - private ulong h2, h1; - - alias Element = ulong[2]; /// The element type for 128-bit implementation. - - this(ulong seed) pure nothrow @nogc - { - h2 = h1 = seed; - } - - this(ulong seed2, ulong seed1) pure nothrow @nogc - { - h2 = seed2; - h1 = seed1; - } - - /++ - Adds a single Element of data without increasing `element_count`. - Make sure to increase `element_count` by `Element.sizeof` for each call to `putElement`. - +/ - void putElement(Element block) pure nothrow @nogc - { - h1 = update(h1, block[0], h2, c1, c2, 31, 27, 0x52dce729U); - h2 = update(h2, block[1], h1, c2, c1, 33, 31, 0x38495ab5U); - } - - /// Put remainder bytes. This must be called only once after `putElement` and before `finalize`. - void putRemainder(scope const(ubyte[]) data...) pure nothrow @nogc - { - assert(data.length < Element.sizeof); - assert(data.length >= 0); - element_count += data.length; - ulong k1 = 0; - ulong k2 = 0; - final switch (data.length & 15) - { - case 15: - k2 ^= ulong(data[14]) << 48; - goto case; - case 14: - k2 ^= ulong(data[13]) << 40; - goto case; - case 13: - k2 ^= ulong(data[12]) << 32; - goto case; - case 12: - k2 ^= ulong(data[11]) << 24; - goto case; - case 11: - k2 ^= ulong(data[10]) << 16; - goto case; - case 10: - k2 ^= ulong(data[9]) << 8; - goto case; - case 9: - k2 ^= ulong(data[8]) << 0; - h2 ^= shuffle(k2, c2, c1, 33); - goto case; - case 8: - k1 ^= ulong(data[7]) << 56; - goto case; - case 7: - k1 ^= ulong(data[6]) << 48; - goto case; - case 6: - k1 ^= ulong(data[5]) << 40; - goto case; - case 5: - k1 ^= ulong(data[4]) << 32; - goto case; - case 4: - k1 ^= ulong(data[3]) << 24; - goto case; - case 3: - k1 ^= ulong(data[2]) << 16; - goto case; - case 2: - k1 ^= ulong(data[1]) << 8; - goto case; - case 1: - k1 ^= ulong(data[0]) << 0; - h1 ^= shuffle(k1, c1, c2, 31); - goto case; - case 0: - } - } - - /// Incorporate `element_count` and finalizes the hash. - void finalize() pure nothrow @nogc - { - h1 ^= element_count; - h2 ^= element_count; - - h1 += h2; - h2 += h1; - h1 = fmix(h1); - h2 = fmix(h2); - h1 += h2; - h2 += h1; - } - - /// Returns the hash as an ulong[2] value. - Element get() pure nothrow @nogc - { - return [h1, h2]; - } - - /// Returns the current hashed value as an ubyte array. - ubyte[16] getBytes() pure nothrow @nogc - { - return cast(typeof(return)) get(); - } - } - else - { - alias Element = char; // This is needed to trigger the following error message. - static assert(false, "MurmurHash3(" ~ size.stringof ~ ", " ~ opt.stringof ~ ") is not implemented"); - } - - /++ - Pushes an array of elements at once. It is more efficient to push as much data as possible in a single call. - On platforms that do not support unaligned reads (MIPS or old ARM chips), the compiler may produce slower code to ensure correctness. - +/ - void putElements(scope const(Element[]) elements...) pure nothrow @nogc - { - foreach (const block; elements) - { - putElement(block); - } - element_count += elements.length * Element.sizeof; - } - - //------------------------------------------------------------------------- - // Implementation of the Digest API. - //------------------------------------------------------------------------- - - private union BufferUnion - { - Element block; - ubyte[Element.sizeof] data; - } - - private BufferUnion buffer; - private size_t bufferSize; - - @disable this(this); - - // Initialize - void start() - { - this = this.init; - } - - /++ - Adds data to the digester. This function can be called many times in a row - after start but before finish. - +/ - void put(scope const(ubyte)[] data...) pure nothrow - { - // Buffer should never be full while entering this function. - assert(bufferSize < Element.sizeof); - - // Check if the incoming data doesn't fill up a whole block buffer. - if (bufferSize + data.length < Element.sizeof) - { - buffer.data[bufferSize .. bufferSize + data.length] = data[]; - bufferSize += data.length; - return; - } - - // Check if there's some leftover data in the first block buffer, and - // fill the remaining space first. - if (bufferSize != 0) - { - const bufferLeeway = Element.sizeof - bufferSize; - buffer.data[bufferSize .. $] = data[0 .. bufferLeeway]; - putElement(buffer.block); - element_count += Element.sizeof; - data = data[bufferLeeway .. $]; - } - - // Do main work: process chunks of `Element.sizeof` bytes. - const numElements = data.length / Element.sizeof; - const remainderStart = numElements * Element.sizeof; - version (HaveUnalignedLoads) - { - foreach (ref const Element block; cast(const(Element[])) data[0 .. remainderStart]) - { - putElement(block); - } - } - else - { - void processChunks(T)() @trusted - { - alias TChunk = T[Element.sizeof / T.sizeof]; - foreach (ref const chunk; cast(const(TChunk[])) data[0 .. remainderStart]) - { - static if (T.alignof >= Element.alignof) - { - putElement(*cast(const(Element)*) chunk.ptr); - } - else - { - Element[1] alignedCopy = void; - version (LDC) - { - import ldc.intrinsics : llvm_memcpy; - llvm_memcpy(alignedCopy.ptr, chunk.ptr, Element.sizeof, T.alignof); - } - else - { - (cast(T[]) alignedCopy)[] = chunk[]; - } - putElement(alignedCopy[0]); - } - } - } - - const startAddress = cast(size_t) data.ptr; - static if (size >= 64) - { - if ((startAddress & 7) == 0) - { - processChunks!ulong(); - goto L_end; - } - } - static assert(size >= 32); - if ((startAddress & 3) == 0) - processChunks!uint(); - else if ((startAddress & 1) == 0) - processChunks!ushort(); - else - processChunks!ubyte(); - -L_end: - } - element_count += numElements * Element.sizeof; - data = data[remainderStart .. $]; - - // Now add remaining data to buffer. - assert(data.length < Element.sizeof); - bufferSize = data.length; - buffer.data[0 .. data.length] = data[]; - } - - /++ - Finalizes the computation of the hash and returns the computed value. - Note that `finish` can be called only once and that no subsequent calls - to `put` is allowed. - +/ - ubyte[Element.sizeof] finish() pure nothrow - { - auto tail = buffer.data[0 .. bufferSize]; - if (tail.length > 0) - { - putRemainder(tail); - } - finalize(); - return getBytes(); - } - - //------------------------------------------------------------------------- - // MurmurHash3 utils - //------------------------------------------------------------------------- - - private T rotl(T)(T x, uint y) - in - { - import std.traits : isUnsigned; - - static assert(isUnsigned!T); - debug assert(y >= 0 && y <= (T.sizeof * 8)); - } - do - { - return ((x << y) | (x >> ((T.sizeof * 8) - y))); - } - - private T shuffle(T)(T k, T c1, T c2, ubyte r1) - { - import std.traits : isUnsigned; - - static assert(isUnsigned!T); - k *= c1; - k = rotl(k, r1); - k *= c2; - return k; - } - - private T update(T)(ref T h, T k, T mixWith, T c1, T c2, ubyte r1, ubyte r2, T n) - { - import std.traits : isUnsigned; - - static assert(isUnsigned!T); - h ^= shuffle(k, c1, c2, r1); - h = rotl(h, r2); - h += mixWith; - return h * 5 + n; - } - - private uint fmix(uint h) pure nothrow @nogc - { - h ^= h >> 16; - h *= 0x85ebca6b; - h ^= h >> 13; - h *= 0xc2b2ae35; - h ^= h >> 16; - return h; - } - - private ulong fmix(ulong k) pure nothrow @nogc - { - k ^= k >> 33; - k *= 0xff51afd7ed558ccd; - k ^= k >> 33; - k *= 0xc4ceb9fe1a85ec53; - k ^= k >> 33; - return k; - } -} - - -/// The convenient digest template allows for quick hashing of any data. -@safe unittest -{ - ubyte[4] hashed = digest!(MurmurHash3!32)([1, 2, 3, 4]); - assert(hashed == [0, 173, 69, 68]); -} - -/** -One can also hash ubyte data piecewise by instanciating a hasher and call -the 'put' method. -*/ -@safe unittest -{ - const(ubyte)[] data1 = [1, 2, 3]; - const(ubyte)[] data2 = [4, 5, 6, 7]; - // The incoming data will be buffered and hashed element by element. - MurmurHash3!32 hasher; - hasher.put(data1); - hasher.put(data2); - // The call to 'finish' ensures: - // - the remaining bits are processed - // - the hash gets finalized - auto hashed = hasher.finish(); - assert(hashed == [181, 151, 88, 252]); -} - -version (StdUnittest) -{ - private auto hash(H, Element = H.Element)(string data) - { - H hasher; - immutable elements = data.length / Element.sizeof; - hasher.putElements(cast(const(Element)[]) data[0 .. elements * Element.sizeof]); - hasher.putRemainder(cast(const(ubyte)[]) data[elements * Element.sizeof .. $]); - hasher.finalize(); - return hasher.getBytes(); - } - - private void checkResult(H)(in string[string] groundtruth) - { - foreach (data, expectedHash; groundtruth) - { - assert(data.digest!H.toHexString() == expectedHash); - assert(data.hash!H.toHexString() == expectedHash); - H hasher; - foreach (element; data) - { - hasher.put(element); - } - assert(hasher.finish.toHexString() == expectedHash); - } - } -} - -@safe unittest -{ - // dfmt off - checkResult!(MurmurHash3!32)([ - "" : "00000000", - "a" : "B269253C", - "ab" : "5FD7BF9B", - "abc" : "FA93DDB3", - "abcd" : "6A67ED43", - "abcde" : "F69A9BE8", - "abcdef" : "85C08161", - "abcdefg" : "069B3C88", - "abcdefgh" : "C4CCDD49", - "abcdefghi" : "F0061442", - "abcdefghij" : "91779288", - "abcdefghijk" : "DF253B5F", - "abcdefghijkl" : "273D6FA3", - "abcdefghijklm" : "1B1612F2", - "abcdefghijklmn" : "F06D52F8", - "abcdefghijklmno" : "D2F7099D", - "abcdefghijklmnop" : "ED9162E7", - "abcdefghijklmnopq" : "4A5E65B6", - "abcdefghijklmnopqr" : "94A819C2", - "abcdefghijklmnopqrs" : "C15BBF85", - "abcdefghijklmnopqrst" : "9A711CBE", - "abcdefghijklmnopqrstu" : "ABE7195A", - "abcdefghijklmnopqrstuv" : "C73CB670", - "abcdefghijklmnopqrstuvw" : "1C4D1EA5", - "abcdefghijklmnopqrstuvwx" : "3939F9B0", - "abcdefghijklmnopqrstuvwxy" : "1A568338", - "abcdefghijklmnopqrstuvwxyz" : "6D034EA3"]); - // dfmt on -} - -@safe unittest -{ - // dfmt off - checkResult!(MurmurHash3!(128,32))([ - "" : "00000000000000000000000000000000", - "a" : "3C9394A71BB056551BB056551BB05655", - "ab" : "DF5184151030BE251030BE251030BE25", - "abc" : "D1C6CD75A506B0A2A506B0A2A506B0A2", - "abcd" : "AACCB6962EC6AF452EC6AF452EC6AF45", - "abcde" : "FB2E40C5BCC5245D7701725A7701725A", - "abcdef" : "0AB97CE12127AFA1F9DFBEA9F9DFBEA9", - "abcdefg" : "D941B590DE3A86092869774A2869774A", - "abcdefgh" : "3611F4AE8714B1AD92806CFA92806CFA", - "abcdefghi" : "1C8C05AD6F590622107DD2147C4194DD", - "abcdefghij" : "A72ED9F50E90379A2AAA92C77FF12F69", - "abcdefghijk" : "DDC9C8A01E111FCA2DF1FE8257975EBD", - "abcdefghijkl" : "FE038573C02482F4ADDFD42753E58CD2", - "abcdefghijklm" : "15A23AC1ECA1AEDB66351CF470DE2CD9", - "abcdefghijklmn" : "8E11EC75D71F5D60F4456F944D89D4F1", - "abcdefghijklmno" : "691D6DEEAED51A4A5714CE84A861A7AD", - "abcdefghijklmnop" : "2776D29F5612B990218BCEE445BA93D1", - "abcdefghijklmnopq" : "D3A445046F5C51642ADC6DD99D07111D", - "abcdefghijklmnopqr" : "AA5493A0DA291D966A9E7128585841D9", - "abcdefghijklmnopqrs" : "281B6A4F9C45B9BFC3B77850930F2C20", - "abcdefghijklmnopqrst" : "19342546A8216DB62873B49E545DCB1F", - "abcdefghijklmnopqrstu" : "A6C0F30D6C738620E7B9590D2E088D99", - "abcdefghijklmnopqrstuv" : "A7D421D9095CDCEA393CBBA908342384", - "abcdefghijklmnopqrstuvw" : "C3A93D572B014949317BAD7EE809158F", - "abcdefghijklmnopqrstuvwx" : "802381D77956833791F87149326E4801", - "abcdefghijklmnopqrstuvwxy" : "0AC619A5302315755A80D74ADEFAA842", - "abcdefghijklmnopqrstuvwxyz" : "1306343E662F6F666E56F6172C3DE344"]); - // dfmt on -} - -@safe unittest -{ - // dfmt off - checkResult!(MurmurHash3!(128,64))([ - "" : "00000000000000000000000000000000", - "a" : "897859F6655555855A890E51483AB5E6", - "ab" : "2E1BED16EA118B93ADD4529B01A75EE6", - "abc" : "6778AD3F3F3F96B4522DCA264174A23B", - "abcd" : "4FCD5646D6B77BB875E87360883E00F2", - "abcde" : "B8BB96F491D036208CECCF4BA0EEC7C5", - "abcdef" : "55BFA3ACBF867DE45C842133990971B0", - "abcdefg" : "99E49EC09F2FCDA6B6BB55B13AA23A1C", - "abcdefgh" : "028CEF37B00A8ACCA14069EB600D8948", - "abcdefghi" : "64793CF1CFC0470533E041B7F53DB579", - "abcdefghij" : "998C2F770D5BC1B6C91A658CDC854DA2", - "abcdefghijk" : "029D78DFB8D095A871E75A45E2317CBB", - "abcdefghijkl" : "94E17AE6B19BF38E1C62FF7232309E1F", - "abcdefghijklm" : "73FAC0A78D2848167FCCE70DFF7B652E", - "abcdefghijklmn" : "E075C3F5A794D09124336AD2276009EE", - "abcdefghijklmno" : "FB2F0C895124BE8A612A969C2D8C546A", - "abcdefghijklmnop" : "23B74C22A33CCAC41AEB31B395D63343", - "abcdefghijklmnopq" : "57A6BD887F746475E40D11A19D49DAEC", - "abcdefghijklmnopqr" : "508A7F90EC8CF0776BC7005A29A8D471", - "abcdefghijklmnopqrs" : "886D9EDE23BC901574946FB62A4D8AA6", - "abcdefghijklmnopqrst" : "F1E237F926370B314BD016572AF40996", - "abcdefghijklmnopqrstu" : "3CC9FF79E268D5C9FB3C9BE9C148CCD7", - "abcdefghijklmnopqrstuv" : "56F8ABF430E388956DA9F4A8741FDB46", - "abcdefghijklmnopqrstuvw" : "8E234F9DBA0A4840FFE9541CEBB7BE83", - "abcdefghijklmnopqrstuvwx" : "F72CDED40F96946408F22153A3CF0F79", - "abcdefghijklmnopqrstuvwxy" : "0F96072FA4CBE771DBBD9E398115EEED", - "abcdefghijklmnopqrstuvwxyz" : "A94A6F517E9D9C7429D5A7B6899CADE9"]); - // dfmt on -} - -@safe unittest -{ - // Pushing unaligned data and making sure the result is still coherent. - void testUnalignedHash(H)() - { - immutable ubyte[1028] data = 0xAC; - immutable alignedHash = digest!H(data[0 .. 1024]); - foreach (i; 1 .. 5) - { - immutable unalignedHash = digest!H(data[i .. 1024 + i]); - assert(alignedHash == unalignedHash); - } - } - - testUnalignedHash!(MurmurHash3!32)(); - testUnalignedHash!(MurmurHash3!(128, 32))(); - testUnalignedHash!(MurmurHash3!(128, 64))(); -} diff --git a/phobos/std/digest/package.d b/phobos/std/digest/package.d deleted file mode 100644 index ea3738b..0000000 --- a/phobos/std/digest/package.d +++ /dev/null @@ -1,1214 +0,0 @@ -/** - * This module describes the digest APIs used in Phobos. All digests follow - * these APIs. Additionally, this module contains useful helper methods which - * can be used with every digest type. - * -$(SCRIPT inhibitQuickIndex = 1;) - -$(DIVC quickindex, -$(BOOKTABLE , -$(TR $(TH Category) $(TH Functions) -) -$(TR $(TDNW Template API) $(TD $(MYREF isDigest) $(MYREF DigestType) $(MYREF hasPeek) - $(MYREF hasBlockSize) - $(MYREF ExampleDigest) $(MYREF digest) $(MYREF hexDigest) $(MYREF makeDigest) -) -) -$(TR $(TDNW OOP API) $(TD $(MYREF Digest) -) -) -$(TR $(TDNW Helper functions) $(TD $(MYREF toHexString) $(MYREF secureEqual)) -) -$(TR $(TDNW Implementation helpers) $(TD $(MYREF digestLength) $(MYREF WrapperDigest)) -) -) -) - - * APIs: - * There are two APIs for digests: The template API and the OOP API. The template API uses structs - * and template helpers like $(LREF isDigest). The OOP API implements digests as classes inheriting - * the $(LREF Digest) interface. All digests are named so that the template API struct is called "$(B x)" - * and the OOP API class is called "$(B x)Digest". For example we have `MD5` <--> `MD5Digest`, - * `CRC32` <--> `CRC32Digest`, etc. - * - * The template API is slightly more efficient. It does not have to allocate memory dynamically, - * all memory is allocated on the stack. The OOP API has to allocate in the finish method if no - * buffer was provided. If you provide a buffer to the OOP APIs finish function, it doesn't allocate, - * but the $(LREF Digest) classes still have to be created using `new` which allocates them using the GC. - * - * The OOP API is useful to change the digest function and/or digest backend at 'runtime'. The benefit here - * is that switching e.g. Phobos MD5Digest and an OpenSSLMD5Digest implementation is ABI compatible. - * - * If just one specific digest type and backend is needed, the template API is usually a good fit. - * In this simplest case, the template API can even be used without templates: Just use the "$(B x)" structs - * directly. - * - * License: $(HTTP www.boost.org/LICENSE_1_0.txt, Boost License 1.0). - * Authors: - * Johannes Pfau - * - * Source: $(PHOBOSSRC std/digest/package.d) - * - * CTFE: - * Digests do not work in CTFE - * - * TODO: - * Digesting single bits (as opposed to bytes) is not implemented. This will be done as another - * template constraint helper (hasBitDigesting!T) and an additional interface (BitDigest) - */ -/* Copyright Johannes Pfau 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.digest; - -public import std.ascii : LetterCase; -import std.meta : allSatisfy; -import std.range.primitives; -import std.traits; - - -/// -@system unittest -{ - import std.digest.crc; - - //Simple example - char[8] hexHash = hexDigest!CRC32("The quick brown fox jumps over the lazy dog"); - assert(hexHash == "39A34F41"); - - //Simple example, using the API manually - CRC32 context = makeDigest!CRC32(); - context.put(cast(ubyte[])"The quick brown fox jumps over the lazy dog"); - ubyte[4] hash = context.finish(); - assert(toHexString(hash) == "39A34F41"); -} - -/// -@system unittest -{ - //Generating the hashes of a file, idiomatic D way - import std.digest.crc, std.digest.md, std.digest.sha; - import std.stdio; - - // Digests a file and prints the result. - void digestFile(Hash)(string filename) - if (isDigest!Hash) - { - auto file = File(filename); - auto result = digest!Hash(file.byChunk(4096 * 1024)); - writefln("%s (%s) = %s", Hash.stringof, filename, toHexString(result)); - } - - void main(string[] args) - { - foreach (name; args[1 .. $]) - { - digestFile!MD5(name); - digestFile!SHA1(name); - digestFile!CRC32(name); - } - } -} -/// -@system unittest -{ - //Generating the hashes of a file using the template API - import std.digest.crc, std.digest.md, std.digest.sha; - import std.stdio; - // Digests a file and prints the result. - void digestFile(Hash)(ref Hash hash, string filename) - if (isDigest!Hash) - { - File file = File(filename); - - //As digests imlement OutputRange, we could use std.algorithm.copy - //Let's do it manually for now - foreach (buffer; file.byChunk(4096 * 1024)) - hash.put(buffer); - - auto result = hash.finish(); - writefln("%s (%s) = %s", Hash.stringof, filename, toHexString(result)); - } - - void uMain(string[] args) - { - MD5 md5; - SHA1 sha1; - CRC32 crc32; - - md5.start(); - sha1.start(); - crc32.start(); - - foreach (arg; args[1 .. $]) - { - digestFile(md5, arg); - digestFile(sha1, arg); - digestFile(crc32, arg); - } - } -} - -/// -@system unittest -{ - import std.digest.crc, std.digest.md, std.digest.sha; - import std.stdio; - - // Digests a file and prints the result. - void digestFile(Digest hash, string filename) - { - File file = File(filename); - - //As digests implement OutputRange, we could use std.algorithm.copy - //Let's do it manually for now - foreach (buffer; file.byChunk(4096 * 1024)) - hash.put(buffer); - - ubyte[] result = hash.finish(); - writefln("%s (%s) = %s", typeid(hash).toString(), filename, toHexString(result)); - } - - void umain(string[] args) - { - auto md5 = new MD5Digest(); - auto sha1 = new SHA1Digest(); - auto crc32 = new CRC32Digest(); - - foreach (arg; args[1 .. $]) - { - digestFile(md5, arg); - digestFile(sha1, arg); - digestFile(crc32, arg); - } - } -} - -version (StdDdoc) - version = ExampleDigest; - -version (ExampleDigest) -{ - /** - * This documents the general structure of a Digest in the template API. - * All digest implementations should implement the following members and therefore pass - * the $(LREF isDigest) test. - * - * Note: - * $(UL - * $(LI A digest must be a struct (value type) to pass the $(LREF isDigest) test.) - * $(LI A digest passing the $(LREF isDigest) test is always an `OutputRange`) - * ) - */ - struct ExampleDigest - { - public: - /** - * Use this to feed the digest with data. - * Also implements the $(REF isOutputRange, std,range,primitives) - * interface for `ubyte` and `const(ubyte)[]`. - * The following usages of `put` must work for any type which - * passes $(LREF isDigest): - * Example: - * ---- - * ExampleDigest dig; - * dig.put(cast(ubyte) 0); //single ubyte - * dig.put(cast(ubyte) 0, cast(ubyte) 0); //variadic - * ubyte[10] buf; - * dig.put(buf); //buffer - * ---- - */ - @trusted void put(scope const(ubyte)[] data...) - { - - } - - /** - * This function is used to (re)initialize the digest. - * It must be called before using the digest and it also works as a 'reset' function - * if the digest has already processed data. - */ - @trusted void start() - { - - } - - /** - * The finish function returns the final hash sum and resets the Digest. - * - * Note: - * The actual type returned by finish depends on the digest implementation. - * `ubyte[16]` is just used as an example. It is guaranteed that the type is a - * static array of ubytes. - * - * $(UL - * $(LI Use $(LREF DigestType) to obtain the actual return type.) - * $(LI Use $(LREF digestLength) to obtain the length of the ubyte array.) - * ) - */ - @trusted ubyte[16] finish() - { - return (ubyte[16]).init; - } - } -} - -/// -@system unittest -{ - //Using the OutputRange feature - import std.algorithm.mutation : copy; - import std.digest.md; - import std.range : repeat; - - auto oneMillionRange = repeat!ubyte(cast(ubyte)'a', 1000000); - auto ctx = makeDigest!MD5(); - copy(oneMillionRange, &ctx); //Note: You must pass a pointer to copy! - assert(ctx.finish().toHexString() == "7707D6AE4E027C70EEA2A935C2296F21"); -} - -/** - * Use this to check if a type is a digest. See $(LREF ExampleDigest) to see what - * a type must provide to pass this check. - * - * Note: - * This is very useful as a template constraint (see examples) - * - * BUGS: - * $(UL - * $(LI Does not yet verify that put takes scope parameters.) - * $(LI Should check that finish() returns a ubyte[num] array) - * ) - */ -template isDigest(T) -{ - import std.range : isOutputRange; - enum bool isDigest = isOutputRange!(T, const(ubyte)[]) && isOutputRange!(T, ubyte) && - is(T == struct) && - is(typeof( - { - T dig = void; //Can define - dig.put(cast(ubyte) 0, cast(ubyte) 0); //varags - dig.start(); //has start - auto value = dig.finish(); //has finish - })); -} - -/// -@system unittest -{ - import std.digest.crc; - static assert(isDigest!CRC32); -} -/// -@system unittest -{ - import std.digest.crc; - void myFunction(T)() - if (isDigest!T) - { - T dig; - dig.start(); - auto result = dig.finish(); - } - myFunction!CRC32(); -} - -/** - * Use this template to get the type which is returned by a digest's $(LREF finish) method. - */ -template DigestType(T) -{ - static if (isDigest!T) - { - alias DigestType = - ReturnType!(typeof( - { - T dig = void; - return dig.finish(); - })); - } - else - static assert(false, T.stringof ~ " is not a digest! (fails isDigest!T)"); -} - -/// -@system unittest -{ - import std.digest.crc; - assert(is(DigestType!(CRC32) == ubyte[4])); -} -/// -@system unittest -{ - import std.digest.crc; - CRC32 dig; - dig.start(); - DigestType!CRC32 result = dig.finish(); -} - -/** - * Used to check if a digest supports the `peek` method. - * Peek has exactly the same function signatures as finish, but it doesn't reset - * the digest's internal state. - * - * Note: - * $(UL - * $(LI This is very useful as a template constraint (see examples)) - * $(LI This also checks if T passes $(LREF isDigest)) - * ) - */ -template hasPeek(T) -{ - enum bool hasPeek = isDigest!T && - is(typeof( - { - T dig = void; //Can define - DigestType!T val = dig.peek(); - })); -} - -/// -@system unittest -{ - import std.digest.crc, std.digest.md; - assert(!hasPeek!(MD5)); - assert(hasPeek!CRC32); -} -/// -@system unittest -{ - import std.digest.crc; - void myFunction(T)() - if (hasPeek!T) - { - T dig; - dig.start(); - auto result = dig.peek(); - } - myFunction!CRC32(); -} - -/** - * Checks whether the digest has a `blockSize` member, which contains the - * digest's internal block size in bits. It is primarily used by $(REF HMAC, std,digest,hmac). - */ - -template hasBlockSize(T) -if (isDigest!T) -{ - enum bool hasBlockSize = __traits(compiles, { size_t blockSize = T.blockSize; }); -} - -/// -@system unittest -{ - import std.digest.hmac, std.digest.md; - static assert(hasBlockSize!MD5 && MD5.blockSize == 512); - static assert(hasBlockSize!(HMAC!MD5) && HMAC!MD5.blockSize == 512); -} - -package template isDigestibleRange(Range) -{ - import std.digest.md; - import std.range : isInputRange, ElementType; - enum bool isDigestibleRange = isInputRange!Range && is(typeof( - { - MD5 ha; //Could use any conformant hash - ElementType!Range val; - ha.put(val); - })); -} - -/** - * This is a convenience function to calculate a hash using the template API. - * Every digest passing the $(LREF isDigest) test can be used with this function. - * - * Params: - * range= an `InputRange` with `ElementType` `ubyte`, `ubyte[]` or `ubyte[num]` - */ -DigestType!Hash digest(Hash, Range)(auto ref Range range) -if (!isArray!Range - && isDigestibleRange!Range) -{ - Hash hash; - hash.start(); - alias E = ElementType!Range; // Not necessarily ubyte. Could be ubyte[N] or ubyte[] or something w/alias this. - static if (!(__traits(isScalar, E) && E.sizeof == 1)) - { - foreach (e; range) - hash.put(e); - return hash.finish(); - } - else - { - static if (hasBlockSize!Hash) - enum bufferBytes = Hash.blockSize >= (8192 * 8) ? 8192 : Hash.blockSize <= 64 ? 8 : (Hash.blockSize / 8); - else - enum bufferBytes = 8; - ubyte[bufferBytes] buffer = void; - static if (isRandomAccessRange!Range && hasLength!Range) - { - const end = range.length; - size_t i = 0; - while (end - i >= buffer.length) - { - foreach (ref e; buffer) - e = range[i++]; - hash.put(buffer); - } - if (const remaining = end - i) - { - foreach (ref e; buffer[0 .. remaining]) - e = range[i++]; - hash.put(buffer[0 .. remaining]); - } - return hash.finish(); - } - else - { - for (;;) - { - size_t n = buffer.length; - foreach (i, ref e; buffer) - { - if (range.empty) - { - n = i; - break; - } - e = range.front; - range.popFront(); - } - if (n) - hash.put(buffer[0 .. n]); - if (n != buffer.length) - return hash.finish(); - } - } - } -} - -/// -@system unittest -{ - import std.digest.md; - import std.range : repeat; - auto testRange = repeat!ubyte(cast(ubyte)'a', 100); - auto md5 = digest!MD5(testRange); -} - -/** - * This overload of the digest function handles arrays. - * - * Params: - * data= one or more arrays of any type - */ -DigestType!Hash digest(Hash, T...)(scope const T data) -if (allSatisfy!(isArray, typeof(data))) -{ - Hash hash; - hash.start(); - foreach (datum; data) - hash.put(cast(const(ubyte[]))datum); - return hash.finish(); -} - -/// -@system unittest -{ - import std.digest.crc, std.digest.md, std.digest.sha; - auto md5 = digest!MD5( "The quick brown fox jumps over the lazy dog"); - auto sha1 = digest!SHA1( "The quick brown fox jumps over the lazy dog"); - auto crc32 = digest!CRC32("The quick brown fox jumps over the lazy dog"); - assert(toHexString(crc32) == "39A34F41"); -} - -/// -@system unittest -{ - import std.digest.crc; - auto crc32 = digest!CRC32("The quick ", "brown ", "fox jumps over the lazy dog"); - assert(toHexString(crc32) == "39A34F41"); -} - -/** - * This is a convenience function similar to $(LREF digest), but it returns the string - * representation of the hash. Every digest passing the $(LREF isDigest) test can be used with this - * function. - * - * Params: - * order= the order in which the bytes are processed (see $(LREF toHexString)) - * range= an `InputRange` with `ElementType` `ubyte`, `ubyte[]` or `ubyte[num]` - */ -char[digestLength!(Hash)*2] hexDigest(Hash, Order order = Order.increasing, Range)(ref Range range) -if (!isArray!Range && isDigestibleRange!Range) -{ - return toHexString!order(digest!Hash(range)); -} - -/// -@system unittest -{ - import std.digest.md; - import std.range : repeat; - auto testRange = repeat!ubyte(cast(ubyte)'a', 100); - assert(hexDigest!MD5(testRange) == "36A92CC94A9E0FA21F625F8BFB007ADF"); -} - -/** - * This overload of the hexDigest function handles arrays. - * - * Params: - * order= the order in which the bytes are processed (see $(LREF toHexString)) - * data= one or more arrays of any type - */ -char[digestLength!(Hash)*2] hexDigest(Hash, Order order = Order.increasing, T...)(scope const T data) -if (allSatisfy!(isArray, typeof(data))) -{ - return toHexString!order(digest!Hash(data)); -} - -/// -@system unittest -{ - import std.digest.crc; - assert(hexDigest!(CRC32, Order.decreasing)("The quick brown fox jumps over the lazy dog") == "414FA339"); -} -/// -@system unittest -{ - import std.digest.crc; - assert(hexDigest!(CRC32, Order.decreasing)("The quick ", "brown ", "fox jumps over the lazy dog") == "414FA339"); -} - -/** - * This is a convenience function which returns an initialized digest, so it's not necessary to call - * start manually. - */ -Hash makeDigest(Hash)() -{ - Hash hash; - hash.start(); - return hash; -} - -/// -@system unittest -{ - import std.digest.md; - auto md5 = makeDigest!MD5(); - md5.put(0); - assert(toHexString(md5.finish()) == "93B885ADFE0DA089CDF634904FD59F71"); -} - -/*+*************************** End of template part, welcome to OOP land **************************/ - -/** - * This describes the OOP API. To understand when to use the template API and when to use the OOP API, - * see the module documentation at the top of this page. - * - * The Digest interface is the base interface which is implemented by all digests. - * - * Note: - * A Digest implementation is always an `OutputRange` - */ -interface Digest -{ - public: - /** - * Use this to feed the digest with data. - * Also implements the $(REF isOutputRange, std,range,primitives) - * interface for `ubyte` and `const(ubyte)[]`. - * - * Example: - * ---- - * void test(Digest dig) - * { - * dig.put(cast(ubyte) 0); //single ubyte - * dig.put(cast(ubyte) 0, cast(ubyte) 0); //variadic - * ubyte[10] buf; - * dig.put(buf); //buffer - * } - * ---- - */ - @trusted nothrow void put(scope const(ubyte)[] data...); - - /** - * Resets the internal state of the digest. - * Note: - * $(LREF finish) calls this internally, so it's not necessary to call - * `reset` manually after a call to $(LREF finish). - */ - @trusted nothrow void reset(); - - /** - * This is the length in bytes of the hash value which is returned by $(LREF finish). - * It's also the required size of a buffer passed to $(LREF finish). - */ - @trusted nothrow @property size_t length() const; - - /** - * The finish function returns the hash value. It takes an optional buffer to copy the data - * into. If a buffer is passed, it must be at least $(LREF length) bytes big. - */ - @trusted nothrow ubyte[] finish(); - ///ditto - nothrow ubyte[] finish(ubyte[] buf); - // https://issues.dlang.org/show_bug.cgi?id=6549 - /*in - { - assert(buf.length >= this.length); - }*/ - - /** - * This is a convenience function to calculate the hash of a value using the OOP API. - */ - final @trusted nothrow ubyte[] digest(scope const(void[])[] data...) - { - this.reset(); - foreach (datum; data) - this.put(cast(ubyte[]) datum); - return this.finish(); - } -} - -/// -@system unittest -{ - //Using the OutputRange feature - import std.algorithm.mutation : copy; - import std.digest.md; - import std.range : repeat; - - auto oneMillionRange = repeat!ubyte(cast(ubyte)'a', 1000000); - auto ctx = new MD5Digest(); - copy(oneMillionRange, ctx); - assert(ctx.finish().toHexString() == "7707D6AE4E027C70EEA2A935C2296F21"); -} - -/// -@system unittest -{ - import std.digest.crc, std.digest.md, std.digest.sha; - ubyte[] md5 = (new MD5Digest()).digest("The quick brown fox jumps over the lazy dog"); - ubyte[] sha1 = (new SHA1Digest()).digest("The quick brown fox jumps over the lazy dog"); - ubyte[] crc32 = (new CRC32Digest()).digest("The quick brown fox jumps over the lazy dog"); - assert(crcHexString(crc32) == "414FA339"); -} - -/// -@system unittest -{ - import std.digest.crc; - ubyte[] crc32 = (new CRC32Digest()).digest("The quick ", "brown ", "fox jumps over the lazy dog"); - assert(crcHexString(crc32) == "414FA339"); -} - -@system unittest -{ - import std.range : isOutputRange; - assert(!isDigest!(Digest)); - assert(isOutputRange!(Digest, ubyte)); -} - -/// -@system unittest -{ - void test(Digest dig) - { - dig.put(cast(ubyte) 0); //single ubyte - dig.put(cast(ubyte) 0, cast(ubyte) 0); //variadic - ubyte[10] buf; - dig.put(buf); //buffer - } -} - -/*+*************************** End of OOP part, helper functions follow ***************************/ - -/** - * See $(LREF toHexString) - */ -enum Order : bool -{ - increasing, /// - decreasing /// -} - -/// -@safe unittest -{ - import std.digest.crc : CRC32; - - auto crc32 = digest!CRC32("The quick ", "brown ", "fox jumps over the lazy dog"); - assert(crc32.toHexString!(Order.decreasing) == "414FA339"); - assert(crc32.toHexString!(LetterCase.lower, Order.decreasing) == "414fa339"); -} - - -/** - * Used to convert a hash value (a static or dynamic array of ubytes) to a string. - * Can be used with the OOP and with the template API. - * - * The additional order parameter can be used to specify the order of the input data. - * By default the data is processed in increasing order, starting at index 0. To process it in the - * opposite order, pass Order.decreasing as a parameter. - * - * The additional letterCase parameter can be used to specify the case of the output data. - * By default the output is in upper case. To change it to the lower case - * pass LetterCase.lower as a parameter. - * - * Note: - * The function overloads returning a string allocate their return values - * using the GC. The versions returning static arrays use pass-by-value for - * the return value, effectively avoiding dynamic allocation. - */ -char[num*2] toHexString(Order order = Order.increasing, size_t num, LetterCase letterCase = LetterCase.upper) -(const ubyte[num] digest) -{ - - char[num*2] result; - size_t i; - toHexStringImpl!(order, letterCase)(digest, result); - return result; -} - -///ditto -char[num*2] toHexString(LetterCase letterCase, Order order = Order.increasing, size_t num)(in ubyte[num] digest) -{ - return toHexString!(order, num, letterCase)(digest); -} - -///ditto -string toHexString(Order order = Order.increasing, LetterCase letterCase = LetterCase.upper) -(in ubyte[] digest) -{ - auto result = new char[digest.length*2]; - toHexStringImpl!(order, letterCase)(digest, result); - import std.exception : assumeUnique; - // memory was just created, so casting to immutable is safe - return () @trusted { return assumeUnique(result); }(); -} - -///ditto -string toHexString(LetterCase letterCase, Order order = Order.increasing)(in ubyte[] digest) -{ - return toHexString!(order, letterCase)(digest); -} - -//For more example unittests, see Digest.digest, digest - -/// -@safe unittest -{ - import std.digest.crc; - //Test with template API: - auto crc32 = digest!CRC32("The quick ", "brown ", "fox jumps over the lazy dog"); - //Lower case variant: - assert(toHexString!(LetterCase.lower)(crc32) == "39a34f41"); - //Usually CRCs are printed in this order, though: - assert(toHexString!(Order.decreasing)(crc32) == "414FA339"); - assert(toHexString!(LetterCase.lower, Order.decreasing)(crc32) == "414fa339"); -} - -/// -@safe unittest -{ - import std.digest.crc; - // With OOP API - auto crc32 = (new CRC32Digest()).digest("The quick ", "brown ", "fox jumps over the lazy dog"); - //Usually CRCs are printed in this order, though: - assert(toHexString!(Order.decreasing)(crc32) == "414FA339"); -} - -@safe unittest -{ - ubyte[16] data; - assert(toHexString(data) == "00000000000000000000000000000000"); - - assert(toHexString(cast(ubyte[4])[42, 43, 44, 45]) == "2A2B2C2D"); - assert(toHexString(cast(ubyte[])[42, 43, 44, 45]) == "2A2B2C2D"); - assert(toHexString!(Order.decreasing)(cast(ubyte[4])[42, 43, 44, 45]) == "2D2C2B2A"); - assert(toHexString!(Order.decreasing, LetterCase.lower)(cast(ubyte[4])[42, 43, 44, 45]) == "2d2c2b2a"); - assert(toHexString!(Order.decreasing)(cast(ubyte[])[42, 43, 44, 45]) == "2D2C2B2A"); -} - -/*+*********************** End of public helper part, private helpers follow ***********************/ - -/* - * Used to convert from a ubyte[] slice to a ref ubyte[N]. - * This helper is used internally in the WrapperDigest template to wrap the template API's - * finish function. - */ -ref T[N] asArray(size_t N, T)(ref T[] source, string errorMsg = "") -{ - assert(source.length >= N, errorMsg); - return *cast(T[N]*) source.ptr; -} - -/* - * Fill in a preallocated buffer with the ASCII hex representation from a byte buffer - */ -private void toHexStringImpl(Order order, LetterCase letterCase, BB, HB) -(scope const ref BB byteBuffer, ref HB hexBuffer){ - static if (letterCase == LetterCase.upper) - { - import std.ascii : hexDigits = hexDigits; - } - else - { - import std.ascii : hexDigits = lowerHexDigits; - } - - size_t i; - static if (order == Order.increasing) - { - foreach (u; byteBuffer) - { - hexBuffer[i++] = hexDigits[u >> 4]; - hexBuffer[i++] = hexDigits[u & 15]; - } - } - else - { - size_t j = byteBuffer.length -1; - while (i < byteBuffer.length*2) - { - hexBuffer[i++] = hexDigits[byteBuffer[j] >> 4]; - hexBuffer[i++] = hexDigits[byteBuffer[j] & 15]; - j--; - } - } -} - - -/* - * Returns the length (in bytes) of the hash value produced by T. - */ -template digestLength(T) -if (isDigest!T) -{ - enum size_t digestLength = (ReturnType!(T.finish)).length; -} - -@safe pure nothrow @nogc -unittest -{ - import std.digest.md : MD5; - import std.digest.sha : SHA1, SHA256, SHA512; - assert(digestLength!MD5 == 16); - assert(digestLength!SHA1 == 20); - assert(digestLength!SHA256 == 32); - assert(digestLength!SHA512 == 64); -} - -/** - * Wraps a template API hash struct into a Digest interface. - * Modules providing digest implementations will usually provide - * an alias for this template (e.g. MD5Digest, SHA1Digest, ...). - */ -class WrapperDigest(T) -if (isDigest!T) : Digest -{ - protected: - T _digest; - - public final: - /** - * Initializes the digest. - */ - this() - { - _digest.start(); - } - - /** - * Use this to feed the digest with data. - * Also implements the $(REF isOutputRange, std,range,primitives) - * interface for `ubyte` and `const(ubyte)[]`. - */ - @trusted nothrow void put(scope const(ubyte)[] data...) - { - _digest.put(data); - } - - /** - * Resets the internal state of the digest. - * Note: - * $(LREF finish) calls this internally, so it's not necessary to call - * `reset` manually after a call to $(LREF finish). - */ - @trusted nothrow void reset() - { - _digest.start(); - } - - /** - * This is the length in bytes of the hash value which is returned by $(LREF finish). - * It's also the required size of a buffer passed to $(LREF finish). - */ - @trusted nothrow @property size_t length() const pure - { - return digestLength!T; - } - - /** - * The finish function returns the hash value. It takes an optional buffer to copy the data - * into. If a buffer is passed, it must have a length at least $(LREF length) bytes. - * - * Example: - * -------- - * - * import std.digest.md; - * ubyte[16] buf; - * auto hash = new WrapperDigest!MD5(); - * hash.put(cast(ubyte) 0); - * auto result = hash.finish(buf[]); - * //The result is now in result (and in buf). If you pass a buffer which is bigger than - * //necessary, result will have the correct length, but buf will still have it's original - * //length - * -------- - */ - nothrow ubyte[] finish(ubyte[] buf) - in - { - assert(buf.length >= this.length, "Given buffer is smaller than the local buffer."); - } - do - { - enum string msg = "Buffer needs to be at least " ~ digestLength!(T).stringof ~ " bytes " ~ - "big, check " ~ typeof(this).stringof ~ ".length!"; - asArray!(digestLength!T)(buf, msg) = _digest.finish(); - return buf[0 .. digestLength!T]; - } - - ///ditto - @trusted nothrow ubyte[] finish() - { - enum len = digestLength!T; - auto buf = new ubyte[len]; - asArray!(digestLength!T)(buf) = _digest.finish(); - return buf; - } - - version (StdDdoc) - { - /** - * Works like `finish` but does not reset the internal state, so it's possible - * to continue putting data into this WrapperDigest after a call to peek. - * - * These functions are only available if `hasPeek!T` is true. - */ - @trusted ubyte[] peek(ubyte[] buf) const; - ///ditto - @trusted ubyte[] peek() const; - } - else static if (hasPeek!T) - { - @trusted ubyte[] peek(ubyte[] buf) const - in - { - assert(buf.length >= this.length, "Given buffer is smaller than the local buffer."); - } - do - { - enum string msg = "Buffer needs to be at least " ~ digestLength!(T).stringof ~ " bytes " ~ - "big, check " ~ typeof(this).stringof ~ ".length!"; - asArray!(digestLength!T)(buf, msg) = _digest.peek(); - return buf[0 .. digestLength!T]; - } - - @trusted ubyte[] peek() const - { - enum len = digestLength!T; - auto buf = new ubyte[len]; - asArray!(digestLength!T)(buf) = _digest.peek(); - return buf; - } - } -} - -/// -@system unittest -{ - import std.digest.md; - //Simple example - auto hash = new WrapperDigest!MD5(); - hash.put(cast(ubyte) 0); - auto result = hash.finish(); -} - -/// -@system unittest -{ - //using a supplied buffer - import std.digest.md; - ubyte[16] buf; - auto hash = new WrapperDigest!MD5(); - hash.put(cast(ubyte) 0); - auto result = hash.finish(buf[]); - //The result is now in result (and in buf). If you pass a buffer which is bigger than - //necessary, result will have the correct length, but buf will still have it's original - //length -} - -@safe unittest -{ - // Test peek & length - import std.digest.crc; - auto hash = new WrapperDigest!CRC32(); - assert(hash.length == 4); - hash.put(cast(const(ubyte[]))"The quick brown fox jumps over the lazy dog"); - assert(hash.peek().toHexString() == "39A34F41"); - ubyte[5] buf; - assert(hash.peek(buf).toHexString() == "39A34F41"); -} - -/** - * Securely compares two digest representations while protecting against timing - * attacks. Do not use `==` to compare digest representations. - * - * The attack happens as follows: - * - * $(OL - * $(LI An attacker wants to send harmful data to your server, which - * requires a integrity HMAC SHA1 token signed with a secret.) - * $(LI The length of the token is known to be 40 characters long due to its format, - * so the attacker first sends `"0000000000000000000000000000000000000000"`, - * then `"1000000000000000000000000000000000000000"`, and so on.) - * $(LI The given HMAC token is compared with the expected token using the - * `==` string comparison, which returns `false` as soon as the first wrong - * element is found. If a wrong element is found, then a rejection is sent - * back to the sender.) - * $(LI Eventually, the attacker is able to determine the first character in - * the correct token because the sever takes slightly longer to return a - * rejection. This is due to the comparison moving on to second item in - * the two arrays, seeing they are different, and then sending the rejection.) - * $(LI It may seem like too small of a difference in time for the attacker - * to notice, but security researchers have shown that differences as - * small as $(LINK2 http://www.cs.rice.edu/~dwallach/pub/crosby-timing2009.pdf, - * 20Âľs can be reliably distinguished) even with network inconsistencies.) - * $(LI Repeat the process for each character until the attacker has the whole - * correct token and the server accepts the harmful data. This can be done - * in a week with the attacker pacing the attack to 10 requests per second - * with only one client.) - * ) - * - * This function defends against this attack by always comparing every single - * item in the array if the two arrays are the same length. Therefore, this - * function is always $(BIGOH n) for ranges of the same length. - * - * This attack can also be mitigated via rate limiting and banning IPs which have too - * many rejected requests. However, this does not completely solve the problem, - * as the attacker could be in control of a bot net. To fully defend against - * the timing attack, rate limiting, banning IPs, and using this function - * should be used together. - * - * Params: - * r1 = A digest representation - * r2 = A digest representation - * Returns: - * `true` if both representations are equal, `false` otherwise - * See_Also: - * $(LINK2 https://en.wikipedia.org/wiki/Timing_attack, The Wikipedia article - * on timing attacks). - */ -bool secureEqual(R1, R2)(R1 r1, R2 r2) -if (isInputRange!R1 && isInputRange!R2 && !isInfinite!R1 && !isInfinite!R2 && - (isIntegral!(ElementEncodingType!R1) || isSomeChar!(ElementEncodingType!R1)) && - !is(CommonType!(ElementEncodingType!R1, ElementEncodingType!R2) == void)) -{ - static if (hasLength!R1 && hasLength!R2) - if (r1.length != r2.length) - return false; - - int result; - - static if (isRandomAccessRange!R1 && isRandomAccessRange!R2 && - hasLength!R1 && hasLength!R2) - { - foreach (i; 0 .. r1.length) - result |= r1[i] ^ r2[i]; - } - else static if (hasLength!R1 && hasLength!R2) - { - // Lengths are the same so we can squeeze out a bit of performance - // by not checking if r2 is empty - for (; !r1.empty; r1.popFront(), r2.popFront()) - { - result |= r1.front ^ r2.front; - } - } - else - { - // Generic case, walk both ranges - for (; !r1.empty; r1.popFront(), r2.popFront()) - { - if (r2.empty) return false; - result |= r1.front ^ r2.front; - } - if (!r2.empty) return false; - } - - return result == 0; -} - -/// -@system pure unittest -{ - import std.digest.hmac : hmac; - import std.digest.sha : SHA1; - import std.string : representation; - - // a typical HMAC data integrity verification - auto secret = "A7GZIP6TAQA6OHM7KZ42KB9303CEY0MOV5DD6NTV".representation; - auto data = "data".representation; - - auto hex1 = data.hmac!SHA1(secret).toHexString; - auto hex2 = data.hmac!SHA1(secret).toHexString; - auto hex3 = "data1".representation.hmac!SHA1(secret).toHexString; - - assert( secureEqual(hex1[], hex2[])); - assert(!secureEqual(hex1[], hex3[])); -} - -@system pure unittest -{ - import std.internal.test.dummyrange : ReferenceInputRange; - import std.range : takeExactly; - import std.string : representation; - import std.utf : byWchar, byDchar; - - { - auto hex1 = "02CA3484C375EDD3C0F08D3F50D119E61077".representation; - auto hex2 = "02CA3484C375EDD3C0F08D3F50D119E610779018".representation; - assert(!secureEqual(hex1, hex2)); - } - { - auto hex1 = "02CA3484C375EDD3C0F08D3F50D119E610779018"w.representation; - auto hex2 = "02CA3484C375EDD3C0F08D3F50D119E610779018"d.representation; - assert(secureEqual(hex1, hex2)); - } - { - auto hex1 = "02CA3484C375EDD3C0F08D3F50D119E610779018".byWchar; - auto hex2 = "02CA3484C375EDD3C0F08D3F50D119E610779018".byDchar; - assert(secureEqual(hex1, hex2)); - } - { - auto hex1 = "02CA3484C375EDD3C0F08D3F50D119E61077".byWchar; - auto hex2 = "02CA3484C375EDD3C0F08D3F50D119E610779018".byDchar; - assert(!secureEqual(hex1, hex2)); - } - { - auto hex1 = new ReferenceInputRange!int([0, 1, 2, 3, 4, 5, 6, 7, 8]).takeExactly(9); - auto hex2 = new ReferenceInputRange!int([0, 1, 2, 3, 4, 5, 6, 7, 8]).takeExactly(9); - assert(secureEqual(hex1, hex2)); - } - { - auto hex1 = new ReferenceInputRange!int([0, 1, 2, 3, 4, 5, 6, 7, 8]).takeExactly(9); - auto hex2 = new ReferenceInputRange!int([0, 1, 2, 3, 4, 5, 6, 7, 9]).takeExactly(9); - assert(!secureEqual(hex1, hex2)); - } -} diff --git a/phobos/std/digest/ripemd.d b/phobos/std/digest/ripemd.d deleted file mode 100644 index 0fdc251..0000000 --- a/phobos/std/digest/ripemd.d +++ /dev/null @@ -1,758 +0,0 @@ -/** - * Computes RIPEMD-160 hashes of arbitrary data. RIPEMD-160 hashes are 20 byte quantities - * that are like a checksum or CRC, but are more robust. - * -$(SCRIPT inhibitQuickIndex = 1;) - -$(DIVC quickindex, -$(BOOKTABLE , -$(TR $(TH Category) $(TH Functions) -) -$(TR $(TDNW Template API) $(TD $(MYREF RIPEMD160) -) -) -$(TR $(TDNW OOP API) $(TD $(MYREF RIPEMD160Digest)) -) -$(TR $(TDNW Helpers) $(TD $(MYREF ripemd160Of)) -) -) -) - - * This module conforms to the APIs defined in $(MREF std, digest). To understand the - * differences between the template and the OOP API, see $(MREF std, digest). - * - * This module publicly imports `std.digest` and can be used as a stand-alone - * module. - * - * License: $(HTTP www.boost.org/LICENSE_1_0.txt, Boost License 1.0). - * - * CTFE: - * Digests do not work in CTFE - * - * Authors: - * Kai Nacke $(BR) - * The algorithm was designed by Hans Dobbertin, Antoon Bosselaers, and Bart Preneel. $(BR) - * The D implementation is a direct translation of the ANSI C implementation by Antoon Bosselaers. - * - * References: - * $(UL - * $(LI $(LINK2 http://homes.esat.kuleuven.be/~bosselae/ripemd160.html, The hash function RIPEMD-160)) - * $(LI $(LINK2 http://en.wikipedia.org/wiki/RIPEMD-160, Wikipedia on RIPEMD-160)) - * ) - * - * Source: $(PHOBOSSRC std/digest/ripemd.d) - * - */ - -module std.digest.ripemd; - -public import std.digest; - -/// -@safe unittest -{ - //Template API - import std.digest.md; - - ubyte[20] hash = ripemd160Of("abc"); - assert(toHexString(hash) == "8EB208F7E05D987A9B044A8E98C6B087F15A0BFC"); - - //Feeding data - ubyte[1024] data; - RIPEMD160 md; - md.start(); - md.put(data[]); - md.start(); //Start again - md.put(data[]); - hash = md.finish(); -} - -/// -@safe unittest -{ - //OOP API - import std.digest.md; - - auto md = new RIPEMD160Digest(); - ubyte[] hash = md.digest("abc"); - assert(toHexString(hash) == "8EB208F7E05D987A9B044A8E98C6B087F15A0BFC"); - - //Feeding data - ubyte[1024] data; - md.put(data[]); - md.reset(); //Start again - md.put(data[]); - hash = md.finish(); -} - -/** - * Template API RIPEMD160 implementation. - * See `std.digest` for differences between template and OOP API. - */ -struct RIPEMD160 -{ - import core.bitop : rol; - private: - // magic initialization constants - uint[5] _state = [0x67452301,0xefcdab89,0x98badcfe,0x10325476,0xc3d2e1f0]; // state (ABCDE) - ulong _count; //number of bits, modulo 2^64 - ubyte[64] _buffer; // input buffer - - static immutable ubyte[64] _padding = - [ - 0x80, 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, 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, 0, 0, 0, 0, 0, 0, 0, 0, 0 - ]; - - // F, G, H, I and J are basic RIPEMD160 functions - static @safe pure nothrow @nogc - { - uint F(uint x, uint y, uint z) { return x ^ y ^ z; } - uint G(uint x, uint y, uint z) { return (x & y) | (~x & z); } - uint H(uint x, uint y, uint z) { return (x | ~y) ^ z; } - uint I(uint x, uint y, uint z) { return (x & z) | (y & ~z); } - uint J(uint x, uint y, uint z) { return x ^ (y | ~z); } - } - - /* - * FF, GG, HH, and II transformations for rounds 1, 2, 3, and 4. - * Rotation is separate from addition to prevent recomputation. - */ - - /* the ten basic operations FF() through III() */ - static void FF(ref uint a, uint b, ref uint c, uint d, uint e, uint x, uint s) - @safe pure nothrow @nogc - { - a += F(b, c, d) + x; - a = rol(a, s) + e; - c = rol(c, 10); - } - - static void GG(ref uint a, uint b, ref uint c, uint d, uint e, uint x, uint s) - @safe pure nothrow @nogc - { - a += G(b, c, d) + x + 0x5a827999UL; - a = rol(a, s) + e; - c = rol(c, 10); - } - - static void HH(ref uint a, uint b, ref uint c, uint d, uint e, uint x, uint s) - @safe pure nothrow @nogc - { - a += H(b, c, d) + x + 0x6ed9eba1UL; - a = rol(a, s) + e; - c = rol(c, 10); - } - - static void II(ref uint a, uint b, ref uint c, uint d, uint e, uint x, uint s) - @safe pure nothrow @nogc - { - a += I(b, c, d) + x + 0x8f1bbcdcUL; - a = rol(a, s) + e; - c = rol(c, 10); - } - - static void JJ(ref uint a, uint b, ref uint c, uint d, uint e, uint x, uint s) - @safe pure nothrow @nogc - { - a += J(b, c, d) + x + 0xa953fd4eUL; - a = rol(a, s) + e; - c = rol(c, 10); - } - - /* - * FFF, GGG, HHH, and III transformations for parallel rounds 1, 2, 3, and 4. - * Rotation is separate from addition to prevent recomputation. - */ - - static void FFF(ref uint a, uint b, ref uint c, uint d, uint e, uint x, uint s) - @safe pure nothrow @nogc - { - a += F(b, c, d) + x; - a = rol(a, s) + e; - c = rol(c, 10); - } - - static void GGG(ref uint a, uint b, ref uint c, uint d, uint e, uint x, uint s) - @safe pure nothrow @nogc - { - a += G(b, c, d) + x + 0x7a6d76e9UL; - a = rol(a, s) + e; - c = rol(c, 10); - } - - static void HHH(ref uint a, uint b, ref uint c, uint d, uint e, uint x, uint s) - @safe pure nothrow @nogc - { - a += H(b, c, d) + x + 0x6d703ef3UL; - a = rol(a, s) + e; - c = rol(c, 10); - } - - static void III(ref uint a, uint b, ref uint c, uint d, uint e, uint x, uint s) - @safe pure nothrow @nogc - { - a += I(b, c, d) + x + 0x5c4dd124UL; - a = rol(a, s) + e; - c = rol(c, 10); - } - - static void JJJ(ref uint a, uint b, ref uint c, uint d, uint e, uint x, uint s) - @safe pure nothrow @nogc - { - a += J(b, c, d) + x + 0x50a28be6UL; - a = rol(a, s) + e; - c = rol(c, 10); - } - - /* - * RIPEMD160 basic transformation. Transforms state based on block. - */ - - void transform(const(ubyte[64])* block) - pure nothrow @nogc - { - uint aa = _state[0], - bb = _state[1], - cc = _state[2], - dd = _state[3], - ee = _state[4]; - uint aaa = _state[0], - bbb = _state[1], - ccc = _state[2], - ddd = _state[3], - eee = _state[4]; - - uint[16] x = void; - - version (BigEndian) - { - import std.bitmanip : littleEndianToNative; - - for (size_t i = 0; i < 16; i++) - { - x[i] = littleEndianToNative!uint(*cast(ubyte[4]*)&(*block)[i*4]); - } - } - else - { - (cast(ubyte*) x.ptr)[0 .. 64] = (cast(ubyte*) block)[0 .. 64]; - } - - /* round 1 */ - FF(aa, bb, cc, dd, ee, x[ 0], 11); - FF(ee, aa, bb, cc, dd, x[ 1], 14); - FF(dd, ee, aa, bb, cc, x[ 2], 15); - FF(cc, dd, ee, aa, bb, x[ 3], 12); - FF(bb, cc, dd, ee, aa, x[ 4], 5); - FF(aa, bb, cc, dd, ee, x[ 5], 8); - FF(ee, aa, bb, cc, dd, x[ 6], 7); - FF(dd, ee, aa, bb, cc, x[ 7], 9); - FF(cc, dd, ee, aa, bb, x[ 8], 11); - FF(bb, cc, dd, ee, aa, x[ 9], 13); - FF(aa, bb, cc, dd, ee, x[10], 14); - FF(ee, aa, bb, cc, dd, x[11], 15); - FF(dd, ee, aa, bb, cc, x[12], 6); - FF(cc, dd, ee, aa, bb, x[13], 7); - FF(bb, cc, dd, ee, aa, x[14], 9); - FF(aa, bb, cc, dd, ee, x[15], 8); - - /* round 2 */ - GG(ee, aa, bb, cc, dd, x[ 7], 7); - GG(dd, ee, aa, bb, cc, x[ 4], 6); - GG(cc, dd, ee, aa, bb, x[13], 8); - GG(bb, cc, dd, ee, aa, x[ 1], 13); - GG(aa, bb, cc, dd, ee, x[10], 11); - GG(ee, aa, bb, cc, dd, x[ 6], 9); - GG(dd, ee, aa, bb, cc, x[15], 7); - GG(cc, dd, ee, aa, bb, x[ 3], 15); - GG(bb, cc, dd, ee, aa, x[12], 7); - GG(aa, bb, cc, dd, ee, x[ 0], 12); - GG(ee, aa, bb, cc, dd, x[ 9], 15); - GG(dd, ee, aa, bb, cc, x[ 5], 9); - GG(cc, dd, ee, aa, bb, x[ 2], 11); - GG(bb, cc, dd, ee, aa, x[14], 7); - GG(aa, bb, cc, dd, ee, x[11], 13); - GG(ee, aa, bb, cc, dd, x[ 8], 12); - - /* round 3 */ - HH(dd, ee, aa, bb, cc, x[ 3], 11); - HH(cc, dd, ee, aa, bb, x[10], 13); - HH(bb, cc, dd, ee, aa, x[14], 6); - HH(aa, bb, cc, dd, ee, x[ 4], 7); - HH(ee, aa, bb, cc, dd, x[ 9], 14); - HH(dd, ee, aa, bb, cc, x[15], 9); - HH(cc, dd, ee, aa, bb, x[ 8], 13); - HH(bb, cc, dd, ee, aa, x[ 1], 15); - HH(aa, bb, cc, dd, ee, x[ 2], 14); - HH(ee, aa, bb, cc, dd, x[ 7], 8); - HH(dd, ee, aa, bb, cc, x[ 0], 13); - HH(cc, dd, ee, aa, bb, x[ 6], 6); - HH(bb, cc, dd, ee, aa, x[13], 5); - HH(aa, bb, cc, dd, ee, x[11], 12); - HH(ee, aa, bb, cc, dd, x[ 5], 7); - HH(dd, ee, aa, bb, cc, x[12], 5); - - /* round 4 */ - II(cc, dd, ee, aa, bb, x[ 1], 11); - II(bb, cc, dd, ee, aa, x[ 9], 12); - II(aa, bb, cc, dd, ee, x[11], 14); - II(ee, aa, bb, cc, dd, x[10], 15); - II(dd, ee, aa, bb, cc, x[ 0], 14); - II(cc, dd, ee, aa, bb, x[ 8], 15); - II(bb, cc, dd, ee, aa, x[12], 9); - II(aa, bb, cc, dd, ee, x[ 4], 8); - II(ee, aa, bb, cc, dd, x[13], 9); - II(dd, ee, aa, bb, cc, x[ 3], 14); - II(cc, dd, ee, aa, bb, x[ 7], 5); - II(bb, cc, dd, ee, aa, x[15], 6); - II(aa, bb, cc, dd, ee, x[14], 8); - II(ee, aa, bb, cc, dd, x[ 5], 6); - II(dd, ee, aa, bb, cc, x[ 6], 5); - II(cc, dd, ee, aa, bb, x[ 2], 12); - - /* round 5 */ - JJ(bb, cc, dd, ee, aa, x[ 4], 9); - JJ(aa, bb, cc, dd, ee, x[ 0], 15); - JJ(ee, aa, bb, cc, dd, x[ 5], 5); - JJ(dd, ee, aa, bb, cc, x[ 9], 11); - JJ(cc, dd, ee, aa, bb, x[ 7], 6); - JJ(bb, cc, dd, ee, aa, x[12], 8); - JJ(aa, bb, cc, dd, ee, x[ 2], 13); - JJ(ee, aa, bb, cc, dd, x[10], 12); - JJ(dd, ee, aa, bb, cc, x[14], 5); - JJ(cc, dd, ee, aa, bb, x[ 1], 12); - JJ(bb, cc, dd, ee, aa, x[ 3], 13); - JJ(aa, bb, cc, dd, ee, x[ 8], 14); - JJ(ee, aa, bb, cc, dd, x[11], 11); - JJ(dd, ee, aa, bb, cc, x[ 6], 8); - JJ(cc, dd, ee, aa, bb, x[15], 5); - JJ(bb, cc, dd, ee, aa, x[13], 6); - - /* parallel round 1 */ - JJJ(aaa, bbb, ccc, ddd, eee, x[ 5], 8); - JJJ(eee, aaa, bbb, ccc, ddd, x[14], 9); - JJJ(ddd, eee, aaa, bbb, ccc, x[ 7], 9); - JJJ(ccc, ddd, eee, aaa, bbb, x[ 0], 11); - JJJ(bbb, ccc, ddd, eee, aaa, x[ 9], 13); - JJJ(aaa, bbb, ccc, ddd, eee, x[ 2], 15); - JJJ(eee, aaa, bbb, ccc, ddd, x[11], 15); - JJJ(ddd, eee, aaa, bbb, ccc, x[ 4], 5); - JJJ(ccc, ddd, eee, aaa, bbb, x[13], 7); - JJJ(bbb, ccc, ddd, eee, aaa, x[ 6], 7); - JJJ(aaa, bbb, ccc, ddd, eee, x[15], 8); - JJJ(eee, aaa, bbb, ccc, ddd, x[ 8], 11); - JJJ(ddd, eee, aaa, bbb, ccc, x[ 1], 14); - JJJ(ccc, ddd, eee, aaa, bbb, x[10], 14); - JJJ(bbb, ccc, ddd, eee, aaa, x[ 3], 12); - JJJ(aaa, bbb, ccc, ddd, eee, x[12], 6); - - /* parallel round 2 */ - III(eee, aaa, bbb, ccc, ddd, x[ 6], 9); - III(ddd, eee, aaa, bbb, ccc, x[11], 13); - III(ccc, ddd, eee, aaa, bbb, x[ 3], 15); - III(bbb, ccc, ddd, eee, aaa, x[ 7], 7); - III(aaa, bbb, ccc, ddd, eee, x[ 0], 12); - III(eee, aaa, bbb, ccc, ddd, x[13], 8); - III(ddd, eee, aaa, bbb, ccc, x[ 5], 9); - III(ccc, ddd, eee, aaa, bbb, x[10], 11); - III(bbb, ccc, ddd, eee, aaa, x[14], 7); - III(aaa, bbb, ccc, ddd, eee, x[15], 7); - III(eee, aaa, bbb, ccc, ddd, x[ 8], 12); - III(ddd, eee, aaa, bbb, ccc, x[12], 7); - III(ccc, ddd, eee, aaa, bbb, x[ 4], 6); - III(bbb, ccc, ddd, eee, aaa, x[ 9], 15); - III(aaa, bbb, ccc, ddd, eee, x[ 1], 13); - III(eee, aaa, bbb, ccc, ddd, x[ 2], 11); - - /* parallel round 3 */ - HHH(ddd, eee, aaa, bbb, ccc, x[15], 9); - HHH(ccc, ddd, eee, aaa, bbb, x[ 5], 7); - HHH(bbb, ccc, ddd, eee, aaa, x[ 1], 15); - HHH(aaa, bbb, ccc, ddd, eee, x[ 3], 11); - HHH(eee, aaa, bbb, ccc, ddd, x[ 7], 8); - HHH(ddd, eee, aaa, bbb, ccc, x[14], 6); - HHH(ccc, ddd, eee, aaa, bbb, x[ 6], 6); - HHH(bbb, ccc, ddd, eee, aaa, x[ 9], 14); - HHH(aaa, bbb, ccc, ddd, eee, x[11], 12); - HHH(eee, aaa, bbb, ccc, ddd, x[ 8], 13); - HHH(ddd, eee, aaa, bbb, ccc, x[12], 5); - HHH(ccc, ddd, eee, aaa, bbb, x[ 2], 14); - HHH(bbb, ccc, ddd, eee, aaa, x[10], 13); - HHH(aaa, bbb, ccc, ddd, eee, x[ 0], 13); - HHH(eee, aaa, bbb, ccc, ddd, x[ 4], 7); - HHH(ddd, eee, aaa, bbb, ccc, x[13], 5); - - /* parallel round 4 */ - GGG(ccc, ddd, eee, aaa, bbb, x[ 8], 15); - GGG(bbb, ccc, ddd, eee, aaa, x[ 6], 5); - GGG(aaa, bbb, ccc, ddd, eee, x[ 4], 8); - GGG(eee, aaa, bbb, ccc, ddd, x[ 1], 11); - GGG(ddd, eee, aaa, bbb, ccc, x[ 3], 14); - GGG(ccc, ddd, eee, aaa, bbb, x[11], 14); - GGG(bbb, ccc, ddd, eee, aaa, x[15], 6); - GGG(aaa, bbb, ccc, ddd, eee, x[ 0], 14); - GGG(eee, aaa, bbb, ccc, ddd, x[ 5], 6); - GGG(ddd, eee, aaa, bbb, ccc, x[12], 9); - GGG(ccc, ddd, eee, aaa, bbb, x[ 2], 12); - GGG(bbb, ccc, ddd, eee, aaa, x[13], 9); - GGG(aaa, bbb, ccc, ddd, eee, x[ 9], 12); - GGG(eee, aaa, bbb, ccc, ddd, x[ 7], 5); - GGG(ddd, eee, aaa, bbb, ccc, x[10], 15); - GGG(ccc, ddd, eee, aaa, bbb, x[14], 8); - - /* parallel round 5 */ - FFF(bbb, ccc, ddd, eee, aaa, x[12] , 8); - FFF(aaa, bbb, ccc, ddd, eee, x[15] , 5); - FFF(eee, aaa, bbb, ccc, ddd, x[10] , 12); - FFF(ddd, eee, aaa, bbb, ccc, x[ 4] , 9); - FFF(ccc, ddd, eee, aaa, bbb, x[ 1] , 12); - FFF(bbb, ccc, ddd, eee, aaa, x[ 5] , 5); - FFF(aaa, bbb, ccc, ddd, eee, x[ 8] , 14); - FFF(eee, aaa, bbb, ccc, ddd, x[ 7] , 6); - FFF(ddd, eee, aaa, bbb, ccc, x[ 6] , 8); - FFF(ccc, ddd, eee, aaa, bbb, x[ 2] , 13); - FFF(bbb, ccc, ddd, eee, aaa, x[13] , 6); - FFF(aaa, bbb, ccc, ddd, eee, x[14] , 5); - FFF(eee, aaa, bbb, ccc, ddd, x[ 0] , 15); - FFF(ddd, eee, aaa, bbb, ccc, x[ 3] , 13); - FFF(ccc, ddd, eee, aaa, bbb, x[ 9] , 11); - FFF(bbb, ccc, ddd, eee, aaa, x[11] , 11); - - /* combine results */ - ddd += cc + _state[1]; /* final result for _state[0] */ - _state[1] = _state[2] + dd + eee; - _state[2] = _state[3] + ee + aaa; - _state[3] = _state[4] + aa + bbb; - _state[4] = _state[0] + bb + ccc; - _state[0] = ddd; - - //Zeroize sensitive information. - x[] = 0; - } - - public: - enum blockSize = 512; - - /** - * Use this to feed the digest with data. - * Also implements the $(REF isOutputRange, std,range,primitives) - * interface for `ubyte` and `const(ubyte)[]`. - * - * Example: - * ---- - * RIPEMD160 dig; - * dig.put(cast(ubyte) 0); //single ubyte - * dig.put(cast(ubyte) 0, cast(ubyte) 0); //variadic - * ubyte[10] buf; - * dig.put(buf); //buffer - * ---- - */ - void put(scope const(ubyte)[] data...) @trusted pure nothrow @nogc - { - uint i, index, partLen; - auto inputLen = data.length; - - //Compute number of bytes mod 64 - index = (cast(uint)_count >> 3) & (64 - 1); - - //Update number of bits - _count += inputLen * 8; - - partLen = 64 - index; - - //Transform as many times as possible - if (inputLen >= partLen) - { - (&_buffer[index])[0 .. partLen] = data.ptr[0 .. partLen]; - transform(&_buffer); - - for (i = partLen; i + 63 < inputLen; i += 64) - { - transform(cast(const(ubyte[64])*)(data[i .. i + 64].ptr)); - } - - index = 0; - } - else - { - i = 0; - } - - /* Buffer remaining input */ - if (inputLen - i) - (&_buffer[index])[0 .. inputLen-i] = (&data[i])[0 .. inputLen-i]; - } - - /** - * Used to (re)initialize the RIPEMD160 digest. - * - * Note: - * For this RIPEMD160 Digest implementation calling start after default construction - * is not necessary. Calling start is only necessary to reset the Digest. - * - * Generic code which deals with different Digest types should always call start though. - * - * Example: - * -------- - * RIPEMD160 digest; - * //digest.start(); //Not necessary - * digest.put(0); - * -------- - */ - void start() @safe pure nothrow @nogc - { - this = RIPEMD160.init; - } - - /** - * Returns the finished RIPEMD160 hash. This also calls $(LREF start) to - * reset the internal state. - * - * Example: - * -------- - * //Simple example - * RIPEMD160 hash; - * hash.start(); - * hash.put(cast(ubyte) 0); - * ubyte[20] result = hash.finish(); - * assert(toHexString(result) == "C81B94933420221A7AC004A90242D8B1D3E5070D"); - * -------- - */ - ubyte[20] finish() @trusted pure nothrow @nogc - { - import std.bitmanip : nativeToLittleEndian; - - ubyte[20] data = void; - ubyte[8] bits = void; - uint index, padLen; - - //Save number of bits - bits[0 .. 8] = nativeToLittleEndian(_count)[]; - - //Pad out to 56 mod 64 - index = (cast(uint)_count >> 3) & (64 - 1); - padLen = (index < 56) ? (56 - index) : (120 - index); - put(_padding[0 .. padLen]); - - //Append length (before padding) - put(bits); - - //Store state in digest - data[0 .. 4] = nativeToLittleEndian(_state[0])[]; - data[4 .. 8] = nativeToLittleEndian(_state[1])[]; - data[8 .. 12] = nativeToLittleEndian(_state[2])[]; - data[12 .. 16] = nativeToLittleEndian(_state[3])[]; - data[16 .. 20] = nativeToLittleEndian(_state[4])[]; - - /* Zeroize sensitive information. */ - start(); - return data; - } -} - -/// -@safe unittest -{ - //Simple example, hashing a string using ripemd160Of helper function - ubyte[20] hash = ripemd160Of("abc"); - //Let's get a hash string - assert(toHexString(hash) == "8EB208F7E05D987A9B044A8E98C6B087F15A0BFC"); -} - -/// -@safe unittest -{ - //Using the basic API - RIPEMD160 hash; - hash.start(); - ubyte[1024] data; - //Initialize data here... - hash.put(data); - ubyte[20] result = hash.finish(); -} - -/// -@safe unittest -{ - //Let's use the template features: - void doSomething(T)(ref T hash) - if (isDigest!T) - { - hash.put(cast(ubyte) 0); - } - RIPEMD160 md; - md.start(); - doSomething(md); - assert(toHexString(md.finish()) == "C81B94933420221A7AC004A90242D8B1D3E5070D"); -} - -/// -@safe unittest -{ - //Simple example - RIPEMD160 hash; - hash.start(); - hash.put(cast(ubyte) 0); - ubyte[20] result = hash.finish(); - assert(toHexString(result) == "C81B94933420221A7AC004A90242D8B1D3E5070D"); -} - -@safe unittest -{ - assert(isDigest!RIPEMD160); -} - -@system unittest -{ - import std.conv : hexString; - import std.range; - - ubyte[20] digest; - - RIPEMD160 md; - md.put(cast(ubyte[])"abcdef"); - md.start(); - md.put(cast(ubyte[])""); - assert(md.finish() == cast(ubyte[]) hexString!"9c1185a5c5e9fc54612808977ee8f548b2258d31"); - - digest = ripemd160Of(""); - assert(digest == cast(ubyte[]) hexString!"9c1185a5c5e9fc54612808977ee8f548b2258d31"); - - digest = ripemd160Of("a"); - assert(digest == cast(ubyte[]) hexString!"0bdc9d2d256b3ee9daae347be6f4dc835a467ffe"); - - digest = ripemd160Of("abc"); - assert(digest == cast(ubyte[]) hexString!"8eb208f7e05d987a9b044a8e98c6b087f15a0bfc"); - - digest = ripemd160Of("abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq"); - assert(digest == cast(ubyte[]) hexString!"12a053384a9c0c88e405a06c27dcf49ada62eb2b"); - - digest = ripemd160Of("message digest"); - assert(digest == cast(ubyte[]) hexString!"5d0689ef49d2fae572b881b123a85ffa21595f36"); - - digest = ripemd160Of("abcdefghijklmnopqrstuvwxyz"); - assert(digest == cast(ubyte[]) hexString!"f71c27109c692c1b56bbdceb5b9d2865b3708dbc"); - - digest = ripemd160Of("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"); - assert(digest == cast(ubyte[]) hexString!"b0e20b6e3116640286ed3a87a5713079b21f5189"); - - digest = ripemd160Of("1234567890123456789012345678901234567890"~ - "1234567890123456789012345678901234567890"); - assert(digest == cast(ubyte[]) hexString!"9b752e45573d4b39f4dbd3323cab82bf63326bfb"); - - enum ubyte[20] input = cast(ubyte[20]) hexString!"f71c27109c692c1b56bbdceb5b9d2865b3708dbc"; - assert(toHexString(input) - == "F71C27109C692C1B56BBDCEB5B9D2865B3708DBC"); - - ubyte[] onemilliona = new ubyte[1000000]; - onemilliona[] = 'a'; - digest = ripemd160Of(onemilliona); - assert(digest == cast(ubyte[]) hexString!"52783243c1697bdbe16d37f97f68f08325dc1528"); - - auto oneMillionRange = repeat!ubyte(cast(ubyte)'a', 1000000); - digest = ripemd160Of(oneMillionRange); - assert(digest == cast(ubyte[]) hexString!"52783243c1697bdbe16d37f97f68f08325dc1528"); -} - -/** - * This is a convenience alias for $(REF digest, std,digest) using the - * RIPEMD160 implementation. - */ -//simple alias doesn't work here, hope this gets inlined... -auto ripemd160Of(T...)(T data) -{ - return digest!(RIPEMD160, T)(data); -} - -/// -@safe unittest -{ - ubyte[20] hash = ripemd160Of("abc"); - assert(hash == digest!RIPEMD160("abc")); -} - -/** - * OOP API RIPEMD160 implementation. - * See `std.digest` for differences between template and OOP API. - * - * This is an alias for $(D $(REF WrapperDigest, std,digest)!RIPEMD160), - * see there for more information. - */ -alias RIPEMD160Digest = WrapperDigest!RIPEMD160; - -/// -@safe unittest -{ - //Simple example, hashing a string using Digest.digest helper function - auto md = new RIPEMD160Digest(); - ubyte[] hash = md.digest("abc"); - //Let's get a hash string - assert(toHexString(hash) == "8EB208F7E05D987A9B044A8E98C6B087F15A0BFC"); -} - -/// -@system unittest -{ - //Let's use the OOP features: - void test(Digest dig) - { - dig.put(cast(ubyte) 0); - } - auto md = new RIPEMD160Digest(); - test(md); - - //Let's use a custom buffer: - ubyte[20] buf; - ubyte[] result = md.finish(buf[]); - assert(toHexString(result) == "C81B94933420221A7AC004A90242D8B1D3E5070D"); -} - -@system unittest -{ - import std.conv : hexString; - auto md = new RIPEMD160Digest(); - - md.put(cast(ubyte[])"abcdef"); - md.reset(); - md.put(cast(ubyte[])""); - assert(md.finish() == cast(ubyte[]) hexString!"9c1185a5c5e9fc54612808977ee8f548b2258d31"); - - md.put(cast(ubyte[])"abcdefghijklmnopqrstuvwxyz"); - ubyte[20] result; - auto result2 = md.finish(result[]); - assert(result[0 .. 20] == result2 && result2 == cast(ubyte[]) hexString!"f71c27109c692c1b56bbdceb5b9d2865b3708dbc"); - - debug - { - import std.exception; - assertThrown!Error(md.finish(result[0 .. 19])); - } - - assert(md.length == 20); - - assert(md.digest("") == cast(ubyte[]) hexString!"9c1185a5c5e9fc54612808977ee8f548b2258d31"); - - assert(md.digest("a") == cast(ubyte[]) hexString!"0bdc9d2d256b3ee9daae347be6f4dc835a467ffe"); - - assert(md.digest("abc") == cast(ubyte[]) hexString!"8eb208f7e05d987a9b044a8e98c6b087f15a0bfc"); - - assert(md.digest("abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq") - == cast(ubyte[]) hexString!"12a053384a9c0c88e405a06c27dcf49ada62eb2b"); - - assert(md.digest("message digest") == cast(ubyte[]) hexString!"5d0689ef49d2fae572b881b123a85ffa21595f36"); - - assert(md.digest("abcdefghijklmnopqrstuvwxyz") - == cast(ubyte[]) hexString!"f71c27109c692c1b56bbdceb5b9d2865b3708dbc"); - - assert(md.digest("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789") - == cast(ubyte[]) hexString!"b0e20b6e3116640286ed3a87a5713079b21f5189"); - - assert(md.digest("1234567890123456789012345678901234567890", - "1234567890123456789012345678901234567890") - == cast(ubyte[]) hexString!"9b752e45573d4b39f4dbd3323cab82bf63326bfb"); - - assert(md.digest(new ubyte[160/8]) // 160 zero bits - == cast(ubyte[]) hexString!"5c00bd4aca04a9057c09b20b05f723f2e23deb65"); -} diff --git a/phobos/std/digest/sha.d b/phobos/std/digest/sha.d deleted file mode 100644 index 5c7c3d5..0000000 --- a/phobos/std/digest/sha.d +++ /dev/null @@ -1,1248 +0,0 @@ -// Written in the D programming language. -/** - * Computes SHA1 and SHA2 hashes of arbitrary data. SHA hashes are 20 to 64 byte - * quantities (depending on the SHA algorithm) that are like a checksum or CRC, - * but are more robust. - * -$(SCRIPT inhibitQuickIndex = 1;) - -$(DIVC quickindex, -$(BOOKTABLE , -$(TR $(TH Category) $(TH Functions) -) -$(TR $(TDNW Template API) $(TD $(MYREF SHA1) -) -) -$(TR $(TDNW OOP API) $(TD $(MYREF SHA1Digest)) -) -$(TR $(TDNW Helpers) $(TD $(MYREF sha1Of)) -) -) -) - - * SHA2 comes in several different versions, all supported by this module: - * SHA-224, SHA-256, SHA-384, SHA-512, SHA-512/224 and SHA-512/256. - * - * This module conforms to the APIs defined in $(MREF std, digest). To understand the - * differences between the template and the OOP API, see $(MREF std, digest). - * - * This module publicly imports `std.digest` and can be used as a stand-alone - * module. - * - * License: $(HTTP www.boost.org/LICENSE_1_0.txt, Boost License 1.0). - * - * CTFE: - * Digests do not work in CTFE - * - * Authors: - * The routines and algorithms are derived from the - * $(I Secure Hash Signature Standard (SHS) (FIPS PUB 180-2)). $(BR ) - * Kai Nacke, Johannes Pfau, Nick Sabalausky - * - * References: - * $(UL - * $(LI $(LINK2 http://csrc.nist.gov/publications/fips/fips180-2/fips180-2withchangenotice.pdf, FIPS PUB180-2)) - * $(LI $(LINK2 http://software.intel.com/en-us/articles/improving-the-performance-of-the-secure-hash-algorithm-1/, Fast implementation of SHA1)) - * $(LI $(LINK2 http://en.wikipedia.org/wiki/Secure_Hash_Algorithm, Wikipedia article about SHA)) - * ) - * - * Source: $(PHOBOSSRC std/digest/sha.d) - * - */ - -/* Copyright Kai Nacke 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.digest.sha; - -/// -@safe unittest -{ - //Template API - import std.digest.sha; - - ubyte[20] hash1 = sha1Of("abc"); - assert(toHexString(hash1) == "A9993E364706816ABA3E25717850C26C9CD0D89D"); - - ubyte[28] hash224 = sha224Of("abc"); - assert(toHexString(hash224) == "23097D223405D8228642A477BDA255B32AADBCE4BDA0B3F7E36C9DA7"); - - //Feeding data - ubyte[1024] data; - SHA1 sha1; - sha1.start(); - sha1.put(data[]); - sha1.start(); //Start again - sha1.put(data[]); - hash1 = sha1.finish(); -} - -/// -@safe unittest -{ - //OOP API - import std.digest.sha; - - auto sha1 = new SHA1Digest(); - ubyte[] hash1 = sha1.digest("abc"); - assert(toHexString(hash1) == "A9993E364706816ABA3E25717850C26C9CD0D89D"); - - auto sha224 = new SHA224Digest(); - ubyte[] hash224 = sha224.digest("abc"); - assert(toHexString(hash224) == "23097D223405D8228642A477BDA255B32AADBCE4BDA0B3F7E36C9DA7"); - - //Feeding data - ubyte[1024] data; - sha1.put(data[]); - sha1.reset(); //Start again - sha1.put(data[]); - hash1 = sha1.finish(); -} - -version (D_InlineAsm_X86) -{ - version (D_PIC) {} // https://issues.dlang.org/show_bug.cgi?id=9378 - else private version = USE_SSSE3; -} -else version (D_InlineAsm_X86_64) -{ - private version = USE_SSSE3; -} - -import core.bitop; - -public import std.digest; - -/* - * Helper methods for encoding the buffer. - * Can be removed if the optimizer can inline the methods from std.bitmanip. - */ -version (LittleEndian) -{ - private alias nativeToBigEndian = bswap; - private alias bigEndianToNative = bswap; -} -else pragma(inline, true) private pure @nogc nothrow @safe -{ - uint nativeToBigEndian(uint val) { return val; } - ulong nativeToBigEndian(ulong val) { return val; } - alias bigEndianToNative = nativeToBigEndian; -} - -/** - * Template API SHA1/SHA2 implementation. Supports: SHA-1, SHA-224, SHA-256, - * SHA-384, SHA-512, SHA-512/224 and SHA-512/256. - * - * The hashBlockSize and digestSize are in bits. However, it's likely easier to - * simply use the convenience aliases: SHA1, SHA224, SHA256, SHA384, SHA512, - * SHA512_224 and SHA512_256. - * - * See `std.digest` for differences between template and OOP API. - */ -struct SHA(uint hashBlockSize, uint digestSize) -{ - enum blockSize = hashBlockSize; - - static assert(blockSize == 512 || blockSize == 1024, - "Invalid SHA blockSize, must be 512 or 1024"); - static assert(digestSize == 160 || digestSize == 224 || digestSize == 256 || digestSize == 384 || digestSize == 512, - "Invalid SHA digestSize, must be 224, 256, 384 or 512"); - static assert(!(blockSize == 512 && digestSize > 256), - "Invalid SHA digestSize for a blockSize of 512. The digestSize must be 160, 224 or 256."); - static assert(!(blockSize == 1024 && digestSize < 224), - "Invalid SHA digestSize for a blockSize of 1024. The digestSize must be 224, 256, 384 or 512."); - - static if (digestSize == 160) /* SHA-1 */ - { - version (USE_SSSE3) - { - import core.cpuid : ssse3; - import std.internal.digest.sha_SSSE3 : sse3_constants=constants, transformSSSE3; - - static void transform(uint[5]* state, const(ubyte[64])* block) pure nothrow @nogc - { - if (ssse3) - { - version (D_InlineAsm_X86_64) - // constants as extra argument for PIC - // see https://issues.dlang.org/show_bug.cgi?id=9378 - transformSSSE3(state, block, &sse3_constants); - else - transformSSSE3(state, block); - } - else - transformX86(state, block); - } - } - else - { - alias transform = transformX86; - } - } - else static if (blockSize == 512) /* SHA-224, SHA-256 */ - alias transform = transformSHA2!uint; - else static if (blockSize == 1024) /* SHA-384, SHA-512, SHA-512/224, SHA-512/256 */ - alias transform = transformSHA2!ulong; - else - static assert(0); - - private: - /* magic initialization constants - state (ABCDEFGH) */ - static if (blockSize == 512 && digestSize == 160) /* SHA-1 */ - { - uint[5] state = - [0x67452301,0xefcdab89,0x98badcfe,0x10325476,0xc3d2e1f0]; - } - else static if (blockSize == 512 && digestSize == 224) /* SHA-224 */ - { - uint[8] state = [ - 0xc1059ed8, 0x367cd507, 0x3070dd17, 0xf70e5939, - 0xffc00b31, 0x68581511, 0x64f98fa7, 0xbefa4fa4, - ]; - } - else static if (blockSize == 512 && digestSize == 256) /* SHA-256 */ - { - uint[8] state = [ - 0x6a09e667, 0xbb67ae85, 0x3c6ef372, 0xa54ff53a, - 0x510e527f, 0x9b05688c, 0x1f83d9ab, 0x5be0cd19, - ]; - } - else static if (blockSize == 1024 && digestSize == 224) /* SHA-512/224 */ - { - ulong[8] state = [ - 0x8C3D37C8_19544DA2, 0x73E19966_89DCD4D6, - 0x1DFAB7AE_32FF9C82, 0x679DD514_582F9FCF, - 0x0F6D2B69_7BD44DA8, 0x77E36F73_04C48942, - 0x3F9D85A8_6A1D36C8, 0x1112E6AD_91D692A1, - ]; - } - else static if (blockSize == 1024 && digestSize == 256) /* SHA-512/256 */ - { - ulong[8] state = [ - 0x22312194_FC2BF72C, 0x9F555FA3_C84C64C2, - 0x2393B86B_6F53B151, 0x96387719_5940EABD, - 0x96283EE2_A88EFFE3, 0xBE5E1E25_53863992, - 0x2B0199FC_2C85B8AA, 0x0EB72DDC_81C52CA2, - ]; - } - else static if (blockSize == 1024 && digestSize == 384) /* SHA-384 */ - { - ulong[8] state = [ - 0xcbbb9d5d_c1059ed8, 0x629a292a_367cd507, - 0x9159015a_3070dd17, 0x152fecd8_f70e5939, - 0x67332667_ffc00b31, 0x8eb44a87_68581511, - 0xdb0c2e0d_64f98fa7, 0x47b5481d_befa4fa4, - ]; - } - else static if (blockSize == 1024 && digestSize == 512) /* SHA-512 */ - { - ulong[8] state = [ - 0x6a09e667_f3bcc908, 0xbb67ae85_84caa73b, - 0x3c6ef372_fe94f82b, 0xa54ff53a_5f1d36f1, - 0x510e527f_ade682d1, 0x9b05688c_2b3e6c1f, - 0x1f83d9ab_fb41bd6b, 0x5be0cd19_137e2179, - ]; - } - else - static assert(0); - - /* constants */ - static if (blockSize == 512) - { - static immutable uint[64] constants = [ - 0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5, 0x3956c25b, 0x59f111f1, 0x923f82a4, 0xab1c5ed5, - 0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3, 0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174, - 0xe49b69c1, 0xefbe4786, 0x0fc19dc6, 0x240ca1cc, 0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da, - 0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7, 0xc6e00bf3, 0xd5a79147, 0x06ca6351, 0x14292967, - 0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13, 0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85, - 0xa2bfe8a1, 0xa81a664b, 0xc24b8b70, 0xc76c51a3, 0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070, - 0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5, 0x391c0cb3, 0x4ed8aa4a, 0x5b9cca4f, 0x682e6ff3, - 0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208, 0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2, - ]; - } - else static if (blockSize == 1024) - { - static immutable ulong[80] constants = [ - 0x428a2f98_d728ae22, 0x71374491_23ef65cd, 0xb5c0fbcf_ec4d3b2f, 0xe9b5dba5_8189dbbc, - 0x3956c25b_f348b538, 0x59f111f1_b605d019, 0x923f82a4_af194f9b, 0xab1c5ed5_da6d8118, - 0xd807aa98_a3030242, 0x12835b01_45706fbe, 0x243185be_4ee4b28c, 0x550c7dc3_d5ffb4e2, - 0x72be5d74_f27b896f, 0x80deb1fe_3b1696b1, 0x9bdc06a7_25c71235, 0xc19bf174_cf692694, - 0xe49b69c1_9ef14ad2, 0xefbe4786_384f25e3, 0x0fc19dc6_8b8cd5b5, 0x240ca1cc_77ac9c65, - 0x2de92c6f_592b0275, 0x4a7484aa_6ea6e483, 0x5cb0a9dc_bd41fbd4, 0x76f988da_831153b5, - 0x983e5152_ee66dfab, 0xa831c66d_2db43210, 0xb00327c8_98fb213f, 0xbf597fc7_beef0ee4, - 0xc6e00bf3_3da88fc2, 0xd5a79147_930aa725, 0x06ca6351_e003826f, 0x14292967_0a0e6e70, - 0x27b70a85_46d22ffc, 0x2e1b2138_5c26c926, 0x4d2c6dfc_5ac42aed, 0x53380d13_9d95b3df, - 0x650a7354_8baf63de, 0x766a0abb_3c77b2a8, 0x81c2c92e_47edaee6, 0x92722c85_1482353b, - 0xa2bfe8a1_4cf10364, 0xa81a664b_bc423001, 0xc24b8b70_d0f89791, 0xc76c51a3_0654be30, - 0xd192e819_d6ef5218, 0xd6990624_5565a910, 0xf40e3585_5771202a, 0x106aa070_32bbd1b8, - 0x19a4c116_b8d2d0c8, 0x1e376c08_5141ab53, 0x2748774c_df8eeb99, 0x34b0bcb5_e19b48a8, - 0x391c0cb3_c5c95a63, 0x4ed8aa4a_e3418acb, 0x5b9cca4f_7763e373, 0x682e6ff3_d6b2b8a3, - 0x748f82ee_5defb2fc, 0x78a5636f_43172f60, 0x84c87814_a1f0ab72, 0x8cc70208_1a6439ec, - 0x90befffa_23631e28, 0xa4506ceb_de82bde9, 0xbef9a3f7_b2c67915, 0xc67178f2_e372532b, - 0xca273ece_ea26619c, 0xd186b8c7_21c0c207, 0xeada7dd6_cde0eb1e, 0xf57d4f7f_ee6ed178, - 0x06f067aa_72176fba, 0x0a637dc5_a2c898a6, 0x113f9804_bef90dae, 0x1b710b35_131c471b, - 0x28db77f5_23047d84, 0x32caab7b_40c72493, 0x3c9ebe0a_15c9bebc, 0x431d67c4_9c100d4c, - 0x4cc5d4be_cb3e42b6, 0x597f299c_fc657e2a, 0x5fcb6fab_3ad6faec, 0x6c44198c_4a475817, - ]; - } - else - static assert(0); - - /* - * number of bits, modulo 2^64 (ulong[1]) or 2^128 (ulong[2]), - * should just use ucent instead of ulong[2] once it's available - */ - ulong[blockSize/512] count; - ubyte[blockSize/8] buffer; /* input buffer */ - - static immutable ubyte[128] padding = - [ - 0x80, 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, 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, 0, 0, 0, 0, 0, 0, 0, 0, 0, - - 0x00, 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, 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, 0, 0, 0, 0, 0, 0, 0, 0, 0 - ]; - - /* - * Basic SHA1/SHA2 functions. - */ - pragma(inline, true) - static @safe pure nothrow @nogc - { - /* All SHA1/SHA2 */ - T Ch(T)(T x, T y, T z) { return z ^ (x & (y ^ z)); } - T Maj(T)(T x, T y, T z) { return (x & y) | (z & (x ^ y)); } - - /* SHA-1 */ - uint Parity(uint x, uint y, uint z) { return x ^ y ^ z; } - - /* SHA-224, SHA-256 */ - uint BigSigma0(uint x) { return core.bitop.ror(x, 2) ^ core.bitop.ror(x, 13) ^ core.bitop.ror(x, 22); } - uint BigSigma1(uint x) { return core.bitop.ror(x, 6) ^ core.bitop.ror(x, 11) ^ core.bitop.ror(x, 25); } - uint SmSigma0(uint x) { return core.bitop.ror(x, 7) ^ core.bitop.ror(x, 18) ^ x >> 3; } - uint SmSigma1(uint x) { return core.bitop.ror(x, 17) ^ core.bitop.ror(x, 19) ^ x >> 10; } - - /* SHA-384, SHA-512, SHA-512/224, SHA-512/256 */ - ulong BigSigma0(ulong x) { return core.bitop.ror(x, 28) ^ core.bitop.ror(x, 34) ^ core.bitop.ror(x, 39); } - ulong BigSigma1(ulong x) { return core.bitop.ror(x, 14) ^ core.bitop.ror(x, 18) ^ core.bitop.ror(x, 41); } - ulong SmSigma0(ulong x) { return core.bitop.ror(x, 1) ^ core.bitop.ror(x, 8) ^ x >> 7; } - ulong SmSigma1(ulong x) { return core.bitop.ror(x, 19) ^ core.bitop.ror(x, 61) ^ x >> 6; } - } - - /* - * SHA1 basic transformation. Transforms state based on block. - */ - static void T_0_15(int i, const(ubyte[64])* input, ref uint[16] W, uint A, ref uint B, uint C, uint D, - uint E, ref uint T) pure nothrow @nogc - { - uint Wi = W[i] = bigEndianToNative(*cast(uint*) &((*input)[i*4])); - T = Ch(B, C, D) + E + core.bitop.rol(A, 5) + Wi + 0x5a827999; - B = core.bitop.rol(B, 30); - } - - static void T_16_19(int i, ref uint[16] W, uint A, ref uint B, uint C, uint D, uint E, ref uint T) - pure nothrow @nogc - { - W[i&15] = core.bitop.rol(W[(i-3)&15] ^ W[(i-8)&15] ^ W[(i-14)&15] ^ W[(i-16)&15], 1); - T = Ch(B, C, D) + E + core.bitop.rol(A, 5) + W[i&15] + 0x5a827999; - B = core.bitop.rol(B, 30); - } - - static void T_20_39(int i, ref uint[16] W, uint A, ref uint B, uint C, uint D, uint E, - ref uint T) pure nothrow @nogc - { - W[i&15] = core.bitop.rol(W[(i-3)&15] ^ W[(i-8)&15] ^ W[(i-14)&15] ^ W[(i-16)&15], 1); - T = Parity(B, C, D) + E + core.bitop.rol(A, 5) + W[i&15] + 0x6ed9eba1; - B = core.bitop.rol(B, 30); - } - - static void T_40_59(int i, ref uint[16] W, uint A, ref uint B, uint C, uint D, uint E, - ref uint T) pure nothrow @nogc - { - W[i&15] = core.bitop.rol(W[(i-3)&15] ^ W[(i-8)&15] ^ W[(i-14)&15] ^ W[(i-16)&15], 1); - T = Maj(B, C, D) + E + core.bitop.rol(A, 5) + W[i&15] + 0x8f1bbcdc; - B = core.bitop.rol(B, 30); - } - - static void T_60_79(int i, ref uint[16] W, uint A, ref uint B, uint C, uint D, uint E, - ref uint T) pure nothrow @nogc - { - W[i&15] = core.bitop.rol(W[(i-3)&15] ^ W[(i-8)&15] ^ W[(i-14)&15] ^ W[(i-16)&15], 1); - T = Parity(B, C, D) + E + core.bitop.rol(A, 5) + W[i&15] + 0xca62c1d6; - B = core.bitop.rol(B, 30); - } - - private static void transformX86(uint[5]* state, const(ubyte[64])* block) pure nothrow @nogc - { - uint A, B, C, D, E, T; - uint[16] W = void; - - A = (*state)[0]; - B = (*state)[1]; - C = (*state)[2]; - D = (*state)[3]; - E = (*state)[4]; - - T_0_15 ( 0, block, W, A, B, C, D, E, T); - T_0_15 ( 1, block, W, T, A, B, C, D, E); - T_0_15 ( 2, block, W, E, T, A, B, C, D); - T_0_15 ( 3, block, W, D, E, T, A, B, C); - T_0_15 ( 4, block, W, C, D, E, T, A, B); - T_0_15 ( 5, block, W, B, C, D, E, T, A); - T_0_15 ( 6, block, W, A, B, C, D, E, T); - T_0_15 ( 7, block, W, T, A, B, C, D, E); - T_0_15 ( 8, block, W, E, T, A, B, C, D); - T_0_15 ( 9, block, W, D, E, T, A, B, C); - T_0_15 (10, block, W, C, D, E, T, A, B); - T_0_15 (11, block, W, B, C, D, E, T, A); - T_0_15 (12, block, W, A, B, C, D, E, T); - T_0_15 (13, block, W, T, A, B, C, D, E); - T_0_15 (14, block, W, E, T, A, B, C, D); - T_0_15 (15, block, W, D, E, T, A, B, C); - T_16_19(16, W, C, D, E, T, A, B); - T_16_19(17, W, B, C, D, E, T, A); - T_16_19(18, W, A, B, C, D, E, T); - T_16_19(19, W, T, A, B, C, D, E); - T_20_39(20, W, E, T, A, B, C, D); - T_20_39(21, W, D, E, T, A, B, C); - T_20_39(22, W, C, D, E, T, A, B); - T_20_39(23, W, B, C, D, E, T, A); - T_20_39(24, W, A, B, C, D, E, T); - T_20_39(25, W, T, A, B, C, D, E); - T_20_39(26, W, E, T, A, B, C, D); - T_20_39(27, W, D, E, T, A, B, C); - T_20_39(28, W, C, D, E, T, A, B); - T_20_39(29, W, B, C, D, E, T, A); - T_20_39(30, W, A, B, C, D, E, T); - T_20_39(31, W, T, A, B, C, D, E); - T_20_39(32, W, E, T, A, B, C, D); - T_20_39(33, W, D, E, T, A, B, C); - T_20_39(34, W, C, D, E, T, A, B); - T_20_39(35, W, B, C, D, E, T, A); - T_20_39(36, W, A, B, C, D, E, T); - T_20_39(37, W, T, A, B, C, D, E); - T_20_39(38, W, E, T, A, B, C, D); - T_20_39(39, W, D, E, T, A, B, C); - T_40_59(40, W, C, D, E, T, A, B); - T_40_59(41, W, B, C, D, E, T, A); - T_40_59(42, W, A, B, C, D, E, T); - T_40_59(43, W, T, A, B, C, D, E); - T_40_59(44, W, E, T, A, B, C, D); - T_40_59(45, W, D, E, T, A, B, C); - T_40_59(46, W, C, D, E, T, A, B); - T_40_59(47, W, B, C, D, E, T, A); - T_40_59(48, W, A, B, C, D, E, T); - T_40_59(49, W, T, A, B, C, D, E); - T_40_59(50, W, E, T, A, B, C, D); - T_40_59(51, W, D, E, T, A, B, C); - T_40_59(52, W, C, D, E, T, A, B); - T_40_59(53, W, B, C, D, E, T, A); - T_40_59(54, W, A, B, C, D, E, T); - T_40_59(55, W, T, A, B, C, D, E); - T_40_59(56, W, E, T, A, B, C, D); - T_40_59(57, W, D, E, T, A, B, C); - T_40_59(58, W, C, D, E, T, A, B); - T_40_59(59, W, B, C, D, E, T, A); - T_60_79(60, W, A, B, C, D, E, T); - T_60_79(61, W, T, A, B, C, D, E); - T_60_79(62, W, E, T, A, B, C, D); - T_60_79(63, W, D, E, T, A, B, C); - T_60_79(64, W, C, D, E, T, A, B); - T_60_79(65, W, B, C, D, E, T, A); - T_60_79(66, W, A, B, C, D, E, T); - T_60_79(67, W, T, A, B, C, D, E); - T_60_79(68, W, E, T, A, B, C, D); - T_60_79(69, W, D, E, T, A, B, C); - T_60_79(70, W, C, D, E, T, A, B); - T_60_79(71, W, B, C, D, E, T, A); - T_60_79(72, W, A, B, C, D, E, T); - T_60_79(73, W, T, A, B, C, D, E); - T_60_79(74, W, E, T, A, B, C, D); - T_60_79(75, W, D, E, T, A, B, C); - T_60_79(76, W, C, D, E, T, A, B); - T_60_79(77, W, B, C, D, E, T, A); - T_60_79(78, W, A, B, C, D, E, T); - T_60_79(79, W, T, A, B, C, D, E); - - (*state)[0] += E; - (*state)[1] += T; - (*state)[2] += A; - (*state)[3] += B; - (*state)[4] += C; - - /* Zeroize sensitive information. */ - W[] = 0; - } - - /* - * SHA2 basic transformation. Transforms state based on block. - */ - pragma(inline, true) - static void T_SHA2_0_15(Word)(int i, const(ubyte[blockSize/8])* input, ref Word[16] W, - Word A, Word B, Word C, ref Word D, Word E, Word F, Word G, ref Word H, Word K) - pure nothrow @nogc - { - Word Wi = W[i] = bigEndianToNative(*cast(Word*) &((*input)[i*Word.sizeof])); - Word T1 = H + BigSigma1(E) + Ch(E, F, G) + K + Wi; - Word T2 = BigSigma0(A) + Maj(A, B, C); - D += T1; - H = T1 + T2; - } - - // Temporarily disable inlining because it increases build speed by 10x. - // pragma(inline, true) - static void T_SHA2_16_79(Word)(int i, ref Word[16] W, - Word A, Word B, Word C, ref Word D, Word E, Word F, Word G, ref Word H, Word K) - pure nothrow @nogc - { - W[i&15] = SmSigma1(W[(i-2)&15]) + W[(i-7)&15] + SmSigma0(W[(i-15)&15]) + W[i&15]; - Word T1 = H + BigSigma1(E) + Ch(E, F, G) + K + W[i&15]; - Word T2 = BigSigma0(A) + Maj(A, B, C); - D += T1; - H = T1 + T2; - } - - private static void transformSHA2(Word)(Word[8]* state, const(ubyte[blockSize/8])* block) - pure nothrow @nogc - { - Word A, B, C, D, E, F, G, H; - Word[16] W = void; - - A = (*state)[0]; - B = (*state)[1]; - C = (*state)[2]; - D = (*state)[3]; - E = (*state)[4]; - F = (*state)[5]; - G = (*state)[6]; - H = (*state)[7]; - - T_SHA2_0_15!Word ( 0, block, W, A, B, C, D, E, F, G, H, constants[ 0]); - T_SHA2_0_15!Word ( 1, block, W, H, A, B, C, D, E, F, G, constants[ 1]); - T_SHA2_0_15!Word ( 2, block, W, G, H, A, B, C, D, E, F, constants[ 2]); - T_SHA2_0_15!Word ( 3, block, W, F, G, H, A, B, C, D, E, constants[ 3]); - T_SHA2_0_15!Word ( 4, block, W, E, F, G, H, A, B, C, D, constants[ 4]); - T_SHA2_0_15!Word ( 5, block, W, D, E, F, G, H, A, B, C, constants[ 5]); - T_SHA2_0_15!Word ( 6, block, W, C, D, E, F, G, H, A, B, constants[ 6]); - T_SHA2_0_15!Word ( 7, block, W, B, C, D, E, F, G, H, A, constants[ 7]); - T_SHA2_0_15!Word ( 8, block, W, A, B, C, D, E, F, G, H, constants[ 8]); - T_SHA2_0_15!Word ( 9, block, W, H, A, B, C, D, E, F, G, constants[ 9]); - T_SHA2_0_15!Word (10, block, W, G, H, A, B, C, D, E, F, constants[10]); - T_SHA2_0_15!Word (11, block, W, F, G, H, A, B, C, D, E, constants[11]); - T_SHA2_0_15!Word (12, block, W, E, F, G, H, A, B, C, D, constants[12]); - T_SHA2_0_15!Word (13, block, W, D, E, F, G, H, A, B, C, constants[13]); - T_SHA2_0_15!Word (14, block, W, C, D, E, F, G, H, A, B, constants[14]); - T_SHA2_0_15!Word (15, block, W, B, C, D, E, F, G, H, A, constants[15]); - T_SHA2_16_79!Word(16, W, A, B, C, D, E, F, G, H, constants[16]); - T_SHA2_16_79!Word(17, W, H, A, B, C, D, E, F, G, constants[17]); - T_SHA2_16_79!Word(18, W, G, H, A, B, C, D, E, F, constants[18]); - T_SHA2_16_79!Word(19, W, F, G, H, A, B, C, D, E, constants[19]); - T_SHA2_16_79!Word(20, W, E, F, G, H, A, B, C, D, constants[20]); - T_SHA2_16_79!Word(21, W, D, E, F, G, H, A, B, C, constants[21]); - T_SHA2_16_79!Word(22, W, C, D, E, F, G, H, A, B, constants[22]); - T_SHA2_16_79!Word(23, W, B, C, D, E, F, G, H, A, constants[23]); - T_SHA2_16_79!Word(24, W, A, B, C, D, E, F, G, H, constants[24]); - T_SHA2_16_79!Word(25, W, H, A, B, C, D, E, F, G, constants[25]); - T_SHA2_16_79!Word(26, W, G, H, A, B, C, D, E, F, constants[26]); - T_SHA2_16_79!Word(27, W, F, G, H, A, B, C, D, E, constants[27]); - T_SHA2_16_79!Word(28, W, E, F, G, H, A, B, C, D, constants[28]); - T_SHA2_16_79!Word(29, W, D, E, F, G, H, A, B, C, constants[29]); - T_SHA2_16_79!Word(30, W, C, D, E, F, G, H, A, B, constants[30]); - T_SHA2_16_79!Word(31, W, B, C, D, E, F, G, H, A, constants[31]); - T_SHA2_16_79!Word(32, W, A, B, C, D, E, F, G, H, constants[32]); - T_SHA2_16_79!Word(33, W, H, A, B, C, D, E, F, G, constants[33]); - T_SHA2_16_79!Word(34, W, G, H, A, B, C, D, E, F, constants[34]); - T_SHA2_16_79!Word(35, W, F, G, H, A, B, C, D, E, constants[35]); - T_SHA2_16_79!Word(36, W, E, F, G, H, A, B, C, D, constants[36]); - T_SHA2_16_79!Word(37, W, D, E, F, G, H, A, B, C, constants[37]); - T_SHA2_16_79!Word(38, W, C, D, E, F, G, H, A, B, constants[38]); - T_SHA2_16_79!Word(39, W, B, C, D, E, F, G, H, A, constants[39]); - T_SHA2_16_79!Word(40, W, A, B, C, D, E, F, G, H, constants[40]); - T_SHA2_16_79!Word(41, W, H, A, B, C, D, E, F, G, constants[41]); - T_SHA2_16_79!Word(42, W, G, H, A, B, C, D, E, F, constants[42]); - T_SHA2_16_79!Word(43, W, F, G, H, A, B, C, D, E, constants[43]); - T_SHA2_16_79!Word(44, W, E, F, G, H, A, B, C, D, constants[44]); - T_SHA2_16_79!Word(45, W, D, E, F, G, H, A, B, C, constants[45]); - T_SHA2_16_79!Word(46, W, C, D, E, F, G, H, A, B, constants[46]); - T_SHA2_16_79!Word(47, W, B, C, D, E, F, G, H, A, constants[47]); - T_SHA2_16_79!Word(48, W, A, B, C, D, E, F, G, H, constants[48]); - T_SHA2_16_79!Word(49, W, H, A, B, C, D, E, F, G, constants[49]); - T_SHA2_16_79!Word(50, W, G, H, A, B, C, D, E, F, constants[50]); - T_SHA2_16_79!Word(51, W, F, G, H, A, B, C, D, E, constants[51]); - T_SHA2_16_79!Word(52, W, E, F, G, H, A, B, C, D, constants[52]); - T_SHA2_16_79!Word(53, W, D, E, F, G, H, A, B, C, constants[53]); - T_SHA2_16_79!Word(54, W, C, D, E, F, G, H, A, B, constants[54]); - T_SHA2_16_79!Word(55, W, B, C, D, E, F, G, H, A, constants[55]); - T_SHA2_16_79!Word(56, W, A, B, C, D, E, F, G, H, constants[56]); - T_SHA2_16_79!Word(57, W, H, A, B, C, D, E, F, G, constants[57]); - T_SHA2_16_79!Word(58, W, G, H, A, B, C, D, E, F, constants[58]); - T_SHA2_16_79!Word(59, W, F, G, H, A, B, C, D, E, constants[59]); - T_SHA2_16_79!Word(60, W, E, F, G, H, A, B, C, D, constants[60]); - T_SHA2_16_79!Word(61, W, D, E, F, G, H, A, B, C, constants[61]); - T_SHA2_16_79!Word(62, W, C, D, E, F, G, H, A, B, constants[62]); - T_SHA2_16_79!Word(63, W, B, C, D, E, F, G, H, A, constants[63]); - - static if (is(Word == ulong)) - { - T_SHA2_16_79!Word(64, W, A, B, C, D, E, F, G, H, constants[64]); - T_SHA2_16_79!Word(65, W, H, A, B, C, D, E, F, G, constants[65]); - T_SHA2_16_79!Word(66, W, G, H, A, B, C, D, E, F, constants[66]); - T_SHA2_16_79!Word(67, W, F, G, H, A, B, C, D, E, constants[67]); - T_SHA2_16_79!Word(68, W, E, F, G, H, A, B, C, D, constants[68]); - T_SHA2_16_79!Word(69, W, D, E, F, G, H, A, B, C, constants[69]); - T_SHA2_16_79!Word(70, W, C, D, E, F, G, H, A, B, constants[70]); - T_SHA2_16_79!Word(71, W, B, C, D, E, F, G, H, A, constants[71]); - T_SHA2_16_79!Word(72, W, A, B, C, D, E, F, G, H, constants[72]); - T_SHA2_16_79!Word(73, W, H, A, B, C, D, E, F, G, constants[73]); - T_SHA2_16_79!Word(74, W, G, H, A, B, C, D, E, F, constants[74]); - T_SHA2_16_79!Word(75, W, F, G, H, A, B, C, D, E, constants[75]); - T_SHA2_16_79!Word(76, W, E, F, G, H, A, B, C, D, constants[76]); - T_SHA2_16_79!Word(77, W, D, E, F, G, H, A, B, C, constants[77]); - T_SHA2_16_79!Word(78, W, C, D, E, F, G, H, A, B, constants[78]); - T_SHA2_16_79!Word(79, W, B, C, D, E, F, G, H, A, constants[79]); - } - - (*state)[0] += A; - (*state)[1] += B; - (*state)[2] += C; - (*state)[3] += D; - (*state)[4] += E; - (*state)[5] += F; - (*state)[6] += G; - (*state)[7] += H; - - /* Zeroize sensitive information. */ - W[] = 0; - } - - public: - /** - * SHA initialization. Begins an SHA1/SHA2 operation. - * - * Note: - * For this SHA Digest implementation calling start after default construction - * is not necessary. Calling start is only necessary to reset the Digest. - * - * Generic code which deals with different Digest types should always call start though. - * - * Example: - * -------- - * SHA1 digest; - * //digest.start(); //Not necessary - * digest.put(0); - * -------- - */ - void start() @safe pure nothrow @nogc - { - this = typeof(this).init; - } - - /** - * Use this to feed the digest with data. - * Also implements the $(REF isOutputRange, std,range,primitives) - * interface for `ubyte` and `const(ubyte)[]`. - */ - void put(scope const(ubyte)[] input...) @trusted pure nothrow @nogc - { - enum blockSizeInBytes = blockSize/8; - - size_t i; - uint index, partLen; - auto inputLen = input.length; - - /* Compute number of bytes mod block size (64 or 128 bytes) */ - index = (cast(uint) count[0] >> 3) & (blockSizeInBytes - 1); - - /* Update number of bits */ - static if (blockSize == 512) - count[0] += inputLen * 8; - else static if (blockSize == 1024) - { - /* ugly hack to work around lack of ucent */ - auto oldCount0 = count[0]; - count[0] += inputLen * 8; - if (count[0] < oldCount0) - count[1]++; - } - else - static assert(0); - - partLen = blockSizeInBytes - index; - - /* Transform as many times as possible. */ - if (inputLen >= partLen) - { - (&buffer[index])[0 .. partLen] = input.ptr[0 .. partLen]; - transform (&state, &buffer); - - for (i = partLen; i + blockSizeInBytes-1 < inputLen; i += blockSizeInBytes) - transform(&state, cast(ubyte[blockSizeInBytes]*)(input.ptr + i)); - - index = 0; - } - else - i = 0; - - /* Buffer remaining input */ - if (inputLen - i) - (&buffer[index])[0 .. inputLen-i] = (&input[i])[0 .. inputLen-i]; - } - - @safe unittest - { - typeof(this) dig; - dig.put(cast(ubyte) 0); //single ubyte - dig.put(cast(ubyte) 0, cast(ubyte) 0); //variadic - ubyte[10] buf; - dig.put(buf); //buffer - } - - - /** - * Returns the finished SHA hash. This also calls $(LREF start) to - * reset the internal state. - */ - ubyte[digestSize/8] finish() @trusted pure nothrow @nogc - { - static if (blockSize == 512) - { - uint[8] data = void; - uint index, padLen; - - /* Save number of bits */ - ulong bits = nativeToBigEndian(count[0]); - - /* Pad out to 56 mod 64. */ - index = (cast(uint) count[0] >> 3) & (64 - 1); - padLen = (index < 56) ? (56 - index) : (120 - index); - put(padding[0 .. padLen]); - - /* Append length (before padding) */ - put((cast(ubyte*) &bits)[0 .. bits.sizeof]); - - /* Store state in digest */ - static foreach (i; 0 .. (digestSize == 160) ? 5 : 8) - data[i] = nativeToBigEndian(state[i]); - - /* Zeroize sensitive information. */ - start(); - return (cast(ubyte*) data.ptr)[0 .. digestSize/8]; - } - else static if (blockSize == 1024) - { - ulong[8] data = void; - uint index, padLen; - - /* Save number of bits */ - ulong[2] bits = [nativeToBigEndian(count[1]), nativeToBigEndian(count[0])]; - - /* Pad out to 112 mod 128. */ - index = (cast(uint) count[0] >> 3) & (128 - 1); - padLen = (index < 112) ? (112 - index) : (240 - index); - put(padding[0 .. padLen]); - - /* Append length (before padding) */ - put((cast(ubyte*) &bits)[0 .. bits.sizeof]); - - /* Store state in digest */ - static foreach (i; 0 .. 8) - data[i] = nativeToBigEndian(state[i]); - - /* Zeroize sensitive information. */ - start(); - return (cast(ubyte*) data.ptr)[0 .. digestSize/8]; - } - else - static assert(0); - } - /// - @safe unittest - { - //Simple example - SHA1 hash; - hash.start(); - hash.put(cast(ubyte) 0); - ubyte[20] result = hash.finish(); - } -} - -/// -@safe unittest -{ - //Simple example, hashing a string using sha1Of helper function - ubyte[20] hash = sha1Of("abc"); - //Let's get a hash string - assert(toHexString(hash) == "A9993E364706816ABA3E25717850C26C9CD0D89D"); - - //The same, but using SHA-224 - ubyte[28] hash224 = sha224Of("abc"); - assert(toHexString(hash224) == "23097D223405D8228642A477BDA255B32AADBCE4BDA0B3F7E36C9DA7"); -} - -/// -@safe unittest -{ - //Using the basic API - SHA1 hash; - hash.start(); - ubyte[1024] data; - //Initialize data here... - hash.put(data); - ubyte[20] result = hash.finish(); -} - -/// -@safe unittest -{ - //Let's use the template features: - //Note: When passing a SHA1 to a function, it must be passed by reference! - void doSomething(T)(ref T hash) - if (isDigest!T) - { - hash.put(cast(ubyte) 0); - } - SHA1 sha; - sha.start(); - doSomething(sha); - assert(toHexString(sha.finish()) == "5BA93C9DB0CFF93F52B521D7420E43F6EDA2784F"); -} - -alias SHA1 = SHA!(512, 160); /// SHA alias for SHA-1, hash is ubyte[20] -alias SHA224 = SHA!(512, 224); /// SHA alias for SHA-224, hash is ubyte[28] -alias SHA256 = SHA!(512, 256); /// SHA alias for SHA-256, hash is ubyte[32] -alias SHA384 = SHA!(1024, 384); /// SHA alias for SHA-384, hash is ubyte[48] -alias SHA512 = SHA!(1024, 512); /// SHA alias for SHA-512, hash is ubyte[64] -alias SHA512_224 = SHA!(1024, 224); /// SHA alias for SHA-512/224, hash is ubyte[28] -alias SHA512_256 = SHA!(1024, 256); /// SHA alias for SHA-512/256, hash is ubyte[32] - -@safe unittest -{ - assert(isDigest!SHA1); - assert(isDigest!SHA224); - assert(isDigest!SHA256); - assert(isDigest!SHA384); - assert(isDigest!SHA512); - assert(isDigest!SHA512_224); - assert(isDigest!SHA512_256); -} - -@system unittest -{ - import std.conv : hexString; - import std.range; - - ubyte[20] digest; - ubyte[28] digest224; - ubyte[32] digest256; - ubyte[48] digest384; - ubyte[64] digest512; - ubyte[28] digest512_224; - ubyte[32] digest512_256; - - SHA1 sha; - sha.put(cast(ubyte[])"abcdef"); - sha.start(); - sha.put(cast(ubyte[])""); - assert(sha.finish() == cast(ubyte[]) hexString!"da39a3ee5e6b4b0d3255bfef95601890afd80709"); - - SHA224 sha224; - sha224.put(cast(ubyte[])"abcdef"); - sha224.start(); - sha224.put(cast(ubyte[])""); - assert(sha224.finish() == cast(ubyte[]) hexString!"d14a028c2a3a2bc9476102bb288234c415a2b01f828ea62ac5b3e42f"); - - SHA256 sha256; - sha256.put(cast(ubyte[])"abcdef"); - sha256.start(); - sha256.put(cast(ubyte[])""); - assert(sha256.finish() == cast(ubyte[]) - hexString!"e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855"); - - SHA384 sha384; - sha384.put(cast(ubyte[])"abcdef"); - sha384.start(); - sha384.put(cast(ubyte[])""); - assert(sha384.finish() == cast(ubyte[]) hexString!("38b060a751ac96384cd9327eb1b1e36a21fdb71114be07434c" - ~"0cc7bf63f6e1da274edebfe76f65fbd51ad2f14898b95b")); - - SHA512 sha512; - sha512.put(cast(ubyte[])"abcdef"); - sha512.start(); - sha512.put(cast(ubyte[])""); - assert(sha512.finish() == cast(ubyte[]) - hexString!("cf83e1357eefb8bdf1542850d66d8007d620e4050b571" - ~"5dc83f4a921d36ce9ce47d0d13c5d85f2b0ff8318d2877eec2f63b931bd47417a81a538327af927da3e")); - - SHA512_224 sha512_224; - sha512_224.put(cast(ubyte[])"abcdef"); - sha512_224.start(); - sha512_224.put(cast(ubyte[])""); - assert(sha512_224.finish() == cast(ubyte[]) hexString!"6ed0dd02806fa89e25de060c19d3ac86cabb87d6a0ddd05c333b84f4"); - - SHA512_256 sha512_256; - sha512_256.put(cast(ubyte[])"abcdef"); - sha512_256.start(); - sha512_256.put(cast(ubyte[])""); - assert(sha512_256.finish() == cast(ubyte[]) - hexString!"c672b8d1ef56ed28ab87c3622c5114069bdd3ad7b8f9737498d0c01ecef0967a"); - - digest = sha1Of (""); - digest224 = sha224Of (""); - digest256 = sha256Of (""); - digest384 = sha384Of (""); - digest512 = sha512Of (""); - digest512_224 = sha512_224Of(""); - digest512_256 = sha512_256Of(""); - assert(digest == cast(ubyte[]) hexString!"da39a3ee5e6b4b0d3255bfef95601890afd80709"); - assert(digest224 == cast(ubyte[]) hexString!"d14a028c2a3a2bc9476102bb288234c415a2b01f828ea62ac5b3e42f"); - assert(digest256 == cast(ubyte[]) hexString!"e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855"); - assert(digest384 == cast(ubyte[]) hexString!("38b060a751ac96384cd9327eb1b1e36a21fdb71114be07434c" - ~"0cc7bf63f6e1da274edebfe76f65fbd51ad2f14898b95b")); - assert(digest512 == cast(ubyte[]) hexString!("cf83e1357eefb8bdf1542850d66d8007d620e4050b5715dc83" - ~"f4a921d36ce9ce47d0d13c5d85f2b0ff8318d2877eec2f63b931bd47417a81a538327af927da3e")); - assert(digest512_224 == cast(ubyte[]) hexString!"6ed0dd02806fa89e25de060c19d3ac86cabb87d6a0ddd05c333b84f4"); - assert(digest512_256 == cast(ubyte[]) hexString!"c672b8d1ef56ed28ab87c3622c5114069bdd3ad7b8f9737498d0c01ecef0967a"); - - digest = sha1Of ("a"); - digest224 = sha224Of ("a"); - digest256 = sha256Of ("a"); - digest384 = sha384Of ("a"); - digest512 = sha512Of ("a"); - digest512_224 = sha512_224Of("a"); - digest512_256 = sha512_256Of("a"); - assert(digest == cast(ubyte[]) hexString!"86f7e437faa5a7fce15d1ddcb9eaeaea377667b8"); - assert(digest224 == cast(ubyte[]) hexString!"abd37534c7d9a2efb9465de931cd7055ffdb8879563ae98078d6d6d5"); - assert(digest256 == cast(ubyte[]) hexString!"ca978112ca1bbdcafac231b39a23dc4da786eff8147c4e72b9807785afee48bb"); - assert(digest384 == cast(ubyte[]) hexString!("54a59b9f22b0b80880d8427e548b7c23abd873486e1f035dce9" - ~"cd697e85175033caa88e6d57bc35efae0b5afd3145f31")); - assert(digest512 == cast(ubyte[]) hexString!("1f40fc92da241694750979ee6cf582f2d5d7d28e18335de05ab" - ~"c54d0560e0f5302860c652bf08d560252aa5e74210546f369fbbbce8c12cfc7957b2652fe9a75")); - assert(digest512_224 == cast(ubyte[]) hexString!"d5cdb9ccc769a5121d4175f2bfdd13d6310e0d3d361ea75d82108327"); - assert(digest512_256 == cast(ubyte[]) hexString!"455e518824bc0601f9fb858ff5c37d417d67c2f8e0df2babe4808858aea830f8"); - - digest = sha1Of ("abc"); - digest224 = sha224Of ("abc"); - digest256 = sha256Of ("abc"); - digest384 = sha384Of ("abc"); - digest512 = sha512Of ("abc"); - digest512_224 = sha512_224Of("abc"); - digest512_256 = sha512_256Of("abc"); - assert(digest == cast(ubyte[]) hexString!"a9993e364706816aba3e25717850c26c9cd0d89d"); - assert(digest224 == cast(ubyte[]) hexString!"23097d223405d8228642a477bda255b32aadbce4bda0b3f7e36c9da7"); - assert(digest256 == cast(ubyte[]) hexString!"ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad"); - assert(digest384 == cast(ubyte[]) hexString!("cb00753f45a35e8bb5a03d699ac65007272c32ab0eded1631a" - ~"8b605a43ff5bed8086072ba1e7cc2358baeca134c825a7")); - assert(digest512 == cast(ubyte[]) hexString!("ddaf35a193617abacc417349ae20413112e6fa4e89a97ea20a9" - ~"eeee64b55d39a2192992a274fc1a836ba3c23a3feebbd454d4423643ce80e2a9ac94fa54ca49f")); - assert(digest512_224 == cast(ubyte[]) hexString!"4634270f707b6a54daae7530460842e20e37ed265ceee9a43e8924aa"); - assert(digest512_256 == cast(ubyte[]) hexString!"53048e2681941ef99b2e29b76b4c7dabe4c2d0c634fc6d46e0e2f13107e7af23"); - - digest = sha1Of ("abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq"); - digest224 = sha224Of ("abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq"); - digest256 = sha256Of ("abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq"); - digest384 = sha384Of ("abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq"); - digest512 = sha512Of ("abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq"); - digest512_224 = sha512_224Of("abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq"); - digest512_256 = sha512_256Of("abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq"); - assert(digest == cast(ubyte[]) hexString!"84983e441c3bd26ebaae4aa1f95129e5e54670f1"); - assert(digest224 == cast(ubyte[]) hexString!"75388b16512776cc5dba5da1fd890150b0c6455cb4f58b1952522525"); - assert(digest256 == cast(ubyte[]) hexString!"248d6a61d20638b8e5c026930c3e6039a33ce45964ff2167f6ecedd419db06c1"); - assert(digest384 == cast(ubyte[]) hexString!("3391fdddfc8dc7393707a65b1b4709397cf8b1d162af05abfe" - ~"8f450de5f36bc6b0455a8520bc4e6f5fe95b1fe3c8452b")); - assert(digest512 == cast(ubyte[]) hexString!("204a8fc6dda82f0a0ced7beb8e08a41657c16ef468b228a827" - ~"9be331a703c33596fd15c13b1b07f9aa1d3bea57789ca031ad85c7a71dd70354ec631238ca3445")); - assert(digest512_224 == cast(ubyte[]) hexString!"e5302d6d54bb242275d1e7622d68df6eb02dedd13f564c13dbda2174"); - assert(digest512_256 == cast(ubyte[]) hexString!"bde8e1f9f19bb9fd3406c90ec6bc47bd36d8ada9f11880dbc8a22a7078b6a461"); - - digest = sha1Of ("message digest"); - digest224 = sha224Of ("message digest"); - digest256 = sha256Of ("message digest"); - digest384 = sha384Of ("message digest"); - digest512 = sha512Of ("message digest"); - digest512_224 = sha512_224Of("message digest"); - digest512_256 = sha512_256Of("message digest"); - assert(digest == cast(ubyte[]) hexString!"c12252ceda8be8994d5fa0290a47231c1d16aae3"); - assert(digest224 == cast(ubyte[]) hexString!"2cb21c83ae2f004de7e81c3c7019cbcb65b71ab656b22d6d0c39b8eb"); - assert(digest256 == cast(ubyte[]) hexString!"f7846f55cf23e14eebeab5b4e1550cad5b509e3348fbc4efa3a1413d393cb650"); - assert(digest384 == cast(ubyte[]) hexString!("473ed35167ec1f5d8e550368a3db39be54639f828868e9454c" - ~"239fc8b52e3c61dbd0d8b4de1390c256dcbb5d5fd99cd5")); - assert(digest512 == cast(ubyte[]) hexString!("107dbf389d9e9f71a3a95f6c055b9251bc5268c2be16d6c134" - ~"92ea45b0199f3309e16455ab1e96118e8a905d5597b72038ddb372a89826046de66687bb420e7c")); - assert(digest512_224 == cast(ubyte[]) hexString!"ad1a4db188fe57064f4f24609d2a83cd0afb9b398eb2fcaeaae2c564"); - assert(digest512_256 == cast(ubyte[]) hexString!"0cf471fd17ed69d990daf3433c89b16d63dec1bb9cb42a6094604ee5d7b4e9fb"); - - digest = sha1Of ("abcdefghijklmnopqrstuvwxyz"); - digest224 = sha224Of ("abcdefghijklmnopqrstuvwxyz"); - digest256 = sha256Of ("abcdefghijklmnopqrstuvwxyz"); - digest384 = sha384Of ("abcdefghijklmnopqrstuvwxyz"); - digest512 = sha512Of ("abcdefghijklmnopqrstuvwxyz"); - digest512_224 = sha512_224Of("abcdefghijklmnopqrstuvwxyz"); - digest512_256 = sha512_256Of("abcdefghijklmnopqrstuvwxyz"); - assert(digest == cast(ubyte[]) hexString!"32d10c7b8cf96570ca04ce37f2a19d84240d3a89"); - assert(digest224 == cast(ubyte[]) hexString!"45a5f72c39c5cff2522eb3429799e49e5f44b356ef926bcf390dccc2"); - assert(digest256 == cast(ubyte[]) hexString!"71c480df93d6ae2f1efad1447c66c9525e316218cf51fc8d9ed832f2daf18b73"); - assert(digest384 == cast(ubyte[]) hexString!("feb67349df3db6f5924815d6c3dc133f091809213731fe5c7b5" - ~"f4999e463479ff2877f5f2936fa63bb43784b12f3ebb4")); - assert(digest512 == cast(ubyte[]) hexString!("4dbff86cc2ca1bae1e16468a05cb9881c97f1753bce3619034" - ~"898faa1aabe429955a1bf8ec483d7421fe3c1646613a59ed5441fb0f321389f77f48a879c7b1f1")); - assert(digest512_224 == cast(ubyte[]) hexString!"ff83148aa07ec30655c1b40aff86141c0215fe2a54f767d3f38743d8"); - assert(digest512_256 == cast(ubyte[]) hexString!"fc3189443f9c268f626aea08a756abe7b726b05f701cb08222312ccfd6710a26"); - - digest = sha1Of ("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"); - digest224 = sha224Of ("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"); - digest256 = sha256Of ("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"); - digest384 = sha384Of ("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"); - digest512 = sha512Of ("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"); - digest512_224 = sha512_224Of("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"); - digest512_256 = sha512_256Of("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"); - assert(digest == cast(ubyte[]) hexString!"761c457bf73b14d27e9e9265c46f4b4dda11f940"); - assert(digest224 == cast(ubyte[]) hexString!"bff72b4fcb7d75e5632900ac5f90d219e05e97a7bde72e740db393d9"); - assert(digest256 == cast(ubyte[]) hexString!"db4bfcbd4da0cd85a60c3c37d3fbd8805c77f15fc6b1fdfe614ee0a7c8fdb4c0"); - assert(digest384 == cast(ubyte[]) hexString!("1761336e3f7cbfe51deb137f026f89e01a448e3b1fafa64039" - ~"c1464ee8732f11a5341a6f41e0c202294736ed64db1a84")); - assert(digest512 == cast(ubyte[]) hexString!("1e07be23c26a86ea37ea810c8ec7809352515a970e9253c26f" - ~"536cfc7a9996c45c8370583e0a78fa4a90041d71a4ceab7423f19c71b9d5a3e01249f0bebd5894")); - assert(digest512_224 == cast(ubyte[]) hexString!"a8b4b9174b99ffc67d6f49be9981587b96441051e16e6dd036b140d3"); - assert(digest512_256 == cast(ubyte[]) hexString!"cdf1cc0effe26ecc0c13758f7b4a48e000615df241284185c39eb05d355bb9c8"); - - digest = sha1Of ("1234567890123456789012345678901234567890"~ - "1234567890123456789012345678901234567890"); - digest224 = sha224Of ("1234567890123456789012345678901234567890"~ - "1234567890123456789012345678901234567890"); - digest256 = sha256Of ("1234567890123456789012345678901234567890"~ - "1234567890123456789012345678901234567890"); - digest384 = sha384Of ("1234567890123456789012345678901234567890"~ - "1234567890123456789012345678901234567890"); - digest512 = sha512Of ("1234567890123456789012345678901234567890"~ - "1234567890123456789012345678901234567890"); - digest512_224 = sha512_224Of("1234567890123456789012345678901234567890"~ - "1234567890123456789012345678901234567890"); - digest512_256 = sha512_256Of("1234567890123456789012345678901234567890"~ - "1234567890123456789012345678901234567890"); - assert(digest == cast(ubyte[]) hexString!"50abf5706a150990a08b2c5ea40fa0e585554732"); - assert(digest224 == cast(ubyte[]) hexString!"b50aecbe4e9bb0b57bc5f3ae760a8e01db24f203fb3cdcd13148046e"); - assert(digest256 == cast(ubyte[]) hexString!"f371bc4a311f2b009eef952dd83ca80e2b60026c8e935592d0f9c308453c813e"); - assert(digest384 == cast(ubyte[]) hexString!("b12932b0627d1c060942f5447764155655bd4da0c9afa6dd9b" - ~"9ef53129af1b8fb0195996d2de9ca0df9d821ffee67026")); - assert(digest512 == cast(ubyte[]) hexString!("72ec1ef1124a45b047e8b7c75a932195135bb61de24ec0d191" - ~"4042246e0aec3a2354e093d76f3048b456764346900cb130d2a4fd5dd16abb5e30bcb850dee843")); - assert(digest512_224 == cast(ubyte[]) hexString!"ae988faaa47e401a45f704d1272d99702458fea2ddc6582827556dd2"); - assert(digest512_256 == cast(ubyte[]) hexString!"2c9fdbc0c90bdd87612ee8455474f9044850241dc105b1e8b94b8ddf5fac9148"); - - ubyte[] onemilliona = new ubyte[1000000]; - onemilliona[] = 'a'; - digest = sha1Of(onemilliona); - digest224 = sha224Of(onemilliona); - digest256 = sha256Of(onemilliona); - digest384 = sha384Of(onemilliona); - digest512 = sha512Of(onemilliona); - digest512_224 = sha512_224Of(onemilliona); - digest512_256 = sha512_256Of(onemilliona); - assert(digest == cast(ubyte[]) hexString!"34aa973cd4c4daa4f61eeb2bdbad27316534016f"); - assert(digest224 == cast(ubyte[]) hexString!"20794655980c91d8bbb4c1ea97618a4bf03f42581948b2ee4ee7ad67"); - assert(digest256 == cast(ubyte[]) hexString!"cdc76e5c9914fb9281a1c7e284d73e67f1809a48a497200e046d39ccc7112cd0"); - assert(digest384 == cast(ubyte[]) hexString!("9d0e1809716474cb086e834e310a4a1ced149e9c00f2485279" - ~"72cec5704c2a5b07b8b3dc38ecc4ebae97ddd87f3d8985")); - assert(digest512 == cast(ubyte[]) hexString!("e718483d0ce769644e2e42c7bc15b4638e1f98b13b20442856" - ~"32a803afa973ebde0ff244877ea60a4cb0432ce577c31beb009c5c2c49aa2e4eadb217ad8cc09b")); - assert(digest512_224 == cast(ubyte[]) hexString!"37ab331d76f0d36de422bd0edeb22a28accd487b7a8453ae965dd287"); - assert(digest512_256 == cast(ubyte[]) hexString!"9a59a052930187a97038cae692f30708aa6491923ef5194394dc68d56c74fb21"); - - auto oneMillionRange = repeat!ubyte(cast(ubyte)'a', 1000000); - digest = sha1Of(oneMillionRange); - digest224 = sha224Of(oneMillionRange); - digest256 = sha256Of(oneMillionRange); - digest384 = sha384Of(oneMillionRange); - digest512 = sha512Of(oneMillionRange); - digest512_224 = sha512_224Of(oneMillionRange); - digest512_256 = sha512_256Of(oneMillionRange); - assert(digest == cast(ubyte[]) hexString!"34aa973cd4c4daa4f61eeb2bdbad27316534016f"); - assert(digest224 == cast(ubyte[]) hexString!"20794655980c91d8bbb4c1ea97618a4bf03f42581948b2ee4ee7ad67"); - assert(digest256 == cast(ubyte[]) hexString!"cdc76e5c9914fb9281a1c7e284d73e67f1809a48a497200e046d39ccc7112cd0"); - assert(digest384 == cast(ubyte[]) hexString!("9d0e1809716474cb086e834e310a4a1ced149e9c00f2485279" - ~"72cec5704c2a5b07b8b3dc38ecc4ebae97ddd87f3d8985")); - assert(digest512 == cast(ubyte[]) hexString!("e718483d0ce769644e2e42c7bc15b4638e1f98b13b20442856" - ~"32a803afa973ebde0ff244877ea60a4cb0432ce577c31beb009c5c2c49aa2e4eadb217ad8cc09b")); - assert(digest512_224 == cast(ubyte[]) hexString!"37ab331d76f0d36de422bd0edeb22a28accd487b7a8453ae965dd287"); - assert(digest512_256 == cast(ubyte[]) hexString!"9a59a052930187a97038cae692f30708aa6491923ef5194394dc68d56c74fb21"); - - enum ubyte[20] input = cast(ubyte[20]) hexString!"a9993e364706816aba3e25717850c26c9cd0d89d"; - assert(toHexString(input) - == "A9993E364706816ABA3E25717850C26C9CD0D89D"); -} - -/** - * These are convenience aliases for $(REF digest, std,digest) using the - * SHA implementation. - */ -//simple alias doesn't work here, hope this gets inlined... -auto sha1Of(T...)(T data) -{ - return digest!(SHA1, T)(data); -} -///ditto -auto sha224Of(T...)(T data) -{ - return digest!(SHA224, T)(data); -} -///ditto -auto sha256Of(T...)(T data) -{ - return digest!(SHA256, T)(data); -} -///ditto -auto sha384Of(T...)(T data) -{ - return digest!(SHA384, T)(data); -} -///ditto -auto sha512Of(T...)(T data) -{ - return digest!(SHA512, T)(data); -} -///ditto -auto sha512_224Of(T...)(T data) -{ - return digest!(SHA512_224, T)(data); -} -///ditto -auto sha512_256Of(T...)(T data) -{ - return digest!(SHA512_256, T)(data); -} - -/// -@safe unittest -{ - ubyte[20] hash = sha1Of("abc"); - assert(hash == digest!SHA1("abc")); - - ubyte[28] hash224 = sha224Of("abc"); - assert(hash224 == digest!SHA224("abc")); - - ubyte[32] hash256 = sha256Of("abc"); - assert(hash256 == digest!SHA256("abc")); - - ubyte[48] hash384 = sha384Of("abc"); - assert(hash384 == digest!SHA384("abc")); - - ubyte[64] hash512 = sha512Of("abc"); - assert(hash512 == digest!SHA512("abc")); - - ubyte[28] hash512_224 = sha512_224Of("abc"); - assert(hash512_224 == digest!SHA512_224("abc")); - - ubyte[32] hash512_256 = sha512_256Of("abc"); - assert(hash512_256 == digest!SHA512_256("abc")); -} - -@safe unittest -{ - string a = "Mary has ", b = "a little lamb"; - int[] c = [ 1, 2, 3, 4, 5 ]; - auto d = toHexString(sha1Of(a, b, c)); - version (LittleEndian) - assert(d[] == "CDBB611D00AC2387B642D3D7BDF4C3B342237110", d.dup); - else - assert(d[] == "A0F1196C7A379C09390476D9CA4AA11B71FD11C8", d.dup); -} - -/** - * OOP API SHA1 and SHA2 implementations. - * See `std.digest` for differences between template and OOP API. - * - * This is an alias for $(D $(REF WrapperDigest, std,digest)!SHA1), see - * there for more information. - */ -alias SHA1Digest = WrapperDigest!SHA1; -alias SHA224Digest = WrapperDigest!SHA224; ///ditto -alias SHA256Digest = WrapperDigest!SHA256; ///ditto -alias SHA384Digest = WrapperDigest!SHA384; ///ditto -alias SHA512Digest = WrapperDigest!SHA512; ///ditto -alias SHA512_224Digest = WrapperDigest!SHA512_224; ///ditto -alias SHA512_256Digest = WrapperDigest!SHA512_256; ///ditto - -/// -@safe unittest -{ - //Simple example, hashing a string using Digest.digest helper function - auto sha = new SHA1Digest(); - ubyte[] hash = sha.digest("abc"); - //Let's get a hash string - assert(toHexString(hash) == "A9993E364706816ABA3E25717850C26C9CD0D89D"); - - //The same, but using SHA-224 - auto sha224 = new SHA224Digest(); - ubyte[] hash224 = sha224.digest("abc"); - //Let's get a hash string - assert(toHexString(hash224) == "23097D223405D8228642A477BDA255B32AADBCE4BDA0B3F7E36C9DA7"); -} - -/// -@system unittest -{ - //Let's use the OOP features: - void test(Digest dig) - { - dig.put(cast(ubyte) 0); - } - auto sha = new SHA1Digest(); - test(sha); - - //Let's use a custom buffer: - ubyte[20] buf; - ubyte[] result = sha.finish(buf[]); - assert(toHexString(result) == "5BA93C9DB0CFF93F52B521D7420E43F6EDA2784F"); -} - -@system unittest -{ - import std.conv : hexString; - import std.exception; - auto sha = new SHA1Digest(); - - sha.put(cast(ubyte[])"abcdef"); - sha.reset(); - sha.put(cast(ubyte[])""); - assert(sha.finish() == cast(ubyte[]) hexString!"da39a3ee5e6b4b0d3255bfef95601890afd80709"); - - sha.put(cast(ubyte[])"abcdefghijklmnopqrstuvwxyz"); - ubyte[22] result; - auto result2 = sha.finish(result[]); - assert(result[0 .. 20] == result2 && result2 == cast(ubyte[]) hexString!"32d10c7b8cf96570ca04ce37f2a19d84240d3a89"); - - debug - assertThrown!Error(sha.finish(result[0 .. 15])); - - assert(sha.length == 20); - - assert(sha.digest("") == cast(ubyte[]) hexString!"da39a3ee5e6b4b0d3255bfef95601890afd80709"); - - assert(sha.digest("a") == cast(ubyte[]) hexString!"86f7e437faa5a7fce15d1ddcb9eaeaea377667b8"); - - assert(sha.digest("abc") == cast(ubyte[]) hexString!"a9993e364706816aba3e25717850c26c9cd0d89d"); - - assert(sha.digest("abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq") - == cast(ubyte[]) hexString!"84983e441c3bd26ebaae4aa1f95129e5e54670f1"); - - assert(sha.digest("message digest") == cast(ubyte[]) hexString!"c12252ceda8be8994d5fa0290a47231c1d16aae3"); - - assert(sha.digest("abcdefghijklmnopqrstuvwxyz") - == cast(ubyte[]) hexString!"32d10c7b8cf96570ca04ce37f2a19d84240d3a89"); - - assert(sha.digest("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789") - == cast(ubyte[]) hexString!"761c457bf73b14d27e9e9265c46f4b4dda11f940"); - - assert(sha.digest("1234567890123456789012345678901234567890", - "1234567890123456789012345678901234567890") - == cast(ubyte[]) hexString!"50abf5706a150990a08b2c5ea40fa0e585554732"); - - ubyte[] onemilliona = new ubyte[1000000]; - onemilliona[] = 'a'; - assert(sha.digest(onemilliona) == cast(ubyte[]) hexString!"34aa973cd4c4daa4f61eeb2bdbad27316534016f"); -} diff --git a/phobos/std/encoding.d b/phobos/std/encoding.d deleted file mode 100644 index 9334f31..0000000 --- a/phobos/std/encoding.d +++ /dev/null @@ -1,3854 +0,0 @@ -// Written in the D programming language. - -/** -Classes and functions for handling and transcoding between various encodings. - -For cases where the encoding is known at compile-time, functions are provided -for arbitrary encoding and decoding of characters, arbitrary transcoding -between strings of different type, as well as validation and sanitization. - -Encodings currently supported are UTF-8, UTF-16, UTF-32, ASCII, ISO-8859-1 -(also known as LATIN-1), ISO-8859-2 (LATIN-2), WINDOWS-1250, WINDOWS-1251 -and WINDOWS-1252. - -$(SCRIPT inhibitQuickIndex = 1;) -$(DIVC quickindex, -$(BOOKTABLE, -$(TR $(TH Category) $(TH Functions)) -$(TR $(TD Decode) $(TD - $(LREF codePoints) - $(LREF decode) - $(LREF decodeReverse) - $(LREF safeDecode) -)) -$(TR $(TD Conversion) $(TD - $(LREF codeUnits) - $(LREF sanitize) - $(LREF transcode) -)) -$(TR $(TD Classification) $(TD - $(LREF canEncode) - $(LREF isValid) - $(LREF isValidCodePoint) - $(LREF isValidCodeUnit) -)) -$(TR $(TD BOM) $(TD - $(LREF BOM) - $(LREF BOMSeq) - $(LREF getBOM) - $(LREF utfBOM) -)) -$(TR $(TD Length & Index) $(TD - $(LREF firstSequence) - $(LREF encodedLength) - $(LREF index) - $(LREF lastSequence) - $(LREF validLength) -)) -$(TR $(TD Encoding schemes) $(TD - $(LREF encodingName) - $(LREF EncodingScheme) - $(LREF EncodingSchemeASCII) - $(LREF EncodingSchemeLatin1) - $(LREF EncodingSchemeLatin2) - $(LREF EncodingSchemeUtf16Native) - $(LREF EncodingSchemeUtf32Native) - $(LREF EncodingSchemeUtf8) - $(LREF EncodingSchemeWindows1250) - $(LREF EncodingSchemeWindows1251) - $(LREF EncodingSchemeWindows1252) -)) -$(TR $(TD Representation) $(TD - $(LREF AsciiChar) - $(LREF AsciiString) - $(LREF Latin1Char) - $(LREF Latin1String) - $(LREF Latin2Char) - $(LREF Latin2String) - $(LREF Windows1250Char) - $(LREF Windows1250String) - $(LREF Windows1251Char) - $(LREF Windows1251String) - $(LREF Windows1252Char) - $(LREF Windows1252String) -)) -$(TR $(TD Exceptions) $(TD - $(LREF INVALID_SEQUENCE) - $(LREF EncodingException) -)) -)) - -For cases where the encoding is not known at compile-time, but is -known at run-time, the abstract class $(LREF EncodingScheme) -and its subclasses is provided. To construct a run-time encoder/decoder, -one does e.g. - ----------------------------------------------------- -auto e = EncodingScheme.create("utf-8"); ----------------------------------------------------- - -This library supplies $(LREF EncodingScheme) subclasses for ASCII, -ISO-8859-1 (also known as LATIN-1), ISO-8859-2 (LATIN-2), WINDOWS-1250, -WINDOWS-1251, WINDOWS-1252, UTF-8, and (on little-endian architectures) -UTF-16LE and UTF-32LE; or (on big-endian architectures) UTF-16BE and UTF-32BE. - -This library provides a mechanism whereby other modules may add $(LREF -EncodingScheme) subclasses for any other encoding. - -Copyright: Copyright Janice Caron 2008 - 2009. -License: $(HTTP www.boost.org/LICENSE_1_0.txt, Boost License 1.0). -Authors: Janice Caron -Source: $(PHOBOSSRC std/encoding.d) -*/ -/* - Copyright Janice Caron 2008 - 2009. -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.encoding; - -import std.range.primitives; -import std.traits; -import std.typecons; - -@system unittest -{ - static ubyte[][] validStrings = - [ - // Plain ASCII - cast(ubyte[])"hello", - - // First possible sequence of a certain length - [ 0x00 ], // U+00000000 one byte - [ 0xC2, 0x80 ], // U+00000080 two bytes - [ 0xE0, 0xA0, 0x80 ], // U+00000800 three bytes - [ 0xF0, 0x90, 0x80, 0x80 ], // U+00010000 three bytes - - // Last possible sequence of a certain length - [ 0x7F ], // U+0000007F one byte - [ 0xDF, 0xBF ], // U+000007FF two bytes - [ 0xEF, 0xBF, 0xBF ], // U+0000FFFF three bytes - - // Other boundary conditions - [ 0xED, 0x9F, 0xBF ], - // U+0000D7FF Last character before surrogates - [ 0xEE, 0x80, 0x80 ], - // U+0000E000 First character after surrogates - [ 0xEF, 0xBF, 0xBD ], - // U+0000FFFD Unicode replacement character - [ 0xF4, 0x8F, 0xBF, 0xBF ], - // U+0010FFFF Very last character - - // Non-character code points - /* NOTE: These are legal in UTF, and may be converted from - one UTF to another, however they do not represent Unicode - characters. These code points have been reserved by - Unicode as non-character code points. They are permissible - for data exchange within an application, but they are are - not permitted to be used as characters. Since this module - deals with UTF, and not with Unicode per se, we choose to - accept them here. */ - [ 0xDF, 0xBE ], // U+0000FFFE - [ 0xDF, 0xBF ], // U+0000FFFF - ]; - - static ubyte[][] invalidStrings = - [ - // First possible sequence of a certain length, but greater - // than U+10FFFF - [ 0xF8, 0x88, 0x80, 0x80, 0x80 ], // U+00200000 five bytes - [ 0xFC, 0x84, 0x80, 0x80, 0x80, 0x80 ], // U+04000000 six bytes - - // Last possible sequence of a certain length, but greater than U+10FFFF - [ 0xF7, 0xBF, 0xBF, 0xBF ], // U+001FFFFF four bytes - [ 0xFB, 0xBF, 0xBF, 0xBF, 0xBF ], // U+03FFFFFF five bytes - [ 0xFD, 0xBF, 0xBF, 0xBF, 0xBF, 0xBF ], // U+7FFFFFFF six bytes - - // Other boundary conditions - [ 0xF4, 0x90, 0x80, 0x80 ], // U+00110000 - // First code - // point after - // last character - - // Unexpected continuation bytes - [ 0x80 ], - [ 0xBF ], - [ 0x20, 0x80, 0x20 ], - [ 0x20, 0xBF, 0x20 ], - [ 0x80, 0x9F, 0xA0 ], - - // Lonely start bytes - [ 0xC0 ], - [ 0xCF ], - [ 0x20, 0xC0, 0x20 ], - [ 0x20, 0xCF, 0x20 ], - [ 0xD0 ], - [ 0xDF ], - [ 0x20, 0xD0, 0x20 ], - [ 0x20, 0xDF, 0x20 ], - [ 0xE0 ], - [ 0xEF ], - [ 0x20, 0xE0, 0x20 ], - [ 0x20, 0xEF, 0x20 ], - [ 0xF0 ], - [ 0xF1 ], - [ 0xF2 ], - [ 0xF3 ], - [ 0xF4 ], - [ 0xF5 ], // If this were legal it would start a character > U+10FFFF - [ 0xF6 ], // If this were legal it would start a character > U+10FFFF - [ 0xF7 ], // If this were legal it would start a character > U+10FFFF - - [ 0xEF, 0xBF ], // Three byte sequence with third byte missing - [ 0xF7, 0xBF, 0xBF ], // Four byte sequence with fourth byte missing - [ 0xEF, 0xBF, 0xF7, 0xBF, 0xBF ], // Concatenation of the above - - // Impossible bytes - [ 0xF8 ], - [ 0xF9 ], - [ 0xFA ], - [ 0xFB ], - [ 0xFC ], - [ 0xFD ], - [ 0xFE ], - [ 0xFF ], - [ 0x20, 0xF8, 0x20 ], - [ 0x20, 0xF9, 0x20 ], - [ 0x20, 0xFA, 0x20 ], - [ 0x20, 0xFB, 0x20 ], - [ 0x20, 0xFC, 0x20 ], - [ 0x20, 0xFD, 0x20 ], - [ 0x20, 0xFE, 0x20 ], - [ 0x20, 0xFF, 0x20 ], - - // Overlong sequences, all representing U+002F - /* With a safe UTF-8 decoder, all of the following five overlong - representations of the ASCII character slash ("/") should be - rejected like a malformed UTF-8 sequence */ - [ 0xC0, 0xAF ], - [ 0xE0, 0x80, 0xAF ], - [ 0xF0, 0x80, 0x80, 0xAF ], - [ 0xF8, 0x80, 0x80, 0x80, 0xAF ], - [ 0xFC, 0x80, 0x80, 0x80, 0x80, 0xAF ], - - // Maximum overlong sequences - /* Below you see the highest Unicode value that is still resulting in - an overlong sequence if represented with the given number of bytes. - This is a boundary test for safe UTF-8 decoders. All five - characters should be rejected like malformed UTF-8 sequences. */ - [ 0xC1, 0xBF ], // U+0000007F - [ 0xE0, 0x9F, 0xBF ], // U+000007FF - [ 0xF0, 0x8F, 0xBF, 0xBF ], // U+0000FFFF - [ 0xF8, 0x87, 0xBF, 0xBF, 0xBF ], // U+001FFFFF - [ 0xFC, 0x83, 0xBF, 0xBF, 0xBF, 0xBF ], // U+03FFFFFF - - // Overlong representation of the NUL character - /* The following five sequences should also be rejected like malformed - UTF-8 sequences and should not be treated like the ASCII NUL - character. */ - [ 0xC0, 0x80 ], - [ 0xE0, 0x80, 0x80 ], - [ 0xF0, 0x80, 0x80, 0x80 ], - [ 0xF8, 0x80, 0x80, 0x80, 0x80 ], - [ 0xFC, 0x80, 0x80, 0x80, 0x80, 0x80 ], - - // Illegal code positions - /* The following UTF-8 sequences should be rejected like malformed - sequences, because they never represent valid ISO 10646 characters - and a UTF-8 decoder that accepts them might introduce security - problems comparable to overlong UTF-8 sequences. */ - [ 0xED, 0xA0, 0x80 ], // U+D800 - [ 0xED, 0xAD, 0xBF ], // U+DB7F - [ 0xED, 0xAE, 0x80 ], // U+DB80 - [ 0xED, 0xAF, 0xBF ], // U+DBFF - [ 0xED, 0xB0, 0x80 ], // U+DC00 - [ 0xED, 0xBE, 0x80 ], // U+DF80 - [ 0xED, 0xBF, 0xBF ], // U+DFFF - ]; - - static string[] sanitizedStrings = - [ - "\uFFFD","\uFFFD", - "\uFFFD","\uFFFD","\uFFFD","\uFFFD","\uFFFD","\uFFFD"," \uFFFD ", - " \uFFFD ","\uFFFD\uFFFD\uFFFD","\uFFFD","\uFFFD"," \uFFFD "," \uFFFD ", - "\uFFFD","\uFFFD"," \uFFFD "," \uFFFD ","\uFFFD","\uFFFD"," \uFFFD ", - " \uFFFD ","\uFFFD","\uFFFD","\uFFFD","\uFFFD","\uFFFD","\uFFFD", - "\uFFFD","\uFFFD","\uFFFD","\uFFFD","\uFFFD\uFFFD","\uFFFD","\uFFFD", - "\uFFFD","\uFFFD","\uFFFD","\uFFFD","\uFFFD","\uFFFD"," \uFFFD ", - " \uFFFD "," \uFFFD "," \uFFFD "," \uFFFD "," \uFFFD "," \uFFFD ", - " \uFFFD ","\uFFFD","\uFFFD","\uFFFD","\uFFFD","\uFFFD","\uFFFD", - "\uFFFD","\uFFFD","\uFFFD","\uFFFD","\uFFFD","\uFFFD","\uFFFD","\uFFFD", - "\uFFFD","\uFFFD","\uFFFD","\uFFFD","\uFFFD","\uFFFD","\uFFFD","\uFFFD", - ]; - - // HELPER FUNCTIONS - // we can probably do this better... - static char toHexDigit(int n) - { - return "0123456789ABCDEF"[n & 0xF]; - } - - static string makeReadable(string s) - { - string r = "\""; - foreach (char c;s) - { - if (c >= 0x20 && c < 0x80) - { - r ~= c; - } - else - { - r ~= "\\x"; - r ~= toHexDigit(c >> 4); - r ~= toHexDigit(c); - } - } - r ~= "\""; - return r; - } - - void transcodeReverse(Src,Dst)(immutable(Src)[] s, out immutable(Dst)[] r) - { - static if (is(Src == Dst)) - { - return s; - } - else static if (is(Src == AsciiChar)) - { - transcodeReverse!(char,Dst)(cast(string) s,r); - } - else - { - foreach_reverse (d;codePoints(s)) - { - foreach_reverse (c;codeUnits!(Dst)(d)) - { - r = c ~ r; - } - } - } - } - - // Make sure everything that should be valid, is - foreach (a;validStrings) - { - string s = cast(string) a; - assert(isValid(s),"Failed to validate: "~makeReadable(s)); - } - - // Make sure everything that shouldn't be valid, isn't - foreach (a;invalidStrings) - { - string s = cast(string) a; - assert(!isValid(s),"Incorrectly validated: "~makeReadable(s)); - } - - // Make sure we can sanitize everything bad - assert(invalidStrings.length == sanitizedStrings.length); - for (int i=0; i m_charMapEnd && c < 0x100)) return true; - if (c >= 0xFFFD) return false; - - auto idx = 0; - while (idx < bstMap.length) - { - if (bstMap[idx][0] == c) return true; - idx = bstMap[idx][0] > c ? 2 * idx + 1 : 2 * idx + 2; // next BST index - } - - return false; - } - - bool isValidCodeUnit(E c) @safe pure @nogc nothrow - { - if (c < m_charMapStart || c > m_charMapEnd) return true; - return charMap[c-m_charMapStart] != 0xFFFD; - } - - size_t encodedLength(dchar c) @safe pure @nogc nothrow - in - { - assert(canEncode(c)); - } - do - { - return 1; - } - - void encodeViaWrite()(dchar c) - { - if (c < m_charMapStart || (c > m_charMapEnd && c < 0x100)) {} - else if (c >= 0xFFFD) { c = '?'; } - else - { - auto idx = 0; - while (idx < bstMap.length) - { - if (bstMap[idx][0] == c) - { - write(cast(E) bstMap[idx][1]); - return; - } - idx = bstMap[idx][0] > c ? 2 * idx + 1 : 2 * idx + 2; // next BST index - } - c = '?'; - } - write(cast(E) c); - } - - void skipViaRead()() - { - read(); - } - - dchar decodeViaRead()() - { - E c = read(); - return (c >= m_charMapStart && c <= m_charMapEnd) ? charMap[c-m_charMapStart] : c; - } - - dchar safeDecodeViaRead()() - { - immutable E c = read(); - immutable d = (c >= m_charMapStart && c <= m_charMapEnd) ? charMap[c-m_charMapStart] : c; - return d == 0xFFFD ? INVALID_SEQUENCE : d; - } - - dchar decodeReverseViaRead()() - { - E c = read(); - return (c >= m_charMapStart && c <= m_charMapEnd) ? charMap[c-m_charMapStart] : c; - } - - @property EString replacementSequence() @safe pure @nogc nothrow - { - return cast(EString)("?"); - } - - mixin EncoderFunctions; -} - -//============================================================================= -// ASCII -//============================================================================= - -/** Defines various character sets. */ -enum AsciiChar : ubyte { _init } -/// Ditto -alias AsciiString = immutable(AsciiChar)[]; - -template EncoderInstance(CharType : AsciiChar) -{ - alias E = AsciiChar; - alias EString = AsciiString; - - @property string encodingName() @safe pure nothrow @nogc - { - return "ASCII"; - } - - bool canEncode(dchar c) @safe pure nothrow @nogc - { - return c < 0x80; - } - - bool isValidCodeUnit(AsciiChar c) @safe pure nothrow @nogc - { - return c < 0x80; - } - - size_t encodedLength(dchar c) @safe pure nothrow @nogc - in - { - assert(canEncode(c)); - } - do - { - return 1; - } - - void encodeX(Range)(dchar c, Range r) - { - if (!canEncode(c)) c = '?'; - r.write(cast(AsciiChar) c); - } - - void encodeViaWrite()(dchar c) - { - if (!canEncode(c)) c = '?'; - write(cast(AsciiChar) c); - } - - void skipViaRead()() - { - read(); - } - - dchar decodeViaRead()() - { - return read(); - } - - dchar safeDecodeViaRead()() - { - immutable c = read(); - return canEncode(c) ? c : INVALID_SEQUENCE; - } - - dchar decodeReverseViaRead()() - { - return read(); - } - - @property EString replacementSequence() @safe pure nothrow @nogc - { - return cast(EString)("?"); - } - - mixin EncoderFunctions; -} - -//============================================================================= -// ISO-8859-1 -//============================================================================= - -/** Defines an Latin1-encoded character. */ -enum Latin1Char : ubyte { _init } -/** -Defines an Latin1-encoded string (as an array of $(D -immutable(Latin1Char))). - */ -alias Latin1String = immutable(Latin1Char)[]; - -template EncoderInstance(CharType : Latin1Char) -{ - alias E = Latin1Char; - alias EString = Latin1String; - - @property string encodingName() @safe pure nothrow @nogc - { - return "ISO-8859-1"; - } - - bool canEncode(dchar c) @safe pure nothrow @nogc - { - return c < 0x100; - } - - bool isValidCodeUnit(Latin1Char c) @safe pure nothrow @nogc - { - return true; - } - - size_t encodedLength(dchar c) @safe pure nothrow @nogc - in - { - assert(canEncode(c)); - } - do - { - return 1; - } - - void encodeViaWrite()(dchar c) - { - if (!canEncode(c)) c = '?'; - write(cast(Latin1Char) c); - } - - void skipViaRead()() - { - read(); - } - - dchar decodeViaRead()() - { - return read(); - } - - dchar safeDecodeViaRead()() - { - return read(); - } - - dchar decodeReverseViaRead()() - { - return read(); - } - - @property EString replacementSequence() @safe pure nothrow @nogc - { - return cast(EString)("?"); - } - - mixin EncoderFunctions; -} - -//============================================================================= -// ISO-8859-2 -//============================================================================= - -/// Defines a Latin2-encoded character. -enum Latin2Char : ubyte { _init } - -/** - * Defines an Latin2-encoded string (as an array of $(D - * immutable(Latin2Char))). - */ -alias Latin2String = immutable(Latin2Char)[]; - -private template EncoderInstance(CharType : Latin2Char) -{ - import std.typecons : Tuple, tuple; - - alias E = Latin2Char; - alias EString = Latin2String; - - @property string encodingName() @safe pure nothrow @nogc - { - return "ISO-8859-2"; - } - - private static immutable dchar m_charMapStart = 0xa1; - private static immutable dchar m_charMapEnd = 0xff; - - private immutable wstring charMap = - "\u0104\u02D8\u0141\u00A4\u013D\u015A\u00A7\u00A8"~ - "\u0160\u015E\u0164\u0179\u00AD\u017D\u017B\u00B0"~ - "\u0105\u02DB\u0142\u00B4\u013E\u015B\u02C7\u00B8"~ - "\u0161\u015F\u0165\u017A\u02DD\u017E\u017C\u0154"~ - "\u00C1\u00C2\u0102\u00C4\u0139\u0106\u00C7\u010C"~ - "\u00C9\u0118\u00CB\u011A\u00CD\u00CE\u010E\u0110"~ - "\u0143\u0147\u00D3\u00D4\u0150\u00D6\u00D7\u0158"~ - "\u016E\u00DA\u0170\u00DC\u00DD\u0162\u00DF\u0155"~ - "\u00E1\u00E2\u0103\u00E4\u013A\u0107\u00E7\u010D"~ - "\u00E9\u0119\u00EB\u011B\u00ED\u00EE\u010F\u0111"~ - "\u0144\u0148\u00F3\u00F4\u0151\u00F6\u00F7\u0159"~ - "\u016F\u00FA\u0171\u00FC\u00FD\u0163\u02D9"; - - private immutable Tuple!(wchar, char)[] bstMap = [ - tuple('\u0148','\xF2'), tuple('\u00F3','\xF3'), tuple('\u0165','\xBB'), - tuple('\u00D3','\xD3'), tuple('\u010F','\xEF'), tuple('\u015B','\xB6'), - tuple('\u017C','\xBF'), tuple('\u00C1','\xC1'), tuple('\u00E1','\xE1'), - tuple('\u0103','\xE3'), tuple('\u013A','\xE5'), tuple('\u0155','\xE0'), - tuple('\u0161','\xB9'), tuple('\u0171','\xFB'), tuple('\u02D8','\xA2'), - tuple('\u00AD','\xAD'), tuple('\u00C9','\xC9'), tuple('\u00DA','\xDA'), - tuple('\u00E9','\xE9'), tuple('\u00FA','\xFA'), tuple('\u0107','\xE6'), - tuple('\u0119','\xEA'), tuple('\u0142','\xB3'), tuple('\u0151','\xF5'), - tuple('\u0159','\xF8'), tuple('\u015F','\xBA'), tuple('\u0163','\xFE'), - tuple('\u016F','\xF9'), tuple('\u017A','\xBC'), tuple('\u017E','\xBE'), - tuple('\u02DB','\xB2'), tuple('\u00A7','\xA7'), tuple('\u00B4','\xB4'), - tuple('\u00C4','\xC4'), tuple('\u00CD','\xCD'), tuple('\u00D6','\xD6'), - tuple('\u00DD','\xDD'), tuple('\u00E4','\xE4'), tuple('\u00ED','\xED'), - tuple('\u00F6','\xF6'), tuple('\u00FD','\xFD'), tuple('\u0105','\xB1'), - tuple('\u010D','\xE8'), tuple('\u0111','\xF0'), tuple('\u011B','\xEC'), - tuple('\u013E','\xB5'), tuple('\u0144','\xF1'), tuple('\u0150','\xD5'), - tuple('\u0154','\xC0'), tuple('\u0158','\xD8'), tuple('\u015A','\xA6'), - tuple('\u015E','\xAA'), tuple('\u0160','\xA9'), tuple('\u0162','\xDE'), - tuple('\u0164','\xAB'), tuple('\u016E','\xD9'), tuple('\u0170','\xDB'), - tuple('\u0179','\xAC'), tuple('\u017B','\xAF'), tuple('\u017D','\xAE'), - tuple('\u02C7','\xB7'), tuple('\u02D9','\xFF'), tuple('\u02DD','\xBD'), - tuple('\u00A4','\xA4'), tuple('\u00A8','\xA8'), tuple('\u00B0','\xB0'), - tuple('\u00B8','\xB8'), tuple('\u00C2','\xC2'), tuple('\u00C7','\xC7'), - tuple('\u00CB','\xCB'), tuple('\u00CE','\xCE'), tuple('\u00D4','\xD4'), - tuple('\u00D7','\xD7'), tuple('\u00DC','\xDC'), tuple('\u00DF','\xDF'), - tuple('\u00E2','\xE2'), tuple('\u00E7','\xE7'), tuple('\u00EB','\xEB'), - tuple('\u00EE','\xEE'), tuple('\u00F4','\xF4'), tuple('\u00F7','\xF7'), - tuple('\u00FC','\xFC'), tuple('\u0102','\xC3'), tuple('\u0104','\xA1'), - tuple('\u0106','\xC6'), tuple('\u010C','\xC8'), tuple('\u010E','\xCF'), - tuple('\u0110','\xD0'), tuple('\u0118','\xCA'), tuple('\u011A','\xCC'), - tuple('\u0139','\xC5'), tuple('\u013D','\xA5'), tuple('\u0141','\xA3'), - tuple('\u0143','\xD1'), tuple('\u0147','\xD2') - ]; - - mixin GenericEncoder!(); -} - -//============================================================================= -// WINDOWS-1250 -//============================================================================= - -/// Defines a Windows1250-encoded character. -enum Windows1250Char : ubyte { _init } - -/** - * Defines an Windows1250-encoded string (as an array of $(D - * immutable(Windows1250Char))). - */ -alias Windows1250String = immutable(Windows1250Char)[]; - -private template EncoderInstance(CharType : Windows1250Char) -{ - import std.typecons : Tuple, tuple; - - alias E = Windows1250Char; - alias EString = Windows1250String; - - @property string encodingName() @safe pure nothrow @nogc - { - return "windows-1250"; - } - - private static immutable dchar m_charMapStart = 0x80; - private static immutable dchar m_charMapEnd = 0xff; - - private immutable wstring charMap = - "\u20AC\uFFFD\u201A\uFFFD\u201E\u2026\u2020\u2021"~ - "\uFFFD\u2030\u0160\u2039\u015A\u0164\u017D\u0179"~ - "\uFFFD\u2018\u2019\u201C\u201D\u2022\u2013\u2014"~ - "\uFFFD\u2122\u0161\u203A\u015B\u0165\u017E\u017A"~ - "\u00A0\u02C7\u02D8\u0141\u00A4\u0104\u00A6\u00A7"~ - "\u00A8\u00A9\u015E\u00AB\u00AC\u00AD\u00AE\u017B"~ - "\u00B0\u00B1\u02DB\u0142\u00B4\u00B5\u00B6\u00B7"~ - "\u00B8\u0105\u015F\u00BB\u013D\u02DD\u013E\u017C"~ - "\u0154\u00C1\u00C2\u0102\u00C4\u0139\u0106\u00C7"~ - "\u010C\u00C9\u0118\u00CB\u011A\u00CD\u00CE\u010E"~ - "\u0110\u0143\u0147\u00D3\u00D4\u0150\u00D6\u00D7"~ - "\u0158\u016E\u00DA\u0170\u00DC\u00DD\u0162\u00DF"~ - "\u0155\u00E1\u00E2\u0103\u00E4\u013A\u0107\u00E7"~ - "\u010D\u00E9\u0119\u00EB\u011B\u00ED\u00EE\u010F"~ - "\u0111\u0144\u0148\u00F3\u00F4\u0151\u00F6\u00F7"~ - "\u0159\u016F\u00FA\u0171\u00FC\u00FD\u0163\u02D9"; - - private immutable Tuple!(wchar, char)[] bstMap = [ - tuple('\u011A','\xCC'), tuple('\u00DC','\xDC'), tuple('\u0179','\x8F'), - tuple('\u00B7','\xB7'), tuple('\u00FC','\xFC'), tuple('\u0158','\xD8'), - tuple('\u201C','\x93'), tuple('\u00AC','\xAC'), tuple('\u00CB','\xCB'), - tuple('\u00EB','\xEB'), tuple('\u010C','\xC8'), tuple('\u0143','\xD1'), - tuple('\u0162','\xDE'), tuple('\u02D9','\xFF'), tuple('\u2039','\x8B'), - tuple('\u00A7','\xA7'), tuple('\u00B1','\xB1'), tuple('\u00C2','\xC2'), - tuple('\u00D4','\xD4'), tuple('\u00E2','\xE2'), tuple('\u00F4','\xF4'), - tuple('\u0104','\xA5'), tuple('\u0110','\xD0'), tuple('\u013D','\xBC'), - tuple('\u0150','\xD5'), tuple('\u015E','\xAA'), tuple('\u016E','\xD9'), - tuple('\u017D','\x8E'), tuple('\u2014','\x97'), tuple('\u2021','\x87'), - tuple('\u20AC','\x80'), tuple('\u00A4','\xA4'), tuple('\u00A9','\xA9'), - tuple('\u00AE','\xAE'), tuple('\u00B5','\xB5'), tuple('\u00BB','\xBB'), - tuple('\u00C7','\xC7'), tuple('\u00CE','\xCE'), tuple('\u00D7','\xD7'), - tuple('\u00DF','\xDF'), tuple('\u00E7','\xE7'), tuple('\u00EE','\xEE'), - tuple('\u00F7','\xF7'), tuple('\u0102','\xC3'), tuple('\u0106','\xC6'), - tuple('\u010E','\xCF'), tuple('\u0118','\xCA'), tuple('\u0139','\xC5'), - tuple('\u0141','\xA3'), tuple('\u0147','\xD2'), tuple('\u0154','\xC0'), - tuple('\u015A','\x8C'), tuple('\u0160','\x8A'), tuple('\u0164','\x8D'), - tuple('\u0170','\xDB'), tuple('\u017B','\xAF'), tuple('\u02C7','\xA1'), - tuple('\u02DD','\xBD'), tuple('\u2019','\x92'), tuple('\u201E','\x84'), - tuple('\u2026','\x85'), tuple('\u203A','\x9B'), tuple('\u2122','\x99'), - tuple('\u00A0','\xA0'), tuple('\u00A6','\xA6'), tuple('\u00A8','\xA8'), - tuple('\u00AB','\xAB'), tuple('\u00AD','\xAD'), tuple('\u00B0','\xB0'), - tuple('\u00B4','\xB4'), tuple('\u00B6','\xB6'), tuple('\u00B8','\xB8'), - tuple('\u00C1','\xC1'), tuple('\u00C4','\xC4'), tuple('\u00C9','\xC9'), - tuple('\u00CD','\xCD'), tuple('\u00D3','\xD3'), tuple('\u00D6','\xD6'), - tuple('\u00DA','\xDA'), tuple('\u00DD','\xDD'), tuple('\u00E1','\xE1'), - tuple('\u00E4','\xE4'), tuple('\u00E9','\xE9'), tuple('\u00ED','\xED'), - tuple('\u00F3','\xF3'), tuple('\u00F6','\xF6'), tuple('\u00FA','\xFA'), - tuple('\u00FD','\xFD'), tuple('\u0103','\xE3'), tuple('\u0105','\xB9'), - tuple('\u0107','\xE6'), tuple('\u010D','\xE8'), tuple('\u010F','\xEF'), - tuple('\u0111','\xF0'), tuple('\u0119','\xEA'), tuple('\u011B','\xEC'), - tuple('\u013A','\xE5'), tuple('\u013E','\xBE'), tuple('\u0142','\xB3'), - tuple('\u0144','\xF1'), tuple('\u0148','\xF2'), tuple('\u0151','\xF5'), - tuple('\u0155','\xE0'), tuple('\u0159','\xF8'), tuple('\u015B','\x9C'), - tuple('\u015F','\xBA'), tuple('\u0161','\x9A'), tuple('\u0163','\xFE'), - tuple('\u0165','\x9D'), tuple('\u016F','\xF9'), tuple('\u0171','\xFB'), - tuple('\u017A','\x9F'), tuple('\u017C','\xBF'), tuple('\u017E','\x9E'), - tuple('\u02D8','\xA2'), tuple('\u02DB','\xB2'), tuple('\u2013','\x96'), - tuple('\u2018','\x91'), tuple('\u201A','\x82'), tuple('\u201D','\x94'), - tuple('\u2020','\x86'), tuple('\u2022','\x95'), tuple('\u2030','\x89') - ]; - - mixin GenericEncoder!(); -} - -//============================================================================= -// WINDOWS-1251 -//============================================================================= - -/// Defines a Windows1251-encoded character. -enum Windows1251Char : ubyte { _init } - -/** - * Defines an Windows1251-encoded string (as an array of $(D - * immutable(Windows1251Char))). - */ -alias Windows1251String = immutable(Windows1251Char)[]; - -private template EncoderInstance(CharType : Windows1251Char) -{ - import std.typecons : Tuple, tuple; - - alias E = Windows1251Char; - alias EString = Windows1251String; - - @property string encodingName() @safe pure nothrow @nogc - { - return "windows-1251"; - } - - private static immutable dchar m_charMapStart = 0x80; - private static immutable dchar m_charMapEnd = 0xff; - - private immutable wstring charMap = - "\u0402\u0403\u201A\u0453\u201E\u2026\u2020\u2021"~ - "\u20AC\u2030\u0409\u2039\u040A\u040C\u040B\u040F"~ - "\u0452\u2018\u2019\u201C\u201D\u2022\u2013\u2014"~ - "\uFFFD\u2122\u0459\u203A\u045A\u045C\u045B\u045F"~ - "\u00A0\u040E\u045E\u0408\u00A4\u0490\u00A6\u00A7"~ - "\u0401\u00A9\u0404\u00AB\u00AC\u00AD\u00AE\u0407"~ - "\u00B0\u00B1\u0406\u0456\u0491\u00B5\u00B6\u00B7"~ - "\u0451\u2116\u0454\u00BB\u0458\u0405\u0455\u0457"~ - "\u0410\u0411\u0412\u0413\u0414\u0415\u0416\u0417"~ - "\u0418\u0419\u041A\u041B\u041C\u041D\u041E\u041F"~ - "\u0420\u0421\u0422\u0423\u0424\u0425\u0426\u0427"~ - "\u0428\u0429\u042A\u042B\u042C\u042D\u042E\u042F"~ - "\u0430\u0431\u0432\u0433\u0434\u0435\u0436\u0437"~ - "\u0438\u0439\u043A\u043B\u043C\u043D\u043E\u043F"~ - "\u0440\u0441\u0442\u0443\u0444\u0445\u0446\u0447"~ - "\u0448\u0449\u044A\u044B\u044C\u044D\u044E\u044F"; - - private immutable Tuple!(wchar, char)[] bstMap = [ - tuple('\u0432','\xE2'),tuple('\u0412','\xC2'),tuple('\u0453','\x83'), - tuple('\u0401','\xA8'),tuple('\u0422','\xD2'),tuple('\u0442','\xF2'), - tuple('\u2018','\x91'),tuple('\u00AD','\xAD'),tuple('\u0409','\x8A'), - tuple('\u041A','\xCA'),tuple('\u042A','\xDA'),tuple('\u043A','\xEA'), - tuple('\u044A','\xFA'),tuple('\u045B','\x9E'),tuple('\u2022','\x95'), - tuple('\u00A7','\xA7'),tuple('\u00B5','\xB5'),tuple('\u0405','\xBD'), - tuple('\u040E','\xA1'),tuple('\u0416','\xC6'),tuple('\u041E','\xCE'), - tuple('\u0426','\xD6'),tuple('\u042E','\xDE'),tuple('\u0436','\xE6'), - tuple('\u043E','\xEE'),tuple('\u0446','\xF6'),tuple('\u044E','\xFE'), - tuple('\u0457','\xBF'),tuple('\u0490','\xA5'),tuple('\u201D','\x94'), - tuple('\u203A','\x9B'),tuple('\u00A4','\xA4'),tuple('\u00AB','\xAB'), - tuple('\u00B0','\xB0'),tuple('\u00B7','\xB7'),tuple('\u0403','\x81'), - tuple('\u0407','\xAF'),tuple('\u040B','\x8E'),tuple('\u0410','\xC0'), - tuple('\u0414','\xC4'),tuple('\u0418','\xC8'),tuple('\u041C','\xCC'), - tuple('\u0420','\xD0'),tuple('\u0424','\xD4'),tuple('\u0428','\xD8'), - tuple('\u042C','\xDC'),tuple('\u0430','\xE0'),tuple('\u0434','\xE4'), - tuple('\u0438','\xE8'),tuple('\u043C','\xEC'),tuple('\u0440','\xF0'), - tuple('\u0444','\xF4'),tuple('\u0448','\xF8'),tuple('\u044C','\xFC'), - tuple('\u0451','\xB8'),tuple('\u0455','\xBE'),tuple('\u0459','\x9A'), - tuple('\u045E','\xA2'),tuple('\u2013','\x96'),tuple('\u201A','\x82'), - tuple('\u2020','\x86'),tuple('\u2030','\x89'),tuple('\u2116','\xB9'), - tuple('\u00A0','\xA0'),tuple('\u00A6','\xA6'),tuple('\u00A9','\xA9'), - tuple('\u00AC','\xAC'),tuple('\u00AE','\xAE'),tuple('\u00B1','\xB1'), - tuple('\u00B6','\xB6'),tuple('\u00BB','\xBB'),tuple('\u0402','\x80'), - tuple('\u0404','\xAA'),tuple('\u0406','\xB2'),tuple('\u0408','\xA3'), - tuple('\u040A','\x8C'),tuple('\u040C','\x8D'),tuple('\u040F','\x8F'), - tuple('\u0411','\xC1'),tuple('\u0413','\xC3'),tuple('\u0415','\xC5'), - tuple('\u0417','\xC7'),tuple('\u0419','\xC9'),tuple('\u041B','\xCB'), - tuple('\u041D','\xCD'),tuple('\u041F','\xCF'),tuple('\u0421','\xD1'), - tuple('\u0423','\xD3'),tuple('\u0425','\xD5'),tuple('\u0427','\xD7'), - tuple('\u0429','\xD9'),tuple('\u042B','\xDB'),tuple('\u042D','\xDD'), - tuple('\u042F','\xDF'),tuple('\u0431','\xE1'),tuple('\u0433','\xE3'), - tuple('\u0435','\xE5'),tuple('\u0437','\xE7'),tuple('\u0439','\xE9'), - tuple('\u043B','\xEB'),tuple('\u043D','\xED'),tuple('\u043F','\xEF'), - tuple('\u0441','\xF1'),tuple('\u0443','\xF3'),tuple('\u0445','\xF5'), - tuple('\u0447','\xF7'),tuple('\u0449','\xF9'),tuple('\u044B','\xFB'), - tuple('\u044D','\xFD'),tuple('\u044F','\xFF'),tuple('\u0452','\x90'), - tuple('\u0454','\xBA'),tuple('\u0456','\xB3'),tuple('\u0458','\xBC'), - tuple('\u045A','\x9C'),tuple('\u045C','\x9D'),tuple('\u045F','\x9F'), - tuple('\u0491','\xB4'),tuple('\u2014','\x97'),tuple('\u2019','\x92'), - tuple('\u201C','\x93'),tuple('\u201E','\x84'),tuple('\u2021','\x87'), - tuple('\u2026','\x85'),tuple('\u2039','\x8B'),tuple('\u20AC','\x88'), - tuple('\u2122','\x99') - ]; - - mixin GenericEncoder!(); -} - -//============================================================================= -// WINDOWS-1252 -//============================================================================= - -/// Defines a Windows1252-encoded character. -enum Windows1252Char : ubyte { _init } - -/** - * Defines an Windows1252-encoded string (as an array of $(D - * immutable(Windows1252Char))). - */ -alias Windows1252String = immutable(Windows1252Char)[]; - -template EncoderInstance(CharType : Windows1252Char) -{ - import std.typecons : Tuple, tuple; - - alias E = Windows1252Char; - alias EString = Windows1252String; - - @property string encodingName() @safe pure nothrow @nogc - { - return "windows-1252"; - } - - private static immutable dchar m_charMapStart = 0x80; - private static immutable dchar m_charMapEnd = 0x9f; - - private immutable wstring charMap = - "\u20AC\uFFFD\u201A\u0192\u201E\u2026\u2020\u2021"~ - "\u02C6\u2030\u0160\u2039\u0152\uFFFD\u017D\uFFFD"~ - "\uFFFD\u2018\u2019\u201C\u201D\u2022\u2013\u2014"~ - "\u02DC\u2122\u0161\u203A\u0153\uFFFD\u017E\u0178"; - - private immutable Tuple!(wchar, char)[] bstMap = [ - tuple('\u201C','\x93'), tuple('\u0192','\x83'), tuple('\u2039','\x8B'), - tuple('\u0161','\x9A'), tuple('\u2014','\x97'), tuple('\u2021','\x87'), - tuple('\u20AC','\x80'), tuple('\u0153','\x9C'), tuple('\u017D','\x8E'), - tuple('\u02DC','\x98'), tuple('\u2019','\x92'), tuple('\u201E','\x84'), - tuple('\u2026','\x85'), tuple('\u203A','\x9B'), tuple('\u2122','\x99'), - tuple('\u0152','\x8C'), tuple('\u0160','\x8A'), tuple('\u0178','\x9F'), - tuple('\u017E','\x9E'), tuple('\u02C6','\x88'), tuple('\u2013','\x96'), - tuple('\u2018','\x91'), tuple('\u201A','\x82'), tuple('\u201D','\x94'), - tuple('\u2020','\x86'), tuple('\u2022','\x95'), tuple('\u2030','\x89') - ]; - - mixin GenericEncoder!(); -} - -//============================================================================= -// UTF-8 -//============================================================================= - -template EncoderInstance(CharType : char) -{ - alias E = char; - alias EString = immutable(char)[]; - - @property string encodingName() @safe pure nothrow @nogc - { - return "UTF-8"; - } - - bool canEncode(dchar c) @safe pure nothrow @nogc - { - return isValidCodePoint(c); - } - - bool isValidCodeUnit(char c) @safe pure nothrow @nogc - { - return (c < 0xC0 || (c >= 0xC2 && c < 0xF5)); - } - - immutable ubyte[128] tailTable = - [ - 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,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,0,0,0,0,0,0,0,0,0,0, - 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, - 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, - 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, - 3,3,3,3,3,3,3,3,4,4,4,4,5,5,6,0, - ]; - - private int tails(char c) @safe pure nothrow @nogc - in - { - assert(c >= 0x80); - } - do - { - return tailTable[c-0x80]; - } - - size_t encodedLength(dchar c) @safe pure nothrow @nogc - in - { - assert(canEncode(c)); - } - do - { - if (c < 0x80) return 1; - if (c < 0x800) return 2; - if (c < 0x10000) return 3; - return 4; - } - - void encodeViaWrite()(dchar c) - { - if (c < 0x80) - { - write(cast(char) c); - } - else if (c < 0x800) - { - write(cast(char)((c >> 6) + 0xC0)); - write(cast(char)((c & 0x3F) + 0x80)); - } - else if (c < 0x10000) - { - write(cast(char)((c >> 12) + 0xE0)); - write(cast(char)(((c >> 6) & 0x3F) + 0x80)); - write(cast(char)((c & 0x3F) + 0x80)); - } - else - { - write(cast(char)((c >> 18) + 0xF0)); - write(cast(char)(((c >> 12) & 0x3F) + 0x80)); - write(cast(char)(((c >> 6) & 0x3F) + 0x80)); - write(cast(char)((c & 0x3F) + 0x80)); - } - } - - void skipViaRead()() - { - auto c = read(); - if (c < 0xC0) return; - int n = tails(cast(char) c); - for (size_t i=0; i 0xF4) // fail overlong 4-6-byte sequences - || (c == 0xE0 && ((d & 0xE0) == 0x80)) // fail overlong 3-byte sequences - || (c == 0xED && ((d & 0xE0) == 0xA0)) // fail surrogates - || (c == 0xF0 && ((d & 0xF0) == 0x80)) // fail overlong 4-byte sequences - || (c == 0xF4 && ((d & 0xF0) >= 0x90)) // fail code points > 0x10FFFF - ); - - c &= (1 << (6 - n)) - 1; - for (size_t i=0; i> 10))); - write(cast(wchar)(0xDC00 + (n & 0x3FF))); - } - } - - void skipViaRead()() - { - immutable c = read(); - if (c < 0xD800 || c >= 0xE000) return; - read(); - } - - dchar decodeViaRead()() - { - wchar c = read(); - if (c < 0xD800 || c >= 0xE000) return cast(dchar) c; - wchar d = read(); - c &= 0x3FF; - d &= 0x3FF; - return 0x10000 + (c << 10) + d; - } - - dchar safeDecodeViaRead()() - { - wchar c = read(); - if (c < 0xD800 || c >= 0xE000) return cast(dchar) c; - if (c >= 0xDC00) return INVALID_SEQUENCE; - if (!canRead) return INVALID_SEQUENCE; - wchar d = peek(); - if (d < 0xDC00 || d >= 0xE000) return INVALID_SEQUENCE; - d = read(); - c &= 0x3FF; - d &= 0x3FF; - return 0x10000 + (c << 10) + d; - } - - dchar decodeReverseViaRead()() - { - wchar c = read(); - if (c < 0xD800 || c >= 0xE000) return cast(dchar) c; - wchar d = read(); - c &= 0x3FF; - d &= 0x3FF; - return 0x10000 + (d << 10) + c; - } - - @property EString replacementSequence() @safe pure nothrow @nogc - { - return "\uFFFD"w; - } - - mixin EncoderFunctions; -} - -//============================================================================= -// UTF-32 -//============================================================================= - -template EncoderInstance(CharType : dchar) -{ - alias E = dchar; - alias EString = immutable(dchar)[]; - - @property string encodingName() @safe pure nothrow @nogc - { - return "UTF-32"; - } - - bool canEncode(dchar c) @safe pure @nogc nothrow - { - return isValidCodePoint(c); - } - - bool isValidCodeUnit(dchar c) @safe pure @nogc nothrow - { - return isValidCodePoint(c); - } - - size_t encodedLength(dchar c) @safe pure @nogc nothrow - in - { - assert(canEncode(c)); - } - do - { - return 1; - } - - void encodeViaWrite()(dchar c) - { - write(c); - } - - void skipViaRead()() - { - read(); - } - - dchar decodeViaRead()() - { - return cast(dchar) read(); - } - - dchar safeDecodeViaRead()() - { - immutable c = read(); - return isValidCodePoint(c) ? c : INVALID_SEQUENCE; - } - - dchar decodeReverseViaRead()() - { - return cast(dchar) read(); - } - - @property EString replacementSequence() @safe pure nothrow @nogc - { - return "\uFFFD"d; - } - - mixin EncoderFunctions; -} - -//============================================================================= -// Below are forwarding functions which expose the function to the user - -/** -Returns true if c is a valid code point - - Note that this includes the non-character code points U+FFFE and U+FFFF, - since these are valid code points (even though they are not valid - characters). - - Supersedes: - This function supersedes `std.utf.startsValidDchar()`. - - Standards: Unicode 5.0, ASCII, ISO-8859-1, ISO-8859-2, WINDOWS-1250, - WINDOWS-1251, WINDOWS-1252 - - Params: - c = the code point to be tested - */ -bool isValidCodePoint(dchar c) @safe pure nothrow @nogc -{ - return c < 0xD800 || (c >= 0xE000 && c < 0x110000); -} - -/** - Returns the name of an encoding. - - The type of encoding cannot be deduced. Therefore, it is necessary to - explicitly specify the encoding type. - - Standards: Unicode 5.0, ASCII, ISO-8859-1, ISO-8859-2, WINDOWS-1250, - WINDOWS-1251, WINDOWS-1252 - */ -@property string encodingName(T)() -{ - return EncoderInstance!(T).encodingName; -} - -/// -@safe unittest -{ - assert(encodingName!(char) == "UTF-8"); - assert(encodingName!(wchar) == "UTF-16"); - assert(encodingName!(dchar) == "UTF-32"); - assert(encodingName!(AsciiChar) == "ASCII"); - assert(encodingName!(Latin1Char) == "ISO-8859-1"); - assert(encodingName!(Latin2Char) == "ISO-8859-2"); - assert(encodingName!(Windows1250Char) == "windows-1250"); - assert(encodingName!(Windows1251Char) == "windows-1251"); - assert(encodingName!(Windows1252Char) == "windows-1252"); -} - -/** - Returns true iff it is possible to represent the specified codepoint - in the encoding. - - The type of encoding cannot be deduced. Therefore, it is necessary to - explicitly specify the encoding type. - - Standards: Unicode 5.0, ASCII, ISO-8859-1, ISO-8859-2, WINDOWS-1250, - WINDOWS-1251, WINDOWS-1252 - */ -bool canEncode(E)(dchar c) -{ - return EncoderInstance!(E).canEncode(c); -} - -/// -@safe pure unittest -{ - assert( canEncode!(Latin1Char)('A')); - assert( canEncode!(Latin2Char)('A')); - assert(!canEncode!(AsciiChar)('\u00A0')); - assert( canEncode!(Latin1Char)('\u00A0')); - assert( canEncode!(Latin2Char)('\u00A0')); - assert( canEncode!(Windows1250Char)('\u20AC')); - assert(!canEncode!(Windows1250Char)('\u20AD')); - assert(!canEncode!(Windows1250Char)('\uFFFD')); - assert( canEncode!(Windows1251Char)('\u0402')); - assert(!canEncode!(Windows1251Char)('\u20AD')); - assert(!canEncode!(Windows1251Char)('\uFFFD')); - assert( canEncode!(Windows1252Char)('\u20AC')); - assert(!canEncode!(Windows1252Char)('\u20AD')); - assert(!canEncode!(Windows1252Char)('\uFFFD')); - assert(!canEncode!(char)(cast(dchar) 0x110000)); -} - -/// How to check an entire string -@safe pure unittest -{ - import std.algorithm.searching : find; - import std.utf : byDchar; - - assert("The quick brown fox" - .byDchar - .find!(x => !canEncode!AsciiChar(x)) - .empty); -} - -/** - Returns true if the code unit is legal. For example, the byte 0x80 would - not be legal in ASCII, because ASCII code units must always be in the range - 0x00 to 0x7F. - - Standards: Unicode 5.0, ASCII, ISO-8859-1, ISO-8859-2, WINDOWS-1250, - WINDOWS-1251, WINDOWS-1252 - - Params: - c = the code unit to be tested - */ -bool isValidCodeUnit(E)(E c) -{ - return EncoderInstance!(E).isValidCodeUnit(c); -} - -/// -@system pure unittest -{ - assert(!isValidCodeUnit(cast(char) 0xC0)); - assert(!isValidCodeUnit(cast(char) 0xFF)); - assert( isValidCodeUnit(cast(wchar) 0xD800)); - assert(!isValidCodeUnit(cast(dchar) 0xD800)); - assert(!isValidCodeUnit(cast(AsciiChar) 0xA0)); - assert( isValidCodeUnit(cast(Windows1250Char) 0x80)); - assert(!isValidCodeUnit(cast(Windows1250Char) 0x81)); - assert( isValidCodeUnit(cast(Windows1251Char) 0x80)); - assert(!isValidCodeUnit(cast(Windows1251Char) 0x98)); - assert( isValidCodeUnit(cast(Windows1252Char) 0x80)); - assert(!isValidCodeUnit(cast(Windows1252Char) 0x81)); -} - -/** - Returns true if the string is encoded correctly - - Supersedes: - This function supersedes std.utf.validate(), however note that this - function returns a bool indicating whether the input was valid or not, - whereas the older function would throw an exception. - - Standards: Unicode 5.0, ASCII, ISO-8859-1, ISO-8859-2, WINDOWS-1250, - WINDOWS-1251, WINDOWS-1252 - - Params: - s = the string to be tested - */ -bool isValid(E)(const(E)[] s) -{ - return s.length == validLength(s); -} - -/// -@system pure unittest -{ - assert( isValid("\u20AC100")); - assert(!isValid(cast(char[3])[167, 133, 175])); -} - -/** - Returns the length of the longest possible substring, starting from - the first code unit, which is validly encoded. - - Standards: Unicode 5.0, ASCII, ISO-8859-1, ISO-8859-2, WINDOWS-1250, - WINDOWS-1251, WINDOWS-1252 - - Params: - s = the string to be tested - */ -size_t validLength(E)(const(E)[] s) -{ - size_t result, before = void; - while ((before = s.length) > 0) - { - if (EncoderInstance!(E).safeDecode(s) == INVALID_SEQUENCE) - break; - result += before - s.length; - } - return result; -} - -/** - Sanitizes a string by replacing malformed code unit sequences with valid - code unit sequences. The result is guaranteed to be valid for this encoding. - - If the input string is already valid, this function returns the original, - otherwise it constructs a new string by replacing all illegal code unit - sequences with the encoding's replacement character, Invalid sequences will - be replaced with the Unicode replacement character (U+FFFD) if the - character repertoire contains it, otherwise invalid sequences will be - replaced with '?'. - - Standards: Unicode 5.0, ASCII, ISO-8859-1, ISO-8859-2, WINDOWS-1250, - WINDOWS-1251, WINDOWS-1252 - - Params: - s = the string to be sanitized - */ -immutable(E)[] sanitize(E)(immutable(E)[] s) -{ - size_t n = validLength(s); - if (n == s.length) return s; - - auto repSeq = EncoderInstance!(E).replacementSequence; - - // Count how long the string needs to be. - // Overestimating is not a problem - size_t len = s.length; - const(E)[] t = s[n..$]; - while (t.length != 0) - { - immutable c = EncoderInstance!(E).safeDecode(t); - assert(c == INVALID_SEQUENCE); - len += repSeq.length; - t = t[validLength(t)..$]; - } - - // Now do the write - E[] array = new E[len]; - array[0 .. n] = s[0 .. n]; - size_t offset = n; - - t = s[n..$]; - while (t.length != 0) - { - immutable c = EncoderInstance!(E).safeDecode(t); - assert(c == INVALID_SEQUENCE); - array[offset .. offset+repSeq.length] = repSeq[]; - offset += repSeq.length; - n = validLength(t); - array[offset .. offset+n] = t[0 .. n]; - offset += n; - t = t[n..$]; - } - return cast(immutable(E)[])array[0 .. offset]; -} - -/// -@system pure unittest -{ - assert(sanitize("hello \xF0\x80world") == "hello \xEF\xBF\xBDworld"); -} - -/** - Returns the length of the first encoded sequence. - - The input to this function MUST be validly encoded. - This is enforced by the function's in-contract. - - Standards: Unicode 5.0, ASCII, ISO-8859-1, ISO-8859-2, WINDOWS-1250, - WINDOWS-1251, WINDOWS-1252 - - Params: - s = the string to be sliced - */ -size_t firstSequence(E)(const(E)[] s) -in -{ - assert(s.length != 0); - const(E)[] u = s; - assert(safeDecode(u) != INVALID_SEQUENCE); -} -do -{ - auto before = s.length; - EncoderInstance!(E).skip(s); - return before - s.length; -} - -/// -@system pure unittest -{ - assert(firstSequence("\u20AC1000") == "\u20AC".length); - assert(firstSequence("hel") == "h".length); -} - -/** - Returns the length of the last encoded sequence. - - The input to this function MUST be validly encoded. - This is enforced by the function's in-contract. - - Standards: Unicode 5.0, ASCII, ISO-8859-1, ISO-8859-2, WINDOWS-1250, - WINDOWS-1251, WINDOWS-1252 - - Params: - s = the string to be sliced - */ -size_t lastSequence(E)(const(E)[] s) -in -{ - assert(s.length != 0); - assert(isValid(s)); -} -do -{ - const(E)[] t = s; - EncoderInstance!(E).decodeReverse(s); - return t.length - s.length; -} - -/// -@system pure unittest -{ - assert(lastSequence("1000\u20AC") == "\u20AC".length); - assert(lastSequence("hellĂś") == "Ăś".length); -} - -/** - Returns the array index at which the (n+1)th code point begins. - - The input to this function MUST be validly encoded. - This is enforced by the function's in-contract. - - Supersedes: - This function supersedes std.utf.toUTFindex(). - - Standards: Unicode 5.0, ASCII, ISO-8859-1, ISO-8859-2, WINDOWS-1250, - WINDOWS-1251, WINDOWS-1252 - - Params: - s = the string to be counted - n = the current code point index - */ -ptrdiff_t index(E)(const(E)[] s,int n) -in -{ - assert(isValid(s)); - assert(n >= 0); -} -do -{ - const(E)[] t = s; - for (size_t i=0; i> 6))); - put(range, cast(char)(0x80 | (c & 0x3F))); - return 2; - } - if (c <= 0xFFFF) - { - put(range, cast(char)(0xE0 | (c >> 12))); - put(range, cast(char)(0x80 | ((c >> 6) & 0x3F))); - put(range, cast(char)(0x80 | (c & 0x3F))); - return 3; - } - if (c <= 0x10FFFF) - { - put(range, cast(char)(0xF0 | (c >> 18))); - put(range, cast(char)(0x80 | ((c >> 12) & 0x3F))); - put(range, cast(char)(0x80 | ((c >> 6) & 0x3F))); - put(range, cast(char)(0x80 | (c & 0x3F))); - return 4; - } - else - { - assert(0); - } - } - else static if (is(immutable E == immutable wchar)) - { - if (c <= 0xFFFF) - { - range.put(cast(wchar) c); - return 1; - } - range.put(cast(wchar) ((((c - 0x10000) >> 10) & 0x3FF) + 0xD800)); - range.put(cast(wchar) (((c - 0x10000) & 0x3FF) + 0xDC00)); - return 2; - } - else static if (is(immutable E == immutable dchar)) - { - range.put(c); - return 1; - } - else - { - static assert(0); - } -} - -@safe pure unittest -{ - import std.array; - Appender!(char[]) r; - assert(encode!(char)('T', r) == 1); - assert(encode!(wchar)('T', r) == 1); - assert(encode!(dchar)('T', r) == 1); -} - -/** - Encodes a single code point to a delegate. - - This function encodes a single code point into one or more code units. - The code units are passed one at a time to the supplied delegate. - - The input to this function MUST be a valid code point. - This is enforced by the function's in-contract. - - The type of the output cannot be deduced. Therefore, it is necessary to - explicitly specify the encoding as a template parameter. - - Supersedes: - This function supersedes std.utf.encode(), however, note that the - function codeUnits() supersedes it more conveniently. - - Standards: Unicode 5.0, ASCII, ISO-8859-1, ISO-8859-2, WINDOWS-1250, - WINDOWS-1251, WINDOWS-1252 - - Params: - c = the code point to be encoded - dg = the delegate to invoke for each code unit - */ -void encode(E)(dchar c, void delegate(E) dg) -in -{ - assert(isValidCodePoint(c)); -} -do -{ - EncoderInstance!(E).encode(c,dg); -} - -/** -Encodes the contents of `s` in units of type `Tgt`, writing the result to an -output range. - -Returns: The number of `Tgt` elements written. -Params: -Tgt = Element type of `range`. -s = Input array. -range = Output range. - */ -size_t encode(Tgt, Src, R)(in Src[] s, R range) -{ - size_t result; - foreach (c; s) - { - result += encode!(Tgt)(c, range); - } - return result; -} - -/** - Returns a foreachable struct which can bidirectionally iterate over all - code points in a string. - - The input to this function MUST be validly encoded. - This is enforced by the function's in-contract. - - You can foreach either - with or without an index. If an index is specified, it will be initialized - at each iteration with the offset into the string at which the code point - begins. - - Supersedes: - This function supersedes std.utf.decode(). - - Standards: Unicode 5.0, ASCII, ISO-8859-1, ISO-8859-2, WINDOWS-1250, - WINDOWS-1251, WINDOWS-1252 - - Params: - s = the string to be decoded - - Example: - -------------------------------------------------------- - string s = "hello world"; - foreach (c;codePoints(s)) - { - // do something with c (which will always be a dchar) - } - -------------------------------------------------------- - - Note that, currently, foreach (c:codePoints(s)) is superior to foreach (c;s) - in that the latter will fall over on encountering U+FFFF. - */ -CodePoints!(E) codePoints(E)(immutable(E)[] s) -in -{ - assert(isValid(s)); -} -do -{ - return CodePoints!(E)(s); -} - -/// -@system unittest -{ - string s = "hello"; - string t; - foreach (c;codePoints(s)) - { - t ~= cast(char) c; - } - assert(s == t); -} - -/** - Returns a foreachable struct which can bidirectionally iterate over all - code units in a code point. - - The input to this function MUST be a valid code point. - This is enforced by the function's in-contract. - - The type of the output cannot be deduced. Therefore, it is necessary to - explicitly specify the encoding type in the template parameter. - - Supersedes: - This function supersedes std.utf.encode(). - - Standards: Unicode 5.0, ASCII, ISO-8859-1, ISO-8859-2, WINDOWS-1250, - WINDOWS-1251, WINDOWS-1252 - - Params: - c = the code point to be encoded - */ -CodeUnits!(E) codeUnits(E)(dchar c) -in -{ - assert(isValidCodePoint(c)); -} -do -{ - return CodeUnits!(E)(c); -} - -/// -@system unittest -{ - char[] a; - foreach (c;codeUnits!(char)(cast(dchar)'\u20AC')) - { - a ~= c; - } - assert(a.length == 3); - assert(a[0] == 0xE2); - assert(a[1] == 0x82); - assert(a[2] == 0xAC); -} - -/** - Convert a string from one encoding to another. - - Supersedes: - This function supersedes std.utf.toUTF8(), std.utf.toUTF16() and - std.utf.toUTF32() - (but note that to!() supersedes it more conveniently). - - Standards: Unicode 5.0, ASCII, ISO-8859-1, ISO-8859-2, WINDOWS-1250, - WINDOWS-1251, WINDOWS-1252 - - Params: - s = Source string. $(B Must) be validly encoded. - This is enforced by the function's in-contract. - r = Destination string - - See_Also: - $(REF to, std,conv) - */ -void transcode(Src, Dst)(Src[] s, out Dst[] r) -in -{ - assert(isValid(s)); -} -do -{ - static if (is(Src == Dst) && is(Src == immutable)) - { - r = s; - } - else static if (is(immutable Src == immutable AsciiChar)) - { - transcode(cast(const(char)[])s, r); - } - else - { - static if (is(immutable Dst == immutable wchar)) - { - immutable minReservePlace = 2; - } - else static if (is(immutable Dst == immutable dchar)) - { - immutable minReservePlace = 1; - } - else - { - immutable minReservePlace = 6; - } - - auto buffer = new Unqual!Dst[s.length]; - auto tmpBuffer = buffer; - - while (s.length != 0) - { - if (tmpBuffer.length < minReservePlace) - { - size_t prevLength = buffer.length; - buffer.length += s.length + minReservePlace; - tmpBuffer = buffer[prevLength - tmpBuffer.length .. $]; - } - EncoderInstance!(Unqual!Dst).encode(decode(s), tmpBuffer); - } - - r = cast(Dst[]) buffer[0 .. buffer.length - tmpBuffer.length]; - } -} - -/// -@system pure unittest -{ - wstring ws; - // transcode from UTF-8 to UTF-16 - transcode("hello world",ws); - assert(ws == "hello world"w); - - Latin1String ls; - // transcode from UTF-16 to ISO-8859-1 - transcode(ws, ls); - assert(ls == "hello world"); -} - -@system pure unittest -{ - import std.meta; - import std.range; - { - import std.conv : to; - - string asciiCharString = to!string(iota(0, 128, 1)); - - alias Types = AliasSeq!(string, Latin1String, Latin2String, AsciiString, - Windows1250String, Windows1251String, Windows1252String, dstring, wstring); - foreach (S; Types) - foreach (D; Types) - { - string str; - S sStr; - D dStr; - transcode(asciiCharString, sStr); - transcode(sStr, dStr); - transcode(dStr, str); - assert(asciiCharString == str); - } - } - { - string czechChars = "PříliĹĄ ĹžluĹĽoučkĂ˝ kůň Ăşpěl ďábelskĂŠ Ăłdy."; - alias Types = AliasSeq!(string, dstring, wstring); - foreach (S; Types) - foreach (D; Types) - { - string str; - S sStr; - D dStr; - transcode(czechChars, sStr); - transcode(sStr, dStr); - transcode(dStr, str); - assert(czechChars == str); - } - } -} - -@system unittest // mutable/const input/output -{ - import std.meta : AliasSeq; - - static foreach (O; AliasSeq!(Latin1Char, const Latin1Char, immutable Latin1Char)) - {{ - O[] output; - - char[] mutableInput = "äbc".dup; - transcode(mutableInput, output); - assert(output == [0xE4, 'b', 'c']); - - const char[] constInput = "Ăśbc"; - transcode(constInput, output); - assert(output == [0xF6, 'b', 'c']); - - immutable char[] immutInput = "Ăźbc"; - transcode(immutInput, output); - assert(output == [0xFC, 'b', 'c']); - }} - - // Make sure that const/mutable input is copied. - static foreach (C; AliasSeq!(char, const char)) - {{ - C[] input = "foo".dup; - C[] output; - transcode(input, output); - assert(input == output); - assert(input !is output); - }} - - // But immutable input should not be copied. - string input = "foo"; - string output; - transcode(input, output); - assert(input is output); -} - -//============================================================================= - -/** The base class for exceptions thrown by this module */ -class EncodingException : Exception { this(string msg) @safe pure { super(msg); } } - -class UnrecognizedEncodingException : EncodingException -{ - private this(string msg) @safe pure { super(msg); } -} - -/** Abstract base class of all encoding schemes */ -abstract class EncodingScheme -{ - import std.uni : toLower; - - /** - * Registers a subclass of EncodingScheme. - * - * This function allows user-defined subclasses of EncodingScheme to - * be declared in other modules. - * - * Params: - * Klass = The subclass of EncodingScheme to register. - * - * Example: - * ---------------------------------------------- - * class Amiga1251 : EncodingScheme - * { - * shared static this() - * { - * EncodingScheme.register!Amiga1251; - * } - * } - * ---------------------------------------------- - */ - static void register(Klass:EncodingScheme)() - { - scope scheme = new Klass(); - foreach (encodingName;scheme.names()) - { - supported[toLower(encodingName)] = () => new Klass(); - } - } - - deprecated("Please pass the EncodingScheme subclass as template argument instead.") - static void register(string className) - { - auto scheme = cast(EncodingScheme) ClassInfo.find(className).create(); - if (scheme is null) - throw new EncodingException("Unable to create class "~className); - foreach (encodingName;scheme.names()) - { - supportedFactories[toLower(encodingName)] = className; - } - } - - /** - * Obtains a subclass of EncodingScheme which is capable of encoding - * and decoding the named encoding scheme. - * - * This function is only aware of EncodingSchemes which have been - * registered with the register() function. - * - * Example: - * --------------------------------------------------- - * auto scheme = EncodingScheme.create("Amiga-1251"); - * --------------------------------------------------- - */ - static EncodingScheme create(string encodingName) - { - static bool registerDefaultEncodings() - { - EncodingScheme.register!EncodingSchemeASCII; - EncodingScheme.register!EncodingSchemeLatin1; - EncodingScheme.register!EncodingSchemeLatin2; - EncodingScheme.register!EncodingSchemeWindows1250; - EncodingScheme.register!EncodingSchemeWindows1251; - EncodingScheme.register!EncodingSchemeWindows1252; - EncodingScheme.register!EncodingSchemeUtf8; - EncodingScheme.register!EncodingSchemeUtf16Native; - EncodingScheme.register!EncodingSchemeUtf32Native; - return true; - } - - static shared bool initialized; - import std.concurrency : initOnce; - initOnce!initialized(registerDefaultEncodings()); - encodingName = toLower(encodingName); - - if (auto p = encodingName in supported) - return (*p)(); - - auto p = encodingName in supportedFactories; - if (p is null) - throw new EncodingException("Unrecognized Encoding: "~encodingName); - string className = *p; - auto scheme = cast(EncodingScheme) ClassInfo.find(className).create(); - if (scheme is null) throw new EncodingException("Unable to create class "~className); - return scheme; - } - - const - { - /** - * Returns the standard name of the encoding scheme - */ - abstract override string toString(); - - /** - * Returns an array of all known names for this encoding scheme - */ - abstract string[] names(); - - /** - * Returns true if the character c can be represented - * in this encoding scheme. - */ - abstract bool canEncode(dchar c); - - /** - * Returns the number of ubytes required to encode this code point. - * - * The input to this function MUST be a valid code point. - * - * Params: - * c = the code point to be encoded - * - * Returns: - * the number of ubytes required. - */ - abstract size_t encodedLength(dchar c); - - /** - * Encodes a single code point into a user-supplied, fixed-size buffer. - * - * This function encodes a single code point into one or more ubytes. - * The supplied buffer must be code unit aligned. - * (For example, UTF-16LE or UTF-16BE must be wchar-aligned, - * UTF-32LE or UTF-32BE must be dchar-aligned, etc.) - * - * The input to this function MUST be a valid code point. - * - * Params: - * c = the code point to be encoded - * buffer = the destination array - * - * Returns: - * the number of ubytes written. - */ - abstract size_t encode(dchar c, ubyte[] buffer); - - /** - * Decodes a single code point. - * - * This function removes one or more ubytes from the start of an array, - * and returns the decoded code point which those ubytes represent. - * - * The input to this function MUST be validly encoded. - * - * Params: - * s = the array whose first code point is to be decoded - */ - abstract dchar decode(ref const(ubyte)[] s); - - /** - * Decodes a single code point. The input does not have to be valid. - * - * This function removes one or more ubytes from the start of an array, - * and returns the decoded code point which those ubytes represent. - * - * This function will accept an invalidly encoded array as input. - * If an invalid sequence is found at the start of the string, this - * function will remove it, and return the value INVALID_SEQUENCE. - * - * Params: - * s = the array whose first code point is to be decoded - */ - abstract dchar safeDecode(ref const(ubyte)[] s); - - /** - * Returns the sequence of ubytes to be used to represent - * any character which cannot be represented in the encoding scheme. - * - * Normally this will be a representation of some substitution - * character, such as U+FFFD or '?'. - */ - abstract @property immutable(ubyte)[] replacementSequence(); - } - - /** - * Returns true if the array is encoded correctly - * - * Params: - * s = the array to be tested - */ - bool isValid(const(ubyte)[] s) - { - while (s.length != 0) - { - if (safeDecode(s) == INVALID_SEQUENCE) - return false; - } - return true; - } - - /** - * Returns the length of the longest possible substring, starting from - * the first element, which is validly encoded. - * - * Params: - * s = the array to be tested - */ - size_t validLength()(const(ubyte)[] s) - { - const(ubyte)[] r = s; - const(ubyte)[] t = s; - while (s.length != 0) - { - if (safeDecode(s) == INVALID_SEQUENCE) break; - t = s; - } - return r.length - t.length; - } - - /** - * Sanitizes an array by replacing malformed ubyte sequences with valid - * ubyte sequences. The result is guaranteed to be valid for this - * encoding scheme. - * - * If the input array is already valid, this function returns the - * original, otherwise it constructs a new array by replacing all illegal - * sequences with the encoding scheme's replacement sequence. - * - * Params: - * s = the string to be sanitized - */ - immutable(ubyte)[] sanitize()(immutable(ubyte)[] s) - { - auto n = validLength(s); - if (n == s.length) return s; - - auto repSeq = replacementSequence; - - // Count how long the string needs to be. - // Overestimating is not a problem - auto len = s.length; - const(ubyte)[] t = s[n..$]; - while (t.length != 0) - { - immutable c = safeDecode(t); - assert(c == INVALID_SEQUENCE); - len += repSeq.length; - t = t[validLength(t)..$]; - } - - // Now do the write - ubyte[] array = new ubyte[len]; - array[0 .. n] = s[0 .. n]; - auto offset = n; - - t = s[n..$]; - while (t.length != 0) - { - immutable c = safeDecode(t); - assert(c == INVALID_SEQUENCE); - array[offset .. offset+repSeq.length] = repSeq[]; - offset += repSeq.length; - n = validLength(t); - array[offset .. offset+n] = t[0 .. n]; - offset += n; - t = t[n..$]; - } - return cast(immutable(ubyte)[])array[0 .. offset]; - } - - /** - * Returns the length of the first encoded sequence. - * - * The input to this function MUST be validly encoded. - * This is enforced by the function's in-contract. - * - * Params: - * s = the array to be sliced - */ - size_t firstSequence()(const(ubyte)[] s) - in - { - assert(s.length != 0); - const(ubyte)[] u = s; - assert(safeDecode(u) != INVALID_SEQUENCE); - } - do - { - const(ubyte)[] t = s; - decode(s); - return t.length - s.length; - } - - /** - * Returns the total number of code points encoded in a ubyte array. - * - * The input to this function MUST be validly encoded. - * This is enforced by the function's in-contract. - * - * Params: - * s = the string to be counted - */ - size_t count()(const(ubyte)[] s) - in - { - assert(isValid(s)); - } - do - { - size_t n = 0; - while (s.length != 0) - { - decode(s); - ++n; - } - return n; - } - - /** - * Returns the array index at which the (n+1)th code point begins. - * - * The input to this function MUST be validly encoded. - * This is enforced by the function's in-contract. - * - * Params: - * s = the string to be counted - * n = the current code point index - */ - ptrdiff_t index()(const(ubyte)[] s, size_t n) - in - { - assert(isValid(s)); - assert(n >= 0); - } - do - { - const(ubyte)[] t = s; - for (size_t i=0; i " ~ schemeNames[i]); - assert(valid[i] == decStr,"Error encode/decode UTF8 <=> " ~ schemeNames[i]); - - if (schemeNames[i] == "ISO-8859-1" || schemeNames[i] == "ISO-8859-2") - { - assert(scheme.safeDecode(invalid) != INVALID_SEQUENCE); - } - else - { - assert(scheme.safeDecode(invalid) == INVALID_SEQUENCE); - } - assert(scheme.replacementSequence() == cast(immutable(ubyte)[])"?"); - } - assert(invalid.length == 0); -} - -/** - EncodingScheme to handle UTF-8 - - This scheme recognises the following names: - "UTF-8" - */ -class EncodingSchemeUtf8 : EncodingScheme -{ - /* // moved to std.internal.phobosinit - shared static this() - { - EncodingScheme.register("std.encoding.EncodingSchemeUtf8"); - }*/ - - const - { - override string[] names() @safe pure nothrow - { - return - [ - "UTF-8" - ]; - } - - override string toString() @safe pure nothrow @nogc - { - return "UTF-8"; - } - - override bool canEncode(dchar c) @safe pure nothrow @nogc - { - return std.encoding.canEncode!(char)(c); - } - - override size_t encodedLength(dchar c) @safe pure nothrow @nogc - { - return std.encoding.encodedLength!(char)(c); - } - - override size_t encode(dchar c, ubyte[] buffer) @safe pure nothrow @nogc - { - auto r = cast(char[]) buffer; - return std.encoding.encode(c,r); - } - - override dchar decode(ref const(ubyte)[] s) @safe pure nothrow @nogc - { - auto t = cast(const(char)[]) s; - dchar c = std.encoding.decode(t); - s = s[$-t.length..$]; - return c; - } - - override dchar safeDecode(ref const(ubyte)[] s) @safe pure nothrow @nogc - { - auto t = cast(const(char)[]) s; - dchar c = std.encoding.safeDecode(t); - s = s[$-t.length..$]; - return c; - } - - override @property immutable(ubyte)[] replacementSequence() @safe pure nothrow @nogc - { - return cast(immutable(ubyte)[])"\uFFFD"; - } - } -} - -/** - EncodingScheme to handle UTF-16 in native byte order - - This scheme recognises the following names: - "UTF-16LE" (little-endian architecture only) - "UTF-16BE" (big-endian architecture only) - */ -class EncodingSchemeUtf16Native : EncodingScheme -{ - /* // moved to std.internal.phobosinit - shared static this() - { - EncodingScheme.register("std.encoding.EncodingSchemeUtf16Native"); - }*/ - - const - { - version (LittleEndian) { enum string NAME = "UTF-16LE"; } - version (BigEndian) { enum string NAME = "UTF-16BE"; } - - override string[] names() @safe pure nothrow - { - return [ NAME ]; - } - - override string toString() @safe pure nothrow @nogc - { - return NAME; - } - - override bool canEncode(dchar c) @safe pure nothrow @nogc - { - return std.encoding.canEncode!(wchar)(c); - } - - override size_t encodedLength(dchar c) @safe pure nothrow @nogc - { - return std.encoding.encodedLength!(wchar)(c); - } - - override size_t encode(dchar c, ubyte[] buffer) @safe pure nothrow @nogc - { - auto r = cast(wchar[]) buffer; - return wchar.sizeof * std.encoding.encode(c,r); - } - - override dchar decode(ref const(ubyte)[] s) @safe pure nothrow @nogc - in - { - assert((s.length & 1) == 0); - } - do - { - auto t = cast(const(wchar)[]) s; - dchar c = std.encoding.decode(t); - s = s[$-t.length * wchar.sizeof..$]; - return c; - } - - override dchar safeDecode(ref const(ubyte)[] s) @safe pure nothrow @nogc - in - { - assert((s.length & 1) == 0); - } - do - { - auto t = cast(const(wchar)[]) s; - dchar c = std.encoding.safeDecode(t); - s = s[$-t.length * wchar.sizeof..$]; - return c; - } - - override @property immutable(ubyte)[] replacementSequence() @safe pure nothrow @nogc - { - return cast(immutable(ubyte)[])"\uFFFD"w; - } - } -} -@system unittest -{ - version (LittleEndian) - { - auto efrom = EncodingScheme.create("utf-16le"); - ubyte[6] sample = [154,1, 155,1, 156,1]; - } - version (BigEndian) - { - auto efrom = EncodingScheme.create("utf-16be"); - ubyte[6] sample = [1,154, 1,155, 1,156]; - } - const(ubyte)[] ub = cast(const(ubyte)[])sample; - dchar dc = efrom.safeDecode(ub); - assert(dc == 410); - assert(ub.length == 4); -} - -/** - EncodingScheme to handle UTF-32 in native byte order - - This scheme recognises the following names: - "UTF-32LE" (little-endian architecture only) - "UTF-32BE" (big-endian architecture only) - */ -class EncodingSchemeUtf32Native : EncodingScheme -{ - /* // moved to std.internal.phobosinit - shared static this() - { - EncodingScheme.register("std.encoding.EncodingSchemeUtf32Native"); - }*/ - - const - { - version (LittleEndian) { enum string NAME = "UTF-32LE"; } - version (BigEndian) { enum string NAME = "UTF-32BE"; } - - override string[] names() @safe pure nothrow - { - return [ NAME ]; - } - - override string toString() @safe pure nothrow @nogc - { - return NAME; - } - - override bool canEncode(dchar c) @safe pure nothrow @nogc - { - return std.encoding.canEncode!(dchar)(c); - } - - override size_t encodedLength(dchar c) @safe pure nothrow @nogc - { - return std.encoding.encodedLength!(dchar)(c); - } - - override size_t encode(dchar c, ubyte[] buffer) @safe pure nothrow @nogc - { - auto r = cast(dchar[]) buffer; - return dchar.sizeof * std.encoding.encode(c,r); - } - - override dchar decode(ref const(ubyte)[] s) @safe pure nothrow @nogc - in - { - assert((s.length & 3) == 0); - } - do - { - auto t = cast(const(dchar)[]) s; - dchar c = std.encoding.decode(t); - s = s[$-t.length * dchar.sizeof..$]; - return c; - } - - override dchar safeDecode(ref const(ubyte)[] s) @safe pure nothrow @nogc - in - { - assert((s.length & 3) == 0); - } - do - { - auto t = cast(const(dchar)[]) s; - dchar c = std.encoding.safeDecode(t); - s = s[$-t.length * dchar.sizeof..$]; - return c; - } - - override @property immutable(ubyte)[] replacementSequence() @safe pure nothrow @nogc - { - return cast(immutable(ubyte)[])"\uFFFD"d; - } - } -} -@system unittest -{ - version (LittleEndian) - { - auto efrom = EncodingScheme.create("utf-32le"); - ubyte[12] sample = [154,1,0,0, 155,1,0,0, 156,1,0,0]; - } - version (BigEndian) - { - auto efrom = EncodingScheme.create("utf-32be"); - ubyte[12] sample = [0,0,1,154, 0,0,1,155, 0,0,1,156]; - } - const(ubyte)[] ub = cast(const(ubyte)[])sample; - dchar dc = efrom.safeDecode(ub); - assert(dc == 410); - assert(ub.length == 8); -} - -//============================================================================= - - -/** Definitions of common Byte Order Marks. -The elements of the `enum` can used as indices into `bomTable` to get -matching `BOMSeq`. -*/ -enum BOM -{ - none = 0, /// no BOM was found - utf32be = 1, /// [0x00, 0x00, 0xFE, 0xFF] - utf32le = 2, /// [0xFF, 0xFE, 0x00, 0x00] - utf7 = 3, /** [0x2B, 0x2F, 0x76, 0x38] - [0x2B, 0x2F, 0x76, 0x39], - [0x2B, 0x2F, 0x76, 0x2B], - [0x2B, 0x2F, 0x76, 0x2F], - [0x2B, 0x2F, 0x76, 0x38, 0x2D] - */ - utf1 = 8, /// [0xF7, 0x64, 0x4C] - utfebcdic = 9, /// [0xDD, 0x73, 0x66, 0x73] - scsu = 10, /// [0x0E, 0xFE, 0xFF] - bocu1 = 11, /// [0xFB, 0xEE, 0x28] - gb18030 = 12, /// [0x84, 0x31, 0x95, 0x33] - utf8 = 13, /// [0xEF, 0xBB, 0xBF] - utf16be = 14, /// [0xFE, 0xFF] - utf16le = 15 /// [0xFF, 0xFE] -} - -/// The type stored inside `bomTable`. -alias BOMSeq = Tuple!(BOM, "schema", ubyte[], "sequence"); - -/** Mapping of a byte sequence to $(B Byte Order Mark (BOM)) -*/ -immutable bomTable = [ - BOMSeq(BOM.none, null), - BOMSeq(BOM.utf32be, cast(ubyte[])([0x00, 0x00, 0xFE, 0xFF])), - BOMSeq(BOM.utf32le, cast(ubyte[])([0xFF, 0xFE, 0x00, 0x00])), - BOMSeq(BOM.utf7, cast(ubyte[])([0x2B, 0x2F, 0x76, 0x39])), - BOMSeq(BOM.utf7, cast(ubyte[])([0x2B, 0x2F, 0x76, 0x2B])), - BOMSeq(BOM.utf7, cast(ubyte[])([0x2B, 0x2F, 0x76, 0x2F])), - BOMSeq(BOM.utf7, cast(ubyte[])([0x2B, 0x2F, 0x76, 0x38, 0x2D])), - BOMSeq(BOM.utf7, cast(ubyte[])([0x2B, 0x2F, 0x76, 0x38])), - BOMSeq(BOM.utf1, cast(ubyte[])([0xF7, 0x64, 0x4C])), - BOMSeq(BOM.utfebcdic, cast(ubyte[])([0xDD, 0x73, 0x66, 0x73])), - BOMSeq(BOM.scsu, cast(ubyte[])([0x0E, 0xFE, 0xFF])), - BOMSeq(BOM.bocu1, cast(ubyte[])([0xFB, 0xEE, 0x28])), - BOMSeq(BOM.gb18030, cast(ubyte[])([0x84, 0x31, 0x95, 0x33])), - BOMSeq(BOM.utf8, cast(ubyte[])([0xEF, 0xBB, 0xBF])), - BOMSeq(BOM.utf16be, cast(ubyte[])([0xFE, 0xFF])), - BOMSeq(BOM.utf16le, cast(ubyte[])([0xFF, 0xFE])) -]; - -/** Returns a `BOMSeq` for a given `input`. -If no `BOM` is present the `BOMSeq` for `BOM.none` is -returned. The `BOM` sequence at the beginning of the range will -not be comsumed from the passed range. If you pass a reference type -range make sure that `save` creates a deep copy. - -Params: - input = The sequence to check for the `BOM` - -Returns: - the found `BOMSeq` corresponding to the passed `input`. -*/ -immutable(BOMSeq) getBOM(Range)(Range input) -if (isForwardRange!Range && is(immutable ElementType!Range == immutable ubyte)) -{ - import std.algorithm.searching : startsWith; - foreach (it; bomTable[1 .. $]) - { - if (startsWith(input.save, it.sequence)) - { - return it; - } - } - - return bomTable[0]; -} - -/// -@system unittest -{ - import std.format : format; - - auto ts = dchar(0x0000FEFF) ~ "Hello World"d; - - auto entry = getBOM(cast(ubyte[]) ts); - version (BigEndian) - { - assert(entry.schema == BOM.utf32be, format("%s", entry.schema)); - } - else - { - assert(entry.schema == BOM.utf32le, format("%s", entry.schema)); - } -} - -@system unittest -{ - import std.format : format; - - foreach (idx, it; bomTable) - { - auto s = it[1] ~ cast(ubyte[])"hello world"; - auto i = getBOM(s); - assert(i[0] == bomTable[idx][0]); - - if (idx < 4 || idx > 7) // get around the multiple utf7 bom's - { - assert(i[0] == BOM.init + idx); - assert(i[1] == it[1]); - } - } -} - -@safe pure unittest -{ - struct BOMInputRange - { - ubyte[] arr; - - @property ubyte front() - { - return this.arr.front; - } - - @property bool empty() - { - return this.arr.empty; - } - - void popFront() - { - this.arr = this.arr[1 .. $]; - } - - @property typeof(this) save() - { - return this; - } - } - - static assert( isInputRange!BOMInputRange); - static assert(!isArray!BOMInputRange); - - ubyte[] dummyEnd = [0,0,0,0]; - - foreach (idx, it; bomTable[1 .. $]) - { - { - auto ir = BOMInputRange(it.sequence.dup); - - auto b = getBOM(ir); - assert(b.schema == it.schema); - assert(ir.arr == it.sequence); - } - - { - auto noBom = it.sequence[0 .. 1].dup ~ dummyEnd; - size_t oldLen = noBom.length; - assert(oldLen - 4 < it.sequence.length); - - auto ir = BOMInputRange(noBom.dup); - auto b = getBOM(ir); - assert(b.schema == BOM.none); - assert(noBom.length == oldLen); - } - } -} - -/** Constant defining a fully decoded BOM */ -enum dchar utfBOM = 0xfeff; diff --git a/phobos/std/exception.d b/phobos/std/exception.d deleted file mode 100644 index 9b815da..0000000 --- a/phobos/std/exception.d +++ /dev/null @@ -1,2483 +0,0 @@ -// Written in the D programming language. - -/++ - This module defines functions related to exceptions and general error - handling. It also defines functions intended to aid in unit testing. - -$(SCRIPT inhibitQuickIndex = 1;) -$(DIVC quickindex, -$(BOOKTABLE, -$(TR $(TH Category) $(TH Functions)) -$(TR $(TD Assumptions) $(TD - $(LREF assertNotThrown) - $(LREF assertThrown) - $(LREF assumeUnique) - $(LREF assumeWontThrow) - $(LREF mayPointTo) -)) -$(TR $(TD Enforce) $(TD - $(LREF doesPointTo) - $(LREF enforce) - $(LREF errnoEnforce) -)) -$(TR $(TD Handlers) $(TD - $(LREF collectException) - $(LREF collectExceptionMsg) - $(LREF ifThrown) - $(LREF handle) -)) -$(TR $(TD Other) $(TD - $(LREF basicExceptionCtors) - $(LREF emptyExceptionMsg) - $(LREF ErrnoException) - $(LREF RangePrimitive) -)) -)) - - Copyright: Copyright Andrei Alexandrescu 2008-, Jonathan M Davis 2011-. - License: $(HTTP boost.org/LICENSE_1_0.txt, Boost License 1.0) - Authors: $(HTTP erdani.org, Andrei Alexandrescu) and - $(HTTP jmdavisprog.com, Jonathan M Davis) - Source: $(PHOBOSSRC std/exception.d) - - +/ -module std.exception; - -/// Synopis -@system unittest -{ - import core.stdc.stdlib : malloc, free; - import std.algorithm.comparison : equal; - import std.algorithm.iteration : map, splitter; - import std.algorithm.searching : endsWith; - import std.conv : ConvException, to; - import std.range : front, retro; - - // use enforce like assert - int a = 3; - enforce(a > 2, "a needs to be higher than 2."); - - // enforce can throw a custom exception - enforce!ConvException(a > 2, "a needs to be higher than 2."); - - // enforce will return it's input - enum size = 42; - auto memory = enforce(malloc(size), "malloc failed")[0 .. size]; - scope(exit) free(memory.ptr); - - // collectException can be used to test for exceptions - Exception e = collectException("abc".to!int); - assert(e.file.endsWith("conv.d")); - - // and just for the exception message - string msg = collectExceptionMsg("abc".to!int); - assert(msg == "Unexpected 'a' when converting from type string to type int"); - - // assertThrown can be used to assert that an exception is thrown - assertThrown!ConvException("abc".to!int); - - // ifThrown can be used to provide a default value if an exception is thrown - assert("x".to!int().ifThrown(0) == 0); - - // handle is a more advanced version of ifThrown for ranges - auto r = "12,1337z32,54".splitter(',').map!(a => to!int(a)); - auto h = r.handle!(ConvException, RangePrimitive.front, (e, r) => 0); - assert(h.equal([12, 0, 54])); - assertThrown!ConvException(h.retro.equal([54, 0, 12])); - - // basicExceptionCtors avoids the boilerplate when creating custom exceptions - static class MeaCulpa : Exception - { - mixin basicExceptionCtors; - } - e = collectException((){throw new MeaCulpa("diagnostic message");}()); - assert(e.msg == "diagnostic message"); - assert(e.file == __FILE__); - assert(e.line == __LINE__ - 3); - - // assumeWontThrow can be used to cast throwing code into `nothrow` - void exceptionFreeCode() nothrow - { - // auto-decoding only throws if an invalid UTF char is given - assumeWontThrow("abc".front); - } - - // assumeUnique can be used to cast mutable instance to an `immutable` one - // use with care - char[] str = " mutable".dup; - str[0 .. 2] = "im"; - immutable res = assumeUnique(str); - assert(res == "immutable"); -} - -import std.range.primitives; -import std.traits; - -/++ - Asserts that the given expression does $(I not) throw the given type - of `Throwable`. If a `Throwable` of the given type is thrown, - it is caught and does not escape assertNotThrown. Rather, an - `AssertError` is thrown. However, any other `Throwable`s will escape. - - Params: - T = The `Throwable` to test for. - expression = The expression to test. - msg = Optional message to output on test failure. - If msg is empty, and the thrown exception has a - non-empty msg field, the exception's msg field - will be output on test failure. - file = The file where the error occurred. - Defaults to `__FILE__`. - line = The line where the error occurred. - Defaults to `__LINE__`. - - Throws: - `AssertError` if the given `Throwable` is thrown. - - Returns: - the result of `expression`. - +/ -auto assertNotThrown(T : Throwable = Exception, E) - (lazy E expression, - string msg = null, - string file = __FILE__, - size_t line = __LINE__) -{ - import core.exception : AssertError; - try - { - return expression(); - } - catch (T t) - { - immutable message = msg.length == 0 ? t.msg : msg; - immutable tail = message.length == 0 ? "." : ": " ~ message; - throw new AssertError("assertNotThrown failed: " ~ T.stringof ~ " was thrown" ~ tail, file, line, t); - } -} -/// -@system unittest -{ - import core.exception : AssertError; - - import std.string; - assertNotThrown!StringException(enforce!StringException(true, "Error!")); - - //Exception is the default. - assertNotThrown(enforce!StringException(true, "Error!")); - - assert(collectExceptionMsg!AssertError(assertNotThrown!StringException( - enforce!StringException(false, "Error!"))) == - `assertNotThrown failed: StringException was thrown: Error!`); -} -@system unittest -{ - import core.exception : AssertError; - import std.string; - assert(collectExceptionMsg!AssertError(assertNotThrown!StringException( - enforce!StringException(false, ""), "Error!")) == - `assertNotThrown failed: StringException was thrown: Error!`); - - assert(collectExceptionMsg!AssertError(assertNotThrown!StringException( - enforce!StringException(false, ""))) == - `assertNotThrown failed: StringException was thrown.`); - - assert(collectExceptionMsg!AssertError(assertNotThrown!StringException( - enforce!StringException(false, ""), "")) == - `assertNotThrown failed: StringException was thrown.`); -} - -@system unittest -{ - import core.exception : AssertError; - - static noreturn throwEx(Throwable t) { throw t; } - bool nothrowEx() { return true; } - - try - { - assert(assertNotThrown!Exception(nothrowEx())); - } - catch (AssertError) assert(0); - - try - { - assert(assertNotThrown!Exception(nothrowEx(), "It's a message")); - } - catch (AssertError) assert(0); - - try - { - assert(assertNotThrown!AssertError(nothrowEx())); - } - catch (AssertError) assert(0); - - try - { - assert(assertNotThrown!AssertError(nothrowEx(), "It's a message")); - } - catch (AssertError) assert(0); - - { - bool thrown = false; - try - { - assertNotThrown!Exception( - throwEx(new Exception("It's an Exception"))); - } - catch (AssertError) thrown = true; - assert(thrown); - } - - { - bool thrown = false; - try - { - assertNotThrown!Exception( - throwEx(new Exception("It's an Exception")), "It's a message"); - } - catch (AssertError) thrown = true; - assert(thrown); - } - - { - bool thrown = false; - try - { - assertNotThrown!AssertError( - throwEx(new AssertError("It's an AssertError", __FILE__, __LINE__))); - } - catch (AssertError) thrown = true; - assert(thrown); - } - - { - bool thrown = false; - try - { - assertNotThrown!AssertError( - throwEx(new AssertError("It's an AssertError", __FILE__, __LINE__)), - "It's a message"); - } - catch (AssertError) thrown = true; - assert(thrown); - } -} - -/++ - Asserts that the given expression throws the given type of `Throwable`. - The `Throwable` is caught and does not escape assertThrown. However, - any other `Throwable`s $(I will) escape, and if no `Throwable` - of the given type is thrown, then an `AssertError` is thrown. - - Params: - T = The `Throwable` to test for. - expression = The expression to test. - msg = Optional message to output on test failure. - file = The file where the error occurred. - Defaults to `__FILE__`. - line = The line where the error occurred. - Defaults to `__LINE__`. - - Throws: - `AssertError` if the given `Throwable` is not thrown. - +/ -void assertThrown(T : Throwable = Exception, E) - (lazy E expression, - string msg = null, - string file = __FILE__, - size_t line = __LINE__) -{ - import core.exception : AssertError; - - try - expression(); - catch (T) - return; - - static if (!is(immutable E == immutable noreturn)) - throw new AssertError("assertThrown failed: No " ~ T.stringof ~ " was thrown" - ~ (msg.length == 0 ? "." : ": ") ~ msg, - file, line); -} -/// -@system unittest -{ - import core.exception : AssertError; - import std.string; - - assertThrown!StringException(enforce!StringException(false, "Error!")); - - //Exception is the default. - assertThrown(enforce!StringException(false, "Error!")); - - assert(collectExceptionMsg!AssertError(assertThrown!StringException( - enforce!StringException(true, "Error!"))) == - `assertThrown failed: No StringException was thrown.`); -} - -@system unittest -{ - import core.exception : AssertError; - - static noreturn throwEx(Throwable t) { throw t; } - void nothrowEx() { } - - try - { - assertThrown!Exception(throwEx(new Exception("It's an Exception"))); - } - catch (AssertError) assert(0); - - try - { - assertThrown!Exception(throwEx(new Exception("It's an Exception")), - "It's a message"); - } - catch (AssertError) assert(0); - - try - { - assertThrown!AssertError(throwEx(new AssertError("It's an AssertError", - __FILE__, __LINE__))); - } - catch (AssertError) assert(0); - - try - { - assertThrown!AssertError(throwEx(new AssertError("It's an AssertError", - __FILE__, __LINE__)), - "It's a message"); - } - catch (AssertError) assert(0); - - - { - bool thrown = false; - try - assertThrown!Exception(nothrowEx()); - catch (AssertError) - thrown = true; - - assert(thrown); - } - - { - bool thrown = false; - try - assertThrown!Exception(nothrowEx(), "It's a message"); - catch (AssertError) - thrown = true; - - assert(thrown); - } - - { - bool thrown = false; - try - assertThrown!AssertError(nothrowEx()); - catch (AssertError) - thrown = true; - - assert(thrown); - } - - { - bool thrown = false; - try - assertThrown!AssertError(nothrowEx(), "It's a message"); - catch (AssertError) - thrown = true; - - assert(thrown); - } -} - - -/++ - Enforces that the given value is true. - If the given value is false, an exception is thrown. - The - $(UL - $(LI `msg` - error message as a `string`) - $(LI `dg` - custom delegate that return a string and is only called if an exception occurred) - $(LI `ex` - custom exception to be thrown. It is `lazy` and is only created if an exception occurred) - ) - - Params: - value = The value to test. - E = Exception type to throw if the value evaluates to false. - msg = The error message to put in the exception if it is thrown. - dg = The delegate to be called if the value evaluates to false. - ex = The exception to throw if the value evaluates to false. - file = The source file of the caller. - line = The line number of the caller. - - Returns: `value`, if `cast(bool) value` is true. Otherwise, - depending on the chosen overload, `new Exception(msg)`, `dg()` or `ex` is thrown. - - $(PANEL - $(NOTE `enforce` is used to throw exceptions and is therefore intended to - aid in error handling. It is $(I not) intended for verifying the logic - of your program - that is what `assert` is for.) - - Do not use - `enforce` inside of contracts (i.e. inside of `in` and `out` - blocks and `invariant`s), because contracts are compiled out when - compiling with $(I -release). - ) - - If a delegate is passed, the safety and purity of this function are inferred - from `Dg`'s safety and purity. - +/ -template enforce(E : Throwable = Exception) -if (is(typeof(new E("", string.init, size_t.init)) : Throwable) || - is(typeof(new E(string.init, size_t.init)) : Throwable)) -{ - /// - T enforce(T)(T value, lazy const(char)[] msg = null, - string file = __FILE__, size_t line = __LINE__) - if (is(typeof({ if (!value) {} }))) - { - if (!value) bailOut!E(file, line, msg); - return value; - } -} - -/// ditto -pragma(inline, true) // LDC: Must inline because of __FILE__ as template parameter -T enforce(T, Dg, string file = __FILE__, size_t line = __LINE__) - (T value, scope Dg dg) -if (isSomeFunction!Dg && is(typeof( dg() )) && - is(typeof({ if (!value) {} }))) -{ - if (!value) dg(); - return value; -} - -/// ditto -T enforce(T)(T value, lazy Throwable ex) -{ - if (!value) throw ex(); - return value; -} - -/// -@system unittest -{ - import core.stdc.stdlib : malloc, free; - import std.conv : ConvException, to; - - // use enforce like assert - int a = 3; - enforce(a > 2, "a needs to be higher than 2."); - - // enforce can throw a custom exception - enforce!ConvException(a > 2, "a needs to be higher than 2."); - - // enforce will return it's input - enum size = 42; - auto memory = enforce(malloc(size), "malloc failed")[0 .. size]; - scope(exit) free(memory.ptr); -} - -/// -@safe unittest -{ - assertNotThrown(enforce(true, new Exception("this should not be thrown"))); - assertThrown(enforce(false, new Exception("this should be thrown"))); -} - -/// -@safe unittest -{ - assert(enforce(123) == 123); - - try - { - enforce(false, "error"); - assert(false); - } - catch (Exception e) - { - assert(e.msg == "error"); - assert(e.file == __FILE__); - assert(e.line == __LINE__-7); - } -} - -/// Alias your own enforce function -@safe unittest -{ - import std.conv : ConvException; - alias convEnforce = enforce!ConvException; - assertNotThrown(convEnforce(true)); - assertThrown!ConvException(convEnforce(false, "blah")); -} - -private noreturn bailOut(E : Throwable = Exception)(string file, size_t line, scope const(char)[] msg) -{ - static if (is(typeof(new E(string.init, string.init, size_t.init)))) - { - throw new E(msg ? msg.idup : "Enforcement failed", file, line); - } - else static if (is(typeof(new E(string.init, size_t.init)))) - { - throw new E(file, line); - } - else - { - static assert(0, "Expected this(string, string, size_t) or this(string, size_t)" ~ - " constructor for " ~ __traits(identifier, E)); - } -} - -// https://issues.dlang.org/show_bug.cgi?id=10510 -@safe unittest -{ - extern(C) void cFoo() { } - enforce(false, &cFoo); -} - -// purity and safety inference test -@system unittest -{ - static foreach (EncloseSafe; [false, true]) - static foreach (EnclosePure; [false, true]) - { - static foreach (BodySafe; [false, true]) - static foreach (BodyPure; [false, true]) - {{ - enum code = - "delegate void() " ~ - (EncloseSafe ? "@safe " : "") ~ - (EnclosePure ? "pure " : "") ~ - "{ enforce(true, { " ~ - "int n; " ~ - (BodySafe ? "" : "auto p = &n + 10; " ) ~ // unsafe code - (BodyPure ? "" : "static int g; g = 10; ") ~ // impure code - "}); " ~ - "}"; - enum expect = - (BodySafe || !EncloseSafe) && (!EnclosePure || BodyPure); - - version (none) - pragma(msg, "safe = ", EncloseSafe?1:0, "/", BodySafe?1:0, ", ", - "pure = ", EnclosePure?1:0, "/", BodyPure?1:0, ", ", - "expect = ", expect?"OK":"NG", ", ", - "code = ", code); - - static assert(__traits(compiles, mixin(code)()) == expect); - }} - } -} - -// Test for https://issues.dlang.org/show_bug.cgi?id=8637 -@system unittest -{ - struct S - { - static int g; - ~this() {} // impure & unsafe destructor - bool opCast(T:bool)() { - int* p = cast(int*) 0; // unsafe operation - int n = g; // impure operation - return true; - } - } - S s; - - enforce(s); - enforce(s, {}); - enforce(s, new Exception("")); - - errnoEnforce(s); - - alias E1 = Exception; - static class E2 : Exception - { - this(string fn, size_t ln) { super("", fn, ln); } - } - static class E3 : Exception - { - this(string msg) { super(msg, __FILE__, __LINE__); } - } - enforce!E1(s); - enforce!E2(s); -} - -// https://issues.dlang.org/show_bug.cgi?id=14685 -@safe unittest -{ - class E : Exception - { - this() { super("Not found"); } - } - static assert(!__traits(compiles, { enforce!E(false); })); -} - -/++ - Enforces that the given value is true, throwing an `ErrnoException` if it - is not. - - Params: - value = The value to test. - msg = The message to include in the `ErrnoException` if it is thrown. - - Returns: `value`, if `cast(bool) value` is true. Otherwise, - $(D new ErrnoException(msg)) is thrown. It is assumed that the last - operation set `errno` to an error code corresponding with the failed - condition. - +/ -alias errnoEnforce = enforce!ErrnoException; - -/// -@system unittest -{ - import core.stdc.stdio : fclose, fgets, fopen; - import std.file : thisExePath; - import std.string : toStringz; - - auto f = fopen(thisExePath.toStringz, "r").errnoEnforce; - scope(exit) fclose(f); - char[100] buf; - auto line = fgets(buf.ptr, buf.length, f); - enforce(line !is null); // expect a non-empty line -} - -/++ - Catches and returns the exception thrown from the given expression. - If no exception is thrown, then null is returned and `result` is - set to the result of the expression. - - Note that while `collectException` $(I can) be used to collect any - `Throwable` and not just `Exception`s, it is generally ill-advised to - catch anything that is neither an `Exception` nor a type derived from - `Exception`. So, do not use `collectException` to collect - non-`Exception`s unless you're sure that that's what you really want to - do. - - Params: - T = The type of exception to catch. - expression = The expression which may throw an exception. - result = The result of the expression if no exception is thrown. -+/ -T collectException(T = Exception, E)(lazy E expression, ref E result) -{ - try - { - result = expression(); - } - catch (T e) - { - return e; - } - // Avoid "statement not reachable" warning - static if (!is(immutable E == immutable noreturn)) - return null; -} -/// -@system unittest -{ - int b; - int foo() { throw new Exception("blah"); } - assert(collectException(foo(), b)); - - version (D_NoBoundsChecks) {} - else - { - // check for out of bounds error - int[] a = new int[3]; - import core.exception : RangeError; - assert(collectException!RangeError(a[4], b)); - } -} - -/++ - Catches and returns the exception thrown from the given expression. - If no exception is thrown, then null is returned. `E` can be - `void`. - - Note that while `collectException` $(I can) be used to collect any - `Throwable` and not just `Exception`s, it is generally ill-advised to - catch anything that is neither an `Exception` nor a type derived from - `Exception`. So, do not use `collectException` to collect - non-`Exception`s unless you're sure that that's what you really want to - do. - - Params: - T = The type of exception to catch. - expression = The expression which may throw an exception. -+/ -T collectException(T : Throwable = Exception, E)(lazy E expression) -{ - try - { - expression(); - } - catch (T t) - { - return t; - } - // Avoid "statement not reachable" warning - static if (!is(immutable E == immutable noreturn)) - return null; -} - -/// -@safe unittest -{ - int foo() { throw new Exception("blah"); } - assert(collectException(foo()).msg == "blah"); -} - -/++ - Catches the exception thrown from the given expression and returns the - msg property of that exception. If no exception is thrown, then null is - returned. `E` can be `void`. - - If an exception is thrown but it has an empty message, then - `emptyExceptionMsg` is returned. - - Note that while `collectExceptionMsg` $(I can) be used to collect any - `Throwable` and not just `Exception`s, it is generally ill-advised to - catch anything that is neither an `Exception` nor a type derived from - `Exception`. So, do not use `collectExceptionMsg` to collect - non-`Exception`s unless you're sure that that's what you really want to - do. - - Params: - T = The type of exception to catch. - expression = The expression which may throw an exception. -+/ -string collectExceptionMsg(T = Exception, E)(lazy E expression) -{ - import std.array : empty; - try - { - expression(); - - // Avoid "statement not reachable" warning - static if (!is(immutable E == immutable noreturn)) - return cast(string) null; - } - catch (T e) - return e.msg.empty ? emptyExceptionMsg : e.msg; -} -/// -@safe unittest -{ - void throwFunc() { throw new Exception("My Message."); } - assert(collectExceptionMsg(throwFunc()) == "My Message."); - - void nothrowFunc() {} - assert(collectExceptionMsg(nothrowFunc()) is null); - - void throwEmptyFunc() { throw new Exception(""); } - assert(collectExceptionMsg(throwEmptyFunc()) == emptyExceptionMsg); -} - -/++ - Value that collectExceptionMsg returns when it catches an exception - with an empty exception message. - +/ -enum emptyExceptionMsg = ""; - -// https://issues.dlang.org/show_bug.cgi?id=22364 -@system unittest -{ - static noreturn foo() { throw new Exception(""); } - - const ex = collectException!(Exception, noreturn)(foo()); - assert(ex); - - const msg = collectExceptionMsg!(Exception, noreturn)(foo()); - assert(msg); - - noreturn n; - - // Triggers a backend assertion failure - // collectException!(Exception, noreturn)(foo(), n); - - static assert(__traits(compiles, collectException!(Exception, noreturn)(foo(), n))); -} - -/** -Casts a mutable array to an immutable array in an idiomatic -manner. Technically, `assumeUnique` just inserts a cast, -but its name documents assumptions on the part of the -caller. `assumeUnique(arr)` should only be called when -there are no more active mutable aliases to elements of $(D -arr). To strengthen this assumption, `assumeUnique(arr)` -also clears `arr` before returning. Essentially $(D -assumeUnique(arr)) indicates commitment from the caller that there -is no more mutable access to any of `arr`'s elements -(transitively), and that all future accesses will be done through -the immutable array returned by `assumeUnique`. - -Typically, `assumeUnique` is used to return arrays from -functions that have allocated and built them. - -Params: - array = The array to cast to immutable. - -Returns: The immutable array. - -Example: - -$(RUNNABLE_EXAMPLE ----- -string letters() -{ - char[] result = new char['z' - 'a' + 1]; - foreach (i, ref e; result) - { - e = cast(char)('a' + i); - } - return assumeUnique(result); -} ----- -) - -The use in the example above is correct because `result` -was private to `letters` and the memory it referenced can no longer be written to -after the function returns. The following example shows an -incorrect use of `assumeUnique`. - -Bad: - -$(RUNNABLE_EXAMPLE ----- -char[] buffer; -string letters(char first, char last) -{ - if (first >= last) return null; // fine - auto sneaky = buffer; - sneaky.length = last - first + 1; - foreach (i, ref e; sneaky) - { - e = cast(char)('a' + i); - } - return assumeUnique(sneaky); // BAD -} ----- -) - -The example above wreaks havoc on client code because it modifies the -returned array that the previous caller considered immutable. To obtain an -immutable array from the writable array `buffer`, replace -the last line with: - ----- -return to!(string)(sneaky); // not that sneaky anymore ----- - -The `to` call will duplicate the array appropriately. - -$(PANEL -$(NOTE Checking for uniqueness during compilation is -possible in certain cases, especially when a function is -marked (or inferred) as `pure`. The following example does not -need to call `assumeUnique` because the compiler can infer the -uniqueness of the array in the pure function:) - -$(RUNNABLE_EXAMPLE ----- -static string letters() pure -{ - char[] result = new char['z' - 'a' + 1]; - foreach (i, ref e; result) - { - e = cast(char)('a' + i); - } - return result; -} ----- -) - -For more on infering uniqueness see the $(B unique) and -$(B lent) keywords in the -$(HTTP www.cs.cmu.edu/~aldrich/papers/aldrich-dissertation.pdf, ArchJava) -language. -) - -The downside of using `assumeUnique`'s -convention-based usage is that at this time there is no -formal checking of the correctness of the assumption; -on the upside, the idiomatic use of `assumeUnique` is -simple and rare enough to be tolerable. - */ -immutable(T)[] assumeUnique(T)(T[] array) pure nothrow -{ - return .assumeUnique(array); // call ref version -} -/// ditto -immutable(T)[] assumeUnique(T)(ref T[] array) pure nothrow -{ - auto result = cast(immutable(T)[]) array; - array = null; - return result; -} -/// ditto -immutable(T[U]) assumeUnique(T, U)(ref T[U] array) pure nothrow -{ - auto result = cast(immutable(T[U])) array; - array = null; - return result; -} - -/// -@system unittest -{ - int[] arr = new int[1]; - auto arr1 = arr.assumeUnique; - static assert(is(typeof(arr1) == immutable(int)[])); - assert(arr == null); - assert(arr1 == [0]); -} - -/// -@system unittest -{ - int[string] arr = ["a":1]; - auto arr1 = arr.assumeUnique; - static assert(is(typeof(arr1) == immutable(int[string]))); - assert(arr == null); - assert(arr1.keys == ["a"]); -} - -/** - * Wraps a possibly-throwing expression in a `nothrow` wrapper so that it - * can be called by a `nothrow` function. - * - * This wrapper function documents commitment on the part of the caller that - * the appropriate steps have been taken to avoid whatever conditions may - * trigger an exception during the evaluation of `expr`. If it turns out - * that the expression $(I does) throw at runtime, the wrapper will throw an - * `AssertError`. - * - * (Note that `Throwable` objects such as `AssertError` that do not - * subclass `Exception` may be thrown even from `nothrow` functions, - * since they are considered to be serious runtime problems that cannot be - * recovered from.) - * - * Params: - * expr = The expression asserted not to throw. - * msg = The message to include in the `AssertError` if the assumption turns - * out to be false. - * file = The source file name of the caller. - * line = The line number of the caller. - * - * Returns: - * The value of `expr`, if any. - */ -T assumeWontThrow(T)(lazy T expr, - string msg = null, - string file = __FILE__, - size_t line = __LINE__) nothrow -{ - import core.exception : AssertError; - try - { - return expr; - } - catch (Exception e) - { - import std.range.primitives : empty; - immutable tail = msg.empty ? "." : ": " ~ msg; - throw new AssertError("assumeWontThrow failed: Expression did throw" ~ - tail, file, line); - } -} - -/// -@safe unittest -{ - import std.math.algebraic : sqrt; - - // This function may throw. - int squareRoot(int x) - { - if (x < 0) - throw new Exception("Tried to take root of negative number"); - return cast(int) sqrt(cast(double) x); - } - - // This function never throws. - int computeLength(int x, int y) nothrow - { - // Since x*x + y*y is always positive, we can safely assume squareRoot - // won't throw, and use it to implement this nothrow function. If it - // does throw (e.g., if x*x + y*y overflows a 32-bit value), then the - // program will terminate. - return assumeWontThrow(squareRoot(x*x + y*y)); - } - - assert(computeLength(3, 4) == 5); -} - -@system unittest -{ - import core.exception : AssertError; - - void alwaysThrows() - { - throw new Exception("I threw up"); - } - void bad() nothrow - { - assumeWontThrow(alwaysThrows()); - } - assertThrown!AssertError(bad()); -} - -/** -Checks whether a given source object contains pointers or references to a given -target object. - -Params: - source = The source object - target = The target object - -Bugs: - The function is explicitly annotated `@nogc` because inference could fail, - see $(LINK2 https://issues.dlang.org/show_bug.cgi?id=17084, Bugzilla issue 17084). - -Returns: `true` if `source`'s representation embeds a pointer -that points to `target`'s representation or somewhere inside -it. - -If `source` is or contains a dynamic array, then, then these functions will check -if there is overlap between the dynamic array and `target`'s representation. - -If `source` is a class, then it will be handled as a pointer. - -If `target` is a pointer, a dynamic array or a class, then these functions will only -check if `source` points to `target`, $(I not) what `target` references. - -If `source` is or contains a union or `void[n]`, then there may be either false positives or -false negatives: - -`doesPointTo` will return `true` if it is absolutely certain -`source` points to `target`. It may produce false negatives, but never -false positives. This function should be prefered when trying to validate -input data. - -`mayPointTo` will return `false` if it is absolutely certain -`source` does not point to `target`. It may produce false positives, but never -false negatives. This function should be prefered for defensively choosing a -code path. - -Note: Evaluating $(D doesPointTo(x, x)) checks whether `x` has -internal pointers. This should only be done as an assertive test, -as the language is free to assume objects don't have internal pointers -(TDPL 7.1.3.5). -*/ -bool doesPointTo(S, T, Tdummy=void)(auto ref const S source, ref const T target) @nogc @trusted pure nothrow -if (__traits(isRef, source) || isDynamicArray!S || - is(S == U*, U) || is(S == class)) -{ - static if (is(S == U*, U) || is(S == class) || is(S == interface)) - { - const m = *cast(void**) &source; - const b = cast(void*) ⌖ - const e = b + target.sizeof; - return b <= m && m < e; - } - else static if (is(S == struct) || is(S == union)) - { - foreach (i, Subobj; typeof(source.tupleof)) - static if (!isUnionAliased!(S, i)) - if (doesPointTo(source.tupleof[i], target)) return true; - return false; - } - else static if (isStaticArray!S) - { - static if (!is(S == void[n], size_t n)) - { - foreach (ref s; source) - if (doesPointTo(s, target)) return true; - } - return false; - } - else static if (isDynamicArray!S) - { - import std.array : overlap; - return overlap(cast(void[]) source, cast(void[])(&target)[0 .. 1]).length != 0; - } - else - { - return false; - } -} - -// for shared objects -/// ditto -bool doesPointTo(S, T)(auto ref const shared S source, ref const shared T target) @trusted pure nothrow -{ - return doesPointTo!(shared S, shared T, void)(source, target); -} - -/// ditto -bool mayPointTo(S, T, Tdummy=void)(auto ref const S source, ref const T target) @trusted pure nothrow -if (__traits(isRef, source) || isDynamicArray!S || - is(S == U*, U) || is(S == class)) -{ - static if (is(S == U*, U) || is(S == class) || is(S == interface)) - { - const m = *cast(void**) &source; - const b = cast(void*) ⌖ - const e = b + target.sizeof; - return b <= m && m < e; - } - else static if (is(S == struct) || is(S == union)) - { - foreach (i, Subobj; typeof(source.tupleof)) - if (mayPointTo(source.tupleof[i], target)) return true; - return false; - } - else static if (isStaticArray!S) - { - static if (is(S == void[n], size_t n)) - { - static if (n >= (void[]).sizeof) - { - // could contain a slice, which could point at anything. - // But a void[N] that is all 0 cannot point anywhere - import std.algorithm.searching : any; - if (__ctfe || any(cast(ubyte[]) source[])) - return true; - } - else static if (n >= (void*).sizeof) - { - // Reinterpreting cast is impossible during ctfe - if (__ctfe) - return true; - - // Only check for properly aligned pointers - enum al = (void*).alignof - 1; - const base = cast(size_t) &source; - const alBase = (base + al) & ~al; - - if ((n - (alBase - base)) >= (void*).sizeof && - mayPointTo(*(cast(void**) alBase), target)) - return true; - } - } - else - { - foreach (size_t i; 0 .. S.length) - if (mayPointTo(source[i], target)) return true; - } - - return false; - } - else static if (isDynamicArray!S) - { - import std.array : overlap; - return overlap(cast(void[]) source, cast(void[])(&target)[0 .. 1]).length != 0; - } - else - { - return false; - } -} - -// for shared objects -/// ditto -bool mayPointTo(S, T)(auto ref const shared S source, ref const shared T target) @trusted pure nothrow -{ - return mayPointTo!(shared S, shared T, void)(source, target); -} - -/// Pointers -@system unittest -{ - int i = 0; - int* p = null; - assert(!p.doesPointTo(i)); - p = &i; - assert( p.doesPointTo(i)); -} - -/// Structs and Unions -@system unittest -{ - struct S - { - int v; - int* p; - } - int i; - auto s = S(0, &i); - - // structs and unions "own" their members - // pointsTo will answer true if one of the members pointsTo. - assert(!s.doesPointTo(s.v)); //s.v is just v member of s, so not pointed. - assert( s.p.doesPointTo(i)); //i is pointed by s.p. - assert( s .doesPointTo(i)); //which means i is pointed by s itself. - - // Unions will behave exactly the same. Points to will check each "member" - // individually, even if they share the same memory -} - -/// Arrays (dynamic and static) -@system unittest -{ - int i; - // trick the compiler when initializing slice - // https://issues.dlang.org/show_bug.cgi?id=18637 - int* p = &i; - int[] slice = [0, 1, 2, 3, 4]; - int[5] arr = [0, 1, 2, 3, 4]; - int*[] slicep = [p]; - int*[1] arrp = [&i]; - - // A slice points to all of its members: - assert( slice.doesPointTo(slice[3])); - assert(!slice[0 .. 2].doesPointTo(slice[3])); // Object 3 is outside of the - // slice [0 .. 2] - - // Note that a slice will not take into account what its members point to. - assert( slicep[0].doesPointTo(i)); - assert(!slicep .doesPointTo(i)); - - // static arrays are objects that own their members, just like structs: - assert(!arr.doesPointTo(arr[0])); // arr[0] is just a member of arr, so not - // pointed. - assert( arrp[0].doesPointTo(i)); // i is pointed by arrp[0]. - assert( arrp .doesPointTo(i)); // which means i is pointed by arrp - // itself. - - // Notice the difference between static and dynamic arrays: - assert(!arr .doesPointTo(arr[0])); - assert( arr[].doesPointTo(arr[0])); - assert( arrp .doesPointTo(i)); - assert(!arrp[].doesPointTo(i)); -} - -/// Classes -@system unittest -{ - class C - { - this(int* p){this.p = p;} - int* p; - } - int i; - C a = new C(&i); - C b = a; - - // Classes are a bit particular, as they are treated like simple pointers - // to a class payload. - assert( a.p.doesPointTo(i)); // a.p points to i. - assert(!a .doesPointTo(i)); // Yet a itself does not point i. - - //To check the class payload itself, iterate on its members: - () - { - import std.traits : Fields; - - foreach (index, _; Fields!C) - if (doesPointTo(a.tupleof[index], i)) - return; - assert(0); - }(); - - // To check if a class points a specific payload, a direct memmory check - // can be done: - auto aLoc = cast(ubyte[__traits(classInstanceSize, C)]*) a; - assert(b.doesPointTo(*aLoc)); // b points to where a is pointing -} - - -version (StdUnittest) -{ - // https://issues.dlang.org/show_bug.cgi?id=17084 - // the bug doesn't happen if these declarations are in the unittest block - // (static or not). - private struct Page17084 - { - URL17084 url; - int opCmp(P)(P) { return 0; } - int opCmp(P)(shared(P)) shared { return 0; } - } - - private struct URL17084 - { - int[] queryParams; - string toString()() const { return ""; } - alias toString this; - } -} - -// https://issues.dlang.org/show_bug.cgi?id=17084 -@system unittest -{ - import std.algorithm.sorting : sort; - Page17084[] s; - sort(s); - shared(Page17084)[] p; - sort(p); -} - -@system unittest -{ - struct S1 { int a; S1 * b; } - S1 a1; - S1 * p = &a1; - assert(doesPointTo(p, a1)); - - S1 a2; - a2.b = &a1; - assert(doesPointTo(a2, a1)); - - struct S3 { int[10] a; } - S3 a3; - auto a4 = a3.a[2 .. 3]; - assert(doesPointTo(a4, a3)); - - auto a5 = new double[4]; - auto a6 = a5[1 .. 2]; - assert(!doesPointTo(a5, a6)); - - auto a7 = new double[3]; - auto a8 = new double[][1]; - a8[0] = a7; - assert(!doesPointTo(a8[0], a8[0])); - - // don't invoke postblit on subobjects - { - static struct NoCopy { this(this) { assert(0); } } - static struct Holder { NoCopy a, b, c; } - Holder h; - cast(void) doesPointTo(h, h); - } - - shared S3 sh3; - shared sh3sub = sh3.a[]; - assert(doesPointTo(sh3sub, sh3)); - - int[] darr = [1, 2, 3, 4]; - - //dynamic arrays don't point to each other, or slices of themselves - assert(!doesPointTo(darr, darr)); - assert(!doesPointTo(darr[0 .. 1], darr)); - - //But they do point their elements - foreach (i; 0 .. 4) - assert(doesPointTo(darr, darr[i])); - assert(doesPointTo(darr[0 .. 3], darr[2])); - assert(!doesPointTo(darr[0 .. 3], darr[3])); -} - -@system unittest -{ - //tests with static arrays - //Static arrays themselves are just objects, and don't really *point* to anything. - //They aggregate their contents, much the same way a structure aggregates its attributes. - //*However* The elements inside the static array may themselves point to stuff. - - //Standard array - int[2] k; - assert(!doesPointTo(k, k)); //an array doesn't point to itself - //Technically, k doesn't point its elements, although it does alias them - assert(!doesPointTo(k, k[0])); - assert(!doesPointTo(k, k[1])); - //But an extracted slice will point to the same array. - assert(doesPointTo(k[], k)); - assert(doesPointTo(k[], k[1])); - - //An array of pointers - int*[2] pp; - int a; - int b; - pp[0] = &a; - assert( doesPointTo(pp, a)); //The array contains a pointer to a - assert(!doesPointTo(pp, b)); //The array does NOT contain a pointer to b - assert(!doesPointTo(pp, pp)); //The array does not point itslef - - //A struct containing a static array of pointers - static struct S - { - int*[2] p; - } - S s; - s.p[0] = &a; - assert( doesPointTo(s, a)); //The struct contains an array that points a - assert(!doesPointTo(s, b)); //But doesn't point b - assert(!doesPointTo(s, s)); //The struct doesn't actually point itslef. - - //An array containing structs that have pointers - static struct SS - { - int* p; - } - SS[2] ss = [SS(&a), SS(null)]; - assert( doesPointTo(ss, a)); //The array contains a struct that points to a - assert(!doesPointTo(ss, b)); //The array doesn't contains a struct that points to b - assert(!doesPointTo(ss, ss)); //The array doesn't point itself. - - // https://issues.dlang.org/show_bug.cgi?id=20426 - align((void*).alignof) void[32] voidArr = void; - (cast(void*[]) voidArr[])[] = null; // Ensure no false pointers - - // zeroed void ranges can't point at anything - assert(!mayPointTo(voidArr, a)); - assert(!mayPointTo(voidArr, b)); - - *cast(void**) &voidArr[16] = &a; // Pointers should be found - - alias SA = void[size_t.sizeof + 3]; - SA *smallArr1 = cast(SA*)&voidArr; - SA *smallArr2 = cast(SA*)&(voidArr[16]); - - // But it should only consider properly aligned pointers - // Write single bytes to avoid issues due to misaligned writes - void*[1] tmp = [&b]; - (cast(ubyte[]) voidArr[3 .. 3 + (void*).sizeof])[] = cast(ubyte[]) tmp[]; - - - assert( mayPointTo(*smallArr2, a)); - assert(!mayPointTo(*smallArr1, b)); - - assert(!doesPointTo(voidArr, a)); // Value might be a false pointer - assert(!doesPointTo(voidArr, b)); - - SA *smallArr3 = cast(SA *) &voidArr[13]; // Works for weird sizes/alignments - assert( mayPointTo(*smallArr3, a)); - assert(!mayPointTo(*smallArr3, b)); - - assert(!doesPointTo(*smallArr3, a)); - assert(!doesPointTo(*smallArr3, b)); - - auto v3 = cast(void[3]*) &voidArr[16]; // Arrays smaller than pointers are ignored - assert(!mayPointTo(*v3, a)); - assert(!mayPointTo(*v3, b)); - - assert(!doesPointTo(*v3, a)); - assert(!doesPointTo(*v3, b)); - - assert(mayPointTo(voidArr, a)); // slice-contiaining void[N] might point at anything - assert(mayPointTo(voidArr, b)); - - static assert(() { - void[16] arr1 = void; - void[size_t.sizeof] arr2 = void; - int var; - return mayPointTo(arr1, var) && !doesPointTo(arr1, var) && - mayPointTo(arr2, var) && !doesPointTo(arr2, var); - }()); -} - - -@system unittest //Unions -{ - int i; - union U //Named union - { - size_t asInt = 0; - int* asPointer; - } - struct S - { - union //Anonymous union - { - size_t asInt = 0; - int* asPointer; - } - } - - U u; - S s; - assert(!doesPointTo(u, i)); - assert(!doesPointTo(s, i)); - assert(!mayPointTo(u, i)); - assert(!mayPointTo(s, i)); - - u.asPointer = &i; - s.asPointer = &i; - assert(!doesPointTo(u, i)); - assert(!doesPointTo(s, i)); - assert( mayPointTo(u, i)); - assert( mayPointTo(s, i)); - - u.asInt = cast(size_t)&i; - s.asInt = cast(size_t)&i; - assert(!doesPointTo(u, i)); - assert(!doesPointTo(s, i)); - assert( mayPointTo(u, i)); - assert( mayPointTo(s, i)); -} - -@system unittest //Classes -{ - int i; - static class A - { - int* p; - } - A a = new A, b = a; - assert(!doesPointTo(a, b)); //a does not point to b - a.p = &i; - assert(!doesPointTo(a, i)); //a does not point to i -} -@safe unittest //alias this test -{ - static int i; - static int j; - struct S - { - int* p; - @property int* foo(){return &i;} - alias foo this; - } - assert(is(S : int*)); - S s = S(&j); - assert(!doesPointTo(s, i)); - assert( doesPointTo(s, j)); - assert( doesPointTo(cast(int*) s, i)); - assert(!doesPointTo(cast(int*) s, j)); -} - -/+ -Returns true if the field at index `i` in ($D T) shares its address with another field. - -Note: This does not merelly check if the field is a member of an union, but also that -it is not a single child. -+/ -package enum isUnionAliased(T, size_t i) = isUnionAliasedImpl!T(T.tupleof[i].offsetof); -private bool isUnionAliasedImpl(T)(size_t offset) -{ - int count = 0; - foreach (i, U; typeof(T.tupleof)) - if (T.tupleof[i].offsetof == offset) - ++count; - return count >= 2; -} -// -@safe unittest -{ - static struct S - { - int a0; //Not aliased - union - { - int a1; //Not aliased - } - union - { - int a2; //Aliased - int a3; //Aliased - } - union A4 - { - int b0; //Not aliased - } - A4 a4; - union A5 - { - int b0; //Aliased - int b1; //Aliased - } - A5 a5; - } - - static assert(!isUnionAliased!(S, 0)); //a0; - static assert(!isUnionAliased!(S, 1)); //a1; - static assert( isUnionAliased!(S, 2)); //a2; - static assert( isUnionAliased!(S, 3)); //a3; - static assert(!isUnionAliased!(S, 4)); //a4; - static assert(!isUnionAliased!(S.A4, 0)); //a4.b0; - static assert(!isUnionAliased!(S, 5)); //a5; - static assert( isUnionAliased!(S.A5, 0)); //a5.b0; - static assert( isUnionAliased!(S.A5, 1)); //a5.b1; -} - -version (CRuntime_Glibc) version = GNU_STRERROR; -version (CRuntime_UClibc) version = GNU_STRERROR; - -package string errnoString(int errno) nothrow @trusted -{ - import core.stdc.string : strlen; - version (GNU_STRERROR) - { - import core.stdc.string : strerror_r; - char[1024] buf = void; - auto s = strerror_r(errno, buf.ptr, buf.length); - } - else version (Posix) - { - // XSI-compliant - import core.stdc.string : strerror_r; - char[1024] buf = void; - const(char)* s; - if (strerror_r(errno, buf.ptr, buf.length) == 0) - s = buf.ptr; - else - return "Unknown error"; - } - else - { - import core.stdc.string : strerror; - auto s = strerror(errno); - } - return s[0 .. s.strlen].idup; -} - -/********************* - * Thrown if errors that set `errno` occur. - */ -class ErrnoException : Exception -{ - /// Operating system error code. - final @property uint errno() nothrow pure scope @nogc @safe { return _errno; } - private uint _errno; - /// Localized error message generated through $(REF strerror_r, core,stdc,string) or $(REF strerror, core,stdc,string). - final @property string errnoMsg() nothrow pure scope @nogc @safe { return _errnoMsg; } - private string _errnoMsg; - /// Constructor which takes an error message. The current global $(REF errno, core,stdc,errno) value is used as error code. - this(string msg, string file = null, size_t line = 0) @safe - { - import core.stdc.errno : errno; - this(msg, errno, file, line); - } - /// Constructor which takes an error message and error code. - this(string msg, int errno, string file = null, size_t line = 0) @safe - { - _errno = errno; - _errnoMsg = errnoString(errno); - super(msg ~ " (" ~ errnoMsg ~ ")", file, line); - } -} - -/// -@safe unittest -{ - import core.stdc.errno : EAGAIN; - auto ex = new ErrnoException("oh no", EAGAIN); - assert(ex.errno == EAGAIN); -} - -/// errno is used by default if no explicit error code is provided -@safe unittest -{ - import core.stdc.errno : errno, EAGAIN; - - auto old = errno; - scope(exit) errno = old; - - // fake that errno got set by the callee - errno = EAGAIN; - auto ex = new ErrnoException("oh no"); - assert(ex.errno == EAGAIN); -} - -/++ - ML-style functional exception handling. Runs the supplied expression and - returns its result. If the expression throws a `Throwable`, runs the - supplied error handler instead and return its result. The error handler's - type must be the same as the expression's type. - - Params: - E = The type of `Throwable`s to catch. Defaults to `Exception` - T1 = The type of the expression. - T2 = The return type of the error handler. - expression = The expression to run and return its result. - errorHandler = The handler to run if the expression throwed. - - Returns: - expression, if it does not throw. Otherwise, returns the result of - errorHandler. -+/ -//lazy version -CommonType!(T1, T2) ifThrown(E : Throwable = Exception, T1, T2)(lazy scope T1 expression, lazy scope T2 errorHandler) -{ - static assert(!is(typeof(return) == void), - "The error handler's return value(" - ~ T2.stringof ~ - ") does not have a common type with the expression(" - ~ T1.stringof ~ - ")." - ); - try - { - return expression(); - } - catch (E) - { - return errorHandler(); - } -} - -///ditto -//delegate version -CommonType!(T1, T2) ifThrown(E : Throwable, T1, T2)(lazy scope T1 expression, scope T2 delegate(E) errorHandler) -{ - static assert(!is(typeof(return) == void), - "The error handler's return value(" - ~ T2.stringof ~ - ") does not have a common type with the expression(" - ~ T1.stringof ~ - ")." - ); - try - { - return expression(); - } - catch (E e) - { - return errorHandler(e); - } -} - -///ditto -//delegate version, general overload to catch any Exception -CommonType!(T1, T2) ifThrown(T1, T2)(lazy scope T1 expression, scope T2 delegate(Exception) errorHandler) -{ - static assert(!is(typeof(return) == void), - "The error handler's return value(" - ~ T2.stringof ~ - ") does not have a common type with the expression(" - ~ T1.stringof ~ - ")." - ); - try - { - return expression(); - } - catch (Exception e) - { - return errorHandler(e); - } -} - -/// Revert to a default value upon an error: -@safe unittest -{ - import std.conv : to; - assert("x".to!int.ifThrown(0) == 0); -} - -/** -Chain multiple calls to ifThrown, each capturing errors from the -entire preceding expression. -*/ -@safe unittest -{ - import std.conv : ConvException, to; - string s = "true"; - assert(s.to!int.ifThrown(cast(int) s.to!double) - .ifThrown(cast(int) s.to!bool) == 1); - - s = "2.0"; - assert(s.to!int.ifThrown(cast(int) s.to!double) - .ifThrown(cast(int) s.to!bool) == 2); - - // Respond differently to different types of errors - alias orFallback = (lazy a) => a.ifThrown!ConvException("not a number") - .ifThrown!Exception("number too small"); - - assert(orFallback(enforce("x".to!int < 1).to!string) == "not a number"); - assert(orFallback(enforce("2".to!int < 1).to!string) == "number too small"); -} - -/** -The expression and the errorHandler must have a common type they can both -be implicitly casted to, and that type will be the type of the compound -expression. -*/ -@safe unittest -{ - // null and new Object have a common type(Object). - static assert(is(typeof(null.ifThrown(new Object())) == Object)); - static assert(is(typeof((new Object()).ifThrown(null)) == Object)); - - // 1 and new Object do not have a common type. - static assert(!__traits(compiles, 1.ifThrown(new Object()))); - static assert(!__traits(compiles, (new Object()).ifThrown(1))); -} - -/// Use a lambda to get the thrown object. -@system unittest -{ - import std.format : format; - assert("%s".format.ifThrown!Exception(e => e.classinfo.name) == "std.format.FormatException"); -} - -//Verify Examples -@system unittest -{ - import std.conv; - import std.string; - //Revert to a default value upon an error: - assert("x".to!int().ifThrown(0) == 0); - - //Chaining multiple calls to ifThrown to attempt multiple things in a row: - string s="true"; - assert(s.to!int(). - ifThrown(cast(int) s.to!double()). - ifThrown(cast(int) s.to!bool()) - == 1); - - //Respond differently to different types of errors - assert(enforce("x".to!int() < 1).to!string() - .ifThrown!ConvException("not a number") - .ifThrown!Exception("number too small") - == "not a number"); - - //null and new Object have a common type(Object). - static assert(is(typeof(null.ifThrown(new Object())) == Object)); - static assert(is(typeof((new Object()).ifThrown(null)) == Object)); - - //1 and new Object do not have a common type. - static assert(!__traits(compiles, 1.ifThrown(new Object()))); - static assert(!__traits(compiles, (new Object()).ifThrown(1))); - - //Use a lambda to get the thrown object. - assert("%s".format().ifThrown(e => e.classinfo.name) == "std.format.FormatException"); -} - -@system unittest -{ - import core.exception; - import std.conv; - import std.string; - //Basic behaviour - all versions. - assert("1".to!int().ifThrown(0) == 1); - assert("x".to!int().ifThrown(0) == 0); - assert("1".to!int().ifThrown!ConvException(0) == 1); - assert("x".to!int().ifThrown!ConvException(0) == 0); - assert("1".to!int().ifThrown(e=>0) == 1); - assert("x".to!int().ifThrown(e=>0) == 0); - static if (__traits(compiles, 0.ifThrown!Exception(e => 0))) //This will only work with a fix that was not yet pulled - { - assert("1".to!int().ifThrown!ConvException(e=>0) == 1); - assert("x".to!int().ifThrown!ConvException(e=>0) == 0); - } - - //Exceptions other than stated not caught. - assert("x".to!int().ifThrown!StringException(0).collectException!ConvException() !is null); - static if (__traits(compiles, 0.ifThrown!Exception(e => 0))) //This will only work with a fix that was not yet pulled - { - assert("x".to!int().ifThrown!StringException(e=>0).collectException!ConvException() !is null); - } - - //Default does not include errors. - int throwRangeError() { throw new RangeError; } - assert(throwRangeError().ifThrown(0).collectException!RangeError() !is null); - assert(throwRangeError().ifThrown(e=>0).collectException!RangeError() !is null); - - //Incompatible types are not accepted. - static assert(!__traits(compiles, 1.ifThrown(new Object()))); - static assert(!__traits(compiles, (new Object()).ifThrown(1))); - static assert(!__traits(compiles, 1.ifThrown(e=>new Object()))); - static assert(!__traits(compiles, (new Object()).ifThrown(e=>1))); -} - -version (StdUnittest) package -void assertCTFEable(alias dg)() -{ - static assert({ cast(void) dg(); return true; }()); - cast(void) dg(); -} - -/** This `enum` is used to select the primitives of the range to handle by the - $(LREF handle) range wrapper. The values of the `enum` can be `OR`'d to - select multiple primitives to be handled. - - `RangePrimitive.access` is a shortcut for the access primitives; `front`, - `back` and `opIndex`. - - `RangePrimitive.pop` is a shortcut for the mutating primitives; - `popFront` and `popBack`. - */ -enum RangePrimitive -{ - front = 0b00_0000_0001, /// - back = 0b00_0000_0010, /// Ditto - popFront = 0b00_0000_0100, /// Ditto - popBack = 0b00_0000_1000, /// Ditto - empty = 0b00_0001_0000, /// Ditto - save = 0b00_0010_0000, /// Ditto - length = 0b00_0100_0000, /// Ditto - opDollar = 0b00_1000_0000, /// Ditto - opIndex = 0b01_0000_0000, /// Ditto - opSlice = 0b10_0000_0000, /// Ditto - access = front | back | opIndex, /// Ditto - pop = popFront | popBack, /// Ditto -} - -/// -pure @safe unittest -{ - import std.algorithm.comparison : equal; - import std.algorithm.iteration : map, splitter; - import std.conv : to, ConvException; - - auto s = "12,1337z32,54,2,7,9,1z,6,8"; - - // The next line composition will throw when iterated - // as some elements of the input do not convert to integer - auto r = s.splitter(',').map!(a => to!int(a)); - - // Substitute 0 for cases of ConvException - auto h = r.handle!(ConvException, RangePrimitive.front, (e, r) => 0); - assert(h.equal([12, 0, 54, 2, 7, 9, 0, 6, 8])); -} - -/// -pure @safe unittest -{ - import std.algorithm.comparison : equal; - import std.range : retro; - import std.utf : UTFException; - - auto str = "hello\xFFworld"; // 0xFF is an invalid UTF-8 code unit - - auto handled = str.handle!(UTFException, RangePrimitive.access, - (e, r) => ' '); // Replace invalid code points with spaces - - assert(handled.equal("hello world")); // `front` is handled, - assert(handled.retro.equal("dlrow olleh")); // as well as `back` -} - -/** Handle exceptions thrown from range primitives. - -Use the $(LREF RangePrimitive) enum to specify which primitives to _handle. -Multiple range primitives can be handled at once by using the `OR` operator -or the pseudo-primitives `RangePrimitive.access` and `RangePrimitive.pop`. -All handled primitives must have return types or values compatible with the -user-supplied handler. - -Params: - E = The type of `Throwable` to _handle. - primitivesToHandle = Set of range primitives to _handle. - handler = The callable that is called when a handled primitive throws a - `Throwable` of type `E`. The handler must accept arguments of - the form $(D E, ref IRange) and its return value is used as the primitive's - return value whenever `E` is thrown. For `opIndex`, the handler can - optionally recieve a third argument; the index that caused the exception. - input = The range to _handle. - -Returns: A wrapper `struct` that preserves the range interface of `input`. - -Note: -Infinite ranges with slicing support must return an instance of -$(REF Take, std,range) when sliced with a specific lower and upper -bound (see $(REF hasSlicing, std,range,primitives)); `handle` deals with -this by `take`ing 0 from the return value of the handler function and -returning that when an exception is caught. -*/ -auto handle(E : Throwable, RangePrimitive primitivesToHandle, alias handler, Range)(Range input) -if (isInputRange!Range) -{ - static struct Handler - { - private Range range; - - static if (isForwardRange!Range) - { - @property typeof(this) save() - { - static if (primitivesToHandle & RangePrimitive.save) - { - try - { - return typeof(this)(range.save); - } - catch (E exception) - { - return typeof(this)(handler(exception, this.range)); - } - } - else - return typeof(this)(range.save); - } - } - - static if (isInfinite!Range) - { - enum bool empty = false; - } - else - { - @property bool empty() - { - static if (primitivesToHandle & RangePrimitive.empty) - { - try - { - return this.range.empty; - } - catch (E exception) - { - return handler(exception, this.range); - } - } - else - return this.range.empty; - } - } - - @property auto ref front() - { - static if (primitivesToHandle & RangePrimitive.front) - { - try - { - return this.range.front; - } - catch (E exception) - { - return handler(exception, this.range); - } - } - else - return this.range.front; - } - - void popFront() - { - static if (primitivesToHandle & RangePrimitive.popFront) - { - try - { - this.range.popFront(); - } - catch (E exception) - { - handler(exception, this.range); - } - } - else - this.range.popFront(); - } - - static if (isBidirectionalRange!Range) - { - @property auto ref back() - { - static if (primitivesToHandle & RangePrimitive.back) - { - try - { - return this.range.back; - } - catch (E exception) - { - return handler(exception, this.range); - } - } - else - return this.range.back; - } - - void popBack() - { - static if (primitivesToHandle & RangePrimitive.popBack) - { - try - { - this.range.popBack(); - } - catch (E exception) - { - handler(exception, this.range); - } - } - else - this.range.popBack(); - } - } - - static if (isRandomAccessRange!Range) - { - auto ref opIndex(size_t index) - { - static if (primitivesToHandle & RangePrimitive.opIndex) - { - try - { - return this.range[index]; - } - catch (E exception) - { - static if (__traits(compiles, handler(exception, this.range, index))) - return handler(exception, this.range, index); - else - return handler(exception, this.range); - } - } - else - return this.range[index]; - } - } - - static if (hasLength!Range) - { - @property auto length() - { - static if (primitivesToHandle & RangePrimitive.length) - { - try - { - return this.range.length; - } - catch (E exception) - { - return handler(exception, this.range); - } - } - else - return this.range.length; - } - } - - static if (hasSlicing!Range) - { - static if (hasLength!Range) - { - typeof(this) opSlice(size_t lower, size_t upper) - { - static if (primitivesToHandle & RangePrimitive.opSlice) - { - try - { - return typeof(this)(this.range[lower .. upper]); - } - catch (E exception) - { - return typeof(this)(handler(exception, this.range)); - } - } - else - return typeof(this)(this.range[lower .. upper]); - } - } - else static if (is(typeof(Range.init[size_t.init .. $]))) - { - import std.range : Take, takeExactly; - static struct DollarToken {} - enum opDollar = DollarToken.init; - - typeof(this) opSlice(size_t lower, DollarToken) - { - static if (primitivesToHandle & RangePrimitive.opSlice) - { - try - { - return typeof(this)(this.range[lower .. $]); - } - catch (E exception) - { - return typeof(this)(handler(exception, this.range)); - } - } - else - return typeof(this)(this.range[lower .. $]); - } - - Take!Handler opSlice(size_t lower, size_t upper) - { - static if (primitivesToHandle & RangePrimitive.opSlice) - { - try - { - return takeExactly(typeof(this)(this.range[lower .. $]), upper - 1); - } - catch (E exception) - { - return takeExactly(typeof(this)(handler(exception, this.range)), 0); - } - } - else - return takeExactly(typeof(this)(this.range[lower .. $]), upper - 1); - } - } - } - } - - return Handler(input); -} - -/// -pure @safe unittest -{ - import std.algorithm.comparison : equal; - import std.algorithm.iteration : map, splitter; - import std.conv : to, ConvException; - - auto s = "12,1337z32,54,2,7,9,1z,6,8"; - - // The next line composition will throw when iterated - // as some elements of the input do not convert to integer - auto r = s.splitter(',').map!(a => to!int(a)); - - // Substitute 0 for cases of ConvException - auto h = r.handle!(ConvException, RangePrimitive.front, (e, r) => 0); - assert(h.equal([12, 0, 54, 2, 7, 9, 0, 6, 8])); -} - -/// -pure @safe unittest -{ - import std.algorithm.comparison : equal; - import std.range : retro; - import std.utf : UTFException; - - auto str = "hello\xFFworld"; // 0xFF is an invalid UTF-8 code unit - - auto handled = str.handle!(UTFException, RangePrimitive.access, - (e, r) => ' '); // Replace invalid code points with spaces - - assert(handled.equal("hello world")); // `front` is handled, - assert(handled.retro.equal("dlrow olleh")); // as well as `back` -} - -pure nothrow @safe unittest -{ - static struct ThrowingRange - { - pure @safe: - @property bool empty() - { - throw new Exception("empty has thrown"); - } - - @property int front() - { - throw new Exception("front has thrown"); - } - - @property int back() - { - throw new Exception("back has thrown"); - } - - void popFront() - { - throw new Exception("popFront has thrown"); - } - - void popBack() - { - throw new Exception("popBack has thrown"); - } - - int opIndex(size_t) - { - throw new Exception("opIndex has thrown"); - } - - ThrowingRange opSlice(size_t, size_t) - { - throw new Exception("opSlice has thrown"); - } - - @property size_t length() - { - throw new Exception("length has thrown"); - } - - alias opDollar = length; - - @property ThrowingRange save() - { - throw new Exception("save has thrown"); - } - } - - static assert(isInputRange!ThrowingRange); - static assert(isForwardRange!ThrowingRange); - static assert(isBidirectionalRange!ThrowingRange); - static assert(hasSlicing!ThrowingRange); - static assert(hasLength!ThrowingRange); - - auto f = ThrowingRange(); - auto fb = f.handle!(Exception, RangePrimitive.front | RangePrimitive.back, - (e, r) => -1)(); - assert(fb.front == -1); - assert(fb.back == -1); - assertThrown(fb.popFront()); - assertThrown(fb.popBack()); - assertThrown(fb.empty); - assertThrown(fb.save); - assertThrown(fb[0]); - - auto accessRange = f.handle!(Exception, RangePrimitive.access, - (e, r) => -1); - assert(accessRange.front == -1); - assert(accessRange.back == -1); - assert(accessRange[0] == -1); - assertThrown(accessRange.popFront()); - assertThrown(accessRange.popBack()); - - auto pfb = f.handle!(Exception, RangePrimitive.pop, (e, r) => -1)(); - - pfb.popFront(); // this would throw otherwise - pfb.popBack(); // this would throw otherwise - - auto em = f.handle!(Exception, - RangePrimitive.empty, (e, r) => false)(); - - assert(!em.empty); - - auto arr = f.handle!(Exception, - RangePrimitive.opIndex, (e, r) => 1337)(); - - assert(arr[0] == 1337); - - auto arr2 = f.handle!(Exception, - RangePrimitive.opIndex, (e, r, i) => i)(); - - assert(arr2[0] == 0); - assert(arr2[1337] == 1337); - - auto save = f.handle!(Exception, - RangePrimitive.save, - function(Exception e, ref ThrowingRange r) { - return ThrowingRange(); - })(); - - save.save; - - auto slice = f.handle!(Exception, - RangePrimitive.opSlice, (e, r) => ThrowingRange())(); - - auto sliced = slice[0 .. 1337]; // this would throw otherwise - - static struct Infinite - { - import std.range : Take; - pure @safe: - enum bool empty = false; - int front() { assert(false); } - void popFront() { assert(false); } - Infinite save() @property { assert(false); } - static struct DollarToken {} - enum opDollar = DollarToken.init; - Take!Infinite opSlice(size_t, size_t) { assert(false); } - Infinite opSlice(size_t, DollarToken) - { - throw new Exception("opSlice has thrown"); - } - } - - static assert(isInputRange!Infinite); - static assert(isInfinite!Infinite); - static assert(hasSlicing!Infinite); - - assertThrown(Infinite()[0 .. $]); - - auto infinite = Infinite.init.handle!(Exception, - RangePrimitive.opSlice, (e, r) => Infinite())(); - - auto infSlice = infinite[0 .. $]; // this would throw otherwise -} - - -/++ - Convenience mixin for trivially sub-classing exceptions - - Even trivially sub-classing an exception involves writing boilerplate code - for the constructor to: 1$(RPAREN) correctly pass in the source file and line number - the exception was thrown from; 2$(RPAREN) be usable with $(LREF enforce) which - expects exception constructors to take arguments in a fixed order. This - mixin provides that boilerplate code. - - Note however that you need to mark the $(B mixin) line with at least a - minimal (i.e. just $(B ///)) DDoc comment if you want the mixed-in - constructors to be documented in the newly created Exception subclass. - - $(RED Current limitation): Due to - $(LINK2 https://issues.dlang.org/show_bug.cgi?id=11500, bug #11500), - currently the constructors specified in this mixin cannot be overloaded with - any other custom constructors. Thus this mixin can currently only be used - when no such custom constructors need to be explicitly specified. - +/ -mixin template basicExceptionCtors() -{ - /++ - Params: - msg = The message for the exception. - file = The file where the exception occurred. - line = The line number where the exception occurred. - next = The previous exception in the chain of exceptions, if any. - +/ - this(string msg, string file = __FILE__, size_t line = __LINE__, - Throwable next = null) @nogc @safe pure nothrow - { - super(msg, file, line, next); - } - - /++ - Params: - msg = The message for the exception. - next = The previous exception in the chain of exceptions. - file = The file where the exception occurred. - line = The line number where the exception occurred. - +/ - this(string msg, Throwable next, string file = __FILE__, - size_t line = __LINE__) @nogc @safe pure nothrow - { - super(msg, file, line, next); - } -} - -/// -@safe unittest -{ - class MeaCulpa: Exception - { - /// - mixin basicExceptionCtors; - } - - try - throw new MeaCulpa("test"); - catch (MeaCulpa e) - { - assert(e.msg == "test"); - assert(e.file == __FILE__); - assert(e.line == __LINE__ - 5); - } -} - -@safe pure nothrow unittest -{ - class TestException : Exception { mixin basicExceptionCtors; } - auto e = new Exception("msg"); - auto te1 = new TestException("foo"); - auto te2 = new TestException("foo", e); -} - -@safe unittest -{ - class TestException : Exception { mixin basicExceptionCtors; } - auto e = new Exception("!!!"); - - auto te1 = new TestException("message", "file", 42, e); - assert(te1.msg == "message"); - assert(te1.file == "file"); - assert(te1.line == 42); - assert(te1.next is e); - - auto te2 = new TestException("message", e, "file", 42); - assert(te2.msg == "message"); - assert(te2.file == "file"); - assert(te2.line == 42); - assert(te2.next is e); - - auto te3 = new TestException("foo"); - assert(te3.msg == "foo"); - assert(te3.file == __FILE__); - assert(te3.line == __LINE__ - 3); - assert(te3.next is null); - - auto te4 = new TestException("foo", e); - assert(te4.msg == "foo"); - assert(te4.file == __FILE__); - assert(te4.line == __LINE__ - 3); - assert(te4.next is e); -} diff --git a/phobos/std/experimental/allocator/building_blocks/affix_allocator.d b/phobos/std/experimental/allocator/building_blocks/affix_allocator.d deleted file mode 100644 index d0d0b7c..0000000 --- a/phobos/std/experimental/allocator/building_blocks/affix_allocator.d +++ /dev/null @@ -1,562 +0,0 @@ -// Written in the D programming language. -/** -Source: $(PHOBOSSRC std/experimental/allocator/building_blocks/affix_allocator.d) -*/ -module std.experimental.allocator.building_blocks.affix_allocator; - -/** - -Allocator that adds some extra data before (of type `Prefix`) and/or after -(of type `Suffix`) any allocation made with its parent allocator. This is -useful for uses where additional allocation-related information is needed, such -as mutexes, reference counts, or walls for debugging memory corruption errors. - -If `Prefix` is not `void`, `Allocator` must guarantee an alignment at -least as large as `Prefix.alignof`. - -Suffixes are slower to get at because of alignment rounding, so prefixes should -be preferred. However, small prefixes blunt the alignment so if a large -alignment with a small affix is needed, suffixes should be chosen. - -The following methods are defined if `Allocator` defines them, and forward to it: `deallocateAll`, `empty`, `owns`. - */ -struct AffixAllocator(Allocator, Prefix, Suffix = void) -{ - import std.algorithm.comparison : min; - import core.lifetime : emplace; - import std.experimental.allocator : RCIAllocator, theAllocator; - import std.experimental.allocator.common : stateSize, forwardToMember, - roundUpToMultipleOf, alignedAt, alignDownTo, roundUpToMultipleOf, - hasStaticallyKnownAlignment; - import std.math.traits : isPowerOf2; - import std.traits : hasMember; - import std.typecons : Ternary; - - static if (hasStaticallyKnownAlignment!Allocator) - { - static assert( - !stateSize!Prefix || Allocator.alignment >= Prefix.alignof, - "AffixAllocator does not work with allocators offering a smaller" - ~ " alignment than the prefix alignment."); - } - static assert(alignment % Suffix.alignof == 0, - "This restriction could be relaxed in the future."); - - /** - If `Prefix` is `void`, the alignment is that of the parent. Otherwise, the alignment is the same as the `Prefix`'s alignment. - */ - static if (hasStaticallyKnownAlignment!Allocator) - { - enum uint alignment = isPowerOf2(stateSize!Prefix) - ? min(stateSize!Prefix, Allocator.alignment) - : (stateSize!Prefix ? Prefix.alignof : Allocator.alignment); - } - else static if (is(Prefix == void)) - { - enum uint alignment = platformAlignment; - } - else - { - enum uint alignment = Prefix.alignof; - } - - /** - If the parent allocator `Allocator` is stateful, an instance of it is - stored as a member. Otherwise, `AffixAllocator` uses - `Allocator.instance`. In either case, the name `_parent` is uniformly - used for accessing the parent allocator. - */ - static if (stateSize!Allocator) - { - Allocator _parent; - static if (is(Allocator == RCIAllocator)) - { - @nogc nothrow pure @safe - Allocator parent() - { - static @nogc nothrow - RCIAllocator wrapAllocatorObject() - { - import std.experimental.allocator.gc_allocator : GCAllocator; - import std.experimental.allocator : allocatorObject; - - return allocatorObject(GCAllocator.instance); - } - - if (_parent.isNull) - { - // If the `_parent` allocator is `null` we will assign - // an object that references the GC as the `parent`. - auto fn = (() @trusted => - cast(RCIAllocator function() @nogc nothrow pure @safe)(&wrapAllocatorObject))(); - _parent = fn(); - } - - // `RCIAllocator.alignment` currently doesn't have any attributes - // so we must cast; throughout the allocators module, `alignment` - // is defined as an `enum` for the existing allocators. - // `alignment` should always be `@nogc nothrow pure @safe`; once - // this is enforced by the interface we can remove the cast - auto pureAlign = (() @trusted => - cast(uint delegate() @nogc nothrow pure @safe)(&_parent.alignment))(); - assert(alignment <= pureAlign()); - return _parent; - } - } - else - { - alias parent = _parent; - } - } - else - { - alias parent = Allocator.instance; - } - - private template Impl() - { - - size_t goodAllocSize(size_t s) - { - import std.experimental.allocator.common : goodAllocSize; - auto a = actualAllocationSize(s); - return roundUpToMultipleOf(parent.goodAllocSize(a) - - stateSize!Prefix - stateSize!Suffix, - this.alignment); - } - - private size_t actualAllocationSize(size_t s) const - { - assert(s > 0); - static if (!stateSize!Suffix) - { - return s + stateSize!Prefix; - } - else - { - return - roundUpToMultipleOf(s + stateSize!Prefix, Suffix.alignof) - + stateSize!Suffix; - } - } - - private void[] actualAllocation(void[] b) const - { - assert(b !is null); - return (b.ptr - stateSize!Prefix) - [0 .. actualAllocationSize(b.length)]; - } - - // Common code shared between allocate and allocateZeroed. - private enum _processAndReturnAllocateResult = - q{ - if (result is null) return null; - static if (stateSize!Prefix) - { - assert(result.ptr.alignedAt(Prefix.alignof)); - emplace!Prefix(cast(Prefix*) result.ptr); - } - static if (stateSize!Suffix) - { - auto suffixP = result.ptr + result.length - Suffix.sizeof; - assert(suffixP.alignedAt(Suffix.alignof)); - emplace!Suffix(cast(Suffix*)(suffixP)); - } - return result[stateSize!Prefix .. stateSize!Prefix + bytes]; - }; - - void[] allocate(size_t bytes) - { - if (!bytes) return null; - auto result = parent.allocate(actualAllocationSize(bytes)); - mixin(_processAndReturnAllocateResult); - } - - static if (hasMember!(Allocator, "allocateZeroed")) - package(std) void[] allocateZeroed()(size_t bytes) - { - if (!bytes) return null; - auto result = parent.allocateZeroed(actualAllocationSize(bytes)); - mixin(_processAndReturnAllocateResult); - } - - static if (hasMember!(Allocator, "allocateAll")) - void[] allocateAll() - { - auto result = parent.allocateAll(); - if (result is null) return null; - if (result.length < actualAllocationSize(1)) - { - deallocate(result); - return null; - } - static if (stateSize!Prefix) - { - assert(result.length > stateSize!Prefix); - emplace!Prefix(cast(Prefix*) result.ptr); - result = result[stateSize!Prefix .. $]; - } - static if (stateSize!Suffix) - { - assert(result.length > stateSize!Suffix); - // Ehm, find a properly aligned place for the suffix - auto p = (result.ptr + result.length - stateSize!Suffix) - .alignDownTo(Suffix.alignof); - assert(p > result.ptr); - emplace!Suffix(cast(Suffix*) p); - result = result[0 .. p - result.ptr]; - } - return result; - } - - static if (hasMember!(Allocator, "owns")) - Ternary owns(void[] b) - { - if (b is null) return Ternary.no; - return parent.owns((() @trusted => actualAllocation(b))()); - } - - static if (hasMember!(Allocator, "resolveInternalPointer")) - Ternary resolveInternalPointer(const void* p, ref void[] result) - { - void[] p1; - Ternary r = parent.resolveInternalPointer(p, p1); - if (r != Ternary.yes || p1 is null) - return r; - p1 = p1[stateSize!Prefix .. $]; - auto p2 = (() @trusted => (&p1[0] + p1.length - stateSize!Suffix) - .alignDownTo(Suffix.alignof))(); - result = p1[0 .. p2 - &p1[0]]; - return Ternary.yes; - } - - static if (!stateSize!Suffix && hasMember!(Allocator, "expand") - && hasMember!(Allocator, "owns")) - bool expand(ref void[] b, size_t delta) - { - if (!b || delta == 0) return delta == 0; - if (owns(b) == Ternary.no) return false; - auto t = (() @trusted => actualAllocation(b))(); - const result = parent.expand(t, delta); - if (!result) return false; - b = (() @trusted => b.ptr[0 .. b.length + delta])(); - return true; - } - - static if (hasMember!(Allocator, "reallocate")) - bool reallocate(ref void[] b, size_t s) - { - if (b is null) - { - b = allocate(s); - return b.length == s; - } - auto t = actualAllocation(b); - const result = parent.reallocate(t, actualAllocationSize(s)); - if (!result) return false; // no harm done - b = t.ptr[stateSize!Prefix .. stateSize!Prefix + s]; - return true; - } - - static if (hasMember!(Allocator, "deallocate")) - bool deallocate(void[] b) - { - if (!b.ptr) return true; - return parent.deallocate(actualAllocation(b)); - } - - /* The following methods are defined if `ParentAllocator` defines - them, and forward to it: `deallocateAll`, `empty`.*/ - mixin(forwardToMember("parent", - "deallocateAll", "empty")); - - // Computes suffix type given buffer type - private template Payload2Affix(Payload, Affix) - { - static if (is(Payload[] : void[])) - alias Payload2Affix = Affix; - else static if (is(Payload[] : shared(void)[])) - alias Payload2Affix = shared Affix; - else static if (is(Payload[] : immutable(void)[])) - alias Payload2Affix = shared Affix; - else static if (is(Payload[] : const(shared(void))[])) - alias Payload2Affix = shared Affix; - else static if (is(Payload[] : const(void)[])) - alias Payload2Affix = const Affix; - else - static assert(0, "Internal error for type " ~ Payload.stringof); - } - - // Extra functions - static if (stateSize!Prefix) - { - static auto ref prefix(T)(T[] b) - { - assert(b.ptr && b.ptr.alignedAt(Prefix.alignof)); - return (cast(Payload2Affix!(T, Prefix)*) b.ptr)[-1]; - } - } - static if (stateSize!Suffix) - auto ref suffix(T)(T[] b) - { - assert(b.ptr); - auto p = b.ptr - stateSize!Prefix - + actualAllocationSize(b.length); - assert(p && p.alignedAt(Suffix.alignof)); - return (cast(Payload2Affix!(T, Suffix)*) p)[-1]; - } - } - - version (StdDdoc) - { - /** - Standard allocator methods. Each is defined if and only if the parent - allocator defines the homonym method (except for `goodAllocSize`, - which may use the global default). Also, the methods will be $(D - shared) if the parent allocator defines them as such. - */ - size_t goodAllocSize(size_t); - /// Ditto - void[] allocate(size_t); - /// Ditto - Ternary owns(void[]); - /// Ditto - bool expand(ref void[] b, size_t delta); - /// Ditto - bool reallocate(ref void[] b, size_t s); - /// Ditto - bool deallocate(void[] b); - /// Ditto - bool deallocateAll(); - /// Ditto - Ternary empty(); - - /** - The `instance` singleton is defined if and only if the parent allocator - has no state and defines its own `it` object. - */ - static AffixAllocator instance; - - /** - Affix access functions offering references to the affixes of a - block `b` previously allocated with this allocator. `b` may not be null. - They are defined if and only if the corresponding affix is not `void`. - - The qualifiers of the affix are not always the same as the qualifiers - of the argument. This is because the affixes are not part of the data - itself, but instead are just $(I associated) with the data and known - to the allocator. The table below documents the type of `preffix(b)` and - `affix(b)` depending on the type of `b`. - - $(BOOKTABLE Result of `prefix`/`suffix` depending on argument (`U` is - any unqualified type, `Affix` is `Prefix` or `Suffix`), - $(TR $(TH Argument$(NBSP)Type) $(TH Return) $(TH Comments)) - - $(TR $(TD `shared(U)[]`) $(TD `ref shared Affix`) - $(TD Data is shared across threads and the affix follows suit.)) - - $(TR $(TD `immutable(U)[]`) $(TD `ref shared Affix`) - $(TD Although the data is immutable, the allocator "knows" the - underlying memory is mutable, so `immutable` is elided for the affix - which is independent from the data itself. However, the result is - `shared` because `immutable` is implicitly shareable so multiple - threads may access and manipulate the affix for the same data.)) - - $(TR $(TD `const(shared(U))[]`) $(TD `ref shared Affix`) - $(TD The data is always shareable across threads. Even if the data - is `const`, the affix is modifiable by the same reasoning as for - `immutable`.)) - - $(TR $(TD `const(U)[]`) $(TD `ref const Affix`) - $(TD The input may have originated from `U[]` or `immutable(U)[]`, - so it may be actually shared or not. Returning an unqualified affix - may result in race conditions, whereas returning a `shared` affix - may result in inadvertent sharing of mutable thread-local data - across multiple threads. So the returned type is conservatively - `ref const`.)) - - $(TR $(TD `U[]`) $(TD `ref Affix`) - $(TD Unqualified data has unqualified affixes.)) - ) - - Precondition: `b !is null` and `b` must have been allocated with - this allocator. - */ - static ref auto prefix(T)(T[] b); - /// Ditto - ref auto suffix(T)(T[] b); - } - else static if (is(typeof(Allocator.instance) == shared)) - { - static assert(stateSize!Allocator == 0); - static shared AffixAllocator instance; - shared { mixin Impl!(); } - } - else static if (is(Allocator == shared)) - { - static assert(stateSize!Allocator != 0); - shared { mixin Impl!(); } - } - else - { - mixin Impl!(); - static if (stateSize!Allocator == 0) - __gshared AffixAllocator instance; - } -} - -/// -@system unittest -{ - import std.experimental.allocator.mallocator : Mallocator; - // One word before and after each allocation. - alias A = AffixAllocator!(Mallocator, size_t, size_t); - auto b = A.instance.allocate(11); - A.instance.prefix(b) = 0xCAFE_BABE; - A.instance.suffix(b) = 0xDEAD_BEEF; - assert(A.instance.prefix(b) == 0xCAFE_BABE - && A.instance.suffix(b) == 0xDEAD_BEEF); -} - -@system unittest -{ - import std.experimental.allocator.gc_allocator : GCAllocator; - import std.experimental.allocator : theAllocator, RCIAllocator; - - // One word before and after each allocation. - auto A = AffixAllocator!(RCIAllocator, size_t, size_t)(theAllocator); - auto a = A.allocate(11); - A.prefix(a) = 0xCAFE_BABE; - A.suffix(a) = 0xDEAD_BEEF; - assert(A.prefix(a) == 0xCAFE_BABE - && A.suffix(a) == 0xDEAD_BEEF); - - // One word before and after each allocation. - auto B = AffixAllocator!(RCIAllocator, size_t, size_t)(); - auto b = B.allocate(11); - B.prefix(b) = 0xCAFE_BABE; - B.suffix(b) = 0xDEAD_BEEF; - assert(B.prefix(b) == 0xCAFE_BABE - && B.suffix(b) == 0xDEAD_BEEF); -} - -version (StdUnittest) -@system unittest -{ - import std.experimental.allocator.building_blocks.bitmapped_block - : BitmappedBlock; - import std.experimental.allocator.common : testAllocator; - testAllocator!({ - auto a = AffixAllocator!(BitmappedBlock!128, ulong, ulong) - (BitmappedBlock!128(new ubyte[128 * 4096])); - return a; - }); -} - -// Test empty -@system unittest -{ - import std.experimental.allocator.building_blocks.bitmapped_block : BitmappedBlock; - import std.typecons : Ternary; - - auto a = AffixAllocator!(BitmappedBlock!128, ulong, ulong) - (BitmappedBlock!128(new ubyte[128 * 4096])); - assert((() pure nothrow @safe @nogc => a.empty)() == Ternary.yes); - auto b = a.allocate(42); - assert(b.length == 42); - assert((() pure nothrow @safe @nogc => a.empty)() == Ternary.no); -} - -@system unittest -{ - import std.experimental.allocator.mallocator : Mallocator; - alias A = AffixAllocator!(Mallocator, size_t); - auto b = A.instance.allocate(10); - A.instance.prefix(b) = 10; - assert(A.instance.prefix(b) == 10); - - import std.experimental.allocator.building_blocks.null_allocator - : NullAllocator; - alias B = AffixAllocator!(NullAllocator, size_t); - b = B.instance.allocate(100); - assert(b is null); -} - -@system unittest -{ - import std.experimental.allocator; - import std.experimental.allocator.gc_allocator; - import std.typecons : Ternary; - alias MyAllocator = AffixAllocator!(GCAllocator, uint); - auto a = MyAllocator.instance.makeArray!(shared int)(100); - static assert(is(typeof(&MyAllocator.instance.prefix(a)) == shared(uint)*)); - auto b = MyAllocator.instance.makeArray!(shared const int)(100); - static assert(is(typeof(&MyAllocator.instance.prefix(b)) == shared(uint)*)); - auto c = MyAllocator.instance.makeArray!(immutable int)(100); - static assert(is(typeof(&MyAllocator.instance.prefix(c)) == shared(uint)*)); - auto d = MyAllocator.instance.makeArray!(int)(100); - static assert(is(typeof(&MyAllocator.instance.prefix(d)) == uint*)); - auto e = MyAllocator.instance.makeArray!(const int)(100); - static assert(is(typeof(&MyAllocator.instance.prefix(e)) == const(uint)*)); - - void[] p; - assert((() nothrow @safe @nogc => MyAllocator.instance.resolveInternalPointer(null, p))() == Ternary.no); - assert((() nothrow @safe => MyAllocator.instance.resolveInternalPointer(&d[0], p))() == Ternary.yes); - assert(p.ptr is d.ptr && p.length >= d.length); -} - -@system unittest -{ - import std.experimental.allocator.gc_allocator; - alias a = AffixAllocator!(GCAllocator, uint).instance; - - // Check that goodAllocSize inherits from parent, i.e. GCAllocator - assert(__traits(compiles, (() nothrow @safe @nogc => a.goodAllocSize(1))())); - - // Ensure deallocate inherits from parent - auto b = a.allocate(42); - assert(b.length == 42); - () nothrow @nogc { a.deallocate(b); }(); -} - -@system unittest -{ - import std.experimental.allocator.building_blocks.region : BorrowedRegion; - - auto a = AffixAllocator!(BorrowedRegion!(), uint)(BorrowedRegion!()(new ubyte[1024 * 64])); - auto b = a.allocate(42); - assert(b.length == 42); - // Test that expand infers from parent - assert((() pure nothrow @safe @nogc => a.expand(b, 58))()); - assert(b.length == 100); - // Test that deallocateAll infers from parent - assert((() nothrow @nogc => a.deallocateAll())()); -} - -// Test that reallocate infers from parent -@system unittest -{ - import std.experimental.allocator.mallocator : Mallocator; - - alias a = AffixAllocator!(Mallocator, uint).instance; - auto b = a.allocate(42); - assert(b.length == 42); - assert((() nothrow @nogc => a.reallocate(b, 100))()); - assert(b.length == 100); - assert((() nothrow @nogc => a.deallocate(b))()); -} - -@system unittest -{ - import std.experimental.allocator : processAllocator, RCISharedAllocator; - import std.traits; - - alias SharedAllocT = shared AffixAllocator!(RCISharedAllocator, int); - static assert(is(RCISharedAllocator == shared)); - static assert(!is(SharedAllocT.instance)); - - SharedAllocT a = SharedAllocT(processAllocator); - auto buf = a.allocate(10); - static assert(is(typeof(a.allocate) == shared)); - assert(buf.length == 10); -} diff --git a/phobos/std/experimental/allocator/building_blocks/aligned_block_list.d b/phobos/std/experimental/allocator/building_blocks/aligned_block_list.d deleted file mode 100644 index 99768bc..0000000 --- a/phobos/std/experimental/allocator/building_blocks/aligned_block_list.d +++ /dev/null @@ -1,699 +0,0 @@ -// Written in the D programming language. -/** -`AlignedBlockList` represents a wrapper around a chain of allocators, allowing for fast deallocations -and preserving a low degree of fragmentation by means of aligned allocations. - -Source: $(PHOBOSSRC std/experimental/allocator/building_blocks/aligned_block_list.d) -*/ -module std.experimental.allocator.building_blocks.aligned_block_list; - -import std.experimental.allocator.common; -import std.experimental.allocator.building_blocks.null_allocator; - -// Common function implementation for thread local and shared AlignedBlockList -private mixin template AlignedBlockListImpl(bool isShared) -{ - import std.traits : hasMember; - import std.typecons : Ternary; - - static if (isShared) - import core.internal.spinlock : SpinLock; - -private: - // Doubly linked list of 'AlignedBlockNode' - // Each node contains an `Allocator` followed by its payload - static struct AlignedBlockNode - { - AlignedBlockNode* next, prev; - Allocator bAlloc; - - static if (isShared) - { - shared(size_t) bytesUsed; - // Since the lock is not taken when allocating, this acts like a refcount - // keeping the node alive - uint keepAlive; - } - else - { - size_t bytesUsed; - } - } - - // Root of the internal doubly linked list - AlignedBlockNode* root; - - // Number of active nodes - uint numNodes; - - // If the numNodes exceeds this limit, we will start deallocating nodes - enum uint maxNodes = 64; - - // This lock is always taken when changing the list - // To improve performance, the lock is not taken when the allocation logic is called - static if (isShared) - SpinLock lock = SpinLock(SpinLock.Contention.brief); - - // Moves a node to the front of the list, allowing for quick allocations - void moveToFront(AlignedBlockNode* tmp) - { - auto localRoot = cast(AlignedBlockNode*) root; - if (tmp == localRoot) - return; - - if (tmp.prev) tmp.prev.next = tmp.next; - if (tmp.next) tmp.next.prev = tmp.prev; - if (localRoot) localRoot.prev = tmp; - tmp.next = localRoot; - tmp.prev = null; - - root = cast(typeof(root)) tmp; - } - - // Removes a node from the list, including its payload - // The payload is deallocated by calling 'parent.deallocate' - void removeNode(AlignedBlockNode* tmp) - { - auto next = tmp.next; - if (tmp.prev) tmp.prev.next = tmp.next; - if (tmp.next) tmp.next.prev = tmp.prev; - parent.deallocate((cast(void*) tmp)[0 .. theAlignment]); - - if (tmp == cast(AlignedBlockNode*) root) - root = cast(typeof(root)) next; - - static if (isShared) - { - import core.atomic : atomicOp; - atomicOp!"-="(numNodes, 1); - } - else - { - numNodes--; - } - } - - // If the nodes do not have available space, a new node is created - // by drawing memory from the parent allocator with aligned allocations. - // The new node is inserted at the front of the list - bool insertNewNode() - { - void[] buf = parent.alignedAllocate(theAlignment, theAlignment); - if (buf is null) - return false; - - auto localRoot = cast(AlignedBlockNode*) root; - auto newNode = cast(AlignedBlockNode*) buf; - - // The first part of the allocation represent the node contents - // followed by the actual payload - ubyte[] payload = cast(ubyte[]) buf[AlignedBlockNode.sizeof .. $]; - newNode.bAlloc = Allocator(payload); - - newNode.next = localRoot; - newNode.prev = null; - if (localRoot) - localRoot.prev = newNode; - root = cast(typeof(root)) newNode; - - static if (isShared) - { - import core.atomic : atomicOp; - atomicOp!"+="(numNodes, 1); - } - else - { - numNodes++; - } - - return true; - } - -public: - static if (stateSize!ParentAllocator) ParentAllocator parent; - else alias parent = ParentAllocator.instance; - - enum ulong alignment = Allocator.alignment; - - // Since all memory is drawn from ParentAllocator, we can - // forward this to the parent - static if (hasMember!(ParentAllocator, "owns")) - Ternary owns(void[] b) - { - return parent.owns(b); - } - - // Use `theAlignment` to find the node which allocated this block - bool deallocate(void[] b) - { - if (b is null) - return true; - - // Round buffer to nearest `theAlignment` multiple to quickly find - // the `parent` `AlignedBlockNode` - enum ulong mask = ~(theAlignment - 1); - ulong ptr = ((cast(ulong) b.ptr) & mask); - AlignedBlockNode *node = cast(AlignedBlockNode*) ptr; - if (node.bAlloc.deallocate(b)) - { - static if (isShared) - { - import core.atomic : atomicOp; - atomicOp!"-="(node.bytesUsed, b.length); - } - else - { - node.bytesUsed -= b.length; - } - return true; - } - return false; - } - - // Allocate works only if memory can be provided via `alignedAllocate` from the parent - static if (hasMember!(ParentAllocator, "alignedAllocate")) - void[] allocate(size_t n) - { - static if (isShared) - import core.atomic : atomicOp, atomicLoad; - - if (n == 0 || n > theAlignment) - return null; - - static if (isShared) - { - lock.lock(); - scope(exit) lock.unlock(); - } - - auto tmp = cast(AlignedBlockNode*) root; - - // Iterate through list and find first node which has memory available - while (tmp) - { - auto next = tmp.next; - static if (isShared) - { - // Allocations can happen outside the lock - // Make sure nobody deletes this node while using it - tmp.keepAlive++; - if (next) next.keepAlive++; - lock.unlock(); - } - - auto result = tmp.bAlloc.allocate(n); - if (result.length == n) - { - // Success - static if (isShared) - { - atomicOp!"+="(tmp.bytesUsed, n); - lock.lock(); - } - else - { - tmp.bytesUsed += n; - } - - // Most likely this node has memory for more allocations - // Move it to the front - moveToFront(tmp); - - static if (isShared) - { - tmp.keepAlive--; - if (next) next.keepAlive--; - } - - return result; - } - - // This node can now be removed if necessary - static if (isShared) - { - lock.lock(); - tmp.keepAlive--; - if (next) next.keepAlive--; - } - - if (!next) - break; - - tmp = next; - next = tmp.next; - - // If there are too many nodes, free memory by removing empty nodes - static if (isShared) - { - if (atomicLoad(numNodes) > maxNodes && - atomicLoad(tmp.bytesUsed) == 0 && - tmp.keepAlive == 0) - { - removeNode(tmp); - } - } - else - { - if (numNodes > maxNodes && tmp.bytesUsed == 0) - { - removeNode(tmp); - } - } - - tmp = next; - } - - // Cannot create new AlignedBlockNode. Most likely the ParentAllocator ran out of resources - if (!insertNewNode()) - return null; - - tmp = cast(typeof(tmp)) root; - void[] result = tmp.bAlloc.allocate(n); - - static if (isShared) - { - atomicOp!"+="(root.bytesUsed, result.length); - } - else - { - root.bytesUsed += result.length; - } - - return result; - } - - // goodAllocSize should not use state - size_t goodAllocSize(const size_t n) - { - Allocator a = null; - return a.goodAllocSize(n); - } -} - -/** -`AlignedBlockList` represents a wrapper around a chain of allocators, allowing for fast deallocations -and preserving a low degree of fragmentation. -The allocator holds internally a doubly linked list of `Allocator` objects, which will serve allocations -in a most-recently-used fashion. Most recent allocators used for `allocate` calls, will be -moved to the front of the list. - -Although allocations are in theory served in linear searching time, `deallocate` calls take -$(BIGOH 1) time, by using aligned allocations. `ParentAllocator` must implement `alignedAllocate` -and it must be able to allocate `theAlignment` bytes at the same alignment. Each aligned allocation -done by `ParentAllocator` will contain metadata for an `Allocator`, followed by its payload. - -Params: - Allocator = the allocator which is used to manage each node; it must have a constructor which receives - `ubyte[]` and it must not have any parent allocators, except for the `NullAllocator` - ParentAllocator = each node draws memory from the parent allocator; it must support `alignedAllocate` - theAlignment = alignment of each block and at the same time length of each node -*/ -struct AlignedBlockList(Allocator, ParentAllocator, ulong theAlignment = (1 << 21)) -{ - version (StdDdoc) - { - import std.typecons : Ternary; - import std.traits : hasMember; - - /** - Returns a chunk of memory of size `n` - It finds the first node in the `AlignedBlockNode` list which has available memory, - and moves it to the front of the list. - - All empty nodes which cannot return new memory, are removed from the list. - - Params: - n = bytes to allocate - Returns: - A chunk of memory of the required length or `null` on failure or - */ - static if (hasMember!(ParentAllocator, "alignedAllocate")) - void[] allocate(size_t n); - - /** - Deallocates the buffer `b` given as parameter. Deallocations take place in constant - time, regardless of the number of nodes in the list. `b.ptr` is rounded down - to the nearest multiple of the `alignment` to quickly find the corresponding - `AlignedBlockNode`. - - Params: - b = buffer candidate for deallocation - Returns: - `true` on success and `false` on failure - */ - bool deallocate(void[] b); - - /** - Returns `Ternary.yes` if the buffer belongs to the parent allocator and - `Ternary.no` otherwise. - - Params: - b = buffer tested if owned by this allocator - Returns: - `Ternary.yes` if owned by this allocator and `Ternary.no` otherwise - */ - static if (hasMember!(ParentAllocator, "owns")) - Ternary owns(void[] b); - } - else - { - import std.math.traits : isPowerOf2; - static assert(isPowerOf2(alignment)); - mixin AlignedBlockListImpl!false; - } -} - -/// -@system unittest -{ - import std.experimental.allocator.building_blocks.ascending_page_allocator : AscendingPageAllocator; - import std.experimental.allocator.building_blocks.segregator : Segregator; - import std.experimental.allocator.building_blocks.bitmapped_block : BitmappedBlock; - import std.typecons : Ternary; - - /* - In this example we use 'AlignedBlockList' in conjunction with other allocators - in order to create a more complex allocator. - - The 'SuperAllocator' uses a 'Segregator' to distribute allocations to sub-allocators, - based on the requested size. - - Each sub-allocator is represented by an 'AlignedBlockList' of 'BitmappedBlocks'. - Each 'AlignedBlockList' draws memory from a root allocator which in this case is an 'AscendingPageAllocator' - - Such an allocator not only provides good performance, but also a low degree of memory fragmentation. - */ - alias SuperAllocator = Segregator!( - 32, - AlignedBlockList!(BitmappedBlock!32, AscendingPageAllocator*, 1 << 12), - Segregator!( - - 64, - AlignedBlockList!(BitmappedBlock!64, AscendingPageAllocator*, 1 << 12), - Segregator!( - - 128, - AlignedBlockList!(BitmappedBlock!128, AscendingPageAllocator*, 1 << 12), - AscendingPageAllocator* - ))); - - SuperAllocator a; - auto pageAlloc = AscendingPageAllocator(128 * 4096); - - // Set the parent allocator for all the sub allocators - a.allocatorForSize!256 = &pageAlloc; - a.allocatorForSize!128.parent = &pageAlloc; - a.allocatorForSize!64.parent = &pageAlloc; - a.allocatorForSize!32.parent = &pageAlloc; - - enum testNum = 10; - void[][testNum] buf; - - // Allocations of size 32 will go to the first 'AlignedBlockList' - foreach (j; 0 .. testNum) - { - buf[j] = a.allocate(32); - assert(buf[j].length == 32); - - // This is owned by the first 'AlignedBlockList' - assert(a.allocatorForSize!32.owns(buf[j]) == Ternary.yes); - } - - // Free the memory - foreach (j; 0 .. testNum) - assert(a.deallocate(buf[j])); - - // Allocations of size 64 will go to the second 'AlignedBlockList' - foreach (j; 0 .. testNum) - { - buf[j] = a.allocate(64); - assert(buf[j].length == 64); - - // This is owned by the second 'AlignedBlockList' - assert(a.allocatorForSize!64.owns(buf[j]) == Ternary.yes); - } - - // Free the memory - foreach (j; 0 .. testNum) - assert(a.deallocate(buf[j])); - - // Allocations of size 128 will go to the third 'AlignedBlockList' - foreach (j; 0 .. testNum) - { - buf[j] = a.allocate(128); - assert(buf[j].length == 128); - - // This is owned by the third 'AlignedBlockList' - assert(a.allocatorForSize!128.owns(buf[j]) == Ternary.yes); - } - - // Free the memory - foreach (j; 0 .. testNum) - assert(a.deallocate(buf[j])); - - // Allocations which exceed 128, will go to the 'AscendingPageAllocator*' - void[] b = a.allocate(256); - assert(b.length == 256); - a.deallocate(b); -} - -/** -`SharedAlignedBlockList` is the threadsafe version of `AlignedBlockList`. -The `Allocator` template parameter must refer a shared allocator. -Also, `ParentAllocator` must be a shared allocator, supporting `alignedAllocate`. - -Params: - Allocator = the shared allocator which is used to manage each node; it must have a constructor which receives - `ubyte[]` and it must not have any parent allocators, except for the `NullAllocator` - ParentAllocator = each node draws memory from the parent allocator; it must be shared and support `alignedAllocate` - theAlignment = alignment of each block and at the same time length of each node -*/ -shared struct SharedAlignedBlockList(Allocator, ParentAllocator, ulong theAlignment = (1 << 21)) -{ - version (StdDdoc) - { - import std.typecons : Ternary; - import std.traits : hasMember; - - /** - Returns a chunk of memory of size `n` - It finds the first node in the `AlignedBlockNode` list which has available memory, - and moves it to the front of the list. - - All empty nodes which cannot return new memory, are removed from the list. - - Params: - n = bytes to allocate - Returns: - A chunk of memory of the required length or `null` on failure or - */ - static if (hasMember!(ParentAllocator, "alignedAllocate")) - void[] allocate(size_t n); - - /** - Deallocates the buffer `b` given as parameter. Deallocations take place in constant - time, regardless of the number of nodes in the list. `b.ptr` is rounded down - to the nearest multiple of the `alignment` to quickly find the corresponding - `AlignedBlockNode`. - - Params: - b = buffer candidate for deallocation - Returns: - `true` on success and `false` on failure - */ - bool deallocate(void[] b); - - /** - Returns `Ternary.yes` if the buffer belongs to the parent allocator and - `Ternary.no` otherwise. - - Params: - b = buffer tested if owned by this allocator - Returns: - `Ternary.yes` if owned by this allocator and `Ternary.no` otherwise - */ - static if (hasMember!(ParentAllocator, "owns")) - Ternary owns(void[] b); - } - else - { - import std.math.traits : isPowerOf2; - static assert(isPowerOf2(alignment)); - mixin AlignedBlockListImpl!true; - } -} - -/// -@system unittest -{ - import std.experimental.allocator.building_blocks.region : SharedBorrowedRegion; - import std.experimental.allocator.building_blocks.ascending_page_allocator : SharedAscendingPageAllocator; - import std.experimental.allocator.building_blocks.null_allocator : NullAllocator; - import core.thread : ThreadGroup; - - enum numThreads = 8; - enum size = 2048; - enum maxIter = 10; - - /* - In this example we use 'SharedAlignedBlockList' together with - 'SharedBorrowedRegion', in order to create a fast, thread-safe allocator. - */ - alias SuperAllocator = SharedAlignedBlockList!( - SharedBorrowedRegion!(1), - SharedAscendingPageAllocator, - 4096); - - SuperAllocator a; - // The 'SuperAllocator' will draw memory from a 'SharedAscendingPageAllocator' - a.parent = SharedAscendingPageAllocator(4096 * 1024); - - // Launch 'numThreads', each performing allocations - void fun() - { - foreach (i; 0 .. maxIter) - { - void[] b = a.allocate(size); - assert(b.length == size); - } - } - - auto tg = new ThreadGroup; - foreach (i; 0 .. numThreads) - { - tg.create(&fun); - } - tg.joinAll(); -} - -version (StdUnittest) -{ - static void testrw(void[] b) - { - ubyte* buf = cast(ubyte*) b.ptr; - size_t len = (b.length).roundUpToMultipleOf(4096); - for (int i = 0; i < len; i += 4096) - { - buf[i] = (cast(ubyte) i % 256); - assert(buf[i] == (cast(ubyte) i % 256)); - } - } -} - -@system unittest -{ - import std.experimental.allocator.building_blocks.region; - import std.experimental.allocator.building_blocks.ascending_page_allocator; - import std.random; - import std.algorithm.sorting : sort; - import core.thread : ThreadGroup; - import core.internal.spinlock : SpinLock; - - enum pageSize = 4096; - enum numThreads = 10; - enum maxIter = 20; - enum totalAllocs = maxIter * numThreads; - size_t count = 0; - SpinLock lock = SpinLock(SpinLock.Contention.brief); - - alias SuperAllocator = SharedAlignedBlockList!( - SharedBorrowedRegion!(1), - SharedAscendingPageAllocator, - 1 << 16); - void[][totalAllocs] buf; - - SuperAllocator a; - a.parent = SharedAscendingPageAllocator(4096 * 1024); - - void fun() - { - auto rnd = Random(1000); - - foreach (i; 0 .. maxIter) - { - auto size = uniform(1, pageSize + 1, rnd); - void[] b = a.allocate(size); - assert(b.length == size); - testrw(b); - - lock.lock(); - buf[count++] = b; - lock.unlock(); - } - } - auto tg = new ThreadGroup; - foreach (i; 0 .. numThreads) - { - tg.create(&fun); - } - tg.joinAll(); - - sort!((a, b) => a.ptr < b.ptr)(buf[0 .. totalAllocs]); - foreach (i; 0 .. totalAllocs - 1) - { - assert(buf[i].ptr + a.goodAllocSize(buf[i].length) <= buf[i + 1].ptr); - } - - foreach (i; 0 .. totalAllocs) - { - assert(a.deallocate(buf[totalAllocs - 1 - i])); - } -} - -@system unittest -{ - import std.experimental.allocator.building_blocks.ascending_page_allocator : AscendingPageAllocator; - import std.experimental.allocator.building_blocks.segregator : Segregator; - import std.experimental.allocator.building_blocks.bitmapped_block : BitmappedBlock; - import std.random; - - alias SuperAllocator = Segregator!( - 256, - AlignedBlockList!(BitmappedBlock!256, AscendingPageAllocator*, 1 << 16), - Segregator!( - - 512, - AlignedBlockList!(BitmappedBlock!512, AscendingPageAllocator*, 1 << 16), - Segregator!( - - 1024, - AlignedBlockList!(BitmappedBlock!1024, AscendingPageAllocator*, 1 << 16), - Segregator!( - - 2048, - AlignedBlockList!(BitmappedBlock!2048, AscendingPageAllocator*, 1 << 16), - AscendingPageAllocator* - )))); - - SuperAllocator a; - auto pageAlloc = AscendingPageAllocator(4096 * 4096); - a.allocatorForSize!4096 = &pageAlloc; - a.allocatorForSize!2048.parent = &pageAlloc; - a.allocatorForSize!1024.parent = &pageAlloc; - a.allocatorForSize!512.parent = &pageAlloc; - a.allocatorForSize!256.parent = &pageAlloc; - - auto rnd = Random(1000); - - size_t maxIter = 10; - enum testNum = 10; - void[][testNum] buf; - int maxSize = 8192; - foreach (i; 0 .. maxIter) - { - foreach (j; 0 .. testNum) - { - auto size = uniform(1, maxSize + 1, rnd); - buf[j] = a.allocate(size); - assert(buf[j].length == size); - testrw(buf[j]); - } - - randomShuffle(buf[]); - - foreach (j; 0 .. testNum) - { - assert(a.deallocate(buf[j])); - } - } -} diff --git a/phobos/std/experimental/allocator/building_blocks/allocator_list.d b/phobos/std/experimental/allocator/building_blocks/allocator_list.d deleted file mode 100644 index ca83785..0000000 --- a/phobos/std/experimental/allocator/building_blocks/allocator_list.d +++ /dev/null @@ -1,1000 +0,0 @@ -// Written in the D programming language. -/** -Source: $(PHOBOSSRC std/experimental/allocator/building_blocks/allocator_list.d) -*/ -module std.experimental.allocator.building_blocks.allocator_list; - -import core.memory : pageSize; - -import std.experimental.allocator.building_blocks.null_allocator; -import std.experimental.allocator.common; -import std.experimental.allocator.gc_allocator; - -// Turn this on for debugging -// debug = allocator_list; - -/** - -Given an $(LINK2 https://en.wikipedia.org/wiki/Factory_(object-oriented_programming), -object factory) of type `Factory` or a factory function -`factoryFunction`, and optionally also `BookkeepingAllocator` as a supplemental -allocator for bookkeeping, `AllocatorList` creates an allocator that lazily -creates as many allocators are needed for satisfying client allocation requests. - -An embedded list builds a most-recently-used strategy: the most recent -allocators used in calls to either `allocate`, `owns` (successful calls -only), or `deallocate` are tried for new allocations in order of their most -recent use. Thus, although core operations take in theory $(BIGOH k) time for -`k` allocators in current use, in many workloads the factor is sublinear. -Details of the actual strategy may change in future releases. - -`AllocatorList` is primarily intended for coarse-grained handling of -allocators, i.e. the number of allocators in the list is expected to be -relatively small compared to the number of allocations handled by each -allocator. However, the per-allocator overhead is small so using -`AllocatorList` with a large number of allocators should be satisfactory as long -as the most-recently-used strategy is fast enough for the application. - -`AllocatorList` makes an effort to return allocated memory back when no -longer used. It does so by destroying empty allocators. However, in order to -avoid thrashing (excessive creation/destruction of allocators under certain use -patterns), it keeps unused allocators for a while. - -Params: -factoryFunction = A function or template function (including function literals). -New allocators are created by calling `factoryFunction(n)` with strictly -positive numbers `n`. Delegates that capture their enviroment are not created -amid concerns regarding garbage creation for the environment. When the factory -needs state, a `Factory` object should be used. - -BookkeepingAllocator = Allocator used for storing bookkeeping data. The size of -bookkeeping data is proportional to the number of allocators. If $(D -BookkeepingAllocator) is `NullAllocator`, then `AllocatorList` is -"ouroboros-style", i.e. it keeps the bookkeeping data in memory obtained from -the allocators themselves. Note that for ouroboros-style management, the size -`n` passed to `make` will be occasionally different from the size -requested by client code. - -Factory = Type of a factory object that returns new allocators on a need -basis. For an object `sweatshop` of type `Factory`, `sweatshop(n)` should -return an allocator able to allocate at least `n` bytes (i.e. `Factory` must -define `opCall(size_t)` to return an allocator object). Usually the capacity of -allocators created should be much larger than `n` such that an allocator can -be used for many subsequent allocations. `n` is passed only to ensure the -minimum necessary for the next allocation. The factory object is allowed to hold -state, which will be stored inside `AllocatorList` as a direct `public` member -called `factory`. - -*/ -struct AllocatorList(Factory, BookkeepingAllocator = GCAllocator) -{ - import core.lifetime : emplace; - import std.experimental.allocator.building_blocks.stats_collector - : StatsCollector, Options; - import std.traits : hasMember; - import std.typecons : Ternary; - - private enum ouroboros = is(BookkeepingAllocator == NullAllocator); - - /** - Alias for `typeof(Factory()(1))`, i.e. the type of the individual - allocators. - */ - alias Allocator = typeof(Factory.init(1)); - // Allocator used internally - private alias SAllocator = StatsCollector!(Allocator, Options.bytesUsed); - - private static struct Node - { - // Allocator in this node - SAllocator a; - Node* next; - - @disable this(this); - - // Is this node unused? - void setUnused() { next = &this; } - bool unused() const { return next is &this; } - - // Just forward everything to the allocator - alias a this; - } - - /** - If `BookkeepingAllocator` is not `NullAllocator`, `bkalloc` is - defined and accessible. - */ - - // State is stored in an array, but it has a list threaded through it by - // means of "nextIdx". - - // state - static if (!ouroboros) - { - static if (stateSize!BookkeepingAllocator) BookkeepingAllocator bkalloc; - else alias bkalloc = BookkeepingAllocator.instance; - } - static if (stateSize!Factory) - { - Factory factory; - } - private Node[] allocators; - private Node* root; - - static if (stateSize!Factory) - { - private auto make(size_t n) { return factory(n); } - } - else - { - private auto make(size_t n) { Factory f; return f(n); } - } - - /** - Constructs an `AllocatorList` given a factory object. This constructor is - defined only if `Factory` has state. - */ - static if (stateSize!Factory) - this(ref Factory plant) - { - factory = plant; - } - /// Ditto - static if (stateSize!Factory) - this(Factory plant) - { - factory = plant; - } - - static if (hasMember!(Allocator, "deallocateAll") - && hasMember!(Allocator, "owns")) - ~this() - { - deallocateAll; - } - - /** - The alignment offered. - */ - enum uint alignment = Allocator.alignment; - - /** - Allocate a block of size `s`. First tries to allocate from the existing - list of already-created allocators. If neither can satisfy the request, - creates a new allocator by calling `make(s)` and delegates the request - to it. However, if the allocation fresh off a newly created allocator - fails, subsequent calls to `allocate` will not cause more calls to $(D - make). - */ - void[] allocate(size_t s) - { - for (auto p = &root, n = *p; n; p = &n.next, n = *p) - { - auto result = n.allocate(s); - if (result.length != s) continue; - // Bring to front if not already - if (root != n) - { - *p = n.next; - n.next = root; - root = n; - } - return result; - } - - // Add a new allocator - if (auto a = addAllocator(s)) - { - auto result = a.allocate(s); - assert(owns(result) == Ternary.yes || !result.ptr); - return result; - } - return null; - } - - static if (hasMember!(Allocator, "allocateZeroed")) - package(std) void[] allocateZeroed()(size_t s) - { - for (auto p = &root, n = *p; n; p = &n.next, n = *p) - { - auto result = n.allocateZeroed(s); - if (result.length != s) continue; - // Bring to front if not already - if (root != n) - { - *p = n.next; - n.next = root; - root = n; - } - return result; - } - - // Add a new allocator - if (auto a = addAllocator(s)) - { - auto result = a.allocateZeroed(s); - assert(owns(result) == Ternary.yes || !result.ptr); - return result; - } - return null; - } - - /** - Allocate a block of size `s` with alignment `a`. First tries to allocate - from the existing list of already-created allocators. If neither can - satisfy the request, creates a new allocator by calling `make(s + a - 1)` - and delegates the request to it. However, if the allocation fresh off a - newly created allocator fails, subsequent calls to `alignedAllocate` - will not cause more calls to `make`. - */ - static if (hasMember!(Allocator, "alignedAllocate")) - void[] alignedAllocate(size_t s, uint theAlignment) - { - import std.algorithm.comparison : max; - import core.checkedint : addu; - - if (theAlignment == 0 || s == 0) - return null; - - for (auto p = &root, n = *p; n; p = &n.next, n = *p) - { - auto result = n.alignedAllocate(s, theAlignment); - if (result.length != s) continue; - // Bring to front if not already - if (root != n) - { - *p = n.next; - n.next = root; - root = n; - } - return result; - } - - bool overflow = false; - size_t maxSize = addu(s - 1, cast(size_t) theAlignment, overflow); - assert(!overflow, "Requested size is too large"); - if (overflow) - return null; - - // Add a new allocator - if (auto a = addAllocator(maxSize)) - { - auto result = a.alignedAllocate(s, theAlignment); - assert(owns(result) == Ternary.yes || !result.ptr); - return result; - } - return null; - } - - private void moveAllocators(void[] newPlace) - { - assert(newPlace.ptr.alignedAt(Node.alignof)); - assert(newPlace.length % Node.sizeof == 0); - auto newAllocators = cast(Node[]) newPlace; - assert(allocators.length <= newAllocators.length); - - // Move allocators - foreach (i, ref e; allocators) - { - if (e.unused) - { - newAllocators[i].setUnused; - continue; - } - import core.stdc.string : memcpy; - memcpy(&newAllocators[i].a, &e.a, e.a.sizeof); - if (e.next) - { - newAllocators[i].next = newAllocators.ptr - + (e.next - allocators.ptr); - } - else - { - newAllocators[i].next = null; - } - } - - // Mark the unused portion as unused - foreach (i; allocators.length .. newAllocators.length) - { - newAllocators[i].setUnused; - } - auto toFree = allocators; - - // Change state - root = newAllocators.ptr + (root - allocators.ptr); - allocators = newAllocators; - - // Free the olden buffer - static if (ouroboros) - { - static if (hasMember!(Allocator, "deallocate") - && hasMember!(Allocator, "owns")) - deallocate(toFree); - } - else - { - bkalloc.deallocate(toFree); - } - } - - static if (ouroboros) - private Node* addAllocator(size_t atLeastBytes) - { - void[] t = allocators; - static if (hasMember!(Allocator, "expand") - && hasMember!(Allocator, "owns")) - { - immutable bool expanded = t && this.expand(t, Node.sizeof); - } - else - { - enum expanded = false; - } - if (expanded) - { - import core.stdc.string : memcpy; - assert(t.length % Node.sizeof == 0); - assert(t.ptr.alignedAt(Node.alignof)); - allocators = cast(Node[]) t; - allocators[$ - 1].setUnused; - auto newAlloc = SAllocator(make(atLeastBytes)); - memcpy(&allocators[$ - 1].a, &newAlloc, newAlloc.sizeof); - emplace(&newAlloc); - } - else - { - immutable toAlloc = (allocators.length + 1) * Node.sizeof - + atLeastBytes + 128; - auto newAlloc = SAllocator(make(toAlloc)); - auto newPlace = newAlloc.allocate( - (allocators.length + 1) * Node.sizeof); - if (!newPlace) return null; - moveAllocators(newPlace); - import core.stdc.string : memcpy; - memcpy(&allocators[$ - 1].a, &newAlloc, newAlloc.sizeof); - emplace(&newAlloc); - assert(allocators[$ - 1].owns(allocators) == Ternary.yes); - } - // Insert as new root - if (root != &allocators[$ - 1]) - { - allocators[$ - 1].next = root; - root = &allocators[$ - 1]; - } - else - { - // This is the first one - root.next = null; - } - assert(!root.unused); - return root; - } - - static if (!ouroboros) - private Node* addAllocator(size_t atLeastBytes) - { - void[] t = allocators; - static if (hasMember!(BookkeepingAllocator, "expand")) - immutable bool expanded = bkalloc.expand(t, Node.sizeof); - else - immutable bool expanded = false; - if (expanded) - { - assert(t.length % Node.sizeof == 0); - assert(t.ptr.alignedAt(Node.alignof)); - allocators = cast(Node[]) t; - allocators[$ - 1].setUnused; - } - else - { - // Could not expand, create a new block - t = bkalloc.allocate((allocators.length + 1) * Node.sizeof); - assert(t.length % Node.sizeof == 0); - if (!t.ptr) return null; - moveAllocators(t); - } - assert(allocators[$ - 1].unused); - auto newAlloc = SAllocator(make(atLeastBytes)); - import core.stdc.string : memcpy; - memcpy(&allocators[$ - 1].a, &newAlloc, newAlloc.sizeof); - emplace(&newAlloc); - // Creation succeeded, insert as root - if (allocators.length == 1) - allocators[$ - 1].next = null; - else - allocators[$ - 1].next = root; - assert(allocators[$ - 1].a.bytesUsed == 0); - root = &allocators[$ - 1]; - return root; - } - - /** - Defined only if `Allocator` defines `owns`. Tries each allocator in - turn, in most-recently-used order. If the owner is found, it is moved to - the front of the list as a side effect under the assumption it will be used - soon. - - Returns: `Ternary.yes` if one allocator was found to return `Ternary.yes`, - `Ternary.no` if all component allocators returned `Ternary.no`, and - `Ternary.unknown` if no allocator returned `Ternary.yes` and at least one - returned `Ternary.unknown`. - */ - static if (hasMember!(Allocator, "owns")) - Ternary owns(void[] b) - { - auto result = Ternary.no; - for (auto p = &root, n = *p; n; p = &n.next, n = *p) - { - immutable t = n.owns(b); - if (t != Ternary.yes) - { - if (t == Ternary.unknown) result = t; - continue; - } - // Move the owner to front, speculating it'll be used - if (n != root) - { - *p = n.next; - n.next = root; - root = n; - } - return Ternary.yes; - } - return result; - } - - /** - Defined only if `Allocator.expand` is defined. Finds the owner of `b` - and calls `expand` for it. The owner is not brought to the head of the - list. - */ - static if (hasMember!(Allocator, "expand") - && hasMember!(Allocator, "owns")) - bool expand(ref void[] b, size_t delta) - { - if (!b) return delta == 0; - for (auto p = &root, n = *p; n; p = &n.next, n = *p) - { - if (n.owns(b) == Ternary.yes) return n.expand(b, delta); - } - return false; - } - - /** - Defined only if `Allocator.reallocate` is defined. Finds the owner of - `b` and calls `reallocate` for it. If that fails, calls the global - `reallocate`, which allocates a new block and moves memory. - */ - static if (hasMember!(Allocator, "reallocate")) - bool reallocate(ref void[] b, size_t s) - { - // First attempt to reallocate within the existing node - if (!b.ptr) - { - b = allocate(s); - return b.length == s; - } - for (auto p = &root, n = *p; n; p = &n.next, n = *p) - { - if (n.owns(b) == Ternary.yes) return n.reallocate(b, s); - } - // Failed, but we may find new memory in a new node. - return .reallocate(this, b, s); - } - - /** - Defined if `Allocator.deallocate` and `Allocator.owns` are defined. - */ - static if (hasMember!(Allocator, "deallocate") - && hasMember!(Allocator, "owns")) - bool deallocate(void[] b) - { - if (!b.ptr) return true; - assert(allocators.length); - assert(owns(b) == Ternary.yes); - bool result; - for (auto p = &root, n = *p; ; p = &n.next, n = *p) - { - assert(n); - if (n.owns(b) != Ternary.yes) continue; - result = n.deallocate(b); - // Bring to front - if (n != root) - { - *p = n.next; - n.next = root; - root = n; - } - if (n.empty != Ternary.yes) return result; - break; - } - // Hmmm... should we return this allocator back to the wild? Let's - // decide if there are TWO empty allocators we can release ONE. This - // is to avoid thrashing. - // Note that loop starts from the second element. - for (auto p = &root.next, n = *p; n; p = &n.next, n = *p) - { - if (n.unused || n.empty != Ternary.yes) continue; - // Used and empty baby, nuke it! - n.a.destroy; - *p = n.next; - n.setUnused; - break; - } - return result; - } - - /** - Defined only if `Allocator.owns` and `Allocator.deallocateAll` are - defined. - */ - static if (ouroboros && hasMember!(Allocator, "deallocateAll") - && hasMember!(Allocator, "owns")) - bool deallocateAll() - { - Node* special; - foreach (ref n; allocators) - { - if (n.unused) continue; - if (n.owns(allocators) == Ternary.yes) - { - special = &n; - continue; - } - n.a.deallocateAll; - n.a.destroy; - } - assert(special || !allocators.ptr); - if (special) - { - static if (stateSize!SAllocator) - { - import core.stdc.string : memcpy; - SAllocator specialCopy; - assert(special.a.sizeof == specialCopy.sizeof); - memcpy(&specialCopy, &special.a, specialCopy.sizeof); - emplace(&special.a); - specialCopy.deallocateAll(); - } - else - { - special.deallocateAll(); - } - } - allocators = null; - root = null; - return true; - } - - static if (!ouroboros && hasMember!(Allocator, "deallocateAll") - && hasMember!(Allocator, "owns")) - bool deallocateAll() - { - foreach (ref n; allocators) - { - if (n.unused) continue; - n.a.deallocateAll; - n.a.destroy; - } - bkalloc.deallocate(allocators); - allocators = null; - root = null; - return true; - } - - /** - Returns `Ternary.yes` if no allocators are currently active, - `Ternary.no` otherwise. This methods never returns `Ternary.unknown`. - */ - pure nothrow @safe @nogc - Ternary empty() const - { - return Ternary(!allocators.length); - } -} - -/// Ditto -template AllocatorList(alias factoryFunction, - BookkeepingAllocator = GCAllocator) -{ - alias A = typeof(factoryFunction(1)); - static assert( - // is a template function (including literals) - is(typeof({A function(size_t) @system x = factoryFunction!size_t;})) - || - // or a function (including literals) - is(typeof({A function(size_t) @system x = factoryFunction;})) - , - "Only function names and function literals that take size_t" - ~ " and return an allocator are accepted, not " - ~ typeof(factoryFunction).stringof - ); - static struct Factory - { - A opCall(size_t n) { return factoryFunction(n); } - } - alias AllocatorList = .AllocatorList!(Factory, BookkeepingAllocator); -} - -/// -version (Posix) @system unittest -{ - import std.algorithm.comparison : max; - import std.experimental.allocator.building_blocks.free_list : ContiguousFreeList; - import std.experimental.allocator.building_blocks.null_allocator : NullAllocator; - import std.experimental.allocator.building_blocks.region : Region; - import std.experimental.allocator.building_blocks.segregator : Segregator; - import std.experimental.allocator.gc_allocator : GCAllocator; - import std.experimental.allocator.mmap_allocator : MmapAllocator; - - // Ouroboros allocator list based upon 4MB regions, fetched directly from - // mmap. All memory is released upon destruction. - alias A1 = AllocatorList!((n) => Region!MmapAllocator(max(n, 1024 * 4096)), - NullAllocator); - - // Allocator list based upon 4MB regions, fetched from the garbage - // collector. All memory is released upon destruction. - alias A2 = AllocatorList!((n) => Region!GCAllocator(max(n, 1024 * 4096))); - - // Ouroboros allocator list based upon 4MB regions, fetched from the garbage - // collector. Memory is left to the collector. - alias A3 = AllocatorList!( - (n) => Region!NullAllocator(new ubyte[max(n, 1024 * 4096)]), - NullAllocator); - - // Allocator list that creates one freelist for all objects - alias A4 = - Segregator!( - 64, AllocatorList!( - (n) => ContiguousFreeList!(NullAllocator, 0, 64)( - cast(ubyte[])(GCAllocator.instance.allocate(4096)))), - GCAllocator); - - A4 a; - auto small = a.allocate(64); - assert(small); - a.deallocate(small); - auto b1 = a.allocate(1024 * 8192); - assert(b1 !is null); // still works due to overdimensioning - b1 = a.allocate(1024 * 10); - assert(b1.length == 1024 * 10); -} - -@system unittest -{ - // Create an allocator based upon 4MB regions, fetched from the GC heap. - import std.algorithm.comparison : max; - import std.experimental.allocator.building_blocks.region : Region; - AllocatorList!((n) => Region!GCAllocator(new ubyte[max(n, 1024 * 4096)]), - NullAllocator) a; - const b1 = a.allocate(1024 * 8192); - assert(b1 !is null); // still works due to overdimensioning - const b2 = a.allocate(1024 * 10); - assert(b2.length == 1024 * 10); - a.deallocateAll(); -} - -@system unittest -{ - // Create an allocator based upon 4MB regions, fetched from the GC heap. - import std.algorithm.comparison : max; - import std.experimental.allocator.building_blocks.region : BorrowedRegion; - AllocatorList!((n) => BorrowedRegion!()(new ubyte[max(n, 1024 * 4096)])) a; - auto b1 = a.alignedAllocate(1024 * 8192, 1024); - assert(b1 !is null); // still works due to overdimensioning - assert(b1.length == 1024 * 8192); - assert(b1.ptr.alignedAt(1024)); - assert(a.allocators.length == 1); - - b1 = a.alignedAllocate(0, 1024); - assert(b1.length == 0); - assert(a.allocators.length == 1); - - b1 = a.allocate(1024 * 10); - assert(b1.length == 1024 * 10); - - assert(a.reallocate(b1, 1024)); - assert(b1.length == 1024); - - a.deallocateAll(); -} - -@system unittest -{ - import core.exception : AssertError; - import std.exception : assertThrown; - - // Create an allocator based upon 4MB regions, fetched from the GC heap. - import std.algorithm.comparison : max; - import std.experimental.allocator.building_blocks.region : BorrowedRegion; - AllocatorList!((n) => BorrowedRegion!()(new ubyte[max(n, 1024 * 4096)])) a; - auto b1 = a.alignedAllocate(0, 1); - assert(b1 is null); - - b1 = a.alignedAllocate(1, 0); - assert(b1 is null); - - b1 = a.alignedAllocate(0, 0); - assert(b1 is null); - - assertThrown!AssertError(a.alignedAllocate(size_t.max, 1024)); - a.deallocateAll(); -} - -@system unittest -{ - import std.typecons : Ternary; - - // Create an allocator based upon 4MB regions, fetched from the GC heap. - import std.algorithm.comparison : max; - import std.experimental.allocator.building_blocks.region : BorrowedRegion; - AllocatorList!((n) => BorrowedRegion!()(new ubyte[max(n, 1024 * 4096)])) a; - auto b0 = a.alignedAllocate(1, 1024); - assert(b0.length == 1); - assert(b0.ptr.alignedAt(1024)); - assert(a.allocators.length == 1); - - auto b1 = a.alignedAllocate(1024 * 4096, 1024); - assert(b1.length == 1024 * 4096); - assert(b1.ptr.alignedAt(1024)); - assert(a.allocators.length == 2); - - auto b2 = a.alignedAllocate(1024, 128); - assert(b2.length == 1024); - assert(b2.ptr.alignedAt(128)); - assert(a.allocators.length == 2); - - auto b3 = a.allocate(1024); - assert(b3.length == 1024); - assert(a.allocators.length == 2); - - auto b4 = a.allocate(1024 * 4096); - assert(b4.length == 1024 * 4096); - assert(a.allocators.length == 3); - - assert(a.root.empty == Ternary.no); - assert(a.deallocate(b4)); - assert(a.root.empty == Ternary.yes); - - assert(a.deallocate(b1)); - a.deallocateAll(); -} - -@system unittest -{ - // Create an allocator based upon 4MB regions, fetched from the GC heap. - import std.algorithm.comparison : max; - import std.experimental.allocator.building_blocks.region : BorrowedRegion; - AllocatorList!((n) => BorrowedRegion!()(new ubyte[max(n, 1024 * 4096)])) a; - auto b1 = a.allocate(1024 * 8192); - assert(b1 !is null); // still works due to overdimensioning - b1 = a.allocate(1024 * 10); - assert(b1.length == 1024 * 10); - assert(a.reallocate(b1, 1024)); - assert(b1.length == 1024); - a.deallocateAll(); -} - -@system unittest -{ - import std.algorithm.comparison : max; - import std.experimental.allocator.building_blocks.region : BorrowedRegion; - import std.experimental.allocator.mallocator : Mallocator; - import std.typecons : Ternary; - AllocatorList!((n) => BorrowedRegion!()(new ubyte[max(n, 1024 * 4096)]), Mallocator) a; - auto b1 = a.allocate(1024 * 8192); - assert(b1 !is null); - b1 = a.allocate(1024 * 10); - assert(b1.length == 1024 * 10); - assert((() pure nothrow @safe @nogc => a.expand(b1, 10))()); - assert(b1.length == 1025 * 10); - a.allocate(1024 * 4095); - assert((() pure nothrow @safe @nogc => a.empty)() == Ternary.no); - // Ensure deallocateAll infers from parent - assert((() nothrow @nogc => a.deallocateAll())()); - assert((() pure nothrow @safe @nogc => a.empty)() == Ternary.yes); -} - -@system unittest -{ - import std.experimental.allocator.building_blocks.region : Region; - enum bs = GCAllocator.alignment; - AllocatorList!((n) => Region!GCAllocator(256 * bs)) a; - auto b1 = a.allocate(192 * bs); - assert(b1.length == 192 * bs); - assert(a.allocators.length == 1); - auto b2 = a.allocate(64 * bs); - assert(b2.length == 64 * bs); - assert(a.allocators.length == 1); - auto b3 = a.allocate(192 * bs); - assert(b3.length == 192 * bs); - assert(a.allocators.length == 2); - // Ensure deallocate inherits from parent allocators - () nothrow @nogc { a.deallocate(b1); }(); - b1 = a.allocate(64 * bs); - assert(b1.length == 64 * bs); - assert(a.allocators.length == 2); - a.deallocateAll(); -} - -@system unittest -{ - import std.experimental.allocator.building_blocks.ascending_page_allocator : AscendingPageAllocator; - import std.experimental.allocator.mallocator : Mallocator; - import std.algorithm.comparison : max; - import std.typecons : Ternary; - - static void testrw(void[] b) - { - ubyte* buf = cast(ubyte*) b.ptr; - for (int i = 0; i < b.length; i += pageSize) - { - buf[i] = cast(ubyte) (i % 256); - assert(buf[i] == cast(ubyte) (i % 256)); - } - } - - enum numPages = 2; - AllocatorList!((n) => AscendingPageAllocator(max(n, numPages * pageSize)), Mallocator) a; - - void[] b1 = a.allocate(1); - assert(b1.length == 1); - b1 = a.allocate(2); - assert(b1.length == 2); - testrw(b1); - assert(a.root.a.parent.getAvailableSize() == 0); - - void[] b2 = a.allocate((numPages + 1) * pageSize); - assert(b2.length == (numPages + 1) * pageSize); - testrw(b2); - - void[] b3 = a.allocate(3); - assert(b3.length == 3); - testrw(b3); - - void[] b4 = a.allocate(0); - assert(b4.length == 0); - - assert(a.allocators.length == 3); - assert(a.owns(b1) == Ternary.yes); - assert(a.owns(b2) == Ternary.yes); - assert(a.owns(b3) == Ternary.yes); - - assert(a.expand(b1, pageSize - b1.length)); - assert(b1.length == pageSize); - assert(!a.expand(b1, 1)); - assert(!a.expand(b2, 1)); - - testrw(b1); - testrw(b2); - testrw(b3); - - assert(a.deallocate(b1)); - assert(a.deallocate(b2)); - - assert(a.deallocateAll()); -} - -@system unittest -{ - import std.experimental.allocator.building_blocks.ascending_page_allocator : AscendingPageAllocator; - import std.experimental.allocator.mallocator : Mallocator; - import std.algorithm.comparison : max; - import std.typecons : Ternary; - - static void testrw(void[] b) - { - ubyte* buf = cast(ubyte*) b.ptr; - for (int i = 0; i < b.length; i += pageSize) - { - buf[i] = cast(ubyte) (i % 256); - assert(buf[i] == cast(ubyte) (i % 256)); - } - } - - enum numPages = 2; - AllocatorList!((n) => AscendingPageAllocator(max(n, numPages * pageSize)), NullAllocator) a; - - void[] b1 = a.allocate(1); - assert(b1.length == 1); - b1 = a.allocate(2); - assert(b1.length == 2); - testrw(b1); - - void[] b2 = a.allocate((numPages + 1) * pageSize); - assert(b2.length == (numPages + 1) * pageSize); - testrw(b2); - - void[] b3 = a.allocate(3); - assert(b3.length == 3); - testrw(b3); - - void[] b4 = a.allocate(0); - assert(b4.length == 0); - - assert(a.allocators.length == 3); - assert(a.owns(b1) == Ternary.yes); - assert(a.owns(b2) == Ternary.yes); - assert(a.owns(b3) == Ternary.yes); - - assert(a.expand(b1, pageSize - b1.length)); - assert(b1.length == pageSize); - assert(!a.expand(b1, 1)); - assert(!a.expand(b2, 1)); - - testrw(b1); - testrw(b2); - testrw(b3); - - assert(a.deallocate(b1)); - assert(a.deallocate(b2)); - - const alignment = cast(uint) (70 * pageSize); - b3 = a.alignedAllocate(70 * pageSize, alignment); - assert(b3.length == 70 * pageSize); - assert(b3.ptr.alignedAt(alignment)); - testrw(b3); - assert(a.allocators.length == 4); - assert(a.deallocate(b3)); - - - assert(a.deallocateAll()); -} - -@system unittest -{ - import std.experimental.allocator.building_blocks.ascending_page_allocator : AscendingPageAllocator; - import std.experimental.allocator.mallocator : Mallocator; - import std.algorithm.comparison : max; - import std.typecons : Ternary; - - static void testrw(void[] b) - { - ubyte* buf = cast(ubyte*) b.ptr; - for (int i = 0; i < b.length; i += pageSize) - { - buf[i] = cast(ubyte) (i % 256); - assert(buf[i] == cast(ubyte) (i % 256)); - } - } - - enum numPages = 5; - AllocatorList!((n) => AscendingPageAllocator(max(n, numPages * pageSize)), NullAllocator) a; - const alignment = cast(uint) (2 * pageSize); - auto b = a.alignedAllocate(1, alignment); - assert(b.length == 1); - assert(a.expand(b, pageSize - 1)); - assert(b.ptr.alignedAt(alignment)); - assert(b.length == pageSize); - - b = a.allocate(pageSize); - assert(b.length == pageSize); - assert(a.allocators.length == 1); - - assert(a.allocate(pageSize * 5).length == pageSize * 5); - assert(a.allocators.length == 2); - - assert(a.deallocateAll()); -} - -@system unittest -{ - import std.experimental.allocator.building_blocks.ascending_page_allocator : AscendingPageAllocator; - import std.algorithm.comparison : max; - - enum maxIter = 100; - enum numPages = 10; - const chunkSize = pageSize / 8; - - AllocatorList!((n) => AscendingPageAllocator(max(n, numPages * pageSize)), NullAllocator) a; - foreach (i; 0 .. maxIter) - { - auto b1 = a.allocate(chunkSize); - assert(b1.length == chunkSize); - - assert(a.deallocate(b1)); - } - - assert(a.deallocateAll()); -} diff --git a/phobos/std/experimental/allocator/building_blocks/ascending_page_allocator.d b/phobos/std/experimental/allocator/building_blocks/ascending_page_allocator.d deleted file mode 100644 index bfb78c5..0000000 --- a/phobos/std/experimental/allocator/building_blocks/ascending_page_allocator.d +++ /dev/null @@ -1,1007 +0,0 @@ -// Written in the D programming language. -/** -Source: $(PHOBOSSRC std/experimental/allocator/building_blocks/ascending_page_allocator.d) -*/ -module std.experimental.allocator.building_blocks.ascending_page_allocator; - -import core.memory : pageSize; - -import std.experimental.allocator.common; - -// Common implementations for shared and thread local AscendingPageAllocator -private mixin template AscendingPageAllocatorImpl(bool isShared) -{ - bool deallocate(void[] buf) nothrow @nogc - { - size_t goodSize = goodAllocSize(buf.length); - version (Posix) - { - import core.sys.posix.sys.mman : mmap, MAP_FAILED, MAP_PRIVATE, - MAP_ANON, MAP_FIXED, PROT_NONE, munmap; - - auto ptr = mmap(buf.ptr, goodSize, PROT_NONE, MAP_ANON | MAP_PRIVATE | MAP_FIXED, -1, 0); - if (ptr == MAP_FAILED) - return false; - } - else version (Windows) - { - import core.sys.windows.winbase : VirtualFree; - import core.sys.windows.winnt : MEM_DECOMMIT; - - auto ret = VirtualFree(buf.ptr, goodSize, MEM_DECOMMIT); - if (ret == 0) - return false; - } - else - { - static assert(0, "Unsupported OS"); - } - - static if (!isShared) - { - pagesUsed -= goodSize / pageSize; - } - - return true; - } - - Ternary owns(void[] buf) nothrow @nogc - { - if (!data) - return Ternary.no; - return Ternary(buf.ptr >= data && buf.ptr < buf.ptr + numPages * pageSize); - } - - bool deallocateAll() nothrow @nogc - { - version (Posix) - { - import core.sys.posix.sys.mman : munmap; - auto ret = munmap(cast(void*) data, numPages * pageSize); - if (ret != 0) - assert(0, "Failed to unmap memory, munmap failure"); - } - else version (Windows) - { - import core.sys.windows.winbase : VirtualFree; - import core.sys.windows.winnt : MEM_RELEASE; - auto ret = VirtualFree(cast(void*) data, 0, MEM_RELEASE); - if (ret == 0) - assert(0, "Failed to unmap memory, VirtualFree failure"); - } - else - { - static assert(0, "Unsupported OS version"); - } - data = null; - offset = null; - return true; - } - - size_t goodAllocSize(size_t n) nothrow @nogc - { - return n.roundUpToMultipleOf(cast(uint) pageSize); - } - - this(size_t n) nothrow @nogc - { - static if (isShared) - { - lock = SpinLock(SpinLock.Contention.brief); - } - - pageSize = .pageSize; - numPages = n.roundUpToMultipleOf(cast(uint) pageSize) / pageSize; - - version (Posix) - { - import core.sys.posix.sys.mman : mmap, MAP_ANON, PROT_NONE, - MAP_PRIVATE, MAP_FAILED; - - data = cast(typeof(data)) mmap(null, pageSize * numPages, - PROT_NONE, MAP_ANON | MAP_PRIVATE, -1, 0); - if (data == MAP_FAILED) - assert(0, "Failed to mmap memory"); - } - else version (Windows) - { - import core.sys.windows.winbase : VirtualAlloc; - import core.sys.windows.winnt : MEM_RESERVE, PAGE_NOACCESS; - - data = cast(typeof(data)) VirtualAlloc(null, pageSize * numPages, - MEM_RESERVE, PAGE_NOACCESS); - if (!data) - assert(0, "Failed to VirtualAlloc memory"); - } - else - { - static assert(0, "Unsupported OS version"); - } - - offset = data; - readWriteLimit = data; - } - - size_t getAvailableSize() nothrow @nogc - { - static if (isShared) - { - lock.lock(); - } - - auto size = numPages * pageSize + data - offset; - static if (isShared) - { - lock.unlock(); - } - return size; - } - - // Sets the protection of a memory range to read/write - private bool extendMemoryProtection(void* start, size_t size) nothrow @nogc - { - version (Posix) - { - import core.sys.posix.sys.mman : mprotect, PROT_WRITE, PROT_READ; - - auto ret = mprotect(start, size, PROT_WRITE | PROT_READ); - return ret == 0; - } - else version (Windows) - { - import core.sys.windows.winbase : VirtualAlloc; - import core.sys.windows.winnt : MEM_COMMIT, PAGE_READWRITE; - - auto ret = VirtualAlloc(start, size, MEM_COMMIT, PAGE_READWRITE); - return ret != null; - } - else - { - static assert(0, "Unsupported OS"); - } - } -} - -/** -`AscendingPageAllocator` is a fast and safe allocator that rounds all allocations -to multiples of the system's page size. It reserves a range of virtual addresses -(using `mmap` on Posix and `VirtualAlloc` on Windows) and allocates memory at consecutive virtual -addresses. - -When a chunk of memory is requested, the allocator finds a range of -virtual pages that satisfy the requested size, changing their protection to -read/write using OS primitives (`mprotect` and `VirtualProtect`, respectively). -The physical memory is allocated on demand, when the pages are accessed. - -Deallocation removes any read/write permissions from the target pages -and notifies the OS to reclaim the physical memory, while keeping the virtual -memory. - -Because the allocator does not reuse memory, any dangling references to -deallocated memory will always result in deterministically crashing the process. - -See_Also: -$(HTTPS microsoft.com/en-us/research/wp-content/uploads/2017/03/kedia2017mem.pdf, Simple Fast and Safe Manual Memory Management) for the general approach. -*/ -struct AscendingPageAllocator -{ - import std.typecons : Ternary; - - // Docs for mixin functions - version (StdDdoc) - { - /** - Rounds the mapping size to the next multiple of the page size and calls - the OS primitive responsible for creating memory mappings: `mmap` on POSIX and - `VirtualAlloc` on Windows. - - Params: - n = mapping size in bytes - */ - this(size_t n) nothrow @nogc; - - /** - Rounds the requested size to the next multiple of the page size. - */ - size_t goodAllocSize(size_t n) nothrow @nogc; - - /** - Decommit all physical memory associated with the buffer given as parameter, - but keep the range of virtual addresses. - - On POSIX systems `deallocate` calls `mmap` with `MAP_FIXED' a second time to decommit the memory. - On Windows, it uses `VirtualFree` with `MEM_DECOMMIT`. - */ - void deallocate(void[] b) nothrow @nogc; - - /** - Returns `Ternary.yes` if the passed buffer is inside the range of virtual adresses. - Does not guarantee that the passed buffer is still valid. - */ - Ternary owns(void[] buf) nothrow @nogc; - - /** - Removes the memory mapping causing all physical memory to be decommited and - the virtual address space to be reclaimed. - */ - bool deallocateAll() nothrow @nogc; - - /** - Returns the available size for further allocations in bytes. - */ - size_t getAvailableSize() nothrow @nogc; - } - -private: - size_t pageSize; - size_t numPages; - - // The start of the virtual address range - void* data; - - // Keeps track of there the next allocation should start - void* offset; - - // Number of pages which contain alive objects - size_t pagesUsed; - - // On allocation requests, we allocate an extra 'extraAllocPages' pages - // The address up to which we have permissions is stored in 'readWriteLimit' - void* readWriteLimit; - enum extraAllocPages = 1000; - -public: - enum uint alignment = 4096; - - // Inject common function implementations - mixin AscendingPageAllocatorImpl!false; - - /** - Rounds the allocation size to the next multiple of the page size. - The allocation only reserves a range of virtual pages but the actual - physical memory is allocated on demand, when accessing the memory. - - Params: - n = Bytes to allocate - - Returns: - `null` on failure or if the requested size exceeds the remaining capacity. - */ - void[] allocate(size_t n) nothrow @nogc - { - import std.algorithm.comparison : min; - - immutable pagedBytes = numPages * pageSize; - size_t goodSize = goodAllocSize(n); - - // Requested exceeds the virtual memory range - if (goodSize > pagedBytes || offset - data > pagedBytes - goodSize) - return null; - - // Current allocation exceeds readable/writable memory area - if (offset + goodSize > readWriteLimit) - { - // Extend r/w memory range to new limit - void* newReadWriteLimit = min(data + pagedBytes, - offset + goodSize + extraAllocPages * pageSize); - if (newReadWriteLimit != readWriteLimit) - { - assert(newReadWriteLimit > readWriteLimit); - if (!extendMemoryProtection(readWriteLimit, newReadWriteLimit - readWriteLimit)) - return null; - - readWriteLimit = newReadWriteLimit; - } - } - - void* result = offset; - offset += goodSize; - pagesUsed += goodSize / pageSize; - - return cast(void[]) result[0 .. n]; - } - - /** - Rounds the allocation size to the next multiple of the page size. - The allocation only reserves a range of virtual pages but the actual - physical memory is allocated on demand, when accessing the memory. - - The allocated memory is aligned to the specified alignment `a`. - - Params: - n = Bytes to allocate - a = Alignment - - Returns: - `null` on failure or if the requested size exceeds the remaining capacity. - */ - void[] alignedAllocate(size_t n, uint a) nothrow @nogc - { - void* alignedStart = cast(void*) roundUpToMultipleOf(cast(size_t) offset, a); - assert(alignedStart.alignedAt(a)); - immutable pagedBytes = numPages * pageSize; - size_t goodSize = goodAllocSize(n); - if (goodSize > pagedBytes || - alignedStart - data > pagedBytes - goodSize) - return null; - - // Same logic as allocate, only that the buffer must be properly aligned - auto oldOffset = offset; - offset = alignedStart; - auto result = allocate(n); - if (!result) - offset = oldOffset; - return result; - } - - /** - If the passed buffer is not the last allocation, then `delta` can be - at most the number of bytes left on the last page. - Otherwise, we can expand the last allocation until the end of the virtual - address range. - */ - bool expand(ref void[] b, size_t delta) nothrow @nogc - { - import std.algorithm.comparison : min; - - if (!delta) return true; - if (b is null) return false; - - size_t goodSize = goodAllocSize(b.length); - size_t bytesLeftOnPage = goodSize - b.length; - - // If this is not the last allocation, we can only expand until - // completely filling the last page covered by this buffer - if (b.ptr + goodSize != offset && delta > bytesLeftOnPage) - return false; - - size_t extraPages = 0; - - // If the extra `delta` bytes requested do not fit the last page - // compute how many extra pages are neeeded - if (delta > bytesLeftOnPage) - { - extraPages = goodAllocSize(delta - bytesLeftOnPage) / pageSize; - } - else - { - b = cast(void[]) b.ptr[0 .. b.length + delta]; - return true; - } - - if (extraPages > numPages || offset - data > pageSize * (numPages - extraPages)) - return false; - - void* newPtrEnd = b.ptr + goodSize + extraPages * pageSize; - if (newPtrEnd > readWriteLimit) - { - void* newReadWriteLimit = min(data + numPages * pageSize, - newPtrEnd + extraAllocPages * pageSize); - if (newReadWriteLimit > readWriteLimit) - { - if (!extendMemoryProtection(readWriteLimit, newReadWriteLimit - readWriteLimit)) - return false; - - readWriteLimit = newReadWriteLimit; - } - } - - pagesUsed += extraPages; - offset += extraPages * pageSize; - b = cast(void[]) b.ptr[0 .. b.length + delta]; - return true; - } - - /** - Returns `Ternary.yes` if the allocator does not contain any alive objects - and `Ternary.no` otherwise. - */ - Ternary empty() nothrow @nogc - { - return Ternary(pagesUsed == 0); - } - - /** - Unmaps the whole virtual address range on destruction. - */ - ~this() nothrow @nogc - { - if (data) - deallocateAll(); - } -} - -/// -@system @nogc nothrow unittest -{ - import core.memory : pageSize; - - size_t numPages = 100; - void[] buf; - void[] prevBuf = null; - AscendingPageAllocator a = AscendingPageAllocator(numPages * pageSize); - - foreach (i; 0 .. numPages) - { - // Allocation is rounded up to page size - buf = a.allocate(pageSize - 100); - assert(buf.length == pageSize - 100); - - // Allocations are served at increasing addresses - if (prevBuf) - assert(prevBuf.ptr + pageSize == buf.ptr); - - assert(a.deallocate(buf)); - prevBuf = buf; - } -} - -/** -`SharedAscendingPageAllocator` is the threadsafe version of `AscendingPageAllocator`. -*/ -shared struct SharedAscendingPageAllocator -{ - import std.typecons : Ternary; - import core.internal.spinlock : SpinLock; - - // Docs for mixin functions - version (StdDdoc) - { - /** - Rounds the mapping size to the next multiple of the page size and calls - the OS primitive responsible for creating memory mappings: `mmap` on POSIX and - `VirtualAlloc` on Windows. - - Params: - n = mapping size in bytes - */ - this(size_t n) nothrow @nogc; - - /** - Rounds the requested size to the next multiple of the page size. - */ - size_t goodAllocSize(size_t n) nothrow @nogc; - - /** - Decommit all physical memory associated with the buffer given as parameter, - but keep the range of virtual addresses. - - On POSIX systems `deallocate` calls `mmap` with `MAP_FIXED' a second time to decommit the memory. - On Windows, it uses `VirtualFree` with `MEM_DECOMMIT`. - */ - void deallocate(void[] b) nothrow @nogc; - - /** - Returns `Ternary.yes` if the passed buffer is inside the range of virtual adresses. - Does not guarantee that the passed buffer is still valid. - */ - Ternary owns(void[] buf) nothrow @nogc; - - /** - Removes the memory mapping causing all physical memory to be decommited and - the virtual address space to be reclaimed. - */ - bool deallocateAll() nothrow @nogc; - - /** - Returns the available size for further allocations in bytes. - */ - size_t getAvailableSize() nothrow @nogc; - } - -private: - size_t pageSize; - size_t numPages; - - // The start of the virtual address range - shared void* data; - - // Keeps track of there the next allocation should start - shared void* offset; - - // On allocation requests, we allocate an extra 'extraAllocPages' pages - // The address up to which we have permissions is stored in 'readWriteLimit' - shared void* readWriteLimit; - enum extraAllocPages = 1000; - SpinLock lock; - -public: - enum uint alignment = 4096; - - // Inject common function implementations - mixin AscendingPageAllocatorImpl!true; - - /** - Rounds the allocation size to the next multiple of the page size. - The allocation only reserves a range of virtual pages but the actual - physical memory is allocated on demand, when accessing the memory. - - Params: - n = Bytes to allocate - - Returns: - `null` on failure or if the requested size exceeds the remaining capacity. - */ - void[] allocate(size_t n) nothrow @nogc - { - return allocateImpl(n, 1); - } - - /** - Rounds the allocation size to the next multiple of the page size. - The allocation only reserves a range of virtual pages but the actual - physical memory is allocated on demand, when accessing the memory. - - The allocated memory is aligned to the specified alignment `a`. - - Params: - n = Bytes to allocate - a = Alignment - - Returns: - `null` on failure or if the requested size exceeds the remaining capacity. - */ - void[] alignedAllocate(size_t n, uint a) nothrow @nogc - { - // For regular `allocate` calls, `a` will be set to 1 - return allocateImpl(n, a); - } - - private void[] allocateImpl(size_t n, uint a) nothrow @nogc - { - import std.algorithm.comparison : min; - - size_t localExtraAlloc; - void* localOffset; - immutable pagedBytes = numPages * pageSize; - size_t goodSize = goodAllocSize(n); - - if (goodSize > pagedBytes) - return null; - - lock.lock(); - scope(exit) lock.unlock(); - - localOffset = cast(void*) offset; - void* alignedStart = cast(void*) roundUpToMultipleOf(cast(size_t) localOffset, a); - assert(alignedStart.alignedAt(a)); - if (alignedStart - data > pagedBytes - goodSize) - return null; - - localOffset = alignedStart + goodSize; - if (localOffset > readWriteLimit) - { - void* newReadWriteLimit = min(cast(void*) data + pagedBytes, - cast(void*) localOffset + extraAllocPages * pageSize); - assert(newReadWriteLimit > readWriteLimit); - localExtraAlloc = newReadWriteLimit - readWriteLimit; - if (!extendMemoryProtection(cast(void*) readWriteLimit, localExtraAlloc)) - return null; - readWriteLimit = cast(shared(void*)) newReadWriteLimit; - } - - offset = cast(typeof(offset)) localOffset; - return cast(void[]) alignedStart[0 .. n]; - } - - /** - If the passed buffer is not the last allocation, then `delta` can be - at most the number of bytes left on the last page. - Otherwise, we can expand the last allocation until the end of the virtual - address range. - */ - bool expand(ref void[] b, size_t delta) nothrow @nogc - { - import std.algorithm.comparison : min; - - if (!delta) return true; - if (b is null) return false; - - void* localOffset; - size_t localExtraAlloc; - size_t goodSize = goodAllocSize(b.length); - size_t bytesLeftOnPage = goodSize - b.length; - - if (bytesLeftOnPage >= delta) - { - b = cast(void[]) b.ptr[0 .. b.length + delta]; - return true; - } - - lock.lock(); - scope(exit) lock.unlock(); - - localOffset = cast(void*) offset; - if (b.ptr + goodSize != localOffset) - return false; - - size_t extraPages = goodAllocSize(delta - bytesLeftOnPage) / pageSize; - if (extraPages > numPages || localOffset - data > pageSize * (numPages - extraPages)) - return false; - - - localOffset = b.ptr + goodSize + extraPages * pageSize; - if (localOffset > readWriteLimit) - { - void* newReadWriteLimit = min(cast(void*) data + numPages * pageSize, - localOffset + extraAllocPages * pageSize); - assert(newReadWriteLimit > readWriteLimit); - localExtraAlloc = newReadWriteLimit - readWriteLimit; - if (!extendMemoryProtection(cast(void*) readWriteLimit, localExtraAlloc)) - return false; - readWriteLimit = cast(shared(void*)) newReadWriteLimit; - } - - offset = cast(typeof(offset)) localOffset; - b = cast(void[]) b.ptr[0 .. b.length + delta]; - return true; - } -} - -/// -@system unittest -{ - import core.memory : pageSize; - import core.thread : ThreadGroup; - - enum numThreads = 100; - shared SharedAscendingPageAllocator a = SharedAscendingPageAllocator(pageSize * numThreads); - - void fun() - { - void[] b = a.allocate(pageSize); - assert(b.length == pageSize); - - assert(a.deallocate(b)); - } - - auto tg = new ThreadGroup; - foreach (i; 0 .. numThreads) - { - tg.create(&fun); - } - tg.joinAll(); -} - -version (StdUnittest) -{ - private static void testrw(void[] b) @nogc nothrow - { - ubyte* buf = cast(ubyte*) b.ptr; - buf[0] = 100; - assert(buf[0] == 100); - buf[b.length - 1] = 101; - assert(buf[b.length - 1] == 101); - } -} - -@system @nogc nothrow unittest -{ - static void testAlloc(Allocator)(ref Allocator a) @nogc nothrow - { - void[] b1 = a.allocate(1); - assert(a.getAvailableSize() == 3 * pageSize); - testrw(b1); - void[] b2 = a.allocate(2); - assert(a.getAvailableSize() == 2 * pageSize); - testrw(b2); - void[] b3 = a.allocate(pageSize + 1); - assert(a.getAvailableSize() == 0); - - testrw(b3); - assert(b1.length == 1); - assert(b2.length == 2); - assert(b3.length == pageSize + 1); - - assert(a.offset - a.data == 4 * pageSize); - void[] b4 = a.allocate(4); - assert(!b4); - - a.deallocate(b1); - assert(a.data); - a.deallocate(b2); - assert(a.data); - a.deallocate(b3); - } - - AscendingPageAllocator a = AscendingPageAllocator(4 * pageSize); - shared SharedAscendingPageAllocator aa = SharedAscendingPageAllocator(4 * pageSize); - - testAlloc(a); - testAlloc(aa); -} - -@system @nogc nothrow unittest -{ - size_t numPages = 26214; - AscendingPageAllocator a = AscendingPageAllocator(numPages * pageSize); - foreach (i; 0 .. numPages) - { - void[] buf = a.allocate(pageSize); - assert(buf.length == pageSize); - testrw(buf); - a.deallocate(buf); - } - - assert(!a.allocate(1)); - assert(a.getAvailableSize() == 0); -} - -@system @nogc nothrow unittest -{ - size_t numPages = 26214; - uint alignment = cast(uint) pageSize; - AscendingPageAllocator a = AscendingPageAllocator(numPages * pageSize); - - foreach (i; 0 .. numPages) - { - void[] buf = a.alignedAllocate(pageSize, alignment); - assert(buf.length == pageSize); - testrw(buf); - a.deallocate(buf); - } - - assert(!a.allocate(1)); - assert(a.getAvailableSize() == 0); -} - -@system @nogc nothrow unittest -{ - static void testAlloc(Allocator)(ref Allocator a) @nogc nothrow - { - import std.traits : hasMember; - - size_t numPages = 5; - uint alignment = cast(uint) pageSize; - - void[] b1 = a.allocate(pageSize / 2); - assert(b1.length == pageSize / 2); - - void[] b2 = a.alignedAllocate(pageSize / 2, alignment); - assert(a.expand(b1, pageSize / 2)); - assert(a.expand(b1, 0)); - assert(!a.expand(b1, 1)); - testrw(b1); - - assert(a.expand(b2, pageSize / 2)); - testrw(b2); - assert(b2.length == pageSize); - - assert(a.getAvailableSize() == pageSize * 3); - - void[] b3 = a.allocate(pageSize / 2); - assert(a.reallocate(b1, b1.length)); - assert(a.reallocate(b2, b2.length)); - assert(a.reallocate(b3, b3.length)); - - assert(b3.length == pageSize / 2); - testrw(b3); - assert(a.expand(b3, pageSize / 4)); - testrw(b3); - assert(a.expand(b3, 0)); - assert(b3.length == pageSize / 2 + pageSize / 4); - assert(a.expand(b3, pageSize / 4 - 1)); - testrw(b3); - assert(a.expand(b3, 0)); - assert(b3.length == pageSize - 1); - assert(a.expand(b3, 2)); - assert(a.expand(b3, 0)); - assert(a.getAvailableSize() == pageSize); - assert(b3.length == pageSize + 1); - testrw(b3); - - assert(a.reallocate(b1, b1.length)); - assert(a.reallocate(b2, b2.length)); - assert(a.reallocate(b3, b3.length)); - - assert(a.reallocate(b3, 2 * pageSize)); - testrw(b3); - assert(a.reallocate(b1, pageSize - 1)); - testrw(b1); - assert(a.expand(b1, 1)); - testrw(b1); - assert(!a.expand(b1, 1)); - - a.deallocate(b1); - a.deallocate(b2); - a.deallocate(b3); - } - - size_t numPages = 5; - uint alignment = cast(uint) pageSize; - AscendingPageAllocator a = AscendingPageAllocator(numPages * pageSize); - shared SharedAscendingPageAllocator aa = SharedAscendingPageAllocator(numPages * pageSize); - - testAlloc(a); - testAlloc(aa); -} - -@system @nogc nothrow unittest -{ - size_t numPages = 21000; - enum testNum = 100; - enum allocPages = 10; - void[][testNum] buf; - AscendingPageAllocator a = AscendingPageAllocator(numPages * pageSize); - - for (int i = 0; i < numPages; i += testNum * allocPages) - { - foreach (j; 0 .. testNum) - { - buf[j] = a.allocate(pageSize * allocPages); - testrw(buf[j]); - } - - foreach (j; 0 .. testNum) - { - a.deallocate(buf[j]); - } - } -} - -@system @nogc nothrow unittest -{ - size_t numPages = 21000; - enum testNum = 100; - enum allocPages = 10; - void[][testNum] buf; - shared SharedAscendingPageAllocator a = SharedAscendingPageAllocator(numPages * pageSize); - - for (int i = 0; i < numPages; i += testNum * allocPages) - { - foreach (j; 0 .. testNum) - { - buf[j] = a.allocate(pageSize * allocPages); - testrw(buf[j]); - } - - foreach (j; 0 .. testNum) - { - a.deallocate(buf[j]); - } - } -} - -@system @nogc nothrow unittest -{ - enum numPages = 2; - AscendingPageAllocator a = AscendingPageAllocator(numPages * pageSize); - void[] b = a.allocate((numPages + 1) * pageSize); - assert(b is null); - b = a.allocate(1); - assert(b.length == 1); - assert(a.getAvailableSize() == pageSize); - a.deallocateAll(); - assert(!a.data && !a.offset); -} - -@system @nogc nothrow unittest -{ - enum numPages = 26; - AscendingPageAllocator a = AscendingPageAllocator(numPages * pageSize); - uint alignment = cast(uint) ((numPages / 2) * pageSize); - void[] b = a.alignedAllocate(pageSize, alignment); - assert(b.length == pageSize); - testrw(b); - assert(b.ptr.alignedAt(alignment)); - a.deallocateAll(); - assert(!a.data && !a.offset); -} - -@system @nogc nothrow unittest -{ - enum numPages = 10; - AscendingPageAllocator a = AscendingPageAllocator(numPages * pageSize); - uint alignment = cast(uint) (2 * pageSize); - - void[] b1 = a.alignedAllocate(pageSize, alignment); - assert(b1.length == pageSize); - testrw(b1); - assert(b1.ptr.alignedAt(alignment)); - - void[] b2 = a.alignedAllocate(pageSize, alignment); - assert(b2.length == pageSize); - testrw(b2); - assert(b2.ptr.alignedAt(alignment)); - - void[] b3 = a.alignedAllocate(pageSize, alignment); - assert(b3.length == pageSize); - testrw(b3); - assert(b3.ptr.alignedAt(alignment)); - - void[] b4 = a.allocate(pageSize); - assert(b4.length == pageSize); - testrw(b4); - - assert(a.deallocate(b1)); - assert(a.deallocate(b2)); - assert(a.deallocate(b3)); - assert(a.deallocate(b4)); - - a.deallocateAll(); - assert(!a.data && !a.offset); -} - -@system unittest -{ - import core.thread : ThreadGroup; - import std.algorithm.sorting : sort; - import core.internal.spinlock : SpinLock; - - enum numThreads = 100; - SpinLock lock = SpinLock(SpinLock.Contention.brief); - ulong[numThreads] ptrVals; - size_t count = 0; - shared SharedAscendingPageAllocator a = SharedAscendingPageAllocator(pageSize * numThreads); - - void fun() - { - void[] b = a.allocate(4000); - assert(b.length == 4000); - - assert(a.expand(b, 96)); - assert(b.length == 4096); - - lock.lock(); - ptrVals[count] = cast(ulong) b.ptr; - count++; - lock.unlock(); - } - - auto tg = new ThreadGroup; - foreach (i; 0 .. numThreads) - { - tg.create(&fun); - } - tg.joinAll(); - - ptrVals[].sort(); - foreach (i; 0 .. numThreads - 1) - { - assert(ptrVals[i] + pageSize == ptrVals[i + 1]); - } -} - -@system unittest -{ - import core.thread : ThreadGroup; - import std.algorithm.sorting : sort; - import core.internal.spinlock : SpinLock; - - SpinLock lock = SpinLock(SpinLock.Contention.brief); - enum numThreads = 100; - void[][numThreads] buf; - size_t count = 0; - shared SharedAscendingPageAllocator a = SharedAscendingPageAllocator(2 * pageSize * numThreads); - - void fun() - { - enum expand = 96; - void[] b = a.allocate(pageSize - expand); - assert(b.length == pageSize - expand); - - assert(a.expand(b, expand)); - assert(b.length == pageSize); - - a.expand(b, pageSize); - assert(b.length == pageSize || b.length == pageSize * 2); - - lock.lock(); - buf[count] = b; - count++; - lock.unlock(); - } - - auto tg = new ThreadGroup; - foreach (i; 0 .. numThreads) - { - tg.create(&fun); - } - tg.joinAll(); - - sort!((a, b) => a.ptr < b.ptr)(buf[0 .. 100]); - foreach (i; 0 .. numThreads - 1) - { - assert(buf[i].ptr + buf[i].length == buf[i + 1].ptr); - } -} diff --git a/phobos/std/experimental/allocator/building_blocks/bitmapped_block.d b/phobos/std/experimental/allocator/building_blocks/bitmapped_block.d deleted file mode 100644 index 571e4e6..0000000 --- a/phobos/std/experimental/allocator/building_blocks/bitmapped_block.d +++ /dev/null @@ -1,2793 +0,0 @@ -// Written in the D programming language. -/** -Source: $(PHOBOSSRC std/experimental/allocator/building_blocks/bitmapped_block.d) -*/ -module std.experimental.allocator.building_blocks.bitmapped_block; - -import std.experimental.allocator.building_blocks.null_allocator; -import std.experimental.allocator.common; -import std.typecons : Flag, Yes, No; - - -// Common implementation for shared and non-shared versions of the BitmappedBlock -private mixin template BitmappedBlockImpl(bool isShared, bool multiBlock) -{ - import std.conv : text; - import std.traits : hasMember; - import std.typecons : Ternary; - import std.typecons : tuple, Tuple; - - static if (isShared && multiBlock) - import core.internal.spinlock : SpinLock; - - static assert(theBlockSize > 0 && theAlignment.isGoodStaticAlignment); - static assert(theBlockSize == chooseAtRuntime || - theBlockSize % theAlignment == 0, "Block size must be a multiple of the alignment"); - - static if (theBlockSize != chooseAtRuntime) - { - alias blockSize = theBlockSize; - } - else - { - // It is the caller's responsibilty to synchronize this with - // allocate/deallocate in shared environments - @property uint blockSize() { return _blockSize; } - @property void blockSize(uint s) - { - static if (multiBlock) - { - assert((cast(BitVector) _control).length == 0 && s % alignment == 0); - } - else - { - assert(_control.length == 0 && s % alignment == 0); - } - _blockSize = s; - } - private uint _blockSize; - } - - static if (is(ParentAllocator == NullAllocator)) - { - private enum parentAlignment = platformAlignment; - } - else - { - private alias parentAlignment = ParentAllocator.alignment; - static assert(parentAlignment >= ulong.alignof); - } - - alias alignment = theAlignment; - - static if (stateSize!ParentAllocator) - { - ParentAllocator parent; - } - else - { - alias parent = ParentAllocator.instance; - } - - private size_t _blocks; - private void[] _payload; - private size_t _startIdx; - - // For multiblock, '_control' is a BitVector, otherwise just a regular ulong[] - static if (multiBlock) - { - // Keeps track of first block which has never been used in an allocation. - // All blocks which are located right to the '_freshBit', should have never been - // allocated - private ulong _freshBit; - private BitVector _control; - } - else - { - private ulong[] _control; - } - - static if (multiBlock && isShared) - { - SpinLock lock = SpinLock(SpinLock.Contention.brief); - } - - pure nothrow @safe @nogc - private size_t totalAllocation(size_t capacity) - { - auto blocks = capacity.divideRoundUp(blockSize); - auto leadingUlongs = blocks.divideRoundUp(64); - import std.algorithm.comparison : min; - immutable initialAlignment = min(parentAlignment, - 1U << min(31U, trailingZeros(leadingUlongs * 8))); - auto maxSlack = alignment <= initialAlignment - ? 0 - : alignment - initialAlignment; - return leadingUlongs * 8 + maxSlack + blockSize * blocks; - } - - this(ubyte[] data) - { - immutable a = data.ptr.effectiveAlignment; - assert(a >= size_t.alignof || !data.ptr, - "Data must be aligned properly"); - - immutable ulong totalBits = data.length * 8; - immutable ulong bitsPerBlock = blockSize * 8 + 1; - _blocks = totalBits / bitsPerBlock; - - // Reality is a bit more complicated, iterate until a good number of - // blocks found. - size_t localBlocks; - for (localBlocks = _blocks; localBlocks; --localBlocks) - { - immutable controlWords = localBlocks.divideRoundUp(64); - auto payload = data[controlWords * 8 .. $].roundStartToMultipleOf( - alignment); - if (payload.length < localBlocks * blockSize) - { - // Overestimated - continue; - } - - // Need the casts for shared versions - static if (multiBlock) - { - _control = cast(typeof(_control)) BitVector((cast(ulong*) data.ptr)[0 .. controlWords]); - (cast(BitVector) _control)[] = 0; - } - else - { - _control = (cast(typeof(_control.ptr)) data.ptr)[0 .. controlWords]; - _control[] = 0; - } - - _payload = cast(typeof(_payload)) payload; - break; - } - - _blocks = cast(typeof(_blocks)) localBlocks; - } - - static if (chooseAtRuntime == theBlockSize) - this(ubyte[] data, uint blockSize) - { - this._blockSize = blockSize; - this(data); - } - - static if (!is(ParentAllocator == NullAllocator) && !stateSize!ParentAllocator) - this(size_t capacity) - { - size_t toAllocate = totalAllocation(capacity); - auto data = cast(ubyte[])(parent.allocate(toAllocate)); - this(data); - assert(_blocks * blockSize >= capacity); - } - - static if (!is(ParentAllocator == NullAllocator) && stateSize!ParentAllocator) - this(ParentAllocator parent, size_t capacity) - { - this.parent = parent; - size_t toAllocate = totalAllocation(capacity); - auto data = cast(ubyte[])(parent.allocate(toAllocate)); - this(data); - } - - static if (!is(ParentAllocator == NullAllocator) && - chooseAtRuntime == theBlockSize && - !stateSize!ParentAllocator) - this(size_t capacity, uint blockSize) - { - this._blockSize = blockSize; - this(capacity); - } - - static if (!is(ParentAllocator == NullAllocator) && - chooseAtRuntime == theBlockSize && - stateSize!ParentAllocator) - this(ParentAllocator parent, size_t capacity, uint blockSize) - { - this._blockSize = blockSize; - this(parent, capacity); - } - - static if (!is(ParentAllocator == NullAllocator) - && hasMember!(ParentAllocator, "deallocate")) - ~this() - { - // multiblock bitmapped blocks use a BitVector - static if (multiBlock) - { - void* start = cast(void*) _control.rep.ptr; - } - else - { - void* start = cast(void*) _control.ptr; - } - void* end = cast(void*) (_payload.ptr + _payload.length); - parent.deallocate(start[0 .. end - start]); - } - - pure nothrow @safe @nogc - size_t goodAllocSize(size_t n) - { - return n.roundUpToMultipleOf(blockSize); - } - - // Implementation of the 'multiBlock' BitmappedBlock - // For the shared version, the methods are protected by a common lock - static if (multiBlock) - { - /* - Adjusts the memoized _startIdx to the leftmost control word that has at - least one zero bit. Assumes all control words to the left of $(D - _control[_startIdx]) are already occupied. - */ - private void adjustStartIdx() - { - while (_startIdx < _control.rep.length && _control.rep[_startIdx] == ulong.max) - { - static if (isShared) - { - // Shared demands atomic increment, however this is protected - // by a lock. Regular increment is fine - auto localStart = _startIdx + 1; - _startIdx = localStart; - } - else - { - ++_startIdx; - } - } - } - - /* - Based on the latest allocated bit, 'newBit', it adjusts '_freshBit' - */ - pure nothrow @safe @nogc - private void adjustFreshBit(const ulong newBit) - { - import std.algorithm.comparison : max; - static if (isShared) - { - auto localFreshBit = max(newBit, _freshBit); - _freshBit = localFreshBit; - } - else - { - _freshBit = max(newBit, _freshBit); - } - } - - /* - Returns the blocks corresponding to the control bits starting at word index - wordIdx and bit index msbIdx (MSB=0) for a total of howManyBlocks. - */ - @trusted - private void[] blocksFor(this _)(size_t wordIdx, uint msbIdx, size_t howManyBlocks) - { - assert(msbIdx <= 63); - const start = (wordIdx * 64 + msbIdx) * blockSize; - const end = start + blockSize * howManyBlocks; - if (start == end) return null; - if (end <= _payload.length) return cast(void[]) _payload[start .. end]; - // This could happen if we have more control bits than available memory. - // That's possible because the control bits are rounded up to fit in - // 64-bit words. - return null; - } - - static if (isShared) - nothrow @safe @nogc - void[] allocate(const size_t s) - { - lock.lock(); - scope(exit) lock.unlock(); - - return allocateImpl(s); - } - - static if (!isShared) - pure nothrow @safe @nogc - void[] allocate(const size_t s) - { - return allocateImpl(s); - } - - - // If shared, this is protected by a lock inside 'allocate' - pure nothrow @trusted @nogc - private void[] allocateImpl(const size_t s) - { - const blocks = s.divideRoundUp(blockSize); - void[] result; - - Lswitch: - switch (blocks) - { - case 1: - // inline code here for speed - // find the next available block - foreach (i; _startIdx .. _control.rep.length) - { - const w = _control.rep[i]; - if (w == ulong.max) continue; - uint j = leadingOnes(w); - assert(j < 64, "Invalid number of blocks"); - assert((_control.rep[i] & ((1UL << 63) >> j)) == 0, "Corrupted bitmap"); - static if (isShared) - { - // Need the cast because shared does not recognize the lock - *(cast(ulong*) &_control._rep[i]) |= (1UL << 63) >> j; - } - else - { - _control.rep[i] |= (1UL << 63) >> j; - } - if (i == _startIdx) - { - adjustStartIdx(); - } - result = blocksFor(i, j, 1); - break Lswitch; - } - goto case 0; // fall through - case 0: - return null; - case 2: .. case 64: - result = smallAlloc(cast(uint) blocks); - break; - default: - result = hugeAlloc(blocks); - break; - } - if (result) - { - adjustFreshBit((result.ptr - _payload.ptr) / blockSize + blocks); - } - return result.ptr ? result.ptr[0 .. s] : null; - } - - @trusted void[] allocateFresh(const size_t s) - { - static if (isShared) - { - lock.lock(); - scope(exit) lock.unlock(); - } - - const blocks = s.divideRoundUp(blockSize); - - void[] result = blocksFor(cast(size_t) (_freshBit / 64), - cast(uint) (_freshBit % 64), blocks); - if (result) - { - (cast(BitVector) _control)[_freshBit .. _freshBit + blocks] = 1; - static if (isShared) - { - ulong localFreshBit = _freshBit; - localFreshBit += blocks; - _freshBit = localFreshBit; - } - else - { - _freshBit += blocks; - } - } - return result; - } - - void[] alignedAllocate(size_t n, uint a) - { - static if (isShared) - { - lock.lock(); - scope(exit) lock.unlock(); - } - - return alignedAllocateImpl(n, a); - } - - // If shared, this is protected by a lock inside 'alignedAllocate' - private void[] alignedAllocateImpl(size_t n, uint a) - { - import std.math.traits : isPowerOf2; - assert(a.isPowerOf2); - if (a <= alignment) return allocate(n); - - // Overallocate to make sure we can get an aligned block - auto b = allocateImpl((n + a - alignment).roundUpToMultipleOf(blockSize)); - if (!b.ptr) return null; - auto result = b.roundStartToMultipleOf(a); - assert(result.length >= n); - result = result.ptr[0 .. n]; // final result - - // Free any blocks that might be slack at the beginning - auto slackHeadingBlocks = (result.ptr - b.ptr) / blockSize; - if (slackHeadingBlocks) - { - deallocateImpl(b[0 .. slackHeadingBlocks * blockSize]); - } - - // Free any blocks that might be slack at the end - auto slackTrailingBlocks = ((b.ptr + b.length) - - (result.ptr + result.length)) / blockSize; - if (slackTrailingBlocks) - { - deallocateImpl(b[$ - slackTrailingBlocks * blockSize .. $]); - } - - return result; - } - - /* - Tries to allocate "blocks" blocks at the exact position indicated by the - position wordIdx/msbIdx (msbIdx counts from MSB, i.e. MSB has index 0). If - it succeeds, fills "result" with the result and returns tuple(size_t.max, - 0). Otherwise, returns a tuple with the next position to search. - */ - private Tuple!(size_t, uint) allocateAt(size_t wordIdx, uint msbIdx, - size_t blocks, ref void[] result) - { - assert(blocks > 0); - assert(wordIdx < _control.rep.length); - assert(msbIdx <= 63); - void[] tmpResult; - result = null; - if (msbIdx + blocks <= 64) - { - // Allocation should fit this control word - static if (isShared) - { - ulong localControl = _control.rep[wordIdx]; - bool didSetBit = setBitsIfZero(localControl, - cast(uint) (64 - msbIdx - blocks), 63 - msbIdx); - _control.rep[wordIdx] = localControl; - } - else - { - bool didSetBit = setBitsIfZero(_control.rep[wordIdx], - cast(uint) (64 - msbIdx - blocks), 63 - msbIdx); - } - if (didSetBit) - { - tmpResult = blocksFor(wordIdx, msbIdx, blocks); - if (!tmpResult) - { - static if (isShared) - { - localControl = _control.rep[wordIdx]; - resetBits(localControl, - cast(uint) (64 - msbIdx - blocks), 63 - msbIdx); - _control.rep[wordIdx] = localControl; - } - else - { - resetBits(_control.rep[wordIdx], - cast(uint) (64 - msbIdx - blocks), 63 - msbIdx); - } - return tuple(size_t.max - 1, 0u); - } - result = tmpResult; - tmpResult = null; - return tuple(size_t.max, 0u); - } - // Can't allocate, make a suggestion - return msbIdx + blocks == 64 - ? tuple(wordIdx + 1, 0u) - : tuple(wordIdx, cast(uint) (msbIdx + blocks)); - } - // Allocation spans two control words or more - immutable mask = ulong.max >> msbIdx; - if (_control.rep[wordIdx] & mask) - { - // We can't allocate the rest of this control word, - // return a suggestion. - return tuple(wordIdx + 1, 0u); - } - // We can allocate the rest of this control word, but we first need to - // make sure we can allocate the tail. - if (wordIdx + 1 == _control.rep.length) - { - // No more memory - return tuple(_control.rep.length, 0u); - } - auto hint = allocateAt(wordIdx + 1, 0, blocks - 64 + msbIdx, result); - if (hint[0] == size_t.max) - { - tmpResult = blocksFor(wordIdx, msbIdx, blocks); - if (!tmpResult) - { - return tuple(size_t.max - 1, 0u); - } - static if (isShared) - { - // Dont want atomics, because this is protected by 'lock' - ulong localControl = _control.rep[wordIdx]; - localControl |= mask; - _control.rep[wordIdx] = localControl; - } - else - { - _control.rep[wordIdx] |= mask; - } - result = tmpResult; - tmpResult = null; - return tuple(size_t.max, 0u); - } - // Failed, return a suggestion that skips this whole run. - return hint; - } - - /* Allocates as many blocks as possible at the end of the blocks indicated - by wordIdx. Returns the number of blocks allocated. */ - private uint allocateAtTail(size_t wordIdx) - { - assert(wordIdx < _control.rep.length); - const available = trailingZeros(_control.rep[wordIdx]); - static if (isShared) - { - ulong localControl = _control.rep[wordIdx]; - localControl |= ulong.max >> available; - _control.rep[wordIdx] = localControl; - } - else - { - _control.rep[wordIdx] |= ulong.max >> available; - } - return available; - } - - pure nothrow @safe @nogc - private void[] smallAlloc(uint blocks) return scope - { - assert(blocks >= 2 && blocks <= 64); - void[] result; - foreach (i; _startIdx .. _control.rep.length) - { - // Test within the current 64-bit word - const v = _control.rep[i]; - if (v == ulong.max) continue; - auto j = findContigOnes(~v, blocks); - if (j < 64) - { - // yay, found stuff - result = blocksFor(i, j, blocks); - if (result) - { - static if (isShared) - { - ulong localControl = _control.rep[i]; - setBits(localControl, 64 - j - blocks, 63 - j); - _control.rep[i] = localControl; - } - else - { - setBits(_control.rep[i], 64 - j - blocks, 63 - j); - } - } - return result; - } - // Next, try allocations that cross a word - auto available = trailingZeros(v); - if (available == 0) continue; - if (i + 1 >= _control.rep.length) break; - assert(available < blocks); // otherwise we should have found it - auto needed = blocks - available; - assert(needed > 0 && needed < 64); - result = blocksFor(i, 64 - available, blocks); - if (result && allocateAtFront(i + 1, needed)) - { - static if (isShared) - { - ulong localControl = _control.rep[i]; - localControl |= (1UL << available) - 1; - _control.rep[i] = localControl; - } - else - { - _control.rep[i] |= (1UL << available) - 1; - } - return result; - } - } - return null; - } - - pure nothrow @trusted @nogc - private void[] hugeAlloc(size_t blocks) return scope - { - assert(blocks > 64); - if (_startIdx == _control._rep.length) - { - assert((cast(BitVector) _control).allAre1); - return null; - } - - auto i = (cast(BitVector)_control).findZeros(blocks, _startIdx * 64); - if (i == i.max || i + blocks > _blocks) return null; - // Allocate those bits - (cast(BitVector) _control)[i .. i + blocks] = 1; - return cast(void[]) _payload[cast(size_t) (i * blockSize) - .. cast(size_t) ((i + blocks) * blockSize)]; - } - - // Rounds sizeInBytes to a multiple of blockSize. - private size_t bytes2blocks(size_t sizeInBytes) - { - return (sizeInBytes + blockSize - 1) / blockSize; - } - - /* Allocates given blocks at the beginning blocks indicated by wordIdx. - Returns true if allocation was possible, false otherwise. */ - private bool allocateAtFront(size_t wordIdx, uint blocks) - { - assert(wordIdx < _control.rep.length && blocks >= 1 && blocks <= 64); - const mask = (1UL << (64 - blocks)) - 1; - if (_control.rep[wordIdx] > mask) return false; - static if (isShared) - { - ulong localControl = _control.rep[wordIdx]; - localControl |= ~mask; - _control.rep[wordIdx] = localControl; - } - else - { - _control.rep[wordIdx] |= ~mask; - } - return true; - } - - // Since the lock is not pure, only the single threaded 'expand' is pure - static if (isShared) - { - nothrow @trusted @nogc - bool expand(ref void[] b, immutable size_t delta) - { - lock.lock(); - scope(exit) lock.unlock(); - - return expandImpl(b, delta); - } - } - else - { - pure nothrow @trusted @nogc - bool expand(ref void[] b, immutable size_t delta) - { - return expandImpl(b, delta); - } - } - - // If shared, this is protected by a lock inside 'expand' - pure nothrow @trusted @nogc - private bool expandImpl(ref void[] b, immutable size_t delta) - { - // Dispose with trivial corner cases - if (b is null || delta == 0) return delta == 0; - - /* To simplify matters, refuse to expand buffers that don't start at a block start (this may be the case for blocks allocated with alignedAllocate). - */ - if ((b.ptr - _payload.ptr) % blockSize) return false; - - const blocksOld = bytes2blocks(b.length); - const blocksNew = bytes2blocks(b.length + delta); - assert(blocksOld <= blocksNew); - - // Possibly we have enough slack at the end of the block! - if (blocksOld == blocksNew) - { - b = b.ptr[0 .. b.length + delta]; - return true; - } - - assert((b.ptr - _payload.ptr) % blockSize == 0); - const blockIdx = (b.ptr - _payload.ptr) / blockSize; - const blockIdxAfter = blockIdx + blocksOld; - - // Try the maximum - const wordIdx = blockIdxAfter / 64, - msbIdx = cast(uint) (blockIdxAfter % 64); - void[] p; - auto hint = allocateAt(wordIdx, msbIdx, blocksNew - blocksOld, p); - if (hint[0] != size_t.max) - { - return false; - } - // Expansion successful - assert(p.ptr == b.ptr + blocksOld * blockSize); - b = b.ptr[0 .. b.length + delta]; - adjustFreshBit(blockIdx + blocksNew); - return true; - } - - @system bool reallocate(ref void[] b, size_t newSize) - { - static if (isShared) - { - lock.lock(); - scope(exit) lock.unlock(); - } - - return reallocateImpl(b, newSize); - } - - // If shared, this is protected by a lock inside 'reallocate' - private @system bool reallocateImpl(ref void[] b, size_t newSize) - { - static bool slowReallocate(Allocator)(ref Allocator a, ref void[] b, size_t s) - { - if (b.length == s) return true; - if (b.length <= s && a.expandImpl(b, s - b.length)) return true; - auto newB = a.allocateImpl(s); - if (newB.length != s) return false; - if (newB.length <= b.length) newB[] = b[0 .. newB.length]; - else newB[0 .. b.length] = b[]; - a.deallocateImpl(b); - b = newB; - return true; - } - - if (!b.ptr) - { - b = allocateImpl(newSize); - return b.length == newSize; - } - if (newSize == 0) - { - deallocateImpl(b); - b = null; - return true; - } - if (newSize < b.length) - { - // Shrink. Will shrink in place by deallocating the trailing part. - auto newCapacity = bytes2blocks(newSize) * blockSize; - deallocateImpl(b[newCapacity .. $]); - b = b[0 .. newSize]; - return true; - } - // Go the slow route - return slowReallocate(this, b, newSize); - } - - @system bool alignedReallocate(ref void[] b, size_t newSize, uint a) - { - static if (isShared) - { - lock.lock(); - scope(exit) lock.unlock(); - } - - return alignedReallocateImpl(b, newSize, a); - } - - // If shared, this is protected by a lock inside 'alignedReallocate' - private @system bool alignedReallocateImpl(ref void[] b, size_t newSize, uint a) - { - static bool slowAlignedReallocate(Allocator)(ref Allocator alloc, - ref void[] b, size_t s, uint a) - { - if (b.length <= s && b.ptr.alignedAt(a) - && alloc.expandImpl(b, s - b.length)) return true; - - auto newB = alloc.alignedAllocateImpl(s, a); - if (newB.length != s) return false; - if (newB.length <= b.length) newB[] = b[0 .. newB.length]; - else newB[0 .. b.length] = b[]; - alloc.deallocateImpl(b); - b = newB; - return true; - } - - if (newSize == 0) - { - deallocateImpl(b); - b = null; - return true; - } - // Go the slow route - return slowAlignedReallocate(this, b, newSize, a); - } - - nothrow @nogc - bool deallocate(void[] b) - { - static if (isShared) - { - lock.lock(); - scope(exit) lock.unlock(); - } - - return deallocateImpl(b); - } - - // If shared, this is protected by a lock inside 'deallocate' - nothrow @nogc - private bool deallocateImpl(void[] b) - { - if (b is null) return true; - - // Locate position - immutable pos = b.ptr - _payload.ptr; - immutable blockIdx = pos / blockSize; - - // Adjust pointer, might be inside a block due to alignedAllocate - void* begin = cast(void*) (_payload.ptr + blockIdx * blockSize), - end = cast(void*) (b.ptr + b.length); - b = begin[0 .. end - begin]; - // Round up size to multiple of block size - auto blocks = b.length.divideRoundUp(blockSize); - - // Get into details - auto wordIdx = blockIdx / 64, msbIdx = cast(uint) (blockIdx % 64); - if (_startIdx > wordIdx) _startIdx = wordIdx; - - // Three stages: heading bits, full words, leftover bits - if (msbIdx) - { - if (blocks + msbIdx <= 64) - { - static if (isShared) - { - ulong localControl = _control.rep[wordIdx]; - resetBits(localControl, - cast(uint) (64 - msbIdx - blocks), - 63 - msbIdx); - _control.rep[wordIdx] = localControl; - } - else - { - resetBits(_control.rep[wordIdx], - cast(uint) (64 - msbIdx - blocks), - 63 - msbIdx); - } - return true; - } - else - { - static if (isShared) - { - ulong localControl = _control.rep[wordIdx]; - localControl &= ulong.max << (64 - msbIdx); - _control.rep[wordIdx] = localControl; - } - else - { - _control.rep[wordIdx] &= ulong.max << (64 - msbIdx); - } - blocks -= 64 - msbIdx; - ++wordIdx; - msbIdx = 0; - } - } - - // Stage 2: reset one word at a time - for (; blocks >= 64; blocks -= 64) - { - _control.rep[wordIdx++] = 0; - } - - // Stage 3: deal with leftover bits, if any - assert(wordIdx <= _control.rep.length); - if (blocks) - { - static if (isShared) - { - ulong localControl = _control.rep[wordIdx]; - localControl &= ulong.max >> blocks; - _control.rep[wordIdx] = localControl; - } - else - { - _control.rep[wordIdx] &= ulong.max >> blocks; - } - } - return true; - } - - // Since the lock is not pure, only the single threaded version is pure - static if (isShared) - { - nothrow @nogc - bool deallocateAll() - { - lock.lock(); - scope(exit) lock.unlock(); - - (cast(BitVector) _control)[] = 0; - _startIdx = 0; - return true; - } - } - else - { - pure nothrow @nogc - bool deallocateAll() - { - _control[] = 0; - _startIdx = 0; - return true; - } - } - - // Since the lock is not pure, only the single threaded version is pure - static if (isShared) - { - nothrow @safe @nogc - Ternary empty() - { - lock.lock(); - scope(exit) lock.unlock(); - - return emptyImpl(); - } - } - else - { - pure nothrow @safe @nogc - Ternary empty() - { - return Ternary(_control.allAre0()); - } - } - - pure nothrow @trusted @nogc - private Ternary emptyImpl() - { - return Ternary((cast(BitVector) _control).allAre0()); - } - - // Debug helper - debug(StdBitmapped) - private void dump() - { - import std.stdio : writefln, writeln; - - ulong controlLen = (cast(BitVector) _control).length; - writefln("%s @ %s {", typeid(this), cast(void*) (cast(BitVector) _control)._rep.ptr); - scope(exit) writeln("}"); - assert(_payload.length >= blockSize * _blocks); - assert(controlLen >= _blocks); - writefln(" _startIdx=%s; blockSize=%s; blocks=%s", - _startIdx, blockSize, _blocks); - if (!controlLen) return; - uint blockCount = 1; - bool inAllocatedStore = (cast(BitVector) _control)[0]; - void* start = cast(void*) _payload.ptr; - for (size_t i = 1;; ++i) - { - if (i >= _blocks || (cast(BitVector) _control)[i] != inAllocatedStore) - { - writefln(" %s block at 0x%s, length: %s (%s*%s)", - inAllocatedStore ? "Busy" : "Free", - cast(void*) start, - blockCount * blockSize, - blockCount, blockSize); - if (i >= _blocks) break; - assert(i < controlLen); - inAllocatedStore = (cast(BitVector) _control)[i]; - start = cast(void*) (_payload.ptr + blockCount * blockSize); - blockCount = 1; - } - else - { - ++blockCount; - } - } - } - - void[] allocateAll() return scope - { - static if (isShared) - { - lock.lock(); - scope(exit) lock.unlock(); - } - - if (emptyImpl != Ternary.yes) return null; - (cast(BitVector) _control)[] = 1; - return cast(void[]) _payload; - } - } // Finish Yes.multiblock implementation specifics - else - { - static if (isShared) - pure nothrow @trusted @nogc - void[] allocate(const size_t s) - { - import core.atomic : cas, atomicLoad, atomicOp; - import core.bitop : bsr; - import std.range : iota; - import std.algorithm.iteration : map; - import std.array : array; - - if (s.divideRoundUp(blockSize) != 1) - return null; - - // First zero bit position for all values in the 0 - 255 range - // for fast lookup - static immutable ubyte[255] firstZero = iota(255U).map! - (x => (7 - (bsr((~x) & 0x000000ff)))).array; - - foreach (size_t i; 0 .. _control.length) - { - ulong controlVal, newControlVal, bitIndex; - do - { - bitIndex = 0; - newControlVal = 0; - controlVal = atomicLoad(_control[i]); - - // skip all control words which have all bits set - if (controlVal == ulong.max) - break; - - // fast lookup of first byte which has at least one zero bit - foreach (byteIndex; 0 .. 8) - { - ulong mask = (0xFFUL << (8 * (7 - byteIndex))); - if ((mask & controlVal) != mask) - { - ubyte byteVal = cast(ubyte) ((mask & controlVal) >> (8 * (7 - byteIndex))); - bitIndex += firstZero[byteVal]; - newControlVal = controlVal | (1UL << (63 - bitIndex)); - break; - } - bitIndex += 8; - } - } while (!cas(&_control[i], controlVal, newControlVal)); - - auto blockIndex = bitIndex + 64 * i; - if (controlVal != ulong.max && blockIndex < _blocks) - { - size_t payloadBlockStart = cast(size_t) blockIndex * blockSize; - return cast(void[]) _payload[payloadBlockStart .. payloadBlockStart + s]; - } - } - - return null; - } - - static if (!isShared) - pure nothrow @trusted @nogc - void[] allocate(const size_t s) - { - import core.bitop : bsr; - import std.range : iota; - import std.algorithm.iteration : map; - import std.array : array; - - if (s.divideRoundUp(blockSize) != 1) - return null; - - // First zero bit position for all values in the 0 - 255 range - // for fast lookup - static immutable ubyte[255] firstZero = iota(255U).map! - (x => (7 - (bsr((~x) & 0x000000ff)))).array; - - _startIdx = (_startIdx + 1) % _control.length; - foreach (size_t idx; 0 .. _control.length) - { - size_t i = (idx + _startIdx) % _control.length; - size_t bitIndex = 0; - // skip all control words which have all bits set - if (_control[i] == ulong.max) - continue; - - // fast lookup of first byte which has at least one zero bit - foreach (byteIndex; 0 .. 8) - { - ulong mask = (0xFFUL << (8 * (7 - byteIndex))); - if ((mask & _control[i]) != mask) - { - ubyte byteVal = cast(ubyte) ((mask & _control[i]) >> (8 * (7 - byteIndex))); - bitIndex += firstZero[byteVal]; - _control[i] |= (1UL << (63 - bitIndex)); - break; - } - bitIndex += 8; - } - - auto blockIndex = bitIndex + 64 * i; - if (blockIndex < _blocks) - { - size_t payloadBlockStart = cast(size_t) blockIndex * blockSize; - return cast(void[]) _payload[payloadBlockStart .. payloadBlockStart + s]; - } - } - - return null; - } - - nothrow @nogc - bool deallocate(void[] b) - { - static if (isShared) - import core.atomic : atomicOp; - - if (b is null) - return true; - - auto blockIndex = (b.ptr - _payload.ptr) / blockSize; - auto controlIndex = blockIndex / 64; - auto bitIndex = blockIndex % 64; - static if (isShared) - { - atomicOp!"&="(_control[controlIndex], ~(1UL << (63 - bitIndex))); - } - else - { - _control[controlIndex] &= ~(1UL << (63 - bitIndex)); - } - - return true; - } - - pure nothrow @trusted @nogc - bool expand(ref void[] b, immutable size_t delta) - { - if (delta == 0) - return true; - - immutable newLength = delta + b.length; - if (b is null || newLength > blockSize) - return false; - - b = b.ptr[0 .. newLength]; - return true; - } - } // Finish No.multiblock implementation specifics - - pure nothrow @trusted @nogc - Ternary owns(const void[] b) const - { - assert(b || b.length == 0, "Corrupt block."); - return Ternary(b && _payload && (&b[0] >= &_payload[0]) - && (&b[0] + b.length) <= (&_payload[0] + _payload.length)); - } -} - -/** -`BitmappedBlock` implements a simple heap consisting of one contiguous area -of memory organized in blocks, each of size `theBlockSize`. A block is a unit -of allocation. A bitmap serves as bookkeeping data, more precisely one bit per -block indicating whether that block is currently allocated or not. - -Passing `NullAllocator` as `ParentAllocator` (the default) means user code -manages allocation of the memory block from the outside; in that case -`BitmappedBlock` must be constructed with a `ubyte[]` preallocated block and -has no responsibility regarding the lifetime of its support underlying storage. -If another allocator type is passed, `BitmappedBlock` defines a destructor that -uses the parent allocator to release the memory block. That makes the combination of `AllocatorList`, -`BitmappedBlock`, and a back-end allocator such as `MmapAllocator` -a simple and scalable solution for memory allocation. - -There are advantages to storing bookkeeping data separated from the payload -(as opposed to e.g. using `AffixAllocator` to store metadata together with -each allocation). The layout is more compact (overhead is one bit per block), -searching for a free block during allocation enjoys better cache locality, and -deallocation does not touch memory around the payload being deallocated (which -is often cold). - -Allocation requests are handled on a first-fit basis. Although linear in -complexity, allocation is in practice fast because of the compact bookkeeping -representation, use of simple and fast bitwise routines, and caching of the -first available block position. A known issue with this general approach is -fragmentation, partially mitigated by coalescing. Since `BitmappedBlock` does -not need to maintain the allocated size, freeing memory implicitly coalesces -free blocks together. Also, tuning `blockSize` has a considerable impact on -both internal and external fragmentation. - -If the last template parameter is set to `No.multiblock`, the allocator will only serve -allocations which require at most `theBlockSize`. The `BitmappedBlock` has a specialized -implementation for single-block allocations which allows for greater performance, -at the cost of not being able to allocate more than one block at a time. - -The size of each block can be selected either during compilation or at run -time. Statically-known block sizes are frequent in practice and yield slightly -better performance. To choose a block size statically, pass it as the `blockSize` -parameter as in `BitmappedBlock!(4096)`. To choose a block -size parameter, use `BitmappedBlock!(chooseAtRuntime)` and pass the -block size to the constructor. - -Params: - theBlockSize = the length of a block, which must be a multiple of `theAlignment` - - theAlignment = alignment of each block - - ParentAllocator = allocator from which the `BitmappedBlock` will draw memory. - If set to `NullAllocator`, the storage must be passed via the constructor - - f = `Yes.multiblock` to support allocations spanning across multiple blocks and - `No.multiblock` to support single block allocations. - Although limited by single block allocations, `No.multiblock` will generally - provide higher performance. -*/ -struct BitmappedBlock(size_t theBlockSize, uint theAlignment = platformAlignment, - ParentAllocator = NullAllocator, Flag!"multiblock" f = Yes.multiblock) -{ - version (StdDdoc) - { - /** - Constructs a block allocator given a hunk of memory, or a desired capacity - in bytes. - $(UL - $(LI If `ParentAllocator` is $(REF_ALTTEXT `NullAllocator`, NullAllocator, std,experimental,allocator,building_blocks,null_allocator), - only the constructor taking `data` is defined and the user is responsible for freeing `data` if desired.) - $(LI Otherwise, both constructors are defined. The `data`-based - constructor assumes memory has been allocated with the parent allocator. - The `capacity`-based constructor uses `ParentAllocator` to allocate - an appropriate contiguous hunk of memory. Regardless of the constructor - used, the destructor releases the memory by using `ParentAllocator.deallocate`.) - ) - */ - this(ubyte[] data); - - /// Ditto - this(ubyte[] data, uint blockSize); - - /// Ditto - this(size_t capacity); - - /// Ditto - this(ParentAllocator parent, size_t capacity); - - /// Ditto - this(size_t capacity, uint blockSize); - - /// Ditto - this(ParentAllocator parent, size_t capacity, uint blockSize); - - /** - If `blockSize == chooseAtRuntime`, `BitmappedBlock` offers a read/write - property `blockSize`. It must be set before any use of the allocator. - Otherwise (i.e. `theBlockSize` is a legit constant), `blockSize` is - an alias for `theBlockSize`. Whether constant or variable, must also be - a multiple of `alignment`. This constraint is `assert`ed statically - and dynamically. - */ - alias blockSize = theBlockSize; - - /** - The _alignment offered is user-configurable statically through parameter - `theAlignment`, defaulted to `platformAlignment`. - */ - alias alignment = theAlignment; - - /** - The _parent allocator. Depending on whether `ParentAllocator` holds state - or not, this is a member variable or an alias for - `ParentAllocator.instance`. - */ - ParentAllocator parent; - - /** - Returns the actual bytes allocated when `n` bytes are requested, i.e. - `n.roundUpToMultipleOf(blockSize)`. - */ - pure nothrow @safe @nogc - size_t goodAllocSize(size_t n); - - /** - Returns `Ternary.yes` if `b` belongs to the `BitmappedBlock` object, - `Ternary.no` otherwise. Never returns `Ternary.unkown`. (This - method is somewhat tolerant in that accepts an interior slice.) - */ - pure nothrow @trusted @nogc - Ternary owns(const void[] b) const; - - /** - Expands in place a buffer previously allocated by `BitmappedBlock`. - If instantiated with `No.multiblock`, the expansion fails if the new length - exceeds `theBlockSize`. - */ - pure nothrow @trusted @nogc - bool expand(ref void[] b, immutable size_t delta); - - /** - Deallocates a block previously allocated with this allocator. - */ - nothrow @nogc - bool deallocate(void[] b); - - /** - Allocates `s` bytes of memory and returns it, or `null` if memory - could not be allocated. - - The following information might be of help with choosing the appropriate - block size. Actual allocation occurs in sizes multiple of the block size. - Allocating one block is the fastest because only one 0 bit needs to be - found in the metadata. Allocating 2 through 64 blocks is the next cheapest - because it affects a maximum of two `ulong` in the metadata. - Allocations greater than 64 blocks require a multiword search through the - metadata. - - If instantiated with `No.multiblock`, it performs a search for the first zero - bit in the bitmap and sets it. - */ - pure nothrow @trusted @nogc - void[] allocate(const size_t s); - - /** - Allocates s bytes of memory and returns it, or `null` if memory could not be allocated. - `allocateFresh` behaves just like allocate, the only difference being that this always - returns unused(fresh) memory. Although there may still be available space in the `BitmappedBlock`, - `allocateFresh` could still return null, because all the available blocks have been previously deallocated. - */ - @trusted void[] allocateFresh(const size_t s); - - /** - If the `BitmappedBlock` object is empty (has no active allocation), allocates - all memory within and returns a slice to it. Otherwise, returns `null` - (i.e. no attempt is made to allocate the largest available block). - */ - void[] allocateAll(); - - /** - Returns `Ternary.yes` if no memory is currently allocated with this - allocator, otherwise `Ternary.no`. This method never returns - `Ternary.unknown`. - */ - pure nothrow @safe @nogc - Ternary empty(); - - /** - Forcibly deallocates all memory allocated by this allocator, making it - available for further allocations. Does not return memory to `ParentAllocator`. - */ - pure nothrow @nogc - bool deallocateAll(); - - /** - Reallocates a block previously allocated with `alignedAllocate`. Contractions do not occur in place. - */ - @system bool alignedReallocate(ref void[] b, size_t newSize, uint a); - - /** - Reallocates a previously-allocated block. Contractions occur in place. - */ - @system bool reallocate(ref void[] b, size_t newSize); - - /** - Allocates a block with specified alignment `a`. The alignment must be a - power of 2. If `a <= alignment`, function forwards to `allocate`. - Otherwise, it attempts to overallocate and then adjust the result for - proper alignment. In the worst case the slack memory is around two blocks. - */ - void[] alignedAllocate(size_t n, uint a); - - /** - If `ParentAllocator` is not `NullAllocator` and defines `deallocate`, - the destructor is defined to deallocate the block held. - */ - ~this(); - } - else - { - version (StdUnittest) - @system unittest - { - import std.algorithm.comparison : max; - import std.experimental.allocator.mallocator : AlignedMallocator; - auto m = cast(ubyte[])(AlignedMallocator.instance.alignedAllocate(1024 * 64, - max(theAlignment, cast(uint) size_t.sizeof))); - scope(exit) () nothrow @nogc { AlignedMallocator.instance.deallocate(m); }(); - static if (theBlockSize == chooseAtRuntime) - { - testAllocator!(() => BitmappedBlock!(theBlockSize, theAlignment, NullAllocator)(m, 64)); - } - else - { - testAllocator!(() => BitmappedBlock!(theBlockSize, theAlignment, NullAllocator)(m)); - } - } - mixin BitmappedBlockImpl!(false, f == Yes.multiblock); - } -} - -/// -@system unittest -{ - // Create a block allocator on top of a 10KB stack region. - import std.experimental.allocator.building_blocks.region : InSituRegion; - import std.traits : hasMember; - InSituRegion!(10_240, 64) r; - auto a = BitmappedBlock!(64, 64)(cast(ubyte[])(r.allocateAll())); - static assert(hasMember!(InSituRegion!(10_240, 64), "allocateAll")); - const b = a.allocate(100); - assert(b.length == 100); -} - -/// -@system unittest -{ - import std.experimental.allocator.mallocator : Mallocator; - import std.typecons : Flag, Yes; - - enum blockSize = 64; - enum numBlocks = 10; - - // The 'BitmappedBlock' is implicitly instantiated with Yes.multiblock - auto a = BitmappedBlock!(blockSize, 8, Mallocator, Yes.multiblock)(numBlocks * blockSize); - - // Instantiated with Yes.multiblock, can allocate more than one block at a time - void[] buf = a.allocate(2 * blockSize); - assert(buf.length == 2 * blockSize); - assert(a.deallocate(buf)); - - // Can also allocate less than one block - buf = a.allocate(blockSize / 2); - assert(buf.length == blockSize / 2); - - // Expands inside the same block - assert(a.expand(buf, blockSize / 2)); - assert(buf.length == blockSize); - - // If Yes.multiblock, can expand past the size of a single block - assert(a.expand(buf, 3 * blockSize)); - assert(buf.length == 4 * blockSize); - assert(a.deallocate(buf)); -} - -/// -@system unittest -{ - import std.experimental.allocator.mallocator : Mallocator; - import std.typecons : Flag, No; - - enum blockSize = 64; - auto a = BitmappedBlock!(blockSize, 8, Mallocator, No.multiblock)(1024 * blockSize); - - // Since instantiated with No.multiblock, can only allocate at most the block size - void[] buf = a.allocate(blockSize + 1); - assert(buf is null); - - buf = a.allocate(blockSize); - assert(buf.length == blockSize); - assert(a.deallocate(buf)); - - // This is also fine, because it's less than the block size - buf = a.allocate(blockSize / 2); - assert(buf.length == blockSize / 2); - - // Can expand the buffer until its length is at most 64 - assert(a.expand(buf, blockSize / 2)); - assert(buf.length == blockSize); - - // Cannot expand anymore - assert(!a.expand(buf, 1)); - assert(a.deallocate(buf)); -} - -// Test instantiation with stateful allocators -@system unittest -{ - import std.experimental.allocator.mallocator : Mallocator; - import std.experimental.allocator.building_blocks.region : Region; - auto r = Region!Mallocator(1024 * 96); - auto a = BitmappedBlock!(chooseAtRuntime, 8, Region!Mallocator*, No.multiblock)(&r, 1024 * 64, 1024); -} - -/** -The threadsafe version of the $(LREF BitmappedBlock). -The semantics of the `SharedBitmappedBlock` are identical to the regular $(LREF BitmappedBlock). - -Params: - theBlockSize = the length of a block, which must be a multiple of `theAlignment` - - theAlignment = alignment of each block - - ParentAllocator = allocator from which the `BitmappedBlock` will draw memory. - If set to `NullAllocator`, the storage must be passed via the constructor - - f = `Yes.multiblock` to support allocations spanning across multiple blocks and - `No.multiblock` to support single block allocations. - Although limited by single block allocations, `No.multiblock` will generally - provide higher performance. -*/ -shared struct SharedBitmappedBlock(size_t theBlockSize, uint theAlignment = platformAlignment, - ParentAllocator = NullAllocator, Flag!"multiblock" f = Yes.multiblock) -{ - version (StdDdoc) - { - /** - Constructs a block allocator given a hunk of memory, or a desired capacity - in bytes. - $(UL - $(LI If `ParentAllocator` is $(REF_ALTTEXT `NullAllocator`, NullAllocator, std,experimental,allocator,building_blocks,null_allocator), - only the constructor taking `data` is defined and the user is responsible for freeing `data` if desired.) - $(LI Otherwise, both constructors are defined. The `data`-based - constructor assumes memory has been allocated with the parent allocator. - The `capacity`-based constructor uses `ParentAllocator` to allocate - an appropriate contiguous hunk of memory. Regardless of the constructor - used, the destructor releases the memory by using `ParentAllocator.deallocate`.) - ) - */ - this(ubyte[] data); - - /// Ditto - this(ubyte[] data, uint blockSize); - - /// Ditto - this(size_t capacity); - - /// Ditto - this(ParentAllocator parent, size_t capacity); - - /// Ditto - this(size_t capacity, uint blockSize); - - /// Ditto - this(ParentAllocator parent, size_t capacity, uint blockSize); - - /** - If `blockSize == chooseAtRuntime`, `SharedBitmappedBlock` offers a read/write - property `blockSize`. It must be set before any use of the allocator. - Otherwise (i.e. `theBlockSize` is a legit constant), `blockSize` is - an alias for `theBlockSize`. Whether constant or variable, must also be - a multiple of `alignment`. This constraint is `assert`ed statically - and dynamically. - */ - alias blockSize = theBlockSize; - - /** - The _alignment offered is user-configurable statically through parameter - `theAlignment`, defaulted to `platformAlignment`. - */ - alias alignment = theAlignment; - - /** - The _parent allocator. Depending on whether `ParentAllocator` holds state - or not, this is a member variable or an alias for - `ParentAllocator.instance`. - */ - ParentAllocator parent; - - /** - Returns the actual bytes allocated when `n` bytes are requested, i.e. - `n.roundUpToMultipleOf(blockSize)`. - */ - pure nothrow @safe @nogc - size_t goodAllocSize(size_t n); - - /** - Returns `Ternary.yes` if `b` belongs to the `SharedBitmappedBlock` object, - `Ternary.no` otherwise. Never returns `Ternary.unkown`. (This - method is somewhat tolerant in that accepts an interior slice.) - */ - pure nothrow @trusted @nogc - Ternary owns(const void[] b) const; - - /** - Expands in place a buffer previously allocated by `SharedBitmappedBlock`. - Expansion fails if the new length exceeds the block size. - */ - bool expand(ref void[] b, immutable size_t delta); - - /** - Deallocates the given buffer `b`, by atomically setting the corresponding - bit to `0`. `b` must be valid, and cannot contain multiple adjacent `blocks`. - */ - nothrow @nogc - bool deallocate(void[] b); - - /** - Allocates `s` bytes of memory and returns it, or `null` if memory - could not be allocated. - - The `SharedBitmappedBlock` cannot allocate more than the given block size. - Allocations are satisfied by searching the first unset bit in the bitmap, - and atomically setting it. - In rare memory pressure scenarios, the allocation could fail. - */ - nothrow @trusted @nogc - void[] allocate(const size_t s); - - /** - Allocates s bytes of memory and returns it, or `null` if memory could not be allocated. - `allocateFresh` behaves just like allocate, the only difference being that this always - returns unused(fresh) memory. Although there may still be available space in the `SharedBitmappedBlock`, - `allocateFresh` could still return null, because all the available blocks have been previously deallocated. - */ - @trusted void[] allocateFresh(const size_t s); - - /** - If the `SharedBitmappedBlock` object is empty (has no active allocation), allocates - all memory within and returns a slice to it. Otherwise, returns `null` - (i.e. no attempt is made to allocate the largest available block). - */ - void[] allocateAll(); - - /** - Returns `Ternary.yes` if no memory is currently allocated with this - allocator, otherwise `Ternary.no`. This method never returns - `Ternary.unknown`. - */ - nothrow @safe @nogc - Ternary empty(); - - /** - Forcibly deallocates all memory allocated by this allocator, making it - available for further allocations. Does not return memory to `ParentAllocator`. - */ - nothrow @nogc - bool deallocateAll(); - - /** - Reallocates a block previously allocated with `alignedAllocate`. Contractions do not occur in place. - */ - @system bool alignedReallocate(ref void[] b, size_t newSize, uint a); - - /** - Reallocates a previously-allocated block. Contractions occur in place. - */ - @system bool reallocate(ref void[] b, size_t newSize); - - /** - Allocates a block with specified alignment `a`. The alignment must be a - power of 2. If `a <= alignment`, function forwards to `allocate`. - Otherwise, it attempts to overallocate and then adjust the result for - proper alignment. In the worst case the slack memory is around two blocks. - */ - void[] alignedAllocate(size_t n, uint a); - - /** - If `ParentAllocator` is not `NullAllocator` and defines `deallocate`, - the destructor is defined to deallocate the block held. - */ - ~this(); - } - else - { - version (StdUnittest) - @system unittest - { - import std.algorithm.comparison : max; - import std.experimental.allocator.mallocator : AlignedMallocator; - auto m = cast(ubyte[])(AlignedMallocator.instance.alignedAllocate(1024 * 64, - max(theAlignment, cast(uint) size_t.sizeof))); - scope(exit) () nothrow @nogc { AlignedMallocator.instance.deallocate(m); }(); - static if (theBlockSize == chooseAtRuntime) - { - testAllocator!(() => SharedBitmappedBlock!(theBlockSize, theAlignment, NullAllocator)(m, 64)); - } - else - { - testAllocator!(() => SharedBitmappedBlock!(theBlockSize, theAlignment, NullAllocator)(m)); - } - } - mixin BitmappedBlockImpl!(true, f == Yes.multiblock); - } -} - -/// -@system unittest -{ - import std.experimental.allocator.mallocator : Mallocator; - import std.experimental.allocator.common : platformAlignment; - import std.typecons : Flag, Yes, No; - - // Create 'numThreads' threads, each allocating in parallel a chunk of memory - static void testAlloc(Allocator)(ref Allocator a, size_t allocSize) - { - import core.thread : ThreadGroup; - import std.algorithm.sorting : sort; - import core.internal.spinlock : SpinLock; - - SpinLock lock = SpinLock(SpinLock.Contention.brief); - enum numThreads = 10; - void[][numThreads] buf; - size_t count = 0; - - // Each threads allocates 'allocSize' - void fun() - { - void[] b = a.allocate(allocSize); - assert(b.length == allocSize); - - lock.lock(); - scope(exit) lock.unlock(); - - buf[count] = b; - count++; - } - - auto tg = new ThreadGroup; - foreach (i; 0 .. numThreads) - { - tg.create(&fun); - } - tg.joinAll(); - - // Sorting the allocations made by each thread, we expect the buffers to be - // adjacent inside the SharedBitmappedBlock - sort!((a, b) => a.ptr < b.ptr)(buf[0 .. numThreads]); - foreach (i; 0 .. numThreads - 1) - { - assert(buf[i].ptr + a.goodAllocSize(buf[i].length) <= buf[i + 1].ptr); - } - - // Deallocate everything - foreach (i; 0 .. numThreads) - { - assert(a.deallocate(buf[i])); - } - } - - enum blockSize = 64; - auto alloc1 = SharedBitmappedBlock!(blockSize, platformAlignment, Mallocator, Yes.multiblock)(1024 * 1024); - auto alloc2 = SharedBitmappedBlock!(blockSize, platformAlignment, Mallocator, No.multiblock)(1024 * 1024); - testAlloc(alloc1, 2 * blockSize); - testAlloc(alloc2, blockSize); -} - -@system unittest -{ - // Test chooseAtRuntime - // Create a block allocator on top of a 10KB stack region. - import std.experimental.allocator.building_blocks.region : InSituRegion; - import std.traits : hasMember; - InSituRegion!(10_240, 64) r; - uint blockSize = 64; - auto a = BitmappedBlock!(chooseAtRuntime, 64)(cast(ubyte[])(r.allocateAll()), blockSize); - static assert(hasMember!(InSituRegion!(10_240, 64), "allocateAll")); - const b = (() pure nothrow @safe @nogc => a.allocate(100))(); - assert(b.length == 100); -} - -pure @safe unittest -{ - import std.typecons : Ternary; - - auto a = (() @trusted => BitmappedBlock!(64, 64, NullAllocator, Yes.multiblock)(new ubyte[10_240]))(); - () nothrow @nogc { - assert(a.empty == Ternary.yes); - const b = a.allocate(100); - assert(b.length == 100); - assert(a.empty == Ternary.no); - }(); -} - -@safe unittest -{ - import std.typecons : Ternary; - - auto a = (() @trusted => SharedBitmappedBlock!(64, 64, NullAllocator, Yes.multiblock)(new ubyte[10_240]))(); - assert((() nothrow @safe @nogc => a.empty)() == Ternary.yes); - const b = a.allocate(100); - assert(b.length == 100); -} - -version (StdUnittest) -@system unittest -{ - import std.experimental.allocator.gc_allocator : GCAllocator; - testAllocator!(() => BitmappedBlock!(64, 8, GCAllocator)(1024 * 64)); -} - -version (StdUnittest) -@system unittest -{ - // Test chooseAtRuntime - import std.experimental.allocator.gc_allocator : GCAllocator; - uint blockSize = 64; - testAllocator!(() => BitmappedBlock!(chooseAtRuntime, 8, GCAllocator, Yes.multiblock)(1024 * 64, blockSize)); - testAllocator!(() => BitmappedBlock!(chooseAtRuntime, 8, GCAllocator, No.multiblock)(1024 * 64, blockSize)); -} - -version (StdUnittest) -@system unittest -{ - import std.experimental.allocator.mallocator : Mallocator; - testAllocator!(() => SharedBitmappedBlock!(64, 8, Mallocator, Yes.multiblock)(1024 * 64)); - testAllocator!(() => SharedBitmappedBlock!(64, 8, Mallocator, No.multiblock)(1024 * 64)); -} - -version (StdUnittest) -@system unittest -{ - // Test chooseAtRuntime - import std.experimental.allocator.mallocator : Mallocator; - uint blockSize = 64; - testAllocator!(() => SharedBitmappedBlock!(chooseAtRuntime, 8, Mallocator, Yes.multiblock)(1024 * 64, blockSize)); - testAllocator!(() => SharedBitmappedBlock!(chooseAtRuntime, 8, Mallocator, No.multiblock)(1024 * 64, blockSize)); -} - -@system unittest -{ - static void testAllocateAll(size_t bs, bool isShared = true)(size_t blocks, uint blocksAtATime) - { - template attribAllocate(string size) - { - static if (isShared) - { - const char[] attribAllocate = "(() nothrow @safe @nogc => a.allocate(" ~ size ~ "))()"; - } - else - { - const char[] attribAllocate = "(() pure nothrow @safe @nogc => a.allocate(" ~ size ~ "))()"; - } - } - - assert(bs); - import std.typecons : Ternary; - import std.algorithm.comparison : min; - import std.experimental.allocator.gc_allocator : GCAllocator; - - static if (isShared) - { - auto a = SharedBitmappedBlock!(bs, min(bs, platformAlignment), NullAllocator)( - cast(ubyte[])(GCAllocator.instance.allocate((blocks * bs * 8 + blocks) / 8))); - } - else - { - auto a = BitmappedBlock!(bs, min(bs, platformAlignment), NullAllocator)( - cast(ubyte[])(GCAllocator.instance.allocate((blocks * bs * 8 + blocks) / 8))); - } - - import std.conv : text; - assert(blocks >= a._blocks, text(blocks, " < ", a._blocks)); - blocks = a._blocks; - - // test allocation of 0 bytes - auto x = mixin(attribAllocate!("0")); - assert(x is null); - // test allocation of 1 byte - x = mixin(attribAllocate!("1")); - assert(x.length == 1 || blocks == 0); - assert((() nothrow @nogc => a.deallocateAll())()); - assert(a.empty() == Ternary.yes); - bool twice = true; - - begin: - foreach (i; 0 .. blocks / blocksAtATime) - { - auto b = mixin(attribAllocate!("bs * blocksAtATime")); - assert(b.length == bs * blocksAtATime, text(i, ": ", b.length)); - } - - assert(mixin(attribAllocate!("bs * blocksAtATime")) is null); - if (a._blocks % blocksAtATime == 0) - { - assert(mixin(attribAllocate!("1")) is null); - } - - // Now deallocate all and do it again! - assert((() nothrow @nogc => a.deallocateAll())()); - - // Test deallocation - - auto v = new void[][blocks / blocksAtATime]; - foreach (i; 0 .. blocks / blocksAtATime) - { - auto b = mixin(attribAllocate!("bs * blocksAtATime")); - assert(b.length == bs * blocksAtATime, text(i, ": ", b.length)); - v[i] = b; - } - assert(mixin(attribAllocate!("bs * blocksAtATime")) is null); - if (a._blocks % blocksAtATime == 0) - { - assert(mixin(attribAllocate!("1")) is null); - } - - foreach (i; 0 .. blocks / blocksAtATime) - { - () nothrow @nogc { a.deallocate(v[i]); }(); - } - - foreach (i; 0 .. blocks / blocksAtATime) - { - auto b = mixin(attribAllocate!("bs * blocksAtATime")); - assert(b.length == bs * blocksAtATime, text(i, ": ", b.length)); - v[i] = b; - } - - foreach (i; 0 .. v.length) - { - () nothrow @nogc { a.deallocate(v[i]); }(); - } - - if (twice) - { - twice = false; - goto begin; - } - - assert((() nothrow @nogc => a.deallocateAll())()); - - // test expansion - if (blocks >= blocksAtATime) - { - foreach (i; 0 .. blocks / blocksAtATime - 1) - { - auto b = mixin(attribAllocate!("bs * blocksAtATime")); - assert(b.length == bs * blocksAtATime, text(i, ": ", b.length)); - (cast(ubyte[]) b)[] = 0xff; - static if (isShared) - { - assert((() nothrow @safe @nogc => a.expand(b, blocksAtATime * bs))() - , text(i)); - } - else - { - assert((() pure nothrow @safe @nogc => a.expand(b, blocksAtATime * bs))() - , text(i)); - } - (cast(ubyte[]) b)[] = 0xfe; - assert(b.length == bs * blocksAtATime * 2, text(i, ": ", b.length)); - a.reallocate(b, blocksAtATime * bs) || assert(0); - assert(b.length == bs * blocksAtATime, text(i, ": ", b.length)); - } - } - } - - testAllocateAll!(1)(0, 1); - testAllocateAll!(1, false)(0, 1); - testAllocateAll!(1)(8, 1); - testAllocateAll!(1, false)(8, 1); - - testAllocateAll!(4096)(128, 1); - testAllocateAll!(4096, false)(128, 1); - - testAllocateAll!(1)(0, 2); - testAllocateAll!(1)(128, 2); - testAllocateAll!(4096)(128, 2); - - testAllocateAll!(1, false)(0, 2); - testAllocateAll!(1, false)(128, 2); - testAllocateAll!(4096, false)(128, 2); - - testAllocateAll!(1)(0, 4); - testAllocateAll!(1)(128, 4); - testAllocateAll!(4096)(128, 4); - - testAllocateAll!(1, false)(0, 4); - testAllocateAll!(1, false)(128, 4); - testAllocateAll!(4096, false)(128, 4); - - testAllocateAll!(1)(0, 3); - testAllocateAll!(1)(24, 3); - testAllocateAll!(3008)(100, 1); - testAllocateAll!(3008)(100, 3); - - testAllocateAll!(1, false)(0, 3); - testAllocateAll!(1, false)(24, 3); - testAllocateAll!(3008, false)(100, 1); - testAllocateAll!(3008, false)(100, 3); - - testAllocateAll!(1)(0, 128); - testAllocateAll!(1)(128 * 1, 128); - testAllocateAll!(128 * 20)(13 * 128, 128); - - testAllocateAll!(1, false)(0, 128); - testAllocateAll!(1, false)(128 * 1, 128); - testAllocateAll!(128 * 20, false)(13 * 128, 128); -} - -@system unittest -{ - import std.experimental.allocator.mallocator : Mallocator; - - enum blocks = 10000; - int count = 0; - - ubyte[] payload = cast(ubyte[]) Mallocator.instance.allocate(blocks * 16); - auto a = BitmappedBlock!(16, 16)(payload); - void[][] buf = cast(void[][]) Mallocator.instance.allocate((void[]).sizeof * blocks); - - assert(!a.allocateFresh(0)); - assert(!a._control[0]); - - void[] b = a.allocate(256 * 16); - assert(b.length == 256 * 16); - count += 256; - - assert(!a._control[count]); - b = a.allocateFresh(16); - assert(b.length == 16); - count++; - assert(a._control[count - 1]); - - b = a.allocateFresh(16 * 300); - assert(b.length == 16 * 300); - count += 300; - - for (int i = 0; i < count; i++) - assert(a._control[i]); - assert(!a._control[count]); - - assert(a.expand(b, 313 * 16)); - count += 313; - - for (int i = 0; i < count; i++) - assert(a._control[i]); - assert(!a._control[count]); - - b = a.allocate(64 * 16); - assert(b.length == 64 * 16); - count += 64; - - b = a.allocateFresh(16); - assert(b.length == 16); - count++; - - for (int i = 0; i < count; i++) - assert(a._control[i]); - assert(!a._control[count]); - - assert(a.deallocateAll()); - for (int i = 0; i < a._blocks; i++) - assert(!a._control[i]); - - b = a.allocateFresh(257 * 16); - assert(b.length == 257 * 16); - for (int i = 0; i < count; i++) - assert(!a._control[i]); - for (int i = count; i < count + 257; i++) - assert(a._control[i]); - count += 257; - assert(!a._control[count]); - - while (true) - { - b = a.allocate(16); - if (!b) - break; - assert(b.length == 16); - } - - assert(!a.allocateFresh(16)); - assert(a.deallocateAll()); - - assert(a.allocate(16).length == 16); - assert(!a.allocateFresh(16)); -} - - -@system unittest -{ - import std.experimental.allocator.mallocator : Mallocator; - import std.random; - - static void testAlloc(Allocator)() - { - auto numBlocks = [1, 64, 256]; - enum blocks = 10000; - int iter = 0; - - ubyte[] payload = cast(ubyte[]) Mallocator.instance.allocate(blocks * 16); - auto a = Allocator(payload); - void[][] buf = cast(void[][]) Mallocator.instance.allocate((void[]).sizeof * blocks); - - auto rnd = Random(); - while (iter < blocks) - { - int event = uniform(0, 2, rnd); - int doExpand = uniform(0, 2, rnd); - int allocSize = numBlocks[uniform(0, 3, rnd)] * 16; - int expandSize = numBlocks[uniform(0, 3, rnd)] * 16; - int doDeallocate = uniform(0, 2, rnd); - - if (event) buf[iter] = a.allocate(allocSize); - else buf[iter] = a.allocateFresh(allocSize); - - if (!buf[iter]) - break; - assert(buf[iter].length == allocSize); - - auto oldSize = buf[iter].length; - if (doExpand && a.expand(buf[iter], expandSize)) - assert(buf[iter].length == expandSize + oldSize); - - if (doDeallocate) - { - assert(a.deallocate(buf[iter])); - buf[iter] = null; - } - - iter++; - } - - while (iter < blocks) - { - buf[iter++] = a.allocate(16); - if (!buf[iter - 1]) - break; - assert(buf[iter - 1].length == 16); - } - - for (size_t i = 0; i < a._blocks; i++) - assert((cast(BitVector) a._control)[i]); - - assert(!a.allocate(16)); - for (size_t i = 0; i < iter; i++) - { - if (buf[i]) - assert(a.deallocate(buf[i])); - } - - for (size_t i = 0; i < a._blocks; i++) - assert(!(cast(BitVector) a._control)[i]); - } - - testAlloc!(BitmappedBlock!(16, 16))(); - testAlloc!(SharedBitmappedBlock!(16, 16))(); -} - -// Test totalAllocation and goodAllocSize -nothrow @safe @nogc unittest -{ - BitmappedBlock!(8, 8, NullAllocator) h1; - assert(h1.goodAllocSize(1) == 8); - assert(h1.totalAllocation(1) >= 8); - assert(h1.totalAllocation(64) >= 64); - assert(h1.totalAllocation(8 * 64) >= 8 * 64); - assert(h1.totalAllocation(8 * 63) >= 8 * 63); - assert(h1.totalAllocation(8 * 64 + 1) >= 8 * 65); - - BitmappedBlock!(64, 8, NullAllocator) h2; - assert(h2.goodAllocSize(1) == 64); - assert(h2.totalAllocation(1) >= 64); - assert(h2.totalAllocation(64 * 64) >= 64 * 64); - - BitmappedBlock!(4096, 4096, NullAllocator) h3; - assert(h3.goodAllocSize(1) == 4096); - assert(h3.totalAllocation(1) >= 4096); - assert(h3.totalAllocation(64 * 4096) >= 64 * 4096); - assert(h3.totalAllocation(64 * 4096 + 1) >= 65 * 4096); -} - -// Test owns -@system unittest -{ - import std.experimental.allocator.gc_allocator : GCAllocator; - import std.typecons : Ternary; - - auto a = BitmappedBlock!(64, 8, GCAllocator)(1024 * 64); - const void[] buff = (() pure nothrow @safe @nogc => a.allocate(42))(); - - assert((() nothrow @safe @nogc => a.owns(buff))() == Ternary.yes); - assert((() nothrow @safe @nogc => a.owns(null))() == Ternary.no); -} - -// BitmappedBlockWithInternalPointers -/** - -A `BitmappedBlock` with additional structure for supporting `resolveInternalPointer`. -To that end, `BitmappedBlockWithInternalPointers` adds a -bitmap (one bit per block) that marks object starts. The bitmap itself has -variable size and is allocated together with regular allocations. - -The time complexity of `resolveInternalPointer` is $(BIGOH k), where `k` -is the size of the object within which the internal pointer is looked up. - -*/ -struct BitmappedBlockWithInternalPointers( - size_t theBlockSize, uint theAlignment = platformAlignment, - ParentAllocator = NullAllocator) -{ - import std.conv : text; - import std.typecons : Ternary; - - static if (!stateSize!ParentAllocator) - version (StdUnittest) - @system unittest - { - import std.experimental.allocator.mallocator : AlignedMallocator; - auto m = cast(ubyte[])(AlignedMallocator.instance.alignedAllocate(1024 * 64, - theAlignment)); - scope(exit) () nothrow @nogc { AlignedMallocator.instance.deallocate(m); }(); - testAllocator!(() => BitmappedBlockWithInternalPointers(m)); - } - - // state { - private BitmappedBlock!(theBlockSize, theAlignment, ParentAllocator) _heap; - private BitVector _allocStart; - // } - - /** - Constructors accepting desired capacity or a preallocated buffer, similar - in semantics to those of `BitmappedBlock`. - */ - static if (!stateSize!ParentAllocator) - this(ubyte[] data) - { - _heap = BitmappedBlock!(theBlockSize, theAlignment, ParentAllocator)(data); - } - - static if (stateSize!ParentAllocator) - this(ParentAllocator parent, ubyte[] data) - { - _heap = BitmappedBlock!(theBlockSize, theAlignment, ParentAllocator)(data); - _heap.parent = parent; - } - - /// Ditto - static if (!is(ParentAllocator == NullAllocator) && !stateSize!ParentAllocator) - this(size_t capacity) - { - // Add room for the _allocStart vector - _heap = BitmappedBlock!(theBlockSize, theAlignment, ParentAllocator) - (capacity + capacity.divideRoundUp(64)); - } - - /// Ditto - static if (!is(ParentAllocator == NullAllocator) && stateSize!ParentAllocator) - this(ParentAllocator parent, size_t capacity) - { - // Add room for the _allocStart vector - _heap = BitmappedBlock!(theBlockSize, theAlignment, ParentAllocator) - (parent, capacity + capacity.divideRoundUp(64)); - } - - // Makes sure there's enough room for _allocStart - @safe - private bool ensureRoomForAllocStart(size_t len) - { - if (_allocStart.length >= len) return true; - // Must ensure there's room - immutable oldLength = _allocStart.rep.length; - immutable bits = len.roundUpToMultipleOf(64); - void[] b = _allocStart.rep; - if ((() @trusted => !_heap.reallocate(b, bits / 8))()) return false; - assert(b.length * 8 == bits); - _allocStart = BitVector((() @trusted => cast(ulong[]) b)()); - assert(_allocStart.rep.length * 64 == bits); - _allocStart.rep[oldLength .. $] = ulong.max; - return true; - } - - /** - Allocator primitives. - */ - alias alignment = theAlignment; - - /// Ditto - pure nothrow @safe @nogc - size_t goodAllocSize(size_t n) - { - return n.roundUpToMultipleOf(_heap.blockSize); - } - - /// Ditto - void[] allocate(size_t bytes) - { - auto r = _heap.allocate(bytes); - if (!r.ptr) return r; - immutable block = (() @trusted => (r.ptr - _heap._payload.ptr) / _heap.blockSize)(); - immutable blocks = - (r.length + _heap.blockSize - 1) / _heap.blockSize; - if (!ensureRoomForAllocStart(block + blocks)) - { - // Failed, free r and bailout - () @trusted { _heap.deallocate(r); r = null; }(); - return null; - } - assert(block < _allocStart.length); - assert(block + blocks <= _allocStart.length); - // Mark the _allocStart bits - assert(blocks > 0); - _allocStart[block] = 1; - _allocStart[block + 1 .. block + blocks] = 0; - assert(block + blocks == _allocStart.length - || _allocStart[block + blocks] == 1); - return r; - } - - /// Ditto - void[] allocateAll() - { - auto r = _heap.allocateAll(); - if (!r.ptr) return r; - // Carve space at the end for _allocStart - auto p = alignDownTo(r.ptr + r.length - 8, ulong.alignof); - r = r[0 .. p - r.ptr]; - // Initialize _allocStart - _allocStart = BitVector(cast(ulong[]) p[0 .. 8]); - _allocStart[] = 0; - immutable block = (r.ptr - _heap._payload.ptr) / _heap.blockSize; - assert(block < _allocStart.length); - _allocStart[block] = 1; - return r; - } - - /// Ditto - bool expand(ref void[] b, size_t bytes) - { - if (!bytes) return true; - if (b is null) return false; - immutable oldBlocks = - (b.length + _heap.blockSize - 1) / _heap.blockSize; - assert(oldBlocks); - immutable newBlocks = - (b.length + bytes + _heap.blockSize - 1) / _heap.blockSize; - assert(newBlocks >= oldBlocks); - immutable block = (() @trusted => (b.ptr - _heap._payload.ptr) / _heap.blockSize)(); - assert(_allocStart[block]); - if (!ensureRoomForAllocStart(block + newBlocks) - || !_heap.expand(b, bytes)) - { - return false; - } - // Zero only the expanded bits - _allocStart[block + oldBlocks .. block + newBlocks] = 0; - assert(_allocStart[block]); - return true; - } - - /// Ditto - bool deallocate(void[] b) - { - // No need to touch _allocStart here - except for the first bit, it's - // meaningless in freed memory. The first bit is already 1. - return _heap.deallocate(b); - // TODO: one smart thing to do is reduce memory occupied by - // _allocStart if we're freeing the rightmost block. - } - - /// Ditto - nothrow @safe @nogc - Ternary resolveInternalPointer(const void* p, ref void[] result) - { - if ((() @trusted => _heap._payload - && (p < &_heap._payload[0] - || p >= &_heap._payload[0] + _heap._payload.length))()) - { - return Ternary.no; - } - // Find block start - auto block = (() @trusted => (p - &_heap._payload[0]) / _heap.blockSize)(); - if (block >= _allocStart.length) return Ternary.no; - // Within an allocation, must find the 1 just to the left of it - auto i = _allocStart.find1Backward(block); - if (i == i.max) return Ternary.no; - auto j = _allocStart.find1(i + 1); - result = (() @trusted => _heap._payload.ptr[cast(size_t) (_heap.blockSize * i) - .. cast(size_t) (_heap.blockSize * j)])(); - return Ternary.yes; - } - - /// Ditto - Ternary empty() - { - return _heap.empty; - } - - // Currently unused - private void markAllAsUnused() - { - // Mark all deallocated memory with 1 so we minimize damage created by - // false pointers. TODO: improve speed. - foreach (i, ref e; _allocStart.rep) - { - // Set to 1 all bits in _allocStart[i] that were 0 in control, and - // leave the others unchanged. - // (0, 0) => 1; (0, 1) => 0; (1, 0) => 1; (1, 1) => 1 - e |= ~_heap._control.rep[i]; - } - // Now zero all control bits - _heap._control[] = 0; - // EXCEPT for the _allocStart block itself - markAsUsed(_allocStart.rep); - } - - // Currently unused - private bool markAsUsed(void[] b) - { - // Locate position - immutable pos = b.ptr - _heap._payload.ptr; - assert(pos % _heap.blockSize == 0); - auto blockIdx = pos / _heap.blockSize; - if (_heap._control[blockIdx]) return false; - // Round up size to multiple of block size - auto blocks = b.length.divideRoundUp(_heap.blockSize); - _heap._control[blockIdx .. blockIdx + blocks] = 1; - return true; - } - - // Currently unused - private void doneMarking() - { - // Nothing to do, what's free stays free. - } -} - -@system unittest -{ - import std.typecons : Ternary; - - auto h = BitmappedBlockWithInternalPointers!(4096)(new ubyte[4096 * 1024]); - assert((() nothrow @safe @nogc => h.empty)() == Ternary.yes); - auto b = (() pure nothrow @safe @nogc => h.allocate(123))(); - assert(b.length == 123); - assert((() nothrow @safe @nogc => h.empty)() == Ternary.no); - - void[] p; - void* offset = &b[0] + 17; - assert((() nothrow @safe @nogc => h.resolveInternalPointer(offset, p))() == Ternary.yes); - assert(p.ptr is b.ptr); - assert(p.length >= b.length); - b = (() pure nothrow @safe @nogc => h.allocate(4096))(); - - offset = &b[0]; - assert((() nothrow @safe @nogc => h.resolveInternalPointer(offset, p))() == Ternary.yes); - assert(p is b); - - offset = &b[0] + 11; - assert((() nothrow @safe @nogc => h.resolveInternalPointer(offset, p))() == Ternary.yes); - assert(p is b); - - void[] unchanged = p; - offset = &b[0] - 40_970; - assert((() nothrow @safe @nogc => h.resolveInternalPointer(offset, p))() == Ternary.no); - assert(p is unchanged); - - assert((() @safe => h.expand(b, 1))()); - assert(b.length == 4097); - offset = &b[0] + 4096; - assert((() nothrow @safe @nogc => h.resolveInternalPointer(offset, p))() == Ternary.yes); - assert(p.ptr is b.ptr); - - // Ensure deallocate inherits from parent - () nothrow @nogc { h.deallocate(b); }(); -} - -@system unittest -{ - auto h = BitmappedBlockWithInternalPointers!(4096)(new ubyte[4096 * 1024]); - assert((() pure nothrow @safe @nogc => h.goodAllocSize(1))() == 4096); -} - -// Test instantiation with stateful allocators -@system unittest -{ - import std.experimental.allocator.mallocator : Mallocator; - import std.experimental.allocator.building_blocks.region : Region; - auto r = Region!Mallocator(1024 * 1024); - auto h = BitmappedBlockWithInternalPointers!(4096, 8, Region!Mallocator*)(&r, 4096 * 1024); -} - -/** -Returns the number of most significant ones before a zero can be found in `x`. -If `x` contains no zeros (i.e. is equal to `ulong.max`), returns 64. -*/ -pure nothrow @safe @nogc -private uint leadingOnes(ulong x) -{ - import core.bitop : bsr; - const x_ = ~x; - return x_ == 0 ? 64 : (63 - bsr(x_)); -} - -@safe unittest -{ - assert(leadingOnes(0) == 0); - assert(leadingOnes(~0UL) == 64); - assert(leadingOnes(0xF000_0000_0000_0000) == 4); - assert(leadingOnes(0xE400_0000_0000_0000) == 3); - assert(leadingOnes(0xC700_0200_0000_0000) == 2); - assert(leadingOnes(0x8000_0030_0000_0000) == 1); - assert(leadingOnes(0x2000_0000_0000_0000) == 0); -} - -/** -Finds a run of contiguous ones in `x` of length at least `n`. -*/ -pure nothrow @safe @nogc -private uint findContigOnes(ulong x, uint n) -{ - while (n > 1) - { - immutable s = n >> 1; - x &= x << s; - n -= s; - } - return leadingOnes(~x); -} - -@safe unittest -{ - assert(findContigOnes(0x0000_0000_0000_0300, 2) == 54); - - assert(findContigOnes(~0UL, 1) == 0); - assert(findContigOnes(~0UL, 2) == 0); - assert(findContigOnes(~0UL, 32) == 0); - assert(findContigOnes(~0UL, 64) == 0); - assert(findContigOnes(0UL, 1) == 64); - - assert(findContigOnes(0x4000_0000_0000_0000, 1) == 1); - assert(findContigOnes(0x0000_0F00_0000_0000, 4) == 20); -} - -/* -Unconditionally sets the bits from lsb through msb in w to zero. -*/ -pure nothrow @safe @nogc -private void setBits(ref ulong w, uint lsb, uint msb) -{ - assert(lsb <= msb && msb < 64); - const mask = (ulong.max << lsb) & (ulong.max >> (63 - msb)); - w |= mask; -} - -@safe unittest -{ - ulong w; - w = 0; setBits(w, 0, 63); assert(w == ulong.max); - w = 0; setBits(w, 1, 63); assert(w == ulong.max - 1); - w = 6; setBits(w, 0, 1); assert(w == 7); - w = 6; setBits(w, 3, 3); assert(w == 14); -} - -/* Are bits from lsb through msb in w zero? If so, make then 1 -and return the resulting w. Otherwise, just return 0. -*/ -pure nothrow @safe @nogc -private bool setBitsIfZero(ref ulong w, uint lsb, uint msb) -{ - assert(lsb <= msb && msb < 64); - const mask = (ulong.max << lsb) & (ulong.max >> (63 - msb)); - if (w & mask) return false; - w |= mask; - return true; -} - -// Assigns bits in w from lsb through msb to zero. -pure nothrow @safe @nogc -private void resetBits(ref ulong w, uint lsb, uint msb) -{ - assert(lsb <= msb && msb < 64); - const mask = (ulong.max << lsb) & (ulong.max >> (63 - msb)); - w &= ~mask; -} - -/* -Bit disposition is MSB=0 (leftmost, big endian). -*/ -private struct BitVector -{ - ulong[] _rep; - - auto rep(this _)() { return _rep; } - - pure nothrow @safe @nogc - this(ulong[] data) { _rep = data; } - - pure nothrow @safe @nogc - void opSliceAssign(bool b) { _rep[] = b ? ulong.max : 0; } - - pure nothrow @safe @nogc - void opSliceAssign(bool b, ulong x, ulong y) - { - assert(x <= y && y <= _rep.length * 64); - if (x == y) return; - --y; - assert(x / 64 <= size_t.max); - immutable i1 = cast(size_t) (x / 64); - immutable uint b1 = 63 - x % 64; - assert(y / 64 <= size_t.max); - immutable i2 = cast(size_t) (y / 64); - immutable uint b2 = 63 - y % 64; - assert(i1 <= i2 && i2 < _rep.length); - if (i1 == i2) - { - // Inside the same word - assert(b1 >= b2); - if (b) setBits(_rep[i1], b2, b1); - else resetBits(_rep[i1], b2, b1); - } - else - { - // Spans multiple words - assert(i1 < i2); - if (b) setBits(_rep[i1], 0, b1); - else resetBits(_rep[i1], 0, b1); - _rep[i1 + 1 .. i2] = (b ? ulong.max : 0); - if (b) setBits(_rep[i2], b2, 63); - else resetBits(_rep[i2], b2, 63); - } - } - - pure nothrow @safe @nogc - bool opIndex(ulong x) - { - assert(x < length); - return (_rep[cast(size_t) (x / 64)] - & (0x8000_0000_0000_0000UL >> (x % 64))) != 0; - } - - pure nothrow @safe @nogc - void opIndexAssign(bool b, ulong x) - { - assert(x / 64 <= size_t.max); - immutable i = cast(size_t) (x / 64); - immutable j = 0x8000_0000_0000_0000UL >> (x % 64); - if (b) _rep[i] |= j; - else _rep[i] &= ~j; - } - - pure nothrow @safe @nogc - ulong length() const - { - return _rep.length * 64; - } - - /* Returns the index of the first 1 to the right of i (including i itself), - or length if not found. - */ - pure nothrow @safe @nogc - ulong find1(ulong i) - { - assert(i < length); - assert(i / 64 <= size_t.max); - auto w = cast(size_t) (i / 64); - immutable b = i % 64; // 0 through 63, 0 when i == 0 - immutable mask = ulong.max >> b; - if (auto current = _rep[w] & mask) - { - // Great, found - return w * 64 + leadingOnes(~current); - } - // The current word doesn't have the solution, find the leftmost 1 - // going to the right. - for (++w; w < _rep.length; ++w) - { - if (auto current = _rep[w]) - { - return w * 64 + leadingOnes(~current); - } - } - return length; - } - - /* Returns the index of the first 1 to the left of i (including i itself), - or ulong.max if not found. - */ - pure nothrow @safe @nogc - ulong find1Backward(ulong i) - { - assert(i < length); - auto w = cast(size_t) (i / 64); - immutable b = 63 - (i % 64); // 0 through 63, 63 when i == 0 - immutable mask = ~((1UL << b) - 1); - assert(mask != 0); - // First, let's see if the current word has a bit larger than ours. - if (auto currentWord = _rep[w] & mask) - { - // Great, this word contains the result. - return w * 64 + 63 - currentWord.trailingZeros; - } - // The current word doesn't have the solution, find the rightmost 1 - // going to the left. - while (w >= 1) - { - --w; - if (auto currentWord = _rep[w]) - return w * 64 + (63 - currentWord.trailingZeros); - } - return ulong.max; - } - - /// Are all bits zero? - pure nothrow @safe @nogc - bool allAre0() const - { - foreach (w; _rep) if (w) return false; - return true; - } - - /// Are all bits one? - pure nothrow @safe @nogc - bool allAre1() const - { - foreach (w; _rep) if (w != ulong.max) return false; - return true; - } - - pure nothrow @safe @nogc - ulong findZeros(immutable size_t howMany, ulong start) - { - assert(start < length); - assert(howMany > 64); - auto i = cast(size_t) (start / 64); - while (_rep[i] & 1) - { - // No trailing zeros in this word, try the next one - if (++i == _rep.length) return ulong.max; - start = i * 64; - } - // Adjust start to have only trailing zeros after it - auto prefixLength = 64; - while (_rep[i] & (ulong.max >> (64 - prefixLength))) - { - assert(prefixLength > 0); - --prefixLength; - ++start; - } - - assert(howMany > prefixLength); - auto needed = howMany - prefixLength; - for (++i; needed >= 64; needed -= 64, ++i) - { - if (i >= _rep.length) return ulong.max; - if (_rep[i] != 0) return findZeros(howMany, i * 64); - } - // Leftover < 64 bits - assert(needed < 64); - if (!needed) return start; - if (i >= _rep.length) return ulong.max; - if (leadingOnes(~_rep[i]) >= needed) return start; - return findZeros(howMany, i * 64); - } -} - -@safe unittest -{ - auto v = BitVector(new ulong[10]); - assert(v.length == 640); - - v[] = 0; - v[53] = 1; - assert(v[52] == 0); - assert(v[53] == 1); - assert(v[54] == 0); - - v[] = 0; - v[53 .. 55] = 1; - assert(v[52] == 0); - assert(v[53] == 1); - assert(v[54] == 1); - assert(v[55] == 0); - - v[] = 0; - v[2 .. 65] = 1; - assert(v.rep[0] == 0x3FFF_FFFF_FFFF_FFFF); - assert(v.rep[1] == 0x8000_0000_0000_0000); - assert(v.rep[2] == 0); - - v[] = 0; - assert(v.find1Backward(0) == ulong.max); - assert(v.find1Backward(43) == ulong.max); - assert(v.find1Backward(83) == ulong.max); - - v[0] = 1; - assert(v.find1Backward(0) == 0); - assert(v.find1Backward(43) == 0); - import std.conv : text; - assert(v.find1Backward(83) == 0, text(v.find1Backward(83))); - - v[0] = 0; - v[101] = 1; - assert(v.find1Backward(0) == ulong.max); - assert(v.find1Backward(43) == ulong.max); - assert(v.find1Backward(83) == ulong.max); - assert(v.find1Backward(100) == ulong.max); - assert(v.find1Backward(101) == 101); - assert(v.find1Backward(553) == 101); - - v[0 .. v.length] = 0; - v[v.length .. v.length] = 0; - v[0 .. 0] = 0; - - v[] = 0; - assert(v.find1(0) == v.length); - v[139] = 1; - assert(v.find1(0) == 139); - assert(v.find1(100) == 139); - assert(v.find1(138) == 139); - assert(v.find1(139) == 139); - assert(v.find1(140) == v.length); - - v[] = 0; - assert(v.findZeros(100, 0) == 0); - foreach (i; 0 .. 500) - assert(v.findZeros(100, i) == i, text(v.findZeros(100, i), " != ", i)); - assert(v.findZeros(540, 99) == 99); - assert(v.findZeros(99, 540) == 540); - assert(v.findZeros(540, 100) == 100); - assert(v.findZeros(640, 0) == 0); - assert(v.findZeros(641, 1) == ulong.max); - assert(v.findZeros(641, 100) == ulong.max); -} diff --git a/phobos/std/experimental/allocator/building_blocks/bucketizer.d b/phobos/std/experimental/allocator/building_blocks/bucketizer.d deleted file mode 100644 index aab1f60..0000000 --- a/phobos/std/experimental/allocator/building_blocks/bucketizer.d +++ /dev/null @@ -1,348 +0,0 @@ -// Written in the D programming language. -/** -Source: $(PHOBOSSRC std/experimental/allocator/building_blocks/bucketizer.d) -*/ -module std.experimental.allocator.building_blocks.bucketizer; - -/** - -A `Bucketizer` uses distinct allocators for handling allocations of sizes in -the intervals $(D [min, min + step - 1]), $(D [min + step, min + 2 * step - 1]), -$(D [min + 2 * step, min + 3 * step - 1]), `...`, $(D [max - step + 1, max]). - -`Bucketizer` holds a fixed-size array of allocators and dispatches calls to -them appropriately. The size of the array is $(D (max + 1 - min) / step), which -must be an exact division. - -Allocations for sizes smaller than `min` or larger than `max` are illegal -for `Bucketizer`. To handle them separately, `Segregator` may be of use. - -*/ -struct Bucketizer(Allocator, size_t min, size_t max, size_t step) -{ - import common = std.experimental.allocator.common : roundUpToMultipleOf, - alignedAt; - import std.traits : hasMember; - import std.typecons : Ternary; - - static assert((max - (min - 1)) % step == 0, - "Invalid limits when instantiating " ~ Bucketizer.stringof); - - // state - /** - The array of allocators is publicly available for e.g. initialization and - inspection. - */ - Allocator[(max + 1 - min) / step] buckets; - - pure nothrow @safe @nogc - private Allocator* allocatorFor(size_t n) - { - const i = (n - min) / step; - return i < buckets.length ? &buckets[i] : null; - } - - /** - The alignment offered is the same as `Allocator.alignment`. - */ - enum uint alignment = Allocator.alignment; - - /** - Rounds up to the maximum size of the bucket in which `bytes` falls. - */ - pure nothrow @safe @nogc - size_t goodAllocSize(size_t bytes) const - { - // round up bytes such that bytes - min + 1 is a multiple of step - assert(bytes >= min); - const min_1 = min - 1; - return min_1 + roundUpToMultipleOf(bytes - min_1, step); - } - - /** - Directs the call to either one of the `buckets` allocators. - */ - void[] allocate(size_t bytes) - { - if (!bytes) return null; - if (auto a = allocatorFor(bytes)) - { - const actual = goodAllocSize(bytes); - auto result = a.allocate(actual); - return result.ptr ? result.ptr[0 .. bytes] : null; - } - return null; - } - - static if (hasMember!(Allocator, "allocateZeroed")) - package(std) void[] allocateZeroed()(size_t bytes) - { - if (!bytes) return null; - if (auto a = allocatorFor(bytes)) - { - const actual = goodAllocSize(bytes); - auto result = a.allocateZeroed(actual); - return result.ptr ? result.ptr[0 .. bytes] : null; - } - return null; - } - - /** - Allocates the requested `bytes` of memory with specified `alignment`. - Directs the call to either one of the `buckets` allocators. Defined only - if `Allocator` defines `alignedAllocate`. - */ - static if (hasMember!(Allocator, "alignedAllocate")) - void[] alignedAllocate(size_t bytes, uint alignment) - { - if (!bytes) return null; - if (auto a = allocatorFor(bytes)) - { - const actual = goodAllocSize(bytes); - auto result = a.alignedAllocate(actual, alignment); - return result !is null ? (() @trusted => (&result[0])[0 .. bytes])() : null; - } - return null; - } - - /** - This method allows expansion within the respective bucket range. It succeeds - if both `b.length` and $(D b.length + delta) fall in a range of the form - $(D [min + k * step, min + (k + 1) * step - 1]). - */ - bool expand(ref void[] b, size_t delta) - { - if (!b || delta == 0) return delta == 0; - assert(b.length >= min && b.length <= max); - const available = goodAllocSize(b.length); - const desired = b.length + delta; - if (available < desired) return false; - b = (() @trusted => b.ptr[0 .. desired])(); - return true; - } - - /** - This method allows reallocation within the respective bucket range. If both - `b.length` and `size` fall in a range of the form $(D [min + k * - step, min + (k + 1) * step - 1]), then reallocation is in place. Otherwise, - reallocation with moving is attempted. - */ - bool reallocate(ref void[] b, size_t size) - { - if (size == 0) - { - deallocate(b); - b = null; - return true; - } - if (size >= b.length && expand(b, size - b.length)) - { - return true; - } - assert(b.length >= min && b.length <= max); - if (goodAllocSize(size) == goodAllocSize(b.length)) - { - b = b.ptr[0 .. size]; - return true; - } - // Move cross buckets - return common.reallocate(this, b, size); - } - - /** - Similar to `reallocate`, with alignment. Defined only if `Allocator` - defines `alignedReallocate`. - */ - static if (hasMember!(Allocator, "alignedReallocate")) - bool alignedReallocate(ref void[] b, size_t size, uint a) - { - if (size == 0) - { - deallocate(b); - b = null; - return true; - } - if (size >= b.length && b.ptr.alignedAt(a) && expand(b, size - b.length)) - { - return true; - } - assert(b.length >= min && b.length <= max); - if (goodAllocSize(size) == goodAllocSize(b.length) && b.ptr.alignedAt(a)) - { - b = b.ptr[0 .. size]; - return true; - } - // Move cross buckets - return common.alignedReallocate(this, b, size, a); - } - - /** - Defined only if `Allocator` defines `owns`. Finds the owner of `b` and forwards the call to it. - */ - static if (hasMember!(Allocator, "owns")) - Ternary owns(void[] b) - { - if (!b.ptr) return Ternary.no; - if (auto a = allocatorFor(b.length)) - { - const actual = goodAllocSize(b.length); - return a.owns(b.ptr[0 .. actual]); - } - return Ternary.no; - } - - /** - This method is only defined if `Allocator` defines `deallocate`. - */ - static if (hasMember!(Allocator, "deallocate")) - bool deallocate(void[] b) - { - if (!b.ptr) return true; - if (auto a = allocatorFor(b.length)) - { - a.deallocate(b.ptr[0 .. goodAllocSize(b.length)]); - } - return true; - } - - /** - This method is only defined if all allocators involved define $(D - deallocateAll), and calls it for each bucket in turn. Returns `true` if all - allocators could deallocate all. - */ - static if (hasMember!(Allocator, "deallocateAll")) - bool deallocateAll() - { - bool result = true; - foreach (ref a; buckets) - { - if (!a.deallocateAll()) result = false; - } - return result; - } - - /** - This method is only defined if all allocators involved define $(D - resolveInternalPointer), and tries it for each bucket in turn. - */ - static if (hasMember!(Allocator, "resolveInternalPointer")) - Ternary resolveInternalPointer(const void* p, ref void[] result) - { - foreach (ref a; buckets) - { - Ternary r = a.resolveInternalPointer(p, result); - if (r == Ternary.yes) return r; - } - return Ternary.no; - } -} - -/// -@system unittest -{ - import std.algorithm.comparison : max; - import std.experimental.allocator.building_blocks.allocator_list : AllocatorList; - import std.experimental.allocator.building_blocks.free_list : FreeList; - import std.experimental.allocator.building_blocks.region : Region; - import std.experimental.allocator.common : unbounded; - import std.experimental.allocator.mallocator : Mallocator; - import std.typecons : Ternary; - Bucketizer!( - FreeList!( - AllocatorList!( - (size_t n) => Region!Mallocator(max(n, 1024 * 1024))), - 0, unbounded), - 65, 512, 64) a; - auto b = a.allocate(400); - assert(b.length == 400); - assert(a.owns(b) == Ternary.yes); - a.deallocate(b); -} - -@system unittest -{ - import std.algorithm.comparison : max; - import std.experimental.allocator.building_blocks.allocator_list : AllocatorList; - import std.experimental.allocator.building_blocks.free_list : FreeList; - import std.experimental.allocator.building_blocks.region : Region; - import std.experimental.allocator.common : unbounded; - import std.experimental.allocator.mallocator : Mallocator; - import std.typecons : Ternary; - - Bucketizer!( - FreeList!( - AllocatorList!( - (size_t n) => Region!Mallocator(max(n, 1024 * 1024)), Mallocator), - 0, unbounded), - 65, 512, 64) a; - - assert((() pure nothrow @safe @nogc => a.goodAllocSize(65))() == 128); - - auto b = a.allocate(100); - assert(b.length == 100); - // Make reallocate use extend - assert((() nothrow @nogc => a.reallocate(b, 101))()); - assert(b.length == 101); - // Move cross buckets - assert((() nothrow @nogc => a.reallocate(b, 200))()); - assert(b.length == 200); - // Free through realloc - assert((() nothrow @nogc => a.reallocate(b, 0))()); - assert(b is null); - // Ensure deallocate inherits from parent allocators - assert((() nothrow @nogc => a.deallocate(b))()); - assert((() nothrow @nogc => a.deallocateAll())()); -} - -// Test alignedAllocate -@system unittest -{ - import std.experimental.allocator.building_blocks.bitmapped_block : BitmappedBlock; - import std.experimental.allocator.gc_allocator : GCAllocator; - - Bucketizer!(BitmappedBlock!(64, 8, GCAllocator), 65, 512, 64) a; - foreach (ref bucket; a.buckets) - { - bucket = BitmappedBlock!(64, 8, GCAllocator)(new ubyte[1024]); - } - - auto b = a.alignedAllocate(100, 16); - assert(b.length == 100); - assert(a.alignedAllocate(42, 16) is null); - assert(a.alignedAllocate(0, 16) is null); - assert((() pure nothrow @safe @nogc => a.expand(b, 0))()); - assert(b.length == 100); - assert((() pure nothrow @safe @nogc => a.expand(b, 28))()); - assert(b.length == 128); - assert((() pure nothrow @safe @nogc => !a.expand(b, 1))()); -} - -@system unittest -{ - import std.experimental.allocator.building_blocks.bitmapped_block : BitmappedBlock; - import std.experimental.allocator.gc_allocator : GCAllocator; - - Bucketizer!(BitmappedBlock!(64, 8, GCAllocator), 1, 512, 64) a; - foreach (ref bucket; a.buckets) - { - bucket = BitmappedBlock!(64, 8, GCAllocator)(new ubyte[1024]); - } - - auto b = a.alignedAllocate(1, 4); - assert(b.length == 1); - // Make reallocate use extend - assert(a.alignedReallocate(b, 11, 4)); - assert(b.length == 11); - // Make reallocate use use realloc because of alignment change - assert(a.alignedReallocate(b, 21, 16)); - assert(b.length == 21); - // Make reallocate use extend - assert(a.alignedReallocate(b, 22, 16)); - assert(b.length == 22); - // Move cross buckets - assert(a.alignedReallocate(b, 101, 16)); - assert(b.length == 101); - // Free through realloc - assert(a.alignedReallocate(b, 0, 16)); - assert(b is null); -} diff --git a/phobos/std/experimental/allocator/building_blocks/fallback_allocator.d b/phobos/std/experimental/allocator/building_blocks/fallback_allocator.d deleted file mode 100644 index 3990418..0000000 --- a/phobos/std/experimental/allocator/building_blocks/fallback_allocator.d +++ /dev/null @@ -1,520 +0,0 @@ -// Written in the D programming language. -/** -Source: $(PHOBOSSRC std/experimental/allocator/building_blocks/fallback_allocator.d) -*/ -module std.experimental.allocator.building_blocks.fallback_allocator; - -import std.experimental.allocator.common; - -/** -`FallbackAllocator` is the allocator equivalent of an "or" operator in -algebra. An allocation request is first attempted with the `Primary` -allocator. If that returns `null`, the request is forwarded to the $(D -Fallback) allocator. All other requests are dispatched appropriately to one of -the two allocators. - -In order to work, `FallbackAllocator` requires that `Primary` defines the -`owns` method. This is needed in order to decide which allocator was -responsible for a given allocation. - -`FallbackAllocator` is useful for fast, special-purpose allocators backed up -by general-purpose allocators. The example below features a stack region backed -up by the `GCAllocator`. -*/ -struct FallbackAllocator(Primary, Fallback) -{ - import std.algorithm.comparison : min; - import std.traits : hasMember; - import std.typecons : Ternary; - - // Need both allocators to be stateless - // This is to avoid using default initialized stateful allocators - static if (!stateSize!Primary && !stateSize!Fallback) - version (StdUnittest) - @system unittest - { - testAllocator!(() => FallbackAllocator()); - } - - /// The primary allocator. - static if (stateSize!Primary) Primary primary; - else alias primary = Primary.instance; - - /// The fallback allocator. - static if (stateSize!Fallback) Fallback fallback; - else alias fallback = Fallback.instance; - - /** - If both `Primary` and `Fallback` are stateless, `FallbackAllocator` - defines a static instance called `instance`. - */ - static if (!stateSize!Primary && !stateSize!Fallback) - { - static FallbackAllocator instance; - } - - /** - The alignment offered is the minimum of the two allocators' alignment. - */ - enum uint alignment = min(Primary.alignment, Fallback.alignment); - - /** - Allocates memory trying the primary allocator first. If it returns $(D - null), the fallback allocator is tried. - */ - void[] allocate(size_t s) - { - auto result = primary.allocate(s); - return result.length == s ? result : fallback.allocate(s); - } - - static if (hasMember!(Primary, "allocateZeroed") - || (hasMember!(Fallback, "allocateZeroed"))) - package(std) void[] allocateZeroed()(size_t s) - { - // Try to allocate with primary. - static if (hasMember!(Primary, "allocateZeroed")) - { - void[] result = primary.allocateZeroed(s); - if (result.length == s) return result; - } - else - { - void[] result = primary.allocate(s); - if (result.length == s) - { - (() @trusted => (cast(ubyte[]) result)[] = 0)(); - return result; - } - } - // Allocate with fallback. - static if (hasMember!(Fallback, "allocateZeroed")) - { - return fallback.allocateZeroed(s); - } - else - { - result = fallback.allocate(s); - (() @trusted => (cast(ubyte[]) result)[] = 0)(); // OK even if result is null. - return result; - } - } - - /** - `FallbackAllocator` offers `alignedAllocate` iff at least one of the - allocators also offers it. It attempts to allocate using either or both. - */ - static if (hasMember!(Primary, "alignedAllocate") - || hasMember!(Fallback, "alignedAllocate")) - void[] alignedAllocate(size_t s, uint a) - { - static if (hasMember!(Primary, "alignedAllocate")) - {{ - auto result = primary.alignedAllocate(s, a); - if (result.length == s) return result; - }} - static if (hasMember!(Fallback, "alignedAllocate")) - {{ - auto result = fallback.alignedAllocate(s, a); - if (result.length == s) return result; - }} - return null; - } - - /** - - `expand` is defined if and only if at least one of the allocators - defines `expand`. It works as follows. If `primary.owns(b)`, then the - request is forwarded to `primary.expand` if it is defined, or fails - (returning `false`) otherwise. If `primary` does not own `b`, then - the request is forwarded to `fallback.expand` if it is defined, or fails - (returning `false`) otherwise. - - */ - static if (hasMember!(Primary, "owns") - && (hasMember!(Primary, "expand") || hasMember!(Fallback, "expand"))) - bool expand(ref void[] b, size_t delta) - { - if (!delta) return true; - if (!b.ptr) return false; - if (primary.owns(b) == Ternary.yes) - { - static if (hasMember!(Primary, "expand")) - return primary.expand(b, delta); - else - return false; - } - static if (hasMember!(Fallback, "expand")) - return fallback.expand(b, delta); - else - return false; - } - - /** - - `reallocate` works as follows. If `primary.owns(b)`, then $(D - primary.reallocate(b, newSize)) is attempted. If it fails, an attempt is - made to move the allocation from `primary` to `fallback`. - - If `primary` does not own `b`, then $(D fallback.reallocate(b, - newSize)) is attempted. If that fails, an attempt is made to move the - allocation from `fallback` to `primary`. - - */ - static if (hasMember!(Primary, "owns")) - bool reallocate(ref void[] b, size_t newSize) - { - bool crossAllocatorMove(From, To)(ref From from, ref To to) - { - auto b1 = to.allocate(newSize); - if (b1.length != newSize) return false; - if (b.length < newSize) b1[0 .. b.length] = b[]; - else b1[] = b[0 .. newSize]; - static if (hasMember!(From, "deallocate")) - from.deallocate(b); - b = b1; - return true; - } - - if (b is null || primary.owns(b) == Ternary.yes) - { - return primary.reallocate(b, newSize) - // Move from primary to fallback - || crossAllocatorMove(primary, fallback); - } - return fallback.reallocate(b, newSize) - // Interesting. Move from fallback to primary. - || crossAllocatorMove(fallback, primary); - } - - static if (hasMember!(Primary, "owns") - && (hasMember!(Primary, "alignedAllocate") - || hasMember!(Fallback, "alignedAllocate"))) - bool alignedReallocate(ref void[] b, size_t newSize, uint a) - { - bool crossAllocatorMove(From, To)(ref From from, ref To to) - { - static if (!hasMember!(To, "alignedAllocate")) - { - return false; - } - else - { - auto b1 = to.alignedAllocate(newSize, a); - if (b1.length != newSize) return false; - if (b.length < newSize) b1[0 .. b.length] = b[]; - else b1[] = b[0 .. newSize]; - static if (hasMember!(From, "deallocate")) - from.deallocate(b); - b = b1; - return true; - } - } - - static if (hasMember!(Primary, "alignedAllocate")) - { - if (b is null || primary.owns(b) == Ternary.yes) - { - return primary.alignedReallocate(b, newSize, a) - || crossAllocatorMove(primary, fallback); - } - } - static if (hasMember!(Fallback, "alignedAllocate")) - { - return fallback.alignedReallocate(b, newSize, a) - || crossAllocatorMove(fallback, primary); - } - else - { - return false; - } - } - - /** - `owns` is defined if and only if both allocators define `owns`. - Returns $(D primary.owns(b) | fallback.owns(b)). - */ - static if (hasMember!(Primary, "owns") && hasMember!(Fallback, "owns")) - Ternary owns(void[] b) - { - return primary.owns(b) | fallback.owns(b); - } - - /** - `resolveInternalPointer` is defined if and only if both allocators - define it. - */ - static if (hasMember!(Primary, "resolveInternalPointer") - && hasMember!(Fallback, "resolveInternalPointer")) - Ternary resolveInternalPointer(const void* p, ref void[] result) - { - Ternary r = primary.resolveInternalPointer(p, result); - return r == Ternary.no ? fallback.resolveInternalPointer(p, result) : r; - } - - /** - `deallocate` is defined if and only if at least one of the allocators - define `deallocate`. It works as follows. If `primary.owns(b)`, - then the request is forwarded to `primary.deallocate` if it is defined, - or is a no-op otherwise. If `primary` does not own `b`, then the - request is forwarded to `fallback.deallocate` if it is defined, or is a - no-op otherwise. - */ - static if (hasMember!(Primary, "owns") && - (hasMember!(Primary, "deallocate") - || hasMember!(Fallback, "deallocate"))) - bool deallocate(void[] b) - { - if (primary.owns(b) == Ternary.yes) - { - static if (hasMember!(Primary, "deallocate")) - return primary.deallocate(b); - else - return false; - } - else - { - static if (hasMember!(Fallback, "deallocate")) - return fallback.deallocate(b); - else - return false; - } - } - - /** - `empty` is defined if both allocators also define it. - - Returns: $(D primary.empty & fallback.empty) - */ - static if (hasMember!(Primary, "empty") - && hasMember!(Fallback, "empty")) - Ternary empty() - { - return primary.empty & fallback.empty; - } -} - -@system unittest -{ - import std.conv : text; - import std.experimental.allocator.building_blocks.region : InSituRegion; - import std.experimental.allocator.gc_allocator : GCAllocator; - import std.typecons : Ternary; - FallbackAllocator!(InSituRegion!16_384, GCAllocator) a; - // This allocation uses the stack - auto b1 = a.allocate(1024); - assert(b1.length == 1024, text(b1.length)); - assert((() pure nothrow @safe @nogc => a.primary.owns(b1))() == Ternary.yes); - assert((() nothrow => a.reallocate(b1, 2048))()); - assert(b1.length == 2048, text(b1.length)); - assert((() pure nothrow @safe @nogc => a.primary.owns(b1))() == Ternary.yes); - // This large allocation will go to the GCAllocator - auto b2 = a.allocate(1024 * 1024); - assert((() pure nothrow @safe @nogc => a.primary.owns(b2))() == Ternary.no); - // Ensure deallocate inherits from parent allocators - () nothrow @nogc { a.deallocate(b1); }(); - () nothrow @nogc { a.deallocate(b2); }(); -} - -@system unittest -{ - import std.experimental.allocator.building_blocks.bitmapped_block : BitmappedBlockWithInternalPointers; - import std.typecons : Ternary; - - alias A = - FallbackAllocator!( - BitmappedBlockWithInternalPointers!(4096), - BitmappedBlockWithInternalPointers!(4096) - ); - - A a = A( - BitmappedBlockWithInternalPointers!(4096)(new ubyte[4096 * 1024]), - BitmappedBlockWithInternalPointers!(4096)(new ubyte[4096 * 1024]) - ); - - assert((() nothrow @safe @nogc => a.empty)() == Ternary.yes); - auto b = a.allocate(201); - assert(b.length == 201); - assert(a.reallocate(b, 202)); - assert(b.length == 202); - assert((() nothrow @safe @nogc => a.empty)() == Ternary.no); -} - -@system unittest -{ - import std.experimental.allocator.building_blocks.region : BorrowedRegion; - import std.typecons : Ternary; - - auto a = FallbackAllocator!(BorrowedRegion!(), BorrowedRegion!())( - BorrowedRegion!()(new ubyte[4096 * 1024]), - BorrowedRegion!()(new ubyte[4096 * 1024])); - - auto b = a.alignedAllocate(42, 8); - assert(b.length == 42); - assert((() nothrow @nogc => a.alignedReallocate(b, 100, 8))()); - assert(b.length == 100); -} - -version (StdUnittest) -@system unittest -{ - import std.experimental.allocator.building_blocks.bitmapped_block : BitmappedBlockWithInternalPointers; - import std.typecons : Ternary; - - alias A = - FallbackAllocator!( - BitmappedBlockWithInternalPointers!(4096), - BitmappedBlockWithInternalPointers!(4096) - ); - - // Run testAllocator here since both allocators stateful - testAllocator!( - () => A( - BitmappedBlockWithInternalPointers!(4096)(new ubyte[4096 * 1024]), - BitmappedBlockWithInternalPointers!(4096)(new ubyte[4096 * 1024]) - ) - ); -} - -@system unittest -{ - import std.experimental.allocator.mallocator : Mallocator; - import std.typecons : Ternary; - - alias a = FallbackAllocator!(Mallocator, Mallocator).instance; - - auto b = a.allocate(42); - assert(b.length == 42); - assert((() nothrow @nogc => a.reallocate(b, 100))()); - assert(b.length == 100); -} - -/* -Forwards an argument from one function to another -*/ -private auto ref forward(alias arg)() -{ - static if (__traits(isRef, arg)) - { - return arg; - } - else - { - import std.algorithm.mutation : move; - return move(arg); - } -} - -@safe unittest -{ - void fun(T)(auto ref T, string) { /* ... */ } - void gun(T...)(auto ref T args) - { - fun(forward!(args[0]), forward!(args[1])); - } - gun(42, "hello"); - int x; - gun(x, "hello"); -} - -@safe unittest -{ - static void checkByRef(T)(auto ref T value) - { - static assert(__traits(isRef, value)); - } - - static void checkByVal(T)(auto ref T value) - { - static assert(!__traits(isRef, value)); - } - - static void test1(ref int a) { checkByRef(forward!a); } - static void test2(int a) { checkByVal(forward!a); } - static void test3() { int a; checkByVal(forward!a); } -} - -/** -Convenience function that uses type deduction to return the appropriate -`FallbackAllocator` instance. To initialize with allocators that don't have -state, use their `it` static member. -*/ -FallbackAllocator!(Primary, Fallback) -fallbackAllocator(Primary, Fallback)(auto ref Primary p, auto ref Fallback f) -{ - alias R = FallbackAllocator!(Primary, Fallback); - - static if (stateSize!Primary) - static if (stateSize!Fallback) - return R(forward!p, forward!f); - else - return R(forward!p); - else - static if (stateSize!Fallback) - return R(forward!f); - else - return R(); -} - -/// -@system unittest -{ - import std.experimental.allocator.building_blocks.region : Region; - import std.experimental.allocator.gc_allocator : GCAllocator; - import std.typecons : Ternary; - auto a = fallbackAllocator(Region!GCAllocator(1024), GCAllocator.instance); - auto b1 = a.allocate(1020); - assert(b1.length == 1020); - assert(a.primary.owns(b1) == Ternary.yes); - auto b2 = a.allocate(10); - assert(b2.length == 10); - assert(a.primary.owns(b2) == Ternary.no); -} - -version (StdUnittest) -@system unittest -{ - import std.experimental.allocator.building_blocks.region : Region; - import std.experimental.allocator.gc_allocator : GCAllocator; - testAllocator!(() => fallbackAllocator(Region!GCAllocator(1024), GCAllocator.instance)); -} - -// Ensure `owns` inherits function attributes -@system unittest -{ - import std.experimental.allocator.building_blocks.region : InSituRegion; - import std.typecons : Ternary; - - FallbackAllocator!(InSituRegion!16_384, InSituRegion!16_384) a; - auto buff = a.allocate(42); - assert((() pure nothrow @safe @nogc => a.owns(buff))() == Ternary.yes); -} - -@system unittest -{ - import std.experimental.allocator.gc_allocator : GCAllocator; - import std.typecons : Ternary; - - auto a = fallbackAllocator(GCAllocator.instance, GCAllocator.instance); - auto b = a.allocate(1020); - assert(b.length == 1020); - - void[] p; - assert((() nothrow @safe @nogc => a.resolveInternalPointer(null, p))() == Ternary.no); - assert((() nothrow @safe @nogc => a.resolveInternalPointer(&b[0], p))() == Ternary.yes); -} - -@system unittest -{ - import std.experimental.allocator.building_blocks.region : BorrowedRegion; - import std.typecons : Ternary; - - alias A = FallbackAllocator!(BorrowedRegion!(), BorrowedRegion!()); - auto a = A(BorrowedRegion!()(new ubyte[16_384]), BorrowedRegion!()(new ubyte[16_384])); - - auto b = a.allocate(42); - assert(b.length == 42); - assert((() pure nothrow @safe @nogc => a.owns(b))() == Ternary.yes); - assert((() nothrow @safe @nogc => a.expand(b, 58))()); - assert(b.length == 100); -} diff --git a/phobos/std/experimental/allocator/building_blocks/free_list.d b/phobos/std/experimental/allocator/building_blocks/free_list.d deleted file mode 100644 index d2b3209..0000000 --- a/phobos/std/experimental/allocator/building_blocks/free_list.d +++ /dev/null @@ -1,1320 +0,0 @@ -// Written in the D programming language. -/** -Source: $(PHOBOSSRC std/experimental/allocator/building_blocks/free_list.d) -*/ -module std.experimental.allocator.building_blocks.free_list; - -import std.experimental.allocator.common; -import std.typecons : Flag, Yes, No; - -/** - -$(HTTP en.wikipedia.org/wiki/Free_list, Free list allocator), stackable on top of -another allocator. Allocation requests between `min` and `max` bytes are -rounded up to `max` and served from a singly-linked list of buffers -deallocated in the past. All other allocations are directed to $(D -ParentAllocator). Due to the simplicity of free list management, allocations -from the free list are fast. If `adaptive` is set to `Yes.adaptive`, -the free list gradually reduces its size if allocations tend to use the parent -allocator much more than the lists' available nodes. - -One instantiation is of particular interest: $(D FreeList!(0, unbounded)) puts -every deallocation in the freelist, and subsequently serves any allocation from -the freelist (if not empty). There is no checking of size matching, which would -be incorrect for a freestanding allocator but is both correct and fast when an -owning allocator on top of the free list allocator (such as `Segregator`) is -already in charge of handling size checking. - -The following methods are defined if `ParentAllocator` defines them, and -forward to it: `expand`, `owns`, `reallocate`. - -*/ -struct FreeList(ParentAllocator, - size_t minSize, size_t maxSize = minSize, - Flag!"adaptive" adaptive = No.adaptive) -{ - import std.conv : text; - import std.exception : enforce; - import std.traits : hasMember; - import std.typecons : Ternary; - import std.experimental.allocator.building_blocks.null_allocator : NullAllocator; - - static assert(minSize != unbounded, "Use minSize = 0 for no low bound."); - static assert(maxSize >= (void*).sizeof, - "Maximum size must accommodate a pointer."); - - private enum unchecked = minSize == 0 && maxSize == unbounded; - - private enum hasTolerance = !unchecked && (minSize != maxSize - || maxSize == chooseAtRuntime); - - static if (minSize == chooseAtRuntime) - { - /** - Returns the smallest allocation size eligible for allocation from the - freelist. (If $(D minSize != chooseAtRuntime), this is simply an alias - for `minSize`.) - */ - @property size_t min() const - { - assert(_min != chooseAtRuntime); - return _min; - } - /** - If `FreeList` has been instantiated with $(D minSize == - chooseAtRuntime), then the `min` property is writable. Setting it - must precede any allocation. - - Params: - low = new value for `min` - - Precondition: $(D low <= max), or $(D maxSize == chooseAtRuntime) and - `max` has not yet been initialized. Also, no allocation has been - yet done with this allocator. - - Postcondition: $(D min == low) - */ - @property void min(size_t low) - { - assert(low <= max || max == chooseAtRuntime); - minimize; - _min = low; - } - } - else - { - alias min = minSize; - } - - static if (maxSize == chooseAtRuntime) - { - /** - Returns the largest allocation size eligible for allocation from the - freelist. (If $(D maxSize != chooseAtRuntime), this is simply an alias - for `maxSize`.) All allocation requests for sizes greater than or - equal to `min` and less than or equal to `max` are rounded to $(D - max) and forwarded to the parent allocator. When the block fitting the - same constraint gets deallocated, it is put in the freelist with the - allocated size assumed to be `max`. - */ - @property size_t max() const { return _max; } - - /** - If `FreeList` has been instantiated with $(D maxSize == - chooseAtRuntime), then the `max` property is writable. Setting it - must precede any allocation. - - Params: - high = new value for `max` - - Precondition: $(D high >= min), or $(D minSize == chooseAtRuntime) and - `min` has not yet been initialized. Also $(D high >= (void*).sizeof). Also, no allocation has been yet done with this allocator. - - Postcondition: $(D max == high) - */ - @property void max(size_t high) - { - assert((high >= min || min == chooseAtRuntime) - && high >= (void*).sizeof); - minimize; - _max = high; - } - - @system unittest - { - import std.experimental.allocator.common : chooseAtRuntime; - import std.experimental.allocator.mallocator : Mallocator; - - FreeList!(Mallocator, chooseAtRuntime, chooseAtRuntime) a; - a.min = 64; - a.max = 128; - assert(a.min == 64); - assert(a.max == 128); - } - } - else - { - alias max = maxSize; - } - - private bool tooSmall(size_t n) const - { - static if (minSize == 0) return false; - else return n < min; - } - - private bool tooLarge(size_t n) const - { - static if (maxSize == unbounded) return false; - else return n > max; - } - - private bool freeListEligible(size_t n) const - { - static if (unchecked) - { - return true; - } - else - { - static if (minSize == 0) - { - if (!n) return false; - } - static if (minSize == maxSize && minSize != chooseAtRuntime) - return n == maxSize; - else - return !tooSmall(n) && !tooLarge(n); - } - } - - static if (!unchecked) - private void[] blockFor(Node* p) - { - assert(p); - return (cast(void*) p)[0 .. max]; - } - - // statistics - static if (adaptive == Yes.adaptive) - { - private enum double windowLength = 1000.0; - private enum double tooFewMisses = 0.01; - private double probMiss = 1.0; // start with a high miss probability - private uint accumSamples, accumMisses; - - void updateStats() - { - assert(accumSamples >= accumMisses); - /* - Given that for the past windowLength samples we saw misses with - estimated probability probMiss, and assuming the new sample wasMiss or - not, what's the new estimated probMiss? - */ - probMiss = (probMiss * windowLength + accumMisses) - / (windowLength + accumSamples); - assert(probMiss <= 1.0); - accumSamples = 0; - accumMisses = 0; - // If probability to miss is under x%, yank one off the freelist - static if (!unchecked) - { - if (probMiss < tooFewMisses && _root) - { - auto b = blockFor(_root); - _root = _root.next; - parent.deallocate(b); - } - } - } - } - - private struct Node { Node* next; } - static assert(ParentAllocator.alignment >= Node.alignof); - - // state - /** - The parent allocator. Depending on whether `ParentAllocator` holds state - or not, this is a member variable or an alias for - `ParentAllocator.instance`. - */ - static if (stateSize!ParentAllocator) ParentAllocator parent; - else alias parent = ParentAllocator.instance; - private Node* root; - static if (minSize == chooseAtRuntime) private size_t _min = chooseAtRuntime; - static if (maxSize == chooseAtRuntime) private size_t _max = chooseAtRuntime; - - /** - Alignment offered. - */ - alias alignment = ParentAllocator.alignment; - - /** - If $(D maxSize == unbounded), returns `parent.goodAllocSize(bytes)`. - Otherwise, returns `max` for sizes in the interval $(D [min, max]), and - `parent.goodAllocSize(bytes)` otherwise. - - Precondition: - If set at runtime, `min` and/or `max` must be initialized - appropriately. - - Postcondition: - $(D result >= bytes) - */ - size_t goodAllocSize(size_t bytes) - { - assert(minSize != chooseAtRuntime && maxSize != chooseAtRuntime); - static if (maxSize != unbounded) - { - if (freeListEligible(bytes)) - { - assert(parent.goodAllocSize(max) == max, - text("Wrongly configured freelist: maximum should be ", - parent.goodAllocSize(max), " instead of ", max)); - return max; - } - } - return parent.goodAllocSize(bytes); - } - - private void[] allocateEligible(string fillMode)(size_t bytes) - if (fillMode == "void" || fillMode == "zero") - { - enum bool isFillZero = fillMode == "zero"; - assert(bytes); - if (root) - { - // faster - auto result = (cast(ubyte*) root)[0 .. bytes]; - root = root.next; - static if (isFillZero) result[0 .. bytes] = 0; - return result; - } - // slower - static if (hasTolerance) - { - immutable toAllocate = max; - } - else - { - alias toAllocate = bytes; - } - assert(toAllocate == max || max == unbounded); - static if (isFillZero) - auto result = parent.allocateZeroed(toAllocate); - else - auto result = parent.allocate(toAllocate); - static if (hasTolerance) - { - if (result) result = result.ptr[0 .. bytes]; - } - static if (adaptive == Yes.adaptive) - { - ++accumMisses; - updateStats; - } - return result; - } - - /** - Allocates memory either off of the free list or from the parent allocator. - If `n` is within $(D [min, max]) or if the free list is unchecked - ($(D minSize == 0 && maxSize == size_t.max)), then the free list is - consulted first. If not empty (hit), the block at the front of the free - list is removed from the list and returned. Otherwise (miss), a new block - of `max` bytes is allocated, truncated to `n` bytes, and returned. - - Params: - n = number of bytes to allocate - - Returns: - The allocated block, or `null`. - - Precondition: - If set at runtime, `min` and/or `max` must be initialized - appropriately. - - Postcondition: $(D result.length == bytes || result is null) - */ - void[] allocate(size_t n) - { - static if (adaptive == Yes.adaptive) ++accumSamples; - assert(n < size_t.max / 2); - // fast path - if (freeListEligible(n)) - { - return allocateEligible!"void"(n); - } - // slower - static if (adaptive == Yes.adaptive) - { - updateStats; - } - return parent.allocate(n); - } - - static if (hasMember!(ParentAllocator, "allocateZeroed")) - package(std) void[] allocateZeroed()(size_t n) - { - static if (adaptive == Yes.adaptive) ++accumSamples; - assert(n < size_t.max / 2); - // fast path - if (freeListEligible(n)) - { - return allocateEligible!"zero"(n); - } - // slower - static if (adaptive == Yes.adaptive) - { - updateStats; - } - return parent.allocateZeroed(n); - } - - // Forwarding methods - mixin(forwardToMember("parent", - "expand", "owns", "reallocate")); - - /** - If `block.length` is within $(D [min, max]) or if the free list is - unchecked ($(D minSize == 0 && maxSize == size_t.max)), then inserts the - block at the front of the free list. For all others, forwards to $(D - parent.deallocate) if `Parent.deallocate` is defined. - - Params: - block = Block to deallocate. - - Precondition: - If set at runtime, `min` and/or `max` must be initialized - appropriately. The block must have been allocated with this - freelist, and no dynamic changing of `min` or `max` is allowed to - occur between allocation and deallocation. - */ - bool deallocate(void[] block) - { - if (freeListEligible(block.length)) - { - if (min == 0) - { - // In this case a null pointer might have made it this far. - if (block is null) return true; - } - auto t = root; - root = cast(Node*) block.ptr; - root.next = t; - return true; - } - static if (hasMember!(ParentAllocator, "deallocate")) - return parent.deallocate(block); - else - return false; - } - - /** - Defined only if `ParentAllocator` defines `deallocateAll`. If so, - forwards to it and resets the freelist. - */ - static if (hasMember!(ParentAllocator, "deallocateAll")) - bool deallocateAll() - { - root = null; - return parent.deallocateAll(); - } - - /** - Nonstandard function that minimizes the memory usage of the freelist by - freeing each element in turn. Defined only if `ParentAllocator` defines - `deallocate`. $(D FreeList!(0, unbounded)) does not have this function. - */ - static if (hasMember!(ParentAllocator, "deallocate") && !unchecked) - void minimize() - { - while (root) - { - auto nuke = blockFor(root); - root = root.next; - parent.deallocate(nuke); - } - } - - /** - If `ParentAllocator` defines `deallocate`, the list frees all nodes - on destruction. $(D FreeList!(0, unbounded)) does not deallocate the memory - on destruction. - */ - static if (!is(ParentAllocator == NullAllocator) && - hasMember!(ParentAllocator, "deallocate") && !unchecked) - ~this() - { - minimize(); - } -} - -@system unittest -{ - import std.experimental.allocator.mallocator : Mallocator; - import std.experimental.allocator.building_blocks.stats_collector - : StatsCollector, Options; - - struct StatsCollectorWrapper { - ~this() - { - // buf2 should still be around and buf1 deallocated - assert(parent.numDeallocate == 1); - assert(parent.bytesUsed == 16); - } - static StatsCollector!(Mallocator, Options.all) parent; - alias parent this; - } - - FreeList!(StatsCollectorWrapper, 16, 16) fl; - auto buf1 = fl.allocate(16); - auto buf2 = fl.allocate(16); - assert(fl.parent.bytesUsed == 32); - - // After this, the list has 1 node, so no actual deallocation by Mallocator - fl.deallocate(buf1); - assert(fl.parent.bytesUsed == 32); - - // Destruction should only deallocate the node - destroy(fl); -} - -@system unittest -{ - import std.experimental.allocator.gc_allocator : GCAllocator; - FreeList!(GCAllocator, 0, 8) fl; - assert(fl.root is null); - auto b1 = fl.allocate(7); - fl.allocate(8); - assert(fl.root is null); - // Ensure deallocate inherits from parent - () nothrow @nogc { fl.deallocate(b1); }(); - assert(fl.root !is null); - fl.allocate(8); - assert(fl.root is null); -} - -@system unittest -{ - import std.experimental.allocator.gc_allocator : GCAllocator; - FreeList!(GCAllocator, 0, 16) fl; - // Not @nogc because of std.conv.text - assert((() nothrow @safe /*@nogc*/ => fl.goodAllocSize(1))() == 16); -} - -// Test that deallocateAll infers from parent -@system unittest -{ - import std.experimental.allocator.building_blocks.region : BorrowedRegion; - - auto fl = FreeList!(BorrowedRegion!(), 0, 16)(BorrowedRegion!()(new ubyte[1024 * 64])); - auto b = fl.allocate(42); - assert(b.length == 42); - assert((() pure nothrow @safe @nogc => fl.expand(b, 48))()); - assert(b.length == 90); - assert((() nothrow @nogc => fl.reallocate(b, 100))()); - assert(b.length == 100); - assert((() nothrow @nogc => fl.deallocateAll())()); -} - -/** -Free list built on top of exactly one contiguous block of memory. The block is -assumed to have been allocated with `ParentAllocator`, and is released in -`ContiguousFreeList`'s destructor (unless `ParentAllocator` is $(D -NullAllocator)). - -`ContiguousFreeList` has most advantages of `FreeList` but fewer -disadvantages. It has better cache locality because items are closer to one -another. It imposes less fragmentation on its parent allocator. - -The disadvantages of `ContiguousFreeList` over `FreeList` are its pay -upfront model (as opposed to `FreeList`'s pay-as-you-go approach), and a -hard limit on the number of nodes in the list. Thus, a large number of long- -lived objects may occupy the entire block, making it unavailable for serving -allocations from the free list. However, an absolute cap on the free list size -may be beneficial. - -The options $(D minSize == unbounded) and $(D maxSize == unbounded) are not -available for `ContiguousFreeList`. -*/ -struct ContiguousFreeList(ParentAllocator, - size_t minSize, size_t maxSize = minSize) -{ - import std.experimental.allocator.building_blocks.null_allocator - : NullAllocator; - import std.experimental.allocator.building_blocks.stats_collector - : StatsCollector, Options; - import std.traits : hasMember; - import std.typecons : Ternary; - - alias Impl = FreeList!(NullAllocator, minSize, maxSize); - enum unchecked = minSize == 0 && maxSize == unbounded; - alias Node = Impl.Node; - - alias SParent = StatsCollector!(ParentAllocator, Options.bytesUsed); - - // state - /** - The parent allocator. Depending on whether `ParentAllocator` holds state - or not, this is a member variable or an alias for - `ParentAllocator.instance`. - */ - SParent parent; - FreeList!(NullAllocator, minSize, maxSize) fl; - void[] support; - size_t allocated; - - /// Alignment offered. - enum uint alignment = (void*).alignof; - - private void initialize(ubyte[] buffer, size_t itemSize = fl.max) - { - assert(itemSize != unbounded && itemSize != chooseAtRuntime); - assert(buffer.ptr.alignedAt(alignment)); - immutable available = buffer.length / itemSize; - if (available == 0) return; - support = buffer; - fl.root = cast(Node*) buffer.ptr; - auto past = cast(Node*) (buffer.ptr + available * itemSize); - for (auto n = fl.root; ; ) - { - auto next = cast(Node*) (cast(ubyte*) n + itemSize); - if (next == past) - { - n.next = null; - break; - } - assert(next < past); - assert(n < next); - n.next = next; - n = next; - } - } - - /** - Constructors setting up the memory structured as a free list. - - Params: - buffer = Buffer to structure as a free list. If `ParentAllocator` is not - `NullAllocator`, the buffer is assumed to be allocated by `parent` - and will be freed in the destructor. - parent = Parent allocator. For construction from stateless allocators, use - their `instance` static member. - bytes = Bytes (not items) to be allocated for the free list. Memory will be - allocated during construction and deallocated in the destructor. - max = Maximum size eligible for freelisting. Construction with this - parameter is defined only if $(D maxSize == chooseAtRuntime) or $(D maxSize - == unbounded). - min = Minimum size eligible for freelisting. Construction with this - parameter is defined only if $(D minSize == chooseAtRuntime). If this - condition is met and no `min` parameter is present, `min` is - initialized with `max`. - */ - static if (!stateSize!ParentAllocator) - this(ubyte[] buffer) - { - initialize(buffer); - } - - /// ditto - static if (stateSize!ParentAllocator) - this(ParentAllocator parent, ubyte[] buffer) - { - initialize(buffer); - this.parent = SParent(parent); - } - - /// ditto - static if (!stateSize!ParentAllocator) - this(size_t bytes) - { - initialize(cast(ubyte[])(ParentAllocator.instance.allocate(bytes))); - } - - /// ditto - static if (stateSize!ParentAllocator) - this(ParentAllocator parent, size_t bytes) - { - initialize(cast(ubyte[])(parent.allocate(bytes))); - this.parent = SParent(parent); - } - - /// ditto - static if (!stateSize!ParentAllocator - && (maxSize == chooseAtRuntime || maxSize == unbounded)) - this(size_t bytes, size_t max) - { - static if (maxSize == chooseAtRuntime) fl.max = max; - static if (minSize == chooseAtRuntime) fl.min = max; - initialize(cast(ubyte[])(parent.allocate(bytes)), max); - } - - /// ditto - static if (stateSize!ParentAllocator - && (maxSize == chooseAtRuntime || maxSize == unbounded)) - this(ParentAllocator parent, size_t bytes, size_t max) - { - static if (maxSize == chooseAtRuntime) fl.max = max; - static if (minSize == chooseAtRuntime) fl.min = max; - initialize(cast(ubyte[])(parent.allocate(bytes)), max); - this.parent = SParent(parent); - } - - /// ditto - static if (!stateSize!ParentAllocator - && (maxSize == chooseAtRuntime || maxSize == unbounded) - && minSize == chooseAtRuntime) - this(size_t bytes, size_t min, size_t max) - { - static if (maxSize == chooseAtRuntime) fl.max = max; - fl.min = min; - initialize(cast(ubyte[])(parent.allocate(bytes)), max); - static if (stateSize!ParentAllocator) - this.parent = SParent(parent); - } - - /// ditto - static if (stateSize!ParentAllocator - && (maxSize == chooseAtRuntime || maxSize == unbounded) - && minSize == chooseAtRuntime) - this(ParentAllocator parent, size_t bytes, size_t min, size_t max) - { - static if (maxSize == chooseAtRuntime) fl.max = max; - fl.min = min; - initialize(cast(ubyte[])(parent.allocate(bytes)), max); - static if (stateSize!ParentAllocator) - this.parent = SParent(parent); - } - - /** - If `n` is eligible for freelisting, returns `max`. Otherwise, returns - `parent.goodAllocSize(n)`. - - Precondition: - If set at runtime, `min` and/or `max` must be initialized - appropriately. - - Postcondition: - $(D result >= bytes) - */ - size_t goodAllocSize(size_t n) - { - if (fl.freeListEligible(n)) return fl.max; - return parent.goodAllocSize(n); - } - - /** - Allocate `n` bytes of memory. If `n` is eligible for freelist and the - freelist is not empty, pops the memory off the free list. In all other - cases, uses the parent allocator. - */ - void[] allocate(size_t n) - { - auto result = fl.allocate(n); - if (result) - { - // Only case we care about: eligible sizes allocated from us - ++allocated; - return result; - } - // All others, allocate from parent - return parent.allocate(n); - } - - /** - Defined if `ParentAllocator` defines it. Checks whether the block - belongs to this allocator. - */ - static if (hasMember!(SParent, "owns") || unchecked) - // Ternary owns(const void[] b) const ? - Ternary owns(void[] b) - { - if ((() @trusted => support && b - && (&support[0] <= &b[0]) - && (&b[0] < &support[0] + support.length) - )()) - return Ternary.yes; - static if (unchecked) - return Ternary.no; - else - return parent.owns(b); - } - - /** - Deallocates `b`. If it's of eligible size, it's put on the free list. - Otherwise, it's returned to `parent`. - - Precondition: `b` has been allocated with this allocator, or is $(D - null). - */ - bool deallocate(void[] b) - { - if (support.ptr <= b.ptr && b.ptr < support.ptr + support.length) - { - // we own this guy - assert(fl.freeListEligible(b.length)); - assert(allocated); - --allocated; - // Put manually in the freelist - auto t = fl.root; - fl.root = cast(Node*) b.ptr; - fl.root.next = t; - return true; - } - return parent.deallocate(b); - } - - /** - Deallocates everything from the parent. - */ - static if (hasMember!(ParentAllocator, "deallocateAll") - && stateSize!ParentAllocator) - bool deallocateAll() - { - bool result = fl.deallocateAll && parent.deallocateAll; - allocated = 0; - return result; - } - - /** - Returns `Ternary.yes` if no memory is currently allocated with this - allocator, `Ternary.no` otherwise. This method never returns - `Ternary.unknown`. - */ - Ternary empty() - { - return Ternary(allocated == 0 && parent.bytesUsed == 0); - } -} - -/// -@safe unittest -{ - import std.experimental.allocator.building_blocks.allocator_list - : AllocatorList; - import std.experimental.allocator.gc_allocator : GCAllocator; - - import std.experimental.allocator.common : unbounded; - - alias ScalableFreeList = AllocatorList!((n) => - ContiguousFreeList!(GCAllocator, 0, unbounded)(4096) - ); -} - -@system unittest -{ - import std.experimental.allocator.building_blocks.null_allocator - : NullAllocator; - import std.typecons : Ternary; - alias A = ContiguousFreeList!(NullAllocator, 0, 64); - auto a = A(new ubyte[1024]); - - assert((() nothrow @safe @nogc => a.empty)() == Ternary.yes); - - assert((() pure nothrow @safe @nogc => a.goodAllocSize(15))() == 64); - assert((() pure nothrow @safe @nogc => a.goodAllocSize(65))() - == (() nothrow @safe @nogc => NullAllocator.instance.goodAllocSize(65))()); - - auto b = a.allocate(100); - assert((() nothrow @safe @nogc => a.empty)() == Ternary.yes); - assert(b.length == 0); - // Ensure deallocate inherits from parent - () nothrow @nogc { a.deallocate(b); }(); - b = a.allocate(64); - assert((() nothrow @safe @nogc => a.empty)() == Ternary.no); - assert(b.length == 64); - assert((() nothrow @safe @nogc => a.owns(b))() == Ternary.yes); - assert((() nothrow @safe @nogc => a.owns(null))() == Ternary.no); - () nothrow @nogc { a.deallocate(b); }(); -} - -@system unittest -{ - import std.experimental.allocator.building_blocks.region : Region; - import std.experimental.allocator.gc_allocator : GCAllocator; - import std.typecons : Ternary; - alias A = ContiguousFreeList!(Region!GCAllocator, 0, 64); - auto a = A(Region!GCAllocator(1024 * 4), 1024); - - assert((() nothrow @safe @nogc => a.empty)() == Ternary.yes); - - assert((() pure nothrow @safe @nogc => a.goodAllocSize(15))() == 64); - assert((() pure nothrow @safe @nogc => a.goodAllocSize(65))() - == (() pure nothrow @safe @nogc => a.parent.goodAllocSize(65))()); - - auto b = a.allocate(100); - assert((() nothrow @safe @nogc => a.empty)() == Ternary.no); - assert(a.allocated == 0); - assert(b.length == 100); - // Ensure deallocate inherits from parent - assert((() nothrow @nogc => a.deallocate(b))()); - assert((() nothrow @safe @nogc => a.empty)() == Ternary.yes); - b = a.allocate(64); - assert((() nothrow @safe @nogc => a.empty)() == Ternary.no); - assert(b.length == 64); - assert(a.reallocate(b, 100)); - assert(b.length == 100); - assert((() nothrow @safe @nogc => a.owns(b))() == Ternary.yes); - assert((() nothrow @safe @nogc => a.owns(null))() == Ternary.no); - // Test deallocate infers from parent - assert((() nothrow @nogc => a.deallocate(b))()); - assert((() nothrow @nogc => a.deallocateAll())()); -} - -@system unittest -{ - import std.experimental.allocator.gc_allocator : GCAllocator; - alias A = ContiguousFreeList!(GCAllocator, 64, 64); - auto a = A(1024); - const b = a.allocate(100); - assert(b.length == 100); -} - -/** -FreeList shared across threads. Allocation and deallocation are lock-free. The -parameters have the same semantics as for `FreeList`. - -`expand` is defined to forward to `ParentAllocator.expand` -(it must be also `shared`). -*/ -struct SharedFreeList(ParentAllocator, - size_t minSize, size_t maxSize = minSize, size_t approxMaxNodes = unbounded) -{ - import std.conv : text; - import std.exception : enforce; - import std.traits : hasMember; - - static if (hasMember!(ParentAllocator, "owns")) - { - import std.typecons : Ternary; - } - - static assert(approxMaxNodes, "approxMaxNodes must not be null."); - static assert(minSize != unbounded, "Use minSize = 0 for no low bound."); - static assert(maxSize >= (void*).sizeof, - "Maximum size must accommodate a pointer."); - - import core.atomic : atomicOp, cas; - import core.internal.spinlock : SpinLock; - - private enum unchecked = minSize == 0 && maxSize == unbounded; - - static if (minSize != chooseAtRuntime) - { - alias min = minSize; - } - else - { - private shared size_t _min = chooseAtRuntime; - @property size_t min() const shared - { - assert(_min != chooseAtRuntime); - return _min; - } - @property void min(size_t x) shared - { - enforce(x <= max); - enforce(cas(&_min, chooseAtRuntime, x), - "SharedFreeList.min must be initialized exactly once."); - } - static if (maxSize == chooseAtRuntime) - { - // Both bounds can be set, provide one function for setting both in - // one shot. - void setBounds(size_t low, size_t high) shared - { - enforce(low <= high && high >= (void*).sizeof); - enforce(cas(&_min, chooseAtRuntime, low), - "SharedFreeList.min must be initialized exactly once."); - enforce(cas(&_max, chooseAtRuntime, high), - "SharedFreeList.max must be initialized exactly once."); - } - } - } - - private bool tooSmall(size_t n) const shared - { - static if (minSize == 0) return false; - else static if (minSize == chooseAtRuntime) return n < _min; - else return n < minSize; - } - - static if (maxSize != chooseAtRuntime) - { - alias max = maxSize; - } - else - { - private shared size_t _max = chooseAtRuntime; - @property size_t max() const shared { return _max; } - @property void max(size_t x) shared - { - enforce(x >= min && x >= (void*).sizeof); - enforce(cas(&_max, chooseAtRuntime, x), - "SharedFreeList.max must be initialized exactly once."); - } - } - - private bool tooLarge(size_t n) const shared - { - static if (maxSize == unbounded) return false; - else static if (maxSize == chooseAtRuntime) return n > _max; - else return n > maxSize; - } - - private bool freeListEligible(size_t n) const shared - { - static if (minSize == maxSize && minSize != chooseAtRuntime) - return n == maxSize; - else return !tooSmall(n) && !tooLarge(n); - } - - static if (approxMaxNodes != chooseAtRuntime) - { - alias approxMaxLength = approxMaxNodes; - } - else - { - private shared size_t _approxMaxLength = chooseAtRuntime; - @property size_t approxMaxLength() const shared { return _approxMaxLength; } - @property void approxMaxLength(size_t x) shared { _approxMaxLength = enforce(x); } - } - - static if (approxMaxNodes != unbounded) - { - private shared size_t nodes; - private void incNodes() shared - { - atomicOp!("+=")(nodes, 1); - } - private void decNodes() shared - { - assert(nodes); - atomicOp!("-=")(nodes, 1); - } - private void resetNodes() shared - { - nodes = 0; - } - private bool nodesFull() shared - { - return nodes >= approxMaxLength; - } - } - else - { - private static void incNodes() { } - private static void decNodes() { } - private static void resetNodes() { } - private enum bool nodesFull = false; - } - - version (StdDdoc) - { - /** - Properties for getting (and possibly setting) the bounds. Setting bounds - is allowed only once , and before any allocation takes place. Otherwise, - the primitives have the same semantics as those of `FreeList`. - */ - @property size_t min(); - /// Ditto - @property void min(size_t newMinSize); - /// Ditto - @property size_t max(); - /// Ditto - @property void max(size_t newMaxSize); - /// Ditto - void setBounds(size_t newMin, size_t newMax); - - /** - Properties for getting (and possibly setting) the approximate maximum length of a shared freelist. - */ - @property size_t approxMaxLength() const shared; - /// ditto - @property void approxMaxLength(size_t x) shared; - } - - /** - The parent allocator. Depending on whether `ParentAllocator` holds state - or not, this is a member variable or an alias for - `ParentAllocator.instance`. - */ - static if (stateSize!ParentAllocator) shared ParentAllocator parent; - else alias parent = ParentAllocator.instance; - - mixin(forwardToMember("parent", "expand")); - - private SpinLock lock; - - private struct Node { Node* next; } - static assert(ParentAllocator.alignment >= Node.alignof); - private Node* _root; - - /// Standard primitives. - enum uint alignment = ParentAllocator.alignment; - - /// Ditto - size_t goodAllocSize(size_t bytes) shared - { - if (freeListEligible(bytes)) return maxSize == unbounded ? bytes : max; - return parent.goodAllocSize(bytes); - } - - /// Ditto - static if (hasMember!(ParentAllocator, "owns")) - Ternary owns(const void[] b) shared const - { - return parent.owns(b); - } - - /// Ditto - static if (hasMember!(ParentAllocator, "reallocate")) - bool reallocate(ref void[] b, size_t s) shared - { - return parent.reallocate(b, s); - } - - /// Ditto - void[] allocate(size_t bytes) shared - { - assert(bytes < size_t.max / 2); - if (!freeListEligible(bytes)) return parent.allocate(bytes); - if (maxSize != unbounded) bytes = max; - - // Try to pop off the freelist - lock.lock(); - if (!_root) - { - lock.unlock(); - return allocateFresh(bytes); - } - else - { - auto oldRoot = _root; - _root = _root.next; - decNodes(); - lock.unlock(); - return (cast(ubyte*) oldRoot)[0 .. bytes]; - } - } - - private void[] allocateFresh(const size_t bytes) shared - { - assert(bytes == max || max == unbounded); - return parent.allocate(bytes); - } - - /// Ditto - bool deallocate(void[] b) shared - { - if (!nodesFull && freeListEligible(b.length)) - { - auto newRoot = cast(shared Node*) b.ptr; - lock.lock(); - newRoot.next = _root; - _root = newRoot; - incNodes(); - lock.unlock(); - return true; - } - static if (hasMember!(ParentAllocator, "deallocate")) - return parent.deallocate(b); - else - return false; - } - - /// Ditto - bool deallocateAll() shared - { - bool result = false; - lock.lock(); - scope(exit) lock.unlock(); - static if (hasMember!(ParentAllocator, "deallocateAll")) - { - result = parent.deallocateAll(); - } - else static if (hasMember!(ParentAllocator, "deallocate")) - { - result = true; - for (auto n = _root; n;) - { - auto tmp = n.next; - if (!parent.deallocate((cast(ubyte*) n)[0 .. max])) - result = false; - n = tmp; - } - } - _root = null; - resetNodes(); - return result; - } - - /** - Nonstandard function that minimizes the memory usage of the freelist by - freeing each element in turn. Defined only if `ParentAllocator` defines - `deallocate`. - */ - static if (hasMember!(ParentAllocator, "deallocate") && !unchecked) - void minimize() shared - { - lock.lock(); - scope(exit) lock.unlock(); - - for (auto n = _root; n;) - { - auto tmp = n.next; - parent.deallocate((cast(ubyte*) n)[0 .. max]); - n = tmp; - } - - _root = null; - resetNodes(); - } -} - -/// -@safe unittest -{ - import std.experimental.allocator.common : chooseAtRuntime; - import std.experimental.allocator.mallocator : Mallocator; - - shared SharedFreeList!(Mallocator, chooseAtRuntime, chooseAtRuntime) a; - a.setBounds(64, 128); - assert(a.max == 128); - assert(a.min == 64); -} - -/// -@safe unittest -{ - import std.experimental.allocator.common : chooseAtRuntime; - import std.experimental.allocator.mallocator : Mallocator; - - shared SharedFreeList!(Mallocator, 50, 50, chooseAtRuntime) a; - // Set the maxSize first so setting the minSize doesn't throw - a.approxMaxLength = 128; - assert(a.approxMaxLength == 128); - a.approxMaxLength = 1024; - assert(a.approxMaxLength == 1024); - a.approxMaxLength = 1; - assert(a.approxMaxLength == 1); -} - -@system unittest -{ - import core.thread : ThreadGroup; - import std.algorithm.comparison : equal; - import std.experimental.allocator.mallocator : Mallocator; - import std.range : repeat; - - static shared SharedFreeList!(Mallocator, 64, 128, 10) a; - - assert((() nothrow @safe @nogc => a.goodAllocSize(1))() == platformAlignment); - - auto b = a.allocate(96); - // Ensure deallocate inherits from parent - () nothrow @nogc { a.deallocate(b); }(); - - void fun() - { - auto b = cast(size_t[]) a.allocate(96); - b[] = cast(size_t) &b; - - assert(b.equal(repeat(cast(size_t) &b, b.length))); - () nothrow @nogc { a.deallocate(b); }(); - } - - auto tg = new ThreadGroup; - foreach (i; 0 .. 20) - { - tg.create(&fun); - } - - tg.joinAll(); -} - -@system unittest -{ - import std.experimental.allocator.mallocator : Mallocator; - static shared SharedFreeList!(Mallocator, 64, 128, 10) a; - auto b = a.allocate(100); - // Ensure deallocate inherits from parent - () nothrow @nogc { a.deallocate(b); }(); - assert(a.nodes == 1); - b = []; - assert((() nothrow @nogc => a.deallocateAll())()); - assert(a.nodes == 0); -} - -@system unittest -{ - import std.experimental.allocator.mallocator : Mallocator; - static shared SharedFreeList!(Mallocator, 64, 128, 10) a; - auto b = a.allocate(100); - auto c = a.allocate(100); - // Ensure deallocate inherits from parent - () nothrow @nogc { a.deallocate(c); }(); - assert(a.nodes == 1); - c = []; - a.minimize(); - assert(a.nodes == 0); - () nothrow @nogc { a.deallocate(b); }(); - assert(a.nodes == 1); - b = []; - a.minimize(); - assert(a.nodes == 0); -} - -@system unittest -{ - import std.experimental.allocator.mallocator : Mallocator; - static shared SharedFreeList!(Mallocator, 64, 128, 10) a; - auto b = a.allocate(100); - auto c = a.allocate(100); - assert(a.nodes == 0); - // Ensure deallocate inherits from parent - () nothrow @nogc { a.deallocate(b); }(); - () nothrow @nogc { a.deallocate(c); }(); - assert(a.nodes == 2); - b = []; - c = []; - a.minimize(); - assert(a.nodes == 0); -} - -@system unittest -{ - import std.experimental.allocator.mallocator : Mallocator; - shared SharedFreeList!(Mallocator, chooseAtRuntime, chooseAtRuntime) a; - scope(exit) assert((() nothrow @nogc => a.deallocateAll())()); - auto c = a.allocate(64); - assert((() nothrow @nogc => a.reallocate(c, 96))()); - assert(c.length == 96); - // Ensure deallocate inherits from parent - () nothrow @nogc { a.deallocate(c); }(); -} - -@system unittest -{ - import std.experimental.allocator.mallocator : Mallocator; - shared SharedFreeList!(Mallocator, chooseAtRuntime, chooseAtRuntime, chooseAtRuntime) a; - scope(exit) assert((() nothrow @nogc => a.deallocateAll())()); - a.allocate(64); -} - -@system unittest -{ - import std.experimental.allocator.mallocator : Mallocator; - shared SharedFreeList!(Mallocator, 30, 40) a; - scope(exit) assert((() nothrow @nogc => a.deallocateAll())()); - a.allocate(64); -} - -@system unittest -{ - import std.experimental.allocator.mallocator : Mallocator; - shared SharedFreeList!(Mallocator, 30, 40, chooseAtRuntime) a; - scope(exit) assert((() nothrow @nogc => a.deallocateAll())()); - a.allocate(64); -} - -@system unittest -{ - // Pull request #5556 - import std.experimental.allocator.mallocator : Mallocator; - shared SharedFreeList!(Mallocator, 0, chooseAtRuntime) a; - scope(exit) assert((() nothrow @nogc => a.deallocateAll())()); - a.max = 64; - a.allocate(64); -} - -@system unittest -{ - // Pull request #5556 - import std.experimental.allocator.mallocator : Mallocator; - shared SharedFreeList!(Mallocator, chooseAtRuntime, 64) a; - scope(exit) assert((() nothrow @nogc => a.deallocateAll())()); - a.min = 32; - a.allocate(64); -} diff --git a/phobos/std/experimental/allocator/building_blocks/free_tree.d b/phobos/std/experimental/allocator/building_blocks/free_tree.d deleted file mode 100644 index fe59e26..0000000 --- a/phobos/std/experimental/allocator/building_blocks/free_tree.d +++ /dev/null @@ -1,515 +0,0 @@ -// Written in the D programming language. -/** -Source: $(PHOBOSSRC std/experimental/allocator/building_blocks/_free_tree.d) -*/ -module std.experimental.allocator.building_blocks.free_tree; - -import std.experimental.allocator.common; - -//debug = std_experimental_allocator_free_tree; - -/** - -The Free Tree allocator, stackable on top of any other allocator, bears -similarity with the free list allocator. Instead of a singly-linked list of -previously freed blocks, it maintains a binary search tree. This allows the -Free Tree allocator to manage blocks of arbitrary lengths and search them -efficiently. - -Common uses of `FreeTree` include: - -$(UL -$(LI Adding `deallocate` capability to an allocator that lacks it (such as simple regions).) -$(LI Getting the benefits of multiple adaptable freelists that do not need to -be tuned for one specific size but insted automatically adapts itself to -frequently used sizes.) -) - -The free tree has special handling of duplicates (a singly-linked list per -node) in anticipation of large number of duplicates. Allocation time from the -free tree is expected to be $(BIGOH log n) where `n` is the number of -distinct sizes (not total nodes) kept in the free tree. - -Allocation requests first search the tree for a buffer of suitable size -deallocated in the past. If a match is found, the node is removed from the tree -and the memory is returned. Otherwise, the allocation is directed to $(D -ParentAllocator). If at this point `ParentAllocator` also fails to allocate, -`FreeTree` frees everything and then tries the parent allocator again. - -Upon deallocation, the deallocated block is inserted in the internally -maintained free tree (not returned to the parent). The free tree is not kept -balanced. Instead, it has a last-in-first-out flavor because newly inserted -blocks are rotated to the root of the tree. That way allocations are cache -friendly and also frequently used sizes are more likely to be found quickly, -whereas seldom used sizes migrate to the leaves of the tree. - -`FreeTree` rounds up small allocations to at least $(D 4 * size_t.sizeof), -which on 64-bit system is one cache line size. If very small objects need to -be efficiently allocated, the `FreeTree` should be fronted with an -appropriate small object allocator. - -The following methods are defined if `ParentAllocator` defines them, and forward to it: `allocateAll`, `expand`, `owns`, `reallocate`. -*/ -struct FreeTree(ParentAllocator) -{ - static assert(ParentAllocator.alignment % size_t.alignof == 0, - "FreeTree must be on top of a word-aligned allocator"); - - import std.algorithm.comparison : min, max; - import std.algorithm.mutation : swap; - import std.traits : hasMember; - - // State - static if (stateSize!ParentAllocator) private ParentAllocator parent; - else private alias parent = ParentAllocator.instance; - private Node* root; // that's the entire added state - - private struct Node - { - Node*[2] kid; - Node* sibling; - size_t size; - ref Node* left() { return kid[0]; } - ref Node* right() { return kid[1]; } - } - - // Removes "which" from the tree, returns the memory it occupied - private void[] remove(ref Node* which) - { - assert(which); - assert(!which.sibling); - auto result = (cast(ubyte*) which)[0 .. which.size]; - if (!which.right) which = which.left; - else if (!which.left) which = which.right; - else - { - // result has two kids - static bool toggler; - // Crude randomization: alternate left/right choices - toggler = !toggler; - auto newRoot = which.kid[toggler], orphan = which.kid[!toggler]; - which = newRoot; - for (Node* n = void; (n = newRoot.kid[!toggler]) !is null; ) - { - newRoot = n; - } - newRoot.kid[!toggler] = orphan; - } - return result; - } - - private void[] findAndRemove(ref Node* n, size_t s) - { - if (!n) return null; - if (s == n.size) - { - if (auto sis = n.sibling) - { - // Nice, give away one from the freelist - auto result = (cast(ubyte*) sis)[0 .. sis.size]; - n.sibling = sis.sibling; - return result; - } - return remove(n); - } - return findAndRemove(n.kid[s > n.size], s); - } - - debug(std_experimental_allocator_free_tree) - private void dump() - { - import std.stdio : writef, writefln, writeln; - writeln(typeof(this).stringof, "@", &this, " {"); - scope(exit) writeln("}"); - - if (!root) return; - - static void recurse(Node* n, uint indent = 4) - { - if (!n) - { - writefln("%*s(null)", indent, ""); - return; - } - for (auto sis = n; sis; sis = sis.sibling) - { - writef("%*s%x (%s bytes) ", indent, "", - cast(void*) n, n.size); - } - writeln; - if (!n.left && !n.right) return; - recurse(n.left, indent + 4); - recurse(n.right, indent + 4); - } - recurse(root); - } - - private string formatSizes() - { - string result = "("; - void recurse(Node* n) - { - if (!n) - { - result ~= "_"; - return; - } - import std.conv : to; - result ~= to!string(n.size); - for (auto sis = n.sibling; sis; sis = sis.sibling) - { - result ~= "+moar"; - } - if (n.left || n.right) - { - result ~= " ("; - recurse(n.left); - result ~= ' '; - recurse(n.right); - result ~= ")"; - } - } - recurse(root); - return result ~= ")"; - } - - private static void rotate(ref Node* parent, bool toRight) - { - assert(parent); - auto opposing = parent.kid[!toRight]; - if (!opposing) return; - parent.kid[!toRight] = opposing.kid[toRight]; - opposing.kid[toRight] = parent; - parent = opposing; - } - - // Inserts which into the tree, making it the new root - private void insertAsRoot(Node* which) - { - assert(which); - debug(std_experimental_allocator_free_tree) - { - assertValid; - scope(exit) assertValid; - } - - static void recurse(ref Node* where, Node* which) - { - if (!where) - { - where = which; - which.left = null; - which.right = null; - which.sibling = null; - return; - } - if (which.size == where.size) - { - // Special handling of duplicates - which.sibling = where.sibling; - where.sibling = which; - which.left = null; - which.right = null; - return; - } - bool goRight = which.size > where.size; - recurse(where.kid[goRight], which); - rotate(where, !goRight); - } - recurse(root, which); - } - - private void assertValid() - { - debug(std_experimental_allocator_free_tree) - { - static bool isBST(Node* n, size_t lb = 0, size_t ub = size_t.max) - { - if (!n) return true; - for (auto sis = n.sibling; sis; sis = sis.sibling) - { - assert(n.size == sis.size); - assert(sis.left is null); - assert(sis.right is null); - } - return lb < n.size && n.size <= ub - && isBST(n.left, lb, min(ub, n.size)) - && isBST(n.right, max(lb, n.size), ub); - } - if (isBST(root)) return; - dump; - assert(0); - } - } - - /** - The `FreeTree` is word aligned. - */ - enum uint alignment = size_t.alignof; - - /** - The `FreeTree` allocator is noncopyable. - */ - this(this) @disable; - - /** - The destructor of `FreeTree` releases all memory back to the parent - allocator. - */ - static if (hasMember!(ParentAllocator, "deallocate")) - ~this() - { - clear; - } - - /** - Returns $(D parent.goodAllocSize(max(Node.sizeof, s))). - */ - static if (stateSize!ParentAllocator) - size_t goodAllocSize(size_t s) - { - return parent.goodAllocSize(max(Node.sizeof, s)); - } - else - static size_t goodAllocSize(size_t s) - { - return parent.goodAllocSize(max(Node.sizeof, s)); - } - - /** - - Allocates `n` bytes of memory. First consults the free tree, and returns - from it if a suitably sized block is found. Otherwise, the parent allocator - is tried. If allocation from the parent succeeds, the allocated block is - returned. Otherwise, the free tree tries an alternate strategy: If $(D - ParentAllocator) defines `deallocate`, `FreeTree` releases all of its - contents and tries again. - - TODO: Splitting and coalescing should be implemented if `ParentAllocator` does not defined `deallocate`. - - */ - void[] allocate(size_t n) - { - assertValid; - if (n == 0) return null; - - immutable s = goodAllocSize(n); - - // Consult the free tree. - auto result = findAndRemove(root, s); - if (result.ptr) return result.ptr[0 .. n]; - - // No block found, try the parent allocator. - result = parent.allocate(s); - if (result.ptr) return result.ptr[0 .. n]; - - // Parent ran out of juice, desperation mode on - static if (hasMember!(ParentAllocator, "deallocate")) - { - clear; - // Try parent allocator again. - result = parent.allocate(s); - if (result.ptr) return result.ptr[0 .. n]; - return null; - } - else - { - // TODO: get smart here - return null; - } - } - - // Forwarding methods - mixin(forwardToMember("parent", - "allocateAll", "expand", "owns", "reallocate")); - - /** Places `b` into the free tree. */ - bool deallocate(void[] b) - { - if (!b.ptr) return true; - auto which = cast(Node*) b.ptr; - which.size = goodAllocSize(b.length); - // deliberately don't initialize which.left and which.right - assert(which.size >= Node.sizeof); - insertAsRoot(which); - return true; - } - - @system unittest // test a few simple configurations - { - import std.experimental.allocator.gc_allocator; - FreeTree!GCAllocator a; - auto b1 = a.allocate(10000); - auto b2 = a.allocate(20000); - auto b3 = a.allocate(30000); - assert(b1.ptr && b2.ptr && b3.ptr); - () nothrow @nogc { a.deallocate(b1); }(); - () nothrow @nogc { a.deallocate(b3); }(); - () nothrow @nogc { a.deallocate(b2); }(); - assert(a.formatSizes == "(20480 (12288 32768))", a.formatSizes); - - b1 = a.allocate(10000); - assert(a.formatSizes == "(20480 (_ 32768))", a.formatSizes); - b1 = a.allocate(30000); - assert(a.formatSizes == "(20480)", a.formatSizes); - b1 = a.allocate(20000); - assert(a.formatSizes == "(_)", a.formatSizes); - } - - @system unittest // build a complex free tree - { - import std.experimental.allocator.gc_allocator, std.range; - FreeTree!GCAllocator a; - uint[] sizes = [3008,704,1856,576,1632,672,832,1856,1120,2656,1216,672, - 448,992,2400,1376,2688,2656,736,1440]; - void[][] allocs; - foreach (s; sizes) - allocs ~= a.allocate(s); - foreach_reverse (b; allocs) - { - assert(b.ptr); - () nothrow @nogc { a.deallocate(b); }(); - } - a.assertValid; - allocs = null; - foreach (s; sizes) - allocs ~= a.allocate(s); - assert(a.root is null); - a.assertValid; - } - - /** Defined if `ParentAllocator.deallocate` exists, and returns to it - all memory held in the free tree. */ - static if (hasMember!(ParentAllocator, "deallocate")) - void clear() - { - void recurse(Node* n) - { - if (!n) return; - recurse(n.left); - recurse(n.right); - parent.deallocate((cast(ubyte*) n)[0 .. n.size]); - } - recurse(root); - root = null; - } - - /** - - Defined if `ParentAllocator.deallocateAll` exists, and forwards to it. - Also nullifies the free tree (it's assumed the parent frees all memory - stil managed by the free tree). - - */ - static if (hasMember!(ParentAllocator, "deallocateAll")) - bool deallocateAll() - { - // This is easy, just nuke the root and deallocate all from the - // parent - root = null; - return parent.deallocateAll; - } -} - -version (StdUnittest) -@system unittest -{ - import std.experimental.allocator.gc_allocator; - testAllocator!(() => FreeTree!GCAllocator()); -} - -// https://issues.dlang.org/show_bug.cgi?id=16506 -@system unittest -{ - import std.experimental.allocator.gc_allocator : GCAllocator; - import std.experimental.allocator.mallocator : Mallocator; - - static void f(ParentAllocator)(size_t sz) - { - static FreeTree!ParentAllocator myAlloc; - byte[] _payload = cast(byte[]) myAlloc.allocate(sz); - assert(_payload, "_payload is null"); - _payload[] = 0; - () nothrow @nogc { myAlloc.deallocate(_payload); }(); - } - - f!Mallocator(33); - f!Mallocator(43); - f!GCAllocator(1); -} - -// https://issues.dlang.org/show_bug.cgi?id=16507 -@system unittest -{ - static struct MyAllocator - { - byte dummy; - static bool alive = true; - void[] allocate(size_t s) { return new byte[](s); } - bool deallocate(void[] ) { if (alive) assert(false); return true; } - enum alignment = size_t.sizeof; - } - - FreeTree!MyAllocator ft; - void[] x = ft.allocate(1); - () nothrow @nogc { ft.deallocate(x); }(); - ft.allocate(1000); - MyAllocator.alive = false; -} - -@system unittest // "desperation mode" -{ - uint myDeallocCounter = 0; - - struct MyAllocator - { - byte[] allocation; - void[] allocate(size_t s) - { - if (allocation.ptr) return null; - allocation = new byte[](s); - return allocation; - } - bool deallocate(void[] ) - { - ++myDeallocCounter; - allocation = null; - return true; - } - enum alignment = size_t.sizeof; - } - - FreeTree!MyAllocator ft; - void[] x = ft.allocate(1); - () nothrow @nogc { ft.deallocate(x); }(); - assert(myDeallocCounter == 0); - x = ft.allocate(1000); // Triggers "desperation mode". - assert(myDeallocCounter == 1); - assert(x.ptr); - void[] y = ft.allocate(1000); /* Triggers "desperation mode" but there's - nothing to deallocate so MyAllocator can't deliver. */ - assert(myDeallocCounter == 1); - assert(y.ptr is null); -} - -@system unittest -{ - import std.experimental.allocator.gc_allocator; - FreeTree!GCAllocator a; - - assert((() nothrow @safe @nogc => a.goodAllocSize(1))() == typeof(*a.root).sizeof); -} - -@system unittest -{ - import std.experimental.allocator.building_blocks.region : BorrowedRegion; - - auto a = FreeTree!(BorrowedRegion!())(BorrowedRegion!()(new ubyte[1024 * 64])); - auto b = a.allocate(42); - assert(b.length == 42); - assert((() pure nothrow @safe @nogc => a.expand(b, 22))()); - assert(b.length == 64); - assert((() nothrow @nogc => a.reallocate(b, 100))()); - assert(b.length == 100); - assert((() nothrow @nogc => a.deallocateAll())()); -} diff --git a/phobos/std/experimental/allocator/building_blocks/kernighan_ritchie.d b/phobos/std/experimental/allocator/building_blocks/kernighan_ritchie.d deleted file mode 100644 index 167cf1b..0000000 --- a/phobos/std/experimental/allocator/building_blocks/kernighan_ritchie.d +++ /dev/null @@ -1,939 +0,0 @@ -// Written in the D programming language. -/** -Source: $(PHOBOSSRC std/experimental/allocator/building_blocks/kernighan_ritchie.d) -*/ -module std.experimental.allocator.building_blocks.kernighan_ritchie; -import std.experimental.allocator.building_blocks.null_allocator : - NullAllocator; - -//debug = KRRegion; -debug(KRRegion) import std.stdio; - -// KRRegion -/** -`KRRegion` draws inspiration from the $(MREF_ALTTEXT region allocation -strategy, std,experimental,allocator,building_blocks,region) and also the -$(HTTP stackoverflow.com/questions/13159564/explain-this-implementation-of-malloc-from-the-kr-book, -famed allocator) described by Brian Kernighan and Dennis Ritchie in section 8.7 -of the book $(HTTP amazon.com/exec/obidos/ASIN/0131103628/classicempire, "The C -Programming Language"), Second Edition, Prentice Hall, 1988. - -$(H4 `KRRegion` = `Region` + Kernighan-Ritchie Allocator) - -Initially, `KRRegion` starts in "region" mode: allocations are served from -the memory chunk in a region fashion. Thus, as long as there is enough memory -left, `KRRegion.allocate` has the performance profile of a region allocator. -Deallocation inserts (in $(BIGOH 1) time) the deallocated blocks in an -unstructured freelist, which is not read in region mode. - -Once the region cannot serve an `allocate` request, `KRRegion` switches -to "free list" mode. It sorts the list of previously deallocated blocks by -address and serves allocation requests off that free list. The allocation and -deallocation follow the pattern described by Kernighan and Ritchie. - -The recommended use of `KRRegion` is as a $(I region with deallocation). If the -`KRRegion` is dimensioned appropriately, it could often not enter free list -mode during its lifetime. Thus it is as fast as a simple region, whilst -offering deallocation at a small cost. When the region memory is exhausted, -the previously deallocated memory is still usable, at a performance cost. If -the region is not excessively large and fragmented, the linear allocation and -deallocation cost may still be compensated for by the good locality -characteristics. - -If the chunk of memory managed is large, it may be desirable to switch -management to free list from the beginning. That way, memory may be used in a -more compact manner than region mode. To force free list mode, call $(D -switchToFreeList) shortly after construction or when deemed appropriate. - -The smallest size that can be allocated is two words (16 bytes on 64-bit -systems, 8 bytes on 32-bit systems). This is because the free list management -needs two words (one for the length, the other for the next pointer in the -singly-linked list). - -The `ParentAllocator` type parameter is the type of the allocator used to -allocate the memory chunk underlying the `KRRegion` object. Choosing the -default (`NullAllocator`) means the user is responsible for passing a buffer -at construction (and for deallocating it if necessary). Otherwise, `KRRegion` -automatically deallocates the buffer during destruction. For that reason, if -`ParentAllocator` is not `NullAllocator`, then `KRRegion` is not -copyable. - -$(H4 Implementation Details) - -In free list mode, `KRRegion` embeds a free blocks list onto the chunk of -memory. The free list is circular, coalesced, and sorted by address at all -times. Allocations and deallocations take time proportional to the number of -previously deallocated blocks. (In practice the cost may be lower, e.g. if -memory is deallocated in reverse order of allocation, all operations take -constant time.) Memory utilization is good (small control structure and no -per-allocation overhead). The disadvantages of freelist mode include proneness -to fragmentation, a minimum allocation size of two words, and linear worst-case -allocation and deallocation times. - -Similarities of `KRRegion` (in free list mode) with the -Kernighan-Ritchie allocator: - -$(UL -$(LI Free blocks have variable size and are linked in a singly-linked list.) -$(LI The freelist is maintained in increasing address order, which makes -coalescing easy.) -$(LI The strategy for finding the next available block is first fit.) -$(LI The free list is circular, with the last node pointing back to the first.) -$(LI Coalescing is carried during deallocation.) -) - -Differences from the Kernighan-Ritchie allocator: - -$(UL -$(LI Once the chunk is exhausted, the Kernighan-Ritchie allocator allocates -another chunk using operating system primitives. For better composability, $(D -KRRegion) just gets full (returns `null` on new allocation requests). The -decision to allocate more blocks is deferred to a higher-level entity. For an -example, see the example below using `AllocatorList` in conjunction with $(D -KRRegion).) -$(LI Allocated blocks do not hold a size prefix. This is because in D the size -information is available in client code at deallocation time.) -) - -*/ -struct KRRegion(ParentAllocator = NullAllocator) -{ - import std.experimental.allocator.common : stateSize, alignedAt; - import std.traits : hasMember; - import std.typecons : Ternary; - - private static struct Node - { - import std.typecons : tuple, Tuple; - - Node* next; - size_t size; - - this(this) @disable; - - void[] payload() inout - { - return (cast(ubyte*) &this)[0 .. size]; - } - - bool adjacent(in Node* right) const - { - assert(right); - auto p = payload; - return p.ptr < right && right < p.ptr + p.length + Node.sizeof; - } - - bool coalesce(void* memoryEnd = null) - { - // Coalesce the last node before the memory end with any possible gap - if (memoryEnd - && memoryEnd < payload.ptr + payload.length + Node.sizeof) - { - size += memoryEnd - (payload.ptr + payload.length); - return true; - } - - if (!adjacent(next)) return false; - size = (cast(ubyte*) next + next.size) - cast(ubyte*) &this; - next = next.next; - return true; - } - - Tuple!(void[], Node*) allocateHere(size_t bytes) - { - assert(bytes >= Node.sizeof); - assert(bytes % Node.alignof == 0); - assert(next); - assert(!adjacent(next)); - if (size < bytes) return typeof(return)(); - assert(size >= bytes); - immutable leftover = size - bytes; - - if (leftover >= Node.sizeof) - { - // There's room for another node - auto newNode = cast(Node*) ((cast(ubyte*) &this) + bytes); - newNode.size = leftover; - newNode.next = next == &this ? newNode : next; - assert(next); - return tuple(payload, newNode); - } - - // No slack space, just return next node - return tuple(payload, next == &this ? null : next); - } - } - - // state - /** - If `ParentAllocator` holds state, `parent` is a public member of type - `KRRegion`. Otherwise, `parent` is an `alias` for - `ParentAllocator.instance`. - */ - static if (stateSize!ParentAllocator) ParentAllocator parent; - else alias parent = ParentAllocator.instance; - private void[] payload; - private Node* root; - private bool regionMode() const { return bytesUsedRegionMode != size_t.max; } - private void cancelRegionMode() { bytesUsedRegionMode = size_t.max; } - private size_t bytesUsedRegionMode = 0; - - auto byNodePtr() - { - static struct Range - { - Node* start, current; - @property bool empty() { return !current; } - @property Node* front() { return current; } - void popFront() - { - assert(current && current.next); - current = current.next; - if (current == start) current = null; - } - @property Range save() { return this; } - } - import std.range : isForwardRange; - static assert(isForwardRange!Range); - return Range(root, root); - } - - string toString() - { - import std.format : format; - string s = "KRRegion@"; - s ~= format("%s-%s(0x%s[%s] %s", &this, &this + 1, - payload.ptr, payload.length, - regionMode ? "(region)" : "(freelist)"); - - Node* lastNode = null; - if (!regionMode) - { - foreach (node; byNodePtr) - { - s ~= format(", %sfree(0x%s[%s])", - lastNode && lastNode.adjacent(node) ? "+" : "", - cast(void*) node, node.size); - lastNode = node; - } - } - else - { - for (auto node = root; node; node = node.next) - { - s ~= format(", %sfree(0x%s[%s])", - lastNode && lastNode.adjacent(node) ? "+" : "", - cast(void*) node, node.size); - lastNode = node; - } - } - - s ~= ')'; - return s; - } - - private void assertValid(string s) - { - assert(!regionMode); - if (!payload.ptr) - { - assert(!root, s); - return; - } - if (!root) - { - return; - } - assert(root >= payload.ptr, s); - assert(root < payload.ptr + payload.length, s); - - // Check that the list terminates - size_t n; - foreach (node; byNodePtr) - { - assert(node.next); - assert(!node.adjacent(node.next)); - assert(n++ < payload.length / Node.sizeof, s); - } - } - - private Node* sortFreelist(Node* root) - { - // Find a monotonic run - auto last = root; - for (;;) - { - if (!last.next) return root; - if (last > last.next) break; - assert(last < last.next); - last = last.next; - } - auto tail = last.next; - last.next = null; - tail = sortFreelist(tail); - return merge(root, tail); - } - - private Node* merge(Node* left, Node* right) - { - assert(left != right); - if (!left) return right; - if (!right) return left; - if (left < right) - { - auto result = left; - result.next = merge(left.next, right); - return result; - } - auto result = right; - result.next = merge(left, right.next); - return result; - } - - private void coalesceAndMakeCircular() - { - for (auto n = root;;) - { - assert(!n.next || n < n.next); - if (!n.next) - { - // Convert to circular - n.next = root; - break; - } - if (n.coalesce) continue; // possibly another coalesce - n = n.next; - } - } - - /** - Create a `KRRegion`. If `ParentAllocator` is not `NullAllocator`, - `KRRegion`'s destructor will call `parent.deallocate`. - - Params: - b = Block of memory to serve as support for the allocator. Memory must be - larger than two words and word-aligned. - n = Capacity desired. This constructor is defined only if $(D - ParentAllocator) is not `NullAllocator`. - */ - this(ubyte[] b) - { - if (b.length < Node.sizeof) - { - // Init as empty - assert(root is null); - assert(payload is null); - return; - } - assert(b.length >= Node.sizeof); - assert(b.ptr.alignedAt(Node.alignof)); - assert(b.length >= 2 * Node.sizeof); - payload = b; - root = cast(Node*) b.ptr; - // Initialize the free list with all list - assert(regionMode); - root.next = null; - root.size = b.length; - debug(KRRegion) writefln("KRRegion@%s: init with %s[%s]", &this, - b.ptr, b.length); - } - - /// Ditto - static if (!is(ParentAllocator == NullAllocator) && !stateSize!ParentAllocator) - this(size_t n) - { - assert(n > Node.sizeof); - this(cast(ubyte[])(parent.allocate(n))); - } - - /// Ditto - static if (!is(ParentAllocator == NullAllocator) && stateSize!ParentAllocator) - this(ParentAllocator parent, size_t n) - { - assert(n > Node.sizeof); - this.parent = parent; - this(cast(ubyte[])(parent.allocate(n))); - } - - /// Ditto - static if (!is(ParentAllocator == NullAllocator) - && hasMember!(ParentAllocator, "deallocate")) - ~this() - { - parent.deallocate(payload); - } - - /** - Forces free list mode. If already in free list mode, does nothing. - Otherwise, sorts the free list accumulated so far and switches strategy for - future allocations to KR style. - */ - void switchToFreeList() - { - if (!regionMode) return; - cancelRegionMode; - if (!root) return; - root = sortFreelist(root); - coalesceAndMakeCircular; - } - - /* - Noncopyable - */ - @disable this(this); - - /** - Word-level alignment. - */ - enum alignment = Node.alignof; - - /** - Allocates `n` bytes. Allocation searches the list of available blocks - until a free block with `n` or more bytes is found (first fit strategy). - The block is split (if larger) and returned. - - Params: n = number of bytes to _allocate - - Returns: A word-aligned buffer of `n` bytes, or `null`. - */ - void[] allocate(size_t n) - { - if (!n || !root) return null; - const actualBytes = goodAllocSize(n); - - // Try the region first - if (regionMode) - { - // Only look at the head of the freelist - if (root.size >= actualBytes) - { - // Enough room for allocation - bytesUsedRegionMode += actualBytes; - void* result = root; - immutable balance = root.size - actualBytes; - if (balance >= Node.sizeof) - { - auto newRoot = cast(Node*) (result + actualBytes); - newRoot.next = root.next; - newRoot.size = balance; - root = newRoot; - } - else - { - root = null; - switchToFreeList; - } - return result[0 .. n]; - } - - // Not enough memory, switch to freelist mode and fall through - switchToFreeList; - } - - // Try to allocate from next after the iterating node - for (auto pnode = root;;) - { - assert(!pnode.adjacent(pnode.next)); - auto k = pnode.next.allocateHere(actualBytes); - if (k[0] !is null) - { - // awes - assert(k[0].length >= n); - if (root == pnode.next) root = k[1]; - pnode.next = k[1]; - return k[0][0 .. n]; - } - - pnode = pnode.next; - if (pnode == root) break; - } - return null; - } - - /** - Deallocates `b`, which is assumed to have been previously allocated with - this allocator. Deallocation performs a linear search in the free list to - preserve its sorting order. It follows that blocks with higher addresses in - allocators with many free blocks are slower to deallocate. - - Params: b = block to be deallocated - */ - nothrow @nogc - bool deallocate(void[] b) - { - debug(KRRegion) writefln("KRRegion@%s: deallocate(%s[%s])", &this, - b.ptr, b.length); - if (!b.ptr) return true; - assert(owns(b) == Ternary.yes); - assert(b.ptr.alignedAt(Node.alignof)); - - // Insert back in the freelist, keeping it sorted by address. Do not - // coalesce at this time. Instead, do it lazily during allocation. - auto n = cast(Node*) b.ptr; - n.size = goodAllocSize(b.length); - auto memoryEnd = payload.ptr + payload.length; - - if (regionMode) - { - assert(root); - // Insert right after root - bytesUsedRegionMode -= n.size; - n.next = root.next; - root.next = n; - return true; - } - - if (!root) - { - // What a sight for sore eyes - root = n; - root.next = root; - - // If the first block freed is the last one allocated, - // maybe there's a gap after it. - root.coalesce(memoryEnd); - return true; - } - - version (assert) foreach (test; byNodePtr) - { - assert(test != n); - } - // Linear search - auto pnode = root; - do - { - assert(pnode && pnode.next); - assert(pnode != n); - assert(pnode.next != n); - - if (pnode < pnode.next) - { - if (pnode > n || n > pnode.next) continue; - // Insert in between pnode and pnode.next - n.next = pnode.next; - pnode.next = n; - n.coalesce; - pnode.coalesce; - root = pnode; - return true; - } - else if (pnode < n) - { - // Insert at the end of the list - // Add any possible gap at the end of n to the length of n - n.next = pnode.next; - pnode.next = n; - n.coalesce(memoryEnd); - pnode.coalesce; - root = pnode; - return true; - } - else if (n < pnode.next) - { - // Insert at the front of the list - n.next = pnode.next; - pnode.next = n; - n.coalesce; - root = n; - return true; - } - } - while ((pnode = pnode.next) != root); - assert(0, "Wrong parameter passed to deallocate"); - } - - /** - Allocates all memory available to this allocator. If the allocator is empty, - returns the entire available block of memory. Otherwise, it still performs - a best-effort allocation: if there is no fragmentation (e.g. `allocate` - has been used but not `deallocate`), allocates and returns the only - available block of memory. - - The operation takes time proportional to the number of adjacent free blocks - at the front of the free list. These blocks get coalesced, whether - `allocateAll` succeeds or fails due to fragmentation. - */ - void[] allocateAll() - { - if (regionMode) switchToFreeList; - if (root && root.next == root) - return allocate(root.size); - return null; - } - - /// - @system unittest - { - import std.experimental.allocator.gc_allocator : GCAllocator; - auto alloc = KRRegion!GCAllocator(1024 * 64); - const b1 = alloc.allocate(2048); - assert(b1.length == 2048); - const b2 = alloc.allocateAll; - assert(b2.length == 1024 * 62); - } - - /** - Deallocates all memory currently allocated, making the allocator ready for - other allocations. This is a $(BIGOH 1) operation. - */ - pure nothrow @nogc - bool deallocateAll() - { - debug(KRRegion) assertValid("deallocateAll"); - debug(KRRegion) scope(exit) assertValid("deallocateAll"); - root = cast(Node*) payload.ptr; - - // Reset to regionMode - bytesUsedRegionMode = 0; - if (root) - { - root.next = null; - root.size = payload.length; - } - return true; - } - - /** - Checks whether the allocator is responsible for the allocation of `b`. - It does a simple $(BIGOH 1) range check. `b` should be a buffer either - allocated with `this` or obtained through other means. - */ - pure nothrow @trusted @nogc - Ternary owns(void[] b) - { - debug(KRRegion) assertValid("owns"); - debug(KRRegion) scope(exit) assertValid("owns"); - return Ternary(b && payload && (&b[0] >= &payload[0]) - && (&b[0] < &payload[0] + payload.length)); - } - - /** - Adjusts `n` to a size suitable for allocation (two words or larger, - word-aligned). - */ - pure nothrow @safe @nogc - static size_t goodAllocSize(size_t n) - { - import std.experimental.allocator.common : roundUpToMultipleOf; - return n <= Node.sizeof - ? Node.sizeof : n.roundUpToMultipleOf(alignment); - } - - /** - Returns: `Ternary.yes` if the allocator is empty, `Ternary.no` otherwise. - Never returns `Ternary.unknown`. - */ - pure nothrow @safe @nogc - Ternary empty() - { - if (regionMode) - return Ternary(bytesUsedRegionMode == 0); - - return Ternary(root && root.size == payload.length); - } -} - -/** -`KRRegion` is preferable to `Region` as a front for a general-purpose -allocator if `deallocate` is needed, yet the actual deallocation traffic is -relatively low. The example below shows a `KRRegion` using stack storage -fronting the GC allocator. -*/ -@system unittest -{ - import std.experimental.allocator.building_blocks.fallback_allocator - : fallbackAllocator; - import std.experimental.allocator.gc_allocator : GCAllocator; - import std.typecons : Ternary; - // KRRegion fronting a general-purpose allocator - align(KRRegion!().alignment) ubyte[1024 * 128] buf; - auto alloc = fallbackAllocator(KRRegion!()(buf), GCAllocator.instance); - auto b = alloc.allocate(100); - assert(b.length == 100); - assert((() pure nothrow @safe @nogc => alloc.primary.owns(b))() == Ternary.yes); -} - -/** -The code below defines a scalable allocator consisting of 1 MB (or larger) -blocks fetched from the garbage-collected heap. Each block is organized as a -KR-style heap. More blocks are allocated and freed on a need basis. - -This is the closest example to the allocator introduced in the K$(AMP)R book. -It should perform slightly better because instead of searching through one -large free list, it searches through several shorter lists in LRU order. Also, -it actually returns memory to the operating system when possible. -*/ -@system unittest -{ - import std.algorithm.comparison : max; - import std.experimental.allocator.building_blocks.allocator_list - : AllocatorList; - import std.experimental.allocator.mmap_allocator : MmapAllocator; - AllocatorList!(n => KRRegion!MmapAllocator(max(n * 16, 1024 * 1024))) alloc; -} - -@system unittest -{ - import std.algorithm.comparison : max; - import std.experimental.allocator.building_blocks.allocator_list - : AllocatorList; - import std.experimental.allocator.mallocator : Mallocator; - import std.typecons : Ternary; - /* - Create a scalable allocator consisting of 1 MB (or larger) blocks fetched - from the garbage-collected heap. Each block is organized as a KR-style - heap. More blocks are allocated and freed on a need basis. - */ - AllocatorList!(n => KRRegion!Mallocator(max(n * 16, 1024 * 1024)), - NullAllocator) alloc; - void[][50] array; - foreach (i; 0 .. array.length) - { - auto length = i * 10_000 + 1; - array[i] = alloc.allocate(length); - assert(array[i].ptr); - assert(array[i].length == length); - } - import std.random : randomShuffle; - randomShuffle(array[]); - foreach (i; 0 .. array.length) - { - assert(array[i].ptr); - assert((() pure nothrow @safe @nogc => alloc.owns(array[i]))() == Ternary.yes); - () nothrow @nogc { alloc.deallocate(array[i]); }(); - } -} - -@system unittest -{ - import std.algorithm.comparison : max; - import std.experimental.allocator.building_blocks.allocator_list - : AllocatorList; - import std.experimental.allocator.mmap_allocator : MmapAllocator; - import std.typecons : Ternary; - /* - Create a scalable allocator consisting of 1 MB (or larger) blocks fetched - from the garbage-collected heap. Each block is organized as a KR-style - heap. More blocks are allocated and freed on a need basis. - */ - AllocatorList!((n) { - auto result = KRRegion!MmapAllocator(max(n * 2, 1024 * 1024)); - return result; - }) alloc; - void[][99] array; - foreach (i; 0 .. array.length) - { - auto length = i * 10_000 + 1; - array[i] = alloc.allocate(length); - assert(array[i].ptr); - foreach (j; 0 .. i) - { - assert(array[i].ptr != array[j].ptr); - } - assert(array[i].length == length); - } - import std.random : randomShuffle; - randomShuffle(array[]); - foreach (i; 0 .. array.length) - { - assert((() pure nothrow @safe @nogc => alloc.owns(array[i]))() == Ternary.yes); - () nothrow @nogc { alloc.deallocate(array[i]); }(); - } -} - -version (StdUnittest) -@system unittest -{ - import std.algorithm.comparison : max; - import std.experimental.allocator.building_blocks.allocator_list - : AllocatorList; - import std.experimental.allocator.common : testAllocator; - import std.experimental.allocator.gc_allocator : GCAllocator; - testAllocator!(() => AllocatorList!( - n => KRRegion!GCAllocator(max(n * 16, 1024 * 1024)))()); -} - -@system unittest -{ - import std.experimental.allocator.gc_allocator : GCAllocator; - - auto alloc = KRRegion!GCAllocator(1024 * 1024); - - void[][] array; - foreach (i; 1 .. 4) - { - array ~= alloc.allocate(i); - assert(array[$ - 1].length == i); - } - () nothrow @nogc { alloc.deallocate(array[1]); }(); - () nothrow @nogc { alloc.deallocate(array[0]); }(); - () nothrow @nogc { alloc.deallocate(array[2]); }(); - assert(alloc.allocateAll().length == 1024 * 1024); -} - -@system unittest -{ - import std.experimental.allocator.gc_allocator : GCAllocator; - import std.typecons : Ternary; - auto alloc = KRRegion!()( - cast(ubyte[])(GCAllocator.instance.allocate(1024 * 1024))); - const store = alloc.allocate(KRRegion!().sizeof); - auto p = cast(KRRegion!()* ) store.ptr; - import core.lifetime : emplace; - import core.stdc.string : memcpy; - import std.conv : text; - - memcpy(p, &alloc, alloc.sizeof); - emplace(&alloc); - - void[][100] array; - foreach (i; 0 .. array.length) - { - auto length = 100 * i + 1; - array[i] = p.allocate(length); - assert(array[i].length == length, text(array[i].length)); - assert((() pure nothrow @safe @nogc => p.owns(array[i]))() == Ternary.yes); - } - import std.random : randomShuffle; - randomShuffle(array[]); - foreach (i; 0 .. array.length) - { - assert((() pure nothrow @safe @nogc => p.owns(array[i]))() == Ternary.yes); - () nothrow @nogc { p.deallocate(array[i]); }(); - } - auto b = p.allocateAll(); - assert(b.length == 1024 * 1024 - KRRegion!().sizeof, text(b.length)); -} - -@system unittest -{ - import std.typecons : Ternary; - import std.experimental.allocator.gc_allocator : GCAllocator; - auto alloc = KRRegion!()( - cast(ubyte[])(GCAllocator.instance.allocate(1024 * 1024))); - auto p = alloc.allocateAll(); - assert(p.length == 1024 * 1024); - assert((() nothrow @nogc => alloc.deallocateAll())()); - assert(alloc.empty() == Ternary.yes); - p = alloc.allocateAll(); - assert(p.length == 1024 * 1024); -} - -@system unittest -{ - import std.random : randomCover; - import std.typecons : Ternary; - - // Both sequences must work on either system - - // A sequence of allocs which generates the error described in https://issues.dlang.org/show_bug.cgi?id=16564 - // that is a gap at the end of buf from the perspective of the allocator - - // for 64 bit systems (leftover balance = 8 bytes < 16) - int[] sizes64 = [18904, 2008, 74904, 224, 111904, 1904, 52288, 8]; - - // for 32 bit systems (leftover balance < 8) - int[] sizes32 = [81412, 107068, 49892, 23768]; - - - void test(int[] sizes) - { - align(size_t.sizeof) ubyte[256 * 1024] buf; - auto a = KRRegion!()(buf); - - void[][] bufs; - - foreach (size; sizes) - { - bufs ~= a.allocate(size); - } - - foreach (b; bufs.randomCover) - { - () nothrow @nogc { a.deallocate(b); }(); - } - - assert((() pure nothrow @safe @nogc => a.empty)() == Ternary.yes); - } - - test(sizes64); - test(sizes32); -} - -@system unittest -{ - import std.typecons : Ternary; - - // For 64 bits, we allocate in multiples of 8, but the minimum alloc size is 16. - // This can create gaps. - // This test is an example of such a case. The gap is formed between the block - // allocated for the second value in sizes and the third. There is also a gap - // at the very end. (total lost 2 * word) - - int[] sizes64 = [2008, 18904, 74904, 224, 111904, 1904, 52288, 8]; - int[] sizes32 = [81412, 107068, 49892, 23768]; - - int word64 = 8; - int word32 = 4; - - void test(int[] sizes, int word) - { - align(size_t.sizeof) ubyte[256 * 1024] buf; - auto a = KRRegion!()(buf); - - void[][] bufs; - - foreach (size; sizes) - { - bufs ~= a.allocate(size); - } - - () nothrow @nogc { a.deallocate(bufs[1]); }(); - bufs ~= a.allocate(sizes[1] - word); - - () nothrow @nogc { a.deallocate(bufs[0]); }(); - foreach (i; 2 .. bufs.length) - { - () nothrow @nogc { a.deallocate(bufs[i]); }(); - } - - assert((() pure nothrow @safe @nogc => a.empty)() == Ternary.yes); - } - - test(sizes64, word64); - test(sizes32, word32); -} - -@system unittest -{ - import std.experimental.allocator.gc_allocator : GCAllocator; - - auto a = KRRegion!GCAllocator(1024 * 1024); - assert((() pure nothrow @safe @nogc => a.goodAllocSize(1))() == typeof(*a.root).sizeof); -} - -@system unittest -{ import std.typecons : Ternary; - - align(KRRegion!().alignment) ubyte[1024] b; - auto alloc = KRRegion!()(b); - - auto k = alloc.allocate(128); - assert(k.length == 128); - assert(alloc.empty == Ternary.no); - assert(alloc.deallocate(k)); - assert(alloc.empty == Ternary.yes); - - k = alloc.allocate(512); - assert(k.length == 512); - assert(alloc.empty == Ternary.no); - assert(alloc.deallocate(k)); - assert(alloc.empty == Ternary.yes); - - k = alloc.allocate(1024); - assert(k.length == 1024); - assert(alloc.empty == Ternary.no); - assert(alloc.deallocate(k)); - assert(alloc.empty == Ternary.yes); -} diff --git a/phobos/std/experimental/allocator/building_blocks/null_allocator.d b/phobos/std/experimental/allocator/building_blocks/null_allocator.d deleted file mode 100644 index 06b4e15..0000000 --- a/phobos/std/experimental/allocator/building_blocks/null_allocator.d +++ /dev/null @@ -1,93 +0,0 @@ -// Written in the D programming language. -/** -Source: $(PHOBOSSRC std/experimental/allocator/building_blocks/null_allocator.d) -*/ -module std.experimental.allocator.building_blocks.null_allocator; - -/** -`NullAllocator` is an emphatically empty implementation of the allocator -interface. Although it has no direct use, it is useful as a "terminator" in -composite allocators. -*/ -struct NullAllocator -{ - import std.typecons : Ternary; - - nothrow @nogc pure @safe: - /** - `NullAllocator` advertises a relatively large _alignment equal to 64 KB. - This is because `NullAllocator` never actually needs to honor this - alignment and because composite allocators using `NullAllocator` - shouldn't be unnecessarily constrained. - */ - enum uint alignment = 64 * 1024; - // /// Returns `n`. - //size_t goodAllocSize(size_t n) shared const - //{ return .goodAllocSize(this, n); } - /// Always returns `null`. - void[] allocate(size_t) shared { return null; } - /// Always returns `null`. - void[] alignedAllocate(size_t, uint) shared { return null; } - /// Always returns `null`. - void[] allocateAll() shared { return null; } - /** - These methods return `false`. - Precondition: $(D b is null). This is because there is no other possible - legitimate input. - */ - bool expand(ref void[] b, size_t s) shared - { assert(b is null); return s == 0; } - /// Ditto - bool reallocate(ref void[] b, size_t) shared - { assert(b is null); return false; } - /// Ditto - bool alignedReallocate(ref void[] b, size_t, uint) shared - { assert(b is null); return false; } - /// Returns `Ternary.no`. - Ternary owns(const void[]) shared const { return Ternary.no; } - /** - Returns `Ternary.no`. - */ - Ternary resolveInternalPointer(const void*, ref void[]) shared const - { return Ternary.no; } - /** - No-op. - Precondition: $(D b is null) - */ - bool deallocate(void[] b) shared { assert(b is null); return true; } - /** - No-op. - */ - bool deallocateAll() shared { return true; } - /** - Returns `Ternary.yes`. - */ - Ternary empty() shared const { return Ternary.yes; } - /** - Returns the `shared` global instance of the `NullAllocator`. - */ - static shared NullAllocator instance; -} - -nothrow @nogc pure @safe unittest -{ - alias a = NullAllocator.instance; - - assert(a.alignedAllocate(100, 0) is null); - assert(a.allocateAll() is null); - auto b = a.allocate(100); - assert(b is null); - assert(a.expand(b, 0)); - assert(!a.expand(b, 42)); - assert(!a.reallocate(b, 42)); - assert(!a.alignedReallocate(b, 42, 0)); - assert(a.deallocate(b)); - assert(a.deallocateAll()); - - import std.typecons : Ternary; - assert(a.empty == Ternary.yes); - assert(a.owns(null) == Ternary.no); - - void[] p; - assert(a.resolveInternalPointer(null, p) == Ternary.no); -} diff --git a/phobos/std/experimental/allocator/building_blocks/package.d b/phobos/std/experimental/allocator/building_blocks/package.d deleted file mode 100644 index 6bc527d..0000000 --- a/phobos/std/experimental/allocator/building_blocks/package.d +++ /dev/null @@ -1,328 +0,0 @@ -// Written in the D programming language. -/** -$(H2 Assembling Your Own Allocator) - -This package also implements -untyped composable memory allocators. They are $(I untyped) because they deal -exclusively in `void[]` and have no notion of what type the memory allocated -would be destined for. They are $(I composable) because the included allocators -are building blocks that can be assembled in complex nontrivial allocators. - -$(P Unlike the allocators for the C and C++ programming languages, which manage -the allocated size internally, these allocators require that the client -maintains (or knows $(I a priori)) the allocation size for each piece of memory -allocated. Put simply, the client must pass the allocated size upon -deallocation. Storing the size in the _allocator has significant negative -performance implications, and is virtually always redundant because client code -needs knowledge of the allocated size in order to avoid buffer overruns. (See -more discussion in a $(HTTP open- -std.org/JTC1/SC22/WG21/docs/papers/2013/n3536.html, proposal) for sized -deallocation in C++.) For this reason, allocators herein traffic in `void[]` -as opposed to `void*`.) - -$(P In order to be usable as an _allocator, a type should implement the -following methods with their respective semantics. Only `alignment` and $(D -allocate) are required. If any of the other methods is missing, the _allocator -is assumed to not have that capability (for example some allocators do not offer -manual deallocation of memory). Allocators should NOT implement -unsupported methods to always fail. For example, an allocator that lacks the -capability to implement `alignedAllocate` should not define it at all (as -opposed to defining it to always return `null` or throw an exception). The -missing implementation statically informs other components about the -allocator's capabilities and allows them to make design decisions accordingly.) - -$(BOOKTABLE , -$(TR $(TH Method name) $(TH Semantics)) - -$(TR $(TDC uint alignment;, $(POST $(RES) > 0)) $(TD Returns the minimum -alignment of all data returned by the allocator. An allocator may implement $(D -alignment) as a statically-known `enum` value only. Applications that need -dynamically-chosen alignment values should use the `alignedAllocate` and $(D -alignedReallocate) APIs.)) - -$(TR $(TDC size_t goodAllocSize(size_t n);, $(POST $(RES) >= n)) $(TD Allocators -customarily allocate memory in discretely-sized chunks. Therefore, a request for -`n` bytes may result in a larger allocation. The extra memory allocated goes -unused and adds to the so-called $(HTTP goo.gl/YoKffF,internal fragmentation). -The function `goodAllocSize(n)` returns the actual number of bytes that would -be allocated upon a request for `n` bytes. This module defines a default -implementation that returns `n` rounded up to a multiple of the allocator's -alignment.)) - -$(TR $(TDC void[] allocate(size_t s);, $(POST $(RES) is null || $(RES).length == -s)) $(TD If $(D s == 0), the call may return any empty slice (including $(D -null)). Otherwise, the call allocates `s` bytes of memory and returns the -allocated block, or `null` if the request could not be satisfied.)) - -$(TR $(TDC void[] alignedAllocate(size_t s, uint a);, $(POST $(RES) is null || -$(RES).length == s)) $(TD Similar to `allocate`, with the additional -guarantee that the memory returned is aligned to at least `a` bytes. `a` -must be a power of 2.)) - -$(TR $(TDC void[] allocateAll();) $(TD Offers all of allocator's memory to the -caller, so it's usually defined by fixed-size allocators. If the allocator is -currently NOT managing any memory, then `allocateAll()` shall allocate and -return all memory available to the allocator, and subsequent calls to all -allocation primitives should not succeed (e.g. `allocate` shall return $(D -null) etc). Otherwise, `allocateAll` only works on a best-effort basis, and -the allocator is allowed to return `null` even if does have available memory. -Memory allocated with `allocateAll` is not otherwise special (e.g. can be -reallocated or deallocated with the usual primitives, if defined).)) - -$(TR $(TDC bool expand(ref void[] b, size_t delta);, $(POST !$(RES) || b.length -== $(I old)(b).length + delta)) $(TD Expands `b` by `delta` bytes. If $(D -delta == 0), succeeds without changing `b`. If $(D b is null), returns -`false` (the null pointer cannot be expanded in place). Otherwise, $(D -b) must be a buffer previously allocated with the same allocator. If expansion -was successful, `expand` changes `b`'s length to $(D b.length + delta) and -returns `true`. Upon failure, the call effects no change upon the allocator -object, leaves `b` unchanged, and returns `false`.)) - -$(TR $(TDC bool reallocate(ref void[] b, size_t s);, $(POST !$(RES) || b.length -== s)) $(TD Reallocates `b` to size `s`, possibly moving memory around. -`b` must be `null` or a buffer allocated with the same allocator. If -reallocation was successful, `reallocate` changes `b` appropriately and -returns `true`. Upon failure, the call effects no change upon the allocator -object, leaves `b` unchanged, and returns `false`. An allocator should -implement `reallocate` if it can derive some advantage from doing so; -otherwise, this module defines a `reallocate` free function implemented in -terms of `expand`, `allocate`, and `deallocate`.)) - -$(TR $(TDC bool alignedReallocate(ref void[] b,$(BR) size_t s, uint a);, $(POST -!$(RES) || b.length == s)) $(TD Similar to `reallocate`, but guarantees the -reallocated memory is aligned at `a` bytes. The buffer must have been -originated with a call to `alignedAllocate`. `a` must be a power of 2 -greater than `(void*).sizeof`. An allocator should implement $(D -alignedReallocate) if it can derive some advantage from doing so; otherwise, -this module defines a `alignedReallocate` free function implemented in terms -of `expand`, `alignedAllocate`, and `deallocate`.)) - -$(TR $(TDC Ternary owns(void[] b);) $(TD Returns `Ternary.yes` if `b` has been -allocated with this allocator. An allocator should define this method only if it -can decide on ownership precisely and fast (in constant time, logarithmic time, -or linear time with a low multiplication factor). Traditional allocators such as -the C heap do not define such functionality. If $(D b is null), the allocator -shall return `Ternary.no`, i.e. no allocator owns the `null` slice.)) - -$(TR $(TDC Ternary resolveInternalPointer(void* p, ref void[] result);) $(TD If -`p` is a pointer somewhere inside a block allocated with this allocator, -`result` holds a pointer to the beginning of the allocated block and returns -`Ternary.yes`. Otherwise, `result` holds `null` and returns `Ternary.no`. -If the pointer points immediately after an allocated block, the result is -implementation defined.)) - -$(TR $(TDC bool deallocate(void[] b);) $(TD If $(D b is null), does -nothing and returns `true`. Otherwise, deallocates memory previously allocated -with this allocator and returns `true` if successful, `false` otherwise. An -implementation that would not support deallocation (i.e. would always return -`false` should not define this primitive at all.))) - -$(TR $(TDC bool deallocateAll();, $(POST empty)) $(TD Deallocates all memory -allocated with this allocator. If an allocator implements this method, it must -specify whether its destructor calls it, too.)) - -$(TR $(TDC Ternary empty();) $(TD Returns `Ternary.yes` if and only if the -allocator holds no memory (i.e. no allocation has occurred, or all allocations -have been deallocated).)) - -$(TR $(TDC static Allocator instance;, $(POST instance $(I is a valid) -Allocator $(I object))) $(TD Some allocators are $(I monostate), i.e. have only -an instance and hold only global state. (Notable examples are C's own -`malloc`-based allocator and D's garbage-collected heap.) Such allocators must -define a static `instance` instance that serves as the symbolic placeholder -for the global instance of the allocator. An allocator should not hold state -and define `instance` simultaneously. Depending on whether the allocator is -thread-safe or not, this instance may be `shared`.)) -) - -$(H2 Sample Assembly) - -The example below features an _allocator modeled after $(HTTP goo.gl/m7329l, -jemalloc), which uses a battery of free-list allocators spaced so as to keep -internal fragmentation to a minimum. The `FList` definitions specify no -bounds for the freelist because the `Segregator` does all size selection in -advance. - -Sizes through 3584 bytes are handled via freelists of staggered sizes. Sizes -from 3585 bytes through 4072 KB are handled by a `BitmappedBlock` with a -block size of 4 KB. Sizes above that are passed direct to the `GCAllocator`. - -$(RUNNABLE_EXAMPLE - ---- - import std.experimental.allocator; - import std.algorithm.comparison : max; - - alias FList = FreeList!(GCAllocator, 0, unbounded); - alias A = Segregator!( - 8, FreeList!(GCAllocator, 0, 8), - 128, Bucketizer!(FList, 1, 128, 16), - 256, Bucketizer!(FList, 129, 256, 32), - 512, Bucketizer!(FList, 257, 512, 64), - 1024, Bucketizer!(FList, 513, 1024, 128), - 2048, Bucketizer!(FList, 1025, 2048, 256), - 3584, Bucketizer!(FList, 2049, 3584, 512), - 4072 * 1024, AllocatorList!(n => Region!GCAllocator(max(n, 1024 * 4096))), - GCAllocator - ); - A tuMalloc; - auto b = tuMalloc.allocate(500); - assert(b.length == 500); - auto c = tuMalloc.allocate(113); - assert(c.length == 113); - assert(tuMalloc.expand(c, 14)); - tuMalloc.deallocate(b); - tuMalloc.deallocate(c); - ---- -) - -$(H2 Allocating memory for sharing across threads) - -One allocation pattern used in multithreaded applications is to share memory -across threads, and to deallocate blocks in a different thread than the one that -allocated it. - -All allocators in this module accept and return `void[]` (as opposed to -$(D shared void[])). This is because at the time of allocation, deallocation, or -reallocation, the memory is effectively not `shared` (if it were, it would -reveal a bug at the application level). - -The issue remains of calling `a.deallocate(b)` from a different thread than -the one that allocated `b`. It follows that both threads must have access to -the same instance `a` of the respective allocator type. By definition of D, -this is possible only if `a` has the `shared` qualifier. It follows that -the allocator type must implement `allocate` and `deallocate` as $(D -shared) methods. That way, the allocator commits to allowing usable `shared` -instances. - -Conversely, allocating memory with one non-`shared` allocator, passing it -across threads (by casting the obtained buffer to `shared`), and later -deallocating it in a different thread (either with a different allocator object -or with the same allocator object after casting it to `shared`) is illegal. - -$(H2 Building Blocks) - -$(P The table below gives a synopsis of predefined allocator building blocks, -with their respective modules. Either `import` the needed modules individually, -or `import` `std.experimental.building_blocks`, which imports them all -`public`ly. The building blocks can be assembled in unbounded ways and also -combined with your own. For a collection of typical and useful preassembled -allocators and for inspiration in defining more such assemblies, refer to -$(MREF std,experimental,allocator,showcase).) - -$(BOOKTABLE, -$(TR $(TH Allocator$(BR)) $(TH Description)) - -$(TR $(TDC2 NullAllocator, null_allocator) $(TD Very good at doing absolutely nothing. A good -starting point for defining other allocators or for studying the API.)) - -$(TR $(TDC3 GCAllocator, gc_allocator) $(TD The system-provided garbage-collector allocator. -This should be the default fallback allocator tapping into system memory. It -offers manual `free` and dutifully collects litter.)) - -$(TR $(TDC3 Mallocator, mallocator) $(TD The C heap _allocator, a.k.a. $(D -malloc)/`realloc`/`free`. Use sparingly and only for code that is unlikely -to leak.)) - -$(TR $(TDC3 AlignedMallocator, mallocator) $(TD Interface to OS-specific _allocators that -support specifying alignment: -$(HTTP man7.org/linux/man-pages/man3/posix_memalign.3.html, `posix_memalign`) -on Posix and $(HTTP msdn.microsoft.com/en-us/library/fs9stz4e(v=vs.80).aspx, -`__aligned_xxx`) on Windows.)) - -$(TR $(TDC2 AlignedBlockList, aligned_block_list) $(TD A wrapper around a list of allocators -which allow for very fast deallocations.)) - -$(TR $(TDC2 AffixAllocator, affix_allocator) $(TD Allocator that allows and manages allocating -extra prefix and/or a suffix bytes for each block allocated.)) - -$(TR $(TDC2 BitmappedBlock, bitmapped_block) $(TD Organizes one contiguous chunk of memory in -equal-size blocks and tracks allocation status at the cost of one bit per -block.)) - -$(TR $(TDC2 FallbackAllocator, fallback_allocator) $(TD Allocator that combines two other - allocators - primary and fallback. Allocation requests are first tried with primary, and - upon failure are passed to the fallback. Useful for small and fast allocators - fronting general-purpose ones.)) - -$(TR $(TDC2 FreeList, free_list) $(TD Allocator that implements a $(HTTP -wikipedia.org/wiki/Free_list, free list) on top of any other allocator. The -preferred size, tolerance, and maximum elements are configurable at compile- and -run time.)) - -$(TR $(TDC2 SharedFreeList, free_list) $(TD Same features as `FreeList`, but packaged as -a `shared` structure that is accessible to several threads.)) - -$(TR $(TDC2 FreeTree, free_tree) $(TD Allocator similar to `FreeList` that uses a -binary search tree to adaptively store not one, but many free lists.)) - -$(TR $(TDC2 Region, region) $(TD Region allocator organizes a chunk of memory as a -simple bump-the-pointer allocator.)) - -$(TR $(TDC2 InSituRegion, region) $(TD Region holding its own allocation, most often on -the stack. Has statically-determined size.)) - -$(TR $(TDC2 SbrkRegion, region) $(TD Region using $(D $(LINK2 https://en.wikipedia.org/wiki/Sbrk, -sbrk)) for allocating memory.)) - -$(TR $(TDC3 MmapAllocator, mmap_allocator) $(TD Allocator using - $(D $(LINK2 https://en.wikipedia.org/wiki/Mmap, mmap)) directly.)) - -$(TR $(TDC2 StatsCollector, stats_collector) $(TD Collect statistics about any other -allocator.)) - -$(TR $(TDC2 Quantizer, quantizer) $(TD Allocates in coarse-grained quantas, thus -improving performance of reallocations by often reallocating in place. The drawback is higher memory consumption because of allocated and unused memory.)) - -$(TR $(TDC2 AllocatorList, allocator_list) $(TD Given an allocator factory, lazily creates as -many allocators as needed to satisfy allocation requests. The allocators are -stored in a linked list. Requests for allocation are satisfied by searching the -list in a linear manner.)) - -$(TR $(TDC2 Segregator, segregator) $(TD Segregates allocation requests by size -and dispatches them to distinct allocators.)) - -$(TR $(TDC2 Bucketizer, bucketizer) $(TD Divides allocation sizes in discrete buckets and -uses an array of allocators, one per bucket, to satisfy requests.)) - -$(TR $(TDC2 AscendingPageAllocator, ascending_page_allocator) $(TD A memory safe allocator -where sizes are rounded to a multiple of the page size and allocations are satisfied at increasing addresses.)) - -$(COMMENT $(TR $(TDC2 InternalPointersTree) $(TD Adds support for resolving internal -pointers on top of another allocator.))) -) - -Source: $(PHOBOSSRC std/experimental/allocator/building_blocks/package.d) - -Macros: -MYREF2 = $(REF_SHORT $1, std,experimental,allocator,building_blocks,$2) -MYREF3 = $(REF_SHORT $1, std,experimental,allocator,$2) -TDC = $(TDNW `$1`$+) -TDC2 = $(TDNW $(D $(MYREF2 $1,$+))$(BR)$(SMALL -`std.experimental.allocator.building_blocks.$2`)) -TDC3 = $(TDNW $(D $(MYREF3 $1,$+))$(BR)$(SMALL -`std.experimental.allocator.$2`)) -RES = $(I result) -POST = $(BR)$(SMALL $(I Post:) $(BLUE `$0`)) -*/ - -module std.experimental.allocator.building_blocks; - -public import - std.experimental.allocator.building_blocks.affix_allocator, - std.experimental.allocator.building_blocks.aligned_block_list, - std.experimental.allocator.building_blocks.allocator_list, - std.experimental.allocator.building_blocks.ascending_page_allocator, - std.experimental.allocator.building_blocks.bucketizer, - std.experimental.allocator.building_blocks.fallback_allocator, - std.experimental.allocator.building_blocks.free_list, - std.experimental.allocator.building_blocks.free_tree, - std.experimental.allocator.gc_allocator, - std.experimental.allocator.building_blocks.bitmapped_block, - std.experimental.allocator.building_blocks.kernighan_ritchie, - std.experimental.allocator.mallocator, - std.experimental.allocator.mmap_allocator, - std.experimental.allocator.building_blocks.null_allocator, - std.experimental.allocator.building_blocks.quantizer, - std.experimental.allocator.building_blocks.region, - std.experimental.allocator.building_blocks.segregator, - std.experimental.allocator.building_blocks.stats_collector; diff --git a/phobos/std/experimental/allocator/building_blocks/quantizer.d b/phobos/std/experimental/allocator/building_blocks/quantizer.d deleted file mode 100644 index 3334a86..0000000 --- a/phobos/std/experimental/allocator/building_blocks/quantizer.d +++ /dev/null @@ -1,334 +0,0 @@ -// Written in the D programming language. -/** -Source: $(PHOBOSSRC std/experimental/allocator/building_blocks/quantizer.d) -*/ -module std.experimental.allocator.building_blocks.quantizer; - -import std.experimental.allocator.common; - -/** -This allocator sits on top of `ParentAllocator` and quantizes allocation sizes, -usually from arbitrary positive numbers to a small set of round numbers (e.g. -powers of two, page sizes etc). This technique is commonly used to: - -$(UL -$(LI Preallocate more memory than requested such that later on, when -reallocation is needed (e.g. to grow an array), expansion can be done quickly -in place. Reallocation to smaller sizes is also fast (in-place) when the new -size requested is within the same quantum as the existing size. Code that's -reallocation-heavy can therefore benefit from fronting a generic allocator with -a `Quantizer`. These advantages are present even if `ParentAllocator` does not -support reallocation at all.) -$(LI Improve behavior of allocators sensitive to allocation sizes, such as -`FreeList` and `FreeTree`. Rounding allocation requests up makes for smaller -free lists/trees at the cost of slack memory (internal fragmentation).) -) - -The following methods are forwarded to the parent allocator if present: -`allocateAll`, `owns`, `deallocateAll`, `empty`. - -Preconditions: `roundingFunction` must satisfy three constraints. These are -not enforced (save for the use of `assert`) for the sake of efficiency. -$(OL -$(LI $(D roundingFunction(n) >= n) for all `n` of type `size_t`;) -$(LI `roundingFunction` must be monotonically increasing, i.e. $(D -roundingFunction(n1) <= roundingFunction(n2)) for all $(D n1 < n2);) -$(LI `roundingFunction` must be `nothrow`, `@safe`, `@nogc` and `pure`, i.e. -always return the same value for a given `n`.) -) -*/ -struct Quantizer(ParentAllocator, alias roundingFunction) -{ - import std.traits : hasMember; - - /** - The parent allocator. Depending on whether `ParentAllocator` holds state - or not, this is a member variable or an alias for - `ParentAllocator.instance`. - */ - static if (stateSize!ParentAllocator) - { - ParentAllocator parent; - } - else - { - alias parent = ParentAllocator.instance; - __gshared Quantizer instance; - } - - /** - Returns `roundingFunction(n)`. - */ - size_t goodAllocSize(size_t n) - { - auto result = roundingFunction(n); - assert(result >= n); - return result; - } - - /** - Alignment is identical to that of the parent. - */ - enum alignment = ParentAllocator.alignment; - - /** - Gets a larger buffer `buf` by calling - `parent.allocate(goodAllocSize(n))`. If `buf` is `null`, returns - `null`. Otherwise, returns $(D buf[0 .. n]). - */ - void[] allocate(size_t n) - { - auto result = parent.allocate(goodAllocSize(n)); - return result.ptr ? result.ptr[0 .. n] : null; - } - - static if (hasMember!(ParentAllocator, "allocateZeroed")) - package(std) void[] allocateZeroed()(size_t n) - { - auto result = parent.allocateZeroed(goodAllocSize(n)); - return result.ptr ? result.ptr[0 .. n] : null; - } - - /** - Defined only if `parent.alignedAllocate` exists and works similarly to - `allocate` by forwarding to - $(D parent.alignedAllocate(goodAllocSize(n), a)). - */ - static if (hasMember!(ParentAllocator, "alignedAllocate")) - void[] alignedAllocate(size_t n, uint a) - { - auto result = parent.alignedAllocate(goodAllocSize(n), a); - return result.ptr ? result.ptr[0 .. n] : null; - } - - /** - First checks whether there's enough slack memory preallocated for `b` - by evaluating $(D b.length + delta <= goodAllocSize(b.length)). If that's - the case, expands `b` in place. Otherwise, attempts to use - `parent.expand` appropriately if present. - */ - bool expand(ref void[] b, size_t delta) - { - if (!b || delta == 0) return delta == 0; - immutable allocated = goodAllocSize(b.length), - needed = b.length + delta, - neededAllocation = goodAllocSize(needed); - assert(b.length <= allocated); - assert(needed <= neededAllocation); - assert(allocated <= neededAllocation); - // Second test needed because expand must work for null pointers, too. - if (allocated == neededAllocation) - { - // Nice! - b = (() @trusted => b.ptr[0 .. needed])(); - return true; - } - // Hail Mary - static if (hasMember!(ParentAllocator, "expand")) - { - // Expand to the appropriate quantum - auto original = (() @trusted => b.ptr[0 .. allocated])(); - assert(goodAllocSize(needed) >= allocated); - if (!parent.expand(original, neededAllocation - allocated)) - return false; - // Dial back the size - b = (() @trusted => original.ptr[0 .. needed])(); - return true; - } - else - { - return false; - } - } - - /** - Expands or shrinks allocated block to an allocated size of $(D - goodAllocSize(s)). Expansion occurs in place under the conditions required - by `expand`. Shrinking occurs in place if $(D goodAllocSize(b.length) - == goodAllocSize(s)). - */ - bool reallocate(ref void[] b, size_t s) - { - if (!b.ptr) - { - b = allocate(s); - return b.length == s; - } - if (s >= b.length && expand(b, s - b.length)) return true; - immutable toAllocate = goodAllocSize(s), - allocated = goodAllocSize(b.length); - // Are the lengths within the same quantum? - if (allocated == toAllocate) - { - // Reallocation (whether up or down) will be done in place - b = b.ptr[0 .. s]; - return true; - } - // Defer to parent (or global) with quantized size - auto original = b.ptr[0 .. allocated]; - if (!parent.reallocate(original, toAllocate)) return false; - b = original.ptr[0 .. s]; - return true; - } - - /** - Defined only if `ParentAllocator.alignedAllocate` exists. Expansion - occurs in place under the conditions required by `expand`. Shrinking - occurs in place if $(D goodAllocSize(b.length) == goodAllocSize(s)). - */ - static if (hasMember!(ParentAllocator, "alignedAllocate")) - bool alignedReallocate(ref void[] b, size_t s, uint a) - { - if (!b.ptr) - { - b = alignedAllocate(s, a); - return b.length == s; - } - if (s >= b.length && b.ptr.alignedAt(a) && expand(b, s - b.length)) return true; - immutable toAllocate = goodAllocSize(s), - allocated = goodAllocSize(b.length); - // Are the lengths within the same quantum? - if (allocated == toAllocate && b.ptr.alignedAt(a)) - { - assert(b.ptr); // code above must have caught this - // Reallocation (whether up or down) will be done in place - b = b.ptr[0 .. s]; - return true; - } - // Defer to parent (or global) with quantized size - auto original = b.ptr[0 .. allocated]; - if (!parent.alignedReallocate(original, toAllocate, a)) return false; - b = original.ptr[0 .. s]; - return true; - } - - /** - Defined if `ParentAllocator.deallocate` exists and forwards to - $(D parent.deallocate(b.ptr[0 .. goodAllocSize(b.length)])). - */ - static if (hasMember!(ParentAllocator, "deallocate")) - bool deallocate(void[] b) - { - if (!b.ptr) return true; - return parent.deallocate(b.ptr[0 .. goodAllocSize(b.length)]); - } - - // Forwarding methods - mixin(forwardToMember("parent", - "allocateAll", "owns", "deallocateAll", "empty")); -} - -/// -@system unittest -{ - import std.experimental.allocator.building_blocks.free_tree : FreeTree; - import std.experimental.allocator.gc_allocator : GCAllocator; - - size_t roundUpToMultipleOf(size_t s, uint base) - { - auto rem = s % base; - return rem ? s + base - rem : s; - } - - // Quantize small allocations to a multiple of cache line, large ones to a - // multiple of page size - alias MyAlloc = Quantizer!( - FreeTree!GCAllocator, - n => roundUpToMultipleOf(n, n <= 16_384 ? 64 : 4096)); - MyAlloc alloc; - const buf = alloc.allocate(256); - assert(buf.ptr); -} - -version (StdUnittest) -@system unittest -{ - import std.experimental.allocator.gc_allocator : GCAllocator; - alias MyAlloc = Quantizer!(GCAllocator, - (size_t n) => n.roundUpToMultipleOf(64)); - testAllocator!(() => MyAlloc()); - - assert((() pure nothrow @safe @nogc => MyAlloc().goodAllocSize(1))() == 64); - - auto a = MyAlloc(); - auto b = a.allocate(42); - assert(b.length == 42); - // Inplace expand, since goodAllocSize is 64 - assert((() @safe => a.expand(b, 22))()); - //assert((() nothrow @safe => a.expand(b, 22))()); - assert(b.length == 64); - // Trigger parent.expand, which may or may not succed - //() nothrow @safe { a.expand(b, 1); }(); - () @safe { a.expand(b, 1); }(); - assert(a.reallocate(b, 100)); - assert(b.length == 100); - // Ensure deallocate inherits from parent - () nothrow @nogc { a.deallocate(b); }(); -} - -@system unittest -{ - import std.experimental.allocator.building_blocks.region : Region; - import std.experimental.allocator.mallocator : Mallocator; - import std.typecons : Ternary; - - alias Alloc = Quantizer!(Region!(Mallocator), - (size_t n) => n.roundUpToMultipleOf(64)); - auto a = Alloc(Region!Mallocator(1024 * 64)); - const b = a.allocate(42); - assert(b.length == 42); - // Check that owns inherits from parent, i.e. Region - assert((() pure nothrow @safe @nogc => a.owns(b))() == Ternary.yes); - assert((() pure nothrow @safe @nogc => a.owns(null))() == Ternary.no); - - auto c = a.allocate(42); - assert(c.length == 42); - assert((() pure nothrow @safe @nogc => a.owns(c))() == Ternary.yes); - // Inplace expand, since goodAllocSize is 64 - assert((() nothrow @safe => a.expand(c, 22))()); - assert(c.length == 64); - // Trigger parent.expand - assert((() nothrow @safe => a.expand(c, 1))()); - assert(c.length == 65); - // Check that reallocate inherits from parent - assert((() nothrow @nogc => a.reallocate(c, 100))()); - assert(c.length == 100); -} - -version (StdUnittest) -@system unittest -{ - import std.experimental.allocator.building_blocks.region : Region; - import std.experimental.allocator.mallocator : Mallocator; - - alias MyAlloc = Quantizer!(Region!(Mallocator), - (size_t n) => n.roundUpToMultipleOf(64)); - testAllocator!(() => MyAlloc(Region!Mallocator(1024 * 64))); - - auto a = MyAlloc(Region!Mallocator(1024 * 64)); - void[] b; - assert((() nothrow @nogc => a.alignedReallocate(b, 42, 16))()); - assert(b.length == 42); - assert(alignedAt(&b[0], 16)); -} - -version (StdUnittest) -@system unittest -{ - import std.experimental.allocator.building_blocks.region : BorrowedRegion; - import std.typecons : Ternary; - - alias MyAlloc = Quantizer!(BorrowedRegion!(), - (size_t n) => n.roundUpToMultipleOf(64)); - testAllocator!(() => MyAlloc(BorrowedRegion!()(new ubyte[1024 * 64]))); - - auto a = MyAlloc(BorrowedRegion!()(new ubyte[1024 * 64])); - // Check that empty inherits from parent - assert((() pure nothrow @safe @nogc => a.empty)() == Ternary.yes); - auto b = a.allocate(42); - assert(b.length == 42); - assert((() pure nothrow @safe @nogc => a.empty)() == Ternary.no); - // Check that deallocateAll inherits from parent - assert((() nothrow @nogc => a.deallocateAll())()); - assert((() pure nothrow @safe @nogc => a.empty)() == Ternary.yes); -} diff --git a/phobos/std/experimental/allocator/building_blocks/region.d b/phobos/std/experimental/allocator/building_blocks/region.d deleted file mode 100644 index a23746a..0000000 --- a/phobos/std/experimental/allocator/building_blocks/region.d +++ /dev/null @@ -1,1704 +0,0 @@ -// Written in the D programming language. -/** -Source: $(PHOBOSSRC std/experimental/allocator/building_blocks/region.d) -*/ -module std.experimental.allocator.building_blocks.region; - -import std.experimental.allocator.building_blocks.null_allocator; -import std.experimental.allocator.common; -import std.typecons : Flag, Yes, No; - -version (OSX) - version = Darwin; -else version (iOS) - version = Darwin; -else version (TVOS) - version = Darwin; -else version (WatchOS) - version = Darwin; - -/** -A `Region` allocator allocates memory straight from one contiguous chunk. -There is no deallocation, and once the region is full, allocation requests -return `null`. Therefore, `Region`s are often used (a) in conjunction with -more sophisticated allocators; or (b) for batch-style very fast allocations -that deallocate everything at once. - -The region only stores three pointers, corresponding to the current position in -the store and the limits. One allocation entails rounding up the allocation -size for alignment purposes, bumping the current pointer, and comparing it -against the limit. - -`Region` deallocates the chunk of memory during destruction. - -The `minAlign` parameter establishes alignment. If $(D minAlign > 1), the -sizes of all allocation requests are rounded up to a multiple of `minAlign`. -Applications aiming at maximum speed may want to choose $(D minAlign = 1) and -control alignment externally. - -*/ -struct Region(ParentAllocator, - uint minAlign = platformAlignment, - Flag!"growDownwards" growDownwards = No.growDownwards) -{ - static assert(minAlign.isGoodStaticAlignment); - static assert(ParentAllocator.alignment >= minAlign); - - import std.traits : hasMember; - import std.typecons : Ternary; - - // state - /** - The _parent allocator. Depending on whether `ParentAllocator` holds state - or not, this is a member variable or an alias for - `ParentAllocator.instance`. - */ - static if (stateSize!ParentAllocator) - { - ParentAllocator parent; - } - else - { - alias parent = ParentAllocator.instance; - } - - private BorrowedRegion!(minAlign, growDownwards) _impl; - - private void* roundedBegin() const pure nothrow @trusted @nogc - { - return _impl.roundedBegin; - } - - private void* roundedEnd() const pure nothrow @trusted @nogc - { - return _impl.roundedEnd; - } - /** - Constructs a region backed by a user-provided store. - Assumes the memory was allocated with `ParentAllocator`. - - Params: - store = User-provided store backing up the region. Assumed to have been - allocated with `ParentAllocator`. - n = Bytes to allocate using `ParentAllocator`. If `parent.allocate(n)` - returns `null`, the region will be initialized as empty (correctly - initialized but unable to allocate). - */ - this(ubyte[] store) pure nothrow @nogc - { - _impl = store; - } - - /// Ditto - static if (!stateSize!ParentAllocator) - this(size_t n) - { - this(cast(ubyte[]) (parent.allocate(n.roundUpToAlignment(alignment)))); - } - - /// Ditto - static if (stateSize!ParentAllocator) - this(ParentAllocator parent, size_t n) - { - this.parent = parent; - this(cast(ubyte[]) (parent.allocate(n.roundUpToAlignment(alignment)))); - } - - /* - TODO: The postblit of `BasicRegion` should be disabled because such objects - should not be copied around naively. - */ - - /** - If `ParentAllocator` defines `deallocate`, the region defines a destructor - that uses `ParentAllocator.deallocate` to free the memory chunk. - */ - static if (hasMember!(ParentAllocator, "deallocate")) - ~this() - { - with (_impl) parent.deallocate(_begin[0 .. _end - _begin]); - } - - /** - Rounds the given size to a multiple of the `alignment` - */ - size_t goodAllocSize(size_t n) const pure nothrow @safe @nogc - { - return _impl.goodAllocSize(n); - } - - /** - Alignment offered. - */ - alias alignment = minAlign; - - /** - Allocates `n` bytes of memory. The shortest path involves an alignment - adjustment (if $(D alignment > 1)), an increment, and a comparison. - - Params: - n = number of bytes to allocate - - Returns: - A properly-aligned buffer of size `n` or `null` if request could not - be satisfied. - */ - void[] allocate(size_t n) pure nothrow @trusted @nogc - { - return _impl.allocate(n); - } - - /** - Allocates `n` bytes of memory aligned at alignment `a`. - - Params: - n = number of bytes to allocate - a = alignment for the allocated block - - Returns: - Either a suitable block of `n` bytes aligned at `a`, or `null`. - */ - void[] alignedAllocate(size_t n, uint a) pure nothrow @trusted @nogc - { - return _impl.alignedAllocate(n, a); - } - - /// Allocates and returns all memory available to this region. - void[] allocateAll() pure nothrow @trusted @nogc - { - return _impl.allocateAll; - } - - /** - Expands an allocated block in place. Expansion will succeed only if the - block is the last allocated. Defined only if `growDownwards` is - `No.growDownwards`. - */ - static if (growDownwards == No.growDownwards) - bool expand(ref void[] b, size_t delta) pure nothrow @safe @nogc - { - return _impl.expand(b, delta); - } - - /** - Deallocates `b`. This works only if `b` was obtained as the last call - to `allocate`; otherwise (i.e. another allocation has occurred since) it - does nothing. - - Params: - b = Block previously obtained by a call to `allocate` against this - allocator (`null` is allowed). - */ - bool deallocate(void[] b) pure nothrow @nogc - { - return _impl.deallocate(b); - } - - /** - Deallocates all memory allocated by this region, which can be subsequently - reused for new allocations. - */ - bool deallocateAll() pure nothrow @nogc - { - return _impl.deallocateAll; - } - - /** - Queries whether `b` has been allocated with this region. - - Params: - b = Arbitrary block of memory (`null` is allowed; `owns(null)` returns - `false`). - - Returns: - `true` if `b` has been allocated with this region, `false` otherwise. - */ - Ternary owns(const void[] b) const pure nothrow @trusted @nogc - { - return _impl.owns(b); - } - - /** - Returns `Ternary.yes` if no memory has been allocated in this region, - `Ternary.no` otherwise. (Never returns `Ternary.unknown`.) - */ - Ternary empty() const pure nothrow @safe @nogc - { - return _impl.empty; - } - - /// Nonstandard property that returns bytes available for allocation. - size_t available() const @safe pure nothrow @nogc - { - return _impl.available; - } -} - -/// -@system nothrow unittest -{ - import std.algorithm.comparison : max; - import std.experimental.allocator.building_blocks.allocator_list - : AllocatorList; - import std.experimental.allocator.mallocator : Mallocator; - import std.typecons : Ternary; - // Create a scalable list of regions. Each gets at least 1MB at a time by - // using malloc. - auto batchAllocator = AllocatorList!( - (size_t n) => Region!Mallocator(max(n, 1024 * 1024)) - )(); - assert(batchAllocator.empty == Ternary.yes); - auto b = batchAllocator.allocate(101); - assert(b.length == 101); - assert(batchAllocator.empty == Ternary.no); - // This will cause a second allocation - b = batchAllocator.allocate(2 * 1024 * 1024); - assert(b.length == 2 * 1024 * 1024); - // Destructor will free the memory -} - -@system nothrow @nogc unittest -{ - import std.experimental.allocator.mallocator : Mallocator; - import std.typecons : Ternary; - - static void testAlloc(Allocator)(ref Allocator a) - { - assert((() pure nothrow @safe @nogc => a.empty)() == Ternary.yes); - const b = a.allocate(101); - assert(b.length == 101); - assert((() nothrow @safe @nogc => a.owns(b))() == Ternary.yes); - - // Ensure deallocate inherits from parent allocators - auto c = a.allocate(42); - assert(c.length == 42); - assert((() nothrow @nogc => a.deallocate(c))()); - assert((() pure nothrow @safe @nogc => a.empty)() == Ternary.no); - } - - // Create a 64 KB region allocated with malloc - auto reg = Region!(Mallocator, Mallocator.alignment, - Yes.growDownwards)(1024 * 64); - testAlloc(reg); - - // Create a 64 KB shared region allocated with malloc - auto sharedReg = SharedRegion!(Mallocator, Mallocator.alignment, - Yes.growDownwards)(1024 * 64); - testAlloc(sharedReg); -} - -@system nothrow @nogc unittest -{ - // test 'this(ubyte[] store)' constructed regions properly clean up - // their inner storage after destruction - import std.experimental.allocator.mallocator : Mallocator; - - static shared struct LocalAllocator - { - nothrow @nogc: - enum alignment = Mallocator.alignment; - void[] buf; - bool deallocate(void[] b) - { - assert(buf.ptr == b.ptr && buf.length == b.length); - return true; - } - - void[] allocate(size_t n) - { - return null; - } - - } - - enum bufLen = 10 * Mallocator.alignment; - void[] tmp = Mallocator.instance.allocate(bufLen); - - LocalAllocator a; - a.buf = cast(typeof(a.buf)) tmp[1 .. $]; - - auto reg = Region!(LocalAllocator, Mallocator.alignment, - Yes.growDownwards)(cast(ubyte[]) a.buf); - auto sharedReg = SharedRegion!(LocalAllocator, Mallocator.alignment, - Yes.growDownwards)(cast(ubyte[]) a.buf); - reg.parent = a; - sharedReg.parent = a; - - Mallocator.instance.deallocate(tmp); -} - -version (StdUnittest) -@system unittest -{ - import std.experimental.allocator.mallocator : Mallocator; - - testAllocator!(() => Region!(Mallocator)(1024 * 64)); - testAllocator!(() => Region!(Mallocator, Mallocator.alignment, Yes.growDownwards)(1024 * 64)); - - testAllocator!(() => SharedRegion!(Mallocator)(1024 * 64)); - testAllocator!(() => SharedRegion!(Mallocator, Mallocator.alignment, Yes.growDownwards)(1024 * 64)); -} - -@system nothrow @nogc unittest -{ - import std.experimental.allocator.mallocator : Mallocator; - - auto reg = Region!(Mallocator)(1024 * 64); - auto b = reg.allocate(101); - assert(b.length == 101); - assert((() pure nothrow @safe @nogc => reg.expand(b, 20))()); - assert((() pure nothrow @safe @nogc => reg.expand(b, 73))()); - assert((() pure nothrow @safe @nogc => !reg.expand(b, 1024 * 64))()); - assert((() nothrow @nogc => reg.deallocateAll())()); -} - -/** -A `BorrowedRegion` allocates directly from a user-provided block of memory. - -Unlike a `Region`, a `BorrowedRegion` does not own the memory it allocates from -and will not deallocate that memory upon destruction. Instead, it is the user's -responsibility to ensure that the memory is properly disposed of. - -In all other respects, a `BorrowedRegion` behaves exactly like a `Region`. -*/ -struct BorrowedRegion(uint minAlign = platformAlignment, - Flag!"growDownwards" growDownwards = No.growDownwards) -{ - static assert(minAlign.isGoodStaticAlignment); - - import std.typecons : Ternary; - - // state - private void* _current, _begin, _end; - - private void* roundedBegin() const pure nothrow @trusted @nogc - { - return cast(void*) roundUpToAlignment(cast(size_t) _begin, alignment); - } - - private void* roundedEnd() const pure nothrow @trusted @nogc - { - return cast(void*) roundDownToAlignment(cast(size_t) _end, alignment); - } - - /** - Constructs a region backed by a user-provided store. - - Params: - store = User-provided store backing up the region. - */ - this(ubyte[] store) pure nothrow @nogc - { - _begin = store.ptr; - _end = store.ptr + store.length; - static if (growDownwards) - _current = roundedEnd(); - else - _current = roundedBegin(); - } - - /* - TODO: The postblit of `BorrowedRegion` should be disabled because such objects - should not be copied around naively. - */ - - /** - Rounds the given size to a multiple of the `alignment` - */ - size_t goodAllocSize(size_t n) const pure nothrow @safe @nogc - { - return n.roundUpToAlignment(alignment); - } - - /** - Alignment offered. - */ - alias alignment = minAlign; - - /** - Allocates `n` bytes of memory. The shortest path involves an alignment - adjustment (if $(D alignment > 1)), an increment, and a comparison. - - Params: - n = number of bytes to allocate - - Returns: - A properly-aligned buffer of size `n` or `null` if request could not - be satisfied. - */ - void[] allocate(size_t n) pure nothrow @trusted @nogc - { - const rounded = goodAllocSize(n); - if (n == 0 || rounded < n || available < rounded) return null; - - static if (growDownwards) - { - assert(available >= rounded); - auto result = (_current - rounded)[0 .. n]; - assert(result.ptr >= _begin); - _current = result.ptr; - assert(owns(result) == Ternary.yes); - } - else - { - auto result = _current[0 .. n]; - _current += rounded; - } - - return result; - } - - /** - Allocates `n` bytes of memory aligned at alignment `a`. - - Params: - n = number of bytes to allocate - a = alignment for the allocated block - - Returns: - Either a suitable block of `n` bytes aligned at `a`, or `null`. - */ - void[] alignedAllocate(size_t n, uint a) pure nothrow @trusted @nogc - { - import std.math.traits : isPowerOf2; - assert(a.isPowerOf2); - - const rounded = goodAllocSize(n); - if (n == 0 || rounded < n || available < rounded) return null; - - static if (growDownwards) - { - auto tmpCurrent = _current - rounded; - auto result = tmpCurrent.alignDownTo(a); - if (result <= tmpCurrent && result >= _begin) - { - _current = result; - return cast(void[]) result[0 .. n]; - } - } - else - { - // Just bump the pointer to the next good allocation - auto newCurrent = _current.alignUpTo(a); - if (newCurrent < _current || newCurrent > _end) - return null; - - auto save = _current; - _current = newCurrent; - auto result = allocate(n); - if (result.ptr) - { - assert(result.length == n); - return result; - } - // Failed, rollback - _current = save; - } - return null; - } - - /// Allocates and returns all memory available to this region. - void[] allocateAll() pure nothrow @trusted @nogc - { - static if (growDownwards) - { - auto result = _begin[0 .. available]; - _current = _begin; - } - else - { - auto result = _current[0 .. available]; - _current = _end; - } - return result; - } - - /** - Expands an allocated block in place. Expansion will succeed only if the - block is the last allocated. Defined only if `growDownwards` is - `No.growDownwards`. - */ - static if (growDownwards == No.growDownwards) - bool expand(ref void[] b, size_t delta) pure nothrow @safe @nogc - { - assert(owns(b) == Ternary.yes || b is null); - assert((() @trusted => b.ptr + b.length <= _current)() || b is null); - if (b is null || delta == 0) return delta == 0; - auto newLength = b.length + delta; - if ((() @trusted => _current < b.ptr + b.length + alignment)()) - { - immutable currentGoodSize = this.goodAllocSize(b.length); - immutable newGoodSize = this.goodAllocSize(newLength); - immutable goodDelta = newGoodSize - currentGoodSize; - // This was the last allocation! Allocate some more and we're done. - if (goodDelta == 0 - || (() @trusted => allocate(goodDelta).length == goodDelta)()) - { - b = (() @trusted => b.ptr[0 .. newLength])(); - assert((() @trusted => _current < b.ptr + b.length + alignment)()); - return true; - } - } - return false; - } - - /** - Deallocates `b`. This works only if `b` was obtained as the last call - to `allocate`; otherwise (i.e. another allocation has occurred since) it - does nothing. - - Params: - b = Block previously obtained by a call to `allocate` against this - allocator (`null` is allowed). - */ - bool deallocate(void[] b) pure nothrow @nogc - { - assert(owns(b) == Ternary.yes || b.ptr is null); - auto rounded = goodAllocSize(b.length); - static if (growDownwards) - { - if (b.ptr == _current) - { - _current += rounded; - return true; - } - } - else - { - if (b.ptr + rounded == _current) - { - assert(b.ptr !is null || _current is null); - _current = b.ptr; - return true; - } - } - return false; - } - - /** - Deallocates all memory allocated by this region, which can be subsequently - reused for new allocations. - */ - bool deallocateAll() pure nothrow @nogc - { - static if (growDownwards) - { - _current = roundedEnd(); - } - else - { - _current = roundedBegin(); - } - return true; - } - - /** - Queries whether `b` has been allocated with this region. - - Params: - b = Arbitrary block of memory (`null` is allowed; `owns(null)` returns - `false`). - - Returns: - `true` if `b` has been allocated with this region, `false` otherwise. - */ - Ternary owns(const void[] b) const pure nothrow @trusted @nogc - { - return Ternary(b && (&b[0] >= _begin) && (&b[0] + b.length <= _end)); - } - - /** - Returns `Ternary.yes` if no memory has been allocated in this region, - `Ternary.no` otherwise. (Never returns `Ternary.unknown`.) - */ - Ternary empty() const pure nothrow @safe @nogc - { - static if (growDownwards) - return Ternary(_current == roundedEnd()); - else - return Ternary(_current == roundedBegin()); - } - - /// Nonstandard property that returns bytes available for allocation. - size_t available() const @safe pure nothrow @nogc - { - static if (growDownwards) - { - return _current - _begin; - } - else - { - return _end - _current; - } - } -} - -/// -@system nothrow @nogc unittest -{ - import std.typecons : Ternary; - - ubyte[1024] store; - auto myRegion = BorrowedRegion!(1)(store[]); - - assert(myRegion.empty == Ternary.yes); - assert(myRegion.available == store.length); - - void[] b = myRegion.allocate(101); - - assert(b.length == 101); - assert(myRegion.empty == Ternary.no); - assert(myRegion.owns(b) == Ternary.yes); - assert(myRegion.available == store.length - b.length); - - void[] b2 = myRegion.allocate(256); - - // Can only free the most recent allocation - assert(myRegion.deallocate(b) == false); - assert(myRegion.deallocate(b2) == true); - - myRegion.deallocateAll(); - - assert(myRegion.empty == Ternary.yes); -} - -@system nothrow @nogc unittest -{ - import std.experimental.allocator.mallocator : AlignedMallocator; - import std.typecons : Ternary; - - ubyte[] buf = cast(ubyte[]) AlignedMallocator.instance.alignedAllocate(64, 64); - auto reg = BorrowedRegion!(64, Yes.growDownwards)(buf); - assert(reg.alignedAllocate(10, 32).length == 10); - assert(!reg.available); -} - -/** - -`InSituRegion` is a convenient region that carries its storage within itself -(in the form of a statically-sized array). - -The first template argument is the size of the region and the second is the -needed alignment. Depending on the alignment requested and platform details, -the actual available storage may be smaller than the compile-time parameter. To -make sure that at least `n` bytes are available in the region, use -$(D InSituRegion!(n + a - 1, a)). - -Given that the most frequent use of `InSituRegion` is as a stack allocator, it -allocates starting at the end on systems where stack grows downwards, such that -hot memory is used first. - -*/ -struct InSituRegion(size_t size, size_t minAlign = platformAlignment) -{ - import std.algorithm.comparison : max; - import std.conv : to; - import std.traits : hasMember; - import std.typecons : Ternary; - import core.thread.types : isStackGrowingDown; - - static assert(minAlign.isGoodStaticAlignment); - static assert(size >= minAlign); - - static if (isStackGrowingDown) - enum growDownwards = Yes.growDownwards; - else - enum growDownwards = No.growDownwards; - - @disable this(this); - - // state { - private BorrowedRegion!(minAlign, growDownwards) _impl; - union - { - private ubyte[size] _store = void; - private double _forAlignmentOnly1; - } - // } - - /** - An alias for `minAlign`, which must be a valid alignment (nonzero power - of 2). The start of the region and all allocation requests will be rounded - up to a multiple of the alignment. - - ---- - InSituRegion!(4096) a1; - assert(a1.alignment == platformAlignment); - InSituRegion!(4096, 64) a2; - assert(a2.alignment == 64); - ---- - */ - alias alignment = minAlign; - - private void lazyInit() - { - assert(!_impl._current); - _impl = typeof(_impl)(_store); - assert(_impl._current.alignedAt(alignment)); - } - - /** - Allocates `bytes` and returns them, or `null` if the region cannot - accommodate the request. For efficiency reasons, if $(D bytes == 0) the - function returns an empty non-null slice. - */ - void[] allocate(size_t n) - { - // Fast path - entry: - auto result = _impl.allocate(n); - if (result.length == n) return result; - // Slow path - if (_impl._current) return null; // no more room - lazyInit; - assert(_impl._current); - goto entry; - } - - /** - As above, but the memory allocated is aligned at `a` bytes. - */ - void[] alignedAllocate(size_t n, uint a) - { - // Fast path - entry: - auto result = _impl.alignedAllocate(n, a); - if (result.length == n) return result; - // Slow path - if (_impl._current) return null; // no more room - lazyInit; - assert(_impl._current); - goto entry; - } - - /** - Deallocates `b`. This works only if `b` was obtained as the last call - to `allocate`; otherwise (i.e. another allocation has occurred since) it - does nothing. This semantics is tricky and therefore `deallocate` is - defined only if `Region` is instantiated with `Yes.defineDeallocate` - as the third template argument. - - Params: - b = Block previously obtained by a call to `allocate` against this - allocator (`null` is allowed). - */ - bool deallocate(void[] b) - { - if (!_impl._current) return b is null; - return _impl.deallocate(b); - } - - /** - Returns `Ternary.yes` if `b` is the result of a previous allocation, - `Ternary.no` otherwise. - */ - Ternary owns(const void[] b) pure nothrow @safe @nogc - { - if (!_impl._current) return Ternary.no; - return _impl.owns(b); - } - - /** - Expands an allocated block in place. Expansion will succeed only if the - block is the last allocated. - */ - static if (hasMember!(typeof(_impl), "expand")) - bool expand(ref void[] b, size_t delta) - { - if (!_impl._current) lazyInit; - return _impl.expand(b, delta); - } - - /** - Deallocates all memory allocated with this allocator. - */ - bool deallocateAll() - { - // We don't care to lazily init the region - return _impl.deallocateAll; - } - - /** - Allocates all memory available with this allocator. - */ - void[] allocateAll() - { - if (!_impl._current) lazyInit; - return _impl.allocateAll; - } - - /** - Nonstandard function that returns the bytes available for allocation. - */ - size_t available() - { - if (!_impl._current) lazyInit; - return _impl.available; - } -} - -/// -@system unittest -{ - // 128KB region, allocated to x86's cache line - InSituRegion!(128 * 1024, 16) r1; - auto a1 = r1.allocate(101); - assert(a1.length == 101); - - // 128KB region, with fallback to the garbage collector. - import std.experimental.allocator.building_blocks.fallback_allocator - : FallbackAllocator; - import std.experimental.allocator.building_blocks.free_list - : FreeList; - import std.experimental.allocator.building_blocks.bitmapped_block - : BitmappedBlock; - import std.experimental.allocator.gc_allocator : GCAllocator; - FallbackAllocator!(InSituRegion!(128 * 1024), GCAllocator) r2; - const a2 = r2.allocate(102); - assert(a2.length == 102); - - // Reap with GC fallback. - InSituRegion!(128 * 1024, 8) tmp3; - FallbackAllocator!(BitmappedBlock!(64, 8), GCAllocator) r3; - r3.primary = BitmappedBlock!(64, 8)(cast(ubyte[]) (tmp3.allocateAll())); - const a3 = r3.allocate(103); - assert(a3.length == 103); - - // Reap/GC with a freelist for small objects up to 16 bytes. - InSituRegion!(128 * 1024, 64) tmp4; - FreeList!(FallbackAllocator!(BitmappedBlock!(64, 64), GCAllocator), 0, 16) r4; - r4.parent.primary = BitmappedBlock!(64, 64)(cast(ubyte[]) (tmp4.allocateAll())); - const a4 = r4.allocate(104); - assert(a4.length == 104); -} - -@system pure nothrow unittest -{ - import std.typecons : Ternary; - - InSituRegion!(4096, 1) r1; - auto a = r1.allocate(2001); - assert(a.length == 2001); - import std.conv : text; - assert(r1.available == 2095, text(r1.available)); - // Ensure deallocate inherits from parent - assert((() nothrow @nogc => r1.deallocate(a))()); - assert((() nothrow @nogc => r1.deallocateAll())()); - - InSituRegion!(65_536, 1024*4) r2; - assert(r2.available <= 65_536); - a = r2.allocate(2001); - assert(a.length == 2001); - const void[] buff = r2.allocate(42); - assert((() nothrow @safe @nogc => r2.owns(buff))() == Ternary.yes); - assert((() nothrow @nogc => r2.deallocateAll())()); -} - -version (CRuntime_Musl) -{ - // sbrk and brk are disabled in Musl: - // https://git.musl-libc.org/cgit/musl/commit/?id=7a995fe706e519a4f55399776ef0df9596101f93 - // https://git.musl-libc.org/cgit/musl/commit/?id=863d628d93ea341b6a32661a1654320ce69f6a07 -} -version (DragonFlyBSD) -{ - // sbrk is deprecated in favor of mmap (we could implement a mmap + MAP_NORESERVE + PROT_NONE version) - // brk has been removed - // https://www.dragonflydigest.com/2019/02/22/22586.html - // http://gitweb.dragonflybsd.org/dragonfly.git/commitdiff/dc676eaefa61b0f47bbea1c53eab86fd5ccd78c6 - // http://gitweb.dragonflybsd.org/dragonfly.git/commitdiff/4b5665564ef37dc939a3a9ffbafaab9894c18885 - // http://gitweb.dragonflybsd.org/dragonfly.git/commitdiff/8618d94a0e2ff8303ad93c123a3fa598c26a116e -} -else -{ - private extern(C) void* sbrk(long) nothrow @nogc; - private extern(C) int brk(shared void*) nothrow @nogc; -} - -/** - -Allocator backed by $(D $(LINK2 https://en.wikipedia.org/wiki/Sbrk, sbrk)) -for Posix systems. Due to the fact that `sbrk` is not thread-safe -$(HTTP lifecs.likai.org/2010/02/sbrk-is-not-thread-safe.html, by design), -`SbrkRegion` uses a mutex internally. This implies -that uncontrolled calls to `brk` and `sbrk` may affect the workings of $(D -SbrkRegion) adversely. - -*/ -version (CRuntime_Musl) {} else -version (DragonFlyBSD) {} else -version (Posix) struct SbrkRegion(uint minAlign = platformAlignment) -{ - import core.sys.posix.pthread : pthread_mutex_init, pthread_mutex_destroy, - pthread_mutex_t, pthread_mutex_lock, pthread_mutex_unlock, - - PTHREAD_MUTEX_INITIALIZER; - private static shared pthread_mutex_t sbrkMutex = PTHREAD_MUTEX_INITIALIZER; - import std.typecons : Ternary; - - static assert(minAlign.isGoodStaticAlignment); - static assert(size_t.sizeof == (void*).sizeof); - private shared void* _brkInitial, _brkCurrent; - - /** - Instance shared by all callers. - */ - static shared SbrkRegion instance; - - /** - Standard allocator primitives. - */ - enum uint alignment = minAlign; - - /** - Rounds the given size to a multiple of thew `alignment` - */ - size_t goodAllocSize(size_t n) shared const pure nothrow @safe @nogc - { - return n.roundUpToMultipleOf(alignment); - } - - /// Ditto - void[] allocate(size_t bytes) shared @trusted nothrow @nogc - { - // Take alignment rounding into account - const rounded = goodAllocSize(bytes); - - pthread_mutex_lock(cast(pthread_mutex_t*) &sbrkMutex) == 0 || assert(0); - scope(exit) pthread_mutex_unlock(cast(pthread_mutex_t*) &sbrkMutex) == 0 - || assert(0); - // Assume sbrk returns the old break. Most online documentation confirms - // that, except for http://www.inf.udec.cl/~leo/Malloc_tutorial.pdf, - // which claims the returned value is not portable. - auto p = sbrk(rounded); - if (p == cast(void*) -1) - { - return null; - } - if (!_brkInitial) - { - _brkInitial = cast(shared) p; - assert(cast(size_t) _brkInitial % minAlign == 0, - "Too large alignment chosen for " ~ typeof(this).stringof); - } - _brkCurrent = cast(shared) (p + rounded); - return p[0 .. bytes]; - } - - /// Ditto - void[] alignedAllocate(size_t bytes, uint a) shared @trusted nothrow @nogc - { - pthread_mutex_lock(cast(pthread_mutex_t*) &sbrkMutex) == 0 || assert(0); - scope(exit) pthread_mutex_unlock(cast(pthread_mutex_t*) &sbrkMutex) == 0 - || assert(0); - if (!_brkInitial) - { - // This is one extra call, but it'll happen only once. - _brkInitial = cast(shared) sbrk(0); - assert(cast(size_t) _brkInitial % minAlign == 0, - "Too large alignment chosen for " ~ typeof(this).stringof); - (_brkInitial != cast(void*) -1) || assert(0); - _brkCurrent = _brkInitial; - } - immutable size_t delta = cast(shared void*) roundUpToMultipleOf( - cast(size_t) _brkCurrent, a) - _brkCurrent; - // Still must make sure the total size is aligned to the allocator's - // alignment. - immutable rounded = (bytes + delta).roundUpToMultipleOf(alignment); - - auto p = sbrk(rounded); - if (p == cast(void*) -1) - { - return null; - } - _brkCurrent = cast(shared) (p + rounded); - return p[delta .. delta + bytes]; - } - - /** - - The `expand` method may only succeed if the argument is the last block - allocated. In that case, `expand` attempts to push the break pointer to - the right. - - */ - bool expand(ref void[] b, size_t delta) shared nothrow @trusted @nogc - { - if (b is null || delta == 0) return delta == 0; - assert(_brkInitial && _brkCurrent); // otherwise where did b come from? - pthread_mutex_lock(cast(pthread_mutex_t*) &sbrkMutex) == 0 || assert(0); - scope(exit) pthread_mutex_unlock(cast(pthread_mutex_t*) &sbrkMutex) == 0 - || assert(0); - - // Take alignment rounding into account - const rounded = goodAllocSize(b.length); - - const slack = rounded - b.length; - if (delta <= slack) - { - b = b.ptr[0 .. b.length + delta]; - return true; - } - - if (_brkCurrent != b.ptr + rounded) return false; - // Great, can expand the last block - delta -= slack; - - const roundedDelta = goodAllocSize(delta); - auto p = sbrk(roundedDelta); - if (p == cast(void*) -1) - { - return false; - } - _brkCurrent = cast(shared) (p + roundedDelta); - b = b.ptr[0 .. b.length + slack + delta]; - return true; - } - - /// Ditto - Ternary owns(const void[] b) shared pure nothrow @trusted @nogc - { - // No need to lock here. - assert(!_brkCurrent || !b || &b[0] + b.length <= _brkCurrent); - return Ternary(_brkInitial && b && (&b[0] >= _brkInitial)); - } - - /** - - The `deallocate` method only works (and returns `true`) on systems - that support reducing the break address (i.e. accept calls to `sbrk` - with negative offsets). OSX does not accept such. In addition the argument - must be the last block allocated. - - */ - bool deallocate(void[] b) shared nothrow @nogc - { - // Take alignment rounding into account - const rounded = goodAllocSize(b.length); - pthread_mutex_lock(cast(pthread_mutex_t*) &sbrkMutex) == 0 || assert(0); - scope(exit) pthread_mutex_unlock(cast(pthread_mutex_t*) &sbrkMutex) == 0 - || assert(0); - if (_brkCurrent != b.ptr + rounded) return false; - assert(b.ptr >= _brkInitial); - if (sbrk(-rounded) == cast(void*) -1) - return false; - _brkCurrent = cast(shared) b.ptr; - return true; - } - - /** - The `deallocateAll` method only works (and returns `true`) on systems - that support reducing the break address (i.e. accept calls to `sbrk` - with negative offsets). OSX does not accept such. - */ - nothrow @nogc - bool deallocateAll() shared - { - pthread_mutex_lock(cast(pthread_mutex_t*) &sbrkMutex) == 0 || assert(0); - scope(exit) pthread_mutex_unlock(cast(pthread_mutex_t*) &sbrkMutex) == 0 - || assert(0); - return !_brkInitial || brk(_brkInitial) == 0; - } - - /// Standard allocator API. - Ternary empty() shared pure nothrow @safe @nogc - { - // Also works when they're both null. - return Ternary(_brkCurrent == _brkInitial); - } -} - -version (CRuntime_Musl) {} else -version (DragonFlyBSD) {} else -version (Posix) @system nothrow @nogc unittest -{ - // Let's test the assumption that sbrk(n) returns the old address - const p1 = sbrk(0); - const p2 = sbrk(4096); - assert(p1 == p2); - const p3 = sbrk(0); - assert(p3 == p2 + 4096); - // Try to reset brk, but don't make a fuss if it doesn't work - sbrk(-4096); -} - -version (CRuntime_Musl) {} else -version (DragonFlyBSD) {} else -version (Posix) @system nothrow @nogc unittest -{ - import std.typecons : Ternary; - import std.algorithm.comparison : min; - alias alloc = SbrkRegion!(min(8, platformAlignment)).instance; - assert((() nothrow @safe @nogc => alloc.empty)() == Ternary.yes); - auto a = alloc.alignedAllocate(2001, 4096); - assert(a.length == 2001); - assert((() nothrow @safe @nogc => alloc.empty)() == Ternary.no); - auto oldBrkCurr = alloc._brkCurrent; - auto b = alloc.allocate(2001); - assert(b.length == 2001); - assert((() nothrow @safe @nogc => alloc.expand(b, 0))()); - assert(b.length == 2001); - // Expand with a small size to fit the rounded slack due to alignment - assert((() nothrow @safe @nogc => alloc.expand(b, 1))()); - assert(b.length == 2002); - // Exceed the rounded slack due to alignment - assert((() nothrow @safe @nogc => alloc.expand(b, 10))()); - assert(b.length == 2012); - assert((() nothrow @safe @nogc => alloc.owns(a))() == Ternary.yes); - assert((() nothrow @safe @nogc => alloc.owns(b))() == Ternary.yes); - // reducing the brk does not work on OSX - version (Darwin) {} else - { - assert((() nothrow @nogc => alloc.deallocate(b))()); - // Check that expand and deallocate work well - assert(oldBrkCurr == alloc._brkCurrent); - assert((() nothrow @nogc => alloc.deallocate(a))()); - assert((() nothrow @nogc => alloc.deallocateAll())()); - } - const void[] c = alloc.allocate(2001); - assert(c.length == 2001); - assert((() nothrow @safe @nogc => alloc.owns(c))() == Ternary.yes); - assert((() nothrow @safe @nogc => alloc.owns(null))() == Ternary.no); -} - -/** -The threadsafe version of the `Region` allocator. -Allocations and deallocations are lock-free based using $(REF cas, core,atomic). -*/ -shared struct SharedRegion(ParentAllocator, - uint minAlign = platformAlignment, - Flag!"growDownwards" growDownwards = No.growDownwards) -{ - static assert(minAlign.isGoodStaticAlignment); - static assert(ParentAllocator.alignment >= minAlign); - - import std.traits : hasMember; - import std.typecons : Ternary; - - // state - /** - The _parent allocator. Depending on whether `ParentAllocator` holds state - or not, this is a member variable or an alias for - `ParentAllocator.instance`. - */ - static if (stateSize!ParentAllocator) - { - ParentAllocator parent; - } - else - { - alias parent = ParentAllocator.instance; - } - private shared SharedBorrowedRegion!(minAlign, growDownwards) _impl; - - private void* roundedBegin() const pure nothrow @trusted @nogc - { - return _impl.roundedBegin; - } - - private void* roundedEnd() const pure nothrow @trusted @nogc - { - return _impl.roundedEnd; - } - - - /** - Constructs a region backed by a user-provided store. - Assumes the memory was allocated with `ParentAllocator`. - - Params: - store = User-provided store backing up the region. Assumed to have been - allocated with `ParentAllocator`. - n = Bytes to allocate using `ParentAllocator`. If `parent.allocate(n)` - returns `null`, the region will be initialized as empty (correctly - initialized but unable to allocate). - */ - this(ubyte[] store) pure nothrow @nogc - { - _impl = store; - } - - /// Ditto - this(size_t n) - { - this(cast(ubyte[]) (parent.allocate(n.roundUpToAlignment(alignment)))); - } - - /** - Rounds the given size to a multiple of the `alignment` - */ - size_t goodAllocSize(size_t n) const pure nothrow @safe @nogc - { - return _impl.goodAllocSize(n); - } - - /** - Alignment offered. - */ - alias alignment = minAlign; - - /** - Allocates `n` bytes of memory. The allocation is served by atomically incrementing - a pointer which keeps track of the current used space. - - Params: - n = number of bytes to allocate - - Returns: - A properly-aligned buffer of size `n`, or `null` if request could not - be satisfied. - */ - void[] allocate(size_t n) pure nothrow @trusted @nogc - { - return _impl.allocate(n); - } - - /** - Deallocates `b`. This works only if `b` was obtained as the last call - to `allocate`; otherwise (i.e. another allocation has occurred since) it - does nothing. - - Params: - b = Block previously obtained by a call to `allocate` against this - allocator (`null` is allowed). - */ - bool deallocate(void[] b) pure nothrow @nogc - { - return _impl.deallocate(b); - } - - /** - Deallocates all memory allocated by this region, which can be subsequently - reused for new allocations. - */ - bool deallocateAll() pure nothrow @nogc - { - return _impl.deallocateAll; - } - - /** - Allocates `n` bytes of memory aligned at alignment `a`. - Params: - n = number of bytes to allocate - a = alignment for the allocated block - - Returns: - Either a suitable block of `n` bytes aligned at `a`, or `null`. - */ - void[] alignedAllocate(size_t n, uint a) pure nothrow @trusted @nogc - { - return _impl.alignedAllocate(n, a); - } - - /** - Queries whether `b` has been allocated with this region. - - Params: - b = Arbitrary block of memory (`null` is allowed; `owns(null)` returns - `false`). - - Returns: - `true` if `b` has been allocated with this region, `false` otherwise. - */ - Ternary owns(const void[] b) const pure nothrow @trusted @nogc - { - return _impl.owns(b); - } - - /** - Returns `Ternary.yes` if no memory has been allocated in this region, - `Ternary.no` otherwise. (Never returns `Ternary.unknown`.) - */ - Ternary empty() const pure nothrow @safe @nogc - { - return _impl.empty; - } - - /** - If `ParentAllocator` defines `deallocate`, the region defines a destructor - that uses `ParentAllocator.deallocate` to free the memory chunk. - */ - static if (hasMember!(ParentAllocator, "deallocate")) - ~this() - { - with (_impl) parent.deallocate(cast(void[]) _begin[0 .. _end - _begin]); - } -} - -@system unittest -{ - import std.experimental.allocator.mallocator : Mallocator; - - static void testAlloc(Allocator)(ref Allocator a, bool growDownwards) - { - import core.thread : ThreadGroup; - import std.algorithm.sorting : sort; - import core.internal.spinlock : SpinLock; - - SpinLock lock = SpinLock(SpinLock.Contention.brief); - enum numThreads = 100; - void[][numThreads] buf; - size_t count = 0; - - void fun() - { - void[] b = a.allocate(63); - assert(b.length == 63); - - lock.lock(); - buf[count] = b; - count++; - lock.unlock(); - } - - auto tg = new ThreadGroup; - foreach (i; 0 .. numThreads) - { - tg.create(&fun); - } - tg.joinAll(); - - sort!((a, b) => a.ptr < b.ptr)(buf[0 .. numThreads]); - foreach (i; 0 .. numThreads - 1) - { - assert(buf[i].ptr + a.goodAllocSize(buf[i].length) == buf[i + 1].ptr); - } - - assert(!a.deallocate(buf[1])); - - foreach (i; 0 .. numThreads) - { - if (!growDownwards) - assert(a.deallocate(buf[numThreads - 1 - i])); - else - assert(a.deallocate(buf[i])); - } - - assert(a.deallocateAll()); - void[] b = a.allocate(63); - assert(b.length == 63); - assert(a.deallocate(b)); - } - - auto a1 = SharedRegion!(Mallocator, Mallocator.alignment, - Yes.growDownwards)(1024 * 64); - - auto a2 = SharedRegion!(Mallocator, Mallocator.alignment, - No.growDownwards)(1024 * 64); - - testAlloc(a1, true); - testAlloc(a2, false); -} - -@system unittest -{ - import std.experimental.allocator.mallocator : Mallocator; - - static void testAlloc(Allocator)(ref Allocator a, bool growDownwards) - { - import core.thread : ThreadGroup; - import std.algorithm.sorting : sort; - import core.internal.spinlock : SpinLock; - - SpinLock lock = SpinLock(SpinLock.Contention.brief); - enum numThreads = 100; - void[][2 * numThreads] buf; - size_t count = 0; - - void fun() - { - void[] b = a.allocate(63); - assert(b.length == 63); - - lock.lock(); - buf[count] = b; - count++; - lock.unlock(); - - b = a.alignedAllocate(63, 32); - assert(b.length == 63); - assert(cast(size_t) b.ptr % 32 == 0); - - lock.lock(); - buf[count] = b; - count++; - lock.unlock(); - } - - auto tg = new ThreadGroup; - foreach (i; 0 .. numThreads) - { - tg.create(&fun); - } - tg.joinAll(); - - sort!((a, b) => a.ptr < b.ptr)(buf[0 .. 2 * numThreads]); - foreach (i; 0 .. 2 * numThreads - 1) - { - assert(buf[i].ptr + buf[i].length <= buf[i + 1].ptr); - } - - assert(!a.deallocate(buf[1])); - assert(a.deallocateAll()); - - void[] b = a.allocate(13); - assert(b.length == 13); - assert(a.deallocate(b)); - } - - auto a1 = SharedRegion!(Mallocator, Mallocator.alignment, - Yes.growDownwards)(1024 * 64); - - auto a2 = SharedRegion!(Mallocator, Mallocator.alignment, - No.growDownwards)(1024 * 64); - - testAlloc(a1, true); - testAlloc(a2, false); -} - -/** -A `SharedBorrowedRegion` allocates directly from a user-provided block of memory. - -Unlike a `SharedRegion`, a `SharedBorrowedRegion` does not own the memory it -allocates from and will not deallocate that memory upon destruction. Instead, -it is the user's responsibility to ensure that the memory is properly disposed -of. - -In all other respects, a `SharedBorrowedRegion` behaves exactly like a `SharedRegion`. -*/ -shared struct SharedBorrowedRegion(uint minAlign = platformAlignment, - Flag!"growDownwards" growDownwards = No.growDownwards) -{ - static assert(minAlign.isGoodStaticAlignment); - - import std.typecons : Ternary; - - // state - private void* _current, _begin, _end; - - private void* roundedBegin() shared const pure nothrow @trusted @nogc - { - return cast(void*) roundUpToAlignment(cast(size_t) _begin, alignment); - } - - private void* roundedEnd() shared const pure nothrow @trusted @nogc - { - return cast(void*) roundDownToAlignment(cast(size_t) _end, alignment); - } - - /** - Constructs a region backed by a user-provided store. - - Params: - store = User-provided store backing up the region. Must not be aliased. - */ - this(ubyte[] store) shared pure nothrow @nogc - { - _begin = cast(typeof(_begin)) store.ptr; - _end = cast(typeof(_end)) (store.ptr + store.length); - static if (growDownwards) - _current = cast(typeof(_current)) roundedEnd(); - else - _current = cast(typeof(_current)) roundedBegin(); - } - - /* - TODO: The postblit of `SharedBorrowedRegion` should be disabled because - such objects should not be copied around naively. - */ - - /** - Rounds the given size to a multiple of the `alignment` - */ - size_t goodAllocSize(size_t n) shared const pure nothrow @safe @nogc - { - return n.roundUpToAlignment(alignment); - } - - /** - Alignment offered. - */ - alias alignment = minAlign; - - /** - Allocates `n` bytes of memory. The allocation is served by atomically incrementing - a pointer which keeps track of the current used space. - - Params: - n = number of bytes to allocate - - Returns: - A properly-aligned buffer of size `n`, or `null` if request could not - be satisfied. - */ - void[] allocate(size_t n) shared pure nothrow @trusted @nogc - { - import core.atomic : cas, atomicLoad; - - if (n == 0) return null; - const rounded = goodAllocSize(n); - - shared void* localCurrent, localNewCurrent; - static if (growDownwards) - { - do - { - localCurrent = atomicLoad(_current); - localNewCurrent = localCurrent - rounded; - if (localNewCurrent > localCurrent || localNewCurrent < _begin) - return null; - } while (!cas(&_current, localCurrent, localNewCurrent)); - - return cast(void[]) localNewCurrent[0 .. n]; - } - else - { - do - { - localCurrent = atomicLoad(_current); - localNewCurrent = localCurrent + rounded; - if (localNewCurrent < localCurrent || localNewCurrent > _end) - return null; - } while (!cas(&_current, localCurrent, localNewCurrent)); - - return cast(void[]) localCurrent[0 .. n]; - } - - assert(0, "Unexpected error in SharedBorrowedRegion.allocate"); - } - - /** - Allocates `n` bytes of memory aligned at alignment `a`. - - Params: - n = number of bytes to allocate - a = alignment for the allocated block - - Returns: - Either a suitable block of `n` bytes aligned at `a`, or `null`. - */ - void[] alignedAllocate(size_t n, uint a) shared pure nothrow @trusted @nogc - { - import core.atomic : cas, atomicLoad; - import std.math.traits : isPowerOf2; - - assert(a.isPowerOf2); - if (n == 0) return null; - - const rounded = goodAllocSize(n); - shared void* localCurrent, localNewCurrent; - - static if (growDownwards) - { - do - { - localCurrent = atomicLoad(_current); - auto alignedCurrent = cast(void*)(localCurrent - rounded); - localNewCurrent = cast(shared(void*)) alignedCurrent.alignDownTo(a); - if (alignedCurrent > localCurrent || localNewCurrent > alignedCurrent || - localNewCurrent < _begin) - return null; - } while (!cas(&_current, localCurrent, localNewCurrent)); - - return cast(void[]) localNewCurrent[0 .. n]; - } - else - { - do - { - localCurrent = atomicLoad(_current); - auto alignedCurrent = alignUpTo(cast(void*) localCurrent, a); - localNewCurrent = cast(shared(void*)) (alignedCurrent + rounded); - if (alignedCurrent < localCurrent || localNewCurrent < alignedCurrent || - localNewCurrent > _end) - return null; - } while (!cas(&_current, localCurrent, localNewCurrent)); - - return cast(void[]) (localNewCurrent - rounded)[0 .. n]; - } - - assert(0, "Unexpected error in SharedBorrowedRegion.alignedAllocate"); - } - - /** - Deallocates `b`. This works only if `b` was obtained as the last call - to `allocate`; otherwise (i.e. another allocation has occurred since) it - does nothing. - - Params: - b = Block previously obtained by a call to `allocate` against this - allocator (`null` is allowed). - */ - bool deallocate(void[] b) shared pure nothrow @nogc - { - import core.atomic : cas, atomicLoad; - - const rounded = goodAllocSize(b.length); - shared void* localCurrent, localNewCurrent; - - // The cas is done only once, because only the last allocation can be reverted - localCurrent = atomicLoad(_current); - static if (growDownwards) - { - localNewCurrent = localCurrent + rounded; - if (b.ptr == localCurrent) - return cas(&_current, localCurrent, localNewCurrent); - } - else - { - localNewCurrent = localCurrent - rounded; - if (b.ptr == localNewCurrent) - return cas(&_current, localCurrent, localNewCurrent); - } - - return false; - } - - /** - Deallocates all memory allocated by this region, which can be subsequently - reused for new allocations. - */ - bool deallocateAll() shared pure nothrow @nogc - { - import core.atomic : atomicStore; - static if (growDownwards) - { - atomicStore(_current, cast(shared(void*)) roundedEnd()); - } - else - { - atomicStore(_current, cast(shared(void*)) roundedBegin()); - } - return true; - } - - /** - Queries whether `b` has been allocated with this region. - - Params: - b = Arbitrary block of memory (`null` is allowed; `owns(null)` returns - `false`). - - Returns: - `true` if `b` has been allocated with this region, `false` otherwise. - */ - Ternary owns(const void[] b) shared const pure nothrow @trusted @nogc - { - return Ternary(b && (&b[0] >= _begin) && (&b[0] + b.length <= _end)); - } - - /** - Returns `Ternary.yes` if no memory has been allocated in this region, - `Ternary.no` otherwise. (Never returns `Ternary.unknown`.) - */ - Ternary empty() shared const pure nothrow @safe @nogc - { - import core.atomic : atomicLoad; - - auto localCurrent = atomicLoad(_current); - static if (growDownwards) - return Ternary(localCurrent == roundedEnd()); - else - return Ternary(localCurrent == roundedBegin()); - } -} diff --git a/phobos/std/experimental/allocator/building_blocks/scoped_allocator.d b/phobos/std/experimental/allocator/building_blocks/scoped_allocator.d deleted file mode 100644 index 96859b0..0000000 --- a/phobos/std/experimental/allocator/building_blocks/scoped_allocator.d +++ /dev/null @@ -1,303 +0,0 @@ -// Written in the D programming language. -/** -Source: $(PHOBOSSRC std/experimental/allocator/building_blocks/scoped_allocator.d) -*/ -module std.experimental.allocator.building_blocks.scoped_allocator; - -import std.experimental.allocator.common; - -/** - -`ScopedAllocator` delegates all allocation requests to `ParentAllocator`. -When destroyed, the `ScopedAllocator` object automatically calls $(D -deallocate) for all memory allocated through its lifetime. (The $(D -deallocateAll) function is also implemented with the same semantics.) - -`deallocate` is also supported, which is where most implementation effort -and overhead of `ScopedAllocator` go. If `deallocate` is not needed, a -simpler design combining `AllocatorList` with `Region` is recommended. - -*/ -struct ScopedAllocator(ParentAllocator) -{ - static if (!stateSize!ParentAllocator) - { - // This test is available only for stateless allocators - version (StdUnittest) - @system unittest - { - testAllocator!(() => ScopedAllocator()); - } - } - - import std.experimental.allocator.building_blocks.affix_allocator - : AffixAllocator; - import std.traits : hasMember; - import std.typecons : Ternary; - - private struct Node - { - Node* prev; - Node* next; - size_t length; - } - - alias Allocator = AffixAllocator!(ParentAllocator, Node); - - // state - /** - If `ParentAllocator` is stateful, `parent` is a property giving access - to an `AffixAllocator!ParentAllocator`. Otherwise, `parent` is an alias for `AffixAllocator!ParentAllocator.instance`. - */ - static if (stateSize!ParentAllocator) - { - Allocator parent; - } - else - { - alias parent = Allocator.instance; - } - private Node* root; - - /** - `ScopedAllocator` is not copyable. - */ - @disable this(this); - - /** - `ScopedAllocator`'s destructor releases all memory allocated during its - lifetime. - */ - ~this() - { - deallocateAll; - } - - /// Alignment offered - enum alignment = Allocator.alignment; - - /** - Forwards to `parent.goodAllocSize` (which accounts for the management - overhead). - */ - size_t goodAllocSize(size_t n) - { - return parent.goodAllocSize(n); - } - - // Common code shared between allocate and allocateZeroed. - private enum _processAndReturnAllocateResult = - q{ - if (!b.ptr) return b; - Node* toInsert = & parent.prefix(b); - toInsert.prev = null; - toInsert.next = root; - toInsert.length = n; - assert(!root || !root.prev); - if (root) root.prev = toInsert; - root = toInsert; - return b; - }; - - /** - Allocates memory. For management it actually allocates extra memory from - the parent. - */ - void[] allocate(size_t n) - { - auto b = parent.allocate(n); - mixin(_processAndReturnAllocateResult); - } - - static if (hasMember!(Allocator, "allocateZeroed")) - package(std) void[] allocateZeroed()(size_t n) - { - auto b = parent.allocateZeroed(n); - mixin(_processAndReturnAllocateResult); - } - - /** - Forwards to $(D parent.expand(b, delta)). - */ - static if (hasMember!(Allocator, "expand")) - bool expand(ref void[] b, size_t delta) - { - auto result = parent.expand(b, delta); - if (result && b) - { - () @trusted { parent.prefix(b).length = b.length; }(); - } - return result; - } - - /** - Reallocates `b` to new size `s`. - */ - bool reallocate(ref void[] b, size_t s) - { - // Remove from list - if (b.ptr) - { - Node* n = & parent.prefix(b); - if (n.prev) n.prev.next = n.next; - else root = n.next; - if (n.next) n.next.prev = n.prev; - } - auto result = parent.reallocate(b, s); - // Add back to list - if (b.ptr) - { - Node* n = & parent.prefix(b); - n.prev = null; - n.next = root; - n.length = s; - if (root) root.prev = n; - root = n; - } - return result; - } - - /** - Forwards to `parent.owns(b)`. - */ - static if (hasMember!(Allocator, "owns")) - Ternary owns(void[] b) - { - return parent.owns(b); - } - - /** - Deallocates `b`. - */ - static if (hasMember!(Allocator, "deallocate")) - bool deallocate(void[] b) - { - // Remove from list - if (b.ptr) - { - Node* n = & parent.prefix(b); - if (n.prev) n.prev.next = n.next; - else root = n.next; - if (n.next) n.next.prev = n.prev; - } - return parent.deallocate(b); - } - - /** - Deallocates all memory allocated. - */ - bool deallocateAll() - { - bool result = true; - for (auto n = root; n; ) - { - void* p = n + 1; - auto length = n.length; - n = n.next; - if (!parent.deallocate(p[0 .. length])) - result = false; - } - root = null; - return result; - } - - /** - Returns `Ternary.yes` if this allocator is not responsible for any memory, - `Ternary.no` otherwise. (Never returns `Ternary.unknown`.) - */ - pure nothrow @safe @nogc - Ternary empty() const - { - return Ternary(root is null); - } -} - -/// -@system unittest -{ - import std.experimental.allocator.mallocator : Mallocator; - import std.typecons : Ternary; - ScopedAllocator!Mallocator alloc; - assert(alloc.empty == Ternary.yes); - const b = alloc.allocate(10); - assert(b.length == 10); - assert(alloc.empty == Ternary.no); -} - -version (StdUnittest) -@system unittest -{ - import std.experimental.allocator.gc_allocator : GCAllocator; - testAllocator!(() => ScopedAllocator!GCAllocator()); -} - -@system unittest // https://issues.dlang.org/show_bug.cgi?id=16046 -{ - import std.exception; - import std.experimental.allocator; - import std.experimental.allocator.mallocator; - ScopedAllocator!Mallocator alloc; - auto foo = alloc.make!int(1).enforce; - auto bar = alloc.make!int(2).enforce; - alloc.dispose(foo); - alloc.dispose(bar); // segfault here -} - -@system unittest -{ - import std.experimental.allocator.gc_allocator : GCAllocator; - ScopedAllocator!GCAllocator a; - - assert(__traits(compiles, (() nothrow @safe @nogc => a.goodAllocSize(0))())); - - // Ensure deallocate inherits from parent allocators - auto b = a.allocate(42); - assert(b.length == 42); - () nothrow @nogc { a.deallocate(b); }(); -} - -// Test that deallocateAll infers from parent -@system unittest -{ - import std.experimental.allocator.building_blocks.region : BorrowedRegion; - - ScopedAllocator!(BorrowedRegion!()) a; - a.parent.parent = BorrowedRegion!()(new ubyte[1024 * 64]); - auto b = a.allocate(42); - assert(b.length == 42); - assert((() pure nothrow @safe @nogc => a.expand(b, 22))()); - assert(b.length == 64); - assert((() nothrow @nogc => a.reallocate(b, 100))()); - assert(b.length == 100); - assert((() nothrow @nogc => a.deallocateAll())()); -} - -@system unittest -{ - import std.experimental.allocator.building_blocks.region : Region; - import std.experimental.allocator.mallocator : Mallocator; - import std.typecons : Ternary; - - auto a = Region!(Mallocator)(1024 * 64); - auto b = a.allocate(42); - assert(b.length == 42); - assert((() pure nothrow @safe @nogc => a.expand(b, 22))()); - assert(b.length == 64); - assert((() pure nothrow @safe @nogc => a.owns(b))() == Ternary.yes); - assert((() nothrow @nogc => a.reallocate(b, 100))()); - assert(b.length == 100); - assert((() pure nothrow @safe @nogc => a.owns(b))() == Ternary.yes); - assert((() pure nothrow @safe @nogc => a.owns(null))() == Ternary.no); -} - -// Test empty -@system unittest -{ - import std.experimental.allocator.mallocator : Mallocator; - import std.typecons : Ternary; - ScopedAllocator!Mallocator alloc; - - assert((() pure nothrow @safe @nogc => alloc.empty)() == Ternary.yes); - const b = alloc.allocate(10); - assert((() pure nothrow @safe @nogc => alloc.empty)() == Ternary.no); -} diff --git a/phobos/std/experimental/allocator/building_blocks/segregator.d b/phobos/std/experimental/allocator/building_blocks/segregator.d deleted file mode 100644 index ff089bd..0000000 --- a/phobos/std/experimental/allocator/building_blocks/segregator.d +++ /dev/null @@ -1,520 +0,0 @@ -// Written in the D programming language. -/** -Source: $(PHOBOSSRC std/experimental/allocator/building_blocks/segregator.d) -*/ -module std.experimental.allocator.building_blocks.segregator; - -import std.experimental.allocator.common; - -/** -Dispatches allocations (and deallocations) between two allocators ($(D -SmallAllocator) and `LargeAllocator`) depending on the size allocated, as -follows. All allocations smaller than or equal to `threshold` will be -dispatched to `SmallAllocator`. The others will go to `LargeAllocator`. - -If both allocators are `shared`, the `Segregator` will also offer $(D -shared) methods. -*/ -struct Segregator(size_t threshold, SmallAllocator, LargeAllocator) -{ - import std.algorithm.comparison : min; - import std.traits : hasMember, ReturnType; - import std.typecons : Ternary; - - static if (stateSize!SmallAllocator) private SmallAllocator _small; - else private alias _small = SmallAllocator.instance; - static if (stateSize!LargeAllocator) private LargeAllocator _large; - else private alias _large = LargeAllocator.instance; - - version (StdDdoc) - { - /** - The alignment offered is the minimum of the two allocators' alignment. - */ - enum uint alignment; - /** - This method is defined only if at least one of the allocators defines - it. The good allocation size is obtained from `SmallAllocator` if $(D - s <= threshold), or `LargeAllocator` otherwise. (If one of the - allocators does not define `goodAllocSize`, the default - implementation in this module applies.) - */ - static size_t goodAllocSize(size_t s); - /** - The memory is obtained from `SmallAllocator` if $(D s <= threshold), - or `LargeAllocator` otherwise. - */ - void[] allocate(size_t); - /** - This method is defined if both allocators define it, and forwards to - `SmallAllocator` or `LargeAllocator` appropriately. - */ - void[] alignedAllocate(size_t, uint); - /** - This method is defined only if at least one of the allocators defines - it. If `SmallAllocator` defines `expand` and $(D b.length + - delta <= threshold), the call is forwarded to `SmallAllocator`. If $(D - LargeAllocator) defines `expand` and $(D b.length > threshold), the - call is forwarded to `LargeAllocator`. Otherwise, the call returns - `false`. - */ - bool expand(ref void[] b, size_t delta); - /** - This method is defined only if at least one of the allocators defines - it. If `SmallAllocator` defines `reallocate` and $(D b.length <= - threshold && s <= threshold), the call is forwarded to $(D - SmallAllocator). If `LargeAllocator` defines `expand` and $(D - b.length > threshold && s > threshold), the call is forwarded to $(D - LargeAllocator). Otherwise, the call returns `false`. - */ - bool reallocate(ref void[] b, size_t s); - /** - This method is defined only if at least one of the allocators defines - it, and work similarly to `reallocate`. - */ - bool alignedReallocate(ref void[] b, size_t s, uint a); - /** - This method is defined only if both allocators define it. The call is - forwarded to `SmallAllocator` if $(D b.length <= threshold), or $(D - LargeAllocator) otherwise. - */ - Ternary owns(void[] b); - /** - This function is defined only if both allocators define it, and forwards - appropriately depending on `b.length`. - */ - bool deallocate(void[] b); - /** - This function is defined only if both allocators define it, and calls - `deallocateAll` for them in turn. - */ - bool deallocateAll(); - /** - This function is defined only if both allocators define it, and returns - the conjunction of `empty` calls for the two. - */ - Ternary empty(); - } - - /** - Composite allocators involving nested instantiations of `Segregator` make - it difficult to access individual sub-allocators stored within. $(D - allocatorForSize) simplifies the task by supplying the allocator nested - inside a `Segregator` that is responsible for a specific size `s`. - - Example: - ---- - alias A = Segregator!(300, - Segregator!(200, A1, A2), - A3); - A a; - static assert(typeof(a.allocatorForSize!10) == A1); - static assert(typeof(a.allocatorForSize!250) == A2); - static assert(typeof(a.allocatorForSize!301) == A3); - ---- - */ - ref auto allocatorForSize(size_t s)() - { - static if (s <= threshold) - static if (is(SmallAllocator == Segregator!(Args), Args...)) - return _small.allocatorForSize!s; - else return _small; - else - static if (is(LargeAllocator == Segregator!(Args), Args...)) - return _large.allocatorForSize!s; - else return _large; - } - - enum uint alignment = min(SmallAllocator.alignment, - LargeAllocator.alignment); - - private template Impl() - { - size_t goodAllocSize(size_t s) - { - return s <= threshold - ? _small.goodAllocSize(s) - : _large.goodAllocSize(s); - } - - void[] allocate(size_t s) - { - return s <= threshold ? _small.allocate(s) : _large.allocate(s); - } - - static if (hasMember!(SmallAllocator, "alignedAllocate") - && hasMember!(LargeAllocator, "alignedAllocate")) - void[] alignedAllocate(size_t s, uint a) - { - return s <= threshold - ? _small.alignedAllocate(s, a) - : _large.alignedAllocate(s, a); - } - - static if (hasMember!(SmallAllocator, "expand") - || hasMember!(LargeAllocator, "expand")) - bool expand(ref void[] b, size_t delta) - { - if (!delta) return true; - if (b.length + delta <= threshold) - { - // Old and new allocations handled by _small - static if (hasMember!(SmallAllocator, "expand")) - return _small.expand(b, delta); - else - return false; - } - if (b.length > threshold) - { - // Old and new allocations handled by _large - static if (hasMember!(LargeAllocator, "expand")) - return _large.expand(b, delta); - else - return false; - } - // Oops, cross-allocator transgression - return false; - } - - static if (hasMember!(SmallAllocator, "reallocate") - || hasMember!(LargeAllocator, "reallocate")) - bool reallocate(ref void[] b, size_t s) - { - static if (hasMember!(SmallAllocator, "reallocate")) - if (b.length <= threshold && s <= threshold) - { - // Old and new allocations handled by _small - return _small.reallocate(b, s); - } - static if (hasMember!(LargeAllocator, "reallocate")) - if (b.length > threshold && s > threshold) - { - // Old and new allocations handled by _large - return _large.reallocate(b, s); - } - // Cross-allocator transgression - return .reallocate(this, b, s); - } - - static if (hasMember!(SmallAllocator, "alignedReallocate") - || hasMember!(LargeAllocator, "alignedReallocate")) - bool alignedReallocate(ref void[] b, size_t s, uint a) - { - static if (hasMember!(SmallAllocator, "alignedReallocate")) - if (b.length <= threshold && s <= threshold) - { - // Old and new allocations handled by _small - return _small.alignedReallocate(b, s, a); - } - static if (hasMember!(LargeAllocator, "alignedReallocate")) - if (b.length > threshold && s > threshold) - { - // Old and new allocations handled by _large - return _large.alignedReallocate(b, s, a); - } - // Cross-allocator transgression - return .alignedReallocate(this, b, s, a); - } - - static if (hasMember!(SmallAllocator, "allocateZeroed") - || hasMember!(LargeAllocator, "allocateZeroed")) - package(std) void[] allocateZeroed()(size_t s) - { - if (s <= threshold) - { - static if (hasMember!(SmallAllocator, "allocateZeroed")) - return _small.allocateZeroed(s); - else - { - auto b = _small.allocate(s); - (() @trusted => (cast(ubyte[]) b)[] = 0)(); // OK even if b is null. - return b; - } - } - else - { - static if (hasMember!(LargeAllocator, "allocateZeroed")) - return _large.allocateZeroed(s); - else - { - auto b = _large.allocate(s); - (() @trusted => (cast(ubyte[]) b)[] = 0)(); // OK even if b is null. - return b; - } - } - } - - static if (hasMember!(SmallAllocator, "owns") - && hasMember!(LargeAllocator, "owns")) - Ternary owns(void[] b) - { - return Ternary(b.length <= threshold - ? _small.owns(b) : _large.owns(b)); - } - - static if (hasMember!(SmallAllocator, "deallocate") - && hasMember!(LargeAllocator, "deallocate")) - bool deallocate(void[] data) - { - return data.length <= threshold - ? _small.deallocate(data) - : _large.deallocate(data); - } - - static if (hasMember!(SmallAllocator, "deallocateAll") - && hasMember!(LargeAllocator, "deallocateAll")) - bool deallocateAll() - { - // Use & insted of && to evaluate both - return _small.deallocateAll() & _large.deallocateAll(); - } - - static if (hasMember!(SmallAllocator, "empty") - && hasMember!(LargeAllocator, "empty")) - Ternary empty() - { - return _small.empty & _large.empty; - } - - static if (hasMember!(SmallAllocator, "resolveInternalPointer") - && hasMember!(LargeAllocator, "resolveInternalPointer")) - Ternary resolveInternalPointer(const void* p, ref void[] result) - { - Ternary r = _small.resolveInternalPointer(p, result); - return r == Ternary.no ? _large.resolveInternalPointer(p, result) : r; - } - } - - private enum sharedMethods = - !stateSize!SmallAllocator - && !stateSize!LargeAllocator - && is(typeof(SmallAllocator.instance) == shared) - && is(typeof(LargeAllocator.instance) == shared); - - static if (sharedMethods) - { - static shared Segregator instance; - shared { mixin Impl!(); } - } - else - { - static if (!stateSize!SmallAllocator && !stateSize!LargeAllocator) - __gshared Segregator instance; - mixin Impl!(); - } -} - -/// -@system unittest -{ - import std.experimental.allocator.building_blocks.free_list : FreeList; - import std.experimental.allocator.gc_allocator : GCAllocator; - import std.experimental.allocator.mallocator : Mallocator; - alias A = - Segregator!( - 1024 * 4, - Segregator!( - 128, FreeList!(Mallocator, 0, 128), - GCAllocator), - Segregator!( - 1024 * 1024, Mallocator, - GCAllocator) - ); - A a; - auto b = a.allocate(200); - assert(b.length == 200); - a.deallocate(b); -} - -/** -A `Segregator` with more than three arguments expands to a composition of -elemental `Segregator`s, as illustrated by the following example: - ----- -alias A = - Segregator!( - n1, A1, - n2, A2, - n3, A3, - A4 - ); ----- - -With this definition, allocation requests for `n1` bytes or less are directed -to `A1`; requests between $(D n1 + 1) and `n2` bytes (inclusive) are -directed to `A2`; requests between $(D n2 + 1) and `n3` bytes (inclusive) -are directed to `A3`; and requests for more than `n3` bytes are directed -to `A4`. If some particular range should not be handled, `NullAllocator` -may be used appropriately. - -*/ -template Segregator(Args...) -if (Args.length > 3) -{ - // Binary search - private enum cutPoint = ((Args.length - 2) / 4) * 2; - static if (cutPoint >= 2) - { - alias Segregator = .Segregator!( - Args[cutPoint], - .Segregator!(Args[0 .. cutPoint], Args[cutPoint + 1]), - .Segregator!(Args[cutPoint + 2 .. $]) - ); - } - else - { - // Favor small sizes - alias Segregator = .Segregator!( - Args[0], - Args[1], - .Segregator!(Args[2 .. $]) - ); - } -} - -/// -@system unittest -{ - import std.experimental.allocator.building_blocks.free_list : FreeList; - import std.experimental.allocator.gc_allocator : GCAllocator; - import std.experimental.allocator.mallocator : Mallocator; - alias A = - Segregator!( - 128, FreeList!(Mallocator, 0, 128), - 1024 * 4, GCAllocator, - 1024 * 1024, Mallocator, - GCAllocator - ); - A a; - auto b = a.allocate(201); - assert(b.length == 201); - a.deallocate(b); -} - -@system unittest -{ - import std.experimental.allocator.gc_allocator : GCAllocator; - import std.experimental.allocator.building_blocks.kernighan_ritchie : KRRegion; - Segregator!(128, GCAllocator, KRRegion!GCAllocator) alloc; - assert((() nothrow @safe @nogc => alloc.goodAllocSize(1))() - == GCAllocator.instance.goodAllocSize(1)); - - // Note: we infer `shared` from GCAllocator.goodAllocSize so we need a - // shared object in order to be able to use the function - shared Segregator!(128, GCAllocator, GCAllocator) sharedAlloc; - assert((() nothrow @safe @nogc => sharedAlloc.goodAllocSize(1))() - == GCAllocator.instance.goodAllocSize(1)); -} - -@system unittest -{ - import std.experimental.allocator.building_blocks.bitmapped_block : BitmappedBlock; - import std.typecons : Ternary; - - alias A = - Segregator!( - 128, BitmappedBlock!(4096), - BitmappedBlock!(4096) - ); - - A a = A( - BitmappedBlock!(4096)(new ubyte[4096 * 1024]), - BitmappedBlock!(4096)(new ubyte[4096 * 1024]) - ); - - assert(a.empty == Ternary.yes); - auto b = a.allocate(42); - assert(b.length == 42); - assert(a.empty == Ternary.no); - assert(a.alignedReallocate(b, 256, 512)); - assert(b.length == 256); - assert(a.alignedReallocate(b, 42, 512)); - assert(b.length == 42); - assert((() pure nothrow @safe @nogc => a.owns(b))() == Ternary.yes); - assert((() pure nothrow @safe @nogc => a.owns(null))() == Ternary.no); - // Ensure deallocate inherits from parent allocators - assert((() nothrow @nogc => a.deallocate(b))()); - assert(a.empty == Ternary.yes); - - // Test that deallocateAll inherits from parents - auto c = a.allocate(42); - assert(c.length == 42); - assert((() pure nothrow @safe @nogc => a.expand(c, 58))()); - assert(c.length == 100); - assert(a.empty == Ternary.no); - assert((() nothrow @nogc => a.deallocateAll())()); - assert(a.empty == Ternary.yes); -} - -@system unittest -{ - import std.experimental.allocator.gc_allocator : GCAllocator; - import std.typecons : Ternary; - - shared Segregator!(1024 * 4, GCAllocator, GCAllocator) a; - - auto b = a.allocate(201); - assert(b.length == 201); - - void[] p; - assert((() nothrow @safe @nogc => a.resolveInternalPointer(&b[0], p))() == Ternary.yes); - assert((() nothrow @safe @nogc => a.resolveInternalPointer(null, p))() == Ternary.no); - - // Ensure deallocate inherits from parent allocators - assert((() nothrow @nogc => a.deallocate(b))()); -} - -@system unittest -{ - import std.experimental.allocator.building_blocks.bitmapped_block : BitmappedBlockWithInternalPointers; - import std.typecons : Ternary; - - alias A = - Segregator!( - 10_240, BitmappedBlockWithInternalPointers!(4096), - BitmappedBlockWithInternalPointers!(4096) - ); - - A a = A( - BitmappedBlockWithInternalPointers!(4096)(new ubyte[4096 * 1024]), - BitmappedBlockWithInternalPointers!(4096)(new ubyte[4096 * 1024]) - ); - - assert((() nothrow @safe @nogc => a.empty)() == Ternary.yes); - auto b = a.allocate(201); - assert(b.length == 201); - assert((() nothrow @safe @nogc => a.empty)() == Ternary.no); - assert((() nothrow @nogc => a.deallocate(b))()); -} - -// Test that reallocate infers from parent -@system unittest -{ - import std.experimental.allocator.mallocator : Mallocator; - - alias a = Segregator!(10_240, Mallocator, Mallocator).instance; - - auto b = a.allocate(42); - assert(b.length == 42); - assert((() nothrow @nogc => a.reallocate(b, 100))()); - assert(b.length == 100); - assert((() nothrow @nogc => a.deallocate(b))()); -} - -@system unittest -{ - import std.experimental.allocator.building_blocks.region : BorrowedRegion; - import std.typecons : Ternary; - - auto a = Segregator!(10_240, BorrowedRegion!(), BorrowedRegion!())( - BorrowedRegion!()(new ubyte[4096 * 1024]), - BorrowedRegion!()(new ubyte[4096 * 1024])); - - assert((() nothrow @safe @nogc => a.empty)() == Ternary.yes); - auto b = a.alignedAllocate(42, 8); - assert(b.length == 42); - assert((() nothrow @nogc => a.alignedReallocate(b, 100, 8))()); - assert(b.length == 100); - assert((() nothrow @safe @nogc => a.empty)() == Ternary.no); - assert((() nothrow @nogc => a.deallocate(b))()); -} diff --git a/phobos/std/experimental/allocator/building_blocks/stats_collector.d b/phobos/std/experimental/allocator/building_blocks/stats_collector.d deleted file mode 100644 index 1f8687c..0000000 --- a/phobos/std/experimental/allocator/building_blocks/stats_collector.d +++ /dev/null @@ -1,898 +0,0 @@ -// Written in the D programming language. -/** -Allocator that collects useful statistics about allocations, both global and per -calling point. The statistics collected can be configured statically by choosing -combinations of `Options` appropriately. - -Source: $(PHOBOSSRC std/experimental/allocator/building_blocks/stats_collector.d) -*/ -module std.experimental.allocator.building_blocks.stats_collector; - -/// -@safe unittest -{ - import std.experimental.allocator.gc_allocator : GCAllocator; - import std.experimental.allocator.building_blocks.free_list : FreeList; - alias Allocator = StatsCollector!(GCAllocator, Options.bytesUsed); -} - -import std.experimental.allocator.common; - -/** -_Options for `StatsCollector` defined below. Each enables during -compilation one specific counter, statistic, or other piece of information. -*/ -enum Options : ulong -{ - /** - Counts the number of calls to `owns`. - */ - numOwns = 1u << 0, - /** - Counts the number of calls to `allocate`. All calls are counted, - including requests for zero bytes or failed requests. - */ - numAllocate = 1u << 1, - /** - Counts the number of calls to `allocate` that succeeded, i.e. they - returned a block as large as requested. (N.B. requests for zero bytes count - as successful.) - */ - numAllocateOK = 1u << 2, - /** - Counts the number of calls to `expand`, regardless of arguments or - result. - */ - numExpand = 1u << 3, - /** - Counts the number of calls to `expand` that resulted in a successful - expansion. - */ - numExpandOK = 1u << 4, - /** - Counts the number of calls to `reallocate`, regardless of arguments or - result. - */ - numReallocate = 1u << 5, - /** - Counts the number of calls to `reallocate` that succeeded. - (Reallocations to zero bytes count as successful.) - */ - numReallocateOK = 1u << 6, - /** - Counts the number of calls to `reallocate` that resulted in an in-place - reallocation (no memory moved). If this number is close to the total number - of reallocations, that indicates the allocator finds room at the current - block's end in a large fraction of the cases, but also that internal - fragmentation may be high (the size of the unit of allocation is large - compared to the typical allocation size of the application). - */ - numReallocateInPlace = 1u << 7, - /** - Counts the number of calls to `deallocate`. - */ - numDeallocate = 1u << 8, - /** - Counts the number of calls to `deallocateAll`. - */ - numDeallocateAll = 1u << 9, - /** - Counts the number of calls to `alignedAllocate`. All calls are counted, - including requests for zero bytes or failed requests. - */ - numAlignedAllocate = 1u << 10, - /** - Counts the number of calls to `alignedAllocate` that succeeded, i.e. they - returned a block as large as requested. (N.B. requests for zero bytes count - as successful.) - */ - numAlignedAllocateOk = 1u << 11, - /** - Chooses all `numXxx` flags. - */ - numAll = (1u << 12) - 1, - /** - Tracks bytes currently allocated by this allocator. This number goes up - and down as memory is allocated and deallocated, and is zero if the - allocator currently has no active allocation. - */ - bytesUsed = 1u << 12, - /** - Tracks total cumulative bytes allocated by means of `allocate`, - `expand`, and `reallocate` (when resulting in an expansion). This - number always grows and indicates allocation traffic. To compute bytes - deallocated cumulatively, subtract `bytesUsed` from `bytesAllocated`. - */ - bytesAllocated = 1u << 13, - /** - Tracks the sum of all `delta` values in calls of the form - $(D expand(b, delta)) that succeed (return `true`). - */ - bytesExpanded = 1u << 14, - /** - Tracks the sum of all $(D b.length - s) with $(D b.length > s) in calls of - the form $(D realloc(b, s)) that succeed (return `true`). In per-call - statistics, also unambiguously counts the bytes deallocated with - `deallocate`. - */ - bytesContracted = 1u << 15, - /** - Tracks the sum of all bytes moved as a result of calls to `realloc` that - were unable to reallocate in place. A large number (relative to $(D - bytesAllocated)) indicates that the application should use larger - preallocations. - */ - bytesMoved = 1u << 16, - /** - Tracks the sum of all bytes NOT moved as result of calls to `realloc` - that managed to reallocate in place. A large number (relative to $(D - bytesAllocated)) indicates that the application is expansion-intensive and - is saving a good amount of moves. However, if this number is relatively - small and `bytesSlack` is high, it means the application is - overallocating for little benefit. - */ - bytesNotMoved = 1u << 17, - /** - Measures the sum of extra bytes allocated beyond the bytes requested, i.e. - the $(HTTP goo.gl/YoKffF, internal fragmentation). This is the current - effective number of slack bytes, and it goes up and down with time. - */ - bytesSlack = 1u << 18, - /** - Measures the maximum bytes allocated over the time. This is useful for - dimensioning allocators. - */ - bytesHighTide = 1u << 19, - /** - Chooses all `byteXxx` flags. - */ - bytesAll = ((1u << 20) - 1) & ~numAll, - /** - Combines all flags above. - */ - all = (1u << 20) - 1 -} - -/** - -Allocator that collects extra data about allocations. Since each piece of -information adds size and time overhead, statistics can be individually enabled -or disabled through compile-time `flags`. - -All stats of the form `numXxx` record counts of events occurring, such as -calls to functions and specific results. The stats of the form `bytesXxx` -collect cumulative sizes. - -In addition, the data `callerSize`, `callerModule`, `callerFile`, $(D -callerLine), and `callerTime` is associated with each specific allocation. -This data prefixes each allocation. - -*/ -struct StatsCollector(Allocator, ulong flags = Options.all, - ulong perCallFlags = 0) -{ -private: - import std.traits : hasMember, Signed; - import std.typecons : Ternary; - - static string define(string type, string[] names...) - { - string result; - foreach (v; names) - result ~= "static if (flags & Options."~v~") {" - ~ "private "~type~" _"~v~";" - ~ "public const("~type~") "~v~"() const { return _"~v~"; }" - ~ "}"; - return result; - } - - void add(string counter)(Signed!size_t n) - { - mixin("static if (flags & Options." ~ counter - ~ ") _" ~ counter ~ " += n;"); - static if (counter == "bytesUsed" && (flags & Options.bytesHighTide)) - { - if (bytesHighTide < bytesUsed ) _bytesHighTide = bytesUsed; - } - } - - void up(string counter)() { add!counter(1); } - void down(string counter)() { add!counter(-1); } - - version (StdDdoc) - { - /** - Read-only properties enabled by the homonym `flags` chosen by the - user. - - Example: - ---- - StatsCollector!(Mallocator, - Options.bytesUsed | Options.bytesAllocated) a; - auto d1 = a.allocate(10); - auto d2 = a.allocate(11); - a.deallocate(d1); - assert(a.bytesAllocated == 21); - assert(a.bytesUsed == 11); - a.deallocate(d2); - assert(a.bytesAllocated == 21); - assert(a.bytesUsed == 0); - ---- - */ - @property ulong numOwns() const; - /// Ditto - @property ulong numAllocate() const; - /// Ditto - @property ulong numAllocateOK() const; - /// Ditto - @property ulong numExpand() const; - /// Ditto - @property ulong numExpandOK() const; - /// Ditto - @property ulong numReallocate() const; - /// Ditto - @property ulong numReallocateOK() const; - /// Ditto - @property ulong numReallocateInPlace() const; - /// Ditto - @property ulong numDeallocate() const; - /// Ditto - @property ulong numDeallocateAll() const; - /// Ditto - @property ulong numAlignedAllocate() const; - /// Ditto - @property ulong numAlignedAllocateOk() const; - /// Ditto - @property ulong bytesUsed() const; - /// Ditto - @property ulong bytesAllocated() const; - /// Ditto - @property ulong bytesExpanded() const; - /// Ditto - @property ulong bytesContracted() const; - /// Ditto - @property ulong bytesMoved() const; - /// Ditto - @property ulong bytesNotMoved() const; - /// Ditto - @property ulong bytesSlack() const; - /// Ditto - @property ulong bytesHighTide() const; - } - -public: - /** - The parent allocator is publicly accessible either as a direct member if it - holds state, or as an alias to `Allocator.instance` otherwise. One may use - it for making calls that won't count toward statistics collection. - */ - static if (stateSize!Allocator) Allocator parent; - else alias parent = Allocator.instance; - -private: - // Per-allocator state - mixin(define("ulong", - "numOwns", - "numAllocate", - "numAllocateOK", - "numExpand", - "numExpandOK", - "numReallocate", - "numReallocateOK", - "numReallocateInPlace", - "numDeallocate", - "numDeallocateAll", - "numAlignedAllocate", - "numAlignedAllocateOk", - "bytesUsed", - "bytesAllocated", - "bytesExpanded", - "bytesContracted", - "bytesMoved", - "bytesNotMoved", - "bytesSlack", - "bytesHighTide", - )); - -public: - - /// Alignment offered is equal to `Allocator.alignment`. - alias alignment = Allocator.alignment; - - /** - Increments `numOwns` (per instance and and per call) and forwards to $(D - parent.owns(b)). - */ - static if (hasMember!(Allocator, "owns")) - { - static if ((perCallFlags & Options.numOwns) == 0) - Ternary owns(void[] b) - { return ownsImpl(b); } - else - pragma(inline, true) // LDC: Must inline because of __FILE__ as template parameter - Ternary owns(string f = __FILE__, uint n = __LINE__)(void[] b) - { return ownsImpl!(f, n)(b); } - } - - private Ternary ownsImpl(string f = null, uint n = 0)(void[] b) - { - up!"numOwns"; - addPerCall!(f, n, "numOwns")(1); - return parent.owns(b); - } - - /** - Forwards to `parent.allocate`. Affects per instance: `numAllocate`, - `bytesUsed`, `bytesAllocated`, `bytesSlack`, `numAllocateOK`, - and `bytesHighTide`. Affects per call: `numAllocate`, $(D - numAllocateOK), and `bytesAllocated`. - */ - static if (!(perCallFlags - & (Options.numAllocate | Options.numAllocateOK - | Options.bytesAllocated))) - { - void[] allocate(size_t n) - { return allocateImpl(n); } - } - else - { - pragma(inline, true) // LDC: Must inline because of __FILE__ as template parameter - void[] allocate(string f = __FILE__, ulong n = __LINE__) - (size_t bytes) - { return allocateImpl!(f, n)(bytes); } - } - - // Common code currently shared between allocateImpl and allocateZeroedImpl. - private enum _updateStatsForAllocateResult = - q{ - add!"bytesUsed"(result.length); - add!"bytesAllocated"(result.length); - immutable slack = this.goodAllocSize(result.length) - result.length; - add!"bytesSlack"(slack); - up!"numAllocate"; - add!"numAllocateOK"(result.length == bytes); // allocating 0 bytes is OK - addPerCall!(f, n, "numAllocate", "numAllocateOK", "bytesAllocated") - (1, result.length == bytes, result.length); - }; - - private void[] allocateImpl(string f = null, ulong n = 0)(size_t bytes) - { - auto result = parent.allocate(bytes); - mixin(_updateStatsForAllocateResult); - return result; - } - - static if (hasMember!(Allocator, "allocateZeroed")) - { - static if (!(perCallFlags - & (Options.numAllocate | Options.numAllocateOK - | Options.bytesAllocated))) - { - package(std) void[] allocateZeroed()(size_t n) - { return allocateZeroedImpl(n); } - } - else - { - pragma(inline, true) // LDC: Must inline because of __FILE__ as template parameter - package(std) void[] allocateZeroed(string f = __FILE__, ulong n = __LINE__) - (size_t bytes) - { return allocateZeroedImpl!(f, n)(bytes); } - } - - private void[] allocateZeroedImpl(string f = null, ulong n = 0)(size_t bytes) - { - auto result = parent.allocateZeroed(bytes); - // Note: calls to `allocateZeroed` are counted for statistical purposes - // as if they were calls to `allocate`. If/when `allocateZeroed` is made - // public it might be of interest to count such calls separately. - mixin(_updateStatsForAllocateResult); - return result; - } - } - - /** - Forwards to `parent.alignedAllocate`. Affects per instance: `numAlignedAllocate`, - `bytesUsed`, `bytesAllocated`, `bytesSlack`, `numAlignedAllocateOk`, - and `bytesHighTide`. Affects per call: `numAlignedAllocate`, `numAlignedAllocateOk`, - and `bytesAllocated`. - */ - static if (!(perCallFlags - & (Options.numAlignedAllocate | Options.numAlignedAllocateOk - | Options.bytesAllocated))) - { - void[] alignedAllocate(size_t n, uint a) - { return alignedAllocateImpl(n, a); } - } - else - { - pragma(inline, true) // LDC: Must inline because of __FILE__ as template parameter - void[] alignedAllocate(string f = __FILE__, ulong n = __LINE__) - (size_t bytes, uint a) - { return alignedAllocateImpl!(f, n)(bytes, a); } - } - - private void[] alignedAllocateImpl(string f = null, ulong n = 0)(size_t bytes, uint a) - { - up!"numAlignedAllocate"; - static if (!hasMember!(Allocator, "alignedAllocate")) - { - if (bytes == 0) - up!"numAlignedAllocateOk"; - void[] result = null; - } - else - { - auto result = parent.alignedAllocate(bytes, a); - add!"bytesUsed"(result.length); - add!"bytesAllocated"(result.length); - immutable slack = this.goodAllocSize(result.length) - result.length; - add!"bytesSlack"(slack); - add!"numAlignedAllocateOk"(result.length == bytes); // allocating 0 bytes is OK - } - addPerCall!(f, n, "numAlignedAllocate", "numAlignedAllocateOk", "bytesAllocated") - (1, result.length == bytes, result.length); - - return result; - } - - /** - Defined whether or not `Allocator.expand` is defined. Affects - per instance: `numExpand`, `numExpandOK`, `bytesExpanded`, - `bytesSlack`, `bytesAllocated`, and `bytesUsed`. Affects per call: - `numExpand`, `numExpandOK`, `bytesExpanded`, and - `bytesAllocated`. - */ - static if (!(perCallFlags - & (Options.numExpand | Options.numExpandOK | Options.bytesExpanded))) - { - bool expand(ref void[] b, size_t delta) - { return expandImpl(b, delta); } - } - else - { - pragma(inline, true) // LDC: Must inline because of __FILE__ as template parameter - bool expand(string f = __FILE__, uint n = __LINE__) - (ref void[] b, size_t delta) - { return expandImpl!(f, n)(b, delta); } - } - - private bool expandImpl(string f = null, uint n = 0)(ref void[] b, size_t s) - { - up!"numExpand"; - Signed!size_t slack = 0; - static if (!hasMember!(Allocator, "expand")) - { - auto result = s == 0; - } - else - { - immutable bytesSlackB4 = this.goodAllocSize(b.length) - b.length; - auto result = parent.expand(b, s); - if (result) - { - up!"numExpandOK"; - add!"bytesUsed"(s); - add!"bytesAllocated"(s); - add!"bytesExpanded"(s); - slack = Signed!size_t(this.goodAllocSize(b.length) - b.length - - bytesSlackB4); - add!"bytesSlack"(slack); - } - } - immutable xtra = result ? s : 0; - addPerCall!(f, n, "numExpand", "numExpandOK", "bytesExpanded", - "bytesAllocated") - (1, result, xtra, xtra); - return result; - } - - /** - Defined whether or not `Allocator.reallocate` is defined. Affects - per instance: `numReallocate`, `numReallocateOK`, $(D - numReallocateInPlace), `bytesNotMoved`, `bytesAllocated`, $(D - bytesSlack), `bytesExpanded`, and `bytesContracted`. Affects per call: - `numReallocate`, `numReallocateOK`, `numReallocateInPlace`, - `bytesNotMoved`, `bytesExpanded`, `bytesContracted`, and - `bytesMoved`. - */ - static if (!(perCallFlags - & (Options.numReallocate | Options.numReallocateOK - | Options.numReallocateInPlace | Options.bytesNotMoved - | Options.bytesExpanded | Options.bytesContracted - | Options.bytesMoved))) - { - bool reallocate(ref void[] b, size_t s) - { return reallocateImpl(b, s); } - } - else - { - pragma(inline, true) // LDC: Must inline because of __FILE__ as template parameter - bool reallocate(string f = __FILE__, ulong n = __LINE__) - (ref void[] b, size_t s) - { return reallocateImpl!(f, n)(b, s); } - } - - private bool reallocateImpl(string f = null, uint n = 0) - (ref void[] b, size_t s) - { - up!"numReallocate"; - const bytesSlackB4 = this.goodAllocSize(b.length) - b.length; - const oldB = b.ptr; - const oldLength = b.length; - - const result = parent.reallocate(b, s); - - Signed!size_t slack = 0; - bool wasInPlace = false; - Signed!size_t delta = 0; - - if (result) - { - up!"numReallocateOK"; - slack = (this.goodAllocSize(b.length) - b.length) - bytesSlackB4; - add!"bytesSlack"(slack); - add!"bytesUsed"(Signed!size_t(b.length - oldLength)); - if (oldB == b.ptr) - { - // This was an in-place reallocation, yay - wasInPlace = true; - up!"numReallocateInPlace"; - add!"bytesNotMoved"(oldLength); - delta = b.length - oldLength; - if (delta >= 0) - { - // Expansion - add!"bytesAllocated"(delta); - add!"bytesExpanded"(delta); - } - else - { - // Contraction - add!"bytesContracted"(-delta); - } - } - else - { - // This was a allocate-move-deallocate cycle - add!"bytesAllocated"(b.length); - add!"bytesMoved"(oldLength); - } - } - addPerCall!(f, n, "numReallocate", "numReallocateOK", - "numReallocateInPlace", "bytesNotMoved", - "bytesExpanded", "bytesContracted", "bytesMoved") - (1, result, wasInPlace, wasInPlace ? oldLength : 0, - delta >= 0 ? delta : 0, delta < 0 ? -delta : 0, - wasInPlace ? 0 : oldLength); - return result; - } - - /** - Defined whether or not `Allocator.deallocate` is defined. Affects - per instance: `numDeallocate`, `bytesUsed`, and `bytesSlack`. - Affects per call: `numDeallocate` and `bytesContracted`. - */ - static if (!(perCallFlags & - (Options.numDeallocate | Options.bytesContracted))) - bool deallocate(void[] b) - { return deallocateImpl(b); } - else - pragma(inline, true) // LDC: Must inline because of __FILE__ as template parameter - bool deallocate(string f = __FILE__, uint n = __LINE__)(void[] b) - { return deallocateImpl!(f, n)(b); } - - private bool deallocateImpl(string f = null, uint n = 0)(void[] b) - { - up!"numDeallocate"; - add!"bytesUsed"(-Signed!size_t(b.length)); - add!"bytesSlack"(-(this.goodAllocSize(b.length) - b.length)); - addPerCall!(f, n, "numDeallocate", "bytesContracted")(1, b.length); - static if (hasMember!(Allocator, "deallocate")) - return parent.deallocate(b); - else - return false; - } - - static if (hasMember!(Allocator, "deallocateAll")) - { - /** - Defined only if `Allocator.deallocateAll` is defined. Affects - per instance and per call `numDeallocateAll`. - */ - static if (!(perCallFlags & Options.numDeallocateAll)) - bool deallocateAll() - { return deallocateAllImpl(); } - else - pragma(inline, true) // LDC: Must inline because of __FILE__ as template parameter - bool deallocateAll(string f = __FILE__, uint n = __LINE__)() - { return deallocateAllImpl!(f, n)(); } - - private bool deallocateAllImpl(string f = null, uint n = 0)() - { - up!"numDeallocateAll"; - addPerCall!(f, n, "numDeallocateAll")(1); - static if ((flags & Options.bytesUsed)) - _bytesUsed = 0; - return parent.deallocateAll(); - } - } - - /** - Defined only if `Options.bytesUsed` is defined. Returns $(D bytesUsed == - 0). - */ - static if (flags & Options.bytesUsed) - pure nothrow @safe @nogc - Ternary empty() - { - return Ternary(_bytesUsed == 0); - } - - /** - Reports per instance statistics to `output` (e.g. `stdout`). The - format is simple: one kind and value per line, separated by a colon, e.g. - `bytesAllocated:7395404` - */ - void reportStatistics(R)(auto ref R output) - { - import std.conv : to; - import std.traits : EnumMembers; - foreach (e; EnumMembers!Options) - { - static if ((flags & e) && e != Options.numAll - && e != Options.bytesAll && e != Options.all) - output.write(e.to!string, ":", mixin(e.to!string), '\n'); - } - } - - static if (perCallFlags) - { - /** - Defined if `perCallFlags` is nonzero. - */ - struct PerCallStatistics - { - /// The file and line of the call. - string file; - /// Ditto - uint line; - /// The options corresponding to the statistics collected. - Options[] opts; - /// The values of the statistics. Has the same length as `opts`. - ulong[] values; - // Next in the chain. - private PerCallStatistics* next; - - /** - Format to a string such as: - $(D mymodule.d(655): [numAllocate:21, numAllocateOK:21, bytesAllocated:324202]). - */ - string toString() const - { - import std.conv : text, to; - auto result = text(file, "(", line, "): ["); - foreach (i, opt; opts) - { - if (i) result ~= ", "; - result ~= opt.to!string; - result ~= ':'; - result ~= values[i].to!string; - } - return result ~= "]"; - } - } - private static PerCallStatistics* root; - - /** - Defined if `perCallFlags` is nonzero. Iterates all monitored - file/line instances. The order of iteration is not meaningful (items - are inserted at the front of a list upon the first call), so - preprocessing the statistics after collection might be appropriate. - */ - static auto byFileLine() - { - static struct Voldemort - { - PerCallStatistics* current; - bool empty() { return !current; } - ref PerCallStatistics front() { return *current; } - void popFront() { current = current.next; } - auto save() { return this; } - } - return Voldemort(root); - } - - /** - Defined if `perCallFlags` is nonzero. Outputs (e.g. to a `File`) - a simple report of the collected per-call statistics. - */ - static void reportPerCallStatistics(R)(auto ref R output) - { - output.write("Stats for: ", StatsCollector.stringof, '\n'); - foreach (ref stat; byFileLine) - { - output.write(stat, '\n'); - } - } - - private PerCallStatistics* statsAt(string f, uint n, opts...)() - { - import std.array : array; - import std.range : repeat; - - static PerCallStatistics s = { f, n, [ opts ], - repeat(0UL, opts.length).array }; - static bool inserted; - - if (!inserted) - { - // Insert as root - s.next = root; - root = &s; - inserted = true; - } - return &s; - } - - private void addPerCall(string f, uint n, names...)(ulong[] values...) - { - import std.array : join; - enum ulong mask = mixin("Options."~[names].join("|Options.")); - static if (perCallFlags & mask) - { - // Per allocation info - auto ps = mixin("statsAt!(f, n," - ~ "Options."~[names].join(", Options.") - ~")"); - foreach (i; 0 .. names.length) - { - ps.values[i] += values[i]; - } - } - } - } - else - { - private void addPerCall(string f, uint n, names...)(ulong[]...) - { - } - } -} - -/// -@system unittest -{ - import std.experimental.allocator.building_blocks.free_list : FreeList; - import std.experimental.allocator.gc_allocator : GCAllocator; - alias Allocator = StatsCollector!(GCAllocator, Options.all, Options.all); - - Allocator alloc; - auto b = alloc.allocate(10); - alloc.reallocate(b, 20); - alloc.deallocate(b); - - import std.file : deleteme, remove; - import std.range : walkLength; - import std.stdio : File; - - auto f = deleteme ~ "-dlang.std.experimental.allocator.stats_collector.txt"; - scope(exit) remove(f); - Allocator.reportPerCallStatistics(File(f, "w")); - alloc.reportStatistics(File(f, "a")); - assert(File(f).byLine.walkLength == 24); -} - -@system unittest -{ - void test(Allocator)() - { - import std.range : walkLength; - import std.typecons : Ternary; - - Allocator a; - assert((() pure nothrow @safe @nogc => a.empty)() == Ternary.yes); - auto b1 = a.allocate(100); - assert(a.numAllocate == 1); - assert((() nothrow @safe => a.expand(b1, 0))()); - assert(a.reallocate(b1, b1.length + 1)); - auto b2 = a.allocate(101); - assert(a.numAllocate == 2); - assert(a.bytesAllocated == 202); - assert(a.bytesUsed == 202); - auto b3 = a.allocate(202); - assert(a.numAllocate == 3); - assert(a.bytesAllocated == 404); - assert((() pure nothrow @safe @nogc => a.empty)() == Ternary.no); - - () nothrow @nogc { a.deallocate(b2); }(); - assert(a.numDeallocate == 1); - () nothrow @nogc { a.deallocate(b1); }(); - assert(a.numDeallocate == 2); - () nothrow @nogc { a.deallocate(b3); }(); - assert(a.numDeallocate == 3); - assert(a.numAllocate == a.numDeallocate); - assert(a.bytesUsed == 0); - } - - import std.experimental.allocator.building_blocks.free_list : FreeList; - import std.experimental.allocator.gc_allocator : GCAllocator; - test!(StatsCollector!(GCAllocator, Options.all, Options.all)); - test!(StatsCollector!(FreeList!(GCAllocator, 128), Options.all, - Options.all)); -} - -@system unittest -{ - void test(Allocator)() - { - import std.range : walkLength; - Allocator a; - auto b1 = a.allocate(100); - assert((() nothrow @safe => a.expand(b1, 0))()); - assert(a.reallocate(b1, b1.length + 1)); - auto b2 = a.allocate(101); - auto b3 = a.allocate(202); - - () nothrow @nogc { a.deallocate(b2); }(); - () nothrow @nogc { a.deallocate(b1); }(); - () nothrow @nogc { a.deallocate(b3); }(); - } - import std.experimental.allocator.building_blocks.free_list : FreeList; - import std.experimental.allocator.gc_allocator : GCAllocator; - test!(StatsCollector!(GCAllocator, 0, 0)); -} - -@system unittest -{ - import std.experimental.allocator.gc_allocator : GCAllocator; - StatsCollector!(GCAllocator, 0, 0) a; - - // calls std.experimental.allocator.common.goodAllocSize - assert((() pure nothrow @safe @nogc => a.goodAllocSize(1))()); -} - -@system unittest -{ - import std.experimental.allocator.building_blocks.region : BorrowedRegion; - - auto a = StatsCollector!(BorrowedRegion!(), Options.all, Options.all)(BorrowedRegion!()(new ubyte[1024 * 64])); - auto b = a.allocate(42); - assert(b.length == 42); - // Test that reallocate infers from parent - assert((() nothrow @nogc => a.reallocate(b, 100))()); - assert(b.length == 100); - // Test that deallocateAll infers from parent - assert((() nothrow @nogc => a.deallocateAll())()); -} - -@system unittest -{ - import std.experimental.allocator.building_blocks.region : BorrowedRegion; - - auto a = StatsCollector!(BorrowedRegion!(), Options.all)(BorrowedRegion!()(new ubyte[1024 * 64])); - auto b = a.alignedAllocate(42, 128); - assert(b.length == 42); - assert(b.ptr.alignedAt(128)); - assert(a.numAlignedAllocate == 1); - assert(a.numAlignedAllocateOk == 1); - assert(a.bytesUsed == 42); - - b = a.alignedAllocate(23, 256); - assert(b.length == 23); - assert(b.ptr.alignedAt(256)); - assert(a.numAlignedAllocate == 2); - assert(a.numAlignedAllocateOk == 2); - assert(a.bytesUsed == 65); - - b = a.alignedAllocate(0, 512); - assert(b.length == 0); - assert(a.numAlignedAllocate == 3); - assert(a.numAlignedAllocateOk == 3); - assert(a.bytesUsed == 65); - - b = a.alignedAllocate(1024 * 1024, 512); - assert(b is null); - assert(a.numAlignedAllocate == 4); - assert(a.numAlignedAllocateOk == 3); - assert(a.bytesUsed == 65); -} diff --git a/phobos/std/experimental/allocator/common.d b/phobos/std/experimental/allocator/common.d deleted file mode 100644 index d2efe33..0000000 --- a/phobos/std/experimental/allocator/common.d +++ /dev/null @@ -1,779 +0,0 @@ -// Written in the D programming language. -/** -Utility and ancillary artifacts of `std.experimental.allocator`. This module -shouldn't be used directly; its functionality will be migrated into more -appropriate parts of `std`. - -Authors: $(HTTP erdani.com, Andrei Alexandrescu), Timon Gehr (`Ternary`) - -Source: $(PHOBOSSRC std/experimental/allocator/common.d) -*/ -module std.experimental.allocator.common; -import std.algorithm.comparison, std.traits; - -/** -Is `true` iff `A` is an allocator. - */ -enum isAllocator(A) = (is(typeof(A.allocate(size_t.init)) == void[]) && is(typeof(A.alignment) : size_t)); - -/// -@safe @nogc nothrow pure -unittest -{ - import std.experimental.allocator.building_blocks.null_allocator : NullAllocator; - import std.experimental.allocator.mallocator : Mallocator; - import std.experimental.allocator.gc_allocator : GCAllocator; - import std.experimental.allocator.mmap_allocator : MmapAllocator; - static assert(isAllocator!NullAllocator); - static assert(isAllocator!Mallocator); - static assert(isAllocator!GCAllocator); - static assert(isAllocator!MmapAllocator); - static assert(!isAllocator!int); -} - -/** -Returns the size in bytes of the state that needs to be allocated to hold an -object of type `T`. `stateSize!T` is zero for `struct`s that are not -nested and have no nonstatic member variables. - */ -template stateSize(T) -{ - static if (is(T == class) || is(T == interface)) - enum stateSize = __traits(classInstanceSize, T); - else static if (is(T == struct) || is(T == union)) - enum stateSize = Fields!T.length || isNested!T ? T.sizeof : 0; - else static if (is(T == void)) - enum size_t stateSize = 0; - else - enum stateSize = T.sizeof; -} - -@safe @nogc nothrow pure -unittest -{ - static assert(stateSize!void == 0); - struct A {} - static assert(stateSize!A == 0); - struct B { int x; } - static assert(stateSize!B == 4); - interface I1 {} - //static assert(stateSize!I1 == 2 * size_t.sizeof); - class C1 {} - static assert(stateSize!C1 == 3 * size_t.sizeof); - class C2 { char c; } - static assert(stateSize!C2 == 4 * size_t.sizeof); - static class C3 { char c; } - static assert(stateSize!C3 == 2 * size_t.sizeof + char.sizeof); -} - -/** -Returns `true` if the `Allocator` has the alignment known at compile time; -otherwise it returns `false`. - */ -template hasStaticallyKnownAlignment(Allocator) -{ - enum hasStaticallyKnownAlignment = __traits(compiles, - {enum x = Allocator.alignment;}); -} - -/** -`chooseAtRuntime` is a compile-time constant of type `size_t` that several -parameterized structures in this module recognize to mean deferral to runtime of -the exact value. For example, $(D BitmappedBlock!(Allocator, 4096)) (described in -detail below) defines a block allocator with block size of 4096 bytes, whereas -$(D BitmappedBlock!(Allocator, chooseAtRuntime)) defines a block allocator that has a -field storing the block size, initialized by the user. -*/ -enum chooseAtRuntime = size_t.max - 1; - -/** -`unbounded` is a compile-time constant of type `size_t` that several -parameterized structures in this module recognize to mean "infinite" bounds for -the parameter. For example, `Freelist` (described in detail below) accepts a -`maxNodes` parameter limiting the number of freelist items. If `unbounded` -is passed for `maxNodes`, then there is no limit and no checking for the -number of nodes. -*/ -enum unbounded = size_t.max; - -/** -The alignment that is guaranteed to accommodate any D object allocation on the -current platform. -*/ -enum uint platformAlignment = std.algorithm.comparison.max(double.alignof, real.alignof); - -/** -The default good size allocation is deduced as `n` rounded up to the -allocator's alignment. -*/ -size_t goodAllocSize(A)(auto ref A a, size_t n) -{ - return n.roundUpToMultipleOf(a.alignment); -} - -/* -Returns s rounded up to a multiple of base. -*/ -@safe @nogc nothrow pure -package size_t roundUpToMultipleOf(size_t s, uint base) -{ - assert(base); - auto rem = s % base; - return rem ? s + base - rem : s; -} - -@safe @nogc nothrow pure -unittest -{ - assert(10.roundUpToMultipleOf(11) == 11); - assert(11.roundUpToMultipleOf(11) == 11); - assert(12.roundUpToMultipleOf(11) == 22); - assert(118.roundUpToMultipleOf(11) == 121); -} - -/* -Returns `n` rounded up to a multiple of alignment, which must be a power of 2. -*/ -@safe @nogc nothrow pure -package size_t roundUpToAlignment(size_t n, uint alignment) -{ - import std.math.traits : isPowerOf2; - assert(alignment.isPowerOf2); - immutable uint slack = cast(uint) n & (alignment - 1); - const result = slack - ? n + alignment - slack - : n; - assert(result >= n); - return result; -} - -@safe @nogc nothrow pure -unittest -{ - assert(10.roundUpToAlignment(4) == 12); - assert(11.roundUpToAlignment(2) == 12); - assert(12.roundUpToAlignment(8) == 16); - assert(118.roundUpToAlignment(64) == 128); -} - -/* -Returns `n` rounded down to a multiple of alignment, which must be a power of 2. -*/ -@safe @nogc nothrow pure -package size_t roundDownToAlignment(size_t n, uint alignment) -{ - import std.math.traits : isPowerOf2; - assert(alignment.isPowerOf2); - return n & ~size_t(alignment - 1); -} - -@safe @nogc nothrow pure -unittest -{ - assert(10.roundDownToAlignment(4) == 8); - assert(11.roundDownToAlignment(2) == 10); - assert(12.roundDownToAlignment(8) == 8); - assert(63.roundDownToAlignment(64) == 0); -} - -/* -Advances the beginning of `b` to start at alignment `a`. The resulting buffer -may therefore be shorter. Returns the adjusted buffer, or null if obtaining a -non-empty buffer is impossible. -*/ -@nogc nothrow pure -package void[] roundUpToAlignment(void[] b, uint a) -{ - auto e = b.ptr + b.length; - auto p = cast(void*) roundUpToAlignment(cast(size_t) b.ptr, a); - if (e <= p) return null; - return p[0 .. e - p]; -} - -@nogc nothrow pure -@system unittest -{ - void[] empty; - assert(roundUpToAlignment(empty, 4) == null); - char[128] buf; - // At least one pointer inside buf is 128-aligned - assert(roundUpToAlignment(buf, 128) !is null); -} - -/* -Like `a / b` but rounds the result up, not down. -*/ -@safe @nogc nothrow pure -package size_t divideRoundUp(size_t a, size_t b) -{ - assert(b); - return (a + b - 1) / b; -} - -/* -Returns `s` rounded up to a multiple of `base`. -*/ -@nogc nothrow pure -package void[] roundStartToMultipleOf(void[] s, uint base) -{ - assert(base); - auto p = cast(void*) roundUpToMultipleOf( - cast(size_t) s.ptr, base); - auto end = s.ptr + s.length; - return p[0 .. end - p]; -} - -nothrow pure -@system unittest -{ - void[] p; - assert(roundStartToMultipleOf(p, 16) is null); - p = new ulong[10]; - assert(roundStartToMultipleOf(p, 16) is p); -} - -/* -Returns `s` rounded up to the nearest power of 2. -*/ -@safe @nogc nothrow pure -package size_t roundUpToPowerOf2(size_t s) -{ - import std.meta : AliasSeq; - assert(s <= (size_t.max >> 1) + 1); - --s; - static if (size_t.sizeof == 4) - alias Shifts = AliasSeq!(1, 2, 4, 8, 16); - else - alias Shifts = AliasSeq!(1, 2, 4, 8, 16, 32); - foreach (i; Shifts) - { - s |= s >> i; - } - return s + 1; -} - -@safe @nogc nothrow pure -unittest -{ - assert(0.roundUpToPowerOf2 == 0); - assert(1.roundUpToPowerOf2 == 1); - assert(2.roundUpToPowerOf2 == 2); - assert(3.roundUpToPowerOf2 == 4); - assert(7.roundUpToPowerOf2 == 8); - assert(8.roundUpToPowerOf2 == 8); - assert(10.roundUpToPowerOf2 == 16); - assert(11.roundUpToPowerOf2 == 16); - assert(12.roundUpToPowerOf2 == 16); - assert(118.roundUpToPowerOf2 == 128); - assert((size_t.max >> 1).roundUpToPowerOf2 == (size_t.max >> 1) + 1); - assert(((size_t.max >> 1) + 1).roundUpToPowerOf2 == (size_t.max >> 1) + 1); -} - -/* -Returns the number of trailing zeros of `x`. -*/ -@safe @nogc nothrow pure -package uint trailingZeros(ulong x) -{ - import core.bitop : bsf; - return x == 0 ? 64 : bsf(x); -} - -@safe @nogc nothrow pure -unittest -{ - assert(trailingZeros(0) == 64); - assert(trailingZeros(1) == 0); - assert(trailingZeros(2) == 1); - assert(trailingZeros(3) == 0); - assert(trailingZeros(4) == 2); -} - -/* -Returns `true` if `ptr` is aligned at `alignment`. -*/ -@nogc nothrow pure -package bool alignedAt(T)(T* ptr, uint alignment) -{ - return cast(size_t) ptr % alignment == 0; -} - -/* -Returns the effective alignment of `ptr`, i.e. the largest power of two that is -a divisor of `ptr`. -*/ -@nogc nothrow pure -package size_t effectiveAlignment(void* ptr) -{ - return (cast(size_t) 1) << trailingZeros(cast(size_t) ptr); -} - -@nogc nothrow pure -@system unittest -{ - int x; - assert(effectiveAlignment(&x) >= int.alignof); - - const max = (cast(size_t) 1) << (size_t.sizeof * 8 - 1); - assert(effectiveAlignment(cast(void*) max) == max); -} - -/* -Aligns a pointer down to a specified alignment. The resulting pointer is less -than or equal to the given pointer. -*/ -@nogc nothrow pure -package void* alignDownTo(return scope void* ptr, uint alignment) -{ - import std.math.traits : isPowerOf2; - assert(alignment.isPowerOf2); - return cast(void*) (cast(size_t) ptr & ~(alignment - 1UL)); -} - -/* -Aligns a pointer up to a specified alignment. The resulting pointer is greater -than or equal to the given pointer. -*/ -@nogc nothrow pure -package void* alignUpTo(return scope void* ptr, uint alignment) -{ - import std.math.traits : isPowerOf2; - assert(alignment.isPowerOf2); - immutable uint slack = cast(size_t) ptr & (alignment - 1U); - return slack ? ptr + alignment - slack : ptr; -} - -@safe @nogc nothrow pure -package bool isGoodStaticAlignment(uint x) -{ - import std.math.traits : isPowerOf2; - return x.isPowerOf2; -} - -@safe @nogc nothrow pure -package bool isGoodDynamicAlignment(uint x) -{ - import std.math.traits : isPowerOf2; - return x.isPowerOf2 && x >= (void*).sizeof; -} - -/** -The default `reallocate` function first attempts to use `expand`. If $(D -Allocator.expand) is not defined or returns `false`, `reallocate` -allocates a new block of memory of appropriate size and copies data from the old -block to the new block. Finally, if `Allocator` defines `deallocate`, $(D -reallocate) uses it to free the old memory block. - -`reallocate` does not attempt to use `Allocator.reallocate` even if -defined. This is deliberate so allocators may use it internally within their own -implementation of `reallocate`. - -*/ -bool reallocate(Allocator)(ref Allocator a, ref void[] b, size_t s) -{ - if (b.length == s) return true; - static if (hasMember!(Allocator, "expand")) - { - if (b.length <= s && a.expand(b, s - b.length)) return true; - } - auto newB = a.allocate(s); - if (newB.length != s) return false; - if (newB.length <= b.length) newB[] = b[0 .. newB.length]; - else newB[0 .. b.length] = b[]; - static if (hasMember!(Allocator, "deallocate")) - a.deallocate(b); - b = newB; - return true; -} - -/** - -The default `alignedReallocate` function first attempts to use `expand`. -If `Allocator.expand` is not defined or returns `false`, $(D -alignedReallocate) allocates a new block of memory of appropriate size and -copies data from the old block to the new block. Finally, if `Allocator` -defines `deallocate`, `alignedReallocate` uses it to free the old memory -block. - -`alignedReallocate` does not attempt to use `Allocator.reallocate` even if -defined. This is deliberate so allocators may use it internally within their own -implementation of `reallocate`. - -*/ -bool alignedReallocate(Allocator)(ref Allocator alloc, - ref void[] b, size_t s, uint a) -if (hasMember!(Allocator, "alignedAllocate")) -{ - static if (hasMember!(Allocator, "expand")) - { - if (b.length <= s && b.ptr.alignedAt(a) - && alloc.expand(b, s - b.length)) return true; - } - else - { - if (b.length == s && b.ptr.alignedAt(a)) return true; - } - auto newB = alloc.alignedAllocate(s, a); - if (newB.length != s) return false; - if (newB.length <= b.length) newB[] = b[0 .. newB.length]; - else newB[0 .. b.length] = b[]; - static if (hasMember!(Allocator, "deallocate")) - alloc.deallocate(b); - b = newB; - return true; -} - -@system unittest -{ - bool called = false; - struct DummyAllocator - { - void[] alignedAllocate(size_t size, uint alignment) - { - called = true; - return null; - } - } - - struct DummyAllocatorExpand - { - void[] alignedAllocate(size_t size, uint alignment) - { - return null; - } - - bool expand(ref void[] b, size_t length) - { - called = true; - return true; - } - } - - char[128] buf; - uint alignment = 32; - auto alignedPtr = roundUpToMultipleOf(cast(size_t) buf.ptr, alignment); - auto diff = alignedPtr - cast(size_t) buf.ptr; - - // Align the buffer to 'alignment' - void[] b = cast(void[]) (buf.ptr + diff)[0 .. buf.length - diff]; - - DummyAllocator a1; - // Ask for same length and alignment, should not call 'alignedAllocate' - assert(alignedReallocate(a1, b, b.length, alignment)); - assert(!called); - - // Ask for same length, different alignment - // should call 'alignedAllocate' if not aligned to new value - alignedReallocate(a1, b, b.length, alignment + 1); - assert(b.ptr.alignedAt(alignment + 1) || called); - called = false; - - DummyAllocatorExpand a2; - // Ask for bigger length, same alignment, should call 'expand' - assert(alignedReallocate(a2, b, b.length + 1, alignment)); - assert(called); - called = false; - - // Ask for bigger length, different alignment - // should call 'alignedAllocate' if not aligned to new value - alignedReallocate(a2, b, b.length + 1, alignment + 1); - assert(b.ptr.alignedAt(alignment + 1) || !called); -} - -/** -Forwards each of the methods in `funs` (if defined) to `member`. -*/ -/*package*/ string forwardToMember(string member, string[] funs...) -{ - string result = " import std.traits : hasMember, Parameters;\n"; - foreach (fun; funs) - { - result ~= " - static if (hasMember!(typeof("~member~"), `"~fun~"`)) - auto ref "~fun~"(Parameters!(typeof("~member~"."~fun~")) args) - { - return "~member~"."~fun~"(args); - }\n"; - } - return result; -} - -version (StdUnittest) -{ - - package void testAllocator(alias make)() - { - import std.conv : text; - import std.math.traits : isPowerOf2; - import std.stdio : writeln, stderr; - import std.typecons : Ternary; - alias A = typeof(make()); - scope(failure) stderr.writeln("testAllocator failed for ", A.stringof); - - auto a = make(); - - // Test alignment - static assert(A.alignment.isPowerOf2); - - // Test goodAllocSize - assert(a.goodAllocSize(1) >= A.alignment, - text(a.goodAllocSize(1), " < ", A.alignment)); - assert(a.goodAllocSize(11) >= 11.roundUpToMultipleOf(A.alignment)); - assert(a.goodAllocSize(111) >= 111.roundUpToMultipleOf(A.alignment)); - - // Test allocate - assert(a.allocate(0) is null); - - auto b1 = a.allocate(1); - assert(b1.length == 1); - auto b2 = a.allocate(2); - assert(b2.length == 2); - assert(b2.ptr + b2.length <= b1.ptr || b1.ptr + b1.length <= b2.ptr); - - // Test allocateZeroed - static if (hasMember!(A, "allocateZeroed")) - {{ - auto b3 = a.allocateZeroed(8); - if (b3 !is null) - { - assert(b3.length == 8); - foreach (e; cast(ubyte[]) b3) - assert(e == 0); - } - }} - - // Test alignedAllocate - static if (hasMember!(A, "alignedAllocate")) - {{ - auto b3 = a.alignedAllocate(1, 256); - assert(b3.length <= 1); - assert(b3.ptr.alignedAt(256)); - assert(a.alignedReallocate(b3, 2, 512)); - assert(b3.ptr.alignedAt(512)); - static if (hasMember!(A, "alignedDeallocate")) - { - a.alignedDeallocate(b3); - } - }} - else - { - static assert(!hasMember!(A, "alignedDeallocate")); - // This seems to be a bug in the compiler: - //static assert(!hasMember!(A, "alignedReallocate"), A.stringof); - } - - static if (hasMember!(A, "allocateAll")) - {{ - auto aa = make(); - if (aa.allocateAll().ptr) - { - // Can't get any more memory - assert(!aa.allocate(1).ptr); - } - auto ab = make(); - const b4 = ab.allocateAll(); - assert(b4.length); - // Can't get any more memory - assert(!ab.allocate(1).ptr); - }} - - static if (hasMember!(A, "expand")) - {{ - assert(a.expand(b1, 0)); - auto len = b1.length; - if (a.expand(b1, 102)) - { - assert(b1.length == len + 102, text(b1.length, " != ", len + 102)); - } - auto aa = make(); - void[] b5 = null; - assert(aa.expand(b5, 0)); - assert(b5 is null); - assert(!aa.expand(b5, 1)); - assert(b5.length == 0); - }} - - void[] b6 = null; - assert(a.reallocate(b6, 0)); - assert(b6.length == 0); - assert(a.reallocate(b6, 1)); - assert(b6.length == 1, text(b6.length)); - assert(a.reallocate(b6, 2)); - assert(b6.length == 2); - - // Test owns - static if (hasMember!(A, "owns")) - {{ - assert(a.owns(null) == Ternary.no); - assert(a.owns(b1) == Ternary.yes); - assert(a.owns(b2) == Ternary.yes); - assert(a.owns(b6) == Ternary.yes); - }} - - static if (hasMember!(A, "resolveInternalPointer")) - {{ - void[] p; - assert(a.resolveInternalPointer(null, p) == Ternary.no); - Ternary r = a.resolveInternalPointer(b1.ptr, p); - assert(p.ptr is b1.ptr && p.length >= b1.length); - r = a.resolveInternalPointer(b1.ptr + b1.length / 2, p); - assert(p.ptr is b1.ptr && p.length >= b1.length); - r = a.resolveInternalPointer(b2.ptr, p); - assert(p.ptr is b2.ptr && p.length >= b2.length); - r = a.resolveInternalPointer(b2.ptr + b2.length / 2, p); - assert(p.ptr is b2.ptr && p.length >= b2.length); - r = a.resolveInternalPointer(b6.ptr, p); - assert(p.ptr is b6.ptr && p.length >= b6.length); - r = a.resolveInternalPointer(b6.ptr + b6.length / 2, p); - assert(p.ptr is b6.ptr && p.length >= b6.length); - static int[10] b7 = [ 1, 2, 3 ]; - assert(a.resolveInternalPointer(b7.ptr, p) == Ternary.no); - assert(a.resolveInternalPointer(b7.ptr + b7.length / 2, p) == Ternary.no); - assert(a.resolveInternalPointer(b7.ptr + b7.length, p) == Ternary.no); - int[3] b8 = [ 1, 2, 3 ]; - assert(a.resolveInternalPointer(b8.ptr, p) == Ternary.no); - assert(a.resolveInternalPointer(b8.ptr + b8.length / 2, p) == Ternary.no); - assert(a.resolveInternalPointer(b8.ptr + b8.length, p) == Ternary.no); - }} - } - - package void testAllocatorObject(RCAllocInterface)(RCAllocInterface a) - { - // this used to be a template constraint, but moving it inside prevents - // unnecessary import of std.experimental.allocator - import std.experimental.allocator : RCIAllocator, RCISharedAllocator; - static assert(is(RCAllocInterface == RCIAllocator) - || is (RCAllocInterface == RCISharedAllocator)); - - import std.conv : text; - import std.math.traits : isPowerOf2; - import std.stdio : writeln, stderr; - import std.typecons : Ternary; - scope(failure) stderr.writeln("testAllocatorObject failed for ", - RCAllocInterface.stringof); - - assert(!a.isNull); - - // Test alignment - assert(a.alignment.isPowerOf2); - - // Test goodAllocSize - assert(a.goodAllocSize(1) >= a.alignment, - text(a.goodAllocSize(1), " < ", a.alignment)); - assert(a.goodAllocSize(11) >= 11.roundUpToMultipleOf(a.alignment)); - assert(a.goodAllocSize(111) >= 111.roundUpToMultipleOf(a.alignment)); - - // Test empty - assert(a.empty != Ternary.no); - - // Test allocate - assert(a.allocate(0) is null); - - auto b1 = a.allocate(1); - assert(b1.length == 1); - auto b2 = a.allocate(2); - assert(b2.length == 2); - assert(b2.ptr + b2.length <= b1.ptr || b1.ptr + b1.length <= b2.ptr); - - // Test alignedAllocate - { - // If not implemented it will return null, so those should pass - auto b3 = a.alignedAllocate(1, 256); - assert(b3.length <= 1); - assert(b3.ptr.alignedAt(256)); - if (a.alignedReallocate(b3, 1, 256)) - { - // If it is false, then the wrapped allocator did not implement - // this - assert(a.alignedReallocate(b3, 2, 512)); - assert(b3.ptr.alignedAt(512)); - } - } - - // Test allocateAll - { - auto aa = a.allocateAll(); - if (aa.ptr) - { - // Can't get any more memory - assert(!a.allocate(1).ptr); - a.deallocate(aa); - } - const b4 = a.allocateAll(); - if (b4.ptr) - { - // Can't get any more memory - assert(!a.allocate(1).ptr); - } - } - - // Test expand - { - assert(a.expand(b1, 0)); - auto len = b1.length; - if (a.expand(b1, 102)) - { - assert(b1.length == len + 102, text(b1.length, " != ", len + 102)); - } - } - - void[] b6 = null; - assert(a.reallocate(b6, 0)); - assert(b6.length == 0); - assert(a.reallocate(b6, 1)); - assert(b6.length == 1, text(b6.length)); - assert(a.reallocate(b6, 2)); - assert(b6.length == 2); - - // Test owns - { - if (a.owns(null) != Ternary.unknown) - { - assert(a.owns(null) == Ternary.no); - assert(a.owns(b1) == Ternary.yes); - assert(a.owns(b2) == Ternary.yes); - assert(a.owns(b6) == Ternary.yes); - } - } - - // Test resolveInternalPointer - { - void[] p; - if (a.resolveInternalPointer(null, p) != Ternary.unknown) - { - assert(a.resolveInternalPointer(null, p) == Ternary.no); - Ternary r = a.resolveInternalPointer(b1.ptr, p); - assert(p.ptr is b1.ptr && p.length >= b1.length); - r = a.resolveInternalPointer(b1.ptr + b1.length / 2, p); - assert(p.ptr is b1.ptr && p.length >= b1.length); - r = a.resolveInternalPointer(b2.ptr, p); - assert(p.ptr is b2.ptr && p.length >= b2.length); - r = a.resolveInternalPointer(b2.ptr + b2.length / 2, p); - assert(p.ptr is b2.ptr && p.length >= b2.length); - r = a.resolveInternalPointer(b6.ptr, p); - assert(p.ptr is b6.ptr && p.length >= b6.length); - r = a.resolveInternalPointer(b6.ptr + b6.length / 2, p); - assert(p.ptr is b6.ptr && p.length >= b6.length); - static int[10] b7 = [ 1, 2, 3 ]; - assert(a.resolveInternalPointer(b7.ptr, p) == Ternary.no); - assert(a.resolveInternalPointer(b7.ptr + b7.length / 2, p) == Ternary.no); - assert(a.resolveInternalPointer(b7.ptr + b7.length, p) == Ternary.no); - int[3] b8 = [ 1, 2, 3 ]; - assert(a.resolveInternalPointer(b8.ptr, p) == Ternary.no); - assert(a.resolveInternalPointer(b8.ptr + b8.length / 2, p) == Ternary.no); - assert(a.resolveInternalPointer(b8.ptr + b8.length, p) == Ternary.no); - } - } - - // Test deallocateAll - { - if (a.deallocateAll()) - { - if (a.empty != Ternary.unknown) - { - assert(a.empty == Ternary.yes); - } - } - } - } -} diff --git a/phobos/std/experimental/allocator/gc_allocator.d b/phobos/std/experimental/allocator/gc_allocator.d deleted file mode 100644 index d356c73..0000000 --- a/phobos/std/experimental/allocator/gc_allocator.d +++ /dev/null @@ -1,199 +0,0 @@ -// Written in the D programming language. -/** -D's built-in garbage-collected allocator. - -Source: $(PHOBOSSRC std/experimental/allocator/_gc_allocator.d) -*/ -module std.experimental.allocator.gc_allocator; -import std.experimental.allocator.common; - -/** -D's built-in garbage-collected allocator. -*/ -struct GCAllocator -{ - import core.memory : GC; - import std.typecons : Ternary; - version (StdUnittest) @system unittest { testAllocator!(() => GCAllocator.instance); } - - /** - The alignment is a static constant equal to `platformAlignment`, which - ensures proper alignment for any D data type. - */ - enum uint alignment = platformAlignment; - - /** - Standard allocator methods per the semantics defined above. The $(D - deallocate) and `reallocate` methods are `@system` because they may - move memory around, leaving dangling pointers in user code. - */ - pure nothrow @trusted void[] allocate(size_t bytes) shared const - { - if (!bytes) return null; - auto p = GC.malloc(bytes); - return p ? p[0 .. bytes] : null; - } - - /// Ditto - pure nothrow @trusted bool expand(ref void[] b, size_t delta) shared const - { - if (delta == 0) return true; - if (b is null) return false; - immutable curLength = GC.sizeOf(b.ptr); - assert(curLength != 0); // we have a valid GC pointer here - immutable desired = b.length + delta; - if (desired > curLength) // check to see if the current block can't hold the data - { - immutable sizeRequest = desired - curLength; - immutable newSize = GC.extend(b.ptr, sizeRequest, sizeRequest); - if (newSize == 0) - { - // expansion unsuccessful - return false; - } - assert(newSize >= desired); - } - b = b.ptr[0 .. desired]; - return true; - } - - /// Ditto - pure nothrow @system bool reallocate(ref void[] b, size_t newSize) shared const - { - import core.exception : OutOfMemoryError; - try - { - auto p = cast(ubyte*) GC.realloc(b.ptr, newSize); - b = p[0 .. newSize]; - } - catch (OutOfMemoryError) - { - // leave the block in place, tell caller - return false; - } - return true; - } - - /// Ditto - pure nothrow @trusted @nogc - Ternary resolveInternalPointer(const void* p, ref void[] result) shared const - { - auto r = GC.addrOf(cast(void*) p); - if (!r) return Ternary.no; - result = r[0 .. GC.sizeOf(r)]; - return Ternary.yes; - } - - /// Ditto - pure nothrow @system @nogc - bool deallocate(void[] b) shared const - { - GC.free(b.ptr); - return true; - } - - /// Ditto - pure nothrow @safe @nogc - size_t goodAllocSize(size_t n) shared const - { - if (n == 0) - return 0; - if (n <= 16) - return 16; - - import core.bitop : bsr; - - auto largestBit = bsr(n-1) + 1; - if (largestBit <= 12) // 4096 or less - return size_t(1) << largestBit; - - // larger, we use a multiple of 4096. - return ((n + 4095) / 4096) * 4096; - } - - package pure nothrow @trusted void[] allocateZeroed()(size_t bytes) shared const - { - if (!bytes) return null; - auto p = GC.calloc(bytes); - return p ? p[0 .. bytes] : null; - } - - /** - Returns the global instance of this allocator type. The garbage collected - allocator is thread-safe, therefore all of its methods and `instance` itself - are `shared`. - */ - - static shared const GCAllocator instance; - - // Leave it undocummented for now. - nothrow @trusted void collect() shared const - { - GC.collect(); - } -} - -/// -pure @system unittest -{ - auto buffer = GCAllocator.instance.allocate(1024 * 1024 * 4); - // deallocate upon scope's end (alternatively: leave it to collection) - scope(exit) GCAllocator.instance.deallocate(buffer); - //... -} - -pure @safe unittest -{ - auto b = GCAllocator.instance.allocate(10_000); - assert(GCAllocator.instance.expand(b, 1)); -} - -pure @system unittest -{ - import core.memory : GC; - import std.typecons : Ternary; - - // test allocation sizes - assert((() nothrow @safe @nogc => GCAllocator.instance.goodAllocSize(1))() == 16); - for (size_t s = 16; s <= 8192; s *= 2) - { - assert((() nothrow @safe @nogc => GCAllocator.instance.goodAllocSize(s))() == s); - assert((() nothrow @safe @nogc => GCAllocator.instance.goodAllocSize(s - (s / 2) + 1))() == s); - - auto buffer = GCAllocator.instance.allocate(s); - scope(exit) () nothrow @nogc { GCAllocator.instance.deallocate(buffer); }(); - - void[] p; - assert((() nothrow @safe => GCAllocator.instance.resolveInternalPointer(null, p))() == Ternary.no); - assert((() nothrow @safe => GCAllocator.instance.resolveInternalPointer(&buffer[0], p))() == Ternary.yes); - assert(p.ptr is buffer.ptr && p.length >= buffer.length); - - assert(GC.sizeOf(buffer.ptr) == s); - - // the GC should provide power of 2 as "good" sizes, but other sizes are allowed, too - version (none) - { - auto buffer2 = GCAllocator.instance.allocate(s - (s / 2) + 1); - scope(exit) () nothrow @nogc { GCAllocator.instance.deallocate(buffer2); }(); - - assert(GC.sizeOf(buffer2.ptr) == s); - } - } - - // anything above a page is simply rounded up to next page - assert((() nothrow @safe @nogc => GCAllocator.instance.goodAllocSize(4096 * 4 + 1))() == 4096 * 5); -} - -pure nothrow @safe unittest -{ - import std.typecons : Ternary; - - void[] buffer = GCAllocator.instance.allocate(42); - void[] result; - Ternary found = GCAllocator.instance.resolveInternalPointer(&buffer[0], result); - - assert(found == Ternary.yes && &result[0] == &buffer[0] && result.length >= buffer.length); - assert(GCAllocator.instance.resolveInternalPointer(null, result) == Ternary.no); - void *badPtr = (() @trusted => cast(void*)(0xdeadbeef))(); - assert(GCAllocator.instance.resolveInternalPointer(badPtr, result) == Ternary.no); -} diff --git a/phobos/std/experimental/allocator/mallocator.d b/phobos/std/experimental/allocator/mallocator.d deleted file mode 100644 index 02d5cf8..0000000 --- a/phobos/std/experimental/allocator/mallocator.d +++ /dev/null @@ -1,448 +0,0 @@ -// Written in the D programming language. -/** -The C heap allocator. - -Source: $(PHOBOSSRC std/experimental/allocator/mallocator.d) -*/ -module std.experimental.allocator.mallocator; -import std.experimental.allocator.common; - -/** - The C heap allocator. - */ -struct Mallocator -{ - version (StdUnittest) @system unittest { testAllocator!(() => Mallocator.instance); } - - /** - The alignment is a static constant equal to `platformAlignment`, which - ensures proper alignment for any D data type. - */ - enum uint alignment = platformAlignment; - - /** - Standard allocator methods per the semantics defined above. The - `deallocate` and `reallocate` methods are `@system` because they - may move memory around, leaving dangling pointers in user code. Somewhat - paradoxically, `malloc` is `@safe` but that's only useful to safe - programs that can afford to leak memory allocated. - */ - @trusted @nogc nothrow pure - void[] allocate(size_t bytes) shared const - { - import core.memory : pureMalloc; - if (!bytes) return null; - auto p = pureMalloc(bytes); - return p ? p[0 .. bytes] : null; - } - - /// Ditto - @system @nogc nothrow pure - bool deallocate(void[] b) shared const - { - import core.memory : pureFree; - pureFree(b.ptr); - return true; - } - - /// Ditto - @system @nogc nothrow pure - bool reallocate(ref void[] b, size_t s) shared const - { - import core.memory : pureRealloc; - if (!s) - { - // fuzzy area in the C standard, see http://goo.gl/ZpWeSE - // so just deallocate and nullify the pointer - deallocate(b); - b = null; - return true; - } - auto p = cast(ubyte*) pureRealloc(b.ptr, s); - if (!p) return false; - b = p[0 .. s]; - return true; - } - - @trusted @nogc nothrow pure - package void[] allocateZeroed()(size_t bytes) shared const - { - import core.memory : pureCalloc; - if (!bytes) return null; - auto p = pureCalloc(1, bytes); - return p ? p[0 .. bytes] : null; - } - - /** - Returns the global instance of this allocator type. The C heap allocator is - thread-safe, therefore all of its methods and `it` itself are - `shared`. - */ - static shared Mallocator instance; -} - -/// -@nogc @system nothrow unittest -{ - auto buffer = Mallocator.instance.allocate(1024 * 1024 * 4); - scope(exit) Mallocator.instance.deallocate(buffer); - //... -} - -@nogc @system nothrow pure unittest -{ - @nogc nothrow pure - static void test(A)() - { - int* p = null; - p = cast(int*) A.instance.allocate(int.sizeof); - scope(exit) () nothrow @nogc { A.instance.deallocate(p[0 .. int.sizeof]); }(); - *p = 42; - assert(*p == 42); - } - test!Mallocator(); -} - -@nogc @system nothrow pure unittest -{ - static void test(A)() - { - import std.experimental.allocator : make; - Object p = null; - p = A.instance.make!Object(); - assert(p !is null); - } - - test!Mallocator(); -} - -version (Windows) -{ - // DMD Win 32 bit, DigitalMars C standard library misses the _aligned_xxx - // functions family (snn.lib) - version (CRuntime_DigitalMars) - { - // Helper to cast the infos written before the aligned pointer - // this header keeps track of the size (required to realloc) and of - // the base ptr (required to free). - private struct AlignInfo - { - void* basePtr; - size_t size; - - @nogc nothrow - static AlignInfo* opCall(void* ptr) - { - return cast(AlignInfo*) (ptr - AlignInfo.sizeof); - } - } - - @nogc nothrow - private void* _aligned_malloc(size_t size, size_t alignment) - { - import core.stdc.stdlib : malloc; - size_t offset = alignment + size_t.sizeof * 2 - 1; - - // unaligned chunk - void* basePtr = malloc(size + offset); - if (!basePtr) return null; - - // get aligned location within the chunk - void* alignedPtr = cast(void**)((cast(size_t)(basePtr) + offset) - & ~(alignment - 1)); - - // write the header before the aligned pointer - AlignInfo* head = AlignInfo(alignedPtr); - head.basePtr = basePtr; - head.size = size; - - return alignedPtr; - } - - @nogc nothrow - private void* _aligned_realloc(void* ptr, size_t size, size_t alignment) - { - import core.stdc.stdlib : free; - import core.stdc.string : memcpy; - - if (!ptr) return _aligned_malloc(size, alignment); - - // gets the header from the exising pointer - AlignInfo* head = AlignInfo(ptr); - - // gets a new aligned pointer - void* alignedPtr = _aligned_malloc(size, alignment); - if (!alignedPtr) - { - //to https://msdn.microsoft.com/en-us/library/ms235462.aspx - //see Return value: in this case the original block is unchanged - return null; - } - - // copy exising data - memcpy(alignedPtr, ptr, head.size); - free(head.basePtr); - - return alignedPtr; - } - - @nogc nothrow - private void _aligned_free(void *ptr) - { - import core.stdc.stdlib : free; - if (!ptr) return; - AlignInfo* head = AlignInfo(ptr); - free(head.basePtr); - } - - } - // DMD Win 64 bit, uses microsoft standard C library which implements them - else - { - @nogc nothrow private extern(C) void* _aligned_malloc(size_t, size_t); - @nogc nothrow private extern(C) void _aligned_free(void *memblock); - @nogc nothrow private extern(C) void* _aligned_realloc(void *, size_t, size_t); - } -} - -/** - Aligned allocator using OS-specific primitives, under a uniform API. - */ -struct AlignedMallocator -{ - version (StdUnittest) @system unittest { testAllocator!(() => typeof(this).instance); } - - /** - The default alignment is `platformAlignment`. - */ - enum uint alignment = platformAlignment; - - /** - Forwards to $(D alignedAllocate(bytes, platformAlignment)). - */ - @trusted @nogc nothrow - void[] allocate(size_t bytes) shared - { - if (!bytes) return null; - return alignedAllocate(bytes, alignment); - } - - /** - Uses $(HTTP man7.org/linux/man-pages/man3/posix_memalign.3.html, - `posix_memalign`) on Posix and - $(HTTP msdn.microsoft.com/en-us/library/8z34s9c6(v=vs.80).aspx, - `__aligned_malloc`) on Windows. - */ - version (Posix) - @trusted @nogc nothrow - void[] alignedAllocate(size_t bytes, uint a) shared - { - import core.stdc.errno : ENOMEM, EINVAL; - import core.sys.posix.stdlib : posix_memalign; - assert(a.isGoodDynamicAlignment); - void* result; - auto code = posix_memalign(&result, a, bytes); - -version (OSX) -version (LDC_AddressSanitizer) -{ - // The return value with AddressSanitizer may be -1 instead of ENOMEM - // or EINVAL. See https://bugs.llvm.org/show_bug.cgi?id=36510 - if (code == -1) - return null; -} - if (code == ENOMEM) - return null; - - else if (code == EINVAL) - { - assert(0, "AlignedMallocator.alignment is not a power of two " - ~"multiple of (void*).sizeof, according to posix_memalign!"); - } - else if (code != 0) - assert(0, "posix_memalign returned an unknown code!"); - - else - return result[0 .. bytes]; - } - else version (Windows) - @trusted @nogc nothrow - void[] alignedAllocate(size_t bytes, uint a) shared - { - auto result = _aligned_malloc(bytes, a); - return result ? result[0 .. bytes] : null; - } - else static assert(0); - - /** - Calls `free(b.ptr)` on Posix and - $(HTTP msdn.microsoft.com/en-US/library/17b5h8td(v=vs.80).aspx, - `__aligned_free(b.ptr)`) on Windows. - */ - version (Posix) - @system @nogc nothrow - bool deallocate(void[] b) shared - { - import core.stdc.stdlib : free; - free(b.ptr); - return true; - } - else version (Windows) - @system @nogc nothrow - bool deallocate(void[] b) shared - { - _aligned_free(b.ptr); - return true; - } - else static assert(0); - - /** - Forwards to $(D alignedReallocate(b, newSize, platformAlignment)). - Should be used with blocks obtained with `allocate` otherwise the custom - alignment passed with `alignedAllocate` can be lost. - */ - @system @nogc nothrow - bool reallocate(ref void[] b, size_t newSize) shared - { - return alignedReallocate(b, newSize, alignment); - } - - /** - On Posix there is no `realloc` for aligned memory, so `alignedReallocate` emulates - the needed behavior by using `alignedAllocate` to get a new block. The existing - block is copied to the new block and then freed. - On Windows, calls $(HTTPS msdn.microsoft.com/en-us/library/y69db7sx.aspx, - $(D __aligned_realloc(b.ptr, newSize, a))). - */ - version (Windows) - @system @nogc nothrow - bool alignedReallocate(ref void[] b, size_t s, uint a) shared - { - if (!s) - { - deallocate(b); - b = null; - return true; - } - auto p = cast(ubyte*) _aligned_realloc(b.ptr, s, a); - if (!p) return false; - b = p[0 .. s]; - return true; - } - - /// ditto - version (Posix) - @system @nogc nothrow - bool alignedReallocate(ref void[] b, size_t s, uint a) shared - { - if (!s) - { - deallocate(b); - b = null; - return true; - } - auto p = alignedAllocate(s, a); - if (!p.ptr) - { - return false; - } - import std.algorithm.comparison : min; - const upTo = min(s, b.length); - p[0 .. upTo] = b[0 .. upTo]; - deallocate(b); - b = p; - return true; - } - - /** - Returns the global instance of this allocator type. The C heap allocator is - thread-safe, therefore all of its methods and `instance` itself are - `shared`. - */ - static shared AlignedMallocator instance; -} - -/// -@nogc @system nothrow unittest -{ - auto buffer = AlignedMallocator.instance.alignedAllocate(1024 * 1024 * 4, - 128); - scope(exit) AlignedMallocator.instance.deallocate(buffer); - //... -} - -version (Posix) -@nogc @system nothrow unittest -{ - // https://issues.dlang.org/show_bug.cgi?id=16398 - // test the "pseudo" alignedReallocate for Posix - void[] b = AlignedMallocator.instance.alignedAllocate(16, 32); - (cast(ubyte[]) b)[] = ubyte(1); - AlignedMallocator.instance.alignedReallocate(b, 32, 32); - ubyte[16] o; - o[] = 1; - assert((cast(ubyte[]) b)[0 .. 16] == o); - AlignedMallocator.instance.alignedReallocate(b, 4, 32); - assert((cast(ubyte[]) b)[0 .. 3] == o[0 .. 3]); - AlignedMallocator.instance.alignedReallocate(b, 128, 32); - assert((cast(ubyte[]) b)[0 .. 3] == o[0 .. 3]); - AlignedMallocator.instance.deallocate(b); - - void[] c; - AlignedMallocator.instance.alignedReallocate(c, 32, 32); - assert(c.ptr); - - version (LDC_AddressSanitizer) {} else // AddressSanitizer does not support such large memory allocations (0x10000000000 max) - version (DragonFlyBSD) {} else /* FIXME: Malloc on DragonFly does not return NULL when allocating more than UINTPTR_MAX - * $(LINK: https://bugs.dragonflybsd.org/issues/3114, dragonfly bug report) - * $(LINK: https://github.com/dlang/druntime/pull/1999#discussion_r157536030, PR Discussion) */ - assert(!AlignedMallocator.instance.alignedReallocate(c, size_t.max, 4096)); - AlignedMallocator.instance.deallocate(c); -} - -version (CRuntime_DigitalMars) -@nogc @system nothrow unittest -{ - void* m; - - size_t m_addr() { return cast(size_t) m; } - - m = _aligned_malloc(16, 0x10); - if (m) - { - assert((m_addr & 0xF) == 0); - _aligned_free(m); - } - - m = _aligned_malloc(16, 0x100); - if (m) - { - assert((m_addr & 0xFF) == 0); - _aligned_free(m); - } - - m = _aligned_malloc(16, 0x1000); - if (m) - { - assert((m_addr & 0xFFF) == 0); - _aligned_free(m); - } - - m = _aligned_malloc(16, 0x10); - if (m) - { - assert((cast(size_t) m & 0xF) == 0); - m = _aligned_realloc(m, 32, 0x10000); - if (m) assert((m_addr & 0xFFFF) == 0); - _aligned_free(m); - } - - m = _aligned_malloc(8, 0x10); - if (m) - { - *cast(ulong*) m = 0X01234567_89ABCDEF; - m = _aligned_realloc(m, 0x800, 0x1000); - if (m) assert(*cast(ulong*) m == 0X01234567_89ABCDEF); - _aligned_free(m); - } -} diff --git a/phobos/std/experimental/allocator/mmap_allocator.d b/phobos/std/experimental/allocator/mmap_allocator.d deleted file mode 100644 index 4151d0e..0000000 --- a/phobos/std/experimental/allocator/mmap_allocator.d +++ /dev/null @@ -1,138 +0,0 @@ -// Written in the D programming language. -/** -Source: $(PHOBOSSRC std/experimental/allocator/_mmap_allocator.d) -*/ -module std.experimental.allocator.mmap_allocator; - -/** -Allocator (currently defined only for Posix and Windows) using -$(D $(LINK2 https://en.wikipedia.org/wiki/Mmap, mmap)) -and $(D $(LUCKY munmap)) directly (or their Windows equivalents). There is no -additional structure: each call to `allocate(s)` issues a call to -$(D mmap(null, s, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0)), -and each call to `deallocate(b)` issues $(D munmap(b.ptr, b.length)). -So `MmapAllocator` is usually intended for allocating large chunks to be -managed by fine-granular allocators. -*/ -struct MmapAllocator -{ - /// The one shared instance. - static shared const MmapAllocator instance; - - /** - Alignment is page-size and hardcoded to 4096 (even though on certain systems - it could be larger). - */ - enum size_t alignment = 4096; - - version (Posix) - { - /// Allocator API. - pure nothrow @nogc @safe - void[] allocate(size_t bytes) shared const - { - import core.sys.posix.sys.mman : MAP_ANON, PROT_READ, - PROT_WRITE, MAP_PRIVATE, MAP_FAILED; - if (!bytes) return null; - const errnosave = (() @trusted => fakePureErrno())(); // For purity revert changes to errno. - auto p = (() @trusted => fakePureMmap(null, bytes, PROT_READ | PROT_WRITE, - MAP_PRIVATE | MAP_ANON, -1, 0))(); - if (p is MAP_FAILED) - { - (() @trusted => fakePureErrno() = errnosave)(); // errno only changed on MAP_FAILED. - return null; - } - return (() @trusted => p[0 .. bytes])(); - } - - /// Ditto - pure nothrow @nogc - bool deallocate(void[] b) shared const - { - // Because we assert(0) on error we don't need to reset errno for purity. - if (b.ptr) fakePureMunmap(b.ptr, b.length) == 0 || assert(0); - return true; - } - - // Anonymous mmap might be zero-filled on all Posix systems but - // not all commit to this in the documentation. - version (linux) - // http://man7.org/linux/man-pages/man2/mmap.2.html - package alias allocateZeroed = allocate; - else version (NetBSD) - // http://netbsd.gw.com/cgi-bin/man-cgi?mmap+2+NetBSD-current - package alias allocateZeroed = allocate; - else version (Solaris) - // https://docs.oracle.com/cd/E88353_01/html/E37841/mmap-2.html - package alias allocateZeroed = allocate; - else version (AIX) - // https://www.ibm.com/support/knowledgecenter/en/ssw_aix_71/com.ibm.aix.basetrf1/mmap.htm - package alias allocateZeroed = allocate; - } - else version (Windows) - { - import core.sys.windows.winnt : MEM_COMMIT, PAGE_READWRITE, MEM_RELEASE; - - /// Allocator API. - pure nothrow @nogc @safe - void[] allocate(size_t bytes) shared const - { - if (!bytes) return null; - // For purity ensure last-error does not visibly change. - const lastErrorSave = (() @trusted => GetLastError())(); - auto p = (() @trusted => VirtualAlloc(null, bytes, MEM_COMMIT, PAGE_READWRITE))(); - if (p == null) - { - // Last-error only changed if allocation failed. - (() @trusted => SetLastError(lastErrorSave))(); - return null; - } - return (() @trusted => p[0 .. bytes])(); - } - - /// Ditto - pure nothrow @nogc - bool deallocate(void[] b) shared const - { - const lastErrorSave = GetLastError(); // For purity ensure last-error does not visibly change. - scope(exit) SetLastError(lastErrorSave); - return b.ptr is null || VirtualFree(b.ptr, 0, MEM_RELEASE) != 0; - } - - package alias allocateZeroed = allocate; - } -} - -// pure wrappers around `mmap` and `munmap` because they are used here locally -// solely to perform allocation and deallocation which in this case is `pure` -version (Posix) -extern (C) private pure @system @nogc nothrow -{ - import core.sys.posix.sys.types : off_t; - pragma(mangle, "fakePureErrnoImpl") ref int fakePureErrno(); - pragma(mangle, "mmap") void* fakePureMmap(void*, size_t, int, int, int, off_t); - pragma(mangle, "munmap") int fakePureMunmap(void*, size_t); -} - -// Pure wrappers around VirtualAlloc/VirtualFree for use here only. Their use is sound -// because when we call them we ensure that last-error is not visibly changed. -version (Windows) -extern (Windows) private pure @system @nogc nothrow -{ - import core.sys.windows.basetsd : SIZE_T; - import core.sys.windows.windef : BOOL, DWORD; - import core.sys.windows.winnt : LPVOID, PVOID; - - DWORD GetLastError(); - void SetLastError(DWORD); - PVOID VirtualAlloc(PVOID, SIZE_T, DWORD, DWORD); - BOOL VirtualFree(PVOID, SIZE_T, DWORD); -} - -pure nothrow @safe @nogc unittest -{ - alias alloc = MmapAllocator.instance; - auto p = alloc.allocate(100); - assert(p.length == 100); - () @trusted { alloc.deallocate(p); p = null; }(); -} diff --git a/phobos/std/experimental/allocator/package.d b/phobos/std/experimental/allocator/package.d deleted file mode 100644 index 7dbc47a..0000000 --- a/phobos/std/experimental/allocator/package.d +++ /dev/null @@ -1,3908 +0,0 @@ -// Written in the D programming language. -/** - -High-level interface for allocators. Implements bundled allocation/creation -and destruction/deallocation of data including `struct`s and `class`es, -and also array primitives related to allocation. This module is the entry point -for both making use of allocators and for their documentation. - -$(SCRIPT inhibitQuickIndex = 1;) -$(BOOKTABLE, -$(TR $(TH Category) $(TH Functions)) -$(TR $(TD Make) $(TD - $(LREF make) - $(LREF makeArray) - $(LREF makeMultidimensionalArray) -)) -$(TR $(TD Dispose) $(TD - $(LREF dispose) - $(LREF disposeMultidimensionalArray) -)) -$(TR $(TD Modify) $(TD - $(LREF expandArray) - $(LREF shrinkArray) -)) -$(TR $(TD Global) $(TD - $(LREF processAllocator) - $(LREF theAllocator) -)) -$(TR $(TD Class interface) $(TD - $(LREF CAllocatorImpl) - $(LREF CSharedAllocatorImpl) - $(LREF IAllocator) - $(LREF ISharedAllocator) -)) -$(TR $(TD Structs) $(TD - $(LREF allocatorObject) - $(LREF RCIAllocator) - $(LREF RCISharedAllocator) - $(LREF sharedAllocatorObject) - $(LREF ThreadLocal) -)) -) - -Synopsis: -$(RUNNABLE_EXAMPLE ---- -// Allocate an int, initialize it with 42 -int* p = theAllocator.make!int(42); -assert(*p == 42); -// Destroy and deallocate it -theAllocator.dispose(p); - -// Allocate using the global process allocator -p = processAllocator.make!int(100); -assert(*p == 100); -// Destroy and deallocate -processAllocator.dispose(p); ---- -) -$(RUNNABLE_EXAMPLE ---- -// Create an array of 50 doubles initialized to -1.0 -double[] arr = theAllocator.makeArray!double(50, -1.0); -// Append two zeros to it -theAllocator.expandArray(arr, 2, 0.0); -// On second thought, take that back -theAllocator.shrinkArray(arr, 2); -// Destroy and deallocate -theAllocator.dispose(arr); ---- -) - -$(H2 Layered Structure) - -D's allocators have a layered structure in both implementation and documentation: - -$(OL -$(LI A high-level, dynamically-typed layer (described further down in this -module). It consists of an interface called $(LREF IAllocator), which concrete -allocators need to implement. The interface primitives themselves are oblivious -to the type of the objects being allocated; they only deal in `void[]`, by -necessity of the interface being dynamic (as opposed to type-parameterized). -Each thread has a current allocator it uses by default, which is a thread-local -variable $(LREF theAllocator) of type $(LREF IAllocator). The process has a -global allocator called $(LREF processAllocator), also of type $(LREF -IAllocator). When a new thread is created, $(LREF processAllocator) is copied -into $(LREF theAllocator). An application can change the objects to which these -references point. By default, at application startup, $(LREF processAllocator) -refers to an object that uses D's garbage collected heap. This layer also -include high-level functions such as $(LREF make) and $(LREF dispose) that -comfortably allocate/create and respectively destroy/deallocate objects. This -layer is all needed for most casual uses of allocation primitives.) - -$(LI A mid-level, statically-typed layer for assembling several allocators into -one. It uses properties of the type of the objects being created to route -allocation requests to possibly specialized allocators. This layer is relatively -thin and implemented and documented in the $(MREF -std,experimental,allocator,typed) module. It allows an interested user to e.g. -use different allocators for arrays versus fixed-sized objects, to the end of -better overall performance.) - -$(LI A low-level collection of highly generic $(I heap building blocks)$(MDASH) -Lego-like pieces that can be used to assemble application-specific allocators. -The real allocation smarts are occurring at this level. This layer is of -interest to advanced applications that want to configure their own allocators. -A good illustration of typical uses of these building blocks is module $(MREF -std,experimental,allocator,showcase) which defines a collection of frequently- -used preassembled allocator objects. The implementation and documentation entry -point is $(MREF std,experimental,allocator,building_blocks). By design, the -primitives of the static interface have the same signatures as the $(LREF -IAllocator) primitives but are for the most part optional and driven by static -introspection. The parameterized class $(LREF CAllocatorImpl) offers an -immediate and useful means to package a static low-level allocator into an -implementation of $(LREF IAllocator).) - -$(LI Core allocator objects that interface with D's garbage collected heap -($(MREF std,experimental,allocator,gc_allocator)), the C `malloc` family -($(MREF std,experimental,allocator,mallocator)), and the OS ($(MREF -std,experimental,allocator,mmap_allocator)). Most custom allocators would -ultimately obtain memory from one of these core allocators.) -) - -$(H2 Idiomatic Use of `std.experimental.allocator`) - -As of this time, `std.experimental.allocator` is not integrated with D's -built-in operators that allocate memory, such as `new`, array literals, or -array concatenation operators. That means `std.experimental.allocator` is -opt-in$(MDASH)applications need to make explicit use of it. - -For casual creation and disposal of dynamically-allocated objects, use $(LREF -make), $(LREF dispose), and the array-specific functions $(LREF makeArray), -$(LREF expandArray), and $(LREF shrinkArray). These use by default D's garbage -collected heap, but open the application to better configuration options. These -primitives work either with `theAllocator` but also with any allocator obtained -by combining heap building blocks. For example: - ----- -void fun(size_t n) -{ - // Use the current allocator - int[] a1 = theAllocator.makeArray!int(n); - scope(exit) theAllocator.dispose(a1); - ... -} ----- - -To experiment with alternative allocators, set $(LREF theAllocator) for the -current thread. For example, consider an application that allocates many 8-byte -objects. These are not well supported by the default allocator, so a -$(MREF_ALTTEXT free list allocator, -std,experimental,allocator,building_blocks,free_list) would be recommended. -To install one in `main`, the application would use: - ----- -void main() -{ - import std.experimental.allocator.building_blocks.free_list - : FreeList; - theAllocator = allocatorObject(FreeList!8()); - ... -} ----- - -$(H3 Saving the `IAllocator` Reference For Later Use) - -As with any global resource, setting `theAllocator` and `processAllocator` -should not be done often and casually. In particular, allocating memory with -one allocator and deallocating with another causes undefined behavior. -Typically, these variables are set during application initialization phase and -last through the application. - -To avoid this, long-lived objects that need to perform allocations, -reallocations, and deallocations relatively often may want to store a reference -to the allocator object they use throughout their lifetime. Then, instead of -using `theAllocator` for internal allocation-related tasks, they'd use the -internally held reference. For example, consider a user-defined hash table: - ----- -struct HashTable -{ - private IAllocator allocator; - this(size_t buckets, IAllocator allocator = theAllocator) { - this.allocator = allocator; - ... - } - // Getter and setter - IAllocator allocator() { return allocator; } - void allocator(IAllocator a) { assert(empty); allocator = a; } -} ----- - -Following initialization, the `HashTable` object would consistently use its -`allocator` object for acquiring memory. Furthermore, setting -`HashTable.allocator` to point to a different allocator should be legal but -only if the object is empty; otherwise, the object wouldn't be able to -deallocate its existing state. - -$(H3 Using Allocators without `IAllocator`) - -Allocators assembled from the heap building blocks don't need to go through -`IAllocator` to be usable. They have the same primitives as `IAllocator` and -they work with $(LREF make), $(LREF makeArray), $(LREF dispose) etc. So it -suffice to create allocator objects wherever fit and use them appropriately: - ----- -void fun(size_t n) -{ - // Use a stack-installed allocator for up to 64KB - StackFront!65536 myAllocator; - int[] a2 = myAllocator.makeArray!int(n); - scope(exit) myAllocator.dispose(a2); - ... -} ----- - -In this case, `myAllocator` does not obey the `IAllocator` interface, but -implements its primitives so it can work with `makeArray` by means of duck -typing. - -One important thing to note about this setup is that statically-typed assembled -allocators are almost always faster than allocators that go through -`IAllocator`. An important rule of thumb is: "assemble allocator first, adapt -to `IAllocator` after". A good allocator implements intricate logic by means of -template assembly, and gets wrapped with `IAllocator` (usually by means of -$(LREF allocatorObject)) only once, at client level. - -Copyright: Andrei Alexandrescu 2013-. - -License: $(HTTP boost.org/LICENSE_1_0.txt, Boost License 1.0). - -Authors: $(HTTP erdani.com, Andrei Alexandrescu) - -Source: $(PHOBOSSRC std/experimental/allocator) - -*/ - -module std.experimental.allocator; - -public import std.experimental.allocator.common, - std.experimental.allocator.typed; - -// Fix https://issues.dlang.org/show_bug.cgi?id=17806 -// this should always be the first unittest in this module in order to ensure -// that we use the `processAllocator` setter before the getter -@system unittest -{ - import std.experimental.allocator.mallocator : Mallocator; - import std.experimental.allocator.gc_allocator : GCAllocator; - auto newAlloc = sharedAllocatorObject(Mallocator.instance); - processAllocator = newAlloc; - assert(processAllocator is newAlloc); - processAllocator = sharedAllocatorObject(GCAllocator.instance); -} - -// Example in the synopsis above -@system unittest -{ - import std.algorithm.comparison : min, max; - import std.experimental.allocator.building_blocks.allocator_list - : AllocatorList; - import std.experimental.allocator.building_blocks.bitmapped_block - : BitmappedBlock; - import std.experimental.allocator.building_blocks.bucketizer : Bucketizer; - import std.experimental.allocator.building_blocks.free_list : FreeList; - import std.experimental.allocator.building_blocks.segregator : Segregator; - import std.experimental.allocator.gc_allocator : GCAllocator; - - alias FList = FreeList!(GCAllocator, 0, unbounded); - alias A = Segregator!( - 8, FreeList!(GCAllocator, 0, 8), - 128, Bucketizer!(FList, 1, 128, 16), - 256, Bucketizer!(FList, 129, 256, 32), - 512, Bucketizer!(FList, 257, 512, 64), - 1024, Bucketizer!(FList, 513, 1024, 128), - 2048, Bucketizer!(FList, 1025, 2048, 256), - 3584, Bucketizer!(FList, 2049, 3584, 512), - 4072 * 1024, AllocatorList!( - (n) => BitmappedBlock!(4096)( - cast(ubyte[])(GCAllocator.instance.allocate( - max(n, 4072 * 1024))))), - GCAllocator - ); - A tuMalloc; - auto b = tuMalloc.allocate(500); - assert(b.length == 500); - auto c = tuMalloc.allocate(113); - assert(c.length == 113); - assert(tuMalloc.expand(c, 14)); - tuMalloc.deallocate(b); - tuMalloc.deallocate(c); -} - -import std.range.primitives; -import std.traits; -import std.typecons; - -/** -Dynamic allocator interface. Code that defines allocators ultimately implements -this interface. This should be used wherever a uniform type is required for -encapsulating various allocator implementations. - -Composition of allocators is not recommended at this level due to -inflexibility of dynamic interfaces and inefficiencies caused by cascaded -multiple calls. Instead, compose allocators using the static interface defined -in $(MREF std,experimental,allocator,building_blocks), -then adapt the composed -allocator to `IAllocator` (possibly by using $(LREF CAllocatorImpl) below). - -Methods returning `Ternary` return `Ternary.yes` upon success, -`Ternary.no` upon failure, and `Ternary.unknown` if the primitive is not -implemented by the allocator instance. -*/ -interface IAllocator -{ -nothrow: - /** - Returns the alignment offered. - */ - @property uint alignment(); - - /** - Returns the good allocation size that guarantees zero internal - fragmentation. - */ - size_t goodAllocSize(size_t s); - - /** - Allocates `n` bytes of memory. - */ - void[] allocate(size_t, TypeInfo ti = null); - - /** - Allocates `n` bytes of memory with specified alignment `a`. Implementations - that do not support this primitive should always return `null`. - */ - void[] alignedAllocate(size_t n, uint a); - - /** - Allocates and returns all memory available to this allocator. - Implementations that do not support this primitive should always return - `null`. - */ - void[] allocateAll(); - - /** - Expands a memory block in place and returns `true` if successful. - Implementations that don't support this primitive should always return - `false`. - */ - bool expand(ref void[], size_t); - - /// Reallocates a memory block. - bool reallocate(ref void[], size_t); - - /// Reallocates a memory block with specified alignment. - bool alignedReallocate(ref void[] b, size_t size, uint alignment); - - /** - Returns `Ternary.yes` if the allocator owns `b`, `Ternary.no` if - the allocator doesn't own `b`, and `Ternary.unknown` if ownership - cannot be determined. Implementations that don't support this primitive - should always return `Ternary.unknown`. - */ - Ternary owns(void[] b); - - /** - Resolves an internal pointer to the full block allocated. Implementations - that don't support this primitive should always return `Ternary.unknown`. - */ - Ternary resolveInternalPointer(const void* p, ref void[] result); - - /** - Deallocates a memory block. Implementations that don't support this - primitive should always return `false`. A simple way to check that an - allocator supports deallocation is to call `deallocate(null)`. - */ - bool deallocate(void[] b); - - /** - Deallocates all memory. Implementations that don't support this primitive - should always return `false`. - */ - bool deallocateAll(); - - /** - Returns `Ternary.yes` if no memory is currently allocated from this - allocator, `Ternary.no` if some allocations are currently active, or - `Ternary.unknown` if not supported. - */ - Ternary empty(); - - /** - Increases the reference count of the concrete class that implements this - interface. - - For stateless allocators, this does nothing. - */ - @safe @nogc pure - void incRef(); - - /** - Decreases the reference count of the concrete class that implements this - interface. - When the reference count is `0`, the object self-destructs. - - Returns: `true` if the reference count is greater than `0` and `false` when - it hits `0`. For stateless allocators, it always returns `true`. - */ - @safe @nogc pure - bool decRef(); -} - -/** -A reference counted struct that wraps the dynamic allocator interface. -This should be used wherever a uniform type is required for encapsulating -various allocator implementations. - -Code that defines allocators ultimately implements the $(LREF IAllocator) -interface, possibly by using $(LREF CAllocatorImpl) below, and then build a -`RCIAllocator` out of this. - -Composition of allocators is not recommended at this level due to -inflexibility of dynamic interfaces and inefficiencies caused by cascaded -multiple calls. Instead, compose allocators using the static interface defined -in $(A std_experimental_allocator_building_blocks.html, -`std.experimental.allocator.building_blocks`), then adapt the composed -allocator to `RCIAllocator` (possibly by using $(LREF allocatorObject) below). -*/ -struct RCIAllocator -{ - private IAllocator _alloc; - -nothrow: - private @nogc pure @safe - this(this _)(IAllocator alloc) - { - assert(alloc); - _alloc = alloc; - } - - @nogc pure @safe - this(this) - { - if (_alloc !is null) - { - _alloc.incRef(); - } - } - - @nogc pure @safe - ~this() - { - if (_alloc !is null) - { - bool isLast = !_alloc.decRef(); - if (isLast) _alloc = null; - } - } - - @nogc pure @safe - auto ref opAssign()(typeof(this) rhs) - { - if (_alloc is rhs._alloc) - { - return this; - } - // incRef was allready called by rhs posblit, so we're just moving - // calling dtor is the equivalent of decRef - __dtor(); - _alloc = rhs._alloc; - // move - rhs._alloc = null; - return this; - } - - @nogc pure @safe - bool isNull(this _)() - { - return _alloc is null; - } - - @property uint alignment() - { - assert(_alloc); - return _alloc.alignment(); - } - - size_t goodAllocSize(size_t s) - { - assert(_alloc); - return _alloc.goodAllocSize(s); - } - - void[] allocate(size_t n, TypeInfo ti = null) - { - assert(_alloc); - return _alloc.allocate(n, ti); - } - - void[] alignedAllocate(size_t n, uint a) - { - assert(_alloc); - return _alloc.alignedAllocate(n, a); - } - - void[] allocateAll() - { - assert(_alloc); - return _alloc.allocateAll(); - } - - bool expand(ref void[] b, size_t size) - { - assert(_alloc); - return _alloc.expand(b, size); - } - - bool reallocate(ref void[] b, size_t size) - { - assert(_alloc); - return _alloc.reallocate(b, size); - } - - bool alignedReallocate(ref void[] b, size_t size, uint alignment) - { - assert(_alloc); - return _alloc.alignedReallocate(b, size, alignment); - } - - Ternary owns(void[] b) - { - assert(_alloc); - return _alloc.owns(b); - } - - Ternary resolveInternalPointer(const void* p, ref void[] result) - { - assert(_alloc); - return _alloc.resolveInternalPointer(p, result); - } - - bool deallocate(void[] b) - { - assert(_alloc); - return _alloc.deallocate(b); - } - - bool deallocateAll() - { - assert(_alloc); - return _alloc.deallocateAll(); - } - - Ternary empty() - { - assert(_alloc); - return _alloc.empty(); - } -} - -@system unittest -{ - import std.experimental.allocator.building_blocks.region : BorrowedRegion; - import std.conv : emplace; - - auto reg = BorrowedRegion!()(new ubyte[1024]); - auto state = reg.allocate(stateSize!(CAllocatorImpl!(BorrowedRegion!(), Yes.indirect))); - auto regObj = emplace!(CAllocatorImpl!(BorrowedRegion!(), Yes.indirect))(state, ®); - - auto rcalloc = RCIAllocator(regObj); - auto b = rcalloc.allocate(10); - assert(b.length == 10); - - // The reference counting is zero based - assert((cast(CAllocatorImpl!(BorrowedRegion!(), Yes.indirect))(rcalloc._alloc)).rc == 1); - { - auto rca2 = rcalloc; - assert((cast(CAllocatorImpl!(BorrowedRegion!(), Yes.indirect))(rcalloc._alloc)).rc == 2); - } - assert((cast(CAllocatorImpl!(BorrowedRegion!(), Yes.indirect))(rcalloc._alloc)).rc == 1); -} - -@system unittest -{ - import std.conv; - import std.experimental.allocator.mallocator; - import std.experimental.allocator.building_blocks.stats_collector; - - alias SCAlloc = StatsCollector!(Mallocator, Options.bytesUsed); - SCAlloc statsCollectorAlloc; - - ulong bytesUsed = statsCollectorAlloc.bytesUsed; - assert(bytesUsed == 0); - - { - auto _allocator = allocatorObject(&statsCollectorAlloc); - bytesUsed = statsCollectorAlloc.bytesUsed; - assert(bytesUsed == stateSize!(CAllocatorImpl!(SCAlloc, Yes.indirect))); - } - - bytesUsed = statsCollectorAlloc.bytesUsed; - assert(bytesUsed == 0, "RCIAllocator leaks memory; leaked " - ~ to!string(bytesUsed) ~ " bytes"); -} - -@system unittest -{ - import std.conv; - import std.experimental.allocator.mallocator; - import std.experimental.allocator.building_blocks.stats_collector; - - alias SCAlloc = StatsCollector!(Mallocator, Options.bytesUsed); - SCAlloc statsCollectorAlloc; - - ulong bytesUsed = statsCollectorAlloc.bytesUsed; - assert(bytesUsed == 0); - - { - auto _allocator = allocatorObject(statsCollectorAlloc); - - // Ensure that the allocator was passed through in CAllocatorImpl - // This allocator was used to allocate the chunk that holds the - // CAllocatorImpl object; which is it's own wrapper - bytesUsed = (cast(CAllocatorImpl!(SCAlloc))(_allocator._alloc)).impl.bytesUsed; - assert(bytesUsed == stateSize!(CAllocatorImpl!(SCAlloc)), - "RCIAllocator leaks memory; leaked " ~ to!string(bytesUsed) ~ " bytes"); - _allocator.allocate(1); - bytesUsed = (cast(CAllocatorImpl!(SCAlloc))(_allocator._alloc)).impl.bytesUsed; - assert(bytesUsed == stateSize!(CAllocatorImpl!(SCAlloc)) + 1, - "RCIAllocator leaks memory; leaked " ~ to!string(bytesUsed) ~ " bytes"); - } - - bytesUsed = statsCollectorAlloc.bytesUsed; - assert(bytesUsed == stateSize!(CAllocatorImpl!(SCAlloc)), - "RCIAllocator leaks memory; leaked " - ~ to!string(bytesUsed) ~ " bytes"); -} - -/** -Dynamic shared allocator interface. Code that defines allocators shareable -across threads ultimately implements this interface. This should be used -wherever a uniform type is required for encapsulating various allocator -implementations. - -Composition of allocators is not recommended at this level due to -inflexibility of dynamic interfaces and inefficiencies caused by cascaded -multiple calls. Instead, compose allocators using the static interface defined -in $(MREF std,experimental,allocator,building_blocks), -then adapt the composed -allocator to `ISharedAllocator` (possibly by using $(LREF CSharedAllocatorImpl) below). - -Methods returning `Ternary` return `Ternary.yes` upon success, -`Ternary.no` upon failure, and `Ternary.unknown` if the primitive is not -implemented by the allocator instance. -*/ -interface ISharedAllocator -{ -nothrow: - /** - Returns the alignment offered. - */ - @property uint alignment() shared; - - /** - Returns the good allocation size that guarantees zero internal - fragmentation. - */ - size_t goodAllocSize(size_t s) shared; - - /** - Allocates `n` bytes of memory. - */ - void[] allocate(size_t, TypeInfo ti = null) shared; - - /** - Allocates `n` bytes of memory with specified alignment `a`. Implementations - that do not support this primitive should always return `null`. - */ - void[] alignedAllocate(size_t n, uint a) shared; - - /** - Allocates and returns all memory available to this allocator. - Implementations that do not support this primitive should always return - `null`. - */ - void[] allocateAll() shared; - - /** - Expands a memory block in place and returns `true` if successful. - Implementations that don't support this primitive should always return - `false`. - */ - bool expand(ref void[], size_t) shared; - - /// Reallocates a memory block. - bool reallocate(ref void[], size_t) shared; - - /// Reallocates a memory block with specified alignment. - bool alignedReallocate(ref void[] b, size_t size, uint alignment) shared; - - /** - Returns `Ternary.yes` if the allocator owns `b`, `Ternary.no` if - the allocator doesn't own `b`, and `Ternary.unknown` if ownership - cannot be determined. Implementations that don't support this primitive - should always return `Ternary.unknown`. - */ - Ternary owns(void[] b) shared; - - /** - Resolves an internal pointer to the full block allocated. Implementations - that don't support this primitive should always return `Ternary.unknown`. - */ - Ternary resolveInternalPointer(const void* p, ref void[] result) shared; - - /** - Deallocates a memory block. Implementations that don't support this - primitive should always return `false`. A simple way to check that an - allocator supports deallocation is to call `deallocate(null)`. - */ - bool deallocate(void[] b) shared; - - /** - Deallocates all memory. Implementations that don't support this primitive - should always return `false`. - */ - bool deallocateAll() shared; - - /** - Returns `Ternary.yes` if no memory is currently allocated from this - allocator, `Ternary.no` if some allocations are currently active, or - `Ternary.unknown` if not supported. - */ - Ternary empty() shared; - - /** - Increases the reference count of the concrete class that implements this - interface. - - For stateless allocators, this does nothing. - */ - @safe @nogc pure - void incRef() shared; - - /** - Decreases the reference count of the concrete class that implements this - interface. - When the reference count is `0`, the object self-destructs. - - For stateless allocators, this does nothing. - - Returns: `true` if the reference count is greater than `0` and `false` when - it hits `0`. For stateless allocators, it always returns `true`. - */ - @safe @nogc pure - bool decRef() shared; -} - -/** -A reference counted struct that wraps the dynamic shared allocator interface. -This should be used wherever a uniform type is required for encapsulating -various allocator implementations. - -Code that defines allocators shareable across threads ultimately implements the -$(LREF ISharedAllocator) interface, possibly by using -$(LREF CSharedAllocatorImpl) below, and then build a `RCISharedAllocator` out -of this. - -Composition of allocators is not recommended at this level due to -inflexibility of dynamic interfaces and inefficiencies caused by cascaded -multiple calls. Instead, compose allocators using the static interface defined -in $(A std_experimental_allocator_building_blocks.html, -`std.experimental.allocator.building_blocks`), then adapt the composed allocator -to `RCISharedAllocator` (possibly by using $(LREF sharedAllocatorObject) below). -*/ -shared struct RCISharedAllocator -{ - private ISharedAllocator _alloc; - -nothrow: - private @nogc pure @safe - this(shared ISharedAllocator alloc) - { - assert(alloc); - _alloc = alloc; - } - - @nogc pure @safe - this(this) - { - if (_alloc !is null) - { - _alloc.incRef(); - } - } - - @nogc pure @safe - ~this() - { - if (_alloc !is null) - { - bool isLast = !_alloc.decRef(); - if (isLast) _alloc = null; - } - } - - @nogc pure @safe - auto ref opAssign()(RCISharedAllocator rhs) - { - if (_alloc is rhs._alloc) - { - return this; - } - // incRef was allready called by rhs posblit, so we're just moving - if (_alloc !is null) - { - _alloc.decRef(); - } - _alloc = rhs._alloc; - // move - rhs._alloc = null; - return this; - } - - @nogc pure @safe - bool isNull(this _)() - { - return _alloc is null; - } - - @property uint alignment() - { - assert(_alloc); - return _alloc.alignment(); - } - - size_t goodAllocSize(size_t s) - { - assert(_alloc); - return _alloc.goodAllocSize(s); - } - - void[] allocate(size_t n, TypeInfo ti = null) - { - assert(_alloc); - return _alloc.allocate(n, ti); - } - - void[] alignedAllocate(size_t n, uint a) - { - assert(_alloc); - return _alloc.alignedAllocate(n, a); - } - - void[] allocateAll() - { - assert(_alloc); - return _alloc.allocateAll(); - } - - bool expand(ref void[] b, size_t size) - { - assert(_alloc); - return _alloc.expand(b, size); - } - - bool reallocate(ref void[] b, size_t size) - { - assert(_alloc); - return _alloc.reallocate(b, size); - } - - bool alignedReallocate(ref void[] b, size_t size, uint alignment) - { - assert(_alloc); - return _alloc.alignedReallocate(b, size, alignment); - } - - Ternary owns(void[] b) - { - assert(_alloc); - return _alloc.owns(b); - } - - Ternary resolveInternalPointer(const void* p, ref void[] result) - { - assert(_alloc); - return _alloc.resolveInternalPointer(p, result); - } - - bool deallocate(void[] b) - { - assert(_alloc); - return _alloc.deallocate(b); - } - - bool deallocateAll() - { - assert(_alloc); - return _alloc.deallocateAll(); - } - - Ternary empty() - { - assert(_alloc); - return _alloc.empty(); - } -} - -private RCISharedAllocator _processAllocator; -private RCIAllocator _threadAllocator; - -@nogc nothrow @safe -private ref RCIAllocator setupThreadAllocator() -{ - /* - Forwards the `_threadAllocator` calls to the `processAllocator` - */ - static class ThreadAllocator : IAllocator - { - nothrow: - private RCISharedAllocator _allocator; - - @nogc @safe - this(ref RCISharedAllocator procAlloc) - { - _allocator = procAlloc; - } - - override @property uint alignment() - { - return _allocator.alignment(); - } - - override size_t goodAllocSize(size_t s) - { - return _allocator.goodAllocSize(s); - } - - override void[] allocate(size_t n, TypeInfo ti = null) - { - return _allocator.allocate(n, ti); - } - - override void[] alignedAllocate(size_t n, uint a) - { - return _allocator.alignedAllocate(n, a); - } - - override void[] allocateAll() - { - return _allocator.allocateAll(); - } - - override bool expand(ref void[] b, size_t size) - { - return _allocator.expand(b, size); - } - - override bool reallocate(ref void[] b, size_t size) - { - return _allocator.reallocate(b, size); - } - - override bool alignedReallocate(ref void[] b, size_t size, uint alignment) - { - return _allocator.alignedReallocate(b, size, alignment); - } - - override Ternary owns(void[] b) - { - return _allocator.owns(b); - } - - override Ternary resolveInternalPointer(const void* p, ref void[] result) - { - return _allocator.resolveInternalPointer(p, result); - } - - override bool deallocate(void[] b) - { - return _allocator.deallocate(b); - } - - override bool deallocateAll() - { - return _allocator.deallocateAll(); - } - - override Ternary empty() - { - return _allocator.empty(); - } - - @nogc pure @safe - override void incRef() - { - _allocator._alloc.incRef(); - } - - @nogc pure @safe - override bool decRef() - { - return _allocator._alloc.decRef(); - } - } - - assert(_threadAllocator.isNull); - import core.lifetime : emplace; - static ulong[stateSize!(ThreadAllocator).divideRoundUp(ulong.sizeof)] _threadAllocatorState; - () @trusted { - _threadAllocator = RCIAllocator(emplace!(ThreadAllocator)(_threadAllocatorState[], processAllocator())); - }(); - return _threadAllocator; -} - -// Fix threadAllocator bug: the threadAllocator should hold an internal reference -// to the processAllocator that it's using -@system unittest -{ - import std.experimental.allocator.mallocator : Mallocator; - - auto a = sharedAllocatorObject(Mallocator.instance); - auto buf = theAllocator.allocate(42); - processAllocator = a; - theAllocator.deallocate(buf); -} - -/** -Gets/sets the allocator for the current thread. This is the default allocator -that should be used for allocating thread-local memory. For allocating memory -to be shared across threads, use `processAllocator` (below). By default, -`theAllocator` ultimately fetches memory from `processAllocator`, which -in turn uses the garbage collected heap. -*/ -@nogc nothrow @safe -@property ref RCIAllocator theAllocator() -{ - alias p = _threadAllocator; - return !p.isNull() ? p : setupThreadAllocator(); -} - -/// Ditto -nothrow @system @nogc -@property void theAllocator(RCIAllocator a) -{ - assert(!a.isNull); - _threadAllocator = a; -} - -/// -@system unittest -{ - // Install a new allocator that is faster for 128-byte allocations. - import std.experimental.allocator.building_blocks.free_list : FreeList; - import std.experimental.allocator.gc_allocator : GCAllocator; - auto oldAllocator = theAllocator; - scope(exit) theAllocator = oldAllocator; - theAllocator = allocatorObject(FreeList!(GCAllocator, 128)()); - // Use the now changed allocator to allocate an array - const ubyte[] arr = theAllocator.makeArray!ubyte(128); - assert(arr.ptr); - //... -} - -/** -Gets/sets the allocator for the current process. This allocator must be used -for allocating memory shared across threads. Objects created using this -allocator can be cast to `shared`. -*/ -@nogc nothrow @trusted -@property ref RCISharedAllocator processAllocator() -{ - import std.experimental.allocator.gc_allocator : GCAllocator; - import std.concurrency : initOnce; - - static RCISharedAllocator* forceAttributes() - { - return &initOnce!_processAllocator( - sharedAllocatorObject(GCAllocator.instance)); - } - - return *(cast(RCISharedAllocator* function() @nogc nothrow)(&forceAttributes))(); -} - -/// Ditto -@nogc nothrow @system -@property void processAllocator(ref RCISharedAllocator a) -{ - assert(!a.isNull); - processAllocator() = a; -} - -@system unittest -{ - import core.exception : AssertError; - import std.exception : assertThrown; - import std.experimental.allocator.building_blocks.free_list : SharedFreeList; - import std.experimental.allocator.mallocator : Mallocator; - - assert(!processAllocator.isNull); - assert(!theAllocator.isNull); - - testAllocatorObject(processAllocator); - testAllocatorObject(theAllocator); - - shared SharedFreeList!(Mallocator, chooseAtRuntime, chooseAtRuntime) sharedFL; - RCISharedAllocator sharedFLObj = sharedAllocatorObject(sharedFL); - alias SharedAllocT = CSharedAllocatorImpl!( - shared SharedFreeList!( - Mallocator, chooseAtRuntime, chooseAtRuntime)); - - assert((cast(SharedAllocT)(sharedFLObj._alloc)).rc == 1); - assert(!sharedFLObj.isNull); - testAllocatorObject(sharedFLObj); - - // Test processAllocator setter - RCISharedAllocator oldProcessAllocator = processAllocator; - processAllocator = sharedFLObj; - assert((cast(SharedAllocT)(sharedFLObj._alloc)).rc == 2); - assert(processAllocator._alloc is sharedFLObj._alloc); - - testAllocatorObject(processAllocator); - testAllocatorObject(theAllocator); - assertThrown!AssertError(processAllocator = RCISharedAllocator(null)); - - // Restore initial processAllocator state - processAllocator = oldProcessAllocator; - assert((cast(SharedAllocT)(sharedFLObj._alloc)).rc == 1); - assert(processAllocator is oldProcessAllocator); - - RCISharedAllocator indirectShFLObj = sharedAllocatorObject(&sharedFL); - testAllocatorObject(indirectShFLObj); - alias IndirectSharedAllocT = CSharedAllocatorImpl!( - shared SharedFreeList!( - Mallocator, chooseAtRuntime, chooseAtRuntime) - , Yes.indirect); - - assert((cast(IndirectSharedAllocT)(indirectShFLObj._alloc)).rc == 1); - - RCIAllocator indirectMallocator = allocatorObject(&Mallocator.instance); - testAllocatorObject(indirectMallocator); -} - -/** -Dynamically allocates (using `alloc`) and then creates in the memory -allocated an object of type `T`, using `args` (if any) for its -initialization. Initialization occurs in the memory allocated and is otherwise -semantically the same as `T(args)`. -(Note that using `alloc.make!(T[])` creates a pointer to an (empty) array -of `T`s, not an array. To use an allocator to allocate and initialize an -array, use `alloc.makeArray!T` described below.) - -Params: -T = Type of the object being created. -alloc = The allocator used for getting the needed memory. It may be an object -implementing the static interface for allocators, or an `IAllocator` -reference. -args = Optional arguments used for initializing the created object. If not -present, the object is default constructed. - -Returns: If `T` is a class type, returns a reference to the created `T` -object. Otherwise, returns a `T*` pointing to the created object. In all -cases, returns `null` if allocation failed. - -Throws: If `T`'s constructor throws, deallocates the allocated memory and -propagates the exception. -*/ -auto make(T, Allocator, A...)(auto ref Allocator alloc, auto ref A args) -{ - import std.algorithm.comparison : max; - static if (!is(T == class) && !is(T == interface) && A.length == 0 - && __traits(compiles, {T t;}) && __traits(isZeroInit, T) - && is(typeof(alloc.allocateZeroed(size_t.max)))) - { - auto m = alloc.allocateZeroed(max(T.sizeof, 1)); - return (() @trusted => cast(T*) m.ptr)(); - } - else - { - import core.internal.lifetime : emplaceRef; - import core.lifetime : emplace; - - auto m = alloc.allocate(max(stateSize!T, 1)); - if (!m.ptr) return null; - - // make can only be @safe if emplace or emplaceRef is `pure` - auto construct() - { - static if (is(T == class)) return emplace!T(m, args); - else - { - // Assume cast is safe as allocation succeeded for `stateSize!T` - auto p = () @trusted { return cast(T*) m.ptr; }(); - emplaceRef!T(*p, args); - return p; - } - } - - scope(failure) - { - static if (is(typeof(() pure { return construct(); }))) - { - // Assume deallocation is safe because: - // 1) in case of failure, `m` is the only reference to this memory - // 2) `m` is known to originate from `alloc` - () @trusted { alloc.deallocate(m); }(); - } - else - { - alloc.deallocate(m); - } - } - - return construct(); - } -} - -/// -@system unittest -{ - // Dynamically allocate one integer - const int* p1 = theAllocator.make!int; - // It's implicitly initialized with its .init value - assert(*p1 == 0); - // Dynamically allocate one double, initialize to 42.5 - const double* p2 = theAllocator.make!double(42.5); - assert(*p2 == 42.5); - - // Dynamically allocate a struct - static struct Point - { - int x, y, z; - } - // Use the generated constructor taking field values in order - const Point* p = theAllocator.make!Point(1, 2); - assert(p.x == 1 && p.y == 2 && p.z == 0); - - // Dynamically allocate a class object - static class Customer - { - uint id = uint.max; - this() {} - this(uint id) { this.id = id; } - // ... - } - Customer cust = theAllocator.make!Customer; - assert(cust.id == uint.max); // default initialized - cust = theAllocator.make!Customer(42); - assert(cust.id == 42); - - // explicit passing of outer pointer - static class Outer - { - int x = 3; - class Inner - { - auto getX() { return x; } - } - } - auto outer = theAllocator.make!Outer(); - auto inner = theAllocator.make!(Outer.Inner)(outer); - assert(outer.x == inner.getX); -} - -// https://issues.dlang.org/show_bug.cgi?id=15639 -// https://issues.dlang.org/show_bug.cgi?id=15772 -@system unittest -{ - abstract class Foo {} - class Bar: Foo {} - static assert(!is(typeof(theAllocator.make!Foo))); - static assert( is(typeof(theAllocator.make!Bar))); -} - -@system unittest -{ - void test(Allocator)(auto ref Allocator alloc) - { - const int* a = alloc.make!int(10); - assert(*a == 10); - - struct A - { - int x; - string y; - double z; - } - - A* b = alloc.make!A(42); - assert(b.x == 42); - assert(b.y is null); - import std.math.traits : isNaN; - assert(b.z.isNaN); - - b = alloc.make!A(43, "44", 45); - assert(b.x == 43); - assert(b.y == "44"); - assert(b.z == 45); - - static class B - { - int x; - string y; - double z; - this(int _x, string _y = null, double _z = double.init) - { - x = _x; - y = _y; - z = _z; - } - } - - B c = alloc.make!B(42); - assert(c.x == 42); - assert(c.y is null); - assert(c.z.isNaN); - - c = alloc.make!B(43, "44", 45); - assert(c.x == 43); - assert(c.y == "44"); - assert(c.z == 45); - - const parray = alloc.make!(int[]); - assert((*parray).empty); - } - - import std.experimental.allocator.gc_allocator : GCAllocator; - test(GCAllocator.instance); - test(theAllocator); -} - -// Attribute propagation -nothrow @safe @nogc unittest -{ - import std.experimental.allocator.mallocator : Mallocator; - alias alloc = Mallocator.instance; - - void test(T, Args...)(auto ref Args args) - { - auto k = alloc.make!T(args); - () @trusted { alloc.dispose(k); }(); - } - - test!int; - test!(int*); - test!int(0); - test!(int*)(null); -} - -// should be pure with the GCAllocator -/*pure nothrow*/ @safe unittest -{ - import std.experimental.allocator.gc_allocator : GCAllocator; - - alias alloc = GCAllocator.instance; - - void test(T, Args...)(auto ref Args args) - { - auto k = alloc.make!T(args); - (a) @trusted { a.dispose(k); }(alloc); - } - - test!int(); - test!(int*); - test!int(0); - test!(int*)(null); -} - -// Verify that making an object by calling an impure constructor is not @safe -nothrow @safe @nogc unittest -{ - import std.experimental.allocator.mallocator : Mallocator; - static struct Pure { this(int) pure nothrow @nogc @safe {} } - - cast(void) Mallocator.instance.make!Pure(0); - - static int g = 0; - static struct Impure { this(int) nothrow @nogc @safe { - g++; - } } - static assert(!__traits(compiles, cast(void) Mallocator.instance.make!Impure(0))); -} - -// test failure with a pure, failing struct -@safe unittest -{ - import std.exception : assertThrown, enforce; - - // this struct can't be initialized - struct InvalidStruct - { - this(int b) - { - enforce(1 == 2); - } - } - import std.experimental.allocator.mallocator : Mallocator; - assertThrown(make!InvalidStruct(Mallocator.instance, 42)); -} - -// test failure with an impure, failing struct -@system unittest -{ - import std.exception : assertThrown, enforce; - static int g; - struct InvalidImpureStruct - { - this(int b) - { - g++; - enforce(1 == 2); - } - } - import std.experimental.allocator.mallocator : Mallocator; - assertThrown(make!InvalidImpureStruct(Mallocator.instance, 42)); -} - -// Don't allow zero-ctor-args `make` for structs with `@disable this();` -@system unittest -{ - struct NoDefaultCtor - { - int i; - @disable this(); - } - import std.experimental.allocator.mallocator : Mallocator; - static assert(!__traits(compiles, make!NoDefaultCtor(Mallocator.instance)), - "Don't allow zero-ctor-args `make` for structs with `@disable this();`"); -} - -// https://issues.dlang.org/show_bug.cgi?id=18937 -@safe unittest -{ - static struct S - { - ubyte[16 * 1024] data; - } - - static struct SomeAllocator - { - ubyte[] allocate(size_t) { return []; } - void deallocate(void[]) {} - } - - auto x = SomeAllocator().make!S(); -} - -private void fillWithMemcpy(T)(scope void[] array, auto ref T filler) nothrow -if (T.sizeof == 1) -{ - import core.stdc.string : memset; - import std.traits : CopyConstness; - if (!array.length) return; - memset(array.ptr, *cast(CopyConstness!(T*, ubyte*)) &filler, array.length); -} - -private void fillWithMemcpy(T)(scope void[] array, auto ref T filler) nothrow -if (T.sizeof != 1) -{ - import core.stdc.string : memcpy; - import std.algorithm.comparison : min; - if (!array.length) return; - memcpy(array.ptr, &filler, T.sizeof); - // Fill the array from the initialized portion of itself exponentially. - for (size_t offset = T.sizeof; offset < array.length; ) - { - size_t extent = min(offset, array.length - offset); - memcpy(array.ptr + offset, array.ptr, extent); - offset += extent; - } -} - -@system unittest -{ - // Test T.sizeof == 1 path of fillWithMemcpy. - ubyte[] a; - fillWithMemcpy(a, ubyte(42)); - assert(a.length == 0); - a = [ 1, 2, 3, 4, 5 ]; - fillWithMemcpy(a, ubyte(42)); - assert(a == [ 42, 42, 42, 42, 42]); -} - -@system unittest -{ - int[] a; - fillWithMemcpy(a, 42); - assert(a.length == 0); - a = [ 1, 2, 3, 4, 5 ]; - fillWithMemcpy(a, 42); - assert(a == [ 42, 42, 42, 42, 42]); -} - -//Make shared object -@system unittest -{ - import core.atomic : atomicLoad; - auto psi = theAllocator.make!(shared(int))(10); - assert(10 == (*psi).atomicLoad()); -} - -private T[] uninitializedFillDefault(T)(T[] array) nothrow -{ - static if (__traits(isZeroInit, T)) - { - import core.stdc.string : memset; - if (array !is null) - memset(array.ptr, 0, T.sizeof * array.length); - return array; - } - else static if (is(immutable T == immutable char) || is(immutable T == immutable wchar)) - { - import core.stdc.string : memset; - if (array !is null) - memset(array.ptr, 0xff, T.sizeof * array.length); - return array; - } - else - { - T t = T.init; - fillWithMemcpy(array, t); - return array; - } -} - -pure nothrow @nogc -@system unittest -{ - static struct S { int x = 42; @disable this(this); } - - int[5] expected = [42, 42, 42, 42, 42]; - S[5] arr = void; - uninitializedFillDefault(arr); - assert((cast(int*) arr.ptr)[0 .. arr.length] == expected); -} - -@system unittest -{ - int[] a = [1, 2, 4]; - uninitializedFillDefault(a); - assert(a == [0, 0, 0]); - - char[] b = [1, 2, 4]; - uninitializedFillDefault(b); - assert(b == [0xff, 0xff, 0xff]); - - wchar[] c = [1, 2, 4]; - uninitializedFillDefault(c); - assert(c == [0xffff, 0xffff, 0xffff]); -} - -@system unittest -{ - static struct P { float x = 0; float y = 0; } - - static assert(__traits(isZeroInit, P)); - P[] a = [P(10, 11), P(20, 21), P(40, 41)]; - uninitializedFillDefault(a); - assert(a == [P.init, P.init, P.init]); -} - -/** -Create an array of `T` with `length` elements using `alloc`. The array is either default-initialized, filled with copies of `init`, or initialized with values fetched from `range`. - -Params: -T = element type of the array being created -alloc = the allocator used for getting memory -length = length of the newly created array -init = element used for filling the array -range = range used for initializing the array elements - -Returns: -The newly-created array, or `null` if either `length` was `0` or -allocation failed. - -Throws: -The first two overloads throw only if `alloc`'s primitives do. The -overloads that involve copy initialization deallocate memory and propagate the -exception if the copy operation throws. -*/ -T[] makeArray(T, Allocator)(auto ref Allocator alloc, size_t length) -{ - if (!length) return null; - static if (T.sizeof <= 1) - { - const nAlloc = length * T.sizeof; - } - else - { - import core.checkedint : mulu; - bool overflow; - const nAlloc = mulu(length, T.sizeof, overflow); - if (overflow) return null; - } - - static if (__traits(isZeroInit, T) && hasMember!(Allocator, "allocateZeroed")) - { - auto m = alloc.allocateZeroed(nAlloc); - return (() @trusted => cast(T[]) m)(); - } - else - { - auto m = alloc.allocate(nAlloc); - if (!m.ptr) return null; - alias U = Unqual!T; - return () @trusted { return cast(T[]) uninitializedFillDefault(cast(U[]) m); }(); - } -} - -@system unittest -{ - void test1(A)(auto ref A alloc) - { - int[] a = alloc.makeArray!int(0); - assert(a.length == 0 && a.ptr is null); - a = alloc.makeArray!int(5); - assert(a.length == 5); - static immutable cheatsheet = [0, 0, 0, 0, 0]; - assert(a == cheatsheet); - } - - void test2(A)(auto ref A alloc) - { - static struct S { int x = 42; @disable this(this); } - S[] arr = alloc.makeArray!S(5); - assert(arr.length == 5); - int[] arrInt = () @trusted { return (cast(int*) arr.ptr)[0 .. 5]; }(); - static immutable res = [42, 42, 42, 42, 42]; - assert(arrInt == res); - } - - import std.experimental.allocator.gc_allocator : GCAllocator; - import std.experimental.allocator.mallocator : Mallocator; - (alloc) /*pure nothrow*/ @safe { test1(alloc); test2(alloc);} (GCAllocator.instance); - (alloc) nothrow @safe @nogc { test1(alloc); test2(alloc);} (Mallocator.instance); - test2(theAllocator); -} - -@system unittest -{ - import std.algorithm.comparison : equal; - auto a = theAllocator.makeArray!(shared int)(5); - static assert(is(typeof(a) == shared(int)[])); - assert(a.length == 5); - assert(a.equal([0, 0, 0, 0, 0])); - - auto b = theAllocator.makeArray!(const int)(5); - static assert(is(typeof(b) == const(int)[])); - assert(b.length == 5); - assert(b.equal([0, 0, 0, 0, 0])); - - auto c = theAllocator.makeArray!(immutable int)(5); - static assert(is(typeof(c) == immutable(int)[])); - assert(c.length == 5); - assert(c.equal([0, 0, 0, 0, 0])); -} - -// https://issues.dlang.org/show_bug.cgi?id=19085 - makeArray with void -@system unittest -{ - auto b = theAllocator.makeArray!void(5); - scope(exit) theAllocator.dispose(b); - auto c = cast(ubyte[]) b; - assert(c.length == 5); - assert(c == [0, 0, 0, 0, 0]); // default initialization -} - -private enum hasPurePostblit(T) = !hasElaborateCopyConstructor!T || - is(typeof(() pure { T.init.__xpostblit(); })); - -private enum hasPureDtor(T) = !hasElaborateDestructor!T || - is(typeof(() pure { T.init.__xdtor(); })); - -// `true` when postblit and destructor of T cannot escape references to itself -private enum canSafelyDeallocPostRewind(T) = hasPurePostblit!T && hasPureDtor!T; - -/// Ditto -T[] makeArray(T, Allocator)(auto ref Allocator alloc, size_t length, T init) -{ - if (!length) return null; - auto m = alloc.allocate(T.sizeof * length); - if (!m.ptr) return null; - auto result = () @trusted { return cast(T[]) m; } (); - import std.traits : hasElaborateCopyConstructor; - static if (hasElaborateCopyConstructor!T) - { - scope(failure) - { - static if (canSafelyDeallocPostRewind!T) - () @trusted { alloc.deallocate(m); } (); - else - alloc.deallocate(m); - } - - size_t i = 0; - static if (hasElaborateDestructor!T) - { - scope (failure) - { - foreach (j; 0 .. i) - { - destroy(result[j]); - } - } - } - import core.lifetime : emplace; - for (; i < length; ++i) - { - emplace!T(&result[i], init); - } - } - else - { - alias U = Unqual!T; - () @trusted { fillWithMemcpy(cast(U[]) result, *(cast(U*) &init)); }(); - } - return result; -} - -/// -@system unittest -{ - import std.algorithm.comparison : equal; - static void test(T)() - { - T[] a = theAllocator.makeArray!T(2); - assert(a.equal([0, 0])); - a = theAllocator.makeArray!T(3, 42); - assert(a.equal([42, 42, 42])); - import std.range : only; - a = theAllocator.makeArray!T(only(42, 43, 44)); - assert(a.equal([42, 43, 44])); - } - test!int(); - test!(shared int)(); - test!(const int)(); - test!(immutable int)(); -} - -@system unittest -{ - void test(T)(in T initialValue) - { - auto t = theAllocator.makeArray!T(100, initialValue); - //auto t = theAllocator.makeArray(100, initialValue); // works well with the old code - } - - const int init = 3; - test(init); -} - -@system unittest -{ - void test(A)(auto ref A alloc) - { - long[] a = alloc.makeArray!long(0, 42); - assert(a.length == 0 && a.ptr is null); - a = alloc.makeArray!long(5, 42); - assert(a.length == 5); - assert(a == [ 42, 42, 42, 42, 42 ]); - } - import std.experimental.allocator.gc_allocator : GCAllocator; - (alloc) /*pure nothrow*/ @safe { test(alloc); } (GCAllocator.instance); - test(theAllocator); -} - -// test failure with a pure, failing struct -@safe unittest -{ - import std.exception : assertThrown, enforce; - - struct NoCopy - { - @disable this(); - - this(int b){} - - // can't be copied - this(this) - { - enforce(1 == 2); - } - } - import std.experimental.allocator.mallocator : Mallocator; - assertThrown(makeArray!NoCopy(Mallocator.instance, 10, NoCopy(42))); -} - -// test failure with an impure, failing struct -@system unittest -{ - import std.exception : assertThrown, enforce; - - static int i = 0; - struct Singleton - { - @disable this(); - - this(int b){} - - // can't be copied - this(this) - { - enforce(i++ == 0); - } - - ~this() - { - i--; - } - } - import std.experimental.allocator.mallocator : Mallocator; - assertThrown(makeArray!Singleton(Mallocator.instance, 10, Singleton(42))); -} - -/// Ditto -Unqual!(ElementEncodingType!R)[] makeArray(Allocator, R)(auto ref Allocator alloc, R range) -if (isInputRange!R && !isInfinite!R) -{ - alias T = Unqual!(ElementEncodingType!R); - return makeArray!(T, Allocator, R)(alloc, range); -} - -/// Ditto -T[] makeArray(T, Allocator, R)(auto ref Allocator alloc, R range) -if (isInputRange!R && !isInfinite!R) -{ - static if (isForwardRange!R || hasLength!R) - { - static if (hasLength!R || isNarrowString!R) - immutable length = range.length; - else - immutable length = range.save.walkLength; - - if (!length) return null; - auto m = alloc.allocate(T.sizeof * length); - if (!m.ptr) return null; - auto result = () @trusted { return cast(T[]) m; } (); - - size_t i = 0; - scope (failure) - { - foreach (j; 0 .. i) - { - auto p = () @trusted { return cast(Unqual!T*) &result[j]; }(); - destroy(p); - } - - static if (canSafelyDeallocPostRewind!T) - () @trusted { alloc.deallocate(m); } (); - else - alloc.deallocate(m); - } - - import core.internal.lifetime : emplaceRef; - static if (isNarrowString!R || isRandomAccessRange!R) - { - foreach (j; 0 .. range.length) - { - emplaceRef!T(result[i++], range[j]); - } - } - else - { - for (; !range.empty; range.popFront, ++i) - { - emplaceRef!T(result[i], range.front); - } - } - - return result; - } - else - { - // Estimated size - size_t estimated = 8; - auto m = alloc.allocate(T.sizeof * estimated); - if (!m.ptr) return null; - auto result = () @trusted { return cast(T[]) m; } (); - - size_t initialized = 0; - void bailout() - { - foreach (i; 0 .. initialized + 1) - { - destroy(result[i]); - } - - static if (canSafelyDeallocPostRewind!T) - () @trusted { alloc.deallocate(m); } (); - else - alloc.deallocate(m); - } - scope (failure) bailout; - - for (; !range.empty; range.popFront, ++initialized) - { - if (initialized == estimated) - { - // Need to reallocate - static if (hasPurePostblit!T) - auto success = () @trusted { return alloc.reallocate(m, T.sizeof * (estimated *= 2)); } (); - else - auto success = alloc.reallocate(m, T.sizeof * (estimated *= 2)); - if (!success) - { - bailout; - return null; - } - result = () @trusted { return cast(T[]) m; } (); - } - import core.internal.lifetime : emplaceRef; - emplaceRef(result[initialized], range.front); - } - - if (initialized < estimated) - { - // Try to shrink memory, no harm if not possible - static if (hasPurePostblit!T) - auto success = () @trusted { return alloc.reallocate(m, T.sizeof * initialized); } (); - else - auto success = alloc.reallocate(m, T.sizeof * initialized); - if (success) - result = () @trusted { return cast(T[]) m; } (); - } - - return result[0 .. initialized]; - } -} - -@system unittest -{ - void test(A)(auto ref A alloc) - { - long[] a = alloc.makeArray!long((int[]).init); - assert(a.length == 0 && a.ptr is null); - a = alloc.makeArray!long([5, 42]); - assert(a.length == 2); - assert(a == [ 5, 42]); - - // we can also infer the type - auto b = alloc.makeArray([4.0, 2.0]); - static assert(is(typeof(b) == double[])); - assert(b == [4.0, 2.0]); - } - import std.experimental.allocator.gc_allocator : GCAllocator; - (alloc) pure nothrow @safe { test(alloc); } (GCAllocator.instance); - test(theAllocator); -} - -// infer types for strings -@system unittest -{ - void test(A)(auto ref A alloc) - { - auto c = alloc.makeArray("fooπ😜"); - static assert(is(typeof(c) == char[])); - assert(c == "fooπ😜"); - - auto d = alloc.makeArray("fooπ😜"d); - static assert(is(typeof(d) == dchar[])); - assert(d == "fooπ😜"); - - auto w = alloc.makeArray("fooπ😜"w); - static assert(is(typeof(w) == wchar[])); - assert(w == "fooπ😜"); - } - - import std.experimental.allocator.gc_allocator : GCAllocator; - (alloc) pure nothrow @safe { test(alloc); } (GCAllocator.instance); - test(theAllocator); -} - -/*pure*/ nothrow @safe unittest -{ - import std.algorithm.comparison : equal; - import std.experimental.allocator.gc_allocator : GCAllocator; - import std.internal.test.dummyrange; - import std.range : iota; - foreach (DummyType; AllDummyRanges) - { - (alloc) pure nothrow @safe - { - DummyType d; - auto arr = alloc.makeArray(d); - assert(arr.length == 10); - assert(arr.equal(iota(1, 11))); - } (GCAllocator.instance); - } -} - -// test failure with a pure, failing struct -@safe unittest -{ - import std.exception : assertThrown, enforce; - - struct NoCopy - { - int b; - - @disable this(); - - this(int b) - { - this.b = b; - } - - // can't be copied - this(this) - { - enforce(b < 3, "there can only be three elements"); - } - } - import std.experimental.allocator.mallocator : Mallocator; - auto arr = [NoCopy(1), NoCopy(2), NoCopy(3)]; - assertThrown(makeArray!NoCopy(Mallocator.instance, arr)); - - struct NoCopyRange - { - static j = 0; - bool empty() - { - return j > 5; - } - - auto front() - { - return NoCopy(j); - } - - void popFront() - { - j++; - } - } - makeArray!NoCopy(Mallocator.instance, NoCopyRange()); // rvalue elements are forwarded/moved -} - -// test failure with an impure, failing struct -@system unittest -{ - import std.exception : assertThrown, enforce; - - static i = 0; - static maxElements = 2; - struct NoCopy - { - int val; - @disable this(); - - this(int b){ - this.val = i++; - } - - // can't be copied - this(this) - { - enforce(i++ < maxElements, "there can only be four elements"); - } - } - - import std.experimental.allocator.mallocator : Mallocator; - auto arr = [NoCopy(1), NoCopy(2)]; - assertThrown(makeArray!NoCopy(Mallocator.instance, arr)); - - i = 0; - maxElements = 0; // disallow any postblit - static j = 0; - - struct NoCopyRange - { - bool empty() - { - return j > 100; - } - - auto front() - { - return NoCopy(1); - } - - void popFront() - { - j++; - } - } - - auto arr2 = makeArray!NoCopy(Mallocator.instance, NoCopyRange()); - assert(i == j && i == 101); // all 101 rvalue elements forwarded/moved -} - -version (StdUnittest) -{ - private struct ForcedInputRange(T) - { - T[]* array; - pure nothrow @safe @nogc: - bool empty() { return !array || (*array).empty; } - ref T front() { return (*array)[0]; } - void popFront() { *array = (*array)[1 .. $]; } - } -} - -@system unittest -{ - import std.array : array; - import std.range : iota; - int[] arr = iota(10).array; - - void test(A)(auto ref A alloc) - { - ForcedInputRange!int r; - long[] a = alloc.makeArray!long(r); - assert(a.length == 0 && a.ptr is null); - auto arr2 = arr; - r.array = () @trusted { return &arr2; } (); - a = alloc.makeArray!long(r); - assert(a.length == 10); - assert(a == iota(10).array); - } - import std.experimental.allocator.gc_allocator : GCAllocator; - (alloc) pure nothrow @safe { test(alloc); } (GCAllocator.instance); - test(theAllocator); -} - -/** -Grows `array` by appending `delta` more elements. The needed memory is -allocated using `alloc`. The extra elements added are either default- -initialized, filled with copies of `init`, or initialized with values -fetched from `range`. - -Params: -T = element type of the array being created -alloc = the allocator used for getting memory -array = a reference to the array being grown -delta = number of elements to add (upon success the new length of `array` is -$(D array.length + delta)) -init = element used for filling the array -range = range used for initializing the array elements - -Returns: -`true` upon success, `false` if memory could not be allocated. In the -latter case `array` is left unaffected. - -Throws: -The first two overloads throw only if `alloc`'s primitives do. The -overloads that involve copy initialization deallocate memory and propagate the -exception if the copy operation throws. -*/ -bool expandArray(T, Allocator)(auto ref Allocator alloc, ref T[] array, - size_t delta) -{ - if (!delta) return true; - if (array is null) return false; - immutable oldLength = array.length; - void[] buf = array; - if (!alloc.reallocate(buf, buf.length + T.sizeof * delta)) return false; - array = cast(T[]) buf; - array[oldLength .. $].uninitializedFillDefault; - return true; -} - -@system unittest -{ - void test(A)(auto ref A alloc) - { - auto arr = alloc.makeArray!int([1, 2, 3]); - assert(alloc.expandArray(arr, 3)); - assert(arr == [1, 2, 3, 0, 0, 0]); - } - import std.experimental.allocator.gc_allocator : GCAllocator; - test(GCAllocator.instance); - test(theAllocator); -} - -/// Ditto -bool expandArray(T, Allocator)(auto ref Allocator alloc, ref T[] array, - size_t delta, auto ref T init) -{ - if (!delta) return true; - if (array is null) return false; - void[] buf = array; - if (!alloc.reallocate(buf, buf.length + T.sizeof * delta)) return false; - immutable oldLength = array.length; - array = cast(T[]) buf; - scope(failure) array[oldLength .. $].uninitializedFillDefault; - import std.algorithm.mutation : uninitializedFill; - array[oldLength .. $].uninitializedFill(init); - return true; -} - -@system unittest -{ - void test(A)(auto ref A alloc) - { - auto arr = alloc.makeArray!int([1, 2, 3]); - assert(alloc.expandArray(arr, 3, 1)); - assert(arr == [1, 2, 3, 1, 1, 1]); - } - import std.experimental.allocator.gc_allocator : GCAllocator; - test(GCAllocator.instance); - test(theAllocator); -} - -/// Ditto -bool expandArray(T, Allocator, R)(auto ref Allocator alloc, ref T[] array, - R range) -if (isInputRange!R) -{ - if (array is null) return false; - static if (isForwardRange!R) - { - immutable delta = walkLength(range.save); - if (!delta) return true; - immutable oldLength = array.length; - - // Reallocate support memory - void[] buf = array; - if (!alloc.reallocate(buf, buf.length + T.sizeof * delta)) - { - return false; - } - array = cast(T[]) buf; - // At this point we're committed to the new length. - - auto toFill = array[oldLength .. $]; - scope (failure) - { - // Fill the remainder with default-constructed data - toFill.uninitializedFillDefault; - } - - for (; !range.empty; range.popFront, toFill = toFill[1 .. $]) - { - assert(toFill.length > 0); - import core.lifetime : emplace; - emplace!T(&toFill[0], range.front); - } - assert(toFill.length == 0); - } - else - { - scope(failure) - { - // The last element didn't make it, fill with default - array[$ - 1 .. $].uninitializedFillDefault; - } - void[] buf = array; - for (; !range.empty; range.popFront) - { - if (!alloc.reallocate(buf, buf.length + T.sizeof)) - { - array = cast(T[]) buf; - return false; - } - import core.lifetime : emplace; - emplace!T(buf[$ - T.sizeof .. $], range.front); - } - - array = cast(T[]) buf; - } - return true; -} - -/// -@system unittest -{ - auto arr = theAllocator.makeArray!int([1, 2, 3]); - assert(theAllocator.expandArray(arr, 2)); - assert(arr == [1, 2, 3, 0, 0]); - import std.range : only; - assert(theAllocator.expandArray(arr, only(4, 5))); - assert(arr == [1, 2, 3, 0, 0, 4, 5]); -} - -@system unittest -{ - auto arr = theAllocator.makeArray!int([1, 2, 3]); - ForcedInputRange!int r; - int[] b = [ 1, 2, 3, 4 ]; - auto temp = b; - r.array = &temp; - assert(theAllocator.expandArray(arr, r)); - assert(arr == [1, 2, 3, 1, 2, 3, 4]); -} - -// Regression test for https://issues.dlang.org/show_bug.cgi?id=20929 -@system unittest -{ - static void test(Char, Allocator)(auto ref Allocator alloc) - { - auto arr = alloc.makeArray!Char(1, Char('f')); - - import std.utf : byUTF; - auto forwardRange = "oo".byUTF!Char(); - static assert(isForwardRange!(typeof(forwardRange))); - // Test the forward-range code-path. - assert(alloc.expandArray(arr, forwardRange)); - - assert(arr == "foo"); - - immutable(Char)[] temp = "bar"; - auto inputRange = ForcedInputRange!(immutable(Char))(&temp); - // Test the input-range code-path. - assert(alloc.expandArray(arr, inputRange)); - - assert(arr == "foobar"); - } - - import std.experimental.allocator.gc_allocator : GCAllocator; - test!char(GCAllocator.instance); - test!wchar(GCAllocator.instance); - test!char(theAllocator); - test!wchar(theAllocator); -} - -/** -Shrinks an array by `delta` elements. - -If $(D array.length < delta), does nothing and returns `false`. Otherwise, -destroys the last $(D array.length - delta) elements in the array and then -reallocates the array's buffer. If reallocation fails, fills the array with -default-initialized data. - -Params: -T = element type of the array being created -alloc = the allocator used for getting memory -array = a reference to the array being shrunk -delta = number of elements to remove (upon success the new length of `array` is $(D array.length - delta)) - -Returns: -`true` upon success, `false` if memory could not be reallocated. In the latter -case, the slice $(D array[$ - delta .. $]) is left with default-initialized -elements. - -Throws: -The first two overloads throw only if `alloc`'s primitives do. The -overloads that involve copy initialization deallocate memory and propagate the -exception if the copy operation throws. -*/ -bool shrinkArray(T, Allocator)(auto ref Allocator alloc, - ref T[] array, size_t delta) -{ - if (delta > array.length) return false; - - // Destroy elements. If a destructor throws, fill the already destroyed - // stuff with the default initializer. - { - size_t destroyed; - scope(failure) - { - array[$ - delta .. $][0 .. destroyed].uninitializedFillDefault; - } - foreach (ref e; array[$ - delta .. $]) - { - e.destroy; - ++destroyed; - } - } - - if (delta == array.length) - { - alloc.deallocate(array); - array = null; - return true; - } - - void[] buf = array; - if (!alloc.reallocate(buf, buf.length - T.sizeof * delta)) - { - // urgh, at least fill back with default - array[$ - delta .. $].uninitializedFillDefault; - return false; - } - array = cast(T[]) buf; - return true; -} - -/// -@system unittest -{ - int[] a = theAllocator.makeArray!int(100, 42); - assert(a.length == 100); - assert(theAllocator.shrinkArray(a, 98)); - assert(a.length == 2); - assert(a == [42, 42]); -} - -@system unittest -{ - void test(A)(auto ref A alloc) - { - long[] a = alloc.makeArray!long((int[]).init); - assert(a.length == 0 && a.ptr is null); - a = alloc.makeArray!long(100, 42); - assert(alloc.shrinkArray(a, 98)); - assert(a.length == 2); - assert(a == [ 42, 42]); - } - import std.experimental.allocator.gc_allocator : GCAllocator; - test(GCAllocator.instance); - test(theAllocator); -} - -/** - -Destroys and then deallocates (using `alloc`) the object pointed to by a -pointer, the class object referred to by a `class` or `interface` -reference, or an entire array. It is assumed the respective entities had been -allocated with the same allocator. - -*/ -void dispose(A, T)(auto ref A alloc, auto ref T* p) -{ - static if (hasElaborateDestructor!T) - { - destroy(*p); - } - alloc.deallocate((cast(void*) p)[0 .. T.sizeof]); - static if (__traits(isRef, p)) - p = null; -} - -/// Ditto -void dispose(A, T)(auto ref A alloc, auto ref T p) -if (is(T == class) || is(T == interface)) -{ - if (!p) return; - static if (is(T == interface)) - { - version (Windows) - { - import core.sys.windows.unknwn : IUnknown; - static assert(!is(T: IUnknown), "COM interfaces can't be destroyed in " - ~ __PRETTY_FUNCTION__); - } - auto ob = cast(Object) p; - } - else - alias ob = p; - auto support = (cast(void*) ob)[0 .. typeid(ob).initializer.length]; - destroy(p); - alloc.deallocate(support); - static if (__traits(isRef, p)) - p = null; -} - -/// Ditto -void dispose(A, T)(auto ref A alloc, auto ref T[] array) -{ - static if (hasElaborateDestructor!(typeof(array[0]))) - { - foreach (ref e; array) - { - destroy(e); - } - } - alloc.deallocate(array); - static if (__traits(isRef, array)) - array = null; -} - -@system unittest -{ - static int x; - static interface I - { - void method(); - } - static class A : I - { - int y; - override void method() { x = 21; } - ~this() { x = 42; } - } - static class B : A - { - } - auto a = theAllocator.make!A; - a.method(); - assert(x == 21); - theAllocator.dispose(a); - assert(x == 42); - - B b = theAllocator.make!B; - b.method(); - assert(x == 21); - theAllocator.dispose(b); - assert(x == 42); - - I i = theAllocator.make!B; - i.method(); - assert(x == 21); - theAllocator.dispose(i); - assert(x == 42); - - int[] arr = theAllocator.makeArray!int(43); - theAllocator.dispose(arr); -} - -// https://issues.dlang.org/show_bug.cgi?id=16512 -@system unittest -{ - import std.experimental.allocator.mallocator : Mallocator; - - int* i = Mallocator.instance.make!int(0); - Mallocator.instance.dispose(i); - assert(i is null); - - Object o = Mallocator.instance.make!Object(); - Mallocator.instance.dispose(o); - assert(o is null); - - uint* u = Mallocator.instance.make!uint(0); - Mallocator.instance.dispose((){return u;}()); - assert(u !is null); - - uint[] ua = Mallocator.instance.makeArray!uint([0,1,2]); - Mallocator.instance.dispose(ua); - assert(ua is null); -} - -// https://issues.dlang.org/show_bug.cgi?id=15721 -@system unittest -{ - import std.experimental.allocator.mallocator : Mallocator; - - interface Foo {} - class Bar: Foo {} - - Bar bar; - Foo foo; - bar = Mallocator.instance.make!Bar; - foo = cast(Foo) bar; - Mallocator.instance.dispose(foo); -} - -/** -Allocates a multidimensional array of elements of type T. - -Params: -N = number of dimensions -T = element type of an element of the multidimensional arrat -alloc = the allocator used for getting memory -lengths = static array containing the size of each dimension - -Returns: -An N-dimensional array with individual elements of type T. -*/ -auto makeMultidimensionalArray(T, Allocator, size_t N)(auto ref Allocator alloc, size_t[N] lengths...) -{ - static if (N == 1) - { - return makeArray!T(alloc, lengths[0]); - } - else - { - alias E = typeof(makeMultidimensionalArray!(T, Allocator, N - 1)(alloc, lengths[1 .. $])); - auto ret = makeArray!E(alloc, lengths[0]); - foreach (ref e; ret) - e = makeMultidimensionalArray!(T, Allocator, N - 1)(alloc, lengths[1 .. $]); - return ret; - } -} - -/// -@system unittest -{ - import std.experimental.allocator.mallocator : Mallocator; - - auto mArray = Mallocator.instance.makeMultidimensionalArray!int(2, 3, 6); - - // deallocate when exiting scope - scope(exit) - { - Mallocator.instance.disposeMultidimensionalArray(mArray); - } - - assert(mArray.length == 2); - foreach (lvl2Array; mArray) - { - assert(lvl2Array.length == 3); - foreach (lvl3Array; lvl2Array) - assert(lvl3Array.length == 6); - } -} - -/** -Destroys and then deallocates a multidimensional array, assuming it was -created with makeMultidimensionalArray and the same allocator was used. - -Params: -T = element type of an element of the multidimensional array -alloc = the allocator used for getting memory -array = the multidimensional array that is to be deallocated -*/ -void disposeMultidimensionalArray(T, Allocator)(auto ref Allocator alloc, auto ref T[] array) -{ - static if (isArray!T) - { - foreach (ref e; array) - disposeMultidimensionalArray(alloc, e); - } - - dispose(alloc, array); - static if (__traits(isRef, array)) - array = null; -} - -/// -@system unittest -{ - struct TestAllocator - { - import std.experimental.allocator.common : platformAlignment; - import std.experimental.allocator.mallocator : Mallocator; - - alias allocator = Mallocator.instance; - - private static struct ByteRange - { - void* ptr; - size_t length; - } - - private ByteRange[] _allocations; - - enum uint alignment = platformAlignment; - - void[] allocate(size_t numBytes) - { - auto ret = allocator.allocate(numBytes); - _allocations ~= ByteRange(ret.ptr, ret.length); - return ret; - } - - bool deallocate(void[] bytes) - { - import std.algorithm.mutation : remove; - import std.algorithm.searching : canFind; - - bool pred(ByteRange other) - { return other.ptr == bytes.ptr && other.length == bytes.length; } - - assert(_allocations.canFind!pred); - - _allocations = _allocations.remove!pred; - return allocator.deallocate(bytes); - } - - ~this() - { - assert(!_allocations.length); - } - } - - TestAllocator allocator; - - auto mArray = allocator.makeMultidimensionalArray!int(2, 3, 5, 6, 7, 2); - - allocator.disposeMultidimensionalArray(mArray); -} - -/** - -Returns a dynamically-typed `CAllocator` built around a given statically- -typed allocator `a` of type `A`. Passing a pointer to the allocator -creates a dynamic allocator around the allocator pointed to by the pointer, -without attempting to copy or move it. Passing the allocator by value or -reference behaves as follows. - -$(UL -$(LI If `A` has no state, the resulting object is allocated in static -shared storage.) -$(LI If `A` has state, the result will $(REF move, std,algorithm,mutation) -the supplied allocator $(D A a) within. The result itself is allocated in its -own statically-typed allocator.) -) - -*/ -RCIAllocator allocatorObject(A)(auto ref A a) -if (!isPointer!A) -{ - import core.lifetime : emplace; - static if (stateSize!A == 0) - { - enum s = stateSize!(CAllocatorImpl!A).divideRoundUp(ulong.sizeof); - __gshared ulong[s] state; - __gshared RCIAllocator result; - if (result.isNull) - { - // Don't care about a few races - result = RCIAllocator(emplace!(CAllocatorImpl!A)(state[])); - } - assert(!result.isNull); - return result; - } - else - { - auto state = a.allocate(stateSize!(CAllocatorImpl!A)); - import std.algorithm.mutation : move; - import std.traits : hasMember; - static if (hasMember!(A, "deallocate")) - { - scope(failure) a.deallocate(state); - } - auto tmp = cast(CAllocatorImpl!A) emplace!(CAllocatorImpl!A)(state); - move(a, tmp.impl); - return RCIAllocator(tmp); - } -} - -/// Ditto -RCIAllocator allocatorObject(A)(A* pa) -{ - assert(pa); - import core.lifetime : emplace; - auto state = pa.allocate(stateSize!(CAllocatorImpl!(A, Yes.indirect))); - import std.traits : hasMember; - static if (hasMember!(A, "deallocate")) - { - scope(failure) pa.deallocate(state); - } - return RCIAllocator(emplace!(CAllocatorImpl!(A, Yes.indirect)) - (state, pa)); -} - -/// -@system unittest -{ - import std.experimental.allocator.mallocator : Mallocator; - - RCIAllocator a = allocatorObject(Mallocator.instance); - auto b = a.allocate(100); - assert(b.length == 100); - assert(a.deallocate(b)); - - // The in-situ region must be used by pointer - import std.experimental.allocator.building_blocks.region : InSituRegion; - auto r = InSituRegion!1024(); - a = allocatorObject(&r); - b = a.allocate(200); - assert(b.length == 200); - // In-situ regions can deallocate the last allocation - assert(a.deallocate(b)); -} - -@system unittest -{ - import std.conv; - import std.experimental.allocator.mallocator; - import std.experimental.allocator.building_blocks.stats_collector; - - alias SCAlloc = StatsCollector!(Mallocator, Options.bytesUsed); - SCAlloc statsCollectorAlloc; - assert(statsCollectorAlloc.bytesUsed == 0); - - auto _allocator = allocatorObject(statsCollectorAlloc); - // Ensure that the allocator was passed through in CAllocatorImpl - // This allocator was used to allocate the chunk that holds the - // CAllocatorImpl object; which is it's own wrapper - assert((cast(CAllocatorImpl!(SCAlloc))(_allocator._alloc)).impl.bytesUsed - == stateSize!(CAllocatorImpl!(SCAlloc))); - _allocator.allocate(1); - assert((cast(CAllocatorImpl!(SCAlloc))(_allocator._alloc)).impl.bytesUsed - == stateSize!(CAllocatorImpl!(SCAlloc)) + 1); -} - -/** - -Returns a dynamically-typed `CSharedAllocator` built around a given statically- -typed allocator `a` of type `A`. Passing a pointer to the allocator -creates a dynamic allocator around the allocator pointed to by the pointer, -without attempting to copy or move it. Passing the allocator by value or -reference behaves as follows. - -$(UL -$(LI If `A` has no state, the resulting object is allocated in static -shared storage.) -$(LI If `A` has state and is copyable, the result will -$(REF move, std,algorithm,mutation) the supplied allocator $(D A a) within. -The result itself is allocated in its own statically-typed allocator.) -$(LI If `A` has state and is not copyable, the result will move the -passed-in argument into the result. The result itself is allocated in its own -statically-typed allocator.) -) - -*/ -//nothrow @safe -//nothrow @nogc @safe -nothrow -RCISharedAllocator sharedAllocatorObject(A)(auto ref A a) -if (!isPointer!A) -{ - import core.lifetime : emplace; - static if (stateSize!A == 0) - { - enum s = stateSize!(CSharedAllocatorImpl!A).divideRoundUp(ulong.sizeof); - static shared ulong[s] state; - static RCISharedAllocator result; - if (result.isNull) - { - // Don't care about a few races - result = RCISharedAllocator( - (cast(shared CSharedAllocatorImpl!A)( - emplace!(CSharedAllocatorImpl!A)( - (() @trusted => cast(ulong[]) state[])())))); - } - assert(!result.isNull); - return result; - } - else static if (is(typeof({ shared A b = a; shared A c = b; }))) // copyable - { - auto state = a.allocate(stateSize!(CSharedAllocatorImpl!A)); - import std.algorithm.mutation : move; - import std.traits : hasMember; - static if (hasMember!(A, "deallocate")) - { - scope(failure) a.deallocate(state); - } - auto tmp = emplace!(shared CSharedAllocatorImpl!A)(state); - move(a, tmp.impl); - return RCISharedAllocator(tmp); - } - else // the allocator object is not copyable - { - assert(0, "Not yet implemented"); - } -} - -/// Ditto -RCISharedAllocator sharedAllocatorObject(A)(A* pa) -{ - assert(pa); - import core.lifetime : emplace; - auto state = pa.allocate(stateSize!(CSharedAllocatorImpl!(A, Yes.indirect))); - import std.traits : hasMember; - static if (hasMember!(A, "deallocate")) - { - scope(failure) pa.deallocate(state); - } - return RCISharedAllocator(emplace!(shared CSharedAllocatorImpl!(A, Yes.indirect))(state, pa)); -} - - -/** - -Implementation of `IAllocator` using `Allocator`. This adapts a -statically-built allocator type to `IAllocator` that is directly usable by -non-templated code. - -Usually `CAllocatorImpl` is used indirectly by calling $(LREF theAllocator). -*/ -class CAllocatorImpl(Allocator, Flag!"indirect" indirect = No.indirect) - : IAllocator -{ - import std.traits : hasMember; - - static if (stateSize!Allocator) private size_t rc = 1; - - /** - The implementation is available as a public member. - */ - static if (indirect) - { - nothrow: - private Allocator* pimpl; - - @nogc pure @safe - ref Allocator impl() - { - return *pimpl; - } - - @nogc pure @safe - this(Allocator* pa) - { - pimpl = pa; - } - } - else - { - static if (stateSize!Allocator) Allocator impl; - else alias impl = Allocator.instance; - } - -nothrow: - /// Returns `impl.alignment`. - override @property uint alignment() - { - return impl.alignment; - } - - /** - Returns `impl.goodAllocSize(s)`. - */ - override size_t goodAllocSize(size_t s) - { - return impl.goodAllocSize(s); - } - - /** - Returns `impl.allocate(s)`. - */ - override void[] allocate(size_t s, TypeInfo ti = null) - { - return impl.allocate(s); - } - - /** - If `impl.alignedAllocate` exists, calls it and returns the result. - Otherwise, always returns `null`. - */ - override void[] alignedAllocate(size_t s, uint a) - { - static if (hasMember!(Allocator, "alignedAllocate")) - return impl.alignedAllocate(s, a); - else - return null; - } - - /** - If `Allocator` implements `owns`, forwards to it. Otherwise, returns - `Ternary.unknown`. - */ - override Ternary owns(void[] b) - { - static if (hasMember!(Allocator, "owns")) return impl.owns(b); - else return Ternary.unknown; - } - - /// Returns $(D impl.expand(b, s)) if defined, `false` otherwise. - override bool expand(ref void[] b, size_t s) - { - static if (hasMember!(Allocator, "expand")) - return impl.expand(b, s); - else - return s == 0; - } - - /// Returns $(D impl.reallocate(b, s)). - override bool reallocate(ref void[] b, size_t s) - { - return impl.reallocate(b, s); - } - - /// Forwards to `impl.alignedReallocate` if defined, `false` otherwise. - bool alignedReallocate(ref void[] b, size_t s, uint a) - { - static if (!hasMember!(Allocator, "alignedAllocate")) - { - return false; - } - else - { - return impl.alignedReallocate(b, s, a); - } - } - - // Undocumented for now - Ternary resolveInternalPointer(const void* p, ref void[] result) - { - static if (hasMember!(Allocator, "resolveInternalPointer")) - { - return impl.resolveInternalPointer(p, result); - } - else - { - return Ternary.unknown; - } - } - - /** - If `impl.deallocate` is not defined, returns `false`. Otherwise it forwards - the call. - */ - override bool deallocate(void[] b) - { - static if (hasMember!(Allocator, "deallocate")) - { - return impl.deallocate(b); - } - else - { - return false; - } - } - - /** - Calls `impl.deallocateAll()` and returns the result if defined, - otherwise returns `false`. - */ - override bool deallocateAll() - { - static if (hasMember!(Allocator, "deallocateAll")) - { - return impl.deallocateAll(); - } - else - { - return false; - } - } - - /** - Forwards to `impl.empty()` if defined, otherwise returns `Ternary.unknown`. - */ - override Ternary empty() - { - static if (hasMember!(Allocator, "empty")) - { - return Ternary(impl.empty); - } - else - { - return Ternary.unknown; - } - } - - /** - Returns `impl.allocateAll()` if present, `null` otherwise. - */ - override void[] allocateAll() - { - static if (hasMember!(Allocator, "allocateAll")) - { - return impl.allocateAll(); - } - else - { - return null; - } - } - - @nogc nothrow pure @safe - override void incRef() - { - static if (stateSize!Allocator) ++rc; - } - - @nogc nothrow pure @trusted - override bool decRef() - { - static if (stateSize!Allocator) - { - import core.stdc.string : memcpy; - - if (rc == 1) - { - static if (indirect) - { - Allocator* tmp = pimpl; - } - else - { - Allocator tmp; - memcpy(&tmp, &this.impl, Allocator.sizeof); - } - void[] support = (cast(void*) this)[0 .. stateSize!(typeof(this))]; - tmp.deallocate(support); - return false; - } - - --rc; - return true; - } - else - { - return true; - } - } -} - -/** - -Implementation of `ISharedAllocator` using `Allocator`. This adapts a -statically-built, shareable across threads, allocator type to `ISharedAllocator` -that is directly usable by non-templated code. - -Usually `CSharedAllocatorImpl` is used indirectly by calling -$(LREF processAllocator). -*/ -class CSharedAllocatorImpl(Allocator, Flag!"indirect" indirect = No.indirect) - : ISharedAllocator -{ - import std.traits : hasMember; - import core.atomic : atomicOp, atomicLoad; - - static if (stateSize!Allocator) shared size_t rc = 1; - - /** - The implementation is available as a public member. - */ - static if (indirect) - { - nothrow: - private shared Allocator* pimpl; - - @nogc pure @safe - ref Allocator impl() shared - { - return *pimpl; - } - - @nogc pure @safe - this(Allocator* pa) shared - { - pimpl = pa; - } - } - else - { - static if (stateSize!Allocator) shared Allocator impl; - else alias impl = Allocator.instance; - } - -nothrow: - /// Returns `impl.alignment`. - override @property uint alignment() shared - { - return impl.alignment; - } - - /** - Returns `impl.goodAllocSize(s)`. - */ - override size_t goodAllocSize(size_t s) shared - { - return impl.goodAllocSize(s); - } - - /** - Returns `impl.allocate(s)`. - */ - override void[] allocate(size_t s, TypeInfo ti = null) shared - { - return impl.allocate(s); - } - - /** - If `impl.alignedAllocate` exists, calls it and returns the result. - Otherwise, always returns `null`. - */ - override void[] alignedAllocate(size_t s, uint a) shared - { - static if (hasMember!(Allocator, "alignedAllocate")) - return impl.alignedAllocate(s, a); - else - return null; - } - - /** - If `Allocator` implements `owns`, forwards to it. Otherwise, returns - `Ternary.unknown`. - */ - override Ternary owns(void[] b) shared - { - static if (hasMember!(Allocator, "owns")) return impl.owns(b); - else return Ternary.unknown; - } - - /// Returns $(D impl.expand(b, s)) if defined, `false` otherwise. - override bool expand(ref void[] b, size_t s) shared - { - static if (hasMember!(Allocator, "expand")) - return impl.expand(b, s); - else - return s == 0; - } - - /// Returns $(D impl.reallocate(b, s)). - override bool reallocate(ref void[] b, size_t s) shared - { - return impl.reallocate(b, s); - } - - /// Forwards to `impl.alignedReallocate` if defined, `false` otherwise. - bool alignedReallocate(ref void[] b, size_t s, uint a) shared - { - static if (!hasMember!(Allocator, "alignedAllocate")) - { - return false; - } - else - { - return impl.alignedReallocate(b, s, a); - } - } - - // Undocumented for now - Ternary resolveInternalPointer(const void* p, ref void[] result) shared - { - static if (hasMember!(Allocator, "resolveInternalPointer")) - { - return impl.resolveInternalPointer(p, result); - } - else - { - return Ternary.unknown; - } - } - - /** - If `impl.deallocate` is not defined, returns `false`. Otherwise it forwards - the call. - */ - override bool deallocate(void[] b) shared - { - static if (hasMember!(Allocator, "deallocate")) - { - return impl.deallocate(b); - } - else - { - return false; - } - } - - /** - Calls `impl.deallocateAll()` and returns the result if defined, - otherwise returns `false`. - */ - override bool deallocateAll() shared - { - static if (hasMember!(Allocator, "deallocateAll")) - { - return impl.deallocateAll(); - } - else - { - return false; - } - } - - /** - Forwards to `impl.empty()` if defined, otherwise returns `Ternary.unknown`. - */ - override Ternary empty() shared - { - static if (hasMember!(Allocator, "empty")) - { - return Ternary(impl.empty); - } - else - { - return Ternary.unknown; - } - } - - /** - Returns `impl.allocateAll()` if present, `null` otherwise. - */ - override void[] allocateAll() shared - { - static if (hasMember!(Allocator, "allocateAll")) - { - return impl.allocateAll(); - } - else - { - return null; - } - } - - @nogc nothrow pure @safe - override void incRef() shared - { - static if (stateSize!Allocator) atomicOp!"+="(rc, 1); - } - - @nogc nothrow pure @trusted - override bool decRef() shared - { - static if (stateSize!Allocator) - { - import core.stdc.string : memcpy; - - // rc starts as 1 to avoid comparing with size_t(0) - 1 - if (atomicOp!"-="(rc, 1) == 0) - { - static if (indirect) - { - Allocator* tmp = pimpl; - } - else - { - Allocator tmp; - memcpy(cast(void*) &tmp, cast(void*) &this.impl, Allocator.sizeof); - Allocator empty; - memcpy(cast(void*) &this.impl, cast(void*) &empty, Allocator.sizeof); - } - void[] support = (cast(void*) this)[0 .. stateSize!(typeof(this))]; - (cast(bool delegate(void[]) @nogc nothrow pure)(&tmp.deallocate))(support); - return false; - } - return true; - } - else - { - return true; - } - } -} - - -// Example in intro above -@system unittest -{ - // Allocate an int, initialize it with 42 - int* p = theAllocator.make!int(42); - assert(*p == 42); - - // Destroy and deallocate it - theAllocator.dispose(p); - - // Allocate using the global process allocator - p = processAllocator.make!int(100); - assert(*p == 100); - - // Destroy and deallocate - processAllocator.dispose(p); - - // Create an array of 50 doubles initialized to -1.0 - double[] arr = theAllocator.makeArray!double(50, -1.0); - - // Check internal pointer - void[] result; - assert(theAllocator.resolveInternalPointer(null, result) == Ternary.no); - Ternary r = theAllocator.resolveInternalPointer(arr.ptr, result); - assert(result.ptr is arr.ptr && result.length >= arr.length); - - // Append two zeros to it - theAllocator.expandArray(arr, 2, 0.0); - // On second thought, take that back - theAllocator.shrinkArray(arr, 2); - // Destroy and deallocate - theAllocator.dispose(arr); -} - -/** - -Stores an allocator object in thread-local storage (i.e. non-`shared` D -global). `ThreadLocal!A` is a subtype of `A` so it appears to implement -`A`'s allocator primitives. - -`A` must hold state, otherwise `ThreadLocal!A` refuses instantiation. This -means e.g. `ThreadLocal!Mallocator` does not work because `Mallocator`'s -state is not stored as members of `Mallocator`, but instead is hidden in the -C library implementation. - -*/ -struct ThreadLocal(A) -{ - static assert(stateSize!A, - typeof(A).stringof - ~ " does not have state so it cannot be used with ThreadLocal"); - - /** - The allocator instance. - */ - static A instance; - - /** - `ThreadLocal!A` is a subtype of `A` so it appears to implement `A`'s - allocator primitives. - */ - alias instance this; - - /** - `ThreadLocal` disables all constructors. The intended usage is - `ThreadLocal!A.instance`. - */ - @disable this(); - /// Ditto - @disable this(this); -} - -/// -@system -unittest -{ - import std.experimental.allocator.building_blocks.free_list : FreeList; - import std.experimental.allocator.gc_allocator : GCAllocator; - import std.experimental.allocator.mallocator : Mallocator; - - static assert(!is(ThreadLocal!Mallocator)); - static assert(!is(ThreadLocal!GCAllocator)); - alias Allocator = ThreadLocal!(FreeList!(GCAllocator, 0, 8)); - auto b = Allocator.instance.allocate(5); - static assert(__traits(hasMember, Allocator, "allocate")); -} - -/* -(Not public.) - -A binary search tree that uses no allocation of its own. Instead, it relies on -user code to allocate nodes externally. Then `EmbeddedTree`'s primitives wire -the nodes appropriately. - -Warning: currently `EmbeddedTree` is not using rebalancing, so it may -degenerate. A red-black tree implementation storing the color with one of the -pointers is planned for the future. -*/ -private struct EmbeddedTree(T, alias less) -{ - static struct Node - { - T payload; - Node* left, right; - } - - private Node* root; - - private Node* insert(Node* n, ref Node* backref) - { - backref = n; - n.left = n.right = null; - return n; - } - - Node* find(Node* data) - { - for (auto n = root; n; ) - { - if (less(data, n)) - { - n = n.left; - } - else if (less(n, data)) - { - n = n.right; - } - else - { - return n; - } - } - return null; - } - - Node* insert(Node* data) - { - if (!root) - { - root = data; - data.left = data.right = null; - return root; - } - auto n = root; - for (;;) - { - if (less(data, n)) - { - if (!n.left) - { - // Found insertion point - return insert(data, n.left); - } - n = n.left; - } - else if (less(n, data)) - { - if (!n.right) - { - // Found insertion point - return insert(data, n.right); - } - n = n.right; - } - else - { - // Found - return n; - } - if (!n) return null; - } - } - - Node* remove(Node* data) - { - auto n = root; - Node* parent = null; - for (;;) - { - if (!n) return null; - if (less(data, n)) - { - parent = n; - n = n.left; - } - else if (less(n, data)) - { - parent = n; - n = n.right; - } - else - { - // Found - remove(n, parent); - return n; - } - } - } - - private void remove(Node* n, Node* parent) - { - assert(n); - assert(!parent || parent.left == n || parent.right == n); - Node** referrer = parent - ? (parent.left == n ? &parent.left : &parent.right) - : &root; - if (!n.left) - { - *referrer = n.right; - } - else if (!n.right) - { - *referrer = n.left; - } - else - { - // Find the leftmost child in the right subtree - auto leftmost = n.right; - Node** leftmostReferrer = &n.right; - while (leftmost.left) - { - leftmostReferrer = &leftmost.left; - leftmost = leftmost.left; - } - // Unlink leftmost from there - *leftmostReferrer = leftmost.right; - // Link leftmost in lieu of n - leftmost.left = n.left; - leftmost.right = n.right; - *referrer = leftmost; - } - } - - Ternary empty() const - { - return Ternary(!root); - } - - void dump() - { - import std.stdio : writeln; - writeln(typeid(this), " @ ", cast(void*) &this); - dump(root, 3); - } - - void dump(Node* r, uint indent) - { - import std.stdio : write, writeln; - import std.range : repeat; - import std.array : array; - - write(repeat(' ', indent).array); - if (!r) - { - writeln("(null)"); - return; - } - writeln(r.payload, " @ ", cast(void*) r); - dump(r.left, indent + 3); - dump(r.right, indent + 3); - } - - void assertSane() - { - static bool isBST(Node* r, Node* lb, Node* ub) - { - if (!r) return true; - if (lb && !less(lb, r)) return false; - if (ub && !less(r, ub)) return false; - return isBST(r.left, lb, r) && - isBST(r.right, r, ub); - } - if (isBST(root, null, null)) return; - dump; - assert(0); - } -} - -@system -unittest -{ - import std.experimental.allocator.gc_allocator : GCAllocator; - - alias a = GCAllocator.instance; - alias Tree = EmbeddedTree!(int, (a, b) => a.payload < b.payload); - Tree t; - assert(t.empty == Ternary.yes); - int[] vals = [ 6, 3, 9, 1, 0, 2, 8, 11 ]; - foreach (v; vals) - { - auto n = new Tree.Node(v, null, null); - assert(t.insert(n)); - assert(n); - t.assertSane; - } - assert(t.empty != Ternary.yes); - foreach (v; vals) - { - Tree.Node n = { v }; - assert(t.remove(&n)); - t.assertSane; - } - assert(t.empty == Ternary.yes); -} - -/* - -`InternalPointersTree` adds a primitive on top of another allocator: calling -`resolveInternalPointer(p)` returns the block within which the internal -pointer `p` lies. Pointers right after the end of allocated blocks are also -considered internal. - -The implementation stores three additional words with each allocation (one for -the block size and two for search management). - -*/ -private struct InternalPointersTree(Allocator) -{ - import std.experimental.allocator.building_blocks.affix_allocator : AffixAllocator; - - alias Tree = EmbeddedTree!(size_t, - (a, b) => cast(void*) a + a.payload < cast(void*) b); - alias Parent = AffixAllocator!(Allocator, Tree.Node); - - // Own state - private Tree blockMap; - - alias alignment = Parent.alignment; - - /** - The implementation is available as a public member. - */ - static if (stateSize!Parent) Parent parent; - else alias parent = Parent.instance; - - /// Allocator API. - void[] allocate(size_t bytes) - { - auto r = parent.allocate(bytes); - if (!r.ptr) return r; - Tree.Node* n = &parent.prefix(r); - n.payload = bytes; - blockMap.insert(n) || assert(0); - return r; - } - - /// Ditto - bool deallocate(void[] b) - { - if (!b.ptr) return true; - Tree.Node* n = &parent.prefix(b); - blockMap.remove(n) || assert(false); - parent.deallocate(b); - return true; - } - - /// Ditto - static if (hasMember!(Allocator, "reallocate")) - bool reallocate(ref void[] b, size_t s) - { - auto n = &parent.prefix(b); - assert(n.payload == b.length); - blockMap.remove(n) || assert(0); - if (!parent.reallocate(b, s)) - { - // Failed, must reinsert the same node in the tree - assert(n.payload == b.length); - blockMap.insert(n) || assert(0); - return false; - } - // Insert the new node - n = &parent.prefix(b); - n.payload = s; - blockMap.insert(n) || assert(0); - return true; - } - - /// Ditto - Ternary owns(void[] b) - { - void[] result; - return resolveInternalPointer(b.ptr, result); - } - - /// Ditto - Ternary empty() - { - return Ternary(blockMap.empty); - } - - /** Returns the block inside which `p` resides, or `null` if the - pointer does not belong. - */ - pure nothrow @safe @nogc - Ternary resolveInternalPointer(const void* p, ref void[] result) - { - // Must define a custom find - Tree.Node* find() - { - for (auto n = blockMap.root; n; ) - { - if (p < n) - { - n = n.left; - } - else if ((() @trusted => p > (cast(void*) (n + 1)) + n.payload)()) - { - n = n.right; - } - else - { - return n; - } - } - return null; - } - - auto n = find(); - if (!n) return Ternary.no; - result = (() @trusted => (cast(void*) (n + 1))[0 .. n.payload])(); - return Ternary.yes; - } -} - -@system -unittest -{ - import std.experimental.allocator.mallocator : Mallocator; - import std.random : randomCover; - - InternalPointersTree!(Mallocator) a; - int[] vals = [ 6, 3, 9, 1, 2, 8, 11 ]; - void[][] allox; - foreach (v; vals) - { - allox ~= a.allocate(v); - } - a.blockMap.assertSane; - - foreach (b; allox) - { - () pure nothrow @safe { - void[] p; - Ternary r = (() @nogc => a.resolveInternalPointer(&b[0], p))(); - assert(&p[0] == &b[0] && p.length >= b.length); - r = a.resolveInternalPointer((() @trusted => &b[0] + b.length)(), p); - - /* This line randomly fails on MacOS 12.x x64 - * https://issues.dlang.org/show_bug.cgi?id=22660 - * Commenting it out until someone can fix it. - */ - //assert(&p[0] == &b[0] && p.length >= b.length); - - r = a.resolveInternalPointer((() @trusted => &b[0] + b.length / 2)(), p); - assert(&p[0] == &b[0] && p.length >= b.length); - auto bogus = new void[b.length]; - assert(a.resolveInternalPointer(&bogus[0], p) == Ternary.no); - }(); - } - - foreach (b; allox.randomCover) - { - () nothrow @nogc { a.deallocate(b); }(); - } - - assert(a.empty == Ternary.yes); -} - -//version (std_allocator_benchmark) -@system -unittest -{ - import std.experimental.allocator.building_blocks.null_allocator : NullAllocator; - import std.experimental.allocator.building_blocks.allocator_list : AllocatorList; - import std.experimental.allocator.building_blocks.bitmapped_block : BitmappedBlock; - import std.experimental.allocator.building_blocks.segregator : Segregator; - import std.experimental.allocator.building_blocks.bucketizer : Bucketizer; - import std.experimental.allocator.building_blocks.free_list : FreeList; - import std.experimental.allocator.gc_allocator : GCAllocator; - import std.experimental.allocator.mallocator : Mallocator; - - static void testSpeed(A)() - { - static if (stateSize!A) A a; - else alias a = A.instance; - - void[][128] bufs; - - import std.random; - foreach (i; 0 .. 100_000) - { - auto j = uniform(0, bufs.length); - switch (uniform(0, 2)) - { - case 0: - () nothrow @nogc { a.deallocate(bufs[j]); }(); - bufs[j] = a.allocate(uniform(0, 4096)); - break; - case 1: - () nothrow @nogc { a.deallocate(bufs[j]); }(); - bufs[j] = null; - break; - default: - assert(0); - } - } - } - - import std.algorithm.comparison : max; - - alias FList = FreeList!(GCAllocator, 0, unbounded); - alias A = Segregator!( - 8, FreeList!(GCAllocator, 0, 8), - 128, Bucketizer!(FList, 1, 128, 16), - 256, Bucketizer!(FList, 129, 256, 32), - 512, Bucketizer!(FList, 257, 512, 64), - 1024, Bucketizer!(FList, 513, 1024, 128), - 2048, Bucketizer!(FList, 1025, 2048, 256), - 3584, Bucketizer!(FList, 2049, 3584, 512), - 4072 * 1024, AllocatorList!( - (size_t n) => BitmappedBlock!(4096)(cast(ubyte[]) GCAllocator.instance.allocate( - max(n, 4072 * 1024)))), - GCAllocator - ); - - import std.stdio; - import std.conv : to; - import std.datetime.stopwatch; - import std.algorithm.iteration : map; - - if (false) writeln(benchmark!( - testSpeed!NullAllocator, - testSpeed!Mallocator, - testSpeed!GCAllocator, - testSpeed!(ThreadLocal!A), - testSpeed!(A), - )(20)[].map!(t => t.to!Duration)); -} - -@system -unittest -{ - import std.experimental.allocator.building_blocks.free_list : FreeList; - import std.experimental.allocator.building_blocks.region : InSituRegion; - import std.experimental.allocator.building_blocks.fallback_allocator : FallbackAllocator; - import std.experimental.allocator.gc_allocator : GCAllocator; - import std.experimental.allocator.mallocator : Mallocator; - - auto a = allocatorObject(Mallocator.instance); - auto b = a.allocate(100); - assert(b.length == 100); - - FreeList!(GCAllocator, 0, 8) fl; - auto sa = allocatorObject(fl); - b = a.allocate(101); - assert(b.length == 101); - - FallbackAllocator!(InSituRegion!(10240, 64), GCAllocator) fb; - // Doesn't work yet... - //a = allocatorObject(fb); - //b = a.allocate(102); - //assert(b.length == 102); -} - -/// -@system -unittest -{ - import std.experimental.allocator.building_blocks.allocator_list : AllocatorList; - import std.experimental.allocator.building_blocks.bitmapped_block : BitmappedBlock; - import std.experimental.allocator.building_blocks.segregator : Segregator; - import std.experimental.allocator.building_blocks.bucketizer : Bucketizer; - import std.experimental.allocator.building_blocks.free_list : FreeList; - import std.experimental.allocator.gc_allocator : GCAllocator; - - /// Define an allocator bound to the built-in GC. - auto alloc = allocatorObject(GCAllocator.instance); - auto b = alloc.allocate(42); - assert(b.length == 42); - assert(alloc.deallocate(b)); - - import std.algorithm.comparison : max; - // Define an elaborate allocator and bind it to the class API. - alias FList = FreeList!(GCAllocator, 0, unbounded); - alias A = ThreadLocal!( - Segregator!( - 8, FreeList!(GCAllocator, 0, 8), - 128, Bucketizer!(FList, 1, 128, 16), - 256, Bucketizer!(FList, 129, 256, 32), - 512, Bucketizer!(FList, 257, 512, 64), - 1024, Bucketizer!(FList, 513, 1024, 128), - 2048, Bucketizer!(FList, 1025, 2048, 256), - 3584, Bucketizer!(FList, 2049, 3584, 512), - 4072 * 1024, AllocatorList!( - (n) => BitmappedBlock!(4096)(cast(ubyte[]) GCAllocator.instance.allocate( - max(n, 4072 * 1024)))), - GCAllocator - ) - ); - - auto alloc2 = allocatorObject(A.instance); - b = alloc2.allocate(101); - assert(alloc2.deallocate(b)); -} diff --git a/phobos/std/experimental/allocator/showcase.d b/phobos/std/experimental/allocator/showcase.d deleted file mode 100644 index 3f08c56..0000000 --- a/phobos/std/experimental/allocator/showcase.d +++ /dev/null @@ -1,93 +0,0 @@ -// Written in the D programming language. -/** -Collection of typical and useful prebuilt allocators using the given -components. User code would typically import this module and use its -facilities, or import individual heap building blocks and assemble them. - -Source: $(PHOBOSSRC std/experimental/allocator/_showcase.d) -*/ -module std.experimental.allocator.showcase; - -import std.experimental.allocator.building_blocks.fallback_allocator, - std.experimental.allocator.gc_allocator, - std.experimental.allocator.building_blocks.region; -import std.traits : hasMember; - -/** - -Allocator that uses stack allocation for up to `stackSize` bytes and -then falls back to `Allocator`. Defined as: - ----- -alias StackFront(size_t stackSize, Allocator) = - FallbackAllocator!( - InSituRegion!(stackSize, Allocator.alignment, - hasMember!(Allocator, "deallocate") - ? Yes.defineDeallocate - : No.defineDeallocate), - Allocator); ----- - -Choosing `stackSize` is as always a compromise. Too small a size exhausts the -stack storage after a few allocations, after which there are no gains over the -backup allocator. Too large a size increases the stack consumed by the thread -and may end up worse off because it explores cold portions of the stack. - -*/ -alias StackFront(size_t stackSize, Allocator = GCAllocator) = - FallbackAllocator!( - InSituRegion!(stackSize, Allocator.alignment), - Allocator); - -/// -@system unittest -{ - StackFront!4096 a; - auto b = a.allocate(4000); - assert(b.length == 4000); - auto c = a.allocate(4000); - assert(c.length == 4000); - a.deallocate(b); - a.deallocate(c); -} - -/** -Creates a scalable `AllocatorList` of `Regions`, each having at least -`bytesPerRegion` bytes. Allocation is very fast. This allocator does not offer -`deallocate` but does free all regions in its destructor. It is recommended for -short-lived batch applications that count on never running out of memory. -*/ -auto mmapRegionList(size_t bytesPerRegion) -{ - static struct Factory - { - size_t bytesPerRegion; - import std.algorithm.comparison : max; - import std.experimental.allocator.building_blocks.region - : Region; - import std.experimental.allocator.mmap_allocator - : MmapAllocator; - this(size_t n) - { - bytesPerRegion = n; - } - auto opCall(size_t n) - { - return Region!MmapAllocator(max(n, bytesPerRegion)); - } - } - import std.experimental.allocator.building_blocks.allocator_list - : AllocatorList; - import std.experimental.allocator.building_blocks.null_allocator - : NullAllocator; - auto shop = Factory(bytesPerRegion); - return AllocatorList!(Factory, NullAllocator)(shop); -} - -/// -@system unittest -{ - auto alloc = mmapRegionList(1024 * 1024); - const b = alloc.allocate(100); - assert(b.length == 100); -} diff --git a/phobos/std/experimental/allocator/typed.d b/phobos/std/experimental/allocator/typed.d deleted file mode 100644 index 85cc649..0000000 --- a/phobos/std/experimental/allocator/typed.d +++ /dev/null @@ -1,427 +0,0 @@ -// Written in the D programming language. -/** -This module defines `TypedAllocator`, a statically-typed allocator that -aggregates multiple untyped allocators and uses them depending on the static -properties of the types allocated. For example, distinct allocators may be used -for thread-local vs. thread-shared data, or for fixed-size data (`struct`, -`class` objects) vs. resizable data (arrays). - -Source: $(PHOBOSSRC std/experimental/allocator/typed.d) - -Macros: -T2=$(TR `$1` $(TD $(ARGS $+))) -*/ - -module std.experimental.allocator.typed; - -import std.experimental.allocator; -import std.experimental.allocator.common; -import std.range : isInputRange, isForwardRange, walkLength, save, empty, - front, popFront; -import std.traits : isPointer, hasElaborateDestructor; -import std.typecons : Flag, Yes, No; - -/** -Allocation-related flags dictated by type characteristics. `TypedAllocator` -deduces these flags from the type being allocated and uses the appropriate -allocator accordingly. -*/ -enum AllocFlag : uint -{ - _init = 0, - /** - Fixed-size allocation (unlikely to get reallocated later). Examples: `int`, - `double`, any `struct` or `class` type. By default it is assumed that the - allocation is variable-size, i.e. susceptible to later reallocation - (for example all array types). This flag is advisory, i.e. in-place resizing - may be attempted for `fixedSize` allocations and may succeed. The flag is - just a hint to the compiler it may use allocation strategies that work well - with objects of fixed size. - */ - fixedSize = 1, - /** - The type being allocated embeds no pointers. Examples: `int`, `int[]`, $(D - Tuple!(int, float)). The implicit conservative assumption is that the type - has members with indirections so it needs to be scanned if garbage - collected. Example of types with pointers: `int*[]`, $(D Tuple!(int, - string)). - */ - hasNoIndirections = 4, - /** - By default it is conservatively assumed that allocated memory may be `cast` - to `shared`, passed across threads, and deallocated in a different thread - than the one that allocated it. If that's not the case, there are two - options. First, `immutableShared` means the memory is allocated for - `immutable` data and will be deallocated in the same thread it was - allocated in. Second, `threadLocal` means the memory is not to be shared - across threads at all. The two flags cannot be simultaneously present. - */ - immutableShared = 8, - /// ditto - threadLocal = 16, -} - -/** -`TypedAllocator` acts like a chassis on which several specialized allocators -can be assembled. To let the system make a choice about a particular kind of -allocation, use `Default` for the respective parameters. - -There is a hierarchy of allocation kinds. When an allocator is implemented for -a given combination of flags, it is used. Otherwise, the next down the list is -chosen. - -$(BOOKTABLE , - -$(TR $(TH `AllocFlag` combination) $(TH Description)) - -$(T2 AllocFlag.threadLocal |$(NBSP)AllocFlag.hasNoIndirections -|$(NBSP)AllocFlag.fixedSize, -This is the most specific allocation policy: the memory being allocated is -thread local, has no indirections at all, and will not be reallocated. Examples -of types fitting this description: `int`, `double`, $(D Tuple!(int, long)), but -not $(D Tuple!(int, string)), which contains an indirection.) - -$(T2 AllocFlag.threadLocal |$(NBSP)AllocFlag.hasNoIndirections, -As above, but may be reallocated later. Examples of types fitting this -description are `int[]`, `double[]`, $(D Tuple!(int, long)[]), but not -$(D Tuple!(int, string)[]), which contains an indirection.) - -$(T2 AllocFlag.threadLocal, -As above, but may embed indirections. Examples of types fitting this -description are `int*[]`, `Object[]`, $(D Tuple!(int, string)[]).) - -$(T2 AllocFlag.immutableShared |$(NBSP)AllocFlag.hasNoIndirections -|$(NBSP)AllocFlag.fixedSize, -The type being allocated is `immutable` and has no pointers. The thread that -allocated it must also deallocate it. Example: `immutable(int)`.) - -$(T2 AllocFlag.immutableShared |$(NBSP)AllocFlag.hasNoIndirections, -As above, but the type may be appended to in the future. Example: `string`.) - -$(T2 AllocFlag.immutableShared, -As above, but the type may embed references. Example: `immutable(Object)[]`.) - -$(T2 AllocFlag.hasNoIndirections |$(NBSP)AllocFlag.fixedSize, -The type being allocated may be shared across threads, embeds no indirections, -and has fixed size.) - -$(T2 AllocFlag.hasNoIndirections, -The type being allocated may be shared across threads, may embed indirections, -and has variable size.) - -$(T2 AllocFlag.fixedSize, -The type being allocated may be shared across threads, may embed indirections, -and has fixed size.) - -$(T2 0, The most conservative/general allocation: memory may be shared, -deallocated in a different thread, may or may not be resized, and may embed -references.) -) - -Params: -PrimaryAllocator = The default allocator. -Policies = Zero or more pairs consisting of an `AllocFlag` and an allocator -type. -*/ -struct TypedAllocator(PrimaryAllocator, Policies...) -{ - import std.algorithm.sorting : isSorted; - import std.meta : AliasSeq; - import std.typecons : Tuple; - - static assert(Policies.length == 0 || isSorted([Stride2!Policies])); - - private template Stride2(T...) - { - static if (T.length >= 2) - { - alias Stride2 = AliasSeq!(T[0], Stride2!(T[2 .. $])); - } - else - { - alias Stride2 = AliasSeq!(T[0 .. $]); - } - } - - // state - static if (stateSize!PrimaryAllocator) private PrimaryAllocator primary; - else alias primary = PrimaryAllocator.instance; - static if (Policies.length > 0) - private Tuple!(Stride2!(Policies[1 .. $])) extras; - - private static bool match(uint have, uint want) - { - enum uint maskAway = - ~(AllocFlag.immutableShared | AllocFlag.threadLocal); - // Do we offer thread local? - if (have & AllocFlag.threadLocal) - { - if (want & AllocFlag.threadLocal) - return match(have & maskAway, want & maskAway); - return false; - } - if (have & AllocFlag.immutableShared) - { - // Okay to ask for either thread local or immutable shared - if (want & (AllocFlag.threadLocal - | AllocFlag.immutableShared)) - return match(have & maskAway, want & maskAway); - return false; - } - // From here on we have full-blown thread sharing. - if (have & AllocFlag.hasNoIndirections) - { - if (want & AllocFlag.hasNoIndirections) - return match(have & ~AllocFlag.hasNoIndirections, - want & ~AllocFlag.hasNoIndirections); - return false; - } - // Fixed size or variable size both match. - return true; - } - - /** - Given `flags` as a combination of `AllocFlag` values, or a type `T`, returns - the allocator that's a closest fit in capabilities. - */ - auto ref allocatorFor(uint flags)() - { - static if (Policies.length == 0 || !match(Policies[0], flags)) - { - return primary; - } - else static if (Policies.length && match(Policies[$ - 2], flags)) - { - return extras[$ - 1]; - } - else - { - foreach (i, choice; Stride2!Policies) - { - static if (!match(choice, flags)) - { - return extras[i - 1]; - } - } - assert(0); - } - } - - /// ditto - auto ref allocatorFor(T)() - { - static if (is(T == void[])) - { - return primary; - } - else - { - return allocatorFor!(type2flags!T)(); - } - } - - /** - Given a type `T`, returns its allocation-related flags as a combination of - `AllocFlag` values. - */ - static uint type2flags(T)() - { - uint result; - static if (is(T == immutable)) - result |= AllocFlag.immutableShared; - else static if (is(T == shared)) - result |= AllocFlag.forSharing; - static if (!is(T == U[], U)) - result |= AllocFlag.fixedSize; - import std.traits : hasIndirections; - static if (!hasIndirections!T) - result |= AllocFlag.hasNoIndirections; - return result; - } - - /** - Dynamically allocates (using the appropriate allocator chosen with - `allocatorFor!T`) and then creates in the memory allocated an object of - type `T`, using `args` (if any) for its initialization. Initialization - occurs in the memory allocated and is otherwise semantically the same as - `T(args)`. (Note that using `make!(T[])` creates a pointer to an - (empty) array of `T`s, not an array. To allocate and initialize an - array, use `makeArray!T` described below.) - - Params: - T = Type of the object being created. - args = Optional arguments used for initializing the created object. If not - present, the object is default constructed. - - Returns: If `T` is a class type, returns a reference to the created `T` - object. Otherwise, returns a `T*` pointing to the created object. In all - cases, returns `null` if allocation failed. - - Throws: If `T`'s constructor throws, deallocates the allocated memory and - propagates the exception. - */ - auto make(T, A...)(auto ref A args) - { - return .make!T(allocatorFor!T, args); - } - - /** - Create an array of `T` with `length` elements. The array is either - default-initialized, filled with copies of `init`, or initialized with - values fetched from `range`. - - Params: - T = element type of the array being created - length = length of the newly created array - init = element used for filling the array - range = range used for initializing the array elements - - Returns: - The newly-created array, or `null` if either `length` was `0` or - allocation failed. - - Throws: - The first two overloads throw only if the used allocator's primitives do. - The overloads that involve copy initialization deallocate memory and propagate the exception if the copy operation throws. - */ - T[] makeArray(T)(size_t length) - { - return .makeArray!T(allocatorFor!(T[]), length); - } - - /// Ditto - T[] makeArray(T)(size_t length, auto ref T init) - { - return .makeArray!T(allocatorFor!(T[]), init, length); - } - - /// Ditto - T[] makeArray(T, R)(R range) - if (isInputRange!R) - { - return .makeArray!T(allocatorFor!(T[]), range); - } - - /** - Grows `array` by appending `delta` more elements. The needed memory is - allocated using the same allocator that was used for the array type. The - extra elements added are either default-initialized, filled with copies of - `init`, or initialized with values fetched from `range`. - - Params: - T = element type of the array being created - array = a reference to the array being grown - delta = number of elements to add (upon success the new length of `array` - is $(D array.length + delta)) - init = element used for filling the array - range = range used for initializing the array elements - - Returns: - `true` upon success, `false` if memory could not be allocated. In the - latter case `array` is left unaffected. - - Throws: - The first two overloads throw only if the used allocator's primitives do. - The overloads that involve copy initialization deallocate memory and - propagate the exception if the copy operation throws. - */ - bool expandArray(T)(ref T[] array, size_t delta) - { - return .expandArray(allocatorFor!(T[]), array, delta); - } - /// Ditto - bool expandArray(T)(T[] array, size_t delta, auto ref T init) - { - return .expandArray(allocatorFor!(T[]), array, delta, init); - } - /// Ditto - bool expandArray(T, R)(ref T[] array, R range) - if (isInputRange!R) - { - return .expandArray(allocatorFor!(T[]), array, range); - } - - /** - Shrinks an array by `delta` elements using `allocatorFor!(T[])`. - - If $(D arr.length < delta), does nothing and returns `false`. Otherwise, - destroys the last $(D arr.length - delta) elements in the array and then - reallocates the array's buffer. If reallocation fails, fills the array with - default-initialized data. - - Params: - T = element type of the array being created - arr = a reference to the array being shrunk - delta = number of elements to remove (upon success the new length of - `arr` is $(D arr.length - delta)) - - Returns: - `true` upon success, `false` if memory could not be reallocated. In the - latter case $(D arr[$ - delta .. $]) is left with default-initialized - elements. - - Throws: - The first two overloads throw only if the used allocator's primitives do. - The overloads that involve copy initialization deallocate memory and - propagate the exception if the copy operation throws. - */ - bool shrinkArray(T)(ref T[] arr, size_t delta) - { - return .shrinkArray(allocatorFor!(T[]), arr, delta); - } - - /** - Destroys and then deallocates (using `allocatorFor!T`) the object pointed - to by a pointer, the class object referred to by a `class` or `interface` - reference, or an entire array. It is assumed the respective entities had - been allocated with the same allocator. - */ - void dispose(T)(T* p) - { - return .dispose(allocatorFor!T, p); - } - /// Ditto - void dispose(T)(T p) - if (is(T == class) || is(T == interface)) - { - return .dispose(allocatorFor!T, p); - } - /// Ditto - void dispose(T)(T[] array) - { - return .dispose(allocatorFor!(T[]), array); - } -} - -/// -@system unittest -{ - import std.experimental.allocator.gc_allocator : GCAllocator; - import std.experimental.allocator.mallocator : Mallocator; - import std.experimental.allocator.mmap_allocator : MmapAllocator; - alias MyAllocator = TypedAllocator!(GCAllocator, - AllocFlag.fixedSize | AllocFlag.threadLocal, Mallocator, - AllocFlag.fixedSize | AllocFlag.threadLocal - | AllocFlag.hasNoIndirections, - MmapAllocator, - ); - - MyAllocator a; - auto b = &a.allocatorFor!0(); - static assert(is(typeof(*b) == shared const(GCAllocator))); - enum f1 = AllocFlag.fixedSize | AllocFlag.threadLocal; - auto c = &a.allocatorFor!f1(); - static assert(is(typeof(*c) == Mallocator)); - enum f2 = AllocFlag.fixedSize | AllocFlag.threadLocal; - static assert(is(typeof(a.allocatorFor!f2()) == Mallocator)); - // Partial match - enum f3 = AllocFlag.threadLocal; - static assert(is(typeof(a.allocatorFor!f3()) == Mallocator)); - - int* p = a.make!int; - scope(exit) a.dispose(p); - int[] arr = a.makeArray!int(42); - scope(exit) a.dispose(arr); - assert(a.expandArray(arr, 3)); - assert(a.shrinkArray(arr, 4)); -} diff --git a/phobos/std/experimental/checkedint.d b/phobos/std/experimental/checkedint.d deleted file mode 100644 index 2be5a2e..0000000 --- a/phobos/std/experimental/checkedint.d +++ /dev/null @@ -1,14 +0,0 @@ - -/** - * This module is now deprecated, use $(MREF std, checkedint) - * instead. - * - * Copyright: Copyright The D Language Foundation 2005 - 2015. - * License: $(HTTP www.boost.org/LICENSE_1_0.txt, Boost License 1.0). - * Authors: - * Source: $(PHOBOSSRC std/experimental/checkedint.d) - * - * $(SCRIPT inhibitQuickIndex = 1;) - */ -deprecated module std.experimental.checkedint; -public import std.checkedint; diff --git a/phobos/std/experimental/logger/core.d b/phobos/std/experimental/logger/core.d deleted file mode 100644 index a30ae58..0000000 --- a/phobos/std/experimental/logger/core.d +++ /dev/null @@ -1,13 +0,0 @@ -/** - * This module is now deprecated, use $(MREF std, logger, core) - * instead. - * - * Copyright: Copyright The D Language Foundation 2005 - 2015. - * License: $(HTTP www.boost.org/LICENSE_1_0.txt, Boost License 1.0). - * Authors: - * Source: $(PHOBOSSRC std/experimental/logger/core.d) - * - * $(SCRIPT inhibitQuickIndex = 1;) - */ -module std.experimental.logger.core; -public import std.logger.core; diff --git a/phobos/std/experimental/logger/filelogger.d b/phobos/std/experimental/logger/filelogger.d deleted file mode 100644 index 3205a25..0000000 --- a/phobos/std/experimental/logger/filelogger.d +++ /dev/null @@ -1,13 +0,0 @@ -/** - * This module is now deprecated, use $(MREF std, logger, filelogger) - * instead. - * - * Copyright: Copyright The D Language Foundation 2005 - 2015. - * License: $(HTTP www.boost.org/LICENSE_1_0.txt, Boost License 1.0). - * Authors: - * Source: $(PHOBOSSRC std/experimental/logger/filelogger.d) - * - * $(SCRIPT inhibitQuickIndex = 1;) - */ -module std.experimental.logger.filelogger; -public import std.logger.filelogger; diff --git a/phobos/std/experimental/logger/multilogger.d b/phobos/std/experimental/logger/multilogger.d deleted file mode 100644 index ae00b25..0000000 --- a/phobos/std/experimental/logger/multilogger.d +++ /dev/null @@ -1,13 +0,0 @@ -/** - * This module is now deprecated, use $(MREF std, logger, multilogger) - * instead. - * - * Copyright: Copyright The D Language Foundation 2005 - 2015. - * License: $(HTTP www.boost.org/LICENSE_1_0.txt, Boost License 1.0). - * Authors: - * Source: $(PHOBOSSRC std/experimental/logger/multilogger.d) - * - * $(SCRIPT inhibitQuickIndex = 1;) - */ -module std.experimental.logger.multilogger; -public import std.logger.multilogger; diff --git a/phobos/std/experimental/logger/nulllogger.d b/phobos/std/experimental/logger/nulllogger.d deleted file mode 100644 index 2c1f0ba..0000000 --- a/phobos/std/experimental/logger/nulllogger.d +++ /dev/null @@ -1,13 +0,0 @@ -/** - * This module is now deprecated, use $(MREF std, logger, nulllogger) - * instead. - * - * Copyright: Copyright The D Language Foundation 2005 - 2015. - * License: $(HTTP www.boost.org/LICENSE_1_0.txt, Boost License 1.0). - * Authors: - * Source: $(PHOBOSSRC std/experimental/logger/nulllogger.d) - * - * $(SCRIPT inhibitQuickIndex = 1;) - */ -module std.experimental.logger.nulllogger; -public import std.logger.nulllogger; diff --git a/phobos/std/experimental/logger/package.d b/phobos/std/experimental/logger/package.d deleted file mode 100644 index 4d19ea9..0000000 --- a/phobos/std/experimental/logger/package.d +++ /dev/null @@ -1,17 +0,0 @@ -/** - * This module is now deprecated, use $(MREF std, logger) - * instead. - * - * Copyright: Copyright The D Language Foundation 2005 - 2015. - * License: $(HTTP www.boost.org/LICENSE_1_0.txt, Boost License 1.0). - * Authors: - * Source: $(PHOBOSSRC std/experimental/logger/package.d) - * - * $(SCRIPT inhibitQuickIndex = 1;) - */ -module std.experimental.logger; - -public import std.logger.core; -public import std.logger.filelogger; -public import std.logger.multilogger; -public import std.logger.nulllogger; diff --git a/phobos/std/experimental/note.md b/phobos/std/experimental/note.md deleted file mode 100644 index 3773270..0000000 --- a/phobos/std/experimental/note.md +++ /dev/null @@ -1 +0,0 @@ -This is intended for experimental modules. diff --git a/phobos/std/file.d b/phobos/std/file.d deleted file mode 100644 index 1db779b..0000000 --- a/phobos/std/file.d +++ /dev/null @@ -1,5514 +0,0 @@ -// Written in the D programming language. - -/** -Utilities for manipulating files and scanning directories. Functions -in this module handle files as a unit, e.g., read or write one file -at a time. For opening files and manipulating them via handles refer -to module $(MREF std, stdio). - -$(SCRIPT inhibitQuickIndex = 1;) -$(DIVC quickindex, -$(BOOKTABLE, -$(TR $(TH Category) $(TH Functions)) -$(TR $(TD General) $(TD - $(LREF exists) - $(LREF isDir) - $(LREF isFile) - $(LREF isSymlink) - $(LREF rename) - $(LREF thisExePath) -)) -$(TR $(TD Directories) $(TD - $(LREF chdir) - $(LREF dirEntries) - $(LREF getcwd) - $(LREF mkdir) - $(LREF mkdirRecurse) - $(LREF rmdir) - $(LREF rmdirRecurse) - $(LREF tempDir) -)) -$(TR $(TD Files) $(TD - $(LREF append) - $(LREF copy) - $(LREF read) - $(LREF readText) - $(LREF remove) - $(LREF slurp) - $(LREF write) -)) -$(TR $(TD Symlinks) $(TD - $(LREF symlink) - $(LREF readLink) -)) -$(TR $(TD Attributes) $(TD - $(LREF attrIsDir) - $(LREF attrIsFile) - $(LREF attrIsSymlink) - $(LREF getAttributes) - $(LREF getLinkAttributes) - $(LREF getSize) - $(LREF setAttributes) -)) -$(TR $(TD Timestamp) $(TD - $(LREF getTimes) - $(LREF getTimesWin) - $(LREF setTimes) - $(LREF timeLastModified) - $(LREF timeLastAccessed) - $(LREF timeStatusChanged) -)) -$(TR $(TD Other) $(TD - $(LREF DirEntry) - $(LREF FileException) - $(LREF PreserveAttributes) - $(LREF SpanMode) - $(LREF getAvailableDiskSpace) -)) -)) - - -Copyright: Copyright The D Language Foundation 2007 - 2011. -See_Also: The $(HTTP ddili.org/ders/d.en/files.html, official tutorial) for an -introduction to working with files in D, module -$(MREF std, stdio) for opening files and manipulating them via handles, -and module $(MREF std, path) for manipulating path strings. - -License: $(HTTP boost.org/LICENSE_1_0.txt, Boost License 1.0). -Authors: $(HTTP digitalmars.com, Walter Bright), - $(HTTP erdani.org, Andrei Alexandrescu), - $(HTTP jmdavisprog.com, Jonathan M Davis) -Source: $(PHOBOSSRC std/file.d) - */ -module std.file; - -import core.stdc.errno, core.stdc.stdlib, core.stdc.string; -import core.time : abs, dur, hnsecs, seconds; - -import std.datetime.date : DateTime; -import std.datetime.systime : Clock, SysTime, unixTimeToStdTime; -import std.internal.cstring; -import std.meta; -import std.range; -import std.traits; -import std.typecons; - -version (OSX) - version = Darwin; -else version (iOS) - version = Darwin; -else version (TVOS) - version = Darwin; -else version (WatchOS) - version = Darwin; - -version (Windows) -{ - import core.sys.windows.winbase, core.sys.windows.winnt, std.windows.syserror; -} -else version (Posix) -{ - import core.sys.posix.dirent, core.sys.posix.fcntl, core.sys.posix.sys.stat, - core.sys.posix.sys.time, core.sys.posix.unistd, core.sys.posix.utime; -} -else - static assert(false, "Module " ~ .stringof ~ " not implemented for this OS."); - -// Character type used for operating system filesystem APIs -version (Windows) -{ - private alias FSChar = WCHAR; // WCHAR can be aliased to wchar or wchar_t -} -else version (Posix) -{ - private alias FSChar = char; -} -else - static assert(0); - -// Purposefully not documented. Use at your own risk -@property string deleteme() @safe -{ - import std.conv : text; - import std.path : buildPath; - import std.process : thisProcessID; - - enum base = "deleteme.dmd.unittest.pid"; - static string fileName; - - if (!fileName) - fileName = text(buildPath(tempDir(), base), thisProcessID); - return fileName; -} - -version (StdUnittest) private struct TestAliasedString -{ - string get() @safe @nogc pure nothrow return scope { return _s; } - alias get this; - @disable this(this); - string _s; -} - -version (Android) -{ - package enum system_directory = "/system/etc"; - package enum system_file = "/system/etc/hosts"; -} -else version (Posix) -{ - package enum system_directory = "/usr/include"; - package enum system_file = "/usr/include/assert.h"; -} - - -/++ - Exception thrown for file I/O errors. - +/ -class FileException : Exception -{ - import std.conv : text, to; - - /++ - OS error code. - +/ - immutable uint errno; - - private this(scope const(char)[] name, scope const(char)[] msg, string file, size_t line, uint errno) @safe pure - { - if (msg.empty) - super(name is null ? "(null)" : name.idup, file, line); - else - super(text(name is null ? "(null)" : name, ": ", msg), file, line); - - this.errno = errno; - } - - /++ - Constructor which takes an error message. - - Params: - name = Name of file for which the error occurred. - msg = Message describing the error. - file = The file where the error occurred. - line = The _line where the error occurred. - +/ - this(scope const(char)[] name, scope const(char)[] msg, string file = __FILE__, size_t line = __LINE__) @safe pure - { - this(name, msg, file, line, 0); - } - - /++ - Constructor which takes the error number ($(LUCKY GetLastError) - in Windows, $(D_PARAM errno) in POSIX). - - Params: - name = Name of file for which the error occurred. - errno = The error number. - file = The file where the error occurred. - Defaults to `__FILE__`. - line = The _line where the error occurred. - Defaults to `__LINE__`. - +/ - version (Windows) this(scope const(char)[] name, - uint errno = .GetLastError(), - string file = __FILE__, - size_t line = __LINE__) @safe - { - this(name, generateSysErrorMsg(errno), file, line, errno); - } - else version (Posix) this(scope const(char)[] name, - uint errno = .errno, - string file = __FILE__, - size_t line = __LINE__) @trusted - { - import std.exception : errnoString; - this(name, errnoString(errno), file, line, errno); - } -} - -/// -@safe unittest -{ - import std.exception : assertThrown; - - assertThrown!FileException("non.existing.file.".readText); -} - -private T cenforce(T)(T condition, lazy scope const(char)[] name, string file = __FILE__, size_t line = __LINE__) -{ - if (condition) - return condition; - version (Windows) - { - throw new FileException(name, .GetLastError(), file, line); - } - else version (Posix) - { - throw new FileException(name, .errno, file, line); - } -} - -version (Windows) -@trusted -private T cenforce(T)(T condition, scope const(char)[] name, scope const(FSChar)* namez, - string file = __FILE__, size_t line = __LINE__) -{ - if (condition) - return condition; - if (!name) - { - import core.stdc.wchar_ : wcslen; - import std.conv : to; - - auto len = namez ? wcslen(namez) : 0; - name = to!string(namez[0 .. len]); - } - throw new FileException(name, .GetLastError(), file, line); -} - -version (Posix) -@trusted -private T cenforce(T)(T condition, scope const(char)[] name, scope const(FSChar)* namez, - string file = __FILE__, size_t line = __LINE__) -{ - if (condition) - return condition; - if (!name) - { - import core.stdc.string : strlen; - - auto len = namez ? strlen(namez) : 0; - name = namez[0 .. len].idup; - } - throw new FileException(name, .errno, file, line); -} - -// https://issues.dlang.org/show_bug.cgi?id=17102 -@safe unittest -{ - try - { - cenforce(false, null, null, - __FILE__, __LINE__); - } - catch (FileException) {} -} - -/* ********************************** - * Basic File operations. - */ - -/******************************************** -Read entire contents of file `name` and returns it as an untyped -array. If the file size is larger than `upTo`, only `upTo` -bytes are _read. - -Params: - name = string or range of characters representing the file _name - upTo = if present, the maximum number of bytes to _read - -Returns: Untyped array of bytes _read. - -Throws: $(LREF FileException) on error. - -See_Also: $(REF readText, std,file) for reading and validating a text file. - */ - -void[] read(R)(R name, size_t upTo = size_t.max) -if (isSomeFiniteCharInputRange!R && !isConvertibleToString!R) -{ - static if (isNarrowString!R && is(immutable ElementEncodingType!R == immutable char)) - return readImpl(name, name.tempCString!FSChar(), upTo); - else - return readImpl(null, name.tempCString!FSChar(), upTo); -} - -/// -@safe unittest -{ - import std.utf : byChar; - scope(exit) - { - assert(exists(deleteme)); - remove(deleteme); - } - - std.file.write(deleteme, "1234"); // deleteme is the name of a temporary file - assert(read(deleteme, 2) == "12"); - assert(read(deleteme.byChar) == "1234"); - assert((cast(const(ubyte)[])read(deleteme)).length == 4); -} - -/// ditto -void[] read(R)(auto ref R name, size_t upTo = size_t.max) -if (isConvertibleToString!R) -{ - return read!(StringTypeOf!R)(name, upTo); -} - -@safe unittest -{ - static assert(__traits(compiles, read(TestAliasedString(null)))); -} - -version (Posix) private void[] readImpl(scope const(char)[] name, scope const(FSChar)* namez, - size_t upTo = size_t.max) @trusted -{ - import core.memory : GC; - import std.algorithm.comparison : min; - import std.conv : to; - import std.checkedint : checked; - - // A few internal configuration parameters { - enum size_t - minInitialAlloc = 1024 * 4, - maxInitialAlloc = size_t.max / 2, - sizeIncrement = 1024 * 16, - maxSlackMemoryAllowed = 1024; - // } - - immutable fd = core.sys.posix.fcntl.open(namez, - core.sys.posix.fcntl.O_RDONLY); - cenforce(fd != -1, name); - scope(exit) core.sys.posix.unistd.close(fd); - - stat_t statbuf = void; - cenforce(fstat(fd, &statbuf) == 0, name, namez); - - immutable initialAlloc = min(upTo, to!size_t(statbuf.st_size - ? min(statbuf.st_size + 1, maxInitialAlloc) - : minInitialAlloc)); - void[] result = GC.malloc(initialAlloc, GC.BlkAttr.NO_SCAN)[0 .. initialAlloc]; - scope(failure) GC.free(result.ptr); - - auto size = checked(size_t(0)); - - for (;;) - { - immutable actual = core.sys.posix.unistd.read(fd, result.ptr + size.get, - (min(result.length, upTo) - size).get); - cenforce(actual != -1, name, namez); - if (actual == 0) break; - size += actual; - if (size >= upTo) break; - if (size < result.length) continue; - immutable newAlloc = size + sizeIncrement; - result = GC.realloc(result.ptr, newAlloc.get, GC.BlkAttr.NO_SCAN)[0 .. newAlloc.get]; - } - - return result.length - size >= maxSlackMemoryAllowed - ? GC.realloc(result.ptr, size.get, GC.BlkAttr.NO_SCAN)[0 .. size.get] - : result[0 .. size.get]; -} - -version (Windows) -private extern (Windows) @nogc nothrow -{ - pragma(mangle, CreateFileW.mangleof) - HANDLE trustedCreateFileW(scope const(wchar)* namez, DWORD dwDesiredAccess, - DWORD dwShareMode, SECURITY_ATTRIBUTES* lpSecurityAttributes, - DWORD dwCreationDisposition, DWORD dwFlagsAndAttributes, - HANDLE hTemplateFile) @trusted; - - pragma(mangle, CloseHandle.mangleof) BOOL trustedCloseHandle(HANDLE) @trusted; -} - -version (Windows) private void[] readImpl(scope const(char)[] name, scope const(FSChar)* namez, - size_t upTo = size_t.max) @trusted -{ - import core.memory : GC; - import std.algorithm.comparison : min; - static trustedGetFileSize(HANDLE hFile, out ulong fileSize) - { - DWORD sizeHigh; - DWORD sizeLow = GetFileSize(hFile, &sizeHigh); - const bool result = sizeLow != INVALID_FILE_SIZE; - if (result) - fileSize = makeUlong(sizeLow, sizeHigh); - return result; - } - static trustedReadFile(HANDLE hFile, void *lpBuffer, size_t nNumberOfBytesToRead) - { - // Read by chunks of size < 4GB (Windows API limit) - size_t totalNumRead = 0; - while (totalNumRead != nNumberOfBytesToRead) - { - const uint chunkSize = min(nNumberOfBytesToRead - totalNumRead, 0xffff_0000); - DWORD numRead = void; - const result = ReadFile(hFile, lpBuffer + totalNumRead, chunkSize, &numRead, null); - if (result == 0 || numRead != chunkSize) - return false; - totalNumRead += chunkSize; - } - return true; - } - - alias defaults = - AliasSeq!(GENERIC_READ, - FILE_SHARE_READ | FILE_SHARE_WRITE, (SECURITY_ATTRIBUTES*).init, - OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL | FILE_FLAG_SEQUENTIAL_SCAN, - HANDLE.init); - auto h = trustedCreateFileW(namez, defaults); - - cenforce(h != INVALID_HANDLE_VALUE, name, namez); - scope(exit) cenforce(trustedCloseHandle(h), name, namez); - ulong fileSize = void; - cenforce(trustedGetFileSize(h, fileSize), name, namez); - size_t size = min(upTo, fileSize); - auto buf = () { return GC.malloc(size, GC.BlkAttr.NO_SCAN)[0 .. size]; } (); - - scope(failure) - { - () { GC.free(buf.ptr); } (); - } - - if (size) - cenforce(trustedReadFile(h, &buf[0], size), name, namez); - return buf[0 .. size]; -} - -version (linux) @safe unittest -{ - // A file with "zero" length that doesn't have 0 length at all - auto s = std.file.readText("/proc/cpuinfo"); - assert(s.length > 0); - //writefln("'%s'", s); -} - -@safe unittest -{ - scope(exit) if (exists(deleteme)) remove(deleteme); - import std.stdio; - auto f = File(deleteme, "w"); - f.write("abcd"); f.flush(); - assert(read(deleteme) == "abcd"); -} - -/++ - Reads and validates (using $(REF validate, std, utf)) a text file. S can be - an array of any character type. However, no width or endian conversions are - performed. So, if the width or endianness of the characters in the given - file differ from the width or endianness of the element type of S, then - validation will fail. - - Params: - S = the string type of the file - name = string or range of characters representing the file _name - - Returns: Array of characters read. - - Throws: $(LREF FileException) if there is an error reading the file, - $(REF UTFException, std, utf) on UTF decoding error. - - See_Also: $(REF read, std,file) for reading a binary file. -+/ -S readText(S = string, R)(auto ref R name) -if (isSomeString!S && (isSomeFiniteCharInputRange!R || is(StringTypeOf!R))) -{ - import std.algorithm.searching : startsWith; - import std.encoding : getBOM, BOM; - import std.exception : enforce; - import std.format : format; - import std.utf : UTFException, validate; - - static if (is(StringTypeOf!R)) - StringTypeOf!R filename = name; - else - auto filename = name; - - static auto trustedCast(T)(void[] buf) @trusted { return cast(T) buf; } - auto data = trustedCast!(ubyte[])(read(filename)); - - immutable bomSeq = getBOM(data); - immutable bom = bomSeq.schema; - - static if (is(immutable ElementEncodingType!S == immutable char)) - { - with(BOM) switch (bom) - { - case utf16be: - case utf16le: throw new UTFException("UTF-8 requested. BOM is for UTF-16"); - case utf32be: - case utf32le: throw new UTFException("UTF-8 requested. BOM is for UTF-32"); - default: break; - } - } - else static if (is(immutable ElementEncodingType!S == immutable wchar)) - { - with(BOM) switch (bom) - { - case utf8: throw new UTFException("UTF-16 requested. BOM is for UTF-8"); - case utf16be: - { - version (BigEndian) - break; - else - throw new UTFException("BOM is for UTF-16 LE on Big Endian machine"); - } - case utf16le: - { - version (BigEndian) - throw new UTFException("BOM is for UTF-16 BE on Little Endian machine"); - else - break; - } - case utf32be: - case utf32le: throw new UTFException("UTF-8 requested. BOM is for UTF-32"); - default: break; - } - } - else - { - with(BOM) switch (bom) - { - case utf8: throw new UTFException("UTF-16 requested. BOM is for UTF-8"); - case utf16be: - case utf16le: throw new UTFException("UTF-8 requested. BOM is for UTF-16"); - case utf32be: - { - version (BigEndian) - break; - else - throw new UTFException("BOM is for UTF-32 LE on Big Endian machine"); - } - case utf32le: - { - version (BigEndian) - throw new UTFException("BOM is for UTF-32 BE on Little Endian machine"); - else - break; - } - default: break; - } - } - - if (data.length % ElementEncodingType!S.sizeof != 0) - throw new UTFException(format!"The content of %s is not UTF-%s"(filename, ElementEncodingType!S.sizeof * 8)); - - auto result = trustedCast!S(data); - validate(result); - return result; -} - -/// Read file with UTF-8 text. -@safe unittest -{ - write(deleteme, "abc"); // deleteme is the name of a temporary file - scope(exit) remove(deleteme); - string content = readText(deleteme); - assert(content == "abc"); -} - -// Read file with UTF-8 text but try to read it as UTF-16. -@safe unittest -{ - import std.exception : assertThrown; - import std.utf : UTFException; - - write(deleteme, "abc"); - scope(exit) remove(deleteme); - // Throws because the file is not valid UTF-16. - assertThrown!UTFException(readText!wstring(deleteme)); -} - -// Read file with UTF-16 text. -@safe unittest -{ - import std.algorithm.searching : skipOver; - - write(deleteme, "\uFEFFabc"w); // With BOM - scope(exit) remove(deleteme); - auto content = readText!wstring(deleteme); - assert(content == "\uFEFFabc"w); - // Strips BOM if present. - content.skipOver('\uFEFF'); - assert(content == "abc"w); -} - -@safe unittest -{ - static assert(__traits(compiles, readText(TestAliasedString(null)))); -} - -@safe unittest -{ - import std.array : appender; - import std.bitmanip : append, Endian; - import std.exception : assertThrown; - import std.path : buildPath; - import std.string : representation; - import std.utf : UTFException; - - mkdir(deleteme); - scope(exit) rmdirRecurse(deleteme); - - immutable none8 = buildPath(deleteme, "none8"); - immutable none16 = buildPath(deleteme, "none16"); - immutable utf8 = buildPath(deleteme, "utf8"); - immutable utf16be = buildPath(deleteme, "utf16be"); - immutable utf16le = buildPath(deleteme, "utf16le"); - immutable utf32be = buildPath(deleteme, "utf32be"); - immutable utf32le = buildPath(deleteme, "utf32le"); - immutable utf7 = buildPath(deleteme, "utf7"); - - write(none8, "京都市"); - write(none16, "京都市"w); - write(utf8, (cast(char[])[0xEF, 0xBB, 0xBF]) ~ "京都市"); - { - auto str = "\uFEFF京都市"w; - auto arr = appender!(ubyte[])(); - foreach (c; str) - arr.append(c); - write(utf16be, arr.data); - } - { - auto str = "\uFEFF京都市"w; - auto arr = appender!(ubyte[])(); - foreach (c; str) - arr.append!(ushort, Endian.littleEndian)(c); - write(utf16le, arr.data); - } - { - auto str = "\U0000FEFF京都市"d; - auto arr = appender!(ubyte[])(); - foreach (c; str) - arr.append(c); - write(utf32be, arr.data); - } - { - auto str = "\U0000FEFF京都市"d; - auto arr = appender!(ubyte[])(); - foreach (c; str) - arr.append!(uint, Endian.littleEndian)(c); - write(utf32le, arr.data); - } - write(utf7, (cast(ubyte[])[0x2B, 0x2F, 0x76, 0x38, 0x2D]) ~ "foobar".representation); - - assertThrown!UTFException(readText(none16)); - assert(readText(utf8) == (cast(char[])[0xEF, 0xBB, 0xBF]) ~ "京都市"); - assertThrown!UTFException(readText(utf16be)); - assertThrown!UTFException(readText(utf16le)); - assertThrown!UTFException(readText(utf32be)); - assertThrown!UTFException(readText(utf32le)); - assert(readText(utf7) == (cast(char[])[0x2B, 0x2F, 0x76, 0x38, 0x2D]) ~ "foobar"); - - assertThrown!UTFException(readText!wstring(none8)); - assert(readText!wstring(none16) == "京都市"w); - assertThrown!UTFException(readText!wstring(utf8)); - version (BigEndian) - { - assert(readText!wstring(utf16be) == "\uFEFF京都市"w); - assertThrown!UTFException(readText!wstring(utf16le)); - } - else - { - assertThrown!UTFException(readText!wstring(utf16be)); - assert(readText!wstring(utf16le) == "\uFEFF京都市"w); - } - assertThrown!UTFException(readText!wstring(utf32be)); - assertThrown!UTFException(readText!wstring(utf32le)); - assertThrown!UTFException(readText!wstring(utf7)); - - assertThrown!UTFException(readText!dstring(utf8)); - assertThrown!UTFException(readText!dstring(utf16be)); - assertThrown!UTFException(readText!dstring(utf16le)); - version (BigEndian) - { - assert(readText!dstring(utf32be) == "\U0000FEFF京都市"d); - assertThrown!UTFException(readText!dstring(utf32le)); - } - else - { - assertThrown!UTFException(readText!dstring(utf32be)); - assert(readText!dstring(utf32le) == "\U0000FEFF京都市"d); - } - assertThrown!UTFException(readText!dstring(utf7)); -} - -/********************************************* -Write `buffer` to file `name`. - -Creates the file if it does not already exist. - -Params: - name = string or range of characters representing the file _name - buffer = data to be written to file - -Throws: $(LREF FileException) on error. - -See_also: $(REF toFile, std,stdio) - */ -void write(R)(R name, const void[] buffer) -if ((isSomeFiniteCharInputRange!R || isSomeString!R) && !isConvertibleToString!R) -{ - static if (isNarrowString!R && is(immutable ElementEncodingType!R == immutable char)) - writeImpl(name, name.tempCString!FSChar(), buffer, false); - else - writeImpl(null, name.tempCString!FSChar(), buffer, false); -} - -/// -@safe unittest -{ - scope(exit) - { - assert(exists(deleteme)); - remove(deleteme); - } - - int[] a = [ 0, 1, 1, 2, 3, 5, 8 ]; - write(deleteme, a); // deleteme is the name of a temporary file - const bytes = read(deleteme); - const fileInts = () @trusted { return cast(int[]) bytes; }(); - assert(fileInts == a); -} - -/// ditto -void write(R)(auto ref R name, const void[] buffer) -if (isConvertibleToString!R) -{ - write!(StringTypeOf!R)(name, buffer); -} - -@safe unittest -{ - static assert(__traits(compiles, write(TestAliasedString(null), null))); -} - -/********************************************* -Appends `buffer` to file `name`. - -Creates the file if it does not already exist. - -Params: - name = string or range of characters representing the file _name - buffer = data to be appended to file - -Throws: $(LREF FileException) on error. - */ -void append(R)(R name, const void[] buffer) -if ((isSomeFiniteCharInputRange!R || isSomeString!R) && !isConvertibleToString!R) -{ - static if (isNarrowString!R && is(immutable ElementEncodingType!R == immutable char)) - writeImpl(name, name.tempCString!FSChar(), buffer, true); - else - writeImpl(null, name.tempCString!FSChar(), buffer, true); -} - -/// -@safe unittest -{ - scope(exit) - { - assert(exists(deleteme)); - remove(deleteme); - } - - int[] a = [ 0, 1, 1, 2, 3, 5, 8 ]; - write(deleteme, a); // deleteme is the name of a temporary file - int[] b = [ 13, 21 ]; - append(deleteme, b); - const bytes = read(deleteme); - const fileInts = () @trusted { return cast(int[]) bytes; }(); - assert(fileInts == a ~ b); -} - -/// ditto -void append(R)(auto ref R name, const void[] buffer) -if (isConvertibleToString!R) -{ - append!(StringTypeOf!R)(name, buffer); -} - -@safe unittest -{ - static assert(__traits(compiles, append(TestAliasedString("foo"), [0, 1, 2, 3]))); -} - -// POSIX implementation helper for write and append - -version (Posix) private void writeImpl(scope const(char)[] name, scope const(FSChar)* namez, - scope const(void)[] buffer, bool append) @trusted -{ - import std.conv : octal; - - // append or write - auto mode = append ? O_CREAT | O_WRONLY | O_APPEND - : O_CREAT | O_WRONLY | O_TRUNC; - - immutable fd = core.sys.posix.fcntl.open(namez, mode, octal!666); - cenforce(fd != -1, name, namez); - { - scope(failure) core.sys.posix.unistd.close(fd); - - immutable size = buffer.length; - size_t sum, cnt = void; - while (sum != size) - { - cnt = (size - sum < 2^^30) ? (size - sum) : 2^^30; - const numwritten = core.sys.posix.unistd.write(fd, buffer.ptr + sum, cnt); - if (numwritten != cnt) - break; - sum += numwritten; - } - cenforce(sum == size, name, namez); - } - cenforce(core.sys.posix.unistd.close(fd) == 0, name, namez); -} - -// Windows implementation helper for write and append - -version (Windows) private void writeImpl(scope const(char)[] name, scope const(FSChar)* namez, - scope const(void)[] buffer, bool append) @trusted -{ - HANDLE h; - if (append) - { - alias defaults = - AliasSeq!(GENERIC_WRITE, 0, null, OPEN_ALWAYS, - FILE_ATTRIBUTE_NORMAL | FILE_FLAG_SEQUENTIAL_SCAN, - HANDLE.init); - - h = CreateFileW(namez, defaults); - cenforce(h != INVALID_HANDLE_VALUE, name, namez); - cenforce(SetFilePointer(h, 0, null, FILE_END) != INVALID_SET_FILE_POINTER, - name, namez); - } - else // write - { - alias defaults = - AliasSeq!(GENERIC_WRITE, 0, null, CREATE_ALWAYS, - FILE_ATTRIBUTE_NORMAL | FILE_FLAG_SEQUENTIAL_SCAN, - HANDLE.init); - - h = CreateFileW(namez, defaults); - cenforce(h != INVALID_HANDLE_VALUE, name, namez); - } - immutable size = buffer.length; - size_t sum, cnt = void; - DWORD numwritten = void; - while (sum != size) - { - cnt = (size - sum < 2^^30) ? (size - sum) : 2^^30; - WriteFile(h, buffer.ptr + sum, cast(uint) cnt, &numwritten, null); - if (numwritten != cnt) - break; - sum += numwritten; - } - cenforce(sum == size && CloseHandle(h), name, namez); -} - -/*************************************************** - * Rename file `from` _to `to`, moving it between directories if required. - * If the target file exists, it is overwritten. - * - * It is not possible to rename a file across different mount points - * or drives. On POSIX, the operation is atomic. That means, if `to` - * already exists there will be no time period during the operation - * where `to` is missing. See - * $(HTTP man7.org/linux/man-pages/man2/rename.2.html, manpage for rename) - * for more details. - * - * Params: - * from = string or range of characters representing the existing file name - * to = string or range of characters representing the target file name - * - * Throws: $(LREF FileException) on error. - */ -void rename(RF, RT)(RF from, RT to) -if ((isSomeFiniteCharInputRange!RF || isSomeString!RF) && !isConvertibleToString!RF && - (isSomeFiniteCharInputRange!RT || isSomeString!RT) && !isConvertibleToString!RT) -{ - // Place outside of @trusted block - auto fromz = from.tempCString!FSChar(); - auto toz = to.tempCString!FSChar(); - - static if (isNarrowString!RF && is(immutable ElementEncodingType!RF == immutable char)) - alias f = from; - else - enum string f = null; - - static if (isNarrowString!RT && is(immutable ElementEncodingType!RT == immutable char)) - alias t = to; - else - enum string t = null; - - renameImpl(f, t, fromz, toz); -} - -/// ditto -void rename(RF, RT)(auto ref RF from, auto ref RT to) -if (isConvertibleToString!RF || isConvertibleToString!RT) -{ - import std.meta : staticMap; - alias Types = staticMap!(convertToString, RF, RT); - rename!Types(from, to); -} - -@safe unittest -{ - static assert(__traits(compiles, rename(TestAliasedString(null), TestAliasedString(null)))); - static assert(__traits(compiles, rename("", TestAliasedString(null)))); - static assert(__traits(compiles, rename(TestAliasedString(null), ""))); - import std.utf : byChar; - static assert(__traits(compiles, rename(TestAliasedString(null), "".byChar))); -} - -/// -@safe unittest -{ - auto t1 = deleteme, t2 = deleteme~"2"; - scope(exit) foreach (t; [t1, t2]) if (t.exists) t.remove(); - - t1.write("1"); - t1.rename(t2); - assert(t2.readText == "1"); - - t1.write("2"); - t1.rename(t2); - assert(t2.readText == "2"); -} - -private void renameImpl(scope const(char)[] f, scope const(char)[] t, - scope const(FSChar)* fromz, scope const(FSChar)* toz) @trusted -{ - version (Windows) - { - import std.exception : enforce; - - const result = MoveFileExW(fromz, toz, MOVEFILE_REPLACE_EXISTING); - if (!result) - { - import core.stdc.wchar_ : wcslen; - import std.conv : to, text; - - if (!f) - f = to!(typeof(f))(fromz[0 .. wcslen(fromz)]); - - if (!t) - t = to!(typeof(t))(toz[0 .. wcslen(toz)]); - - enforce(false, - new FileException( - text("Attempting to rename file ", f, " to ", t))); - } - } - else version (Posix) - { - static import core.stdc.stdio; - - cenforce(core.stdc.stdio.rename(fromz, toz) == 0, t, toz); - } -} - -@safe unittest -{ - import std.utf : byWchar; - - auto t1 = deleteme, t2 = deleteme~"2"; - scope(exit) foreach (t; [t1, t2]) if (t.exists) t.remove(); - - write(t1, "1"); - rename(t1, t2); - assert(readText(t2) == "1"); - - write(t1, "2"); - rename(t1, t2.byWchar); - assert(readText(t2) == "2"); -} - -/*************************************************** -Delete file `name`. - -Params: - name = string or range of characters representing the file _name - -Throws: $(LREF FileException) on error. - */ -void remove(R)(R name) -if (isSomeFiniteCharInputRange!R && !isConvertibleToString!R) -{ - static if (isNarrowString!R && is(immutable ElementEncodingType!R == immutable char)) - removeImpl(name, name.tempCString!FSChar()); - else - removeImpl(null, name.tempCString!FSChar()); -} - -/// ditto -void remove(R)(auto ref R name) -if (isConvertibleToString!R) -{ - remove!(StringTypeOf!R)(name); -} - -/// -@safe unittest -{ - import std.exception : assertThrown; - - deleteme.write("Hello"); - assert(deleteme.readText == "Hello"); - - deleteme.remove; - assertThrown!FileException(deleteme.readText); -} - -@safe unittest -{ - static assert(__traits(compiles, remove(TestAliasedString("foo")))); -} - -private void removeImpl(scope const(char)[] name, scope const(FSChar)* namez) @trusted -{ - version (Windows) - { - cenforce(DeleteFileW(namez), name, namez); - } - else version (Posix) - { - static import core.stdc.stdio; - - if (!name) - { - import core.stdc.string : strlen; - - auto len = namez ? strlen(namez) : 0; - name = namez[0 .. len]; - } - cenforce(core.stdc.stdio.remove(namez) == 0, - "Failed to remove file " ~ (name is null ? "(null)" : name)); - } -} - -@safe unittest -{ - import std.exception : collectExceptionMsg, assertThrown; - import std.algorithm.searching : startsWith; - - string filename = null; // e.g. as returned by File.tmpfile.name - - version (linux) - { - // exact exception message is OS-dependent - auto msg = filename.remove.collectExceptionMsg!FileException; - assert(msg.startsWith("Failed to remove file (null):"), msg); - } - else version (Windows) - { - // don't test exact message on windows, it's language dependent - auto msg = filename.remove.collectExceptionMsg!FileException; - assert(msg.startsWith("(null):"), msg); - } - else - { - assertThrown!FileException(filename.remove); - } -} - -version (Windows) private WIN32_FILE_ATTRIBUTE_DATA getFileAttributesWin(R)(R name) -if (isSomeFiniteCharInputRange!R) -{ - auto namez = name.tempCString!FSChar(); - - WIN32_FILE_ATTRIBUTE_DATA fad = void; - - static if (isNarrowString!R && is(immutable ElementEncodingType!R == immutable char)) - { - static void getFA(scope const(char)[] name, scope const(FSChar)* namez, - out WIN32_FILE_ATTRIBUTE_DATA fad) @trusted - { - import std.exception : enforce; - enforce(GetFileAttributesExW(namez, GET_FILEEX_INFO_LEVELS.GetFileExInfoStandard, &fad), - new FileException(name.idup)); - } - getFA(name, namez, fad); - } - else - { - static void getFA(scope const(FSChar)* namez, out WIN32_FILE_ATTRIBUTE_DATA fad) @trusted - { - import core.stdc.wchar_ : wcslen; - import std.conv : to; - import std.exception : enforce; - - enforce(GetFileAttributesExW(namez, GET_FILEEX_INFO_LEVELS.GetFileExInfoStandard, &fad), - new FileException(namez[0 .. wcslen(namez)].to!string)); - } - getFA(namez, fad); - } - return fad; -} - -version (Windows) private ulong makeUlong(DWORD dwLow, DWORD dwHigh) @safe pure nothrow @nogc -{ - ULARGE_INTEGER li; - li.LowPart = dwLow; - li.HighPart = dwHigh; - return li.QuadPart; -} - -version (Posix) private extern (C) pragma(mangle, stat.mangleof) -int trustedStat(scope const(FSChar)* namez, ref stat_t buf) @nogc nothrow @trusted; - -/** -Get size of file `name` in bytes. - -Params: - name = string or range of characters representing the file _name -Returns: - The size of file in bytes. -Throws: - $(LREF FileException) on error (e.g., file not found). - */ -ulong getSize(R)(R name) -if (isSomeFiniteCharInputRange!R && !isConvertibleToString!R) -{ - version (Windows) - { - with (getFileAttributesWin(name)) - return makeUlong(nFileSizeLow, nFileSizeHigh); - } - else version (Posix) - { - auto namez = name.tempCString(); - - static if (isNarrowString!R && is(immutable ElementEncodingType!R == immutable char)) - alias names = name; - else - string names = null; - stat_t statbuf = void; - cenforce(trustedStat(namez, statbuf) == 0, names, namez); - return statbuf.st_size; - } -} - -/// ditto -ulong getSize(R)(auto ref R name) -if (isConvertibleToString!R) -{ - return getSize!(StringTypeOf!R)(name); -} - -@safe unittest -{ - static assert(__traits(compiles, getSize(TestAliasedString("foo")))); -} - -/// -@safe unittest -{ - scope(exit) deleteme.remove; - - // create a file of size 1 - write(deleteme, "a"); - assert(getSize(deleteme) == 1); - - // create a file of size 3 - write(deleteme, "abc"); - assert(getSize(deleteme) == 3); -} - -@safe unittest -{ - // create a file of size 1 - write(deleteme, "a"); - scope(exit) deleteme.exists && deleteme.remove; - assert(getSize(deleteme) == 1); - // create a file of size 3 - write(deleteme, "abc"); - import std.utf : byChar; - assert(getSize(deleteme.byChar) == 3); -} - -// Reads a time field from a stat_t with full precision. -version (Posix) -private SysTime statTimeToStdTime(char which)(ref const stat_t statbuf) -{ - auto unixTime = mixin(`statbuf.st_` ~ which ~ `time`); - long stdTime = unixTimeToStdTime(unixTime); - - static if (is(typeof(mixin(`statbuf.st_` ~ which ~ `tim`)))) - stdTime += mixin(`statbuf.st_` ~ which ~ `tim.tv_nsec`) / 100; - else - static if (is(typeof(mixin(`statbuf.st_` ~ which ~ `timensec`)))) - stdTime += mixin(`statbuf.st_` ~ which ~ `timensec`) / 100; - else - static if (is(typeof(mixin(`statbuf.st_` ~ which ~ `time_nsec`)))) - stdTime += mixin(`statbuf.st_` ~ which ~ `time_nsec`) / 100; - else - static if (is(typeof(mixin(`statbuf.__st_` ~ which ~ `timensec`)))) - stdTime += mixin(`statbuf.__st_` ~ which ~ `timensec`) / 100; - - return SysTime(stdTime); -} - -/++ - Get the access and modified times of file or folder `name`. - - Params: - name = File/Folder _name to get times for. - accessTime = Time the file/folder was last accessed. - modificationTime = Time the file/folder was last modified. - - Throws: - $(LREF FileException) on error. - +/ -void getTimes(R)(R name, - out SysTime accessTime, - out SysTime modificationTime) -if (isSomeFiniteCharInputRange!R && !isConvertibleToString!R) -{ - version (Windows) - { - import std.datetime.systime : FILETIMEToSysTime; - - with (getFileAttributesWin(name)) - { - accessTime = FILETIMEToSysTime(&ftLastAccessTime); - modificationTime = FILETIMEToSysTime(&ftLastWriteTime); - } - } - else version (Posix) - { - auto namez = name.tempCString(); - - stat_t statbuf = void; - - static if (isNarrowString!R && is(immutable ElementEncodingType!R == immutable char)) - alias names = name; - else - string names = null; - cenforce(trustedStat(namez, statbuf) == 0, names, namez); - - accessTime = statTimeToStdTime!'a'(statbuf); - modificationTime = statTimeToStdTime!'m'(statbuf); - } -} - -/// ditto -void getTimes(R)(auto ref R name, - out SysTime accessTime, - out SysTime modificationTime) -if (isConvertibleToString!R) -{ - return getTimes!(StringTypeOf!R)(name, accessTime, modificationTime); -} - -/// -@safe unittest -{ - import std.datetime : abs, SysTime; - - scope(exit) deleteme.remove; - write(deleteme, "a"); - - SysTime accessTime, modificationTime; - - getTimes(deleteme, accessTime, modificationTime); - - import std.datetime : Clock, seconds; - auto currTime = Clock.currTime(); - enum leeway = 5.seconds; - - auto diffAccess = accessTime - currTime; - auto diffModification = modificationTime - currTime; - assert(abs(diffAccess) <= leeway); - assert(abs(diffModification) <= leeway); -} - -@safe unittest -{ - SysTime atime, mtime; - static assert(__traits(compiles, getTimes(TestAliasedString("foo"), atime, mtime))); -} - -@safe unittest -{ - import std.stdio : writefln; - - auto currTime = Clock.currTime(); - - write(deleteme, "a"); - scope(exit) assert(deleteme.exists), deleteme.remove; - - SysTime accessTime1; - SysTime modificationTime1; - - getTimes(deleteme, accessTime1, modificationTime1); - - enum leeway = 5.seconds; - - { - auto diffa = accessTime1 - currTime; - auto diffm = modificationTime1 - currTime; - scope(failure) writefln("[%s] [%s] [%s] [%s] [%s]", accessTime1, modificationTime1, currTime, diffa, diffm); - - assert(abs(diffa) <= leeway); - assert(abs(diffm) <= leeway); - } - - version (fullFileTests) - { - import core.thread; - enum sleepTime = dur!"seconds"(2); - Thread.sleep(sleepTime); - - currTime = Clock.currTime(); - write(deleteme, "b"); - - SysTime accessTime2 = void; - SysTime modificationTime2 = void; - - getTimes(deleteme, accessTime2, modificationTime2); - - { - auto diffa = accessTime2 - currTime; - auto diffm = modificationTime2 - currTime; - scope(failure) writefln("[%s] [%s] [%s] [%s] [%s]", accessTime2, modificationTime2, currTime, diffa, diffm); - - //There is no guarantee that the access time will be updated. - assert(abs(diffa) <= leeway + sleepTime); - assert(abs(diffm) <= leeway); - } - - assert(accessTime1 <= accessTime2); - assert(modificationTime1 <= modificationTime2); - } -} - - -version (StdDdoc) -{ - /++ - $(BLUE This function is Windows-Only.) - - Get creation/access/modified times of file `name`. - - This is the same as `getTimes` except that it also gives you the file - creation time - which isn't possible on POSIX systems. - - Params: - name = File _name to get times for. - fileCreationTime = Time the file was created. - fileAccessTime = Time the file was last accessed. - fileModificationTime = Time the file was last modified. - - Throws: - $(LREF FileException) on error. - +/ - void getTimesWin(R)(R name, - out SysTime fileCreationTime, - out SysTime fileAccessTime, - out SysTime fileModificationTime) - if (isSomeFiniteCharInputRange!R || isConvertibleToString!R); - // above line contains both constraints for docs - // (so users know how it can be called) -} -else version (Windows) -{ - void getTimesWin(R)(R name, - out SysTime fileCreationTime, - out SysTime fileAccessTime, - out SysTime fileModificationTime) - if (isSomeFiniteCharInputRange!R && !isConvertibleToString!R) - { - import std.datetime.systime : FILETIMEToSysTime; - - with (getFileAttributesWin(name)) - { - fileCreationTime = FILETIMEToSysTime(&ftCreationTime); - fileAccessTime = FILETIMEToSysTime(&ftLastAccessTime); - fileModificationTime = FILETIMEToSysTime(&ftLastWriteTime); - } - } - - void getTimesWin(R)(auto ref R name, - out SysTime fileCreationTime, - out SysTime fileAccessTime, - out SysTime fileModificationTime) - if (isConvertibleToString!R) - { - getTimesWin!(StringTypeOf!R)(name, fileCreationTime, fileAccessTime, fileModificationTime); - } -} - -version (Windows) @system unittest -{ - import std.stdio : writefln; - auto currTime = Clock.currTime(); - - write(deleteme, "a"); - scope(exit) { assert(exists(deleteme)); remove(deleteme); } - - SysTime creationTime1 = void; - SysTime accessTime1 = void; - SysTime modificationTime1 = void; - - getTimesWin(deleteme, creationTime1, accessTime1, modificationTime1); - - enum leeway = dur!"seconds"(5); - - { - auto diffc = creationTime1 - currTime; - auto diffa = accessTime1 - currTime; - auto diffm = modificationTime1 - currTime; - scope(failure) - { - writefln("[%s] [%s] [%s] [%s] [%s] [%s] [%s]", - creationTime1, accessTime1, modificationTime1, currTime, diffc, diffa, diffm); - } - - // Deleting and recreating a file doesn't seem to always reset the "file creation time" - //assert(abs(diffc) <= leeway); - assert(abs(diffa) <= leeway); - assert(abs(diffm) <= leeway); - } - - version (fullFileTests) - { - import core.thread; - Thread.sleep(dur!"seconds"(2)); - - currTime = Clock.currTime(); - write(deleteme, "b"); - - SysTime creationTime2 = void; - SysTime accessTime2 = void; - SysTime modificationTime2 = void; - - getTimesWin(deleteme, creationTime2, accessTime2, modificationTime2); - - { - auto diffa = accessTime2 - currTime; - auto diffm = modificationTime2 - currTime; - scope(failure) - { - writefln("[%s] [%s] [%s] [%s] [%s]", - accessTime2, modificationTime2, currTime, diffa, diffm); - } - - assert(abs(diffa) <= leeway); - assert(abs(diffm) <= leeway); - } - - assert(creationTime1 == creationTime2); - assert(accessTime1 <= accessTime2); - assert(modificationTime1 <= modificationTime2); - } - - { - SysTime ctime, atime, mtime; - static assert(__traits(compiles, getTimesWin(TestAliasedString("foo"), ctime, atime, mtime))); - } -} - -version (Darwin) -private -{ - import core.stdc.config : c_ulong; - enum ATTR_CMN_MODTIME = 0x00000400, ATTR_CMN_ACCTIME = 0x00001000; - alias attrgroup_t = uint; - static struct attrlist - { - ushort bitmapcount, reserved; - attrgroup_t commonattr, volattr, dirattr, fileattr, forkattr; - } - extern(C) int setattrlist(scope const(char)* path, scope ref attrlist attrs, - scope void* attrbuf, size_t attrBufSize, c_ulong options) nothrow @nogc @system; -} - -/++ - Set access/modified times of file or folder `name`. - - Params: - name = File/Folder _name to get times for. - accessTime = Time the file/folder was last accessed. - modificationTime = Time the file/folder was last modified. - - Throws: - $(LREF FileException) on error. - +/ -void setTimes(R)(R name, - SysTime accessTime, - SysTime modificationTime) -if (isSomeFiniteCharInputRange!R && !isConvertibleToString!R) -{ - auto namez = name.tempCString!FSChar(); - static if (isNarrowString!R && is(immutable ElementEncodingType!R == immutable char)) - alias names = name; - else - string names = null; - setTimesImpl(names, namez, accessTime, modificationTime); -} - -/// -@safe unittest -{ - import std.datetime : DateTime, hnsecs, SysTime; - - scope(exit) deleteme.remove; - write(deleteme, "a"); - - SysTime accessTime = SysTime(DateTime(2010, 10, 4, 0, 0, 30)); - SysTime modificationTime = SysTime(DateTime(2018, 10, 4, 0, 0, 30)); - setTimes(deleteme, accessTime, modificationTime); - - SysTime accessTimeResolved, modificationTimeResolved; - getTimes(deleteme, accessTimeResolved, modificationTimeResolved); - - assert(accessTime == accessTimeResolved); - assert(modificationTime == modificationTimeResolved); -} - -/// ditto -void setTimes(R)(auto ref R name, - SysTime accessTime, - SysTime modificationTime) -if (isConvertibleToString!R) -{ - setTimes!(StringTypeOf!R)(name, accessTime, modificationTime); -} - -private void setTimesImpl(scope const(char)[] names, scope const(FSChar)* namez, - SysTime accessTime, SysTime modificationTime) @trusted -{ - version (Windows) - { - import std.datetime.systime : SysTimeToFILETIME; - const ta = SysTimeToFILETIME(accessTime); - const tm = SysTimeToFILETIME(modificationTime); - alias defaults = - AliasSeq!(FILE_WRITE_ATTRIBUTES, - 0, - null, - OPEN_EXISTING, - FILE_ATTRIBUTE_NORMAL | - FILE_ATTRIBUTE_DIRECTORY | - FILE_FLAG_BACKUP_SEMANTICS, - HANDLE.init); - auto h = CreateFileW(namez, defaults); - - cenforce(h != INVALID_HANDLE_VALUE, names, namez); - - scope(exit) - cenforce(CloseHandle(h), names, namez); - - cenforce(SetFileTime(h, null, &ta, &tm), names, namez); - } - else - { - static if (is(typeof(&utimensat))) - { - timespec[2] t = void; - t[0] = accessTime.toTimeSpec(); - t[1] = modificationTime.toTimeSpec(); - cenforce(utimensat(AT_FDCWD, namez, t, 0) == 0, names, namez); - } - else - { - version (Darwin) - { - // Set modification & access times with setattrlist to avoid precision loss. - attrlist attrs = { bitmapcount: 5, reserved: 0, - commonattr: ATTR_CMN_MODTIME | ATTR_CMN_ACCTIME, - volattr: 0, dirattr: 0, fileattr: 0, forkattr: 0 }; - timespec[2] attrbuf = [modificationTime.toTimeSpec(), accessTime.toTimeSpec()]; - if (0 == setattrlist(namez, attrs, &attrbuf, attrbuf.sizeof, 0)) - return; - if (.errno != ENOTSUP) - cenforce(false, names, namez); - // Not all volumes support setattrlist. In such cases - // fall through to the utimes implementation. - } - timeval[2] t = void; - t[0] = accessTime.toTimeVal(); - t[1] = modificationTime.toTimeVal(); - cenforce(utimes(namez, t) == 0, names, namez); - } - } -} - -@safe unittest -{ - if (false) // Test instatiation - setTimes(TestAliasedString("foo"), SysTime.init, SysTime.init); -} - -@safe unittest -{ - import std.stdio : File; - string newdir = deleteme ~ r".dir"; - string dir = newdir ~ r"/a/b/c"; - string file = dir ~ "/file"; - - if (!exists(dir)) mkdirRecurse(dir); - { auto f = File(file, "w"); } - - void testTimes(int hnsecValue) - { - foreach (path; [file, dir]) // test file and dir - { - SysTime atime = SysTime(DateTime(2010, 10, 4, 0, 0, 30), hnsecs(hnsecValue)); - SysTime mtime = SysTime(DateTime(2011, 10, 4, 0, 0, 30), hnsecs(hnsecValue)); - setTimes(path, atime, mtime); - - SysTime atime_res; - SysTime mtime_res; - getTimes(path, atime_res, mtime_res); - assert(atime == atime_res); - assert(mtime == mtime_res); - } - } - - testTimes(0); - version (linux) - testTimes(123_456_7); - - rmdirRecurse(newdir); -} - -// https://issues.dlang.org/show_bug.cgi?id=23683 -@safe unittest -{ - scope(exit) deleteme.remove; - import std.stdio : File; - auto f = File(deleteme, "wb"); - SysTime time = SysTime(DateTime(2018, 10, 4, 0, 0, 30)); - setTimes(deleteme, time, time); -} - -/++ - Returns the time that the given file was last modified. - - Params: - name = the name of the file to check - Returns: - A $(REF SysTime,std,datetime,systime). - Throws: - $(LREF FileException) if the given file does not exist. -+/ -SysTime timeLastModified(R)(R name) -if (isSomeFiniteCharInputRange!R && !isConvertibleToString!R) -{ - version (Windows) - { - SysTime dummy; - SysTime ftm; - - getTimesWin(name, dummy, dummy, ftm); - - return ftm; - } - else version (Posix) - { - auto namez = name.tempCString!FSChar(); - stat_t statbuf = void; - - static if (isNarrowString!R && is(immutable ElementEncodingType!R == immutable char)) - alias names = name; - else - string names = null; - cenforce(trustedStat(namez, statbuf) == 0, names, namez); - - return statTimeToStdTime!'m'(statbuf); - } -} - -/// ditto -SysTime timeLastModified(R)(auto ref R name) -if (isConvertibleToString!R) -{ - return timeLastModified!(StringTypeOf!R)(name); -} - -/// -@safe unittest -{ - import std.datetime : abs, DateTime, hnsecs, SysTime; - scope(exit) deleteme.remove; - - import std.datetime : Clock, seconds; - auto currTime = Clock.currTime(); - enum leeway = 5.seconds; - deleteme.write("bb"); - assert(abs(deleteme.timeLastModified - currTime) <= leeway); -} - -@safe unittest -{ - static assert(__traits(compiles, timeLastModified(TestAliasedString("foo")))); -} - -/++ - Returns the time that the given file was last modified. If the - file does not exist, returns `returnIfMissing`. - - A frequent usage pattern occurs in build automation tools such as - $(HTTP gnu.org/software/make, make) or $(HTTP - en.wikipedia.org/wiki/Apache_Ant, ant). To check whether file $(D - target) must be rebuilt from file `source` (i.e., `target` is - older than `source` or does not exist), use the comparison - below. The code throws a $(LREF FileException) if `source` does not - exist (as it should). On the other hand, the `SysTime.min` default - makes a non-existing `target` seem infinitely old so the test - correctly prompts building it. - - Params: - name = The name of the file to get the modification time for. - returnIfMissing = The time to return if the given file does not exist. - Returns: - A $(REF SysTime,std,datetime,systime). - -Example: --------------------- -if (source.timeLastModified >= target.timeLastModified(SysTime.min)) -{ - // must (re)build -} -else -{ - // target is up-to-date -} --------------------- -+/ -SysTime timeLastModified(R)(R name, SysTime returnIfMissing) -if (isSomeFiniteCharInputRange!R) -{ - version (Windows) - { - if (!exists(name)) - return returnIfMissing; - - SysTime dummy; - SysTime ftm; - - getTimesWin(name, dummy, dummy, ftm); - - return ftm; - } - else version (Posix) - { - auto namez = name.tempCString!FSChar(); - stat_t statbuf = void; - - return trustedStat(namez, statbuf) != 0 ? - returnIfMissing : - statTimeToStdTime!'m'(statbuf); - } -} - -/// -@safe unittest -{ - import std.datetime : SysTime; - - assert("file.does.not.exist".timeLastModified(SysTime.min) == SysTime.min); - - auto source = deleteme ~ "source"; - auto target = deleteme ~ "target"; - scope(exit) source.remove, target.remove; - - source.write("."); - assert(target.timeLastModified(SysTime.min) < source.timeLastModified); - target.write("."); - assert(target.timeLastModified(SysTime.min) >= source.timeLastModified); -} - -version (StdDdoc) -{ - /++ - $(BLUE This function is POSIX-Only.) - - Returns the time that the given file was last modified. - Params: - statbuf = stat_t retrieved from file. - +/ - SysTime timeLastModified()(auto ref stat_t statbuf) pure nothrow {assert(false);} - /++ - $(BLUE This function is POSIX-Only.) - - Returns the time that the given file was last accessed. - Params: - statbuf = stat_t retrieved from file. - +/ - SysTime timeLastAccessed()(auto ref stat_t statbuf) pure nothrow {assert(false);} - /++ - $(BLUE This function is POSIX-Only.) - - Returns the time that the given file was last changed. - Params: - statbuf = stat_t retrieved from file. - +/ - SysTime timeStatusChanged()(auto ref stat_t statbuf) pure nothrow {assert(false);} -} -else version (Posix) -{ - SysTime timeLastModified()(auto ref stat_t statbuf) pure nothrow - { - return statTimeToStdTime!'m'(statbuf); - } - SysTime timeLastAccessed()(auto ref stat_t statbuf) pure nothrow - { - return statTimeToStdTime!'a'(statbuf); - } - SysTime timeStatusChanged()(auto ref stat_t statbuf) pure nothrow - { - return statTimeToStdTime!'c'(statbuf); - } - - @safe unittest - { - stat_t statbuf; - // check that both lvalues and rvalues work - timeLastAccessed(statbuf); - cast(void) timeLastAccessed(stat_t.init); - } -} - -@safe unittest -{ - //std.process.executeShell("echo a > deleteme"); - if (exists(deleteme)) - remove(deleteme); - - write(deleteme, "a\n"); - - scope(exit) - { - assert(exists(deleteme)); - remove(deleteme); - } - - // assert(lastModified("deleteme") > - // lastModified("this file does not exist", SysTime.min)); - //assert(lastModified("deleteme") > lastModified(__FILE__)); -} - - -// Tests sub-second precision of querying file times. -// Should pass on most modern systems running on modern filesystems. -// Exceptions: -// - FreeBSD, where one would need to first set the -// vfs.timestamp_precision sysctl to a value greater than zero. -// - OS X, where the native filesystem (HFS+) stores filesystem -// timestamps with 1-second precision. -// -// Note: on linux systems, although in theory a change to a file date -// can be tracked with precision of 4 msecs, this test waits 20 msecs -// to prevent possible problems relative to the CI services the dlang uses, -// as they may have the HZ setting that controls the software clock set to 100 -// (instead of the more common 250). -// see https://man7.org/linux/man-pages/man7/time.7.html -// https://stackoverflow.com/a/14393315, -// https://issues.dlang.org/show_bug.cgi?id=21148 -version (FreeBSD) {} else -version (DragonFlyBSD) {} else -version (OSX) {} else -@safe unittest -{ - import core.thread; - - if (exists(deleteme)) - remove(deleteme); - - SysTime lastTime; - foreach (n; 0 .. 3) - { - write(deleteme, "a"); - auto time = timeLastModified(deleteme); - remove(deleteme); - assert(time != lastTime); - lastTime = time; - () @trusted { Thread.sleep(20.msecs); }(); - } -} - - -/** - * Determine whether the given file (or directory) _exists. - * Params: - * name = string or range of characters representing the file _name - * Returns: - * true if the file _name specified as input _exists - */ -bool exists(R)(R name) -if (isSomeFiniteCharInputRange!R && !isConvertibleToString!R) -{ - return existsImpl(name.tempCString!FSChar()); -} - -/// ditto -bool exists(R)(auto ref R name) -if (isConvertibleToString!R) -{ - return exists!(StringTypeOf!R)(name); -} - -/// -@safe unittest -{ - auto f = deleteme ~ "does.not.exist"; - assert(!f.exists); - - f.write("hello"); - assert(f.exists); - - f.remove; - assert(!f.exists); -} - -private bool existsImpl(scope const(FSChar)* namez) @trusted nothrow @nogc -{ - version (Windows) - { - // http://msdn.microsoft.com/library/default.asp?url=/library/en-us/ - // fileio/base/getfileattributes.asp - return GetFileAttributesW(namez) != 0xFFFFFFFF; - } - else version (Posix) - { - /* - The reason why we use stat (and not access) here is - the quirky behavior of access for SUID programs: if - we used access, a file may not appear to "exist", - despite that the program would be able to open it - just fine. The behavior in question is described as - follows in the access man page: - - > The check is done using the calling process's real - > UID and GID, rather than the effective IDs as is - > done when actually attempting an operation (e.g., - > open(2)) on the file. This allows set-user-ID - > programs to easily determine the invoking user's - > authority. - - While various operating systems provide eaccess or - euidaccess functions, these are not part of POSIX - - so it's safer to use stat instead. - */ - - stat_t statbuf = void; - return lstat(namez, &statbuf) == 0; - } - else - static assert(0); -} - -/// -@safe unittest -{ - assert(".".exists); - assert(!"this file does not exist".exists); - deleteme.write("a\n"); - scope(exit) deleteme.remove; - assert(deleteme.exists); -} - -// https://issues.dlang.org/show_bug.cgi?id=16573 -@safe unittest -{ - enum S : string { foo = "foo" } - assert(__traits(compiles, S.foo.exists)); -} - -/++ - Returns the attributes of the given file. - - Note that the file attributes on Windows and POSIX systems are - completely different. On Windows, they're what is returned by - $(HTTP msdn.microsoft.com/en-us/library/aa364944(v=vs.85).aspx, - GetFileAttributes), whereas on POSIX systems, they're the - `st_mode` value which is part of the $(D stat struct) gotten by - calling the $(HTTP en.wikipedia.org/wiki/Stat_%28Unix%29, `stat`) - function. - - On POSIX systems, if the given file is a symbolic link, then - attributes are the attributes of the file pointed to by the symbolic - link. - - Params: - name = The file to get the attributes of. - Returns: - The attributes of the file as a `uint`. - Throws: $(LREF FileException) on error. - +/ -uint getAttributes(R)(R name) -if (isSomeFiniteCharInputRange!R && !isConvertibleToString!R) -{ - version (Windows) - { - auto namez = name.tempCString!FSChar(); - static auto trustedGetFileAttributesW(scope const(FSChar)* namez) @trusted - { - return GetFileAttributesW(namez); - } - immutable result = trustedGetFileAttributesW(namez); - - static if (isNarrowString!R && is(immutable ElementEncodingType!R == immutable char)) - alias names = name; - else - string names = null; - cenforce(result != INVALID_FILE_ATTRIBUTES, names, namez); - - return result; - } - else version (Posix) - { - auto namez = name.tempCString!FSChar(); - stat_t statbuf = void; - - static if (isNarrowString!R && is(immutable ElementEncodingType!R == immutable char)) - alias names = name; - else - string names = null; - cenforce(trustedStat(namez, statbuf) == 0, names, namez); - - return statbuf.st_mode; - } -} - -/// ditto -uint getAttributes(R)(auto ref R name) -if (isConvertibleToString!R) -{ - return getAttributes!(StringTypeOf!R)(name); -} - -/// getAttributes with a file -@safe unittest -{ - import std.exception : assertThrown; - - auto f = deleteme ~ "file"; - scope(exit) f.remove; - - assert(!f.exists); - assertThrown!FileException(f.getAttributes); - - f.write("."); - auto attributes = f.getAttributes; - assert(!attributes.attrIsDir); - assert(attributes.attrIsFile); -} - -/// getAttributes with a directory -@safe unittest -{ - import std.exception : assertThrown; - - auto dir = deleteme ~ "dir"; - scope(exit) dir.rmdir; - - assert(!dir.exists); - assertThrown!FileException(dir.getAttributes); - - dir.mkdir; - auto attributes = dir.getAttributes; - assert(attributes.attrIsDir); - assert(!attributes.attrIsFile); -} - -@safe unittest -{ - static assert(__traits(compiles, getAttributes(TestAliasedString(null)))); -} - -/++ - If the given file is a symbolic link, then this returns the attributes of the - symbolic link itself rather than file that it points to. If the given file - is $(I not) a symbolic link, then this function returns the same result - as getAttributes. - - On Windows, getLinkAttributes is identical to getAttributes. It exists on - Windows so that you don't have to special-case code for Windows when dealing - with symbolic links. - - Params: - name = The file to get the symbolic link attributes of. - - Returns: - the attributes - - Throws: - $(LREF FileException) on error. - +/ -uint getLinkAttributes(R)(R name) -if (isSomeFiniteCharInputRange!R && !isConvertibleToString!R) -{ - version (Windows) - { - return getAttributes(name); - } - else version (Posix) - { - auto namez = name.tempCString!FSChar(); - static auto trustedLstat(const(FSChar)* namez, ref stat_t buf) @trusted - { - return lstat(namez, &buf); - } - stat_t lstatbuf = void; - static if (isNarrowString!R && is(immutable ElementEncodingType!R == immutable char)) - alias names = name; - else - string names = null; - cenforce(trustedLstat(namez, lstatbuf) == 0, names, namez); - return lstatbuf.st_mode; - } -} - -/// ditto -uint getLinkAttributes(R)(auto ref R name) -if (isConvertibleToString!R) -{ - return getLinkAttributes!(StringTypeOf!R)(name); -} - -/// -@safe unittest -{ - import std.exception : assertThrown; - - auto source = deleteme ~ "source"; - auto target = deleteme ~ "target"; - - assert(!source.exists); - assertThrown!FileException(source.getLinkAttributes); - - // symlinking isn't available on Windows - version (Posix) - { - scope(exit) source.remove, target.remove; - - target.write("target"); - target.symlink(source); - assert(source.readText == "target"); - assert(source.isSymlink); - assert(source.getLinkAttributes.attrIsSymlink); - } -} - -/// if the file is no symlink, getLinkAttributes behaves like getAttributes -@safe unittest -{ - import std.exception : assertThrown; - - auto f = deleteme ~ "file"; - scope(exit) f.remove; - - assert(!f.exists); - assertThrown!FileException(f.getLinkAttributes); - - f.write("."); - auto attributes = f.getLinkAttributes; - assert(!attributes.attrIsDir); - assert(attributes.attrIsFile); -} - -/// if the file is no symlink, getLinkAttributes behaves like getAttributes -@safe unittest -{ - import std.exception : assertThrown; - - auto dir = deleteme ~ "dir"; - scope(exit) dir.rmdir; - - assert(!dir.exists); - assertThrown!FileException(dir.getLinkAttributes); - - dir.mkdir; - auto attributes = dir.getLinkAttributes; - assert(attributes.attrIsDir); - assert(!attributes.attrIsFile); -} - -@safe unittest -{ - static assert(__traits(compiles, getLinkAttributes(TestAliasedString(null)))); -} - -/++ - Set the _attributes of the given file. - - For example, a programmatic equivalent of Unix's `chmod +x name` - to make a file executable is - `name.setAttributes(name.getAttributes | octal!700)`. - - Params: - name = the file _name - attributes = the _attributes to set the file to - - Throws: - $(LREF FileException) if the given file does not exist. - +/ -void setAttributes(R)(R name, uint attributes) -if (isSomeFiniteCharInputRange!R && !isConvertibleToString!R) -{ - version (Windows) - { - auto namez = name.tempCString!FSChar(); - static auto trustedSetFileAttributesW(scope const(FSChar)* namez, uint dwFileAttributes) @trusted - { - return SetFileAttributesW(namez, dwFileAttributes); - } - static if (isNarrowString!R && is(immutable ElementEncodingType!R == immutable char)) - alias names = name; - else - string names = null; - cenforce(trustedSetFileAttributesW(namez, attributes), names, namez); - } - else version (Posix) - { - auto namez = name.tempCString!FSChar(); - static auto trustedChmod(scope const(FSChar)* namez, mode_t mode) @trusted - { - return chmod(namez, mode); - } - assert(attributes <= mode_t.max); - static if (isNarrowString!R && is(immutable ElementEncodingType!R == immutable char)) - alias names = name; - else - string names = null; - cenforce(!trustedChmod(namez, cast(mode_t) attributes), names, namez); - } -} - -/// ditto -void setAttributes(R)(auto ref R name, uint attributes) -if (isConvertibleToString!R) -{ - return setAttributes!(StringTypeOf!R)(name, attributes); -} - -@safe unittest -{ - static assert(__traits(compiles, setAttributes(TestAliasedString(null), 0))); -} - -/// setAttributes with a file -@safe unittest -{ - import std.exception : assertThrown; - import std.conv : octal; - - auto f = deleteme ~ "file"; - version (Posix) - { - scope(exit) f.remove; - - assert(!f.exists); - assertThrown!FileException(f.setAttributes(octal!777)); - - f.write("."); - auto attributes = f.getAttributes; - assert(!attributes.attrIsDir); - assert(attributes.attrIsFile); - - f.setAttributes(octal!777); - attributes = f.getAttributes; - - assert((attributes & 1023) == octal!777); - } -} - -/// setAttributes with a directory -@safe unittest -{ - import std.exception : assertThrown; - import std.conv : octal; - - auto dir = deleteme ~ "dir"; - version (Posix) - { - scope(exit) dir.rmdir; - - assert(!dir.exists); - assertThrown!FileException(dir.setAttributes(octal!777)); - - dir.mkdir; - auto attributes = dir.getAttributes; - assert(attributes.attrIsDir); - assert(!attributes.attrIsFile); - - dir.setAttributes(octal!777); - attributes = dir.getAttributes; - - assert((attributes & 1023) == octal!777); - } -} - -/++ - Returns whether the given file is a directory. - - Params: - name = The path to the file. - - Returns: - true if name specifies a directory - - Throws: - $(LREF FileException) if the given file does not exist. - +/ -@property bool isDir(R)(R name) -if (isSomeFiniteCharInputRange!R && !isConvertibleToString!R) -{ - version (Windows) - { - return (getAttributes(name) & FILE_ATTRIBUTE_DIRECTORY) != 0; - } - else version (Posix) - { - return (getAttributes(name) & S_IFMT) == S_IFDIR; - } -} - -/// ditto -@property bool isDir(R)(auto ref R name) -if (isConvertibleToString!R) -{ - return name.isDir!(StringTypeOf!R); -} - -/// - -@safe unittest -{ - import std.exception : assertThrown; - - auto dir = deleteme ~ "dir"; - auto f = deleteme ~ "f"; - scope(exit) dir.rmdir, f.remove; - - assert(!dir.exists); - assertThrown!FileException(dir.isDir); - - dir.mkdir; - assert(dir.isDir); - - f.write("."); - assert(!f.isDir); -} - -@safe unittest -{ - static assert(__traits(compiles, TestAliasedString(null).isDir)); -} - -@safe unittest -{ - version (Windows) - { - if ("C:\\Program Files\\".exists) - assert("C:\\Program Files\\".isDir); - - if ("C:\\Windows\\system.ini".exists) - assert(!"C:\\Windows\\system.ini".isDir); - } - else version (Posix) - { - if (system_directory.exists) - assert(system_directory.isDir); - - if (system_file.exists) - assert(!system_file.isDir); - } -} - -@safe unittest -{ - version (Windows) - enum dir = "C:\\Program Files\\"; - else version (Posix) - enum dir = system_directory; - - if (dir.exists) - { - DirEntry de = DirEntry(dir); - assert(de.isDir); - assert(DirEntry(dir).isDir); - } -} - -/++ - Returns whether the given file _attributes are for a directory. - - Params: - attributes = The file _attributes. - - Returns: - true if attributes specifies a directory -+/ -bool attrIsDir(uint attributes) @safe pure nothrow @nogc -{ - version (Windows) - { - return (attributes & FILE_ATTRIBUTE_DIRECTORY) != 0; - } - else version (Posix) - { - return (attributes & S_IFMT) == S_IFDIR; - } -} - -/// -@safe unittest -{ - import std.exception : assertThrown; - - auto dir = deleteme ~ "dir"; - auto f = deleteme ~ "f"; - scope(exit) dir.rmdir, f.remove; - - assert(!dir.exists); - assertThrown!FileException(dir.getAttributes.attrIsDir); - - dir.mkdir; - assert(dir.isDir); - assert(dir.getAttributes.attrIsDir); - - f.write("."); - assert(!f.isDir); - assert(!f.getAttributes.attrIsDir); -} - -@safe unittest -{ - version (Windows) - { - if ("C:\\Program Files\\".exists) - { - assert(attrIsDir(getAttributes("C:\\Program Files\\"))); - assert(attrIsDir(getLinkAttributes("C:\\Program Files\\"))); - } - - if ("C:\\Windows\\system.ini".exists) - { - assert(!attrIsDir(getAttributes("C:\\Windows\\system.ini"))); - assert(!attrIsDir(getLinkAttributes("C:\\Windows\\system.ini"))); - } - } - else version (Posix) - { - if (system_directory.exists) - { - assert(attrIsDir(getAttributes(system_directory))); - assert(attrIsDir(getLinkAttributes(system_directory))); - } - - if (system_file.exists) - { - assert(!attrIsDir(getAttributes(system_file))); - assert(!attrIsDir(getLinkAttributes(system_file))); - } - } -} - - -/++ - Returns whether the given file (or directory) is a file. - - On Windows, if a file is not a directory, then it's a file. So, - either `isFile` or `isDir` will return true for any given file. - - On POSIX systems, if `isFile` is `true`, that indicates that the file - is a regular file (e.g. not a block not device). So, on POSIX systems, it's - possible for both `isFile` and `isDir` to be `false` for a - particular file (in which case, it's a special file). You can use - `getAttributes` to get the attributes to figure out what type of special - it is, or you can use `DirEntry` to get at its `statBuf`, which is the - result from `stat`. In either case, see the man page for `stat` for - more information. - - Params: - name = The path to the file. - - Returns: - true if name specifies a file - - Throws: - $(LREF FileException) if the given file does not exist. -+/ -@property bool isFile(R)(R name) -if (isSomeFiniteCharInputRange!R && !isConvertibleToString!R) -{ - version (Windows) - return !name.isDir; - else version (Posix) - return (getAttributes(name) & S_IFMT) == S_IFREG; -} - -/// ditto -@property bool isFile(R)(auto ref R name) -if (isConvertibleToString!R) -{ - return isFile!(StringTypeOf!R)(name); -} - -/// -@safe unittest -{ - import std.exception : assertThrown; - - auto dir = deleteme ~ "dir"; - auto f = deleteme ~ "f"; - scope(exit) dir.rmdir, f.remove; - - dir.mkdir; - assert(!dir.isFile); - - assert(!f.exists); - assertThrown!FileException(f.isFile); - - f.write("."); - assert(f.isFile); -} - -// https://issues.dlang.org/show_bug.cgi?id=15658 -@safe unittest -{ - DirEntry e = DirEntry("."); - static assert(is(typeof(isFile(e)))); -} - -@safe unittest -{ - static assert(__traits(compiles, TestAliasedString(null).isFile)); -} - -@safe unittest -{ - version (Windows) - { - if ("C:\\Program Files\\".exists) - assert(!"C:\\Program Files\\".isFile); - - if ("C:\\Windows\\system.ini".exists) - assert("C:\\Windows\\system.ini".isFile); - } - else version (Posix) - { - if (system_directory.exists) - assert(!system_directory.isFile); - - if (system_file.exists) - assert(system_file.isFile); - } -} - - -/++ - Returns whether the given file _attributes are for a file. - - On Windows, if a file is not a directory, it's a file. So, either - `attrIsFile` or `attrIsDir` will return `true` for the - _attributes of any given file. - - On POSIX systems, if `attrIsFile` is `true`, that indicates that the - file is a regular file (e.g. not a block not device). So, on POSIX systems, - it's possible for both `attrIsFile` and `attrIsDir` to be `false` - for a particular file (in which case, it's a special file). If a file is a - special file, you can use the _attributes to check what type of special file - it is (see the man page for `stat` for more information). - - Params: - attributes = The file _attributes. - - Returns: - true if the given file _attributes are for a file - -Example: --------------------- -assert(attrIsFile(getAttributes("/etc/fonts/fonts.conf"))); -assert(attrIsFile(getLinkAttributes("/etc/fonts/fonts.conf"))); --------------------- - +/ -bool attrIsFile(uint attributes) @safe pure nothrow @nogc -{ - version (Windows) - { - return (attributes & FILE_ATTRIBUTE_DIRECTORY) == 0; - } - else version (Posix) - { - return (attributes & S_IFMT) == S_IFREG; - } -} - -/// -@safe unittest -{ - import std.exception : assertThrown; - - auto dir = deleteme ~ "dir"; - auto f = deleteme ~ "f"; - scope(exit) dir.rmdir, f.remove; - - dir.mkdir; - assert(!dir.isFile); - assert(!dir.getAttributes.attrIsFile); - - assert(!f.exists); - assertThrown!FileException(f.getAttributes.attrIsFile); - - f.write("."); - assert(f.isFile); - assert(f.getAttributes.attrIsFile); -} - -@safe unittest -{ - version (Windows) - { - if ("C:\\Program Files\\".exists) - { - assert(!attrIsFile(getAttributes("C:\\Program Files\\"))); - assert(!attrIsFile(getLinkAttributes("C:\\Program Files\\"))); - } - - if ("C:\\Windows\\system.ini".exists) - { - assert(attrIsFile(getAttributes("C:\\Windows\\system.ini"))); - assert(attrIsFile(getLinkAttributes("C:\\Windows\\system.ini"))); - } - } - else version (Posix) - { - if (system_directory.exists) - { - assert(!attrIsFile(getAttributes(system_directory))); - assert(!attrIsFile(getLinkAttributes(system_directory))); - } - - if (system_file.exists) - { - assert(attrIsFile(getAttributes(system_file))); - assert(attrIsFile(getLinkAttributes(system_file))); - } - } -} - - -/++ - Returns whether the given file is a symbolic link. - - On Windows, returns `true` when the file is either a symbolic link or a - junction point. - - Params: - name = The path to the file. - - Returns: - true if name is a symbolic link - - Throws: - $(LREF FileException) if the given file does not exist. - +/ -@property bool isSymlink(R)(R name) -if (isSomeFiniteCharInputRange!R && !isConvertibleToString!R) -{ - version (Windows) - return (getAttributes(name) & FILE_ATTRIBUTE_REPARSE_POINT) != 0; - else version (Posix) - return (getLinkAttributes(name) & S_IFMT) == S_IFLNK; -} - -/// ditto -@property bool isSymlink(R)(auto ref R name) -if (isConvertibleToString!R) -{ - return name.isSymlink!(StringTypeOf!R); -} - -@safe unittest -{ - static assert(__traits(compiles, TestAliasedString(null).isSymlink)); -} - -/// -@safe unittest -{ - import std.exception : assertThrown; - - auto source = deleteme ~ "source"; - auto target = deleteme ~ "target"; - - assert(!source.exists); - assertThrown!FileException(source.isSymlink); - - // symlinking isn't available on Windows - version (Posix) - { - scope(exit) source.remove, target.remove; - - target.write("target"); - target.symlink(source); - assert(source.readText == "target"); - assert(source.isSymlink); - assert(source.getLinkAttributes.attrIsSymlink); - } -} - -@system unittest -{ - version (Windows) - { - if ("C:\\Program Files\\".exists) - assert(!"C:\\Program Files\\".isSymlink); - - if ("C:\\Users\\".exists && "C:\\Documents and Settings\\".exists) - assert("C:\\Documents and Settings\\".isSymlink); - - enum fakeSymFile = "C:\\Windows\\system.ini"; - if (fakeSymFile.exists) - { - assert(!fakeSymFile.isSymlink); - - assert(!fakeSymFile.isSymlink); - assert(!attrIsSymlink(getAttributes(fakeSymFile))); - assert(!attrIsSymlink(getLinkAttributes(fakeSymFile))); - - assert(attrIsFile(getAttributes(fakeSymFile))); - assert(attrIsFile(getLinkAttributes(fakeSymFile))); - assert(!attrIsDir(getAttributes(fakeSymFile))); - assert(!attrIsDir(getLinkAttributes(fakeSymFile))); - - assert(getAttributes(fakeSymFile) == getLinkAttributes(fakeSymFile)); - } - } - else version (Posix) - { - if (system_directory.exists) - { - assert(!system_directory.isSymlink); - - immutable symfile = deleteme ~ "_slink\0"; - scope(exit) if (symfile.exists) symfile.remove(); - - core.sys.posix.unistd.symlink(system_directory, symfile.ptr); - - assert(symfile.isSymlink); - assert(!attrIsSymlink(getAttributes(symfile))); - assert(attrIsSymlink(getLinkAttributes(symfile))); - - assert(attrIsDir(getAttributes(symfile))); - assert(!attrIsDir(getLinkAttributes(symfile))); - - assert(!attrIsFile(getAttributes(symfile))); - assert(!attrIsFile(getLinkAttributes(symfile))); - } - - if (system_file.exists) - { - assert(!system_file.isSymlink); - - immutable symfile = deleteme ~ "_slink\0"; - scope(exit) if (symfile.exists) symfile.remove(); - - core.sys.posix.unistd.symlink(system_file, symfile.ptr); - - assert(symfile.isSymlink); - assert(!attrIsSymlink(getAttributes(symfile))); - assert(attrIsSymlink(getLinkAttributes(symfile))); - - assert(!attrIsDir(getAttributes(symfile))); - assert(!attrIsDir(getLinkAttributes(symfile))); - - assert(attrIsFile(getAttributes(symfile))); - assert(!attrIsFile(getLinkAttributes(symfile))); - } - } - - static assert(__traits(compiles, () @safe { return "dummy".isSymlink; })); -} - - -/++ - Returns whether the given file attributes are for a symbolic link. - - On Windows, return `true` when the file is either a symbolic link or a - junction point. - - Params: - attributes = The file attributes. - - Returns: - true if attributes are for a symbolic link - -Example: --------------------- -core.sys.posix.unistd.symlink("/etc/fonts/fonts.conf", "/tmp/alink"); - -assert(!getAttributes("/tmp/alink").isSymlink); -assert(getLinkAttributes("/tmp/alink").isSymlink); --------------------- - +/ -bool attrIsSymlink(uint attributes) @safe pure nothrow @nogc -{ - version (Windows) - return (attributes & FILE_ATTRIBUTE_REPARSE_POINT) != 0; - else version (Posix) - return (attributes & S_IFMT) == S_IFLNK; -} - -/// -@safe unittest -{ - import std.exception : assertThrown; - - auto source = deleteme ~ "source"; - auto target = deleteme ~ "target"; - - assert(!source.exists); - assertThrown!FileException(source.getLinkAttributes.attrIsSymlink); - - // symlinking isn't available on Windows - version (Posix) - { - scope(exit) source.remove, target.remove; - - target.write("target"); - target.symlink(source); - assert(source.readText == "target"); - assert(source.isSymlink); - assert(source.getLinkAttributes.attrIsSymlink); - } -} - -/** -Change directory to `pathname`. Equivalent to `cd` on -Windows and POSIX. - -Params: - pathname = the directory to step into - -Throws: $(LREF FileException) on error. - */ -void chdir(R)(R pathname) -if (isSomeFiniteCharInputRange!R && !isConvertibleToString!R) -{ - // Place outside of @trusted block - auto pathz = pathname.tempCString!FSChar(); - - version (Windows) - { - static auto trustedChdir(scope const(FSChar)* pathz) @trusted - { - return SetCurrentDirectoryW(pathz); - } - } - else version (Posix) - { - static auto trustedChdir(scope const(FSChar)* pathz) @trusted - { - return core.sys.posix.unistd.chdir(pathz) == 0; - } - } - static if (isNarrowString!R && is(immutable ElementEncodingType!R == immutable char)) - alias pathStr = pathname; - else - string pathStr = null; - cenforce(trustedChdir(pathz), pathStr, pathz); -} - -/// ditto -void chdir(R)(auto ref R pathname) -if (isConvertibleToString!R) -{ - return chdir!(StringTypeOf!R)(pathname); -} - -/// -@system unittest -{ - import std.algorithm.comparison : equal; - import std.algorithm.sorting : sort; - import std.array : array; - import std.path : buildPath; - - auto cwd = getcwd; - auto dir = deleteme ~ "dir"; - dir.mkdir; - scope(exit) cwd.chdir, dir.rmdirRecurse; - - dir.buildPath("a").write("."); - dir.chdir; // step into dir - "b".write("."); - assert(dirEntries(".", SpanMode.shallow).array.sort.equal( - [".".buildPath("a"), ".".buildPath("b")] - )); -} - -@safe unittest -{ - static assert(__traits(compiles, chdir(TestAliasedString(null)))); -} - -/** -Make a new directory `pathname`. - -Params: - pathname = the path of the directory to make - -Throws: - $(LREF FileException) on POSIX or $(LREF WindowsException) on Windows - if an error occured. - */ -void mkdir(R)(R pathname) -if (isSomeFiniteCharInputRange!R && !isConvertibleToString!R) -{ - // Place outside of @trusted block - const pathz = pathname.tempCString!FSChar(); - - version (Windows) - { - static auto trustedCreateDirectoryW(scope const(FSChar)* pathz) @trusted - { - return CreateDirectoryW(pathz, null); - } - static if (isNarrowString!R && is(immutable ElementEncodingType!R == immutable char)) - alias pathStr = pathname; - else - string pathStr = null; - wenforce(trustedCreateDirectoryW(pathz), pathStr, pathz); - } - else version (Posix) - { - import std.conv : octal; - - static auto trustedMkdir(scope const(FSChar)* pathz, mode_t mode) @trusted - { - return core.sys.posix.sys.stat.mkdir(pathz, mode); - } - static if (isNarrowString!R && is(immutable ElementEncodingType!R == immutable char)) - alias pathStr = pathname; - else - string pathStr = null; - cenforce(trustedMkdir(pathz, octal!777) == 0, pathStr, pathz); - } -} - -/// ditto -void mkdir(R)(auto ref R pathname) -if (isConvertibleToString!R) -{ - return mkdir!(StringTypeOf!R)(pathname); -} - -@safe unittest -{ - import std.file : mkdir; - static assert(__traits(compiles, mkdir(TestAliasedString(null)))); -} - -/// -@safe unittest -{ - import std.file : mkdir; - - auto dir = deleteme ~ "dir"; - scope(exit) dir.rmdir; - - dir.mkdir; - assert(dir.exists); -} - -/// -@safe unittest -{ - import std.exception : assertThrown; - assertThrown("a/b/c/d/e".mkdir); -} - -// Same as mkdir but ignores "already exists" errors. -// Returns: "true" if the directory was created, -// "false" if it already existed. -private bool ensureDirExists()(scope const(char)[] pathname) -{ - import std.exception : enforce; - const pathz = pathname.tempCString!FSChar(); - - version (Windows) - { - if (() @trusted { return CreateDirectoryW(pathz, null); }()) - return true; - cenforce(GetLastError() == ERROR_ALREADY_EXISTS, pathname.idup); - } - else version (Posix) - { - import std.conv : octal; - - if (() @trusted { return core.sys.posix.sys.stat.mkdir(pathz, octal!777); }() == 0) - return true; - cenforce(errno == EEXIST || errno == EISDIR, pathname); - } - enforce(pathname.isDir, new FileException(pathname.idup)); - return false; -} - -/** -Make directory and all parent directories as needed. - -Does nothing if the directory specified by -`pathname` already exists. - -Params: - pathname = the full path of the directory to create - -Throws: $(LREF FileException) on error. - */ -void mkdirRecurse(scope const(char)[] pathname) @safe -{ - import std.path : dirName, baseName; - - const left = dirName(pathname); - if (left.length != pathname.length && !exists(left)) - { - mkdirRecurse(left); - } - if (!baseName(pathname).empty) - { - ensureDirExists(pathname); - } -} - -/// -@safe unittest -{ - import std.path : buildPath; - - auto dir = deleteme ~ "dir"; - scope(exit) dir.rmdirRecurse; - - dir.mkdir; - assert(dir.exists); - dir.mkdirRecurse; // does nothing - - // creates all parent directories as needed - auto nested = dir.buildPath("a", "b", "c"); - nested.mkdirRecurse; - assert(nested.exists); -} - -/// -@safe unittest -{ - import std.exception : assertThrown; - - scope(exit) deleteme.remove; - deleteme.write("a"); - - // cannot make directory as it's already a file - assertThrown!FileException(deleteme.mkdirRecurse); -} - -@safe unittest -{ - import std.exception : assertThrown; - { - import std.path : buildPath, buildNormalizedPath; - - immutable basepath = deleteme ~ "_dir"; - scope(exit) () @trusted { rmdirRecurse(basepath); }(); - - auto path = buildPath(basepath, "a", "..", "b"); - mkdirRecurse(path); - path = path.buildNormalizedPath; - assert(path.isDir); - - path = buildPath(basepath, "c"); - write(path, ""); - assertThrown!FileException(mkdirRecurse(path)); - - path = buildPath(basepath, "d"); - mkdirRecurse(path); - mkdirRecurse(path); // should not throw - } - - version (Windows) - { - assertThrown!FileException(mkdirRecurse(`1:\foobar`)); - } - - // https://issues.dlang.org/show_bug.cgi?id=3570 - { - immutable basepath = deleteme ~ "_dir"; - version (Windows) - { - immutable path = basepath ~ "\\fake\\here\\"; - } - else version (Posix) - { - immutable path = basepath ~ `/fake/here/`; - } - - mkdirRecurse(path); - assert(basepath.exists && basepath.isDir); - scope(exit) () @trusted { rmdirRecurse(basepath); }(); - assert(path.exists && path.isDir); - } -} - -/**************************************************** -Remove directory `pathname`. - -Params: - pathname = Range or string specifying the directory name - -Throws: $(LREF FileException) on error. - */ -void rmdir(R)(R pathname) -if (isSomeFiniteCharInputRange!R && !isConvertibleToString!R) -{ - // Place outside of @trusted block - auto pathz = pathname.tempCString!FSChar(); - - version (Windows) - { - static auto trustedRmdir(scope const(FSChar)* pathz) @trusted - { - return RemoveDirectoryW(pathz); - } - } - else version (Posix) - { - static auto trustedRmdir(scope const(FSChar)* pathz) @trusted - { - return core.sys.posix.unistd.rmdir(pathz) == 0; - } - } - static if (isNarrowString!R && is(immutable ElementEncodingType!R == immutable char)) - alias pathStr = pathname; - else - string pathStr = null; - cenforce(trustedRmdir(pathz), pathStr, pathz); -} - -/// ditto -void rmdir(R)(auto ref R pathname) -if (isConvertibleToString!R) -{ - rmdir!(StringTypeOf!R)(pathname); -} - -@safe unittest -{ - static assert(__traits(compiles, rmdir(TestAliasedString(null)))); -} - -/// -@safe unittest -{ - auto dir = deleteme ~ "dir"; - - dir.mkdir; - assert(dir.exists); - dir.rmdir; - assert(!dir.exists); -} - -/++ - $(BLUE This function is POSIX-Only.) - - Creates a symbolic _link (_symlink). - - Params: - original = The file that is being linked. This is the target path that's - stored in the _symlink. A relative path is relative to the created - _symlink. - link = The _symlink to create. A relative path is relative to the - current working directory. - - Throws: - $(LREF FileException) on error (which includes if the _symlink already - exists). - +/ -version (StdDdoc) void symlink(RO, RL)(RO original, RL link) -if ((isSomeFiniteCharInputRange!RO || isConvertibleToString!RO) && - (isSomeFiniteCharInputRange!RL || isConvertibleToString!RL)); -else version (Posix) void symlink(RO, RL)(RO original, RL link) -if ((isSomeFiniteCharInputRange!RO || isConvertibleToString!RO) && - (isSomeFiniteCharInputRange!RL || isConvertibleToString!RL)) -{ - static if (isConvertibleToString!RO || isConvertibleToString!RL) - { - import std.meta : staticMap; - alias Types = staticMap!(convertToString, RO, RL); - symlink!Types(original, link); - } - else - { - import std.conv : text; - auto oz = original.tempCString(); - auto lz = link.tempCString(); - alias posixSymlink = core.sys.posix.unistd.symlink; - immutable int result = () @trusted { return posixSymlink(oz, lz); } (); - cenforce(result == 0, text(link)); - } -} - -version (Posix) @safe unittest -{ - if (system_directory.exists) - { - immutable symfile = deleteme ~ "_slink\0"; - scope(exit) if (symfile.exists) symfile.remove(); - - symlink(system_directory, symfile); - - assert(symfile.exists); - assert(symfile.isSymlink); - assert(!attrIsSymlink(getAttributes(symfile))); - assert(attrIsSymlink(getLinkAttributes(symfile))); - - assert(attrIsDir(getAttributes(symfile))); - assert(!attrIsDir(getLinkAttributes(symfile))); - - assert(!attrIsFile(getAttributes(symfile))); - assert(!attrIsFile(getLinkAttributes(symfile))); - } - - if (system_file.exists) - { - assert(!system_file.isSymlink); - - immutable symfile = deleteme ~ "_slink\0"; - scope(exit) if (symfile.exists) symfile.remove(); - - symlink(system_file, symfile); - - assert(symfile.exists); - assert(symfile.isSymlink); - assert(!attrIsSymlink(getAttributes(symfile))); - assert(attrIsSymlink(getLinkAttributes(symfile))); - - assert(!attrIsDir(getAttributes(symfile))); - assert(!attrIsDir(getLinkAttributes(symfile))); - - assert(attrIsFile(getAttributes(symfile))); - assert(!attrIsFile(getLinkAttributes(symfile))); - } -} - -version (Posix) @safe unittest -{ - static assert(__traits(compiles, - symlink(TestAliasedString(null), TestAliasedString(null)))); -} - - -/++ - $(BLUE This function is POSIX-Only.) - - Returns the path to the file pointed to by a symlink. Note that the - path could be either relative or absolute depending on the symlink. - If the path is relative, it's relative to the symlink, not the current - working directory. - - Throws: - $(LREF FileException) on error. - +/ -version (StdDdoc) string readLink(R)(R link) -if (isSomeFiniteCharInputRange!R || isConvertibleToString!R); -else version (Posix) string readLink(R)(R link) -if (isSomeFiniteCharInputRange!R || isConvertibleToString!R) -{ - static if (isConvertibleToString!R) - { - return readLink!(convertToString!R)(link); - } - else - { - import std.conv : to; - import std.exception : assumeUnique; - alias posixReadlink = core.sys.posix.unistd.readlink; - enum bufferLen = 2048; - enum maxCodeUnits = 6; - char[bufferLen] buffer; - const linkz = link.tempCString(); - auto size = () @trusted { - return posixReadlink(linkz, buffer.ptr, buffer.length); - } (); - cenforce(size != -1, to!string(link)); - - if (size <= bufferLen - maxCodeUnits) - return to!string(buffer[0 .. size]); - - auto dynamicBuffer = new char[](bufferLen * 3 / 2); - - foreach (i; 0 .. 10) - { - size = () @trusted { - return posixReadlink(linkz, dynamicBuffer.ptr, - dynamicBuffer.length); - } (); - cenforce(size != -1, to!string(link)); - - if (size <= dynamicBuffer.length - maxCodeUnits) - { - dynamicBuffer.length = size; - return () @trusted { - return assumeUnique(dynamicBuffer); - } (); - } - - dynamicBuffer.length = dynamicBuffer.length * 3 / 2; - } - - throw new FileException(to!string(link), "Path is too long to read."); - } -} - -version (Posix) @safe unittest -{ - import std.exception : assertThrown; - import std.string; - - foreach (file; [system_directory, system_file]) - { - if (file.exists) - { - immutable symfile = deleteme ~ "_slink\0"; - scope(exit) if (symfile.exists) symfile.remove(); - - symlink(file, symfile); - assert(readLink(symfile) == file, format("Failed file: %s", file)); - } - } - - assertThrown!FileException(readLink("/doesnotexist")); -} - -version (Posix) @safe unittest -{ - static assert(__traits(compiles, readLink(TestAliasedString("foo")))); -} - -version (Posix) @system unittest // input range of dchars -{ - mkdirRecurse(deleteme); - scope(exit) if (deleteme.exists) rmdirRecurse(deleteme); - write(deleteme ~ "/f", ""); - import std.range.interfaces : InputRange, inputRangeObject; - import std.utf : byChar; - immutable string link = deleteme ~ "/l"; - symlink("f", link); - InputRange!(ElementType!string) linkr = inputRangeObject(link); - alias R = typeof(linkr); - static assert(isInputRange!R); - static assert(!isForwardRange!R); - assert(readLink(linkr) == "f"); -} - - -/**************************************************** - * Get the current working directory. - * Throws: $(LREF FileException) on error. - */ -version (Windows) string getcwd() @trusted -{ - import std.conv : to; - import std.checkedint : checked; - /* GetCurrentDirectory's return value: - 1. function succeeds: the number of characters that are written to - the buffer, not including the terminating null character. - 2. function fails: zero - 3. the buffer (lpBuffer) is not large enough: the required size of - the buffer, in characters, including the null-terminating character. - */ - version (StdUnittest) - enum BUF_SIZE = 10; // trigger reallocation code - else - enum BUF_SIZE = 4096; // enough for most common case - wchar[BUF_SIZE] buffW = void; - immutable n = cenforce(GetCurrentDirectoryW(to!DWORD(buffW.length), buffW.ptr), - "getcwd"); - // we can do it because toUTFX always produces a fresh string - if (n < buffW.length) - { - return buffW[0 .. n].to!string; - } - else //staticBuff isn't enough - { - auto cn = checked(n); - auto ptr = cast(wchar*) malloc((cn * wchar.sizeof).get); - scope(exit) free(ptr); - immutable n2 = GetCurrentDirectoryW(cn.get, ptr); - cenforce(n2 && n2 < cn, "getcwd"); - return ptr[0 .. n2].to!string; - } -} -else version (Solaris) string getcwd() @trusted -{ - /* BUF_SIZE >= PATH_MAX */ - enum BUF_SIZE = 4096; - /* The user should be able to specify any size buffer > 0 */ - auto p = cenforce(core.sys.posix.unistd.getcwd(null, BUF_SIZE), - "cannot get cwd"); - scope(exit) core.stdc.stdlib.free(p); - return p[0 .. core.stdc.string.strlen(p)].idup; -} -else version (Posix) string getcwd() @trusted -{ - auto p = cenforce(core.sys.posix.unistd.getcwd(null, 0), - "cannot get cwd"); - scope(exit) core.stdc.stdlib.free(p); - return p[0 .. core.stdc.string.strlen(p)].idup; -} - -/// -@safe unittest -{ - auto s = getcwd(); - assert(s.length); -} - -/** - * Returns the full path of the current executable. - * - * Returns: - * The path of the executable as a `string`. - * - * Throws: - * $(REF1 Exception, object) - */ -@trusted string thisExePath() -{ - version (Darwin) - { - import core.sys.darwin.mach.dyld : _NSGetExecutablePath; - import core.sys.posix.stdlib : realpath; - import std.conv : to; - import std.exception : errnoEnforce; - - uint size; - - _NSGetExecutablePath(null, &size); // get the length of the path - auto buffer = new char[size]; - _NSGetExecutablePath(buffer.ptr, &size); - - auto absolutePath = realpath(buffer.ptr, null); // let the function allocate - - scope (exit) - { - if (absolutePath) - free(absolutePath); - } - - errnoEnforce(absolutePath); - return to!(string)(absolutePath); - } - else version (linux) - { - return readLink("/proc/self/exe"); - } - else version (Windows) - { - import std.conv : to; - import std.exception : enforce; - - wchar[MAX_PATH] buf; - wchar[] buffer = buf[]; - - while (true) - { - auto len = GetModuleFileNameW(null, buffer.ptr, cast(DWORD) buffer.length); - wenforce(len); - if (len != buffer.length) - return to!(string)(buffer[0 .. len]); - buffer.length *= 2; - } - } - else version (DragonFlyBSD) - { - import core.sys.dragonflybsd.sys.sysctl : sysctl, CTL_KERN, KERN_PROC, KERN_PROC_PATHNAME; - import std.exception : errnoEnforce, assumeUnique; - - int[4] mib = [CTL_KERN, KERN_PROC, KERN_PROC_PATHNAME, -1]; - size_t len; - - auto result = sysctl(mib.ptr, mib.length, null, &len, null, 0); // get the length of the path - errnoEnforce(result == 0); - - auto buffer = new char[len - 1]; - result = sysctl(mib.ptr, mib.length, buffer.ptr, &len, null, 0); - errnoEnforce(result == 0); - - return buffer.assumeUnique; - } - else version (FreeBSD) - { - import core.sys.freebsd.sys.sysctl : sysctl, CTL_KERN, KERN_PROC, KERN_PROC_PATHNAME; - import std.exception : errnoEnforce, assumeUnique; - - int[4] mib = [CTL_KERN, KERN_PROC, KERN_PROC_PATHNAME, -1]; - size_t len; - - auto result = sysctl(mib.ptr, mib.length, null, &len, null, 0); // get the length of the path - errnoEnforce(result == 0); - - auto buffer = new char[len - 1]; - result = sysctl(mib.ptr, mib.length, buffer.ptr, &len, null, 0); - errnoEnforce(result == 0); - - return buffer.assumeUnique; - } - else version (NetBSD) - { - import core.sys.netbsd.sys.sysctl : sysctl, CTL_KERN, KERN_PROC_ARGS, KERN_PROC_PATHNAME; - import std.exception : errnoEnforce, assumeUnique; - - int[4] mib = [CTL_KERN, KERN_PROC_ARGS, -1, KERN_PROC_PATHNAME]; - size_t len; - - auto result = sysctl(mib.ptr, mib.length, null, &len, null, 0); // get the length of the path - errnoEnforce(result == 0); - - auto buffer = new char[len - 1]; - result = sysctl(mib.ptr, mib.length, buffer.ptr, &len, null, 0); - errnoEnforce(result == 0); - - return buffer.assumeUnique; - } - else version (OpenBSD) - { - import core.sys.openbsd.sys.sysctl : sysctl, CTL_KERN, KERN_PROC_ARGS, KERN_PROC_ARGV; - import core.sys.posix.unistd : getpid; - import std.conv : to; - import std.exception : enforce, errnoEnforce; - import std.process : searchPathFor; - - int[4] mib = [CTL_KERN, KERN_PROC_ARGS, getpid(), KERN_PROC_ARGV]; - size_t len; - - auto result = sysctl(mib.ptr, mib.length, null, &len, null, 0); - errnoEnforce(result == 0); - - auto argv = new char*[len - 1]; - result = sysctl(mib.ptr, mib.length, argv.ptr, &len, null, 0); - errnoEnforce(result == 0); - - auto argv0 = argv[0]; - if (*argv0 == '/' || *argv0 == '.') - { - import core.sys.posix.stdlib : realpath; - auto absolutePath = realpath(argv0, null); - scope (exit) - { - if (absolutePath) - free(absolutePath); - } - errnoEnforce(absolutePath); - return to!(string)(absolutePath); - } - else - { - auto absolutePath = searchPathFor(to!string(argv0)); - errnoEnforce(absolutePath); - return absolutePath; - } - } - else version (Solaris) - { - import core.sys.posix.unistd : getpid; - import std.string : format; - - // Only Solaris 10 and later - return readLink(format("/proc/%d/path/a.out", getpid())); - } - else version (Hurd) - { - return readLink("/proc/self/exe"); - } - else - static assert(0, "thisExePath is not supported on this platform"); -} - -/// -@safe unittest -{ - import std.path : isAbsolute; - auto path = thisExePath(); - - assert(path.exists); - assert(path.isAbsolute); - assert(path.isFile); -} - -version (StdDdoc) -{ - /++ - Info on a file, similar to what you'd get from stat on a POSIX system. - +/ - struct DirEntry - { - @safe: - /++ - Constructs a `DirEntry` for the given file (or directory). - - Params: - path = The file (or directory) to get a DirEntry for. - - Throws: - $(LREF FileException) if the file does not exist. - +/ - this(return scope string path); - - version (Windows) - { - private this(string path, in WIN32_FIND_DATAW *fd); - } - else version (Posix) - { - private this(string path, core.sys.posix.dirent.dirent* fd); - } - - /++ - Returns the path to the file represented by this `DirEntry`. - -Example: --------------------- -auto de1 = DirEntry("/etc/fonts/fonts.conf"); -assert(de1.name == "/etc/fonts/fonts.conf"); - -auto de2 = DirEntry("/usr/share/include"); -assert(de2.name == "/usr/share/include"); --------------------- - +/ - @property string name() const return scope; - - - /++ - Returns whether the file represented by this `DirEntry` is a - directory. - -Example: --------------------- -auto de1 = DirEntry("/etc/fonts/fonts.conf"); -assert(!de1.isDir); - -auto de2 = DirEntry("/usr/share/include"); -assert(de2.isDir); --------------------- - +/ - @property bool isDir() scope; - - - /++ - Returns whether the file represented by this `DirEntry` is a file. - - On Windows, if a file is not a directory, then it's a file. So, - either `isFile` or `isDir` will return `true`. - - On POSIX systems, if `isFile` is `true`, that indicates that - the file is a regular file (e.g. not a block not device). So, on - POSIX systems, it's possible for both `isFile` and `isDir` to - be `false` for a particular file (in which case, it's a special - file). You can use `attributes` or `statBuf` to get more - information about a special file (see the stat man page for more - details). - -Example: --------------------- -auto de1 = DirEntry("/etc/fonts/fonts.conf"); -assert(de1.isFile); - -auto de2 = DirEntry("/usr/share/include"); -assert(!de2.isFile); --------------------- - +/ - @property bool isFile() scope; - - /++ - Returns whether the file represented by this `DirEntry` is a - symbolic link. - - On Windows, return `true` when the file is either a symbolic - link or a junction point. - +/ - @property bool isSymlink() scope; - - /++ - Returns the size of the file represented by this `DirEntry` - in bytes. - +/ - @property ulong size() scope; - - /++ - $(BLUE This function is Windows-Only.) - - Returns the creation time of the file represented by this - `DirEntry`. - +/ - @property SysTime timeCreated() const scope; - - /++ - Returns the time that the file represented by this `DirEntry` was - last accessed. - - Note that many file systems do not update the access time for files - (generally for performance reasons), so there's a good chance that - `timeLastAccessed` will return the same value as - `timeLastModified`. - +/ - @property SysTime timeLastAccessed() scope; - - /++ - Returns the time that the file represented by this `DirEntry` was - last modified. - +/ - @property SysTime timeLastModified() scope; - - /++ - $(BLUE This function is POSIX-Only.) - - Returns the time that the file represented by this `DirEntry` was - last changed (not only in contents, but also in permissions or ownership). - +/ - @property SysTime timeStatusChanged() const scope; - - /++ - Returns the _attributes of the file represented by this `DirEntry`. - - Note that the file _attributes on Windows and POSIX systems are - completely different. On, Windows, they're what is returned by - `GetFileAttributes` - $(HTTP msdn.microsoft.com/en-us/library/aa364944(v=vs.85).aspx, GetFileAttributes) - Whereas, an POSIX systems, they're the `st_mode` value which is - part of the `stat` struct gotten by calling `stat`. - - On POSIX systems, if the file represented by this `DirEntry` is a - symbolic link, then _attributes are the _attributes of the file - pointed to by the symbolic link. - +/ - @property uint attributes() scope; - - /++ - On POSIX systems, if the file represented by this `DirEntry` is a - symbolic link, then `linkAttributes` are the attributes of the - symbolic link itself. Otherwise, `linkAttributes` is identical to - `attributes`. - - On Windows, `linkAttributes` is identical to `attributes`. It - exists on Windows so that you don't have to special-case code for - Windows when dealing with symbolic links. - +/ - @property uint linkAttributes() scope; - - version (Windows) - alias stat_t = void*; - - /++ - $(BLUE This function is POSIX-Only.) - - The `stat` struct gotten from calling `stat`. - +/ - @property stat_t statBuf() scope; - } -} -else version (Windows) -{ - struct DirEntry - { - @safe: - public: - alias name this; - - this(return scope string path) - { - import std.datetime.systime : FILETIMEToSysTime; - - if (!path.exists()) - throw new FileException(path, "File does not exist"); - - _name = path; - - with (getFileAttributesWin(path)) - { - _size = makeUlong(nFileSizeLow, nFileSizeHigh); - _timeCreated = FILETIMEToSysTime(&ftCreationTime); - _timeLastAccessed = FILETIMEToSysTime(&ftLastAccessTime); - _timeLastModified = FILETIMEToSysTime(&ftLastWriteTime); - _attributes = dwFileAttributes; - } - } - - private this(string path, WIN32_FIND_DATAW *fd) @trusted - { - import core.stdc.wchar_ : wcslen; - import std.conv : to; - import std.datetime.systime : FILETIMEToSysTime; - import std.path : buildPath; - - fd.cFileName[$ - 1] = 0; - - size_t clength = wcslen(&fd.cFileName[0]); - _name = buildPath(path, fd.cFileName[0 .. clength].to!string); - _size = (cast(ulong) fd.nFileSizeHigh << 32) | fd.nFileSizeLow; - _timeCreated = FILETIMEToSysTime(&fd.ftCreationTime); - _timeLastAccessed = FILETIMEToSysTime(&fd.ftLastAccessTime); - _timeLastModified = FILETIMEToSysTime(&fd.ftLastWriteTime); - _attributes = fd.dwFileAttributes; - } - - @property string name() const pure nothrow return scope - { - return _name; - } - - @property bool isDir() const pure nothrow scope - { - return (attributes & FILE_ATTRIBUTE_DIRECTORY) != 0; - } - - @property bool isFile() const pure nothrow scope - { - //Are there no options in Windows other than directory and file? - //If there are, then this probably isn't the best way to determine - //whether this DirEntry is a file or not. - return !isDir; - } - - @property bool isSymlink() const pure nothrow scope - { - return (attributes & FILE_ATTRIBUTE_REPARSE_POINT) != 0; - } - - @property ulong size() const pure nothrow scope - { - return _size; - } - - @property SysTime timeCreated() const pure nothrow return scope - { - return cast(SysTime)_timeCreated; - } - - @property SysTime timeLastAccessed() const pure nothrow return scope - { - return cast(SysTime)_timeLastAccessed; - } - - @property SysTime timeLastModified() const pure nothrow return scope - { - return cast(SysTime)_timeLastModified; - } - - @property uint attributes() const pure nothrow scope - { - return _attributes; - } - - @property uint linkAttributes() const pure nothrow scope - { - return _attributes; - } - - private: - string _name; /// The file or directory represented by this DirEntry. - - SysTime _timeCreated; /// The time when the file was created. - SysTime _timeLastAccessed; /// The time when the file was last accessed. - SysTime _timeLastModified; /// The time when the file was last modified. - - ulong _size; /// The size of the file in bytes. - uint _attributes; /// The file attributes from WIN32_FIND_DATAW. - } -} -else version (Posix) -{ - struct DirEntry - { - @safe: - public: - alias name this; - - this(return scope string path) - { - if (!path.exists) - throw new FileException(path, "File does not exist"); - - _name = path; - - _didLStat = false; - _didStat = false; - _dTypeSet = false; - } - - private this(string path, core.sys.posix.dirent.dirent* fd) @safe - { - import std.path : buildPath; - - static if (is(typeof(fd.d_namlen))) - immutable len = fd.d_namlen; - else - immutable len = (() @trusted => core.stdc.string.strlen(fd.d_name.ptr))(); - - _name = buildPath(path, (() @trusted => fd.d_name.ptr[0 .. len])()); - - _didLStat = false; - _didStat = false; - - //fd_d_type doesn't work for all file systems, - //in which case the result is DT_UNKOWN. But we - //can determine the correct type from lstat, so - //we'll only set the dtype here if we could - //correctly determine it (not lstat in the case - //of DT_UNKNOWN in case we don't ever actually - //need the dtype, thus potentially avoiding the - //cost of calling lstat). - static if (__traits(compiles, fd.d_type != DT_UNKNOWN)) - { - if (fd.d_type != DT_UNKNOWN) - { - _dType = fd.d_type; - _dTypeSet = true; - } - else - _dTypeSet = false; - } - else - { - // e.g. Solaris does not have the d_type member - _dTypeSet = false; - } - } - - @property string name() const pure nothrow return scope - { - return _name; - } - - @property bool isDir() scope - { - _ensureStatOrLStatDone(); - - return (_statBuf.st_mode & S_IFMT) == S_IFDIR; - } - - @property bool isFile() scope - { - _ensureStatOrLStatDone(); - - return (_statBuf.st_mode & S_IFMT) == S_IFREG; - } - - @property bool isSymlink() scope - { - _ensureLStatDone(); - - return (_lstatMode & S_IFMT) == S_IFLNK; - } - - @property ulong size() scope - { - _ensureStatDone(); - return _statBuf.st_size; - } - - @property SysTime timeStatusChanged() scope - { - _ensureStatDone(); - - return statTimeToStdTime!'c'(_statBuf); - } - - @property SysTime timeLastAccessed() scope - { - _ensureStatDone(); - - return statTimeToStdTime!'a'(_statBuf); - } - - @property SysTime timeLastModified() scope - { - _ensureStatDone(); - - return statTimeToStdTime!'m'(_statBuf); - } - - @property uint attributes() scope - { - _ensureStatDone(); - - return _statBuf.st_mode; - } - - @property uint linkAttributes() scope - { - _ensureLStatDone(); - - return _lstatMode; - } - - @property stat_t statBuf() scope - { - _ensureStatDone(); - - return _statBuf; - } - - private: - /++ - This is to support lazy evaluation, because doing stat's is - expensive and not always needed. - +/ - void _ensureStatDone() @trusted scope - { - import std.exception : enforce; - - if (_didStat) - return; - - enforce(stat(_name.tempCString(), &_statBuf) == 0, - "Failed to stat file `" ~ _name ~ "'"); - - _didStat = true; - } - - /++ - This is to support lazy evaluation, because doing stat's is - expensive and not always needed. - - Try both stat and lstat for isFile and isDir - to detect broken symlinks. - +/ - void _ensureStatOrLStatDone() @trusted scope - { - if (_didStat) - return; - - if (stat(_name.tempCString(), &_statBuf) != 0) - { - _ensureLStatDone(); - - _statBuf = stat_t.init; - _statBuf.st_mode = S_IFLNK; - } - else - { - _didStat = true; - } - } - - /++ - This is to support lazy evaluation, because doing stat's is - expensive and not always needed. - +/ - void _ensureLStatDone() @trusted scope - { - import std.exception : enforce; - - if (_didLStat) - return; - - stat_t statbuf = void; - enforce(lstat(_name.tempCString(), &statbuf) == 0, - "Failed to stat file `" ~ _name ~ "'"); - - _lstatMode = statbuf.st_mode; - - _dTypeSet = true; - _didLStat = true; - } - - string _name; /// The file or directory represented by this DirEntry. - - stat_t _statBuf = void; /// The result of stat(). - uint _lstatMode; /// The stat mode from lstat(). - ubyte _dType; /// The type of the file. - - bool _didLStat = false; /// Whether lstat() has been called for this DirEntry. - bool _didStat = false; /// Whether stat() has been called for this DirEntry. - bool _dTypeSet = false; /// Whether the dType of the file has been set. - } -} - -@system unittest -{ - version (Windows) - { - if ("C:\\Program Files\\".exists) - { - auto de = DirEntry("C:\\Program Files\\"); - assert(!de.isFile); - assert(de.isDir); - assert(!de.isSymlink); - } - - if ("C:\\Users\\".exists && "C:\\Documents and Settings\\".exists) - { - auto de = DirEntry("C:\\Documents and Settings\\"); - assert(de.isSymlink); - } - - if ("C:\\Windows\\system.ini".exists) - { - auto de = DirEntry("C:\\Windows\\system.ini"); - assert(de.isFile); - assert(!de.isDir); - assert(!de.isSymlink); - } - } - else version (Posix) - { - import std.exception : assertThrown; - - if (system_directory.exists) - { - { - auto de = DirEntry(system_directory); - assert(!de.isFile); - assert(de.isDir); - assert(!de.isSymlink); - } - - immutable symfile = deleteme ~ "_slink\0"; - scope(exit) if (symfile.exists) symfile.remove(); - - core.sys.posix.unistd.symlink(system_directory, symfile.ptr); - - { - auto de = DirEntry(symfile); - assert(!de.isFile); - assert(de.isDir); - assert(de.isSymlink); - } - - symfile.remove(); - core.sys.posix.unistd.symlink((deleteme ~ "_broken_symlink\0").ptr, symfile.ptr); - - { - // https://issues.dlang.org/show_bug.cgi?id=8298 - DirEntry de = DirEntry(symfile); - - assert(!de.isFile); - assert(!de.isDir); - assert(de.isSymlink); - assertThrown(de.size); - assertThrown(de.timeStatusChanged); - assertThrown(de.timeLastAccessed); - assertThrown(de.timeLastModified); - assertThrown(de.attributes); - assertThrown(de.statBuf); - assert(symfile.exists); - symfile.remove(); - } - } - - if (system_file.exists) - { - auto de = DirEntry(system_file); - assert(de.isFile); - assert(!de.isDir); - assert(!de.isSymlink); - } - } -} - -alias PreserveAttributes = Flag!"preserveAttributes"; - -version (StdDdoc) -{ - /// Defaults to `Yes.preserveAttributes` on Windows, and the opposite on all other platforms. - PreserveAttributes preserveAttributesDefault; -} -else version (Windows) -{ - enum preserveAttributesDefault = Yes.preserveAttributes; -} -else -{ - enum preserveAttributesDefault = No.preserveAttributes; -} - -/*************************************************** -Copy file `from` _to file `to`. File timestamps are preserved. -File attributes are preserved, if `preserve` equals `Yes.preserveAttributes`. -On Windows only `Yes.preserveAttributes` (the default on Windows) is supported. -If the target file exists, it is overwritten. - -Params: - from = string or range of characters representing the existing file name - to = string or range of characters representing the target file name - preserve = whether to _preserve the file attributes - -Throws: $(LREF FileException) on error. - */ -void copy(RF, RT)(RF from, RT to, PreserveAttributes preserve = preserveAttributesDefault) -if (isSomeFiniteCharInputRange!RF && !isConvertibleToString!RF && - isSomeFiniteCharInputRange!RT && !isConvertibleToString!RT) -{ - // Place outside of @trusted block - auto fromz = from.tempCString!FSChar(); - auto toz = to.tempCString!FSChar(); - - static if (isNarrowString!RF && is(immutable ElementEncodingType!RF == immutable char)) - alias f = from; - else - enum string f = null; - - static if (isNarrowString!RT && is(immutable ElementEncodingType!RT == immutable char)) - alias t = to; - else - enum string t = null; - - copyImpl(f, t, fromz, toz, preserve); -} - -/// ditto -void copy(RF, RT)(auto ref RF from, auto ref RT to, PreserveAttributes preserve = preserveAttributesDefault) -if (isConvertibleToString!RF || isConvertibleToString!RT) -{ - import std.meta : staticMap; - alias Types = staticMap!(convertToString, RF, RT); - copy!Types(from, to, preserve); -} - -/// -@safe unittest -{ - auto source = deleteme ~ "source"; - auto target = deleteme ~ "target"; - auto targetNonExistent = deleteme ~ "target2"; - - scope(exit) source.remove, target.remove, targetNonExistent.remove; - - source.write("source"); - target.write("target"); - - assert(target.readText == "target"); - - source.copy(target); - assert(target.readText == "source"); - - source.copy(targetNonExistent); - assert(targetNonExistent.readText == "source"); -} - -// https://issues.dlang.org/show_bug.cgi?id=15319 -@safe unittest -{ - assert(__traits(compiles, copy("from.txt", "to.txt"))); -} - -private void copyImpl(scope const(char)[] f, scope const(char)[] t, - scope const(FSChar)* fromz, scope const(FSChar)* toz, - PreserveAttributes preserve) @trusted -{ - version (Windows) - { - assert(preserve == Yes.preserveAttributes); - immutable result = CopyFileW(fromz, toz, false); - if (!result) - { - import core.stdc.wchar_ : wcslen; - import std.conv : to; - import std.format : format; - - /++ - Reference resources: https://docs.microsoft.com/en-us/windows/win32/api/winbase/nf-winbase-copyfilew - Because OS copyfilew handles both source and destination paths, - the GetLastError does not accurately locate whether the error is for the source or destination. - +/ - if (!f) - f = to!(typeof(f))(fromz[0 .. wcslen(fromz)]); - if (!t) - t = to!(typeof(t))(toz[0 .. wcslen(toz)]); - - throw new FileException(format!"Copy from %s to %s"(f, t)); - } - } - else version (Posix) - { - static import core.stdc.stdio; - import std.conv : to, octal; - - immutable fdr = core.sys.posix.fcntl.open(fromz, O_RDONLY); - cenforce(fdr != -1, f, fromz); - scope(exit) core.sys.posix.unistd.close(fdr); - - stat_t statbufr = void; - cenforce(fstat(fdr, &statbufr) == 0, f, fromz); - //cenforce(core.sys.posix.sys.stat.fstat(fdr, &statbufr) == 0, f, fromz); - - immutable fdw = core.sys.posix.fcntl.open(toz, - O_CREAT | O_WRONLY, octal!666); - cenforce(fdw != -1, t, toz); - { - scope(failure) core.sys.posix.unistd.close(fdw); - - stat_t statbufw = void; - cenforce(fstat(fdw, &statbufw) == 0, t, toz); - if (statbufr.st_dev == statbufw.st_dev && statbufr.st_ino == statbufw.st_ino) - throw new FileException(t, "Source and destination are the same file"); - } - - scope(failure) core.stdc.stdio.remove(toz); - { - scope(failure) core.sys.posix.unistd.close(fdw); - cenforce(ftruncate(fdw, 0) == 0, t, toz); - - auto BUFSIZ = 4096u * 16; - auto buf = core.stdc.stdlib.malloc(BUFSIZ); - if (!buf) - { - BUFSIZ = 4096; - buf = core.stdc.stdlib.malloc(BUFSIZ); - if (!buf) - { - import core.exception : onOutOfMemoryError; - onOutOfMemoryError(); - } - } - scope(exit) core.stdc.stdlib.free(buf); - - for (auto size = statbufr.st_size; size; ) - { - immutable toxfer = (size > BUFSIZ) ? BUFSIZ : cast(size_t) size; - cenforce( - core.sys.posix.unistd.read(fdr, buf, toxfer) == toxfer - && core.sys.posix.unistd.write(fdw, buf, toxfer) == toxfer, - f, fromz); - assert(size >= toxfer); - size -= toxfer; - } - if (preserve) - cenforce(fchmod(fdw, to!mode_t(statbufr.st_mode)) == 0, f, fromz); - } - - cenforce(core.sys.posix.unistd.close(fdw) != -1, f, fromz); - - setTimesImpl(t, toz, statbufr.statTimeToStdTime!'a', statbufr.statTimeToStdTime!'m'); - } -} - -// https://issues.dlang.org/show_bug.cgi?id=14817 -@safe unittest -{ - import std.algorithm, std.file; - auto t1 = deleteme, t2 = deleteme~"2"; - scope(exit) foreach (t; [t1, t2]) if (t.exists) t.remove(); - write(t1, "11"); - copy(t1, t2); - assert(readText(t2) == "11"); - write(t1, "2"); - copy(t1, t2); - assert(readText(t2) == "2"); - - import std.utf : byChar; - copy(t1.byChar, t2.byChar); - assert(readText(t2.byChar) == "2"); - -// https://issues.dlang.org/show_bug.cgi?id=20370 - version (Windows) - assert(t1.timeLastModified == t2.timeLastModified); - else static if (is(typeof(&utimensat)) || is(typeof(&setattrlist))) - assert(t1.timeLastModified == t2.timeLastModified); - else - assert(abs(t1.timeLastModified - t2.timeLastModified) < dur!"usecs"(1)); -} - -// https://issues.dlang.org/show_bug.cgi?id=11434 -@safe version (Posix) @safe unittest -{ - import std.conv : octal; - auto t1 = deleteme, t2 = deleteme~"2"; - scope(exit) foreach (t; [t1, t2]) if (t.exists) t.remove(); - write(t1, "1"); - setAttributes(t1, octal!767); - copy(t1, t2, Yes.preserveAttributes); - assert(readText(t2) == "1"); - assert(getAttributes(t2) == octal!100767); -} - -// https://issues.dlang.org/show_bug.cgi?id=15865 -@safe unittest -{ - import std.exception : assertThrown; - auto t = deleteme; - write(t, "a"); - scope(exit) t.remove(); - assertThrown!FileException(copy(t, t)); - assert(readText(t) == "a"); -} - -// https://issues.dlang.org/show_bug.cgi?id=19834 -version (Windows) @safe unittest -{ - import std.exception : collectException; - import std.algorithm.searching : startsWith; - import std.format : format; - - auto f = deleteme; - auto t = f ~ "2"; - auto ex = collectException(copy(f, t)); - assert(ex.msg.startsWith(format!"Copy from %s to %s"(f, t))); -} - -/++ - Remove directory and all of its content and subdirectories, - recursively. - - Params: - pathname = the path of the directory to completely remove - de = The $(LREF DirEntry) to remove - - Throws: - $(LREF FileException) if there is an error (including if the given - file is not a directory). - +/ -void rmdirRecurse(scope const(char)[] pathname) @safe -{ - //No references to pathname will be kept after rmdirRecurse, - //so the cast is safe - rmdirRecurse(DirEntry((() @trusted => cast(string) pathname)())); -} - -/// ditto -void rmdirRecurse(ref scope DirEntry de) @safe -{ - if (!de.isDir) - throw new FileException(de.name, "Not a directory"); - - if (de.isSymlink) - { - version (Windows) - rmdir(de.name); - else - remove(de.name); - } - else - { - // dirEntries is @system without DIP1000 because it uses - // a DirIterator with a SafeRefCounted variable, but here, no - // references to the payload are escaped to the outside, so this should - // be @trusted - () @trusted { - // all children, recursively depth-first - foreach (DirEntry e; dirEntries(de.name, SpanMode.depth, false)) - { - attrIsDir(e.linkAttributes) ? rmdir(e.name) : remove(e.name); - } - }(); - - // the dir itself - rmdir(de.name); - } -} -///ditto -//Note, without this overload, passing an RValue DirEntry still works, but -//actually fully reconstructs a DirEntry inside the -//"rmdirRecurse(in char[] pathname)" implementation. That is needlessly -//expensive. -//A DirEntry is a bit big (72B), so keeping the "by ref" signature is desirable. -void rmdirRecurse(scope DirEntry de) @safe -{ - rmdirRecurse(de); -} - -/// -@system unittest -{ - import std.path : buildPath; - - auto dir = deleteme.buildPath("a", "b", "c"); - - dir.mkdirRecurse; - assert(dir.exists); - - deleteme.rmdirRecurse; - assert(!dir.exists); - assert(!deleteme.exists); -} - -version (Windows) @system unittest -{ - import std.exception : enforce; - auto d = deleteme ~ r".dir\a\b\c\d\e\f\g"; - mkdirRecurse(d); - rmdirRecurse(deleteme ~ ".dir"); - enforce(!exists(deleteme ~ ".dir")); -} - -version (Posix) @system unittest -{ - import std.exception : enforce, collectException; - - collectException(rmdirRecurse(deleteme)); - auto d = deleteme~"/a/b/c/d/e/f/g"; - enforce(collectException(mkdir(d))); - mkdirRecurse(d); - core.sys.posix.unistd.symlink((deleteme~"/a/b/c\0").ptr, - (deleteme~"/link\0").ptr); - rmdirRecurse(deleteme~"/link"); - enforce(exists(d)); - rmdirRecurse(deleteme); - enforce(!exists(deleteme)); - - d = deleteme~"/a/b/c/d/e/f/g"; - mkdirRecurse(d); - const linkTarget = deleteme ~ "/link"; - symlink(deleteme ~ "/a/b/c", linkTarget); - rmdirRecurse(deleteme); - enforce(!exists(deleteme)); -} - -@safe unittest -{ - ubyte[] buf = new ubyte[10]; - buf[] = 3; - string unit_file = deleteme ~ "-unittest_write.tmp"; - if (exists(unit_file)) remove(unit_file); - write(unit_file, cast(void[]) buf); - void[] buf2 = read(unit_file); - assert(cast(void[]) buf == buf2); - - string unit2_file = deleteme ~ "-unittest_write2.tmp"; - copy(unit_file, unit2_file); - buf2 = read(unit2_file); - assert(cast(void[]) buf == buf2); - - remove(unit_file); - assert(!exists(unit_file)); - remove(unit2_file); - assert(!exists(unit2_file)); -} - -/** - * Dictates directory spanning policy for $(D_PARAM dirEntries) (see below). - */ -enum SpanMode -{ - /** Only spans one directory. */ - shallow, - /** Spans the directory in - $(HTTPS en.wikipedia.org/wiki/Tree_traversal#Post-order, - _depth-first $(B post)-order), i.e. the content of any - subdirectory is spanned before that subdirectory itself. Useful - e.g. when recursively deleting files. */ - depth, - /** Spans the directory in - $(HTTPS en.wikipedia.org/wiki/Tree_traversal#Pre-order, depth-first - $(B pre)-order), i.e. the content of any subdirectory is spanned - right after that subdirectory itself. - - Note that `SpanMode.breadth` will not result in all directory - members occurring before any subdirectory members, i.e. it is not - _true - $(HTTPS en.wikipedia.org/wiki/Tree_traversal#Breadth-first_search, - _breadth-first traversal). - */ - breadth, -} - -/// -@system unittest -{ - import std.algorithm.comparison : equal; - import std.algorithm.iteration : map; - import std.algorithm.sorting : sort; - import std.array : array; - import std.path : buildPath, relativePath; - - auto root = deleteme ~ "root"; - scope(exit) root.rmdirRecurse; - root.mkdir; - - root.buildPath("animals").mkdir; - root.buildPath("animals", "cat").mkdir; - - alias removeRoot = (return scope e) => e.relativePath(root); - - assert(root.dirEntries(SpanMode.depth).map!removeRoot.equal( - [buildPath("animals", "cat"), "animals"])); - - assert(root.dirEntries(SpanMode.breadth).map!removeRoot.equal( - ["animals", buildPath("animals", "cat")])); - - root.buildPath("plants").mkdir; - - assert(root.dirEntries(SpanMode.shallow).array.sort.map!removeRoot.equal( - ["animals", "plants"])); -} - -private struct DirIteratorImpl -{ - @safe: - SpanMode _mode; - // Whether we should follow symlinked directories while iterating. - // It also indicates whether we should avoid functions which call - // stat (since we should only need lstat in this case and it would - // be more efficient to not call stat in addition to lstat). - bool _followSymlink; - DirEntry _cur; - DirHandle[] _stack; - DirEntry[] _stashed; //used in depth first mode - - //stack helpers - void pushExtra(DirEntry de) - { - _stashed ~= de; - } - - //ditto - bool hasExtra() - { - return _stashed.length != 0; - } - - //ditto - DirEntry popExtra() - { - DirEntry de; - de = _stashed[$-1]; - _stashed.popBack(); - return de; - } - - version (Windows) - { - WIN32_FIND_DATAW _findinfo; - struct DirHandle - { - string dirpath; - HANDLE h; - } - - bool stepIn(string directory) @safe - { - import std.path : chainPath; - auto searchPattern = chainPath(directory, "*.*"); - - static auto trustedFindFirstFileW(typeof(searchPattern) pattern, scope WIN32_FIND_DATAW* findinfo) @trusted - { - return FindFirstFileW(pattern.tempCString!FSChar(), findinfo); - } - - HANDLE h = trustedFindFirstFileW(searchPattern, &_findinfo); - cenforce(h != INVALID_HANDLE_VALUE, directory); - _stack ~= DirHandle(directory, h); - return toNext(false, &_findinfo); - } - - bool next() - { - if (_stack.length == 0) - return false; - return toNext(true, &_findinfo); - } - - bool toNext(bool fetch, scope WIN32_FIND_DATAW* findinfo) @trusted - { - import core.stdc.wchar_ : wcscmp; - - if (fetch) - { - if (FindNextFileW(_stack[$-1].h, findinfo) == FALSE) - { - popDirStack(); - return false; - } - } - while (wcscmp(&findinfo.cFileName[0], ".") == 0 || - wcscmp(&findinfo.cFileName[0], "..") == 0) - if (FindNextFileW(_stack[$-1].h, findinfo) == FALSE) - { - popDirStack(); - return false; - } - _cur = DirEntry(_stack[$-1].dirpath, findinfo); - return true; - } - - void popDirStack() @trusted - { - assert(_stack.length != 0); - FindClose(_stack[$-1].h); - _stack.popBack(); - } - - void releaseDirStack() @trusted - { - foreach (d; _stack) - FindClose(d.h); - } - - bool mayStepIn() - { - return _followSymlink ? _cur.isDir : _cur.isDir && !_cur.isSymlink; - } - } - else version (Posix) - { - struct DirHandle - { - string dirpath; - DIR* h; - } - - bool stepIn(string directory) - { - static auto trustedOpendir(string dir) @trusted - { - return opendir(dir.tempCString()); - } - - auto h = directory.length ? trustedOpendir(directory) : trustedOpendir("."); - cenforce(h, directory); - _stack ~= (DirHandle(directory, h)); - return next(); - } - - bool next() @trusted - { - if (_stack.length == 0) - return false; - - for (dirent* fdata; (fdata = readdir(_stack[$-1].h)) != null; ) - { - // Skip "." and ".." - if (core.stdc.string.strcmp(&fdata.d_name[0], ".") && - core.stdc.string.strcmp(&fdata.d_name[0], "..")) - { - _cur = DirEntry(_stack[$-1].dirpath, fdata); - return true; - } - } - - popDirStack(); - return false; - } - - void popDirStack() @trusted - { - assert(_stack.length != 0); - closedir(_stack[$-1].h); - _stack.popBack(); - } - - void releaseDirStack() @trusted - { - foreach (d; _stack) - closedir(d.h); - } - - bool mayStepIn() - { - return _followSymlink ? _cur.isDir : attrIsDir(_cur.linkAttributes); - } - } - - this(R)(R pathname, SpanMode mode, bool followSymlink) - if (isSomeFiniteCharInputRange!R) - { - _mode = mode; - _followSymlink = followSymlink; - - static if (isNarrowString!R && is(immutable ElementEncodingType!R == immutable char)) - alias pathnameStr = pathname; - else - { - import std.array : array; - string pathnameStr = pathname.array; - } - if (stepIn(pathnameStr)) - { - if (_mode == SpanMode.depth) - while (mayStepIn()) - { - auto thisDir = _cur; - if (stepIn(_cur.name)) - { - pushExtra(thisDir); - } - else - break; - } - } - } - - @property bool empty() - { - return _stashed.length == 0 && _stack.length == 0; - } - - @property DirEntry front() - { - return _cur; - } - - void popFront() - { - switch (_mode) - { - case SpanMode.depth: - if (next()) - { - while (mayStepIn()) - { - auto thisDir = _cur; - if (stepIn(_cur.name)) - { - pushExtra(thisDir); - } - else - break; - } - } - else if (hasExtra()) - _cur = popExtra(); - break; - case SpanMode.breadth: - if (mayStepIn()) - { - if (!stepIn(_cur.name)) - while (!empty && !next()){} - } - else - while (!empty && !next()){} - break; - default: - next(); - } - } - - ~this() - { - releaseDirStack(); - } -} - -// Must be a template, because the destructor is unsafe or safe depending on -// whether `-preview=dip1000` is in use. Otherwise, linking errors would -// result. -struct _DirIterator(bool useDIP1000) -{ - static assert(useDIP1000 == dip1000Enabled, - "Please don't override useDIP1000 to disagree with compiler switch."); - -private: - SafeRefCounted!(DirIteratorImpl, RefCountedAutoInitialize.no) impl; - - this(string pathname, SpanMode mode, bool followSymlink) @trusted - { - impl = typeof(impl)(pathname, mode, followSymlink); - } -public: - @property bool empty() @trusted { return impl.empty; } - @property DirEntry front() @trusted { return impl.front; } - void popFront() @trusted { impl.popFront(); } -} - -// This has the client code to automatically use and link to the correct -// template instance -alias DirIterator = _DirIterator!dip1000Enabled; - -/++ - Returns an $(REF_ALTTEXT input range, isInputRange, std,range,primitives) - of `DirEntry` that lazily iterates a given directory, - also provides two ways of foreach iteration. The iteration variable can be of - type `string` if only the name is needed, or `DirEntry` - if additional details are needed. The span _mode dictates how the - directory is traversed. The name of each iterated directory entry - contains the absolute or relative _path (depending on _pathname). - - Note: The order of returned directory entries is as it is provided by the - operating system / filesystem, and may not follow any particular sorting. - - Params: - useDIP1000 = used to instantiate this function separately for code with - and without -preview=dip1000 compiler switch, because it - affects the ABI of this function. Set automatically - - don't touch. - - path = The directory to iterate over. - If empty, the current directory will be iterated. - - pattern = Optional string with wildcards, such as $(RED - "*.d"). When present, it is used to filter the - results by their file name. The supported wildcard - strings are described under $(REF globMatch, - std,_path). - - mode = Whether the directory's sub-directories should be - iterated in depth-first post-order ($(LREF depth)), - depth-first pre-order ($(LREF breadth)), or not at all - ($(LREF shallow)). - - followSymlink = Whether symbolic links which point to directories - should be treated as directories and their contents - iterated over. - - Returns: - An $(REF_ALTTEXT input range, isInputRange,std,range,primitives) of - $(LREF DirEntry). - - Throws: - $(UL - $(LI $(LREF FileException) if the $(B path) directory does not exist or read permission is denied.) - $(LI $(LREF FileException) if $(B mode) is not `shallow` and a subdirectory cannot be read.) - ) - -Example: --------------------- -// Iterate a directory in depth -foreach (string name; dirEntries("destroy/me", SpanMode.depth)) -{ - remove(name); -} - -// Iterate the current directory in breadth -foreach (string name; dirEntries("", SpanMode.breadth)) -{ - writeln(name); -} - -// Iterate a directory and get detailed info about it -foreach (DirEntry e; dirEntries("dmd-testing", SpanMode.breadth)) -{ - writeln(e.name, "\t", e.size); -} - -// Iterate over all *.d files in current directory and all its subdirectories -auto dFiles = dirEntries("", SpanMode.depth).filter!(f => f.name.endsWith(".d")); -foreach (d; dFiles) - writeln(d.name); - -// Hook it up with std.parallelism to compile them all in parallel: -foreach (d; parallel(dFiles, 1)) //passes by 1 file to each thread -{ - string cmd = "dmd -c " ~ d.name; - writeln(cmd); - std.process.executeShell(cmd); -} - -// Iterate over all D source files in current directory and all its -// subdirectories -auto dFiles = dirEntries("","*.{d,di}",SpanMode.depth); -foreach (d; dFiles) - writeln(d.name); --------------------- -To handle subdirectories with denied read permission, use `SpanMode.shallow`: ---- -void scan(string path) -{ - foreach (DirEntry entry; dirEntries(path, SpanMode.shallow)) - { - try - { - writeln(entry.name); - if (entry.isDir) - scan(entry.name); - } - catch (FileException fe) { continue; } // ignore - } -} - -scan(""); ---- -+/ - -// For some reason, doing the same alias-to-a-template trick as with DirIterator -// does not work here. -auto dirEntries(bool useDIP1000 = dip1000Enabled) - (string path, SpanMode mode, bool followSymlink = true) -{ - return _DirIterator!useDIP1000(path, mode, followSymlink); -} - -/// Duplicate functionality of D1's `std.file.listdir()`: -@safe unittest -{ - string[] listdir(string pathname) - { - import std.algorithm.iteration : map, filter; - import std.array : array; - import std.path : baseName; - - return dirEntries(pathname, SpanMode.shallow) - .filter!(a => a.isFile) - .map!((return a) => baseName(a.name)) - .array; - } - - // Can be safe only with -preview=dip1000 - @safe void main(string[] args) - { - import std.stdio : writefln; - - string[] files = listdir(args[1]); - writefln("%s", files); - } -} - -@system unittest -{ - import std.algorithm.comparison : equal; - import std.algorithm.iteration : map; - import std.algorithm.searching : startsWith; - import std.array : array; - import std.conv : to; - import std.path : buildPath, absolutePath; - import std.file : dirEntries; - import std.process : thisProcessID; - import std.range.primitives : walkLength; - - version (Android) - string testdir = deleteme; // This has to be an absolute path when - // called from a shared library on Android, - // ie an apk - else - string testdir = tempDir.buildPath("deleteme.dmd.unittest.std.file" ~ to!string(thisProcessID)); - mkdirRecurse(buildPath(testdir, "somedir")); - scope(exit) rmdirRecurse(testdir); - write(buildPath(testdir, "somefile"), null); - write(buildPath(testdir, "somedir", "somedeepfile"), null); - - // testing range interface - size_t equalEntries(string relpath, SpanMode mode) - { - import std.exception : enforce; - auto len = enforce(walkLength(dirEntries(absolutePath(relpath), mode))); - assert(walkLength(dirEntries(relpath, mode)) == len); - assert(equal( - map!((return a) => absolutePath(a.name))(dirEntries(relpath, mode)), - map!(a => a.name)(dirEntries(absolutePath(relpath), mode)))); - return len; - } - - assert(equalEntries(testdir, SpanMode.shallow) == 2); - assert(equalEntries(testdir, SpanMode.depth) == 3); - assert(equalEntries(testdir, SpanMode.breadth) == 3); - - // testing opApply - foreach (string name; dirEntries(testdir, SpanMode.breadth)) - { - //writeln(name); - assert(name.startsWith(testdir)); - } - foreach (DirEntry e; dirEntries(absolutePath(testdir), SpanMode.breadth)) - { - //writeln(name); - assert(e.isFile || e.isDir, e.name); - } - - // https://issues.dlang.org/show_bug.cgi?id=7264 - foreach (string name; dirEntries(testdir, "*.d", SpanMode.breadth)) - { - - } - foreach (entry; dirEntries(testdir, SpanMode.breadth)) - { - static assert(is(typeof(entry) == DirEntry)); - } - // https://issues.dlang.org/show_bug.cgi?id=7138 - auto a = array(dirEntries(testdir, SpanMode.shallow)); - - // https://issues.dlang.org/show_bug.cgi?id=11392 - auto dFiles = dirEntries(testdir, SpanMode.shallow); - foreach (d; dFiles){} - - // https://issues.dlang.org/show_bug.cgi?id=15146 - dirEntries("", SpanMode.shallow).walkLength(); -} - -/// Ditto -auto dirEntries(bool useDIP1000 = dip1000Enabled) - (string path, string pattern, SpanMode mode, - bool followSymlink = true) -{ - import std.algorithm.iteration : filter; - import std.path : globMatch, baseName; - - bool f(DirEntry de) { return globMatch(baseName(de.name), pattern); } - return filter!f(_DirIterator!useDIP1000(path, mode, followSymlink)); -} - -@safe unittest -{ - import std.stdio : writefln; - immutable dpath = deleteme ~ "_dir"; - immutable fpath = deleteme ~ "_file"; - immutable sdpath = deleteme ~ "_sdir"; - immutable sfpath = deleteme ~ "_sfile"; - scope(exit) - { - if (dpath.exists) rmdirRecurse(dpath); - if (fpath.exists) remove(fpath); - if (sdpath.exists) remove(sdpath); - if (sfpath.exists) remove(sfpath); - } - - mkdir(dpath); - write(fpath, "hello world"); - version (Posix) () @trusted - { - core.sys.posix.unistd.symlink((dpath ~ '\0').ptr, (sdpath ~ '\0').ptr); - core.sys.posix.unistd.symlink((fpath ~ '\0').ptr, (sfpath ~ '\0').ptr); - } (); - - static struct Flags { bool dir, file, link; } - auto tests = [dpath : Flags(true), fpath : Flags(false, true)]; - version (Posix) - { - tests[sdpath] = Flags(true, false, true); - tests[sfpath] = Flags(false, true, true); - } - - auto past = Clock.currTime() - 2.seconds; - auto future = past + 4.seconds; - - foreach (path, flags; tests) - { - auto de = DirEntry(path); - assert(de.name == path); - assert(de.isDir == flags.dir); - assert(de.isFile == flags.file); - assert(de.isSymlink == flags.link); - - assert(de.isDir == path.isDir); - assert(de.isFile == path.isFile); - assert(de.isSymlink == path.isSymlink); - assert(de.size == path.getSize()); - assert(de.attributes == getAttributes(path)); - assert(de.linkAttributes == getLinkAttributes(path)); - - scope(failure) writefln("[%s] [%s] [%s] [%s]", past, de.timeLastAccessed, de.timeLastModified, future); - assert(de.timeLastAccessed > past); - assert(de.timeLastAccessed < future); - assert(de.timeLastModified > past); - assert(de.timeLastModified < future); - - assert(attrIsDir(de.attributes) == flags.dir); - assert(attrIsDir(de.linkAttributes) == (flags.dir && !flags.link)); - assert(attrIsFile(de.attributes) == flags.file); - assert(attrIsFile(de.linkAttributes) == (flags.file && !flags.link)); - assert(!attrIsSymlink(de.attributes)); - assert(attrIsSymlink(de.linkAttributes) == flags.link); - - version (Windows) - { - assert(de.timeCreated > past); - assert(de.timeCreated < future); - } - else version (Posix) - { - assert(de.timeStatusChanged > past); - assert(de.timeStatusChanged < future); - assert(de.attributes == de.statBuf.st_mode); - } - } -} - -// Make sure that dirEntries does not butcher Unicode file names -// https://issues.dlang.org/show_bug.cgi?id=17962 -@safe unittest -{ - import std.algorithm.comparison : equal; - import std.algorithm.iteration : map; - import std.algorithm.sorting : sort; - import std.array : array; - import std.path : buildPath; - import std.uni : normalize; - - // The Unicode normalization is required to make the tests pass on Mac OS X. - auto dir = deleteme ~ normalize("𐐡"); - scope(exit) if (dir.exists) rmdirRecurse(dir); - mkdir(dir); - auto files = ["Hello World", - "Ma ChĂŠrie.jpeg", - "さいごの果実.txt"].map!(a => buildPath(dir, normalize(a)))().array(); - sort(files); - foreach (file; files) - write(file, "nothing"); - - auto result = dirEntries(dir, SpanMode.shallow).map!((return a) => a.name.normalize()).array(); - sort(result); - - assert(equal(files, result)); -} - -// https://issues.dlang.org/show_bug.cgi?id=21250 -@system unittest -{ - import std.exception : assertThrown; - assertThrown!Exception(dirEntries("237f5babd6de21f40915826699582e36", "*.bin", SpanMode.depth)); -} - -/** - * Reads a file line by line and parses the line into a single value or a - * $(REF Tuple, std,typecons) of values depending on the length of `Types`. - * The lines are parsed using the specified format string. The format string is - * passed to $(REF formattedRead, std,_format), and therefore must conform to the - * _format string specification outlined in $(MREF std, _format). - * - * Params: - * Types = the types that each of the elements in the line should be returned as - * filename = the name of the file to read - * format = the _format string to use when reading - * - * Returns: - * If only one type is passed, then an array of that type. Otherwise, an - * array of $(REF Tuple, std,typecons)s. - * - * Throws: - * `Exception` if the format string is malformed. Also, throws `Exception` - * if any of the lines in the file are not fully consumed by the call - * to $(REF formattedRead, std,_format). Meaning that no empty lines or lines - * with extra characters are allowed. - */ -Select!(Types.length == 1, Types[0][], Tuple!(Types)[]) -slurp(Types...)(string filename, scope const(char)[] format) -{ - import std.array : appender; - import std.conv : text; - import std.exception : enforce; - import std.format.read : formattedRead; - import std.stdio : File; - import std.string : stripRight; - - auto app = appender!(typeof(return))(); - ElementType!(typeof(return)) toAdd; - auto f = File(filename); - scope(exit) f.close(); - foreach (line; f.byLine()) - { - formattedRead(line, format, &toAdd); - enforce(line.stripRight("\r").empty, - text("Trailing characters at the end of line: `", line, - "'")); - app.put(toAdd); - } - return app.data; -} - -/// -@system unittest -{ - import std.typecons : tuple; - - scope(exit) - { - assert(exists(deleteme)); - remove(deleteme); - } - - write(deleteme, "12 12.25\n345 1.125"); // deleteme is the name of a temporary file - - // Load file; each line is an int followed by comma, whitespace and a - // double. - auto a = slurp!(int, double)(deleteme, "%s %s"); - assert(a.length == 2); - assert(a[0] == tuple(12, 12.25)); - assert(a[1] == tuple(345, 1.125)); -} - -@system unittest -{ - import std.typecons : tuple; - - scope(exit) - { - assert(exists(deleteme)); - remove(deleteme); - } - write(deleteme, "10\r\n20"); - assert(slurp!(int)(deleteme, "%d") == [10, 20]); -} - -/** -Returns the path to a directory for temporary files. -On POSIX platforms, it searches through the following list of directories -and returns the first one which is found to exist: -$(OL - $(LI The directory given by the `TMPDIR` environment variable.) - $(LI The directory given by the `TEMP` environment variable.) - $(LI The directory given by the `TMP` environment variable.) - $(LI `/tmp/`) - $(LI `/var/tmp/`) - $(LI `/usr/tmp/`) -) - -On all platforms, `tempDir` returns the current working directory on failure. - -The return value of the function is cached, so the procedures described -below will only be performed the first time the function is called. All -subsequent runs will return the same string, regardless of whether -environment variables and directory structures have changed in the -meantime. - -The POSIX `tempDir` algorithm is inspired by Python's -$(LINK2 http://docs.python.org/library/tempfile.html#tempfile.tempdir, `tempfile.tempdir`). - -Returns: - On Windows, this function returns the result of calling the Windows API function - $(LINK2 http://msdn.microsoft.com/en-us/library/windows/desktop/aa364992.aspx, `GetTempPath`). - - On POSIX platforms, it searches through the following list of directories - and returns the first one which is found to exist: - $(OL - $(LI The directory given by the `TMPDIR` environment variable.) - $(LI The directory given by the `TEMP` environment variable.) - $(LI The directory given by the `TMP` environment variable.) - $(LI `/tmp`) - $(LI `/var/tmp`) - $(LI `/usr/tmp`) - ) - - On all platforms, `tempDir` returns `"."` on failure, representing - the current working directory. -*/ -string tempDir() @trusted -{ - // We must check that the end of a path is not a separator, before adding another - // If we don't we end up with https://issues.dlang.org/show_bug.cgi?id=22738 - static string addSeparator(string input) - { - import std.path : dirSeparator; - import std.algorithm.searching : endsWith; - - // It is very rare a directory path will reach this point with a directory separator at the end - // However on OSX this can happen, so we must verify lest we break user code i.e. https://github.com/dlang/dub/pull/2208 - if (!input.endsWith(dirSeparator)) - return input ~ dirSeparator; - else - return input; - } - - static string cache; - if (cache is null) - { - version (Windows) - { - import std.conv : to; - // http://msdn.microsoft.com/en-us/library/windows/desktop/aa364992(v=vs.85).aspx - wchar[MAX_PATH + 2] buf; - DWORD len = GetTempPathW(buf.length, buf.ptr); - if (len) cache = buf[0 .. len].to!string; - } - else version (Posix) - { - import std.process : environment; - // This function looks through the list of alternative directories - // and returns the first one which exists and is a directory. - static string findExistingDir(T...)(lazy T alternatives) - { - foreach (dir; alternatives) - if (!dir.empty && exists(dir)) return addSeparator(dir); - return null; - } - - cache = findExistingDir(environment.get("TMPDIR"), - environment.get("TEMP"), - environment.get("TMP"), - "/tmp", - "/var/tmp", - "/usr/tmp"); - } - else static assert(false, "Unsupported platform"); - - if (cache is null) - { - cache = addSeparator(getcwd()); - } - } - return cache; -} - -/// -@safe unittest -{ - import std.ascii : letters; - import std.conv : to; - import std.path : buildPath; - import std.random : randomSample; - import std.utf : byCodeUnit; - - // random id with 20 letters - auto id = letters.byCodeUnit.randomSample(20).to!string; - auto myFile = tempDir.buildPath(id ~ "my_tmp_file"); - scope(exit) myFile.remove; - - myFile.write("hello"); - assert(myFile.readText == "hello"); -} - -@safe unittest -{ - import std.algorithm.searching : endsWith; - import std.path : dirSeparator; - assert(tempDir.endsWith(dirSeparator)); - - // https://issues.dlang.org/show_bug.cgi?id=22738 - assert(!tempDir.endsWith(dirSeparator ~ dirSeparator)); -} - -/** -Returns the available disk space based on a given path. -On Windows, `path` must be a directory; on POSIX systems, it can be a file or directory. - -Params: - path = on Windows, it must be a directory; on POSIX it can be a file or directory -Returns: - Available space in bytes - -Throws: - $(LREF FileException) in case of failure -*/ -ulong getAvailableDiskSpace(scope const(char)[] path) @safe -{ - version (Windows) - { - import core.sys.windows.winbase : GetDiskFreeSpaceExW; - import core.sys.windows.winnt : ULARGE_INTEGER; - import std.internal.cstring : tempCStringW; - - ULARGE_INTEGER freeBytesAvailable; - auto err = () @trusted { - return GetDiskFreeSpaceExW(path.tempCStringW(), &freeBytesAvailable, null, null); - } (); - cenforce(err != 0, "Cannot get available disk space"); - - return freeBytesAvailable.QuadPart; - } - else version (Posix) - { - import std.internal.cstring : tempCString; - - version (FreeBSD) - { - import core.sys.freebsd.sys.mount : statfs, statfs_t; - - statfs_t stats; - auto err = () @trusted { - return statfs(path.tempCString(), &stats); - } (); - cenforce(err == 0, "Cannot get available disk space"); - - return stats.f_bavail * stats.f_bsize; - } - else - { - import core.sys.posix.sys.statvfs : statvfs, statvfs_t; - - statvfs_t stats; - auto err = () @trusted { - return statvfs(path.tempCString(), &stats); - } (); - cenforce(err == 0, "Cannot get available disk space"); - - return stats.f_bavail * stats.f_frsize; - } - } - else static assert(0, "Unsupported platform"); -} - -/// -@safe unittest -{ - import std.exception : assertThrown; - - auto space = getAvailableDiskSpace("."); - assert(space > 0); - - assertThrown!FileException(getAvailableDiskSpace("ThisFileDoesNotExist123123")); -} diff --git a/phobos/std/format/internal/floats.d b/phobos/std/format/internal/floats.d deleted file mode 100644 index 88b9d22..0000000 --- a/phobos/std/format/internal/floats.d +++ /dev/null @@ -1,2936 +0,0 @@ -// Written in the D programming language. - -/* - Helper functions for formatting floating point numbers. - - Copyright: Copyright The D Language Foundation 2019 - - - License: $(HTTP boost.org/LICENSE_1_0.txt, Boost License 1.0). - - Authors: Bernhard Seckinger - - Source: $(PHOBOSSRC std/format/internal/floats.d) - */ - -module std.format.internal.floats; - -import std.format.spec : FormatSpec; - -// wrapper for unittests -private auto printFloat(T, Char)(const(T) val, FormatSpec!Char f) -if (is(T == float) || is(T == double) - || (is(T == real) && (T.mant_dig == double.mant_dig || T.mant_dig == 64))) -{ - import std.array : appender; - auto w = appender!string(); - - printFloat(w, val, f); - return w.data; -} - -/// Returns: whether `c` is a supported format specifier for floats -package(std.format) bool isFloatSpec(char c) nothrow @nogc pure @safe -{ - return c == 'a' || c == 'A' - || c == 'e' || c == 'E' - || c == 'f' || c == 'F' - || c == 'g' || c == 'G'; -} - -package(std.format) void printFloat(Writer, T, Char)(auto ref Writer w, const(T) val, FormatSpec!Char f) -if (is(T == float) || is(T == double) - || (is(T == real) && (T.mant_dig == double.mant_dig || T.mant_dig == 64))) -{ - import std.math.operations : extractBitpattern, FloatingPointBitpattern; - - auto bp = extractBitpattern(val); - - ulong mnt = bp.mantissa; - int exp = bp.exponent; - string sgn = bp.negative ? "-" : ""; - - if (sgn == "" && f.flPlus) sgn = "+"; - if (sgn == "" && f.flSpace) sgn = " "; - - assert(isFloatSpec(f.spec), "unsupported format specifier"); - bool is_upper = f.spec == 'A' || f.spec == 'E' || f.spec=='F' || f.spec=='G'; - - // special treatment for nan and inf - if (exp == T.max_exp) - { - import std.format.internal.write : writeAligned; - - f.flZero = false; - writeAligned(w, sgn, "", (mnt == 0) ? ( is_upper ? "INF" : "inf" ) : ( is_upper ? "NAN" : "nan" ), f); - return; - } - - final switch (f.spec) - { - case 'a': case 'A': - printFloatA(w, val, f, sgn, exp, mnt, is_upper); - break; - case 'e': case 'E': - printFloatE!false(w, val, f, sgn, exp, mnt, is_upper); - break; - case 'f': case 'F': - printFloatF!false(w, val, f, sgn, exp, mnt, is_upper); - break; - case 'g': case 'G': - printFloatG(w, val, f, sgn, exp, mnt, is_upper); - break; - } -} - -private void printFloatA(Writer, T, Char)(auto ref Writer w, const(T) val, - FormatSpec!Char f, string sgn, int exp, ulong mnt, bool is_upper) -if (is(T == float) || is(T == double) - || (is(T == real) && (T.mant_dig == double.mant_dig || T.mant_dig == 64))) -{ - import std.algorithm.comparison : max; - import std.format.internal.write : writeAligned, PrecisionType; - - char[3] prefix; - if (sgn != "") prefix[0] = sgn[0]; - prefix[1] = '0'; - prefix[2] = is_upper ? 'X' : 'x'; - - // print exponent - if (mnt == 0) - { - if (f.precision == f.UNSPECIFIED) - f.precision = 0; - writeAligned(w, prefix[1 - sgn.length .. $], "0", ".", is_upper ? "P+0" : "p+0", - f, PrecisionType.fractionalDigits); - return; - } - - // save integer part - char first = '0' + ((mnt >> (T.mant_dig - 1)) & 1); - mnt &= (1L << (T.mant_dig - 1)) - 1; - - static if (is(T == float) || (is(T == real) && T.mant_dig == 64)) - { - mnt <<= 1; // make mnt dividable by 4 - enum mant_len = T.mant_dig; - } - else - enum mant_len = T.mant_dig - 1; - static assert(mant_len % 4 == 0, "mantissa with wrong length"); - - // print full mantissa - char[(mant_len - 1) / 4 + 3] hex_mant; - size_t hex_mant_pos = 2; - size_t pos = mant_len; - - auto gap = 39 - 32 * is_upper; - while (pos >= 4 && (mnt & (((1L << (pos - 1)) - 1) << 1) + 1) != 0) - { - pos -= 4; - size_t tmp = (mnt >> pos) & 15; - // For speed reasons the better readable - // ... = tmp < 10 ? ('0' + tmp) : ((is_upper ? 'A' : 'a') + tmp - 10)) - // has been replaced with an expression without branches, doing the same - hex_mant[hex_mant_pos++] = cast(char) (tmp + gap * ((tmp + 6) >> 4) + '0'); - } - hex_mant[0] = first; - hex_mant[1] = '.'; - - if (f.precision == f.UNSPECIFIED) - f.precision = cast(int) hex_mant_pos - 2; - - auto exp_sgn = exp >= 0 ? '+' : '-'; - if (exp < 0) exp = -exp; - - static if (is(T == real) && real.mant_dig == 64) - enum max_exp_digits = 8; - else static if (is(T == float)) - enum max_exp_digits = 5; - else - enum max_exp_digits = 6; - - char[max_exp_digits] exp_str; - size_t exp_pos = max_exp_digits; - - do - { - exp_str[--exp_pos] = '0' + exp % 10; - exp /= 10; - } while (exp > 0); - - exp_str[--exp_pos] = exp_sgn; - exp_str[--exp_pos] = is_upper ? 'P' : 'p'; - - if (f.precision < hex_mant_pos - 2) - { - import std.format.internal.write : RoundingClass, round; - - RoundingClass rc; - - if (hex_mant[f.precision + 2] == '0') - rc = RoundingClass.ZERO; - else if (hex_mant[f.precision + 2] < '8') - rc = RoundingClass.LOWER; - else if (hex_mant[f.precision + 2] > '8') - rc = RoundingClass.UPPER; - else - rc = RoundingClass.FIVE; - - if (rc == RoundingClass.ZERO || rc == RoundingClass.FIVE) - { - foreach (i;f.precision + 3 .. hex_mant_pos) - { - if (hex_mant[i] > '0') - { - rc = rc == RoundingClass.ZERO ? RoundingClass.LOWER : RoundingClass.UPPER; - break; - } - } - } - - hex_mant_pos = f.precision + 2; - - round(hex_mant, 0, hex_mant_pos, rc, sgn == "-", is_upper ? 'F' : 'f'); - } - - writeAligned(w, prefix[1 - sgn.length .. $], hex_mant[0 .. 1], hex_mant[1 .. hex_mant_pos], - exp_str[exp_pos .. $], f, PrecisionType.fractionalDigits); -} - -@safe unittest -{ - auto f = FormatSpec!dchar(""); - f.spec = 'a'; - assert(printFloat(float.nan, f) == "nan"); - assert(printFloat(-float.nan, f) == "-nan"); - assert(printFloat(float.infinity, f) == "inf"); - assert(printFloat(-float.infinity, f) == "-inf"); - assert(printFloat(0.0f, f) == "0x0p+0"); - assert(printFloat(-0.0f, f) == "-0x0p+0"); - - assert(printFloat(double.nan, f) == "nan"); - assert(printFloat(-double.nan, f) == "-nan"); - assert(printFloat(double.infinity, f) == "inf"); - assert(printFloat(-double.infinity, f) == "-inf"); - assert(printFloat(0.0, f) == "0x0p+0"); - assert(printFloat(-0.0, f) == "-0x0p+0"); - - static if (real.mant_dig > 64) - { - pragma(msg, "printFloat tests disabled because of unsupported `real` format"); - } - else - { - assert(printFloat(real.nan, f) == "nan"); - assert(printFloat(-real.nan, f) == "-nan"); - assert(printFloat(real.infinity, f) == "inf"); - assert(printFloat(-real.infinity, f) == "-inf"); - assert(printFloat(0.0L, f) == "0x0p+0"); - assert(printFloat(-0.0L, f) == "-0x0p+0"); - } - - import std.math.operations : nextUp; - - assert(printFloat(nextUp(0.0f), f) == "0x0.000002p-126"); - assert(printFloat(float.epsilon, f) == "0x1p-23"); - assert(printFloat(float.min_normal, f) == "0x1p-126"); - assert(printFloat(float.max, f) == "0x1.fffffep+127"); - - assert(printFloat(nextUp(0.0), f) == "0x0.0000000000001p-1022"); - assert(printFloat(double.epsilon, f) == "0x1p-52"); - assert(printFloat(double.min_normal, f) == "0x1p-1022"); - assert(printFloat(double.max, f) == "0x1.fffffffffffffp+1023"); - - static if (real.mant_dig == 64) - { - assert(printFloat(nextUp(0.0L), f) == "0x0.0000000000000002p-16382"); - assert(printFloat(real.epsilon, f) == "0x1p-63"); - assert(printFloat(real.min_normal, f) == "0x1p-16382"); - assert(printFloat(real.max, f) == "0x1.fffffffffffffffep+16383"); - } - - import std.math.constants : E, PI, PI_2, PI_4, M_1_PI, M_2_PI, M_2_SQRTPI, - LN10, LN2, LOG2, LOG2E, LOG2T, LOG10E, SQRT2, SQRT1_2; - - assert(printFloat(cast(float) E, f) == "0x1.5bf0a8p+1"); - assert(printFloat(cast(float) PI, f) == "0x1.921fb6p+1"); - assert(printFloat(cast(float) PI_2, f) == "0x1.921fb6p+0"); - assert(printFloat(cast(float) PI_4, f) == "0x1.921fb6p-1"); - assert(printFloat(cast(float) M_1_PI, f) == "0x1.45f306p-2"); - assert(printFloat(cast(float) M_2_PI, f) == "0x1.45f306p-1"); - assert(printFloat(cast(float) M_2_SQRTPI, f) == "0x1.20dd76p+0"); - assert(printFloat(cast(float) LN10, f) == "0x1.26bb1cp+1"); - assert(printFloat(cast(float) LN2, f) == "0x1.62e43p-1"); - assert(printFloat(cast(float) LOG2, f) == "0x1.344136p-2"); - assert(printFloat(cast(float) LOG2E, f) == "0x1.715476p+0"); - assert(printFloat(cast(float) LOG2T, f) == "0x1.a934fp+1"); - assert(printFloat(cast(float) LOG10E, f) == "0x1.bcb7b2p-2"); - assert(printFloat(cast(float) SQRT2, f) == "0x1.6a09e6p+0"); - assert(printFloat(cast(float) SQRT1_2, f) == "0x1.6a09e6p-1"); - - assert(printFloat(cast(double) E, f) == "0x1.5bf0a8b145769p+1"); - assert(printFloat(cast(double) PI, f) == "0x1.921fb54442d18p+1"); - assert(printFloat(cast(double) PI_2, f) == "0x1.921fb54442d18p+0"); - assert(printFloat(cast(double) PI_4, f) == "0x1.921fb54442d18p-1"); - assert(printFloat(cast(double) M_1_PI, f) == "0x1.45f306dc9c883p-2"); - assert(printFloat(cast(double) M_2_PI, f) == "0x1.45f306dc9c883p-1"); - assert(printFloat(cast(double) M_2_SQRTPI, f) == "0x1.20dd750429b6dp+0"); - assert(printFloat(cast(double) LN10, f) == "0x1.26bb1bbb55516p+1"); - assert(printFloat(cast(double) LN2, f) == "0x1.62e42fefa39efp-1"); - assert(printFloat(cast(double) LOG2, f) == "0x1.34413509f79ffp-2"); - assert(printFloat(cast(double) LOG2E, f) == "0x1.71547652b82fep+0"); - assert(printFloat(cast(double) LOG2T, f) == "0x1.a934f0979a371p+1"); - assert(printFloat(cast(double) LOG10E, f) == "0x1.bcb7b1526e50ep-2"); - assert(printFloat(cast(double) SQRT2, f) == "0x1.6a09e667f3bcdp+0"); - assert(printFloat(cast(double) SQRT1_2, f) == "0x1.6a09e667f3bcdp-1"); - - static if (real.mant_dig == 64) - { - assert(printFloat(E, f) == "0x1.5bf0a8b145769536p+1"); - assert(printFloat(PI, f) == "0x1.921fb54442d1846ap+1"); - assert(printFloat(PI_2, f) == "0x1.921fb54442d1846ap+0"); - assert(printFloat(PI_4, f) == "0x1.921fb54442d1846ap-1"); - assert(printFloat(M_1_PI, f) == "0x1.45f306dc9c882a54p-2"); - assert(printFloat(M_2_PI, f) == "0x1.45f306dc9c882a54p-1"); - assert(printFloat(M_2_SQRTPI, f) == "0x1.20dd750429b6d11ap+0"); - assert(printFloat(LN10, f) == "0x1.26bb1bbb5551582ep+1"); - assert(printFloat(LN2, f) == "0x1.62e42fefa39ef358p-1"); - assert(printFloat(LOG2, f) == "0x1.34413509f79fef32p-2"); - assert(printFloat(LOG2E, f) == "0x1.71547652b82fe178p+0"); - assert(printFloat(LOG2T, f) == "0x1.a934f0979a3715fcp+1"); - assert(printFloat(LOG10E, f) == "0x1.bcb7b1526e50e32ap-2"); - assert(printFloat(SQRT2, f) == "0x1.6a09e667f3bcc908p+0"); - assert(printFloat(SQRT1_2, f) == "0x1.6a09e667f3bcc908p-1"); - } -} - -@safe unittest -{ - auto f = FormatSpec!dchar(""); - f.spec = 'a'; - f.precision = 3; - - assert(printFloat(1.0f, f) == "0x1.000p+0"); - assert(printFloat(3.3f, f) == "0x1.a66p+1"); - assert(printFloat(2.9f, f) == "0x1.733p+1"); - - assert(printFloat(1.0, f) == "0x1.000p+0"); - assert(printFloat(3.3, f) == "0x1.a66p+1"); - assert(printFloat(2.9, f) == "0x1.733p+1"); - - static if (real.mant_dig == 64) - { - assert(printFloat(1.0L, f) == "0x1.000p+0"); - assert(printFloat(3.3L, f) == "0x1.a66p+1"); - assert(printFloat(2.9L, f) == "0x1.733p+1"); - } -} - -@safe unittest -{ - auto f = FormatSpec!dchar(""); - f.spec = 'a'; - f.precision = 0; - - assert(printFloat(1.0f, f) == "0x1p+0"); - assert(printFloat(3.3f, f) == "0x2p+1"); - assert(printFloat(2.9f, f) == "0x1p+1"); - - assert(printFloat(1.0, f) == "0x1p+0"); - assert(printFloat(3.3, f) == "0x2p+1"); - assert(printFloat(2.9, f) == "0x1p+1"); - - static if (real.mant_dig == 64) - { - assert(printFloat(1.0L, f) == "0x1p+0"); - assert(printFloat(3.3L, f) == "0x2p+1"); - assert(printFloat(2.9L, f) == "0x1p+1"); - } -} - -@safe unittest -{ - auto f = FormatSpec!dchar(""); - f.spec = 'a'; - f.precision = 0; - f.flHash = true; - - assert(printFloat(1.0f, f) == "0x1.p+0"); - assert(printFloat(3.3f, f) == "0x2.p+1"); - assert(printFloat(2.9f, f) == "0x1.p+1"); - - assert(printFloat(1.0, f) == "0x1.p+0"); - assert(printFloat(3.3, f) == "0x2.p+1"); - assert(printFloat(2.9, f) == "0x1.p+1"); - - static if (real.mant_dig == 64) - { - assert(printFloat(1.0L, f) == "0x1.p+0"); - assert(printFloat(3.3L, f) == "0x2.p+1"); - assert(printFloat(2.9L, f) == "0x1.p+1"); - } -} - -@safe unittest -{ - auto f = FormatSpec!dchar(""); - f.spec = 'a'; - f.width = 22; - - assert(printFloat(1.0f, f) == " 0x1p+0"); - assert(printFloat(3.3f, f) == " 0x1.a66666p+1"); - assert(printFloat(2.9f, f) == " 0x1.733334p+1"); - - assert(printFloat(1.0, f) == " 0x1p+0"); - assert(printFloat(3.3, f) == " 0x1.a666666666666p+1"); - assert(printFloat(2.9, f) == " 0x1.7333333333333p+1"); - - static if (real.mant_dig == 64) - { - f.width = 25; - assert(printFloat(1.0L, f) == " 0x1p+0"); - assert(printFloat(3.3L, f) == " 0x1.a666666666666666p+1"); - assert(printFloat(2.9L, f) == " 0x1.7333333333333334p+1"); - } -} - -@safe unittest -{ - auto f = FormatSpec!dchar(""); - f.spec = 'a'; - f.width = 22; - f.flDash = true; - - assert(printFloat(1.0f, f) == "0x1p+0 "); - assert(printFloat(3.3f, f) == "0x1.a66666p+1 "); - assert(printFloat(2.9f, f) == "0x1.733334p+1 "); - - assert(printFloat(1.0, f) == "0x1p+0 "); - assert(printFloat(3.3, f) == "0x1.a666666666666p+1 "); - assert(printFloat(2.9, f) == "0x1.7333333333333p+1 "); - - static if (real.mant_dig == 64) - { - f.width = 25; - assert(printFloat(1.0L, f) == "0x1p+0 "); - assert(printFloat(3.3L, f) == "0x1.a666666666666666p+1 "); - assert(printFloat(2.9L, f) == "0x1.7333333333333334p+1 "); - } -} - -@safe unittest -{ - auto f = FormatSpec!dchar(""); - f.spec = 'a'; - f.width = 22; - f.flZero = true; - - assert(printFloat(1.0f, f) == "0x00000000000000001p+0"); - assert(printFloat(3.3f, f) == "0x0000000001.a66666p+1"); - assert(printFloat(2.9f, f) == "0x0000000001.733334p+1"); - - assert(printFloat(1.0, f) == "0x00000000000000001p+0"); - assert(printFloat(3.3, f) == "0x001.a666666666666p+1"); - assert(printFloat(2.9, f) == "0x001.7333333333333p+1"); - - static if (real.mant_dig == 64) - { - f.width = 25; - assert(printFloat(1.0L, f) == "0x00000000000000000001p+0"); - assert(printFloat(3.3L, f) == "0x001.a666666666666666p+1"); - assert(printFloat(2.9L, f) == "0x001.7333333333333334p+1"); - } -} - -@safe unittest -{ - auto f = FormatSpec!dchar(""); - f.spec = 'a'; - f.width = 22; - f.flPlus = true; - - assert(printFloat(1.0f, f) == " +0x1p+0"); - assert(printFloat(3.3f, f) == " +0x1.a66666p+1"); - assert(printFloat(2.9f, f) == " +0x1.733334p+1"); - - assert(printFloat(1.0, f) == " +0x1p+0"); - assert(printFloat(3.3, f) == " +0x1.a666666666666p+1"); - assert(printFloat(2.9, f) == " +0x1.7333333333333p+1"); - - static if (real.mant_dig == 64) - { - f.width = 25; - assert(printFloat(1.0L, f) == " +0x1p+0"); - assert(printFloat(3.3L, f) == " +0x1.a666666666666666p+1"); - assert(printFloat(2.9L, f) == " +0x1.7333333333333334p+1"); - } -} - -@safe unittest -{ - auto f = FormatSpec!dchar(""); - f.spec = 'a'; - f.width = 22; - f.flDash = true; - f.flSpace = true; - - assert(printFloat(1.0f, f) == " 0x1p+0 "); - assert(printFloat(3.3f, f) == " 0x1.a66666p+1 "); - assert(printFloat(2.9f, f) == " 0x1.733334p+1 "); - - assert(printFloat(1.0, f) == " 0x1p+0 "); - assert(printFloat(3.3, f) == " 0x1.a666666666666p+1 "); - assert(printFloat(2.9, f) == " 0x1.7333333333333p+1 "); - - static if (real.mant_dig == 64) - { - f.width = 25; - assert(printFloat(1.0L, f) == " 0x1p+0 "); - assert(printFloat(3.3L, f) == " 0x1.a666666666666666p+1 "); - assert(printFloat(2.9L, f) == " 0x1.7333333333333334p+1 "); - } -} - -@safe unittest -{ - import std.math.hardware; // cannot be selective, because FloatingPointControl might not be defined - - // std.math's FloatingPointControl isn't available on all target platforms - static if (is(FloatingPointControl)) - { - FloatingPointControl fpctrl; - - auto f = FormatSpec!dchar(""); - f.spec = 'a'; - f.precision = 1; - - fpctrl.rounding = FloatingPointControl.roundToNearest; - - /* tiesAwayFromZero currently not supported - assert(printFloat(0x1.18p0, f) == "0x1.2p+0"); - assert(printFloat(0x1.28p0, f) == "0x1.3p+0"); - assert(printFloat(0x1.1ap0, f) == "0x1.2p+0"); - assert(printFloat(0x1.16p0, f) == "0x1.1p+0"); - assert(printFloat(0x1.10p0, f) == "0x1.1p+0"); - assert(printFloat(-0x1.18p0, f) == "-0x1.2p+0"); - assert(printFloat(-0x1.28p0, f) == "-0x1.3p+0"); - assert(printFloat(-0x1.1ap0, f) == "-0x1.2p+0"); - assert(printFloat(-0x1.16p0, f) == "-0x1.1p+0"); - assert(printFloat(-0x1.10p0, f) == "-0x1.1p+0"); - */ - - assert(printFloat(0x1.18p0, f) == "0x1.2p+0"); - assert(printFloat(0x1.28p0, f) == "0x1.2p+0"); - assert(printFloat(0x1.1ap0, f) == "0x1.2p+0"); - assert(printFloat(0x1.16p0, f) == "0x1.1p+0"); - assert(printFloat(0x1.10p0, f) == "0x1.1p+0"); - assert(printFloat(-0x1.18p0, f) == "-0x1.2p+0"); - assert(printFloat(-0x1.28p0, f) == "-0x1.2p+0"); - assert(printFloat(-0x1.1ap0, f) == "-0x1.2p+0"); - assert(printFloat(-0x1.16p0, f) == "-0x1.1p+0"); - assert(printFloat(-0x1.10p0, f) == "-0x1.1p+0"); - - fpctrl.rounding = FloatingPointControl.roundToZero; - - assert(printFloat(0x1.18p0, f) == "0x1.1p+0"); - assert(printFloat(0x1.28p0, f) == "0x1.2p+0"); - assert(printFloat(0x1.1ap0, f) == "0x1.1p+0"); - assert(printFloat(0x1.16p0, f) == "0x1.1p+0"); - assert(printFloat(0x1.10p0, f) == "0x1.1p+0"); - assert(printFloat(-0x1.18p0, f) == "-0x1.1p+0"); - assert(printFloat(-0x1.28p0, f) == "-0x1.2p+0"); - assert(printFloat(-0x1.1ap0, f) == "-0x1.1p+0"); - assert(printFloat(-0x1.16p0, f) == "-0x1.1p+0"); - assert(printFloat(-0x1.10p0, f) == "-0x1.1p+0"); - - fpctrl.rounding = FloatingPointControl.roundUp; - - assert(printFloat(0x1.18p0, f) == "0x1.2p+0"); - assert(printFloat(0x1.28p0, f) == "0x1.3p+0"); - assert(printFloat(0x1.1ap0, f) == "0x1.2p+0"); - assert(printFloat(0x1.16p0, f) == "0x1.2p+0"); - assert(printFloat(0x1.10p0, f) == "0x1.1p+0"); - assert(printFloat(-0x1.18p0, f) == "-0x1.1p+0"); - assert(printFloat(-0x1.28p0, f) == "-0x1.2p+0"); - assert(printFloat(-0x1.1ap0, f) == "-0x1.1p+0"); - assert(printFloat(-0x1.16p0, f) == "-0x1.1p+0"); - assert(printFloat(-0x1.10p0, f) == "-0x1.1p+0"); - - fpctrl.rounding = FloatingPointControl.roundDown; - - assert(printFloat(0x1.18p0, f) == "0x1.1p+0"); - assert(printFloat(0x1.28p0, f) == "0x1.2p+0"); - assert(printFloat(0x1.1ap0, f) == "0x1.1p+0"); - assert(printFloat(0x1.16p0, f) == "0x1.1p+0"); - assert(printFloat(0x1.10p0, f) == "0x1.1p+0"); - assert(printFloat(-0x1.18p0, f) == "-0x1.2p+0"); - assert(printFloat(-0x1.28p0, f) == "-0x1.3p+0"); - assert(printFloat(-0x1.1ap0, f) == "-0x1.2p+0"); - assert(printFloat(-0x1.16p0, f) == "-0x1.2p+0"); - assert(printFloat(-0x1.10p0, f) == "-0x1.1p+0"); - } -} - -// for 100% coverage -@safe unittest -{ - auto f = FormatSpec!dchar(""); - f.spec = 'a'; - f.precision = 3; - - assert(printFloat(0x1.19f81p0, f) == "0x1.1a0p+0"); - assert(printFloat(0x1.19f01p0, f) == "0x1.19fp+0"); -} - -@safe unittest -{ - auto f = FormatSpec!dchar(""); - f.spec = 'A'; - f.precision = 3; - - assert(printFloat(0x1.19f81p0, f) == "0X1.1A0P+0"); - assert(printFloat(0x1.19f01p0, f) == "0X1.19FP+0"); -} - -private void printFloatE(bool g, Writer, T, Char)(auto ref Writer w, const(T) val, - FormatSpec!Char f, string sgn, int exp, ulong mnt, bool is_upper) -if (is(T == float) || is(T == double) - || (is(T == real) && (T.mant_dig == double.mant_dig || T.mant_dig == 64))) -{ - import std.format.internal.write : writeAligned, PrecisionType, RoundingClass, round; - - static if (!g) - { - if (f.precision == f.UNSPECIFIED) - f.precision = 6; - } - - // special treatment for 0.0 - if (mnt == 0) - { - static if (g) - writeAligned(w, sgn, "0", ".", "", f, PrecisionType.allDigits); - else - writeAligned(w, sgn, "0", ".", is_upper ? "E+00" : "e+00", f, PrecisionType.fractionalDigits); - return; - } - - char[T.mant_dig + T.max_exp] dec_buf; - char[T.max_10_exp.stringof.length + 2] exp_buf; - - int final_exp = 0; - - RoundingClass rc; - - // Depending on exp, we will use one of three algorithms: - // - // Algorithm A: For large exponents (exp >= T.mant_dig) - // Algorithm B: For small exponents (exp < T.mant_dig - 61) - // Algorithm C: For exponents close to 0. - // - // Algorithm A: - // The number to print looks like this: mantissa followed by several zeros. - // - // We know, that there is no fractional part, so we can just use integer division, - // consecutivly dividing by 10 and writing down the remainder from right to left. - // Unfortunately the integer is too large to fit in an ulong, so we use something - // like BigInt: An array of ulongs. We only use 60 bits of that ulongs, because - // this simplifies (and speeds up) the division to come. - // - // For the division we use integer division with reminder for each ulong and put - // the reminder of each step in the first 4 bits of ulong of the next step (think of - // long division for the rationale behind this). The final reminder is the next - // digit (from right to left). - // - // This results in the output we would have for the %f specifier. We now adjust this - // for %e: First we calculate the place, where the exponent should be printed, filling - // up with zeros if needed and second we move the leftmost digit one to the left - // and inserting a dot. - // - // After that we decide on the rounding type, using the digits right of the position, - // where the exponent will be printed (currently they are still there, but will be - // overwritten later). - // - // Algorithm B: - // The number to print looks like this: zero dot several zeros followed by the mantissa - // - // We know, that the number has no integer part. The algorithm consecutivly multiplies - // by 10. The integer part (rounded down) after the multiplication is the next digit - // (from left to right). This integer part is removed after each step. - // Again, the number is represented as an array of ulongs, with only 60 bits used of - // every ulong. - // - // For the multiplication we use normal integer multiplication, which can result in digits - // in the uppermost 4 bits. These 4 digits are the carry which is added to the result - // of the next multiplication and finally the last carry is the next digit. - // - // Other than for the %f specifier, this multiplication is splitted into two almost - // identical parts. The first part lasts as long as we find zeros. We need to do this - // to calculate the correct exponent. - // - // The second part will stop, when only zeros remain or when we've got enough digits - // for the requested precision. In the second case, we have to find out, which rounding - // we have. Aside from special cases we do this by calculating one more digit. - // - // Algorithm C: - // This time, we know, that the integral part and the fractional part each fit into a - // ulong. The mantissa might be partially in both parts or completely in the fractional - // part. - // - // We first calculate the integral part by consecutive division by 10. Depending on the - // precision this might result in more digits, than we need. In that case we calculate - // the position of the exponent and the rounding type. - // - // If there is no integral part, we need to find the first non zero digit. We do this by - // consecutive multiplication by 10, saving the first non zero digit followed by a dot. - // - // In either case, we continue filling up with the fractional part until we have enough - // digits. If still necessary, we decide the rounding type, mainly by looking at the - // next digit. - - size_t right = 1; - size_t start = 1; - size_t left = 1; - - static if (is(T == real) && real.mant_dig == 64) - { - enum small_bound = 0; - enum max_buf = 275; - } - else - { - enum small_bound = T.mant_dig - 61; - static if (is(T == float)) - enum max_buf = 4; - else - enum max_buf = 18; - } - - ulong[max_buf] bigbuf; - if (exp >= T.mant_dig) - { - start = left = right = dec_buf.length; - - // large number without fractional digits - // - // As this number does not fit in a ulong, we use an array of ulongs. We only use 60 of the 64 bits, - // because this makes it much more easy to implement the division by 10. - int count = exp / 60 + 1; - - // only the first few ulongs contain the mantiassa. The rest are zeros. - int lower = 60 - (exp - T.mant_dig + 1) % 60; - - static if (is(T == real) && real.mant_dig == 64) - { - // for x87 reals, the lowest ulong may contain more than 60 bits, - // because the mantissa is 63 (>60) bits long - // therefore we need one ulong less - if (lower <= 3) count--; - } - - // saved in big endian format - ulong[] mybig = bigbuf[0 .. count]; - - if (lower < T.mant_dig) - { - mybig[0] = mnt >> lower; - mybig[1] = (mnt & ((1L << lower) - 1)) << 60 - lower; - } - else - mybig[0] = (mnt & ((1L << lower) - 1)) << 60 - lower; - - // Generation of digits by consecutive division with reminder by 10. - int msu = 0; // Most significant ulong; when it get's zero, we can ignore it further on - while (msu < count - 1 || mybig[$ - 1] != 0) - { - ulong mod = 0; - foreach (i;msu .. count) - { - mybig[i] |= mod << 60; - mod = mybig[i] % 10; - mybig[i] /= 10; - } - if (mybig[msu] == 0) - ++msu; - - dec_buf[--left] = cast(byte) ('0' + mod); - ++final_exp; - } - --final_exp; - - static if (g) - start = left + f.precision; - else - start = left + f.precision + 1; - - // move leftmost digit one more left and add dot between - dec_buf[left - 1] = dec_buf[left]; - dec_buf[left] = '.'; - --left; - - // rounding type - if (start >= right) - rc = RoundingClass.ZERO; - else if (dec_buf[start] != '0' && dec_buf[start] != '5') - rc = dec_buf[start] > '5' ? RoundingClass.UPPER : RoundingClass.LOWER; - else - { - rc = dec_buf[start] == '5' ? RoundingClass.FIVE : RoundingClass.ZERO; - foreach (i; start + 1 .. right) - if (dec_buf[i] > '0') - { - rc = rc == RoundingClass.FIVE ? RoundingClass.UPPER : RoundingClass.LOWER; - break; - } - } - - if (start < right) right = start; - } - else if (exp < small_bound) - { - // small number without integer digits - // - // Again this number does not fit in a ulong and we use an array of ulongs. And again we - // only use 60 bits, because this simplifies the multiplication by 10. - int count = (T.mant_dig - exp - 2) / 60 + 1; - - // saved in little endian format - ulong[] mybig = bigbuf[0 .. count]; - - // only the last few ulongs contain the mantiassa. Because of little endian - // format these are the ulongs at index 0 and 1 (and 2 in case of x87 reals). - // The rest are zeros. - int upper = 60 - (-exp - 1) % 60; - - static if (is(T == real) && real.mant_dig == 64) - { - if (upper < 4) - { - mybig[0] = (mnt & ((1L << (4 - upper)) - 1)) << 56 + upper; - mybig[1] = (mnt >> (4 - upper)) & ((1L << 60) - 1); - mybig[2] = mnt >> 64 - upper; - } - else - { - mybig[0] = (mnt & ((1L << (T.mant_dig - upper)) - 1)) << 60 - (T.mant_dig - upper); - mybig[1] = mnt >> (T.mant_dig - upper); - } - } - else - { - if (upper < T.mant_dig) - { - mybig[0] = (mnt & ((1L << (T.mant_dig - upper)) - 1)) << 60 - (T.mant_dig - upper); - mybig[1] = mnt >> (T.mant_dig - upper); - } - else - mybig[0] = mnt << (upper - T.mant_dig); - } - - int lsu = 0; // Least significant ulong; when it get's zero, we can ignore it further on - - // adding zeros, until we reach first nonzero - while (lsu < count - 1 || mybig[$ - 1]!=0) - { - ulong over = 0; - foreach (i; lsu .. count) - { - mybig[i] = mybig[i] * 10 + over; - over = mybig[i] >> 60; - mybig[i] &= (1L << 60) - 1; - } - if (mybig[lsu] == 0) - ++lsu; - --final_exp; - - if (over != 0) - { - dec_buf[right++] = cast(byte) ('0' + over); - dec_buf[right++] = '.'; - break; - } - } - - // adding more digits - static if (g) - start = right - 1; - else - start = right; - while ((lsu < count - 1 || mybig[$ - 1] != 0) && right - start < f.precision) - { - ulong over = 0; - foreach (i;lsu .. count) - { - mybig[i] = mybig[i] * 10 + over; - over = mybig[i] >> 60; - mybig[i] &= (1L << 60) - 1; - } - if (mybig[lsu] == 0) - ++lsu; - - dec_buf[right++] = cast(byte) ('0' + over); - } - - // rounding type - if (lsu >= count - 1 && mybig[count - 1] == 0) - rc = RoundingClass.ZERO; - else if (lsu == count - 1 && mybig[lsu] == 1L << 59) - rc = RoundingClass.FIVE; - else - { - ulong over = 0; - foreach (i;lsu .. count) - { - mybig[i] = mybig[i] * 10 + over; - over = mybig[i] >> 60; - mybig[i] &= (1L << 60) - 1; - } - rc = over >= 5 ? RoundingClass.UPPER : RoundingClass.LOWER; - } - } - else - { - // medium sized number, probably with integer and fractional digits - // this is fastest, because both parts fit into a ulong each - ulong int_part = mnt >> (T.mant_dig - 1 - exp); - ulong frac_part = mnt & ((1L << (T.mant_dig - 1 - exp)) - 1); - - // for x87 reals the mantiassa might be up to 3 bits too long - // we need to save these bits as a tail and handle this separately - static if (is(T == real) && real.mant_dig == 64) - { - ulong tail = 0; - ulong tail_length = 0; - if (exp < 3) - { - tail = frac_part & ((1L << (3 - exp)) - 1); - tail_length = 3 - exp; - frac_part >>= 3 - exp; - exp = 3; - } - } - - start = 0; - - // could we already decide on the rounding mode in the integer part? - bool found = false; - - if (int_part > 0) - { - import core.bitop : bsr; - left = right = int_part.bsr * 100 / 332 + 4; - - // integer part, if there is something to print - while (int_part >= 10) - { - dec_buf[--left] = '0' + (int_part % 10); - int_part /= 10; - ++final_exp; - ++start; - } - - dec_buf[--left] = '.'; - dec_buf[--left] = cast(byte) ('0' + int_part); - - static if (g) - auto limit = f.precision + 1; - else - auto limit = f.precision + 2; - - if (right - left > limit) - { - auto old_right = right; - right = left + limit; - - if (dec_buf[right] == '5' || dec_buf[right] == '0') - { - rc = dec_buf[right] == '5' ? RoundingClass.FIVE : RoundingClass.ZERO; - if (frac_part != 0) - rc = rc == RoundingClass.FIVE ? RoundingClass.UPPER : RoundingClass.LOWER; - else - foreach (i;right + 1 .. old_right) - if (dec_buf[i] > '0') - { - rc = rc == RoundingClass.FIVE ? RoundingClass.UPPER : RoundingClass.LOWER; - break; - } - } - else - rc = dec_buf[right] > '5' ? RoundingClass.UPPER : RoundingClass.LOWER; - found = true; - } - } - else - { - // fractional part, skipping leading zeros - while (frac_part != 0) - { - --final_exp; - frac_part *= 10; - static if (is(T == real) && real.mant_dig == 64) - { - if (tail_length > 0) - { - // together this is *= 10; - tail *= 5; - tail_length--; - - frac_part += tail >> tail_length; - if (tail_length > 0) - tail &= (1L << tail_length) - 1; - } - } - auto tmp = frac_part >> (T.mant_dig - 1 - exp); - frac_part &= ((1L << (T.mant_dig - 1 - exp)) - 1); - if (tmp > 0) - { - dec_buf[right++] = cast(byte) ('0' + tmp); - dec_buf[right++] = '.'; - break; - } - } - - rc = RoundingClass.ZERO; - } - - static if (g) - size_t limit = f.precision - 1; - else - size_t limit = f.precision; - - // the fractional part after the zeros - while (frac_part != 0 && start < limit) - { - frac_part *= 10; - static if (is(T == real) && real.mant_dig == 64) - { - if (tail_length > 0) - { - // together this is *= 10; - tail *= 5; - tail_length--; - - frac_part += tail >> tail_length; - if (tail_length > 0) - tail &= (1L << tail_length) - 1; - } - } - dec_buf[right++] = cast(byte) ('0' + (frac_part >> (T.mant_dig - 1 - exp))); - frac_part &= ((1L << (T.mant_dig - 1 - exp)) - 1); - ++start; - } - - static if (g) - limit = right - left - 1; - else - limit = start; - - // rounding mode, if not allready known - if (frac_part != 0 && !found) - { - frac_part *= 10; - auto nextDigit = frac_part >> (T.mant_dig - 1 - exp); - frac_part &= ((1L << (T.mant_dig - 1 - exp)) - 1); - - if (nextDigit == 5 && frac_part == 0) - rc = RoundingClass.FIVE; - else if (nextDigit >= 5) - rc = RoundingClass.UPPER; - else - rc = RoundingClass.LOWER; - } - } - - if (round(dec_buf, left, right, rc, sgn == "-")) - { - left--; - right--; - dec_buf[left + 2] = dec_buf[left + 1]; - dec_buf[left + 1] = '.'; - final_exp++; - } - - // printing exponent - auto neg = final_exp < 0; - if (neg) final_exp = -final_exp; - - size_t exp_pos = exp_buf.length; - - do - { - exp_buf[--exp_pos] = '0' + final_exp%10; - final_exp /= 10; - } while (final_exp > 0); - if (exp_buf.length - exp_pos == 1) - exp_buf[--exp_pos] = '0'; - exp_buf[--exp_pos] = neg ? '-' : '+'; - exp_buf[--exp_pos] = is_upper ? 'E' : 'e'; - - while (right > left + 1 && dec_buf[right - 1] == '0') right--; - - if (right == left + 1) - dec_buf[right++] = '.'; - - static if (g) - writeAligned(w, sgn, dec_buf[left .. left + 1], dec_buf[left + 1 .. right], - exp_buf[exp_pos .. $], f, PrecisionType.allDigits); - else - writeAligned(w, sgn, dec_buf[left .. left + 1], dec_buf[left + 1 .. right], - exp_buf[exp_pos .. $], f, PrecisionType.fractionalDigits); -} - -@safe unittest -{ - auto f = FormatSpec!dchar(""); - f.spec = 'e'; - assert(printFloat(float.nan, f) == "nan"); - assert(printFloat(-float.nan, f) == "-nan"); - assert(printFloat(float.infinity, f) == "inf"); - assert(printFloat(-float.infinity, f) == "-inf"); - assert(printFloat(0.0f, f) == "0.000000e+00"); - assert(printFloat(-0.0f, f) == "-0.000000e+00"); - // cast needed due to https://issues.dlang.org/show_bug.cgi?id=20361 - assert(printFloat(cast(float) 1e-40, f) == "9.999946e-41"); - assert(printFloat(cast(float) -1e-40, f) == "-9.999946e-41"); - assert(printFloat(1e-30f, f) == "1.000000e-30"); - assert(printFloat(-1e-30f, f) == "-1.000000e-30"); - assert(printFloat(1e-10f, f) == "1.000000e-10"); - assert(printFloat(-1e-10f, f) == "-1.000000e-10"); - assert(printFloat(0.1f, f) == "1.000000e-01"); - assert(printFloat(-0.1f, f) == "-1.000000e-01"); - assert(printFloat(10.0f, f) == "1.000000e+01"); - assert(printFloat(-10.0f, f) == "-1.000000e+01"); - assert(printFloat(1e30f, f) == "1.000000e+30"); - assert(printFloat(-1e30f, f) == "-1.000000e+30"); - - import std.math.operations : nextUp, nextDown; - assert(printFloat(nextUp(0.0f), f) == "1.401298e-45"); - assert(printFloat(nextDown(-0.0f), f) == "-1.401298e-45"); -} - -@safe unittest -{ - auto f = FormatSpec!dchar(""); - f.spec = 'e'; - f.width = 20; - f.precision = 10; - - assert(printFloat(float.nan, f) == " nan"); - assert(printFloat(-float.nan, f) == " -nan"); - assert(printFloat(float.infinity, f) == " inf"); - assert(printFloat(-float.infinity, f) == " -inf"); - assert(printFloat(0.0f, f) == " 0.0000000000e+00"); - assert(printFloat(-0.0f, f) == " -0.0000000000e+00"); - // cast needed due to https://issues.dlang.org/show_bug.cgi?id=20361 - assert(printFloat(cast(float) 1e-40, f) == " 9.9999461011e-41"); - assert(printFloat(cast(float) -1e-40, f) == " -9.9999461011e-41"); - assert(printFloat(1e-30f, f) == " 1.0000000032e-30"); - assert(printFloat(-1e-30f, f) == " -1.0000000032e-30"); - assert(printFloat(1e-10f, f) == " 1.0000000134e-10"); - assert(printFloat(-1e-10f, f) == " -1.0000000134e-10"); - assert(printFloat(0.1f, f) == " 1.0000000149e-01"); - assert(printFloat(-0.1f, f) == " -1.0000000149e-01"); - assert(printFloat(10.0f, f) == " 1.0000000000e+01"); - assert(printFloat(-10.0f, f) == " -1.0000000000e+01"); - assert(printFloat(1e30f, f) == " 1.0000000150e+30"); - assert(printFloat(-1e30f, f) == " -1.0000000150e+30"); - - import std.math.operations : nextUp, nextDown; - assert(printFloat(nextUp(0.0f), f) == " 1.4012984643e-45"); - assert(printFloat(nextDown(-0.0f), f) == " -1.4012984643e-45"); -} - -@safe unittest -{ - auto f = FormatSpec!dchar(""); - f.spec = 'e'; - f.width = 20; - f.precision = 10; - f.flDash = true; - - assert(printFloat(float.nan, f) == "nan "); - assert(printFloat(-float.nan, f) == "-nan "); - assert(printFloat(float.infinity, f) == "inf "); - assert(printFloat(-float.infinity, f) == "-inf "); - assert(printFloat(0.0f, f) == "0.0000000000e+00 "); - assert(printFloat(-0.0f, f) == "-0.0000000000e+00 "); - // cast needed due to https://issues.dlang.org/show_bug.cgi?id=20361 - assert(printFloat(cast(float) 1e-40, f) == "9.9999461011e-41 "); - assert(printFloat(cast(float) -1e-40, f) == "-9.9999461011e-41 "); - assert(printFloat(1e-30f, f) == "1.0000000032e-30 "); - assert(printFloat(-1e-30f, f) == "-1.0000000032e-30 "); - assert(printFloat(1e-10f, f) == "1.0000000134e-10 "); - assert(printFloat(-1e-10f, f) == "-1.0000000134e-10 "); - assert(printFloat(0.1f, f) == "1.0000000149e-01 "); - assert(printFloat(-0.1f, f) == "-1.0000000149e-01 "); - assert(printFloat(10.0f, f) == "1.0000000000e+01 "); - assert(printFloat(-10.0f, f) == "-1.0000000000e+01 "); - assert(printFloat(1e30f, f) == "1.0000000150e+30 "); - assert(printFloat(-1e30f, f) == "-1.0000000150e+30 "); - - import std.math.operations : nextUp, nextDown; - assert(printFloat(nextUp(0.0f), f) == "1.4012984643e-45 "); - assert(printFloat(nextDown(-0.0f), f) == "-1.4012984643e-45 "); -} - -@safe unittest -{ - auto f = FormatSpec!dchar(""); - f.spec = 'e'; - f.width = 20; - f.precision = 10; - f.flZero = true; - - assert(printFloat(float.nan, f) == " nan"); - assert(printFloat(-float.nan, f) == " -nan"); - assert(printFloat(float.infinity, f) == " inf"); - assert(printFloat(-float.infinity, f) == " -inf"); - assert(printFloat(0.0f, f) == "00000.0000000000e+00"); - assert(printFloat(-0.0f, f) == "-0000.0000000000e+00"); - // cast needed due to https://issues.dlang.org/show_bug.cgi?id=20361 - assert(printFloat(cast(float) 1e-40, f) == "00009.9999461011e-41"); - assert(printFloat(cast(float) -1e-40, f) == "-0009.9999461011e-41"); - assert(printFloat(1e-30f, f) == "00001.0000000032e-30"); - assert(printFloat(-1e-30f, f) == "-0001.0000000032e-30"); - assert(printFloat(1e-10f, f) == "00001.0000000134e-10"); - assert(printFloat(-1e-10f, f) == "-0001.0000000134e-10"); - assert(printFloat(0.1f, f) == "00001.0000000149e-01"); - assert(printFloat(-0.1f, f) == "-0001.0000000149e-01"); - assert(printFloat(10.0f, f) == "00001.0000000000e+01"); - assert(printFloat(-10.0f, f) == "-0001.0000000000e+01"); - assert(printFloat(1e30f, f) == "00001.0000000150e+30"); - assert(printFloat(-1e30f, f) == "-0001.0000000150e+30"); - - import std.math.operations : nextUp, nextDown; - assert(printFloat(nextUp(0.0f), f) == "00001.4012984643e-45"); - assert(printFloat(nextDown(-0.0f), f) == "-0001.4012984643e-45"); -} - -@safe unittest -{ - import std.math.hardware; // cannot be selective, because FloatingPointControl might not be defined - - // std.math's FloatingPointControl isn't available on all target platforms - static if (is(FloatingPointControl)) - { - FloatingPointControl fpctrl; - - auto f = FormatSpec!dchar(""); - f.spec = 'e'; - f.precision = 1; - - fpctrl.rounding = FloatingPointControl.roundToNearest; - - /* - assert(printFloat(11.5f, f) == "1.2e+01"); - assert(printFloat(12.5f, f) == "1.3e+01"); - assert(printFloat(11.7f, f) == "1.2e+01"); - assert(printFloat(11.3f, f) == "1.1e+01"); - assert(printFloat(11.0f, f) == "1.1e+01"); - assert(printFloat(-11.5f, f) == "-1.2e+01"); - assert(printFloat(-12.5f, f) == "-1.3e+01"); - assert(printFloat(-11.7f, f) == "-1.2e+01"); - assert(printFloat(-11.3f, f) == "-1.1e+01"); - assert(printFloat(-11.0f, f) == "-1.1e+01"); - */ - - assert(printFloat(11.5f, f) == "1.2e+01"); - assert(printFloat(12.5f, f) == "1.2e+01"); - assert(printFloat(11.7f, f) == "1.2e+01"); - assert(printFloat(11.3f, f) == "1.1e+01"); - assert(printFloat(11.0f, f) == "1.1e+01"); - assert(printFloat(-11.5f, f) == "-1.2e+01"); - assert(printFloat(-12.5f, f) == "-1.2e+01"); - assert(printFloat(-11.7f, f) == "-1.2e+01"); - assert(printFloat(-11.3f, f) == "-1.1e+01"); - assert(printFloat(-11.0f, f) == "-1.1e+01"); - - fpctrl.rounding = FloatingPointControl.roundToZero; - - assert(printFloat(11.5f, f) == "1.1e+01"); - assert(printFloat(12.5f, f) == "1.2e+01"); - assert(printFloat(11.7f, f) == "1.1e+01"); - assert(printFloat(11.3f, f) == "1.1e+01"); - assert(printFloat(11.0f, f) == "1.1e+01"); - assert(printFloat(-11.5f, f) == "-1.1e+01"); - assert(printFloat(-12.5f, f) == "-1.2e+01"); - assert(printFloat(-11.7f, f) == "-1.1e+01"); - assert(printFloat(-11.3f, f) == "-1.1e+01"); - assert(printFloat(-11.0f, f) == "-1.1e+01"); - - fpctrl.rounding = FloatingPointControl.roundUp; - - assert(printFloat(11.5f, f) == "1.2e+01"); - assert(printFloat(12.5f, f) == "1.3e+01"); - assert(printFloat(11.7f, f) == "1.2e+01"); - assert(printFloat(11.3f, f) == "1.2e+01"); - assert(printFloat(11.0f, f) == "1.1e+01"); - assert(printFloat(-11.5f, f) == "-1.1e+01"); - assert(printFloat(-12.5f, f) == "-1.2e+01"); - assert(printFloat(-11.7f, f) == "-1.1e+01"); - assert(printFloat(-11.3f, f) == "-1.1e+01"); - assert(printFloat(-11.0f, f) == "-1.1e+01"); - - fpctrl.rounding = FloatingPointControl.roundDown; - - assert(printFloat(11.5f, f) == "1.1e+01"); - assert(printFloat(12.5f, f) == "1.2e+01"); - assert(printFloat(11.7f, f) == "1.1e+01"); - assert(printFloat(11.3f, f) == "1.1e+01"); - assert(printFloat(11.0f, f) == "1.1e+01"); - assert(printFloat(-11.5f, f) == "-1.2e+01"); - assert(printFloat(-12.5f, f) == "-1.3e+01"); - assert(printFloat(-11.7f, f) == "-1.2e+01"); - assert(printFloat(-11.3f, f) == "-1.2e+01"); - assert(printFloat(-11.0f, f) == "-1.1e+01"); - } -} - -@safe unittest -{ - auto f = FormatSpec!dchar(""); - f.spec = 'e'; - assert(printFloat(double.nan, f) == "nan"); - assert(printFloat(-double.nan, f) == "-nan"); - assert(printFloat(double.infinity, f) == "inf"); - assert(printFloat(-double.infinity, f) == "-inf"); - assert(printFloat(0.0, f) == "0.000000e+00"); - assert(printFloat(-0.0, f) == "-0.000000e+00"); - // / 1000 needed due to https://issues.dlang.org/show_bug.cgi?id=20361 - assert(printFloat(1e-307 / 1000, f) == "1.000000e-310"); - assert(printFloat(-1e-307 / 1000, f) == "-1.000000e-310"); - assert(printFloat(1e-30, f) == "1.000000e-30"); - assert(printFloat(-1e-30, f) == "-1.000000e-30"); - assert(printFloat(1e-10, f) == "1.000000e-10"); - assert(printFloat(-1e-10, f) == "-1.000000e-10"); - assert(printFloat(0.1, f) == "1.000000e-01"); - assert(printFloat(-0.1, f) == "-1.000000e-01"); - assert(printFloat(10.0, f) == "1.000000e+01"); - assert(printFloat(-10.0, f) == "-1.000000e+01"); - assert(printFloat(1e300, f) == "1.000000e+300"); - assert(printFloat(-1e300, f) == "-1.000000e+300"); - - import std.math.operations : nextUp, nextDown; - assert(printFloat(nextUp(0.0), f) == "4.940656e-324"); - assert(printFloat(nextDown(-0.0), f) == "-4.940656e-324"); -} - -@safe unittest -{ - static if (real.mant_dig > 64) - { - pragma(msg, "printFloat tests disabled because of unsupported `real` format"); - } - else - { - auto f = FormatSpec!dchar(""); - f.spec = 'e'; - assert(printFloat(real.nan, f) == "nan"); - assert(printFloat(-real.nan, f) == "-nan"); - assert(printFloat(real.infinity, f) == "inf"); - assert(printFloat(-real.infinity, f) == "-inf"); - } -} - -@safe unittest -{ - auto f = FormatSpec!dchar(""); - f.spec = 'e'; - - import std.math.operations : nextUp; - - double eps = nextUp(0.0); - f.precision = 1000; - assert(printFloat(eps, f) == - "4.9406564584124654417656879286822137236505980261432476442558568250067550727020875186529983636163599" - ~"23797965646954457177309266567103559397963987747960107818781263007131903114045278458171678489821036" - ~"88718636056998730723050006387409153564984387312473397273169615140031715385398074126238565591171026" - ~"65855668676818703956031062493194527159149245532930545654440112748012970999954193198940908041656332" - ~"45247571478690147267801593552386115501348035264934720193790268107107491703332226844753335720832431" - ~"93609238289345836806010601150616980975307834227731832924790498252473077637592724787465608477820373" - ~"44696995336470179726777175851256605511991315048911014510378627381672509558373897335989936648099411" - ~"64205702637090279242767544565229087538682506419718265533447265625000000000000000000000000000000000" - ~"00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" - ~"00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" - ~"000000000000000000000e-324"); - - f.precision = 50; - assert(printFloat(double.max, f) == - "1.79769313486231570814527423731704356798070567525845e+308"); - assert(printFloat(double.epsilon, f) == - "2.22044604925031308084726333618164062500000000000000e-16"); - - f.precision = 10; - assert(printFloat(1.0/3.0, f) == "3.3333333333e-01"); - assert(printFloat(1.0/7.0, f) == "1.4285714286e-01"); - assert(printFloat(1.0/9.0, f) == "1.1111111111e-01"); -} - -@safe unittest -{ - auto f = FormatSpec!dchar(""); - f.spec = 'e'; - f.precision = 15; - - import std.math.constants : E, PI, PI_2, PI_4, M_1_PI, M_2_PI, M_2_SQRTPI, - LN10, LN2, LOG2, LOG2E, LOG2T, LOG10E, SQRT2, SQRT1_2; - - assert(printFloat(cast(double) E, f) == "2.718281828459045e+00"); - assert(printFloat(cast(double) PI, f) == "3.141592653589793e+00"); - assert(printFloat(cast(double) PI_2, f) == "1.570796326794897e+00"); - assert(printFloat(cast(double) PI_4, f) == "7.853981633974483e-01"); - assert(printFloat(cast(double) M_1_PI, f) == "3.183098861837907e-01"); - assert(printFloat(cast(double) M_2_PI, f) == "6.366197723675814e-01"); - assert(printFloat(cast(double) M_2_SQRTPI, f) == "1.128379167095513e+00"); - assert(printFloat(cast(double) LN10, f) == "2.302585092994046e+00"); - assert(printFloat(cast(double) LN2, f) == "6.931471805599453e-01"); - assert(printFloat(cast(double) LOG2, f) == "3.010299956639812e-01"); - assert(printFloat(cast(double) LOG2E, f) == "1.442695040888963e+00"); - assert(printFloat(cast(double) LOG2T, f) == "3.321928094887362e+00"); - assert(printFloat(cast(double) LOG10E, f) == "4.342944819032518e-01"); - assert(printFloat(cast(double) SQRT2, f) == "1.414213562373095e+00"); - assert(printFloat(cast(double) SQRT1_2, f) == "7.071067811865476e-01"); -} - -// for 100% coverage -@safe unittest -{ - import std.math.hardware; // cannot be selective, because FloatingPointControl might not be defined - - auto f = FormatSpec!dchar(""); - f.spec = 'E'; - f.precision = 80; - assert(printFloat(5.62776e+12f, f) == - "5.62775982080000000000000000000000000000000000000000000000000000000000000000000000E+12"); - - f.precision = 49; - assert(printFloat(2.5997869e-12f, f) == - "2.5997869221999758693186777236405760049819946289062E-12"); - - f.precision = 6; - assert(printFloat(-1.1418613e+07f, f) == "-1.141861E+07"); - assert(printFloat(-1.368281e+07f, f) == "-1.368281E+07"); - - f.precision = 1; - assert(printFloat(-245.666f, f) == "-2.5E+02"); - - static if (is(FloatingPointControl)) - { - FloatingPointControl fpctrl; - - fpctrl.rounding = FloatingPointControl.roundUp; - - f.precision = 0; - assert(printFloat(709422.0f, f) == "8E+05"); - } -} - -@safe unittest -{ - static if (real.mant_dig > 64) - { - pragma(msg, "printFloat tests disabled because of unsupported `real` format"); - } - else - { - auto f = FormatSpec!dchar(""); - f.spec = 'e'; - assert(printFloat(real.nan, f) == "nan"); - assert(printFloat(-real.nan, f) == "-nan"); - assert(printFloat(real.infinity, f) == "inf"); - assert(printFloat(-real.infinity, f) == "-inf"); - assert(printFloat(0.0L, f) == "0.000000e+00"); - assert(printFloat(-0.0L, f) == "-0.000000e+00"); - } - - static if (real.mant_dig == 64) - { - assert(printFloat(1e-4940L, f) == "1.000000e-4940"); - assert(printFloat(-1e-4940L, f) == "-1.000000e-4940"); - assert(printFloat(1e-30L, f) == "1.000000e-30"); - assert(printFloat(-1e-30L, f) == "-1.000000e-30"); - assert(printFloat(1e-10L, f) == "1.000000e-10"); - assert(printFloat(-1e-10L, f) == "-1.000000e-10"); - assert(printFloat(0.1L, f) == "1.000000e-01"); - assert(printFloat(-0.1L, f) == "-1.000000e-01"); - assert(printFloat(10.0L, f) == "1.000000e+01"); - assert(printFloat(-10.0L, f) == "-1.000000e+01"); - version (Windows) {} // https://issues.dlang.org/show_bug.cgi?id=20972 - else - { - assert(printFloat(1e4000L, f) == "1.000000e+4000"); - assert(printFloat(-1e4000L, f) == "-1.000000e+4000"); - } - - import std.math.operations : nextUp, nextDown; - assert(printFloat(nextUp(0.0L), f) == "3.645200e-4951"); - assert(printFloat(nextDown(-0.0L), f) == "-3.645200e-4951"); - } -} - -@safe unittest -{ - import std.exception : assertCTFEable; - import std.math.exponential : log2; - import std.math.operations : nextDown; - - assertCTFEable!( - { - // log2 is broken for x87-reals on some computers in CTFE - // the following tests excludes these computers from the tests - // (https://issues.dlang.org/show_bug.cgi?id=21757) - enum test = cast(int) log2(3.05e2312L); - static if (real.mant_dig == 64 && test == 7681) - { - auto f = FormatSpec!dchar(""); - f.spec = 'e'; - assert(printFloat(real.infinity, f) == "inf"); - assert(printFloat(10.0L, f) == "1.000000e+01"); - assert(printFloat(2.6080L, f) == "2.608000e+00"); - assert(printFloat(3.05e2312L, f) == "3.050000e+2312"); - - f.precision = 60; - assert(printFloat(2.65e-54L, f) == - "2.650000000000000000059009987400547013941028940935296547599415e-54"); - - /* - commented out, because CTFE is currently too slow for 5000 digits with extreme values - - f.precision = 5000; - auto result2 = printFloat(1.2119e-4822L, f); - assert(result2.length == 5008); - assert(result2[$ - 20 .. $] == "60729486595339e-4822"); - auto result3 = printFloat(real.min_normal, f); - assert(result3.length == 5008); - assert(result3[$ - 20 .. $] == "20781410082267e-4932"); - auto result4 = printFloat(real.min_normal.nextDown, f); - assert(result4.length == 5008); - assert(result4[$ - 20 .. $] == "81413263331006e-4932"); - */ - } - }); -} - -private void printFloatF(bool g, Writer, T, Char)(auto ref Writer w, const(T) val, - FormatSpec!Char f, string sgn, int exp, ulong mnt, bool is_upper) -if (is(T == float) || is(T == double) - || (is(T == real) && (T.mant_dig == double.mant_dig || T.mant_dig == 64))) -{ - import std.format.internal.write : writeAligned, PrecisionType, RoundingClass, round; - - static if (!g) - { - if (f.precision == f.UNSPECIFIED) - f.precision = 6; - } - - // special treatment for 0.0 - if (exp == 0 && mnt == 0) - { - writeAligned(w, sgn, "0", ".", "", f, PrecisionType.fractionalDigits); - return; - } - - char[T.max_exp + T.mant_dig + 1] dec_buf; - - RoundingClass rc; - - // Depending on exp, we will use one of three algorithms: - // - // Algorithm A: For large exponents (exp >= T.mant_dig) - // Algorithm B: For small exponents (exp < T.mant_dig - 61) - // Algorithm C: For exponents close to 0. - // - // Algorithm A: - // The number to print looks like this: mantissa followed by several zeros. - // - // We know, that there is no fractional part, so we can just use integer division, - // consecutivly dividing by 10 and writing down the remainder from right to left. - // Unfortunately the integer is too large to fit in an ulong, so we use something - // like BigInt: An array of ulongs. We only use 60 bits of that ulongs, because - // this simplifies (and speeds up) the division to come. - // - // For the division we use integer division with reminder for each ulong and put - // the reminder of each step in the first 4 bits of ulong of the next step (think of - // long division for the rationale behind this). The final reminder is the next - // digit (from right to left). - // - // Algorithm B: - // The number to print looks like this: zero dot several zeros followed by the mantissa - // - // We know, that the number has no integer part. The algorithm consecutivly multiplies - // by 10. The integer part (rounded down) after the multiplication is the next digit - // (from left to right). This integer part is removed after each step. - // Again, the number is represented as an array of ulongs, with only 60 bits used of - // every ulong. - // - // For the multiplication we use normal integer multiplication, which can result in digits - // in the uppermost 4 bits. These 4 digits are the carry which is added to the result - // of the next multiplication and finally the last carry is the next digit. - // - // The calculation will stop, when only zeros remain or when we've got enough digits - // for the requested precision. In the second case, we have to find out, which rounding - // we have. Aside from special cases we do this by calculating one more digit. - // - // Algorithm C: - // This time, we know, that the integral part and the fractional part each fit into a - // ulong. The mantissa might be partially in both parts or completely in the fractional - // part. - // - // We first calculate the integral part by consecutive division by 10. Then we calculate - // the fractional part by consecutive multiplication by 10. Again only until we have enough - // digits. Finally, we decide the rounding type, mainly by looking at the next digit. - - static if (is(T == real) && real.mant_dig == 64) - { - enum small_bound = 0; - enum max_buf = 275; - } - else - { - enum small_bound = T.mant_dig - 61; - static if (is(T == float)) - enum max_buf = 4; - else - enum max_buf = 18; - } - - size_t start = 2; - size_t left = 2; - size_t right = 2; - - ulong[max_buf] bigbuf; - if (exp >= T.mant_dig) - { - left = start = dec_buf.length - 1; - right = dec_buf.length; - dec_buf[start] = '.'; - - // large number without fractional digits - // - // As this number does not fit in a ulong, we use an array of ulongs. We only use 60 of the 64 bits, - // because this makes it much more easy to implement the division by 10. - int count = exp / 60 + 1; - - // only the first few ulongs contain the mantiassa. The rest are zeros. - int lower = 60 - (exp - T.mant_dig + 1) % 60; - - static if (is(T == real) && real.mant_dig == 64) - { - // for x87 reals, the lowest ulong may contain more than 60 bits, - // because the mantissa is 63 (>60) bits long - // therefore we need one ulong less - if (lower <= 3) count--; - } - - // saved in big endian format - ulong[] mybig = bigbuf[0 .. count]; - - if (lower < T.mant_dig) - { - mybig[0] = mnt >> lower; - mybig[1] = (mnt & ((1L << lower) - 1)) << 60 - lower; - } - else - mybig[0] = (mnt & ((1L << lower) - 1)) << 60 - lower; - - // Generation of digits by consecutive division with reminder by 10. - int msu = 0; // Most significant ulong; when it get's zero, we can ignore it furtheron - while (msu < count - 1 || mybig[$ - 1] != 0) - { - ulong mod = 0; - foreach (i;msu .. count) - { - mybig[i] |= mod << 60; - mod = mybig[i] % 10; - mybig[i] /= 10; - } - if (mybig[msu] == 0) - ++msu; - - dec_buf[--left] = cast(byte) ('0' + mod); - } - - rc = RoundingClass.ZERO; - } - else if (exp < small_bound) - { - // small number without integer digits - // - // Again this number does not fit in a ulong and we use an array of ulongs. And again we - // only use 60 bits, because this simplifies the multiplication by 10. - int count = (T.mant_dig - exp - 2) / 60 + 1; - - // saved in little endian format - ulong[] mybig = bigbuf[0 .. count]; - - // only the last few ulongs contain the mantiassa. Because of little endian - // format these are the ulongs at index 0 and 1 (and 2 in case of x87 reals). - // The rest are zeros. - int upper = 60 - (-exp - 1) % 60; - - static if (is(T == real) && real.mant_dig == 64) - { - if (upper < 4) - { - mybig[0] = (mnt & ((1L << (4 - upper)) - 1)) << 56 + upper; - mybig[1] = (mnt >> (4 - upper)) & ((1L << 60) - 1); - mybig[2] = mnt >> 64 - upper; - } - else - { - mybig[0] = (mnt & ((1L << (T.mant_dig - upper)) - 1)) << 60 - (T.mant_dig - upper); - mybig[1] = mnt >> (T.mant_dig - upper); - } - } - else - { - if (upper < T.mant_dig) - { - mybig[0] = (mnt & ((1L << (T.mant_dig - upper)) - 1)) << 60 - (T.mant_dig - upper); - mybig[1] = mnt >> (T.mant_dig - upper); - } - else - mybig[0] = mnt << (upper - T.mant_dig); - } - - dec_buf[--left] = '0'; // 0 left of the dot - dec_buf[right++] = '.'; - - static if (g) - { - // precision starts at first non zero, so we move start - // to the right, until we found first non zero, thus avoiding - // a premature break of the loop - bool found = false; - start = left + 1; - } - - // Generation of digits by consecutive multiplication by 10. - int lsu = 0; // Least significant ulong; when it get's zero, we can ignore it furtheron - while ((lsu < count - 1 || mybig[$ - 1] != 0) && right - start - 1 < f.precision) - { - ulong over = 0; - foreach (i;lsu .. count) - { - mybig[i] = mybig[i] * 10 + over; - over = mybig[i] >> 60; - mybig[i] &= (1L << 60) - 1; - } - if (mybig[lsu] == 0) - ++lsu; - - dec_buf[right++] = cast(byte) ('0' + over); - - static if (g) - { - if (dec_buf[right - 1] != '0') - found = true; - else if (!found) - start++; - } - } - - static if (g) start = 2; - - if (lsu >= count - 1 && mybig[count - 1] == 0) - rc = RoundingClass.ZERO; - else if (lsu == count - 1 && mybig[lsu] == 1L << 59) - rc = RoundingClass.FIVE; - else - { - ulong over = 0; - foreach (i;lsu .. count) - { - mybig[i] = mybig[i] * 10 + over; - over = mybig[i] >> 60; - mybig[i] &= (1L << 60) - 1; - } - rc = over >= 5 ? RoundingClass.UPPER : RoundingClass.LOWER; - } - } - else - { - // medium sized number, probably with integer and fractional digits - // this is fastest, because both parts fit into a ulong each - ulong int_part = mnt >> (T.mant_dig - 1 - exp); - ulong frac_part = mnt & ((1L << (T.mant_dig - 1 - exp)) - 1); - - // for x87 reals the mantiassa might be up to 3 bits too long - // we need to save these bits as a tail and handle this separately - static if (is(T == real) && real.mant_dig == 64) - { - ulong tail = 0; - ulong tail_length = 0; - if (exp < 3) - { - tail = frac_part & ((1L << (3 - exp)) - 1); - tail_length = 3 - exp; - frac_part >>= 3 - exp; - exp = 3; - } - } - - static if (g) auto found = int_part > 0; // searching first non zero - - // creating int part - if (int_part == 0) - dec_buf[--left] = '0'; - else - { - import core.bitop : bsr; - left = right = start = int_part.bsr * 100 / 332 + 4; - - while (int_part > 0) - { - dec_buf[--left] = '0' + (int_part % 10); - int_part /= 10; - } - } - - static if (g) size_t save_start = right; - - dec_buf[right++] = '.'; - - // creating frac part - static if (g) start = left + (found ? 0 : 1); - while (frac_part != 0 && right - start - 1 < f.precision) - { - frac_part *= 10; - static if (is(T == real) && real.mant_dig == 64) - { - if (tail_length > 0) - { - // together this is *= 10; - tail *= 5; - tail_length--; - - frac_part += tail >> tail_length; - if (tail_length > 0) - tail &= (1L << tail_length) - 1; - } - } - dec_buf[right++] = cast(byte)('0' + (frac_part >> (T.mant_dig - 1 - exp))); - - static if (g) - { - if (dec_buf[right - 1] != '0') - found = true; - else if (!found) - start++; - } - - frac_part &= ((1L << (T.mant_dig - 1 - exp)) - 1); - } - - static if (g) start = save_start; - - if (frac_part == 0) - rc = RoundingClass.ZERO; - else - { - frac_part *= 10; - auto nextDigit = frac_part >> (T.mant_dig - 1 - exp); - frac_part &= ((1L << (T.mant_dig - 1 - exp)) - 1); - - if (nextDigit == 5 && frac_part == 0) - rc = RoundingClass.FIVE; - else if (nextDigit >= 5) - rc = RoundingClass.UPPER; - else - rc = RoundingClass.LOWER; - } - } - - if (round(dec_buf, left, right, rc, sgn == "-")) left--; - - while (right > start + 1 && dec_buf[right - 1] == '0') right--; - - static if (g) - writeAligned(w, sgn, dec_buf[left .. start], dec_buf[start .. right], "", f, PrecisionType.allDigits); - else - writeAligned(w, sgn, dec_buf[left .. start], dec_buf[start .. right], "", f, PrecisionType.fractionalDigits); -} - -@safe unittest -{ - auto f = FormatSpec!dchar(""); - f.spec = 'f'; - assert(printFloat(float.nan, f) == "nan"); - assert(printFloat(-float.nan, f) == "-nan"); - assert(printFloat(float.infinity, f) == "inf"); - assert(printFloat(-float.infinity, f) == "-inf"); - assert(printFloat(0.0f, f) == "0.000000"); - assert(printFloat(-0.0f, f) == "-0.000000"); - // cast needed due to https://issues.dlang.org/show_bug.cgi?id=20361 - assert(printFloat(cast(float) 1e-40, f) == "0.000000"); - assert(printFloat(cast(float) -1e-40, f) == "-0.000000"); - assert(printFloat(1e-30f, f) == "0.000000"); - assert(printFloat(-1e-30f, f) == "-0.000000"); - assert(printFloat(1e-10f, f) == "0.000000"); - assert(printFloat(-1e-10f, f) == "-0.000000"); - assert(printFloat(0.1f, f) == "0.100000"); - assert(printFloat(-0.1f, f) == "-0.100000"); - assert(printFloat(10.0f, f) == "10.000000"); - assert(printFloat(-10.0f, f) == "-10.000000"); - assert(printFloat(1e30f, f) == "1000000015047466219876688855040.000000"); - assert(printFloat(-1e30f, f) == "-1000000015047466219876688855040.000000"); - - import std.math.operations : nextUp, nextDown; - assert(printFloat(nextUp(0.0f), f) == "0.000000"); - assert(printFloat(nextDown(-0.0f), f) == "-0.000000"); -} - -@safe unittest -{ - auto f = FormatSpec!dchar(""); - f.spec = 'f'; - f.width = 20; - f.precision = 10; - - assert(printFloat(float.nan, f) == " nan"); - assert(printFloat(-float.nan, f) == " -nan"); - assert(printFloat(float.infinity, f) == " inf"); - assert(printFloat(-float.infinity, f) == " -inf"); - assert(printFloat(0.0f, f) == " 0.0000000000"); - assert(printFloat(-0.0f, f) == " -0.0000000000"); - // cast needed due to https://issues.dlang.org/show_bug.cgi?id=20361 - assert(printFloat(cast(float) 1e-40, f) == " 0.0000000000"); - assert(printFloat(cast(float) -1e-40, f) == " -0.0000000000"); - assert(printFloat(1e-30f, f) == " 0.0000000000"); - assert(printFloat(-1e-30f, f) == " -0.0000000000"); - assert(printFloat(1e-10f, f) == " 0.0000000001"); - assert(printFloat(-1e-10f, f) == " -0.0000000001"); - assert(printFloat(0.1f, f) == " 0.1000000015"); - assert(printFloat(-0.1f, f) == " -0.1000000015"); - assert(printFloat(10.0f, f) == " 10.0000000000"); - assert(printFloat(-10.0f, f) == " -10.0000000000"); - assert(printFloat(1e30f, f) == "1000000015047466219876688855040.0000000000"); - assert(printFloat(-1e30f, f) == "-1000000015047466219876688855040.0000000000"); - - import std.math.operations : nextUp, nextDown; - assert(printFloat(nextUp(0.0f), f) == " 0.0000000000"); - assert(printFloat(nextDown(-0.0f), f) == " -0.0000000000"); -} - -@safe unittest -{ - auto f = FormatSpec!dchar(""); - f.spec = 'f'; - f.width = 20; - f.precision = 10; - f.flDash = true; - - assert(printFloat(float.nan, f) == "nan "); - assert(printFloat(-float.nan, f) == "-nan "); - assert(printFloat(float.infinity, f) == "inf "); - assert(printFloat(-float.infinity, f) == "-inf "); - assert(printFloat(0.0f, f) == "0.0000000000 "); - assert(printFloat(-0.0f, f) == "-0.0000000000 "); - // cast needed due to https://issues.dlang.org/show_bug.cgi?id=20361 - assert(printFloat(cast(float) 1e-40, f) == "0.0000000000 "); - assert(printFloat(cast(float) -1e-40, f) == "-0.0000000000 "); - assert(printFloat(1e-30f, f) == "0.0000000000 "); - assert(printFloat(-1e-30f, f) == "-0.0000000000 "); - assert(printFloat(1e-10f, f) == "0.0000000001 "); - assert(printFloat(-1e-10f, f) == "-0.0000000001 "); - assert(printFloat(0.1f, f) == "0.1000000015 "); - assert(printFloat(-0.1f, f) == "-0.1000000015 "); - assert(printFloat(10.0f, f) == "10.0000000000 "); - assert(printFloat(-10.0f, f) == "-10.0000000000 "); - assert(printFloat(1e30f, f) == "1000000015047466219876688855040.0000000000"); - assert(printFloat(-1e30f, f) == "-1000000015047466219876688855040.0000000000"); - - import std.math.operations : nextUp, nextDown; - assert(printFloat(nextUp(0.0f), f) == "0.0000000000 "); - assert(printFloat(nextDown(-0.0f), f) == "-0.0000000000 "); -} - -@safe unittest -{ - auto f = FormatSpec!dchar(""); - f.spec = 'f'; - f.width = 20; - f.precision = 10; - f.flZero = true; - - assert(printFloat(float.nan, f) == " nan"); - assert(printFloat(-float.nan, f) == " -nan"); - assert(printFloat(float.infinity, f) == " inf"); - assert(printFloat(-float.infinity, f) == " -inf"); - assert(printFloat(0.0f, f) == "000000000.0000000000"); - assert(printFloat(-0.0f, f) == "-00000000.0000000000"); - // cast needed due to https://issues.dlang.org/show_bug.cgi?id=20361 - assert(printFloat(cast(float) 1e-40, f) == "000000000.0000000000"); - assert(printFloat(cast(float) -1e-40, f) == "-00000000.0000000000"); - assert(printFloat(1e-30f, f) == "000000000.0000000000"); - assert(printFloat(-1e-30f, f) == "-00000000.0000000000"); - assert(printFloat(1e-10f, f) == "000000000.0000000001"); - assert(printFloat(-1e-10f, f) == "-00000000.0000000001"); - assert(printFloat(0.1f, f) == "000000000.1000000015"); - assert(printFloat(-0.1f, f) == "-00000000.1000000015"); - assert(printFloat(10.0f, f) == "000000010.0000000000"); - assert(printFloat(-10.0f, f) == "-00000010.0000000000"); - assert(printFloat(1e30f, f) == "1000000015047466219876688855040.0000000000"); - assert(printFloat(-1e30f, f) == "-1000000015047466219876688855040.0000000000"); - - import std.math.operations : nextUp, nextDown; - assert(printFloat(nextUp(0.0f), f) == "000000000.0000000000"); - assert(printFloat(nextDown(-0.0f), f) == "-00000000.0000000000"); -} - -@safe unittest -{ - import std.math.hardware; // cannot be selective, because FloatingPointControl might not be defined - - // std.math's FloatingPointControl isn't available on all target platforms - static if (is(FloatingPointControl)) - { - FloatingPointControl fpctrl; - - auto f = FormatSpec!dchar(""); - f.spec = 'f'; - f.precision = 0; - - fpctrl.rounding = FloatingPointControl.roundToNearest; - - /* - assert(printFloat(11.5f, f) == "12"); - assert(printFloat(12.5f, f) == "13"); - assert(printFloat(11.7f, f) == "12"); - assert(printFloat(11.3f, f) == "11"); - assert(printFloat(11.0f, f) == "11"); - assert(printFloat(-11.5f, f) == "-12"); - assert(printFloat(-12.5f, f) == "-13"); - assert(printFloat(-11.7f, f) == "-12"); - assert(printFloat(-11.3f, f) == "-11"); - assert(printFloat(-11.0f, f) == "-11"); - */ - - assert(printFloat(11.5f, f) == "12"); - assert(printFloat(12.5f, f) == "12"); - assert(printFloat(11.7f, f) == "12"); - assert(printFloat(11.3f, f) == "11"); - assert(printFloat(11.0f, f) == "11"); - assert(printFloat(-11.5f, f) == "-12"); - assert(printFloat(-12.5f, f) == "-12"); - assert(printFloat(-11.7f, f) == "-12"); - assert(printFloat(-11.3f, f) == "-11"); - assert(printFloat(-11.0f, f) == "-11"); - - fpctrl.rounding = FloatingPointControl.roundToZero; - - assert(printFloat(11.5f, f) == "11"); - assert(printFloat(12.5f, f) == "12"); - assert(printFloat(11.7f, f) == "11"); - assert(printFloat(11.3f, f) == "11"); - assert(printFloat(11.0f, f) == "11"); - assert(printFloat(-11.5f, f) == "-11"); - assert(printFloat(-12.5f, f) == "-12"); - assert(printFloat(-11.7f, f) == "-11"); - assert(printFloat(-11.3f, f) == "-11"); - assert(printFloat(-11.0f, f) == "-11"); - - fpctrl.rounding = FloatingPointControl.roundUp; - - assert(printFloat(11.5f, f) == "12"); - assert(printFloat(12.5f, f) == "13"); - assert(printFloat(11.7f, f) == "12"); - assert(printFloat(11.3f, f) == "12"); - assert(printFloat(11.0f, f) == "11"); - assert(printFloat(-11.5f, f) == "-11"); - assert(printFloat(-12.5f, f) == "-12"); - assert(printFloat(-11.7f, f) == "-11"); - assert(printFloat(-11.3f, f) == "-11"); - assert(printFloat(-11.0f, f) == "-11"); - - fpctrl.rounding = FloatingPointControl.roundDown; - - assert(printFloat(11.5f, f) == "11"); - assert(printFloat(12.5f, f) == "12"); - assert(printFloat(11.7f, f) == "11"); - assert(printFloat(11.3f, f) == "11"); - assert(printFloat(11.0f, f) == "11"); - assert(printFloat(-11.5f, f) == "-12"); - assert(printFloat(-12.5f, f) == "-13"); - assert(printFloat(-11.7f, f) == "-12"); - assert(printFloat(-11.3f, f) == "-12"); - assert(printFloat(-11.0f, f) == "-11"); - } -} - -@safe unittest -{ - auto f = FormatSpec!dchar(""); - f.spec = 'f'; - assert(printFloat(double.nan, f) == "nan"); - assert(printFloat(-double.nan, f) == "-nan"); - assert(printFloat(double.infinity, f) == "inf"); - assert(printFloat(-double.infinity, f) == "-inf"); - assert(printFloat(0.0, f) == "0.000000"); - assert(printFloat(-0.0, f) == "-0.000000"); - // / 1000 needed due to https://issues.dlang.org/show_bug.cgi?id=20361 - assert(printFloat(1e-307 / 1000, f) == "0.000000"); - assert(printFloat(-1e-307 / 1000, f) == "-0.000000"); - assert(printFloat(1e-30, f) == "0.000000"); - assert(printFloat(-1e-30, f) == "-0.000000"); - assert(printFloat(1e-10, f) == "0.000000"); - assert(printFloat(-1e-10, f) == "-0.000000"); - assert(printFloat(0.1, f) == "0.100000"); - assert(printFloat(-0.1, f) == "-0.100000"); - assert(printFloat(10.0, f) == "10.000000"); - assert(printFloat(-10.0, f) == "-10.000000"); - assert(printFloat(1e300, f) == - "100000000000000005250476025520442024870446858110815915491585411551180245798890819578637137508044786" - ~"404370444383288387817694252323536043057564479218478670698284838720092657580373783023379478809005936" - ~"895323497079994508111903896764088007465274278014249457925878882005684283811566947219638686545940054" - ~"0160.000000"); - assert(printFloat(-1e300, f) == - "-100000000000000005250476025520442024870446858110815915491585411551180245798890819578637137508044786" - ~"404370444383288387817694252323536043057564479218478670698284838720092657580373783023379478809005936" - ~"895323497079994508111903896764088007465274278014249457925878882005684283811566947219638686545940054" - ~"0160.000000"); - - import std.math.operations : nextUp, nextDown; - assert(printFloat(nextUp(0.0), f) == "0.000000"); - assert(printFloat(nextDown(-0.0), f) == "-0.000000"); -} - -@safe unittest -{ - static if (real.mant_dig > 64) - { - pragma(msg, "printFloat tests disabled because of unsupported `real` format"); - } - else - { - auto f = FormatSpec!dchar(""); - f.spec = 'f'; - assert(printFloat(real.nan, f) == "nan"); - assert(printFloat(-real.nan, f) == "-nan"); - assert(printFloat(real.infinity, f) == "inf"); - assert(printFloat(-real.infinity, f) == "-inf"); - assert(printFloat(0.0L, f) == "0.000000"); - assert(printFloat(-0.0L, f) == "-0.000000"); - } - - static if (real.mant_dig == 64) - { - assert(printFloat(1e-4940L, f) == "0.000000"); - assert(printFloat(-1e-4940L, f) == "-0.000000"); - assert(printFloat(1e-30L, f) == "0.000000"); - assert(printFloat(-1e-30L, f) == "-0.000000"); - assert(printFloat(1e-10L, f) == "0.000000"); - assert(printFloat(-1e-10L, f) == "-0.000000"); - assert(printFloat(0.1L, f) == "0.100000"); - assert(printFloat(-0.1L, f) == "-0.100000"); - assert(printFloat(10.0L, f) == "10.000000"); - assert(printFloat(-10.0L, f) == "-10.000000"); - version (Windows) {} // https://issues.dlang.org/show_bug.cgi?id=20972 - else - { - auto result1 = printFloat(1e4000L, f); - assert(result1.length == 4007 && result1[0 .. 40] == "9999999999999999999965463873099623784932"); - auto result2 = printFloat(-1e4000L, f); - assert(result2.length == 4008 && result2[0 .. 40] == "-999999999999999999996546387309962378493"); - } - - import std.math.operations : nextUp, nextDown; - assert(printFloat(nextUp(0.0L), f) == "0.000000"); - assert(printFloat(nextDown(-0.0L), f) == "-0.000000"); - } -} - -@safe unittest -{ - import std.exception : assertCTFEable; - import std.math.exponential : log2; - import std.math.operations : nextDown; - - assertCTFEable!( - { - // log2 is broken for x87-reals on some computers in CTFE - // the following tests excludes these computers from the tests - // (https://issues.dlang.org/show_bug.cgi?id=21757) - enum test = cast(int) log2(3.05e2312L); - static if (real.mant_dig == 64 && test == 7681) - { - auto f = FormatSpec!dchar(""); - f.spec = 'f'; - assert(printFloat(real.infinity, f) == "inf"); - assert(printFloat(10.0L, f) == "10.000000"); - assert(printFloat(2.6080L, f) == "2.608000"); - auto result1 = printFloat(3.05e2312L, f); - assert(result1.length == 2320); - assert(result1[0 .. 20] == "30499999999999999999"); - - f.precision = 60; - assert(printFloat(2.65e-54L, f) == - "0.000000000000000000000000000000000000000000000000000002650000"); - - /* - commented out, because CTFE is currently too slow for 5000 digits with extreme values - - f.precision = 5000; - auto result2 = printFloat(1.2119e-4822L, f); - assert(result2.length == 5002); - assert(result2[$ - 20 .. $] == "60076763752233836613"); - auto result3 = printFloat(real.min_normal, f); - assert(result3.length == 5002); - assert(result3[$ - 20 .. $] == "47124010882722980874"); - auto result4 = printFloat(real.min_normal.nextDown, f); - assert(result4.length == 5002); - assert(result4[$ - 20 .. $] == "52925846892214823939"); - */ - } - }); -} - -@safe unittest -{ - auto f = FormatSpec!dchar(""); - f.spec = 'f'; - - import std.math.operations : nextUp; - - double eps = nextUp(0.0); - f.precision = 1000; - assert(printFloat(eps, f) == - "0.0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" - ~"00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" - ~"00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" - ~"00000000000000000000000000000049406564584124654417656879286822137236505980261432476442558568250067" - ~"55072702087518652998363616359923797965646954457177309266567103559397963987747960107818781263007131" - ~"90311404527845817167848982103688718636056998730723050006387409153564984387312473397273169615140031" - ~"71538539807412623856559117102665855668676818703956031062493194527159149245532930545654440112748012" - ~"97099995419319894090804165633245247571478690147267801593552386115501348035264934720193790268107107" - ~"49170333222684475333572083243193609238289345836806010601150616980975307834227731832924790498252473" - ~"07763759272478746560847782037344696995336470179726777175851256605511991315048911014510378627381672" - ~"509558373897335989937"); - - f.precision = 0; - assert(printFloat(double.max, f) == - "179769313486231570814527423731704356798070567525844996598917476803157260780028538760589558632766878" - ~"17154045895351438246423432132688946418276846754670353751698604991057655128207624549009038932894407" - ~"58685084551339423045832369032229481658085593321233482747978262041447231687381771809192998812504040" - ~"26184124858368"); - - f.precision = 50; - assert(printFloat(double.epsilon, f) == - "0.00000000000000022204460492503130808472633361816406"); - - f.precision = 10; - assert(printFloat(1.0/3.0, f) == "0.3333333333"); - assert(printFloat(1.0/7.0, f) == "0.1428571429"); - assert(printFloat(1.0/9.0, f) == "0.1111111111"); -} - -@safe unittest -{ - auto f = FormatSpec!dchar(""); - f.spec = 'f'; - f.precision = 15; - - import std.math.constants : E, PI, PI_2, PI_4, M_1_PI, M_2_PI, M_2_SQRTPI, - LN10, LN2, LOG2, LOG2E, LOG2T, LOG10E, SQRT2, SQRT1_2; - - assert(printFloat(cast(double) E, f) == "2.718281828459045"); - assert(printFloat(cast(double) PI, f) == "3.141592653589793"); - assert(printFloat(cast(double) PI_2, f) == "1.570796326794897"); - assert(printFloat(cast(double) PI_4, f) == "0.785398163397448"); - assert(printFloat(cast(double) M_1_PI, f) == "0.318309886183791"); - assert(printFloat(cast(double) M_2_PI, f) == "0.636619772367581"); - assert(printFloat(cast(double) M_2_SQRTPI, f) == "1.128379167095513"); - assert(printFloat(cast(double) LN10, f) == "2.302585092994046"); - assert(printFloat(cast(double) LN2, f) == "0.693147180559945"); - assert(printFloat(cast(double) LOG2, f) == "0.301029995663981"); - assert(printFloat(cast(double) LOG2E, f) == "1.442695040888963"); - assert(printFloat(cast(double) LOG2T, f) == "3.321928094887362"); - assert(printFloat(cast(double) LOG10E, f) == "0.434294481903252"); - assert(printFloat(cast(double) SQRT2, f) == "1.414213562373095"); - assert(printFloat(cast(double) SQRT1_2, f) == "0.707106781186548"); -} - -// for 100% coverage -@safe unittest -{ - auto f = FormatSpec!dchar(""); - f.spec = 'f'; - f.precision = 1; - assert(printFloat(9.99, f) == "10.0"); - - import std.math.operations : nextUp; - - float eps = nextUp(0.0f); - - f.precision = 148; - assert(printFloat(eps, f) == - "0.0000000000000000000000000000000000000000000014012984643248170709237295832899161312802619418765157" - ~"717570682838897910826858606014866381883621215820312"); - - f.precision = 149; - assert(printFloat(eps, f) == - "0.0000000000000000000000000000000000000000000014012984643248170709237295832899161312802619418765157" - ~"7175706828388979108268586060148663818836212158203125"); -} - -private void printFloatG(Writer, T, Char)(auto ref Writer w, const(T) val, - FormatSpec!Char f, string sgn, int exp, ulong mnt, bool is_upper) -if (is(T == float) || is(T == double) - || (is(T == real) && (T.mant_dig == double.mant_dig || T.mant_dig == 64))) -{ - import core.math : abs = fabs; - - if (f.precision == f.UNSPECIFIED) - f.precision = 6; - - if (f.precision == 0) - f.precision = 1; - - import std.math.hardware; - import std.format.internal.write : RoundingMode; - - auto rm = RoundingMode.toNearestTiesToEven; - - if (!__ctfe) - { - // std.math's FloatingPointControl isn't available on all target platforms - static if (is(FloatingPointControl)) - { - switch (FloatingPointControl.rounding) - { - case FloatingPointControl.roundUp: - rm = RoundingMode.up; - break; - case FloatingPointControl.roundDown: - rm = RoundingMode.down; - break; - case FloatingPointControl.roundToZero: - rm = RoundingMode.toZero; - break; - case FloatingPointControl.roundToNearest: - rm = RoundingMode.toNearestTiesToEven; - break; - default: assert(false, "Unknown floating point rounding mode"); - } - } - } - - bool useE = false; - - final switch (rm) - { - case RoundingMode.up: - useE = abs(val) >= 10.0 ^^ f.precision - (val > 0 ? 1 : 0) - || abs(val) < 0.0001 - (val > 0 ? (10.0 ^^ (-4 - f.precision)) : 0); - break; - case RoundingMode.down: - useE = abs(val) >= 10.0 ^^ f.precision - (val < 0 ? 1 : 0) - || abs(val) < 0.0001 - (val < 0 ? (10.0 ^^ (-4 - f.precision)) : 0); - break; - case RoundingMode.toZero: - useE = abs(val) >= 10.0 ^^ f.precision - || abs(val) < 0.0001; - break; - case RoundingMode.toNearestTiesToEven: - case RoundingMode.toNearestTiesAwayFromZero: - useE = abs(val) >= 10.0 ^^ f.precision - 0.5 - || abs(val) < 0.0001 - 0.5 * (10.0 ^^ (-4 - f.precision)); - break; - } - - if (useE) - return printFloatE!true(w, val, f, sgn, exp, mnt, is_upper); - else - return printFloatF!true(w, val, f, sgn, exp, mnt, is_upper); -} - -@safe unittest -{ - // This one tests the switch between e-like and f-like output. - // There is a small gap left between the two, where the used - // variation is not clearly defined. This is intentional and due - // to the way, D handles floating point numbers. On different - // computers with different reals the results may vary in this gap. - - import std.math.operations : nextDown, nextUp; - import std.math.hardware; // cannot be selective, because FloatingPointControl might not be defined - - auto f = FormatSpec!dchar(""); - f.spec = 'g'; - - double val = 999999.5; - assert(printFloat(val.nextUp, f) == "1e+06"); - val = nextDown(val); - assert(printFloat(val.nextDown, f) == "999999"); - - val = 0.00009999995; - assert(printFloat(val.nextUp, f) == "0.0001"); - val = nextDown(val); - assert(printFloat(val.nextDown, f) == "9.99999e-05"); - - static if (is(FloatingPointControl)) - { - FloatingPointControl fpctrl; - - fpctrl.rounding = FloatingPointControl.roundToZero; - - val = 1000000; - assert(printFloat(val.nextUp, f) == "1e+06"); - val = nextDown(val); - assert(printFloat(val.nextDown, f) == "999999"); - - val = 0.0001; - assert(printFloat(val.nextUp, f) == "0.0001"); - val = nextDown(val); - assert(printFloat(val.nextDown, f) == "9.99999e-05"); - - fpctrl.rounding = FloatingPointControl.roundUp; - - val = 999999; - assert(printFloat(val.nextUp, f) == "1e+06"); - val = nextDown(val); - assert(printFloat(val.nextDown, f) == "999999"); - - // 0.0000999999 is actually represented as 0.0000999998999..., which is - // less than 0.0000999999, so we need to use nextUp to get the corner case here - val = nextUp(0.0000999999); - assert(printFloat(val.nextUp, f) == "0.0001"); - val = nextDown(val); - assert(printFloat(val.nextDown, f) == "9.99999e-05"); - - fpctrl.rounding = FloatingPointControl.roundDown; - - val = 1000000; - assert(printFloat(val.nextUp, f) == "1e+06"); - val = nextDown(val); - assert(printFloat(val.nextDown, f) == "999999"); - - val = 0.0001; - assert(printFloat(val.nextUp, f) == "0.0001"); - val = nextDown(val); - assert(printFloat(val.nextDown, f) == "9.99999e-05"); - } -} - -@safe unittest -{ - auto f = FormatSpec!dchar(""); - f.spec = 'g'; - assert(printFloat(float.nan, f) == "nan"); - assert(printFloat(-float.nan, f) == "-nan"); - assert(printFloat(float.infinity, f) == "inf"); - assert(printFloat(-float.infinity, f) == "-inf"); - assert(printFloat(0.0f, f) == "0"); - assert(printFloat(-0.0f, f) == "-0"); - - // cast needed due to https://issues.dlang.org/show_bug.cgi?id=20361 - assert(printFloat(cast(float) 1e-40, f) == "9.99995e-41"); - assert(printFloat(cast(float) -1e-40, f) == "-9.99995e-41"); - assert(printFloat(1e-30f, f) == "1e-30"); - assert(printFloat(-1e-30f, f) == "-1e-30"); - assert(printFloat(1e-10f, f) == "1e-10"); - assert(printFloat(-1e-10f, f) == "-1e-10"); - assert(printFloat(0.1f, f) == "0.1"); - assert(printFloat(-0.1f, f) == "-0.1"); - assert(printFloat(10.0f, f) == "10"); - assert(printFloat(-10.0f, f) == "-10"); - assert(printFloat(1e30f, f) == "1e+30"); - assert(printFloat(-1e30f, f) == "-1e+30"); - - import std.math.operations : nextUp, nextDown; - assert(printFloat(nextUp(0.0f), f) == "1.4013e-45"); - assert(printFloat(nextDown(-0.0f), f) == "-1.4013e-45"); -} - -@safe unittest -{ - auto f = FormatSpec!dchar(""); - f.spec = 'g'; - f.width = 20; - f.precision = 10; - - assert(printFloat(float.nan, f) == " nan"); - assert(printFloat(-float.nan, f) == " -nan"); - assert(printFloat(float.infinity, f) == " inf"); - assert(printFloat(-float.infinity, f) == " -inf"); - assert(printFloat(0.0f, f) == " 0"); - assert(printFloat(-0.0f, f) == " -0"); - // cast needed due to https://issues.dlang.org/show_bug.cgi?id=20361 - assert(printFloat(cast(float) 1e-40, f) == " 9.999946101e-41"); - assert(printFloat(cast(float) -1e-40, f) == " -9.999946101e-41"); - assert(printFloat(1e-30f, f) == " 1.000000003e-30"); - assert(printFloat(-1e-30f, f) == " -1.000000003e-30"); - assert(printFloat(1e-10f, f) == " 1.000000013e-10"); - assert(printFloat(-1e-10f, f) == " -1.000000013e-10"); - assert(printFloat(0.1f, f) == " 0.1000000015"); - assert(printFloat(-0.1f, f) == " -0.1000000015"); - assert(printFloat(10.0f, f) == " 10"); - assert(printFloat(-10.0f, f) == " -10"); - assert(printFloat(1e30f, f) == " 1.000000015e+30"); - assert(printFloat(-1e30f, f) == " -1.000000015e+30"); - - import std.math.operations : nextUp, nextDown; - assert(printFloat(nextUp(0.0f), f) == " 1.401298464e-45"); - assert(printFloat(nextDown(-0.0f), f) == " -1.401298464e-45"); -} - -@safe unittest -{ - auto f = FormatSpec!dchar(""); - f.spec = 'g'; - f.width = 20; - f.precision = 10; - f.flDash = true; - - assert(printFloat(float.nan, f) == "nan "); - assert(printFloat(-float.nan, f) == "-nan "); - assert(printFloat(float.infinity, f) == "inf "); - assert(printFloat(-float.infinity, f) == "-inf "); - assert(printFloat(0.0f, f) == "0 "); - assert(printFloat(-0.0f, f) == "-0 "); - - // cast needed due to https://issues.dlang.org/show_bug.cgi?id=20361 - assert(printFloat(cast(float) 1e-40, f) == "9.999946101e-41 "); - assert(printFloat(cast(float) -1e-40, f) == "-9.999946101e-41 "); - assert(printFloat(1e-30f, f) == "1.000000003e-30 "); - assert(printFloat(-1e-30f, f) == "-1.000000003e-30 "); - assert(printFloat(1e-10f, f) == "1.000000013e-10 "); - assert(printFloat(-1e-10f, f) == "-1.000000013e-10 "); - assert(printFloat(0.1f, f) == "0.1000000015 "); - assert(printFloat(-0.1f, f) == "-0.1000000015 "); - assert(printFloat(10.0f, f) == "10 "); - assert(printFloat(-10.0f, f) == "-10 "); - assert(printFloat(1e30f, f) == "1.000000015e+30 "); - assert(printFloat(-1e30f, f) == "-1.000000015e+30 "); - - import std.math.operations : nextUp, nextDown; - assert(printFloat(nextUp(0.0f), f) == "1.401298464e-45 "); - assert(printFloat(nextDown(-0.0f), f) == "-1.401298464e-45 "); -} - -@safe unittest -{ - auto f = FormatSpec!dchar(""); - f.spec = 'g'; - f.width = 20; - f.precision = 10; - f.flZero = true; - - assert(printFloat(float.nan, f) == " nan"); - assert(printFloat(-float.nan, f) == " -nan"); - assert(printFloat(float.infinity, f) == " inf"); - assert(printFloat(-float.infinity, f) == " -inf"); - assert(printFloat(0.0f, f) == "00000000000000000000"); - assert(printFloat(-0.0f, f) == "-0000000000000000000"); - - // cast needed due to https://issues.dlang.org/show_bug.cgi?id=20361 - assert(printFloat(cast(float) 1e-40, f) == "000009.999946101e-41"); - assert(printFloat(cast(float) -1e-40, f) == "-00009.999946101e-41"); - assert(printFloat(1e-30f, f) == "000001.000000003e-30"); - assert(printFloat(-1e-30f, f) == "-00001.000000003e-30"); - assert(printFloat(1e-10f, f) == "000001.000000013e-10"); - assert(printFloat(-1e-10f, f) == "-00001.000000013e-10"); - assert(printFloat(0.1f, f) == "000000000.1000000015"); - assert(printFloat(-0.1f, f) == "-00000000.1000000015"); - assert(printFloat(10.0f, f) == "00000000000000000010"); - assert(printFloat(-10.0f, f) == "-0000000000000000010"); - assert(printFloat(1e30f, f) == "000001.000000015e+30"); - assert(printFloat(-1e30f, f) == "-00001.000000015e+30"); - - import std.math.operations : nextUp, nextDown; - assert(printFloat(nextUp(0.0f), f) == "000001.401298464e-45"); - assert(printFloat(nextDown(-0.0f), f) == "-00001.401298464e-45"); -} - -@safe unittest -{ - auto f = FormatSpec!dchar(""); - f.spec = 'g'; - f.precision = 10; - f.flHash = true; - - assert(printFloat(float.nan, f) == "nan"); - assert(printFloat(-float.nan, f) == "-nan"); - assert(printFloat(float.infinity, f) == "inf"); - assert(printFloat(-float.infinity, f) == "-inf"); - assert(printFloat(0.0f, f) == "0.000000000"); - assert(printFloat(-0.0f, f) == "-0.000000000"); - - // cast needed due to https://issues.dlang.org/show_bug.cgi?id=20361 - assert(printFloat(cast(float) 1e-40, f) == "9.999946101e-41"); - assert(printFloat(cast(float) -1e-40, f) == "-9.999946101e-41"); - assert(printFloat(1e-30f, f) == "1.000000003e-30"); - assert(printFloat(-1e-30f, f) == "-1.000000003e-30"); - assert(printFloat(1e-10f, f) == "1.000000013e-10"); - assert(printFloat(-1e-10f, f) == "-1.000000013e-10"); - assert(printFloat(0.1f, f) == "0.1000000015"); - assert(printFloat(-0.1f, f) == "-0.1000000015"); - assert(printFloat(10.0f, f) == "10.00000000"); - assert(printFloat(-10.0f, f) == "-10.00000000"); - assert(printFloat(1e30f, f) == "1.000000015e+30"); - assert(printFloat(-1e30f, f) == "-1.000000015e+30"); - - import std.math.operations : nextUp, nextDown; - assert(printFloat(nextUp(0.0f), f) == "1.401298464e-45"); - assert(printFloat(nextDown(-0.0f), f) == "-1.401298464e-45"); -} - -@safe unittest -{ - import std.math.hardware; // cannot be selective, because FloatingPointControl might not be defined - - // std.math's FloatingPointControl isn't available on all target platforms - static if (is(FloatingPointControl)) - { - FloatingPointControl fpctrl; - - char[256] buf; - auto f = FormatSpec!dchar(""); - f.spec = 'g'; - f.precision = 2; - - fpctrl.rounding = FloatingPointControl.roundToNearest; - - /* - assert(printFloat(11.5f, f, RoundingMode.toNearestTiesAwayFromZero) == "12"); - assert(printFloat(12.5f, f, RoundingMode.toNearestTiesAwayFromZero) == "13"); - assert(printFloat(11.7f, f, RoundingMode.toNearestTiesAwayFromZero) == "12"); - assert(printFloat(11.3f, f, RoundingMode.toNearestTiesAwayFromZero) == "11"); - assert(printFloat(11.0f, f, RoundingMode.toNearestTiesAwayFromZero) == "11"); - assert(printFloat(-11.5f, f, RoundingMode.toNearestTiesAwayFromZero) == "-12"); - assert(printFloat(-12.5f, f, RoundingMode.toNearestTiesAwayFromZero) == "-13"); - assert(printFloat(-11.7f, f, RoundingMode.toNearestTiesAwayFromZero) == "-12"); - assert(printFloat(-11.3f, f, RoundingMode.toNearestTiesAwayFromZero) == "-11"); - assert(printFloat(-11.0f, f, RoundingMode.toNearestTiesAwayFromZero) == "-11"); - */ - - // ties to even - assert(printFloat(11.5f, f) == "12"); - assert(printFloat(12.5f, f) == "12"); - assert(printFloat(11.7f, f) == "12"); - assert(printFloat(11.3f, f) == "11"); - assert(printFloat(11.0f, f) == "11"); - assert(printFloat(-11.5f, f) == "-12"); - assert(printFloat(-12.5f, f) == "-12"); - assert(printFloat(-11.7f, f) == "-12"); - assert(printFloat(-11.3f, f) == "-11"); - assert(printFloat(-11.0f, f) == "-11"); - - fpctrl.rounding = FloatingPointControl.roundToZero; - - assert(printFloat(11.5f, f) == "11"); - assert(printFloat(12.5f, f) == "12"); - assert(printFloat(11.7f, f) == "11"); - assert(printFloat(11.3f, f) == "11"); - assert(printFloat(11.0f, f) == "11"); - assert(printFloat(-11.5f, f) == "-11"); - assert(printFloat(-12.5f, f) == "-12"); - assert(printFloat(-11.7f, f) == "-11"); - assert(printFloat(-11.3f, f) == "-11"); - assert(printFloat(-11.0f, f) == "-11"); - - fpctrl.rounding = FloatingPointControl.roundUp; - - assert(printFloat(11.5f, f) == "12"); - assert(printFloat(12.5f, f) == "13"); - assert(printFloat(11.7f, f) == "12"); - assert(printFloat(11.3f, f) == "12"); - assert(printFloat(11.0f, f) == "11"); - assert(printFloat(-11.5f, f) == "-11"); - assert(printFloat(-12.5f, f) == "-12"); - assert(printFloat(-11.7f, f) == "-11"); - assert(printFloat(-11.3f, f) == "-11"); - assert(printFloat(-11.0f, f) == "-11"); - - fpctrl.rounding = FloatingPointControl.roundDown; - - assert(printFloat(11.5f, f) == "11"); - assert(printFloat(12.5f, f) == "12"); - assert(printFloat(11.7f, f) == "11"); - assert(printFloat(11.3f, f) == "11"); - assert(printFloat(11.0f, f) == "11"); - assert(printFloat(-11.5f, f) == "-12"); - assert(printFloat(-12.5f, f) == "-13"); - assert(printFloat(-11.7f, f) == "-12"); - assert(printFloat(-11.3f, f) == "-12"); - assert(printFloat(-11.0f, f) == "-11"); - } -} - -@safe unittest -{ - auto f = FormatSpec!dchar(""); - f.spec = 'g'; - - assert(printFloat(double.nan, f) == "nan"); - assert(printFloat(-double.nan, f) == "-nan"); - assert(printFloat(double.infinity, f) == "inf"); - assert(printFloat(-double.infinity, f) == "-inf"); - assert(printFloat(0.0, f) == "0"); - assert(printFloat(-0.0, f) == "-0"); - - // / 1000 needed due to https://issues.dlang.org/show_bug.cgi?id=20361 - assert(printFloat(1e-307 / 1000, f) == "1e-310"); - assert(printFloat(-1e-307 / 1000, f) == "-1e-310"); - assert(printFloat(1e-30, f) == "1e-30"); - assert(printFloat(-1e-30, f) == "-1e-30"); - assert(printFloat(1e-10, f) == "1e-10"); - assert(printFloat(-1e-10, f) == "-1e-10"); - assert(printFloat(0.1, f) == "0.1"); - assert(printFloat(-0.1, f) == "-0.1"); - assert(printFloat(10.0, f) == "10"); - assert(printFloat(-10.0, f) == "-10"); - assert(printFloat(1e300, f) == "1e+300"); - assert(printFloat(-1e300, f) == "-1e+300"); - - import std.math.operations : nextUp, nextDown; - assert(printFloat(nextUp(0.0), f) == "4.94066e-324"); - assert(printFloat(nextDown(-0.0), f) == "-4.94066e-324"); -} - -@safe unittest -{ - static if (real.mant_dig > 64) - { - pragma(msg, "printFloat tests disabled because of unsupported `real` format"); - } - else - { - char[256] buf; - auto f = FormatSpec!dchar(""); - f.spec = 'g'; - - assert(printFloat(real.nan, f) == "nan"); - assert(printFloat(-real.nan, f) == "-nan"); - assert(printFloat(real.infinity, f) == "inf"); - assert(printFloat(-real.infinity, f) == "-inf"); - } -} - -@safe unittest -{ - auto f = FormatSpec!dchar(""); - f.spec = 'g'; - - import std.math.operations : nextUp; - - double eps = nextUp(0.0); - f.precision = 1000; - assert(printFloat(eps, f) == - "4.940656458412465441765687928682213723650598026143247644255856825006" - ~ "755072702087518652998363616359923797965646954457177309266567103559" - ~ "397963987747960107818781263007131903114045278458171678489821036887" - ~ "186360569987307230500063874091535649843873124733972731696151400317" - ~ "153853980741262385655911710266585566867681870395603106249319452715" - ~ "914924553293054565444011274801297099995419319894090804165633245247" - ~ "571478690147267801593552386115501348035264934720193790268107107491" - ~ "703332226844753335720832431936092382893458368060106011506169809753" - ~ "078342277318329247904982524730776375927247874656084778203734469699" - ~ "533647017972677717585125660551199131504891101451037862738167250955" - ~ "837389733598993664809941164205702637090279242767544565229087538682" - ~ "506419718265533447265625e-324"); - - f.precision = 50; - assert(printFloat(double.max, f) == - "1.7976931348623157081452742373170435679807056752584e+308"); - assert(printFloat(double.epsilon, f) == - "2.220446049250313080847263336181640625e-16"); - - f.precision = 10; - assert(printFloat(1.0/3.0, f) == "0.3333333333"); - assert(printFloat(1.0/7.0, f) == "0.1428571429"); - assert(printFloat(1.0/9.0, f) == "0.1111111111"); -} - -@safe unittest -{ - auto f = FormatSpec!dchar(""); - f.spec = 'g'; - f.precision = 15; - - import std.math.constants : E, PI, PI_2, PI_4, M_1_PI, M_2_PI, M_2_SQRTPI, - LN10, LN2, LOG2, LOG2E, LOG2T, LOG10E, SQRT2, SQRT1_2; - - assert(printFloat(cast(double) E, f) == "2.71828182845905"); - assert(printFloat(cast(double) PI, f) == "3.14159265358979"); - assert(printFloat(cast(double) PI_2, f) == "1.5707963267949"); - assert(printFloat(cast(double) PI_4, f) == "0.785398163397448"); - assert(printFloat(cast(double) M_1_PI, f) == "0.318309886183791"); - assert(printFloat(cast(double) M_2_PI, f) == "0.636619772367581"); - assert(printFloat(cast(double) M_2_SQRTPI, f) == "1.12837916709551"); - assert(printFloat(cast(double) LN10, f) == "2.30258509299405"); - assert(printFloat(cast(double) LN2, f) == "0.693147180559945"); - assert(printFloat(cast(double) LOG2, f) == "0.301029995663981"); - assert(printFloat(cast(double) LOG2E, f) == "1.44269504088896"); - assert(printFloat(cast(double) LOG2T, f) == "3.32192809488736"); - assert(printFloat(cast(double) LOG10E, f) == "0.434294481903252"); - assert(printFloat(cast(double) SQRT2, f) == "1.4142135623731"); - assert(printFloat(cast(double) SQRT1_2, f) == "0.707106781186548"); -} - -// for 100% coverage -@safe unittest -{ - auto f = FormatSpec!dchar(""); - f.spec = 'g'; - f.precision = 0; - - assert(printFloat(0.009999, f) == "0.01"); -} - -@safe unittest -{ - static if (real.mant_dig > 64) - { - pragma(msg, "printFloat tests disabled because of unsupported `real` format"); - } - else - { - auto f = FormatSpec!dchar(""); - f.spec = 'g'; - assert(printFloat(real.nan, f) == "nan"); - assert(printFloat(-real.nan, f) == "-nan"); - assert(printFloat(real.infinity, f) == "inf"); - assert(printFloat(-real.infinity, f) == "-inf"); - assert(printFloat(0.0L, f) == "0"); - assert(printFloat(-0.0L, f) == "-0"); - } - - static if (real.mant_dig == 64) - { - assert(printFloat(1e-4940L, f) == "1e-4940"); - assert(printFloat(-1e-4940L, f) == "-1e-4940"); - assert(printFloat(1e-30L, f) == "1e-30"); - assert(printFloat(-1e-30L, f) == "-1e-30"); - assert(printFloat(1e-10L, f) == "1e-10"); - assert(printFloat(-1e-10L, f) == "-1e-10"); - assert(printFloat(0.1L, f) == "0.1"); - assert(printFloat(-0.1L, f) == "-0.1"); - assert(printFloat(10.0L, f) == "10"); - assert(printFloat(-10.0L, f) == "-10"); - version (Windows) {} // https://issues.dlang.org/show_bug.cgi?id=20972 - else - { - assert(printFloat(1e4000L, f) == "1e+4000"); - assert(printFloat(-1e4000L, f) == "-1e+4000"); - } - - import std.math.operations : nextUp, nextDown; - assert(printFloat(nextUp(0.0L), f) == "3.6452e-4951"); - assert(printFloat(nextDown(-0.0L), f) == "-3.6452e-4951"); - } -} - -@safe unittest -{ - import std.exception : assertCTFEable; - import std.math.exponential : log2; - import std.math.operations : nextDown; - - assertCTFEable!( - { - // log2 is broken for x87-reals on some computers in CTFE - // the following tests excludes these computers from the tests - // (https://issues.dlang.org/show_bug.cgi?id=21757) - enum test = cast(int) log2(3.05e2312L); - static if (real.mant_dig == 64 && test == 7681) - { - auto f = FormatSpec!dchar(""); - f.spec = 'g'; - assert(printFloat(real.infinity, f) == "inf"); - assert(printFloat(10.0L, f) == "10"); - assert(printFloat(2.6080L, f) == "2.608"); - assert(printFloat(3.05e2312L, f) == "3.05e+2312"); - - f.precision = 60; - assert(printFloat(2.65e-54L, f) == - "2.65000000000000000005900998740054701394102894093529654759941e-54"); - - /* - commented out, because CTFE is currently too slow for 5000 digits with extreme values - - f.precision = 5000; - auto result2 = printFloat(1.2119e-4822L, f); - assert(result2.length == 5007); - assert(result2[$ - 20 .. $] == "26072948659534e-4822"); - auto result3 = printFloat(real.min_normal, f); - assert(result3.length == 5007); - assert(result3[$ - 20 .. $] == "72078141008227e-4932"); - auto result4 = printFloat(real.min_normal.nextDown, f); - assert(result4.length == 5007); - assert(result4[$ - 20 .. $] == "48141326333101e-4932"); - */ - } - }); -} - -// check no allocations -@safe unittest -{ - import std.format : NoOpSink; - auto w = NoOpSink(); - - import core.memory; - auto stats = () @trusted { return GC.stats; } (); - - auto f = FormatSpec!dchar(""); - f.spec = 'a'; - printFloat(w, float.nan, f); - printFloat(w, -float.infinity, f); - printFloat(w, 0.0f, f); - - printFloat(w, -double.nan, f); - printFloat(w, double.infinity, f); - printFloat(w, -0.0, f); - - import std.math.operations : nextUp; - import std.math.constants : E; - - printFloat(w, nextUp(0.0f), f); - printFloat(w, cast(float) E, f); - - f.precision = 1000; - printFloat(w, float.nan, f); - printFloat(w, 0.0, f); - printFloat(w, 1.23456789e+100, f); - - f.spec = 'E'; - f.precision = 80; - printFloat(w, 5.62776e+12f, f); - - f.precision = 6; - printFloat(w, -1.1418613e+07f, f); - - f.precision = 20; - printFloat(w, double.max, f); - printFloat(w, nextUp(0.0), f); - - f.precision = 1000; - printFloat(w, 1.0, f); - - f.spec = 'f'; - f.precision = 15; - printFloat(w, cast(double) E, f); - - f.precision = 20; - printFloat(w, double.max, f); - printFloat(w, nextUp(0.0), f); - - f.precision = 1000; - printFloat(w, 1.0, f); - - f.spec = 'g'; - f.precision = 15; - printFloat(w, cast(double) E, f); - - f.precision = 20; - printFloat(w, double.max, f); - printFloat(w, nextUp(0.0), f); - - f.flHash = true; - f.precision = 1000; - printFloat(w, 1.0, f); - - assert(() @trusted { return GC.stats.usedSize; } () == stats.usedSize); -} diff --git a/phobos/std/format/internal/read.d b/phobos/std/format/internal/read.d deleted file mode 100644 index 9130499..0000000 --- a/phobos/std/format/internal/read.d +++ /dev/null @@ -1,392 +0,0 @@ -// Written in the D programming language. - -/* - Copyright: Copyright The D Language Foundation 2000-2013. - - License: $(HTTP boost.org/LICENSE_1_0.txt, Boost License 1.0). - - Authors: $(HTTP walterbright.com, Walter Bright), $(HTTP erdani.com, - Andrei Alexandrescu), and Kenji Hara - - Source: $(PHOBOSSRC std/format/internal/read.d) - */ -module std.format.internal.read; - -import std.range.primitives : ElementEncodingType, ElementType, isInputRange; - -import std.traits : isAggregateType, isArray, isAssociativeArray, - isDynamicArray, isFloatingPoint, isIntegral, isSomeChar, isSomeString, - isStaticArray, StringTypeOf; - -import std.format.spec : FormatSpec; - -package(std.format): - -void skipData(Range, Char)(ref Range input, scope const ref FormatSpec!Char spec) -{ - import std.ascii : isDigit; - import std.conv : text; - import std.range.primitives : empty, front, popFront; - - switch (spec.spec) - { - case 'c': input.popFront(); break; - case 'd': - if (input.front == '+' || input.front == '-') input.popFront(); - goto case 'u'; - case 'u': - while (!input.empty && isDigit(input.front)) input.popFront(); - break; - default: - assert(false, - text("Format specifier not understood: %", spec.spec)); - } -} - -private template acceptedSpecs(T) -{ - static if (isIntegral!T) - enum acceptedSpecs = "bdosuxX"; - else static if (isFloatingPoint!T) - enum acceptedSpecs = "seEfgG"; - else static if (isSomeChar!T) - enum acceptedSpecs = "bcdosuxX"; // integral + 'c' - else - enum acceptedSpecs = ""; -} - -T unformatValueImpl(T, Range, Char)(ref Range input, scope const ref FormatSpec!Char spec) -if (isInputRange!Range && is(immutable T == immutable bool)) -{ - import std.algorithm.searching : find; - import std.conv : parse, text; - import std.format : enforceFmt, unformatValue; - - if (spec.spec == 's') return parse!T(input); - - enforceFmt(find(acceptedSpecs!long, spec.spec).length, - text("Wrong unformat specifier '%", spec.spec , "' for ", T.stringof)); - - return unformatValue!long(input, spec) != 0; -} - -T unformatValueImpl(T, Range, Char)(ref Range input, scope const ref FormatSpec!Char spec) -if (isInputRange!Range && is(T == typeof(null))) -{ - import std.conv : parse, text; - import std.format : enforceFmt; - - enforceFmt(spec.spec == 's', - text("Wrong unformat specifier '%", spec.spec , "' for ", T.stringof)); - - return parse!T(input); -} - -T unformatValueImpl(T, Range, Char)(ref Range input, scope const ref FormatSpec!Char spec) -if (isInputRange!Range && isIntegral!T && !is(T == enum) && isSomeChar!(ElementType!Range)) -{ - import std.algorithm.searching : find; - import std.conv : parse, text; - import std.format : enforceFmt, FormatException; - - if (spec.spec == 'r') - { - static if (is(immutable ElementEncodingType!Range == immutable char) - || is(immutable ElementEncodingType!Range == immutable byte) - || is(immutable ElementEncodingType!Range == immutable ubyte)) - return rawRead!T(input); - else - throw new FormatException( - "The raw read specifier %r may only be used with narrow strings and ranges of bytes." - ); - } - - enforceFmt(find(acceptedSpecs!T, spec.spec).length, - text("Wrong unformat specifier '%", spec.spec , "' for ", T.stringof)); - - enforceFmt(spec.width == 0, "Parsing integers with a width specification is not implemented"); // TODO - - immutable uint base = - spec.spec == 'x' || spec.spec == 'X' ? 16 : - spec.spec == 'o' ? 8 : - spec.spec == 'b' ? 2 : - spec.spec == 's' || spec.spec == 'd' || spec.spec == 'u' ? 10 : 0; - assert(base != 0, "base must be not equal to zero"); - - return parse!T(input, base); - -} - -T unformatValueImpl(T, Range, Char)(ref Range input, scope const ref FormatSpec!Char spec) -if (isFloatingPoint!T && !is(T == enum) && isInputRange!Range - && isSomeChar!(ElementType!Range)&& !is(Range == enum)) -{ - import std.algorithm.searching : find; - import std.conv : parse, text; - import std.format : enforceFmt, FormatException; - - if (spec.spec == 'r') - { - static if (is(immutable ElementEncodingType!Range == immutable char) - || is(immutable ElementEncodingType!Range == immutable byte) - || is(immutable ElementEncodingType!Range == immutable ubyte)) - return rawRead!T(input); - else - throw new FormatException( - "The raw read specifier %r may only be used with narrow strings and ranges of bytes." - ); - } - - enforceFmt(find(acceptedSpecs!T, spec.spec).length, - text("Wrong unformat specifier '%", spec.spec , "' for ", T.stringof)); - - return parse!T(input); -} - -T unformatValueImpl(T, Range, Char)(ref Range input, scope const ref FormatSpec!Char spec) -if (isInputRange!Range && isSomeChar!T && !is(T == enum) && isSomeChar!(ElementType!Range)) -{ - import std.algorithm.searching : find; - import std.conv : to, text; - import std.range.primitives : empty, front, popFront; - import std.format : enforceFmt, unformatValue; - - if (spec.spec == 's' || spec.spec == 'c') - { - auto result = to!T(input.front); - input.popFront(); - return result; - } - - enforceFmt(find(acceptedSpecs!T, spec.spec).length, - text("Wrong unformat specifier '%", spec.spec , "' for ", T.stringof)); - - static if (T.sizeof == 1) - return unformatValue!ubyte(input, spec); - else static if (T.sizeof == 2) - return unformatValue!ushort(input, spec); - else static if (T.sizeof == 4) - return unformatValue!uint(input, spec); - else - static assert(false, T.stringof ~ ".sizeof must be 1, 2, or 4 not " ~ - to!string(T.sizeof)); -} - -T unformatValueImpl(T, Range, Char)(ref Range input, scope const ref FormatSpec!Char fmt) -if (isInputRange!Range && is(StringTypeOf!T) && !isAggregateType!T && !is(T == enum)) -{ - import std.conv : text; - import std.range.primitives : empty, front, popFront, put; - import std.format : enforceFmt; - - const spec = fmt.spec; - if (spec == '(') - { - return unformatRange!T(input, fmt); - } - enforceFmt(spec == 's', - text("Wrong unformat specifier '%", spec , "' for ", T.stringof)); - - static if (isStaticArray!T) - { - T result; - auto app = result[]; - } - else - { - import std.array : appender; - auto app = appender!T(); - } - if (fmt.trailing.empty) - { - for (; !input.empty; input.popFront()) - { - static if (isStaticArray!T) - if (app.empty) - break; - app.put(input.front); - } - } - else - { - immutable end = fmt.trailing.front; - for (; !input.empty && input.front != end; input.popFront()) - { - static if (isStaticArray!T) - if (app.empty) - break; - app.put(input.front); - } - } - static if (isStaticArray!T) - { - enforceFmt(app.empty, "need more input"); - return result; - } - else - return app.data; -} - -T unformatValueImpl(T, Range, Char)(ref Range input, scope const ref FormatSpec!Char fmt) -if (isInputRange!Range && !is(StringTypeOf!T) && !isAggregateType!T - && (isArray!T || isAssociativeArray!T || is(T == enum))) -{ - import std.conv : parse, text; - import std.format : enforceFmt; - - const spec = fmt.spec; - if (spec == '(') - { - return unformatRange!T(input, fmt); - } - - enforceFmt(spec == 's', - text("Wrong unformat specifier '%", spec , "' for ", T.stringof)); - - return parse!T(input); -} - -/* - * Function that performs raw reading. Used by unformatValue - * for integral and float types. - */ -private T rawRead(T, Range)(ref Range input) -if (is(immutable ElementEncodingType!Range == immutable char) - || is(immutable ElementEncodingType!Range == immutable byte) - || is(immutable ElementEncodingType!Range == immutable ubyte)) -{ - import std.range.primitives : popFront; - - union X - { - ubyte[T.sizeof] raw; - T typed; - } - X x; - foreach (i; 0 .. T.sizeof) - { - static if (isSomeString!Range) - { - x.raw[i] = input[0]; - input = input[1 .. $]; - } - else - { - // TODO: recheck this - x.raw[i] = input.front; - input.popFront(); - } - } - return x.typed; -} - -private T unformatRange(T, Range, Char)(ref Range input, scope const ref FormatSpec!Char spec) -in (spec.spec == '(', "spec.spec must be '(' not " ~ spec.spec) -{ - import std.range.primitives : empty, front, popFront; - import std.format : enforceFmt, format; - - T result; - static if (isStaticArray!T) - { - size_t i; - } - - const(Char)[] cont = spec.trailing; - for (size_t j = 0; j < spec.trailing.length; ++j) - { - if (spec.trailing[j] == '%') - { - cont = spec.trailing[0 .. j]; - break; - } - } - - bool checkEnd() - { - return input.empty || !cont.empty && input.front == cont.front; - } - - if (!checkEnd()) - { - for (;;) - { - auto fmt = FormatSpec!Char(spec.nested); - fmt.readUpToNextSpec(input); - enforceFmt(!input.empty, "Unexpected end of input when parsing range"); - - static if (isStaticArray!T) - { - result[i++] = unformatElement!(typeof(T.init[0]))(input, fmt); - } - else static if (isDynamicArray!T) - { - import std.conv : WideElementType; - result ~= unformatElement!(WideElementType!T)(input, fmt); - } - else static if (isAssociativeArray!T) - { - auto key = unformatElement!(typeof(T.init.keys[0]))(input, fmt); - fmt.readUpToNextSpec(input); // eat key separator - - result[key] = unformatElement!(typeof(T.init.values[0]))(input, fmt); - } - - static if (isStaticArray!T) - { - enforceFmt(i <= T.length, - "Too many format specifiers for static array of length %d".format(T.length)); - } - - if (spec.sep !is null) - fmt.readUpToNextSpec(input); - auto sep = spec.sep !is null ? spec.sep : fmt.trailing; - - if (checkEnd()) - break; - - if (!sep.empty && input.front == sep.front) - { - while (!sep.empty) - { - enforceFmt(!input.empty, - "Unexpected end of input when parsing range separator"); - enforceFmt(input.front == sep.front, - "Unexpected character when parsing range separator"); - input.popFront(); - sep.popFront(); - } - } - } - } - static if (isStaticArray!T) - { - enforceFmt(i == T.length, - "Too few (%d) format specifiers for static array of length %d".format(i, T.length)); - } - return result; -} - -T unformatElement(T, Range, Char)(ref Range input, scope const ref FormatSpec!Char spec) -if (isInputRange!Range) -{ - import std.conv : parseElement; - import std.format.read : unformatValue; - - static if (isSomeString!T) - { - if (spec.spec == 's') - { - return parseElement!T(input); - } - } - else static if (isSomeChar!T) - { - if (spec.spec == 's') - { - return parseElement!T(input); - } - } - - return unformatValue!T(input, spec); -} diff --git a/phobos/std/format/internal/write.d b/phobos/std/format/internal/write.d deleted file mode 100644 index 16c7a51..0000000 --- a/phobos/std/format/internal/write.d +++ /dev/null @@ -1,3966 +0,0 @@ -// Written in the D programming language. - -/* - Copyright: Copyright The D Language Foundation 2000-2013. - - License: $(HTTP boost.org/LICENSE_1_0.txt, Boost License 1.0). - - Authors: $(HTTP walterbright.com, Walter Bright), $(HTTP erdani.com, - Andrei Alexandrescu), and Kenji Hara - - Source: $(PHOBOSSRC std/format/internal/write.d) - */ -module std.format.internal.write; - -import std.format.spec : FormatSpec; -import std.range.primitives : isInputRange; -import std.traits; - -version (StdUnittest) -{ - import std.exception : assertCTFEable; - import std.format : format; -} - -package(std.format): - -/* - `bool`s are formatted as `"true"` or `"false"` with `%s` and as `1` or - `0` with integral-specific format specs. - */ -void formatValueImpl(Writer, T, Char)(auto ref Writer w, const(T) obj, scope const ref FormatSpec!Char f) -if (is(BooleanTypeOf!T) && !is(T == enum) && !hasToString!(T, Char)) -{ - BooleanTypeOf!T val = obj; - - if (f.spec == 's') - writeAligned(w, val ? "true" : "false", f); - else - formatValueImpl(w, cast(byte) val, f); -} - -@safe pure unittest -{ - assertCTFEable!( - { - formatTest(false, "false"); - formatTest(true, "true"); - }); -} - -@safe unittest -{ - struct S1 - { - bool val; - alias val this; - } - - struct S2 - { - bool val; - alias val this; - string toString() const { return "S"; } - } - - formatTest(S1(false), "false"); - formatTest(S1(true), "true"); - formatTest(S2(false), "S"); - formatTest(S2(true), "S"); -} - -@safe pure unittest -{ - string t1 = format("[%6s] [%6s] [%-6s]", true, false, true); - assert(t1 == "[ true] [ false] [true ]"); - - string t2 = format("[%3s] [%-2s]", true, false); - assert(t2 == "[true] [false]"); -} - -// https://issues.dlang.org/show_bug.cgi?id=20534 -@safe pure unittest -{ - assert(format("%r",false) == "\0"); -} - -@safe pure unittest -{ - assert(format("%07s",true) == " true"); -} - -@safe pure unittest -{ - assert(format("%=8s",true) == " true "); - assert(format("%=9s",false) == " false "); - assert(format("%=9s",true) == " true "); - assert(format("%-=9s",true) == " true "); - assert(format("%=10s",false) == " false "); - assert(format("%-=10s",false) == " false "); -} - -/* - `null` literal is formatted as `"null"` - */ -void formatValueImpl(Writer, T, Char)(auto ref Writer w, const(T) obj, scope const ref FormatSpec!Char f) -if (is(immutable T == immutable typeof(null)) && !is(T == enum) && !hasToString!(T, Char)) -{ - import std.format : enforceFmt; - - const spec = f.spec; - enforceFmt(spec == 's', "null literal cannot match %" ~ spec); - - writeAligned(w, "null", f); -} - -@safe pure unittest -{ - import std.exception : collectExceptionMsg; - import std.format : FormatException; - import std.range.primitives : back; - - assert(collectExceptionMsg!FormatException(format("%p", null)).back == 'p'); - - assertCTFEable!( - { - formatTest(null, "null"); - }); -} - -@safe pure unittest -{ - string t = format("[%6s] [%-6s]", null, null); - assert(t == "[ null] [null ]"); -} - -/* - Integrals are formatted like $(REF printf, core, stdc, stdio). - */ -void formatValueImpl(Writer, T, Char)(auto ref Writer w, const(T) obj, scope const ref FormatSpec!Char f) -if (is(IntegralTypeOf!T) && !is(T == enum) && !hasToString!(T, Char)) -{ - alias U = IntegralTypeOf!T; - U val = obj; // Extracting alias this may be impure/system/may-throw - - if (f.spec == 'r') - { - // raw write, skip all else and write the thing - auto raw = (ref val) @trusted { - return (cast(const char*) &val)[0 .. val.sizeof]; - }(val); - import std.range.primitives : put; - if (needToSwapEndianess(f)) - foreach_reverse (c; raw) - put(w, c); - else - foreach (c; raw) - put(w, c); - return; - } - - static if (isSigned!U) - { - const negative = val < 0 && f.spec != 'x' && f.spec != 'X' && f.spec != 'b' && f.spec != 'o' && f.spec != 'u'; - ulong arg = negative ? -cast(ulong) val : val; - } - else - { - const negative = false; - ulong arg = val; - } - arg &= Unsigned!U.max; - - formatValueImplUlong!(Writer, Char)(w, arg, negative, f); -} - -// Helper function for `formatValueImpl` that avoids template bloat -private void formatValueImplUlong(Writer, Char)(auto ref Writer w, ulong arg, in bool negative, - scope const ref FormatSpec!Char f) -{ - immutable uint base = baseOfSpec(f.spec); - - const bool zero = arg == 0; - char[64] digits = void; - size_t pos = digits.length - 1; - do - { - /* `cast(char)` is needed because value range propagation (VRP) cannot - * analyze `base` because it’s computed in a separate function - * (`baseOfSpec`). */ - digits[pos--] = cast(char) ('0' + arg % base); - if (base > 10 && digits[pos + 1] > '9') - digits[pos + 1] += ((f.spec == 'x' || f.spec == 'a') ? 'a' : 'A') - '0' - 10; - arg /= base; - } while (arg > 0); - - char[3] prefix = void; - size_t left = 2; - size_t right = 2; - - // add sign - if (f.spec != 'x' && f.spec != 'X' && f.spec != 'b' && f.spec != 'o' && f.spec != 'u') - { - if (negative) - prefix[right++] = '-'; - else if (f.flPlus) - prefix[right++] = '+'; - else if (f.flSpace) - prefix[right++] = ' '; - } - - // not a floating point like spec - if (f.spec == 'x' || f.spec == 'X' || f.spec == 'b' || f.spec == 'o' || f.spec == 'u' - || f.spec == 'd' || f.spec == 's') - { - if (f.flHash && (base == 16) && !zero) - { - prefix[--left] = f.spec; - prefix[--left] = '0'; - } - if (f.flHash && (base == 8) && !zero - && (digits.length - (pos + 1) >= f.precision || f.precision == f.UNSPECIFIED)) - prefix[--left] = '0'; - - writeAligned(w, prefix[left .. right], digits[pos + 1 .. $], "", f, true); - return; - } - - FormatSpec!Char fs = f; - if (f.precision == f.UNSPECIFIED) - fs.precision = cast(typeof(fs.precision)) (digits.length - pos - 2); - - // %f like output - if (f.spec == 'f' || f.spec == 'F' - || ((f.spec == 'g' || f.spec == 'G') && (fs.precision >= digits.length - pos - 2))) - { - if (f.precision == f.UNSPECIFIED) - fs.precision = 0; - - writeAligned(w, prefix[left .. right], digits[pos + 1 .. $], ".", "", fs, - (f.spec == 'g' || f.spec == 'G') ? PrecisionType.allDigits : PrecisionType.fractionalDigits); - - return; - } - - import std.algorithm.searching : all; - - // at least one digit for %g - if ((f.spec == 'g' || f.spec == 'G') && fs.precision == 0) - fs.precision = 1; - - // rounding - size_t digit_end = pos + fs.precision + ((f.spec == 'g' || f.spec == 'G') ? 1 : 2); - if (digit_end <= digits.length) - { - RoundingClass rt = RoundingClass.ZERO; - if (digit_end < digits.length) - { - auto tie = (f.spec == 'a' || f.spec == 'A') ? '8' : '5'; - if (digits[digit_end] >= tie) - { - rt = RoundingClass.UPPER; - if (digits[digit_end] == tie && digits[digit_end + 1 .. $].all!(a => a == '0')) - rt = RoundingClass.FIVE; - } - else - { - rt = RoundingClass.LOWER; - if (digits[digit_end .. $].all!(a => a == '0')) - rt = RoundingClass.ZERO; - } - } - - if (round(digits, pos + 1, digit_end, rt, negative, - f.spec == 'a' ? 'f' : (f.spec == 'A' ? 'F' : '9'))) - { - pos--; - digit_end--; - } - } - - // convert to scientific notation - char[1] int_digit = void; - int_digit[0] = digits[pos + 1]; - digits[pos + 1] = '.'; - - char[4] suffix = void; - - if (f.spec == 'e' || f.spec == 'E' || f.spec == 'g' || f.spec == 'G') - { - suffix[0] = (f.spec == 'e' || f.spec == 'g') ? 'e' : 'E'; - suffix[1] = '+'; - suffix[2] = cast(char) ('0' + (digits.length - pos - 2) / 10); - suffix[3] = cast(char) ('0' + (digits.length - pos - 2) % 10); - } - else - { - if (right == 3) - prefix[0] = prefix[2]; - prefix[1] = '0'; - prefix[2] = f.spec == 'a' ? 'x' : 'X'; - - left = right == 3 ? 0 : 1; - right = 3; - - suffix[0] = f.spec == 'a' ? 'p' : 'P'; - suffix[1] = '+'; - suffix[2] = cast(char) ('0' + ((digits.length - pos - 2) * 4) / 10); - suffix[3] = cast(char) ('0' + ((digits.length - pos - 2) * 4) % 10); - } - - import std.algorithm.comparison : min; - - // remove trailing zeros - if ((f.spec == 'g' || f.spec == 'G') && !f.flHash) - { - digit_end = min(digit_end, digits.length); - while (digit_end > pos + 1 && - (digits[digit_end - 1] == '0' || digits[digit_end - 1] == '.')) - digit_end--; - } - - writeAligned(w, prefix[left .. right], int_digit[0 .. $], - digits[pos + 1 .. min(digit_end, $)], - suffix[0 .. $], fs, - (f.spec == 'g' || f.spec == 'G') ? PrecisionType.allDigits : PrecisionType.fractionalDigits); -} - -private uint baseOfSpec(in char spec) @safe pure -{ - typeof(return) base = - spec == 'x' || spec == 'X' || spec == 'a' || spec == 'A' ? 16 : - spec == 'o' ? 8 : - spec == 'b' ? 2 : - spec == 's' || spec == 'd' || spec == 'u' - || spec == 'e' || spec == 'E' || spec == 'f' || spec == 'F' - || spec == 'g' || spec == 'G' ? 10 : - 0; - - import std.format : enforceFmt; - enforceFmt(base > 0, - "incompatible format character for integral argument: %" ~ spec); - - return base; -} - -@safe pure unittest -{ - assertCTFEable!( - { - formatTest(byte.min, "-128"); - formatTest(byte.max, "127"); - formatTest(short.min, "-32768"); - formatTest(short.max, "32767"); - formatTest(int.min, "-2147483648"); - formatTest(int.max, "2147483647"); - formatTest(long.min, "-9223372036854775808"); - formatTest(long.max, "9223372036854775807"); - - formatTest(ubyte.min, "0"); - formatTest(ubyte.max, "255"); - formatTest(ushort.min, "0"); - formatTest(ushort.max, "65535"); - formatTest(uint.min, "0"); - formatTest(uint.max, "4294967295"); - formatTest(ulong.min, "0"); - formatTest(ulong.max, "18446744073709551615"); - }); -} - -// https://issues.dlang.org/show_bug.cgi?id=18838 -@safe pure unittest -{ - assert("%12,d".format(0) == " 0"); -} - -@safe pure unittest -{ - import std.exception : collectExceptionMsg; - import std.format : FormatException; - import std.range.primitives : back; - - assert(collectExceptionMsg!FormatException(format("%c", 5)).back == 'c'); - - assertCTFEable!( - { - formatTest(9, "9"); - formatTest(10, "10"); - }); -} - -@safe unittest -{ - struct S1 - { - long val; - alias val this; - } - - struct S2 - { - long val; - alias val this; - string toString() const { return "S"; } - } - - formatTest(S1(10), "10"); - formatTest(S2(10), "S"); -} - -// https://issues.dlang.org/show_bug.cgi?id=20064 -@safe unittest -{ - assert(format( "%03,d", 1234) == "1,234"); - assert(format( "%04,d", 1234) == "1,234"); - assert(format( "%05,d", 1234) == "1,234"); - assert(format( "%06,d", 1234) == "01,234"); - assert(format( "%07,d", 1234) == "001,234"); - assert(format( "%08,d", 1234) == "0,001,234"); - assert(format( "%09,d", 1234) == "0,001,234"); - assert(format("%010,d", 1234) == "00,001,234"); - assert(format("%011,d", 1234) == "000,001,234"); - assert(format("%012,d", 1234) == "0,000,001,234"); - assert(format("%013,d", 1234) == "0,000,001,234"); - assert(format("%014,d", 1234) == "00,000,001,234"); - assert(format("%015,d", 1234) == "000,000,001,234"); - assert(format("%016,d", 1234) == "0,000,000,001,234"); - assert(format("%017,d", 1234) == "0,000,000,001,234"); - - assert(format( "%03,d", -1234) == "-1,234"); - assert(format( "%04,d", -1234) == "-1,234"); - assert(format( "%05,d", -1234) == "-1,234"); - assert(format( "%06,d", -1234) == "-1,234"); - assert(format( "%07,d", -1234) == "-01,234"); - assert(format( "%08,d", -1234) == "-001,234"); - assert(format( "%09,d", -1234) == "-0,001,234"); - assert(format("%010,d", -1234) == "-0,001,234"); - assert(format("%011,d", -1234) == "-00,001,234"); - assert(format("%012,d", -1234) == "-000,001,234"); - assert(format("%013,d", -1234) == "-0,000,001,234"); - assert(format("%014,d", -1234) == "-0,000,001,234"); - assert(format("%015,d", -1234) == "-00,000,001,234"); - assert(format("%016,d", -1234) == "-000,000,001,234"); - assert(format("%017,d", -1234) == "-0,000,000,001,234"); -} - -@safe pure unittest -{ - string t1 = format("[%6s] [%-6s]", 123, 123); - assert(t1 == "[ 123] [123 ]"); - - string t2 = format("[%6s] [%-6s]", -123, -123); - assert(t2 == "[ -123] [-123 ]"); -} - -@safe pure unittest -{ - formatTest(byte.min, "-128"); - formatTest(short.min, "-32768"); - formatTest(int.min, "-2147483648"); - formatTest(long.min, "-9223372036854775808"); -} - -// https://issues.dlang.org/show_bug.cgi?id=21777 -@safe pure unittest -{ - assert(format!"%20.5,d"(cast(short) 120) == " 00,120"); - assert(format!"%20.5,o"(cast(short) 120) == " 00,170"); - assert(format!"%20.5,x"(cast(short) 120) == " 00,078"); - assert(format!"%20.5,2d"(cast(short) 120) == " 0,01,20"); - assert(format!"%20.5,2o"(cast(short) 120) == " 0,01,70"); - assert(format!"%20.5,4d"(cast(short) 120) == " 0,0120"); - assert(format!"%20.5,4o"(cast(short) 120) == " 0,0170"); - assert(format!"%20.5,4x"(cast(short) 120) == " 0,0078"); - assert(format!"%20.5,2x"(3000) == " 0,0b,b8"); - assert(format!"%20.5,4d"(3000) == " 0,3000"); - assert(format!"%20.5,4o"(3000) == " 0,5670"); - assert(format!"%20.5,4x"(3000) == " 0,0bb8"); - assert(format!"%20.5,d"(-400) == " -00,400"); - assert(format!"%20.30d"(-400) == "-000000000000000000000000000400"); - assert(format!"%20.5,4d"(0) == " 0,0000"); - assert(format!"%0#.8,2s"(12345) == "00,01,23,45"); - assert(format!"%0#.9,3x"(55) == "0x000,000,037"); -} - -// https://issues.dlang.org/show_bug.cgi?id=21814 -@safe pure unittest -{ - assert(format("%,0d",1000) == "1000"); -} - -// https://issues.dlang.org/show_bug.cgi?id=21817 -@safe pure unittest -{ - assert(format!"%u"(-5) == "4294967291"); -} - -// https://issues.dlang.org/show_bug.cgi?id=21820 -@safe pure unittest -{ - assert(format!"%#.0o"(0) == "0"); -} - -@safe pure unittest -{ - assert(format!"%e"(10000) == "1.0000e+04"); - assert(format!"%.2e"(10000) == "1.00e+04"); - assert(format!"%.10e"(10000) == "1.0000000000e+04"); - - assert(format!"%e"(9999) == "9.999e+03"); - assert(format!"%.2e"(9999) == "1.00e+04"); - assert(format!"%.10e"(9999) == "9.9990000000e+03"); - - assert(format!"%f"(10000) == "10000"); - assert(format!"%.2f"(10000) == "10000.00"); - - assert(format!"%g"(10000) == "10000"); - assert(format!"%.2g"(10000) == "1e+04"); - assert(format!"%.10g"(10000) == "10000"); - - assert(format!"%#g"(10000) == "10000."); - assert(format!"%#.2g"(10000) == "1.0e+04"); - assert(format!"%#.10g"(10000) == "10000.00000"); - - assert(format!"%g"(9999) == "9999"); - assert(format!"%.2g"(9999) == "1e+04"); - assert(format!"%.10g"(9999) == "9999"); - - assert(format!"%a"(0x10000) == "0x1.0000p+16"); - assert(format!"%.2a"(0x10000) == "0x1.00p+16"); - assert(format!"%.10a"(0x10000) == "0x1.0000000000p+16"); - - assert(format!"%a"(0xffff) == "0xf.fffp+12"); - assert(format!"%.2a"(0xffff) == "0x1.00p+16"); - assert(format!"%.10a"(0xffff) == "0xf.fff0000000p+12"); -} - -@safe pure unittest -{ - assert(format!"%.3e"(ulong.max) == "1.845e+19"); - assert(format!"%.3f"(ulong.max) == "18446744073709551615.000"); - assert(format!"%.3g"(ulong.max) == "1.84e+19"); - assert(format!"%.3a"(ulong.max) == "0x1.000p+64"); - - assert(format!"%.3e"(long.min) == "-9.223e+18"); - assert(format!"%.3f"(long.min) == "-9223372036854775808.000"); - assert(format!"%.3g"(long.min) == "-9.22e+18"); - assert(format!"%.3a"(long.min) == "-0x8.000p+60"); - - assert(format!"%e"(0) == "0e+00"); - assert(format!"%f"(0) == "0"); - assert(format!"%g"(0) == "0"); - assert(format!"%a"(0) == "0x0p+00"); -} - -@safe pure unittest -{ - assert(format!"%.0g"(1500) == "2e+03"); -} - -// https://issues.dlang.org/show_bug.cgi?id=21900# -@safe pure unittest -{ - assert(format!"%.1a"(472) == "0x1.ep+08"); -} - -/* - Floating-point values are formatted like $(REF printf, core, stdc, stdio) - */ -void formatValueImpl(Writer, T, Char)(auto ref Writer w, const(T) obj, - scope const ref FormatSpec!Char f) -if (is(FloatingPointTypeOf!T) && !is(T == enum) && !hasToString!(T, Char)) -{ - import std.format : enforceFmt; - import std.range.primitives : put; - import std.format.internal.floats : printFloat, isFloatSpec; - - FloatingPointTypeOf!T val = obj; - const char spec = f.spec; - - if (spec == 'r') - { - // raw write, skip all else and write the thing - auto raw = (ref val) @trusted { - return (cast(const char*) &val)[0 .. val.sizeof]; - }(val); - - if (needToSwapEndianess(f)) - { - foreach_reverse (c; raw) - put(w, c); - } - else - { - foreach (c; raw) - put(w, c); - } - return; - } - - FormatSpec!Char fs = f; // fs is copy for change its values. - fs.spec = spec == 's' ? 'g' : spec; - enforceFmt(isFloatSpec(fs.spec), "incompatible format character for floating point argument: %" ~ spec); - - static if (is(T == float) || is(T == double) - || (is(T == real) && (T.mant_dig == double.mant_dig || T.mant_dig == 64))) - { - alias tval = val; - } - else - { - import std.math.traits : isInfinity; - import std.math.operations : nextUp; - - // reals that are not supported by printFloat are cast to double. - double tval = val; - - // Numbers greater than double.max are converted to double.max: - if (val > double.max && !isInfinity(val)) - tval = double.max; - if (val < -double.max && !isInfinity(val)) - tval = -double.max; - - // Numbers between the smallest representable double subnormal and 0.0 - // are converted to the smallest representable double subnormal: - enum doubleLowest = nextUp(0.0); - if (val > 0 && val < doubleLowest) - tval = doubleLowest; - if (val < 0 && val > -doubleLowest) - tval = -doubleLowest; - } - - printFloat(w, tval, fs); -} - -@safe unittest -{ - assert(format("%.1f", 1337.7) == "1337.7"); - assert(format("%,3.2f", 1331.982) == "1,331.98"); - assert(format("%,3.0f", 1303.1982) == "1,303"); - assert(format("%#,3.4f", 1303.1982) == "1,303.1982"); - assert(format("%#,3.0f", 1303.1982) == "1,303."); -} - -@safe pure unittest -{ - import std.conv : to; - import std.exception : collectExceptionMsg; - import std.format : FormatException; - import std.meta : AliasSeq; - import std.range.primitives : back; - - assert(collectExceptionMsg!FormatException(format("%d", 5.1)).back == 'd'); - - static foreach (T; AliasSeq!(float, double, real)) - { - formatTest(to!( T)(5.5), "5.5"); - formatTest(to!( const T)(5.5), "5.5"); - formatTest(to!(immutable T)(5.5), "5.5"); - - formatTest(T.nan, "nan"); - } -} - -@safe unittest -{ - formatTest(2.25, "2.25"); - - struct S1 - { - double val; - alias val this; - } - struct S2 - { - double val; - alias val this; - string toString() const { return "S"; } - } - - formatTest(S1(2.25), "2.25"); - formatTest(S2(2.25), "S"); -} - -// https://issues.dlang.org/show_bug.cgi?id=19939 -@safe unittest -{ - assert(format("^%13,3.2f$", 1.00) == "^ 1.00$"); - assert(format("^%13,3.2f$", 10.00) == "^ 10.00$"); - assert(format("^%13,3.2f$", 100.00) == "^ 100.00$"); - assert(format("^%13,3.2f$", 1_000.00) == "^ 1,000.00$"); - assert(format("^%13,3.2f$", 10_000.00) == "^ 10,000.00$"); - assert(format("^%13,3.2f$", 100_000.00) == "^ 100,000.00$"); - assert(format("^%13,3.2f$", 1_000_000.00) == "^ 1,000,000.00$"); - assert(format("^%13,3.2f$", 10_000_000.00) == "^10,000,000.00$"); -} - -// https://issues.dlang.org/show_bug.cgi?id=20069 -@safe unittest -{ - assert(format("%012,f", -1234.0) == "-1,234.000000"); - assert(format("%013,f", -1234.0) == "-1,234.000000"); - assert(format("%014,f", -1234.0) == "-01,234.000000"); - assert(format("%011,f", 1234.0) == "1,234.000000"); - assert(format("%012,f", 1234.0) == "1,234.000000"); - assert(format("%013,f", 1234.0) == "01,234.000000"); - assert(format("%014,f", 1234.0) == "001,234.000000"); - assert(format("%015,f", 1234.0) == "0,001,234.000000"); - assert(format("%016,f", 1234.0) == "0,001,234.000000"); - - assert(format( "%08,.2f", -1234.0) == "-1,234.00"); - assert(format( "%09,.2f", -1234.0) == "-1,234.00"); - assert(format("%010,.2f", -1234.0) == "-01,234.00"); - assert(format("%011,.2f", -1234.0) == "-001,234.00"); - assert(format("%012,.2f", -1234.0) == "-0,001,234.00"); - assert(format("%013,.2f", -1234.0) == "-0,001,234.00"); - assert(format("%014,.2f", -1234.0) == "-00,001,234.00"); - assert(format( "%08,.2f", 1234.0) == "1,234.00"); - assert(format( "%09,.2f", 1234.0) == "01,234.00"); - assert(format("%010,.2f", 1234.0) == "001,234.00"); - assert(format("%011,.2f", 1234.0) == "0,001,234.00"); - assert(format("%012,.2f", 1234.0) == "0,001,234.00"); - assert(format("%013,.2f", 1234.0) == "00,001,234.00"); - assert(format("%014,.2f", 1234.0) == "000,001,234.00"); - assert(format("%015,.2f", 1234.0) == "0,000,001,234.00"); - assert(format("%016,.2f", 1234.0) == "0,000,001,234.00"); -} - -@safe unittest -{ - import std.math.hardware; // cannot be selective, because FloatingPointControl might not be defined - - // std.math's FloatingPointControl isn't available on all target platforms - static if (is(FloatingPointControl)) - { - assert(FloatingPointControl.rounding == FloatingPointControl.roundToNearest); - } - - // https://issues.dlang.org/show_bug.cgi?id=20320 - real a = 0.16; - real b = 0.016; - assert(format("%.1f", a) == "0.2"); - assert(format("%.2f", b) == "0.02"); - - double a1 = 0.16; - double b1 = 0.016; - assert(format("%.1f", a1) == "0.2"); - assert(format("%.2f", b1) == "0.02"); - - // https://issues.dlang.org/show_bug.cgi?id=9889 - assert(format("%.1f", 0.09) == "0.1"); - assert(format("%.1f", -0.09) == "-0.1"); - assert(format("%.1f", 0.095) == "0.1"); - assert(format("%.1f", -0.095) == "-0.1"); - assert(format("%.1f", 0.094) == "0.1"); - assert(format("%.1f", -0.094) == "-0.1"); -} - -@safe unittest -{ - double a = 123.456; - double b = -123.456; - double c = 123.0; - - assert(format("%10.4f",a) == " 123.4560"); - assert(format("%-10.4f",a) == "123.4560 "); - assert(format("%+10.4f",a) == " +123.4560"); - assert(format("% 10.4f",a) == " 123.4560"); - assert(format("%010.4f",a) == "00123.4560"); - assert(format("%#10.4f",a) == " 123.4560"); - - assert(format("%10.4f",b) == " -123.4560"); - assert(format("%-10.4f",b) == "-123.4560 "); - assert(format("%+10.4f",b) == " -123.4560"); - assert(format("% 10.4f",b) == " -123.4560"); - assert(format("%010.4f",b) == "-0123.4560"); - assert(format("%#10.4f",b) == " -123.4560"); - - assert(format("%10.0f",c) == " 123"); - assert(format("%-10.0f",c) == "123 "); - assert(format("%+10.0f",c) == " +123"); - assert(format("% 10.0f",c) == " 123"); - assert(format("%010.0f",c) == "0000000123"); - assert(format("%#10.0f",c) == " 123."); - - assert(format("%+010.4f",a) == "+0123.4560"); - assert(format("% 010.4f",a) == " 0123.4560"); - assert(format("% +010.4f",a) == "+0123.4560"); -} - -@safe unittest -{ - string t1 = format("[%6s] [%-6s]", 12.3, 12.3); - assert(t1 == "[ 12.3] [12.3 ]"); - - string t2 = format("[%6s] [%-6s]", -12.3, -12.3); - assert(t2 == "[ -12.3] [-12.3 ]"); -} - -// https://issues.dlang.org/show_bug.cgi?id=20396 -@safe unittest -{ - import std.math.operations : nextUp; - - assert(format!"%a"(nextUp(0.0f)) == "0x0.000002p-126"); - assert(format!"%a"(nextUp(0.0)) == "0x0.0000000000001p-1022"); -} - -// https://issues.dlang.org/show_bug.cgi?id=20371 -@safe unittest -{ - assert(format!"%.1000a"(1.0).length == 1007); - assert(format!"%.600f"(0.1).length == 602); - assert(format!"%.600e"(0.1L).length == 606); -} - -@safe unittest -{ - import std.math.hardware; // cannot be selective, because FloatingPointControl might not be defined - - // std.math's FloatingPointControl isn't available on all target platforms - static if (is(FloatingPointControl)) - { - FloatingPointControl fpctrl; - - fpctrl.rounding = FloatingPointControl.roundUp; - assert(format!"%.0e"(3.5) == "4e+00"); - assert(format!"%.0e"(4.5) == "5e+00"); - assert(format!"%.0e"(-3.5) == "-3e+00"); - assert(format!"%.0e"(-4.5) == "-4e+00"); - - fpctrl.rounding = FloatingPointControl.roundDown; - assert(format!"%.0e"(3.5) == "3e+00"); - assert(format!"%.0e"(4.5) == "4e+00"); - assert(format!"%.0e"(-3.5) == "-4e+00"); - assert(format!"%.0e"(-4.5) == "-5e+00"); - - fpctrl.rounding = FloatingPointControl.roundToZero; - assert(format!"%.0e"(3.5) == "3e+00"); - assert(format!"%.0e"(4.5) == "4e+00"); - assert(format!"%.0e"(-3.5) == "-3e+00"); - assert(format!"%.0e"(-4.5) == "-4e+00"); - - fpctrl.rounding = FloatingPointControl.roundToNearest; - assert(format!"%.0e"(3.5) == "4e+00"); - assert(format!"%.0e"(4.5) == "4e+00"); - assert(format!"%.0e"(-3.5) == "-4e+00"); - assert(format!"%.0e"(-4.5) == "-4e+00"); - } -} - -@safe pure unittest -{ - static assert(format("%e",1.0) == "1.000000e+00"); - static assert(format("%e",-1.234e156) == "-1.234000e+156"); - static assert(format("%a",1.0) == "0x1p+0"); - static assert(format("%a",-1.234e156) == "-0x1.7024c96ca3ce4p+518"); - static assert(format("%f",1.0) == "1.000000"); - static assert(format("%f",-1.234e156) == - "-123399999999999990477495546305353609103201879173427886566531" ~ - "0740685826234179310516880117527217443004051984432279880308552" ~ - "009640198043032289366552939010719744.000000"); - static assert(format("%g",1.0) == "1"); - static assert(format("%g",-1.234e156) == "-1.234e+156"); - - static assert(format("%e",1.0f) == "1.000000e+00"); - static assert(format("%e",-1.234e23f) == "-1.234000e+23"); - static assert(format("%a",1.0f) == "0x1p+0"); - static assert(format("%a",-1.234e23f) == "-0x1.a2187p+76"); - static assert(format("%f",1.0f) == "1.000000"); - static assert(format("%f",-1.234e23f) == "-123399998884238311030784.000000"); - static assert(format("%g",1.0f) == "1"); - static assert(format("%g",-1.234e23f) == "-1.234e+23"); -} - -// https://issues.dlang.org/show_bug.cgi?id=21641 -@safe unittest -{ - float a = -999999.8125; - assert(format("%#.5g",a) == "-1.0000e+06"); - assert(format("%#.6g",a) == "-1.00000e+06"); -} - -// https://issues.dlang.org/show_bug.cgi?id=8424 -@safe pure unittest -{ - static assert(format("%s", 0.6f) == "0.6"); - static assert(format("%s", 0.6) == "0.6"); - static assert(format("%s", 0.6L) == "0.6"); -} - -// https://issues.dlang.org/show_bug.cgi?id=9297 -@safe pure unittest -{ - static if (real.mant_dig == 64) // 80 bit reals - { - assert(format("%.25f", 1.6180339887_4989484820_4586834365L) == "1.6180339887498948482072100"); - } -} - -// https://issues.dlang.org/show_bug.cgi?id=21853 -@safe pure unittest -{ - import std.math.exponential : log2; - - // log2 is broken for x87-reals on some computers in CTFE - // the following test excludes these computers from the test - // (https://issues.dlang.org/show_bug.cgi?id=21757) - enum test = cast(int) log2(3.05e2312L); - static if (real.mant_dig == 64 && test == 7681) // 80 bit reals - { - static assert(format!"%e"(real.max) == "1.189731e+4932"); - } -} - -// https://issues.dlang.org/show_bug.cgi?id=21842 -@safe pure unittest -{ - assert(format!"%-+05,g"(1.0) == "+1 "); -} - -// https://issues.dlang.org/show_bug.cgi?id=20536 -@safe pure unittest -{ - real r = .00000095367431640625L; - assert(format("%a", r) == "0x1p-20"); -} - -// https://issues.dlang.org/show_bug.cgi?id=21840 -@safe pure unittest -{ - assert(format!"% 0,e"(0.0) == " 0.000000e+00"); -} - -// https://issues.dlang.org/show_bug.cgi?id=21841 -@safe pure unittest -{ - assert(format!"%0.0,e"(0.0) == "0e+00"); -} - -// https://issues.dlang.org/show_bug.cgi?id=21836 -@safe pure unittest -{ - assert(format!"%-5,1g"(0.0) == "0 "); -} - -// https://issues.dlang.org/show_bug.cgi?id=21838 -@safe pure unittest -{ - assert(format!"%#,a"(0.0) == "0x0.p+0"); -} - -/* - Formatting a `creal` is deprecated but still kept around for a while. - */ -deprecated("Use of complex types is deprecated. Use std.complex") -void formatValueImpl(Writer, T, Char)(auto ref Writer w, const(T) obj, scope const ref FormatSpec!Char f) -if (is(immutable T : immutable creal) && !is(T == enum) && !hasToString!(T, Char)) -{ - import std.range.primitives : put; - - immutable creal val = obj; - - formatValueImpl(w, val.re, f); - if (val.im >= 0) - { - put(w, '+'); - } - formatValueImpl(w, val.im, f); - put(w, 'i'); -} - -/* - Formatting an `ireal` is deprecated but still kept around for a while. - */ -deprecated("Use of imaginary types is deprecated. Use std.complex") -void formatValueImpl(Writer, T, Char)(auto ref Writer w, const(T) obj, scope const ref FormatSpec!Char f) -if (is(immutable T : immutable ireal) && !is(T == enum) && !hasToString!(T, Char)) -{ - import std.range.primitives : put; - - immutable ireal val = obj; - - formatValueImpl(w, val.im, f); - put(w, 'i'); -} - -/* - Individual characters are formatted as Unicode characters with `%s` - and as integers with integral-specific format specs - */ -void formatValueImpl(Writer, T, Char)(auto ref Writer w, const(T) obj, scope const ref FormatSpec!Char f) -if (is(CharTypeOf!T) && !is(T == enum) && !hasToString!(T, Char)) -{ - import std.meta : AliasSeq; - - CharTypeOf!T[1] val = obj; - - if (f.spec == 's' || f.spec == 'c') - writeAligned(w, val[], f); - else - { - alias U = AliasSeq!(ubyte, ushort, uint)[CharTypeOf!T.sizeof/2]; - formatValueImpl(w, cast(U) val[0], f); - } -} - -@safe pure unittest -{ - assertCTFEable!( - { - formatTest('c', "c"); - }); -} - -@safe unittest -{ - struct S1 - { - char val; - alias val this; - } - - struct S2 - { - char val; - alias val this; - string toString() const { return "S"; } - } - - formatTest(S1('c'), "c"); - formatTest(S2('c'), "S"); -} - -@safe unittest -{ - //Little Endian - formatTest("%-r", cast( char)'c', ['c' ]); - formatTest("%-r", cast(wchar)'c', ['c', 0 ]); - formatTest("%-r", cast(dchar)'c', ['c', 0, 0, 0]); - formatTest("%-r", '本', ['\x2c', '\x67'] ); - - //Big Endian - formatTest("%+r", cast( char)'c', [ 'c']); - formatTest("%+r", cast(wchar)'c', [0, 'c']); - formatTest("%+r", cast(dchar)'c', [0, 0, 0, 'c']); - formatTest("%+r", '本', ['\x67', '\x2c']); -} - - -@safe pure unittest -{ - string t1 = format("[%6s] [%-6s]", 'A', 'A'); - assert(t1 == "[ A] [A ]"); - string t2 = format("[%6s] [%-6s]", '本', '本'); - assert(t2 == "[ 本] [本 ]"); -} - -/* - Strings are formatted like $(REF printf, core, stdc, stdio) - */ -void formatValueImpl(Writer, T, Char)(auto ref Writer w, scope const(T) obj, - scope const ref FormatSpec!Char f) -if (is(StringTypeOf!T) && !is(StaticArrayTypeOf!T) && !is(T == enum) && !hasToString!(T, Char)) -{ - Unqual!(const(StringTypeOf!T)) val = obj; // for `alias this`, see bug5371 - formatRange(w, val, f); -} - -@safe unittest -{ - formatTest("abc", "abc"); -} - -@safe pure unittest -{ - import std.exception : collectExceptionMsg; - import std.range.primitives : back; - - assert(collectExceptionMsg(format("%d", "hi")).back == 'd'); -} - -@safe unittest -{ - // Test for bug 5371 for structs - struct S1 - { - const string var; - alias var this; - } - - struct S2 - { - string var; - alias var this; - } - - formatTest(S1("s1"), "s1"); - formatTest(S2("s2"), "s2"); -} - -@safe unittest -{ - struct S3 - { - string val; alias val this; - string toString() const { return "S"; } - } - - formatTest(S3("s3"), "S"); -} - -@safe pure unittest -{ - //Little Endian - formatTest("%-r", "ab"c, ['a' , 'b' ]); - formatTest("%-r", "ab"w, ['a', 0 , 'b', 0 ]); - formatTest("%-r", "ab"d, ['a', 0, 0, 0, 'b', 0, 0, 0]); - formatTest("%-r", "日本語"c, ['\xe6', '\x97', '\xa5', '\xe6', '\x9c', '\xac', - '\xe8', '\xaa', '\x9e']); - formatTest("%-r", "日本語"w, ['\xe5', '\x65', '\x2c', '\x67', '\x9e', '\x8a']); - formatTest("%-r", "日本語"d, ['\xe5', '\x65', '\x00', '\x00', '\x2c', '\x67', - '\x00', '\x00', '\x9e', '\x8a', '\x00', '\x00']); - - //Big Endian - formatTest("%+r", "ab"c, [ 'a', 'b']); - formatTest("%+r", "ab"w, [ 0, 'a', 0, 'b']); - formatTest("%+r", "ab"d, [0, 0, 0, 'a', 0, 0, 0, 'b']); - formatTest("%+r", "日本語"c, ['\xe6', '\x97', '\xa5', '\xe6', '\x9c', '\xac', - '\xe8', '\xaa', '\x9e']); - formatTest("%+r", "日本語"w, ['\x65', '\xe5', '\x67', '\x2c', '\x8a', '\x9e']); - formatTest("%+r", "日本語"d, ['\x00', '\x00', '\x65', '\xe5', '\x00', '\x00', - '\x67', '\x2c', '\x00', '\x00', '\x8a', '\x9e']); -} - -@safe pure unittest -{ - string t1 = format("[%6s] [%-6s]", "AB", "AB"); - assert(t1 == "[ AB] [AB ]"); - string t2 = format("[%6s] [%-6s]", "本Ä", "本Ä"); - assert(t2 == "[ 本Ä] [本Ä ]"); -} - -// https://issues.dlang.org/show_bug.cgi?id=6640 -@safe unittest -{ - import std.range.primitives : front, popFront; - - struct Range - { - @safe: - - string value; - @property bool empty() const { return !value.length; } - @property dchar front() const { return value.front; } - void popFront() { value.popFront(); } - - @property size_t length() const { return value.length; } - } - immutable table = - [ - ["[%s]", "[string]"], - ["[%10s]", "[ string]"], - ["[%-10s]", "[string ]"], - ["[%(%02x %)]", "[73 74 72 69 6e 67]"], - ["[%(%c %)]", "[s t r i n g]"], - ]; - foreach (e; table) - { - formatTest(e[0], "string", e[1]); - formatTest(e[0], Range("string"), e[1]); - } -} - -@safe unittest -{ - import std.meta : AliasSeq; - - // string literal from valid UTF sequence is encoding free. - static foreach (StrType; AliasSeq!(string, wstring, dstring)) - { - // Valid and printable (ASCII) - formatTest([cast(StrType)"hello"], - `["hello"]`); - - // 1 character escape sequences (' is not escaped in strings) - formatTest([cast(StrType)"\"'\0\\\a\b\f\n\r\t\v"], - `["\"'\0\\\a\b\f\n\r\t\v"]`); - - // 1 character optional escape sequences - formatTest([cast(StrType)"\'\?"], - `["'?"]`); - - // Valid and non-printable code point (<= U+FF) - formatTest([cast(StrType)"\x10\x1F\x20test"], - `["\x10\x1F test"]`); - - // Valid and non-printable code point (<= U+FFFF) - formatTest([cast(StrType)"\u200B..\u200F"], - `["\u200B..\u200F"]`); - - // Valid and non-printable code point (<= U+10FFFF) - formatTest([cast(StrType)"\U000E0020..\U000E007F"], - `["\U000E0020..\U000E007F"]`); - } - - // invalid UTF sequence needs hex-string literal postfix (c/w/d) - () @trusted - { - // U+FFFF with UTF-8 (Invalid code point for interchange) - formatTest([cast(string)[0xEF, 0xBF, 0xBF]], - `[[cast(char) 0xEF, cast(char) 0xBF, cast(char) 0xBF]]`); - - // U+FFFF with UTF-16 (Invalid code point for interchange) - formatTest([cast(wstring)[0xFFFF]], - `[[cast(wchar) 0xFFFF]]`); - - // U+FFFF with UTF-32 (Invalid code point for interchange) - formatTest([cast(dstring)[0xFFFF]], - `[[cast(dchar) 0xFFFF]]`); - } (); -} - -/* - Static-size arrays are formatted as dynamic arrays. - */ -void formatValueImpl(Writer, T, Char)(auto ref Writer w, auto ref T obj, - scope const ref FormatSpec!Char f) -if (is(StaticArrayTypeOf!T) && !is(T == enum) && !hasToString!(T, Char)) -{ - formatValueImpl(w, obj[], f); -} - -// Test for https://issues.dlang.org/show_bug.cgi?id=8310 -@safe unittest -{ - import std.array : appender; - import std.format : formatValue; - - FormatSpec!char f; - auto w = appender!string(); - - char[2] two = ['a', 'b']; - formatValue(w, two, f); - - char[2] getTwo() { return two; } - formatValue(w, getTwo(), f); -} - -// https://issues.dlang.org/show_bug.cgi?id=18205 -@safe pure unittest -{ - assert("|%8s|".format("abc") == "| abc|"); - assert("|%8s|".format("ιβγ") == "| ιβγ|"); - assert("|%8s|".format(" ") == "| |"); - assert("|%8s|".format("ĂŠtĂŠ"d) == "| ĂŠtĂŠ|"); - assert("|%8s|".format("ĂŠtĂŠ 2018"w) == "|ĂŠtĂŠ 2018|"); - - assert("%2s".format("e\u0301"w) == " e\u0301"); - assert("%2s".format("a\u0310\u0337"d) == " a\u0310\u0337"); -} - -/* - Dynamic arrays are formatted as input ranges. - */ -void formatValueImpl(Writer, T, Char)(auto ref Writer w, T obj, scope const ref FormatSpec!Char f) -if (is(DynamicArrayTypeOf!T) && !is(StringTypeOf!T) && !is(T == enum) && !hasToString!(T, Char)) -{ - static if (is(immutable(ArrayTypeOf!T) == immutable(void[]))) - { - formatValueImpl(w, cast(const ubyte[]) obj, f); - } - else static if (!isInputRange!T) - { - alias U = Unqual!(ArrayTypeOf!T); - static assert(isInputRange!U, U.stringof ~ " must be an InputRange"); - U val = obj; - formatValueImpl(w, val, f); - } - else - { - formatRange(w, obj, f); - } -} - -// https://issues.dlang.org/show_bug.cgi?id=20848 -@safe unittest -{ - class C - { - immutable(void)[] data; - } - - import std.typecons : Nullable; - Nullable!C c; -} - -// alias this, input range I/F, and toString() -@safe unittest -{ - struct S(int flags) - { - int[] arr; - static if (flags & 1) - alias arr this; - - static if (flags & 2) - { - @property bool empty() const { return arr.length == 0; } - @property int front() const { return arr[0] * 2; } - void popFront() { arr = arr[1 .. $]; } - } - - static if (flags & 4) - string toString() const { return "S"; } - } - - formatTest(S!0b000([0, 1, 2]), "S!0([0, 1, 2])"); - formatTest(S!0b001([0, 1, 2]), "[0, 1, 2]"); // Test for bug 7628 - formatTest(S!0b010([0, 1, 2]), "[0, 2, 4]"); - formatTest(S!0b011([0, 1, 2]), "[0, 2, 4]"); - formatTest(S!0b100([0, 1, 2]), "S"); - formatTest(S!0b101([0, 1, 2]), "S"); // Test for bug 7628 - formatTest(S!0b110([0, 1, 2]), "S"); - formatTest(S!0b111([0, 1, 2]), "S"); -} - -@safe unittest -{ - // void[] - void[] val0; - formatTest(val0, "[]"); - - void[] val = cast(void[]) cast(ubyte[])[1, 2, 3]; - formatTest(val, "[1, 2, 3]"); - - void[0] sval0 = []; - formatTest(sval0, "[]"); - - void[3] sval = () @trusted { return cast(void[3]) cast(ubyte[3])[1, 2, 3]; } (); - formatTest(sval, "[1, 2, 3]"); -} - -@safe unittest -{ - // const(T[]) -> const(T)[] - const short[] a = [1, 2, 3]; - formatTest(a, "[1, 2, 3]"); - - struct S - { - const(int[]) arr; - alias arr this; - } - - auto s = S([1,2,3]); - formatTest(s, "[1, 2, 3]"); -} - -@safe unittest -{ - // nested range formatting with array of string - formatTest("%({%(%02x %)}%| %)", ["test", "msg"], - `{74 65 73 74} {6d 73 67}`); -} - -@safe unittest -{ - // stop auto escaping inside range formatting - auto arr = ["hello", "world"]; - formatTest("%(%s, %)", arr, `"hello", "world"`); - formatTest("%-(%s, %)", arr, `hello, world`); - - auto aa1 = [1:"hello", 2:"world"]; - formatTest("%(%s:%s, %)", aa1, [`1:"hello", 2:"world"`, `2:"world", 1:"hello"`]); - formatTest("%-(%s:%s, %)", aa1, [`1:hello, 2:world`, `2:world, 1:hello`]); - - auto aa2 = [1:["ab", "cd"], 2:["ef", "gh"]]; - formatTest("%-(%s:%s, %)", aa2, [`1:["ab", "cd"], 2:["ef", "gh"]`, `2:["ef", "gh"], 1:["ab", "cd"]`]); - formatTest("%-(%s:%(%s%), %)", aa2, [`1:"ab""cd", 2:"ef""gh"`, `2:"ef""gh", 1:"ab""cd"`]); - formatTest("%-(%s:%-(%s%)%|, %)", aa2, [`1:abcd, 2:efgh`, `2:efgh, 1:abcd`]); -} - -// https://issues.dlang.org/show_bug.cgi?id=18778 -@safe pure unittest -{ - assert(format("%-(%1$s - %1$s, %)", ["A", "B", "C"]) == "A - A, B - B, C - C"); -} - -@safe pure unittest -{ - int[] a = [ 1, 3, 2 ]; - formatTest("testing %(%s & %) embedded", a, - "testing 1 & 3 & 2 embedded"); - formatTest("testing %((%s) %)) wyda3", a, - "testing (1) (3) (2) wyda3"); - - int[0] empt = []; - formatTest("(%s)", empt, "([])"); -} - -// input range formatting -private void formatRange(Writer, T, Char)(ref Writer w, ref T val, scope const ref FormatSpec!Char f) -if (isInputRange!T) -{ - import std.conv : text; - import std.format : FormatException, formatValue, NoOpSink; - import std.range.primitives : ElementType, empty, front, hasLength, - walkLength, isForwardRange, isInfinite, popFront, put; - - // in this mode, we just want to do a representative print to discover - // if the format spec is valid - enum formatTestMode = is(Writer == NoOpSink); - - // Formatting character ranges like string - if (f.spec == 's') - { - alias E = ElementType!T; - - static if (!is(E == enum) && is(CharTypeOf!E)) - { - static if (is(StringTypeOf!T)) - writeAligned(w, val[0 .. f.precision < $ ? f.precision : $], f); - else - { - if (!f.flDash) - { - static if (hasLength!T) - { - // right align - auto len = val.length; - } - else static if (isForwardRange!T && !isInfinite!T) - { - auto len = walkLength(val.save); - } - else - { - import std.format : enforceFmt; - enforceFmt(f.width == 0, "Cannot right-align a range without length"); - size_t len = 0; - } - if (f.precision != f.UNSPECIFIED && len > f.precision) - len = f.precision; - - if (f.width > len) - foreach (i ; 0 .. f.width - len) - put(w, ' '); - if (f.precision == f.UNSPECIFIED) - put(w, val); - else - { - size_t printed = 0; - for (; !val.empty && printed < f.precision; val.popFront(), ++printed) - put(w, val.front); - } - } - else - { - size_t printed = void; - - // left align - if (f.precision == f.UNSPECIFIED) - { - static if (hasLength!T) - { - printed = val.length; - put(w, val); - } - else - { - printed = 0; - for (; !val.empty; val.popFront(), ++printed) - { - put(w, val.front); - static if (formatTestMode) break; // one is enough to test - } - } - } - else - { - printed = 0; - for (; !val.empty && printed < f.precision; val.popFront(), ++printed) - put(w, val.front); - } - - if (f.width > printed) - foreach (i ; 0 .. f.width - printed) - put(w, ' '); - } - } - } - else - { - put(w, f.seqBefore); - if (!val.empty) - { - formatElement(w, val.front, f); - val.popFront(); - for (size_t i; !val.empty; val.popFront(), ++i) - { - put(w, f.seqSeparator); - formatElement(w, val.front, f); - static if (formatTestMode) break; // one is enough to test - } - } - static if (!isInfinite!T) put(w, f.seqAfter); - } - } - else if (f.spec == 'r') - { - static if (is(DynamicArrayTypeOf!T)) - { - alias ARR = DynamicArrayTypeOf!T; - scope a = cast(ARR) val; - foreach (e ; a) - { - formatValue(w, e, f); - static if (formatTestMode) break; // one is enough to test - } - } - else - { - for (size_t i; !val.empty; val.popFront(), ++i) - { - formatValue(w, val.front, f); - static if (formatTestMode) break; // one is enough to test - } - } - } - else if (f.spec == '(') - { - if (val.empty) - return; - // Nested specifier is to be used - for (;;) - { - auto fmt = FormatSpec!Char(f.nested); - w: while (true) - { - immutable r = fmt.writeUpToNextSpec(w); - // There was no format specifier, so break - if (!r) - break; - if (f.flDash) - formatValue(w, val.front, fmt); - else - formatElement(w, val.front, fmt); - // Check if there will be a format specifier farther on in the - // string. If so, continue the loop, otherwise break. This - // prevents extra copies of the `sep` from showing up. - foreach (size_t i; 0 .. fmt.trailing.length) - if (fmt.trailing[i] == '%') - continue w; - break w; - } - static if (formatTestMode) - { - break; // one is enough to test - } - else - { - if (f.sep !is null) - { - put(w, fmt.trailing); - val.popFront(); - if (val.empty) - break; - put(w, f.sep); - } - else - { - val.popFront(); - if (val.empty) - break; - put(w, fmt.trailing); - } - } - } - } - else - throw new FormatException(text("Incorrect format specifier for range: %", f.spec)); -} - -// https://issues.dlang.org/show_bug.cgi?id=20218 -@safe pure unittest -{ - void notCalled() - { - import std.range : repeat; - - auto value = 1.repeat; - - // test that range is not evaluated to completion at compiletime - format!"%s"(value); - } -} - -// character formatting with ecaping -void formatChar(Writer)(ref Writer w, in dchar c, in char quote) -{ - import std.format : formattedWrite; - import std.range.primitives : put; - import std.uni : isGraphical; - - string fmt; - if (isGraphical(c)) - { - if (c == quote || c == '\\') - put(w, '\\'); - put(w, c); - return; - } - else if (c <= 0xFF) - { - if (c < 0x20) - { - foreach (i, k; "\n\r\t\a\b\f\v\0") - { - if (c == k) - { - put(w, '\\'); - put(w, "nrtabfv0"[i]); - return; - } - } - } - fmt = "\\x%02X"; - } - else if (c <= 0xFFFF) - fmt = "\\u%04X"; - else - fmt = "\\U%08X"; - - formattedWrite(w, fmt, cast(uint) c); -} - -/* - Associative arrays are formatted by using `':'` and $(D ", ") as - separators, and enclosed by `'['` and `']'`. - */ -void formatValueImpl(Writer, T, Char)(auto ref Writer w, T obj, scope const ref FormatSpec!Char f) -if (is(AssocArrayTypeOf!T) && !is(T == enum) && !hasToString!(T, Char)) -{ - import std.format : enforceFmt, formatValue; - import std.range.primitives : put; - - AssocArrayTypeOf!T val = obj; - const spec = f.spec; - - enforceFmt(spec == 's' || spec == '(', - "incompatible format character for associative array argument: %" ~ spec); - - enum const(Char)[] defSpec = "%s" ~ f.keySeparator ~ "%s" ~ f.seqSeparator; - auto fmtSpec = spec == '(' ? f.nested : defSpec; - - auto key_first = true; - - // testing correct nested format spec - import std.format : NoOpSink; - auto noop = NoOpSink(); - auto test = FormatSpec!Char(fmtSpec); - enforceFmt(test.writeUpToNextSpec(noop), - "nested format string for associative array contains no format specifier"); - enforceFmt(test.indexStart <= 2, - "positional parameter in nested format string for associative array may only be 1 or 2"); - if (test.indexStart == 2) - key_first = false; - - enforceFmt(test.writeUpToNextSpec(noop), - "nested format string for associative array contains only one format specifier"); - enforceFmt(test.indexStart <= 2, - "positional parameter in nested format string for associative array may only be 1 or 2"); - enforceFmt(test.indexStart == 0 || ((test.indexStart == 2) == key_first), - "wrong combination of positional parameters in nested format string"); - - enforceFmt(!test.writeUpToNextSpec(noop), - "nested format string for associative array contains more than two format specifiers"); - - size_t i = 0; - immutable end = val.length; - - if (spec == 's') - put(w, f.seqBefore); - foreach (k, ref v; val) - { - auto fmt = FormatSpec!Char(fmtSpec); - - foreach (pos; 1 .. 3) - { - fmt.writeUpToNextSpec(w); - - if (key_first == (pos == 1)) - { - if (f.flDash) - formatValue(w, k, fmt); - else - formatElement(w, k, fmt); - } - else - { - if (f.flDash) - formatValue(w, v, fmt); - else - formatElement(w, v, fmt); - } - } - - if (f.sep !is null) - { - fmt.writeUpToNextSpec(w); - if (++i != end) - put(w, f.sep); - } - else - { - if (++i != end) - fmt.writeUpToNextSpec(w); - } - } - if (spec == 's') - put(w, f.seqAfter); -} - -@safe unittest -{ - import std.exception : collectExceptionMsg; - import std.format : FormatException; - import std.range.primitives : back; - - assert(collectExceptionMsg!FormatException(format("%d", [0:1])).back == 'd'); - - int[string] aa0; - formatTest(aa0, `[]`); - - // elements escaping - formatTest(["aaa":1, "bbb":2], - [`["aaa":1, "bbb":2]`, `["bbb":2, "aaa":1]`]); - formatTest(['c':"str"], - `['c':"str"]`); - formatTest(['"':"\"", '\'':"'"], - [`['"':"\"", '\'':"'"]`, `['\'':"'", '"':"\""]`]); - - // range formatting for AA - auto aa3 = [1:"hello", 2:"world"]; - // escape - formatTest("{%(%s:%s $ %)}", aa3, - [`{1:"hello" $ 2:"world"}`, `{2:"world" $ 1:"hello"}`]); - // use range formatting for key and value, and use %| - formatTest("{%([%04d->%(%c.%)]%| $ %)}", aa3, - [`{[0001->h.e.l.l.o] $ [0002->w.o.r.l.d]}`, - `{[0002->w.o.r.l.d] $ [0001->h.e.l.l.o]}`]); - - // https://issues.dlang.org/show_bug.cgi?id=12135 - formatTest("%(%s:<%s>%|,%)", [1:2], "1:<2>"); - formatTest("%(%s:<%s>%|%)" , [1:2], "1:<2>"); -} - -@safe unittest -{ - struct S1 - { - int[char] val; - alias val this; - } - - struct S2 - { - int[char] val; - alias val this; - string toString() const { return "S"; } - } - - formatTest(S1(['c':1, 'd':2]), [`['c':1, 'd':2]`, `['d':2, 'c':1]`]); - formatTest(S2(['c':1, 'd':2]), "S"); -} - -// https://issues.dlang.org/show_bug.cgi?id=21875 -@safe unittest -{ - import std.exception : assertThrown; - import std.format : FormatException; - - auto aa = [ 1 : "x", 2 : "y", 3 : "z" ]; - - assertThrown!FormatException(format("%(%)", aa)); - assertThrown!FormatException(format("%(%s%)", aa)); - assertThrown!FormatException(format("%(%s%s%s%)", aa)); -} - -@safe unittest -{ - import std.exception : assertThrown; - import std.format : FormatException; - - auto aa = [ 1 : "x", 2 : "y", 3 : "z" ]; - - assertThrown!FormatException(format("%(%3$s%s%)", aa)); - assertThrown!FormatException(format("%(%s%3$s%)", aa)); - assertThrown!FormatException(format("%(%1$s%1$s%)", aa)); - assertThrown!FormatException(format("%(%2$s%2$s%)", aa)); - assertThrown!FormatException(format("%(%s%1$s%)", aa)); -} - -// https://issues.dlang.org/show_bug.cgi?id=21808 -@safe unittest -{ - auto spelled = [ 1 : "one" ]; - assert(format("%-(%2$s (%1$s)%|, %)", spelled) == "one (1)"); - - spelled[2] = "two"; - auto result = format("%-(%2$s (%1$s)%|, %)", spelled); - assert(result == "one (1), two (2)" || result == "two (2), one (1)"); -} - -enum HasToStringResult -{ - none, - hasSomeToString, - inCharSink, - inCharSinkFormatString, - inCharSinkFormatSpec, - constCharSink, - constCharSinkFormatString, - constCharSinkFormatSpec, - customPutWriter, - customPutWriterFormatSpec, -} - -private alias DScannerBug895 = int[256]; -private immutable bool hasPreviewIn = ((in DScannerBug895 a) { return __traits(isRef, a); })(DScannerBug895.init); - -template hasToString(T, Char) -{ - static if (isPointer!T) - { - // X* does not have toString, even if X is aggregate type has toString. - enum hasToString = HasToStringResult.none; - } - else static if (is(typeof( - (T val) { - const FormatSpec!Char f; - static struct S {void put(scope Char s){}} - S s; - val.toString(s, f); - static assert(!__traits(compiles, val.toString(s, FormatSpec!Char())), - "force toString to take parameters by ref"); - static assert(!__traits(compiles, val.toString(S(), f)), - "force toString to take parameters by ref"); - }))) - { - enum hasToString = HasToStringResult.customPutWriterFormatSpec; - } - else static if (is(typeof( - (T val) { - static struct S {void put(scope Char s){}} - S s; - val.toString(s); - static assert(!__traits(compiles, val.toString(S())), - "force toString to take parameters by ref"); - }))) - { - enum hasToString = HasToStringResult.customPutWriter; - } - else static if (is(typeof((T val) { FormatSpec!Char f; val.toString((scope const(char)[] s){}, f); }))) - { - enum hasToString = HasToStringResult.constCharSinkFormatSpec; - } - else static if (is(typeof((T val) { val.toString((scope const(char)[] s){}, "%s"); }))) - { - enum hasToString = HasToStringResult.constCharSinkFormatString; - } - else static if (is(typeof((T val) { val.toString((scope const(char)[] s){}); }))) - { - enum hasToString = HasToStringResult.constCharSink; - } - - else static if (hasPreviewIn && - is(typeof((T val) { FormatSpec!Char f; val.toString((in char[] s){}, f); }))) - { - enum hasToString = HasToStringResult.inCharSinkFormatSpec; - } - else static if (hasPreviewIn && - is(typeof((T val) { val.toString((in char[] s){}, "%s"); }))) - { - enum hasToString = HasToStringResult.inCharSinkFormatString; - } - else static if (hasPreviewIn && - is(typeof((T val) { val.toString((in char[] s){}); }))) - { - enum hasToString = HasToStringResult.inCharSink; - } - - else static if (is(ReturnType!((T val) { return val.toString(); }) S) && isSomeString!S) - { - enum hasToString = HasToStringResult.hasSomeToString; - } - else - { - enum hasToString = HasToStringResult.none; - } -} - -@safe unittest -{ - import std.range.primitives : isOutputRange; - - static struct A - { - void toString(Writer)(ref Writer w) - if (isOutputRange!(Writer, string)) - {} - } - static struct B - { - void toString(scope void delegate(scope const(char)[]) sink, scope FormatSpec!char fmt) {} - } - static struct C - { - void toString(scope void delegate(scope const(char)[]) sink, string fmt) {} - } - static struct D - { - void toString(scope void delegate(scope const(char)[]) sink) {} - } - static struct E - { - string toString() {return "";} - } - static struct F - { - void toString(Writer)(ref Writer w, scope const ref FormatSpec!char fmt) - if (isOutputRange!(Writer, string)) - {} - } - static struct G - { - string toString() {return "";} - void toString(Writer)(ref Writer w) if (isOutputRange!(Writer, string)) {} - } - static struct H - { - string toString() {return "";} - void toString(Writer)(ref Writer w, scope const ref FormatSpec!char fmt) - if (isOutputRange!(Writer, string)) - {} - } - static struct I - { - void toString(Writer)(ref Writer w) if (isOutputRange!(Writer, string)) {} - void toString(Writer)(ref Writer w, scope const ref FormatSpec!char fmt) - if (isOutputRange!(Writer, string)) - {} - } - static struct J - { - string toString() {return "";} - void toString(Writer)(ref Writer w, scope ref FormatSpec!char fmt) - if (isOutputRange!(Writer, string)) - {} - } - static struct K - { - void toString(Writer)(Writer w, scope const ref FormatSpec!char fmt) - if (isOutputRange!(Writer, string)) - {} - } - static struct L - { - void toString(Writer)(ref Writer w, scope const FormatSpec!char fmt) - if (isOutputRange!(Writer, string)) - {} - } - static struct M - { - void toString(scope void delegate(in char[]) sink, in FormatSpec!char fmt) {} - } - static struct N - { - void toString(scope void delegate(in char[]) sink, string fmt) {} - } - static struct O - { - void toString(scope void delegate(in char[]) sink) {} - } - - with(HasToStringResult) - { - static assert(hasToString!(A, char) == customPutWriter); - static assert(hasToString!(B, char) == constCharSinkFormatSpec); - static assert(hasToString!(C, char) == constCharSinkFormatString); - static assert(hasToString!(D, char) == constCharSink); - static assert(hasToString!(E, char) == hasSomeToString); - static assert(hasToString!(F, char) == customPutWriterFormatSpec); - static assert(hasToString!(G, char) == customPutWriter); - static assert(hasToString!(H, char) == customPutWriterFormatSpec); - static assert(hasToString!(I, char) == customPutWriterFormatSpec); - static assert(hasToString!(J, char) == hasSomeToString); - static assert(hasToString!(K, char) == constCharSinkFormatSpec); - static assert(hasToString!(L, char) == none); - static if (hasPreviewIn) - { - static assert(hasToString!(M, char) == inCharSinkFormatSpec); - static assert(hasToString!(N, char) == inCharSinkFormatString); - static assert(hasToString!(O, char) == inCharSink); - } - } -} - -// const toString methods -@safe unittest -{ - import std.range.primitives : isOutputRange; - - static struct A - { - void toString(Writer)(ref Writer w) const - if (isOutputRange!(Writer, string)) - {} - } - static struct B - { - void toString(scope void delegate(scope const(char)[]) sink, scope FormatSpec!char fmt) const {} - } - static struct C - { - void toString(scope void delegate(scope const(char)[]) sink, string fmt) const {} - } - static struct D - { - void toString(scope void delegate(scope const(char)[]) sink) const {} - } - static struct E - { - string toString() const {return "";} - } - static struct F - { - void toString(Writer)(ref Writer w, scope const ref FormatSpec!char fmt) const - if (isOutputRange!(Writer, string)) - {} - } - static struct G - { - string toString() const {return "";} - void toString(Writer)(ref Writer w) const if (isOutputRange!(Writer, string)) {} - } - static struct H - { - string toString() const {return "";} - void toString(Writer)(ref Writer w, scope const ref FormatSpec!char fmt) const - if (isOutputRange!(Writer, string)) - {} - } - static struct I - { - void toString(Writer)(ref Writer w) const if (isOutputRange!(Writer, string)) {} - void toString(Writer)(ref Writer w, scope const ref FormatSpec!char fmt) const - if (isOutputRange!(Writer, string)) - {} - } - static struct J - { - string toString() const {return "";} - void toString(Writer)(ref Writer w, scope ref FormatSpec!char fmt) const - if (isOutputRange!(Writer, string)) - {} - } - static struct K - { - void toString(Writer)(Writer w, scope const ref FormatSpec!char fmt) const - if (isOutputRange!(Writer, string)) - {} - } - static struct L - { - void toString(Writer)(ref Writer w, scope const FormatSpec!char fmt) const - if (isOutputRange!(Writer, string)) - {} - } - static struct M - { - void toString(scope void delegate(in char[]) sink, in FormatSpec!char fmt) const {} - } - static struct N - { - void toString(scope void delegate(in char[]) sink, string fmt) const {} - } - static struct O - { - void toString(scope void delegate(in char[]) sink) const {} - } - - with(HasToStringResult) - { - static assert(hasToString!(A, char) == customPutWriter); - static assert(hasToString!(B, char) == constCharSinkFormatSpec); - static assert(hasToString!(C, char) == constCharSinkFormatString); - static assert(hasToString!(D, char) == constCharSink); - static assert(hasToString!(E, char) == hasSomeToString); - static assert(hasToString!(F, char) == customPutWriterFormatSpec); - static assert(hasToString!(G, char) == customPutWriter); - static assert(hasToString!(H, char) == customPutWriterFormatSpec); - static assert(hasToString!(I, char) == customPutWriterFormatSpec); - static assert(hasToString!(J, char) == hasSomeToString); - static assert(hasToString!(K, char) == constCharSinkFormatSpec); - static assert(hasToString!(L, char) == none); - static if (hasPreviewIn) - { - static assert(hasToString!(M, char) == inCharSinkFormatSpec); - static assert(hasToString!(N, char) == inCharSinkFormatString); - static assert(hasToString!(O, char) == inCharSink); - } - - // https://issues.dlang.org/show_bug.cgi?id=22873 - static assert(hasToString!(inout(A), char) == customPutWriter); - static assert(hasToString!(inout(B), char) == constCharSinkFormatSpec); - static assert(hasToString!(inout(C), char) == constCharSinkFormatString); - static assert(hasToString!(inout(D), char) == constCharSink); - static assert(hasToString!(inout(E), char) == hasSomeToString); - static assert(hasToString!(inout(F), char) == customPutWriterFormatSpec); - static assert(hasToString!(inout(G), char) == customPutWriter); - static assert(hasToString!(inout(H), char) == customPutWriterFormatSpec); - static assert(hasToString!(inout(I), char) == customPutWriterFormatSpec); - static assert(hasToString!(inout(J), char) == hasSomeToString); - static assert(hasToString!(inout(K), char) == constCharSinkFormatSpec); - static assert(hasToString!(inout(L), char) == none); - static if (hasPreviewIn) - { - static assert(hasToString!(inout(M), char) == inCharSinkFormatSpec); - static assert(hasToString!(inout(N), char) == inCharSinkFormatString); - static assert(hasToString!(inout(O), char) == inCharSink); - } - } -} - -// object formatting with toString -private void formatObject(Writer, T, Char)(ref Writer w, ref T val, scope const ref FormatSpec!Char f) -if (hasToString!(T, Char)) -{ - import std.format : NoOpSink; - import std.range.primitives : put; - - enum overload = hasToString!(T, Char); - - enum noop = is(Writer == NoOpSink); - - static if (overload == HasToStringResult.customPutWriterFormatSpec) - { - static if (!noop) val.toString(w, f); - } - else static if (overload == HasToStringResult.customPutWriter) - { - static if (!noop) val.toString(w); - } - else static if (overload == HasToStringResult.constCharSinkFormatSpec) - { - static if (!noop) val.toString((scope const(char)[] s) { put(w, s); }, f); - } - else static if (overload == HasToStringResult.constCharSinkFormatString) - { - static if (!noop) val.toString((scope const(char)[] s) { put(w, s); }, f.getCurFmtStr()); - } - else static if (overload == HasToStringResult.constCharSink) - { - static if (!noop) val.toString((scope const(char)[] s) { put(w, s); }); - } - else static if (overload == HasToStringResult.inCharSinkFormatSpec) - { - static if (!noop) val.toString((in char[] s) { put(w, s); }, f); - } - else static if (overload == HasToStringResult.inCharSinkFormatString) - { - static if (!noop) val.toString((in char[] s) { put(w, s); }, f.getCurFmtStr()); - } - else static if (overload == HasToStringResult.inCharSink) - { - static if (!noop) val.toString((in char[] s) { put(w, s); }); - } - else static if (overload == HasToStringResult.hasSomeToString) - { - static if (!noop) put(w, val.toString()); - } - else - { - static assert(0, "No way found to format " ~ T.stringof ~ " as string"); - } -} - -@system unittest -{ - import std.exception : assertThrown; - import std.format : FormatException; - - static interface IF1 { } - class CIF1 : IF1 { } - static struct SF1 { } - static union UF1 { } - static class CF1 { } - - static interface IF2 { string toString(); } - static class CIF2 : IF2 { override string toString() { return ""; } } - static struct SF2 { string toString() { return ""; } } - static union UF2 { string toString() { return ""; } } - static class CF2 { override string toString() { return ""; } } - - static interface IK1 { void toString(scope void delegate(scope const(char)[]) sink, - FormatSpec!char) const; } - static class CIK1 : IK1 { override void toString(scope void delegate(scope const(char)[]) sink, - FormatSpec!char) const { sink("CIK1"); } } - static struct KS1 { void toString(scope void delegate(scope const(char)[]) sink, - FormatSpec!char) const { sink("KS1"); } } - - static union KU1 { void toString(scope void delegate(scope const(char)[]) sink, - FormatSpec!char) const { sink("KU1"); } } - - static class KC1 { void toString(scope void delegate(scope const(char)[]) sink, - FormatSpec!char) const { sink("KC1"); } } - - IF1 cif1 = new CIF1; - assertThrown!FormatException(format("%f", cif1)); - assertThrown!FormatException(format("%f", SF1())); - assertThrown!FormatException(format("%f", UF1())); - assertThrown!FormatException(format("%f", new CF1())); - - IF2 cif2 = new CIF2; - assertThrown!FormatException(format("%f", cif2)); - assertThrown!FormatException(format("%f", SF2())); - assertThrown!FormatException(format("%f", UF2())); - assertThrown!FormatException(format("%f", new CF2())); - - IK1 cik1 = new CIK1; - assert(format("%f", cik1) == "CIK1"); - assert(format("%f", KS1()) == "KS1"); - assert(format("%f", KU1()) == "KU1"); - assert(format("%f", new KC1()) == "KC1"); -} - -/* - Aggregates - */ -void formatValueImpl(Writer, T, Char)(auto ref Writer w, T val, scope const ref FormatSpec!Char f) -if (is(T == class) && !is(T == enum)) -{ - import std.range.primitives : put; - - enforceValidFormatSpec!(T, Char)(f); - - // TODO: remove this check once `@disable override` deprecation cycle is finished - static if (__traits(hasMember, T, "toString") && isSomeFunction!(val.toString)) - static assert(!__traits(isDisabled, T.toString), T.stringof ~ - " cannot be formatted because its `toString` is marked with `@disable`"); - - if (val is null) - put(w, "null"); - else - { - import std.algorithm.comparison : among; - enum overload = hasToString!(T, Char); - with(HasToStringResult) - static if ((is(T == immutable) || is(T == const) || is(T == shared)) && overload == none) - { - // Remove this when Object gets const toString - // https://issues.dlang.org/show_bug.cgi?id=7879 - static if (is(T == immutable)) - put(w, "immutable("); - else static if (is(T == const)) - put(w, "const("); - else static if (is(T == shared)) - put(w, "shared("); - - put(w, typeid(Unqual!T).name); - put(w, ')'); - } - else static if (overload.among(constCharSink, constCharSinkFormatString, constCharSinkFormatSpec) || - (!isInputRange!T && !is(BuiltinTypeOf!T))) - { - formatObject!(Writer, T, Char)(w, val, f); - } - else - { - static if (!is(__traits(parent, T.toString) == Object)) // not inherited Object.toString - { - formatObject(w, val, f); - } - else static if (isInputRange!T) - { - formatRange(w, val, f); - } - else static if (is(BuiltinTypeOf!T X)) - { - X x = val; - formatValueImpl(w, x, f); - } - else - { - formatObject(w, val, f); - } - } - } -} - -@system unittest -{ - import std.array : appender; - import std.range.interfaces : inputRangeObject; - - // class range (https://issues.dlang.org/show_bug.cgi?id=5154) - auto c = inputRangeObject([1,2,3,4]); - formatTest(c, "[1, 2, 3, 4]"); - assert(c.empty); - c = null; - formatTest(c, "null"); -} - -@system unittest -{ - // https://issues.dlang.org/show_bug.cgi?id=5354 - // If the class has both range I/F and custom toString, the use of custom - // toString routine is prioritized. - - // Enable the use of custom toString that gets a sink delegate - // for class formatting. - - enum inputRangeCode = - q{ - int[] arr; - this(int[] a){ arr = a; } - @property int front() const { return arr[0]; } - @property bool empty() const { return arr.length == 0; } - void popFront(){ arr = arr[1 .. $]; } - }; - - class C1 - { - mixin(inputRangeCode); - void toString(scope void delegate(scope const(char)[]) dg, - scope const ref FormatSpec!char f) const - { - dg("[012]"); - } - } - class C2 - { - mixin(inputRangeCode); - void toString(scope void delegate(const(char)[]) dg, string f) const { dg("[012]"); } - } - class C3 - { - mixin(inputRangeCode); - void toString(scope void delegate(const(char)[]) dg) const { dg("[012]"); } - } - class C4 - { - mixin(inputRangeCode); - override string toString() const { return "[012]"; } - } - class C5 - { - mixin(inputRangeCode); - } - - formatTest(new C1([0, 1, 2]), "[012]"); - formatTest(new C2([0, 1, 2]), "[012]"); - formatTest(new C3([0, 1, 2]), "[012]"); - formatTest(new C4([0, 1, 2]), "[012]"); - formatTest(new C5([0, 1, 2]), "[0, 1, 2]"); -} - -// outside the unittest block, otherwise the FQN of the -// class contains the line number of the unittest -version (StdUnittest) -{ - private class C {} -} - -// https://issues.dlang.org/show_bug.cgi?id=7879 -@safe unittest -{ - const(C) c; - auto s = format("%s", c); - assert(s == "null"); - - immutable(C) c2 = new C(); - s = format("%s", c2); - assert(s == "immutable(std.format.internal.write.C)"); - - const(C) c3 = new C(); - s = format("%s", c3); - assert(s == "const(std.format.internal.write.C)"); - - shared(C) c4 = new C(); - s = format("%s", c4); - assert(s == "shared(std.format.internal.write.C)"); -} - -// https://issues.dlang.org/show_bug.cgi?id=7879 -@safe unittest -{ - class F - { - override string toString() const @safe - { - return "Foo"; - } - } - - const(F) c; - auto s = format("%s", c); - assert(s == "null"); - - const(F) c2 = new F(); - s = format("%s", c2); - assert(s == "Foo", s); -} - -void formatValueImpl(Writer, T, Char)(auto ref Writer w, T val, scope const ref FormatSpec!Char f) -if (is(T == interface) && (hasToString!(T, Char) || !is(BuiltinTypeOf!T)) && !is(T == enum)) -{ - import std.range.primitives : put; - - enforceValidFormatSpec!(T, Char)(f); - if (val is null) - put(w, "null"); - else - { - static if (__traits(hasMember, T, "toString") && isSomeFunction!(val.toString)) - static assert(!__traits(isDisabled, T.toString), T.stringof ~ - " cannot be formatted because its `toString` is marked with `@disable`"); - - static if (hasToString!(T, Char) != HasToStringResult.none) - { - formatObject(w, val, f); - } - else static if (isInputRange!T) - { - formatRange(w, val, f); - } - else - { - version (Windows) - { - import core.sys.windows.com : IUnknown; - static if (is(T : IUnknown)) - { - formatValueImpl(w, *cast(void**)&val, f); - } - else - { - formatValueImpl(w, cast(Object) val, f); - } - } - else - { - formatValueImpl(w, cast(Object) val, f); - } - } - } -} - -@system unittest -{ - import std.range.interfaces : InputRange, inputRangeObject; - - // interface - InputRange!int i = inputRangeObject([1,2,3,4]); - formatTest(i, "[1, 2, 3, 4]"); - assert(i.empty); - i = null; - formatTest(i, "null"); - - // interface (downcast to Object) - interface Whatever {} - class C : Whatever - { - override @property string toString() const { return "ab"; } - } - Whatever val = new C; - formatTest(val, "ab"); - - // https://issues.dlang.org/show_bug.cgi?id=11175 - version (Windows) - { - import core.sys.windows.com : IID, IUnknown; - import core.sys.windows.windef : HRESULT; - - interface IUnknown2 : IUnknown { } - - class D : IUnknown2 - { - extern(Windows) HRESULT QueryInterface(const(IID)* riid, void** pvObject) { return typeof(return).init; } - extern(Windows) uint AddRef() { return 0; } - extern(Windows) uint Release() { return 0; } - } - - IUnknown2 d = new D; - string expected = format("%X", cast(void*) d); - formatTest(d, expected); - } -} - -// Maybe T is noncopyable struct, so receive it by 'auto ref'. -void formatValueImpl(Writer, T, Char)(auto ref Writer w, auto ref T val, - scope const ref FormatSpec!Char f) -if ((is(T == struct) || is(T == union)) && (hasToString!(T, Char) || !is(BuiltinTypeOf!T)) - && !is(T == enum)) -{ - import std.range.primitives : put; - - static if (__traits(hasMember, T, "toString") && isSomeFunction!(val.toString)) - static assert(!__traits(isDisabled, T.toString), T.stringof ~ - " cannot be formatted because its `toString` is marked with `@disable`"); - - enforceValidFormatSpec!(T, Char)(f); - static if (hasToString!(T, Char)) - { - formatObject(w, val, f); - } - else static if (isInputRange!T) - { - formatRange(w, val, f); - } - else static if (is(T == struct)) - { - enum left = T.stringof~"("; - enum separator = ", "; - enum right = ")"; - - put(w, left); - static foreach (i; 0 .. T.tupleof.length) - {{ - static if (__traits(identifier, val.tupleof[i]) == "this") - { - // ignore hidden context pointer - } - else static if (0 < i && T.tupleof[i-1].offsetof == T.tupleof[i].offsetof) - { - static if (i == T.tupleof.length - 1 || T.tupleof[i].offsetof != T.tupleof[i+1].offsetof) - { - enum el = separator ~ __traits(identifier, T.tupleof[i]) ~ "}"; - put(w, el); - } - else - { - enum el = separator ~ __traits(identifier, T.tupleof[i]); - put(w, el); - } - } - else static if (i+1 < T.tupleof.length && T.tupleof[i].offsetof == T.tupleof[i+1].offsetof) - { - enum el = (i > 0 ? separator : "") ~ "#{overlap " ~ __traits(identifier, T.tupleof[i]); - put(w, el); - } - else - { - static if (i > 0) - put(w, separator); - formatElement(w, val.tupleof[i], f); - } - }} - put(w, right); - } - else - { - put(w, T.stringof); - } -} - -// https://issues.dlang.org/show_bug.cgi?id=9588 -@safe pure unittest -{ - struct S { int x; bool empty() { return false; } } - formatTest(S(), "S(0)"); -} - -// https://issues.dlang.org/show_bug.cgi?id=4638 -@safe unittest -{ - struct U8 { string toString() const { return "blah"; } } - struct U16 { wstring toString() const { return "blah"; } } - struct U32 { dstring toString() const { return "blah"; } } - formatTest(U8(), "blah"); - formatTest(U16(), "blah"); - formatTest(U32(), "blah"); -} - -// https://issues.dlang.org/show_bug.cgi?id=3890 -@safe unittest -{ - struct Int{ int n; } - struct Pair{ string s; Int i; } - formatTest(Pair("hello", Int(5)), - `Pair("hello", Int(5))`); -} - -// https://issues.dlang.org/show_bug.cgi?id=9117 -@safe unittest -{ - import std.format : formattedWrite; - - static struct Frop {} - - static struct Foo - { - int n = 0; - alias n this; - T opCast(T) () if (is(T == Frop)) - { - return Frop(); - } - string toString() - { - return "Foo"; - } - } - - static struct Bar - { - Foo foo; - alias foo this; - string toString() - { - return "Bar"; - } - } - - const(char)[] result; - void put(scope const char[] s) { result ~= s; } - - Foo foo; - formattedWrite(&put, "%s", foo); // OK - assert(result == "Foo"); - - result = null; - - Bar bar; - formattedWrite(&put, "%s", bar); // NG - assert(result == "Bar"); - - result = null; - - int i = 9; - formattedWrite(&put, "%s", 9); - assert(result == "9"); -} - -@safe unittest -{ - // union formatting without toString - union U1 - { - int n; - string s; - } - U1 u1; - formatTest(u1, "U1"); - - // union formatting with toString - union U2 - { - int n; - string s; - string toString() @trusted const { return s; } - } - U2 u2; - () @trusted { u2.s = "hello"; } (); - formatTest(u2, "hello"); -} - -@safe unittest -{ - import std.array : appender; - import std.format : formatValue; - - // https://issues.dlang.org/show_bug.cgi?id=7230 - static struct Bug7230 - { - string s = "hello"; - union { - string a; - int b; - double c; - } - long x = 10; - } - - Bug7230 bug; - bug.b = 123; - - FormatSpec!char f; - auto w = appender!(char[])(); - formatValue(w, bug, f); - assert(w.data == `Bug7230("hello", #{overlap a, b, c}, 10)`); -} - -@safe unittest -{ - import std.array : appender; - import std.format : formatValue; - - static struct S{ @disable this(this); } - S s; - - FormatSpec!char f; - auto w = appender!string(); - formatValue(w, s, f); - assert(w.data == "S()"); -} - -@safe unittest -{ - import std.array : appender; - import std.format : formatValue; - - //struct Foo { @disable string toString(); } - //Foo foo; - - interface Bar { @disable string toString(); } - Bar bar; - - auto w = appender!(char[])(); - FormatSpec!char f; - - // NOTE: structs cant be tested : the assertion is correct so compilation - // continues and fails when trying to link the unimplemented toString. - //static assert(!__traits(compiles, formatValue(w, foo, f))); - static assert(!__traits(compiles, formatValue(w, bar, f))); -} - -// https://issues.dlang.org/show_bug.cgi?id=21722 -@safe unittest -{ - struct Bar - { - void toString (scope void delegate (scope const(char)[]) sink, string fmt) - { - sink("Hello"); - } - } - - Bar b; - auto result = () @trusted { return format("%b", b); } (); - assert(result == "Hello"); - - static if (hasPreviewIn) - { - struct Foo - { - void toString(scope void delegate(in char[]) sink, in FormatSpec!char fmt) - { - sink("Hello"); - } - } - - Foo f; - assert(format("%b", f) == "Hello"); - - struct Foo2 - { - void toString(scope void delegate(in char[]) sink, string fmt) - { - sink("Hello"); - } - } - - Foo2 f2; - assert(format("%b", f2) == "Hello"); - } -} - -@safe unittest -{ - import std.array : appender; - import std.format : singleSpec; - - // Bug #17269. Behavior similar to `struct A { Nullable!string B; }` - struct StringAliasThis - { - @property string value() const { assert(0); } - alias value this; - string toString() { return "helloworld"; } - private string _value; - } - struct TestContainer - { - StringAliasThis testVar; - } - - auto w = appender!string(); - auto spec = singleSpec("%s"); - formatElement(w, TestContainer(), spec); - - assert(w.data == "TestContainer(helloworld)", w.data); -} - -// https://issues.dlang.org/show_bug.cgi?id=17269 -@safe unittest -{ - import std.typecons : Nullable; - - struct Foo - { - Nullable!string bar; - } - - Foo f; - formatTest(f, "Foo(Nullable.null)"); -} - -// https://issues.dlang.org/show_bug.cgi?id=19003 -@safe unittest -{ - struct S - { - int i; - - @disable this(); - - invariant { assert(this.i); } - - this(int i) @safe in { assert(i); } do { this.i = i; } - - string toString() { return "S"; } - } - - S s = S(1); - - format!"%s"(s); -} - -void enforceValidFormatSpec(T, Char)(scope const ref FormatSpec!Char f) -{ - import std.format : enforceFmt; - import std.range : isInputRange; - import std.format.internal.write : hasToString, HasToStringResult; - - enum overload = hasToString!(T, Char); - static if ( - overload != HasToStringResult.constCharSinkFormatSpec && - overload != HasToStringResult.constCharSinkFormatString && - overload != HasToStringResult.inCharSinkFormatSpec && - overload != HasToStringResult.inCharSinkFormatString && - overload != HasToStringResult.customPutWriterFormatSpec && - !isInputRange!T) - { - enforceFmt(f.spec == 's', - "Expected '%s' format specifier for type '" ~ T.stringof ~ "'"); - } -} - -/* - `enum`s are formatted like their base value - */ -void formatValueImpl(Writer, T, Char)(auto ref Writer w, T val, scope const ref FormatSpec!Char f) -if (is(T == enum)) -{ - import std.array : appender; - import std.range.primitives : put; - - if (f.spec != 's') - return formatValueImpl(w, cast(OriginalType!T) val, f); - - foreach (immutable member; __traits(allMembers, T)) - if (val == __traits(getMember, T, member)) - return formatValueImpl(w, member, f); - - auto w2 = appender!string(); - - // val is not a member of T, output cast(T) rawValue instead. - enum prefix = "cast(" ~ T.stringof ~ ")"; - put(w2, prefix); - static assert(!is(OriginalType!T == T), "OriginalType!" ~ T.stringof ~ - "must not be equal to " ~ T.stringof); - - FormatSpec!Char f2 = f; - f2.width = 0; - formatValueImpl(w2, cast(OriginalType!T) val, f2); - writeAligned(w, w2.data, f); -} - -@safe unittest -{ - enum A { first, second, third } - formatTest(A.second, "second"); - formatTest(cast(A) 72, "cast(A)72"); -} -@safe unittest -{ - enum A : string { one = "uno", two = "dos", three = "tres" } - formatTest(A.three, "three"); - formatTest(cast(A)"mill\ón", "cast(A)mill\ón"); -} -@safe unittest -{ - enum A : bool { no, yes } - formatTest(A.yes, "yes"); - formatTest(A.no, "no"); -} -@safe unittest -{ - // Test for bug 6892 - enum Foo { A = 10 } - formatTest("%s", Foo.A, "A"); - formatTest(">%4s<", Foo.A, "> A<"); - formatTest("%04d", Foo.A, "0010"); - formatTest("%+2u", Foo.A, "10"); - formatTest("%02x", Foo.A, "0a"); - formatTest("%3o", Foo.A, " 12"); - formatTest("%b", Foo.A, "1010"); -} - -@safe pure unittest -{ - enum A { one, two, three } - - string t1 = format("[%6s] [%-6s]", A.one, A.one); - assert(t1 == "[ one] [one ]"); - string t2 = format("[%10s] [%-10s]", cast(A) 10, cast(A) 10); - assert(t2 == "[ cast(A)" ~ "10] [cast(A)" ~ "10 ]"); // due to bug in style checker -} - -// https://issues.dlang.org/show_bug.cgi?id=8921 -@safe unittest -{ - enum E : char { A = 'a', B = 'b', C = 'c' } - E[3] e = [E.A, E.B, E.C]; - formatTest(e, "[A, B, C]"); - - E[] e2 = [E.A, E.B, E.C]; - formatTest(e2, "[A, B, C]"); -} - -/* - Pointers are formatted as hex integers. - */ -void formatValueImpl(Writer, T, Char)(auto ref Writer w, scope const(T) val, scope const ref FormatSpec!Char f) -if (isPointer!T && !is(T == enum) && !hasToString!(T, Char)) -{ - static if (is(typeof({ shared const void* p = val; }))) - alias SharedOf(T) = shared(T); - else - alias SharedOf(T) = T; - - const SharedOf!(void*) p = val; - const pnum = () @trusted { return cast(ulong) p; }(); - - if (f.spec == 's') - { - if (p is null) - { - writeAligned(w, "null", f); - return; - } - FormatSpec!Char fs = f; // fs is copy for change its values. - fs.spec = 'X'; - formatValueImpl(w, pnum, fs); - } - else - { - import std.format : enforceFmt; - enforceFmt(f.spec == 'X' || f.spec == 'x', - "Expected one of %s, %x or %X for pointer type."); - formatValueImpl(w, pnum, f); - } -} - -@safe pure unittest -{ - int* p; - - string t1 = format("[%6s] [%-6s]", p, p); - assert(t1 == "[ null] [null ]"); -} - -@safe pure unittest -{ - int* p = null; - formatTest(p, "null"); - - auto q = () @trusted { return cast(void*) 0xFFEECCAA; }(); - formatTest(q, "FFEECCAA"); -} - -// https://issues.dlang.org/show_bug.cgi?id=11782 -@safe pure unittest -{ - import std.range : iota; - - auto a = iota(0, 10); - auto b = iota(0, 10); - auto p = () @trusted { auto result = &a; return result; }(); - - assert(format("%s",p) != format("%s",b)); -} - -@safe pure unittest -{ - // Test for https://issues.dlang.org/show_bug.cgi?id=7869 - struct S - { - string toString() const { return ""; } - } - S* p = null; - formatTest(p, "null"); - - S* q = () @trusted { return cast(S*) 0xFFEECCAA; } (); - formatTest(q, "FFEECCAA"); -} - -// https://issues.dlang.org/show_bug.cgi?id=9336 -@system pure unittest -{ - shared int i; - format("%s", &i); -} - -// https://issues.dlang.org/show_bug.cgi?id=11778 -@safe pure unittest -{ - import std.exception : assertThrown; - import std.format : FormatException; - - int* p = null; - assertThrown!FormatException(format("%d", p)); - assertThrown!FormatException(format("%04d", () @trusted { return p + 2; } ())); -} - -// https://issues.dlang.org/show_bug.cgi?id=12505 -@safe pure unittest -{ - void* p = null; - formatTest("%08X", p, "00000000"); -} - -/* - SIMD vectors are formatted as arrays. - */ -void formatValueImpl(Writer, V, Char)(auto ref Writer w, const(V) val, scope const ref FormatSpec!Char f) -if (isSIMDVector!V) -{ - formatValueImpl(w, val.array, f); -} - -@safe unittest -{ - import core.simd; // cannot be selective, because float4 might not be defined - - static if (is(float4)) - { - version (X86) - { - version (OSX) {/* https://issues.dlang.org/show_bug.cgi?id=17823 */} - } - else - { - float4 f; - f.array[0] = 1; - f.array[1] = 2; - f.array[2] = 3; - f.array[3] = 4; - formatTest(f, "[1, 2, 3, 4]"); - } - } -} - -/* - Delegates are formatted by `ReturnType delegate(Parameters) FunctionAttributes` - - Known bug: Because of issue https://issues.dlang.org/show_bug.cgi?id=18269 - the FunctionAttributes might be wrong. - */ -void formatValueImpl(Writer, T, Char)(auto ref Writer w, scope const(T), scope const ref FormatSpec!Char f) -if (isDelegate!T) -{ - formatValueImpl(w, T.stringof, f); -} - -@safe unittest -{ - import std.array : appender; - import std.format : formatValue; - - void func() @system { __gshared int x; ++x; throw new Exception("msg"); } - version (linux) - { - FormatSpec!char f; - auto w = appender!string(); - formatValue(w, &func, f); - assert(w.data.length >= 15 && w.data[0 .. 15] == "void delegate()"); - } -} - -// string elements are formatted like UTF-8 string literals. -void formatElement(Writer, T, Char)(auto ref Writer w, T val, scope const ref FormatSpec!Char f) -if (is(StringTypeOf!T) && !hasToString!(T, Char) && !is(T == enum)) -{ - import std.array : appender; - import std.format.write : formattedWrite, formatValue; - import std.range.primitives : put; - import std.utf : decode, UTFException; - - StringTypeOf!T str = val; // https://issues.dlang.org/show_bug.cgi?id=8015 - - if (f.spec == 's') - { - try - { - // ignore other specifications and quote - for (size_t i = 0; i < str.length; ) - { - auto c = decode(str, i); - // \uFFFE and \uFFFF are considered valid by isValidDchar, - // so need checking for interchange. - if (c == 0xFFFE || c == 0xFFFF) - goto LinvalidSeq; - } - put(w, '\"'); - for (size_t i = 0; i < str.length; ) - { - auto c = decode(str, i); - formatChar(w, c, '"'); - } - put(w, '\"'); - return; - } - catch (UTFException) - { - } - - // If val contains invalid UTF sequence, formatted like HexString literal - LinvalidSeq: - static if (is(typeof(str[0]) : const(char))) - { - enum type = ""; - alias IntArr = const(ubyte)[]; - } - else static if (is(typeof(str[0]) : const(wchar))) - { - enum type = "w"; - alias IntArr = const(ushort)[]; - } - else static if (is(typeof(str[0]) : const(dchar))) - { - enum type = "d"; - alias IntArr = const(uint)[]; - } - formattedWrite(w, "[%(cast(" ~ type ~ "char) 0x%X%|, %)]", cast(IntArr) str); - } - else - formatValue(w, str, f); -} - -@safe pure unittest -{ - import std.array : appender; - import std.format.spec : singleSpec; - - auto w = appender!string(); - auto spec = singleSpec("%s"); - formatElement(w, "Hello World", spec); - - assert(w.data == "\"Hello World\""); -} - -@safe unittest -{ - import std.array : appender; - import std.format.spec : singleSpec; - - auto w = appender!string(); - auto spec = singleSpec("%s"); - formatElement(w, "H", spec); - - assert(w.data == "\"H\"", w.data); -} - -// https://issues.dlang.org/show_bug.cgi?id=15888 -@safe pure unittest -{ - import std.array : appender; - import std.format.spec : singleSpec; - - ushort[] a = [0xFF_FE, 0x42]; - auto w = appender!string(); - auto spec = singleSpec("%s"); - formatElement(w, cast(wchar[]) a, spec); - assert(w.data == `[cast(wchar) 0xFFFE, cast(wchar) 0x42]`); - - uint[] b = [0x0F_FF_FF_FF, 0x42]; - w = appender!string(); - spec = singleSpec("%s"); - formatElement(w, cast(dchar[]) b, spec); - assert(w.data == `[cast(dchar) 0xFFFFFFF, cast(dchar) 0x42]`); -} - -// Character elements are formatted like UTF-8 character literals. -void formatElement(Writer, T, Char)(auto ref Writer w, T val, scope const ref FormatSpec!Char f) -if (is(CharTypeOf!T) && !is(T == enum)) -{ - import std.range.primitives : put; - import std.format.write : formatValue; - - if (f.spec == 's') - { - put(w, '\''); - formatChar(w, val, '\''); - put(w, '\''); - } - else - formatValue(w, val, f); -} - -// Maybe T is noncopyable struct, so receive it by 'auto ref'. -void formatElement(Writer, T, Char)(auto ref Writer w, auto ref T val, scope const ref FormatSpec!Char f) -if ((!is(StringTypeOf!T) || hasToString!(T, Char)) && !is(CharTypeOf!T) || is(T == enum)) -{ - import std.format.write : formatValue; - - formatValue(w, val, f); -} - -// Fix for https://issues.dlang.org/show_bug.cgi?id=1591 -int getNthInt(string kind, A...)(uint index, A args) -{ - return getNth!(kind, isIntegral, int)(index, args); -} - -T getNth(string kind, alias Condition, T, A...)(uint index, A args) -{ - import std.conv : text, to; - import std.format : FormatException; - - switch (index) - { - foreach (n, _; A) - { - case n: - static if (Condition!(typeof(args[n]))) - { - return to!T(args[n]); - } - else - { - throw new FormatException( - text(kind, " expected, not ", typeof(args[n]).stringof, - " for argument #", index + 1)); - } - } - default: - throw new FormatException(text("Missing ", kind, " argument")); - } -} - -private bool needToSwapEndianess(Char)(scope const ref FormatSpec!Char f) -{ - import std.system : endian, Endian; - - return endian == Endian.littleEndian && f.flPlus - || endian == Endian.bigEndian && f.flDash; -} - -void writeAligned(Writer, T, Char)(auto ref Writer w, T s, scope const ref FormatSpec!Char f) -if (isSomeString!T) -{ - FormatSpec!Char fs = f; - fs.flZero = false; - writeAligned(w, "", "", s, fs); -} - -@safe pure unittest -{ - import std.array : appender; - import std.format : singleSpec; - - auto w = appender!string(); - auto spec = singleSpec("%s"); - writeAligned(w, "a本Ä", spec); - assert(w.data == "a本Ä", w.data); -} - -@safe pure unittest -{ - import std.array : appender; - import std.format : singleSpec; - - auto w = appender!string(); - auto spec = singleSpec("%10s"); - writeAligned(w, "a本Ä", spec); - assert(w.data == " a本Ä", "|" ~ w.data ~ "|"); -} - -@safe pure unittest -{ - import std.array : appender; - import std.format : singleSpec; - - auto w = appender!string(); - auto spec = singleSpec("%-10s"); - writeAligned(w, "a本Ä", spec); - assert(w.data == "a本Ä ", w.data); -} - -enum PrecisionType -{ - none, - integer, - fractionalDigits, - allDigits, -} - -void writeAligned(Writer, T1, T2, T3, Char)(auto ref Writer w, - T1 prefix, T2 grouped, T3 suffix, scope const ref FormatSpec!Char f, - bool integer_precision = false) -if (isSomeString!T1 && isSomeString!T2 && isSomeString!T3) -{ - writeAligned(w, prefix, grouped, "", suffix, f, - integer_precision ? PrecisionType.integer : PrecisionType.none); -} - -void writeAligned(Writer, T1, T2, T3, T4, Char)(auto ref Writer w, - T1 prefix, T2 grouped, T3 fracts, T4 suffix, scope const ref FormatSpec!Char f, - PrecisionType p = PrecisionType.none) -if (isSomeString!T1 && isSomeString!T2 && isSomeString!T3 && isSomeString!T4) -{ - // writes: left padding, prefix, leading zeros, grouped, fracts, suffix, right padding - - if (p == PrecisionType.integer && f.precision == f.UNSPECIFIED) - p = PrecisionType.none; - - import std.range.primitives : put; - - long prefixWidth; - long groupedWidth = grouped.length; // TODO: does not take graphemes into account - long fractsWidth = fracts.length; // TODO: does not take graphemes into account - long suffixWidth; - - // TODO: remove this workaround which hides https://issues.dlang.org/show_bug.cgi?id=21815 - if (f.width > 0) - { - prefixWidth = getWidth(prefix); - suffixWidth = getWidth(suffix); - } - - auto doGrouping = f.flSeparator && groupedWidth > 0 - && f.separators > 0 && f.separators != f.UNSPECIFIED; - // front = number of symbols left of the leftmost separator - long front = doGrouping ? (groupedWidth - 1) % f.separators + 1 : 0; - // sepCount = number of separators to be inserted - long sepCount = doGrouping ? (groupedWidth - 1) / f.separators : 0; - - long trailingZeros = 0; - if (p == PrecisionType.fractionalDigits) - trailingZeros = f.precision - (fractsWidth - 1); - if (p == PrecisionType.allDigits && f.flHash) - { - if (grouped != "0") - trailingZeros = f.precision - (fractsWidth - 1) - groupedWidth; - else - { - trailingZeros = f.precision - fractsWidth; - foreach (i;0 .. fracts.length) - if (fracts[i] != '0' && fracts[i] != '.') - { - trailingZeros = f.precision - (fracts.length - i); - break; - } - } - } - - auto nodot = fracts == "." && trailingZeros == 0 && !f.flHash; - - if (nodot) fractsWidth = 0; - - long width = prefixWidth + sepCount + groupedWidth + fractsWidth + trailingZeros + suffixWidth; - long delta = f.width - width; - - // with integers, precision is considered the minimum number of digits; - // if digits are missing, we have to recalculate everything - long pregrouped = 0; - if (p == PrecisionType.integer && groupedWidth < f.precision) - { - pregrouped = f.precision - groupedWidth; - delta -= pregrouped; - if (doGrouping) - { - front = ((front - 1) + pregrouped) % f.separators + 1; - delta -= (f.precision - 1) / f.separators - sepCount; - } - } - - // left padding - if ((!f.flZero || p == PrecisionType.integer) && delta > 0) - { - if (f.flEqual) - { - foreach (i ; 0 .. delta / 2 + ((delta % 2 == 1 && !f.flDash) ? 1 : 0)) - put(w, ' '); - } - else if (!f.flDash) - { - foreach (i ; 0 .. delta) - put(w, ' '); - } - } - - // prefix - put(w, prefix); - - // leading grouped zeros - if (f.flZero && p != PrecisionType.integer && !f.flDash && delta > 0) - { - if (doGrouping) - { - // front2 and sepCount2 are the same as above for the leading zeros - long front2 = (delta + front - 1) % (f.separators + 1) + 1; - long sepCount2 = (delta + front - 1) / (f.separators + 1); - delta -= sepCount2; - - // according to POSIX: if the first symbol is a separator, - // an additional zero is put left of it, even if that means, that - // the total width is one more then specified - if (front2 > f.separators) { front2 = 1; } - - foreach (i ; 0 .. delta) - { - if (front2 == 0) - { - put(w, f.separatorChar); - front2 = f.separators; - } - front2--; - - put(w, '0'); - } - - // separator between zeros and grouped - if (front == f.separators) - put(w, f.separatorChar); - } - else - foreach (i ; 0 .. delta) - put(w, '0'); - } - - // grouped content - if (doGrouping) - { - // TODO: this does not take graphemes into account - foreach (i;0 .. pregrouped + grouped.length) - { - if (front == 0) - { - put(w, f.separatorChar); - front = f.separators; - } - front--; - - put(w, i < pregrouped ? '0' : grouped[cast(size_t) (i - pregrouped)]); - } - } - else - { - foreach (i;0 .. pregrouped) - put(w, '0'); - put(w, grouped); - } - - // fracts - if (!nodot) - put(w, fracts); - - // trailing zeros - foreach (i ; 0 .. trailingZeros) - put(w, '0'); - - // suffix - put(w, suffix); - - // right padding - if (delta > 0) - { - if (f.flEqual) - { - foreach (i ; 0 .. delta / 2 + ((delta % 2 == 1 && f.flDash) ? 1 : 0)) - put(w, ' '); - } - else if (f.flDash) - { - foreach (i ; 0 .. delta) - put(w, ' '); - } - } -} - -@safe pure unittest -{ - import std.array : appender; - import std.format : singleSpec; - - auto w = appender!string(); - auto spec = singleSpec("%s"); - writeAligned(w, "pre", "grouping", "suf", spec); - assert(w.data == "pregroupingsuf", w.data); - - w = appender!string(); - spec = singleSpec("%20s"); - writeAligned(w, "pre", "grouping", "suf", spec); - assert(w.data == " pregroupingsuf", w.data); - - w = appender!string(); - spec = singleSpec("%-20s"); - writeAligned(w, "pre", "grouping", "suf", spec); - assert(w.data == "pregroupingsuf ", w.data); - - w = appender!string(); - spec = singleSpec("%020s"); - writeAligned(w, "pre", "grouping", "suf", spec); - assert(w.data == "pre000000groupingsuf", w.data); - - w = appender!string(); - spec = singleSpec("%-020s"); - writeAligned(w, "pre", "grouping", "suf", spec); - assert(w.data == "pregroupingsuf ", w.data); - - w = appender!string(); - spec = singleSpec("%20,1s"); - writeAligned(w, "pre", "grouping", "suf", spec); - assert(w.data == "preg,r,o,u,p,i,n,gsuf", w.data); - - w = appender!string(); - spec = singleSpec("%20,2s"); - writeAligned(w, "pre", "grouping", "suf", spec); - assert(w.data == " pregr,ou,pi,ngsuf", w.data); - - w = appender!string(); - spec = singleSpec("%20,3s"); - writeAligned(w, "pre", "grouping", "suf", spec); - assert(w.data == " pregr,oup,ingsuf", w.data); - - w = appender!string(); - spec = singleSpec("%20,10s"); - writeAligned(w, "pre", "grouping", "suf", spec); - assert(w.data == " pregroupingsuf", w.data); - - w = appender!string(); - spec = singleSpec("%020,1s"); - writeAligned(w, "pre", "grouping", "suf", spec); - assert(w.data == "preg,r,o,u,p,i,n,gsuf", w.data); - - w = appender!string(); - spec = singleSpec("%020,2s"); - writeAligned(w, "pre", "grouping", "suf", spec); - assert(w.data == "pre00,gr,ou,pi,ngsuf", w.data); - - w = appender!string(); - spec = singleSpec("%020,3s"); - writeAligned(w, "pre", "grouping", "suf", spec); - assert(w.data == "pre00,0gr,oup,ingsuf", w.data); - - w = appender!string(); - spec = singleSpec("%020,10s"); - writeAligned(w, "pre", "grouping", "suf", spec); - assert(w.data == "pre000,00groupingsuf", w.data); - - w = appender!string(); - spec = singleSpec("%021,3s"); - writeAligned(w, "pre", "grouping", "suf", spec); - assert(w.data == "pre000,0gr,oup,ingsuf", w.data); - - // According to https://github.com/dlang/phobos/pull/7112 this - // is defined by POSIX standard: - w = appender!string(); - spec = singleSpec("%022,3s"); - writeAligned(w, "pre", "grouping", "suf", spec); - assert(w.data == "pre0,000,0gr,oup,ingsuf", w.data); - - w = appender!string(); - spec = singleSpec("%023,3s"); - writeAligned(w, "pre", "grouping", "suf", spec); - assert(w.data == "pre0,000,0gr,oup,ingsuf", w.data); - - w = appender!string(); - spec = singleSpec("%,3s"); - writeAligned(w, "pre", "grouping", "suf", spec); - assert(w.data == "pregr,oup,ingsuf", w.data); -} - -@safe pure unittest -{ - import std.array : appender; - import std.format : singleSpec; - - auto w = appender!string(); - auto spec = singleSpec("%.10s"); - writeAligned(w, "pre", "grouping", "suf", spec, true); - assert(w.data == "pre00groupingsuf", w.data); - - w = appender!string(); - spec = singleSpec("%.10,3s"); - writeAligned(w, "pre", "grouping", "suf", spec, true); - assert(w.data == "pre0,0gr,oup,ingsuf", w.data); - - w = appender!string(); - spec = singleSpec("%25.10,3s"); - writeAligned(w, "pre", "grouping", "suf", spec, true); - assert(w.data == " pre0,0gr,oup,ingsuf", w.data); - - // precision has precedence over zero flag - w = appender!string(); - spec = singleSpec("%025.12,3s"); - writeAligned(w, "pre", "grouping", "suf", spec, true); - assert(w.data == " pre000,0gr,oup,ingsuf", w.data); - - w = appender!string(); - spec = singleSpec("%025.13,3s"); - writeAligned(w, "pre", "grouping", "suf", spec, true); - assert(w.data == " pre0,000,0gr,oup,ingsuf", w.data); -} - -@safe unittest -{ - assert(format("%,d", 1000) == "1,000"); - assert(format("%,f", 1234567.891011) == "1,234,567.891011"); - assert(format("%,?d", '?', 1000) == "1?000"); - assert(format("%,1d", 1000) == "1,0,0,0", format("%,1d", 1000)); - assert(format("%,*d", 4, -12345) == "-1,2345"); - assert(format("%,*?d", 4, '_', -12345) == "-1_2345"); - assert(format("%,6?d", '_', -12345678) == "-12_345678"); - assert(format("%12,3.3f", 1234.5678) == " 1,234.568", "'" ~ - format("%12,3.3f", 1234.5678) ~ "'"); -} - -private long getWidth(T)(T s) -{ - import std.algorithm.searching : all; - import std.uni : graphemeStride; - - // check for non-ascii character - if (s.all!(a => a <= 0x7F)) return s.length; - - //TODO: optimize this - long width = 0; - for (size_t i; i < s.length; i += graphemeStride(s, i)) - ++width; - return width; -} - -enum RoundingClass { ZERO, LOWER, FIVE, UPPER } -enum RoundingMode { up, down, toZero, toNearestTiesToEven, toNearestTiesAwayFromZero } - -bool round(T)(ref T sequence, size_t left, size_t right, RoundingClass type, bool negative, char max = '9') -in (left >= 0) // should be left > 0, but if you know ahead, that there's no carry, left == 0 is fine -in (left < sequence.length) -in (right >= 0) -in (right <= sequence.length) -in (right >= left) -in (max == '9' || max == 'f' || max == 'F') -{ - import std.math.hardware; - - auto mode = RoundingMode.toNearestTiesToEven; - - if (!__ctfe) - { - // std.math's FloatingPointControl isn't available on all target platforms - static if (is(FloatingPointControl)) - { - switch (FloatingPointControl.rounding) - { - case FloatingPointControl.roundUp: - mode = RoundingMode.up; - break; - case FloatingPointControl.roundDown: - mode = RoundingMode.down; - break; - case FloatingPointControl.roundToZero: - mode = RoundingMode.toZero; - break; - case FloatingPointControl.roundToNearest: - mode = RoundingMode.toNearestTiesToEven; - break; - default: assert(false, "Unknown floating point rounding mode"); - } - } - } - - bool roundUp = false; - if (mode == RoundingMode.up) - roundUp = type != RoundingClass.ZERO && !negative; - else if (mode == RoundingMode.down) - roundUp = type != RoundingClass.ZERO && negative; - else if (mode == RoundingMode.toZero) - roundUp = false; - else - { - roundUp = type == RoundingClass.UPPER; - - if (type == RoundingClass.FIVE) - { - // IEEE754 allows for two different ways of implementing roundToNearest: - - if (mode == RoundingMode.toNearestTiesAwayFromZero) - roundUp = true; - else - { - // Round to nearest, ties to even - auto last = sequence[right - 1]; - if (last == '.') last = sequence[right - 2]; - roundUp = (last <= '9' && last % 2 != 0) || (last > '9' && last % 2 == 0); - } - } - } - - if (!roundUp) return false; - - foreach_reverse (i;left .. right) - { - if (sequence[i] == '.') continue; - if (sequence[i] == max) - sequence[i] = '0'; - else - { - if (max != '9' && sequence[i] == '9') - sequence[i] = max == 'f' ? 'a' : 'A'; - else - sequence[i]++; - return false; - } - } - - sequence[left - 1] = '1'; - return true; -} - -@safe unittest -{ - char[10] c; - size_t left = 5; - size_t right = 8; - - c[4 .. 8] = "x.99"; - assert(round(c, left, right, RoundingClass.UPPER, false) == true); - assert(c[4 .. 8] == "1.00"); - - c[4 .. 8] = "x.99"; - assert(round(c, left, right, RoundingClass.FIVE, false) == true); - assert(c[4 .. 8] == "1.00"); - - c[4 .. 8] = "x.99"; - assert(round(c, left, right, RoundingClass.LOWER, false) == false); - assert(c[4 .. 8] == "x.99"); - - c[4 .. 8] = "x.99"; - assert(round(c, left, right, RoundingClass.ZERO, false) == false); - assert(c[4 .. 8] == "x.99"); - - import std.math.hardware; - static if (is(FloatingPointControl)) - { - FloatingPointControl fpctrl; - - fpctrl.rounding = FloatingPointControl.roundUp; - - c[4 .. 8] = "x.99"; - assert(round(c, left, right, RoundingClass.UPPER, false) == true); - assert(c[4 .. 8] == "1.00"); - - c[4 .. 8] = "x.99"; - assert(round(c, left, right, RoundingClass.FIVE, false) == true); - assert(c[4 .. 8] == "1.00"); - - c[4 .. 8] = "x.99"; - assert(round(c, left, right, RoundingClass.LOWER, false) == true); - assert(c[4 .. 8] == "1.00"); - - c[4 .. 8] = "x.99"; - assert(round(c, left, right, RoundingClass.ZERO, false) == false); - assert(c[4 .. 8] == "x.99"); - - fpctrl.rounding = FloatingPointControl.roundDown; - - c[4 .. 8] = "x.99"; - assert(round(c, left, right, RoundingClass.UPPER, false) == false); - assert(c[4 .. 8] == "x.99"); - - c[4 .. 8] = "x.99"; - assert(round(c, left, right, RoundingClass.FIVE, false) == false); - assert(c[4 .. 8] == "x.99"); - - c[4 .. 8] = "x.99"; - assert(round(c, left, right, RoundingClass.LOWER, false) == false); - assert(c[4 .. 8] == "x.99"); - - c[4 .. 8] = "x.99"; - assert(round(c, left, right, RoundingClass.ZERO, false) == false); - assert(c[4 .. 8] == "x.99"); - - fpctrl.rounding = FloatingPointControl.roundToZero; - - c[4 .. 8] = "x.99"; - assert(round(c, left, right, RoundingClass.UPPER, false) == false); - assert(c[4 .. 8] == "x.99"); - - c[4 .. 8] = "x.99"; - assert(round(c, left, right, RoundingClass.FIVE, false) == false); - assert(c[4 .. 8] == "x.99"); - - c[4 .. 8] = "x.99"; - assert(round(c, left, right, RoundingClass.LOWER, false) == false); - assert(c[4 .. 8] == "x.99"); - - c[4 .. 8] = "x.99"; - assert(round(c, left, right, RoundingClass.ZERO, false) == false); - assert(c[4 .. 8] == "x.99"); - } -} - -@safe unittest -{ - char[10] c; - size_t left = 5; - size_t right = 8; - - c[4 .. 8] = "x8.5"; - assert(round(c, left, right, RoundingClass.UPPER, true) == false); - assert(c[4 .. 8] == "x8.6"); - - c[4 .. 8] = "x8.5"; - assert(round(c, left, right, RoundingClass.FIVE, true) == false); - assert(c[4 .. 8] == "x8.6"); - - c[4 .. 8] = "x8.4"; - assert(round(c, left, right, RoundingClass.FIVE, true) == false); - assert(c[4 .. 8] == "x8.4"); - - c[4 .. 8] = "x8.5"; - assert(round(c, left, right, RoundingClass.LOWER, true) == false); - assert(c[4 .. 8] == "x8.5"); - - c[4 .. 8] = "x8.5"; - assert(round(c, left, right, RoundingClass.ZERO, true) == false); - assert(c[4 .. 8] == "x8.5"); - - import std.math.hardware; - static if (is(FloatingPointControl)) - { - FloatingPointControl fpctrl; - - fpctrl.rounding = FloatingPointControl.roundUp; - - c[4 .. 8] = "x8.5"; - assert(round(c, left, right, RoundingClass.UPPER, true) == false); - assert(c[4 .. 8] == "x8.5"); - - c[4 .. 8] = "x8.5"; - assert(round(c, left, right, RoundingClass.FIVE, true) == false); - assert(c[4 .. 8] == "x8.5"); - - c[4 .. 8] = "x8.5"; - assert(round(c, left, right, RoundingClass.LOWER, true) == false); - assert(c[4 .. 8] == "x8.5"); - - c[4 .. 8] = "x8.5"; - assert(round(c, left, right, RoundingClass.ZERO, true) == false); - assert(c[4 .. 8] == "x8.5"); - - fpctrl.rounding = FloatingPointControl.roundDown; - - c[4 .. 8] = "x8.5"; - assert(round(c, left, right, RoundingClass.UPPER, true) == false); - assert(c[4 .. 8] == "x8.6"); - - c[4 .. 8] = "x8.5"; - assert(round(c, left, right, RoundingClass.FIVE, true) == false); - assert(c[4 .. 8] == "x8.6"); - - c[4 .. 8] = "x8.5"; - assert(round(c, left, right, RoundingClass.LOWER, true) == false); - assert(c[4 .. 8] == "x8.6"); - - c[4 .. 8] = "x8.5"; - assert(round(c, left, right, RoundingClass.ZERO, true) == false); - assert(c[4 .. 8] == "x8.5"); - - fpctrl.rounding = FloatingPointControl.roundToZero; - - c[4 .. 8] = "x8.5"; - assert(round(c, left, right, RoundingClass.UPPER, true) == false); - assert(c[4 .. 8] == "x8.5"); - - c[4 .. 8] = "x8.5"; - assert(round(c, left, right, RoundingClass.FIVE, true) == false); - assert(c[4 .. 8] == "x8.5"); - - c[4 .. 8] = "x8.5"; - assert(round(c, left, right, RoundingClass.LOWER, true) == false); - assert(c[4 .. 8] == "x8.5"); - - c[4 .. 8] = "x8.5"; - assert(round(c, left, right, RoundingClass.ZERO, true) == false); - assert(c[4 .. 8] == "x8.5"); - } -} - -@safe unittest -{ - char[10] c; - size_t left = 5; - size_t right = 8; - - c[4 .. 8] = "x8.9"; - assert(round(c, left, right, RoundingClass.UPPER, true, 'f') == false); - assert(c[4 .. 8] == "x8.a"); - - c[4 .. 8] = "x8.9"; - assert(round(c, left, right, RoundingClass.UPPER, true, 'F') == false); - assert(c[4 .. 8] == "x8.A"); - - c[4 .. 8] = "x8.f"; - assert(round(c, left, right, RoundingClass.UPPER, true, 'f') == false); - assert(c[4 .. 8] == "x9.0"); -} - -version (StdUnittest) -private void formatTest(T)(T val, string expected, size_t ln = __LINE__, string fn = __FILE__) -{ - formatTest(val, [expected], ln, fn); -} - -version (StdUnittest) -private void formatTest(T)(string fmt, T val, string expected, size_t ln = __LINE__, string fn = __FILE__) @safe -{ - formatTest(fmt, val, [expected], ln, fn); -} - -version (StdUnittest) -private void formatTest(T)(T val, string[] expected, size_t ln = __LINE__, string fn = __FILE__) -{ - import core.exception : AssertError; - import std.algorithm.searching : canFind; - import std.array : appender; - import std.conv : text; - import std.exception : enforce; - import std.format.write : formatValue; - - FormatSpec!char f; - auto w = appender!string(); - formatValue(w, val, f); - enforce!AssertError(expected.canFind(w.data), - text("expected one of `", expected, "`, result = `", w.data, "`"), fn, ln); -} - -version (StdUnittest) -private void formatTest(T)(string fmt, T val, string[] expected, size_t ln = __LINE__, string fn = __FILE__) @safe -{ - import core.exception : AssertError; - import std.algorithm.searching : canFind; - import std.array : appender; - import std.conv : text; - import std.exception : enforce; - import std.format.write : formattedWrite; - - auto w = appender!string(); - formattedWrite(w, fmt, val); - enforce!AssertError(expected.canFind(w.data), - text("expected one of `", expected, "`, result = `", w.data, "`"), fn, ln); -} diff --git a/phobos/std/format/package.d b/phobos/std/format/package.d deleted file mode 100644 index f1d4705..0000000 --- a/phobos/std/format/package.d +++ /dev/null @@ -1,1832 +0,0 @@ -// Written in the D programming language. - -/** -This package provides string formatting functionality using -`printf` style format strings. - -$(BOOKTABLE , -$(TR $(TH Submodule) $(TH Function Name) $(TH Description)) -$(TR - $(TD $(I package)) - $(TD $(LREF format)) - $(TD Converts its arguments according to a format string into a string.) -) -$(TR - $(TD $(I package)) - $(TD $(LREF sformat)) - $(TD Converts its arguments according to a format string into a buffer.) -) -$(TR - $(TD $(I package)) - $(TD $(LREF FormatException)) - $(TD Signals a problem while formatting.) -) -$(TR - $(TD $(MREF_ALTTEXT $(D write), std, format, write)) - $(TD $(REF_ALTTEXT $(D formattedWrite), formattedWrite, std, format, write)) - $(TD Converts its arguments according to a format string and writes - the result to an output range.) -) -$(TR - $(TD $(MREF_ALTTEXT $(D write), std, format, write)) - $(TD $(REF_ALTTEXT $(D formatValue), formatValue, std, format, write)) - $(TD Formats a value of any type according to a format specifier and - writes the result to an output range.) -) -$(TR - $(TD $(MREF_ALTTEXT $(D read), std, format, read)) - $(TD $(REF_ALTTEXT $(D formattedRead), formattedRead, std, format, read)) - $(TD Reads an input range according to a format string and stores the read - values into its arguments.) -) -$(TR - $(TD $(MREF_ALTTEXT $(D read), std, format, read)) - $(TD $(REF_ALTTEXT $(D unformatValue), unformatValue, std, format, read)) - $(TD Reads a value from the given input range and converts it according to - a format specifier.) -) -$(TR - $(TD $(MREF_ALTTEXT $(D spec), std, format, spec)) - $(TD $(REF_ALTTEXT $(D FormatSpec), FormatSpec, std, format, spec)) - $(TD A general handler for format strings.) -) -$(TR - $(TD $(MREF_ALTTEXT $(D spec), std, format, spec)) - $(TD $(REF_ALTTEXT $(D singleSpec), singleSpec, std, format, spec)) - $(TD Helper function that returns a `FormatSpec` for a single format specifier.) -)) - -Limitation: This package does not support localization, but - adheres to the rounding mode of the floating point unit, if - available. - -$(SECTION3 Format Strings) - -The functions contained in this package use $(I format strings). A -format string describes the layout of another string for reading or -writing purposes. A format string is composed of normal text -interspersed with $(I format specifiers). A format specifier starts -with a percentage sign $(B '%'), optionally followed by one or more -$(I parameters) and ends with a $(I format indicator). A format -indicator may be a simple $(I format character) or a $(I compound -indicator). - -$(I Format strings) are composed according to the following grammar: - -$(PRE -$(I FormatString): - $(I FormatStringItem) $(I FormatString) -$(I FormatStringItem): - $(I Character) - $(I FormatSpecifier) -$(I FormatSpecifier): - $(B '%') $(I Parameters) $(I FormatIndicator) - -$(I FormatIndicator): - $(I FormatCharacter) - $(I CompoundIndicator) -$(I FormatCharacter): - $(I see remark below) -$(I CompoundIndicator): - $(B '$(LPAREN)') $(I FormatString) $(B '%$(RPAREN)') - $(B '$(LPAREN)') $(I FormatString) $(B '%|') $(I Delimiter) $(B '%$(RPAREN)') -$(I Delimiter) - $(I empty) - $(I Character) $(I Delimiter) - -$(I Parameters): - $(I Position) $(I Flags) $(I Width) $(I Precision) $(I Separator) -$(I Position): - $(I empty) - $(I Integer) $(B '$') - $(I Integer) $(B ':') $(I Integer) $(B '$') - $(I Integer) $(B ':') $(B '$') -$(I Flags): - $(I empty) - $(I Flag) $(I Flags) -$(I Flag): - $(B '-')|$(B '+')|$(B ' ')|$(B '0')|$(B '#')|$(B '=') -$(I Width): - $(I OptionalPositionalInteger) -$(I Precision): - $(I empty) - $(B '.') $(I OptionalPositionalInteger) -$(I Separator): - $(I empty) - $(B ',') $(I OptionalInteger) - $(B ',') $(I OptionalInteger) $(B '?') -$(I OptionalInteger): - $(I empty) - $(I Integer) - $(B '*') -$(I OptionalPositionalInteger): - $(I OptionalInteger) - $(B '*') $(I Integer) $(B '$') - -$(I Character) - $(B '%%') - $(I AnyCharacterExceptPercent) -$(I Integer): - $(I NonZeroDigit) $(I Digits) -$(I Digits): - $(I empty) - $(I Digit) $(I Digits) -$(I NonZeroDigit): - $(B '1')|$(B '2')|$(B '3')|$(B '4')|$(B '5')|$(B '6')|$(B '7')|$(B '8')|$(B '9') -$(I Digit): - $(B '0')|$(B '1')|$(B '2')|$(B '3')|$(B '4')|$(B '5')|$(B '6')|$(B '7')|$(B '8')|$(B '9') -) - -Note: $(I FormatCharacter) is unspecified. It can be any character -that has no other purpose in this grammar, but it is -recommended to assign (lower- and uppercase) letters. - -Note: The $(I Parameters) of a $(I CompoundIndicator) are currently -limited to a $(B '-') flag. - -$(SECTION4 Format Indicator) - -The $(I format indicator) can either be a single character or an -expression surrounded by $(B %\() and $(B %\)). It specifies the -basic manner in which a value will be formatted and is the minimum -requirement to format a value. - -The following characters can be used as $(I format characters): - -$(BOOKTABLE , - $(TR $(TH FormatCharacter) $(TH Semantics)) - $(TR $(TD $(B 's')) - $(TD To be formatted in a human readable format. - Can be used with all types.)) - $(TR $(TD $(B 'c')) - $(TD To be formatted as a character.)) - $(TR $(TD $(B 'd')) - $(TD To be formatted as a signed decimal integer.)) - $(TR $(TD $(B 'u')) - $(TD To be formatted as a decimal image of the underlying bit representation.)) - $(TR $(TD $(B 'b')) - $(TD To be formatted as a binary image of the underlying bit representation.)) - $(TR $(TD $(B 'o')) - $(TD To be formatted as an octal image of the underlying bit representation.)) - $(TR $(TD $(B 'x') / $(B 'X')) - $(TD To be formatted as a hexadecimal image of the underlying bit representation.)) - $(TR $(TD $(B 'e') / $(B 'E')) - $(TD To be formatted as a real number in decimal scientific notation.)) - $(TR $(TD $(B 'f') / $(B 'F')) - $(TD To be formatted as a real number in decimal natural notation.)) - $(TR $(TD $(B 'g') / $(B 'G')) - $(TD To be formatted as a real number in decimal short notation. - Depending on the number, a scientific notation or - a natural notation is used.)) - $(TR $(TD $(B 'a') / $(B 'A')) - $(TD To be formatted as a real number in hexadecimal scientific notation.)) - $(TR $(TD $(B 'r')) - $(TD To be formatted as raw bytes. - The output may not be printable and depends on endianness.)) -) - -The $(I compound indicator) can be used to describe compound types -like arrays or structs in more detail. A compound type is enclosed -within $(B '%\(') and $(B '%\)'). The enclosed sub-format string is -applied to individual elements. The trailing portion of the -sub-format string following the specifier for the element is -interpreted as the delimiter, and is therefore omitted following the -last element. The $(B '%|') specifier may be used to explicitly -indicate the start of the delimiter, so that the preceding portion of -the string will be included following the last element. - -The $(I format string) inside of the $(I compound indicator) should -contain exactly one $(I format specifier) (two in case of associative -arrays), which specifies the formatting mode of the elements of the -compound type. This $(I format specifier) can be a $(I compound -indicator) itself. - -Note: Inside a $(I compound indicator), strings and characters are -escaped automatically. To avoid this behavior, use `"%-$(LPAREN)"` -instead of `"%$(LPAREN)"`. - -$(SECTION4 Flags) - -There are several flags that affect the outcome of the formatting. - -$(BOOKTABLE , - $(TR $(TH Flag) $(TH Semantics)) - $(TR $(TD $(B '-')) - $(TD When the formatted result is shorter than the value - given by the width parameter, the output is left - justified. Without the $(B '-') flag, the output remains - right justified. - - There are two exceptions where the $(B '-') flag has a - different meaning: (1) with $(B 'r') it denotes to use little - endian and (2) in case of a compound indicator it means that - no special handling of the members is applied.)) - $(TR $(TD $(B '=')) - $(TD When the formatted result is shorter than the value - given by the width parameter, the output is centered. - If the central position is not possible it is moved slightly - to the right. In this case, if $(B '-') flag is present in - addition to the $(B '=') flag, it is moved slightly to the left.)) - $(TR $(TD $(B '+') / $(B ' ')) - $(TD Applies to numerical values. By default, positive numbers are not - formatted to include the `+` sign. With one of these two flags present, - positive numbers are preceded by a plus sign or a space. - When both flags are present, a plus sign is used. - - In case of $(B 'r'), a big endian format is used.)) - $(TR $(TD $(B '0')) - $(TD Is applied to numerical values that are printed right justified. - If the zero flag is present, the space left to the number is - filled with zeros instead of spaces.)) - $(TR $(TD $(B '#')) - $(TD Denotes that an alternative output must be used. This depends on the type - to be formatted and the $(I format character) used. See the - sections below for more information.)) -) - -$(SECTION4 Width$(COMMA) Precision and Separator) - -The $(I width) parameter specifies the minimum width of the result. - -The meaning of $(I precision) depends on the format indicator. For -integers it denotes the minimum number of digits printed, for -real numbers it denotes the number of fractional digits and for -strings and compound types it denotes the maximum number of elements -that are included in the output. - -A $(I separator) is used for formatting numbers. If it is specified, -the output is divided into chunks of three digits, separated by a $(B -','). The number of digits in a chunk can be given explicitly by -providing a number or a $(B '*') after the $(B ','). - -In all three cases the number of digits can be replaced by a $(B -'*'). In this scenario, the next argument is used as the number of -digits. If the argument is a negative number, the $(I precision) and -$(I separator) parameters are considered unspecified. For $(I width), -the absolute value is used and the $(B '-') flag is set. - -The $(I separator) can also be followed by a $(B '?'). In that case, -an additional argument is used to specify the symbol that should be -used to separate the chunks. - -$(SECTION4 Position) - -By default, the arguments are processed in the provided order. With -the $(I position) parameter it is possible to address arguments -directly. It is also possible to denote a series of arguments with -two numbers separated by $(B ':'), that are all processed in the same -way. The second number can be omitted. In that case the series ends -with the last argument. - -It's also possible to use positional arguments for $(I width), $(I -precision) and $(I separator) by adding a number and a $(B -'$(DOLLAR)') after the $(B '*'). - -$(SECTION4 Types) - -This section describes the result of combining types with format -characters. It is organized in 2 subsections: a list of general -information regarding the formatting of types in the presence of -format characters and a table that contains details for every -available combination of type and format character. - -When formatting types, the following rules apply: - -$(UL - $(LI If the format character is upper case, the resulting string will - be formatted using upper case letters.) - $(LI The default precision for floating point numbers is 6 digits.) - $(LI Rounding of floating point numbers adheres to the rounding mode - of the floating point unit, if available.) - $(LI The floating point values `NaN` and `Infinity` are formatted as - `nan` and `inf`, possibly preceded by $(B '+') or $(B '-') sign.) - $(LI Formatting reals is only supported for 64 bit reals and 80 bit reals. - All other reals are cast to double before they are formatted. This will - cause the result to be `inf` for very large numbers.) - $(LI Characters and strings formatted with the $(B 's') format character - inside of compound types are surrounded by single and double quotes - and unprintable characters are escaped. To avoid this, a $(B '-') - flag can be specified for the compound specifier - $(LPAREN)e.g. `"%-$(LPAREN)%s%$(RPAREN)"` instead of `"%$(LPAREN)%s%$(RPAREN)"` $(RPAREN).) - $(LI Structs, unions, classes and interfaces are formatted by calling a - `toString` method if available. - See $(MREF_ALTTEXT $(D module std.format.write), std, format, write) for more - details.) - $(LI Only part of these combinations can be used for reading. See - $(MREF_ALTTEXT $(D module std.format.read), std, format, read) for more - detailed information.) -) - -This table contains descriptions for every possible combination of -type and format character: - -$(BOOKTABLE , - $(TR $(THMINWIDTH Type) $(THMINWIDTH Format Character) $(TH Formatted as...)) - $(TR $(MULTIROW_CELL 1, `null`) - $(TD $(B 's')) - $(TD `null`) - ) - $(TR $(MULTIROW_CELL 3, `bool`) - $(TD $(B 's')) - $(TD `false` or `true`) - ) - $(TR $(TD $(B 'b'), $(B 'd'), $(B 'o'), $(B 'u'), $(B 'x'), $(B 'X')) - $(TD As the integrals 0 or 1 with the same format character. - - $(I Please note, that $(B 'o') and $(B 'x') with $(B '#') flag - might produce unexpected results due to special handling of - the value 0.)) - ) - $(TR $(TD $(B 'r')) - $(TD `\0` or `\1`) - ) - $(TR $(MULTIROW_CELL 4, $(I Integral)) - $(TD $(B 's'), $(B 'd')) - $(TD A signed decimal number. The $(B '#') flag is ignored.) - ) - $(TR $(TD $(B 'b'), $(B 'o'), $(B 'u'), $(B 'x'), $(B 'X')) - $(TD An unsigned binary, decimal, octal or hexadecimal number. - - In case of $(B 'o') and $(B 'x'), the $(B '#') flag - denotes that the number must be preceded by `0` and `0x`, with - the exception of the value 0, where this does not apply. For - $(B 'b') and $(B 'u') the $(B '#') flag has no effect.) - ) - $(TR $(TD $(B 'e'), $(B 'E'), $(B 'f'), $(B 'F'), $(B 'g'), $(B 'G'), $(B 'a'), $(B 'A')) - $(TD As a floating point value with the same specifier. - - Default precision is large enough to add all digits - of the integral value. - - In case of ($B 'a') and $(B 'A'), the integral digit can be - any hexadecimal digit. - ) - ) - $(TR $(TD $(B 'r')) - $(TD Characters taken directly from the binary representation.) - ) - $(TR $(MULTIROW_CELL 5, $(I Floating Point)) - $(TD $(B 'e'), $(B 'E')) - $(TD Scientific notation: Exactly one integral digit followed by a dot - and fractional digits, followed by the exponent. - The exponent is formatted as $(B 'e') followed by - a $(B '+') or $(B '-') sign, followed by at least - two digits. - - When there are no fractional digits and the $(B '#') flag - is $(I not) present, the dot is omitted.) - ) - $(TR $(TD $(B 'f'), $(B 'F')) - $(TD Natural notation: Integral digits followed by a dot and - fractional digits. - - When there are no fractional digits and the $(B '#') flag - is $(I not) present, the dot is omitted. - - $(I Please note: the difference between $(B 'f') and $(B 'F') - is only visible for `NaN` and `Infinity`.)) - ) - $(TR $(TD $(B 's'), $(B 'g'), $(B 'G')) - $(TD Short notation: If the absolute value is larger than `10 ^^ precision` - or smaller than `0.0001`, the scientific notation is used. - If not, the natural notation is applied. - - In both cases $(I precision) denotes the count of all digits, including - the integral digits. Trailing zeros (including a trailing dot) are removed. - - If $(B '#') flag is present, trailing zeros are not removed.) - ) - $(TR $(TD $(B 'a'), $(B 'A')) - $(TD Hexadecimal scientific notation: `0x` followed by `1` - (or `0` in case of value zero or denormalized number) - followed by a dot, fractional digits in hexadecimal - notation and an exponent. The exponent is build by `p`, - followed by a sign and the exponent in $(I decimal) notation. - - When there are no fractional digits and the $(B '#') flag - is $(I not) present, the dot is omitted.) - ) - $(TR $(TD $(B 'r')) - $(TD Characters taken directly from the binary representation.) - ) - $(TR $(MULTIROW_CELL 3, $(I Character)) - $(TD $(B 's'), $(B 'c')) - $(TD As the character. - - Inside of a compound indicator $(B 's') is treated differently: The - character is surrounded by single quotes and non printable - characters are escaped. This can be avoided by preceding - the compound indicator with a $(B '-') flag - $(LPAREN)e.g. `"%-$(LPAREN)%s%$(RPAREN)"`$(RPAREN).) - ) - $(TR $(TD $(B 'b'), $(B 'd'), $(B 'o'), $(B 'u'), $(B 'x'), $(B 'X')) - $(TD As the integral that represents the character.) - ) - $(TR $(TD $(B 'r')) - $(TD Characters taken directly from the binary representation.) - ) - $(TR $(MULTIROW_CELL 3, $(I String)) - $(TD $(B 's')) - $(TD The sequence of characters that form the string. - - Inside of a compound indicator the string is surrounded by double quotes - and non printable characters are escaped. This can be avoided - by preceding the compound indicator with a $(B '-') flag - $(LPAREN)e.g. `"%-$(LPAREN)%s%$(RPAREN)"`$(RPAREN).) - ) - $(TR $(TD $(B 'r')) - $(TD The sequence of characters, each formatted with $(B 'r').) - ) - $(TR $(TD compound) - $(TD As an array of characters.) - ) - $(TR $(MULTIROW_CELL 3, $(I Array)) - $(TD $(B 's')) - $(TD When the elements are characters, the array is formatted as - a string. In all other cases the array is surrounded by square brackets - and the elements are separated by a comma and a space. If the elements - are strings, they are surrounded by double quotes and non - printable characters are escaped.) - ) - $(TR $(TD $(B 'r')) - $(TD The sequence of the elements, each formatted with $(B 'r').) - ) - $(TR $(TD compound) - $(TD The sequence of the elements, each formatted according to the specifications - given inside of the compound specifier.) - ) - $(TR $(MULTIROW_CELL 2, $(I Associative Array)) - $(TD $(B 's')) - $(TD As a sequence of the elements in unpredictable order. The output is - surrounded by square brackets. The elements are separated by a - comma and a space. The elements are formatted as `key:value`.) - ) - $(TR $(TD compound) - $(TD As a sequence of the elements in unpredictable order. Each element - is formatted according to the specifications given inside of the - compound specifier. The first specifier is used for formatting - the key and the second specifier is used for formatting the value. - The order can be changed with positional arguments. For example - `"%(%2$s (%1$s), %)"` will write the value, followed by the key in - parenthesis.) - ) - $(TR $(MULTIROW_CELL 2, $(I Enum)) - $(TD $(B 's')) - $(TD The name of the value. If the name is not available, the base value - is used, preceeded by a cast.) - ) - $(TR $(TD All, but $(B 's')) - $(TD Enums can be formatted with all format characters that can be used - with the base value. In that case they are formatted like the base value.) - ) - $(TR $(MULTIROW_CELL 3, $(I Input Range)) - $(TD $(B 's')) - $(TD When the elements of the range are characters, they are written like a string. - In all other cases, the elements are enclosed by square brackets and separated - by a comma and a space.) - ) - $(TR $(TD $(B 'r')) - $(TD The sequence of the elements, each formatted with $(B 'r').) - ) - $(TR $(TD compound) - $(TD The sequence of the elements, each formatted according to the specifications - given inside of the compound specifier.) - ) - $(TR $(MULTIROW_CELL 1, $(I Struct)) - $(TD $(B 's')) - $(TD When the struct has neither an applicable `toString` - nor is an input range, it is formatted as follows: - `StructType(field1, field2, ...)`.) - ) - $(TR $(MULTIROW_CELL 1, $(I Class)) - $(TD $(B 's')) - $(TD When the class has neither an applicable `toString` - nor is an input range, it is formatted as the - fully qualified name of the class.) - ) - $(TR $(MULTIROW_CELL 1, $(I Union)) - $(TD $(B 's')) - $(TD When the union has neither an applicable `toString` - nor is an input range, it is formatted as its base name.) - ) - $(TR $(MULTIROW_CELL 2, $(I Pointer)) - $(TD $(B 's')) - $(TD A null pointer is formatted as 'null'. All other pointers are - formatted as hexadecimal numbers with the format character $(B 'X').) - ) - $(TR $(TD $(B 'x'), $(B 'X')) - $(TD Formatted as a hexadecimal number.) - ) - $(TR $(MULTIROW_CELL 3, $(I SIMD vector)) - $(TD $(B 's')) - $(TD The array is surrounded by square brackets - and the elements are separated by a comma and a space.) - ) - $(TR $(TD $(B 'r')) - $(TD The sequence of the elements, each formatted with $(B 'r').) - ) - $(TR $(TD compound) - $(TD The sequence of the elements, each formatted according to the specifications - given inside of the compound specifier.) - ) - $(TR $(MULTIROW_CELL 1, $(I Delegate)) - $(TD $(B 's'), $(B 'r'), compound) - $(TD As the `.stringof` of this delegate treated as a string. - - $(I Please note: The implementation is currently buggy - and its use is discouraged.)) - ) -) - -Copyright: Copyright The D Language Foundation 2000-2021. - -Macros: -SUBREF = $(REF_ALTTEXT $2, $2, std, format, $1)$(NBSP) -MULTIROW_CELL = $+ -THMINWIDTH = $0 - -License: $(HTTP boost.org/LICENSE_1_0.txt, Boost License 1.0). - -Authors: $(HTTP walterbright.com, Walter Bright), $(HTTP erdani.com, -Andrei Alexandrescu), and Kenji Hara - -Source: $(PHOBOSSRC std/format/package.d) - */ -module std.format; - -/// Simple use: -@safe unittest -{ - // Easiest way is to use `%s` everywhere: - assert(format("I got %s %s for %s euros.", 30, "eggs", 5.27) == "I got 30 eggs for 5.27 euros."); - - // Other format characters provide more control: - assert(format("I got %b %(%X%) for %f euros.", 30, "eggs", 5.27) == "I got 11110 65676773 for 5.270000 euros."); -} - -/// Compound specifiers allow formatting arrays and other compound types: -@safe unittest -{ -/* -The trailing end of the sub-format string following the specifier for -each item is interpreted as the array delimiter, and is therefore -omitted following the last array item: - */ - assert(format("My items are %(%s %).", [1,2,3]) == "My items are 1 2 3."); - assert(format("My items are %(%s, %).", [1,2,3]) == "My items are 1, 2, 3."); - -/* -The "%|" delimiter specifier may be used to indicate where the -delimiter begins, so that the portion of the format string prior to -it will be retained in the last array element: - */ - assert(format("My items are %(-%s-%|, %).", [1,2,3]) == "My items are -1-, -2-, -3-."); - -/* -These compound format specifiers may be nested in the case of a -nested array argument: - */ - auto mat = [[1, 2, 3], - [4, 5, 6], - [7, 8, 9]]; - - assert(format("%(%(%d %) - %)", mat), "1 2 3 - 4 5 6 - 7 8 9"); - assert(format("[%(%(%d %) - %)]", mat), "[1 2 3 - 4 5 6 - 7 8 9]"); - assert(format("[%([%(%d %)]%| - %)]", mat), "[1 2 3] - [4 5 6] - [7 8 9]"); - -/* -Strings and characters are escaped automatically inside compound -format specifiers. To avoid this behavior, use "%-(" instead of "%(": - */ - assert(format("My friends are %s.", ["John", "Nancy"]) == `My friends are ["John", "Nancy"].`); - assert(format("My friends are %(%s, %).", ["John", "Nancy"]) == `My friends are "John", "Nancy".`); - assert(format("My friends are %-(%s, %).", ["John", "Nancy"]) == `My friends are John, Nancy.`); -} - -/// Using parameters: -@safe unittest -{ - // Flags can be used to influence to outcome: - assert(format("%g != %+#g", 3.14, 3.14) == "3.14 != +3.14000"); - - // Width and precision help to arrange the formatted result: - assert(format(">%10.2f<", 1234.56789) == "> 1234.57<"); - - // Numbers can be grouped: - assert(format("%,4d", int.max) == "21,4748,3647"); - - // It's possible to specify the position of an argument: - assert(format("%3$s %1$s", 3, 17, 5) == "5 3"); -} - -/// Providing parameters as arguments: -@safe unittest -{ - // Width as argument - assert(format(">%*s<", 10, "abc") == "> abc<"); - - // Precision as argument - assert(format(">%.*f<", 5, 123.2) == ">123.20000<"); - - // Grouping as argument - assert(format("%,*d", 1, int.max) == "2,1,4,7,4,8,3,6,4,7"); - - // Grouping separator as argument - assert(format("%,3?d", '_', int.max) == "2_147_483_647"); - - // All at once - assert(format("%*.*,*?d", 20, 15, 6, '/', int.max) == " 000/002147/483647"); -} - -public import std.format.read; -public import std.format.spec; -public import std.format.write; - -import std.exception : enforce; -import std.range.primitives : isInputRange; -import std.traits : CharTypeOf, isSomeChar, isSomeString, StringTypeOf; -import std.format.internal.write : hasToString; - -/** -Signals an issue encountered while formatting. - */ -class FormatException : Exception -{ - /// Generic constructor. - @safe @nogc pure nothrow - this() - { - super("format error"); - } - - /** - Creates a new instance of `FormatException`. - - Params: - msg = message of the exception - fn = file name of the file where the exception was created (optional) - ln = line number of the file where the exception was created (optional) - next = for internal use, should always be null (optional) - */ - @safe @nogc pure nothrow - this(string msg, string fn = __FILE__, size_t ln = __LINE__, Throwable next = null) - { - super(msg, fn, ln, next); - } -} - -/// -@safe unittest -{ - import std.exception : assertThrown; - - assertThrown!FormatException(format("%d", "foo")); -} - -package alias enforceFmt = enforce!FormatException; - -// @@@DEPRECATED_[2.107.0]@@@ -deprecated("formatElement was accidentally made public and will be removed in 2.107.0") -void formatElement(Writer, T, Char)(auto ref Writer w, T val, scope const ref FormatSpec!Char f) -if (is(StringTypeOf!T) && !hasToString!(T, Char) && !is(T == enum)) -{ - import std.format.internal.write : fe = formatElement; - - fe(w, val, f); -} - -// @@@DEPRECATED_[2.107.0]@@@ -deprecated("formatElement was accidentally made public and will be removed in 2.107.0") -void formatElement(Writer, T, Char)(auto ref Writer w, T val, scope const ref FormatSpec!Char f) -if (is(CharTypeOf!T) && !is(T == enum)) -{ - import std.format.internal.write : fe = formatElement; - - fe(w, val, f); -} - -// @@@DEPRECATED_[2.107.0]@@@ -deprecated("formatElement was accidentally made public and will be removed in 2.107.0") -void formatElement(Writer, T, Char)(auto ref Writer w, auto ref T val, scope const ref FormatSpec!Char f) -if ((!is(StringTypeOf!T) || hasToString!(T, Char)) && !is(CharTypeOf!T) || is(T == enum)) -{ - import std.format.internal.write : fe = formatElement; - - fe(w, val, f); -} - -// Like NullSink, but toString() isn't even called at all. Used to test the format string. -package struct NoOpSink -{ - void put(E)(scope const E) pure @safe @nogc nothrow {} -} - -// @@@DEPRECATED_[2.107.0]@@@ -deprecated("unformatElement was accidentally made public and will be removed in 2.107.0") -T unformatElement(T, Range, Char)(ref Range input, scope const ref FormatSpec!Char spec) -if (isInputRange!Range) -{ - import std.format.internal.read : ue = unformatElement; - - return ue(input, spec); -} - -// Used to check format strings are compatible with argument types -package(std) enum checkFormatException(alias fmt, Args...) = -{ - import std.conv : text; - - try - { - auto n = .formattedWrite(NoOpSink(), fmt, Args.init); - - enforceFmt(n == Args.length, text("Orphan format arguments: args[", n, "..", Args.length, "]")); - } - catch (Exception e) - return e.msg; - return null; -}(); - -/** -Converts its arguments according to a format string into a string. - -The second version of `format` takes the format string as template -argument. In this case, it is checked for consistency at -compile-time and produces slightly faster code, because the length of -the output buffer can be estimated in advance. - -Params: - fmt = a $(MREF_ALTTEXT format string, std,format) - args = a variadic list of arguments to be formatted - Char = character type of `fmt` - Args = a variadic list of types of the arguments - -Returns: - The formatted string. - -Throws: - A $(LREF FormatException) if formatting did not succeed. - -See_Also: - $(LREF sformat) for a variant, that tries to avoid garbage collection. - */ -immutable(Char)[] format(Char, Args...)(in Char[] fmt, Args args) -if (isSomeChar!Char) -{ - import std.array : appender; - - auto w = appender!(immutable(Char)[]); - auto n = formattedWrite(w, fmt, args); - version (all) - { - // In the future, this check will be removed to increase consistency - // with formattedWrite - import std.conv : text; - enforceFmt(n == args.length, text("Orphan format arguments: args[", n, "..", args.length, "]")); - } - return w.data; -} - -/// -@safe pure unittest -{ - assert(format("Here are %d %s.", 3, "apples") == "Here are 3 apples."); - - assert("Increase: %7.2f %%".format(17.4285) == "Increase: 17.43 %"); -} - -@safe pure unittest -{ - import std.exception : assertCTFEable, assertThrown; - - assertCTFEable!( - { - assert(format("foo") == "foo"); - assert(format("foo%%") == "foo%"); - assert(format("foo%s", 'C') == "fooC"); - assert(format("%s foo", "bar") == "bar foo"); - assert(format("%s foo %s", "bar", "abc") == "bar foo abc"); - assert(format("foo %d", -123) == "foo -123"); - assert(format("foo %d", 123) == "foo 123"); - - assertThrown!FormatException(format("foo %s")); - assertThrown!FormatException(format("foo %s", 123, 456)); - - assert(format("hel%slo%s%s%s", "world", -138, 'c', true) == "helworldlo-138ctrue"); - }); - - assert(is(typeof(format("happy")) == string)); - assert(is(typeof(format("happy"w)) == wstring)); - assert(is(typeof(format("happy"d)) == dstring)); -} - -// https://issues.dlang.org/show_bug.cgi?id=16661 -@safe pure unittest -{ - assert(format("%.2f"d, 0.4) == "0.40"); - assert("%02d"d.format(1) == "01"d); -} - -@safe unittest -{ - int i; - string s; - - s = format("hello world! %s %s %s%s%s", true, 57, 1_000_000_000, 'x', " foo"); - assert(s == "hello world! true 57 1000000000x foo"); - - s = format("%s %A %s", 1.67, -1.28, float.nan); - assert(s == "1.67 -0X1.47AE147AE147BP+0 nan", s); - - s = format("%x %X", 0x1234AF, 0xAFAFAFAF); - assert(s == "1234af AFAFAFAF"); - - s = format("%b %o", 0x1234AF, 0xAFAFAFAF); - assert(s == "100100011010010101111 25753727657"); - - s = format("%d %s", 0x1234AF, 0xAFAFAFAF); - assert(s == "1193135 2947526575"); -} - -@safe unittest -{ - import std.conv : octal; - - string s; - int i; - - s = format("%#06.*f", 2, 12.345); - assert(s == "012.35"); - - s = format("%#0*.*f", 6, 2, 12.345); - assert(s == "012.35"); - - s = format("%7.4g:", 12.678); - assert(s == " 12.68:"); - - s = format("%7.4g:", 12.678L); - assert(s == " 12.68:"); - - s = format("%04f|%05d|%#05x|%#5x", -4.0, -10, 1, 1); - assert(s == "-4.000000|-0010|0x001| 0x1"); - - i = -10; - s = format("%d|%3d|%03d|%1d|%01.4f", i, i, i, i, cast(double) i); - assert(s == "-10|-10|-10|-10|-10.0000"); - - i = -5; - s = format("%d|%3d|%03d|%1d|%01.4f", i, i, i, i, cast(double) i); - assert(s == "-5| -5|-05|-5|-5.0000"); - - i = 0; - s = format("%d|%3d|%03d|%1d|%01.4f", i, i, i, i, cast(double) i); - assert(s == "0| 0|000|0|0.0000"); - - i = 5; - s = format("%d|%3d|%03d|%1d|%01.4f", i, i, i, i, cast(double) i); - assert(s == "5| 5|005|5|5.0000"); - - i = 10; - s = format("%d|%3d|%03d|%1d|%01.4f", i, i, i, i, cast(double) i); - assert(s == "10| 10|010|10|10.0000"); - - s = format("%.0d", 0); - assert(s == "0"); - - s = format("%.g", .34); - assert(s == "0.3"); - - s = format("%.0g", .34); - assert(s == "0.3"); - - s = format("%.2g", .34); - assert(s == "0.34"); - - s = format("%0.0008f", 1e-08); - assert(s == "0.00000001"); - - s = format("%0.0008f", 1e-05); - assert(s == "0.00001000"); - - s = "helloworld"; - string r; - r = format("%.2s", s[0 .. 5]); - assert(r == "he"); - r = format("%.20s", s[0 .. 5]); - assert(r == "hello"); - r = format("%8s", s[0 .. 5]); - assert(r == " hello"); - - byte[] arrbyte = new byte[4]; - arrbyte[0] = 100; - arrbyte[1] = -99; - arrbyte[3] = 0; - r = format("%s", arrbyte); - assert(r == "[100, -99, 0, 0]"); - - ubyte[] arrubyte = new ubyte[4]; - arrubyte[0] = 100; - arrubyte[1] = 200; - arrubyte[3] = 0; - r = format("%s", arrubyte); - assert(r == "[100, 200, 0, 0]"); - - short[] arrshort = new short[4]; - arrshort[0] = 100; - arrshort[1] = -999; - arrshort[3] = 0; - r = format("%s", arrshort); - assert(r == "[100, -999, 0, 0]"); - - ushort[] arrushort = new ushort[4]; - arrushort[0] = 100; - arrushort[1] = 20_000; - arrushort[3] = 0; - r = format("%s", arrushort); - assert(r == "[100, 20000, 0, 0]"); - - int[] arrint = new int[4]; - arrint[0] = 100; - arrint[1] = -999; - arrint[3] = 0; - r = format("%s", arrint); - assert(r == "[100, -999, 0, 0]"); - - long[] arrlong = new long[4]; - arrlong[0] = 100; - arrlong[1] = -999; - arrlong[3] = 0; - r = format("%s", arrlong); - assert(r == "[100, -999, 0, 0]"); - - ulong[] arrulong = new ulong[4]; - arrulong[0] = 100; - arrulong[1] = 999; - arrulong[3] = 0; - r = format("%s", arrulong); - assert(r == "[100, 999, 0, 0]"); - - string[] arr2 = new string[4]; - arr2[0] = "hello"; - arr2[1] = "world"; - arr2[3] = "foo"; - r = format("%s", arr2); - assert(r == `["hello", "world", "", "foo"]`); - - r = format("%.8d", 7); - assert(r == "00000007"); - r = format("%.8x", 10); - assert(r == "0000000a"); - - r = format("%-3d", 7); - assert(r == "7 "); - - r = format("%-1*d", 4, 3); - assert(r == "3 "); - - r = format("%*d", -3, 7); - assert(r == "7 "); - - r = format("%.*d", -3, 7); - assert(r == "7"); - - r = format("%-1.*f", 2, 3.1415); - assert(r == "3.14"); - - r = format("abc"c); - assert(r == "abc"); - - //format() returns the same type as inputted. - wstring wr; - wr = format("def"w); - assert(wr == "def"w); - - dstring dr; - dr = format("ghi"d); - assert(dr == "ghi"d); - - // Empty static character arrays work as well - const char[0] cempty; - assert(format("test%spath", cempty) == "testpath"); - const wchar[0] wempty; - assert(format("test%spath", wempty) == "testpath"); - const dchar[0] dempty; - assert(format("test%spath", dempty) == "testpath"); - - void* p = () @trusted { return cast(void*) 0xDEADBEEF; } (); - r = format("%s", p); - assert(r == "DEADBEEF"); - - r = format("%#x", 0xabcd); - assert(r == "0xabcd"); - r = format("%#X", 0xABCD); - assert(r == "0XABCD"); - - r = format("%#o", octal!12345); - assert(r == "012345"); - r = format("%o", 9); - assert(r == "11"); - r = format("%#o", 0); // https://issues.dlang.org/show_bug.cgi?id=15663 - assert(r == "0"); - - r = format("%+d", 123); - assert(r == "+123"); - r = format("%+d", -123); - assert(r == "-123"); - r = format("% d", 123); - assert(r == " 123"); - r = format("% d", -123); - assert(r == "-123"); - - r = format("%%"); - assert(r == "%"); - - r = format("%d", true); - assert(r == "1"); - r = format("%d", false); - assert(r == "0"); - - r = format("%d", 'a'); - assert(r == "97"); - wchar wc = 'a'; - r = format("%d", wc); - assert(r == "97"); - dchar dc = 'a'; - r = format("%d", dc); - assert(r == "97"); - - byte b = byte.max; - r = format("%x", b); - assert(r == "7f"); - r = format("%x", ++b); - assert(r == "80"); - r = format("%x", ++b); - assert(r == "81"); - - short sh = short.max; - r = format("%x", sh); - assert(r == "7fff"); - r = format("%x", ++sh); - assert(r == "8000"); - r = format("%x", ++sh); - assert(r == "8001"); - - i = int.max; - r = format("%x", i); - assert(r == "7fffffff"); - r = format("%x", ++i); - assert(r == "80000000"); - r = format("%x", ++i); - assert(r == "80000001"); - - r = format("%x", 10); - assert(r == "a"); - r = format("%X", 10); - assert(r == "A"); - r = format("%x", 15); - assert(r == "f"); - r = format("%X", 15); - assert(r == "F"); - - Object c = null; - r = () @trusted { return format("%s", c); } (); - assert(r == "null"); - - enum TestEnum - { - Value1, Value2 - } - r = format("%s", TestEnum.Value2); - assert(r == "Value2"); - - immutable(char[5])[int] aa = ([3:"hello", 4:"betty"]); - r = () @trusted { return format("%s", aa.values); } (); - assert(r == `["hello", "betty"]` || r == `["betty", "hello"]`); - r = format("%s", aa); - assert(r == `[3:"hello", 4:"betty"]` || r == `[4:"betty", 3:"hello"]`); - - static const dchar[] ds = ['a','b']; - for (int j = 0; j < ds.length; ++j) - { - r = format(" %d", ds[j]); - if (j == 0) - assert(r == " 97"); - else - assert(r == " 98"); - } - - r = format(">%14d<, %s", 15, [1,2,3]); - assert(r == "> 15<, [1, 2, 3]"); - - assert(format("%8s", "bar") == " bar"); - assert(format("%8s", "b\u00e9ll\u00f4") == " b\u00e9ll\u00f4"); -} - -@safe unittest -{ - import std.exception : assertCTFEable; - - assertCTFEable!( - { - auto tmp = format("%,d", 1000); - assert(tmp == "1,000", "'" ~ tmp ~ "'"); - - tmp = format("%,?d", 'z', 1234567); - assert(tmp == "1z234z567", "'" ~ tmp ~ "'"); - - tmp = format("%10,?d", 'z', 1234567); - assert(tmp == " 1z234z567", "'" ~ tmp ~ "'"); - - tmp = format("%11,2?d", 'z', 1234567); - assert(tmp == " 1z23z45z67", "'" ~ tmp ~ "'"); - - tmp = format("%11,*?d", 2, 'z', 1234567); - assert(tmp == " 1z23z45z67", "'" ~ tmp ~ "'"); - - tmp = format("%11,*d", 2, 1234567); - assert(tmp == " 1,23,45,67", "'" ~ tmp ~ "'"); - - tmp = format("%11,2d", 1234567); - assert(tmp == " 1,23,45,67", "'" ~ tmp ~ "'"); - }); -} - -@safe unittest -{ - auto tmp = format("%,f", 1000.0); - assert(tmp == "1,000.000000", "'" ~ tmp ~ "'"); - - tmp = format("%,f", 1234567.891011); - assert(tmp == "1,234,567.891011", "'" ~ tmp ~ "'"); - - tmp = format("%,f", -1234567.891011); - assert(tmp == "-1,234,567.891011", "'" ~ tmp ~ "'"); - - tmp = format("%,2f", 1234567.891011); - assert(tmp == "1,23,45,67.891011", "'" ~ tmp ~ "'"); - - tmp = format("%18,f", 1234567.891011); - assert(tmp == " 1,234,567.891011", "'" ~ tmp ~ "'"); - - tmp = format("%18,?f", '.', 1234567.891011); - assert(tmp == " 1.234.567.891011", "'" ~ tmp ~ "'"); - - tmp = format("%,?.3f", 'ä', 1234567.891011); - assert(tmp == "1ä234ä567.891", "'" ~ tmp ~ "'"); - - tmp = format("%,*?.3f", 1, 'ä', 1234567.891011); - assert(tmp == "1ä2ä3ä4ä5ä6ä7.891", "'" ~ tmp ~ "'"); - - tmp = format("%,4?.3f", '_', 1234567.891011); - assert(tmp == "123_4567.891", "'" ~ tmp ~ "'"); - - tmp = format("%12,3.3f", 1234.5678); - assert(tmp == " 1,234.568", "'" ~ tmp ~ "'"); - - tmp = format("%,e", 3.141592653589793238462); - assert(tmp == "3.141593e+00", "'" ~ tmp ~ "'"); - - tmp = format("%15,e", 3.141592653589793238462); - assert(tmp == " 3.141593e+00", "'" ~ tmp ~ "'"); - - tmp = format("%15,e", -3.141592653589793238462); - assert(tmp == " -3.141593e+00", "'" ~ tmp ~ "'"); - - tmp = format("%.4,*e", 2, 3.141592653589793238462); - assert(tmp == "3.1416e+00", "'" ~ tmp ~ "'"); - - tmp = format("%13.4,*e", 2, 3.141592653589793238462); - assert(tmp == " 3.1416e+00", "'" ~ tmp ~ "'"); - - tmp = format("%,.0f", 3.14); - assert(tmp == "3", "'" ~ tmp ~ "'"); - - tmp = format("%3,g", 1_000_000.123456); - assert(tmp == "1e+06", "'" ~ tmp ~ "'"); - - tmp = format("%19,?f", '.', -1234567.891011); - assert(tmp == " -1.234.567.891011", "'" ~ tmp ~ "'"); -} - -// Test for multiple indexes -@safe unittest -{ - auto tmp = format("%2:5$s", 1, 2, 3, 4, 5); - assert(tmp == "2345", tmp); -} - -// https://issues.dlang.org/show_bug.cgi?id=18047 -@safe unittest -{ - auto cmp = " 123,456"; - assert(cmp.length == 12, format("%d", cmp.length)); - auto tmp = format("%12,d", 123456); - assert(tmp.length == 12, format("%d", tmp.length)); - - assert(tmp == cmp, "'" ~ tmp ~ "'"); -} - -// https://issues.dlang.org/show_bug.cgi?id=17459 -@safe unittest -{ - auto cmp = "100"; - auto tmp = format("%0d", 100); - assert(tmp == cmp, tmp); - - cmp = "0100"; - tmp = format("%04d", 100); - assert(tmp == cmp, tmp); - - cmp = "0,000,000,100"; - tmp = format("%012,3d", 100); - assert(tmp == cmp, tmp); - - cmp = "0,000,001,000"; - tmp = format("%012,3d", 1_000); - assert(tmp == cmp, tmp); - - cmp = "0,000,100,000"; - tmp = format("%012,3d", 100_000); - assert(tmp == cmp, tmp); - - cmp = "0,001,000,000"; - tmp = format("%012,3d", 1_000_000); - assert(tmp == cmp, tmp); - - cmp = "0,100,000,000"; - tmp = format("%012,3d", 100_000_000); - assert(tmp == cmp, tmp); -} - -// https://issues.dlang.org/show_bug.cgi?id=17459 -@safe unittest -{ - auto cmp = "100,000"; - auto tmp = format("%06,d", 100_000); - assert(tmp == cmp, tmp); - - cmp = "100,000"; - tmp = format("%07,d", 100_000); - assert(tmp == cmp, tmp); - - cmp = "0,100,000"; - tmp = format("%08,d", 100_000); - assert(tmp == cmp, tmp); -} - -// https://issues.dlang.org/show_bug.cgi?id=20288 -@safe unittest -{ - string s = format("%,.2f", double.nan); - assert(s == "nan", s); - - s = format("%,.2F", double.nan); - assert(s == "NAN", s); - - s = format("%,.2f", -double.nan); - assert(s == "-nan", s); - - s = format("%,.2F", -double.nan); - assert(s == "-NAN", s); - - string g = format("^%13s$", "nan"); - string h = "^ nan$"; - assert(g == h, "\ngot:" ~ g ~ "\nexp:" ~ h); - string a = format("^%13,3.2f$", double.nan); - string b = format("^%13,3.2F$", double.nan); - string c = format("^%13,3.2f$", -double.nan); - string d = format("^%13,3.2F$", -double.nan); - assert(a == "^ nan$", "\ngot:'"~ a ~ "'\nexp:'^ nan$'"); - assert(b == "^ NAN$", "\ngot:'"~ b ~ "'\nexp:'^ NAN$'"); - assert(c == "^ -nan$", "\ngot:'"~ c ~ "'\nexp:'^ -nan$'"); - assert(d == "^ -NAN$", "\ngot:'"~ d ~ "'\nexp:'^ -NAN$'"); - - a = format("^%-13,3.2f$", double.nan); - b = format("^%-13,3.2F$", double.nan); - c = format("^%-13,3.2f$", -double.nan); - d = format("^%-13,3.2F$", -double.nan); - assert(a == "^nan $", "\ngot:'"~ a ~ "'\nexp:'^nan $'"); - assert(b == "^NAN $", "\ngot:'"~ b ~ "'\nexp:'^NAN $'"); - assert(c == "^-nan $", "\ngot:'"~ c ~ "'\nexp:'^-nan $'"); - assert(d == "^-NAN $", "\ngot:'"~ d ~ "'\nexp:'^-NAN $'"); - - a = format("^%+13,3.2f$", double.nan); - b = format("^%+13,3.2F$", double.nan); - c = format("^%+13,3.2f$", -double.nan); - d = format("^%+13,3.2F$", -double.nan); - assert(a == "^ +nan$", "\ngot:'"~ a ~ "'\nexp:'^ +nan$'"); - assert(b == "^ +NAN$", "\ngot:'"~ b ~ "'\nexp:'^ +NAN$'"); - assert(c == "^ -nan$", "\ngot:'"~ c ~ "'\nexp:'^ -nan$'"); - assert(d == "^ -NAN$", "\ngot:'"~ d ~ "'\nexp:'^ -NAN$'"); - - a = format("^%-+13,3.2f$", double.nan); - b = format("^%-+13,3.2F$", double.nan); - c = format("^%-+13,3.2f$", -double.nan); - d = format("^%-+13,3.2F$", -double.nan); - assert(a == "^+nan $", "\ngot:'"~ a ~ "'\nexp:'^+nan $'"); - assert(b == "^+NAN $", "\ngot:'"~ b ~ "'\nexp:'^+NAN $'"); - assert(c == "^-nan $", "\ngot:'"~ c ~ "'\nexp:'^-nan $'"); - assert(d == "^-NAN $", "\ngot:'"~ d ~ "'\nexp:'^-NAN $'"); - - a = format("^%- 13,3.2f$", double.nan); - b = format("^%- 13,3.2F$", double.nan); - c = format("^%- 13,3.2f$", -double.nan); - d = format("^%- 13,3.2F$", -double.nan); - assert(a == "^ nan $", "\ngot:'"~ a ~ "'\nexp:'^ nan $'"); - assert(b == "^ NAN $", "\ngot:'"~ b ~ "'\nexp:'^ NAN $'"); - assert(c == "^-nan $", "\ngot:'"~ c ~ "'\nexp:'^-nan $'"); - assert(d == "^-NAN $", "\ngot:'"~ d ~ "'\nexp:'^-NAN $'"); -} - -@safe unittest -{ - struct S - { - int a; - - void toString(void delegate(const(char)[]) sink, string fmt) - { - auto spec = singleSpec(fmt); - sink.formatValue(a, spec); - } - } - - S s = S(1); - auto result = () @trusted { return format!"%5,3d"(s); } (); - assert(result == " 1"); -} - -// https://issues.dlang.org/show_bug.cgi?id=23245 -@safe unittest -{ - static struct S - { - string toString() { return "S"; } - } - - S[1] s; - assert(format("%s", s) == "[S]"); -} - -// https://issues.dlang.org/show_bug.cgi?id=23246 -@safe unittest -{ - static struct S - { - string toString() { return "S"; } - } - - S[int] s = [0 : S()]; - assert(format("%s", s) == "[0:S]"); -} - -/// ditto -typeof(fmt) format(alias fmt, Args...)(Args args) -if (isSomeString!(typeof(fmt))) -{ - import std.array : appender; - import std.range.primitives : ElementEncodingType; - import std.traits : Unqual; - - alias e = checkFormatException!(fmt, Args); - alias Char = Unqual!(ElementEncodingType!(typeof(fmt))); - - static assert(!e, e); - auto w = appender!(immutable(Char)[]); - - // no need to traverse the string twice during compile time - if (!__ctfe) - { - enum len = guessLength!Char(fmt); - w.reserve(len); - } - else - { - w.reserve(fmt.length); - } - - formattedWrite(w, fmt, args); - return w.data; -} - -/// The format string can be checked at compile-time: -@safe pure unittest -{ - auto s = format!"%s is %s"("Pi", 3.14); - assert(s == "Pi is 3.14"); - - // This line doesn't compile, because 3.14 cannot be formatted with %d: - // s = format!"%s is %d"("Pi", 3.14); -} - -@safe pure unittest -{ - string s; - static assert(!__traits(compiles, {s = format!"%l"();})); // missing arg - static assert(!__traits(compiles, {s = format!""(404);})); // surplus arg - static assert(!__traits(compiles, {s = format!"%d"(4.03);})); // incompatible arg -} - -// https://issues.dlang.org/show_bug.cgi?id=17381 -@safe pure unittest -{ - static assert(!__traits(compiles, format!"%s"(1.5, 2))); - static assert(!__traits(compiles, format!"%f"(1.5, 2))); - static assert(!__traits(compiles, format!"%s"(1.5L, 2))); - static assert(!__traits(compiles, format!"%f"(1.5L, 2))); -} - -// called during compilation to guess the length of the -// result of format -private size_t guessLength(Char, S)(S fmtString) -{ - import std.array : appender; - - size_t len; - auto output = appender!(immutable(Char)[])(); - auto spec = FormatSpec!Char(fmtString); - while (spec.writeUpToNextSpec(output)) - { - // take a guess - if (spec.width == 0 && (spec.precision == spec.UNSPECIFIED || spec.precision == spec.DYNAMIC)) - { - switch (spec.spec) - { - case 'c': - ++len; - break; - case 'd': - case 'x': - case 'X': - len += 3; - break; - case 'b': - len += 8; - break; - case 'f': - case 'F': - len += 10; - break; - case 's': - case 'e': - case 'E': - case 'g': - case 'G': - len += 12; - break; - default: break; - } - - continue; - } - - if ((spec.spec == 'e' || spec.spec == 'E' || spec.spec == 'g' || - spec.spec == 'G' || spec.spec == 'f' || spec.spec == 'F') && - spec.precision != spec.UNSPECIFIED && spec.precision != spec.DYNAMIC && - spec.width == 0 - ) - { - len += spec.precision + 5; - continue; - } - - if (spec.width == spec.precision) - len += spec.width; - else if (spec.width > 0 && spec.width != spec.DYNAMIC && - (spec.precision == spec.UNSPECIFIED || spec.width > spec.precision)) - { - len += spec.width; - } - else if (spec.precision != spec.UNSPECIFIED && spec.precision > spec.width) - len += spec.precision; - } - len += output.data.length; - return len; -} - -@safe pure -unittest -{ - assert(guessLength!char("%c") == 1); - assert(guessLength!char("%d") == 3); - assert(guessLength!char("%x") == 3); - assert(guessLength!char("%b") == 8); - assert(guessLength!char("%f") == 10); - assert(guessLength!char("%s") == 12); - assert(guessLength!char("%02d") == 2); - assert(guessLength!char("%02d") == 2); - assert(guessLength!char("%4.4d") == 4); - assert(guessLength!char("%2.4f") == 4); - assert(guessLength!char("%02d:%02d:%02d") == 8); - assert(guessLength!char("%0.2f") == 7); - assert(guessLength!char("%0*d") == 0); -} - -/** -Converts its arguments according to a format string into a buffer. -The buffer has to be large enough to hold the formatted string. - -The second version of `sformat` takes the format string as a template -argument. In this case, it is checked for consistency at -compile-time. - -Params: - buf = the buffer where the formatted string should go - fmt = a $(MREF_ALTTEXT format string, std,format) - args = a variadic list of arguments to be formatted - Char = character type of `fmt` - Args = a variadic list of types of the arguments - -Returns: - A slice of `buf` containing the formatted string. - -Throws: - A $(REF_ALTTEXT RangeError, RangeError, core, exception) if `buf` - isn't large enough to hold the formatted string - and a $(LREF FormatException) if formatting did not succeed. - -Note: - In theory this function should be `@nogc`. But with the current - implementation there are some cases where allocations occur: - - $(UL - $(LI An exception is thrown.) - $(LI A custom `toString` function of a compound type allocates.)) - */ -char[] sformat(Char, Args...)(return scope char[] buf, scope const(Char)[] fmt, Args args) -{ - import core.exception : RangeError; - import std.range.primitives; - import std.utf : encode; - - static struct Sink - { - char[] buf; - size_t i; - void put(char c) - { - if (buf.length <= i) - throw new RangeError(__FILE__, __LINE__); - - buf[i] = c; - i += 1; - } - void put(dchar c) - { - char[4] enc; - auto n = encode(enc, c); - - if (buf.length < i + n) - throw new RangeError(__FILE__, __LINE__); - - buf[i .. i + n] = enc[0 .. n]; - i += n; - } - void put(scope const(char)[] s) - { - if (buf.length < i + s.length) - throw new RangeError(__FILE__, __LINE__); - - buf[i .. i + s.length] = s[]; - i += s.length; - } - void put(scope const(wchar)[] s) - { - for (; !s.empty; s.popFront()) - put(s.front); - } - void put(scope const(dchar)[] s) - { - for (; !s.empty; s.popFront()) - put(s.front); - } - } - auto sink = Sink(buf); - auto n = formattedWrite(sink, fmt, args); - version (all) - { - // In the future, this check will be removed to increase consistency - // with formattedWrite - import std.conv : text; - enforceFmt( - n == args.length, - text("Orphan format arguments: args[", n, " .. ", args.length, "]") - ); - } - return buf[0 .. sink.i]; -} - -/// ditto -char[] sformat(alias fmt, Args...)(char[] buf, Args args) -if (isSomeString!(typeof(fmt))) -{ - alias e = checkFormatException!(fmt, Args); - static assert(!e, e); - return .sformat(buf, fmt, args); -} - -/// -@safe pure unittest -{ - char[20] buf; - assert(sformat(buf[], "Here are %d %s.", 3, "apples") == "Here are 3 apples."); - - assert(buf[].sformat("Increase: %7.2f %%", 17.4285) == "Increase: 17.43 %"); -} - -/// The format string can be checked at compile-time: -@safe pure unittest -{ - char[20] buf; - - assert(sformat!"Here are %d %s."(buf[], 3, "apples") == "Here are 3 apples."); - - // This line doesn't compile, because 3.14 cannot be formatted with %d: - // writeln(sformat!"Here are %d %s."(buf[], 3.14, "apples")); -} - -// checking, what is implicitly and explicitly stated in the public unittest -@safe unittest -{ - import std.exception : assertThrown; - - char[20] buf; - assertThrown!FormatException(sformat(buf[], "Here are %d %s.", 3.14, "apples")); - assert(!__traits(compiles, sformat!"Here are %d %s."(buf[], 3.14, "apples"))); -} - -@safe unittest -{ - import core.exception : RangeError; - import std.exception : assertCTFEable, assertThrown; - - assertCTFEable!( - { - char[10] buf; - - assert(sformat(buf[], "foo") == "foo"); - assert(sformat(buf[], "foo%%") == "foo%"); - assert(sformat(buf[], "foo%s", 'C') == "fooC"); - assert(sformat(buf[], "%s foo", "bar") == "bar foo"); - () @trusted { - assertThrown!RangeError(sformat(buf[], "%s foo %s", "bar", "abc")); - } (); - assert(sformat(buf[], "foo %d", -123) == "foo -123"); - assert(sformat(buf[], "foo %d", 123) == "foo 123"); - - assertThrown!FormatException(sformat(buf[], "foo %s")); - assertThrown!FormatException(sformat(buf[], "foo %s", 123, 456)); - - assert(sformat(buf[], "%s %s %s", "c"c, "w"w, "d"d) == "c w d"); - }); -} - -@safe unittest // ensure that sformat avoids the GC -{ - import core.memory : GC; - - const a = ["foo", "bar"]; - const u = () @trusted { return GC.stats().usedSize; } (); - char[20] buf; - sformat(buf, "%d", 123); - sformat(buf, "%s", a); - sformat(buf, "%s", 'c'); - const v = () @trusted { return GC.stats().usedSize; } (); - assert(u == v); -} - -@safe unittest // https://issues.dlang.org/show_bug.cgi?id=23488 -{ - static struct R - { - string s = "Ü"; - bool empty() { return s.length == 0; } - char front() { return s[0]; } - void popFront() { s = s[1 .. $]; } - } - char[2] buf; - assert(sformat(buf, "%s", R()) == "Ü"); -} - -version (StdUnittest) -private void formatReflectTest(T)(ref T val, string fmt, string formatted, string fn = __FILE__, size_t ln = __LINE__) -{ - formatReflectTest(val, fmt, [formatted], fn, ln); -} - -version (StdUnittest) -private void formatReflectTest(T)(ref T val, string fmt, string[] formatted, string fn = __FILE__, size_t ln = __LINE__) -{ - import core.exception : AssertError; - import std.algorithm.searching : canFind; - import std.array : appender; - import std.math.operations : isClose; - import std.traits : FloatingPointTypeOf; - - auto w = appender!string(); - formattedWrite(w, fmt, val); - - auto input = w.data; - enforce!AssertError(formatted.canFind(input), input, fn, ln); - - T val2; - formattedRead(input, fmt, val2); - - static if (is(FloatingPointTypeOf!T)) - enforce!AssertError(isClose(val, val2), input, fn, ln); - else - enforce!AssertError(val == val2, input, fn, ln); -} - -@safe unittest -{ - void booleanTest() - { - auto b = true; - formatReflectTest(b, "%s", `true`); - formatReflectTest(b, "%b", `1`); - formatReflectTest(b, "%o", `1`); - formatReflectTest(b, "%d", `1`); - formatReflectTest(b, "%u", `1`); - formatReflectTest(b, "%x", `1`); - } - - void integerTest() - { - auto n = 127; - formatReflectTest(n, "%s", `127`); - formatReflectTest(n, "%b", `1111111`); - formatReflectTest(n, "%o", `177`); - formatReflectTest(n, "%d", `127`); - formatReflectTest(n, "%u", `127`); - formatReflectTest(n, "%x", `7f`); - } - - void floatingTest() - { - auto f = 3.14; - formatReflectTest(f, "%s", `3.14`); - formatReflectTest(f, "%e", `3.140000e+00`); - formatReflectTest(f, "%f", `3.140000`); - formatReflectTest(f, "%g", `3.14`); - } - - void charTest() - { - auto c = 'a'; - formatReflectTest(c, "%s", `a`); - formatReflectTest(c, "%c", `a`); - formatReflectTest(c, "%b", `1100001`); - formatReflectTest(c, "%o", `141`); - formatReflectTest(c, "%d", `97`); - formatReflectTest(c, "%u", `97`); - formatReflectTest(c, "%x", `61`); - } - - void strTest() - { - auto s = "hello"; - formatReflectTest(s, "%s", `hello`); - formatReflectTest(s, "%(%c,%)", `h,e,l,l,o`); - formatReflectTest(s, "%(%s,%)", `'h','e','l','l','o'`); - formatReflectTest(s, "[%(<%c>%| $ %)]", `[ $ $ $ $ ]`); - } - - void daTest() - { - auto a = [1,2,3,4]; - formatReflectTest(a, "%s", `[1, 2, 3, 4]`); - formatReflectTest(a, "[%(%s; %)]", `[1; 2; 3; 4]`); - formatReflectTest(a, "[%(<%s>%| $ %)]", `[<1> $ <2> $ <3> $ <4>]`); - } - - void saTest() - { - int[4] sa = [1,2,3,4]; - formatReflectTest(sa, "%s", `[1, 2, 3, 4]`); - formatReflectTest(sa, "[%(%s; %)]", `[1; 2; 3; 4]`); - formatReflectTest(sa, "[%(<%s>%| $ %)]", `[<1> $ <2> $ <3> $ <4>]`); - } - - void aaTest() - { - auto aa = [1:"hello", 2:"world"]; - formatReflectTest(aa, "%s", [`[1:"hello", 2:"world"]`, `[2:"world", 1:"hello"]`]); - formatReflectTest(aa, "[%(%s->%s, %)]", [`[1->"hello", 2->"world"]`, `[2->"world", 1->"hello"]`]); - formatReflectTest(aa, "{%([%s=%(%c%)]%|; %)}", [`{[1=hello]; [2=world]}`, `{[2=world]; [1=hello]}`]); - } - - import std.exception : assertCTFEable; - - assertCTFEable!( - { - booleanTest(); - integerTest(); - floatingTest(); - charTest(); - strTest(); - daTest(); - saTest(); - aaTest(); - }); -} diff --git a/phobos/std/format/read.d b/phobos/std/format/read.d deleted file mode 100644 index da9d0dc..0000000 --- a/phobos/std/format/read.d +++ /dev/null @@ -1,763 +0,0 @@ -// Written in the D programming language. - -/** -This is a submodule of $(MREF std, format). - -It provides two functions for reading formatted input: $(LREF -unformatValue) and $(LREF formattedRead). The former reads a single -value. The latter reads several values at once and matches the -characters found between format specifiers. - -Parameters are ignored, except for the ones consisting of a single -$(B '*'). See $(LREF formattedRead) for more information. - -A space outside of a format specifier has a special meaning: it -matches any sequence of whitespace characters, not just a single -space. - -The following combinations of format characters and types are -available: - -$(BOOKTABLE , -$(TR $(TH) $(TH s) $(TH c) $(TH d, u, b, o, x, X) $(TH e, E, f, g, G) $(TH r) $(TH compound)) -$(TR $(TD `bool`) $(TD yes) $(TD $(MDASH)) $(TD yes) $(TD $(MDASH)) $(TD $(MDASH)) $(TD $(MDASH))) -$(TR $(TD `null`) $(TD yes) $(TD $(MDASH)) $(TD $(MDASH)) $(TD $(MDASH)) $(TD $(MDASH)) $(TD $(MDASH))) -$(TR $(TD $(I integer)) $(TD yes) $(TD $(MDASH)) $(TD yes) $(TD $(MDASH)) $(TD yes) $(TD $(MDASH))) -$(TR $(TD $(I floating point)) $(TD yes) $(TD $(MDASH)) $(TD $(MDASH)) $(TD yes) $(TD yes) $(TD $(MDASH))) -$(TR $(TD $(I character)) $(TD yes) $(TD yes) $(TD yes) $(TD $(MDASH)) $(TD $(MDASH)) $(TD $(MDASH))) -$(TR $(TD $(I string)) $(TD yes) $(TD $(MDASH)) $(TD $(MDASH)) $(TD $(MDASH)) $(TD $(MDASH)) $(TD yes)) -$(TR $(TD $(I array)) $(TD yes) $(TD $(MDASH)) $(TD $(MDASH)) $(TD $(MDASH)) $(TD $(MDASH)) $(TD yes)) -$(TR $(TD $(I associative array)) $(TD yes) $(TD $(MDASH)) $(TD $(MDASH)) $(TD $(MDASH)) $(TD $(MDASH)) $(TD yes)) -) - -Below are highlighted examples on how these combinations are used -with $(LREF unformatValue), however, they apply for $(LREF -formattedRead) also - -Copyright: Copyright The D Language Foundation 2000-2013. - -License: $(HTTP boost.org/LICENSE_1_0.txt, Boost License 1.0). - -Authors: $(HTTP walterbright.com, Walter Bright), $(HTTP erdani.com, -Andrei Alexandrescu), and Kenji Hara - -Source: $(PHOBOSSRC std/format/read.d) - */ -module std.format.read; - -/// Booleans -@safe pure unittest -{ - import std.format.spec : singleSpec; - - auto str = "false"; - auto spec = singleSpec("%s"); - assert(str.unformatValue!bool(spec) == false); - - str = "1"; - spec = singleSpec("%d"); - assert(str.unformatValue!bool(spec) == true); -} - -/// Null values -@safe pure unittest -{ - import std.format.spec : singleSpec; - - auto str = "null"; - auto spec = singleSpec("%s"); - assert(str.unformatValue!(typeof(null))(spec) == null); -} - -/// Integrals -@safe pure unittest -{ - import std.format.spec : singleSpec; - - // signed decimal values - auto str = "123"; - auto spec = singleSpec("%s"); - assert(str.unformatValue!int(spec) == 123); - - // hexadecimal values - str = "ABC"; - spec = singleSpec("%X"); - assert(str.unformatValue!int(spec) == 2748); - - // octal values - str = "11610"; - spec = singleSpec("%o"); - assert(str.unformatValue!int(spec) == 5000); - - // raw read, depends on endianess - str = "\x75\x01"; - spec = singleSpec("%r"); - auto result = str.unformatValue!short(spec); - assert(result == 373 /* little endian */ || result == 29953 /* big endian */ ); -} - -/// Floating point numbers -@safe pure unittest -{ - import std.format.spec : singleSpec; - import std.math.operations : isClose; - - // natural notation - auto str = "123.456"; - auto spec = singleSpec("%s"); - assert(str.unformatValue!double(spec).isClose(123.456)); - - // scientific notation - str = "1e17"; - spec = singleSpec("%e"); - assert(str.unformatValue!double(spec).isClose(1e17)); - - // raw read, depends on endianess - str = "\x40\x00\x00\xBF"; - spec = singleSpec("%r"); - auto result = str.unformatValue!float(spec); - assert(isClose(result, -0.5) /* little endian */ || isClose(result, 2.0) /* big endian */ ); -} - -/// Characters -@safe pure unittest -{ - import std.format.spec : singleSpec; - - // only the first character is read - auto str = "abc"; - auto spec = singleSpec("%s"); - assert(str.unformatValue!char(spec) == 'a'); - - // using a numerical format character treats the read number as unicode code point - str = "65"; - spec = singleSpec("%d"); - assert(str.unformatValue!char(spec) == 'A'); - - str = "41"; - spec = singleSpec("%x"); - assert(str.unformatValue!char(spec) == 'A'); - - str = "10003"; - spec = singleSpec("%d"); - assert(str.unformatValue!dchar(spec) == '✓'); -} - -/// Arrays -@safe pure unittest -{ - import std.format.spec : singleSpec; - - // string value - string str = "aaa"; - auto spec = singleSpec("%s"); - assert(str.unformatValue!(dchar[])(spec) == "aaa"d); - - // fixed size array with characters - str = "aaa"; - spec = singleSpec("%s"); - dchar[3] ret = ['a', 'a', 'a']; - assert(str.unformatValue!(dchar[3])(spec) == ret); - - // dynamic array - str = "[1, 2, 3, 4]"; - spec = singleSpec("%s"); - assert(str.unformatValue!(int[])(spec) == [1, 2, 3, 4]); - - // fixed size array with integers - str = "[1, 2, 3, 4]"; - spec = singleSpec("%s"); - int[4] ret2 = [1, 2, 3, 4]; - assert(str.unformatValue!(int[4])(spec) == ret2); - - // compound specifiers can be used for more control - str = "1,2,3"; - spec = singleSpec("%(%s,%)"); - assert(str.unformatValue!(int[])(spec) == [1, 2, 3]); - - str = "cool"; - spec = singleSpec("%(%c%)"); - assert(str.unformatValue!(char[])(spec) == ['c', 'o', 'o', 'l']); -} - -/// Associative arrays -@safe pure unittest -{ - import std.format.spec : singleSpec; - - // as single value - auto str = `["one": 1, "two": 2]`; - auto spec = singleSpec("%s"); - assert(str.unformatValue!(int[string])(spec) == ["one": 1, "two": 2]); - - // with compound specifier for more control - str = "1/1, 2/4, 3/9"; - spec = singleSpec("%(%d/%d%|, %)"); - assert(str.unformatValue!(int[int])(spec) == [1: 1, 2: 4, 3: 9]); -} - -import std.format.spec : FormatSpec; -import std.format.internal.read; -import std.traits : isSomeString; - -/** -Reads an input range according to a format string and stores the read -values into its arguments. - -Format specifiers with format character $(B 'd'), $(B 'u') and $(B -'c') can take a $(B '*') parameter for skipping values. - -The second version of `formattedRead` takes the format string as -template argument. In this case, it is checked for consistency at -compile-time. - -Params: - r = an $(REF_ALTTEXT input range, isInputRange, std, range, primitives), - where the formatted input is read from - fmt = a $(MREF_ALTTEXT format string, std,format) - args = a variadic list of arguments where the read values are stored - Range = the type of the input range `r` - Char = the character type used for `fmt` - Args = a variadic list of types of the arguments - -Returns: - The number of variables filled. If the input range `r` ends early, - this number will be less than the number of variables provided. - -Throws: - A $(REF_ALTTEXT FormatException, FormatException, std, format) - if reading did not succeed. - -Note: - For backward compatibility the arguments `args` can be given as pointers - to that variable, but it is not recommended to do so, because this - option might be removed in the future. - */ -uint formattedRead(Range, Char, Args...)(auto ref Range r, const(Char)[] fmt, auto ref Args args) -{ - import std.format : enforceFmt; - import std.range.primitives : empty; - import std.traits : isPointer; - import std.typecons : isTuple; - - auto spec = FormatSpec!Char(fmt); - static if (!Args.length) - { - spec.readUpToNextSpec(r); - enforceFmt(spec.trailing.empty, "Trailing characters in formattedRead format string"); - return 0; - } - else - { - enum hasPointer = isPointer!(typeof(args[0])); - - // The function below accounts for '*' == fields meant to be - // read and skipped - void skipUnstoredFields() - { - for (;;) - { - spec.readUpToNextSpec(r); - if (spec.width != spec.DYNAMIC) break; - // must skip this field - skipData(r, spec); - } - } - - skipUnstoredFields(); - if (r.empty) - { - // Input is empty, nothing to read - return 0; - } - - static if (hasPointer) - alias A = typeof(*args[0]); - else - alias A = typeof(args[0]); - - static if (isTuple!A) - { - foreach (i, T; A.Types) - { - static if (hasPointer) - (*args[0])[i] = unformatValue!(T)(r, spec); - else - args[0][i] = unformatValue!(T)(r, spec); - skipUnstoredFields(); - } - } - else - { - static if (hasPointer) - *args[0] = unformatValue!(A)(r, spec); - else - args[0] = unformatValue!(A)(r, spec); - } - return 1 + formattedRead(r, spec.trailing, args[1 .. $]); - } -} - -/// ditto -uint formattedRead(alias fmt, Range, Args...)(auto ref Range r, auto ref Args args) -if (isSomeString!(typeof(fmt))) -{ - import std.format : checkFormatException; - import std.meta : staticMap; - import std.typecons : Tuple; - - - // formattedRead supports std.typecons.Tuple - // however, checkFormatException does not - // this means that all std.typecons.Tuple's types in Args must be unwrapped - // and passed to checkFormatException - template Flatten(T) - { - static if (is(T : Tuple!Args, Args...)) - alias Flatten = Args; - else - alias Flatten = T; - } - - alias e = checkFormatException!(fmt, staticMap!(Flatten, Args)); - static assert(!e, e); - return .formattedRead(r, fmt, args); -} - -/// -@safe pure unittest -{ - string object; - char cmp; - int value; - - assert(formattedRead("angle < 36", "%s %c %d", object, cmp, value) == 3); - assert(object == "angle"); - assert(cmp == '<'); - assert(value == 36); - - // reading may end early: - assert(formattedRead("length >", "%s %c %d", object, cmp, value) == 2); - assert(object == "length"); - assert(cmp == '>'); - // value is not changed: - assert(value == 36); -} - -/// The format string can be checked at compile-time: -@safe pure unittest -{ - string a; - int b; - double c; - - assert("hello!124:34.5".formattedRead!"%s!%s:%s"(a, b, c) == 3); - assert(a == "hello"); - assert(b == 124); - assert(c == 34.5); -} - -/// Skipping values -@safe pure unittest -{ - string item; - double amount; - - assert("orange: (12%) 15.25".formattedRead("%s: (%*d%%) %f", item, amount) == 2); - assert(item == "orange"); - assert(amount == 15.25); - - // can also be used with tuples - import std.typecons : Tuple; - - Tuple!(int, float) t; - char[] line = "1 7643 2.125".dup; - formattedRead(line, "%s %*u %s", t); - assert(t[0] == 1 && t[1] == 2.125); -} - -// https://issues.dlang.org/show_bug.cgi?id=23600 -@safe pure unittest -{ - import std.typecons : Tuple, tuple; - - string h, w; - Tuple!(int, float) t; - - assert("hello 1 2.34 world".formattedRead!"%s %d %f %s"(h, t, w) == 3); - assert(h == "hello"); - assert(t == tuple(1, 2.34f)); - assert(w == "world"); -} - -@safe unittest -{ - import std.math.operations : isClose; - import std.math.traits : isNaN; - import std.range.primitives : empty; - - string s = " 1.2 3.4 "; - double x, y, z; - assert(formattedRead(s, " %s %s %s ", x, y, z) == 2); - assert(s.empty); - assert(isClose(x, 1.2)); - assert(isClose(y, 3.4)); - assert(isNaN(z)); -} - -// for backwards compatibility -@safe pure unittest -{ - string s = "hello!124:34.5"; - string a; - int b; - double c; - formattedRead(s, "%s!%s:%s", &a, &b, &c); - assert(a == "hello" && b == 124 && c == 34.5); - - // mix pointers and auto-ref - s = "world!200:42.25"; - formattedRead(s, "%s!%s:%s", a, &b, &c); - assert(a == "world" && b == 200 && c == 42.25); - - s = "world1!201:42.5"; - formattedRead(s, "%s!%s:%s", &a, &b, c); - assert(a == "world1" && b == 201 && c == 42.5); - - s = "world2!202:42.75"; - formattedRead(s, "%s!%s:%s", a, b, &c); - assert(a == "world2" && b == 202 && c == 42.75); -} - -// for backwards compatibility -@safe pure unittest -{ - import std.math.operations : isClose; - import std.math.traits : isNaN; - import std.range.primitives : empty; - - string s = " 1.2 3.4 "; - double x, y, z; - assert(formattedRead(s, " %s %s %s ", &x, &y, &z) == 2); - assert(s.empty); - assert(isClose(x, 1.2)); - assert(isClose(y, 3.4)); - assert(isNaN(z)); -} - -@safe unittest -{ - string s = "hello!124:34.5"; - string a; - int b; - double c; - formattedRead(s, "%s!%s:%s", &a, &b, &c); - assert(a == "hello" && b == 124 && c == 34.5); -} - -@safe pure unittest -{ - string line; - - bool f1; - - line = "true"; - formattedRead(line, "%s", &f1); - assert(f1); - - line = "TrUE"; - formattedRead(line, "%s", &f1); - assert(f1); - - line = "false"; - formattedRead(line, "%s", &f1); - assert(!f1); - - line = "fALsE"; - formattedRead(line, "%s", &f1); - assert(!f1); - - line = "1"; - formattedRead(line, "%d", &f1); - assert(f1); - - line = "-1"; - formattedRead(line, "%d", &f1); - assert(f1); - - line = "0"; - formattedRead(line, "%d", &f1); - assert(!f1); - - line = "-0"; - formattedRead(line, "%d", &f1); - assert(!f1); -} - -@safe pure unittest -{ - union B - { - char[int.sizeof] untyped; - int typed; - } - - B b; - b.typed = 5; - char[] input = b.untyped[]; - int witness; - formattedRead(input, "%r", &witness); - assert(witness == b.typed); -} - -@safe pure unittest -{ - union A - { - char[float.sizeof] untyped; - float typed; - } - - A a; - a.typed = 5.5; - char[] input = a.untyped[]; - float witness; - formattedRead(input, "%r", &witness); - assert(witness == a.typed); -} - -@safe pure unittest -{ - import std.typecons : Tuple; - - char[] line = "1 2".dup; - int a, b; - formattedRead(line, "%s %s", &a, &b); - assert(a == 1 && b == 2); - - line = "10 2 3".dup; - formattedRead(line, "%d ", &a); - assert(a == 10); - assert(line == "2 3"); - - Tuple!(int, float) t; - line = "1 2.125".dup; - formattedRead(line, "%d %g", &t); - assert(t[0] == 1 && t[1] == 2.125); - - line = "1 7643 2.125".dup; - formattedRead(line, "%s %*u %s", &t); - assert(t[0] == 1 && t[1] == 2.125); -} - -@safe pure unittest -{ - string line; - - char c1, c2; - - line = "abc"; - formattedRead(line, "%s%c", &c1, &c2); - assert(c1 == 'a' && c2 == 'b'); - assert(line == "c"); -} - -@safe pure unittest -{ - string line; - - line = "[1,2,3]"; - int[] s1; - formattedRead(line, "%s", &s1); - assert(s1 == [1,2,3]); -} - -@safe pure unittest -{ - string line; - - line = "[1,2,3]"; - int[] s1; - formattedRead(line, "[%(%s,%)]", &s1); - assert(s1 == [1,2,3]); - - line = `["hello", "world"]`; - string[] s2; - formattedRead(line, "[%(%s, %)]", &s2); - assert(s2 == ["hello", "world"]); - - line = "123 456"; - int[] s3; - formattedRead(line, "%(%s %)", &s3); - assert(s3 == [123, 456]); - - line = "h,e,l,l,o; w,o,r,l,d"; - string[] s4; - formattedRead(line, "%(%(%c,%); %)", &s4); - assert(s4 == ["hello", "world"]); -} - -@safe pure unittest -{ - import std.exception : assertThrown; - - string line; - - int[4] sa1; - line = `[1,2,3,4]`; - formattedRead(line, "%s", &sa1); - assert(sa1 == [1,2,3,4]); - - int[4] sa2; - line = `[1,2,3]`; - assertThrown(formattedRead(line, "%s", &sa2)); - - int[4] sa3; - line = `[1,2,3,4,5]`; - assertThrown(formattedRead(line, "%s", &sa3)); -} - -@safe pure unittest -{ - import std.exception : assertThrown; - import std.format : FormatException; - - string input; - - int[4] sa1; - input = `[1,2,3,4]`; - formattedRead(input, "[%(%s,%)]", &sa1); - assert(sa1 == [1,2,3,4]); - - int[4] sa2; - input = `[1,2,3]`; - assertThrown!FormatException(formattedRead(input, "[%(%s,%)]", &sa2)); -} - -@safe pure unittest -{ - string line; - - string s1, s2; - - line = "hello, world"; - formattedRead(line, "%s", &s1); - assert(s1 == "hello, world", s1); - - line = "hello, world;yah"; - formattedRead(line, "%s;%s", &s1, &s2); - assert(s1 == "hello, world", s1); - assert(s2 == "yah", s2); - - line = `['h','e','l','l','o']`; - string s3; - formattedRead(line, "[%(%s,%)]", &s3); - assert(s3 == "hello"); - - line = `"hello"`; - string s4; - formattedRead(line, "\"%(%c%)\"", &s4); - assert(s4 == "hello"); -} - -@safe pure unittest -{ - string line; - - string[int] aa1; - line = `[1:"hello", 2:"world"]`; - formattedRead(line, "%s", &aa1); - assert(aa1 == [1:"hello", 2:"world"]); - - int[string] aa2; - line = `{"hello"=1; "world"=2}`; - formattedRead(line, "{%(%s=%s; %)}", &aa2); - assert(aa2 == ["hello":1, "world":2]); - - int[string] aa3; - line = `{[hello=1]; [world=2]}`; - formattedRead(line, "{%([%(%c%)=%s]%|; %)}", &aa3); - assert(aa3 == ["hello":1, "world":2]); -} - -// test rvalue using -@safe pure unittest -{ - string[int] aa1; - formattedRead!("%s")(`[1:"hello", 2:"world"]`, aa1); - assert(aa1 == [1:"hello", 2:"world"]); - - int[string] aa2; - formattedRead(`{"hello"=1; "world"=2}`, "{%(%s=%s; %)}", aa2); - assert(aa2 == ["hello":1, "world":2]); -} - -/** -Reads a value from the given _input range and converts it according to a -format specifier. - -Params: - input = the $(REF_ALTTEXT input range, isInputRange, std, range, primitives), - to read from - spec = a $(MREF_ALTTEXT format string, std,format) - T = type to return - Range = the type of the input range `input` - Char = the character type used for `spec` - -Returns: - A value from `input` of type `T`. - -Throws: - A $(REF_ALTTEXT FormatException, FormatException, std, format) - if reading did not succeed. - -See_Also: - $(REF parse, std, conv) and $(REF to, std, conv) - */ -T unformatValue(T, Range, Char)(ref Range input, scope const ref FormatSpec!Char spec) -{ - return unformatValueImpl!T(input, spec); -} - -/// -@safe pure unittest -{ - import std.format.spec : singleSpec; - - string s = "42"; - auto spec = singleSpec("%s"); - assert(unformatValue!int(s, spec) == 42); -} - -// https://issues.dlang.org/show_bug.cgi?id=7241 -@safe pure unittest -{ - string input = "a"; - auto spec = FormatSpec!char("%s"); - spec.readUpToNextSpec(input); - auto result = unformatValue!(dchar[1])(input, spec); - assert(result[0] == 'a'); -} - -// https://issues.dlang.org/show_bug.cgi?id=20393 -@safe pure unittest -{ - import std.exception : assertThrown; - string str = "foo 12a-buzz"; - string a, c; - int b; - assertThrown(formattedRead(str, "%s %d-%s", &a, &b, &c)); -} - -// https://issues.dlang.org/show_bug.cgi?id=18051 -@safe pure unittest -{ - import std.format : format; - - enum Op { lt, gt, eq } - - auto s = format!"%s"(Op.lt); - Op op; - assert(formattedRead!"%s"(s, op) == 1); - assert(op == Op.lt); -} diff --git a/phobos/std/format/spec.d b/phobos/std/format/spec.d deleted file mode 100644 index b129686..0000000 --- a/phobos/std/format/spec.d +++ /dev/null @@ -1,949 +0,0 @@ -// Written in the D programming language. - -/** -This is a submodule of $(MREF std, format). - -It centers around a struct called $(LREF FormatSpec), which takes a -$(MREF_ALTTEXT format string, std,format) and provides tools for -parsing this string. Additionally this module contains a function -$(LREF singleSpec) which helps treating a single format specifier. - -Copyright: Copyright The D Language Foundation 2000-2013. - -License: $(HTTP boost.org/LICENSE_1_0.txt, Boost License 1.0). - -Authors: $(HTTP walterbright.com, Walter Bright), $(HTTP erdani.com, -Andrei Alexandrescu), and Kenji Hara - -Source: $(PHOBOSSRC std/format/spec.d) - */ -module std.format.spec; - -import std.traits : Unqual; - -template FormatSpec(Char) -if (!is(Unqual!Char == Char)) -{ - alias FormatSpec = FormatSpec!(Unqual!Char); -} - -/** -A general handler for format strings. - -This handler centers around the function $(LREF writeUpToNextSpec), -which parses the $(MREF_ALTTEXT format string, std,format) until the -next format specifier is found. After the call, it provides -information about this format specifier in its numerous variables. - -Params: - Char = the character type of the format string - */ -struct FormatSpec(Char) -if (is(Unqual!Char == Char)) -{ - import std.algorithm.searching : startsWith; - import std.ascii : isDigit; - import std.conv : parse, text, to; - import std.range.primitives; - - /** - Minimum width. - - _Default: `0`. - */ - int width = 0; - - /** - Precision. Its semantic depends on the format character. - - See $(MREF_ALTTEXT format string, std,format) for more details. - _Default: `UNSPECIFIED`. - */ - int precision = UNSPECIFIED; - - /** - Number of elements between separators. - - _Default: `UNSPECIFIED`. - */ - int separators = UNSPECIFIED; - - /** - The separator charactar is supplied at runtime. - - _Default: false. - */ - bool dynamicSeparatorChar = false; - - /** - Set to `DYNAMIC` when the separator character is supplied at runtime. - - _Default: `UNSPECIFIED`. - - $(RED Warning: - `separatorCharPos` is deprecated. It will be removed in 2.107.0. - Please use `dynamicSeparatorChar` instead.) - */ - // @@@DEPRECATED_[2.107.0]@@@ - deprecated("separatorCharPos will be removed in 2.107.0. Please use dynamicSeparatorChar instead.") - int separatorCharPos() { return dynamicSeparatorChar ? DYNAMIC : UNSPECIFIED; } - - /// ditto - // @@@DEPRECATED_[2.107.0]@@@ - deprecated("separatorCharPos will be removed in 2.107.0. Please use dynamicSeparatorChar instead.") - void separatorCharPos(int value) { dynamicSeparatorChar = value == DYNAMIC; } - - /** - Character to use as separator. - - _Default: `','`. - */ - dchar separatorChar = ','; - - /** - Special value for `width`, `precision` and `separators`. - - It flags that these values will be passed at runtime through - variadic arguments. - */ - enum int DYNAMIC = int.max; - - /** - Special value for `precision` and `separators`. - - It flags that these values have not been specified. - */ - enum int UNSPECIFIED = DYNAMIC - 1; - - /** - The format character. - - _Default: `'s'`. - */ - char spec = 's'; - - /** - Index of the argument for positional parameters. - - Counting starts with `1`. Set to `0` if not used. Default: `0`. - */ - ubyte indexStart; - - /** - Index of the last argument for positional parameter ranges. - - Counting starts with `1`. Set to `0` if not used. Default: `0`. - */ - ubyte indexEnd; - - version (StdDdoc) - { - /// The format specifier contained a `'-'`. - bool flDash; - - /// The format specifier contained a `'0'`. - bool flZero; - - /// The format specifier contained a space. - bool flSpace; - - /// The format specifier contained a `'+'`. - bool flPlus; - - /// The format specifier contained a `'#'`. - bool flHash; - - /// The format specifier contained a `'='`. - bool flEqual; - - /// The format specifier contained a `','`. - bool flSeparator; - - // Fake field to allow compilation - ubyte allFlags; - } - else - { - union - { - import std.bitmanip : bitfields; - mixin(bitfields!( - bool, "flDash", 1, - bool, "flZero", 1, - bool, "flSpace", 1, - bool, "flPlus", 1, - bool, "flHash", 1, - bool, "flEqual", 1, - bool, "flSeparator", 1, - ubyte, "", 1)); - ubyte allFlags; - } - } - - /// The inner format string of a nested format specifier. - const(Char)[] nested; - - /** - The separator of a nested format specifier. - - `null` means, there is no separator. `empty`, but not `null`, - means zero length separator. - */ - const(Char)[] sep; - - /// Contains the part of the format string, that has not yet been parsed. - const(Char)[] trailing; - - /// Sequence `"["` inserted before each range or range like structure. - enum immutable(Char)[] seqBefore = "["; - - /// Sequence `"]"` inserted after each range or range like structure. - enum immutable(Char)[] seqAfter = "]"; - - /** - Sequence `":"` inserted between element key and element value of - an associative array. - */ - enum immutable(Char)[] keySeparator = ":"; - - /** - Sequence `", "` inserted between elements of a range, a range like - structure or the elements of an associative array. - */ - enum immutable(Char)[] seqSeparator = ", "; - - /** - Creates a new `FormatSpec`. - - The string is lazily evaluated. That means, nothing is done, - until $(LREF writeUpToNextSpec) is called. - - Params: - fmt = a $(MREF_ALTTEXT format string, std,format) - */ - this(in Char[] fmt) @safe pure - { - trailing = fmt; - } - - /** - Writes the format string to an output range until the next format - specifier is found and parse that format specifier. - - See the $(MREF_ALTTEXT description of format strings, std,format) for more - details about the format specifier. - - Params: - writer = an $(REF_ALTTEXT output range, isOutputRange, std, range, primitives), - where the format string is written to - OutputRange = type of the output range - - Returns: - True, if a format specifier is found and false, if the end of the - format string has been reached. - - Throws: - A $(REF_ALTTEXT FormatException, FormatException, std,format) - when parsing the format specifier did not succeed. - */ - bool writeUpToNextSpec(OutputRange)(ref OutputRange writer) scope - { - import std.format : enforceFmt; - - if (trailing.empty) - return false; - for (size_t i = 0; i < trailing.length; ++i) - { - if (trailing[i] != '%') continue; - put(writer, trailing[0 .. i]); - trailing = trailing[i .. $]; - enforceFmt(trailing.length >= 2, `Unterminated format specifier: "%"`); - trailing = trailing[1 .. $]; - - if (trailing[0] != '%') - { - // Spec found. Fill up the spec, and bailout - fillUp(); - return true; - } - // Doubled! Reset and Keep going - i = 0; - } - // no format spec found - put(writer, trailing); - trailing = null; - return false; - } - - private void fillUp() scope - { - import std.format : enforceFmt, FormatException; - - // Reset content - if (__ctfe) - { - flDash = false; - flZero = false; - flSpace = false; - flPlus = false; - flEqual = false; - flHash = false; - flSeparator = false; - } - else - { - allFlags = 0; - } - - width = 0; - precision = UNSPECIFIED; - nested = null; - // Parse the spec (we assume we're past '%' already) - for (size_t i = 0; i < trailing.length; ) - { - switch (trailing[i]) - { - case '(': - // Embedded format specifier. - auto j = i + 1; - // Get the matching balanced paren - for (uint innerParens;;) - { - enforceFmt(j + 1 < trailing.length, - text("Incorrect format specifier: %", trailing[i .. $])); - if (trailing[j++] != '%') - { - // skip, we're waiting for %( and %) - continue; - } - if (trailing[j] == '-') // for %-( - { - ++j; // skip - enforceFmt(j < trailing.length, - text("Incorrect format specifier: %", trailing[i .. $])); - } - if (trailing[j] == ')') - { - if (innerParens-- == 0) break; - } - else if (trailing[j] == '|') - { - if (innerParens == 0) break; - } - else if (trailing[j] == '(') - { - ++innerParens; - } - } - if (trailing[j] == '|') - { - auto k = j; - for (++j;;) - { - if (trailing[j++] != '%') - continue; - if (trailing[j] == '%') - ++j; - else if (trailing[j] == ')') - break; - else - throw new FormatException( - text("Incorrect format specifier: %", - trailing[j .. $])); - } - nested = trailing[i + 1 .. k - 1]; - sep = trailing[k + 1 .. j - 1]; - } - else - { - nested = trailing[i + 1 .. j - 1]; - sep = null; // no separator - } - //this = FormatSpec(innerTrailingSpec); - spec = '('; - // We practically found the format specifier - trailing = trailing[j + 1 .. $]; - return; - case '-': flDash = true; ++i; break; - case '+': flPlus = true; ++i; break; - case '=': flEqual = true; ++i; break; - case '#': flHash = true; ++i; break; - case '0': flZero = true; ++i; break; - case ' ': flSpace = true; ++i; break; - case '*': - if (isDigit(trailing[++i])) - { - // a '*' followed by digits and '$' is a - // positional format - trailing = trailing[1 .. $]; - width = -parse!(typeof(width))(trailing); - i = 0; - enforceFmt(trailing[i++] == '$', - text("$ expected after '*", -width, "' in format string")); - } - else - { - // read result - width = DYNAMIC; - } - break; - case '1': .. case '9': - auto tmp = trailing[i .. $]; - const widthOrArgIndex = parse!uint(tmp); - enforceFmt(tmp.length, - text("Incorrect format specifier %", trailing[i .. $])); - i = trailing.length - tmp.length; - if (tmp.startsWith('$')) - { - // index of the form %n$ - indexEnd = indexStart = to!ubyte(widthOrArgIndex); - ++i; - } - else if (tmp.startsWith(':')) - { - // two indexes of the form %m:n$, or one index of the form %m:$ - indexStart = to!ubyte(widthOrArgIndex); - tmp = tmp[1 .. $]; - if (tmp.startsWith('$')) - { - indexEnd = indexEnd.max; - } - else - { - indexEnd = parse!(typeof(indexEnd))(tmp); - } - i = trailing.length - tmp.length; - enforceFmt(trailing[i++] == '$', - "$ expected"); - } - else - { - // width - width = to!int(widthOrArgIndex); - } - break; - case ',': - // Precision - ++i; - flSeparator = true; - - if (trailing[i] == '*') - { - ++i; - // read result - separators = DYNAMIC; - } - else if (isDigit(trailing[i])) - { - auto tmp = trailing[i .. $]; - separators = parse!int(tmp); - i = trailing.length - tmp.length; - } - else - { - // "," was specified, but nothing after it - separators = 3; - } - - if (trailing[i] == '?') - { - dynamicSeparatorChar = true; - ++i; - } - - break; - case '.': - // Precision - if (trailing[++i] == '*') - { - if (isDigit(trailing[++i])) - { - // a '.*' followed by digits and '$' is a - // positional precision - trailing = trailing[i .. $]; - i = 0; - precision = -parse!int(trailing); - enforceFmt(trailing[i++] == '$', - "$ expected"); - } - else - { - // read result - precision = DYNAMIC; - } - } - else if (trailing[i] == '-') - { - // negative precision, as good as 0 - precision = 0; - auto tmp = trailing[i .. $]; - parse!int(tmp); // skip digits - i = trailing.length - tmp.length; - } - else if (isDigit(trailing[i])) - { - auto tmp = trailing[i .. $]; - precision = parse!int(tmp); - i = trailing.length - tmp.length; - } - else - { - // "." was specified, but nothing after it - precision = 0; - } - break; - default: - // this is the format char - spec = cast(char) trailing[i++]; - trailing = trailing[i .. $]; - return; - } // end switch - } // end for - throw new FormatException(text("Incorrect format specifier: ", trailing)); - } - - //-------------------------------------------------------------------------- - package bool readUpToNextSpec(R)(ref R r) scope - { - import std.ascii : isLower, isWhite; - import std.format : enforceFmt; - import std.utf : stride; - - // Reset content - if (__ctfe) - { - flDash = false; - flZero = false; - flSpace = false; - flPlus = false; - flHash = false; - flEqual = false; - flSeparator = false; - } - else - { - allFlags = 0; - } - width = 0; - precision = UNSPECIFIED; - nested = null; - // Parse the spec - while (trailing.length) - { - const c = trailing[0]; - if (c == '%' && trailing.length > 1) - { - const c2 = trailing[1]; - if (c2 == '%') - { - assert(!r.empty, "Required at least one more input"); - // Require a '%' - enforceFmt (r.front == '%', - text("parseToFormatSpec: Cannot find character '", - c2, "' in the input string.")); - trailing = trailing[2 .. $]; - r.popFront(); - } - else - { - enforceFmt(isLower(c2) || c2 == '*' || c2 == '(', - text("'%", c2, "' not supported with formatted read")); - trailing = trailing[1 .. $]; - fillUp(); - return true; - } - } - else - { - if (c == ' ') - { - while (!r.empty && isWhite(r.front)) r.popFront(); - //r = std.algorithm.find!(not!(isWhite))(r); - } - else - { - enforceFmt(!r.empty && r.front == trailing.front, - text("parseToFormatSpec: Cannot find character '", - c, "' in the input string.")); - r.popFront(); - } - trailing = trailing[stride(trailing, 0) .. $]; - } - } - return false; - } - - package string getCurFmtStr() const - { - import std.array : appender; - import std.format.write : formatValue; - - auto w = appender!string(); - auto f = FormatSpec!Char("%s"); // for stringnize - - put(w, '%'); - if (indexStart != 0) - { - formatValue(w, indexStart, f); - put(w, '$'); - } - if (flDash) put(w, '-'); - if (flZero) put(w, '0'); - if (flSpace) put(w, ' '); - if (flPlus) put(w, '+'); - if (flEqual) put(w, '='); - if (flHash) put(w, '#'); - if (width != 0) - formatValue(w, width, f); - if (precision != FormatSpec!Char.UNSPECIFIED) - { - put(w, '.'); - formatValue(w, precision, f); - } - if (flSeparator) put(w, ','); - if (separators != FormatSpec!Char.UNSPECIFIED) - formatValue(w, separators, f); - put(w, spec); - return w.data; - } - - /** - Provides a string representation. - - Returns: - The string representation. - */ - string toString() const @safe pure - { - import std.array : appender; - - auto app = appender!string(); - app.reserve(200 + trailing.length); - toString(app); - return app.data; - } - - /** - Writes a string representation to an output range. - - Params: - writer = an $(REF_ALTTEXT output range, isOutputRange, std, range, primitives), - where the representation is written to - OutputRange = type of the output range - */ - void toString(OutputRange)(ref OutputRange writer) const - if (isOutputRange!(OutputRange, char)) - { - import std.format.write : formatValue; - - auto s = singleSpec("%s"); - - put(writer, "address = "); - formatValue(writer, &this, s); - put(writer, "\nwidth = "); - formatValue(writer, width, s); - put(writer, "\nprecision = "); - formatValue(writer, precision, s); - put(writer, "\nspec = "); - formatValue(writer, spec, s); - put(writer, "\nindexStart = "); - formatValue(writer, indexStart, s); - put(writer, "\nindexEnd = "); - formatValue(writer, indexEnd, s); - put(writer, "\nflDash = "); - formatValue(writer, flDash, s); - put(writer, "\nflZero = "); - formatValue(writer, flZero, s); - put(writer, "\nflSpace = "); - formatValue(writer, flSpace, s); - put(writer, "\nflPlus = "); - formatValue(writer, flPlus, s); - put(writer, "\nflEqual = "); - formatValue(writer, flEqual, s); - put(writer, "\nflHash = "); - formatValue(writer, flHash, s); - put(writer, "\nflSeparator = "); - formatValue(writer, flSeparator, s); - put(writer, "\nnested = "); - formatValue(writer, nested, s); - put(writer, "\ntrailing = "); - formatValue(writer, trailing, s); - put(writer, '\n'); - } -} - -/// -@safe pure unittest -{ - import std.array : appender; - - auto a = appender!(string)(); - auto fmt = "Number: %6.4e\nString: %s"; - auto f = FormatSpec!char(fmt); - - assert(f.writeUpToNextSpec(a) == true); - - assert(a.data == "Number: "); - assert(f.trailing == "\nString: %s"); - assert(f.spec == 'e'); - assert(f.width == 6); - assert(f.precision == 4); - - assert(f.writeUpToNextSpec(a) == true); - - assert(a.data == "Number: \nString: "); - assert(f.trailing == ""); - assert(f.spec == 's'); - - assert(f.writeUpToNextSpec(a) == false); - - assert(a.data == "Number: \nString: "); -} - -@safe unittest -{ - import std.array : appender; - import std.conv : text; - import std.exception : assertThrown; - import std.format : FormatException; - - auto w = appender!(char[])(); - auto f = FormatSpec!char("abc%sdef%sghi"); - f.writeUpToNextSpec(w); - assert(w.data == "abc", w.data); - assert(f.trailing == "def%sghi", text(f.trailing)); - f.writeUpToNextSpec(w); - assert(w.data == "abcdef", w.data); - assert(f.trailing == "ghi"); - // test with embedded %%s - f = FormatSpec!char("ab%%cd%%ef%sg%%h%sij"); - w.clear(); - f.writeUpToNextSpec(w); - assert(w.data == "ab%cd%ef" && f.trailing == "g%%h%sij", w.data); - f.writeUpToNextSpec(w); - assert(w.data == "ab%cd%efg%h" && f.trailing == "ij"); - // https://issues.dlang.org/show_bug.cgi?id=4775 - f = FormatSpec!char("%%%s"); - w.clear(); - f.writeUpToNextSpec(w); - assert(w.data == "%" && f.trailing == ""); - f = FormatSpec!char("%%%%%s%%"); - w.clear(); - while (f.writeUpToNextSpec(w)) continue; - assert(w.data == "%%%"); - - f = FormatSpec!char("a%%b%%c%"); - w.clear(); - assertThrown!FormatException(f.writeUpToNextSpec(w)); - assert(w.data == "a%b%c" && f.trailing == "%"); -} - -// https://issues.dlang.org/show_bug.cgi?id=5237 -@safe unittest -{ - import std.array : appender; - - auto w = appender!string(); - auto f = FormatSpec!char("%.16f"); - f.writeUpToNextSpec(w); // dummy eating - assert(f.spec == 'f'); - auto fmt = f.getCurFmtStr(); - assert(fmt == "%.16f"); -} - -// https://issues.dlang.org/show_bug.cgi?id=14059 -@safe unittest -{ - import std.array : appender; - import std.exception : assertThrown; - import std.format : FormatException; - - auto a = appender!(string)(); - - auto f = FormatSpec!char("%-(%s%"); // %)") - assertThrown!FormatException(f.writeUpToNextSpec(a)); - - f = FormatSpec!char("%(%-"); // %)") - assertThrown!FormatException(f.writeUpToNextSpec(a)); -} - -@safe unittest -{ - import std.array : appender; - import std.format : format; - - auto a = appender!(string)(); - - auto f = FormatSpec!char("%,d"); - f.writeUpToNextSpec(a); - - assert(f.spec == 'd', format("%s", f.spec)); - assert(f.precision == FormatSpec!char.UNSPECIFIED); - assert(f.separators == 3); - - f = FormatSpec!char("%5,10f"); - f.writeUpToNextSpec(a); - assert(f.spec == 'f', format("%s", f.spec)); - assert(f.separators == 10); - assert(f.width == 5); - - f = FormatSpec!char("%5,10.4f"); - f.writeUpToNextSpec(a); - assert(f.spec == 'f', format("%s", f.spec)); - assert(f.separators == 10); - assert(f.width == 5); - assert(f.precision == 4); -} - -@safe pure unittest -{ - import std.algorithm.searching : canFind, findSplitBefore; - - auto expected = "width = 2" ~ - "\nprecision = 5" ~ - "\nspec = f" ~ - "\nindexStart = 0" ~ - "\nindexEnd = 0" ~ - "\nflDash = false" ~ - "\nflZero = false" ~ - "\nflSpace = false" ~ - "\nflPlus = false" ~ - "\nflEqual = false" ~ - "\nflHash = false" ~ - "\nflSeparator = false" ~ - "\nnested = " ~ - "\ntrailing = \n"; - auto spec = singleSpec("%2.5f"); - auto res = spec.toString(); - // make sure the address exists, then skip it - assert(res.canFind("address")); - assert(res.findSplitBefore("width")[1] == expected); -} - -// https://issues.dlang.org/show_bug.cgi?id=15348 -@safe pure unittest -{ - import std.array : appender; - import std.exception : collectExceptionMsg; - import std.format : FormatException; - - auto w = appender!(char[])(); - auto f = FormatSpec!char("%*10d"); - - assert(collectExceptionMsg!FormatException(f.writeUpToNextSpec(w)) - == "$ expected after '*10' in format string"); -} - -/** -Helper function that returns a `FormatSpec` for a single format specifier. - -Params: - fmt = a $(MREF_ALTTEXT format string, std,format) - containing a single format specifier - Char = character type of `fmt` - -Returns: - A $(LREF FormatSpec) with the format specifier parsed. - -Throws: - A $(REF_ALTTEXT FormatException, FormatException, std,format) when the - format string contains no format specifier or more than a single format - specifier or when the format specifier is malformed. - */ -FormatSpec!Char singleSpec(Char)(Char[] fmt) -{ - import std.conv : text; - import std.format : enforceFmt; - import std.range.primitives : empty, front; - - enforceFmt(fmt.length >= 2, "fmt must be at least 2 characters long"); - enforceFmt(fmt.front == '%', "fmt must start with a '%' character"); - enforceFmt(fmt[1] != '%', "'%%' is not a permissible format specifier"); - - static struct DummyOutputRange - { - void put(C)(scope const C[] buf) {} // eat elements - } - auto a = DummyOutputRange(); - auto spec = FormatSpec!Char(fmt); - //dummy write - spec.writeUpToNextSpec(a); - - enforceFmt(spec.trailing.empty, - text("Trailing characters in fmt string: '", spec.trailing)); - - return spec; -} - -/// -@safe pure unittest -{ - import std.array : appender; - import std.format.write : formatValue; - - auto spec = singleSpec("%10.3e"); - auto writer = appender!string(); - writer.formatValue(42.0, spec); - - assert(writer.data == " 4.200e+01"); -} - -@safe pure unittest -{ - import std.exception : assertThrown; - import std.format : FormatException; - - auto spec = singleSpec("%2.3e"); - - assert(spec.trailing == ""); - assert(spec.spec == 'e'); - assert(spec.width == 2); - assert(spec.precision == 3); - - assertThrown!FormatException(singleSpec("")); - assertThrown!FormatException(singleSpec("%")); - assertThrown!FormatException(singleSpec("%2.3")); - assertThrown!FormatException(singleSpec("2.3e")); - assertThrown!FormatException(singleSpec("Test%2.3e")); - assertThrown!FormatException(singleSpec("%2.3eTest")); - assertThrown!FormatException(singleSpec("%%")); -} - -// @@@DEPRECATED_[2.107.0]@@@ -deprecated("enforceValidFormatSpec was accidentally made public and will be removed in 2.107.0") -void enforceValidFormatSpec(T, Char)(scope const ref FormatSpec!Char f) -{ - import std.format.internal.write : evfs = enforceValidFormatSpec; - - evfs!T(f); -} - -@safe unittest -{ - import std.exception : collectExceptionMsg; - import std.format : format, FormatException; - - // width/precision - assert(collectExceptionMsg!FormatException(format("%*.d", 5.1, 2)) - == "integer width expected, not double for argument #1"); - assert(collectExceptionMsg!FormatException(format("%-1*.d", 5.1, 2)) - == "integer width expected, not double for argument #1"); - - assert(collectExceptionMsg!FormatException(format("%.*d", '5', 2)) - == "integer precision expected, not char for argument #1"); - assert(collectExceptionMsg!FormatException(format("%-1.*d", 4.7, 3)) - == "integer precision expected, not double for argument #1"); - assert(collectExceptionMsg!FormatException(format("%.*d", 5)) - == "Orphan format specifier: %d"); - assert(collectExceptionMsg!FormatException(format("%*.*d", 5)) - == "Missing integer precision argument"); - - // dynamicSeparatorChar - assert(collectExceptionMsg!FormatException(format("%,?d", 5)) - == "separator character expected, not int for argument #1"); - assert(collectExceptionMsg!FormatException(format("%,?d", '?')) - == "Orphan format specifier: %d"); - assert(collectExceptionMsg!FormatException(format("%.*,*?d", 5)) - == "Missing separator digit width argument"); -} - diff --git a/phobos/std/format/write.d b/phobos/std/format/write.d deleted file mode 100644 index 2aa45d7..0000000 --- a/phobos/std/format/write.d +++ /dev/null @@ -1,1332 +0,0 @@ -// Written in the D programming language. - -/** -This is a submodule of $(MREF std, format). - -It provides two functions for writing formatted output: $(LREF -formatValue) and $(LREF formattedWrite). The former writes a single -value. The latter writes several values at once, interspersed with -unformatted text. - -The following combinations of format characters and types are -available: - -$(BOOKTABLE , -$(TR $(TH) $(TH s) $(TH c) $(TH d, u, b, o) $(TH x, X) $(TH e, E, f, F, g, G, a, A) $(TH r) $(TH compound)) -$(TR $(TD `bool`) $(TD yes) $(TD $(MDASH)) $(TD yes) $(TD yes) $(TD $(MDASH)) $(TD yes) $(TD $(MDASH))) -$(TR $(TD `null`) $(TD yes) $(TD $(MDASH)) $(TD $(MDASH)) $(TD $(MDASH)) $(TD $(MDASH)) $(TD $(MDASH)) $(TD $(MDASH))) -$(TR $(TD $(I integer)) $(TD yes) $(TD $(MDASH)) $(TD yes) $(TD yes) $(TD yes) $(TD yes) $(TD $(MDASH))) -$(TR $(TD $(I floating point)) $(TD yes) $(TD $(MDASH)) $(TD $(MDASH)) $(TD $(MDASH)) $(TD yes) $(TD yes) $(TD $(MDASH))) -$(TR $(TD $(I character)) $(TD yes) $(TD yes) $(TD yes) $(TD yes) $(TD $(MDASH)) $(TD yes) $(TD $(MDASH))) -$(TR $(TD $(I string)) $(TD yes) $(TD $(MDASH)) $(TD $(MDASH)) $(TD $(MDASH)) $(TD $(MDASH)) $(TD yes) $(TD yes)) -$(TR $(TD $(I array)) $(TD yes) $(TD $(MDASH)) $(TD $(MDASH)) $(TD $(MDASH)) $(TD $(MDASH)) $(TD yes) $(TD yes)) -$(TR $(TD $(I associative array)) $(TD yes) $(TD $(MDASH)) $(TD $(MDASH)) $(TD $(MDASH)) $(TD $(MDASH)) $(TD $(MDASH)) $(TD yes)) -$(TR $(TD $(I pointer)) $(TD yes) $(TD $(MDASH)) $(TD $(MDASH)) $(TD yes) $(TD $(MDASH)) $(TD $(MDASH)) $(TD $(MDASH))) -$(TR $(TD $(I SIMD vectors)) $(TD yes) $(TD $(MDASH)) $(TD $(MDASH)) $(TD $(MDASH)) $(TD $(MDASH)) $(TD yes) $(TD yes)) -$(TR $(TD $(I delegates)) $(TD yes) $(TD $(MDASH)) $(TD $(MDASH)) $(TD $(MDASH)) $(TD $(MDASH)) $(TD yes) $(TD yes)) -) - -Enums can be used with all format characters of the base type. - -$(SECTION3 Structs$(COMMA) Unions$(COMMA) Classes$(COMMA) and Interfaces) - -Aggregate types can define various `toString` functions. If this -function takes a $(REF_ALTTEXT FormatSpec, FormatSpec, std, format, -spec) or a $(I format string) as argument, the function decides -which format characters are accepted. If no `toString` is defined and -the aggregate is an $(REF_ALTTEXT input range, isInputRange, std, -range, primitives), it is treated like a range, that is $(B 's'), $(B -'r') and a compound specifier are accepted. In all other cases -aggregate types only accept $(B 's'). - -`toString` should have one of the following signatures: - ---- -void toString(Writer, Char)(ref Writer w, const ref FormatSpec!Char fmt) -void toString(Writer)(ref Writer w) -string toString(); ---- - -Where `Writer` is an $(REF_ALTTEXT output range, isOutputRange, -std,range,primitives) which accepts characters $(LPAREN)of type -`Char` in the first version$(RPAREN). The template type does not have -to be called `Writer`. - -Sometimes it's not possible to use a template, for example when -`toString` overrides `Object.toString`. In this case, the following -$(LPAREN)slower and less flexible$(RPAREN) functions can be used: - ---- -void toString(void delegate(const(char)[]) sink, const ref FormatSpec!char fmt); -void toString(void delegate(const(char)[]) sink, string fmt); -void toString(void delegate(const(char)[]) sink); ---- - -When several of the above `toString` versions are available, the -versions with `Writer` take precedence over the versions with a -`sink`. `string toString()` has the lowest priority. - -If none of the above mentioned `toString` versions are available, the -aggregates will be formatted by other means, in the following -order: - -If an aggregate is an $(REF_ALTTEXT input range, isInputRange, std, -range, primitives), it is formatted like an input range. - -If an aggregate is a builtin type (using `alias this`), it is formatted -like the builtin type. - -If all else fails, structs are formatted like `Type(field1, field2, ...)`, -classes and interfaces are formatted with their fully qualified name -and unions with their base name. - -Copyright: Copyright The D Language Foundation 2000-2013. - -License: $(HTTP boost.org/LICENSE_1_0.txt, Boost License 1.0). - -Authors: $(HTTP walterbright.com, Walter Bright), $(HTTP erdani.com, -Andrei Alexandrescu), and Kenji Hara - -Source: $(PHOBOSSRC std/format/write.d) - */ -module std.format.write; - -/** -`bool`s are formatted as `"true"` or `"false"` with `%s` and like the -`byte`s 1 and 0 with all other format characters. - */ -@safe pure unittest -{ - import std.array : appender; - import std.format.spec : singleSpec; - - auto w1 = appender!string(); - auto spec1 = singleSpec("%s"); - formatValue(w1, true, spec1); - - assert(w1.data == "true"); - - auto w2 = appender!string(); - auto spec2 = singleSpec("%#x"); - formatValue(w2, true, spec2); - - assert(w2.data == "0x1"); -} - -/// The `null` literal is formatted as `"null"`. -@safe pure unittest -{ - import std.array : appender; - import std.format.spec : singleSpec; - - auto w = appender!string(); - auto spec = singleSpec("%s"); - formatValue(w, null, spec); - - assert(w.data == "null"); -} - -/** -Integrals are formatted in (signed) every day notation with `%s` and -`%d` and as an (unsigned) image of the underlying bit representation -with `%b` (binary), `%u` (decimal), `%o` (octal), and `%x` (hexadecimal). - */ -@safe pure unittest -{ - import std.array : appender; - import std.format.spec : singleSpec; - - auto w1 = appender!string(); - auto spec1 = singleSpec("%d"); - formatValue(w1, -1337, spec1); - - assert(w1.data == "-1337"); - - auto w2 = appender!string(); - auto spec2 = singleSpec("%x"); - formatValue(w2, -1337, spec2); - - assert(w2.data == "fffffac7"); -} - -/** -Floating-point values are formatted in natural notation with `%f`, in -scientific notation with `%e`, in short notation with `%g`, and in -hexadecimal scientific notation with `%a`. If a rounding mode is -available, they are rounded according to this rounding mode, otherwise -they are rounded to the nearest value, ties to even. - */ -@safe unittest -{ - import std.array : appender; - import std.format.spec : singleSpec; - - auto w1 = appender!string(); - auto spec1 = singleSpec("%.3f"); - formatValue(w1, 1337.7779, spec1); - - assert(w1.data == "1337.778"); - - auto w2 = appender!string(); - auto spec2 = singleSpec("%.3e"); - formatValue(w2, 1337.7779, spec2); - - assert(w2.data == "1.338e+03"); - - auto w3 = appender!string(); - auto spec3 = singleSpec("%.3g"); - formatValue(w3, 1337.7779, spec3); - - assert(w3.data == "1.34e+03"); - - auto w4 = appender!string(); - auto spec4 = singleSpec("%.3a"); - formatValue(w4, 1337.7779, spec4); - - assert(w4.data == "0x1.4e7p+10"); -} - -/** -Individual characters (`char`, `wchar`, or `dchar`) are formatted as -Unicode characters with `%s` and `%c` and as integers (`ubyte`, -`ushort`, `uint`) with all other format characters. With -$(MREF_ALTTEXT compound specifiers, std,format) characters are -treated differently. - */ -@safe pure unittest -{ - import std.array : appender; - import std.format.spec : singleSpec; - - auto w1 = appender!string(); - auto spec1 = singleSpec("%c"); - formatValue(w1, 'ĂŹ', spec1); - - assert(w1.data == "ĂŹ"); - - auto w2 = appender!string(); - auto spec2 = singleSpec("%#x"); - formatValue(w2, 'ĂŹ', spec2); - - assert(w2.data == "0xec"); -} - -/** -Strings are formatted as a sequence of characters with `%s`. -Non-printable characters are not escaped. With a compound specifier -the string is treated like a range of characters. With $(MREF_ALTTEXT -compound specifiers, std,format) strings are treated differently. - */ -@safe pure unittest -{ - import std.array : appender; - import std.format.spec : singleSpec; - - auto w1 = appender!string(); - auto spec1 = singleSpec("%s"); - formatValue(w1, "hello", spec1); - - assert(w1.data == "hello"); - - auto w2 = appender!string(); - auto spec2 = singleSpec("%(%#x%|/%)"); - formatValue(w2, "hello", spec2); - - assert(w2.data == "0x68/0x65/0x6c/0x6c/0x6f"); -} - -/// Static arrays are formatted as dynamic arrays. -@safe pure unittest -{ - import std.array : appender; - import std.format.spec : singleSpec; - - auto w = appender!string(); - auto spec = singleSpec("%s"); - int[2] two = [1, 2]; - formatValue(w, two, spec); - - assert(w.data == "[1, 2]"); -} - -/** -Dynamic arrays are formatted as input ranges. - */ -@safe pure unittest -{ - import std.array : appender; - import std.format.spec : singleSpec; - - auto w1 = appender!string(); - auto spec1 = singleSpec("%s"); - auto two = [1, 2]; - formatValue(w1, two, spec1); - - assert(w1.data == "[1, 2]"); - - auto w2 = appender!string(); - auto spec2 = singleSpec("%(%g%|, %)"); - auto consts = [3.1415926, 299792458, 6.67430e-11]; - formatValue(w2, consts, spec2); - - assert(w2.data == "3.14159, 2.99792e+08, 6.6743e-11"); - - // void[] is treated like ubyte[] - auto w3 = appender!string(); - auto spec3 = singleSpec("%s"); - void[] val = cast(void[]) cast(ubyte[])[1, 2, 3]; - formatValue(w3, val, spec3); - - assert(w3.data == "[1, 2, 3]"); -} - -/** -Associative arrays are formatted by using `':'` and `", "` as -separators, enclosed by `'['` and `']'` when used with `%s`. It's -also possible to use a compound specifier for better control. - -Please note, that the order of the elements is not defined, therefore -the result of this function might differ. - */ -@safe pure unittest -{ - import std.array : appender; - import std.format.spec : singleSpec; - - auto aa = [10:17.5, 20:9.99]; - - auto w1 = appender!string(); - auto spec1 = singleSpec("%s"); - formatValue(w1, aa, spec1); - - assert(w1.data == "[10:17.5, 20:9.99]" || w1.data == "[20:9.99, 10:17.5]"); - - auto w2 = appender!string(); - auto spec2 = singleSpec("%(%x = %.0e%| # %)"); - formatValue(w2, aa, spec2); - - assert(w2.data == "a = 2e+01 # 14 = 1e+01" || w2.data == "14 = 1e+01 # a = 2e+01"); -} - -/** -`enum`s are formatted as their name when used with `%s` and like -their base value else. - */ -@safe pure unittest -{ - import std.array : appender; - import std.format.spec : singleSpec; - - enum A { first, second, third } - - auto w1 = appender!string(); - auto spec1 = singleSpec("%s"); - formatValue(w1, A.second, spec1); - - assert(w1.data == "second"); - - auto w2 = appender!string(); - auto spec2 = singleSpec("%d"); - formatValue(w2, A.second, spec2); - - assert(w2.data == "1"); - - // values of an enum that have no name are formatted with %s using a cast - A a = A.third; - a++; - - auto w3 = appender!string(); - auto spec3 = singleSpec("%s"); - formatValue(w3, a, spec3); - - assert(w3.data == "cast(A)3"); -} - -/** -`structs`, `unions`, `classes` and `interfaces` can be formatted in -several different ways. The following example highlights `struct` -formatting, however, it applies to other aggregates as well. - */ -@safe unittest -{ - import std.array : appender; - import std.format.spec : FormatSpec, singleSpec; - - // Using a `toString` with a writer - static struct Point1 - { - import std.range.primitives : isOutputRange, put; - - int x, y; - - void toString(W)(ref W writer, scope const ref FormatSpec!char f) - if (isOutputRange!(W, char)) - { - put(writer, "("); - formatValue(writer, x, f); - put(writer, ","); - formatValue(writer, y, f); - put(writer, ")"); - } - } - - auto w1 = appender!string(); - auto spec1 = singleSpec("%s"); - auto p1 = Point1(16, 11); - - formatValue(w1, p1, spec1); - assert(w1.data == "(16,11)"); - - // Using a `toString` with a sink - static struct Point2 - { - int x, y; - - void toString(scope void delegate(scope const(char)[]) @safe sink, - scope const FormatSpec!char fmt) const - { - sink("("); - sink.formatValue(x, fmt); - sink(","); - sink.formatValue(y, fmt); - sink(")"); - } - } - - auto w2 = appender!string(); - auto spec2 = singleSpec("%03d"); - auto p2 = Point2(16,11); - - formatValue(w2, p2, spec2); - assert(w2.data == "(016,011)"); - - // Using `string toString()` - static struct Point3 - { - int x, y; - - string toString() - { - import std.conv : to; - - return "(" ~ to!string(x) ~ "," ~ to!string(y) ~ ")"; - } - } - - auto w3 = appender!string(); - auto spec3 = singleSpec("%s"); // has to be %s - auto p3 = Point3(16,11); - - formatValue(w3, p3, spec3); - assert(w3.data == "(16,11)"); - - // without `toString` - static struct Point4 - { - int x, y; - } - - auto w4 = appender!string(); - auto spec4 = singleSpec("%s"); // has to be %s - auto p4 = Point4(16,11); - - formatValue(w4, p4, spec3); - assert(w4.data == "Point4(16, 11)"); -} - -/// Pointers are formatted as hexadecimal integers. -@safe pure unittest -{ - import std.array : appender; - import std.format.spec : singleSpec; - - auto w1 = appender!string(); - auto spec1 = singleSpec("%s"); - auto p1 = () @trusted { return cast(void*) 0xFFEECCAA; } (); - formatValue(w1, p1, spec1); - - assert(w1.data == "FFEECCAA"); - - // null pointers are printed as `"null"` when used with `%s` and as hexadecimal integer else - auto w2 = appender!string(); - auto spec2 = singleSpec("%s"); - auto p2 = () @trusted { return cast(void*) 0x00000000; } (); - formatValue(w2, p2, spec2); - - assert(w2.data == "null"); - - auto w3 = appender!string(); - auto spec3 = singleSpec("%x"); - formatValue(w3, p2, spec3); - - assert(w3.data == "0"); -} - -/// SIMD vectors are formatted as arrays. -@safe unittest -{ - import core.simd; // cannot be selective, because float4 might not be defined - import std.array : appender; - import std.format.spec : singleSpec; - - auto w = appender!string(); - auto spec = singleSpec("%s"); - - static if (is(float4)) - { - version (X86) {} - else - { - float4 f4; - f4.array[0] = 1; - f4.array[1] = 2; - f4.array[2] = 3; - f4.array[3] = 4; - - formatValue(w, f4, spec); - assert(w.data == "[1, 2, 3, 4]"); - } - } -} - -import std.format.internal.write; - -import std.format.spec : FormatSpec; -import std.traits : isSomeString; - -/** -Converts its arguments according to a format string and writes -the result to an output range. - -The second version of `formattedWrite` takes the format string as a -template argument. In this case, it is checked for consistency at -compile-time. - -Params: - w = an $(REF_ALTTEXT output range, isOutputRange, std, range, primitives), - where the formatted result is written to - fmt = a $(MREF_ALTTEXT format string, std,format) - args = a variadic list of arguments to be formatted - Writer = the type of the writer `w` - Char = character type of `fmt` - Args = a variadic list of types of the arguments - -Returns: - The index of the last argument that was formatted. If no positional - arguments are used, this is the number of arguments that where formatted. - -Throws: - A $(REF_ALTTEXT FormatException, FormatException, std, format) - if formatting did not succeed. - -Note: - In theory this function should be `@nogc`. But with the current - implementation there are some cases where allocations occur. - See $(REF_ALTTEXT $(D sformat), sformat, std, format) for more details. - */ -uint formattedWrite(Writer, Char, Args...)(auto ref Writer w, const scope Char[] fmt, Args args) -{ - import std.conv : text; - import std.format : enforceFmt, FormatException; - import std.traits : isSomeChar; - - auto spec = FormatSpec!Char(fmt); - - // Are we already done with formats? Then just dump each parameter in turn - uint currentArg = 0; - while (spec.writeUpToNextSpec(w)) - { - if (currentArg == Args.length && !spec.indexStart) - { - // leftover spec? - enforceFmt(fmt.length == 0, - text("Orphan format specifier: %", spec.spec)); - break; - } - - if (spec.width == spec.DYNAMIC) - { - auto width = getNthInt!"integer width"(currentArg, args); - if (width < 0) - { - spec.flDash = true; - width = -width; - } - spec.width = width; - ++currentArg; - } - else if (spec.width < 0) - { - // means: get width as a positional parameter - auto index = cast(uint) -spec.width; - assert(index > 0, "The index must be greater than zero"); - auto width = getNthInt!"integer width"(index - 1, args); - if (currentArg < index) currentArg = index; - if (width < 0) - { - spec.flDash = true; - width = -width; - } - spec.width = width; - } - - if (spec.precision == spec.DYNAMIC) - { - auto precision = getNthInt!"integer precision"(currentArg, args); - if (precision >= 0) spec.precision = precision; - // else negative precision is same as no precision - else spec.precision = spec.UNSPECIFIED; - ++currentArg; - } - else if (spec.precision < 0) - { - // means: get precision as a positional parameter - auto index = cast(uint) -spec.precision; - assert(index > 0, "The precision must be greater than zero"); - auto precision = getNthInt!"integer precision"(index- 1, args); - if (currentArg < index) currentArg = index; - if (precision >= 0) spec.precision = precision; - // else negative precision is same as no precision - else spec.precision = spec.UNSPECIFIED; - } - - if (spec.separators == spec.DYNAMIC) - { - auto separators = getNthInt!"separator digit width"(currentArg, args); - spec.separators = separators; - ++currentArg; - } - - if (spec.dynamicSeparatorChar) - { - auto separatorChar = - getNth!("separator character", isSomeChar, dchar)(currentArg, args); - spec.separatorChar = separatorChar; - spec.dynamicSeparatorChar = false; - ++currentArg; - } - - if (currentArg == Args.length && !spec.indexStart) - { - // leftover spec? - enforceFmt(fmt.length == 0, - text("Orphan format specifier: %", spec.spec)); - break; - } - - // Format an argument - // This switch uses a static foreach to generate a jump table. - // Currently `spec.indexStart` use the special value '0' to signal - // we should use the current argument. An enhancement would be to - // always store the index. - size_t index = currentArg; - if (spec.indexStart != 0) - index = spec.indexStart - 1; - else - ++currentArg; - SWITCH: switch (index) - { - foreach (i, Tunused; Args) - { - case i: - formatValue(w, args[i], spec); - if (currentArg < spec.indexEnd) - currentArg = spec.indexEnd; - // A little know feature of format is to format a range - // of arguments, e.g. `%1:3$` will format the first 3 - // arguments. Since they have to be consecutive we can - // just use explicit fallthrough to cover that case. - if (i + 1 < spec.indexEnd) - { - // You cannot goto case if the next case is the default - static if (i + 1 < Args.length) - goto case; - else - goto default; - } - else - break SWITCH; - } - default: - throw new FormatException( - text("Positional specifier %", spec.indexStart, '$', spec.spec, - " index exceeds ", Args.length)); - } - } - return currentArg; -} - -/// -@safe pure unittest -{ - import std.array : appender; - - auto writer1 = appender!string(); - formattedWrite(writer1, "%s is the ultimate %s.", 42, "answer"); - assert(writer1[] == "42 is the ultimate answer."); - - auto writer2 = appender!string(); - formattedWrite(writer2, "Increase: %7.2f %%", 17.4285); - assert(writer2[] == "Increase: 17.43 %"); -} - -/// ditto -uint formattedWrite(alias fmt, Writer, Args...)(auto ref Writer w, Args args) -if (isSomeString!(typeof(fmt))) -{ - import std.format : checkFormatException; - - alias e = checkFormatException!(fmt, Args); - static assert(!e, e); - return .formattedWrite(w, fmt, args); -} - -/// The format string can be checked at compile-time: -@safe pure unittest -{ - import std.array : appender; - - auto writer = appender!string(); - writer.formattedWrite!"%d is the ultimate %s."(42, "answer"); - assert(writer[] == "42 is the ultimate answer."); - - // This line doesn't compile, because 3.14 cannot be formatted with %d: - // writer.formattedWrite!"%d is the ultimate %s."(3.14, "answer"); -} - -@safe pure unittest -{ - import std.array : appender; - - auto stream = appender!string(); - formattedWrite(stream, "%s", 1.1); - assert(stream.data == "1.1", stream.data); -} - -@safe pure unittest -{ - import std.array; - - auto w = appender!string(); - formattedWrite(w, "%s %d", "@safe/pure", 42); - assert(w.data == "@safe/pure 42"); -} - -@safe pure unittest -{ - char[20] buf; - auto w = buf[]; - formattedWrite(w, "%s %d", "@safe/pure", 42); - assert(buf[0 .. $ - w.length] == "@safe/pure 42"); -} - -@safe pure unittest -{ - import std.algorithm.iteration : map; - import std.array : appender; - - auto stream = appender!string(); - formattedWrite(stream, "%s", map!"a*a"([2, 3, 5])); - assert(stream.data == "[4, 9, 25]", stream.data); - - // Test shared data. - stream = appender!string(); - shared int s = 6; - formattedWrite(stream, "%s", s); - assert(stream.data == "6"); -} - -@safe pure unittest -{ - // testing positional parameters - import std.array : appender; - import std.exception : collectExceptionMsg; - import std.format : FormatException; - - auto w = appender!(char[])(); - formattedWrite(w, - "Numbers %2$s and %1$s are reversed and %1$s%2$s repeated", - 42, 0); - assert(w.data == "Numbers 0 and 42 are reversed and 420 repeated", - w.data); - assert(collectExceptionMsg!FormatException(formattedWrite(w, "%1$s, %3$s", 1, 2)) - == "Positional specifier %3$s index exceeds 2"); - - w.clear(); - formattedWrite(w, "asd%s", 23); - assert(w.data == "asd23", w.data); - w.clear(); - formattedWrite(w, "%s%s", 23, 45); - assert(w.data == "2345", w.data); -} - -// https://issues.dlang.org/show_bug.cgi?id=3479 -@safe unittest -{ - import std.array : appender; - - auto stream = appender!(char[])(); - formattedWrite(stream, "%2$.*1$d", 12, 10); - assert(stream.data == "000000000010", stream.data); -} - -// https://issues.dlang.org/show_bug.cgi?id=6893 -@safe unittest -{ - import std.array : appender; - - enum E : ulong { A, B, C } - auto stream = appender!(char[])(); - formattedWrite(stream, "%s", E.C); - assert(stream.data == "C"); -} - -@safe pure unittest -{ - import std.array : appender; - - auto stream = appender!string(); - formattedWrite(stream, "%u", 42); - assert(stream.data == "42", stream.data); -} - -@safe pure unittest -{ - // testing raw writes - import std.array : appender; - - auto w = appender!(char[])(); - uint a = 0x02030405; - formattedWrite(w, "%+r", a); - assert(w.data.length == 4 && w.data[0] == 2 && w.data[1] == 3 - && w.data[2] == 4 && w.data[3] == 5); - - w.clear(); - formattedWrite(w, "%-r", a); - assert(w.data.length == 4 && w.data[0] == 5 && w.data[1] == 4 - && w.data[2] == 3 && w.data[3] == 2); -} - -@safe unittest -{ - import std.array : appender; - import std.conv : text, octal; - - auto stream = appender!(char[])(); - - formattedWrite(stream, "hello world! %s %s ", true, 57, 1_000_000_000, 'x', " foo"); - assert(stream.data == "hello world! true 57 ", stream.data); - stream.clear(); - - formattedWrite(stream, "%g %A %s", 1.67, -1.28, float.nan); - assert(stream.data == "1.67 -0X1.47AE147AE147BP+0 nan", stream.data); - stream.clear(); - - formattedWrite(stream, "%x %X", 0x1234AF, 0xAFAFAFAF); - assert(stream.data == "1234af AFAFAFAF"); - stream.clear(); - - formattedWrite(stream, "%b %o", 0x1234AF, 0xAFAFAFAF); - assert(stream.data == "100100011010010101111 25753727657"); - stream.clear(); - - formattedWrite(stream, "%d %s", 0x1234AF, 0xAFAFAFAF); - assert(stream.data == "1193135 2947526575"); - stream.clear(); - - formattedWrite(stream, "%a %A", 1.32, 6.78f); - assert(stream.data == "0x1.51eb851eb851fp+0 0X1.B1EB86P+2"); - stream.clear(); - - formattedWrite(stream, "%#06.*f", 2, 12.345); - assert(stream.data == "012.35"); - stream.clear(); - - formattedWrite(stream, "%#0*.*f", 6, 2, 12.345); - assert(stream.data == "012.35"); - stream.clear(); - - const real constreal = 1; - formattedWrite(stream, "%g",constreal); - assert(stream.data == "1"); - stream.clear(); - - formattedWrite(stream, "%7.4g:", 12.678); - assert(stream.data == " 12.68:"); - stream.clear(); - - formattedWrite(stream, "%7.4g:", 12.678L); - assert(stream.data == " 12.68:"); - stream.clear(); - - formattedWrite(stream, "%04f|%05d|%#05x|%#5x", -4.0, -10, 1, 1); - assert(stream.data == "-4.000000|-0010|0x001| 0x1", stream.data); - stream.clear(); - - int i; - string s; - - i = -10; - formattedWrite(stream, "%d|%3d|%03d|%1d|%01.4f", i, i, i, i, cast(double) i); - assert(stream.data == "-10|-10|-10|-10|-10.0000"); - stream.clear(); - - i = -5; - formattedWrite(stream, "%d|%3d|%03d|%1d|%01.4f", i, i, i, i, cast(double) i); - assert(stream.data == "-5| -5|-05|-5|-5.0000"); - stream.clear(); - - i = 0; - formattedWrite(stream, "%d|%3d|%03d|%1d|%01.4f", i, i, i, i, cast(double) i); - assert(stream.data == "0| 0|000|0|0.0000"); - stream.clear(); - - i = 5; - formattedWrite(stream, "%d|%3d|%03d|%1d|%01.4f", i, i, i, i, cast(double) i); - assert(stream.data == "5| 5|005|5|5.0000"); - stream.clear(); - - i = 10; - formattedWrite(stream, "%d|%3d|%03d|%1d|%01.4f", i, i, i, i, cast(double) i); - assert(stream.data == "10| 10|010|10|10.0000"); - stream.clear(); - - formattedWrite(stream, "%.0d", 0); - assert(stream.data == "0"); - stream.clear(); - - formattedWrite(stream, "%.g", .34); - assert(stream.data == "0.3"); - stream.clear(); - - stream.clear(); - formattedWrite(stream, "%.0g", .34); - assert(stream.data == "0.3"); - - stream.clear(); - formattedWrite(stream, "%.2g", .34); - assert(stream.data == "0.34"); - - stream.clear(); - formattedWrite(stream, "%0.0008f", 1e-08); - assert(stream.data == "0.00000001"); - - stream.clear(); - formattedWrite(stream, "%0.0008f", 1e-05); - assert(stream.data == "0.00001000"); - - s = "helloworld"; - string r; - stream.clear(); - formattedWrite(stream, "%.2s", s[0 .. 5]); - assert(stream.data == "he"); - stream.clear(); - formattedWrite(stream, "%.20s", s[0 .. 5]); - assert(stream.data == "hello"); - stream.clear(); - formattedWrite(stream, "%8s", s[0 .. 5]); - assert(stream.data == " hello"); - - byte[] arrbyte = new byte[4]; - arrbyte[0] = 100; - arrbyte[1] = -99; - arrbyte[3] = 0; - stream.clear(); - formattedWrite(stream, "%s", arrbyte); - assert(stream.data == "[100, -99, 0, 0]", stream.data); - - ubyte[] arrubyte = new ubyte[4]; - arrubyte[0] = 100; - arrubyte[1] = 200; - arrubyte[3] = 0; - stream.clear(); - formattedWrite(stream, "%s", arrubyte); - assert(stream.data == "[100, 200, 0, 0]", stream.data); - - short[] arrshort = new short[4]; - arrshort[0] = 100; - arrshort[1] = -999; - arrshort[3] = 0; - stream.clear(); - formattedWrite(stream, "%s", arrshort); - assert(stream.data == "[100, -999, 0, 0]"); - stream.clear(); - formattedWrite(stream, "%s", arrshort); - assert(stream.data == "[100, -999, 0, 0]"); - - ushort[] arrushort = new ushort[4]; - arrushort[0] = 100; - arrushort[1] = 20_000; - arrushort[3] = 0; - stream.clear(); - formattedWrite(stream, "%s", arrushort); - assert(stream.data == "[100, 20000, 0, 0]"); - - int[] arrint = new int[4]; - arrint[0] = 100; - arrint[1] = -999; - arrint[3] = 0; - stream.clear(); - formattedWrite(stream, "%s", arrint); - assert(stream.data == "[100, -999, 0, 0]"); - stream.clear(); - formattedWrite(stream, "%s", arrint); - assert(stream.data == "[100, -999, 0, 0]"); - - long[] arrlong = new long[4]; - arrlong[0] = 100; - arrlong[1] = -999; - arrlong[3] = 0; - stream.clear(); - formattedWrite(stream, "%s", arrlong); - assert(stream.data == "[100, -999, 0, 0]"); - stream.clear(); - formattedWrite(stream, "%s",arrlong); - assert(stream.data == "[100, -999, 0, 0]"); - - ulong[] arrulong = new ulong[4]; - arrulong[0] = 100; - arrulong[1] = 999; - arrulong[3] = 0; - stream.clear(); - formattedWrite(stream, "%s", arrulong); - assert(stream.data == "[100, 999, 0, 0]"); - - string[] arr2 = new string[4]; - arr2[0] = "hello"; - arr2[1] = "world"; - arr2[3] = "foo"; - stream.clear(); - formattedWrite(stream, "%s", arr2); - assert(stream.data == `["hello", "world", "", "foo"]`, stream.data); - - stream.clear(); - formattedWrite(stream, "%.8d", 7); - assert(stream.data == "00000007"); - - stream.clear(); - formattedWrite(stream, "%.8x", 10); - assert(stream.data == "0000000a"); - - stream.clear(); - formattedWrite(stream, "%-3d", 7); - assert(stream.data == "7 "); - - stream.clear(); - formattedWrite(stream, "%*d", -3, 7); - assert(stream.data == "7 "); - - stream.clear(); - formattedWrite(stream, "%.*d", -3, 7); - assert(stream.data == "7"); - - stream.clear(); - formattedWrite(stream, "%s", "abc"c); - assert(stream.data == "abc"); - stream.clear(); - formattedWrite(stream, "%s", "def"w); - assert(stream.data == "def", text(stream.data.length)); - stream.clear(); - formattedWrite(stream, "%s", "ghi"d); - assert(stream.data == "ghi"); - - @trusted void* deadBeef() { return cast(void*) 0xDEADBEEF; } - stream.clear(); - formattedWrite(stream, "%s", deadBeef()); - assert(stream.data == "DEADBEEF", stream.data); - - stream.clear(); - formattedWrite(stream, "%#x", 0xabcd); - assert(stream.data == "0xabcd"); - stream.clear(); - formattedWrite(stream, "%#X", 0xABCD); - assert(stream.data == "0XABCD"); - - stream.clear(); - formattedWrite(stream, "%#o", octal!12345); - assert(stream.data == "012345"); - stream.clear(); - formattedWrite(stream, "%o", 9); - assert(stream.data == "11"); - - stream.clear(); - formattedWrite(stream, "%+d", 123); - assert(stream.data == "+123"); - stream.clear(); - formattedWrite(stream, "%+d", -123); - assert(stream.data == "-123"); - stream.clear(); - formattedWrite(stream, "% d", 123); - assert(stream.data == " 123"); - stream.clear(); - formattedWrite(stream, "% d", -123); - assert(stream.data == "-123"); - - stream.clear(); - formattedWrite(stream, "%%"); - assert(stream.data == "%"); - - stream.clear(); - formattedWrite(stream, "%d", true); - assert(stream.data == "1"); - stream.clear(); - formattedWrite(stream, "%d", false); - assert(stream.data == "0"); - - stream.clear(); - formattedWrite(stream, "%d", 'a'); - assert(stream.data == "97", stream.data); - wchar wc = 'a'; - stream.clear(); - formattedWrite(stream, "%d", wc); - assert(stream.data == "97"); - dchar dc = 'a'; - stream.clear(); - formattedWrite(stream, "%d", dc); - assert(stream.data == "97"); - - byte b = byte.max; - stream.clear(); - formattedWrite(stream, "%x", b); - assert(stream.data == "7f"); - stream.clear(); - formattedWrite(stream, "%x", ++b); - assert(stream.data == "80"); - stream.clear(); - formattedWrite(stream, "%x", ++b); - assert(stream.data == "81"); - - short sh = short.max; - stream.clear(); - formattedWrite(stream, "%x", sh); - assert(stream.data == "7fff"); - stream.clear(); - formattedWrite(stream, "%x", ++sh); - assert(stream.data == "8000"); - stream.clear(); - formattedWrite(stream, "%x", ++sh); - assert(stream.data == "8001"); - - i = int.max; - stream.clear(); - formattedWrite(stream, "%x", i); - assert(stream.data == "7fffffff"); - stream.clear(); - formattedWrite(stream, "%x", ++i); - assert(stream.data == "80000000"); - stream.clear(); - formattedWrite(stream, "%x", ++i); - assert(stream.data == "80000001"); - - stream.clear(); - formattedWrite(stream, "%x", 10); - assert(stream.data == "a"); - stream.clear(); - formattedWrite(stream, "%X", 10); - assert(stream.data == "A"); - stream.clear(); - formattedWrite(stream, "%x", 15); - assert(stream.data == "f"); - stream.clear(); - formattedWrite(stream, "%X", 15); - assert(stream.data == "F"); - - @trusted void ObjectTest() - { - Object c = null; - stream.clear(); - formattedWrite(stream, "%s", c); - assert(stream.data == "null"); - } - ObjectTest(); - - enum TestEnum - { - Value1, Value2 - } - stream.clear(); - formattedWrite(stream, "%s", TestEnum.Value2); - assert(stream.data == "Value2", stream.data); - stream.clear(); - formattedWrite(stream, "%s", cast(TestEnum) 5); - assert(stream.data == "cast(TestEnum)5", stream.data); - - //immutable(char[5])[int] aa = ([3:"hello", 4:"betty"]); - //stream.clear(); - //formattedWrite(stream, "%s", aa.values); - //assert(stream.data == "[[h,e,l,l,o],[b,e,t,t,y]]"); - //stream.clear(); - //formattedWrite(stream, "%s", aa); - //assert(stream.data == "[3:[h,e,l,l,o],4:[b,e,t,t,y]]"); - - static const dchar[] ds = ['a','b']; - for (int j = 0; j < ds.length; ++j) - { - stream.clear(); formattedWrite(stream, " %d", ds[j]); - if (j == 0) - assert(stream.data == " 97"); - else - assert(stream.data == " 98"); - } - - stream.clear(); - formattedWrite(stream, "%.-3d", 7); - assert(stream.data == "7", ">" ~ stream.data ~ "<"); -} - -@safe unittest -{ - import std.array : appender; - import std.meta : AliasSeq; - - immutable(char[5])[int] aa = ([3:"hello", 4:"betty"]); - assert(aa[3] == "hello"); - assert(aa[4] == "betty"); - - auto stream = appender!(char[])(); - alias AllNumerics = - AliasSeq!(byte, ubyte, short, ushort, int, uint, long, ulong, - float, double, real); - foreach (T; AllNumerics) - { - T value = 1; - stream.clear(); - formattedWrite(stream, "%s", value); - assert(stream.data == "1"); - } - - stream.clear(); - formattedWrite(stream, "%s", aa); -} - -/** -Formats a value of any type according to a format specifier and -writes the result to an output range. - -More details about how types are formatted, and how the format -specifier influences the outcome, can be found in the definition of a -$(MREF_ALTTEXT format string, std,format). - -Params: - w = an $(REF_ALTTEXT output range, isOutputRange, std, range, primitives) where - the formatted value is written to - val = the value to write - f = a $(REF_ALTTEXT FormatSpec, FormatSpec, std, format, spec) defining the - format specifier - Writer = the type of the output range `w` - T = the type of value `val` - Char = the character type used for `f` - -Throws: - A $(LREF FormatException) if formatting did not succeed. - -Note: - In theory this function should be `@nogc`. But with the current - implementation there are some cases where allocations occur. - See $(REF_ALTTEXT $(D sformat), sformat, std, format) for more details. - -See_Also: - $(LREF formattedWrite) which formats several values at once. - */ -void formatValue(Writer, T, Char)(auto ref Writer w, auto ref T val, scope const ref FormatSpec!Char f) -{ - import std.format : enforceFmt; - - enforceFmt(f.width != f.DYNAMIC && f.precision != f.DYNAMIC - && f.separators != f.DYNAMIC && !f.dynamicSeparatorChar, - "Dynamic argument not allowed for `formatValue`"); - - formatValueImpl(w, val, f); -} - -/// -@safe pure unittest -{ - import std.array : appender; - import std.format.spec : singleSpec; - - auto writer = appender!string(); - auto spec = singleSpec("%08b"); - writer.formatValue(42, spec); - assert(writer.data == "00101010"); - - spec = singleSpec("%2s"); - writer.formatValue('=', spec); - assert(writer.data == "00101010 ="); - - spec = singleSpec("%+14.6e"); - writer.formatValue(42.0, spec); - assert(writer.data == "00101010 = +4.200000e+01"); -} - -// https://issues.dlang.org/show_bug.cgi?id=15386 -@safe pure unittest -{ - import std.array : appender; - import std.format.spec : FormatSpec; - import std.format : FormatException; - import std.exception : assertThrown; - - auto w = appender!(char[])(); - auto dor = appender!(char[])(); - auto fs = FormatSpec!char("%.*s"); - fs.writeUpToNextSpec(dor); - assertThrown!FormatException(formatValue(w, 0, fs)); - - fs = FormatSpec!char("%*s"); - fs.writeUpToNextSpec(dor); - assertThrown!FormatException(formatValue(w, 0, fs)); - - fs = FormatSpec!char("%,*s"); - fs.writeUpToNextSpec(dor); - assertThrown!FormatException(formatValue(w, 0, fs)); - - fs = FormatSpec!char("%,?s"); - fs.writeUpToNextSpec(dor); - assertThrown!FormatException(formatValue(w, 0, fs)); - - assertThrown!FormatException(formattedWrite(w, "%(%0*d%)", new int[1])); -} - -// https://issues.dlang.org/show_bug.cgi?id=22609 -@safe pure unittest -{ - static enum State: ubyte { INACTIVE } - static struct S { - State state = State.INACTIVE; - int generation = 1; - alias state this; - // DMDBUG: https://issues.dlang.org/show_bug.cgi?id=16657 - auto opEquals(S other) const { return state == other.state && generation == other.generation; } - auto opEquals(State other) const { return state == other; } - } - - import std.array : appender; - import std.format.spec : singleSpec; - - auto writer = appender!string(); - const spec = singleSpec("%s"); - S a; - writer.formatValue(a, spec); - assert(writer.data == "0"); -} - -// https://issues.dlang.org/show_bug.cgi?id=23400 -@safe pure unittest -{ - import std.range : nullSink; - import std.format.spec : singleSpec; - - static struct S - { - // non-const opEquals method - bool opEquals(S rhs) { return false; } - } - - enum E { a = S() } - - E e; - auto writer = nullSink; - const spec = singleSpec("%s"); - writer.formatValue(e, spec); -} diff --git a/phobos/std/functional.d b/phobos/std/functional.d deleted file mode 100644 index 588a9c8..0000000 --- a/phobos/std/functional.d +++ /dev/null @@ -1,2007 +0,0 @@ -// Written in the D programming language. - -/** -Functions that manipulate other functions. - -This module provides functions for compile time function composition. These -functions are helpful when constructing predicates for the algorithms in -$(MREF std, algorithm) or $(MREF std, range). - -$(SCRIPT inhibitQuickIndex = 1;) -$(DIVC quickindex, -$(BOOKTABLE , -$(TR $(TH Function Name) $(TH Description) -) - $(TR $(TD $(LREF adjoin)) - $(TD Joins a couple of functions into one that executes the original - functions independently and returns a tuple with all the results. - )) - $(TR $(TD $(LREF compose), $(LREF pipe)) - $(TD Join a couple of functions into one that executes the original - functions one after the other, using one function's result for the next - function's argument. - )) - $(TR $(TD $(LREF lessThan), $(LREF greaterThan), $(LREF equalTo)) - $(TD Ready-made predicate functions to compare two values. - )) - $(TR $(TD $(LREF memoize)) - $(TD Creates a function that caches its result for fast re-evaluation. - )) - $(TR $(TD $(LREF not)) - $(TD Creates a function that negates another. - )) - $(TR $(TD $(LREF partial)) - $(TD Creates a function that binds the first argument of a given function - to a given value. - )) - $(TR $(TD $(LREF curry)) - $(TD Converts a multi-argument function into a series of single-argument - functions. f(x, y) == curry(f)(x)(y) - )) - $(TR $(TD $(LREF reverseArgs)) - $(TD Predicate that reverses the order of its arguments. - )) - $(TR $(TD $(LREF toDelegate)) - $(TD Converts a callable to a delegate. - )) - $(TR $(TD $(LREF unaryFun), $(LREF binaryFun)) - $(TD Create a unary or binary function from a string. Most often - used when defining algorithms on ranges. - )) - $(TR $(TD $(LREF bind)) - $(TD Passes the fields of a struct as arguments to a function. - )) -)) - -Copyright: Copyright Andrei Alexandrescu 2008 - 2009. -License: $(HTTP boost.org/LICENSE_1_0.txt, Boost License 1.0). -Authors: $(HTTP erdani.org, Andrei Alexandrescu) -Source: $(PHOBOSSRC std/functional.d) -*/ -/* - Copyright Andrei Alexandrescu 2008 - 2009. -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.functional; - -import std.meta : AliasSeq, Reverse; -import std.traits : isCallable, Parameters; -import std.conv : toCtString; - -import std.internal.attributes : betterC; - -public import core.lifetime : forward; - -private template needOpCallAlias(alias fun) -{ - /* Determine whether or not unaryFun and binaryFun need to alias to fun or - * fun.opCall. Basically, fun is a function object if fun(...) compiles. We - * want is(unaryFun!fun) (resp., is(binaryFun!fun)) to be true if fun is - * any function object. There are 4 possible cases: - * - * 1) fun is the type of a function object with static opCall; - * 2) fun is an instance of a function object with static opCall; - * 3) fun is the type of a function object with non-static opCall; - * 4) fun is an instance of a function object with non-static opCall. - * - * In case (1), is(unaryFun!fun) should compile, but does not if unaryFun - * aliases itself to fun, because typeof(fun) is an error when fun itself - * is a type. So it must be aliased to fun.opCall instead. All other cases - * should be aliased to fun directly. - */ - static if (is(typeof(fun.opCall) == function)) - { - enum needOpCallAlias = !is(typeof(fun)) && __traits(compiles, () { - return fun(Parameters!fun.init); - }); - } - else - enum needOpCallAlias = false; -} - -/** -Transforms a `string` representing an expression into a unary -function. The `string` must either use symbol name `a` as -the parameter or provide the symbol via the `parmName` argument. - -Params: - fun = a `string` or a callable - parmName = the name of the parameter if `fun` is a string. Defaults - to `"a"`. -Returns: - If `fun` is a `string`, a new single parameter function - - If `fun` is not a `string`, an alias to `fun`. -*/ -template unaryFun(alias fun, string parmName = "a") -{ - static if (is(typeof(fun) : string)) - { - static if (!fun._ctfeMatchUnary(parmName)) - { - import std.algorithm, std.conv, std.exception, std.math, std.range, std.string; - import std.meta, std.traits, std.typecons; - } - auto unaryFun(ElementType)(auto ref ElementType __a) - { - mixin("alias " ~ parmName ~ " = __a ;"); - return mixin(fun); - } - } - else static if (needOpCallAlias!fun) - { - // https://issues.dlang.org/show_bug.cgi?id=9906 - alias unaryFun = fun.opCall; - } - else - { - alias unaryFun = fun; - } -} - -/// -@safe unittest -{ - // Strings are compiled into functions: - alias isEven = unaryFun!("(a & 1) == 0"); - assert(isEven(2) && !isEven(1)); -} - -@safe unittest -{ - static int f1(int a) { return a + 1; } - static assert(is(typeof(unaryFun!(f1)(1)) == int)); - assert(unaryFun!(f1)(41) == 42); - int f2(int a) { return a + 1; } - static assert(is(typeof(unaryFun!(f2)(1)) == int)); - assert(unaryFun!(f2)(41) == 42); - assert(unaryFun!("a + 1")(41) == 42); - //assert(unaryFun!("return a + 1;")(41) == 42); - - int num = 41; - assert(unaryFun!"a + 1"(num) == 42); - - // https://issues.dlang.org/show_bug.cgi?id=9906 - struct Seen - { - static bool opCall(int n) { return true; } - } - static assert(needOpCallAlias!Seen); - static assert(is(typeof(unaryFun!Seen(1)))); - assert(unaryFun!Seen(1)); - - Seen s; - static assert(!needOpCallAlias!s); - static assert(is(typeof(unaryFun!s(1)))); - assert(unaryFun!s(1)); - - struct FuncObj - { - bool opCall(int n) { return true; } - } - FuncObj fo; - static assert(!needOpCallAlias!fo); - static assert(is(typeof(unaryFun!fo))); - assert(unaryFun!fo(1)); - - // Function object with non-static opCall can only be called with an - // instance, not with merely the type. - static assert(!is(typeof(unaryFun!FuncObj))); -} - -/** -Transforms a `string` representing an expression into a binary function. The -`string` must either use symbol names `a` and `b` as the parameters or -provide the symbols via the `parm1Name` and `parm2Name` arguments. - -Params: - fun = a `string` or a callable - parm1Name = the name of the first parameter if `fun` is a string. - Defaults to `"a"`. - parm2Name = the name of the second parameter if `fun` is a string. - Defaults to `"b"`. -Returns: - If `fun` is not a string, `binaryFun` aliases itself away to - `fun`. -*/ -template binaryFun(alias fun, string parm1Name = "a", - string parm2Name = "b") -{ - static if (is(typeof(fun) : string)) - { - static if (!fun._ctfeMatchBinary(parm1Name, parm2Name)) - { - import std.algorithm, std.conv, std.exception, std.math, std.range, std.string; - import std.meta, std.traits, std.typecons; - } - auto binaryFun(ElementType1, ElementType2) - (auto ref ElementType1 __a, auto ref ElementType2 __b) - { - mixin("alias "~parm1Name~" = __a ;"); - mixin("alias "~parm2Name~" = __b ;"); - return mixin(fun); - } - } - else static if (needOpCallAlias!fun) - { - // https://issues.dlang.org/show_bug.cgi?id=9906 - alias binaryFun = fun.opCall; - } - else - { - alias binaryFun = fun; - } -} - -/// -@safe unittest -{ - alias less = binaryFun!("a < b"); - assert(less(1, 2) && !less(2, 1)); - alias greater = binaryFun!("a > b"); - assert(!greater("1", "2") && greater("2", "1")); -} - -@safe unittest -{ - static int f1(int a, string b) { return a + 1; } - static assert(is(typeof(binaryFun!(f1)(1, "2")) == int)); - assert(binaryFun!(f1)(41, "a") == 42); - string f2(int a, string b) { return b ~ "2"; } - static assert(is(typeof(binaryFun!(f2)(1, "1")) == string)); - assert(binaryFun!(f2)(1, "4") == "42"); - assert(binaryFun!("a + b")(41, 1) == 42); - //@@BUG - //assert(binaryFun!("return a + b;")(41, 1) == 42); - - // https://issues.dlang.org/show_bug.cgi?id=9906 - struct Seen - { - static bool opCall(int x, int y) { return true; } - } - static assert(is(typeof(binaryFun!Seen))); - assert(binaryFun!Seen(1,1)); - - struct FuncObj - { - bool opCall(int x, int y) { return true; } - } - FuncObj fo; - static assert(!needOpCallAlias!fo); - static assert(is(typeof(binaryFun!fo))); - assert(unaryFun!fo(1,1)); - - // Function object with non-static opCall can only be called with an - // instance, not with merely the type. - static assert(!is(typeof(binaryFun!FuncObj))); -} - -// skip all ASCII chars except a .. z, A .. Z, 0 .. 9, '_' and '.'. -private uint _ctfeSkipOp(ref string op) -{ - if (!__ctfe) assert(false); - import std.ascii : isASCII, isAlphaNum; - immutable oldLength = op.length; - while (op.length) - { - immutable front = op[0]; - if (front.isASCII() && !(front.isAlphaNum() || front == '_' || front == '.')) - op = op[1..$]; - else - break; - } - return oldLength != op.length; -} - -// skip all digits -private uint _ctfeSkipInteger(ref string op) -{ - if (!__ctfe) assert(false); - import std.ascii : isDigit; - immutable oldLength = op.length; - while (op.length) - { - immutable front = op[0]; - if (front.isDigit()) - op = op[1..$]; - else - break; - } - return oldLength != op.length; -} - -// skip name -private uint _ctfeSkipName(ref string op, string name) -{ - if (!__ctfe) assert(false); - if (op.length >= name.length && op[0 .. name.length] == name) - { - op = op[name.length..$]; - return 1; - } - return 0; -} - -// returns 1 if `fun` is trivial unary function -private uint _ctfeMatchUnary(string fun, string name) -{ - if (!__ctfe) assert(false); - fun._ctfeSkipOp(); - for (;;) - { - immutable h = fun._ctfeSkipName(name) + fun._ctfeSkipInteger(); - if (h == 0) - { - fun._ctfeSkipOp(); - break; - } - else if (h == 1) - { - if (!fun._ctfeSkipOp()) - break; - } - else - return 0; - } - return fun.length == 0; -} - -@safe unittest -{ - static assert(!_ctfeMatchUnary("sqrt(ё)", "ё")); - static assert(!_ctfeMatchUnary("ё.sqrt", "ё")); - static assert(!_ctfeMatchUnary(".ё+ё", "ё")); - static assert(!_ctfeMatchUnary("_ё+ё", "ё")); - static assert(!_ctfeMatchUnary("ёё", "ё")); - static assert(_ctfeMatchUnary("a+a", "a")); - static assert(_ctfeMatchUnary("a + 10", "a")); - static assert(_ctfeMatchUnary("4 == a", "a")); - static assert(_ctfeMatchUnary("2 == a", "a")); - static assert(_ctfeMatchUnary("1 != a", "a")); - static assert(_ctfeMatchUnary("a != 4", "a")); - static assert(_ctfeMatchUnary("a< 1", "a")); - static assert(_ctfeMatchUnary("434 < a", "a")); - static assert(_ctfeMatchUnary("132 > a", "a")); - static assert(_ctfeMatchUnary("123 >a", "a")); - static assert(_ctfeMatchUnary("a>82", "a")); - static assert(_ctfeMatchUnary("ё>82", "ё")); - static assert(_ctfeMatchUnary("ё[ё(ё)]", "ё")); - static assert(_ctfeMatchUnary("ё[21]", "ё")); -} - -// returns 1 if `fun` is trivial binary function -private uint _ctfeMatchBinary(string fun, string name1, string name2) -{ - if (!__ctfe) assert(false); - fun._ctfeSkipOp(); - for (;;) - { - immutable h = fun._ctfeSkipName(name1) + fun._ctfeSkipName(name2) + fun._ctfeSkipInteger(); - if (h == 0) - { - fun._ctfeSkipOp(); - break; - } - else if (h == 1) - { - if (!fun._ctfeSkipOp()) - break; - } - else - return 0; - } - return fun.length == 0; -} - -@safe unittest -{ - - static assert(!_ctfeMatchBinary("sqrt(ё)", "ё", "b")); - static assert(!_ctfeMatchBinary("ё.sqrt", "ё", "b")); - static assert(!_ctfeMatchBinary(".ё+ё", "ё", "b")); - static assert(!_ctfeMatchBinary("_ё+ё", "ё", "b")); - static assert(!_ctfeMatchBinary("ёё", "ё", "b")); - static assert(_ctfeMatchBinary("a+a", "a", "b")); - static assert(_ctfeMatchBinary("a + 10", "a", "b")); - static assert(_ctfeMatchBinary("4 == a", "a", "b")); - static assert(_ctfeMatchBinary("2 == a", "a", "b")); - static assert(_ctfeMatchBinary("1 != a", "a", "b")); - static assert(_ctfeMatchBinary("a != 4", "a", "b")); - static assert(_ctfeMatchBinary("a< 1", "a", "b")); - static assert(_ctfeMatchBinary("434 < a", "a", "b")); - static assert(_ctfeMatchBinary("132 > a", "a", "b")); - static assert(_ctfeMatchBinary("123 >a", "a", "b")); - static assert(_ctfeMatchBinary("a>82", "a", "b")); - static assert(_ctfeMatchBinary("ё>82", "ё", "q")); - static assert(_ctfeMatchBinary("ё[ё(10)]", "ё", "q")); - static assert(_ctfeMatchBinary("ё[21]", "ё", "q")); - - static assert(!_ctfeMatchBinary("sqrt(ё)+b", "b", "ё")); - static assert(!_ctfeMatchBinary("ё.sqrt-b", "b", "ё")); - static assert(!_ctfeMatchBinary(".ё+b", "b", "ё")); - static assert(!_ctfeMatchBinary("_b+ё", "b", "ё")); - static assert(!_ctfeMatchBinary("ba", "b", "a")); - static assert(_ctfeMatchBinary("a+b", "b", "a")); - static assert(_ctfeMatchBinary("a + b", "b", "a")); - static assert(_ctfeMatchBinary("b == a", "b", "a")); - static assert(_ctfeMatchBinary("b == a", "b", "a")); - static assert(_ctfeMatchBinary("b != a", "b", "a")); - static assert(_ctfeMatchBinary("a != b", "b", "a")); - static assert(_ctfeMatchBinary("a< b", "b", "a")); - static assert(_ctfeMatchBinary("b < a", "b", "a")); - static assert(_ctfeMatchBinary("b > a", "b", "a")); - static assert(_ctfeMatchBinary("b >a", "b", "a")); - static assert(_ctfeMatchBinary("a>b", "b", "a")); - static assert(_ctfeMatchBinary("ё>b", "b", "ё")); - static assert(_ctfeMatchBinary("b[ё(-1)]", "b", "ё")); - static assert(_ctfeMatchBinary("ё[-21]", "b", "ё")); -} - -//undocumented -template safeOp(string S) -if (S=="<"||S==">"||S=="<="||S==">="||S=="=="||S=="!=") -{ - import std.traits : isIntegral; - private bool unsafeOp(ElementType1, ElementType2)(ElementType1 a, ElementType2 b) pure - if (isIntegral!ElementType1 && isIntegral!ElementType2) - { - import std.traits : CommonType; - alias T = CommonType!(ElementType1, ElementType2); - return mixin("cast(T)a "~S~" cast(T) b"); - } - - bool safeOp(T0, T1)(auto ref T0 a, auto ref T1 b) - { - import std.traits : mostNegative; - static if (isIntegral!T0 && isIntegral!T1 && - (mostNegative!T0 < 0) != (mostNegative!T1 < 0)) - { - static if (S == "<=" || S == "<") - { - static if (mostNegative!T0 < 0) - immutable result = a < 0 || unsafeOp(a, b); - else - immutable result = b >= 0 && unsafeOp(a, b); - } - else - { - static if (mostNegative!T0 < 0) - immutable result = a >= 0 && unsafeOp(a, b); - else - immutable result = b < 0 || unsafeOp(a, b); - } - } - else - { - static assert(is(typeof(mixin("a "~S~" b"))), - "Invalid arguments: Cannot compare types " ~ T0.stringof ~ " and " ~ T1.stringof ~ "."); - - immutable result = mixin("a "~S~" b"); - } - return result; - } -} - -@safe unittest //check user defined types -{ - import std.algorithm.comparison : equal; - struct Foo - { - int a; - auto opEquals(Foo foo) - { - return a == foo.a; - } - } - assert(safeOp!"!="(Foo(1), Foo(2))); -} - -/** - Predicate that returns $(D_PARAM a < b). - Correctly compares signed and unsigned integers, ie. -1 < 2U. -*/ -alias lessThan = safeOp!"<"; - -/// -pure @safe @nogc nothrow unittest -{ - assert(lessThan(2, 3)); - assert(lessThan(2U, 3U)); - assert(lessThan(2, 3.0)); - assert(lessThan(-2, 3U)); - assert(lessThan(2, 3U)); - assert(!lessThan(3U, -2)); - assert(!lessThan(3U, 2)); - assert(!lessThan(0, 0)); - assert(!lessThan(0U, 0)); - assert(!lessThan(0, 0U)); -} - -/** - Predicate that returns $(D_PARAM a > b). - Correctly compares signed and unsigned integers, ie. 2U > -1. -*/ -alias greaterThan = safeOp!">"; - -/// -@safe unittest -{ - assert(!greaterThan(2, 3)); - assert(!greaterThan(2U, 3U)); - assert(!greaterThan(2, 3.0)); - assert(!greaterThan(-2, 3U)); - assert(!greaterThan(2, 3U)); - assert(greaterThan(3U, -2)); - assert(greaterThan(3U, 2)); - assert(!greaterThan(0, 0)); - assert(!greaterThan(0U, 0)); - assert(!greaterThan(0, 0U)); -} - -/** - Predicate that returns $(D_PARAM a == b). - Correctly compares signed and unsigned integers, ie. !(-1 == ~0U). -*/ -alias equalTo = safeOp!"=="; - -/// -@safe unittest -{ - assert(equalTo(0U, 0)); - assert(equalTo(0, 0U)); - assert(!equalTo(-1, ~0U)); -} -/** -N-ary predicate that reverses the order of arguments, e.g., given -$(D pred(a, b, c)), returns $(D pred(c, b, a)). - -Params: - pred = A callable -Returns: - A function which calls `pred` after reversing the given parameters -*/ -template reverseArgs(alias pred) -{ - auto reverseArgs(Args...)(auto ref Args args) - if (is(typeof(pred(Reverse!args)))) - { - return pred(Reverse!args); - } -} - -/// -@safe unittest -{ - alias gt = reverseArgs!(binaryFun!("a < b")); - assert(gt(2, 1) && !gt(1, 1)); -} - -/// -@safe unittest -{ - int x = 42; - bool xyz(int a, int b) { return a * x < b / x; } - auto foo = &xyz; - foo(4, 5); - alias zyx = reverseArgs!(foo); - assert(zyx(5, 4) == foo(4, 5)); -} - -/// -@safe unittest -{ - alias gt = reverseArgs!(binaryFun!("a < b")); - assert(gt(2, 1) && !gt(1, 1)); - int x = 42; - bool xyz(int a, int b) { return a * x < b / x; } - auto foo = &xyz; - foo(4, 5); - alias zyx = reverseArgs!(foo); - assert(zyx(5, 4) == foo(4, 5)); -} - -/// -@safe unittest -{ - int abc(int a, int b, int c) { return a * b + c; } - alias cba = reverseArgs!abc; - assert(abc(91, 17, 32) == cba(32, 17, 91)); -} - -/// -@safe unittest -{ - int a(int a) { return a * 2; } - alias _a = reverseArgs!a; - assert(a(2) == _a(2)); -} - -/// -@safe unittest -{ - int b() { return 4; } - alias _b = reverseArgs!b; - assert(b() == _b()); -} - -/** -Negates predicate `pred`. - -Params: - pred = A string or a callable -Returns: - A function which calls `pred` and returns the logical negation of its - return value. - */ -template not(alias pred) -{ - auto not(T...)(auto ref T args) - { - static if (is(typeof(!pred(args)))) - return !pred(args); - else static if (T.length == 1) - return !unaryFun!pred(args); - else static if (T.length == 2) - return !binaryFun!pred(args); - else - static assert(0); - } -} - -/// -@safe unittest -{ - import std.algorithm.searching : find; - import std.uni : isWhite; - string a = " Hello, world!"; - assert(find!(not!isWhite)(a) == "Hello, world!"); -} - -@safe unittest -{ - assert(not!"a != 5"(5)); - assert(not!"a != b"(5, 5)); - - assert(not!(() => false)()); - assert(not!(a => a != 5)(5)); - assert(not!((a, b) => a != b)(5, 5)); - assert(not!((a, b, c) => a * b * c != 125 )(5, 5, 5)); -} - -/** -$(LINK2 http://en.wikipedia.org/wiki/Partial_application, Partially -applies) $(D_PARAM fun) by tying its first argument to $(D_PARAM arg). - -Params: - fun = A callable - arg = The first argument to apply to `fun` -Returns: - A new function which calls `fun` with `arg` plus the passed parameters. - */ -template partial(alias fun, alias arg) -{ - import std.traits : isCallable; - // Check whether fun is a user defined type which implements opCall or a template. - // As opCall itself can be templated, std.traits.isCallable does not work here. - enum isSomeFunctor = (is(typeof(fun) == struct) || is(typeof(fun) == class)) && __traits(hasMember, fun, "opCall"); - static if (isSomeFunctor || __traits(isTemplate, fun)) - { - auto partial(Ts...)(Ts args2) - { - static if (is(typeof(fun(arg, args2)))) - { - return fun(arg, args2); - } - else - { - static string errormsg() - { - string msg = "Cannot call '" ~ fun.stringof ~ "' with arguments " ~ - "(" ~ arg.stringof; - foreach (T; Ts) - msg ~= ", " ~ T.stringof; - msg ~= ")."; - return msg; - } - static assert(0, errormsg()); - } - } - } - else static if (!isCallable!fun) - { - static assert(false, "Cannot apply partial to a non-callable '" ~ fun.stringof ~ "'."); - } - else - { - import std.meta : Filter; - - static if (__traits(compiles, __traits(getOverloads, - __traits(parent, fun), __traits(identifier, fun)))) - alias overloads = __traits(getOverloads, __traits(parent, fun), - __traits(identifier, fun)); - else - alias overloads = AliasSeq!(fun); - - enum isCallableWithArg(alias fun) = Parameters!fun.length > 0 && - is(typeof(arg) : Parameters!fun[0]); - alias candidates = Filter!(isCallableWithArg, overloads); - - static if (overloads.length == 1 && Parameters!fun.length == 0) - { - static assert(0, "Cannot partially apply '" ~ fun.stringof ~ "'." ~ - "'" ~ fun.stringof ~ "' has 0 arguments."); - } - else static if (candidates.length == 0) - { - import std.meta : NoDuplicates, staticMap; - - enum hasParameters(alias fun) = Parameters!fun.length > 0; - alias firstParameter(alias fun) = Parameters!fun[0]; - alias firstParameters = NoDuplicates!( - staticMap!(firstParameter, Filter!(hasParameters, overloads))); - - string errorMsg() - { - string msg = "Argument mismatch for '" ~ fun.stringof ~ - "': expected " ~ firstParameters[0].stringof; - static foreach (firstParam; firstParameters[1 .. $]) - msg ~= " or " ~ firstParam.stringof; - msg ~= ", but got " ~ typeof(arg).stringof ~ "."; - - return msg; - } - static assert(0, errorMsg()); - } - else - { - import std.traits : ReturnType; - static foreach (candidate; candidates) - ReturnType!candidate partial(Parameters!candidate[1..$] args2) - { - return candidate(arg, args2); - } - } - } -} - -/// -@safe unittest -{ - int fun(int a, int b) { return a + b; } - alias fun5 = partial!(fun, 5); - assert(fun5(6) == 11); - // Note that in most cases you'd use an alias instead of a value - // assignment. Using an alias allows you to partially evaluate template - // functions without committing to a particular type of the function. -} - -// https://issues.dlang.org/show_bug.cgi?id=21457 -/// -@safe unittest -{ - // Overloads are resolved when the partially applied function is called - // with the remaining arguments. - struct S - { - static char fun(int i, string s) { return s[i]; } - static int fun(int a, int b) { return a * b; } - } - alias fun3 = partial!(S.fun, 3); - assert(fun3("hello") == 'l'); - assert(fun3(10) == 30); -} - -// tests for partially evaluating callables -@safe unittest -{ - static int f1(int a, int b) { return a + b; } - assert(partial!(f1, 5)(6) == 11); - - int f2(int a, int b) { return a + b; } - int x = 5; - assert(partial!(f2, x)(6) == 11); - x = 7; - assert(partial!(f2, x)(6) == 13); - static assert(partial!(f2, 5)(6) == 11); - - auto dg = &f2; - auto f3 = &partial!(dg, x); - assert(f3(6) == 13); - - static int funOneArg(int a) { return a; } - assert(partial!(funOneArg, 1)() == 1); - - static int funThreeArgs(int a, int b, int c) { return a + b + c; } - alias funThreeArgs1 = partial!(funThreeArgs, 1); - assert(funThreeArgs1(2, 3) == 6); - static assert(!is(typeof(funThreeArgs1(2)))); - - enum xe = 5; - alias fe = partial!(f2, xe); - static assert(fe(6) == 11); -} - -// tests for partially evaluating templated/overloaded callables -@safe unittest -{ - static auto add(A, B)(A x, B y) - { - return x + y; - } - - alias add5 = partial!(add, 5); - assert(add5(6) == 11); - static assert(!is(typeof(add5()))); - static assert(!is(typeof(add5(6, 7)))); - - // taking address of templated partial evaluation needs explicit type - auto dg = &add5!(int); - assert(dg(6) == 11); - - int x = 5; - alias addX = partial!(add, x); - assert(addX(6) == 11); - - static struct Callable - { - static string opCall(string a, string b) { return a ~ b; } - int opCall(int a, int b) { return a * b; } - double opCall(double a, double b) { return a + b; } - } - Callable callable; - assert(partial!(Callable, "5")("6") == "56"); - assert(partial!(callable, 5)(6) == 30); - assert(partial!(callable, 7.0)(3.0) == 7.0 + 3.0); - - static struct TCallable - { - auto opCall(A, B)(A a, B b) - { - return a + b; - } - } - TCallable tcallable; - assert(partial!(tcallable, 5)(6) == 11); - static assert(!is(typeof(partial!(tcallable, "5")(6)))); - - static struct NonCallable{} - static assert(!__traits(compiles, partial!(NonCallable, 5)), "Partial should not work on non-callable structs."); - static assert(!__traits(compiles, partial!(NonCallable.init, 5)), - "Partial should not work on instances of non-callable structs."); - - static A funOneArg(A)(A a) { return a; } - alias funOneArg1 = partial!(funOneArg, 1); - assert(funOneArg1() == 1); - - static auto funThreeArgs(A, B, C)(A a, B b, C c) { return a + b + c; } - alias funThreeArgs1 = partial!(funThreeArgs, 1); - assert(funThreeArgs1(2, 3) == 6); - static assert(!is(typeof(funThreeArgs1(1)))); - - auto dg2 = &funOneArg1!(); - assert(dg2() == 1); -} - -// Fix https://issues.dlang.org/show_bug.cgi?id=15732 -@safe unittest -{ - // Test whether it works with functions. - auto partialFunction(){ - auto fullFunction = (float a, float b, float c) => a + b / c; - alias apply1 = partial!(fullFunction, 1); - return &apply1; - } - auto result = partialFunction()(2, 4); - assert(result == 1.5f); - - // And with delegates. - auto partialDelegate(float c){ - auto fullDelegate = (float a, float b) => a + b / c; - alias apply1 = partial!(fullDelegate, 1); - return &apply1; - } - auto result2 = partialDelegate(4)(2); - assert(result2 == 1.5f); -} - -/** -Takes a function of (potentially) many arguments, and returns a function taking -one argument and returns a callable taking the rest. f(x, y) == curry(f)(x)(y) - -Params: - F = a function taking at least one argument - t = a callable object whose opCall takes at least 1 object -Returns: - A single parameter callable object -*/ -template curry(alias F) -if (isCallable!F && Parameters!F.length) -{ - //inspired from the implementation from Artur Skawina here: - //https://forum.dlang.org/post/mailman.1626.1340110492.24740.digitalmars-d@puremagic.com - //This implementation stores a copy of all filled in arguments with each curried result - //this way, the curried functions are independent and don't share any references - //eg: auto fc = curry!f; auto fc1 = fc(1); auto fc2 = fc(2); fc1(3) != fc2(3) - struct CurryImpl(size_t N) - { - alias FParams = Parameters!F; - FParams[0 .. N] storedArguments; - static if (N > 0) - { - this(U : FParams[N - 1])(ref CurryImpl!(N - 1) prev, ref U arg) - { - storedArguments[0 .. N - 1] = prev.storedArguments[]; - storedArguments[N-1] = arg; - } - } - - auto opCall(U : FParams[N])(auto ref U arg) return scope - { - static if (N == FParams.length - 1) - { - return F(storedArguments, arg); - } - else - { - return CurryImpl!(N + 1)(this, arg); - } - } - } - - auto curry() - { - CurryImpl!0 res; - return res; // return CurryImpl!0.init segfaults for delegates on Windows - } -} - -/// -pure @safe @nogc nothrow unittest -{ - int f(int x, int y, int z) - { - return x + y + z; - } - auto cf = curry!f; - auto cf1 = cf(1); - auto cf2 = cf(2); - - assert(cf1(2)(3) == f(1, 2, 3)); - assert(cf2(2)(3) == f(2, 2, 3)); -} - -///ditto -auto curry(T)(T t) -if (isCallable!T && Parameters!T.length) -{ - static auto fun(ref T inst, ref Parameters!T args) - { - return inst(args); - } - - return curry!fun()(t); -} - -/// -pure @safe @nogc nothrow unittest -{ - //works with callable structs too - struct S - { - int w; - int opCall(int x, int y, int z) - { - return w + x + y + z; - } - } - - S s; - s.w = 5; - - auto cs = curry(s); - auto cs1 = cs(1); - auto cs2 = cs(2); - - assert(cs1(2)(3) == s(1, 2, 3)); - assert(cs1(2)(3) == (1 + 2 + 3 + 5)); - assert(cs2(2)(3) ==s(2, 2, 3)); -} - - -@safe pure @nogc nothrow unittest -{ - //currying a single argument function does nothing - int pork(int a){ return a*2;} - auto curryPork = curry!pork; - assert(curryPork(0) == pork(0)); - assert(curryPork(1) == pork(1)); - assert(curryPork(-1) == pork(-1)); - assert(curryPork(1000) == pork(1000)); - - //test 2 argument function - double mixedVeggies(double a, int b, bool) - { - return a + b; - } - - auto mixedCurry = curry!mixedVeggies; - assert(mixedCurry(10)(20)(false) == mixedVeggies(10, 20, false)); - assert(mixedCurry(100)(200)(true) == mixedVeggies(100, 200, true)); - - // struct with opCall - struct S - { - double opCall(int x, double y, short z) const pure nothrow @nogc - { - return x*y*z; - } - } - - S s; - auto curriedStruct = curry(s); - assert(curriedStruct(1)(2)(short(3)) == s(1, 2, short(3))); - assert(curriedStruct(300)(20)(short(10)) == s(300, 20, short(10))); -} - -pure @safe nothrow unittest -{ - auto cfl = curry!((double a, int b) => a + b); - assert(cfl(13)(2) == 15); - - int c = 42; - auto cdg = curry!((double a, int b) => a + b + c); - assert(cdg(13)(2) == 57); - - static class C - { - int opCall(int mult, int add) pure @safe nothrow @nogc scope - { - return mult * 42 + add; - } - } - - scope C ci = new C(); - scope cc = curry(ci); - assert(cc(2)(4) == ci(2, 4)); -} - -// Disallows callables without parameters -pure @safe @nogc nothrow unittest -{ - static void noargs() {} - static assert(!__traits(compiles, curry!noargs())); - - static struct NoArgs - { - void opCall() {} - } - - static assert(!__traits(compiles, curry(NoArgs.init))); -} - -private template Iota(size_t n) -{ - static if (n == 0) - alias Iota = AliasSeq!(); - else - alias Iota = AliasSeq!(Iota!(n - 1), n - 1); -} - -/** -Takes multiple functions and adjoins them together. - -Params: - F = the call-able(s) to adjoin -Returns: - A new function which returns a $(REF Tuple, std,typecons). Each of the - elements of the tuple will be the return values of `F`. - -Note: In the special case where only a single function is provided -($(D F.length == 1)), adjoin simply aliases to the single passed function -(`F[0]`). -*/ -template adjoin(F...) -if (F.length >= 1) -{ - static if (F.length == 1) - alias adjoin = F[0]; - else - auto adjoin(V...)(auto ref V a) - { - import std.typecons : tuple; - import std.meta : staticMap; - - auto resultElement(size_t i)() - { - return F[i](a); - } - - return tuple(staticMap!(resultElement, Iota!(F.length))); - } -} - -/// -@safe unittest -{ - import std.typecons : Tuple; - static bool f1(int a) { return a != 0; } - static int f2(int a) { return a / 2; } - auto x = adjoin!(f1, f2)(5); - assert(is(typeof(x) == Tuple!(bool, int))); - assert(x[0] == true && x[1] == 2); -} - -@safe unittest -{ - import std.typecons : Tuple; - static bool F1(int a) { return a != 0; } - auto x1 = adjoin!(F1)(5); - static int F2(int a) { return a / 2; } - auto x2 = adjoin!(F1, F2)(5); - assert(is(typeof(x2) == Tuple!(bool, int))); - assert(x2[0] && x2[1] == 2); - auto x3 = adjoin!(F1, F2, F2)(5); - assert(is(typeof(x3) == Tuple!(bool, int, int))); - assert(x3[0] && x3[1] == 2 && x3[2] == 2); - - bool F4(int a) { return a != x1; } - alias eff4 = adjoin!(F4); - static struct S - { - bool delegate(int) @safe store; - int fun() { return 42 + store(5); } - } - S s; - s.store = (int a) { return eff4(a); }; - auto x4 = s.fun(); - assert(x4 == 43); -} - -@safe unittest -{ - import std.meta : staticMap; - import std.typecons : Tuple, tuple; - alias funs = staticMap!(unaryFun, "a", "a * 2", "a * 3", "a * a", "-a"); - alias afun = adjoin!funs; - assert(afun(5) == tuple(5, 10, 15, 25, -5)); - - static class C{} - alias IC = immutable(C); - IC foo(){return typeof(return).init;} - Tuple!(IC, IC, IC, IC) ret1 = adjoin!(foo, foo, foo, foo)(); - - static struct S{int* p;} - alias IS = immutable(S); - IS bar(){return typeof(return).init;} - enum Tuple!(IS, IS, IS, IS) ret2 = adjoin!(bar, bar, bar, bar)(); -} - -// https://issues.dlang.org/show_bug.cgi?id=21347 -@safe @betterC unittest -{ - alias f = (int n) => n + 1; - alias g = (int n) => n + 2; - alias h = (int n) => n + 3; - alias i = (int n) => n + 4; - - auto result = adjoin!(f, g, h, i)(0); - - assert(result[0] == 1); - assert(result[1] == 2); - assert(result[2] == 3); - assert(result[3] == 4); -} - -/** - Composes passed-in functions $(D fun[0], fun[1], ...). - - Params: - fun = the call-able(s) or `string`(s) to compose into one function - Returns: - A new function `f(x)` that in turn returns `fun[0](fun[1](...(x)))...`. - - See_Also: $(LREF pipe) -*/ -template compose(fun...) -if (fun.length > 0) -{ - static if (fun.length == 1) - { - alias compose = unaryFun!(fun[0]); - } - else - { - alias fun0 = unaryFun!(fun[0]); - alias rest = compose!(fun[1 .. $]); - - auto compose(Args...)(Args args) - { - return fun0(rest(args)); - } - } -} - -/// -@safe unittest -{ - import std.algorithm.comparison : equal; - import std.algorithm.iteration : map; - import std.array : split; - import std.conv : to; - - // First split a string in whitespace-separated tokens and then - // convert each token into an integer - assert(compose!(map!(to!(int)), split)("1 2 3").equal([1, 2, 3])); -} - -// https://issues.dlang.org/show_bug.cgi?id=6484 -@safe unittest -{ - int f(int a) { return a; } - int g(int a) { return a; } - int h(int a,int b,int c) { return a * b * c; } - - alias F = compose!(f,g,h); - assert(F(1,2,3) == f(g(h(1,2,3)))); -} - -/** - Pipes functions in sequence. Offers the same functionality as $(D - compose), but with functions specified in reverse order. This may - lead to more readable code in some situation because the order of - execution is the same as lexical order. - - Params: - fun = the call-able(s) or `string`(s) to compose into one function - Returns: - A new function `f(x)` that in turn returns `fun[$-1](...fun[1](fun[0](x)))...`. - - Example: - ----- -// Read an entire text file, split the resulting string in -// whitespace-separated tokens, and then convert each token into an -// integer -int[] a = pipe!(readText, split, map!(to!(int)))("file.txt"); ----- - - See_Also: $(LREF compose) - */ -alias pipe(fun...) = compose!(Reverse!(fun)); - -/// -@safe unittest -{ - import std.conv : to; - string foo(int a) { return to!(string)(a); } - int bar(string a) { return to!(int)(a) + 1; } - double baz(int a) { return a + 0.5; } - assert(compose!(baz, bar, foo)(1) == 2.5); - assert(pipe!(foo, bar, baz)(1) == 2.5); - - assert(compose!(baz, `to!(int)(a) + 1`, foo)(1) == 2.5); - assert(compose!(baz, bar)("1"[]) == 2.5); - - assert(compose!(baz, bar)("1") == 2.5); - - assert(compose!(`a + 0.5`, `to!(int)(a) + 1`, foo)(1) == 2.5); -} - -/** - * $(LINK2 https://en.wikipedia.org/wiki/Memoization, Memoizes) a function so as - * to avoid repeated computation. The memoization structure is a hash table keyed by a - * tuple of the function's arguments. There is a speed gain if the - * function is repeatedly called with the same arguments and is more - * expensive than a hash table lookup. For more information on memoization, refer to $(HTTP docs.google.com/viewer?url=http%3A%2F%2Fhop.perl.plover.com%2Fbook%2Fpdf%2F03CachingAndMemoization.pdf, this book chapter). - -Example: ----- -double transmogrify(int a, string b) -{ - ... expensive computation ... -} -alias fastTransmogrify = memoize!transmogrify; -unittest -{ - auto slow = transmogrify(2, "hello"); - auto fast = fastTransmogrify(2, "hello"); - assert(slow == fast); -} ----- - -Params: - fun = the call-able to memozie - maxSize = The maximum size of the GC buffer to hold the return values -Returns: - A new function which calls `fun` and caches its return values. - -Note: - Technically the memoized function should be pure because `memoize` assumes it will - always return the same result for a given tuple of arguments. However, `memoize` does not - enforce that because sometimes it is useful to memoize an impure function, too. -*/ -template memoize(alias fun) -{ - import std.traits : ReturnType; - // https://issues.dlang.org/show_bug.cgi?id=13580 - // alias Args = Parameters!fun; - - ReturnType!fun memoize(Parameters!fun args) - { - alias Args = Parameters!fun; - import std.typecons : Tuple; - import std.traits : Unqual; - - static Unqual!(ReturnType!fun)[Tuple!Args] memo; - auto t = Tuple!Args(args); - if (auto p = t in memo) - return *p; - auto r = fun(args); - memo[t] = r; - return r; - } -} - -/// ditto -template memoize(alias fun, uint maxSize) -{ - import std.traits : ReturnType; - // https://issues.dlang.org/show_bug.cgi?id=13580 - // alias Args = Parameters!fun; - ReturnType!fun memoize(Parameters!fun args) - { - import std.meta : staticMap; - import std.traits : hasIndirections, Unqual; - import std.typecons : tuple; - static struct Value { staticMap!(Unqual, Parameters!fun) args; Unqual!(ReturnType!fun) res; } - static Value[] memo; - static size_t[] initialized; - - if (!memo.length) - { - import core.memory : GC; - - // Ensure no allocation overflows - static assert(maxSize < size_t.max / Value.sizeof); - static assert(maxSize < size_t.max - (8 * size_t.sizeof - 1)); - - enum attr = GC.BlkAttr.NO_INTERIOR | (hasIndirections!Value ? 0 : GC.BlkAttr.NO_SCAN); - memo = (cast(Value*) GC.malloc(Value.sizeof * maxSize, attr))[0 .. maxSize]; - enum nwords = (maxSize + 8 * size_t.sizeof - 1) / (8 * size_t.sizeof); - initialized = (cast(size_t*) GC.calloc(nwords * size_t.sizeof, attr | GC.BlkAttr.NO_SCAN))[0 .. nwords]; - } - - import core.bitop : bt, bts; - import core.lifetime : emplace; - - size_t hash; - foreach (ref arg; args) - hash = hashOf(arg, hash); - // cuckoo hashing - immutable idx1 = hash % maxSize; - if (!bt(initialized.ptr, idx1)) - { - emplace(&memo[idx1], args, fun(args)); - // only set to initialized after setting args and value - // https://issues.dlang.org/show_bug.cgi?id=14025 - bts(initialized.ptr, idx1); - return memo[idx1].res; - } - else if (memo[idx1].args == args) - return memo[idx1].res; - // FNV prime - immutable idx2 = (hash * 16_777_619) % maxSize; - if (!bt(initialized.ptr, idx2)) - { - emplace(&memo[idx2], memo[idx1]); - bts(initialized.ptr, idx2); - } - else if (memo[idx2].args == args) - return memo[idx2].res; - else if (idx1 != idx2) - memo[idx2] = memo[idx1]; - - memo[idx1] = Value(args, fun(args)); - return memo[idx1].res; - } -} - -/** - * To _memoize a recursive function, simply insert the memoized call in lieu of the plain recursive call. - * For example, to transform the exponential-time Fibonacci implementation into a linear-time computation: - */ -@safe nothrow -unittest -{ - ulong fib(ulong n) @safe nothrow - { - return n < 2 ? n : memoize!fib(n - 2) + memoize!fib(n - 1); - } - assert(fib(10) == 55); -} - -/** - * To improve the speed of the factorial function, - */ -@safe unittest -{ - ulong fact(ulong n) @safe - { - return n < 2 ? 1 : n * memoize!fact(n - 1); - } - assert(fact(10) == 3628800); -} - -/** - * This memoizes all values of `fact` up to the largest argument. To only cache the final - * result, move `memoize` outside the function as shown below. - */ -@safe unittest -{ - ulong factImpl(ulong n) @safe - { - return n < 2 ? 1 : n * factImpl(n - 1); - } - alias fact = memoize!factImpl; - assert(fact(10) == 3628800); -} - -/** - * When the `maxSize` parameter is specified, memoize will used - * a fixed size hash table to limit the number of cached entries. - */ -@system unittest // not @safe due to memoize -{ - ulong fact(ulong n) - { - // Memoize no more than 8 values - return n < 2 ? 1 : n * memoize!(fact, 8)(n - 1); - } - assert(fact(8) == 40320); - // using more entries than maxSize will overwrite existing entries - assert(fact(10) == 3628800); -} - -@system unittest // not @safe due to memoize -{ - import core.math : sqrt; - alias msqrt = memoize!(function double(double x) { return sqrt(x); }); - auto y = msqrt(2.0); - assert(y == msqrt(2.0)); - y = msqrt(4.0); - assert(y == sqrt(4.0)); - - // alias mrgb2cmyk = memoize!rgb2cmyk; - // auto z = mrgb2cmyk([43, 56, 76]); - // assert(z == mrgb2cmyk([43, 56, 76])); - - //alias mfib = memoize!fib; - - static ulong fib(ulong n) @safe - { - alias mfib = memoize!fib; - return n < 2 ? 1 : mfib(n - 2) + mfib(n - 1); - } - - auto z = fib(10); - assert(z == 89); - - static ulong fact(ulong n) @safe - { - alias mfact = memoize!fact; - return n < 2 ? 1 : n * mfact(n - 1); - } - assert(fact(10) == 3628800); - - // https://issues.dlang.org/show_bug.cgi?id=12568 - static uint len2(const string s) { // Error - alias mLen2 = memoize!len2; - if (s.length == 0) - return 0; - else - return 1 + mLen2(s[1 .. $]); - } - - int _func(int x) @safe { return 1; } - alias func = memoize!(_func, 10); - assert(func(int.init) == 1); - assert(func(int.init) == 1); -} - -// https://issues.dlang.org/show_bug.cgi?id=16079 -// memoize should work with arrays -@system unittest // not @safe with -dip1000 due to memoize -{ - int executed = 0; - T median(T)(const T[] nums) { - import std.algorithm.sorting : sort; - executed++; - auto arr = nums.dup; - arr.sort(); - if (arr.length % 2) - return arr[$ / 2]; - else - return (arr[$ / 2 - 1] - + arr[$ / 2]) / 2; - } - - alias fastMedian = memoize!(median!int); - - assert(fastMedian([7, 5, 3]) == 5); - assert(fastMedian([7, 5, 3]) == 5); - - assert(executed == 1); -} - -// https://issues.dlang.org/show_bug.cgi?id=16079: memoize should work with structs -@safe unittest -{ - int executed = 0; - T pickFirst(T)(T first) - { - executed++; - return first; - } - - struct Foo { int k; } - Foo A = Foo(3); - - alias first = memoize!(pickFirst!Foo); - assert(first(Foo(3)) == A); - assert(first(Foo(3)) == A); - assert(executed == 1); -} - -// https://issues.dlang.org/show_bug.cgi?id=20439 memoize should work with void opAssign -@safe unittest -{ - static struct S - { - void opAssign(S) {} - } - - assert(memoize!(() => S()) == S()); -} - -// https://issues.dlang.org/show_bug.cgi?id=16079: memoize should work with classes -@system unittest // not @safe with -dip1000 due to memoize -{ - int executed = 0; - T pickFirst(T)(T first) - { - executed++; - return first; - } - - class Bar - { - size_t k; - this(size_t k) - { - this.k = k; - } - override size_t toHash() - { - return k; - } - override bool opEquals(Object o) - { - auto b = cast(Bar) o; - return b && k == b.k; - } - } - - alias firstClass = memoize!(pickFirst!Bar); - assert(firstClass(new Bar(3)).k == 3); - assert(firstClass(new Bar(3)).k == 3); - assert(executed == 1); -} - -// https://issues.dlang.org/show_bug.cgi?id=20302 -@system unittest -{ - version (none) // TODO change `none` to `all` and fix remaining limitations - struct S { const int len; } - else - struct S { int len; } - - static string fun000( string str, S s) { return str[0 .. s.len] ~ "123"; } - static string fun001( string str, const S s) { return str[0 .. s.len] ~ "123"; } - static string fun010(const string str, S s) { return str[0 .. s.len] ~ "123"; } - static string fun011(const string str, const S s) { return str[0 .. s.len] ~ "123"; } - static const(string) fun100( string str, S s) { return str[0 .. s.len] ~ "123"; } - static const(string) fun101( string str, const S s) { return str[0 .. s.len] ~ "123"; } - static const(string) fun110(const string str, S s) { return str[0 .. s.len] ~ "123"; } - static const(string) fun111(const string str, const S s) { return str[0 .. s.len] ~ "123"; } - - static foreach (fun; AliasSeq!(fun000, fun001, fun010, fun011, fun100, fun101, fun110, fun111)) - {{ - alias mfun = memoize!fun; - assert(mfun("abcdefgh", S(3)) == "abc123"); - - alias mfun2 = memoize!(fun, 42); - assert(mfun2("asd", S(3)) == "asd123"); - }} -} - -private struct DelegateFaker(F) -{ - import std.typecons : FuncInfo, MemberFunctionGenerator; - - // for @safe - static F castToF(THIS)(THIS x) @trusted - { - return cast(F) x; - } - - /* - * What all the stuff below does is this: - *-------------------- - * struct DelegateFaker(F) { - * extern(linkage) - * [ref] ReturnType!F doIt(Parameters!F args) [@attributes] - * { - * auto fp = cast(F) &this; - * return fp(args); - * } - * } - *-------------------- - */ - - // We will use MemberFunctionGenerator in std.typecons. This is a policy - // configuration for generating the doIt(). - template GeneratingPolicy() - { - // Inform the genereator that we only have type information. - enum WITHOUT_SYMBOL = true; - - // Generate the function body of doIt(). - template generateFunctionBody(unused...) - { - enum generateFunctionBody = - // [ref] ReturnType doIt(Parameters args) @attributes - q{ - // When this function gets called, the this pointer isn't - // really a this pointer (no instance even really exists), but - // a function pointer that points to the function to be called. - // Cast it to the correct type and call it. - - auto fp = castToF(&this); - return fp(args); - }; - } - } - // Type information used by the generated code. - alias FuncInfo_doIt = FuncInfo!(F); - - // Generate the member function doIt(). - mixin( MemberFunctionGenerator!(GeneratingPolicy!()) - .generateFunction!("FuncInfo_doIt", "doIt", F) ); -} - -/** - * Convert a callable to a delegate with the same parameter list and - * return type, avoiding heap allocations and use of auxiliary storage. - * - * Params: - * fp = a function pointer or an aggregate type with `opCall` defined. - * Returns: - * A delegate with the context pointer pointing to nothing. - * - * Example: - * ---- - * void doStuff() { - * writeln("Hello, world."); - * } - * - * void runDelegate(void delegate() myDelegate) { - * myDelegate(); - * } - * - * auto delegateToPass = toDelegate(&doStuff); - * runDelegate(delegateToPass); // Calls doStuff, prints "Hello, world." - * ---- - * - * BUGS: - * $(UL - * $(LI Does not work with `@safe` functions.) - * $(LI Ignores C-style / D-style variadic arguments.) - * ) - */ -auto toDelegate(F)(auto ref F fp) -if (isCallable!(F)) -{ - static if (is(F == delegate)) - { - return fp; - } - else static if (is(typeof(&F.opCall) == delegate) - || (is(typeof(&F.opCall) V : V*) && is(V == function))) - { - return toDelegate(&fp.opCall); - } - else - { - alias DelType = typeof(&(new DelegateFaker!(F)).doIt); - - static struct DelegateFields { - union { - DelType del; - //pragma(msg, typeof(del)); - - struct { - void* contextPtr; - void* funcPtr; - } - } - } - - // fp is stored in the returned delegate's context pointer. - // The returned delegate's function pointer points to - // DelegateFaker.doIt. - DelegateFields df; - - df.contextPtr = cast(void*) fp; - - DelegateFaker!(F) dummy; - auto dummyDel = &dummy.doIt; - df.funcPtr = dummyDel.funcptr; - - return df.del; - } -} - -/// -@system unittest -{ - static int inc(ref uint num) { - num++; - return 8675309; - } - - uint myNum = 0; - auto incMyNumDel = toDelegate(&inc); - auto returnVal = incMyNumDel(myNum); - assert(myNum == 1); -} - -@system unittest // not @safe due to toDelegate -{ - static int inc(ref uint num) { - num++; - return 8675309; - } - - uint myNum = 0; - auto incMyNumDel = toDelegate(&inc); - int delegate(ref uint) dg = incMyNumDel; - auto returnVal = incMyNumDel(myNum); - assert(myNum == 1); - - interface I { int opCall(); } - class C: I { int opCall() { inc(myNum); return myNum;} } - auto c = new C; - auto i = cast(I) c; - - auto getvalc = toDelegate(c); - assert(getvalc() == 2); - - auto getvali = toDelegate(i); - assert(getvali() == 3); - - struct S1 { int opCall() { inc(myNum); return myNum; } } - static assert(!is(typeof(&s1.opCall) == delegate)); - S1 s1; - auto getvals1 = toDelegate(s1); - assert(getvals1() == 4); - - struct S2 { static int opCall() { return 123456; } } - static assert(!is(typeof(&S2.opCall) == delegate)); - S2 s2; - auto getvals2 =&S2.opCall; - assert(getvals2() == 123456); - - /* test for attributes */ - { - static int refvar = 0xDeadFace; - - static ref int func_ref() { return refvar; } - static int func_pure() pure { return 1; } - static int func_nothrow() nothrow { return 2; } - static int func_property() @property { return 3; } - static int func_safe() @safe { return 4; } - static int func_trusted() @trusted { return 5; } - static int func_system() @system { return 6; } - static int func_pure_nothrow() pure nothrow { return 7; } - static int func_pure_nothrow_safe() pure nothrow @safe { return 8; } - - auto dg_ref = toDelegate(&func_ref); - int delegate() pure dg_pure = toDelegate(&func_pure); - int delegate() nothrow dg_nothrow = toDelegate(&func_nothrow); - int delegate() @property dg_property = toDelegate(&func_property); - int delegate() @safe dg_safe = toDelegate(&func_safe); - int delegate() @trusted dg_trusted = toDelegate(&func_trusted); - int delegate() @system dg_system = toDelegate(&func_system); - int delegate() pure nothrow dg_pure_nothrow = toDelegate(&func_pure_nothrow); - int delegate() @safe pure nothrow dg_pure_nothrow_safe = toDelegate(&func_pure_nothrow_safe); - - //static assert(is(typeof(dg_ref) == ref int delegate())); // [BUG@DMD] - - assert(dg_ref() == refvar); - assert(dg_pure() == 1); - assert(dg_nothrow() == 2); - assert(dg_property() == 3); - assert(dg_safe() == 4); - assert(dg_trusted() == 5); - assert(dg_system() == 6); - assert(dg_pure_nothrow() == 7); - assert(dg_pure_nothrow_safe() == 8); - } - /* test for linkage */ - { - struct S - { - extern(C) static void xtrnC() {} - extern(D) static void xtrnD() {} - } - auto dg_xtrnC = toDelegate(&S.xtrnC); - auto dg_xtrnD = toDelegate(&S.xtrnD); - static assert(! is(typeof(dg_xtrnC) == typeof(dg_xtrnD))); - } -} - -/** - * Passes the fields of a struct as arguments to a function. - * - * Can be used with a $(LINK2 https://dlang.org/spec/expression.html#function_literals, - * function literal) to give temporary names to the fields of a struct or - * tuple. - * - * Params: - * fun = Callable that the struct's fields will be passed to. - * - * Returns: - * A function that accepts a single struct as an argument and passes its - * fields to `fun` when called. - */ -template bind(alias fun) -{ - /** - * Params: - * args = The struct or tuple whose fields will be used as arguments. - * - * Returns: `fun(args.tupleof)` - */ - auto ref bind(T)(auto ref T args) - if (is(T == struct)) - { - import std.meta : Map = staticMap; - import core.lifetime : move; - - // Forwards the i'th member of `args` - // Needed because core.lifetime.forward doesn't work on struct members - template forwardArg(size_t i) - { - static if (__traits(isRef, args) || !is(typeof(move(args.tupleof[i])))) - enum forwardArg = "args.tupleof[" ~ toCtString!i ~ "], "; - else - enum forwardArg = "move(args.tupleof[" ~ toCtString!i ~ "]), "; - } - - static if (args.tupleof.length == 0) - enum argList = ""; - else - alias argList = Map!(forwardArg, Iota!(args.tupleof.length)); - - return mixin("fun(", argList, ")"); - } -} - -/// Giving names to tuple elements -@safe unittest -{ - import std.typecons : tuple; - - auto name = tuple("John", "Doe"); - string full = name.bind!((first, last) => first ~ " " ~ last); - assert(full == "John Doe"); -} - -/// Passing struct fields to a function -@safe unittest -{ - import std.algorithm.comparison : min, max; - - struct Pair - { - int a; - int b; - } - - auto p = Pair(123, 456); - assert(p.bind!min == 123); // min(p.a, p.b) - assert(p.bind!max == 456); // max(p.a, p.b) -} - -/// In a range pipeline -@safe unittest -{ - import std.algorithm.iteration : map, filter; - import std.algorithm.comparison : equal; - import std.typecons : tuple; - - auto ages = [ - tuple("Alice", 35), - tuple("Bob", 64), - tuple("Carol", 21), - tuple("David", 39), - tuple("Eve", 50) - ]; - - auto overForty = ages - .filter!(bind!((name, age) => age > 40)) - .map!(bind!((name, age) => name)); - - assert(overForty.equal(["Bob", "Eve"])); -} - -// Zero arguments -@safe unittest -{ - struct Empty {} - - assert(Empty().bind!(() => 123) == 123); -} - -// Non-copyable arguments -@safe unittest -{ - import std.typecons : tuple; - - static struct NoCopy - { - int n; - @disable this(this); - } - - static struct Pair - { - NoCopy a, b; - } - - static auto fun(NoCopy a, NoCopy b) - { - return tuple(a.n, b.n); - } - - auto expected = fun(NoCopy(1), NoCopy(2)); - assert(Pair(NoCopy(1), NoCopy(2)).bind!fun == expected); -} - -// ref arguments -@safe unittest -{ - import std.typecons : tuple; - - auto t = tuple(123, 456); - t.bind!((ref int a, int b) { a = 789; b = 1011; }); - - assert(t[0] == 789); - assert(t[1] == 456); -} - -// auto ref arguments -@safe unittest -{ - import std.typecons : tuple; - - auto t = tuple(123); - t.bind!((auto ref x) { - static assert(__traits(isRef, x)); - }); - tuple(123).bind!((auto ref x) { - static assert(!__traits(isRef, x)); - }); -} diff --git a/phobos/std/getopt.d b/phobos/std/getopt.d deleted file mode 100644 index 5b9ca78..0000000 --- a/phobos/std/getopt.d +++ /dev/null @@ -1,1974 +0,0 @@ -// Written in the D programming language. - -/** -Processing of command line options. - -The getopt module implements a `getopt` function, which adheres to -the POSIX syntax for command line options. GNU extensions are -supported in the form of long options introduced by a double dash -("--"). Support for bundling of command line options, as was the case -with the more traditional single-letter approach, is provided but not -enabled by default. - -Copyright: Copyright Andrei Alexandrescu 2008 - 2015. -License: $(HTTP www.boost.org/LICENSE_1_0.txt, Boost License 1.0). -Authors: $(HTTP erdani.org, Andrei Alexandrescu) -Credits: This module and its documentation are inspired by Perl's - $(HTTPS perldoc.perl.org/Getopt/Long.html, Getopt::Long) module. The syntax of - D's `getopt` is simpler than its Perl counterpart because $(D - getopt) infers the expected parameter types from the static types of - the passed-in pointers. -Source: $(PHOBOSSRC std/getopt.d) -*/ -/* - Copyright Andrei Alexandrescu 2008 - 2015. -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.getopt; - -import std.exception : basicExceptionCtors; -import std.traits; - -/** -Thrown on one of the following conditions: -$(UL - $(LI An unrecognized command-line argument is passed, and - `std.getopt.config.passThrough` was not present.) - $(LI A command-line option was not found, and - `std.getopt.config.required` was present.) - $(LI A callback option is missing a value.) -) -*/ -class GetOptException : Exception -{ - mixin basicExceptionCtors; -} - -static assert(is(typeof(new GetOptException("message")))); -static assert(is(typeof(new GetOptException("message", Exception.init)))); - -/** - Parse and remove command line options from a string array. - - Synopsis: - ---------- -import std.getopt; - -string data = "file.dat"; -int length = 24; -bool verbose; -enum Color { no, yes }; -Color color; - -void main(string[] args) -{ - auto helpInformation = getopt( - args, - "length", &length, // numeric - "file", &data, // string - "verbose", &verbose, // flag - "color", "Information about this color", &color); // enum - ... - - if (helpInformation.helpWanted) - { - defaultGetoptPrinter("Some information about the program.", - helpInformation.options); - } -} ---------- - - The `getopt` function takes a reference to the command line - (as received by `main`) as its first argument, and an - unbounded number of pairs of strings and pointers. Each string is an - option meant to "fill" the value referenced by the pointer to its - right (the "bound" pointer). The option string in the call to - `getopt` should not start with a dash. - - In all cases, the command-line options that were parsed and used by - `getopt` are removed from `args`. Whatever in the - arguments did not look like an option is left in `args` for - further processing by the program. Values that were unaffected by the - options are not touched, so a common idiom is to initialize options - to their defaults and then invoke `getopt`. If a - command-line argument is recognized as an option with a parameter and - the parameter cannot be parsed properly (e.g., a number is expected - but not present), a `ConvException` exception is thrown. - If `std.getopt.config.passThrough` was not passed to `getopt` - and an unrecognized command-line argument is found, or if a required - argument is missing a `GetOptException` is thrown. - - Depending on the type of the pointer being bound, `getopt` - recognizes the following kinds of options: - - $(OL - $(LI $(I Boolean options). A lone argument sets the option to `true`. - Additionally $(B true) or $(B false) can be set within the option separated - with an "=" sign: - ---------- - bool verbose = false, debugging = true; - getopt(args, "verbose", &verbose, "debug", &debugging); ---------- - - To set `verbose` to `true`, invoke the program with either - `--verbose` or `--verbose=true`. - - To set `debugging` to `false`, invoke the program with - `--debugging=false`. - ) - - $(LI $(I Numeric options.) If an option is bound to a numeric type, a - number is expected as the next option, or right within the option separated - with an "=" sign: - ---------- - uint timeout; - getopt(args, "timeout", &timeout); ---------- - - To set `timeout` to `5`, invoke the program with either - `--timeout=5` or $(D --timeout 5). - ) - - $(LI $(I Incremental options.) If an option name has a "+" suffix and is - bound to a numeric type, then the option's value tracks the number of times - the option occurred on the command line: - ---------- - uint paranoid; - getopt(args, "paranoid+", ¶noid); ---------- - - Invoking the program with "--paranoid --paranoid --paranoid" will set $(D - paranoid) to 3. Note that an incremental option never expects a parameter, - e.g., in the command line "--paranoid 42 --paranoid", the "42" does not set - `paranoid` to 42; instead, `paranoid` is set to 2 and "42" is not - considered as part of the normal program arguments. - ) - - $(LI $(I Enum options.) If an option is bound to an enum, an enum symbol as - a string is expected as the next option, or right within the option - separated with an "=" sign: - ---------- - enum Color { no, yes }; - Color color; // default initialized to Color.no - getopt(args, "color", &color); ---------- - - To set `color` to `Color.yes`, invoke the program with either - `--color=yes` or $(D --color yes). - ) - - $(LI $(I String options.) If an option is bound to a string, a string is - expected as the next option, or right within the option separated with an - "=" sign: - ---------- -string outputFile; -getopt(args, "output", &outputFile); ---------- - - Invoking the program with "--output=myfile.txt" or "--output myfile.txt" - will set `outputFile` to "myfile.txt". If you want to pass a string - containing spaces, you need to use the quoting that is appropriate to your - shell, e.g. --output='my file.txt'. - ) - - $(LI $(I Array options.) If an option is bound to an array, a new element - is appended to the array each time the option occurs: - ---------- -string[] outputFiles; -getopt(args, "output", &outputFiles); ---------- - - Invoking the program with "--output=myfile.txt --output=yourfile.txt" or - "--output myfile.txt --output yourfile.txt" will set `outputFiles` to - $(D [ "myfile.txt", "yourfile.txt" ]). - - Alternatively you can set $(LREF arraySep) to allow multiple elements in - one parameter. - ---------- -string[] outputFiles; -arraySep = ","; // defaults to "", meaning one element per parameter -getopt(args, "output", &outputFiles); ---------- - - With the above code you can invoke the program with - "--output=myfile.txt,yourfile.txt", or "--output myfile.txt,yourfile.txt".) - - $(LI $(I Hash options.) If an option is bound to an associative array, a - string of the form "name=value" is expected as the next option, or right - within the option separated with an "=" sign: - ---------- -double[string] tuningParms; -getopt(args, "tune", &tuningParms); ---------- - - Invoking the program with e.g. "--tune=alpha=0.5 --tune beta=0.6" will set - `tuningParms` to [ "alpha" : 0.5, "beta" : 0.6 ]. - - Alternatively you can set $(LREF arraySep) as the element separator: - ---------- -double[string] tuningParms; -arraySep = ","; // defaults to "", meaning one element per parameter -getopt(args, "tune", &tuningParms); ---------- - - With the above code you can invoke the program with - "--tune=alpha=0.5,beta=0.6", or "--tune alpha=0.5,beta=0.6". - - In general, the keys and values can be of any parsable types. - ) - - $(LI $(I Callback options.) An option can be bound to a function or - delegate with the signature $(D void function()), $(D void function(string - option)), $(D void function(string option, string value)), or their - delegate equivalents. - - $(UL - $(LI If the callback doesn't take any arguments, the callback is - invoked whenever the option is seen. - ) - - $(LI If the callback takes one string argument, the option string - (without the leading dash(es)) is passed to the callback. After that, - the option string is considered handled and removed from the options - array. - ---------- -void main(string[] args) -{ - uint verbosityLevel = 1; - void myHandler(string option) - { - if (option == "quiet") - { - verbosityLevel = 0; - } - else - { - assert(option == "verbose"); - verbosityLevel = 2; - } - } - getopt(args, "verbose", &myHandler, "quiet", &myHandler); -} ---------- - - ) - - $(LI If the callback takes two string arguments, the option string is - handled as an option with one argument, and parsed accordingly. The - option and its value are passed to the callback. After that, whatever - was passed to the callback is considered handled and removed from the - list. - ---------- -int main(string[] args) -{ - uint verbosityLevel = 1; - bool handlerFailed = false; - void myHandler(string option, string value) - { - switch (value) - { - case "quiet": verbosityLevel = 0; break; - case "verbose": verbosityLevel = 2; break; - case "shouting": verbosityLevel = verbosityLevel.max; break; - default : - stderr.writeln("Unknown verbosity level ", value); - handlerFailed = true; - break; - } - } - getopt(args, "verbosity", &myHandler); - return handlerFailed ? 1 : 0; -} ---------- - ) - )) -) - -Options_with_multiple_names: -Sometimes option synonyms are desirable, e.g. "--verbose", -"--loquacious", and "--garrulous" should have the same effect. Such -alternate option names can be included in the option specification, -using "|" as a separator: - ---------- -bool verbose; -getopt(args, "verbose|loquacious|garrulous", &verbose); ---------- - -Case: -By default options are case-insensitive. You can change that behavior -by passing `getopt` the `caseSensitive` directive like this: - ---------- -bool foo, bar; -getopt(args, - std.getopt.config.caseSensitive, - "foo", &foo, - "bar", &bar); ---------- - -In the example above, "--foo" and "--bar" are recognized, but "--Foo", "--Bar", -"--FOo", "--bAr", etc. are rejected. -The directive is active until the end of `getopt`, or until the -converse directive `caseInsensitive` is encountered: - ---------- -bool foo, bar; -getopt(args, - std.getopt.config.caseSensitive, - "foo", &foo, - std.getopt.config.caseInsensitive, - "bar", &bar); ---------- - -The option "--Foo" is rejected due to $(D -std.getopt.config.caseSensitive), but not "--Bar", "--bAr" -etc. because the directive $(D -std.getopt.config.caseInsensitive) turned sensitivity off before -option "bar" was parsed. - -Short_versus_long_options: -Traditionally, programs accepted single-letter options preceded by -only one dash (e.g. `-t`). `getopt` accepts such parameters -seamlessly. When used with a double-dash (e.g. `--t`), a -single-letter option behaves the same as a multi-letter option. When -used with a single dash, a single-letter option is accepted. - -To set `timeout` to `5`, use either of the following: `--timeout=5`, -`--timeout 5`, `--t=5`, `--t 5`, `-t5`, or `-t 5`. Forms such as -`-timeout=5` will be not accepted. - -For more details about short options, refer also to the next section. - -Bundling: -Single-letter options can be bundled together, i.e. "-abc" is the same as -$(D "-a -b -c"). By default, this option is turned off. You can turn it on -with the `std.getopt.config.bundling` directive: - ---------- -bool foo, bar; -getopt(args, - std.getopt.config.bundling, - "foo|f", &foo, - "bar|b", &bar); ---------- - -In case you want to only enable bundling for some of the parameters, -bundling can be turned off with `std.getopt.config.noBundling`. - -Required: -An option can be marked as required. If that option is not present in the -arguments an exception will be thrown. - ---------- -bool foo, bar; -getopt(args, - std.getopt.config.required, - "foo|f", &foo, - "bar|b", &bar); ---------- - -Only the option directly following `std.getopt.config.required` is -required. - -Passing_unrecognized_options_through: -If an application needs to do its own processing of whichever arguments -`getopt` did not understand, it can pass the -`std.getopt.config.passThrough` directive to `getopt`: - ---------- -bool foo, bar; -getopt(args, - std.getopt.config.passThrough, - "foo", &foo, - "bar", &bar); ---------- - -An unrecognized option such as "--baz" will be found untouched in -`args` after `getopt` returns. - -Help_Information_Generation: -If an option string is followed by another string, this string serves as a -description for this option. The `getopt` function returns a struct of type -`GetoptResult`. This return value contains information about all passed options -as well a $(D bool GetoptResult.helpWanted) flag indicating whether information -about these options was requested. The `getopt` function always adds an option for -`--help|-h` to set the flag if the option is seen on the command line. - -Options_Terminator: -A lone double-dash terminates `getopt` gathering. It is used to -separate program options from other parameters (e.g., options to be passed -to another program). Invoking the example above with $(D "--foo -- --bar") -parses foo but leaves "--bar" in `args`. The double-dash itself is -removed from the argument array unless the `std.getopt.config.keepEndOfOptions` -directive is given. -*/ -GetoptResult getopt(T...)(ref string[] args, T opts) -{ - import std.exception : enforce; - enforce(args.length, - "Invalid arguments string passed: program name missing"); - configuration cfg; - GetoptResult rslt; - - GetOptException excep; - void[][string] visitedLongOpts, visitedShortOpts; - getoptImpl(args, cfg, rslt, excep, visitedLongOpts, visitedShortOpts, opts); - - if (!rslt.helpWanted && excep !is null) - { - throw excep; - } - - return rslt; -} - -/// -@safe unittest -{ - auto args = ["prog", "--foo", "-b"]; - - bool foo; - bool bar; - auto rslt = getopt(args, "foo|f", "Some information about foo.", &foo, "bar|b", - "Some help message about bar.", &bar); - - if (rslt.helpWanted) - { - defaultGetoptPrinter("Some information about the program.", - rslt.options); - } -} - -/** - Configuration options for `getopt`. - - You can pass them to `getopt` in any position, except in between an option - string and its bound pointer. -*/ -enum config { - /// Turn case sensitivity on - caseSensitive, - /// Turn case sensitivity off (default) - caseInsensitive, - /// Turn bundling on - bundling, - /// Turn bundling off (default) - noBundling, - /// Pass unrecognized arguments through - passThrough, - /// Signal unrecognized arguments as errors (default) - noPassThrough, - /// Stop at first argument that does not look like an option - stopOnFirstNonOption, - /// Do not erase the endOfOptions separator from args - keepEndOfOptions, - /// Make the next option a required option - required -} - -/** The result of the `getopt` function. - -`helpWanted` is set if the option `--help` or `-h` was passed to the option parser. -*/ -struct GetoptResult { - bool helpWanted; /// Flag indicating if help was requested - Option[] options; /// All possible options -} - -/** Information about an option. -*/ -struct Option { - string optShort; /// The short symbol for this option - string optLong; /// The long symbol for this option - string help; /// The description of this option - bool required; /// If a option is required, not passing it will result in an error -} - -private pure Option splitAndGet(string opt) @trusted nothrow -{ - import std.array : split; - auto sp = split(opt, "|"); - Option ret; - if (sp.length > 1) - { - ret.optShort = "-" ~ (sp[0].length < sp[1].length ? - sp[0] : sp[1]); - ret.optLong = "--" ~ (sp[0].length > sp[1].length ? - sp[0] : sp[1]); - } - else if (sp[0].length > 1) - { - ret.optLong = "--" ~ sp[0]; - } - else - { - ret.optShort = "-" ~ sp[0]; - } - - return ret; -} - -@safe unittest -{ - auto oshort = splitAndGet("f"); - assert(oshort.optShort == "-f"); - assert(oshort.optLong == ""); - - auto olong = splitAndGet("foo"); - assert(olong.optShort == ""); - assert(olong.optLong == "--foo"); - - auto oshortlong = splitAndGet("f|foo"); - assert(oshortlong.optShort == "-f"); - assert(oshortlong.optLong == "--foo"); - - auto olongshort = splitAndGet("foo|f"); - assert(olongshort.optShort == "-f"); - assert(olongshort.optLong == "--foo"); -} - -/* -This function verifies that the variadic parameters passed in getOpt -follow this pattern: - - [config override], option, [description], receiver, - - - config override: a config value, optional - - option: a string or a char - - description: a string, optional - - receiver: a pointer or a callable -*/ -private template optionValidator(A...) -{ - import std.format : format; - - enum fmt = "getopt validator: %s (at position %d)"; - enum isReceiver(T) = is(T == U*, U) || (is(T == function)) || (is(T == delegate)); - enum isOptionStr(T) = isSomeString!T || isSomeChar!T; - - auto validator() - { - string msg; - static if (A.length > 0) - { - static if (isReceiver!(A[0])) - { - msg = format(fmt, "first argument must be a string or a config", 0); - } - else static if (!isOptionStr!(A[0]) && !is(A[0] == config)) - { - msg = format(fmt, "invalid argument type: " ~ A[0].stringof, 0); - } - else - { - static foreach (i; 1 .. A.length) - { - static if (!isReceiver!(A[i]) && !isOptionStr!(A[i]) && - !(is(A[i] == config))) - { - msg = format(fmt, "invalid argument type: " ~ A[i].stringof, i); - goto end; - } - else static if (isReceiver!(A[i]) && !isOptionStr!(A[i-1])) - { - msg = format(fmt, "a receiver can not be preceeded by a receiver", i); - goto end; - } - else static if (i > 1 && isOptionStr!(A[i]) && isOptionStr!(A[i-1]) - && isSomeString!(A[i-2])) - { - msg = format(fmt, "a string can not be preceeded by two strings", i); - goto end; - } - } - } - static if (!isReceiver!(A[$-1]) && !is(A[$-1] == config)) - { - msg = format(fmt, "last argument must be a receiver or a config", - A.length -1); - } - } - end: - return msg; - } - enum message = validator; - alias optionValidator = message; -} - -@safe pure unittest -{ - alias P = void*; - alias S = string; - alias A = char; - alias C = config; - alias F = void function(); - - static assert(optionValidator!(S,P) == ""); - static assert(optionValidator!(S,F) == ""); - static assert(optionValidator!(A,P) == ""); - static assert(optionValidator!(A,F) == ""); - - static assert(optionValidator!(C,S,P) == ""); - static assert(optionValidator!(C,S,F) == ""); - static assert(optionValidator!(C,A,P) == ""); - static assert(optionValidator!(C,A,F) == ""); - - static assert(optionValidator!(C,S,S,P) == ""); - static assert(optionValidator!(C,S,S,F) == ""); - static assert(optionValidator!(C,A,S,P) == ""); - static assert(optionValidator!(C,A,S,F) == ""); - - static assert(optionValidator!(C,S,S,P) == ""); - static assert(optionValidator!(C,S,S,P,C,S,F) == ""); - static assert(optionValidator!(C,S,P,C,S,S,F) == ""); - - static assert(optionValidator!(C,A,P,A,S,F) == ""); - static assert(optionValidator!(C,A,P,C,A,S,F) == ""); - - static assert(optionValidator!(P,S,S) != ""); - static assert(optionValidator!(P,P,S) != ""); - static assert(optionValidator!(P,F,S,P) != ""); - static assert(optionValidator!(C,C,S) != ""); - static assert(optionValidator!(S,S,P,S,S,P,S) != ""); - static assert(optionValidator!(S,S,P,P) != ""); - static assert(optionValidator!(S,S,S,P) != ""); - - static assert(optionValidator!(C,A,S,P,C,A,F) == ""); - static assert(optionValidator!(C,A,P,C,A,S,F) == ""); -} - -// https://issues.dlang.org/show_bug.cgi?id=15914 -@safe unittest -{ - import std.exception : assertThrown; - bool opt; - string[] args = ["program", "-a"]; - getopt(args, config.passThrough, 'a', &opt); - assert(opt); - opt = false; - args = ["program", "-a"]; - getopt(args, 'a', &opt); - assert(opt); - opt = false; - args = ["program", "-a"]; - getopt(args, 'a', "help string", &opt); - assert(opt); - opt = false; - args = ["program", "-a"]; - getopt(args, config.caseSensitive, 'a', "help string", &opt); - assert(opt); - - assertThrown(getopt(args, "", "forgot to put a string", &opt)); -} - -private void getoptImpl(T...)(ref string[] args, ref configuration cfg, - ref GetoptResult rslt, ref GetOptException excep, - void[][string] visitedLongOpts, void[][string] visitedShortOpts, T opts) -{ - enum validationMessage = optionValidator!T; - static assert(validationMessage == "", validationMessage); - - import std.algorithm.mutation : remove; - import std.conv : to; - import std.uni : toLower; - static if (opts.length) - { - static if (is(typeof(opts[0]) : config)) - { - // it's a configuration flag, act on it - setConfig(cfg, opts[0]); - return getoptImpl(args, cfg, rslt, excep, visitedLongOpts, - visitedShortOpts, opts[1 .. $]); - } - else - { - // it's an option string - auto option = to!string(opts[0]); - if (option.length == 0) - { - excep = new GetOptException("An option name may not be an empty string", excep); - return; - } - Option optionHelp = splitAndGet(option); - optionHelp.required = cfg.required; - - if (optionHelp.optLong.length) - { - auto name = optionHelp.optLong; - if (!cfg.caseSensitive) - name = name.toLower(); - assert(name !in visitedLongOpts, - "Long option " ~ optionHelp.optLong ~ " is multiply defined"); - - visitedLongOpts[optionHelp.optLong] = []; - } - - if (optionHelp.optShort.length) - { - auto name = optionHelp.optShort; - if (!cfg.caseSensitive) - name = name.toLower(); - assert(name !in visitedShortOpts, - "Short option " ~ optionHelp.optShort - ~ " is multiply defined"); - - visitedShortOpts[optionHelp.optShort] = []; - } - - static if (is(typeof(opts[1]) : string)) - { - alias receiver = opts[2]; - optionHelp.help = opts[1]; - immutable lowSliceIdx = 3; - } - else - { - alias receiver = opts[1]; - immutable lowSliceIdx = 2; - } - - rslt.options ~= optionHelp; - - bool incremental; - // Handle options of the form --blah+ - if (option.length && option[$ - 1] == autoIncrementChar) - { - option = option[0 .. $ - 1]; - incremental = true; - } - - bool optWasHandled = handleOption(option, receiver, args, cfg, incremental); - - if (cfg.required && !optWasHandled) - { - excep = new GetOptException("Required option " - ~ option ~ " was not supplied", excep); - } - cfg.required = false; - - getoptImpl(args, cfg, rslt, excep, visitedLongOpts, - visitedShortOpts, opts[lowSliceIdx .. $]); - } - } - else - { - // no more options to look for, potentially some arguments left - for (size_t i = 1; i < args.length;) - { - auto a = args[i]; - if (endOfOptions.length && a == endOfOptions) - { - // Consume the "--" if keepEndOfOptions is not specified - if (!cfg.keepEndOfOptions) - args = args.remove(i); - break; - } - if (a.length < 2 || a[0] != optionChar) - { - // not an option - if (cfg.stopOnFirstNonOption) break; - ++i; - continue; - } - if (a == "--help" || a == "-h") - { - rslt.helpWanted = true; - args = args.remove(i); - continue; - } - if (!cfg.passThrough) - { - throw new GetOptException("Unrecognized option "~a, excep); - } - ++i; - } - - Option helpOpt; - helpOpt.optShort = "-h"; - helpOpt.optLong = "--help"; - helpOpt.help = "This help information."; - rslt.options ~= helpOpt; - } -} - -private bool handleOption(R)(string option, R receiver, ref string[] args, - ref configuration cfg, bool incremental) -{ - import std.algorithm.iteration : map, splitter; - import std.ascii : isAlpha; - import std.conv : text, to; - // Scan arguments looking for a match for this option - bool ret = false; - for (size_t i = 1; i < args.length; ) - { - auto a = args[i]; - if (endOfOptions.length && a == endOfOptions) break; - if (cfg.stopOnFirstNonOption && (!a.length || a[0] != optionChar)) - { - // first non-option is end of options - break; - } - // Unbundle bundled arguments if necessary - if (cfg.bundling && a.length > 2 && a[0] == optionChar && - a[1] != optionChar) - { - string[] expanded; - foreach (j, dchar c; a[1 .. $]) - { - // If the character is not alpha, stop right there. This allows - // e.g. -j100 to work as "pass argument 100 to option -j". - if (!isAlpha(c)) - { - if (c == '=') - j++; - expanded ~= a[j + 1 .. $]; - break; - } - expanded ~= text(optionChar, c); - } - args = args[0 .. i] ~ expanded ~ args[i + 1 .. $]; - continue; - } - - string val; - if (!optMatch(a, option, val, cfg)) - { - ++i; - continue; - } - - ret = true; - - // found it - // from here on, commit to eat args[i] - // (and potentially args[i + 1] too, but that comes later) - args = args[0 .. i] ~ args[i + 1 .. $]; - - static if (is(typeof(*receiver) == bool)) - { - if (val.length) - { - // parse '--b=true/false' - *receiver = to!(typeof(*receiver))(val); - } - else - { - // no argument means set it to true - *receiver = true; - } - } - else - { - import std.exception : enforce; - // non-boolean option, which might include an argument - enum isCallbackWithLessThanTwoParameters = - (is(typeof(receiver) == delegate) || is(typeof(*receiver) == function)) && - !is(typeof(receiver("", ""))); - if (!isCallbackWithLessThanTwoParameters && !(val.length) && !incremental) - { - // Eat the next argument too. Check to make sure there's one - // to be eaten first, though. - enforce!GetOptException(i < args.length, - "Missing value for argument " ~ a ~ "."); - val = args[i]; - args = args[0 .. i] ~ args[i + 1 .. $]; - } - static if (is(typeof(*receiver) == enum)) - { - *receiver = to!(typeof(*receiver))(val); - } - else static if (is(typeof(*receiver) : real)) - { - // numeric receiver - if (incremental) ++*receiver; - else *receiver = to!(typeof(*receiver))(val); - } - else static if (is(typeof(*receiver) == string)) - { - // string receiver - *receiver = to!(typeof(*receiver))(val); - } - else static if (is(typeof(receiver) == delegate) || - is(typeof(*receiver) == function)) - { - static if (is(typeof(receiver("", "")) : void)) - { - // option with argument - receiver(option, val); - } - else static if (is(typeof(receiver("")) : void)) - { - alias RType = typeof(receiver("")); - static assert(is(RType : void), - "Invalid receiver return type " ~ RType.stringof); - // boolean-style receiver - receiver(option); - } - else - { - alias RType = typeof(receiver()); - static assert(is(RType : void), - "Invalid receiver return type " ~ RType.stringof); - // boolean-style receiver without argument - receiver(); - } - } - else static if (isArray!(typeof(*receiver))) - { - // array receiver - import std.range : ElementEncodingType; - alias E = ElementEncodingType!(typeof(*receiver)); - - if (arraySep == "") - { - *receiver ~= to!E(val); - } - else - { - foreach (elem; val.splitter(arraySep).map!(a => to!E(a))()) - *receiver ~= elem; - } - } - else static if (isAssociativeArray!(typeof(*receiver))) - { - // hash receiver - alias K = typeof(receiver.keys[0]); - alias V = typeof(receiver.values[0]); - - import std.range : only; - import std.string : indexOf; - import std.typecons : Tuple, tuple; - - static Tuple!(K, V) getter(string input) - { - auto j = indexOf(input, assignChar); - enforce!GetOptException(j != -1, "Could not find '" - ~ to!string(assignChar) ~ "' in argument '" ~ input ~ "'."); - auto key = input[0 .. j]; - auto value = input[j + 1 .. $]; - return tuple(to!K(key), to!V(value)); - } - - static void setHash(Range)(R receiver, Range range) - { - foreach (k, v; range.map!getter) - (*receiver)[k] = v; - } - - if (arraySep == "") - setHash(receiver, val.only); - else - setHash(receiver, val.splitter(arraySep)); - } - else - static assert(false, "getopt does not know how to handle the type " ~ typeof(receiver).stringof); - } - } - - return ret; -} - -// https://issues.dlang.org/show_bug.cgi?id=17574 -@safe unittest -{ - import std.algorithm.searching : startsWith; - - try - { - string[string] mapping; - immutable as = arraySep; - arraySep = ","; - scope (exit) - arraySep = as; - string[] args = ["testProgram", "-m", "a=b,c=\"d,e,f\""]; - args.getopt("m", &mapping); - assert(false, "Exception not thrown"); - } - catch (GetOptException goe) - assert(goe.msg.startsWith("Could not find")); -} - -// https://issues.dlang.org/show_bug.cgi?id=5316 - arrays with arraySep -@safe unittest -{ - import std.conv; - - arraySep = ","; - scope (exit) arraySep = ""; - - string[] names; - auto args = ["program.name", "-nfoo,bar,baz"]; - getopt(args, "name|n", &names); - assert(names == ["foo", "bar", "baz"], to!string(names)); - - names = names.init; - args = ["program.name", "-n", "foo,bar,baz"]; - getopt(args, "name|n", &names); - assert(names == ["foo", "bar", "baz"], to!string(names)); - - names = names.init; - args = ["program.name", "--name=foo,bar,baz"]; - getopt(args, "name|n", &names); - assert(names == ["foo", "bar", "baz"], to!string(names)); - - names = names.init; - args = ["program.name", "--name", "foo,bar,baz"]; - getopt(args, "name|n", &names); - assert(names == ["foo", "bar", "baz"], to!string(names)); -} - -// https://issues.dlang.org/show_bug.cgi?id=5316 - associative arrays with arraySep -@safe unittest -{ - import std.conv; - - arraySep = ","; - scope (exit) arraySep = ""; - - int[string] values; - values = values.init; - auto args = ["program.name", "-vfoo=0,bar=1,baz=2"]; - getopt(args, "values|v", &values); - assert(values == ["foo":0, "bar":1, "baz":2], to!string(values)); - - values = values.init; - args = ["program.name", "-v", "foo=0,bar=1,baz=2"]; - getopt(args, "values|v", &values); - assert(values == ["foo":0, "bar":1, "baz":2], to!string(values)); - - values = values.init; - args = ["program.name", "--values=foo=0,bar=1,baz=2"]; - getopt(args, "values|t", &values); - assert(values == ["foo":0, "bar":1, "baz":2], to!string(values)); - - values = values.init; - args = ["program.name", "--values", "foo=0,bar=1,baz=2"]; - getopt(args, "values|v", &values); - assert(values == ["foo":0, "bar":1, "baz":2], to!string(values)); -} - -version (LDC) version (Windows) version = LDC_Windows; - -version (LDC_Windows) -{ - // cannot access TLS globals directly across DLL boundaries - - private - { - dchar _optionChar = '-'; - string _endOfOptions = "--"; - dchar _assignChar = '='; - string _arraySep = ""; - } - - @property @safe @nogc nothrow - pragma(inline, false) // could be safely inlined in the binary containing Phobos only - { - ref dchar optionChar() { return _optionChar; } - ref string endOfOptions() { return _endOfOptions; } - ref dchar assignChar() { return _assignChar; } - ref string arraySep() { return _arraySep; } - } -} -else -{ - /** - The option character (default '-'). - - Defaults to '-' but it can be assigned to prior to calling `getopt`. - */ - dchar optionChar = '-'; - - /** - The string that conventionally marks the end of all options (default '--'). - - Defaults to "--" but can be assigned to prior to calling `getopt`. Assigning an - empty string to `endOfOptions` effectively disables it. - */ - string endOfOptions = "--"; - - /** - The assignment character used in options with parameters (default '='). - - Defaults to '=' but can be assigned to prior to calling `getopt`. - */ - dchar assignChar = '='; - - /** - When set to "", parameters to array and associative array receivers are - treated as an individual argument. That is, only one argument is appended or - inserted per appearance of the option switch. If `arraySep` is set to - something else, then each parameter is first split by the separator, and the - individual pieces are treated as arguments to the same option. - - Defaults to "" but can be assigned to prior to calling `getopt`. - */ - string arraySep = ""; -} // !LDC_Windows - -private enum autoIncrementChar = '+'; - -private struct configuration -{ - import std.bitmanip : bitfields; - mixin(bitfields!( - bool, "caseSensitive", 1, - bool, "bundling", 1, - bool, "passThrough", 1, - bool, "stopOnFirstNonOption", 1, - bool, "keepEndOfOptions", 1, - bool, "required", 1, - ubyte, "", 2)); -} - -private bool optMatch(string arg, scope string optPattern, ref string value, - configuration cfg) @safe -{ - import std.algorithm.iteration : splitter; - import std.string : indexOf; - import std.uni : icmp; - //writeln("optMatch:\n ", arg, "\n ", optPattern, "\n ", value); - //scope(success) writeln("optMatch result: ", value); - if (arg.length < 2 || arg[0] != optionChar) return false; - // yank the leading '-' - arg = arg[1 .. $]; - immutable isLong = arg.length > 1 && arg[0] == optionChar; - //writeln("isLong: ", isLong); - // yank the second '-' if present - if (isLong) arg = arg[1 .. $]; - immutable eqPos = indexOf(arg, assignChar); - if (isLong && eqPos >= 0) - { - // argument looks like --opt=value - value = arg[eqPos + 1 .. $]; - arg = arg[0 .. eqPos]; - } - else - { - if (!isLong && eqPos == 1) - { - // argument looks like -o=value - value = arg[2 .. $]; - arg = arg[0 .. 1]; - } - else - if (!isLong && !cfg.bundling) - { - // argument looks like -ovalue and there's no bundling - value = arg[1 .. $]; - arg = arg[0 .. 1]; - } - else - { - // argument looks like --opt, or -oxyz with bundling - value = null; - } - } - //writeln("Arg: ", arg, " pattern: ", optPattern, " value: ", value); - // Split the option - foreach (v; splitter(optPattern, "|")) - { - //writeln("Trying variant: ", v, " against ", arg); - if (arg == v || (!cfg.caseSensitive && icmp(arg, v) == 0)) - return true; - if (cfg.bundling && !isLong && v.length == 1 - && indexOf(arg, v) >= 0) - { - //writeln("success"); - return true; - } - } - return false; -} - -private void setConfig(ref configuration cfg, config option) @safe pure nothrow @nogc -{ - final switch (option) - { - case config.caseSensitive: cfg.caseSensitive = true; break; - case config.caseInsensitive: cfg.caseSensitive = false; break; - case config.bundling: cfg.bundling = true; break; - case config.noBundling: cfg.bundling = false; break; - case config.passThrough: cfg.passThrough = true; break; - case config.noPassThrough: cfg.passThrough = false; break; - case config.required: cfg.required = true; break; - case config.stopOnFirstNonOption: - cfg.stopOnFirstNonOption = true; break; - case config.keepEndOfOptions: - cfg.keepEndOfOptions = true; break; - } -} - -@safe unittest -{ - import std.conv; - import std.math.operations : isClose; - - uint paranoid = 2; - string[] args = ["program.name", "--paranoid", "--paranoid", "--paranoid"]; - getopt(args, "paranoid+", ¶noid); - assert(paranoid == 5, to!(string)(paranoid)); - - enum Color { no, yes } - Color color; - args = ["program.name", "--color=yes",]; - getopt(args, "color", &color); - assert(color, to!(string)(color)); - - color = Color.no; - args = ["program.name", "--color", "yes",]; - getopt(args, "color", &color); - assert(color, to!(string)(color)); - - string data = "file.dat"; - int length = 24; - bool verbose = false; - args = ["program.name", "--length=5", "--file", "dat.file", "--verbose"]; - getopt( - args, - "length", &length, - "file", &data, - "verbose", &verbose); - assert(args.length == 1); - assert(data == "dat.file"); - assert(length == 5); - assert(verbose); - - // - string[] outputFiles; - args = ["program.name", "--output=myfile.txt", "--output", "yourfile.txt"]; - getopt(args, "output", &outputFiles); - assert(outputFiles.length == 2 - && outputFiles[0] == "myfile.txt" && outputFiles[1] == "yourfile.txt"); - - outputFiles = []; - arraySep = ","; - args = ["program.name", "--output", "myfile.txt,yourfile.txt"]; - getopt(args, "output", &outputFiles); - assert(outputFiles.length == 2 - && outputFiles[0] == "myfile.txt" && outputFiles[1] == "yourfile.txt"); - arraySep = ""; - - foreach (testArgs; - [["program.name", "--tune=alpha=0.5", "--tune", "beta=0.6"], - ["program.name", "--tune=alpha=0.5,beta=0.6"], - ["program.name", "--tune", "alpha=0.5,beta=0.6"]]) - { - arraySep = ","; - double[string] tuningParms; - getopt(testArgs, "tune", &tuningParms); - assert(testArgs.length == 1); - assert(tuningParms.length == 2); - assert(isClose(tuningParms["alpha"], 0.5)); - assert(isClose(tuningParms["beta"], 0.6)); - arraySep = ""; - } - - uint verbosityLevel = 1; - void myHandler(string option) - { - if (option == "quiet") - { - verbosityLevel = 0; - } - else - { - assert(option == "verbose"); - verbosityLevel = 2; - } - } - args = ["program.name", "--quiet"]; - getopt(args, "verbose", &myHandler, "quiet", &myHandler); - assert(verbosityLevel == 0); - args = ["program.name", "--verbose"]; - getopt(args, "verbose", &myHandler, "quiet", &myHandler); - assert(verbosityLevel == 2); - - verbosityLevel = 1; - void myHandler2(string option, string value) - { - assert(option == "verbose"); - verbosityLevel = 2; - } - args = ["program.name", "--verbose", "2"]; - getopt(args, "verbose", &myHandler2); - assert(verbosityLevel == 2); - - verbosityLevel = 1; - void myHandler3() - { - verbosityLevel = 2; - } - args = ["program.name", "--verbose"]; - getopt(args, "verbose", &myHandler3); - assert(verbosityLevel == 2); - - bool foo, bar; - args = ["program.name", "--foo", "--bAr"]; - getopt(args, - std.getopt.config.caseSensitive, - std.getopt.config.passThrough, - "foo", &foo, - "bar", &bar); - assert(args[1] == "--bAr"); - - // test stopOnFirstNonOption - - args = ["program.name", "--foo", "nonoption", "--bar"]; - foo = bar = false; - getopt(args, - std.getopt.config.stopOnFirstNonOption, - "foo", &foo, - "bar", &bar); - assert(foo && !bar && args[1] == "nonoption" && args[2] == "--bar"); - - args = ["program.name", "--foo", "nonoption", "--zab"]; - foo = bar = false; - getopt(args, - std.getopt.config.stopOnFirstNonOption, - "foo", &foo, - "bar", &bar); - assert(foo && !bar && args[1] == "nonoption" && args[2] == "--zab"); - - args = ["program.name", "--fb1", "--fb2=true", "--tb1=false"]; - bool fb1, fb2; - bool tb1 = true; - getopt(args, "fb1", &fb1, "fb2", &fb2, "tb1", &tb1); - assert(fb1 && fb2 && !tb1); - - // test keepEndOfOptions - - args = ["program.name", "--foo", "nonoption", "--bar", "--", "--baz"]; - getopt(args, - std.getopt.config.keepEndOfOptions, - "foo", &foo, - "bar", &bar); - assert(args == ["program.name", "nonoption", "--", "--baz"]); - - // Ensure old behavior without the keepEndOfOptions - - args = ["program.name", "--foo", "nonoption", "--bar", "--", "--baz"]; - getopt(args, - "foo", &foo, - "bar", &bar); - assert(args == ["program.name", "nonoption", "--baz"]); - - // test function callbacks - - static class MyEx : Exception - { - this() { super(""); } - this(string option) { this(); this.option = option; } - this(string option, string value) { this(option); this.value = value; } - - string option; - string value; - } - - static void myStaticHandler1() { throw new MyEx(); } - args = ["program.name", "--verbose"]; - try { getopt(args, "verbose", &myStaticHandler1); assert(0); } - catch (MyEx ex) { assert(ex.option is null && ex.value is null); } - - static void myStaticHandler2(string option) { throw new MyEx(option); } - args = ["program.name", "--verbose"]; - try { getopt(args, "verbose", &myStaticHandler2); assert(0); } - catch (MyEx ex) { assert(ex.option == "verbose" && ex.value is null); } - - static void myStaticHandler3(string option, string value) { throw new MyEx(option, value); } - args = ["program.name", "--verbose", "2"]; - try { getopt(args, "verbose", &myStaticHandler3); assert(0); } - catch (MyEx ex) { assert(ex.option == "verbose" && ex.value == "2"); } - - // check that GetOptException is thrown if the value is missing - args = ["program.name", "--verbose"]; - try { getopt(args, "verbose", &myStaticHandler3); assert(0); } - catch (GetOptException e) {} - catch (Exception e) { assert(0); } -} - -@safe unittest // @safe std.getopt.config option use -{ - long x = 0; - string[] args = ["program", "--inc-x", "--inc-x"]; - getopt(args, - std.getopt.config.caseSensitive, - "inc-x", "Add one to x", delegate void() { x++; }); - assert(x == 2); -} - -// https://issues.dlang.org/show_bug.cgi?id=2142 -@safe unittest -{ - bool f_linenum, f_filename; - string[] args = [ "", "-nl" ]; - getopt - ( - args, - std.getopt.config.bundling, - //std.getopt.config.caseSensitive, - "linenum|l", &f_linenum, - "filename|n", &f_filename - ); - assert(f_linenum); - assert(f_filename); -} - -// https://issues.dlang.org/show_bug.cgi?id=6887 -@safe unittest -{ - string[] p; - string[] args = ["", "-pa"]; - getopt(args, "p", &p); - assert(p.length == 1); - assert(p[0] == "a"); -} - -// https://issues.dlang.org/show_bug.cgi?id=6888 -@safe unittest -{ - int[string] foo; - auto args = ["", "-t", "a=1"]; - getopt(args, "t", &foo); - assert(foo == ["a":1]); -} - -// https://issues.dlang.org/show_bug.cgi?id=9583 -@safe unittest -{ - int opt; - auto args = ["prog", "--opt=123", "--", "--a", "--b", "--c"]; - getopt(args, "opt", &opt); - assert(args == ["prog", "--a", "--b", "--c"]); -} - -@safe unittest -{ - string foo, bar; - auto args = ["prog", "-thello", "-dbar=baz"]; - getopt(args, "t", &foo, "d", &bar); - assert(foo == "hello"); - assert(bar == "bar=baz"); - - // From https://issues.dlang.org/show_bug.cgi?id=5762 - string a; - args = ["prog", "-a-0x12"]; - getopt(args, config.bundling, "a|addr", &a); - assert(a == "-0x12", a); - args = ["prog", "--addr=-0x12"]; - getopt(args, config.bundling, "a|addr", &a); - assert(a == "-0x12"); - - // From https://issues.dlang.org/show_bug.cgi?id=11764 - args = ["main", "-test"]; - bool opt; - args.getopt(config.passThrough, "opt", &opt); - assert(args == ["main", "-test"]); - - // From https://issues.dlang.org/show_bug.cgi?id=15220 - args = ["main", "-o=str"]; - string o; - args.getopt("o", &o); - assert(o == "str"); - - args = ["main", "-o=str"]; - o = null; - args.getopt(config.bundling, "o", &o); - assert(o == "str"); -} - -// https://issues.dlang.org/show_bug.cgi?id=5228 -@safe unittest -{ - import std.conv; - import std.exception; - - auto args = ["prog", "--foo=bar"]; - int abc; - assertThrown!GetOptException(getopt(args, "abc", &abc)); - - args = ["prog", "--abc=string"]; - assertThrown!ConvException(getopt(args, "abc", &abc)); -} - -// https://issues.dlang.org/show_bug.cgi?id=7693 -@safe unittest -{ - import std.exception; - - enum Foo { - bar, - baz - } - - auto args = ["prog", "--foo=barZZZ"]; - Foo foo; - assertThrown(getopt(args, "foo", &foo)); - args = ["prog", "--foo=bar"]; - assertNotThrown(getopt(args, "foo", &foo)); - args = ["prog", "--foo", "barZZZ"]; - assertThrown(getopt(args, "foo", &foo)); - args = ["prog", "--foo", "baz"]; - assertNotThrown(getopt(args, "foo", &foo)); -} - -// Same as https://issues.dlang.org/show_bug.cgi?id=7693 only for `bool` -@safe unittest -{ - import std.exception; - - auto args = ["prog", "--foo=truefoobar"]; - bool foo; - assertThrown(getopt(args, "foo", &foo)); - args = ["prog", "--foo"]; - getopt(args, "foo", &foo); - assert(foo); -} - -@safe unittest -{ - bool foo; - auto args = ["prog", "--foo"]; - getopt(args, "foo", &foo); - assert(foo); -} - -@safe unittest -{ - bool foo; - bool bar; - auto args = ["prog", "--foo", "-b"]; - getopt(args, config.caseInsensitive,"foo|f", "Some foo", &foo, - config.caseSensitive, "bar|b", "Some bar", &bar); - assert(foo); - assert(bar); -} - -@safe unittest -{ - bool foo; - bool bar; - auto args = ["prog", "-b", "--foo", "-z"]; - getopt(args, config.caseInsensitive, config.required, "foo|f", "Some foo", - &foo, config.caseSensitive, "bar|b", "Some bar", &bar, - config.passThrough); - assert(foo); - assert(bar); -} - -@safe unittest -{ - import std.exception; - - bool foo; - bool bar; - auto args = ["prog", "-b", "-z"]; - assertThrown(getopt(args, config.caseInsensitive, config.required, "foo|f", - "Some foo", &foo, config.caseSensitive, "bar|b", "Some bar", &bar, - config.passThrough)); -} - -@safe unittest -{ - import std.exception; - - bool foo; - bool bar; - auto args = ["prog", "--foo", "-z"]; - assertNotThrown(getopt(args, config.caseInsensitive, config.required, - "foo|f", "Some foo", &foo, config.caseSensitive, "bar|b", "Some bar", - &bar, config.passThrough)); - assert(foo); - assert(!bar); -} - -@safe unittest -{ - bool foo; - auto args = ["prog", "-f"]; - auto r = getopt(args, config.caseInsensitive, "help|f", "Some foo", &foo); - assert(foo); - assert(!r.helpWanted); -} - -@safe unittest // implicit help option without config.passThrough -{ - string[] args = ["program", "--help"]; - auto r = getopt(args); - assert(r.helpWanted); -} - -// std.getopt: implicit help option breaks the next argument -// https://issues.dlang.org/show_bug.cgi?id=13316 -@safe unittest -{ - string[] args = ["program", "--help", "--", "something"]; - getopt(args); - assert(args == ["program", "something"]); - - args = ["program", "--help", "--"]; - getopt(args); - assert(args == ["program"]); - - bool b; - args = ["program", "--help", "nonoption", "--option"]; - getopt(args, config.stopOnFirstNonOption, "option", &b); - assert(args == ["program", "nonoption", "--option"]); -} - -// std.getopt: endOfOptions broken when it doesn't look like an option -// https://issues.dlang.org/show_bug.cgi?id=13317 -@safe unittest -{ - auto endOfOptionsBackup = endOfOptions; - scope(exit) endOfOptions = endOfOptionsBackup; - endOfOptions = "endofoptions"; - string[] args = ["program", "endofoptions", "--option"]; - bool b = false; - getopt(args, "option", &b); - assert(!b); - assert(args == ["program", "--option"]); -} - -// make std.getopt ready for DIP 1000 -// https://issues.dlang.org/show_bug.cgi?id=20480 -@safe unittest -{ - string[] args = ["test", "--foo", "42", "--bar", "BAR"]; - int foo; - string bar; - getopt(args, "foo", &foo, "bar", "bar help", &bar); - assert(foo == 42); - assert(bar == "BAR"); -} - -/** This function prints the passed `Option`s and text in an aligned manner on `stdout`. - -The passed text will be printed first, followed by a newline, then the short -and long version of every option will be printed. The short and long version -will be aligned to the longest option of every `Option` passed. If the option -is required, then "Required:" will be printed after the long version of the -`Option`. If a help message is present it will be printed next. The format is -illustrated by this code: - ------------- -foreach (it; opt) -{ - writefln("%*s %*s%s%s", lengthOfLongestShortOption, it.optShort, - lengthOfLongestLongOption, it.optLong, - it.required ? " Required: " : " ", it.help); -} ------------- - -Params: - text = The text to printed at the beginning of the help output. - opt = The `Option` extracted from the `getopt` parameter. -*/ -void defaultGetoptPrinter(string text, Option[] opt) @safe -{ - import std.stdio : stdout; - // stdout global __gshared is trusted with a locked text writer - auto w = (() @trusted => stdout.lockingTextWriter())(); - - defaultGetoptFormatter(w, text, opt); -} - -/** This function writes the passed text and `Option` into an output range -in the manner described in the documentation of function -`defaultGetoptPrinter`, unless the style option is used. - -Params: - output = The output range used to write the help information. - text = The text to print at the beginning of the help output. - opt = The `Option` extracted from the `getopt` parameter. - style = The manner in which to display the output of each `Option.` -*/ -void defaultGetoptFormatter(Output)(Output output, string text, Option[] opt, string style = "%*s %*s%*s%s\n") -{ - import std.algorithm.comparison : min, max; - import std.format.write : formattedWrite; - - output.formattedWrite("%s\n", text); - - size_t ls, ll; - bool hasRequired = false; - foreach (it; opt) - { - ls = max(ls, it.optShort.length); - ll = max(ll, it.optLong.length); - - hasRequired = hasRequired || it.required; - } - - string re = " Required: "; - - foreach (it; opt) - { - output.formattedWrite(style, ls, it.optShort, ll, it.optLong, - hasRequired ? re.length : 1, it.required ? re : " ", it.help); - } -} - -@safe unittest -{ - import std.conv; - - import std.array; - import std.string; - bool a; - auto args = ["prog", "--foo"]; - auto t = getopt(args, "foo|f", "Help", &a); - string s; - auto app = appender!string(); - defaultGetoptFormatter(app, "Some Text", t.options); - - string helpMsg = app.data; - //writeln(helpMsg); - assert(helpMsg.length); - assert(helpMsg.count("\n") == 3, to!string(helpMsg.count("\n")) ~ " " - ~ helpMsg); - assert(helpMsg.indexOf("--foo") != -1); - assert(helpMsg.indexOf("-f") != -1); - assert(helpMsg.indexOf("-h") != -1); - assert(helpMsg.indexOf("--help") != -1); - assert(helpMsg.indexOf("Help") != -1); - - string wanted = "Some Text\n-f --foo Help\n-h --help This help " - ~ "information.\n"; - assert(wanted == helpMsg); -} - -@safe unittest -{ - import std.array ; - import std.conv; - import std.string; - bool a; - auto args = ["prog", "--foo"]; - auto t = getopt(args, config.required, "foo|f", "Help", &a); - string s; - auto app = appender!string(); - defaultGetoptFormatter(app, "Some Text", t.options); - - string helpMsg = app.data; - //writeln(helpMsg); - assert(helpMsg.length); - assert(helpMsg.count("\n") == 3, to!string(helpMsg.count("\n")) ~ " " - ~ helpMsg); - assert(helpMsg.indexOf("Required:") != -1); - assert(helpMsg.indexOf("--foo") != -1); - assert(helpMsg.indexOf("-f") != -1); - assert(helpMsg.indexOf("-h") != -1); - assert(helpMsg.indexOf("--help") != -1); - assert(helpMsg.indexOf("Help") != -1); - - string wanted = "Some Text\n-f --foo Required: Help\n-h --help " - ~ " This help information.\n"; - assert(wanted == helpMsg, helpMsg ~ wanted); -} - -// https://issues.dlang.org/show_bug.cgi?id=14724 -@safe unittest -{ - bool a; - auto args = ["prog", "--help"]; - GetoptResult rslt; - try - { - rslt = getopt(args, config.required, "foo|f", "bool a", &a); - } - catch (Exception e) - { - enum errorMsg = "If the request for help was passed required options" ~ - "must not be set."; - assert(false, errorMsg); - } - - assert(rslt.helpWanted); -} - -// throw on duplicate options -@system unittest -{ - import core.exception : AssertError; - import std.exception : assertNotThrown, assertThrown; - auto args = ["prog", "--abc", "1"]; - int abc, def; - assertThrown!AssertError(getopt(args, "abc", &abc, "abc", &abc)); - assertThrown!AssertError(getopt(args, "abc|a", &abc, "def|a", &def)); - assertNotThrown!AssertError(getopt(args, "abc", &abc, "def", &def)); - - // https://issues.dlang.org/show_bug.cgi?id=23940 - assertThrown!AssertError(getopt(args, - "abc", &abc, "ABC", &def)); - assertThrown!AssertError(getopt(args, config.caseInsensitive, - "abc", &abc, "ABC", &def)); - assertNotThrown!AssertError(getopt(args, config.caseSensitive, - "abc", &abc, "ABC", &def)); -} - -// https://issues.dlang.org/show_bug.cgi?id=17327 repeated option use -@safe unittest -{ - long num = 0; - - string[] args = ["program", "--num", "3"]; - getopt(args, "n|num", &num); - assert(num == 3); - - args = ["program", "--num", "3", "--num", "5"]; - getopt(args, "n|num", &num); - assert(num == 5); - - args = ["program", "--n", "3", "--num", "5", "-n", "-7"]; - getopt(args, "n|num", &num); - assert(num == -7); - - void add1() { num++; } - void add2(string option) { num += 2; } - void addN(string option, string value) - { - import std.conv : to; - num += value.to!long; - } - - num = 0; - args = ["program", "--add1", "--add2", "--add1", "--add", "5", "--add2", "--add", "10"]; - getopt(args, - "add1", "Add 1 to num", &add1, - "add2", "Add 2 to num", &add2, - "add", "Add N to num", &addN,); - assert(num == 21); - - bool flag = false; - args = ["program", "--flag"]; - getopt(args, "f|flag", "Boolean", &flag); - assert(flag); - - flag = false; - args = ["program", "-f", "-f"]; - getopt(args, "f|flag", "Boolean", &flag); - assert(flag); - - flag = false; - args = ["program", "--flag=true", "--flag=false"]; - getopt(args, "f|flag", "Boolean", &flag); - assert(!flag); - - flag = false; - args = ["program", "--flag=true", "--flag=false", "-f"]; - getopt(args, "f|flag", "Boolean", &flag); - assert(flag); -} - -@system unittest // Delegates as callbacks -{ - alias TwoArgOptionHandler = void delegate(string option, string value) @safe; - - TwoArgOptionHandler makeAddNHandler(ref long dest) - { - void addN(ref long dest, string n) - { - import std.conv : to; - dest += n.to!long; - } - - return (option, value) => addN(dest, value); - } - - long x = 0; - long y = 0; - - string[] args = - ["program", "--x-plus-1", "--x-plus-1", "--x-plus-5", "--x-plus-n", "10", - "--y-plus-n", "25", "--y-plus-7", "--y-plus-n", "15", "--y-plus-3"]; - - getopt(args, - "x-plus-1", "Add one to x", delegate void() { x += 1; }, - "x-plus-5", "Add five to x", delegate void(string option) { x += 5; }, - "x-plus-n", "Add NUM to x", makeAddNHandler(x), - "y-plus-7", "Add seven to y", delegate void() { y += 7; }, - "y-plus-3", "Add three to y", delegate void(string option) { y += 3; }, - "y-plus-n", "Add NUM to x", makeAddNHandler(y),); - - assert(x == 17); - assert(y == 50); -} - -// Hyphens at the start of option values; -// https://issues.dlang.org/show_bug.cgi?id=17650 -@safe unittest -{ - auto args = ["program", "-m", "-5", "-n", "-50", "-c", "-", "-f", "-"]; - - int m; - int n; - char c; - string f; - - getopt(args, - "m|mm", "integer", &m, - "n|nn", "integer", &n, - "c|cc", "character", &c, - "f|file", "filename or hyphen for stdin", &f); - - assert(m == -5); - assert(n == -50); - assert(c == '-'); - assert(f == "-"); -} - -// Hyphen at the option value; -// https://issues.dlang.org/show_bug.cgi?id=22394 -@safe unittest -{ - auto args = ["program", "-"]; - - getopt(args); - - assert(args == ["program", "-"]); -} - -@safe unittest -{ - import std.conv; - - import std.array; - import std.string; - bool a; - auto args = ["prog", "--foo"]; - auto t = getopt(args, "foo|f", "Help", &a); - string s; - auto app = appender!string(); - defaultGetoptFormatter(app, "Some Text", t.options, "\t\t%*s %*s%*s\n%s\n"); - - string helpMsg = app.data; - //writeln(helpMsg); - assert(helpMsg.length); - assert(helpMsg.count("\n") == 5, to!string(helpMsg.count("\n")) ~ " " - ~ helpMsg); - assert(helpMsg.indexOf("--foo") != -1); - assert(helpMsg.indexOf("-f") != -1); - assert(helpMsg.indexOf("-h") != -1); - assert(helpMsg.indexOf("--help") != -1); - assert(helpMsg.indexOf("Help") != -1); - - string wanted = "Some Text\n\t\t-f --foo \nHelp\n\t\t-h --help \nThis help " - ~ "information.\n"; - assert(wanted == helpMsg); -} diff --git a/phobos/std/int128.d b/phobos/std/int128.d deleted file mode 100644 index 9289544..0000000 --- a/phobos/std/int128.d +++ /dev/null @@ -1,653 +0,0 @@ -// Written in the D programming language -/** - * Implements a signed 128 bit integer type. - * - Author: Walter Bright - Copyright: Copyright (c) 2022, D Language Foundation - License: $(HTTP boost.org/LICENSE_1_0.txt, Boost License 1.0) - Source: $(PHOBOSSRC std/int128.d) - */ -module std.int128; - -private import core.int128; - - -/*********************************** - * 128 bit signed integer type. - */ - -public struct Int128 -{ - @safe pure nothrow @nogc - { - Cent data; /// core.int128.Cent - - /**************** - * Construct an `Int128` from a `long` value. - * The upper 64 bits are formed by sign extension. - * Params: - * lo = signed lower 64 bits - */ - this(long lo) - { - data.lo = lo; - data.hi = lo < 0 ? ~0L : 0; - } - - /**************** - * Construct an `Int128` from a `ulong` value. - * The upper 64 bits are set to zero. - * Params: - * lo = unsigned lower 64 bits - */ - this(ulong lo) - { - data.lo = lo; - data.hi = 0; - } - - /**************** - * Construct an `Int128` from a `long` value. - * Params: - * hi = upper 64 bits - * lo = lower 64 bits - */ - this(long hi, long lo) - { - data.hi = hi; - data.lo = lo; - } - - /******************** - * Construct an `Int128` from a `Cent`. - * Params: - * data = Cent data - */ - this(Cent data) - { - this.data = data; - } - - /******************** - * Returns: hash value for Int128 - */ - size_t toHash() const - { - return cast(size_t)((data.lo & 0xFFFF_FFFF) + (data.hi & 0xFFFF_FFFF) + (data.lo >> 32) + (data.hi >> 32)); - } - - /************************ - * Compare for equality - * Params: lo = signed value to compare with - * Returns: true if Int128 equals value - */ - bool opEquals(long lo) const - { - return data.lo == lo && data.hi == (lo >> 63); - } - - /************************ - * Compare for equality - * Params: lo = unsigned value to compare with - * Returns: true if Int128 equals value - */ - bool opEquals(ulong lo) const - { - return data.hi == 0 && data.lo == lo; - } - - /************************ - * Compare for equality - * Params: op2 = value to compare with - * Returns: true if Int128 equals value - */ - bool opEquals(Int128 op2) const - { - return data.hi == op2.data.hi && data.lo == op2.data.lo; - } - - /** Support unary arithmentic operator + - * Params: op = "+" - * Returns: lvalue of result - */ - Int128 opUnary(string op)() const - if (op == "+") - { - return this; - } - - /** Support unary arithmentic operator - ~ - * Params: op = "-", "~" - * Returns: lvalue of result - */ - Int128 opUnary(string op)() const - if (op == "-" || op == "~") - { - static if (op == "-") - return Int128(neg(this.data)); - else static if (op == "~") - return Int128(com(this.data)); - } - - /** Support unary arithmentic operator ++ -- - * Params: op = "++", "--" - * Returns: lvalue of result - */ - Int128 opUnary(string op)() - if (op == "++" || op == "--") - { - static if (op == "++") - this.data = inc(this.data); - else static if (op == "--") - this.data = dec(this.data); - else - static assert(0, op); - return this; - } - - /** Support casting to a bool - * Params: T = bool - * Returns: true if value is not zero - */ - bool opCast(T : bool)() const - { - return tst(this.data); - } - } // @safe pure nothrow @nogc - - /** Support casting to an integral type - * Params: T = integral type - * Returns: low bits of value reinterpreted as T - */ - T opCast(T : long)() const - if (is(byte : T)) - { - return cast(T) this.data.lo; - } - - /// - @safe unittest - { - const Int128 a = Int128(0xffff_ffff_ffff_ffffL, 0x0123_4567_89ab_cdefL); - assert(cast(long) a == 0x0123_4567_89ab_cdefL); - assert(cast(int) a == 0x89ab_cdef); - assert(cast(byte) a == cast(byte) 0xef); - } - - /** Support casting to floating point type - * Params: T = floating point type - * Returns: value cast to floating point with environment-dependent - * rounding if the value is not exactly representable - */ - T opCast(T : real)() const - { - import core.math : ldexp; - if (cast(long) this.data.hi >= 0) - return ldexp(cast(T) this.data.hi, 64) + this.data.lo; - else - { - const absData = neg(this.data); - return -ldexp(cast(T) absData.hi, 64) - absData.lo; - } - } - - /// - @safe unittest - { - const Int128 a = Int128(-1L << 60); - assert(cast(double) a == -(2.0 ^^ 60)); - assert(cast(double) (a * a) == 2.0 ^^ 120); - } - - /** Support binary arithmetic operators + - * / % & | ^ << >> >>> - * Params: - * op = one of the arithmetic binary operators - * op2 = second operand - * Returns: value after the operation is applied - */ - Int128 opBinary(string op)(Int128 op2) const - if (op == "+" || op == "-" || - op == "*" || op == "/" || op == "%" || - op == "&" || op == "|" || op == "^") - { - static if (op == "+") - return Int128(add(this.data, op2.data)); - else static if (op == "-") - return Int128(sub(this.data, op2.data)); - else static if (op == "*") - return Int128(mul(this.data, op2.data)); - else static if (op == "/") - return Int128(div(this.data, op2.data)); - else static if (op == "%") - { - Cent modulus; - divmod(this.data, op2.data, modulus); - return Int128(modulus); - } - else static if (op == "&") - return Int128(and(this.data, op2.data)); - else static if (op == "|") - return Int128(or(this.data, op2.data)); - else static if (op == "^") - return Int128(xor(this.data, op2.data)); - else - static assert(0, "wrong op value"); - } - - /// ditto - Int128 opBinary(string op, Int)(const Int op2) const - if ((op == "+" || op == "-" || - op == "*" || op == "/" || op == "%" || - op == "&" || op == "|" || op == "^") && - is(Int : long) && __traits(isIntegral, Int)) - { - static if (__traits(isUnsigned, Int)) - return mixin("this " ~ op ~ " Int128(0, op2)"); - else - return mixin("this " ~ op ~ " Int128((cast(long) op2) >> 63 , op2)"); - } - - /// ditto - Int128 opBinary(string op, IntLike)(auto ref IntLike op2) const - if ((op == "+" || op == "-" || - op == "*" || op == "/" || op == "%" || - op == "&" || op == "|" || op == "^") && - is(IntLike : long) && !__traits(isIntegral, IntLike)) - { - return opBinary!(op)(__traits(getMember, op2, __traits(getAliasThis, IntLike)[0])); - } - - /// ditto - Int128 opBinaryRight(string op, Int)(const Int op2) const - if ((op == "+" || op == "-" || - op == "*" || op == "/" || op == "%" || - op == "&" || op == "|" || op == "^") && - is(Int : long) && __traits(isIntegral, Int)) - { - static if (__traits(isUnsigned, Int)) - mixin("return Int128(0, op2) " ~ op ~ " this;"); - else - mixin("return Int128((cast(long) op2) >> 63, op2) " ~ op ~ " this;"); - } - - /// ditto - Int128 opBinaryRight(string op, IntLike)(auto ref IntLike op2) const - if ((op == "+" || op == "-" || - op == "*" || op == "/" || op == "%" || - op == "&" || op == "|" || op == "^") && - is(IntLike : long) && !__traits(isIntegral, IntLike)) - { - return opBinaryRight!(op)(__traits(getMember, op2, __traits(getAliasThis, IntLike)[0])); - } - - /// ditto - Int128 opBinary(string op)(long op2) const - if (op == "<<") - { - return Int128(shl(this.data, cast(uint) op2)); - } - - /// ditto - Int128 opBinary(string op)(long op2) const - if (op == ">>") - { - return Int128(sar(this.data, cast(uint) op2)); - } - - /// ditto - Int128 opBinary(string op)(long op2) const - if (op == ">>>") - { - return Int128(shr(this.data, cast(uint) op2)); - } - - /** arithmetic assignment operators += -= *= /= %= &= |= ^= <<= >>= >>>= - * Params: op = one of +, -, etc. - * op2 = second operand - * Returns: lvalue of updated left operand - */ - ref Int128 opOpAssign(string op)(Int128 op2) - if (op == "+" || op == "-" || - op == "*" || op == "/" || op == "%" || - op == "&" || op == "|" || op == "^" || - op == "<<" || op == ">>" || op == ">>>") - { - mixin("this = this " ~ op ~ " op2;"); - return this; - } - - /// ditto - ref Int128 opOpAssign(string op, Int)(auto ref Int op2) - if ((op == "+" || op == "-" || - op == "*" || op == "/" || op == "%" || - op == "&" || op == "|" || op == "^" || - op == "<<" || op == ">>" || op == ">>>") - && is(Int : long)) - { - mixin("this = this " ~ op ~ " op2;"); - return this; - } - - /** support arithmentic comparison operators < <= > >= - * Params: op2 = right hand operand - * Returns: -1 for less than, 0 for equals, 1 for greater than - */ - int opCmp(Int128 op2) const @nogc nothrow pure @safe - { - return this == op2 ? 0 : gt(this.data, op2.data) * 2 - 1; - } - - /// ditto - int opCmp(Int)(const Int op2) const @nogc nothrow pure @safe - if (is(Int : long) && __traits(isIntegral, Int)) - { - static if (__traits(isUnsigned, Int)) - return opCmp(Int128(0, op2)); - else - return opCmp(Int128((cast(long) op2) >> 63, op2)); - } - - /// ditto - int opCmp(IntLike)(auto ref IntLike op2) const - if (is(IntLike : long) && !__traits(isIntegral, IntLike)) - { - return opCmp(__traits(getMember, op2, __traits(getAliasThis, IntLike)[0])); - } - - /** - * Formats `Int128` with either `%d`, `%x`, `%X`, or `%s` (same as `%d`). - * - * Params: - * sink = $(REF_ALTTEXT Output range, isOutputRange, std, range, primitives) - * to write to. - * fmt = A $(REF FormatSpec, std,format) which controls how the number - * is displayed. - * - * Throws: - * $(REF FormatException, std,format) if the format specifier is - * not one of 'd', 'x', 'X', 's'. - * - * See_Also: $(REF formatValue, std,format) - */ - void toString(Writer, FormatSpec)(scope ref Writer sink, scope const ref FormatSpec fmt) const - { - import std.range.primitives : put; - import std.format : FormatException, Fmt = FormatSpec; - - static if (is(FormatSpec == Fmt!Char, Char)) - { - // Puts "Char" into scope if the pattern matches. - } - static assert(is(Char), - "Expecting `FormatSpec` to be instantiation of `std.format.FormatSpec`"); - - Char[39] buf = void; - size_t bufStart = void; - Char signChar = 0; - if (fmt.spec == 'd' || fmt.spec == 's') - { - const bool isNeg = 0 > cast(long) this.data.hi; - Cent val = isNeg ? neg(this.data) : this.data; - immutable Cent radix = { lo: 10, hi: 0 }; - Cent modulus; - bufStart = buf.length; - do - { - uint x = void; - if (ugt(radix, val)) - { - x = cast(uint) val.lo; - val = Cent(0, 0); - } - else - { - val = udivmod(val, radix, modulus); - x = cast(uint) modulus.lo; - } - buf[--bufStart] = cast(Char) ('0' + x); - } while (tst(val)); - if (isNeg) - signChar = '-'; - else if (fmt.flPlus) - signChar = '+'; - else if (fmt.flSpace) - signChar = ' '; - } - else if (fmt.spec == 'x' || fmt.spec == 'X') - { - immutable hexDigits = fmt.spec == 'X' ? "0123456789ABCDEF" : "0123456789abcdef"; - ulong a = data.lo; - bufStart = buf.length - 1; - size_t penPos = buf.length - 1; - do - { - if ((buf[penPos] = hexDigits[0xF & cast(uint) a]) != '0') - bufStart = penPos; - a >>>= 4; - } while (--penPos >= buf.length - 16); - a = data.hi; - do - { - if ((buf[penPos] = hexDigits[0xF & cast(uint) a]) != '0') - bufStart = penPos; - a >>>= 4; - } while (--penPos >= buf.length - 32); - } - else - { - throw new FormatException("Format specifier not understood: %" ~ fmt.spec); - } - - const minw = (buf.length - bufStart) + int(signChar != 0); - const maxw = minw < fmt.width ? fmt.width : minw; - const difw = maxw - minw; - - static void putRepeatedChars(Char c)(scope ref Writer sink, size_t n) - { - static immutable Char[8] array = [c, c, c, c, c, c, c, c]; - foreach (_; 0 .. n / 8) - put(sink, array[0 .. 8]); - if (n & 7) - put(sink, array[0 .. n & 7]); - } - - if (!fmt.flDash && !fmt.flZero && difw) - putRepeatedChars!' '(sink, difw); - - if (signChar) - { - Char[1] signCharBuf = signChar; - put(sink, signCharBuf[0 .. 1]); - } - - if (!fmt.flDash && fmt.flZero && difw) - putRepeatedChars!'0'(sink, difw); - - put(sink, buf[bufStart .. $]); - - if (fmt.flDash && difw) - putRepeatedChars!' '(sink, difw); - } - - /** - `toString` is rarely directly invoked; the usual way of using it is via - $(REF format, std, format): - */ - @safe unittest - { - import std.format : format; - - assert(format("%s", Int128.max) == "170141183460469231731687303715884105727"); - assert(format("%s", Int128.min) == "-170141183460469231731687303715884105728"); - assert(format("%x", Int128.max) == "7fffffffffffffffffffffffffffffff"); - assert(format("%X", Int128.max) == "7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF"); - assert(format("%032X", Int128(123L)) == "0000000000000000000000000000007B"); - assert(format("%+ 40d", Int128(123L)) == " +123"); - assert(format("%+-40d", Int128(123L)) == "+123 "); - } - - /// Also can format as `wchar` or `dchar`. - @safe unittest - { - import std.conv : to; - - assert(to!wstring(Int128.max) == "170141183460469231731687303715884105727"w); - assert(to!dstring(Int128.max) == "170141183460469231731687303715884105727"d); - } - - enum min = Int128(long.min, 0); /// minimum value - enum max = Int128(long.max, ulong.max); /// maximum value -} - -/********************************************* Tests ************************************/ - -version (unittest) -{ -import core.stdc.stdio; - -@trusted void print(Int128 c) -{ - printf("%lld, %lld\n", c.data.hi, c.data.lo); -} - -@trusted void printx(Int128 c) -{ - printf("%llx, %llx\n", c.data.hi, c.data.lo); -} -} - -/// Int128 tests -@safe pure nothrow @nogc -unittest -{ - Int128 c = Int128(5, 6); - assert(c == c); - assert(c == +c); - assert(c == - -c); - assert(~c == Int128(~5, ~6)); - ++c; - assert(c == Int128(5, 7)); - assert(--c == Int128(5, 6)); - assert(!!c); - assert(!Int128()); - - assert(c + Int128(10, 20) == Int128(15, 26)); - assert(c - Int128(1, 2) == Int128(4, 4)); - assert(c * Int128(100, 2) == Int128(610, 12)); - assert(c / Int128(3, 2) == Int128(0, 1)); - assert(c % Int128(3, 2) == Int128(2, 4)); - assert((c & Int128(3, 2)) == Int128(1, 2)); - assert((c | Int128(3, 2)) == Int128(7, 6)); - assert((c ^ Int128(3, 2)) == Int128(6, 4)); - - assert(c + 15 == Int128(5, 21)); - assert(c - 15 == Int128(4, -9)); - assert(c * 15 == Int128(75, 90)); - assert(c / 15 == Int128(0, 6148914691236517205)); - assert(c % 15 == Int128(0, 11)); - assert((c & 15) == Int128(0, 6)); - assert((c | 15) == Int128(5, 15)); - assert((c ^ 15) == Int128(5, 9)); - - assert(15 + c == Int128(5, 21)); - assert(15 - c == Int128(-5, 9)); - assert(15 * c == Int128(75, 90)); - assert(15 / c == Int128(0, 0)); - assert(15 % c == Int128(0, 15)); - assert((15 & c) == Int128(0, 6)); - assert((15 | c) == Int128(5, 15)); - assert((15 ^ c) == Int128(5, 9)); - - assert(c << 1 == Int128(10, 12)); - assert(-c >> 1 == Int128(-3, 9223372036854775805)); - assert(-c >>> 1 == Int128(9223372036854775805, 9223372036854775805)); - - assert((c += 1) == Int128(5, 7)); - assert((c -= 1) == Int128(5, 6)); - assert((c += Int128(0, 1)) == Int128(5, 7)); - assert((c -= Int128(0, 1)) == Int128(5, 6)); - assert((c *= 2) == Int128(10, 12)); - assert((c /= 2) == Int128(5, 6)); - assert((c %= 2) == Int128()); - c += Int128(5, 6); - assert((c *= Int128(10, 20)) == Int128(160, 120)); - assert((c /= Int128(10, 20)) == Int128(0, 15)); - c += Int128(72, 0); - assert((c %= Int128(10, 20)) == Int128(1, -125)); - assert((c &= Int128(3, 20)) == Int128(1, 0)); - assert((c |= Int128(8, 2)) == Int128(9, 2)); - assert((c ^= Int128(8, 2)) == Int128(1, 0)); - c |= Int128(10, 5); - assert((c <<= 1) == Int128(11 * 2, 5 * 2)); - assert((c >>>= 1) == Int128(11, 5)); - c = Int128(long.min, long.min); - assert((c >>= 1) == Int128(long.min >> 1, cast(ulong) long.min >> 1)); - - assert(-Int128.min == Int128.min); - assert(Int128.max + 1 == Int128.min); - - c = Int128(5, 6); - assert(c < Int128(6, 5)); - assert(c > 10); - - c = Int128(-1UL); - assert(c == -1UL); - c = Int128(-1L); - assert(c == -1L); -} - -@system unittest -{ - alias Seq(T...) = T; - Int128 c = Int128(-1L); - assert(c.opCmp(-1L) == 0); - // To avoid regression calling opCmp with any integral type needs to - // work without the compiler complaining "opCmp called with argument - // X matches both <...>". - static foreach (Int; Seq!(long, int, short, byte, ulong, uint, ushort, ubyte, dchar, wchar, char)) - assert(c < Int.max); - static foreach (Int; Seq!(int, short, byte)) - assert(c.opCmp(Int(-1)) == 0); - assert(c < true); - // To avoid regression calling opCmp with any type that converts to an - // integral type through alias this needs to work regardless of whether - // the alias is safe/pure/nothrow/nogc and regardless of whether the - // type has a disabled postblit. - static struct Wrapped(T) - { - T value; - uint count; - T get() @system { ++count; return value; } // not const - alias get this; - @disable this(this); // no implicit copies - } - assert(c.opCmp(Wrapped!long(-1)) == 0); - auto w = Wrapped!ulong(ulong.max); - w.count++; // avoid invalid D-Scanner message that w could have been declared const - assert(c < w); - - const zero = Int128(0L); - const one = Int128(1L); - const neg_one = Int128(-1L); - const neg_two = Int128(-2L); - // Correct result with ulong.max: - assert(zero + ulong.max == ulong.max); - assert(one * ulong.max == ulong.max); - assert((neg_one & ulong.max) == ulong.max); - assert((zero | ulong.max) == ulong.max); - assert((zero ^ ulong.max) == ulong.max); - // Correct result with negative arguments: - assert(zero + -1L == -1L); - assert(neg_two * -3L == 6L); - assert(neg_two / -2L == 1L); - assert(neg_two % -2L == 0L); - assert((neg_one & -1L) == -1L); - assert((zero | -1L) == -1L); - assert((zero ^ -1L) == -1L); - // Ensure alias this still works. - { - Int128 a = zero; - assert((a ^= w) == ulong.max); - } - assert((Wrapped!long(-1L) + zero) == -1L); -} diff --git a/phobos/std/internal/attributes.d b/phobos/std/internal/attributes.d deleted file mode 100644 index 2405326..0000000 --- a/phobos/std/internal/attributes.d +++ /dev/null @@ -1,11 +0,0 @@ -module std.internal.attributes; - -/** -Used to annotate `unittest`s which need to be tested in a `-betterC` environment. - -Such `unittest`s will be compiled and executed without linking druntime in, with -a `__traits(getUnitTests, mixin(__MODULE__))` style test runner. -Note that just like any other `unittest` in phobos, they will also be compiled -and executed without `-betterC`. -*/ -package(std) enum betterC = 1; diff --git a/phobos/std/internal/cstring.d b/phobos/std/internal/cstring.d deleted file mode 100644 index b21f58d..0000000 --- a/phobos/std/internal/cstring.d +++ /dev/null @@ -1,312 +0,0 @@ -/** -Helper functions for working with $(I C strings). - -This module is intended to provide fast, safe and garbage free -way to work with $(I C strings). - -Copyright: Denis Shelomovskij 2013-2014 - -License: $(HTTP boost.org/LICENSE_1_0.txt, Boost License 1.0). - -Authors: Denis Shelomovskij - -Macros: -COREREF = $(HTTP dlang.org/phobos/core_$1.html#$2, `core.$1.$2`) -*/ -module std.internal.cstring; - -/// -@safe unittest -{ - version (Posix) - { - import core.stdc.stdlib : free; - import core.sys.posix.stdlib : setenv; - import std.exception : enforce; - - void setEnvironment(scope const(char)[] name, scope const(char)[] value) - { enforce(setenv(name.tempCString(), value.tempCString(), 1) != -1); } - } - - version (Windows) - { - import core.sys.windows.winbase : SetEnvironmentVariableW; - import std.exception : enforce; - - void setEnvironment(scope const(char)[] name, scope const(char)[] value) - { enforce(SetEnvironmentVariableW(name.tempCStringW(), value.tempCStringW())); } - } -} - -import std.range; -import std.traits; - -/** -Creates temporary 0-terminated $(I C string) with copy of passed text. - -Params: - To = character type of returned C string - str = string or input range to be converted - -Returns: - -The value returned is implicitly convertible to $(D const To*) and -has two properties: `ptr` to access $(I C string) as $(D const To*) -and `buffPtr` to access it as `To*`. - -The value returned can be indexed by [] to access it as an array. - -The temporary $(I C string) is valid unless returned object is destroyed. -Thus if returned object is assigned to a variable the temporary is -valid unless the variable goes out of scope. If returned object isn't -assigned to a variable it will be destroyed at the end of creating -primary expression. - -Implementation_note: -For small strings tempCString will use stack allocated buffer, -for large strings (approximately 250 characters and more) it will -allocate temporary one using C's `malloc`. - -Note: -This function is intended to be used in function call expression (like -`strlen(str.tempCString())`). Incorrect usage of this function may -lead to memory corruption. -See $(RED WARNING) in $(B Examples) section. -*/ - -auto tempCString(To = char, From)(scope From str) -if (isSomeChar!To && (isInputRange!From || isSomeString!From) && - isSomeChar!(ElementEncodingType!From)) -{ - alias CF = Unqual!(ElementEncodingType!From); - - auto res = TempCStringBuffer!To.trustedVoidInit(); // expensive to fill _buff[] - - // Note: res._ptr can't point to res._buff as structs are movable. - - // https://issues.dlang.org/show_bug.cgi?id=14980 - static if (isSomeString!From) - { - if (str is null) - { - res._length = 0; - res._ptr = null; - return res; - } - } - - // Use slice assignment if available. - static if (To.sizeof == CF.sizeof && is(typeof(res._buff[0 .. str.length] = str[]))) - { - if (str.length < res._buff.length) - { - res._buff[0 .. str.length] = str[]; - res._buff[str.length] = 0; - res._ptr = res.useStack; - } - else - { - import std.internal.memory : enforceMalloc; - if (false) - { - // This code is removed by the compiler but causes `@safe`ty - // to be inferred correctly. - CF[0] x; - x[] = str[0 .. 0]; - } - res._ptr = () @trusted { - auto p = cast(CF*) enforceMalloc((str.length + 1) * CF.sizeof); - p[0 .. str.length] = str[]; - p[str.length] = 0; - return cast(To*) p; - }(); - } - res._length = str.length; - return res; - } - else - { - static assert(!(isSomeString!From && CF.sizeof == To.sizeof), "Should be using slice assignment."); - To[] p = res._buff; - size_t i; - - size_t strLength; - static if (hasLength!From) - { - strLength = str.length; - } - import std.utf : byUTF; - static if (isSomeString!From) - auto r = cast(const(CF)[])str; // because inout(CF) causes problems with byUTF - else - alias r = str; - To[] heapBuffer; - foreach (const c; byUTF!(Unqual!To)(r)) - { - if (i + 1 == p.length) - { - if (heapBuffer is null) - heapBuffer = trustedReallocStack(p, strLength); - else - heapBuffer = trustedRealloc(heapBuffer); - p = heapBuffer; - } - p[i++] = c; - } - p[i] = 0; - res._length = i; - res._ptr = (heapBuffer is null ? res.useStack : &heapBuffer[0]); - return res; - } -} - -/// -nothrow @nogc @system unittest -{ - import core.stdc.string; - - string str = "abc"; - - // Intended usage - assert(strlen(str.tempCString()) == 3); - - // Correct usage - auto tmp = str.tempCString(); - assert(strlen(tmp) == 3); // or `tmp.ptr`, or `tmp.buffPtr` - - // $(RED WARNING): $(RED Incorrect usage) - auto pInvalid1 = str.tempCString().ptr; - const char* pInvalid2 = str.tempCString(); - // Both pointers refer to invalid memory here as - // returned values aren't assigned to a variable and - // both primary expressions are ended. -} - -@safe pure nothrow @nogc unittest -{ - static inout(C)[] arrayFor(C)(inout(C)* cstr) pure nothrow @nogc @trusted - { - assert(cstr); - size_t length = 0; - while (cstr[length]) - ++length; - return cstr[0 .. length]; - } - - assert(arrayFor("abc".tempCString()) == "abc"); - assert(arrayFor("abc"d.tempCString().ptr) == "abc"); - assert(arrayFor("abc".tempCString!wchar().buffPtr) == "abc"w); - - import std.utf : byChar, byWchar; - char[300] abc = 'a'; - assert(arrayFor(tempCString(abc[].byChar).buffPtr) == abc); - assert(arrayFor(tempCString(abc[].byWchar).buffPtr) == abc); - assert(tempCString(abc[].byChar)[] == abc); -} - -// https://issues.dlang.org/show_bug.cgi?id=14980 -pure nothrow @nogc @safe unittest -{ - const(char[]) str = null; - auto res = tempCString(str); - const char* ptr = res; - assert(ptr is null); -} - -version (Windows) -{ - import core.sys.windows.winnt : WCHAR; - alias tempCStringW = tempCString!(WCHAR, const(char)[]); -} - -private struct TempCStringBuffer(To = char) -{ -@trusted pure nothrow @nogc: - - @disable this(); - @disable this(this); - alias ptr this; /// implicitly covert to raw pointer - - @property inout(To)* buffPtr() return inout - { - return _ptr == useStack ? _buff.ptr : _ptr; - } - - @property const(To)* ptr() const - { - return buffPtr; - } - - const(To)[] opIndex() const pure - { - return buffPtr[0 .. _length]; - } - - ~this() - { - if (_ptr != useStack) - { - import core.memory : pureFree; - pureFree(_ptr); - } - } - -private: - enum To* useStack = () @trusted { return cast(To*) size_t.max; }(); - - To* _ptr; - size_t _length; // length of the string - version (StdUnittest) - // the 'small string optimization' - { - // smaller size to trigger reallocations. Padding is to account for - // unittest/non-unittest cross-compilation (to avoid corruption) - To[16 / To.sizeof] _buff; - To[(256 - 16) / To.sizeof] _unittest_pad; - } - else - { - To[256 / To.sizeof] _buff; // production size - } - - static TempCStringBuffer trustedVoidInit() { TempCStringBuffer res = void; return res; } -} - -private To[] trustedRealloc(To)(return scope To[] buf) - @trusted @nogc pure nothrow -{ - pragma(inline, false); // because it's rarely called - import std.internal.memory : enforceRealloc; - - const size_t newlen = buf.length * 3 / 2; - if (buf.length >= size_t.max / (2 * To.sizeof)) - { - version (D_Exceptions) - { - import core.exception : onOutOfMemoryError; - onOutOfMemoryError(); - } - else - { - assert(0, "Memory allocation failed"); - } - } - auto ptr = cast(To*) enforceRealloc(buf.ptr, newlen * To.sizeof); - return ptr[0 .. newlen]; - -} - -private To[] trustedReallocStack(To)(scope To[] buf, size_t strLength) - @trusted @nogc pure nothrow -{ - pragma(inline, false); // because it's rarely called - - import std.internal.memory : enforceMalloc; - - size_t newlen = buf.length * 3 / 2; - if (newlen <= strLength) - newlen = strLength + 1; // +1 for terminating 0 - auto ptr = cast(To*) enforceMalloc(newlen * To.sizeof); - ptr[0 .. buf.length] = buf[]; - return ptr[0 .. newlen]; -} diff --git a/phobos/std/internal/digest/sha_SSSE3.d b/phobos/std/internal/digest/sha_SSSE3.d deleted file mode 100644 index 707436b..0000000 --- a/phobos/std/internal/digest/sha_SSSE3.d +++ /dev/null @@ -1,802 +0,0 @@ -// Written in the D programming language. - -/** - * Computes SHA1 digests of arbitrary data, using an optimized algorithm with SSSE3 instructions. - * - * Authors: - * The general idea is described by Dean Gaudet. - * Another important observation is published by Max Locktyukhin. - * (Both implementations are public domain.) - * Translation to X86 and D by Kai Nacke - * - * References: - * $(LINK2 http://arctic.org/~dean/crypto/sha1.html) - * $(LINK2 http://software.intel.com/en-us/articles/improving-the-performance-of-the-secure-hash-algorithm-1/, Fast implementation of SHA1) - */ -module std.internal.digest.sha_SSSE3; - -version (D_InlineAsm_X86) -{ - version (D_PIC) {} // https://issues.dlang.org/show_bug.cgi?id=9378 - else - { - private version = USE_SSSE3; - private version = _32Bit; - } -} -else version (D_InlineAsm_X86_64) -{ - private version = USE_SSSE3; - private version = _64Bit; -} - -// LDC: misc. asm adaptations due to non-reversed extern(D) parameters! - -/* - * The idea is quite simple. The SHA-1 specification defines the following message schedule: - * W[i] = (W[i-3] ^ W[i-8] ^ W[i-14] ^ W[i-16]) rol 1 - * - * To employ SSE, simply write down the formula four times: - * W[i ] = (W[i-3] ^ W[i-8] ^ W[i-14] ^ W[i-16]) rol 1 - * W[i+1] = (W[i-2] ^ W[i-7] ^ W[i-13] ^ W[i-15]) rol 1 - * W[i+2] = (W[i-1] ^ W[i-6] ^ W[i-12] ^ W[i-14]) rol 1 - * W[i+3] = (W[i ] ^ W[i-5] ^ W[i-11] ^ W[i-13]) rol 1 - * The last formula requires value W[i] computed with the first formula. - * Because the xor operation and the rotate operation are commutative, we can replace the - * last formula with - * W[i+3] = ( 0 ^ W[i-5] ^ W[i-11] ^ W[i-13]) rol 1 - * and then calculate - * W[i+3] ^= W[i] rol 1 - * which unfortunately requires many additional operations. This approach was described by - * Dean Gaudet. - * - * Max Locktyukhin observed that - * W[i] = W[i-A] ^ W[i-B] - * is equivalent to - * W[i] = W[i-2*A] ^ W[i-2*B] - * (if the indices are still in valid ranges). Using this observation, the formula is - * translated to - * W[i] = (W[i-6] ^ W[i-16] ^ W[i-28] ^ W[i-32]) rol 2 - * Again, to employ SSE the formula is used four times. - * - * Later on, the expression W[i] + K(i) is used. (K(i) is the constant used in round i.) - * Once the 4 W[i] are calculated, we can also add the four K(i) values with one SSE instruction. - * - * The 32bit and 64bit implementations are almost identical. The main difference is that there - * are only 8 XMM registers in 32bit mode. Therefore, space on the stack is needed to save - * computed values. - */ - -version (USE_SSSE3) -{ - /* - * The general idea is to use the XMM registers as a sliding window over - * message schedule. XMM0 to XMM7 are used to store the last 64 byte of - * the message schedule. In 64 bit mode this is fine because of the number of - * registers. The main difference of the 32 bit code is that a part of the - * calculated message schedule is saved on the stack because 2 temporary - * registers are needed. - */ - - /* Number of message words we are precalculating. */ - private immutable int PRECALC_AHEAD = 16; - - /* T1 and T2 are used for intermediate results of computations. */ - private immutable string T1 = "EAX"; - private immutable string T2 = "EBX"; - - /* The registers used for the SHA-1 variables. */ - private immutable string A = "ECX"; - private immutable string B = "ESI"; - private immutable string C = "EDI"; - private immutable string D = "EBP"; - private immutable string E = "EDX"; - - /* */ - version (_32Bit) - { - private immutable string SP = "ESP"; - private immutable string STATE_PTR = "EAX"; - private immutable string BUFFER_PTR = "EBX"; - - // Control byte for shuffle instruction (only used in round 0-15) - private immutable string X_SHUFFLECTL = "XMM6"; - - // Round constant (only used in round 0-15) - private immutable string X_CONSTANT = "XMM7"; - } - version (_64Bit) - { - private immutable string SP = "RSP"; - private immutable string STATE_PTR = "R8"; - private immutable string BUFFER_PTR = "R9"; - private immutable string CONSTANTS_PTR = "R10"; - - // Registers for temporary results (XMM10 and XMM11 are also used temporary) - private immutable string W_TMP = "XMM8"; - private immutable string W_TMP2 = "XMM9"; - - // Control byte for shuffle instruction (only used in round 0-15) - private immutable string X_SHUFFLECTL = "XMM12"; - - // Round constant - private immutable string X_CONSTANT = "XMM13"; - } - - /* The control words for the byte shuffle instruction and the round constants. */ - align(16) public immutable uint[4][5] constants = - [ - // The control words for the byte shuffle instruction. - [ 0x0001_0203, 0x0405_0607, 0x0809_0a0b, 0x0c0d_0e0f ], - // Constants for round 0-19 - [ 0x5a827999, 0x5a827999, 0x5a827999, 0x5a827999 ], - // Constants for round 20-39 - [ 0x6ed9eba1, 0x6ed9eba1, 0x6ed9eba1, 0x6ed9eba1 ], - // Constants for round 40-59 - [ 0x8f1bbcdc, 0x8f1bbcdc, 0x8f1bbcdc, 0x8f1bbcdc ], - // Constants for round 60-79 - [ 0xca62c1d6, 0xca62c1d6, 0xca62c1d6, 0xca62c1d6 ] - ]; - - /** Simple version to produce numbers < 100 as string. */ - private nothrow pure string to_string(uint i) - { - if (i < 10) - return "0123456789"[i .. i + 1]; - - assert(i < 100); - char[2] s; - s[0] = cast(char)(i / 10 + '0'); - s[1] = cast(char)(i % 10 + '0'); - return s.idup; - } - - /** Returns the reference to the byte shuffle control word. */ - private nothrow pure string bswap_shufb_ctl() - { - version (_64Bit) - return "["~CONSTANTS_PTR~"]"; - else - return "[constants]"; - } - - /** Returns the reference to constant used in round i. */ - private nothrow pure string constant(uint i) - { - version (_64Bit) - return "16 + 16*"~to_string(i/20)~"["~CONSTANTS_PTR~"]"; - else - return "[constants + 16 + 16*"~to_string(i/20)~"]"; - } - - /** Returns the XMM register number used in round i */ - private nothrow pure uint regno(uint i) - { - return (i/4)&7; - } - - /** Returns reference to storage of vector W[i .. i+4]. */ - private nothrow pure string WiV(uint i) - { - return "["~SP~" + WI_PTR + "~to_string((i/4)&7)~"*16]"; - } - - /** Returns reference to storage of vector (W + K)[i .. i+4]. */ - private nothrow pure string WiKiV(uint i) - { - return "["~SP~" + WI_PLUS_KI_PTR + "~to_string((i/4)&3)~"*16]"; - } - - /** Returns reference to storage of value W[i] + K[i]. */ - private nothrow pure string WiKi(uint i) - { - return "["~SP~" + WI_PLUS_KI_PTR + 4*"~to_string(i&15)~"]"; - } - - /** - * Chooses the instruction sequence based on the 32bit or 64bit model. - */ - version (_32Bit) - { - private nothrow pure string[] swt3264(return scope string[] insn32, scope string[]) - { - return insn32; - } - } - version (_64Bit) - { - private nothrow pure string[] swt3264(scope string[], return scope string[] insn64) - { - return insn64; - } - } - - /** - * Flattens the instruction sequence and wraps it in an asm block. - */ - private nothrow pure string wrap(string[] insn) - { - string s = "asm pure nothrow @nogc {"; - foreach (t; insn) s ~= (t ~ "; \n"); - s ~= "}"; - return s; - // Is not CTFE: - // return "asm pure nothrow @nogc { " ~ join(insn, "; \n") ~ "}"; - } - - /** - * Weaves the 2 instruction sequences together. - */ - private nothrow pure string[] weave(string[] seq1, string[] seq2, uint dist = 1) - { - string[] res = []; - auto i1 = 0, i2 = 0; - while (i1 < seq1.length || i2 < seq2.length) - { - if (i2 < seq2.length) - { - res ~= seq2[i2 .. i2+1]; - i2 += 1; - } - if (i1 < seq1.length) - { - import std.algorithm.comparison : min; - - res ~= seq1[i1 .. min(i1+dist, $)]; - i1 += dist; - } - } - return res; - } - - /** - * Generates instructions to load state from memory into registers. - */ - private nothrow pure string[] loadstate(string base, string a, string b, string c, string d, string e) - { - return ["mov "~a~",["~base~" + 0*4]", - "mov "~b~",["~base~" + 1*4]", - "mov "~c~",["~base~" + 2*4]", - "mov "~d~",["~base~" + 3*4]", - "mov "~e~",["~base~" + 4*4]" ]; - } - - /** - * Generates instructions to update state from registers, saving result in memory. - */ - private nothrow pure string[] savestate(string base, string a, string b, string c, string d, string e) - { - return ["add ["~base~" + 0*4],"~a, - "add ["~base~" + 1*4],"~b, - "add ["~base~" + 2*4],"~c, - "add ["~base~" + 3*4],"~d, - "add ["~base~" + 4*4],"~e ]; - } - - /** Calculates Ch(x, y, z) = z ^ (x & (y ^ z)) */ - private nothrow pure string[] Ch(string x, string y, string z) - { - return ["mov "~T1~","~y, - "xor "~T1~","~z, - "and "~T1~","~x, - "xor "~T1~","~z ]; - } - - /** Calculates Parity(x, y, z) = x ^ y ^ z */ - private nothrow pure string[] Parity(string x, string y, string z) - { - return ["mov "~T1~","~z, - "xor "~T1~","~y, - "xor "~T1~","~x ]; - } - - /** Calculates Maj(x, y, z) = (x & y) | (z & (x ^ y)) */ - private nothrow pure string[] Maj(string x, string y, string z) - { - return ["mov "~T1~","~y, - "mov "~T2~","~x, - "or "~T1~","~x, - "and "~T2~","~y, - "and "~T1~","~z, - "or "~T1~","~T2 ]; - } - - /** Returns function for round i. Function returns result in T1 and may destroy T2. */ - private nothrow pure string[] F(int i, string b, string c, string d) - { - string[] insn; - if (i >= 0 && i <= 19) insn = Ch(b, c, d); - else if (i >= 20 && i <= 39) insn = Parity(b, c, d); - else if (i >= 40 && i <= 59) insn = Maj(b, c, d); - else if (i >= 60 && i <= 79) insn = Parity(b, c, d); - else assert(false, "Coding error"); - return insn; - } - - /** Returns instruction used to setup a round. */ - private nothrow pure string[] xsetup(int i) - { - if (i == 0) - { - return swt3264(["movdqa "~X_SHUFFLECTL~","~bswap_shufb_ctl(), - "movdqa "~X_CONSTANT~","~constant(i)], - ["movdqa "~X_SHUFFLECTL~","~bswap_shufb_ctl(), - "movdqa "~X_CONSTANT~","~constant(i)]); - } - version (_64Bit) - { - if (i%20 == 0) - { - return ["movdqa "~X_CONSTANT~","~constant(i)]; - } - } - return []; - } - - /** - * Loads the message words and performs the little to big endian conversion. - * Requires that the shuffle control word and the round constant is loaded - * into required XMM register. The BUFFER_PTR register must point to the - * buffer. - */ - private nothrow pure string[] precalc_00_15(int i) - { - int regno = regno(i); - - string W = "XMM" ~ to_string(regno); - version (_32Bit) - { - string W_TMP = "XMM" ~ to_string(regno+2); - } - version (_64Bit) - { - string W_TMP = "XMM" ~ to_string(regno+8); - } - - if ((i & 3) == 0) - { - return ["movdqu "~W~",["~BUFFER_PTR~" + "~to_string(regno)~"*16]"]; - } - else if ((i & 3) == 1) - { - return ["pshufb "~W~","~X_SHUFFLECTL] ~ - swt3264(["movdqa "~WiV(i)~","~W], []); - } - else if ((i & 3) == 2) - { - return ["movdqa "~W_TMP~","~W, - "paddd "~W_TMP~","~X_CONSTANT, - ]; - } - else - { - return ["movdqa "~WiKiV(i)~","~W_TMP, - ]; - } - } - - /** - * Done on 4 consequtive W[i] values in a single XMM register - * W[i ] = (W[i-3] ^ W[i-8] ^ W[i-14] ^ W[i-16]) rol 1 - * W[i+1] = (W[i-2] ^ W[i-7] ^ W[i-13] ^ W[i-15]) rol 1 - * W[i+2] = (W[i-1] ^ W[i-6] ^ W[i-12] ^ W[i-14]) rol 1 - * W[i+3] = ( 0 ^ W[i-5] ^ W[i-11] ^ W[i-13]) rol 1 - * - * This additional calculation unfortunately requires many additional operations - * W[i+3] ^= W[i] rol 1 - * - * Once we have 4 W[i] values in XMM we can also add four K values with one instruction - * W[i:i+3] += {K,K,K,K} - */ - private nothrow pure string[] precalc_16_31(int i) - { - int regno = regno(i); - - string W = "XMM" ~ to_string(regno); - string W_minus_4 = "XMM" ~ to_string((regno-1)&7); - string W_minus_8 = "XMM" ~ to_string((regno-2)&7); - string W_minus_12 = "XMM" ~ to_string((regno-3)&7); - string W_minus_16 = "XMM" ~ to_string((regno-4)&7); - version (_32Bit) - { - string W_TMP = "XMM" ~ to_string((regno+1)&7); - string W_TMP2 = "XMM" ~ to_string((regno+2)&7); - } - - if ((i & 3) == 0) - { - return ["movdqa "~W~","~W_minus_12, - "palignr "~W~","~W_minus_16~",8", // W[i] = W[i-14] - "pxor "~W~","~W_minus_16, // W[i] ^= W[i-16] - "pxor "~W~","~W_minus_8, // W[i] ^= W[i-8] - "movdqa "~W_TMP~","~W_minus_4, - ]; - } - else if ((i & 3) == 1) - { - return ["psrldq "~W_TMP~",4", // W[i-3] - "pxor "~W~","~W_TMP, // W[i] ^= W[i-3] - "movdqa "~W_TMP~","~W, - "psrld "~W~",31", - "pslld "~W_TMP~",1", - ]; - } - else if ((i & 3) == 2) - { - return ["por "~W~","~W_TMP, - "movdqa "~W_TMP~","~W, - "pslldq "~W_TMP~",12", - "movdqa "~W_TMP2~","~W_TMP, - "pslld "~W_TMP~",1", - ]; - } - else - { - return ["psrld "~W_TMP2~",31", - "por "~W_TMP~","~W_TMP2, - "pxor "~W~","~W_TMP, - "movdqa "~W_TMP~","~W ] ~ - swt3264(["movdqa "~WiV(i)~","~W, - "paddd "~W_TMP~","~constant(i) ], - ["paddd "~W_TMP~","~X_CONSTANT ]) ~ - ["movdqa "~WiKiV(i)~","~W_TMP]; - } - } - - /** Performs the main calculation as decribed above. */ - private nothrow pure string[] precalc_32_79(int i) - { - int regno = regno(i); - - string W = "XMM" ~ to_string(regno); - string W_minus_4 = "XMM" ~ to_string((regno-1)&7); - string W_minus_8 = "XMM" ~ to_string((regno-2)&7); - string W_minus_16 = "XMM" ~ to_string((regno-4)&7); - version (_32Bit) - { - string W_minus_28 = "[ESP + WI_PTR + "~ to_string((regno-7)&7)~"*16]"; - string W_minus_32 = "[ESP + WI_PTR + "~ to_string((regno-8)&7)~"*16]"; - string W_TMP = "XMM" ~ to_string((regno+1)&7); - string W_TMP2 = "XMM" ~ to_string((regno+2)&7); - } - version (_64Bit) - { - string W_minus_28 = "XMM" ~ to_string((regno-7)&7); - string W_minus_32 = "XMM" ~ to_string((regno-8)&7); - } - - if ((i & 3) == 0) - { - return swt3264(["movdqa "~W~","~W_minus_32], []) ~ - ["movdqa "~W_TMP~","~W_minus_4, - "pxor "~W~","~W_minus_28, // W is W_minus_32 before xor - "palignr "~W_TMP~","~W_minus_8~",8", - ]; - } - else if ((i & 3) == 1) - { - return ["pxor "~W~","~W_minus_16, - "pxor "~W~","~W_TMP, - "movdqa "~W_TMP~","~W, - ]; - } - else if ((i & 3) == 2) - { - return ["psrld "~W~",30", - "pslld "~W_TMP~",2", - "por "~W_TMP~","~W, - ]; - } - else - { - if (i < 76) - return ["movdqa "~W~","~W_TMP] ~ - swt3264(["movdqa "~WiV(i)~","~W, - "paddd "~W_TMP~","~constant(i)], - ["paddd "~W_TMP~","~X_CONSTANT]) ~ - ["movdqa "~WiKiV(i)~","~W_TMP]; - else - return swt3264(["paddd "~W_TMP~","~constant(i)], - ["paddd "~W_TMP~","~X_CONSTANT]) ~ - ["movdqa "~WiKiV(i)~","~W_TMP]; - } - } - - /** Choose right precalc method. */ - private nothrow pure string[] precalc(int i) - { - if (i >= 0 && i < 16) return precalc_00_15(i); - if (i >= 16 && i < 32) return precalc_16_31(i); - if (i >= 32 && i < 80) return precalc_32_79(i); - return []; - } - - /** - * Return code for round i and i+1. - * Performs the following rotation: - * in=>out: A=>D, B=>E, C=>A, D=>B, E=>C - */ - private nothrow pure string[] round(int i, string a, string b, string c, string d, string e) - { - return xsetup(PRECALC_AHEAD + i) ~ - weave(F(i, b, c, d) ~ // Returns result in T1; may destroy T2 - ["add "~e~","~WiKi(i), - "ror "~b~",2", - "mov "~T2~","~a, - "add "~d~","~WiKi(i+1), - "rol "~T2~",5", - "add "~e~","~T1 ], - precalc(PRECALC_AHEAD + i), 2) ~ - weave( - ["add "~T2~","~e, // T2 = (A <<< 5) + F(B, C, D) + Wi + Ki + E - "mov "~e~","~T2, - "rol "~T2~",5", - "add "~d~","~T2 ] ~ - F(i+1, a, b, c) ~ // Returns result in T1; may destroy T2 - ["add "~d~","~T1, - "ror "~a~",2"], - precalc(PRECALC_AHEAD + i+1), 2); - } - - // Offset into stack (see below) - version (_32Bit) - { - private enum { STATE_OFS = 4, WI_PLUS_KI_PTR = 8, WI_PTR = 72 } - } - version (_64Bit) - { - private enum { WI_PLUS_KI_PTR = 0 } - } - - /** The prologue sequence. */ - private nothrow pure string[] prologue() - { - version (_32Bit) - { - /* - * Parameters: - * EAX contains pointer to state - * - * Stack layout as follows: - * +----------------+ - * | ptr to buffer | - * +----------------+ - * | return address | - * +----------------+ - * | EBP | - * +----------------+ - * | ESI | - * +----------------+ - * | EDI | - * +----------------+ - * | EBX | - * +----------------+ - * | Space for | - * | Wi | <- ESP+72 - * +----------------+ - * | Space for | - * | Wi+Ki | <- ESP+8 - * +----------------+ <- 16byte aligned - * | ptr to state | <- ESP+4 - * +----------------+ - * | old ESP | <- ESP - * +----------------+ - */ - static assert(STATE_PTR == "EAX"); - static assert(BUFFER_PTR == "EBX"); - return [// Save registers according to calling convention - "push EBP", - "push ESI", - "push EDI", - "push EBX", - // Load parameters - "mov "~BUFFER_PTR~", [ESP + 5*4]", //pointer to input buffer - // Align stack - "mov EBP, ESP", - "sub ESP, 4*16 + 8*16", - "and ESP, 0xffff_fff0", - "push "~STATE_PTR, - "push EBP", - ]; - } - else version (Win64) - { - /* - * Parameters: - * RCX contains pointer to state - * RDX contains pointer to input buffer - * R8 contains pointer to constants - * - * Stack layout as follows: - * +----------------+ - * | return address | - * +----------------+ - * | RBP | - * +----------------+ - * | RBX | - * +----------------+ - * | RSI | - * +----------------+ - * | RDI | - * +----------------+ - * | Unused | - * +----------------+ - * | XMM6-XMM13 | 8*16 bytes - * +----------------+ - * | Space for | - * | Wi+Ki | <- RSP - * +----------------+ <- 16byte aligned - */ - return [// Save registers according to calling convention - "push RBP", - "push RBX", - "push RSI", - "push RDI", - // Save parameters - "mov "~CONSTANTS_PTR~", R8", //pointer to constants to avoid absolute addressing - "mov "~STATE_PTR~", RCX", //pointer to state - "mov "~BUFFER_PTR~", RDX", //pointer to buffer - // Align stack - "sub RSP, 4*16+8+8*16", - "movdqa [RSP+4*16], XMM6", - "movdqa [RSP+5*16], XMM7", - "movdqa [RSP+6*16], XMM8", - "movdqa [RSP+7*16], XMM9", - "movdqa [RSP+8*16], XMM10", - "movdqa [RSP+9*16], XMM11", - "movdqa [RSP+10*16], XMM12", - "movdqa [RSP+11*16], XMM13", - ]; - } - else version (_64Bit) - { - /* - * Parameters: - * RDI contains pointer to state - * RSI contains pointer to input buffer - * RDX contains pointer to constants - * - * Stack layout as follows: - * +----------------+ - * | return address | - * +----------------+ - * | RBP | - * +----------------+ - * | RBX | - * +----------------+ - * | Unused | - * +----------------+ - * | Space for | - * | Wi+Ki | <- RSP - * +----------------+ <- 16byte aligned - */ - return [// Save registers according to calling convention - "push RBP", - "push RBX", - // Save parameters - "mov "~STATE_PTR~", RDI", //pointer to state - "mov "~BUFFER_PTR~", RSI", //pointer to buffer - "mov "~CONSTANTS_PTR~", RDX", //pointer to constants to avoid absolute addressing - // Align stack - "sub RSP, 4*16+8", - ]; - } - } - - /** - * The epilogue sequence. Just pop the saved registers from stack and return to caller. - */ - private nothrow pure string[] epilogue() - { - version (_32Bit) - { - return ["pop ESP", - "pop EBX", - "pop EDI", - "pop ESI", - "pop EBP", - "ret 4", - ]; - } - else version (Win64) - { - return ["movdqa XMM6,[RSP+4*16]", - "movdqa XMM7,[RSP+5*16]", - "movdqa XMM8,[RSP+6*16]", - "movdqa XMM9,[RSP+7*16]", - "movdqa XMM10,[RSP+8*16]", - "movdqa XMM11,[RSP+9*16]", - "movdqa XMM12,[RSP+10*16]", - "movdqa XMM13,[RSP+11*16]", - "add RSP,4*16+8+8*16", - "pop RDI", - "pop RSI", - "pop RBX", - "pop RBP", - "ret 0", - ]; - } - else version (_64Bit) - { - return ["add RSP,4*16+8", - "pop RBX", - "pop RBP", - "ret 0", - ]; - } - } - - // constants as extra argument for PIC - // https://issues.dlang.org/show_bug.cgi?id=9378 - import std.meta : AliasSeq; - version (_64Bit) - alias ExtraArgs = AliasSeq!(typeof(&constants)); - else - alias ExtraArgs = AliasSeq!(); - - /** - * - */ - public void transformSSSE3(uint[5]* state, const(ubyte[64])* buffer, ExtraArgs) pure nothrow @nogc - { - mixin(wrap(["naked"] ~ prologue())); - // Precalc first 4*16=64 bytes - mixin(wrap(xsetup(0))); - mixin(wrap(weave(precalc(0)~precalc(1)~precalc(2)~precalc(3), - precalc(4)~precalc(5)~precalc(6)~precalc(7)))); - mixin(wrap(weave(loadstate(STATE_PTR, A, B, C, D, E), - weave(precalc(8)~precalc(9)~precalc(10)~precalc(11), - precalc(12)~precalc(13)~precalc(14)~precalc(15))))); - // Round 1 - mixin(wrap(round( 0, A, B, C, D, E))); - mixin(wrap(round( 2, D, E, A, B, C))); - mixin(wrap(round( 4, B, C, D, E, A))); - mixin(wrap(round( 6, E, A, B, C, D))); - mixin(wrap(round( 8, C, D, E, A, B))); - mixin(wrap(round(10, A, B, C, D, E))); - mixin(wrap(round(12, D, E, A, B, C))); - mixin(wrap(round(14, B, C, D, E, A))); - mixin(wrap(round(16, E, A, B, C, D))); - mixin(wrap(round(18, C, D, E, A, B))); - // Round 2 - mixin(wrap(round(20, A, B, C, D, E))); - mixin(wrap(round(22, D, E, A, B, C))); - mixin(wrap(round(24, B, C, D, E, A))); - mixin(wrap(round(26, E, A, B, C, D))); - mixin(wrap(round(28, C, D, E, A, B))); - mixin(wrap(round(30, A, B, C, D, E))); - mixin(wrap(round(32, D, E, A, B, C))); - mixin(wrap(round(34, B, C, D, E, A))); - mixin(wrap(round(36, E, A, B, C, D))); - mixin(wrap(round(38, C, D, E, A, B))); - // Round 3 - mixin(wrap(round(40, A, B, C, D, E))); - mixin(wrap(round(42, D, E, A, B, C))); - mixin(wrap(round(44, B, C, D, E, A))); - mixin(wrap(round(46, E, A, B, C, D))); - mixin(wrap(round(48, C, D, E, A, B))); - mixin(wrap(round(50, A, B, C, D, E))); - mixin(wrap(round(52, D, E, A, B, C))); - mixin(wrap(round(54, B, C, D, E, A))); - mixin(wrap(round(56, E, A, B, C, D))); - mixin(wrap(round(58, C, D, E, A, B))); - // Round 4 - mixin(wrap(round(60, A, B, C, D, E))); - mixin(wrap(round(62, D, E, A, B, C))); - mixin(wrap(round(64, B, C, D, E, A))); - mixin(wrap(round(66, E, A, B, C, D))); - mixin(wrap(round(68, C, D, E, A, B))); - mixin(wrap(round(70, A, B, C, D, E))); - mixin(wrap(round(72, D, E, A, B, C))); - mixin(wrap(round(74, B, C, D, E, A))); - mixin(wrap(round(76, E, A, B, C, D))); - mixin(wrap(round(78, C, D, E, A, B))); - version (_32Bit) - { - // Load pointer to state - mixin(wrap(["mov "~STATE_PTR~",[ESP + STATE_OFS]"])); - } - mixin(wrap(savestate(STATE_PTR, A, B, C, D, E))); - mixin(wrap(epilogue())); - } -} diff --git a/phobos/std/internal/math/biguintarm.d b/phobos/std/internal/math/biguintarm.d deleted file mode 100644 index c31e26b..0000000 --- a/phobos/std/internal/math/biguintarm.d +++ /dev/null @@ -1,1056 +0,0 @@ -/** Optimised asm arbitrary precision arithmetic ('bignum') - * routines for ARM processors. - * - * All functions operate on arrays of uints, stored LSB first. - * If there is a destination array, it will be the first parameter. - * Currently, all of these functions are subject to change, and are - * intended for internal use only. - */ - -/* Copyright Kai Nacke 2016. - * 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) - */ - -/** - * Like the generic module biguintnoasm, some functions assume - * non-empty arrays. - */ - -module std.internal.math.biguintarm; - -version (LDC): -version (ARM): - -import ldc.llvmasm; - -static import stdnoasm = std.internal.math.biguintnoasm; - -@trusted: - -public: -alias BigDigit = stdnoasm.BigDigit; // A Bignum is an array of BigDigits. - - // Limits for when to switch between multiplication algorithms. -enum : int { KARATSUBALIMIT = 10 }; // Minimum value for which Karatsuba is worthwhile. -enum : int { KARATSUBASQUARELIMIT=12 }; // Minimum value for which square Karatsuba is worthwhile - - -/** Multi-byte addition or subtraction - * dest[] = src1[] + src2[] + carry (0 or 1). - * or dest[] = src1[] - src2[] - carry (0 or 1). - * Returns carry or borrow (0 or 1). - * Set op == '+' for addition, '-' for subtraction. - */ -uint multibyteAddSub(char op)(uint[] dest, const(uint) [] src1, - const (uint) [] src2, uint carry) pure @nogc nothrow -{ - assert(carry == 0 || carry == 1); - assert(src1.length >= dest.length && src2.length >= dest.length); - static if (op == '+') - { - enum opcs = "adcs"; // Use "addition with carry" - enum foc = "@"; // Use comment - } - else - { - enum opcs = "sbcs"; // Use "subtraction with carry" - enum foc = "eor"; // Use "exclusive or" - } - return __asm!uint(` cmp $2,#0 @ Check dest.length - beq 1f - `~foc~` $0,$0,#1 @ Flip carry or comment - mov r5,#0 @ Initialize index - 2: - ldr r6,[${3:m},r5,LSL #2] @ Load *(src1.ptr + index) - ldr r7,[${4:m},r5,LSL #2] @ Load *(src2.ptr + index) - lsrs $0,$0,#1 @ Set carry - `~opcs~` r6,r6,r7 @ Add/Sub with carry - str r6,[${1:m},r5,LSL #2] @ Store *(dest.ptr + index) - adc $0,$0,#0 @ Store carry - add r5,r5,#1 @ Increment index - cmp $2,r5 - bhi 2b - `~foc~` $0,$0,#1 @ Flip carry or comment - 1:`, - "=&r,=*m,r,*m,*m,0,~{r5},~{r6},~{r7},~{cpsr}", - dest.ptr, dest.length, src1.ptr, src2.ptr, carry); -} - -unittest -{ - // Some simple checks to validate the interface - uint carry; - uint [] a = new uint[40]; - uint [] b = new uint[40]; - uint [] c = new uint[40]; - - // Add - a[0] = 0xFFFFFFFE; - b[0] = 0x00000001; - c[1] = 0xDEADBEEF; - carry = multibyteAddSub!('+')(c[0..1], a[0..1], b[0..1], 0); - assert(c[0] == 0xFFFFFFFF); - assert(carry == 0); - - a[0] = 0xFFFFFFFE; - b[0] = 0x00000000; - carry = multibyteAddSub!('+')(c[0..1], a[0..1], b[0..1], 1); - assert(c[0] == 0xFFFFFFFF); - assert(carry == 0); - - a[0] = 0xFFFFFFFF; - b[0] = 0x00000001; - carry = multibyteAddSub!('+')(c[0..1], a[0..1], b[0..1], 0); - assert(c[0] == 0x00000000); - assert(carry == 1); - - a[0] = 0xFFFFFFFF; - b[0] = 0x00000000; - carry = multibyteAddSub!('+')(c[0..1], a[0..1], b[0..1], 1); - assert(c[0] == 0x00000000); - assert(carry == 1); - - a[0] = 0xFFFFFFFF; - a[1] = 0x00000000; - b[0] = 0x00000001; - b[1] = 0x00000000; - c[0] = 0xDEADBEEF; - c[1] = 0xDEADBEEF; - c[2] = 0xDEADBEEF; - carry = multibyteAddSub!('+')(c[0..2], a[0..2], b[0..2], 0); - assert(c[0] == 0x00000000); - assert(c[1] == 0x00000001); - assert(c[2] == 0xDEADBEEF); - assert(carry == 0); - - a[0] = 0xFFFF0000; - b[0] = 0x0001FFFF; - for (size_t i = 1; i < 9; i++) - { - a[i] = 0x0000FFFF; - b[i] = 0xFFFF0000; - c[i] = 0xDEADBEEF; - } - a[9] = 0x00000000; - b[9] = 0x00000000; - c[9] = 0xDEADBEEF; - c[10] = 0xDEADBEEF; - carry = multibyteAddSub!('+')(c[0..10], a[0..10], b[0..10], 0); - assert(c[0] == 0x0000FFFF); - for (size_t i = 1; i < 9; i++) - assert(c[i] == 0x00000000); - assert(c[9] == 0x00000001); - assert(c[10] == 0xDEADBEEF); - assert(carry == 0); - - - // Sub - a[0] = 0xFFFFFFFF; - b[0] = 0x00000000; - c[1] = 0xDEADBEEF; - carry = multibyteAddSub!('-')(c[0..1], a[0..1], b[0..1], 1); - assert(c[0] == 0xFFFFFFFE); - assert(c[1] == 0xDEADBEEF); - assert(carry == 0); - - a[0] = 0xFFFFFFFF; - b[0] = 0x00000001; - c[1] = 0xDEADBEEF; - carry = multibyteAddSub!('-')(c[0..1], a[0..1], b[0..1], 1); - assert(c[0] == 0xFFFFFFFD); - assert(c[1] == 0xDEADBEEF); - assert(carry == 0); - - a[0] = 0xC0000000; - a[1] = 0x7000BEEF; - b[0] = 0x80000000; - b[1] = 0x3000BABE; - c[0] = 0x40000000; - c[1] = 0x40000431; - carry = multibyteAddSub!('-')(c[0..2], a[0..2], b[0..2], 0); - assert(c[0] == 0x40000000); - assert(c[1] == 0x40000431); - assert(carry == 0); -} - -unittest -{ - uint [] a = new uint[40]; - uint [] b = new uint[40]; - uint [] c = new uint[40]; - for (size_t i = 0; i < a.length; ++i) - { - if (i&1) a[i]=cast(uint)(0x8000_0000 + i); - else a[i]=cast(uint)i; - b[i]= 0x8000_0003; - } - c[19]=0x3333_3333; - uint carry = multibyteAddSub!('+')(c[0..18], b[0..18], a[0..18], 0); - assert(c[0]==0x8000_0003); - assert(c[1]==4); - assert(c[19]==0x3333_3333); // check for overrun - assert(carry==1); - for (size_t i = 0; i < a.length; ++i) - { - a[i] = b[i] = c[i] = 0; - } - a[8]=0x048D159E; - b[8]=0x048D159E; - a[10]=0x1D950C84; - b[10]=0x1D950C84; - a[5] =0x44444444; - carry = multibyteAddSub!('-')(a[0..12], a[0..12], b[0..12], 0); - assert(a[11] == 0); - for (size_t i = 0; i < 10; ++i) - if (i != 5) - assert(a[i] == 0); - - for (size_t q = 3; q < 36; ++q) - { - for (size_t i = 0; i< a.length; ++i) - { - a[i] = b[i] = c[i] = 0; - } - a[q-2]=0x040000; - b[q-2]=0x040000; - carry = multibyteAddSub!('-')(a[0..q], a[0..q], b[0..q], 0); - assert(a[q-2]==0); - } -} - - -/** dest[] += carry, or dest[] -= carry. - * op must be '+' or '-' - * Returns final carry or borrow (0 or 1) - */ -uint multibyteIncrementAssign(char op)(uint[] dest, uint carry) - pure @nogc nothrow -{ - static if (op == '+') - { - enum ops = "adds"; - enum bcc = "bcc"; - } - else - { - enum ops = "subs"; - enum bcc = "bcs"; - } - return __asm!uint(` cmp $2,0 @ Check dest.length - beq 1f - ldr r6,$1 @ Load *(dest) - `~ops~` r6,r6,$0 @ Add/Sub - str r6,$1 @ Store *(dest + index) - mov $0,#0 @ Assume result "carry 0" - `~bcc~` 1f - cmp $2,#1 - beq 2f - mov r5,#1 @ Initialize index - 3: - ldr r6,[${1:m},r5,LSL #2] @ Load *(dest + index) - `~ops~` r6,r6,#1 @ Add/Sub - str r6,[${1:m},r5,LSL #2] @ Store *(dest + index) - `~bcc~` 1f - add r5,r5,#1 @ Increment index - cmp $2,r5 - bhi 3b - 2: - mov $0,#1 @ Result "carry 1" - 1:`, - "=&r,=*m,r,0,~{r5},~{r6},~{cpsr}", - dest.ptr, dest.length, carry); -} - -unittest -{ - // Some simple checks to validate the interface - uint carry; - uint [] a = new uint[40]; - - // Add - a[0] = 0xFFFFFFFE; - a[1] = 0xDEADBEEF; - carry = multibyteIncrementAssign!('+')(a[0..1], 1); - assert(a[0] == 0xFFFFFFFF); - assert(a[1] == 0xDEADBEEF); - assert(carry == 0); - - a[0] = 0xFFFFFFFF; - a[1] = 0xDEADBEEF; - carry = multibyteIncrementAssign!('+')(a[0..1], 0); - assert(a[0] == 0xFFFFFFFF); - assert(a[1] == 0xDEADBEEF); - assert(carry == 0); - - a[0] = 0xFFFFFFFF; - a[1] = 0xDEADBEEF; - carry = multibyteIncrementAssign!('+')(a[0..1], 1); - assert(a[0] == 0x00000000); - assert(a[1] == 0xDEADBEEF); - assert(carry == 1); - - a[0] = 0xFFFFFFFF; - a[1] = 0x0000FFFF; - a[2] = 0xDEADBEEF; - carry = multibyteIncrementAssign!('+')(a[0..2], 1); - assert(a[0] == 0x00000000); - assert(a[1] == 0x00010000); - assert(a[2] == 0xDEADBEEF); - assert(carry == 0); - - a[0] = 0xFFFFFFFF; - a[1] = 0xFFFFFFFF; - a[2] = 0x0000FFFF; - a[3] = 0xDEADBEEF; - carry = multibyteIncrementAssign!('+')(a[0..3], 1); - assert(a[0] == 0x00000000); - assert(a[1] == 0x00000000); - assert(a[2] == 0x00010000); - assert(a[3] == 0xDEADBEEF); - assert(carry == 0); - - a[0] = 0xFFFFFFFF; - a[1] = 0xFFFFFFFF; - a[2] = 0xFFFFFFFF; - a[3] = 0xDEADBEEF; - carry = multibyteIncrementAssign!('+')(a[0..3], 1); - assert(a[0] == 0x00000000); - assert(a[1] == 0x00000000); - assert(a[2] == 0x00000000); - assert(a[3] == 0xDEADBEEF); - assert(carry == 1); - - - // Sub - a[0] = 0xFFFFFFFF; - a[1] = 0xDEADBEEF; - carry = multibyteIncrementAssign!('-')(a[0..1], 1); - assert(a[0] == 0xFFFFFFFE); - assert(a[1] == 0xDEADBEEF); - assert(carry == 0); - - a[0] = 0xFFFFFFFF; - a[1] = 0xDEADBEEF; - carry = multibyteIncrementAssign!('-')(a[0..1], 0); - assert(a[0] == 0xFFFFFFFF); - assert(a[1] == 0xDEADBEEF); - assert(carry == 0); - - a[0] = 0x00000000; - a[1] = 0xDEADBEEF; - carry = multibyteIncrementAssign!('-')(a[0..1], 1); - assert(a[0] == 0xFFFFFFFF); - assert(a[1] == 0xDEADBEEF); - assert(carry == 1); - - a[0] = 0x00000000; - a[1] = 0x00000000; - a[2] = 0xDEADBEEF; - carry = multibyteIncrementAssign!('-')(a[0..2], 1); - assert(a[0] == 0xFFFFFFFF); - assert(a[1] == 0xFFFFFFFF); - assert(a[2] == 0xDEADBEEF); - assert(carry == 1); - - a[0] = 0x00000000; - a[1] = 0x00000000; - a[2] = 0x00000000; - a[3] = 0xDEADBEEF; - carry = multibyteIncrementAssign!('-')(a[0..3], 1); - assert(a[0] == 0xFFFFFFFF); - assert(a[1] == 0xFFFFFFFF); - assert(a[2] == 0xFFFFFFFF); - assert(a[3] == 0xDEADBEEF); - assert(carry == 1); - - a[0] = 0x00000000; - a[1] = 0x00000000; - a[2] = 0x00000010; - a[3] = 0xDEADBEEF; - carry = multibyteIncrementAssign!('-')(a[0..3], 1); - assert(a[0] == 0xFFFFFFFF); - assert(a[1] == 0xFFFFFFFF); - assert(a[2] == 0x0000000F); - assert(a[3] == 0xDEADBEEF); - assert(carry == 0); -} - -/** dest[] = src[] << numbits - * numbits must be in the range 1..31 - */ -uint multibyteShl(uint [] dest, const(uint) [] src, uint numbits) - pure @nogc nothrow -{ - assert(dest.length > 0 && src.length >= dest.length); - assert(numbits >= 1 && numbits <= 31); - return __asm!uint(` mov $0,#0 @ result = 0 - mov r5,#0 @ Initialize index - 1: - ldr r6,[${3:m},r5,LSL #2] @ Load *(src + index) - lsl r7,r6,$4 - add r7,r7,$0 - str r7,[${1:m},r5,LSL #2] @ Store *(dest + index) - lsr $0,r6,$5 - add r5,r5,#1 - cmp $2,r5 - bhi 1b`, - "=&r,=*m,r,*m,r,r,~{r5},~{r6},~{r7},~{cpsr}", - dest.ptr, dest.length, src.ptr, numbits, 32-numbits); -} - - -/** dest[] = src[] >> numbits - * numbits must be in the range 1..31 - */ -void multibyteShr(uint [] dest, const(uint) [] src, uint numbits) - pure @nogc nothrow -{ - assert(dest.length > 0 && src.length >= dest.length); - assert(numbits >= 1 && numbits <= 31); - __asm(` mov r0,#0 @ result = 0 - mov r5,$1 @ Initialize index - 1: - sub r5,#1 - ldr r6,[${2:m},r5,LSL #2] @ Load *(src + index) - lsr r7,r6,$3 - add r7,r7,r0 - str r7,[${0:m},r5,LSL #2] @ Store *(dest + index) - lsl r0,r6,$4 - cmp $1,r5 - bhi 1b`, - "=*m,r,*m,r,r,~{r0},~{r5},~{r6},~{r7},~{cpsr}", - dest.ptr, dest.length, src.ptr, numbits, 32-numbits); -} - -unittest -{ - uint [] aa = [0x1222_2223, 0x4555_5556, 0x8999_999A, 0xBCCC_CCCD, 0xEEEE_EEEE]; - multibyteShr(aa[0..$-2], aa, 4); - assert(aa[0] == 0x6122_2222 && aa[1] == 0xA455_5555 && aa[2] == 0x0899_9999); - assert(aa[3] == 0xBCCC_CCCD); - - aa = [0x1222_2223, 0x4555_5556, 0x8999_999A, 0xBCCC_CCCD, 0xEEEE_EEEE]; - multibyteShr(aa[0..$-1], aa, 4); - assert(aa[0] == 0x6122_2222 && aa[1] == 0xA455_5555 - && aa[2] == 0xD899_9999 && aa[3] == 0x0BCC_CCCC); - - aa = [0xF0FF_FFFF, 0x1222_2223, 0x4555_5556, 0x8999_999A, 0xBCCC_CCCD, - 0xEEEE_EEEE]; - multibyteShl(aa[1..4], aa[1..$], 4); - assert(aa[0] == 0xF0FF_FFFF && aa[1] == 0x2222_2230 - && aa[2]==0x5555_5561 && aa[3]==0x9999_99A4 && aa[4]==0x0BCCC_CCCD); -} - -/** dest[] = src[] * multiplier + carry. - * Returns carry. - */ -uint multibyteMul(uint[] dest, const(uint)[] src, uint multiplier, uint carry) - pure @nogc nothrow -{ - assert(src.length >= dest.length); - return __asm!uint(` cmp $2,#0 @ Check dest.length - beq 1f - mov r5,#0 @ Initialize index - - movs r8,$2,LSR #1 @ Loop unrolled 2 times - beq 2f - lsl r8,#1 - 3: - mov r7,#0 @ Clear high word - ldr r6,[${3:m},r5,LSL #2] @ Load *(src + index) - umlal $0,r7,r6,$4 @ r6 * $4 + r7:$0 - str $0,[${1:m},r5,LSL #2] @ Store *(dest + index) - add r5,r5,#1 - mov $0,#0 @ Clear high word - ldr r6,[${3:m},r5,LSL #2] @ Load *(src + index) - umlal r7,$0,r6,$4 @ r6 * $4 + $0:r7 - str r7,[${1:m},r5,LSL #2] @ Store *(dest + index) - add r5,r5,#1 - cmp r8,r5 - bhi 3b - cmp $2,r5 - beq 1f - - 2: - mov r7,#0 @ Clear high word - ldr r6,[${3:m},r5,LSL #2] @ Load *(src + index) - umlal $0,r7,r6,$4 @ r6 * $4 + r7:$0 - str $0,[${1:m},r5,LSL #2] @ Store *(dest + index) - mov $0,r7 @ Move high word to low word - @add r5,r5,#1 - @cmp $2,r5 - @bhi 2b - 1:`, - "=&r,=*m,r,*m,r,0,~{r5},~{r6},~{r7},~{r8},~{cpsr}", - dest.ptr, dest.length, src.ptr, multiplier, carry); -} - -unittest -{ - uint [] aa = [0xF0FF_FFFF, 0x1222_2223, 0x4555_5556, 0x8999_999A, - 0xBCCC_CCCD, 0xEEEE_EEEE]; - multibyteMul(aa[1..4], aa[1..4], 16, 0); - assert(aa[0] == 0xF0FF_FFFF && aa[1] == 0x2222_2230 && aa[2]==0x5555_5561 - && aa[3]==0x9999_99A4 && aa[4]==0x0BCCC_CCCD); -} - -/** - * dest[] += src[] * multiplier + carry(0..FFFF_FFFF). - * Returns carry out of MSB (0..FFFF_FFFF). - */ -uint multibyteMulAdd(char op)(uint [] dest, const(uint)[] src, - uint multiplier, uint carry) pure @nogc nothrow -{ - assert(dest.length > 0 && dest.length == src.length); - static if (op == '+') - { - enum adds = "adds"; - enum adc = "adc"; - enum com = "@"; - } - else - { - enum adds = "rsbs"; - enum adc = "rsc"; - enum com = ""; - } - return __asm!uint(` mov r5,#0 @ Initialize index - 1: - mov r8,#0 @ High word of carry r8:$0 - ldr r6,[${3:m},r5,LSL #2] @ Load *(src + index) - ldr r7,[${1:m},r5,LSL #2] @ Load *(dest + index) - umlal $0,r8,r6,$4 @ r6 * $4 + r8:$0 - `~adds~` $0,$0,r7 - str $0,[${1:m},r5,LSL #2] @ Store *(dest + index) - `~adc~` $0,r8,#0 - `~com~` rsb $0,$0,#0 @ FIXME: Fold negate into rsc - add r5,r5,#1 - cmp $2,r5 - bhi 1b`, - "=r,=*m,r,*m,r,0,~{r5},~{r6},~{r7},~{r8},~{cpsr}", - dest.ptr, dest.length, src.ptr, multiplier, carry); -} - -unittest -{ - - uint [] aa = [0xF0FF_FFFF, 0x1222_2223, 0x4555_5556, 0x8999_999A, - 0xBCCC_CCCD, 0xEEEE_EEEE]; - uint [] bb = [0x1234_1234, 0xF0F0_F0F0, 0x00C0_C0C0, 0xF0F0_F0F0, - 0xC0C0_C0C0]; - multibyteMulAdd!('+')(bb[1..$-1], aa[1..$-2], 16, 5); - assert(bb[0] == 0x1234_1234 && bb[4] == 0xC0C0_C0C0); - assert(bb[1] == 0x2222_2230 + 0xF0F0_F0F0 + 5 - && bb[2] == 0x5555_5561 + 0x00C0_C0C0 + 1 - && bb[3] == 0x9999_99A4 + 0xF0F0_F0F0 ); - multibyteMulAdd!('-')(bb[1..$-1], aa[1..$-2], 16, 5); - assert(bb[0] == 0x1234_1234 && bb[1] == 0xF0F0_F0F0); - assert(bb[2] == 0x00C0_C0C0 && bb[3] == 0xF0F0_F0F0); - assert(bb[4] == 0xC0C0_C0C0); -} - - -/** - Sets result = result[0..left.length] + left * right - - It is defined in this way to allow cache-efficient multiplication. - This function is equivalent to: - ---- - for (size_t i = 0; i< right.length; ++i) { - dest[left.length + i] = multibyteMulAdd(dest[i..left.length+i], - left, right[i], 0); - } - ---- - */ -void multibyteMultiplyAccumulate(uint [] dest, const(uint)[] left, const(uint) - [] right) pure @nogc nothrow -{ - for (size_t i = 0; i < right.length; ++i) - { - dest[left.length + i] = multibyteMulAdd!('+')(dest[i..left.length+i], - left, right[i], 0); - } -} - -/** dest[] /= divisor. - * overflow is the initial remainder, and must be in the range 0..divisor-1. - */ -uint multibyteDivAssign(uint [] dest, uint divisor, uint overflow) - pure @nogc nothrow -{ - ulong c = cast(ulong)overflow; - for(ptrdiff_t i = dest.length-1; i>= 0; --i) - { - c = (c<<32) + cast(ulong)(dest[i]); - uint q = cast(uint)(c/divisor); - c -= divisor * q; - dest[i] = q; - } - return cast(uint)c; -} - -unittest -{ - uint [] aa = new uint[101]; - for (uint i = 0; i < aa.length; ++i) - aa[i] = 0x8765_4321 * (i+3); - uint overflow = multibyteMul(aa, aa, 0x8EFD_FCFB, 0x33FF_7461); - uint r = multibyteDivAssign(aa, 0x8EFD_FCFB, overflow); - for (uint i=0; i= 2*src.length); - __asm(` cmp $2,0 @ Check src.length - beq 1f - mov r5,#0 @ Initialize index 1 - mov r6,${0:m} @ Initialize dest - mov r3,#0 @ Initialize carry lo - mov r4,#0 @ Initialize carry hi - 2: - ldr r7,[${1:m},r5,LSL #2] @ Load *(src + index) - ldr r8,[r6] @ Load *(dest + 2*index) - umlal r3,r4,r7,r7 @ r7 * r7 + r4:r3 - adds r3,r8 - str r3,[r6],#4 @ Store *(dest + 2*index) - ldr r8,[r6] @ Load *(dest + 2*index +1) - adcs r3,r4,r8 - str r3,[r6],#4 @ Store *(dest + 2*index + 1) - mov r4,#0 @ Initialize carry hi - adc r3,r4,#0 @ Initialize carry lo - add r5,r5,#1 - cmp $2,r5 - bhi 2b - 1:`, - "=*m,*m,r,~{r3},~{r4},~{r5},~{r6},~{r7},~{r8},~{cpsr}", - dest.ptr, src.ptr, src.length); -} - -// Does half a square multiply. (square = diagonal + 2*triangle) -void multibyteTriangleAccumulate(uint[] dest, const(uint)[] x) - pure @nogc nothrow -{ - assert(dest.length >= 2*x.length && x.length > 0); - // x[0]*x[1...$] + x[1]*x[2..$] + ... + x[$-2]x[$-1..$] - dest[x.length] = multibyteMul(dest[1 .. x.length], x[1..$], x[0], 0); - if (x.length < 4) - { - if (x.length == 3) - { - ulong c = cast(ulong)(x[$-1]) * x[$-2] + dest[2*x.length-3]; - dest[2*x.length - 3] = cast(uint)c; - c >>= 32; - dest[2*x.length - 2] = cast(uint)c; - } - return; - } - for (size_t i = 2; i < x.length - 2; ++i) - { - dest[i-1+ x.length] = multibyteMulAdd!('+')( - dest[i+i-1 .. i+x.length-1], x[i..$], x[i-1], 0); - } - // Unroll the last two entries, to reduce loop overhead: - ulong c = cast(ulong)(x[$-3]) * x[$-2] + dest[2*x.length-5]; - dest[2*x.length-5] = cast(uint)c; - c >>= 32; - c += cast(ulong)(x[$-3]) * x[$-1] + dest[2*x.length-4]; - dest[2*x.length-4] = cast(uint)c; - c >>= 32; - c += cast(ulong)(x[$-1]) * x[$-2]; - dest[2*x.length-3] = cast(uint)c; - c >>= 32; - dest[2*x.length-2] = cast(uint)c; -} - -void multibyteSquare(BigDigit[] result, const(BigDigit) [] x) pure @nogc nothrow -{ - multibyteTriangleAccumulate(result, x); - result[$-1] = multibyteShl(result[1..$-1], result[1..$-1], 1); // mul by 2 - result[0] = 0; - multibyteAddDiagonalSquares(result, x); -} - -version (unittest) -{ - static import std.internal.math.biguintnoasm; - import core.stdc.stdio : printf; - import std.random; - - size_t rndArraySz(size_t sz = maxArraySz) - { - return uniform(1, sz); - } - - immutable tombstone = 0xDEADBEEF; - - void rndNum(uint[] a, size_t sz) - { - for (int i = 0; i < sz; i++) - a[i] = uniform!uint(); - for (int i = sz; i < a.length; i++) - a[i] = tombstone; - } - - void initNum(uint[] a) - { - for (int i = 0; i < a.length; i++) - a[i] = tombstone; - } - - void println(string str, uint[] a) - { - printf("%.*s: ", str.length, str.ptr); - for (size_t i = 0; i < a.length; i++) printf("%08X ", a[i]); - printf("\n"); - } - - void println(string str, uint[] a, size_t sz) - { - printf("%.*s: ", str.length, str.ptr); - for (size_t i = 0; i < sz; i++) printf("%08X ", a[i]); - printf("\n"); - } - - immutable loopMax = 100_000; - - immutable size_t maxArraySz = 273; - uint[] a = new uint[maxArraySz+1]; - uint[] b = new uint[maxArraySz+1]; - uint[] r1 = new uint[maxArraySz+1]; - uint[] r2 = new uint[maxArraySz+1]; - - void testMultibyteAddSub() - { - for (int j = 0; j < loopMax; j++) - { - auto arraySz = rndArraySz(); - - rndNum(a, arraySz); - rndNum(b, arraySz); - initNum(r1); - initNum(r2); - immutable uint carry = uniform(0, 2); - // Add - auto c1 = multibyteAddSub!('+')(r1[0..arraySz], a[0..arraySz], b[0..arraySz], carry); - auto c2 = std.internal.math.biguintnoasm.multibyteAddSub!('+')(r2[0..arraySz], a[0..arraySz], b[0..arraySz], carry); - assert(r1[0..arraySz] == r2[0..arraySz]); - assert(c1 == c2); - assert(c1 == 0 || c1 == 1); - assert(a[arraySz] == tombstone); - assert(b[arraySz] == tombstone); - assert(r1[arraySz] == tombstone); - assert(r2[arraySz] == tombstone); - // Sub - c1 = multibyteAddSub!('-')(r1[0..arraySz], a[0..arraySz], b[0..arraySz], carry); - c2 = std.internal.math.biguintnoasm.multibyteAddSub!('-')(r2[0..arraySz], a[0..arraySz], b[0..arraySz], carry); - assert(r1[0..arraySz] == r2[0..arraySz]); - assert(c1 == c2); - assert(c1 == 0 || c1 == 1); - assert(a[arraySz] == tombstone); - assert(b[arraySz] == tombstone); - assert(r1[arraySz] == tombstone); - assert(r2[arraySz] == tombstone); - } - } - - void testMultibyteIncrementAssign() - { - auto arraySz = rndArraySz(); - - rndNum(r1, arraySz); - a[] = r1[]; - b[] = r1[]; - immutable uint carry = uniform!uint(); - // Add - auto c1 = multibyteIncrementAssign!('+')(a[0..arraySz], carry); - auto c2 = std.internal.math.biguintnoasm.multibyteIncrementAssign!('+')(b[0..arraySz], carry); - assert(a[0..arraySz] == b[0..arraySz]); - assert(c1 == c2); - assert(c1 == 0 || c1 == 1); - assert(a[arraySz] == tombstone); - assert(b[arraySz] == tombstone); - // Sub - a[] = r1[]; - b[] = r1[]; - c1 = multibyteIncrementAssign!('-')(a[0..arraySz], carry); - c2 = std.internal.math.biguintnoasm.multibyteIncrementAssign!('-')(b[0..arraySz], carry); - assert(a[0..arraySz] == b[0..arraySz]); - assert(c1 == c2); - assert(c1 == 0 || c1 == 1); - assert(a[arraySz] == tombstone); - assert(b[arraySz] == tombstone); - } - - void testMultibyteShl() - { - for (int i = 0; i < loopMax; i++) - { - auto arraySz = rndArraySz(); - - rndNum(a, arraySz); - initNum(r1); - initNum(r2); - immutable uint shift = uniform(1, 32); - auto sh1 = multibyteShl(r1[0..arraySz], a[0..arraySz], shift); - auto sh2 = std.internal.math.biguintnoasm.multibyteShl(r2[0..arraySz], a[0..arraySz], shift); - assert(r1[0..arraySz] == r2[0..arraySz]); - assert(sh1 == sh2); - assert(a[arraySz] == tombstone); - assert(r1[arraySz] == tombstone); - assert(r2[arraySz] == tombstone); - } - } - - void testMultibyteShr() - { - for (int i = 0; i < loopMax; i++) - { - auto arraySz = rndArraySz(); - - rndNum(a, arraySz); - initNum(r1); - initNum(r2); - immutable uint shift = uniform(1, 32); - multibyteShr(r1[0..arraySz], a[0..arraySz], shift); - std.internal.math.biguintnoasm.multibyteShr(r2[0..arraySz], a[0..arraySz], shift); - assert(r1[0..arraySz] == r2[0..arraySz]); - assert(a[arraySz] == tombstone); - assert(r1[arraySz] == tombstone); - assert(r2[arraySz] == tombstone); - } - } - - void testMultibyteMul() - { - for (int i = 0; i < loopMax; i++) - { - auto arraySz = rndArraySz(); - - rndNum(a, arraySz); - initNum(r1); - initNum(r2); - immutable uint mult = uniform!uint(); - immutable uint carry = uniform(0, 2); - auto c1 = multibyteMul(r1[0..arraySz], a[0..arraySz], mult, carry); - auto c2 = std.internal.math.biguintnoasm.multibyteMul(r2[0..arraySz], a[0..arraySz], mult, carry); - assert(r1[0..arraySz] == r2[0..arraySz]); - assert(c1 == c2); - assert(a[arraySz] == tombstone); - assert(r1[arraySz] == tombstone); - assert(r2[arraySz] == tombstone); - } - } - - void testMultibyteMulAdd() - { - for (int i = 0; i < loopMax; i++) - { - auto arraySz = rndArraySz(); - - rndNum(a, arraySz); - initNum(r1); - initNum(r2); - immutable uint mult = uniform!uint(); - immutable uint carry = uniform(0, 2); - // Add - auto c1 = multibyteMulAdd!('+')(r1[0..arraySz], a[0..arraySz], mult, carry); - auto c2 = std.internal.math.biguintnoasm.multibyteMulAdd!('+')(r2[0..arraySz], a[0..arraySz], mult, carry); - assert(r1[0..arraySz] == r2[0..arraySz]); - assert(c1 == c2); - assert(a[arraySz] == tombstone); - assert(r1[arraySz] == tombstone); - assert(r2[arraySz] == tombstone); - // Sub - c1 = multibyteMulAdd!('-')(r1[0..arraySz], a[0..arraySz], mult, carry); - c2 = std.internal.math.biguintnoasm.multibyteMulAdd!('-')(r2[0..arraySz], a[0..arraySz], mult, carry); - assert(r1[0..arraySz] == r2[0..arraySz]); - assert(c1 == c2); - assert(a[arraySz] == tombstone); - assert(r1[arraySz] == tombstone); - assert(r2[arraySz] == tombstone); - } - } - - void testMultibyteAddDiagonalSquares() - { - for (int i = 0; i < loopMax; i++) - { - auto arraySz = rndArraySz(maxArraySz/2); - - rndNum(a, arraySz); - initNum(r1); - initNum(r2); - multibyteAddDiagonalSquares(r1, a[0..arraySz]); - std.internal.math.biguintnoasm.multibyteAddDiagonalSquares(r2, a[0..arraySz]); - assert(r1[0..2*arraySz] == r2[0..2*arraySz]); - assert(a[arraySz] == tombstone); - assert(r1[2*arraySz+1] == tombstone); - assert(r2[2*arraySz+1] == tombstone); - } - } - - unittest - { - testMultibyteAddSub(); - testMultibyteIncrementAssign(); - testMultibyteShl(); - testMultibyteShr(); - testMultibyteMul(); - testMultibyteMulAdd(); - testMultibyteAddDiagonalSquares(); - } -} -//version = timings; -version (timings) -{ - static import std.internal.math.biguintnoasm; - import core.stdc.stdio : printf; - import core.time : TickDuration; - import std.datetime : StopWatch; - import std.random; - - void report(string name, TickDuration time1, TickDuration time2) - { - printf("Result for %.*s: Speedup = %.4f\n", name.length, name.ptr, cast(float)time2.hnsecs / cast(float)time1.hnsecs); - printf("Opt: %lld usec %lld nsec\n", cast(ulong)time1.usecs, cast(ulong)time1.hnsecs); - printf("Base: %lld usec %lld nsec\n", cast(ulong)time2.usecs, cast(ulong)time2.hnsecs); - } - - void main() - { - StopWatch sw; - immutable size_t loopCount = 10_000; - immutable size_t arraySz = 380; - immutable size_t dataSz = 1000; - uint[][] data = new uint[][dataSz]; - foreach (ref d; data) - { - d = new uint[arraySz]; - foreach (ref i; d) i = uniform!uint(); - } - uint[] r = new uint[arraySz]; - - // Warm-up - for (size_t i = 0; i < dataSz; i +=2) - { - auto c1 = multibyteAddSub!('+')(r, data[i], data[i+1], 0); - } - // Time - sw.start(); - for (size_t j = 0; j < loopCount; j++) - { - for (size_t i = 0; i < dataSz; i +=2) - { - auto c1 = multibyteAddSub!('+')(r, data[i], data[i+1], 0); - } - } - sw.stop(); - auto time1 = sw.peek(); - - // Warm-up - for (size_t i = 0; i < dataSz; i +=2) - { - auto c1 = std.internal.math.biguintnoasm.multibyteAddSub!('+')(r, data[i], data[i+1], 0); - } - // Time - sw.start(); - for (size_t j = 0; j < loopCount; j++) - { - for (size_t i = 0; i < dataSz; i +=2) - { - auto c1 = std.internal.math.biguintnoasm.multibyteAddSub!('+')(r, data[i], data[i+1], 0); - } - } - sw.stop(); - auto time2 = sw.peek(); - - // Print result - report("multibyteAddSub!('+')", time1, time2); - - // Warm-up - for (size_t i = 0; i < dataSz; i +=2) - { - auto c1 = multibyteMul(r, data[i], 17, 0); - } - // Time - sw.start(); - for (size_t j = 0; j < loopCount; j++) - { - for (size_t i = 0; i < dataSz; i +=2) - { - auto c1 = multibyteMul(r, data[i], 17, 0); - } - } - sw.stop(); - auto time3 = sw.peek(); - - // Warm-up - for (size_t i = 0; i < dataSz; i +=2) - { - auto c1 = std.internal.math.biguintnoasm.multibyteMul(r, data[i], 17, 0); - } - // Time - sw.start(); - for (size_t j = 0; j < loopCount; j++) - { - for (size_t i = 0; i < dataSz; i +=2) - { - auto c1 = std.internal.math.biguintnoasm.multibyteMul(r, data[i], 17, 0); - } - } - sw.stop(); - auto time4 = sw.peek(); - - // Print result - report("multibyteMul", time3, time4); - - // Warm-up - for (size_t i = 0; i < dataSz; i +=2) - { - auto c1 = multibyteMulAdd!('+')(r, data[i], 17, 0); - } - // Time - sw.start(); - for (size_t j = 0; j < loopCount; j++) - { - r[] = 0; - for (size_t i = 0; i < dataSz; i +=2) - { - auto c1 = multibyteMulAdd!('+')(r, data[i], 17, 0); - } - } - sw.stop(); - auto time5 = sw.peek(); - - // Warm-up - for (size_t i = 0; i < dataSz; i +=2) - { - auto c1 = std.internal.math.biguintnoasm.multibyteMulAdd!('+')(r, data[i], 17, 0); - } - // Time - sw.start(); - for (size_t j = 0; j < loopCount; j++) - { - r[] = 0; - for (size_t i = 0; i < dataSz; i +=2) - { - auto c1 = std.internal.math.biguintnoasm.multibyteMulAdd!('+')(r, data[i], 17, 0); - } - } - sw.stop(); - auto time6 = sw.peek(); - - // Print result - report("multibyteMulAdd!('+')", time5, time6); - } -} diff --git a/phobos/std/internal/math/biguintcore.d b/phobos/std/internal/math/biguintcore.d deleted file mode 100644 index f174795..0000000 --- a/phobos/std/internal/math/biguintcore.d +++ /dev/null @@ -1,2956 +0,0 @@ -/** Fundamental operations for arbitrary-precision arithmetic - * - * These functions are for internal use only. - */ -/* Copyright Don Clugston 2008 - 2010. - * 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) - */ -/* References: - "Modern Computer Arithmetic" (MCA) is the primary reference for all - algorithms used in this library. - - R.P. Brent and P. Zimmermann, "Modern Computer Arithmetic", - Version 0.5.9, (Oct 2010). - - C. Burkinel and J. Ziegler, "Fast Recursive Division", MPI-I-98-1-022, - Max-Planck Institute fuer Informatik, (Oct 1998). - - G. Hanrot, M. Quercia, and P. Zimmermann, "The Middle Product Algorithm, I.", - INRIA 4664, (Dec 2002). - - M. Bodrato and A. Zanoni, "What about Toom-Cook Matrices Optimality?", - http://bodrato.it/papers (2006). - - A. Fog, "Optimizing subroutines in assembly language", - www.agner.org/optimize (2008). - - A. Fog, "The microarchitecture of Intel and AMD CPU's", - www.agner.org/optimize (2008). - - A. Fog, "Instruction tables: Lists of instruction latencies, throughputs - and micro-operation breakdowns for Intel and AMD CPU's.", www.agner.org/optimize (2008). - -Idioms: - Many functions in this module use - 'func(Tulong)(Tulong x) if (is(Tulong == ulong))' rather than 'func(ulong x)' - in order to disable implicit conversion. - -*/ -module std.internal.math.biguintcore; - -version (D_InlineAsm_X86) -{ - static import std.internal.math.biguintx86; -} -version (LDC) -{ - version (D_InlineAsm_X86) - version = HaveAsmVersion; - - version (ARM) - { - version (ARM_Thumb) {} - else - { - static import std.internal.math.biguintarm; - version = LDC_ARM_asm; - version = HaveAsmVersion; - } - } -} -static import std.internal.math.biguintnoasm; - -import std.internal.math.biguintnoasm : BigDigit, KARATSUBALIMIT, - KARATSUBASQUARELIMIT; - -alias multibyteAdd = multibyteAddSub!('+'); -alias multibyteSub = multibyteAddSub!('-'); - -private import std.traits; -private import std.range.primitives; -public import std.ascii : LetterCase; -import std.range.primitives; -import std.traits; - -private: - -// dipatchers to the right low-level primitives. Added to allow BigInt CTFE for -// 32 bit systems (https://issues.dlang.org/show_bug.cgi?id=14767) although it's -// used by the other architectures too. -// See comments below in case it has to be refactored. -version (HaveAsmVersion) -uint multibyteAddSub(char op)(uint[] dest, const(uint)[] src1, const (uint)[] src2, uint carry) -{ - // must be checked before, otherwise D_InlineAsm_X86 is true. - if (__ctfe) - return std.internal.math.biguintnoasm.multibyteAddSub!op(dest, src1, src2, carry); - // Runtime. - else version (D_InlineAsm_X86) - return std.internal.math.biguintx86.multibyteAddSub!op(dest, src1, src2, carry); - else version (LDC_ARM_asm) - return std.internal.math.biguintarm.multibyteAddSub!op(dest, src1, src2, carry); - // Runtime if no asm available. - else - return std.internal.math.biguintnoasm.multibyteAddSub!op(dest, src1, src2, carry); -} -// Any other architecture -else alias multibyteAddSub = std.internal.math.biguintnoasm.multibyteAddSub; - -version (HaveAsmVersion) -uint multibyteIncrementAssign(char op)(uint[] dest, uint carry) -{ - if (__ctfe) - return std.internal.math.biguintnoasm.multibyteIncrementAssign!op(dest, carry); - else version (D_InlineAsm_X86) - return std.internal.math.biguintx86.multibyteIncrementAssign!op(dest, carry); - else version (LDC_ARM_asm) - return std.internal.math.biguintarm.multibyteIncrementAssign!op(dest, carry); - else - return std.internal.math.biguintnoasm.multibyteIncrementAssign!op(dest, carry); -} -else alias multibyteIncrementAssign = std.internal.math.biguintnoasm.multibyteIncrementAssign; - -version (HaveAsmVersion) -uint multibyteShl()(uint[] dest, const(uint)[] src, uint numbits) -{ - if (__ctfe) - return std.internal.math.biguintnoasm.multibyteShl(dest, src, numbits); - else version (D_InlineAsm_X86) - return std.internal.math.biguintx86.multibyteShl(dest, src, numbits); - else version (LDC_ARM_asm) - return std.internal.math.biguintarm.multibyteShl(dest, src, numbits); - else - return std.internal.math.biguintnoasm.multibyteShl(dest, src, numbits); -} -else alias multibyteShl = std.internal.math.biguintnoasm.multibyteShl; - -version (HaveAsmVersion) -void multibyteShr()(uint[] dest, const(uint)[] src, uint numbits) -{ - if (__ctfe) - std.internal.math.biguintnoasm.multibyteShr(dest, src, numbits); - else version (D_InlineAsm_X86) - std.internal.math.biguintx86.multibyteShr(dest, src, numbits); - else version (LDC_ARM_asm) - std.internal.math.biguintarm.multibyteShr(dest, src, numbits); - else - std.internal.math.biguintnoasm.multibyteShr(dest, src, numbits); -} -else alias multibyteShr = std.internal.math.biguintnoasm.multibyteShr; - -version (HaveAsmVersion) -uint multibyteMul()(uint[] dest, const(uint)[] src, uint multiplier, uint carry) -{ - if (__ctfe) - return std.internal.math.biguintnoasm.multibyteMul(dest, src, multiplier, carry); - else version (D_InlineAsm_X86) - return std.internal.math.biguintx86.multibyteMul(dest, src, multiplier, carry); - else version (LDC_ARM_asm) - return std.internal.math.biguintarm.multibyteMul(dest, src, multiplier, carry); - else - return std.internal.math.biguintnoasm.multibyteMul(dest, src, multiplier, carry); -} -else alias multibyteMul = std.internal.math.biguintnoasm.multibyteMul; - -version (HaveAsmVersion) -uint multibyteMulAdd(char op)(uint[] dest, const(uint)[] src, uint multiplier, uint carry) -{ - if (__ctfe) - return std.internal.math.biguintnoasm.multibyteMulAdd!op(dest, src, multiplier, carry); - else version (D_InlineAsm_X86) - return std.internal.math.biguintx86.multibyteMulAdd!op(dest, src, multiplier, carry); - else version (LDC_ARM_asm) - return std.internal.math.biguintarm.multibyteMulAdd!op(dest, src, multiplier, carry); - else - return std.internal.math.biguintnoasm.multibyteMulAdd!op(dest, src, multiplier, carry); -} -else alias multibyteMulAdd = std.internal.math.biguintnoasm.multibyteMulAdd; - -version (HaveAsmVersion) -void multibyteMultiplyAccumulate()(uint[] dest, const(uint)[] left, const(uint)[] right) -{ - if (__ctfe) - std.internal.math.biguintnoasm.multibyteMultiplyAccumulate(dest, left, right); - else version (D_InlineAsm_X86) - std.internal.math.biguintx86.multibyteMultiplyAccumulate(dest, left, right); - else version (LDC_ARM_asm) - std.internal.math.biguintarm.multibyteMultiplyAccumulate(dest, left, right); - else - std.internal.math.biguintnoasm.multibyteMultiplyAccumulate(dest, left, right); -} -else alias multibyteMultiplyAccumulate = std.internal.math.biguintnoasm.multibyteMultiplyAccumulate; - -version (HaveAsmVersion) -uint multibyteDivAssign()(uint[] dest, uint divisor, uint overflow) -{ - if (__ctfe) - return std.internal.math.biguintnoasm.multibyteDivAssign(dest, divisor, overflow); - else version (D_InlineAsm_X86) - return std.internal.math.biguintx86.multibyteDivAssign(dest, divisor, overflow); - else version (LDC_ARM_asm) - return std.internal.math.biguintarm.multibyteDivAssign(dest, divisor, overflow); - else - return std.internal.math.biguintnoasm.multibyteDivAssign(dest, divisor, overflow); -} -else alias multibyteDivAssign = std.internal.math.biguintnoasm.multibyteDivAssign; - -version (HaveAsmVersion) -void multibyteAddDiagonalSquares()(uint[] dest, const(uint)[] src) -{ - if (__ctfe) - std.internal.math.biguintnoasm.multibyteAddDiagonalSquares(dest, src); - else version (D_InlineAsm_X86) - std.internal.math.biguintx86.multibyteAddDiagonalSquares(dest, src); - else version (LDC_ARM_asm) - std.internal.math.biguintarm.multibyteAddDiagonalSquares(dest, src); - else - std.internal.math.biguintnoasm.multibyteAddDiagonalSquares(dest, src); -} -else alias multibyteAddDiagonalSquares = std.internal.math.biguintnoasm.multibyteAddDiagonalSquares; - -version (HaveAsmVersion) -void multibyteTriangleAccumulate()(uint[] dest, const(uint)[] x) -{ - if (__ctfe) - std.internal.math.biguintnoasm.multibyteTriangleAccumulate(dest, x); - else version (D_InlineAsm_X86) - std.internal.math.biguintx86.multibyteTriangleAccumulate(dest, x); - else version (LDC_ARM_asm) - std.internal.math.biguintarm.multibyteTriangleAccumulate(dest, x); - else - std.internal.math.biguintnoasm.multibyteTriangleAccumulate(dest, x); -} -else alias multibyteTriangleAccumulate = std.internal.math.biguintnoasm.multibyteTriangleAccumulate; - -version (HaveAsmVersion) -void multibyteSquare()(BigDigit[] result, const(BigDigit)[] x) -{ - if (__ctfe) - std.internal.math.biguintnoasm.multibyteSquare(result, x); - else version (D_InlineAsm_X86) - std.internal.math.biguintx86.multibyteSquare(result, x); - else version (LDC_ARM_asm) - std.internal.math.biguintarm.multibyteSquare(result, x); - else - std.internal.math.biguintnoasm.multibyteSquare(result, x); -} -else alias multibyteSquare = std.internal.math.biguintnoasm.multibyteSquare; - -// Limits for when to switch between algorithms. -// Half the size of the data cache. -@nogc nothrow pure @safe size_t getCacheLimit() -{ - import core.cpuid : dataCaches; - return dataCaches[0].size * 1024 / 2; -} -enum size_t FASTDIVLIMIT = 100; // crossover to recursive division - - -// These constants are used by shift operations -static if (BigDigit.sizeof == int.sizeof) -{ - enum { LG2BIGDIGITBITS = 5, BIGDIGITSHIFTMASK = 31 } - alias BIGHALFDIGIT = ushort; -} -else static if (BigDigit.sizeof == long.sizeof) -{ - alias BIGHALFDIGIT = uint; - enum { LG2BIGDIGITBITS = 6, BIGDIGITSHIFTMASK = 63 } -} -else static assert(0, "Unsupported BigDigit size"); - -import std.exception : assumeUnique; -import std.traits : isIntegral; -enum BigDigitBits = BigDigit.sizeof*8; -template maxBigDigits(T) -if (isIntegral!T) -{ - enum maxBigDigits = (T.sizeof+BigDigit.sizeof-1)/BigDigit.sizeof; -} - -static immutable BigDigit[] ZERO = [0]; -static immutable BigDigit[] ONE = [1]; -static immutable BigDigit[] TWO = [2]; -static immutable BigDigit[] TEN = [10]; - - -public: - -/// BigUint performs memory management and wraps the low-level calls. -struct BigUint -{ -private: - pure invariant() - { - assert( data.length >= 1 && (data.length == 1 || data[$-1] != 0 ), - "Invariant requires data to not empty or zero"); - } - - immutable(BigDigit) [] data = ZERO; - - this(return scope immutable(BigDigit) [] x) pure nothrow @nogc @safe - { - data = x; - } - package(std) // used from: std.bigint - this(T)(T x) pure nothrow @safe scope if (isIntegral!T) - { - opAssign(x); - } - - enum trustedAssumeUnique = function(BigDigit[] input) pure @trusted @nogc { - return assumeUnique(input); - }; -public: - // Length in uints - @property size_t uintLength() pure nothrow const @safe @nogc scope - { - static if (BigDigit.sizeof == uint.sizeof) - { - return data.length; - } - else static if (BigDigit.sizeof == ulong.sizeof) - { - return data.length * 2 - - ((data[$-1] & 0xFFFF_FFFF_0000_0000L) ? 1 : 0); - } - } - @property size_t ulongLength() pure nothrow const @safe @nogc scope - { - static if (BigDigit.sizeof == uint.sizeof) - { - return (data.length + 1) >> 1; - } - else static if (BigDigit.sizeof == ulong.sizeof) - { - return data.length; - } - } - - // The value at (cast(ulong[]) data)[n] - ulong peekUlong(size_t n) pure nothrow const @safe @nogc scope - { - static if (BigDigit.sizeof == int.sizeof) - { - if (data.length == n*2 + 1) return data[n*2]; - return data[n*2] + ((cast(ulong) data[n*2 + 1]) << 32 ); - } - else static if (BigDigit.sizeof == long.sizeof) - { - return data[n]; - } - } - - uint peekUint(size_t n) pure nothrow const @safe @nogc scope - { - static if (BigDigit.sizeof == int.sizeof) - { - return data[n]; - } - else - { - immutable x = data[n >> 1]; - return (n & 1) ? cast(uint)(x >> 32) : cast(uint) x; - } - } - - /// - void opAssign(Tulong)(Tulong u) pure nothrow @safe scope if (is (Tulong == ulong)) - { - if (u == 0) data = ZERO; - else if (u == 1) data = ONE; - else if (u == 2) data = TWO; - else if (u == 10) data = TEN; - else - { - static if (BigDigit.sizeof == int.sizeof) - { - uint ulo = cast(uint)(u & 0xFFFF_FFFF); - uint uhi = cast(uint)(u >> 32); - if (uhi == 0) - { - data = [ulo]; - } - else - { - data = [ulo, uhi]; - } - } - else static if (BigDigit.sizeof == long.sizeof) - { - data = [u]; - } - } - } - void opAssign(Tdummy = void)(BigUint y) pure nothrow @nogc @safe scope - { - this.data = y.data; - } - - /// - int opCmp(Tdummy = void)(const BigUint y) pure nothrow @nogc const @safe scope - { - if (data.length != y.data.length) - return (data.length > y.data.length) ? 1 : -1; - size_t k = highestDifferentDigit(data, y.data); - if (data[k] == y.data[k]) - return 0; - return data[k] > y.data[k] ? 1 : -1; - } - - /// - int opCmp(Tulong)(Tulong y) pure nothrow @nogc const @safe scope if (is (Tulong == ulong)) - { - if (data.length > maxBigDigits!Tulong) - return 1; - - foreach_reverse (i; 0 .. maxBigDigits!Tulong) - { - BigDigit tmp = cast(BigDigit)(y>>(i*BigDigitBits)); - if (tmp == 0) - if (data.length >= i+1) - { - // Since ZERO is [0], so we cannot simply return 1 here, as - // data[i] would be 0 for i == 0 in that case. - return (data[i] > 0) ? 1 : 0; - } - else - continue; - else - if (i+1 > data.length) - return -1; - else if (tmp != data[i]) - return data[i] > tmp ? 1 : -1; - } - return 0; - } - - bool opEquals(Tdummy = void)(ref const BigUint y) pure nothrow @nogc const @safe scope - { - return y.data[] == data[]; - } - - bool opEquals(Tdummy = void)(ulong y) pure nothrow @nogc const @safe scope - { - if (data.length > 2) - return false; - uint ylo = cast(uint)(y & 0xFFFF_FFFF); - uint yhi = cast(uint)(y >> 32); - if (data.length == 2 && data[1]!=yhi) - return false; - if (data.length == 1 && yhi != 0) - return false; - return (data[0] == ylo); - } - - bool isZero() pure const nothrow @safe @nogc scope - { - return data.length == 1 && data[0] == 0; - } - - size_t numBytes() pure nothrow const @safe @nogc scope - { - return data.length * BigDigit.sizeof; - } - - // the extra bytes are added to the start of the string - char [] toDecimalString(int frontExtraBytes) const pure nothrow @safe scope - { - immutable predictlength = 20+20*(data.length/2); // just over 19 - char [] buff = new char[frontExtraBytes + predictlength]; - ptrdiff_t sofar = biguintToDecimal(buff, data.dup); - return buff[sofar-frontExtraBytes..$]; - } - - /** Convert to a hex string, printing a minimum number of digits 'minPadding', - * allocating an additional 'frontExtraBytes' at the start of the string. - * Padding is done with padChar, which may be '0' or ' '. - * 'separator' is a digit separation character. If non-zero, it is inserted - * between every 8 digits. - * Separator characters do not contribute to the minPadding. - */ - char [] toHexString(int frontExtraBytes, char separator = 0, - int minPadding=0, char padChar = '0', - LetterCase letterCase = LetterCase.upper) const pure nothrow @safe scope - { - // Calculate number of extra padding bytes - size_t extraPad = (minPadding > data.length * 2 * BigDigit.sizeof) - ? minPadding - data.length * 2 * BigDigit.sizeof : 0; - - // Length not including separator bytes - size_t lenBytes = data.length * 2 * BigDigit.sizeof; - - // Calculate number of separator bytes - size_t mainSeparatorBytes = separator ? (lenBytes / 8) - 1 : 0; - immutable totalSeparatorBytes = separator ? ((extraPad + lenBytes + 7) / 8) - 1: 0; - - char [] buff = new char[lenBytes + extraPad + totalSeparatorBytes + frontExtraBytes]; - biguintToHex(buff[$ - lenBytes - mainSeparatorBytes .. $], data, separator, letterCase); - if (extraPad > 0) - { - if (separator) - { - size_t start = frontExtraBytes; // first index to pad - if (extraPad &7) - { - // Do 1 to 7 extra zeros. - buff[frontExtraBytes .. frontExtraBytes + (extraPad & 7)] = padChar; - buff[frontExtraBytes + (extraPad & 7)] = (padChar == ' ' ? ' ' : separator); - start += (extraPad & 7) + 1; - } - for (int i=0; i< (extraPad >> 3); ++i) - { - buff[start .. start + 8] = padChar; - buff[start + 8] = (padChar == ' ' ? ' ' : separator); - start += 9; - } - } - else - { - buff[frontExtraBytes .. frontExtraBytes + extraPad]=padChar; - } - } - int z = frontExtraBytes; - if (lenBytes > minPadding) - { - // Strip leading zeros. - ptrdiff_t maxStrip = lenBytes - minPadding; - while (z< buff.length-1 && (buff[z]=='0' || buff[z]==padChar) && maxStrip>0) - { - ++z; - --maxStrip; - } - } - if (padChar!='0') - { - // Convert leading zeros into padChars. - for (size_t k= z; k< buff.length-1 && (buff[k]=='0' || buff[k]==padChar); ++k) - { - if (buff[k]=='0') buff[k]=padChar; - } - } - return buff[z-frontExtraBytes..$]; - } - - /** - * Convert to an octal string. - */ - char[] toOctalString() pure nothrow @safe const scope - { - auto predictLength = 1 + data.length*BigDigitBits / 3; - char[] buff = new char[predictLength]; - size_t firstNonZero = biguintToOctal(buff, data); - return buff[firstNonZero .. $]; - } - - // return false if invalid character found - bool fromHexString(Range)(Range s) scope if ( - isBidirectionalRange!Range && isSomeChar!(ElementType!Range)) - { - import std.range : walkLength; - - //Strip leading zeros - while (!s.empty && s.front == '0') - s.popFront; - - if (s.empty) - { - data = ZERO; - return true; - } - - immutable len = (s.save.walkLength + 15) / 4; - auto tmp = new BigDigit[len + 1]; - uint part, sofar, partcount; - - foreach_reverse (character; s) - { - if (character == '_') - continue; - - uint x; - if (character >= '0' && character <= '9') - { - x = character - '0'; - } - else if (character >= 'A' && character <= 'F') - { - x = character - 'A' + 10; - } - else if (character >= 'a' && character <= 'f') - { - x = character - 'a' + 10; - } - else - { - return false; - } - - part >>= 4; - part |= (x << (32 - 4)); - ++partcount; - - if (partcount == 8) - { - tmp[sofar] = part; - ++sofar; - partcount = 0; - part = 0; - } - } - if (part) - { - for ( ; partcount != 8; ++partcount) part >>= 4; - tmp[sofar] = part; - ++sofar; - } - if (sofar == 0) - data = ZERO; - else - data = trustedAssumeUnique(tmp[0 .. sofar]); - - return true; - } - - // return true if OK; false if erroneous characters found - bool fromDecimalString(Range)(Range s) scope if ( - isForwardRange!Range && isSomeChar!(ElementType!Range)) - { - import std.range : walkLength; - - while (!s.empty && s.front == '0') - { - s.popFront; - } - - if (s.empty) - { - data = ZERO; - return true; - } - - auto predict_length = (18 * 2 + 2 * s.save.walkLength) / 19; - auto tmp = new BigDigit[predict_length]; - - tmp.length = biguintFromDecimal(tmp, s); - - data = trustedAssumeUnique(tmp); - return true; - } - - void fromMagnitude(Range)(Range magnitude) scope - if (isInputRange!Range - && (isForwardRange!Range || hasLength!Range) - && isUnsigned!(ElementType!Range)) - { - while (!magnitude.empty && magnitude.front == 0) - magnitude.popFront; - static if (hasLength!Range) - immutable inputLen = magnitude.length; - else - immutable inputLen = magnitude.save.walkLength; - if (!inputLen) - { - this.data = ZERO; - return; - } - // `magnitude` has its most significant element first but BigUint.data - // stores the most significant last. - BigDigit[] newDigits; - alias E = ElementType!Range; - static if (E.sizeof == BigDigit.sizeof) - { - newDigits = new BigDigit[inputLen]; - foreach_reverse (ref digit; newDigits) - { - digit = magnitude.front; - magnitude.popFront(); - } - } - else static if (E.sizeof < BigDigit.sizeof) - { - enum elementsPerDigit = BigDigit.sizeof / E.sizeof; - newDigits = new BigDigit[(inputLen + elementsPerDigit - 1) / elementsPerDigit]; - immutable remainder = inputLen % elementsPerDigit; - // If there is a remainder specially assemble the most significant digit. - if (remainder) - { - BigDigit tmp = magnitude.front; - magnitude.popFront(); - foreach (_; 1 .. remainder) - { - tmp = (tmp << (E.sizeof * 8)) | magnitude.front; - magnitude.popFront(); - } - newDigits[$-1] = tmp; - } - // Assemble full digits from most to least significant. - foreach_reverse (ref digit; newDigits[0 .. $ - int(remainder != 0)]) - { - BigDigit tmp; - static foreach (n; 0 .. elementsPerDigit) - { - tmp |= cast(BigDigit) magnitude.front << - ((BigDigit.sizeof - (E.sizeof * (n + 1))) * 8); - magnitude.popFront(); - } - digit = tmp; - } - } - else static if (E.sizeof > BigDigit.sizeof) - { - enum digitsPerElement = E.sizeof / BigDigit.sizeof; - newDigits = new BigDigit[inputLen * digitsPerElement]; - size_t i = newDigits.length - 1; - foreach (element; magnitude) - { - static foreach (n; 0 .. digitsPerElement) - newDigits[i - n] = - cast(BigDigit) (element >> ((E.sizeof - (BigDigit.sizeof * (n + 1))) * 8)); - i -= digitsPerElement; - } - while (newDigits[$-1] == 0) - newDigits = newDigits[0 .. $-1]; - } - else - static assert(0); - this.data = trustedAssumeUnique(newDigits); - return; - } - - nothrow pure @safe unittest - { - immutable BigDigit[] referenceData = [BigDigit(0x2003_4005), 0x6007_8009, 0xABCD]; - // Internal representation is most-significant-last but `fromMagnitude` - // argument is most-significant-first. - immutable BigDigit[] referenceMagnitude = [BigDigit(0xABCD), 0x6007_8009, 0x2003_4005]; - BigUint b; - // Test with reference magnitude. - b.fromMagnitude(referenceMagnitude); - assert(b.data == referenceData); - // Test ubyte array. - import std.bitmanip : nativeToBigEndian; - ubyte[] ubyteMagnitude = nativeToBigEndian(referenceMagnitude[0]) ~ - nativeToBigEndian(referenceMagnitude[1]) ~ - nativeToBigEndian(referenceMagnitude[2]); - b.data = ZERO; - b.fromMagnitude(ubyteMagnitude); - assert(b.data == referenceData); - // Test ulong array. - static if (BigDigit.sizeof == uint.sizeof) - immutable(ulong)[] ulongMagnitude = [ulong(referenceMagnitude[0]), - ((cast(ulong) referenceMagnitude[1]) << 32) | referenceMagnitude[2], - ]; - else static if (BigDigit.sizeof == ulong.sizeof) - alias ulongMagnitude = referenceMagnitude; - b.data = ZERO; - b.fromMagnitude(ulongMagnitude); - assert(b.data == referenceData); - } - - //////////////////////// - // - // All of these member functions create a new BigUint. - - // return x >> y - BigUint opBinary(string op, Tulong)(Tulong y) pure nothrow @safe const return scope - if (op == ">>" && is (Tulong == ulong)) - { - assert(y > 0, "Can not right shift BigUint by 0"); - uint bits = cast(uint) y & BIGDIGITSHIFTMASK; - if ((y >> LG2BIGDIGITBITS) >= data.length) return BigUint(ZERO); - uint words = cast(uint)(y >> LG2BIGDIGITBITS); - if (bits == 0) - { - return BigUint(data[words..$]); - } - else - { - uint [] result = new BigDigit[data.length - words]; - multibyteShr(result, data[words..$], bits); - - if (result.length > 1 && result[$-1] == 0) - return BigUint(trustedAssumeUnique(result[0 .. $-1])); - else - return BigUint(trustedAssumeUnique(result)); - } - } - - // return x << y - BigUint opBinary(string op, Tulong)(Tulong y) pure nothrow @safe const scope - if (op == "<<" && is (Tulong == ulong)) - { - assert(y > 0, "Can not left shift BigUint by 0"); - if (isZero()) return this; - uint bits = cast(uint) y & BIGDIGITSHIFTMASK; - assert((y >> LG2BIGDIGITBITS) < cast(ulong)(uint.max), - "Shift result exceeds temporary store"); - uint words = cast(uint)(y >> LG2BIGDIGITBITS); - BigDigit [] result = new BigDigit[data.length + words+1]; - result[0 .. words] = 0; - if (bits == 0) - { - result[words .. words+data.length] = data[]; - return BigUint(trustedAssumeUnique(result[0 .. words+data.length])); - } - else - { - immutable c = multibyteShl(result[words .. words+data.length], data, bits); - if (c == 0) return BigUint(trustedAssumeUnique(result[0 .. words+data.length])); - result[$-1] = c; - return BigUint(trustedAssumeUnique(result)); - } - } - - // If wantSub is false, return x + y, leaving sign unchanged - // If wantSub is true, return abs(x - y), negating sign if x < y - static BigUint addOrSubInt(Tulong)(const scope BigUint x, Tulong y, - bool wantSub, ref bool sign) pure nothrow @safe if (is(Tulong == ulong)) - { - BigUint r; - if (wantSub) - { // perform a subtraction - if (x.data.length > 2) - { - // subInt returns GC allocated array, can be safely cast to immutable - r.data = (() @trusted => cast(immutable) subInt(x.data, y))(); - } - else - { // could change sign! - ulong xx = x.data[0]; - if (x.data.length > 1) - xx += (cast(ulong) x.data[1]) << 32; - ulong d; - if (xx <= y) - { - d = y - xx; - sign = !sign; - } - else - { - d = xx - y; - } - if (d == 0) - { - r = 0UL; - sign = false; - return r; - } - if (d > uint.max) - { - r.data = [cast(uint)(d & 0xFFFF_FFFF), cast(uint)(d >> 32)]; - } - else - { - r.data = [cast(uint)(d & 0xFFFF_FFFF)]; - } - } - } - else - { - // addInt returns GC allocated array, can be safely cast to immutable - r.data = (() @trusted => cast(immutable) addInt(x.data, y))(); - } - return r; - } - - // If wantSub is false, return x + y, leaving sign unchanged. - // If wantSub is true, return abs(x - y), negating sign if x cast(immutable) sub(x.data, y.data, &negative))(); - sign ^= negative; - if (r.isZero()) - { - sign = false; - } - } - else - { - // add returns GC allocated array, can be safely cast to immutable - r.data = (() @trusted => cast(immutable) add(x.data, y.data))(); - } - return r; - } - - - // return x*y. - static BigUint mulInt(T = ulong)(BigUint x, T y) pure nothrow @safe - { - if (y == 0 || x == 0) return BigUint(ZERO); - static if (T.sizeof * 8 <= 32) - uint hi = 0; - else - uint hi = cast(uint) (y >>> 32); - uint lo = cast(uint) (y & 0xFFFF_FFFF); - uint [] result = new BigDigit[x.data.length+1+(hi != 0)]; - result[x.data.length] = multibyteMul(result[0 .. x.data.length], x.data, lo, 0); - if (hi != 0) - { - result[x.data.length+1] = multibyteMulAdd!('+')(result[1 .. x.data.length+1], - x.data, hi, 0); - } - return BigUint(removeLeadingZeros(trustedAssumeUnique(result))); - } - - /* return x * y. - */ - static BigUint mul(scope BigUint x, scope BigUint y) pure nothrow @safe - { - if (y == 0 || x == 0) - return BigUint(ZERO); - auto len = x.data.length + y.data.length; - BigDigit [] result = new BigDigit[len]; - if (y.data.length > x.data.length) - { - mulInternal(result, y.data, x.data); - } - else - { - if (x.data[]==y.data[]) squareInternal(result, x.data); - else mulInternal(result, x.data, y.data); - } - // the highest element could be zero, - // in which case we need to reduce the length - return BigUint(removeLeadingZeros(trustedAssumeUnique(result))); - } - - // return x / y - static BigUint divInt(T)(return scope BigUint x, T y_) pure nothrow @safe - if ( is(immutable T == immutable uint) ) - { - uint y = y_; - if (y == 1) - return x; - uint [] result = new BigDigit[x.data.length]; - if ((y&(-y))==y) - { - assert(y != 0, "BigUint division by zero"); - // perfect power of 2 - uint b = 0; - for (;y != 1; y>>=1) - { - ++b; - } - multibyteShr(result, x.data, b); - } - else - { - result[] = x.data[]; - cast(void) multibyteDivAssign(result, y, 0); - } - return BigUint(removeLeadingZeros(trustedAssumeUnique(result))); - } - - static BigUint divInt(T)(scope BigUint x, T y) pure nothrow @safe - if ( is(immutable T == immutable ulong) ) - { - if (y <= uint.max) - return divInt!uint(x, cast(uint) y); - if (x.data.length < 2) - return BigUint(ZERO); - uint hi = cast(uint)(y >>> 32); - uint lo = cast(uint)(y & 0xFFFF_FFFF); - immutable uint[2] z = [lo, hi]; - BigDigit[] result = new BigDigit[x.data.length - z.length + 1]; - divModInternal(result, null, x.data, z[]); - return BigUint(removeLeadingZeros(trustedAssumeUnique(result))); - } - - // return x % y - static uint modInt(T)(scope BigUint x, T y_) pure if ( is(immutable T == immutable uint) ) - { - import core.memory : GC; - uint y = y_; - assert(y != 0, "% 0 not allowed"); - if ((y&(-y)) == y) - { // perfect power of 2 - return x.data[0] & (y-1); - } - else - { - // horribly inefficient - malloc, copy, & store are unnecessary. - uint [] wasteful = new BigDigit[x.data.length]; - wasteful[] = x.data[]; - immutable rem = multibyteDivAssign(wasteful, y, 0); - () @trusted { GC.free(wasteful.ptr); } (); - return rem; - } - } - - // return x / y - static BigUint div(return scope BigUint x, scope BigUint y) pure nothrow @safe - { - if (y.data.length > x.data.length) - return BigUint(ZERO); - if (y.data.length == 1) - return divInt(x, y.data[0]); - BigDigit [] result = new BigDigit[x.data.length - y.data.length + 1]; - divModInternal(result, null, x.data, y.data); - return BigUint(removeLeadingZeros(trustedAssumeUnique(result))); - } - - // return x % y - static BigUint mod(return scope BigUint x, scope BigUint y) pure nothrow @safe - { - if (y.data.length > x.data.length) return x; - if (y.data.length == 1) - { - return BigUint([modInt(x, y.data[0])]); - } - BigDigit [] result = new BigDigit[x.data.length - y.data.length + 1]; - BigDigit [] rem = new BigDigit[y.data.length]; - divModInternal(result, rem, x.data, y.data); - return BigUint(removeLeadingZeros(trustedAssumeUnique(rem))); - } - - // Return x / y in quotient, x % y in remainder - static void divMod(BigUint x, scope BigUint y, - out BigUint quotient, out BigUint remainder) pure nothrow @safe - { - /* TODO Qualify parameter `x` as `return` when it applies to `out` parameters */ - if (y.data.length > x.data.length) - { - quotient = 0uL; - remainder = x; - } - else if (y.data.length == 1) - { - quotient = divInt(x, y.data[0]); - remainder = BigUint([modInt(x, y.data[0])]); - } - else - { - BigDigit[] quot = new BigDigit[x.data.length - y.data.length + 1]; - BigDigit[] rem = new BigDigit[y.data.length]; - divModInternal(quot, rem, x.data, y.data); - quotient = BigUint(removeLeadingZeros(trustedAssumeUnique(quot))); - remainder = BigUint(removeLeadingZeros(trustedAssumeUnique(rem))); - } - } - - // return x op y - static BigUint bitwiseOp(string op)(scope BigUint x, scope BigUint y, bool xSign, bool ySign, ref bool resultSign) - pure nothrow @safe if (op == "|" || op == "^" || op == "&") - { - auto d1 = includeSign(x.data, y.uintLength, xSign); - auto d2 = includeSign(y.data, x.uintLength, ySign); - - foreach (i; 0 .. d1.length) - { - mixin("d1[i] " ~ op ~ "= d2[i];"); - } - - mixin("resultSign = xSign " ~ op ~ " ySign;"); - - if (resultSign) - { - twosComplement(d1, d1); - } - - return BigUint(removeLeadingZeros(trustedAssumeUnique(d1))); - } - - /** - * Return a BigUint which is x raised to the power of y. - * Method: Powers of 2 are removed from x, then left-to-right binary - * exponentiation is used. - * Memory allocation is minimized: at most one temporary BigUint is used. - */ - static BigUint pow(return scope BigUint x, ulong y) pure nothrow @safe - { - // Deal with the degenerate cases first. - if (y == 0) return BigUint(ONE); - if (y == 1) return x; - if (x == 0 || x == 1) return x; - - BigUint result; - - // Simplify, step 1: Remove all powers of 2. - uint firstnonzero = firstNonZeroDigit(x.data); - // Now we know x = x[firstnonzero..$] * (2^^(firstnonzero*BigDigitBits)) - // where BigDigitBits = BigDigit.sizeof * 8 - - // See if x[firstnonzero..$] can now fit into a single digit. - bool singledigit = ((x.data.length - firstnonzero) == 1); - // If true, then x0 is that digit - // and the result will be (x0 ^^ y) * (2^^(firstnonzero*y*BigDigitBits)) - BigDigit x0 = x.data[firstnonzero]; - assert(x0 != 0, "pow(0, y) not allowed"); - // Length of the non-zero portion - size_t nonzerolength = x.data.length - firstnonzero; - ulong y0; - uint evenbits = 0; // number of even bits in the bottom of x - while (!(x0 & 1)) - { - x0 >>= 1; - ++evenbits; - } - - if (x.data.length- firstnonzero == 2) - { - // Check for a single digit straddling a digit boundary - const BigDigit x1 = x.data[firstnonzero+1]; - if ((x1 >> evenbits) == 0) - { - x0 |= (x1 << (BigDigit.sizeof * 8 - evenbits)); - singledigit = true; - } - } - // Now if (singledigit), x^^y = (x0 ^^ y) * 2^^(evenbits * y) * 2^^(firstnonzero*y*BigDigitBits)) - - uint evenshiftbits = 0; // Total powers of 2 to shift by, at the end - - // Simplify, step 2: For singledigits, see if we can trivially reduce y - - BigDigit finalMultiplier = 1UL; - - if (singledigit) - { - // x fits into a single digit. Raise it to the highest power we can - // that still fits into a single digit, then reduce the exponent accordingly. - // We're quite likely to have a residual multiply at the end. - // For example, 10^^100 = (((5^^13)^^7) * 5^^9) * 2^^100. - // and 5^^13 still fits into a uint. - evenshiftbits = cast(uint)( (evenbits * y) & BIGDIGITSHIFTMASK); - if (x0 == 1) - { // Perfect power of 2 - result = 1UL; - return result << (evenbits + firstnonzero * 8 * BigDigit.sizeof) * y; - } - immutable p = highestPowerBelowUintMax(x0); - if (y <= p) - { // Just do it with pow - result = cast(ulong) intpow(x0, y); - if (evenbits + firstnonzero == 0) - return result; - return result << (evenbits + firstnonzero * 8 * BigDigit.sizeof) * y; - } - y0 = y / p; - finalMultiplier = intpow(x0, y - y0*p); - x0 = intpow(x0, p); - // Result is x0 - nonzerolength = 1; - } - // Now if (singledigit), x^^y = finalMultiplier * (x0 ^^ y0) * 2^^(evenbits * y) * 2^^(firstnonzero*y*BigDigitBits)) - - // Perform a crude check for overflow and allocate result buffer. - // The length required is y * lg2(x) bits. - // which will always fit into y*x.length digits. But this is - // a gross overestimate if x is small (length 1 or 2) and the highest - // digit is nearly empty. - // A better estimate is: - // y * lg2(x[$-1]/BigDigit.max) + y * (x.length - 1) digits, - // and the first term is always between - // y * (bsr(x.data[$-1]) + 1) / BIGDIGITBITS and - // y * (bsr(x.data[$-1]) + 2) / BIGDIGITBITS - // For single digit payloads, we already have - // x^^y = finalMultiplier * (x0 ^^ y0) * 2^^(evenbits * y) * 2^^(firstnonzero*y*BigDigitBits)) - // and x0 is almost a full digit, so it's a tight estimate. - // Number of digits is therefore 1 + x0.length*y0 + (evenbits*y)/BIGDIGIT + firstnonzero*y - // Note that the divisions must be rounded up. - - // Estimated length in BigDigits - immutable estimatelength = singledigit - ? 1 + y0 + ((evenbits*y + BigDigit.sizeof * 8 - 1) / (BigDigit.sizeof *8)) + firstnonzero*y - : x.data.length * y; - // Imprecise check for overflow. Makes the extreme cases easier to debug - // (less extreme overflow will result in an out of memory error). - if (estimatelength > uint.max/(4*BigDigit.sizeof)) - assert(0, "Overflow in BigInt.pow"); - - // The result buffer includes space for all the trailing zeros - BigDigit [] resultBuffer = new BigDigit[cast(size_t) estimatelength]; - - // Do all the powers of 2! - size_t result_start = cast(size_t)( firstnonzero * y - + (singledigit ? ((evenbits * y) >> LG2BIGDIGITBITS) : 0)); - - resultBuffer[0 .. result_start] = 0; - BigDigit [] t1 = resultBuffer[result_start..$]; - BigDigit [] r1; - - if (singledigit) - { - r1 = t1[0 .. 1]; - r1[0] = x0; - y = y0; - } - else - { - // It's not worth right shifting by evenbits unless we also shrink the length after each - // multiply or squaring operation. That might still be worthwhile for large y. - r1 = t1[0 .. x.data.length - firstnonzero]; - r1[0..$] = x.data[firstnonzero..$]; - } - - if (y>1) - { // Set r1 = r1 ^^ y. - // The secondary buffer only needs space for the multiplication results - BigDigit [] t2 = new BigDigit[resultBuffer.length - result_start]; - BigDigit [] r2; - - int shifts = 63; // num bits in a long - while (!(y & 0x8000_0000_0000_0000L)) - { - y <<= 1; - --shifts; - } - y <<=1; - - while (y != 0) - { - // For each bit of y: Set r1 = r1 * r1 - // If the bit is 1, set r1 = r1 * x - // Eg, if y is 0b101, result = ((x^^2)^^2)*x == x^^5. - // Optimization opportunity: if more than 2 bits in y are set, - // it's usually possible to reduce the number of multiplies - // by caching odd powers of x. eg for y = 54, - // (0b110110), set u = x^^3, and result is ((u^^8)*u)^^2 - r2 = t2[0 .. r1.length*2]; - squareInternal(r2, r1); - if (y & 0x8000_0000_0000_0000L) - { - r1 = t1[0 .. r2.length + nonzerolength]; - if (singledigit) - { - r1[$-1] = multibyteMul(r1[0 .. $-1], r2, x0, 0); - } - else - { - mulInternal(r1, r2, x.data[firstnonzero..$]); - } - } - else - { - r1 = t1[0 .. r2.length]; - r1[] = r2[]; - } - y <<=1; - shifts--; - } - while (shifts>0) - { - r2 = t2[0 .. r1.length * 2]; - squareInternal(r2, r1); - r1 = t1[0 .. r2.length]; - r1[] = r2[]; - --shifts; - } - } - - if (finalMultiplier != 1) - { - const BigDigit carry = multibyteMul(r1, r1, finalMultiplier, 0); - if (carry) - { - r1 = t1[0 .. r1.length + 1]; - r1[$-1] = carry; - } - } - if (evenshiftbits) - { - const BigDigit carry = multibyteShl(r1, r1, evenshiftbits); - if (carry != 0) - { - r1 = t1[0 .. r1.length + 1]; - r1[$ - 1] = carry; - } - } - while (r1[$ - 1]==0) - { - r1=r1[0 .. $ - 1]; - } - return BigUint(trustedAssumeUnique(resultBuffer[0 .. result_start + r1.length])); - } - - // Implement toHash so that BigUint works properly as an AA key. - size_t toHash() const @nogc nothrow pure @safe scope - { - return .hashOf(data); - } - -} // end BigUint - -@safe pure nothrow unittest -{ - // ulong comparison test - BigUint a = [1]; - assert(a == 1); - // https://issues.dlang.org/show_bug.cgi?id=9548 - assert(a < 0x8000_0000_0000_0000UL); - - // https://issues.dlang.org/show_bug.cgi?id=12234 - BigUint z = [0]; - assert(z == 0UL); - assert(!(z > 0UL)); - assert(!(z < 0UL)); -} - -// https://issues.dlang.org/show_bug.cgi?id=16223 -@system pure nothrow unittest -{ - BigUint a = [3]; - int b = 5; - assert(BigUint.mulInt(a,b) == 15); -} - -// Remove leading zeros from x, to restore the BigUint invariant -inout(BigDigit) [] removeLeadingZeros(return scope inout(BigDigit) [] x) pure nothrow @safe -{ - size_t k = x.length; - while (k>1 && x[k - 1]==0) --k; - return x[0 .. k]; -} - -pure @safe unittest -{ - BigUint r = BigUint([5]); - BigUint t = BigUint([7]); - BigUint s = BigUint.mod(r, t); - assert(s == 5); -} - - -@safe pure unittest -{ - BigUint r; - r = 5UL; - assert(r.peekUlong(0) == 5UL); - assert(r.peekUint(0) == 5U); - r = 0x1234_5678_9ABC_DEF0UL; - assert(r.peekUlong(0) == 0x1234_5678_9ABC_DEF0UL); - assert(r.peekUint(0) == 0x9ABC_DEF0U); -} - - -// Pow tests -pure @safe unittest -{ - BigUint r, s; - r.fromHexString("80000000_00000001"); - s = BigUint.pow(r, 5); - r.fromHexString("08000000_00000000_50000000_00000001_40000000_00000002_80000000" - ~ "_00000002_80000000_00000001"); - assert(s == r); - s = 10UL; - s = BigUint.pow(s, 39); - r.fromDecimalString("1000000000000000000000000000000000000000"); - assert(s == r); - r.fromHexString("1_E1178E81_00000000"); - s = BigUint.pow(r, 15); // Regression test: this used to overflow array bounds - - r.fromDecimalString("000_000_00"); - assert(r == 0); - r.fromDecimalString("0007"); - assert(r == 7); - r.fromDecimalString("0"); - assert(r == 0); -} - -// Radix conversion tests -@safe pure unittest -{ - BigUint r; - r.fromHexString("1_E1178E81_00000000"); - assert(r.toHexString(0, '_', 0) == "1_E1178E81_00000000"); - assert(r.toHexString(0, '_', 20) == "0001_E1178E81_00000000"); - assert(r.toHexString(0, '_', 16+8) == "00000001_E1178E81_00000000"); - assert(r.toHexString(0, '_', 16+9) == "0_00000001_E1178E81_00000000"); - assert(r.toHexString(0, '_', 16+8+8) == "00000000_00000001_E1178E81_00000000"); - assert(r.toHexString(0, '_', 16+8+8+1) == "0_00000000_00000001_E1178E81_00000000"); - assert(r.toHexString(0, '_', 16+8+8+1, ' ') == " 1_E1178E81_00000000"); - assert(r.toHexString(0, 0, 16+8+8+1) == "00000000000000001E1178E8100000000"); - r = 0UL; - assert(r.toHexString(0, '_', 0) == "0"); - assert(r.toHexString(0, '_', 7) == "0000000"); - assert(r.toHexString(0, '_', 7, ' ') == " 0"); - assert(r.toHexString(0, '#', 9) == "0#00000000"); - assert(r.toHexString(0, 0, 9) == "000000000"); -} - -// -@safe pure unittest -{ - BigUint r; - r.fromHexString("1_E1178E81_00000000"); - assert(r.toHexString(0, '_', 0, '0', LetterCase.lower) == "1_e1178e81_00000000"); - assert(r.toHexString(0, '_', 20, '0', LetterCase.lower) == "0001_e1178e81_00000000"); - assert(r.toHexString(0, '_', 16+8, '0', LetterCase.lower) == "00000001_e1178e81_00000000"); - assert(r.toHexString(0, '_', 16+9, '0', LetterCase.lower) == "0_00000001_e1178e81_00000000"); - assert(r.toHexString(0, '_', 16+8+8, '0', LetterCase.lower) == "00000000_00000001_e1178e81_00000000"); - assert(r.toHexString(0, '_', 16+8+8+1, '0', LetterCase.lower) == "0_00000000_00000001_e1178e81_00000000"); - assert(r.toHexString(0, '_', 16+8+8+1, ' ', LetterCase.lower) == " 1_e1178e81_00000000"); - assert(r.toHexString(0, 0, 16+8+8+1, '0', LetterCase.lower) == "00000000000000001e1178e8100000000"); - r = 0UL; - assert(r.toHexString(0, '_', 0, '0', LetterCase.lower) == "0"); - assert(r.toHexString(0, '_', 7, '0', LetterCase.lower) == "0000000"); - assert(r.toHexString(0, '_', 7, ' ', LetterCase.lower) == " 0"); - assert(r.toHexString(0, '#', 9, '0', LetterCase.lower) == "0#00000000"); - assert(r.toHexString(0, 'Z', 9, '0', LetterCase.lower) == "0Z00000000"); - assert(r.toHexString(0, 0, 9, '0', LetterCase.lower) == "000000000"); -} - - -private: -void twosComplement(const(BigDigit) [] x, BigDigit[] result) -pure nothrow @safe -{ - foreach (i; 0 .. x.length) - { - result[i] = ~x[i]; - } - result[x.length..$] = BigDigit.max; - - foreach (i; 0 .. result.length) - { - if (result[i] == BigDigit.max) - { - result[i] = 0; - } - else - { - result[i] += 1; - break; - } - } -} - -// Encode BigInt as BigDigit array (sign and 2's complement) -BigDigit[] includeSign(scope const(BigDigit) [] x, size_t minSize, bool sign) -pure nothrow @safe -{ - size_t length = (x.length > minSize) ? x.length : minSize; - BigDigit [] result = new BigDigit[length]; - if (sign) - { - twosComplement(x, result); - } - else - { - result[0 .. x.length] = x; - } - return result; -} - -// works for any type -T intpow(T)(T x, ulong n) pure nothrow @safe -{ - T p; - - switch (n) - { - case 0: - p = 1; - break; - - case 1: - p = x; - break; - - case 2: - p = x * x; - break; - - default: - p = 1; - while (1) - { - if (n & 1) - p *= x; - n >>= 1; - if (!n) - break; - x *= x; - } - break; - } - return p; -} - - -// returns the maximum power of x that will fit in a uint. -int highestPowerBelowUintMax(uint x) pure nothrow @safe -{ - assert(x > 1, "x must be greater than 1"); - static immutable ubyte [22] maxpwr = [ 31, 20, 15, 13, 12, 11, 10, 10, 9, 9, - 8, 8, 8, 8, 7, 7, 7, 7, 7, 7, 7, 7]; - if (x<24) return maxpwr[x-2]; - if (x<41) return 6; - if (x<85) return 5; - if (x<256) return 4; - if (x<1626) return 3; - if (x<65_536) return 2; - return 1; -} - -// returns the maximum power of x that will fit in a ulong. -int highestPowerBelowUlongMax(uint x) pure nothrow @safe -{ - assert(x > 1, "x must be greater than 1"); - static immutable ubyte [39] maxpwr = [ 63, 40, 31, 27, 24, 22, 21, 20, 19, 18, - 17, 17, 16, 16, 15, 15, 15, 15, 14, 14, - 14, 14, 13, 13, 13, 13, 13, 13, 13, 12, - 12, 12, 12, 12, 12, 12, 12, 12, 12]; - if (x<41) return maxpwr[x-2]; - if (x<57) return 11; - if (x<85) return 10; - if (x<139) return 9; - if (x<256) return 8; - if (x<566) return 7; - if (x<1626) return 6; - if (x<7132) return 5; - if (x<65_536) return 4; - if (x<2_642_246) return 3; - return 2; -} - -version (StdUnittest) -{ - -private int slowHighestPowerBelowUintMax(uint x) pure nothrow @safe -{ - int pwr = 1; - for (ulong q = x;x*q < cast(ulong) uint.max; ) - { - q*=x; ++pwr; - } - return pwr; -} - -@safe pure unittest -{ - assert(highestPowerBelowUintMax(10)==9); - for (int k=82; k<88; ++k) - { - assert(highestPowerBelowUintMax(k)== slowHighestPowerBelowUintMax(k)); - } -} - -} - - -/* General unsigned subtraction routine for bigints. - * Sets result = x - y. If the result is negative, negative will be true. - * Returns: - * unique memory - */ -BigDigit [] sub(const scope BigDigit [] x, const scope BigDigit [] y, bool *negative) -pure nothrow @safe -{ - if (x.length == y.length) - { - // There's a possibility of cancellation, if x and y are almost equal. - ptrdiff_t last = highestDifferentDigit(x, y); - BigDigit [] result = new BigDigit[last+1]; - if (x[last] < y[last]) - { // we know result is negative - multibyteSub(result[0 .. last+1], y[0 .. last+1], x[0 .. last+1], 0); - *negative = true; - } - else - { // positive or zero result - multibyteSub(result[0 .. last+1], x[0 .. last+1], y[0 .. last+1], 0); - *negative = false; - } - while (result.length > 1 && result[$-1] == 0) - { - result = result[0..$-1]; - } -// if (result.length >1 && result[$-1]==0) return result[0..$-1]; - return result; - } - // Lengths are different - const(BigDigit) [] large, small; - if (x.length < y.length) - { - *negative = true; - large = y; small = x; - } - else - { - *negative = false; - large = x; small = y; - } - // result.length will be equal to larger length, or could decrease by 1. - - - BigDigit [] result = new BigDigit[large.length]; - BigDigit carry = multibyteSub(result[0 .. small.length], large[0 .. small.length], small, 0); - result[small.length..$] = large[small.length..$]; - if (carry) - { - multibyteIncrementAssign!('-')(result[small.length..$], carry); - } - while (result.length > 1 && result[$-1] == 0) - { - result = result[0..$-1]; - } - return result; -} - - -/* - * return a + b - * Returns: - * unique memory - */ -BigDigit [] add(const scope BigDigit [] a, const scope BigDigit [] b) pure nothrow @safe -{ - const(BigDigit) [] x, y; - if (a.length < b.length) - { - x = b; y = a; - } - else - { - x = a; y = b; - } - // now we know x.length > y.length - // create result. add 1 in case it overflows - BigDigit [] result = new BigDigit[x.length + 1]; - - BigDigit carry = multibyteAdd(result[0 .. y.length], x[0 .. y.length], y, 0); - if (x.length != y.length) - { - result[y.length..$-1]= x[y.length..$]; - carry = multibyteIncrementAssign!('+')(result[y.length..$-1], carry); - } - if (carry) - { - result[$-1] = carry; - return result; - } - else - return result[0..$-1]; -} - -/** return x + y - */ -BigDigit [] addInt(const BigDigit[] x, ulong y) @safe pure nothrow -{ - uint hi = cast(uint)(y >>> 32); - uint lo = cast(uint)(y& 0xFFFF_FFFF); - auto len = x.length; - if (x.length < 2 && hi != 0) ++len; - BigDigit [] result = new BigDigit[len+1]; - result[0 .. x.length] = x[]; - if (x.length < 2 && hi != 0) - { - result[1]=hi; - hi=0; - } - uint carry = multibyteIncrementAssign!('+')(result[0..$-1], lo); - if (hi != 0) carry += multibyteIncrementAssign!('+')(result[1..$-1], hi); - if (carry) - { - result[$-1] = carry; - return result; - } - else - return result[0..$-1]; -} - -/** Return x - y. - * x must be greater than y. - */ -BigDigit [] subInt(const BigDigit[] x, ulong y) pure nothrow @safe -{ - uint hi = cast(uint)(y >>> 32); - uint lo = cast(uint)(y & 0xFFFF_FFFF); - BigDigit [] result = new BigDigit[x.length]; - result[] = x[]; - multibyteIncrementAssign!('-')(result[], lo); - if (hi) - multibyteIncrementAssign!('-')(result[1..$], hi); - if (result[$-1] == 0) - return result[0..$-1]; - else - return result; -} - -/** General unsigned multiply routine for bigints. - * Sets result = x * y. - * - * The length of y must not be larger than the length of x. - * Different algorithms are used, depending on the lengths of x and y. - * TODO: "Modern Computer Arithmetic" suggests the OddEvenKaratsuba algorithm for the - * unbalanced case. (But I doubt it would be faster in practice). - * - */ -void mulInternal(BigDigit[] result, const(BigDigit)[] x, const(BigDigit)[] y) - pure nothrow @safe -{ - import core.memory : GC; - assert( result.length == x.length + y.length, - "result array must have enough space to store computed result"); - assert( y.length > 0, "y must not be empty"); - assert( x.length >= y.length, "x must be greater or equal than y"); - if (y.length <= KARATSUBALIMIT) - { - // Small multiplier, we'll just use the asm classic multiply. - if (y.length == 1) - { // Trivial case, no cache effects to worry about - result[x.length] = multibyteMul(result[0 .. x.length], x, y[0], 0); - return; - } - - immutable CACHELIMIT = getCacheLimit; - if (x.length + y.length < CACHELIMIT) - return mulSimple(result, x, y); - - // If x is so big that it won't fit into the cache, we divide it into chunks - // Every chunk must be greater than y.length. - // We make the first chunk shorter, if necessary, to ensure this. - - auto chunksize = CACHELIMIT / y.length; - immutable residual = x.length % chunksize; - if (residual < y.length) - { - chunksize -= y.length; - } - - // Use schoolbook multiply. - mulSimple(result[0 .. chunksize + y.length], x[0 .. chunksize], y); - auto done = chunksize; - - while (done < x.length) - { - // result[done .. done+ylength] already has a value. - chunksize = (done + (CACHELIMIT / y.length) < x.length) ? (CACHELIMIT / y.length) : x.length - done; - BigDigit [KARATSUBALIMIT] partial; - partial[0 .. y.length] = result[done .. done+y.length]; - mulSimple(result[done .. done+chunksize+y.length], x[done .. done+chunksize], y); - addAssignSimple(result[done .. done+chunksize + y.length], partial[0 .. y.length]); - done += chunksize; - } - return; - } - - immutable half = (x.length >> 1) + (x.length & 1); - if (2*y.length*y.length <= x.length*x.length) - { - // UNBALANCED MULTIPLY - // Use school multiply to cut into quasi-squares of Karatsuba-size - // or larger. The ratio of the two sides of the 'square' must be - // between 1.414:1 and 1:1. Use Karatsuba on each chunk. - // - // For maximum performance, we want the ratio to be as close to - // 1:1 as possible. To achieve this, we can either pad x or y. - // The best choice depends on the modulus x%y. - auto numchunks = x.length / y.length; - auto chunksize = y.length; - auto extra = x.length % y.length; - auto maxchunk = chunksize + extra; - bool paddingY; // true = we're padding Y, false = we're padding X. - bool isExtraSmall = extra * extra * 2 < y.length * y.length; - if (numchunks == 1 && isExtraSmall) - { - // We divide (x_first_half * y) and (x_last_half * y) - // between 1.414:1 and 1.707:1 (1.707 = 1+1/sqrt(2)). - // (1.414 ~ 1.707)/2:1 is balanced. - BigDigit [] scratchbuff = new BigDigit[karatsubaRequiredBuffSize(y.length) + y.length]; - BigDigit [] partial = scratchbuff[$ - y.length .. $]; - scratchbuff = scratchbuff[0 .. $ - y.length]; - mulKaratsuba(result[0 .. half + y.length], y, x[0 .. half], scratchbuff); - partial[] = result[half .. half + y.length]; - mulKaratsuba(result[half .. $], y, x[half .. $], scratchbuff); - BigDigit c = addAssignSimple(result[half .. half + y.length], partial); - if (c) multibyteIncrementAssign!('+')(result[half + y.length..$], c); - () @trusted { GC.free(scratchbuff.ptr); } (); - } - else - { - if (isExtraSmall) - { - // The leftover bit is small enough that it should be incorporated - // in the existing chunks. - // Make all the chunks a tiny bit bigger - // (We're padding y with zeros) - chunksize += extra / numchunks; - extra = x.length - chunksize*numchunks; - // there will probably be a few left over. - // Every chunk will either have size chunksize, or chunksize+1. - maxchunk = chunksize + 1; - paddingY = true; - assert(chunksize + extra + chunksize *(numchunks-1) == x.length, - "Unexpected size"); - } - else - { - // the extra bit is large enough that it's worth making a new chunk. - // (This means we're padding x with zeros, when doing the first one). - maxchunk = chunksize; - ++numchunks; - paddingY = false; - assert(extra + chunksize *(numchunks-1) == x.length, - "Unexpected size"); - } - // We make the buffer a bit bigger so we have space for the partial sums. - BigDigit [] scratchbuff = new BigDigit[karatsubaRequiredBuffSize(maxchunk) + y.length]; - BigDigit [] partial = scratchbuff[$ - y.length .. $]; - scratchbuff = scratchbuff[0 .. $ - y.length]; - size_t done; // how much of X have we done so far? - if (paddingY) - { - // If the first chunk is bigger, do it first. We're padding y. - mulKaratsuba(result[0 .. y.length + chunksize + (extra > 0 ? 1 : 0 )], - x[0 .. chunksize + (extra>0?1:0)], y, scratchbuff); - done = chunksize + (extra > 0 ? 1 : 0); - if (extra) --extra; - } - else - { // We're padding X. Begin with the extra bit. - mulKaratsuba(result[0 .. y.length + extra], y, x[0 .. extra], scratchbuff); - done = extra; - extra = 0; - } - immutable basechunksize = chunksize; - while (done < x.length) - { - chunksize = basechunksize + (extra > 0 ? 1 : 0); - if (extra) --extra; - partial[] = result[done .. done+y.length]; - mulKaratsuba(result[done .. done + y.length + chunksize], - x[done .. done+chunksize], y, scratchbuff); - addAssignSimple(result[done .. done + y.length + chunksize], partial); - done += chunksize; - } - () @trusted { GC.free(scratchbuff.ptr); } (); - } - } - else - { - // Balanced. Use Karatsuba directly. - BigDigit [] scratchbuff = new BigDigit[karatsubaRequiredBuffSize(x.length)]; - mulKaratsuba(result, x, y, scratchbuff); - () @trusted { GC.free(scratchbuff.ptr); } (); - } -} - -// https://issues.dlang.org/show_bug.cgi?id=20493 -@safe unittest -{ - // the bug report has a testcase with very large numbers (~10^3800 and ~10^2300) - // the number itself isn't important, only the amount of digits, so we do a simpler - // multiplication of the same size, analogous to: - // 11111111 * 11111111 = 0123456787654321 - // but instead of base 10, it's in base `BigDigit` - - BigDigit[398] x = 1; - BigDigit[236] y = 1; - BigDigit[x.length + y.length] result; - mulInternal(result[], x[], y[]); - - // create an array of the form [1, 2, ..., y.length, ..., y.length, y.length-1, ..., 1, 0] - BigDigit[x.length + y.length] expected = y.length; - foreach (BigDigit i; 0 .. y.length) - { - expected[i] = i+1; - expected[$-1-i] = i; - } - - assert(result == expected); -} - -/** General unsigned squaring routine for BigInts. - * Sets result = x*x. - * NOTE: If the highest half-digit of x is zero, the highest digit of result will - * also be zero. - */ -void squareInternal(BigDigit[] result, const BigDigit[] x) pure nothrow @safe -{ - import core.memory : GC; - // Squaring is potentially half a multiply, plus add the squares of - // the diagonal elements. - assert(result.length == 2*x.length, - "result needs to have twice the capacity of x"); - if (x.length <= KARATSUBASQUARELIMIT) - { - if (x.length == 1) - { - result[1] = multibyteMul(result[0 .. 1], x, x[0], 0); - return; - } - return squareSimple(result, x); - } - // The nice thing about squaring is that it always stays balanced - BigDigit [] scratchbuff = new BigDigit[karatsubaRequiredBuffSize(x.length)]; - squareKaratsuba(result, x, scratchbuff); - () @trusted { GC.free(scratchbuff.ptr); } (); -} - - -import core.bitop : bsr; - -/// if remainder is null, only calculate quotient. -void divModInternal(BigDigit [] quotient, BigDigit[] remainder, const BigDigit [] u, - const BigDigit [] v) pure nothrow @safe -{ - import core.memory : GC; - assert(quotient.length == u.length - v.length + 1, - "Invalid quotient length"); - assert(remainder == null || remainder.length == v.length, - "Invalid remainder"); - assert(v.length > 1, "v must have more than 1 element"); - assert(u.length >= v.length, "u must be as longer or longer than v"); - - // Normalize by shifting v left just enough so that - // its high-order bit is on, and shift u left the - // same amount. The highest bit of u will never be set. - - BigDigit [] vn = new BigDigit[v.length]; - BigDigit [] un = new BigDigit[u.length + 1]; - // How much to left shift v, so that its MSB is set. - uint s = BIGDIGITSHIFTMASK - bsr(v[$-1]); - if (s != 0) - { - multibyteShl(vn, v, s); - un[$-1] = multibyteShl(un[0..$-1], u, s); - } - else - { - vn[] = v[]; - un[0..$-1] = u[]; - un[$-1] = 0; - } - if (quotient.length= 0; --i) - { - toHexZeroPadded(buff[x .. x+8], data[i], letterCase); - x+=8; - if (separator) - { - if (i>0) buff[x] = separator; - ++x; - } - } - return buff; -} - -/** - * Convert a big uint into an octal string. - * - * Params: - * buff = The destination buffer for the octal string. Must be large enough to - * store the result, including leading zeroes, which is - * ceil(data.length * BigDigitBits / 3) characters. - * The buffer is filled from back to front, starting from `buff[$-1]`. - * data = The biguint to be converted. - * - * Returns: The index of the leading non-zero digit in `buff`. Will be - * `buff.length - 1` if the entire big uint is zero. - */ -size_t biguintToOctal(char[] buff, const(BigDigit)[] data) - pure nothrow @safe @nogc -{ - ubyte carry = 0; - int shift = 0; - size_t penPos = buff.length - 1; - size_t lastNonZero = buff.length - 1; - - pragma(inline) void output(uint digit) @nogc nothrow - { - if (digit != 0) - lastNonZero = penPos; - buff[penPos--] = cast(char)('0' + digit); - } - - foreach (bigdigit; data) - { - if (shift < 0) - { - // Some bits were carried over from previous word. - assert(shift > -3, "shift must be greater than -3"); - output(((bigdigit << -shift) | carry) & 0b111); - shift += 3; - assert(shift > 0, "shift must be 1 or greater"); - } - - while (shift <= BigDigitBits - 3) - { - output((bigdigit >>> shift) & 0b111); - shift += 3; - } - - if (shift < BigDigitBits) - { - // Some bits need to be carried forward. - carry = (bigdigit >>> shift) & 0b11; - } - shift -= BigDigitBits; - assert(shift >= -2 && shift <= 0, "shift must in [-2,0]"); - } - - if (shift < 0) - { - // Last word had bits that haven't been output yet. - assert(shift > -3, "Shift must be greater than -3"); - output(carry); - } - - return lastNonZero; -} - -/** Convert a big uint into a decimal string. - * - * Params: - * data The biguint to be converted. Will be destroyed. - * buff The destination buffer for the decimal string. Must be - * large enough to store the result, including leading zeros. - * Will be filled backwards, starting from buff[$-1]. - * - * buff.length must be >= (data.length*32)/log2(10) = 9.63296 * data.length. - * Returns: - * the lowest index of buff which was used. - */ -size_t biguintToDecimal(char [] buff, BigDigit [] data) pure nothrow @safe -{ - ptrdiff_t sofar = buff.length; - // Might be better to divide by (10^38/2^32) since that gives 38 digits for - // the price of 3 divisions and a shr; this version only gives 27 digits - // for 3 divisions. - while (data.length>1) - { - uint rem = multibyteDivAssign(data, 10_0000_0000, 0); - itoaZeroPadded(buff[sofar-9 .. sofar], rem); - sofar -= 9; - if (data[$-1] == 0 && data.length > 1) - { - data.length = data.length - 1; - } - } - itoaZeroPadded(buff[sofar-10 .. sofar], data[0]); - sofar -= 10; - // and strip off the leading zeros - while (sofar != buff.length-1 && buff[sofar] == '0') - sofar++; - return sofar; -} - -/** Convert a decimal string into a big uint. - * - * Params: - * data The biguint to be receive the result. Must be large enough to - * store the result. - * s The decimal string. May contain _ or 0 .. 9 - * - * The required length for the destination buffer is slightly less than - * 1 + s.length/log2(10) = 1 + s.length/3.3219. - * - * Returns: - * the highest index of data which was used. - */ -int biguintFromDecimal(Range)(BigDigit[] data, Range s) -if ( - isInputRange!Range && - isSomeChar!(ElementType!Range) && - !isInfinite!Range) -in -{ - static if (hasLength!Range) - assert((data.length >= 2) || (data.length == 1 && s.length == 1), - "data has a invalid length"); -} -do -{ - import std.conv : ConvException; - - // Convert to base 1e19 = 10_000_000_000_000_000_000. - // (this is the largest power of 10 that will fit into a long). - // The length will be less than 1 + s.length/log2(10) = 1 + s.length/3.3219. - // 485 bits will only just fit into 146 decimal digits. - // As we convert the string, we record the number of digits we've seen in base 19: - // hi is the number of digits/19, lo is the extra digits (0 to 18). - // TODO: This is inefficient for very large strings (it is O(n^^2)). - // We should take advantage of fast multiplication once the numbers exceed - // Karatsuba size. - uint lo = 0; // number of powers of digits, 0 .. 18 - uint x = 0; - ulong y = 0; - uint hi = 0; // number of base 1e19 digits - data[0] = 0; // initially number is 0. - if (data.length > 1) - data[1] = 0; - - foreach (character; s) - { - if (character == '_') - continue; - - if (character < '0' || character > '9') - throw new ConvException("invalid digit"); - x *= 10; - x += character - '0'; - ++lo; - if (lo == 9) - { - y = x; - x = 0; - } - if (lo == 18) - { - y *= 10_0000_0000; - y += x; - x = 0; - } - if (lo == 19) - { - y *= 10; - y += x; - x = 0; - // Multiply existing number by 10^19, then add y1. - if (hi>0) - { - data[hi] = multibyteMul(data[0 .. hi], data[0 .. hi], 1_220_703_125*2u, 0); // 5^13*2 = 0x9184_E72A - ++hi; - data[hi] = multibyteMul(data[0 .. hi], data[0 .. hi], 15_625*262_144u, 0); // 5^6*2^18 = 0xF424_0000 - ++hi; - } - else - hi = 2; - uint c = multibyteIncrementAssign!('+')(data[0 .. hi], cast(uint)(y&0xFFFF_FFFF)); - c += multibyteIncrementAssign!('+')(data[1 .. hi], cast(uint)(y >> 32)); - if (c != 0) - { - data[hi]=c; - ++hi; - } - y = 0; - lo = 0; - } - } - // Now set y = all remaining digits. - if (lo >= 18) - { - } - else if (lo >= 9) - { - for (int k=9; k>> 32); - hi=2; - } - } - else - { - while (lo>0) - { - immutable c = multibyteMul(data[0 .. hi], data[0 .. hi], 10, 0); - if (c != 0) - { - data[hi]=c; - ++hi; - } - --lo; - } - uint c = multibyteIncrementAssign!('+')(data[0 .. hi], cast(uint)(y&0xFFFF_FFFF)); - if (y > 0xFFFF_FFFFL) - { - c += multibyteIncrementAssign!('+')(data[1 .. hi], cast(uint)(y >> 32)); - } - if (c != 0) - { - data[hi]=c; - ++hi; - } - } - } - while (hi>1 && data[hi-1]==0) - --hi; - return hi; -} - - -// ------------------------ -// These in-place functions are only for internal use; they are incompatible -// with COW. - -// Classic 'schoolbook' multiplication. -void mulSimple(BigDigit[] result, const(BigDigit) [] left, - const(BigDigit)[] right) pure nothrow @safe -in -{ - assert(result.length == left.length + right.length, - "Result must be able to store left + right"); - assert(right.length>1, "right must not be empty"); -} -do -{ - result[left.length] = multibyteMul(result[0 .. left.length], left, right[0], 0); - multibyteMultiplyAccumulate(result[1..$], left, right[1..$]); -} - -// Classic 'schoolbook' squaring -void squareSimple(BigDigit[] result, const(BigDigit) [] x) pure nothrow @safe -in -{ - assert(result.length == 2*x.length, "result must be twice as long as x"); - assert(x.length>1, "x must not be empty"); -} -do -{ - multibyteSquare(result, x); -} - - -// add two uints of possibly different lengths. Result must be as long -// as the larger length. -// Returns carry (0 or 1). -uint addSimple(BigDigit[] result, const BigDigit [] left, const BigDigit [] right) -pure nothrow @safe -in -{ - assert(result.length == left.length, - "result and left must be of the same length"); - assert(left.length >= right.length, - "left must be longer or of equal length to right"); - assert(right.length > 0, "right must not be empty"); -} -do -{ - uint carry = multibyteAdd(result[0 .. right.length], - left[0 .. right.length], right, 0); - if (right.length < left.length) - { - result[right.length .. left.length] = left[right.length .. $]; - carry = multibyteIncrementAssign!('+')(result[right.length..$], carry); - } - return carry; -} - -/* result = result - right - * Returns carry = 1 if result was less than right. -*/ -BigDigit subAssignSimple(BigDigit [] result, const(BigDigit) [] right) -pure nothrow @safe -{ - assert(result.length >= right.length, - "result must be longer or of equal length to right"); - uint c = multibyteSub(result[0 .. right.length], result[0 .. right.length], right, 0); - if (c && result.length > right.length) - c = multibyteIncrementAssign!('-')(result[right.length .. $], c); - return c; -} - -/* result = result + right -*/ -BigDigit addAssignSimple(BigDigit [] result, const(BigDigit) [] right) -pure nothrow @safe -{ - assert(result.length >= right.length, - "result must be longer or of equal length to right"); - uint c = multibyteAdd(result[0 .. right.length], result[0 .. right.length], right, 0); - if (c && result.length > right.length) - c = multibyteIncrementAssign!('+')(result[right.length .. $], c); - return c; -} - -/* performs result += wantSub? - right : right; -*/ -BigDigit addOrSubAssignSimple(BigDigit [] result, const(BigDigit) [] right, - bool wantSub) pure nothrow @safe -{ - if (wantSub) - return subAssignSimple(result, right); - else - return addAssignSimple(result, right); -} - - -// return true if x= y.length, - "x must be longer or of equal length to y"); - auto k = x.length-1; - while (x[k]==0 && k >= y.length) - --k; - if (k >= y.length) - return false; - while (k>0 && x[k]==y[k]) - --k; - return x[k] < y[k]; -} - -// Set result = abs(x-y), return true if result is negative(x= y.length) ? x.length : y.length), - "result must capable to store the maximum of x and y"); - - size_t minlen; - bool negative; - if (x.length >= y.length) - { - minlen = y.length; - negative = less(x, y); - } - else - { - minlen = x.length; - negative = !less(y, x); - } - const (BigDigit)[] large, small; - if (negative) - { - large = y; small = x; - } - else - { - large = x; small = y; - } - - BigDigit carry = multibyteSub(result[0 .. minlen], large[0 .. minlen], small[0 .. minlen], 0); - if (x.length != y.length) - { - result[minlen .. large.length]= large[minlen..$]; - result[large.length..$] = 0; - if (carry) - multibyteIncrementAssign!('-')(result[minlen..$], carry); - } - return negative; -} - -/* Determine how much space is required for the temporaries - * when performing a Karatsuba multiplication. - * TODO: determining a tight bound is non-trivial and depends on KARATSUBALIMIT, see: - * https://issues.dlang.org/show_bug.cgi?id=20493 - */ -size_t karatsubaRequiredBuffSize(size_t xlen) pure nothrow @safe -{ - return xlen <= KARATSUBALIMIT ? 0 : (xlen * 9) / 4; -} - -/* Sets result = x*y, using Karatsuba multiplication. -* x must be longer or equal to y. -* Valid only for balanced multiplies, where x is not shorter than y. -* It is superior to schoolbook multiplication if and only if -* sqrt(2)*y.length > x.length > y.length. -* Karatsuba multiplication is O(n^1.59), whereas schoolbook is O(n^2) -* The maximum allowable length of x and y is uint.max; but better algorithms -* should be used far before that length is reached. -* Params: -* scratchbuff An array long enough to store all the temporaries. Will be destroyed. -*/ -void mulKaratsuba(BigDigit [] result, const(BigDigit) [] x, - const(BigDigit)[] y, BigDigit [] scratchbuff) pure nothrow @safe -{ - assert(x.length >= y.length, "x must be greater or equal to y"); - assert(result.length < uint.max, "Operands too large"); - assert(result.length == x.length + y.length, - "result must be as large as x + y"); - if (x.length <= KARATSUBALIMIT) - { - return mulSimple(result, x, y); - } - // Must be almost square (otherwise, a schoolbook iteration is better) - assert(2L * y.length * y.length > (x.length-1) * (x.length-1), - "Bigint Internal Error: Asymmetric Karatsuba"); - - // The subtractive version of Karatsuba multiply uses the following result: - // (Nx1 + x0)*(Ny1 + y0) = (N*N)*x1y1 + x0y0 + N * (x0y0 + x1y1 - mid) - // where mid = (x0-x1)*(y0-y1) - // requiring 3 multiplies of length N, instead of 4. - // The advantage of the subtractive over the additive version is that - // the mid multiply cannot exceed length N. But there are subtleties: - // (x0-x1),(y0-y1) may be negative or zero. To keep it simple, we - // retain all of the leading zeros in the subtractions - - // half length, round up. - auto half = (x.length >> 1) + (x.length & 1); - - const(BigDigit) [] x0 = x[0 .. half]; - const(BigDigit) [] x1 = x[half .. $]; - const(BigDigit) [] y0 = y[0 .. half]; - const(BigDigit) [] y1 = y[half .. $]; - BigDigit [] mid = scratchbuff[0 .. half*2]; - BigDigit [] newscratchbuff = scratchbuff[half*2 .. $]; - BigDigit [] resultLow = result[0 .. 2*half]; - BigDigit [] resultHigh = result[2*half .. $]; - // initially use result to store temporaries - BigDigit [] xdiff= result[0 .. half]; - BigDigit [] ydiff = result[half .. half*2]; - - // First, we calculate mid, and sign of mid - immutable bool midNegative = inplaceSub(xdiff, x0, x1) - ^ inplaceSub(ydiff, y0, y1); - mulKaratsuba(mid, xdiff, ydiff, newscratchbuff); - - // Low half of result gets x0 * y0. High half gets x1 * y1 - - mulKaratsuba(resultLow, x0, y0, newscratchbuff); - - if (2L * y1.length * y1.length < x1.length * x1.length) - { - // an asymmetric situation has been created. - // Worst case is if x:y = 1.414 : 1, then x1:y1 = 2.41 : 1. - // Applying one schoolbook multiply gives us two pieces each 1.2:1 - if (y1.length <= KARATSUBALIMIT) - mulSimple(resultHigh, x1, y1); - else - { - // divide x1 in two, then use schoolbook multiply on the two pieces. - auto quarter = (x1.length >> 1) + (x1.length & 1); - immutable ysmaller = (quarter >= y1.length); - mulKaratsuba(resultHigh[0 .. quarter+y1.length], ysmaller ? x1[0 .. quarter] : y1, - ysmaller ? y1 : x1[0 .. quarter], newscratchbuff); - // Save the part which will be overwritten. - immutable ysmaller2 = ((x1.length - quarter) >= y1.length); - newscratchbuff[0 .. y1.length] = resultHigh[quarter .. quarter + y1.length]; - mulKaratsuba(resultHigh[quarter..$], ysmaller2 ? x1[quarter..$] : y1, - ysmaller2 ? y1 : x1[quarter..$], newscratchbuff[y1.length..$]); - - resultHigh[quarter..$].addAssignSimple(newscratchbuff[0 .. y1.length]); - } - } - else - mulKaratsuba(resultHigh, x1, y1, newscratchbuff); - - /* We now have result = x0y0 + (N*N)*x1y1 - Before adding or subtracting mid, we must calculate - result += N * (x0y0 + x1y1) - We can do this with three half-length additions. With a = x0y0, b = x1y1: - aHI aLO - + aHI aLO - + bHI bLO - + bHI bLO - = R3 R2 R1 R0 - R1 = aHI + bLO + aLO - R2 = aHI + bLO + aHI + carry_from_R1 - R3 = bHi + carry_from_R2 - - It might actually be quicker to do it in two full-length additions: - newscratchbuff[2*half] = addSimple(newscratchbuff[0 .. 2*half], result[0 .. 2*half], result[2*half..$]); - addAssignSimple(result[half..$], newscratchbuff[0 .. 2*half+1]); - */ - BigDigit[] R1 = result[half .. half*2]; - BigDigit[] R2 = result[half*2 .. half*3]; - BigDigit[] R3 = result[half*3..$]; - BigDigit c1 = multibyteAdd(R2, R2, R1, 0); // c1:R2 = R2 + R1 - BigDigit c2 = multibyteAdd(R1, R2, result[0 .. half], 0); // c2:R1 = R2 + R1 + R0 - BigDigit c3 = addAssignSimple(R2, R3); // R2 = R2 + R1 + R3 - if (c1+c2) - multibyteIncrementAssign!('+')(result[half*2..$], c1+c2); - if (c1+c3) - multibyteIncrementAssign!('+')(R3, c1+c3); - - // And finally we subtract mid - addOrSubAssignSimple(result[half..$], mid, !midNegative); -} - -void squareKaratsuba(BigDigit [] result, const BigDigit [] x, - BigDigit [] scratchbuff) pure nothrow @safe -{ - // See mulKaratsuba for implementation comments. - // Squaring is simpler, since it never gets asymmetric. - assert(result.length < uint.max, "Operands too large"); - assert(result.length == 2*x.length, - "result must be twice the length of x"); - if (x.length <= KARATSUBASQUARELIMIT) - { - return squareSimple(result, x); - } - // half length, round up. - auto half = (x.length >> 1) + (x.length & 1); - - const(BigDigit)[] x0 = x[0 .. half]; - const(BigDigit)[] x1 = x[half .. $]; - BigDigit [] mid = scratchbuff[0 .. half*2]; - BigDigit [] newscratchbuff = scratchbuff[half*2 .. $]; - // initially use result to store temporaries - BigDigit [] xdiff= result[0 .. half]; - const BigDigit [] ydiff = result[half .. half*2]; - - // First, we calculate mid. We don't need its sign - inplaceSub(xdiff, x0, x1); - squareKaratsuba(mid, xdiff, newscratchbuff); - - // Set result = x0x0 + (N*N)*x1x1 - squareKaratsuba(result[0 .. 2*half], x0, newscratchbuff); - squareKaratsuba(result[2*half .. $], x1, newscratchbuff); - - /* result += N * (x0x0 + x1x1) - Do this with three half-length additions. With a = x0x0, b = x1x1: - R1 = aHI + bLO + aLO - R2 = aHI + bLO + aHI + carry_from_R1 - R3 = bHi + carry_from_R2 - */ - BigDigit[] R1 = result[half .. half*2]; - BigDigit[] R2 = result[half*2 .. half*3]; - BigDigit[] R3 = result[half*3..$]; - BigDigit c1 = multibyteAdd(R2, R2, R1, 0); // c1:R2 = R2 + R1 - BigDigit c2 = multibyteAdd(R1, R2, result[0 .. half], 0); // c2:R1 = R2 + R1 + R0 - BigDigit c3 = addAssignSimple(R2, R3); // R2 = R2 + R1 + R3 - if (c1+c2) multibyteIncrementAssign!('+')(result[half*2..$], c1+c2); - if (c1+c3) multibyteIncrementAssign!('+')(R3, c1+c3); - - // And finally we subtract mid, which is always positive - subAssignSimple(result[half..$], mid); -} - -/* Knuth's Algorithm D, as presented in - * H.S. Warren, "Hacker's Delight", Addison-Wesley Professional (2002). - * Also described in "Modern Computer Arithmetic" 0.2, Exercise 1.8.18. - * Given u and v, calculates quotient = u / v, u = u % v. - * v must be normalized (ie, the MSB of v must be 1). - * The most significant words of quotient and u may be zero. - * u[0 .. v.length] holds the remainder. - */ -void schoolbookDivMod(BigDigit [] quotient, BigDigit [] u, in BigDigit [] v) - pure nothrow @safe -{ - assert(quotient.length == u.length - v.length, - "quotient has wrong length"); - assert(v.length > 1, "v must not be empty"); - assert(u.length >= v.length, "u must be larger or equal to v"); - assert((v[$ - 1] & 0x8000_0000) != 0, "Invalid value at v[$ - 1]"); - assert(u[$ - 1] < v[$ - 1], "u[$ - 1] must be less than v[$ - 1]"); - // BUG: This code only works if BigDigit is uint. - uint vhi = v[$-1]; - uint vlo = v[$-2]; - - for (ptrdiff_t j = u.length - v.length - 1; j >= 0; j--) - { - // Compute estimate of quotient[j], - // qhat = (three most significant words of u)/(two most sig words of v). - uint qhat; - if (u[j + v.length] == vhi) - { - // uu/vhi could exceed uint.max (it will be 0x8000_0000 or 0x8000_0001) - qhat = uint.max; - } - else - { - uint ulo = u[j + v.length - 2]; - version (D_InlineAsm_X86) - { - // Note: On DMD, this is only ~10% faster than the non-asm code. - uint *p = &u[j + v.length - 1]; - asm pure nothrow @trusted - { - mov EAX, p; - mov EDX, [EAX+4]; - mov EAX, [EAX]; - div dword ptr [vhi]; - mov qhat, EAX; - mov ECX, EDX; -div3by2correction: - mul dword ptr [vlo]; // EDX:EAX = qhat * vlo - sub EAX, ulo; - sbb EDX, ECX; - jbe div3by2done; - mov EAX, qhat; - dec EAX; - mov qhat, EAX; - add ECX, dword ptr [vhi]; - jnc div3by2correction; -div3by2done: ; - } - } - else - { // version (InlineAsm) - ulong uu = (cast(ulong)(u[j + v.length]) << 32) | u[j + v.length - 1]; - immutable bigqhat = uu / vhi; - ulong rhat = uu - bigqhat * vhi; - qhat = cast(uint) bigqhat; -again: - if (cast(ulong) qhat * vlo > ((rhat << 32) + ulo)) - { - --qhat; - rhat += vhi; - if (!(rhat & 0xFFFF_FFFF_0000_0000L)) - goto again; - } - } // version (InlineAsm) - } - // Multiply and subtract. - uint carry = multibyteMulAdd!('-')(u[j .. j + v.length], v, qhat, 0); - - if (u[j+v.length] < carry) - { - // If we subtracted too much, add back - --qhat; - carry -= multibyteAdd(u[j .. j + v.length],u[j .. j + v.length], v, 0); - } - quotient[j] = qhat; - u[j + v.length] = u[j + v.length] - carry; - } -} - -// TODO: Replace with a library call -void itoaZeroPadded(char[] output, uint value) - pure nothrow @safe @nogc -{ - for (auto i = output.length; i--;) - { - if (value < 10) - { - output[i] = cast(char)(value + '0'); - value = 0; - } - else - { - output[i] = cast(char)(value % 10 + '0'); - value /= 10; - } - } -} - -void toHexZeroPadded(char[] output, uint value, - LetterCase letterCase = LetterCase.upper) pure nothrow @safe -{ - ptrdiff_t x = output.length - 1; - static immutable string upperHexDigits = "0123456789ABCDEF"; - static immutable string lowerHexDigits = "0123456789abcdef"; - for ( ; x >= 0; --x) - { - if (letterCase == LetterCase.upper) - { - output[x] = upperHexDigits[value & 0xF]; - } - else - { - output[x] = lowerHexDigits[value & 0xF]; - } - value >>= 4; - } -} - -// Returns the highest value of i for which left[i]!=right[i], -// or 0 if left[] == right[] -size_t highestDifferentDigit(const BigDigit [] left, const BigDigit [] right) -pure nothrow @nogc @safe -{ - assert(left.length == right.length, - "left have a length equal to that of right"); - for (ptrdiff_t i = left.length - 1; i>0; --i) - { - if (left[i] != right[i]) - return i; - } - return 0; -} - -// Returns the lowest value of i for which x[i]!=0. -int firstNonZeroDigit(const BigDigit [] x) pure nothrow @nogc @safe -{ - int k = 0; - while (x[k]==0) - { - ++k; - assert(k < x.length, "k must be less than x.length"); - } - return k; -} - -/* - Calculate quotient and remainder of u / v using fast recursive division. - v must be normalised, and must be at least half as long as u. - Given u and v, v normalised, calculates quotient = u/v, u = u%v. - scratch is temporary storage space, length must be >= quotient + 1. - -Returns: - u[0 .. v.length] is the remainder. u[v.length..$] is corrupted. - - Implements algorithm 1.8 from MCA. - This algorithm has an annoying special case. After the first recursion, the - highest bit of the quotient may be set. This means that in the second - recursive call, the 'in' contract would be violated. (This happens only - when the top quarter of u is equal to the top half of v. A base 10 - equivalent example of this situation is 5517/56; the first step gives - 55/5 = 11). To maintain the in contract, we pad a zero to the top of both - u and the quotient. 'mayOverflow' indicates that that the special case - has occurred. - (In MCA, a different strategy is used: the in contract is weakened, and - schoolbookDivMod is more general: it allows the high bit of u to be set). - See also: - - C. Burkinel and J. Ziegler, "Fast Recursive Division", MPI-I-98-1-022, - Max-Planck Institute fuer Informatik, (Oct 1998). -*/ -void recursiveDivMod(BigDigit[] quotient, BigDigit[] u, const(BigDigit)[] v, - BigDigit[] scratch, bool mayOverflow = false) - pure nothrow @safe -in -{ - // v must be normalized - assert(v.length > 1, "v must not be empty"); - assert((v[$ - 1] & 0x8000_0000) != 0, "Invalid value at v[$ - 1]"); - assert(!(u[$ - 1] & 0x8000_0000), "Invalid value at u[$ - 1]"); - assert(quotient.length == u.length - v.length, - "quotient must be of equal length of u - v"); - if (mayOverflow) - { - assert(u[$-1] == 0, "Invalid value at u[$ - 1]"); - assert(u[$-2] & 0x8000_0000, "Invalid value at u[$ - 2]"); - } - - // Must be symmetric. Use block schoolbook division if not. - assert((mayOverflow ? u.length-1 : u.length) <= 2 * v.length, - "Invalid length of u"); - assert((mayOverflow ? u.length-1 : u.length) >= v.length, - "Invalid length of u"); - assert(scratch.length >= quotient.length + (mayOverflow ? 0 : 1), - "Invalid quotient length"); -} -do -{ - if (quotient.length < FASTDIVLIMIT) - { - return schoolbookDivMod(quotient, u, v); - } - - // Split quotient into two halves, but keep padding in the top half - auto k = (mayOverflow ? quotient.length - 1 : quotient.length) >> 1; - - // RECURSION 1: Calculate the high half of the quotient - - // Note that if u and quotient were padded, they remain padded during - // this call, so in contract is satisfied. - recursiveDivMod(quotient[k .. $], u[2 * k .. $], v[k .. $], - scratch, mayOverflow); - - // quotient[k..$] is our guess at the high quotient. - // u[2*k .. 2.*k + v.length - k = k + v.length] is the high part of the - // first remainder. u[0 .. 2*k] is the low part. - - // Calculate the full first remainder to be - // remainder - highQuotient * lowDivisor - // reducing highQuotient until the remainder is positive. - // The low part of the remainder, u[0 .. k], cannot be altered by this. - - adjustRemainder(quotient[k .. $], u[k .. k + v.length], v, k, - scratch[0 .. quotient.length], mayOverflow); - - // RECURSION 2: Calculate the low half of the quotient - // The full first remainder is now in u[0 .. k + v.length]. - - if (u[k + v.length - 1] & 0x8000_0000) - { - // Special case. The high quotient is 0x1_00...000 or 0x1_00...001. - // This means we need an extra quotient word for the next recursion. - // We need to restore the invariant for the recursive calls. - // We do this by padding both u and quotient. Extending u is trivial, - // because the higher words will not be used again. But for the - // quotient, we're clobbering the low word of the high quotient, - // so we need save it, and add it back in after the recursive call. - - auto clobberedQuotient = quotient[k]; - u[k+v.length] = 0; - - recursiveDivMod(quotient[0 .. k+1], u[k .. k + v.length+1], - v[k .. $], scratch, true); - adjustRemainder(quotient[0 .. k+1], u[0 .. v.length], v, k, - scratch[0 .. 2 * k+1], true); - - // Now add the quotient word that got clobbered earlier. - multibyteIncrementAssign!('+')(quotient[k..$], clobberedQuotient); - } - else - { - // The special case has NOT happened. - recursiveDivMod(quotient[0 .. k], u[k .. k + v.length], v[k .. $], - scratch, false); - - // high remainder is in u[k .. k+(v.length-k)] == u[k .. v.length] - - adjustRemainder(quotient[0 .. k], u[0 .. v.length], v, k, - scratch[0 .. 2 * k]); - } -} - -// rem -= quot * v[0 .. k]. -// If would make rem negative, decrease quot until rem is >= 0. -// Needs (quot.length * k) scratch space to store the result of the multiply. -void adjustRemainder(BigDigit[] quot, BigDigit[] rem, const(BigDigit)[] v, - ptrdiff_t k, - BigDigit[] scratch, bool mayOverflow = false) pure nothrow @safe -{ - assert(rem.length == v.length, "rem must be as long as v"); - mulInternal(scratch, quot, v[0 .. k]); - uint carry = 0; - if (mayOverflow) - carry = scratch[$-1] + subAssignSimple(rem, scratch[0..$-1]); - else - carry = subAssignSimple(rem, scratch); - while (carry) - { - multibyteIncrementAssign!('-')(quot, 1); // quot-- - carry -= multibyteAdd(rem, rem, v, 0); - } -} - -// Cope with unbalanced division by performing block schoolbook division. -void blockDivMod(BigDigit [] quotient, BigDigit [] u, in BigDigit [] v) -pure nothrow @safe -{ - import core.memory : GC; - assert(quotient.length == u.length - v.length, - "quotient must be of equal length of u - v"); - assert(v.length > 1, "v must not be empty"); - assert(u.length >= v.length, "u must be longer or of equal length as v"); - assert((v[$-1] & 0x8000_0000)!=0, "Invalid value at v[$ - 1]"); - assert((u[$-1] & 0x8000_0000)==0, "Invalid value at u[$ - 1]"); - BigDigit [] scratch = new BigDigit[v.length + 1]; - - // Perform block schoolbook division, with 'v.length' blocks. - auto m = u.length - v.length; - while (m > v.length) - { - immutable mayOverflow = (u[m + v.length -1 ] & 0x8000_0000)!=0; - BigDigit saveq; - if (mayOverflow) - { - u[m + v.length] = 0; - saveq = quotient[m]; - } - recursiveDivMod(quotient[m-v.length .. m + (mayOverflow? 1: 0)], - u[m - v.length .. m + v.length + (mayOverflow? 1: 0)], v, scratch, mayOverflow); - if (mayOverflow) - { - assert(quotient[m] == 0, "quotient must not be 0"); - quotient[m] = saveq; - } - m -= v.length; - } - recursiveDivMod(quotient[0 .. m], u[0 .. m + v.length], v, scratch); - () @trusted { GC.free(scratch.ptr); } (); -} - -@system unittest -{ - version (none) - { - import core.stdc.stdio; - - void printBiguint(const uint [] data) - { - char [] buff = biguintToHex(new char[data.length*9], data, '_'); - printf("%.*s\n", cast(int) buff.length, buff.ptr); - } - - void printDecimalBigUint(BigUint data) - { - auto str = data.toDecimalString(0); - printf("%.*s\n", cast(int) str.length, str.ptr); - } - } - - uint [] a, b; - a = new uint[43]; - b = new uint[179]; - for (int i=0; i 0xFFFF_FFFF); - } - return cast(uint) c; -} - -@safe unittest -{ - uint [] a = new uint[40]; - uint [] b = new uint[40]; - uint [] c = new uint[40]; - for (size_t i = 0; i < a.length; ++i) - { - if (i&1) a[i]=cast(uint)(0x8000_0000 + i); - else a[i]=cast(uint) i; - b[i]= 0x8000_0003; - } - c[19]=0x3333_3333; - uint carry = multibyteAddSub!('+')(c[0 .. 18], b[0 .. 18], a[0 .. 18], 0); - assert(c[0]==0x8000_0003, "c[0] has invalid value"); - assert(c[1]==4, "c[1] must be for"); - assert(c[19]==0x3333_3333, "c[19] has invalid value"); // check for overrun - assert(carry == 1, "carry must be 1"); - for (size_t i = 0; i < a.length; ++i) - { - a[i] = b[i] = c[i] = 0; - } - a[8]=0x048D159E; - b[8]=0x048D159E; - a[10]=0x1D950C84; - b[10]=0x1D950C84; - a[5] =0x44444444; - carry = multibyteAddSub!('-')(a[0 .. 12], a[0 .. 12], b[0 .. 12], 0); - assert(a[11] == 0, "a[11] must be 0"); - for (size_t i = 0; i < 10; ++i) - if (i != 5) - assert(a[i] == 0, "a[1] must be 0"); - - for (size_t q = 3; q < 36; ++q) - { - for (size_t i = 0; i< a.length; ++i) - { - a[i] = b[i] = c[i] = 0; - } - a[q-2]=0x040000; - b[q-2]=0x040000; - carry = multibyteAddSub!('-')(a[0 .. q], a[0 .. q], b[0 .. q], 0); - assert(a[q-2]==0, "a[q-2] must be 0"); - } -} - - - -/** dest[] += carry, or dest[] -= carry. - * op must be '+' or '-' - * Returns final carry or borrow (0 or 1) - */ -uint multibyteIncrementAssign(char op)(uint[] dest, uint carry) - pure @nogc @safe -{ - static if (op=='+') - { - ulong c = carry; - c += dest[0]; - dest[0] = cast(uint) c; - if (c <= 0xFFFF_FFFF) - return 0; - - for (size_t i = 1; i < dest.length; ++i) - { - ++dest[i]; - if (dest[i] != 0) - return 0; - } - return 1; - } - else - { - ulong c = carry; - c = dest[0] - c; - dest[0] = cast(uint) c; - if (c <= 0xFFFF_FFFF) - return 0; - for (size_t i = 1; i < dest.length; ++i) - { - --dest[i]; - if (dest[i] != 0xFFFF_FFFF) - return 0; - } - return 1; - } -} - -/** dest[] = src[] << numbits - * numbits must be in the range 1 .. 31 - */ -uint multibyteShl(uint [] dest, const(uint) [] src, uint numbits) - pure @nogc @safe -{ - ulong c = 0; - for (size_t i = 0; i < dest.length; ++i) - { - c += (cast(ulong)(src[i]) << numbits); - dest[i] = cast(uint) c; - c >>>= 32; - } - return cast(uint) c; -} - - -/** dest[] = src[] >> numbits - * numbits must be in the range 1 .. 31 - */ -void multibyteShr(uint [] dest, const(uint) [] src, uint numbits) - pure @nogc @safe -{ - ulong c = 0; - for (ptrdiff_t i = dest.length; i != 0; --i) - { - c += (src[i-1] >>numbits) + (cast(ulong)(src[i-1]) << (64 - numbits)); - dest[i-1] = cast(uint) c; - c >>>= 32; - } -} - -@safe unittest -{ - - uint [] aa = [0x1222_2223, 0x4555_5556, 0x8999_999A, 0xBCCC_CCCD, 0xEEEE_EEEE]; - multibyteShr(aa[0..$-2], aa, 4); - assert(aa[0] == 0x6122_2222 && aa[1] == 0xA455_5555 && aa[2] == 0x0899_9999); - assert(aa[3] == 0xBCCC_CCCD); - - aa = [0x1222_2223, 0x4555_5556, 0x8999_999A, 0xBCCC_CCCD, 0xEEEE_EEEE]; - multibyteShr(aa[0..$-1], aa, 4); - assert(aa[0] == 0x6122_2222 && aa[1] == 0xA455_5555 - && aa[2] == 0xD899_9999 && aa[3] == 0x0BCC_CCCC); - - aa = [0xF0FF_FFFF, 0x1222_2223, 0x4555_5556, 0x8999_999A, 0xBCCC_CCCD, - 0xEEEE_EEEE]; - multibyteShl(aa[1 .. 4], aa[1..$], 4); - assert(aa[0] == 0xF0FF_FFFF && aa[1] == 0x2222_2230 - && aa[2]==0x5555_5561 && aa[3]==0x9999_99A4 && aa[4]==0x0BCCC_CCCD); -} - -/** dest[] = src[] * multiplier + carry. - * Returns carry. - */ -uint multibyteMul(uint[] dest, const(uint)[] src, uint multiplier, uint carry) - pure @nogc @safe -{ - assert(dest.length == src.length, "dest and src must have the same length"); - ulong c = carry; - for (size_t i = 0; i < src.length; ++i) - { - c += cast(ulong)(src[i]) * multiplier; - dest[i] = cast(uint) c; - c>>=32; - } - return cast(uint) c; -} - -@safe unittest -{ - uint [] aa = [0xF0FF_FFFF, 0x1222_2223, 0x4555_5556, 0x8999_999A, - 0xBCCC_CCCD, 0xEEEE_EEEE]; - multibyteMul(aa[1 .. 4], aa[1 .. 4], 16, 0); - assert(aa[0] == 0xF0FF_FFFF && aa[1] == 0x2222_2230 && aa[2]==0x5555_5561 - && aa[3]==0x9999_99A4 && aa[4]==0x0BCCC_CCCD); -} - -/** - * dest[] += src[] * multiplier + carry(0 .. FFFF_FFFF). - * Returns carry out of MSB (0 .. FFFF_FFFF). - */ -uint multibyteMulAdd(char op)(uint [] dest, const(uint)[] src, - uint multiplier, uint carry) pure @nogc @safe -{ - assert(dest.length == src.length, "dest and src must have the same length"); - ulong c = carry; - for (size_t i = 0; i < src.length; ++i) - { - static if (op=='+') - { - c += cast(ulong)(multiplier) * src[i] + dest[i]; - dest[i] = cast(uint) c; - c >>= 32; - } - else - { - c += cast(ulong) multiplier * src[i]; - ulong t = cast(ulong) dest[i] - cast(uint) c; - dest[i] = cast(uint) t; - c = cast(uint)((c >> 32) - (t >> 32)); - } - } - return cast(uint) c; -} - -@safe unittest -{ - - uint [] aa = [0xF0FF_FFFF, 0x1222_2223, 0x4555_5556, 0x8999_999A, - 0xBCCC_CCCD, 0xEEEE_EEEE]; - uint [] bb = [0x1234_1234, 0xF0F0_F0F0, 0x00C0_C0C0, 0xF0F0_F0F0, - 0xC0C0_C0C0]; - multibyteMulAdd!('+')(bb[1..$-1], aa[1..$-2], 16, 5); - assert(bb[0] == 0x1234_1234 && bb[4] == 0xC0C0_C0C0); - assert(bb[1] == 0x2222_2230 + 0xF0F0_F0F0 + 5 - && bb[2] == 0x5555_5561 + 0x00C0_C0C0 + 1 - && bb[3] == 0x9999_99A4 + 0xF0F0_F0F0 ); -} - - -/** - Sets result = result[0 .. left.length] + left * right - - It is defined in this way to allow cache-efficient multiplication. - This function is equivalent to: - ---- - for (size_t i = 0; i< right.length; ++i) - { - dest[left.length + i] = multibyteMulAdd(dest[i .. left.length+i], - left, right[i], 0); - } - ---- - */ -void multibyteMultiplyAccumulate(uint [] dest, const(uint)[] left, const(uint) - [] right) pure @nogc @safe -{ - for (size_t i = 0; i < right.length; ++i) - { - dest[left.length + i] = multibyteMulAdd!('+')(dest[i .. left.length+i], - left, right[i], 0); - } -} - -/** dest[] /= divisor. - * overflow is the initial remainder, and must be in the range 0 .. divisor-1. - */ -uint multibyteDivAssign(uint [] dest, uint divisor, uint overflow) - pure @nogc @safe -{ - ulong c = cast(ulong) overflow; - for (ptrdiff_t i = dest.length-1; i >= 0; --i) - { - c = (c << 32) + cast(ulong)(dest[i]); - uint q = cast(uint)(c/divisor); - c -= divisor * q; - dest[i] = q; - } - return cast(uint) c; -} - -@safe unittest -{ - uint [] aa = new uint[101]; - for (uint i = 0; i < aa.length; ++i) - aa[i] = 0x8765_4321 * (i+3); - uint overflow = multibyteMul(aa, aa, 0x8EFD_FCFB, 0x33FF_7461); - uint r = multibyteDivAssign(aa, 0x8EFD_FCFB, overflow); - for (uint i=0; i>=32) + dest[2*i+1]; - dest[2*i+1] = cast(uint) c; - c >>= 32; - } -} - -// Does half a square multiply. (square = diagonal + 2*triangle) -void multibyteTriangleAccumulate(uint[] dest, const(uint)[] x) - pure @nogc @safe -{ - // x[0]*x[1...$] + x[1]*x[2..$] + ... + x[$-2]x[$-1..$] - dest[x.length] = multibyteMul(dest[1 .. x.length], x[1..$], x[0], 0); - if (x.length < 4) - { - if (x.length == 3) - { - ulong c = cast(ulong)(x[$-1]) * x[$-2] + dest[2*x.length-3]; - dest[2*x.length - 3] = cast(uint) c; - c >>= 32; - dest[2*x.length - 2] = cast(uint) c; - } - return; - } - for (size_t i = 2; i < x.length - 2; ++i) - { - dest[i-1+ x.length] = multibyteMulAdd!('+')( - dest[i+i-1 .. i+x.length-1], x[i..$], x[i-1], 0); - } - // Unroll the last two entries, to reduce loop overhead: - ulong c = cast(ulong)(x[$-3]) * x[$-2] + dest[2*x.length-5]; - dest[2*x.length-5] = cast(uint) c; - c >>= 32; - c += cast(ulong)(x[$-3]) * x[$-1] + dest[2*x.length-4]; - dest[2*x.length-4] = cast(uint) c; - c >>= 32; - c += cast(ulong)(x[$-1]) * x[$-2]; - dest[2*x.length-3] = cast(uint) c; - c >>= 32; - dest[2*x.length-2] = cast(uint) c; -} - -void multibyteSquare(BigDigit[] result, const(BigDigit) [] x) pure @nogc @safe -{ - multibyteTriangleAccumulate(result, x); - result[$-1] = multibyteShl(result[1..$-1], result[1..$-1], 1); // mul by 2 - result[0] = 0; - multibyteAddDiagonalSquares(result, x); -} diff --git a/phobos/std/internal/math/biguintx86.d b/phobos/std/internal/math/biguintx86.d deleted file mode 100644 index d5ed6ca..0000000 --- a/phobos/std/internal/math/biguintx86.d +++ /dev/null @@ -1,1385 +0,0 @@ -/** Optimised asm arbitrary precision arithmetic ('bignum') - * routines for X86 processors. - * - * All functions operate on arrays of uints, stored LSB first. - * If there is a destination array, it will be the first parameter. - * Currently, all of these functions are subject to change, and are - * intended for internal use only. - * The symbol [#] indicates an array of machine words which is to be - * interpreted as a multi-byte number. - */ - -/* Copyright Don Clugston 2008 - 2010. - * 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) - */ -/** - * In simple terms, there are 3 modern x86 microarchitectures: - * (a) the P6 family (Pentium Pro, PII, PIII, PM, Core), produced by Intel; - * (b) the K6, Athlon, and AMD64 families, produced by AMD; and - * (c) the Pentium 4, produced by Marketing. - * - * This code has been optimised for the Intel P6 family. - * Generally the code remains near-optimal for Intel Core2/Corei7, after - * translating EAX-> RAX, etc, since all these CPUs use essentially the same - * pipeline, and are typically limited by memory access. - * The code uses techniques described in Agner Fog's superb Pentium manuals - * available at www.agner.org. - * Not optimised for AMD, which can do two memory loads per cycle (Intel - * CPUs can only do one). Despite this, performance is superior on AMD. - * Performance is dreadful on P4. - * - * Timing results (cycles per int) - * --Intel Pentium-- --AMD-- - * PM P4 Core2 K7 - * +,- 2.25 15.6 2.25 1.5 - * <<,>> 2.0 6.6 2.0 5.0 - * (<< MMX) 1.7 5.3 1.5 1.2 - * * 5.0 15.0 4.0 4.3 - * mulAdd 5.7 19.0 4.9 4.0 - * div 30.0 32.0 32.0 22.4 - * mulAcc(32) 6.5 20.0 5.4 4.9 - * - * mulAcc(32) is multiplyAccumulate() for a 32*32 multiply. Thus it includes - * function call overhead. - * The timing for Div is quite unpredictable, but it's probably too slow - * to be useful. On 64-bit processors, these times should - * halve if run in 64-bit mode, except for the MMX functions. - */ - -module std.internal.math.biguintx86; - -@system: -pure: -nothrow: - -/* - Naked asm is used throughout, because: - (a) it frees up the EBP register - (b) compiler bugs prevent the use of .ptr when a frame pointer is used. -*/ - -// LDC: misc. asm adaptations due to non-reversed extern(D) parameters! - -version (D_InlineAsm_X86) -{ - -private: - -/* Duplicate string s, with n times, substituting index for '@'. - * - * Each instance of '@' in s is replaced by 0,1,...n-1. This is a helper - * function for some of the asm routines. - */ -string indexedLoopUnroll(int n, string s) pure @safe -{ - string u; - for (int i = 0; i9 ? ""~ cast(char)('0'+i/10) : "") ~ cast(char)('0' + i%10); - - int last = 0; - for (int j = 0; j> numbits - * numbits must be in the range 1 .. 31 - * This version uses MMX. - */ -uint multibyteShl(uint [] dest, const uint [] src, uint numbits) pure @safe @nogc -{ - // Timing: - // K7 1.2/int. PM 1.7/int P4 5.3/int - enum { FIRSTPARAM = 4*4 } // 3* pushes + return address. - asm pure nothrow @nogc @trusted { - naked; - push ESI; - push EDI; - push EBX; - mov EBX, [ESP + FIRSTPARAM + 4*0]; //dest.length; - mov EDI, [ESP + FIRSTPARAM + 4*1]; //dest.ptr; - mov ESI, [ESP + FIRSTPARAM + 4*3]; //src.ptr; - mov EAX, [ESP + FIRSTPARAM + 4*4]; //numbits; - - movd MM3, EAX; // numbits = bits to shift left - xor EAX, 63; - align 16; - inc EAX; - movd MM4, EAX ; // 64-numbits = bits to shift right - - // Get the return value into EAX - and EAX, 31; // EAX = 32-numbits - movd MM2, EAX; // 32-numbits - movd MM1, [ESI+4*EBX-4]; - psrlq MM1, MM2; - movd EAX, MM1; // EAX = return value - test EBX, 1; - jz L_even; -L_odd: - cmp EBX, 1; - jz L_length1; - - // deal with odd lengths - movq MM1, [ESI+4*EBX-8]; - psrlq MM1, MM2; - movd [EDI +4*EBX-4], MM1; - sub EBX, 1; -L_even: // It's either singly or doubly even - movq MM2, [ESI + 4*EBX - 8]; - psllq MM2, MM3; - sub EBX, 2; - jle L_last; - movq MM1, MM2; - add EBX, 2; - test EBX, 2; - jz L_onceeven; - sub EBX, 2; - - // MAIN LOOP -- 128 bytes per iteration - L_twiceeven: // here MM2 is the carry - movq MM0, [ESI + 4*EBX-8]; - psrlq MM0, MM4; - movq MM1, [ESI + 4*EBX-8]; - psllq MM1, MM3; - por MM2, MM0; - movq [EDI +4*EBX], MM2; -L_onceeven: // here MM1 is the carry - movq MM0, [ESI + 4*EBX-16]; - psrlq MM0, MM4; - movq MM2, [ESI + 4*EBX-16]; - por MM1, MM0; - movq [EDI +4*EBX-8], MM1; - psllq MM2, MM3; - sub EBX, 4; - jg L_twiceeven; -L_last: - movq [EDI +4*EBX], MM2; -L_alldone: - emms; // NOTE: costs 6 cycles on Intel CPUs - pop EBX; - pop EDI; - pop ESI; - ret 5*4; - -L_length1: - // length 1 is a special case - movd MM1, [ESI]; - psllq MM1, MM3; - movd [EDI], MM1; - jmp L_alldone; - } -} - -void multibyteShr(uint [] dest, const uint [] src, uint numbits) pure @safe @nogc -{ - enum { FIRSTPARAM = 4*4 } // 3* pushes + return address. - asm pure nothrow @nogc @trusted { - naked; - push ESI; - push EDI; - push EBX; - mov EBX, [ESP + FIRSTPARAM + 4*0]; //dest.length; - mov EDI, [ESP + FIRSTPARAM + 4*1]; //dest.ptr; - mov EAX, [ESP + FIRSTPARAM + 4*4]; //numbits; -align 16; - mov ESI, [ESP + FIRSTPARAM + 4*3]; //src.ptr; - lea EDI, [EDI + 4*EBX]; // EDI = end of dest - lea ESI, [ESI + 4*EBX]; // ESI = end of src - neg EBX; // count UP to zero. - - movd MM3, EAX; // numbits = bits to shift right - xor EAX, 63; - inc EAX; - movd MM4, EAX ; // 64-numbits = bits to shift left - - test EBX, 1; - jz L_even; -L_odd: - // deal with odd lengths - and EAX, 31; // EAX = 32-numbits - movd MM2, EAX; // 32-numbits - cmp EBX, -1; - jz L_length1; - - movq MM0, [ESI+4*EBX]; - psrlq MM0, MM3; - movd [EDI +4*EBX], MM0; - add EBX, 1; -L_even: - movq MM2, [ESI + 4*EBX]; - psrlq MM2, MM3; - - movq MM1, MM2; - add EBX, 4; - cmp EBX, -2+4; - jz L_last; - // It's either singly or doubly even - sub EBX, 2; - test EBX, 2; - jnz L_onceeven; - add EBX, 2; - - // MAIN LOOP -- 128 bytes per iteration - L_twiceeven: // here MM2 is the carry - movq MM0, [ESI + 4*EBX-8]; - psllq MM0, MM4; - movq MM1, [ESI + 4*EBX-8]; - psrlq MM1, MM3; - por MM2, MM0; - movq [EDI +4*EBX-16], MM2; -L_onceeven: // here MM1 is the carry - movq MM0, [ESI + 4*EBX]; - psllq MM0, MM4; - movq MM2, [ESI + 4*EBX]; - por MM1, MM0; - movq [EDI +4*EBX-8], MM1; - psrlq MM2, MM3; - add EBX, 4; - jl L_twiceeven; -L_last: - movq [EDI +4*EBX-16], MM2; -L_alldone: - emms; // NOTE: costs 6 cycles on Intel CPUs - pop EBX; - pop EDI; - pop ESI; - ret 5*4; - -L_length1: - // length 1 is a special case - movd MM1, [ESI+4*EBX]; - psrlq MM1, MM3; - movd [EDI +4*EBX], MM1; - jmp L_alldone; - - } -} - -/** dest[#] = src[#] >> numbits - * numbits must be in the range 1 .. 31 - */ -void multibyteShrNoMMX(uint [] dest, const uint [] src, uint numbits) pure @safe @nogc -{ - // Timing: Optimal for P6 family. - // 2.0 cycles/int on PPro .. PM (limited by execution port p0) - // Terrible performance on AMD64, which has 7 cycles for SHRD!! - enum { FIRSTPARAM = 4*4 } // 3* pushes + return address. - asm pure nothrow @nogc @trusted { - naked; - push ESI; - push EDI; - push EBX; - mov EBX, [ESP + FIRSTPARAM + 4*0]; //dest.length; - mov EDI, [ESP + FIRSTPARAM + 4*1]; //dest.ptr; - mov ESI, [ESP + FIRSTPARAM + 4*3]; //src.ptr; - mov ECX, [ESP + FIRSTPARAM + 4*4]; //numbits; - - lea EDI, [EDI + 4*EBX]; // EDI = end of dest - lea ESI, [ESI + 4*EBX]; // ESI = end of src - neg EBX; // count UP to zero. - mov EAX, [ESI + 4*EBX]; - cmp EBX, -1; - jz L_last; - mov EDX, [ESI + 4*EBX]; - test EBX, 1; - jz L_odd; - add EBX, 1; -L_even: - mov EDX, [ ESI + 4*EBX]; - shrd EAX, EDX, CL; - mov [-4 + EDI+4*EBX], EAX; -L_odd: - mov EAX, [4 + ESI + 4*EBX]; - shrd EDX, EAX, CL; - mov [EDI + 4*EBX], EDX; - add EBX, 2; - jl L_even; -L_last: - shr EAX, CL; - mov [-4 + EDI], EAX; - - pop EBX; - pop EDI; - pop ESI; - ret 5*4; - } -} - -@safe unittest -{ - - uint [] aa = [0x1222_2223, 0x4555_5556, 0x8999_999A, 0xBCCC_CCCD, 0xEEEE_EEEE]; - multibyteShr(aa[0..$-1], aa, 4); - assert(aa[0] == 0x6122_2222 && aa[1]==0xA455_5555 - && aa[2]==0xD899_9999 && aa[3]==0x0BCC_CCCC); - - aa = [0x1222_2223, 0x4555_5556, 0x8999_999A, 0xBCCC_CCCD, 0xEEEE_EEEE]; - multibyteShr(aa[2..$-1], aa[2..$-1], 4); - assert(aa[0] == 0x1222_2223 && aa[1]==0x4555_5556 - && aa[2]==0xD899_9999 && aa[3]==0x0BCC_CCCC); - - aa = [0x1222_2223, 0x4555_5556, 0x8999_999A, 0xBCCC_CCCD, 0xEEEE_EEEE]; - multibyteShr(aa[0..$-2], aa, 4); - assert(aa[1]==0xA455_5555 && aa[2]==0x0899_9999); - assert(aa[0]==0x6122_2222); - assert(aa[3]==0xBCCC_CCCD); - - - aa = [0xF0FF_FFFF, 0x1222_2223, 0x4555_5556, 0x8999_999A, 0xBCCC_CCCD, 0xEEEE_EEEE]; - uint r = multibyteShl(aa[2 .. 4], aa[2 .. 4], 4); - assert(aa[0] == 0xF0FF_FFFF && aa[1]==0x1222_2223 - && aa[2]==0x5555_5560 && aa[3]==0x9999_99A4 && aa[4]==0xBCCC_CCCD); - assert(r == 8); - - aa = [0xF0FF_FFFF, 0x1222_2223, 0x4555_5556, 0x8999_999A, 0xBCCC_CCCD, 0xEEEE_EEEE]; - r = multibyteShl(aa[1 .. 4], aa[1 .. 4], 4); - assert(aa[0] == 0xF0FF_FFFF - && aa[2]==0x5555_5561); - assert(aa[3]==0x9999_99A4 && aa[4]==0xBCCC_CCCD); - assert(r == 8); - assert(aa[1]==0x2222_2230); - - aa = [0xF0FF_FFFF, 0x1222_2223, 0x4555_5556, 0x8999_999A, 0xBCCC_CCCD, 0xEEEE_EEEE]; - r = multibyteShl(aa[0 .. 4], aa[1 .. 5], 31); -} - -/** dest[#] = src[#] * multiplier + carry. - * Returns carry. - */ -uint multibyteMul(uint[] dest, const uint[] src, uint multiplier, uint carry) - pure @safe @nogc -{ - // Timing: definitely not optimal. - // Pentium M: 5.0 cycles/operation, has 3 resource stalls/iteration - // Fastest implementation found was 4.6 cycles/op, but not worth the complexity. - - enum { FIRSTPARAM = 4*4 } // 4* pushes + return address. - // We'll use p2 (load unit) instead of the overworked p0 or p1 (ALU units) - // when initializing variables to zero. - version (D_PIC) - { - enum { zero = 0 } - } - else version (LDC) - { - // Cannot define statics in naked functions with LDC. - enum { zero = 0 } - } - else - { - static immutable int zero = 0; - } - asm pure nothrow @nogc @trusted { - naked; - push ESI; - push EDI; - push EBX; - - mov EBX, [ESP + FIRSTPARAM + 4*0]; // dest.length - mov EDI, [ESP + FIRSTPARAM + 4*1]; // dest.ptr - mov ESI, [ESP + FIRSTPARAM + 4*3]; // src.ptr - align 16; - lea EDI, [EDI + 4*EBX]; // EDI = end of dest - lea ESI, [ESI + 4*EBX]; // ESI = end of src - mov ECX, [ESP + FIRSTPARAM + 4*5]; // carry - neg EBX; // count UP to zero. - test EBX, 1; - jnz L_odd; - add EBX, 1; - L1: - mov EAX, [-4 + ESI + 4*EBX]; - mul int ptr [ESP + FIRSTPARAM + 4*4]; //[multiplier]; - add EAX, ECX; - mov ECX, zero; - mov [-4+EDI + 4*EBX], EAX; - adc ECX, EDX; -L_odd: - mov EAX, [ESI + 4*EBX]; // p2 - mul int ptr [ESP + FIRSTPARAM + 4*4]; //[multiplier]; // p0*3, - add EAX, ECX; - mov ECX, zero; - adc ECX, EDX; - mov [EDI + 4*EBX], EAX; - add EBX, 2; - jl L1; - - mov EAX, ECX; // get final carry - - pop EBX; - pop EDI; - pop ESI; - ret 6*4; - } -} - -@system unittest -{ - uint [] aa = [0xF0FF_FFFF, 0x1222_2223, 0x4555_5556, 0x8999_999A, 0xBCCC_CCCD, 0xEEEE_EEEE]; - multibyteMul(aa[1 .. 4], aa[1 .. 4], 16, 0); - assert(aa[0] == 0xF0FF_FFFF && aa[1] == 0x2222_2230 && - aa[2]==0x5555_5561 && aa[3]==0x9999_99A4 && aa[4]==0x0BCCC_CCCD); -} - -// The inner multiply-and-add loop, together with the Even entry point. -// Multiples by M_ADDRESS which should be "ESP+FIRSTPARAM" or "ESP". OP must be "add" or "sub" -// This is the most time-critical code in the BigInt library. -// It is used by both MulAdd, multiplyAccumulate, and triangleAccumulate -string asmMulAdd_innerloop(string OP, string M_ADDRESS) pure @safe { - // The bottlenecks in this code are extremely complicated. The MUL, ADD, and ADC - // need 4 cycles on each of the ALUs units p0 and p1. So we use memory load - // (unit p2) for initializing registers to zero. - // There are also dependencies between the instructions, and we run up against the - // ROB-read limit (can only read 2 registers per cycle). - // We also need the number of uops in the loop to be a multiple of 3. - // The only available execution unit for this is p3 (memory write). Unfortunately we can't do that - // if Position-Independent Code is required. - - // Register usage - // ESI = end of src - // EDI = end of dest - // EBX = index. Counts up to zero (in steps of 2). - // EDX:EAX = scratch, used in multiply. - // ECX = carry1. - // EBP = carry2. - // ESP = points to the multiplier. - - // The first member of 'dest' which will be modified is [EDI+4*EBX]. - // EAX must already contain the first member of 'src', [ESI+4*EBX]. - - version (D_PIC) { bool using_PIC = true; } else version (LDC) { bool using_PIC = true; } else { bool using_PIC = false; } - return " - // Entry point for even length - add EBX, 1; - mov EBP, ECX; // carry - - mul int ptr [" ~ M_ADDRESS ~ "]; // M - mov ECX, 0; - - add EBP, EAX; - mov EAX, [ESI+4*EBX]; - adc ECX, EDX; - - mul int ptr [" ~ M_ADDRESS ~ "]; // M - " ~ OP ~ " [-4+EDI+4*EBX], EBP; - mov EBP, zero; - - adc ECX, EAX; - mov EAX, [4+ESI+4*EBX]; - - adc EBP, EDX; - add EBX, 2; - jnl L_done; -L1: - mul int ptr [" ~ M_ADDRESS ~ "]; - " ~ OP ~ " [-8+EDI+4*EBX], ECX; - adc EBP, EAX; - mov ECX, zero; - mov EAX, [ESI+4*EBX]; - adc ECX, EDX; -" ~ - (using_PIC ? "" : " mov storagenop, EDX; ") // make #uops in loop a multiple of 3, can't do this in PIC mode. -~ " - mul int ptr [" ~ M_ADDRESS ~ "]; - " ~ OP ~ " [-4+EDI+4*EBX], EBP; - mov EBP, zero; - - adc ECX, EAX; - mov EAX, [4+ESI+4*EBX]; - - adc EBP, EDX; - add EBX, 2; - jl L1; -L_done: " ~ OP ~ " [-8+EDI+4*EBX], ECX; - adc EBP, 0; -"; - // final carry is now in EBP -} - -string asmMulAdd_enter_odd(string OP, string M_ADDRESS) pure @safe -{ - return " - mul int ptr [" ~M_ADDRESS ~"]; - mov EBP, zero; - add ECX, EAX; - mov EAX, [4+ESI+4*EBX]; - - adc EBP, EDX; - add EBX, 2; - jl L1; - jmp L_done; -"; -} - -version (D_PIC) {} else -{ - // This cannot be declared inside the relevant functions because __gshared is - // not allowed in @safe functions. It cannot be regular shared either since - // that gives an access violation on Win32 for some reason. - private __gshared int storagenopTriangleAccumulate; - private __gshared int storagenopMulAdd; - private __gshared int storagenopMultiplyAccumulate; -} - -/** - * dest[#] += src[#] * multiplier OP carry(0 .. FFFF_FFFF). - * where op == '+' or '-' - * Returns carry out of MSB (0 .. FFFF_FFFF). - */ -uint multibyteMulAdd(char op)(uint [] dest, const uint [] src, uint - multiplier, uint carry) pure @safe @nogc { - // Timing: This is the most time-critical bignum function. - // Pentium M: 5.4 cycles/operation, still has 2 resource stalls + 1load block/iteration - - // The main loop is pipelined and unrolled by 2, - // so entry to the loop is also complicated. - - // Register usage - // EDX:EAX = multiply - // EBX = counter - // ECX = carry1 - // EBP = carry2 - // EDI = dest - // ESI = src - - enum string OP = (op=='+')? "add" : "sub"; - version (D_PIC) - { - enum { zero = 0 } - } - else version (LDC) - { - // Cannot define statics in naked functions with LDC. - enum { zero = 0 } - } - else - { - // use p2 (load unit) instead of the overworked p0 or p1 (ALU units) - // when initializing registers to zero. - static immutable int zero = 0; - // use p3/p4 units - alias storagenop = storagenopMulAdd; // write-only - } - - enum { FIRSTPARAM = 5*4 } // 4* pushes + return address. - asm pure nothrow @nogc @trusted { - naked; - - push ESI; - push EDI; - push EBX; - push EBP; - mov EBX, [ESP + FIRSTPARAM + 4*0]; // dest.length - mov EDI, [ESP + FIRSTPARAM + 4*1]; // dest.ptr - align 16; - nop; - mov ESI, [ESP + FIRSTPARAM + 4*3]; // src.ptr - lea EDI, [EDI + 4*EBX]; // EDI = end of dest - lea ESI, [ESI + 4*EBX]; // ESI = end of src - mov EBP, 0; - mov ECX, [ESP + FIRSTPARAM + 4*5]; // ECX = input carry. - neg EBX; // count UP to zero. - mov EAX, [ESI+4*EBX]; - test EBX, 1; - jnz L_enter_odd; - } - // Main loop, with entry point for even length - mixin("asm pure nothrow @nogc @trusted {" ~ asmMulAdd_innerloop(OP, "ESP+FIRSTPARAM+4*4") ~ "}"); - asm pure nothrow @nogc @trusted { - mov EAX, EBP; // get final carry - pop EBP; - pop EBX; - pop EDI; - pop ESI; - ret 6*4; - } -L_enter_odd: - mixin("asm pure nothrow @nogc @trusted {" ~ asmMulAdd_enter_odd(OP, "ESP+FIRSTPARAM+4*4") ~ "}"); -} - -@system unittest -{ - - uint [] aa = [0xF0FF_FFFF, 0x1222_2223, 0x4555_5556, 0x8999_999A, 0xBCCC_CCCD, 0xEEEE_EEEE]; - uint [] bb = [0x1234_1234, 0xF0F0_F0F0, 0x00C0_C0C0, 0xF0F0_F0F0, 0xC0C0_C0C0]; - multibyteMulAdd!('+')(bb[1..$-1], aa[1..$-2], 16, 5); - assert(bb[0] == 0x1234_1234 && bb[4] == 0xC0C0_C0C0); - assert(bb[1] == 0x2222_2230 + 0xF0F0_F0F0+5 && bb[2] == 0x5555_5561+0x00C0_C0C0+1 - && bb[3] == 0x9999_99A4+0xF0F0_F0F0 ); -} - -/** - Sets result[#] = result[0 .. left.length] + left[#] * right[#] - - It is defined in this way to allow cache-efficient multiplication. - This function is equivalent to: - ---- - for (int i = 0; i< right.length; ++i) - { - dest[left.length + i] = multibyteMulAdd(dest[i .. left.length+i], - left, right[i], 0); - } - ---- - */ -void multibyteMultiplyAccumulate(uint [] dest, const uint[] left, - const uint [] right) pure @safe @nogc { - // Register usage - // EDX:EAX = used in multiply - // EBX = index - // ECX = carry1 - // EBP = carry2 - // EDI = end of dest for this pass through the loop. Index for outer loop. - // ESI = end of left. never changes - // [ESP] = M = right[i] = multiplier for this pass through the loop. - // right.length is changed into dest.ptr+dest.length - version (D_PIC) - { - enum { zero = 0 } - } - else version (LDC) - { - // Cannot define statics in naked functions with LDC. - enum { zero = 0 } - } - else - { - // use p2 (load unit) instead of the overworked p0 or p1 (ALU units) - // when initializing registers to zero. - static immutable int zero = 0; - // use p3/p4 units - alias storagenop = storagenopMultiplyAccumulate; // write-only - } - - enum { FIRSTPARAM = 6*4 } // 4* pushes + local + return address. - asm pure nothrow @nogc @trusted { - naked; - - push ESI; - push EDI; - align 16; - push EBX; - push EBP; - push EAX; // local variable M - mov EDI, [ESP + FIRSTPARAM + 4*1]; // dest.ptr - mov EBX, [ESP + FIRSTPARAM + 4*2]; // left.length - mov ESI, [ESP + FIRSTPARAM + 4*3]; // left.ptr - lea EDI, [EDI + 4*EBX]; // EDI = end of dest for first pass - - mov EAX, [ESP + FIRSTPARAM + 4*4]; // right.length - lea EAX, [EDI + 4*EAX]; - mov [ESP + FIRSTPARAM + 4*4], EAX; // last value for EDI - - lea ESI, [ESI + 4*EBX]; // ESI = end of left - mov EAX, [ESP + FIRSTPARAM + 4*5]; // right.ptr - mov EAX, [EAX]; - mov [ESP], EAX; // M -outer_loop: - mov EBP, 0; - mov ECX, 0; // ECX = input carry. - neg EBX; // count UP to zero. - mov EAX, [ESI+4*EBX]; - test EBX, 1; - jnz L_enter_odd; - } - // -- Inner loop, with even entry point - mixin("asm pure nothrow @nogc @trusted { " ~ asmMulAdd_innerloop("add", "ESP") ~ "}"); - asm pure nothrow @nogc @trusted { - mov [-4+EDI+4*EBX], EBP; - add EDI, 4; - cmp EDI, [ESP + FIRSTPARAM + 4*4]; // is EDI = &dest[$]? - jz outer_done; - mov EAX, [ESP + FIRSTPARAM + 4*5]; // right.ptr - mov EAX, [EAX+4]; // get new M - mov [ESP], EAX; // save new M - add int ptr [ESP + FIRSTPARAM + 4*5], 4; // right.ptr - mov EBX, [ESP + FIRSTPARAM + 4*2]; // left.length - jmp outer_loop; -outer_done: - pop EAX; - pop EBP; - pop EBX; - pop EDI; - pop ESI; - ret 6*4; - } -L_enter_odd: - mixin("asm pure nothrow @nogc @trusted {" ~ asmMulAdd_enter_odd("add", "ESP") ~ "}"); -} - -/** dest[#] /= divisor. - * overflow is the initial remainder, and must be in the range 0 .. divisor-1. - * divisor must not be a power of 2 (use right shift for that case; - * A division by zero will occur if divisor is a power of 2). - * Returns the final remainder - * - * Based on public domain code by Eric Bainville. - * (http://www.bealto.com/) Used with permission. - */ -uint multibyteDivAssign(uint [] dest, uint divisor, uint overflow) pure @safe @nogc -{ - // Timing: limited by a horrible dependency chain. - // Pentium M: 18 cycles/op, 8 resource stalls/op. - // EAX, EDX = scratch, used by MUL - // EDI = dest - // CL = shift - // ESI = quotient - // EBX = remainderhi - // EBP = remainderlo - // [ESP-4] = mask - // [ESP] = kinv (2^64 /divisor) - enum { FIRSTPARAM = 5*4 } // 4* pushes + return address. - enum { LOCALS = 2*4} // MASK, KINV - asm pure nothrow @nogc @trusted { - naked; - - push ESI; - push EDI; - push EBX; - push EBP; - - mov EBX, [ESP + FIRSTPARAM + 4*0]; // dest.length - mov EDI, [ESP + FIRSTPARAM + 4*1]; // dest.ptr - - // Loop from msb to lsb - lea EDI, [EDI + 4*EBX]; - mov EBP, [ESP + FIRSTPARAM + 4*3]; // rem is the input remainder, in 0 .. divisor-1 - // Build the pseudo-inverse of divisor k: 2^64/k - // First determine the shift in ecx to get the max number of bits in kinv - xor ECX, ECX; - mov EAX, [ESP + FIRSTPARAM + 4*2]; //divisor; - mov EDX, 1; -kinv1: - inc ECX; - ror EDX, 1; - shl EAX, 1; - jnc kinv1; - dec ECX; - // Here, ecx is a left shift moving the msb of k to bit 32 - - mov EAX, 1; - shl EAX, CL; - dec EAX; - ror EAX, CL ; //ecx bits at msb - push EAX; // MASK - - // Then divide 2^(32+cx) by divisor (edx already ok) - xor EAX, EAX; - div int ptr [ESP + FIRSTPARAM+LOCALS + 4*1]; //divisor; - push EAX; // kinv - align 16; -L2: - // Get 32 bits of quotient approx, multiplying - // most significant word of (rem*2^32+input) - mov EAX, [ESP+4]; //MASK; - and EAX, [EDI - 4]; - or EAX, EBP; - rol EAX, CL; - mov EBX, EBP; - mov EBP, [EDI - 4]; - mul int ptr [ESP]; //KINV; - - shl EAX, 1; - rcl EDX, 1; - - // Multiply by k and subtract to get remainder - // Subtraction must be done on two words - mov EAX, EDX; - mov ESI, EDX; // quot = high word - mul int ptr [ESP + FIRSTPARAM+LOCALS + 4*2]; //divisor; - sub EBP, EAX; - sbb EBX, EDX; - jz Lb; // high word is 0, goto adjust on single word - - // Adjust quotient and remainder on two words -Ld: inc ESI; - sub EBP, [ESP + FIRSTPARAM+LOCALS + 4*2]; //divisor; - sbb EBX, 0; - jnz Ld; - - // Adjust quotient and remainder on single word -Lb: cmp EBP, [ESP + FIRSTPARAM+LOCALS + 4*2]; //divisor; - jc Lc; // rem in 0 .. divisor-1, OK - sub EBP, [ESP + FIRSTPARAM+LOCALS + 4*2]; //divisor; - inc ESI; - jmp Lb; - - // Store result -Lc: - mov [EDI - 4], ESI; - lea EDI, [EDI - 4]; - dec int ptr [ESP + FIRSTPARAM+LOCALS + 4*0]; // dest.length - jnz L2; - - pop EAX; // discard kinv - pop EAX; // discard mask - - mov EAX, EBP; // return final remainder - pop EBP; - pop EBX; - pop EDI; - pop ESI; - ret 4*4; - } -} - -@safe unittest -{ - uint [] aa = new uint[101]; - for (int i=0; i>= 32; - c += cast(ulong)(x[$-3]) * x[$-1] + dest[$-4]; - dest[$-4] = cast(uint) c; - c >>= 32; -length2: - c += cast(ulong)(x[$-2]) * x[$-1]; - dest[$-3] = cast(uint) c; - c >>= 32; - dest[$-2] = cast(uint) c; -} - -//dest += src[0]*src[1...$] + src[1]*src[2..$] + ... + src[$-3]*src[$-2..$]+ src[$-2]*src[$-1] -// assert(dest.length = src.length*2); -// assert(src.length >= 3); -void multibyteTriangleAccumulateAsm(uint[] dest, const uint[] src) pure @safe @nogc -{ - // Register usage - // EDX:EAX = used in multiply - // EBX = index - // ECX = carry1 - // EBP = carry2 - // EDI = end of dest for this pass through the loop. Index for outer loop. - // ESI = end of src. never changes - // [ESP] = M = src[i] = multiplier for this pass through the loop. - // dest.length is changed into dest.ptr+dest.length - version (D_PIC) - { - enum { zero = 0 } - } - else version (LDC) - { - // Cannot define statics in naked functions with LDC. - enum { zero = 0 } - } - else - { - // use p2 (load unit) instead of the overworked p0 or p1 (ALU units) - // when initializing registers to zero. - static immutable int zero = 0; - // use p3/p4 units - alias storagenop = storagenopTriangleAccumulate; // write-only - } - - enum { FIRSTPARAM = 6*4 } // 4* pushes + local + return address. - asm pure nothrow @nogc @trusted { - naked; - - push ESI; - push EDI; - align 16; - push EBX; - push EBP; - push EAX; // local variable M= src[i] - mov EDI, [ESP + FIRSTPARAM + 4*1]; // dest.ptr - mov EBX, [ESP + FIRSTPARAM + 4*2]; // src.length - mov ESI, [ESP + FIRSTPARAM + 4*3]; // src.ptr - - lea ESI, [ESI + 4*EBX]; // ESI = end of left - add int ptr [ESP + FIRSTPARAM + 4*3], 4; // src.ptr, used for getting M - - // local variable [ESP + FIRSTPARAM + 4*0] = last value for EDI - lea EDI, [EDI + 4*EBX]; // EDI = end of dest for first pass - - lea EAX, [EDI + 4*EBX-3*4]; // up to src.length - 3 - mov [ESP + FIRSTPARAM + 4*0], EAX; // last value for EDI = &dest[src.length*2 -3] - - cmp EBX, 3; - jz length_is_3; - - // We start at src[1], not src[0]. - dec EBX; - mov [ESP + FIRSTPARAM + 4*2], EBX; - -outer_loop: - mov EBX, [ESP + FIRSTPARAM + 4*2]; // src.length - mov EBP, 0; - mov ECX, 0; // ECX = input carry. - dec int ptr [ESP + FIRSTPARAM + 4*2]; // Next time, the length will be shorter by 1. - neg EBX; // count UP to zero. - - mov EAX, [ESI + 4*EBX - 4*1]; // get new M - mov [ESP], EAX; // save new M - - mov EAX, [ESI+4*EBX]; - test EBX, 1; - jnz L_enter_odd; - } - // -- Inner loop, with even entry point - mixin("asm pure nothrow @nogc @trusted { " ~ asmMulAdd_innerloop("add", "ESP") ~ "}"); - asm pure nothrow @nogc @trusted { - mov [-4+EDI+4*EBX], EBP; - add EDI, 4; - cmp EDI, [ESP + FIRSTPARAM + 4*0]; // is EDI = &dest[$-3]? - jnz outer_loop; -length_is_3: - mov EAX, [ESI - 4*3]; - mul EAX, [ESI - 4*2]; - mov ECX, 0; - add [EDI-2*4], EAX; // ECX:dest[$-5] += x[$-3] * x[$-2] - adc ECX, EDX; - - mov EAX, [ESI - 4*3]; - mul EAX, [ESI - 4*1]; // x[$-3] * x[$-1] - add EAX, ECX; - mov ECX, 0; - adc EDX, 0; - // now EDX: EAX = c + x[$-3] * x[$-1] - add [EDI-1*4], EAX; // ECX:dest[$-4] += (EDX:EAX) - adc ECX, EDX; // ECX holds dest[$-3], it acts as carry for the last row -// do length == 2 - mov EAX, [ESI - 4*2]; - mul EAX, [ESI - 4*1]; - add ECX, EAX; - adc EDX, 0; - mov [EDI - 0*4], ECX; // dest[$-2:$-3] = c + x[$-2] * x[$-1]; - mov [EDI + 1*4], EDX; - - pop EAX; - pop EBP; - pop EBX; - pop EDI; - pop ESI; - ret 4*4; - } -L_enter_odd: - mixin("asm pure nothrow @nogc @trusted {" ~ asmMulAdd_enter_odd("add", "ESP") ~ "}"); -} - -@safe unittest -{ - uint [] aa = new uint[200]; - uint [] a = aa[0 .. 100]; - uint [] b = new uint [100]; - aa[] = 761; - a[] = 0; - b[] = 0; - a[3] = 6; - b[0]=1; - b[1] = 17; - b[50 .. 100]=78; - multibyteTriangleAccumulateAsm(a, b[0 .. 50]); - uint [] c = new uint[100]; - c[] = 0; - c[1] = 17; - c[3] = 6; - assert(a[]==c[]); - assert(a[0]==0); - aa[] = 0xFFFF_FFFF; - a[] = 0; - b[] = 0; - b[0]= 0xbf6a1f01; - b[1]= 0x6e38ed64; - b[2]= 0xdaa797ed; - b[3] = 0; - - multibyteTriangleAccumulateAsm(a[0 .. 8], b[0 .. 4]); - assert(a[1]==0x3a600964); - assert(a[2]==0x339974f6); - assert(a[3]==0x46736fce); - assert(a[4]==0x5e24a2b4); - - b[3] = 0xe93ff9f4; - b[4] = 0x184f03; - a[]=0; - multibyteTriangleAccumulateAsm(a[0 .. 14], b[0 .. 7]); - assert(a[3]==0x79fff5c2); - assert(a[4]==0xcf384241); - assert(a[5]== 0x4a17fc8); - assert(a[6]==0x4d549025); -} - - -void multibyteSquare(BigDigit[] result, const BigDigit [] x) pure @safe @nogc -{ - if (x.length < 4) - { - // Special cases, not worth doing triangular. - result[x.length] = multibyteMul(result[0 .. x.length], x, x[0], 0); - multibyteMultiplyAccumulate(result[1..$], x, x[1..$]); - return; - } - // Do half a square multiply. - // dest += src[0]*src[1...$] + src[1]*src[2..$] + ... + src[$-3]*src[$-2..$]+ src[$-2]*src[$-1] - result[x.length] = multibyteMul(result[1 .. x.length], x[1..$], x[0], 0); - multibyteTriangleAccumulateAsm(result[2..$], x[1..$]); - // Multiply by 2 - result[$-1] = multibyteShlNoMMX(result[1..$-1], result[1..$-1], 1); - // And add the diagonal elements - result[0] = 0; - multibyteAddDiagonalSquares(result, x); -} - -version (BignumPerformanceTest) -{ -import core.stdc.stdio; -int clock() { asm { push EBX; xor EAX, EAX; cpuid; pop EBX; rdtsc; } } - -__gshared uint [2200] X1; -__gshared uint [2200] Y1; -__gshared uint [4000] Z1; - -void testPerformance() pure -{ - // The performance results at the top of this file were obtained using - // a Windows device driver to access the CPU performance counters. - // The code below is less accurate but more widely usable. - // The value for division is quite inconsistent. - for (int i=0; i$0 - * GAMMA = Γ - * INTEGRAL = ∫ - * INTEGRATE = $(BIG ∫$(SMALL $1)$2) - * POWER = $1$2 - * BIGSUM = $(BIG Σ $2$(SMALL $1)) - * CHOOSE = $(BIG () $(SMALL $1)$(SMALL $2) $(BIG )) - * TABLE_SV = - * - * $0
Special Values
- * SVH = $(TR $(TH $1) $(TH $2)) - * SV = $(TR $(TD $1) $(TD $2)) - */ -module std.internal.math.errorfunction; -import std.math; -import core.math : fabs, sqrt; - -pure: -nothrow: -@safe: -@nogc: - -private { -immutable real EXP_2 = 0.135335283236612691893999494972484403L; /* exp(-2) */ -enum real SQRT2PI = 2.50662827463100050241576528481104525L; // sqrt(2pi) - - -enum real MAXLOG = 0x1.62e42fefa39ef358p+13L; // log(real.max) -enum real MINLOG = -0x1.6436716d5406e6d8p+13L; // log(real.min*real.epsilon) = log(smallest denormal) -} - -T rationalPoly(T)(T x, const(T) [] numerator, const(T) [] denominator) pure nothrow -{ - return poly(x, numerator)/poly(x, denominator); -} - - -private { - -/* erfc(x) = exp(-x^2) P(1/x)/Q(1/x) - 1/8 <= 1/x <= 1 - Peak relative error 5.8e-21 */ -immutable real[10] P = [ -0x1.30dfa809b3cc6676p-17L, 0x1.38637cd0913c0288p+18L, - 0x1.2f015e047b4476bp+22L, 0x1.24726f46aa9ab08p+25L, 0x1.64b13c6395dc9c26p+27L, - 0x1.294c93046ad55b5p+29L, 0x1.5962a82f92576dap+30L, 0x1.11a709299faba04ap+31L, - 0x1.11028065b087be46p+31L, 0x1.0d8ef40735b097ep+30L -]; - -immutable real[11] Q = [ 0x1.14d8e2a72dec49f4p+19L, 0x1.0c880ff467626e1p+23L, - 0x1.04417ef060b58996p+26L, 0x1.404e61ba86df4ebap+28L, 0x1.0f81887bc82b873ap+30L, - 0x1.4552a5e39fb49322p+31L, 0x1.11779a0ceb2a01cep+32L, 0x1.3544dd691b5b1d5cp+32L, - 0x1.a91781f12251f02ep+31L, 0x1.0d8ef3da605a1c86p+30L, 1.0L -]; - -// For 128 bit quadruple-precision floats, we use a higher-precision implementation -// with more polynomial segments. -enum isIEEEQuadruple = floatTraits!real.realFormat == RealFormat.ieeeQuadruple; -static if (isIEEEQuadruple) -{ - // erfc(x + 0.25) = erfc(0.25) + x R(x) - // 0 <= x < 0.125 - // Peak relative error 1.4e-35 - immutable real[9] RNr13 = [ - -2.353707097641280550282633036456457014829E3L, - 3.871159656228743599994116143079870279866E2L, - -3.888105134258266192210485617504098426679E2L, - -2.129998539120061668038806696199343094971E1L, - -8.125462263594034672468446317145384108734E1L, - 8.151549093983505810118308635926270319660E0L, - -5.033362032729207310462422357772568553670E0L, - -4.253956621135136090295893547735851168471E-2L, - -8.098602878463854789780108161581050357814E-2L - ]; - immutable real[9] RDr13 = [ - 2.220448796306693503549505450626652881752E3L, - 1.899133258779578688791041599040951431383E2L, - 1.061906712284961110196427571557149268454E3L, - 7.497086072306967965180978101974566760042E1L, - 2.146796115662672795876463568170441327274E2L, - 1.120156008362573736664338015952284925592E1L, - 2.211014952075052616409845051695042741074E1L, - 6.469655675326150785692908453094054988938E-1L, - 1.0 - ]; - - // erfc(0.25) = C13a + C13b to extra precision. - immutable real C13a = 0.723663330078125L; - immutable real C13b = 1.0279753638067014931732235184287934646022E-5L; - - // erfc(x + 0.375) = erfc(0.375) + x R(x) - // 0 <= x < 0.125 - // Peak relative error 1.2e-35 - immutable real[9] RNr14 = [ - -2.446164016404426277577283038988918202456E3L, - 6.718753324496563913392217011618096698140E2L, - -4.581631138049836157425391886957389240794E2L, - -2.382844088987092233033215402335026078208E1L, - -7.119237852400600507927038680970936336458E1L, - 1.313609646108420136332418282286454287146E1L, - -6.188608702082264389155862490056401365834E0L, - -2.787116601106678287277373011101132659279E-2L, - -2.230395570574153963203348263549700967918E-2L - ]; - immutable real[9] RDr14 = [ - 2.495187439241869732696223349840963702875E3L, - 2.503549449872925580011284635695738412162E2L, - 1.159033560988895481698051531263861842461E3L, - 9.493751466542304491261487998684383688622E1L, - 2.276214929562354328261422263078480321204E2L, - 1.367697521219069280358984081407807931847E1L, - 2.276988395995528495055594829206582732682E1L, - 7.647745753648996559837591812375456641163E-1L, - 1.0L - ]; - - // erfc(0.375) = C14a + C14b to extra precision. - immutable real C14a = 0.5958709716796875L; - immutable real C14b = 1.2118885490201676174914080878232469565953E-5L; - - // erfc(x + 0.5) = erfc(0.5) + x R(x) - // 0 <= x < 0.125 - // Peak relative error 4.7e-36 - immutable real[9] RNr15 = [ - -2.624212418011181487924855581955853461925E3L, - 8.473828904647825181073831556439301342756E2L, - -5.286207458628380765099405359607331669027E2L, - -3.895781234155315729088407259045269652318E1L, - -6.200857908065163618041240848728398496256E1L, - 1.469324610346924001393137895116129204737E1L, - -6.961356525370658572800674953305625578903E0L, - 5.145724386641163809595512876629030548495E-3L, - 1.990253655948179713415957791776180406812E-2L - ]; - immutable real[9] RDr15 = [ - 2.986190760847974943034021764693341524962E3L, - 5.288262758961073066335410218650047725985E2L, - 1.363649178071006978355113026427856008978E3L, - 1.921707975649915894241864988942255320833E2L, - 2.588651100651029023069013885900085533226E2L, - 2.628752920321455606558942309396855629459E1L, - 2.455649035885114308978333741080991380610E1L, - 1.378826653595128464383127836412100939126E0L, - 1.0L - ]; - // erfc(0.5) = C15a + C15b to extra precision. - immutable real C15a = 0.4794921875L; - immutable real C15b = 7.9346869534623172533461080354712635484242E-6L; - - // erfc(x + 0.625) = erfc(0.625) + x R(x) - // 0 <= x < 0.125 - // Peak relative error 5.1e-36 - immutable real[9] RNr16 = [ - -2.347887943200680563784690094002722906820E3L, - 8.008590660692105004780722726421020136482E2L, - -5.257363310384119728760181252132311447963E2L, - -4.471737717857801230450290232600243795637E1L, - -4.849540386452573306708795324759300320304E1L, - 1.140885264677134679275986782978655952843E1L, - -6.731591085460269447926746876983786152300E0L, - 1.370831653033047440345050025876085121231E-1L, - 2.022958279982138755020825717073966576670E-2L, - ]; - immutable real[9] RDr16 = [ - 3.075166170024837215399323264868308087281E3L, - 8.730468942160798031608053127270430036627E2L, - 1.458472799166340479742581949088453244767E3L, - 3.230423687568019709453130785873540386217E2L, - 2.804009872719893612081109617983169474655E2L, - 4.465334221323222943418085830026979293091E1L, - 2.612723259683205928103787842214809134746E1L, - 2.341526751185244109722204018543276124997E0L, - 1.0L - ]; - // erfc(0.625) = C16a + C16b to extra precision. - immutable real C16a = 0.3767547607421875L; - immutable real C16b = 4.3570693945275513594941232097252997287766E-6L; - - // erfc(x + 0.75) = erfc(0.75) + x R(x) - // 0 <= x < 0.125 - // Peak relative error 1.7e-35 - immutable real[9] RNr17 = [ - -1.767068734220277728233364375724380366826E3L, - 6.693746645665242832426891888805363898707E2L, - -4.746224241837275958126060307406616817753E2L, - -2.274160637728782675145666064841883803196E1L, - -3.541232266140939050094370552538987982637E1L, - 6.988950514747052676394491563585179503865E0L, - -5.807687216836540830881352383529281215100E0L, - 3.631915988567346438830283503729569443642E-1L, - -1.488945487149634820537348176770282391202E-2L - ]; - immutable real[9] RDr17 = [ - 2.748457523498150741964464942246913394647E3L, - 1.020213390713477686776037331757871252652E3L, - 1.388857635935432621972601695296561952738E3L, - 3.903363681143817750895999579637315491087E2L, - 2.784568344378139499217928969529219886578E2L, - 5.555800830216764702779238020065345401144E1L, - 2.646215470959050279430447295801291168941E1L, - 2.984905282103517497081766758550112011265E0L, - 1.0L - ]; - // erfc(0.75) = C17a + C17b to extra precision. - immutable real C17a = 0.2888336181640625L; - immutable real C17b = 1.0748182422368401062165408589222625794046E-5L; - - - // erfc(x + 0.875) = erfc(0.875) + x R(x) - // 0 <= x < 0.125 - // Peak relative error 2.2e-35 - immutable real[9] RNr18 = [ - -1.342044899087593397419622771847219619588E3L, - 6.127221294229172997509252330961641850598E2L, - -4.519821356522291185621206350470820610727E2L, - 1.223275177825128732497510264197915160235E1L, - -2.730789571382971355625020710543532867692E1L, - 4.045181204921538886880171727755445395862E0L, - -4.925146477876592723401384464691452700539E0L, - 5.933878036611279244654299924101068088582E-1L, - -5.557645435858916025452563379795159124753E-2L - ]; - immutable real[9] RDr18 = [ - 2.557518000661700588758505116291983092951E3L, - 1.070171433382888994954602511991940418588E3L, - 1.344842834423493081054489613250688918709E3L, - 4.161144478449381901208660598266288188426E2L, - 2.763670252219855198052378138756906980422E2L, - 5.998153487868943708236273854747564557632E1L, - 2.657695108438628847733050476209037025318E1L, - 3.252140524394421868923289114410336976512E0L, - 1.0L - ]; - - // erfc(0.875) = C18a + C18b to extra precision. - immutable real C18a = 0.215911865234375L; - immutable real C18b = 1.3073705765341685464282101150637224028267E-5L; - - // erfc(x + 1.0) = erfc(1.0) + x R(x) - // 0 <= x < 0.125 - // Peak relative error 1.6e-35 - immutable real[9] RNr19 = [ - -1.139180936454157193495882956565663294826E3L, - 6.134903129086899737514712477207945973616E2L, - -4.628909024715329562325555164720732868263E2L, - 4.165702387210732352564932347500364010833E1L, - -2.286979913515229747204101330405771801610E1L, - 1.870695256449872743066783202326943667722E0L, - -4.177486601273105752879868187237000032364E0L, - 7.533980372789646140112424811291782526263E-1L, - -8.629945436917752003058064731308767664446E-2L - ]; - immutable real[9] RDr19 = [ - 2.744303447981132701432716278363418643778E3L, - 1.266396359526187065222528050591302171471E3L, - 1.466739461422073351497972255511919814273E3L, - 4.868710570759693955597496520298058147162E2L, - 2.993694301559756046478189634131722579643E2L, - 6.868976819510254139741559102693828237440E1L, - 2.801505816247677193480190483913753613630E1L, - 3.604439909194350263552750347742663954481E0L, - 1.0L - ]; - - // erfc(1.0) = C19a + C19b to extra precision. - immutable real C19a = 0.15728759765625L; - immutable real C19b = 1.1609394035130658779364917390740703933002E-5L; - - // erfc(x + 1.125) = erfc(1.125) + x R(x) - // 0 <= x < 0.125 - // Peak relative error 3.6e-36 - immutable real[9] RNr20 = [ - -9.652706916457973956366721379612508047640E2L, - 5.577066396050932776683469951773643880634E2L, - -4.406335508848496713572223098693575485978E2L, - 5.202893466490242733570232680736966655434E1L, - -1.931311847665757913322495948705563937159E1L, - -9.364318268748287664267341457164918090611E-2L, - -3.306390351286352764891355375882586201069E0L, - 7.573806045289044647727613003096916516475E-1L, - -9.611744011489092894027478899545635991213E-2L - ]; - immutable real[9] RDr20 = [ - 3.032829629520142564106649167182428189014E3L, - 1.659648470721967719961167083684972196891E3L, - 1.703545128657284619402511356932569292535E3L, - 6.393465677731598872500200253155257708763E2L, - 3.489131397281030947405287112726059221934E2L, - 8.848641738570783406484348434387611713070E1L, - 3.132269062552392974833215844236160958502E1L, - 4.430131663290563523933419966185230513168E0L, - 1.0L - ]; - - // erfc(1.125) = C20a + C20b to extra precision. - immutable real C20a = 0.111602783203125L; - immutable real C20b = 8.9850951672359304215530728365232161564636E-6L; - - // erfc(x) = exp(-x^2) 1/x R(1/x^2) / S(1/x^2) - // 7/8 <= 1/x < 1 - // Peak relative error 1.4e-35 - immutable real[10] RNr8 = [ - 3.587451489255356250759834295199296936784E1L, - 5.406249749087340431871378009874875889602E2L, - 2.931301290625250886238822286506381194157E3L, - 7.359254185241795584113047248898753470923E3L, - 9.201031849810636104112101947312492532314E3L, - 5.749697096193191467751650366613289284777E3L, - 1.710415234419860825710780802678697889231E3L, - 2.150753982543378580859546706243022719599E2L, - 8.740953582272147335100537849981160931197E0L, - 4.876422978828717219629814794707963640913E-2L - ]; - immutable real[10] RDr8 = [ - 6.358593134096908350929496535931630140282E1L, - 9.900253816552450073757174323424051765523E2L, - 5.642928777856801020545245437089490805186E3L, - 1.524195375199570868195152698617273739609E4L, - 2.113829644500006749947332935305800887345E4L, - 1.526438562626465706267943737310282977138E4L, - 5.561370922149241457131421914140039411782E3L, - 9.394035530179705051609070428036834496942E2L, - 6.147019596150394577984175188032707343615E1L, - 1.0L - ]; - - // erfc(x) = exp(-x^2) 1/x R(1/x^2) / S(1/x^2) - // 3/4 <= 1/x < 7/8 - // Peak relative error 1.7e-36 - immutable real[10] RNr7 = [ - 1.293515364043117601705535485785956592493E2L, - 2.474534371269189867053251150063459055230E3L, - 1.756537563959875738809491329250457486510E4L, - 5.977479535376639763773153344676726091607E4L, - 1.054313816139671870123172936972055385049E5L, - 9.754699773487726957401038094714603033904E4L, - 4.579131242577671038339922925213209214880E4L, - 1.000710322164510887997115157797717324370E4L, - 8.496863250712471449526805271633794700452E2L, - 1.797349831386892396933210199236530557333E1L - ]; - immutable real[11] RDr7 = [ - 2.292696320307033494820399866075534515002E2L, - 4.500632608295626968062258401895610053116E3L, - 3.321218723485498111535866988511716659339E4L, - 1.196084512221845156596781258490840961462E5L, - 2.287033883912529843927983406878910939930E5L, - 2.370223495794642027268482075021298394425E5L, - 1.305173734022437154610938308944995159199E5L, - 3.589386258485887630236490009835928559621E4L, - 4.339996864041074149726360516336773136101E3L, - 1.753135522665469574605384979152863899099E2L, - 1.0L - ]; - - // erfc(x) = exp(-x^2) 1/x R(1/x^2) / S(1/x^2) - // 5/8 <= 1/x < 3/4 - // Peak relative error 1.6e-35 - immutable real[10] RNr6 = [ - 1.423313561367394923305025174137639124533E1L, - 3.244462503273003837514629113846075327206E2L, - 2.784937282403293364911673341412846781934E3L, - 1.163060685810874867196849890286455473382E4L, - 2.554141394931962276102205517358731053756E4L, - 2.982733782500729530503336931258698708782E4L, - 1.789683564523810605328169719436374742840E4L, - 5.056032142227470121262177112822018882754E3L, - 5.605349942234782054561269306895707034586E2L, - 1.561652599080729507274832243665726064881E1L - ]; - immutable real[11] RDr6 = [ - 2.522757606611479946069351519410222913326E1L, - 5.876797910931896554014229647006604017806E2L, - 5.211092128250480712011248211246144751074E3L, - 2.282679910855404599271496827409168580797E4L, - 5.371245819205596609986320599133109262447E4L, - 6.926186102106400355114925675028888924445E4L, - 4.794366033363621432575096487724913414473E4L, - 1.673190682734065914573814938835674963896E4L, - 2.589544846151313120096957014256536236242E3L, - 1.349438432583208276883323156200117027433E2L, - 1.0L - ]; - - // erfc(x) = exp(-x^2) 1/x R(1/x^2) / S(1/x^2) - // 1/2 <= 1/x < 5/8 - // Peak relative error 4.3e-36 - immutable real[11] RNr5 = [ - 6.743447478279267339131211137241149796763E-2L, - 2.031339015932962998168472743355874796350E0L, - 2.369234815713196480221800940201367484379E1L, - 1.387819614016107433603101545594790875922E2L, - 4.435600256936515839937720907171966121786E2L, - 7.881577949936817507981170892417739733047E2L, - 7.615749099291545976179905281851765734680E2L, - 3.752484528663442467089606663006771157777E2L, - 8.279644286027145214308303292537009564726E1L, - 6.201462983413738162709722770960040042647E0L, - 6.649631608720062333043506249503378282697E-2L - ]; - immutable real[11] RDr5 = [ - 1.195244945161889822018178270706903972343E-1L, - 3.660216908153253021384862427197665991311E0L, - 4.373405883243078019655721779021995159854E1L, - 2.653305963056235008916733402786877121865E2L, - 8.921329790491152046318422124415895506335E2L, - 1.705552231555600759729260640562363304312E3L, - 1.832711109606893446763174603477244625325E3L, - 1.056823953275835649973998168744261083316E3L, - 2.975561981792909722126456997074344895584E2L, - 3.393149095158232521894537008472203487436E1L, - 1.0L - ]; - - // erfc(x) = exp(-x^2) 1/x R(1/x^2) / S(1/x^2) - // 3/8 <= 1/x < 1/2 - // Peak relative error 1.8e-36 - immutable real[11] RNr4 = [ - 3.558685919236420073872459554885612994007E-2L, - 1.460223642496950651561817195253277924528E0L, - 2.379856746555189546876720308841066577268E1L, - 2.005205521246554860334064698817220160117E2L, - 9.533374928664989955629120027419490517596E2L, - 2.623024576994438336130421711314560425373E3L, - 4.126446434603735586340585027628851620886E3L, - 3.540675861596687801829655387867654030013E3L, - 1.506037084891064572653273761987617394259E3L, - 2.630715699182706745867272452228891752353E2L, - 1.202476629652900619635409242749750364878E1L - ]; - immutable real[12] RDr4 = [ - 6.307606561714590590399683184410336583739E-2L, - 2.619717051134271249293056836082721776665E0L, - 4.344441402681725017630451522968410844608E1L, - 3.752891116408399440953195184301023399176E2L, - 1.849305988804654653921972804388006355502E3L, - 5.358505261991675891835885654499883449403E3L, - 9.091890995405251314631428721090705475825E3L, - 8.731418313949291797856351745278287516416E3L, - 4.420211285043270337492325400764271868740E3L, - 1.031487363021856106882306509107923200832E3L, - 8.387036084846046121805145056040429461783E1L, - 1.0L - ]; - - // erfc(x) = exp(-x^2) 1/x R(1/x^2) / S(1/x^2) - // 1/4 <= 1/x < 3/8 - // Peak relative error 8.1e-37 - immutable real[12] RNr3 = [ - 4.584481542956275354582319313040418316755E-5L, - 2.674923158288848442110883948437930349128E-3L, - 6.344232532055212248017211243740466847311E-2L, - 7.985145965992002744933550450451513513963E-1L, - 5.845078061888281665064746347663123946270E0L, - 2.566625318816866587482759497608029522596E1L, - 6.736225182343446605268837827950856640948E1L, - 1.021796460139598089409347761712730512053E2L, - 8.344336615515430530929955615400706619764E1L, - 3.207749011528249356283897356277376306967E1L, - 4.386185123863412086856423971695142026036E0L, - 8.971576448581208351826868348023528863856E-2L - ]; - immutable real[12] RDr3 = [ - 8.125781965218112303281657065320409661370E-5L, - 4.781806762611504685247817818428945295520E-3L, - 1.147785538413798317790357996845767614561E-1L, - 1.469285552007088106614218996464752307606E0L, - 1.101712261349880339221039938999124077650E1L, - 5.008507527095093413713171655268276861071E1L, - 1.383058691613468970486425146336829447433E2L, - 2.264114250278912520501010108736339599752E2L, - 2.081377197698598680576330179979996940039E2L, - 9.724438129690013609440151781601781137944E1L, - 1.907905050771832372089975877589291760121E1L, - 1.0L - ]; - - // erfc(x) = exp(-x^2) 1/x R(1/x^2) / S(1/x^2) - // 1/8 <= 1/x < 1/4 - // Peak relative error 1.5e-36 - immutable real[12] RNr2 = [ - 6.928615158005256885698045840589513728399E-7L, - 5.616245938942075826026382337922413007879E-5L, - 1.871624980715261794832358438894219696113E-3L, - 3.349922063795792371642023765253747563009E-2L, - 3.531865233974349943956345502463135695834E-1L, - 2.264714157625072773976468825160906342360E0L, - 8.810720294489253776747319730638214883026E0L, - 2.014056685571655833019183248931442888437E1L, - 2.524586947657190747039554310814128743320E1L, - 1.520656940937208886246188940244581671609E1L, - 3.334145500790963675035841482334493680498E0L, - 1.122108380007109245896534245151140632457E-1L - ]; - immutable real[12] RDr2 = [ - 1.228065061824874795984937092427781089256E-6L, - 1.001593999520159167559129042893802235969E-4L, - 3.366527555699367241421450749821030974446E-3L, - 6.098626947195865254152265585991861150369E-2L, - 6.541547922508613985813189387198804660235E-1L, - 4.301130233305371976727117480925676583204E0L, - 1.737155892350891711527711121692994762909E1L, - 4.206892112110558214680649401236873828801E1L, - 5.787487996025016843403524261574779631219E1L, - 4.094047148590822715163181507813774861621E1L, - 1.230603930056944875836549716515643997094E1L, - 1.0L - ]; - - // erfc(x) = exp(-x^2) 1/x R(1/x^2) / S(1/x^2) - // 1/128 <= 1/x < 1/8 - // Peak relative error 2.2e-36 - immutable real[10] RNr1 = [ - 1.293111801138199795250035229383033322539E-6L, - 9.785224720880980456171438759161161816706E-5L, - 2.932474396233212166056331430621176065943E-3L, - 4.496429595719847083917435337780697436921E-2L, - 3.805989855927720844877478869846718877846E-1L, - 1.789745532222426292126781724570152590071E0L, - 4.465737379634389318903237306594171764628E0L, - 5.268535822258082278401240171488850433767E0L, - 2.258357766807433839494276681092713991651E0L, - 1.504459334078750002966538036652860809497E-1L - ]; - immutable real[10] RDr1 = [ - 2.291980991578770070179177302906728371406E-6L, - 1.745845828808028552029674694534934620384E-4L, - 5.283248841982102317072923869576785278019E-3L, - 8.221212297078141470944454807434634848018E-2L, - 7.120500674861902950423510939060230945621E-1L, - 3.475435367560809622183983439133664598155E0L, - 9.243253391989233533874386043611304387113E0L, - 1.227894792475280941511758877318903197188E1L, - 6.789361410398841316638617624392719077724E0L, - 1.0L - ]; - - // erf(z+1) = erfConst + P(z)/Q(z) - // -.125 <= z <= 0 - // Peak relative error 7.3e-36 - immutable real erfConst = 0.845062911510467529296875L; - immutable real[9] TN2 = [ - -4.088889697077485301010486931817357000235E1L, - 7.157046430681808553842307502826960051036E3L, - -2.191561912574409865550015485451373731780E3L, - 2.180174916555316874988981177654057337219E3L, - 2.848578658049670668231333682379720943455E2L, - 1.630362490952512836762810462174798925274E2L, - 6.317712353961866974143739396865293596895E0L, - 2.450441034183492434655586496522857578066E1L, - 5.127662277706787664956025545897050896203E-1L - ]; - immutable real[10] TD2 = [ - 1.731026445926834008273768924015161048885E4L, - 1.209682239007990370796112604286048173750E4L, - 1.160950290217993641320602282462976163857E4L, - 5.394294645127126577825507169061355698157E3L, - 2.791239340533632669442158497532521776093E3L, - 8.989365571337319032943005387378993827684E2L, - 2.974016493766349409725385710897298069677E2L, - 6.148192754590376378740261072533527271947E1L, - 1.178502892490738445655468927408440847480E1L, - 1.0L - ]; - - // erf(x) = x + x P(x^2)/Q(x^2) - // 0 <= x <= 7/8 - // Peak relative error 1.8e-35 - immutable real[9] TN1 = [ - -3.858252324254637124543172907442106422373E10L, - 9.580319248590464682316366876952214879858E10L, - 1.302170519734879977595901236693040544854E10L, - 2.922956950426397417800321486727032845006E9L, - 1.764317520783319397868923218385468729799E8L, - 1.573436014601118630105796794840834145120E7L, - 4.028077380105721388745632295157816229289E5L, - 1.644056806467289066852135096352853491530E4L, - 3.390868480059991640235675479463287886081E1L - ]; - immutable real[10] TD1 = [ - -3.005357030696532927149885530689529032152E11L, - -1.342602283126282827411658673839982164042E11L, - -2.777153893355340961288511024443668743399E10L, - -3.483826391033531996955620074072768276974E9L, - -2.906321047071299585682722511260895227921E8L, - -1.653347985722154162439387878512427542691E7L, - -6.245520581562848778466500301865173123136E5L, - -1.402124304177498828590239373389110545142E4L, - -1.209368072473510674493129989468348633579E2L, - 1.0L - ]; -} -else -{ - /* erfc(x) = exp(-x^2) 1/x R(1/x^2) / S(1/x^2) - 1/128 <= 1/x < 1/8 - Peak relative error 1.9e-21 */ - immutable real[5] R = [ 0x1.b9f6d8b78e22459ep-6L, 0x1.1b84686b0a4ea43ap-1L, - 0x1.b8f6aebe96000c2ap+1L, 0x1.cb1dbedac27c8ec2p+2L, 0x1.cf885f8f572a4c14p+1L - ]; - - immutable real[6] S = [ - 0x1.87ae3cae5f65eb5ep-5L, 0x1.01616f266f306d08p+0L, 0x1.a4abe0411eed6c22p+2L, - 0x1.eac9ce3da600abaap+3L, 0x1.5752a9ac2faebbccp+3L, 1.0L - ]; - - /* erf(x) = x P(x^2)/Q(x^2) - 0 <= x <= 1 - Peak relative error 7.6e-23 */ - immutable real[7] T = [ 0x1.0da01654d757888cp+20L, 0x1.2eb7497bc8b4f4acp+17L, - 0x1.79078c19530f72a8p+15L, 0x1.4eaf2126c0b2c23p+11L, 0x1.1f2ea81c9d272a2ep+8L, - 0x1.59ca6e2d866e625p+2L, 0x1.c188e0b67435faf4p-4L - ]; - - immutable real[7] U = [ 0x1.dde6025c395ae34ep+19L, 0x1.c4bc8b6235df35aap+18L, - 0x1.8465900e88b6903ap+16L, 0x1.855877093959ffdp+13L, 0x1.e5c44395625ee358p+9L, - 0x1.6a0fed103f1c68a6p+5L, 1.0L - ]; -} -} - -/** - * Complementary error function - * - * erfc(x) = 1 - erf(x), and has high relative accuracy for - * values of x far from zero. (For values near zero, use erf(x)). - * - * 1 - erf(x) = 2/ $(SQRT)(π) - * $(INTEGRAL x, $(INFINITY)) exp( - $(POWER t, 2)) dt - * - * - * For small x, erfc(x) = 1 - erf(x); otherwise rational - * approximations are computed. - * - * A special function expx2(x) is used to suppress error amplification - * in computing exp(-x^2). - */ -real erfc(real a) -{ - if (a == real.infinity) - return 0.0; - if (a == -real.infinity) - return 2.0; - - immutable x = (a < 0.0) ? -a : a; - - if (x < (isIEEEQuadruple ? 0.25 : 1.0)) - return 1.0 - erf(a); - - static if (isIEEEQuadruple) - { - if (x < 1.25) - { - real y; - final switch (cast(int)(8.0 * x)) - { - case 2: - const z = x - 0.25; - y = C13b + z * rationalPoly(z, RNr13, RDr13); - y += C13a; - break; - case 3: - const z = x - 0.375; - y = C14b + z * rationalPoly(z, RNr14, RDr14); - y += C14a; - break; - case 4: - const z = x - 0.5; - y = C15b + z * rationalPoly(z, RNr15, RDr15); - y += C15a; - break; - case 5: - const z = x - 0.625; - y = C16b + z * rationalPoly(z, RNr16, RDr16); - y += C16a; - break; - case 6: - const z = x - 0.75; - y = C17b + z * rationalPoly(z, RNr17, RDr17); - y += C17a; - break; - case 7: - const z = x - 0.875; - y = C18b + z * rationalPoly(z, RNr18, RDr18); - y += C18a; - break; - case 8: - const z = x - 1.0; - y = C19b + z * rationalPoly(z, RNr19, RDr19); - y += C19a; - break; - case 9: - const z = x - 1.125; - y = C20b + z * rationalPoly(z, RNr20, RDr20); - y += C20a; - break; - } - if (a < 0.0) - y = 2.0 - y; - return y; - } - } - - if (-a * a < -MAXLOG) - { - // underflow - if (a < 0.0) return 2.0; - else return 0.0; - } - - real y; - immutable z = expx2(a, -1); - - static if (isIEEEQuadruple) - { - y = z * erfce(x); - } - else - { - y = 1.0 / x; - if (x < 8.0) - y = z * rationalPoly(y, P, Q); - else - y = z * y * rationalPoly(y * y, R, S); - } - - if (a < 0.0) - y = 2.0 - y; - - if (y == 0.0) - { - // underflow - if (a < 0.0) return 2.0; - else return 0.0; - } - - return y; -} - - -private { -/* Exponentially scaled erfc function - exp(x^2) erfc(x) - valid for x > 1. - Use with normalDistribution and expx2. */ -static if (isIEEEQuadruple) -{ - real erfce(real x) - { - immutable z = 1.0L / (x * x); - - real p; - switch (cast(int)(8.0 / x)) - { - default: - case 0: - p = rationalPoly(z, RNr1, RDr1); - break; - case 1: - p = rationalPoly(z, RNr2, RDr2); - break; - case 2: - p = rationalPoly(z, RNr3, RDr3); - break; - case 3: - p = rationalPoly(z, RNr4, RDr4); - break; - case 4: - p = rationalPoly(z, RNr5, RDr5); - break; - case 5: - p = rationalPoly(z, RNr6, RDr6); - break; - case 6: - p = rationalPoly(z, RNr7, RDr7); - break; - case 7: - p = rationalPoly(z, RNr8, RDr8); - break; - } - return p / x; - } -} -else -{ - real erfce(real x) - { - real y = 1.0/x; - - if (x < 8.0) - { - return rationalPoly(y, P, Q); - } - else - { - return y * rationalPoly(y * y, R, S); - } - } -} -} - -/** - * Error function - * - * The integral is - * - * erf(x) = 2/ $(SQRT)(π) - * $(INTEGRAL 0, x) exp( - $(POWER t, 2)) dt - * - * The magnitude of x is limited to about 106.56 for IEEE 80-bit - * arithmetic; 1 or -1 is returned outside this range. - * - * For 0 <= |x| < 1, a rational polynomials are used; otherwise - * erf(x) = 1 - erfc(x). - * - * ACCURACY: - * Relative error: - * arithmetic domain # trials peak rms - * IEEE 0,1 50000 2.0e-19 5.7e-20 - */ -real erf(real x) -{ - if (x == 0.0) - return x; // deal with negative zero - if (x == -real.infinity) - return -1.0; - if (x == real.infinity) - return 1.0; - immutable ax = fabs(x); - if (ax > 1.0L) - return 1.0L - erfc(x); - - static if (isIEEEQuadruple) - { - immutable z = x * x; - - real y; - if (ax < 0.875) - { - y = ax + ax * rationalPoly(x * x, TN1, TD1); - } - else - { - y = erfConst + rationalPoly(ax - 1.0L, TN2, TD2); - } - - if (x < 0) - y = -y; - return y; - } - else - { - real z = x * x; - return x * rationalPoly(x * x, T, U); - } -} - -@safe unittest -{ - // High resolution test points. - enum real erfc0_250 = 0.723663330078125L + 1.0279753638067014931732235184287934646022E-5L; - enum real erfc0_375 = 0.5958709716796875L + 1.2118885490201676174914080878232469565953E-5L; - enum real erfc0_500 = 0.4794921875L + 7.9346869534623172533461080354712635484242E-6L; - enum real erfc0_625 = 0.3767547607421875L + 4.3570693945275513594941232097252997287766E-6L; - enum real erfc0_750 = 0.2888336181640625L + 1.0748182422368401062165408589222625794046E-5L; - enum real erfc0_875 = 0.215911865234375L + 1.3073705765341685464282101150637224028267E-5L; - enum real erfc1_000 = 0.15728759765625L + 1.1609394035130658779364917390740703933002E-5L; - enum real erfc1_125 = 0.111602783203125L + 8.9850951672359304215530728365232161564636E-6L; - - enum real erf0_875 = (1-0.215911865234375L) - 1.3073705765341685464282101150637224028267E-5L; - - static bool isNaNWithPayload(real x, ulong payload) @safe pure nothrow @nogc - { - return isNaN(x) && getNaNPayload(x) == payload; - } - - assert(feqrel(erfc(0.250L), erfc0_250 )>=real.mant_dig-1); - assert(feqrel(erfc(0.375L), erfc0_375 )>=real.mant_dig-0); - assert(feqrel(erfc(0.500L), erfc0_500 )>=real.mant_dig-2); - assert(feqrel(erfc(0.625L), erfc0_625 )>=real.mant_dig-1); - assert(feqrel(erfc(0.750L), erfc0_750 )>=real.mant_dig-1); - assert(feqrel(erfc(0.875L), erfc0_875 )>=real.mant_dig-4); - assert(feqrel(erfc(1.000L), erfc1_000 )>=real.mant_dig-2); - assert(feqrel(erfc(1.125L), erfc1_125 )>=real.mant_dig-2); - assert(feqrel(erf(0.875L), erf0_875 )>=real.mant_dig-1); - // The DMC implementation of erfc() fails this next test (just). - // Test point from Mathematica 11.0. - assert(feqrel(erfc(4.1L), 6.70002765408489837272673380763418472e-9L) >= real.mant_dig-5); - - assert(isIdentical(erf(0.0),0.0)); - assert(isIdentical(erf(-0.0),-0.0)); - assert(erf(real.infinity) == 1.0); - assert(erf(-real.infinity) == -1.0); - assert(isNaNWithPayload(erf(NaN(0xDEF)), 0xDEF)); - assert(isNaNWithPayload(erfc(NaN(0xDEF)), 0xDEF)); - assert(isIdentical(erfc(real.infinity),0.0)); - assert(erfc(-real.infinity) == 2.0); - assert(erfc(0) == 1.0); -} - -/* - * Exponential of squared argument - * - * Computes y = exp(x*x) while suppressing error amplification - * that would ordinarily arise from the inexactness of the - * exponential argument x*x. - * - * If sign < 0, the result is inverted; i.e., y = exp(-x*x) . - * - * ACCURACY: - * Relative error: - * arithmetic domain # trials peak rms - * IEEE -106.566, 106.566 10^5 1.6e-19 4.4e-20 - */ - -real expx2(real x, int sign) -{ - /* - Cephes Math Library Release 2.9: June, 2000 - Copyright 2000 by Stephen L. Moshier - */ - const real M = 32_768.0; - const real MINV = 3.0517578125e-5L; - - x = fabs(x); - if (sign < 0) - x = -x; - - /* Represent x as an exact multiple of M plus a residual. - M is a power of 2 chosen so that exp(m * m) does not overflow - or underflow and so that |x - m| is small. */ - real m = MINV * floor(M * x + 0.5L); - real f = x - m; - - /* x^2 = m^2 + 2mf + f^2 */ - real u = m * m; - real u1 = 2 * m * f + f * f; - - if (sign < 0) - { - u = -u; - u1 = -u1; - } - - if ((u+u1) > MAXLOG) - return real.infinity; - - /* u is exact, u1 is small. */ - return exp(u) * exp(u1); -} - - -/* -Computes the normal distribution function. - -The normal (or Gaussian, or bell-shaped) distribution is -defined as: - -normalDist(x) = 1/$(SQRT) π $(INTEGRAL -$(INFINITY), x) exp( - $(POWER t, 2)/2) dt - = 0.5 + 0.5 * erf(x/sqrt(2)) - = 0.5 * erfc(- x/sqrt(2)) - -To maintain accuracy at high values of x, use -normalDistribution(x) = 1 - normalDistribution(-x). - -Accuracy: -Within a few bits of machine resolution over the entire -range. - -References: -$(LINK http://www.netlib.org/cephes/ldoubdoc.html), -G. Marsaglia, "Evaluating the Normal Distribution", -Journal of Statistical Software 11, (July 2004). -*/ -real normalDistributionImpl(real a) -{ - real x = a * SQRT1_2; - real z = fabs(x); - - if ( z < 1.0 ) - return 0.5L + 0.5L * erf(x); - else - { - real y = 0.5L * erfce(z); - /* Multiply by exp(-x^2 / 2) */ - z = expx2(a, -1); - y = y * sqrt(z); - if ( x > 0.0L ) - y = 1.0L - y; - return y; - } -} - -version (LDC) version (X86) version (CRuntime_Microsoft) version = LDC_MSVC_X86; - -@safe unittest -{ -assert(fabs(normalDistributionImpl(1L) - (0.841344746068543L)) < 0.0000000000000005L); -version (LDC_MSVC_X86) -{ - // NaN propagation has regressed for 32-bit MSVC with LLVM 11 and enabled optimizations - assert(isNaN(normalDistributionImpl(NaN(0x325)))); -} -else - assert(isIdentical(normalDistributionImpl(NaN(0x325)), NaN(0x325))); -} - -/* - * Inverse of Normal distribution function - * - * Returns the argument, x, for which the area under the - * Normal probability density function (integrated from - * minus infinity to x) is equal to p. - * - * For small arguments 0 < p < exp(-2), the program computes - * z = sqrt( -2 log(p) ); then the approximation is - * x = z - log(z)/z - (1/z) P(1/z) / Q(1/z) . - * For larger arguments, x/sqrt(2 pi) = w + w^3 R(w^2)/S(w^2)) , - * where w = p - 0.5. - */ -// TODO: isIEEEQuadruple (128 bit) real implementation; not available from CEPHES. -real normalDistributionInvImpl(real p) -in { - assert(p >= 0.0L && p <= 1.0L, "Domain error"); -} -do -{ -static immutable real[8] P0 = -[ -0x1.758f4d969484bfdcp-7L, 0x1.53cee17a59259dd2p-3L, - -0x1.ea01e4400a9427a2p-1L, 0x1.61f7504a0105341ap+1L, -0x1.09475a594d0399f6p+2L, - 0x1.7c59e7a0df99e3e2p+1L, -0x1.87a81da52edcdf14p-1L, 0x1.1fb149fd3f83600cp-7L -]; - -static immutable real[8] Q0 = -[ -0x1.64b92ae791e64bb2p-7L, 0x1.7585c7d597298286p-3L, - -0x1.40011be4f7591ce6p+0L, 0x1.1fc067d8430a425ep+2L, -0x1.21008ffb1e7ccdf2p+3L, - 0x1.3d1581cf9bc12fccp+3L, -0x1.53723a89fd8f083cp+2L, 1.0L -]; - -static immutable real[10] P1 = -[ 0x1.20ceea49ea142f12p-13L, 0x1.cbe8a7267aea80bp-7L, - 0x1.79fea765aa787c48p-2L, 0x1.d1f59faa1f4c4864p+1L, 0x1.1c22e426a013bb96p+4L, - 0x1.a8675a0c51ef3202p+5L, 0x1.75782c4f83614164p+6L, 0x1.7a2f3d90948f1666p+6L, - 0x1.5cd116ee4c088c3ap+5L, 0x1.1361e3eb6e3cc20ap+2L -]; - -static immutable real[10] Q1 = -[ 0x1.3a4ce1406cea98fap-13L, 0x1.f45332623335cda2p-7L, - 0x1.98f28bbd4b98db1p-2L, 0x1.ec3b24f9c698091cp+1L, 0x1.1cc56ecda7cf58e4p+4L, - 0x1.92c6f7376bf8c058p+5L, 0x1.4154c25aa47519b4p+6L, 0x1.1b321d3b927849eap+6L, - 0x1.403a5f5a4ce7b202p+4L, 1.0L -]; - -static immutable real[8] P2 = -[ 0x1.8c124a850116a6d8p-21L, 0x1.534abda3c2fb90bap-13L, - 0x1.29a055ec93a4718cp-7L, 0x1.6468e98aad6dd474p-3L, 0x1.3dab2ef4c67a601cp+0L, - 0x1.e1fb3a1e70c67464p+1L, 0x1.b6cce8035ff57b02p+2L, 0x1.9f4c9e749ff35f62p+1L -]; - -static immutable real[8] Q2 = -[ 0x1.af03f4fc0655e006p-21L, 0x1.713192048d11fb2p-13L, - 0x1.4357e5bbf5fef536p-7L, 0x1.7fdac8749985d43cp-3L, 0x1.4a080c813a2d8e84p+0L, - 0x1.c3a4b423cdb41bdap+1L, 0x1.8160694e24b5557ap+2L, 1.0L -]; - -static immutable real[8] P3 = -[ -0x1.55da447ae3806168p-34L, -0x1.145635641f8778a6p-24L, - -0x1.abf46d6b48040128p-17L, -0x1.7da550945da790fcp-11L, -0x1.aa0b2a31157775fap-8L, - 0x1.b11d97522eed26bcp-3L, 0x1.1106d22f9ae89238p+1L, 0x1.029a358e1e630f64p+1L -]; - -static immutable real[8] Q3 = -[ -0x1.74022dd5523e6f84p-34L, -0x1.2cb60d61e29ee836p-24L, - -0x1.d19e6ec03a85e556p-17L, -0x1.9ea2a7b4422f6502p-11L, -0x1.c54b1e852f107162p-8L, - 0x1.e05268dd3c07989ep-3L, 0x1.239c6aff14afbf82p+1L, 1.0L -]; - - if (p <= 0.0L || p >= 1.0L) - { - if (p == 0.0L) - return -real.infinity; - if ( p == 1.0L ) - return real.infinity; - return real.nan; // domain error - } - int code = 1; - real y = p; - if ( y > (1.0L - EXP_2) ) - { - y = 1.0L - y; - code = 0; - } - - real x, z, y2, x0, x1; - - if ( y > EXP_2 ) - { - y = y - 0.5L; - y2 = y * y; - x = y + y * (y2 * rationalPoly( y2, P0, Q0)); - return x * SQRT2PI; - } - - x = sqrt( -2.0L * log(y) ); - x0 = x - log(x)/x; - z = 1.0L/x; - if ( x < 8.0L ) - { - x1 = z * rationalPoly( z, P1, Q1); - } - else if ( x < 32.0L ) - { - x1 = z * rationalPoly( z, P2, Q2); - } - else - { - x1 = z * rationalPoly( z, P3, Q3); - } - x = x0 - x1; - if ( code != 0 ) - { - x = -x; - } - return x; -} - - -@safe unittest -{ - // TODO: Use verified test points. - // The values below are from Excel 2003. - assert(fabs(normalDistributionInvImpl(0.001) - (-3.09023230616779L)) < 0.00000000000005L); - assert(fabs(normalDistributionInvImpl(1e-50) - (-14.9333375347885L)) < 0.00000000000005L); - assert(feqrel(normalDistributionInvImpl(0.999L), -normalDistributionInvImpl(0.001L)) > real.mant_dig-6); - - // Excel 2003 gets all the following values wrong! - assert(normalDistributionInvImpl(0.0) == -real.infinity); - assert(normalDistributionInvImpl(1.0) == real.infinity); - assert(normalDistributionInvImpl(0.5) == 0); - // (Excel 2003 returns norminv(p) = -30 for all p < 1e-200). - // The value tested here is the one the function returned in Jan 2006. - real unknown1 = normalDistributionInvImpl(1e-250L); - assert( fabs(unknown1 -(-33.79958617269L) ) < 0.00000005L); -} diff --git a/phobos/std/internal/math/gammafunction.d b/phobos/std/internal/math/gammafunction.d deleted file mode 100644 index 703ecb1..0000000 --- a/phobos/std/internal/math/gammafunction.d +++ /dev/null @@ -1,1848 +0,0 @@ -/** - * Implementation of the gamma and beta functions, and their integrals. - * - * License: $(HTTP boost.org/LICENSE_1_0.txt, Boost License 1.0). - * Copyright: Based on the CEPHES math library, which is - * Copyright (C) 1994 Stephen L. Moshier (moshier@world.std.com). - * Authors: Stephen L. Moshier (original C code). Conversion to D by Don Clugston - * - * -Macros: - * TABLE_SV = - * - * $0
Special Values
- * SVH = $(TR $(TH $1) $(TH $2)) - * SV = $(TR $(TD $1) $(TD $2)) - * GAMMA = Γ - * INTEGRATE = $(BIG ∫$(SMALL $1)$2) - * POWER = $1$2 - * NAN = $(RED NAN) - */ -module std.internal.math.gammafunction; -import std.internal.math.errorfunction; -import std.math; -import core.math : fabs, sin, sqrt; - -pure: -nothrow: -@safe: -@nogc: - -private { - -enum real SQRT2PI = 2.50662827463100050242E0L; // sqrt(2pi) -immutable real EULERGAMMA = 0.57721_56649_01532_86060_65120_90082_40243_10421_59335_93992L; /** Euler-Mascheroni constant 0.57721566.. */ - -// Polynomial approximations for gamma and loggamma. - -immutable real[8] GammaNumeratorCoeffs = [ 1.0L, - 0x1.acf42d903366539ep-1L, 0x1.73a991c8475f1aeap-2L, 0x1.c7e918751d6b2a92p-4L, - 0x1.86d162cca32cfe86p-6L, 0x1.0c378e2e6eaf7cd8p-8L, 0x1.dc5c66b7d05feb54p-12L, - 0x1.616457b47e448694p-15L -]; - -immutable real[9] GammaDenominatorCoeffs = [ 1.0L, - 0x1.a8f9faae5d8fc8bp-2L, -0x1.cb7895a6756eebdep-3L, -0x1.7b9bab006d30652ap-5L, - 0x1.c671af78f312082ep-6L, -0x1.a11ebbfaf96252dcp-11L, -0x1.447b4d2230a77ddap-10L, - 0x1.ec1d45bb85e06696p-13L,-0x1.d4ce24d05bd0a8e6p-17L -]; - -immutable real[9] GammaSmallCoeffs = [ 1.0L, - 0x1.2788cfc6fb618f52p-1L, -0x1.4fcf4026afa2f7ecp-1L, -0x1.5815e8fa24d7e306p-5L, - 0x1.5512320aea2ad71ap-3L, -0x1.59af0fb9d82e216p-5L, -0x1.3b4b61d3bfdf244ap-7L, - 0x1.d9358e9d9d69fd34p-8L, -0x1.38fc4bcbada775d6p-10L -]; - -immutable real[9] GammaSmallNegCoeffs = [ -1.0L, - 0x1.2788cfc6fb618f54p-1L, 0x1.4fcf4026afa2bc4cp-1L, -0x1.5815e8fa2468fec8p-5L, - -0x1.5512320baedaf4b6p-3L, -0x1.59af0fa283baf07ep-5L, 0x1.3b4a70de31e05942p-7L, - 0x1.d9398be3bad13136p-8L, 0x1.291b73ee05bcbba2p-10L -]; - -immutable real[7] logGammaStirlingCoeffs = [ - 0x1.5555555555553f98p-4L, -0x1.6c16c16c07509b1p-9L, 0x1.a01a012461cbf1e4p-11L, - -0x1.3813089d3f9d164p-11L, 0x1.b911a92555a277b8p-11L, -0x1.ed0a7b4206087b22p-10L, - 0x1.402523859811b308p-8L -]; - -immutable real[7] logGammaNumerator = [ - -0x1.0edd25913aaa40a2p+23L, -0x1.31c6ce2e58842d1ep+24L, -0x1.f015814039477c3p+23L, - -0x1.74ffe40c4b184b34p+22L, -0x1.0d9c6d08f9eab55p+20L, -0x1.54c6b71935f1fc88p+16L, - -0x1.0e761b42932b2aaep+11L -]; - -immutable real[8] logGammaDenominator = [ - -0x1.4055572d75d08c56p+24L, -0x1.deeb6013998e4d76p+24L, -0x1.106f7cded5dcc79ep+24L, - -0x1.25e17184848c66d2p+22L, -0x1.301303b99a614a0ap+19L, -0x1.09e76ab41ae965p+15L, - -0x1.00f95ced9e5f54eep+9L, 1.0L -]; - -/* - * Helper function: Gamma function computed by Stirling's formula. - * - * Stirling's formula for the gamma function is: - * - * $(GAMMA)(x) = sqrt(2 π) xx-0.5 exp(-x) (1 + 1/x P(1/x)) - * - */ -real gammaStirling(real x) -{ - // CEPHES code Copyright 1994 by Stephen L. Moshier - - static immutable real[9] SmallStirlingCoeffs = [ - 0x1.55555555555543aap-4L, 0x1.c71c71c720dd8792p-9L, -0x1.5f7268f0b5907438p-9L, - -0x1.e13cd410e0477de6p-13L, 0x1.9b0f31643442616ep-11L, 0x1.2527623a3472ae08p-14L, - -0x1.37f6bc8ef8b374dep-11L,-0x1.8c968886052b872ap-16L, 0x1.76baa9c6d3eeddbcp-11L - ]; - - static immutable real[7] LargeStirlingCoeffs = [ 1.0L, - 8.33333333333333333333E-2L, 3.47222222222222222222E-3L, - -2.68132716049382716049E-3L, -2.29472093621399176955E-4L, - 7.84039221720066627474E-4L, 6.97281375836585777429E-5L - ]; - - real w = 1.0L/x; - real y = exp(x); - if ( x > 1024.0L ) - { - // For large x, use rational coefficients from the analytical expansion. - w = poly(w, LargeStirlingCoeffs); - // Avoid overflow in pow() - real v = pow( x, 0.5L * x - 0.25L ); - y = v * (v / y); - } - else - { - w = 1.0L + w * poly( w, SmallStirlingCoeffs); - static if (floatTraits!(real).realFormat == RealFormat.ieeeDouble) - { - // Avoid overflow in pow() for 64-bit reals - if (x > 143.0) - { - real v = pow( x, 0.5 * x - 0.25 ); - y = v * (v / y); - } - else - { - y = pow( x, x - 0.5 ) / y; - } - } - else - { - y = pow( x, x - 0.5L ) / y; - } - } - y = SQRT2PI * y * w; - return y; -} - -/* - * Helper function: Incomplete gamma function computed by Temme's expansion. - * - * This is a port of igamma_temme_large from Boost. - * - */ -real igammaTemmeLarge(real a, real x) -{ - static immutable real[][13] coef = [ - [ -0.333333333333333333333L, 0.0833333333333333333333L, - -0.0148148148148148148148L, 0.00115740740740740740741L, - 0.000352733686067019400353L, -0.0001787551440329218107L, - 0.39192631785224377817e-4L, -0.218544851067999216147e-5L, - -0.18540622107151599607e-5L, 0.829671134095308600502e-6L, - -0.176659527368260793044e-6L, 0.670785354340149858037e-8L, - 0.102618097842403080426e-7L, -0.438203601845335318655e-8L, - 0.914769958223679023418e-9L, -0.255141939949462497669e-10L, - -0.583077213255042506746e-10L, 0.243619480206674162437e-10L, - -0.502766928011417558909e-11L ], - [ -0.00185185185185185185185L, -0.00347222222222222222222L, - 0.00264550264550264550265L, -0.000990226337448559670782L, - 0.000205761316872427983539L, -0.40187757201646090535e-6L, - -0.18098550334489977837e-4L, 0.764916091608111008464e-5L, - -0.161209008945634460038e-5L, 0.464712780280743434226e-8L, - 0.137863344691572095931e-6L, -0.575254560351770496402e-7L, - 0.119516285997781473243e-7L, -0.175432417197476476238e-10L, - -0.100915437106004126275e-8L, 0.416279299184258263623e-9L, - -0.856390702649298063807e-10L ], - [ 0.00413359788359788359788L, -0.00268132716049382716049L, - 0.000771604938271604938272L, 0.200938786008230452675e-5L, - -0.000107366532263651605215L, 0.529234488291201254164e-4L, - -0.127606351886187277134e-4L, 0.342357873409613807419e-7L, - 0.137219573090629332056e-5L, -0.629899213838005502291e-6L, - 0.142806142060642417916e-6L, -0.204770984219908660149e-9L, - -0.140925299108675210533e-7L, 0.622897408492202203356e-8L, - -0.136704883966171134993e-8L ], - [ 0.000649434156378600823045L, 0.000229472093621399176955L, - -0.000469189494395255712128L, 0.000267720632062838852962L, - -0.756180167188397641073e-4L, -0.239650511386729665193e-6L, - 0.110826541153473023615e-4L, -0.56749528269915965675e-5L, - 0.142309007324358839146e-5L, -0.278610802915281422406e-10L, - -0.169584040919302772899e-6L, 0.809946490538808236335e-7L, - -0.191111684859736540607e-7L ], - [ -0.000861888290916711698605L, 0.000784039221720066627474L, - -0.000299072480303190179733L, -0.146384525788434181781e-5L, - 0.664149821546512218666e-4L, -0.396836504717943466443e-4L, - 0.113757269706784190981e-4L, 0.250749722623753280165e-9L, - -0.169541495365583060147e-5L, 0.890750753220530968883e-6L, - -0.229293483400080487057e-6L ], - [ -0.000336798553366358150309L, -0.697281375836585777429e-4L, - 0.000277275324495939207873L, -0.000199325705161888477003L, - 0.679778047793720783882e-4L, 0.141906292064396701483e-6L, - -0.135940481897686932785e-4L, 0.801847025633420153972e-5L, - -0.229148117650809517038e-5L ], - [ 0.000531307936463992223166L, -0.000592166437353693882865L, - 0.000270878209671804482771L, 0.790235323266032787212e-6L, - -0.815396936756196875093e-4L, 0.561168275310624965004e-4L, - -0.183291165828433755673e-4L, -0.307961345060330478256e-8L, - 0.346515536880360908674e-5L, -0.20291327396058603727e-5L, - 0.57887928631490037089e-6L ], - [ 0.000344367606892377671254L, 0.517179090826059219337e-4L, - -0.000334931610811422363117L, 0.000281269515476323702274L, - -0.000109765822446847310235L, -0.127410090954844853795e-6L, - 0.277444515115636441571e-4L, -0.182634888057113326614e-4L, - 0.578769494973505239894e-5L ], - [ -0.000652623918595309418922L, 0.000839498720672087279993L, - -0.000438297098541721005061L, -0.696909145842055197137e-6L, - 0.000166448466420675478374L, -0.000127835176797692185853L, - 0.462995326369130429061e-4L ], - [ -0.000596761290192746250124L, -0.720489541602001055909e-4L, - 0.000678230883766732836162L, -0.0006401475260262758451L, - 0.000277501076343287044992L ], - [ 0.00133244544948006563713L, -0.0019144384985654775265L, - 0.00110893691345966373396L ], - [ 0.00157972766073083495909L, 0.000162516262783915816899L, - -0.00206334210355432762645L, 0.00213896861856890981541L, - -0.00101085593912630031708L ], - [ -0.00407251211951401664727L, 0.00640336283380806979482L, - -0.00404101610816766177474L ] - ]; - - // avoid nans when one of the arguments is inf: - if (x == real.infinity && a != real.infinity) - return 0; - - if (x != real.infinity && a == real.infinity) - return 1; - - real sigma = (x - a) / a; - real phi = sigma - log(sigma + 1); - - real y = a * phi; - real z = sqrt(2 * phi); - if (x < a) - z = -z; - - real[13] workspace; - foreach (i; 0 .. coef.length) - workspace[i] = poly(z, coef[i]); - - real result = poly(1 / a, workspace); - result *= exp(-y) / sqrt(2 * PI * a); - if (x < a) - result = -result; - - result += erfc(sqrt(y)) / 2; - - return result; -} - -} // private - -public: -/// The maximum value of x for which gamma(x) < real.infinity. -static if (floatTraits!(real).realFormat == RealFormat.ieeeQuadruple) - enum real MAXGAMMA = 1755.5483429L; -else static if (floatTraits!(real).realFormat == RealFormat.ieeeExtended) - enum real MAXGAMMA = 1755.5483429L; -else static if (floatTraits!(real).realFormat == RealFormat.ieeeExtended53) - enum real MAXGAMMA = 1755.5483429L; -else static if (floatTraits!(real).realFormat == RealFormat.ieeeDouble) - enum real MAXGAMMA = 171.6243769L; -else - static assert(0, "missing MAXGAMMA for other real types"); - - -/***************************************************** - * The Gamma function, $(GAMMA)(x) - * - * $(GAMMA)(x) is a generalisation of the factorial function - * to real and complex numbers. - * Like x!, $(GAMMA)(x+1) = x*$(GAMMA)(x). - * - * Mathematically, if z.re > 0 then - * $(GAMMA)(z) = $(INTEGRATE 0, ∞) $(POWER t, z-1)$(POWER e, -t) dt - * - * $(TABLE_SV - * $(SVH x, $(GAMMA)(x) ) - * $(SV $(NAN), $(NAN) ) - * $(SV ±0.0, ±∞) - * $(SV integer > 0, (x-1)! ) - * $(SV integer < 0, $(NAN) ) - * $(SV +∞, +∞ ) - * $(SV -∞, $(NAN) ) - * ) - */ -real gamma(real x) -{ -/* Based on code from the CEPHES library. - * CEPHES code Copyright 1994 by Stephen L. Moshier - * - * Arguments |x| <= 13 are reduced by recurrence and the function - * approximated by a rational function of degree 7/8 in the - * interval (2,3). Large arguments are handled by Stirling's - * formula. Large negative arguments are made positive using - * a reflection formula. - */ - - real q, z; - if (isNaN(x)) return x; - if (x == -x.infinity) return real.nan; - if ( fabs(x) > MAXGAMMA ) return real.infinity; - if (x == 0) return 1.0 / x; // +- infinity depending on sign of x, create an exception. - - q = fabs(x); - - if ( q > 13.0L ) - { - // Large arguments are handled by Stirling's - // formula. Large negative arguments are made positive using - // the reflection formula. - - if ( x < 0.0L ) - { - if (x < -1/real.epsilon) - { - // Large negatives lose all precision - return real.nan; - } - int sgngam = 1; // sign of gamma. - long intpart = cast(long)(q); - if (q == intpart) - return real.nan; // poles for all integers <0. - real p = intpart; - if ( (intpart & 1) == 0 ) - sgngam = -1; - z = q - p; - if ( z > 0.5L ) - { - p += 1.0L; - z = q - p; - } - z = q * sin( PI * z ); - z = fabs(z) * gammaStirling(q); - if ( z <= PI/real.max ) return sgngam * real.infinity; - return sgngam * PI/z; - } - else - { - return gammaStirling(x); - } - } - - // Arguments |x| <= 13 are reduced by recurrence and the function - // approximated by a rational function of degree 7/8 in the - // interval (2,3). - - z = 1.0L; - while ( x >= 3.0L ) - { - x -= 1.0L; - z *= x; - } - - while ( x < -0.03125L ) - { - z /= x; - x += 1.0L; - } - - if ( x <= 0.03125L ) - { - if ( x == 0.0L ) - return real.nan; - else - { - if ( x < 0.0L ) - { - x = -x; - return z / (x * poly( x, GammaSmallNegCoeffs )); - } - else - { - return z / (x * poly( x, GammaSmallCoeffs )); - } - } - } - - while ( x < 2.0L ) - { - z /= x; - x += 1.0L; - } - if ( x == 2.0L ) return z; - - x -= 2.0L; - return z * poly( x, GammaNumeratorCoeffs ) / poly( x, GammaDenominatorCoeffs ); -} - -@safe unittest -{ - // gamma(n) = factorial(n-1) if n is an integer. - real fact = 1.0L; - for (int i=1; fact= real.mant_dig-15); - fact *= (i*1.0L); - } - assert(gamma(0.0) == real.infinity); - assert(gamma(-0.0) == -real.infinity); - assert(isNaN(gamma(-1.0))); - assert(isNaN(gamma(-15.0))); - assert(isIdentical(gamma(NaN(0xABC)), NaN(0xABC))); - assert(gamma(real.infinity) == real.infinity); - assert(gamma(real.max) == real.infinity); - assert(isNaN(gamma(-real.infinity))); - assert(gamma(real.min_normal*real.epsilon) == real.infinity); - assert(gamma(MAXGAMMA)< real.infinity); - assert(gamma(MAXGAMMA*2) == real.infinity); - - // Test some high-precision values (50 decimal digits) - real SQRT_PI = 1.77245385090551602729816748334114518279754945612238L; - - - assert(feqrel(gamma(0.5L), SQRT_PI) >= real.mant_dig-1); - assert(feqrel(gamma(17.25L), 4.224986665692703551570937158682064589938e13L) >= real.mant_dig-4); - - assert(feqrel(gamma(1.0 / 3.0L), 2.67893853470774763365569294097467764412868937795730L) >= real.mant_dig-2); - assert(feqrel(gamma(0.25L), - 3.62560990822190831193068515586767200299516768288006L) >= real.mant_dig-1); - assert(feqrel(gamma(1.0 / 5.0L), - 4.59084371199880305320475827592915200343410999829340L) >= real.mant_dig-1); -} - -/***************************************************** - * Natural logarithm of gamma function. - * - * Returns the base e (2.718...) logarithm of the absolute - * value of the gamma function of the argument. - * - * For reals, logGamma is equivalent to log(fabs(gamma(x))). - * - * $(TABLE_SV - * $(SVH x, logGamma(x) ) - * $(SV $(NAN), $(NAN) ) - * $(SV integer <= 0, +∞ ) - * $(SV ±∞, +∞ ) - * ) - */ -real logGamma(real x) -{ - /* Based on code from the CEPHES library. - * CEPHES code Copyright 1994 by Stephen L. Moshier - * - * For arguments greater than 33, the logarithm of the gamma - * function is approximated by the logarithmic version of - * Stirling's formula using a polynomial approximation of - * degree 4. Arguments between -33 and +33 are reduced by - * recurrence to the interval [2,3] of a rational approximation. - * The cosecant reflection formula is employed for arguments - * less than -33. - */ - real q, w, z, f, nx; - - if (isNaN(x)) return x; - if (fabs(x) == x.infinity) return x.infinity; - - if ( x < -34.0L ) - { - q = -x; - w = logGamma(q); - real p = floor(q); - if ( p == q ) - return real.infinity; - int intpart = cast(int)(p); - real sgngam = 1; - if ( (intpart & 1) == 0 ) - sgngam = -1; - z = q - p; - if ( z > 0.5L ) - { - p += 1.0L; - z = p - q; - } - z = q * sin( PI * z ); - if ( z == 0.0L ) - return sgngam * real.infinity; - /* z = LOGPI - logl( z ) - w; */ - z = log( PI/z ) - w; - return z; - } - - if ( x < 13.0L ) - { - z = 1.0L; - nx = floor( x + 0.5L ); - f = x - nx; - while ( x >= 3.0L ) - { - nx -= 1.0L; - x = nx + f; - z *= x; - } - while ( x < 2.0L ) - { - if ( fabs(x) <= 0.03125L ) - { - if ( x == 0.0L ) - return real.infinity; - if ( x < 0.0L ) - { - x = -x; - q = z / (x * poly( x, GammaSmallNegCoeffs)); - } else - q = z / (x * poly( x, GammaSmallCoeffs)); - return log( fabs(q) ); - } - z /= nx + f; - nx += 1.0L; - x = nx + f; - } - z = fabs(z); - if ( x == 2.0L ) - return log(z); - x = (nx - 2.0L) + f; - real p = x * rationalPoly( x, logGammaNumerator, logGammaDenominator); - return log(z) + p; - } - - // const real MAXLGM = 1.04848146839019521116e+4928L; - // if ( x > MAXLGM ) return sgngaml * real.infinity; - - const real LOGSQRT2PI = 0.91893853320467274178L; // log( sqrt( 2*pi ) ) - - q = ( x - 0.5L ) * log(x) - x + LOGSQRT2PI; - if (x > 1.0e10L) return q; - real p = 1.0L / (x*x); - q += poly( p, logGammaStirlingCoeffs ) / x; - return q ; -} - -@safe unittest -{ - assert(isIdentical(logGamma(NaN(0xDEF)), NaN(0xDEF))); - assert(logGamma(real.infinity) == real.infinity); - assert(logGamma(-1.0) == real.infinity); - assert(logGamma(0.0) == real.infinity); - assert(logGamma(-50.0) == real.infinity); - assert(isIdentical(0.0L, logGamma(1.0L))); - assert(isIdentical(0.0L, logGamma(2.0L))); - assert(logGamma(real.min_normal*real.epsilon) == real.infinity); - assert(logGamma(-real.min_normal*real.epsilon) == real.infinity); - - // x, correct loggamma(x), correct d/dx loggamma(x). - immutable static real[] testpoints = [ - 8.0L, 8.525146484375L + 1.48766904143001655310E-5, 2.01564147795560999654E0L, - 8.99993896484375e-1L, 6.6375732421875e-2L + 5.11505711292524166220E-6L, -7.54938684259372234258E-1, - 7.31597900390625e-1L, 2.2369384765625e-1 + 5.21506341809849792422E-6L, -1.13355566660398608343E0L, - 2.31639862060546875e-1L, 1.3686676025390625L + 1.12609441752996145670E-5L, -4.56670961813812679012E0, - 1.73162841796875L, -8.88214111328125e-2L + 3.36207740803753034508E-6L, 2.33339034686200586920E-1L, - 1.23162841796875L, -9.3902587890625e-2L + 1.28765089229009648104E-5L, -2.49677345775751390414E-1L, - 7.3786976294838206464e19L, 3.301798506038663053312e21L - 1.656137564136932662487046269677E5L, - 4.57477139169563904215E1L, - 1.08420217248550443401E-19L, 4.36682586669921875e1L + 1.37082843669932230418E-5L, - -9.22337203685477580858E18L, - 1.0L, 0.0L, -5.77215664901532860607E-1L, - 2.0L, 0.0L, 4.22784335098467139393E-1L, - -0.5L, 1.2655029296875L + 9.19379714539648894580E-6L, 3.64899739785765205590E-2L, - -1.5L, 8.6004638671875e-1L + 6.28657731014510932682E-7L, 7.03156640645243187226E-1L, - -2.5L, -5.6243896484375E-2L + 1.79986700949327405470E-7, 1.10315664064524318723E0L, - -3.5L, -1.30902099609375L + 1.43111007079536392848E-5L, 1.38887092635952890151E0L - ]; - // TODO: test derivatives as well. - for (int i=0; i real.mant_dig-5); - if (testpoints[i] real.mant_dig-5); - } - } - assert(feqrel(logGamma(-50.2L),log(fabs(gamma(-50.2L)))) > real.mant_dig-2); - assert(feqrel(logGamma(-0.008L),log(fabs(gamma(-0.008L)))) > real.mant_dig-2); - assert(feqrel(logGamma(-38.8L),log(fabs(gamma(-38.8L)))) > real.mant_dig-4); - static if (real.mant_dig >= 64) // incl. 80-bit reals - assert(feqrel(logGamma(1500.0L),log(gamma(1500.0L))) > real.mant_dig-2); - else static if (real.mant_dig >= 53) // incl. 64-bit reals - assert(feqrel(logGamma(150.0L),log(gamma(150.0L))) > real.mant_dig-2); -} - - -private { -/* - * These value can be calculated like this: - * 1) Get exact real.max/min_normal/epsilon from compiler: - * writefln!"%a"(real.max/min_normal_epsilon) - * 2) Convert for Wolfram Alpha - * 0xf.fffffffffffffffp+16380 ==> (f.fffffffffffffff base 16) * 2^16380 - * 3) Calculate result on wofram alpha: - * http://www.wolframalpha.com/input/?i=ln((1.ffffffffffffffffffffffffffff+base+16)+*+2%5E16383)+in+base+2 - * 4) Convert to proper format: - * string mantissa = "1.011..."; - * write(mantissa[0 .. 2]); mantissa = mantissa[2 .. $]; - * for (size_t i = 0; i < mantissa.length/4; i++) - * { - * writef!"%x"(to!ubyte(mantissa[0 .. 4], 2)); mantissa = mantissa[4 .. $]; - * } - */ -static if (floatTraits!(real).realFormat == RealFormat.ieeeQuadruple) -{ - enum real MAXLOG = 0x1.62e42fefa39ef35793c7673007e6p+13L; // log(real.max) - enum real MINLOG = -0x1.6546282207802c89d24d65e96274p+13L; // log(real.min_normal*real.epsilon) = log(smallest denormal) -} -else static if (floatTraits!(real).realFormat == RealFormat.ieeeExtended) -{ - enum real MAXLOG = 0x1.62e42fefa39ef358p+13L; // log(real.max) - enum real MINLOG = -0x1.6436716d5406e6d8p+13L; // log(real.min_normal*real.epsilon) = log(smallest denormal) -} -else static if (floatTraits!(real).realFormat == RealFormat.ieeeExtended53) -{ - enum real MAXLOG = 0x1.62e42fefa39ef358p+13L; // log(real.max) - enum real MINLOG = -0x1.6436716d5406e6d8p+13L; // log(real.min_normal*real.epsilon) = log(smallest denormal) -} -else static if (floatTraits!(real).realFormat == RealFormat.ieeeDouble) -{ - enum real MAXLOG = 0x1.62e42fefa39efp+9L; // log(real.max) - enum real MINLOG = -0x1.74385446d71c3p+9L; // log(real.min_normal*real.epsilon) = log(smallest denormal) -} -else - static assert(0, "missing MAXLOG and MINLOG for other real types"); - -enum real BETA_BIG = 9.223372036854775808e18L; -enum real BETA_BIGINV = 1.084202172485504434007e-19L; -} - -/** Incomplete beta integral - * - * Returns incomplete beta integral of the arguments, evaluated - * from zero to x. The regularized incomplete beta function is defined as - * - * betaIncomplete(a, b, x) = Γ(a+b)/(Γ(a) Γ(b)) * - * $(INTEGRATE 0, x) $(POWER t, a-1)$(POWER (1-t),b-1) dt - * - * and is the same as the cumulative distribution function. - * - * The domain of definition is 0 <= x <= 1. In this - * implementation a and b are restricted to positive values. - * The integral from x to 1 may be obtained by the symmetry - * relation - * - * betaIncompleteCompl(a, b, x ) = betaIncomplete( b, a, 1-x ) - * - * The integral is evaluated by a continued fraction expansion - * or, when b*x is small, by a power series. - */ -real betaIncomplete(real aa, real bb, real xx ) -{ - if ( !(aa>0 && bb>0) ) - { - if ( isNaN(aa) ) return aa; - if ( isNaN(bb) ) return bb; - return real.nan; // domain error - } - if (!(xx>0 && xx<1.0)) - { - if (isNaN(xx)) return xx; - if ( xx == 0.0L ) return 0.0; - if ( xx == 1.0L ) return 1.0; - return real.nan; // domain error - } - if ( (bb * xx) <= 1.0L && xx <= 0.95L) - { - return betaDistPowerSeries(aa, bb, xx); - } - real x; - real xc; // = 1 - x - - real a, b; - int flag = 0; - - /* Reverse a and b if x is greater than the mean. */ - if ( xx > (aa/(aa+bb)) ) - { - // here x > aa/(aa+bb) and (bb*x>1 or x>0.95) - flag = 1; - a = bb; - b = aa; - xc = xx; - x = 1.0L - xx; - } - else - { - a = aa; - b = bb; - xc = 1.0L - xx; - x = xx; - } - - if ( flag == 1 && (b * x) <= 1.0L && x <= 0.95L) - { - // here xx > aa/(aa+bb) and ((bb*xx>1) or xx>0.95) and (aa*(1-xx)<=1) and xx > 0.05 - return 1.0 - betaDistPowerSeries(a, b, x); // note loss of precision - } - - real w; - // Choose expansion for optimal convergence - // One is for x * (a+b+2) < (a+1), - // the other is for x * (a+b+2) > (a+1). - real y = x * (a+b-2.0L) - (a-1.0L); - if ( y < 0.0L ) - { - w = betaDistExpansion1( a, b, x ); - } - else - { - w = betaDistExpansion2( a, b, x ) / xc; - } - - /* Multiply w by the factor - a b - x (1-x) Gamma(a+b) / ( a Gamma(a) Gamma(b) ) . */ - - y = a * log(x); - real t = b * log(xc); - if ( (a+b) < MAXGAMMA && fabs(y) < MAXLOG && fabs(t) < MAXLOG ) - { - t = pow(xc,b); - t *= pow(x,a); - t /= a; - t *= w; - t *= gamma(a+b) / (gamma(a) * gamma(b)); - } - else - { - /* Resort to logarithms. */ - y += t + logGamma(a+b) - logGamma(a) - logGamma(b); - y += log(w/a); - - t = exp(y); -/+ - // There seems to be a bug in Cephes at this point. - // Problems occur for y > MAXLOG, not y < MINLOG. - if ( y < MINLOG ) - { - t = 0.0L; - } - else - { - t = exp(y); - } -+/ - } - if ( flag == 1 ) - { -/+ // CEPHES includes this code, but I think it is erroneous. - if ( t <= real.epsilon ) - { - t = 1.0L - real.epsilon; - } else -+/ - t = 1.0L - t; - } - return t; -} - -/** Inverse of incomplete beta integral - * - * Given y, the function finds x such that - * - * betaIncomplete(a, b, x) == y - * - * Newton iterations or interval halving is used. - */ -real betaIncompleteInv(real aa, real bb, real yy0 ) -{ - real a, b, y0, d, y, x, x0, x1, lgm, yp, di, dithresh, yl, yh, xt; - int i, rflg, dir, nflg; - - if (isNaN(yy0)) return yy0; - if (isNaN(aa)) return aa; - if (isNaN(bb)) return bb; - if ( yy0 <= 0.0L ) - return 0.0L; - if ( yy0 >= 1.0L ) - return 1.0L; - x0 = 0.0L; - yl = 0.0L; - x1 = 1.0L; - yh = 1.0L; - if ( aa <= 1.0L || bb <= 1.0L ) - { - dithresh = 1.0e-7L; - rflg = 0; - a = aa; - b = bb; - y0 = yy0; - x = a/(a+b); - y = betaIncomplete( a, b, x ); - nflg = 0; - goto ihalve; - } - else - { - nflg = 0; - dithresh = 1.0e-4L; - } - - // approximation to inverse function - - yp = -normalDistributionInvImpl( yy0 ); - - if ( yy0 > 0.5L ) - { - rflg = 1; - a = bb; - b = aa; - y0 = 1.0L - yy0; - yp = -yp; - } - else - { - rflg = 0; - a = aa; - b = bb; - y0 = yy0; - } - - lgm = (yp * yp - 3.0L)/6.0L; - x = 2.0L/( 1.0L/(2.0L * a-1.0L) + 1.0L/(2.0L * b - 1.0L) ); - d = yp * sqrt( x + lgm ) / x - - ( 1.0L/(2.0L * b - 1.0L) - 1.0L/(2.0L * a - 1.0L) ) - * (lgm + (5.0L/6.0L) - 2.0L/(3.0L * x)); - d = 2.0L * d; - if ( d < MINLOG ) - { - x = 1.0L; - goto under; - } - x = a/( a + b * exp(d) ); - y = betaIncomplete( a, b, x ); - yp = (y - y0)/y0; - if ( fabs(yp) < 0.2 ) - goto newt; - - /* Resort to interval halving if not close enough. */ -ihalve: - - dir = 0; - di = 0.5L; - for ( i=0; i<400; i++ ) - { - if ( i != 0 ) - { - x = x0 + di * (x1 - x0); - if ( x == 1.0L ) - { - x = 1.0L - real.epsilon; - } - if ( x == 0.0L ) - { - di = 0.5; - x = x0 + di * (x1 - x0); - if ( x == 0.0 ) - goto under; - } - y = betaIncomplete( a, b, x ); - yp = (x1 - x0)/(x1 + x0); - if ( fabs(yp) < dithresh ) - goto newt; - yp = (y-y0)/y0; - if ( fabs(yp) < dithresh ) - goto newt; - } - if ( y < y0 ) - { - x0 = x; - yl = y; - if ( dir < 0 ) - { - dir = 0; - di = 0.5L; - } else if ( dir > 3 ) - di = 1.0L - (1.0L - di) * (1.0L - di); - else if ( dir > 1 ) - di = 0.5L * di + 0.5L; - else - di = (y0 - y)/(yh - yl); - dir += 1; - if ( x0 > 0.95L ) - { - if ( rflg == 1 ) - { - rflg = 0; - a = aa; - b = bb; - y0 = yy0; - } - else - { - rflg = 1; - a = bb; - b = aa; - y0 = 1.0 - yy0; - } - x = 1.0L - x; - y = betaIncomplete( a, b, x ); - x0 = 0.0; - yl = 0.0; - x1 = 1.0; - yh = 1.0; - goto ihalve; - } - } - else - { - x1 = x; - if ( rflg == 1 && x1 < real.epsilon ) - { - x = 0.0L; - goto done; - } - yh = y; - if ( dir > 0 ) - { - dir = 0; - di = 0.5L; - } - else if ( dir < -3 ) - di = di * di; - else if ( dir < -1 ) - di = 0.5L * di; - else - di = (y - y0)/(yh - yl); - dir -= 1; - } - } - if ( x0 >= 1.0L ) - { - // partial loss of precision - x = 1.0L - real.epsilon; - goto done; - } - if ( x <= 0.0L ) - { -under: - // underflow has occurred - x = real.min_normal * real.min_normal; - goto done; - } - -newt: - - if ( nflg ) - { - goto done; - } - nflg = 1; - lgm = logGamma(a+b) - logGamma(a) - logGamma(b); - - for ( i=0; i<15; i++ ) - { - /* Compute the function at this point. */ - if ( i != 0 ) - y = betaIncomplete(a,b,x); - if ( y < yl ) - { - x = x0; - y = yl; - } - else if ( y > yh ) - { - x = x1; - y = yh; - } - else if ( y < y0 ) - { - x0 = x; - yl = y; - } - else - { - x1 = x; - yh = y; - } - if ( x == 1.0L || x == 0.0L ) - break; - /* Compute the derivative of the function at this point. */ - d = (a - 1.0L) * log(x) + (b - 1.0L) * log(1.0L - x) + lgm; - if ( d < MINLOG ) - { - goto done; - } - if ( d > MAXLOG ) - { - break; - } - d = exp(d); - /* Compute the step to the next approximation of x. */ - d = (y - y0)/d; - xt = x - d; - if ( xt <= x0 ) - { - y = (x - x0) / (x1 - x0); - xt = x0 + 0.5L * y * (x - x0); - if ( xt <= 0.0L ) - break; - } - if ( xt >= x1 ) - { - y = (x1 - x) / (x1 - x0); - xt = x1 - 0.5L * y * (x1 - x); - if ( xt >= 1.0L ) - break; - } - x = xt; - if ( fabs(d/x) < (128.0L * real.epsilon) ) - goto done; - } - /* Did not converge. */ - dithresh = 256.0L * real.epsilon; - goto ihalve; - -done: - if ( rflg ) - { - if ( x <= real.epsilon ) - x = 1.0L - real.epsilon; - else - x = 1.0L - x; - } - return x; -} - -@safe unittest { // also tested by the normal distribution - // check NaN propagation - assert(isIdentical(betaIncomplete(NaN(0xABC),2,3), NaN(0xABC))); - assert(isIdentical(betaIncomplete(7,NaN(0xABC),3), NaN(0xABC))); - assert(isIdentical(betaIncomplete(7,15,NaN(0xABC)), NaN(0xABC))); - assert(isIdentical(betaIncompleteInv(NaN(0xABC),1,17), NaN(0xABC))); - assert(isIdentical(betaIncompleteInv(2,NaN(0xABC),8), NaN(0xABC))); - assert(isIdentical(betaIncompleteInv(2,3, NaN(0xABC)), NaN(0xABC))); - - assert(isNaN(betaIncomplete(-1, 2, 3))); - - assert(betaIncomplete(1, 2, 0)==0); - assert(betaIncomplete(1, 2, 1)==1); - assert(isNaN(betaIncomplete(1, 2, 3))); - assert(betaIncompleteInv(1, 1, 0)==0); - assert(betaIncompleteInv(1, 1, 1)==1); - - // Test against Mathematica betaRegularized[z,a,b] - // These arbitrary points are chosen to give good code coverage. - assert(feqrel(betaIncomplete(8, 10, 0.2L), 0.010_934_315_234_099_2L) >= real.mant_dig - 5); - assert(feqrel(betaIncomplete(2, 2.5L, 0.9L), 0.989_722_597_604_452_767_171_003_59L) >= real.mant_dig - 1); - static if (real.mant_dig >= 64) // incl. 80-bit reals - assert(feqrel(betaIncomplete(1000, 800, 0.5L), 1.179140859734704555102808541457164E-06L) >= real.mant_dig - 13); - else - assert(feqrel(betaIncomplete(1000, 800, 0.5L), 1.179140859734704555102808541457164E-06L) >= real.mant_dig - 14); - assert(feqrel(betaIncomplete(0.0001, 10000, 0.0001L), 0.999978059362107134278786L) >= real.mant_dig - 18); - assert(betaIncomplete(0.01L, 327726.7L, 0.545113L) == 1.0); - assert(feqrel(betaIncompleteInv(8, 10, 0.010_934_315_234_099_2L), 0.2L) >= real.mant_dig - 2); - assert(feqrel(betaIncomplete(0.01L, 498.437L, 0.0121433L), 0.99999664562033077636065L) >= real.mant_dig - 1); - assert(feqrel(betaIncompleteInv(5, 10, 0.2000002972865658842L), 0.229121208190918L) >= real.mant_dig - 3); - assert(feqrel(betaIncompleteInv(4, 7, 0.8000002209179505L), 0.483657360076904L) >= real.mant_dig - 3); - - // Coverage tests. I don't have correct values for these tests, but - // these values cover most of the code, so they are useful for - // regression testing. - // Extensive testing failed to increase the coverage. It seems likely that about - // half the code in this function is unnecessary; there is potential for - // significant improvement over the original CEPHES code. - static if (real.mant_dig == 64) // 80-bit reals - { - assert(betaIncompleteInv(0.01L, 8e-48L, 5.45464e-20L) == 1-real.epsilon); - assert(betaIncompleteInv(0.01L, 8e-48L, 9e-26L) == 1-real.epsilon); - - // Beware: a one-bit change in pow() changes almost all digits in the result! - assert(feqrel( - betaIncompleteInv(0x1.b3d151fbba0eb18p+1L, 1.2265e-19L, 2.44859e-18L), - 0x1.c0110c8531d0952cp-1L - ) > 10); - // This next case uncovered a one-bit difference in the FYL2X instruction - // between Intel and AMD processors. This difference gets magnified by 2^^38. - // WolframAlpha crashes attempting to calculate this. - assert(feqrel(betaIncompleteInv(0x1.ff1275ae5b939bcap-41L, 4.6713e18L, 0.0813601L), - 0x1.f97749d90c7adba8p-63L) >= real.mant_dig - 39); - real a1 = 3.40483L; - assert(betaIncompleteInv(a1, 4.0640301659679627772e19L, 0.545113L) == 0x1.ba8c08108aaf5d14p-109L); - real b1 = 2.82847e-25L; - assert(feqrel(betaIncompleteInv(0.01L, b1, 9e-26L), 0x1.549696104490aa9p-830L) >= real.mant_dig-10); - - // --- Problematic cases --- - // This is a situation where the series expansion fails to converge - assert( isNaN(betaIncompleteInv(0.12167L, 4.0640301659679627772e19L, 0.0813601L))); - // This next result is almost certainly erroneous. - // Mathematica states: "(cannot be determined by current methods)" - assert(betaIncomplete(1.16251e20L, 2.18e39L, 5.45e-20L) == -real.infinity); - // WolframAlpha gives no result for this, though indicates that it approximately 1.0 - 1.3e-9 - assert(1 - betaIncomplete(0.01L, 328222, 4.0375e-5L) == 0x1.5f62926b4p-30L); - } -} - - -private { -// Implementation functions - -// Continued fraction expansion #1 for incomplete beta integral -// Use when x < (a+1)/(a+b+2) -real betaDistExpansion1(real a, real b, real x ) -{ - real xk, pk, pkm1, pkm2, qk, qkm1, qkm2; - real k1, k2, k3, k4, k5, k6, k7, k8; - real r, t, ans; - int n; - - k1 = a; - k2 = a + b; - k3 = a; - k4 = a + 1.0L; - k5 = 1.0L; - k6 = b - 1.0L; - k7 = k4; - k8 = a + 2.0L; - - pkm2 = 0.0L; - qkm2 = 1.0L; - pkm1 = 1.0L; - qkm1 = 1.0L; - ans = 1.0L; - r = 1.0L; - n = 0; - const real thresh = 3.0L * real.epsilon; - do - { - xk = -( x * k1 * k2 )/( k3 * k4 ); - pk = pkm1 + pkm2 * xk; - qk = qkm1 + qkm2 * xk; - pkm2 = pkm1; - pkm1 = pk; - qkm2 = qkm1; - qkm1 = qk; - - xk = ( x * k5 * k6 )/( k7 * k8 ); - pk = pkm1 + pkm2 * xk; - qk = qkm1 + qkm2 * xk; - pkm2 = pkm1; - pkm1 = pk; - qkm2 = qkm1; - qkm1 = qk; - - if ( qk != 0.0L ) - r = pk/qk; - if ( r != 0.0L ) - { - t = fabs( (ans - r)/r ); - ans = r; - } - else - { - t = 1.0L; - } - - if ( t < thresh ) - return ans; - - k1 += 1.0L; - k2 += 1.0L; - k3 += 2.0L; - k4 += 2.0L; - k5 += 1.0L; - k6 -= 1.0L; - k7 += 2.0L; - k8 += 2.0L; - - if ( (fabs(qk) + fabs(pk)) > BETA_BIG ) - { - pkm2 *= BETA_BIGINV; - pkm1 *= BETA_BIGINV; - qkm2 *= BETA_BIGINV; - qkm1 *= BETA_BIGINV; - } - if ( (fabs(qk) < BETA_BIGINV) || (fabs(pk) < BETA_BIGINV) ) - { - pkm2 *= BETA_BIG; - pkm1 *= BETA_BIG; - qkm2 *= BETA_BIG; - qkm1 *= BETA_BIG; - } - } - while ( ++n < 400 ); -// loss of precision has occurred -// mtherr( "incbetl", PLOSS ); - return ans; -} - -// Continued fraction expansion #2 for incomplete beta integral -// Use when x > (a+1)/(a+b+2) -real betaDistExpansion2(real a, real b, real x ) -{ - real xk, pk, pkm1, pkm2, qk, qkm1, qkm2; - real k1, k2, k3, k4, k5, k6, k7, k8; - real r, t, ans, z; - - k1 = a; - k2 = b - 1.0L; - k3 = a; - k4 = a + 1.0L; - k5 = 1.0L; - k6 = a + b; - k7 = a + 1.0L; - k8 = a + 2.0L; - - pkm2 = 0.0L; - qkm2 = 1.0L; - pkm1 = 1.0L; - qkm1 = 1.0L; - z = x / (1.0L-x); - ans = 1.0L; - r = 1.0L; - int n = 0; - const real thresh = 3.0L * real.epsilon; - do - { - xk = -( z * k1 * k2 )/( k3 * k4 ); - pk = pkm1 + pkm2 * xk; - qk = qkm1 + qkm2 * xk; - pkm2 = pkm1; - pkm1 = pk; - qkm2 = qkm1; - qkm1 = qk; - - xk = ( z * k5 * k6 )/( k7 * k8 ); - pk = pkm1 + pkm2 * xk; - qk = qkm1 + qkm2 * xk; - pkm2 = pkm1; - pkm1 = pk; - qkm2 = qkm1; - qkm1 = qk; - - if ( qk != 0.0L ) - r = pk/qk; - if ( r != 0.0L ) - { - t = fabs( (ans - r)/r ); - ans = r; - } else - t = 1.0L; - - if ( t < thresh ) - return ans; - k1 += 1.0L; - k2 -= 1.0L; - k3 += 2.0L; - k4 += 2.0L; - k5 += 1.0L; - k6 += 1.0L; - k7 += 2.0L; - k8 += 2.0L; - - if ( (fabs(qk) + fabs(pk)) > BETA_BIG ) - { - pkm2 *= BETA_BIGINV; - pkm1 *= BETA_BIGINV; - qkm2 *= BETA_BIGINV; - qkm1 *= BETA_BIGINV; - } - if ( (fabs(qk) < BETA_BIGINV) || (fabs(pk) < BETA_BIGINV) ) - { - pkm2 *= BETA_BIG; - pkm1 *= BETA_BIG; - qkm2 *= BETA_BIG; - qkm1 *= BETA_BIG; - } - } while ( ++n < 400 ); -// loss of precision has occurred -//mtherr( "incbetl", PLOSS ); - return ans; -} - -/* Power series for incomplete gamma integral. - Use when b*x is small. */ -real betaDistPowerSeries(real a, real b, real x ) -{ - real ai = 1.0L / a; - real u = (1.0L - b) * x; - real v = u / (a + 1.0L); - real t1 = v; - real t = u; - real n = 2.0L; - real s = 0.0L; - real z = real.epsilon * ai; - while ( fabs(v) > z ) - { - u = (n - b) * x / n; - t *= u; - v = t / (a + n); - s += v; - n += 1.0L; - } - s += t1; - s += ai; - - u = a * log(x); - if ( (a+b) < MAXGAMMA && fabs(u) < MAXLOG ) - { - t = gamma(a+b)/(gamma(a)*gamma(b)); - s = s * t * pow(x,a); - } - else - { - t = logGamma(a+b) - logGamma(a) - logGamma(b) + u + log(s); - - if ( t < MINLOG ) - { - s = 0.0L; - } else - s = exp(t); - } - return s; -} - -} - -/*************************************** - * Incomplete gamma integral and its complement - * - * These functions are defined by - * - * gammaIncomplete = ( $(INTEGRATE 0, x) $(POWER e, -t) $(POWER t, a-1) dt )/ $(GAMMA)(a) - * - * gammaIncompleteCompl(a,x) = 1 - gammaIncomplete(a,x) - * = ($(INTEGRATE x, ∞) $(POWER e, -t) $(POWER t, a-1) dt )/ $(GAMMA)(a) - * - * In this implementation both arguments must be positive. - * The integral is evaluated by either a power series or - * continued fraction expansion, depending on the relative - * values of a and x. - */ -real gammaIncomplete(real a, real x ) -in -{ - assert(x >= 0); - assert(a > 0); -} -do -{ - /* left tail of incomplete gamma function: - * - * inf. k - * a -x - x - * x e > ---------- - * - - - * k=0 | (a+k+1) - * - */ - if (x == 0) - return 0.0L; - - if ( (x > 1.0L) && (x > a ) ) - return 1.0L - gammaIncompleteCompl(a,x); - - real ax = a * log(x) - x - logGamma(a); -/+ - if ( ax < MINLOGL ) return 0; // underflow - // { mtherr( "igaml", UNDERFLOW ); return( 0.0L ); } -+/ - ax = exp(ax); - - /* power series */ - real r = a; - real c = 1.0L; - real ans = 1.0L; - - do - { - r += 1.0L; - c *= x/r; - ans += c; - } while ( c/ans > real.epsilon ); - - return ans * ax/a; -} - -/** ditto */ -real gammaIncompleteCompl(real a, real x ) -in -{ - assert(x >= 0); - assert(a > 0); -} -do -{ - if (x == 0) - return 1.0L; - if ( (x < 1.0L) || (x < a) ) - return 1.0L - gammaIncomplete(a,x); - - // DAC (Cephes bug fix): This is necessary to avoid - // spurious nans, eg - // log(x)-x = NaN when x = real.infinity - const real MAXLOGL = 1.1356523406294143949492E4L; - if (x > MAXLOGL) - return igammaTemmeLarge(a, x); - - real ax = a * log(x) - x - logGamma(a); -//const real MINLOGL = -1.1355137111933024058873E4L; -// if ( ax < MINLOGL ) return 0; // underflow; - ax = exp(ax); - - - /* continued fraction */ - real y = 1.0L - a; - real z = x + y + 1.0L; - real c = 0.0L; - - real pk, qk, t; - - real pkm2 = 1.0L; - real qkm2 = x; - real pkm1 = x + 1.0L; - real qkm1 = z * x; - real ans = pkm1/qkm1; - - do - { - c += 1.0L; - y += 1.0L; - z += 2.0L; - real yc = y * c; - pk = pkm1 * z - pkm2 * yc; - qk = qkm1 * z - qkm2 * yc; - if ( qk != 0.0L ) - { - real r = pk/qk; - t = fabs( (ans - r)/r ); - ans = r; - } - else - { - t = 1.0L; - } - pkm2 = pkm1; - pkm1 = pk; - qkm2 = qkm1; - qkm1 = qk; - - const real BIG = 9.223372036854775808e18L; - - if ( fabs(pk) > BIG ) - { - pkm2 /= BIG; - pkm1 /= BIG; - qkm2 /= BIG; - qkm1 /= BIG; - } - } while ( t > real.epsilon ); - - return ans * ax; -} - -/** Inverse of complemented incomplete gamma integral - * - * Given a and p, the function finds x such that - * - * gammaIncompleteCompl( a, x ) = p. - * - * Starting with the approximate value x = a $(POWER t, 3), where - * t = 1 - d - normalDistributionInv(p) sqrt(d), - * and d = 1/9a, - * the routine performs up to 10 Newton iterations to find the - * root of incompleteGammaCompl(a,x) - p = 0. - */ -real gammaIncompleteComplInv(real a, real p) -in -{ - assert(p >= 0 && p <= 1); - assert(a>0); -} -do -{ - if (p == 0) return real.infinity; - - real y0 = p; - const real MAXLOGL = 1.1356523406294143949492E4L; - real x0, x1, x, yl, yh, y, d, lgm, dithresh; - int i, dir; - - /* bound the solution */ - x0 = real.max; - yl = 0.0L; - x1 = 0.0L; - yh = 1.0L; - dithresh = 4.0 * real.epsilon; - - /* approximation to inverse function */ - d = 1.0L/(9.0L*a); - y = 1.0L - d - normalDistributionInvImpl(y0) * sqrt(d); - x = a * y * y * y; - - lgm = logGamma(a); - - for ( i=0; i<10; i++ ) - { - if ( x > x0 || x < x1 ) - goto ihalve; - y = gammaIncompleteCompl(a,x); - if ( y < yl || y > yh ) - goto ihalve; - if ( y < y0 ) - { - x0 = x; - yl = y; - } - else - { - x1 = x; - yh = y; - } - /* compute the derivative of the function at this point */ - d = (a - 1.0L) * log(x0) - x0 - lgm; - if ( d < -MAXLOGL ) - goto ihalve; - d = -exp(d); - /* compute the step to the next approximation of x */ - d = (y - y0)/d; - x = x - d; - if ( i < 3 ) continue; - if ( fabs(d/x) < dithresh ) return x; - } - - /* Resort to interval halving if Newton iteration did not converge. */ -ihalve: - d = 0.0625L; - if ( x0 == real.max ) - { - if ( x <= 0.0L ) - x = 1.0L; - while ( x0 == real.max ) - { - x = (1.0L + d) * x; - y = gammaIncompleteCompl( a, x ); - if ( y < y0 ) - { - x0 = x; - yl = y; - break; - } - d = d + d; - } - } - d = 0.5L; - dir = 0; - - for ( i=0; i<400; i++ ) - { - x = x1 + d * (x0 - x1); - y = gammaIncompleteCompl( a, x ); - lgm = (x0 - x1)/(x1 + x0); - if ( fabs(lgm) < dithresh ) - break; - lgm = (y - y0)/y0; - if ( fabs(lgm) < dithresh ) - break; - if ( x <= 0.0L ) - break; - if ( y > y0 ) - { - x1 = x; - yh = y; - if ( dir < 0 ) - { - dir = 0; - d = 0.5L; - } else if ( dir > 1 ) - d = 0.5L * d + 0.5L; - else - d = (y0 - yl)/(yh - yl); - dir += 1; - } - else - { - x0 = x; - yl = y; - if ( dir > 0 ) - { - dir = 0; - d = 0.5L; - } else if ( dir < -1 ) - d = 0.5L * d; - else - d = (y0 - yl)/(yh - yl); - dir -= 1; - } - } - /+ - if ( x == 0.0L ) - mtherr( "igamil", UNDERFLOW ); - +/ - return x; -} - -@safe unittest -{ -//Values from Excel's GammaInv(1-p, x, 1) -assert(fabs(gammaIncompleteComplInv(1, 0.5L) - 0.693147188044814L) < 0.00000005L); -assert(fabs(gammaIncompleteComplInv(12, 0.99L) - 5.42818075054289L) < 0.00000005L); -assert(fabs(gammaIncompleteComplInv(100, 0.8L) - 91.5013985848288L) < 0.000005L); -assert(gammaIncomplete(1, 0)==0); -assert(gammaIncompleteCompl(1, 0)==1); -assert(gammaIncomplete(4545, real.infinity)==1); - -// Values from Excel's (1-GammaDist(x, alpha, 1, TRUE)) - -assert(fabs(1.0L-gammaIncompleteCompl(0.5L, 2) - 0.954499729507309L) < 0.00000005L); -assert(fabs(gammaIncomplete(0.5L, 2) - 0.954499729507309L) < 0.00000005L); -// Fixed Cephes bug: -assert(gammaIncompleteCompl(384, real.infinity)==0); -assert(gammaIncompleteComplInv(3, 0)==real.infinity); -// Fixed a bug that caused gammaIncompleteCompl to return a wrong value when -// x was larger than a, but not by much, and both were large: -// The value is from WolframAlpha (Gamma[100000, 100001, inf] / Gamma[100000]) -static if (real.mant_dig >= 64) // incl. 80-bit reals - assert(fabs(gammaIncompleteCompl(100000, 100001) - 0.49831792109L) < 0.000000000005L); -else - assert(fabs(gammaIncompleteCompl(100000, 100001) - 0.49831792109L) < 0.00000005L); -} - - -// DAC: These values are Bn / n for n=2,4,6,8,10,12,14. -immutable real [7] Bn_n = [ - 1.0L/(6*2), -1.0L/(30*4), 1.0L/(42*6), -1.0L/(30*8), - 5.0L/(66*10), -691.0L/(2730*12), 7.0L/(6*14) ]; - -/** Digamma function -* -* The digamma function is the logarithmic derivative of the gamma function. -* -* digamma(x) = d/dx logGamma(x) -* -* References: -* 1. Abramowitz, M., and Stegun, I. A. (1970). -* Handbook of mathematical functions. Dover, New York, -* pages 258-259, equations 6.3.6 and 6.3.18. -*/ -real digamma(real x) -{ - // Based on CEPHES, Stephen L. Moshier. - - real p, q, nz, s, w, y, z; - long i, n; - int negative; - - negative = 0; - nz = 0.0; - - if ( x <= 0.0 ) - { - negative = 1; - q = x; - p = floor(q); - if ( p == q ) - { - return real.nan; // singularity. - } - /* Remove the zeros of tan(PI x) - * by subtracting the nearest integer from x - */ - nz = q - p; - if ( nz != 0.5 ) - { - if ( nz > 0.5 ) - { - p += 1.0; - nz = q - p; - } - nz = PI/tan(PI*nz); - } - else - { - nz = 0.0; - } - x = 1.0 - x; - } - - // check for small positive integer - if ((x <= 13.0) && (x == floor(x)) ) - { - y = 0.0; - n = lrint(x); - // DAC: CEPHES bugfix. Cephes did this in reverse order, which - // created a larger roundoff error. - for (i=n-1; i>0; --i) - { - y+=1.0L/i; - } - y -= EULERGAMMA; - goto done; - } - - s = x; - w = 0.0; - while ( s < 10.0 ) - { - w += 1.0/s; - s += 1.0; - } - - if ( s < 1.0e17L ) - { - z = 1.0/(s * s); - y = z * poly(z, Bn_n); - } else - y = 0.0; - - y = log(s) - 0.5L/s - y - w; - -done: - if ( negative ) - { - y -= nz; - } - return y; -} - -@safe unittest -{ - // Exact values - assert(digamma(1.0)== -EULERGAMMA); - assert(feqrel(digamma(0.25), -PI/2 - 3* LN2 - EULERGAMMA) >= real.mant_dig-7); - assert(feqrel(digamma(1.0L/6), -PI/2 *sqrt(3.0L) - 2* LN2 -1.5*log(3.0L) - EULERGAMMA) >= real.mant_dig-7); - assert(digamma(-5.0).isNaN()); - assert(feqrel(digamma(2.5), -EULERGAMMA - 2*LN2 + 2.0 + 2.0L/3) >= real.mant_dig-9); - assert(isIdentical(digamma(NaN(0xABC)), NaN(0xABC))); - - for (int k=1; k<40; ++k) - { - real y=0; - for (int u=k; u >= 1; --u) - { - y += 1.0L/u; - } - assert(feqrel(digamma(k+1.0), -EULERGAMMA + y) >= real.mant_dig-2); - } -} - -/** Log Minus Digamma function -* -* logmdigamma(x) = log(x) - digamma(x) -* -* References: -* 1. Abramowitz, M., and Stegun, I. A. (1970). -* Handbook of mathematical functions. Dover, New York, -* pages 258-259, equations 6.3.6 and 6.3.18. -*/ -real logmdigamma(real x) -{ - if (x <= 0.0) - { - if (x == 0.0) - { - return real.infinity; - } - return real.nan; - } - - real s = x; - real w = 0.0; - while ( s < 10.0 ) - { - w += 1.0/s; - s += 1.0; - } - - real y; - if ( s < 1.0e17L ) - { - immutable real z = 1.0/(s * s); - y = z * poly(z, Bn_n); - } else - y = 0.0; - - return x == s ? y + 0.5L/s : (log(x/s) + 0.5L/s + y + w); -} - -@safe unittest -{ - assert(logmdigamma(-5.0).isNaN()); - assert(isIdentical(logmdigamma(NaN(0xABC)), NaN(0xABC))); - assert(logmdigamma(0.0) == real.infinity); - for (auto x = 0.01; x < 1.0; x += 0.1) - assert(isClose(digamma(x), log(x) - logmdigamma(x))); - for (auto x = 1.0; x < 15.0; x += 1.0) - assert(isClose(digamma(x), log(x) - logmdigamma(x))); -} - -/** Inverse of the Log Minus Digamma function - * - * Returns x such $(D log(x) - digamma(x) == y). - * - * References: - * 1. Abramowitz, M., and Stegun, I. A. (1970). - * Handbook of mathematical functions. Dover, New York, - * pages 258-259, equation 6.3.18. - * - * Authors: Ilya Yaroshenko - */ -real logmdigammaInverse(real y) -{ - import std.numeric : findRoot; - // FIXME: should be returned back to enum. - // Fix requires CTFEable `log` on non-x86 targets (check both LDC and GDC). - immutable maxY = logmdigamma(real.min_normal); - assert(maxY > 0 && maxY <= real.max); - - if (y >= maxY) - { - //lim x->0 (log(x)-digamma(x))*x == 1 - return 1 / y; - } - if (y < 0) - { - return real.nan; - } - if (y < real.min_normal) - { - //6.3.18 - return 0.5 / y; - } - if (y > 0) - { - // x/2 <= logmdigamma(1 / x) <= x, x > 0 - // calls logmdigamma ~6 times - return 1 / findRoot((real x) => logmdigamma(1 / x) - y, y, 2*y); - } - return y; //NaN -} - -@safe unittest -{ - import std.typecons; - //WolframAlpha, 22.02.2015 - immutable Tuple!(real, real)[5] testData = [ - tuple(1.0L, 0.615556766479594378978099158335549201923L), - tuple(1.0L/8, 4.15937801516894947161054974029150730555L), - tuple(1.0L/1024, 512.166612384991507850643277924243523243L), - tuple(0.000500083333325000003968249801594877323784632117L, 1000.0L), - tuple(1017.644138623741168814449776695062817947092468536L, 1.0L/1024), - ]; - foreach (test; testData) - assert(isClose(logmdigammaInverse(test[0]), test[1], 2e-15L)); - - assert(isClose(logmdigamma(logmdigammaInverse(1)), 1, 1e-15L)); - assert(isClose(logmdigamma(logmdigammaInverse(real.min_normal)), real.min_normal, 1e-15L)); - assert(isClose(logmdigamma(logmdigammaInverse(real.max/2)), real.max/2, 1e-15L)); - assert(isClose(logmdigammaInverse(logmdigamma(1)), 1, 1e-15L)); - assert(isClose(logmdigammaInverse(logmdigamma(real.min_normal)), real.min_normal, 1e-15L)); - assert(isClose(logmdigammaInverse(logmdigamma(real.max/2)), real.max/2, 1e-15L)); -} diff --git a/phobos/std/internal/memory.d b/phobos/std/internal/memory.d deleted file mode 100644 index 991cd68..0000000 --- a/phobos/std/internal/memory.d +++ /dev/null @@ -1,58 +0,0 @@ -module std.internal.memory; - -package(std): - -version (D_Exceptions) -{ - import core.exception : onOutOfMemoryError; - private enum allocationFailed = `onOutOfMemoryError();`; -} -else -{ - private enum allocationFailed = `assert(0, "Memory allocation failed");`; -} - -// (below comments are non-DDOC, but are written in similar style) - -/+ -Mnemonic for `enforce!OutOfMemoryError(malloc(size))` that (unlike malloc) -can be considered pure because it causes the program to abort if the result -of the allocation is null, with the consequence that errno will not be -visibly changed by calling this function. Note that `malloc` can also -return `null` in non-failure situations if given an argument of 0. Hence, -it is a programmer error to use this function if the requested allocation -size is logically permitted to be zero. `enforceCalloc` and `enforceRealloc` -work analogously. - -All these functions are usable in `betterC`. -+/ -void* enforceMalloc()(size_t size) @nogc nothrow pure @safe -{ - auto result = fakePureMalloc(size); - if (!result) mixin(allocationFailed); - return result; -} - -// ditto -void* enforceCalloc()(size_t nmemb, size_t size) @nogc nothrow pure @safe -{ - auto result = fakePureCalloc(nmemb, size); - if (!result) mixin(allocationFailed); - return result; -} - -// ditto -void* enforceRealloc()(return scope void* ptr, size_t size) @nogc nothrow pure @system -{ - auto result = fakePureRealloc(ptr, size); - if (!result) mixin(allocationFailed); - return result; -} - -// Purified for local use only. -extern (C) @nogc nothrow pure private -{ - pragma(mangle, "malloc") void* fakePureMalloc(size_t) @safe; - pragma(mangle, "calloc") void* fakePureCalloc(size_t nmemb, size_t size) @safe; - pragma(mangle, "realloc") void* fakePureRealloc(return scope void* ptr, size_t size) @system; -} diff --git a/phobos/std/internal/scopebuffer.d b/phobos/std/internal/scopebuffer.d deleted file mode 100644 index 7c1f8c0..0000000 --- a/phobos/std/internal/scopebuffer.d +++ /dev/null @@ -1,399 +0,0 @@ -/* - * Copyright: 2014 by Digital Mars - * License: $(LINK2 http://boost.org/LICENSE_1_0.txt, Boost License 1.0). - * Authors: Walter Bright - * Source: $(PHOBOSSRC std/internal/scopebuffer.d) - */ - -module std.internal.scopebuffer; - - -//debug=ScopeBuffer; - -import core.stdc.stdlib : realloc; -import std.traits; -import std.internal.attributes : betterC; - -/************************************** - * ScopeBuffer encapsulates using a local array as a temporary buffer. - * It is initialized with a local array that should be large enough for - * most uses. If the need exceeds that size, ScopeBuffer will reallocate - * the data using its `realloc` function. - * - * ScopeBuffer cannot contain more than `(uint.max-16)/2` elements. - * - * ScopeBuffer is an Output Range. - * - * Since ScopeBuffer may store elements of type `T` in `malloc`'d memory, - * those elements are not scanned when the GC collects. This can cause - * memory corruption. Do not use ScopeBuffer when elements of type `T` point - * to the GC heap, except when a `realloc` function is provided which supports this. - * - * Example: ---- -import core.stdc.stdio; -import std.internal.scopebuffer; -void main() -{ - char[2] buf = void; - auto textbuf = ScopeBuffer!char(buf); - scope(exit) textbuf.free(); // necessary for cleanup - - // Put characters and strings into textbuf, verify they got there - textbuf.put('a'); - textbuf.put('x'); - textbuf.put("abc"); - assert(textbuf.length == 5); - assert(textbuf[1 .. 3] == "xa"); - assert(textbuf[3] == 'b'); - - // Can shrink it - textbuf.length = 3; - assert(textbuf[0 .. textbuf.length] == "axa"); - assert(textbuf[textbuf.length - 1] == 'a'); - assert(textbuf[1 .. 3] == "xa"); - - textbuf.put('z'); - assert(textbuf[] == "axaz"); - - // Can shrink it to 0 size, and reuse same memory - textbuf.length = 0; -} ---- - * It is invalid to access ScopeBuffer's contents when ScopeBuffer goes out of scope. - * Hence, copying the contents are necessary to keep them around: ---- -import std.internal.scopebuffer; -string cat(string s1, string s2) -{ - char[10] tmpbuf = void; - auto textbuf = ScopeBuffer!char(tmpbuf); - scope(exit) textbuf.free(); - textbuf.put(s1); - textbuf.put(s2); - textbuf.put("even more"); - return textbuf[].idup; -} ---- - * ScopeBuffer is intended for high performance usages in `@system` and `@trusted` code. - * It is designed to fit into two 64 bit registers, again for high performance use. - * If used incorrectly, memory leaks and corruption can result. Be sure to use - * $(D scope(exit) textbuf.free();) for proper cleanup, and do not refer to a ScopeBuffer - * instance's contents after `ScopeBuffer.free()` has been called. - * - * The `realloc` parameter defaults to C's `realloc()`. Another can be supplied to override it. - * - * ScopeBuffer instances may be copied, as in: ---- -textbuf = doSomething(textbuf, args); ---- - * which can be very efficent, but these must be regarded as a move rather than a copy. - * Additionally, the code between passing and returning the instance must not throw - * exceptions, otherwise when `ScopeBuffer.free()` is called, memory may get corrupted. - */ - -@system -struct ScopeBuffer(T, alias realloc = /*core.stdc.stdlib*/.realloc) -if (isAssignable!T && - !hasElaborateDestructor!T && - !hasElaborateCopyConstructor!T && - !hasElaborateAssign!T) -{ - import core.exception : onOutOfMemoryError; - import core.stdc.string : memcpy; - - - /************************** - * Initialize with buf to use as scratch buffer space. - * Params: - * buf = Scratch buffer space, must have length that is even - * Example: - * --- - * ubyte[10] tmpbuf = void; - * auto sbuf = ScopeBuffer!ubyte(tmpbuf); - * --- - * Note: - * If buf was created by the same `realloc` passed as a parameter - * to `ScopeBuffer`, then the contents of `ScopeBuffer` can be extracted without needing - * to copy them, and `ScopeBuffer.free()` will not need to be called. - */ - this(T[] buf) - in - { - assert(!(buf.length & wasResized)); // assure even length of scratch buffer space - assert(buf.length <= uint.max); // because we cast to uint later - } - do - { - this.buf = buf.ptr; - this.bufLen = cast(uint) buf.length; - } - - @system @betterC unittest - { - ubyte[10] tmpbuf = void; - auto sbuf = ScopeBuffer!ubyte(tmpbuf); - } - - /************************** - * Releases any memory used. - * This will invalidate any references returned by the `[]` operator. - * A destructor is not used, because that would make it not POD - * (Plain Old Data) and it could not be placed in registers. - */ - void free() - { - debug(ScopeBuffer) buf[0 .. bufLen] = 0; - if (bufLen & wasResized) - realloc(buf, 0); - buf = null; - bufLen = 0; - used = 0; - } - - /************************ - * Append element c to the buffer. - * This member function makes `ScopeBuffer` an Output Range. - */ - void put(T c) - { - /* j will get enregistered, while used will not because resize() may change used - */ - const j = used; - if (j == bufLen) - { - assert(j <= (uint.max - 16) / 2); - resize(j * 2 + 16); - } - buf[j] = c; - used = j + 1; - } - - /************************ - * Append array s to the buffer. - * - * If `const(T)` can be converted to `T`, then put will accept - * `const(T)[]` as input. It will accept a `T[]` otherwise. - */ - package alias CT = Select!(is(const(T) : T), const(T), T); - /// ditto - void put(CT[] s) - { - const newlen = used + s.length; - assert((cast(ulong) used + s.length) <= uint.max); - const len = bufLen; - if (newlen > len) - { - assert(len <= uint.max / 2); - resize(newlen <= len * 2 ? len * 2 : newlen); - } - buf[used .. newlen] = s[]; - used = cast(uint) newlen; - } - - /****** - * Returns: - * A slice into the temporary buffer. - * Warning: - * The result is only valid until the next `put()` or `ScopeBuffer` goes out of scope. - */ - @system inout(T)[] opSlice(size_t lower, size_t upper) inout - in - { - assert(lower <= bufLen); - assert(upper <= bufLen); - assert(lower <= upper); - } - do - { - return buf[lower .. upper]; - } - - /// ditto - @system inout(T)[] opSlice() inout - { - assert(used <= bufLen); - return buf[0 .. used]; - } - - /******* - * Returns: - * The element at index i. - */ - ref inout(T) opIndex(size_t i) inout - { - assert(i < bufLen); - return buf[i]; - } - - /*** - * Returns: - * The number of elements in the `ScopeBuffer`. - */ - @property size_t length() const - { - return used; - } - - /*** - * Used to shrink the length of the buffer, - * typically to `0` so the buffer can be reused. - * Cannot be used to extend the length of the buffer. - */ - @property void length(size_t i) - in - { - assert(i <= this.used); - } - do - { - this.used = cast(uint) i; - } - - alias opDollar = length; - - private: - T* buf; - // Using uint instead of size_t so the struct fits in 2 registers in 64 bit code - uint bufLen; - enum wasResized = 1; // this bit is set in bufLen if we control the memory - uint used; - - void resize(size_t newsize) - in - { - assert(newsize <= uint.max); - } - do - { - //writefln("%s: oldsize %s newsize %s", id, buf.length, newsize); - newsize |= wasResized; - void *newBuf = realloc((bufLen & wasResized) ? buf : null, newsize * T.sizeof); - if (!newBuf) - onOutOfMemoryError(); - if (!(bufLen & wasResized)) - { - memcpy(newBuf, buf, used * T.sizeof); - debug(ScopeBuffer) buf[0 .. bufLen] = 0; - } - buf = cast(T*) newBuf; - bufLen = cast(uint) newsize; - - /* This function is called only rarely, - * inlining results in poorer register allocation. - */ - version (DigitalMars) - /* With dmd, a fake loop will prevent inlining. - * Using a hack until a language enhancement is implemented. - */ - while (1) { break; } - } -} - -@system @betterC unittest -{ - import core.stdc.stdio; - import std.range; - - char[2] tmpbuf = void; - { - // Exercise all the lines of code except for assert(0)'s - auto textbuf = ScopeBuffer!char(tmpbuf); - scope(exit) textbuf.free(); - - static assert(isOutputRange!(ScopeBuffer!char, char)); - - textbuf.put('a'); - textbuf.put('x'); - textbuf.put("abc"); // tickle put([])'s resize - assert(textbuf.length == 5); - assert(textbuf[1 .. 3] == "xa"); - assert(textbuf[3] == 'b'); - - textbuf.length = textbuf.length - 1; - assert(textbuf[0 .. textbuf.length] == "axab"); - - textbuf.length = 3; - assert(textbuf[0 .. textbuf.length] == "axa"); - assert(textbuf[textbuf.length - 1] == 'a'); - assert(textbuf[1 .. 3] == "xa"); - - textbuf.put(cast(dchar)'z'); - assert(textbuf[] == "axaz"); - - textbuf.length = 0; // reset for reuse - assert(textbuf.length == 0); - - foreach (char c; "asdf;lasdlfaklsdjfalksdjfa;lksdjflkajsfdasdfkja;sdlfj") - { - textbuf.put(c); // tickle put(c)'s resize - } - assert(textbuf[] == "asdf;lasdlfaklsdjfalksdjfa;lksdjflkajsfdasdfkja;sdlfj"); - } // run destructor on textbuf here - -} - -@system unittest -{ - string cat(string s1, string s2) - { - char[10] tmpbuf = void; - auto textbuf = ScopeBuffer!char(tmpbuf); - scope(exit) textbuf.free(); - textbuf.put(s1); - textbuf.put(s2); - textbuf.put("even more"); - return textbuf[].idup; - } - - auto s = cat("hello", "betty"); - assert(s == "hellobettyeven more"); -} - -// const -@system @betterC unittest -{ - char[10] tmpbuf = void; - auto textbuf = ScopeBuffer!char(tmpbuf); - scope(exit) textbuf.free(); - foreach (i; 0 .. 10) textbuf.put('w'); - const csb = textbuf; - const elem = csb[3]; - const slice0 = csb[0 .. 5]; - const slice1 = csb[]; -} - -/********************************* - * Creates a `ScopeBuffer` instance using type deduction - see - * $(LREF .ScopeBuffer.this) for details. - * Params: - * tmpbuf = the initial buffer to use - * Returns: - * An instance of `ScopeBuffer`. - */ - -auto scopeBuffer(T)(T[] tmpbuf) -{ - return ScopeBuffer!T(tmpbuf); -} - -/// -@system @betterC unittest -{ - ubyte[10] tmpbuf = void; - auto sb = scopeBuffer(tmpbuf); - scope(exit) sb.free(); -} - -@system @betterC unittest -{ - ScopeBuffer!(int*) b; - int*[] s; - b.put(s); - - ScopeBuffer!char c; - string s1; - char[] s2; - c.put(s1); - c.put(s2); -} diff --git a/phobos/std/internal/test/dummyrange.d b/phobos/std/internal/test/dummyrange.d deleted file mode 100644 index e07e275..0000000 --- a/phobos/std/internal/test/dummyrange.d +++ /dev/null @@ -1,562 +0,0 @@ -/** -For testing only. -Used with the dummy ranges for testing higher order ranges. -*/ -module std.internal.test.dummyrange; - -import std.meta; -import std.range.primitives; -import std.typecons; - -enum RangeType -{ - Input, - Forward, - Bidirectional, - Random -} - -enum Length -{ - Yes, - No -} - -enum ReturnBy -{ - Reference, - Value -} - -import std.traits : isArray; - -// Range that's useful for testing other higher order ranges, -// can be parametrized with attributes. It just dumbs down an array of -// numbers 1 .. 10. -struct DummyRange(ReturnBy _r, Length _l, RangeType _rt, T = uint[]) -if (isArray!T) -{ - private static immutable uinttestData = - [1U, 2U, 3U, 4U, 5U, 6U, 7U, 8U, 9U, 10U]; - // These enums are so that the template params are visible outside - // this instantiation. - enum r = _r; - enum l = _l; - enum rt = _rt; - - static if (is(T == uint[])) - { - T arr = uinttestData; - } - else - { - T arr; - } - - alias RetType = ElementType!(T); - alias RetTypeNoAutoDecoding = ElementEncodingType!(T); - - void reinit() - { - // Workaround for DMD bug 4378 - static if (is(T == uint[])) - { - arr = uinttestData.dup; - } - } - - void popFront() - { - arr = arr[1..$]; - } - - @property bool empty() const - { - return arr.length == 0; - } - - static if (r == ReturnBy.Reference) - { - @property ref inout(RetType) front() inout - { - return arr[0]; - } - } - else - { - @property RetType front() const - { - return arr[0]; - } - - @property void front(RetTypeNoAutoDecoding val) - { - arr[0] = val; - } - } - - static if (rt >= RangeType.Forward) - { - @property typeof(this) save() - { - return this; - } - } - - static if (rt >= RangeType.Bidirectional) - { - void popBack() - { - arr = arr[0..$ - 1]; - } - - static if (r == ReturnBy.Reference) - { - @property ref inout(RetType) back() inout - { - return arr[$ - 1]; - } - } - else - { - @property RetType back() const - { - return arr[$ - 1]; - } - - @property void back(RetTypeNoAutoDecoding val) - { - arr[$ - 1] = val; - } - } - } - - static if (rt >= RangeType.Random) - { - static if (r == ReturnBy.Reference) - { - ref inout(RetType) opIndex(size_t index) inout - { - return arr[index]; - } - } - else - { - RetType opIndex(size_t index) const - { - return arr[index]; - } - - RetType opIndexAssign(RetTypeNoAutoDecoding val, size_t index) - { - return arr[index] = val; - } - - RetType opIndexOpAssign(string op)(RetTypeNoAutoDecoding value, size_t index) - { - mixin("return arr[index] " ~ op ~ "= value;"); - } - - RetType opIndexUnary(string op)(size_t index) - { - mixin("return " ~ op ~ "arr[index];"); - } - } - - typeof(this) opSlice(size_t lower, size_t upper) - { - auto ret = this; - ret.arr = arr[lower .. upper]; - return ret; - } - - typeof(this) opSlice() - { - return this; - } - } - - static if (l == Length.Yes) - { - @property size_t length() const - { - return arr.length; - } - - alias opDollar = length; - } -} - -enum dummyLength = 10; - -alias AllDummyRanges = AliasSeq!( - DummyRange!(ReturnBy.Reference, Length.Yes, RangeType.Forward), - DummyRange!(ReturnBy.Reference, Length.Yes, RangeType.Bidirectional), - DummyRange!(ReturnBy.Reference, Length.Yes, RangeType.Random), - DummyRange!(ReturnBy.Reference, Length.No, RangeType.Forward), - DummyRange!(ReturnBy.Reference, Length.No, RangeType.Bidirectional), - DummyRange!(ReturnBy.Value, Length.Yes, RangeType.Input), - DummyRange!(ReturnBy.Value, Length.Yes, RangeType.Forward), - DummyRange!(ReturnBy.Value, Length.Yes, RangeType.Bidirectional), - DummyRange!(ReturnBy.Value, Length.Yes, RangeType.Random), - DummyRange!(ReturnBy.Value, Length.No, RangeType.Input), - DummyRange!(ReturnBy.Value, Length.No, RangeType.Forward), - DummyRange!(ReturnBy.Value, Length.No, RangeType.Bidirectional) -); - -template AllDummyRangesType(T) -{ - alias AllDummyRangesType = AliasSeq!( - DummyRange!(ReturnBy.Reference, Length.Yes, RangeType.Forward, T), - DummyRange!(ReturnBy.Reference, Length.Yes, RangeType.Bidirectional, T), - DummyRange!(ReturnBy.Reference, Length.Yes, RangeType.Random, T), - DummyRange!(ReturnBy.Reference, Length.No, RangeType.Forward, T), - DummyRange!(ReturnBy.Reference, Length.No, RangeType.Bidirectional, T), - DummyRange!(ReturnBy.Value, Length.Yes, RangeType.Input, T), - DummyRange!(ReturnBy.Value, Length.Yes, RangeType.Forward, T), - DummyRange!(ReturnBy.Value, Length.Yes, RangeType.Bidirectional, T), - DummyRange!(ReturnBy.Value, Length.Yes, RangeType.Random, T), - DummyRange!(ReturnBy.Value, Length.No, RangeType.Input, T), - DummyRange!(ReturnBy.Value, Length.No, RangeType.Forward, T), - DummyRange!(ReturnBy.Value, Length.No, RangeType.Bidirectional, T) - ); -} - -/** -Tests whether forward, bidirectional and random access properties are -propagated properly from the base range(s) R to the higher order range -H. Useful in combination with DummyRange for testing several higher -order ranges. -*/ -template propagatesRangeType(H, R...) -{ - static if (allSatisfy!(isRandomAccessRange, R)) - enum bool propagatesRangeType = isRandomAccessRange!H; - else static if (allSatisfy!(isBidirectionalRange, R)) - enum bool propagatesRangeType = isBidirectionalRange!H; - else static if (allSatisfy!(isForwardRange, R)) - enum bool propagatesRangeType = isForwardRange!H; - else - enum bool propagatesRangeType = isInputRange!H; -} - -template propagatesLength(H, R...) -{ - static if (allSatisfy!(hasLength, R)) - enum bool propagatesLength = hasLength!H; - else - enum bool propagatesLength = !hasLength!H; -} - -/** -Reference type input range -*/ -class ReferenceInputRange(T) -{ - import std.array : array; - - this(Range)(Range r) if (isInputRange!Range) {_payload = array(r);} - final @property ref T front(){return _payload.front;} - final void popFront(){_payload.popFront();} - final @property bool empty(){return _payload.empty;} - protected T[] _payload; -} - -/** -Infinite input range -*/ -class ReferenceInfiniteInputRange(T) -{ - this(T first = T.init) {_val = first;} - final @property T front(){return _val;} - final void popFront(){++_val;} - enum bool empty = false; - protected T _val; -} - -/** -Reference forward range -*/ -class ReferenceForwardRange(T) : ReferenceInputRange!T -{ - this(Range)(Range r) if (isInputRange!Range) {super(r);} - final @property auto save(this This)() {return new This( _payload);} -} - -/** -Infinite forward range -*/ -class ReferenceInfiniteForwardRange(T) : ReferenceInfiniteInputRange!T -{ - this(T first = T.init) {super(first);} - final @property ReferenceInfiniteForwardRange save() - {return new ReferenceInfiniteForwardRange!T(_val);} -} - -/** -Reference bidirectional range -*/ -class ReferenceBidirectionalRange(T) : ReferenceForwardRange!T -{ - this(Range)(Range r) if (isInputRange!Range) {super(r);} - final @property ref T back(){return _payload.back;} - final void popBack(){_payload.popBack();} -} - -@safe unittest -{ - static assert(isInputRange!(ReferenceInputRange!int)); - static assert(isInputRange!(ReferenceInfiniteInputRange!int)); - - static assert(isForwardRange!(ReferenceForwardRange!int)); - static assert(isForwardRange!(ReferenceInfiniteForwardRange!int)); - - static assert(isBidirectionalRange!(ReferenceBidirectionalRange!int)); -} - -private: - -pure struct Cmp(T) -if (is(T == uint)) -{ - static auto iota(size_t low = 1, size_t high = 11) - { - import std.range : iota; - return iota(cast(uint) low, cast(uint) high); - } - - static void initialize(ref uint[] arr) - { - import std.array : array; - arr = iota().array; - } - - static bool function(uint,uint) cmp = function(uint a, uint b) { return a == b; }; - - enum dummyValue = 1337U; - enum dummyValueRslt = 1337U * 2; -} - -pure struct Cmp(T) -if (is(T == double)) -{ - import std.math.operations : isClose; - - static auto iota(size_t low = 1, size_t high = 11) - { - import std.range : iota; - return iota(cast(double) low, cast(double) high, 1.0); - } - - static void initialize(ref double[] arr) - { - import std.array : array; - arr = iota().array; - } - - alias cmp = isClose!(double,double,double); - - enum dummyValue = 1337.0; - enum dummyValueRslt = 1337.0 * 2.0; -} - -struct TestFoo -{ - int a; - - bool opEquals(const ref TestFoo other) const - { - return this.a == other.a; - } - - TestFoo opBinary(string op)(TestFoo other) - { - TestFoo ret = this; - mixin("ret.a " ~ op ~ "= other.a;"); - return ret; - } - - TestFoo opOpAssign(string op)(TestFoo other) - { - mixin("this.a " ~ op ~ "= other.a;"); - return this; - } -} - -pure struct Cmp(T) -if (is(T == TestFoo)) -{ - static auto iota(size_t low = 1, size_t high = 11) - { - import std.algorithm.iteration : map; - import std.range : iota; - return iota(cast(int) low, cast(int) high).map!(a => TestFoo(a)); - } - - static void initialize(ref TestFoo[] arr) - { - import std.array : array; - arr = iota().array; - } - - static bool function(TestFoo,TestFoo) cmp = function(TestFoo a, TestFoo b) - { - return a.a == b.a; - }; - - @property static TestFoo dummyValue() - { - return TestFoo(1337); - } - - @property static TestFoo dummyValueRslt() - { - return TestFoo(1337 * 2); - } -} - -@system unittest -{ - import std.algorithm.comparison : equal; - import std.range : iota, retro, repeat; - - static void testInputRange(T,Cmp)() - { - T it; - Cmp.initialize(it.arr); - for (size_t numRuns = 0; numRuns < 2; ++numRuns) - { - if (numRuns == 1) - { - static if (is(immutable ElementType!(T) == immutable uint)) - { - it.reinit(); - } - - Cmp.initialize(it.arr); - } - - assert(equal!(Cmp.cmp)(it, Cmp.iota(1, 11))); - - static if (hasLength!T) - { - assert(it.length == 10); - } - - assert(!Cmp.cmp(it.front, Cmp.dummyValue)); - auto s = it.front; - it.front = Cmp.dummyValue; - assert(Cmp.cmp(it.front, Cmp.dummyValue)); - it.front = s; - - auto cmp = Cmp.iota(1,11); - - size_t jdx = 0; - while (!it.empty && !cmp.empty) - { - static if (hasLength!T) - { - assert(it.length == 10 - jdx); - } - - assert(Cmp.cmp(it.front, cmp.front)); - it.popFront(); - cmp.popFront(); - - ++jdx; - } - - assert(it.empty); - assert(cmp.empty); - } - - } - - static void testForwardRange(T,Cmp)() - { - T it; - Cmp.initialize(it.arr); - auto s = it.save(); - s.popFront(); - assert(!Cmp.cmp(s.front, it.front)); - } - - static void testBidirectionalRange(T,Cmp)() - { - T it; - Cmp.initialize(it.arr); - assert(equal!(Cmp.cmp)(it.retro, Cmp.iota().retro)); - - auto s = it.back; - assert(!Cmp.cmp(s, Cmp.dummyValue)); - it.back = Cmp.dummyValue; - assert( Cmp.cmp(it.back, Cmp.dummyValue)); - it.back = s; - } - - static void testRandomAccessRange(T,Cmp)() - { - T it; - Cmp.initialize(it.arr); - size_t idx = 0; - foreach (jt; it) - { - assert(it[idx] == jt); - - T copy = it[idx .. $]; - auto cmp = Cmp.iota(idx + 1, it.length + 1); - assert(equal!(Cmp.cmp)(copy, cmp)); - - ++idx; - } - - { - auto copy = it; - copy.arr = it.arr.dup; - for (size_t i = 0; i < copy.length; ++i) - { - copy[i] = Cmp.dummyValue; - copy[i] += Cmp.dummyValue; - } - assert(equal!(Cmp.cmp)(copy, Cmp.dummyValueRslt.repeat(copy.length))); - } - - static if (it.r == ReturnBy.Reference) - { - T copy; - copy.arr = it.arr.dup; - for (size_t i = 0; i < copy.length; ++i) - { - copy[i] = Cmp.dummyValue; - copy[i] += Cmp.dummyValue; - } - - assert(equal!(Cmp.cmp)(copy, Cmp.dummyValueRslt.repeat(copy.length))); - } - } - - import std.meta : AliasSeq; - - static foreach (S; AliasSeq!(uint, double, TestFoo)) - { - foreach (T; AllDummyRangesType!(S[])) - { - testInputRange!(T,Cmp!S)(); - - static if (isForwardRange!T) - { - testForwardRange!(T,Cmp!S)(); - } - - static if (isBidirectionalRange!T) - { - testBidirectionalRange!(T,Cmp!S)(); - } - - static if (isRandomAccessRange!T) - { - testRandomAccessRange!(T,Cmp!S)(); - } - } - } -} diff --git a/phobos/std/internal/test/range.d b/phobos/std/internal/test/range.d deleted file mode 100644 index 4a5ff72..0000000 --- a/phobos/std/internal/test/range.d +++ /dev/null @@ -1,116 +0,0 @@ -/** -For testing only. -Contains tests related to member privacy that cannot be verified inside -std.range itself. -*/ -module std.internal.test.range; - -// Note: currently can't be @safe because RefCounted, which is used by chunks, -// isn't. -@system /*@safe*/ unittest -{ - import std.algorithm.comparison : equal; - import std.range : chunks; - - struct R - { - int state = 0; - @property bool empty() { return state >= 5; } - @property int front() { return state; } - void popFront() { state++; } - } - - auto r = R().chunks(3); - assert(r.equal!equal([[ 0, 1, 2 ], [ 3, 4 ]])); -} - -// https://issues.dlang.org/show_bug.cgi?id=24415 -@safe unittest -{ - import std.range : only; - - static struct S(T) - { - T i; - - this(ref return scope inout(S) rhs) scope @safe inout pure nothrow - { - i = rhs.i; - } - } - { - auto a = only(S!int(42)); - auto b = a; - assert(!b.empty); - assert(b.front == S!int(42)); - - a.popFront(); - auto c = a; - assert(c.empty); - } - { - auto a = only(S!(const int)(42)); - auto b = a; - assert(!b.empty); - assert(b.front == S!(const int)(42)); - - a.popFront(); - auto c = a; - assert(c.empty); - } - { - auto a = only(S!(immutable int)(42)); - auto b = a; - assert(!b.empty); - assert(b.front == S!(immutable int)(42)); - - a.popFront(); - auto c = a; - assert(c.empty); - } - { - auto a = only(S!int(42), S!int(192)); - auto b = a; - assert(!b.empty); - assert(b.front == S!int(42)); - - a.popFront(); - auto c = a; - assert(!c.empty); - assert(c.front == S!int(192)); - - a.popFront(); - auto d = a; - assert(d.empty); - } - { - auto a = only(S!(const int)(42), S!(const int)(192)); - auto b = a; - assert(!b.empty); - assert(b.front == S!(const int)(42)); - - a.popFront(); - auto c = a; - assert(!c.empty); - assert(c.front == S!(const int)(192)); - - a.popFront(); - auto d = a; - assert(d.empty); - } - { - auto a = only(S!(immutable int)(42), S!(immutable int)(192)); - auto b = a; - assert(!b.empty); - assert(b.front == S!(immutable int)(42)); - - a.popFront(); - auto c = a; - assert(!c.empty); - assert(c.front == S!(immutable int)(192)); - - a.popFront(); - auto d = a; - assert(d.empty); - } -} diff --git a/phobos/std/internal/test/uda.d b/phobos/std/internal/test/uda.d deleted file mode 100644 index 88e3a1b..0000000 --- a/phobos/std/internal/test/uda.d +++ /dev/null @@ -1,16 +0,0 @@ -/** -For testing only. -Provides a struct with UDA's defined in an external module. -Useful for validating behavior with member privacy. -*/ -module std.internal.test.uda; - -enum Attr; - -struct HasPrivateMembers -{ - @Attr int a; - int b; - @Attr private int c; - private int d; -} diff --git a/phobos/std/internal/unicode_comp.d b/phobos/std/internal/unicode_comp.d deleted file mode 100644 index 28b2e0d..0000000 --- a/phobos/std/internal/unicode_comp.d +++ /dev/null @@ -1,1829 +0,0 @@ -module std.internal.unicode_comp; -import std.internal.unicode_tables; - -@safe pure nothrow @nogc package(std): - - -static if (size_t.sizeof == 4) -{ -//10144 bytes -enum combiningClassTrieEntries = TrieEntry!(ubyte, 8, 8, 5)(x" -0000000000000040000005C0", -x" -0000010000000B00000010A0", -x" -020201000402030206020205090807020000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000002000100040003000000000000000000000000000000000000000500000000 -0000000000000000000700060000000800000009000B000A00000000000D000C000F000E000000100000000000110000 -00130012000000140000001500170016001800000000001900180000001B001A001800000000001A001800000000001A -001800000000001A000000000000001A001800000000001C001800000000001A001D00000000001A000000000000001E -001F00000000002000210000000000220024002300250000000000260000002700280000000000000000002900000000 -0000000000000000000000000000000000000000000000000000000000000000000000000000002A0000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -002C002B00000000000000000000002D0000000000000000002E000000000000002F0000000000000000000000000000 -0000003000310000003200000000003300340000003600350037000000380000003900000000000000000000003B003A -000000000000000000000000003D003C0000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000003F003E0000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000004000000000000000410000000000000042000000000000000000000000000000000000 -000000000000000000000000000000000043000000000000000000440000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000450000000000460047000000000000000000000000000000000000 -004900480000000000000000004A0035004B00000000004C004D00000000004E0000000000000000004F000000510050 -000000000000000000000000001A00000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000520000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -005300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000005400000000000000000000000000000055000000000000005600000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -005800570000000000000000005900000000000000000000000000000000000000000000000000000000000000000000 -005A00000000000000000000000000000000000000000000005B0000005C0000000000000000005D0000005E00000000 -00000000005F0048006000000000000000620061004D0000000000000000006300640000000000000000000000650000 -006600000067001A0000000000000000000000000000006800000000000000690000000000000000004100000000006A -0041000000000000006B000000000000006C000000000000000000000000000000600000000000000000000000000000 -006D00000000006E00000000004E0000002C00000000006F000000700000000000000000000000000000000000000000 -004100000000000000000000000000000000000000000071000000720000000000000000000000000000000000000000 -000000000000007300000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000740000 -007500000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000076000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000770000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000780000007A007900000000000000000000007B0000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -00000000000000000000000000000000007D007C000000000000007E0000000000750000000000000000000000000000 -0000000000000000007F0000008000000000000000000000000000000000000000000000000000000000000000810000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000820000000000000083000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -00000000000000000000000000000000E6E6E6E6E6E6E6E6E6E6E6E6E6E6E6E6E6E6E6E6DCDCE8E6D8E8DCDCDCDCDCDC -DCCACADCCADCDCDCDCDCDCCADCDCDCDCDCDCDCDC01010101DCDCDC01E6E6E6DCE6E6E6E6DCE6F0E6E6E6DCDC00DCDCE6 -DCE6E6E6E6DCDCDCE6DCDCE8E9EAEAE9E6E9EAEAE6E6E6E6E6E6E6E6E6E6E6E600000000000000000000000000000000 -E6000000E6E6E6E600000000000000000000000000000000000000000000000000000000000000000000000000000000 -E6E6DC00E6DCE6E6DCDEE6E6E6E6E6E6DCDCE6E6DCDCDCDCE6DCE6E6E6E4DEE60D0C0B0A11100F0E1413131217001615 -001918001200DCE600000000000000000000000000000000000000000000000000000000000000000000000000000000 -E6E6E6E6E6E6E6E600201F1E0000000000000000000000001B0000001F1E1D1CE6222120E6DCDCE6E6E6E6E6DCE6E6DC -000000000000000000000000000000000000002300000000000000000000000000000000000000000000000000000000 -00000000E6E60000E6E6E6E6E60000E6DCE6E6E6E60000E6E6DC00E60000DCE600000000000000000000000000000000 -000000000000000000000000000000000000240000000000000000000000000000000000000000000000000000000000 -E6E6DCE6DCE6E6DCDCE6DCDCE6DCE6DCE6DCE6E6E6DCE6DC00E6E6DC0000000000000000000000000000000000000000 -0000000000000000E6000000E6E6E6E6E6DCE6E600000000000000000000DC0000000000000000000000000000000000 -00000000E6E60000E600E6E6E6E6E6E6E6E6E6E6E6E6E600E6E6E6000000E6E600000000000000000000000000000000 -000000000000000000000000000000000000000000000000DCDCDC000000000000000000000000000000000000000000 -0000000000000000DCDCDCE6E6E6E6E60000000000000000E6E60000DCE6E6E6DCDCDCDCE6E6E6E6E6E6E6E6E6E6E6E6 -DC00E6E6E6DCE6E6E6E6DCE6DCDCDCE6E61D1C1BE6DCE6E6E6DCDCE6E6E6E6E600000000000000000000000000000000 -0000000000000000000000000000000700000000000000000000000000000900E6DCE600000000E60000000000000000 -000000000000000000000000000009000000000000000000000000000000000000000000000000000000000000000000 -00000000000000000000000000E600000000000000000000000000000000090000000000005B54000000000000000000 -000000000000000000000000000000000000000000000000090000000000000900000000000000000009000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000009676700000000 -00000000000000006B6B6B6B000000000000000000000000000000000000000000000000000000000000000000000000 -0000000000000000000976760000000000000000000000007A7A7A7A0000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000DCDC0000000000000000000000000000000000000000 -00000000DC00DC000000D800000000000000000000000000000000000000000000828100000000848282000000008282 -E6E60082E6E600090000000000000000000000000000000000000000000000000000000000DC00000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000070000000009090000000000 -0000000000000000000000000000DC000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000E6E6E6000000000000000000000000000000000000000000000009090000000000000000 -000000000000000000000000000000000000000000000009000000000000000000000000000000000000000000000000 -0009000000000000000000000000E60000000000000000000000E4000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000DCE6DE000000000000000000000000000000000000000000 -00000000E6000000000000DC000000000000000900000000000000000000000000000000E6E6E600E6E6E6E6DC0000E6 -00000000000000000000000000000000E6E6E6E6DCDCDCE6E6DCDCDCDC00DCE6DCE6E6DCE6E6E6DCE6DCE6E600E6E6E6 -000000000000000000000000000000000000000000000000000000000000000000000000000000070000000000000000 -00000000000000090000000000000000000000000000000000000000000000000000000000000000E6000000E6E6E6DC -E6E6E6E60000000000000000000000000000000000000000090900000000000000000000000000000000000000000000 -000000000007000000000000000000000909000000000000000000000000000000000000000000000000000000000000 -000000000700000000000000000000000000000000000000000000000000000000E6E6E6DCDCDC01E6E6DCDCDCDCDCDC -010100E601010101000000010000DC0000000000000000E60000E6E600000000E6DCE6E6E6E6E6E6E6DCE6E6DCD6EAE6 -E6E6E6CAE6E6E6E6E6E6E6E6E6E6E6E6E6E6E6E6E6E6E6E6E6E6E6E6E6E6E6E6E6E6E6E6E4E8E6E6E6DADCE4DCE6DCE9 -000000000000000000000000000000000101E6E6E6E6E6E6E6010101000000E60000E600E60101000101E6DCDCDCDCDC -000000E6000000000000000000000000000000000000000000000000E60000000000E6E6000000000000000000000000 -0000000000000000000000000000000000000000000000000000000009000000E6E6E6E6E6E6E6E6E6E6E6E6E6E6E6E6 -E6E6E6E6E6E6E6E6E6E6E6E6E6E6E6E60000000000000000E4DA0000E0E0DEE800000000000000000000000000000000 -0000000000000000000000000000000000000000000000000008080000000000000000000000000000000000E6000000 -00000000E6E6E6E6E6E6E6E60000E6E600000000000000000000000000000000000000000000000000000000E6E60000 -000000000000000000000000000000000000E6E600000000000000000000000000000000000900000000000000000000 -000000000000000000000000000000000000000000000000000000000000000900000000000000000000000000000000 -E6E6E6E6E6E6E6E6E6E6E6E6E6E6E6E60000E6E60000000000000000000000000000000000000000DC0000000000DCDC -000000000000000000000000000000000000000000000000000000000000000009000000000000000000000000000000 -000000000000000000000000000000000700000000000000000000000000000000000009000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000E6E600E6E60000DC000000E6E6E60000 -0000E6000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -0000000000090000000000000000000000000000000000000000000000000000000000000000000000000000001A0000 -E6E6E6E6DCE6E6E6DCDCDCDCE6E6DCDC0000000000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000DC00000000DC00000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000E6E6000000E6E6E600000000000000000000000000000000E600DC00 -0000000000000000000000000000000000000000000000000000000000000000000000000000000000DC01E609000000 -0000000000DCE60000000000000000000000000000000000000000000000000000000000E6E6E6E60000000000000000 -000000000000000000000000000000000000000000000000E6000000000000E600000000000000000000000000000000 -00000000000000000000000000000000000000000000000000000000DCDCDC0000000000DCDC0000DCE6E6E6DCDCDCE6 -000000DC000000000000000000000000DCE600000000DCE6000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000900000000000000000900000000000000000000000000000000000000 -0000000000000000000709000000000000E6E6E600000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000900000000000009000000000000000000000009000000000007000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000709000000000000000000 -000000000000000000090700000000000000000000000000000000000000000000000000000000000000000000000000 -0000000000000000070000000000000700000000E6E60000E6E6E6E6000000E6E6E6E6E6000000E60000000000000000 -0009000000070000000000000000000000000000000000000000000000E6000007090000000000000000000000000000 -000000000000000000000000000000000000000700000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000007090000000000000000000000000000000000000900000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000090900 -070000000000000000000000000000000000000000000000000000000000000000000000090000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000090000000000 -000700000000090900000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000900000000000000000000000009090000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000101010100000001000000000000000000000000000000000000000000000000 -E6E6E6E600E6E6E600000000000000000000000000000000000000000000000000000606000000000000000000000000 -00000000000000000000000000000000000000000000000000000000000100000000000001D8D80000000101D8D8E200 -00D8D8D800000000DC000000DCDCDCDC00DCDCDCE6E6E600DCDCE6E60000000000000000000000000000000000000000 -0000000000000000E6E600000000E6E600000000000000000000000000000000E6E60000000000E60000000000000000 -00000000000000000000000000000000E6E6E6E600E6E6E6E6E6E6E6E6E6E6E6E6E6E6E6E6E6E6E6E60000E6E6E6E6E6 -E600E6E6E6E600E600E6E6E60000000000000000000000000000000000000000000000000000000000000000E6000000 -0000000000000000000000000000000000000000000000000000000000E6000000000000000000000000000000000000 -000000000000000000000000E6E6E6E600000000000000000000000000000000000000000000000000000000E6DCE8E8 -0000000000000000000000000000000000000000000000000000000000000000DCDCDCDC00DCDCDC0000000000000000 -00000000E6E6E6E60007E6E6000000000000000000000000000000000000000000000000000000000000000000000000 -00000000000000000000000000000000", -); -enum composeIdxMask = (1 << 11) - 1, composeCntShift = 11; -enum compositionJumpTrieEntries = TrieEntry!(ushort, 12, 9)(x" -0000000000000800", -x" -0000100000002600", -x" -00010000000300020005000400070006000700080007000700090007000A0007000C000B000700070007000700070007 -0007000D0007000700070007000700070007000700070007000700070007000700070007000700070007000700070007 -000700070007000700070007000700070007000700070007000700070007000700070007000700070007000700070007 -000700070007000700070007000700070007000700070007000700070007000700070007000700070007000700070007 -000700070007000700070007000700070007000700070007000700070007000700070007000700070007000700070007 -0007000700070007000700070007000700070007000700070007000700070007000F000E000700100007001100070007 -000700070007000700070007000700070007000700070007000700070007000700070007000700070007000700070007 -000700070007000700070007000700070007000700070007000700070007000700070007000700070007000700070007 -000700070007000700070007000700070007000700070007000700070007000700070007000700070007000700070007 -000700070007000700070007000700070007000700070007000700070007000700070007000700070007000700070007 -000700070007000700070007000700070007000700070007000700070007000700070007000700070007000700070007 -000700070007000700070007000700070007000700070007000700070007000700070007000700070007000700070007 -000700070007000700070007000700070007000700070007000700070007000700070007000700070007000700070007 -000700070007000700070007000700070007000700070007000700070007000700070007000700070007000700070007 -000700070007000700070007000700070007000700070007000700070007000700070007000700070007000700070007 -000700070007000700070007000700070007000700070007000700070007000700070007000700070007000700070007 -000700070007000700070007000700070007000700070007000700070007000700070007000700070007000700070007 -000700070007000700070007000700070007000700070007000700070007000700070007000700070007000700070007 -000700070007000700070007000700070007000700070007000700070007000700070007000700070007000700070007 -000700070007000700070007000700070007000700070007000700070007000700070007000700070007000700070007 -000700070007000700070007000700070007000700070007000700070007000700070007000700070007000700070007 -000700070007000700070007000700070007000700070007000700070007000700070007000700070007000700070007 -000700070007000700070007000700070007000700070007000700070007000700070007000700070007000700070007 -000700070007000700070007000700070007000700070007000700070007000700070007000700070007000700070007 -000700070007000700070007000700070007000700070007000700070007000700070007000700070007000700070007 -000700070007000700070007000700070007000700070007000700070007000700070007000700070007000700070007 -000700070007000700070007000700070007000700070007000700070007000700070007000700070007000700070007 -000700070007000700070007000700070007000700070007000700070007000700070007000700070007000700070007 -000700070007000700070007000700070007000700070007000700070007000700070007000700070007000700070007 -000700070007000700070007000700070007000700070007000700070007000700070007000700070007000700070007 -000700070007000700070007000700070007000700070007000700070007000700070007000700070007000700070007 -000700070007000700070007000700070007000700070007000700070007000700070007000700070007000700070007 -000700070007000700070007000700070007000700070007000700070007000700070007000700070007000700070007 -000700070007000700070007000700070007000700070007000700070007000700070007000700070007000700070007 -000700070007000700070007000700070007000700070007000700070007000700070007000700070007000700070007 -000700070007000700070007000700070007000700070007000700070007000700070007000700070007000700070007 -000700070007000700070007000700070007000700070007000700070007000700070007000700070007000700070007 -000700070007000700070007000700070007000700070007000700070007000700070007000700070007000700070007 -000700070007000700070007000700070007000700070007000700070007000700070007000700070007000700070007 -000700070007000700070007000700070007000700070007000700070007000700070007000700070007000700070007 -000700070007000700070007000700070007000700070007000700070007000700070007000700070007000700070007 -000700070007000700070007000700070007000700070007000700070007000700070007000700070007000700070007 -000700070007000700070007000700070007000700070007000700070007000700070007000700070007000700070007 -000700070007000700070007000700070007000700070007000700070007000700070007000700070007000700070007 -000700070007000700070007000700070007000700070007000700070007000700070007000700070007000700070007 -000700070007000700070007000700070007000700070007000700070007000700070007000700070007000700070007 -000700070007000700070007000700070007000700070007000700070007000700070007000700070007000700070007 -000700070007000700070007000700070007000700070007000700070007000700070007000700070007000700070007 -000700070007000700070007000700070007000700070007000700070007000700070007000700070007000700070007 -000700070007000700070007000700070007000700070007000700070007000700070007000700070007000700070007 -000700070007000700070007000700070007000700070007000700070007000700070007000700070007000700070007 -000700070007000700070007000700070007000700070007000700070007000700070007000700070007000700070007 -000700070007000700070007000700070007000700070007000700070007000700070007000700070007000700070007 -000700070007000700070007000700070007000700070007000700070007000700070007000700070007000700070007 -000700070007000700070007000700070007000700070007000700070007000700070007000700070007000700070007 -000700070007000700070007000700070007000700070007000700070007000700070007000700070007000700070007 -000700070007000700070007000700070007000700070007000700070007000700070007000700070007000700070007 -000700070007000700070007000700070007000700070007000700070007000700070007000700070007000700070007 -000700070007000700070007000700070007000700070007000700070007000700070007000700070007000700070007 -000700070007000700070007000700070007000700070007000700070007000700070007000700070007000700070007 -000700070007000700070007000700070007000700070007000700070007000700070007000700070007000700070007 -000700070007000700070007000700070007000700070007000700070007000700070007000700070007000700070007 -000700070007000700070007000700070007000700070007000700070007000700070007000700070007000700070007 -000700070007000700070007000700070007000700070007000700070007000700070007000700070007000700070007 -000700070007000700070007000700070007000700070007000700070007000700070007000700070007000700070007 -000700070007000700070007000700070007000700070007000700070007000700070007000700070007000700070007 -000700070007000700070007000700070007000700070007000700070007000700070007000700070007000700070007 -000700070007000700070007000700070007000700070007000700070007000700070007000700070007000700070007 -000700070007000700070007000700070007000700070007000700070007000700070007000700070007000700070007 -000700070007000700070007000700070007000700070007000700070007000700070007000700070007000700070007 -000700070007000700070007000700070007000700070007000700070007000700070007000700070007000700070007 -000700070007000700070007000700070007000700070007000700070007000700070007000700070007000700070007 -000700070007000700070007000700070007000700070007000700070007000700070007000700070007000700070007 -000700070007000700070007000700070007000700070007000700070007000700070007000700070007000700070007 -000700070007000700070007000700070007000700070007000700070007000700070007000700070007000700070007 -000700070007000700070007000700070007000700070007000700070007000700070007000700070007000700070007 -000700070007000700070007000700070007000700070007000700070007000700070007000700070007000700070007 -000700070007000700070007000700070007000700070007000700070007000700070007000700070007000700070007 -000700070007000700070007000700070007000700070007000700070007000700070007000700070007000700070007 -000700070007000700070007000700070007000700070007000700070007000700070007000700070007000700070007 -000700070007000700070007000700070007000700070007000700070007000700070007000700070007000700070007 -000700070007000700070007000700070007000700070007000700070007000700070007000700070007000700070007 -000700070007000700070007000700070007000700070007000700070007000700070007000700070007000700070007 -000700070007000700070007000700070007000700070007000700070007000700070007000700070007000700070007 -000700070007000700070007000700070007000700070007000700070007000700070007000700070007000700070007 -000700070007000700070007000700070007000700070007000700070007000700070007000700070007000700070007 -000700070007000700070007000700070007000700070007000700070007000700070007000700070007000700070007 -000700070007000700070007000700070007000700070007000700070007000700070007000700070007000700070007 -000700070007000700070007000700070007000700070007000700070007000700070007000700070007000700070007 -000700070007000700070007000700070007000700070007000700070007000700070007000700070007000700070007 -000700070007000700070007000700070007000700070007000700070007000700070007000700070007000700070007 -000700070007000700070007000700070007000700070007000700070007000700070007000700070007000700070007 -000700070007000700070007000700070007000700070007000700070007000700070007000700070007000700070007 -000700070007000700070007000700070007000700070007000700070007000700070007000700070007000700070007 -000700070007000700070007000700070007000700070007000700070007000700070007000700070007000700070007 -000700070007000700070007000700070007000700070007000700070007000700070007000700070007000700070007 -000700070007000700070007000700070007000700070007000700070007000700070007000700070007000700070007 -000700070007000700070007000700070007000700070007000700070007000700070007000700070007000700070007 -000700070007000700070007000700070007000700070007000700070007000700070007000700070007000700070007 -000700070007000700070007000700070007000700070007000700070007000700070007000700070007000700070007 -000700070007000700070007000700070007000700070007000700070007000700070007000700070007000700070007 -000700070007000700070007000700070007000700070007000700070007000700070007000700070007000700070007 -000700070007000700070007000700070007000700070007000700070007000700070007000700070007000700070007 -000700070007000700070007000700070007000700070007000700070007000700070007000700070007000700070007 -000700070007000700070007000700070007000700070007000700070007000700070007000700070007000700070007 -000700070007000700070007000700070007000700070007000700070007000700070007000700070007000700070007 -000700070007000700070007000700070007000700070007000700070007000700070007000700070007000700070007 -000700070007000700070007000700070007000700070007000700070007000700070007000700070007000700070007 -000700070007000700070007000700070007000700070007000700070007000700070007000700070007000700070007 -000700070007000700070007000700070007000700070007000700070007000700070007000700070007000700070007 -000700070007000700070007000700070007000700070007000700070007000700070007000700070007000700070007 -000700070007000700070007000700070007000700070007000700070007000700070007000700070007000700070007 -000700070007000700070007000700070007000700070007000700070007000700070007000700070007000700070007 -000700070007000700070007000700070007000700070007000700070007000700070007000700070007000700070007 -000700070007000700070007000700070007000700070007000700070007000700070007000700070007000700070007 -000700070007000700070007000700070007000700070007000700070007000700070007000700070007000700070007 -000700070007000700070007000700070007000700070007000700070007000700070007000700070007000700070007 -000700070007000700070007000700070007000700070007000700070007000700070007000700070007000700070007 -000700070007000700070007000700070007000700070007000700070007000700070007000700070007000700070007 -000700070007000700070007000700070007000700070007000700070007000700070007000700070007000700070007 -000700070007000700070007000700070007000700070007000700070007000700070007000700070007000700070007 -000700070007000700070007000700070007000700070007000700070007000700070007000700070007000700070007 -000700070007000700070007000700070007000700070007000700070007000700070007000700070007000700070007 -000700070007000700070007000700070007000700070007000700070007000700070007000700070007000700070007 -000700070007000700070007000700070007000700070007000700070007000700070007000700070007000700070007 -000700070007000700070007000700070007000700070007000700070007000700070007000700070007000700070007 -000700070007000700070007000700070007000700070007000700070007000700070007000700070007000700070007 -000700070007000700070007000700070007000700070007000700070007000700070007000700070007000700070007 -000700070007000700070007000700070007000700070007000700070007000700070007000700070007000700070007 -000700070007000700070007000700070007000700070007000700070007000700070007000700070007000700070007 -000700070007000700070007000700070007000700070007000700070007000700070007000700070007000700070007 -000700070007000700070007000700070007000700070007000700070007000700070007000700070007000700070007 -000700070007000700070007000700070007000700070007000700070007000700070007000700070007000700070007 -000700070007000700070007000700070007000700070007000700070007000700070007000700070007000700070007 -000700070007000700070007000700070007000700070007000700070007000700070007000700070007000700070007 -000700070007000700070007000700070007000700070007000700070007000700070007000700070007000700070007 -000700070007000700070007000700070007000700070007000700070007000700070007000700070007000700070007 -000700070007000700070007000700070007000700070007000700070007000700070007000700070007000700070007 -000700070007000700070007000700070007000700070007000700070007000700070007000700070007000700070007 -000700070007000700070007000700070007000700070007000700070007000700070007000700070007000700070007 -000700070007000700070007000700070007000700070007000700070007000700070007000700070007000700070007 -000700070007000700070007000700070007000700070007000700070007000700070007000700070007000700070007 -000700070007000700070007000700070007000700070007000700070007000700070007000700070007000700070007 -000700070007000700070007000700070007000700070007000700070007000700070007000700070007000700070007 -000700070007000700070007000700070007000700070007000700070007000700070007000700070007000700070007 -000700070007000700070007000700070007000700070007000700070007000700070007000700070007000700070007 -000700070007000700070007000700070007000700070007000700070007000700070007000700070007000700070007 -000700070007000700070007000700070007000700070007000700070007000700070007000700070007000700070007 -000700070007000700070007000700070007000700070007000700070007000700070007000700070007000700070007 -000700070007000700070007000700070007000700070007000700070007000700070007000700070007000700070007 -000700070007000700070007000700070007000700070007000700070007000700070007000700070007000700070007 -000700070007000700070007000700070007000700070007000700070007000700070007000700070007000700070007 -000700070007000700070007000700070007000700070007000700070007000700070007000700070007000700070007 -000700070007000700070007000700070007000700070007000700070007000700070007000700070007000700070007 -000700070007000700070007000700070007000700070007000700070007000700070007000700070007000700070007 -000700070007000700070007000700070007000700070007000700070007000700070007000700070007000700070007 -000700070007000700070007000700070007000700070007000700070007000700070007000700070007000700070007 -000700070007000700070007000700070007000700070007000700070007000700070007000700070007000700070007 -000700070007000700070007000700070007000700070007000700070007000700070007000700070007000700070007 -000700070007000700070007000700070007000700070007000700070007000700070007000700070007000700070007 -000700070007000700070007000700070007000700070007000700070007000700070007000700070007000700070007 -000700070007000700070007000700070007000700070007000700070007000700070007000700070007000700070007 -000700070007000700070007000700070007000700070007000700070007000700070007000700070007000700070007 -000700070007000700070007000700070007000700070007000700070007000700070007000700070007000700070007 -000700070007000700070007000700070007000700070007000700070007000700070007000700070007000700070007 -000700070007000700070007000700070007000700070007000700070007000700070007000700070007000700070007 -000700070007000700070007000700070007000700070007000700070007000700070007000700070007000700070007 -000700070007000700070007000700070007000700070007000700070007000700070007000700070007000700070007 -000700070007000700070007000700070007000700070007000700070007000700070007000700070007000700070007 -000700070007000700070007000700070007000700070007000700070007000700070007000700070007000700070007 -0007000700070007000700070007000700070007000700070007000700070007FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFF08010800FFFF08028003FFFF281618138821301B383308327841383A28510850185C30568068485F -FFFF10783882407A9890388930A510A348AD10ABFFFF30B6FFFFFFFFFFFFFFFF80BCFFFF28CF18CC88DA30D438EC08EB -70FB40F3290B11091916311081224919FFFF1132393C4134994B41433960115E51691167FFFF3173FFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF1979FFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF217C0981098009841182 -FFFFFFFFFFFF2185FFFFFFFF0989FFFFFFFFFFFFFFFFFFFF198E218AFFFF0991FFFF0992FFFFFFFFFFFF2193FFFFFFFF -FFFFFFFFFFFF2197099C099B099F119DFFFFFFFFFFFF21A0FFFFFFFF09A4FFFFFFFFFFFFFFFFFFFF19A921A5FFFF09AC -FFFF09ADFFFFFFFFFFFF21AEFFFFFFFFFFFFFFFF21B621B2FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFF11BC11BAFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF11C011BEFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFF09C309C2FFFFFFFFFFFFFFFF09C509C4FFFFFFFFFFFFFFFFFFFFFFFF09C709C609C909C8FFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF09CAFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -29D029CBFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF29D5FFFFFFFF29DAFFFFFFFFFFFFFFFF09DFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFF09E109E0FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF09E309E209E509E4FFFFFFFFFFFFFFFF09E709E6 -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFF09E8FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF39E9FFFFFFFFFFFF21F0FFFF29F4FFFF -39F9FFFFFFFFFFFFFFFFFFFF2200FFFF0A04FFFFFFFFFFFF3205FFFFFFFFFFFF2A0BFFFFFFFFFFFFFFFF0A10FFFF0A11 -4212FFFFFFFFFFFF221AFFFF321EFFFF4224FFFFFFFFFFFFFFFFFFFF222CFFFF1230FFFFFFFFFFFF4232FFFFFFFFFFFF -323AFFFF1A431A40FFFFFFFFFFFF0A46FFFFFFFFFFFF1247FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0A49FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFF124A0A4CFFFF1A4DFFFF0A521250FFFF2253FFFF0A57FFFFFFFFFFFF0A58FFFFFFFF2259FFFFFFFFFFFF0A5DFFFF -FFFFFFFF0A5EFFFF0A5FFFFFFFFFFFFFFFFF12600A62FFFF1A63FFFF0A681266FFFF2269FFFF0A6DFFFFFFFFFFFF0A6E -FFFFFFFF226FFFFFFFFFFFFF0A73FFFFFFFFFFFF0A74FFFF0A75FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0A76 -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFF0A780A77FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0A7A0A79FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -0A7C0A7BFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFF1A7DFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0A80FFFF0A81FFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0A82FFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0A830A84FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0A85FFFFFFFFFFFFFFFFFFFFFFFFFFFF0A860A87FFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF1288FFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF1A8AFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFF0A8DFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFF0A90128EFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0A91FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0A92FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF1A93 -FFFFFFFFFFFF0A96FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFF0A991297FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF1A9AFFFFFFFFFFFFFFFF0A9DFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0A9EFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0A9FFFFF0AA0FFFF0AA1FFFF0AA2FFFF0AA3FFFFFFFFFFFF -0AA4FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0AA5FFFF0AA60AA80AA7 -FFFFFFFFFFFF0AA9FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFF0AAB0AAAFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0AAD0AACFFFFFFFFFFFFFFFF -FFFFFFFF0AAF0AAEFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF12B212B0FFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0AB50AB4FFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0AB70AB6FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF22BC22B80AC10AC00AC30AC20AC50AC4 -22CA22C60ACF0ACE0AD10AD00AD30AD212D612D4FFFFFFFFFFFFFFFFFFFFFFFF12DA12D8FFFFFFFFFFFFFFFFFFFFFFFF -22E022DC0AE50AE40AE70AE60AE90AE822EE22EA0AF30AF20AF50AF40AF70AF61AFB1AF8FFFFFFFFFFFFFFFFFFFFFFFF -1B011AFEFFFFFFFFFFFFFFFFFFFFFFFF13061304FFFFFFFFFFFFFFFFFFFFFFFF130A1308FFFFFFFFFFFFFFFFFFFFFFFF -1B0F1B0CFFFFFFFFFFFFFFFFFFFFFFFF1B12FFFFFFFFFFFFFFFFFFFFFFFFFFFF231923150B1E0B1D0B200B1F0B220B21 -232723230B2C0B2B0B2E0B2D0B300B2FFFFF0B31FFFFFFFFFFFF0B32FFFFFFFFFFFFFFFFFFFFFFFFFFFF0B33FFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFF0B34FFFFFFFFFFFFFFFFFFFFFFFF1B35FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0B38 -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0B39 -FFFFFFFFFFFFFFFFFFFFFFFFFFFF1B3AFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFF0B3DFFFF0B3EFFFF0B3FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0B40FFFF0B41FFFF0B42FFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0B43FFFFFFFFFFFFFFFFFFFF -FFFF0B440B45FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFF0B46FFFF0B47FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFF0B48FFFFFFFFFFFFFFFF0B49FFFF0B4AFFFFFFFFFFFFFFFF0B4BFFFFFFFF0B4CFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0B4DFFFFFFFFFFFF0B4F0B4EFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0B510B50FFFFFFFF0B530B52FFFFFFFF0B550B540B570B56FFFFFFFF -FFFFFFFF0B590B58FFFFFFFF0B5B0B5AFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0B5CFFFFFFFF0B5DFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0B5EFFFFFFFFFFFFFFFF0B600B5F0B61FFFFFFFFFFFFFFFFFFFF -FFFFFFFF0B630B620B650B64FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0B66 -FFFFFFFF0B67FFFF0B68FFFF0B69FFFF0B6AFFFF0B6BFFFF0B6CFFFF0B6DFFFF0B6EFFFF0B6FFFFF0B70FFFF0B71FFFF -0B72FFFFFFFFFFFFFFFF0B73FFFF0B74FFFF0B75FFFFFFFFFFFFFFFF1376FFFFFFFFFFFFFFFF1378137AFFFFFFFFFFFF -FFFF137C137EFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0B80FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0B81 -FFFFFFFF0B82FFFF0B83FFFF0B84FFFF0B85FFFF0B86FFFF0B87FFFF0B88FFFF0B89FFFF0B8AFFFF0B8BFFFF0B8CFFFF -0B8DFFFFFFFFFFFFFFFF0B8EFFFF0B8FFFFF0B90FFFFFFFFFFFFFFFF1391FFFFFFFFFFFFFFFF13931395FFFFFFFFFFFF -FFFF13971399FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0B9BFFFF -0B9D0B9CFFFF0B9EFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0B9FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0BA0FFFF0BA1FFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFF0BA2FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -0BA3FFFFFFFF0BA4FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFF13A5FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF1BA7FFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0BAB0BAAFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFF0BACFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF", -); -dstring compositionTable() nothrow @nogc pure @safe { -static immutable dchar[1882] t = -"\u0338\u226E\u0338\u2260\u0338\u226F\u0300\u00C0\u0301\u00C1\u0302\u00C2\u0303\u00C3\u0304\u0100\u0306\u0102\u0307"d~ -"\u0226\u0308\u00C4\u0309\u1EA2\u030A\u00C5\u030C\u01CD\u030F\u0200\u0311\u0202\u0323\u1EA0\u0325\u1E00\u0328\u0104"d~ -"\u0307\u1E02\u0323\u1E04\u0331\u1E06\u0301\u0106\u0302\u0108\u0307\u010A\u030C\u010C\u0327\u00C7\u0307\u1E0A\u030C"d~ -"\u010E\u0323\u1E0C\u0327\u1E10\u032D\u1E12\u0331\u1E0E\u0300\u00C8\u0301\u00C9\u0302\u00CA\u0303\u1EBC\u0304\u0112"d~ -"\u0306\u0114\u0307\u0116\u0308\u00CB\u0309\u1EBA\u030C\u011A\u030F\u0204\u0311\u0206\u0323\u1EB8\u0327\u0228\u0328"d~ -"\u0118\u032D\u1E18\u0330\u1E1A\u0307\u1E1E\u0301\u01F4\u0302\u011C\u0304\u1E20\u0306\u011E\u0307\u0120\u030C\u01E6"d~ -"\u0327\u0122\u0302\u0124\u0307\u1E22\u0308\u1E26\u030C\u021E\u0323\u1E24\u0327\u1E28\u032E\u1E2A\u0300\u00CC\u0301"d~ -"\u00CD\u0302\u00CE\u0303\u0128\u0304\u012A\u0306\u012C\u0307\u0130\u0308\u00CF\u0309\u1EC8\u030C\u01CF\u030F\u0208"d~ -"\u0311\u020A\u0323\u1ECA\u0328\u012E\u0330\u1E2C\u0302\u0134\u0301\u1E30\u030C\u01E8\u0323\u1E32\u0327\u0136\u0331"d~ -"\u1E34\u0301\u0139\u030C\u013D\u0323\u1E36\u0327\u013B\u032D\u1E3C\u0331\u1E3A\u0301\u1E3E\u0307\u1E40\u0323\u1E42"d~ -"\u0300\u01F8\u0301\u0143\u0303\u00D1\u0307\u1E44\u030C\u0147\u0323\u1E46\u0327\u0145\u032D\u1E4A\u0331\u1E48\u0300"d~ -"\u00D2\u0301\u00D3\u0302\u00D4\u0303\u00D5\u0304\u014C\u0306\u014E\u0307\u022E\u0308\u00D6\u0309\u1ECE\u030B\u0150"d~ -"\u030C\u01D1\u030F\u020C\u0311\u020E\u031B\u01A0\u0323\u1ECC\u0328\u01EA\u0301\u1E54\u0307\u1E56\u0301\u0154\u0307"d~ -"\u1E58\u030C\u0158\u030F\u0210\u0311\u0212\u0323\u1E5A\u0327\u0156\u0331\u1E5E\u0301\u015A\u0302\u015C\u0307\u1E60"d~ -"\u030C\u0160\u0323\u1E62\u0326\u0218\u0327\u015E\u0307\u1E6A\u030C\u0164\u0323\u1E6C\u0326\u021A\u0327\u0162\u032D"d~ -"\u1E70\u0331\u1E6E\u0300\u00D9\u0301\u00DA\u0302\u00DB\u0303\u0168\u0304\u016A\u0306\u016C\u0308\u00DC\u0309\u1EE6"d~ -"\u030A\u016E\u030B\u0170\u030C\u01D3\u030F\u0214\u0311\u0216\u031B\u01AF\u0323\u1EE4\u0324\u1E72\u0328\u0172\u032D"d~ -"\u1E76\u0330\u1E74\u0303\u1E7C\u0323\u1E7E\u0300\u1E80\u0301\u1E82\u0302\u0174\u0307\u1E86\u0308\u1E84\u0323\u1E88"d~ -"\u0307\u1E8A\u0308\u1E8C\u0300\u1EF2\u0301\u00DD\u0302\u0176\u0303\u1EF8\u0304\u0232\u0307\u1E8E\u0308\u0178\u0309"d~ -"\u1EF6\u0323\u1EF4\u0301\u0179\u0302\u1E90\u0307\u017B\u030C\u017D\u0323\u1E92\u0331\u1E94\u0300\u00E0\u0301\u00E1"d~ -"\u0302\u00E2\u0303\u00E3\u0304\u0101\u0306\u0103\u0307\u0227\u0308\u00E4\u0309\u1EA3\u030A\u00E5\u030C\u01CE\u030F"d~ -"\u0201\u0311\u0203\u0323\u1EA1\u0325\u1E01\u0328\u0105\u0307\u1E03\u0323\u1E05\u0331\u1E07\u0301\u0107\u0302\u0109"d~ -"\u0307\u010B\u030C\u010D\u0327\u00E7\u0307\u1E0B\u030C\u010F\u0323\u1E0D\u0327\u1E11\u032D\u1E13\u0331\u1E0F\u0300"d~ -"\u00E8\u0301\u00E9\u0302\u00EA\u0303\u1EBD\u0304\u0113\u0306\u0115\u0307\u0117\u0308\u00EB\u0309\u1EBB\u030C\u011B"d~ -"\u030F\u0205\u0311\u0207\u0323\u1EB9\u0327\u0229\u0328\u0119\u032D\u1E19\u0330\u1E1B\u0307\u1E1F\u0301\u01F5\u0302"d~ -"\u011D\u0304\u1E21\u0306\u011F\u0307\u0121\u030C\u01E7\u0327\u0123\u0302\u0125\u0307\u1E23\u0308\u1E27\u030C\u021F"d~ -"\u0323\u1E25\u0327\u1E29\u032E\u1E2B\u0331\u1E96\u0300\u00EC\u0301\u00ED\u0302\u00EE\u0303\u0129\u0304\u012B\u0306"d~ -"\u012D\u0308\u00EF\u0309\u1EC9\u030C\u01D0\u030F\u0209\u0311\u020B\u0323\u1ECB\u0328\u012F\u0330\u1E2D\u0302\u0135"d~ -"\u030C\u01F0\u0301\u1E31\u030C\u01E9\u0323\u1E33\u0327\u0137\u0331\u1E35\u0301\u013A\u030C\u013E\u0323\u1E37\u0327"d~ -"\u013C\u032D\u1E3D\u0331\u1E3B\u0301\u1E3F\u0307\u1E41\u0323\u1E43\u0300\u01F9\u0301\u0144\u0303\u00F1\u0307\u1E45"d~ -"\u030C\u0148\u0323\u1E47\u0327\u0146\u032D\u1E4B\u0331\u1E49\u0300\u00F2\u0301\u00F3\u0302\u00F4\u0303\u00F5\u0304"d~ -"\u014D\u0306\u014F\u0307\u022F\u0308\u00F6\u0309\u1ECF\u030B\u0151\u030C\u01D2\u030F\u020D\u0311\u020F\u031B\u01A1"d~ -"\u0323\u1ECD\u0328\u01EB\u0301\u1E55\u0307\u1E57\u0301\u0155\u0307\u1E59\u030C\u0159\u030F\u0211\u0311\u0213\u0323"d~ -"\u1E5B\u0327\u0157\u0331\u1E5F\u0301\u015B\u0302\u015D\u0307\u1E61\u030C\u0161\u0323\u1E63\u0326\u0219\u0327\u015F"d~ -"\u0307\u1E6B\u0308\u1E97\u030C\u0165\u0323\u1E6D\u0326\u021B\u0327\u0163\u032D\u1E71\u0331\u1E6F\u0300\u00F9\u0301"d~ -"\u00FA\u0302\u00FB\u0303\u0169\u0304\u016B\u0306\u016D\u0308\u00FC\u0309\u1EE7\u030A\u016F\u030B\u0171\u030C\u01D4"d~ -"\u030F\u0215\u0311\u0217\u031B\u01B0\u0323\u1EE5\u0324\u1E73\u0328\u0173\u032D\u1E77\u0330\u1E75\u0303\u1E7D\u0323"d~ -"\u1E7F\u0300\u1E81\u0301\u1E83\u0302\u0175\u0307\u1E87\u0308\u1E85\u030A\u1E98\u0323\u1E89\u0307\u1E8B\u0308\u1E8D"d~ -"\u0300\u1EF3\u0301\u00FD\u0302\u0177\u0303\u1EF9\u0304\u0233\u0307\u1E8F\u0308\u00FF\u0309\u1EF7\u030A\u1E99\u0323"d~ -"\u1EF5\u0301\u017A\u0302\u1E91\u0307\u017C\u030C\u017E\u0323\u1E93\u0331\u1E95\u0300\u1FED\u0301\u0385\u0342\u1FC1"d~ -"\u0300\u1EA6\u0301\u1EA4\u0303\u1EAA\u0309\u1EA8\u0304\u01DE\u0301\u01FA\u0301\u01FC\u0304\u01E2\u0301\u1E08\u0300"d~ -"\u1EC0\u0301\u1EBE\u0303\u1EC4\u0309\u1EC2\u0301\u1E2E\u0300\u1ED2\u0301\u1ED0\u0303\u1ED6\u0309\u1ED4\u0301\u1E4C"d~ -"\u0304\u022C\u0308\u1E4E\u0304\u022A\u0301\u01FE\u0300\u01DB\u0301\u01D7\u0304\u01D5\u030C\u01D9\u0300\u1EA7\u0301"d~ -"\u1EA5\u0303\u1EAB\u0309\u1EA9\u0304\u01DF\u0301\u01FB\u0301\u01FD\u0304\u01E3\u0301\u1E09\u0300\u1EC1\u0301\u1EBF"d~ -"\u0303\u1EC5\u0309\u1EC3\u0301\u1E2F\u0300\u1ED3\u0301\u1ED1\u0303\u1ED7\u0309\u1ED5\u0301\u1E4D\u0304\u022D\u0308"d~ -"\u1E4F\u0304\u022B\u0301\u01FF\u0300\u01DC\u0301\u01D8\u0304\u01D6\u030C\u01DA\u0300\u1EB0\u0301\u1EAE\u0303\u1EB4"d~ -"\u0309\u1EB2\u0300\u1EB1\u0301\u1EAF\u0303\u1EB5\u0309\u1EB3\u0300\u1E14\u0301\u1E16\u0300\u1E15\u0301\u1E17\u0300"d~ -"\u1E50\u0301\u1E52\u0300\u1E51\u0301\u1E53\u0307\u1E64\u0307\u1E65\u0307\u1E66\u0307\u1E67\u0301\u1E78\u0301\u1E79"d~ -"\u0308\u1E7A\u0308\u1E7B\u0307\u1E9B\u0300\u1EDC\u0301\u1EDA\u0303\u1EE0\u0309\u1EDE\u0323\u1EE2\u0300\u1EDD\u0301"d~ -"\u1EDB\u0303\u1EE1\u0309\u1EDF\u0323\u1EE3\u0300\u1EEA\u0301\u1EE8\u0303\u1EEE\u0309\u1EEC\u0323\u1EF0\u0300\u1EEB"d~ -"\u0301\u1EE9\u0303\u1EEF\u0309\u1EED\u0323\u1EF1\u030C\u01EE\u0304\u01EC\u0304\u01ED\u0304\u01E0\u0304\u01E1\u0306"d~ -"\u1E1C\u0306\u1E1D\u0304\u0230\u0304\u0231\u030C\u01EF\u0300\u1FBA\u0301\u0386\u0304\u1FB9\u0306\u1FB8\u0313\u1F08"d~ -"\u0314\u1F09\u0345\u1FBC\u0300\u1FC8\u0301\u0388\u0313\u1F18\u0314\u1F19\u0300\u1FCA\u0301\u0389\u0313\u1F28\u0314"d~ -"\u1F29\u0345\u1FCC\u0300\u1FDA\u0301\u038A\u0304\u1FD9\u0306\u1FD8\u0308\u03AA\u0313\u1F38\u0314\u1F39\u0300\u1FF8"d~ -"\u0301\u038C\u0313\u1F48\u0314\u1F49\u0314\u1FEC\u0300\u1FEA\u0301\u038E\u0304\u1FE9\u0306\u1FE8\u0308\u03AB\u0314"d~ -"\u1F59\u0300\u1FFA\u0301\u038F\u0313\u1F68\u0314\u1F69\u0345\u1FFC\u0345\u1FB4\u0345\u1FC4\u0300\u1F70\u0301\u03AC"d~ -"\u0304\u1FB1\u0306\u1FB0\u0313\u1F00\u0314\u1F01\u0342\u1FB6\u0345\u1FB3\u0300\u1F72\u0301\u03AD\u0313\u1F10\u0314"d~ -"\u1F11\u0300\u1F74\u0301\u03AE\u0313\u1F20\u0314\u1F21\u0342\u1FC6\u0345\u1FC3\u0300\u1F76\u0301\u03AF\u0304\u1FD1"d~ -"\u0306\u1FD0\u0308\u03CA\u0313\u1F30\u0314\u1F31\u0342\u1FD6\u0300\u1F78\u0301\u03CC\u0313\u1F40\u0314\u1F41\u0313"d~ -"\u1FE4\u0314\u1FE5\u0300\u1F7A\u0301\u03CD\u0304\u1FE1\u0306\u1FE0\u0308\u03CB\u0313\u1F50\u0314\u1F51\u0342\u1FE6"d~ -"\u0300\u1F7C\u0301\u03CE\u0313\u1F60\u0314\u1F61\u0342\u1FF6\u0345\u1FF3\u0300\u1FD2\u0301\u0390\u0342\u1FD7\u0300"d~ -"\u1FE2\u0301\u03B0\u0342\u1FE7\u0345\u1FF4\u0301\u03D3\u0308\u03D4\u0308\u0407\u0306\u04D0\u0308\u04D2\u0301\u0403"d~ -"\u0300\u0400\u0306\u04D6\u0308\u0401\u0306\u04C1\u0308\u04DC\u0308\u04DE\u0300\u040D\u0304\u04E2\u0306\u0419\u0308"d~ -"\u04E4\u0301\u040C\u0308\u04E6\u0304\u04EE\u0306\u040E\u0308\u04F0\u030B\u04F2\u0308\u04F4\u0308\u04F8\u0308\u04EC"d~ -"\u0306\u04D1\u0308\u04D3\u0301\u0453\u0300\u0450\u0306\u04D7\u0308\u0451\u0306\u04C2\u0308\u04DD\u0308\u04DF\u0300"d~ -"\u045D\u0304\u04E3\u0306\u0439\u0308\u04E5\u0301\u045C\u0308\u04E7\u0304\u04EF\u0306\u045E\u0308\u04F1\u030B\u04F3"d~ -"\u0308\u04F5\u0308\u04F9\u0308\u04ED\u0308\u0457\u030F\u0476\u030F\u0477\u0308\u04DA\u0308\u04DB\u0308\u04EA\u0308"d~ -"\u04EB\u0653\u0622\u0654\u0623\u0655\u0625\u0654\u0624\u0654\u0626\u0654\u06C2\u0654\u06D3\u0654\u06C0\u093C\u0929"d~ -"\u093C\u0931\u093C\u0934\u09BE\u09CB\u09D7\u09CC\u0B3E\u0B4B\u0B56\u0B48\u0B57\u0B4C\u0BD7\u0B94\u0BBE\u0BCA\u0BD7"d~ -"\u0BCC\u0BBE\u0BCB\u0C56\u0C48\u0CD5\u0CC0\u0CC2\u0CCA\u0CD5\u0CC7\u0CD6\u0CC8\u0CD5\u0CCB\u0D3E\u0D4A\u0D57\u0D4C"d~ -"\u0D3E\u0D4B\u0DCA\u0DDA\u0DCF\u0DDC\u0DDF\u0DDE\u0DCA\u0DDD\u102E\u1026\u1B35\u1B06\u1B35\u1B08\u1B35\u1B0A\u1B35"d~ -"\u1B0C\u1B35\u1B0E\u1B35\u1B12\u1B35\u1B3B\u1B35\u1B3D\u1B35\u1B40\u1B35\u1B41\u1B35\u1B43\u0304\u1E38\u0304\u1E39"d~ -"\u0304\u1E5C\u0304\u1E5D\u0307\u1E68\u0307\u1E69\u0302\u1EAC\u0306\u1EB6\u0302\u1EAD\u0306\u1EB7\u0302\u1EC6\u0302"d~ -"\u1EC7\u0302\u1ED8\u0302\u1ED9\u0300\u1F02\u0301\u1F04\u0342\u1F06\u0345\u1F80\u0300\u1F03\u0301\u1F05\u0342\u1F07"d~ -"\u0345\u1F81\u0345\u1F82\u0345\u1F83\u0345\u1F84\u0345\u1F85\u0345\u1F86\u0345\u1F87\u0300\u1F0A\u0301\u1F0C\u0342"d~ -"\u1F0E\u0345\u1F88\u0300\u1F0B\u0301\u1F0D\u0342\u1F0F\u0345\u1F89\u0345\u1F8A\u0345\u1F8B\u0345\u1F8C\u0345\u1F8D"d~ -"\u0345\u1F8E\u0345\u1F8F\u0300\u1F12\u0301\u1F14\u0300\u1F13\u0301\u1F15\u0300\u1F1A\u0301\u1F1C\u0300\u1F1B\u0301"d~ -"\u1F1D\u0300\u1F22\u0301\u1F24\u0342\u1F26\u0345\u1F90\u0300\u1F23\u0301\u1F25\u0342\u1F27\u0345\u1F91\u0345\u1F92"d~ -"\u0345\u1F93\u0345\u1F94\u0345\u1F95\u0345\u1F96\u0345\u1F97\u0300\u1F2A\u0301\u1F2C\u0342\u1F2E\u0345\u1F98\u0300"d~ -"\u1F2B\u0301\u1F2D\u0342\u1F2F\u0345\u1F99\u0345\u1F9A\u0345\u1F9B\u0345\u1F9C\u0345\u1F9D\u0345\u1F9E\u0345\u1F9F"d~ -"\u0300\u1F32\u0301\u1F34\u0342\u1F36\u0300\u1F33\u0301\u1F35\u0342\u1F37\u0300\u1F3A\u0301\u1F3C\u0342\u1F3E\u0300"d~ -"\u1F3B\u0301\u1F3D\u0342\u1F3F\u0300\u1F42\u0301\u1F44\u0300\u1F43\u0301\u1F45\u0300\u1F4A\u0301\u1F4C\u0300\u1F4B"d~ -"\u0301\u1F4D\u0300\u1F52\u0301\u1F54\u0342\u1F56\u0300\u1F53\u0301\u1F55\u0342\u1F57\u0300\u1F5B\u0301\u1F5D\u0342"d~ -"\u1F5F\u0300\u1F62\u0301\u1F64\u0342\u1F66\u0345\u1FA0\u0300\u1F63\u0301\u1F65\u0342\u1F67\u0345\u1FA1\u0345\u1FA2"d~ -"\u0345\u1FA3\u0345\u1FA4\u0345\u1FA5\u0345\u1FA6\u0345\u1FA7\u0300\u1F6A\u0301\u1F6C\u0342\u1F6E\u0345\u1FA8\u0300"d~ -"\u1F6B\u0301\u1F6D\u0342\u1F6F\u0345\u1FA9\u0345\u1FAA\u0345\u1FAB\u0345\u1FAC\u0345\u1FAD\u0345\u1FAE\u0345\u1FAF"d~ -"\u0345\u1FB2\u0345\u1FC2\u0345\u1FF2\u0345\u1FB7\u0300\u1FCD\u0301\u1FCE\u0342\u1FCF\u0345\u1FC7\u0345\u1FF7\u0300"d~ -"\u1FDD\u0301\u1FDE\u0342\u1FDF\u0338\u219A\u0338\u219B\u0338\u21AE\u0338\u21CD\u0338\u21CF\u0338\u21CE\u0338\u2204"d~ -"\u0338\u2209\u0338\u220C\u0338\u2224\u0338\u2226\u0338\u2241\u0338\u2244\u0338\u2247\u0338\u2249\u0338\u226D\u0338"d~ -"\u2262\u0338\u2270\u0338\u2271\u0338\u2274\u0338\u2275\u0338\u2278\u0338\u2279\u0338\u2280\u0338\u2281\u0338\u22E0"d~ -"\u0338\u22E1\u0338\u2284\u0338\u2285\u0338\u2288\u0338\u2289\u0338\u22E2\u0338\u22E3\u0338\u22AC\u0338\u22AD\u0338"d~ -"\u22AE\u0338\u22AF\u0338\u22EA\u0338\u22EB\u0338\u22EC\u0338\u22ED\u3099\u3094\u3099\u304C\u3099\u304E\u3099\u3050"d~ -"\u3099\u3052\u3099\u3054\u3099\u3056\u3099\u3058\u3099\u305A\u3099\u305C\u3099\u305E\u3099\u3060\u3099\u3062\u3099"d~ -"\u3065\u3099\u3067\u3099\u3069\u3099\u3070\u309A\u3071\u3099\u3073\u309A\u3074\u3099\u3076\u309A\u3077\u3099\u3079"d~ -"\u309A\u307A\u3099\u307C\u309A\u307D\u3099\u309E\u3099\u30F4\u3099\u30AC\u3099\u30AE\u3099\u30B0\u3099\u30B2\u3099"d~ -"\u30B4\u3099\u30B6\u3099\u30B8\u3099\u30BA\u3099\u30BC\u3099\u30BE\u3099\u30C0\u3099\u30C2\u3099\u30C5\u3099\u30C7"d~ -"\u3099\u30C9\u3099\u30D0\u309A\u30D1\u3099\u30D3\u309A\u30D4\u3099\u30D6\u309A\u30D7\u3099\u30D9\u309A\u30DA\u3099"d~ -"\u30DC\u309A\u30DD\u3099\u30F7\u3099\u30F8\u3099\u30F9\u3099\u30FA\u3099\u30FE\U000110BA\U0001109A\U000110BA\U0001109C"d~ -"\U000110BA\U000110AB\U00011127\U0001112E\U00011127\U0001112F\U0001133E\U0001134B\U00011357\U0001134C\U000114B0"d~ -"\U000114BC\U000114BA\U000114BB\U000114BD\U000114BE\U000115AF\U000115BA\U000115AF\U000115BB\U00011930\U00011938"d; -return t[]; -} - -} - - -static if (size_t.sizeof == 8) -{ -//10144 bytes -enum combiningClassTrieEntries = TrieEntry!(ubyte, 8, 8, 5)(x" -0000000000000000000000000000002000000000000002E0", -x" -00000000000001000000000000000B0000000000000010A0", -x" -040203020202010009080702060202050000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000004000300020001000000000000000000000000000000000000000000000005 -00000000000000000000000800070006000B000A00000009000D000C0000000000000010000F000E0011000000000000 -000000140013001200170016000000150000001900180000001B001A001800000000001A001800000000001A00180000 -0000001A001800000000001A000000000000001C001800000000001A001800000000001A001D00000000001E00000000 -00000020001F000000000022002100000025000000240023000000270000002600000000002800000000000000000029 -00000000000000000000000000000000000000000000000000000000000000000000002A000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -00000000002C002B0000002D00000000000000000000000000000000002E000000000000002F00000000000000000000 -00310000000000300000003300320000003600350034000000380000003700000000000000390000003B003A00000000 -0000000000000000003D003C000000000000000000000000000000000000000000000000000000000000000000000000 -0000000000000000003F003E000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000400000000000000041000000000000004200000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000430000000000000000004400000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000045000000000000004700000000004600000000000000000000000000000000 -0000000000490048004A0035000000000000004C004B00000000004E004D0000000000000000000000510050004F0000 -0000000000000000001A0000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000005200000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000053000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000540000000000000000000000000000005500000000000000560000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000058005700590000000000000000000000000000000000000000000000000000000000000000000000000000 -00000000005A000000000000000000000000000000000000005C0000005B00000000005D00000000000000000000005E -005F0048000000000000000000600000004D000000620061000000630000000000000000006400000065000000000000 -0067001A0066000000000000000000000000006800000000000000690000000000000000000000000000006A00410000 -000000000041000000000000006B000000000000006C0000000000000000000000000000006000000000000000000000 -0000006E006D0000004E0000000000000000006F002C0000000000000000007000000000000000000000000000000000 -000000000041000000000000000000000000007100000000000000000000007200000000000000000000000000000000 -000000730000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000074000000000000 -000000000075000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000007600000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000007700000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -00000000000000000000000000000000007800000000000000000000007A00790000007B000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000007D007C000000000000007E00000000007500000000000000000000 -000000000000000000800000007F00000000000000000000000000000000000000000000000000000081000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000082000000000000008300000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -00000000000000000000000000000000E6E6E6E6E6E6E6E6E6E6E6E6E6E6E6E6DCDCE8E6E6E6E6E6DCDCDCDCD8E8DCDC -CADCDCDCDCCACADCDCDCDCDCDCDCDCCA01010101DCDCDCDCE6E6E6DCDCDCDC01DCE6F0E6E6E6E6E600DCDCE6E6E6DCDC -E6DCDCDCDCE6E6E6E9EAEAE9E6DCDCE8E6E6E6E6E6E9EAEAE6E6E6E6E6E6E6E600000000000000000000000000000000 -E6E6E6E6E600000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -E6DCE6E6E6E6DC00E6E6E6E6DCDEE6E6DCDCDCDCDCDCE6E6E6E4DEE6E6DCE6E611100F0E0D0C0B0A1700161514131312 -1200DCE60019180000000000000000000000000000000000000000000000000000000000000000000000000000000000 -E6E6E6E6E6E6E6E60000000000201F1E00000000000000001F1E1D1C1B000000E6DCDCE6E6222120DCE6E6DCE6E6E6E6 -000000000000000000000000000000000000000000000023000000000000000000000000000000000000000000000000 -E6E6000000000000E60000E6E6E6E6E6E60000E6DCE6E6E60000DCE6E6DC00E600000000000000000000000000000000 -000000000000000000000000000000000000000000002400000000000000000000000000000000000000000000000000 -DCE6E6DCE6E6DCE6E6DCE6DCDCE6DCDCE6DCE6DCE6DCE6E60000000000E6E6DC00000000000000000000000000000000 -0000000000000000E6E6E6E6E600000000000000E6DCE6E60000DC000000000000000000000000000000000000000000 -E6E6000000000000E6E6E6E6E600E6E6E6E6E600E6E6E6E60000E6E6E6E6E60000000000000000000000000000000000 -00000000000000000000000000000000000000000000000000000000DCDCDC0000000000000000000000000000000000 -0000000000000000E6E6E6E6DCDCDCE60000000000000000DCE6E6E6E6E60000E6E6E6E6DCDCDCDCE6E6E6E6E6E6E6E6 -E6DCE6E6DC00E6E6DCDCDCE6E6E6DCE6E6DCE6E6E61D1C1BE6E6E6E6E6DCDCE600000000000000000000000000000000 -0000000000000000000000070000000000000000000000000000090000000000000000E6E6DCE6000000000000000000 -000000000000000000000900000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000E600000000000000000000000000000000090000000000005B5400000000000000000000000000 -000000000000000000000000000000000000000000000000000000090900000000000000000000000000000000090000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000096767 -0000000000000000000000006B6B6B6B0000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000976760000000000000000000000007A7A7A7A00000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000DCDC00000000000000000000000000000000 -DC00DC0000000000000000000000D8000000000000000000000000000000000000000084008281000000828282820000 -E6E60009E6E6008200000000000000000000000000000000000000000000000000DC0000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000007000000000000000000000000090900 -00000000000000000000DC00000000000000000000000000000000000000000000000000000000000000000000000000 -0000000000000000E6E6E600000000000000000000000000000000000000000000000909000000000000000000000000 -000000000000000000000000000000000000000900000000000000000000000000000000000000000000000000000000 -00000000000900000000E600000000000000000000000000000000000000E40000000000000000000000000000000000 -00000000000000000000000000000000000000000000000000000000DCE6DE0000000000000000000000000000000000 -E60000000000000000000000000000DC00000000000000090000000000000000E6E6E60000000000DC0000E6E6E6E6E6 -00000000000000000000000000000000DCDCDCE6E6E6E6E6DC00DCE6E6DCDCDCE6E6E6DCDCE6E6DC00E6E6E6E6DCE6E6 -000000000000000000000000000000000000000000000000000000000000000000000007000000000000000000000000 -00000009000000000000000000000000000000000000000000000000000000000000000000000000E6E6E6DCE6000000 -00000000E6E6E6E600000000000000000000000000000000000000000909000000000000000000000000000000000000 -000700000000000000000000000000000000000009090000000000000000000000000000000000000000000000000000 -0700000000000000000000000000000000000000000000000000000000000000DCDCDC0100E6E6E6DCDCDCDCE6E6DCDC -01010101010100E60000DC0000000001000000E600000000000000000000E6E6E6E6E6E6E6DCE6E6DCD6EAE6E6DCE6E6 -E6E6E6E6E6E6E6CAE6E6E6E6E6E6E6E6E6E6E6E6E6E6E6E6E6E6E6E6E6E6E6E6E4E8E6E6E6E6E6E6DCE6DCE9E6DADCE4 -00000000000000000000000000000000E6E6E6E60101E6E6000000E6E6010101E60101000000E600DCDCDCDC0101E6DC -00000000000000E600000000000000000000000000000000E600000000000000000000000000E6E60000000000000000 -0000000000000000000000000000000000000000000000000900000000000000E6E6E6E6E6E6E6E6E6E6E6E6E6E6E6E6 -E6E6E6E6E6E6E6E6E6E6E6E6E6E6E6E60000000000000000E0E0DEE8E4DA000000000000000000000000000000000000 -00000000000000000000000000000000000000000000000000000000000808000000000000000000E600000000000000 -E6E6E6E6000000000000E6E6E6E6E6E6000000000000000000000000000000000000000000000000E6E6000000000000 -00000000000000000000000000000000000000000000E6E6000000000000000000090000000000000000000000000000 -000000000000000000000000000000000000000000000000000000090000000000000000000000000000000000000000 -E6E6E6E6E6E6E6E6E6E6E6E6E6E6E6E6000000000000E6E6000000000000000000000000000000000000DCDCDC000000 -000000000000000000000000000000000000000000000000000000000000000000000000090000000000000000000000 -000000000000000000000000000000000000000007000000000000000000000000000000000000090000000000000000 -0000000000000000000000000000000000000000000000000000000000000000E60000DCE6E600E6E6E60000000000E6 -000000000000E60000000000000000000000000000000000000000000000000000000000000000000000000000000000 -00090000000000000000000000000000000000000000000000000000000000000000000000000000001A000000000000 -DCE6E6E6E6E6E6E6E6E6DCDCDCDCDCDC0000000000000000000000000000000000000000000000000000000000000000 -00000000000000000000DC000000000000000000000000DC000000000000000000000000000000000000000000000000 -00000000000000000000000000000000E6E60000000000000000000000E6E6E60000000000000000E600DC0000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000900000000DC01E6 -00DCE60000000000000000000000000000000000000000000000000000000000E6E6E6E6000000000000000000000000 -000000000000000000000000000000000000000000000000000000E6E600000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000DCDCDC0000000000DCDC000000000000DCDCDCE6DCE6E6E6 -00000000000000DC00000000000000000000DCE6DCE60000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000009090000000000000000000000000000000000000000000000 -000000000000000000000000000709000000000000E6E6E6000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000909000000000000000000000000000000000000090000000000070000 -000000000000000000000000000000000000000000000000000000000000000000070900000000000000000000000000 -000000000000000000000000000907000000000000000000000000000000000000000000000000000000000000000000 -00000000000000000000000707000000E6E6000000000000000000E6E6E6E6E6000000E6E6E6E6E60000000000000000 -00070000000900000000000000000000000000000000000000E600000000000000000000070900000000000000000000 -000000000000000000000000000000000000000000000007000000000000000000000000000000000000000000000000 -000000000000000000000000000000000709000000000000000000000000000000000000000000000000000009000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000009090000000000 -000000000700000000000000000000000000000000000000000000000000000009000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000900 -000009090007000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -090000000000000000000000000000000000000000090900000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000101010101000000000000000000000000000000000000000000000000 -00E6E6E6E6E6E6E600000000000000000000000000000000000000000000000000000000000006060000000000000000 -000000000000000000000000000000000000000000000000000100000000000001D8D80000000000D8D8E20000000101 -0000000000D8D8D8DCDCDCDCDC000000E6E6E60000DCDCDC00000000DCDCE6E600000000000000000000000000000000 -00000000000000000000E6E6E6E6000000000000000000000000000000000000000000E6E6E600000000000000000000 -0000000000000000000000000000000000E6E6E6E6E6E6E6E6E6E6E6E6E6E6E6E6E6E6E6E6E6E6E6E6E6E6E6E60000E6 -E6E600E6E600E6E60000000000E6E6E6000000000000000000000000000000000000000000000000E600000000000000 -00000000000000000000000000000000000000000000000000E600000000000000000000000000000000000000000000 -0000000000000000E6E6E6E600000000000000000000000000000000000000000000000000000000E6DCE8E800000000 -000000000000000000000000000000000000000000000000000000000000000000DCDCDCDCDCDCDC0000000000000000 -E6E6E6E600000000000000000007E6E60000000000000000000000000000000000000000000000000000000000000000 -00000000000000000000000000000000", -); -enum composeIdxMask = (1 << 11) - 1, composeCntShift = 11; -enum compositionJumpTrieEntries = TrieEntry!(ushort, 12, 9)(x" -00000000000000000000000000000400", -x" -00000000000010000000000000002600", -x" -000300020001000000070006000500040007000700070008000A00070009000700070007000C000B0007000700070007 -000700070007000D00070007000700070007000700070007000700070007000700070007000700070007000700070007 -000700070007000700070007000700070007000700070007000700070007000700070007000700070007000700070007 -000700070007000700070007000700070007000700070007000700070007000700070007000700070007000700070007 -000700070007000700070007000700070007000700070007000700070007000700070007000700070007000700070007 -000700070007000700070007000700070007000700070007000700070007000700070010000F000E0007000700070011 -000700070007000700070007000700070007000700070007000700070007000700070007000700070007000700070007 -000700070007000700070007000700070007000700070007000700070007000700070007000700070007000700070007 -000700070007000700070007000700070007000700070007000700070007000700070007000700070007000700070007 -000700070007000700070007000700070007000700070007000700070007000700070007000700070007000700070007 -000700070007000700070007000700070007000700070007000700070007000700070007000700070007000700070007 -000700070007000700070007000700070007000700070007000700070007000700070007000700070007000700070007 -000700070007000700070007000700070007000700070007000700070007000700070007000700070007000700070007 -000700070007000700070007000700070007000700070007000700070007000700070007000700070007000700070007 -000700070007000700070007000700070007000700070007000700070007000700070007000700070007000700070007 -000700070007000700070007000700070007000700070007000700070007000700070007000700070007000700070007 -000700070007000700070007000700070007000700070007000700070007000700070007000700070007000700070007 -000700070007000700070007000700070007000700070007000700070007000700070007000700070007000700070007 -000700070007000700070007000700070007000700070007000700070007000700070007000700070007000700070007 -000700070007000700070007000700070007000700070007000700070007000700070007000700070007000700070007 -000700070007000700070007000700070007000700070007000700070007000700070007000700070007000700070007 -000700070007000700070007000700070007000700070007000700070007000700070007000700070007000700070007 -000700070007000700070007000700070007000700070007000700070007000700070007000700070007000700070007 -000700070007000700070007000700070007000700070007000700070007000700070007000700070007000700070007 -000700070007000700070007000700070007000700070007000700070007000700070007000700070007000700070007 -000700070007000700070007000700070007000700070007000700070007000700070007000700070007000700070007 -000700070007000700070007000700070007000700070007000700070007000700070007000700070007000700070007 -000700070007000700070007000700070007000700070007000700070007000700070007000700070007000700070007 -000700070007000700070007000700070007000700070007000700070007000700070007000700070007000700070007 -000700070007000700070007000700070007000700070007000700070007000700070007000700070007000700070007 -000700070007000700070007000700070007000700070007000700070007000700070007000700070007000700070007 -000700070007000700070007000700070007000700070007000700070007000700070007000700070007000700070007 -000700070007000700070007000700070007000700070007000700070007000700070007000700070007000700070007 -000700070007000700070007000700070007000700070007000700070007000700070007000700070007000700070007 -000700070007000700070007000700070007000700070007000700070007000700070007000700070007000700070007 -000700070007000700070007000700070007000700070007000700070007000700070007000700070007000700070007 -000700070007000700070007000700070007000700070007000700070007000700070007000700070007000700070007 -000700070007000700070007000700070007000700070007000700070007000700070007000700070007000700070007 -000700070007000700070007000700070007000700070007000700070007000700070007000700070007000700070007 -000700070007000700070007000700070007000700070007000700070007000700070007000700070007000700070007 -000700070007000700070007000700070007000700070007000700070007000700070007000700070007000700070007 -000700070007000700070007000700070007000700070007000700070007000700070007000700070007000700070007 -000700070007000700070007000700070007000700070007000700070007000700070007000700070007000700070007 -000700070007000700070007000700070007000700070007000700070007000700070007000700070007000700070007 -000700070007000700070007000700070007000700070007000700070007000700070007000700070007000700070007 -000700070007000700070007000700070007000700070007000700070007000700070007000700070007000700070007 -000700070007000700070007000700070007000700070007000700070007000700070007000700070007000700070007 -000700070007000700070007000700070007000700070007000700070007000700070007000700070007000700070007 -000700070007000700070007000700070007000700070007000700070007000700070007000700070007000700070007 -000700070007000700070007000700070007000700070007000700070007000700070007000700070007000700070007 -000700070007000700070007000700070007000700070007000700070007000700070007000700070007000700070007 -000700070007000700070007000700070007000700070007000700070007000700070007000700070007000700070007 -000700070007000700070007000700070007000700070007000700070007000700070007000700070007000700070007 -000700070007000700070007000700070007000700070007000700070007000700070007000700070007000700070007 -000700070007000700070007000700070007000700070007000700070007000700070007000700070007000700070007 -000700070007000700070007000700070007000700070007000700070007000700070007000700070007000700070007 -000700070007000700070007000700070007000700070007000700070007000700070007000700070007000700070007 -000700070007000700070007000700070007000700070007000700070007000700070007000700070007000700070007 -000700070007000700070007000700070007000700070007000700070007000700070007000700070007000700070007 -000700070007000700070007000700070007000700070007000700070007000700070007000700070007000700070007 -000700070007000700070007000700070007000700070007000700070007000700070007000700070007000700070007 -000700070007000700070007000700070007000700070007000700070007000700070007000700070007000700070007 -000700070007000700070007000700070007000700070007000700070007000700070007000700070007000700070007 -000700070007000700070007000700070007000700070007000700070007000700070007000700070007000700070007 -000700070007000700070007000700070007000700070007000700070007000700070007000700070007000700070007 -000700070007000700070007000700070007000700070007000700070007000700070007000700070007000700070007 -000700070007000700070007000700070007000700070007000700070007000700070007000700070007000700070007 -000700070007000700070007000700070007000700070007000700070007000700070007000700070007000700070007 -000700070007000700070007000700070007000700070007000700070007000700070007000700070007000700070007 -000700070007000700070007000700070007000700070007000700070007000700070007000700070007000700070007 -000700070007000700070007000700070007000700070007000700070007000700070007000700070007000700070007 -000700070007000700070007000700070007000700070007000700070007000700070007000700070007000700070007 -000700070007000700070007000700070007000700070007000700070007000700070007000700070007000700070007 -000700070007000700070007000700070007000700070007000700070007000700070007000700070007000700070007 -000700070007000700070007000700070007000700070007000700070007000700070007000700070007000700070007 -000700070007000700070007000700070007000700070007000700070007000700070007000700070007000700070007 -000700070007000700070007000700070007000700070007000700070007000700070007000700070007000700070007 -000700070007000700070007000700070007000700070007000700070007000700070007000700070007000700070007 -000700070007000700070007000700070007000700070007000700070007000700070007000700070007000700070007 -000700070007000700070007000700070007000700070007000700070007000700070007000700070007000700070007 -000700070007000700070007000700070007000700070007000700070007000700070007000700070007000700070007 -000700070007000700070007000700070007000700070007000700070007000700070007000700070007000700070007 -000700070007000700070007000700070007000700070007000700070007000700070007000700070007000700070007 -000700070007000700070007000700070007000700070007000700070007000700070007000700070007000700070007 -000700070007000700070007000700070007000700070007000700070007000700070007000700070007000700070007 -000700070007000700070007000700070007000700070007000700070007000700070007000700070007000700070007 -000700070007000700070007000700070007000700070007000700070007000700070007000700070007000700070007 -000700070007000700070007000700070007000700070007000700070007000700070007000700070007000700070007 -000700070007000700070007000700070007000700070007000700070007000700070007000700070007000700070007 -000700070007000700070007000700070007000700070007000700070007000700070007000700070007000700070007 -000700070007000700070007000700070007000700070007000700070007000700070007000700070007000700070007 -000700070007000700070007000700070007000700070007000700070007000700070007000700070007000700070007 -000700070007000700070007000700070007000700070007000700070007000700070007000700070007000700070007 -000700070007000700070007000700070007000700070007000700070007000700070007000700070007000700070007 -000700070007000700070007000700070007000700070007000700070007000700070007000700070007000700070007 -000700070007000700070007000700070007000700070007000700070007000700070007000700070007000700070007 -000700070007000700070007000700070007000700070007000700070007000700070007000700070007000700070007 -000700070007000700070007000700070007000700070007000700070007000700070007000700070007000700070007 -000700070007000700070007000700070007000700070007000700070007000700070007000700070007000700070007 -000700070007000700070007000700070007000700070007000700070007000700070007000700070007000700070007 -000700070007000700070007000700070007000700070007000700070007000700070007000700070007000700070007 -000700070007000700070007000700070007000700070007000700070007000700070007000700070007000700070007 -000700070007000700070007000700070007000700070007000700070007000700070007000700070007000700070007 -000700070007000700070007000700070007000700070007000700070007000700070007000700070007000700070007 -000700070007000700070007000700070007000700070007000700070007000700070007000700070007000700070007 -000700070007000700070007000700070007000700070007000700070007000700070007000700070007000700070007 -000700070007000700070007000700070007000700070007000700070007000700070007000700070007000700070007 -000700070007000700070007000700070007000700070007000700070007000700070007000700070007000700070007 -000700070007000700070007000700070007000700070007000700070007000700070007000700070007000700070007 -000700070007000700070007000700070007000700070007000700070007000700070007000700070007000700070007 -000700070007000700070007000700070007000700070007000700070007000700070007000700070007000700070007 -000700070007000700070007000700070007000700070007000700070007000700070007000700070007000700070007 -000700070007000700070007000700070007000700070007000700070007000700070007000700070007000700070007 -000700070007000700070007000700070007000700070007000700070007000700070007000700070007000700070007 -000700070007000700070007000700070007000700070007000700070007000700070007000700070007000700070007 -000700070007000700070007000700070007000700070007000700070007000700070007000700070007000700070007 -000700070007000700070007000700070007000700070007000700070007000700070007000700070007000700070007 -000700070007000700070007000700070007000700070007000700070007000700070007000700070007000700070007 -000700070007000700070007000700070007000700070007000700070007000700070007000700070007000700070007 -000700070007000700070007000700070007000700070007000700070007000700070007000700070007000700070007 -000700070007000700070007000700070007000700070007000700070007000700070007000700070007000700070007 -000700070007000700070007000700070007000700070007000700070007000700070007000700070007000700070007 -000700070007000700070007000700070007000700070007000700070007000700070007000700070007000700070007 -000700070007000700070007000700070007000700070007000700070007000700070007000700070007000700070007 -000700070007000700070007000700070007000700070007000700070007000700070007000700070007000700070007 -000700070007000700070007000700070007000700070007000700070007000700070007000700070007000700070007 -000700070007000700070007000700070007000700070007000700070007000700070007000700070007000700070007 -000700070007000700070007000700070007000700070007000700070007000700070007000700070007000700070007 -000700070007000700070007000700070007000700070007000700070007000700070007000700070007000700070007 -000700070007000700070007000700070007000700070007000700070007000700070007000700070007000700070007 -000700070007000700070007000700070007000700070007000700070007000700070007000700070007000700070007 -000700070007000700070007000700070007000700070007000700070007000700070007000700070007000700070007 -000700070007000700070007000700070007000700070007000700070007000700070007000700070007000700070007 -000700070007000700070007000700070007000700070007000700070007000700070007000700070007000700070007 -000700070007000700070007000700070007000700070007000700070007000700070007000700070007000700070007 -000700070007000700070007000700070007000700070007000700070007000700070007000700070007000700070007 -000700070007000700070007000700070007000700070007000700070007000700070007000700070007000700070007 -000700070007000700070007000700070007000700070007000700070007000700070007000700070007000700070007 -000700070007000700070007000700070007000700070007000700070007000700070007000700070007000700070007 -000700070007000700070007000700070007000700070007000700070007000700070007000700070007000700070007 -000700070007000700070007000700070007000700070007000700070007000700070007000700070007000700070007 -000700070007000700070007000700070007000700070007000700070007000700070007000700070007000700070007 -000700070007000700070007000700070007000700070007000700070007000700070007000700070007000700070007 -000700070007000700070007000700070007000700070007000700070007000700070007000700070007000700070007 -000700070007000700070007000700070007000700070007000700070007000700070007000700070007000700070007 -000700070007000700070007000700070007000700070007000700070007000700070007000700070007000700070007 -000700070007000700070007000700070007000700070007000700070007000700070007000700070007000700070007 -000700070007000700070007000700070007000700070007000700070007000700070007000700070007000700070007 -000700070007000700070007000700070007000700070007000700070007000700070007000700070007000700070007 -000700070007000700070007000700070007000700070007000700070007000700070007000700070007000700070007 -000700070007000700070007000700070007000700070007000700070007000700070007000700070007000700070007 -000700070007000700070007000700070007000700070007000700070007000700070007000700070007000700070007 -000700070007000700070007000700070007000700070007000700070007000700070007000700070007000700070007 -000700070007000700070007000700070007000700070007000700070007000700070007000700070007000700070007 -000700070007000700070007000700070007000700070007000700070007000700070007000700070007000700070007 -000700070007000700070007000700070007000700070007000700070007000700070007000700070007000700070007 -000700070007000700070007000700070007000700070007000700070007000700070007000700070007000700070007 -000700070007000700070007000700070007000700070007000700070007000700070007000700070007000700070007 -000700070007000700070007000700070007000700070007000700070007000700070007000700070007000700070007 -000700070007000700070007000700070007000700070007000700070007000700070007000700070007000700070007 -000700070007000700070007000700070007000700070007000700070007000700070007000700070007000700070007 -000700070007000700070007000700070007000700070007000700070007000700070007000700070007000700070007 -000700070007000700070007000700070007000700070007000700070007000700070007000700070007000700070007 -000700070007000700070007000700070007000700070007000700070007000700070007000700070007000700070007 -000700070007000700070007000700070007000700070007000700070007000700070007000700070007000700070007 -000700070007000700070007000700070007000700070007000700070007000700070007000700070007000700070007 -000700070007000700070007000700070007000700070007000700070007000700070007000700070007000700070007 -000700070007000700070007000700070007000700070007000700070007000700070007000700070007000700070007 -000700070007000700070007000700070007000700070007000700070007000700070007000700070007000700070007 -000700070007000700070007000700070007000700070007000700070007000700070007000700070007000700070007 -0007000700070007000700070007000700070007000700070007000700070007FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFF080208010800281618138003FFFF383308328821301B285108507841383A8068485F185C3056 -3882407AFFFF107830A510A398903889FFFF30B648AD10ABFFFFFFFFFFFFFFFF28CF18CC80BCFFFF38EC08EB88DA30D4 -290B110970FB40F38122491919163110393C4134FFFF11323960115E994B4143FFFF317351691167FFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF1979FFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF217CFFFFFFFF0984118209810980 -FFFF2185FFFFFFFF0989FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0991198E218AFFFFFFFFFFFF0992FFFFFFFFFFFF2193 -FFFF2197FFFFFFFF099F119D099C099BFFFF21A0FFFFFFFF09A4FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF09AC19A921A5 -FFFFFFFFFFFF09ADFFFFFFFFFFFF21AE21B621B2FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -11BC11BAFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF11C011BEFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -09C309C2FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF09C509C4FFFFFFFFFFFFFFFF09C909C809C709C6FFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF09CAFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFF29D029CBFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF29D5FFFFFFFFFFFFFFFFFFFFFFFF29DA09DFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -09E109E0FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF09E309E2FFFFFFFFFFFFFFFF09E509E409E709E6FFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFF09E8FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF39E9FFFF29F4FFFF21F0FFFF -FFFFFFFF39F9FFFF2200FFFFFFFFFFFFFFFFFFFF0A04FFFFFFFFFFFF3205FFFFFFFFFFFF2A0BFFFFFFFF0A11FFFF0A10 -FFFFFFFF4212FFFF321EFFFF221AFFFFFFFFFFFF4224FFFF222CFFFFFFFFFFFFFFFFFFFF1230FFFFFFFFFFFF4232FFFF -1A431A40323AFFFFFFFF0A46FFFFFFFFFFFF1247FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0A49FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -0A4CFFFFFFFF124A0A5212501A4DFFFFFFFF0A57FFFF2253FFFF0A58FFFFFFFF2259FFFFFFFFFFFF0A5DFFFFFFFFFFFF -0A5EFFFFFFFFFFFFFFFFFFFF0A5FFFFF0A62FFFFFFFF12600A6812661A63FFFFFFFF0A6DFFFF2269FFFF0A6EFFFFFFFF -226FFFFFFFFFFFFF0A73FFFFFFFFFFFF0A74FFFFFFFFFFFFFFFFFFFF0A75FFFFFFFFFFFFFFFFFFFFFFFF0A76FFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFF0A780A77FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0A7A0A79FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFF0A7C0A7BFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFF1A7DFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0A81FFFF0A80FFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0A82FFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0A83FFFFFFFFFFFFFFFF0A84FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0A85FFFFFFFFFFFFFFFF0A87FFFFFFFF0A86FFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF1288FFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF1A8AFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFF0A8DFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFF0A90128EFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0A91FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0A92FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF1A93FFFFFFFF -FFFF0A96FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFF0A991297FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF1A9AFFFFFFFFFFFFFFFF0A9DFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0A9EFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0AA0FFFF0A9FFFFF0AA2FFFF0AA1FFFFFFFFFFFF0AA3FFFF -FFFFFFFF0AA4FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0AA5FFFFFFFF0AA80AA7FFFF0AA6 -FFFF0AA9FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFF0AAB0AAAFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0AAD0AACFFFFFFFFFFFFFFFFFFFFFFFF -0AAF0AAEFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF12B212B0FFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0AB50AB4FFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0AB70AB6FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0AC10AC022BC22B80AC50AC40AC30AC2 -0ACF0ACE22CA22C60AD30AD20AD10AD0FFFFFFFF12D612D4FFFFFFFFFFFFFFFFFFFFFFFF12DA12D8FFFFFFFFFFFFFFFF -0AE50AE422E022DC0AE90AE80AE70AE60AF30AF222EE22EA0AF70AF60AF50AF4FFFFFFFF1AFB1AF8FFFFFFFFFFFFFFFF -FFFFFFFF1B011AFEFFFFFFFFFFFFFFFFFFFFFFFF13061304FFFFFFFFFFFFFFFFFFFFFFFF130A1308FFFFFFFFFFFFFFFF -FFFFFFFF1B0F1B0CFFFFFFFFFFFFFFFFFFFFFFFF1B12FFFFFFFFFFFFFFFFFFFF0B1E0B1D231923150B220B210B200B1F -0B2C0B2B232723230B300B2F0B2E0B2DFFFFFFFFFFFF0B31FFFFFFFFFFFF0B32FFFFFFFFFFFFFFFFFFFFFFFFFFFF0B33 -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFF0B34FFFFFFFFFFFFFFFFFFFFFFFF1B35FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0B38FFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0B39FFFFFFFF -FFFFFFFFFFFFFFFFFFFF1B3AFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFF0B3EFFFF0B3DFFFFFFFFFFFF0B3FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0B41FFFF0B40FFFFFFFFFFFF0B42 -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0B43FFFFFFFFFFFFFFFFFFFFFFFFFFFF -0B45FFFFFFFF0B44FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -0B46FFFFFFFFFFFFFFFFFFFF0B47FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFF0B480B49FFFFFFFFFFFFFFFFFFFF0B4AFFFFFFFFFFFFFFFF0B4BFFFFFFFF0B4CFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0B4DFFFFFFFFFFFF0B4F0B4E -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0B510B50FFFFFFFF0B530B52FFFFFFFF0B550B54FFFFFFFFFFFFFFFF0B570B56 -0B590B58FFFFFFFF0B5B0B5AFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0B5D0B5CFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0B5EFFFFFFFFFFFFFFFFFFFFFFFF0B61FFFF0B600B5FFFFFFFFFFFFFFFFF -0B630B62FFFFFFFFFFFFFFFF0B650B64FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0B66FFFFFFFF -0B67FFFFFFFFFFFF0B69FFFF0B68FFFF0B6BFFFF0B6AFFFF0B6DFFFF0B6CFFFF0B6FFFFF0B6EFFFF0B71FFFF0B70FFFF -FFFFFFFF0B72FFFFFFFF0B74FFFF0B73FFFFFFFFFFFF0B751376FFFFFFFFFFFFFFFF1378FFFFFFFFFFFFFFFF137AFFFF -137EFFFFFFFF137CFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0B80FFFFFFFFFFFFFFFFFFFFFFFF0B81FFFFFFFF -0B82FFFFFFFFFFFF0B84FFFF0B83FFFF0B86FFFF0B85FFFF0B88FFFF0B87FFFF0B8AFFFF0B89FFFF0B8CFFFF0B8BFFFF -FFFFFFFF0B8DFFFFFFFF0B8FFFFF0B8EFFFFFFFFFFFF0B901391FFFFFFFFFFFFFFFF1393FFFFFFFFFFFFFFFF1395FFFF -1399FFFFFFFF1397FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0B9BFFFFFFFFFFFF -FFFF0B9E0B9D0B9CFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0B9FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0BA1FFFF0BA0FFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFF0BA2FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFF0BA40BA3FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFF13A5FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF1BA7FFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0BAB0BAAFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFF0BACFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF", -); -dstring compositionTable() nothrow @nogc pure @safe { -static immutable dchar[1882] t = -"\u0338\u226E\u0338\u2260\u0338\u226F\u0300\u00C0\u0301\u00C1\u0302\u00C2\u0303\u00C3\u0304\u0100\u0306\u0102\u0307"d~ -"\u0226\u0308\u00C4\u0309\u1EA2\u030A\u00C5\u030C\u01CD\u030F\u0200\u0311\u0202\u0323\u1EA0\u0325\u1E00\u0328\u0104"d~ -"\u0307\u1E02\u0323\u1E04\u0331\u1E06\u0301\u0106\u0302\u0108\u0307\u010A\u030C\u010C\u0327\u00C7\u0307\u1E0A\u030C"d~ -"\u010E\u0323\u1E0C\u0327\u1E10\u032D\u1E12\u0331\u1E0E\u0300\u00C8\u0301\u00C9\u0302\u00CA\u0303\u1EBC\u0304\u0112"d~ -"\u0306\u0114\u0307\u0116\u0308\u00CB\u0309\u1EBA\u030C\u011A\u030F\u0204\u0311\u0206\u0323\u1EB8\u0327\u0228\u0328"d~ -"\u0118\u032D\u1E18\u0330\u1E1A\u0307\u1E1E\u0301\u01F4\u0302\u011C\u0304\u1E20\u0306\u011E\u0307\u0120\u030C\u01E6"d~ -"\u0327\u0122\u0302\u0124\u0307\u1E22\u0308\u1E26\u030C\u021E\u0323\u1E24\u0327\u1E28\u032E\u1E2A\u0300\u00CC\u0301"d~ -"\u00CD\u0302\u00CE\u0303\u0128\u0304\u012A\u0306\u012C\u0307\u0130\u0308\u00CF\u0309\u1EC8\u030C\u01CF\u030F\u0208"d~ -"\u0311\u020A\u0323\u1ECA\u0328\u012E\u0330\u1E2C\u0302\u0134\u0301\u1E30\u030C\u01E8\u0323\u1E32\u0327\u0136\u0331"d~ -"\u1E34\u0301\u0139\u030C\u013D\u0323\u1E36\u0327\u013B\u032D\u1E3C\u0331\u1E3A\u0301\u1E3E\u0307\u1E40\u0323\u1E42"d~ -"\u0300\u01F8\u0301\u0143\u0303\u00D1\u0307\u1E44\u030C\u0147\u0323\u1E46\u0327\u0145\u032D\u1E4A\u0331\u1E48\u0300"d~ -"\u00D2\u0301\u00D3\u0302\u00D4\u0303\u00D5\u0304\u014C\u0306\u014E\u0307\u022E\u0308\u00D6\u0309\u1ECE\u030B\u0150"d~ -"\u030C\u01D1\u030F\u020C\u0311\u020E\u031B\u01A0\u0323\u1ECC\u0328\u01EA\u0301\u1E54\u0307\u1E56\u0301\u0154\u0307"d~ -"\u1E58\u030C\u0158\u030F\u0210\u0311\u0212\u0323\u1E5A\u0327\u0156\u0331\u1E5E\u0301\u015A\u0302\u015C\u0307\u1E60"d~ -"\u030C\u0160\u0323\u1E62\u0326\u0218\u0327\u015E\u0307\u1E6A\u030C\u0164\u0323\u1E6C\u0326\u021A\u0327\u0162\u032D"d~ -"\u1E70\u0331\u1E6E\u0300\u00D9\u0301\u00DA\u0302\u00DB\u0303\u0168\u0304\u016A\u0306\u016C\u0308\u00DC\u0309\u1EE6"d~ -"\u030A\u016E\u030B\u0170\u030C\u01D3\u030F\u0214\u0311\u0216\u031B\u01AF\u0323\u1EE4\u0324\u1E72\u0328\u0172\u032D"d~ -"\u1E76\u0330\u1E74\u0303\u1E7C\u0323\u1E7E\u0300\u1E80\u0301\u1E82\u0302\u0174\u0307\u1E86\u0308\u1E84\u0323\u1E88"d~ -"\u0307\u1E8A\u0308\u1E8C\u0300\u1EF2\u0301\u00DD\u0302\u0176\u0303\u1EF8\u0304\u0232\u0307\u1E8E\u0308\u0178\u0309"d~ -"\u1EF6\u0323\u1EF4\u0301\u0179\u0302\u1E90\u0307\u017B\u030C\u017D\u0323\u1E92\u0331\u1E94\u0300\u00E0\u0301\u00E1"d~ -"\u0302\u00E2\u0303\u00E3\u0304\u0101\u0306\u0103\u0307\u0227\u0308\u00E4\u0309\u1EA3\u030A\u00E5\u030C\u01CE\u030F"d~ -"\u0201\u0311\u0203\u0323\u1EA1\u0325\u1E01\u0328\u0105\u0307\u1E03\u0323\u1E05\u0331\u1E07\u0301\u0107\u0302\u0109"d~ -"\u0307\u010B\u030C\u010D\u0327\u00E7\u0307\u1E0B\u030C\u010F\u0323\u1E0D\u0327\u1E11\u032D\u1E13\u0331\u1E0F\u0300"d~ -"\u00E8\u0301\u00E9\u0302\u00EA\u0303\u1EBD\u0304\u0113\u0306\u0115\u0307\u0117\u0308\u00EB\u0309\u1EBB\u030C\u011B"d~ -"\u030F\u0205\u0311\u0207\u0323\u1EB9\u0327\u0229\u0328\u0119\u032D\u1E19\u0330\u1E1B\u0307\u1E1F\u0301\u01F5\u0302"d~ -"\u011D\u0304\u1E21\u0306\u011F\u0307\u0121\u030C\u01E7\u0327\u0123\u0302\u0125\u0307\u1E23\u0308\u1E27\u030C\u021F"d~ -"\u0323\u1E25\u0327\u1E29\u032E\u1E2B\u0331\u1E96\u0300\u00EC\u0301\u00ED\u0302\u00EE\u0303\u0129\u0304\u012B\u0306"d~ -"\u012D\u0308\u00EF\u0309\u1EC9\u030C\u01D0\u030F\u0209\u0311\u020B\u0323\u1ECB\u0328\u012F\u0330\u1E2D\u0302\u0135"d~ -"\u030C\u01F0\u0301\u1E31\u030C\u01E9\u0323\u1E33\u0327\u0137\u0331\u1E35\u0301\u013A\u030C\u013E\u0323\u1E37\u0327"d~ -"\u013C\u032D\u1E3D\u0331\u1E3B\u0301\u1E3F\u0307\u1E41\u0323\u1E43\u0300\u01F9\u0301\u0144\u0303\u00F1\u0307\u1E45"d~ -"\u030C\u0148\u0323\u1E47\u0327\u0146\u032D\u1E4B\u0331\u1E49\u0300\u00F2\u0301\u00F3\u0302\u00F4\u0303\u00F5\u0304"d~ -"\u014D\u0306\u014F\u0307\u022F\u0308\u00F6\u0309\u1ECF\u030B\u0151\u030C\u01D2\u030F\u020D\u0311\u020F\u031B\u01A1"d~ -"\u0323\u1ECD\u0328\u01EB\u0301\u1E55\u0307\u1E57\u0301\u0155\u0307\u1E59\u030C\u0159\u030F\u0211\u0311\u0213\u0323"d~ -"\u1E5B\u0327\u0157\u0331\u1E5F\u0301\u015B\u0302\u015D\u0307\u1E61\u030C\u0161\u0323\u1E63\u0326\u0219\u0327\u015F"d~ -"\u0307\u1E6B\u0308\u1E97\u030C\u0165\u0323\u1E6D\u0326\u021B\u0327\u0163\u032D\u1E71\u0331\u1E6F\u0300\u00F9\u0301"d~ -"\u00FA\u0302\u00FB\u0303\u0169\u0304\u016B\u0306\u016D\u0308\u00FC\u0309\u1EE7\u030A\u016F\u030B\u0171\u030C\u01D4"d~ -"\u030F\u0215\u0311\u0217\u031B\u01B0\u0323\u1EE5\u0324\u1E73\u0328\u0173\u032D\u1E77\u0330\u1E75\u0303\u1E7D\u0323"d~ -"\u1E7F\u0300\u1E81\u0301\u1E83\u0302\u0175\u0307\u1E87\u0308\u1E85\u030A\u1E98\u0323\u1E89\u0307\u1E8B\u0308\u1E8D"d~ -"\u0300\u1EF3\u0301\u00FD\u0302\u0177\u0303\u1EF9\u0304\u0233\u0307\u1E8F\u0308\u00FF\u0309\u1EF7\u030A\u1E99\u0323"d~ -"\u1EF5\u0301\u017A\u0302\u1E91\u0307\u017C\u030C\u017E\u0323\u1E93\u0331\u1E95\u0300\u1FED\u0301\u0385\u0342\u1FC1"d~ -"\u0300\u1EA6\u0301\u1EA4\u0303\u1EAA\u0309\u1EA8\u0304\u01DE\u0301\u01FA\u0301\u01FC\u0304\u01E2\u0301\u1E08\u0300"d~ -"\u1EC0\u0301\u1EBE\u0303\u1EC4\u0309\u1EC2\u0301\u1E2E\u0300\u1ED2\u0301\u1ED0\u0303\u1ED6\u0309\u1ED4\u0301\u1E4C"d~ -"\u0304\u022C\u0308\u1E4E\u0304\u022A\u0301\u01FE\u0300\u01DB\u0301\u01D7\u0304\u01D5\u030C\u01D9\u0300\u1EA7\u0301"d~ -"\u1EA5\u0303\u1EAB\u0309\u1EA9\u0304\u01DF\u0301\u01FB\u0301\u01FD\u0304\u01E3\u0301\u1E09\u0300\u1EC1\u0301\u1EBF"d~ -"\u0303\u1EC5\u0309\u1EC3\u0301\u1E2F\u0300\u1ED3\u0301\u1ED1\u0303\u1ED7\u0309\u1ED5\u0301\u1E4D\u0304\u022D\u0308"d~ -"\u1E4F\u0304\u022B\u0301\u01FF\u0300\u01DC\u0301\u01D8\u0304\u01D6\u030C\u01DA\u0300\u1EB0\u0301\u1EAE\u0303\u1EB4"d~ -"\u0309\u1EB2\u0300\u1EB1\u0301\u1EAF\u0303\u1EB5\u0309\u1EB3\u0300\u1E14\u0301\u1E16\u0300\u1E15\u0301\u1E17\u0300"d~ -"\u1E50\u0301\u1E52\u0300\u1E51\u0301\u1E53\u0307\u1E64\u0307\u1E65\u0307\u1E66\u0307\u1E67\u0301\u1E78\u0301\u1E79"d~ -"\u0308\u1E7A\u0308\u1E7B\u0307\u1E9B\u0300\u1EDC\u0301\u1EDA\u0303\u1EE0\u0309\u1EDE\u0323\u1EE2\u0300\u1EDD\u0301"d~ -"\u1EDB\u0303\u1EE1\u0309\u1EDF\u0323\u1EE3\u0300\u1EEA\u0301\u1EE8\u0303\u1EEE\u0309\u1EEC\u0323\u1EF0\u0300\u1EEB"d~ -"\u0301\u1EE9\u0303\u1EEF\u0309\u1EED\u0323\u1EF1\u030C\u01EE\u0304\u01EC\u0304\u01ED\u0304\u01E0\u0304\u01E1\u0306"d~ -"\u1E1C\u0306\u1E1D\u0304\u0230\u0304\u0231\u030C\u01EF\u0300\u1FBA\u0301\u0386\u0304\u1FB9\u0306\u1FB8\u0313\u1F08"d~ -"\u0314\u1F09\u0345\u1FBC\u0300\u1FC8\u0301\u0388\u0313\u1F18\u0314\u1F19\u0300\u1FCA\u0301\u0389\u0313\u1F28\u0314"d~ -"\u1F29\u0345\u1FCC\u0300\u1FDA\u0301\u038A\u0304\u1FD9\u0306\u1FD8\u0308\u03AA\u0313\u1F38\u0314\u1F39\u0300\u1FF8"d~ -"\u0301\u038C\u0313\u1F48\u0314\u1F49\u0314\u1FEC\u0300\u1FEA\u0301\u038E\u0304\u1FE9\u0306\u1FE8\u0308\u03AB\u0314"d~ -"\u1F59\u0300\u1FFA\u0301\u038F\u0313\u1F68\u0314\u1F69\u0345\u1FFC\u0345\u1FB4\u0345\u1FC4\u0300\u1F70\u0301\u03AC"d~ -"\u0304\u1FB1\u0306\u1FB0\u0313\u1F00\u0314\u1F01\u0342\u1FB6\u0345\u1FB3\u0300\u1F72\u0301\u03AD\u0313\u1F10\u0314"d~ -"\u1F11\u0300\u1F74\u0301\u03AE\u0313\u1F20\u0314\u1F21\u0342\u1FC6\u0345\u1FC3\u0300\u1F76\u0301\u03AF\u0304\u1FD1"d~ -"\u0306\u1FD0\u0308\u03CA\u0313\u1F30\u0314\u1F31\u0342\u1FD6\u0300\u1F78\u0301\u03CC\u0313\u1F40\u0314\u1F41\u0313"d~ -"\u1FE4\u0314\u1FE5\u0300\u1F7A\u0301\u03CD\u0304\u1FE1\u0306\u1FE0\u0308\u03CB\u0313\u1F50\u0314\u1F51\u0342\u1FE6"d~ -"\u0300\u1F7C\u0301\u03CE\u0313\u1F60\u0314\u1F61\u0342\u1FF6\u0345\u1FF3\u0300\u1FD2\u0301\u0390\u0342\u1FD7\u0300"d~ -"\u1FE2\u0301\u03B0\u0342\u1FE7\u0345\u1FF4\u0301\u03D3\u0308\u03D4\u0308\u0407\u0306\u04D0\u0308\u04D2\u0301\u0403"d~ -"\u0300\u0400\u0306\u04D6\u0308\u0401\u0306\u04C1\u0308\u04DC\u0308\u04DE\u0300\u040D\u0304\u04E2\u0306\u0419\u0308"d~ -"\u04E4\u0301\u040C\u0308\u04E6\u0304\u04EE\u0306\u040E\u0308\u04F0\u030B\u04F2\u0308\u04F4\u0308\u04F8\u0308\u04EC"d~ -"\u0306\u04D1\u0308\u04D3\u0301\u0453\u0300\u0450\u0306\u04D7\u0308\u0451\u0306\u04C2\u0308\u04DD\u0308\u04DF\u0300"d~ -"\u045D\u0304\u04E3\u0306\u0439\u0308\u04E5\u0301\u045C\u0308\u04E7\u0304\u04EF\u0306\u045E\u0308\u04F1\u030B\u04F3"d~ -"\u0308\u04F5\u0308\u04F9\u0308\u04ED\u0308\u0457\u030F\u0476\u030F\u0477\u0308\u04DA\u0308\u04DB\u0308\u04EA\u0308"d~ -"\u04EB\u0653\u0622\u0654\u0623\u0655\u0625\u0654\u0624\u0654\u0626\u0654\u06C2\u0654\u06D3\u0654\u06C0\u093C\u0929"d~ -"\u093C\u0931\u093C\u0934\u09BE\u09CB\u09D7\u09CC\u0B3E\u0B4B\u0B56\u0B48\u0B57\u0B4C\u0BD7\u0B94\u0BBE\u0BCA\u0BD7"d~ -"\u0BCC\u0BBE\u0BCB\u0C56\u0C48\u0CD5\u0CC0\u0CC2\u0CCA\u0CD5\u0CC7\u0CD6\u0CC8\u0CD5\u0CCB\u0D3E\u0D4A\u0D57\u0D4C"d~ -"\u0D3E\u0D4B\u0DCA\u0DDA\u0DCF\u0DDC\u0DDF\u0DDE\u0DCA\u0DDD\u102E\u1026\u1B35\u1B06\u1B35\u1B08\u1B35\u1B0A\u1B35"d~ -"\u1B0C\u1B35\u1B0E\u1B35\u1B12\u1B35\u1B3B\u1B35\u1B3D\u1B35\u1B40\u1B35\u1B41\u1B35\u1B43\u0304\u1E38\u0304\u1E39"d~ -"\u0304\u1E5C\u0304\u1E5D\u0307\u1E68\u0307\u1E69\u0302\u1EAC\u0306\u1EB6\u0302\u1EAD\u0306\u1EB7\u0302\u1EC6\u0302"d~ -"\u1EC7\u0302\u1ED8\u0302\u1ED9\u0300\u1F02\u0301\u1F04\u0342\u1F06\u0345\u1F80\u0300\u1F03\u0301\u1F05\u0342\u1F07"d~ -"\u0345\u1F81\u0345\u1F82\u0345\u1F83\u0345\u1F84\u0345\u1F85\u0345\u1F86\u0345\u1F87\u0300\u1F0A\u0301\u1F0C\u0342"d~ -"\u1F0E\u0345\u1F88\u0300\u1F0B\u0301\u1F0D\u0342\u1F0F\u0345\u1F89\u0345\u1F8A\u0345\u1F8B\u0345\u1F8C\u0345\u1F8D"d~ -"\u0345\u1F8E\u0345\u1F8F\u0300\u1F12\u0301\u1F14\u0300\u1F13\u0301\u1F15\u0300\u1F1A\u0301\u1F1C\u0300\u1F1B\u0301"d~ -"\u1F1D\u0300\u1F22\u0301\u1F24\u0342\u1F26\u0345\u1F90\u0300\u1F23\u0301\u1F25\u0342\u1F27\u0345\u1F91\u0345\u1F92"d~ -"\u0345\u1F93\u0345\u1F94\u0345\u1F95\u0345\u1F96\u0345\u1F97\u0300\u1F2A\u0301\u1F2C\u0342\u1F2E\u0345\u1F98\u0300"d~ -"\u1F2B\u0301\u1F2D\u0342\u1F2F\u0345\u1F99\u0345\u1F9A\u0345\u1F9B\u0345\u1F9C\u0345\u1F9D\u0345\u1F9E\u0345\u1F9F"d~ -"\u0300\u1F32\u0301\u1F34\u0342\u1F36\u0300\u1F33\u0301\u1F35\u0342\u1F37\u0300\u1F3A\u0301\u1F3C\u0342\u1F3E\u0300"d~ -"\u1F3B\u0301\u1F3D\u0342\u1F3F\u0300\u1F42\u0301\u1F44\u0300\u1F43\u0301\u1F45\u0300\u1F4A\u0301\u1F4C\u0300\u1F4B"d~ -"\u0301\u1F4D\u0300\u1F52\u0301\u1F54\u0342\u1F56\u0300\u1F53\u0301\u1F55\u0342\u1F57\u0300\u1F5B\u0301\u1F5D\u0342"d~ -"\u1F5F\u0300\u1F62\u0301\u1F64\u0342\u1F66\u0345\u1FA0\u0300\u1F63\u0301\u1F65\u0342\u1F67\u0345\u1FA1\u0345\u1FA2"d~ -"\u0345\u1FA3\u0345\u1FA4\u0345\u1FA5\u0345\u1FA6\u0345\u1FA7\u0300\u1F6A\u0301\u1F6C\u0342\u1F6E\u0345\u1FA8\u0300"d~ -"\u1F6B\u0301\u1F6D\u0342\u1F6F\u0345\u1FA9\u0345\u1FAA\u0345\u1FAB\u0345\u1FAC\u0345\u1FAD\u0345\u1FAE\u0345\u1FAF"d~ -"\u0345\u1FB2\u0345\u1FC2\u0345\u1FF2\u0345\u1FB7\u0300\u1FCD\u0301\u1FCE\u0342\u1FCF\u0345\u1FC7\u0345\u1FF7\u0300"d~ -"\u1FDD\u0301\u1FDE\u0342\u1FDF\u0338\u219A\u0338\u219B\u0338\u21AE\u0338\u21CD\u0338\u21CF\u0338\u21CE\u0338\u2204"d~ -"\u0338\u2209\u0338\u220C\u0338\u2224\u0338\u2226\u0338\u2241\u0338\u2244\u0338\u2247\u0338\u2249\u0338\u226D\u0338"d~ -"\u2262\u0338\u2270\u0338\u2271\u0338\u2274\u0338\u2275\u0338\u2278\u0338\u2279\u0338\u2280\u0338\u2281\u0338\u22E0"d~ -"\u0338\u22E1\u0338\u2284\u0338\u2285\u0338\u2288\u0338\u2289\u0338\u22E2\u0338\u22E3\u0338\u22AC\u0338\u22AD\u0338"d~ -"\u22AE\u0338\u22AF\u0338\u22EA\u0338\u22EB\u0338\u22EC\u0338\u22ED\u3099\u3094\u3099\u304C\u3099\u304E\u3099\u3050"d~ -"\u3099\u3052\u3099\u3054\u3099\u3056\u3099\u3058\u3099\u305A\u3099\u305C\u3099\u305E\u3099\u3060\u3099\u3062\u3099"d~ -"\u3065\u3099\u3067\u3099\u3069\u3099\u3070\u309A\u3071\u3099\u3073\u309A\u3074\u3099\u3076\u309A\u3077\u3099\u3079"d~ -"\u309A\u307A\u3099\u307C\u309A\u307D\u3099\u309E\u3099\u30F4\u3099\u30AC\u3099\u30AE\u3099\u30B0\u3099\u30B2\u3099"d~ -"\u30B4\u3099\u30B6\u3099\u30B8\u3099\u30BA\u3099\u30BC\u3099\u30BE\u3099\u30C0\u3099\u30C2\u3099\u30C5\u3099\u30C7"d~ -"\u3099\u30C9\u3099\u30D0\u309A\u30D1\u3099\u30D3\u309A\u30D4\u3099\u30D6\u309A\u30D7\u3099\u30D9\u309A\u30DA\u3099"d~ -"\u30DC\u309A\u30DD\u3099\u30F7\u3099\u30F8\u3099\u30F9\u3099\u30FA\u3099\u30FE\U000110BA\U0001109A\U000110BA\U0001109C"d~ -"\U000110BA\U000110AB\U00011127\U0001112E\U00011127\U0001112F\U0001133E\U0001134B\U00011357\U0001134C\U000114B0"d~ -"\U000114BC\U000114BA\U000114BB\U000114BD\U000114BE\U000115AF\U000115BA\U000115AF\U000115BB\U00011930\U00011938"d; -return t[]; -} - -} - diff --git a/phobos/std/internal/unicode_decomp.d b/phobos/std/internal/unicode_decomp.d deleted file mode 100644 index d596d48..0000000 --- a/phobos/std/internal/unicode_decomp.d +++ /dev/null @@ -1,3251 +0,0 @@ -//Written in the D programming language -/** - * License: $(HTTP boost.org/LICENSE_1_0.txt, Boost License 1.0). - * - * Authors: Dmitry Olshansky - * - */ -// !!! DO NOT EDIT !!! -// !!! Did you even read the comment? !!! -// This module is automatically generated from Unicode Character Database files -// https://github.com/dlang/phobos/blob/master/tools/unicode_table_generator.d -//dfmt off -module std.internal.unicode_decomp; -import std.internal.unicode_tables; - -@safe pure nothrow @nogc package(std): - - -static if (size_t.sizeof == 4) -{ -//23488 bytes -enum compatMappingTrieEntries = TrieEntry!(ushort, 8, 8, 5)(x" -000000000000004000000540", -x" -0000010000000A0000002360", -x" -020201000402030202020205070602020202020208020202000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000001000000030002000500040007000600080000000A0009 -000C000B00000000000D0000000F000E00000000001100100013001200150014001700160019001800000000001B001A -00000000000000000000001C00000000001D0000001E0000000000000000001F00000000000000000000000000000000 -000000000000000000000000000000000020000000000021000000000000002200230000000000240000000000000000 -0000000000000025000000260000002700000000000000280000000000000029000000000000002A000000000000002B -002C000000000000002D00000000002E0000002F00310030003300320000000000340000000000000000000000350000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000037003600000038000000000000000000000000000000000000000000000000 -00390000003B003A003D003C00000000003F003E0041004000430042004500440047004600490048004B004A004D004C -004F004E0051005000530052000000000055005400570056005900580000005A005C005B005E005D0060005F00610000 -006200000000000000000000000000000000000000630000006500640067006600000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000006800690000000000000000006A00000000000000000000000000000000 -00000000006B0000000000000000000000000000006C0000000000000000000000000000000000000000006D006E0000 -0070006F0072007100740073000000750077007600790078007B007A007D007C007E00000080007F0000008100000000 -00830082008500840087008600890088008B008A008D008C008F008E0091009000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000920000000000000000009300000000000000940000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000096009500000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000980097009A0099009C009B009E009D00A0009F00A200A100A400A3000000A5 -00A700A600A900A800AB00AA00AD00AC00AF00AE00B100B000B300B200B500B400B700B600B900B800BB00BA00BD00BC -00BF00BE00C100C000C300C200C500C400C700C600C900C800CB00CA00CD00CC00000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000CF00CE000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000D100D00000000000D2000000000000000000000000000000000000000000000000000000000000 -00000000000000D30000000000000000000000000000000000D4000000000000000000000000000000D5000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -00D600000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -0000000000D800D700D90000000000DA0000000000000000000000000000000000000000000000000000000000000000 -00DC00DB00DE00DD00E000DF00E200E100E400E300E600E500DB00E700E800DC00E900DE00EB00EA00EC00E200EE00ED -00F000EF00F200F100F400F300F600F50000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -00F7000000F900F800000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000FB00FA00FD00FC00FF00FE00000000 -000000000000000000000000000000000000000000000000000000000000000001010100010301020000010400000000 -010601050000010700000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000010800000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000010A0109010C010B010E010D0110010F -011201110114011301160115011801170000011900000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000100000000000000000000000000000015000006920000000000090000 -00000000030F0343120F000300000000031400480000078A03C603CE000004940570056D0586057305B005A605F80000 -062E062B0658063106E706E406F906EA0792000007AC07A907C207AF000007E608B4000008BA08B7096208CE00000000 -09AF09AC09C509B209EF09E50A4A00000A8C0A890AB60A8F0B490B460B580B4C0C4400000C610C5E0C770C6400000C9B -0D6B00000D710D6E0E0F0D850E1E000009C8058909CB058C0A0D05CE0A3E05EC0A4105EF0A4405F20A4705F50A71061A -000000000AA506470AB006520AB306550AD306750ABC065E0AFE069A0B0406A00B0706A30B0D06A90B1906BA00000000 -0B4F06ED0B5206F00B5506F30B6E070F000006F60B3A06D80B7607170BB1072E0743000007500BCF07460BDC07400BD2 -00000BCC078F0000079E0C4107980C500F300C4A000000000C8607D10C9107DC0CA507F0000000000D0A08450D20085B -0D10084B0D2E086F0D3508760D4C088D0D3B087C0D6008A90D5708A0000000000D7408BD0D7B08C40D8208CB0D9B08E4 -0D9E08E70DC709100DEC09420E120965097C097109820E2C060D0E32084306140CB107FC000000000000000000000000 -00000000000000000000000008F3000000000DAA00000000000000000000000000000000000000000000000000000000 -00000000000000000613060C07360A6A0BBC073D0786078305B70C35070309F607F30B6208EA0CA808D90DA108D50D90 -08DD0D8C08D10D9400000D8809E805A909E105A20E680E5D000000000B0A06A60BAB07280CCF081A0CD2081D0F070E82 -06090B790A6706100AFB0697000000000C3E078C09F205B30E650E5A0E6F0E6009F905BA09FC05BD0ABF06610AC20664 -0B6507060B6807090CAB07F60CAE07F90D13084E0D1608510DA408ED0DA708F00D49088A0D5D08A6000000000B2206C3 -00000000000000000000000009DE059F0ACC066E0C9E07E90C7E07C90C9407DF0C9707E20E18096B0000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -0EB70B100A14073E0EE10EDD0DE40EE90000069500000000000000000000000000000000000000000000000000000000 -00000000000000000000000000000000000000000000000000000000000000000012000F004B00240027000600000000 -0A2B0EB10B44084300000F0A000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000004001A002B00000000001D0000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -00000000000000000000000000000000000000000000000000000F2E0000000000000000000000540000000000000546 -0000000000000000001C000307410F450FC00FA0000010110000104A10991073000011E2000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000107C101A115210EC11D21176 -000012700000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -00000000126911DB12601232000012A811CB11471073106E1299107C0000124B00000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000124D120B00001255114D100A00000000 -106A00000000000000000000000000001313130D130A000000000000130100000000000000000000131F132B00001334 -000000000000000000000000000000001325000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000013790000000000000000000000000000 -00000000000000000000000000000000000000000000000000000000000000001361135B135400000000000013C60000 -0000000000000000137313810000139C0000000000000000000000000000000000000000000000000000000000000000 -00000000000000000000000013D013CD0000000000000000000000000000000013160000000013660000000000000000 -0000000000000000000000000000000013481304134B130700000000135E13100000000013E213DD13691319136E131C -0000000013761322137C1328138C132E0000000013EA13E513BD134313991331139F133713A2133A13AD133D00000000 -13B6134000000000000000000000000000000000000000000000000013ED000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000149414911497183A00001875 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -00000000000000000000000000000000000000000000000000000000000000000000000000000000149A00001906183D -000018BC00000000000000000000000000001918000018FD000000000000000000000000000000000000000000000000 -000000001915000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -192D00000000000000000000000000001936000000000000000019390000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -191E191B19241921192A1927193319300000000000000000000000000000000000000000194500000000194800000000 -000000000000000000000000000000000000000000000000193F193C1942000000000000000000000000000000000000 -000000000000000000000000000000000000000019570000000000000000195A00000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -194B00001951194E00000000000019540000000000000000000000000000000000001966196300000000196900000000 -0000000000000000000000000000000000000000000000001960195D0000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000196C0000000000000000000000000000000000000000 -00000000000000000000000000000000000000001975196F000019720000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000001978000000000000000000000000 -00000000000000000000000000000000000000000000000000000000000000000000197B000000000000000019850000 -000019881981197E00000000000000000000000000000000000000000000000000000000000000000000000000000000 -00000000000000000000000000000000000000001991198B0000198E0000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000001994199A19970000199E00000000000000000000000000000000 -000000000000000000000000000000000000000019A10000000000000000000000000000000000000000000000000000 -00000000000000000000000000000000000000000000000000000000000000000000000019AA00000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -00000000000000000000000000000000000000000000000019A719A40000000000000000000000000000000000000000 -0000000000000000000019AD000000000000000000000000000000000000000000000000000000000000000000000000 -0000000019B200000000000000000000000000000000000019B500000000000000000000000019B80000000019BB0000 -0000000000000000000019BE000000000000000000000000000000000000000019AF0000000000000000000000000000 -0000000019C1000019C4000019DC19E019E319E700000000000000000000000019C70000000000000000000000000000 -000000000000000000000000000000000000000019CD00000000000000000000000000000000000019D0000000000000 -00000000000019D30000000019D600000000000000000000000019D90000000000000000000000000000000000000000 -19CA0000000000000000000000000000000000000000000000000000000019EA00000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -00000000000000000000000000000000000000000000000000000000000000000000000000000000000019ED00000000 -00000000000000000000000000001AE300001AE600001AE900001AEC00001AEF0000000000001AF20000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -00000000000000000000000000000000000000001AF500001AF80000000000001AFE1AFB1B0100000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000E580568000005D5062905E606870E7C06CF06AC071A0607 -07230734000007810E8B07A7076D06AF056B0890069207730E910E8F093A1B040A800A310EA30EA106020EA50B7C0000 -0E7805D30E97078A1B081B060BA60CD61B0C0A27086D0EC911471B0E114B1149129B12990A140AE3086D0A2711491147 -1299124D0000129B00000000000000000000000000000000000000000000000000001388000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000E9300000E9909A30EA50E6B0EA90ADC0EB50EAD0EBD0EBB1B100EBF0EC50F140F161B120ECB0ECD0ED10ECF -0ED50ED30EEB0EDB0E800EED0EF50EF30EF71B0A06110EF90F030F0111CB0F050A0A05CB0A1605DA0A1905DD0A1C05E0 -0A4D05FB0A6E06170A74061D0A7D06260A7706200A7A06230AA8064A0AAC064E0AD606780AD9067B0ACF06710AF20684 -0B01069D0B1C06BD0B2506C60B1F06C00B2806C90B2B06CC0B7107120B5B06FC0BA807250BAE072B0BB407310BD50749 -0BD8074C0BE207560BDF07530C1507750C1807780C1B077B0C4707950C4D079B0C5607A40C5307A10C7A07C50C8207CD -0C8907D40C8D07D80CEF08380CF2083B0D0D08480D1908540D1C08570D23085E0D3808790D4208830D3108720D3E087F -0D4508860D51089D0D5A08A30D6608AF0D6308AC0DC4090D0DCD09160DCA09130D7708C00D7E08C70DDE09270DE1092A -0DE6093C0DE9093F0DF209480DEF09450DF8094B0E0409570E07095A0E1B096E0E2F097F0E3809880E3B098B0D540B2E -0E240DF50D3809A9000000000000000009FF05C009EC05AD09B9057A09B5057609C1058209BD057E0A0205C309D20593 -09CE058F09DA059B09D605970A0605C70AC506670AB9065B0AA206440A9606380A9206340A9E06400A9A063C0AC8066A -0B5F07000B6B070C0CC808130CA207ED0C6B07B60C6707B20C7307BE0C6F07BA0CCB08160CB808030CB407FF0CC0080B -0CBC08070CC4080F0DC1090A0D9808E10DB108FA0DAD08F60DB909020DB508FE0DBD09060E0C095F0E2709770E210974 -0E150968000000000000000000000000111B10F9111E10FC112711051130110E0F700F4E0F730F510F7C0F5A0F850F63 -11601155116311581167115C000000000FAE0FA30FB10FA60FB50FAA00000000119F117D11A2118011AB118911B41192 -0FE50FC30FE80FC60FF10FCF0FFA0FD811F911EA11FC11ED120011F1120411F5102C101D102F10201033102410371028 -12401235124312381247123C000000001058104D105B1050105F10540000000012871278128A127B128E127F12921283 -107F00001082000010860000108A000012D112AF12D412B212DD12BB12E612C410BE109C10C1109F10CA10A810D310B1 -10EC10E51152114F1176116F11D211CF1232122F1260125D12A812A1000000001139111711221100112B110911341112 -0F8E0F6C0F770F550F800F5E0F890F6711BD119B11A6118411AF118D11B8119610030FE10FEC0FCA0FF50FD30FFE0FDC -12EF12CD12D812B612E112BF12EA12C810DC10BA10C510A310CE10AC10D710B510F310F6114410E8000010EF1140113D -0F480F4B0F450F42002A0F92002A11CD0020005111C811720000117911C411C10FA00F9D0FC00FBD002D100700350031 -11D511D811E211DE0000000011E61208101410171011100E003C000000440040126312661270126C1252124F12741296 -1076107910731070001810670998001C0000000012FA12A4000012AB12F612F3104A104710991096000310E000000039 -00010001000100010001000100010001000100010000000100000000000000001B1600000000000000000000004E0000 -00000000000000000000000000000000000000000000000002FC02FA000002FF00000000000000000000000000010000 -000000001B1C000000001B1F1B2B1B2800000000000000000000008F0000000C00000000000000000000000005630000 -009205600000000000000000000000000000000000000000000000001B23000000000000000000000000000000010000 -00000000000000000000000000000000000000000000000000000000000000000AE30305000000000374036503920383 -03B003A11B5A02F400A105440B3E00A503140305030F0343037403650392038303B003A11B5A02F400A10544000000A5 -0A8006920B44078A0B100EA10A2B0B7C0B3E05D308430CD600000BA60000000000000000000000000000000000000000 -000008420000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -09A509A10E5005E30A2100000E7E0A250E53000006AC060206AC06AC0E740B1006CF06CF0A2B07340781000000000789 -06AF0000076D083E076D076D00000000089208660000089A0000060A0000077F0000060A05B0071A05E305D50A800000 -067E06290723000013FF078A1415140D0AE3141A068000001149124B10630F9700001B580000000005E600000A800A31 -073E0AE300000000000000000000000003E203DA03CA03C103D204550498045903D604CF03DE04E704EB049C03BE0511 -06D106CF06DE06D4091B06B20922091E068206E10953095005E30734072305E60B330AE30B400B360DD2086D0DD90DD5 -0B440B430E000DFD09A30A2B05D30A3100000000000000000000000000000000030D0000000000000000000000000000 -00000000000000000000000000000000000000001B3A1B33000000000000000000000000000000000000000000000000 -00000000000000000000000000001B3F0000000000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000001B4200001B451B4800000000000000000000000000000000 -00000000000000000000000000000000000000000000000000001B4D000000001B5200000000000000001B5500000000 -0000000000000000000000000000000000000000000000000000000000000000000000000000000000001B5C00001B5F -00000000000000001B651B621B6E000000001B7100000000000000000000000000000000000000000000000000000000 -1B7500000000000000001B781B7B00001B7E000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000055600001B84000000000000000000000000000000001B810000055B054A -1B8A1B87000000001B901B8D000000001B961B930000000000000000000000001B9C1B99000000001BA81BA500000000 -1BAE1BAB0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000001BBA1BB71BC01BBD00000000000000000000000000000000 -000000000000000000000000000000001BA21B9F1BB41BB10000000000000000000000001BC61BC31BCC1BC900000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -1BE4000000001BE600000000000000000000000000000000000000000000000000000000000000000000000000000000 -034303140365030F0383037403A10392031C03B00342032F036403550382037303A0039103F703AF00D900A300E600E2 -00EE00EA00F600F200A700FA00B100AC00BB00B600C500C000CF00CA00DD00D403460319036803590386037703A40395 -031F03B303450332036703580385037603A3039403FA03B2016A01660172016E017A01760182017E018A01860192018E -019A019601A2019E01AA01A601B201AE01BA01B601C201BE01CA01C605D5056805E605E3067E062906AC0687060706CF -0734071A0781072306AF07A7076D083E0890086106B2056B06820773060A095D093A06920A3109A30ADC0A800B100602 -073E0AE30A2B0B7C0B3E05D30CD6078A0A1405D80BA60843086D0A270B440DE406110695000003050000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000001B69000000000000000000000000000000000000000000000000000000000000000000000000 -00000000000000000000000000000000000000000000000000000000000000000000000000000000054F054200000552 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -00000000000000000000000000000000000000000000000000001BD90000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000000000000000000006B2073E00000000 -000000000000000000000000000000000000000000000000000000001BDE000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -00000000000000000000000000000000000000000000000000000000232E000000000000000000000000000000000000 -000000000000000000000000000000000000000027640000000000000000000000000000000000000000000000000000 -1FAA1F981FB81FB01FC21FBC1FCA1FC61FFD1FD0201120092021201920392029203D203B206B205720772073207F2079 -208F208D209D209B20A920A1210120F7212B21212131212F214A213921662160217C21762180217E218E218C21AA21A4 -21B221AE21C121BA21DB21C521E121DD21E721E321F321EB223D220122492247228B228922972295229F229B032522A3 -032922C3231222D523202318232C232623322330233623342394233823BE23B823C223C023C823C423D423CA23E623E2 -24082406240E240C241424122428242624382436243C243A24582446245C245A248E246E24A0249024AC24A824CE24BC -24F424F0250424FE250C25082510250E251E251C2530252C253625342540253C25442542254A254825A4259A25C025BE -25DA25C425E225DE260425E6260A2606260E260C262626242634262A26442636264A2648266C265C267826762692267C -269C269426B426AE26C826BC26CE26CC26D226D026DA26D826EC26DE26F026EE26FE26FC270C27022710270E27162714 -271A27182724271C27322730273A2738273E273C27442742274C274627542750275A2756275E275C2766276200000000 -000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000001BF82081207F000020830000000000000000 -00000000000000000000000000000000000000000000000000001C2D00001C3000001C3300001C3600001C3900001C3C -00001C3F00001C4200001C4500001C4800001C4B00001C4E1C5100001C5400001C570000000000000000000000000000 -1C5D1C5A1C60000000001C631C691C661C6C000000001C6F1C781C750000000000000000000000000000000000000000 -00000000000000000000000000000000000000000000000000001C2A0000000000000000008700000000008A1C7B1C82 -00000000000000000000000000000000000000000000000000001CD500001CF200001CA500001D4100001D4A00001D5E -00001D6E00001D7900001D7E00001D8B00001D9000001D9A1DA100001DA600001CBF0000000000000000000000000000 -1DD51DCC1DEA000000001DF11E101E061E25000000001E2D1D591E500000000000000000000000000000000000000000 -00000000000000000000000000000000000000000000000000001CB01EF300001F011EFC00001F06000000001D501F0F -000000000000000000000000000000000000000000000000000000000000000019EF00001ABB19F41ABD19F619FB1ABF -1A021A001AC31AC11AC71AC51ACB1AC91A071A511A111A0C1A131A5B1A1A1A181A2C1A221A391A2E1A431A3E19F21A48 -1A851A831A891A871A8D1A8B1A371A8F1A951A931A991A971A9D1A201AA11A9F1AA51AA31AA91A2A1A4D1A7F1ACD1A4F -1AD11ACF1AD51AD31AD91AD71ADB1A531A551ADD1A591A571A5F1A5D1A631A611A671A651A6B1A691A6F1A6D1A731A71 -1A771A751AE11ADF1A7B1A791AAB1A7D1AAF1AAD1AB31AB11AB71AB500001AB9000000001FC61F9820F91F9E1FAC1FA0 -24161FA21FA61FBC213E1F9A1FD0210301D701CE01E901E001FB01F2020D0204023302250245023C0257024E01DB01D2 -01ED01E401FF01F6021102080237022902490240025B02520216022E0000021E0270026002A002680288027402840264 -0290026C02C402B002B802C002A402EC02BC02AC02D002B402C8029802D402E40278028C02A8029C027C02CC029402E8 -028002D802E002DC21C320D324B022950000000000000000000000000000000004060831041C04110432042704400439 -044E04470475046E047F047C0485048219F619EF1A0219FB1A0C1A071A1A1A131A2E1A221A3E1A391A481A4319F819F1 -1A0419FD1A0E1A091A1C1A151A301A241A401A3B1A4A1A451A271A3300001A1F1FC61F9820F91F9E20131FC820111F9C -207F1FBE2394032922D523382101267C22ED032522F222CD23CE20B9247C26102494205D214A241A1FFB265423482091 -1FDD26E0213C20251FAC1FA021AC1FA2207B20B32164216A1FDB24422089261A04882135048E048B04B1049104B704B4 -04BD04BA04C304C004C904C604E404CC034E033B04D604A3050304F2052905180327053A034D033A0A8506B407390A82 -1CA01C871CB91CAE1CD31CC91D221CF01D481D3F1D6C1D5C1D7C1CCD1D8E1D891D9F1CAA1C8D1DA41DBE1D551D3D1DC0 -1DCA1DBC1E041DE81E4E1E231E8B1CE01E9E1CD11EB31EAF1EC81EC01CE41ECA1EE31C9C1EF11D0C1EFF1EFA1FD81F04 -1C8F1C891C9A1C941CA81CA21CBB1CB31CCB1CC21CE21CCF1CEB1CE61CDD1CD81CFA1CF51CFF1D061D0E1D0B1D1C1D15 -1D2C1D101D3A1D331D531D441D611D571D711D661D851D811DA91D931D301DB21DC61DBB1DD81DE41DCF1DDF1DFB1DF4 -1DED1E001E1A1E131E1F1E091E301E441E4A1E341E3E1E391E5B1E281E671E531E6A1E611E741E6E1E7D1E791E851E81 -1E921E8D1EA01E951D171EA41EBA1EB51ECE1EC21ED81ED31EE51EDD1D1E1EE8033F031103610352037F0370039D038E -03BB03AC033E032B03600351037E036F039C038D03BA03AB040D0402042304180B12042E056A0A560C5B0A120A5C0CE9 -0A630A5F21BC06DB213B22B222EF22AB0C290CDD0BED1211071C0B7E0689075B0B8F0A290C2C0CE0121D121406010BF9 -0B81068D068C0764122008960A590C000AEF0B9712230C380A330C030C060B9A0A370A360C0A0B9D0A3B0A3A0C1E0BA1 -06910C2207680B850CF906900D030CFD0C3B0CEC0C1212260C2F0CE30BF01217076F0B890C320CE60BF3121A07720B8C -077E0BB705D7099C0A300A2D05E805FF06940A5306AE0B16071F0B3D0BA507220BC20BBF0BC90BC50BFC0BF608230C0E -08260CD80D28082E0939086C05D1092D034A0337036C035D038A037B03A80399032303B703490336036B035C0389037A -03A7039803FE03B604140409042A041F043C0435044A0443047104510AF7047800000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000000000000000000013B913B200000000 -00000000000000000000000000000000000000000000000000000000000000000000276E000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -00000000067E05E30000083E000000000E7A0E7200000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000002772276C27740EC1 -000000000000000000000000000000000EFB000000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000022C52608261C26361FAE2378276220AB2144276220DD267C2239214224FC2434 -25B62598265A25D0234C230623EE23A22670257C1FC0270623102095259623B42198272E258E2388225325D825BC252A -22CF21D123DA23581FE326602065202F230C2287244423B2259225082630259E271E26C42462272C24DE2482268A256E -25F2273221DF211F251A24BA246623CC26C026182186211D2362230824D6237C26A024EA252020632033203524E02498 -26A4257A2255260025F8230621701FB423E4220B207524241FEB246A1FA421FD22932342209F24D4244A211525EA257E -26482328225D233E226D255A1FCE24222031200F24CA22F625F025462069267A214A20C322A121D9246C238A270A2698 -2740273422C120572642231E222D21BF2283223B23A8238224922400251624E42582263A268C264E2059204323A020CD -25EA25C6220721CF2324226923DE24B820FB1FD621A2217023EC220D250223FA267E251226C626BE1FE526E42674248C -221926B21FF71FC42182217223062299243223AE2658258822B9275E2055269E22E522DF23702354242023F224D22460 -201326EA26A6224321941FEF263C2364221D21F523E422E9204726AA218A20BB22D722AE234622FC242A23F025C824F8 -267825CE207D26B820BD2372240223B026AC259027362722236022E324A8252E24C024AE239C23D81FD225FE20492562 -21C7204124C62257234E2168264022BF26A225C021D525DE20E91FFF0000000000002113000022B720370000243E23DC -247E2470248A248024C226CA0000250400002594000025F626500000000026660000000026F626F4272A26F826B02662 -1FF91FE7206120032087206720EF20E1211120F3218821172213218C222B2227228D223722BB22A5235A22F823802368 -23BA23AA246423F4247622F224782474247C247A2488248624A4249A24E424B224EC24E8250A24F6254C25322580254C -25E025D425FC25FA2620261E2650264C26DC26BA220F26E8253E283300000000202D1FA81FE1200D20172001206D205F -20D720E120ED20DF211B211321462140215C215421D721D321FF21EF22232217222B221B2237222522712245227B2277 -22B7228F22D122CF232022DB235423282376237A238E2380245623AA23D623BC23FC23DC241C240A2430242E2440243E -245224482468245024B224A624DA24BE24F224E42564250A25B2257825DC25D625EC25E025EE25F625F825FA25FC25F4 -262026022656263E2682267226BA26A826D426CA26E226DC271226E828092762281B28071F501F3C284F1F5228912861 -276027580000000000000000000000000AE20ADE0AE10AE60D2B0AE500000D2B00000000000000000000000000000000 -0000000013F9000013F313F013F613FC000000000000000014300000147D000013FF1451141F141A140B143914621446 -02F4147814751472146E146A14041401140F1407141C1417142714210000142A1433142D143B14360000144300001448 -144E144B1453000000001456145F145C146714641424147A143E1412140A145918C318C318C918C918C918C918CB18CB -18CB18CB18CF18CF18CF18CF18C718C718C718C718CD18CD18CD18CD18C518C518C518C518E718E718E718E718E918E9 -18E918E918D318D318D318D318D118D118D118D118D518D518D518D518D718D718D718D718DD18DD18DB18DB18DF18DF -18D918D918E318E318E118E118EB18EB18EB18EB18EF18EF18EF18EF18F318F318F318F318F118F118F118F118F518F5 -18F718F718F718F71918191818FB18FB18FB18FB18F918F918F918F9163C163C19151915000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -0000000018ED000018ED18ED18AE18ED18AA18AE18B218AA190618B2190D190D19001900190B190B18B618B618B618B6 -14C014C01878187818B818B8189C189C18AC18AC18A818A818B018B018B418B418A018B418A018A0190F190F190F190F -1880187C18A01890149F18A414A914A214BF14B614C914C214E314D8151514F8151D1518152F15261537153214DC14CD -157C14E014E7157F15B015A515C515B7163E160D1650164D1665165B16731670168F168A16B2162016EA16B516F016ED -16FE16F717061701171C170D1724171F172A17271730172D173E173317551741177317641624163714FC178B15041500 -150815D117D8150C17FA17EB18141803181C1817182A181F1847182D1855184E186F185E158F1872184215990064005D -0072006B00800079188C18881894189018A418A014B3148614B914B614C214BF14F514F2150F14F81518151515231520 -152915261532152F170116FE171F171C17301724173E173316371741178B162415D11790180017FD180E180318171814 -18581842185E185B186F1869187C1872188418801898189014A2149F14B614A914C914BC14E314D8151214F815371526 -14DC14CD157C14E015A514E715B715B0160D15C5163E16181650164D1665165B168A16701620168F16B516B216ED16EA -16F716F0170D1706172A17271730172D1755173317731764148B1637150014FC15D1150417EB17D8180317FA181C1811 -1830181F184E1847185E18551890169814B6189814F814BC1526151215C5152C15F715D4173016021637173318111803 -1698185E16D716D0168216DE16AA168516C416AD15D716C7160515DA153B160814D114A614EB14D5164514AD16681648 -15DF166B15F115E615F415F7161B15C21682166216AA168516C416AD15D716C7160515DA153B160814D114A614EB14D5 -164514AD1668164815DF166B15F115E615F415F7161B15C215DF166215F115E615D415F71673160215B015A515DF15B7 -15F115E6168A1673148E148E000000000000000000000000000000000000000000000000000000000000000000000000 -14DB14CC14DF14DB14FB14E6150314FF154E154E156C157015A815B315CC15AC15C815CC15D015D01610161015E91641 -15E215E915FA15FA15FE15FE165E16531676165E167A16761692167E169E169E16B816A216BC16C016F316F317141710 -176F17671758176B17761758177D177617A9177D17B617AD179E179617C117BD000000001822179A17EE182617DF17F2 -17E317DF1806180A1861186114D414AC14EE14D0150B14EA155615071552153A161415BA165715ED17811760184A1851 -17CC186517F6171817671710173A16A617C517DB1736175C17DB175C1565153E16FA17A2173614A51641169217E715BE -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -00000000000000000000000000000000000000000000000000000000000000001709163A14841489161E17B116961594 -16231635154216270000159C000000000000000000000000000000000000000000000000000000000000000000000000 -1BE002F605401BE2008D05461C26009302FF1C2800000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000001B1A02FC09961B1800A109960E3E00A51BFA0E421BF41BFE1BE81BF61BE41BEA -1BEC1BE61BF01BEE00001BF2098E0000000C0992000C000C0996000C099609961BE002F6000002FA05400546008D0093 -00A11B1A0E3E00A51BFA0E4200971BFE02F2009D02F802F40559054800000544009909900566009B0000000000000000 -16CA00570000005A0000006116CD006816D4006F16DB007616E2007D16E500841491148014941491183A14941497183A -187514971875187514821875149D1482149D149D14C5149D14C714C514C714C7151B14C7151B151B14A0151B14A014A0 -14A314A014A314A314AA14A314AA14AA158B14AA158D158B1487158D14B4148715A314B415A315A315DD15A315DD15DD -160B15DD160B160B164B160B164B164B166E164B166E166E1688166E16881688168D1688168D168D16B0168D16B016B0 -16E816B016E816E8170416E8170417041722170417221722159717221597159714B7159714B714B714BA14B714BA14BA -148C14BA148C148C1833148C14C0183314A714C014A714A7174914A7174D17491751174D174617510000174600000000 -008D000000970095009B0099009F009D00A500A102F402F202F802F6030302FA03140305030F03430374036503920383 -03B003A10546054005440548009305590568056605E305D5062905E60687067E06CF06AC071A06070723073407A70781 -083E06AF0861076D056B0890077306B2095D0682098E060A09920990099609940692099809A3093A0A800A3106020ADC -0AE30B100B7C073E05D30A2B078A0B3E05D80CD608430A140A270BA60DE4086D06950B440E3E06110E420E401BD50E44 -1BE21BD71BEE1BEC1F0B1BE01C851F041CAC1C9E1CC71CB71EBE1EB11D9D1EC61C871CC51CAE1CA01CC91CB91CF01CD3 -1D3F1D221D5C1D481CCD1D6C1D891D7C1CAA1D8E1DA41D9F1D551C8D1DC01DBE1DBC1D3D1DE81DCA1E231E041CE01E4E -1CD11E8B1EAF1E9E1EC01EB31ECA1EC81C9C1CE41D0C1EE31CB51EF1008B008819EF1A7F1ABB19F41ABD19F619FB1ABF -1A021A001AC31AC11AC71AC51ACB1AC91A071A511A111A0C1A131A5B1A1A1A181A2C1A221A391A2E1A431A3E00001A48 -000000001A8319F21A871A851A8B1A89000000001A8F1A8D1A931A371A971A95000000001A201A991A9F1A9D1AA31AA1 -000000001A2A1AA500001AA9000000000E480E4600090E4E0E4A0E4C00001B2F1B311BCF1B381B361BD11B3D00001BD3 -00000000000000000000000000000000000000000000000000000000000000000F3300000E630F350E950F0E0F1C0000 -0F2027760E9B0F1E1B140E9D0EA70E9F0EB30F280EAB0EAF0E740F100EB90F120F2A0EEF0EC30F2C277027D127D30EC7 -27D50EFD0ED70E6D05D80ED927D70EDF0EE50EE30F260EE727780F220EF10F2400001BDC0F180EFF0F0C0F1A0E870E85 -27D90E89000027DB00000000000000000000000000000000000000000000000000000000000000000000000000000000 -00000000000000000000000000000000000000000000277A0000277D0000000000000000000000000000000000000000 -000000002780000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000002786278300000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000278900000000278C00000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000279200000000278F00002795 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -00000000279B279800000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000279E00000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000027A427A1 -27AB27A727B327AF000027B7000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000027BB000027BE27C627C227C9000027CD000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -05D5056805E605E3067E062906AC0687060706CF0734071A0781072306AF07A7076D083E0890086106B2056B06820773 -060A095D093A06920A3109A30ADC0A800B100602073E0AE30A2B0B7C0B3E05D30CD6078A0A1405D80BA60843086D0A27 -0B440DE40611069505D5056805E605E3067E062906AC0687060706CF0734071A0781072306AF07A7076D083E08900861 -06B2056B06820773060A095D093A06920A3109A30ADC0A8000000602073E0AE30A2B0B7C0B3E05D30CD6078A0A1405D8 -0BA60843086D0A270B440DE40611069505D5056805E605E3067E062906AC0687060706CF0734071A0781072306AF07A7 -076D083E0890086106B2056B06820773060A095D093A06920A3109A30ADC0A800B100602073E0AE30A2B0B7C0B3E05D3 -0CD6078A0A1405D80BA60843086D0A270B440DE4061106950000056805E605E30000000000000687060700000000071A -0781000006AF07A70000083E0890086106B2056B06820773060A095D093A06920A3109A30ADC00000B100000073E0AE3 -0A2B0B7C0B3E05D30CD600000A1405D80BA60843086D0A270B440DE40611069505D5056805E605E3067E062906AC0687 -060706CF0734071A0781072306AF07A7076D083E0890086106B2056B06820773060A095D093A06920A3109A30ADC0A80 -0B100602073E0AE30A2B0B7C0B3E05D30CD6078A0A1405D80BA60843086D0A270B440DE40611069505D5056805E60000 -067E062900000687060700000734071A0781072306AF07A70000083E0890086106B2056B068207730000095D093A0692 -0A3109A30ADC0A800B100602073E0AE30A2B0B7C0B3E05D30CD6078A0A1405D80BA60843086D0A270B440DE406110695 -05D5056805E60000067E062900000687060706CF0734071A00000723000007A7000000000890086106B2056B06820773 -0000095D093A06920A3109A30ADC0A800B100602073E0AE30A2B0B7C0B3E05D30CD6078A0A1405D80BA60843086D0A27 -0B440DE40611069505D5056805E605E3067E062906AC0687060706CF0734071A0781072306AF07A7076D083E08900861 -06B2056B06820773060A095D093A06920A3109A30ADC0A800B100602073E0AE30A2B0B7C0B3E05D30CD6078A0A1405D8 -0BA60843086D0A270B440DE4061106950781072306AF07A7076D083E0890086106B2056B06820773060A095D093A0692 -0A3109A30ADC0A800B100602073E0AE30A2B0B7C0B3E05D30CD6078A0A1405D8060A095D093A06920A3109A30ADC0A80 -0B100602073E0AE30A2B0B7C0B3E05D30CD6078A0A1405D80BA60843086D0A270B440DE40611069505D5056805E605E3 -067E062906AC0687060706CF0734071A0781072306AF07A7076D083E0890086106B2056B06820773060A095D093A0692 -0A3109A30ADC0A800B100602073E0AE30A2B0B7C0B3E05D30CD6078A0A1405D80BA60843086D0A270B440DE406110695 -05D5056805E605E3067E062906AC0687060706CF0734071A0781072306AF07A70B440DE4061106950E8D0E7600000000 -0F950F400F990F970FB90F9B100A0FBB103B100C103F103D1043104110631045100A1065106C106A108E106E10921090 -1B50077F114710E3114B1149116B114D11CB116D120B11CD120F120D122B1229124B122D1255124D125912571299125B -129D129B1B4B129F11CB114D1299120B124B124D0F950F400F990F970FB90F9B100A0FBB103B100C103F103D10431041 -10631045100A1065106C106A108E106E109210901B50077F114710E3114B1149116B114D11CB116D120B11CD120F120D -122B1229124B122D1255124D125912571299125B129D129B1B4B129F11CB114D1299120B124B124D0F950F400F990F97 -0FB90F9B100A0FBB103B100C103F103D1043104110631045100A1065106C106A108E106E109210901B50077F114710E3 -114B1149116B114D11CB116D120B11CD120F120D122B1229124B122D1255124D125912571299125B129D129B1B4B129F -11CB114D1299120B124B124D0F950F400F990F970FB90F9B100A0FBB103B100C103F103D1043104110631045100A1065 -106C106A108E106E109210901B50077F114710E3114B1149116B114D11CB116D120B11CD120F120D122B1229124B122D -1255124D125912571299125B129D129B1B4B129F11CB114D1299120B124B124D0F950F400F990F970FB90F9B100A0FBB -103B100C103F103D1043104110631045100A1065106C106A108E106E109210901B50077F114710E3114B1149116B114D -11CB116D120B11CD120F120D122B1229124B122D1255124D125912571299125B129D129B1B4B129F11CB114D1299120B -124B124D12FF12FD0000000003140305030F0343037403650392038303B003A103140305030F03430374036503920383 -03B003A103140305030F0343037403650392038303B003A103140305030F0343037403650392038303B003A103140305 -030F0343037403650392038303B003A10000000000000000000000000000000000000000000000000000000000000000 -134E13461352135013591357136C1364137F137113861384138F138A139313911397139513A713A513AB13A913B413B0 -13C013BB13E0276A13C913C413D713E8134613DB1350134E13571352136413591371136C1384137F138F138A13971393 -13A713A513AB13A913B213B013D313B413C213C413D513CB13D927680000000000000000000000000000000000000000 -00000000000000000000000000000000149D1482158B14A01833000014A314B414A7166E1597172214BA14B7168D15A3 -160B16E81487170414C715DD14AA151B164B158D16B0168818F518BF18C118E5149D0000000014A00000148C14A30000 -14A700001597172214BA14B7168D15A3160B16E80000170414C715DD14AA151B164B000016B000000000000000000000 -00000000000014A00000000014A3000014A700001597000014BA0000168D15A3160B000000001704000015DD14AA0000 -164B000016B0000018F5000018C10000149D0000000014A00000148C14A3000014A7166E0000172214BA14B7168D15A3 -160B16E80000170414C715DD14AA151B164B000016B01688000018BF000018E5149D1482158B14A01833148C14A314B4 -14A7166E1597000014BA14B7168D15A3160B16E81487170414C715DD14AA151B164B158D16B016880000000000000000 -149D0000158B14A01833000014A314B414A7166E1597000014BA14B7168D15A3160B16E81487170414C715DD14AA151B -164B158D16B0168800000000000000000307030A03F1031604AB046804FA04DE0520050B000005310000000000000000 -010200FE010A01060112010E011A01160122011E012A01260132012E013A01360142013E014A01460152014E015A0156 -0162015E05E31BFC05E5076D0000093605D5056805E605E3067E062906AC0687060706CF0734071A0781072306AF07A7 -076D083E0890086106B2056B06820773060A095D076F06B1086908630933082A00000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000000000000761075E0000076C00000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000060600000000000000000000000000000000000000000000000000000000 -1D4D1C7200001D5C000000000000000000000000000000000000000000000000216222491DA620A521331FC6213E25E4 -22B01FCC229923A421F7204F229D201B24D820452612240E20C12125224F237E1F98226526521F9E1FAC21AC225F20B3 -224B262624A22484236E20B7032922CD20512418266E20E700000000000000001C001C181C0C1C041C101C1C1C081C20 -00001C1400000000000000000000000020AF21F900000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000003140305030F03430374036503920383 -03B003A10000000000000000000000001FB21FB627DD1FBA1FE71FDF1FED1FE91FF31FF11FF51FF927E51F1220052003 -201520071F1427DF201B200B202327E31FD42027202D202B203B28A11F18203F204D204B20532051205F1F1A20672061 -206F206D20752071208720852093208B2099209720992099239627EB20A720A320AD27ED20B520B120BF20C920C720C5 -20CF20CB20D520D120DB20D920DF20DB20E520E320FD20EB20FF20EF20F520F121232041210921072105210B210D210F -27EF2119212921272133212D2148213727F327F12150214E214C21521F1C2156215A1F1E215E215E216C27F52170216E -27F72174217A2178217E21E921841F202192218C27FB219027FD2196219A219C21A6219E1F2221A821B421B021B821B6 -27FF1F2421C91F2621CD21CB28B521D1280321DD2538280321E521E528111F2821ED28651F2A21F1220321FB22092205 -1F2E221122131F2C221528052221221F22212223222B2229222F22332235223122392237224121BD2251224D22632259 -2261280B2267225B2275226B2279280D226F2273227D1F30228522811F32227F2291228D22A7280F22B522C722BB1F38 -1F341F36201F201D22BD22C91F6A252222D122CF22DD22D3281922D922E71F3A22F422E1281D22F822EB22FA1F3C22FE -23022300230A2304230E281F23141F3E23162821231C1F40232823222823232A282527F92827233A23442340234A233C -235A235223562354235E235C23502829236C236A23761F42282B237423842366282F282D23902386238C238E23921F44 -2398239A27E1239E283123A6283523AC23BC23B6283723C623D223D0283B283923E823E023EA1F461F481F4823F823F6 -23FE23FC1F4A2404283D2410283F241E280124242841242C284528431F4E1F4C28492448284B2847244C284D244E244E -1F5224521F54245428511F562462245E1F582468247A2853285728552496248A249A1F5A249E249C285B285924AA285B -285D1F5C24B624B4285F1F5E1F6024C424C824CC286324D01F6224DC24E624E21F6424EE28692867286B1F66286D24FA -25062500286F250A25142871251828731F6828132526252425281F6C2875215828152877253828172646253A25501F6E -2552254E2554205B255825562879255C255E255A256A2568256C2560258025762570256625742572287F287B1F70287D -258625842887258A2881258C1F741F7228852883259C1F7625A0259E25A825A225AC25A625B225AA25B025AE1F7825B4 -25BA25B825C21F7A288925C425CC25CA25D21F7C288B1F161F7E288D25E81F80260225F4288F260A2616261426282622 -27E72893262C262E27E92632263E2638289728952664265E289926682680266A26882686268E2684289B26901F822696 -289D269A26B61F8426C221A028A3289F1F881F8628A526D628A71F8A26E226E228A926E61F8C26F2270026FA27082704 -28AB1F8E272027121F90272627281F921F9428AD28B128AF273A28B327441F96274A27482752274E28B7275600000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -00000000000000000000000000000000", -); -//12544 bytes -enum canonMappingTrieEntries = TrieEntry!(ushort, 8, 8, 5)(x" -000000000000004000000440", -x" -000001000000080000001000", -x" -020201000302020202020204020502020202020206020202000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000002000100040003000600050007000000090008 -000B000A00000000000000000000000000000000000D000C000F000E0000001000120011001400130000000000160015 -000000000000000000000000000000000017000000000000000000000000001800000000000000000000000000000000 -00000000000000000000000000000000001900000000001A000000000000001B001C00000000001D0000000000000000 -000000000000001E0000001F000000200000000000000021000000000000002200000000000000230000000000000024 -000000000000000000000000000000000000000000260025002800270000000000290000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -00000000000000000000000000000000002B002A0000002C000000000000000000000000000000000000000000000000 -00000000000000000000000000000000002E002D0030002F00320031003400330036003500380037003A0039003C003B -0000003D000000000000000000000000003E0000000000000040003F0000004100430042004500440047004600480000 -004900000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000004A00000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000004C004B004E004D0050004F00000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000520051005400530056005500580057 -005A0059005C005B005E005D0000005F0061006000000062000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000006400630000000000650000000000000000000000000000 -000000000000000000000000000000000000000000000066000000000000000000000000000000000067000000000000 -000000000000000000680000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000069000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000006B006A006C00000000006D00000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -00000000000000000000000000000000006F006E0071007000730072007500740077007600790078007B007A007D007C -0000007E0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000F000C00250012004F00450085000000A1009E00CB00A40121011E013301240188000001A0019D01B601A3000001DA -026D00000273027002F30287000000000322031F03380325036203580398000003B403B103DE03B7043704340446043A -049C000004B404B104CA04B7000004EE05840000058A0587060D059E061C0000033B0028033E002B0380006D038C0079 -038F007C0392007F0395008203A2008F0000000003CD00BA03D800C503DB00C803FB00E803E400D1040A00F7041000FD -0413010004190106041C010900000000043D01270440012A0443012D045C014900000130000000000462014F0471015D -0163000001700477016604840000047A000000000185000001940499018E04A8000004A20000000004D901C504E401D0 -04F801E400000000052F021B0545023105350221054B02370552023E0569025505580244057B02640572025B00000000 -058D02760594027D059B028405B4029D05B702A005E002C905F502DE061002F6030B0302031106280314062E00000631 -050401F000000000000000000000000000000000000000000000000002AC0000000005C3000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000000000000000000000560000013D0369 -01E7045002A304FB029205BA028E05A9029605A5028A05AD000005A1035B0048035400410653064A0000000004160103 -046B01570522020E05250211065F065C0000046500000000040700F4000000000496018203650052065006470656064D -036C0059036F005C03E700D403EA00D7045301400456014304FE01EA050101ED05380224053B022705BD02A605C002A9 -056602520578026100000000042501120000000000000000000000000351003E03F400E104F101DD04D101BD04E701D3 -04EA01D6061602FC0000000000000000000000000000000000000000000000000010000D066B00000000013700000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000066200000000 -000000000000000000000000000000010000000000000000063D00000645067006DF06C30000072C0000075907980778 -000008D10000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -0000000007810735084707E908C108670000092F00000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000092808CA091F08FD0000095F0000000009B40000000009B700000000 -0000000000000000000000000000000009CC09C609C300000000000009BA0000000000000000000009D809E4000009ED -0000000000000000000000000000000009DE000000000000000000000000000000000000000000000000000000000000 -00000000000000000000000000000000000000000000000000000000000000000A200000000000000000000000000000 -00000000000000000000000000000000000000000000000000000000000000000A0E0A080A050000000000000A410000 -00000000000000000A1A0A2600000A2F0000000000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000A470A440000000000000000000000000000000009CF000000000A110000000000000000 -0000000000000000000000000000000009FF09BD0A0209C0000000000A0B09C9000000000A4D0A4A0A1409D20A1709D5 -000000000A1D09DB0A2309E10A2909E7000000000A530A500A3E09FC0A2C09EA0A3209F00A3509F30A3809F600000000 -0A3B09F9000000000000000000000000000000000AC10ABE0AC40AC700000ACA00000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000AD300000ACD0000000000000000 -00000000000000000000000000000000000000000AD00000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000AE800000000000000000000000000000AF100000000000000000AF400000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000AD90AD60ADF0ADC0AE50AE20AEE0AEB00000000000000000000000000000000 -000000000B00000000000B03000000000000000000000000000000000000000000000000000000000AFA0AF70AFD0000 -0000000000000000000000000000000000000000000000000000000000000000000000000B1200000000000000000B15 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000B0600000B0C0B090000000000000B0F00000000000000000000000000000000 -00000B210B1E000000000B24000000000000000000000000000000000000000000000000000000000B1B0B1800000000 -0000000000000000000000000000000000000000000000000000000000000000000000000000000000000B2700000000 -0000000000000000000000000000000000000000000000000000000000000000000000000B300B2A00000B2D00000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -00000B330000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -00000B3600000000000000000B40000000000B430B3C0B39000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000000000000B4C0B4600000B4900000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -00000000000000000000000000000000000000000000000000000000000000000000000000000B4F0B550B5200000B59 -000000000B5F0000000000000000000000000000000000000B620000000000000000000000000B65000000000B680000 -000000000000000000000B6B00000000000000000000000000000000000000000B5C0000000000000000000000000000 -000000000B6E00000B71000000000B8900000B8C0000000000000000000000000B740000000000000000000000000000 -00000000000000000000000000000000000000000B7A0000000000000000000000000000000000000B7D000000000000 -0000000000000B80000000000B830000000000000000000000000B860000000000000000000000000000000000000000 -0B77000000000000000000000000000000000000000000000000000000000B8F00000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000B92 -00000B9500000B9800000B9B00000B9E0000000000000BA1000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000BA400000BA70000000000000BAD0BAA0BB00000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000037D006A038300700386007303890076 -039B0088039F008C03A5009203AE009B03A8009503AB009803D000BD03D400C103FE00EB040100EE03F700E4040400F1 -040D00FA041F010C042801150422010F042B0118042E011B045F014C0449013604680154046E015A04740160047D0169 -0480016C048A017604870173048D01790490017C0493017F049F018B04A5019104AE019A04AB019704CD01B904D501C1 -04DC01C804E001CC05290215052C02180532021E053E022A0541022D0548023405550241055F024B054E023A055B0247 -0562024E056C02580575025E0581026A057E026705DD02C605E602CF05E302CC059002790597028005E902D205EC02D5 -05EF02D805F202DB05FB02E405F802E1060102E7060402EA060702ED061902FF062B030E063403170637031A056F0431 -062205FE0659000000000000000000000372005F035F004C032C001903280015033400210330001D0375006203450032 -0341002E034D003A034900360379006603ED00DA03E100CE03CA00B703BE00AB03BA00A703C600B303C200AF03F000DD -044D013A04590146051B020704F501E104BE01AA04BA01A604C601B204C201AE051E020A050B01F7050701F3051301FF -050F01FB0517020305DA02C305B1029A05CA02B305C602AF05D202BB05CE02B705D602BF060A02F006250308061F0305 -061302F9000000000000000000000000081807F6081B07F908240802082D080B069B0679069E067C06A7068506B0068E -0855084A0858084D085C08510000000006D106C606D406C906D806CD000000000890086E08930871089C087A08A50883 -070406E2070706E5071006EE071906F708E808D908EB08DC08EF08E008F308E407470738074A073B074E073F07520743 -090B0900090E090309120907000000000767075C076A075F076E076300000000094609370949093A094D093E09510942 -0784000007870000078B0000078F000009880966098B096909940972099D097B07BD079B07C0079E07C907A707D207B0 -07E907E2084708440867086008C108BE08FD08FA091F091C095F09580000000008360814081F07FD082808060831080F -06B9069706A2068006AB068906B4069208AE088C0897087508A0087E08A9088707220700070B06E9071406F2071D06FB -09A60984098F096D0998097609A1097F07DB07B907C407A207CD07AB07D607B407F007F3084107E5000007EC083D083A -067306760670066D000006BD000008BC0640000008B908630000086A08B508B206C306C006DF06DC0BB307260BB90BB6 -08C408C708D108CD0000000008D508F7072F0732072C07290BBC00000BC20BBF09220925092F092B0919091609330955 -077B077E07780775063A0772031D063D0000000009B1095B0000096209AD09AA0759075607980795064307DF00000000 -0BC70BC50000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000079300000000004F01520000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000000000000BCC0BC90000000000000000 -0000000000000000000000000000000000000000000000000000000000000BCF00000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000BD200000BD50BD8 -0000000000000000000000000000000000000000000000000000000000000000000000000000000000000BDB00000000 -0BDE00000000000000000BE1000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000BE400000BE70000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000BEA00000000000000000BED0BF000000BF30000000000000000000000000000 -00000000000000000000000000000000000000000000000000000000000000000000000600000BF90000000000000000 -00000000000000000BF60000000900030BFF0BFC000000000C050C02000000000C0B0C08000000000000000000000000 -0C110C0E000000000C1D0C1A000000000C230C2000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000C2F0C2C0C350C32 -00000000000000000000000000000000000000000000000000000000000000000C170C140C290C260000000000000000 -000000000C3B0C380C410C3E000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000C47000000000C49000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -00000000000000000000000000000000000000000000000000000C440000000000000000000000000000000000000000 -000000000000000000000C4E00000C5100000C5400000C5700000C5A00000C5D00000C6000000C6300000C6600000C69 -00000C6C00000C6F0C7200000C7500000C7800000000000000000000000000000C7E0C7B0C81000000000C840C8A0C87 -0C8D000000000C900C960C93000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000C4B0000000000000000000000000000000000000C9900000000000000000000000000000000 -000000000000000000000C9F00000CA200000CA500000CA800000CAB00000CAE00000CB100000CB400000CB700000CBA -00000CBD00000CC00CC300000CC600000CC900000000000000000000000000000CCF0CCC0CD2000000000CD50CDB0CD8 -0CDE000000000CE10CE70CE4000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000C9C0CEA00000CF00CED00000CF30000000000000CF60FB71241124B125D0D83104313270E29 -0E9913270E4F12930F550E97116710CD11FD11E31279121510190FEB109D1069128911C70D8D12F30FF50E1D11E11079 -0EDB130911D910510F65121D120311890FBD0EFF108D10250D9D127D0E050DD90FF10F9510D3107711DD1171125911E7 -12FB12CF10E91307114D110712A111B9122F130B0F0B0E87117D112F10ED108312CB12490ECB0E85102F0FED11471047 -12B11159117F0E030DDD0DDF114F111512B511C50F67123D12350FEB0EBB0D8710950F270E1110C10DA510F10D7F0F1B -0F9D10110E23114510D70E7D122711C9126D10050F6F100D0F7B11A50D9110BF0DDB0DC3113D0FDB122D11950E091291 -0E9F0E370FA10F0710F3105312F712AB1313130D0FB50DF912690FFD0F490EF30F910F57106D104B111110AF11791153 -11CD126112A312710DFB0DE910670E411227120B0F230EFD10030F771091112D0E670D970EE50EBB109B0F29116B10A9 -1295117512D112C90D9F12DD128D110F0F3512C10DB10D8F0EC70EBD0FEB0F9F10CB1073127711D30FAD13230DF712AF -0FD10FCB103B102110BD10A1114310E70DC512E312B70F5D0ED70DA9126310310F390F1710950FD50DEB12BB0ECF0E31 -0FC30FA710150FE110C3109F120D1163128F12130E1312C50E33103D10B1107512BD11DB130F12FF102D0FCF1121118B -113311251063108B0D93123B0DED11AD0EF50DE711390F69101B0EB512670FB312B312050F0312210E590DB500000000 -00000E7B00000FAB0DE1000010CF108F110310F5110D1105113512D30000116D000011DF000012331273000000001283 -0000000012E912E7130512EB12BF127F0DB30DA10E010DB90E170E070E5F0E530E790E630ECD0E7F0F2F0ED10F470F43 -0F970F530FAF0FA310270FDD10491035107D106F10EB10A310FB10F710FD10F9110110FF110B1109111D111711531127 -115B1157117311611197118D11CB11971223121912391237124F124D1273126F12D912C70F2B12E1119313D600000000 -0DD70D810D9B0DC10DC90DB70E0B0DFF0E490E530E5D0E510E830E7B0E9B0E950EB10EA90F050F010F1D0F130F3F0F33 -0F470F370F530F410F7F0F5F0F890F850FAB0F990FBF0FBD0FFF0FC710211005104110451057104910E3106F1089107F -10AB108F10B910B510C910C710D110CF10DF10D510EF10DD1127111F11491131115F115311AF117311F911C3121F121B -12291223122B12331235123712391231124F123F127512651299128B12C712B912D512D312DB12D912F912E113AC1327 -13BE13AA0D370D2313F20D39143414041325132100000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000A7A00000ABB0000 -00000000000000000000000000000000000000000AB50AB20AAE0AAA0A590A560A5F0A5C0A680A650A710A6B00000A74 -0A7D0A770A830A8000000A8900000A8C0A920A8F0A95000000000A980AA10A9E0AA70AA40A6E0AB80A860A6200000A9B -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000013290000132C00000000 -0000000000000000000000000000000000000000132F0000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000013351332 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -00000000133800000000133B000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -00000000134100000000133E000013440000000000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000134A1347000000000000000000000000000000000000000000000000 -00000000000000000000000000000000000000000000000000000000000000000000134D000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -00000000000000000000000013531350135A13561362135E000013660000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000136A0000136D137513711378 -0000137C0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000D850D8913800D8B0DA10D990DA70DA30DAD0DAB0DAF0DB313880CF90DBB0DB9 -0DC70DBD0CFB13820DCB0DBF0DD113860D950DD30DD70DD50DE314440CFF0DE50DF10DEF0DF50DF30DFF0D010E070E01 -0E0D0E0B0E110E0F0E170E150E1B0E190E210E1F0E210E21105D138E0E270E250E2B13900E2F0E2D0E350E3D0E3B0E39 -0E430E3F0E470E450E4D0E4B0E510E4D0E570E550E690E5B0E6B0E5F0E650E610E890DE70E710E6F0E6D0E730E750E77 -13920E810E8D0E8B0E910E8F0E9D0E93139613940EA50EA30EA10EA70D030EAB0EAF0D050EB30EB30EB713980EBB0EB9 -139A0EBF0EC30EC10EC50F0F0EC90D070ED50ED1139E0ED313A00ED90EDD0EDF0EE70EE10D090EE90EED0EEB0EF10EEF -13A20D0B0EF70D0D0EFB0EF914580EFF13A60F09118F13A60F0D0F0D13B40D0F0F1114080D110F150F1F0F190F250F21 -0D150F2D0F2F0D130F3113A80F3D0F3B0F3D0F3F0F470F450F4B0F4F0F510F4D0F550F530F5B0F590F630F610F730F6B -0F7113AE0F750F6D0F830F790F8713B00F7D0F810F8B0D170F930F8F0D190F8D0F9B0F970FA513B20FA90FB90FAF0D1F -0D1B0D1D0DCF0DCD0FB10FBB0D5111810FBF0FBD0FC90FC113BC0FC50FD30D210FD90FCD13C00FDD0FD70FDF0D230FE3 -0FE70FE50FEF0FE90FF313C20FF70D250FF913C40FFB0D271005100113C6100713C8139C13CA10091013100F1017100B -1027101F10231021102B1029101D13CC1039103710410D2913CE103F104D103313D213D01059104F10551057105B0D2B -105F10611384106513D4106B13D81071107F107B13DA10811087108513DE13DC1097109310990D2D0D2F0D2F10A710A5 -10AD10AB0D3110B313E010B713E210BB13A410C113E410C513E813E60D350D3313EC10D513EE13EA10D913F010DB10DB -0D3910DF0D3B10E113F40D3D10E910E50D3F10EF10FF13F613FA13F81113110D11170D41111B111913FE13FC112313FE -14000D43112B112914020D450D471137113B113F140611410D49114B115511510D4B115D140C140A140E0D4D14101165 -116F11691412117311771414117B14160D4F13B61185118311870D5314180EAD13B8141A118F13BA126B1191119B0D55 -119D1199119F0DFD11A311A1141C11A711A911A511B511B311B711AB11CB11C111BB11B111BF11BD1422141E0D571420 -11D111CF142A11D5142411D70D5B0D591428142611E50D5D11E911E711EF11EB11F311ED11F911F111F711F50D5F11FB -120111FF12070D61142C12091211120F12170D63142E0CFD0D65143012250D67123F1231143212431247124512531251 -138A143612551257138C125B1265125F143A14381281127B143C128512971287129F129D12A5129B143E12A70D6912A9 -144012AD12C30D6B12CD0EE3144614420D6F0D6D144812D7144A0D7112DB12DB144C12DF0D7312E512EF12ED12F512F1 -144E0D7512FD12F90D77130113030D790D7B1450145414521311145613150D7D13191317131D131B145A131F00000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -00000000000000000000000000000000", -); -dstring decompCanonTable() nothrow @nogc pure @safe { -static immutable dchar[5212] t = -"\u0000\u003B\u0000\u003C\u0338\u0000\u003D\u0338\u0000\u003E\u0338\u0000\u0041\u0300\u0000\u0041\u0301\u0000\u0041"d~ -"\u0302\u0000\u0041\u0302\u0300\u0000\u0041\u0302\u0301\u0000\u0041\u0302\u0303\u0000\u0041\u0302\u0309\u0000\u0041"d~ -"\u0303\u0000\u0041\u0304\u0000\u0041\u0306\u0000\u0041\u0306\u0300\u0000\u0041\u0306\u0301\u0000\u0041\u0306\u0303"d~ -"\u0000\u0041\u0306\u0309\u0000\u0041\u0307\u0000\u0041\u0307\u0304\u0000\u0041\u0308\u0000\u0041\u0308\u0304\u0000"d~ -"\u0041\u0309\u0000\u0041\u030A\u0000\u0041\u030A\u0301\u0000\u0041\u030C\u0000\u0041\u030F\u0000\u0041\u0311\u0000"d~ -"\u0041\u0323\u0000\u0041\u0323\u0302\u0000\u0041\u0323\u0306\u0000\u0041\u0325\u0000\u0041\u0328\u0000\u0042\u0307"d~ -"\u0000\u0042\u0323\u0000\u0042\u0331\u0000\u0043\u0301\u0000\u0043\u0302\u0000\u0043\u0307\u0000\u0043\u030C\u0000"d~ -"\u0043\u0327\u0000\u0043\u0327\u0301\u0000\u0044\u0307\u0000\u0044\u030C\u0000\u0044\u0323\u0000\u0044\u0327\u0000"d~ -"\u0044\u032D\u0000\u0044\u0331\u0000\u0045\u0300\u0000\u0045\u0301\u0000\u0045\u0302\u0000\u0045\u0302\u0300\u0000"d~ -"\u0045\u0302\u0301\u0000\u0045\u0302\u0303\u0000\u0045\u0302\u0309\u0000\u0045\u0303\u0000\u0045\u0304\u0000\u0045"d~ -"\u0304\u0300\u0000\u0045\u0304\u0301\u0000\u0045\u0306\u0000\u0045\u0307\u0000\u0045\u0308\u0000\u0045\u0309\u0000"d~ -"\u0045\u030C\u0000\u0045\u030F\u0000\u0045\u0311\u0000\u0045\u0323\u0000\u0045\u0323\u0302\u0000\u0045\u0327\u0000"d~ -"\u0045\u0327\u0306\u0000\u0045\u0328\u0000\u0045\u032D\u0000\u0045\u0330\u0000\u0046\u0307\u0000\u0047\u0301\u0000"d~ -"\u0047\u0302\u0000\u0047\u0304\u0000\u0047\u0306\u0000\u0047\u0307\u0000\u0047\u030C\u0000\u0047\u0327\u0000\u0048"d~ -"\u0302\u0000\u0048\u0307\u0000\u0048\u0308\u0000\u0048\u030C\u0000\u0048\u0323\u0000\u0048\u0327\u0000\u0048\u032E"d~ -"\u0000\u0049\u0300\u0000\u0049\u0301\u0000\u0049\u0302\u0000\u0049\u0303\u0000\u0049\u0304\u0000\u0049\u0306\u0000"d~ -"\u0049\u0307\u0000\u0049\u0308\u0000\u0049\u0308\u0301\u0000\u0049\u0309\u0000\u0049\u030C\u0000\u0049\u030F\u0000"d~ -"\u0049\u0311\u0000\u0049\u0323\u0000\u0049\u0328\u0000\u0049\u0330\u0000\u004A\u0302\u0000\u004B\u0000\u004B\u0301"d~ -"\u0000\u004B\u030C\u0000\u004B\u0323\u0000\u004B\u0327\u0000\u004B\u0331\u0000\u004C\u0301\u0000\u004C\u030C\u0000"d~ -"\u004C\u0323\u0000\u004C\u0323\u0304\u0000\u004C\u0327\u0000\u004C\u032D\u0000\u004C\u0331\u0000\u004D\u0301\u0000"d~ -"\u004D\u0307\u0000\u004D\u0323\u0000\u004E\u0300\u0000\u004E\u0301\u0000\u004E\u0303\u0000\u004E\u0307\u0000\u004E"d~ -"\u030C\u0000\u004E\u0323\u0000\u004E\u0327\u0000\u004E\u032D\u0000\u004E\u0331\u0000\u004F\u0300\u0000\u004F\u0301"d~ -"\u0000\u004F\u0302\u0000\u004F\u0302\u0300\u0000\u004F\u0302\u0301\u0000\u004F\u0302\u0303\u0000\u004F\u0302\u0309"d~ -"\u0000\u004F\u0303\u0000\u004F\u0303\u0301\u0000\u004F\u0303\u0304\u0000\u004F\u0303\u0308\u0000\u004F\u0304\u0000"d~ -"\u004F\u0304\u0300\u0000\u004F\u0304\u0301\u0000\u004F\u0306\u0000\u004F\u0307\u0000\u004F\u0307\u0304\u0000\u004F"d~ -"\u0308\u0000\u004F\u0308\u0304\u0000\u004F\u0309\u0000\u004F\u030B\u0000\u004F\u030C\u0000\u004F\u030F\u0000\u004F"d~ -"\u0311\u0000\u004F\u031B\u0000\u004F\u031B\u0300\u0000\u004F\u031B\u0301\u0000\u004F\u031B\u0303\u0000\u004F\u031B"d~ -"\u0309\u0000\u004F\u031B\u0323\u0000\u004F\u0323\u0000\u004F\u0323\u0302\u0000\u004F\u0328\u0000\u004F\u0328\u0304"d~ -"\u0000\u0050\u0301\u0000\u0050\u0307\u0000\u0052\u0301\u0000\u0052\u0307\u0000\u0052\u030C\u0000\u0052\u030F\u0000"d~ -"\u0052\u0311\u0000\u0052\u0323\u0000\u0052\u0323\u0304\u0000\u0052\u0327\u0000\u0052\u0331\u0000\u0053\u0301\u0000"d~ -"\u0053\u0301\u0307\u0000\u0053\u0302\u0000\u0053\u0307\u0000\u0053\u030C\u0000\u0053\u030C\u0307\u0000\u0053\u0323"d~ -"\u0000\u0053\u0323\u0307\u0000\u0053\u0326\u0000\u0053\u0327\u0000\u0054\u0307\u0000\u0054\u030C\u0000\u0054\u0323"d~ -"\u0000\u0054\u0326\u0000\u0054\u0327\u0000\u0054\u032D\u0000\u0054\u0331\u0000\u0055\u0300\u0000\u0055\u0301\u0000"d~ -"\u0055\u0302\u0000\u0055\u0303\u0000\u0055\u0303\u0301\u0000\u0055\u0304\u0000\u0055\u0304\u0308\u0000\u0055\u0306"d~ -"\u0000\u0055\u0308\u0000\u0055\u0308\u0300\u0000\u0055\u0308\u0301\u0000\u0055\u0308\u0304\u0000\u0055\u0308\u030C"d~ -"\u0000\u0055\u0309\u0000\u0055\u030A\u0000\u0055\u030B\u0000\u0055\u030C\u0000\u0055\u030F\u0000\u0055\u0311\u0000"d~ -"\u0055\u031B\u0000\u0055\u031B\u0300\u0000\u0055\u031B\u0301\u0000\u0055\u031B\u0303\u0000\u0055\u031B\u0309\u0000"d~ -"\u0055\u031B\u0323\u0000\u0055\u0323\u0000\u0055\u0324\u0000\u0055\u0328\u0000\u0055\u032D\u0000\u0055\u0330\u0000"d~ -"\u0056\u0303\u0000\u0056\u0323\u0000\u0057\u0300\u0000\u0057\u0301\u0000\u0057\u0302\u0000\u0057\u0307\u0000\u0057"d~ -"\u0308\u0000\u0057\u0323\u0000\u0058\u0307\u0000\u0058\u0308\u0000\u0059\u0300\u0000\u0059\u0301\u0000\u0059\u0302"d~ -"\u0000\u0059\u0303\u0000\u0059\u0304\u0000\u0059\u0307\u0000\u0059\u0308\u0000\u0059\u0309\u0000\u0059\u0323\u0000"d~ -"\u005A\u0301\u0000\u005A\u0302\u0000\u005A\u0307\u0000\u005A\u030C\u0000\u005A\u0323\u0000\u005A\u0331\u0000\u0060"d~ -"\u0000\u0061\u0300\u0000\u0061\u0301\u0000\u0061\u0302\u0000\u0061\u0302\u0300\u0000\u0061\u0302\u0301\u0000\u0061"d~ -"\u0302\u0303\u0000\u0061\u0302\u0309\u0000\u0061\u0303\u0000\u0061\u0304\u0000\u0061\u0306\u0000\u0061\u0306\u0300"d~ -"\u0000\u0061\u0306\u0301\u0000\u0061\u0306\u0303\u0000\u0061\u0306\u0309\u0000\u0061\u0307\u0000\u0061\u0307\u0304"d~ -"\u0000\u0061\u0308\u0000\u0061\u0308\u0304\u0000\u0061\u0309\u0000\u0061\u030A\u0000\u0061\u030A\u0301\u0000\u0061"d~ -"\u030C\u0000\u0061\u030F\u0000\u0061\u0311\u0000\u0061\u0323\u0000\u0061\u0323\u0302\u0000\u0061\u0323\u0306\u0000"d~ -"\u0061\u0325\u0000\u0061\u0328\u0000\u0062\u0307\u0000\u0062\u0323\u0000\u0062\u0331\u0000\u0063\u0301\u0000\u0063"d~ -"\u0302\u0000\u0063\u0307\u0000\u0063\u030C\u0000\u0063\u0327\u0000\u0063\u0327\u0301\u0000\u0064\u0307\u0000\u0064"d~ -"\u030C\u0000\u0064\u0323\u0000\u0064\u0327\u0000\u0064\u032D\u0000\u0064\u0331\u0000\u0065\u0300\u0000\u0065\u0301"d~ -"\u0000\u0065\u0302\u0000\u0065\u0302\u0300\u0000\u0065\u0302\u0301\u0000\u0065\u0302\u0303\u0000\u0065\u0302\u0309"d~ -"\u0000\u0065\u0303\u0000\u0065\u0304\u0000\u0065\u0304\u0300\u0000\u0065\u0304\u0301\u0000\u0065\u0306\u0000\u0065"d~ -"\u0307\u0000\u0065\u0308\u0000\u0065\u0309\u0000\u0065\u030C\u0000\u0065\u030F\u0000\u0065\u0311\u0000\u0065\u0323"d~ -"\u0000\u0065\u0323\u0302\u0000\u0065\u0327\u0000\u0065\u0327\u0306\u0000\u0065\u0328\u0000\u0065\u032D\u0000\u0065"d~ -"\u0330\u0000\u0066\u0307\u0000\u0067\u0301\u0000\u0067\u0302\u0000\u0067\u0304\u0000\u0067\u0306\u0000\u0067\u0307"d~ -"\u0000\u0067\u030C\u0000\u0067\u0327\u0000\u0068\u0302\u0000\u0068\u0307\u0000\u0068\u0308\u0000\u0068\u030C\u0000"d~ -"\u0068\u0323\u0000\u0068\u0327\u0000\u0068\u032E\u0000\u0068\u0331\u0000\u0069\u0300\u0000\u0069\u0301\u0000\u0069"d~ -"\u0302\u0000\u0069\u0303\u0000\u0069\u0304\u0000\u0069\u0306\u0000\u0069\u0308\u0000\u0069\u0308\u0301\u0000\u0069"d~ -"\u0309\u0000\u0069\u030C\u0000\u0069\u030F\u0000\u0069\u0311\u0000\u0069\u0323\u0000\u0069\u0328\u0000\u0069\u0330"d~ -"\u0000\u006A\u0302\u0000\u006A\u030C\u0000\u006B\u0301\u0000\u006B\u030C\u0000\u006B\u0323\u0000\u006B\u0327\u0000"d~ -"\u006B\u0331\u0000\u006C\u0301\u0000\u006C\u030C\u0000\u006C\u0323\u0000\u006C\u0323\u0304\u0000\u006C\u0327\u0000"d~ -"\u006C\u032D\u0000\u006C\u0331\u0000\u006D\u0301\u0000\u006D\u0307\u0000\u006D\u0323\u0000\u006E\u0300\u0000\u006E"d~ -"\u0301\u0000\u006E\u0303\u0000\u006E\u0307\u0000\u006E\u030C\u0000\u006E\u0323\u0000\u006E\u0327\u0000\u006E\u032D"d~ -"\u0000\u006E\u0331\u0000\u006F\u0300\u0000\u006F\u0301\u0000\u006F\u0302\u0000\u006F\u0302\u0300\u0000\u006F\u0302"d~ -"\u0301\u0000\u006F\u0302\u0303\u0000\u006F\u0302\u0309\u0000\u006F\u0303\u0000\u006F\u0303\u0301\u0000\u006F\u0303"d~ -"\u0304\u0000\u006F\u0303\u0308\u0000\u006F\u0304\u0000\u006F\u0304\u0300\u0000\u006F\u0304\u0301\u0000\u006F\u0306"d~ -"\u0000\u006F\u0307\u0000\u006F\u0307\u0304\u0000\u006F\u0308\u0000\u006F\u0308\u0304\u0000\u006F\u0309\u0000\u006F"d~ -"\u030B\u0000\u006F\u030C\u0000\u006F\u030F\u0000\u006F\u0311\u0000\u006F\u031B\u0000\u006F\u031B\u0300\u0000\u006F"d~ -"\u031B\u0301\u0000\u006F\u031B\u0303\u0000\u006F\u031B\u0309\u0000\u006F\u031B\u0323\u0000\u006F\u0323\u0000\u006F"d~ -"\u0323\u0302\u0000\u006F\u0328\u0000\u006F\u0328\u0304\u0000\u0070\u0301\u0000\u0070\u0307\u0000\u0072\u0301\u0000"d~ -"\u0072\u0307\u0000\u0072\u030C\u0000\u0072\u030F\u0000\u0072\u0311\u0000\u0072\u0323\u0000\u0072\u0323\u0304\u0000"d~ -"\u0072\u0327\u0000\u0072\u0331\u0000\u0073\u0301\u0000\u0073\u0301\u0307\u0000\u0073\u0302\u0000\u0073\u0307\u0000"d~ -"\u0073\u030C\u0000\u0073\u030C\u0307\u0000\u0073\u0323\u0000\u0073\u0323\u0307\u0000\u0073\u0326\u0000\u0073\u0327"d~ -"\u0000\u0074\u0307\u0000\u0074\u0308\u0000\u0074\u030C\u0000\u0074\u0323\u0000\u0074\u0326\u0000\u0074\u0327\u0000"d~ -"\u0074\u032D\u0000\u0074\u0331\u0000\u0075\u0300\u0000\u0075\u0301\u0000\u0075\u0302\u0000\u0075\u0303\u0000\u0075"d~ -"\u0303\u0301\u0000\u0075\u0304\u0000\u0075\u0304\u0308\u0000\u0075\u0306\u0000\u0075\u0308\u0000\u0075\u0308\u0300"d~ -"\u0000\u0075\u0308\u0301\u0000\u0075\u0308\u0304\u0000\u0075\u0308\u030C\u0000\u0075\u0309\u0000\u0075\u030A\u0000"d~ -"\u0075\u030B\u0000\u0075\u030C\u0000\u0075\u030F\u0000\u0075\u0311\u0000\u0075\u031B\u0000\u0075\u031B\u0300\u0000"d~ -"\u0075\u031B\u0301\u0000\u0075\u031B\u0303\u0000\u0075\u031B\u0309\u0000\u0075\u031B\u0323\u0000\u0075\u0323\u0000"d~ -"\u0075\u0324\u0000\u0075\u0328\u0000\u0075\u032D\u0000\u0075\u0330\u0000\u0076\u0303\u0000\u0076\u0323\u0000\u0077"d~ -"\u0300\u0000\u0077\u0301\u0000\u0077\u0302\u0000\u0077\u0307\u0000\u0077\u0308\u0000\u0077\u030A\u0000\u0077\u0323"d~ -"\u0000\u0078\u0307\u0000\u0078\u0308\u0000\u0079\u0300\u0000\u0079\u0301\u0000\u0079\u0302\u0000\u0079\u0303\u0000"d~ -"\u0079\u0304\u0000\u0079\u0307\u0000\u0079\u0308\u0000\u0079\u0309\u0000\u0079\u030A\u0000\u0079\u0323\u0000\u007A"d~ -"\u0301\u0000\u007A\u0302\u0000\u007A\u0307\u0000\u007A\u030C\u0000\u007A\u0323\u0000\u007A\u0331\u0000\u00A8\u0300"d~ -"\u0000\u00A8\u0301\u0000\u00A8\u0342\u0000\u00B4\u0000\u00B7\u0000\u00C6\u0301\u0000\u00C6\u0304\u0000\u00D8\u0301"d~ -"\u0000\u00E6\u0301\u0000\u00E6\u0304\u0000\u00F8\u0301\u0000\u017F\u0307\u0000\u01B7\u030C\u0000\u0292\u030C\u0000"d~ -"\u02B9\u0000\u0300\u0000\u0301\u0000\u0308\u0301\u0000\u0313\u0000\u0391\u0300\u0000\u0391\u0301\u0000\u0391\u0304"d~ -"\u0000\u0391\u0306\u0000\u0391\u0313\u0000\u0391\u0313\u0300\u0000\u0391\u0313\u0300\u0345\u0000\u0391\u0313\u0301"d~ -"\u0000\u0391\u0313\u0301\u0345\u0000\u0391\u0313\u0342\u0000\u0391\u0313\u0342\u0345\u0000\u0391\u0313\u0345\u0000"d~ -"\u0391\u0314\u0000\u0391\u0314\u0300\u0000\u0391\u0314\u0300\u0345\u0000\u0391\u0314\u0301\u0000\u0391\u0314\u0301"d~ -"\u0345\u0000\u0391\u0314\u0342\u0000\u0391\u0314\u0342\u0345\u0000\u0391\u0314\u0345\u0000\u0391\u0345\u0000\u0395"d~ -"\u0300\u0000\u0395\u0301\u0000\u0395\u0313\u0000\u0395\u0313\u0300\u0000\u0395\u0313\u0301\u0000\u0395\u0314\u0000"d~ -"\u0395\u0314\u0300\u0000\u0395\u0314\u0301\u0000\u0397\u0300\u0000\u0397\u0301\u0000\u0397\u0313\u0000\u0397\u0313"d~ -"\u0300\u0000\u0397\u0313\u0300\u0345\u0000\u0397\u0313\u0301\u0000\u0397\u0313\u0301\u0345\u0000\u0397\u0313\u0342"d~ -"\u0000\u0397\u0313\u0342\u0345\u0000\u0397\u0313\u0345\u0000\u0397\u0314\u0000\u0397\u0314\u0300\u0000\u0397\u0314"d~ -"\u0300\u0345\u0000\u0397\u0314\u0301\u0000\u0397\u0314\u0301\u0345\u0000\u0397\u0314\u0342\u0000\u0397\u0314\u0342"d~ -"\u0345\u0000\u0397\u0314\u0345\u0000\u0397\u0345\u0000\u0399\u0300\u0000\u0399\u0301\u0000\u0399\u0304\u0000\u0399"d~ -"\u0306\u0000\u0399\u0308\u0000\u0399\u0313\u0000\u0399\u0313\u0300\u0000\u0399\u0313\u0301\u0000\u0399\u0313\u0342"d~ -"\u0000\u0399\u0314\u0000\u0399\u0314\u0300\u0000\u0399\u0314\u0301\u0000\u0399\u0314\u0342\u0000\u039F\u0300\u0000"d~ -"\u039F\u0301\u0000\u039F\u0313\u0000\u039F\u0313\u0300\u0000\u039F\u0313\u0301\u0000\u039F\u0314\u0000\u039F\u0314"d~ -"\u0300\u0000\u039F\u0314\u0301\u0000\u03A1\u0314\u0000\u03A5\u0300\u0000\u03A5\u0301\u0000\u03A5\u0304\u0000\u03A5"d~ -"\u0306\u0000\u03A5\u0308\u0000\u03A5\u0314\u0000\u03A5\u0314\u0300\u0000\u03A5\u0314\u0301\u0000\u03A5\u0314\u0342"d~ -"\u0000\u03A9\u0000\u03A9\u0300\u0000\u03A9\u0301\u0000\u03A9\u0313\u0000\u03A9\u0313\u0300\u0000\u03A9\u0313\u0300"d~ -"\u0345\u0000\u03A9\u0313\u0301\u0000\u03A9\u0313\u0301\u0345\u0000\u03A9\u0313\u0342\u0000\u03A9\u0313\u0342\u0345"d~ -"\u0000\u03A9\u0313\u0345\u0000\u03A9\u0314\u0000\u03A9\u0314\u0300\u0000\u03A9\u0314\u0300\u0345\u0000\u03A9\u0314"d~ -"\u0301\u0000\u03A9\u0314\u0301\u0345\u0000\u03A9\u0314\u0342\u0000\u03A9\u0314\u0342\u0345\u0000\u03A9\u0314\u0345"d~ -"\u0000\u03A9\u0345\u0000\u03B1\u0300\u0000\u03B1\u0300\u0345\u0000\u03B1\u0301\u0000\u03B1\u0301\u0345\u0000\u03B1"d~ -"\u0304\u0000\u03B1\u0306\u0000\u03B1\u0313\u0000\u03B1\u0313\u0300\u0000\u03B1\u0313\u0300\u0345\u0000\u03B1\u0313"d~ -"\u0301\u0000\u03B1\u0313\u0301\u0345\u0000\u03B1\u0313\u0342\u0000\u03B1\u0313\u0342\u0345\u0000\u03B1\u0313\u0345"d~ -"\u0000\u03B1\u0314\u0000\u03B1\u0314\u0300\u0000\u03B1\u0314\u0300\u0345\u0000\u03B1\u0314\u0301\u0000\u03B1\u0314"d~ -"\u0301\u0345\u0000\u03B1\u0314\u0342\u0000\u03B1\u0314\u0342\u0345\u0000\u03B1\u0314\u0345\u0000\u03B1\u0342\u0000"d~ -"\u03B1\u0342\u0345\u0000\u03B1\u0345\u0000\u03B5\u0300\u0000\u03B5\u0301\u0000\u03B5\u0313\u0000\u03B5\u0313\u0300"d~ -"\u0000\u03B5\u0313\u0301\u0000\u03B5\u0314\u0000\u03B5\u0314\u0300\u0000\u03B5\u0314\u0301\u0000\u03B7\u0300\u0000"d~ -"\u03B7\u0300\u0345\u0000\u03B7\u0301\u0000\u03B7\u0301\u0345\u0000\u03B7\u0313\u0000\u03B7\u0313\u0300\u0000\u03B7"d~ -"\u0313\u0300\u0345\u0000\u03B7\u0313\u0301\u0000\u03B7\u0313\u0301\u0345\u0000\u03B7\u0313\u0342\u0000\u03B7\u0313"d~ -"\u0342\u0345\u0000\u03B7\u0313\u0345\u0000\u03B7\u0314\u0000\u03B7\u0314\u0300\u0000\u03B7\u0314\u0300\u0345\u0000"d~ -"\u03B7\u0314\u0301\u0000\u03B7\u0314\u0301\u0345\u0000\u03B7\u0314\u0342\u0000\u03B7\u0314\u0342\u0345\u0000\u03B7"d~ -"\u0314\u0345\u0000\u03B7\u0342\u0000\u03B7\u0342\u0345\u0000\u03B7\u0345\u0000\u03B9\u0000\u03B9\u0300\u0000\u03B9"d~ -"\u0301\u0000\u03B9\u0304\u0000\u03B9\u0306\u0000\u03B9\u0308\u0000\u03B9\u0308\u0300\u0000\u03B9\u0308\u0301\u0000"d~ -"\u03B9\u0308\u0342\u0000\u03B9\u0313\u0000\u03B9\u0313\u0300\u0000\u03B9\u0313\u0301\u0000\u03B9\u0313\u0342\u0000"d~ -"\u03B9\u0314\u0000\u03B9\u0314\u0300\u0000\u03B9\u0314\u0301\u0000\u03B9\u0314\u0342\u0000\u03B9\u0342\u0000\u03BF"d~ -"\u0300\u0000\u03BF\u0301\u0000\u03BF\u0313\u0000\u03BF\u0313\u0300\u0000\u03BF\u0313\u0301\u0000\u03BF\u0314\u0000"d~ -"\u03BF\u0314\u0300\u0000\u03BF\u0314\u0301\u0000\u03C1\u0313\u0000\u03C1\u0314\u0000\u03C5\u0300\u0000\u03C5\u0301"d~ -"\u0000\u03C5\u0304\u0000\u03C5\u0306\u0000\u03C5\u0308\u0000\u03C5\u0308\u0300\u0000\u03C5\u0308\u0301\u0000\u03C5"d~ -"\u0308\u0342\u0000\u03C5\u0313\u0000\u03C5\u0313\u0300\u0000\u03C5\u0313\u0301\u0000\u03C5\u0313\u0342\u0000\u03C5"d~ -"\u0314\u0000\u03C5\u0314\u0300\u0000\u03C5\u0314\u0301\u0000\u03C5\u0314\u0342\u0000\u03C5\u0342\u0000\u03C9\u0300"d~ -"\u0000\u03C9\u0300\u0345\u0000\u03C9\u0301\u0000\u03C9\u0301\u0345\u0000\u03C9\u0313\u0000\u03C9\u0313\u0300\u0000"d~ -"\u03C9\u0313\u0300\u0345\u0000\u03C9\u0313\u0301\u0000\u03C9\u0313\u0301\u0345\u0000\u03C9\u0313\u0342\u0000\u03C9"d~ -"\u0313\u0342\u0345\u0000\u03C9\u0313\u0345\u0000\u03C9\u0314\u0000\u03C9\u0314\u0300\u0000\u03C9\u0314\u0300\u0345"d~ -"\u0000\u03C9\u0314\u0301\u0000\u03C9\u0314\u0301\u0345\u0000\u03C9\u0314\u0342\u0000\u03C9\u0314\u0342\u0345\u0000"d~ -"\u03C9\u0314\u0345\u0000\u03C9\u0342\u0000\u03C9\u0342\u0345\u0000\u03C9\u0345\u0000\u03D2\u0301\u0000\u03D2\u0308"d~ -"\u0000\u0406\u0308\u0000\u0410\u0306\u0000\u0410\u0308\u0000\u0413\u0301\u0000\u0415\u0300\u0000\u0415\u0306\u0000"d~ -"\u0415\u0308\u0000\u0416\u0306\u0000\u0416\u0308\u0000\u0417\u0308\u0000\u0418\u0300\u0000\u0418\u0304\u0000\u0418"d~ -"\u0306\u0000\u0418\u0308\u0000\u041A\u0301\u0000\u041E\u0308\u0000\u0423\u0304\u0000\u0423\u0306\u0000\u0423\u0308"d~ -"\u0000\u0423\u030B\u0000\u0427\u0308\u0000\u042B\u0308\u0000\u042D\u0308\u0000\u0430\u0306\u0000\u0430\u0308\u0000"d~ -"\u0433\u0301\u0000\u0435\u0300\u0000\u0435\u0306\u0000\u0435\u0308\u0000\u0436\u0306\u0000\u0436\u0308\u0000\u0437"d~ -"\u0308\u0000\u0438\u0300\u0000\u0438\u0304\u0000\u0438\u0306\u0000\u0438\u0308\u0000\u043A\u0301\u0000\u043E\u0308"d~ -"\u0000\u0443\u0304\u0000\u0443\u0306\u0000\u0443\u0308\u0000\u0443\u030B\u0000\u0447\u0308\u0000\u044B\u0308\u0000"d~ -"\u044D\u0308\u0000\u0456\u0308\u0000\u0474\u030F\u0000\u0475\u030F\u0000\u04D8\u0308\u0000\u04D9\u0308\u0000\u04E8"d~ -"\u0308\u0000\u04E9\u0308\u0000\u05D0\u05B7\u0000\u05D0\u05B8\u0000\u05D0\u05BC\u0000\u05D1\u05BC\u0000\u05D1\u05BF"d~ -"\u0000\u05D2\u05BC\u0000\u05D3\u05BC\u0000\u05D4\u05BC\u0000\u05D5\u05B9\u0000\u05D5\u05BC\u0000\u05D6\u05BC\u0000"d~ -"\u05D8\u05BC\u0000\u05D9\u05B4\u0000\u05D9\u05BC\u0000\u05DA\u05BC\u0000\u05DB\u05BC\u0000\u05DB\u05BF\u0000\u05DC"d~ -"\u05BC\u0000\u05DE\u05BC\u0000\u05E0\u05BC\u0000\u05E1\u05BC\u0000\u05E3\u05BC\u0000\u05E4\u05BC\u0000\u05E4\u05BF"d~ -"\u0000\u05E6\u05BC\u0000\u05E7\u05BC\u0000\u05E8\u05BC\u0000\u05E9\u05BC\u0000\u05E9\u05BC\u05C1\u0000\u05E9\u05BC"d~ -"\u05C2\u0000\u05E9\u05C1\u0000\u05E9\u05C2\u0000\u05EA\u05BC\u0000\u05F2\u05B7\u0000\u0627\u0653\u0000\u0627\u0654"d~ -"\u0000\u0627\u0655\u0000\u0648\u0654\u0000\u064A\u0654\u0000\u06C1\u0654\u0000\u06D2\u0654\u0000\u06D5\u0654\u0000"d~ -"\u0915\u093C\u0000\u0916\u093C\u0000\u0917\u093C\u0000\u091C\u093C\u0000\u0921\u093C\u0000\u0922\u093C\u0000\u0928"d~ -"\u093C\u0000\u092B\u093C\u0000\u092F\u093C\u0000\u0930\u093C\u0000\u0933\u093C\u0000\u09A1\u09BC\u0000\u09A2\u09BC"d~ -"\u0000\u09AF\u09BC\u0000\u09C7\u09BE\u0000\u09C7\u09D7\u0000\u0A16\u0A3C\u0000\u0A17\u0A3C\u0000\u0A1C\u0A3C\u0000"d~ -"\u0A2B\u0A3C\u0000\u0A32\u0A3C\u0000\u0A38\u0A3C\u0000\u0B21\u0B3C\u0000\u0B22\u0B3C\u0000\u0B47\u0B3E\u0000\u0B47"d~ -"\u0B56\u0000\u0B47\u0B57\u0000\u0B92\u0BD7\u0000\u0BC6\u0BBE\u0000\u0BC6\u0BD7\u0000\u0BC7\u0BBE\u0000\u0C46\u0C56"d~ -"\u0000\u0CBF\u0CD5\u0000\u0CC6\u0CC2\u0000\u0CC6\u0CC2\u0CD5\u0000\u0CC6\u0CD5\u0000\u0CC6\u0CD6\u0000\u0D46\u0D3E"d~ -"\u0000\u0D46\u0D57\u0000\u0D47\u0D3E\u0000\u0DD9\u0DCA\u0000\u0DD9\u0DCF\u0000\u0DD9\u0DCF\u0DCA\u0000\u0DD9\u0DDF"d~ -"\u0000\u0F40\u0FB5\u0000\u0F42\u0FB7\u0000\u0F4C\u0FB7\u0000\u0F51\u0FB7\u0000\u0F56\u0FB7\u0000\u0F5B\u0FB7\u0000"d~ -"\u0F71\u0F72\u0000\u0F71\u0F74\u0000\u0F71\u0F80\u0000\u0F90\u0FB5\u0000\u0F92\u0FB7\u0000\u0F9C\u0FB7\u0000\u0FA1"d~ -"\u0FB7\u0000\u0FA6\u0FB7\u0000\u0FAB\u0FB7\u0000\u0FB2\u0F80\u0000\u0FB3\u0F80\u0000\u1025\u102E\u0000\u1B05\u1B35"d~ -"\u0000\u1B07\u1B35\u0000\u1B09\u1B35\u0000\u1B0B\u1B35\u0000\u1B0D\u1B35\u0000\u1B11\u1B35\u0000\u1B3A\u1B35\u0000"d~ -"\u1B3C\u1B35\u0000\u1B3E\u1B35\u0000\u1B3F\u1B35\u0000\u1B42\u1B35\u0000\u1FBF\u0300\u0000\u1FBF\u0301\u0000\u1FBF"d~ -"\u0342\u0000\u1FFE\u0300\u0000\u1FFE\u0301\u0000\u1FFE\u0342\u0000\u2002\u0000\u2003\u0000\u2190\u0338\u0000\u2192"d~ -"\u0338\u0000\u2194\u0338\u0000\u21D0\u0338\u0000\u21D2\u0338\u0000\u21D4\u0338\u0000\u2203\u0338\u0000\u2208\u0338"d~ -"\u0000\u220B\u0338\u0000\u2223\u0338\u0000\u2225\u0338\u0000\u223C\u0338\u0000\u2243\u0338\u0000\u2245\u0338\u0000"d~ -"\u2248\u0338\u0000\u224D\u0338\u0000\u2261\u0338\u0000\u2264\u0338\u0000\u2265\u0338\u0000\u2272\u0338\u0000\u2273"d~ -"\u0338\u0000\u2276\u0338\u0000\u2277\u0338\u0000\u227A\u0338\u0000\u227B\u0338\u0000\u227C\u0338\u0000\u227D\u0338"d~ -"\u0000\u2282\u0338\u0000\u2283\u0338\u0000\u2286\u0338\u0000\u2287\u0338\u0000\u2291\u0338\u0000\u2292\u0338\u0000"d~ -"\u22A2\u0338\u0000\u22A8\u0338\u0000\u22A9\u0338\u0000\u22AB\u0338\u0000\u22B2\u0338\u0000\u22B3\u0338\u0000\u22B4"d~ -"\u0338\u0000\u22B5\u0338\u0000\u2ADD\u0338\u0000\u3008\u0000\u3009\u0000\u3046\u3099\u0000\u304B\u3099\u0000\u304D"d~ -"\u3099\u0000\u304F\u3099\u0000\u3051\u3099\u0000\u3053\u3099\u0000\u3055\u3099\u0000\u3057\u3099\u0000\u3059\u3099"d~ -"\u0000\u305B\u3099\u0000\u305D\u3099\u0000\u305F\u3099\u0000\u3061\u3099\u0000\u3064\u3099\u0000\u3066\u3099\u0000"d~ -"\u3068\u3099\u0000\u306F\u3099\u0000\u306F\u309A\u0000\u3072\u3099\u0000\u3072\u309A\u0000\u3075\u3099\u0000\u3075"d~ -"\u309A\u0000\u3078\u3099\u0000\u3078\u309A\u0000\u307B\u3099\u0000\u307B\u309A\u0000\u309D\u3099\u0000\u30A6\u3099"d~ -"\u0000\u30AB\u3099\u0000\u30AD\u3099\u0000\u30AF\u3099\u0000\u30B1\u3099\u0000\u30B3\u3099\u0000\u30B5\u3099\u0000"d~ -"\u30B7\u3099\u0000\u30B9\u3099\u0000\u30BB\u3099\u0000\u30BD\u3099\u0000\u30BF\u3099\u0000\u30C1\u3099\u0000\u30C4"d~ -"\u3099\u0000\u30C6\u3099\u0000\u30C8\u3099\u0000\u30CF\u3099\u0000\u30CF\u309A\u0000\u30D2\u3099\u0000\u30D2\u309A"d~ -"\u0000\u30D5\u3099\u0000\u30D5\u309A\u0000\u30D8\u3099\u0000\u30D8\u309A\u0000\u30DB\u3099\u0000\u30DB\u309A\u0000"d~ -"\u30EF\u3099\u0000\u30F0\u3099\u0000\u30F1\u3099\u0000\u30F2\u3099\u0000\u30FD\u3099\u0000\u349E\u0000\u34B9\u0000"d~ -"\u34BB\u0000\u34DF\u0000\u3515\u0000\u36EE\u0000\u36FC\u0000\u3781\u0000\u382F\u0000\u3862\u0000\u387C\u0000\u38C7"d~ -"\u0000\u38E3\u0000\u391C\u0000\u393A\u0000\u3A2E\u0000\u3A6C\u0000\u3AE4\u0000\u3B08\u0000\u3B19\u0000\u3B49\u0000"d~ -"\u3B9D\u0000\u3C18\u0000\u3C4E\u0000\u3D33\u0000\u3D96\u0000\u3EAC\u0000\u3EB8\u0000\u3F1B\u0000\u3FFC\u0000\u4008"d~ -"\u0000\u4018\u0000\u4039\u0000\u4046\u0000\u4096\u0000\u40E3\u0000\u412F\u0000\u4202\u0000\u4227\u0000\u42A0\u0000"d~ -"\u4301\u0000\u4334\u0000\u4359\u0000\u43D5\u0000\u43D9\u0000\u440B\u0000\u446B\u0000\u452B\u0000\u455D\u0000\u4561"d~ -"\u0000\u456B\u0000\u45D7\u0000\u45F9\u0000\u4635\u0000\u46BE\u0000\u46C7\u0000\u4995\u0000\u49E6\u0000\u4A6E\u0000"d~ -"\u4A76\u0000\u4AB2\u0000\u4B33\u0000\u4BCE\u0000\u4CCE\u0000\u4CED\u0000\u4CF8\u0000\u4D56\u0000\u4E0D\u0000\u4E26"d~ -"\u0000\u4E32\u0000\u4E38\u0000\u4E39\u0000\u4E3D\u0000\u4E41\u0000\u4E82\u0000\u4E86\u0000\u4EAE\u0000\u4EC0\u0000"d~ -"\u4ECC\u0000\u4EE4\u0000\u4F60\u0000\u4F80\u0000\u4F86\u0000\u4F8B\u0000\u4FAE\u0000\u4FBB\u0000\u4FBF\u0000\u5002"d~ -"\u0000\u502B\u0000\u507A\u0000\u5099\u0000\u50CF\u0000\u50DA\u0000\u50E7\u0000\u5140\u0000\u5145\u0000\u514D\u0000"d~ -"\u5154\u0000\u5164\u0000\u5167\u0000\u5168\u0000\u5169\u0000\u516D\u0000\u5177\u0000\u5180\u0000\u518D\u0000\u5192"d~ -"\u0000\u5195\u0000\u5197\u0000\u51A4\u0000\u51AC\u0000\u51B5\u0000\u51B7\u0000\u51C9\u0000\u51CC\u0000\u51DC\u0000"d~ -"\u51DE\u0000\u51F5\u0000\u5203\u0000\u5207\u0000\u5217\u0000\u5229\u0000\u523A\u0000\u523B\u0000\u5246\u0000\u5272"d~ -"\u0000\u5277\u0000\u5289\u0000\u529B\u0000\u52A3\u0000\u52B3\u0000\u52C7\u0000\u52C9\u0000\u52D2\u0000\u52DE\u0000"d~ -"\u52E4\u0000\u52F5\u0000\u52FA\u0000\u5305\u0000\u5306\u0000\u5317\u0000\u533F\u0000\u5349\u0000\u5351\u0000\u535A"d~ -"\u0000\u5373\u0000\u5375\u0000\u537D\u0000\u537F\u0000\u53C3\u0000\u53CA\u0000\u53DF\u0000\u53E5\u0000\u53EB\u0000"d~ -"\u53F1\u0000\u5406\u0000\u540F\u0000\u541D\u0000\u5438\u0000\u5442\u0000\u5448\u0000\u5468\u0000\u549E\u0000\u54A2"d~ -"\u0000\u54BD\u0000\u54F6\u0000\u5510\u0000\u5553\u0000\u5555\u0000\u5563\u0000\u5584\u0000\u5587\u0000\u5599\u0000"d~ -"\u559D\u0000\u55AB\u0000\u55B3\u0000\u55C0\u0000\u55C2\u0000\u55E2\u0000\u5606\u0000\u5651\u0000\u5668\u0000\u5674"d~ -"\u0000\u56F9\u0000\u5716\u0000\u5717\u0000\u578B\u0000\u57CE\u0000\u57F4\u0000\u580D\u0000\u5831\u0000\u5832\u0000"d~ -"\u5840\u0000\u585A\u0000\u585E\u0000\u58A8\u0000\u58AC\u0000\u58B3\u0000\u58D8\u0000\u58DF\u0000\u58EE\u0000\u58F2"d~ -"\u0000\u58F7\u0000\u5906\u0000\u591A\u0000\u5922\u0000\u5944\u0000\u5948\u0000\u5951\u0000\u5954\u0000\u5962\u0000"d~ -"\u5973\u0000\u59D8\u0000\u59EC\u0000\u5A1B\u0000\u5A27\u0000\u5A62\u0000\u5A66\u0000\u5AB5\u0000\u5B08\u0000\u5B28"d~ -"\u0000\u5B3E\u0000\u5B85\u0000\u5BC3\u0000\u5BD8\u0000\u5BE7\u0000\u5BEE\u0000\u5BF3\u0000\u5BFF\u0000\u5C06\u0000"d~ -"\u5C22\u0000\u5C3F\u0000\u5C60\u0000\u5C62\u0000\u5C64\u0000\u5C65\u0000\u5C6E\u0000\u5C8D\u0000\u5CC0\u0000\u5D19"d~ -"\u0000\u5D43\u0000\u5D50\u0000\u5D6B\u0000\u5D6E\u0000\u5D7C\u0000\u5DB2\u0000\u5DBA\u0000\u5DE1\u0000\u5DE2\u0000"d~ -"\u5DFD\u0000\u5E28\u0000\u5E3D\u0000\u5E69\u0000\u5E74\u0000\u5EA6\u0000\u5EB0\u0000\u5EB3\u0000\u5EB6\u0000\u5EC9"d~ -"\u0000\u5ECA\u0000\u5ED2\u0000\u5ED3\u0000\u5ED9\u0000\u5EEC\u0000\u5EFE\u0000\u5F04\u0000\u5F22\u0000\u5F53\u0000"d~ -"\u5F62\u0000\u5F69\u0000\u5F6B\u0000\u5F8B\u0000\u5F9A\u0000\u5FA9\u0000\u5FAD\u0000\u5FCD\u0000\u5FD7\u0000\u5FF5"d~ -"\u0000\u5FF9\u0000\u6012\u0000\u601C\u0000\u6075\u0000\u6081\u0000\u6094\u0000\u60C7\u0000\u60D8\u0000\u60E1\u0000"d~ -"\u6108\u0000\u6144\u0000\u6148\u0000\u614C\u0000\u614E\u0000\u6160\u0000\u6168\u0000\u617A\u0000\u618E\u0000\u6190"d~ -"\u0000\u61A4\u0000\u61AF\u0000\u61B2\u0000\u61DE\u0000\u61F2\u0000\u61F6\u0000\u6200\u0000\u6210\u0000\u621B\u0000"d~ -"\u622E\u0000\u6234\u0000\u625D\u0000\u62B1\u0000\u62C9\u0000\u62CF\u0000\u62D3\u0000\u62D4\u0000\u62FC\u0000\u62FE"d~ -"\u0000\u633D\u0000\u6350\u0000\u6368\u0000\u637B\u0000\u6383\u0000\u63A0\u0000\u63A9\u0000\u63C4\u0000\u63C5\u0000"d~ -"\u63E4\u0000\u641C\u0000\u6422\u0000\u6452\u0000\u6469\u0000\u6477\u0000\u647E\u0000\u649A\u0000\u649D\u0000\u64C4"d~ -"\u0000\u654F\u0000\u6556\u0000\u656C\u0000\u6578\u0000\u6599\u0000\u65C5\u0000\u65E2\u0000\u65E3\u0000\u6613\u0000"d~ -"\u6649\u0000\u6674\u0000\u6688\u0000\u6691\u0000\u669C\u0000\u66B4\u0000\u66C6\u0000\u66F4\u0000\u66F8\u0000\u6700"d~ -"\u0000\u6717\u0000\u671B\u0000\u6721\u0000\u674E\u0000\u6753\u0000\u6756\u0000\u675E\u0000\u677B\u0000\u6785\u0000"d~ -"\u6797\u0000\u67F3\u0000\u67FA\u0000\u6817\u0000\u681F\u0000\u6852\u0000\u6881\u0000\u6885\u0000\u688E\u0000\u68A8"d~ -"\u0000\u6914\u0000\u6942\u0000\u69A3\u0000\u69EA\u0000\u6A02\u0000\u6A13\u0000\u6AA8\u0000\u6AD3\u0000\u6ADB\u0000"d~ -"\u6B04\u0000\u6B21\u0000\u6B54\u0000\u6B72\u0000\u6B77\u0000\u6B79\u0000\u6B9F\u0000\u6BAE\u0000\u6BBA\u0000\u6BBB"d~ -"\u0000\u6C4E\u0000\u6C67\u0000\u6C88\u0000\u6CBF\u0000\u6CCC\u0000\u6CCD\u0000\u6CE5\u0000\u6D16\u0000\u6D1B\u0000"d~ -"\u6D1E\u0000\u6D34\u0000\u6D3E\u0000\u6D41\u0000\u6D69\u0000\u6D6A\u0000\u6D77\u0000\u6D78\u0000\u6D85\u0000\u6DCB"d~ -"\u0000\u6DDA\u0000\u6DEA\u0000\u6DF9\u0000\u6E1A\u0000\u6E2F\u0000\u6E6E\u0000\u6E9C\u0000\u6EBA\u0000\u6EC7\u0000"d~ -"\u6ECB\u0000\u6ED1\u0000\u6EDB\u0000\u6F0F\u0000\u6F22\u0000\u6F23\u0000\u6F6E\u0000\u6FC6\u0000\u6FEB\u0000\u6FFE"d~ -"\u0000\u701B\u0000\u701E\u0000\u7039\u0000\u704A\u0000\u7070\u0000\u7077\u0000\u707D\u0000\u7099\u0000\u70AD\u0000"d~ -"\u70C8\u0000\u70D9\u0000\u7145\u0000\u7149\u0000\u716E\u0000\u719C\u0000\u71CE\u0000\u71D0\u0000\u7210\u0000\u721B"d~ -"\u0000\u7228\u0000\u722B\u0000\u7235\u0000\u7250\u0000\u7262\u0000\u7280\u0000\u7295\u0000\u72AF\u0000\u72C0\u0000"d~ -"\u72FC\u0000\u732A\u0000\u7375\u0000\u737A\u0000\u7387\u0000\u738B\u0000\u73A5\u0000\u73B2\u0000\u73DE\u0000\u7406"d~ -"\u0000\u7409\u0000\u7422\u0000\u7447\u0000\u745C\u0000\u7469\u0000\u7471\u0000\u7485\u0000\u7489\u0000\u7498\u0000"d~ -"\u74CA\u0000\u7506\u0000\u7524\u0000\u753B\u0000\u753E\u0000\u7559\u0000\u7565\u0000\u7570\u0000\u75E2\u0000\u7610"d~ -"\u0000\u761D\u0000\u761F\u0000\u7642\u0000\u7669\u0000\u76CA\u0000\u76DB\u0000\u76E7\u0000\u76F4\u0000\u7701\u0000"d~ -"\u771E\u0000\u771F\u0000\u7740\u0000\u774A\u0000\u778B\u0000\u77A7\u0000\u784E\u0000\u786B\u0000\u788C\u0000\u7891"d~ -"\u0000\u78CA\u0000\u78CC\u0000\u78FB\u0000\u792A\u0000\u793C\u0000\u793E\u0000\u7948\u0000\u7949\u0000\u7950\u0000"d~ -"\u7956\u0000\u795D\u0000\u795E\u0000\u7965\u0000\u797F\u0000\u798D\u0000\u798E\u0000\u798F\u0000\u79AE\u0000\u79CA"d~ -"\u0000\u79EB\u0000\u7A1C\u0000\u7A40\u0000\u7A4A\u0000\u7A4F\u0000\u7A81\u0000\u7AB1\u0000\u7ACB\u0000\u7AEE\u0000"d~ -"\u7B20\u0000\u7BC0\u0000\u7BC6\u0000\u7BC9\u0000\u7C3E\u0000\u7C60\u0000\u7C7B\u0000\u7C92\u0000\u7CBE\u0000\u7CD2"d~ -"\u0000\u7CD6\u0000\u7CE3\u0000\u7CE7\u0000\u7CE8\u0000\u7D00\u0000\u7D10\u0000\u7D22\u0000\u7D2F\u0000\u7D5B\u0000"d~ -"\u7D63\u0000\u7DA0\u0000\u7DBE\u0000\u7DC7\u0000\u7DF4\u0000\u7E02\u0000\u7E09\u0000\u7E37\u0000\u7E41\u0000\u7E45"d~ -"\u0000\u7F3E\u0000\u7F72\u0000\u7F79\u0000\u7F7A\u0000\u7F85\u0000\u7F95\u0000\u7F9A\u0000\u7FBD\u0000\u7FFA\u0000"d~ -"\u8001\u0000\u8005\u0000\u8046\u0000\u8060\u0000\u806F\u0000\u8070\u0000\u807E\u0000\u808B\u0000\u80AD\u0000\u80B2"d~ -"\u0000\u8103\u0000\u813E\u0000\u81D8\u0000\u81E8\u0000\u81ED\u0000\u8201\u0000\u8204\u0000\u8218\u0000\u826F\u0000"d~ -"\u8279\u0000\u828B\u0000\u8291\u0000\u829D\u0000\u82B1\u0000\u82B3\u0000\u82BD\u0000\u82E5\u0000\u82E6\u0000\u831D"d~ -"\u0000\u8323\u0000\u8336\u0000\u8352\u0000\u8353\u0000\u8363\u0000\u83AD\u0000\u83BD\u0000\u83C9\u0000\u83CA\u0000"d~ -"\u83CC\u0000\u83DC\u0000\u83E7\u0000\u83EF\u0000\u83F1\u0000\u843D\u0000\u8449\u0000\u8457\u0000\u84EE\u0000\u84F1"d~ -"\u0000\u84F3\u0000\u84FC\u0000\u8516\u0000\u8564\u0000\u85CD\u0000\u85FA\u0000\u8606\u0000\u8612\u0000\u862D\u0000"d~ -"\u863F\u0000\u8650\u0000\u865C\u0000\u8667\u0000\u8669\u0000\u8688\u0000\u86A9\u0000\u86E2\u0000\u870E\u0000\u8728"d~ -"\u0000\u876B\u0000\u8779\u0000\u8786\u0000\u87BA\u0000\u87E1\u0000\u8801\u0000\u881F\u0000\u884C\u0000\u8860\u0000"d~ -"\u8863\u0000\u88C2\u0000\u88CF\u0000\u88D7\u0000\u88DE\u0000\u88E1\u0000\u88F8\u0000\u88FA\u0000\u8910\u0000\u8941"d~ -"\u0000\u8964\u0000\u8986\u0000\u898B\u0000\u8996\u0000\u8AA0\u0000\u8AAA\u0000\u8ABF\u0000\u8ACB\u0000\u8AD2\u0000"d~ -"\u8AD6\u0000\u8AED\u0000\u8AF8\u0000\u8AFE\u0000\u8B01\u0000\u8B39\u0000\u8B58\u0000\u8B80\u0000\u8B8A\u0000\u8C48"d~ -"\u0000\u8C55\u0000\u8CAB\u0000\u8CC1\u0000\u8CC2\u0000\u8CC8\u0000\u8CD3\u0000\u8D08\u0000\u8D1B\u0000\u8D77\u0000"d~ -"\u8DBC\u0000\u8DCB\u0000\u8DEF\u0000\u8DF0\u0000\u8ECA\u0000\u8ED4\u0000\u8F26\u0000\u8F2A\u0000\u8F38\u0000\u8F3B"d~ -"\u0000\u8F62\u0000\u8F9E\u0000\u8FB0\u0000\u8FB6\u0000\u9023\u0000\u9038\u0000\u9072\u0000\u907C\u0000\u908F\u0000"d~ -"\u9094\u0000\u90CE\u0000\u90DE\u0000\u90F1\u0000\u90FD\u0000\u9111\u0000\u911B\u0000\u916A\u0000\u9199\u0000\u91B4"d~ -"\u0000\u91CC\u0000\u91CF\u0000\u91D1\u0000\u9234\u0000\u9238\u0000\u9276\u0000\u927C\u0000\u92D7\u0000\u92D8\u0000"d~ -"\u9304\u0000\u934A\u0000\u93F9\u0000\u9415\u0000\u958B\u0000\u95AD\u0000\u95B7\u0000\u962E\u0000\u964B\u0000\u964D"d~ -"\u0000\u9675\u0000\u9678\u0000\u967C\u0000\u9686\u0000\u96A3\u0000\u96B7\u0000\u96B8\u0000\u96C3\u0000\u96E2\u0000"d~ -"\u96E3\u0000\u96F6\u0000\u96F7\u0000\u9723\u0000\u9732\u0000\u9748\u0000\u9756\u0000\u97DB\u0000\u97E0\u0000\u97FF"d~ -"\u0000\u980B\u0000\u9818\u0000\u9829\u0000\u983B\u0000\u985E\u0000\u98E2\u0000\u98EF\u0000\u98FC\u0000\u9928\u0000"d~ -"\u9929\u0000\u99A7\u0000\u99C2\u0000\u99F1\u0000\u99FE\u0000\u9A6A\u0000\u9B12\u0000\u9B6F\u0000\u9C40\u0000\u9C57"d~ -"\u0000\u9CFD\u0000\u9D67\u0000\u9DB4\u0000\u9DFA\u0000\u9E1E\u0000\u9E7F\u0000\u9E97\u0000\u9E9F\u0000\u9EBB\u0000"d~ -"\u9ECE\u0000\u9EF9\u0000\u9EFE\u0000\u9F05\u0000\u9F0F\u0000\u9F16\u0000\u9F3B\u0000\u9F43\u0000\u9F8D\u0000\u9F8E"d~ -"\u0000\u9F9C\u0000\U00011099\U000110BA\u0000\U0001109B\U000110BA\u0000\U000110A5\U000110BA\u0000\U00011131\U00011127"d~ -"\u0000\U00011132\U00011127\u0000\U00011347\U0001133E\u0000\U00011347\U00011357\u0000\U000114B9\U000114B0\u0000"d~ -"\U000114B9\U000114BA\u0000\U000114B9\U000114BD\u0000\U000115B8\U000115AF\u0000\U000115B9\U000115AF\u0000\U00011935"d~ -"\U00011930\u0000\U0001D157\U0001D165\u0000\U0001D158\U0001D165\u0000\U0001D158\U0001D165\U0001D16E\u0000\U0001D158"d~ -"\U0001D165\U0001D16F\u0000\U0001D158\U0001D165\U0001D170\u0000\U0001D158\U0001D165\U0001D171\u0000\U0001D158\U0001D165"d~ -"\U0001D172\u0000\U0001D1B9\U0001D165\u0000\U0001D1B9\U0001D165\U0001D16E\u0000\U0001D1B9\U0001D165\U0001D16F\u0000"d~ -"\U0001D1BA\U0001D165\u0000\U0001D1BA\U0001D165\U0001D16E\u0000\U0001D1BA\U0001D165\U0001D16F\u0000\U00020122\u0000"d~ -"\U0002051C\u0000\U00020525\u0000\U0002054B\u0000\U0002063A\u0000\U00020804\u0000\U000208DE\u0000\U00020A2C\u0000"d~ -"\U00020B63\u0000\U000214E4\u0000\U000216A8\u0000\U000216EA\u0000\U000219C8\u0000\U00021B18\u0000\U00021D0B\u0000"d~ -"\U00021DE4\u0000\U00021DE6\u0000\U00022183\u0000\U0002219F\u0000\U00022331\u0000\U000226D4\u0000\U00022844\u0000"d~ -"\U0002284A\u0000\U00022B0C\u0000\U00022BF1\u0000\U0002300A\u0000\U000232B8\u0000\U0002335F\u0000\U00023393\u0000"d~ -"\U0002339C\u0000\U000233C3\u0000\U000233D5\u0000\U0002346D\u0000\U000236A3\u0000\U000238A7\u0000\U00023A8D\u0000"d~ -"\U00023AFA\u0000\U00023CBC\u0000\U00023D1E\u0000\U00023ED1\u0000\U00023F5E\u0000\U00023F8E\u0000\U00024263\u0000"d~ -"\U000242EE\u0000\U000243AB\u0000\U00024608\u0000\U00024735\u0000\U00024814\u0000\U00024C36\u0000\U00024C92\u0000"d~ -"\U00024FA1\u0000\U00024FB8\u0000\U00025044\u0000\U000250F2\u0000\U000250F3\u0000\U00025119\u0000\U00025133\u0000"d~ -"\U00025249\u0000\U0002541D\u0000\U00025626\u0000\U0002569A\u0000\U000256C5\u0000\U0002597C\u0000\U00025AA7\u0000"d~ -"\U00025BAB\u0000\U00025C80\u0000\U00025CD0\u0000\U00025F86\u0000\U000261DA\u0000\U00026228\u0000\U00026247\u0000"d~ -"\U000262D9\u0000\U0002633E\u0000\U000264DA\u0000\U00026523\u0000\U000265A8\u0000\U000267A7\u0000\U000267B5\u0000"d~ -"\U00026B3C\u0000\U00026C36\u0000\U00026CD5\u0000\U00026D6B\u0000\U00026F2C\u0000\U00026FB1\u0000\U000270D2\u0000"d~ -"\U000273CA\u0000\U00027667\u0000\U000278AE\u0000\U00027966\u0000\U00027CA8\u0000\U00027ED3\u0000\U00027F2F\u0000"d~ -"\U000285D2\u0000\U000285ED\u0000\U0002872E\u0000\U00028BFA\u0000\U00028D77\u0000\U00029145\u0000\U000291DF\u0000"d~ -"\U0002921A\u0000\U0002940A\u0000\U00029496\u0000\U000295B6\u0000\U00029B30\u0000\U0002A0CE\u0000\U0002A105\u0000"d~ -"\U0002A20E\u0000\U0002A291\u0000\U0002A392\u0000\U0002A600\u0000"d; -return t[]; -} -dstring decompCompatTable() nothrow @nogc pure @safe { -static immutable dchar[10425] t = -"\u0000\u0020\u0000\u0020\u0301\u0000\u0020\u0303\u0000\u0020\u0304\u0000\u0020\u0305\u0000\u0020\u0306\u0000\u0020"d~ -"\u0307\u0000\u0020\u0308\u0000\u0020\u0308\u0300\u0000\u0020\u0308\u0301\u0000\u0020\u0308\u0342\u0000\u0020\u030A"d~ -"\u0000\u0020\u030B\u0000\u0020\u0313\u0000\u0020\u0313\u0300\u0000\u0020\u0313\u0301\u0000\u0020\u0313\u0342\u0000"d~ -"\u0020\u0314\u0000\u0020\u0314\u0300\u0000\u0020\u0314\u0301\u0000\u0020\u0314\u0342\u0000\u0020\u0327\u0000\u0020"d~ -"\u0328\u0000\u0020\u0333\u0000\u0020\u0342\u0000\u0020\u0345\u0000\u0020\u064B\u0000\u0020\u064C\u0000\u0020\u064C"d~ -"\u0651\u0000\u0020\u064D\u0000\u0020\u064D\u0651\u0000\u0020\u064E\u0000\u0020\u064E\u0651\u0000\u0020\u064F\u0000"d~ -"\u0020\u064F\u0651\u0000\u0020\u0650\u0000\u0020\u0650\u0651\u0000\u0020\u0651\u0000\u0020\u0651\u0670\u0000\u0020"d~ -"\u0652\u0000\u0020\u3099\u0000\u0020\u309A\u0000\u0021\u0000\u0021\u0021\u0000\u0021\u003F\u0000\u0022\u0000\u0023"d~ -"\u0000\u0024\u0000\u0025\u0000\u0026\u0000\u0027\u0000\u0028\u0000\u0028\u0031\u0029\u0000\u0028\u0031\u0030\u0029"d~ -"\u0000\u0028\u0031\u0031\u0029\u0000\u0028\u0031\u0032\u0029\u0000\u0028\u0031\u0033\u0029\u0000\u0028\u0031\u0034"d~ -"\u0029\u0000\u0028\u0031\u0035\u0029\u0000\u0028\u0031\u0036\u0029\u0000\u0028\u0031\u0037\u0029\u0000\u0028\u0031"d~ -"\u0038\u0029\u0000\u0028\u0031\u0039\u0029\u0000\u0028\u0032\u0029\u0000\u0028\u0032\u0030\u0029\u0000\u0028\u0033"d~ -"\u0029\u0000\u0028\u0034\u0029\u0000\u0028\u0035\u0029\u0000\u0028\u0036\u0029\u0000\u0028\u0037\u0029\u0000\u0028"d~ -"\u0038\u0029\u0000\u0028\u0039\u0029\u0000\u0028\u0041\u0029\u0000\u0028\u0042\u0029\u0000\u0028\u0043\u0029\u0000"d~ -"\u0028\u0044\u0029\u0000\u0028\u0045\u0029\u0000\u0028\u0046\u0029\u0000\u0028\u0047\u0029\u0000\u0028\u0048\u0029"d~ -"\u0000\u0028\u0049\u0029\u0000\u0028\u004A\u0029\u0000\u0028\u004B\u0029\u0000\u0028\u004C\u0029\u0000\u0028\u004D"d~ -"\u0029\u0000\u0028\u004E\u0029\u0000\u0028\u004F\u0029\u0000\u0028\u0050\u0029\u0000\u0028\u0051\u0029\u0000\u0028"d~ -"\u0052\u0029\u0000\u0028\u0053\u0029\u0000\u0028\u0054\u0029\u0000\u0028\u0055\u0029\u0000\u0028\u0056\u0029\u0000"d~ -"\u0028\u0057\u0029\u0000\u0028\u0058\u0029\u0000\u0028\u0059\u0029\u0000\u0028\u005A\u0029\u0000\u0028\u0061\u0029"d~ -"\u0000\u0028\u0062\u0029\u0000\u0028\u0063\u0029\u0000\u0028\u0064\u0029\u0000\u0028\u0065\u0029\u0000\u0028\u0066"d~ -"\u0029\u0000\u0028\u0067\u0029\u0000\u0028\u0068\u0029\u0000\u0028\u0069\u0029\u0000\u0028\u006A\u0029\u0000\u0028"d~ -"\u006B\u0029\u0000\u0028\u006C\u0029\u0000\u0028\u006D\u0029\u0000\u0028\u006E\u0029\u0000\u0028\u006F\u0029\u0000"d~ -"\u0028\u0070\u0029\u0000\u0028\u0071\u0029\u0000\u0028\u0072\u0029\u0000\u0028\u0073\u0029\u0000\u0028\u0074\u0029"d~ -"\u0000\u0028\u0075\u0029\u0000\u0028\u0076\u0029\u0000\u0028\u0077\u0029\u0000\u0028\u0078\u0029\u0000\u0028\u0079"d~ -"\u0029\u0000\u0028\u007A\u0029\u0000\u0028\u1100\u0029\u0000\u0028\u1100\u1161\u0029\u0000\u0028\u1102\u0029\u0000"d~ -"\u0028\u1102\u1161\u0029\u0000\u0028\u1103\u0029\u0000\u0028\u1103\u1161\u0029\u0000\u0028\u1105\u0029\u0000\u0028"d~ -"\u1105\u1161\u0029\u0000\u0028\u1106\u0029\u0000\u0028\u1106\u1161\u0029\u0000\u0028\u1107\u0029\u0000\u0028\u1107"d~ -"\u1161\u0029\u0000\u0028\u1109\u0029\u0000\u0028\u1109\u1161\u0029\u0000\u0028\u110B\u0029\u0000\u0028\u110B\u1161"d~ -"\u0029\u0000\u0028\u110B\u1169\u110C\u1165\u11AB\u0029\u0000\u0028\u110B\u1169\u1112\u116E\u0029\u0000\u0028\u110C"d~ -"\u0029\u0000\u0028\u110C\u1161\u0029\u0000\u0028\u110C\u116E\u0029\u0000\u0028\u110E\u0029\u0000\u0028\u110E\u1161"d~ -"\u0029\u0000\u0028\u110F\u0029\u0000\u0028\u110F\u1161\u0029\u0000\u0028\u1110\u0029\u0000\u0028\u1110\u1161\u0029"d~ -"\u0000\u0028\u1111\u0029\u0000\u0028\u1111\u1161\u0029\u0000\u0028\u1112\u0029\u0000\u0028\u1112\u1161\u0029\u0000"d~ -"\u0028\u4E00\u0029\u0000\u0028\u4E03\u0029\u0000\u0028\u4E09\u0029\u0000\u0028\u4E5D\u0029\u0000\u0028\u4E8C\u0029"d~ -"\u0000\u0028\u4E94\u0029\u0000\u0028\u4EE3\u0029\u0000\u0028\u4F01\u0029\u0000\u0028\u4F11\u0029\u0000\u0028\u516B"d~ -"\u0029\u0000\u0028\u516D\u0029\u0000\u0028\u52B4\u0029\u0000\u0028\u5341\u0029\u0000\u0028\u5354\u0029\u0000\u0028"d~ -"\u540D\u0029\u0000\u0028\u547C\u0029\u0000\u0028\u56DB\u0029\u0000\u0028\u571F\u0029\u0000\u0028\u5B66\u0029\u0000"d~ -"\u0028\u65E5\u0029\u0000\u0028\u6708\u0029\u0000\u0028\u6709\u0029\u0000\u0028\u6728\u0029\u0000\u0028\u682A\u0029"d~ -"\u0000\u0028\u6C34\u0029\u0000\u0028\u706B\u0029\u0000\u0028\u7279\u0029\u0000\u0028\u76E3\u0029\u0000\u0028\u793E"d~ -"\u0029\u0000\u0028\u795D\u0029\u0000\u0028\u796D\u0029\u0000\u0028\u81EA\u0029\u0000\u0028\u81F3\u0029\u0000\u0028"d~ -"\u8CA1\u0029\u0000\u0028\u8CC7\u0029\u0000\u0028\u91D1\u0029\u0000\u0029\u0000\u002A\u0000\u002B\u0000\u002C\u0000"d~ -"\u002D\u0000\u002E\u0000\u002E\u002E\u0000\u002E\u002E\u002E\u0000\u002F\u0000\u0030\u0000\u0030\u002C\u0000\u0030"d~ -"\u002E\u0000\u0030\u2044\u0033\u0000\u0030\u70B9\u0000\u0031\u0000\u0031\u002C\u0000\u0031\u002E\u0000\u0031\u0030"d~ -"\u0000\u0031\u0030\u002E\u0000\u0031\u0030\u65E5\u0000\u0031\u0030\u6708\u0000\u0031\u0030\u70B9\u0000\u0031\u0031"d~ -"\u0000\u0031\u0031\u002E\u0000\u0031\u0031\u65E5\u0000\u0031\u0031\u6708\u0000\u0031\u0031\u70B9\u0000\u0031\u0032"d~ -"\u0000\u0031\u0032\u002E\u0000\u0031\u0032\u65E5\u0000\u0031\u0032\u6708\u0000\u0031\u0032\u70B9\u0000\u0031\u0033"d~ -"\u0000\u0031\u0033\u002E\u0000\u0031\u0033\u65E5\u0000\u0031\u0033\u70B9\u0000\u0031\u0034\u0000\u0031\u0034\u002E"d~ -"\u0000\u0031\u0034\u65E5\u0000\u0031\u0034\u70B9\u0000\u0031\u0035\u0000\u0031\u0035\u002E\u0000\u0031\u0035\u65E5"d~ -"\u0000\u0031\u0035\u70B9\u0000\u0031\u0036\u0000\u0031\u0036\u002E\u0000\u0031\u0036\u65E5\u0000\u0031\u0036\u70B9"d~ -"\u0000\u0031\u0037\u0000\u0031\u0037\u002E\u0000\u0031\u0037\u65E5\u0000\u0031\u0037\u70B9\u0000\u0031\u0038\u0000"d~ -"\u0031\u0038\u002E\u0000\u0031\u0038\u65E5\u0000\u0031\u0038\u70B9\u0000\u0031\u0039\u0000\u0031\u0039\u002E\u0000"d~ -"\u0031\u0039\u65E5\u0000\u0031\u0039\u70B9\u0000\u0031\u2044\u0000\u0031\u2044\u0031\u0030\u0000\u0031\u2044\u0032"d~ -"\u0000\u0031\u2044\u0033\u0000\u0031\u2044\u0034\u0000\u0031\u2044\u0035\u0000\u0031\u2044\u0036\u0000\u0031\u2044"d~ -"\u0037\u0000\u0031\u2044\u0038\u0000\u0031\u2044\u0039\u0000\u0031\u65E5\u0000\u0031\u6708\u0000\u0031\u70B9\u0000"d~ -"\u0032\u0000\u0032\u002C\u0000\u0032\u002E\u0000\u0032\u0030\u0000\u0032\u0030\u002E\u0000\u0032\u0030\u65E5\u0000"d~ -"\u0032\u0030\u70B9\u0000\u0032\u0031\u0000\u0032\u0031\u65E5\u0000\u0032\u0031\u70B9\u0000\u0032\u0032\u0000\u0032"d~ -"\u0032\u65E5\u0000\u0032\u0032\u70B9\u0000\u0032\u0033\u0000\u0032\u0033\u65E5\u0000\u0032\u0033\u70B9\u0000\u0032"d~ -"\u0034\u0000\u0032\u0034\u65E5\u0000\u0032\u0034\u70B9\u0000\u0032\u0035\u0000\u0032\u0035\u65E5\u0000\u0032\u0036"d~ -"\u0000\u0032\u0036\u65E5\u0000\u0032\u0037\u0000\u0032\u0037\u65E5\u0000\u0032\u0038\u0000\u0032\u0038\u65E5\u0000"d~ -"\u0032\u0039\u0000\u0032\u0039\u65E5\u0000\u0032\u2044\u0033\u0000\u0032\u2044\u0035\u0000\u0032\u65E5\u0000\u0032"d~ -"\u6708\u0000\u0032\u70B9\u0000\u0033\u0000\u0033\u002C\u0000\u0033\u002E\u0000\u0033\u0030\u0000\u0033\u0030\u65E5"d~ -"\u0000\u0033\u0031\u0000\u0033\u0031\u65E5\u0000\u0033\u0032\u0000\u0033\u0033\u0000\u0033\u0034\u0000\u0033\u0035"d~ -"\u0000\u0033\u0036\u0000\u0033\u0037\u0000\u0033\u0038\u0000\u0033\u0039\u0000\u0033\u2044\u0034\u0000\u0033\u2044"d~ -"\u0035\u0000\u0033\u2044\u0038\u0000\u0033\u65E5\u0000\u0033\u6708\u0000\u0033\u70B9\u0000\u0034\u0000\u0034\u002C"d~ -"\u0000\u0034\u002E\u0000\u0034\u0030\u0000\u0034\u0031\u0000\u0034\u0032\u0000\u0034\u0033\u0000\u0034\u0034\u0000"d~ -"\u0034\u0035\u0000\u0034\u0036\u0000\u0034\u0037\u0000\u0034\u0038\u0000\u0034\u0039\u0000\u0034\u2044\u0035\u0000"d~ -"\u0034\u65E5\u0000\u0034\u6708\u0000\u0034\u70B9\u0000\u0035\u0000\u0035\u002C\u0000\u0035\u002E\u0000\u0035\u0030"d~ -"\u0000\u0035\u2044\u0036\u0000\u0035\u2044\u0038\u0000\u0035\u65E5\u0000\u0035\u6708\u0000\u0035\u70B9\u0000\u0036"d~ -"\u0000\u0036\u002C\u0000\u0036\u002E\u0000\u0036\u65E5\u0000\u0036\u6708\u0000\u0036\u70B9\u0000\u0037\u0000\u0037"d~ -"\u002C\u0000\u0037\u002E\u0000\u0037\u2044\u0038\u0000\u0037\u65E5\u0000\u0037\u6708\u0000\u0037\u70B9\u0000\u0038"d~ -"\u0000\u0038\u002C\u0000\u0038\u002E\u0000\u0038\u65E5\u0000\u0038\u6708\u0000\u0038\u70B9\u0000\u0039\u0000\u0039"d~ -"\u002C\u0000\u0039\u002E\u0000\u0039\u65E5\u0000\u0039\u6708\u0000\u0039\u70B9\u0000\u003A\u0000\u003A\u003A\u003D"d~ -"\u0000\u003B\u0000\u003C\u0000\u003C\u0338\u0000\u003D\u0000\u003D\u003D\u0000\u003D\u003D\u003D\u0000\u003D\u0338"d~ -"\u0000\u003E\u0000\u003E\u0338\u0000\u003F\u0000\u003F\u0021\u0000\u003F\u003F\u0000\u0040\u0000\u0041\u0000\u0041"d~ -"\u0055\u0000\u0041\u0300\u0000\u0041\u0301\u0000\u0041\u0302\u0000\u0041\u0302\u0300\u0000\u0041\u0302\u0301\u0000"d~ -"\u0041\u0302\u0303\u0000\u0041\u0302\u0309\u0000\u0041\u0303\u0000\u0041\u0304\u0000\u0041\u0306\u0000\u0041\u0306"d~ -"\u0300\u0000\u0041\u0306\u0301\u0000\u0041\u0306\u0303\u0000\u0041\u0306\u0309\u0000\u0041\u0307\u0000\u0041\u0307"d~ -"\u0304\u0000\u0041\u0308\u0000\u0041\u0308\u0304\u0000\u0041\u0309\u0000\u0041\u030A\u0000\u0041\u030A\u0301\u0000"d~ -"\u0041\u030C\u0000\u0041\u030F\u0000\u0041\u0311\u0000\u0041\u0323\u0000\u0041\u0323\u0302\u0000\u0041\u0323\u0306"d~ -"\u0000\u0041\u0325\u0000\u0041\u0328\u0000\u0041\u2215\u006D\u0000\u0042\u0000\u0042\u0071\u0000\u0042\u0307\u0000"d~ -"\u0042\u0323\u0000\u0042\u0331\u0000\u0043\u0000\u0043\u0044\u0000\u0043\u006F\u002E\u0000\u0043\u0301\u0000\u0043"d~ -"\u0302\u0000\u0043\u0307\u0000\u0043\u030C\u0000\u0043\u0327\u0000\u0043\u0327\u0301\u0000\u0043\u2215\u006B\u0067"d~ -"\u0000\u0044\u0000\u0044\u004A\u0000\u0044\u005A\u0000\u0044\u005A\u030C\u0000\u0044\u007A\u0000\u0044\u007A\u030C"d~ -"\u0000\u0044\u0307\u0000\u0044\u030C\u0000\u0044\u0323\u0000\u0044\u0327\u0000\u0044\u032D\u0000\u0044\u0331\u0000"d~ -"\u0045\u0000\u0045\u0300\u0000\u0045\u0301\u0000\u0045\u0302\u0000\u0045\u0302\u0300\u0000\u0045\u0302\u0301\u0000"d~ -"\u0045\u0302\u0303\u0000\u0045\u0302\u0309\u0000\u0045\u0303\u0000\u0045\u0304\u0000\u0045\u0304\u0300\u0000\u0045"d~ -"\u0304\u0301\u0000\u0045\u0306\u0000\u0045\u0307\u0000\u0045\u0308\u0000\u0045\u0309\u0000\u0045\u030C\u0000\u0045"d~ -"\u030F\u0000\u0045\u0311\u0000\u0045\u0323\u0000\u0045\u0323\u0302\u0000\u0045\u0327\u0000\u0045\u0327\u0306\u0000"d~ -"\u0045\u0328\u0000\u0045\u032D\u0000\u0045\u0330\u0000\u0046\u0000\u0046\u0041\u0058\u0000\u0046\u0307\u0000\u0047"d~ -"\u0000\u0047\u0042\u0000\u0047\u0048\u007A\u0000\u0047\u0050\u0061\u0000\u0047\u0079\u0000\u0047\u0301\u0000\u0047"d~ -"\u0302\u0000\u0047\u0304\u0000\u0047\u0306\u0000\u0047\u0307\u0000\u0047\u030C\u0000\u0047\u0327\u0000\u0048\u0000"d~ -"\u0048\u0050\u0000\u0048\u0056\u0000\u0048\u0067\u0000\u0048\u007A\u0000\u0048\u0302\u0000\u0048\u0307\u0000\u0048"d~ -"\u0308\u0000\u0048\u030C\u0000\u0048\u0323\u0000\u0048\u0327\u0000\u0048\u032E\u0000\u0049\u0000\u0049\u0049\u0000"d~ -"\u0049\u0049\u0049\u0000\u0049\u004A\u0000\u0049\u0055\u0000\u0049\u0056\u0000\u0049\u0058\u0000\u0049\u0300\u0000"d~ -"\u0049\u0301\u0000\u0049\u0302\u0000\u0049\u0303\u0000\u0049\u0304\u0000\u0049\u0306\u0000\u0049\u0307\u0000\u0049"d~ -"\u0308\u0000\u0049\u0308\u0301\u0000\u0049\u0309\u0000\u0049\u030C\u0000\u0049\u030F\u0000\u0049\u0311\u0000\u0049"d~ -"\u0323\u0000\u0049\u0328\u0000\u0049\u0330\u0000\u004A\u0000\u004A\u0302\u0000\u004B\u0000\u004B\u0042\u0000\u004B"d~ -"\u004B\u0000\u004B\u004D\u0000\u004B\u0301\u0000\u004B\u030C\u0000\u004B\u0323\u0000\u004B\u0327\u0000\u004B\u0331"d~ -"\u0000\u004C\u0000\u004C\u004A\u0000\u004C\u0054\u0044\u0000\u004C\u006A\u0000\u004C\u00B7\u0000\u004C\u0301\u0000"d~ -"\u004C\u030C\u0000\u004C\u0323\u0000\u004C\u0323\u0304\u0000\u004C\u0327\u0000\u004C\u032D\u0000\u004C\u0331\u0000"d~ -"\u004D\u0000\u004D\u0042\u0000\u004D\u0043\u0000\u004D\u0044\u0000\u004D\u0048\u007A\u0000\u004D\u0050\u0061\u0000"d~ -"\u004D\u0052\u0000\u004D\u0056\u0000\u004D\u0057\u0000\u004D\u0301\u0000\u004D\u0307\u0000\u004D\u0323\u0000\u004D"d~ -"\u03A9\u0000\u004E\u0000\u004E\u004A\u0000\u004E\u006A\u0000\u004E\u006F\u0000\u004E\u0300\u0000\u004E\u0301\u0000"d~ -"\u004E\u0303\u0000\u004E\u0307\u0000\u004E\u030C\u0000\u004E\u0323\u0000\u004E\u0327\u0000\u004E\u032D\u0000\u004E"d~ -"\u0331\u0000\u004F\u0000\u004F\u0300\u0000\u004F\u0301\u0000\u004F\u0302\u0000\u004F\u0302\u0300\u0000\u004F\u0302"d~ -"\u0301\u0000\u004F\u0302\u0303\u0000\u004F\u0302\u0309\u0000\u004F\u0303\u0000\u004F\u0303\u0301\u0000\u004F\u0303"d~ -"\u0304\u0000\u004F\u0303\u0308\u0000\u004F\u0304\u0000\u004F\u0304\u0300\u0000\u004F\u0304\u0301\u0000\u004F\u0306"d~ -"\u0000\u004F\u0307\u0000\u004F\u0307\u0304\u0000\u004F\u0308\u0000\u004F\u0308\u0304\u0000\u004F\u0309\u0000\u004F"d~ -"\u030B\u0000\u004F\u030C\u0000\u004F\u030F\u0000\u004F\u0311\u0000\u004F\u031B\u0000\u004F\u031B\u0300\u0000\u004F"d~ -"\u031B\u0301\u0000\u004F\u031B\u0303\u0000\u004F\u031B\u0309\u0000\u004F\u031B\u0323\u0000\u004F\u0323\u0000\u004F"d~ -"\u0323\u0302\u0000\u004F\u0328\u0000\u004F\u0328\u0304\u0000\u0050\u0000\u0050\u0048\u0000\u0050\u0050\u004D\u0000"d~ -"\u0050\u0050\u0056\u0000\u0050\u0052\u0000\u0050\u0054\u0045\u0000\u0050\u0061\u0000\u0050\u0301\u0000\u0050\u0307"d~ -"\u0000\u0051\u0000\u0052\u0000\u0052\u0073\u0000\u0052\u0301\u0000\u0052\u0307\u0000\u0052\u030C\u0000\u0052\u030F"d~ -"\u0000\u0052\u0311\u0000\u0052\u0323\u0000\u0052\u0323\u0304\u0000\u0052\u0327\u0000\u0052\u0331\u0000\u0053\u0000"d~ -"\u0053\u0044\u0000\u0053\u004D\u0000\u0053\u0053\u0000\u0053\u0076\u0000\u0053\u0301\u0000\u0053\u0301\u0307\u0000"d~ -"\u0053\u0302\u0000\u0053\u0307\u0000\u0053\u030C\u0000\u0053\u030C\u0307\u0000\u0053\u0323\u0000\u0053\u0323\u0307"d~ -"\u0000\u0053\u0326\u0000\u0053\u0327\u0000\u0054\u0000\u0054\u0045\u004C\u0000\u0054\u0048\u007A\u0000\u0054\u004D"d~ -"\u0000\u0054\u0307\u0000\u0054\u030C\u0000\u0054\u0323\u0000\u0054\u0326\u0000\u0054\u0327\u0000\u0054\u032D\u0000"d~ -"\u0054\u0331\u0000\u0055\u0000\u0055\u0300\u0000\u0055\u0301\u0000\u0055\u0302\u0000\u0055\u0303\u0000\u0055\u0303"d~ -"\u0301\u0000\u0055\u0304\u0000\u0055\u0304\u0308\u0000\u0055\u0306\u0000\u0055\u0308\u0000\u0055\u0308\u0300\u0000"d~ -"\u0055\u0308\u0301\u0000\u0055\u0308\u0304\u0000\u0055\u0308\u030C\u0000\u0055\u0309\u0000\u0055\u030A\u0000\u0055"d~ -"\u030B\u0000\u0055\u030C\u0000\u0055\u030F\u0000\u0055\u0311\u0000\u0055\u031B\u0000\u0055\u031B\u0300\u0000\u0055"d~ -"\u031B\u0301\u0000\u0055\u031B\u0303\u0000\u0055\u031B\u0309\u0000\u0055\u031B\u0323\u0000\u0055\u0323\u0000\u0055"d~ -"\u0324\u0000\u0055\u0328\u0000\u0055\u032D\u0000\u0055\u0330\u0000\u0056\u0000\u0056\u0049\u0000\u0056\u0049\u0049"d~ -"\u0000\u0056\u0049\u0049\u0049\u0000\u0056\u0303\u0000\u0056\u0323\u0000\u0056\u2215\u006D\u0000\u0057\u0000\u0057"d~ -"\u0043\u0000\u0057\u005A\u0000\u0057\u0062\u0000\u0057\u0300\u0000\u0057\u0301\u0000\u0057\u0302\u0000\u0057\u0307"d~ -"\u0000\u0057\u0308\u0000\u0057\u0323\u0000\u0058\u0000\u0058\u0049\u0000\u0058\u0049\u0049\u0000\u0058\u0307\u0000"d~ -"\u0058\u0308\u0000\u0059\u0000\u0059\u0300\u0000\u0059\u0301\u0000\u0059\u0302\u0000\u0059\u0303\u0000\u0059\u0304"d~ -"\u0000\u0059\u0307\u0000\u0059\u0308\u0000\u0059\u0309\u0000\u0059\u0323\u0000\u005A\u0000\u005A\u0301\u0000\u005A"d~ -"\u0302\u0000\u005A\u0307\u0000\u005A\u030C\u0000\u005A\u0323\u0000\u005A\u0331\u0000\u005B\u0000\u005C\u0000\u005D"d~ -"\u0000\u005E\u0000\u005F\u0000\u0060\u0000\u0061\u0000\u0061\u002E\u006D\u002E\u0000\u0061\u002F\u0063\u0000\u0061"d~ -"\u002F\u0073\u0000\u0061\u02BE\u0000\u0061\u0300\u0000\u0061\u0301\u0000\u0061\u0302\u0000\u0061\u0302\u0300\u0000"d~ -"\u0061\u0302\u0301\u0000\u0061\u0302\u0303\u0000\u0061\u0302\u0309\u0000\u0061\u0303\u0000\u0061\u0304\u0000\u0061"d~ -"\u0306\u0000\u0061\u0306\u0300\u0000\u0061\u0306\u0301\u0000\u0061\u0306\u0303\u0000\u0061\u0306\u0309\u0000\u0061"d~ -"\u0307\u0000\u0061\u0307\u0304\u0000\u0061\u0308\u0000\u0061\u0308\u0304\u0000\u0061\u0309\u0000\u0061\u030A\u0000"d~ -"\u0061\u030A\u0301\u0000\u0061\u030C\u0000\u0061\u030F\u0000\u0061\u0311\u0000\u0061\u0323\u0000\u0061\u0323\u0302"d~ -"\u0000\u0061\u0323\u0306\u0000\u0061\u0325\u0000\u0061\u0328\u0000\u0062\u0000\u0062\u0061\u0072\u0000\u0062\u0307"d~ -"\u0000\u0062\u0323\u0000\u0062\u0331\u0000\u0063\u0000\u0063\u002F\u006F\u0000\u0063\u002F\u0075\u0000\u0063\u0061"d~ -"\u006C\u0000\u0063\u0063\u0000\u0063\u0064\u0000\u0063\u006D\u0000\u0063\u006D\u0032\u0000\u0063\u006D\u0033\u0000"d~ -"\u0063\u0301\u0000\u0063\u0302\u0000\u0063\u0307\u0000\u0063\u030C\u0000\u0063\u0327\u0000\u0063\u0327\u0301\u0000"d~ -"\u0064\u0000\u0064\u0042\u0000\u0064\u0061\u0000\u0064\u006C\u0000\u0064\u006D\u0000\u0064\u006D\u0032\u0000\u0064"d~ -"\u006D\u0033\u0000\u0064\u007A\u0000\u0064\u007A\u030C\u0000\u0064\u0307\u0000\u0064\u030C\u0000\u0064\u0323\u0000"d~ -"\u0064\u0327\u0000\u0064\u032D\u0000\u0064\u0331\u0000\u0065\u0000\u0065\u0056\u0000\u0065\u0072\u0067\u0000\u0065"d~ -"\u0300\u0000\u0065\u0301\u0000\u0065\u0302\u0000\u0065\u0302\u0300\u0000\u0065\u0302\u0301\u0000\u0065\u0302\u0303"d~ -"\u0000\u0065\u0302\u0309\u0000\u0065\u0303\u0000\u0065\u0304\u0000\u0065\u0304\u0300\u0000\u0065\u0304\u0301\u0000"d~ -"\u0065\u0306\u0000\u0065\u0307\u0000\u0065\u0308\u0000\u0065\u0309\u0000\u0065\u030C\u0000\u0065\u030F\u0000\u0065"d~ -"\u0311\u0000\u0065\u0323\u0000\u0065\u0323\u0302\u0000\u0065\u0327\u0000\u0065\u0327\u0306\u0000\u0065\u0328\u0000"d~ -"\u0065\u032D\u0000\u0065\u0330\u0000\u0066\u0000\u0066\u0066\u0000\u0066\u0066\u0069\u0000\u0066\u0066\u006C\u0000"d~ -"\u0066\u0069\u0000\u0066\u006C\u0000\u0066\u006D\u0000\u0066\u0307\u0000\u0067\u0000\u0067\u0061\u006C\u0000\u0067"d~ -"\u0301\u0000\u0067\u0302\u0000\u0067\u0304\u0000\u0067\u0306\u0000\u0067\u0307\u0000\u0067\u030C\u0000\u0067\u0327"d~ -"\u0000\u0068\u0000\u0068\u0050\u0061\u0000\u0068\u0061\u0000\u0068\u0302\u0000\u0068\u0307\u0000\u0068\u0308\u0000"d~ -"\u0068\u030C\u0000\u0068\u0323\u0000\u0068\u0327\u0000\u0068\u032E\u0000\u0068\u0331\u0000\u0069\u0000\u0069\u0069"d~ -"\u0000\u0069\u0069\u0069\u0000\u0069\u006A\u0000\u0069\u006E\u0000\u0069\u0076\u0000\u0069\u0078\u0000\u0069\u0300"d~ -"\u0000\u0069\u0301\u0000\u0069\u0302\u0000\u0069\u0303\u0000\u0069\u0304\u0000\u0069\u0306\u0000\u0069\u0308\u0000"d~ -"\u0069\u0308\u0301\u0000\u0069\u0309\u0000\u0069\u030C\u0000\u0069\u030F\u0000\u0069\u0311\u0000\u0069\u0323\u0000"d~ -"\u0069\u0328\u0000\u0069\u0330\u0000\u006A\u0000\u006A\u0302\u0000\u006A\u030C\u0000\u006B\u0000\u006B\u0041\u0000"d~ -"\u006B\u0048\u007A\u0000\u006B\u0050\u0061\u0000\u006B\u0056\u0000\u006B\u0057\u0000\u006B\u0063\u0061\u006C\u0000"d~ -"\u006B\u0067\u0000\u006B\u006C\u0000\u006B\u006D\u0000\u006B\u006D\u0032\u0000\u006B\u006D\u0033\u0000\u006B\u0074"d~ -"\u0000\u006B\u0301\u0000\u006B\u030C\u0000\u006B\u0323\u0000\u006B\u0327\u0000\u006B\u0331\u0000\u006B\u03A9\u0000"d~ -"\u006C\u0000\u006C\u006A\u0000\u006C\u006D\u0000\u006C\u006E\u0000\u006C\u006F\u0067\u0000\u006C\u0078\u0000\u006C"d~ -"\u00B7\u0000\u006C\u0301\u0000\u006C\u030C\u0000\u006C\u0323\u0000\u006C\u0323\u0304\u0000\u006C\u0327\u0000\u006C"d~ -"\u032D\u0000\u006C\u0331\u0000\u006D\u0000\u006D\u0032\u0000\u006D\u0033\u0000\u006D\u0041\u0000\u006D\u0056\u0000"d~ -"\u006D\u0057\u0000\u006D\u0062\u0000\u006D\u0067\u0000\u006D\u0069\u006C\u0000\u006D\u006C\u0000\u006D\u006D\u0000"d~ -"\u006D\u006D\u0032\u0000\u006D\u006D\u0033\u0000\u006D\u006F\u006C\u0000\u006D\u0073\u0000\u006D\u0301\u0000\u006D"d~ -"\u0307\u0000\u006D\u0323\u0000\u006D\u2215\u0073\u0000\u006D\u2215\u0073\u0032\u0000\u006E\u0000\u006E\u0041\u0000"d~ -"\u006E\u0046\u0000\u006E\u0056\u0000\u006E\u0057\u0000\u006E\u006A\u0000\u006E\u006D\u0000\u006E\u0073\u0000\u006E"d~ -"\u0300\u0000\u006E\u0301\u0000\u006E\u0303\u0000\u006E\u0307\u0000\u006E\u030C\u0000\u006E\u0323\u0000\u006E\u0327"d~ -"\u0000\u006E\u032D\u0000\u006E\u0331\u0000\u006F\u0000\u006F\u0056\u0000\u006F\u0300\u0000\u006F\u0301\u0000\u006F"d~ -"\u0302\u0000\u006F\u0302\u0300\u0000\u006F\u0302\u0301\u0000\u006F\u0302\u0303\u0000\u006F\u0302\u0309\u0000\u006F"d~ -"\u0303\u0000\u006F\u0303\u0301\u0000\u006F\u0303\u0304\u0000\u006F\u0303\u0308\u0000\u006F\u0304\u0000\u006F\u0304"d~ -"\u0300\u0000\u006F\u0304\u0301\u0000\u006F\u0306\u0000\u006F\u0307\u0000\u006F\u0307\u0304\u0000\u006F\u0308\u0000"d~ -"\u006F\u0308\u0304\u0000\u006F\u0309\u0000\u006F\u030B\u0000\u006F\u030C\u0000\u006F\u030F\u0000\u006F\u0311\u0000"d~ -"\u006F\u031B\u0000\u006F\u031B\u0300\u0000\u006F\u031B\u0301\u0000\u006F\u031B\u0303\u0000\u006F\u031B\u0309\u0000"d~ -"\u006F\u031B\u0323\u0000\u006F\u0323\u0000\u006F\u0323\u0302\u0000\u006F\u0328\u0000\u006F\u0328\u0304\u0000\u0070"d~ -"\u0000\u0070\u002E\u006D\u002E\u0000\u0070\u0041\u0000\u0070\u0046\u0000\u0070\u0056\u0000\u0070\u0057\u0000\u0070"d~ -"\u0063\u0000\u0070\u0073\u0000\u0070\u0301\u0000\u0070\u0307\u0000\u0071\u0000\u0072\u0000\u0072\u0061\u0064\u0000"d~ -"\u0072\u0061\u0064\u2215\u0073\u0000\u0072\u0061\u0064\u2215\u0073\u0032\u0000\u0072\u0301\u0000\u0072\u0307\u0000"d~ -"\u0072\u030C\u0000\u0072\u030F\u0000\u0072\u0311\u0000\u0072\u0323\u0000\u0072\u0323\u0304\u0000\u0072\u0327\u0000"d~ -"\u0072\u0331\u0000\u0073\u0000\u0073\u0072\u0000\u0073\u0074\u0000\u0073\u0301\u0000\u0073\u0301\u0307\u0000\u0073"d~ -"\u0302\u0000\u0073\u0307\u0000\u0073\u030C\u0000\u0073\u030C\u0307\u0000\u0073\u0323\u0000\u0073\u0323\u0307\u0000"d~ -"\u0073\u0326\u0000\u0073\u0327\u0000\u0074\u0000\u0074\u0307\u0000\u0074\u0308\u0000\u0074\u030C\u0000\u0074\u0323"d~ -"\u0000\u0074\u0326\u0000\u0074\u0327\u0000\u0074\u032D\u0000\u0074\u0331\u0000\u0075\u0000\u0075\u0300\u0000\u0075"d~ -"\u0301\u0000\u0075\u0302\u0000\u0075\u0303\u0000\u0075\u0303\u0301\u0000\u0075\u0304\u0000\u0075\u0304\u0308\u0000"d~ -"\u0075\u0306\u0000\u0075\u0308\u0000\u0075\u0308\u0300\u0000\u0075\u0308\u0301\u0000\u0075\u0308\u0304\u0000\u0075"d~ -"\u0308\u030C\u0000\u0075\u0309\u0000\u0075\u030A\u0000\u0075\u030B\u0000\u0075\u030C\u0000\u0075\u030F\u0000\u0075"d~ -"\u0311\u0000\u0075\u031B\u0000\u0075\u031B\u0300\u0000\u0075\u031B\u0301\u0000\u0075\u031B\u0303\u0000\u0075\u031B"d~ -"\u0309\u0000\u0075\u031B\u0323\u0000\u0075\u0323\u0000\u0075\u0324\u0000\u0075\u0328\u0000\u0075\u032D\u0000\u0075"d~ -"\u0330\u0000\u0076\u0000\u0076\u0069\u0000\u0076\u0069\u0069\u0000\u0076\u0069\u0069\u0069\u0000\u0076\u0303\u0000"d~ -"\u0076\u0323\u0000\u0077\u0000\u0077\u0300\u0000\u0077\u0301\u0000\u0077\u0302\u0000\u0077\u0307\u0000\u0077\u0308"d~ -"\u0000\u0077\u030A\u0000\u0077\u0323\u0000\u0078\u0000\u0078\u0069\u0000\u0078\u0069\u0069\u0000\u0078\u0307\u0000"d~ -"\u0078\u0308\u0000\u0079\u0000\u0079\u0300\u0000\u0079\u0301\u0000\u0079\u0302\u0000\u0079\u0303\u0000\u0079\u0304"d~ -"\u0000\u0079\u0307\u0000\u0079\u0308\u0000\u0079\u0309\u0000\u0079\u030A\u0000\u0079\u0323\u0000\u007A\u0000\u007A"d~ -"\u0301\u0000\u007A\u0302\u0000\u007A\u0307\u0000\u007A\u030C\u0000\u007A\u0323\u0000\u007A\u0331\u0000\u007B\u0000"d~ -"\u007C\u0000\u007D\u0000\u007E\u0000\u00A2\u0000\u00A3\u0000\u00A5\u0000\u00A6\u0000\u00AC\u0000\u00B0\u0043\u0000"d~ -"\u00B0\u0046\u0000\u00B7\u0000\u00C6\u0000\u00C6\u0301\u0000\u00C6\u0304\u0000\u00D8\u0301\u0000\u00E6\u0000\u00E6"d~ -"\u0301\u0000\u00E6\u0304\u0000\u00F0\u0000\u00F8\u0000\u00F8\u0301\u0000\u0126\u0000\u0127\u0000\u0131\u0000\u014B"d~ -"\u0000\u0153\u0000\u018E\u0000\u0190\u0000\u01AB\u0000\u01B7\u030C\u0000\u01C0\u0000\u01C1\u0000\u01C2\u0000\u0222"d~ -"\u0000\u0237\u0000\u0250\u0000\u0251\u0000\u0252\u0000\u0253\u0000\u0254\u0000\u0255\u0000\u0256\u0000\u0257\u0000"d~ -"\u0258\u0000\u0259\u0000\u025B\u0000\u025C\u0000\u025E\u0000\u025F\u0000\u0260\u0000\u0261\u0000\u0262\u0000\u0263"d~ -"\u0000\u0264\u0000\u0265\u0000\u0266\u0000\u0267\u0000\u0268\u0000\u0269\u0000\u026A\u0000\u026B\u0000\u026C\u0000"d~ -"\u026D\u0000\u026E\u0000\u026F\u0000\u0270\u0000\u0271\u0000\u0272\u0000\u0273\u0000\u0274\u0000\u0275\u0000\u0276"d~ -"\u0000\u0277\u0000\u0278\u0000\u0279\u0000\u027A\u0000\u027B\u0000\u027D\u0000\u027E\u0000\u0280\u0000\u0281\u0000"d~ -"\u0282\u0000\u0283\u0000\u0284\u0000\u0288\u0000\u0289\u0000\u028A\u0000\u028B\u0000\u028C\u0000\u028D\u0000\u028E"d~ -"\u0000\u028F\u0000\u0290\u0000\u0291\u0000\u0292\u0000\u0292\u030C\u0000\u0295\u0000\u0298\u0000\u0299\u0000\u029B"d~ -"\u0000\u029C\u0000\u029D\u0000\u029F\u0000\u02A1\u0000\u02A2\u0000\u02A3\u0000\u02A4\u0000\u02A5\u0000\u02A6\u0000"d~ -"\u02A7\u0000\u02A8\u0000\u02A9\u0000\u02AA\u0000\u02AB\u0000\u02B9\u0000\u02BC\u006E\u0000\u02D0\u0000\u02D1\u0000"d~ -"\u0300\u0000\u0301\u0000\u0308\u0301\u0000\u0313\u0000\u0391\u0000\u0391\u0300\u0000\u0391\u0301\u0000\u0391\u0304"d~ -"\u0000\u0391\u0306\u0000\u0391\u0313\u0000\u0391\u0313\u0300\u0000\u0391\u0313\u0300\u0345\u0000\u0391\u0313\u0301"d~ -"\u0000\u0391\u0313\u0301\u0345\u0000\u0391\u0313\u0342\u0000\u0391\u0313\u0342\u0345\u0000\u0391\u0313\u0345\u0000"d~ -"\u0391\u0314\u0000\u0391\u0314\u0300\u0000\u0391\u0314\u0300\u0345\u0000\u0391\u0314\u0301\u0000\u0391\u0314\u0301"d~ -"\u0345\u0000\u0391\u0314\u0342\u0000\u0391\u0314\u0342\u0345\u0000\u0391\u0314\u0345\u0000\u0391\u0345\u0000\u0392"d~ -"\u0000\u0393\u0000\u0394\u0000\u0395\u0000\u0395\u0300\u0000\u0395\u0301\u0000\u0395\u0313\u0000\u0395\u0313\u0300"d~ -"\u0000\u0395\u0313\u0301\u0000\u0395\u0314\u0000\u0395\u0314\u0300\u0000\u0395\u0314\u0301\u0000\u0396\u0000\u0397"d~ -"\u0000\u0397\u0300\u0000\u0397\u0301\u0000\u0397\u0313\u0000\u0397\u0313\u0300\u0000\u0397\u0313\u0300\u0345\u0000"d~ -"\u0397\u0313\u0301\u0000\u0397\u0313\u0301\u0345\u0000\u0397\u0313\u0342\u0000\u0397\u0313\u0342\u0345\u0000\u0397"d~ -"\u0313\u0345\u0000\u0397\u0314\u0000\u0397\u0314\u0300\u0000\u0397\u0314\u0300\u0345\u0000\u0397\u0314\u0301\u0000"d~ -"\u0397\u0314\u0301\u0345\u0000\u0397\u0314\u0342\u0000\u0397\u0314\u0342\u0345\u0000\u0397\u0314\u0345\u0000\u0397"d~ -"\u0345\u0000\u0398\u0000\u0399\u0000\u0399\u0300\u0000\u0399\u0301\u0000\u0399\u0304\u0000\u0399\u0306\u0000\u0399"d~ -"\u0308\u0000\u0399\u0313\u0000\u0399\u0313\u0300\u0000\u0399\u0313\u0301\u0000\u0399\u0313\u0342\u0000\u0399\u0314"d~ -"\u0000\u0399\u0314\u0300\u0000\u0399\u0314\u0301\u0000\u0399\u0314\u0342\u0000\u039A\u0000\u039B\u0000\u039C\u0000"d~ -"\u039D\u0000\u039E\u0000\u039F\u0000\u039F\u0300\u0000\u039F\u0301\u0000\u039F\u0313\u0000\u039F\u0313\u0300\u0000"d~ -"\u039F\u0313\u0301\u0000\u039F\u0314\u0000\u039F\u0314\u0300\u0000\u039F\u0314\u0301\u0000\u03A0\u0000\u03A1\u0000"d~ -"\u03A1\u0314\u0000\u03A3\u0000\u03A4\u0000\u03A5\u0000\u03A5\u0300\u0000\u03A5\u0301\u0000\u03A5\u0304\u0000\u03A5"d~ -"\u0306\u0000\u03A5\u0308\u0000\u03A5\u0314\u0000\u03A5\u0314\u0300\u0000\u03A5\u0314\u0301\u0000\u03A5\u0314\u0342"d~ -"\u0000\u03A6\u0000\u03A7\u0000\u03A8\u0000\u03A9\u0000\u03A9\u0300\u0000\u03A9\u0301\u0000\u03A9\u0313\u0000\u03A9"d~ -"\u0313\u0300\u0000\u03A9\u0313\u0300\u0345\u0000\u03A9\u0313\u0301\u0000\u03A9\u0313\u0301\u0345\u0000\u03A9\u0313"d~ -"\u0342\u0000\u03A9\u0313\u0342\u0345\u0000\u03A9\u0313\u0345\u0000\u03A9\u0314\u0000\u03A9\u0314\u0300\u0000\u03A9"d~ -"\u0314\u0300\u0345\u0000\u03A9\u0314\u0301\u0000\u03A9\u0314\u0301\u0345\u0000\u03A9\u0314\u0342\u0000\u03A9\u0314"d~ -"\u0342\u0345\u0000\u03A9\u0314\u0345\u0000\u03A9\u0345\u0000\u03B1\u0000\u03B1\u0300\u0000\u03B1\u0300\u0345\u0000"d~ -"\u03B1\u0301\u0000\u03B1\u0301\u0345\u0000\u03B1\u0304\u0000\u03B1\u0306\u0000\u03B1\u0313\u0000\u03B1\u0313\u0300"d~ -"\u0000\u03B1\u0313\u0300\u0345\u0000\u03B1\u0313\u0301\u0000\u03B1\u0313\u0301\u0345\u0000\u03B1\u0313\u0342\u0000"d~ -"\u03B1\u0313\u0342\u0345\u0000\u03B1\u0313\u0345\u0000\u03B1\u0314\u0000\u03B1\u0314\u0300\u0000\u03B1\u0314\u0300"d~ -"\u0345\u0000\u03B1\u0314\u0301\u0000\u03B1\u0314\u0301\u0345\u0000\u03B1\u0314\u0342\u0000\u03B1\u0314\u0342\u0345"d~ -"\u0000\u03B1\u0314\u0345\u0000\u03B1\u0342\u0000\u03B1\u0342\u0345\u0000\u03B1\u0345\u0000\u03B2\u0000\u03B3\u0000"d~ -"\u03B4\u0000\u03B5\u0000\u03B5\u0300\u0000\u03B5\u0301\u0000\u03B5\u0313\u0000\u03B5\u0313\u0300\u0000\u03B5\u0313"d~ -"\u0301\u0000\u03B5\u0314\u0000\u03B5\u0314\u0300\u0000\u03B5\u0314\u0301\u0000\u03B6\u0000\u03B7\u0000\u03B7\u0300"d~ -"\u0000\u03B7\u0300\u0345\u0000\u03B7\u0301\u0000\u03B7\u0301\u0345\u0000\u03B7\u0313\u0000\u03B7\u0313\u0300\u0000"d~ -"\u03B7\u0313\u0300\u0345\u0000\u03B7\u0313\u0301\u0000\u03B7\u0313\u0301\u0345\u0000\u03B7\u0313\u0342\u0000\u03B7"d~ -"\u0313\u0342\u0345\u0000\u03B7\u0313\u0345\u0000\u03B7\u0314\u0000\u03B7\u0314\u0300\u0000\u03B7\u0314\u0300\u0345"d~ -"\u0000\u03B7\u0314\u0301\u0000\u03B7\u0314\u0301\u0345\u0000\u03B7\u0314\u0342\u0000\u03B7\u0314\u0342\u0345\u0000"d~ -"\u03B7\u0314\u0345\u0000\u03B7\u0342\u0000\u03B7\u0342\u0345\u0000\u03B7\u0345\u0000\u03B8\u0000\u03B9\u0000\u03B9"d~ -"\u0300\u0000\u03B9\u0301\u0000\u03B9\u0304\u0000\u03B9\u0306\u0000\u03B9\u0308\u0000\u03B9\u0308\u0300\u0000\u03B9"d~ -"\u0308\u0301\u0000\u03B9\u0308\u0342\u0000\u03B9\u0313\u0000\u03B9\u0313\u0300\u0000\u03B9\u0313\u0301\u0000\u03B9"d~ -"\u0313\u0342\u0000\u03B9\u0314\u0000\u03B9\u0314\u0300\u0000\u03B9\u0314\u0301\u0000\u03B9\u0314\u0342\u0000\u03B9"d~ -"\u0342\u0000\u03BA\u0000\u03BB\u0000\u03BC\u0000\u03BC\u0041\u0000\u03BC\u0046\u0000\u03BC\u0056\u0000\u03BC\u0057"d~ -"\u0000\u03BC\u0067\u0000\u03BC\u006C\u0000\u03BC\u006D\u0000\u03BC\u0073\u0000\u03BD\u0000\u03BE\u0000\u03BF\u0000"d~ -"\u03BF\u0300\u0000\u03BF\u0301\u0000\u03BF\u0313\u0000\u03BF\u0313\u0300\u0000\u03BF\u0313\u0301\u0000\u03BF\u0314"d~ -"\u0000\u03BF\u0314\u0300\u0000\u03BF\u0314\u0301\u0000\u03C0\u0000\u03C1\u0000\u03C1\u0313\u0000\u03C1\u0314\u0000"d~ -"\u03C2\u0000\u03C3\u0000\u03C4\u0000\u03C5\u0000\u03C5\u0300\u0000\u03C5\u0301\u0000\u03C5\u0304\u0000\u03C5\u0306"d~ -"\u0000\u03C5\u0308\u0000\u03C5\u0308\u0300\u0000\u03C5\u0308\u0301\u0000\u03C5\u0308\u0342\u0000\u03C5\u0313\u0000"d~ -"\u03C5\u0313\u0300\u0000\u03C5\u0313\u0301\u0000\u03C5\u0313\u0342\u0000\u03C5\u0314\u0000\u03C5\u0314\u0300\u0000"d~ -"\u03C5\u0314\u0301\u0000\u03C5\u0314\u0342\u0000\u03C5\u0342\u0000\u03C6\u0000\u03C7\u0000\u03C8\u0000\u03C9\u0000"d~ -"\u03C9\u0300\u0000\u03C9\u0300\u0345\u0000\u03C9\u0301\u0000\u03C9\u0301\u0345\u0000\u03C9\u0313\u0000\u03C9\u0313"d~ -"\u0300\u0000\u03C9\u0313\u0300\u0345\u0000\u03C9\u0313\u0301\u0000\u03C9\u0313\u0301\u0345\u0000\u03C9\u0313\u0342"d~ -"\u0000\u03C9\u0313\u0342\u0345\u0000\u03C9\u0313\u0345\u0000\u03C9\u0314\u0000\u03C9\u0314\u0300\u0000\u03C9\u0314"d~ -"\u0300\u0345\u0000\u03C9\u0314\u0301\u0000\u03C9\u0314\u0301\u0345\u0000\u03C9\u0314\u0342\u0000\u03C9\u0314\u0342"d~ -"\u0345\u0000\u03C9\u0314\u0345\u0000\u03C9\u0342\u0000\u03C9\u0342\u0345\u0000\u03C9\u0345\u0000\u03DC\u0000\u03DD"d~ -"\u0000\u0406\u0308\u0000\u0410\u0306\u0000\u0410\u0308\u0000\u0413\u0301\u0000\u0415\u0300\u0000\u0415\u0306\u0000"d~ -"\u0415\u0308\u0000\u0416\u0306\u0000\u0416\u0308\u0000\u0417\u0308\u0000\u0418\u0300\u0000\u0418\u0304\u0000\u0418"d~ -"\u0306\u0000\u0418\u0308\u0000\u041A\u0301\u0000\u041E\u0308\u0000\u0423\u0304\u0000\u0423\u0306\u0000\u0423\u0308"d~ -"\u0000\u0423\u030B\u0000\u0427\u0308\u0000\u042B\u0308\u0000\u042D\u0308\u0000\u0430\u0000\u0430\u0306\u0000\u0430"d~ -"\u0308\u0000\u0431\u0000\u0432\u0000\u0433\u0000\u0433\u0301\u0000\u0434\u0000\u0435\u0000\u0435\u0300\u0000\u0435"d~ -"\u0306\u0000\u0435\u0308\u0000\u0436\u0000\u0436\u0306\u0000\u0436\u0308\u0000\u0437\u0000\u0437\u0308\u0000\u0438"d~ -"\u0000\u0438\u0300\u0000\u0438\u0304\u0000\u0438\u0306\u0000\u0438\u0308\u0000\u043A\u0000\u043A\u0301\u0000\u043B"d~ -"\u0000\u043C\u0000\u043D\u0000\u043E\u0000\u043E\u0308\u0000\u043F\u0000\u0440\u0000\u0441\u0000\u0442\u0000\u0443"d~ -"\u0000\u0443\u0304\u0000\u0443\u0306\u0000\u0443\u0308\u0000\u0443\u030B\u0000\u0444\u0000\u0445\u0000\u0446\u0000"d~ -"\u0447\u0000\u0447\u0308\u0000\u0448\u0000\u044A\u0000\u044B\u0000\u044B\u0308\u0000\u044C\u0000\u044D\u0000\u044D"d~ -"\u0308\u0000\u044E\u0000\u0455\u0000\u0456\u0000\u0456\u0308\u0000\u0458\u0000\u045F\u0000\u0474\u030F\u0000\u0475"d~ -"\u030F\u0000\u0491\u0000\u04AB\u0000\u04AF\u0000\u04B1\u0000\u04CF\u0000\u04D8\u0308\u0000\u04D9\u0000\u04D9\u0308"d~ -"\u0000\u04E8\u0308\u0000\u04E9\u0000\u04E9\u0308\u0000\u0565\u0582\u0000\u0574\u0565\u0000\u0574\u056B\u0000\u0574"d~ -"\u056D\u0000\u0574\u0576\u0000\u057E\u0576\u0000\u05D0\u0000\u05D0\u05B7\u0000\u05D0\u05B8\u0000\u05D0\u05BC\u0000"d~ -"\u05D0\u05DC\u0000\u05D1\u0000\u05D1\u05BC\u0000\u05D1\u05BF\u0000\u05D2\u0000\u05D2\u05BC\u0000\u05D3\u0000\u05D3"d~ -"\u05BC\u0000\u05D4\u0000\u05D4\u05BC\u0000\u05D5\u05B9\u0000\u05D5\u05BC\u0000\u05D6\u05BC\u0000\u05D8\u05BC\u0000"d~ -"\u05D9\u05B4\u0000\u05D9\u05BC\u0000\u05DA\u05BC\u0000\u05DB\u0000\u05DB\u05BC\u0000\u05DB\u05BF\u0000\u05DC\u0000"d~ -"\u05DC\u05BC\u0000\u05DD\u0000\u05DE\u05BC\u0000\u05E0\u05BC\u0000\u05E1\u05BC\u0000\u05E2\u0000\u05E3\u05BC\u0000"d~ -"\u05E4\u05BC\u0000\u05E4\u05BF\u0000\u05E6\u05BC\u0000\u05E7\u05BC\u0000\u05E8\u0000\u05E8\u05BC\u0000\u05E9\u05BC"d~ -"\u0000\u05E9\u05BC\u05C1\u0000\u05E9\u05BC\u05C2\u0000\u05E9\u05C1\u0000\u05E9\u05C2\u0000\u05EA\u0000\u05EA\u05BC"d~ -"\u0000\u05F2\u05B7\u0000\u0621\u0000\u0627\u0000\u0627\u0643\u0628\u0631\u0000\u0627\u0644\u0644\u0647\u0000\u0627"d~ -"\u064B\u0000\u0627\u0653\u0000\u0627\u0654\u0000\u0627\u0655\u0000\u0627\u0674\u0000\u0628\u0000\u0628\u062C\u0000"d~ -"\u0628\u062D\u0000\u0628\u062D\u064A\u0000\u0628\u062E\u0000\u0628\u062E\u064A\u0000\u0628\u0631\u0000\u0628\u0632"d~ -"\u0000\u0628\u0645\u0000\u0628\u0646\u0000\u0628\u0647\u0000\u0628\u0649\u0000\u0628\u064A\u0000\u0629\u0000\u062A"d~ -"\u0000\u062A\u062C\u0000\u062A\u062C\u0645\u0000\u062A\u062C\u0649\u0000\u062A\u062C\u064A\u0000\u062A\u062D\u0000"d~ -"\u062A\u062D\u062C\u0000\u062A\u062D\u0645\u0000\u062A\u062E\u0000\u062A\u062E\u0645\u0000\u062A\u062E\u0649\u0000"d~ -"\u062A\u062E\u064A\u0000\u062A\u0631\u0000\u062A\u0632\u0000\u062A\u0645\u0000\u062A\u0645\u062C\u0000\u062A\u0645"d~ -"\u062D\u0000\u062A\u0645\u062E\u0000\u062A\u0645\u0649\u0000\u062A\u0645\u064A\u0000\u062A\u0646\u0000\u062A\u0647"d~ -"\u0000\u062A\u0649\u0000\u062A\u064A\u0000\u062B\u0000\u062B\u062C\u0000\u062B\u0631\u0000\u062B\u0632\u0000\u062B"d~ -"\u0645\u0000\u062B\u0646\u0000\u062B\u0647\u0000\u062B\u0649\u0000\u062B\u064A\u0000\u062C\u0000\u062C\u062D\u0000"d~ -"\u062C\u062D\u0649\u0000\u062C\u062D\u064A\u0000\u062C\u0644\u0020\u062C\u0644\u0627\u0644\u0647\u0000\u062C\u0645"d~ -"\u0000\u062C\u0645\u062D\u0000\u062C\u0645\u0649\u0000\u062C\u0645\u064A\u0000\u062C\u0649\u0000\u062C\u064A\u0000"d~ -"\u062D\u0000\u062D\u062C\u0000\u062D\u062C\u064A\u0000\u062D\u0645\u0000\u062D\u0645\u0649\u0000\u062D\u0645\u064A"d~ -"\u0000\u062D\u0649\u0000\u062D\u064A\u0000\u062E\u0000\u062E\u062C\u0000\u062E\u062D\u0000\u062E\u0645\u0000\u062E"d~ -"\u0649\u0000\u062E\u064A\u0000\u062F\u0000\u0630\u0000\u0630\u0670\u0000\u0631\u0000\u0631\u0633\u0648\u0644\u0000"d~ -"\u0631\u0670\u0000\u0631\u06CC\u0627\u0644\u0000\u0632\u0000\u0633\u0000\u0633\u062C\u0000\u0633\u062C\u062D\u0000"d~ -"\u0633\u062C\u0649\u0000\u0633\u062D\u0000\u0633\u062D\u062C\u0000\u0633\u062E\u0000\u0633\u062E\u0649\u0000\u0633"d~ -"\u062E\u064A\u0000\u0633\u0631\u0000\u0633\u0645\u0000\u0633\u0645\u062C\u0000\u0633\u0645\u062D\u0000\u0633\u0645"d~ -"\u0645\u0000\u0633\u0647\u0000\u0633\u0649\u0000\u0633\u064A\u0000\u0634\u0000\u0634\u062C\u0000\u0634\u062C\u064A"d~ -"\u0000\u0634\u062D\u0000\u0634\u062D\u0645\u0000\u0634\u062D\u064A\u0000\u0634\u062E\u0000\u0634\u0631\u0000\u0634"d~ -"\u0645\u0000\u0634\u0645\u062E\u0000\u0634\u0645\u0645\u0000\u0634\u0647\u0000\u0634\u0649\u0000\u0634\u064A\u0000"d~ -"\u0635\u0000\u0635\u062D\u0000\u0635\u062D\u062D\u0000\u0635\u062D\u064A\u0000\u0635\u062E\u0000\u0635\u0631\u0000"d~ -"\u0635\u0644\u0639\u0645\u0000\u0635\u0644\u0649\u0000\u0635\u0644\u0649\u0020\u0627\u0644\u0644\u0647\u0020\u0639"d~ -"\u0644\u064A\u0647\u0020\u0648\u0633\u0644\u0645\u0000\u0635\u0644\u06D2\u0000\u0635\u0645\u0000\u0635\u0645\u0645"d~ -"\u0000\u0635\u0649\u0000\u0635\u064A\u0000\u0636\u0000\u0636\u062C\u0000\u0636\u062D\u0000\u0636\u062D\u0649\u0000"d~ -"\u0636\u062D\u064A\u0000\u0636\u062E\u0000\u0636\u062E\u0645\u0000\u0636\u0631\u0000\u0636\u0645\u0000\u0636\u0649"d~ -"\u0000\u0636\u064A\u0000\u0637\u0000\u0637\u062D\u0000\u0637\u0645\u0000\u0637\u0645\u062D\u0000\u0637\u0645\u0645"d~ -"\u0000\u0637\u0645\u064A\u0000\u0637\u0649\u0000\u0637\u064A\u0000\u0638\u0000\u0638\u0645\u0000\u0639\u0000\u0639"d~ -"\u062C\u0000\u0639\u062C\u0645\u0000\u0639\u0644\u064A\u0647\u0000\u0639\u0645\u0000\u0639\u0645\u0645\u0000\u0639"d~ -"\u0645\u0649\u0000\u0639\u0645\u064A\u0000\u0639\u0649\u0000\u0639\u064A\u0000\u063A\u0000\u063A\u062C\u0000\u063A"d~ -"\u0645\u0000\u063A\u0645\u0645\u0000\u063A\u0645\u0649\u0000\u063A\u0645\u064A\u0000\u063A\u0649\u0000\u063A\u064A"d~ -"\u0000\u0640\u064B\u0000\u0640\u064E\u0000\u0640\u064E\u0651\u0000\u0640\u064F\u0000\u0640\u064F\u0651\u0000\u0640"d~ -"\u0650\u0000\u0640\u0650\u0651\u0000\u0640\u0651\u0000\u0640\u0652\u0000\u0641\u0000\u0641\u062C\u0000\u0641\u062D"d~ -"\u0000\u0641\u062E\u0000\u0641\u062E\u0645\u0000\u0641\u0645\u0000\u0641\u0645\u064A\u0000\u0641\u0649\u0000\u0641"d~ -"\u064A\u0000\u0642\u0000\u0642\u062D\u0000\u0642\u0644\u06D2\u0000\u0642\u0645\u0000\u0642\u0645\u062D\u0000\u0642"d~ -"\u0645\u0645\u0000\u0642\u0645\u064A\u0000\u0642\u0649\u0000\u0642\u064A\u0000\u0643\u0000\u0643\u0627\u0000\u0643"d~ -"\u062C\u0000\u0643\u062D\u0000\u0643\u062E\u0000\u0643\u0644\u0000\u0643\u0645\u0000\u0643\u0645\u0645\u0000\u0643"d~ -"\u0645\u064A\u0000\u0643\u0649\u0000\u0643\u064A\u0000\u0644\u0000\u0644\u0627\u0000\u0644\u0627\u0653\u0000\u0644"d~ -"\u0627\u0654\u0000\u0644\u0627\u0655\u0000\u0644\u062C\u0000\u0644\u062C\u062C\u0000\u0644\u062C\u0645\u0000\u0644"d~ -"\u062C\u064A\u0000\u0644\u062D\u0000\u0644\u062D\u0645\u0000\u0644\u062D\u0649\u0000\u0644\u062D\u064A\u0000\u0644"d~ -"\u062E\u0000\u0644\u062E\u0645\u0000\u0644\u0645\u0000\u0644\u0645\u062D\u0000\u0644\u0645\u064A\u0000\u0644\u0647"d~ -"\u0000\u0644\u0649\u0000\u0644\u064A\u0000\u0645\u0000\u0645\u0627\u0000\u0645\u062C\u0000\u0645\u062C\u062D\u0000"d~ -"\u0645\u062C\u062E\u0000\u0645\u062C\u0645\u0000\u0645\u062C\u064A\u0000\u0645\u062D\u0000\u0645\u062D\u062C\u0000"d~ -"\u0645\u062D\u0645\u0000\u0645\u062D\u0645\u062F\u0000\u0645\u062D\u064A\u0000\u0645\u062E\u0000\u0645\u062E\u062C"d~ -"\u0000\u0645\u062E\u0645\u0000\u0645\u062E\u064A\u0000\u0645\u0645\u0000\u0645\u0645\u064A\u0000\u0645\u0649\u0000"d~ -"\u0645\u064A\u0000\u0646\u0000\u0646\u062C\u0000\u0646\u062C\u062D\u0000\u0646\u062C\u0645\u0000\u0646\u062C\u0649"d~ -"\u0000\u0646\u062C\u064A\u0000\u0646\u062D\u0000\u0646\u062D\u0645\u0000\u0646\u062D\u0649\u0000\u0646\u062D\u064A"d~ -"\u0000\u0646\u062E\u0000\u0646\u0631\u0000\u0646\u0632\u0000\u0646\u0645\u0000\u0646\u0645\u0649\u0000\u0646\u0645"d~ -"\u064A\u0000\u0646\u0646\u0000\u0646\u0647\u0000\u0646\u0649\u0000\u0646\u064A\u0000\u0647\u0000\u0647\u062C\u0000"d~ -"\u0647\u0645\u0000\u0647\u0645\u062C\u0000\u0647\u0645\u0645\u0000\u0647\u0649\u0000\u0647\u064A\u0000\u0647\u0670"d~ -"\u0000\u0648\u0000\u0648\u0633\u0644\u0645\u0000\u0648\u0654\u0000\u0648\u0674\u0000\u0649\u0000\u0649\u0670\u0000"d~ -"\u064A\u0000\u064A\u062C\u0000\u064A\u062C\u064A\u0000\u064A\u062D\u0000\u064A\u062D\u064A\u0000\u064A\u062E\u0000"d~ -"\u064A\u0631\u0000\u064A\u0632\u0000\u064A\u0645\u0000\u064A\u0645\u0645\u0000\u064A\u0645\u064A\u0000\u064A\u0646"d~ -"\u0000\u064A\u0647\u0000\u064A\u0649\u0000\u064A\u064A\u0000\u064A\u0654\u0000\u064A\u0654\u0627\u0000\u064A\u0654"d~ -"\u062C\u0000\u064A\u0654\u062D\u0000\u064A\u0654\u062E\u0000\u064A\u0654\u0631\u0000\u064A\u0654\u0632\u0000\u064A"d~ -"\u0654\u0645\u0000\u064A\u0654\u0646\u0000\u064A\u0654\u0647\u0000\u064A\u0654\u0648\u0000\u064A\u0654\u0649\u0000"d~ -"\u064A\u0654\u064A\u0000\u064A\u0654\u06C6\u0000\u064A\u0654\u06C7\u0000\u064A\u0654\u06C8\u0000\u064A\u0654\u06D0"d~ -"\u0000\u064A\u0654\u06D5\u0000\u064A\u0674\u0000\u066E\u0000\u066F\u0000\u0671\u0000\u0679\u0000\u067A\u0000\u067B"d~ -"\u0000\u067E\u0000\u067F\u0000\u0680\u0000\u0683\u0000\u0684\u0000\u0686\u0000\u0687\u0000\u0688\u0000\u068C\u0000"d~ -"\u068D\u0000\u068E\u0000\u0691\u0000\u0698\u0000\u06A1\u0000\u06A4\u0000\u06A6\u0000\u06A9\u0000\u06AD\u0000\u06AF"d~ -"\u0000\u06B1\u0000\u06B3\u0000\u06BA\u0000\u06BB\u0000\u06BE\u0000\u06C1\u0000\u06C1\u0654\u0000\u06C5\u0000\u06C6"d~ -"\u0000\u06C7\u0000\u06C7\u0674\u0000\u06C8\u0000\u06C9\u0000\u06CB\u0000\u06CC\u0000\u06D0\u0000\u06D2\u0000\u06D2"d~ -"\u0654\u0000\u06D5\u0654\u0000\u0915\u093C\u0000\u0916\u093C\u0000\u0917\u093C\u0000\u091C\u093C\u0000\u0921\u093C"d~ -"\u0000\u0922\u093C\u0000\u0928\u093C\u0000\u092B\u093C\u0000\u092F\u093C\u0000\u0930\u093C\u0000\u0933\u093C\u0000"d~ -"\u09A1\u09BC\u0000\u09A2\u09BC\u0000\u09AF\u09BC\u0000\u09C7\u09BE\u0000\u09C7\u09D7\u0000\u0A16\u0A3C\u0000\u0A17"d~ -"\u0A3C\u0000\u0A1C\u0A3C\u0000\u0A2B\u0A3C\u0000\u0A32\u0A3C\u0000\u0A38\u0A3C\u0000\u0B21\u0B3C\u0000\u0B22\u0B3C"d~ -"\u0000\u0B47\u0B3E\u0000\u0B47\u0B56\u0000\u0B47\u0B57\u0000\u0B92\u0BD7\u0000\u0BC6\u0BBE\u0000\u0BC6\u0BD7\u0000"d~ -"\u0BC7\u0BBE\u0000\u0C46\u0C56\u0000\u0CBF\u0CD5\u0000\u0CC6\u0CC2\u0000\u0CC6\u0CC2\u0CD5\u0000\u0CC6\u0CD5\u0000"d~ -"\u0CC6\u0CD6\u0000\u0D46\u0D3E\u0000\u0D46\u0D57\u0000\u0D47\u0D3E\u0000\u0DD9\u0DCA\u0000\u0DD9\u0DCF\u0000\u0DD9"d~ -"\u0DCF\u0DCA\u0000\u0DD9\u0DDF\u0000\u0E4D\u0E32\u0000\u0EAB\u0E99\u0000\u0EAB\u0EA1\u0000\u0ECD\u0EB2\u0000\u0F0B"d~ -"\u0000\u0F40\u0FB5\u0000\u0F42\u0FB7\u0000\u0F4C\u0FB7\u0000\u0F51\u0FB7\u0000\u0F56\u0FB7\u0000\u0F5B\u0FB7\u0000"d~ -"\u0F71\u0F72\u0000\u0F71\u0F74\u0000\u0F71\u0F80\u0000\u0F90\u0FB5\u0000\u0F92\u0FB7\u0000\u0F9C\u0FB7\u0000\u0FA1"d~ -"\u0FB7\u0000\u0FA6\u0FB7\u0000\u0FAB\u0FB7\u0000\u0FB2\u0F71\u0F80\u0000\u0FB2\u0F80\u0000\u0FB3\u0F71\u0F80\u0000"d~ -"\u0FB3\u0F80\u0000\u1025\u102E\u0000\u10DC\u0000\u1100\u0000\u1100\u1161\u0000\u1101\u0000\u1102\u0000\u1102\u1161"d~ -"\u0000\u1103\u0000\u1103\u1161\u0000\u1104\u0000\u1105\u0000\u1105\u1161\u0000\u1106\u0000\u1106\u1161\u0000\u1107"d~ -"\u0000\u1107\u1161\u0000\u1108\u0000\u1109\u0000\u1109\u1161\u0000\u110A\u0000\u110B\u0000\u110B\u1161\u0000\u110B"d~ -"\u116E\u0000\u110C\u0000\u110C\u1161\u0000\u110C\u116E\u110B\u1174\u0000\u110D\u0000\u110E\u0000\u110E\u1161\u0000"d~ -"\u110E\u1161\u11B7\u1100\u1169\u0000\u110F\u0000\u110F\u1161\u0000\u1110\u0000\u1110\u1161\u0000\u1111\u0000\u1111"d~ -"\u1161\u0000\u1112\u0000\u1112\u1161\u0000\u1114\u0000\u1115\u0000\u111A\u0000\u111C\u0000\u111D\u0000\u111E\u0000"d~ -"\u1120\u0000\u1121\u0000\u1122\u0000\u1123\u0000\u1127\u0000\u1129\u0000\u112B\u0000\u112C\u0000\u112D\u0000\u112E"d~ -"\u0000\u112F\u0000\u1132\u0000\u1136\u0000\u1140\u0000\u1147\u0000\u114C\u0000\u1157\u0000\u1158\u0000\u1159\u0000"d~ -"\u1160\u0000\u1161\u0000\u1162\u0000\u1163\u0000\u1164\u0000\u1165\u0000\u1166\u0000\u1167\u0000\u1168\u0000\u1169"d~ -"\u0000\u116A\u0000\u116B\u0000\u116C\u0000\u116D\u0000\u116E\u0000\u116F\u0000\u1170\u0000\u1171\u0000\u1172\u0000"d~ -"\u1173\u0000\u1174\u0000\u1175\u0000\u1184\u0000\u1185\u0000\u1188\u0000\u1191\u0000\u1192\u0000\u1194\u0000\u119E"d~ -"\u0000\u11A1\u0000\u11AA\u0000\u11AC\u0000\u11AD\u0000\u11B0\u0000\u11B1\u0000\u11B2\u0000\u11B3\u0000\u11B4\u0000"d~ -"\u11B5\u0000\u11C7\u0000\u11C8\u0000\u11CC\u0000\u11CE\u0000\u11D3\u0000\u11D7\u0000\u11D9\u0000\u11DD\u0000\u11DF"d~ -"\u0000\u11F1\u0000\u11F2\u0000\u1B05\u1B35\u0000\u1B07\u1B35\u0000\u1B09\u1B35\u0000\u1B0B\u1B35\u0000\u1B0D\u1B35"d~ -"\u0000\u1B11\u1B35\u0000\u1B3A\u1B35\u0000\u1B3C\u1B35\u0000\u1B3E\u1B35\u0000\u1B3F\u1B35\u0000\u1B42\u1B35\u0000"d~ -"\u1D02\u0000\u1D16\u0000\u1D17\u0000\u1D1C\u0000\u1D1D\u0000\u1D25\u0000\u1D7B\u0000\u1D85\u0000\u1D91\u0000\u2010"d~ -"\u0000\u2013\u0000\u2014\u0000\u2032\u2032\u0000\u2032\u2032\u2032\u0000\u2032\u2032\u2032\u2032\u0000\u2035\u2035"d~ -"\u0000\u2035\u2035\u2035\u0000\u20A9\u0000\u2190\u0000\u2190\u0338\u0000\u2191\u0000\u2192\u0000\u2192\u0338\u0000"d~ -"\u2193\u0000\u2194\u0338\u0000\u21D0\u0338\u0000\u21D2\u0338\u0000\u21D4\u0338\u0000\u2202\u0000\u2203\u0338\u0000"d~ -"\u2207\u0000\u2208\u0338\u0000\u220B\u0338\u0000\u2211\u0000\u2212\u0000\u2223\u0338\u0000\u2225\u0338\u0000\u222B"d~ -"\u222B\u0000\u222B\u222B\u222B\u0000\u222B\u222B\u222B\u222B\u0000\u222E\u222E\u0000\u222E\u222E\u222E\u0000\u223C"d~ -"\u0338\u0000\u2243\u0338\u0000\u2245\u0338\u0000\u2248\u0338\u0000\u224D\u0338\u0000\u2261\u0338\u0000\u2264\u0338"d~ -"\u0000\u2265\u0338\u0000\u2272\u0338\u0000\u2273\u0338\u0000\u2276\u0338\u0000\u2277\u0338\u0000\u227A\u0338\u0000"d~ -"\u227B\u0338\u0000\u227C\u0338\u0000\u227D\u0338\u0000\u2282\u0338\u0000\u2283\u0338\u0000\u2286\u0338\u0000\u2287"d~ -"\u0338\u0000\u2291\u0338\u0000\u2292\u0338\u0000\u22A2\u0338\u0000\u22A8\u0338\u0000\u22A9\u0338\u0000\u22AB\u0338"d~ -"\u0000\u22B2\u0338\u0000\u22B3\u0338\u0000\u22B4\u0338\u0000\u22B5\u0338\u0000\u2502\u0000\u25A0\u0000\u25CB\u0000"d~ -"\u2985\u0000\u2986\u0000\u2ADD\u0338\u0000\u2C71\u0000\u2D61\u0000\u3001\u0000\u3002\u0000\u3008\u0000\u3009\u0000"d~ -"\u300A\u0000\u300B\u0000\u300C\u0000\u300D\u0000\u300E\u0000\u300F\u0000\u3010\u0000\u3011\u0000\u3012\u0000\u3014"d~ -"\u0000\u3014\u0053\u3015\u0000\u3014\u4E09\u3015\u0000\u3014\u4E8C\u3015\u0000\u3014\u52DD\u3015\u0000\u3014\u5B89"d~ -"\u3015\u0000\u3014\u6253\u3015\u0000\u3014\u6557\u3015\u0000\u3014\u672C\u3015\u0000\u3014\u70B9\u3015\u0000\u3014"d~ -"\u76D7\u3015\u0000\u3015\u0000\u3016\u0000\u3017\u0000\u3046\u3099\u0000\u304B\u3099\u0000\u304D\u3099\u0000\u304F"d~ -"\u3099\u0000\u3051\u3099\u0000\u3053\u3099\u0000\u3055\u3099\u0000\u3057\u3099\u0000\u3059\u3099\u0000\u305B\u3099"d~ -"\u0000\u305D\u3099\u0000\u305F\u3099\u0000\u3061\u3099\u0000\u3064\u3099\u0000\u3066\u3099\u0000\u3068\u3099\u0000"d~ -"\u306F\u3099\u0000\u306F\u309A\u0000\u3072\u3099\u0000\u3072\u309A\u0000\u3075\u3099\u0000\u3075\u309A\u0000\u3078"d~ -"\u3099\u0000\u3078\u309A\u0000\u307B\u304B\u0000\u307B\u3099\u0000\u307B\u309A\u0000\u3088\u308A\u0000\u3099\u0000"d~ -"\u309A\u0000\u309D\u3099\u0000\u30A1\u0000\u30A2\u0000\u30A2\u30CF\u309A\u30FC\u30C8\u0000\u30A2\u30EB\u30D5\u30A1"d~ -"\u0000\u30A2\u30F3\u30D8\u309A\u30A2\u0000\u30A2\u30FC\u30EB\u0000\u30A3\u0000\u30A4\u0000\u30A4\u30CB\u30F3\u30AF"d~ -"\u3099\u0000\u30A4\u30F3\u30C1\u0000\u30A5\u0000\u30A6\u0000\u30A6\u3099\u0000\u30A6\u30A9\u30F3\u0000\u30A7\u0000"d~ -"\u30A8\u0000\u30A8\u30B9\u30AF\u30FC\u30C8\u3099\u0000\u30A8\u30FC\u30AB\u30FC\u0000\u30A9\u0000\u30AA\u0000\u30AA"d~ -"\u30F3\u30B9\u0000\u30AA\u30FC\u30E0\u0000\u30AB\u0000\u30AB\u3099\u0000\u30AB\u3099\u30ED\u30F3\u0000\u30AB\u3099"d~ -"\u30F3\u30DE\u0000\u30AB\u30A4\u30EA\u0000\u30AB\u30E9\u30C3\u30C8\u0000\u30AB\u30ED\u30EA\u30FC\u0000\u30AD\u0000"d~ -"\u30AD\u3099\u0000\u30AD\u3099\u30AB\u3099\u0000\u30AD\u3099\u30CB\u30FC\u0000\u30AD\u3099\u30EB\u30BF\u3099\u30FC"d~ -"\u0000\u30AD\u30E5\u30EA\u30FC\u0000\u30AD\u30ED\u0000\u30AD\u30ED\u30AF\u3099\u30E9\u30E0\u0000\u30AD\u30ED\u30E1"d~ -"\u30FC\u30C8\u30EB\u0000\u30AD\u30ED\u30EF\u30C3\u30C8\u0000\u30AF\u0000\u30AF\u3099\u0000\u30AF\u3099\u30E9\u30E0"d~ -"\u0000\u30AF\u3099\u30E9\u30E0\u30C8\u30F3\u0000\u30AF\u30EB\u30BB\u3099\u30A4\u30ED\u0000\u30AF\u30ED\u30FC\u30CD"d~ -"\u0000\u30B1\u0000\u30B1\u3099\u0000\u30B1\u30FC\u30B9\u0000\u30B3\u0000\u30B3\u3099\u0000\u30B3\u30B3\u0000\u30B3"d~ -"\u30C8\u0000\u30B3\u30EB\u30CA\u0000\u30B3\u30FC\u30DB\u309A\u0000\u30B5\u0000\u30B5\u3099\u0000\u30B5\u30A4\u30AF"d~ -"\u30EB\u0000\u30B5\u30F3\u30C1\u30FC\u30E0\u0000\u30B7\u0000\u30B7\u3099\u0000\u30B7\u30EA\u30F3\u30AF\u3099\u0000"d~ -"\u30B9\u0000\u30B9\u3099\u0000\u30BB\u0000\u30BB\u3099\u0000\u30BB\u30F3\u30C1\u0000\u30BB\u30F3\u30C8\u0000\u30BD"d~ -"\u0000\u30BD\u3099\u0000\u30BF\u0000\u30BF\u3099\u0000\u30BF\u3099\u30FC\u30B9\u0000\u30C1\u0000\u30C1\u3099\u0000"d~ -"\u30C3\u0000\u30C4\u0000\u30C4\u3099\u0000\u30C6\u0000\u30C6\u3099\u0000\u30C6\u3099\u30B7\u0000\u30C8\u0000\u30C8"d~ -"\u3099\u0000\u30C8\u3099\u30EB\u0000\u30C8\u30F3\u0000\u30CA\u0000\u30CA\u30CE\u0000\u30CB\u0000\u30CC\u0000\u30CD"d~ -"\u0000\u30CE\u0000\u30CE\u30C3\u30C8\u0000\u30CF\u0000\u30CF\u3099\u0000\u30CF\u3099\u30FC\u30EC\u30EB\u0000\u30CF"d~ -"\u309A\u0000\u30CF\u309A\u30FC\u30BB\u30F3\u30C8\u0000\u30CF\u309A\u30FC\u30C4\u0000\u30CF\u30A4\u30C4\u0000\u30D2"d~ -"\u0000\u30D2\u3099\u0000\u30D2\u3099\u30EB\u0000\u30D2\u309A\u0000\u30D2\u309A\u30A2\u30B9\u30C8\u30EB\u0000\u30D2"d~ -"\u309A\u30AF\u30EB\u0000\u30D2\u309A\u30B3\u0000\u30D5\u0000\u30D5\u3099\u0000\u30D5\u3099\u30C3\u30B7\u30A7\u30EB"d~ -"\u0000\u30D5\u309A\u0000\u30D5\u30A1\u30E9\u30C3\u30C8\u3099\u0000\u30D5\u30A3\u30FC\u30C8\u0000\u30D5\u30E9\u30F3"d~ -"\u0000\u30D8\u0000\u30D8\u3099\u0000\u30D8\u3099\u30FC\u30BF\u0000\u30D8\u309A\u0000\u30D8\u309A\u30BD\u0000\u30D8"d~ -"\u309A\u30CB\u30D2\u0000\u30D8\u309A\u30F3\u30B9\u0000\u30D8\u309A\u30FC\u30B7\u3099\u0000\u30D8\u30AF\u30BF\u30FC"d~ -"\u30EB\u0000\u30D8\u30EB\u30C4\u0000\u30DB\u0000\u30DB\u3099\u0000\u30DB\u3099\u30EB\u30C8\u0000\u30DB\u309A\u0000"d~ -"\u30DB\u309A\u30A4\u30F3\u30C8\u0000\u30DB\u309A\u30F3\u30C8\u3099\u0000\u30DB\u30F3\u0000\u30DB\u30FC\u30EB\u0000"d~ -"\u30DB\u30FC\u30F3\u0000\u30DE\u0000\u30DE\u30A4\u30AF\u30ED\u0000\u30DE\u30A4\u30EB\u0000\u30DE\u30C3\u30CF\u0000"d~ -"\u30DE\u30EB\u30AF\u0000\u30DE\u30F3\u30B7\u30E7\u30F3\u0000\u30DF\u0000\u30DF\u30AF\u30ED\u30F3\u0000\u30DF\u30EA"d~ -"\u0000\u30DF\u30EA\u30CF\u3099\u30FC\u30EB\u0000\u30E0\u0000\u30E1\u0000\u30E1\u30AB\u3099\u0000\u30E1\u30AB\u3099"d~ -"\u30C8\u30F3\u0000\u30E1\u30FC\u30C8\u30EB\u0000\u30E2\u0000\u30E3\u0000\u30E4\u0000\u30E4\u30FC\u30C8\u3099\u0000"d~ -"\u30E4\u30FC\u30EB\u0000\u30E5\u0000\u30E6\u0000\u30E6\u30A2\u30F3\u0000\u30E7\u0000\u30E8\u0000\u30E9\u0000\u30EA"d~ -"\u0000\u30EA\u30C3\u30C8\u30EB\u0000\u30EA\u30E9\u0000\u30EB\u0000\u30EB\u30D2\u309A\u30FC\u0000\u30EB\u30FC\u30D5"d~ -"\u3099\u30EB\u0000\u30EC\u0000\u30EC\u30E0\u0000\u30EC\u30F3\u30C8\u30B1\u3099\u30F3\u0000\u30ED\u0000\u30EF\u0000"d~ -"\u30EF\u3099\u0000\u30EF\u30C3\u30C8\u0000\u30F0\u0000\u30F0\u3099\u0000\u30F1\u0000\u30F1\u3099\u0000\u30F2\u0000"d~ -"\u30F2\u3099\u0000\u30F3\u0000\u30FB\u0000\u30FC\u0000\u30FD\u3099\u0000\u349E\u0000\u34B9\u0000\u34BB\u0000\u34DF"d~ -"\u0000\u3515\u0000\u36EE\u0000\u36FC\u0000\u3781\u0000\u382F\u0000\u3862\u0000\u387C\u0000\u38C7\u0000\u38E3\u0000"d~ -"\u391C\u0000\u393A\u0000\u3A2E\u0000\u3A6C\u0000\u3AE4\u0000\u3B08\u0000\u3B19\u0000\u3B49\u0000\u3B9D\u0000\u3C18"d~ -"\u0000\u3C4E\u0000\u3D33\u0000\u3D96\u0000\u3EAC\u0000\u3EB8\u0000\u3F1B\u0000\u3FFC\u0000\u4008\u0000\u4018\u0000"d~ -"\u4039\u0000\u4046\u0000\u4096\u0000\u40E3\u0000\u412F\u0000\u4202\u0000\u4227\u0000\u42A0\u0000\u4301\u0000\u4334"d~ -"\u0000\u4359\u0000\u43D5\u0000\u43D9\u0000\u440B\u0000\u446B\u0000\u452B\u0000\u455D\u0000\u4561\u0000\u456B\u0000"d~ -"\u45D7\u0000\u45F9\u0000\u4635\u0000\u46BE\u0000\u46C7\u0000\u4995\u0000\u49E6\u0000\u4A6E\u0000\u4A76\u0000\u4AB2"d~ -"\u0000\u4B33\u0000\u4BCE\u0000\u4CCE\u0000\u4CED\u0000\u4CF8\u0000\u4D56\u0000\u4E00\u0000\u4E01\u0000\u4E03\u0000"d~ -"\u4E09\u0000\u4E0A\u0000\u4E0B\u0000\u4E0D\u0000\u4E19\u0000\u4E26\u0000\u4E28\u0000\u4E2D\u0000\u4E32\u0000\u4E36"d~ -"\u0000\u4E38\u0000\u4E39\u0000\u4E3D\u0000\u4E3F\u0000\u4E41\u0000\u4E59\u0000\u4E5D\u0000\u4E82\u0000\u4E85\u0000"d~ -"\u4E86\u0000\u4E8C\u0000\u4E94\u0000\u4EA0\u0000\u4EA4\u0000\u4EAE\u0000\u4EBA\u0000\u4EC0\u0000\u4ECC\u0000\u4EE4"d~ -"\u0000\u4EE4\u548C\u0000\u4F01\u0000\u4F11\u0000\u4F60\u0000\u4F80\u0000\u4F86\u0000\u4F8B\u0000\u4FAE\u0000\u4FBB"d~ -"\u0000\u4FBF\u0000\u5002\u0000\u502B\u0000\u507A\u0000\u5099\u0000\u50CF\u0000\u50DA\u0000\u50E7\u0000\u512A\u0000"d~ -"\u513F\u0000\u5140\u0000\u5145\u0000\u514D\u0000\u5154\u0000\u5164\u0000\u5165\u0000\u5167\u0000\u5168\u0000\u5169"d~ -"\u0000\u516B\u0000\u516D\u0000\u5177\u0000\u5180\u0000\u5182\u0000\u518D\u0000\u5192\u0000\u5195\u0000\u5196\u0000"d~ -"\u5197\u0000\u5199\u0000\u51A4\u0000\u51AB\u0000\u51AC\u0000\u51B5\u0000\u51B7\u0000\u51C9\u0000\u51CC\u0000\u51DC"d~ -"\u0000\u51DE\u0000\u51E0\u0000\u51F5\u0000\u5200\u0000\u5203\u0000\u5207\u0000\u5217\u0000\u521D\u0000\u5229\u0000"d~ -"\u523A\u0000\u523B\u0000\u5246\u0000\u524D\u0000\u5272\u0000\u5277\u0000\u5289\u0000\u529B\u0000\u52A3\u0000\u52B3"d~ -"\u0000\u52B4\u0000\u52C7\u0000\u52C9\u0000\u52D2\u0000\u52DE\u0000\u52E4\u0000\u52F5\u0000\u52F9\u0000\u52FA\u0000"d~ -"\u5305\u0000\u5306\u0000\u5315\u0000\u5317\u0000\u531A\u0000\u5338\u0000\u533B\u0000\u533F\u0000\u5341\u0000\u5344"d~ -"\u0000\u5345\u0000\u5349\u0000\u5351\u0000\u5354\u0000\u535A\u0000\u535C\u0000\u5369\u0000\u5370\u0000\u5373\u0000"d~ -"\u5375\u0000\u537D\u0000\u537F\u0000\u5382\u0000\u53B6\u0000\u53C3\u0000\u53C8\u0000\u53CA\u0000\u53CC\u0000\u53DF"d~ -"\u0000\u53E3\u0000\u53E5\u0000\u53EB\u0000\u53EF\u0000\u53F1\u0000\u53F3\u0000\u5406\u0000\u5408\u0000\u540D\u0000"d~ -"\u540F\u0000\u541D\u0000\u5438\u0000\u5439\u0000\u5442\u0000\u5448\u0000\u5468\u0000\u549E\u0000\u54A2\u0000\u54BD"d~ -"\u0000\u54F6\u0000\u5510\u0000\u554F\u0000\u5553\u0000\u5555\u0000\u5563\u0000\u5584\u0000\u5587\u0000\u5599\u0000"d~ -"\u559D\u0000\u55AB\u0000\u55B3\u0000\u55B6\u0000\u55C0\u0000\u55C2\u0000\u55E2\u0000\u5606\u0000\u5651\u0000\u5668"d~ -"\u0000\u5674\u0000\u56D7\u0000\u56DB\u0000\u56F9\u0000\u5716\u0000\u5717\u0000\u571F\u0000\u5730\u0000\u578B\u0000"d~ -"\u57CE\u0000\u57F4\u0000\u580D\u0000\u5831\u0000\u5832\u0000\u5840\u0000\u585A\u0000\u585E\u0000\u58A8\u0000\u58AC"d~ -"\u0000\u58B3\u0000\u58D8\u0000\u58DF\u0000\u58EB\u0000\u58EE\u0000\u58F0\u0000\u58F2\u0000\u58F7\u0000\u5902\u0000"d~ -"\u5906\u0000\u590A\u0000\u5915\u0000\u591A\u0000\u591C\u0000\u5922\u0000\u5927\u0000\u5927\u6B63\u0000\u5929\u0000"d~ -"\u5944\u0000\u5948\u0000\u5951\u0000\u5954\u0000\u5962\u0000\u5973\u0000\u59D8\u0000\u59EC\u0000\u5A1B\u0000\u5A27"d~ -"\u0000\u5A62\u0000\u5A66\u0000\u5AB5\u0000\u5B08\u0000\u5B28\u0000\u5B3E\u0000\u5B50\u0000\u5B57\u0000\u5B66\u0000"d~ -"\u5B80\u0000\u5B85\u0000\u5B97\u0000\u5BC3\u0000\u5BD8\u0000\u5BE7\u0000\u5BEE\u0000\u5BF3\u0000\u5BF8\u0000\u5BFF"d~ -"\u0000\u5C06\u0000\u5C0F\u0000\u5C22\u0000\u5C38\u0000\u5C3F\u0000\u5C60\u0000\u5C62\u0000\u5C64\u0000\u5C65\u0000"d~ -"\u5C6E\u0000\u5C71\u0000\u5C8D\u0000\u5CC0\u0000\u5D19\u0000\u5D43\u0000\u5D50\u0000\u5D6B\u0000\u5D6E\u0000\u5D7C"d~ -"\u0000\u5DB2\u0000\u5DBA\u0000\u5DDB\u0000\u5DE1\u0000\u5DE2\u0000\u5DE5\u0000\u5DE6\u0000\u5DF1\u0000\u5DFD\u0000"d~ -"\u5DFE\u0000\u5E28\u0000\u5E3D\u0000\u5E69\u0000\u5E72\u0000\u5E73\u6210\u0000\u5E74\u0000\u5E7A\u0000\u5E7C\u0000"d~ -"\u5E7F\u0000\u5EA6\u0000\u5EB0\u0000\u5EB3\u0000\u5EB6\u0000\u5EC9\u0000\u5ECA\u0000\u5ED2\u0000\u5ED3\u0000\u5ED9"d~ -"\u0000\u5EEC\u0000\u5EF4\u0000\u5EFE\u0000\u5F04\u0000\u5F0B\u0000\u5F13\u0000\u5F22\u0000\u5F50\u0000\u5F53\u0000"d~ -"\u5F61\u0000\u5F62\u0000\u5F69\u0000\u5F6B\u0000\u5F73\u0000\u5F8B\u0000\u5F8C\u0000\u5F97\u0000\u5F9A\u0000\u5FA9"d~ -"\u0000\u5FAD\u0000\u5FC3\u0000\u5FCD\u0000\u5FD7\u0000\u5FF5\u0000\u5FF9\u0000\u6012\u0000\u601C\u0000\u6075\u0000"d~ -"\u6081\u0000\u6094\u0000\u60C7\u0000\u60D8\u0000\u60E1\u0000\u6108\u0000\u6144\u0000\u6148\u0000\u614C\u0000\u614E"d~ -"\u0000\u6160\u0000\u6168\u0000\u617A\u0000\u618E\u0000\u6190\u0000\u61A4\u0000\u61AF\u0000\u61B2\u0000\u61DE\u0000"d~ -"\u61F2\u0000\u61F6\u0000\u6200\u0000\u6208\u0000\u6210\u0000\u621B\u0000\u622E\u0000\u6234\u0000\u6236\u0000\u624B"d~ -"\u0000\u6253\u0000\u625D\u0000\u6295\u0000\u62B1\u0000\u62C9\u0000\u62CF\u0000\u62D3\u0000\u62D4\u0000\u62FC\u0000"d~ -"\u62FE\u0000\u6307\u0000\u633D\u0000\u6350\u0000\u6355\u0000\u6368\u0000\u637B\u0000\u6383\u0000\u63A0\u0000\u63A9"d~ -"\u0000\u63C4\u0000\u63C5\u0000\u63E4\u0000\u641C\u0000\u6422\u0000\u6452\u0000\u6469\u0000\u6477\u0000\u647E\u0000"d~ -"\u649A\u0000\u649D\u0000\u64C4\u0000\u652F\u0000\u6534\u0000\u654F\u0000\u6556\u0000\u656C\u0000\u6578\u0000\u6587"d~ -"\u0000\u6597\u0000\u6599\u0000\u65A4\u0000\u65B0\u0000\u65B9\u0000\u65C5\u0000\u65E0\u0000\u65E2\u0000\u65E3\u0000"d~ -"\u65E5\u0000\u660E\u6CBB\u0000\u6613\u0000\u6620\u0000\u662D\u548C\u0000\u6649\u0000\u6674\u0000\u6688\u0000\u6691"d~ -"\u0000\u669C\u0000\u66B4\u0000\u66C6\u0000\u66F0\u0000\u66F4\u0000\u66F8\u0000\u6700\u0000\u6708\u0000\u6709\u0000"d~ -"\u6717\u0000\u671B\u0000\u6721\u0000\u6728\u0000\u674E\u0000\u6753\u0000\u6756\u0000\u675E\u0000\u677B\u0000\u6785"d~ -"\u0000\u6797\u0000\u67F3\u0000\u67FA\u0000\u6817\u0000\u681F\u0000\u682A\u0000\u682A\u5F0F\u4F1A\u793E\u0000\u6852"d~ -"\u0000\u6881\u0000\u6885\u0000\u688E\u0000\u68A8\u0000\u6914\u0000\u6942\u0000\u69A3\u0000\u69EA\u0000\u6A02\u0000"d~ -"\u6A13\u0000\u6AA8\u0000\u6AD3\u0000\u6ADB\u0000\u6B04\u0000\u6B20\u0000\u6B21\u0000\u6B54\u0000\u6B62\u0000\u6B63"d~ -"\u0000\u6B72\u0000\u6B77\u0000\u6B79\u0000\u6B9F\u0000\u6BAE\u0000\u6BB3\u0000\u6BBA\u0000\u6BBB\u0000\u6BCB\u0000"d~ -"\u6BCD\u0000\u6BD4\u0000\u6BDB\u0000\u6C0F\u0000\u6C14\u0000\u6C34\u0000\u6C4E\u0000\u6C67\u0000\u6C88\u0000\u6CBF"d~ -"\u0000\u6CCC\u0000\u6CCD\u0000\u6CE5\u0000\u6CE8\u0000\u6D16\u0000\u6D1B\u0000\u6D1E\u0000\u6D34\u0000\u6D3E\u0000"d~ -"\u6D41\u0000\u6D69\u0000\u6D6A\u0000\u6D77\u0000\u6D78\u0000\u6D85\u0000\u6DCB\u0000\u6DDA\u0000\u6DEA\u0000\u6DF9"d~ -"\u0000\u6E1A\u0000\u6E2F\u0000\u6E6E\u0000\u6E80\u0000\u6E9C\u0000\u6EBA\u0000\u6EC7\u0000\u6ECB\u0000\u6ED1\u0000"d~ -"\u6EDB\u0000\u6F0F\u0000\u6F14\u0000\u6F22\u0000\u6F23\u0000\u6F6E\u0000\u6FC6\u0000\u6FEB\u0000\u6FFE\u0000\u701B"d~ -"\u0000\u701E\u0000\u7039\u0000\u704A\u0000\u706B\u0000\u7070\u0000\u7077\u0000\u707D\u0000\u7099\u0000\u70AD\u0000"d~ -"\u70C8\u0000\u70D9\u0000\u7121\u0000\u7145\u0000\u7149\u0000\u716E\u0000\u719C\u0000\u71CE\u0000\u71D0\u0000\u7210"d~ -"\u0000\u721B\u0000\u7228\u0000\u722A\u0000\u722B\u0000\u7235\u0000\u7236\u0000\u723B\u0000\u723F\u0000\u7247\u0000"d~ -"\u7250\u0000\u7259\u0000\u725B\u0000\u7262\u0000\u7279\u0000\u7280\u0000\u7295\u0000\u72AC\u0000\u72AF\u0000\u72C0"d~ -"\u0000\u72FC\u0000\u732A\u0000\u7375\u0000\u737A\u0000\u7384\u0000\u7387\u0000\u7389\u0000\u738B\u0000\u73A5\u0000"d~ -"\u73B2\u0000\u73DE\u0000\u7406\u0000\u7409\u0000\u7422\u0000\u7447\u0000\u745C\u0000\u7469\u0000\u7471\u0000\u7485"d~ -"\u0000\u7489\u0000\u7498\u0000\u74CA\u0000\u74DC\u0000\u74E6\u0000\u7506\u0000\u7518\u0000\u751F\u0000\u7524\u0000"d~ -"\u7528\u0000\u7530\u0000\u7532\u0000\u7533\u0000\u7537\u0000\u753B\u0000\u753E\u0000\u7559\u0000\u7565\u0000\u7570"d~ -"\u0000\u758B\u0000\u7592\u0000\u75E2\u0000\u7610\u0000\u761D\u0000\u761F\u0000\u7642\u0000\u7669\u0000\u7676\u0000"d~ -"\u767D\u0000\u76AE\u0000\u76BF\u0000\u76CA\u0000\u76DB\u0000\u76E3\u0000\u76E7\u0000\u76EE\u0000\u76F4\u0000\u7701"d~ -"\u0000\u771E\u0000\u771F\u0000\u7740\u0000\u774A\u0000\u778B\u0000\u77A7\u0000\u77DB\u0000\u77E2\u0000\u77F3\u0000"d~ -"\u784E\u0000\u786B\u0000\u788C\u0000\u7891\u0000\u78CA\u0000\u78CC\u0000\u78FB\u0000\u792A\u0000\u793A\u0000\u793C"d~ -"\u0000\u793E\u0000\u7948\u0000\u7949\u0000\u7950\u0000\u7956\u0000\u795D\u0000\u795E\u0000\u7965\u0000\u797F\u0000"d~ -"\u7981\u0000\u798D\u0000\u798E\u0000\u798F\u0000\u79AE\u0000\u79B8\u0000\u79BE\u0000\u79CA\u0000\u79D8\u0000\u79EB"d~ -"\u0000\u7A1C\u0000\u7A40\u0000\u7A4A\u0000\u7A4F\u0000\u7A74\u0000\u7A7A\u0000\u7A81\u0000\u7AB1\u0000\u7ACB\u0000"d~ -"\u7AEE\u0000\u7AF9\u0000\u7B20\u0000\u7B8F\u0000\u7BC0\u0000\u7BC6\u0000\u7BC9\u0000\u7C3E\u0000\u7C60\u0000\u7C73"d~ -"\u0000\u7C7B\u0000\u7C92\u0000\u7CBE\u0000\u7CD2\u0000\u7CD6\u0000\u7CE3\u0000\u7CE7\u0000\u7CE8\u0000\u7CF8\u0000"d~ -"\u7D00\u0000\u7D10\u0000\u7D22\u0000\u7D2F\u0000\u7D42\u0000\u7D5B\u0000\u7D63\u0000\u7DA0\u0000\u7DBE\u0000\u7DC7"d~ -"\u0000\u7DF4\u0000\u7E02\u0000\u7E09\u0000\u7E37\u0000\u7E41\u0000\u7E45\u0000\u7F36\u0000\u7F3E\u0000\u7F51\u0000"d~ -"\u7F72\u0000\u7F79\u0000\u7F7A\u0000\u7F85\u0000\u7F8A\u0000\u7F95\u0000\u7F9A\u0000\u7FBD\u0000\u7FFA\u0000\u8001"d~ -"\u0000\u8005\u0000\u800C\u0000\u8012\u0000\u8033\u0000\u8046\u0000\u8060\u0000\u806F\u0000\u8070\u0000\u807E\u0000"d~ -"\u807F\u0000\u8089\u0000\u808B\u0000\u80AD\u0000\u80B2\u0000\u8103\u0000\u813E\u0000\u81D8\u0000\u81E3\u0000\u81E8"d~ -"\u0000\u81EA\u0000\u81ED\u0000\u81F3\u0000\u81FC\u0000\u8201\u0000\u8204\u0000\u820C\u0000\u8218\u0000\u821B\u0000"d~ -"\u821F\u0000\u826E\u0000\u826F\u0000\u8272\u0000\u8278\u0000\u8279\u0000\u828B\u0000\u8291\u0000\u829D\u0000\u82B1"d~ -"\u0000\u82B3\u0000\u82BD\u0000\u82E5\u0000\u82E6\u0000\u831D\u0000\u8323\u0000\u8336\u0000\u8352\u0000\u8353\u0000"d~ -"\u8363\u0000\u83AD\u0000\u83BD\u0000\u83C9\u0000\u83CA\u0000\u83CC\u0000\u83DC\u0000\u83E7\u0000\u83EF\u0000\u83F1"d~ -"\u0000\u843D\u0000\u8449\u0000\u8457\u0000\u84EE\u0000\u84F1\u0000\u84F3\u0000\u84FC\u0000\u8516\u0000\u8564\u0000"d~ -"\u85CD\u0000\u85FA\u0000\u8606\u0000\u8612\u0000\u862D\u0000\u863F\u0000\u864D\u0000\u8650\u0000\u865C\u0000\u8667"d~ -"\u0000\u8669\u0000\u866B\u0000\u8688\u0000\u86A9\u0000\u86E2\u0000\u870E\u0000\u8728\u0000\u876B\u0000\u8779\u0000"d~ -"\u8786\u0000\u87BA\u0000\u87E1\u0000\u8801\u0000\u881F\u0000\u8840\u0000\u884C\u0000\u8860\u0000\u8863\u0000\u88C2"d~ -"\u0000\u88CF\u0000\u88D7\u0000\u88DE\u0000\u88E1\u0000\u88F8\u0000\u88FA\u0000\u8910\u0000\u8941\u0000\u8964\u0000"d~ -"\u897E\u0000\u8986\u0000\u898B\u0000\u8996\u0000\u89D2\u0000\u89E3\u0000\u8A00\u0000\u8AA0\u0000\u8AAA\u0000\u8ABF"d~ -"\u0000\u8ACB\u0000\u8AD2\u0000\u8AD6\u0000\u8AED\u0000\u8AF8\u0000\u8AFE\u0000\u8B01\u0000\u8B39\u0000\u8B58\u0000"d~ -"\u8B80\u0000\u8B8A\u0000\u8C37\u0000\u8C46\u0000\u8C48\u0000\u8C55\u0000\u8C78\u0000\u8C9D\u0000\u8CA1\u0000\u8CA9"d~ -"\u0000\u8CAB\u0000\u8CC1\u0000\u8CC2\u0000\u8CC7\u0000\u8CC8\u0000\u8CD3\u0000\u8D08\u0000\u8D1B\u0000\u8D64\u0000"d~ -"\u8D70\u0000\u8D77\u0000\u8DB3\u0000\u8DBC\u0000\u8DCB\u0000\u8DEF\u0000\u8DF0\u0000\u8EAB\u0000\u8ECA\u0000\u8ED4"d~ -"\u0000\u8F26\u0000\u8F2A\u0000\u8F38\u0000\u8F3B\u0000\u8F62\u0000\u8F9B\u0000\u8F9E\u0000\u8FB0\u0000\u8FB5\u0000"d~ -"\u8FB6\u0000\u9023\u0000\u9038\u0000\u904A\u0000\u9069\u0000\u9072\u0000\u907C\u0000\u908F\u0000\u9091\u0000\u9094"d~ -"\u0000\u90CE\u0000\u90DE\u0000\u90F1\u0000\u90FD\u0000\u9111\u0000\u911B\u0000\u9149\u0000\u914D\u0000\u916A\u0000"d~ -"\u9199\u0000\u91B4\u0000\u91C6\u0000\u91CC\u0000\u91CF\u0000\u91D1\u0000\u9234\u0000\u9238\u0000\u9276\u0000\u927C"d~ -"\u0000\u92D7\u0000\u92D8\u0000\u9304\u0000\u934A\u0000\u93F9\u0000\u9415\u0000\u9577\u0000\u9580\u0000\u958B\u0000"d~ -"\u95AD\u0000\u95B7\u0000\u961C\u0000\u962E\u0000\u964B\u0000\u964D\u0000\u9675\u0000\u9678\u0000\u967C\u0000\u9686"d~ -"\u0000\u96A3\u0000\u96B6\u0000\u96B7\u0000\u96B8\u0000\u96B9\u0000\u96C3\u0000\u96E2\u0000\u96E3\u0000\u96E8\u0000"d~ -"\u96F6\u0000\u96F7\u0000\u9723\u0000\u9732\u0000\u9748\u0000\u9751\u0000\u9756\u0000\u975E\u0000\u9762\u0000\u9769"d~ -"\u0000\u97CB\u0000\u97DB\u0000\u97E0\u0000\u97ED\u0000\u97F3\u0000\u97FF\u0000\u9801\u0000\u9805\u0000\u980B\u0000"d~ -"\u9818\u0000\u9829\u0000\u983B\u0000\u985E\u0000\u98A8\u0000\u98DB\u0000\u98DF\u0000\u98E2\u0000\u98EF\u0000\u98FC"d~ -"\u0000\u9928\u0000\u9929\u0000\u9996\u0000\u9999\u0000\u99A7\u0000\u99AC\u0000\u99C2\u0000\u99F1\u0000\u99FE\u0000"d~ -"\u9A6A\u0000\u9AA8\u0000\u9AD8\u0000\u9ADF\u0000\u9B12\u0000\u9B25\u0000\u9B2F\u0000\u9B32\u0000\u9B3C\u0000\u9B5A"d~ -"\u0000\u9B6F\u0000\u9C40\u0000\u9C57\u0000\u9CE5\u0000\u9CFD\u0000\u9D67\u0000\u9DB4\u0000\u9DFA\u0000\u9E1E\u0000"d~ -"\u9E75\u0000\u9E7F\u0000\u9E97\u0000\u9E9F\u0000\u9EA5\u0000\u9EBB\u0000\u9EC3\u0000\u9ECD\u0000\u9ECE\u0000\u9ED1"d~ -"\u0000\u9EF9\u0000\u9EFD\u0000\u9EFE\u0000\u9F05\u0000\u9F0E\u0000\u9F0F\u0000\u9F13\u0000\u9F16\u0000\u9F20\u0000"d~ -"\u9F3B\u0000\u9F43\u0000\u9F4A\u0000\u9F52\u0000\u9F8D\u0000\u9F8E\u0000\u9F9C\u0000\u9F9F\u0000\u9FA0\u0000\uA651"d~ -"\u0000\uA689\u0000\uA727\u0000\uA76F\u0000\uA78E\u0000\uAB37\u0000\uAB52\u0000\uAB66\u0000\uAB67\u0000\U00011099"d~ -"\U000110BA\u0000\U0001109B\U000110BA\u0000\U000110A5\U000110BA\u0000\U00011131\U00011127\u0000\U00011132\U00011127"d~ -"\u0000\U00011347\U0001133E\u0000\U00011347\U00011357\u0000\U000114B9\U000114B0\u0000\U000114B9\U000114BA\u0000"d~ -"\U000114B9\U000114BD\u0000\U000115B8\U000115AF\u0000\U000115B9\U000115AF\u0000\U00011935\U00011930\u0000\U0001D157"d~ -"\U0001D165\u0000\U0001D158\U0001D165\u0000\U0001D158\U0001D165\U0001D16E\u0000\U0001D158\U0001D165\U0001D16F\u0000"d~ -"\U0001D158\U0001D165\U0001D170\u0000\U0001D158\U0001D165\U0001D171\u0000\U0001D158\U0001D165\U0001D172\u0000\U0001D1B9"d~ -"\U0001D165\u0000\U0001D1B9\U0001D165\U0001D16E\u0000\U0001D1B9\U0001D165\U0001D16F\u0000\U0001D1BA\U0001D165\u0000"d~ -"\U0001D1BA\U0001D165\U0001D16E\u0000\U0001D1BA\U0001D165\U0001D16F\u0000\U0001DF04\u0000\U0001DF05\u0000\U0001DF06"d~ -"\u0000\U0001DF08\u0000\U0001DF0A\u0000\U0001DF1E\u0000\U00020122\u0000\U0002051C\u0000\U00020525\u0000\U0002054B"d~ -"\u0000\U0002063A\u0000\U00020804\u0000\U000208DE\u0000\U00020A2C\u0000\U00020B63\u0000\U000214E4\u0000\U000216A8"d~ -"\u0000\U000216EA\u0000\U000219C8\u0000\U00021B18\u0000\U00021D0B\u0000\U00021DE4\u0000\U00021DE6\u0000\U00022183"d~ -"\u0000\U0002219F\u0000\U00022331\u0000\U000226D4\u0000\U00022844\u0000\U0002284A\u0000\U00022B0C\u0000\U00022BF1"d~ -"\u0000\U0002300A\u0000\U000232B8\u0000\U0002335F\u0000\U00023393\u0000\U0002339C\u0000\U000233C3\u0000\U000233D5"d~ -"\u0000\U0002346D\u0000\U000236A3\u0000\U000238A7\u0000\U00023A8D\u0000\U00023AFA\u0000\U00023CBC\u0000\U00023D1E"d~ -"\u0000\U00023ED1\u0000\U00023F5E\u0000\U00023F8E\u0000\U00024263\u0000\U000242EE\u0000\U000243AB\u0000\U00024608"d~ -"\u0000\U00024735\u0000\U00024814\u0000\U00024C36\u0000\U00024C92\u0000\U00024FA1\u0000\U00024FB8\u0000\U00025044"d~ -"\u0000\U000250F2\u0000\U000250F3\u0000\U00025119\u0000\U00025133\u0000\U00025249\u0000\U0002541D\u0000\U00025626"d~ -"\u0000\U0002569A\u0000\U000256C5\u0000\U0002597C\u0000\U00025AA7\u0000\U00025BAB\u0000\U00025C80\u0000\U00025CD0"d~ -"\u0000\U00025F86\u0000\U000261DA\u0000\U00026228\u0000\U00026247\u0000\U000262D9\u0000\U0002633E\u0000\U000264DA"d~ -"\u0000\U00026523\u0000\U000265A8\u0000\U000267A7\u0000\U000267B5\u0000\U00026B3C\u0000\U00026C36\u0000\U00026CD5"d~ -"\u0000\U00026D6B\u0000\U00026F2C\u0000\U00026FB1\u0000\U000270D2\u0000\U000273CA\u0000\U00027667\u0000\U000278AE"d~ -"\u0000\U00027966\u0000\U00027CA8\u0000\U00027ED3\u0000\U00027F2F\u0000\U000285D2\u0000\U000285ED\u0000\U0002872E"d~ -"\u0000\U00028BFA\u0000\U00028D77\u0000\U00029145\u0000\U000291DF\u0000\U0002921A\u0000\U0002940A\u0000\U00029496"d~ -"\u0000\U000295B6\u0000\U00029B30\u0000\U0002A0CE\u0000\U0002A105\u0000\U0002A20E\u0000\U0002A291\u0000\U0002A392"d~ -"\u0000\U0002A600\u0000"d; -return t[]; -} - -} - - -static if (size_t.sizeof == 8) -{ -//23488 bytes -enum compatMappingTrieEntries = TrieEntry!(ushort, 8, 8, 5)(x" -0000000000000000000000000000002000000000000002A0", -x" -00000000000001000000000000000A000000000000002360", -x" -040203020202010007060202020202050802020202020202000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -00000000000000000000000000000000000000000000000000030002000100000007000600050004000A000900080000 -00000000000C000B000F000E000D0000001100100000000000150014001300120019001800170016001B001A00000000 -0000000000000000000000000000001C001E0000001D00000000001F0000000000000000000000000000000000000000 -000000000000000000000000000000000000002100200000000000220000000000000024002300000000000000000000 -00000025000000000000002700000026000000280000000000000029000000000000002A000000000000002B00000000 -00000000002C00000000002E002D0000003100300000002F000000000033003200000000003400000035000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000003800370036000000000000000000000000000000000000000000000000 -003B003A0039000000000000003D003C00410040003F003E00450044004300420049004800470046004D004C004B004A -00510050004F004E000000000053005200570056005500540000005A00590058005E005D005C005B006100000060005F -000000000062000000000000000000000063000000000000006700660065006400000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000690000000000680000006A0000000000000000000000000000000000000000 -006B0000000000000000000000000000006C00000000000000000000000000000000000000000000006E00000000006D -007200710070006F00000075007400730079007800770076007D007C007B007A0080007F007E00000000000000000081 -00850084008300820089008800870086008D008C008B008A00910090008F008E00000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000009200930000000000000094000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -009600950000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -00000000000000000000000000000000009A009900980097009E009D009C009B00A200A100A0009F000000A500A400A3 -00A900A800A700A600AD00AC00AB00AA00B100B000AF00AE00B500B400B300B200B900B800B700B600BD00BC00BB00BA -00C100C000BF00BE00C500C400C300C200C900C800C700C600CD00CC00CB00CA00000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -00000000000000000000000000CF00CE0000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -00000000000000000000000000D100D00000000000D20000000000000000000000000000000000000000000000000000 -000000D300000000000000000000000000000000000000000000000000D4000000000000000000000000000000D50000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -0000000000D6000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -00D800D700000000000000DA00D900000000000000000000000000000000000000000000000000000000000000000000 -00DE00DD00DC00DB00E200E100E000DF00E600E500E400E300E800DC00DB00E700EB00EA00E900DE00EE00ED00EC00E2 -00F200F100F000EF00F600F500F400F30000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -00F900F800F7000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000FD00FC00FB00FA0000000000FF00FE -000000000000000000000000000000000000000000000000000000000000000001030102010101000000000000000104 -000001070106010500000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000001080000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000010C010B010A01090110010F010E010D -011401130112011101180117011601150000000000000119000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000001000000000000000000000692000000150009000000000000 -030F03430000000000000000120F00030000078A031400480000049403C603CE058605730570056D05F8000005B005A6 -06580631062E062B06F906EA06E706E407AC07A907920000000007E607C207AF08BA08B708B4000000000000096208CE -09C509B209AF09AC0A4A000009EF09E50AB60A8F0A8C0A890B580B4C0B490B460C610C5E0C44000000000C9B0C770C64 -0D710D6E0D6B00000E1E00000E0F0D8509CB058C09C805890A3E05EC0A0D05CE0A4405F20A4105EF0A71061A0A4705F5 -0AA50647000000000AB306550AB006520ABC065E0AD306750B0406A00AFE069A0B0D06A90B0706A3000000000B1906BA -0B5206F00B4F06ED0B6E070F0B5506F30B3A06D8000006F60BB1072E0B76071707500BCF0743000007400BD207460BDC -078F000000000BCC07980C50079E0C41000000000F300C4A0C9107DC0C8607D1000000000CA507F00D20085B0D0A0845 -0D2E086F0D10084B0D4C088D0D3508760D6008A90D3B087C000000000D5708A00D7B08C40D7408BD0D9B08E40D8208CB -0DC709100D9E08E70E1209650DEC094209820E2C097C097108430614060D0E32000000000CB107FC0000000000000000 -000000000000000008F30000000000000000000000000DAA000000000000000000000000000000000000000000000000 -000000000000000007360A6A0613060C078607830BBC073D070309F605B70C3508EA0CA807F30B6208D50D9008D90DA1 -08D10D9408DD0D8C09E805A900000D880E680E5D09E105A20B0A06A6000000000CCF081A0BAB07280F070E820CD2081D -0A67061006090B79000000000AFB069709F205B30C3E078C0E6F0E600E650E5A09FC05BD09F905BA0AC206640ABF0661 -0B6807090B6507060CAE07F90CAB07F60D1608510D13084E0DA708F00DA408ED0D5D08A60D49088A0B2206C300000000 -000000000000000009DE059F000000000C9E07E90ACC066E0C9407DF0C7E07C90E18096B0C9707E20000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -0A14073E0EB70B100DE40EE90EE10EDD0000000000000695000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000004B00240012000F0000000000270006 -0B4408430A2B0EB10000000000000F0A0000000000000000000000000000000000000000000000000000000000000000 -00000000000000000000000000000000002B00000004001A000000000000001D00000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000F2E00000054000000000000054600000000 -000000000000000007410F45001C0003000010110FC00FA0109910730000104A00000000000011E20000000000000000 -0000000000000000000000000000000000000000000000000000000000000000107C101A0000000011D21176115210EC -000000000000127000000000000000000000000000000000000000000000000000000000000000000000000000000000 -126911DB00000000000012A8126012321073106E11CB11470000124B1299107C00000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000001255124D120B00000000114D100A -00000000106A00000000000000000000130A00001313130D1301000000000000000000000000000000001334131F132B -000000000000000000000000000000000000000013250000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000137900000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000135400001361135B13C6000000000000 -00000000000000000000139C137313810000000000000000000000000000000000000000000000000000000000000000 -000000000000000013D013CD000000000000000000000000000000000000000000001366131600000000000000000000 -00000000000000000000000000000000134B130713481304135E13100000000013E213DD00000000136E131C13691319 -1376132200000000138C132E137C132813EA13E5000000001399133113BD134313A2133A139F13370000000013AD133D -0000000013B613400000000000000000000000000000000013ED00000000000000000000000000000000000000000000 -00000000000000000000000000000000000000000000000000000000000000001494149100000000000018751497183A -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000001906183D149A0000 -00000000000018BC0000000000000000000018FD00001918000000000000000000000000000000000000000000000000 -191500000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -00000000192D000000000000000000000000000019360000000000000000193900000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -19241921191E191B19331930192A19270000000000000000000000000000000019450000000000000000000000001948 -00000000000000000000000000000000000000000000000019420000193F193C00000000000000000000000000000000 -0000000000000000000000000000000019570000000000000000195A0000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -1951194E194B000000001954000000000000000000000000000000000000000019630000000019660000000000001969 -000000000000000000000000000000000000000000000000000000001960195D00000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000196C00000000000000000000000000000000 -000000000000000000000000000000001975196F00000000000000000000197200000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000019780000000000000000 -0000000000000000000000000000000000000000000000000000000000000000000000000000197B1985000000000000 -1981197E0000198800000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000001991198B00000000000000000000198E00000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000001994000000000000199E199A199700000000000000000000000000000000 -0000000000000000000000000000000019A1000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000019AA0000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000019A719A400000000000000000000000000000000 -000000000000000000000000000019AD0000000000000000000000000000000000000000000000000000000000000000 -19B2000000000000000000000000000000000000000000000000000019B50000000019B80000000019BB000000000000 -000000000000000000000000000019BE000000000000000000000000000000000000000019AF00000000000000000000 -19C100000000000019DC19E019C400000000000019E319E700000000000000000000000019C700000000000000000000 -0000000000000000000000000000000019CD000000000000000000000000000000000000000000000000000019D00000 -000019D30000000019D6000000000000000000000000000000000000000019D900000000000000000000000000000000 -0000000019CA000000000000000000000000000000000000000019EA0000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000019ED -000000000000000000001AE30000000000001AE900001AE600001AEF00001AEC00001AF2000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000001AF5000000000000000000001AF800001B0100001AFE1AFB0000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000005D50E58056806870E7C062905E6071A060706CF06AC -0000078107230734076D06AF0E8B07A706920773056B0890093A1B040E910E8F0EA30EA10A800A310B7C000006020EA5 -0E97078A0E7805D30BA60CD61B081B06086D0EC91B0C0A27114B114911471B0E0A140AE3129B129911491147086D0A27 -0000129B1299124D00000000000000000000000000000000000000000000000000000000000013880000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -0E930000000000000EA50E6B0E9909A30EB50EAD0EA90ADC1B100EBF0EBD0EBB0F161B120EC50F140ED10ECF0ECB0ECD -0EEB0EDB0ED50ED30EF50EF30E800EED06110EF90EF71B0A11CB0F050F030F010A1605DA0A0A05CB0A1C05E00A1905DD -0A6E06170A4D05FB0A7D06260A74061D0A7A06230A7706200AAC064E0AA8064A0AD9067B0AD606780AF206840ACF0671 -0B1C06BD0B01069D0B1F06C00B2506C60B2B06CC0B2806C90B5B06FC0B7107120BAE072B0BA807250BD507490BB40731 -0BE207560BD8074C0C1507750BDF07530C1B077B0C1807780C4D079B0C4707950C5307A10C5607A40C8207CD0C7A07C5 -0C8D07D80C8907D40CF2083B0CEF08380D1908540D0D08480D23085E0D1C08570D4208830D3808790D3E087F0D310872 -0D51089D0D4508860D6608AF0D5A08A30DC4090D0D6308AC0DCA09130DCD09160D7E08C70D7708C00DE1092A0DDE0927 -0DE9093F0DE6093C0DEF09450DF209480E0409570DF8094B0E1B096E0E07095A0E3809880E2F097F0D540B2E0E3B098B -0D3809A90E240DF5000000000000000009EC05AD09FF05C009B5057609B9057A09BD057E09C1058209D205930A0205C3 -09DA059B09CE058F0A0605C709D605970AB9065B0AC506670A9606380AA206440A9E06400A9206340AC8066A0A9A063C -0B6B070C0B5F07000CA207ED0CC808130C6707B20C6B07B60C6F07BA0C7307BE0CB808030CCB08160CC0080B0CB407FF -0CC4080F0CBC08070D9808E10DC1090A0DAD08F60DB108FA0DB508FE0DB909020E0C095F0DBD09060E2109740E270977 -000000000E1509680000000000000000111E10FC111B10F91130110E112711050F730F510F700F4E0F850F630F7C0F5A -1163115811601155000000001167115C0FB10FA60FAE0FA3000000000FB50FAA11A21180119F117D11B4119211AB1189 -0FE80FC60FE50FC30FFA0FD80FF10FCF11FC11ED11F911EA120411F5120011F1102F1020102C101D1037102810331024 -1243123812401235000000001247123C105B10501058104D00000000105F1054128A127B1287127812921283128E127F -10820000107F0000108A00001086000012D412B212D112AF12E612C412DD12BB10C1109F10BE109C10D310B110CA10A8 -1152114F10EC10E511D211CF1176116F1260125D1232122F0000000012A812A1112211001139111711341112112B1109 -0F770F550F8E0F6C0F890F670F800F5E11A6118411BD119B11B8119611AF118D0FEC0FCA10030FE10FFE0FDC0FF50FD3 -12D812B612EF12CD12EA12C812E112BF10C510A310DC10BA10D710B510CE10AC114410E810F310F61140113D000010EF -0F450F420F480F4B002A11CD002A0F9211C811720020005111C411C1000011790FC00FBD0FA00F9D00350031002D1007 -11E211DE11D511D811E61208000000001011100E1014101700440040003C00001270126C12631266127412961252124F -10731070107610790998001C0018106712FA12A40000000012F612F3000012AB10991096104A104700000039000310E0 -0001000100010001000100010001000100000001000100010000000000000000000000001B160000004E000000000000 -000000000000000000000000000000000000000000000000000002FF02FC02FA00000000000000000001000000000000 -1B1C0000000000001B2B1B2800001B1F00000000000000000000000C0000008F00000000000000000563000000000000 -0000000000920560000000000000000000000000000000001B2300000000000000000000000000000001000000000000 -0000000000000000000000000000000000000000000000000000000000000000000000000AE303050392038303740365 -1B5A02F403B003A10B3E00A500A10544030F03430314030503920383037403651B5A02F403B003A1000000A500A10544 -0B44078A0A8006920A2B0B7C0B100EA108430CD60B3E05D30000000000000BA600000000000000000000000000000000 -000000000000084200000000000000000000000000000000000000000000000000000000000000000000000000000000 -0E5005E309A509A10E7E0A250A21000006AC06020E5300000E740B1006AC06AC0A2B073406CF06CF0000078907810000 -076D083E06AF000000000000076D076D0000089A089208660000077F0000060A05B0071A0000060A0A80000005E305D5 -07230000067E06291415140D13FF078A068000000AE3141A10630F971149124B0000000000001B580A800A3105E60000 -00000000073E0AE3000000000000000003CA03C103E203DA0498045903D2045503DE04E703D604CF03BE051104EB049C -06DE06D406D106CF0922091E091B06B209530950068206E1072305E605E307340B400B360B330AE30DD90DD50DD2086D -0E000DFD0B440B4305D30A3109A30A2B0000000000000000000000000000000000000000030D00000000000000000000 -000000000000000000000000000000001B3A1B3300000000000000000000000000000000000000000000000000000000 -000000000000000000001B3F000000000000000000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000001B451B481B42000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000001B4D000000001B5200000000000000001B55 -0000000000000000000000000000000000000000000000000000000000000000000000000000000000001B5F00001B5C -00000000000000001B6E00001B651B620000000000001B71000000000000000000000000000000000000000000000000 -000000001B7500001B7B000000001B78000000001B7E0000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000001B840000055600000000000000000000000000000000055B054A1B810000 -000000001B8A1B87000000001B901B8D000000001B961B930000000000000000000000001B9C1B99000000001BA81BA5 -000000001BAE1BAB00000000000000000000000000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000001BC01BBD1BBA1BB700000000000000000000000000000000 -000000000000000000000000000000001BB41BB11BA21B9F00000000000000001BC61BC300000000000000001BCC1BC9 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -00001BE61BE4000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -0365030F0343031403A10392038303740342032F031C03B0038203730364035503F703AF03A0039100E600E200D900A3 -00F600F200EE00EA00B100AC00A700FA00C500C000BB00B600DD00D400CF00CA036803590346031903A4039503860377 -03450332031F03B3038503760367035803FA03B203A303940172016E016A01660182017E017A01760192018E018A0186 -01A2019E019A019601B201AE01AA01A601C201BE01BA01B605D5056801CA01C6067E062905E605E3060706CF06AC0687 -078107230734071A076D083E06AF07A706B2056B08900861060A095D068207730A3109A3093A06920B1006020ADC0A80 -0A2B0B7C073E0AE30CD6078A0B3E05D30BA608430A1405D80B440DE4086D0A2700000305061106950000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -00000000000000000000000000001B690000000000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000000000000000000000000552054F0542 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000001BD900000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000006B2073E -0000000000000000000000000000000000000000000000001BDE00000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000232E00000000000000000000000000000000000000000000 -000000000000000000000000000000002764000000000000000000000000000000000000000000000000000000000000 -1FB81FB01FAA1F981FCA1FC61FC21FBC201120091FFD1FD02039202920212019206B2057203D203B207F207920772073 -209D209B208F208D210120F720A920A12131212F212B212121662160214A21392180217E217C217621AA21A4218E218C -21C121BA21B221AE21E121DD21DB21C521F321EB21E721E322492247223D220122972295228B2289032522A3229F229B -231222D5032922C3232C232623202318233623342332233023BE23B82394233823C823C423C223C023E623E223D423CA -240E240C240824062428242624142412243C243A24382436245C245A2458244624A02490248E246E24CE24BC24AC24A8 -250424FE24F424F02510250E250C25082530252C251E251C2540253C25362534254A25482544254225C025BE25A4259A -25E225DE25DA25C4260A2606260425E626262624260E260C264426362634262A266C265C264A26482692267C26782676 -26B426AE269C269426CE26CC26C826BC26DA26D826D226D026F026EE26EC26DE270C270226FE26FC271627142710270E -2724271C271A2718273A27382732273027442742273E273C27542750274C2746275E275C275A27560000000027662762 -000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -00000000000000000000000000000000000000000000000000001BF800000000000020832081207F0000000000000000 -00000000000000000000000000000000000000000000000000001C3000001C2D00001C3600001C3300001C3C00001C39 -00001C4200001C3F00001C4800001C4500001C4E00001C4B1C5400001C510000000000001C5700000000000000000000 -1C6000001C5D1C5A1C691C6600001C6300001C6F1C6C0000000000001C781C7500000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000001C2A00870000000000001C7B1C820000008A -00000000000000000000000000000000000000000000000000001CF200001CD500001D4100001CA500001D5E00001D4A -00001D7900001D6E00001D8B00001D7E00001D9A00001D901DA600001DA10000000000001CBF00000000000000000000 -1DEA00001DD51DCC1E101E0600001DF100001E2D1E250000000000001D591E5000000000000000000000000000000000 -0000000000000000000000000000000000000000000000001EF3000000001CB000001F061F011EFC1D501F0F00000000 -00000000000000000000000000000000000000000000000000000000000000001ABB19F419EF000019FB1ABF1ABD19F6 -1AC31AC11A021A001ACB1AC91AC71AC51A111A0C1A071A511A1A1A181A131A5B1A391A2E1A2C1A2219F21A481A431A3E -1A891A871A851A831A371A8F1A8D1A8B1A991A971A951A931AA11A9F1A9D1A201AA91A2A1AA51AA31ACD1A4F1A4D1A7F -1AD51AD31AD11ACF1ADB1A531AD91AD71A591A571A551ADD1A631A611A5F1A5D1A6B1A691A671A651A731A711A6F1A6D -1AE11ADF1A771A751AAB1A7D1A7B1A791AB31AB11AAF1AAD00001AB91AB71AB51FC61F98000000001FAC1FA020F91F9E -1FA61FBC24161FA21FD02103213E1F9A01E901E001D701CE020D020401FB01F20245023C0233022501DB01D20257024E -01FF01F601ED01E40237022902110208025B0252024902400000021E0216022E02A00268027002600284026402880274 -02C402B00290026C02A402EC02B802C002D002B402BC02AC02D402E402C8029802A8029C0278028C029402E8027C02CC -02E002DC028002D824B0229521C320D300000000000000000000000000000000041C0411040608310440043904320427 -0475046E044E044704850482047F047C1A0219FB19F619EF1A1A1A131A0C1A071A3E1A391A2E1A2219F819F11A481A43 -1A0E1A091A0419FD1A301A241A1C1A151A4A1A451A401A3B00001A1F1A271A3320F91F9E1FC61F9820111F9C20131FC8 -23940329207F1FBE2101267C22D5233822F222CD22ED0325247C261023CE20B9214A241A2494205D234820911FFB2654 -213C20251FDD26E021AC1FA21FAC1FA02164216A207B20B32089261A1FDB2442048E048B0488213504B704B404B10491 -04C304C004BD04BA04E404CC04C904C604D604A3034E033B05290518050304F2034D033A0327053A07390A820A8506B4 -1CB91CAE1CA01C871D221CF01CD31CC91D6C1D5C1D481D3F1D8E1D891D7C1CCD1C8D1DA41D9F1CAA1D3D1DC01DBE1D55 -1E041DE81DCA1DBC1E8B1CE01E4E1E231EB31EAF1E9E1CD11CE41ECA1EC81EC01EF11D0C1EE31C9C1FD81F041EFF1EFA -1C9A1C941C8F1C891CBB1CB31CA81CA21CE21CCF1CCB1CC21CDD1CD81CEB1CE61CFF1D061CFA1CF51D1C1D151D0E1D0B -1D3A1D331D2C1D101D611D571D531D441D851D811D711D661D301DB21DA91D931DD81DE41DC61DBB1DFB1DF41DCF1DDF -1E1A1E131DED1E001E301E441E1F1E091E3E1E391E4A1E341E671E531E5B1E281E741E6E1E6A1E611E851E811E7D1E79 -1EA01E951E921E8D1EBA1EB51D171EA41ED81ED31ECE1EC21D1E1EE81EE51EDD03610352033F0311039D038E037F0370 -033E032B03BB03AC037E036F0360035103BA03AB039C038D04230418040D0402056A0A560B12042E0A5C0CE90C5B0A12 -21BC06DB0A630A5F22EF22AB213B22B20BED12110C290CDD0689075B071C0B7E0C2C0CE00B8F0A2906010BF9121D1214 -068C07640B81068D0A590C001220089612230C380AEF0B970C060B9A0A330C030C0A0B9D0A370A360C1E0BA10A3B0A3A -07680B8506910C220D030CFD0CF906900C1212260C3B0CEC0BF012170C2F0CE30C320CE6076F0B8907720B8C0BF3121A -05D7099C077E0BB705E805FF0A300A2D06AE0B1606940A530BA50722071F0B3D0BC90BC50BC20BBF08230C0E0BFC0BF6 -0D28082E08260CD805D1092D0939086C036C035D034A033703A80399038A037B03490336032303B70389037A036B035C -03FE03B603A70398042A041F04140409044A0443043C04350AF704780471045100000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000013B913B2 -0000000000000000000000000000000000000000000000000000000000000000000000000000276E0000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -067E05E300000000000000000000083E000000000E7A0E72000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000000000000000000027740EC12772276C -00000000000000000000000000000000000000000EFB0000000000000000000000000000000000000000000000000000 -00000000000000000000000000000000261C263622C52608276220AB1FAE237820DD267C2144276224FC243422392142 -265A25D025B6259823EE23A2234C23061FC027062670257C259623B423102095258E23882198272E25BC252A225325D8 -23DA235822CF21D12065202F1FE32660244423B2230C22872630259E259225082462272C271E26C4268A256E24DE2482 -21DF211F25F22732246623CC251A24BA2186211D26C0261824D6237C236223082520206326A024EA24E0249820332035 -2255260026A4257A21701FB425F823062075242423E4220B1FA421FD1FEB246A209F24D42293234225EA257E244A2115 -225D233E264823281FCE2422226D255A24CA22F62031200F2069267A25F0254622A121D9214A20C3270A2698246C238A -22C1205727402734222D21BF2642231E23A823822283223B251624E424922400268C264E2582263A23A020CD20592043 -220721CF25EA25C623DE24B82324226921A2217020FB1FD6250223FA23EC220D26C626BE267E25122674248C1FE526E4 -1FF71FC4221926B2230622992182217226582588243223AE2055269E22B9275E2370235422E522DF24D22460242023F2 -26A62243201326EA263C236421941FEF23E422E9221D21F5218A20BB204726AA234622FC22D722AE25C824F8242A23F0 -207D26B8267825CE240223B020BD23722736272226AC259024A8252E236022E3239C23D824C024AE204925621FD225FE -24C6225721C72041264022BF234E216821D525DE26A225C00000000020E91FFF000022B700002113243E23DC20370000 -248A2480247E24700000250424C226CA000025F600002594000026662650000026F626F40000000026B02662272A26F8 -206120031FF91FE720EF20E12087206721882117211120F3222B22272213218C22BB22A5228D223723802368235A22F8 -246423F423BA23AA24782474247622F224882486247C247A24E424B224A4249A250A24F624EC24E82580254C254C2532 -25FC25FA25E025D42650264C2620261E220F26E826DC26BA00000000253E28331FE1200D202D1FA8206D205F20172001 -20ED20DF20D720E121462140211B211321D721D3215C21542223221721FF21EF22372225222B221B227B227722712245 -22D122CF22B7228F23542328232022DB238E23802376237A23D623BC245623AA241C240A23FC23DC2440243E2430242E -246824502452244824DA24BE24B224A62564250A24F224E425DC25D625B2257825EE25F625EC25E025FC25F425F825FA -2656263E2620260226BA26A82682267226E226DC26D426CA28092762271226E81F501F3C281B280728912861284F1F52 -000000002760275800000000000000000AE10AE60AE20ADE00000D2B0D2B0AE500000000000000000000000000000000 -13F900000000000013F613FC13F313F00000000000000000147D000014300000141F141A13FF145114621446140B1439 -1475147202F4147814041401146E146A141C1417140F14070000142A14271421143B14361433142D0000144800001443 -14530000144E144B145F145C000014561424147A14671464140A1459143E141218C918C918C318C318CB18CB18C918C9 -18CF18CF18CB18CB18C718C718CF18CF18CD18CD18C718C718C518C518CD18CD18E718E718C518C518E918E918E718E7 -18D318D318E918E918D118D118D318D318D518D518D118D118D718D718D518D518DD18DD18D718D718DF18DF18DB18DB -18E318E318D918D918EB18EB18E118E118EF18EF18EB18EB18F318F318EF18EF18F118F118F318F318F518F518F118F1 -18F718F718F718F718FB18FB1918191818F918F918FB18FB163C163C18F918F900000000191519150000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -18ED00000000000018AE18ED18ED18ED18B218AA18AA18AE190D190D190618B2190B190B1900190018B618B618B618B6 -1878187814C014C0189C189C18B818B818A818A818AC18AC18B418B418B018B018A018A018A018B4190F190F190F190F -18A018901880187C14A914A2149F18A414C914C214BF14B6151514F814E314D8152F1526151D151814DC14CD15371532 -14E7157F157C14E015C515B715B015A51650164D163E160D167316701665165B16B21620168F168A16F016ED16EA16B5 -1706170116FE16F71724171F171C170D1730172D172A172717551741173E173316241637177317641504150014FC178B -17D8150C150815D11814180317FA17EB182A181F181C18171855184E1847182D158F1872186F185E0064005D18421599 -008000790072006B18941890188C188814B3148618A418A014C214BF14B914B6150F14F814F514F21523152015181515 -1532152F15291526171F171C170116FE173E173317301724178B162416371741180017FD15D1179018171814180E1803 -185E185B18581842187C1872186F1869189818901884188014B614A914A2149F14E314D814C914BC15371526151214F8 -157C14E014DC14CD15B715B015A514E7163E1618160D15C51665165B1650164D1620168F168A167016ED16EA16B516B2 -170D170616F716F01730172D172A17271773176417551733150014FC148B163717EB17D815D11504181C1811180317FA -184E18471830181F18901698185E185514F814BC14B6189815C5152C152615121730160215F715D41811180316371733 -16D716D01698185E16AA1685168216DE15D716C716C416AD153B1608160515DA14EB14D514D114A616681648164514AD -15F115E615DF166B161B15C215F415F716AA16851682166215D716C716C416AD153B1608160515DA14EB14D514D114A6 -16681648164514AD15F115E615DF166B161B15C215F415F715F115E615DF16621673160215D415F715DF15B715B015A5 -168A167315F115E600000000148E148E0000000000000000000000000000000000000000000000000000000000000000 -14DF14DB14DB14CC150314FF14FB14E6156C1570154E154E15CC15AC15A815B315D015D015C815CC15E9164116101610 -15FA15FA15E215E9165E165315FE15FE167A16761676165E169E169E1692167E16BC16C016B816A21714171016F316F3 -1758176B176F1767177D17761776175817B617AD17A9177D17C117BD179E17961822179A0000000017DF17F217EE1826 -1806180A17E317DF14D414AC18611861150B14EA14EE14D01552153A15561507165715ED161415BA184A185117811760 -17F6171817CC1865173A16A6176717101736175C17C517DB1565153E17DB175C173614A516FA17A217E715BE16411692 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000148414891709163A16961594161E17B1 -1542162716231635000000000000159C0000000000000000000000000000000000000000000000000000000000000000 -05401BE21BE002F61C260093008D05460000000002FF1C28000000000000000000000000000000000000000000000000 -0000000000000000000000000000000009961B181B1A02FC0E3E00A500A109961BF41BFE1BFA0E421BE41BEA1BE81BF6 -1BF01BEE1BEC1BE6098E000000001BF2000C000C000C0992099609960996000C000002FA1BE002F6008D009305400546 -0E3E00A500A11B1A00971BFE1BFA0E4202F802F402F2009D00000544055905480566009B009909900000000000000000 -0000005A16CA005716CD00680000006116DB007616D4006F16E5008416E2007D14941491149114801497183A183A1494 -1875187518751497149D14821482187514C5149D149D149D14C714C714C714C5151B151B151B14C714A014A014A0151B -14A314A314A314A014AA14AA14AA14A3158D158B158B14AA14B414871487158D15A315A315A314B415DD15DD15DD15A3 -160B160B160B15DD164B164B164B160B166E166E166E164B168816881688166E168D168D168D168816B016B016B0168D -16E816E816E816B017041704170416E81722172217221704159715971597172214B714B714B7159714BA14BA14BA14B7 -148C148C148C14BA14C018331833148C14A714A714A714C0174D1749174914A7174617511751174D0000000000001746 -00970095008D0000009F009D009B009902F402F200A500A1030302FA02F802F6030F0343031403050392038303740365 -0546054003B003A1009305590544054805E305D5056805660687067E062905E6071A060706CF06AC07A7078107230734 -0861076D083E06AF077306B2056B0890098E060A095D0682099609940992099009A3093A0692099806020ADC0A800A31 -0B7C073E0AE30B10078A0B3E05D30A2B08430A1405D80CD60DE4086D0A270BA60E3E061106950B441BD50E440E420E40 -1BEE1BEC1BE21BD71C851F041F0B1BE01CC71CB71CAC1C9E1D9D1EC61EBE1EB11CAE1CA01C871CC51CF01CD31CC91CB9 -1D5C1D481D3F1D221D891D7C1CCD1D6C1DA41D9F1CAA1D8E1DC01DBE1D551C8D1DE81DCA1DBC1D3D1CE01E4E1E231E04 -1EAF1E9E1CD11E8B1ECA1EC81EC01EB31D0C1EE31C9C1CE4008B00881CB51EF11ABB19F419EF1A7F19FB1ABF1ABD19F6 -1AC31AC11A021A001ACB1AC91AC71AC51A111A0C1A071A511A1A1A181A131A5B1A391A2E1A2C1A2200001A481A431A3E -1A8319F2000000001A8B1A891A871A851A8F1A8D000000001A971A951A931A371A201A99000000001AA31AA11A9F1A9D -1A2A1AA5000000000000000000001AA900090E4E0E480E4600001B2F0E4A0E4C1B381B361B311BCF00001BD31BD11B3D -00000000000000000000000000000000000000000000000000000000000000000E630F350F3300000F1C00000E950F0E -0E9B0F1E0F2027760EA70E9F1B140E9D0EAB0EAF0EB30F280EB90F120E740F100EC30F2C0F2A0EEF27D30EC7277027D1 -0ED70E6D27D50EFD27D70EDF05D80ED90F260EE70EE50EE30EF10F2427780F220F180EFF00001BDC0E870E850F0C0F1A -000027DB27D90E8900000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000277A00000000000000000000277D00000000000000000000000000000000 -278000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000278627830000000000000000000000000000000000000000 -00000000000000000000000000000000000000000000000000000000000000002789000000000000000000000000278C -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -00000000000000000000000000000000000000000000000000000000000000002792000000000000000027950000278F -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -279B27980000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -00000000000000000000000000000000000000000000279E000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000000000000000000027A427A100000000 -27B327AF27AB27A700000000000027B70000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000027BB00000000000027C227C927BE27C600000000000027CD0000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -05E605E305D5056806AC0687067E06290734071A060706CF06AF07A70781072308900861076D083E0682077306B2056B -093A0692060A095D0ADC0A800A3109A3073E0AE30B1006020B3E05D30A2B0B7C0A1405D80CD6078A086D0A270BA60843 -061106950B440DE405E605E305D5056806AC0687067E06290734071A060706CF06AF07A70781072308900861076D083E -0682077306B2056B093A0692060A095D0ADC0A800A3109A3073E0AE3000006020B3E05D30A2B0B7C0A1405D80CD6078A -086D0A270BA60843061106950B440DE405E605E305D5056806AC0687067E06290734071A060706CF06AF07A707810723 -08900861076D083E0682077306B2056B093A0692060A095D0ADC0A800A3109A3073E0AE30B1006020B3E05D30A2B0B7C -0A1405D80CD6078A086D0A270BA60843061106950B440DE405E605E30000056800000687000000000000071A06070000 -06AF07A707810000089008610000083E0682077306B2056B093A0692060A095D0ADC00000A3109A3073E0AE30B100000 -0B3E05D30A2B0B7C0A1405D80CD60000086D0A270BA60843061106950B440DE405E605E305D5056806AC0687067E0629 -0734071A060706CF06AF07A70781072308900861076D083E0682077306B2056B093A0692060A095D0ADC0A800A3109A3 -073E0AE30B1006020B3E05D30A2B0B7C0A1405D80CD6078A086D0A270BA60843061106950B440DE405E6000005D50568 -00000687067E06290734071A0607000006AF07A707810723089008610000083E0682077306B2056B093A06920000095D -0ADC0A800A3109A3073E0AE30B1006020B3E05D30A2B0B7C0A1405D80CD6078A086D0A270BA60843061106950B440DE4 -05E6000005D5056800000687067E06290734071A060706CF000007A70000072308900861000000000682077306B2056B -093A06920000095D0ADC0A800A3109A3073E0AE30B1006020B3E05D30A2B0B7C0A1405D80CD6078A086D0A270BA60843 -061106950B440DE405E605E305D5056806AC0687067E06290734071A060706CF06AF07A70781072308900861076D083E -0682077306B2056B093A0692060A095D0ADC0A800A3109A3073E0AE30B1006020B3E05D30A2B0B7C0A1405D80CD6078A -086D0A270BA60843061106950B440DE406AF07A70781072308900861076D083E0682077306B2056B093A0692060A095D -0ADC0A800A3109A3073E0AE30B1006020B3E05D30A2B0B7C0A1405D80CD6078A093A0692060A095D0ADC0A800A3109A3 -073E0AE30B1006020B3E05D30A2B0B7C0A1405D80CD6078A086D0A270BA60843061106950B440DE405E605E305D50568 -06AC0687067E06290734071A060706CF06AF07A70781072308900861076D083E0682077306B2056B093A0692060A095D -0ADC0A800A3109A3073E0AE30B1006020B3E05D30A2B0B7C0A1405D80CD6078A086D0A270BA60843061106950B440DE4 -05E605E305D5056806AC0687067E06290734071A060706CF06AF07A707810723061106950B440DE4000000000E8D0E76 -0F990F970F950F40100A0FBB0FB90F9B103F103D103B100C1063104510431041106C106A100A106510921090108E106E -114710E31B50077F116B114D114B1149120B11CD11CB116D122B1229120F120D1255124D124B122D1299125B12591257 -1B4B129F129D129B1299120B11CB114D0F950F40124B124D0FB90F9B0F990F97103B100C100A0FBB10431041103F103D -100A106510631045108E106E106C106A1B50077F10921090114B1149114710E311CB116D116B114D120F120D120B11CD -124B122D122B1229125912571255124D129D129B1299125B11CB114D1B4B129F124B124D1299120B0F990F970F950F40 -100A0FBB0FB90F9B103F103D103B100C1063104510431041106C106A100A106510921090108E106E114710E31B50077F -116B114D114B1149120B11CD11CB116D122B1229120F120D1255124D124B122D1299125B125912571B4B129F129D129B -1299120B11CB114D0F950F40124B124D0FB90F9B0F990F97103B100C100A0FBB10431041103F103D100A106510631045 -108E106E106C106A1B50077F10921090114B1149114710E311CB116D116B114D120F120D120B11CD124B122D122B1229 -125912571255124D129D129B1299125B11CB114D1B4B129F124B124D1299120B0F990F970F950F40100A0FBB0FB90F9B -103F103D103B100C1063104510431041106C106A100A106510921090108E106E114710E31B50077F116B114D114B1149 -120B11CD11CB116D122B1229120F120D1255124D124B122D1299125B125912571B4B129F129D129B1299120B11CB114D -12FF12FD124B124D031403050000000003740365030F034303B003A103920383030F0343031403050392038303740365 -0314030503B003A103740365030F034303B003A103920383030F03430314030503920383037403650314030503B003A1 -03740365030F034303B003A1039203830000000000000000000000000000000000000000000000000000000000000000 -13521350134E1346136C13641359135713861384137F137113931391138F138A13A713A51397139513B413B013AB13A9 -13E0276A13C013BB13D713E813C913C41350134E134613DB13641359135713521384137F1371136C13971393138F138A -13AB13A913A713A513D313B413B213B013D513CB13C213C40000000013D9276800000000000000000000000000000000 -00000000000000000000000000000000158B14A0149D148214A314B4183300001597172214A7166E168D15A314BA14B7 -14871704160B16E814AA151B14C715DD16B01688164B158D18C118E518F518BF000014A0149D000014A300000000148C -1597172214A70000168D15A314BA14B700001704160B16E814AA151B14C715DD16B00000164B00000000000000000000 -000014A00000000014A30000000000001597000014A70000168D15A314BA000000001704160B000014AA0000000015DD -16B00000164B000018C1000018F50000000014A0149D000014A300000000148C0000172214A7166E168D15A314BA14B7 -00001704160B16E814AA151B14C715DD16B01688164B0000000018E5000018BF158B14A0149D148214A314B41833148C -1597000014A7166E168D15A314BA14B714871704160B16E814AA151B14C715DD16B01688164B158D0000000000000000 -158B14A0149D000014A314B4183300001597000014A7166E168D15A314BA14B714871704160B16E814AA151B14C715DD -16B01688164B158D000000000000000003F103160307030A04FA04DE04AB0468000005310520050B0000000000000000 -010A0106010200FE011A01160112010E012A01260122011E013A01360132012E014A01460142013E015A01560152014E -05E31BFC0162015E0000093605E5076D05E605E305D5056806AC0687067E06290734071A060706CF06AF07A707810723 -08900861076D083E0682077306B2056B076F06B1060A095D0933082A0869086300000000000000000000000000000000 -00000000000000000000000000000000000000000000000000000000000000000761075E00000000000000000000076C -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000606000000000000000000000000000000000000000000000000 -00001D5C1D4D1C720000000000000000000000000000000000000000000000001DA620A521622249213E25E421331FC6 -229923A422B01FCC229D201B21F7204F2612240E24D82045224F237E20C1212526521F9E1F982265225F20B31FAC21AC -24A22484224B2626032922CD236E20B7266E20E72051241800000000000000001C0C1C041C001C181C081C201C101C1C -0000000000001C1400000000000000000000000020AF21F9000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000030F0343031403050392038303740365 -0000000003B003A1000000000000000027DD1FBA1FB21FB61FED1FE91FE71FDF1FF51FF91FF31FF12005200327E51F12 -1F1427DF20152007202327E3201B200B202D202B1FD420271F18203F203B28A120532051204D204B20672061205F1F1A -20752071206F206D2093208B20872085209920992099209720A720A3239627EB20B520B120AD27ED20C720C520BF20C9 -20D520D120CF20CB20DF20DB20DB20D920FD20EB20E520E320F520F120FF20EF2109210721232041210D210F2105210B -2129212727EF2119214821372133212D2150214E27F327F11F1C2156214C2152215E215E215A1F1E2170216E216C27F5 -217A217827F7217421841F20217E21E927FB21902192218C219A219C27FD21961F2221A821A6219E21B821B621B421B0 -21C91F2627FF1F2428B521D121CD21CB25382803280321DD28111F2821E521E51F2A21F121ED286522092205220321FB -22131F2C1F2E22112221221F22152805222B22292221222322352231222F2233224121BD22392237226322592251224D -2267225B2261280B2279280D2275226B227D1F30226F22731F32227F2285228122A7280F2291228D22BB1F3822B522C7 -201F201D1F341F361F6A252222BD22C922DD22D322D122CF22E71F3A281922D9281D22F822F422E11F3C22FE22EB22FA -230A23042302230023141F3E230E281F231C1F40231628212823232A232823222827233A282527F9234A233C23442340 -23562354235A235223502829235E235C23761F42236C236A23842366282B237423902386282F282D23921F44238C238E -27E1239E2398239A283523AC283123A6283723C623BC23B6283B283923D223D023EA1F4623E823E023F823F61F481F48 -1F4A240423FE23FC283F241E283D24102841242C280124241F4E1F4C28452843284B284728492448244E244E244C284D -1F5424541F5224522462245E28511F56247A28531F5824682496248A28572855249E249C249A1F5A24AA285B285B2859 -24B624B4285D1F5C1F6024C4285F1F5E286324D024C824CC24E624E21F6224DC286928671F6424EE286D24FA286B1F66 -286F250A250625002518287325142871252625241F6828132875215825281F6C253828172815287725501F6E2646253A -2554205B2552254E2879255C25582556256A2568255E255A25802576256C256025742572257025661F70287D287F287B -2887258A258625841F741F722881258C259C1F762885288325A825A225A0259E25B225AA25AC25A61F7825B425B025AE -25C21F7A25BA25B825CC25CA288925C4288B1F1625D21F7C25E81F801F7E288D288F260A260225F42628262226162614 -262C262E27E72893263E263827E926322664265E289728952680266A28992668268E2684268826861F822696289B2690 -26B61F84289D269A28A3289F26C221A028A526D61F881F8626E226E228A71F8A1F8C26F228A926E627082704270026FA -2720271228AB1F8E27281F921F90272628B128AF1F9428AD27441F96273A28B32752274E274A27480000000028B72756 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -00000000000000000000000000000000", -); -//12544 bytes -enum canonMappingTrieEntries = TrieEntry!(ushort, 8, 8, 5)(x" -000000000000000000000000000000200000000000000220", -x" -000000000000010000000000000008000000000000001000", -x" -030202020202010002050202020202040602020202020202000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000200010000000000060005000400030009000800070000 -00000000000B000A0000000000000000000D000C0000000000000010000F000E00140013001200110016001500000000 -000000000000000000000000000000000000000000170000000000180000000000000000000000000000000000000000 -000000000000000000000000000000000000001A001900000000001B000000000000001D001C00000000000000000000 -0000001E00000000000000200000001F0000002100000000000000220000000000000023000000000000002400000000 -000000000000000000000000000000000026002500000000000000000028002700000000002900000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000002C002B002A000000000000000000000000000000000000000000000000 -000000000000000000000000000000000030002F002E002D00340033003200310038003700360035003C003B003A0039 -000000000000003D000000000000000000000000003E0000000000410040003F00450044004300420048000000470046 -000000000049000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000004A0000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -00000000000000000000000000000000004C004B000000000050004F004E004D00000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000540053005200510058005700560055 -005C005B005A00590000005F005E005D0000006200610060000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000064006300000000006500000000000000000000 -000000000000000000000000000000000000006600000000000000000000000000000000000000000000000000670000 -000000000000000000000000006800000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000690000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -00000000000000000000000000000000006B006A000000000000006D006C000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000710070006F006E00750074007300720079007800770076007D007C007B007A -000000000000007E00000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -00250012000F000C00850000004F004500CB00A400A1009E013301240121011E01A0019D01880000000001DA01B601A3 -02730270026D00000000000002F30287033803250322031F039800000362035803DE03B703B403B10446043A04370434 -04B404B1049C0000000004EE04CA04B7058A058705840000061C0000060D059E033E002B033B0028038C00790380006D -0392007F038F007C03A2008F0395008203CD00BA0000000003DB00C803D800C503E400D103FB00E8041000FD040A00F7 -041901060413010000000000041C01090440012A043D0127045C01490443012D00000000000001300471015D0462014F -01700477016300000000047A016604840185000000000000018E04A80194049900000000000004A204E401D004D901C5 -0000000004F801E405450231052F021B054B023705350221056902550552023E057B026405580244000000000572025B -0594027D058D027605B4029D059B028405E002C905B702A0061002F605F502DE03110628030B0302000006310314062E -00000000050401F00000000000000000000000000000000002AC00000000000000000000000005C30000000000000000 -00000000000000000000000000000000000000000000000000000000000000000000000000000000013D036900560000 -02A304FB01E70450028E05A9029205BA028A05AD029605A5035B0048000005A10653064A035400410416010300000000 -0522020E046B0157065F065C05250211000000000000046500000000040700F403650052049601820656064D06500647 -036F005C036C005903EA00D703E700D40456014304530140050101ED04FE01EA053B02270538022405C002A905BD02A6 -0578026105660252042501120000000000000000000000000351003E0000000004F101DD03F400E104E701D304D101BD -061602FC04EA01D6000000000000000000000000000000000000000000000000066B00000010000D0000000000000137 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000662 -00000000000000000000000100000000000000000000000006450670063D00000000072C06DF06C30798077800000759 -00000000000008D100000000000000000000000000000000000000000000000000000000000000000000000000000000 -078107350000000008C10867084707E9000000000000092F000000000000000000000000000000000000000000000000 -00000000000000000000000000000000092808CA000000000000095F091F08FD09B400000000000000000000000009B7 -0000000000000000000000000000000009C3000009CC09C609BA0000000000000000000000000000000009ED09D809E4 -000000000000000000000000000000000000000009DE0000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000000000000A2000000000000000000000 -00000000000000000000000000000000000000000000000000000000000000000A0500000A0E0A080A41000000000000 -000000000000000000000A2F0A1A0A260000000000000000000000000000000000000000000000000000000000000000 -00000000000000000A470A44000000000000000000000000000000000000000000000A1109CF00000000000000000000 -000000000000000000000000000000000A0209C009FF09BD0A0B09C9000000000A4D0A4A000000000A1709D50A1409D2 -0A1D09DB000000000A2909E70A2309E10A530A50000000000A2C09EA0A3E09FC0A3509F30A3209F0000000000A3809F6 -000000000A3B09F900000000000000000AC10ABE0000000000000ACA0AC40AC700000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000ACD00000AD30000000000000000 -000000000000000000000000000000000AD0000000000000000000000000000000000000000000000000000000000000 -00000000000000000000000000000000000000000AE800000000000000000000000000000AF100000000000000000AF4 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000ADF0ADC0AD90AD60AEE0AEB0AE50AE200000000000000000000000000000000 -0B000000000000000000000000000B030000000000000000000000000000000000000000000000000AFD00000AFA0AF7 -00000000000000000000000000000000000000000000000000000000000000000B1200000000000000000B1500000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000B0C0B090B06000000000B0F0000000000000000000000000000000000000000 -0B1E000000000B210000000000000B24000000000000000000000000000000000000000000000000000000000B1B0B18 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000B27 -00000000000000000000000000000000000000000000000000000000000000000B300B2A000000000000000000000B2D -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -0000000000000B3300000000000000000000000000000000000000000000000000000000000000000000000000000000 -0000000000000B360B400000000000000B3C0B3900000B43000000000000000000000000000000000000000000000000 -00000000000000000000000000000000000000000000000000000000000000000B4C0B46000000000000000000000B49 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000B4F0000000000000B590B550B52 -0B5F00000000000000000000000000000000000000000000000000000B62000000000B65000000000B68000000000000 -00000000000000000000000000000B6B00000000000000000000000000000000000000000B5C00000000000000000000 -0B6E00000000000000000B890B7100000000000000000B8C0000000000000000000000000B7400000000000000000000 -000000000000000000000000000000000B7A00000000000000000000000000000000000000000000000000000B7D0000 -00000B80000000000B8300000000000000000000000000000000000000000B8600000000000000000000000000000000 -000000000B7700000000000000000000000000000000000000000B8F0000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000000000000000000000000B9200000000 -00000B9800000B9500000B9E00000B9B00000BA100000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -0BA4000000000000000000000BA700000BB000000BAD0BAA000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000003830070037D006A0389007603860073 -039F008C039B008803AE009B03A5009203AB009803A8009503D400C103D000BD040100EE03FE00EB040400F103F700E4 -041F010C040D00FA0422010F04280115042E011B042B011804490136045F014C046E015A04680154047D016904740160 -048A01760480016C048D0179048701730493017F0490017C04A50191049F018B04AB019704AE019A04D501C104CD01B9 -04E001CC04DC01C8052C021805290215053E022A0532021E054802340541022D055F024B05550241055B0247054E023A -056C02580562024E0581026A0575025E05DD02C6057E026705E302CC05E602CF059702800590027905EC02D505E902D2 -05F202DB05EF02D805F802E105FB02E4060402EA060102E7061902FF060702ED06340317062B030E056F04310637031A -06590000062205FE0000000000000000035F004C0372005F03280015032C00190330001D033400210345003203750062 -034D003A0341002E037900660349003603E100CE03ED00DA03BE00AB03CA00B703C600B303BA00A703F000DD03C200AF -04590146044D013A04F501E1051B020704BA01A604BE01AA04C201AE04C601B2050B01F7051E020A051301FF050701F3 -05170203050F01FB05B1029A05DA02C305C602AF05CA02B305CE02B705D202BB060A02F005D602BF061F030506250308 -00000000061302F90000000000000000081B07F9081807F6082D080B08240802069E067C069B067906B0068E06A70685 -0858084D0855084A00000000085C085106D406C906D106C60000000006D806CD089308710890086E08A50883089C087A -070706E5070406E2071906F7071006EE08EB08DC08E808D908F308E408EF08E0074A073B0747073807520743074E073F -090E0903090B09000000000009120907076A075F0767075C00000000076E07630949093A0946093709510942094D093E -0787000007840000078F0000078B0000098B096909880966099D097B0994097207C0079E07BD079B07D207B007C907A7 -0847084407E907E208C108BE08670860091F091C08FD08FA00000000095F0958081F07FD083608140831080F08280806 -06A2068006B9069706B4069206AB06890897087508AE088C08A9088708A0087E070B06E907220700071D06FB071406F2 -098F096D09A6098409A1097F0998097607C407A207DB07B907D607B407CD07AB084107E507F007F3083D083A000007EC -0670066D06730676000008BC000006BD08B908630640000008B508B20000086A06DF06DC06C306C00BB90BB60BB30726 -08D108CD08C408C708D508F700000000072C0729072F07320BC20BBF0BBC0000092F092B092209250933095509190916 -07780775077B077E031D063D063A077209B1095B0000000009AD09AA00000962079807950759075600000000064307DF -000000000BC70BC500000000000000000000000000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000079300000000004F0152000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -00000000000000000000000000000000000000000000000000000000000000000BCC0BC9000000000000000000000000 -00000000000000000000000000000000000000000000000000000BCF0000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000BD50BD80BD20000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000BDB -000000000BDE00000000000000000BE10000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000BE700000BE40000000000000000000000000000000000000000000000000000000000000000 -00000000000000000000000000000000000000000BEA00000BF0000000000BED000000000BF300000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000BF9000000060000000000000000 -0000000000000000000900030BF60000000000000BFF0BFC000000000C050C02000000000C0B0C080000000000000000 -000000000C110C0E000000000C1D0C1A000000000C230C20000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000C350C320C2F0C2C -00000000000000000000000000000000000000000000000000000000000000000C290C260C170C140000000000000000 -0C3B0C3800000000000000000C410C3E0000000000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000C490C470000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000C4400000000000000000000000000000000 -000000000000000000000C5100000C4E00000C5700000C5400000C5D00000C5A00000C6300000C6000000C6900000C66 -00000C6F00000C6C0C7500000C720000000000000C78000000000000000000000C8100000C7E0C7B0C8A0C8700000C84 -00000C900C8D0000000000000C960C930000000000000000000000000000000000000000000000000000000000000000 -00000000000000000000000000000C4B000000000000000000000C990000000000000000000000000000000000000000 -000000000000000000000CA200000C9F00000CA800000CA500000CAE00000CAB00000CB400000CB100000CBA00000CB7 -00000CC000000CBD0CC600000CC30000000000000CC9000000000000000000000CD200000CCF0CCC0CDB0CD800000CD5 -00000CE10CDE0000000000000CE70CE40000000000000000000000000000000000000000000000000000000000000000 -00000000000000000CEA000000000C9C00000CF30CF00CED00000CF600000000124B125D0FB7124113270E290D831043 -0E4F12930E991327116710CD0F550E971279121511FD11E3109D106910190FEB0D8D12F3128911C711E110790FF50E1D -11D910510EDB1309120311890F65121D108D10250FBD0EFF0E050DD90D9D127D10D310770FF10F95125911E711DD1171 -10E9130712FB12CF12A111B9114D11070F0B0E87122F130B10ED1083117D112F0ECB0E8512CB124911471047102F0FED -117F0E0312B11159114F11150DDD0DDF0F67123D12B511C50EBB0D8712350FEB0E1110C110950F270D7F0F1B0DA510F1 -0E2311450F9D1011122711C910D70E7D0F6F100D126D10050D9110BF0F7B11A5113D0FDB0DDB0DC30E091291122D1195 -0FA10F070E9F0E3712F712AB10F310530FB50DF91313130D0F490EF312690FFD106D104B0F910F5711791153111110AF -12A3127111CD126110670E410DFB0DE90F230EFD1227120B1091112D10030F770EE50EBB0E670D97116B10A9109B0F29 -12D112C912951175128D110F0D9F12DD0DB10D8F0F3512C10FEB0F9F0EC70EBD127711D310CB10730DF712AF0FAD1323 -103B10210FD10FCB114310E710BD10A112B70F5D0DC512E3126310310ED70DA910950FD50F390F170ECF0E310DEB12BB -10150FE10FC30FA7120D116310C3109F0E1312C5128F121310B110750E33103D130F12FF12BD11DB1121118B102D0FCF -1063108B113311250DED11AD0D93123B11390F690EF50DE712670FB3101B0EB50F03122112B31205000000000E590DB5 -00000FAB00000E7B10CF108F0DE10000110D1105110310F50000116D113512D300001233000011DF0000128312730000 -12E912E70000000012BF127F130512EB0E010DB90DB30DA10E5F0E530E170E070ECD0E7F0E790E630F470F430F2F0ED1 -0FAF0FA30F970F531049103510270FDD10EB10A3107D106F10FD10F910FB10F7110B1109110110FF11531127111D1117 -11731161115B115711CB11971197118D12391237122312191273126F124F124D0F2B12E112D912C700000000119313D6 -0D9B0DC10DD70D810E0B0DFF0DC90DB70E5D0E510E490E530E9B0E950E830E7B0F050F010EB10EA90F3F0F330F1D0F13 -0F530F410F470F370F890F850F7F0F5F0FBF0FBD0FAB0F99102110050FFF0FC710571049104110451089107F10E3106F -10B910B510AB108F10D110CF10C910C710EF10DD10DF10D5114911311127111F11AF1173115F1153121F121B11F911C3 -122B123312291223123912311235123712751265124F123F12C712B91299128B12DB12D912D512D313AC132712F912E1 -0D370D2313BE13AA1434140413F20D390000000013251321000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000ABB00000A7A0000 -000000000000000000000000000000000AB50AB2000000000A590A560AAE0AAA0A680A650A5F0A5C00000A740A710A6B -0A830A800A7D0A7700000A8C00000A890A9500000A920A8F0AA10A9E00000A980A6E0AB80AA70AA400000A9B0A860A62 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -00000000000000000000000000000000000000000000000000000000000000000000132900000000000000000000132C -00000000000000000000000000000000132F000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000001335133200000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -1338000000000000000000000000133B0000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -1341000000000000000013440000133E0000000000000000000000000000000000000000000000000000000000000000 -00000000000000000000000000000000134A134700000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000000000000000134D0000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000013531350000000001362135E135A1356000000000000136600000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000136A00000000000013711378136D1375 -000000000000137C00000000000000000000000000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000013800D8B0D850D890DA70DA30DA10D990DAF0DB30DAD0DAB0DBB0DB913880CF9 -0CFB13820DC70DBD0DD113860DCB0DBF0DD70DD50D950DD30CFF0DE50DE314440DF50DF30DF10DEF0E070E010DFF0D01 -0E110E0F0E0D0E0B0E1B0E190E170E150E210E210E210E1F0E270E25105D138E0E2F0E2D0E2B13900E3B0E390E350E3D -0E470E450E430E3F0E510E4D0E4D0E4B0E690E5B0E570E550E650E610E6B0E5F0E710E6F0E890DE70E750E770E6D0E73 -0E8D0E8B13920E810E9D0E930E910E8F0EA50EA3139613940D030EAB0EA10EA70EB30EB30EAF0D050EBB0EB90EB71398 -0EC30EC1139A0EBF0EC90D070EC50F0F139E0ED30ED50ED10EDD0EDF13A00ED90D090EE90EE70EE10EF10EEF0EED0EEB -0EF70D0D13A20D0B14580EFF0EFB0EF9118F13A613A60F0913B40D0F0F0D0F0D0D110F150F1114080F250F210F1F0F19 -0F2F0D130D150F2D0F3D0F3B0F3113A80F470F450F3D0F3F0F510F4D0F4B0F4F0F5B0F590F550F530F730F6B0F630F61 -0F750F6D0F7113AE0F8713B00F830F790F8B0D170F7D0F810D190F8D0F930F8F0FA513B20F9B0F970FAF0D1F0FA90FB9 -0DCF0DCD0D1B0D1D0D5111810FB10FBB0FC90FC10FBF0FBD0FD30D2113BC0FC513C00FDD0FD90FCD0D230FE30FD70FDF -0FEF0FE90FE70FE50FF70D250FF313C20FFB0D270FF913C413C610071005100113CA100913C8139C1017100B1013100F -102310211027101F101D13CC102B102910410D2910391037104D103313CE103F1059104F13D213D0105B0D2B10551057 -13841065105F106113D8107113D4106B13DA1081107F107B13DE13DC1087108510990D2D1097109310A710A50D2F0D2F -0D3110B310AD10AB13E210BB13E010B713E410C513A410C10D350D3313E813E613EE13EA13EC10D510DB10DB10D913F0 -0D3B10E10D3910DF10E910E513F40D3D10FF13F60D3F10EF1113110D13FA13F8111B111911170D41112313FE13FE13FC -112B112914000D430D47113714020D4514061141113B113F115511510D49114B140C140A0D4B115D14101165140E0D4D -14121173116F1169117B141611771414118511830D4F13B614180EAD11870D53118F13BA13B8141A119B0D55126B1191 -119F0DFD119D1199141C11A711A311A111B511B311A911A511CB11C111B711AB11BF11BD11BB11B10D5714201422141E -142A11D511D111CF0D5B0D59142411D711E50D5D1428142611EF11EB11E911E711F911F111F311ED0D5F11FB11F711F5 -12070D61120111FF1211120F142C1209142E0CFD12170D6312250D670D65143014321243123F12311253125112471245 -12551257138A14361265125F138C125B1281127B143A143812971287143C128512A5129B129F129D0D6912A9143E12A7 -12C30D6B144012AD1446144212CD0EE3144812D70D6F0D6D12DB12DB144A0D710D7312E5144C12DF12F512F112EF12ED -12FD12F9144E0D7513030D790D771301145414520D7B145013150D7D13111456131D131B1319131700000000145A131F -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -00000000000000000000000000000000", -); -dstring decompCanonTable() nothrow @nogc pure @safe { -static immutable dchar[5212] t = -"\u0000\u003B\u0000\u003C\u0338\u0000\u003D\u0338\u0000\u003E\u0338\u0000\u0041\u0300\u0000\u0041\u0301\u0000\u0041"d~ -"\u0302\u0000\u0041\u0302\u0300\u0000\u0041\u0302\u0301\u0000\u0041\u0302\u0303\u0000\u0041\u0302\u0309\u0000\u0041"d~ -"\u0303\u0000\u0041\u0304\u0000\u0041\u0306\u0000\u0041\u0306\u0300\u0000\u0041\u0306\u0301\u0000\u0041\u0306\u0303"d~ -"\u0000\u0041\u0306\u0309\u0000\u0041\u0307\u0000\u0041\u0307\u0304\u0000\u0041\u0308\u0000\u0041\u0308\u0304\u0000"d~ -"\u0041\u0309\u0000\u0041\u030A\u0000\u0041\u030A\u0301\u0000\u0041\u030C\u0000\u0041\u030F\u0000\u0041\u0311\u0000"d~ -"\u0041\u0323\u0000\u0041\u0323\u0302\u0000\u0041\u0323\u0306\u0000\u0041\u0325\u0000\u0041\u0328\u0000\u0042\u0307"d~ -"\u0000\u0042\u0323\u0000\u0042\u0331\u0000\u0043\u0301\u0000\u0043\u0302\u0000\u0043\u0307\u0000\u0043\u030C\u0000"d~ -"\u0043\u0327\u0000\u0043\u0327\u0301\u0000\u0044\u0307\u0000\u0044\u030C\u0000\u0044\u0323\u0000\u0044\u0327\u0000"d~ -"\u0044\u032D\u0000\u0044\u0331\u0000\u0045\u0300\u0000\u0045\u0301\u0000\u0045\u0302\u0000\u0045\u0302\u0300\u0000"d~ -"\u0045\u0302\u0301\u0000\u0045\u0302\u0303\u0000\u0045\u0302\u0309\u0000\u0045\u0303\u0000\u0045\u0304\u0000\u0045"d~ -"\u0304\u0300\u0000\u0045\u0304\u0301\u0000\u0045\u0306\u0000\u0045\u0307\u0000\u0045\u0308\u0000\u0045\u0309\u0000"d~ -"\u0045\u030C\u0000\u0045\u030F\u0000\u0045\u0311\u0000\u0045\u0323\u0000\u0045\u0323\u0302\u0000\u0045\u0327\u0000"d~ -"\u0045\u0327\u0306\u0000\u0045\u0328\u0000\u0045\u032D\u0000\u0045\u0330\u0000\u0046\u0307\u0000\u0047\u0301\u0000"d~ -"\u0047\u0302\u0000\u0047\u0304\u0000\u0047\u0306\u0000\u0047\u0307\u0000\u0047\u030C\u0000\u0047\u0327\u0000\u0048"d~ -"\u0302\u0000\u0048\u0307\u0000\u0048\u0308\u0000\u0048\u030C\u0000\u0048\u0323\u0000\u0048\u0327\u0000\u0048\u032E"d~ -"\u0000\u0049\u0300\u0000\u0049\u0301\u0000\u0049\u0302\u0000\u0049\u0303\u0000\u0049\u0304\u0000\u0049\u0306\u0000"d~ -"\u0049\u0307\u0000\u0049\u0308\u0000\u0049\u0308\u0301\u0000\u0049\u0309\u0000\u0049\u030C\u0000\u0049\u030F\u0000"d~ -"\u0049\u0311\u0000\u0049\u0323\u0000\u0049\u0328\u0000\u0049\u0330\u0000\u004A\u0302\u0000\u004B\u0000\u004B\u0301"d~ -"\u0000\u004B\u030C\u0000\u004B\u0323\u0000\u004B\u0327\u0000\u004B\u0331\u0000\u004C\u0301\u0000\u004C\u030C\u0000"d~ -"\u004C\u0323\u0000\u004C\u0323\u0304\u0000\u004C\u0327\u0000\u004C\u032D\u0000\u004C\u0331\u0000\u004D\u0301\u0000"d~ -"\u004D\u0307\u0000\u004D\u0323\u0000\u004E\u0300\u0000\u004E\u0301\u0000\u004E\u0303\u0000\u004E\u0307\u0000\u004E"d~ -"\u030C\u0000\u004E\u0323\u0000\u004E\u0327\u0000\u004E\u032D\u0000\u004E\u0331\u0000\u004F\u0300\u0000\u004F\u0301"d~ -"\u0000\u004F\u0302\u0000\u004F\u0302\u0300\u0000\u004F\u0302\u0301\u0000\u004F\u0302\u0303\u0000\u004F\u0302\u0309"d~ -"\u0000\u004F\u0303\u0000\u004F\u0303\u0301\u0000\u004F\u0303\u0304\u0000\u004F\u0303\u0308\u0000\u004F\u0304\u0000"d~ -"\u004F\u0304\u0300\u0000\u004F\u0304\u0301\u0000\u004F\u0306\u0000\u004F\u0307\u0000\u004F\u0307\u0304\u0000\u004F"d~ -"\u0308\u0000\u004F\u0308\u0304\u0000\u004F\u0309\u0000\u004F\u030B\u0000\u004F\u030C\u0000\u004F\u030F\u0000\u004F"d~ -"\u0311\u0000\u004F\u031B\u0000\u004F\u031B\u0300\u0000\u004F\u031B\u0301\u0000\u004F\u031B\u0303\u0000\u004F\u031B"d~ -"\u0309\u0000\u004F\u031B\u0323\u0000\u004F\u0323\u0000\u004F\u0323\u0302\u0000\u004F\u0328\u0000\u004F\u0328\u0304"d~ -"\u0000\u0050\u0301\u0000\u0050\u0307\u0000\u0052\u0301\u0000\u0052\u0307\u0000\u0052\u030C\u0000\u0052\u030F\u0000"d~ -"\u0052\u0311\u0000\u0052\u0323\u0000\u0052\u0323\u0304\u0000\u0052\u0327\u0000\u0052\u0331\u0000\u0053\u0301\u0000"d~ -"\u0053\u0301\u0307\u0000\u0053\u0302\u0000\u0053\u0307\u0000\u0053\u030C\u0000\u0053\u030C\u0307\u0000\u0053\u0323"d~ -"\u0000\u0053\u0323\u0307\u0000\u0053\u0326\u0000\u0053\u0327\u0000\u0054\u0307\u0000\u0054\u030C\u0000\u0054\u0323"d~ -"\u0000\u0054\u0326\u0000\u0054\u0327\u0000\u0054\u032D\u0000\u0054\u0331\u0000\u0055\u0300\u0000\u0055\u0301\u0000"d~ -"\u0055\u0302\u0000\u0055\u0303\u0000\u0055\u0303\u0301\u0000\u0055\u0304\u0000\u0055\u0304\u0308\u0000\u0055\u0306"d~ -"\u0000\u0055\u0308\u0000\u0055\u0308\u0300\u0000\u0055\u0308\u0301\u0000\u0055\u0308\u0304\u0000\u0055\u0308\u030C"d~ -"\u0000\u0055\u0309\u0000\u0055\u030A\u0000\u0055\u030B\u0000\u0055\u030C\u0000\u0055\u030F\u0000\u0055\u0311\u0000"d~ -"\u0055\u031B\u0000\u0055\u031B\u0300\u0000\u0055\u031B\u0301\u0000\u0055\u031B\u0303\u0000\u0055\u031B\u0309\u0000"d~ -"\u0055\u031B\u0323\u0000\u0055\u0323\u0000\u0055\u0324\u0000\u0055\u0328\u0000\u0055\u032D\u0000\u0055\u0330\u0000"d~ -"\u0056\u0303\u0000\u0056\u0323\u0000\u0057\u0300\u0000\u0057\u0301\u0000\u0057\u0302\u0000\u0057\u0307\u0000\u0057"d~ -"\u0308\u0000\u0057\u0323\u0000\u0058\u0307\u0000\u0058\u0308\u0000\u0059\u0300\u0000\u0059\u0301\u0000\u0059\u0302"d~ -"\u0000\u0059\u0303\u0000\u0059\u0304\u0000\u0059\u0307\u0000\u0059\u0308\u0000\u0059\u0309\u0000\u0059\u0323\u0000"d~ -"\u005A\u0301\u0000\u005A\u0302\u0000\u005A\u0307\u0000\u005A\u030C\u0000\u005A\u0323\u0000\u005A\u0331\u0000\u0060"d~ -"\u0000\u0061\u0300\u0000\u0061\u0301\u0000\u0061\u0302\u0000\u0061\u0302\u0300\u0000\u0061\u0302\u0301\u0000\u0061"d~ -"\u0302\u0303\u0000\u0061\u0302\u0309\u0000\u0061\u0303\u0000\u0061\u0304\u0000\u0061\u0306\u0000\u0061\u0306\u0300"d~ -"\u0000\u0061\u0306\u0301\u0000\u0061\u0306\u0303\u0000\u0061\u0306\u0309\u0000\u0061\u0307\u0000\u0061\u0307\u0304"d~ -"\u0000\u0061\u0308\u0000\u0061\u0308\u0304\u0000\u0061\u0309\u0000\u0061\u030A\u0000\u0061\u030A\u0301\u0000\u0061"d~ -"\u030C\u0000\u0061\u030F\u0000\u0061\u0311\u0000\u0061\u0323\u0000\u0061\u0323\u0302\u0000\u0061\u0323\u0306\u0000"d~ -"\u0061\u0325\u0000\u0061\u0328\u0000\u0062\u0307\u0000\u0062\u0323\u0000\u0062\u0331\u0000\u0063\u0301\u0000\u0063"d~ -"\u0302\u0000\u0063\u0307\u0000\u0063\u030C\u0000\u0063\u0327\u0000\u0063\u0327\u0301\u0000\u0064\u0307\u0000\u0064"d~ -"\u030C\u0000\u0064\u0323\u0000\u0064\u0327\u0000\u0064\u032D\u0000\u0064\u0331\u0000\u0065\u0300\u0000\u0065\u0301"d~ -"\u0000\u0065\u0302\u0000\u0065\u0302\u0300\u0000\u0065\u0302\u0301\u0000\u0065\u0302\u0303\u0000\u0065\u0302\u0309"d~ -"\u0000\u0065\u0303\u0000\u0065\u0304\u0000\u0065\u0304\u0300\u0000\u0065\u0304\u0301\u0000\u0065\u0306\u0000\u0065"d~ -"\u0307\u0000\u0065\u0308\u0000\u0065\u0309\u0000\u0065\u030C\u0000\u0065\u030F\u0000\u0065\u0311\u0000\u0065\u0323"d~ -"\u0000\u0065\u0323\u0302\u0000\u0065\u0327\u0000\u0065\u0327\u0306\u0000\u0065\u0328\u0000\u0065\u032D\u0000\u0065"d~ -"\u0330\u0000\u0066\u0307\u0000\u0067\u0301\u0000\u0067\u0302\u0000\u0067\u0304\u0000\u0067\u0306\u0000\u0067\u0307"d~ -"\u0000\u0067\u030C\u0000\u0067\u0327\u0000\u0068\u0302\u0000\u0068\u0307\u0000\u0068\u0308\u0000\u0068\u030C\u0000"d~ -"\u0068\u0323\u0000\u0068\u0327\u0000\u0068\u032E\u0000\u0068\u0331\u0000\u0069\u0300\u0000\u0069\u0301\u0000\u0069"d~ -"\u0302\u0000\u0069\u0303\u0000\u0069\u0304\u0000\u0069\u0306\u0000\u0069\u0308\u0000\u0069\u0308\u0301\u0000\u0069"d~ -"\u0309\u0000\u0069\u030C\u0000\u0069\u030F\u0000\u0069\u0311\u0000\u0069\u0323\u0000\u0069\u0328\u0000\u0069\u0330"d~ -"\u0000\u006A\u0302\u0000\u006A\u030C\u0000\u006B\u0301\u0000\u006B\u030C\u0000\u006B\u0323\u0000\u006B\u0327\u0000"d~ -"\u006B\u0331\u0000\u006C\u0301\u0000\u006C\u030C\u0000\u006C\u0323\u0000\u006C\u0323\u0304\u0000\u006C\u0327\u0000"d~ -"\u006C\u032D\u0000\u006C\u0331\u0000\u006D\u0301\u0000\u006D\u0307\u0000\u006D\u0323\u0000\u006E\u0300\u0000\u006E"d~ -"\u0301\u0000\u006E\u0303\u0000\u006E\u0307\u0000\u006E\u030C\u0000\u006E\u0323\u0000\u006E\u0327\u0000\u006E\u032D"d~ -"\u0000\u006E\u0331\u0000\u006F\u0300\u0000\u006F\u0301\u0000\u006F\u0302\u0000\u006F\u0302\u0300\u0000\u006F\u0302"d~ -"\u0301\u0000\u006F\u0302\u0303\u0000\u006F\u0302\u0309\u0000\u006F\u0303\u0000\u006F\u0303\u0301\u0000\u006F\u0303"d~ -"\u0304\u0000\u006F\u0303\u0308\u0000\u006F\u0304\u0000\u006F\u0304\u0300\u0000\u006F\u0304\u0301\u0000\u006F\u0306"d~ -"\u0000\u006F\u0307\u0000\u006F\u0307\u0304\u0000\u006F\u0308\u0000\u006F\u0308\u0304\u0000\u006F\u0309\u0000\u006F"d~ -"\u030B\u0000\u006F\u030C\u0000\u006F\u030F\u0000\u006F\u0311\u0000\u006F\u031B\u0000\u006F\u031B\u0300\u0000\u006F"d~ -"\u031B\u0301\u0000\u006F\u031B\u0303\u0000\u006F\u031B\u0309\u0000\u006F\u031B\u0323\u0000\u006F\u0323\u0000\u006F"d~ -"\u0323\u0302\u0000\u006F\u0328\u0000\u006F\u0328\u0304\u0000\u0070\u0301\u0000\u0070\u0307\u0000\u0072\u0301\u0000"d~ -"\u0072\u0307\u0000\u0072\u030C\u0000\u0072\u030F\u0000\u0072\u0311\u0000\u0072\u0323\u0000\u0072\u0323\u0304\u0000"d~ -"\u0072\u0327\u0000\u0072\u0331\u0000\u0073\u0301\u0000\u0073\u0301\u0307\u0000\u0073\u0302\u0000\u0073\u0307\u0000"d~ -"\u0073\u030C\u0000\u0073\u030C\u0307\u0000\u0073\u0323\u0000\u0073\u0323\u0307\u0000\u0073\u0326\u0000\u0073\u0327"d~ -"\u0000\u0074\u0307\u0000\u0074\u0308\u0000\u0074\u030C\u0000\u0074\u0323\u0000\u0074\u0326\u0000\u0074\u0327\u0000"d~ -"\u0074\u032D\u0000\u0074\u0331\u0000\u0075\u0300\u0000\u0075\u0301\u0000\u0075\u0302\u0000\u0075\u0303\u0000\u0075"d~ -"\u0303\u0301\u0000\u0075\u0304\u0000\u0075\u0304\u0308\u0000\u0075\u0306\u0000\u0075\u0308\u0000\u0075\u0308\u0300"d~ -"\u0000\u0075\u0308\u0301\u0000\u0075\u0308\u0304\u0000\u0075\u0308\u030C\u0000\u0075\u0309\u0000\u0075\u030A\u0000"d~ -"\u0075\u030B\u0000\u0075\u030C\u0000\u0075\u030F\u0000\u0075\u0311\u0000\u0075\u031B\u0000\u0075\u031B\u0300\u0000"d~ -"\u0075\u031B\u0301\u0000\u0075\u031B\u0303\u0000\u0075\u031B\u0309\u0000\u0075\u031B\u0323\u0000\u0075\u0323\u0000"d~ -"\u0075\u0324\u0000\u0075\u0328\u0000\u0075\u032D\u0000\u0075\u0330\u0000\u0076\u0303\u0000\u0076\u0323\u0000\u0077"d~ -"\u0300\u0000\u0077\u0301\u0000\u0077\u0302\u0000\u0077\u0307\u0000\u0077\u0308\u0000\u0077\u030A\u0000\u0077\u0323"d~ -"\u0000\u0078\u0307\u0000\u0078\u0308\u0000\u0079\u0300\u0000\u0079\u0301\u0000\u0079\u0302\u0000\u0079\u0303\u0000"d~ -"\u0079\u0304\u0000\u0079\u0307\u0000\u0079\u0308\u0000\u0079\u0309\u0000\u0079\u030A\u0000\u0079\u0323\u0000\u007A"d~ -"\u0301\u0000\u007A\u0302\u0000\u007A\u0307\u0000\u007A\u030C\u0000\u007A\u0323\u0000\u007A\u0331\u0000\u00A8\u0300"d~ -"\u0000\u00A8\u0301\u0000\u00A8\u0342\u0000\u00B4\u0000\u00B7\u0000\u00C6\u0301\u0000\u00C6\u0304\u0000\u00D8\u0301"d~ -"\u0000\u00E6\u0301\u0000\u00E6\u0304\u0000\u00F8\u0301\u0000\u017F\u0307\u0000\u01B7\u030C\u0000\u0292\u030C\u0000"d~ -"\u02B9\u0000\u0300\u0000\u0301\u0000\u0308\u0301\u0000\u0313\u0000\u0391\u0300\u0000\u0391\u0301\u0000\u0391\u0304"d~ -"\u0000\u0391\u0306\u0000\u0391\u0313\u0000\u0391\u0313\u0300\u0000\u0391\u0313\u0300\u0345\u0000\u0391\u0313\u0301"d~ -"\u0000\u0391\u0313\u0301\u0345\u0000\u0391\u0313\u0342\u0000\u0391\u0313\u0342\u0345\u0000\u0391\u0313\u0345\u0000"d~ -"\u0391\u0314\u0000\u0391\u0314\u0300\u0000\u0391\u0314\u0300\u0345\u0000\u0391\u0314\u0301\u0000\u0391\u0314\u0301"d~ -"\u0345\u0000\u0391\u0314\u0342\u0000\u0391\u0314\u0342\u0345\u0000\u0391\u0314\u0345\u0000\u0391\u0345\u0000\u0395"d~ -"\u0300\u0000\u0395\u0301\u0000\u0395\u0313\u0000\u0395\u0313\u0300\u0000\u0395\u0313\u0301\u0000\u0395\u0314\u0000"d~ -"\u0395\u0314\u0300\u0000\u0395\u0314\u0301\u0000\u0397\u0300\u0000\u0397\u0301\u0000\u0397\u0313\u0000\u0397\u0313"d~ -"\u0300\u0000\u0397\u0313\u0300\u0345\u0000\u0397\u0313\u0301\u0000\u0397\u0313\u0301\u0345\u0000\u0397\u0313\u0342"d~ -"\u0000\u0397\u0313\u0342\u0345\u0000\u0397\u0313\u0345\u0000\u0397\u0314\u0000\u0397\u0314\u0300\u0000\u0397\u0314"d~ -"\u0300\u0345\u0000\u0397\u0314\u0301\u0000\u0397\u0314\u0301\u0345\u0000\u0397\u0314\u0342\u0000\u0397\u0314\u0342"d~ -"\u0345\u0000\u0397\u0314\u0345\u0000\u0397\u0345\u0000\u0399\u0300\u0000\u0399\u0301\u0000\u0399\u0304\u0000\u0399"d~ -"\u0306\u0000\u0399\u0308\u0000\u0399\u0313\u0000\u0399\u0313\u0300\u0000\u0399\u0313\u0301\u0000\u0399\u0313\u0342"d~ -"\u0000\u0399\u0314\u0000\u0399\u0314\u0300\u0000\u0399\u0314\u0301\u0000\u0399\u0314\u0342\u0000\u039F\u0300\u0000"d~ -"\u039F\u0301\u0000\u039F\u0313\u0000\u039F\u0313\u0300\u0000\u039F\u0313\u0301\u0000\u039F\u0314\u0000\u039F\u0314"d~ -"\u0300\u0000\u039F\u0314\u0301\u0000\u03A1\u0314\u0000\u03A5\u0300\u0000\u03A5\u0301\u0000\u03A5\u0304\u0000\u03A5"d~ -"\u0306\u0000\u03A5\u0308\u0000\u03A5\u0314\u0000\u03A5\u0314\u0300\u0000\u03A5\u0314\u0301\u0000\u03A5\u0314\u0342"d~ -"\u0000\u03A9\u0000\u03A9\u0300\u0000\u03A9\u0301\u0000\u03A9\u0313\u0000\u03A9\u0313\u0300\u0000\u03A9\u0313\u0300"d~ -"\u0345\u0000\u03A9\u0313\u0301\u0000\u03A9\u0313\u0301\u0345\u0000\u03A9\u0313\u0342\u0000\u03A9\u0313\u0342\u0345"d~ -"\u0000\u03A9\u0313\u0345\u0000\u03A9\u0314\u0000\u03A9\u0314\u0300\u0000\u03A9\u0314\u0300\u0345\u0000\u03A9\u0314"d~ -"\u0301\u0000\u03A9\u0314\u0301\u0345\u0000\u03A9\u0314\u0342\u0000\u03A9\u0314\u0342\u0345\u0000\u03A9\u0314\u0345"d~ -"\u0000\u03A9\u0345\u0000\u03B1\u0300\u0000\u03B1\u0300\u0345\u0000\u03B1\u0301\u0000\u03B1\u0301\u0345\u0000\u03B1"d~ -"\u0304\u0000\u03B1\u0306\u0000\u03B1\u0313\u0000\u03B1\u0313\u0300\u0000\u03B1\u0313\u0300\u0345\u0000\u03B1\u0313"d~ -"\u0301\u0000\u03B1\u0313\u0301\u0345\u0000\u03B1\u0313\u0342\u0000\u03B1\u0313\u0342\u0345\u0000\u03B1\u0313\u0345"d~ -"\u0000\u03B1\u0314\u0000\u03B1\u0314\u0300\u0000\u03B1\u0314\u0300\u0345\u0000\u03B1\u0314\u0301\u0000\u03B1\u0314"d~ -"\u0301\u0345\u0000\u03B1\u0314\u0342\u0000\u03B1\u0314\u0342\u0345\u0000\u03B1\u0314\u0345\u0000\u03B1\u0342\u0000"d~ -"\u03B1\u0342\u0345\u0000\u03B1\u0345\u0000\u03B5\u0300\u0000\u03B5\u0301\u0000\u03B5\u0313\u0000\u03B5\u0313\u0300"d~ -"\u0000\u03B5\u0313\u0301\u0000\u03B5\u0314\u0000\u03B5\u0314\u0300\u0000\u03B5\u0314\u0301\u0000\u03B7\u0300\u0000"d~ -"\u03B7\u0300\u0345\u0000\u03B7\u0301\u0000\u03B7\u0301\u0345\u0000\u03B7\u0313\u0000\u03B7\u0313\u0300\u0000\u03B7"d~ -"\u0313\u0300\u0345\u0000\u03B7\u0313\u0301\u0000\u03B7\u0313\u0301\u0345\u0000\u03B7\u0313\u0342\u0000\u03B7\u0313"d~ -"\u0342\u0345\u0000\u03B7\u0313\u0345\u0000\u03B7\u0314\u0000\u03B7\u0314\u0300\u0000\u03B7\u0314\u0300\u0345\u0000"d~ -"\u03B7\u0314\u0301\u0000\u03B7\u0314\u0301\u0345\u0000\u03B7\u0314\u0342\u0000\u03B7\u0314\u0342\u0345\u0000\u03B7"d~ -"\u0314\u0345\u0000\u03B7\u0342\u0000\u03B7\u0342\u0345\u0000\u03B7\u0345\u0000\u03B9\u0000\u03B9\u0300\u0000\u03B9"d~ -"\u0301\u0000\u03B9\u0304\u0000\u03B9\u0306\u0000\u03B9\u0308\u0000\u03B9\u0308\u0300\u0000\u03B9\u0308\u0301\u0000"d~ -"\u03B9\u0308\u0342\u0000\u03B9\u0313\u0000\u03B9\u0313\u0300\u0000\u03B9\u0313\u0301\u0000\u03B9\u0313\u0342\u0000"d~ -"\u03B9\u0314\u0000\u03B9\u0314\u0300\u0000\u03B9\u0314\u0301\u0000\u03B9\u0314\u0342\u0000\u03B9\u0342\u0000\u03BF"d~ -"\u0300\u0000\u03BF\u0301\u0000\u03BF\u0313\u0000\u03BF\u0313\u0300\u0000\u03BF\u0313\u0301\u0000\u03BF\u0314\u0000"d~ -"\u03BF\u0314\u0300\u0000\u03BF\u0314\u0301\u0000\u03C1\u0313\u0000\u03C1\u0314\u0000\u03C5\u0300\u0000\u03C5\u0301"d~ -"\u0000\u03C5\u0304\u0000\u03C5\u0306\u0000\u03C5\u0308\u0000\u03C5\u0308\u0300\u0000\u03C5\u0308\u0301\u0000\u03C5"d~ -"\u0308\u0342\u0000\u03C5\u0313\u0000\u03C5\u0313\u0300\u0000\u03C5\u0313\u0301\u0000\u03C5\u0313\u0342\u0000\u03C5"d~ -"\u0314\u0000\u03C5\u0314\u0300\u0000\u03C5\u0314\u0301\u0000\u03C5\u0314\u0342\u0000\u03C5\u0342\u0000\u03C9\u0300"d~ -"\u0000\u03C9\u0300\u0345\u0000\u03C9\u0301\u0000\u03C9\u0301\u0345\u0000\u03C9\u0313\u0000\u03C9\u0313\u0300\u0000"d~ -"\u03C9\u0313\u0300\u0345\u0000\u03C9\u0313\u0301\u0000\u03C9\u0313\u0301\u0345\u0000\u03C9\u0313\u0342\u0000\u03C9"d~ -"\u0313\u0342\u0345\u0000\u03C9\u0313\u0345\u0000\u03C9\u0314\u0000\u03C9\u0314\u0300\u0000\u03C9\u0314\u0300\u0345"d~ -"\u0000\u03C9\u0314\u0301\u0000\u03C9\u0314\u0301\u0345\u0000\u03C9\u0314\u0342\u0000\u03C9\u0314\u0342\u0345\u0000"d~ -"\u03C9\u0314\u0345\u0000\u03C9\u0342\u0000\u03C9\u0342\u0345\u0000\u03C9\u0345\u0000\u03D2\u0301\u0000\u03D2\u0308"d~ -"\u0000\u0406\u0308\u0000\u0410\u0306\u0000\u0410\u0308\u0000\u0413\u0301\u0000\u0415\u0300\u0000\u0415\u0306\u0000"d~ -"\u0415\u0308\u0000\u0416\u0306\u0000\u0416\u0308\u0000\u0417\u0308\u0000\u0418\u0300\u0000\u0418\u0304\u0000\u0418"d~ -"\u0306\u0000\u0418\u0308\u0000\u041A\u0301\u0000\u041E\u0308\u0000\u0423\u0304\u0000\u0423\u0306\u0000\u0423\u0308"d~ -"\u0000\u0423\u030B\u0000\u0427\u0308\u0000\u042B\u0308\u0000\u042D\u0308\u0000\u0430\u0306\u0000\u0430\u0308\u0000"d~ -"\u0433\u0301\u0000\u0435\u0300\u0000\u0435\u0306\u0000\u0435\u0308\u0000\u0436\u0306\u0000\u0436\u0308\u0000\u0437"d~ -"\u0308\u0000\u0438\u0300\u0000\u0438\u0304\u0000\u0438\u0306\u0000\u0438\u0308\u0000\u043A\u0301\u0000\u043E\u0308"d~ -"\u0000\u0443\u0304\u0000\u0443\u0306\u0000\u0443\u0308\u0000\u0443\u030B\u0000\u0447\u0308\u0000\u044B\u0308\u0000"d~ -"\u044D\u0308\u0000\u0456\u0308\u0000\u0474\u030F\u0000\u0475\u030F\u0000\u04D8\u0308\u0000\u04D9\u0308\u0000\u04E8"d~ -"\u0308\u0000\u04E9\u0308\u0000\u05D0\u05B7\u0000\u05D0\u05B8\u0000\u05D0\u05BC\u0000\u05D1\u05BC\u0000\u05D1\u05BF"d~ -"\u0000\u05D2\u05BC\u0000\u05D3\u05BC\u0000\u05D4\u05BC\u0000\u05D5\u05B9\u0000\u05D5\u05BC\u0000\u05D6\u05BC\u0000"d~ -"\u05D8\u05BC\u0000\u05D9\u05B4\u0000\u05D9\u05BC\u0000\u05DA\u05BC\u0000\u05DB\u05BC\u0000\u05DB\u05BF\u0000\u05DC"d~ -"\u05BC\u0000\u05DE\u05BC\u0000\u05E0\u05BC\u0000\u05E1\u05BC\u0000\u05E3\u05BC\u0000\u05E4\u05BC\u0000\u05E4\u05BF"d~ -"\u0000\u05E6\u05BC\u0000\u05E7\u05BC\u0000\u05E8\u05BC\u0000\u05E9\u05BC\u0000\u05E9\u05BC\u05C1\u0000\u05E9\u05BC"d~ -"\u05C2\u0000\u05E9\u05C1\u0000\u05E9\u05C2\u0000\u05EA\u05BC\u0000\u05F2\u05B7\u0000\u0627\u0653\u0000\u0627\u0654"d~ -"\u0000\u0627\u0655\u0000\u0648\u0654\u0000\u064A\u0654\u0000\u06C1\u0654\u0000\u06D2\u0654\u0000\u06D5\u0654\u0000"d~ -"\u0915\u093C\u0000\u0916\u093C\u0000\u0917\u093C\u0000\u091C\u093C\u0000\u0921\u093C\u0000\u0922\u093C\u0000\u0928"d~ -"\u093C\u0000\u092B\u093C\u0000\u092F\u093C\u0000\u0930\u093C\u0000\u0933\u093C\u0000\u09A1\u09BC\u0000\u09A2\u09BC"d~ -"\u0000\u09AF\u09BC\u0000\u09C7\u09BE\u0000\u09C7\u09D7\u0000\u0A16\u0A3C\u0000\u0A17\u0A3C\u0000\u0A1C\u0A3C\u0000"d~ -"\u0A2B\u0A3C\u0000\u0A32\u0A3C\u0000\u0A38\u0A3C\u0000\u0B21\u0B3C\u0000\u0B22\u0B3C\u0000\u0B47\u0B3E\u0000\u0B47"d~ -"\u0B56\u0000\u0B47\u0B57\u0000\u0B92\u0BD7\u0000\u0BC6\u0BBE\u0000\u0BC6\u0BD7\u0000\u0BC7\u0BBE\u0000\u0C46\u0C56"d~ -"\u0000\u0CBF\u0CD5\u0000\u0CC6\u0CC2\u0000\u0CC6\u0CC2\u0CD5\u0000\u0CC6\u0CD5\u0000\u0CC6\u0CD6\u0000\u0D46\u0D3E"d~ -"\u0000\u0D46\u0D57\u0000\u0D47\u0D3E\u0000\u0DD9\u0DCA\u0000\u0DD9\u0DCF\u0000\u0DD9\u0DCF\u0DCA\u0000\u0DD9\u0DDF"d~ -"\u0000\u0F40\u0FB5\u0000\u0F42\u0FB7\u0000\u0F4C\u0FB7\u0000\u0F51\u0FB7\u0000\u0F56\u0FB7\u0000\u0F5B\u0FB7\u0000"d~ -"\u0F71\u0F72\u0000\u0F71\u0F74\u0000\u0F71\u0F80\u0000\u0F90\u0FB5\u0000\u0F92\u0FB7\u0000\u0F9C\u0FB7\u0000\u0FA1"d~ -"\u0FB7\u0000\u0FA6\u0FB7\u0000\u0FAB\u0FB7\u0000\u0FB2\u0F80\u0000\u0FB3\u0F80\u0000\u1025\u102E\u0000\u1B05\u1B35"d~ -"\u0000\u1B07\u1B35\u0000\u1B09\u1B35\u0000\u1B0B\u1B35\u0000\u1B0D\u1B35\u0000\u1B11\u1B35\u0000\u1B3A\u1B35\u0000"d~ -"\u1B3C\u1B35\u0000\u1B3E\u1B35\u0000\u1B3F\u1B35\u0000\u1B42\u1B35\u0000\u1FBF\u0300\u0000\u1FBF\u0301\u0000\u1FBF"d~ -"\u0342\u0000\u1FFE\u0300\u0000\u1FFE\u0301\u0000\u1FFE\u0342\u0000\u2002\u0000\u2003\u0000\u2190\u0338\u0000\u2192"d~ -"\u0338\u0000\u2194\u0338\u0000\u21D0\u0338\u0000\u21D2\u0338\u0000\u21D4\u0338\u0000\u2203\u0338\u0000\u2208\u0338"d~ -"\u0000\u220B\u0338\u0000\u2223\u0338\u0000\u2225\u0338\u0000\u223C\u0338\u0000\u2243\u0338\u0000\u2245\u0338\u0000"d~ -"\u2248\u0338\u0000\u224D\u0338\u0000\u2261\u0338\u0000\u2264\u0338\u0000\u2265\u0338\u0000\u2272\u0338\u0000\u2273"d~ -"\u0338\u0000\u2276\u0338\u0000\u2277\u0338\u0000\u227A\u0338\u0000\u227B\u0338\u0000\u227C\u0338\u0000\u227D\u0338"d~ -"\u0000\u2282\u0338\u0000\u2283\u0338\u0000\u2286\u0338\u0000\u2287\u0338\u0000\u2291\u0338\u0000\u2292\u0338\u0000"d~ -"\u22A2\u0338\u0000\u22A8\u0338\u0000\u22A9\u0338\u0000\u22AB\u0338\u0000\u22B2\u0338\u0000\u22B3\u0338\u0000\u22B4"d~ -"\u0338\u0000\u22B5\u0338\u0000\u2ADD\u0338\u0000\u3008\u0000\u3009\u0000\u3046\u3099\u0000\u304B\u3099\u0000\u304D"d~ -"\u3099\u0000\u304F\u3099\u0000\u3051\u3099\u0000\u3053\u3099\u0000\u3055\u3099\u0000\u3057\u3099\u0000\u3059\u3099"d~ -"\u0000\u305B\u3099\u0000\u305D\u3099\u0000\u305F\u3099\u0000\u3061\u3099\u0000\u3064\u3099\u0000\u3066\u3099\u0000"d~ -"\u3068\u3099\u0000\u306F\u3099\u0000\u306F\u309A\u0000\u3072\u3099\u0000\u3072\u309A\u0000\u3075\u3099\u0000\u3075"d~ -"\u309A\u0000\u3078\u3099\u0000\u3078\u309A\u0000\u307B\u3099\u0000\u307B\u309A\u0000\u309D\u3099\u0000\u30A6\u3099"d~ -"\u0000\u30AB\u3099\u0000\u30AD\u3099\u0000\u30AF\u3099\u0000\u30B1\u3099\u0000\u30B3\u3099\u0000\u30B5\u3099\u0000"d~ -"\u30B7\u3099\u0000\u30B9\u3099\u0000\u30BB\u3099\u0000\u30BD\u3099\u0000\u30BF\u3099\u0000\u30C1\u3099\u0000\u30C4"d~ -"\u3099\u0000\u30C6\u3099\u0000\u30C8\u3099\u0000\u30CF\u3099\u0000\u30CF\u309A\u0000\u30D2\u3099\u0000\u30D2\u309A"d~ -"\u0000\u30D5\u3099\u0000\u30D5\u309A\u0000\u30D8\u3099\u0000\u30D8\u309A\u0000\u30DB\u3099\u0000\u30DB\u309A\u0000"d~ -"\u30EF\u3099\u0000\u30F0\u3099\u0000\u30F1\u3099\u0000\u30F2\u3099\u0000\u30FD\u3099\u0000\u349E\u0000\u34B9\u0000"d~ -"\u34BB\u0000\u34DF\u0000\u3515\u0000\u36EE\u0000\u36FC\u0000\u3781\u0000\u382F\u0000\u3862\u0000\u387C\u0000\u38C7"d~ -"\u0000\u38E3\u0000\u391C\u0000\u393A\u0000\u3A2E\u0000\u3A6C\u0000\u3AE4\u0000\u3B08\u0000\u3B19\u0000\u3B49\u0000"d~ -"\u3B9D\u0000\u3C18\u0000\u3C4E\u0000\u3D33\u0000\u3D96\u0000\u3EAC\u0000\u3EB8\u0000\u3F1B\u0000\u3FFC\u0000\u4008"d~ -"\u0000\u4018\u0000\u4039\u0000\u4046\u0000\u4096\u0000\u40E3\u0000\u412F\u0000\u4202\u0000\u4227\u0000\u42A0\u0000"d~ -"\u4301\u0000\u4334\u0000\u4359\u0000\u43D5\u0000\u43D9\u0000\u440B\u0000\u446B\u0000\u452B\u0000\u455D\u0000\u4561"d~ -"\u0000\u456B\u0000\u45D7\u0000\u45F9\u0000\u4635\u0000\u46BE\u0000\u46C7\u0000\u4995\u0000\u49E6\u0000\u4A6E\u0000"d~ -"\u4A76\u0000\u4AB2\u0000\u4B33\u0000\u4BCE\u0000\u4CCE\u0000\u4CED\u0000\u4CF8\u0000\u4D56\u0000\u4E0D\u0000\u4E26"d~ -"\u0000\u4E32\u0000\u4E38\u0000\u4E39\u0000\u4E3D\u0000\u4E41\u0000\u4E82\u0000\u4E86\u0000\u4EAE\u0000\u4EC0\u0000"d~ -"\u4ECC\u0000\u4EE4\u0000\u4F60\u0000\u4F80\u0000\u4F86\u0000\u4F8B\u0000\u4FAE\u0000\u4FBB\u0000\u4FBF\u0000\u5002"d~ -"\u0000\u502B\u0000\u507A\u0000\u5099\u0000\u50CF\u0000\u50DA\u0000\u50E7\u0000\u5140\u0000\u5145\u0000\u514D\u0000"d~ -"\u5154\u0000\u5164\u0000\u5167\u0000\u5168\u0000\u5169\u0000\u516D\u0000\u5177\u0000\u5180\u0000\u518D\u0000\u5192"d~ -"\u0000\u5195\u0000\u5197\u0000\u51A4\u0000\u51AC\u0000\u51B5\u0000\u51B7\u0000\u51C9\u0000\u51CC\u0000\u51DC\u0000"d~ -"\u51DE\u0000\u51F5\u0000\u5203\u0000\u5207\u0000\u5217\u0000\u5229\u0000\u523A\u0000\u523B\u0000\u5246\u0000\u5272"d~ -"\u0000\u5277\u0000\u5289\u0000\u529B\u0000\u52A3\u0000\u52B3\u0000\u52C7\u0000\u52C9\u0000\u52D2\u0000\u52DE\u0000"d~ -"\u52E4\u0000\u52F5\u0000\u52FA\u0000\u5305\u0000\u5306\u0000\u5317\u0000\u533F\u0000\u5349\u0000\u5351\u0000\u535A"d~ -"\u0000\u5373\u0000\u5375\u0000\u537D\u0000\u537F\u0000\u53C3\u0000\u53CA\u0000\u53DF\u0000\u53E5\u0000\u53EB\u0000"d~ -"\u53F1\u0000\u5406\u0000\u540F\u0000\u541D\u0000\u5438\u0000\u5442\u0000\u5448\u0000\u5468\u0000\u549E\u0000\u54A2"d~ -"\u0000\u54BD\u0000\u54F6\u0000\u5510\u0000\u5553\u0000\u5555\u0000\u5563\u0000\u5584\u0000\u5587\u0000\u5599\u0000"d~ -"\u559D\u0000\u55AB\u0000\u55B3\u0000\u55C0\u0000\u55C2\u0000\u55E2\u0000\u5606\u0000\u5651\u0000\u5668\u0000\u5674"d~ -"\u0000\u56F9\u0000\u5716\u0000\u5717\u0000\u578B\u0000\u57CE\u0000\u57F4\u0000\u580D\u0000\u5831\u0000\u5832\u0000"d~ -"\u5840\u0000\u585A\u0000\u585E\u0000\u58A8\u0000\u58AC\u0000\u58B3\u0000\u58D8\u0000\u58DF\u0000\u58EE\u0000\u58F2"d~ -"\u0000\u58F7\u0000\u5906\u0000\u591A\u0000\u5922\u0000\u5944\u0000\u5948\u0000\u5951\u0000\u5954\u0000\u5962\u0000"d~ -"\u5973\u0000\u59D8\u0000\u59EC\u0000\u5A1B\u0000\u5A27\u0000\u5A62\u0000\u5A66\u0000\u5AB5\u0000\u5B08\u0000\u5B28"d~ -"\u0000\u5B3E\u0000\u5B85\u0000\u5BC3\u0000\u5BD8\u0000\u5BE7\u0000\u5BEE\u0000\u5BF3\u0000\u5BFF\u0000\u5C06\u0000"d~ -"\u5C22\u0000\u5C3F\u0000\u5C60\u0000\u5C62\u0000\u5C64\u0000\u5C65\u0000\u5C6E\u0000\u5C8D\u0000\u5CC0\u0000\u5D19"d~ -"\u0000\u5D43\u0000\u5D50\u0000\u5D6B\u0000\u5D6E\u0000\u5D7C\u0000\u5DB2\u0000\u5DBA\u0000\u5DE1\u0000\u5DE2\u0000"d~ -"\u5DFD\u0000\u5E28\u0000\u5E3D\u0000\u5E69\u0000\u5E74\u0000\u5EA6\u0000\u5EB0\u0000\u5EB3\u0000\u5EB6\u0000\u5EC9"d~ -"\u0000\u5ECA\u0000\u5ED2\u0000\u5ED3\u0000\u5ED9\u0000\u5EEC\u0000\u5EFE\u0000\u5F04\u0000\u5F22\u0000\u5F53\u0000"d~ -"\u5F62\u0000\u5F69\u0000\u5F6B\u0000\u5F8B\u0000\u5F9A\u0000\u5FA9\u0000\u5FAD\u0000\u5FCD\u0000\u5FD7\u0000\u5FF5"d~ -"\u0000\u5FF9\u0000\u6012\u0000\u601C\u0000\u6075\u0000\u6081\u0000\u6094\u0000\u60C7\u0000\u60D8\u0000\u60E1\u0000"d~ -"\u6108\u0000\u6144\u0000\u6148\u0000\u614C\u0000\u614E\u0000\u6160\u0000\u6168\u0000\u617A\u0000\u618E\u0000\u6190"d~ -"\u0000\u61A4\u0000\u61AF\u0000\u61B2\u0000\u61DE\u0000\u61F2\u0000\u61F6\u0000\u6200\u0000\u6210\u0000\u621B\u0000"d~ -"\u622E\u0000\u6234\u0000\u625D\u0000\u62B1\u0000\u62C9\u0000\u62CF\u0000\u62D3\u0000\u62D4\u0000\u62FC\u0000\u62FE"d~ -"\u0000\u633D\u0000\u6350\u0000\u6368\u0000\u637B\u0000\u6383\u0000\u63A0\u0000\u63A9\u0000\u63C4\u0000\u63C5\u0000"d~ -"\u63E4\u0000\u641C\u0000\u6422\u0000\u6452\u0000\u6469\u0000\u6477\u0000\u647E\u0000\u649A\u0000\u649D\u0000\u64C4"d~ -"\u0000\u654F\u0000\u6556\u0000\u656C\u0000\u6578\u0000\u6599\u0000\u65C5\u0000\u65E2\u0000\u65E3\u0000\u6613\u0000"d~ -"\u6649\u0000\u6674\u0000\u6688\u0000\u6691\u0000\u669C\u0000\u66B4\u0000\u66C6\u0000\u66F4\u0000\u66F8\u0000\u6700"d~ -"\u0000\u6717\u0000\u671B\u0000\u6721\u0000\u674E\u0000\u6753\u0000\u6756\u0000\u675E\u0000\u677B\u0000\u6785\u0000"d~ -"\u6797\u0000\u67F3\u0000\u67FA\u0000\u6817\u0000\u681F\u0000\u6852\u0000\u6881\u0000\u6885\u0000\u688E\u0000\u68A8"d~ -"\u0000\u6914\u0000\u6942\u0000\u69A3\u0000\u69EA\u0000\u6A02\u0000\u6A13\u0000\u6AA8\u0000\u6AD3\u0000\u6ADB\u0000"d~ -"\u6B04\u0000\u6B21\u0000\u6B54\u0000\u6B72\u0000\u6B77\u0000\u6B79\u0000\u6B9F\u0000\u6BAE\u0000\u6BBA\u0000\u6BBB"d~ -"\u0000\u6C4E\u0000\u6C67\u0000\u6C88\u0000\u6CBF\u0000\u6CCC\u0000\u6CCD\u0000\u6CE5\u0000\u6D16\u0000\u6D1B\u0000"d~ -"\u6D1E\u0000\u6D34\u0000\u6D3E\u0000\u6D41\u0000\u6D69\u0000\u6D6A\u0000\u6D77\u0000\u6D78\u0000\u6D85\u0000\u6DCB"d~ -"\u0000\u6DDA\u0000\u6DEA\u0000\u6DF9\u0000\u6E1A\u0000\u6E2F\u0000\u6E6E\u0000\u6E9C\u0000\u6EBA\u0000\u6EC7\u0000"d~ -"\u6ECB\u0000\u6ED1\u0000\u6EDB\u0000\u6F0F\u0000\u6F22\u0000\u6F23\u0000\u6F6E\u0000\u6FC6\u0000\u6FEB\u0000\u6FFE"d~ -"\u0000\u701B\u0000\u701E\u0000\u7039\u0000\u704A\u0000\u7070\u0000\u7077\u0000\u707D\u0000\u7099\u0000\u70AD\u0000"d~ -"\u70C8\u0000\u70D9\u0000\u7145\u0000\u7149\u0000\u716E\u0000\u719C\u0000\u71CE\u0000\u71D0\u0000\u7210\u0000\u721B"d~ -"\u0000\u7228\u0000\u722B\u0000\u7235\u0000\u7250\u0000\u7262\u0000\u7280\u0000\u7295\u0000\u72AF\u0000\u72C0\u0000"d~ -"\u72FC\u0000\u732A\u0000\u7375\u0000\u737A\u0000\u7387\u0000\u738B\u0000\u73A5\u0000\u73B2\u0000\u73DE\u0000\u7406"d~ -"\u0000\u7409\u0000\u7422\u0000\u7447\u0000\u745C\u0000\u7469\u0000\u7471\u0000\u7485\u0000\u7489\u0000\u7498\u0000"d~ -"\u74CA\u0000\u7506\u0000\u7524\u0000\u753B\u0000\u753E\u0000\u7559\u0000\u7565\u0000\u7570\u0000\u75E2\u0000\u7610"d~ -"\u0000\u761D\u0000\u761F\u0000\u7642\u0000\u7669\u0000\u76CA\u0000\u76DB\u0000\u76E7\u0000\u76F4\u0000\u7701\u0000"d~ -"\u771E\u0000\u771F\u0000\u7740\u0000\u774A\u0000\u778B\u0000\u77A7\u0000\u784E\u0000\u786B\u0000\u788C\u0000\u7891"d~ -"\u0000\u78CA\u0000\u78CC\u0000\u78FB\u0000\u792A\u0000\u793C\u0000\u793E\u0000\u7948\u0000\u7949\u0000\u7950\u0000"d~ -"\u7956\u0000\u795D\u0000\u795E\u0000\u7965\u0000\u797F\u0000\u798D\u0000\u798E\u0000\u798F\u0000\u79AE\u0000\u79CA"d~ -"\u0000\u79EB\u0000\u7A1C\u0000\u7A40\u0000\u7A4A\u0000\u7A4F\u0000\u7A81\u0000\u7AB1\u0000\u7ACB\u0000\u7AEE\u0000"d~ -"\u7B20\u0000\u7BC0\u0000\u7BC6\u0000\u7BC9\u0000\u7C3E\u0000\u7C60\u0000\u7C7B\u0000\u7C92\u0000\u7CBE\u0000\u7CD2"d~ -"\u0000\u7CD6\u0000\u7CE3\u0000\u7CE7\u0000\u7CE8\u0000\u7D00\u0000\u7D10\u0000\u7D22\u0000\u7D2F\u0000\u7D5B\u0000"d~ -"\u7D63\u0000\u7DA0\u0000\u7DBE\u0000\u7DC7\u0000\u7DF4\u0000\u7E02\u0000\u7E09\u0000\u7E37\u0000\u7E41\u0000\u7E45"d~ -"\u0000\u7F3E\u0000\u7F72\u0000\u7F79\u0000\u7F7A\u0000\u7F85\u0000\u7F95\u0000\u7F9A\u0000\u7FBD\u0000\u7FFA\u0000"d~ -"\u8001\u0000\u8005\u0000\u8046\u0000\u8060\u0000\u806F\u0000\u8070\u0000\u807E\u0000\u808B\u0000\u80AD\u0000\u80B2"d~ -"\u0000\u8103\u0000\u813E\u0000\u81D8\u0000\u81E8\u0000\u81ED\u0000\u8201\u0000\u8204\u0000\u8218\u0000\u826F\u0000"d~ -"\u8279\u0000\u828B\u0000\u8291\u0000\u829D\u0000\u82B1\u0000\u82B3\u0000\u82BD\u0000\u82E5\u0000\u82E6\u0000\u831D"d~ -"\u0000\u8323\u0000\u8336\u0000\u8352\u0000\u8353\u0000\u8363\u0000\u83AD\u0000\u83BD\u0000\u83C9\u0000\u83CA\u0000"d~ -"\u83CC\u0000\u83DC\u0000\u83E7\u0000\u83EF\u0000\u83F1\u0000\u843D\u0000\u8449\u0000\u8457\u0000\u84EE\u0000\u84F1"d~ -"\u0000\u84F3\u0000\u84FC\u0000\u8516\u0000\u8564\u0000\u85CD\u0000\u85FA\u0000\u8606\u0000\u8612\u0000\u862D\u0000"d~ -"\u863F\u0000\u8650\u0000\u865C\u0000\u8667\u0000\u8669\u0000\u8688\u0000\u86A9\u0000\u86E2\u0000\u870E\u0000\u8728"d~ -"\u0000\u876B\u0000\u8779\u0000\u8786\u0000\u87BA\u0000\u87E1\u0000\u8801\u0000\u881F\u0000\u884C\u0000\u8860\u0000"d~ -"\u8863\u0000\u88C2\u0000\u88CF\u0000\u88D7\u0000\u88DE\u0000\u88E1\u0000\u88F8\u0000\u88FA\u0000\u8910\u0000\u8941"d~ -"\u0000\u8964\u0000\u8986\u0000\u898B\u0000\u8996\u0000\u8AA0\u0000\u8AAA\u0000\u8ABF\u0000\u8ACB\u0000\u8AD2\u0000"d~ -"\u8AD6\u0000\u8AED\u0000\u8AF8\u0000\u8AFE\u0000\u8B01\u0000\u8B39\u0000\u8B58\u0000\u8B80\u0000\u8B8A\u0000\u8C48"d~ -"\u0000\u8C55\u0000\u8CAB\u0000\u8CC1\u0000\u8CC2\u0000\u8CC8\u0000\u8CD3\u0000\u8D08\u0000\u8D1B\u0000\u8D77\u0000"d~ -"\u8DBC\u0000\u8DCB\u0000\u8DEF\u0000\u8DF0\u0000\u8ECA\u0000\u8ED4\u0000\u8F26\u0000\u8F2A\u0000\u8F38\u0000\u8F3B"d~ -"\u0000\u8F62\u0000\u8F9E\u0000\u8FB0\u0000\u8FB6\u0000\u9023\u0000\u9038\u0000\u9072\u0000\u907C\u0000\u908F\u0000"d~ -"\u9094\u0000\u90CE\u0000\u90DE\u0000\u90F1\u0000\u90FD\u0000\u9111\u0000\u911B\u0000\u916A\u0000\u9199\u0000\u91B4"d~ -"\u0000\u91CC\u0000\u91CF\u0000\u91D1\u0000\u9234\u0000\u9238\u0000\u9276\u0000\u927C\u0000\u92D7\u0000\u92D8\u0000"d~ -"\u9304\u0000\u934A\u0000\u93F9\u0000\u9415\u0000\u958B\u0000\u95AD\u0000\u95B7\u0000\u962E\u0000\u964B\u0000\u964D"d~ -"\u0000\u9675\u0000\u9678\u0000\u967C\u0000\u9686\u0000\u96A3\u0000\u96B7\u0000\u96B8\u0000\u96C3\u0000\u96E2\u0000"d~ -"\u96E3\u0000\u96F6\u0000\u96F7\u0000\u9723\u0000\u9732\u0000\u9748\u0000\u9756\u0000\u97DB\u0000\u97E0\u0000\u97FF"d~ -"\u0000\u980B\u0000\u9818\u0000\u9829\u0000\u983B\u0000\u985E\u0000\u98E2\u0000\u98EF\u0000\u98FC\u0000\u9928\u0000"d~ -"\u9929\u0000\u99A7\u0000\u99C2\u0000\u99F1\u0000\u99FE\u0000\u9A6A\u0000\u9B12\u0000\u9B6F\u0000\u9C40\u0000\u9C57"d~ -"\u0000\u9CFD\u0000\u9D67\u0000\u9DB4\u0000\u9DFA\u0000\u9E1E\u0000\u9E7F\u0000\u9E97\u0000\u9E9F\u0000\u9EBB\u0000"d~ -"\u9ECE\u0000\u9EF9\u0000\u9EFE\u0000\u9F05\u0000\u9F0F\u0000\u9F16\u0000\u9F3B\u0000\u9F43\u0000\u9F8D\u0000\u9F8E"d~ -"\u0000\u9F9C\u0000\U00011099\U000110BA\u0000\U0001109B\U000110BA\u0000\U000110A5\U000110BA\u0000\U00011131\U00011127"d~ -"\u0000\U00011132\U00011127\u0000\U00011347\U0001133E\u0000\U00011347\U00011357\u0000\U000114B9\U000114B0\u0000"d~ -"\U000114B9\U000114BA\u0000\U000114B9\U000114BD\u0000\U000115B8\U000115AF\u0000\U000115B9\U000115AF\u0000\U00011935"d~ -"\U00011930\u0000\U0001D157\U0001D165\u0000\U0001D158\U0001D165\u0000\U0001D158\U0001D165\U0001D16E\u0000\U0001D158"d~ -"\U0001D165\U0001D16F\u0000\U0001D158\U0001D165\U0001D170\u0000\U0001D158\U0001D165\U0001D171\u0000\U0001D158\U0001D165"d~ -"\U0001D172\u0000\U0001D1B9\U0001D165\u0000\U0001D1B9\U0001D165\U0001D16E\u0000\U0001D1B9\U0001D165\U0001D16F\u0000"d~ -"\U0001D1BA\U0001D165\u0000\U0001D1BA\U0001D165\U0001D16E\u0000\U0001D1BA\U0001D165\U0001D16F\u0000\U00020122\u0000"d~ -"\U0002051C\u0000\U00020525\u0000\U0002054B\u0000\U0002063A\u0000\U00020804\u0000\U000208DE\u0000\U00020A2C\u0000"d~ -"\U00020B63\u0000\U000214E4\u0000\U000216A8\u0000\U000216EA\u0000\U000219C8\u0000\U00021B18\u0000\U00021D0B\u0000"d~ -"\U00021DE4\u0000\U00021DE6\u0000\U00022183\u0000\U0002219F\u0000\U00022331\u0000\U000226D4\u0000\U00022844\u0000"d~ -"\U0002284A\u0000\U00022B0C\u0000\U00022BF1\u0000\U0002300A\u0000\U000232B8\u0000\U0002335F\u0000\U00023393\u0000"d~ -"\U0002339C\u0000\U000233C3\u0000\U000233D5\u0000\U0002346D\u0000\U000236A3\u0000\U000238A7\u0000\U00023A8D\u0000"d~ -"\U00023AFA\u0000\U00023CBC\u0000\U00023D1E\u0000\U00023ED1\u0000\U00023F5E\u0000\U00023F8E\u0000\U00024263\u0000"d~ -"\U000242EE\u0000\U000243AB\u0000\U00024608\u0000\U00024735\u0000\U00024814\u0000\U00024C36\u0000\U00024C92\u0000"d~ -"\U00024FA1\u0000\U00024FB8\u0000\U00025044\u0000\U000250F2\u0000\U000250F3\u0000\U00025119\u0000\U00025133\u0000"d~ -"\U00025249\u0000\U0002541D\u0000\U00025626\u0000\U0002569A\u0000\U000256C5\u0000\U0002597C\u0000\U00025AA7\u0000"d~ -"\U00025BAB\u0000\U00025C80\u0000\U00025CD0\u0000\U00025F86\u0000\U000261DA\u0000\U00026228\u0000\U00026247\u0000"d~ -"\U000262D9\u0000\U0002633E\u0000\U000264DA\u0000\U00026523\u0000\U000265A8\u0000\U000267A7\u0000\U000267B5\u0000"d~ -"\U00026B3C\u0000\U00026C36\u0000\U00026CD5\u0000\U00026D6B\u0000\U00026F2C\u0000\U00026FB1\u0000\U000270D2\u0000"d~ -"\U000273CA\u0000\U00027667\u0000\U000278AE\u0000\U00027966\u0000\U00027CA8\u0000\U00027ED3\u0000\U00027F2F\u0000"d~ -"\U000285D2\u0000\U000285ED\u0000\U0002872E\u0000\U00028BFA\u0000\U00028D77\u0000\U00029145\u0000\U000291DF\u0000"d~ -"\U0002921A\u0000\U0002940A\u0000\U00029496\u0000\U000295B6\u0000\U00029B30\u0000\U0002A0CE\u0000\U0002A105\u0000"d~ -"\U0002A20E\u0000\U0002A291\u0000\U0002A392\u0000\U0002A600\u0000"d; -return t[]; -} -dstring decompCompatTable() nothrow @nogc pure @safe { -static immutable dchar[10425] t = -"\u0000\u0020\u0000\u0020\u0301\u0000\u0020\u0303\u0000\u0020\u0304\u0000\u0020\u0305\u0000\u0020\u0306\u0000\u0020"d~ -"\u0307\u0000\u0020\u0308\u0000\u0020\u0308\u0300\u0000\u0020\u0308\u0301\u0000\u0020\u0308\u0342\u0000\u0020\u030A"d~ -"\u0000\u0020\u030B\u0000\u0020\u0313\u0000\u0020\u0313\u0300\u0000\u0020\u0313\u0301\u0000\u0020\u0313\u0342\u0000"d~ -"\u0020\u0314\u0000\u0020\u0314\u0300\u0000\u0020\u0314\u0301\u0000\u0020\u0314\u0342\u0000\u0020\u0327\u0000\u0020"d~ -"\u0328\u0000\u0020\u0333\u0000\u0020\u0342\u0000\u0020\u0345\u0000\u0020\u064B\u0000\u0020\u064C\u0000\u0020\u064C"d~ -"\u0651\u0000\u0020\u064D\u0000\u0020\u064D\u0651\u0000\u0020\u064E\u0000\u0020\u064E\u0651\u0000\u0020\u064F\u0000"d~ -"\u0020\u064F\u0651\u0000\u0020\u0650\u0000\u0020\u0650\u0651\u0000\u0020\u0651\u0000\u0020\u0651\u0670\u0000\u0020"d~ -"\u0652\u0000\u0020\u3099\u0000\u0020\u309A\u0000\u0021\u0000\u0021\u0021\u0000\u0021\u003F\u0000\u0022\u0000\u0023"d~ -"\u0000\u0024\u0000\u0025\u0000\u0026\u0000\u0027\u0000\u0028\u0000\u0028\u0031\u0029\u0000\u0028\u0031\u0030\u0029"d~ -"\u0000\u0028\u0031\u0031\u0029\u0000\u0028\u0031\u0032\u0029\u0000\u0028\u0031\u0033\u0029\u0000\u0028\u0031\u0034"d~ -"\u0029\u0000\u0028\u0031\u0035\u0029\u0000\u0028\u0031\u0036\u0029\u0000\u0028\u0031\u0037\u0029\u0000\u0028\u0031"d~ -"\u0038\u0029\u0000\u0028\u0031\u0039\u0029\u0000\u0028\u0032\u0029\u0000\u0028\u0032\u0030\u0029\u0000\u0028\u0033"d~ -"\u0029\u0000\u0028\u0034\u0029\u0000\u0028\u0035\u0029\u0000\u0028\u0036\u0029\u0000\u0028\u0037\u0029\u0000\u0028"d~ -"\u0038\u0029\u0000\u0028\u0039\u0029\u0000\u0028\u0041\u0029\u0000\u0028\u0042\u0029\u0000\u0028\u0043\u0029\u0000"d~ -"\u0028\u0044\u0029\u0000\u0028\u0045\u0029\u0000\u0028\u0046\u0029\u0000\u0028\u0047\u0029\u0000\u0028\u0048\u0029"d~ -"\u0000\u0028\u0049\u0029\u0000\u0028\u004A\u0029\u0000\u0028\u004B\u0029\u0000\u0028\u004C\u0029\u0000\u0028\u004D"d~ -"\u0029\u0000\u0028\u004E\u0029\u0000\u0028\u004F\u0029\u0000\u0028\u0050\u0029\u0000\u0028\u0051\u0029\u0000\u0028"d~ -"\u0052\u0029\u0000\u0028\u0053\u0029\u0000\u0028\u0054\u0029\u0000\u0028\u0055\u0029\u0000\u0028\u0056\u0029\u0000"d~ -"\u0028\u0057\u0029\u0000\u0028\u0058\u0029\u0000\u0028\u0059\u0029\u0000\u0028\u005A\u0029\u0000\u0028\u0061\u0029"d~ -"\u0000\u0028\u0062\u0029\u0000\u0028\u0063\u0029\u0000\u0028\u0064\u0029\u0000\u0028\u0065\u0029\u0000\u0028\u0066"d~ -"\u0029\u0000\u0028\u0067\u0029\u0000\u0028\u0068\u0029\u0000\u0028\u0069\u0029\u0000\u0028\u006A\u0029\u0000\u0028"d~ -"\u006B\u0029\u0000\u0028\u006C\u0029\u0000\u0028\u006D\u0029\u0000\u0028\u006E\u0029\u0000\u0028\u006F\u0029\u0000"d~ -"\u0028\u0070\u0029\u0000\u0028\u0071\u0029\u0000\u0028\u0072\u0029\u0000\u0028\u0073\u0029\u0000\u0028\u0074\u0029"d~ -"\u0000\u0028\u0075\u0029\u0000\u0028\u0076\u0029\u0000\u0028\u0077\u0029\u0000\u0028\u0078\u0029\u0000\u0028\u0079"d~ -"\u0029\u0000\u0028\u007A\u0029\u0000\u0028\u1100\u0029\u0000\u0028\u1100\u1161\u0029\u0000\u0028\u1102\u0029\u0000"d~ -"\u0028\u1102\u1161\u0029\u0000\u0028\u1103\u0029\u0000\u0028\u1103\u1161\u0029\u0000\u0028\u1105\u0029\u0000\u0028"d~ -"\u1105\u1161\u0029\u0000\u0028\u1106\u0029\u0000\u0028\u1106\u1161\u0029\u0000\u0028\u1107\u0029\u0000\u0028\u1107"d~ -"\u1161\u0029\u0000\u0028\u1109\u0029\u0000\u0028\u1109\u1161\u0029\u0000\u0028\u110B\u0029\u0000\u0028\u110B\u1161"d~ -"\u0029\u0000\u0028\u110B\u1169\u110C\u1165\u11AB\u0029\u0000\u0028\u110B\u1169\u1112\u116E\u0029\u0000\u0028\u110C"d~ -"\u0029\u0000\u0028\u110C\u1161\u0029\u0000\u0028\u110C\u116E\u0029\u0000\u0028\u110E\u0029\u0000\u0028\u110E\u1161"d~ -"\u0029\u0000\u0028\u110F\u0029\u0000\u0028\u110F\u1161\u0029\u0000\u0028\u1110\u0029\u0000\u0028\u1110\u1161\u0029"d~ -"\u0000\u0028\u1111\u0029\u0000\u0028\u1111\u1161\u0029\u0000\u0028\u1112\u0029\u0000\u0028\u1112\u1161\u0029\u0000"d~ -"\u0028\u4E00\u0029\u0000\u0028\u4E03\u0029\u0000\u0028\u4E09\u0029\u0000\u0028\u4E5D\u0029\u0000\u0028\u4E8C\u0029"d~ -"\u0000\u0028\u4E94\u0029\u0000\u0028\u4EE3\u0029\u0000\u0028\u4F01\u0029\u0000\u0028\u4F11\u0029\u0000\u0028\u516B"d~ -"\u0029\u0000\u0028\u516D\u0029\u0000\u0028\u52B4\u0029\u0000\u0028\u5341\u0029\u0000\u0028\u5354\u0029\u0000\u0028"d~ -"\u540D\u0029\u0000\u0028\u547C\u0029\u0000\u0028\u56DB\u0029\u0000\u0028\u571F\u0029\u0000\u0028\u5B66\u0029\u0000"d~ -"\u0028\u65E5\u0029\u0000\u0028\u6708\u0029\u0000\u0028\u6709\u0029\u0000\u0028\u6728\u0029\u0000\u0028\u682A\u0029"d~ -"\u0000\u0028\u6C34\u0029\u0000\u0028\u706B\u0029\u0000\u0028\u7279\u0029\u0000\u0028\u76E3\u0029\u0000\u0028\u793E"d~ -"\u0029\u0000\u0028\u795D\u0029\u0000\u0028\u796D\u0029\u0000\u0028\u81EA\u0029\u0000\u0028\u81F3\u0029\u0000\u0028"d~ -"\u8CA1\u0029\u0000\u0028\u8CC7\u0029\u0000\u0028\u91D1\u0029\u0000\u0029\u0000\u002A\u0000\u002B\u0000\u002C\u0000"d~ -"\u002D\u0000\u002E\u0000\u002E\u002E\u0000\u002E\u002E\u002E\u0000\u002F\u0000\u0030\u0000\u0030\u002C\u0000\u0030"d~ -"\u002E\u0000\u0030\u2044\u0033\u0000\u0030\u70B9\u0000\u0031\u0000\u0031\u002C\u0000\u0031\u002E\u0000\u0031\u0030"d~ -"\u0000\u0031\u0030\u002E\u0000\u0031\u0030\u65E5\u0000\u0031\u0030\u6708\u0000\u0031\u0030\u70B9\u0000\u0031\u0031"d~ -"\u0000\u0031\u0031\u002E\u0000\u0031\u0031\u65E5\u0000\u0031\u0031\u6708\u0000\u0031\u0031\u70B9\u0000\u0031\u0032"d~ -"\u0000\u0031\u0032\u002E\u0000\u0031\u0032\u65E5\u0000\u0031\u0032\u6708\u0000\u0031\u0032\u70B9\u0000\u0031\u0033"d~ -"\u0000\u0031\u0033\u002E\u0000\u0031\u0033\u65E5\u0000\u0031\u0033\u70B9\u0000\u0031\u0034\u0000\u0031\u0034\u002E"d~ -"\u0000\u0031\u0034\u65E5\u0000\u0031\u0034\u70B9\u0000\u0031\u0035\u0000\u0031\u0035\u002E\u0000\u0031\u0035\u65E5"d~ -"\u0000\u0031\u0035\u70B9\u0000\u0031\u0036\u0000\u0031\u0036\u002E\u0000\u0031\u0036\u65E5\u0000\u0031\u0036\u70B9"d~ -"\u0000\u0031\u0037\u0000\u0031\u0037\u002E\u0000\u0031\u0037\u65E5\u0000\u0031\u0037\u70B9\u0000\u0031\u0038\u0000"d~ -"\u0031\u0038\u002E\u0000\u0031\u0038\u65E5\u0000\u0031\u0038\u70B9\u0000\u0031\u0039\u0000\u0031\u0039\u002E\u0000"d~ -"\u0031\u0039\u65E5\u0000\u0031\u0039\u70B9\u0000\u0031\u2044\u0000\u0031\u2044\u0031\u0030\u0000\u0031\u2044\u0032"d~ -"\u0000\u0031\u2044\u0033\u0000\u0031\u2044\u0034\u0000\u0031\u2044\u0035\u0000\u0031\u2044\u0036\u0000\u0031\u2044"d~ -"\u0037\u0000\u0031\u2044\u0038\u0000\u0031\u2044\u0039\u0000\u0031\u65E5\u0000\u0031\u6708\u0000\u0031\u70B9\u0000"d~ -"\u0032\u0000\u0032\u002C\u0000\u0032\u002E\u0000\u0032\u0030\u0000\u0032\u0030\u002E\u0000\u0032\u0030\u65E5\u0000"d~ -"\u0032\u0030\u70B9\u0000\u0032\u0031\u0000\u0032\u0031\u65E5\u0000\u0032\u0031\u70B9\u0000\u0032\u0032\u0000\u0032"d~ -"\u0032\u65E5\u0000\u0032\u0032\u70B9\u0000\u0032\u0033\u0000\u0032\u0033\u65E5\u0000\u0032\u0033\u70B9\u0000\u0032"d~ -"\u0034\u0000\u0032\u0034\u65E5\u0000\u0032\u0034\u70B9\u0000\u0032\u0035\u0000\u0032\u0035\u65E5\u0000\u0032\u0036"d~ -"\u0000\u0032\u0036\u65E5\u0000\u0032\u0037\u0000\u0032\u0037\u65E5\u0000\u0032\u0038\u0000\u0032\u0038\u65E5\u0000"d~ -"\u0032\u0039\u0000\u0032\u0039\u65E5\u0000\u0032\u2044\u0033\u0000\u0032\u2044\u0035\u0000\u0032\u65E5\u0000\u0032"d~ -"\u6708\u0000\u0032\u70B9\u0000\u0033\u0000\u0033\u002C\u0000\u0033\u002E\u0000\u0033\u0030\u0000\u0033\u0030\u65E5"d~ -"\u0000\u0033\u0031\u0000\u0033\u0031\u65E5\u0000\u0033\u0032\u0000\u0033\u0033\u0000\u0033\u0034\u0000\u0033\u0035"d~ -"\u0000\u0033\u0036\u0000\u0033\u0037\u0000\u0033\u0038\u0000\u0033\u0039\u0000\u0033\u2044\u0034\u0000\u0033\u2044"d~ -"\u0035\u0000\u0033\u2044\u0038\u0000\u0033\u65E5\u0000\u0033\u6708\u0000\u0033\u70B9\u0000\u0034\u0000\u0034\u002C"d~ -"\u0000\u0034\u002E\u0000\u0034\u0030\u0000\u0034\u0031\u0000\u0034\u0032\u0000\u0034\u0033\u0000\u0034\u0034\u0000"d~ -"\u0034\u0035\u0000\u0034\u0036\u0000\u0034\u0037\u0000\u0034\u0038\u0000\u0034\u0039\u0000\u0034\u2044\u0035\u0000"d~ -"\u0034\u65E5\u0000\u0034\u6708\u0000\u0034\u70B9\u0000\u0035\u0000\u0035\u002C\u0000\u0035\u002E\u0000\u0035\u0030"d~ -"\u0000\u0035\u2044\u0036\u0000\u0035\u2044\u0038\u0000\u0035\u65E5\u0000\u0035\u6708\u0000\u0035\u70B9\u0000\u0036"d~ -"\u0000\u0036\u002C\u0000\u0036\u002E\u0000\u0036\u65E5\u0000\u0036\u6708\u0000\u0036\u70B9\u0000\u0037\u0000\u0037"d~ -"\u002C\u0000\u0037\u002E\u0000\u0037\u2044\u0038\u0000\u0037\u65E5\u0000\u0037\u6708\u0000\u0037\u70B9\u0000\u0038"d~ -"\u0000\u0038\u002C\u0000\u0038\u002E\u0000\u0038\u65E5\u0000\u0038\u6708\u0000\u0038\u70B9\u0000\u0039\u0000\u0039"d~ -"\u002C\u0000\u0039\u002E\u0000\u0039\u65E5\u0000\u0039\u6708\u0000\u0039\u70B9\u0000\u003A\u0000\u003A\u003A\u003D"d~ -"\u0000\u003B\u0000\u003C\u0000\u003C\u0338\u0000\u003D\u0000\u003D\u003D\u0000\u003D\u003D\u003D\u0000\u003D\u0338"d~ -"\u0000\u003E\u0000\u003E\u0338\u0000\u003F\u0000\u003F\u0021\u0000\u003F\u003F\u0000\u0040\u0000\u0041\u0000\u0041"d~ -"\u0055\u0000\u0041\u0300\u0000\u0041\u0301\u0000\u0041\u0302\u0000\u0041\u0302\u0300\u0000\u0041\u0302\u0301\u0000"d~ -"\u0041\u0302\u0303\u0000\u0041\u0302\u0309\u0000\u0041\u0303\u0000\u0041\u0304\u0000\u0041\u0306\u0000\u0041\u0306"d~ -"\u0300\u0000\u0041\u0306\u0301\u0000\u0041\u0306\u0303\u0000\u0041\u0306\u0309\u0000\u0041\u0307\u0000\u0041\u0307"d~ -"\u0304\u0000\u0041\u0308\u0000\u0041\u0308\u0304\u0000\u0041\u0309\u0000\u0041\u030A\u0000\u0041\u030A\u0301\u0000"d~ -"\u0041\u030C\u0000\u0041\u030F\u0000\u0041\u0311\u0000\u0041\u0323\u0000\u0041\u0323\u0302\u0000\u0041\u0323\u0306"d~ -"\u0000\u0041\u0325\u0000\u0041\u0328\u0000\u0041\u2215\u006D\u0000\u0042\u0000\u0042\u0071\u0000\u0042\u0307\u0000"d~ -"\u0042\u0323\u0000\u0042\u0331\u0000\u0043\u0000\u0043\u0044\u0000\u0043\u006F\u002E\u0000\u0043\u0301\u0000\u0043"d~ -"\u0302\u0000\u0043\u0307\u0000\u0043\u030C\u0000\u0043\u0327\u0000\u0043\u0327\u0301\u0000\u0043\u2215\u006B\u0067"d~ -"\u0000\u0044\u0000\u0044\u004A\u0000\u0044\u005A\u0000\u0044\u005A\u030C\u0000\u0044\u007A\u0000\u0044\u007A\u030C"d~ -"\u0000\u0044\u0307\u0000\u0044\u030C\u0000\u0044\u0323\u0000\u0044\u0327\u0000\u0044\u032D\u0000\u0044\u0331\u0000"d~ -"\u0045\u0000\u0045\u0300\u0000\u0045\u0301\u0000\u0045\u0302\u0000\u0045\u0302\u0300\u0000\u0045\u0302\u0301\u0000"d~ -"\u0045\u0302\u0303\u0000\u0045\u0302\u0309\u0000\u0045\u0303\u0000\u0045\u0304\u0000\u0045\u0304\u0300\u0000\u0045"d~ -"\u0304\u0301\u0000\u0045\u0306\u0000\u0045\u0307\u0000\u0045\u0308\u0000\u0045\u0309\u0000\u0045\u030C\u0000\u0045"d~ -"\u030F\u0000\u0045\u0311\u0000\u0045\u0323\u0000\u0045\u0323\u0302\u0000\u0045\u0327\u0000\u0045\u0327\u0306\u0000"d~ -"\u0045\u0328\u0000\u0045\u032D\u0000\u0045\u0330\u0000\u0046\u0000\u0046\u0041\u0058\u0000\u0046\u0307\u0000\u0047"d~ -"\u0000\u0047\u0042\u0000\u0047\u0048\u007A\u0000\u0047\u0050\u0061\u0000\u0047\u0079\u0000\u0047\u0301\u0000\u0047"d~ -"\u0302\u0000\u0047\u0304\u0000\u0047\u0306\u0000\u0047\u0307\u0000\u0047\u030C\u0000\u0047\u0327\u0000\u0048\u0000"d~ -"\u0048\u0050\u0000\u0048\u0056\u0000\u0048\u0067\u0000\u0048\u007A\u0000\u0048\u0302\u0000\u0048\u0307\u0000\u0048"d~ -"\u0308\u0000\u0048\u030C\u0000\u0048\u0323\u0000\u0048\u0327\u0000\u0048\u032E\u0000\u0049\u0000\u0049\u0049\u0000"d~ -"\u0049\u0049\u0049\u0000\u0049\u004A\u0000\u0049\u0055\u0000\u0049\u0056\u0000\u0049\u0058\u0000\u0049\u0300\u0000"d~ -"\u0049\u0301\u0000\u0049\u0302\u0000\u0049\u0303\u0000\u0049\u0304\u0000\u0049\u0306\u0000\u0049\u0307\u0000\u0049"d~ -"\u0308\u0000\u0049\u0308\u0301\u0000\u0049\u0309\u0000\u0049\u030C\u0000\u0049\u030F\u0000\u0049\u0311\u0000\u0049"d~ -"\u0323\u0000\u0049\u0328\u0000\u0049\u0330\u0000\u004A\u0000\u004A\u0302\u0000\u004B\u0000\u004B\u0042\u0000\u004B"d~ -"\u004B\u0000\u004B\u004D\u0000\u004B\u0301\u0000\u004B\u030C\u0000\u004B\u0323\u0000\u004B\u0327\u0000\u004B\u0331"d~ -"\u0000\u004C\u0000\u004C\u004A\u0000\u004C\u0054\u0044\u0000\u004C\u006A\u0000\u004C\u00B7\u0000\u004C\u0301\u0000"d~ -"\u004C\u030C\u0000\u004C\u0323\u0000\u004C\u0323\u0304\u0000\u004C\u0327\u0000\u004C\u032D\u0000\u004C\u0331\u0000"d~ -"\u004D\u0000\u004D\u0042\u0000\u004D\u0043\u0000\u004D\u0044\u0000\u004D\u0048\u007A\u0000\u004D\u0050\u0061\u0000"d~ -"\u004D\u0052\u0000\u004D\u0056\u0000\u004D\u0057\u0000\u004D\u0301\u0000\u004D\u0307\u0000\u004D\u0323\u0000\u004D"d~ -"\u03A9\u0000\u004E\u0000\u004E\u004A\u0000\u004E\u006A\u0000\u004E\u006F\u0000\u004E\u0300\u0000\u004E\u0301\u0000"d~ -"\u004E\u0303\u0000\u004E\u0307\u0000\u004E\u030C\u0000\u004E\u0323\u0000\u004E\u0327\u0000\u004E\u032D\u0000\u004E"d~ -"\u0331\u0000\u004F\u0000\u004F\u0300\u0000\u004F\u0301\u0000\u004F\u0302\u0000\u004F\u0302\u0300\u0000\u004F\u0302"d~ -"\u0301\u0000\u004F\u0302\u0303\u0000\u004F\u0302\u0309\u0000\u004F\u0303\u0000\u004F\u0303\u0301\u0000\u004F\u0303"d~ -"\u0304\u0000\u004F\u0303\u0308\u0000\u004F\u0304\u0000\u004F\u0304\u0300\u0000\u004F\u0304\u0301\u0000\u004F\u0306"d~ -"\u0000\u004F\u0307\u0000\u004F\u0307\u0304\u0000\u004F\u0308\u0000\u004F\u0308\u0304\u0000\u004F\u0309\u0000\u004F"d~ -"\u030B\u0000\u004F\u030C\u0000\u004F\u030F\u0000\u004F\u0311\u0000\u004F\u031B\u0000\u004F\u031B\u0300\u0000\u004F"d~ -"\u031B\u0301\u0000\u004F\u031B\u0303\u0000\u004F\u031B\u0309\u0000\u004F\u031B\u0323\u0000\u004F\u0323\u0000\u004F"d~ -"\u0323\u0302\u0000\u004F\u0328\u0000\u004F\u0328\u0304\u0000\u0050\u0000\u0050\u0048\u0000\u0050\u0050\u004D\u0000"d~ -"\u0050\u0050\u0056\u0000\u0050\u0052\u0000\u0050\u0054\u0045\u0000\u0050\u0061\u0000\u0050\u0301\u0000\u0050\u0307"d~ -"\u0000\u0051\u0000\u0052\u0000\u0052\u0073\u0000\u0052\u0301\u0000\u0052\u0307\u0000\u0052\u030C\u0000\u0052\u030F"d~ -"\u0000\u0052\u0311\u0000\u0052\u0323\u0000\u0052\u0323\u0304\u0000\u0052\u0327\u0000\u0052\u0331\u0000\u0053\u0000"d~ -"\u0053\u0044\u0000\u0053\u004D\u0000\u0053\u0053\u0000\u0053\u0076\u0000\u0053\u0301\u0000\u0053\u0301\u0307\u0000"d~ -"\u0053\u0302\u0000\u0053\u0307\u0000\u0053\u030C\u0000\u0053\u030C\u0307\u0000\u0053\u0323\u0000\u0053\u0323\u0307"d~ -"\u0000\u0053\u0326\u0000\u0053\u0327\u0000\u0054\u0000\u0054\u0045\u004C\u0000\u0054\u0048\u007A\u0000\u0054\u004D"d~ -"\u0000\u0054\u0307\u0000\u0054\u030C\u0000\u0054\u0323\u0000\u0054\u0326\u0000\u0054\u0327\u0000\u0054\u032D\u0000"d~ -"\u0054\u0331\u0000\u0055\u0000\u0055\u0300\u0000\u0055\u0301\u0000\u0055\u0302\u0000\u0055\u0303\u0000\u0055\u0303"d~ -"\u0301\u0000\u0055\u0304\u0000\u0055\u0304\u0308\u0000\u0055\u0306\u0000\u0055\u0308\u0000\u0055\u0308\u0300\u0000"d~ -"\u0055\u0308\u0301\u0000\u0055\u0308\u0304\u0000\u0055\u0308\u030C\u0000\u0055\u0309\u0000\u0055\u030A\u0000\u0055"d~ -"\u030B\u0000\u0055\u030C\u0000\u0055\u030F\u0000\u0055\u0311\u0000\u0055\u031B\u0000\u0055\u031B\u0300\u0000\u0055"d~ -"\u031B\u0301\u0000\u0055\u031B\u0303\u0000\u0055\u031B\u0309\u0000\u0055\u031B\u0323\u0000\u0055\u0323\u0000\u0055"d~ -"\u0324\u0000\u0055\u0328\u0000\u0055\u032D\u0000\u0055\u0330\u0000\u0056\u0000\u0056\u0049\u0000\u0056\u0049\u0049"d~ -"\u0000\u0056\u0049\u0049\u0049\u0000\u0056\u0303\u0000\u0056\u0323\u0000\u0056\u2215\u006D\u0000\u0057\u0000\u0057"d~ -"\u0043\u0000\u0057\u005A\u0000\u0057\u0062\u0000\u0057\u0300\u0000\u0057\u0301\u0000\u0057\u0302\u0000\u0057\u0307"d~ -"\u0000\u0057\u0308\u0000\u0057\u0323\u0000\u0058\u0000\u0058\u0049\u0000\u0058\u0049\u0049\u0000\u0058\u0307\u0000"d~ -"\u0058\u0308\u0000\u0059\u0000\u0059\u0300\u0000\u0059\u0301\u0000\u0059\u0302\u0000\u0059\u0303\u0000\u0059\u0304"d~ -"\u0000\u0059\u0307\u0000\u0059\u0308\u0000\u0059\u0309\u0000\u0059\u0323\u0000\u005A\u0000\u005A\u0301\u0000\u005A"d~ -"\u0302\u0000\u005A\u0307\u0000\u005A\u030C\u0000\u005A\u0323\u0000\u005A\u0331\u0000\u005B\u0000\u005C\u0000\u005D"d~ -"\u0000\u005E\u0000\u005F\u0000\u0060\u0000\u0061\u0000\u0061\u002E\u006D\u002E\u0000\u0061\u002F\u0063\u0000\u0061"d~ -"\u002F\u0073\u0000\u0061\u02BE\u0000\u0061\u0300\u0000\u0061\u0301\u0000\u0061\u0302\u0000\u0061\u0302\u0300\u0000"d~ -"\u0061\u0302\u0301\u0000\u0061\u0302\u0303\u0000\u0061\u0302\u0309\u0000\u0061\u0303\u0000\u0061\u0304\u0000\u0061"d~ -"\u0306\u0000\u0061\u0306\u0300\u0000\u0061\u0306\u0301\u0000\u0061\u0306\u0303\u0000\u0061\u0306\u0309\u0000\u0061"d~ -"\u0307\u0000\u0061\u0307\u0304\u0000\u0061\u0308\u0000\u0061\u0308\u0304\u0000\u0061\u0309\u0000\u0061\u030A\u0000"d~ -"\u0061\u030A\u0301\u0000\u0061\u030C\u0000\u0061\u030F\u0000\u0061\u0311\u0000\u0061\u0323\u0000\u0061\u0323\u0302"d~ -"\u0000\u0061\u0323\u0306\u0000\u0061\u0325\u0000\u0061\u0328\u0000\u0062\u0000\u0062\u0061\u0072\u0000\u0062\u0307"d~ -"\u0000\u0062\u0323\u0000\u0062\u0331\u0000\u0063\u0000\u0063\u002F\u006F\u0000\u0063\u002F\u0075\u0000\u0063\u0061"d~ -"\u006C\u0000\u0063\u0063\u0000\u0063\u0064\u0000\u0063\u006D\u0000\u0063\u006D\u0032\u0000\u0063\u006D\u0033\u0000"d~ -"\u0063\u0301\u0000\u0063\u0302\u0000\u0063\u0307\u0000\u0063\u030C\u0000\u0063\u0327\u0000\u0063\u0327\u0301\u0000"d~ -"\u0064\u0000\u0064\u0042\u0000\u0064\u0061\u0000\u0064\u006C\u0000\u0064\u006D\u0000\u0064\u006D\u0032\u0000\u0064"d~ -"\u006D\u0033\u0000\u0064\u007A\u0000\u0064\u007A\u030C\u0000\u0064\u0307\u0000\u0064\u030C\u0000\u0064\u0323\u0000"d~ -"\u0064\u0327\u0000\u0064\u032D\u0000\u0064\u0331\u0000\u0065\u0000\u0065\u0056\u0000\u0065\u0072\u0067\u0000\u0065"d~ -"\u0300\u0000\u0065\u0301\u0000\u0065\u0302\u0000\u0065\u0302\u0300\u0000\u0065\u0302\u0301\u0000\u0065\u0302\u0303"d~ -"\u0000\u0065\u0302\u0309\u0000\u0065\u0303\u0000\u0065\u0304\u0000\u0065\u0304\u0300\u0000\u0065\u0304\u0301\u0000"d~ -"\u0065\u0306\u0000\u0065\u0307\u0000\u0065\u0308\u0000\u0065\u0309\u0000\u0065\u030C\u0000\u0065\u030F\u0000\u0065"d~ -"\u0311\u0000\u0065\u0323\u0000\u0065\u0323\u0302\u0000\u0065\u0327\u0000\u0065\u0327\u0306\u0000\u0065\u0328\u0000"d~ -"\u0065\u032D\u0000\u0065\u0330\u0000\u0066\u0000\u0066\u0066\u0000\u0066\u0066\u0069\u0000\u0066\u0066\u006C\u0000"d~ -"\u0066\u0069\u0000\u0066\u006C\u0000\u0066\u006D\u0000\u0066\u0307\u0000\u0067\u0000\u0067\u0061\u006C\u0000\u0067"d~ -"\u0301\u0000\u0067\u0302\u0000\u0067\u0304\u0000\u0067\u0306\u0000\u0067\u0307\u0000\u0067\u030C\u0000\u0067\u0327"d~ -"\u0000\u0068\u0000\u0068\u0050\u0061\u0000\u0068\u0061\u0000\u0068\u0302\u0000\u0068\u0307\u0000\u0068\u0308\u0000"d~ -"\u0068\u030C\u0000\u0068\u0323\u0000\u0068\u0327\u0000\u0068\u032E\u0000\u0068\u0331\u0000\u0069\u0000\u0069\u0069"d~ -"\u0000\u0069\u0069\u0069\u0000\u0069\u006A\u0000\u0069\u006E\u0000\u0069\u0076\u0000\u0069\u0078\u0000\u0069\u0300"d~ -"\u0000\u0069\u0301\u0000\u0069\u0302\u0000\u0069\u0303\u0000\u0069\u0304\u0000\u0069\u0306\u0000\u0069\u0308\u0000"d~ -"\u0069\u0308\u0301\u0000\u0069\u0309\u0000\u0069\u030C\u0000\u0069\u030F\u0000\u0069\u0311\u0000\u0069\u0323\u0000"d~ -"\u0069\u0328\u0000\u0069\u0330\u0000\u006A\u0000\u006A\u0302\u0000\u006A\u030C\u0000\u006B\u0000\u006B\u0041\u0000"d~ -"\u006B\u0048\u007A\u0000\u006B\u0050\u0061\u0000\u006B\u0056\u0000\u006B\u0057\u0000\u006B\u0063\u0061\u006C\u0000"d~ -"\u006B\u0067\u0000\u006B\u006C\u0000\u006B\u006D\u0000\u006B\u006D\u0032\u0000\u006B\u006D\u0033\u0000\u006B\u0074"d~ -"\u0000\u006B\u0301\u0000\u006B\u030C\u0000\u006B\u0323\u0000\u006B\u0327\u0000\u006B\u0331\u0000\u006B\u03A9\u0000"d~ -"\u006C\u0000\u006C\u006A\u0000\u006C\u006D\u0000\u006C\u006E\u0000\u006C\u006F\u0067\u0000\u006C\u0078\u0000\u006C"d~ -"\u00B7\u0000\u006C\u0301\u0000\u006C\u030C\u0000\u006C\u0323\u0000\u006C\u0323\u0304\u0000\u006C\u0327\u0000\u006C"d~ -"\u032D\u0000\u006C\u0331\u0000\u006D\u0000\u006D\u0032\u0000\u006D\u0033\u0000\u006D\u0041\u0000\u006D\u0056\u0000"d~ -"\u006D\u0057\u0000\u006D\u0062\u0000\u006D\u0067\u0000\u006D\u0069\u006C\u0000\u006D\u006C\u0000\u006D\u006D\u0000"d~ -"\u006D\u006D\u0032\u0000\u006D\u006D\u0033\u0000\u006D\u006F\u006C\u0000\u006D\u0073\u0000\u006D\u0301\u0000\u006D"d~ -"\u0307\u0000\u006D\u0323\u0000\u006D\u2215\u0073\u0000\u006D\u2215\u0073\u0032\u0000\u006E\u0000\u006E\u0041\u0000"d~ -"\u006E\u0046\u0000\u006E\u0056\u0000\u006E\u0057\u0000\u006E\u006A\u0000\u006E\u006D\u0000\u006E\u0073\u0000\u006E"d~ -"\u0300\u0000\u006E\u0301\u0000\u006E\u0303\u0000\u006E\u0307\u0000\u006E\u030C\u0000\u006E\u0323\u0000\u006E\u0327"d~ -"\u0000\u006E\u032D\u0000\u006E\u0331\u0000\u006F\u0000\u006F\u0056\u0000\u006F\u0300\u0000\u006F\u0301\u0000\u006F"d~ -"\u0302\u0000\u006F\u0302\u0300\u0000\u006F\u0302\u0301\u0000\u006F\u0302\u0303\u0000\u006F\u0302\u0309\u0000\u006F"d~ -"\u0303\u0000\u006F\u0303\u0301\u0000\u006F\u0303\u0304\u0000\u006F\u0303\u0308\u0000\u006F\u0304\u0000\u006F\u0304"d~ -"\u0300\u0000\u006F\u0304\u0301\u0000\u006F\u0306\u0000\u006F\u0307\u0000\u006F\u0307\u0304\u0000\u006F\u0308\u0000"d~ -"\u006F\u0308\u0304\u0000\u006F\u0309\u0000\u006F\u030B\u0000\u006F\u030C\u0000\u006F\u030F\u0000\u006F\u0311\u0000"d~ -"\u006F\u031B\u0000\u006F\u031B\u0300\u0000\u006F\u031B\u0301\u0000\u006F\u031B\u0303\u0000\u006F\u031B\u0309\u0000"d~ -"\u006F\u031B\u0323\u0000\u006F\u0323\u0000\u006F\u0323\u0302\u0000\u006F\u0328\u0000\u006F\u0328\u0304\u0000\u0070"d~ -"\u0000\u0070\u002E\u006D\u002E\u0000\u0070\u0041\u0000\u0070\u0046\u0000\u0070\u0056\u0000\u0070\u0057\u0000\u0070"d~ -"\u0063\u0000\u0070\u0073\u0000\u0070\u0301\u0000\u0070\u0307\u0000\u0071\u0000\u0072\u0000\u0072\u0061\u0064\u0000"d~ -"\u0072\u0061\u0064\u2215\u0073\u0000\u0072\u0061\u0064\u2215\u0073\u0032\u0000\u0072\u0301\u0000\u0072\u0307\u0000"d~ -"\u0072\u030C\u0000\u0072\u030F\u0000\u0072\u0311\u0000\u0072\u0323\u0000\u0072\u0323\u0304\u0000\u0072\u0327\u0000"d~ -"\u0072\u0331\u0000\u0073\u0000\u0073\u0072\u0000\u0073\u0074\u0000\u0073\u0301\u0000\u0073\u0301\u0307\u0000\u0073"d~ -"\u0302\u0000\u0073\u0307\u0000\u0073\u030C\u0000\u0073\u030C\u0307\u0000\u0073\u0323\u0000\u0073\u0323\u0307\u0000"d~ -"\u0073\u0326\u0000\u0073\u0327\u0000\u0074\u0000\u0074\u0307\u0000\u0074\u0308\u0000\u0074\u030C\u0000\u0074\u0323"d~ -"\u0000\u0074\u0326\u0000\u0074\u0327\u0000\u0074\u032D\u0000\u0074\u0331\u0000\u0075\u0000\u0075\u0300\u0000\u0075"d~ -"\u0301\u0000\u0075\u0302\u0000\u0075\u0303\u0000\u0075\u0303\u0301\u0000\u0075\u0304\u0000\u0075\u0304\u0308\u0000"d~ -"\u0075\u0306\u0000\u0075\u0308\u0000\u0075\u0308\u0300\u0000\u0075\u0308\u0301\u0000\u0075\u0308\u0304\u0000\u0075"d~ -"\u0308\u030C\u0000\u0075\u0309\u0000\u0075\u030A\u0000\u0075\u030B\u0000\u0075\u030C\u0000\u0075\u030F\u0000\u0075"d~ -"\u0311\u0000\u0075\u031B\u0000\u0075\u031B\u0300\u0000\u0075\u031B\u0301\u0000\u0075\u031B\u0303\u0000\u0075\u031B"d~ -"\u0309\u0000\u0075\u031B\u0323\u0000\u0075\u0323\u0000\u0075\u0324\u0000\u0075\u0328\u0000\u0075\u032D\u0000\u0075"d~ -"\u0330\u0000\u0076\u0000\u0076\u0069\u0000\u0076\u0069\u0069\u0000\u0076\u0069\u0069\u0069\u0000\u0076\u0303\u0000"d~ -"\u0076\u0323\u0000\u0077\u0000\u0077\u0300\u0000\u0077\u0301\u0000\u0077\u0302\u0000\u0077\u0307\u0000\u0077\u0308"d~ -"\u0000\u0077\u030A\u0000\u0077\u0323\u0000\u0078\u0000\u0078\u0069\u0000\u0078\u0069\u0069\u0000\u0078\u0307\u0000"d~ -"\u0078\u0308\u0000\u0079\u0000\u0079\u0300\u0000\u0079\u0301\u0000\u0079\u0302\u0000\u0079\u0303\u0000\u0079\u0304"d~ -"\u0000\u0079\u0307\u0000\u0079\u0308\u0000\u0079\u0309\u0000\u0079\u030A\u0000\u0079\u0323\u0000\u007A\u0000\u007A"d~ -"\u0301\u0000\u007A\u0302\u0000\u007A\u0307\u0000\u007A\u030C\u0000\u007A\u0323\u0000\u007A\u0331\u0000\u007B\u0000"d~ -"\u007C\u0000\u007D\u0000\u007E\u0000\u00A2\u0000\u00A3\u0000\u00A5\u0000\u00A6\u0000\u00AC\u0000\u00B0\u0043\u0000"d~ -"\u00B0\u0046\u0000\u00B7\u0000\u00C6\u0000\u00C6\u0301\u0000\u00C6\u0304\u0000\u00D8\u0301\u0000\u00E6\u0000\u00E6"d~ -"\u0301\u0000\u00E6\u0304\u0000\u00F0\u0000\u00F8\u0000\u00F8\u0301\u0000\u0126\u0000\u0127\u0000\u0131\u0000\u014B"d~ -"\u0000\u0153\u0000\u018E\u0000\u0190\u0000\u01AB\u0000\u01B7\u030C\u0000\u01C0\u0000\u01C1\u0000\u01C2\u0000\u0222"d~ -"\u0000\u0237\u0000\u0250\u0000\u0251\u0000\u0252\u0000\u0253\u0000\u0254\u0000\u0255\u0000\u0256\u0000\u0257\u0000"d~ -"\u0258\u0000\u0259\u0000\u025B\u0000\u025C\u0000\u025E\u0000\u025F\u0000\u0260\u0000\u0261\u0000\u0262\u0000\u0263"d~ -"\u0000\u0264\u0000\u0265\u0000\u0266\u0000\u0267\u0000\u0268\u0000\u0269\u0000\u026A\u0000\u026B\u0000\u026C\u0000"d~ -"\u026D\u0000\u026E\u0000\u026F\u0000\u0270\u0000\u0271\u0000\u0272\u0000\u0273\u0000\u0274\u0000\u0275\u0000\u0276"d~ -"\u0000\u0277\u0000\u0278\u0000\u0279\u0000\u027A\u0000\u027B\u0000\u027D\u0000\u027E\u0000\u0280\u0000\u0281\u0000"d~ -"\u0282\u0000\u0283\u0000\u0284\u0000\u0288\u0000\u0289\u0000\u028A\u0000\u028B\u0000\u028C\u0000\u028D\u0000\u028E"d~ -"\u0000\u028F\u0000\u0290\u0000\u0291\u0000\u0292\u0000\u0292\u030C\u0000\u0295\u0000\u0298\u0000\u0299\u0000\u029B"d~ -"\u0000\u029C\u0000\u029D\u0000\u029F\u0000\u02A1\u0000\u02A2\u0000\u02A3\u0000\u02A4\u0000\u02A5\u0000\u02A6\u0000"d~ -"\u02A7\u0000\u02A8\u0000\u02A9\u0000\u02AA\u0000\u02AB\u0000\u02B9\u0000\u02BC\u006E\u0000\u02D0\u0000\u02D1\u0000"d~ -"\u0300\u0000\u0301\u0000\u0308\u0301\u0000\u0313\u0000\u0391\u0000\u0391\u0300\u0000\u0391\u0301\u0000\u0391\u0304"d~ -"\u0000\u0391\u0306\u0000\u0391\u0313\u0000\u0391\u0313\u0300\u0000\u0391\u0313\u0300\u0345\u0000\u0391\u0313\u0301"d~ -"\u0000\u0391\u0313\u0301\u0345\u0000\u0391\u0313\u0342\u0000\u0391\u0313\u0342\u0345\u0000\u0391\u0313\u0345\u0000"d~ -"\u0391\u0314\u0000\u0391\u0314\u0300\u0000\u0391\u0314\u0300\u0345\u0000\u0391\u0314\u0301\u0000\u0391\u0314\u0301"d~ -"\u0345\u0000\u0391\u0314\u0342\u0000\u0391\u0314\u0342\u0345\u0000\u0391\u0314\u0345\u0000\u0391\u0345\u0000\u0392"d~ -"\u0000\u0393\u0000\u0394\u0000\u0395\u0000\u0395\u0300\u0000\u0395\u0301\u0000\u0395\u0313\u0000\u0395\u0313\u0300"d~ -"\u0000\u0395\u0313\u0301\u0000\u0395\u0314\u0000\u0395\u0314\u0300\u0000\u0395\u0314\u0301\u0000\u0396\u0000\u0397"d~ -"\u0000\u0397\u0300\u0000\u0397\u0301\u0000\u0397\u0313\u0000\u0397\u0313\u0300\u0000\u0397\u0313\u0300\u0345\u0000"d~ -"\u0397\u0313\u0301\u0000\u0397\u0313\u0301\u0345\u0000\u0397\u0313\u0342\u0000\u0397\u0313\u0342\u0345\u0000\u0397"d~ -"\u0313\u0345\u0000\u0397\u0314\u0000\u0397\u0314\u0300\u0000\u0397\u0314\u0300\u0345\u0000\u0397\u0314\u0301\u0000"d~ -"\u0397\u0314\u0301\u0345\u0000\u0397\u0314\u0342\u0000\u0397\u0314\u0342\u0345\u0000\u0397\u0314\u0345\u0000\u0397"d~ -"\u0345\u0000\u0398\u0000\u0399\u0000\u0399\u0300\u0000\u0399\u0301\u0000\u0399\u0304\u0000\u0399\u0306\u0000\u0399"d~ -"\u0308\u0000\u0399\u0313\u0000\u0399\u0313\u0300\u0000\u0399\u0313\u0301\u0000\u0399\u0313\u0342\u0000\u0399\u0314"d~ -"\u0000\u0399\u0314\u0300\u0000\u0399\u0314\u0301\u0000\u0399\u0314\u0342\u0000\u039A\u0000\u039B\u0000\u039C\u0000"d~ -"\u039D\u0000\u039E\u0000\u039F\u0000\u039F\u0300\u0000\u039F\u0301\u0000\u039F\u0313\u0000\u039F\u0313\u0300\u0000"d~ -"\u039F\u0313\u0301\u0000\u039F\u0314\u0000\u039F\u0314\u0300\u0000\u039F\u0314\u0301\u0000\u03A0\u0000\u03A1\u0000"d~ -"\u03A1\u0314\u0000\u03A3\u0000\u03A4\u0000\u03A5\u0000\u03A5\u0300\u0000\u03A5\u0301\u0000\u03A5\u0304\u0000\u03A5"d~ -"\u0306\u0000\u03A5\u0308\u0000\u03A5\u0314\u0000\u03A5\u0314\u0300\u0000\u03A5\u0314\u0301\u0000\u03A5\u0314\u0342"d~ -"\u0000\u03A6\u0000\u03A7\u0000\u03A8\u0000\u03A9\u0000\u03A9\u0300\u0000\u03A9\u0301\u0000\u03A9\u0313\u0000\u03A9"d~ -"\u0313\u0300\u0000\u03A9\u0313\u0300\u0345\u0000\u03A9\u0313\u0301\u0000\u03A9\u0313\u0301\u0345\u0000\u03A9\u0313"d~ -"\u0342\u0000\u03A9\u0313\u0342\u0345\u0000\u03A9\u0313\u0345\u0000\u03A9\u0314\u0000\u03A9\u0314\u0300\u0000\u03A9"d~ -"\u0314\u0300\u0345\u0000\u03A9\u0314\u0301\u0000\u03A9\u0314\u0301\u0345\u0000\u03A9\u0314\u0342\u0000\u03A9\u0314"d~ -"\u0342\u0345\u0000\u03A9\u0314\u0345\u0000\u03A9\u0345\u0000\u03B1\u0000\u03B1\u0300\u0000\u03B1\u0300\u0345\u0000"d~ -"\u03B1\u0301\u0000\u03B1\u0301\u0345\u0000\u03B1\u0304\u0000\u03B1\u0306\u0000\u03B1\u0313\u0000\u03B1\u0313\u0300"d~ -"\u0000\u03B1\u0313\u0300\u0345\u0000\u03B1\u0313\u0301\u0000\u03B1\u0313\u0301\u0345\u0000\u03B1\u0313\u0342\u0000"d~ -"\u03B1\u0313\u0342\u0345\u0000\u03B1\u0313\u0345\u0000\u03B1\u0314\u0000\u03B1\u0314\u0300\u0000\u03B1\u0314\u0300"d~ -"\u0345\u0000\u03B1\u0314\u0301\u0000\u03B1\u0314\u0301\u0345\u0000\u03B1\u0314\u0342\u0000\u03B1\u0314\u0342\u0345"d~ -"\u0000\u03B1\u0314\u0345\u0000\u03B1\u0342\u0000\u03B1\u0342\u0345\u0000\u03B1\u0345\u0000\u03B2\u0000\u03B3\u0000"d~ -"\u03B4\u0000\u03B5\u0000\u03B5\u0300\u0000\u03B5\u0301\u0000\u03B5\u0313\u0000\u03B5\u0313\u0300\u0000\u03B5\u0313"d~ -"\u0301\u0000\u03B5\u0314\u0000\u03B5\u0314\u0300\u0000\u03B5\u0314\u0301\u0000\u03B6\u0000\u03B7\u0000\u03B7\u0300"d~ -"\u0000\u03B7\u0300\u0345\u0000\u03B7\u0301\u0000\u03B7\u0301\u0345\u0000\u03B7\u0313\u0000\u03B7\u0313\u0300\u0000"d~ -"\u03B7\u0313\u0300\u0345\u0000\u03B7\u0313\u0301\u0000\u03B7\u0313\u0301\u0345\u0000\u03B7\u0313\u0342\u0000\u03B7"d~ -"\u0313\u0342\u0345\u0000\u03B7\u0313\u0345\u0000\u03B7\u0314\u0000\u03B7\u0314\u0300\u0000\u03B7\u0314\u0300\u0345"d~ -"\u0000\u03B7\u0314\u0301\u0000\u03B7\u0314\u0301\u0345\u0000\u03B7\u0314\u0342\u0000\u03B7\u0314\u0342\u0345\u0000"d~ -"\u03B7\u0314\u0345\u0000\u03B7\u0342\u0000\u03B7\u0342\u0345\u0000\u03B7\u0345\u0000\u03B8\u0000\u03B9\u0000\u03B9"d~ -"\u0300\u0000\u03B9\u0301\u0000\u03B9\u0304\u0000\u03B9\u0306\u0000\u03B9\u0308\u0000\u03B9\u0308\u0300\u0000\u03B9"d~ -"\u0308\u0301\u0000\u03B9\u0308\u0342\u0000\u03B9\u0313\u0000\u03B9\u0313\u0300\u0000\u03B9\u0313\u0301\u0000\u03B9"d~ -"\u0313\u0342\u0000\u03B9\u0314\u0000\u03B9\u0314\u0300\u0000\u03B9\u0314\u0301\u0000\u03B9\u0314\u0342\u0000\u03B9"d~ -"\u0342\u0000\u03BA\u0000\u03BB\u0000\u03BC\u0000\u03BC\u0041\u0000\u03BC\u0046\u0000\u03BC\u0056\u0000\u03BC\u0057"d~ -"\u0000\u03BC\u0067\u0000\u03BC\u006C\u0000\u03BC\u006D\u0000\u03BC\u0073\u0000\u03BD\u0000\u03BE\u0000\u03BF\u0000"d~ -"\u03BF\u0300\u0000\u03BF\u0301\u0000\u03BF\u0313\u0000\u03BF\u0313\u0300\u0000\u03BF\u0313\u0301\u0000\u03BF\u0314"d~ -"\u0000\u03BF\u0314\u0300\u0000\u03BF\u0314\u0301\u0000\u03C0\u0000\u03C1\u0000\u03C1\u0313\u0000\u03C1\u0314\u0000"d~ -"\u03C2\u0000\u03C3\u0000\u03C4\u0000\u03C5\u0000\u03C5\u0300\u0000\u03C5\u0301\u0000\u03C5\u0304\u0000\u03C5\u0306"d~ -"\u0000\u03C5\u0308\u0000\u03C5\u0308\u0300\u0000\u03C5\u0308\u0301\u0000\u03C5\u0308\u0342\u0000\u03C5\u0313\u0000"d~ -"\u03C5\u0313\u0300\u0000\u03C5\u0313\u0301\u0000\u03C5\u0313\u0342\u0000\u03C5\u0314\u0000\u03C5\u0314\u0300\u0000"d~ -"\u03C5\u0314\u0301\u0000\u03C5\u0314\u0342\u0000\u03C5\u0342\u0000\u03C6\u0000\u03C7\u0000\u03C8\u0000\u03C9\u0000"d~ -"\u03C9\u0300\u0000\u03C9\u0300\u0345\u0000\u03C9\u0301\u0000\u03C9\u0301\u0345\u0000\u03C9\u0313\u0000\u03C9\u0313"d~ -"\u0300\u0000\u03C9\u0313\u0300\u0345\u0000\u03C9\u0313\u0301\u0000\u03C9\u0313\u0301\u0345\u0000\u03C9\u0313\u0342"d~ -"\u0000\u03C9\u0313\u0342\u0345\u0000\u03C9\u0313\u0345\u0000\u03C9\u0314\u0000\u03C9\u0314\u0300\u0000\u03C9\u0314"d~ -"\u0300\u0345\u0000\u03C9\u0314\u0301\u0000\u03C9\u0314\u0301\u0345\u0000\u03C9\u0314\u0342\u0000\u03C9\u0314\u0342"d~ -"\u0345\u0000\u03C9\u0314\u0345\u0000\u03C9\u0342\u0000\u03C9\u0342\u0345\u0000\u03C9\u0345\u0000\u03DC\u0000\u03DD"d~ -"\u0000\u0406\u0308\u0000\u0410\u0306\u0000\u0410\u0308\u0000\u0413\u0301\u0000\u0415\u0300\u0000\u0415\u0306\u0000"d~ -"\u0415\u0308\u0000\u0416\u0306\u0000\u0416\u0308\u0000\u0417\u0308\u0000\u0418\u0300\u0000\u0418\u0304\u0000\u0418"d~ -"\u0306\u0000\u0418\u0308\u0000\u041A\u0301\u0000\u041E\u0308\u0000\u0423\u0304\u0000\u0423\u0306\u0000\u0423\u0308"d~ -"\u0000\u0423\u030B\u0000\u0427\u0308\u0000\u042B\u0308\u0000\u042D\u0308\u0000\u0430\u0000\u0430\u0306\u0000\u0430"d~ -"\u0308\u0000\u0431\u0000\u0432\u0000\u0433\u0000\u0433\u0301\u0000\u0434\u0000\u0435\u0000\u0435\u0300\u0000\u0435"d~ -"\u0306\u0000\u0435\u0308\u0000\u0436\u0000\u0436\u0306\u0000\u0436\u0308\u0000\u0437\u0000\u0437\u0308\u0000\u0438"d~ -"\u0000\u0438\u0300\u0000\u0438\u0304\u0000\u0438\u0306\u0000\u0438\u0308\u0000\u043A\u0000\u043A\u0301\u0000\u043B"d~ -"\u0000\u043C\u0000\u043D\u0000\u043E\u0000\u043E\u0308\u0000\u043F\u0000\u0440\u0000\u0441\u0000\u0442\u0000\u0443"d~ -"\u0000\u0443\u0304\u0000\u0443\u0306\u0000\u0443\u0308\u0000\u0443\u030B\u0000\u0444\u0000\u0445\u0000\u0446\u0000"d~ -"\u0447\u0000\u0447\u0308\u0000\u0448\u0000\u044A\u0000\u044B\u0000\u044B\u0308\u0000\u044C\u0000\u044D\u0000\u044D"d~ -"\u0308\u0000\u044E\u0000\u0455\u0000\u0456\u0000\u0456\u0308\u0000\u0458\u0000\u045F\u0000\u0474\u030F\u0000\u0475"d~ -"\u030F\u0000\u0491\u0000\u04AB\u0000\u04AF\u0000\u04B1\u0000\u04CF\u0000\u04D8\u0308\u0000\u04D9\u0000\u04D9\u0308"d~ -"\u0000\u04E8\u0308\u0000\u04E9\u0000\u04E9\u0308\u0000\u0565\u0582\u0000\u0574\u0565\u0000\u0574\u056B\u0000\u0574"d~ -"\u056D\u0000\u0574\u0576\u0000\u057E\u0576\u0000\u05D0\u0000\u05D0\u05B7\u0000\u05D0\u05B8\u0000\u05D0\u05BC\u0000"d~ -"\u05D0\u05DC\u0000\u05D1\u0000\u05D1\u05BC\u0000\u05D1\u05BF\u0000\u05D2\u0000\u05D2\u05BC\u0000\u05D3\u0000\u05D3"d~ -"\u05BC\u0000\u05D4\u0000\u05D4\u05BC\u0000\u05D5\u05B9\u0000\u05D5\u05BC\u0000\u05D6\u05BC\u0000\u05D8\u05BC\u0000"d~ -"\u05D9\u05B4\u0000\u05D9\u05BC\u0000\u05DA\u05BC\u0000\u05DB\u0000\u05DB\u05BC\u0000\u05DB\u05BF\u0000\u05DC\u0000"d~ -"\u05DC\u05BC\u0000\u05DD\u0000\u05DE\u05BC\u0000\u05E0\u05BC\u0000\u05E1\u05BC\u0000\u05E2\u0000\u05E3\u05BC\u0000"d~ -"\u05E4\u05BC\u0000\u05E4\u05BF\u0000\u05E6\u05BC\u0000\u05E7\u05BC\u0000\u05E8\u0000\u05E8\u05BC\u0000\u05E9\u05BC"d~ -"\u0000\u05E9\u05BC\u05C1\u0000\u05E9\u05BC\u05C2\u0000\u05E9\u05C1\u0000\u05E9\u05C2\u0000\u05EA\u0000\u05EA\u05BC"d~ -"\u0000\u05F2\u05B7\u0000\u0621\u0000\u0627\u0000\u0627\u0643\u0628\u0631\u0000\u0627\u0644\u0644\u0647\u0000\u0627"d~ -"\u064B\u0000\u0627\u0653\u0000\u0627\u0654\u0000\u0627\u0655\u0000\u0627\u0674\u0000\u0628\u0000\u0628\u062C\u0000"d~ -"\u0628\u062D\u0000\u0628\u062D\u064A\u0000\u0628\u062E\u0000\u0628\u062E\u064A\u0000\u0628\u0631\u0000\u0628\u0632"d~ -"\u0000\u0628\u0645\u0000\u0628\u0646\u0000\u0628\u0647\u0000\u0628\u0649\u0000\u0628\u064A\u0000\u0629\u0000\u062A"d~ -"\u0000\u062A\u062C\u0000\u062A\u062C\u0645\u0000\u062A\u062C\u0649\u0000\u062A\u062C\u064A\u0000\u062A\u062D\u0000"d~ -"\u062A\u062D\u062C\u0000\u062A\u062D\u0645\u0000\u062A\u062E\u0000\u062A\u062E\u0645\u0000\u062A\u062E\u0649\u0000"d~ -"\u062A\u062E\u064A\u0000\u062A\u0631\u0000\u062A\u0632\u0000\u062A\u0645\u0000\u062A\u0645\u062C\u0000\u062A\u0645"d~ -"\u062D\u0000\u062A\u0645\u062E\u0000\u062A\u0645\u0649\u0000\u062A\u0645\u064A\u0000\u062A\u0646\u0000\u062A\u0647"d~ -"\u0000\u062A\u0649\u0000\u062A\u064A\u0000\u062B\u0000\u062B\u062C\u0000\u062B\u0631\u0000\u062B\u0632\u0000\u062B"d~ -"\u0645\u0000\u062B\u0646\u0000\u062B\u0647\u0000\u062B\u0649\u0000\u062B\u064A\u0000\u062C\u0000\u062C\u062D\u0000"d~ -"\u062C\u062D\u0649\u0000\u062C\u062D\u064A\u0000\u062C\u0644\u0020\u062C\u0644\u0627\u0644\u0647\u0000\u062C\u0645"d~ -"\u0000\u062C\u0645\u062D\u0000\u062C\u0645\u0649\u0000\u062C\u0645\u064A\u0000\u062C\u0649\u0000\u062C\u064A\u0000"d~ -"\u062D\u0000\u062D\u062C\u0000\u062D\u062C\u064A\u0000\u062D\u0645\u0000\u062D\u0645\u0649\u0000\u062D\u0645\u064A"d~ -"\u0000\u062D\u0649\u0000\u062D\u064A\u0000\u062E\u0000\u062E\u062C\u0000\u062E\u062D\u0000\u062E\u0645\u0000\u062E"d~ -"\u0649\u0000\u062E\u064A\u0000\u062F\u0000\u0630\u0000\u0630\u0670\u0000\u0631\u0000\u0631\u0633\u0648\u0644\u0000"d~ -"\u0631\u0670\u0000\u0631\u06CC\u0627\u0644\u0000\u0632\u0000\u0633\u0000\u0633\u062C\u0000\u0633\u062C\u062D\u0000"d~ -"\u0633\u062C\u0649\u0000\u0633\u062D\u0000\u0633\u062D\u062C\u0000\u0633\u062E\u0000\u0633\u062E\u0649\u0000\u0633"d~ -"\u062E\u064A\u0000\u0633\u0631\u0000\u0633\u0645\u0000\u0633\u0645\u062C\u0000\u0633\u0645\u062D\u0000\u0633\u0645"d~ -"\u0645\u0000\u0633\u0647\u0000\u0633\u0649\u0000\u0633\u064A\u0000\u0634\u0000\u0634\u062C\u0000\u0634\u062C\u064A"d~ -"\u0000\u0634\u062D\u0000\u0634\u062D\u0645\u0000\u0634\u062D\u064A\u0000\u0634\u062E\u0000\u0634\u0631\u0000\u0634"d~ -"\u0645\u0000\u0634\u0645\u062E\u0000\u0634\u0645\u0645\u0000\u0634\u0647\u0000\u0634\u0649\u0000\u0634\u064A\u0000"d~ -"\u0635\u0000\u0635\u062D\u0000\u0635\u062D\u062D\u0000\u0635\u062D\u064A\u0000\u0635\u062E\u0000\u0635\u0631\u0000"d~ -"\u0635\u0644\u0639\u0645\u0000\u0635\u0644\u0649\u0000\u0635\u0644\u0649\u0020\u0627\u0644\u0644\u0647\u0020\u0639"d~ -"\u0644\u064A\u0647\u0020\u0648\u0633\u0644\u0645\u0000\u0635\u0644\u06D2\u0000\u0635\u0645\u0000\u0635\u0645\u0645"d~ -"\u0000\u0635\u0649\u0000\u0635\u064A\u0000\u0636\u0000\u0636\u062C\u0000\u0636\u062D\u0000\u0636\u062D\u0649\u0000"d~ -"\u0636\u062D\u064A\u0000\u0636\u062E\u0000\u0636\u062E\u0645\u0000\u0636\u0631\u0000\u0636\u0645\u0000\u0636\u0649"d~ -"\u0000\u0636\u064A\u0000\u0637\u0000\u0637\u062D\u0000\u0637\u0645\u0000\u0637\u0645\u062D\u0000\u0637\u0645\u0645"d~ -"\u0000\u0637\u0645\u064A\u0000\u0637\u0649\u0000\u0637\u064A\u0000\u0638\u0000\u0638\u0645\u0000\u0639\u0000\u0639"d~ -"\u062C\u0000\u0639\u062C\u0645\u0000\u0639\u0644\u064A\u0647\u0000\u0639\u0645\u0000\u0639\u0645\u0645\u0000\u0639"d~ -"\u0645\u0649\u0000\u0639\u0645\u064A\u0000\u0639\u0649\u0000\u0639\u064A\u0000\u063A\u0000\u063A\u062C\u0000\u063A"d~ -"\u0645\u0000\u063A\u0645\u0645\u0000\u063A\u0645\u0649\u0000\u063A\u0645\u064A\u0000\u063A\u0649\u0000\u063A\u064A"d~ -"\u0000\u0640\u064B\u0000\u0640\u064E\u0000\u0640\u064E\u0651\u0000\u0640\u064F\u0000\u0640\u064F\u0651\u0000\u0640"d~ -"\u0650\u0000\u0640\u0650\u0651\u0000\u0640\u0651\u0000\u0640\u0652\u0000\u0641\u0000\u0641\u062C\u0000\u0641\u062D"d~ -"\u0000\u0641\u062E\u0000\u0641\u062E\u0645\u0000\u0641\u0645\u0000\u0641\u0645\u064A\u0000\u0641\u0649\u0000\u0641"d~ -"\u064A\u0000\u0642\u0000\u0642\u062D\u0000\u0642\u0644\u06D2\u0000\u0642\u0645\u0000\u0642\u0645\u062D\u0000\u0642"d~ -"\u0645\u0645\u0000\u0642\u0645\u064A\u0000\u0642\u0649\u0000\u0642\u064A\u0000\u0643\u0000\u0643\u0627\u0000\u0643"d~ -"\u062C\u0000\u0643\u062D\u0000\u0643\u062E\u0000\u0643\u0644\u0000\u0643\u0645\u0000\u0643\u0645\u0645\u0000\u0643"d~ -"\u0645\u064A\u0000\u0643\u0649\u0000\u0643\u064A\u0000\u0644\u0000\u0644\u0627\u0000\u0644\u0627\u0653\u0000\u0644"d~ -"\u0627\u0654\u0000\u0644\u0627\u0655\u0000\u0644\u062C\u0000\u0644\u062C\u062C\u0000\u0644\u062C\u0645\u0000\u0644"d~ -"\u062C\u064A\u0000\u0644\u062D\u0000\u0644\u062D\u0645\u0000\u0644\u062D\u0649\u0000\u0644\u062D\u064A\u0000\u0644"d~ -"\u062E\u0000\u0644\u062E\u0645\u0000\u0644\u0645\u0000\u0644\u0645\u062D\u0000\u0644\u0645\u064A\u0000\u0644\u0647"d~ -"\u0000\u0644\u0649\u0000\u0644\u064A\u0000\u0645\u0000\u0645\u0627\u0000\u0645\u062C\u0000\u0645\u062C\u062D\u0000"d~ -"\u0645\u062C\u062E\u0000\u0645\u062C\u0645\u0000\u0645\u062C\u064A\u0000\u0645\u062D\u0000\u0645\u062D\u062C\u0000"d~ -"\u0645\u062D\u0645\u0000\u0645\u062D\u0645\u062F\u0000\u0645\u062D\u064A\u0000\u0645\u062E\u0000\u0645\u062E\u062C"d~ -"\u0000\u0645\u062E\u0645\u0000\u0645\u062E\u064A\u0000\u0645\u0645\u0000\u0645\u0645\u064A\u0000\u0645\u0649\u0000"d~ -"\u0645\u064A\u0000\u0646\u0000\u0646\u062C\u0000\u0646\u062C\u062D\u0000\u0646\u062C\u0645\u0000\u0646\u062C\u0649"d~ -"\u0000\u0646\u062C\u064A\u0000\u0646\u062D\u0000\u0646\u062D\u0645\u0000\u0646\u062D\u0649\u0000\u0646\u062D\u064A"d~ -"\u0000\u0646\u062E\u0000\u0646\u0631\u0000\u0646\u0632\u0000\u0646\u0645\u0000\u0646\u0645\u0649\u0000\u0646\u0645"d~ -"\u064A\u0000\u0646\u0646\u0000\u0646\u0647\u0000\u0646\u0649\u0000\u0646\u064A\u0000\u0647\u0000\u0647\u062C\u0000"d~ -"\u0647\u0645\u0000\u0647\u0645\u062C\u0000\u0647\u0645\u0645\u0000\u0647\u0649\u0000\u0647\u064A\u0000\u0647\u0670"d~ -"\u0000\u0648\u0000\u0648\u0633\u0644\u0645\u0000\u0648\u0654\u0000\u0648\u0674\u0000\u0649\u0000\u0649\u0670\u0000"d~ -"\u064A\u0000\u064A\u062C\u0000\u064A\u062C\u064A\u0000\u064A\u062D\u0000\u064A\u062D\u064A\u0000\u064A\u062E\u0000"d~ -"\u064A\u0631\u0000\u064A\u0632\u0000\u064A\u0645\u0000\u064A\u0645\u0645\u0000\u064A\u0645\u064A\u0000\u064A\u0646"d~ -"\u0000\u064A\u0647\u0000\u064A\u0649\u0000\u064A\u064A\u0000\u064A\u0654\u0000\u064A\u0654\u0627\u0000\u064A\u0654"d~ -"\u062C\u0000\u064A\u0654\u062D\u0000\u064A\u0654\u062E\u0000\u064A\u0654\u0631\u0000\u064A\u0654\u0632\u0000\u064A"d~ -"\u0654\u0645\u0000\u064A\u0654\u0646\u0000\u064A\u0654\u0647\u0000\u064A\u0654\u0648\u0000\u064A\u0654\u0649\u0000"d~ -"\u064A\u0654\u064A\u0000\u064A\u0654\u06C6\u0000\u064A\u0654\u06C7\u0000\u064A\u0654\u06C8\u0000\u064A\u0654\u06D0"d~ -"\u0000\u064A\u0654\u06D5\u0000\u064A\u0674\u0000\u066E\u0000\u066F\u0000\u0671\u0000\u0679\u0000\u067A\u0000\u067B"d~ -"\u0000\u067E\u0000\u067F\u0000\u0680\u0000\u0683\u0000\u0684\u0000\u0686\u0000\u0687\u0000\u0688\u0000\u068C\u0000"d~ -"\u068D\u0000\u068E\u0000\u0691\u0000\u0698\u0000\u06A1\u0000\u06A4\u0000\u06A6\u0000\u06A9\u0000\u06AD\u0000\u06AF"d~ -"\u0000\u06B1\u0000\u06B3\u0000\u06BA\u0000\u06BB\u0000\u06BE\u0000\u06C1\u0000\u06C1\u0654\u0000\u06C5\u0000\u06C6"d~ -"\u0000\u06C7\u0000\u06C7\u0674\u0000\u06C8\u0000\u06C9\u0000\u06CB\u0000\u06CC\u0000\u06D0\u0000\u06D2\u0000\u06D2"d~ -"\u0654\u0000\u06D5\u0654\u0000\u0915\u093C\u0000\u0916\u093C\u0000\u0917\u093C\u0000\u091C\u093C\u0000\u0921\u093C"d~ -"\u0000\u0922\u093C\u0000\u0928\u093C\u0000\u092B\u093C\u0000\u092F\u093C\u0000\u0930\u093C\u0000\u0933\u093C\u0000"d~ -"\u09A1\u09BC\u0000\u09A2\u09BC\u0000\u09AF\u09BC\u0000\u09C7\u09BE\u0000\u09C7\u09D7\u0000\u0A16\u0A3C\u0000\u0A17"d~ -"\u0A3C\u0000\u0A1C\u0A3C\u0000\u0A2B\u0A3C\u0000\u0A32\u0A3C\u0000\u0A38\u0A3C\u0000\u0B21\u0B3C\u0000\u0B22\u0B3C"d~ -"\u0000\u0B47\u0B3E\u0000\u0B47\u0B56\u0000\u0B47\u0B57\u0000\u0B92\u0BD7\u0000\u0BC6\u0BBE\u0000\u0BC6\u0BD7\u0000"d~ -"\u0BC7\u0BBE\u0000\u0C46\u0C56\u0000\u0CBF\u0CD5\u0000\u0CC6\u0CC2\u0000\u0CC6\u0CC2\u0CD5\u0000\u0CC6\u0CD5\u0000"d~ -"\u0CC6\u0CD6\u0000\u0D46\u0D3E\u0000\u0D46\u0D57\u0000\u0D47\u0D3E\u0000\u0DD9\u0DCA\u0000\u0DD9\u0DCF\u0000\u0DD9"d~ -"\u0DCF\u0DCA\u0000\u0DD9\u0DDF\u0000\u0E4D\u0E32\u0000\u0EAB\u0E99\u0000\u0EAB\u0EA1\u0000\u0ECD\u0EB2\u0000\u0F0B"d~ -"\u0000\u0F40\u0FB5\u0000\u0F42\u0FB7\u0000\u0F4C\u0FB7\u0000\u0F51\u0FB7\u0000\u0F56\u0FB7\u0000\u0F5B\u0FB7\u0000"d~ -"\u0F71\u0F72\u0000\u0F71\u0F74\u0000\u0F71\u0F80\u0000\u0F90\u0FB5\u0000\u0F92\u0FB7\u0000\u0F9C\u0FB7\u0000\u0FA1"d~ -"\u0FB7\u0000\u0FA6\u0FB7\u0000\u0FAB\u0FB7\u0000\u0FB2\u0F71\u0F80\u0000\u0FB2\u0F80\u0000\u0FB3\u0F71\u0F80\u0000"d~ -"\u0FB3\u0F80\u0000\u1025\u102E\u0000\u10DC\u0000\u1100\u0000\u1100\u1161\u0000\u1101\u0000\u1102\u0000\u1102\u1161"d~ -"\u0000\u1103\u0000\u1103\u1161\u0000\u1104\u0000\u1105\u0000\u1105\u1161\u0000\u1106\u0000\u1106\u1161\u0000\u1107"d~ -"\u0000\u1107\u1161\u0000\u1108\u0000\u1109\u0000\u1109\u1161\u0000\u110A\u0000\u110B\u0000\u110B\u1161\u0000\u110B"d~ -"\u116E\u0000\u110C\u0000\u110C\u1161\u0000\u110C\u116E\u110B\u1174\u0000\u110D\u0000\u110E\u0000\u110E\u1161\u0000"d~ -"\u110E\u1161\u11B7\u1100\u1169\u0000\u110F\u0000\u110F\u1161\u0000\u1110\u0000\u1110\u1161\u0000\u1111\u0000\u1111"d~ -"\u1161\u0000\u1112\u0000\u1112\u1161\u0000\u1114\u0000\u1115\u0000\u111A\u0000\u111C\u0000\u111D\u0000\u111E\u0000"d~ -"\u1120\u0000\u1121\u0000\u1122\u0000\u1123\u0000\u1127\u0000\u1129\u0000\u112B\u0000\u112C\u0000\u112D\u0000\u112E"d~ -"\u0000\u112F\u0000\u1132\u0000\u1136\u0000\u1140\u0000\u1147\u0000\u114C\u0000\u1157\u0000\u1158\u0000\u1159\u0000"d~ -"\u1160\u0000\u1161\u0000\u1162\u0000\u1163\u0000\u1164\u0000\u1165\u0000\u1166\u0000\u1167\u0000\u1168\u0000\u1169"d~ -"\u0000\u116A\u0000\u116B\u0000\u116C\u0000\u116D\u0000\u116E\u0000\u116F\u0000\u1170\u0000\u1171\u0000\u1172\u0000"d~ -"\u1173\u0000\u1174\u0000\u1175\u0000\u1184\u0000\u1185\u0000\u1188\u0000\u1191\u0000\u1192\u0000\u1194\u0000\u119E"d~ -"\u0000\u11A1\u0000\u11AA\u0000\u11AC\u0000\u11AD\u0000\u11B0\u0000\u11B1\u0000\u11B2\u0000\u11B3\u0000\u11B4\u0000"d~ -"\u11B5\u0000\u11C7\u0000\u11C8\u0000\u11CC\u0000\u11CE\u0000\u11D3\u0000\u11D7\u0000\u11D9\u0000\u11DD\u0000\u11DF"d~ -"\u0000\u11F1\u0000\u11F2\u0000\u1B05\u1B35\u0000\u1B07\u1B35\u0000\u1B09\u1B35\u0000\u1B0B\u1B35\u0000\u1B0D\u1B35"d~ -"\u0000\u1B11\u1B35\u0000\u1B3A\u1B35\u0000\u1B3C\u1B35\u0000\u1B3E\u1B35\u0000\u1B3F\u1B35\u0000\u1B42\u1B35\u0000"d~ -"\u1D02\u0000\u1D16\u0000\u1D17\u0000\u1D1C\u0000\u1D1D\u0000\u1D25\u0000\u1D7B\u0000\u1D85\u0000\u1D91\u0000\u2010"d~ -"\u0000\u2013\u0000\u2014\u0000\u2032\u2032\u0000\u2032\u2032\u2032\u0000\u2032\u2032\u2032\u2032\u0000\u2035\u2035"d~ -"\u0000\u2035\u2035\u2035\u0000\u20A9\u0000\u2190\u0000\u2190\u0338\u0000\u2191\u0000\u2192\u0000\u2192\u0338\u0000"d~ -"\u2193\u0000\u2194\u0338\u0000\u21D0\u0338\u0000\u21D2\u0338\u0000\u21D4\u0338\u0000\u2202\u0000\u2203\u0338\u0000"d~ -"\u2207\u0000\u2208\u0338\u0000\u220B\u0338\u0000\u2211\u0000\u2212\u0000\u2223\u0338\u0000\u2225\u0338\u0000\u222B"d~ -"\u222B\u0000\u222B\u222B\u222B\u0000\u222B\u222B\u222B\u222B\u0000\u222E\u222E\u0000\u222E\u222E\u222E\u0000\u223C"d~ -"\u0338\u0000\u2243\u0338\u0000\u2245\u0338\u0000\u2248\u0338\u0000\u224D\u0338\u0000\u2261\u0338\u0000\u2264\u0338"d~ -"\u0000\u2265\u0338\u0000\u2272\u0338\u0000\u2273\u0338\u0000\u2276\u0338\u0000\u2277\u0338\u0000\u227A\u0338\u0000"d~ -"\u227B\u0338\u0000\u227C\u0338\u0000\u227D\u0338\u0000\u2282\u0338\u0000\u2283\u0338\u0000\u2286\u0338\u0000\u2287"d~ -"\u0338\u0000\u2291\u0338\u0000\u2292\u0338\u0000\u22A2\u0338\u0000\u22A8\u0338\u0000\u22A9\u0338\u0000\u22AB\u0338"d~ -"\u0000\u22B2\u0338\u0000\u22B3\u0338\u0000\u22B4\u0338\u0000\u22B5\u0338\u0000\u2502\u0000\u25A0\u0000\u25CB\u0000"d~ -"\u2985\u0000\u2986\u0000\u2ADD\u0338\u0000\u2C71\u0000\u2D61\u0000\u3001\u0000\u3002\u0000\u3008\u0000\u3009\u0000"d~ -"\u300A\u0000\u300B\u0000\u300C\u0000\u300D\u0000\u300E\u0000\u300F\u0000\u3010\u0000\u3011\u0000\u3012\u0000\u3014"d~ -"\u0000\u3014\u0053\u3015\u0000\u3014\u4E09\u3015\u0000\u3014\u4E8C\u3015\u0000\u3014\u52DD\u3015\u0000\u3014\u5B89"d~ -"\u3015\u0000\u3014\u6253\u3015\u0000\u3014\u6557\u3015\u0000\u3014\u672C\u3015\u0000\u3014\u70B9\u3015\u0000\u3014"d~ -"\u76D7\u3015\u0000\u3015\u0000\u3016\u0000\u3017\u0000\u3046\u3099\u0000\u304B\u3099\u0000\u304D\u3099\u0000\u304F"d~ -"\u3099\u0000\u3051\u3099\u0000\u3053\u3099\u0000\u3055\u3099\u0000\u3057\u3099\u0000\u3059\u3099\u0000\u305B\u3099"d~ -"\u0000\u305D\u3099\u0000\u305F\u3099\u0000\u3061\u3099\u0000\u3064\u3099\u0000\u3066\u3099\u0000\u3068\u3099\u0000"d~ -"\u306F\u3099\u0000\u306F\u309A\u0000\u3072\u3099\u0000\u3072\u309A\u0000\u3075\u3099\u0000\u3075\u309A\u0000\u3078"d~ -"\u3099\u0000\u3078\u309A\u0000\u307B\u304B\u0000\u307B\u3099\u0000\u307B\u309A\u0000\u3088\u308A\u0000\u3099\u0000"d~ -"\u309A\u0000\u309D\u3099\u0000\u30A1\u0000\u30A2\u0000\u30A2\u30CF\u309A\u30FC\u30C8\u0000\u30A2\u30EB\u30D5\u30A1"d~ -"\u0000\u30A2\u30F3\u30D8\u309A\u30A2\u0000\u30A2\u30FC\u30EB\u0000\u30A3\u0000\u30A4\u0000\u30A4\u30CB\u30F3\u30AF"d~ -"\u3099\u0000\u30A4\u30F3\u30C1\u0000\u30A5\u0000\u30A6\u0000\u30A6\u3099\u0000\u30A6\u30A9\u30F3\u0000\u30A7\u0000"d~ -"\u30A8\u0000\u30A8\u30B9\u30AF\u30FC\u30C8\u3099\u0000\u30A8\u30FC\u30AB\u30FC\u0000\u30A9\u0000\u30AA\u0000\u30AA"d~ -"\u30F3\u30B9\u0000\u30AA\u30FC\u30E0\u0000\u30AB\u0000\u30AB\u3099\u0000\u30AB\u3099\u30ED\u30F3\u0000\u30AB\u3099"d~ -"\u30F3\u30DE\u0000\u30AB\u30A4\u30EA\u0000\u30AB\u30E9\u30C3\u30C8\u0000\u30AB\u30ED\u30EA\u30FC\u0000\u30AD\u0000"d~ -"\u30AD\u3099\u0000\u30AD\u3099\u30AB\u3099\u0000\u30AD\u3099\u30CB\u30FC\u0000\u30AD\u3099\u30EB\u30BF\u3099\u30FC"d~ -"\u0000\u30AD\u30E5\u30EA\u30FC\u0000\u30AD\u30ED\u0000\u30AD\u30ED\u30AF\u3099\u30E9\u30E0\u0000\u30AD\u30ED\u30E1"d~ -"\u30FC\u30C8\u30EB\u0000\u30AD\u30ED\u30EF\u30C3\u30C8\u0000\u30AF\u0000\u30AF\u3099\u0000\u30AF\u3099\u30E9\u30E0"d~ -"\u0000\u30AF\u3099\u30E9\u30E0\u30C8\u30F3\u0000\u30AF\u30EB\u30BB\u3099\u30A4\u30ED\u0000\u30AF\u30ED\u30FC\u30CD"d~ -"\u0000\u30B1\u0000\u30B1\u3099\u0000\u30B1\u30FC\u30B9\u0000\u30B3\u0000\u30B3\u3099\u0000\u30B3\u30B3\u0000\u30B3"d~ -"\u30C8\u0000\u30B3\u30EB\u30CA\u0000\u30B3\u30FC\u30DB\u309A\u0000\u30B5\u0000\u30B5\u3099\u0000\u30B5\u30A4\u30AF"d~ -"\u30EB\u0000\u30B5\u30F3\u30C1\u30FC\u30E0\u0000\u30B7\u0000\u30B7\u3099\u0000\u30B7\u30EA\u30F3\u30AF\u3099\u0000"d~ -"\u30B9\u0000\u30B9\u3099\u0000\u30BB\u0000\u30BB\u3099\u0000\u30BB\u30F3\u30C1\u0000\u30BB\u30F3\u30C8\u0000\u30BD"d~ -"\u0000\u30BD\u3099\u0000\u30BF\u0000\u30BF\u3099\u0000\u30BF\u3099\u30FC\u30B9\u0000\u30C1\u0000\u30C1\u3099\u0000"d~ -"\u30C3\u0000\u30C4\u0000\u30C4\u3099\u0000\u30C6\u0000\u30C6\u3099\u0000\u30C6\u3099\u30B7\u0000\u30C8\u0000\u30C8"d~ -"\u3099\u0000\u30C8\u3099\u30EB\u0000\u30C8\u30F3\u0000\u30CA\u0000\u30CA\u30CE\u0000\u30CB\u0000\u30CC\u0000\u30CD"d~ -"\u0000\u30CE\u0000\u30CE\u30C3\u30C8\u0000\u30CF\u0000\u30CF\u3099\u0000\u30CF\u3099\u30FC\u30EC\u30EB\u0000\u30CF"d~ -"\u309A\u0000\u30CF\u309A\u30FC\u30BB\u30F3\u30C8\u0000\u30CF\u309A\u30FC\u30C4\u0000\u30CF\u30A4\u30C4\u0000\u30D2"d~ -"\u0000\u30D2\u3099\u0000\u30D2\u3099\u30EB\u0000\u30D2\u309A\u0000\u30D2\u309A\u30A2\u30B9\u30C8\u30EB\u0000\u30D2"d~ -"\u309A\u30AF\u30EB\u0000\u30D2\u309A\u30B3\u0000\u30D5\u0000\u30D5\u3099\u0000\u30D5\u3099\u30C3\u30B7\u30A7\u30EB"d~ -"\u0000\u30D5\u309A\u0000\u30D5\u30A1\u30E9\u30C3\u30C8\u3099\u0000\u30D5\u30A3\u30FC\u30C8\u0000\u30D5\u30E9\u30F3"d~ -"\u0000\u30D8\u0000\u30D8\u3099\u0000\u30D8\u3099\u30FC\u30BF\u0000\u30D8\u309A\u0000\u30D8\u309A\u30BD\u0000\u30D8"d~ -"\u309A\u30CB\u30D2\u0000\u30D8\u309A\u30F3\u30B9\u0000\u30D8\u309A\u30FC\u30B7\u3099\u0000\u30D8\u30AF\u30BF\u30FC"d~ -"\u30EB\u0000\u30D8\u30EB\u30C4\u0000\u30DB\u0000\u30DB\u3099\u0000\u30DB\u3099\u30EB\u30C8\u0000\u30DB\u309A\u0000"d~ -"\u30DB\u309A\u30A4\u30F3\u30C8\u0000\u30DB\u309A\u30F3\u30C8\u3099\u0000\u30DB\u30F3\u0000\u30DB\u30FC\u30EB\u0000"d~ -"\u30DB\u30FC\u30F3\u0000\u30DE\u0000\u30DE\u30A4\u30AF\u30ED\u0000\u30DE\u30A4\u30EB\u0000\u30DE\u30C3\u30CF\u0000"d~ -"\u30DE\u30EB\u30AF\u0000\u30DE\u30F3\u30B7\u30E7\u30F3\u0000\u30DF\u0000\u30DF\u30AF\u30ED\u30F3\u0000\u30DF\u30EA"d~ -"\u0000\u30DF\u30EA\u30CF\u3099\u30FC\u30EB\u0000\u30E0\u0000\u30E1\u0000\u30E1\u30AB\u3099\u0000\u30E1\u30AB\u3099"d~ -"\u30C8\u30F3\u0000\u30E1\u30FC\u30C8\u30EB\u0000\u30E2\u0000\u30E3\u0000\u30E4\u0000\u30E4\u30FC\u30C8\u3099\u0000"d~ -"\u30E4\u30FC\u30EB\u0000\u30E5\u0000\u30E6\u0000\u30E6\u30A2\u30F3\u0000\u30E7\u0000\u30E8\u0000\u30E9\u0000\u30EA"d~ -"\u0000\u30EA\u30C3\u30C8\u30EB\u0000\u30EA\u30E9\u0000\u30EB\u0000\u30EB\u30D2\u309A\u30FC\u0000\u30EB\u30FC\u30D5"d~ -"\u3099\u30EB\u0000\u30EC\u0000\u30EC\u30E0\u0000\u30EC\u30F3\u30C8\u30B1\u3099\u30F3\u0000\u30ED\u0000\u30EF\u0000"d~ -"\u30EF\u3099\u0000\u30EF\u30C3\u30C8\u0000\u30F0\u0000\u30F0\u3099\u0000\u30F1\u0000\u30F1\u3099\u0000\u30F2\u0000"d~ -"\u30F2\u3099\u0000\u30F3\u0000\u30FB\u0000\u30FC\u0000\u30FD\u3099\u0000\u349E\u0000\u34B9\u0000\u34BB\u0000\u34DF"d~ -"\u0000\u3515\u0000\u36EE\u0000\u36FC\u0000\u3781\u0000\u382F\u0000\u3862\u0000\u387C\u0000\u38C7\u0000\u38E3\u0000"d~ -"\u391C\u0000\u393A\u0000\u3A2E\u0000\u3A6C\u0000\u3AE4\u0000\u3B08\u0000\u3B19\u0000\u3B49\u0000\u3B9D\u0000\u3C18"d~ -"\u0000\u3C4E\u0000\u3D33\u0000\u3D96\u0000\u3EAC\u0000\u3EB8\u0000\u3F1B\u0000\u3FFC\u0000\u4008\u0000\u4018\u0000"d~ -"\u4039\u0000\u4046\u0000\u4096\u0000\u40E3\u0000\u412F\u0000\u4202\u0000\u4227\u0000\u42A0\u0000\u4301\u0000\u4334"d~ -"\u0000\u4359\u0000\u43D5\u0000\u43D9\u0000\u440B\u0000\u446B\u0000\u452B\u0000\u455D\u0000\u4561\u0000\u456B\u0000"d~ -"\u45D7\u0000\u45F9\u0000\u4635\u0000\u46BE\u0000\u46C7\u0000\u4995\u0000\u49E6\u0000\u4A6E\u0000\u4A76\u0000\u4AB2"d~ -"\u0000\u4B33\u0000\u4BCE\u0000\u4CCE\u0000\u4CED\u0000\u4CF8\u0000\u4D56\u0000\u4E00\u0000\u4E01\u0000\u4E03\u0000"d~ -"\u4E09\u0000\u4E0A\u0000\u4E0B\u0000\u4E0D\u0000\u4E19\u0000\u4E26\u0000\u4E28\u0000\u4E2D\u0000\u4E32\u0000\u4E36"d~ -"\u0000\u4E38\u0000\u4E39\u0000\u4E3D\u0000\u4E3F\u0000\u4E41\u0000\u4E59\u0000\u4E5D\u0000\u4E82\u0000\u4E85\u0000"d~ -"\u4E86\u0000\u4E8C\u0000\u4E94\u0000\u4EA0\u0000\u4EA4\u0000\u4EAE\u0000\u4EBA\u0000\u4EC0\u0000\u4ECC\u0000\u4EE4"d~ -"\u0000\u4EE4\u548C\u0000\u4F01\u0000\u4F11\u0000\u4F60\u0000\u4F80\u0000\u4F86\u0000\u4F8B\u0000\u4FAE\u0000\u4FBB"d~ -"\u0000\u4FBF\u0000\u5002\u0000\u502B\u0000\u507A\u0000\u5099\u0000\u50CF\u0000\u50DA\u0000\u50E7\u0000\u512A\u0000"d~ -"\u513F\u0000\u5140\u0000\u5145\u0000\u514D\u0000\u5154\u0000\u5164\u0000\u5165\u0000\u5167\u0000\u5168\u0000\u5169"d~ -"\u0000\u516B\u0000\u516D\u0000\u5177\u0000\u5180\u0000\u5182\u0000\u518D\u0000\u5192\u0000\u5195\u0000\u5196\u0000"d~ -"\u5197\u0000\u5199\u0000\u51A4\u0000\u51AB\u0000\u51AC\u0000\u51B5\u0000\u51B7\u0000\u51C9\u0000\u51CC\u0000\u51DC"d~ -"\u0000\u51DE\u0000\u51E0\u0000\u51F5\u0000\u5200\u0000\u5203\u0000\u5207\u0000\u5217\u0000\u521D\u0000\u5229\u0000"d~ -"\u523A\u0000\u523B\u0000\u5246\u0000\u524D\u0000\u5272\u0000\u5277\u0000\u5289\u0000\u529B\u0000\u52A3\u0000\u52B3"d~ -"\u0000\u52B4\u0000\u52C7\u0000\u52C9\u0000\u52D2\u0000\u52DE\u0000\u52E4\u0000\u52F5\u0000\u52F9\u0000\u52FA\u0000"d~ -"\u5305\u0000\u5306\u0000\u5315\u0000\u5317\u0000\u531A\u0000\u5338\u0000\u533B\u0000\u533F\u0000\u5341\u0000\u5344"d~ -"\u0000\u5345\u0000\u5349\u0000\u5351\u0000\u5354\u0000\u535A\u0000\u535C\u0000\u5369\u0000\u5370\u0000\u5373\u0000"d~ -"\u5375\u0000\u537D\u0000\u537F\u0000\u5382\u0000\u53B6\u0000\u53C3\u0000\u53C8\u0000\u53CA\u0000\u53CC\u0000\u53DF"d~ -"\u0000\u53E3\u0000\u53E5\u0000\u53EB\u0000\u53EF\u0000\u53F1\u0000\u53F3\u0000\u5406\u0000\u5408\u0000\u540D\u0000"d~ -"\u540F\u0000\u541D\u0000\u5438\u0000\u5439\u0000\u5442\u0000\u5448\u0000\u5468\u0000\u549E\u0000\u54A2\u0000\u54BD"d~ -"\u0000\u54F6\u0000\u5510\u0000\u554F\u0000\u5553\u0000\u5555\u0000\u5563\u0000\u5584\u0000\u5587\u0000\u5599\u0000"d~ -"\u559D\u0000\u55AB\u0000\u55B3\u0000\u55B6\u0000\u55C0\u0000\u55C2\u0000\u55E2\u0000\u5606\u0000\u5651\u0000\u5668"d~ -"\u0000\u5674\u0000\u56D7\u0000\u56DB\u0000\u56F9\u0000\u5716\u0000\u5717\u0000\u571F\u0000\u5730\u0000\u578B\u0000"d~ -"\u57CE\u0000\u57F4\u0000\u580D\u0000\u5831\u0000\u5832\u0000\u5840\u0000\u585A\u0000\u585E\u0000\u58A8\u0000\u58AC"d~ -"\u0000\u58B3\u0000\u58D8\u0000\u58DF\u0000\u58EB\u0000\u58EE\u0000\u58F0\u0000\u58F2\u0000\u58F7\u0000\u5902\u0000"d~ -"\u5906\u0000\u590A\u0000\u5915\u0000\u591A\u0000\u591C\u0000\u5922\u0000\u5927\u0000\u5927\u6B63\u0000\u5929\u0000"d~ -"\u5944\u0000\u5948\u0000\u5951\u0000\u5954\u0000\u5962\u0000\u5973\u0000\u59D8\u0000\u59EC\u0000\u5A1B\u0000\u5A27"d~ -"\u0000\u5A62\u0000\u5A66\u0000\u5AB5\u0000\u5B08\u0000\u5B28\u0000\u5B3E\u0000\u5B50\u0000\u5B57\u0000\u5B66\u0000"d~ -"\u5B80\u0000\u5B85\u0000\u5B97\u0000\u5BC3\u0000\u5BD8\u0000\u5BE7\u0000\u5BEE\u0000\u5BF3\u0000\u5BF8\u0000\u5BFF"d~ -"\u0000\u5C06\u0000\u5C0F\u0000\u5C22\u0000\u5C38\u0000\u5C3F\u0000\u5C60\u0000\u5C62\u0000\u5C64\u0000\u5C65\u0000"d~ -"\u5C6E\u0000\u5C71\u0000\u5C8D\u0000\u5CC0\u0000\u5D19\u0000\u5D43\u0000\u5D50\u0000\u5D6B\u0000\u5D6E\u0000\u5D7C"d~ -"\u0000\u5DB2\u0000\u5DBA\u0000\u5DDB\u0000\u5DE1\u0000\u5DE2\u0000\u5DE5\u0000\u5DE6\u0000\u5DF1\u0000\u5DFD\u0000"d~ -"\u5DFE\u0000\u5E28\u0000\u5E3D\u0000\u5E69\u0000\u5E72\u0000\u5E73\u6210\u0000\u5E74\u0000\u5E7A\u0000\u5E7C\u0000"d~ -"\u5E7F\u0000\u5EA6\u0000\u5EB0\u0000\u5EB3\u0000\u5EB6\u0000\u5EC9\u0000\u5ECA\u0000\u5ED2\u0000\u5ED3\u0000\u5ED9"d~ -"\u0000\u5EEC\u0000\u5EF4\u0000\u5EFE\u0000\u5F04\u0000\u5F0B\u0000\u5F13\u0000\u5F22\u0000\u5F50\u0000\u5F53\u0000"d~ -"\u5F61\u0000\u5F62\u0000\u5F69\u0000\u5F6B\u0000\u5F73\u0000\u5F8B\u0000\u5F8C\u0000\u5F97\u0000\u5F9A\u0000\u5FA9"d~ -"\u0000\u5FAD\u0000\u5FC3\u0000\u5FCD\u0000\u5FD7\u0000\u5FF5\u0000\u5FF9\u0000\u6012\u0000\u601C\u0000\u6075\u0000"d~ -"\u6081\u0000\u6094\u0000\u60C7\u0000\u60D8\u0000\u60E1\u0000\u6108\u0000\u6144\u0000\u6148\u0000\u614C\u0000\u614E"d~ -"\u0000\u6160\u0000\u6168\u0000\u617A\u0000\u618E\u0000\u6190\u0000\u61A4\u0000\u61AF\u0000\u61B2\u0000\u61DE\u0000"d~ -"\u61F2\u0000\u61F6\u0000\u6200\u0000\u6208\u0000\u6210\u0000\u621B\u0000\u622E\u0000\u6234\u0000\u6236\u0000\u624B"d~ -"\u0000\u6253\u0000\u625D\u0000\u6295\u0000\u62B1\u0000\u62C9\u0000\u62CF\u0000\u62D3\u0000\u62D4\u0000\u62FC\u0000"d~ -"\u62FE\u0000\u6307\u0000\u633D\u0000\u6350\u0000\u6355\u0000\u6368\u0000\u637B\u0000\u6383\u0000\u63A0\u0000\u63A9"d~ -"\u0000\u63C4\u0000\u63C5\u0000\u63E4\u0000\u641C\u0000\u6422\u0000\u6452\u0000\u6469\u0000\u6477\u0000\u647E\u0000"d~ -"\u649A\u0000\u649D\u0000\u64C4\u0000\u652F\u0000\u6534\u0000\u654F\u0000\u6556\u0000\u656C\u0000\u6578\u0000\u6587"d~ -"\u0000\u6597\u0000\u6599\u0000\u65A4\u0000\u65B0\u0000\u65B9\u0000\u65C5\u0000\u65E0\u0000\u65E2\u0000\u65E3\u0000"d~ -"\u65E5\u0000\u660E\u6CBB\u0000\u6613\u0000\u6620\u0000\u662D\u548C\u0000\u6649\u0000\u6674\u0000\u6688\u0000\u6691"d~ -"\u0000\u669C\u0000\u66B4\u0000\u66C6\u0000\u66F0\u0000\u66F4\u0000\u66F8\u0000\u6700\u0000\u6708\u0000\u6709\u0000"d~ -"\u6717\u0000\u671B\u0000\u6721\u0000\u6728\u0000\u674E\u0000\u6753\u0000\u6756\u0000\u675E\u0000\u677B\u0000\u6785"d~ -"\u0000\u6797\u0000\u67F3\u0000\u67FA\u0000\u6817\u0000\u681F\u0000\u682A\u0000\u682A\u5F0F\u4F1A\u793E\u0000\u6852"d~ -"\u0000\u6881\u0000\u6885\u0000\u688E\u0000\u68A8\u0000\u6914\u0000\u6942\u0000\u69A3\u0000\u69EA\u0000\u6A02\u0000"d~ -"\u6A13\u0000\u6AA8\u0000\u6AD3\u0000\u6ADB\u0000\u6B04\u0000\u6B20\u0000\u6B21\u0000\u6B54\u0000\u6B62\u0000\u6B63"d~ -"\u0000\u6B72\u0000\u6B77\u0000\u6B79\u0000\u6B9F\u0000\u6BAE\u0000\u6BB3\u0000\u6BBA\u0000\u6BBB\u0000\u6BCB\u0000"d~ -"\u6BCD\u0000\u6BD4\u0000\u6BDB\u0000\u6C0F\u0000\u6C14\u0000\u6C34\u0000\u6C4E\u0000\u6C67\u0000\u6C88\u0000\u6CBF"d~ -"\u0000\u6CCC\u0000\u6CCD\u0000\u6CE5\u0000\u6CE8\u0000\u6D16\u0000\u6D1B\u0000\u6D1E\u0000\u6D34\u0000\u6D3E\u0000"d~ -"\u6D41\u0000\u6D69\u0000\u6D6A\u0000\u6D77\u0000\u6D78\u0000\u6D85\u0000\u6DCB\u0000\u6DDA\u0000\u6DEA\u0000\u6DF9"d~ -"\u0000\u6E1A\u0000\u6E2F\u0000\u6E6E\u0000\u6E80\u0000\u6E9C\u0000\u6EBA\u0000\u6EC7\u0000\u6ECB\u0000\u6ED1\u0000"d~ -"\u6EDB\u0000\u6F0F\u0000\u6F14\u0000\u6F22\u0000\u6F23\u0000\u6F6E\u0000\u6FC6\u0000\u6FEB\u0000\u6FFE\u0000\u701B"d~ -"\u0000\u701E\u0000\u7039\u0000\u704A\u0000\u706B\u0000\u7070\u0000\u7077\u0000\u707D\u0000\u7099\u0000\u70AD\u0000"d~ -"\u70C8\u0000\u70D9\u0000\u7121\u0000\u7145\u0000\u7149\u0000\u716E\u0000\u719C\u0000\u71CE\u0000\u71D0\u0000\u7210"d~ -"\u0000\u721B\u0000\u7228\u0000\u722A\u0000\u722B\u0000\u7235\u0000\u7236\u0000\u723B\u0000\u723F\u0000\u7247\u0000"d~ -"\u7250\u0000\u7259\u0000\u725B\u0000\u7262\u0000\u7279\u0000\u7280\u0000\u7295\u0000\u72AC\u0000\u72AF\u0000\u72C0"d~ -"\u0000\u72FC\u0000\u732A\u0000\u7375\u0000\u737A\u0000\u7384\u0000\u7387\u0000\u7389\u0000\u738B\u0000\u73A5\u0000"d~ -"\u73B2\u0000\u73DE\u0000\u7406\u0000\u7409\u0000\u7422\u0000\u7447\u0000\u745C\u0000\u7469\u0000\u7471\u0000\u7485"d~ -"\u0000\u7489\u0000\u7498\u0000\u74CA\u0000\u74DC\u0000\u74E6\u0000\u7506\u0000\u7518\u0000\u751F\u0000\u7524\u0000"d~ -"\u7528\u0000\u7530\u0000\u7532\u0000\u7533\u0000\u7537\u0000\u753B\u0000\u753E\u0000\u7559\u0000\u7565\u0000\u7570"d~ -"\u0000\u758B\u0000\u7592\u0000\u75E2\u0000\u7610\u0000\u761D\u0000\u761F\u0000\u7642\u0000\u7669\u0000\u7676\u0000"d~ -"\u767D\u0000\u76AE\u0000\u76BF\u0000\u76CA\u0000\u76DB\u0000\u76E3\u0000\u76E7\u0000\u76EE\u0000\u76F4\u0000\u7701"d~ -"\u0000\u771E\u0000\u771F\u0000\u7740\u0000\u774A\u0000\u778B\u0000\u77A7\u0000\u77DB\u0000\u77E2\u0000\u77F3\u0000"d~ -"\u784E\u0000\u786B\u0000\u788C\u0000\u7891\u0000\u78CA\u0000\u78CC\u0000\u78FB\u0000\u792A\u0000\u793A\u0000\u793C"d~ -"\u0000\u793E\u0000\u7948\u0000\u7949\u0000\u7950\u0000\u7956\u0000\u795D\u0000\u795E\u0000\u7965\u0000\u797F\u0000"d~ -"\u7981\u0000\u798D\u0000\u798E\u0000\u798F\u0000\u79AE\u0000\u79B8\u0000\u79BE\u0000\u79CA\u0000\u79D8\u0000\u79EB"d~ -"\u0000\u7A1C\u0000\u7A40\u0000\u7A4A\u0000\u7A4F\u0000\u7A74\u0000\u7A7A\u0000\u7A81\u0000\u7AB1\u0000\u7ACB\u0000"d~ -"\u7AEE\u0000\u7AF9\u0000\u7B20\u0000\u7B8F\u0000\u7BC0\u0000\u7BC6\u0000\u7BC9\u0000\u7C3E\u0000\u7C60\u0000\u7C73"d~ -"\u0000\u7C7B\u0000\u7C92\u0000\u7CBE\u0000\u7CD2\u0000\u7CD6\u0000\u7CE3\u0000\u7CE7\u0000\u7CE8\u0000\u7CF8\u0000"d~ -"\u7D00\u0000\u7D10\u0000\u7D22\u0000\u7D2F\u0000\u7D42\u0000\u7D5B\u0000\u7D63\u0000\u7DA0\u0000\u7DBE\u0000\u7DC7"d~ -"\u0000\u7DF4\u0000\u7E02\u0000\u7E09\u0000\u7E37\u0000\u7E41\u0000\u7E45\u0000\u7F36\u0000\u7F3E\u0000\u7F51\u0000"d~ -"\u7F72\u0000\u7F79\u0000\u7F7A\u0000\u7F85\u0000\u7F8A\u0000\u7F95\u0000\u7F9A\u0000\u7FBD\u0000\u7FFA\u0000\u8001"d~ -"\u0000\u8005\u0000\u800C\u0000\u8012\u0000\u8033\u0000\u8046\u0000\u8060\u0000\u806F\u0000\u8070\u0000\u807E\u0000"d~ -"\u807F\u0000\u8089\u0000\u808B\u0000\u80AD\u0000\u80B2\u0000\u8103\u0000\u813E\u0000\u81D8\u0000\u81E3\u0000\u81E8"d~ -"\u0000\u81EA\u0000\u81ED\u0000\u81F3\u0000\u81FC\u0000\u8201\u0000\u8204\u0000\u820C\u0000\u8218\u0000\u821B\u0000"d~ -"\u821F\u0000\u826E\u0000\u826F\u0000\u8272\u0000\u8278\u0000\u8279\u0000\u828B\u0000\u8291\u0000\u829D\u0000\u82B1"d~ -"\u0000\u82B3\u0000\u82BD\u0000\u82E5\u0000\u82E6\u0000\u831D\u0000\u8323\u0000\u8336\u0000\u8352\u0000\u8353\u0000"d~ -"\u8363\u0000\u83AD\u0000\u83BD\u0000\u83C9\u0000\u83CA\u0000\u83CC\u0000\u83DC\u0000\u83E7\u0000\u83EF\u0000\u83F1"d~ -"\u0000\u843D\u0000\u8449\u0000\u8457\u0000\u84EE\u0000\u84F1\u0000\u84F3\u0000\u84FC\u0000\u8516\u0000\u8564\u0000"d~ -"\u85CD\u0000\u85FA\u0000\u8606\u0000\u8612\u0000\u862D\u0000\u863F\u0000\u864D\u0000\u8650\u0000\u865C\u0000\u8667"d~ -"\u0000\u8669\u0000\u866B\u0000\u8688\u0000\u86A9\u0000\u86E2\u0000\u870E\u0000\u8728\u0000\u876B\u0000\u8779\u0000"d~ -"\u8786\u0000\u87BA\u0000\u87E1\u0000\u8801\u0000\u881F\u0000\u8840\u0000\u884C\u0000\u8860\u0000\u8863\u0000\u88C2"d~ -"\u0000\u88CF\u0000\u88D7\u0000\u88DE\u0000\u88E1\u0000\u88F8\u0000\u88FA\u0000\u8910\u0000\u8941\u0000\u8964\u0000"d~ -"\u897E\u0000\u8986\u0000\u898B\u0000\u8996\u0000\u89D2\u0000\u89E3\u0000\u8A00\u0000\u8AA0\u0000\u8AAA\u0000\u8ABF"d~ -"\u0000\u8ACB\u0000\u8AD2\u0000\u8AD6\u0000\u8AED\u0000\u8AF8\u0000\u8AFE\u0000\u8B01\u0000\u8B39\u0000\u8B58\u0000"d~ -"\u8B80\u0000\u8B8A\u0000\u8C37\u0000\u8C46\u0000\u8C48\u0000\u8C55\u0000\u8C78\u0000\u8C9D\u0000\u8CA1\u0000\u8CA9"d~ -"\u0000\u8CAB\u0000\u8CC1\u0000\u8CC2\u0000\u8CC7\u0000\u8CC8\u0000\u8CD3\u0000\u8D08\u0000\u8D1B\u0000\u8D64\u0000"d~ -"\u8D70\u0000\u8D77\u0000\u8DB3\u0000\u8DBC\u0000\u8DCB\u0000\u8DEF\u0000\u8DF0\u0000\u8EAB\u0000\u8ECA\u0000\u8ED4"d~ -"\u0000\u8F26\u0000\u8F2A\u0000\u8F38\u0000\u8F3B\u0000\u8F62\u0000\u8F9B\u0000\u8F9E\u0000\u8FB0\u0000\u8FB5\u0000"d~ -"\u8FB6\u0000\u9023\u0000\u9038\u0000\u904A\u0000\u9069\u0000\u9072\u0000\u907C\u0000\u908F\u0000\u9091\u0000\u9094"d~ -"\u0000\u90CE\u0000\u90DE\u0000\u90F1\u0000\u90FD\u0000\u9111\u0000\u911B\u0000\u9149\u0000\u914D\u0000\u916A\u0000"d~ -"\u9199\u0000\u91B4\u0000\u91C6\u0000\u91CC\u0000\u91CF\u0000\u91D1\u0000\u9234\u0000\u9238\u0000\u9276\u0000\u927C"d~ -"\u0000\u92D7\u0000\u92D8\u0000\u9304\u0000\u934A\u0000\u93F9\u0000\u9415\u0000\u9577\u0000\u9580\u0000\u958B\u0000"d~ -"\u95AD\u0000\u95B7\u0000\u961C\u0000\u962E\u0000\u964B\u0000\u964D\u0000\u9675\u0000\u9678\u0000\u967C\u0000\u9686"d~ -"\u0000\u96A3\u0000\u96B6\u0000\u96B7\u0000\u96B8\u0000\u96B9\u0000\u96C3\u0000\u96E2\u0000\u96E3\u0000\u96E8\u0000"d~ -"\u96F6\u0000\u96F7\u0000\u9723\u0000\u9732\u0000\u9748\u0000\u9751\u0000\u9756\u0000\u975E\u0000\u9762\u0000\u9769"d~ -"\u0000\u97CB\u0000\u97DB\u0000\u97E0\u0000\u97ED\u0000\u97F3\u0000\u97FF\u0000\u9801\u0000\u9805\u0000\u980B\u0000"d~ -"\u9818\u0000\u9829\u0000\u983B\u0000\u985E\u0000\u98A8\u0000\u98DB\u0000\u98DF\u0000\u98E2\u0000\u98EF\u0000\u98FC"d~ -"\u0000\u9928\u0000\u9929\u0000\u9996\u0000\u9999\u0000\u99A7\u0000\u99AC\u0000\u99C2\u0000\u99F1\u0000\u99FE\u0000"d~ -"\u9A6A\u0000\u9AA8\u0000\u9AD8\u0000\u9ADF\u0000\u9B12\u0000\u9B25\u0000\u9B2F\u0000\u9B32\u0000\u9B3C\u0000\u9B5A"d~ -"\u0000\u9B6F\u0000\u9C40\u0000\u9C57\u0000\u9CE5\u0000\u9CFD\u0000\u9D67\u0000\u9DB4\u0000\u9DFA\u0000\u9E1E\u0000"d~ -"\u9E75\u0000\u9E7F\u0000\u9E97\u0000\u9E9F\u0000\u9EA5\u0000\u9EBB\u0000\u9EC3\u0000\u9ECD\u0000\u9ECE\u0000\u9ED1"d~ -"\u0000\u9EF9\u0000\u9EFD\u0000\u9EFE\u0000\u9F05\u0000\u9F0E\u0000\u9F0F\u0000\u9F13\u0000\u9F16\u0000\u9F20\u0000"d~ -"\u9F3B\u0000\u9F43\u0000\u9F4A\u0000\u9F52\u0000\u9F8D\u0000\u9F8E\u0000\u9F9C\u0000\u9F9F\u0000\u9FA0\u0000\uA651"d~ -"\u0000\uA689\u0000\uA727\u0000\uA76F\u0000\uA78E\u0000\uAB37\u0000\uAB52\u0000\uAB66\u0000\uAB67\u0000\U00011099"d~ -"\U000110BA\u0000\U0001109B\U000110BA\u0000\U000110A5\U000110BA\u0000\U00011131\U00011127\u0000\U00011132\U00011127"d~ -"\u0000\U00011347\U0001133E\u0000\U00011347\U00011357\u0000\U000114B9\U000114B0\u0000\U000114B9\U000114BA\u0000"d~ -"\U000114B9\U000114BD\u0000\U000115B8\U000115AF\u0000\U000115B9\U000115AF\u0000\U00011935\U00011930\u0000\U0001D157"d~ -"\U0001D165\u0000\U0001D158\U0001D165\u0000\U0001D158\U0001D165\U0001D16E\u0000\U0001D158\U0001D165\U0001D16F\u0000"d~ -"\U0001D158\U0001D165\U0001D170\u0000\U0001D158\U0001D165\U0001D171\u0000\U0001D158\U0001D165\U0001D172\u0000\U0001D1B9"d~ -"\U0001D165\u0000\U0001D1B9\U0001D165\U0001D16E\u0000\U0001D1B9\U0001D165\U0001D16F\u0000\U0001D1BA\U0001D165\u0000"d~ -"\U0001D1BA\U0001D165\U0001D16E\u0000\U0001D1BA\U0001D165\U0001D16F\u0000\U0001DF04\u0000\U0001DF05\u0000\U0001DF06"d~ -"\u0000\U0001DF08\u0000\U0001DF0A\u0000\U0001DF1E\u0000\U00020122\u0000\U0002051C\u0000\U00020525\u0000\U0002054B"d~ -"\u0000\U0002063A\u0000\U00020804\u0000\U000208DE\u0000\U00020A2C\u0000\U00020B63\u0000\U000214E4\u0000\U000216A8"d~ -"\u0000\U000216EA\u0000\U000219C8\u0000\U00021B18\u0000\U00021D0B\u0000\U00021DE4\u0000\U00021DE6\u0000\U00022183"d~ -"\u0000\U0002219F\u0000\U00022331\u0000\U000226D4\u0000\U00022844\u0000\U0002284A\u0000\U00022B0C\u0000\U00022BF1"d~ -"\u0000\U0002300A\u0000\U000232B8\u0000\U0002335F\u0000\U00023393\u0000\U0002339C\u0000\U000233C3\u0000\U000233D5"d~ -"\u0000\U0002346D\u0000\U000236A3\u0000\U000238A7\u0000\U00023A8D\u0000\U00023AFA\u0000\U00023CBC\u0000\U00023D1E"d~ -"\u0000\U00023ED1\u0000\U00023F5E\u0000\U00023F8E\u0000\U00024263\u0000\U000242EE\u0000\U000243AB\u0000\U00024608"d~ -"\u0000\U00024735\u0000\U00024814\u0000\U00024C36\u0000\U00024C92\u0000\U00024FA1\u0000\U00024FB8\u0000\U00025044"d~ -"\u0000\U000250F2\u0000\U000250F3\u0000\U00025119\u0000\U00025133\u0000\U00025249\u0000\U0002541D\u0000\U00025626"d~ -"\u0000\U0002569A\u0000\U000256C5\u0000\U0002597C\u0000\U00025AA7\u0000\U00025BAB\u0000\U00025C80\u0000\U00025CD0"d~ -"\u0000\U00025F86\u0000\U000261DA\u0000\U00026228\u0000\U00026247\u0000\U000262D9\u0000\U0002633E\u0000\U000264DA"d~ -"\u0000\U00026523\u0000\U000265A8\u0000\U000267A7\u0000\U000267B5\u0000\U00026B3C\u0000\U00026C36\u0000\U00026CD5"d~ -"\u0000\U00026D6B\u0000\U00026F2C\u0000\U00026FB1\u0000\U000270D2\u0000\U000273CA\u0000\U00027667\u0000\U000278AE"d~ -"\u0000\U00027966\u0000\U00027CA8\u0000\U00027ED3\u0000\U00027F2F\u0000\U000285D2\u0000\U000285ED\u0000\U0002872E"d~ -"\u0000\U00028BFA\u0000\U00028D77\u0000\U00029145\u0000\U000291DF\u0000\U0002921A\u0000\U0002940A\u0000\U00029496"d~ -"\u0000\U000295B6\u0000\U00029B30\u0000\U0002A0CE\u0000\U0002A105\u0000\U0002A20E\u0000\U0002A291\u0000\U0002A392"d~ -"\u0000\U0002A600\u0000"d; -return t[]; -} - -} - diff --git a/phobos/std/internal/unicode_grapheme.d b/phobos/std/internal/unicode_grapheme.d deleted file mode 100644 index ba80e18..0000000 --- a/phobos/std/internal/unicode_grapheme.d +++ /dev/null @@ -1,571 +0,0 @@ -//Written in the D programming language -/** - * License: $(HTTP boost.org/LICENSE_1_0.txt, Boost License 1.0). - * - * Authors: Dmitry Olshansky - * - */ -// !!! DO NOT EDIT !!! -// !!! Did you even read the comment? !!! -// This module is automatically generated from Unicode Character Database files -// https://github.com/dlang/phobos/blob/master/tools/unicode_table_generator.d -//dfmt off -module std.internal.unicode_grapheme; -import std.internal.unicode_tables; - -package(std): - - -static if (size_t.sizeof == 4) -{ -//832 bytes -enum hangulLVTrieEntries = TrieEntry!(bool, 8, 5, 8)(x" -000000000000004000000080", -x" -000001000000008000000A00", -x" -000000000002010000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000020001000400030006000500010007000300020005000400070006000200010004000300060005 -000100070003000200050004000700060002000100040003000600050001000700030002000500040007000600080001 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000001000000101000000001000000001000000001000000001000000001010000001 -010000000010000000010000000010000000010000000010100000010100000000100000000100000000100000000100 -000000101000000101000000001000000001000000001000000001000000001010000001010000000010000000010000 -000010000000010000000010100000010100000000100000000100000000100000000100000000101000000101000000 -001000000001000000001000000001000000001010000001010000000010000000010000000010000000010000000010 -010000000010000000010000000010000000010000000000000000000000000000000000000000000000000000000000 -00000000000000000000000000000000", -); -//832 bytes -enum hangulLVTTrieEntries = TrieEntry!(bool, 8, 5, 8)(x" -000000000000004000000080", -x" -000001000000008000000A00", -x" -000000000002010000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000020001000400030006000500010007000300020005000400070006000200010004000300060005 -000100070003000200050004000700060002000100040003000600050001000700030002000500040007000600080001 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -00000000000000000000000000000000EFFFFFFEFEFFFFFFFFEFFFFFFFFEFFFFFFFFEFFFFFFFFEFFFFFFFFEFEFFFFFFE -FEFFFFFFFFEFFFFFFFFEFFFFFFFFEFFFFFFFFEFFFFFFFFEFEFFFFFFEFEFFFFFFFFEFFFFFFFFEFFFFFFFFEFFFFFFFFEFF -FFFFFFEFEFFFFFFEFEFFFFFFFFEFFFFFFFFEFFFFFFFFEFFFFFFFFEFFFFFFFFEFEFFFFFFEFEFFFFFFFFEFFFFFFFFEFFFF -FFFFEFFFFFFFFEFFFFFFFFEFEFFFFFFEFEFFFFFFFFEFFFFFFFFEFFFFFFFFEFFFFFFFFEFFFFFFFFEFEFFFFFFEFEFFFFFF -FFEFFFFFFFFEFFFFFFFFEFFFFFFFFEFFFFFFFFEFEFFFFFFEFEFFFFFFFFEFFFFFFFFEFFFFFFFFEFFFFFFFFEFFFFFFFFEF -FEFFFFFFFFEFFFFFFFFEFFFFFFFFEFFFFFFFFEFF0000000F000000000000000000000000000000000000000000000000 -00000000000000000000000000000000", -); -//896 bytes -enum prependTrieEntries = TrieEntry!(bool, 8, 5, 8)(x" -000000000000004000000080", -x" -000001000000008000000C00", -x" -010101000101010101010102010101010101010101010101010101010101010101010101010101010101010101010101 -010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101 -010101010101010101010101010101010101010101010101010101010101010101010101010101010000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000002000100000003000000000004000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000060005000000000000000000000000 -000700000000000800090000000A00000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000003F00000000000000000000000000000000000000002000000000000000 -000080000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000300000000000000000000000000040000000000000000000040000000000000000000000000000000000000000000 -000000000000000000000000000000000000000020000000000020000000000000000000000000000000000000000000 -00000000000000000000000C000000000000000080000000000000020000000000000000000000000000000000000000 -00000000040000000000000000000000000003F000000000000000000000000000000000000000000000004000000000 -000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000", -); -//1280 bytes -enum controlTrieEntries = TrieEntry!(bool, 8, 5, 8)(x" -0000000000000040000000D0", -x" -000001000000012000000E00", -x" -020201000302020202020402020605020202020202020202020202020202020202020202020202020202020202020202 -020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202 -020202020202020202020202020202020202020702020202020202020202020202020202020202020000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000001000000010001000100010001000200010001000100010001000100010001 -000100010001000100010001000100010001000300010001000100010001000100010004000100010001000100010001 -000100010001000100010001000100010001000100010001000100010001000100010001000100010001000100010001 -000100010001000100010001000100010001000100010001000100010001000100010001000100010001000100010001 -000100010001000100010001000100010001000100010001000100010001000100010001000100010001000100010001 -000100010001000100010001000100010001000100010001000100010006000500010001000100010001000100010001 -000100010001000100010001000100010001000100010001000100070001000100010001000100010001000100010001 -000100010001000100010001000100010001000100010001000100010001000100010001000100010001000100010001 -000100010001000100010008000100010001000100010001000100010001000100010001000100010001000100010001 -0009000100010001000100010001000100010001000100010001000100010001000B000A000C000C000C000C000C000C -000C000C000C000C000C000C000C000C0001000100010001000100010001000100010001000100010001000100010001 -000100010001000100010001000100010001000100010001000100010001000100010001000100010001000100010001 -00010001000100010001000100010001FFFFDBFF000000000000000080000000FFFFFFFF000020000000000000000000 -000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000 -000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000 -0000C80000007F00000000000000FFFF0000000000000000000000000000000000000000000000000000000000000000 -00000000000000000000000080000000000000000000000000000000000000000000000000000000000000000FFF0000 -00000000FFFF000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000F000000000000000000000000000000000000000007F8000000000000000000000000000000000000 -FFFFFFFF000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00000000000000000000000000000000 -000000000000000000000000FFFF0000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -0000000000000000000000000000000000000000000000000000000000000000", -); -//1856 bytes -enum spacingMarkTrieEntries = TrieEntry!(bool, 8, 5, 8)(x" -0000000000000040000000B0", -x" -00000100000000E000002400", -x" -010101000101020104010103010501010101010101010101010101010101010101010101010101010101010101010101 -010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101 -010101010101010101010101010101010101010101010101010101010101010101010101010101010000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000010000000300020005000400070006 -00000008000000000000000000090000000A0000000C000B0000000D0000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -00000000000000000000000000000000000F000E00110010000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -00130012001500140017001600190018001B001A0000001C001E001D0020001F00000000000000000000000000000000 -000000000000000000000000002100000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000220000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000008C80000000000DE01000000000000000C800000000000198100000000 -00000008C0000000000000010000000000000008C000000000001A01000000000000000C000000000000198100000000 -000000008000000000001DC6000000000000000E000000000000001E000000000000000C4000000000000D9B00080000 -0000000C8000000000001DC1000000000000000C000000007F030000000C000000000000000800000000000000000000 -0000000000080000000000000000000000000000C0000000000000008000000000000000000000000000000000000000 -000000001802000000C00000000000000000001000000000000000000000000000200000001000000000000000000000 -00000000C0400000000001BF000000000000000001FB0E78000000000000000000000000000000000000000000000000 -060000000000000000A000000007E0000000000000000000000000000000000000000010E80000000000001B00000000 -00000004000004C200000000000C5C800000000000300FF0000000000000000000000000000000000000000000800002 -0000000000000098000000000000000000000003FFF000000000000F000000000000000000000000000C000000000000 -00000008CC3000000000000100000000000000000019800000002000000000000000000000000000000000000020C800 -00000000000000000000000000000000000000000000000000000000000016D800000005000000000000000000000000 -000000040187000000000000000000000000000000001000000000600000000000000004803800000000400100000000 -00000000002C70000000000000000000000000000000000000000000000000070000000C800000000000399E0000000C -000000000000000000000000000000000000000000E000000000002300000000000000005A0600000000000200000000 -00000000000000000000000000000000000000004F030000000000000000000000000000580700000000000000000000 -000000000040D00000000000000000000000000000000040000000000000000000000000000000000000000000000000 -00000000010070000000000000000000000000000000000000000000000000000000000021BE00000000000500000000 -0000000000000000F00E0000000000100000000002000000018000000000000000800000000000000000000000000000 -000000004000800000000000000000000000000000120200000000000000000000000000000000000000000000000000 -00587C000000000000000000000000000000000000000000000000000000000000000000000000000000000000600000 -00000008C03000000000000200000000000000000000000000000000000000000000000000000000FFFE0000FFFFFFFF -000000FF0000000000000000000300000000000000000000000000000000204000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000", -); -//3488 bytes -enum graphemeExtendTrieEntries = TrieEntry!(bool, 8, 5, 8)(x" -000000000000004000000110", -x" -00000100000001A000004B00", -x" -0202010004020302070206050A0908020202020202020202020202020202020202020202020202020202020202020202 -020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202 -020202020202020202020202020202020202020B02020202020202020202020202020202020202020000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -00000000000000000000000000000000000000000001000000030002000500040007000600090008000B000A000D000C -0000000E000F000000000000001000000012001100140013001600150000000000000017000000000000000000000000 -000000000000000000190018000000000000001A00000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000001B001D001C001F001E0000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000002000000000000000220021 -0023000000250024000000000000000000000000000000260027000000290028002B002A002D002C002F002E00310030 -003300320000003400360035003800370000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000039000000000000000000000000000000000000000000000000000000000000000000000000 -00000000003B003A00000000003C00000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -00000000000000000000003D0000000000000000000000000000000000000000000000000000000000000000003E0000 -003F000000000040000000000000000000000000000000410000000000000000003B0042000000430000004400000000 -004600450000000000000000000000000000000000470000000000000000000000000000000000000000000000000000 -004900480000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -00000000000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFF0000FFFF00000000000000000000000000000000 -00000000000000000000000000000000000003F800000000000000000000000000000000000000000000000000000000 -FFFE0000BFFFFFFF000000B60000000007FF000000000000FFFFF8000001000000000000000000009FC0000000003D9F -00020000FFFF0000000007FF00000000000000000001FFC000000000200FF800FBC0000000003EEF0E00000000000000 -FF00000000000000FFFFFC00FFFFFFFB000000071400000000FE21FE0000000C00000002500000000080201E4000000C -000000061000000000023986002300000000000610000000000021BEFC00000C00000002D000000000E0201E0000000C -0000000440000000008020010000000000000011D000000000603DC10000000C0000000290000000006030440000000C -00000003580000000080201E0000000C0000000200000000805C8400000000000000000007F2000000007F8000000000 -000000001FF2000000007F00000000000300000002A00000000000007FFE0000FEFFE0DF1FFFFFFF0000004000000000 -0000000066FDE000C3000000001E0001200020640000000000000000000000000000000000000000E000000000000000 -00000000000000000000000000000000001C0000000C0000000C0000000C0000000000003FB00000200FFE4000000000 -0000B80000000000000000000000000000000060000002000000000000000000000000000E0401870000000000000000 -0000000000000000000000000000000009800000000000007F4000009FF81FE500000000FFFF000000007FFF00000000 -0000000F17F0000000000004000FF8000000000300003B3C000000000003A3400000000000CFF0000000000000000000 -0000000000000000FFF70000031021FD000000000000000000000000000000000000000000000000FFFFFFFFFFFFFFFF -000010000000000000000000000000000000000000000000FFFF00000001FFFF00000000000000000000000000000000 -0000000000000000000000000003800000000000000000000000000080000000000000000000000000000000FFFFFFFF -000000000000FC000000000000000000060000000000000000000000000000000000000000000000000000003FF78000 -C0000000000000000000000000030000000008440000106000000000000000000000000000000000000000308003FFFF -0000000000003FC00003FF80000000000000000733C8000000000000000000200000000000667E000000100810000000 -00000000C19D000000000002004030000000000000000000000000000000000000000000000000000000000000002120 -40000000000000000000000000000000000000000000000000000000000000000000FFFF0000FFFF0000000000000000 -0000000000000000000000000000000000000000000000000000000000000000C0000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000 -0000000000000000000000000000000100000000000000000000000007C0000000000000000000000000000000000000 -0000F06E8700000000000000000000000000000000000000000000000000006000000000000000F00000000000000000 -0000000000000000000000000000000000000000000000000000000000000000000000000000180000000000E0000000 -00000000000000000001FFC0000000000000003C00000000000000000000000000000002FF0000000000007F80190000 -0000000306780000000000040000000000000007001FEF800000000000080000000000037FC0000000009E0000000000 -0000000040D380000000000200000000000000000000000080000000000007F8000000035800000000800001001F1FC0 -0000000000000000000000000000000000000000FF0000004000005C0000000000000000A5F900000000000D00000000 -0000000000000000000000000000000000000000B03C8000300000010000000000000000A7F800000000000100000000 -0000000000BF28000000000000000000E000000000000FBC000000000000000000000000000000000000000000000000 -0000000006FF800000000000000000000000000000000000000000000000000000000000580100000000000800000000 -00000000000000000CF0000000000001000007FE79F800000E7E008000000000037FFC00000000000000000000000000 -00000000BF7F00000000000000000000FFFC0000006DFCFF000000000000000000000000B47E0000000000BF00000000 -00A300000000000000000000000000000000000000000000000000000000000000000000000000000000000000180000 -0000000307C000000000000500000000000000000000000000000000000000000000000000000000003FFF8100000000 -0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001F0000 -00000000007F000000000000000000000000000000000000000000000000000000000000000000000000800000000000 -000780000000000000000000000000100000000000000000000000000000000060000000000000000000000000000000 -FFFFFFFFFFFF3FFF0000007F0000000000000000000000000000000000000000000000000000000000000000F807C3A0 -00000FE700003C00000000000000000000000000000000000000001C0000000000000000000000000000000000000000 -FFFFFFFFF87FFFFFFFFFFFFF00201FFFF80000100000FFFE0000000000000000F9FFFF7F000007DB0000000000000000 -00008000000000000000000000000000000000000000000000000000000000000000000000004000000000000000F000 -000000000000000000000000000000000000000000000000000000000000F00000000000000000000000000000000000 -0000000000000000007F0000000000000000000000000000000007F00000000000000000000000000000000000000000 -00000000000000000000000000000000000000000000000000000000F800000000000000FFFFFFFFFFFFFFFFFFFFFFFF -00000000000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0000FFFF -0000000000000000000000000000000000000000000000000000000000000000", -); -//1344 bytes -enum Extended_PictographicTrieEntries = TrieEntry!(bool, 8, 5, 8)(x" -000000000000004000000090", -x" -00000100000000A000001800", -x" -020201000202020202020202030202020202020202020202020202020202020202020202020202020202020202020202 -020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202 -020202020202020202020202020202020202020202020202020202020202020202020202020202020000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000001000000010001000100010001000100010001000100010001000100010001 -000100010001000100010001000100010001000100010001000100010001000100030002000400010006000500080007 -00090001000A000100010001000100010001000B0001000C000100010001000100010001000100010001000100010001 -000100010001000100010001000100010001000100010001000100010001000100010001000100010001000100010001 -000100010001000100010001000100010001000100010001000100010001000100010001000100010001000100010001 -000E000D0010000F0011000D00130012001500140001000D000D000D0016000D00010001000100010001000100010001 -000100010001000100010001000100010001000100010001000100010001000100010001000100010001000100010001 -000000000000000000000000000000000000000000004200000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000010000000000002000000000000000000000000000000000000000000 -0000000002000004000000000000000003F000000000060000000000000000000C000000000001000000000000000000 -000001000000000000008000070FFE000000000000000000000000000000000000000000000000000000000400000000 -000000000000000000000000000000000000000000400C000000000178000000FFF7FFBFFFFFFFFFFFFFFFFFFFFFFFFF -FFFF003FFFFFFFFFFFFFFFFFFFFFFFFF2057FF3F0018010200B85090000000F800E00000800100020000000000000000 -0000000000300000000000000000000000000000000000000000000000000000180000E0000000000021000000000000 -000000000000000000000000000000000000000020010000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000002800000000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0000E0000000800000000000C003F00007FE4000FFFFE000FFFFFFFF0000003F -0400FFFEF7FC8000FFFFFE00FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFF07FFFFFFFFFFFFFF3FFFFFFFFFFFFFC0FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFF0000FFFF00000000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF000000000000000000000000FFF00000 -0000000000000000FFE00000FFFFFFFF0000F00000000000FC00FF00000000000000FF00FFFFC000FFFFFFFFFFFFFFFF -FFFFF000F7FFFFFFFFFFFFBFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFF3FFFFFFF0000000000000000000000000000000000000000000000000000000000000000", -); - -} - - -static if (size_t.sizeof == 8) -{ -//832 bytes -enum hangulLVTrieEntries = TrieEntry!(bool, 8, 5, 8)(x" -000000000000000000000000000000200000000000000040", -x" -000000000000010000000000000000800000000000000A00", -x" -000201000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000040003000200010001000700060005000500040003000200020001000700060006000500040003 -000300020001000700070006000500040004000300020001000100070006000500050004000300020008000100070006 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000100000010000001000100000010000000000100000010001000000100000010 -001000000100000000001000000100000000001000000100010000001000000100010000001000000000010000001000 -100000010000001000100000010000000000100000010000000000100000010001000000100000010001000000100000 -000001000000100010000001000000100010000001000000000010000001000000000010000001000100000010000001 -000100000010000000000100000010001000000100000010001000000100000000001000000100000000001000000100 -001000000100000000001000000100000000000000000100000000000000000000000000000000000000000000000000 -00000000000000000000000000000000", -); -//832 bytes -enum hangulLVTTrieEntries = TrieEntry!(bool, 8, 5, 8)(x" -000000000000000000000000000000200000000000000040", -x" -000000000000010000000000000000800000000000000A00", -x" -000201000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000040003000200010001000700060005000500040003000200020001000700060006000500040003 -000300020001000700070006000500040004000300020001000100070006000500050004000300020008000100070006 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -00000000000000000000000000000000FEFFFFFFEFFFFFFEFFFEFFFFFFEFFFFFFFFFFEFFFFFFEFFFEFFFFFFEFFFFFFEF -FFEFFFFFFEFFFFFFFFFFEFFFFFFEFFFFFFFFFFEFFFFFFEFFFEFFFFFFEFFFFFFEFFFEFFFFFFEFFFFFFFFFFEFFFFFFEFFF -EFFFFFFEFFFFFFEFFFEFFFFFFEFFFFFFFFFFEFFFFFFEFFFFFFFFFFEFFFFFFEFFFEFFFFFFEFFFFFFEFFFEFFFFFFEFFFFF -FFFFFEFFFFFFEFFFEFFFFFFEFFFFFFEFFFEFFFFFFEFFFFFFFFFFEFFFFFFEFFFFFFFFFFEFFFFFFEFFFEFFFFFFEFFFFFFE -FFFEFFFFFFEFFFFFFFFFFEFFFFFFEFFFEFFFFFFEFFFFFFEFFFEFFFFFFEFFFFFFFFFFEFFFFFFEFFFFFFFFFFEFFFFFFEFF -FFEFFFFFFEFFFFFFFFFFEFFFFFFEFFFF0000000FFFFFFEFF000000000000000000000000000000000000000000000000 -00000000000000000000000000000000", -); -//896 bytes -enum prependTrieEntries = TrieEntry!(bool, 8, 5, 8)(x" -000000000000000000000000000000200000000000000040", -x" -000000000000010000000000000000800000000000000C00", -x" -010101010101010001010101010101020101010101010101010101010101010101010101010101010101010101010101 -010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101 -010101010101010101010101010101010101010101010101010101010101010101010101010101010000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000200010000000000000000000000030000000000040000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000600050000000000000000 -0000000800070000000A0000000900000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -00000000000000000000000000000000000000000000003F000000000000000000000000000000000000000020000000 -000000000000800000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000003000000000004000000000000000000000000000000000000400000000000000000000000000000000000 -000000000000000000000000000000002000000000000000000000000000200000000000000000000000000000000000 -0000000000000000000000000000000C8000000000000000000000000000000200000000000000000000000000000000 -0400000000000000000000000000000000000000000003F0000000000000000000000000000000000000000000000040 -000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000", -); -//1280 bytes -enum controlTrieEntries = TrieEntry!(bool, 8, 5, 8)(x" -000000000000000000000000000000200000000000000068", -x" -000000000000010000000000000001200000000000000E00", -x" -030202020202010002060502020204020202020202020202020202020202020202020202020202020202020202020202 -020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202 -020202020202020202020202020202020202020202020207020202020202020202020202020202020000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000001000100010000000100020001000100010001000100010001000100010001 -000100010001000100010001000100010001000100010003000100010001000100010001000100040001000100010001 -000100010001000100010001000100010001000100010001000100010001000100010001000100010001000100010001 -000100010001000100010001000100010001000100010001000100010001000100010001000100010001000100010001 -000100010001000100010001000100010001000100010001000100010001000100010001000100010001000100010001 -000100010001000100010001000100010001000100010001000600050001000100010001000100010001000100010001 -000100010001000100010001000100010001000100010001000100010001000700010001000100010001000100010001 -000100010001000100010001000100010001000100010001000100010001000100010001000100010001000100010001 -000100010001000100010001000100080001000100010001000100010001000100010001000100010001000100010001 -0001000100090001000100010001000100010001000100010001000100010001000C000C000B000A000C000C000C000C -000C000C000C000C000C000C000C000C0001000100010001000100010001000100010001000100010001000100010001 -000100010001000100010001000100010001000100010001000100010001000100010001000100010001000100010001 -0001000100010001000100010001000100000000FFFFDBFF800000000000000000002000FFFFFFFF0000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000100000000000000000000000 -000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000 -00007F000000C8000000FFFF000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000080000000000000000000000000000000000000000000000000000000000000000FFF000000000000 -FFFF00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -0000000F000000000000000000000000000000000000000007F800000000000000000000000000000000000000000000 -00000000FFFFFFFF0000000000000000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00000000000000000000000000000000 -0000000000000000FFFF000000000000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -0000000000000000000000000000000000000000000000000000000000000000", -); -//1856 bytes -enum spacingMarkTrieEntries = TrieEntry!(bool, 8, 5, 8)(x" -000000000000000000000000000000200000000000000058", -x" -000000000000010000000000000000E00000000000002400", -x" -010102010101010001050101040101030101010101010101010101010101010101010101010101010101010101010101 -010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101 -010101010101010101010101010101010101010101010101010101010101010101010101010101010000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000030002000100000007000600050004 -00000000000000080009000000000000000C000B000A0000000000000000000D00000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000110010000F000E000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -001500140013001200190018001700160000001C001B001A0020001F001E001D00000000000000000000000000000000 -000000000000000000210000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000002200000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -00000000000000000000000000000000C800000000000008000000000000DE01800000000000000C0000000000001981 -C0000000000000080000000000000001C0000000000000080000000000001A01000000000000000C0000000000001981 -80000000000000000000000000001DC6000000000000000E000000000000001E400000000000000C0008000000000D9B -800000000000000C0000000000001DC1000000000000000C000C00007F03000000080000000000000000000000000000 -00080000000000000000000000000000C000000000000000800000000000000000000000000000000000000000000000 -18020000000000000000000000C000000000000000000010000000000000000000100000002000000000000000000000 -C04000000000000000000000000001BF01FB0E7800000000000000000000000000000000000000000000000000000000 -00000000060000000007E00000A0000000000000000000000000000000000000E800000000000010000000000000001B -000004C200000004000C5C800000000000300FF000000000000000000000000000000000000000000080000200000000 -00000098000000000000000000000000FFF0000000000003000000000000000F000000000000000000000000000C0000 -CC3000000000000800000000000000010019800000000000000000000000200000000000000000000020C80000000000 -000000000000000000000000000000000000000000000000000016D80000000000000000000000050000000000000000 -018700000000000400000000000000000000100000000000000000000000006080380000000000040000000000004001 -002C700000000000000000000000000000000000000000000000000700000000800000000000000C0000000C0000399E -0000000000000000000000000000000000E000000000000000000000000000235A060000000000000000000000000002 -000000000000000000000000000000004F03000000000000000000000000000058070000000000000000000000000000 -0040D0000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000 -010070000000000000000000000000000000000000000000000000000000000021BE0000000000000000000000000005 -000000000000000000000010F00E00000200000000000000000000000180000000000000008000000000000000000000 -400080000000000000000000000000000012020000000000000000000000000000000000000000000000000000000000 -0000000000587C0000000000000000000000000000000000000000000000000000000000000000000060000000000000 -C0300000000000080000000000000002000000000000000000000000000000000000000000000000FFFFFFFFFFFE0000 -00000000000000FF00030000000000000000000000000000000020400000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000", -); -//3488 bytes -enum graphemeExtendTrieEntries = TrieEntry!(bool, 8, 5, 8)(x" -000000000000000000000000000000200000000000000088", -x" -000000000000010000000000000001A00000000000004B00", -x" -04020302020201000A090802070206050202020202020202020202020202020202020202020202020202020202020202 -020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202 -02020202020202020202020202020202020202020202020B020202020202020202020202020202020000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -00000000000000000000000000000000000100000000000000050004000300020009000800070006000D000C000B000A -000F00000000000E00100000000000000014001300120011000000000016001500000000000000170000000000000000 -00000000000000000000000000190018000000000000001A000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000001B00000000001F001E001D001C0000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000200000000000000022002100000000 -0025002400230000000000000000000000000026000000000029002800270000002D002C002B002A00310030002F002E -000000340033003200380037003600350000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000390000000000000000000000000000000000000000000000000000000000000000 -003B003A00000000003C0000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000003D000000000000000000000000000000000000000000000000003E000000000000 -00000040003F000000000000000000000000004100000000000000000000000000000043003B00420000000000000044 -000000000046004500000000000000000047000000000000000000000000000000000000000000000000000000000000 -000000000049004800000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -00000000000000000000000000000000FFFFFFFFFFFFFFFF0000FFFFFFFFFFFF00000000000000000000000000000000 -0000000000000000000000000000000000000000000003F8000000000000000000000000000000000000000000000000 -BFFFFFFFFFFE000000000000000000B60000000007FF000000010000FFFFF800000000000000000000003D9F9FC00000 -FFFF00000002000000000000000007FF0001FFC000000000200FF8000000000000003EEFFBC00000000000000E000000 -00000000FF000000FFFFFFFBFFFFFC0014000000000000070000000C00FE21FE50000000000000024000000C0080201E -100000000000000600230000000239861000000000000006FC00000C000021BED0000000000000020000000C00E0201E -40000000000000040000000000802001D0000000000000110000000C00603DC190000000000000020000000C00603044 -58000000000000030000000C0080201E000000000000000200000000805C840007F20000000000000000000000007F80 -1FF20000000000000000000000007F0002A00000030000007FFE0000000000001FFFFFFFFEFFE0DF0000000000000040 -66FDE00000000000001E0001C300000000000000200020640000000000000000000000000000000000000000E0000000 -00000000000000000000000000000000000C0000001C0000000C0000000C00003FB000000000000000000000200FFE40 -000000000000B8000000000000000000000002000000006000000000000000000E040187000000000000000000000000 -0000000000000000000000000000000000000000098000009FF81FE57F400000FFFF0000000000000000000000007FFF -17F000000000000F000FF8000000000400003B3C000000030003A3400000000000CFF000000000000000000000000000 -0000000000000000031021FDFFF70000000000000000000000000000000000000000000000000000FFFFFFFFFFFFFFFF -0000000000001000000000000000000000000000000000000001FFFFFFFF000000000000000000000000000000000000 -00000000000000000003800000000000000000000000000080000000000000000000000000000000FFFFFFFF00000000 -0000FC000000000000000000000000000000000006000000000000000000000000000000000000003FF7800000000000 -00000000C000000000030000000000000000106000000844000000000000000000000000000000008003FFFF00000030 -00003FC000000000000000000003FF8033C8000000000007000000200000000000667E00000000001000000000001008 -C19D00000000000000403000000000020000000000000000000000000000000000000000000000000000212000000000 -00000000400000000000000000000000000000000000000000000000000000000000FFFF0000FFFF0000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000C00000000000000000000000 -000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000 -00000000000000000000000100000000000000000000000007C000000000000000000000000000000000000000000000 -870000000000F06E000000000000000000000000000000000000006000000000000000F0000000000000000000000000 -00000000000000000000000000000000000000000000000000000000000000000000180000000000E000000000000000 -0000000000000000000000000001FFC0000000000000003C0000000000000000FF00000000000002801900000000007F -06780000000000030000000000000004001FEF800000000700080000000000007FC00000000000030000000000009E00 -40D380000000000000000000000000020000000000000000000007F8800000005800000000000003001F1FC000800001 -00000000000000000000000000000000FF00000000000000000000004000005CA5F9000000000000000000000000000D -00000000000000000000000000000000B03C8000000000000000000030000001A7F80000000000000000000000000001 -00BF280000000000000000000000000000000FBCE0000000000000000000000000000000000000000000000000000000 -06FF80000000000000000000000000000000000000000000000000000000000058010000000000000000000000000008 -0000000000000000000000010CF0000079F80000000007FE000000000E7E008000000000037FFC000000000000000000 -BF7F0000000000000000000000000000006DFCFFFFFC00000000000000000000B47E00000000000000000000000000BF -0000000000A3000000000000000000000000000000000000000000000000000000000000000000000018000000000000 -07C0000000000003000000000000000500000000000000000000000000000000000000000000000000000000003FFF81 -00000000000000000000000000000000000000000000000000000000000000000000000000000000001F000000000000 -007F00000000000000000000000000000000000000000000000000000000000000000000000000000000000000008000 -000000000007800000000010000000000000000000000000000000000000000000000000600000000000000000000000 -FFFF3FFFFFFFFFFF000000000000007F000000000000000000000000000000000000000000000000F807C3A000000000 -00003C0000000FE700000000000000000000000000000000000000000000001C00000000000000000000000000000000 -F87FFFFFFFFFFFFF00201FFFFFFFFFFF0000FFFEF80000100000000000000000000007DBF9FFFF7F0000000000000000 -000000000000800000000000000000000000000000000000000000000000000000004000000000000000F00000000000 -0000000000000000000000000000000000000000000000000000F0000000000000000000000000000000000000000000 -000000000000000000000000007F0000000000000000000000000000000007F000000000000000000000000000000000 -000000000000000000000000000000000000000000000000F800000000000000FFFFFFFF00000000FFFFFFFFFFFFFFFF -00000000000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0000FFFFFFFFFFFF -0000000000000000000000000000000000000000000000000000000000000000", -); -//1344 bytes -enum Extended_PictographicTrieEntries = TrieEntry!(bool, 8, 5, 8)(x" -000000000000000000000000000000200000000000000048", -x" -000000000000010000000000000000A00000000000001800", -x" -020202020202010003020202020202020202020202020202020202020202020202020202020202020202020202020202 -020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202 -020202020202020202020202020202020202020202020202020202020202020202020202020202020000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000001000100010000000100010001000100010001000100010001000100010001 -000100010001000100010001000100010001000100010001000100010001000100040001000300020008000700060005 -000A00010009000100010001000100010001000C0001000B000100010001000100010001000100010001000100010001 -000100010001000100010001000100010001000100010001000100010001000100010001000100010001000100010001 -000100010001000100010001000100010001000100010001000100010001000100010001000100010001000100010001 -0010000F000E000D001300120011000D0001000D001500140016000D000D000D00010001000100010001000100010001 -000100010001000100010001000100010001000100010001000100010001000100010001000100010001000100010001 -000000000000000000000000000000000000420000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000001000000000000000000000000000020000000000000000000000000000000000 -020000040000000000000000000000000000060003F000000000000000000000000001000C0000000000000000000000 -0000000000000100070FFE00000080000000000000000000000000000000000000000000000000000000000000000004 -0000000000000000000000000000000000400C00000000007800000000000001FFFFFFFFFFF7FFBFFFFFFFFFFFFFFFFF -FFFFFFFFFFFF003FFFFFFFFFFFFFFFFF001801022057FF3F000000F800B850908001000200E000000000000000000000 -003000000000000000000000000000000000000000000000000000000000000000000000180000E00000000000210000 -000000000000000000000000000000002001000000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000028000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF000080000000E000C003F00000000000FFFFE00007FE40000000003FFFFFFFFF -F7FC80000400FFFEFFFFFFFFFFFFFE00FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFF07FFFFFFFFFFFFFF3FFFFFFFFFFFFFFFFFFFFFFFFFFFFFC0FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFF000000000000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0000000000000000FFF0000000000000 -0000000000000000FFFFFFFFFFE00000000000000000F00000000000FC00FF00FFFFC0000000FF00FFFFFFFFFFFFFFFF -F7FFFFFFFFFFF000FFFFFFFFFFFFFFBFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFF3FFFFFFFFFFFFFFF0000000000000000000000000000000000000000000000000000000000000000", -); - -} - diff --git a/phobos/std/internal/unicode_norm.d b/phobos/std/internal/unicode_norm.d deleted file mode 100644 index bc51c8c..0000000 --- a/phobos/std/internal/unicode_norm.d +++ /dev/null @@ -1,487 +0,0 @@ -//Written in the D programming language -/** - * License: $(HTTP boost.org/LICENSE_1_0.txt, Boost License 1.0). - * - * Authors: Dmitry Olshansky - * - */ -// !!! DO NOT EDIT !!! -// !!! Did you even read the comment? !!! -// This module is automatically generated from Unicode Character Database files -// https://github.com/dlang/phobos/blob/master/tools/unicode_table_generator.d -//dfmt off -module std.internal.unicode_norm; -import std.internal.unicode_tables; - -package(std): - - -static if (size_t.sizeof == 4) -{ -//1728 bytes -enum nfcQCTrieEntries = TrieEntry!(bool, 8, 5, 8)(x" -0000000000000040000000C0", -x" -000001000000010000001E00", -x" -020201000302020202020204020502020202020206020202020202020202020202020202020202020202020202020202 -020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202 -020202020202020202020202020202020202020202020202020202020202020202020202020202020000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000010000000000000000000200030000000500040007000600080000 -000A000900000000000000000000000000000000000B000000000000000C0000000E000D000F00000000000000000000 -000000000000001000000000000000000000001100000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000012000000140013000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000160015001700000019001800000000001A0000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000001B0000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -00000000000000000000000000000000001200120000001C000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000081A9FDF010361F80000003F40100000 -000000800000000000000000000000000000000000000000003800000000000000000000000000000000000000000000 -0000000010000000FF000000000000000000000040000000B08000000000000000000000004800004E00000000000000 -00000000000000000000000000000000000000004000000030C000000000000000000000400000000080000000000000 -000000000000000000400000000000000000000000000000006000040000000000000000400000000080000000000000 -000000000000000080008400000000000000000000000000108420080168020020080002020010840000000000000000 -0000000000004000000000000000000000000000000000000000000000000000000000000000000000000000003FFFFE -00000000FFFFFF0000000007000000000000000000200000000000000000000000000000000000000000000000000000 -0000000000000000000000002AAA0000000000004800000008080A002A00C80800000003000000000000000000000000 -000000000000000000000000000000000000000000000C40000000000000000000000000000000000000000000000000 -000000000000060000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000010000000000000000000000000000000000000000000000006000000000000000000000000000000 -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF7FE53FFFFFFFFC65FFFFFFFFFFFF3FFF -FFFFFFFFFFFFFFFF03FFFFFF00000000A00000005F7FFC0000007FDB0000000000000000000000000000000000000000 -000000000000000000000000000000000000000004000000000000000000000000000000000000800000000000000000 -000000000000000000000000000000000000000040000000008000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000024010000000000000000000000000000000000000000000000000000 -000000000000800000000000000000000000000000010000000000000000000000000000000000000000000000000000 -0000000000000000C00000000000001F00000000F800000000000001000000003FFFFFFF000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", -); -//2048 bytes -enum nfdQCTrieEntries = TrieEntry!(bool, 8, 5, 8)(x" -0000000000000040000000E0", -x" -000001000000014000002400", -x" -020201000504030202020206020702020202020208020202020202020202020202020202020202020202020202020202 -020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202 -020202020202020202020202020202020202020202020202020202020202020202020202020202020000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -00000000000000000000000000000000000100000003000200050004000500060007000500090008000B000A000C0005 -0005000D00050005000500050005000500050005000E0005000500050010000F00120011001400130005000500050005 -000500050005001500050005000500050005001600050005000500050005000500050005000500050005000500050005 -000500050005000500050005000500050005000500050005000500050005000500050005000500050005000500050005 -000500050005000500050005000500050005000500050005000500050005000500050005000500050017001700170017 -001700170017001700170017001700170017001700170017001700170017001700170017001700170017001700170017 -001700170017001700170017001700170017001700170017001700170018001700050005000500050005000500050005 -000500050005000500050005000500050005000500050005000500050005000500050005000500050005000500050005 -00170005001A001900050005000500050005000500050005000500050005000500050005000500050005000500050005 -001C001B001D0005001F001E000500050020000500050005000500050005000500050005000500050005000500050005 -000500050005000500050005000500050021000500050005000500050005000500050005000500050005000500050005 -000500050005000500050005000500050005000500050005000500050005000500050005000500050005000500050005 -001700170005002200050005000500050005000500050005000500050005000500050005000500050005000500050005 -000500050005000500050005000500050005000500050005000500050005000500000000000000000000000000000000 -00000000000000003E7EFFBFBE7EFFBFFFFCFFFF7EF1FF3FFFF3F1F87FFFFF3F0000000000018003DFFFE000FF31FFCF -CFFFFFFF000FFFC000000000000000000000000000000000000000000000000000000000000000000000001B40100000 -0001D7E00001FC0000187C00000000000200708B02000000708B000000C000000000000000000000FCCF0006033FFCFC -0000000000000000000000000000000000000000000000000000000000000000000000000000007C0000000000000000 -000000000000000000080005000000000000000000120200FF000000000000000000000000000000B000180000000000 -00000000004800004E000000000000000000000000000000000000000000000000000000000000003000190000000000 -001000000000000000001C000000000000000000000000000000010000000000000000000000000000000D8100000000 -000000000000000000001C00000000000000000000000000740000000000000000000000000000001084200801680200 -200800020200108400000000000000000000000000000040000000000000000000000000000000000000000000000000 -00045540280000000000000B0000000000000000000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -0BFFFFFFFFFFFFFFFFFFFFFF03FFFFFF3F3FFFFFFFFFFFFFAAFF3F3F3FFFFFFFFFFFFFFF5FDFFFFFEFCFFFDE3FDCFFFF -00000003000000000000000000000000000000000000000000000000000000000000000000000C400000000000000000 -0C000000000040000000E000000000000000121000000050000002920333E005000003330000F0000000000000003C0F -000000000000060000000000000000000000000000000000000000000000000000000000000000000000000000000000 -0000000000000000100000000000000000000000000000005555500036DB02A5401000005555500036DB02A547900000 -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFF0000000F00000000000000007FE53FFFFFFFFC65FFFFFFFFFFFF3FFFFFFFFFFFFFFFFFFF03FFFFFF00000000 -A00000005F7FFC0000007FDB000000000000000000000000000000000000000000000000000000000000000000000000 -14000000000008000000000000000000000000000000C000000000000000000000000000000000000000000000000000 -000000000000000000001800000000000000000000000000000000000000000000000000000000000000000000000000 -0000000058000000000000000000000000000000000000000000000000000000000000000C0000000000000000000000 -00000000010000000000000000000000000000000000000000000000000000000000000000000000C00000000000001F -00000000F800000000000001000000003FFFFFFF00000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000", -); -//2848 bytes -enum nfkcQCTrieEntries = TrieEntry!(bool, 8, 5, 8)(x" -0000000000000040000000E0", -x" -000001000000014000003D00", -x" -020201000402030202020205070602020202020208020202020202020202020202020202020202020202020202020202 -020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202 -020202020202020202020202020202020202020202020202020202020202020202020202020202020000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -00000000000000000000000000000000000100000003000200050004000400060007000400090008000B000A000D000C -000F000E0004000400040004000400040004000400100004001100040013001200150014001700160004001800040004 -0004000400040019001B001A001D001C001F001E00210020000400040004000400040004000400040004000400040004 -000400040004000400040004000400040004000400040004000400040004000400040004000400040004000400040004 -000400040004000400040004000400040004000400040004000400040023002200040004002400040004000400040004 -000400040004000400040004000400040004000400040004000400040004000400040004000400040004000400040004 -000400040004000400040004000400040004000400040004000400040004000400210004002600250027002100290028 -000400040004000400040004002A000400040004000400040004000400040004002C002B002D0004002F002E00040004 -003000040004000400040004000400040004000400040004000400040004000400040004000400040004000400040004 -003100040004000400330032003500340004000400040004000400040004000400040036000400040004000400040004 -000400040004000400040004000400370038000400040039000400040004000400040004003A00040004000400040004 -000400040004000400040004000400040004000400040004000400040004000400040004000400040004000400040004 -002100210004003B00040004000400040004000400040004000400040004000400040004000400040004000400040004 -000400040004000400040004000400040004000400040004000400040004000400000000000000000000000000000000 -00000000773C8501000000000000000000000000800C00000000020180000000000000000000000000001FF0000E0000 -000000000000000000000000000000000000000001FF00003F0000000000001F081A9FDF010361F80000003F44100000 -000000B000000000007F0000023700000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000008000000000000000000000000000000000000000000038000001E00000 -000000000000000000000000000000000000000010000000FF000000000000000000000040000000B080000000000000 -00000000004800004E0000000000000000000000000000000000000000000000000000004000000030C0000000000000 -000000004000000000800000000000000000000000000000004000000000000000000000000000000060000400000000 -000000004000000000800000000000000000000000000000800084000000000000000000000800000000000000000000 -0000000000080000300000000000000000001000000000001084200803E8020020080002020010840000000000000000 -0000000000004000000000000000000000000000000000000000000010000000000000000000000000000000003FFFFE -00000000FFFFFF0000000007000000000000000000200000000000000000000000000000000000000000000000000000 -00000000F7FF7000FFFFBFFF010007FFF8000000FFFFFFFF000000000000000000000000000000000000000000000000 -0C0000000000000000000000000000000000000000000000000000002AAA000000000000E8000000E808EA036A00E808 -008207FF50D8807080800380FFF300001FFF7FFF0000010000000000000000003E6FFEEFFBFBBD57FFFF03E1FFFFFFFF -00000200000000000000000000000000000000000001B000000000000000000000000000000000000000000000000000 -0000000000000600000000000000000000000000000000000000000000000000000000000000000000000000FFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFF000007FF0000100000000000000000000070000000000000000000001000000000000000 -000000000000000000000000300000000000000000000000000000000000000000000000000000000000000000008000 -000000000000000000000000000000000000000000000000000000000000000080000000000000000000000000080000 -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF003FFFFF0000000000000001074000000000000000000000 -9E00000000000000000000008000000000000000FFFE0000FFFFFFFFFFFFFFFFFFFC7FFF000000000000000000000000 -7FFFFFFFFFFFFFFFFFFF00FF7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0000000000000000000000000000000030000000000000000000000000000000 -00000000000000000000000000010000000000000000000000000000031C00000000000000000000F000000000000200 -000000000000000000000000000000007FE53FFFFFFFFC65FFFFFFFFFFFF3FFFFFFFFFFFFFFFFFFF03FFFFFF00000000 -A0F8007F5F7FFFFFFFFFFFDBFFFFFFFFFFFFFFFF0003FFFFFFF80000FFFFFFFFFFFFFFFF3FFFFFFFFFFF0000FFFFFFFF -FFFCFFFFFFFFFFFF000000FF1FFF000003FF0000FFFF0000FFF7FF9FFFD70F7FFFFFFFFFFFFFFFFFFFFFFFFF1FFFFFFF -FFFFFFFEFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF7FFFFFFF1CFCFCFC00007F7F00000000000000000000000000000000 -FFFFFFBE07FDFFFF00000000000000000000000000000000000000000000000000000000040000000000000000000000 -000000000000008000000000000000000000000000000000000000000000000000000000400000000080000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000240100000000000000000000 -000000000000000000000000000000000000000000008000000000000000000000000000000100000000000000000000 -000000000000000000000000000000000000000000000000C00000000000001F00000000F80000000000000100000000 -FFFFFFFFFFFFFFFFFFDFFFFFFFFFFFFFDFFFFFFFEBFFDE64FFFFFFEFFFFFFFFFDFDFE7BF7BFFFFFFFFFDFC5FFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF3FFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFCFFFFFFFFFFF00000000FFFF0000FFFFFFFF00003FFF -00000000000000000000000000000000FFFFFFEF0AF7FE96AA96EA845EF7F7960FFFFBFF0FFFFBEE0000000000000000 -FFFF07FFFFFF7FFF0000FFFF00001C0000010000000000000000000000000000FFFF00070FFFFFFF000301FF00000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003FF0000 -3FFFFFFF0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -00000000000000000000000000000000", -); -//2944 bytes -enum nfkdQCTrieEntries = TrieEntry!(bool, 8, 5, 8)(x" -0000000000000040000000F0", -x" -000001000000016000003E00", -x" -020201000504030202020206080702020202020209020202020202020202020202020202020202020202020202020202 -020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202 -020202020202020202020202020202020202020202020202020202020202020202020202020202020000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000001000000030002000500040007000600080007000A0009000C000B000E000D -0007000F0007000700070007000700070007000700100007001100070013001200150014001700160007001800070007 -0007000700070019001B001A001D001C001F001E00210020000700070007000700070007000700070007000700070007 -000700070007000700070007000700070007000700070007000700070007000700070007000700070007000700070007 -000700070007000700070007000700070007000700070007000700070023002200070007002400070021002100210021 -002100210021002100210021002100210021002100210021002100210021002100210021002100210021002100210021 -002100210021002100210021002100210021002100210021002100210025002100070007000700070007000700070007 -000700070007000700070007000700070007000700070007000700070007000700070007000700070007000700070007 -002100070027002600280021002A0029000700070007000700070007002B000700070007000700070007000700070007 -002D002C002E00070030002F000700070031000700070007000700070007000700070007000700070007000700070007 -000700070007000700070007000700070032000700070007003400330036003500070007000700070007000700070007 -0007003700070007000700070007000700070007000700070007000700070038003900070007003A0007000700070007 -00070007003B000700070007000700070007000700070007000700070007000700070007000700070007000700070007 -00070007000700070007000700070007002100210007003C000700070007000700070007000700070007000700070007 -000700070007000700070007000700070007000700070007000700070007000700070007000700070007000700070007 -0000000000000000000000000000000000000000773C85013E7EFFBFBE7EFFBFFFFCFFFFFEFDFF3FFFF3F3F9FFFFFF3F -0000000000018003DFFFFFF0FF3FFFCFCFFFFFFF000FFFC000000000000000000000000001FF00003F0000000000001F -00000000000000000000001B441000000001D7F00001FC00007F7C00023700000200708B02000000708B000000C00000 -0000000000000000FCCF0006033FFCFC0000000000000000000000000000000000000080000000000000000000000000 -000000000000007C0000000001E000000000000000000000000800050000000000000000000000000000000000000000 -000000000000000000000000000000000000000000120200FF000000000000000000000000000000B000180000000000 -00000000004800004E000000000000000000000000000000000000000000000000000000000000003000190000000000 -001000000000000000001C000000000000000000000000000000010000000000000000000000000000000D8100000000 -000000000000000000001C00000000000000000000000000740000000000000000000000000800000000000000000000 -0000000000080000300000000000000000001000000000001084200803E8020020080002020010840000000000000000 -000000000000004000000000000000000000000000000000000000001000000000045540280000000000000B00000000 -0000000000000000000000000000000000000000F7FF7000FFFFBFFF010007FFF8000000FFFFFFFF0000000000000000 -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0FFFFFFFFFFFFFFFFFFFFFFF03FFFFFF3F3FFFFFFFFFFFFFAAFF3F3F3FFFFFFF -FFFFFFFFFFDFFFFFEFCFFFDF7FDCFFFF008207FF50D8807080800380FFF300001FFF7FFF000001000000000000000000 -3E6FFEEFFBFBBD57FFFF03E1FFFFFFFF0C000200000040000000E00000000000000012100001B050000002920333E005 -000003330000F0000000000000003C0F0000000000000600000000000000000000000000000000000000000000000000 -000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF000007FF00001000000000000000000000700000 -000000000000000010000000000000000000000000000000000000003000000000000000000000000000000000000000 -000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000000 -80000000000000000000000000080000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF003FFFFF00000000 -00000001074000005555500036DB02A5D81000005555500036DB02A5C790000000000000FFFE0000FFFFFFFFFFFFFFFF -FFFC7FFF0000000000000000000000007FFFFFFFFFFFFFFFFFFF00FF7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00000000000000000000000000000000 -3000000000000000000000000000000000000000000000000000000000010000000000000000000000000000031C0000 -0000000000000000F00000000000020000000000000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFF0000000F00000000000000007FE53FFFFFFFFC65FFFFFFFFFFFF3FFFFFFFFFFFFFFFFFFF03FFFFFF00000000 -A0F8007F5F7FFFFFFFFFFFDBFFFFFFFFFFFFFFFF0003FFFFFFF80000FFFFFFFFFFFFFFFF3FFFFFFFFFFF0000FFFFFFFF -FFFCFFFFFFFFFFFF000000FF1FFF000003FF0000FFFF0000FFF7FF9FFFD70F7FFFFFFFFFFFFFFFFFFFFFFFFF1FFFFFFF -FFFFFFFEFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF7FFFFFFF1CFCFCFC00007F7F00000000000000000000000000000000 -FFFFFFBE07FDFFFF00000000000000000000000000000000000000000000000014000000000008000000000000000000 -000000000000C00000000000000000000000000000000000000000000000000000000000000000000000180000000000 -000000000000000000000000000000000000000000000000000000000000000000000000580000000000000000000000 -00000000000000000000000000000000000000000C000000000000000000000000000000010000000000000000000000 -000000000000000000000000000000000000000000000000C00000000000001F00000000F80000000000000100000000 -FFFFFFFFFFFFFFFFFFDFFFFFFFFFFFFFDFFFFFFFEBFFDE64FFFFFFEFFFFFFFFFDFDFE7BF7BFFFFFFFFFDFC5FFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF3FFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFCFFFFFFFFFFF00000000FFFF0000FFFFFFFF00003FFF -00000000000000000000000000000000FFFFFFEF0AF7FE96AA96EA845EF7F7960FFFFBFF0FFFFBEE0000000000000000 -FFFF07FFFFFF7FFF0000FFFF00001C0000010000000000000000000000000000FFFF00070FFFFFFF000301FF00000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003FF0000 -3FFFFFFF0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -00000000000000000000000000000000", -); - -} - - -static if (size_t.sizeof == 8) -{ -//1728 bytes -enum nfcQCTrieEntries = TrieEntry!(bool, 8, 5, 8)(x" -000000000000000000000000000000200000000000000060", -x" -000000000000010000000000000001000000000000001E00", -x" -030202020202010002050202020202040602020202020202020202020202020202020202020202020202020202020202 -020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202 -020202020202020202020202020202020202020202020202020202020202020202020202020202020000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000001000000000000000000020000000000050004000300000008000000070006 -00000000000A00090000000000000000000B000000000000000C000000000000000F0000000E000D0000000000000000 -000000100000000000000000000000000000000000000011000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000014001300120000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000017000000160015000000000019001800000000001A00000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000001B00000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000001C00120012000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000010361F8081A9FDF401000000000003F -000000000000008000000000000000000000000000000000000000000038000000000000000000000000000000000000 -100000000000000000000000FF000000400000000000000000000000B08000000048000000000000000000004E000000 -0000000000000000000000000000000040000000000000000000000030C0000040000000000000000000000000800000 -000000000000000000000000004000000000000000000000000000000060000440000000000000000000000000800000 -000000000000000000000000800084000000000000000000016802001084200802001084200800020000000000000000 -00004000000000000000000000000000000000000000000000000000000000000000000000000000003FFFFE00000000 -FFFFFF000000000000000000000000070020000000000000000000000000000000000000000000000000000000000000 -00000000000000002AAA00000000000048000000000000002A00C80808080A0000000000000000030000000000000000 -0000000000000000000000000000000000000C4000000000000000000000000000000000000000000000000000000000 -000006000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000100000000000000000000000000000000000000000000000060000000000000000000000 -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFC657FE53FFFFFFF3FFFFFFFFFFF -FFFFFFFFFFFFFFFF0000000003FFFFFF5F7FFC00A00000000000000000007FDB00000000000000000000000000000000 -000000000000000000000000000000000400000000000000000000000000000000000080000000000000000000000000 -000000000000000000000000000000004000000000000000000000000080000000000000000000000000000000000000 -000000000000000000000000000000002401000000000000000000000000000000000000000000000000000000000000 -000080000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000 -00000000000000000000001FC0000000F8000000000000000000000000000001000000003FFFFFFF0000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", -); -//2048 bytes -enum nfdQCTrieEntries = TrieEntry!(bool, 8, 5, 8)(x" -000000000000000000000000000000200000000000000070", -x" -000000000000010000000000000001400000000000002400", -x" -050403020202010002070202020202060802020202020202020202020202020202020202020202020202020202020202 -020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202 -020202020202020202020202020202020202020202020202020202020202020202020202020202020000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -00000000000000000000000000000000000300020001000000050006000500040009000800070005000C0005000B000A -000500050005000D0005000500050005000E0005000500050010000F0005000500140013001200110005000500050005 -000500150005000500050005000500050005000500050016000500050005000500050005000500050005000500050005 -000500050005000500050005000500050005000500050005000500050005000500050005000500050005000500050005 -000500050005000500050005000500050005000500050005000500050005000500050005000500050017001700170017 -001700170017001700170017001700170017001700170017001700170017001700170017001700170017001700170017 -001700170017001700170017001700170017001700170017001800170017001700050005000500050005000500050005 -000500050005000500050005000500050005000500050005000500050005000500050005000500050005000500050005 -001A00190017000500050005000500050005000500050005000500050005000500050005000500050005000500050005 -001D0005001C001B00050005001F001E0005000500200005000500050005000500050005000500050005000500050005 -000500050005000500050005000500050005000500210005000500050005000500050005000500050005000500050005 -000500050005000500050005000500050005000500050005000500050005000500050005000500050005000500050005 -000500220017001700050005000500050005000500050005000500050005000500050005000500050005000500050005 -000500050005000500050005000500050005000500050005000500050005000500000000000000000000000000000000 -0000000000000000BE7EFFBF3E7EFFBF7EF1FF3FFFFCFFFF7FFFFF3FFFF3F1F80001800300000000FF31FFCFDFFFE000 -000FFFC0CFFFFFFF0000000000000000000000000000000000000000000000000000000000000000401000000000001B -0001FC000001D7E00000000000187C00020000000200708B00C00000708B00000000000000000000033FFCFCFCCF0006 -00000000000000000000000000000000000000000000000000000000000000000000007C000000000000000000000000 -00000000000000000000000000080005001202000000000000000000FF000000000000000000000000000000B0001800 -0048000000000000000000004E0000000000000000000000000000000000000000000000000000000000000030001900 -00000000001000000000000000001C000000000000000000000000000000010000000000000000000000000000000D81 -00000000000000000000000000001C000000000000000000000000007400000000000000000000000168020010842008 -020010842008000200000000000000000000004000000000000000000000000000000000000000000000000000000000 -2800000000045540000000000000000B00000000000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFF0BFFFFFF03FFFFFFFFFFFFFFFFFFFFFF3F3FFFFF3FFFFFFFAAFF3F3F5FDFFFFFFFFFFFFF3FDCFFFFEFCFFFDE -000000000000000300000000000000000000000000000000000000000000000000000C40000000000000000000000000 -000040000C000000000000000000E00000000050000012100333E005000002920000F0000000033300003C0F00000000 -000006000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -00000000000000000000000010000000000000000000000036DB02A55555500055555000401000004790000036DB02A5 -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -0000000FFFFFFFFF0000000000000000FFFFFC657FE53FFFFFFF3FFFFFFFFFFFFFFFFFFFFFFFFFFF0000000003FFFFFF -5F7FFC00A00000000000000000007FDB0000000000000000000000000000000000000000000000000000000000000000 -000008001400000000000000000000000000C00000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000018000000000000000000000000000000000000000000000000000000000000000000 -58000000000000000000000000000000000000000000000000000000000000000C000000000000000000000000000000 -010000000000000000000000000000000000000000000000000000000000000000000000000000000000001FC0000000 -F8000000000000000000000000000001000000003FFFFFFF000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000", -); -//2848 bytes -enum nfkcQCTrieEntries = TrieEntry!(bool, 8, 5, 8)(x" -000000000000000000000000000000200000000000000070", -x" -000000000000010000000000000001400000000000003D00", -x" -040203020202010007060202020202050802020202020202020202020202020202020202020202020202020202020202 -020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202 -020202020202020202020202020202020202020202020202020202020202020202020202020202020000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -00000000000000000000000000000000000300020001000000040006000500040009000800070004000D000C000B000A -00040004000F000E00040004000400040010000400040004001300120011000400170016001500140004000400040018 -0004001900040004001D001C001B001A00210020001F001E000400040004000400040004000400040004000400040004 -000400040004000400040004000400040004000400040004000400040004000400040004000400040004000400040004 -000400040004000400040004000400040004000400040004002300220004000400240004000400040004000400040004 -000400040004000400040004000400040004000400040004000400040004000400040004000400040004000400040004 -000400040004000400040004000400040004000400040004000400040004000400260025002100040029002800270021 -0004000400040004002A00040004000400040004000400040004000400040004002D0004002C002B00040004002F002E -000400040030000400040004000400040004000400040004000400040004000400040004000400040004000400040004 -000400040031000400350034003300320004000400040004000400040004000400040004000400360004000400040004 -0004000400040004000400370004000400040039003800040004000400040004003A0004000400040004000400040004 -000400040004000400040004000400040004000400040004000400040004000400040004000400040004000400040004 -0004003B0021002100040004000400040004000400040004000400040004000400040004000400040004000400040004 -000400040004000400040004000400040004000400040004000400040004000400000000000000000000000000000000 -773C8501000000000000000000000000800C00000000000080000000000002010000000000000000000E000000001FF0 -0000000000000000000000000000000001FF0000000000000000001F3F000000010361F8081A9FDF441000000000003F -00000000000000B002370000007F00000000000000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000800000000000000000000000000000000001E0000000380000 -00000000000000000000000000000000100000000000000000000000FF000000400000000000000000000000B0800000 -0048000000000000000000004E0000000000000000000000000000000000000040000000000000000000000030C00000 -400000000000000000000000008000000000000000000000000000000040000000000000000000000000000000600004 -400000000000000000000000008000000000000000000000000000008000840000080000000000000000000000000000 -00080000000000000000000030000000000000000000100003E802001084200802001084200800020000000000000000 -00004000000000000000000000000000000000000000000010000000000000000000000000000000003FFFFE00000000 -FFFFFF000000000000000000000000070020000000000000000000000000000000000000000000000000000000000000 -F7FF700000000000010007FFFFFFBFFFFFFFFFFFF8000000000000000000000000000000000000000000000000000000 -000000000C000000000000000000000000000000000000002AAA000000000000E8000000000000006A00E808E808EA03 -50D88070008207FFFFF3000080800380000001001FFF7FFF0000000000000000FBFBBD573E6FFEEFFFFFFFFFFFFF03E1 -000000000000020000000000000000000001B00000000000000000000000000000000000000000000000000000000000 -00000600000000000000000000000000000000000000000000000000000000000000000000000000FFFFFFFF00000000 -FFFFFFFFFFFFFFFF000007FFFFFFFFFF0000000000001000007000000000000000000000000000000000000010000000 -000000000000000030000000000000000000000000000000000000000000000000000000000000000000800000000000 -000000000000000000000000000000000000000000000000000000000000000000000000800000000008000000000000 -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00000000003FFFFF07400000000000010000000000000000 -000000009E0000008000000000000000FFFE000000000000FFFFFFFFFFFFFFFF00000000FFFC7FFF0000000000000000 -FFFFFFFF7FFFFFFF7FFFFFFFFFFF00FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0000000000000000000000000000000000000000300000000000000000000000 -000000000000000000010000000000000000000000000000031C000000000000000000000000000000000200F0000000 -00000000000000000000000000000000FFFFFC657FE53FFFFFFF3FFFFFFFFFFFFFFFFFFFFFFFFFFF0000000003FFFFFF -5F7FFFFFA0F8007FFFFFFFFFFFFFFFDB0003FFFFFFFFFFFFFFFFFFFFFFF800003FFFFFFFFFFFFFFFFFFFFFFFFFFF0000 -FFFFFFFFFFFCFFFF1FFF0000000000FFFFFF000003FF0000FFD70F7FFFF7FF9FFFFFFFFFFFFFFFFF1FFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFEFFFFFFFFFFFFFFFF7FFFFFFFFFFFFFFF00007F7F1CFCFCFC00000000000000000000000000000000 -07FDFFFFFFFFFFBE00000000000000000000000000000000000000000000000004000000000000000000000000000000 -000000800000000000000000000000000000000000000000000000000000000040000000000000000000000000800000 -000000000000000000000000000000000000000000000000000000000000000024010000000000000000000000000000 -000000000000000000000000000000000000800000000000000000000000000000010000000000000000000000000000 -0000000000000000000000000000000000000000000000000000001FC0000000F8000000000000000000000000000001 -FFFFFFFFFFFFFFFFFFFFFFFFFFDFFFFFEBFFDE64DFFFFFFFFFFFFFFFFFFFFFEF7BFFFFFFDFDFE7BFFFFFFFFFFFFDFC5F -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF3FFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFCFFFFFFF00000000000000003FFFFFFFFFFF -000000000000000000000000000000000AF7FE96FFFFFFEF5EF7F796AA96EA840FFFFBEE0FFFFBFF0000000000000000 -FFFF7FFFFFFF07FF00001C000000FFFF000000000001000000000000000000000FFFFFFFFFFF000700000000000301FF -0000000000000000000000000000000000000000000000000000000000000000000000000000000003FF000000000000 -000000003FFFFFFF00000000000000000000000000000000000000000000000000000000000000000000000000000000 -00000000000000000000000000000000", -); -//2944 bytes -enum nfkdQCTrieEntries = TrieEntry!(bool, 8, 5, 8)(x" -000000000000000000000000000000200000000000000078", -x" -000000000000010000000000000001600000000000003E00", -x" -050403020202010008070202020202060902020202020202020202020202020202020202020202020202020202020202 -020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202 -020202020202020202020202020202020202020202020202020202020202020202020202020202020000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000030002000100000007000600050004000A000900080007000E000D000C000B -000700070007000F00070007000700070010000700070007001300120011000700170016001500140007000700070018 -0007001900070007001D001C001B001A00210020001F001E000700070007000700070007000700070007000700070007 -000700070007000700070007000700070007000700070007000700070007000700070007000700070007000700070007 -000700070007000700070007000700070007000700070007002300220007000700240007000700070021002100210021 -002100210021002100210021002100210021002100210021002100210021002100210021002100210021002100210021 -002100210021002100210021002100210021002100210021002500210021002100070007000700070007000700070007 -000700070007000700070007000700070007000700070007000700070007000700070007000700070007000700070007 -0027002600210007002A0029002800210007000700070007002B00070007000700070007000700070007000700070007 -002E0007002D002C000700070030002F0007000700310007000700070007000700070007000700070007000700070007 -000700070007000700070007000700070007000700320007003600350034003300070007000700070007000700070007 -00070007000700370007000700070007000700070007000700070038000700070007003A003900070007000700070007 -003B00070007000700070007000700070007000700070007000700070007000700070007000700070007000700070007 -000700070007000700070007000700070007003C00210021000700070007000700070007000700070007000700070007 -000700070007000700070007000700070007000700070007000700070007000700070007000700070007000700070007 -00000000000000000000000000000000773C850100000000BE7EFFBF3E7EFFBFFEFDFF3FFFFCFFFFFFFFFF3FFFF3F3F9 -0001800300000000FF3FFFCFDFFFFFF0000FFFC0CFFFFFFF000000000000000001FF0000000000000000001F3F000000 -0000000000000000441000000000001B0001FC000001D7F002370000007F7C00020000000200708B00C00000708B0000 -0000000000000000033FFCFCFCCF00060000000000000000000000000000000000000000000000800000000000000000 -0000007C0000000001E00000000000000000000000000000000000000008000500000000000000000000000000000000 -00000000000000000000000000000000001202000000000000000000FF000000000000000000000000000000B0001800 -0048000000000000000000004E0000000000000000000000000000000000000000000000000000000000000030001900 -00000000001000000000000000001C000000000000000000000000000000010000000000000000000000000000000D81 -00000000000000000000000000001C000000000000000000000000007400000000080000000000000000000000000000 -00080000000000000000000030000000000000000000100003E802001084200802001084200800020000000000000000 -00000040000000000000000000000000000000000000000010000000000000002800000000045540000000000000000B -00000000000000000000000000000000F7FF700000000000010007FFFFFFBFFFFFFFFFFFF80000000000000000000000 -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0FFFFFFF03FFFFFFFFFFFFFFFFFFFFFF3F3FFFFF3FFFFFFFAAFF3F3F -FFDFFFFFFFFFFFFF7FDCFFFFEFCFFFDF50D88070008207FFFFF3000080800380000001001FFF7FFF0000000000000000 -FBFBBD573E6FFEEFFFFFFFFFFFFF03E1000040000C000200000000000000E0000001B050000012100333E00500000292 -0000F0000000033300003C0F000000000000060000000000000000000000000000000000000000000000000000000000 -0000000000000000FFFFFFFF00000000FFFFFFFFFFFFFFFF000007FFFFFFFFFF00000000000010000070000000000000 -000000000000000000000000100000000000000000000000300000000000000000000000000000000000000000000000 -000000000000000000008000000000000000000000000000000000000000000000000000000000000000000000000000 -00000000800000000008000000000000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00000000003FFFFF -074000000000000136DB02A55555500055555000D8100000C790000036DB02A5FFFE000000000000FFFFFFFFFFFFFFFF -00000000FFFC7FFF0000000000000000FFFFFFFF7FFFFFFF7FFFFFFFFFFF00FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00000000000000000000000000000000 -00000000300000000000000000000000000000000000000000010000000000000000000000000000031C000000000000 -000000000000000000000200F000000000000000000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -0000000FFFFFFFFF0000000000000000FFFFFC657FE53FFFFFFF3FFFFFFFFFFFFFFFFFFFFFFFFFFF0000000003FFFFFF -5F7FFFFFA0F8007FFFFFFFFFFFFFFFDB0003FFFFFFFFFFFFFFFFFFFFFFF800003FFFFFFFFFFFFFFFFFFFFFFFFFFF0000 -FFFFFFFFFFFCFFFF1FFF0000000000FFFFFF000003FF0000FFD70F7FFFF7FF9FFFFFFFFFFFFFFFFF1FFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFEFFFFFFFFFFFFFFFF7FFFFFFFFFFFFFFF00007F7F1CFCFCFC00000000000000000000000000000000 -07FDFFFFFFFFFFBE00000000000000000000000000000000000000000000000000000800140000000000000000000000 -0000C0000000000000000000000000000000000000000000000000000000000000000000000000000000000000001800 -000000000000000000000000000000000000000000000000000000000000000058000000000000000000000000000000 -000000000000000000000000000000000C00000000000000000000000000000001000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000001FC0000000F8000000000000000000000000000001 -FFFFFFFFFFFFFFFFFFFFFFFFFFDFFFFFEBFFDE64DFFFFFFFFFFFFFFFFFFFFFEF7BFFFFFFDFDFE7BFFFFFFFFFFFFDFC5F -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF3FFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFCFFFFFFF00000000000000003FFFFFFFFFFF -000000000000000000000000000000000AF7FE96FFFFFFEF5EF7F796AA96EA840FFFFBEE0FFFFBFF0000000000000000 -FFFF7FFFFFFF07FF00001C000000FFFF000000000001000000000000000000000FFFFFFFFFFF000700000000000301FF -0000000000000000000000000000000000000000000000000000000000000000000000000000000003FF000000000000 -000000003FFFFFFF00000000000000000000000000000000000000000000000000000000000000000000000000000000 -00000000000000000000000000000000", -); - -} - diff --git a/phobos/std/internal/unicode_tables.d b/phobos/std/internal/unicode_tables.d deleted file mode 100644 index a686e38..0000000 --- a/phobos/std/internal/unicode_tables.d +++ /dev/null @@ -1,8327 +0,0 @@ -//Written in the D programming language -/** - * License: $(HTTP boost.org/LICENSE_1_0.txt, Boost License 1.0). - * - * Authors: Dmitry Olshansky - * - */ -// !!! DO NOT EDIT !!! -// !!! Did you even read the comment? !!! -// This module is automatically generated from Unicode Character Database files -// https://github.com/dlang/phobos/blob/master/tools/unicode_table_generator.d -//dfmt off -module std.internal.unicode_tables; - -@safe pure nothrow @nogc package(std): - - -/// Simple Case Entry, wrapper around uint to extract bit fields from simpleCaseTable() -struct SCE -{ - uint x; - - nothrow @nogc pure @safe: - - this(uint x) - { - this.x = x; - } - - this(uint ch, ubyte n, ubyte size) - { - this.x = ch | n << 20 | size << 24; - } - - int ch() const { return this.x & 0x1FFFF; } - int n() const { return (this.x >> 20) & 0xF; } - int size() const { return this.x >> 24; } -} - -/// Bit backed FullCaseEntry -struct FCE -{ - ulong x; // bit field sizes: 18, 12, 12, 4, 4, 4 - -nothrow @nogc pure @safe: - - this(ulong x) - { - this.x = x; - } - - this(dchar[3] seq, ubyte n, ubyte size, ubyte entry_len) - { - this.x = ulong(seq[0]) << 36 | ulong(seq[1]) << 24 | seq[2] << 12 | n << 8 | size << 4 | entry_len << 0; - } - - dchar[3] seq() const { return [(x >> 36) & 0x1FFFF, (x >> 24) & 0xFFF, (x >> 12) & 0xFFF]; } - ubyte n() const { return (x >> 8) & 0xF; } - ubyte size() const { return (x >> 4) & 0xF; } - ubyte entry_len() const { return (x >> 0) & 0xF; } -} - -struct UnicodeProperty -{ - string name; - ubyte[] compressed; -} - -struct TrieEntry(T...) -{ - immutable(size_t)[] offsets; - immutable(size_t)[] sizes; - immutable(size_t)[] data; -} - -SCE simpleCaseTable(size_t i) -{ -static immutable uint[] t = x" -0201E90B0211E92D0201E9110211E93302000496021004970200A7220210A72302001F7902101FF902001F4402101F4C -0200015A0210015B020010FD02101CBD02016E4C02116E6C02001E3802101E390201E9210211E94302001F2302101F2B -020001A0021001A1030003A3031003C2032003C3020004DC021004DD02002CA602102CA70200017B0210017C0201E906 -0211E928020010DC02101C9C020104CD021104F502001F6502101F6D0200051E0210051F020010D802101C9802002C1A -02102C4A0200027D02102C640200A69A0210A69B02001F0202101F0A020005220210052302002C6002102C6102002C1E -02102C4E0200A6500210A6510200A65C0210A65D020013C90210AB99020104140211043C020013CD0210AB9D020000CE -021000EE02001E7E02101E7F020013F0021013F802001EC402101EC50200A78B0210A78C02000114021001150200042F -0210044F0201E9020211E924020004D8021004D902002CAA02102CAB0200011002100111020004920210049302001E34 -02101E35020013F4021013FC020002240210022502002CA202102CA30200A72E0210A72F02016E4802116E680200004A -0210006A020013A60210AB760200015E0210015F030001C4031001C5032001C602001F4002101F480200A6580210A659 -0200022C0210022D02010C9F02110CDF02001FA702101FAF020013AE0210AB7E0200004202100062020013D50210ABA5 -02016E4002116E60020000D2021000F202001E8202101E8302010C9702110CD7020010E002101CA00200013D0210013E -02000406021004560200029D0210A7B2020001390210013A020001980210019902001EBC02101EBD020003AB021003CB -0200040E0210045E0200A7B60210A7B70200040A0210045A02001EC002101EC10201057202110599020024B9021024D3 -02002C1602102C460200019C0210026F020003A7021003C70200A6920210A693030000C5031000E50320212B02000526 -02100527020002660210A7AA0201057A021105A10201057E021105A502001FD002101FD802000046021000660201E90A -0211E92C0200022802100229020013A20210AB720200A7260210A72702010C9302110CD30200049A0210049B020000D6 -021000F60200010C0210010D020118A8021118C802000162021001630200019E0210022002002C9E02102C9F03000392 -031003B2032003D002001E3002101E3102001FA302101FAB0200216D0210217D0200004E0210006E030000B50310039C -032003BC0200048E0210048F0200A72A0210A72B02010410021104380200038A021003AF02001EFE02101EFF020118AC -021118CC020001EA021001EB02001F0602101F0E02001FC302101FCC020010B402102D140200026A0210A7AE03000412 -0310043203201C800200A7600210A76102010C9B02110CDB020024C8021024E2020001A4021001A50200039F021003BF -020010C402102D24020004A2021004A302002C0302102C3302002CE002102CE1020105760211059D020004E0021004E1 -02002C2A02102C5A02001ED002101ED1020000C2021000E2020004E8021004E90200A7A60210A7A7020010F902101CB9 -02000145021001460200037C021003FE02001EC802101EC90200041A0210043A020000CA021000EA02001F8602101F8E -030004210310044103201C83020104040211042C02010583021105AA0200053F0210056F0200054702100577020013B6 -0210AB86020013EC0210ABBC0200FF2C0210FF4C0200A7BA0210A7BB0200A6480210A649020013C50210AB950200FF24 -0210FF440201040F021104370201041C0211044402010C8302110CC3020002410210024202001E7602101E77020010BC -02102D1C02002C2202102C52020010D002101C900200A7680210A7690200A7320210A7330200011C0210011D02000427 -0210044702001E4002101E41020118B1021118D10200015202100153030003A1031003C1032003F102000397021003B7 -020001AC021001AD020001E2021001E302000218021002190201E91A0211E93C020005160210051702001F2702101F2F -0200A7C90210A7CA02001FE002101FE802002165021021750200A66C0210A66D02000393021003B30200053202100562 -020000560210007602001F3302101F3B020005540210058403001C880310A64A0320A64B0200A68E0210A68F020003D8 -021003D9020024C5021024DF02002CB202102CB302010C8202110CC2020000570210007702016E5302116E730200FF37 -0210FF5702001EF602101EF70201E9120211E93402001F1202101F1A020004A6021004A70200016A0210016B02001F34 -02101F3C02002CDC02102CDD020001B10210028A020118A4021118C402001E2802101E29020001BF021001F70200018B -0210018C020010A702102D07020001280210012902001E6A02101E6B020010AB02102D0B0200041F0210043F02002C2E -02102C5E02010CAF02110CEF020104C5021104ED020118A5021118C502001FE102101FE9030003A6031003C6032003D5 -02002C0A02102C3A0200050E0210050F0200010402100105020003720210037302001E8E02101E8F02000396021003B6 -02001EFA02101EFB020003F2021003F90200A6400210A641020010EC02101CAC020118A1021118C10200016602100167 -02001F3702101F3F02010C8602110CC6020013DD0210ABAD02001F5102101F5902002C9A02102C9B020001FA021001FB -020021690210217902016E5702116E77020010A402102D0403000053031000730320017F0200037602100377020013B1 -0210AB810200046802100469020013E40210ABB402001F9602101F9E020001080210010902010CA002110CE002001E66 -02101E6702001E8A02101E8B02001E4C02101E4D02001F7502101FCB02002C0E02102C3E02001E5002101E51020024C0 -021024DA0200A7500210A751020000DA021000FA020010E902101CA902002CD802102CD902001EB802101EB90200012C -0210012D0200025202102C70020004C3021004C4020024C4021024DE020104B5021104DD02002C0F02102C3F02000533 -0210056302000195021001F602001F1302101F1B020104B8021104E00200055302100583020104250211044D020013D1 -0210ABA1020004F4021004F5020004C7021004C802010CAC02110CEC020004D0021004D10200A75C0210A75D02001E2C -02101E2D020010A802102D0802000464021004650200042602100446020004E4021004E5020010ED02101CAD0200A792 -0210A79302001EDC02101EDD020104BC021104E4020105770211059E02001EA802101EA9020001B8021001B9020000C6 -021000E60200FF300210FF5002016E4402116E640201041B0211044302001E0E02101E0F020004D4021004D502002CAE -02102CAF02002C6B02102C6C0200FF230210FF430200018402100185020105730211059A04000398041003B8042003D1 -043003F4020013D40210ABA40201E9190211E93B020001870210018802002C1302102C430200A7A20210A7A302000194 -02100263020004160210043602002CBE02102CBF02001FA602101FAE020104C6021104EE02001F8202101F8A020104C9 -021104F1020013E00210ABB00200024002102C7F0200A7BE0210A7BF02001F9202101F9A020004020210045202000536 -0210056602000550021005800200A6820210A68302000386021003AC02001FE502101FEC020024C9021024E3020000D3 -021000F3020010F002101CB002001E3C02101E3D02002C8002102C810201058A021105B1020001560210015702001E7A -02101E7B020013ED0210ABBD0200A73E0210A73F0200011802100119020002140210021503001E6003101E6103201E9B -02002C8A02102C8B0200A64C0210A64D03000395031003B5032003F5020001E6021001E702001F8502101F8D02000512 -0210051302001F6102101F690200A6680210A66902010594021105BB02001E1802101E19020013B20210AB8202010415 -0211043D0201041802110440020002450210028C02001F7202101FC8020118B5021118D5020010B802102D18020004C0 -021004CF0201040B021104330200042302100443020010C502102D2502002C2D02102C5D02001F3002101F38020013C1 -0210AB910200A76C0210A76D020104CC021104F402001F7102101FBB0200FF330210FF530200216A0210217A02001ECC -02101ECD020003E8021003E90200A7D00210A7D10200005A0210007A02010CB002110CF0020001D9021001DA02010587 -021105AE020001B5021001B6020005430210057302001F2202101F2A02001EA002101EA102010C8F02110CCF02016E54 -02116E7402001D7D02102C630200A7D60210A7D70200A74C0210A74D020013CE0210AB9E020000CF021000EF020010D9 -02101C99020000450210006502000136021001370200040D0210045D02016E4302116E6302001F7C02101FFA0200024A -0210024B020004B8021004B902002CCA02102CCB02002CED02102CEE0200019F02100275020003A4021003C40200A74E -0210A74F020104C0021104E802001E1602101E17020013AF0210AB7F020005000210050102002C8202102C83020010BD -02102D1D0201E9160211E93802010CA302110CE302010C9402110CD4020104C2021104EA030003A6031003C6032003D5 -02016E4D02116E6D020118B4021118D4020013E90210ABB902001F4102101F49020002870210A7B10200054202100572 -02001E5802101E5902001F4302101F4B02002CEB02102CEC020013EB0210ABBB02001FD102101FD902001EE802101EE9 -020013A30210AB73020003EE021003EF020118B8021118D802010C9E02110CDE0201E9200211E942020000FF02100178 -020104170211043F020118A9021118C902001FB002101FB802002C8802102C89020013A90210AB790201E9090211E92B -020013C20210AB92020024B6021024D002001E5E02101E5F02002C6902102C6A0201041F021104470200037F021003F3 -02000540021005700200039002101FD30300004B0310006B0320212A020004F8021004F9020013F3021013FB020000C9 -021000E90200040F0210045F020010B502102D150200A7460210A747020000CD021000ED02000476021004770200039E -021003BE02001F2002101F28020010FA02101CBA02002C2102102C5102001EA602101EA702001F8302101F8B0200053C -0210056C02001F0102101F0902002CC202102CC30200020802100209020013E30210ABB30200A7960210A79702001F64 -02101F6C02010592021105B9020004BE021004BF02001F9102101F99020003E6021003E702001EE002101EE102001FF3 -02101FFC02000413021004330200042E0210044E020104CA021104F202002C0002102C30020010DB02101C9B020010F6 -02101CB6020118BE021118DE020001A7021001A8020010C302102D23020004150210043502001E1E02101E1F02000506 -0210050702001E6402101E65020000C7021000E70200047C0210047D02001E5202101E53020010F402101CB402010586 -021105AD0200046A0210046B02002C2702102C5702010C8902110CC9020010A202102D020200FF320210FF5204000398 -041003B8042003D1043003F4030003A9031003C90320212602001EDA02101EDB0201E9010211E9230200A79C0210A79D -020010D302101C93020024BC021024D60200FF2F0210FF4F0200025002102C6F02002C2902102C59020004B2021004B3 -0201041902110441020002100210021102002C1502102C4502016E4702116E670200A7540210A75502016E4502116E65 -030001C7031001C8032001C90200047E0210047F020001FC021001FD0200020E0210020F020002440210028902001F77 -02101FDB02002CD602102CD702010589021105B0040004220410044204201C8404301C85020010E702101CA7020004FA -021004FB020010D502101C9502001EAC02101EAD020010B702102D17020013A10210AB710200A7C20210A7C30201E907 -0211E929020013F5021013FD02001EE202101EE302001F6202101F6A02001E9202101E93020001D5021001D602002C06 -02102C3602010C8B02110CCB02001F7002101FBA020003EC021003ED0201E9150211E9370200216E0210217E020013C0 -0210AB9002001EF402101EF50200FF3A0210FF5A020118BC021118DC020004AA021004AB020021630210217302001E1C -02101E1D020013C80210AB98020118AF021118CF02010C9802110CD8020105710211059802010CA702110CE702000051 -02100071020004F2021004F302010CAA02110CEA020010AF02102D0F02002C8E02102C8F02002CC402102CC502001E0A -02101E0B02016E5002116E700201058E021105B50201041102110439020104230211044B0200053A0210056A02001E6C -02101E6D020104BE021104E6020104240211044C020010A902102D09020024C2021024DC02010C8102110CC1020013BC -0210AB8C030004620310046303201C87020118A6021118C602001E4A02101E4B020021620210217202001ED402101ED5 -020010AA02102D0A020004EC021004ED02002C0C02102C3C0200A79A0210A79B020004CB021004CC020104BD021104E5 -02000370021003710201E9140211E936020003DA021003DB02001F3502101F3D02016E5102116E7103000392031003B2 -032003D002002C9602102C9702000391021003B102000534021005640200041B0210043B020013DF0210ABAF02002C2C -02102C5C020001240210012502010CA202110CE202001E8C02101E8D02001EB402101EB50200016C0210016D02001E02 -02101E030200019A0210023D020001020210010302001F1402101F1C0200FF280210FF4802010C8002110CC0020010EE -02101CAE02001F5502101F5D0200A77B0210A77C02000059021000790200014A0210014B02001E2402101E25020013BD -0210AB8D02002CBC02102CBD0201040D021104350201058F021105B6020118A7021118C70200A6600210A66102000514 -021005150200052E0210052F020004A4021004A5020004000210045002016E5802116E780200A64E0210A64F0200FF2E -0210FF4E0201040C02110434020000DB021000FB020001CD021001CE020010E802101CA80200054F0210057F0200A738 -0210A73902016E5F02116E7F02001D790210A77D0201E90F0211E93102002C0702102C37020010A302102D030200A7A0 -0210A7A1030004210310044103201C830200A6800210A681020002820210A7C5020024CB021024E50200040102100451 -0200A7580210A7590200005802100078020001930210026002010CA102110CE10200054E0210057E0200018602100254 -020013DC0210ABAC020024BD021024D7020001B3021001B40200A6960210A697020002160210021702001ECE02101ECF -02002168021021780201E90E0211E93002002C1202102C4202002C2F02102C5F02010CAB02110CEB02001E8602101E87 -030004120310043203201C80020013D60210ABA602001EBA02101EBB0201E91B0211E93D0201041A0211044202001EEE -02101EEF0200FF310210FF510200A7BC0210A7BD020001DB021001DC020001A60210028002001F0702101F0F020118BD -021118DD020000C8021000E8020004D2021004D3020010E102101CA1020013B70210AB8702010585021105AC02016E4B -02116E6B02000172021001730200049E0210049F020004E6021004E7020010B002102D1002002C9C02102C9D020001EE -021001EF020104B4021104DC02010C8702110CC7020013BA0210AB8A02001E1002101E11020002300210023102000428 -0210044802002CD002102CD1020118A0021118C0020003E0021003E102010588021105AF0200A7800210A781020010F5 -02101CB50200053B0210056B020000D5021000F5030004140310043403201C8102001FA402101FAC0200012A0210012B -0200005202100072020024B7021024D10201E9080211E92A04000398041003B8042003D1043003F40200020202100203 -0200027102102C6E020004700210047102002C1B02102C4B0200048A0210048B0200A7520210A7530200FF250210FF45 -0200040702100457020000C1021000E102001E4402101E4502001F9702101F9F02000548021005780200021C0210021D -020013E50210ABB50200051A0210051B02002CB602102CB70200A6540210A655020104030211042B02001E7202101E73 -020010D402101C94020104C1021104E902010C8A02110CCA02001E2A02101E2B030004620310046303201C8702001F76 -02101FDA03000053031000730320017F02002C2602102C56020013A00210AB70020004C5021004C6020003FA021003FB -0200055502100585020013E80210ABB8030001C7031001C8032001C902001F4202101F4A02001F6702101F6F0201057C -021105A30200A7B40210A7B50200A6980210A6990201E9040211E9260200040C0210045C020010DA02101C9A020000D0 -021000F002016E4602116E6602001EC202101EC3020001E4021001E502000044021000640200017D0210017E02001F21 -02101F290200FF390210FF59020013AC0210AB7C02001F0002101F08020003A5021003C50200015802100159020104CF -021104F702001E3A02101E3B0200A72C0210A72D020118B7021118D7020001120210011302001E3602101E3702016E4E -02116E6E02001F7D02101FFB020004940210049502001E8002101E8102000116021001170200042D0210044D020013F2 -021013FA020001790210017A020004DA021004DB02002CA802102CA9020000CC021000EC020001A2021001A3030003A1 -031003C1032003F102001E7C02101E7D020013CF0210AB9F020104120211043A020005200210052102002C1C02102C4C -0200026B02102C6202010591021105B80200015C0210015D0200A6520210A6530200FF260210FF46020001E8021001E9 -020010B602102D160200A6560210A6570200051C0210051D020013C30210AB93020104CB021104F302001EC602101EC7 -020001E0021001E102001F6302101F6B02002C0102102C310201E9000211E92202001E7802101E79020024CE021024E8 -0200A76A0210A76B0200011A0210011B020004290210044902001F8402101F8C020010D602101C9602001FB102101FB9 -0200A7620210A76302010CB202110CF202002C2002102C50020010BE02102D1E0201041E02110446020004E2021004E3 -020004DE021004DF020013EA0210ABBA0200054102100571020005450210057502001F2502101F2D0200015402100155 -02002C2402102C54020013EE0210ABBE0200018102100253030003A3031003C2032003C3020118B3021118D3020010BA -02102D1A020001800210024302001E3E02101E3F020010FF02101CBF02010C8802110CC8020010D202101C920200A76E -0210A76F03001C880310A64A0320A64B020010B202102D12020013C70210AB9702000518021005190200A7660210A767 -020001EC021001ED020104C3021104EB020105740211059B0201E91C0211E93E020010E202101CA202002C0502102C35 -020004A0021004A102001E4202101E43020013B40210AB84020118BF021118DF020001500210015102002CB002102CB1 -020013A40210AB74020004C1021004C2030001CA031001CB032001CC0200004C0210006C020104C7021104EF02016E5A -02116E7A0200022202100223020004900210049102002CF202102CF302010595021105BC0200053D0210056D02000388 -021003AD0200054902100579020013E20210ABB202016E4A02116E6A02001EFC02101EFD02001F9002101F98020004D6 -021004D70200A7B80210A7B9030004140310043403201C8102010CAD02110CED020010F702101CB702016E5502116E75 -0200A7280210A72902001E0002101E010200023202100233020010A502102D0502001F1502101F1D030003A9031003C9 -032021260200013B0210013C0200040802100458020000DC021000FC02001EBE02101EBF02002C1802102C480200A7A8 -0210A7A90200216B0210217B02002C1002102C4002002CDE02102CDF0200029E0210A7B0020000D4021000F4020010EF -02101CAF0200055102100581020013DA0210ABAA020005350210056502010C9D02110CDD020024C6021024E002000410 -0210043002001EB602101EB7020104160211043E02016E4202116E62020001640210016502002CA002102CA102001E2E -02101E2F02010C9502110CD502000498021004990200010A0210010B020118AA021118CA02001E8802101E8902000528 -02100529020013D70210ABA70200A65A0210A65B0200022A0210022B02001FA502101FAD020104B3021104DB0201E90C -0211E92E020024BB021024D5020001F4021001F50200A6900210A6910200FF290210FF4902002C7502102C7602002CB8 -02102CB9020104010211042902002C0D02102C3D020104D0021104F80201E9130211E935020001AF021001B00200A7C7 -0210A7C8020013BF0210AB8F020104020211042A020010EA02101CAA020104BF021104E70201058D021105B402002164 -021021740200018E021001DD020001D3021001D402001F1002101F1802001F5702101F5F02001ED202101ED30200FF2A -0210FF4A02001E0402101E05020004EE021004EF02002C9402102C95020000C0021000E0020013DB0210ABAB020001B2 -0210028B020104260211044E0200A6620210A663020000540210007402000556021005860300042A0310044A03201C86 -020104220211044A02001F3602101F3E0200A7940210A7C4020004CD021004CE020104BB021104E30200023B0210023C -020001CF021001D00200050C0210050D0200A6460210A64702001E4602101E4702010CA602110CE60200A7790210A77A -0200FF360210FF56020118A2021118C2020004EA021004EB0200041D0210043D02016E5202116E720200A6880210A689 -02001EB202101EB3020021600210217002001ED602101ED7020024C3021024DD02016E5E02116E7E020004AC021004AD -0201E9100211E9320200023E02102C6602002CB402102CB50200FF2D0210FF4D0200014C0210014D02001E2202101E23 -020013BB0210AB8B020001430210014402010581021105A8020001700210017102001E9402101E9502002C2802102C58 -0201040E02110436020104B2021104DA0200A7980210A799020013B80210AB8802001E7002101E710400042204100442 -04201C8404301C8502010C8502110CC502001EAE02101EAF020001220210012302010C8C02110CCC020104050211042D -0200042002100440020001AE021002880200021A0210021B0200038C021003CC02001F9502101F9D0400034504100399 -042003B904301FBE0200018A0210025702002C9002102C91020010B102102D110200011E0210011F02001E0802101E09 -0200A7C00210A7C1020013E70210ABB702010580021105A70200041C0210043C0200FB050210FB060200A7A40210A7A5 -02001E7402101E7502002C1402102C440201E90D0211E92F020013A50210AB75020010F302101CB3020024BA021024D4 -0200013F021001400200039D021003BD02002CE202102CE3020010A102102D0102000508021005090200004D0210006D -02000389021003AE0200048C0210048D02002C0402102C340200216F0210217F020004B0021004B102002CD202102CD3 -0200A7340210A73502010584021105AB0200005002100070020001600210016102010CA902110CE902001E3202101E33 -0200020002100201020118AB021118CB0200A7860210A787020118BB021118DB020118AE021118CE0200021E0210021F -020003AA021003CA020010C202102D22020013C40210AB9402000539021005690200A74A0210A74B020000C3021000E3 -0200046E0210046F0200054D0210057D020010E302101CA3020003DE021003DF020024CA021024E40400034504100399 -042003B904301FBE02010C9902110CD90200A75A0210A75B020004190210043902002C0902102C390200037D021003FF -02001F2602101F2E020118B2021118D2020021830210218402002C2302102C53020001320210013302001E5602101E57 -020010AE02102D0E0200A7400210A741020000DD021000FD020010E602101CA603001E6003101E6103201E9B02000478 -021004790200022E0210022F0200A6940210A6950200052C0210052D02010570021105970200A6660210A667020013A8 -0210AB7802010C9602110CD60200020A0210020B020024BF021024D9020013CB0210AB9B02010C9C02110CDC0300041E -0310043E03201C8202001E8402101E8502001EF002101EF102016E5902116E7902002CA402102CA50200A7240210A725 -0200049C0210049D0200010E0210010F020104D3021104FB0201E9030211E9250200019102100192020013DE0210ABAE -030003A0031003C0032003D6020013AD0210AB7D020002060210020702001F4502101F4D020003EA021003EB020010BB -02102D1B02000474021004750200FF380210FF58020004FE021004FF02002C8402102C85020104130211043B02002C1F -02102C4F02001E5C02101E5D02001EE402101EE502002CC802102CC9020104CE021104F6020004BA021004BB0201E905 -0211E927020000D1021000F10200047202100473020118B6021118D602001F7802101FF8020000430210006302001EA2 -02101EA302001F2402101F2C0200019D02100272020004B6021004B7020000DF02101E9E02001F8702101F8F02001E14 -02101E1502001EE602101EE70200040B0210045B02000409021004590201057D021105A4020003A8021003C802001E5A -02101E5B0201057F021105A60200054402100574020010FE02101CBE02001E1202101E13020010B902102D1902000047 -02100067020004FC021004FD02010C9202110CD202002C8602102C8702001E1A02101E1B02010C9002110CD0020010BF -02102D1F020013CA0210AB9A02016E4902116E690200004102100061020001BC021001BD02001EEA02101EEB02000502 -021005030200024802100249020010DF02101C9F030001C4031001C5032001C60200054602100576020013EF0210ABBF -02002CCC02102CCD020010D702101C97020013A70210AB770200047A0210047B02000134021001350300042A0310044A -03201C8602002C1902102C4902000204021002050200025C0210A7AB020024CF021024E902001E5402101E5502010579 -021105A00200054A0210057A0200A6840210A6850200FF270210FF4702001F0502101F0D020021320210214E0200024C -0210024D020013B50210AB8502002CC602102CC7020104080211043002002C1D02102C4D0200023F02102C7E0200A742 -0210A74302001E6202101E6302010CA802110CE8030000B50310039C032003BC02001F8102101F8902010C9A02110CDA -0200012E0210012F0200040302100453020001FE021001FF0400034504100399042003B904301FBE0200017402100175 -020003CF021003D7020024CD021024E702001FB302101FBC020118AD021118CD0200A7440210A745020000D9021000F9 -02001E0C02101E0D02002C1702102C47020003E2021003E30200026C0210A7AD0200A7560210A7570200040502100455 -020001970210026802001F6002101F6802001EEC02101EED02002C8C02102C8D020010F202101CB2020104B6021104DE -0201E91D0211E93F0201040A02110432020013C60210AB9602016E4102116E610200042402100444020001A902100283 -0300039A031003BA032003F0020010E502101CA5020104C8021104F0020104070211042F020013D00210ABA002001FA2 -02101FAA0200046C0210046D020010A002102D000200A7820210A783030000C5031000E50320212B020004B4021004B5 -02002CCE02102CCF0200A73A0210A73B0200FF210210FF41020104B9021104E102001EA402101EA50200038F021003CE -020024BE021024D8020003B002101FE302000049021000690201E91F0211E941020002460210024702001F7302101FC9 -02002C6702102C6802002166021021760200010002100101020003E4021003E5020013D20210ABA20200A79E0210A79F -02001FA002101FA802010CB102110CF102016E5D02116E7D020010C102102D210200A7F50210A7F6020013BE0210AB8E -0200042C0210044C020010DD02101C9D02001F9302101F9B0200024E0210024F020004BC021004BD0200017602100177 -020013AB0210AB7B020013E10210ABB1020005040210050502001F0302101F0B0200A6860210A6870200054C0210057C -02016E4F02116E6F020010EB02101CAB020013D90210ABA90200023A02102C65020004A8021004A902002CDA02102CDB -0300041E0310043E03201C8202000394021003B402002C7202102C73020005100210051102001E2602101E270200A642 -0210A643020004C9021004CA020001470210014802001EB002101EB10200A75E0210A75F0201058C021105B30200A73C -0210A73D020104D1021104F9020104D2021104FA02010590021105B7020000550210007502001F1102101F190200037B -021003FD02001F3102101F3902002CBA02102CBB02000552021005820200A68C0210A68D02001E6E02101E6F02002167 -02102177020000DE021000FE020001900210025B02001EF802101EF9020104B0021104D802010C8E02110CCE02002C08 -02102C380200016E0210016F02016E5C02116E7C020010CD02102D2D02001F7B02101FEB02001E9002101E91020013B3 -0210AB83020001D1021001D202001F5302101F5B020001260210012702001E4802101E4902002C9802102C9902010400 -0211042802010CA402110CE4020118B9021118D9020013B00210AB8002001ED802101ED902001F7402101FCA04000422 -0410044204201C8404301C8502001EF202101EF302002161021021710201042002110448030001F1031001F2032001F3 -020001D7021001D80200A68A0210A68B020003F7021003F80200FF350210FF55020003DC021003DD02010C8D02110CCD -02000418021004380200021202100213020004F0021004F1020024C1021024DB0200050A0210050B0201E9170211E939 -0200A6440210A6450200053702100567020010AC02102D0C020010AD02102D0D020024C7021024E10200046002100461 -02002C2B02102C5B02016E5602116E76020010C702102D27020104060211042E02002CD402102CD5020010F102101CB1 -02001F7A02101FEA02002C0B02102C3B0200FF340210FF540200041702100437020118BA021118DA030001F1031001F2 -032001F303000395031003B5032003F5020001B7021002920200018F0210025902000538021005680200A66A0210A66B -02001F3202101F3A02010421021104490200052A0210052B02000425021004450200A7840210A7850200004F0210006F -0200A6640210A665020104B1021104D9020000C4021000E402010CA502110CE502001EAA02101EAB020013D80210ABA8 -020010E402101CA4020000D8021000F8030001CA031001CB032001CC020010D102101C91020105750211059C0200216C -0210217C02002C0202102C3202016E5B02116E7B0200A7D80210A7D90200FF220210FF420200040402100454020010B3 -02102D1302001ECA02101ECB02010409021104310201E9180211E93A020004F6021004F702002CC002102CC102001E20 -02101E21020104B7021104DF0200039B021003BB020001960210026902002C2502102C550200A7900210A7910200A764 -0210A7650200020C0210020D02001F9402101F9C020118B0021118D0020001820210018302002CAC02102CAD02001F80 -02101F88020024CC021024E60200025102102C6D0200048002100481020013E60210ABB6020024B8021024D20200054B -0210057B020013AA0210AB7A0300004B0310006B0320212A020002610210A7AC02000141021001420300039A031003BA -032003F0020010F802101CB8020104C4021104EC0200018902100256020010DE02101C9E02001E6802101E69020104BA -021104E202001F0402101F0C020105780211059F020004110210043102010582021105A902000466021004670200A736 -0210A737020000CB021000EB020010C002102D20020104270211044F020001DE021001DF02002C9202102C9302000106 -0210010702001E4E02101E4F0200042B0210044B0200A7480210A74902002C1102102C410200A7B30210AB530200053E -0210056E0200014E0210014F020013D30210ABA302000524021005250200A65E0210A65F020001F8021001F9020004AE -021004AF0200A77E0210A77F030003A0031003C0032003D6020002650210A78D0200FF2B0210FF4B02001FA102101FA9 -02001EDE02101EDF020013B90210AB8902000168021001690200022602100227020013F1021013F902001E0602101E07 -02000120021001210201041D0211044502001F6602101F6E020010A602102D0602010C9102110CD102001D8E0210A7C6 -02010C8402110CC402010CAE02110CEE020118A3021118C30200004802100068020013CC0210AB9C0201E91E0211E940 -0200038E021003CD0200053102100561"; -return SCE(t[i]); -} -@property FCE fullCaseTable(size_t index) nothrow @nogc @safe pure -{ -static immutable ulong[] t = x" -001E90B000000021001E92D0000001210010CAE0000000210010CEE00000012100004960000000210000497000000121 -001E911000000021001E933000000121000A722000000021000A7230000001210001F790000000210001FF9000000121 -0001F440000000210001F4C000000121000015A000000021000015B00000012100010FD0000000210001CBD000000121 -0016E4C0000000210016E6C0000001210001E380000000210001E39000000121000FB16000000021000057E576000122 -001E921000000021001E9430000001210001F230000000210001F2B00000012100001A000000002100001A1000000121 -00003A300000003100003C200000013100003C300000023100004DC00000002100004DD0000001210002CA6000000021 -0002CA7000000121000017B000000021000017C000000121001E906000000021001E92800000012100010DC000000021 -0001C9C00000012100104CD00000002100104F50000001210001F650000000210001F6D000000121000051E000000021 -000051F00000012100010D80000000210001C980000001210002C1A0000000210002C4A000000121000027D000000021 -0002C64000000121000A69A000000021000A69B0000001210001F020000000210001F0A0000001210000522000000021 -00005230000001210002C600000000210002C610000001210001FB700000002100003B13423B9123000A650000000021 -000A651000000121000A65C000000021000A65D0000001210002C1E0000000210002C4E0000001210001FB3000000031 -0001FBC00000013100003B13B900023200013C9000000021000AB9900000012100013CD000000021000AB9D000000121 -00000CE00000002100000EE0000001210001E7E0000000210001E7F0000001210010414000000021001043C000000121 -00013F000000002100013F80000001210001EC40000000210001EC5000000121000A78B000000021000A78C000000121 -00001140000000210000115000000121000042F000000021000044F000000121001E902000000021001E924000000121 -00004D800000002100004D90000001210002CAA0000000210002CAB00000012100001100000000210000111000000121 -000049200000002100004930000001210001E340000000210001E3500000012100013F400000002100013FC000000121 -000022400000002100002250000001210002CA20000000210002CA3000000121000A72E000000021000A72F000000121 -0016E480000000210016E68000000121000004A000000021000006A00000012100013A6000000021000AB76000000121 -000015E000000021000015F00000012100001C400000003100001C500000013100001C60000002310001F40000000021 -0001F48000000121000A658000000021000A659000000121000022C000000021000022D0000001210010C9F000000021 -0010CDF0000001210001F5000000002100003C53130001220001FA70000000310001FAF0000001310001F673B9000232 -0000042000000021000006200000012100013AE000000021000AB7E0000001210016E400000000210016E60000000121 -00000D200000002100000F20000001210001E820000000210001E8300000012100013D5000000021000ABA5000000121 -00010E00000000210001CA0000000121000013D000000021000013E00000012100004060000000210000456000000121 -000029D000000021000A7B20000001210000139000000021000013A00000012100001980000000210000199000000121 -0001EBC0000000210001EBD00000012100003AB00000002100003CB000000121000040E000000021000045E000000121 -000A7B6000000021000A7B7000000121000040A000000021000045A0000001210001EC00000000210001EC1000000121 -0010572000000021001059900000012100024B900000002100024D30000001210002C160000000210002C46000000121 -000019C000000021000026F00000012100003A700000002100003C7000000121000A692000000021000A693000000121 -00000C500000003100000E5000000131000212B000000231000052600000002100005270000001210000266000000021 -000A7AA000000121001057A00000002100105A1000000121001057E00000002100105A50000001210001FD0000000021 -0001FD800000012100000460000000210000066000000121001E90A000000021001E92C0000001210000228000000021 -000022900000012100013A2000000021000AB72000000121000A726000000021000A7270000001210010C93000000021 -0010CD3000000121000049A000000021000049B00000012100000D600000002100000F6000000121000010C000000021 -000010D0000001210010C970000000210010CD700000012100001620000000210000163000000121000019E000000021 -00002200000001210001E970000000210000074308000122000039200000003100003B200000013100003D0000000231 -0001E300000000210001E310000001210001FA30000000310001FAB0000001310001F633B900023200118A8000000021 -00118C8000000121000216D000000021000217D0000001210002C9E0000000210002C9F000000121000004E000000021 -000006E00000012100000B5000000031000039C00000013100003BC000000231000048E000000021000048F000000121 -000A72A000000021000A72B00000012100104100000000210010438000000121000038A00000002100003AF000000121 -0001EFE0000000210001EFF00000012100118AC00000002100118CC00000012100001EA00000002100001EB000000121 -0001F060000000210001F0E0000001210001FC30000000310001FCC00000013100003B73B900023200010B4000000021 -0002D14000000121000026A000000021000A7AE000000121000041200000003100004320000001310001C80000000231 -0001F860000000310001F8E0000001310001F063B90002320010C9B0000000210010CDB000000121000A760000000021 -000A76100000012100024C800000002100024E200000012100001A400000002100001A5000000121000039F000000021 -00003BF00000012100010C40000000210002D2400000012100004A200000002100004A30000001210002C03000000021 -0002C330000001210002CE00000000210002CE10000001210010576000000021001059D0000001210001F96000000031 -0001F9E0000001310001F263B900023200004E000000002100004E10000001210002C2A0000000210002C5A000000121 -0001ED00000000210001ED100000012100000C200000002100000E200000012100004E800000002100004E9000000121 -000A7A6000000021000A7A700000012100010F90000000210001CB900000012100001450000000210000146000000121 -000037C00000002100003FE0000001210001EC80000000210001EC9000000121000041A000000021000043A000000121 -00000CA00000002100000EA0000001210001F860000000310001F8E0000001310001F063B90002320000421000000031 -00004410000001310001C830000002310010404000000021001042C000000121000FB02000000021000006606C000122 -000053F000000021000056F0000001210000547000000021000057700000012100013B6000000021000AB86000000121 -00013EC000000021000ABBC000000121000FF2C000000021000FF4C000000121000A7BA000000021000A7BB000000121 -000A648000000021000A64900000012100013C5000000021000AB95000000121000FF24000000021000FF44000000121 -001040F0000000210010437000000121001041C0000000210010444000000121001058300000002100105AA000000121 -0010C830000000210010CC3000000121000024100000002100002420000001210001E760000000210001E77000000121 -00010BC0000000210002D1C0000001210002C220000000210002C5200000012100010D00000000210001C90000000121 -000A768000000021000A769000000121000A732000000021000A733000000121000011C000000021000011D000000121 -000042700000002100004470000001210001E400000000210001E4100000012100118B100000002100118D1000000121 -0000152000000021000015300000012100003A100000003100003C100000013100003F10000002310000397000000021 -00003B700000012100001AC00000002100001AD00000012100001E200000002100001E30000001210000218000000021 -0000219000000121001E91A000000021001E93C000000121000051600000002100005170000001210001F27000000021 -0001F2F000000121000A7C9000000021000A7CA0000001210001FE00000000210001FE80000001210002165000000021 -0002175000000121000A66C000000021000A66D000000121000039300000002100003B30000001210000532000000021 -0000562000000121000005600000002100000760000001210001F330000000210001F3B0000001210000554000000021 -00005840000001210001C88000000031000A64A000000131000A64B000000231000A68E000000021000A68F000000121 -00003D800000002100003D900000012100024C500000002100024DF0000001210002CB20000000210002CB3000000121 -0010C820000000210010CC2000000121000005700000002100000770000001210016E530000000210016E73000000121 -000FF37000000021000FF570000001210001EF60000000210001EF7000000121001E912000000021001E934000000121 -0001F120000000210001F1A00000012100004A600000002100004A7000000121000016A000000021000016B000000121 -0001F340000000210001F3C0000001210002CDC0000000210002CDD00000012100001B1000000021000028A000000121 -00118A400000002100118C40000001210001E280000000210001E2900000012100001BF00000002100001F7000000121 -000018B000000021000018C00000012100010A70000000210002D0700000012100001280000000210000129000000121 -0001E6A0000000210001E6B00000012100010AB0000000210002D0B000000121000041F000000021000043F000000121 -0002C2E0000000210002C5E0000001210010CAF0000000210010CEF00000012100104C500000002100104ED000000121 -00118A500000002100118C50000001210001FE10000000210001FE900000012100003A600000003100003C6000000131 -00003D50000002310002C0A0000000210002C3A000000121000050E000000021000050F0000001210000104000000021 -0000105000000121000037200000002100003730000001210001E8E0000000210001E8F0000001210000396000000021 -00003B60000001210001EFA0000000210001EFB00000012100003F200000002100003F9000000121000A640000000021 -000A64100000012100010EC0000000210001CAC00000012100118A100000002100118C10000001210001FA7000000031 -0001FAF0000001310001F673B9000232000016600000002100001670000001210001F370000000210001F3F000000121 -0010C860000000210010CC600000012100013DD000000021000ABAD0000001210001F510000000210001F59000000121 -0002C9A0000000210002C9B00000012100001FA00000002100001FB00000012100021690000000210002179000000121 -0016E570000000210016E7700000012100010A40000000210002D0400000012100000530000000310000073000000131 -000017F0000002310000376000000021000037700000012100013B1000000021000AB810000001210000468000000021 -000046900000012100013E4000000021000ABB40000001210001F960000000310001F9E0000001310001F263B9000232 -000010800000002100001090000001210010CA00000000210010CE00000001210001E660000000210001E67000000121 -0001E8A0000000210001E8B0000001210001E4C0000000210001E4D0000001210001F750000000210001FCB000000121 -0002C0E0000000210002C3E0000001210001E500000000210001E5100000012100024C000000002100024DA000000121 -000A750000000021000A75100000012100000DA00000002100000FA00000012100010E90000000210001CA9000000121 -0002CD80000000210002CD90000001210001EB80000000210001EB9000000121000012C000000021000012D000000121 -00002520000000210002C7000000012100004C300000002100004C400000012100003B00000000310001FE3000000131 -00003C530830123300024C400000002100024DE0000001210002C0F0000000210002C3F0000001210000533000000021 -0000563000000121000019500000002100001F60000001210001F130000000210001F1B00000012100104B8000000021 -00104E0000000121000055300000002100005830000001210010425000000021001044D00000012100013D1000000021 -000ABA100000012100004F400000002100004F500000012100004C700000002100004C800000012100104B5000000021 -00104DD00000012100004D000000002100004D1000000121000A75C000000021000A75D0000001210010CAC000000021 -0010CEC0000001210001E2C0000000210001E2D00000012100010A80000000210002D080000001210000464000000021 -00004650000001210000426000000021000044600000012100004E400000002100004E500000012100010ED000000021 -0001CAD000000121000A792000000021000A7930000001210001EDC0000000210001EDD00000012100104BC000000021 -00104E40000001210010577000000021001059E0000001210001EA80000000210001EA900000012100001B8000000021 -00001B900000012100000C600000002100000E6000000121000FF30000000021000FF500000001210016E44000000021 -0016E64000000121001041B00000002100104430000001210001E0E0000000210001E0F00000012100004D4000000021 -00004D50000001210002CAE0000000210002CAF0000001210002C6B0000000210002C6C000000121000FF23000000021 -000FF43000000121000018400000002100001850000001210010573000000021001059A0000001210000398000000041 -00003B800000014100003D100000024100003F400000034100013D4000000021000ABA4000000121001E919000000021 -001E93B000000121000018700000002100001880000001210002C130000000210002C43000000121000A7A2000000021 -000A7A300000012100001940000000210000263000000121000041600000002100004360000001210002CBE000000021 -0002CBF0000001210001FA60000000310001FAE0000001310001F663B900023200104C600000002100104EE000000121 -0001F820000000310001F8A0000001310001F023B900023200104C900000002100104F100000012100013E0000000021 -000ABB000000012100002400000000210002C7F000000121000A7BE000000021000A7BF0000001210001F92000000031 -0001F9A0000001310001F223B90002320000402000000021000045200000012100005360000000210000566000000121 -00005500000000210000580000000121000A682000000021000A683000000121000038600000002100003AC000000121 -0001FE50000000210001FEC00000012100024C900000002100024E300000012100000D300000002100000F3000000121 -00010F00000000210001CB00000001210001E3C0000000210001E3D0000001210002C800000000210002C81000000121 -0001F970000000310001F9F0000001310001F273B9000232001058A00000002100105B10000001210000156000000021 -00001570000001210001E7A0000000210001E7B00000012100013ED000000021000ABBD000000121000A73E000000021 -000A73F00000012100001180000000210000119000000121000021400000002100002150000001210001E60000000031 -0001E610000001310001E9B0000002310002C8A0000000210002C8B000000121000A64C000000021000A64D000000121 -000039500000003100003B500000013100003F500000023100001E600000002100001E70000001210001F85000000031 -0001F8D0000001310001F053B9000232000051200000002100005130000001210001F610000000210001F69000000121 -000A668000000021000A669000000121001059400000002100105BB0000001210001E180000000210001E19000000121 -00013B2000000021000AB820000001210010415000000021001043D00000012100104180000000210010440000000121 -0000245000000021000028C0000001210001F720000000210001FC800000012100118B500000002100118D5000000121 -00010B80000000210002D1800000012100004C000000002100004CF000000121001040B0000000210010433000000121 -0000423000000021000044300000012100010C50000000210002D250000001210002C2D0000000210002C5D000000121 -0001F300000000210001F3800000012100013C1000000021000AB91000000121000A76C000000021000A76D000000121 -00104CC00000002100104F40000001210001F710000000210001FBB000000121000FF33000000021000FF53000000121 -000216A000000021000217A000000121000014900000002100002BC06E00012200003E800000002100003E9000000121 -000A7D0000000021000A7D1000000121000005A000000021000007A0000001210010CB00000000210010CF0000000121 -00001D900000002100001DA0000001210001ECC0000000210001ECD00000012100001B500000002100001B6000000121 -000054300000002100005730000001210001F220000000210001F2A000000121001058700000002100105AE000000121 -0001EA00000000210001EA10000001210010C8F0000000210010CCF0000001210016E540000000210016E74000000121 -0001FB400000002100003AC3B90001220001D7D0000000210002C63000000121000A7D6000000021000A7D7000000121 -000A74C000000021000A74D00000012100013CE000000021000AB9E00000012100000CF00000002100000EF000000121 -00010D90000000210001C990000001210000045000000021000006500000012100001360000000210000137000000121 -000040D000000021000045D0000001210016E430000000210016E630000001210001F7C0000000210001FFA000000121 -000024A000000021000024B00000012100004B800000002100004B90000001210002CCA0000000210002CCB000000121 -0002CED0000000210002CEE000000121000019F000000021000027500000012100003A400000002100003C4000000121 -000FB17000000021000057456D000122000A74E000000021000A74F00000012100104C000000002100104E8000000121 -0001E160000000210001E1700000012100013AF000000021000AB7F00000012100005000000000210000501000000121 -0002C820000000210002C8300000012100010BD0000000210002D1D000000121001E916000000021001E938000000121 -0010CA30000000210010CE30000001210010C940000000210010CD400000012100104C200000002100104EA000000121 -00003A600000003100003C600000013100003D50000002310001F930000000310001F9B0000001310001F233B9000232 -00118B400000002100118D400000012100013E9000000021000ABB90000001210016E4D0000000210016E6D000000121 -0001F410000000210001F490000001210000287000000021000A7B100000012100005420000000210000572000000121 -0001E580000000210001E590000001210001F430000000210001F4B0000001210001F910000000310001F99000000131 -0001F213B90002320002CEB0000000210002CEC0000001210001FD10000000210001FD90000001210001EE8000000021 -0001EE900000012100013A3000000021000AB7300000012100003EE00000002100003EF00000012100118B8000000021 -00118D80000001210010C9E0000000210010CDE00000012100013EB000000021000ABBB00000012100000FF000000021 -00001780000001210010417000000021001043F00000012100118A900000002100118C90000001210001FB0000000021 -0001FB80000001210002C880000000210002C8900000012100013A9000000021000AB79000000121001E909000000021 -001E92B00000012100013C2000000021000AB9200000012100024B600000002100024D00000001210001E5E000000021 -0001E5F0000001210002C690000000210002C6A000000121001041F0000000210010447000000121000037F000000021 -00003F30000001210000540000000021000057000000012100003900000000310001FD300000013100003B9308301233 -000004B000000031000006B000000131000212A00000023100004F800000002100004F900000012100013F3000000021 -00013FB00000012100000C900000002100000E9000000121000040F000000021000045F00000012100010B5000000021 -0002D15000000121000A746000000021000A74700000012100000CD00000002100000ED0000001210000476000000021 -0000477000000121000039E00000002100003BE0000001210001F200000000210001F2800000012100010FA000000021 -0001CBA0000001210002C210000000210002C510000001210001EA60000000210001EA70000001210001F83000000031 -0001F8B0000001310001F033B9000232000053C000000021000056C0000001210001F010000000210001F09000000121 -0002CC20000000210002CC30000001210000208000000021000020900000012100013E3000000021000ABB3000000121 -000A796000000021000A7970000001210001F640000000210001F6C000000121001059200000002100105B9000000121 -00004BE00000002100004BF0000001210001F910000000310001F990000001310001F213B900023200003E6000000021 -00003E70000001210001EE00000000210001EE10000001210001FF30000000310001FFC00000013100003C93B9000232 -00001300000000210000069307000122000041300000002100004330000001210001E98000000021000007730A000122 -000042E000000021000044E00000012100010DB0000000210001C9B00000012100010F60000000210001CB6000000121 -0002C000000000210002C3000000012100118BE00000002100118DE00000012100001A700000002100001A8000000121 -00010C30000000210002D23000000121000041500000002100004350000001210001E1E0000000210001E1F000000121 -000050600000002100005070000001210001E640000000210001E6500000012100000C700000002100000E7000000121 -000047C000000021000047D0000001210001E520000000210001E5300000012100010F40000000210001CB4000000121 -001058600000002100105AD000000121000046A000000021000046B0000001210002C270000000210002C57000000121 -00104CA00000002100104F200000012100010A20000000210002D020000001210010C890000000210010CC9000000121 -000FF32000000021000FF52000000121000039800000004100003B800000014100003D100000024100003F4000000341 -00003A900000003100003C900000013100021260000002310001EDA0000000210001EDB000000121001E901000000021 -001E923000000121000A79C000000021000A79D00000012100010D30000000210001C9300000012100024BC000000021 -00024D6000000121000FF2F000000021000FF4F00000012100002500000000210002C6F0000001210002C29000000021 -0002C59000000121001E920000000021001E94200000012100004B200000002100004B30000001210010419000000021 -0010441000000121000021000000002100002110000001210002C150000000210002C450000001210016E47000000021 -0016E67000000121000A754000000021000A7550000001210016E450000000210016E6500000012100001C7000000031 -00001C800000013100001C9000000231000047E000000021000047F00000012100001FC00000002100001FD000000121 -000020E000000021000020F000000121000024400000002100002890000001210001F770000000210001FDB000000121 -0002CD60000000210002CD7000000121001058900000002100105B000000012100004220000000410000442000000141 -0001C840000002410001C8500000034100010E70000000210001CA700000012100004FA00000002100004FB000000121 -00010D50000000210001C950000001210001EAC0000000210001EAD00000012100010B70000000210002D17000000121 -00013A1000000021000AB71000000121000A7C2000000021000A7C3000000121001E907000000021001E929000000121 -00013F500000002100013FD0000001210001EE20000000210001EE30000001210001F620000000210001F6A000000121 -0001E920000000210001E9300000012100001D500000002100001D60000001210002C060000000210002C36000000121 -0010C8B0000000210010CCB0000001210001F700000000210001FBA00000012100003EC00000002100003ED000000121 -001E915000000021001E937000000121000216E000000021000217E00000012100013C0000000021000AB90000000121 -0001EF40000000210001EF50000001210001FB20000000210001F703B9000122000FF3A000000021000FF5A000000121 -00118BC00000002100118DC00000012100004AA00000002100004AB00000012100021630000000210002173000000121 -0001E1C0000000210001E1D00000012100013C8000000021000AB9800000012100118AF00000002100118CF000000121 -0001E9A00000002100000612BE000122001057100000002100105980000001210010CA70000000210010CE7000000121 -0010C980000000210010CD80000001210000051000000021000007100000012100004F200000002100004F3000000121 -0010CAA0000000210010CEA00000012100010AF0000000210002D0F0000001210002C8E0000000210002C8F000000121 -0002CC40000000210002CC50000001210001E0A0000000210001E0B000000121000FB15000000021000057456B000122 -0016E500000000210016E70000000121001058E00000002100105B500000012100104110000000210010439000000121 -0010423000000021001044B000000121000053A000000021000056A0000001210001E6C0000000210001E6D000000121 -00104BE00000002100104E60000001210010424000000021001044C00000012100010A90000000210002D09000000121 -00024C200000002100024DC0000001210010C810000000210010CC100000012100013BC000000021000AB8C000000121 -000046200000003100004630000001310001C8700000023100118A600000002100118C60000001210001E4A000000021 -0001E4B000000121000216200000002100021720000001210001ED40000000210001ED500000012100010AA000000021 -0002D0A00000012100004EC00000002100004ED0000001210002C0C0000000210002C3C000000121000A79A000000021 -000A79B00000012100004CB00000002100004CC00000012100104BD00000002100104E50000001210000370000000021 -00003710000001210001FE700000002100003C5308342123001E914000000021001E93600000012100003DA000000021 -00003DB0000001210001F350000000210001F3D0000001210016E510000000210016E710000001210000392000000031 -00003B200000013100003D00000002310002C960000000210002C970000001210001FA50000000310001FAD000000131 -0001F653B9000232000039100000002100003B100000012100005340000000210000564000000121000041B000000021 -000043B00000012100013DF000000021000ABAF0000001210002C2C0000000210002C5C0000001210000124000000021 -00001250000001210010CA20000000210010CE20000001210001E8C0000000210001E8D0000001210001EB4000000021 -0001EB5000000121000016C000000021000016D0000001210001E020000000210001E03000000121000019A000000021 -000023D000000121000010200000002100001030000001210001F140000000210001F1C000000121000FF28000000021 -000FF480000001210010C800000000210010CC000000012100010EE0000000210001CAE0000001210001F55000000021 -0001F5D000000121000A77B000000021000A77C0000001210001FC700000002100003B73423B91230000059000000021 -0000079000000121000014A000000021000014B0000001210001E240000000210001E2500000012100013BD000000021 -000AB8D0000001210002CBC0000000210002CBD000000121001040D0000000210010435000000121001058F000000021 -00105B600000012100118A700000002100118C7000000121000A660000000021000A6610000001210000514000000021 -0000515000000121000052E000000021000052F00000012100004A400000002100004A50000001210000400000000021 -00004500000001210016E580000000210016E78000000121000A64E000000021000A64F000000121000FF2E000000021 -000FF4E000000121001040C000000021001043400000012100000DB00000002100000FB00000012100001CD000000021 -00001CE00000012100010E80000000210001CA8000000121000054F000000021000057F000000121000A738000000021 -000A7390000001210016E5F0000000210016E7F0000001210001FA60000000310001FAE0000001310001F663B9000232 -001E90F000000021001E9310000001210002C070000000210002C3700000012100010A30000000210002D03000000121 -000A7A0000000021000A7A1000000121000042100000003100004410000001310001C83000000231000A680000000021 -000A6810000001210000282000000021000A7C50000001210001D79000000021000A77D00000012100024CB000000021 -00024E500000012100004010000000210000451000000121000A758000000021000A7590000001210000058000000021 -0000078000000121000019300000002100002600000001210010CA10000000210010CE1000000121000054E000000021 -000057E0000001210000186000000021000025400000012100013DC000000021000ABAC00000012100024BD000000021 -00024D700000012100001B300000002100001B4000000121000A696000000021000A6970000001210000216000000021 -00002170000001210001ECE0000000210001ECF000000121000216800000002100021780000001210001FC6000000021 -00003B73420001220002C120000000210002C420000001210002C2F0000000210002C5F000000121001E90E000000021 -001E9300000001210010CAB0000000210010CEB0000001210001E860000000210001E870000001210000412000000031 -00004320000001310001C8000000023100013D6000000021000ABA60000001210001EBA0000000210001EBB000000121 -001E91B000000021001E93D000000121001041A00000002100104420000001210001EEE0000000210001EEF000000121 -000FF31000000021000FF51000000121000A7BC000000021000A7BD00000012100001DB00000002100001DC000000121 -00001A600000002100002800000001210001F070000000210001F0F00000012100118BD00000002100118DD000000121 -00000C800000002100000E800000012100004D200000002100004D300000012100010E10000000210001CA1000000121 -00013B7000000021000AB87000000121001058500000002100105AC0000001210016E4B0000000210016E6B000000121 -00001720000000210000173000000121000049E000000021000049F00000012100004E600000002100004E7000000121 -00010B00000000210002D100000001210001E99000000021000007930A00012200001EE00000002100001EF000000121 -0002C9C0000000210002C9D00000012100104B400000002100104DC0000001210010C870000000210010CC7000000121 -00013BA000000021000AB8A0000001210001E100000000210001E1100000012100002300000000210000231000000121 -000042800000002100004480000001210002CD00000000210002CD100000012100118A000000002100118C0000000121 -00003E000000002100003E10000001210001FF400000002100003CE3B9000122000A780000000021000A781000000121 -00010F50000000210001CB5000000121000053B000000021000056B00000012100000D500000002100000F5000000121 -001058800000002100105AF000000121000041400000003100004340000001310001C810000002310001FA4000000031 -0001FAC0000001310001F643B9000232000012A000000021000012B00000012100000520000000210000072000000121 -00024B700000002100024D1000000121001E908000000021001E92A000000121000039800000004100003B8000000141 -00003D100000024100003F4000000341000020200000002100002030000001210001F850000000310001F8D000000131 -0001F053B90002320000470000000021000047100000012100002710000000210002C6E0000001210002C1B000000021 -0002C4B000000121000FB030000000210000066066069123000048A000000021000048B000000121000A752000000021 -000A753000000121000FF25000000021000FF450000001210000407000000021000045700000012100000C1000000021 -00000E10000001210001E440000000210001E450000001210001F970000000310001F9F0000001310001F273B9000232 -00005480000000210000578000000121000021C000000021000021D00000012100013E5000000021000ABB5000000121 -000051A000000021000051B0000001210002CB60000000210002CB7000000121000A654000000021000A655000000121 -0010403000000021001042B0000001210001E720000000210001E7300000012100010D40000000210001C94000000121 -00104C100000002100104E90000001210010C8A0000000210010CCA0000001210001E2A0000000210001E2B000000121 -000046200000003100004630000001310001C870000002310001F760000000210001FDA0000001210000053000000031 -0000073000000131000017F0000002310002C260000000210002C5600000012100013A0000000021000AB70000000121 -00003FA00000002100003FB00000012100004C500000002100004C60000001210001F920000000310001F9A000000131 -0001F223B90002320000555000000021000058500000012100013E8000000021000ABB800000012100001C7000000031 -00001C800000013100001C90000002310001F420000000210001F4A0000001210001F670000000210001F6F000000121 -001057C00000002100105A3000000121000A7B4000000021000A7B5000000121000A698000000021000A699000000121 -001E904000000021001E926000000121000040C000000021000045C00000012100010DA0000000210001C9A000000121 -0001FD600000002100003B934200012200000D000000002100000F00000001210016E460000000210016E66000000121 -0001EC20000000210001EC300000012100001E400000002100001E500000012100000440000000210000064000000121 -000017D000000021000017E0000001210001F210000000210001F29000000121000FF39000000021000FF59000000121 -00013AC000000021000AB7C000000121000058700000002100005655820001220001F000000000210001F08000000121 -00003A500000002100003C50000001210000158000000021000015900000012100104CF00000002100104F7000000121 -0001E3A0000000210001E3B000000121000A72C000000021000A72D00000012100118B700000002100118D7000000121 -000011200000002100001130000001210001E360000000210001E370000001210016E4E0000000210016E6E000000121 -0001F7D0000000210001FFB000000121000049400000002100004950000001210001E800000000210001E81000000121 -00001160000000210000117000000121000042D000000021000044D00000012100013F200000002100013FA000000121 -0000179000000021000017A00000012100004DA00000002100004DB0000001210002CA80000000210002CA9000000121 -00000CC00000002100000EC00000012100001A200000002100001A300000012100003A100000003100003C1000000131 -00003F10000002310001E7C0000000210001E7D00000012100013CF000000021000AB9F0000001210010412000000021 -001043A000000121000052000000002100005210000001210002C1C0000000210002C4C000000121000026B000000021 -0002C62000000121000FB140000000210000574565000122000015C000000021000015D000000121000A652000000021 -000A653000000121000FF26000000021000FF4600000012100001E800000002100001E900000012100010B6000000021 -0002D16000000121000A656000000021000A657000000121000051C000000021000051D00000012100013C3000000021 -000AB93000000121001059100000002100105B800000012100104CB00000002100104F30000001210001EC6000000021 -0001EC700000012100001E000000002100001E10000001210001F630000000210001F6B0000001210002C01000000021 -0002C31000000121001E900000000021001E9220000001210001E780000000210001E7900000012100024CE000000021 -00024E8000000121000A76A000000021000A76B000000121000011A000000021000011B0000001210000429000000021 -00004490000001210001F840000000310001F8C0000001310001F043B900023200010D60000000210001C96000000121 -0001FB10000000210001FB9000000121000A762000000021000A7630000001210010CB20000000210010CF2000000121 -0002C200000000210002C5000000012100010BE0000000210002D1E000000121001041E0000000210010446000000121 -0001FF700000002100003C93423B912300004E200000002100004E30000001210001F900000000310001F98000000131 -0001F203B900023200004DE00000002100004DF0000001210001F940000000310001F9C0000001310001F243B9000232 -00005410000000210000571000000121000054500000002100005750000001210001F250000000210001F2D000000121 -000015400000002100001550000001210002C240000000210002C5400000012100013EA000000021000ABBA000000121 -0000181000000021000025300000012100003A300000003100003C200000013100003C300000023100118B3000000021 -00118D300000012100010BA0000000210002D1A000000121000018000000002100002430000001210001E3E000000021 -0001E3F00000012100010FF0000000210001CBF0000001210001FF30000000310001FFC00000013100003C93B9000232 -00010D20000000210001C92000000121000A76E000000021000A76F0000001210010C880000000210010CC8000000121 -0001C88000000031000A64A000000131000A64B00000023100010B20000000210002D1200000012100013C7000000021 -000AB9700000012100005180000000210000519000000121000A766000000021000A76700000012100001EC000000021 -00001ED00000012100104C300000002100104EB0000001210010574000000021001059B00000012100013EE000000021 -000ABBE000000121001E91C000000021001E93E00000012100010E20000000210001CA20000001210002C05000000021 -0002C3500000012100004A000000002100004A10000001210001E420000000210001E4300000012100013B4000000021 -000AB8400000012100118BF00000002100118DF000000121000015000000002100001510000001210001F56000000021 -00003C53133421230002CB00000000210002CB100000012100013A4000000021000AB7400000012100004C1000000021 -00004C200000012100001CA00000003100001CB00000013100001CC000000231000004C000000021000006C000000121 -00104C700000002100104EF0000001210016E5A0000000210016E7A00000012100002220000000210000223000000121 -000049000000002100004910000001210002CF20000000210002CF3000000121001059500000002100105BC000000121 -000053D000000021000056D000000121000038800000002100003AD00000012100005490000000210000579000000121 -00013E2000000021000ABB20000001210016E4A0000000210016E6A0000001210001EFC0000000210001EFD000000121 -0001F900000000310001F980000001310001F203B900023200004D600000002100004D7000000121000A7B8000000021 -000A7B9000000121000041400000003100004340000001310001C810000002310010CAD0000000210010CED000000121 -00010F70000000210001CB70000001210016E550000000210016E75000000121000A728000000021000A729000000121 -0001E000000000210001E010000001210000232000000021000023300000012100010A50000000210002D05000000121 -0001F150000000210001F1D00000012100003A900000003100003C90000001310002126000000231000013B000000021 -000013C0000001210000408000000021000045800000012100000DC00000002100000FC0000001210001EBE000000021 -0001EBF0000001210002C180000000210002C48000000121000A7A8000000021000A7A9000000121000216B000000021 -000217B0000001210002C100000000210002C400000001210002CDE0000000210002CDF000000121000029E000000021 -000A7B000000012100000D400000002100000F400000012100010EF0000000210001CAF0000001210000551000000021 -000058100000012100013DA000000021000ABAA000000121000053500000002100005650000001210001FA0000000031 -0001FA80000001310001F603B900023200024C600000002100024E000000012100004100000000210000430000000121 -0001EB60000000210001EB70000001210001F840000000310001F8C0000001310001F043B90002320010416000000021 -001043E0000001210016E420000000210016E62000000121000016400000002100001650000001210002CA0000000021 -0002CA10000001210001E2E0000000210001E2F0000001210010C950000000210010CD50000001210000498000000021 -0000499000000121000010A000000021000010B0000001210010C9D0000000210010CDD00000012100118AA000000021 -00118CA0000001210001E880000000210001E890000001210000528000000021000052900000012100013D7000000021 -000ABA7000000121000A65A000000021000A65B000000121000022A000000021000022B0000001210001FA5000000031 -0001FAD0000001310001F653B900023200104B300000002100104DB000000121001E90C000000021001E92E000000121 -00024BB00000002100024D500000012100001F400000002100001F5000000121000A690000000021000A691000000121 -000FF29000000021000FF490000001210002C750000000210002C760000001210002CB80000000210002CB9000000121 -001040100000002100104290000001210002C0D0000000210002C3D00000012100104D000000002100104F8000000121 -001E913000000021001E93500000012100001AF00000002100001B0000000121000A7C7000000021000A7C8000000121 -00013BF000000021000AB8F0000001210010402000000021001042A00000012100010EA0000000210001CAA000000121 -00104BF00000002100104E7000000121001058D00000002100105B400000012100021640000000210002174000000121 -000018E00000002100001DD00000012100001D300000002100001D40000001210001F100000000210001F18000000121 -0001F570000000210001F5F0000001210001ED20000000210001ED3000000121000FF2A000000021000FF4A000000121 -0001E040000000210001E0500000012100004EE00000002100004EF0000001210002C940000000210002C95000000121 -00000C000000002100000E00000001210001FE600000002100003C534200012200013DB000000021000ABAB000000121 -00001B2000000021000028B0000001210010426000000021001044E000000121000A662000000021000A663000000121 -0000054000000021000007400000012100005560000000210000586000000121000042A000000031000044A000000131 -0001C860000002310010422000000021001044A0000001210001F360000000210001F3E0000001210001FA1000000031 -0001FA90000001310001F613B900023200004CD00000002100004CE00000012100104BB00000002100104E3000000121 -000023B000000021000023C00000012100001CF00000002100001D0000000121000050C000000021000050D000000121 -000A646000000021000A6470000001210001E460000000210001E470000001210010CA60000000210010CE6000000121 -000A779000000021000A77A000000121000FF36000000021000FF5600000012100118A200000002100118C2000000121 -00004EA00000002100004EB000000121000041D000000021000043D000000121000A794000000021000A7C4000000121 -000A688000000021000A6890000001210001EB20000000210001EB300000012100021600000000210002170000000121 -0001ED60000000210001ED700000012100024C300000002100024DD0000001210016E5E0000000210016E7E000000121 -00004AC00000002100004AD0000001210016E520000000210016E72000000121000023E0000000210002C66000000121 -0002CB40000000210002CB5000000121000FF2D000000021000FF4D000000121000014C000000021000014D000000121 -0001E220000000210001E2300000012100013BB000000021000AB8B00000012100001430000000210000144000000121 -000FB04000000021000006606606C123001058100000002100105A800000012100001700000000210000171000000121 -0001E940000000210001E950000001210001FC20000000210001F743B90001220002C280000000210002C58000000121 -001E910000000021001E932000000121001040E00000002100104360000001210001F830000000310001F8B000000131 -0001F033B9000232000A798000000021000A79900000012100013B8000000021000AB880000001210001E70000000021 -0001E71000000121000042200000004100004420000001410001C840000002410001C850000003410010C85000000021 -0010CC50000001210001EAE0000000210001EAF0000001210000122000000021000012300000012100104B2000000021 -00104DA0000001210010C8C0000000210010CCC0000001210010405000000021001042D0000001210001F87000000031 -0001F8F0000001310001F073B90002320000420000000021000044000000012100001AE0000000210000288000000121 -000021A000000021000021B000000121000038C00000002100003CC0000001210001F950000000310001F9D000000131 -0001F253B90002320000345000000041000039900000014100003B90000002410001FBE000000341000018A000000021 -000025700000012100003900000000310001FD300000013100003B930830123300010B10000000210002D11000000121 -000011E000000021000011F0000001210001E080000000210001E090000001210002C900000000210002C91000000121 -000A7C0000000021000A7C100000012100013E7000000021000ABB7000000121001058000000002100105A7000000121 -000041C000000021000043C000000121000FB05000000031000FB060000001310000073074000232000A7A4000000021 -000A7A50000001210001E740000000210001E750000001210002C140000000210002C44000000121001E90D000000021 -001E92F00000012100013A5000000021000AB7500000012100010F30000000210001CB300000012100024BA000000021 -00024D4000000121000013F0000000210000140000000121000039D00000002100003BD0000001210002CE2000000021 -0002CE300000012100010A10000000210002D0100000012100005080000000210000509000000121000004D000000021 -000006D000000121000038900000002100003AE000000121000048C000000021000048D0000001210002C04000000021 -0002C34000000121000216F000000021000217F00000012100004B000000002100004B10000001210002CD2000000021 -0002CD3000000121000A734000000021000A735000000121000FB0100000002100000660690001220000050000000021 -000007000000012100001600000000210000161000000121001058400000002100105AB0000001210001E32000000021 -0001E330000001210010CA90000000210010CE90000001210000200000000021000020100000012100118AB000000021 -00118CB000000121000A786000000021000A78700000012100118BB00000002100118DB00000012100118AE000000021 -00118CE000000121000021E000000021000021F00000012100003AA00000002100003CA00000012100010C2000000021 -0002D2200000012100013C4000000021000AB9400000012100005390000000210000569000000121000A74A000000021 -000A74B00000012100000C300000002100000E3000000121000046E000000021000046F000000121000054D000000021 -000057D0000001210001FE200000002100003C530830012300010E30000000210001CA30000001210001F52000000021 -00003C531330012300003DE00000002100003DF00000012100024CA00000002100024E40000001210000345000000041 -000039900000014100003B90000002410001FBE0000003410010C990000000210010CD9000000121000A75A000000021 -000A75B000000121000041900000002100004390000001210002C090000000210002C39000000121000037D000000021 -00003FF0000001210001F260000000210001F2E00000012100118B200000002100118D20000001210002183000000021 -00021840000001210002C230000000210002C53000000121000013200000002100001330000001210001E56000000021 -0001E5700000012100010AE0000000210002D0E000000121000A740000000021000A74100000012100000DD000000021 -00000FD00000012100010E60000000210001CA60000001210001E600000000310001E610000001310001E9B000000231 -00004780000000210000479000000121000022E000000021000022F0000001210001FF600000002100003C9342000122 -0001FD200000002100003B9308300123000A694000000021000A695000000121000052C000000021000052D000000121 -00105700000000210010597000000121000A666000000021000A66700000012100001F0000000021000006A30C000122 -0010C960000000210010CD6000000121000020A000000021000020B00000012100024BF00000002100024D9000000121 -00013CB000000021000AB9B0000001210010C9C0000000210010CDC000000121000041E000000031000043E000000131 -0001C820000002310001E840000000210001E850000001210001EF00000000210001EF100000012100013A8000000021 -000AB780000001210016E590000000210016E790000001210002CA40000000210002CA5000000121000A724000000021 -000A725000000121000049C000000021000049D000000121000010E000000021000010F0000001210001FA4000000031 -0001FAC0000001310001F643B900023200104D300000002100104FB000000121001E903000000021001E925000000121 -000019100000002100001920000001210001F800000000310001F880000001310001F003B900023200013DE000000021 -000ABAE00000012100003A000000003100003C000000013100003D600000023100013AD000000021000AB7D000000121 -000020600000002100002070000001210001F450000000210001F4D00000012100003EA00000002100003EB000000121 -00010BB0000000210002D1B00000012100004740000000210000475000000121000FF38000000021000FF58000000121 -00004FE00000002100004FF0000001210002C840000000210002C850000001210010413000000021001043B000000121 -0001FD700000002100003B93083421230001E5C0000000210001E5D0000001210001EE40000000210001EE5000000121 -0002CC80000000210002CC90000001210002C1F0000000210002C4F00000012100004BA00000002100004BB000000121 -00104CE00000002100104F6000000121001E905000000021001E92700000012100000D100000002100000F1000000121 -0000472000000021000047300000012100118B600000002100118D60000001210001F780000000210001FF8000000121 -000004300000002100000630000001210001EA20000000210001EA30000001210001F240000000210001F2C000000121 -000019D000000021000027200000012100004B600000002100004B700000012100000DF0000000310001E9E000000131 -00000730730002320001F870000000310001F8F0000001310001F073B90002320001E140000000210001E15000000121 -0001EE60000000210001EE7000000121000040B000000021000045B00000012100004090000000210000459000000121 -001057D00000002100105A400000012100003A800000002100003C80000001210001E5A0000000210001E5B000000121 -001057F00000002100105A60000001210000544000000021000057400000012100010FE0000000210001CBE000000121 -0001E120000000210001E1300000012100010B90000000210002D1900000012100000470000000210000067000000121 -00004FC00000002100004FD0000001210010C920000000210010CD20000001210002C860000000210002C87000000121 -0001E1A0000000210001E1B0000001210010C900000000210010CD000000012100010BF0000000210002D1F000000121 -00013CA000000021000AB9A0000001210016E490000000210016E6900000012100000410000000210000061000000121 -00001BC00000002100001BD0000001210001EEA0000000210001EEB00000012100005020000000210000503000000121 -000024800000002100002490000001210001F950000000310001F9D0000001310001F253B900023200010DF000000021 -0001C9F00000012100001C400000003100001C500000013100001C600000023100005460000000210000576000000121 -00013EF000000021000ABBF0000001210002CCC0000000210002CCD00000012100010D70000000210001C97000000121 -00013A7000000021000AB77000000121000047A000000021000047B00000012100001340000000210000135000000121 -000042A000000031000044A0000001310001C860000002310002C190000000210002C490000001210000204000000021 -0000205000000121000025C000000021000A7AB00000012100024CF00000002100024E90000001210001E54000000021 -0001E55000000121001057900000002100105A0000000121000054A000000021000057A000000121000A684000000021 -000A685000000121000FF27000000021000FF470000001210001F050000000210001F0D000000121000FB13000000021 -00005745760001220002132000000021000214E000000121000024C000000021000024D00000012100013B5000000021 -000AB850000001210002CC60000000210002CC7000000121001040800000002100104300000001210002C1D000000021 -0002C4D000000121000023F0000000210002C7E000000121000A742000000021000A7430000001210001E62000000021 -0001E630000001210010CA80000000210010CE800000012100000B5000000031000039C00000013100003BC000000231 -0001F810000000310001F890000001310001F013B90002320010C9A0000000210010CDA000000121000012E000000021 -000012F0000001210000403000000021000045300000012100001FE00000002100001FF0000001210000345000000041 -000039900000014100003B90000002410001FBE0000003410000174000000021000017500000012100003CF000000021 -00003D700000012100024CD00000002100024E70000001210001FB30000000310001FBC00000013100003B13B9000232 -00118AD00000002100118CD000000121000A744000000021000A74500000012100000D900000002100000F9000000121 -0001E0C0000000210001E0D0000001210002C170000000210002C4700000012100003E200000002100003E3000000121 -000026C000000021000A7AD000000121000A756000000021000A75700000012100004050000000210000455000000121 -000019700000002100002680000001210001F600000000210001F680000001210001EEC0000000210001EED000000121 -0002C8C0000000210002C8D00000012100010F20000000210001CB200000012100104B600000002100104DE000000121 -001E91D000000021001E93F000000121001040A000000021001043200000012100013C6000000021000AB96000000121 -0016E410000000210016E610000001210000424000000021000044400000012100001A90000000210000283000000121 -000039A00000003100003BA00000013100003F000000023100010E50000000210001CA500000012100104C8000000021 -00104F00000001210010407000000021001042F00000012100013D0000000021000ABA00000001210001FA2000000031 -0001FAA0000001310001F623B9000232000046C000000021000046D00000012100010A00000000210002D00000000121 -000A782000000021000A78300000012100000C500000003100000E5000000131000212B00000023100004B4000000021 -00004B50000001210002CCE0000000210002CCF0000001210001FF20000000210001F7C3B9000122000FF21000000021 -000FF41000000121000A73A000000021000A73B00000012100104B900000002100104E10000001210001EA4000000021 -0001EA5000000121000038F00000002100003CE00000012100024BE00000002100024D800000012100003B0000000031 -0001FE300000013100003C530830123300000490000000210000069000000121001E91F000000021001E941000000121 -000024600000002100002470000001210001F730000000210001FC90000001210002C670000000210002C68000000121 -000216600000002100021760000001210000100000000021000010100000012100003E400000002100003E5000000121 -00013D2000000021000ABA2000000121000A79E000000021000A79F0000001210001FA00000000310001FA8000000131 -0001F603B90002320010CB10000000210010CF10000001210016E5D0000000210016E7D00000012100010C1000000021 -0002D21000000121000A7F5000000021000A7F600000012100013BE000000021000AB8E000000121000042C000000021 -000044C0000001210001FC400000002100003AE3B900012200010DD0000000210001C9D0000001210001F93000000031 -0001F9B0000001310001F233B9000232000024E000000021000024F00000012100004BC00000002100004BD000000121 -0000176000000021000017700000012100013AB000000021000AB7B00000012100013E1000000021000ABB1000000121 -000050400000002100005050000001210001F030000000210001F0B000000121000A686000000021000A687000000121 -000054C000000021000057C0000001210016E4F0000000210016E6F00000012100010EB0000000210001CAB000000121 -00013D9000000021000ABA9000000121000023A0000000210002C6500000012100004A800000002100004A9000000121 -0002CDA0000000210002CDB000000121000041E000000031000043E0000001310001C820000002310001F81000000031 -0001F890000001310001F013B9000232000039400000002100003B40000001210002C720000000210002C73000000121 -000051000000002100005110000001210001E260000000210001E27000000121000A642000000021000A643000000121 -00004C900000002100004CA000000121000014700000002100001480000001210001EB00000000210001EB1000000121 -000A75E000000021000A75F000000121001058C00000002100105B3000000121000A73C000000021000A73D000000121 -00104D100000002100104F900000012100104D200000002100104FA000000121001059000000002100105B7000000121 -000005500000002100000750000001210001F110000000210001F19000000121000037B00000002100003FD000000121 -0001F310000000210001F390000001210002CBA0000000210002CBB00000012100005520000000210000582000000121 -0001FC30000000310001FCC00000013100003B73B90002320001E6E0000000210001E6F0000001210002167000000021 -000217700000012100000DE00000002100000FE0000001210000190000000021000025B000000121000A68C000000021 -000A68D0000001210001EF80000000210001EF900000012100104B000000002100104D80000001210010C8E000000021 -0010CCE0000001210002C080000000210002C38000000121000016E000000021000016F0000001210016E5C000000021 -0016E7C00000012100010CD0000000210002D2D0000001210001F7B0000000210001FEB0000001210001E90000000021 -0001E9100000012100013B3000000021000AB8300000012100001D100000002100001D20000001210001F53000000021 -0001F5B000000121000012600000002100001270000001210001E480000000210001E490000001210002C98000000021 -0002C99000000121001040000000002100104280000001210001FA30000000310001FAB0000001310001F633B9000232 -00118B900000002100118D900000012100013B0000000021000AB800000001210001ED80000000210001ED9000000121 -0010CA40000000210010CE40000001210001F740000000210001FCA00000012100004220000000410000442000000141 -0001C840000002410001C850000003410001EF20000000210001EF300000012100021610000000210002171000000121 -0010420000000021001044800000012100001F100000003100001F200000013100001F300000023100001D7000000021 -00001D8000000121000A68A000000021000A68B00000012100003F700000002100003F8000000121000FF35000000021 -000FF5500000012100003DC00000002100003DD0000001210010C8D0000000210010CCD0000001210000418000000021 -00004380000001210000212000000021000021300000012100004F000000002100004F100000012100024C1000000021 -00024DB000000121000050A000000021000050B000000121001E917000000021001E939000000121000A644000000021 -000A6450000001210000537000000021000056700000012100010AC0000000210002D0C00000012100010AD000000021 -0002D0D00000012100024C700000002100024E1000000121000046000000002100004610000001210002C2B000000021 -0002C5B0000001210016E560000000210016E7600000012100010C70000000210002D27000000121000FB05000000031 -000FB0600000013100000730740002320002CD40000000210002CD500000012100010F10000000210001CB1000000121 -0001F7A0000000210001FEA0000001210002C0B0000000210002C3B000000121000FF34000000021000FF54000000121 -0010406000000021001042E0000001210000417000000021000043700000012100118BA00000002100118DA000000121 -0001F820000000310001F8A0000001310001F023B900023200001F100000003100001F200000013100001F3000000231 -000039500000003100003B500000013100003F500000023100001B70000000210000292000000121000018F000000021 -000025900000012100005380000000210000568000000121000A66A000000021000A66B0000001210001F32000000021 -0001F3A00000012100104210000000210010449000000121000052A000000021000052B0000001210000425000000021 -0000445000000121000A784000000021000A785000000121000004F000000021000006F000000121000A664000000021 -000A6650000001210001FA20000000310001FAA0000001310001F623B900023200104B100000002100104D9000000121 -00000C400000002100000E40000001210010CA50000000210010CE50000001210001EAA0000000210001EAB000000121 -00013D8000000021000ABA800000012100000DF0000000310001E9E000000131000007307300023200010E4000000021 -0001CA400000012100000D800000002100000F800000012100001CA00000003100001CB00000013100001CC000000231 -00010D10000000210001C910000001210010575000000021001059C000000121000216C000000021000217C000000121 -0002C020000000210002C320000001210016E5B0000000210016E7B000000121000A7D8000000021000A7D9000000121 -000FF22000000021000FF420000001210000404000000021000045400000012100010B30000000210002D13000000121 -0001ECA0000000210001ECB00000012100104090000000210010431000000121001E918000000021001E93A000000121 -00004F600000002100004F70000001210002CC00000000210002CC10000001210001E200000000210001E21000000121 -000FB00000000021000006606600012200104B700000002100104DF000000121000039B00000002100003BB000000121 -000019600000002100002690000001210002C250000000210002C55000000121000A790000000021000A791000000121 -000A764000000021000A765000000121000020C000000021000020D0000001210001F940000000310001F9C000000131 -0001F243B900023200118B000000002100118D0000000121000018200000002100001830000001210002CAC000000021 -0002CAD0000001210001F800000000310001F880000001310001F003B900023200024CC00000002100024E6000000121 -00002510000000210002C6D000000121000048000000002100004810000001210001F5400000002100003C5313301123 -00024B800000002100024D2000000121000054B000000021000057B00000012100013AA000000021000AB7A000000121 -000004B000000031000006B000000131000212A0000002310000261000000021000A7AC0000001210000141000000021 -0000142000000121000039A00000003100003BA00000013100003F000000023100010F80000000210001CB8000000121 -00104C400000002100104EC0000001210000189000000021000025600000012100010DE0000000210001C9E000000121 -0001E680000000210001E6900000012100013E6000000021000ABB60000001210001F040000000210001F0C000000121 -0010578000000021001059F0000001210000411000000021000043100000012100104BA00000002100104E2000000121 -00004660000000210000467000000121000A736000000021000A7370000001210001FE400000002100003C1313000122 -00000CB00000002100000EB00000012100010C00000000210002D200000001210010427000000021001044F000000121 -00001DE00000002100001DF0000001210002C920000000210002C9300000012100001060000000210000107000000121 -0001E4E0000000210001E4F000000121000042B000000021000044B000000121000A748000000021000A749000000121 -0002C110000000210002C41000000121000A7B3000000021000AB53000000121000053E000000021000056E000000121 -000014E000000021000014F00000012100013D3000000021000ABA300000012100005240000000210000525000000121 -000A65E000000021000A65F00000012100001F800000002100001F900000012100004AE00000002100004AF000000121 -000A77E000000021000A77F00000012100003A000000003100003C000000013100003D60000002310000265000000021 -000A78D0000001210001FB600000002100003B13420001220001FA10000000310001FA90000001310001F613B9000232 -0001EDE0000000210001EDF00000012100013B9000000021000AB8900000012100001680000000210000169000000121 -0000226000000021000022700000012100013F100000002100013F90000001210001E060000000210001E07000000121 -00001200000000210000121000000121001041D0000000210010445000000121001058200000002100105A9000000121 -0001F660000000210001F6E0000001210001E96000000021000006833100012200010A60000000210002D06000000121 -0010C910000000210010CD10000001210001D8E000000021000A7C60000001210010C840000000210010CC4000000121 -000FF2B000000021000FF4B00000012100118A300000002100118C300000012100000480000000210000068000000121 -00013CC000000021000AB9C000000121001E91E000000021001E940000000121000038E00000002100003CD000000121 -00005310000000210000561000000121"; -return FCE(t[index]); -} - -struct uniProps -{ -private alias _U = immutable(UnicodeProperty); -@property static _U[] tab() pure { return _tab; } -static immutable: -private alias _T = ubyte[]; -_T ID_Start = x"411A061A2F010A0104010517011F0181CA040C0E05070101018081050102020401010601010301010114015301808B0880A6012602010629471B04042D2B2302016301010F0207020A0302011001011E1D590B0118210902040105160401090103011719070B05180106112A3A3603011201070A0F10040802020216010701010304030110010D0201030E020A0108060402021601070102010201021F0401011303100901030116010701020105030112010F0217010B080202021601070102010503011E0201030F01110101060303010403020101010203020303030C1601340801030117011003011A03020102021E01040801030117010A010503011F0201020F0211090103012902011001050308031806051203180109010102073A3001020C073A020101010501180101010A0102090102050101150420013F0801241B05732B140110060404030103020703040D0C01112601010501022B01814D01040207010101040229010402210104020701010104020F01390104024325101056020603826C0211011A054B030B07120D130E120E0D01030F342301040143590729010105460A1F311E02050B2C041A3617093552015D2F1108361E0D020A2C1A2429030A240209072B020329040106010203010580C04081160206022602060208010101010101011F0235010701010303010703040206040D0503010774010D01100D65010401020A01010206060101010101011002040505040111298A7780E5060403020C26010105010238070110170907010701070107010701070107010782260319090705020504560405015A0104052B015E11203010820099C040A0568D432E02810D03100A02142F101F025027090267024005020101010518100103010401171D340E323E06030101020B1C0A17191D072F1C011005010A0A05012917030108141703010332010103020205020101011803020B07030C060206020609070107012B010E06731DA02BA40C170431A02104816E026A26070C050501010A010D0105010101020102016C21816B12400236280C7405018087241A061A0B590306020602060203230C011A01130102010F020E227B4535810B1D03312F200D1E05260A1E0224040801052A809E12240424042808340C0B010F01070102010B010F0107010243813709160A081806012A010945060201012C0102030102170A17091F411301020A160A1A4638060240010F040103011D2A1D031D2308011C1B360A160A130D126E4937330D330D24815C2A06024E1D0A0108162A122E151B170C35390202010D2D20191A241D010201082303010C300E04150101012312011913023F0701010104010F010A072F260802020216010701020105030112010C05809E35120414031E301402010180B82F2904243014013B2B0D01471B250780B92C74401F0802010208010201180F0101015E080227100101011C010A28070115010B2E1301124981070901251101311E70070102012615011906010201200E018147130F01010D01227C014F839A666F1180C48A4C610F843011068FB98247A021B98239071F114F111E123010041F15051382B04080804B0501420D400201011C97F80884D62A09A022E704010701020181230F011D0302010E0408818C89046B050D0309070A97665501470102020102020204010C010101070141010402080107011C010401050101030701815402190119011F0119011F0119011F0119011F0119010887341F060681053E80922D0A07100181411E122C81E41C82F40701040102010F0180C53B44070184B404011B010201010201010A0104010101010601040101010101010301020101020101010101010101010102010102040107010401040101010A01110503010501119144A0A6E020903A0680DE0296820E9D310F826E89A2821E85E2934B059060"; -_T Cs = x"A0D8008800"; -_T Default_Ignorable_Code_Point = x"80AD0182A10182CC018B4202865302550587FB051A05311090F401A0CC9B1080EF0180A0014F09A0BCA70494CF08AC2E859000"; -_T Pd = x"2D01855C0133018E41018405018809068E010102011F0204011C0181BE0113016F01A0CD900225010A0180A9018F9F01"; -_T Unified_Ideograph = x"A0340099C040A05200A05A0E02010101020A01010101020203A105D6A0A6E020903A0680DE0296820E9D310F826E91A2934B059060"; -_T IDS_Binary_Operator = x"A02FF002020A81F101"; -_T Sm = x"2B0110033D0101012D01040125011F0182FE01820F039A3B010D0127030D03808B01270506014405050204010201020107011F02020101011F810C20025A011E19280681D501090136086F01815005021F0A1081008083163F042002810230150206A0CFDC01833801010380A40110033D0101018083010604A0D6D40119011F0119011F0119011F0119011F011901972C02"; -_T Pi = x"80AB019F6C010202020119018DC8010101040102010F010301"; -_T Other_Grapheme_Extend = x"89BE01180181660118016601180180EA0112026701180177010F018D550184D601902102A0CF6E02939E0118018158010C0180F101838001A0B834010805AC2EAD60"; -_T Grapheme_Extend = x"83007081130781072D0101010201020101480B30151001650702060202010423011E1B5B0B3A09090118040109010301052B033C082A180120370101010408040103070A021D013A0101010204080109010A021A010202390104020402020303011E0203010B0239010405010204011402160601013A0101020104080107030A021E013B0101010C01090128010301370101030503010407020B021D013A01020102010301050207020B021C02390201010204080109010A021D0148010401020301010801510102070C08620102090B0749021B0101010101370E01050102050B0124090166040106010202021902040310040D01020206010F0182BF0383B2031D021E021E02400201070801020B09012D03010175022201760304020901060380DB0202013A010107010101010208060A0201301F310430070101050128090C0220040202010338010102030101033A080202809803010D010704010601030280C640820C0180C3218BFE03808D016020822A066902A075D404010A2002500281100103010401190205018097021A120D012608190B2E0330010204020227014306020202020C0108012F01330101030202050201012A02080180EE0102010401A04F300182E1101010816E02825D0180E201809505868603010205042803040180A502823D048183025003460B31047B01360F290102020A033104020207013D03240501083E010C0234090A0402015F030201010206010201809D010308150239020101010116010E07030580C30802030101170151010206010102010102010280EB010204060201021B025508020101026A01010102060101650302040105810309010280F5010A020101040180900402020401200A280602040801090602032E0D01028196070106010152160207010201027A060301010201070101480203010101815B020B0234050501010194FD01060FA0369A053B078418013F045101A04CB80292612E0217821E0101030405080802071E0480940387BB37043208010E011605010F8550070111020701020105640180A0078177013D0481FC0483E0076D07AC16D560808080F0"; -_T Other_ID_Start = x"98850288910115018F6C02"; -_T Pattern_Syntax = x"210F0A071A0401011A042207010101020101010204010401030117011F019F1818080F0213010A813182D080A082761E846C8200808081810304190F01A0CD0D02810502"; -_T Lu = x"411A6517010721010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010201010101010101010101010101010102010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010201010101030201010102010302040102010303020102010101010102010102010102010301010102030107010201020102010101010101010101010101010101020101010101010101010101010101010101020102010103010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010702010202010104010101010101010181210101010301080106010103010101020111010923010203030101010101010101010101010101010101010101010101050102010102023330010101010101010101010101010101010101010101010101010101010101010101090101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010201010101010101010101010102010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010102268B49260101050182D256889A2B0203814001010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010901010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101090808060A08080808060B01010101010101080848040C040C040C050B0481060104010303020302010305060101010101010402040A0205013D018A7C303001010302010101010101040101020108030101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101080101010401A0794D0101010101010101010101010101010101010101010101010101010101010101010101010101010101010101011301010101010101010101010101010101010101010101010101010180870101010101010101010101010103010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010A0101010102010101010101010104010101020101010301010101010101010101010101010101010101010501050101010101010101010101010101010401010601050101011C01A0572B1A84C528808824809C0B010F0107010286EA338BED20A0558020A065A01A1A1A1A1A1A01010202010202020401081A1A1A020104020801071B0201040105010103071B1A1A1A1A1A1A1A1A1A1A1A1E1921192119211921192101913522"; -_T Case_Ignorable = x"270106010B01230101014701040101010401020281F780C0040204010902010180FB0780CF010501312D01010102010201012C010B060A0B010123010A1510016508010A0104210101011E1B5B0B3A0B0401020118182B032C0107020608293A370101010408040103070A020D010F013A010404080114021A010202390104020402020303011E0203010B0239010405010204011402160601013A0102010104080107020B021E013D010C0132010301370101030503010407020B021D013A0102010601050214021C0239020404080114021D014801070301015A0102070B09620102090901010749021B0101010101370E01050102050B0124090166040106010202021902040310040D01020206010F015E0182600383B2031D021E021E02400201070801020B030105012D05330141022201760304020901060380DB0202013A010107010101010208060A02012701081F3104300101050101050128090C0220040202010338010102030101033A08020240065203010D0107040106010302323F0D01226581BD0101030B030D030D030D020C0508020A01020102053105010A01010D01100D33218B8B0271037D010F0160202F0181D5012404030505015D065D03A06F160184E206810E016204010A01011C0450020E224E01170367030302080103010401190205018097021A120D012608190B2E03300102040202110115024206020202020C01080123010B01330101030202050201011B010E020502010164050903790102010401A04F3001809311823D1003010C102201020180A901070106010B01230101012F012D024301150382010180E201809505840506012A0109824603010205042803040180A502823D048183025003460B31047B01360F290102020A0331040202020104010A013203240501083E010C0234090A0402015F030201010206010201809D0103081502390203012507030580C308020301011701540601010402010280EE04060201021B025508020101026A0101010206010165030204010581030901028100020101040180900402020401200A280602040801090602032E0D01028196070106010152160207010201027A060301010201070101480203010101815B020B0234050501010194ED11060FA0369A053B070904840B013F1140020102A0400B04010701028C9E020104925C2E0217822003091002071E0480940387BB37043208010E011605010F8550070111020701020105053E210180A00E8170013D0481FB0583E0076D088AAF05AC0C01011E60808080F0"; -_T Lo = x"80AA010F01810001040480D001833B1B04042D20010A23020163010118020A0302011001011E1D590B01182115162A19070B0518010611293B3603011201070A100F040802020216010701010304030110010D0201030E020A0108060402021601070102010201021F0401011303100901030116010701020105030112010F0217010B080202021601070102010503011E0201030F01110101060303010403020101010203020303030C1601340801030117011003011A03020102021E01040801030117010A010503011F0201020F0211090103012902011001050308031806051203180109010102073A3001020C063B020101010501180101010A010209010205170420013F0801241B05732B140110060404030103020703040D0C0171814901040207010101040229010402210104020701010104020F013901040243251071826C0211011A054B060807120D130E120E0D01030F3428014323013507050222010105460A1F311E02050B2C041A3617093580B02F1108361E0D020A2C1A2429030A1E7104010601020301843A048BF738181709070107010701070107010701070107822701350104560801015A0401052B015E11203010820099C040A05215018477432808810C04100A024201314680A901670103070103010401171D340E323E06030101020B1C0A17191D072F2D0502090A050129170301081410010603010332010103020205020101011802030B07010E0602060206090701078091231DA02BA40C170431A02104816E026A4301010A010D0105010101020102016C21816B12400236280C7405018087690A012D021F0306020602060203230C011A01130102010F020E227B81851D03312F200D14010806260A1E0224040880804E62280834809C813709160A088098060201012C0102030102170A17091F411301020A160A1A4638060240010F040103011D2A1D031D2308011C1B360A160A130D126E4980B724815C2A06024E1D0A0108162A122E151B170C35390202010D2D20191A241D010201082303010C300E04150101012312011913023F0701010104010F010A072F260802020216010701020105030112010C05809E35120414031E301402010180B82F2904243014013B2B0D01471B250780B92C80D30802010208010201180F0101015E080227100101011C010A28070115010B2E1301124981070901251101311E70070102012615011906010201200E018147130F01010D01227C014F839A80E680C48A4C610F843011068FB98247A021B98239071F114F111E12303315051383704B050180AF97F80884D62A09A022F781230F011D0302010E0408818C89046B050D0309070AA022700181F52D210181411E122C81E41B82F50701040102010F0180C5853B04011B010201010201010A0104010101010601040101010101010301020101020101010101010101010102010102040107010401040101010A01110503010501119144A0A6E020903A0680DE0296820E9D310F826E89A2821E85E2934B059060"; -_T Pc = x"5F019FDF021301A0DDDE02180380EF01"; -_T Bidi_Control = x"861C0199F1021A053704"; -_T Co = x"A0E0009900AE0700A0FFFE02A0FFFE"; -_T Variation_Selector = x"980B030101A0E5F010AD02F080F0"; -_T Soft_Dotted = x"690280C4018119011E01340114018140016201010199090133010D010301808401809D0181A50180D6028B3201A1A7A5023202320232023202320232023202320232023202320232028886018131021A01"; -_T Noncharacter_Code_Point = x"A0FDD020820E02A0FFFE02A0FFFE02A0FFFE02A0FFFE02A0FFFE02A0FFFE02A0FFFE02A0FFFE02A0FFFE02A0FFFE02A0FFFE02A0FFFE02A0FFFE02A0FFFE02A0FFFE02A0FFFE"; -_T Logical_Order_Exception = x"8E40057B058AF0030201A090FA0202010102"; -_T Cased = x"411A061A2F010A0104010517011F0180C301040480D0012407021E0560012A040202020401010601010301010114015301808B0880A6012609298B172601010501022B010482A0560206888209072B02034080C04081160206022602060208010101010101011F0235010701010303010703040206040D0503010774010D01100D65010401020A0101030506010101010101040106040102040505040111200302833134871680E5060403020C2601010501A079122E121E8084660304013B0502010101051805010383352B010E0650A04F40070C0584091A061A84A55060240424740B010F01070102010B010F0107010281C3010203012A010984C5330D338BAD40A0556040A065805501470102020102020204010C010101070141010402080107011C010401050101030701815402190119011F0119011F0119011F0119011F0119010887340A0114060681053E88924487EC1A061A061A"; -_T ID_Compat_Math_Continue = x"80B20205019FB601030B010F81730104011601A1B4A20119011F0119011F0119011F0119011F011901"; -_T Math = x"2B0110031F011D0101012D01040125011F0182D80302011A020203820F039A0D011B030B0103010D010E0415050B05410D04010302040512010401020A0101020606010302020201030106030E010144180106010204020420010106020E810C080414025A011E1B010118010B0781BD020C0A04060402020203050E01010102060B080502390101011D0409038150408100820030150206A0CFDC01833706010180A20110031D0101011D0101018083010604A0D4135501470102020102020204010C010101070141010402080107011C01040105010103070181540281240232960004011B010201010201010A0104010101010601040101010101010301020101020101010101010101010102010102040107010401040101010A01110503010501113402"; -_T No = x"80B20205010203893506817806780380850780D907110981B10A84351484730A81E0018695010306060A80C610290182D63C4E1682761E856901849404808A0A1E08010F200A270FA0757006A058D12D4104110281551B2404853408190727094B05160680A0020210022E400934021E034B05680818082907814A0681601F809E0A2A047007808614817B1485450281AE09836713835315A04B8607831E17A06429140C146C19954E0983A13B010301044C2D010F83C20D"; -_T ID_Continue = x"300A071A0401011A2F010A01010102010517011F0181CA040C0E05070101011175010202040101060501010114015301808B01050280A6012602010629082D0101010201020101081B04041D0B054A04660108020A01130201103B02650E3604010201022E121C040B05180106094A018081020A0113010802020216010701010304020902020204080104020105020C0A01010102030106040202160107010201020102020101050402020303010704010107100B03010901030116010701020105020A0103010302010F04020A09070103010802020216010701020105020902020203070304020105020A0101100201060303010403020101010203020303030C040503030104020106010E0A100D0103011701100209010301040702010302010204020A1004010801030117010A0105020901030104070206020104020A01030C0D010301330103010505040705020A0A060103011203180109010102070301040601010108060A02020D3A050F010A270201010105011801010117020501010107010A020420011702060A0B0101010101040A01240414011201240901394A064E022601010501022B01814D01040207010101040229010402210104020701010104020F013901040243020309090E101056020603826C0211011A054B030B071609160B140C0D010301020C5403010402020A2103010B0659072B05460A1F010C040C0A2802050B2C041A060B251C043F011D020B060A0D01080E0110314D030A11090C740C38080A03310209072B0203100301270582160206022602060208010101010101011F0235010701010303010703040206040D050301070F02310213011C010D01100D330D0401030C11010401020A01010206060101010101011002040505040111298A7780E506090C2601010501023807010F18090701070107010701070107010701070120820503190F0105020504560207015F052B015E11203010820099C040A0568D432E02810D031C1430040A01732509026702400502010101051836040113340C460A0A06180301013102240C1D03410E0B061F0137090E020A061703491803021002050A060206020609070107012B010E067B0102020A06A02BA40C170431A02104816E026A26070C05050C010D0105010101020102016C21816B12400236280C04101010030218032005018087130A071A0401011A0A5A0306020602060203230C011A01130102010F020E227B453580880180821D03310F011F200D1E052B051E0224040801052A809E020A06240424042808340C0B010F01070102010B010F0107010243813709160A081806012A010945060201012C0102030102170A17091F411301020A160A1A463806024004010205080103011D02030401201D031D2308011E19360A160A130D126E4937330D330D28080A81462A010203024B200A0108211F162A151B1709471F10093C07010D19070A0635010A04040824020109450404010D01012312012506043E0701010104010F010A073B050A0604010802020216010701020105010A0202020302010601050702070305808B4B050A04041E460101080A80A63602091706224103010B0A2639070A361B020F040A060780B93B654A1508020102080102011E010202090C0A4608022E020801021B3F0801084A03011249810709012D01090F0A181E0216010E49070102012C030101020109080A06060102012501020106070A8136170911012903050D0A56014F839A666F1180C48A4C610F843010168FAA8247A021B98239071F010A064F010A061E02050B3709040C0A0915051382B04080804B04390711400201020B020E97F80884D62A09A022E704010701020181230F011D0302010E0408818C89046B050D0309070A030292612E0217821E050306080802071E0480940381BB5501470102020102020204010C010101070141010402080107011C010401050101030701815402190119011F0119011F0119011F0119011F011901080232820037043208010E011605010F84501F060680D5070111020701020105053E2101702D030E020A040181411F113A81D62A82E60701040102010F0180C50B07294C040A84A604011B010201010201010A0104010101010601040101010101010301020101020101010101010101010102010102040107010401040101010A01110503010501118D340A8406A0A6E020903A0680DE0296820E9D310F826E89A2821E85E2934B059060AADD5080F0"; -_T Me = x"848802963401861E040103A0858B03"; -_T Ideographic = x"A030060219090E0383C599C040A05200A05900816E026AA0750A011B97F80884D62A09A02467818CA04D04A0A6E020903A0680DE0296820E9D310F826E89A2821E85E2934B059060"; -_T Cf = x"80AD01855206160180C001310181800250018F2B0187FC051A053105010AA0DE8F0180F90390C1010F01A0236210A088600494CF08AC2E86011E60"; -_T So = x"80A60102010401010183D101810A027F0280CE010A01130280F701820301817501808206010180840180CF0129018187030F010103020614010101010180850801060102050480C50282F00A82D30182D201809D2281610A0909858302010401020A010102060601010101010104010B020E01010201013A0209050204010201020107011F02020101011F810C08041402070251011E19280645190B514E1680B701090136086F0180F82C2C408100820030150206270220016980E5068165022E1A01590C80D61A1004010D020C0115020602815002040A20240B01101F0B1E08010F200A270F814099C040A05690378361040A020101823D03A052C6107F012D0381E401030104020D0281390939110203010D03012F2D867A02824F018C76018895080411A04B4A040501A051560192B3743C80F60A27023C05031602071E043D1542030180BA5784A9820037043208010E010286C8018B5C0180810182D12C04640C0F020F010F01251780A1381D0D2C040907020E06809A80FB0582D80411030D0377045F060C04010F0C0438080A0628081E02024E81540C0E020D0309072E0107080E040907090780930137"; -_T White_Space = x"0905120164011A0195DF01897F0B1D0205012F018FA001"; -_T Sentence_Terminal = x"21010C01100185490180930380B4012B0380F6013D010101030281250286E402831601040283050180C602809D022D010501813A0281620480AE0202021D0280BC02410283BC0209038DE4010D01160281AD01A074FC01810E0280E3010301817E0256025F0180980280930380900280F901A0526601030280A9010C01100141018AF40284FD052C0480BD0275047F0380810206011002580201026C0181A102817502050F690280F903820501010180FB02570281A40282B4024A02A04B290280850141020B01835301A04E06019DE801"; -_T Lt = x"81C5010201020126019D9508080808080C010F012F01"; -_T Other_Lowercase = x"80AA010F0181F50907021E05600134018D81018C2F3F0D01222582B1010D01100D80D31083501A879202A07A1E0280D20180810303028362040901A05C16010203012A0109A0D8753E"; -_T Other_Alphabetic = x"834501826A0E0101010201020101480B300D01071001650704040202040123011E10660B650203090103010480A70C030706143602020F010205030A021D033A07020202020A010A021D033A050402020204011E0203010B033A08010301021502160304033A070202020209020A021E013B05030301030A01280539070103010308020B021D033A070103010308020B020F010C043A07010301030A010A021D034B060101010812023D0102071201630102060102100180A313090B01246E0C01010204170404030103020703040D0C01010A048674021E021E021E02421380BC022201760C040980DE05390A02144A020B033105300F3C031E090202390B321381B00E86C13488F620A0787408220281620108011705580232100101390126051C0C2D04300C2501430E0C0108022D0332010103020205012C05050180ED08A04F330188570586860301020504831404818302815303350E2D020B032D0909013D03240C12023903300D0E025C09020106010201809D0A17043A07020202020A010A0280D10D01036A1280ED0702071D02520F01016A0B670E81010D80F7060102020203010101808E07020604011C0A2A050104120B2E0E81970801075316010E7A0603010102010301010301420501020104815C040902010130070303A0500E01013707045D02A04CAC01A02361070111020701020105640188B70187E81A061A061A"; -_T XID_Continue = x"300A071A0401011A2F010A01010102010517011F0181CA040C0E05070101011175010203030101060501010114015301808B01050280A6012602010629082D0101010201020101081B04041D0B054A04660108020A01130201103B02650E3604010201022E121C040B05180106094A018081020A0113010802020216010701010304020902020204080104020105020C0A01010102030106040202160107010201020102020101050402020303010704010107100B03010901030116010701020105020A0103010302010F04020A09070103010802020216010701020105020902020203070304020105020A0101100201060303010403020101010203020303030C040503030104020106010E0A100D0103011701100209010301040702010302010204020A1004010801030117010A0105020901030104070206020104020A01030C0D010301330103010505040705020A0A060103011203180109010102070301040601010108060A02020D3A050F010A270201010105011801010117020501010107010A020420011702060A0B0101010101040A01240414011201240901394A064E022601010501022B01814D01040207010101040229010402210104020701010104020F013901040243020309090E101056020603826C0211011A054B030B071609160B140C0D010301020C5403010402020A2103010B0659072B05460A1F010C040C0A2802050B2C041A060B251C043F011D020B060A0D01080E0110314D030A11090C740C38080A03310209072B0203100301270582160206022602060208010101010101011F0235010701010303010703040206040D050301070F02310213011C010D01100D330D0401030C11010401020A01010206060101010101011002040505040111298A7780E506090C2601010501023807010F18090701070107010701070107010701070120820503190F01050205045602020203015F052B015E11203010820099C040A0568D432E02810D031C1430040A01732509026702400502010101051836040113340C460A0A06180301013102240C1D03410E0B061F0137090E020A061703491803021002050A060206020609070107012B010E067B0102020A06A02BA40C170431A02104816E026A26070C05050C010D0105010101020102016C21808B0680DA12400236280A0610101003021803210101010301010101010101017E130A071A0401011A0A5A0306020602060203230C011A01130102010F020E227B453580880180821D03310F011F200D1E052B051E0224040801052A809E020A06240424042808340C0B010F01070102010B010F0107010243813709160A081806012A010945060201012C0102030102170A17091F411301020A160A1A463806024004010205080103011D02030401201D031D2308011E19360A160A130D126E4937330D330D28080A81462A010203024B200A0108211F162A151B1709471F10093C07010D19070A0635010A04040824020109450404010D01012312012506043E0701010104010F010A073B050A0604010802020216010701020105010A0202020302010601050702070305808B4B050A04041E460101080A80A63602091706224103010B0A2639070A361B020F040A060780B93B654A1508020102080102011E010202090C0A4608022E020801021B3F0801084A03011249810709012D01090F0A181E0216010E49070102012C030101020109080A06060102012501020106070A8136170911012903050D0A56014F839A666F1180C48A4C610F843010168FAA8247A021B98239071F010A064F010A061E02050B3709040C0A0915051382B04080804B04390711400201020B020E97F80884D62A09A022E704010701020181230F011D0302010E0408818C89046B050D0309070A030292612E0217821E050306080802071E0480940381BB5501470102020102020204010C010101070141010402080107011C010401050101030701815402190119011F0119011F0119011F0119011F011901080232820037043208010E011605010F84501F060680D5070111020701020105053E2101702D030E020A040181411F113A81D62A82E60701040102010F0180C50B07294C040A84A604011B010201010201010A0104010101010601040101010101010301020101020101010101010101010102010102040107010401040101010A01110503010501118D340A8406A0A6E020903A0680DE0296820E9D310F826E89A2821E85E2934B059060AADD5080F0"; -_T Cc = x"00205F21"; -_T Regional_Indicator = x"A1F1E61A"; -_T Sc = x"24017D0484E9017B0181F20281F202070180F501810701824501899B0188C421A0877701A055C3016C01809A0180DB0203029FF604A0C31E0189B001"; -_T IDS_Unary_Operator = x"A02FFE02"; -_T Pf = x"80BB019F5D0103011C018DC8010101040102010F010301"; -_T Pe = x"290133011F018EBD010101875E0189A90137010F01827A0101011E01843E0101010101010101010101010150012001010101010101010181940101010101010101010101010101010101010101014001010121018425010101010101012C0101010101010181AC01010101010101010103010101010101010202A0CD1E0180D9011D010101010101010101010101010101030111010101010180AA0133011F0102010201"; -_T Other_Uppercase = x"A021601083461AA1CC601A061A061A"; -_T Cn = x"8378020604070101011401818D0126023202030137081B04060B810E013C02650E3B0231020F011C0201010B051F01020680EC010802020216010701010304020902020204080104020105021902030106040202160107010201020102020101050402020303010704010107110A03010901030116010701020105020A0103010302010F04020C0707010301080202021601070102010502090202020307030402010502120A0201060303010403020101010203020303030C040503030104020106010E15050D0103011701100209010301040702010302010204020A071601030117010A0105020901030104070206020104020A01030C0D01030133010301060410021A0103011203180109010102070301040601010108060A02030C3A041D250201010105011801010117020501010107010A02042048012404270124010F010D2580C60101050102817901040207010101040229010402210104020701010104020F0139010402430220031A0656020602829D03590716091809140C0D010301020C5E020A060A061A0659072B05460A1F010C040C0401032A02050B2C041A060B033E0241011D020B060A060E021F314D032F0174083C030F033C072B020B082B0582160206022602060208010101010101011F0235010F010E02060113020301090165010C021B010D03210F210F808C048297190B158714022001815D052D01010501023807020E1809070107010701070107010701070107017E221A01590C80D61A5001560267052B015E01540B3001A0726D033709815C1480B80880CB050201010105183B030A06380846080C06740B1E034E010B04210137090E020A0267181C0A060206020609070107013C047E020A06A02BA40C17043104A0226E026A26070C05051A0105010101020102017D1081BD02360701202A063301130104040501808702010180BE0306020602060203030701070A05020C011A01130102010F020E227B0503042D0358010D03012F2E80821D03310F1C0424091E052B051E0125040E2A809E020A06240424042808340B0C010F01070102010B010F0107010243813709160A081806012A010945060201012C01020301021701480809301301020521031B0501403804140232010205080103011D0203040A070907402027040C0936031D021B051A07040C07504937330D33072E080A81261F012A010302024B2B082A161A261C1417094E042409440A010219070A063501120827096001140B12012F3E0701010104010F010B063B050A0604010802020216010701020105010A0202020302010601050702070305808B5C01051E48080A80A636022622450B0A060D133A060A361B020F041780B93C64530C08020102080102011E0102020C090A4608022E020B1B4808530D49070A80F609012D010E0A1D03200216010E49070102012C030101020109080A06060102012501020106070A81361907110129031C56010F320D839B666F01050B80C48A4C630D84568FAA8247A021B98239071F010A0451010A061E02060A460A0A01070115051382B05B654B0439071140050B020E97F80884D62A09A022E704010701020181230F011D0302010E0408818C89046B050D0309070A0208925C2E021709743C80F60A270280C215467A140C140C57091980875501470102020102020204010C010101070141010402080107011C01040105010103070181540281240282BE0F05010F84501F060680D5070111020701020105053E2101702D030E020A040281401F113A050181D02A82E60701040102010F0180C50210294C040A04028311444C3D80C204011B010201010201010A0104010101010601040101010101010301020101020101010101010101010102010102040107010401040101010A01110503010501113402810E2C04640C0F020F010F01250A80AE381D0D2C040907020E06809A83D80411030D0377045F060C04010F0C0438080A0628081E02024E81540C0E020D0309072E0107080E040907090780930137250A8406A0A6E020903A0680DE0296820E9D310F826E89A2821E85E2934B059060AADC51011E60808080F0A0FE10A0FFFE02A0FFFE"; -_T Hyphen = x"2D017F0184DC01927B018809028E050182E301A0CD670180A9015701"; -_T Other_Math = x"5E0183710302011A0202029C20011B030B01200418020E02410D04010302040512010401020A0101030506010302020201030106030405054B05020401020102010101050202040204120202010101070101060281220480A80201011801110181BD020C0905050502020203050E01010102061802390101011D0409028156021F0A8193163F042002A0D463010101040180D3010101A0D4C15501470102020102020204010C010101070141010402080107011C010401050101030701815402190119011F0119011F0119011F0119011F011901080232960004011B010201010201010A0104010101010601040101010101010301020101020101010101010101010102010102040107010401040101010A0111050301050111"; -_T Ps = x"280132011F018EBE010101875E01897E010301260137010F01827A0101011E01843E0101010101010101010101010150012001010101010101010181940101010101010101010101010101010101010101014001010121018425010101010101011901120101010101010181AC01010101010101010103010101010101010201A0CD210180D7011D010101010101010101010101010101030111010101010180AA0132011F0103010201"; -_T Zs = x"20017F0195DF01897F0B24012F018FA001"; -_T Alphabetic = x"411A061A2F010A0104010517011F0181CA040C0E050701010156012A050102020401010601010301010114015301808B0880A6012602010629270E0101010201020101081B04041D0B053801070E660108040804030A03020110300D65182109020401051802131319070B05180106112A0A0C0307064C01100103040F0D1301080202021601070101030403080202020201010801040201050C020A010403010604020216010701020102010204050402020204010704010111060B0301090103011601070102010503090103010203010F04150404030108020202160107010201050308020202020902040201050D01100201060303010403020101010203020303030C04050303010303010601280D01030117011003080103010308020103020102041C04010801030117010A01050308010301030802060201040D030C0D01030129020801030103010105040705160601030112031801090101020708060101010812020D3A0507060133020101010501180101011301030205010106010E0420013F08012404130410012443370101020510400A04022601010501022B01814D01040207010101040229010402210104020701010104020F01390104024325101056020603826C0211011A054B030B07140B150C140C0D010301020C3402130E0104014359072B05460A1F010C0409171E02050B2C041A361C043F0214320117020B033134010F0108332A02040A2C010B0E3716030A240209072B020329040106010203010580C0270E0B81160206022602060208010101010101011F0235010701010303010703040206040D0503010774010D01100D65010401020A010103050601010101010104010B0204050504011129832D34871680E5060403020C26010105010238070110170907010701070107010701070107010701202F0181D50319090705020504560603015A0104052B015E11203010820099C040A0568D432E02810D03100A02142F050803712709026702400502010101051814012118340C4401012C06030101030A2105230D1D0333010C0F0110100A050137090E12170345010101011803021002040B060206020609070107012B010E067B15A02BA40C170431A02104816E026A26070C05050C010D0105010101020102016C21816B12400236280C7405018087241A061A0B590306020602060203230C011A01130102010F020E227B4535810B1D03312F200D1E052B051E0224040801052A809E12240424042808340C0B010F01070102010B010F0107010243813709160A081806012A010945060201012C0102030102170A17091F411301020A160A1A463806024004010205080103011D2A1D031D2308011C1B360A160A130D126E4937330D330D2881582A010203024E1D0A0108162A122E151B1709462B050A3909010D1917331104082303010940010409020A01010123120122020106043E0701010104010F010A07391704010802020216010701020105030802020202030106010507809C420103010414031E420202010180B83602071906223F010103013B360201471B020E150780B93967401F08020102080102011E0102020202045D08022E0206010101021B33020A114805011249810709012D01070101311E0216010E49070102012C030101020103010102021806010201250102010401018147170911012903036F014F839A666F1180C48A4C610F843011068FB98247A021B98239071F114F111E123010041F15051382B04080804B04390711400201010C020E97F80884D62A09A022E704010701020181230F011D0302010E0408818C89046B050D0309070A040197615501470102020102020204010C010101070141010402080107011C010401050101030701815402190119011F0119011F0119011F0119011F0119010887341F060680D5070111020701020105053E2101702D0A07100181411E122C81E41C82F40701040102010F0180C53B440301030184B404011B010201010201010A0104010101010601040101010101010301020101020101010101010101010102010102040107010401040101010A011105030105011182741A061A061A8E76A0A6E020903A0680DE0296820E9D310F826E89A2821E85E2934B059060"; -_T XID_Start = x"411A061A2F010A0104010517011F0181CA040C0E05070101018081050102030301010601010301010114015301808B0880A6012602010629471B04042D2B2302016301010F0207020A0302011001011E1D590B0118210902040105160401090103011719070B05180106112A3A3603011201070A0F10040802020216010701010304030110010D0201030E020A0108060402021601070102010201021F0401011303100901030116010701020105030112010F0217010B080202021601070102010503011E0201030F01110101060303010403020101010203020303030C1601340801030117011003011A03020102021E01040801030117010A010503011F0201020F0211090103012902011001050308031806051203180109010102073A3001010D073A020101010501180101010A01010A0102050101150420013F0801241B05732B140110060404030103020703040D0C01112601010501022B01814D01040207010101040229010402210104020701010104020F01390104024325101056020603826C0211011A054B030B07120D130E120E0D01030F342301040143590729010105460A1F311E02050B2C041A3617093552015D2F1108361E0D020A2C1A2429030A240209072B020329040106010203010580C04081160206022602060208010101010101011F0235010701010303010703040206040D0503010774010D01100D65010401020A01010206060101010101011002040505040111298A7780E5060403020C26010105010238070110170907010701070107010701070107010782260319090705020504560603015A0104052B015E11203010820099C040A0568D432E02810D03100A02142F101F025027090267024005020101010518100103010401171D340E323E06030101020B1C0A17191D072F1C011005010A0A05012917030108141703010332010103020205020101011803020B07030C060206020609070107012B010E06731DA02BA40C170431A02104816E026A26070C050501010A010D0105010101020102016C21808B0680DA12400236280A770101010301010101010101017E241A061A0B38021F0306020602060203230C011A01130102010F020E227B4535810B1D03312F200D1E05260A1E0224040801052A809E12240424042808340C0B010F01070102010B010F0107010243813709160A081806012A010945060201012C0102030102170A17091F411301020A160A1A4638060240010F040103011D2A1D031D2308011C1B360A160A130D126E4937330D330D24815C2A06024E1D0A0108162A122E151B170C35390202010D2D20191A241D010201082303010C300E04150101012312011913023F0701010104010F010A072F260802020216010701020105030112010C05809E35120414031E301402010180B82F2904243014013B2B0D01471B250780B92C74401F0802010208010201180F0101015E080227100101011C010A28070115010B2E1301124981070901251101311E70070102012615011906010201200E018147130F01010D01227C014F839A666F1180C48A4C610F843011068FB98247A021B98239071F114F111E123010041F15051382B04080804B0501420D400201011C97F80884D62A09A022E704010701020181230F011D0302010E0408818C89046B050D0309070A97665501470102020102020204010C010101070141010402080107011C010401050101030701815402190119011F0119011F0119011F0119011F0119010887341F060681053E80922D0A07100181411E122C81E41C82F40701040102010F0180C53B44070184B404011B010201010201010A0104010101010601040101010101010301020101020101010101010101010102010102040107010401040101010A01110503010501119144A0A6E020903A0680DE0296820E9D310F826E89A2821E85E2934B059060"; -_T Grapheme_Link = x"894D017F017F017F017F017F017F017F016D0210017C016F017F0180C90180B40286D9021E01809D01828D0180E30165024602918B01A07A86012501809701808E016C0181350180F601A05E510186060129010E0139017902808B01740180B401620180F4017F0180FC017F0176017401810D0181030280A10153011201510181A501810402510181A902"; -_T Dash = x"2D01855C0133018E41018405018809063D0127010F018186018C040102011F0204011C0181BE0113016F01A0CD900225010A0180A9018F9F01"; -_T Nl = x"96EE038A6F2302048E7E0119090E03A076AB0AA05A503581CC010801808605A0202A6F"; -_T IDS_Trinary_Operator = x"A02FF202"; -_T Terminal_Punctuation = x"21010A0101010B020301833E010801820101390148010E01010380B4012B0B010180EB02360F1F0181050284F40280AC0104068137028315088305017C034702809D03030127040202813A0281620480AE0201031D0280BC053E0283BC0209038DE4010D0104010A010102030281AC02A074FB02810D0380E305817E0256025F018097038093037F01100280F901A0526403010480A9010A0101010B02030141010201843A01300184860180C7018136028098064406590483B8052C0480BD0770047F038081020601100258056C0181A1030C02816604030F690280F903820501010180FB0257020402819E032D018285024A02852B05A045F90280850141030A01835202A04E06019DE704"; -_T Nd = x"300A86260A80860A80C60A819C0A760A760A760A760A760A760A760A760A760A600A760A460A81160A460A87460A260A812C0A80800A80A60A060A80B60A560A80860A060AA089C60A82A60A260A80C60A160A560A81960AA053160A85860A88860A832C0A80800A3C0A80900A81160A81560A760A81760A660A660A81A60A660A82F60A80F60A460A81A60AA04B060A560A80860AA06C743289400A81A60A81F60A84560A92960A"; -_T ID_Compat_Math_Start = x"A022020104011601A1B4A20119011F0119011F0119011F0119011F011901"; -_T Prepended_Concatenation_Mark = x"86000680D70131018180025001A107DA010F01"; -_T ASCII_Hex_Digit = x"300A07061A06"; -_T Mc = x"890301370102030804010232023A03060202020A012B013A0342013A030801010235023A010101060202020A0166020102030301030A0129033D043D023A0101050202010209021C010E023A03050301030A012A024B0306081202814A023F0180AB0204010601020219020A0302071502020602010A038678011E0180810107080102815A0402030402010680E0023A01010109010102080680910130010501010501023D011E01040202013C010203010103023008080280AB011501933602A077F302020158023210808E022F013002040202036E02020218012D0101016D010202050180ED02010201020101A064130101017F012D030402730118023B01300309020D015D030302010180AA031F023A0201040202020309010A0280D103080203016A0306010104020180ED03060402017103080201016D010102060169020401810503090180F7060102040102010101808E030804040154011D023E018197010E016A010701020180D50504020101815E020C01300208020101A0500F376802A06173020606"; -_T Radical = x"A02E801A01590C80D6"; -_T Quotation_Mark = x"220104018083010F019F5C0819028E070181C9040D03A0CE210480BD0104015A02"; -_T Other_Default_Ignorable_Code_Point = x"834F018E0F0286530288AF0190FE01A0CE3B014F09AD000701011E60808080F08E10"; -_T Join_Control = x"A0200C02"; -_T Grapheme_Base = x"205F210D018252700802060407010101140180E00780A60126023202032E01010102010201091B0406110A0B01012E1510016508010602020104200201011E1D590B010E2B09070318040109010301070F01190501010B051F112A39370101010408040103070A021D0102010802020216010701010304030101020602020201010D020103041805010106040202160107010201020102040318040101070A020301010C0101090103011601070102010503040801010203010F02040C0701080201080202021601070102010503010201060202020F02010304120B0101060303010403020101010203020303030C0501010203030103030115150603010801030117011003010304130302010202040A070A010B01030117010A01050302010201020202010211020102040A01030E0B01030129020101020503010301020403010A041A02020112031801090101020709020607070A02030C3001020B08080D25020101010501180101010A0102090102050101090A02042018021B01010101010E012412010501020531080106010D252D040106010202021902040310040D01020206010F01280101050102817901040207010101040229010402210104020701010104020F013901040243051D031A0656020602829D0359071203010913020309120E0D01030F340201070801020B09030A060A060B050A065907050222010105460A1F04040203040201060701032A02050B2C041A060B03390202033801010901010208060D0A060A060E563007010105010A031B090B0320040202010338010102030101030208300802050F033C072B020B0B010D0107040106010302010580C04081160206022602060208010101010101011F0235010F010E0206011302030109010B051807311002021B010D03213F808C048297190B15871402200181580302052D01010501023807020F1709070107010701070107010701070107215E221A01590C80D61A3A061001560465052B015E01540B3001A0726D033709815C142F04010A20025002060880CB05020101010518100103010401190205040A063808440A0C180D012608190B020B1E0630010204020210010B040701190129060202020B0301080101020A0220013301010302020502010101181102080B060206020609070107013C047501020104030A06A02BA40C170431A02104816E026A26070C05050101180105010101020102017D1081BD023607012010100A162301130104040501808704809D021F0306020602060203030701070D02020C011A01130102010F020E227B0503042D0358010D03012F2D80831D0331101B0424091E05260A1E0125040E2A809E020A06240424042808340B0C010F01070102010B010F0107010243813709160A081806012A010945060201012C01020301021701480809301301020521031B050140380414022F0F040103011D0A09070907402025060C0936031D021B051A07040C07504937330D33072A0C0A81261F012A030102024E2808160B0916120404261C1417090101360F07041E010202010C310402020201040E19070A092405010912082301030B34090A0402011001140B12011C03020101020601023F0701010104010F010B062F01030D0A08020108020202160107010201050301010101040202020302010C07809C38080203010115010101031E30010206010102010102010204080A80A62F010206040201021B24330802010102040B0A060D132B0101010206010102060A361B05020401091780B92F0901020164530C08020102080102011801050102040101040103090A4608022A080401041B010A280602040809010602032E0D0102090D49070A80F60901260E0101060A1D03201901070102014B07010201261501090A060601020125040201010101070A8136130204090F012408020101011756010F320D839B666F01050B80C48A4C630D843011068FB98247A021B98239071F010A0451010A061E07010A30070F0A0A01070115051382B05B654B05380B0D40040C020E97F80884D62A09A022E704010701020181230F011D0302010E0408818C89046B050D0309070A0201020192B0743C80F60A27023C010103041502071E043D154203017A140C140C57091980875501470102020102020204010C010101070141010402080107011C010401050101030701815402812402823237043208010E010784741F060681053E80922D0A07020A040281401E122C040A050181D01C040A82E60701040102010F0180C5020930440701040A04028311444C3D80C204011B010201010201010A0104010101010601040101010101010301020101020101010101010101010102010102040107010401040101010A01110503010501113402810E2C04640C0F020F010F01250A80AE381D0D2C040907020E06809A83D80411030D0377045F060C04010F0C0438080A0628081E02024E81540C0E020D0309072E0107080E040907090780930137250A8406A0A6E020903A0680DE0296820E9D310F826E89A2821E85E2934B059060"; -_T Uppercase = x"411A6517010721010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010201010101010101010101010101010102010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010201010101030201010102010302040102010303020102010101010102010102010102010301010102030107010201020102010101010101010101010101010101020101010101010101010101010101010101020102010103010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010702010202010104010101010101010181210101010301080106010103010101020111010923010203030101010101010101010101010101010101010101010101050102010102023330010101010101010101010101010101010101010101010101010101010101010101090101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010201010101010101010101010102010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010102268B49260101050182D256889A2B0203814001010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010901010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101090808060A08080808060B01010101010101080848040C040C040C050B0481060104010303020302010305060101010101010402040A0205011A10130183321A8730303001010302010101010101040101020108030101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101080101010401A0794D0101010101010101010101010101010101010101010101010101010101010101010101010101010101010101011301010101010101010101010101010101010101010101010101010180870101010101010101010101010103010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010A0101010102010101010101010104010101020101010301010101010101010101010101010101010101010501050101010101010101010101010101010401010601050101011C01A0572B1A84C528808824809C0B010F0107010286EA338BED20A0558020A065A01A1A1A1A1A1A01010202010202020401081A1A1A020104020801071B0201040105010103071B1A1A1A1A1A1A1A1A1A1A1A1E1921192119211921192101913522880E1A061A061A"; -_T Zl = x"A0202801"; -_T Hex_Digit = x"300A07061A06A0FEA90A07061A06"; -_T Zp = x"A0202901"; -_T Pattern_White_Space = x"0905120164019F88021802"; -_T Diacritic = x"5E010101470106010401020281F7809F0108050611020401090280FD0580D1013711011B010101020101808608040280860204020303431B5B0B3A0B22027E08290A101C3D01100103041C014A0110016E0110016E0110012F033C011001070177016E0110016E0110016D0210017C017C0601016B010D054B021B01010101010402420301023E017001010228020405190701010A0282C10383B40280B30B0901815B038139080201300F020B68010F0126093602808A0240065219040106010203323F590C250B81BD0101030B030D030D030D028CF003813D0181FA0669045F01A07572010C0201011C0252020E2266036D0280CA011B12390424015F010C0124018095034104330164050903808002A04F3001830110810E0101012F012D02430182FC01849F06012A0109832A02823B0681D503460B310480C0012901480278023E014C010903680280B202510110011807030580CD0103017B0280FB027E0176027301810D028102020401809C0153011201510181A5018102010102510196AF0FA0369A053B078458115002A03FFE04010701029F012E02178220030306080802071E048E823E80C2078177013D0485E0076D030103"; -_T Lowercase = x"611A2F010A01040124180108010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010201010101010101010101010101010102010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010201010101030201010102010302040102010303020102010101010102010102010102010301010102020203060102010201010101010101010101010101010101020101010101010101010101010101010101020201010103010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010702010202010104010101010101010145012407021E0560012B0101010301020412011B2301020303010101010101010101010101010101010101010101010105010102010202333001010101010101010101010101010101010101010101010101010101010101010101090101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101020101010101010101010101010201010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010130298B472B010482F8068882097780C0410101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010109010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010908060A08080808060A080808080E020808080808080501020601030301020804020208080A03010279010D01100D6D01030203011B010401040102020804040121100401834B1A87463001010302010101010101040101020108030101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101020701010104010C2601010501A0791301010101010101010101010101010101010101010101010101010101010101010101010101010101010101010113010101010101010101010101010101010101010101010101010103808501010101010101010101010103010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010A010101010201010101010101010104010101020101030101010101010101010101010101010101010101050105010101010101010101010101010101040101010601010101010101010118030101010383352B010E0650A04F40070C0584291A84CD28808824809B0B010F0107010281C3010203012A01098505338BCD20A0558020A0659A1A1A0701121A1A1A0401010107010B1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1C1C1901061A1901061A1901061A1901061A190106010187340A0114060681053E88B422"; -_T Ll = x"611A3A0129180108010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010201010101010101010101010101010102010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010201010101030201010102010302040102010303020102010101010102010102010102010301010102020203060102010201010101010101010101010101010101020101010101010101010101010101010101020201010103010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010702010202010104010101010101010145011B80C10101010301030312011B2301020303010101010101010101010101010101010101010101010105010102010202333001010101010101010101010101010101010101010101010101010101010101010101090101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101020101010101010101010101010201010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010130298B472B020382F806888209772C3F0D0122660101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010109010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010908060A08080808060A080808080E020808080808080501020601030301020804020208080A030102811201030203011B010401040102020804040135018AAB3001010302010101010101040101020106050101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101020701010104010C2601010501A07913010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101130101010101010101010101010101010101010101010101010101018087010101010101010101010101030101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010801010101020101010101010101010401010102010103010101010101010101010101010101010101010105010501010101010101010101010101010104010101060101010101010101011C01030183352B05090750A04F40070C0584291A84CD28808824809B0B010F010701028703338BCD20A0558020A0659A1A1A0701121A1A1A0401010107010B1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1C1C1901061A1901061A1901061A1901061A190106010187340A0114060689F722"; -_T Extender = x"80B701821802836E0181B901835A0182F0017F018943013801826301818E0144019389012B0567025D03A06F160185F60183C20116018089016C011502A0547B018810028BDA0182680384CF01A050A902849C020101A0715802880603"; -_T Lm = x"82B012040C0E0507010101808501050181DE0180E60180A402810D0204011F010901030180A00180A70184D4017F0182350186DA016B0182630181D00680AE3F0D01222582B1010D01100D8BDF0280F10180BF0181D5012B05050161025D03A06F160184E206810E0172011C027909500117016903030281D50116018089016C01150267040901A05406012D0287E006012A0109A0638504844F0D40020101A0400C0401070102A030313E80C90783AD01845F01"; -_T Mn = x"83007081130581092D0101010201020101480B30151001650702060202010423011E1B5B0B3A09090118040109010301052B033C082A180120370101010408040103070A021D013A010404080114021A010202390104020402020303011E0203010B0239010405010204011402160601013A0102010104080107020B021E013D010C0132010301370101030503010407020B021D013A0102010601050214021C0239020404080114021D014801070301015A0102070C08620102090B0749021B0101010101370E01050102050B0124090166040106010202021902040310040D01020206010F0182BF0383B2031D021E021E02400201070801020B09012D03010175022201760304020901060380DB0202013A010107010101010208060A0201300E01103104300101050101050128090C0220040202010338010102030101033A080202809803010D010704010601030280C64082D00D0401030C8BFE03808D016020822A046B02A075D401040A2002500281100103010401190205018097021A120D012608190B2E0330010204020227014306020202020C0108012F01330101030202050201012A02080180EE0102010401A04F300182E110101083CD0180E201809505868603010205042803040180A502823D048183025003460B31047B01360F290102020A033104020207013D03240501083E010C0234090A0402015F030201010206010201809D0103081502390203012507030580C308020301011701540601010402010280EE04060201021B025508020101026A0101010206010165030204010581030901028100020101040180900402020401200A280602040801090602032E0D01028196070106010152160207010201027A060301010201070101480203010101815B020B0234050501010194FD01060FA0369A053B078418013F045101A04CB80292612E0217822003110802071E0480940387BB37043208010E011605010F8550070111020701020105640180A0078177013D0481FC0483E0076D07AC17B580F0"; -_T Sk = x"5E01010147010601040103018209040C0E05070101011175010E0285020197340101030B030D030D030D02909C02A07663170902670283D0010E02A0504611837B01010180A201A0F41705"; -_T Po = x"210301030201010101020A0203021B01440105010E02070182BE01080181D20629013601020102012C02140201020D0101034A0466012B0E80E903360F1F018105020A01808C01780179018186010C01816F015A010A0280A80F010170014A0504026F0680AB018264098305017C034702809D0301032506010481390280D802808007010680AC071C027D043B053E0240080B018342020808080902040203030B0101010A8C9A0401027001808F020403020102090102010102020A05010A02040101010D020381AC03390180BD01A0740202810D0363010A017306817C0456022803010131022F01610D10027C047E02100280F901A052240702011601140202040303010407030601010280950301030201010101020A0203021B0124010202819A03829C013001819E0182E70180C7011F01811009260170074207590483B8052C0480BD076D0201047E0430024F0404010D01010358066B0181A1050A020101680180FA1769031C0D4C0180820380FC01810803809B015C08530301055D0A8137052A028285024A0D80AF018470058B7C02A03A7B0280850141050801835204814701A04CBC019DE7058ED202"; -_T Deprecated = x"814901852901890301010188290288C50682B902ADDCD601"; -_T Other_ID_Continue = x"80B70182CF018FE10986680186310290ED01A0CE6901"; -_U[] _tab = [ -_U("Alphabetic", Alphabetic), -_U("ASCII_Hex_Digit", ASCII_Hex_Digit), -_U("Bidi_Control", Bidi_Control), -_U("Cased", Cased), -_U("Case_Ignorable", Case_Ignorable), -_U("Cc", Cc), -_U("Cf", Cf), -_U("Close_Punctuation", Pe), -_U("Cn", Cn), -_U("Co", Co), -_U("Connector_Punctuation", Pc), -_U("Control", Cc), -_U("Cs", Cs), -_U("Currency_Symbol", Sc), -_U("Dash", Dash), -_U("Dash_Punctuation", Pd), -_U("Decimal_Number", Nd), -_U("Default_Ignorable_Code_Point", Default_Ignorable_Code_Point), -_U("Deprecated", Deprecated), -_U("Diacritic", Diacritic), -_U("Enclosing_Mark", Me), -_U("Extender", Extender), -_U("Final_Punctuation", Pf), -_U("Format", Cf), -_U("Grapheme_Base", Grapheme_Base), -_U("Grapheme_Extend", Grapheme_Extend), -_U("Grapheme_Link", Grapheme_Link), -_U("Hex_Digit", Hex_Digit), -_U("Hyphen", Hyphen), -_U("ID_Compat_Math_Continue", ID_Compat_Math_Continue), -_U("ID_Compat_Math_Start", ID_Compat_Math_Start), -_U("ID_Continue", ID_Continue), -_U("Ideographic", Ideographic), -_U("IDS_Binary_Operator", IDS_Binary_Operator), -_U("ID_Start", ID_Start), -_U("IDS_Trinary_Operator", IDS_Trinary_Operator), -_U("IDS_Unary_Operator", IDS_Unary_Operator), -_U("Initial_Punctuation", Pi), -_U("Join_Control", Join_Control), -_U("Letter_Number", Nl), -_U("Line_Separator", Zl), -_U("Ll", Ll), -_U("Lm", Lm), -_U("Lo", Lo), -_U("Logical_Order_Exception", Logical_Order_Exception), -_U("Lowercase", Lowercase), -_U("Lowercase_Letter", Ll), -_U("Lt", Lt), -_U("Lu", Lu), -_U("Math", Math), -_U("Math_Symbol", Sm), -_U("Mc", Mc), -_U("Me", Me), -_U("Mn", Mn), -_U("Modifier_Letter", Lm), -_U("Modifier_Symbol", Sk), -_U("Nd", Nd), -_U("Nl", Nl), -_U("No", No), -_U("Noncharacter_Code_Point", Noncharacter_Code_Point), -_U("Nonspacing_Mark", Mn), -_U("Open_Punctuation", Ps), -_U("Other_Alphabetic", Other_Alphabetic), -_U("Other_Default_Ignorable_Code_Point", Other_Default_Ignorable_Code_Point), -_U("Other_Grapheme_Extend", Other_Grapheme_Extend), -_U("Other_ID_Continue", Other_ID_Continue), -_U("Other_ID_Start", Other_ID_Start), -_U("Other_Letter", Lo), -_U("Other_Lowercase", Other_Lowercase), -_U("Other_Math", Other_Math), -_U("Other_Number", No), -_U("Other_Punctuation", Po), -_U("Other_Symbol", So), -_U("Other_Uppercase", Other_Uppercase), -_U("Paragraph_Separator", Zp), -_U("Pattern_Syntax", Pattern_Syntax), -_U("Pattern_White_Space", Pattern_White_Space), -_U("Pc", Pc), -_U("Pd", Pd), -_U("Pe", Pe), -_U("Pf", Pf), -_U("Pi", Pi), -_U("Po", Po), -_U("Prepended_Concatenation_Mark", Prepended_Concatenation_Mark), -_U("Private_Use", Co), -_U("Ps", Ps), -_U("Quotation_Mark", Quotation_Mark), -_U("Radical", Radical), -_U("Regional_Indicator", Regional_Indicator), -_U("Sc", Sc), -_U("Sentence_Terminal", Sentence_Terminal), -_U("Sk", Sk), -_U("Sm", Sm), -_U("So", So), -_U("Soft_Dotted", Soft_Dotted), -_U("Space_Separator", Zs), -_U("Spacing_Mark", Mc), -_U("Surrogate", Cs), -_U("Terminal_Punctuation", Terminal_Punctuation), -_U("Titlecase_Letter", Lt), -_U("Unassigned", Cn), -_U("Unified_Ideograph", Unified_Ideograph), -_U("Uppercase", Uppercase), -_U("Uppercase_Letter", Lu), -_U("Variation_Selector", Variation_Selector), -_U("White_Space", White_Space), -_U("XID_Continue", XID_Continue), -_U("XID_Start", XID_Start), -_U("Zl", Zl), -_U("Zp", Zp), -_U("Zs", Zs), -]; -} - -struct blocks -{ -private alias _U = immutable(UnicodeProperty); -@property static _U[] tab() pure { return _tab; } -static immutable: -private alias _T = ubyte[]; -_T CJK_Unified_Ideographs_Extension_G = x"A300009350"; -_T Tai_Le = x"995030"; -_T Coptic = x"A02C808080"; -_T Halfwidth_and_Fullwidth_Forms = x"A0FF0080F0"; -_T Cyrillic = x"84008100"; -_T CJK_Radicals_Supplement = x"A02E808080"; -_T Thaana = x"878040"; -_T Specials = x"A0FFF010"; -_T Hangul_Jamo_Extended_B = x"A0D7B050"; -_T Linear_B_Syllabary = x"A100008080"; -_T Ogham = x"968020"; -_T Arabic_Extended_C = x"A10EC040"; -_T Nabataean = x"A1088030"; -_T Makasar = x"A11EE020"; -_T CJK_Unified_Ideographs_Extension_B = x"A20000A0A6E0"; -_T Siddham = x"A115808080"; -_T Kana_Extended_A = x"A1B10030"; -_T Supplemental_Arrows_C = x"A1F8008100"; -_T Kaktovik_Numerals = x"A1D2C020"; -_T Combining_Diacritical_Marks = x"830070"; -_T Vertical_Forms = x"A0FE1010"; -_T Old_Sogdian = x"A10F0030"; -_T Old_South_Arabian = x"A10A6020"; -_T Cyrillic_Extended_C = x"9C8010"; -_T Myanmar = x"900080A0"; -_T Bassa_Vah = x"A16AD030"; -_T Deseret = x"A1040050"; -_T Phaistos_Disc = x"A101D030"; -_T Common_Indic_Number_Forms = x"A0A83010"; -_T Unified_Canadian_Aboriginal_Syllabics_Extended = x"98B050"; -_T Kaithi = x"A1108050"; -_T Medefaidrin = x"A16E4060"; -_T Arabic_Supplement = x"875030"; -_T Yi_Radicals = x"A0A49040"; -_T Tai_Tham = x"9A208090"; -_T Hiragana = x"A0304060"; -_T Hangul_Compatibility_Jamo = x"A0313060"; -_T Linear_B_Ideograms = x"A100808080"; -_T Syriac_Supplement = x"886010"; -_T Ahom = x"A1170050"; -_T Shorthand_Format_Controls = x"A1BCA010"; -_T Phonetic_Extensions = x"9D008080"; -_T Nag_Mundari = x"A1E4D030"; -_T Devanagari = x"89008080"; -_T Tibetan = x"8F008100"; -_T Hanifi_Rohingya = x"A10D0040"; -_T Old_Uyghur = x"A10F7040"; -_T Enclosed_Ideographic_Supplement = x"A1F2008100"; -_T Brahmi = x"A110008080"; -_T Number_Forms = x"A0215040"; -_T Mayan_Numerals = x"A1D2E020"; -_T Ornamental_Dingbats = x"A1F65030"; -_T Symbols_for_Legacy_Computing = x"A1FB008100"; -_T Unified_Canadian_Aboriginal_Syllabics_Extended_A = x"A11AB010"; -_T Greek_and_Coptic = x"83708090"; -_T CJK_Unified_Ideographs_Extension_I = x"A2EBF08270"; -_T Counting_Rod_Numerals = x"A1D36020"; -_T Ethiopic_Extended_B = x"A1E7E020"; -_T Supplemental_Mathematical_Operators = x"A02A008100"; -_T Osage = x"A104B050"; -_T Nushu = x"A1B1708190"; -_T Chess_Symbols = x"A1FA0070"; -_T Cuneiform = x"A120008400"; -_T Takri = x"A1168050"; -_T Enclosed_Alphanumeric_Supplement = x"A1F1008100"; -_T Sutton_SignWriting = x"A1D80082B0"; -_T Miscellaneous_Technical = x"A023008100"; -_T Old_Italic = x"A1030030"; -_T Toto = x"A1E29030"; -_T Hanunoo = x"972020"; -_T Lisu = x"A0A4D030"; -_T Limbu = x"990050"; -_T Saurashtra = x"A0A88060"; -_T Elbasan = x"A1050030"; -_T Variation_Selectors_Supplement = x"AE010080F0"; -_T Psalter_Pahlavi = x"A10B8030"; -_T Palmyrene = x"A1086020"; -_T Tagbanwa = x"976020"; -_T CJK_Unified_Ideographs_Extension_F = x"A2CEB09D40"; -_T Combining_Diacritical_Marks_for_Symbols = x"A020D030"; -_T Dingbats = x"A0270080C0"; -_T Nyiakeng_Puachue_Hmong = x"A1E10050"; -_T Combining_Half_Marks = x"A0FE2010"; -_T Supplementary_Private_Use_Area_A = x"AF0000A10000"; -_T Playing_Cards = x"A1F0A060"; -_T Malayalam = x"8D008080"; -_T Indic_Siyaq_Numbers = x"A1EC7050"; -_T Variation_Selectors = x"A0FE0010"; -_T Thai = x"8E008080"; -_T Supplemental_Arrows_B = x"A029008080"; -_T Zanabazar_Square = x"A11A0050"; -_T Ideographic_Symbols_and_Punctuation = x"A16FE020"; -_T Symbols_and_Pictographs_Extended_A = x"A1FA708090"; -_T High_Private_Use_Surrogates = x"A0DB808080"; -_T Modifier_Tone_Letters = x"A0A70020"; -_T Samaritan = x"880040"; -_T Egyptian_Hieroglyph_Format_Controls = x"A1343030"; -_T Latin_Extended_Additional = x"9E008100"; -_T Coptic_Epact_Numbers = x"A102E020"; -_T Small_Form_Variants = x"A0FE5020"; -_T Balinese = x"9B008080"; -_T Mandaic = x"884020"; -_T Block_Elements = x"A0258020"; -_T Supplemental_Symbols_and_Pictographs = x"A1F9008100"; -_T Miscellaneous_Mathematical_Symbols_A = x"A027C030"; -_T Tifinagh = x"A02D3050"; -_T Arabic_Extended_A = x"88A060"; -_T Cyrillic_Extended_A = x"A02DE020"; -_T Syriac = x"870050"; -_T Latin_Extended_E = x"A0AB3040"; -_T Caucasian_Albanian = x"A1053040"; -_T Soyombo = x"A11A5060"; -_T Syloti_Nagri = x"A0A80030"; -_T NKo = x"87C040"; -_T Tai_Viet = x"A0AA8060"; -_T Low_Surrogates = x"A0DC008400"; -_T Elymaic = x"A10FE020"; -_T Hatran = x"A108E020"; -_T Sundanese_Supplement = x"9CC010"; -_T CJK_Unified_Ideographs_Extension_H = x"A313509060"; -_T Chorasmian = x"A10FB030"; -_T Kana_Extended_B = x"A1AFF010"; -_T Glagolitic = x"A02C0060"; -_T Devanagari_Extended_A = x"A11B0060"; -_T Hangul_Syllables = x"A0AC00A02BB0"; -_T Osmanya = x"A1048030"; -_T Enclosed_CJK_Letters_and_Months = x"A032008100"; -_T Meetei_Mayek_Extensions = x"A0AAE020"; -_T Hangul_Jamo = x"91008100"; -_T Mro = x"A16A4030"; -_T Currency_Symbols = x"A020A030"; -_T Bopomofo_Extended = x"A031A020"; -_T Chakma = x"A1110050"; -_T Pau_Cin_Hau = x"A11AC040"; -_T Latin_Extended_G = x"A1DF008100"; -_T CJK_Unified_Ideographs = x"A04E00A05200"; -_T Znamenny_Musical_Notation = x"A1CF0080D0"; -_T New_Tai_Lue = x"998060"; -_T Modi = x"A1160060"; -_T Bamum_Supplement = x"A168008240"; -_T Cypro_Minoan = x"A12F9070"; -_T Basic_Latin = x"008080"; -_T Multani = x"A1128030"; -_T Alchemical_Symbols = x"A1F7008080"; -_T Bopomofo = x"A0310030"; -_T Adlam = x"A1E90060"; -_T Khitan_Small_Script = x"A18B008200"; -_T Old_Turkic = x"A10C0050"; -_T Lao = x"8E808080"; -_T Cherokee_Supplement = x"A0AB7050"; -_T CJK_Compatibility_Ideographs = x"A0F9008200"; -_T Latin_Extended_F = x"A1078040"; -_T Lisu_Supplement = x"A11FB010"; -_T Latin_Extended_A = x"81008080"; -_T Spacing_Modifier_Letters = x"82B050"; -_T Gothic = x"A1033020"; -_T Sinhala_Archaic_Numbers = x"A111E020"; -_T Egyptian_Hieroglyphs = x"A130008430"; -_T Sinhala = x"8D808080"; -_T Rejang = x"A0A93030"; -_T Glagolitic_Supplement = x"A1E00030"; -_T Lepcha = x"9C0050"; -_T Optical_Character_Recognition = x"A0244020"; -_T Greek_Extended = x"9F008100"; -_T Miscellaneous_Symbols_and_Pictographs = x"A1F3008300"; -_T Emoticons = x"A1F60050"; -_T Tirhuta = x"A1148060"; -_T Marchen = x"A11C7050"; -_T Geometric_Shapes_Extended = x"A1F7808080"; -_T Arabic_Presentation_Forms_A = x"A0FB5082B0"; -_T Cyrillic_Extended_D = x"A1E03060"; -_T Phoenician = x"A1090020"; -_T Wancho = x"A1E2C040"; -_T Meroitic_Hieroglyphs = x"A1098020"; -_T Alphabetic_Presentation_Forms = x"A0FB0050"; -_T Linear_A = x"A106008180"; -_T Mahjong_Tiles = x"A1F00030"; -_T Gurmukhi = x"8A008080"; -_T Byzantine_Musical_Symbols = x"A1D0008100"; -_T Transport_and_Map_Symbols = x"A1F6808080"; -_T Unified_Canadian_Aboriginal_Syllabics = x"94008280"; -_T Khojki = x"A1120050"; -_T Khudawadi = x"A112B050"; -_T Newa = x"A114008080"; -_T Dogra = x"A1180050"; -_T Ancient_Greek_Musical_Notation = x"A1D20050"; -_T Cypriot_Syllabary = x"A1080040"; -_T Latin_1_Supplement = x"80808080"; -_T Hangul_Jamo_Extended_A = x"A0A96020"; -_T Imperial_Aramaic = x"A1084020"; -_T Tamil_Supplement = x"A11FC040"; -_T Oriya = x"8B008080"; -_T Tai_Xuan_Jing_Symbols = x"A1D30060"; -_T Enclosed_Alphanumerics = x"A0246080A0"; -_T Meetei_Mayek = x"A0ABC040"; -_T Tagalog = x"970020"; -_T Sundanese = x"9B8040"; -_T CJK_Compatibility_Ideographs_Supplement = x"A2F8008220"; -_T Cuneiform_Numbers_and_Punctuation = x"A124008080"; -_T Ethiopic_Extended = x"A02D8060"; -_T Domino_Tiles = x"A1F03070"; -_T Old_Permic = x"A1035030"; -_T Supplementary_Private_Use_Area_B = x"B00000"; -_T Shavian = x"A1045030"; -_T Anatolian_Hieroglyphs = x"A144008280"; -_T Lycian = x"A1028020"; -_T Inscriptional_Pahlavi = x"A10B6020"; -_T Ancient_Greek_Numbers = x"A1014050"; -_T Miao = x"A16F0080A0"; -_T CJK_Unified_Ideographs_Extension_E = x"A2B8209690"; -_T Pahawh_Hmong = x"A16B008090"; -_T Tangut = x"A170009800"; -_T Bengali = x"89808080"; -_T CJK_Compatibility_Forms = x"A0FE3020"; -_T Vithkuqi = x"A1057050"; -_T Supplemental_Punctuation = x"A02E008080"; -_T Armenian = x"853060"; -_T Arabic_Presentation_Forms_B = x"A0FE708090"; -_T Tangut_Components = x"A188008300"; -_T Sogdian = x"A10F3040"; -_T Ottoman_Siyaq_Numbers = x"A1ED0050"; -_T Buhid = x"974020"; -_T Myanmar_Extended_A = x"A0AA6020"; -_T Manichaean = x"A10AC040"; -_T Miscellaneous_Mathematical_Symbols_B = x"A029808080"; -_T Latin_Extended_D = x"A0A72080E0"; -_T Avestan = x"A10B0040"; -_T Small_Kana_Extension = x"A1B13040"; -_T Ethiopic_Supplement = x"938020"; -_T Arabic = x"86008100"; -_T Kayah_Li = x"A0A90030"; -_T Javanese = x"A0A98060"; -_T Lydian = x"A1092020"; -_T Aegean_Numbers = x"A1010040"; -_T Braille_Patterns = x"A028008100"; -_T Buginese = x"9A0020"; -_T CJK_Symbols_and_Punctuation = x"A0300040"; -_T Batak = x"9BC040"; -_T Combining_Diacritical_Marks_Supplement = x"9DC040"; -_T High_Surrogates = x"A0D8008380"; -_T Vedic_Extensions = x"9CD030"; -_T Nandinagari = x"A119A060"; -_T Kanbun = x"A0319010"; -_T Arrows = x"A0219070"; -_T Devanagari_Extended = x"A0A8E020"; -_T Inscriptional_Parthian = x"A10B4020"; -_T Cham = x"A0AA0060"; -_T Box_Drawing = x"A025008080"; -_T Ugaritic = x"A1038020"; -_T Georgian = x"90A060"; -_T Tangut_Supplement = x"A18D008080"; -_T Kana_Supplement = x"A1B0008100"; -_T Sharada = x"A1118060"; -_T Tamil = x"8B808080"; -_T Old_Persian = x"A103A040"; -_T Geometric_Shapes = x"A025A060"; -_T Cherokee = x"93A060"; -_T Superscripts_and_Subscripts = x"A0207030"; -_T CJK_Strokes = x"A031C030"; -_T Letterlike_Symbols = x"A0210050"; -_T CJK_Unified_Ideographs_Extension_D = x"A2B74080E0"; -_T Yijing_Hexagram_Symbols = x"A04DC040"; -_T Georgian_Extended = x"9C9030"; -_T Khmer_Symbols = x"99E020"; -_T Sora_Sompeng = x"A110D030"; -_T Private_Use_Area = x"A0E0009900"; -_T Kharoshthi = x"A10A0060"; -_T Dives_Akuru = x"A1190060"; -_T Ancient_Symbols = x"A1019040"; -_T Katakana = x"A030A060"; -_T Ideographic_Description_Characters = x"A02FF010"; -_T Cyrillic_Extended_B = x"A0A64060"; -_T Meroitic_Cursive = x"A109A060"; -_T Mende_Kikakui = x"A1E80080E0"; -_T Rumi_Numeral_Symbols = x"A10E6020"; -_T Combining_Diacritical_Marks_Extended = x"9AB050"; -_T Telugu = x"8C008080"; -_T Cyrillic_Supplement = x"850030"; -_T Ethiopic = x"92008180"; -_T Old_North_Arabian = x"A10A8020"; -_T IPA_Extensions = x"825060"; -_T Vai = x"A0A5008140"; -_T Latin_Extended_C = x"A02C6020"; -_T Bamum = x"A0A6A060"; -_T Control_Pictures = x"A0240040"; -_T Mongolian = x"980080B0"; -_T Warang_Citi = x"A118A060"; -_T Katakana_Phonetic_Extensions = x"A031F010"; -_T Mathematical_Operators = x"A022008100"; -_T Early_Dynastic_Cuneiform = x"A1248080D0"; -_T Ol_Chiki = x"9C5030"; -_T Mahajani = x"A1115030"; -_T Myanmar_Extended_B = x"A0A9E020"; -_T Khmer = x"97808080"; -_T Grantha = x"A113008080"; -_T Kannada = x"8C808080"; -_T Phags_pa = x"A0A84040"; -_T Mathematical_Alphanumeric_Symbols = x"A1D4008400"; -_T Kawi = x"A11F0060"; -_T Musical_Symbols = x"A1D1008100"; -_T Yezidi = x"A10E8040"; -_T Kangxi_Radicals = x"A02F0080E0"; -_T Phonetic_Extensions_Supplement = x"9D8040"; -_T CJK_Unified_Ideographs_Extension_A = x"A0340099C0"; -_T Mongolian_Supplement = x"A1166020"; -_T Bhaiksuki = x"A11C0070"; -_T Hebrew = x"859070"; -_T Gujarati = x"8A808080"; -_T Tangsa = x"A16A7060"; -_T General_Punctuation = x"A0200070"; -_T CJK_Compatibility = x"A033008100"; -_T Yi_Syllables = x"A0A0008490"; -_T Carian = x"A102A040"; -_T Miscellaneous_Symbols = x"A026008100"; -_T CJK_Unified_Ideographs_Extension_C = x"A2A7009040"; -_T Miscellaneous_Symbols_and_Arrows = x"A02B008100"; -_T Old_Hungarian = x"A10C808080"; -_T Latin_Extended_B = x"818080D0"; -_T Runic = x"96A060"; -_T Tags = x"AE00008080"; -_T Supplemental_Arrows_A = x"A027F010"; -_T Masaram_Gondi = x"A11D0060"; -_T Arabic_Extended_B = x"887030"; -_T Ethiopic_Extended_A = x"A0AB0030"; -_T Duployan = x"A1BC0080A0"; -_T Georgian_Supplement = x"A02D0030"; -_T Gunjala_Gondi = x"A11D6050"; -_T Arabic_Mathematical_Alphabetic_Symbols = x"A1EE008100"; -_U[] _tab = [ -_U("Adlam", Adlam), -_U("Aegean Numbers", Aegean_Numbers), -_U("Ahom", Ahom), -_U("Alchemical Symbols", Alchemical_Symbols), -_U("Alphabetic Presentation Forms", Alphabetic_Presentation_Forms), -_U("Anatolian Hieroglyphs", Anatolian_Hieroglyphs), -_U("Ancient Greek Musical Notation", Ancient_Greek_Musical_Notation), -_U("Ancient Greek Numbers", Ancient_Greek_Numbers), -_U("Ancient Symbols", Ancient_Symbols), -_U("Arabic", Arabic), -_U("Arabic Extended-A", Arabic_Extended_A), -_U("Arabic Extended-B", Arabic_Extended_B), -_U("Arabic Extended-C", Arabic_Extended_C), -_U("Arabic Mathematical Alphabetic Symbols", Arabic_Mathematical_Alphabetic_Symbols), -_U("Arabic Presentation Forms-A", Arabic_Presentation_Forms_A), -_U("Arabic Presentation Forms-B", Arabic_Presentation_Forms_B), -_U("Arabic Supplement", Arabic_Supplement), -_U("Armenian", Armenian), -_U("Arrows", Arrows), -_U("Avestan", Avestan), -_U("Balinese", Balinese), -_U("Bamum", Bamum), -_U("Bamum Supplement", Bamum_Supplement), -_U("Basic Latin", Basic_Latin), -_U("Bassa Vah", Bassa_Vah), -_U("Batak", Batak), -_U("Bengali", Bengali), -_U("Bhaiksuki", Bhaiksuki), -_U("Block Elements", Block_Elements), -_U("Bopomofo", Bopomofo), -_U("Bopomofo Extended", Bopomofo_Extended), -_U("Box Drawing", Box_Drawing), -_U("Brahmi", Brahmi), -_U("Braille Patterns", Braille_Patterns), -_U("Buginese", Buginese), -_U("Buhid", Buhid), -_U("Byzantine Musical Symbols", Byzantine_Musical_Symbols), -_U("Carian", Carian), -_U("Caucasian Albanian", Caucasian_Albanian), -_U("Chakma", Chakma), -_U("Cham", Cham), -_U("Cherokee", Cherokee), -_U("Cherokee Supplement", Cherokee_Supplement), -_U("Chess Symbols", Chess_Symbols), -_U("Chorasmian", Chorasmian), -_U("CJK Compatibility", CJK_Compatibility), -_U("CJK Compatibility Forms", CJK_Compatibility_Forms), -_U("CJK Compatibility Ideographs", CJK_Compatibility_Ideographs), -_U("CJK Compatibility Ideographs Supplement", CJK_Compatibility_Ideographs_Supplement), -_U("CJK Radicals Supplement", CJK_Radicals_Supplement), -_U("CJK Strokes", CJK_Strokes), -_U("CJK Symbols and Punctuation", CJK_Symbols_and_Punctuation), -_U("CJK Unified Ideographs", CJK_Unified_Ideographs), -_U("CJK Unified Ideographs Extension A", CJK_Unified_Ideographs_Extension_A), -_U("CJK Unified Ideographs Extension B", CJK_Unified_Ideographs_Extension_B), -_U("CJK Unified Ideographs Extension C", CJK_Unified_Ideographs_Extension_C), -_U("CJK Unified Ideographs Extension D", CJK_Unified_Ideographs_Extension_D), -_U("CJK Unified Ideographs Extension E", CJK_Unified_Ideographs_Extension_E), -_U("CJK Unified Ideographs Extension F", CJK_Unified_Ideographs_Extension_F), -_U("CJK Unified Ideographs Extension G", CJK_Unified_Ideographs_Extension_G), -_U("CJK Unified Ideographs Extension H", CJK_Unified_Ideographs_Extension_H), -_U("CJK Unified Ideographs Extension I", CJK_Unified_Ideographs_Extension_I), -_U("Combining Diacritical Marks", Combining_Diacritical_Marks), -_U("Combining Diacritical Marks Extended", Combining_Diacritical_Marks_Extended), -_U("Combining Diacritical Marks for Symbols", Combining_Diacritical_Marks_for_Symbols), -_U("Combining Diacritical Marks Supplement", Combining_Diacritical_Marks_Supplement), -_U("Combining Half Marks", Combining_Half_Marks), -_U("Common Indic Number Forms", Common_Indic_Number_Forms), -_U("Control Pictures", Control_Pictures), -_U("Coptic", Coptic), -_U("Coptic Epact Numbers", Coptic_Epact_Numbers), -_U("Counting Rod Numerals", Counting_Rod_Numerals), -_U("Cuneiform", Cuneiform), -_U("Cuneiform Numbers and Punctuation", Cuneiform_Numbers_and_Punctuation), -_U("Currency Symbols", Currency_Symbols), -_U("Cypriot Syllabary", Cypriot_Syllabary), -_U("Cypro-Minoan", Cypro_Minoan), -_U("Cyrillic", Cyrillic), -_U("Cyrillic Extended-A", Cyrillic_Extended_A), -_U("Cyrillic Extended-B", Cyrillic_Extended_B), -_U("Cyrillic Extended-C", Cyrillic_Extended_C), -_U("Cyrillic Extended-D", Cyrillic_Extended_D), -_U("Cyrillic Supplement", Cyrillic_Supplement), -_U("Deseret", Deseret), -_U("Devanagari", Devanagari), -_U("Devanagari Extended", Devanagari_Extended), -_U("Devanagari Extended-A", Devanagari_Extended_A), -_U("Dingbats", Dingbats), -_U("Dives Akuru", Dives_Akuru), -_U("Dogra", Dogra), -_U("Domino Tiles", Domino_Tiles), -_U("Duployan", Duployan), -_U("Early Dynastic Cuneiform", Early_Dynastic_Cuneiform), -_U("Egyptian Hieroglyph Format Controls", Egyptian_Hieroglyph_Format_Controls), -_U("Egyptian Hieroglyphs", Egyptian_Hieroglyphs), -_U("Elbasan", Elbasan), -_U("Elymaic", Elymaic), -_U("Emoticons", Emoticons), -_U("Enclosed Alphanumerics", Enclosed_Alphanumerics), -_U("Enclosed Alphanumeric Supplement", Enclosed_Alphanumeric_Supplement), -_U("Enclosed CJK Letters and Months", Enclosed_CJK_Letters_and_Months), -_U("Enclosed Ideographic Supplement", Enclosed_Ideographic_Supplement), -_U("Ethiopic", Ethiopic), -_U("Ethiopic Extended", Ethiopic_Extended), -_U("Ethiopic Extended-A", Ethiopic_Extended_A), -_U("Ethiopic Extended-B", Ethiopic_Extended_B), -_U("Ethiopic Supplement", Ethiopic_Supplement), -_U("General Punctuation", General_Punctuation), -_U("Geometric Shapes", Geometric_Shapes), -_U("Geometric Shapes Extended", Geometric_Shapes_Extended), -_U("Georgian", Georgian), -_U("Georgian Extended", Georgian_Extended), -_U("Georgian Supplement", Georgian_Supplement), -_U("Glagolitic", Glagolitic), -_U("Glagolitic Supplement", Glagolitic_Supplement), -_U("Gothic", Gothic), -_U("Grantha", Grantha), -_U("Greek and Coptic", Greek_and_Coptic), -_U("Greek Extended", Greek_Extended), -_U("Gujarati", Gujarati), -_U("Gunjala Gondi", Gunjala_Gondi), -_U("Gurmukhi", Gurmukhi), -_U("Halfwidth and Fullwidth Forms", Halfwidth_and_Fullwidth_Forms), -_U("Hangul Compatibility Jamo", Hangul_Compatibility_Jamo), -_U("Hangul Jamo", Hangul_Jamo), -_U("Hangul Jamo Extended-A", Hangul_Jamo_Extended_A), -_U("Hangul Jamo Extended-B", Hangul_Jamo_Extended_B), -_U("Hangul Syllables", Hangul_Syllables), -_U("Hanifi Rohingya", Hanifi_Rohingya), -_U("Hanunoo", Hanunoo), -_U("Hatran", Hatran), -_U("Hebrew", Hebrew), -_U("High Private Use Surrogates", High_Private_Use_Surrogates), -_U("High Surrogates", High_Surrogates), -_U("Hiragana", Hiragana), -_U("Ideographic Description Characters", Ideographic_Description_Characters), -_U("Ideographic Symbols and Punctuation", Ideographic_Symbols_and_Punctuation), -_U("Imperial Aramaic", Imperial_Aramaic), -_U("Indic Siyaq Numbers", Indic_Siyaq_Numbers), -_U("Inscriptional Pahlavi", Inscriptional_Pahlavi), -_U("Inscriptional Parthian", Inscriptional_Parthian), -_U("IPA Extensions", IPA_Extensions), -_U("Javanese", Javanese), -_U("Kaithi", Kaithi), -_U("Kaktovik Numerals", Kaktovik_Numerals), -_U("Kana Extended-A", Kana_Extended_A), -_U("Kana Extended-B", Kana_Extended_B), -_U("Kana Supplement", Kana_Supplement), -_U("Kanbun", Kanbun), -_U("Kangxi Radicals", Kangxi_Radicals), -_U("Kannada", Kannada), -_U("Katakana", Katakana), -_U("Katakana Phonetic Extensions", Katakana_Phonetic_Extensions), -_U("Kawi", Kawi), -_U("Kayah Li", Kayah_Li), -_U("Kharoshthi", Kharoshthi), -_U("Khitan Small Script", Khitan_Small_Script), -_U("Khmer", Khmer), -_U("Khmer Symbols", Khmer_Symbols), -_U("Khojki", Khojki), -_U("Khudawadi", Khudawadi), -_U("Lao", Lao), -_U("Latin-1 Supplement", Latin_1_Supplement), -_U("Latin Extended-A", Latin_Extended_A), -_U("Latin Extended Additional", Latin_Extended_Additional), -_U("Latin Extended-B", Latin_Extended_B), -_U("Latin Extended-C", Latin_Extended_C), -_U("Latin Extended-D", Latin_Extended_D), -_U("Latin Extended-E", Latin_Extended_E), -_U("Latin Extended-F", Latin_Extended_F), -_U("Latin Extended-G", Latin_Extended_G), -_U("Lepcha", Lepcha), -_U("Letterlike Symbols", Letterlike_Symbols), -_U("Limbu", Limbu), -_U("Linear A", Linear_A), -_U("Linear B Ideograms", Linear_B_Ideograms), -_U("Linear B Syllabary", Linear_B_Syllabary), -_U("Lisu", Lisu), -_U("Lisu Supplement", Lisu_Supplement), -_U("Low Surrogates", Low_Surrogates), -_U("Lycian", Lycian), -_U("Lydian", Lydian), -_U("Mahajani", Mahajani), -_U("Mahjong Tiles", Mahjong_Tiles), -_U("Makasar", Makasar), -_U("Malayalam", Malayalam), -_U("Mandaic", Mandaic), -_U("Manichaean", Manichaean), -_U("Marchen", Marchen), -_U("Masaram Gondi", Masaram_Gondi), -_U("Mathematical Alphanumeric Symbols", Mathematical_Alphanumeric_Symbols), -_U("Mathematical Operators", Mathematical_Operators), -_U("Mayan Numerals", Mayan_Numerals), -_U("Medefaidrin", Medefaidrin), -_U("Meetei Mayek", Meetei_Mayek), -_U("Meetei Mayek Extensions", Meetei_Mayek_Extensions), -_U("Mende Kikakui", Mende_Kikakui), -_U("Meroitic Cursive", Meroitic_Cursive), -_U("Meroitic Hieroglyphs", Meroitic_Hieroglyphs), -_U("Miao", Miao), -_U("Miscellaneous Mathematical Symbols-A", Miscellaneous_Mathematical_Symbols_A), -_U("Miscellaneous Mathematical Symbols-B", Miscellaneous_Mathematical_Symbols_B), -_U("Miscellaneous Symbols", Miscellaneous_Symbols), -_U("Miscellaneous Symbols and Arrows", Miscellaneous_Symbols_and_Arrows), -_U("Miscellaneous Symbols and Pictographs", Miscellaneous_Symbols_and_Pictographs), -_U("Miscellaneous Technical", Miscellaneous_Technical), -_U("Modi", Modi), -_U("Modifier Tone Letters", Modifier_Tone_Letters), -_U("Mongolian", Mongolian), -_U("Mongolian Supplement", Mongolian_Supplement), -_U("Mro", Mro), -_U("Multani", Multani), -_U("Musical Symbols", Musical_Symbols), -_U("Myanmar", Myanmar), -_U("Myanmar Extended-A", Myanmar_Extended_A), -_U("Myanmar Extended-B", Myanmar_Extended_B), -_U("Nabataean", Nabataean), -_U("Nag Mundari", Nag_Mundari), -_U("Nandinagari", Nandinagari), -_U("Newa", Newa), -_U("New Tai Lue", New_Tai_Lue), -_U("NKo", NKo), -_U("Number Forms", Number_Forms), -_U("Nushu", Nushu), -_U("Nyiakeng Puachue Hmong", Nyiakeng_Puachue_Hmong), -_U("Ogham", Ogham), -_U("Ol Chiki", Ol_Chiki), -_U("Old Hungarian", Old_Hungarian), -_U("Old Italic", Old_Italic), -_U("Old North Arabian", Old_North_Arabian), -_U("Old Permic", Old_Permic), -_U("Old Persian", Old_Persian), -_U("Old Sogdian", Old_Sogdian), -_U("Old South Arabian", Old_South_Arabian), -_U("Old Turkic", Old_Turkic), -_U("Old Uyghur", Old_Uyghur), -_U("Optical Character Recognition", Optical_Character_Recognition), -_U("Oriya", Oriya), -_U("Ornamental Dingbats", Ornamental_Dingbats), -_U("Osage", Osage), -_U("Osmanya", Osmanya), -_U("Ottoman Siyaq Numbers", Ottoman_Siyaq_Numbers), -_U("Pahawh Hmong", Pahawh_Hmong), -_U("Palmyrene", Palmyrene), -_U("Pau Cin Hau", Pau_Cin_Hau), -_U("Phags-pa", Phags_pa), -_U("Phaistos Disc", Phaistos_Disc), -_U("Phoenician", Phoenician), -_U("Phonetic Extensions", Phonetic_Extensions), -_U("Phonetic Extensions Supplement", Phonetic_Extensions_Supplement), -_U("Playing Cards", Playing_Cards), -_U("Private Use Area", Private_Use_Area), -_U("Psalter Pahlavi", Psalter_Pahlavi), -_U("Rejang", Rejang), -_U("Rumi Numeral Symbols", Rumi_Numeral_Symbols), -_U("Runic", Runic), -_U("Samaritan", Samaritan), -_U("Saurashtra", Saurashtra), -_U("Sharada", Sharada), -_U("Shavian", Shavian), -_U("Shorthand Format Controls", Shorthand_Format_Controls), -_U("Siddham", Siddham), -_U("Sinhala", Sinhala), -_U("Sinhala Archaic Numbers", Sinhala_Archaic_Numbers), -_U("Small Form Variants", Small_Form_Variants), -_U("Small Kana Extension", Small_Kana_Extension), -_U("Sogdian", Sogdian), -_U("Sora Sompeng", Sora_Sompeng), -_U("Soyombo", Soyombo), -_U("Spacing Modifier Letters", Spacing_Modifier_Letters), -_U("Specials", Specials), -_U("Sundanese", Sundanese), -_U("Sundanese Supplement", Sundanese_Supplement), -_U("Superscripts and Subscripts", Superscripts_and_Subscripts), -_U("Supplemental Arrows-A", Supplemental_Arrows_A), -_U("Supplemental Arrows-B", Supplemental_Arrows_B), -_U("Supplemental Arrows-C", Supplemental_Arrows_C), -_U("Supplemental Mathematical Operators", Supplemental_Mathematical_Operators), -_U("Supplemental Punctuation", Supplemental_Punctuation), -_U("Supplemental Symbols and Pictographs", Supplemental_Symbols_and_Pictographs), -_U("Supplementary Private Use Area-A", Supplementary_Private_Use_Area_A), -_U("Supplementary Private Use Area-B", Supplementary_Private_Use_Area_B), -_U("Sutton SignWriting", Sutton_SignWriting), -_U("Syloti Nagri", Syloti_Nagri), -_U("Symbols and Pictographs Extended-A", Symbols_and_Pictographs_Extended_A), -_U("Symbols for Legacy Computing", Symbols_for_Legacy_Computing), -_U("Syriac", Syriac), -_U("Syriac Supplement", Syriac_Supplement), -_U("Tagalog", Tagalog), -_U("Tagbanwa", Tagbanwa), -_U("Tags", Tags), -_U("Tai Le", Tai_Le), -_U("Tai Tham", Tai_Tham), -_U("Tai Viet", Tai_Viet), -_U("Tai Xuan Jing Symbols", Tai_Xuan_Jing_Symbols), -_U("Takri", Takri), -_U("Tamil", Tamil), -_U("Tamil Supplement", Tamil_Supplement), -_U("Tangsa", Tangsa), -_U("Tangut", Tangut), -_U("Tangut Components", Tangut_Components), -_U("Tangut Supplement", Tangut_Supplement), -_U("Telugu", Telugu), -_U("Thaana", Thaana), -_U("Thai", Thai), -_U("Tibetan", Tibetan), -_U("Tifinagh", Tifinagh), -_U("Tirhuta", Tirhuta), -_U("Toto", Toto), -_U("Transport and Map Symbols", Transport_and_Map_Symbols), -_U("Ugaritic", Ugaritic), -_U("Unified Canadian Aboriginal Syllabics", Unified_Canadian_Aboriginal_Syllabics), -_U("Unified Canadian Aboriginal Syllabics Extended", Unified_Canadian_Aboriginal_Syllabics_Extended), -_U("Unified Canadian Aboriginal Syllabics Extended-A", Unified_Canadian_Aboriginal_Syllabics_Extended_A), -_U("Vai", Vai), -_U("Variation Selectors", Variation_Selectors), -_U("Variation Selectors Supplement", Variation_Selectors_Supplement), -_U("Vedic Extensions", Vedic_Extensions), -_U("Vertical Forms", Vertical_Forms), -_U("Vithkuqi", Vithkuqi), -_U("Wancho", Wancho), -_U("Warang Citi", Warang_Citi), -_U("Yezidi", Yezidi), -_U("Yijing Hexagram Symbols", Yijing_Hexagram_Symbols), -_U("Yi Radicals", Yi_Radicals), -_U("Yi Syllables", Yi_Syllables), -_U("Zanabazar Square", Zanabazar_Square), -_U("Znamenny Musical Notation", Znamenny_Musical_Notation), -]; -} - -struct scripts -{ -private alias _U = immutable(UnicodeProperty); -@property static _U[] tab() pure { return _tab; } -static immutable: -private alias _T = ubyte[]; -_T Old_Hungarian = x"A10C80330D330706"; -_T Coptic = x"83E20EA02890740507"; -_T Ol_Chiki = x"9C5030"; -_T Cyrillic = x"840080850280A997500980A2014C01906720A0784060A0578E02A0E2003E2101"; -_T Thaana = x"878032"; -_T Inscriptional_Parthian = x"A10B40160208"; -_T Nabataean = x"A108801F0809"; -_T Ogham = x"96801D"; -_T Meroitic_Hieroglyphs = x"A1098020"; -_T Makasar = x"A11EE019"; -_T Siddham = x"A11580360226"; -_T Old_Persian = x"A103A024040E"; -_T Imperial_Aramaic = x"A10840160109"; -_T Myanmar = x"900080A0A099401F6120"; -_T Deseret = x"A1040050"; -_T Kaithi = x"A11080430A01"; -_T Medefaidrin = x"A16E405B"; -_T Kayah_Li = x"A0A9002E0101"; -_T Hiragana = x"A03041560603A17F61811F12011D03A040AD01"; -_T Ahom = x"A117001B020F0417"; -_T Devanagari = x"890051040F021AA09F6020A072000A"; -_T Tibetan = x"8F0048012404270124010F01070402"; -_T Nko = x"87C03B0203"; -_T Brahmi = x"A110004E04240901"; -_T Osage = x"A104B0240424"; -_T Nushu = x"A16FE101A0418E818C"; -_T Cuneiform = x"A12000839A666F01050B80C4"; -_T Takri = x"A116803A060A"; -_T Toto = x"A1E2901F"; -_T Latin = x"411A061A2F010F010517011F0181C127059A1B2606310504050D01464181008171010D01100D808D0206011B0111298AD720A07AA2660340050201010105180E83302B01090104A04F9607841A1A061A882506012A0109A0D7451F0606"; -_T Hanunoo = x"972015"; -_T Limbu = x"99001F010C040C0401030C"; -_T Saurashtra = x"A0A88046080C"; -_T Lisu = x"A0A4D030A07AB001"; -_T Egyptian_Hieroglyphs = x"A130008456"; -_T Elbasan = x"A1050028"; -_T Palmyrene = x"A1086020"; -_T Tagbanwa = x"97600D01030102"; -_T Old_Italic = x"A10300240903"; -_T Caucasian_Albanian = x"A10530340B01"; -_T Malayalam = x"8D000D01030133010301060410021A"; -_T Inherited = x"83007081150281C40B1A0182E004915B1F820103010D010704010601030280C640820C0280C2218F39046B02A0CD6510100E83CF0180E201905A01A0BBC42E0217822003110802071E04AC2F5280F0"; -_T Sora_Sompeng = x"A110D019070A"; -_T Linear_B = x"A100000C011A01130102010F020E227B"; -_T Nyiakeng_Puachue_Hmong = x"A1E1002D030E020A0402"; -_T Meroitic_Cursive = x"A109A0180414022E"; -_T Thai = x"8E013A051C"; -_T Mende_Kikakui = x"A1E80080C50210"; -_T Old_Sogdian = x"A10F0028"; -_T Old_Turkic = x"A10C0049"; -_T Samaritan = x"88002E020F"; -_T Old_South_Arabian = x"A10A6020"; -_T Hanifi_Rohingya = x"A10D0028080A"; -_T Balinese = x"9B004D032F"; -_T Mandaic = x"88401C0201"; -_T SignWriting = x"A1D800828C0F05010F"; -_T Tifinagh = x"A02D303807020E01"; -_T Tai_Viet = x"A0AA80431805"; -_T Syriac = x"87000E013C020381100B"; -_T Soyombo = x"A11A5053"; -_T Elymaic = x"A10FE017"; -_T Hatran = x"A108E01301020505"; -_T Chorasmian = x"A10FB01C"; -_T Glagolitic = x"A02C0060A1B3A0070111020701020105"; -_T Osmanya = x"A104801E020A"; -_T Linear_A = x"A10600813709160A08"; -_T Mro = x"A16A401F010A0402"; -_T Chakma = x"A11100350112"; -_T Modi = x"A11600450B0A"; -_T Bassa_Vah = x"A16AD01E0206"; -_T Han = x"A02E801A01590C80D62F01010119090E0483C499C040A05200A05900816E026AA07508020C02A0900EA0A6E020903A0680DE0296820E9D310F826E89A2821E85E2934B059060"; -_T Multani = x"A112800701010104010F010B"; -_T Bopomofo = x"82EA02A02E192B7020"; -_T Adlam = x"A1E9004C040A0402"; -_T Khitan_Small_Script = x"A16FE4019B1B81D6"; -_T Lao = x"8E810201010105011801010117020501010107010A0204"; -_T Psalter_Pahlavi = x"A10B801207040C07"; -_T Anatolian_Hieroglyphs = x"A144008247"; -_T Canadian_Aboriginal = x"94008280823046A101BA10"; -_T Common = x"00411A061A2F010F010517011F0181C127050502147401090106010101827D0106010E0103012001809C0182040180810284D90181950481220185EF03470280CB02010184CD010D01070401060103020183050C0257010B030B010F11213F2601030206011B01112903048297190B1583A0810082740220016982005E819215010101190F0804045B0203015A0280931020240B0130401F512F015880A899C040A0590022660380A50A80F40180A001818B010E02A051D20280D00A16230113010480930101201A061A0B0A012D02400701070A05810203042D0309500D332D80E41BA0B9A40492AC743C80F60A27023E03110802071E043D80D5140C140C57091980875501470102020102020204010C010101070141010402080107011C010401050101030701815402812402329471444C3D82C22C04640C0F020F010F01250A80AE381A01020D2C040907020E06809A83D80411030D0377045F060C04010F0C0438080A0628081E02024E81540C0E020D0309072E0107080E040907090780930137250AAC0407011E60"; -_T Gothic = x"A103301B"; -_T Yi = x"A0A000848D0337"; -_T Sinhala = x"8D8103011203180109010102070301040601010108060A0203A103EC14"; -_T Rejang = x"A0A930240B01"; -_T Lepcha = x"9C0038030F0303"; -_T Tai_Tham = x"9A203F011D020B060A060E"; -_T Dives_Akuru = x"A1190007020102080102011E0102020C090A"; -_T Meetei_Mayek = x"A0AAE01780C92E020A"; -_T Tirhuta = x"A1148048080A"; -_T Marchen = x"A11C70200216010E"; -_T Wancho = x"A1E2C03A0501"; -_T Phoenician = x"A109001C0301"; -_T Gurmukhi = x"8A0103010604020216010701020102010202010105040202030301070401010711"; -_T Khudawadi = x"A112B03B050A"; -_T Khojki = x"A1120012012F"; -_T Newa = x"A114005C0105"; -_T Dogra = x"A118003C"; -_T Oriya = x"8B01030108020202160107010201050209020202030703040201050212"; -_T Tagalog = x"9700160901"; -_T Sundanese = x"9B8040810008"; -_T Old_Permic = x"A103502B"; -_T Shavian = x"A1045030"; -_T Lycian = x"A102801D"; -_T Miao = x"A16F004B04390711"; -_T Tangut = x"A16FE0011F97F8088300820009"; -_T Bengali = x"8980040108020202160107010103040209020202040801040201050219"; -_T Inscriptional_Pahlavi = x"A10B60130508"; -_T Vithkuqi = x"A105700B010F01070102010B010F01070102"; -_T Armenian = x"85312602320203A0F58305"; -_T New_Tai_Lue = x"99802C041A060B0302"; -_T Sogdian = x"A10F302A"; -_T Buhid = x"974014"; -_T Manichaean = x"A10AC027040C"; -_T Greek = x"83700401030204010104010101010301010114013F0E109926053205040554018140160206022602060208010101010101011F0235010F010E0206011302030109812701A08A3E01A055DA4F1101A0D05F46"; -_T Braille = x"A028008100"; -_T Avestan = x"A10B00360307"; -_T Arabic = x"8600050106010E01030120010A0B1A016C0122503080F01F0102064A011DA0F2507310816B025002360701201070050180878F631F7E03A0DF0004011B010201010201010A0104010101010601040101010101010301020101020101010101010101010102010102040107010401040101010A01110503010501113402"; -_T Javanese = x"A0A9804E020A0402"; -_T Lydian = x"A109201A0501"; -_T Pau_Cin_Hau = x"A11AC039"; -_T Cypro_Minoan = x"A12F9063"; -_T Buginese = x"9A001C0202"; -_T Batak = x"9BC0340804"; -_T Nandinagari = x"A119A008022E020B"; -_T Cham = x"A0AA0037090E020A0204"; -_T Gunjala_Gondi = x"A11D60060102012501020106070A"; -_T Cypriot = x"A10800060201012C010203010201"; -_T Ugaritic = x"A103801E0101"; -_T Georgian = x"90A02601010501022B01048B902B020390402601010501"; -_T Sharada = x"A1118060"; -_T Tamil = x"8B820201060303010403020101010203020303030C040503030104020106010E15A113C5320D01"; -_T Cherokee = x"93A0560206A0977250"; -_T Pahawh_Hmong = x"A16B00460A0A010701150513"; -_T Syloti_Nagri = x"A0A8002D"; -_T Kharoshthi = x"A10A0004010205080103011D0203040A0709"; -_T Zanabazar_Square = x"A11A0048"; -_T Katakana = x"A030A15A020380F01080D02F0158A0CC0E0A012DA0B05204010701020101811F0332010E04"; -_T Telugu = x"8C000D0103011701100209010301040702010302010204020A0709"; -_T Ethiopic = x"92004901040207010101040229010402210104020701010104020F0139010402430220031A99E61709070107010701070107010701070107A07D22060206020609070107A13CB10701040102010F"; -_T Vai = x"A0A500812C"; -_T Bamum = x"A0A6A058A0C1088239"; -_T Hangul = x"910081009E2E0281015E711F411FA076E11D8283A02BA40C170431A027A41F0306020602060203"; -_T Mongolian = x"980002020101140659072BA0FDB50D"; -_T Nag_Mundari = x"A1E4D02A"; -_T Old_Uyghur = x"A10F701A"; -_T Mahajani = x"A1115027"; -_T Khmer = x"97805E020A060A81E620"; -_T Grantha = x"A113000401080202021601070102010502090202020302010601050702070305"; -_T Kannada = x"8C800D01030117010A0105020901030104070206020104020A0103"; -_T Kawi = x"A11F00110129031C"; -_T Yezidi = x"A10E802A01030202"; -_T Old_North_Arabian = x"A10A8020"; -_T Tai_Le = x"99501E0205"; -_T Hebrew = x"859137081B0406A0F5281A0105010101020102010A"; -_T Gujarati = x"8A8103010901030116010701020105020A0103010302010F04020C0707"; -_T Tangsa = x"A16A704F010A"; -_T Carian = x"A102A031"; -_T Bhaiksuki = x"A11C0009012D010E0A1D"; -_T Masaram_Gondi = x"A11D00070102012C030101020109080A"; -_T Runic = x"96A04B030B"; -_T Duployan = x"A1BC006B050D0309070A0204"; -_T Warang_Citi = x"A118A0530C01"; -_T Phags_Pa = x"A0A84038"; -_U[] _tab = [ -_U("Adlam", Adlam), -_U("Ahom", Ahom), -_U("Anatolian_Hieroglyphs", Anatolian_Hieroglyphs), -_U("Arabic", Arabic), -_U("Armenian", Armenian), -_U("Avestan", Avestan), -_U("Balinese", Balinese), -_U("Bamum", Bamum), -_U("Bassa_Vah", Bassa_Vah), -_U("Batak", Batak), -_U("Bengali", Bengali), -_U("Bhaiksuki", Bhaiksuki), -_U("Bopomofo", Bopomofo), -_U("Brahmi", Brahmi), -_U("Braille", Braille), -_U("Buginese", Buginese), -_U("Buhid", Buhid), -_U("Canadian_Aboriginal", Canadian_Aboriginal), -_U("Carian", Carian), -_U("Caucasian_Albanian", Caucasian_Albanian), -_U("Chakma", Chakma), -_U("Cham", Cham), -_U("Cherokee", Cherokee), -_U("Chorasmian", Chorasmian), -_U("Common", Common), -_U("Coptic", Coptic), -_U("Cuneiform", Cuneiform), -_U("Cypriot", Cypriot), -_U("Cypro_Minoan", Cypro_Minoan), -_U("Cyrillic", Cyrillic), -_U("Deseret", Deseret), -_U("Devanagari", Devanagari), -_U("Dives_Akuru", Dives_Akuru), -_U("Dogra", Dogra), -_U("Duployan", Duployan), -_U("Egyptian_Hieroglyphs", Egyptian_Hieroglyphs), -_U("Elbasan", Elbasan), -_U("Elymaic", Elymaic), -_U("Ethiopic", Ethiopic), -_U("Georgian", Georgian), -_U("Glagolitic", Glagolitic), -_U("Gothic", Gothic), -_U("Grantha", Grantha), -_U("Greek", Greek), -_U("Gujarati", Gujarati), -_U("Gunjala_Gondi", Gunjala_Gondi), -_U("Gurmukhi", Gurmukhi), -_U("Han", Han), -_U("Hangul", Hangul), -_U("Hanifi_Rohingya", Hanifi_Rohingya), -_U("Hanunoo", Hanunoo), -_U("Hatran", Hatran), -_U("Hebrew", Hebrew), -_U("Hiragana", Hiragana), -_U("Imperial_Aramaic", Imperial_Aramaic), -_U("Inherited", Inherited), -_U("Inscriptional_Pahlavi", Inscriptional_Pahlavi), -_U("Inscriptional_Parthian", Inscriptional_Parthian), -_U("Javanese", Javanese), -_U("Kaithi", Kaithi), -_U("Kannada", Kannada), -_U("Katakana", Katakana), -_U("Kawi", Kawi), -_U("Kayah_Li", Kayah_Li), -_U("Kharoshthi", Kharoshthi), -_U("Khitan_Small_Script", Khitan_Small_Script), -_U("Khmer", Khmer), -_U("Khojki", Khojki), -_U("Khudawadi", Khudawadi), -_U("Lao", Lao), -_U("Latin", Latin), -_U("Lepcha", Lepcha), -_U("Limbu", Limbu), -_U("Linear_A", Linear_A), -_U("Linear_B", Linear_B), -_U("Lisu", Lisu), -_U("Lycian", Lycian), -_U("Lydian", Lydian), -_U("Mahajani", Mahajani), -_U("Makasar", Makasar), -_U("Malayalam", Malayalam), -_U("Mandaic", Mandaic), -_U("Manichaean", Manichaean), -_U("Marchen", Marchen), -_U("Masaram_Gondi", Masaram_Gondi), -_U("Medefaidrin", Medefaidrin), -_U("Meetei_Mayek", Meetei_Mayek), -_U("Mende_Kikakui", Mende_Kikakui), -_U("Meroitic_Cursive", Meroitic_Cursive), -_U("Meroitic_Hieroglyphs", Meroitic_Hieroglyphs), -_U("Miao", Miao), -_U("Modi", Modi), -_U("Mongolian", Mongolian), -_U("Mro", Mro), -_U("Multani", Multani), -_U("Myanmar", Myanmar), -_U("Nabataean", Nabataean), -_U("Nag_Mundari", Nag_Mundari), -_U("Nandinagari", Nandinagari), -_U("Newa", Newa), -_U("New_Tai_Lue", New_Tai_Lue), -_U("Nko", Nko), -_U("Nushu", Nushu), -_U("Nyiakeng_Puachue_Hmong", Nyiakeng_Puachue_Hmong), -_U("Ogham", Ogham), -_U("Ol_Chiki", Ol_Chiki), -_U("Old_Hungarian", Old_Hungarian), -_U("Old_Italic", Old_Italic), -_U("Old_North_Arabian", Old_North_Arabian), -_U("Old_Permic", Old_Permic), -_U("Old_Persian", Old_Persian), -_U("Old_Sogdian", Old_Sogdian), -_U("Old_South_Arabian", Old_South_Arabian), -_U("Old_Turkic", Old_Turkic), -_U("Old_Uyghur", Old_Uyghur), -_U("Oriya", Oriya), -_U("Osage", Osage), -_U("Osmanya", Osmanya), -_U("Pahawh_Hmong", Pahawh_Hmong), -_U("Palmyrene", Palmyrene), -_U("Pau_Cin_Hau", Pau_Cin_Hau), -_U("Phags_Pa", Phags_Pa), -_U("Phoenician", Phoenician), -_U("Psalter_Pahlavi", Psalter_Pahlavi), -_U("Rejang", Rejang), -_U("Runic", Runic), -_U("Samaritan", Samaritan), -_U("Saurashtra", Saurashtra), -_U("Sharada", Sharada), -_U("Shavian", Shavian), -_U("Siddham", Siddham), -_U("SignWriting", SignWriting), -_U("Sinhala", Sinhala), -_U("Sogdian", Sogdian), -_U("Sora_Sompeng", Sora_Sompeng), -_U("Soyombo", Soyombo), -_U("Sundanese", Sundanese), -_U("Syloti_Nagri", Syloti_Nagri), -_U("Syriac", Syriac), -_U("Tagalog", Tagalog), -_U("Tagbanwa", Tagbanwa), -_U("Tai_Le", Tai_Le), -_U("Tai_Tham", Tai_Tham), -_U("Tai_Viet", Tai_Viet), -_U("Takri", Takri), -_U("Tamil", Tamil), -_U("Tangsa", Tangsa), -_U("Tangut", Tangut), -_U("Telugu", Telugu), -_U("Thaana", Thaana), -_U("Thai", Thai), -_U("Tibetan", Tibetan), -_U("Tifinagh", Tifinagh), -_U("Tirhuta", Tirhuta), -_U("Toto", Toto), -_U("Ugaritic", Ugaritic), -_U("Vai", Vai), -_U("Vithkuqi", Vithkuqi), -_U("Wancho", Wancho), -_U("Warang_Citi", Warang_Citi), -_U("Yezidi", Yezidi), -_U("Yi", Yi), -_U("Zanabazar_Square", Zanabazar_Square), -]; -} - -struct hangul -{ -private alias _U = immutable(UnicodeProperty); -@property static _U[] tab() pure { return _tab; } -static immutable: -private alias _T = ubyte[]; -_T V = x"916048A0C60817"; -_T T = x"91A858A0C5CB31"; -_T LVT = x"A0AC011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B"; -_T L = x"910060A098001D"; -_T LV = x"A0AC00011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B011B01"; -_U[] _tab = [ -_U("L", L), -_U("Leading_Jamo", L), -_U("LV", LV), -_U("LV_Syllable", LV), -_U("LVT", LVT), -_U("LVT_Syllable", LVT), -_U("T", T), -_U("Trailing_Jamo", T), -_U("V", V), -_U("Vowel_Jamo", V), -]; -} -bool isFormatGen(dchar ch) @safe pure nothrow @nogc -{ - if (ch < 8288) - { - if (ch < 2192) - { - if (ch < 1564) - { - if (ch == 173) return true; - if (ch < 1536) return false; - if (ch < 1542) return true; - return false; - } - else if (ch < 1565) return true; - else - { - if (ch == 1757) return true; - if (ch == 1807) return true; - return false; - } - } - else if (ch < 2194) return true; - else - { - if (ch < 8203) - { - if (ch == 2274) return true; - if (ch == 6158) return true; - return false; - } - else if (ch < 8208) return true; - else - { - if (ch < 8234) return false; - if (ch < 8239) return true; - return false; - } - } - } - else if (ch < 8293) return true; - else - { - if (ch < 78896) - { - if (ch < 65529) - { - if (ch < 8294) return false; - if (ch < 8304) return true; - if (ch == 65279) return true; - return false; - } - else if (ch < 65532) return true; - else - { - if (ch == 69821) return true; - if (ch == 69837) return true; - return false; - } - } - else if (ch < 78912) return true; - else - { - if (ch < 917505) - { - if (ch < 113824) return false; - if (ch < 113828) return true; - if (ch < 119155) return false; - if (ch < 119163) return true; - return false; - } - else if (ch < 917506) return true; - else - { - if (ch < 917536) return false; - if (ch < 917632) return true; - return false; - } - } - } -} - -bool isControlGen(dchar ch) @safe pure nothrow @nogc -{ - if (ch < 32) return true; - if (ch < 127) return false; - if (ch < 160) return true; - return false; -} - -bool isSpaceGen(dchar ch) @safe pure nothrow @nogc -{ - if (ch < 160) - { - if (ch == 32) return true; - return false; - } - else if (ch < 161) return true; - else - { - if (ch < 8239) - { - if (ch == 5760) return true; - if (ch < 8192) return false; - if (ch < 8203) return true; - return false; - } - else if (ch < 8240) return true; - else - { - if (ch == 8287) return true; - if (ch == 12288) return true; - return false; - } - } -} - -bool isWhiteGen(dchar ch) @safe pure nothrow @nogc -{ - if (ch < 133) - { - if (ch < 9) return false; - if (ch < 14) return true; - if (ch == 32) return true; - return false; - } - else if (ch < 134) return true; - else - { - if (ch < 8232) - { - if (ch < 5760) - { - if (ch == 160) return true; - return false; - } - else if (ch < 5761) return true; - else - { - if (ch < 8192) return false; - if (ch < 8203) return true; - return false; - } - } - else if (ch < 8234) return true; - else - { - if (ch < 8287) - { - if (ch == 8239) return true; - return false; - } - else if (ch < 8288) return true; - else - { - if (ch == 12288) return true; - return false; - } - } - } -} - -bool isHangL(dchar ch) @safe pure nothrow @nogc -{ - if (ch < 4352) return false; - if (ch < 4448) return true; - if (ch < 43360) return false; - if (ch < 43389) return true; - return false; -} - -bool isHangV(dchar ch) @safe pure nothrow @nogc -{ - if (ch < 4448) return false; - if (ch < 4520) return true; - if (ch < 55216) return false; - if (ch < 55239) return true; - return false; -} - -bool isHangT(dchar ch) @safe pure nothrow @nogc -{ - if (ch < 4520) return false; - if (ch < 4608) return true; - if (ch < 55243) return false; - if (ch < 55292) return true; - return false; -} - - -static if (size_t.sizeof == 4) -{ -//2080 bytes -enum lowerCaseTrieEntries = TrieEntry!(bool, 8, 5, 8)(x" -0000000000000040000000E0", -x" -000001000000014000002500", -x" -020201000402030206020205080702020202020202020202020202020202020202020202020202020202020202020202 -020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202 -020202020202020202020202020202020202020202020202020202020202020202020202020202020000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000001000000030002000500040006000600060006000600060006000600060006 -000600070008000600060006000600060006000600060006000A0009000C000B000E000D000600060006000F00060006 -000600060006000600110010000600060006000600060006000600060006000600060006000600060006000600060006 -000600060006000600060006000600060006000600060006000600060006000600060006000600060006000600060006 -000600060006000600060006000600060006000600060006000600060013001200060006001400060006000600060006 -000600060006000600060006000600060006000600060006000600060006000600060006000600060006000600060006 -000600060006000600060006000600060006000600060006000600060006000600060006001500060006000600160006 -0006000600060006001800170019000600060006000600060006001A0006000600060006000600060006000600060006 -0006001B000600060006000600060006000600060006000600060006000600060006000600060006000600060006001C -000600060006000600060006000600060006000600060006000600060006000600060006000600060006000600060006 -000600060006000600060006000600060006000600060006001E001D0020001F00060006000600060006000600210006 -000600220006000600060006000600060023000600060006000600060006000600060006000600060006000600060006 -000600060006000600060006000600060006000600060006000600060006000600060006000600060006000600060006 -000600060006000600060006000600060006000600060006000600060006000600000000000000000000000007FFFFFE -000000000420040080000000FF7FFFFFAAAAAAAA55AAAAAAAAAAAB55D4AAAAAA4E243129E6512D2AB5555240AA29AAAA -AAAAAAAA93FAAAAAFFFFAA85FFFFFFFFFFEFFFFF01FFFFFF000000030000001F0000000000000000000000203C8A0000 -00010000FFFFF000AAE37FFF192FAAAA00000000FFFF0000FFFFFFFFAAAAAAAAAAAAA802AAAAAAAAAAAAD554AAAAAAAA -AAAAAAAA0000AAAA00000000FFFFFFFF000001FF00000000000000000000000000000000000000000000000000000000 -00000000000000000000000000000000000000000000000000000000000000000000000000000000FFFF0000F7FFFFFF -000000000000000000000000000000000000000000000000000000003F00000000000000000000000000000000000000 -000001FF000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0000000000000000 -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABFEAAAAAAAAAAAAAAAAAAAAAAAAAAAAA003F00FF00FF00FF00FF003F3FFF00FF -00FF00FF40DF00FF00CF00DC00DC00FF000000000000000000000000800200001FFF0000000000000000000000000000 -0008C40032108000000043C0FFFF00000000001000000000000000000000000000000000000000000000000000000000 -0000000000000000FFFF0000000003FF00000000FFFF0000FFFFFFFF3FDA1562AAAAAAAAAAAAAAAAAAAAAAAA0008501A -FFFFFFFF000020BF0000000000000000000000000000000000000000000000000000000000000000AAAAAAAA00002AAA -3AAAAAAA00000000000000000000000000000000AAABAAA8AAAAAAAA95FFAAAAAABA50AAAAA082AA02AA050A075C0000 -00000000FFFF0000F7FFFFFFFFFF03FFFFFFFFFFFFFFFFFF000000000000000000F8007F000000000000000000000000 -00000000000000000000000000000000000000000000000007FFFFFE0000000000000000000000000000000000000000 -00000000FFFFFF000000FFFF000000000000000000000000FF0000000FFFFFFF00000000000000000000000000000000 -FF8000001BFBFFFB000000000000000000000000000000000000000000000000FFFFFFB907FDFFFF0000000000000000 -000000000000000000000000000000000000000000000000FFFFFFFF0007FFFF00000000000000000000000000000000 -0000000000000000FFFFFFFF00000000000000000000000000000000FFFFFFFF00000000000000000000000000000000 -FC000000000FFFFFFFDFC000000000FF0FFFFFFCEBC000000000FFEFFFFFFC00C000000F00FFFFFFFFFC000000000FFF -FFFFFFC0FC000000000FFFFFFFFFC000000000FF0FFFFFFCFFC000000000FFFFFFFFFC000000003FF7FFFFFCF0000003 -0FDFFFFFFFC00000003F7FFFFFFF00000000FDFFFFFFFC0000000BF7000000007FFFFBFF000007E00000000000000000 -0000000000000000000000000000000000000000FFFF0000FFFFFFFF00003FFF00000000000000000000000000000000 -00000000FFFFFFFC0000000F000000000000000000000000000000000000000000000000000000000000000000000000 -00000000000000000000000000000000", -); -//1856 bytes -enum upperCaseTrieEntries = TrieEntry!(bool, 8, 5, 8)(x" -0000000000000040000000E0", -x" -000001000000014000001E00", -x" -020201000402030206020205080702020202020202020202020202020202020202020202020202020202020202020202 -020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202 -020202020202020202020202020202020202020202020202020202020202020202020202020202020000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000001000000030002000500040006000600060006000600060006000600060006 -00060007000800060006000600060006000600060006000600060009000B000A000C0006000600060006000D00060006 -00060006000600060006000E000600060006000600060006000600060006000600060006000600060006000600060006 -000600060006000600060006000600060006000600060006000600060006000600060006000600060006000600060006 -000600060006000600060006000600060006000600060006000600060010000F00060006000600060006000600060006 -000600060006000600060006000600060006000600060006000600060006000600060006000600060006000600060006 -000600060006000600060006000600060006000600060006000600060006000600060006000600060006000600110006 -000600060006000600130012000600060006000600060006000600140006000600060006000600060006000600060006 -000600150006000600060006000600060006000600060006000600060006000600060006000600060006000600060016 -000600060006000600060006000600060006000600060006000600060006000600060006000600060006000600060006 -00060006000600060006000600060006000600060006000600180017001A001900060006000600060006000600060006 -00060006000600060006000600060006001B0006000600060006000600060006001C0006000600060006000600060006 -000600060006000600060006000600060006000600060006000600060006000600060006000600060006000600060006 -0006000600060006000600060006000600060006000600060006000600060006000000000000000007FFFFFE00000000 -00000000000000007F7FFFFF0000000055555555AA555555555554AA2B555555B1DBCED611AED2D54AAAA49055D25555 -555555556C0555550000557A000000000000000000000000000000000000000000000000000000000000000080450000 -FFFED74000000FFB551C8000E6905555FFFFFFFF0000FFFF0000000055555555555554015555555555552AAB55555555 -55555555FFFE5555007FFFFF000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000FFFFFFFF000020BF00000000 -0000000000000000000000000000000000000000FFFFFFFFFFFFFFFF003FFFFF00000000000000000000000000000000 -FFFF0000E7FFFFFF00000000000000005555555555555555555555555555555540155555555555555555555555555555 -3F00FF00FF00FF00AA003F000000FF00000000000F0000000F000F000F001F003E273884C00F3D50000000200000FFFF -000000080000000000000000000000000000000000000000000000000000000000000000FFC000000000FFFF00000000 -FFFFFFFF0000FFFF00000000C025EA9D5555555555555555555555550004280500000000000000005555555500001555 -055555550000000000000000000000000000000055545554555555556A00555555452855555F7D55014102F500200000 -0000000007FFFFFE000000000000000000000000000000000000000000000000FFFFFFFF000000FF0000000000000000 -00000000FFFF0000000FFFFF00000000000000000000000000000000F7FF00000037F7FF000000000000000000000000 -00000000000000000000000000000000FFFFFFFF0007FFFF000000000000000000000000000000000000000000000000 -00000000FFFFFFFF00000000000000000000000000000000FFFFFFFF0000000000000000000000000000000000000000 -03FFFFFFFFF0000000003FFFFFFFFF00D0000003003FDE64FFFF0000000003FF1FDFE7B07B0000000001FC5FFFFFF000 -0000003F03FFFFFFFFF0000000003FFFFFFFFF00F0000003003FFFFFFFFF0000000003FFFFFFFF000000000107FFFFFC -F0000000001FFFFFFFC0000000007FFFFFFF0000000001FF0000040000000000FFFFFFFF000000030000000000000000 -0000000000000000000000000000000000000000FFFF0000FFFF03FFFFFF03FF000003FF000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000", -); -//11648 bytes -enum simpleCaseTrieEntries = TrieEntry!(ushort, 8, 7, 6)(x" -000000000000004000000280", -x" -0000010000000480000011C0", -x" -020201000402030206020205070202020202020202020202020202020202020202020202020202020202020202020202 -020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202 -020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202 -020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202 -020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202 -0202020202020202020202020202020200010000000300020005000400070006000900080000000A000B0000000D000C -000F000E0011001000130012000000140000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000016001500000000000000000000000000000000000000000018001700000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -0000000000000019001A00000000001B001D001C001F001E002100200023002200000000000000000025002400000026 -000000000000000000000000000000000000000000280027000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000002A0029002C002B0000002D00000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -00000000000000000000000000000000002E00000000002F003100300033003200000000000000000000000000000000 -000000000000000000340000000000350000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000036000000000000000000000000000000000000000000000000000000000038003700000000 -0000000000000000000000000000000000000000000000000000000000000000003A0039003C003B003D00000000003E -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000040003F00000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000042004100000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000430000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000450044000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -00000000000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -091DFFFF08E50080031905FB090B00BD09D00B9A0B2B006B082606C70AD200E00467083805D105940A33078501930180 -04D9051EFFFF02F9FFFFFFFFFFFFFFFF091EFFFF08E60081031A05FC090C00BE09D10B9B0B2C006C082706C80AD300E1 -0468083905D205950A3407860194018104DA051FFFFF02FAFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0965FFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF05AE077B0856011809BB0AD803DB0257038F05590B4D01280397062B03150047 -08DD05F3029F0086058B0711FFFF00CB09820AE204FB02130880070108EF0A4505AF077C0857011909BC0AD903DC0258 -0390055A0B4E01290398062C0316004808DE05F402A00087058C0712FFFF00CC09830AE304FC021408810702036A0A46 -09DD09DC04CC04CB01CD01CC0B580B57020001FF0732073100CE00CD08AF08AE005C005B06140613005200510620061F -02B202B106630662015A0159080108000B890B8807E707E604BE04BD0A620A6101B401B305930592021C021B096D096C -FFFFFFFF0879087809390938031C031B0094FFFF06FD0095008E06FE081C008F0B30081D07CA0B31012007CB0A230121 -FFFF0A2404DC04DB07C507C40B660B6506BD06BC016201610683068202AA02A9060A0609000D000C064106400070006F -083B083A00D200D10728072701DF01DE0B810B8001A201A104C604C50A520A5107CF07CE056605650977097609FB09FA -0625036B0021062605FD002205D305FE068806910B180B17026A0269027505260B39027601AF07FAFFFF01B00AC20769 -08B40A47052008B50229027B09900B0900970096FFFF04C908EB00AA032B00D300190018062E062D0105010403CF0553 -09A603D0FFFFFFFF01690168075907EE01A7075A052C077F0301052D0AC0030202560255FFFFFFFF0920091F01ADFFFF -FFFFFFFFFFFFFFFF092A092905E0092B05E205E10AE50AE404FD0AE6079804FE0A5D0799076B0A5E0441076C0A800442 -02FD0A81055102FE076A05520B540B5306550654016B016A05FA05F902C002BF0647064600F200F106A906A80570056F -0ABAFFFF0ABC0ABB0748074701AE022A0B6E0B6D01EB01EA041A04190971097008410840059F059E0940093F08BE08BD -03AE03AD089808970B120B11041C041B040B040A0A8F0A8E02B402B305310530016D016C07F107F005B705B6084B084A -FFFF00D406CE06CD006400630B830B8200C200C1073E073D00790078088A0889057A057906F506F4FFFFFFFFFFFFFFFF -FFFFFFFF07960A0E04CA0797095D07BE014B028B0692014C02D3041D09D509D409260925032403230954095309F709F6 -0B1F04020689021DFFFF052707FB0B3A0AC3FFFF0A48FFFFFFFF0941FFFFFFFF0B2E0521027CFFFF0B76FFFFFFFF00B5 -0B0A0991063C00F9FFFF098A00ABFFFF05A0FFFFFFFF08EC032CFFFFFFFFFFFFFFFFFFFFFFFFFFFF0031FFFFFFFFFFFF -FFFF055409A70516FFFFFFFF0350FFFF041E07EF078001A8FFFF02D4FFFFFFFFFFFFFFFFFFFF0AC1FFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFF0092FFFFFFFF070FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0972FFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF04A504A401CF01CEFFFFFFFF01F601F5 -FFFFFFFF0A37FFFF086E01220382FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0299082806D7FFFF00EBFFFF07F209CA0BA0 -04B30386017C04AE0ABD0A17016601D20973059A0B070B32081E09660106039B062F0B73068AFFFF0607032D00AC0345 -06FA08FD009A084C06D8029A00EC082904B409CE017D04AF0ABE0A18016701D30974059B0B080B33081F09670107039C -06300B74068C068B0608032E00AD034606FB08FE009B084D0BA107F3097809CB059C04B0FFFFFFFF0347FFFF09790B75 -018C018B04A904A80A890A88085F085E058205810989098809DF09DE03BC03BB02F602F508C208C1044A044903630362 -06310B34038301D60ABF059D0A84FFFF01D70A8505DB05DA0A38FFFF086F0123051A04F1096E0291098E0AF505AC0090 -08F906FF08F700A0031D05EF0391009C0B45071F03C1054003D306E70AB6027D086A0A8C04B7012607AA080A01B90A14 -051107EC02DF0A730ACE09A4015B02450664057B0B5B093A062109F0005303C30B46072003C2054103D406E80AB7027E -086B0A8D04B8012707AB080B01BA0A15051207ED02E00A740ACF09A5015C02460665057C0B5C093B062209F1005403C4 -051B04F2096F0292098F0AF605AD009108FA070008F800A1031E05F00392009D0AA30AA205CD05CC024402430B4A0B49 -01FA01F903E603E509B609B50859085805A305A208E008DF08C608C5039A0399088808870937093603DE03DD04180417 -0B220B21FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF05A705A6082B082A00E600E506D006CF005E005D061C061B00050004 -0730072F00CA00C908AD08AC0568056706B506B4010B010A04F004EF01A0019F0A110A100458045707BB07BA0B700B6F -083108300407040609BF09BE08EE08ED0326032508DA08D909F909F803B803B706C202DB021F06C305D80220023705D9 -0A21023804A00A22079204A102DC0793023C023B055C055B0262026106E406E30058005706280627001E001D06790678 -011301120677067602480247056A0569011B011A07A907A8049B049A077807770A910A90046A0469023602350B000AFF -038C038B042C042B090E090D08CA08C903380337092409230A010A0003D803D7082508240A950A94079B079A01CB01CA -0A1C0A1B02C402C304EC04EB0171017006A506A405BB05BA064D064C002C002B06390638003800370B6A0B6900B400B3 -073807370ACD0ACC088E088D04EE04ED0BA2FFFF0227017E071904B50A9A029308520AC40589047D06D503A701330B63 -067C038403030352067E09030135092C06D905B40B270949085A0A060501052407150295022F0A3D05DC0184FFFF0787 -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0BA3FFFF0228017F071A04B60A9B029408530AC5058A047E06D603A801340B64 -067D038503040353067F09040136092D06DA05B50B28094A085B0A07050205250716029602300A3E05DD0185FFFF0788 -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF082209B7050D03EB06F601F001B10B8E -0485024101B704980A9E0A9C046D087C07FE056B0AF706A0039300F704310648090902D908C3068F033B014F09170672 -09EA0B4F03D1084E02E101080AA8FFFFFFFFFFFFFFFFFFFF0A55FFFFFFFFFFFF0AE7015303FC0699042D05C409320668 -0317002D03C905F109F2002509270B3B055D008C085C06B009AB0AE004290882021504FF0A0A0761024901DA071304D3 -0AAE02A108180998058703E106EC03CB011E0B35FFFF039F000EFFFF06950905FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -043305D6036000C3081606C00934006D0374089309FC0B2908BB06030335007E01F70A6D0A5B02CD095506B8055F0137 -0B7E07DA07C8057504DF048B075D09EE02E7044F064E03780141085006A209A00041045D089B091900450B9C06340313 -023309B10B6709E000820271073905430A0C0ADE077D071701E4052804B908B609FE028903AF06DB05B801FB08060B23 -034C05DE035A067A02AD0139092E06860B84004B038D062304390061FFFFFFFF0B85004C038E0624043A0062FFFFFFFF -06E9054205130A160A760A7505CE093CFFFF069DFFFFFFFFFFFFFFFFFFFFFFFF0AE8015403FD069A042E05C509330669 -0318002E03CA05F209F3002609280B3C055E008D085D06B109AC0AE1042A0883021605000A0B0762024A01DB071404D4 -0AAF02A208190999058803E206ED03CC011F0B36FFFF03A0000FFFFF06960906FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -0507FFFFFFFFFFFF030DFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0B92 -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -06F306F204C804C7077607750B870B860803080204740473098509840260025F057805770908090708F408F303340333 -02CC02CB09140913045C045B03D603D50B040B0307C707C604DE04DD0A1E0A1D01AC01AB05CB05CA0240023F072C072B -00DB00DA083F083E0060005F0616061500130012060E060D02A402A306940693015E015D06B706B605B105B0079F079E -0A640A6304930492020802070B5A0B59020E020D03E003DF09460945087B087A03550354090008FF08D208D1037D037C -088508840962096103DA03D9020402030B3E0B3D01B601B50480047F0A420A4107DD07DC05C305C208110810014E014D -065D065C02AC02AB06330632004A0049061E061D0089008808A308A2053F053E073607350206020504C204C101D101D0 -0A5A0A590440043F07D107D0FFFFFFFFFFFFFFFF0886FFFFFFFFFFFFFFFF08F00308030708E808E709C909C803A403A3 -025402530ADD0ADC0430042F07E507E40A260A2507B107B004C404C307220721021A0219054605450099009807040703 -00A300A205F805F7004E004D06530652012501240AFA0AF902F402F30533053201170116077207710497049607B507B4 -0A700A6F03F703F6024E024D0B7D0B7C03BE03BD043C043B08D408D308F608F5035F035E0922092109950994054C054B -08A508A40A780A7704520451019A01990A4A0A4901D501D406E006DF00EE00ED03A906050A020035094F0B41055500F3 -03AA06060A03003609500B42055600F40A35076D022B019D06F804CDFFFFFFFF0A36076E022C019E06F904CEFFFFFFFF -05FF039D00160305068008E9017208700600039E00170306068108EA017308710A3902E501820AC804AA01A301E0078E -0A3A02E601830AC904AB01A401E1078F034E0074035605E308BF000AFFFFFFFF034F0075035705E408C0000BFFFFFFFF -01E6FFFF0A5FFFFF04D5FFFF076FFFFF01E7FFFF0A60FFFF04D6FFFF0770FFFF02C509920656043D002903B305E50B8C -02C609930657043E002A03B405E60B8D02ED044709D602D502090A71041F05CF000808E30A570AB006190321FFFFFFFF -09680B1B03A5028502C1066608F1012A09690B1C03A6028602C2066708F2012B03B906E109F4028F07F40B1305B201FD -03BA06E209F5029007F50B1405B301FE0B7A09E400DC09B3073F0590007C02810B7B09E500DD09B407400591007D0282 -066A0370097CFFFFFFFFFFFFFFFFFFFF066B037102EE0448FFFF097DFFFF0975FFFFFFFF00F5FFFFFFFFFFFFFFFFFFFF -09D702D6020A0A72FFFF00F6FFFFFFFF035C00BB0387FFFFFFFFFFFFFFFFFFFF035D00BC042005D0FFFFFFFFFFFFFFFF -01C3017609CFFFFF029BFFFFFFFFFFFF01C401770A580AB1FFFF029CFFFFFFFFFFFFFFFF03BFFFFFFFFFFFFFFFFFFFFF -000908E4061A0322FFFF03C0FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF06FC -FFFFFFFF09BD0B2DFFFFFFFFFFFFFFFFFFFFFFFFFFFF0951FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0952FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0A7907B204590494017807670A4309DA01EC0534070902F100DE0AEB082E044D -0A7A07B3045A0495017907680A4409DB01ED0535070A02F200DF0AEC082F044EFFFFFFFF0874FFFFFFFF0875FFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0596037A00A60B250745081A052A03FE089909CC -0A92020F07B60487018D02210AA0071D029D010205180860097A0B1D0943065E0597037B00A70B260746081B052B03FF -089A09CD0A93021007B70488018E02220AA1071E029E010305190861097B0B1E0944065FFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF065803C7010C0AED06B2082C050B0443 -086C0A4F0AB201C80753049C0225020B0B5F070B02770538040C0812098600A8093D070505A4002F095B063A08CF003B -03A10670087601510B0B068403E705D4040407D20AA4011402E304BB053A01BB065903C8010D0AEE06B3082D050C0444 -086D0A500AB301C90754049D0226020C0B60070C02780539040D0813098700A9093E070605A50030095C063B08D0003C -03A20671087701520B0C068503E805D5040507D30AA5011502E404BC053B01BC003A0039030E063D0A0F003209D807BF -037E09D90265037F0B200266040305A1FFFF021E0A1A0A19074DFFFFFFFF074EFFFFFFFFFFFFFFFFFFFFFFFF028C095E -02A602A5033A033908CC08CB091209110373037202B902B8099709960470046F07FD07FC0B560B55077A077904B204B1 -0A660A6501E901E8056E056D00D600D5072A07290066006508A908A80020001F062A0629005A00590B1A0B1902640263 -06BF06BE0190018F07C107C005BD05BC0750074F0A3C0A3B04E204E10280027F0B020B0103AC03AB0472047109580957 -08D608D5032803270931093009C109C0057E057D083308320AAD0AAC04220421021802170A130A1201A601A5070E070D -010F010E08210820FFFFFFFFFFFFFFFFFFFFFFFF0358FFFF03290359FFFF032AFFFFFFFF06D206D1FFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF082309B8050E03EC06F701F101B20B8F0486024201B804990A9F0A9D046E087D -07FF056C0AF806A1039400F804320649090A02DA08C40690033C01500918067309EB0B5003D2084F02E201090AA9FFFF -FFFFFFFFFFFFFFFF0A56FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -01D901D80A200A1F0A990A98079D079C0140013F069F069E02BB02BA04F604F5003E003D0643064205BF05BE064B064A -00770076073C073B0040003F0B6C0B6B04EA04E9078407830AD50AD40892089102C802C70AC70AC6017B017AFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0515051402980297094C094B0A050A04 -07AF07AE0A830A820A400A3F018A0189074A074900AF00AE088C088B052F052E05EC05EB00340033FFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0007000608AB08AA00C600C5 -06F106F000E800E70610060F00680067FFFFFFFF01580157083508340B4C0B4B0504050309C309C20A2C0A2B02B002AF -087F087E0960095F09810980039603950B5E0B5D08550854031203110330032F0212021105A905A804110410098D098C -051D051C08690868023E023D0A280A2700FF00FE066D066C0B100B0F06A706A6015601550661066002EA02E9069C069B -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF07A2FFFF04D707A3050804D80B720B710586058509BA09B90AD10AD008450844 -FFFFFFFF004FFFFF0B770050FFFFFFFF0B0E0B0D024C024BFFFF079003B203B107D907D8049F049E03FB03FA09E309E2 -0510050F027A0279080F080E011D011C07080707094200B6098B0B2FFFFF00FA035107100B61009305EA05E9009F009E -06E606E5013E013D0550054F028E028D080508040436043505170791075B0B930174075CFFFF0175FFFFFFFFFFFFFFFF -02F802F7FFFFFFFFFFFFFFFF0310030F0AF20AF1FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF09ECFFFFFFFF09EDFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0B62FFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -043405D7036100C4081706C10935006E0375089409FD0B2A08BC06040336007F01F80A6E0A5C02CE095606B905600138 -0B7F07DB07C9057604E0048C075E09EF02E80450064F03790142085106A309A10042045E089C091A00460B9D06350314 -023409B20B6809E100830272073A05440A0D0ADF077E071801E5052904BA08B709FF028A03B006DC05B901FC08070B24 -034D05DF035B067B02AE013A092F0687FFFFFFFFFFFFFFFF080CFFFFFFFF080DFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF09C4FFFF02670AF305AA0143094D0644074B04CF0B78077307C2013B040004F7 -054D025902EF03ED0A860AB4019707A4060108C7FFFF0453FFFFFFFFFFFFFFFF09C5FFFF02680AF405AB0144094E0645 -074C04D00B79077407C3013C040104F8054E025A02F003EE0A870AB5019807A5060208C8FFFF0454FFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF07510A6705C0075F07EA012F09AF0AAA0AFB095902DD099E04E304F9014507D4 -047900E908CD063602CF0043036C0723040802D1025D05490B8A0147038006740ACA0A7B047B078C023104830B510781 -07520A6805C1076007EB013009B00AAB0AFC095A02DE099F04E404FA014607D5047A00EA08CE063702D00044036D0724 -040902D2025E054A0B8B0148038106750ACB0A7C047C078D023204840B520782FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0AD60A4B074107D6022305710B05099A -09C6022D07940B3F04A2024F0763048105C6033106AA034301BF0B3706C90283028709AD065003C5002702EB060B08D7 -0A2D075508B00A2FFFFFFFFFFFFFFFFF0AD70A4C074207D7022405720B06099B09C7022E07950B4004A3025007640482 -05C7033206AB034401C00B3806CA0284028809AE065103C6002802EC060C08D80A2E075608B10A30FFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -0463088F026B00A40AE906AC0251011009470B43FFFF00B708FB05E7090100B907CC080801310B470561083602FF03E3 -04230583FFFF02A707650A2904E50477063E0A31FFFF03B506D302C90890FFFF00A5046406AD026C01110AEA0B440252 -00B8094805E8FFFF00BA08FC080909020B4807CD0837013203E405620584030002A804240A2AFFFF047807660A3204E6 -03B6063F02CAFFFFFFFF06D4FFFFFFFF048904D10149019107E20B94057301E203E90697044505C80A8A07E803090A4D -0B90091500C7090F072D0341008A0895086604610100096A071B089D007A036605220201033F04BF0ADA0A69046507A0 -083C0963053C046B06EA023901BD0B9609E602FBFFFF066EFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -048A04D2014A019207E30B95057401E303EA0698044605C90A8B07E9030A0A4E0B91091600C80910072E0342008B0896 -086704620101096B071C089E007B036705230202034004C00ADB0A6A046607A1083D0964053D046C06EB023A01BE0B97 -09E702FCFFFF066FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -01DC057F0B9807A601C101A904E70490036E00CF08420733097E00EF045F0848015F0B15068D087202D7034A061108E1 -0A6B036408460AB80557045506BA03CD01DD05800B9907A701C201AA04E80491036F00D008430734097F00F004600849 -01600B16068E087302D8034B061208E20A6C036508470AB90558045606BB03CEFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -09A20084031F07250412025B040E05F5091B0069056306DD034800100A08061704AC0475019507AC06EE030B01EE0AA6 -08A604F30AEF06CB09E80A53050507B809A30085032007260413025C040F05F6091C006A056406DE034900110A090618 -04AD0476019607AD06EF030C01EF0AA708A704F40AF006CC09E90A54050607B903F8065A08B2005508DB05ED04370023 -03760598000000BF0814074305090536000207BC0757019B044B04A60A96033D02730AFD0547016E099C06AE09D20B9E -0014036803F9065B08B3005608DC05EE0438002403770599000100C008150744050A0537000307BD0758019C044C04A7 -0A97033E02740AFE0548016F099D06AF09D30B9F00150369FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF", -); -//11648 bytes -enum fullCaseTrieEntries = TrieEntry!(ushort, 8, 7, 6)(x" -000000000000004000000280", -x" -0000010000000480000011C0", -x" -020201000402030206020205070202020202020202020202020202020202020202020202020202020202020202020202 -020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202 -020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202 -020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202 -020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202 -0202020202020202020202020202020200010000000300020005000400070006000900080000000A000B0000000D000C -000F000E0011001000130012000000140000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000016001500000000000000000000000000000000000000000018001700000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -0000000000000019001A00000000001B001D001C001F001E002100200023002200000000000000000025002400000026 -000000000000000000000000000000000000000000280027000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000002A0029002C002B0000002D00000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -00000000000000000000000000000000002E00000000002F003100300033003200000000000000000000000000000000 -000000000000000000340000000000350000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000036000000000000000000000000000000000000000000000000000000000038003700000000 -0000000000000000000000000000000000000000000000000000000000000000003A0039003C003B003D00000000003E -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000040003F00000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000042004100000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000430000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000450044000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -00000000000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -09BEFFFF0984008A0344065809AC00C70A7C0C6A0BF4007408B107360B9100EF04A608C3062905E60AE607FE01AC0199 -05210569FFFF0320FFFFFFFFFFFFFFFF09BFFFFF0985008B0345065909AD00C80A7D0C6B0BF5007508B207370B9200F0 -04A708C4062A05E70AE707FF01AD019A0522056AFFFF0321FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0A0BFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF060507F208E3012E0A640B9A0412027703C105A60C18013E03C9068A0340004E -097C065002C2009005DA0781FFFF00D50A2A0BA705430230091107710BA20AF9060607F308E4012F0A650B9B04130278 -03C205A70C19013F03CA068B0341004F097D065102C3009105DB0782FFFF00D60A2B0BA80544023109120772039B0AFA -0A890A880512051101E601E50C230C22021D021C07A607A500D800D7094609450065006406730672005B005A067F067E -02D802D706C406C301730172088908880C550C5408680867050405030B180B1701CD01CC05E505E4023902380A140A13 -FFFF03F6090A090909DD09DC03470346009EFFFF076D009F0098076E08A700990BF908A808440BFA013608450AD60137 -031A0AD705240523083F083E0C310C30072A0729017B017A06EB06EA02D002CF06690668000F000E06A0069F00790078 -08C608C500DC00DB079C079B01FB01FA0C4D0C4C01BB01BA050C050B0B080B07084B084A05B305B20A1E0A1D0AAB0AAA -0684039C00250685065A0026062B065B06F006F90BE00BDF028A0289029505710C02029601C80881FFFF01C90B8107E0 -094E0AFB056B094F0247029B0A380BD000A100A0FFFF050F098A00B4035600DD001D001C068D068C01180117040605A0 -0A4E0407FFFFFFFF0182018107D0087401C007D1057707F8032805780B7F032902760275FFFFFFFF09C109C001C6FFFF -FFFFFFFFFFFFFFFF09CE09CD063B09CF063D063C0BAA0BA905450BAB081205460B13081307E20B14047C07E30B39047D -03240B3A059E032507E1059F0C1F0C1E06B606B5018401830657065602E602E506A606A5010101000714071305BD05BC -0B7909280B7B0B7A07BF07BE01C702480C390C3802070206045504540A180A1708CE08CD05F105F009E409E3095B095A -03E103E0092D092C0BD90BD804570456044604450B480B4702DA02D9057C057B0186018508770876060F060E08D808D7 -FFFF00DE073D073C006D006C0C4F0C4E00CC00CB07B407B300820081091B091A05C905C807650764FFFFFFFFFFFFFFFF -FFFFFFFF08100ABE051008110A030838016402AD06FA016502FA04580A810A8009C709C6034F034E09FA09F90AA70AA6 -0BE8043B06F1023AFFFF057208820C030B82FFFF0AFCFFFFFFFF09E5FFFFFFFF0BF7056C029CFFFF0C41FFFFFFFF00BF -0BD10A39069B0109FFFF0A3200B5FFFF05F7FFFFFFFF098B0357FFFFFFFFFFFFFFFFFFFFFFFFFFFF0035FFFFFFFFFFFF -FFFF05A10A4F055FFFFFFFFF0380FFFF0459087507F901C1FFFF02FBFFFFFFFFFFFFFFFFFFFF0B80FFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFF009CFFFFFFFF077FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0A19FFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF04E604E501E801E7FFFFFFFF02120211 -FFFFFFFF0AEAFFFF08FF013803B3FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF02BC08B30746FFFF00FAFFFF08780A750C70 -04F90883019504F10B7C0ACA017F01EB0A1A05EC0BCE0BFB08A90A0C011903CD068E0C3E06F2FFFF0666035800B60372 -076A099E00A408D9074702BD00FB08B404FA0A79019604F20B7D0ACB018001EC0A1B05ED0BCF0BFC08AA0A0D011A03CE -068F0C3F06F406F30667035900B70373076B099F00A508DA0C7108790A1F0A7605EE04F3FFFFFFFF0374FFFF0A200C40 -01A501A404EC04EB0B420B4108F008EF05D105D00A310A300A8B0A8A03F003EF031D031C095F095E0485048403940393 -06900BFD03B401EF0B7E05EF0B3DFFFF01F00B3E063106300AEBFFFF09000139056505390A1502B40A360BBA0603009A -099A076F099800AA0348064A03C300A60C0E079003F8058D040A07570B72029D08FB0B4504FD013C0824089401D20AC4 -055A087203060B2C0B8D0A4C0174026506C505CA0C2609DE06800A9D005C03FC0C0F079103F9058E040B07580B73029E -08FC0B4604FE013D0825089501D30AC5055B087303070B2D0B8E0A4D0175026606C605CB0C2709DF06810A9E005D03FD -0566053A0A1602B50A370BBB0604009B099B0770099900AB0349064B03C400A70B5C0B5B06250624026402630C130C12 -02160215041D041C0A5F0A5E08E608E505F605F5097F097E0963096203CC03CB0919091809DB09DA0415041404530452 -0BEB0BEAFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF05FE05FD08B608B500F500F4073F073E00670066067B067A00050004 -07A407A300D400D30944094305B505B407220721011E011D0538053701B901B80AC10AC004950494083508340C3B0C3A -08BC08BB044204410A680A67098D098C03510350097709760AA90AA803EB03EA07310302023C07320632023D02550633 -0AD4025604E10AD5080C04E20303080D025A025905A905A8028202810754075300610060068706860022002106E006DF -0129012806DB06DA0268026705B705B6013101300823082204DC04DB07EF07EE0B4A0B4904A904A8025402530BC50BC4 -03BE03BD0467046609AF09AE096709660365036409C509C40AB10AB0040F040E08B008AF0B4E0B4D0815081401E401E3 -0ACF0ACE02EB02EA05340533018A01890710070F0613061206AC06AB0030002F06980697003C003B0C350C3400BE00BD -07AE07AD0B8C0B8B09230922053605350C72FFFF02450197078904FB0B5302B608DF0B8305D804BE074403DA014A0C2E -06E403B5032A038206E609A4014C09D00748060C0BF009ED08E70AB60549056F078502B8024D0AF00637019DFFFF0800 -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0C73FFFF02460198078A04FC0B5402B708E00B8405D904BF074503DB014B0C2F -06E503B6032B038306E709A5014D09D10749060D0BF109EE08E80AB7054A0570078602B9024E0AF10638019E06620801 -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF08AD0A60055604220766020C01CA0C5E -04C6026101D004D90B570B5504AC090D088605B80BBC070B03C50107046C06A709AA0300096006F70368016809B806D4 -0A970C1A040808DB0308011B0B61FFFFFFFFFFFFFFFFFFFF0B0BFFFFFFFFFFFF0BAC016C043507020468061C09D606CA -0342003103FE064C0AA1002909CB0C0405AA009608EB071D0A530BA504640913023205470ABA07D8026901F307830519 -0B6802C408A30A4005D60418075C040001340BFEFFFF03D10010FFFF06FD09A6FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -046E062E039100CD08A1072F09D8007603A5093B0AAC0BF2095806600362008C02130B240B1102F409FB072505AC014E -0C4A085B084205C4052704CC07D40A9B030E048A06AD03A9015808DD070D0A48004A049A093009BA004C0C6C0693033E -02510A590C320A8C0094029107AF05900ABC0BA007F607870200057304FF09530AAE02AB03E2074A0610021708900C08 -037A0639039906EE02D3015009D207190C50005403BF06820474006AFFFFFFFF0C51005503C006830475006BFFFFFFFF -0759058F055C0AC60B2F0B2E062609E0FFFF0708FFFFFFFFFFFFFFFFFFFFFFFF0BAD016D043607030469061D09D706CB -0343003203FF064D0AA2002A09CC0C0505AB009708EC071E0A540BA604650914023305480ABB07D9026A01F40784051A -0B6902C508A40A4105D70419075D040101350BFFFFFF03D20011FFFF06FE09A7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -0561FFFFFFFFFFFF0338FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0C62 -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -07630762050E050D07ED07EC0C530C52088B088A04B304B20A2D0A2C0280027F05C705C609A909A80995099403610360 -02F302F209B509B404990498040D040C0BC90BC808410840052605250AD10AD001C501C4062306220260025F07A0079F -00E500E408CA08C9006900680675067400150014066D066C02C702C606FC06FB01770176072407230608060708190818 -0B1A0B1904D404D3022502240C250C24022B022A0417041609EA09E9090C090B0385038409A109A0096F096E03AE03AD -091609150A080A0704110410022102200C070C0601CF01CE04C104C00AF60AF5085E085D061B061A089C089B01670166 -06BE06BD02D202D10692069100510050067D067C0093009209380937058C058B07AC07AB022302220508050701EA01E9 -0B100B0F047B047A084D084C00DF0C5C05BA03FA0917049EFFFFFFFFFFFF0BA303310330098709860A740A7303D603D5 -027402730B9F0B9E046B046A086608650AD90AD8082B082A050A050907930792023702360593059200A300A207740773 -00AD00AC065506540057005606B406B3013B013A0BBF0BBE03270326057E057D012D012C07E907E804D804D7082F082E -0B270B260430042F026E026D0C490C4803F203F10477047609710970099709960390038F09C309C20A3D0A3C05990598 -093A09390B310B30048D048C01B301B20B000AFF01EE01ED074F074E00FD00FC03DC06640AB2003909F30C0A05A20102 -03DD06650AB3003A09F40C0B05A301030AE807E4024901B607680513FFFFFFFF0AE907E5024A01B707690514FFFFFFFF -065C03CF001A032C06E80988018B0901065D03D0001B032D06E90989018C09020AEC030C019B0B8704ED01BC01FC0807 -0AED030D019C0B8804EE01BD01FD0808037E007D0386063E095C000CFFFFFFFF037F007E0387063F095D000DFFFFFFFF -020200850B1508ED051B0BEC07E6072B0203FFFF0B16FFFF051CFFFF07E7FFFF02EC0A3A06B70478002D03E606400C5A -02ED0A3B06B80479002E03E706410C5B031404820A8202FC02260B2A045A0627000A09820B0D0B6A0678034CFFFFFFFF -0AC70BE308560B7605F20794099101400AC80BE408570B7705F307950992014103EC07500AA3063409C80BDA06090219 -03ED07510AA4063509C90BDB060A021A0C450A900B1F0B9507B5094701F7054F0C460A910B200B9607B6094801F80550 -06CC03A10A23048EFFFF0336003F0C4306CD03A203150483FFFF0A24FFFF0A1CFFFFFFFF0AF2084EFFFF0A9F051F0581 -0A8302FD02270B2BFFFF0AF3FFFFFFFF038D00C50884091EFFFFFFFF096C064E038E00C6045B0628FFFFFFFFFFFFFFFF -01DC018F0A7A08E902BE0C1604E707F401DD01900B0E0B6BFFFF02BFFFFFFFFFFFFFFFFF06FF0A6BFFFF05D206D8091C -000B09830679034DFFFF0700FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF076C -FFFFFFFF0A660BF6FFFFFFFFFFFFFFFFFFFFFFFFFFFF09F7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF09F8FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0B32082C049604D5019107DE0AF70A860208057F0779031800EB0BB008B90488 -0B33082D049704D6019207DF0AF80A8702090580077A031900EC0BB108BA0489FFFFFFFF0905FFFFFFFF0906FFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF05E803AB00B00BEE07BC08A505750437092E0A77 -0B4B022C083004C801A602410B59078E02C00115056308F10A210BE609E706BF05E903AC00B10BEF07BD08A605760438 -092F0A780B4C022D083104C901A702420B5A078F02C10116056408F20A220BE709E806C0FFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF06B90402011F0BB2071F08B70554047E -08FD0B050B6C01E107CA04DD024302280C2A077B029705830447089D0A2E00B209E1077505F900330A01069909740045 -03D306D20907016A0BD206EC041E062C043D08500B5D012A030A0501058501D406BA040301200BB3072008B80555047F -08FE0B060B6D01E207CB04DE024402290C2B077C029805840448089E0A2F00B309E2077605FA00340A02069A09750046 -03D406D30908016B0BD306ED041F062D043E08510B5E012B030B0502058601D5003E003D0339069C0ABF00360A840839 -03AF0A85028503B00BE90286043C05F8FFFF023B0ACD0ACC07C4FFFFFFFF07C5FFFFFFFFFFFFFFFFFFFFFFFF02AE0A04 -02C902C8036703660969096809B309B203A403A302DF02DE0A3F0A3E04AF04AE088D088C0C210C2007F107F004F504F4 -0B1C0B1B0205020405BF05BE00EE00ED079E079D006F006E0940093F0024002306890688006300620BE20BE102840283 -072E072D01A901A8083B083A0615061407C707C60AEF0AEE052A052902A0029F0BC70BC603DF03DE04B104B009FE09FD -097309720353035209D509D40A6A0A6905CD05CC08BE08BD0B670B66045D045C023502340AC30AC201BF01BE077E077D -0122012108AC08ABFFFFFFFFFFFFFFFFFFFFFFFF038BFFFF0354038CFFFF0355FFFFFFFF07410740FFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF08AE0A61055704230767020D01CB0C5F04C7026201D104DA0B580B5604AD090E -088705B90BBD070C03C60108046D06A809AB0301096106F80369016909B906D50A980C1B040908DC0309011C0B62FFFF -FFFFFFFFFFFFFFFF0B0CFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -01F201F10AD30AD20B520B510817081601570156070A070902E102E0053E053D0042004106A206A10617061606AA06A9 -0080007F07B207B1004400430C370C360532053107FD07FC0B940B930927092602EF02EE0B860B8501940193FFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF055E055D02BB02BA09F009EF0AB50AB4 -082908280B3C0B3B0AFE0AFD01A301A207C107C000B900B809210920057A05790647064600380037FFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF000900080942094100D000CF -0761076000F700F6066F066E00710070FFFFFFFF0171017008C008BF0C150C14054C054B0A700A6F0ADF0ADE02D602D5 -0910090F0A060A050A290A2803C803C70C290C2808E208E1033D033C035D035C022F022E060005FF044C044B0A350A34 -0568056708FA08F9025C025B0ADB0ADA0114011306CF06CE0BD70BD607120711016F016E06C206C10311031007050704 -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF081CFFFF051D081D0562051E0C3D0C3C05D505D40A630A620B900B8F08D208D1 -FFFFFFFF0058FFFF0C420059FFFFFFFF0BD50BD4026C026BFFFF082603E503E4085A085904E004DF043404330A8F0A8E -05590558029A0299089A0899013301320778077709E600C00A330BF8FFFF010A038107800C2C009D0645064400A900A8 -0756075501550154059D059C02B002AF088F088E047104700560082707D20C63018D07D3FFFF018EFFFFFFFFFFFFFFFF -031F031EFFFFFFFFFFFFFFFF033B033A0BB70BB6FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0A99FFFFFFFF0A9AFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0C2DFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -046F062F039200CE08A2073009D9007703A6093C0AAD0BF3095906610363008D02140B250B1202F509FC072605AD014F -0C4B085C084305C5052804CD07D50A9C030F048B06AE03AA015908DE070E0A49004B049B093109BB004D0C6D0694033F -02520A5A0C330A8D0095029207B005910ABD0BA107F7078802010574050009540AAF02AC03E3074B0611021808910C09 -037B063A039A06EF02D4015109D3071A08C10BCA05FB01480B630846FFFF0B64FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFF09F5FFFF04B4069D035A0016FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0A6DFFFF02870BB80601015A09F106A307C205150C6607EA083C01520439053F -059A0279031604260B3F0B6E01B0081E065E0964FFFF0490FFFFFFFFFFFFFFFF0A6EFFFF02880BB90602015B09F206A4 -07C305160C6707EB083D0153043A0540059B027A031704270B400B6F01B1081F065F0965FFFF0491FFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF07C80B1D061807D6086D01460A570B700BC009FF03040A46052B0541015C0854 -04BA00F8096A069502F60052039D0797044302F8027D05960C56015E03B106D60B890B3404BC0805024F04C40C1C07FA -07C90B1E061907D7086E01470A580B710BC10A0003050A47052C0542015D085504BB00F9096B069602F70053039E0798 -044402F9027E05970C57015F03B206D70B8A0B3504BD0806025004C50C1D07FBFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0B980B0107B80869025705C00BCC0A42 -0A71024B080E0C1004E3026F07DA04C2061E035E0715037001D80C00073802A402A90A5506B10420002B0312066A0978 -0AE007CC094A0AE2FFFFFFFFFFFFFFFF0B990B0207B9086A025805C10BCD0A430A72024C080F0C1104E4027007DB04C3 -061F035F0716037101D90C01073902A502AA0A5606B20421002C0313066B09790AE107CD094B0AE3FFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -04A00924028B00AE0BAE07170271012309EB0C0CFFFF00C1099C064209A200C30848089201600C5805AE08C7032E041A -045E05DCFFFF02CD07DC0ADC052D04B806AF0AE4FFFF03E8074202F00925FFFF00AF04A10718028C01240BAF0C0D0272 -00C209EC0643FFFF00C4099D089309A30C59084908C80161041B05AF05DD032F02CE045F0ADDFFFF04B907DD0AE5052E -03E906B002F1FFFFFFFF0743FFFFFFFF04CA0517016201AA08630C6405C201FE04240706048006200B43086B03320B03 -0C6009B600D109B007A1036E00D9092A08F704A401110A1107A7093200830397056D021E036C05050B9C0B2804A2081A -08CB0A09058904AA075A025D01D600020A930322FFFF06D0FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -04CB0518016301AB08640C6505C301FF04250707048106210B44086C03330B040C6109B700D209B107A2036F00DA092B -08F804A501120A1207A8093300840398056E021F036D05060B9D0B2904A3081B08CC0A0A058A04AB075B025E01D70003 -0A940323FFFF06D1FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -01F505CE0C68082001DA01C2052F04D1039F00E908CF07A90A2600FE049C08D501780BDD06F5090302FE037806700980 -0B22039508D30B7405A404920727040401F605CF0C69082101DB01C3053004D203A000EA08D007AA0A2700FF049D08D6 -01790BDE06F6090402FF0379067109810B23039608D40B7505A5049307280405FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -0A4A008E034A0799044D027B0449065209BC007205B0074C037C00120AB8067604EF04B601AE0836075E0334020A0B5F -093D053B0BB4073A0A950B09054D08320A4B008F034B079A044E027C044A065309BD007305B1074D037D00130AB90677 -04F004B701AF0837075F0335020B0B60093E053C0BB5073B0A960B0A054E0833043106BB094C005E097A064804720027 -03A705EA000000C9089F07BA055205870006085207CE01B4048604E90B4F036A02930BC2059401870A44071B0A7E0C6E -0018043F043206BC094D005F097B06490473002803A805EB000100CA08A007BB055305880007085307CF01B5048704EA -0B50036B02940BC3059501880A45071C0A7F0C6F00190440FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF", -); -//5600 bytes -enum alphaTrieEntries = TrieEntry!(bool, 8, 5, 8)(x" -0000000000000040000001A0", -x" -00000100000002C000007B00", -x" -03020100060504030A0908070E0D0C0B0303030311100F03141413121414141414141414141414141414141414141414 -141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414 -141414141414141414141414141414141414141414141414141414141414141414141414141414140000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000001000000030002000500040007000600090008000B000A000D000C000F000E -000100100012001100010013001500140017001600190018001B001A001C0001001E001D001F001F001F0020001F001F -001F001F001F001F00220021001F002300250024001F001F000100010001000100010001000100010001000100010001 -000100010001000100010001000100010001000100010001002600010001000100010001000100010001000100010001 -000100010001000100010001000100010001000100010001000100010001000100010001000100010001000100010001 -000100010001000100010001000100010001000100010001000100010001000100010001000100010001002700290028 -002B002A002D002C00010001000100010001000100010001000100010001000100010001000100010001000100010001 -0001000100010001000100010001000100010001000100010001000100010001000100010001000100010001002E0001 -001F001F001F001F001F001F001F001F001F001F001F001F001F001F001F001F001F001F001F001F001F001F001F001F -001F001F001F001F001F001F001F001F0001001F0030002F0031000100330032003500340037003600390038003A0001 -003C003B003E003D0040003F00420041004400430046004500480047004A0049004C004B001F004D004F004E00510050 -000100010052000100540053001F001F001F001F001F001F001F001F0055001F0001000100010001001F0056001F001F -001F001F001F001F001F001F001F001F001F001F001F001F00010001001F0057001F001F001F001F001F001F001F001F -001F001F001F001F001F001F001F001F001F001F001F001F001F001F001F001F001F001F001F001F001F001F001F001F -0001000100590058001F001F005B005A0001000100010001000100010001000100010001000100010001000100010001 -000100010001000100010001005C00010001000100010001005E005D001F001F001F001F001F001F001F001F001F001F -001F001F001F001F001F001F001F001F001F001F001F001F001F001F001F001F001F001F001F001F001F001F005F001F -00600001001F0061001F001F001F001F001F001F001F001F001F0062001F001F001F001F001F001F001F001F001F001F -001F001F001F001F001F001F001F001F001F001F001F001F0064006300660065001F001F001F001F001F001F0067001F -00690068001F006A001F006B006C001F006E006D001F001F001F001F001F006F0070001F001F001F001F001F001F001F -001F001F001F001F001F001F001F001F0001000100010001000100010001007100010001000100010001000100010001 -000100010001000100010001007200010001007300010001000100010001000100010001000100010001000100010001 -000100010001000100010001000100740001000100010001000100010001000100010001000100010001000100010001 -00010001000100010001000100010001000100010075000100010001001F0076001F001F001F001F001F001F001F001F -00010001001F0077001F001F001F001F0001000100010001000100010001000100010001000100010001000100010001 -00010001007800010001000100010001000100010001000100010001000100010001000100790001001F001F001F001F -001F001F001F001F001F001F001F001F001F001F001F001F001F001F001F001F001F001F001F001F001F001F001F001F -001F001F001F001F001F001F001F001F001F001F001F001F001F001F001F001F001F001F001F001F001F001F001F001F -001F001F001F001F001F001F001F001F001F001F001F001F001F001F001F001F001F001F001F001F001F001F001F001F -001F001F001F001F001F001F001F001F001F001F001F001F001F001F001F001F000000000000000007FFFFFE07FFFFFE -0000000004200400FF7FFFFFFF7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0003FFC30000501F000000000000000000000020BCDF0000 -FFFFD740FFFFFFFBFFFFFFFFFFBFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFC03FFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFEFFFF027FFFFFFFFFFFFF000001FFBFFF0000FFFF00B6000787FF07FF0000FFFFFFFFFEFFFFFFFFFFC000 -FFFFFFFFFFFFFFFF1FEFFFFF9C00E1FEFFFF0000FFFFFFFFFFFFE000FFFFFFFFFFFFFFFF0003FFFFFFFFFC00043007FF -FCFFFFFF00001FFF01FFFFFFFFFF07FF00007EFFFFFFFFFFFFF003FFFFFF03F8FFFFFFFFEFFFFFFFFFE1DFFFFFFE000F -FFF99FEFE3C5FDFFB080599F1003000FFFF987EEC36DFDFF5E021987003F0000FFFBBFEEE3EDFDFF00011BBF1E00000F -FFF99FEEE3EDFDFFB0C0199F0002000FD63DC7ECC3FFC71800811DC700000000FFFDDFFFE3FFFDFF27601DDF0000000F -FFFDDFEFE3EFFDFF60601DDF000E000FFFFDDFFFE7FFFFFF80F05DDFFC00000FFC7FFFEE2FFBFFFFFF5F807F000C0000 -FFFFFFFE07FFFFFF0000207F00000000FFFFF7D63BFFFFAFF000205F000000000000000100000000FFFFFEFFFFFE1FFF -FEFFFF0F1FFFFFFF0000000000000000FFFFFFFFF97FFFFFFFFF0000FFFFFFFF3C00FFFFFFFFFFFFFFFF20BFF7FFFFFF -FFFFFFFFFFFFFFFF3D7F3DFFFFFFFFFFFFFF3DFF7F3DFFFFFF7FFF3DFFFFFFFFFF3DFFFFFFFFFFFF07FFFFFF00000000 -0000FFFFFFFFFFFFFFFFFFFF3F3FFFFFFFFFFFFEFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFF9FFF07FFFFFEFFFFFFFFFFFFFFFF01FFC7FF800FFFFF000FFFFF000FFFFF000DDFFF -FFFFFFFFFFCFFFFF108001FF0000000000000000FFFFFFFFFFFFFFFF01FFFFFFFFFFFFFFFFFF07FFFFFFFFFF003FFFFF -7FFFFFFF01FF0FFFFFFF0000001F3FFFFFFFFFFFFFFF0FFF000003FF000000000FFFFFFFFFFFFFFF7FFFFFFF001FFFFE -00000000800000800000700100000000FFFFFFFFFFEFFFFF00001FEF00000000FFFFFFFFFC00F3FFFFFFFFFF0003FFBF -FFFFFFFF007FFFFFFC00E0003FFFFFFFFFFF01FFE7FFFFFF00000000046FDE00FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFF00000000001FFF803F3FFFFFFFFFFFFFAAFF3F3F3FFFFFFFFFFFFFFF5FDFFFFF0FCF1FDC1FDC1FFF -000000000000000000000000800200001FFF00000000000000000000000000003E2FFC84F3FFBD50000043E0FFFFFFFF -000001FF0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000FFC00000FFFFFFFF000003FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFF000C781FFFFFFFFFFFFF20BFFFFFFFFF000080FF007FFFFF7F7F7F7F7F7F7F7FFFFFFFFF -0000000000008000000000000000000000000000000000000000000000000000000000E01F3E03FEFFFFFFFEFFFFFFFF -E07FFFFFFFFFFFFEFFFFFFFFF7FFFFFFFFFFFFE0FFFEFFFFFFFFFFFFFFFFFFFF00007FFFFFFFFFFF00000000FFFF0000 -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0000000000000000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -00001FFF00000000FFFF00003FFFFFFFFFFF1FFF00000C00FFFFFFFF8FF07FFFFFFFFFFFFFFFFFFFFFFFFFFF0000FFFF -FF800000FFFFFFFCFFFFFFFFFFFFFFFFFFFFF9FFFFFFFFFF03EB07FFFFFC0000FFFFFFBF000000FFFFFFFFFF000FFFFF -FFFFFFFFFFFFFFFF0000002FE8FC0000FFFFFC00FFFF07FF0007FFFF1FFFFFFFFFFFFFFFFFF7FFFF000080007C00FFFF -FFFFFFFF007FFFFF00003FFFFC7FFFFFFFFFFFFF7FFFFFFF38000005003CFFFF007E7E7EFFFF7F7FF7FFFFFFFFFF03FF -FFFFFFFFFFFFFFFFFFFFFFFF000007FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF000FFFFFF87F0FFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFF3FFFFFFFFFFFFFFFFFFF03FFFFFF00000000E0F8007F5F7FFDFFFFFFFFDBFFFFFFFF -FFFFFFFF0003FFFFFFF80000FFFFFFFFFFFFFFFF3FFFFFFFFFFF0000FFFFFFFFFFFCFFFFFFFFFFFF000000FF0FFF0000 -000000000000000000000000FFDF0000FFFFFFFFFFFFFFFFFFFFFFFF1FFFFFFF0000000007FFFFFE07FFFFFEFFFFFFC0 -FFFFFFFF7FFFFFFF1CFCFCFC00000000FFFFEFFFB7FFFF7F3FFF3FFF00000000FFFFFFFFFFFFFFFFFFFFFFFF07FFFFFF -0000000000000000FFFFFFFF001FFFFF0000000000000000000000000000000000000000000000000000000000000000 -1FFFFFFFFFFFFFFF0001FFFF00000000FFFFFFFFFFFFE000FFFF07FF07FFFFFF3FFFFFFFFFFFFFFF003EFF0F00000000 -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF3FFFFFFFFFFF0000FF0FFFFF0FFFFFFFFFFFFFFFFFFF00FFFFFFFFFFF7FF000F -FFB7F7FF1BFBFFFB0000000000000000FFFFFFFF007FFFFF003FFFFF000000FFFFFFFFBF07FDFFFF0000000000000000 -FFFFFD3F91BFFFFF003FFFFF007FFFFF7FFFFFFF00000000000000000037FFFF003FFFFF03FFFFFF0000000000000000 -FFFFFFFFC0FFFFFF0000000000000000FEEFF06F003FFFFF000000001FFFFFFF1FFFFFFF00000000FFFFFEFF0000001F -FFFFFFFF003FFFFF003FFFFF0007FFFF0003FFFF000000000000000000000000FFFFFFFFFFFFFFFF000001FF00000000 -FFFFFFFF0007FFFFFFFFFFFF0007FFFFFFFFFFFF000000FF000000000000000000000000000000000000000000000000 -00000000000000000000000000000000FFFFFFFF00031BFF00000000000000001FFFFFFFFFFF00800000003FFFFF0000 -00000003FFFF00000000001F007FFFFFFFFFFFFFFFFFFFFF0000003F003E0000FFFFFFFF01FFFFFFFFFF0004000001FF -FFFFFFFF0007FFFFFFFF00F00047FFFFFFFFFFFFFFFFFFFF1400C01E00000000FFFBFFFFC09FFFFF0000000300000000 -BFFFBD7FFFFF01FFFFFFFFFF000001FFFFF99FEFE3EDFDFFE081199F0000000F00000000000000000000000000000000 -FFFFFFFFFFFFFFFF800007BB00000003FFFFFFFFFFFFFFFF000000B30000000000000000000000000000000000000000 -FFFFFFFF7F3FFFFF3F00000000000000FFFFFFFF7FFFFFFF0000001100000000FFFFFFFF013FFFFF0000000000000000 -E7FFFFFF000007FF0000007F0000000000000000000000000000000000000000FFFFFFFF01FFFFFF0000000000000000 -00000000FFFFFFFFFFFFFFFF80000000FF6FF27F99BFFFFF000000070000000000000000FFFFFCFFFCFFFFFF0000001A -FFFFFFFF7FE7FFFFFFFF0000FFFFFFFF20FFFFFFFFFF0000FFFFFFFF01FFFFFFFFFFFDFF7F7FFFFF00000001FFFC0000 -FFFCFFFF007FFEFF0000000000000000FFFFFB7FB47FFFFF000000CBFFFFFDBF017B7FFF000000000000000000000000 -00000000000000000000000000000000000000000000000000000000007FFFFFFFFDFFFFC7FFFFFF0000000100000000 -00000000000100000000000000000000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF03FFFFFF000000000000000000000000 -FFFFFFFFFFFFFFFFFFFFFFFF00007FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0000000F00000000 -0000000000000000000000000000000000000000000000000000000000000000FFFF0000FFFFFFFFFFFFFFFF0001FFFF -FFFFFFFF0000FFFF0000007E0000000000000000000000000000000000000000FFFFFFFFFFFFFFFF0000007F00000000 -00000000000000000000000000000000FFFFFFFF01FFFFFF7FFFFFFFFFFF0000FFFFFFFF7FFFFFFFFFFF000000003FFF -FFFFFFFF0000FFFF0000000FE0FFFFF80000FFFF0000000000000000000000000000000000000000FFFFFFFFFFFFFFFF -00000000000000000000000000000000FFFFFFFFFFFFFFFFFFFF87FFFFFFFFFFFFFF80FF00000000000000000003000B -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFF003FFFFF00000000000001FF00000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000006FEF0000FFFFFFFF0004000700270000FFFF00F0 -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0FFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFF1FFF07FF43FF01FF000000000000000000000000FFFFFFFFFFFFFFFFFFDFFFFFFFFFFFFF -DFFFFFFFEBFFDE64FFFFFFEFFFFFFFFFDFDFE7BF7BFFFFFFFFFDFC5FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF3FF7FFFFFDF7FFFFFFFFDFFFFFFFDFFFFFFFFF7FFFFFFF7FFF -FFFFFDFFFFFFFDFF00000FF7000000007FFFFFFF000007E0000000000000000000000000000000000000000000000000 -F9FFFF7FFFFF07DBFFFFFFFF00003FFF00008000000000000000000000000000FFFFFFFF3F801FFF0000400000000000 -0000000000000000000000000000000000000000000000000000000000000000FFFF000000003FFFFFFFFFFF00000FFF -000000000000000000000000000000000000000000000000FFFF000000000FFF00000000000000000000000000000000 -0000000000000000000000007FFF6F7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0000001F00000000 -FFFFFFFFFFFFFFFF0000088F0000000000000000000000000000000000000000FFFFFFEF0AF7FE96AA96EA845EF7F796 -0FFFFBFF0FFFFBEE000000000000000000000000FFFF0000FFFF03FFFFFF03FF000003FF000000000000000000000000 -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00000000FFFFFFFF03FFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF3FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0003FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFF0001FFFFFFFFFFFFFFFF3FFFFFFF0000000000000000000000000000000000000000 -3FFFFFFF00000000000000000000000000000000000000000000000000000000FFFFFFFFFFFFFFFFFFFF07FFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0000FFFF0000000000000000 -0000000000000000000000000000000000000000000000000000000000000000", -); -//3392 bytes -enum markTrieEntries = TrieEntry!(bool, 8, 5, 8)(x" -000000000000004000000110", -x" -00000100000001A000004800", -x" -0202010004020302070206050A0908020202020202020202020202020202020202020202020202020202020202020202 -020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202 -020202020202020202020202020202020202020B02020202020202020202020202020202020202020000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -00000000000000000000000000000000000000000001000000030002000500040007000600090008000B000A000D000C -0000000E000F000000000000001000000012001100140013001600150000000000000017000000000000000000000000 -000000000000000000190018000000000000001A00000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000001B001D001C001F001E0000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000002000000000000000000021 -0022000000240023000000000000000000000000000000250026000000280027002A0029002C002B002E002D0030002F -003200310000003300350034003700360000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000038000000000000000000000000000000000000000000000000000000000000000000000000 -00000000003A003900000000003B00000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -00000000000000000000003C0000000000000000000000000000000000000000000000000000000000000000003D0000 -003E00000000003F000000000000000000000000000000400000000000000000003A0041000000420000004300000000 -004500440000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -004600000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -00000000000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFF0000FFFF00000000000000000000000000000000 -00000000000000000000000000000000000003F800000000000000000000000000000000000000000000000000000000 -FFFE0000BFFFFFFF000000B60000000007FF000000000000FFFFF8000001000000000000000000009FC0000000003D9F -00020000FFFF0000000007FF00000000000000000001FFC000000000200FF800FBC0000000003EEF0E00000000000000 -FF00000000000000FFFFFC00FFFFFFFB0000000FDC00000000FEFFFF0000000C0000000ED00000000080399F4000000C -0000000ED000000000023987002300000000000ED000000000003BBFFC00000C0000000ED000000000E0399F0000000C -00000004C000000000803DC7000000000000001FD000000000603DDF0000000C0000000ED000000000603DDF0008000C -0000000FD800000000803DDF0000000C0000000E00000000FF5F8400000C00000000000007F2000000007F8000000000 -000000001FF2000000007F000000000003000000C2A0000000000000FFFE0000FEFFE0DF1FFFFFFF0000004000000000 -000000007FFFF800C3C00000001E3F9D3C00BFFC0000000000000000000000000000000000000000E000000000000000 -00000000000000000000000000000000003C0000001C0000000C0000000C000000000000FFF00000200FFFFF00000000 -0000B80000000000000000000000000000000060000002000000000000000000000000000FFF0FFF0000000000000000 -000000000000000000000000000000000F800000000000007FE000009FFFFFFF00000000FFFF000000007FFF00000000 -0000001FFFF000000000001F000FF8000000000700003FFE00000000000FFFC00000000000FFFFF00000000000000000 -0000000000000000FFF70000039021FF000000000000000000000000000000000000000000000000FFFFFFFFFFFFFFFF -000000000000000000000000000000000000000000000000FFFF00000001FFFF00000000000000000000000000000000 -0000000000000000000000000003800000000000000000000000000080000000000000000000000000000000FFFFFFFF -000000000000FC000000000000000000060000000000000000000000000000000000000000000000000000003FF78000 -C000000000000000000000000003000000000844000010F8000000000000000000000003FFF000000000003F8003FFFF -0000000000003FC0000FFF80000000000000000FFFF80000000000010000002000000000007FFE000000300838000000 -00000000C19D0000000000020060F80000000000000000000000000000000000000000000000000000000000000037F8 -40000000000000000000000000000000000000000000000000000000000000000000FFFF0000FFFF0000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000020000000 -000000000000000000000000000000000000000000000000000000000000000100000000000000000000000007C00000 -000000000000000000000000000000000000F06E87000000000000000000000000000000000000000000000000000060 -00000000000000F000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000180000000000E000000000000000000000000001FFC0000000000000003C000000000000000000000000 -00000007FF0000000000007F801900000000000707FF0000000000040000000000000007001FFF800000006000080000 -00000007FFF800000000DE01000000000000000040FFF0000000000200000000000000000000000080000000000007FF -0000000FD80000000080399F001F1FCC0000000000000000000000000000000000000000FFE000004000007F00000000 -00000000FFFF00000000000F000000000000000000000000000000000000000000000000FF3F80003000000100000000 -00000000FFFF000000000001000000000000000000FFF8000000000000000000E000000000000FFF0000000000000000 -000000000000000000000000000000000000000007FFF000000000000000000000000000000000000000000000000000 -0000000079BF00000000000D000000000000000000000000FCFE000000000011000007FE7BF800000FFE008000000000 -03FFFC0000000000000000000000000000000000FF7F80000000000000000000FFFC0000007FFEFF0000000000000000 -00000000B47E0000000000BF0000000000FB7C0000000000000000000000000000000000000000000000000000000000 -000000000000000000000000007800000000000BC7F00000000000070000000000000000000000000000000000000000 -0000000000000000003FFF81000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000001F000000000000007F0000000000000000000000000000000000000000000000000000 -0000000000000000FFFE8000FFFFFFFF000780FF00000000000000000003001000000000000000000000000000000000 -60000000000000000000000000000000FFFFFFFFFFFF3FFF0000007F0000000000000000000000000000000000000000 -000000000000000000000000F807E3E000000FE700003C00000000000000000000000000000000000000001C00000000 -00000000000000000000000000000000FFFFFFFFF87FFFFFFFFFFFFF00201FFFF80000100000FFFE0000000000000000 -F9FFFF7F000007DB00000000000000000000800000000000000000000000000000000000000000000000000000000000 -0000000000004000000000000000F000000000000000000000000000000000000000000000000000000000000000F000 -000000000000000000000000000000000000000000000000007F0000000000000000000000000000000007F000000000 -00000000000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0000FFFF -0000000000000000000000000000000000000000000000000000000000000000", -); -//2848 bytes -enum numberTrieEntries = TrieEntry!(bool, 8, 6, 7)(x" -0000000000000040000001A0", -x" -00000100000002C000002500", -x" -020201000402030207020605090802020202020202020202020202020202020202020202020202020202020202020202 -020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202 -020202020202020202020202020202020202020202020202020202020202020202020202020202020000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000001000000020002000200020002000200020002000200020004000300050002 -000200020007000600060006000900080006000A0006000B000C000C0002000D000E000500020002000200020002000F -000200020002000200100002001100020002000E00130012001400020000000C00020015000200020002000200020002 -00170016001900180002000200020002001B001A0002000200020002001D001C00020002000200020002000200020002 -001E00020002000200020002000200020002001F00200002002200210002000200020002000200020002000200020002 -000200020002000200020002000200020002000200020002000200020002000200020002000200020002000200020002 -000200020002000200020002000200020002000200020002000200020002000200020002000200020002000200020002 -000200020002000200020002000200020002000200020002000200020002000200020002000200020002000200020002 -000200020002000200020002000200020002000200020002000200020002000200020002000200020006002300020002 -000C0024002500170002000C000400020002000200020002000200020002000200020002000200020002000200020002 -000200020002000200020002000200020002000200020002000200020002000200020002000200020002000200020002 -000200020002000200020002000200020002000200020002000200020002000200020002000200020002000200020002 -000200020002000200020002000200020002000200020002000200020002000200020002000200020002000200020002 -000200020002000200020002000200020002000200020002000200020002000E000200020027002600280002002A0029 -00230002000200020002000200020002002C002B002E002D0030002F0032003100330002000200000002003400360035 -00040037003900380004000200020002000C000C000200020005000C0002003A003B00020002000C0002000200020002 -0002003C0023000C00020002003D000C000200020002000200020002000200020002003E000200020002000200020002 -000200020002000200020002000200020002000200020002000200020002000200020002000200020002000200020002 -000200020002000200020002000200020002000200020002000200020002000200020002000200020002000200020002 -00020002000200020002000200020002000200020002000200020002000200020002000200020002000500030002003F -000200020002000200400002000200020002000200020002000200020002000200020002000200020002000200020002 -000200020002000200020002000200020002000200020002000200020002000200020002000200020002000200020002 -000200020002000200020002000200020002000200020002000200020002000200020002000200020002000200020002 -000200020002000200410002000200420002000200020002000200020043000200020002000200020002000200020002 -000200020002000200020002000200020002000200020005000400020002000200040002000200020002000200020002 -004400020002000C00020002000200020046004500020047000200020002000200020002000200480002000200020002 -000200020002000200020002000200020002000200020002000200020004000200020002000200020002000200020002 -000200020002000200020002000200020002000200020002000200020002000200020002000200020002000200020002 -000200020002000200020002000200020002000200020002000200020002000200020002000200020002000200020002 -00020002000200020002000200020002000200020002000200020002000200020000000003FF00000000000000000000 -00000000720C0000000000000000000000000000000000000000000000000000000000000000000000000000000003FF -00000000000000000000000003FF00000000000000000000000003FF000000000000000000000000000000000000FFC0 -00000000000000000000000003F0FFC000000000000000000000000000FCFFC00000000000000000000000000007FFC0 -0000000000000000000000007F00FFC000000000000000007F00000001FFFFC0000000000000000003FF000000000000 -00000000000FFFFF000000000000000003FF00000000000000000000000000000000000000000000000000001FFFFE00 -0000000000000000000000000001C00000000000000000000000000003FF03FF00000000000000000000FFC000000000 -000000000000000007FF00000000000003FF03FF000000000000000000000000000000000000000003FF03FF00000000 -00000000000000000000000003F10000000003FF0000000000000000000000000000000000000000FFFF0000FFFFFFFF -000003E7000000000000000000000000000000000000000000000000FFFFFFFF0FFFFFFF0000000000000000FFFFFC00 -000000000000000000000000FFC00000000FFFFF00000000000000000000000000000000000000000000000020000000 -00000080070003FE0000000000000000003C000000000000000000000000000000000000000003FFFFFEFF0000000000 -000003FFFFFE0000000000000000000000000000000003FF000000000000000000000000003F00000000000000000000 -000000000000000003FF000003FF0000FFFFFF80000FFFFFFFFFFFFF01FFFFFF00000C00000000000000000000000000 -0000000000000000000000000FFFFFFE000000000000000F00000402000000000000000000000000003E000000000000 -0000000000000000FF000000FE000000000000000000FF8000000000F80000000FC00000000000000000000000000000 -0000000030000000FFFCFFFFFFFFFFFF0000000000000000000001FF60000000E000000000000000000000000000F800 -0000000000000000FF000000FF000000000000000000FE000000000000000000000000000000000000000000FC000000 -0000000000000000000000007FFFFFFFE00000000000007F001E000000000000000000000000000000000FE000000000 -0000000000000000FFFC00000000FFFF00000000FFC000000000000000000000000000000000000003FF0000001FFFFE -000000000FFF000000000000000000000000000000000000000000000007FFFF0000000000000000FFFF000000001FFF -0000000000000000001FFFFF00000000FFFFFFFFFFFFFFFFFFFFFFFF00007FFF0000000000000000FBFF000000000003 -007FFFFF0000000000000000000000000000000000000000000FFFFF000FFFFF00000000000000000000000001FFFFFF -0000000000000000FFFFC000FFFFFFFF00000000000000000000FF8000000000000000000000000000000000FFFE0000 -FFFFFFFF001EEFFF0000000000000000FFFFFFFE3FFFBFFF000000000000000000001FFF000000000000000000000000 -00000000000000000000000003FF0000", -); -//3360 bytes -enum punctuationTrieEntries = TrieEntry!(bool, 8, 5, 8)(x" -000000000000004000000100", -x" -000001000000018000004900", -x" -0202010004020302070206050A0908020202020202020202020202020202020202020202020202020202020202020202 -020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202 -020202020202020202020202020202020202020202020202020202020202020202020202020202020000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -00000000000000000000000000000000000100000002000100030001000500040007000600010008000A0009000C000B -0001000D000E00010001000F001100100013001200150014000100160001000100010017001800010001000100190001 -001A000100010001001C001B0001001D0001001E00010001000100010001000100010001000100010001000100010001 -000100010001000100010001000100010001000100010001000100010001000100010001000100010001000100010001 -0001000100010001000100010001000100010001000100010001001F0001002000220021002400230001000100010001 -000100010001000100010001000100010001000100010001000100010001000100010001000100010001000100010001 -000100010001000100010001000100010001000100010001000100010001000100010001000100010025000100270026 -0028000100290001002A000100010001002C002B002E002D000100010030002F00320031000100330035003400370036 -00390038003B003A0001003C003E003D00010001000100010001003F0001000100010001000100010001000100400001 -000100010001000100010001000100010001000100010001000100010001000100010001000100010001000100010001 -000100010042004100010001004400430001000100010001000100010001000100010001000100010001000100010001 -000100010001000100010001000100010001000100010001000100010001000100010001000100010001000100010001 -000100010001000100010045000100010001000100010001000100010001000100010001000100010001000100010001 -000100010001000100010001000100010001000100010046000100010001000100010001000100010001000100010001 -004700010001000100010001000100010001000100010001000100010001000100010001000100010001000100010001 -000100010001000100010001000100010001000100010001000100010001000100010001000100010001000100010001 -00010001000100010001000100010001000000008C00F7EEB8000001280000000000000088C008820000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000040000000 -000000800000000000000000000000000000000000000000FC0000000000000000000600400000000000004900180000 -E8003600000000000000000000003C000000000000000000001000000000000000003FFF000000000000000000000000 -00000000000000000000000003800000000000007FFF0000400000000000000000000000000000000000000000000000 -000000000000000000000000000100300000000000000000000000002000000000000000000000000000000000400000 -000000000000000000000000000100000000000000000000000000000080000000000010000000000000000000000000 -000000000000000000000000000000000000000000000000000000000010000000000000000000000C00800000000000 -000000000000000000000000000000000017FFF03C00000000000000000000000000002000000000061F000000000000 -00000000000000000000FC000000000000000000000000000000000008000000000000000000000000000000000001FF -000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000 -000000000000000000000000000040001800000000000000000000000000380000000000006000000000000000000000 -00000000000000000770000000000000000007FF00000000000000000000000000000000000000000000000000000000 -0000000000000000000000300000000000000000000000000000000000000000C0000000000000000000000000000000 -0000000000003F7F00000000000000000000000000000000FC00000060000001000000000000000000000000F0000000 -00000000F800000000000000C00000000000000000000000000800FF00000000FFFF0000FFFF00FF7FFBFFEF60000000 -0000600000000000000000000000000000000F0000000600000000000000000000000000000000000000000000000000 -000000000000000000000000003FFF000000000000000000000000600000FFC000000000000000000000000000000000 -01FFFFF8000000000F0000003000000000000000000000000000000000000000000000000000000000000000DE000000 -0000000000000000000000000001000000000000000000000000000000000000FFFFFFFFFFFF7FFF3FFCFFFF00000000 -00000000000000000000000000000000FFF3FF0E20010000000000000000000000000000000000010000000008000000 -00000000000000000000000000000000000000000000000000000000C00000000000E000000000000000000040080000 -00000000000000000000000000FC000000000000000000000000000000F0000000000000000000000000C00017000000 -000000000000C00080000000000000000000000000000000C0003FFE000000000000000000000000F000000000000000 -0000000000000000C0000000000300000000000000000000000000000000000000000000000000000000000000000800 -00000000C000000000000000000000000000000000000000000000000000000003FF0000FFFF0000FFF7FFFF00000D0B -000000000000000000000000000000008C00F7EEB8000001A80000000000003F00000000000000000000000000000000 -000000070000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -800000000000000000010000000000000000000000000000000000000000800000000000000000000000000000000000 -000000000000000000800000000000000000000000000000000000000000000080000000800000000000000000000000 -00000000000000000000000000000000000000000000000001FF000080000000000000000000000000000000007F0000 -00000000FE00000000000000000000001E00000000000000000000000000000000000000000000000000000000000000 -00000000000020000000000000000000000000000000000003E0000000000000000003C0000000000000000000000000 -000000000000000000003F800000000000000000D8000000000000030000000000000000000000000000000F00300000 -0000000000000000E80021E000000000000000003F000000000000000000000000000000000002000000000000000000 -00000000000000002C00F800000000000000000000000000000000400000000000000000000000000000000000000000 -000000000000000000FFFFFE0000000000000000000000000000000E00001FFF00000000020000000000000000000000 -000000007000000000000000000000000000000000000000000000000000000000000000080000000000000000000000 -000000000000000000000000000000000000000000000000000000700000000000000000000000000000000000000004 -00000000800000000000007F00000000DC000000000000070000000000000000000003FF000000000000000000000000 -0000000000000000000000000000000000000000000000000000003E0003000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000180000000000000000000000000FFF800000000 -00000000000000000000000080000000000000000000000000000000001F000000000000000000000000000000000000 -00000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000C000 -00000000000000000000000000200000000000000F800000000000100000000000000000000000000000000000000000 -000000000000000000000000000000000780000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000040000000000000000000000000000000080000000000000000000000000000000 -0000000000000000000000000000000000000F800000000000000000000000000000000000000000C000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", -); -//3424 bytes -enum symbolTrieEntries = TrieEntry!(bool, 8, 5, 8)(x" -000000000000004000000100", -x" -000001000000018000004B00", -x" -0302010005030403070303060A0908030303030303030303030303030303030303030303030303030303030303030303 -030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303 -030303030303030303030303030303030303030303030303030303030303030303030303030303030000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000001000000030002000500040007000600090008000B000A000D000C000F000E -000100100011000100010001001300120014000100150001000100010016000100180017001A00190019001B001C0019 -001D0019001E00190001001F002100200023002200190024000100010001000100010001000100010001000100010001 -000100010001000100010001000100010001000100010001002500010001000100010001000100010001000100010001 -000100010001000100010001000100010001000100010001000100010001000100010001000100010001000100010001 -000100010001000100010001000100010001000100010001000100010001000100010001000100010001002600270001 -00010028002A002900010001000100010001000100010001000100010001000100010001000100010001000100010001 -000100010001000100010001000100010001000100010001000100010001000100010001000100010001000100010001 -00010001002B0001002C0001002E002D002F000100010001000100010001000100010030000100310001000100010001 -000100010001000100010001000E00010001000100010001000100010032000100010001000100010001000100010001 -000100010033000100010001000100010001000100010001000100010001000100010001000100010001000100010001 -000100010001000100010001000100010001000100010001000100010001000100010001000100010001000100010001 -000100010001000100010034000100010001000100010001000100010001000100010001000100010001000100350001 -003700360039003800010001003B003A001900190001003C0001000100010001003D00010001003E0001000100010001 -00010001000100010040003F000100410043004200190044001900190046004500190047004900480001000100010001 -000100010001000100010001000100010001000100010001000100010001000100010001000100010001000100010001 -0001000100010001000100010001000100000000700008104000000050000001000000000113D37C0080000000800000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -0000000000000000FFFC003CFFFFAFE00000000000000000000000000020000000000030000000000000000000400000 -000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000 -0000E0000000000000000000000000000000C9C000000000000000000000000000000000000000004000000060000200 -00000000000000000000000000000000000000000000000000000000C040000000000000000000000000000000000000 -00000100000000000000000000000000000000000000000000000000000000000000000000000000000000000C0C0000 -000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000010000 -00000000000000000000000007F800000000000000000000000000008000000000000000000000000000000000000000 -000000000000000000008000020000000000000000000000000000000000000000000000800000000000000000000000 -00000000000000000000000000000000FCE8000E01500000000000000000000000000000C000000001E0DFBF00000000 -00000000000000000000000000000000C000000000000000000000000000000000000000000000000000000000000000 -03FF00000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000 -000000000000000000000000000000000000000000000000080000000000000000000000000000000000000100000000 -0000000000000000C0000000FFFFFFFF0000000000000000000000001FF007FE00000000000000000000000000000000 -0000000000000000000000000000000000000000A0000000E000E0036000E0000000000000000000000400101C000000 -00001C00FFFFFFFF0000000100000000C1D0037B0C0042AF0000BC1F00000000FFFF0C00FFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0FFFFFFF9FFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0000007F000007FF00000000F0000000FFFFFFFFFFFFFFFF000003FF -FFFFFFFFFFFFFFFFFFFFFFFF000000FFFFF00000FFFFFFFFFFFFFF9FFFFF003FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FE000007FFFFFFFFF0FFFFFFCFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFCFFFFFFFBFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -00000000000000000000000000000000000000000000000000000000000007E000000000000000000003000000000000 -FBFFFFFFFFFFFFFFFFFFFFFF000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF003FFFFFFFFF0000 -000C0010C0C0000100000000000000001800000000000000000000000000000000000000000000000000000000000000 -FFC3000000000000FFFFFFFF0000800F7FFFFFFFFFFFFC00000100FFFFFFFFFFFFFFFC000001FFFFFFFFFFFFFFFFFFFF -000000000000000000000000000000000000000000000000FFFFFFFFFFFFFFFF00000000000000000000000000000000 -FFFF0000FFFFFFFF0000007F00000000007FFFFF00000003000000000000000000000600000000000000000000000000 -0000000003C00F0000000000000000000000000000000000000000000000000000000000000000000000000003800000 -0000000000000000000000000000000000000000000000000800000000000C0000000000000000000000000000000000 -0000000000000200000000000000000000000000FFFC0000000000070000000000000000000000000000FFFF00000000 -000000000000000000008000F00000000000000000000000000000000000027400000000000000000000000000000000 -7000081040000000500000010000000000000000000000000000000030007F7F00000000FF80000000000000FE000000 -1FFF73FF00000001FFFF00001FFFFFFF0000000000000000000000000180000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000 -0000000000000000FFE000000003FFFF00000000F0000000000000200000000000000000000000000000000000000000 -00000000000000000000000000000000100000000000000000000000000000000000000000000000FFFF0000FFFFFFFF -FFFFFFFFFFFFFFFF0000000F00000000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF003FFFFF -FFFFFFFFFFFFFE7FFFFFFFFF00001C1FFFFFF018FFFFC3FFFFFFFFFF000007FFFFFFFFFFFFFFFFFF0000002300000000 -00000000000000000000000000000000FFFFFFFFFFFFFFFF007FFFFF0000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000080000020800000000200000002000000000800000008000 -00000200000002000000000800000000000000000780000000000000FFDFE0000000006F000000000000000000000000 -000000000000000000008000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000800000000000000000000000000000000000000000000000000110000000000000000000 -000000000000400000000000000000000000000000000000000000000000000000000000000000000000000000000000 -00000000000000000000000000030000FFFFFFFFFFFF0FFFFFFFFFFFFFFFFFFF000FFFFFFFFE7FFFFFFEFFFE003FFFFF -FFFFE000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00003FFF00000000FFFFFFC0FFFF00070FFFFFFF000301FF0000003F -00000000000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0FFFFFF1FFF1FFF -FFFFFFFFFFFFFFFFFFFFFFFFF87FFFFFFFFFFFFFFFFFFFFF03FFFFFF00010FFFFFFF0FFFFFFFFFFF03FF00FFFFFFFFFF -FFFF00FF00033FFF0000000000000000FFFFFFFFFFFFFFFF000FFFFF1FFF3FFFFFFF01FFBFFFFFFF0FFFC03F01FF01FF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF7FFFFFFFFFFFF000007FF0000000000000000000000000000000000000000 -00000000000000000000000000000000", -); -//6080 bytes -enum graphicalTrieEntries = TrieEntry!(bool, 8, 5, 8)(x" -0000000000000040000001A0", -x" -00000100000002C000008A00", -x" -0202010005040302090807060D0C0B0A02020202100F0E02131312111313131313131313131313131313131313131313 -131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313 -131313131313131313131313131313131313131413131313131313131313131313131313131313130000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -00000000000000000000000000000000000100000002000100030001000500040007000600090008000B000A000D000C -0001000E0010000F000100010012001100140013001600150001001700180001001A0019000100010001001B00010001 -00010001001C0001001E001D0020001F0022002100010023000100010001000100010001000100010001000100010001 -000100010001000100010001000100010001000100010001000100010001000100010001000100010001000100010001 -000100010001000100010001000100010001000100010001000100240026002500280027002A00290001000100010001 -000100010001000100010001000100010001000100010001000100010001000100010001000100010001000100010001 -00010001000100010001000100010001000100010001000100010001002B0001002C002C002C002C002C002C002C002C -002C002C002C002C002C002C002C002C002C002C002C002C002C002C002C002C002C002C002C002C002C002C002C002C -0001002C002E002D002F00010031003000330032003500340037003600380001003A0039003C003B003E003D0040003F -00420041004400430046004500480047004A0049004C004B004E004D0050004F000100010051000100530052002C002C -002C002C002C002C002C002C0054002C0001000100010001002C0055002C002C002C002C002C002C002C002C002C002C -002C002C002C002C00010001002C0056002C002C002C002C002C002C002C002C002C002C002C002C002C002C002C002C -002C002C002C002C002C002C002C002C002C002C002C002C002C002C002C002C0001000100580057002C002C005A0059 -0001000100010001000100010001000100010001000100010001000100010001000100010001000100010001005B0001 -0001000100010001005D005C002C002C002C002C002C002C002C002C002C002C002C002C002C002C002C002C002C002C -002C002C002C002C002C002C002C002C002C002C002C002C002C002C005E002C005F0001002C0060002C002C002C002C -002C002C002C002C002C0061002C002C002C002C002C002C002C002C002C002C002C002C002C002C002C002C0062002C -006400630066006500680067006A006900010001002C006B002C002C006C002C006E006D002C006F002C00700071002C -00730072002C002C00750074002C0076007800770001007900010001007B007A0001007C007E007D002C002C002C002C -0001000100010001000100010001007F0001000100010001000100010001000100010001000100010001000100800001 -000100810001000100010001000100010001000100010001000100010001000100010001000100010001000100010082 -000100010001000100010001000100010001000100010001000100010001000100010001000100010001000100010001 -000100010083000100010001002C0084002C002C002C002C002C002C002C002C00010001002C0085002C002C002C002C -000100010001000100010001000100010001000100010001000100010001000100010001008600010001000100010001 -000100010001000100010001000100010001000100870001002C002C002C002C002C002C002C002C002C002C002C002C -002C002C002C002C002C002C002C002C002C002C002C002C002C002C002C002C002C002C002C002C002C002C002C002C -002C002C002C002C002C002C002C002C002C002C002C002C002C002C002C002C002C002C002C002C002C002C002C002C -0088002C002C002C002C002C002C002C002C002C002C002C002C002C002C002C002C002C002C002C002C002C002C002C -002C002C002C002C002C002C002C002C002C002C002C002C002C002C002C002C002C002C002C002C002C002C002C002C -002C002C002C002C002C002C002C002C002C002C002C002C002C002C002C002C00000000FFFFFFFFFFFFFFFF7FFFFFFF -00000000FFFFDFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFCFFFFFFFFFFD7F0FFFFFFFBFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFE7FFFFFFFFFFFFF -FFFEE7FFFFFFFFFFFFFF00FF001F87FFEFFFFFC0FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFDFFFFFFFFFFFFFFF -FFFF3FFFFFFFFFFFFFFFE7FFFFFFFFFFFFFFFFFF0003FFFFFFFFFFFFE7FFFFFFFFFFFFFF7FFF3FFF4FFFFFFFFFFF07FF -FF007FFFFFFFFFFFFFFFFFFFFFFFFFFBFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF99FEFF3C5FDFFB080799F7FFFFFCF -FFF987EED36DFDFF5E023987007FFFC0FFFBBFEEF3EDFDFF00013BBFFE03FFCFFFF99FEEF3EDFDFFB0E0399F00FFFFCF -D63DC7ECC3FFC71800813DC707FFFFC0FFFDDFFFF3FFFDFF27603DDFFF80FFCFFFFDDFFFF3EFFDFF60603DDF000EFFCF -FFFDDFFFFFFFFFFFFFF0FDDFFFFFFFCFFC7FFFEE2FFBFFFFFF5F847F001CFFC0FFFFFFFE87FFFFFF0FFFFFFF00000000 -FFFFF7D63FFFFFAFF3FF7F5F00000000FFFFFFFFFFFFFFFFFFFFFEFFFFFE1FFFFEFFFFFFDFFFFFFF07FFDFFF00000000 -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF20BFFFFFFFFFFFFFFFFFFFFFFFFF3D7F3DFFFFFFFFFF -FFFF3DFF7F3DFFFFFF7FFF3DFFFFFFFFFF3DFFFFFFFFFFFFE7FFFFFF1FFFFFFF03FFFFFFFFFFFFFFFFFFFFFF3F3FFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF1FFFFFFFFFFFFFFFFFFFFFFF01FFFFFF803FFFFF007FFFFF000FFFFF000DDFFF -FFFFFFFFFFFFFFFF3FFFFFFF03FF03FF03FFBFFFFFFFFFFFFFFFFFFF01FFFFFFFFFFFFFFFFFF07FFFFFFFFFF003FFFFF -7FFFFFFF0FFF0FFFFFFFFFF1001F3FFFFFFFFFFFFFFF0FFFC7FF03FFFFFFFFFFCFFFFFFFFFFFFFFF7FFFFFFF9FFFFFFF -03FF03FFFFFF3FFF00007FFF00000000FFFFFFFFFFFFFFFFFFFF1FFF7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00FFFFF -FFFFFFFFF8FFFFFFFFFFE3FFFFFFFFFFFFFF01FFE7FFFFFFFFFF00FF07FFFFFF3F3FFFFFFFFFFFFFAAFF3F3F3FFFFFFF -FFFFFFFFFFDFFFFFEFCFFFDF7FDCFFFFFFFF07FFFFFF80FFFFFFFFFFFFF300001FFF7FFFFFFFFFFFFFFF00010001FFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0000007F000007FFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFCFFFFFFFBFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE0FFFFFFFFFFFFFFFFF20BFFFFFFFFF800180FF -007FFFFF7F7F7F7F7F7F7F7FFFFFFFFFFFFFFFFFFFFFFFFF3FFFFFFF00000000FBFFFFFFFFFFFFFFFFFFFFFF000FFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF003FFFFFFFFF0000FFFFFFFFFFFFFFFFFFFFFFFEFFFFFFFF -FE7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE0FFFEFFFFFFFFFFFFFFFFFFFFFFFF7FFFFFFFFFFFFFFFFFFFFFFF800F -7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFF1FFFFFFFFFFFFFFF007FFFFFFFFFFFFFFFFF00000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00FFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF03EB07FFFFFC0000FFFFFFFF03FF1FFFFFFFFFFF00FFFFFF -FFFFFFFFFFFFFFFF03FFC03FFFFFFFFFFFFFFFFFFFFFFFFF800FFFFF1FFFFFFFFFFFFFFFFFFFFFFFC3FFBFFF7FFFFFFF -FFFFFFFF007FFFFFF3FF3FFFFFFFFFFFFFFFFFFFFFFFFFFFF8000007007FFFFF007E7E7EFFFF7F7FFFFFFFFFFFFF0FFF -FFFFFFFFFFFFFFFFFFFFFFFF03FF3FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF000FFFFFF87F0FFFFFFF -0000000000000000000000000000000000000000000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFFFFFF3FFF -FFFFFFFFFFFFFFFF03FFFFFF00000000E0F8007F5F7FFFFFFFFFFFDBFFFFFFFFFFFFFFFFFFFFFFFFFFF80007FFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFCFFFFFFFFFFFF000080FFFFFF000003FFFFFFFFFFFFFFFFF7FFFFFFDF0F7F -FFFFFFFFFFFFFFFFFFFFFFFF1FFFFFFFFFFFFFFEFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF7FFFFFFF1CFCFCFC30007F7F -FFFFEFFFB7FFFF7F3FFF3FFF00000000FFFFFFFFFFFFFFFFFFFFFFFF07FFFFFFFFFFFF87FF8FFFFFFFFFFFFFFFFFFFFF -1FFF7FFF00000001FFFF00003FFFFFFF000000000000000000000000000000001FFFFFFFFFFFFFFF0001FFFF0FFFFFFF -FFFFFFFFFFFFE00FFFFF07FF07FFFFFFBFFFFFFFFFFFFFFF003FFF0F00000000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -3FFFFFFFFFFF03FFFF0FFFFF0FFFFFFFFFFFFFFFFFFF00FFFFFFFFFFF7FF800FFFB7F7FF1BFBFFFB0000000000000000 -FFFFFFFF007FFFFF003FFFFF000000FFFFFFFFBF07FDFFFF0000000000000000FFFFFD3F91BFFFFFFFBFFFFFFFFFFFFF -7FFFFFFF0000FF8000000000F837FFFF8FFFFFFF83FFFFFF0000000000000000FFFFFFFFF0FFFFFFFFFCFFFFFFFFFFFF -FEEFF06F873FFFFF01FF01FFFFFFFFFFFFFFFFFF00000000FFFFFFFF007FF87FFFFFFFFFFE3FFFFFFF3FFFFFFF07FFFF -1E03FFFF0000FE000000000000000000FFFFFFFFFFFFFFFF000001FF00000000FFFFFFFF0007FFFFFFFFFFFFFC07FFFF -FFFFFFFF03FF00FF0000000000000000000000000000000000000000000000000000000000000000000000007FFFFFFF -FFFFFFFF00033BFF00000000E0000000FFFFFFFFFFFF00FF03FFFFFFFFFF0000000003FFFFFF000000000FFF007FFFFF -FFFFFFFFFFFFFFFFFFFC3FFF803FFFFFFFFFFFFFDFFFFFFFFFFF000703FF01FFFFFFFFFFFFDFFFFFFFFF00FF007FFFFF -FFFFFFFFFFFFFFFFFFFFFFFF001FFFFEFFFBFFFFFFFFFFFF0000000300000000BFFFBD7FFFFF03FFFFFFFFFF03FF07FF -FFF99FEFFBEDFDFFE081399F001F1FCF00000000000000000000000000000000FFFFFFFFFFFFFFFFEFFFFFFF00000003 -FFFFFFFFFFFFFFFF03FF00FF0000000000000000000000000000000000000000FFFFFFFFFF3FFFFF3FFFFFFF00000000 -FFFFFFFFFFFFFFFF03FF001F00001FFFFFFFFFFF03FFFFFF000003FF00000000E7FFFFFFFFFF0FFF0000007F00000000 -00000000000000000000000000000000FFFFFFFF0FFFFFFF000000000000000000000000FFFFFFFFFFFFFFFF8007FFFF -FF6FF27FF9BFFFFF03FF007F0000000000000000FFFFFCFFFCFFFFFF0000001FFFFFFFFFFFFFFFFFFFFF00FFFFFFFFFF -FFFFFFFFFFFF0007FFFFFFFF01FFFFFF000003FF00000000000000000000000000000000000000000000000000000000 -FFFFFDFFFF7FFFFFFFFF003FFFFF1FFFFFFCFFFF007FFEFF0000000000000000FFFFFB7FB47FFFFF03FF00FFFFFFFDBF -01FB7FFF000003FF00000000000000000000000000000000000000000000000000000000000000000000000001FFFFFF -FFFDFFFFC7FFFFFF03FFFFFF000000000000000000010000FFFFFFFF8003FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -03FFFFFF000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFF001F7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFF0000000F000000000000000000000000000000000000000000000000000000000000000000000000 -FFFF0000FFFFFFFFFFFFFFFF0007FFFFFFFFFFFF0000FFFF003FFFFF0000000000000000000000000000000000000000 -FFFFFFFFFFFFFFFF0000007F0000000000000000000000000000000000000000FFFFFFFF01FFFFFF7FFFFFFFFFFFC3FF -FFFFFFFF7FFFFFFFFFFF03FF003F3FFFFFFFFFFFFFFFFFFFFBFF003FE0FFFFFB0000FFFF000000000000000000000000 -0000000000000000FFFFFFFFFFFFFFFF07FFFFFF000000000000000000000000FFFFFFFFFFFFFFFFFFFF87FFFFFFFFFF -FFFF80FF00000000000000000003001FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00FFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF003FFFFF00000000000001FF000000000000000000000000 -00000000000000000000000000000000000000000000000000000000000000000000000000000000000000006FEF0000 -FFFFFFFF0004000700270000FFFF00F0FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFF0FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF1FFF07FFF3FF01FF000000000000000000000000 -FFFFFFFFFFFF3FFFFFFF007FFFFFFFFFFFFFFFFFFFFFFFFF0000000F00000000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFF003FFFFFFFFFFFFFFFFFFE7FFFFFFFFFF807FFFFFFFFFFFFFFFFFFFFFFFFFFFF000007FF -FFFFFFFFFFFFFFFF0000003F000000000000000000000000000FFFFF000FFFFFFFFFFFFFFFFFFFFF007FFFFF01FFFFFF -00000000000000000000000000000000FFFFFFFFFFFFFFFFFFDFFFFFFFFFFFFFDFFFFFFFEBFFDE64FFFFFFEFFFFFFFFF -DFDFE7BF7BFFFFFFFFFDFC5FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFF3FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFCFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF8000FFF0000FFFE00000000000000007FFFFFFF000007E00000000000000000 -00000000000000000000000000000000F9FFFF7FFFFF07DBFFFFFFFF00003FFF00008000000000000000000000000000 -FFFFFFFF3FFF1FFF0000C3FF000000000000000000000000000000000000000000000000000000000000000000000000 -FFFF000000007FFFFFFFFFFF83FFFFFF000000000000000000000000000000000000000000000000FFFF000003FFFFFF -000000000000000000000000000000000000000000000000000000007FFF6F7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFF007FFF9F00000000FFFFFFFFFFFFFFFFC3FF0FFF0000000000000000000000000000000000000000 -000000000000000000000000FFFE0000FFFFFFFF001FFFFF0000000000000000FFFFFFFE3FFFFFFF0000000000000000 -00000000000000000000000000000000FFFFFFEF0AF7FE96AA96EA845EF7F7960FFFFBFF0FFFFBEE0000000000030000 -FFFFFFFFFFFF0FFFFFFFFFFFFFFFFFFF000FFFFFFFFE7FFFFFFEFFFE003FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFF00003FFF00000000FFFFFFC0FFFF00070FFFFFFF000301FF0000003F00000000000000000000000000000000 -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0FFFFFF1FFF1FFFFFFFFFFFFFFFFFFFFFFFFFFFF87FFFFF -FFFFFFFFFFFFFFFF03FFFFFF00010FFFFFFF0FFFFFFFFFFF03FF00FFFFFFFFFFFFFF00FF00033FFF0000000000000000 -FFFFFFFFFFFFFFFF000FFFFF1FFF3FFFFFFF01FFBFFFFFFF0FFFC03F01FF01FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFF7FFFFFFFFFFFF000007FF03FF0000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00000000 -FFFFFFFF03FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF3FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0003FFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0001FFFFFFFFFFFFFFFF3FFFFFFF00000000 -000000000000000000000000000000003FFFFFFF00000000000000000000000000000000000000000000000000000000 -FFFFFFFFFFFFFFFFFFFF07FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFF0000FFFF0000000000000000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0000FFFF -0000000000000000000000000000000000000000000000000000000000000000", -); -//4824 bytes -enum nonCharacterTrieEntries = TrieEntry!(bool, 7, 4, 4, 6)(x" -00000000000000200000009800000298", -x" -00000080000000F000000400000043C0", -x" -0302010007060504090801010B0B0B0A0B0B0B0B0B0B0B0B0B0B0B0B0B0B0B0B0B0B0B0B0B0B0B0B0B0B0B0B0B0B0B0B -0B0B0B0B0B0B0B0B0B0B0B0C0D0101010D01010100000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000010000000300020005000400070006 -00090008000B000A000D000C000D000D000D000D000D000D000D000D000D000D000D000D000D000D000D000D000D000D -000D000D000D000D000D000D000D000D000E000D000D000F000D000D000D000D000D000D000D000D0010000D000D000D -000D000D000D000D000D000D00120011001400130016001500180017001A0019001C001B001E001D001F000D001D001D -0020001D001D001D001D001D001D001D001D001D00220021000D000D000D000D0023000D0024000D001D001D001D001D -001D001D0025001D001D00260027001D001D001D0028001D002A0029002C002B002E002D0030002F00320031001D0033 -000D000D000D000D000D000D000D000D0034000D000D000D0035000D000D0036000D000D0037000D000D000D000D000D -000D000D00390038001D001D001D003A000D000D000D000D000D003B000D000D001D003C001D001D001D001D001D001D -001D001D001D001D001D001D001D001D001D001D001D001D001D001D001D001D001D003D001D001D001D001D001D001D -001D001D001D001D001D001D001D001D000D000D000D000D000D000D000D000D000D000D000D000D000D000D003E000D -000D000D000D000D000D000D000D000D000D000D000D000D000D000D003E000D00000000000000000000000000000000 -0000000000000000000100000000000200000000000000000004000300060005000000000000000000080007000A0009 -000C000B0000000D00000000000F000E0011001000130012001500140017001600190018001B001A001D001C001F001E -002100200023002200240000002600250000000000270000000000000000000000280000002A0029002C002B002E002D -00000000000000000000000000000000000000000030002F00320031003300000030002D003500340037003600390038 -003B003A003D003C003E0000003F00000041004000430042000000000000000000000000000000000045004400470046 -00480000004A0049000000000000004B00000000000000000000000000000000004D004C000000000000000000000000 -00000000000000000000000000000000000000000000000000000000000000000000000000000000004E00000000004F -000000000050000000520051005400530055000000570056000000000058000000590000000000040000005A005C005B -0000005D0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -00000000005F005E00000000000000000000006000610000000000000062000000610063006400000065000000660000 -00680067006900000038006A006B000000000000000000000000000000000000000000000000000000000000006D006C -00000000000000000000000000000000006E0000006F0000007100700072000000000000000000000000000000740073 -0075002D007600000000005900780077007A0079007B00000000007C007E007D007F007F0080002F0082008100840083 -000000000086008500880087007F00890000000000000000008A0067007F008B004F008C008E008D007F008F00730090 -009200910094009300960095007F009700980000009A0099007F009B007F007F009C007F009E009D009F008700A100A0 -00A2000000A3000000A4004600A5000000A700A600A900A800AB00AA007F007F00AC000000AD0000007F007F005500AE -00AF000000B100B000B300B2007F007F007F00B400B600B500B800B700BA00B900BB0000003000BC007F00B1007F007F -00BE00BD007F00BF00C100C0007F00C2007F007F00C3007F006F00C400C600C500000000000000000000000000000000 -000000000000000000000000007F006F00C700000000000000C80000007F007F007F007F007F007F007F007F007F007F -007F007F007F007F007F007F007F007F007F007F007F007F007F007F007F007F007F007F007F007F007F007F007F007F -007F007F007F007F007F007F009900C900CA0000007F007F007F007F007F007F007F007F007F007F007F007F007F007F -0000000000000000000000000000000000B30000007F007F007F007F007F007F00000000000000000000000000000000 -00CB003000CC007700CD0000007F00CE007F007F007F007F007F007F007F007F0000007F007F00CF00D0000000D200D1 -00000000000000000000000000000000000000000000000000000000006100000000000000CA0000007F0098007F007F -007F007F007F007F007F007F007F007F007F007F007F007F007F007F007F007F007F007F007F007F007F007F00D3007F -000000000000000000D500D4000000000000000000B40000007F007F007F007F00D60000007F00D7007F007F007F007F -007F007F007F007F007F007F007F007F007F007F007F007F007F007F007F007F007F007F007F007F005F006E00C80000 -0000000000350000000000D800D9000000DA000000DB007F00DC0000007F007F00DD000000DF00DE00E100E000000000 -00000000000000E20000000000E300000000000000000000000000000000000000000000007F00E4007F007F007F007F -007F007F007F007F007F007F007F007F007F007F007F007F007F00E5007F007F00E700E6007F00E800EA00E9007F007F -007F007F00EC00EB007F007F007F007F007F007F00ED007F007F007F007F007F007F007F007F007F007F007F00EE007F -0000000000EF000000F00000007F007F007F007F007F007F007F007F007F007F00F1007F007F00F2007F00F3007F007F -00F500F400F700F6007F007F007F007F0000003800F900F80000000000FA00E700FC00FB007F007F0000000000000000 -000000000000000000000000000000000000000000FD000000FE000000FF00000100004B007F01010000000000000000 -010200000104010300000000010601050000000000000000000000000000000000000000009300000000000000000000 -000000000000000000000000000000000000000000000000000000B00000000000000107000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000001080000000000000000 -000000000000000000000000000000000000000000000000000000000109000000000000000000000000000000000000 -00550000007F007F007F007F007F007F00000000000000000000000000000000007F0055007F007F007F007F007F007F -000000000000000000000000000000000000000000000000010A00000000000000000000000000000000000000000000 -000000000000000000000000007F010B0000010C007F007F00000000010B0000007F007F007F007F007F007F007F007F -00000000000000000000000000000000000000000000000000000000010D000000000000000000000000000000000000 -000000000000000000000000010D0000000000000000000000000000030000000000280F000000040000000000010000 -018000000000000000011800000000000000FF00FFE078000000400000000000000018000000000000000000FFFC0000 -0000000018000000000000008000C000B00000000000F80000FC800000000000000660100C3A02004F7F866080000030 -000678112C920200A1FDC678FF80003F000440110C120200FFFEC44001FC0030000660110C1202004F1FC660FF000030 -29C238133C0038E7FF7EC238F800003F000220000C000200D89FC220007F0030000220000C1002009F9FC220FFF10030 -0002200000000000000F02200000003003800011D004000000A07B80FFE3003F0000000178000000F0000000FFFFFFFF -00000829C00000500C0080A0FFFFFFFF000001000001E0000100000020000000F8002000FFFFFFFF0000DF4000000000 -C280C200000000000000C20080C20000008000C20000000000C200000000000018000000E0000000FC00000000000000 -00000000C0C00000E00000000000000000000000FE0000007FC00000FF800000FFF00000FFF22000C0000000FC00FC00 -000000000000F80000000000FFC0000080000000F000F0000000000EFFE0C000000000000000F0003800FC0000000000 -30000000000000008000000060000000FC00FC000000C000FFFF8000FFFFFFFF0000E00080000000000000000FF00000 -000000000700000000001C00000000000000FE00180000000000FF00F8000000C0C00000000000005500C0C0C0000000 -0000000000200000103000208023000000000000000C0020E0008000000000000000FFFEFFFE00000000F00000000000 -00000000FFFFFF80FFFFF80000000000000000000030000000400000000000000000000001F00000000000000000DF40 -000000007FFE7F00FF800000808080808080808000000000C0000000FFFFFFFF040000000000000000000000FFF00000 -FFC000000000FFFF00000001000000000000001F0001000000008000000000000000000000007FF08000000000000000 -0000E000000000000000FF800000000000000000FFFFF00000000000FF000000FC14F8000003FFFF00000000FC00E000 -FC003FC0000000007FF00000E00000003C0040008000000000000000FF8000000C00C0000000000007FFFFF8FF800000 -FF8181810000808000000000FC00C000000000000000FFF000000780F0000000000000000000C000FC000000FFFFFFFF -1F07FF80A080000000000024000000000007FFF8000000000003000000000000FFFF7F000000FFFF000800000020F080 -00000000600000000000000080000000E3030303C1FF80800000100048000080C000C000FFFFFFFF00000000F8000000 -0000007800700000E0008000FFFFFFFE0000FFFFC0000000FFFFFFFFFFFFFFFFFFFE0000F00000000000000000001FF0 -0000F800F80000004000000000000000FFC000F0FFFFFFFFC00000000000FC0000F00000F0000000000000000000FF00 -0000000008007FF000480800E4040004FFC00000FFFFFF0000000040F8020000000002C06E40000080000000FFFF007F -FFFFFFFF07C80000700000007C000000000000000F00000001100F9078C00000FE00FE000000000000000000FFFFFFFF -00000000FF8007800000000001C0000000C0000000F80000E1FC0000FFFF01FFFFFFFE00FFFFFFFF00000000FFF80000 -0000000003F8000000000000FC00FF00FFFFFFFF8000000000000000FFFCC400FFFFFFFF1FFFFFFFFC0000000000FFFF -FFFFFC000000FFFFFFFFF000FF8000000003C0007FC000000000DFF8FC00FE000000FF00FF80000000000000FFE00001 -0004000000000000FFFFFFFCFFFFFFFF400042800000FC0000000000FC00F80000066010041202001F7EC660FFE0E030 -10000000FFFFFFFCFC00FF00FFFFFFFF0000000000C00000FC00FFE0FFFFE00000000000FC000000FFFFFC00FFFFFFFF -180000000000F000FFFFFF80FFFFFFFF00000000F0000000FFFFFFFF00000000000000007FF8000000900D8006400000 -FC00FF80FFFFFFFFFFFFFFFF0000030003000000FFFFFFE00000FF0000000000000000000000FFF80000020000800000 -0000FFC00000E00000030000FF800100000004804B800000FC00FF0000000240FE048000FFFFFC00FFFFFFFFFE000000 -0002000038000000FFFFFFFFFFFEFFFF000000007FFC000000000000FFE08000FFFFFFF0FFFFFFFF0000FFFF00000000 -FFC00000FFFFFFFF8000000000003C000000FC00FFC0C0000400FFC01F000004FFFF0000FFFFFFFFF8000000FFFFFFFF -000078000000000000007F00FFFFFFFFFFFFFFFFFFFCFFE0FFFFFFFF9010FFFF00000000FFFBFFF8FFD8FFFF0000FF0F -00000000E000F8000C00FE00FFFFFFF0000000000000018000000000FFFFF800FFFFFFC0FFFFFFFFFFF00000FFF00000 -FF800000FE0000000020000000000000200000001400219B00000010000000002020184084000000000203A000000000 -00000000000000C0000030000000000007FFF000FFFF000180000000FFFFF81F060000800000F82400000000FFFFC000 -FFFF7FFFFFFFFFFF00000000C000E000FFFF3C00FFFFFFFF0000FFFFFFFF8000000000007C0000000000FFFFFC000000 -FFFFFFFF80009080FF800060FFFFFFFF3C00F000FFFFFFFFFFFFFFFF0001FFFF00000000FFE0000000000001C0000000 -00000010F50801695569157BA1080869F0000400F0000411FFFFFFFFFFFCFFFFFFF000000001800000010001FFC00000 -FFFFFFFF0000003F0000FFF8F0000000FFFCFE00FFFFFFC00F000000E000E0000000000007800000FC000000FFFEF000 -FC00FF00000000000000FF00FFFCC000FFF00000E000C0000000FE0040000000F0003FC0FE00FE000008000000000000 -FFFFF800FC00FFFFC000000000000000000000000000FFFC000000000000FFFE0000F8000000000000000000FFFF0000 -FFFFFFFD0000000000000000C000000000000000C0000000", -); -enum MAX_SIMPLE_LOWER = 1433; -enum MAX_SIMPLE_UPPER = 1450; -enum MAX_SIMPLE_TITLE = 1454; -//10496 bytes -enum toUpperIndexTrieEntries = TrieEntry!(ushort, 8, 7, 6)(x" -000000000000004000000280", -x" -000001000000048000000F80", -x" -020201000402030206020205070202020202020202020202020202020202020202020202020202020202020202020202 -020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202 -020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202 -020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202 -020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202 -0202020202020202020202020202020200010000000300020005000400070006000900080000000A000B0000000D000C -000F000E0011001000130012000000140000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000015000000000000000000000000000000000000000000000016000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -00000000000000170018000000000019001B001A001D001C001F001E0021002000000000000000000022000000000023 -000000000000000000000000000000000000000000240000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000260025002800270000002900000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -00000000000000000000000000000000002A00000000002B002D002C002F002E00000000000000000000000000000000 -000000000000000000300000000000310000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000032000000000000000000000000000000000000000000000000000000000033000000000000 -000000000000000000000000000000000000000000000000000000000000000000350034003600000000000000000037 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000038000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000039000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000003A0000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -0000000000000000003C003B000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -00000000000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0000FFFF00020001000400030006000500080007000A0009000C000B000E000D -0010000F00120011001400130016001500180017FFFF0019FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF001AFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF05AAFFFF001C001B001E001D0020001F00220021 -002400230026002500280027002A0029002C002B002E002D0030002FFFFF003100330032003500340037003600390038 -003AFFFF003BFFFF003CFFFF003DFFFF003EFFFF003FFFFF0040FFFF0041FFFF0042FFFF0043FFFF0044FFFF0045FFFF -0046FFFF0047FFFF0048FFFF0049FFFF004AFFFF004BFFFF004CFFFF004DFFFF004EFFFF004FFFFF0050FFFF0051FFFF -005205AC0053FFFF0054FFFF0055FFFFFFFFFFFFFFFF0056FFFF0057FFFF0058FFFF0059FFFF005AFFFF005BFFFF005C -05C9005D005EFFFF005FFFFF0060FFFF0061FFFF0062FFFF0063FFFF0064FFFF0065FFFF0066FFFF0067FFFF0068FFFF -0069FFFF006AFFFF006BFFFF006CFFFF006DFFFF006EFFFF006FFFFF0070FFFF0071FFFF0072FFFF0073FFFF0074FFFF -FFFFFFFFFFFF0075FFFF007600780077FFFF0079007AFFFF007BFFFFFFFFFFFFFFFF007CFFFFFFFFFFFF007DFFFFFFFF -FFFFFFFFFFFF007E007FFFFFFFFFFFFF0080FFFFFFFF0081FFFFFFFFFFFF00820083FFFF0084FFFF0085FFFFFFFFFFFF -FFFF0086FFFFFFFF0087FFFFFFFFFFFFFFFF0088FFFFFFFFFFFF0089FFFF008A008BFFFFFFFFFFFF008CFFFF008DFFFF -FFFFFFFFFFFFFFFF008EFFFFFFFF008F009100900092FFFFFFFF0093FFFF0094FFFF0095FFFF0096FFFF0097FFFF0098 -FFFF0099FFFF009A009C009B009DFFFF009EFFFF009FFFFF00A0FFFF00A1FFFF00A2FFFF00A3FFFF00A4FFFF00A5FFFF -FFFF05D100A700A600A8FFFFFFFFFFFF00A9FFFF00AAFFFF00ABFFFF00ACFFFF00ADFFFF00AEFFFF00AFFFFF00B0FFFF -00B1FFFF00B2FFFF00B3FFFF00B4FFFF00B5FFFF00B6FFFF00B7FFFF00B8FFFF00B9FFFF00BAFFFF00BBFFFF00BCFFFF -FFFFFFFF00BDFFFF00BEFFFF00BFFFFF00C0FFFF00C1FFFF00C2FFFF00C3FFFF00C4FFFF00C5FFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFF00C600C7FFFFFFFF00C8FFFF00C9FFFFFFFF00CAFFFF00CBFFFF00CCFFFF00CDFFFF00CEFFFF -00D000CF00D200D1FFFF00D300D500D400D6FFFF00D7FFFFFFFF00D8FFFFFFFF00DA00D900DBFFFF00DCFFFFFFFF00DD -00DF00DE00E100E0FFFF00E200E3FFFF00E4FFFFFFFF00E500E6FFFFFFFFFFFFFFFFFFFFFFFFFFFF00E7FFFFFFFFFFFF -FFFF00E800EA00E9FFFFFFFF00EBFFFF00ED00EC00EF00EEFFFF00F0FFFFFFFFFFFFFFFFFFFF00F1FFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFF00F2FFFFFFFF00F3FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00F4FFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00F5FFFF00F6FFFFFFFFFFFF00F7FFFF -FFFFFFFF00F8FFFF00FA00F9FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFF05CBFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFF00FC00FB00FE00FD00FF05CE0101010001030102010501040107010601090108010B010A010D010C -010F010E0111011001130112011501140117011601190118011B011AFFFF011C011E011DFFFFFFFF011FFFFF01210120 -0122FFFF0123FFFF0124FFFF0125FFFF0126FFFF0127FFFF0128FFFF0129FFFF012AFFFF012BFFFF012CFFFF012DFFFF -012F012E013101300132FFFFFFFFFFFFFFFF01330134FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0136013501380137013A0139013C013B -013E013D0140013F01420141014401430146014501480147014A0149014C014B014E014D0150014F0152015101540153 -0156015501580157015A0159015C015B015E015D0160015F01620161016401630165FFFF0166FFFF0167FFFF0168FFFF -0169FFFF016AFFFF016BFFFF016CFFFF016DFFFF016EFFFF016FFFFF0170FFFF0171FFFF0172FFFF0173FFFF0174FFFF -0175FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0176FFFF0177FFFF0178FFFF0179FFFF017AFFFF017BFFFF017CFFFF -017DFFFF017EFFFF017FFFFF0180FFFF0181FFFF0182FFFF0183FFFF0184FFFF0185FFFF0186FFFF0187FFFF0188FFFF -0189FFFF018AFFFF018BFFFF018CFFFF018DFFFF018EFFFF018FFFFF0190FFFFFFFFFFFFFFFF0191FFFF0192FFFF0193 -FFFF0194FFFF0195FFFF0196019801970199FFFF019AFFFF019BFFFF019CFFFF019DFFFF019EFFFF019FFFFF01A0FFFF -01A1FFFF01A2FFFF01A3FFFF01A4FFFF01A5FFFF01A6FFFF01A7FFFF01A8FFFF01A9FFFF01AAFFFF01ABFFFF01ACFFFF -01ADFFFF01AEFFFF01AFFFFF01B0FFFF01B1FFFF01B2FFFF01B3FFFF01B4FFFF01B5FFFF01B6FFFF01B7FFFF01B8FFFF -01B9FFFF01BAFFFF01BBFFFF01BCFFFF01BDFFFF01BEFFFF01BFFFFF01C0FFFF01C1FFFF01C2FFFF01C3FFFF01C4FFFF -01C5FFFF01C6FFFF01C7FFFF01C8FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF01C9FFFF01CB01CA01CD01CC01CF01CE01D101D001D301D201D501D401D701D6 -01D901D801DB01DA01DD01DC01DF01DE01E101E001E301E201E501E401E701E601E901E801EB01EA01ED01EC05BD01EE -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -01F001EF01F201F101F401F301F601F501F801F701FA01F901FC01FB01FE01FD020001FF020202010204020302060205 -02080207020A0209020C020B020E020D0210020F02120211021402130216021502180217FFFF0219021AFFFF021C021B -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF021E021D0220021F02220221FFFFFFFF022402230226022502280227022A0229 -FFFF022BFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF022CFFFFFFFFFFFF022DFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF022EFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF022FFFFF0230FFFF0231FFFF0232FFFF -0233FFFF0234FFFF0235FFFF0236FFFF0237FFFF0238FFFF0239FFFF023AFFFF023BFFFF023CFFFF023DFFFF023EFFFF -023FFFFF0240FFFF0241FFFF0242FFFF0243FFFF0244FFFF0245FFFF0246FFFF0247FFFF0248FFFF0249FFFF024AFFFF -024BFFFF024CFFFF024DFFFF024EFFFF024FFFFF0250FFFF0251FFFF0252FFFF0253FFFF0254FFFF0255FFFF0256FFFF -0257FFFF0258FFFF0259FFFF025AFFFF025BFFFF025CFFFF025DFFFF025EFFFF025FFFFF0260FFFF0261FFFF0262FFFF -0263FFFF0264FFFF0265FFFF0266FFFF0267FFFF0268FFFF0269FFFF026AFFFF026BFFFF026CFFFF026DFFFF026EFFFF -026FFFFF0270FFFF0271FFFF0272FFFF0273FFFF0274FFFF0275FFFF0276FFFF0277FFFF0278FFFF0279FFFF05D505D3 -05D905D7027A05DBFFFFFFFFFFFFFFFF027BFFFF027CFFFF027DFFFF027EFFFF027FFFFF0280FFFF0281FFFF0282FFFF -0283FFFF0284FFFF0285FFFF0286FFFF0287FFFF0288FFFF0289FFFF028AFFFF028BFFFF028CFFFF028DFFFF028EFFFF -028FFFFF0290FFFF0291FFFF0292FFFF0293FFFF0294FFFF0295FFFF0296FFFF0297FFFF0298FFFF0299FFFF029AFFFF -029BFFFF029CFFFF029DFFFF029EFFFF029FFFFF02A0FFFF02A1FFFF02A2FFFF02A3FFFF02A4FFFF02A5FFFF02A6FFFF -02A7FFFF02A8FFFF02A9FFFF02AAFFFF02AC02AB02AE02AD02B002AF02B202B1FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -02B402B302B602B502B802B7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF02BA02B902BC02BB02BE02BD02C002BF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF02C202C102C402C302C602C502C802C7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -02CA02C902CC02CB02CE02CDFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF02CF05DD02D005DF02D105E202D205E5 -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF02D402D302D602D502D802D702DA02D9FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -02DC02DB02DE02DD02E002DF02E202E102E402E302E602E502E802E7FFFFFFFF06080606060C060A0610060E06140612 -06180616061C061A0620061E0624062206280626062C062A0630062E0634063206380636063C063A0640063E06440642 -06480646064C064A0650064E0654065206580656065C065A0660065E066406620302030106660672FFFF0674067E05E8 -FFFFFFFFFFFFFFFFFFFF0668FFFF0304FFFFFFFF066A0676FFFF0678068105EAFFFFFFFFFFFFFFFFFFFF066CFFFFFFFF -0307030605EF05ECFFFFFFFF05F405F2FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0309030805FA05F7030A05FD060105FF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF066E067AFFFF067C06840604FFFFFFFFFFFFFFFFFFFF0670FFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF030CFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -030E030D0310030F03120311031403130316031503180317031A0319031C031BFFFFFFFFFFFFFFFFFFFF031DFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -031F031E0321032003230322032503240327032603290328032B032A032D032C032F032E033103300333033203350334 -03370336FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -03390338033B033A033D033C033F033E0341034003430342034503440347034603490348034B034A034D034C034F034E -0351035003530352035503540357035603590358035B035A035D035C035F035E03610360036303620365036403670366 -0368FFFFFFFFFFFF0369FFFFFFFF036AFFFF036BFFFF036CFFFF036DFFFFFFFFFFFFFFFF036EFFFFFFFFFFFFFFFF036F -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0370FFFF0371FFFF0372FFFF0373FFFF0374FFFF0375FFFF0376FFFF0377FFFF -0378FFFF0379FFFF037AFFFF037BFFFF037CFFFF037DFFFF037EFFFF037FFFFF0380FFFF0381FFFF0382FFFF0383FFFF -0384FFFF0385FFFF0386FFFF0387FFFF0388FFFF0389FFFF038AFFFF038BFFFF038CFFFF038DFFFF038EFFFF038FFFFF -0390FFFF0391FFFF0392FFFF0393FFFF0394FFFF0395FFFF0396FFFF0397FFFF0398FFFF0399FFFF039AFFFF039BFFFF -039CFFFF039DFFFF039EFFFF039FFFFF03A0FFFF03A1FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF03A2FFFF03A3 -FFFFFFFF03A4FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF03A603A503A803A703AA03A903AC03AB -03AE03AD03B003AF03B203B103B403B303B603B503B803B703BA03B903BC03BB03BE03BD03C003BF03C203C103C403C3 -03C603C503C803C703CA03C903CBFFFFFFFFFFFFFFFFFFFF03CCFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF03CDFFFF03CEFFFF03CFFFFF03D0FFFF03D1FFFF03D2FFFF03D3FFFF03D4FFFF -03D5FFFF03D6FFFF03D7FFFF03D8FFFF03D9FFFF03DAFFFF03DBFFFF03DCFFFF03DDFFFF03DEFFFF03DFFFFF03E0FFFF -03E1FFFF03E2FFFF03E3FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -03E4FFFF03E5FFFF03E6FFFF03E7FFFF03E8FFFF03E9FFFF03EAFFFF03EBFFFF03ECFFFF03EDFFFF03EEFFFF03EFFFFF -03F0FFFF03F1FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFF03F2FFFF03F3FFFF03F4FFFF03F5FFFF03F6FFFF03F7FFFF03F8FFFFFFFFFFFF03F9FFFF03FAFFFF03FBFFFF -03FCFFFF03FDFFFF03FEFFFF03FFFFFF0400FFFF0401FFFF0402FFFF0403FFFF0404FFFF0405FFFF0406FFFF0407FFFF -0408FFFF0409FFFF040AFFFF040BFFFF040CFFFF040DFFFF040EFFFF040FFFFF0410FFFF0411FFFF0412FFFF0413FFFF -0414FFFF0415FFFF0416FFFF0417FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0418FFFF0419041AFFFF -041BFFFF041CFFFF041DFFFF041EFFFFFFFFFFFFFFFFFFFFFFFF041FFFFFFFFF0420FFFF0421FFFFFFFF04220423FFFF -0424FFFF0425FFFF0426FFFF0427FFFF0428FFFF0429FFFF042AFFFF042BFFFF042CFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFF042DFFFF042EFFFF042FFFFF0430FFFF0431FFFF0432FFFF0433FFFF0434FFFFFFFFFFFFFFFFFFFF -FFFF0435FFFF0436FFFFFFFFFFFFFFFF0437FFFFFFFFFFFFFFFFFFFF0438FFFF0439FFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF043A -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFF043BFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF043D043C043F043E0441044004430442044504440447044604490448044B044A -044D044C044F044E0451045004530452045504540457045604590458045B045A045D045C045F045E0461046004630462 -046504640467046604690468046B046A046D046C046F046E0471047004730472047504740477047604790478047B047A -047D047C047F047E0481048004830482048504840487048604890488048B048A05AF05AD05B305B105B905B6FFFF05BB -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF05BFFFFF05C305C105C705C5FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF048CFFFF048E048D0490048F04920491049404930496049504980497049A0499 -049C049B049E049D04A0049F04A204A104A404A3FFFF04A5FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF04A704A604A904A804AB04AA04AD04AC -04AF04AE04B104B004B304B204B504B404B704B604B904B804BB04BA04BD04BC04BF04BE04C104C004C304C204C504C4 -04C704C604C904C804CB04CA04CD04CCFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF04CF04CE04D104D004D304D204D504D404D704D604D904D804DB04DA04DD04DC -04DF04DE04E104E004E304E204E504E404E704E604E904E804EB04EA04ED04EC04EF04EE04F104F0FFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF04F2FFFF -04F404F304F604F504F804F704FA04F904FC04FB04FDFFFF04FF04FE0501050005030502050505040507050605090508 -050B050A050CFFFF050E050D0510050F051205110513FFFFFFFF0514FFFFFFFF0516051505180517051A0519051C051B -051E051D0520051F05220521052405230526052505280527052A0529052C052B052E052D0530052F0532053105340533 -0536053505380537053A0539053C053B053E053D0540053F054205410544054305460545FFFF0547FFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF05490548054B054A054D054C054F054E05510550055305520555055405570556 -05590558055B055A055D055C055F055E05610560056305620565056405670566FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF05690568056B056A056D056C056F056E05710570057305720575057405770576 -05790578057B057A057D057C057F057E05810580058305820585058405870586FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFF05890588058B058A058D058C058F058E0591059005930592059505940597059605990598059B059A059D059C -059F059E05A105A005A305A205A505A405A705A605A905A8FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF", -); -//10112 bytes -enum toLowerIndexTrieEntries = TrieEntry!(ushort, 8, 7, 6)(x" -000000000000004000000280", -x" -000001000000048000000EC0", -x" -020201000402030206020205070202020202020202020202020202020202020202020202020202020202020202020202 -020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202 -020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202 -020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202 -020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202 -0202020202020202020202020202020200010000000200000004000300060005000800070000000000090000000B000A -000D000C000F000E00110010000000120000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000014001300000000000000000000000000000000000000000016001500000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -0000000000000017000000000000000000190018001B001A001D001C001F001E00000000000000000021002000000022 -000000000000000000000000000000000000000000240023000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000260025002800270000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -00000000000000000000000000000000002900000000002A002C002B002E002D00000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -00000000000000000000002F000000000000000000000000000000000000000000000000000000000000003000000000 -000000000000000000000000000000000000000000000000000000000000000000000031003300320034000000000035 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000003600000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000003700000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000380000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000039000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -00000000000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -0000FFFF00020001000400030006000500080007000A0009000C000B000E000D0010000F001200110014001300160015 -00180017FFFF0019FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF001B001A001D001C001F001E00210020 -00230022002500240027002600290028002B002A002D002C002F002EFFFF003000320031003400330036003505990037 -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0038FFFF0039FFFF003AFFFF003BFFFF003CFFFF003DFFFF003EFFFF003F -FFFF0040FFFF0041FFFF0042FFFF0043FFFF0044FFFF0045FFFF0046FFFF0047FFFF0048FFFF0049FFFF004AFFFF004B -FFFF004CFFFF004DFFFF004EFFFF004FFFFF059AFFFF0051FFFF0052FFFF00530054FFFF0055FFFF0056FFFF0057FFFF -0058FFFF0059FFFF005AFFFF005BFFFF05A9FFFFFFFF005CFFFF005DFFFF005EFFFF005FFFFF0060FFFF0061FFFF0062 -FFFF0063FFFF0064FFFF0065FFFF0066FFFF0067FFFF0068FFFF0069FFFF006AFFFF006BFFFF006CFFFF006DFFFF006E -FFFF006FFFFF0070FFFF0071FFFF0072007400730075FFFF0076FFFFFFFFFFFF0077FFFFFFFF0078FFFF0079007B007A -007CFFFF007E007DFFFFFFFF0080007F008200810083FFFFFFFF008400860085FFFF0087FFFFFFFF00890088008AFFFF -FFFF008BFFFF008CFFFF008D008F008E0090FFFFFFFFFFFFFFFF0091009300920094FFFF009600950097FFFF0098FFFF -FFFF0099FFFFFFFFFFFF009AFFFFFFFFFFFFFFFFFFFFFFFF009C009B009DFFFFFFFF009E00A0009F00A1FFFF00A2FFFF -00A3FFFF00A4FFFF00A5FFFF00A6FFFF00A7FFFF00A8FFFFFFFFFFFFFFFF00A9FFFF00AAFFFF00ABFFFF00ACFFFF00AD -FFFF00AEFFFF00AFFFFF00B0FFFF00B100B205ACFFFF00B3FFFF00B400B600B5FFFF00B7FFFF00B8FFFF00B9FFFF00BA -FFFF00BBFFFF00BCFFFF00BDFFFF00BEFFFF00BFFFFF00C0FFFF00C1FFFF00C2FFFF00C3FFFF00C4FFFF00C5FFFF00C6 -FFFF00C7FFFF00C8FFFF00C9FFFF00CAFFFF00CBFFFF00CCFFFF00CDFFFF00CEFFFF00CFFFFF00D0FFFF00D1FFFF00D2 -FFFF00D3FFFF00D4FFFFFFFFFFFFFFFFFFFFFFFF00D600D500D7FFFFFFFF00D800D9FFFF00DAFFFF00DC00DBFFFF00DD -FFFF00DEFFFF00DFFFFF00E0FFFF00E1FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00E2FFFF00E3FFFFFFFFFFFF00E4FFFFFFFFFFFFFFFFFFFFFFFF00E5FFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFF00E600E800E7FFFF00E9FFFF00EA00EC00EB00ED05AA00EF00EE00F100F000F300F2 -00F500F400F700F600F900F800FB00FA00FD00FC00FEFFFF010000FF010201010104010301060105FFFFFFFFFFFFFFFF -FFFF05ABFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFF0107FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0108FFFF0109FFFF010AFFFF010B -FFFF010CFFFF010DFFFF010EFFFF010FFFFF0110FFFF0111FFFF0112FFFF0113FFFFFFFFFFFFFFFFFFFF01140115FFFF -0116FFFFFFFF01170118FFFF011A0119011C011B011E011D0120011F01220121012401230126012501280127012A0129 -012C012B012E012D0130012F01320131013401330136013501380137013A0139013C013B013E013D0140013F01420141 -014401430146014501480147014A0149FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF014BFFFF014CFFFF014DFFFF014EFFFF014FFFFF0150FFFF0151FFFF0152 -FFFF0153FFFF0154FFFF0155FFFF0156FFFF0157FFFF0158FFFF0159FFFF015AFFFF015BFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFF015CFFFF015DFFFF015EFFFF015FFFFF0160FFFF0161FFFF0162FFFF0163FFFF0164FFFF0165FFFF0166 -FFFF0167FFFF0168FFFF0169FFFF016AFFFF016BFFFF016CFFFF016DFFFF016EFFFF016FFFFF0170FFFF0171FFFF0172 -FFFF0173FFFF0174FFFF0175FFFF0176017801770179FFFF017AFFFF017BFFFF017CFFFF017DFFFF017EFFFFFFFFFFFF -FFFF017FFFFF0180FFFF0181FFFF0182FFFF0183FFFF0184FFFF0185FFFF0186FFFF0187FFFF0188FFFF0189FFFF018A -FFFF018BFFFF018CFFFF018DFFFF018EFFFF018FFFFF0190FFFF0191FFFF0192FFFF0193FFFF0194FFFF0195FFFF0196 -FFFF0197FFFF0198FFFF0199FFFF019AFFFF019BFFFF019CFFFF019DFFFF019EFFFF019FFFFF01A0FFFF01A1FFFF01A2 -FFFF01A3FFFF01A4FFFF01A5FFFF01A6FFFF01A7FFFF01A8FFFF01A9FFFF01AAFFFF01ABFFFF01ACFFFF01ADFFFF01AE -01AFFFFF01B101B001B301B201B501B401B701B601B901B801BB01BA01BD01BC01BF01BE01C101C001C301C201C501C4 -01C701C601C901C801CB01CA01CD01CC01CF01CE01D101D001D301D2FFFF01D4FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF05A3FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF01D601D501D801D701DA01D901DC01DB01DE01DD01E001DF01E201E101E401E3 -01E601E501E801E701EA01E901EC01EB01EE01ED01F001EF01F201F101F401F301F601F501F801F701FA01F901FBFFFF -FFFFFFFFFFFFFFFF01FCFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF01FE01FD020001FF0202020102040203 -0206020502080207020A0209020C020B020E020D0210020F02120211021402130216021502180217021A0219021C021B -021E021D0220021F02220221022402230226022502280227022A0229022C022B022E022D0230022F0232023102340233 -0236023502380237023A0239023C023B023E023D0240023F02420241024402430246024502480247024A0249024C024B -024E024D0250024F02520251FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF025402530256025502580257025A0259025C025B025E025D0260025F02620261 -026402630266026502680267026A0269026C026B026E026D0270026F02720271027402730276027502780277027A0279 -027C027BFFFF027D027EFFFF0280027FFFFF0281FFFF0282FFFF0283FFFF0284FFFF0285FFFF0286FFFF0287FFFF0288 -FFFF0289FFFF028AFFFF028BFFFF028CFFFF028DFFFF028EFFFF028FFFFF0290FFFF0291FFFF0292FFFF0293FFFF0294 -FFFF0295FFFF0296FFFF0297FFFF0298FFFF0299FFFF029AFFFF029BFFFF029CFFFF029DFFFF029EFFFF029FFFFF02A0 -FFFF02A1FFFF02A2FFFF02A3FFFF02A4FFFF02A5FFFF02A6FFFF02A7FFFF02A8FFFF02A9FFFF02AAFFFF02ABFFFF02AC -FFFF02ADFFFF02AEFFFF02AFFFFF02B0FFFF02B1FFFF02B2FFFF02B3FFFF02B4FFFF02B5FFFF02B6FFFF02B7FFFF02B8 -FFFF02B9FFFF02BAFFFF02BBFFFF02BCFFFF02BDFFFF02BEFFFF02BFFFFF02C0FFFF02C1FFFF02C2FFFF02C3FFFF02C4 -FFFF02C5FFFF02C6FFFF02C7FFFF02C8FFFF02C9FFFF02CAFFFF02CB05AE05AD05B005AFFFFF05B1FFFFFFFFFFFF02CC -FFFF02CDFFFF02CEFFFF02CFFFFF02D0FFFF02D1FFFF02D2FFFF02D3FFFF02D4FFFF02D5FFFF02D6FFFF02D7FFFF02D8 -FFFF02D9FFFF02DAFFFF02DBFFFF02DCFFFF02DDFFFF02DEFFFF02DFFFFF02E0FFFF02E1FFFF02E2FFFF02E3FFFF02E4 -FFFF02E5FFFF02E6FFFF02E7FFFF02E8FFFF02E9FFFF02EAFFFF02EBFFFF02ECFFFF02EDFFFF02EEFFFF02EFFFFF02F0 -FFFF02F1FFFF02F2FFFF02F3FFFF02F4FFFF02F5FFFF02F6FFFF02F7FFFF02F8FFFF02F9FFFF02FAFFFF02FBFFFF02FC -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF02FE02FD030002FF0302030103040303FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -0306030503080307030A0309FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF030C030B030E030D0310030F03120311 -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF031403130316031503180317031A0319FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -031C031B031E031D0320031FFFFFFFFFFFFF05B2FFFF05B3FFFF05B4FFFF05B50321FFFF0322FFFF0323FFFF0324FFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0326032503280327032A0329032C032BFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF05C305C205C505C405C705C605C905C805CB05CA05CD05CC05CF05CE05D105D0 -05D305D205D505D405D705D605D905D805DB05DA05DD05DC05DF05DE05E105E005E305E205E505E405E705E605E905E8 -05EB05EA05ED05EC05EF05EE05F105F0FFFFFFFF05F205F8FFFF05F905FE05B60346034503480347FFFF05F3FFFFFFFF -FFFFFFFF05F405FAFFFF05FB05FF05B7034B034A034D034CFFFF05F5FFFFFFFFFFFFFFFF05B905B8FFFFFFFF05BB05BA -0350034F03520351FFFFFFFFFFFFFFFFFFFFFFFF05BD05BCFFFF05BE05C005BF0354035303560355FFFF0357FFFFFFFF -FFFFFFFF05F605FCFFFF05FD060005C103590358035B035AFFFF05F7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFF035DFFFFFFFF035F035EFFFFFFFFFFFFFFFFFFFFFFFFFFFF0360FFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF03620361036403630366036503680367 -036A0369036C036B036E036D0370036FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFF0371FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF03730372 -037503740377037603790378037B037A037D037C037F037E0381038003830382038503840387038603890388038B038A -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -038D038C038F038E0391039003930392039503940397039603990398039B039A039D039C039F039E03A103A003A303A2 -03A503A403A703A603A903A803AB03AA03AD03AC03AF03AE03B103B003B303B203B503B403B703B603B903B803BB03BA -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFF03BC03BE03BDFFFF03BF03C0FFFF03C1FFFF03C2FFFF03C3FFFF03C503C4FFFF03C6FFFF03C703C8FFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFF03CA03C9FFFF03CBFFFF03CCFFFF03CDFFFF03CEFFFF03CFFFFF03D0FFFF03D1FFFF03D2 -FFFF03D3FFFF03D4FFFF03D5FFFF03D6FFFF03D7FFFF03D8FFFF03D9FFFF03DAFFFF03DBFFFF03DCFFFF03DDFFFF03DE -FFFF03DFFFFF03E0FFFF03E1FFFF03E2FFFF03E3FFFF03E4FFFF03E5FFFF03E6FFFF03E7FFFF03E8FFFF03E9FFFF03EA -FFFF03EBFFFF03ECFFFF03EDFFFF03EEFFFF03EFFFFF03F0FFFF03F1FFFF03F2FFFF03F3FFFF03F4FFFF03F5FFFF03F6 -FFFF03F7FFFF03F8FFFF03F9FFFF03FAFFFF03FBFFFF03FCFFFFFFFFFFFFFFFFFFFFFFFF03FDFFFF03FEFFFFFFFFFFFF -FFFFFFFFFFFF03FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0400FFFF0401FFFF0402FFFF0403 -FFFF0404FFFF0405FFFF0406FFFF0407FFFF0408FFFF0409FFFF040AFFFF040BFFFF040CFFFF040DFFFF040EFFFF040F -FFFF0410FFFF0411FFFF0412FFFF0413FFFF0414FFFF0415FFFF0416FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0417FFFF0418FFFF0419FFFF041AFFFF041BFFFF041CFFFF041DFFFF041E -FFFF041FFFFF0420FFFF0421FFFF0422FFFF0423FFFF0424FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0425FFFF0426FFFF0427FFFF0428FFFF0429FFFF042AFFFF042B -FFFFFFFFFFFF042CFFFF042DFFFF042EFFFF042FFFFF0430FFFF0431FFFF0432FFFF0433FFFF0434FFFF0435FFFF0436 -FFFF0437FFFF0438FFFF0439FFFF043AFFFF043BFFFF043CFFFF043DFFFF043EFFFF043FFFFF0440FFFF0441FFFF0442 -FFFF0443FFFF0444FFFF0445FFFF0446FFFF0447FFFF0448FFFF0449FFFF044AFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -044BFFFF044CFFFF044DFFFFFFFF044EFFFF044FFFFF0450FFFF0451FFFF0452FFFFFFFF0453FFFF0454FFFFFFFFFFFF -FFFF0455FFFF0456FFFFFFFFFFFF0457FFFF0458FFFF0459FFFF045AFFFF045BFFFF045CFFFF045DFFFF045EFFFF045F -FFFF04600462046104640463FFFF04650467046604690468FFFF046AFFFF046BFFFF046CFFFF046DFFFF046EFFFF046F -FFFF0470FFFF047104730472047504740476FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0477FFFFFFFFFFFFFFFFFFFF0478 -FFFF0479FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFF047AFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF059D059C059F059E05A105A0FFFF05A2 -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF05A4FFFF05A605A505A805A7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF047BFFFF047D047C047F047E04810480 -04830482048504840487048604890488048B048A048D048C048F048E0491049004930492FFFF0494FFFFFFFFFFFFFFFF -0496049504980497049A0499049C049B049E049D04A0049F04A204A104A404A304A604A504A804A704AA04A904AC04AB -04AE04AD04B004AF04B204B104B404B304B604B504B804B704BA04B904BC04BBFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF04BE04BD04C004BF04C204C104C404C3 -04C604C504C804C704CA04C904CC04CB04CE04CD04D004CF04D204D104D404D304D604D504D804D704DA04D904DC04DB -04DE04DD04E004DFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -04E204E104E404E304E604E504E804E704EA04E9FFFF04EB04ED04EC04EF04EE04F104F004F304F204F504F404F704F6 -04F904F8FFFF04FA04FC04FB04FE04FD050004FFFFFF050105030502FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF050505040507050605090508050B050A050D050C050F050E0511051005130512 -051505140517051605190518051B051A051D051C051F051E0521052005230522052505240527052605290528052B052A -052D052C052F052E053105300533053205350534FFFF0536FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF05380537053A0539053C053B053E053D0540053F054205410544054305460545 -05480547054A0549054C054B054E054D0550054F05520551055405530556055505580557055A0559055C055B055E055D -0560055F05620561056405630566056505680567056A0569056C056B056E056D0570056F057205710574057305760575 -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF05780577057A0579057C057B057E057D0580057F058205810584058305860585 -05880587058A0589058C058B058E058D0590058F05920591059405930596059505980597FFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF", -); -//10496 bytes -enum toTitleIndexTrieEntries = TrieEntry!(ushort, 8, 7, 6)(x" -000000000000004000000280", -x" -000001000000048000000F80", -x" -020201000402030206020205070202020202020202020202020202020202020202020202020202020202020202020202 -020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202 -020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202 -020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202 -020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202 -0202020202020202020202020202020200010000000300020005000400070006000900080000000A000B0000000D000C -000F000E0011001000130012000000140000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000015000000000000000000000000000000000000000000000016000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -00000000000000170018000000000019001B001A001D001C001F001E0021002000000000000000000022000000000023 -000000000000000000000000000000000000000000240000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000260025002800270000002900000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -00000000000000000000000000000000002A00000000002B002D002C002F002E00000000000000000000000000000000 -000000000000000000300000000000310000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000032000000000000000000000000000000000000000000000000000000000033000000000000 -000000000000000000000000000000000000000000000000000000000000000000350034003600000000000000000037 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000038000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000039000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000003A0000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -0000000000000000003C003B000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -00000000000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0000FFFF00020001000400030006000500080007000A0009000C000B000E000D -0010000F00120011001400130016001500180017FFFF0019FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF001AFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF05AEFFFF001C001B001E001D0020001F00220021 -002400230026002500280027002A0029002C002B002E002D0030002FFFFF003100330032003500340037003600390038 -003AFFFF003BFFFF003CFFFF003DFFFF003EFFFF003FFFFF0040FFFF0041FFFF0042FFFF0043FFFF0044FFFF0045FFFF -0046FFFF0047FFFF0048FFFF0049FFFF004AFFFF004BFFFF004CFFFF004DFFFF004EFFFF004FFFFF0050FFFF0051FFFF -005205B00053FFFF0054FFFF0055FFFFFFFFFFFFFFFF0056FFFF0057FFFF0058FFFF0059FFFF005AFFFF005BFFFF005C -05CD005D005EFFFF005FFFFF0060FFFF0061FFFF0062FFFF0063FFFF0064FFFF0065FFFF0066FFFF0067FFFF0068FFFF -0069FFFF006AFFFF006BFFFF006CFFFF006DFFFF006EFFFF006FFFFF0070FFFF0071FFFF0072FFFF0073FFFF0074FFFF -FFFFFFFFFFFF0075FFFF007600780077FFFF0079007AFFFF007BFFFFFFFFFFFFFFFF007CFFFFFFFFFFFF007DFFFFFFFF -FFFFFFFFFFFF007E007FFFFFFFFFFFFF0080FFFFFFFF0081FFFFFFFFFFFF00820083FFFF0084FFFF0085FFFFFFFFFFFF -FFFF0086FFFFFFFF0087FFFFFFFFFFFFFFFF0088FFFFFFFFFFFF0089FFFF008A008BFFFFFFFFFFFF008CFFFF008DFFFF -FFFFFFFFFFFFFFFF008F008E009100900093009200950094FFFF0096FFFF0097FFFF0098FFFF0099FFFF009AFFFF009B -FFFF009CFFFF009D009F009E00A0FFFF00A1FFFF00A2FFFF00A3FFFF00A4FFFF00A5FFFF00A6FFFF00A7FFFF00A8FFFF -00A905D500AB00AA00ACFFFFFFFFFFFF00ADFFFF00AEFFFF00AFFFFF00B0FFFF00B1FFFF00B2FFFF00B3FFFF00B4FFFF -00B5FFFF00B6FFFF00B7FFFF00B8FFFF00B9FFFF00BAFFFF00BBFFFF00BCFFFF00BDFFFF00BEFFFF00BFFFFF00C0FFFF -FFFFFFFF00C1FFFF00C2FFFF00C3FFFF00C4FFFF00C5FFFF00C6FFFF00C7FFFF00C8FFFF00C9FFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFF00CA00CBFFFFFFFF00CCFFFF00CDFFFFFFFF00CEFFFF00CFFFFF00D0FFFF00D1FFFF00D2FFFF -00D400D300D600D5FFFF00D700D900D800DAFFFF00DBFFFFFFFF00DCFFFFFFFF00DE00DD00DFFFFF00E0FFFFFFFF00E1 -00E300E200E500E4FFFF00E600E7FFFF00E8FFFFFFFF00E900EAFFFFFFFFFFFFFFFFFFFFFFFFFFFF00EBFFFFFFFFFFFF -FFFF00EC00EE00EDFFFFFFFF00EFFFFF00F100F000F300F2FFFF00F4FFFFFFFFFFFFFFFFFFFF00F5FFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFF00F6FFFFFFFF00F7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00F8FFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00F9FFFF00FAFFFFFFFFFFFF00FBFFFF -FFFFFFFF00FCFFFF00FE00FDFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFF05CFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFF010000FF01020101010305D2010501040107010601090108010B010A010D010C010F010E01110110 -01130112011501140117011601190118011B011A011D011C011F011EFFFF012001220121FFFFFFFF0123FFFF01250124 -0126FFFF0127FFFF0128FFFF0129FFFF012AFFFF012BFFFF012CFFFF012DFFFF012EFFFF012FFFFF0130FFFF0131FFFF -01330132013501340136FFFFFFFFFFFFFFFF01370138FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF013A0139013C013B013E013D0140013F -01420141014401430146014501480147014A0149014C014B014E014D0150014F01520151015401530156015501580157 -015A0159015C015B015E015D0160015F016201610164016301660165016801670169FFFF016AFFFF016BFFFF016CFFFF -016DFFFF016EFFFF016FFFFF0170FFFF0171FFFF0172FFFF0173FFFF0174FFFF0175FFFF0176FFFF0177FFFF0178FFFF -0179FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF017AFFFF017BFFFF017CFFFF017DFFFF017EFFFF017FFFFF0180FFFF -0181FFFF0182FFFF0183FFFF0184FFFF0185FFFF0186FFFF0187FFFF0188FFFF0189FFFF018AFFFF018BFFFF018CFFFF -018DFFFF018EFFFF018FFFFF0190FFFF0191FFFF0192FFFF0193FFFF0194FFFFFFFFFFFFFFFF0195FFFF0196FFFF0197 -FFFF0198FFFF0199FFFF019A019C019B019DFFFF019EFFFF019FFFFF01A0FFFF01A1FFFF01A2FFFF01A3FFFF01A4FFFF -01A5FFFF01A6FFFF01A7FFFF01A8FFFF01A9FFFF01AAFFFF01ABFFFF01ACFFFF01ADFFFF01AEFFFF01AFFFFF01B0FFFF -01B1FFFF01B2FFFF01B3FFFF01B4FFFF01B5FFFF01B6FFFF01B7FFFF01B8FFFF01B9FFFF01BAFFFF01BBFFFF01BCFFFF -01BDFFFF01BEFFFF01BFFFFF01C0FFFF01C1FFFF01C2FFFF01C3FFFF01C4FFFF01C5FFFF01C6FFFF01C7FFFF01C8FFFF -01C9FFFF01CAFFFF01CBFFFF01CCFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF01CDFFFF01CF01CE01D101D001D301D201D501D401D701D601D901D801DB01DA -01DD01DC01DF01DE01E101E001E301E201E501E401E701E601E901E801EB01EA01ED01EC01EF01EE01F101F005C101F2 -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -01F401F301F601F501F801F701FA01F901FC01FB01FE01FD020001FF02020201020402030206020502080207020A0209 -020C020B020E020D0210020F02120211021402130216021502180217021A0219021C021BFFFF021D021EFFFF0220021F -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF022202210224022302260225FFFFFFFF02280227022A0229022C022B022E022D -FFFF022FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0230FFFFFFFFFFFF0231FFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0232FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0233FFFF0234FFFF0235FFFF0236FFFF -0237FFFF0238FFFF0239FFFF023AFFFF023BFFFF023CFFFF023DFFFF023EFFFF023FFFFF0240FFFF0241FFFF0242FFFF -0243FFFF0244FFFF0245FFFF0246FFFF0247FFFF0248FFFF0249FFFF024AFFFF024BFFFF024CFFFF024DFFFF024EFFFF -024FFFFF0250FFFF0251FFFF0252FFFF0253FFFF0254FFFF0255FFFF0256FFFF0257FFFF0258FFFF0259FFFF025AFFFF -025BFFFF025CFFFF025DFFFF025EFFFF025FFFFF0260FFFF0261FFFF0262FFFF0263FFFF0264FFFF0265FFFF0266FFFF -0267FFFF0268FFFF0269FFFF026AFFFF026BFFFF026CFFFF026DFFFF026EFFFF026FFFFF0270FFFF0271FFFF0272FFFF -0273FFFF0274FFFF0275FFFF0276FFFF0277FFFF0278FFFF0279FFFF027AFFFF027BFFFF027CFFFF027DFFFF05D905D7 -05DD05DB027E05DFFFFFFFFFFFFFFFFF027FFFFF0280FFFF0281FFFF0282FFFF0283FFFF0284FFFF0285FFFF0286FFFF -0287FFFF0288FFFF0289FFFF028AFFFF028BFFFF028CFFFF028DFFFF028EFFFF028FFFFF0290FFFF0291FFFF0292FFFF -0293FFFF0294FFFF0295FFFF0296FFFF0297FFFF0298FFFF0299FFFF029AFFFF029BFFFF029CFFFF029DFFFF029EFFFF -029FFFFF02A0FFFF02A1FFFF02A2FFFF02A3FFFF02A4FFFF02A5FFFF02A6FFFF02A7FFFF02A8FFFF02A9FFFF02AAFFFF -02ABFFFF02ACFFFF02ADFFFF02AEFFFF02B002AF02B202B102B402B302B602B5FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -02B802B702BA02B902BC02BBFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF02BE02BD02C002BF02C202C102C402C3 -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF02C602C502C802C702CA02C902CC02CBFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -02CE02CD02D002CF02D202D1FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF02D305E102D405E302D505E602D605E9 -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF02D802D702DA02D902DC02DB02DE02DDFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -02E002DF02E202E102E402E302E602E502E802E702EA02E902EC02EBFFFFFFFF060B060A060D060C060F060E06110610 -06130612061506140617061606190618061B061A061D061C061F061E0621062006230622062506240627062606290628 -062B062A062D062C062F062E063106300633063206350634063706360639063803060305063A0640FFFF0642064C05EC -FFFFFFFFFFFFFFFFFFFF063BFFFF0308FFFFFFFF063C0644FFFF0646064F05EEFFFFFFFFFFFFFFFFFFFF063DFFFFFFFF -030B030A05F305F0FFFFFFFF05F805F6FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF030D030C05FE05FB030E060106050603 -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF063E0648FFFF064A06520608FFFFFFFFFFFFFFFFFFFF063FFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0310FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -03120311031403130316031503180317031A0319031C031B031E031D0320031FFFFFFFFFFFFFFFFFFFFF0321FFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -03230322032503240327032603290328032B032A032D032C032F032E0331033003330332033503340337033603390338 -033B033AFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -033D033C033F033E0341034003430342034503440347034603490348034B034A034D034C034F034E0351035003530352 -035503540357035603590358035B035A035D035C035F035E0361036003630362036503640367036603690368036B036A -036CFFFFFFFFFFFF036DFFFFFFFF036EFFFF036FFFFF0370FFFF0371FFFFFFFFFFFFFFFF0372FFFFFFFFFFFFFFFF0373 -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0374FFFF0375FFFF0376FFFF0377FFFF0378FFFF0379FFFF037AFFFF037BFFFF -037CFFFF037DFFFF037EFFFF037FFFFF0380FFFF0381FFFF0382FFFF0383FFFF0384FFFF0385FFFF0386FFFF0387FFFF -0388FFFF0389FFFF038AFFFF038BFFFF038CFFFF038DFFFF038EFFFF038FFFFF0390FFFF0391FFFF0392FFFF0393FFFF -0394FFFF0395FFFF0396FFFF0397FFFF0398FFFF0399FFFF039AFFFF039BFFFF039CFFFF039DFFFF039EFFFF039FFFFF -03A0FFFF03A1FFFF03A2FFFF03A3FFFF03A4FFFF03A5FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF03A6FFFF03A7 -FFFFFFFF03A8FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF03AA03A903AC03AB03AE03AD03B003AF -03B203B103B403B303B603B503B803B703BA03B903BC03BB03BE03BD03C003BF03C203C103C403C303C603C503C803C7 -03CA03C903CC03CB03CE03CD03CFFFFFFFFFFFFFFFFFFFFF03D0FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF03D1FFFF03D2FFFF03D3FFFF03D4FFFF03D5FFFF03D6FFFF03D7FFFF03D8FFFF -03D9FFFF03DAFFFF03DBFFFF03DCFFFF03DDFFFF03DEFFFF03DFFFFF03E0FFFF03E1FFFF03E2FFFF03E3FFFF03E4FFFF -03E5FFFF03E6FFFF03E7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -03E8FFFF03E9FFFF03EAFFFF03EBFFFF03ECFFFF03EDFFFF03EEFFFF03EFFFFF03F0FFFF03F1FFFF03F2FFFF03F3FFFF -03F4FFFF03F5FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFF03F6FFFF03F7FFFF03F8FFFF03F9FFFF03FAFFFF03FBFFFF03FCFFFFFFFFFFFF03FDFFFF03FEFFFF03FFFFFF -0400FFFF0401FFFF0402FFFF0403FFFF0404FFFF0405FFFF0406FFFF0407FFFF0408FFFF0409FFFF040AFFFF040BFFFF -040CFFFF040DFFFF040EFFFF040FFFFF0410FFFF0411FFFF0412FFFF0413FFFF0414FFFF0415FFFF0416FFFF0417FFFF -0418FFFF0419FFFF041AFFFF041BFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF041CFFFF041D041EFFFF -041FFFFF0420FFFF0421FFFF0422FFFFFFFFFFFFFFFFFFFFFFFF0423FFFFFFFF0424FFFF0425FFFFFFFF04260427FFFF -0428FFFF0429FFFF042AFFFF042BFFFF042CFFFF042DFFFF042EFFFF042FFFFF0430FFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFF0431FFFF0432FFFF0433FFFF0434FFFF0435FFFF0436FFFF0437FFFF0438FFFFFFFFFFFFFFFFFFFF -FFFF0439FFFF043AFFFFFFFFFFFFFFFF043BFFFFFFFFFFFFFFFFFFFF043CFFFF043DFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF043E -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFF043FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0441044004430442044504440447044604490448044B044A044D044C044F044E -0451045004530452045504540457045604590458045B045A045D045C045F045E04610460046304620465046404670466 -04690468046B046A046D046C046F046E0471047004730472047504740477047604790478047B047A047D047C047F047E -0481048004830482048504840487048604890488048B048A048D048C048F048E05B305B105B705B505BD05BAFFFF05BF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF05C3FFFF05C705C505CB05C9FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0490FFFF04920491049404930496049504980497049A0499049C049B049E049D -04A0049F04A204A104A404A304A604A504A804A7FFFF04A9FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF04AB04AA04AD04AC04AF04AE04B104B0 -04B304B204B504B404B704B604B904B804BB04BA04BD04BC04BF04BE04C104C004C304C204C504C404C704C604C904C8 -04CB04CA04CD04CC04CF04CE04D104D0FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF04D304D204D504D404D704D604D904D804DB04DA04DD04DC04DF04DE04E104E0 -04E304E204E504E404E704E604E904E804EB04EA04ED04EC04EF04EE04F104F004F304F204F504F4FFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF04F6FFFF -04F804F704FA04F904FC04FB04FE04FD050004FF0501FFFF05030502050505040507050605090508050B050A050D050C -050F050E0510FFFF0512051105140513051605150517FFFFFFFF0518FFFFFFFF051A0519051C051B051E051D0520051F -05220521052405230526052505280527052A0529052C052B052E052D0530052F05320531053405330536053505380537 -053A0539053C053B053E053D0540053F05420541054405430546054505480547054A0549FFFF054BFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF054D054C054F054E0551055005530552055505540557055605590558055B055A -055D055C055F055E0561056005630562056505640567056605690568056B056AFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF056D056C056F056E0571057005730572057505740577057605790578057B057A -057D057C057F057E0581058005830582058505840587058605890588058B058AFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFF058D058C058F058E0591059005930592059505940597059605990598059B059A059D059C059F059E05A105A0 -05A305A205A505A405A705A605A905A805AB05AA05AD05ACFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF", -); -//10368 bytes -enum toUpperSimpleIndexTrieEntries = TrieEntry!(ushort, 8, 7, 6)(x" -000000000000004000000280", -x" -000001000000048000000F40", -x" -020201000402030206020205070202020202020202020202020202020202020202020202020202020202020202020202 -020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202 -020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202 -020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202 -020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202 -0202020202020202020202020202020200010000000300020005000400070006000900080000000A000B0000000D000C -000F000E0011001000130012000000140000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000015000000000000000000000000000000000000000000000016000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -00000000000000170018000000000019001B001A001D001C001F001E0021002000000000000000000022000000000023 -000000000000000000000000000000000000000000240000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000260025002800270000002900000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -00000000000000000000000000000000002A00000000002B002D002C002F002E00000000000000000000000000000000 -000000000000000000300000000000310000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000032000000000000 -000000000000000000000000000000000000000000000000000000000000000000340033003500000000000000000036 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000037000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000038000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000390000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -0000000000000000003B003A000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -00000000000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0000FFFF00020001000400030006000500080007000A0009000C000B000E000D -0010000F00120011001400130016001500180017FFFF0019FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF001AFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF001C001B001E001D0020001F00220021 -002400230026002500280027002A0029002C002B002E002D0030002FFFFF003100330032003500340037003600390038 -003AFFFF003BFFFF003CFFFF003DFFFF003EFFFF003FFFFF0040FFFF0041FFFF0042FFFF0043FFFF0044FFFF0045FFFF -0046FFFF0047FFFF0048FFFF0049FFFF004AFFFF004BFFFF004CFFFF004DFFFF004EFFFF004FFFFF0050FFFF0051FFFF -0052FFFF0053FFFF0054FFFF0055FFFFFFFFFFFFFFFF0056FFFF0057FFFF0058FFFF0059FFFF005AFFFF005BFFFF005C -FFFF005D005EFFFF005FFFFF0060FFFF0061FFFF0062FFFF0063FFFF0064FFFF0065FFFF0066FFFF0067FFFF0068FFFF -0069FFFF006AFFFF006BFFFF006CFFFF006DFFFF006EFFFF006FFFFF0070FFFF0071FFFF0072FFFF0073FFFF0074FFFF -FFFFFFFFFFFF0075FFFF007600780077FFFF0079007AFFFF007BFFFFFFFFFFFFFFFF007CFFFFFFFFFFFF007DFFFFFFFF -FFFFFFFFFFFF007E007FFFFFFFFFFFFF0080FFFFFFFF0081FFFFFFFFFFFF00820083FFFF0084FFFF0085FFFFFFFFFFFF -FFFF0086FFFFFFFF0087FFFFFFFFFFFFFFFF0088FFFFFFFFFFFF0089FFFF008A008BFFFFFFFFFFFF008CFFFF008DFFFF -FFFFFFFFFFFFFFFF008EFFFFFFFF008F009100900092FFFFFFFF0093FFFF0094FFFF0095FFFF0096FFFF0097FFFF0098 -FFFF0099FFFF009A009C009B009DFFFF009EFFFF009FFFFF00A0FFFF00A1FFFF00A2FFFF00A3FFFF00A4FFFF00A5FFFF -FFFFFFFF00A700A600A8FFFFFFFFFFFF00A9FFFF00AAFFFF00ABFFFF00ACFFFF00ADFFFF00AEFFFF00AFFFFF00B0FFFF -00B1FFFF00B2FFFF00B3FFFF00B4FFFF00B5FFFF00B6FFFF00B7FFFF00B8FFFF00B9FFFF00BAFFFF00BBFFFF00BCFFFF -FFFFFFFF00BDFFFF00BEFFFF00BFFFFF00C0FFFF00C1FFFF00C2FFFF00C3FFFF00C4FFFF00C5FFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFF00C600C7FFFFFFFF00C8FFFF00C9FFFFFFFF00CAFFFF00CBFFFF00CCFFFF00CDFFFF00CEFFFF -00D000CF00D200D1FFFF00D300D500D400D6FFFF00D7FFFFFFFF00D8FFFFFFFF00DA00D900DBFFFF00DCFFFFFFFF00DD -00DF00DE00E100E0FFFF00E200E3FFFF00E4FFFFFFFF00E500E6FFFFFFFFFFFFFFFFFFFFFFFFFFFF00E7FFFFFFFFFFFF -FFFF00E800EA00E9FFFFFFFF00EBFFFF00ED00EC00EF00EEFFFF00F0FFFFFFFFFFFFFFFFFFFF00F1FFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFF00F2FFFFFFFF00F3FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00F4FFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00F5FFFF00F6FFFFFFFFFFFF00F7FFFF -FFFFFFFF00F8FFFF00FA00F9FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFF00FC00FB00FE00FD00FFFFFF0101010001030102010501040107010601090108010B010A010D010C -010F010E0111011001130112011501140117011601190118011B011AFFFF011C011E011DFFFFFFFF011FFFFF01210120 -0122FFFF0123FFFF0124FFFF0125FFFF0126FFFF0127FFFF0128FFFF0129FFFF012AFFFF012BFFFF012CFFFF012DFFFF -012F012E013101300132FFFFFFFFFFFFFFFF01330134FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0136013501380137013A0139013C013B -013E013D0140013F01420141014401430146014501480147014A0149014C014B014E014D0150014F0152015101540153 -0156015501580157015A0159015C015B015E015D0160015F01620161016401630165FFFF0166FFFF0167FFFF0168FFFF -0169FFFF016AFFFF016BFFFF016CFFFF016DFFFF016EFFFF016FFFFF0170FFFF0171FFFF0172FFFF0173FFFF0174FFFF -0175FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0176FFFF0177FFFF0178FFFF0179FFFF017AFFFF017BFFFF017CFFFF -017DFFFF017EFFFF017FFFFF0180FFFF0181FFFF0182FFFF0183FFFF0184FFFF0185FFFF0186FFFF0187FFFF0188FFFF -0189FFFF018AFFFF018BFFFF018CFFFF018DFFFF018EFFFF018FFFFF0190FFFFFFFFFFFFFFFF0191FFFF0192FFFF0193 -FFFF0194FFFF0195FFFF0196019801970199FFFF019AFFFF019BFFFF019CFFFF019DFFFF019EFFFF019FFFFF01A0FFFF -01A1FFFF01A2FFFF01A3FFFF01A4FFFF01A5FFFF01A6FFFF01A7FFFF01A8FFFF01A9FFFF01AAFFFF01ABFFFF01ACFFFF -01ADFFFF01AEFFFF01AFFFFF01B0FFFF01B1FFFF01B2FFFF01B3FFFF01B4FFFF01B5FFFF01B6FFFF01B7FFFF01B8FFFF -01B9FFFF01BAFFFF01BBFFFF01BCFFFF01BDFFFF01BEFFFF01BFFFFF01C0FFFF01C1FFFF01C2FFFF01C3FFFF01C4FFFF -01C5FFFF01C6FFFF01C7FFFF01C8FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF01C9FFFF01CB01CA01CD01CC01CF01CE01D101D001D301D201D501D401D701D6 -01D901D801DB01DA01DD01DC01DF01DE01E101E001E301E201E501E401E701E601E901E801EB01EA01ED01ECFFFF01EE -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -01F001EF01F201F101F401F301F601F501F801F701FA01F901FC01FB01FE01FD020001FF020202010204020302060205 -02080207020A0209020C020B020E020D0210020F02120211021402130216021502180217FFFF0219021AFFFF021C021B -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF021E021D0220021F02220221FFFFFFFF022402230226022502280227022A0229 -FFFF022BFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF022CFFFFFFFFFFFF022DFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF022EFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF022FFFFF0230FFFF0231FFFF0232FFFF -0233FFFF0234FFFF0235FFFF0236FFFF0237FFFF0238FFFF0239FFFF023AFFFF023BFFFF023CFFFF023DFFFF023EFFFF -023FFFFF0240FFFF0241FFFF0242FFFF0243FFFF0244FFFF0245FFFF0246FFFF0247FFFF0248FFFF0249FFFF024AFFFF -024BFFFF024CFFFF024DFFFF024EFFFF024FFFFF0250FFFF0251FFFF0252FFFF0253FFFF0254FFFF0255FFFF0256FFFF -0257FFFF0258FFFF0259FFFF025AFFFF025BFFFF025CFFFF025DFFFF025EFFFF025FFFFF0260FFFF0261FFFF0262FFFF -0263FFFF0264FFFF0265FFFF0266FFFF0267FFFF0268FFFF0269FFFF026AFFFF026BFFFF026CFFFF026DFFFF026EFFFF -026FFFFF0270FFFF0271FFFF0272FFFF0273FFFF0274FFFF0275FFFF0276FFFF0277FFFF0278FFFF0279FFFFFFFFFFFF -FFFFFFFF027AFFFFFFFFFFFFFFFFFFFF027BFFFF027CFFFF027DFFFF027EFFFF027FFFFF0280FFFF0281FFFF0282FFFF -0283FFFF0284FFFF0285FFFF0286FFFF0287FFFF0288FFFF0289FFFF028AFFFF028BFFFF028CFFFF028DFFFF028EFFFF -028FFFFF0290FFFF0291FFFF0292FFFF0293FFFF0294FFFF0295FFFF0296FFFF0297FFFF0298FFFF0299FFFF029AFFFF -029BFFFF029CFFFF029DFFFF029EFFFF029FFFFF02A0FFFF02A1FFFF02A2FFFF02A3FFFF02A4FFFF02A5FFFF02A6FFFF -02A7FFFF02A8FFFF02A9FFFF02AAFFFF02AC02AB02AE02AD02B002AF02B202B1FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -02B402B302B602B502B802B7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF02BA02B902BC02BB02BE02BD02C002BF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF02C202C102C402C302C602C502C802C7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -02CA02C902CC02CB02CE02CDFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF02CFFFFF02D0FFFF02D1FFFF02D2FFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF02D402D302D602D502D802D702DA02D9FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -02DC02DB02DE02DD02E002DF02E202E102E402E302E602E502E802E7FFFFFFFF02EA02E902EC02EB02EE02ED02F002EF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF02F202F102F402F302F602F502F802F7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -02FA02F902FC02FB02FE02FD030002FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF030203010303FFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFF0304FFFFFFFF0305FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -03070306FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF03090308FFFFFFFF030AFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF030BFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF030CFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -030E030D0310030F03120311031403130316031503180317031A0319031C031BFFFFFFFFFFFFFFFFFFFF031DFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -031F031E0321032003230322032503240327032603290328032B032A032D032C032F032E033103300333033203350334 -03370336FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -03390338033B033A033D033C033F033E0341034003430342034503440347034603490348034B034A034D034C034F034E -0351035003530352035503540357035603590358035B035A035D035C035F035E03610360036303620365036403670366 -0368FFFFFFFFFFFF0369FFFFFFFF036AFFFF036BFFFF036CFFFF036DFFFFFFFFFFFFFFFF036EFFFFFFFFFFFFFFFF036F -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0370FFFF0371FFFF0372FFFF0373FFFF0374FFFF0375FFFF0376FFFF0377FFFF -0378FFFF0379FFFF037AFFFF037BFFFF037CFFFF037DFFFF037EFFFF037FFFFF0380FFFF0381FFFF0382FFFF0383FFFF -0384FFFF0385FFFF0386FFFF0387FFFF0388FFFF0389FFFF038AFFFF038BFFFF038CFFFF038DFFFF038EFFFF038FFFFF -0390FFFF0391FFFF0392FFFF0393FFFF0394FFFF0395FFFF0396FFFF0397FFFF0398FFFF0399FFFF039AFFFF039BFFFF -039CFFFF039DFFFF039EFFFF039FFFFF03A0FFFF03A1FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF03A2FFFF03A3 -FFFFFFFF03A4FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF03A603A503A803A703AA03A903AC03AB -03AE03AD03B003AF03B203B103B403B303B603B503B803B703BA03B903BC03BB03BE03BD03C003BF03C203C103C403C3 -03C603C503C803C703CA03C903CBFFFFFFFFFFFFFFFFFFFF03CCFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF03CDFFFF03CEFFFF03CFFFFF03D0FFFF03D1FFFF03D2FFFF03D3FFFF03D4FFFF -03D5FFFF03D6FFFF03D7FFFF03D8FFFF03D9FFFF03DAFFFF03DBFFFF03DCFFFF03DDFFFF03DEFFFF03DFFFFF03E0FFFF -03E1FFFF03E2FFFF03E3FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -03E4FFFF03E5FFFF03E6FFFF03E7FFFF03E8FFFF03E9FFFF03EAFFFF03EBFFFF03ECFFFF03EDFFFF03EEFFFF03EFFFFF -03F0FFFF03F1FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFF03F2FFFF03F3FFFF03F4FFFF03F5FFFF03F6FFFF03F7FFFF03F8FFFFFFFFFFFF03F9FFFF03FAFFFF03FBFFFF -03FCFFFF03FDFFFF03FEFFFF03FFFFFF0400FFFF0401FFFF0402FFFF0403FFFF0404FFFF0405FFFF0406FFFF0407FFFF -0408FFFF0409FFFF040AFFFF040BFFFF040CFFFF040DFFFF040EFFFF040FFFFF0410FFFF0411FFFF0412FFFF0413FFFF -0414FFFF0415FFFF0416FFFF0417FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0418FFFF0419041AFFFF -041BFFFF041CFFFF041DFFFF041EFFFFFFFFFFFFFFFFFFFFFFFF041FFFFFFFFF0420FFFF0421FFFFFFFF04220423FFFF -0424FFFF0425FFFF0426FFFF0427FFFF0428FFFF0429FFFF042AFFFF042BFFFF042CFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFF042DFFFF042EFFFF042FFFFF0430FFFF0431FFFF0432FFFF0433FFFF0434FFFFFFFFFFFFFFFFFFFF -FFFF0435FFFF0436FFFFFFFFFFFFFFFF0437FFFFFFFFFFFFFFFFFFFF0438FFFF0439FFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF043A -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFF043BFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF043D043C043F043E0441044004430442044504440447044604490448044B044A -044D044C044F044E0451045004530452045504540457045604590458045B045A045D045C045F045E0461046004630462 -046504640467046604690468046B046A046D046C046F046E0471047004730472047504740477047604790478047B047A -047D047C047F047E0481048004830482048504840487048604890488048B048A048CFFFF048E048D0490048F04920491 -049404930496049504980497049A0499049C049B049E049D04A0049F04A204A104A404A3FFFF04A5FFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -04A704A604A904A804AB04AA04AD04AC04AF04AE04B104B004B304B204B504B404B704B604B904B804BB04BA04BD04BC -04BF04BE04C104C004C304C204C504C404C704C604C904C804CB04CA04CD04CCFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF04CF04CE04D104D004D304D204D504D4 -04D704D604D904D804DB04DA04DD04DC04DF04DE04E104E004E304E204E504E404E704E604E904E804EB04EA04ED04EC -04EF04EE04F104F0FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFF04F2FFFF04F404F304F604F504F804F704FA04F904FC04FB04FDFFFF04FF04FE05010500 -05030502050505040507050605090508050B050A050CFFFF050E050D0510050F051205110513FFFFFFFF0514FFFFFFFF -0516051505180517051A0519051C051B051E051D0520051F05220521052405230526052505280527052A0529052C052B -052E052D0530052F05320531053405330536053505380537053A0539053C053B053E053D0540053F0542054105440543 -05460545FFFF0547FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF05490548054B054A054D054C054F054E -0551055005530552055505540557055605590558055B055A055D055C055F055E05610560056305620565056405670566 -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF05690568056B056A056D056C056F056E -0571057005730572057505740577057605790578057B057A057D057C057F057E05810580058305820585058405870586 -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF05890588058B058A058D058C058F058E059105900593059205950594 -0597059605990598059B059A059D059C059F059E05A105A005A305A205A505A405A705A605A905A8FFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF", -); -//9856 bytes -enum toLowerSimpleIndexTrieEntries = TrieEntry!(ushort, 8, 7, 6)(x" -000000000000004000000280", -x" -000001000000048000000E40", -x" -020201000402030206020205070202020202020202020202020202020202020202020202020202020202020202020202 -020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202 -020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202 -020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202 -020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202 -0202020202020202020202020202020200010000000200000004000300060005000800070000000000090000000B000A -000D000C000F000E00110010000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000013001200000000000000000000000000000000000000000015001400000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -0000000000000016000000000000000000180017001A0019001C001B001E001D00000000000000000020001F00000021 -000000000000000000000000000000000000000000230022000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000250024002700260000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000028000000000029002B002A002D002C00000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000002E00000000 -00000000000000000000000000000000000000000000000000000000000000000000002F003100300032000000000033 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000003400000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000003500000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000360000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000037000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -00000000000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -0000FFFF00020001000400030006000500080007000A0009000C000B000E000D0010000F001200110014001300160015 -00180017FFFF0019FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF001B001A001D001C001F001E00210020 -00230022002500240027002600290028002B002A002D002C002F002EFFFF0030003200310034003300360035FFFF0037 -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0038FFFF0039FFFF003AFFFF003BFFFF003CFFFF003DFFFF003EFFFF003F -FFFF0040FFFF0041FFFF0042FFFF0043FFFF0044FFFF0045FFFF0046FFFF0047FFFF0048FFFF0049FFFF004AFFFF004B -FFFF004CFFFF004DFFFF004EFFFF004FFFFF0050FFFF0051FFFF0052FFFF00530054FFFF0055FFFF0056FFFF0057FFFF -0058FFFF0059FFFF005AFFFF005BFFFFFFFFFFFFFFFF005CFFFF005DFFFF005EFFFF005FFFFF0060FFFF0061FFFF0062 -FFFF0063FFFF0064FFFF0065FFFF0066FFFF0067FFFF0068FFFF0069FFFF006AFFFF006BFFFF006CFFFF006DFFFF006E -FFFF006FFFFF0070FFFF0071FFFF0072007400730075FFFF0076FFFFFFFFFFFF0077FFFFFFFF0078FFFF0079007B007A -007CFFFF007E007DFFFFFFFF0080007F008200810083FFFFFFFF008400860085FFFF0087FFFFFFFF00890088008AFFFF -FFFF008BFFFF008CFFFF008D008F008E0090FFFFFFFFFFFFFFFF0091009300920094FFFF009600950097FFFF0098FFFF -FFFF0099FFFFFFFFFFFF009AFFFFFFFFFFFFFFFFFFFFFFFF009C009B009DFFFFFFFF009E00A0009F00A1FFFF00A2FFFF -00A3FFFF00A4FFFF00A5FFFF00A6FFFF00A7FFFF00A8FFFFFFFFFFFFFFFF00A9FFFF00AAFFFF00ABFFFF00ACFFFF00AD -FFFF00AEFFFF00AFFFFF00B0FFFF00B100B2FFFFFFFF00B3FFFF00B400B600B5FFFF00B7FFFF00B8FFFF00B9FFFF00BA -FFFF00BBFFFF00BCFFFF00BDFFFF00BEFFFF00BFFFFF00C0FFFF00C1FFFF00C2FFFF00C3FFFF00C4FFFF00C5FFFF00C6 -FFFF00C7FFFF00C8FFFF00C9FFFF00CAFFFF00CBFFFF00CCFFFF00CDFFFF00CEFFFF00CFFFFF00D0FFFF00D1FFFF00D2 -FFFF00D3FFFF00D4FFFFFFFFFFFFFFFFFFFFFFFF00D600D500D7FFFFFFFF00D800D9FFFF00DAFFFF00DC00DBFFFF00DD -FFFF00DEFFFF00DFFFFF00E0FFFF00E1FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00E2FFFF00E3FFFFFFFFFFFF00E4FFFFFFFFFFFFFFFFFFFFFFFF00E5FFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFF00E600E800E7FFFF00E9FFFF00EA00EC00EB00EDFFFF00EF00EE00F100F000F300F2 -00F500F400F700F600F900F800FB00FA00FD00FC00FEFFFF010000FF010201010104010301060105FFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFF0107FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0108FFFF0109FFFF010AFFFF010B -FFFF010CFFFF010DFFFF010EFFFF010FFFFF0110FFFF0111FFFF0112FFFF0113FFFFFFFFFFFFFFFFFFFF01140115FFFF -0116FFFFFFFF01170118FFFF011A0119011C011B011E011D0120011F01220121012401230126012501280127012A0129 -012C012B012E012D0130012F01320131013401330136013501380137013A0139013C013B013E013D0140013F01420141 -014401430146014501480147014A0149FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF014BFFFF014CFFFF014DFFFF014EFFFF014FFFFF0150FFFF0151FFFF0152 -FFFF0153FFFF0154FFFF0155FFFF0156FFFF0157FFFF0158FFFF0159FFFF015AFFFF015BFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFF015CFFFF015DFFFF015EFFFF015FFFFF0160FFFF0161FFFF0162FFFF0163FFFF0164FFFF0165FFFF0166 -FFFF0167FFFF0168FFFF0169FFFF016AFFFF016BFFFF016CFFFF016DFFFF016EFFFF016FFFFF0170FFFF0171FFFF0172 -FFFF0173FFFF0174FFFF0175FFFF0176017801770179FFFF017AFFFF017BFFFF017CFFFF017DFFFF017EFFFFFFFFFFFF -FFFF017FFFFF0180FFFF0181FFFF0182FFFF0183FFFF0184FFFF0185FFFF0186FFFF0187FFFF0188FFFF0189FFFF018A -FFFF018BFFFF018CFFFF018DFFFF018EFFFF018FFFFF0190FFFF0191FFFF0192FFFF0193FFFF0194FFFF0195FFFF0196 -FFFF0197FFFF0198FFFF0199FFFF019AFFFF019BFFFF019CFFFF019DFFFF019EFFFF019FFFFF01A0FFFF01A1FFFF01A2 -FFFF01A3FFFF01A4FFFF01A5FFFF01A6FFFF01A7FFFF01A8FFFF01A9FFFF01AAFFFF01ABFFFF01ACFFFF01ADFFFF01AE -01AFFFFF01B101B001B301B201B501B401B701B601B901B801BB01BA01BD01BC01BF01BE01C101C001C301C201C501C4 -01C701C601C901C801CB01CA01CD01CC01CF01CE01D101D001D301D2FFFF01D4FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF01D601D501D801D701DA01D901DC01DB -01DE01DD01E001DF01E201E101E401E301E601E501E801E701EA01E901EC01EB01EE01ED01F001EF01F201F101F401F3 -01F601F501F801F701FA01F901FBFFFFFFFFFFFFFFFFFFFF01FCFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -01FE01FD020001FF02020201020402030206020502080207020A0209020C020B020E020D0210020F0212021102140213 -0216021502180217021A0219021C021B021E021D0220021F02220221022402230226022502280227022A0229022C022B -022E022D0230022F02320231023402330236023502380237023A0239023C023B023E023D0240023F0242024102440243 -0246024502480247024A0249024C024B024E024D0250024F02520251FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF025402530256025502580257025A0259 -025C025B025E025D0260025F02620261026402630266026502680267026A0269026C026B026E026D0270026F02720271 -027402730276027502780277027A0279027C027BFFFF027D027EFFFF0280027FFFFF0281FFFF0282FFFF0283FFFF0284 -FFFF0285FFFF0286FFFF0287FFFF0288FFFF0289FFFF028AFFFF028BFFFF028CFFFF028DFFFF028EFFFF028FFFFF0290 -FFFF0291FFFF0292FFFF0293FFFF0294FFFF0295FFFF0296FFFF0297FFFF0298FFFF0299FFFF029AFFFF029BFFFF029C -FFFF029DFFFF029EFFFF029FFFFF02A0FFFF02A1FFFF02A2FFFF02A3FFFF02A4FFFF02A5FFFF02A6FFFF02A7FFFF02A8 -FFFF02A9FFFF02AAFFFF02ABFFFF02ACFFFF02ADFFFF02AEFFFF02AFFFFF02B0FFFF02B1FFFF02B2FFFF02B3FFFF02B4 -FFFF02B5FFFF02B6FFFF02B7FFFF02B8FFFF02B9FFFF02BAFFFF02BBFFFF02BCFFFF02BDFFFF02BEFFFF02BFFFFF02C0 -FFFF02C1FFFF02C2FFFF02C3FFFF02C4FFFF02C5FFFF02C6FFFF02C7FFFF02C8FFFF02C9FFFF02CAFFFF02CBFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFF02CCFFFF02CDFFFF02CEFFFF02CFFFFF02D0FFFF02D1FFFF02D2FFFF02D3FFFF02D4 -FFFF02D5FFFF02D6FFFF02D7FFFF02D8FFFF02D9FFFF02DAFFFF02DBFFFF02DCFFFF02DDFFFF02DEFFFF02DFFFFF02E0 -FFFF02E1FFFF02E2FFFF02E3FFFF02E4FFFF02E5FFFF02E6FFFF02E7FFFF02E8FFFF02E9FFFF02EAFFFF02EBFFFF02EC -FFFF02EDFFFF02EEFFFF02EFFFFF02F0FFFF02F1FFFF02F2FFFF02F3FFFF02F4FFFF02F5FFFF02F6FFFF02F7FFFF02F8 -FFFF02F9FFFF02FAFFFF02FBFFFF02FCFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF02FE02FD030002FF0302030103040303 -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0306030503080307030A0309FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -030C030B030E030D0310030F03120311FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF031403130316031503180317031A0319 -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF031C031B031E031D0320031FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -0321FFFF0322FFFF0323FFFF0324FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0326032503280327032A0329032C032B -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -032E032D0330032F0332033103340333FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0336033503380337033A0339033C033B -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF033E033D0340033F0342034103440343FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -0346034503480347FFFF0349FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF034B034A034D034CFFFF034EFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0350034F03520351FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -0354035303560355FFFF0357FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF03590358035B035AFFFF035CFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF035DFFFFFFFF035F035EFFFFFFFFFFFFFFFF -FFFFFFFFFFFF0360FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -03620361036403630366036503680367036A0369036C036B036E036D0370036FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0371FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFF03730372037503740377037603790378037B037A037D037C037F037E0381038003830382 -038503840387038603890388038B038AFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF038D038C038F038E0391039003930392039503940397039603990398039B039A -039D039C039F039E03A103A003A303A203A503A403A703A603A903A803AB03AA03AD03AC03AF03AE03B103B003B303B2 -03B503B403B703B603B903B803BB03BAFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF03BC03BE03BDFFFF03BF03C0FFFF03C1FFFF03C2FFFF03C3FFFF03C503C4 -FFFF03C6FFFF03C703C8FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF03CA03C9FFFF03CBFFFF03CCFFFF03CDFFFF03CE -FFFF03CFFFFF03D0FFFF03D1FFFF03D2FFFF03D3FFFF03D4FFFF03D5FFFF03D6FFFF03D7FFFF03D8FFFF03D9FFFF03DA -FFFF03DBFFFF03DCFFFF03DDFFFF03DEFFFF03DFFFFF03E0FFFF03E1FFFF03E2FFFF03E3FFFF03E4FFFF03E5FFFF03E6 -FFFF03E7FFFF03E8FFFF03E9FFFF03EAFFFF03EBFFFF03ECFFFF03EDFFFF03EEFFFF03EFFFFF03F0FFFF03F1FFFF03F2 -FFFF03F3FFFF03F4FFFF03F5FFFF03F6FFFF03F7FFFF03F8FFFF03F9FFFF03FAFFFF03FBFFFF03FCFFFFFFFFFFFFFFFF -FFFFFFFF03FDFFFF03FEFFFFFFFFFFFFFFFFFFFFFFFF03FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFF0400FFFF0401FFFF0402FFFF0403FFFF0404FFFF0405FFFF0406FFFF0407FFFF0408FFFF0409FFFF040AFFFF040B -FFFF040CFFFF040DFFFF040EFFFF040FFFFF0410FFFF0411FFFF0412FFFF0413FFFF0414FFFF0415FFFF0416FFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0417FFFF0418FFFF0419FFFF041A -FFFF041BFFFF041CFFFF041DFFFF041EFFFF041FFFFF0420FFFF0421FFFF0422FFFF0423FFFF0424FFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0425FFFF0426FFFF0427 -FFFF0428FFFF0429FFFF042AFFFF042BFFFFFFFFFFFF042CFFFF042DFFFF042EFFFF042FFFFF0430FFFF0431FFFF0432 -FFFF0433FFFF0434FFFF0435FFFF0436FFFF0437FFFF0438FFFF0439FFFF043AFFFF043BFFFF043CFFFF043DFFFF043E -FFFF043FFFFF0440FFFF0441FFFF0442FFFF0443FFFF0444FFFF0445FFFF0446FFFF0447FFFF0448FFFF0449FFFF044A -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF044BFFFF044CFFFF044DFFFFFFFF044EFFFF044FFFFF0450FFFF0451FFFF0452 -FFFFFFFF0453FFFF0454FFFFFFFFFFFFFFFF0455FFFF0456FFFFFFFFFFFF0457FFFF0458FFFF0459FFFF045AFFFF045B -FFFF045CFFFF045DFFFF045EFFFF045FFFFF04600462046104640463FFFF04650467046604690468FFFF046AFFFF046B -FFFF046CFFFF046DFFFF046EFFFF046FFFFF0470FFFF047104730472047504740476FFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFF0477FFFFFFFFFFFFFFFFFFFF0478FFFF0479FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF047AFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF047BFFFF047D047C047F047E0481048004830482048504840487048604890488 -048B048A048D048C048F048E0491049004930492FFFF0494FFFFFFFFFFFFFFFF0496049504980497049A0499049C049B -049E049D04A0049F04A204A104A404A304A604A504A804A704AA04A904AC04AB04AE04AD04B004AF04B204B104B404B3 -04B604B504B804B704BA04B904BC04BBFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF04BE04BD04C004BF04C204C104C404C304C604C504C804C704CA04C904CC04CB -04CE04CD04D004CF04D204D104D404D304D604D504D804D704DA04D904DC04DB04DE04DD04E004DFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF04E204E104E404E304E604E504E804E7 -04EA04E9FFFF04EB04ED04EC04EF04EE04F104F004F304F204F504F404F704F604F904F8FFFF04FA04FC04FB04FE04FD -050004FFFFFF050105030502FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -050505040507050605090508050B050A050D050C050F050E0511051005130512051505140517051605190518051B051A -051D051C051F051E0521052005230522052505240527052605290528052B052A052D052C052F052E0531053005330532 -05350534FFFF0536FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -05380537053A0539053C053B053E053D0540053F05420541054405430546054505480547054A0549054C054B054E054D -0550054F05520551055405530556055505580557055A0559055C055B055E055D0560055F056205610564056305660565 -05680567056A0569056C056B056E056D0570056F057205710574057305760575FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -05780577057A0579057C057B057E057D0580057F05820581058405830586058505880587058A0589058C058B058E058D -0590058F05920591059405930596059505980597FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF", -); -//10368 bytes -enum toTitleSimpleIndexTrieEntries = TrieEntry!(ushort, 8, 7, 6)(x" -000000000000004000000280", -x" -000001000000048000000F40", -x" -020201000402030206020205070202020202020202020202020202020202020202020202020202020202020202020202 -020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202 -020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202 -020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202 -020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202 -0202020202020202020202020202020200010000000300020005000400070006000900080000000A000B0000000D000C -000F000E0011001000130012000000140000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000015000000000000000000000000000000000000000000000016000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -00000000000000170018000000000019001B001A001D001C001F001E0021002000000000000000000022000000000023 -000000000000000000000000000000000000000000240000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000260025002800270000002900000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -00000000000000000000000000000000002A00000000002B002D002C002F002E00000000000000000000000000000000 -000000000000000000300000000000310000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000032000000000000 -000000000000000000000000000000000000000000000000000000000000000000340033003500000000000000000036 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000037000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000038000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000390000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -0000000000000000003B003A000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -00000000000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0000FFFF00020001000400030006000500080007000A0009000C000B000E000D -0010000F00120011001400130016001500180017FFFF0019FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF001AFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF001C001B001E001D0020001F00220021 -002400230026002500280027002A0029002C002B002E002D0030002FFFFF003100330032003500340037003600390038 -003AFFFF003BFFFF003CFFFF003DFFFF003EFFFF003FFFFF0040FFFF0041FFFF0042FFFF0043FFFF0044FFFF0045FFFF -0046FFFF0047FFFF0048FFFF0049FFFF004AFFFF004BFFFF004CFFFF004DFFFF004EFFFF004FFFFF0050FFFF0051FFFF -0052FFFF0053FFFF0054FFFF0055FFFFFFFFFFFFFFFF0056FFFF0057FFFF0058FFFF0059FFFF005AFFFF005BFFFF005C -FFFF005D005EFFFF005FFFFF0060FFFF0061FFFF0062FFFF0063FFFF0064FFFF0065FFFF0066FFFF0067FFFF0068FFFF -0069FFFF006AFFFF006BFFFF006CFFFF006DFFFF006EFFFF006FFFFF0070FFFF0071FFFF0072FFFF0073FFFF0074FFFF -FFFFFFFFFFFF0075FFFF007600780077FFFF0079007AFFFF007BFFFFFFFFFFFFFFFF007CFFFFFFFFFFFF007DFFFFFFFF -FFFFFFFFFFFF007E007FFFFFFFFFFFFF0080FFFFFFFF0081FFFFFFFFFFFF00820083FFFF0084FFFF0085FFFFFFFFFFFF -FFFF0086FFFFFFFF0087FFFFFFFFFFFFFFFF0088FFFFFFFFFFFF0089FFFF008A008BFFFFFFFFFFFF008CFFFF008DFFFF -FFFFFFFFFFFFFFFF008F008E009100900093009200950094FFFF0096FFFF0097FFFF0098FFFF0099FFFF009AFFFF009B -FFFF009CFFFF009D009F009E00A0FFFF00A1FFFF00A2FFFF00A3FFFF00A4FFFF00A5FFFF00A6FFFF00A7FFFF00A8FFFF -00A9FFFF00AB00AA00ACFFFFFFFFFFFF00ADFFFF00AEFFFF00AFFFFF00B0FFFF00B1FFFF00B2FFFF00B3FFFF00B4FFFF -00B5FFFF00B6FFFF00B7FFFF00B8FFFF00B9FFFF00BAFFFF00BBFFFF00BCFFFF00BDFFFF00BEFFFF00BFFFFF00C0FFFF -FFFFFFFF00C1FFFF00C2FFFF00C3FFFF00C4FFFF00C5FFFF00C6FFFF00C7FFFF00C8FFFF00C9FFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFF00CA00CBFFFFFFFF00CCFFFF00CDFFFFFFFF00CEFFFF00CFFFFF00D0FFFF00D1FFFF00D2FFFF -00D400D300D600D5FFFF00D700D900D800DAFFFF00DBFFFFFFFF00DCFFFFFFFF00DE00DD00DFFFFF00E0FFFFFFFF00E1 -00E300E200E500E4FFFF00E600E7FFFF00E8FFFFFFFF00E900EAFFFFFFFFFFFFFFFFFFFFFFFFFFFF00EBFFFFFFFFFFFF -FFFF00EC00EE00EDFFFFFFFF00EFFFFF00F100F000F300F2FFFF00F4FFFFFFFFFFFFFFFFFFFF00F5FFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFF00F6FFFFFFFF00F7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00F8FFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00F9FFFF00FAFFFFFFFFFFFF00FBFFFF -FFFFFFFF00FCFFFF00FE00FDFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFF010000FF010201010103FFFF010501040107010601090108010B010A010D010C010F010E01110110 -01130112011501140117011601190118011B011A011D011C011F011EFFFF012001220121FFFFFFFF0123FFFF01250124 -0126FFFF0127FFFF0128FFFF0129FFFF012AFFFF012BFFFF012CFFFF012DFFFF012EFFFF012FFFFF0130FFFF0131FFFF -01330132013501340136FFFFFFFFFFFFFFFF01370138FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF013A0139013C013B013E013D0140013F -01420141014401430146014501480147014A0149014C014B014E014D0150014F01520151015401530156015501580157 -015A0159015C015B015E015D0160015F016201610164016301660165016801670169FFFF016AFFFF016BFFFF016CFFFF -016DFFFF016EFFFF016FFFFF0170FFFF0171FFFF0172FFFF0173FFFF0174FFFF0175FFFF0176FFFF0177FFFF0178FFFF -0179FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF017AFFFF017BFFFF017CFFFF017DFFFF017EFFFF017FFFFF0180FFFF -0181FFFF0182FFFF0183FFFF0184FFFF0185FFFF0186FFFF0187FFFF0188FFFF0189FFFF018AFFFF018BFFFF018CFFFF -018DFFFF018EFFFF018FFFFF0190FFFF0191FFFF0192FFFF0193FFFF0194FFFFFFFFFFFFFFFF0195FFFF0196FFFF0197 -FFFF0198FFFF0199FFFF019A019C019B019DFFFF019EFFFF019FFFFF01A0FFFF01A1FFFF01A2FFFF01A3FFFF01A4FFFF -01A5FFFF01A6FFFF01A7FFFF01A8FFFF01A9FFFF01AAFFFF01ABFFFF01ACFFFF01ADFFFF01AEFFFF01AFFFFF01B0FFFF -01B1FFFF01B2FFFF01B3FFFF01B4FFFF01B5FFFF01B6FFFF01B7FFFF01B8FFFF01B9FFFF01BAFFFF01BBFFFF01BCFFFF -01BDFFFF01BEFFFF01BFFFFF01C0FFFF01C1FFFF01C2FFFF01C3FFFF01C4FFFF01C5FFFF01C6FFFF01C7FFFF01C8FFFF -01C9FFFF01CAFFFF01CBFFFF01CCFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF01CDFFFF01CF01CE01D101D001D301D201D501D401D701D601D901D801DB01DA -01DD01DC01DF01DE01E101E001E301E201E501E401E701E601E901E801EB01EA01ED01EC01EF01EE01F101F0FFFF01F2 -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -01F401F301F601F501F801F701FA01F901FC01FB01FE01FD020001FF02020201020402030206020502080207020A0209 -020C020B020E020D0210020F02120211021402130216021502180217021A0219021C021BFFFF021D021EFFFF0220021F -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF022202210224022302260225FFFFFFFF02280227022A0229022C022B022E022D -FFFF022FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0230FFFFFFFFFFFF0231FFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0232FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0233FFFF0234FFFF0235FFFF0236FFFF -0237FFFF0238FFFF0239FFFF023AFFFF023BFFFF023CFFFF023DFFFF023EFFFF023FFFFF0240FFFF0241FFFF0242FFFF -0243FFFF0244FFFF0245FFFF0246FFFF0247FFFF0248FFFF0249FFFF024AFFFF024BFFFF024CFFFF024DFFFF024EFFFF -024FFFFF0250FFFF0251FFFF0252FFFF0253FFFF0254FFFF0255FFFF0256FFFF0257FFFF0258FFFF0259FFFF025AFFFF -025BFFFF025CFFFF025DFFFF025EFFFF025FFFFF0260FFFF0261FFFF0262FFFF0263FFFF0264FFFF0265FFFF0266FFFF -0267FFFF0268FFFF0269FFFF026AFFFF026BFFFF026CFFFF026DFFFF026EFFFF026FFFFF0270FFFF0271FFFF0272FFFF -0273FFFF0274FFFF0275FFFF0276FFFF0277FFFF0278FFFF0279FFFF027AFFFF027BFFFF027CFFFF027DFFFFFFFFFFFF -FFFFFFFF027EFFFFFFFFFFFFFFFFFFFF027FFFFF0280FFFF0281FFFF0282FFFF0283FFFF0284FFFF0285FFFF0286FFFF -0287FFFF0288FFFF0289FFFF028AFFFF028BFFFF028CFFFF028DFFFF028EFFFF028FFFFF0290FFFF0291FFFF0292FFFF -0293FFFF0294FFFF0295FFFF0296FFFF0297FFFF0298FFFF0299FFFF029AFFFF029BFFFF029CFFFF029DFFFF029EFFFF -029FFFFF02A0FFFF02A1FFFF02A2FFFF02A3FFFF02A4FFFF02A5FFFF02A6FFFF02A7FFFF02A8FFFF02A9FFFF02AAFFFF -02ABFFFF02ACFFFF02ADFFFF02AEFFFF02B002AF02B202B102B402B302B602B5FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -02B802B702BA02B902BC02BBFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF02BE02BD02C002BF02C202C102C402C3 -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF02C602C502C802C702CA02C902CC02CBFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -02CE02CD02D002CF02D202D1FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF02D3FFFF02D4FFFF02D5FFFF02D6FFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF02D802D702DA02D902DC02DB02DE02DDFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -02E002DF02E202E102E402E302E602E502E802E702EA02E902EC02EBFFFFFFFF02EE02ED02F002EF02F202F102F402F3 -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF02F602F502F802F702FA02F902FC02FBFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -02FE02FD030002FF0302030103040303FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF030603050307FFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFF0308FFFFFFFF0309FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -030B030AFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF030D030CFFFFFFFF030EFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF030FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0310FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -03120311031403130316031503180317031A0319031C031B031E031D0320031FFFFFFFFFFFFFFFFFFFFF0321FFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -03230322032503240327032603290328032B032A032D032C032F032E0331033003330332033503340337033603390338 -033B033AFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -033D033C033F033E0341034003430342034503440347034603490348034B034A034D034C034F034E0351035003530352 -035503540357035603590358035B035A035D035C035F035E0361036003630362036503640367036603690368036B036A -036CFFFFFFFFFFFF036DFFFFFFFF036EFFFF036FFFFF0370FFFF0371FFFFFFFFFFFFFFFF0372FFFFFFFFFFFFFFFF0373 -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0374FFFF0375FFFF0376FFFF0377FFFF0378FFFF0379FFFF037AFFFF037BFFFF -037CFFFF037DFFFF037EFFFF037FFFFF0380FFFF0381FFFF0382FFFF0383FFFF0384FFFF0385FFFF0386FFFF0387FFFF -0388FFFF0389FFFF038AFFFF038BFFFF038CFFFF038DFFFF038EFFFF038FFFFF0390FFFF0391FFFF0392FFFF0393FFFF -0394FFFF0395FFFF0396FFFF0397FFFF0398FFFF0399FFFF039AFFFF039BFFFF039CFFFF039DFFFF039EFFFF039FFFFF -03A0FFFF03A1FFFF03A2FFFF03A3FFFF03A4FFFF03A5FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF03A6FFFF03A7 -FFFFFFFF03A8FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF03AA03A903AC03AB03AE03AD03B003AF -03B203B103B403B303B603B503B803B703BA03B903BC03BB03BE03BD03C003BF03C203C103C403C303C603C503C803C7 -03CA03C903CC03CB03CE03CD03CFFFFFFFFFFFFFFFFFFFFF03D0FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF03D1FFFF03D2FFFF03D3FFFF03D4FFFF03D5FFFF03D6FFFF03D7FFFF03D8FFFF -03D9FFFF03DAFFFF03DBFFFF03DCFFFF03DDFFFF03DEFFFF03DFFFFF03E0FFFF03E1FFFF03E2FFFF03E3FFFF03E4FFFF -03E5FFFF03E6FFFF03E7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -03E8FFFF03E9FFFF03EAFFFF03EBFFFF03ECFFFF03EDFFFF03EEFFFF03EFFFFF03F0FFFF03F1FFFF03F2FFFF03F3FFFF -03F4FFFF03F5FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFF03F6FFFF03F7FFFF03F8FFFF03F9FFFF03FAFFFF03FBFFFF03FCFFFFFFFFFFFF03FDFFFF03FEFFFF03FFFFFF -0400FFFF0401FFFF0402FFFF0403FFFF0404FFFF0405FFFF0406FFFF0407FFFF0408FFFF0409FFFF040AFFFF040BFFFF -040CFFFF040DFFFF040EFFFF040FFFFF0410FFFF0411FFFF0412FFFF0413FFFF0414FFFF0415FFFF0416FFFF0417FFFF -0418FFFF0419FFFF041AFFFF041BFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF041CFFFF041D041EFFFF -041FFFFF0420FFFF0421FFFF0422FFFFFFFFFFFFFFFFFFFFFFFF0423FFFFFFFF0424FFFF0425FFFFFFFF04260427FFFF -0428FFFF0429FFFF042AFFFF042BFFFF042CFFFF042DFFFF042EFFFF042FFFFF0430FFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFF0431FFFF0432FFFF0433FFFF0434FFFF0435FFFF0436FFFF0437FFFF0438FFFFFFFFFFFFFFFFFFFF -FFFF0439FFFF043AFFFFFFFFFFFFFFFF043BFFFFFFFFFFFFFFFFFFFF043CFFFF043DFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF043E -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFF043FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0441044004430442044504440447044604490448044B044A044D044C044F044E -0451045004530452045504540457045604590458045B045A045D045C045F045E04610460046304620465046404670466 -04690468046B046A046D046C046F046E0471047004730472047504740477047604790478047B047A047D047C047F047E -0481048004830482048504840487048604890488048B048A048D048C048F048E0490FFFF049204910494049304960495 -04980497049A0499049C049B049E049D04A0049F04A204A104A404A304A604A504A804A7FFFF04A9FFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -04AB04AA04AD04AC04AF04AE04B104B004B304B204B504B404B704B604B904B804BB04BA04BD04BC04BF04BE04C104C0 -04C304C204C504C404C704C604C904C804CB04CA04CD04CC04CF04CE04D104D0FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF04D304D204D504D404D704D604D904D8 -04DB04DA04DD04DC04DF04DE04E104E004E304E204E504E404E704E604E904E804EB04EA04ED04EC04EF04EE04F104F0 -04F304F204F504F4FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFF04F6FFFF04F804F704FA04F904FC04FB04FE04FD050004FF0501FFFF0503050205050504 -0507050605090508050B050A050D050C050F050E0510FFFF0512051105140513051605150517FFFFFFFF0518FFFFFFFF -051A0519051C051B051E051D0520051F05220521052405230526052505280527052A0529052C052B052E052D0530052F -05320531053405330536053505380537053A0539053C053B053E053D0540053F05420541054405430546054505480547 -054A0549FFFF054BFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF054D054C054F054E0551055005530552 -055505540557055605590558055B055A055D055C055F055E0561056005630562056505640567056605690568056B056A -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF056D056C056F056E0571057005730572 -057505740577057605790578057B057A057D057C057F057E0581058005830582058505840587058605890588058B058A -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF058D058C058F058E0591059005930592059505940597059605990598 -059B059A059D059C059F059E05A105A005A305A205A505A405A705A605A905A805AB05AA05AD05ACFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF", -); -immutable(uint)[] toUpperTable() nothrow @nogc pure @safe { -static immutable uint[] t = -x" -0000004100000042000000430000004400000045000000460000004700000048000000490000004A0000004B0000004C -0000004D0000004E0000004F000000500000005100000052000000530000005400000055000000560000005700000058 -000000590000005A0000039C000000C0000000C1000000C2000000C3000000C4000000C5000000C6000000C7000000C8 -000000C9000000CA000000CB000000CC000000CD000000CE000000CF000000D0000000D1000000D2000000D3000000D4 -000000D5000000D6000000D8000000D9000000DA000000DB000000DC000000DD000000DE000001780000010000000102 -0000010400000106000001080000010A0000010C0000010E00000110000001120000011400000116000001180000011A -0000011C0000011E00000120000001220000012400000126000001280000012A0000012C0000012E0000004900000132 -0000013400000136000001390000013B0000013D0000013F000001410000014300000145000001470000014A0000014C -0000014E00000150000001520000015400000156000001580000015A0000015C0000015E000001600000016200000164 -00000166000001680000016A0000016C0000016E00000170000001720000017400000176000001790000017B0000017D -00000053000002430000018200000184000001870000018B00000191000001F6000001980000023D00000220000001A0 -000001A2000001A4000001A7000001AC000001AF000001B3000001B5000001B8000001BC000001F7000001C4000001C4 -000001C7000001C7000001CA000001CA000001CD000001CF000001D1000001D3000001D5000001D7000001D9000001DB -0000018E000001DE000001E0000001E2000001E4000001E6000001E8000001EA000001EC000001EE000001F1000001F1 -000001F4000001F8000001FA000001FC000001FE00000200000002020000020400000206000002080000020A0000020C -0000020E00000210000002120000021400000216000002180000021A0000021C0000021E000002220000022400000226 -000002280000022A0000022C0000022E00000230000002320000023B00002C7E00002C7F000002410000024600000248 -0000024A0000024C0000024E00002C6F00002C6D00002C700000018100000186000001890000018A0000018F00000190 -0000A7AB000001930000A7AC000001940000A78D0000A7AA00000197000001960000A7AE00002C620000A7AD0000019C -00002C6E0000019D0000019F00002C64000001A60000A7C5000001A90000A7B1000001AE00000244000001B1000001B2 -00000245000001B70000A7B20000A7B000000399000003700000037200000376000003FD000003FE000003FF00000386 -00000388000003890000038A000003910000039200000393000003940000039500000396000003970000039800000399 -0000039A0000039B0000039C0000039D0000039E0000039F000003A0000003A1000003A3000003A3000003A4000003A5 -000003A6000003A7000003A8000003A9000003AA000003AB0000038C0000038E0000038F0000039200000398000003A6 -000003A0000003CF000003D8000003DA000003DC000003DE000003E0000003E2000003E4000003E6000003E8000003EA -000003EC000003EE0000039A000003A1000003F90000037F00000395000003F7000003FA000004100000041100000412 -000004130000041400000415000004160000041700000418000004190000041A0000041B0000041C0000041D0000041E -0000041F000004200000042100000422000004230000042400000425000004260000042700000428000004290000042A -0000042B0000042C0000042D0000042E0000042F00000400000004010000040200000403000004040000040500000406 -0000040700000408000004090000040A0000040B0000040C0000040D0000040E0000040F000004600000046200000464 -00000466000004680000046A0000046C0000046E00000470000004720000047400000476000004780000047A0000047C -0000047E000004800000048A0000048C0000048E00000490000004920000049400000496000004980000049A0000049C -0000049E000004A0000004A2000004A4000004A6000004A8000004AA000004AC000004AE000004B0000004B2000004B4 -000004B6000004B8000004BA000004BC000004BE000004C1000004C3000004C5000004C7000004C9000004CB000004CD -000004C0000004D0000004D2000004D4000004D6000004D8000004DA000004DC000004DE000004E0000004E2000004E4 -000004E6000004E8000004EA000004EC000004EE000004F0000004F2000004F4000004F6000004F8000004FA000004FC -000004FE00000500000005020000050400000506000005080000050A0000050C0000050E000005100000051200000514 -00000516000005180000051A0000051C0000051E00000520000005220000052400000526000005280000052A0000052C -0000052E0000053100000532000005330000053400000535000005360000053700000538000005390000053A0000053B -0000053C0000053D0000053E0000053F0000054000000541000005420000054300000544000005450000054600000547 -00000548000005490000054A0000054B0000054C0000054D0000054E0000054F00000550000005510000055200000553 -00000554000005550000055600001C9000001C9100001C9200001C9300001C9400001C9500001C9600001C9700001C98 -00001C9900001C9A00001C9B00001C9C00001C9D00001C9E00001C9F00001CA000001CA100001CA200001CA300001CA4 -00001CA500001CA600001CA700001CA800001CA900001CAA00001CAB00001CAC00001CAD00001CAE00001CAF00001CB0 -00001CB100001CB200001CB300001CB400001CB500001CB600001CB700001CB800001CB900001CBA00001CBD00001CBE -00001CBF000013F0000013F1000013F2000013F3000013F4000013F500000412000004140000041E0000042100000422 -000004220000042A000004620000A64A0000A77D00002C630000A7C600001E0000001E0200001E0400001E0600001E08 -00001E0A00001E0C00001E0E00001E1000001E1200001E1400001E1600001E1800001E1A00001E1C00001E1E00001E20 -00001E2200001E2400001E2600001E2800001E2A00001E2C00001E2E00001E3000001E3200001E3400001E3600001E38 -00001E3A00001E3C00001E3E00001E4000001E4200001E4400001E4600001E4800001E4A00001E4C00001E4E00001E50 -00001E5200001E5400001E5600001E5800001E5A00001E5C00001E5E00001E6000001E6200001E6400001E6600001E68 -00001E6A00001E6C00001E6E00001E7000001E7200001E7400001E7600001E7800001E7A00001E7C00001E7E00001E80 -00001E8200001E8400001E8600001E8800001E8A00001E8C00001E8E00001E9000001E9200001E9400001E6000001EA0 -00001EA200001EA400001EA600001EA800001EAA00001EAC00001EAE00001EB000001EB200001EB400001EB600001EB8 -00001EBA00001EBC00001EBE00001EC000001EC200001EC400001EC600001EC800001ECA00001ECC00001ECE00001ED0 -00001ED200001ED400001ED600001ED800001EDA00001EDC00001EDE00001EE000001EE200001EE400001EE600001EE8 -00001EEA00001EEC00001EEE00001EF000001EF200001EF400001EF600001EF800001EFA00001EFC00001EFE00001F08 -00001F0900001F0A00001F0B00001F0C00001F0D00001F0E00001F0F00001F1800001F1900001F1A00001F1B00001F1C -00001F1D00001F2800001F2900001F2A00001F2B00001F2C00001F2D00001F2E00001F2F00001F3800001F3900001F3A -00001F3B00001F3C00001F3D00001F3E00001F3F00001F4800001F4900001F4A00001F4B00001F4C00001F4D00001F59 -00001F5B00001F5D00001F5F00001F6800001F6900001F6A00001F6B00001F6C00001F6D00001F6E00001F6F00001FBA -00001FBB00001FC800001FC900001FCA00001FCB00001FDA00001FDB00001FF800001FF900001FEA00001FEB00001FFA -00001FFB00001F8800001F8900001F8A00001F8B00001F8C00001F8D00001F8E00001F8F00001F9800001F9900001F9A -00001F9B00001F9C00001F9D00001F9E00001F9F00001FA800001FA900001FAA00001FAB00001FAC00001FAD00001FAE -00001FAF00001FB800001FB900001FBC0000039900001FCC00001FD800001FD900001FE800001FE900001FEC00001FFC -00002132000021600000216100002162000021630000216400002165000021660000216700002168000021690000216A -0000216B0000216C0000216D0000216E0000216F00002183000024B6000024B7000024B8000024B9000024BA000024BB -000024BC000024BD000024BE000024BF000024C0000024C1000024C2000024C3000024C4000024C5000024C6000024C7 -000024C8000024C9000024CA000024CB000024CC000024CD000024CE000024CF00002C0000002C0100002C0200002C03 -00002C0400002C0500002C0600002C0700002C0800002C0900002C0A00002C0B00002C0C00002C0D00002C0E00002C0F -00002C1000002C1100002C1200002C1300002C1400002C1500002C1600002C1700002C1800002C1900002C1A00002C1B -00002C1C00002C1D00002C1E00002C1F00002C2000002C2100002C2200002C2300002C2400002C2500002C2600002C27 -00002C2800002C2900002C2A00002C2B00002C2C00002C2D00002C2E00002C2F00002C600000023A0000023E00002C67 -00002C6900002C6B00002C7200002C7500002C8000002C8200002C8400002C8600002C8800002C8A00002C8C00002C8E -00002C9000002C9200002C9400002C9600002C9800002C9A00002C9C00002C9E00002CA000002CA200002CA400002CA6 -00002CA800002CAA00002CAC00002CAE00002CB000002CB200002CB400002CB600002CB800002CBA00002CBC00002CBE -00002CC000002CC200002CC400002CC600002CC800002CCA00002CCC00002CCE00002CD000002CD200002CD400002CD6 -00002CD800002CDA00002CDC00002CDE00002CE000002CE200002CEB00002CED00002CF2000010A0000010A1000010A2 -000010A3000010A4000010A5000010A6000010A7000010A8000010A9000010AA000010AB000010AC000010AD000010AE -000010AF000010B0000010B1000010B2000010B3000010B4000010B5000010B6000010B7000010B8000010B9000010BA -000010BB000010BC000010BD000010BE000010BF000010C0000010C1000010C2000010C3000010C4000010C5000010C7 -000010CD0000A6400000A6420000A6440000A6460000A6480000A64A0000A64C0000A64E0000A6500000A6520000A654 -0000A6560000A6580000A65A0000A65C0000A65E0000A6600000A6620000A6640000A6660000A6680000A66A0000A66C -0000A6800000A6820000A6840000A6860000A6880000A68A0000A68C0000A68E0000A6900000A6920000A6940000A696 -0000A6980000A69A0000A7220000A7240000A7260000A7280000A72A0000A72C0000A72E0000A7320000A7340000A736 -0000A7380000A73A0000A73C0000A73E0000A7400000A7420000A7440000A7460000A7480000A74A0000A74C0000A74E -0000A7500000A7520000A7540000A7560000A7580000A75A0000A75C0000A75E0000A7600000A7620000A7640000A766 -0000A7680000A76A0000A76C0000A76E0000A7790000A77B0000A77E0000A7800000A7820000A7840000A7860000A78B -0000A7900000A7920000A7C40000A7960000A7980000A79A0000A79C0000A79E0000A7A00000A7A20000A7A40000A7A6 -0000A7A80000A7B40000A7B60000A7B80000A7BA0000A7BC0000A7BE0000A7C00000A7C20000A7C70000A7C90000A7D0 -0000A7D60000A7D80000A7F50000A7B3000013A0000013A1000013A2000013A3000013A4000013A5000013A6000013A7 -000013A8000013A9000013AA000013AB000013AC000013AD000013AE000013AF000013B0000013B1000013B2000013B3 -000013B4000013B5000013B6000013B7000013B8000013B9000013BA000013BB000013BC000013BD000013BE000013BF -000013C0000013C1000013C2000013C3000013C4000013C5000013C6000013C7000013C8000013C9000013CA000013CB -000013CC000013CD000013CE000013CF000013D0000013D1000013D2000013D3000013D4000013D5000013D6000013D7 -000013D8000013D9000013DA000013DB000013DC000013DD000013DE000013DF000013E0000013E1000013E2000013E3 -000013E4000013E5000013E6000013E7000013E8000013E9000013EA000013EB000013EC000013ED000013EE000013EF -0000FF210000FF220000FF230000FF240000FF250000FF260000FF270000FF280000FF290000FF2A0000FF2B0000FF2C -0000FF2D0000FF2E0000FF2F0000FF300000FF310000FF320000FF330000FF340000FF350000FF360000FF370000FF38 -0000FF390000FF3A00010400000104010001040200010403000104040001040500010406000104070001040800010409 -0001040A0001040B0001040C0001040D0001040E0001040F000104100001041100010412000104130001041400010415 -000104160001041700010418000104190001041A0001041B0001041C0001041D0001041E0001041F0001042000010421 -000104220001042300010424000104250001042600010427000104B0000104B1000104B2000104B3000104B4000104B5 -000104B6000104B7000104B8000104B9000104BA000104BB000104BC000104BD000104BE000104BF000104C0000104C1 -000104C2000104C3000104C4000104C5000104C6000104C7000104C8000104C9000104CA000104CB000104CC000104CD -000104CE000104CF000104D0000104D1000104D2000104D3000105700001057100010572000105730001057400010575 -000105760001057700010578000105790001057A0001057C0001057D0001057E0001057F000105800001058100010582 -000105830001058400010585000105860001058700010588000105890001058A0001058C0001058D0001058E0001058F -000105900001059100010592000105940001059500010C8000010C8100010C8200010C8300010C8400010C8500010C86 -00010C8700010C8800010C8900010C8A00010C8B00010C8C00010C8D00010C8E00010C8F00010C9000010C9100010C92 -00010C9300010C9400010C9500010C9600010C9700010C9800010C9900010C9A00010C9B00010C9C00010C9D00010C9E -00010C9F00010CA000010CA100010CA200010CA300010CA400010CA500010CA600010CA700010CA800010CA900010CAA -00010CAB00010CAC00010CAD00010CAE00010CAF00010CB000010CB100010CB2000118A0000118A1000118A2000118A3 -000118A4000118A5000118A6000118A7000118A8000118A9000118AA000118AB000118AC000118AD000118AE000118AF -000118B0000118B1000118B2000118B3000118B4000118B5000118B6000118B7000118B8000118B9000118BA000118BB -000118BC000118BD000118BE000118BF00016E4000016E4100016E4200016E4300016E4400016E4500016E4600016E47 -00016E4800016E4900016E4A00016E4B00016E4C00016E4D00016E4E00016E4F00016E5000016E5100016E5200016E53 -00016E5400016E5500016E5600016E5700016E5800016E5900016E5A00016E5B00016E5C00016E5D00016E5E00016E5F -0001E9000001E9010001E9020001E9030001E9040001E9050001E9060001E9070001E9080001E9090001E90A0001E90B -0001E90C0001E90D0001E90E0001E90F0001E9100001E9110001E9120001E9130001E9140001E9150001E9160001E917 -0001E9180001E9190001E91A0001E91B0001E91C0001E91D0001E91E0001E91F0001E9200001E9210200005300000053 -0000013002000046000000460200004600000049020000460000004C0300004600000046000000490300004600000046 -0000004C0200005300000054020000530000005402000535000005520200054400000546020005440000053502000544 -0000053B0200054E00000546020005440000053D020002BC0000004E030003990000030800000301030003A500000308 -000003010200004A0000030C02000048000003310200005400000308020000570000030A020000590000030A02000041 -000002BE020003A500000313030003A50000031300000300030003A50000031300000301030003A50000031300000342 -020003910000034202000397000003420300039900000308000003000300039900000308000003010200039900000342 -030003990000030800000342030003A50000030800000300030003A50000030800000301020003A100000313020003A5 -00000342030003A50000030800000342020003A90000034202001F080000039902001F090000039902001F0A00000399 -02001F0B0000039902001F0C0000039902001F0D0000039902001F0E0000039902001F0F0000039902001F0800000399 -02001F090000039902001F0A0000039902001F0B0000039902001F0C0000039902001F0D0000039902001F0E00000399 -02001F0F0000039902001F280000039902001F290000039902001F2A0000039902001F2B0000039902001F2C00000399 -02001F2D0000039902001F2E0000039902001F2F0000039902001F280000039902001F290000039902001F2A00000399 -02001F2B0000039902001F2C0000039902001F2D0000039902001F2E0000039902001F2F0000039902001F6800000399 -02001F690000039902001F6A0000039902001F6B0000039902001F6C0000039902001F6D0000039902001F6E00000399 -02001F6F0000039902001F680000039902001F690000039902001F6A0000039902001F6B0000039902001F6C00000399 -02001F6D0000039902001F6E0000039902001F6F00000399020003910000039902000391000003990200039700000399 -0200039700000399020003A900000399020003A90000039902001FBA00000399020003860000039902001FCA00000399 -020003890000039902001FFA000003990200038F00000399030003910000034200000399030003970000034200000399 -030003A90000034200000399"; -return t; -} -immutable(uint)[] toLowerTable() nothrow @nogc pure @safe { -static immutable uint[] t = -x" -0000006100000062000000630000006400000065000000660000006700000068000000690000006A0000006B0000006C -0000006D0000006E0000006F000000700000007100000072000000730000007400000075000000760000007700000078 -000000790000007A000000E0000000E1000000E2000000E3000000E4000000E5000000E6000000E7000000E8000000E9 -000000EA000000EB000000EC000000ED000000EE000000EF000000F0000000F1000000F2000000F3000000F4000000F5 -000000F6000000F8000000F9000000FA000000FB000000FC000000FD000000FE00000101000001030000010500000107 -000001090000010B0000010D0000010F00000111000001130000011500000117000001190000011B0000011D0000011F -00000121000001230000012500000127000001290000012B0000012D0000012F00000069000001330000013500000137 -0000013A0000013C0000013E00000140000001420000014400000146000001480000014B0000014D0000014F00000151 -000001530000015500000157000001590000015B0000015D0000015F0000016100000163000001650000016700000169 -0000016B0000016D0000016F00000171000001730000017500000177000000FF0000017A0000017C0000017E00000253 -0000018300000185000002540000018800000256000002570000018C000001DD000002590000025B0000019200000260 -000002630000026900000268000001990000026F0000027200000275000001A1000001A3000001A500000280000001A8 -00000283000001AD00000288000001B00000028A0000028B000001B4000001B600000292000001B9000001BD000001C6 -000001C6000001C9000001C9000001CC000001CC000001CE000001D0000001D2000001D4000001D6000001D8000001DA -000001DC000001DF000001E1000001E3000001E5000001E7000001E9000001EB000001ED000001EF000001F3000001F3 -000001F500000195000001BF000001F9000001FB000001FD000001FF0000020100000203000002050000020700000209 -0000020B0000020D0000020F00000211000002130000021500000217000002190000021B0000021D0000021F0000019E -000002230000022500000227000002290000022B0000022D0000022F000002310000023300002C650000023C0000019A -00002C660000024200000180000002890000028C00000247000002490000024B0000024D0000024F0000037100000373 -00000377000003F3000003AC000003AD000003AE000003AF000003CC000003CD000003CE000003B1000003B2000003B3 -000003B4000003B5000003B6000003B7000003B8000003B9000003BA000003BB000003BC000003BD000003BE000003BF -000003C0000003C1000003C3000003C4000003C5000003C6000003C7000003C8000003C9000003CA000003CB000003D7 -000003D9000003DB000003DD000003DF000003E1000003E3000003E5000003E7000003E9000003EB000003ED000003EF -000003B8000003F8000003F2000003FB0000037B0000037C0000037D0000045000000451000004520000045300000454 -00000455000004560000045700000458000004590000045A0000045B0000045C0000045D0000045E0000045F00000430 -0000043100000432000004330000043400000435000004360000043700000438000004390000043A0000043B0000043C -0000043D0000043E0000043F000004400000044100000442000004430000044400000445000004460000044700000448 -000004490000044A0000044B0000044C0000044D0000044E0000044F0000046100000463000004650000046700000469 -0000046B0000046D0000046F00000471000004730000047500000477000004790000047B0000047D0000047F00000481 -0000048B0000048D0000048F00000491000004930000049500000497000004990000049B0000049D0000049F000004A1 -000004A3000004A5000004A7000004A9000004AB000004AD000004AF000004B1000004B3000004B5000004B7000004B9 -000004BB000004BD000004BF000004CF000004C2000004C4000004C6000004C8000004CA000004CC000004CE000004D1 -000004D3000004D5000004D7000004D9000004DB000004DD000004DF000004E1000004E3000004E5000004E7000004E9 -000004EB000004ED000004EF000004F1000004F3000004F5000004F7000004F9000004FB000004FD000004FF00000501 -000005030000050500000507000005090000050B0000050D0000050F0000051100000513000005150000051700000519 -0000051B0000051D0000051F00000521000005230000052500000527000005290000052B0000052D0000052F00000561 -00000562000005630000056400000565000005660000056700000568000005690000056A0000056B0000056C0000056D -0000056E0000056F00000570000005710000057200000573000005740000057500000576000005770000057800000579 -0000057A0000057B0000057C0000057D0000057E0000057F000005800000058100000582000005830000058400000585 -0000058600002D0000002D0100002D0200002D0300002D0400002D0500002D0600002D0700002D0800002D0900002D0A -00002D0B00002D0C00002D0D00002D0E00002D0F00002D1000002D1100002D1200002D1300002D1400002D1500002D16 -00002D1700002D1800002D1900002D1A00002D1B00002D1C00002D1D00002D1E00002D1F00002D2000002D2100002D22 -00002D2300002D2400002D2500002D2700002D2D0000AB700000AB710000AB720000AB730000AB740000AB750000AB76 -0000AB770000AB780000AB790000AB7A0000AB7B0000AB7C0000AB7D0000AB7E0000AB7F0000AB800000AB810000AB82 -0000AB830000AB840000AB850000AB860000AB870000AB880000AB890000AB8A0000AB8B0000AB8C0000AB8D0000AB8E -0000AB8F0000AB900000AB910000AB920000AB930000AB940000AB950000AB960000AB970000AB980000AB990000AB9A -0000AB9B0000AB9C0000AB9D0000AB9E0000AB9F0000ABA00000ABA10000ABA20000ABA30000ABA40000ABA50000ABA6 -0000ABA70000ABA80000ABA90000ABAA0000ABAB0000ABAC0000ABAD0000ABAE0000ABAF0000ABB00000ABB10000ABB2 -0000ABB30000ABB40000ABB50000ABB60000ABB70000ABB80000ABB90000ABBA0000ABBB0000ABBC0000ABBD0000ABBE -0000ABBF000013F8000013F9000013FA000013FB000013FC000013FD000010D0000010D1000010D2000010D3000010D4 -000010D5000010D6000010D7000010D8000010D9000010DA000010DB000010DC000010DD000010DE000010DF000010E0 -000010E1000010E2000010E3000010E4000010E5000010E6000010E7000010E8000010E9000010EA000010EB000010EC -000010ED000010EE000010EF000010F0000010F1000010F2000010F3000010F4000010F5000010F6000010F7000010F8 -000010F9000010FA000010FD000010FE000010FF00001E0100001E0300001E0500001E0700001E0900001E0B00001E0D -00001E0F00001E1100001E1300001E1500001E1700001E1900001E1B00001E1D00001E1F00001E2100001E2300001E25 -00001E2700001E2900001E2B00001E2D00001E2F00001E3100001E3300001E3500001E3700001E3900001E3B00001E3D -00001E3F00001E4100001E4300001E4500001E4700001E4900001E4B00001E4D00001E4F00001E5100001E5300001E55 -00001E5700001E5900001E5B00001E5D00001E5F00001E6100001E6300001E6500001E6700001E6900001E6B00001E6D -00001E6F00001E7100001E7300001E7500001E7700001E7900001E7B00001E7D00001E7F00001E8100001E8300001E85 -00001E8700001E8900001E8B00001E8D00001E8F00001E9100001E9300001E95000000DF00001EA100001EA300001EA5 -00001EA700001EA900001EAB00001EAD00001EAF00001EB100001EB300001EB500001EB700001EB900001EBB00001EBD -00001EBF00001EC100001EC300001EC500001EC700001EC900001ECB00001ECD00001ECF00001ED100001ED300001ED5 -00001ED700001ED900001EDB00001EDD00001EDF00001EE100001EE300001EE500001EE700001EE900001EEB00001EED -00001EEF00001EF100001EF300001EF500001EF700001EF900001EFB00001EFD00001EFF00001F0000001F0100001F02 -00001F0300001F0400001F0500001F0600001F0700001F1000001F1100001F1200001F1300001F1400001F1500001F20 -00001F2100001F2200001F2300001F2400001F2500001F2600001F2700001F3000001F3100001F3200001F3300001F34 -00001F3500001F3600001F3700001F4000001F4100001F4200001F4300001F4400001F4500001F5100001F5300001F55 -00001F5700001F6000001F6100001F6200001F6300001F6400001F6500001F6600001F6700001F8000001F8100001F82 -00001F8300001F8400001F8500001F8600001F8700001F9000001F9100001F9200001F9300001F9400001F9500001F96 -00001F9700001FA000001FA100001FA200001FA300001FA400001FA500001FA600001FA700001FB000001FB100001F70 -00001F7100001FB300001F7200001F7300001F7400001F7500001FC300001FD000001FD100001F7600001F7700001FE0 -00001FE100001F7A00001F7B00001FE500001F7800001F7900001F7C00001F7D00001FF3000003C90000006B000000E5 -0000214E000021700000217100002172000021730000217400002175000021760000217700002178000021790000217A -0000217B0000217C0000217D0000217E0000217F00002184000024D0000024D1000024D2000024D3000024D4000024D5 -000024D6000024D7000024D8000024D9000024DA000024DB000024DC000024DD000024DE000024DF000024E0000024E1 -000024E2000024E3000024E4000024E5000024E6000024E7000024E8000024E900002C3000002C3100002C3200002C33 -00002C3400002C3500002C3600002C3700002C3800002C3900002C3A00002C3B00002C3C00002C3D00002C3E00002C3F -00002C4000002C4100002C4200002C4300002C4400002C4500002C4600002C4700002C4800002C4900002C4A00002C4B -00002C4C00002C4D00002C4E00002C4F00002C5000002C5100002C5200002C5300002C5400002C5500002C5600002C57 -00002C5800002C5900002C5A00002C5B00002C5C00002C5D00002C5E00002C5F00002C610000026B00001D7D0000027D -00002C6800002C6A00002C6C0000025100000271000002500000025200002C7300002C760000023F0000024000002C81 -00002C8300002C8500002C8700002C8900002C8B00002C8D00002C8F00002C9100002C9300002C9500002C9700002C99 -00002C9B00002C9D00002C9F00002CA100002CA300002CA500002CA700002CA900002CAB00002CAD00002CAF00002CB1 -00002CB300002CB500002CB700002CB900002CBB00002CBD00002CBF00002CC100002CC300002CC500002CC700002CC9 -00002CCB00002CCD00002CCF00002CD100002CD300002CD500002CD700002CD900002CDB00002CDD00002CDF00002CE1 -00002CE300002CEC00002CEE00002CF30000A6410000A6430000A6450000A6470000A6490000A64B0000A64D0000A64F -0000A6510000A6530000A6550000A6570000A6590000A65B0000A65D0000A65F0000A6610000A6630000A6650000A667 -0000A6690000A66B0000A66D0000A6810000A6830000A6850000A6870000A6890000A68B0000A68D0000A68F0000A691 -0000A6930000A6950000A6970000A6990000A69B0000A7230000A7250000A7270000A7290000A72B0000A72D0000A72F -0000A7330000A7350000A7370000A7390000A73B0000A73D0000A73F0000A7410000A7430000A7450000A7470000A749 -0000A74B0000A74D0000A74F0000A7510000A7530000A7550000A7570000A7590000A75B0000A75D0000A75F0000A761 -0000A7630000A7650000A7670000A7690000A76B0000A76D0000A76F0000A77A0000A77C00001D790000A77F0000A781 -0000A7830000A7850000A7870000A78C000002650000A7910000A7930000A7970000A7990000A79B0000A79D0000A79F -0000A7A10000A7A30000A7A50000A7A70000A7A9000002660000025C000002610000026C0000026A0000029E00000287 -0000029D0000AB530000A7B50000A7B70000A7B90000A7BB0000A7BD0000A7BF0000A7C10000A7C30000A79400000282 -00001D8E0000A7C80000A7CA0000A7D10000A7D70000A7D90000A7F60000FF410000FF420000FF430000FF440000FF45 -0000FF460000FF470000FF480000FF490000FF4A0000FF4B0000FF4C0000FF4D0000FF4E0000FF4F0000FF500000FF51 -0000FF520000FF530000FF540000FF550000FF560000FF570000FF580000FF590000FF5A00010428000104290001042A -0001042B0001042C0001042D0001042E0001042F00010430000104310001043200010433000104340001043500010436 -0001043700010438000104390001043A0001043B0001043C0001043D0001043E0001043F000104400001044100010442 -000104430001044400010445000104460001044700010448000104490001044A0001044B0001044C0001044D0001044E -0001044F000104D8000104D9000104DA000104DB000104DC000104DD000104DE000104DF000104E0000104E1000104E2 -000104E3000104E4000104E5000104E6000104E7000104E8000104E9000104EA000104EB000104EC000104ED000104EE -000104EF000104F0000104F1000104F2000104F3000104F4000104F5000104F6000104F7000104F8000104F9000104FA -000104FB0001059700010598000105990001059A0001059B0001059C0001059D0001059E0001059F000105A0000105A1 -000105A3000105A4000105A5000105A6000105A7000105A8000105A9000105AA000105AB000105AC000105AD000105AE -000105AF000105B0000105B1000105B3000105B4000105B5000105B6000105B7000105B8000105B9000105BB000105BC -00010CC000010CC100010CC200010CC300010CC400010CC500010CC600010CC700010CC800010CC900010CCA00010CCB -00010CCC00010CCD00010CCE00010CCF00010CD000010CD100010CD200010CD300010CD400010CD500010CD600010CD7 -00010CD800010CD900010CDA00010CDB00010CDC00010CDD00010CDE00010CDF00010CE000010CE100010CE200010CE3 -00010CE400010CE500010CE600010CE700010CE800010CE900010CEA00010CEB00010CEC00010CED00010CEE00010CEF -00010CF000010CF100010CF2000118C0000118C1000118C2000118C3000118C4000118C5000118C6000118C7000118C8 -000118C9000118CA000118CB000118CC000118CD000118CE000118CF000118D0000118D1000118D2000118D3000118D4 -000118D5000118D6000118D7000118D8000118D9000118DA000118DB000118DC000118DD000118DE000118DF00016E60 -00016E6100016E6200016E6300016E6400016E6500016E6600016E6700016E6800016E6900016E6A00016E6B00016E6C -00016E6D00016E6E00016E6F00016E7000016E7100016E7200016E7300016E7400016E7500016E7600016E7700016E78 -00016E7900016E7A00016E7B00016E7C00016E7D00016E7E00016E7F0001E9220001E9230001E9240001E9250001E926 -0001E9270001E9280001E9290001E92A0001E92B0001E92C0001E92D0001E92E0001E92F0001E9300001E9310001E932 -0001E9330001E9340001E9350001E9360001E9370001E9380001E9390001E93A0001E93B0001E93C0001E93D0001E93E -0001E93F0001E9400001E9410001E9420001E943000000DF02000069000003070000FB000000FB010000FB020000FB03 -0000FB040000FB050000FB06000005870000FB130000FB140000FB150000FB160000FB170000014900000390000003B0 -000001F000001E9600001E9700001E9800001E9900001E9A00001F5000001F5200001F5400001F5600001FB600001FC6 -00001FD200001FD300001FD600001FD700001FE200001FE300001FE400001FE600001FE700001FF600001F8000001F81 -00001F8200001F8300001F8400001F8500001F8600001F8700001F8000001F8100001F8200001F8300001F8400001F85 -00001F8600001F8700001F9000001F9100001F9200001F9300001F9400001F9500001F9600001F9700001F9000001F91 -00001F9200001F9300001F9400001F9500001F9600001F9700001FA000001FA100001FA200001FA300001FA400001FA5 -00001FA600001FA700001FA000001FA100001FA200001FA300001FA400001FA500001FA600001FA700001FB300001FB3 -00001FC300001FC300001FF300001FF300001FB200001FB400001FC200001FC400001FF200001FF400001FB700001FC7 -00001FF7"; -return t; -} -immutable(uint)[] toTitleTable() nothrow @nogc pure @safe { -static immutable uint[] t = -x" -0000004100000042000000430000004400000045000000460000004700000048000000490000004A0000004B0000004C -0000004D0000004E0000004F000000500000005100000052000000530000005400000055000000560000005700000058 -000000590000005A0000039C000000C0000000C1000000C2000000C3000000C4000000C5000000C6000000C7000000C8 -000000C9000000CA000000CB000000CC000000CD000000CE000000CF000000D0000000D1000000D2000000D3000000D4 -000000D5000000D6000000D8000000D9000000DA000000DB000000DC000000DD000000DE000001780000010000000102 -0000010400000106000001080000010A0000010C0000010E00000110000001120000011400000116000001180000011A -0000011C0000011E00000120000001220000012400000126000001280000012A0000012C0000012E0000004900000132 -0000013400000136000001390000013B0000013D0000013F000001410000014300000145000001470000014A0000014C -0000014E00000150000001520000015400000156000001580000015A0000015C0000015E000001600000016200000164 -00000166000001680000016A0000016C0000016E00000170000001720000017400000176000001790000017B0000017D -00000053000002430000018200000184000001870000018B00000191000001F6000001980000023D00000220000001A0 -000001A2000001A4000001A7000001AC000001AF000001B3000001B5000001B8000001BC000001F7000001C5000001C5 -000001C5000001C8000001C8000001C8000001CB000001CB000001CB000001CD000001CF000001D1000001D3000001D5 -000001D7000001D9000001DB0000018E000001DE000001E0000001E2000001E4000001E6000001E8000001EA000001EC -000001EE000001F2000001F2000001F2000001F4000001F8000001FA000001FC000001FE000002000000020200000204 -00000206000002080000020A0000020C0000020E00000210000002120000021400000216000002180000021A0000021C -0000021E000002220000022400000226000002280000022A0000022C0000022E00000230000002320000023B00002C7E -00002C7F0000024100000246000002480000024A0000024C0000024E00002C6F00002C6D00002C700000018100000186 -000001890000018A0000018F000001900000A7AB000001930000A7AC000001940000A78D0000A7AA0000019700000196 -0000A7AE00002C620000A7AD0000019C00002C6E0000019D0000019F00002C64000001A60000A7C5000001A90000A7B1 -000001AE00000244000001B1000001B200000245000001B70000A7B20000A7B000000399000003700000037200000376 -000003FD000003FE000003FF0000038600000388000003890000038A0000039100000392000003930000039400000395 -000003960000039700000398000003990000039A0000039B0000039C0000039D0000039E0000039F000003A0000003A1 -000003A3000003A3000003A4000003A5000003A6000003A7000003A8000003A9000003AA000003AB0000038C0000038E -0000038F0000039200000398000003A6000003A0000003CF000003D8000003DA000003DC000003DE000003E0000003E2 -000003E4000003E6000003E8000003EA000003EC000003EE0000039A000003A1000003F90000037F00000395000003F7 -000003FA000004100000041100000412000004130000041400000415000004160000041700000418000004190000041A -0000041B0000041C0000041D0000041E0000041F00000420000004210000042200000423000004240000042500000426 -0000042700000428000004290000042A0000042B0000042C0000042D0000042E0000042F000004000000040100000402 -000004030000040400000405000004060000040700000408000004090000040A0000040B0000040C0000040D0000040E -0000040F00000460000004620000046400000466000004680000046A0000046C0000046E000004700000047200000474 -00000476000004780000047A0000047C0000047E000004800000048A0000048C0000048E000004900000049200000494 -00000496000004980000049A0000049C0000049E000004A0000004A2000004A4000004A6000004A8000004AA000004AC -000004AE000004B0000004B2000004B4000004B6000004B8000004BA000004BC000004BE000004C1000004C3000004C5 -000004C7000004C9000004CB000004CD000004C0000004D0000004D2000004D4000004D6000004D8000004DA000004DC -000004DE000004E0000004E2000004E4000004E6000004E8000004EA000004EC000004EE000004F0000004F2000004F4 -000004F6000004F8000004FA000004FC000004FE00000500000005020000050400000506000005080000050A0000050C -0000050E00000510000005120000051400000516000005180000051A0000051C0000051E000005200000052200000524 -00000526000005280000052A0000052C0000052E00000531000005320000053300000534000005350000053600000537 -00000538000005390000053A0000053B0000053C0000053D0000053E0000053F00000540000005410000054200000543 -0000054400000545000005460000054700000548000005490000054A0000054B0000054C0000054D0000054E0000054F -00000550000005510000055200000553000005540000055500000556000010D0000010D1000010D2000010D3000010D4 -000010D5000010D6000010D7000010D8000010D9000010DA000010DB000010DC000010DD000010DE000010DF000010E0 -000010E1000010E2000010E3000010E4000010E5000010E6000010E7000010E8000010E9000010EA000010EB000010EC -000010ED000010EE000010EF000010F0000010F1000010F2000010F3000010F4000010F5000010F6000010F7000010F8 -000010F9000010FA000010FD000010FE000010FF000013F0000013F1000013F2000013F3000013F4000013F500000412 -000004140000041E0000042100000422000004220000042A000004620000A64A0000A77D00002C630000A7C600001E00 -00001E0200001E0400001E0600001E0800001E0A00001E0C00001E0E00001E1000001E1200001E1400001E1600001E18 -00001E1A00001E1C00001E1E00001E2000001E2200001E2400001E2600001E2800001E2A00001E2C00001E2E00001E30 -00001E3200001E3400001E3600001E3800001E3A00001E3C00001E3E00001E4000001E4200001E4400001E4600001E48 -00001E4A00001E4C00001E4E00001E5000001E5200001E5400001E5600001E5800001E5A00001E5C00001E5E00001E60 -00001E6200001E6400001E6600001E6800001E6A00001E6C00001E6E00001E7000001E7200001E7400001E7600001E78 -00001E7A00001E7C00001E7E00001E8000001E8200001E8400001E8600001E8800001E8A00001E8C00001E8E00001E90 -00001E9200001E9400001E6000001EA000001EA200001EA400001EA600001EA800001EAA00001EAC00001EAE00001EB0 -00001EB200001EB400001EB600001EB800001EBA00001EBC00001EBE00001EC000001EC200001EC400001EC600001EC8 -00001ECA00001ECC00001ECE00001ED000001ED200001ED400001ED600001ED800001EDA00001EDC00001EDE00001EE0 -00001EE200001EE400001EE600001EE800001EEA00001EEC00001EEE00001EF000001EF200001EF400001EF600001EF8 -00001EFA00001EFC00001EFE00001F0800001F0900001F0A00001F0B00001F0C00001F0D00001F0E00001F0F00001F18 -00001F1900001F1A00001F1B00001F1C00001F1D00001F2800001F2900001F2A00001F2B00001F2C00001F2D00001F2E -00001F2F00001F3800001F3900001F3A00001F3B00001F3C00001F3D00001F3E00001F3F00001F4800001F4900001F4A -00001F4B00001F4C00001F4D00001F5900001F5B00001F5D00001F5F00001F6800001F6900001F6A00001F6B00001F6C -00001F6D00001F6E00001F6F00001FBA00001FBB00001FC800001FC900001FCA00001FCB00001FDA00001FDB00001FF8 -00001FF900001FEA00001FEB00001FFA00001FFB00001F8800001F8900001F8A00001F8B00001F8C00001F8D00001F8E -00001F8F00001F9800001F9900001F9A00001F9B00001F9C00001F9D00001F9E00001F9F00001FA800001FA900001FAA -00001FAB00001FAC00001FAD00001FAE00001FAF00001FB800001FB900001FBC0000039900001FCC00001FD800001FD9 -00001FE800001FE900001FEC00001FFC0000213200002160000021610000216200002163000021640000216500002166 -0000216700002168000021690000216A0000216B0000216C0000216D0000216E0000216F00002183000024B6000024B7 -000024B8000024B9000024BA000024BB000024BC000024BD000024BE000024BF000024C0000024C1000024C2000024C3 -000024C4000024C5000024C6000024C7000024C8000024C9000024CA000024CB000024CC000024CD000024CE000024CF -00002C0000002C0100002C0200002C0300002C0400002C0500002C0600002C0700002C0800002C0900002C0A00002C0B -00002C0C00002C0D00002C0E00002C0F00002C1000002C1100002C1200002C1300002C1400002C1500002C1600002C17 -00002C1800002C1900002C1A00002C1B00002C1C00002C1D00002C1E00002C1F00002C2000002C2100002C2200002C23 -00002C2400002C2500002C2600002C2700002C2800002C2900002C2A00002C2B00002C2C00002C2D00002C2E00002C2F -00002C600000023A0000023E00002C6700002C6900002C6B00002C7200002C7500002C8000002C8200002C8400002C86 -00002C8800002C8A00002C8C00002C8E00002C9000002C9200002C9400002C9600002C9800002C9A00002C9C00002C9E -00002CA000002CA200002CA400002CA600002CA800002CAA00002CAC00002CAE00002CB000002CB200002CB400002CB6 -00002CB800002CBA00002CBC00002CBE00002CC000002CC200002CC400002CC600002CC800002CCA00002CCC00002CCE -00002CD000002CD200002CD400002CD600002CD800002CDA00002CDC00002CDE00002CE000002CE200002CEB00002CED -00002CF2000010A0000010A1000010A2000010A3000010A4000010A5000010A6000010A7000010A8000010A9000010AA -000010AB000010AC000010AD000010AE000010AF000010B0000010B1000010B2000010B3000010B4000010B5000010B6 -000010B7000010B8000010B9000010BA000010BB000010BC000010BD000010BE000010BF000010C0000010C1000010C2 -000010C3000010C4000010C5000010C7000010CD0000A6400000A6420000A6440000A6460000A6480000A64A0000A64C -0000A64E0000A6500000A6520000A6540000A6560000A6580000A65A0000A65C0000A65E0000A6600000A6620000A664 -0000A6660000A6680000A66A0000A66C0000A6800000A6820000A6840000A6860000A6880000A68A0000A68C0000A68E -0000A6900000A6920000A6940000A6960000A6980000A69A0000A7220000A7240000A7260000A7280000A72A0000A72C -0000A72E0000A7320000A7340000A7360000A7380000A73A0000A73C0000A73E0000A7400000A7420000A7440000A746 -0000A7480000A74A0000A74C0000A74E0000A7500000A7520000A7540000A7560000A7580000A75A0000A75C0000A75E -0000A7600000A7620000A7640000A7660000A7680000A76A0000A76C0000A76E0000A7790000A77B0000A77E0000A780 -0000A7820000A7840000A7860000A78B0000A7900000A7920000A7C40000A7960000A7980000A79A0000A79C0000A79E -0000A7A00000A7A20000A7A40000A7A60000A7A80000A7B40000A7B60000A7B80000A7BA0000A7BC0000A7BE0000A7C0 -0000A7C20000A7C70000A7C90000A7D00000A7D60000A7D80000A7F50000A7B3000013A0000013A1000013A2000013A3 -000013A4000013A5000013A6000013A7000013A8000013A9000013AA000013AB000013AC000013AD000013AE000013AF -000013B0000013B1000013B2000013B3000013B4000013B5000013B6000013B7000013B8000013B9000013BA000013BB -000013BC000013BD000013BE000013BF000013C0000013C1000013C2000013C3000013C4000013C5000013C6000013C7 -000013C8000013C9000013CA000013CB000013CC000013CD000013CE000013CF000013D0000013D1000013D2000013D3 -000013D4000013D5000013D6000013D7000013D8000013D9000013DA000013DB000013DC000013DD000013DE000013DF -000013E0000013E1000013E2000013E3000013E4000013E5000013E6000013E7000013E8000013E9000013EA000013EB -000013EC000013ED000013EE000013EF0000FF210000FF220000FF230000FF240000FF250000FF260000FF270000FF28 -0000FF290000FF2A0000FF2B0000FF2C0000FF2D0000FF2E0000FF2F0000FF300000FF310000FF320000FF330000FF34 -0000FF350000FF360000FF370000FF380000FF390000FF3A000104000001040100010402000104030001040400010405 -000104060001040700010408000104090001040A0001040B0001040C0001040D0001040E0001040F0001041000010411 -00010412000104130001041400010415000104160001041700010418000104190001041A0001041B0001041C0001041D -0001041E0001041F0001042000010421000104220001042300010424000104250001042600010427000104B0000104B1 -000104B2000104B3000104B4000104B5000104B6000104B7000104B8000104B9000104BA000104BB000104BC000104BD -000104BE000104BF000104C0000104C1000104C2000104C3000104C4000104C5000104C6000104C7000104C8000104C9 -000104CA000104CB000104CC000104CD000104CE000104CF000104D0000104D1000104D2000104D30001057000010571 -00010572000105730001057400010575000105760001057700010578000105790001057A0001057C0001057D0001057E -0001057F000105800001058100010582000105830001058400010585000105860001058700010588000105890001058A -0001058C0001058D0001058E0001058F000105900001059100010592000105940001059500010C8000010C8100010C82 -00010C8300010C8400010C8500010C8600010C8700010C8800010C8900010C8A00010C8B00010C8C00010C8D00010C8E -00010C8F00010C9000010C9100010C9200010C9300010C9400010C9500010C9600010C9700010C9800010C9900010C9A -00010C9B00010C9C00010C9D00010C9E00010C9F00010CA000010CA100010CA200010CA300010CA400010CA500010CA6 -00010CA700010CA800010CA900010CAA00010CAB00010CAC00010CAD00010CAE00010CAF00010CB000010CB100010CB2 -000118A0000118A1000118A2000118A3000118A4000118A5000118A6000118A7000118A8000118A9000118AA000118AB -000118AC000118AD000118AE000118AF000118B0000118B1000118B2000118B3000118B4000118B5000118B6000118B7 -000118B8000118B9000118BA000118BB000118BC000118BD000118BE000118BF00016E4000016E4100016E4200016E43 -00016E4400016E4500016E4600016E4700016E4800016E4900016E4A00016E4B00016E4C00016E4D00016E4E00016E4F -00016E5000016E5100016E5200016E5300016E5400016E5500016E5600016E5700016E5800016E5900016E5A00016E5B -00016E5C00016E5D00016E5E00016E5F0001E9000001E9010001E9020001E9030001E9040001E9050001E9060001E907 -0001E9080001E9090001E90A0001E90B0001E90C0001E90D0001E90E0001E90F0001E9100001E9110001E9120001E913 -0001E9140001E9150001E9160001E9170001E9180001E9190001E91A0001E91B0001E91C0001E91D0001E91E0001E91F -0001E9200001E92102000053000000730000013002000046000000660200004600000069020000460000006C03000046 -000000660000006903000046000000660000006C02000053000000740200005300000074020005350000058202000544 -000005760200054400000565020005440000056B0200054E00000576020005440000056D020002BC0000004E03000399 -0000030800000301030003A500000308000003010200004A0000030C0200004800000331020000540000030802000057 -0000030A020000590000030A02000041000002BE020003A500000313030003A50000031300000300030003A500000313 -00000301030003A500000313000003420200039100000342020003970000034203000399000003080000030003000399 -00000308000003010200039900000342030003990000030800000342030003A50000030800000300030003A500000308 -00000301020003A100000313020003A500000342030003A50000030800000342020003A90000034200001F8800001F89 -00001F8A00001F8B00001F8C00001F8D00001F8E00001F8F00001F8800001F8900001F8A00001F8B00001F8C00001F8D -00001F8E00001F8F00001F9800001F9900001F9A00001F9B00001F9C00001F9D00001F9E00001F9F00001F9800001F99 -00001F9A00001F9B00001F9C00001F9D00001F9E00001F9F00001FA800001FA900001FAA00001FAB00001FAC00001FAD -00001FAE00001FAF00001FA800001FA900001FAA00001FAB00001FAC00001FAD00001FAE00001FAF00001FBC00001FBC -00001FCC00001FCC00001FFC00001FFC02001FBA00000345020003860000034502001FCA000003450200038900000345 -02001FFA000003450200038F00000345030003910000034200000345030003970000034200000345030003A900000342 -00000345"; -return t; -} - -} - - -static if (size_t.sizeof == 8) -{ -//2080 bytes -enum lowerCaseTrieEntries = TrieEntry!(bool, 8, 5, 8)(x" -000000000000000000000000000000200000000000000070", -x" -000000000000010000000000000001400000000000002500", -x" -040203020202010008070202060202050202020202020202020202020202020202020202020202020202020202020202 -020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202 -020202020202020202020202020202020202020202020202020202020202020202020202020202020000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000003000200010000000600060005000400060006000600060006000600060006 -000800060006000700060006000600060006000600060006000C000B000A000900060006000E000D000600060006000F -000600060006000600060006001100100006000600060006000600060006000600060006000600060006000600060006 -000600060006000600060006000600060006000600060006000600060006000600060006000600060006000600060006 -000600060006000600060006000600060006000600060006001300120006000600140006000600060006000600060006 -000600060006000600060006000600060006000600060006000600060006000600060006000600060006000600060006 -000600060006000600060006000600060006000600060006000600060006000600150006000600060016000600060006 -000600060006000600190006001800170006000600060006000600060006001A00060006000600060006000600060006 -000600060006001B00060006000600060006000600060006000600060006000600060006000600060006001C00060006 -000600060006000600060006000600060006000600060006000600060006000600060006000600060006000600060006 -0006000600060006000600060006000600060006000600060020001F001E001D00060006000600060021000600060006 -000600060006002200060006000600060006000600230006000600060006000600060006000600060006000600060006 -000600060006000600060006000600060006000600060006000600060006000600060006000600060006000600060006 -0006000600060006000600060006000600060006000600060006000600060006000000000000000007FFFFFE00000000 -0420040000000000FF7FFFFF8000000055AAAAAAAAAAAAAAD4AAAAAAAAAAAB55E6512D2A4E243129AA29AAAAB5555240 -93FAAAAAAAAAAAAAFFFFFFFFFFFFAA8501FFFFFFFFEFFFFF0000001F0000000300000000000000003C8A000000000020 -FFFFF00000010000192FAAAAAAE37FFFFFFF000000000000AAAAAAAAFFFFFFFFAAAAAAAAAAAAA802AAAAAAAAAAAAD554 -0000AAAAAAAAAAAAFFFFFFFF0000000000000000000001FF000000000000000000000000000000000000000000000000 -00000000000000000000000000000000000000000000000000000000000000000000000000000000F7FFFFFFFFFF0000 -0000000000000000000000000000000000000000000000003F0000000000000000000000000000000000000000000000 -00000000000001FF0000000000000000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0000000000000000 -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABFEAAAAAAAAAAAAAAAAAAAAA00FF00FF003F00FF3FFF00FF00FF003F -40DF00FF00FF00FF00DC00FF00CF00DC00000000000000008002000000000000000000001FFF00000000000000000000 -321080000008C400FFFF0000000043C00000000000000010000000000000000000000000000000000000000000000000 -0000000000000000000003FFFFFF0000FFFF0000000000003FDA1562FFFFFFFFAAAAAAAAAAAAAAAA0008501AAAAAAAAA -000020BFFFFFFFFF000000000000000000000000000000000000000000000000000000000000000000002AAAAAAAAAAA -000000003AAAAAAA0000000000000000AAABAAA80000000095FFAAAAAAAAAAAAAAA082AAAABA50AA075C000002AA050A -FFFF000000000000FFFF03FFF7FFFFFFFFFFFFFFFFFFFFFF00000000000000000000000000F8007F0000000000000000 -0000000000000000000000000000000000000000000000000000000007FFFFFE00000000000000000000000000000000 -FFFFFF0000000000000000000000FFFF00000000000000000FFFFFFFFF00000000000000000000000000000000000000 -1BFBFFFBFF80000000000000000000000000000000000000000000000000000007FDFFFFFFFFFFB90000000000000000 -0000000000000000000000000000000000000000000000000007FFFFFFFFFFFF00000000000000000000000000000000 -000000000000000000000000FFFFFFFF0000000000000000FFFFFFFF0000000000000000000000000000000000000000 -000FFFFFFC000000000000FFFFDFC000EBC000000FFFFFFCFFFFFC000000FFEF00FFFFFFC000000F00000FFFFFFC0000 -FC000000FFFFFFC0FFFFC000000FFFFF0FFFFFFC000000FF0000FFFFFFC000000000003FFFFFFC00F0000003F7FFFFFC -FFC000000FDFFFFFFFFF0000003F7FFFFFFFFC000000FDFF0000000000000BF7000007E07FFFFBFF0000000000000000 -00000000000000000000000000000000FFFF00000000000000003FFFFFFFFFFF00000000000000000000000000000000 -FFFFFFFC00000000000000000000000F0000000000000000000000000000000000000000000000000000000000000000 -00000000000000000000000000000000", -); -//1856 bytes -enum upperCaseTrieEntries = TrieEntry!(bool, 8, 5, 8)(x" -000000000000000000000000000000200000000000000070", -x" -000000000000010000000000000001400000000000001E00", -x" -040203020202010008070202060202050202020202020202020202020202020202020202020202020202020202020202 -020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202 -020202020202020202020202020202020202020202020202020202020202020202020202020202020000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000003000200010000000600060005000400060006000600060006000600060006 -000800060006000700060006000600060006000600060006000B000A0006000900060006000C0006000600060006000D -0006000600060006000600060006000E0006000600060006000600060006000600060006000600060006000600060006 -000600060006000600060006000600060006000600060006000600060006000600060006000600060006000600060006 -0006000600060006000600060006000600060006000600060010000F0006000600060006000600060006000600060006 -000600060006000600060006000600060006000600060006000600060006000600060006000600060006000600060006 -000600060006000600060006000600060006000600060006000600060006000600060006000600060011000600060006 -000600060006000600060006001300120006000600060006000600060006001400060006000600060006000600060006 -000600060006001500060006000600060006000600060006000600060006000600060006000600060006001600060006 -000600060006000600060006000600060006000600060006000600060006000600060006000600060006000600060006 -000600060006000600060006000600060006000600060006001A00190018001700060006000600060006000600060006 -0006000600060006000600060006000600060006001B0006000600060006000600060006001C00060006000600060006 -000600060006000600060006000600060006000600060006000600060006000600060006000600060006000600060006 -000600060006000600060006000600060006000600060006000600060006000600000000000000000000000007FFFFFE -0000000000000000000000007F7FFFFFAA555555555555552B555555555554AA11AED2D5B1DBCED655D255554AAAA490 -6C05555555555555000000000000557A0000000000000000000000000000000000000000000000008045000000000000 -00000FFBFFFED740E6905555551C80000000FFFFFFFFFFFF555555550000000055555555555554015555555555552AAB -FFFE55555555555500000000007FFFFF0000000000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000FFFFFFFF0000000000000000000020BF -00000000000000000000000000000000FFFFFFFF00000000003FFFFFFFFFFFFF00000000000000000000000000000000 -E7FFFFFFFFFF000000000000000000005555555555555555555555555555555555555555401555555555555555555555 -FF00FF003F00FF000000FF00AA003F000F000000000000000F001F000F000F00C00F3D503E2738840000FFFF00000020 -0000000000000008000000000000000000000000000000000000000000000000FFC0000000000000000000000000FFFF -0000FFFFFFFFFFFFC025EA9D000000005555555555555555000428055555555500000000000000000000155555555555 -0000000005555555000000000000000055545554000000006A00555555555555555F7D555545285500200000014102F5 -07FFFFFE00000000000000000000000000000000000000000000000000000000000000FFFFFFFFFF0000000000000000 -FFFF00000000000000000000000FFFFF0000000000000000F7FF000000000000000000000037F7FF0000000000000000 -000000000000000000000000000000000007FFFFFFFFFFFF000000000000000000000000000000000000000000000000 -FFFFFFFF000000000000000000000000000000000000000000000000FFFFFFFF00000000000000000000000000000000 -FFF0000003FFFFFFFFFFFF0000003FFF003FDE64D0000003000003FFFFFF00007B0000001FDFE7B0FFFFF0000001FC5F -03FFFFFF0000003F00003FFFFFF00000F0000003FFFFFF00FFFF0000003FFFFFFFFFFF00000003FF07FFFFFC00000001 -001FFFFFF000000000007FFFFFC00000000001FFFFFF0000000000000000040000000003FFFFFFFF0000000000000000 -00000000000000000000000000000000FFFF000000000000FFFF03FFFFFF03FF00000000000003FF0000000000000000 -0000000000000000000000000000000000000000000000000000000000000000", -); -//11648 bytes -enum simpleCaseTrieEntries = TrieEntry!(ushort, 8, 7, 6)(x" -000000000000000000000000000000200000000000000140", -x" -0000000000000100000000000000048000000000000011C0", -x" -040203020202010007020202060202050202020202020202020202020202020202020202020202020202020202020202 -020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202 -020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202 -020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202 -020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202 -02020202020202020202020202020202000300020001000000070006000500040000000A00090008000D000C000B0000 -00110010000F000E00000014001300120000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -001600150000000000000000000000000000000000000000001800170000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -00000019000000000000001B001A0000001F001E001D001C002300220021002000000000000000000000002600250024 -000000000000000000000000000000000028002700000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000002C002B002A0029000000000000002D -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000002F002E0000003300320031003000000000000000000000000000000000 -000000000000000000000035003400000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000360000000000000000000000000000000000000000000000000000000000380037 -0000000000000000000000000000000000000000000000000000000000000000003C003B003A00390000003E003D0000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -0040003F0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -004200410000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000004300000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000004500440000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -00000000000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -08E50080091DFFFF090B00BD031905FB0B2B006B09D00B9A0AD200E0082606C705D1059404670838019301800A330785 -FFFF02F904D9051EFFFFFFFFFFFFFFFF08E60081091EFFFF090C00BE031A05FC0B2C006C09D10B9B0AD300E1082706C8 -05D2059504680839019401810A340786FFFF02FA04DA051FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0965FFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0856011805AE077B03DB025709BB0AD80B4D0128038F0559031500470397062B -029F008608DD05F3FFFF00CB058B071104FB021309820AE208EF0A45088007010857011905AF077C03DC025809BC0AD9 -0B4E01290390055A031600480398062C02A0008708DE05F4FFFF00CC058C071204FC021409830AE3036A0A4608810702 -04CC04CB09DD09DC0B580B5701CD01CC07320731020001FF08AF08AE00CE00CD06140613005C005B0620061F00520051 -0663066202B202B108010800015A015907E707E60B890B880A620A6104BE04BD0593059201B401B3096D096C021C021B -08790878FFFFFFFF031C031B0939093806FD00950094FFFF081C008F008E06FE07CA0B310B30081D0A230121012007CB -04DC04DBFFFF0A240B660B6507C507C40162016106BD06BC02AA02A906830682000D000C060A06090070006F06410640 -00D200D1083B083A01DF01DE0728072701A201A10B810B800A520A5104C604C50566056507CF07CE09FB09FA09770976 -002106260625036B05D305FE05FD00220B180B170688069102750526026A026901AF07FA0B3902760AC20769FFFF01B0 -052008B508B40A4709900B090229027BFFFF04C900970096032B00D308EB00AA062E062D0019001803CF055301050104 -FFFFFFFF09A603D0075907EE01690168052C077F01A7075A0AC003020301052DFFFFFFFF0256025501ADFFFF0920091F -FFFFFFFFFFFFFFFF05E0092B092A09290AE50AE405E205E1079804FE04FD0AE6076B0A5E0A5D07990A8004420441076C -055102FE02FD0A810B540B53076A0552016B016A0655065402C002BF05FA05F900F200F1064706460570056F06A906A8 -0ABC0ABB0ABAFFFF01AE022A0748074701EB01EA0B6E0B6D09710970041A0419059F059E0841084008BE08BD0940093F -0898089703AE03AD041C041B0B120B110A8F0A8E040B040A0531053002B402B307F107F0016D016C084B084A05B705B6 -06CE06CDFFFF00D40B830B8200640063073E073D00C200C1088A08890079007806F506F4057A0579FFFFFFFFFFFFFFFF -07960A0EFFFFFFFF095D07BE04CA07970692014C014B028B09D509D402D3041D032403230926092509F709F609540953 -0689021D0B1F040207FB0B3AFFFF05270A48FFFF0AC3FFFFFFFFFFFFFFFF0941027CFFFF0B2E0521FFFF00B50B76FFFF -063C00F90B0A099100ABFFFFFFFF098AFFFF08EC05A0FFFFFFFFFFFF032CFFFFFFFFFFFFFFFFFFFFFFFFFFFF0031FFFF -09A70516FFFF05540350FFFFFFFFFFFF078001A8041E07EFFFFFFFFFFFFF02D4FFFF0AC1FFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFF070F0092FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0972FFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF01CF01CE04A504A401F601F5FFFFFFFF -0A37FFFFFFFFFFFF0382FFFF086E0122FFFFFFFFFFFFFFFFFFFF0299FFFFFFFFFFFF00EB082806D709CA0BA0FFFF07F2 -017C04AE04B30386016601D20ABD0A170B070B320973059A0106039B081E0966068AFFFF062F0B7300AC03450607032D -009A084C06FA08FD00EC082906D8029A017D04AF04B409CE016701D30ABE0A180B080B330974059B0107039C081F0967 -068C068B06300B7400AD03460608032E009B084D06FB08FE097809CB0BA107F3FFFFFFFF059C04B009790B750347FFFF -04A904A8018C018B085F085E0A890A88098909880582058103BC03BB09DF09DE08C208C102F602F503630362044A0449 -038301D606310B340A84FFFF0ABF059D05DB05DA01D70A85086F01230A38FFFF096E0291051A04F105AC0090098E0AF5 -08F700A008F906FF0391009C031D05EF03C105400B45071F0AB6027D03D306E704B70126086A0A8C01B90A1407AA080A -02DF0A73051107EC015B02450ACE09A40B5B093A0664057B005303C3062109F003C205410B4607200AB7027E03D406E8 -04B80127086B0A8D01BA0A1507AB080B02E00A74051207ED015C02460ACF09A50B5C093B0665057C005403C4062209F1 -096F0292051B04F205AD0091098F0AF608F800A108FA07000392009D031E05F005CD05CC0AA30AA20B4A0B4902440243 -03E603E501FA01F90859085809B609B508E008DF05A305A2039A039908C608C509370936088808870418041703DE03DD -FFFFFFFF0B220B21FFFFFFFFFFFFFFFF05A705A6FFFFFFFF00E600E5082B082A005E005D06D006CF00050004061C061B -00CA00C90730072F0568056708AD08AC010B010A06B506B401A0019F04F004EF045804570A110A100B700B6F07BB07BA -040704060831083008EE08ED09BF09BE08DA08D90326032503B803B709F909F8021F06C306C202DB023705D905D80220 -04A00A220A21023802DC0793079204A1055C055B023C023B06E406E302620261062806270058005706790678001E001D -0677067601130112056A05690248024707A907A8011B011A07780777049B049A046A04690A910A900B000AFF02360235 -042C042B038C038B08CA08C9090E090D092409230338033703D803D70A010A000A950A940825082401CB01CA079B079A -02C402C30A1C0A1B0171017004EC04EB05BB05BA06A506A4002C002B064D064C003800370639063800B400B30B6A0B69 -0ACD0ACC0738073704EE04ED088E088D0227017E0BA2FFFF0A9A0293071904B50589047D08520AC401330B6306D503A7 -03030352067C03840135092C067E09030B27094906D905B405010524085A0A06022F0A3D07150295FFFF078705DC0184 -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0228017F0BA3FFFF0A9B0294071A04B6058A047E08530AC501340B6406D603A8 -03040353067D03850136092D067F09040B28094A06DA05B505020525085B0A0702300A3E07160296FFFF078805DD0185 -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF050D03EB082209B701B10B8E06F601F0 -01B7049804850241046D087C0A9E0A9C0AF706A007FE056B04310648039300F708C3068F090902D909170672033B014F -03D1084E09EA0B4F0AA8FFFF02E10108FFFFFFFFFFFFFFFFFFFFFFFF0A55FFFF03FC06990AE7015309320668042D05C4 -03C905F10317002D09270B3B09F20025085C06B0055D008C0429088209AB0AE00A0A0761021504FF071304D3024901DA -081809980AAE02A106EC03CB058703E1FFFF039F011E0B3506950905000EFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -036000C3043305D60934006D081606C009FC0B29037408930335007E08BB06030A5B02CD01F70A6D055F0137095506B8 -07C805750B7E07DA075D09EE04DF048B064E037802E7044F06A209A001410850089B09190041045D0634031300450B9C -0B6709E0023309B10739054300820271077D07170A0C0ADE04B908B601E4052803AF06DB09FE028908060B2305B801FB -035A067A034C05DE092E068602AD0139038D06230B84004BFFFFFFFF04390061038E06240B85004CFFFFFFFF043A0062 -05130A1606E9054205CE093C0A760A75FFFFFFFFFFFF069DFFFFFFFFFFFFFFFF03FD069A0AE8015409330669042E05C5 -03CA05F20318002E09280B3C09F30026085D06B1055E008D042A088309AC0AE10A0B076202160500071404D4024A01DB -081909990AAF02A206ED03CC058803E2FFFF03A0011F0B3606960906000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFF0507FFFFFFFFFFFF030DFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0B92FFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -04C804C706F306F20B870B860776077504740473080308020260025F0985098409080907057805770334033308F408F3 -0914091302CC02CB03D603D5045C045B07C707C60B040B030A1E0A1D04DE04DD05CB05CA01AC01AB072C072B0240023F -083F083E00DB00DA061606150060005F060E060D001300120694069302A402A306B706B6015E015D079F079E05B105B0 -049304920A640A630B5A0B590208020703E003DF020E020D087B087A09460945090008FF03550354037D037C08D208D1 -09620961088508840204020303DA03D901B601B50B3E0B3D0A420A410480047F05C305C207DD07DC014E014D08110810 -02AC02AB065D065C004A00490633063200890088061E061D053F053E08A308A2020602050736073501D101D004C204C1 -0440043F0A5A0A59FFFFFFFF07D107D00886FFFFFFFFFFFFFFFF08F0FFFFFFFF08E808E70308030703A403A309C909C8 -0ADD0ADC0254025307E507E40430042F07B107B00A260A250722072104C404C305460545021A02190704070300990098 -05F805F700A300A206530652004E004D0AFA0AF9012501240533053202F402F3077207710117011607B507B404970496 -03F703F60A700A6F0B7D0B7C024E024D043C043B03BE03BD08F608F508D408D309220921035F035E054C054B09950994 -0A780A7708A508A4019A01990452045101D501D40A4A0A4900EE00ED06E006DF0A02003503A90605055500F3094F0B41 -0A03003603AA0606055600F409500B42022B019D0A35076DFFFFFFFF06F804CD022C019E0A36076EFFFFFFFF06F904CE -0016030505FF039D01720870068008E9001703060600039E01730871068108EA01820AC80A3902E501E0078E04AA01A3 -01830AC90A3A02E601E1078F04AB01A4035605E3034E0074FFFFFFFF08BF000A035705E4034F0075FFFFFFFF08C0000B -0A5FFFFF01E6FFFF076FFFFF04D5FFFF0A60FFFF01E7FFFF0770FFFF04D6FFFF0656043D02C5099205E50B8C002903B3 -0657043E02C6099305E60B8D002A03B409D602D502ED0447041F05CF02090A710A570AB0000808E3FFFFFFFF06190321 -03A5028509680B1B08F1012A02C1066603A6028609690B1C08F2012B02C2066709F4028F03B906E105B201FD07F40B13 -09F5029003BA06E205B301FE07F50B1400DC09B30B7A09E4007C0281073F059000DD09B40B7B09E5007D028207400591 -097CFFFF066A0370FFFFFFFFFFFFFFFF02EE0448066B0371FFFF0975FFFF097D00F5FFFFFFFFFFFFFFFFFFFFFFFFFFFF -020A0A7209D702D6FFFFFFFFFFFF00F60387FFFF035C00BBFFFFFFFFFFFFFFFF042005D0035D00BCFFFFFFFFFFFFFFFF -09CFFFFF01C30176FFFFFFFF029BFFFF0A580AB101C40177FFFFFFFFFFFF029C03BFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -061A0322000908E4FFFFFFFFFFFF03C0FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF06FCFFFFFFFF -09BD0B2DFFFFFFFFFFFFFFFFFFFFFFFFFFFF0951FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0952FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF045904940A7907B20A4309DA01780767070902F101EC0534082E044D00DE0AEB -045A04950A7A07B30A4409DB01790768070A02F201ED0535082F044E00DF0AEC0874FFFFFFFFFFFFFFFFFFFFFFFF0875 -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0596037AFFFFFFFF0745081A00A60B25089909CC052A03FE -07B604870A92020F0AA0071D018D022105180860029D01020943065E097A0B1D00A70B260597037B052B03FF0746081B -0A930210089A09CD018E022207B70488029E01030AA1071E097B0B1E05190861FFFFFFFF0944065FFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF010C0AED065803C7050B044306B2082C -0AB201C8086C0A4F0225020B0753049C027705380B5F070B098600A8040C081205A4002F093D070508CF003B095B063A -0876015103A1067003E705D40B0B06840AA40114040407D2053A01BB02E304BB010D0AEE065903C8050C044406B3082D -0AB301C9086D0A500226020C0754049D027805390B60070C098700A9040D081305A50030093E070608D0003C095C063B -0877015203A2067103E805D50B0C06850AA50115040507D3053B01BC02E404BC030E063D003A003909D807BF0A0F0032 -0265037F037E09D9040305A10B2002660A1A0A19FFFF021EFFFF074E074DFFFFFFFFFFFFFFFFFFFF028C095EFFFFFFFF -033A033902A602A50912091108CC08CB02B902B8037303720470046F099709960B560B5507FD07FC04B204B1077A0779 -01E901E80A660A6500D600D5056E056D00660065072A07290020001F08A908A8005A0059062A0629026402630B1A0B19 -0190018F06BF06BE05BD05BC07C107C00A3C0A3B0750074F0280027F04E204E103AC03AB0B020B010958095704720471 -0328032708D608D509C109C00931093008330832057E057D042204210AAD0AAC0A130A1202180217070E070D01A601A5 -08210820010F010EFFFFFFFFFFFFFFFF0358FFFFFFFFFFFFFFFF032A0329035906D206D1FFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF050E03EC082309B801B20B8F06F701F101B8049904860242046E087D0A9F0A9D -0AF806A107FF056C04320649039400F808C40690090A02DA09180673033C015003D2084F09EB0B500AA9FFFF02E20109 -FFFFFFFFFFFFFFFFFFFFFFFF0A56FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -0A200A1F01D901D8079D079C0A990A98069F069E0140013F04F604F502BB02BA06430642003E003D064B064A05BF05BE -073C073B007700760B6C0B6B0040003F0784078304EA04E9089208910AD50AD40AC70AC602C802C7FFFFFFFF017B017A -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF02980297051505140A050A04094C094B -0A830A8207AF07AE018A01890A400A3F00AF00AE074A0749052F052E088C088B0034003305EC05EBFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00070006FFFFFFFF00C600C508AB08AA -00E800E706F106F0006800670610060F01580157FFFFFFFF0B4C0B4B0835083409C309C20504050302B002AF0A2C0A2B -0960095F087F087E0396039509810980085508540B5E0B5D0330032F0312031105A905A802120211098D098C04110410 -08690868051D051C0A280A27023E023D066D066C00FF00FE06A706A60B100B0F0661066001560155069C069B02EA02E9 -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF04D707A307A2FFFF0B720B71050804D809BA09B905860585084508440AD10AD0 -004FFFFFFFFFFFFFFFFFFFFF0B770050024C024B0B0E0B0D03B203B1FFFF0790049F049E07D907D809E309E203FB03FA -027A02790510050F011D011C080F080E094200B607080707FFFF00FA098B0B2F0B61009303510710009F009E05EA05E9 -013E013D06E606E5028E028D0550054F0436043508050804075B0B9305170791FFFF01750174075CFFFFFFFFFFFFFFFF -FFFFFFFF02F802F70310030FFFFFFFFFFFFFFFFF0AF20AF1FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF09ED09ECFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0B62FFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -036100C4043405D70935006E081706C109FD0B2A037508940336007F08BC06040A5C02CE01F80A6E05600138095606B9 -07C905760B7F07DB075E09EF04E0048C064F037902E8045006A309A101420851089C091A0042045E0635031400460B9D -0B6809E1023409B2073A054400830272077E07180A0D0ADF04BA08B701E5052903B006DC09FF028A08070B2405B901FC -035B067B034D05DF092F068702AE013AFFFFFFFFFFFFFFFFFFFF080D080CFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF02670AF309C4FFFF094D064405AA01430B780773074B04CF040004F707C2013B -02EF03ED054D0259019707A40A860AB4FFFF0453060108C7FFFFFFFFFFFFFFFF02680AF409C5FFFF094E064505AB0144 -0B790774074C04D0040104F807C3013C02F003EE054E025A019807A50A870AB5FFFF0454060208C8FFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF05C0075F07510A6709AF0AAA07EA012F02DD099E0AFB0959014507D404E304F9 -08CD0636047900E9036C072302CF0043025D0549040802D1038006740B8A0147047B078C0ACA0A7B0B51078102310483 -05C1076007520A6809B00AAB07EB013002DE099F0AFC095A014607D504E404FA08CE0637047A00EA036D072402D00044 -025E054A040902D2038106750B8B0148047C078D0ACB0A7C0B52078202320484FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF074107D60AD60A4B0B05099A02230571 -07940B3F09C6022D0763048104A2024F06AA034305C6033106C9028301BF0B37065003C5028709AD060B08D7002702EB -08B00A2F0A2D0755FFFFFFFFFFFFFFFF074207D70AD70A4C0B06099B0224057207950B4009C7022E0764048204A30250 -06AB034405C7033206CA028401C00B38065103C6028809AE060C08D8002802EC08B10A300A2E0756FFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -026B00A40463088F025101100AE906ACFFFF00B709470B43090100B908FB05E701310B4707CC080802FF03E305610836 -FFFF02A70423058304E5047707650A29FFFF03B5063E0A310890FFFF06D302C906AD026C00A504640B44025201110AEA -05E8FFFF00B809480809090200BA08FC083701320B4807CD0584030003E405620A2AFFFF02A804240A3204E604780766 -02CAFFFF03B6063FFFFFFFFFFFFF06D401490191048904D1057301E207E20B94044505C803E9069703090A4D0A8A07E8 -00C7090F0B900915008A0895072D03410100096A08660461007A0366071B089D033F04BF05220201046507A00ADA0A69 -053C046B083C096301BD0B9606EA0239FFFF066E09E602FBFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -014A0192048A04D2057401E307E30B95044605C903EA0698030A0A4E0A8B07E900C809100B910916008B0896072E0342 -0101096B08670462007B0367071C089E034004C005230202046607A10ADB0A6A053D046C083D096401BE0B9706EB023A -FFFF066F09E702FCFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -0B9807A601DC057F04E7049001C101A908420733036E00CF045F0848097E00EF068D0872015F0B15061108E102D7034A -08460AB80A6B036406BA03CD055704550B9907A701DD058004E8049101C201AA08430734036F00D004600849097F00F0 -068E087301600B16061208E202D8034B08470AB90A6C036506BB03CE05580456FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -031F072509A20084040E05F50412025B056306DD091B00690A08061703480010019507AC04AC047501EE0AA606EE030B -0AEF06CB08A604F3050507B809E80A530320072609A30085040F05F60413025C056406DE091C006A0A09061803490011 -019607AD04AD047601EF0AA706EF030C0AF006CC08A704F4050607B909E90A5408B2005503F8065A0437002308DB05ED -000000BF0376059805090536081407430757019B000207BC0A96033D044B04A60547016E02730AFD09D20B9E099C06AE -03F9065B0014036808DC05EE08B30056037705990438002408150744000100C0000307BD050A0537044C04A70758019C -02740AFE0A97033E099D06AF0548016F0015036909D30B9FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF", -); -//11648 bytes -enum fullCaseTrieEntries = TrieEntry!(ushort, 8, 7, 6)(x" -000000000000000000000000000000200000000000000140", -x" -0000000000000100000000000000048000000000000011C0", -x" -040203020202010007020202060202050202020202020202020202020202020202020202020202020202020202020202 -020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202 -020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202 -020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202 -020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202 -02020202020202020202020202020202000300020001000000070006000500040000000A00090008000D000C000B0000 -00110010000F000E00000014001300120000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -001600150000000000000000000000000000000000000000001800170000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -00000019000000000000001B001A0000001F001E001D001C002300220021002000000000000000000000002600250024 -000000000000000000000000000000000028002700000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000002C002B002A0029000000000000002D -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000002F002E0000003300320031003000000000000000000000000000000000 -000000000000000000000035003400000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000360000000000000000000000000000000000000000000000000000000000380037 -0000000000000000000000000000000000000000000000000000000000000000003C003B003A00390000003E003D0000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -0040003F0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -004200410000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000004300000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000004500440000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -00000000000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -0984008A09BEFFFF09AC00C7034406580BF400740A7C0C6A0B9100EF08B10736062905E604A608C301AC01990AE607FE -FFFF032005210569FFFFFFFFFFFFFFFF0985008B09BFFFFF09AD00C8034506590BF500750A7D0C6B0B9200F008B20737 -062A05E704A708C401AD019A0AE707FFFFFF03210522056AFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0A0BFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF08E3012E060507F2041202770A640B9A0C18013E03C105A60340004E03C9068A -02C20090097C0650FFFF00D505DA0781054302300A2A0BA70BA20AF90911077108E4012F060607F3041302780A650B9B -0C19013F03C205A70341004F03CA068B02C30091097D0651FFFF00D605DB0782054402310A2B0BA8039B0AFA09120772 -051205110A890A880C230C2201E601E507A607A5021D021C0946094500D800D70673067200650064067F067E005B005A -06C406C302D802D70889088801730172086808670C550C540B180B170504050305E505E401CD01CC0A140A1302390238 -090A0909FFFF03F60347034609DD09DC076D009F009EFFFF08A700990098076E08440BFA0BF908A80AD6013701360845 -05240523031A0AD70C310C30083F083E017B017A072A072902D002CF06EB06EA000F000E066906680079007806A0069F -00DC00DB08C608C501FB01FA079C079B01BB01BA0C4D0C4C0B080B07050C050B05B305B2084B084A0AAB0AAA0A1E0A1D -002506850684039C062B065B065A00260BE00BDF06F006F902950571028A028901C808810C0202960B8107E0FFFF01C9 -056B094F094E0AFB0A380BD00247029BFFFF050F00A100A0035600DD098A00B4068D068C001D001C040605A001180117 -FFFFFFFF0A4E040707D0087401820181057707F801C007D10B7F032903280578FFFFFFFF0276027501C6FFFF09C109C0 -FFFFFFFFFFFFFFFF063B09CF09CE09CD0BAA0BA9063D063C0812054605450BAB07E20B140B1308130B39047D047C07E3 -059E032503240B3A0C1F0C1E07E1059F0184018306B606B502E602E5065706560101010006A606A505BD05BC07140713 -0B7B0B7A0B79092801C7024807BF07BE020702060C390C380A180A170455045405F105F008CE08CD095B095A09E409E3 -092D092C03E103E0045704560BD90BD80B480B4704460445057C057B02DA02D9087708760186018508D808D7060F060E -073D073CFFFF00DE0C4F0C4E006D006C07B407B300CC00CB091B091A008200810765076405C905C8FFFFFFFFFFFFFFFF -08100ABEFFFFFFFF0A0308380510081106FA0165016402AD0A810A8002FA0458034F034E09C709C60AA70AA609FA09F9 -06F1023A0BE8043B08820C03FFFF05720AFCFFFF0B82FFFFFFFFFFFFFFFF09E5029CFFFF0BF7056CFFFF00BF0C41FFFF -069B01090BD10A3900B5FFFFFFFF0A32FFFF098B05F7FFFFFFFFFFFF0357FFFFFFFFFFFFFFFFFFFFFFFFFFFF0035FFFF -0A4F055FFFFF05A10380FFFFFFFFFFFF07F901C104590875FFFFFFFFFFFF02FBFFFF0B80FFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFF077F009CFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0A19FFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF01E801E704E604E502120211FFFFFFFF -0AEAFFFFFFFFFFFF03B3FFFF08FF0138FFFFFFFFFFFFFFFFFFFF02BCFFFFFFFFFFFF00FA08B307460A750C70FFFF0878 -019504F104F90883017F01EB0B7C0ACA0BCE0BFB0A1A05EC011903CD08A90A0C06F2FFFF068E0C3E00B6037206660358 -00A408D9076A099E00FB08B4074702BD019604F204FA0A79018001EC0B7D0ACB0BCF0BFC0A1B05ED011A03CE08AA0A0D -06F406F3068F0C3F00B703730667035900A508DA076B099F0A1F0A760C710879FFFFFFFF05EE04F30A200C400374FFFF -04EC04EB01A501A408F008EF0B420B410A310A3005D105D003F003EF0A8B0A8A095F095E031D031C0394039304850484 -03B401EF06900BFD0B3DFFFF0B7E05EF0631063001F00B3E090001390AEBFFFF0A1502B4056505390603009A0A360BBA -099800AA099A076F03C300A60348064A03F8058D0C0E07900B72029D040A075704FD013C08FB0B4501D20AC408240894 -03060B2C055A0872017402650B8D0A4C0C2609DE06C505CA005C03FC06800A9D03F9058E0C0F07910B73029E040B0758 -04FE013D08FC0B4601D30AC50825089503070B2D055B0873017502660B8E0A4D0C2709DF06C605CB005D03FD06810A9E -0A1602B50566053A0604009B0A370BBB099900AB099B077003C400A70349064B062506240B5C0B5B0C130C1202640263 -041D041C0216021508E608E50A5F0A5E097F097E05F605F503CC03CB0963096209DB09DA091909180453045204150414 -FFFFFFFF0BEB0BEAFFFFFFFFFFFFFFFF05FE05FDFFFFFFFF00F500F408B608B500670066073F073E00050004067B067A -00D400D307A407A305B505B409440943011E011D0722072101B901B805380537049504940AC10AC00C3B0C3A08350834 -0442044108BC08BB098D098C0A680A67097709760351035003EB03EA0AA90AA8023C073207310302025506330632023D -04E10AD50AD402560303080D080C04E205A905A8025A02590754075302820281068706860061006006E006DF00220021 -06DB06DA0129012805B705B602680267082308220131013007EF07EE04DC04DB04A904A80B4A0B490BC50BC402540253 -0467046603BE03BD0967096609AF09AE09C509C403650364040F040E0AB10AB00B4E0B4D08B008AF01E401E308150814 -02EB02EA0ACF0ACE018A018905340533061306120710070F0030002F06AC06AB003C003B0698069700BE00BD0C350C34 -0B8C0B8B07AE07AD0536053509230922024501970C72FFFF0B5302B6078904FB05D804BE08DF0B83014A0C2E074403DA -032A038206E403B5014C09D006E609A40BF009ED0748060C0549056F08E70AB6024D0AF0078502B8FFFF08000637019D -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF024601980C73FFFF0B5402B7078A04FC05D904BF08E00B84014B0C2F074503DB -032B038306E503B6014D09D106E709A50BF109EE0749060D054A057008E80AB7024E0AF1078602B9066208010638019E -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0556042208AD0A6001CA0C5E0766020C -01D004D904C6026104AC090D0B570B550BBC070B088605B8046C06A703C50107096006F709AA030009B806D403680168 -040808DB0A970C1A0B61FFFF0308011BFFFFFFFFFFFFFFFFFFFFFFFF0B0BFFFF043507020BAC016C09D606CA0468061C -03FE064C0342003109CB0C040AA1002908EB071D05AA0096046409130A530BA50ABA07D80232054707830519026901F3 -08A30A400B6802C4075C040005D60418FFFF03D101340BFE06FD09A60010FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -039100CD046E062E09D8007608A1072F0AAC0BF203A5093B0362008C095806600B1102F402130B2405AC014E09FB0725 -084205C40C4A085B07D40A9B052704CC06AD03A9030E048A070D0A48015808DD093009BA004A049A0693033E004C0C6C -0C320A8C02510A5907AF05900094029107F607870ABC0BA004FF09530200057303E2074A0AAE02AB08900C0806100217 -039906EE037A063909D2071902D3015003BF06820C500054FFFFFFFF0474006A03C006830C510055FFFFFFFF0475006B -055C0AC60759058F062609E00B2F0B2EFFFFFFFFFFFF0708FFFFFFFFFFFFFFFF043607030BAD016D09D706CB0469061D -03FF064D0343003209CC0C050AA2002A08EC071E05AB0097046509140A540BA60ABB07D9023305480784051A026A01F4 -08A40A410B6902C5075D040105D70419FFFF03D201350BFF06FE09A70011FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFF0561FFFFFFFFFFFF0338FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0C62FFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -050E050D076307620C530C5207ED07EC04B304B2088B088A0280027F0A2D0A2C09A909A805C705C60361036009950994 -09B509B402F302F2040D040C04990498084108400BC90BC80AD10AD0052605250623062201C501C407A0079F0260025F -08CA08C900E500E40675067400690068066D066C0015001406FC06FB02C702C607240723017701760819081806080607 -04D404D30B1A0B190C250C240225022404170416022B022A090C090B09EA09E909A109A00385038403AE03AD096F096E -0A080A0709160915022102200411041001CF01CE0C070C060AF60AF504C104C0061B061A085E085D01670166089C089B -02D202D106BE06BD005100500692069100930092067D067C058C058B093809370223022207AC07AB01EA01E905080507 -047B047A0B100B0F00DF0C5C084D084C0917049E05BA03FAFFFF0BA3FFFFFFFF098709860331033003D603D50A740A73 -0B9F0B9E0274027308660865046B046A082B082A0AD90AD807930792050A050905930592023702360774077300A300A2 -0655065400AD00AC06B406B3005700560BBF0BBE013B013A057E057D0327032607E907E8012D012C082F082E04D804D7 -0430042F0B270B260C490C48026E026D0477047603F203F1099709960971097009C309C20390038F059905980A3D0A3C -0B310B30093A093901B301B2048D048C01EE01ED0B000AFF00FD00FC074F074E0AB2003903DC066405A2010209F30C0A -0AB3003A03DD066505A3010309F40C0B024901B60AE807E4FFFFFFFF07680513024A01B70AE907E5FFFFFFFF07690514 -001A032C065C03CF018B090106E80988001B032D065D03D0018C090206E90989019B0B870AEC030C01FC080704ED01BC -019C0B880AED030D01FD080804EE01BD0386063E037E007DFFFFFFFF095C000C0387063F037F007EFFFFFFFF095D000D -0B1508ED0202008507E6072B051B0BEC0B16FFFF0203FFFF07E7FFFF051CFFFF06B7047802EC0A3A06400C5A002D03E6 -06B8047902ED0A3B06410C5B002E03E70A8202FC03140482045A062702260B2A0B0D0B6A000A0982FFFFFFFF0678034C -08560B760AC70BE30991014005F2079408570B770AC80BE40992014105F307950AA3063403EC07500609021909C80BDA -0AA4063503ED0751060A021A09C90BDB0B1F0B950C450A9001F7054F07B509470B200B960C460A9101F8055007B60948 -0A23048E06CC03A1003F0C43FFFF03360315048306CD03A2FFFF0A1CFFFF0A240AF2084EFFFFFFFF051F0581FFFF0A9F -02270B2B0A8302FDFFFFFFFFFFFF0AF30884091E038D00C5096C064EFFFFFFFF045B0628038E00C6FFFFFFFFFFFFFFFF -0A7A08E901DC018F04E707F402BE0C160B0E0B6B01DD0190FFFFFFFFFFFF02BF06FF0A6BFFFFFFFF06D8091CFFFF05D2 -0679034D000B0983FFFFFFFFFFFF0700FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF076CFFFFFFFF -0A660BF6FFFFFFFFFFFFFFFFFFFFFFFFFFFF09F7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF09F8FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF049604D50B32082C0AF70A86019107DE077903180208057F08B9048800EB0BB0 -049704D60B33082D0AF80A87019207DF077A03190209058008BA048900EC0BB10905FFFFFFFFFFFFFFFFFFFFFFFF0906 -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF05E803ABFFFFFFFF07BC08A500B00BEE092E0A7705750437 -083004C80B4B022C0B59078E01A60241056308F102C0011509E706BF0A210BE600B10BEF05E903AC0576043807BD08A6 -0B4C022D092F0A7801A70242083104C902C101160B5A078F0A220BE7056408F2FFFFFFFF09E806C0FFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF011F0BB206B904020554047E071F08B7 -0B6C01E108FD0B050243022807CA04DD029705830C2A077B0A2E00B20447089D05F9003309E10775097400450A010699 -0907016A03D306D2041E062C0BD206EC0B5D012A043D0850058501D4030A050101200BB306BA04030555047F072008B8 -0B6D01E208FE0B060244022907CB04DE029805840C2B077C0A2F00B30448089E05FA003409E20776097500460A02069A -0908016B03D406D3041F062D0BD306ED0B5E012B043E0851058601D5030B05020339069C003E003D0A8408390ABF0036 -028503B003AF0A85043C05F80BE902860ACD0ACCFFFF023BFFFF07C507C4FFFFFFFFFFFFFFFFFFFF02AE0A04FFFFFFFF -0367036602C902C809B309B20969096802DF02DE03A403A304AF04AE0A3F0A3E0C210C20088D088C04F504F407F107F0 -020502040B1C0B1B00EE00ED05BF05BE006F006E079E079D002400230940093F0063006206890688028402830BE20BE1 -01A901A8072E072D06150614083B083A0AEF0AEE07C707C602A0029F052A052903DF03DE0BC70BC609FE09FD04B104B0 -03530352097309720A6A0A6909D509D408BE08BD05CD05CC045D045C0B670B660AC30AC202350234077E077D01BF01BE -08AC08AB01220121FFFFFFFFFFFFFFFF038BFFFFFFFFFFFFFFFF03550354038C07410740FFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0557042308AE0A6101CB0C5F0767020D01D104DA04C7026204AD090E0B580B56 -0BBD070C088705B9046D06A803C60108096106F809AB030109B906D503690169040908DC0A980C1B0B62FFFF0309011C -FFFFFFFFFFFFFFFFFFFFFFFF0B0CFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -0AD30AD201F201F1081708160B520B51070A070901570156053E053D02E102E006A206A10042004106AA06A906170616 -07B207B10080007F0C370C360044004307FD07FC05320531092709260B940B930B860B8502EF02EEFFFFFFFF01940193 -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF02BB02BA055E055D0AB50AB409F009EF -0B3C0B3B0829082801A301A20AFE0AFD00B900B807C107C0057A0579092109200038003706470646FFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00090008FFFFFFFF00D000CF09420941 -00F700F60761076000710070066F066E01710170FFFFFFFF0C150C1408C008BF0A700A6F054C054B02D602D50ADF0ADE -0A060A050910090F03C803C70A290A2808E208E10C290C28035D035C033D033C060005FF022F022E0A350A34044C044B -08FA08F9056805670ADB0ADA025C025B06CF06CE01140113071207110BD70BD606C206C1016F016E0705070403110310 -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF051D081D081CFFFF0C3D0C3C0562051E0A630A6205D505D408D208D10B900B8F -0058FFFFFFFFFFFFFFFFFFFF0C420059026C026B0BD50BD403E503E4FFFF082604E004DF085A08590A8F0A8E04340433 -029A02990559055801330132089A089909E600C007780777FFFF010A0A330BF80C2C009D0381078000A900A806450644 -015501540756075502B002AF059D059C04710470088F088E07D20C6305600827FFFF018E018D07D3FFFFFFFFFFFFFFFF -FFFFFFFF031F031E033B033AFFFFFFFFFFFFFFFF0BB70BB6FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0A9A0A99FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0C2DFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -039200CE046F062F09D9007708A207300AAD0BF303A6093C0363008D095906610B1202F502140B2505AD014F09FC0726 -084305C50C4B085C07D50A9C052804CD06AE03AA030F048B070E0A49015908DE093109BB004B049B0694033F004D0C6D -0C330A8D02520A5A07B005910095029207F707880ABD0BA1050009540201057403E3074B0AAF02AC08910C0906110218 -039A06EF037B063A09D3071A02D4015105FB014808C10BCAFFFF0B640B630846FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -09F5FFFFFFFFFFFF035A001604B4069DFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF02870BB80A6DFFFF09F106A30601015A0C6607EA07C205150439053F083C0152 -03160426059A027901B0081E0B3F0B6EFFFF0490065E0964FFFFFFFFFFFFFFFF02880BB90A6EFFFF09F206A40602015B -0C6707EB07C30516043A0540083D015303170427059B027A01B1081F0B400B6FFFFF0491065F0965FFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF061807D607C80B1D0A570B70086D014603040A460BC009FF015C0854052B0541 -096A069504BA00F8039D079702F60052027D0596044302F803B106D60C56015E04BC08050B890B340C1C07FA024F04C4 -061907D707C90B1E0A580B71086E014703050A470BC10A00015D0855052C0542096B069604BB00F9039E079802F70053 -027E0597044402F903B206D70C57015F04BD08060B8A0B350C1D07FB025004C5FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF07B808690B980B010BCC0A42025705C0 -080E0C100A71024B07DA04C204E3026F07150370061E035E073802A401D80C0006B1042002A90A55066A0978002B0312 -094A0AE20AE007CCFFFFFFFFFFFFFFFF07B9086A0B990B020BCD0A43025805C1080F0C110A72024C07DB04C304E40270 -07160371061F035F073902A501D90C0106B2042102AA0A56066B0979002C0313094B0AE30AE107CDFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -028B00AE04A00924027101230BAE0717FFFF00C109EB0C0C09A200C3099C064201600C5808480892032E041A05AE08C7 -FFFF02CD045E05DC052D04B807DC0ADCFFFF03E806AF0AE40925FFFF074202F00718028C00AF04A10C0D027201240BAF -0643FFFF00C209EC089309A300C4099D08C801610C59084905DD032F041B05AF0ADDFFFF02CE045F0AE5052E04B907DD -02F1FFFF03E906B0FFFFFFFFFFFF0743016201AA04CA051705C201FE08630C64048006200424070603320B030B43086B -00D109B00C6009B600D9092A07A1036E01110A1108F704A40083039707A70932036C0505056D021E04A2081A0B9C0B28 -058904AA08CB0A0901D60002075A025DFFFF06D00A930322FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -016301AB04CB051805C301FF08640C65048106210425070703330B040B44086C00D209B10C6109B700DA092B07A2036F -01120A1208F804A50084039807A80933036D0506056E021F04A3081B0B9D0B29058A04AB08CC0A0A01D70003075B025E -FFFF06D10A940323FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -0C68082001F505CE052F04D101DA01C208CF07A9039F00E9049C08D50A2600FE06F5090301780BDD0670098002FE0378 -08D30B740B2203950727040405A404920C69082101F605CF053004D201DB01C308D007AA03A000EA049D08D60A2700FF -06F6090401790BDE0671098102FF037908D40B750B2303960728040505A50493FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -034A07990A4A008E04490652044D027B05B0074C09BC00720AB80676037C001201AE083604EF04B6020A0B5F075E0334 -0BB4073A093D053B054D08320A950B09034B079A0A4B008F044A0653044E027C05B1074D09BD00730AB90677037D0013 -01AF083704F004B7020B0B60075F03350BB5073B093E053C054E08330A960B0A094C005E043106BB04720027097A0648 -000000C903A705EA05520587089F07BA07CE01B4000608520B4F036A048604E90594018702930BC20A7E0C6E0A44071B -043206BC0018043F097B0649094D005F03A805EB0473002808A007BB000100CA0007085305530588048704EA07CF01B5 -02940BC30B50036B0A45071C05950188001904400A7F0C6FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF", -); -//5600 bytes -enum alphaTrieEntries = TrieEntry!(bool, 8, 5, 8)(x" -0000000000000000000000000000002000000000000000D0", -x" -000000000000010000000000000002C00000000000007B00", -x" -06050403030201000E0D0C0B0A09080711100F0303030303141414141414131214141414141414141414141414141414 -141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414 -141414141414141414141414141414141414141414141414141414141414141414141414141414140000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000030002000100000007000600050004000B000A00090008000F000E000D000C -001200110001001000150014000100130019001800170016001C0001001B001A001F001F001E001D001F001F001F0020 -001F001F001F001F001F002300220021001F001F00250024000100010001000100010001000100010001000100010001 -000100010001000100010001000100010001000100010001000100010026000100010001000100010001000100010001 -000100010001000100010001000100010001000100010001000100010001000100010001000100010001000100010001 -000100010001000100010001000100010001000100010001000100010001000100010001000100010029002800010027 -002D002C002B002A00010001000100010001000100010001000100010001000100010001000100010001000100010001 -00010001000100010001000100010001000100010001000100010001000100010001000100010001002E000100010001 -001F001F001F001F001F001F001F001F001F001F001F001F001F001F001F001F001F001F001F001F001F001F001F001F -001F001F001F001F001F001F001F001F0030002F0001001F00330032003100010037003600350034003A000100390038 -003E003D003C003B004200410040003F0046004500440043004A004900480047001F004D004C004B00510050004F004E -0052000100010001001F001F00540053001F001F001F001F0055001F001F001F0001000100010001001F001F001F0056 -001F001F001F001F001F001F001F001F001F001F001F001F001F005700010001001F001F001F001F001F001F001F001F -001F001F001F001F001F001F001F001F001F001F001F001F001F001F001F001F001F001F001F001F001F001F001F001F -0059005800010001005B005A001F001F0001000100010001000100010001000100010001000100010001000100010001 -0001000100010001005C0001000100010001000100010001001F001F005E005D001F001F001F001F001F001F001F001F -001F001F001F001F001F001F001F001F001F001F001F001F001F001F001F001F001F001F001F001F005F001F001F001F -001F006100600001001F001F001F001F001F001F001F001F001F001F001F0062001F001F001F001F001F001F001F001F -001F001F001F001F001F001F001F001F001F001F001F001F0066006500640063001F001F001F001F0067001F001F001F -001F006A00690068006C001F001F006B001F001F006E006D001F006F001F001F001F001F0070001F001F001F001F001F -001F001F001F001F001F001F001F001F0001000100010001000100710001000100010001000100010001000100010001 -000100010001000100720001000100010001000100010073000100010001000100010001000100010001000100010001 -000100010001000100010074000100010001000100010001000100010001000100010001000100010001000100010001 -000100010001000100010001000100010075000100010001001F007600010001001F001F001F001F001F001F001F001F -001F007700010001001F001F001F001F0001000100010001000100010001000100010001000100010001000100010001 -00780001000100010001000100010001000100010001000100010001000100010079000100010001001F001F001F001F -001F001F001F001F001F001F001F001F001F001F001F001F001F001F001F001F001F001F001F001F001F001F001F001F -001F001F001F001F001F001F001F001F001F001F001F001F001F001F001F001F001F001F001F001F001F001F001F001F -001F001F001F001F001F001F001F001F001F001F001F001F001F001F001F001F001F001F001F001F001F001F001F001F -001F001F001F001F001F001F001F001F001F001F001F001F001F001F001F001F000000000000000007FFFFFE07FFFFFE -0420040000000000FF7FFFFFFF7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0000501F0003FFC30000000000000000BCDF000000000020 -FFFFFFFBFFFFD740FFBFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFC03FFFFFFFFFFFFFFFF -FFFEFFFFFFFFFFFFFFFFFFFF027FFFFFBFFF0000000001FF000787FFFFFF00B6FFFFFFFF07FF0000FFFFC000FEFFFFFF -FFFFFFFFFFFFFFFF9C00E1FE1FEFFFFFFFFFFFFFFFFF0000FFFFFFFFFFFFE0000003FFFFFFFFFFFF043007FFFFFFFC00 -00001FFFFCFFFFFFFFFF07FF01FFFFFFFFFFFFFF00007EFFFFFF03F8FFF003FFEFFFFFFFFFFFFFFFFFFE000FFFE1DFFF -E3C5FDFFFFF99FEF1003000FB080599FC36DFDFFFFF987EE003F00005E021987E3EDFDFFFFFBBFEE1E00000F00011BBF -E3EDFDFFFFF99FEE0002000FB0C0199FC3FFC718D63DC7EC0000000000811DC7E3FFFDFFFFFDDFFF0000000F27601DDF -E3EFFDFFFFFDDFEF000E000F60601DDFE7FFFFFFFFFDDFFFFC00000F80F05DDF2FFBFFFFFC7FFFEE000C0000FF5F807F -07FFFFFFFFFFFFFE000000000000207F3BFFFFAFFFFFF7D600000000F000205F0000000000000001FFFE1FFFFFFFFEFF -1FFFFFFFFEFFFF0F0000000000000000F97FFFFFFFFFFFFFFFFFFFFFFFFF0000FFFFFFFF3C00FFFFF7FFFFFFFFFF20BF -FFFFFFFFFFFFFFFFFFFFFFFF3D7F3DFF7F3DFFFFFFFF3DFFFFFFFFFFFF7FFF3DFFFFFFFFFF3DFFFF0000000007FFFFFF -FFFFFFFF0000FFFF3F3FFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFF9FFFFFFFFFFFFFFFFFFF07FFFFFE01FFC7FFFFFFFFFF000FFFFF800FFFFF000DDFFF000FFFFF -FFCFFFFFFFFFFFFF00000000108001FFFFFFFFFF0000000001FFFFFFFFFFFFFFFFFF07FFFFFFFFFF003FFFFFFFFFFFFF -01FF0FFF7FFFFFFF001F3FFFFFFF0000FFFF0FFFFFFFFFFF00000000000003FFFFFFFFFF0FFFFFFF001FFFFE7FFFFFFF -80000080000000000000000000007001FFEFFFFFFFFFFFFF0000000000001FEFFC00F3FFFFFFFFFF0003FFBFFFFFFFFF -007FFFFFFFFFFFFF3FFFFFFFFC00E000E7FFFFFFFFFF01FF046FDE0000000000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFF001FFF8000000000FFFFFFFF3F3FFFFF3FFFFFFFAAFF3F3F5FDFFFFFFFFFFFFF1FDC1FFF0FCF1FDC -00000000000000008002000000000000000000001FFF00000000000000000000F3FFBD503E2FFC84FFFFFFFF000043E0 -00000000000001FF00000000000000000000000000000000000000000000000000000000000000000000000000000000 -00000000000000000000000000000000FFC0000000000000000003FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFF000C781FFFFFFFFFFFFF20BFFFFFFFFF000080FFFFFFFFFF7F7F7F7F007FFFFFFFFFFFFF7F7F7F7F -00008000000000000000000000000000000000000000000000000000000000001F3E03FE000000E0FFFFFFFFFFFFFFFE -FFFFFFFEE07FFFFFF7FFFFFFFFFFFFFFFFFEFFFFFFFFFFE0FFFFFFFFFFFFFFFFFFFFFFFF00007FFFFFFF000000000000 -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0000000000000000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -0000000000001FFF3FFFFFFFFFFF000000000C00FFFF1FFF8FF07FFFFFFFFFFFFFFFFFFFFFFFFFFF0000FFFFFFFFFFFF -FFFFFFFCFF800000FFFFFFFFFFFFFFFFFFFFFFFFFFFFF9FFFFFC000003EB07FF000000FFFFFFFFBF000FFFFFFFFFFFFF -FFFFFFFFFFFFFFFFE8FC00000000002FFFFF07FFFFFFFC001FFFFFFF0007FFFFFFF7FFFFFFFFFFFF7C00FFFF00008000 -007FFFFFFFFFFFFFFC7FFFFF00003FFF7FFFFFFFFFFFFFFF003CFFFF38000005FFFF7F7F007E7E7EFFFF03FFF7FFFFFF -FFFFFFFFFFFFFFFF000007FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF000FFFFFFFFF0FFFFFFFFFFFF87F -FFFFFFFFFFFFFFFFFFFF3FFFFFFFFFFFFFFFFFFFFFFFFFFF0000000003FFFFFF5F7FFDFFE0F8007FFFFFFFFFFFFFFFDB -0003FFFFFFFFFFFFFFFFFFFFFFF800003FFFFFFFFFFFFFFFFFFFFFFFFFFF0000FFFFFFFFFFFCFFFF0FFF0000000000FF -0000000000000000FFDF000000000000FFFFFFFFFFFFFFFF1FFFFFFFFFFFFFFF07FFFFFE00000000FFFFFFC007FFFFFE -7FFFFFFFFFFFFFFF000000001CFCFCFCB7FFFF7FFFFFEFFF000000003FFF3FFFFFFFFFFFFFFFFFFF07FFFFFFFFFFFFFF -0000000000000000001FFFFFFFFFFFFF0000000000000000000000000000000000000000000000000000000000000000 -FFFFFFFF1FFFFFFF000000000001FFFFFFFFE000FFFFFFFF07FFFFFFFFFF07FFFFFFFFFF3FFFFFFF00000000003EFF0F -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00003FFFFFFF0FFFFFFFFF0FFFFFFFFF00FFFFFFFFFFF7FF000FFFFFFFFF -1BFBFFFBFFB7F7FF0000000000000000007FFFFFFFFFFFFF000000FF003FFFFF07FDFFFFFFFFFFBF0000000000000000 -91BFFFFFFFFFFD3F007FFFFF003FFFFF000000007FFFFFFF0037FFFF0000000003FFFFFF003FFFFF0000000000000000 -C0FFFFFFFFFFFFFF0000000000000000003FFFFFFEEFF06F1FFFFFFF00000000000000001FFFFFFF0000001FFFFFFEFF -003FFFFFFFFFFFFF0007FFFF003FFFFF000000000003FFFF0000000000000000FFFFFFFFFFFFFFFF00000000000001FF -0007FFFFFFFFFFFF0007FFFFFFFFFFFF000000FFFFFFFFFF000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000031BFFFFFFFFFF0000000000000000FFFF00801FFFFFFFFFFF00000000003F -FFFF000000000003007FFFFF0000001FFFFFFFFFFFFFFFFF003E00000000003F01FFFFFFFFFFFFFF000001FFFFFF0004 -0007FFFFFFFFFFFF0047FFFFFFFF00F0FFFFFFFFFFFFFFFF000000001400C01EC09FFFFFFFFBFFFF0000000000000003 -FFFF01FFBFFFBD7F000001FFFFFFFFFFE3EDFDFFFFF99FEF0000000FE081199F00000000000000000000000000000000 -FFFFFFFFFFFFFFFF00000003800007BBFFFFFFFFFFFFFFFF00000000000000B300000000000000000000000000000000 -7F3FFFFFFFFFFFFF000000003F0000007FFFFFFFFFFFFFFF0000000000000011013FFFFFFFFFFFFF0000000000000000 -000007FFE7FFFFFF000000000000007F0000000000000000000000000000000001FFFFFFFFFFFFFF0000000000000000 -FFFFFFFF0000000080000000FFFFFFFF99BFFFFFFF6FF27F0000000000000007FFFFFCFF000000000000001AFCFFFFFF -7FE7FFFFFFFFFFFFFFFFFFFFFFFF0000FFFF000020FFFFFF01FFFFFFFFFFFFFF7F7FFFFFFFFFFDFFFFFC000000000001 -007FFEFFFFFCFFFF0000000000000000B47FFFFFFFFFFB7FFFFFFDBF000000CB00000000017B7FFF0000000000000000 -000000000000000000000000000000000000000000000000007FFFFF00000000C7FFFFFFFFFDFFFF0000000000000001 -00010000000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0000000003FFFFFF0000000000000000 -FFFFFFFFFFFFFFFF00007FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF000000000000000F -0000000000000000000000000000000000000000000000000000000000000000FFFFFFFFFFFF00000001FFFFFFFFFFFF -0000FFFFFFFFFFFF000000000000007E00000000000000000000000000000000FFFFFFFFFFFFFFFF000000000000007F -0000000000000000000000000000000001FFFFFFFFFFFFFFFFFF00007FFFFFFF7FFFFFFFFFFFFFFF00003FFFFFFF0000 -0000FFFFFFFFFFFFE0FFFFF80000000F000000000000FFFF00000000000000000000000000000000FFFFFFFFFFFFFFFF -00000000000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFFFFFF87FF00000000FFFF80FF0003000B00000000 -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFF00000000003FFFFF00000000000001FF000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000006FEF00000000000000040007FFFFFFFFFFFF00F000270000 -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0FFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFF1FFF07FFFFFFFFFF0000000043FF01FF0000000000000000FFFFFFFFFFFFFFFFFFFFFFFFFFDFFFFF -EBFFDE64DFFFFFFFFFFFFFFFFFFFFFEF7BFFFFFFDFDFE7BFFFFFFFFFFFFDFC5FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF3FFFFFFFFFF7FFFFFFF7FFFFFDFFDFFFFFFFDFFFFFFFFF7FFFFFFF7FFF -FFFFFDFFFFFFFDFF0000000000000FF7000007E07FFFFFFF000000000000000000000000000000000000000000000000 -FFFF07DBF9FFFF7F00003FFFFFFFFFFF000000000000800000000000000000003F801FFFFFFFFFFF0000000000004000 -000000000000000000000000000000000000000000000000000000000000000000003FFFFFFF000000000FFFFFFFFFFF -00000000000000000000000000000000000000000000000000000FFFFFFF000000000000000000000000000000000000 -00000000000000007FFF6F7F00000000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF000000000000001F -FFFFFFFFFFFFFFFF000000000000088F000000000000000000000000000000000AF7FE96FFFFFFEF5EF7F796AA96EA84 -0FFFFBEE0FFFFBFF0000000000000000FFFF000000000000FFFF03FFFFFF03FF00000000000003FF0000000000000000 -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00000000FFFFFFFF03FFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF3FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0003FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFF0001FFFFFFFFFFFFFFFFFFFFFFFF000000003FFFFFFF00000000000000000000000000000000 -000000003FFFFFFF000000000000000000000000000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFFFFFF07FF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0000FFFFFFFFFFFF0000000000000000 -0000000000000000000000000000000000000000000000000000000000000000", -); -//3392 bytes -enum markTrieEntries = TrieEntry!(bool, 8, 5, 8)(x" -000000000000000000000000000000200000000000000088", -x" -000000000000010000000000000001A00000000000004800", -x" -04020302020201000A090802070206050202020202020202020202020202020202020202020202020202020202020202 -020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202 -02020202020202020202020202020202020202020202020B020202020202020202020202020202020000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -00000000000000000000000000000000000100000000000000050004000300020009000800070006000D000C000B000A -000F00000000000E00100000000000000014001300120011000000000016001500000000000000170000000000000000 -00000000000000000000000000190018000000000000001A000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000001B00000000001F001E001D001C0000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000200000000000000000002100000000 -0024002300220000000000000000000000000025000000000028002700260000002C002B002A00290030002F002E002D -000000330032003100370036003500340000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000380000000000000000000000000000000000000000000000000000000000000000 -003A003900000000003B0000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000003C000000000000000000000000000000000000000000000000003D000000000000 -0000003F003E000000000000000000000000004000000000000000000000000000000042003A00410000000000000043 -000000000045004400000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000046000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -00000000000000000000000000000000FFFFFFFFFFFFFFFF0000FFFFFFFFFFFF00000000000000000000000000000000 -0000000000000000000000000000000000000000000003F8000000000000000000000000000000000000000000000000 -BFFFFFFFFFFE000000000000000000B60000000007FF000000010000FFFFF800000000000000000000003D9F9FC00000 -FFFF00000002000000000000000007FF0001FFC000000000200FF8000000000000003EEFFBC00000000000000E000000 -00000000FF000000FFFFFFFBFFFFFC00DC0000000000000F0000000C00FEFFFFD00000000000000E4000000C0080399F -D00000000000000E0023000000023987D00000000000000EFC00000C00003BBFD00000000000000E0000000C00E0399F -C0000000000000040000000000803DC7D00000000000001F0000000C00603DDFD00000000000000E0008000C00603DDF -D80000000000000F0000000C00803DDF000000000000000E000C0000FF5F840007F20000000000000000000000007F80 -1FF20000000000000000000000007F00C2A0000003000000FFFE0000000000001FFFFFFFFEFFE0DF0000000000000040 -7FFFF80000000000001E3F9DC3C00000000000003C00BFFC0000000000000000000000000000000000000000E0000000 -00000000000000000000000000000000001C0000003C0000000C0000000C0000FFF000000000000000000000200FFFFF -000000000000B8000000000000000000000002000000006000000000000000000FFF0FFF000000000000000000000000 -00000000000000000000000000000000000000000F8000009FFFFFFF7FE00000FFFF0000000000000000000000007FFF -FFF000000000001F000FF8000000001F00003FFE00000007000FFFC00000000000FFFFF0000000000000000000000000 -0000000000000000039021FFFFF70000000000000000000000000000000000000000000000000000FFFFFFFFFFFFFFFF -0000000000000000000000000000000000000000000000000001FFFFFFFF000000000000000000000000000000000000 -00000000000000000003800000000000000000000000000080000000000000000000000000000000FFFFFFFF00000000 -0000FC000000000000000000000000000000000006000000000000000000000000000000000000003FF7800000000000 -00000000C00000000003000000000000000010F8000008440000000000000000FFF00000000000038003FFFF0000003F -00003FC00000000000000000000FFF80FFF800000000000F0000002000000001007FFE00000000003800000000003008 -C19D0000000000000060F80000000002000000000000000000000000000000000000000000000000000037F800000000 -00000000400000000000000000000000000000000000000000000000000000000000FFFF0000FFFF0000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000002000000000000000 -0000000000000000000000000000000000000000000000000000000100000000000000000000000007C0000000000000 -00000000000000000000000000000000870000000000F06E000000000000000000000000000000000000006000000000 -000000F00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -0000180000000000E0000000000000000000000000000000000000000001FFC0000000000000003C0000000000000000 -FF00000000000007801900000000007F07FF0000000000070000000000000004001FFF80000000070008000000000060 -FFF8000000000007000000000000DE0140FFF0000000000000000000000000020000000000000000000007FF80000000 -D80000000000000F001F1FCC0080399F00000000000000000000000000000000FFE0000000000000000000004000007F -FFFF000000000000000000000000000F00000000000000000000000000000000FF3F8000000000000000000030000001 -FFFF000000000000000000000000000100FFF80000000000000000000000000000000FFFE00000000000000000000000 -0000000000000000000000000000000007FFF00000000000000000000000000000000000000000000000000000000000 -79BF000000000000000000000000000D000000000000000000000011FCFE00007BF80000000007FE000000000FFE0080 -0000000003FFFC000000000000000000FF7F8000000000000000000000000000007FFEFFFFFC00000000000000000000 -B47E00000000000000000000000000BF0000000000FB7C00000000000000000000000000000000000000000000000000 -00000000000000000078000000000000C7F000000000000B000000000000000700000000000000000000000000000000 -000000000000000000000000003FFF810000000000000000000000000000000000000000000000000000000000000000 -0000000000000000001F000000000000007F000000000000000000000000000000000000000000000000000000000000 -0000000000000000FFFFFFFFFFFE800000000000000780FF000300100000000000000000000000000000000000000000 -00000000600000000000000000000000FFFF3FFFFFFFFFFF000000000000007F00000000000000000000000000000000 -0000000000000000F807E3E00000000000003C0000000FE700000000000000000000000000000000000000000000001C -00000000000000000000000000000000F87FFFFFFFFFFFFF00201FFFFFFFFFFF0000FFFEF80000100000000000000000 -000007DBF9FFFF7F00000000000000000000000000008000000000000000000000000000000000000000000000000000 -00004000000000000000F000000000000000000000000000000000000000000000000000000000000000F00000000000 -00000000000000000000000000000000000000000000000000000000007F0000000000000000000000000000000007F0 -00000000000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0000FFFFFFFFFFFF -0000000000000000000000000000000000000000000000000000000000000000", -); -//2848 bytes -enum numberTrieEntries = TrieEntry!(bool, 8, 6, 7)(x" -0000000000000000000000000000002000000000000000D0", -x" -000000000000010000000000000002C00000000000002500", -x" -040203020202010009080202070206050202020202020202020202020202020202020202020202020202020202020202 -020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202 -020202020202020202020202020202020202020202020202020202020202020202020202020202020000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000002000200010000000200020002000200020002000200020005000200040003 -000700060002000200090008000600060006000B0006000A0002000D000C000C00020002000E00050002000F00020002 -00020002000200020011000200100002001300120002000E0000000C0014000200020002000200150002000200020002 -0019001800170016000200020002000200020002001B001A001D001C0002000200020002000200020002000200020002 -00020002001E00020002000200020002002000020002001F000200020022002100020002000200020002000200020002 -000200020002000200020002000200020002000200020002000200020002000200020002000200020002000200020002 -000200020002000200020002000200020002000200020002000200020002000200020002000200020002000200020002 -000200020002000200020002000200020002000200020002000200020002000200020002000200020002000200020002 -000200020002000200020002000200020002000200020002000200020002000200020002000200020002000200060023 -00250017000C0024000400020002000C0002000200020002000200020002000200020002000200020002000200020002 -000200020002000200020002000200020002000200020002000200020002000200020002000200020002000200020002 -000200020002000200020002000200020002000200020002000200020002000200020002000200020002000200020002 -000200020002000200020002000200020002000200020002000200020002000200020002000200020002000200020002 -0002000200020002000200020002000200020002000200020002000E000200020027002600020002002A002900280002 -00020002002300020002000200020002002E002D002C002B003200310030002F00020000003300020036003500020034 -0039003800040037000200020004000200020002000C000C0002003A0005000C0002000C003B00020002000200020002 -0023000C0002003C003D000C0002000200020002000200020002000200020002000200020002003E0002000200020002 -000200020002000200020002000200020002000200020002000200020002000200020002000200020002000200020002 -000200020002000200020002000200020002000200020002000200020002000200020002000200020002000200020002 -000200020002000200020002000200020002000200020002000200020002000200020002000200020002003F00050003 -000200020002000200020002004000020002000200020002000200020002000200020002000200020002000200020002 -000200020002000200020002000200020002000200020002000200020002000200020002000200020002000200020002 -000200020002000200020002000200020002000200020002000200020002000200020002000200020002000200020002 -000200020002000200020042004100020002000200020002004300020002000200020002000200020002000200020002 -000200020002000200020002000200020002000500020002000200020004000200020002000400020002000200020002 -0002000C0044000200020002000200020002004700460045000200020002000200020048000200020002000200020002 -000200020002000200020002000200020002000200020002000400020002000200020002000200020002000200020002 -000200020002000200020002000200020002000200020002000200020002000200020002000200020002000200020002 -000200020002000200020002000200020002000200020002000200020002000200020002000200020002000200020002 -000200020002000200020002000200020002000200020002000200020002000203FF0000000000000000000000000000 -720C0000000000000000000000000000000000000000000000000000000000000000000000000000000003FF00000000 -000000000000000003FF000000000000000000000000000000000000000003FF00000000000000000000FFC000000000 -000000000000000003F0FFC000000000000000000000000000FCFFC00000000000000000000000000007FFC000000000 -00000000000000007F00FFC000000000000000000000000001FFFFC07F00000000000000000000000000000003FF0000 -000FFFFF0000000000000000000000000000000003FF0000000000000000000000000000000000001FFFFE0000000000 -00000000000000000001C00000000000000000000000000003FF03FF000000000000000000000000000000000000FFC0 -00000000000000000000000007FF00000000000003FF03FF000000000000000000000000000000000000000003FF03FF -000000000000000003F100000000000000000000000003FF00000000000000000000000000000000FFFFFFFFFFFF0000 -00000000000003E700000000000000000000000000000000FFFFFFFF00000000000000000FFFFFFFFFFFFC0000000000 -0000000000000000FFC000000000000000000000000FFFFF000000000000000000000000000000002000000000000000 -070003FE00000080000000000000000000000000003C00000000000000000000000003FF0000000000000000FFFEFF00 -FFFE0000000003FF0000000000000000000003FF000000000000000000000000003F0000000000000000000000000000 -000000000000000003FF000003FF0000000FFFFFFFFFFF8001FFFFFFFFFFFFFF0000000000000C000000000000000000 -00000000000000000FFFFFFE000000000000000F000000000000000000000402000000000000000000000000003E0000 -0000000000000000FE000000FF0000000000FF8000000000F800000000000000000000000FC000000000000000000000 -3000000000000000FFFFFFFFFFFCFFFF000000000000000060000000000001FF00000000E00000000000F80000000000 -0000000000000000FF000000FF0000000000FE000000000000000000000000000000000000000000FC00000000000000 -00000000000000007FFFFFFF000000000000007FE000000000000000001E000000000000000000000000000000000FE0 -00000000000000000000FFFFFFFC0000FFC000000000000000000000000000000000000000000000001FFFFE03FF0000 -0FFF000000000000000000000000000000000000000000000007FFFF00000000000000000000000000001FFFFFFF0000 -000000000000000000000000001FFFFFFFFFFFFFFFFFFFFF00007FFFFFFFFFFF000000000000000000000003FBFF0000 -00000000007FFFFF00000000000000000000000000000000000FFFFF000FFFFF000000000000000001FFFFFF00000000 -0000000000000000FFFFFFFFFFFFC0000000000000000000000000000000FF800000000000000000FFFE000000000000 -001EEFFFFFFFFFFF00000000000000003FFFBFFFFFFFFFFE00000000000000000000000000001FFF0000000000000000 -000000000000000003FF000000000000", -); -//3360 bytes -enum punctuationTrieEntries = TrieEntry!(bool, 8, 5, 8)(x" -000000000000000000000000000000200000000000000080", -x" -000000000000010000000000000001800000000000004900", -x" -04020302020201000A090802070206050202020202020202020202020202020202020202020202020202020202020202 -020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202 -020202020202020202020202020202020202020202020202020202020202020202020202020202020000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -00000000000000000000000000000000000200010001000000050004000300010001000800070006000C000B000A0009 -000E00010001000D001100100001000F0015001400130012000100010001001600180001000100170019000100010001 -00010001001A00010001001D001C001B000100010001001E000100010001000100010001000100010001000100010001 -000100010001000100010001000100010001000100010001000100010001000100010001000100010001000100010001 -000100010001000100010001000100010001000100010001000100200001001F00240023002200210001000100010001 -000100010001000100010001000100010001000100010001000100010001000100010001000100010001000100010001 -000100010001000100010001000100010001000100010001000100010001000100010001000100010027002600250001 -002900010028000100010001002A0001002E002D002C002B0030002F0001000100010033003200310037003600350034 -003B003A00390038003E003D0001003C0001000100010001000100010001003F00010001000100010040000100010001 -000100010001000100010001000100010001000100010001000100010001000100010001000100010001000100010001 -004200410001000100440043000100010001000100010001000100010001000100010001000100010001000100010001 -000100010001000100010001000100010001000100010001000100010001000100010001000100010001000100010001 -000100010001000100010001000100450001000100010001000100010001000100010001000100010001000100010001 -000100010001000100010001000100010001004600010001000100010001000100010001000100010001000100010001 -000100010047000100010001000100010001000100010001000100010001000100010001000100010001000100010001 -000100010001000100010001000100010001000100010001000100010001000100010001000100010001000100010001 -000100010001000100010001000100018C00F7EE0000000028000000B800000188C00882000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000004000000000000000 -00000000000000800000000000000000000000000000000000000000FC00000040000000000006000018000000000049 -00000000E800360000003C0000000000000000000000000000000000001000000000000000003FFF0000000000000000 -000000000000000003800000000000007FFF000000000000000000004000000000000000000000000000000000000000 -000000000000000000010030000000000000000000000000200000000000000000000000000000000040000000000000 -000000000000000000010000000000000000000000000000008000000000000000000000000000100000000000000000 -00000000000000000000000000000000000000000000000000100000000000000000000000000000000000000C008000 -000000000000000000000000000000003C0000000017FFF00000000000000000000000000000002000000000061F0000 -0000000000000000000000000000FC00000000000000000008000000000000000000000000000000000001FF00000000 -000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000 -000000000000000000004000000000000000000018000000000038000000000000600000000000000000000000000000 -0000000000000000000000000770000000000000000007FF000000000000000000000000000000000000000000000000 -000000000000000000000000000000300000000000000000000000000000000000000000C00000000000000000000000 -00003F7F000000000000000000000000000000000000000060000001FC0000000000000000000000F000000000000000 -F800000000000000C000000000000000000000000000000000000000000800FFFFFF00FFFFFF0000600000007FFBFFEF -000000000000600000000000000000000000060000000F00000000000000000000000000000000000000000000000000 -0000000000000000003FFF000000000000000000000000000000FFC00000006000000000000000000000000000000000 -0000000001FFFFF8300000000F000000000000000000000000000000000000000000000000000000DE00000000000000 -0000000000000000000100000000000000000000000000000000000000000000FFFF7FFFFFFFFFFF000000003FFCFFFF -0000000000000000000000000000000020010000FFF3FF0E000000000000000000000001000000000800000000000000 -000000000000000000000000000000000000000000000000C000000000000000000000000000E0004008000000000000 -000000000000000000FC000000000000000000000000000000F00000000000000000000000000000170000000000C000 -0000C000000000000000000080000000000000000000000000000000C0003FFE000000000000000000000000F0000000 -000000000000000000030000C00000000000000000000000000000000000000000000000000000000000080000000000 -C000000000000000000000000000000000000000000000000000000000000000FFFF000003FF000000000D0BFFF7FFFF -00000000000000000000000000000000B80000018C00F7EE0000003FA800000000000000000000000000000000000000 -000000000000000700000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000008000000000000000000100000000000000000000000080000000000000000000000000000000000000000000 -000000000000000000000000008000000000000000000000000000000000000080000000800000000000000000000000 -0000000000000000000000000000000000000000000000008000000001FF00000000000000000000007F000000000000 -FE000000000000000000000000000000000000001E000000000000000000000000000000000000000000000000000000 -0000200000000000000000000000000000000000000000000000000003E0000000000000000003C00000000000000000 -00000000000000000000000000003F80D80000000000000000000000000000030000000000000000003000000000000F -000000000000000000000000E80021E03F00000000000000000000000000000000000200000000000000000000000000 -0000000000000000000000002C00F8000000000000000000000000000000004000000000000000000000000000000000 -00000000000000000000000000FFFFFE000000000000000000001FFF0000000E02000000000000000000000000000000 -700000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000007000000000000000000000000400000000 -8000000000000000000000000000007F00000007DC000000000000000000000000000000000003FF0000000000000000 -000000000000000000000000000000000000000000000000000300000000003E00000000000000000000000000000000 -00000000000000000000000000000000000000000000000001800000000000000000000000000000000000000000FFF8 -000000000000000080000000000000000000000000000000001F00000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000600000000000000000000000000000000C00000000000 -000000000000000000200000000000000F80000000000000000000000000001000000000000000000000000000000000 -000000000000000000000000000000000000000007800000000000000000000000000000000000000000000000000000 -000000000000000000000004000000000000000000000000000000000000000000000000800000000000000000000000 -000000000000000000000000000000000000000000000F800000000000000000000000000000000000000000C0000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", -); -//3424 bytes -enum symbolTrieEntries = TrieEntry!(bool, 8, 5, 8)(x" -000000000000000000000000000000200000000000000080", -x" -000000000000010000000000000001800000000000004B00", -x" -05030403030201000A090803070303060303030303030303030303030303030303030303030303030303030303030303 -030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303 -030303030303030303030303030303030303030303030303030303030303030303030303030303030000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000030002000100000007000600050004000B000A00090008000F000E000D000C -0011000100010010001300120001000100150001001400010016000100010001001A001900180017001C00190019001B -001E0019001D0019002100200001001F0019002400230022000100010001000100010001000100010001000100010001 -000100010001000100010001000100010001000100010001000100010025000100010001000100010001000100010001 -000100010001000100010001000100010001000100010001000100010001000100010001000100010001000100010001 -000100010001000100010001000100010001000100010001000100010001000100010001000100010027000100010026 -002A00290001002800010001000100010001000100010001000100010001000100010001000100010001000100010001 -000100010001000100010001000100010001000100010001000100010001000100010001000100010001000100010001 -002B000100010001002E002D002C000100010001002F0001000100010001000100010031000100300001000100010001 -0001000100010001000E0001000100010001000100010001003200010001000100010001000100010001000100010001 -003300010001000100010001000100010001000100010001000100010001000100010001000100010001000100010001 -000100010001000100010001000100010001000100010001000100010001000100010001000100010001000100010001 -000100010001000100010001000100340001000100010001000100010001000100010001000100010035000100010001 -0039003800370036003B003A000100010001003C0019001900010001000100010001003E003D00010001000100010001 -0001000100010001000100410040003F0019004400430042004600450019001900490048001900470001000100010001 -000100010001000100010001000100010001000100010001000100010001000100010001000100010001000100010001 -00010001000100010001000100010001700008100000000050000001400000000113D37C000000000080000000800000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -0000000000000000FFFFAFE0FFFC003C0000000000000000002000000000000000000000000000300040000000000000 -000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000 -000000000000E0000000000000000000000000000000C9C0000000000000000000000000000000006000020040000000 -000000000000000000000000000000000000000000000000C04000000000000000000000000000000000000000000000 -000000000000010000000000000000000000000000000000000000000000000000000000000000000C0C000000000000 -000000000000000000000000000000000000000000000000000200000000000000000000000000000001000000000000 -000000000000000007F80000000000000000000000000000800000000000000000000000000000000000000000000000 -000000000000000002000000000080000000000000000000000000000000000080000000000000000000000000000000 -0000000000000000000000000000000001500000FCE8000E0000000000000000C0000000000000000000000001E0DFBF -0000000000000000000000000000000000000000C0000000000000000000000000000000000000000000000000000000 -0000000003FF000000000000000000000000000000000000000020000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000001 -0000000000000000FFFFFFFFC000000000000000000000001FF007FE0000000000000000000000000000000000000000 -00000000000000000000000000000000A0000000000000006000E000E000E00300000000000000001C00000000040010 -FFFFFFFF00001C0000000000000000010C0042AFC1D0037B000000000000BC1FFFFFFFFFFFFF0C00FFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF9FFFFFFF0FFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0000007FFFFFFFFF00000000000007FFFFFFFFFFF0000000000003FFFFFFFFFF -FFFFFFFFFFFFFFFF000000FFFFFFFFFFFFFFFFFFFFF00000FFFF003FFFFFFF9FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFE000007CFFFFFFFF0FFFFFFFFFFFFFFFFFFFFFFFFCFFFFFFFFFFFFFFFFFFFFFFFBFFFFFFFFFFFFFFFFFFFFF -000000000000000000000000000000000000000000000000000007E00000000000000000000000000000000000030000 -FFFFFFFFFBFFFFFF000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0000003FFFFF -C0C00001000C001000000000000000000000000018000000000000000000000000000000000000000000000000000000 -00000000FFC300000000800FFFFFFFFFFFFFFC007FFFFFFFFFFFFFFF000100FF0001FFFFFFFFFC00FFFFFFFFFFFFFFFF -000000000000000000000000000000000000000000000000FFFFFFFFFFFFFFFF00000000000000000000000000000000 -FFFFFFFFFFFF0000000000000000007F00000003007FFFFF000000000000000000000000000006000000000000000000 -03C00F000000000000000000000000000000000000000000000000000000000000000000000000000380000000000000 -00000000000000000000000000000000000000000000000000000C000800000000000000000000000000000000000000 -00000200000000000000000000000000FFFC00000000000000000000000000070000000000000000000000000000FFFF -0000000000000000F0000000000080000000000000000000000002740000000000000000000000000000000000000000 -40000000700008100000000050000001000000000000000030007F7F00000000FF80000000000000FE00000000000000 -000000011FFF73FF1FFFFFFFFFFF00000000000000000000018000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000 -00000000000000000003FFFFFFE00000F000000000000000000000000000002000000000000000000000000000000000 -00000000000000000000000000000000000000001000000000000000000000000000000000000000FFFFFFFFFFFF0000 -FFFFFFFFFFFFFFFF000000000000000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF003FFFFFFFFFFFFF -FFFFFE7FFFFFFFFF00001C1FFFFFFFFFFFFFC3FFFFFFF018000007FFFFFFFFFFFFFFFFFFFFFFFFFF0000000000000023 -00000000000000000000000000000000FFFFFFFFFFFFFFFF00000000007FFFFF00000000000000000000000000000000 -000000000000000000000000000000000000000000000000080000000800000200200000002000000000800000008000 -000002000000020000000000000000080780000000000000FFDFE00000000000000000000000006F0000000000000000 -000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000000 -000000000000000080000000000000000000000000000000000000000000000000011000000000000000000000000000 -000040000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -00000000000000000003000000000000FFFF0FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE7FFF000FFFFF003FFFFFFFFEFFFE -FFFFFFFFFFFFE000FFFFFFFFFFFFFFFF00003FFFFFFFFFFFFFFFFFC0000000000FFFFFFFFFFF00070000003F000301FF -00000000000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF1FFF1FFFF0FFFFFF -FFFFFFFFFFFFFFFFF87FFFFFFFFFFFFFFFFFFFFFFFFFFFFF00010FFF03FFFFFFFFFFFFFFFFFF0FFFFFFFFFFF03FF00FF -00033FFFFFFF00FF0000000000000000FFFFFFFFFFFFFFFF1FFF3FFF000FFFFFBFFFFFFFFFFF01FF01FF01FF0FFFC03F -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF7FFFF00000000000007FF00000000000000000000000000000000 -00000000000000000000000000000000", -); -//6080 bytes -enum graphicalTrieEntries = TrieEntry!(bool, 8, 5, 8)(x" -0000000000000000000000000000002000000000000000D0", -x" -000000000000010000000000000002C00000000000008A00", -x" -05040302020201000D0C0B0A09080706100F0E0202020202131313131313121113131313131313131313131313131313 -131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313 -131313131313131313131313131313131313131313131314131313131313131313131313131313130000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -00000000000000000000000000000000000200010001000000050004000300010009000800070006000D000C000B000A -0010000F0001000E00120011000100010016001500140013001800010001001700010001001A0019000100010001001B -001C0001000100010020001F001E001D0001002300220021000100010001000100010001000100010001000100010001 -000100010001000100010001000100010001000100010001000100010001000100010001000100010001000100010001 -0001000100010001000100010001000100010001000100010026002500010024002A0029002800270001000100010001 -000100010001000100010001000100010001000100010001000100010001000100010001000100010001000100010001 -000100010001000100010001000100010001000100010001002B000100010001002C002C002C002C002C002C002C002C -002C002C002C002C002C002C002C002C002C002C002C002C002C002C002C002C002C002C002C002C002C002C002C002C -002E002D0001002C00310030002F000100350034003300320038000100370036003C003B003A00390040003F003E003D -00440043004200410048004700460045004C004B004A00490050004F004E004D0051000100010001002C002C00530052 -002C002C002C002C0054002C002C002C0001000100010001002C002C002C0055002C002C002C002C002C002C002C002C -002C002C002C002C002C005600010001002C002C002C002C002C002C002C002C002C002C002C002C002C002C002C002C -002C002C002C002C002C002C002C002C002C002C002C002C002C002C002C002C0058005700010001005A0059002C002C -00010001000100010001000100010001000100010001000100010001000100010001000100010001005B000100010001 -0001000100010001002C002C005D005C002C002C002C002C002C002C002C002C002C002C002C002C002C002C002C002C -002C002C002C002C002C002C002C002C002C002C002C002C005E002C002C002C002C0060005F0001002C002C002C002C -002C002C002C002C002C002C002C0061002C002C002C002C002C002C002C002C002C002C002C002C0062002C002C002C -0066006500640063006A006900680067002C006B00010001006C002C002C002C002C006F006E006D0071002C002C0070 -002C002C00730072002C0076007500740001007900780077007B007A00010001007E007D0001007C002C002C002C002C -00010001000100010001007F000100010001000100010001000100010001000100010001000100010080000100010001 -000100010001008100010001000100010001000100010001000100010001000100010001000100010001008200010001 -000100010001000100010001000100010001000100010001000100010001000100010001000100010001000100010001 -0083000100010001002C008400010001002C002C002C002C002C002C002C002C002C008500010001002C002C002C002C -000100010001000100010001000100010001000100010001000100010001000100860001000100010001000100010001 -000100010001000100010001000100010087000100010001002C002C002C002C002C002C002C002C002C002C002C002C -002C002C002C002C002C002C002C002C002C002C002C002C002C002C002C002C002C002C002C002C002C002C002C002C -002C002C002C002C002C002C002C002C002C002C002C002C002C002C002C002C002C002C002C002C002C002C002C002C -002C002C0088002C002C002C002C002C002C002C002C002C002C002C002C002C002C002C002C002C002C002C002C002C -002C002C002C002C002C002C002C002C002C002C002C002C002C002C002C002C002C002C002C002C002C002C002C002C -002C002C002C002C002C002C002C002C002C002C002C002C002C002C002C002CFFFFFFFF000000007FFFFFFFFFFFFFFF -FFFFDFFF00000000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFCFFFFFFFFFFFFFFFFFFFFFBFFFFD7F0FFFFFFFFFFFFFFFFFFFEFFFFFFFFFFFFFFFFFFFFFE7FFFFF -FFFFFFFFFFFEE7FF001F87FFFFFF00FFFFFFFFFFEFFFFFC0FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFDFFFFFFF -FFFFFFFFFFFF3FFFFFFFFFFFFFFFE7FF0003FFFFFFFFFFFFE7FFFFFFFFFFFFFF7FFF3FFFFFFFFFFFFFFF07FF4FFFFFFF -FFFFFFFFFF007FFFFFFFFFFBFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF3C5FDFFFFF99FEF7FFFFFCFB080799F -D36DFDFFFFF987EE007FFFC05E023987F3EDFDFFFFFBBFEEFE03FFCF00013BBFF3EDFDFFFFF99FEE00FFFFCFB0E0399F -C3FFC718D63DC7EC07FFFFC000813DC7F3FFFDFFFFFDDFFFFF80FFCF27603DDFF3EFFDFFFFFDDFFF000EFFCF60603DDF -FFFFFFFFFFFDDFFFFFFFFFCFFFF0FDDF2FFBFFFFFC7FFFEE001CFFC0FF5F847F87FFFFFFFFFFFFFE000000000FFFFFFF -3FFFFFAFFFFFF7D600000000F3FF7F5FFFFFFFFFFFFFFFFFFFFE1FFFFFFFFEFFDFFFFFFFFEFFFFFF0000000007FFDFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF20BFFFFFFFFFFFFFFFFFFFFFFFFF3D7F3DFF -7F3DFFFFFFFF3DFFFFFFFFFFFF7FFF3DFFFFFFFFFF3DFFFF1FFFFFFFE7FFFFFFFFFFFFFF03FFFFFF3F3FFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF1FFFFFFF01FFFFFFFFFFFFFF007FFFFF803FFFFF000DDFFF000FFFFF -FFFFFFFFFFFFFFFF03FF03FF3FFFFFFFFFFFFFFF03FFBFFF01FFFFFFFFFFFFFFFFFF07FFFFFFFFFF003FFFFFFFFFFFFF -0FFF0FFF7FFFFFFF001F3FFFFFFFFFF1FFFF0FFFFFFFFFFFFFFFFFFFC7FF03FFFFFFFFFFCFFFFFFF9FFFFFFF7FFFFFFF -FFFF3FFF03FF03FF0000000000007FFFFFFFFFFFFFFFFFFF7FFFFFFFFFFF1FFFFFFFFFFFFFFFFFFFF00FFFFFFFFFFFFF -F8FFFFFFFFFFFFFFFFFFFFFFFFFFE3FFE7FFFFFFFFFF01FF07FFFFFFFFFF00FFFFFFFFFF3F3FFFFF3FFFFFFFAAFF3F3F -FFDFFFFFFFFFFFFF7FDCFFFFEFCFFFDFFFFF80FFFFFF07FFFFF30000FFFFFFFFFFFFFFFF1FFF7FFF0001FFFFFFFF0001 -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0FFFFFFFFFFFFFFFFFFF0000007FFFFFFFFFFFFFFFFF000007FF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFCFFFFFFFFFFFFFFFFFFFFFFFBFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE0FFFFFFFFFFFFFFFFF20BFFFFFFFFF800180FFFFFFFFFF -7F7F7F7F007FFFFFFFFFFFFF7F7F7F7FFFFFFFFFFFFFFFFF000000003FFFFFFFFFFFFFFFFBFFFFFF000FFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0000003FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE -FFFFFFFFFE7FFFFFFFFFFFFFFFFFFFFFFFFEFFFFFFFFFFE0FFFFFFFFFFFFFFFFFFFFFFFFFFFF7FFFFFFF800FFFFFFFFF -FFFFFFFF7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFF1FFFFFFFFFFFFFFF007F00000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00FFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFC000003EB07FF03FF1FFFFFFFFFFF00FFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFF03FFC03FFFFFFFFFFFFFFFFF1FFFFFFF800FFFFFFFFFFFFFFFFFFFFF7FFFFFFFC3FFBFFF -007FFFFFFFFFFFFFFFFFFFFFF3FF3FFFFFFFFFFFFFFFFFFF007FFFFFF8000007FFFF7F7F007E7E7EFFFF0FFFFFFFFFFF -FFFFFFFFFFFFFFFF03FF3FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF000FFFFFFFFF0FFFFFFFFFFFF87F -0000000000000000000000000000000000000000000000000000000000000000FFFFFFFFFFFFFFFFFFFF3FFFFFFFFFFF -FFFFFFFFFFFFFFFF0000000003FFFFFF5F7FFFFFE0F8007FFFFFFFFFFFFFFFDBFFFFFFFFFFFFFFFFFFFFFFFFFFF80007 -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFCFFFFFFFF0000000080FFFFFFFFFF03FFFFFFFFDF0F7FFFF7FFFF -FFFFFFFFFFFFFFFF1FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFFFFFFFFFFFF7FFFFFFFFFFFFFFF30007F7F1CFCFCFC -B7FFFF7FFFFFEFFF000000003FFF3FFFFFFFFFFFFFFFFFFF07FFFFFFFFFFFFFFFF8FFFFFFFFFFF87FFFFFFFFFFFFFFFF -000000011FFF7FFF3FFFFFFFFFFF000000000000000000000000000000000000FFFFFFFF1FFFFFFF0FFFFFFF0001FFFF -FFFFE00FFFFFFFFF07FFFFFFFFFF07FFFFFFFFFFBFFFFFFF00000000003FFF0FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFF03FF3FFFFFFF0FFFFFFFFF0FFFFFFFFF00FFFFFFFFFFF7FF800FFFFFFFFF1BFBFFFBFFB7F7FF0000000000000000 -007FFFFFFFFFFFFF000000FF003FFFFF07FDFFFFFFFFFFBF000000000000000091BFFFFFFFFFFD3FFFFFFFFFFFBFFFFF -0000FF807FFFFFFFF837FFFF0000000083FFFFFF8FFFFFFF0000000000000000F0FFFFFFFFFFFFFFFFFFFFFFFFFCFFFF -873FFFFFFEEFF06FFFFFFFFF01FF01FF00000000FFFFFFFF007FF87FFFFFFFFFFE3FFFFFFFFFFFFFFF07FFFFFF3FFFFF -0000FE001E03FFFF0000000000000000FFFFFFFFFFFFFFFF00000000000001FF0007FFFFFFFFFFFFFC07FFFFFFFFFFFF -03FF00FFFFFFFFFF00000000000000000000000000000000000000000000000000000000000000007FFFFFFF00000000 -00033BFFFFFFFFFFE000000000000000FFFF00FFFFFFFFFFFFFF000003FFFFFFFFFF0000000003FF007FFFFF00000FFF -FFFFFFFFFFFFFFFF803FFFFFFFFC3FFFDFFFFFFFFFFFFFFF03FF01FFFFFF0007FFDFFFFFFFFFFFFF007FFFFFFFFF00FF -FFFFFFFFFFFFFFFF001FFFFEFFFFFFFFFFFFFFFFFFFBFFFF0000000000000003FFFF03FFBFFFBD7F03FF07FFFFFFFFFF -FBEDFDFFFFF99FEF001F1FCFE081399F00000000000000000000000000000000FFFFFFFFFFFFFFFF00000003EFFFFFFF -FFFFFFFFFFFFFFFF0000000003FF00FF00000000000000000000000000000000FF3FFFFFFFFFFFFF000000003FFFFFFF -FFFFFFFFFFFFFFFF00001FFF03FF001F03FFFFFFFFFFFFFF00000000000003FFFFFF0FFFE7FFFFFF000000000000007F -000000000000000000000000000000000FFFFFFFFFFFFFFF0000000000000000FFFFFFFF000000008007FFFFFFFFFFFF -F9BFFFFFFF6FF27F0000000003FF007FFFFFFCFF000000000000001FFCFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00FF -FFFF0007FFFFFFFF01FFFFFFFFFFFFFF00000000000003FF000000000000000000000000000000000000000000000000 -FF7FFFFFFFFFFDFFFFFF1FFFFFFF003F007FFEFFFFFCFFFF0000000000000000B47FFFFFFFFFFB7FFFFFFDBF03FF00FF -000003FF01FB7FFF000000000000000000000000000000000000000000000000000000000000000001FFFFFF00000000 -C7FFFFFFFFFDFFFF0000000003FFFFFF00010000000000008003FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -0000000003FFFFFF0000000000000000FFFFFFFFFFFFFFFF001F7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFF000000000000000F0000000000000000000000000000000000000000000000000000000000000000 -FFFFFFFFFFFF00000007FFFFFFFFFFFF0000FFFFFFFFFFFF00000000003FFFFF00000000000000000000000000000000 -FFFFFFFFFFFFFFFF000000000000007F0000000000000000000000000000000001FFFFFFFFFFFFFFFFFFC3FF7FFFFFFF -7FFFFFFFFFFFFFFF003F3FFFFFFF03FFFFFFFFFFFFFFFFFFE0FFFFFBFBFF003F000000000000FFFF0000000000000000 -0000000000000000FFFFFFFFFFFFFFFF0000000007FFFFFF0000000000000000FFFFFFFFFFFFFFFFFFFFFFFFFFFF87FF -00000000FFFF80FF0003001F00000000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00FFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00000000003FFFFF00000000000001FF0000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000006FEF000000000000 -00040007FFFFFFFFFFFF00F000270000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFF0FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF1FFF07FFFFFFFFFF00000000F3FF01FF0000000000000000 -FFFF3FFFFFFFFFFFFFFFFFFFFFFF007FFFFFFFFFFFFFFFFF000000000000000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFF003FFFFFFFFFFFFFFFFFFE7FFFFFFFFFF807FFFFFFFFFFFFFFFFFFFFFFFFFFFF000007FFFFFFFFFF -FFFFFFFFFFFFFFFF000000000000003F0000000000000000000FFFFF000FFFFFFFFFFFFFFFFFFFFF01FFFFFF007FFFFF -00000000000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFFFFDFFFFFEBFFDE64DFFFFFFFFFFFFFFFFFFFFFEF -7BFFFFFFDFDFE7BFFFFFFFFFFFFDFC5FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFF3FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFCFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0000FFFEF8000FFF0000000000000000000007E07FFFFFFF0000000000000000 -00000000000000000000000000000000FFFF07DBF9FFFF7F00003FFFFFFFFFFF00000000000080000000000000000000 -3FFF1FFFFFFFFFFF000000000000C3FF0000000000000000000000000000000000000000000000000000000000000000 -00007FFFFFFF000083FFFFFFFFFFFFFF00000000000000000000000000000000000000000000000003FFFFFFFFFF0000 -0000000000000000000000000000000000000000000000007FFF6F7F00000000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFF00000000007FFF9FFFFFFFFFFFFFFFFF00000000C3FF0FFF00000000000000000000000000000000 -0000000000000000FFFE000000000000001FFFFFFFFFFFFF00000000000000003FFFFFFFFFFFFFFE0000000000000000 -000000000000000000000000000000000AF7FE96FFFFFFEF5EF7F796AA96EA840FFFFBEE0FFFFBFF0003000000000000 -FFFF0FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE7FFF000FFFFF003FFFFFFFFEFFFEFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -00003FFFFFFFFFFFFFFFFFC0000000000FFFFFFFFFFF00070000003F000301FF00000000000000000000000000000000 -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF1FFF1FFFF0FFFFFFFFFFFFFFFFFFFFFFF87FFFFFFFFFFFFF -FFFFFFFFFFFFFFFF00010FFF03FFFFFFFFFFFFFFFFFF0FFFFFFFFFFF03FF00FF00033FFFFFFF00FF0000000000000000 -FFFFFFFFFFFFFFFF1FFF3FFF000FFFFFBFFFFFFFFFFF01FF01FF01FF0FFFC03FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFF7FFFF03FF0000000007FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00000000FFFFFFFF -03FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF3FFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0003FFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0001FFFFFFFFFFFFFFFFFFFFFFFF000000003FFFFFFF -00000000000000000000000000000000000000003FFFFFFF000000000000000000000000000000000000000000000000 -FFFFFFFFFFFFFFFFFFFFFFFFFFFF07FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -0000FFFFFFFFFFFF0000000000000000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0000FFFFFFFFFFFF -0000000000000000000000000000000000000000000000000000000000000000", -); -//4824 bytes -enum nonCharacterTrieEntries = TrieEntry!(bool, 7, 4, 4, 6)(x" -00000000000000000000000000000010000000000000004C000000000000014C", -x" -000000000000008000000000000000F0000000000000040000000000000043C0", -x" -07060504030201000B0B0B0A090801010B0B0B0B0B0B0B0B0B0B0B0B0B0B0B0B0B0B0B0B0B0B0B0B0B0B0B0B0B0B0B0B -0B0B0B0B0B0B0B0B0D0101010B0B0B0C000000000D010101000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000030002000100000007000600050004 -000B000A00090008000D000D000D000C000D000D000D000D000D000D000D000D000D000D000D000D000D000D000D000D -000D000D000D000D000D000D000D000D000D000F000E000D000D000D000D000D000D000D000D000D000D000D0010000D -000D000D000D000D00120011000D000D0016001500140013001A001900180017001E001D001C001B001D001D001F000D -001D001D0020001D001D001D001D001D00220021001D001D000D000D000D000D0024000D0023000D001D001D001D001D -0025001D001D001D0027001D001D00260028001D001D001D002C002B002A00290030002F002E002D001D003300320031 -000D000D000D000D000D000D000D000D000D000D0034000D000D00360035000D0037000D000D000D000D000D000D000D -00390038000D000D001D003A001D001D000D000D000D000D000D000D000D003B001D001D001D003C001D001D001D001D -001D001D001D001D001D001D001D001D001D001D001D001D001D001D001D001D001D001D001D003D001D001D001D001D -001D001D001D001D001D001D001D001D000D000D000D000D000D000D000D000D000D000D000D000D003E000D000D000D -000D000D000D000D000D000D000D000D000D000D000D000D003E000D000D000D00000000000000000000000000000000 -00000000000000000000000200010000000000000000000000060005000400030000000000000000000A000900080007 -0000000D000C000B000F000E0000000000130012001100100017001600150014001B001A00190018001F001E001D001C -0023002200210020002600250024000000270000000000000000000000000000002A002900280000002E002D002C002B -000000000000000000000000000000000030002F000000000033000000320031003500340030002D0039003800370036 -003D003C003B003A003F0000003E00000043004200410040000000000000000000000000000000000047004600450044 -004A0049004800000000004B000000000000000000000000000000000000000000000000004D004C0000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000004F004E0000 -00500000000000000054005300520051005700560055000000580000000000000000000400590000005C005B0000005A -000000000000005D00000000000000000000000000000000000000000000000000000000000000000000000000000000 -005F005E0000000000000000000000000061000000000060006200000000000000640000006100630066000000650000 -0069000000680067006B00000038006A000000000000000000000000000000000000000000000000006D006C00000000 -00000000000000000000000000000000006F0000006E0000007200000071007000000000000000000074007300000000 -007600000075002D0078007700000059007B0000007A0079007E007D0000007C0080002F007F007F0084008300820081 -0086008500000000007F0089008800870000000000000000007F008B008A0067008E008D004F008C00730090007F008F -0094009300920091007F009700960095009A009900980000007F007F007F009B009E009D009C007F00A100A0009F0087 -00A3000000A2000000A5000000A4004600A900A800A700A6007F007F00AB00AA00AD000000AC0000005500AE007F007F -00B100B000AF0000007F007F00B300B200B600B5007F00B400BA00B900B800B7003000BC00BB0000007F007F007F00B1 -007F00BF00BE00BD007F00C200C100C000C3007F007F007F00C600C5006F00C400000000000000000000000000000000 -0000000000000000007F006F000000000000000000C70000007F007F00C80000007F007F007F007F007F007F007F007F -007F007F007F007F007F007F007F007F007F007F007F007F007F007F007F007F007F007F007F007F007F007F007F007F -007F007F007F007F009900C9007F007F007F007F00CA0000007F007F007F007F007F007F007F007F007F007F007F007F -00000000000000000000000000000000007F007F00B30000007F007F007F007F00000000000000000000000000000000 -00CC007700CB0030007F00CE00CD0000007F007F007F007F007F007F007F007F007F00CF0000007F00D200D100D00000 -000000000000000000000000000000000000000000000000006100000000000000CA000000000000007F007F007F0098 -007F007F007F007F007F007F007F007F007F007F007F007F007F007F007F007F007F007F007F007F00D3007F007F007F -00000000000000000000000000D500D400B4000000000000007F007F007F007F007F00D700D60000007F007F007F007F -007F007F007F007F007F007F007F007F007F007F007F007F007F007F007F007F007F007F007F007F00C80000005F006E -003500000000000000D90000000000D800DB007F00DA0000007F007F00DC000000DF00DE00DD00000000000000E100E0 -000000E20000000000E300000000000000000000000000000000000000000000007F00E400000000007F007F007F007F -007F007F007F007F007F007F007F007F007F007F007F007F007F007F007F00E5007F00E800E700E6007F007F00EA00E9 -00EC00EB007F007F007F007F007F007F00ED007F007F007F007F007F007F007F007F007F007F007F00EE007F007F007F -00EF000000000000007F007F00F00000007F007F007F007F007F007F007F007F007F00F200F1007F007F007F007F00F3 -00F700F600F500F4007F007F007F007F00F900F80000003800FA00E700000000007F007F00FC00FB0000000000000000 -0000000000000000000000000000000000FD00000000000000FF000000FE0000007F01010100004B0000000000000000 -010401030102000001060105000000000000000000000000000000000000000000930000000000000000000000000000 -00000000000000000000000000000000000000000000000000000000000000B000000000000001070000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000108000000000000000000000000 -000000000000000000000000000000000000000000000000010900000000000000000000000000000000000000000000 -007F007F00550000007F007F007F007F00000000000000000000000000000000007F007F007F0055007F007F007F007F -00000000000000000000000000000000000000000000000000000000010A000000000000000000000000000000000000 -0000000000000000007F010B00000000007F007F0000010C010B000000000000007F007F007F007F007F007F007F007F -000000000000000000000000000000000000000000000000010D00000000000000000000000000000000000000000000 -0000000000000000010D00000000000000000000000000000300000000000000000000040000280F0001000000000000 -00000000018000000000000000011800FFE078000000FF0000000000000040000000000000001800FFFC000000000000 -18000000000000008000C000000000000000F800B00000000000000000FC80000C3A020000066010800000304F7F8660 -2C92020000067811FF80003FA1FDC6780C1202000004401101FC0030FFFEC4400C12020000066011FF0000304F1FC660 -3C0038E729C23813F800003FFF7EC2380C00020000022000007F0030D89FC2200C10020000022000FFF100309F9FC220 -000000000002200000000030000F0220D004000003800011FFE3003F00A07B807800000000000001FFFFFFFFF0000000 -C000005000000829FFFFFFFF0C0080A00001E000000001002000000001000000FFFFFFFFF8002000000000000000DF40 -00000000C280C20080C200000000C20000000000008000C20000000000C20000E00000001800000000000000FC000000 -C0C000000000000000000000E0000000FE00000000000000FF8000007FC00000FFF22000FFF00000FC00FC00C0000000 -0000F80000000000FFC0000000000000F000F00080000000FFE0C0000000000E0000F00000000000000000003800FC00 -000000003000000060000000800000000000C000FC00FC00FFFFFFFFFFFF8000800000000000E0000FF0000000000000 -07000000000000000000000000001C00180000000000FE00F80000000000FF0000000000C0C00000C00000005500C0C0 -00200000000000008023000010300020000C00200000000000000000E0008000FFFE00000000FFFE000000000000F000 -FFFFFF800000000000000000FFFFF8000030000000000000000000000040000001F00000000000000000DF4000000000 -7FFE7F000000000080808080FF8000000000000080808080FFFFFFFFC00000000000000004000000FFF0000000000000 -0000FFFFFFC000000000000000000001000100000000001F000000000000800000007FF0000000000000000080000000 -000000000000E000000000000000FF80FFFFF00000000000FF000000000000000003FFFFFC14F800FC00E00000000000 -00000000FC003FC0E00000007FF00000800000003C004000FF80000000000000000000000C00C000FF80000007FFFFF8 -00008080FF818181FC00C000000000000000FFF000000000F0000000000007800000C00000000000FFFFFFFFFC000000 -A08000001F07FF800000000000000024000000000007FFF800000000000300000000FFFFFFFF7F000020F08000080000 -60000000000000008000000000000000C1FF8080E30303034800008000001000FFFFFFFFC000C000F800000000000000 -0070000000000078FFFFFFFEE0008000C00000000000FFFFFFFFFFFFFFFFFFFFF0000000FFFE000000001FF000000000 -F80000000000F8000000000040000000FFFFFFFFFFC000F00000FC00C0000000F000000000F000000000FF0000000000 -08007FF000000000E404000400480800FFFFFF00FFC00000F8020000000000406E400000000002C0FFFF007F80000000 -07C80000FFFFFFFF7C000000700000000F0000000000000078C0000001100F9000000000FE00FE00FFFFFFFF00000000 -FF8007800000000001C000000000000000F8000000C00000FFFF01FFE1FC0000FFFFFFFFFFFFFE00FFF8000000000000 -03F8000000000000FC00FF000000000080000000FFFFFFFFFFFCC400000000001FFFFFFFFFFFFFFF0000FFFFFC000000 -0000FFFFFFFFFC00FF800000FFFFF0007FC000000003C000FC00FE000000DFF8FF8000000000FF00FFE0000100000000 -0000000000040000FFFFFFFFFFFFFFFC0000FC0040004280FC00F800000000000412020000066010FFE0E0301F7EC660 -FFFFFFFC10000000FFFFFFFFFC00FF0000C0000000000000FFFFE000FC00FFE0FC00000000000000FFFFFFFFFFFFFC00 -0000F00018000000FFFFFFFFFFFFFF80F00000000000000000000000FFFFFFFF7FF80000000000000640000000900D80 -FFFFFFFFFC00FF8000000300FFFFFFFFFFFFFFE003000000000000000000FF000000FFF8000000000080000000000200 -0000E0000000FFC0FF800100000300004B8000000000048000000240FC00FF00FFFFFC00FE048000FE000000FFFFFFFF -3800000000020000FFFEFFFFFFFFFFFF7FFC000000000000FFE0800000000000FFFFFFFFFFFFFFF0000000000000FFFF -FFFFFFFFFFC0000000003C0080000000FFC0C0000000FC001F0000040400FFC0FFFFFFFFFFFF0000FFFFFFFFF8000000 -0000000000007800FFFFFFFF00007F00FFFCFFE0FFFFFFFF9010FFFFFFFFFFFFFFFBFFF8000000000000FF0FFFD8FFFF -E000F80000000000FFFFFFF00C00FE000000018000000000FFFFF80000000000FFFFFFFFFFFFFFC0FFF00000FFF00000 -FE000000FF80000000000000002000001400219B200000000000000000000010840000002020184000000000000203A0 -000000C0000000000000000000003000FFFF000107FFF000FFFFF81F800000000000F82406000080FFFFC00000000000 -FFFFFFFFFFFF7FFFC000E00000000000FFFFFFFFFFFF3C00FFFF80000000FFFF7C00000000000000FC0000000000FFFF -80009080FFFFFFFFFFFFFFFFFF800060FFFFFFFF3C00F0000001FFFFFFFFFFFFFFE0000000000000C000000000000001 -F508016900000010A10808695569157BF0000411F0000400FFFCFFFFFFFFFFFF00018000FFF00000FFC0000000010001 -0000003FFFFFFFFFF00000000000FFF8FFFFFFC0FFFCFE00E000E0000F0000000780000000000000FFFEF000FC000000 -00000000FC00FF00FFFCC0000000FF00E000C000FFF00000400000000000FE00FE00FE00F0003FC00000000000080000 -FC00FFFFFFFFF80000000000C00000000000FFFC000000000000FFFE00000000000000000000F800FFFF000000000000 -00000000FFFFFFFDC000000000000000C000000000000000", -); -enum MAX_SIMPLE_LOWER = 1433; -enum MAX_SIMPLE_UPPER = 1450; -enum MAX_SIMPLE_TITLE = 1454; -//10496 bytes -enum toUpperIndexTrieEntries = TrieEntry!(ushort, 8, 7, 6)(x" -000000000000000000000000000000200000000000000140", -x" -000000000000010000000000000004800000000000000F80", -x" -040203020202010007020202060202050202020202020202020202020202020202020202020202020202020202020202 -020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202 -020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202 -020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202 -020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202 -02020202020202020202020202020202000300020001000000070006000500040000000A00090008000D000C000B0000 -00110010000F000E00000014001300120000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -001500000000000000000000000000000000000000000000001600000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -00000017000000000000001900180000001D001C001B001A00210020001F001E00000000000000000000002300220000 -000000000000000000000000000000000024000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000280027002600250000000000000029 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000002B002A0000002F002E002D002C00000000000000000000000000000000 -000000000000000000000031003000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000320000000000000000000000000000000000000000000000000000000000330000 -000000000000000000000000000000000000000000000000000000000000000000360000003500340000003700000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -003800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -003900000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000003A00000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000003C003B0000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -00000000000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF000200010000FFFF0006000500040003000A000900080007000E000D000C000B -001200110010000F0016001500140013FFFF001900180017FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF001AFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF05AAFFFFFFFFFFFF001E001D001C001B002200210020001F -0026002500240023002A002900280027002E002D002C002BFFFF00310030002F00350034003300320039003800370036 -003BFFFF003AFFFF003DFFFF003CFFFF003FFFFF003EFFFF0041FFFF0040FFFF0043FFFF0042FFFF0045FFFF0044FFFF -0047FFFF0046FFFF0049FFFF0048FFFF004BFFFF004AFFFF004DFFFF004CFFFF004FFFFF004EFFFF0051FFFF0050FFFF -0053FFFF005205AC0055FFFF0054FFFFFFFF0056FFFFFFFFFFFF0058FFFF0057FFFF005AFFFF0059FFFF005CFFFF005B -005EFFFF05C9005D0060FFFF005FFFFF0062FFFF0061FFFF0064FFFF0063FFFF0066FFFF0065FFFF0068FFFF0067FFFF -006AFFFF0069FFFF006CFFFF006BFFFF006EFFFF006DFFFF0070FFFF006FFFFF0072FFFF0071FFFF0074FFFF0073FFFF -FFFF0075FFFFFFFF00780077FFFF0076007AFFFFFFFF0079FFFFFFFF007BFFFFFFFFFFFFFFFF007CFFFFFFFFFFFF007D -FFFF007EFFFFFFFFFFFFFFFF007FFFFFFFFF00810080FFFFFFFF0082FFFFFFFF0084FFFF0083FFFFFFFFFFFF0085FFFF -FFFFFFFFFFFF0086FFFFFFFF0087FFFFFFFFFFFFFFFF0088FFFF008AFFFF0089FFFFFFFF008BFFFF008DFFFF008CFFFF -FFFFFFFFFFFFFFFFFFFF008F008EFFFF0092FFFF00910090FFFF0094FFFF0093FFFF0096FFFF0095FFFF0098FFFF0097 -FFFF009AFFFF0099009DFFFF009C009B009FFFFF009EFFFF00A1FFFF00A0FFFF00A3FFFF00A2FFFF00A5FFFF00A4FFFF -00A700A6FFFF05D1FFFFFFFF00A8FFFF00AAFFFF00A9FFFF00ACFFFF00ABFFFF00AEFFFF00ADFFFF00B0FFFF00AFFFFF -00B2FFFF00B1FFFF00B4FFFF00B3FFFF00B6FFFF00B5FFFF00B8FFFF00B7FFFF00BAFFFF00B9FFFF00BCFFFF00BBFFFF -00BDFFFFFFFFFFFF00BFFFFF00BEFFFF00C1FFFF00C0FFFF00C3FFFF00C2FFFF00C5FFFF00C4FFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFF00C7FFFFFFFF00C6FFFF00C9FFFF00C800CAFFFFFFFFFFFF00CCFFFF00CBFFFF00CEFFFF00CDFFFF -00D200D100D000CF00D500D4FFFF00D300D7FFFF00D6FFFFFFFFFFFFFFFF00D800DBFFFF00DA00D9FFFF00DD00DCFFFF -00E100E000DF00DE00E3FFFFFFFF00E2FFFF00E500E4FFFFFFFFFFFF00E6FFFFFFFFFFFFFFFFFFFFFFFFFFFF00E7FFFF -00EA00E9FFFF00E800EBFFFFFFFFFFFF00EF00EE00ED00ECFFFFFFFFFFFF00F0FFFF00F1FFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFF00F300F2FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00F4FFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00F6FFFF00F5FFFF00F7FFFFFFFFFFFF -00F8FFFFFFFFFFFFFFFFFFFF00FA00F9FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFF05CBFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFF00FE00FD00FC00FB0101010000FF05CE01050104010301020109010801070106010D010C010B010A -01110110010F010E01150114011301120119011801170116FFFF011C011B011AFFFFFFFF011E011D01210120011FFFFF -0123FFFF0122FFFF0125FFFF0124FFFF0127FFFF0126FFFF0129FFFF0128FFFF012BFFFF012AFFFF012DFFFF012CFFFF -01310130012F012EFFFFFFFF0132FFFF0134FFFFFFFF0133FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0138013701360135013C013B013A0139 -0140013F013E013D01440143014201410148014701460145014C014B014A01490150014F014E014D0154015301520151 -0158015701560155015C015B015A01590160015F015E015D01640163016201610166FFFF0165FFFF0168FFFF0167FFFF -016AFFFF0169FFFF016CFFFF016BFFFF016EFFFF016DFFFF0170FFFF016FFFFF0172FFFF0171FFFF0174FFFF0173FFFF -FFFFFFFF0175FFFFFFFFFFFFFFFFFFFF0176FFFFFFFFFFFF0178FFFF0177FFFF017AFFFF0179FFFF017CFFFF017BFFFF -017EFFFF017DFFFF0180FFFF017FFFFF0182FFFF0181FFFF0184FFFF0183FFFF0186FFFF0185FFFF0188FFFF0187FFFF -018AFFFF0189FFFF018CFFFF018BFFFF018EFFFF018DFFFF0190FFFF018FFFFFFFFF0191FFFFFFFFFFFF0193FFFF0192 -FFFF0195FFFF019401980197FFFF0196019AFFFF0199FFFF019CFFFF019BFFFF019EFFFF019DFFFF01A0FFFF019FFFFF -01A2FFFF01A1FFFF01A4FFFF01A3FFFF01A6FFFF01A5FFFF01A8FFFF01A7FFFF01AAFFFF01A9FFFF01ACFFFF01ABFFFF -01AEFFFF01ADFFFF01B0FFFF01AFFFFF01B2FFFF01B1FFFF01B4FFFF01B3FFFF01B6FFFF01B5FFFF01B8FFFF01B7FFFF -01BAFFFF01B9FFFF01BCFFFF01BBFFFF01BEFFFF01BDFFFF01C0FFFF01BFFFFF01C2FFFF01C1FFFF01C4FFFF01C3FFFF -01C6FFFF01C5FFFF01C8FFFF01C7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF01CB01CA01C9FFFF01CF01CE01CD01CC01D301D201D101D001D701D601D501D4 -01DB01DA01D901D801DF01DE01DD01DC01E301E201E101E001E701E601E501E401EB01EA01E901E805BD01EE01ED01EC -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -01F201F101F001EF01F601F501F401F301FA01F901F801F701FE01FD01FC01FB02020201020001FF0206020502040203 -020A020902080207020E020D020C020B021202110210020F0216021502140213FFFF021902180217021C021B021AFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0220021F021E021DFFFFFFFF022202210226022502240223022A022902280227 -FFFFFFFFFFFF022BFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF022CFFFFFFFFFFFF022DFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF022EFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0230FFFF022FFFFF0232FFFF0231FFFF -0234FFFF0233FFFF0236FFFF0235FFFF0238FFFF0237FFFF023AFFFF0239FFFF023CFFFF023BFFFF023EFFFF023DFFFF -0240FFFF023FFFFF0242FFFF0241FFFF0244FFFF0243FFFF0246FFFF0245FFFF0248FFFF0247FFFF024AFFFF0249FFFF -024CFFFF024BFFFF024EFFFF024DFFFF0250FFFF024FFFFF0252FFFF0251FFFF0254FFFF0253FFFF0256FFFF0255FFFF -0258FFFF0257FFFF025AFFFF0259FFFF025CFFFF025BFFFF025EFFFF025DFFFF0260FFFF025FFFFF0262FFFF0261FFFF -0264FFFF0263FFFF0266FFFF0265FFFF0268FFFF0267FFFF026AFFFF0269FFFF026CFFFF026BFFFF026EFFFF026DFFFF -0270FFFF026FFFFF0272FFFF0271FFFF0274FFFF0273FFFF0276FFFF0275FFFF0278FFFF0277FFFF05D505D30279FFFF -027A05DB05D905D7FFFFFFFFFFFFFFFF027CFFFF027BFFFF027EFFFF027DFFFF0280FFFF027FFFFF0282FFFF0281FFFF -0284FFFF0283FFFF0286FFFF0285FFFF0288FFFF0287FFFF028AFFFF0289FFFF028CFFFF028BFFFF028EFFFF028DFFFF -0290FFFF028FFFFF0292FFFF0291FFFF0294FFFF0293FFFF0296FFFF0295FFFF0298FFFF0297FFFF029AFFFF0299FFFF -029CFFFF029BFFFF029EFFFF029DFFFF02A0FFFF029FFFFF02A2FFFF02A1FFFF02A4FFFF02A3FFFF02A6FFFF02A5FFFF -02A8FFFF02A7FFFF02AAFFFF02A9FFFF02AE02AD02AC02AB02B202B102B002AFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -02B602B502B402B3FFFFFFFF02B802B7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF02BC02BB02BA02B902C002BF02BE02BD -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF02C402C302C202C102C802C702C602C5FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -02CC02CB02CA02C9FFFFFFFF02CE02CDFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF02D005DF02CF05DD02D205E502D105E2 -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF02D602D502D402D302DA02D902D802D7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -02DE02DD02DC02DB02E202E102E002DF02E602E502E402E3FFFFFFFF02E802E7060C060A06080606061406120610060E -061C061A06180616062406220620061E062C062A06280626063406320630062E063C063A06380636064406420640063E -064C064A06480646065406520650064E065C065A06580656066406620660065E0666067203020301067E05E8FFFF0674 -FFFFFFFFFFFFFFFFFFFF0304FFFF0668066A0676FFFFFFFF068105EAFFFF0678FFFFFFFFFFFFFFFFFFFFFFFFFFFF066C -05EF05EC0307030605F405F2FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF05FA05F703090308060105FF030A05FD -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF066E067AFFFFFFFF06840604FFFF067CFFFFFFFFFFFFFFFFFFFFFFFFFFFF0670 -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF030CFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -0310030F030E030D03140313031203110318031703160315031C031B031A0319FFFFFFFFFFFFFFFFFFFFFFFFFFFF031D -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -03210320031F031E03250324032303220329032803270326032D032C032B032A03310330032F032E0335033403330332 -FFFFFFFF03370336FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -033B033A03390338033F033E033D033C03430342034103400347034603450344034B034A03490348034F034E034D034C -03530352035103500357035603550354035B035A03590358035F035E035D035C03630362036103600367036603650364 -FFFFFFFF0368FFFFFFFF036A0369FFFFFFFF036CFFFF036BFFFFFFFFFFFF036D036EFFFFFFFFFFFFFFFF036FFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0371FFFF0370FFFF0373FFFF0372FFFF0375FFFF0374FFFF0377FFFF0376FFFF -0379FFFF0378FFFF037BFFFF037AFFFF037DFFFF037CFFFF037FFFFF037EFFFF0381FFFF0380FFFF0383FFFF0382FFFF -0385FFFF0384FFFF0387FFFF0386FFFF0389FFFF0388FFFF038BFFFF038AFFFF038DFFFF038CFFFF038FFFFF038EFFFF -0391FFFF0390FFFF0393FFFF0392FFFF0395FFFF0394FFFF0397FFFF0396FFFF0399FFFF0398FFFF039BFFFF039AFFFF -039DFFFF039CFFFF039FFFFF039EFFFF03A1FFFF03A0FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF03A3FFFF03A2 -03A4FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF03A803A703A603A503AC03AB03AA03A9 -03B003AF03AE03AD03B403B303B203B103B803B703B603B503BC03BB03BA03B903C003BF03BE03BD03C403C303C203C1 -03C803C703C603C503CBFFFF03CA03C9FFFFFFFFFFFFFFFFFFFFFFFF03CCFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF03CEFFFF03CDFFFF03D0FFFF03CFFFFF03D2FFFF03D1FFFF03D4FFFF03D3FFFF -03D6FFFF03D5FFFF03D8FFFF03D7FFFF03DAFFFF03D9FFFF03DCFFFF03DBFFFF03DEFFFF03DDFFFF03E0FFFF03DFFFFF -03E2FFFF03E1FFFFFFFFFFFF03E3FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -03E5FFFF03E4FFFF03E7FFFF03E6FFFF03E9FFFF03E8FFFF03EBFFFF03EAFFFF03EDFFFF03ECFFFF03EFFFFF03EEFFFF -03F1FFFF03F0FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -03F2FFFFFFFFFFFF03F4FFFF03F3FFFF03F6FFFF03F5FFFF03F8FFFF03F7FFFF03F9FFFFFFFFFFFF03FBFFFF03FAFFFF -03FDFFFF03FCFFFF03FFFFFF03FEFFFF0401FFFF0400FFFF0403FFFF0402FFFF0405FFFF0404FFFF0407FFFF0406FFFF -0409FFFF0408FFFF040BFFFF040AFFFF040DFFFF040CFFFF040FFFFF040EFFFF0411FFFF0410FFFF0413FFFF0412FFFF -0415FFFF0414FFFF0417FFFF0416FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0418FFFFFFFF041AFFFFFFFF0419 -041CFFFF041BFFFF041EFFFF041DFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF041F0421FFFF0420FFFF0423FFFFFFFF0422 -0425FFFF0424FFFF0427FFFF0426FFFF0429FFFF0428FFFF042BFFFF042AFFFFFFFFFFFF042CFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFF042EFFFF042DFFFF0430FFFF042FFFFF0432FFFF0431FFFF0434FFFF0433FFFFFFFFFFFFFFFFFFFF -FFFF0436FFFF0435FFFFFFFFFFFFFFFFFFFFFFFF0437FFFF0438FFFFFFFFFFFFFFFFFFFF0439FFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF043AFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -043BFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF043F043E043D043C04430442044104400447044604450444044B044A04490448 -044F044E044D044C04530452045104500457045604550454045B045A04590458045F045E045D045C0463046204610460 -0467046604650464046B046A04690468046F046E046D046C04730472047104700477047604750474047B047A04790478 -047F047E047D047C04830482048104800487048604850484048B048A0489048805B305B105AF05ADFFFF05BB05B905B6 -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF05BFFFFFFFFFFFFF05C705C505C305C1FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF048E048D048CFFFF049204910490048F0496049504940493049A049904980497 -049E049D049C049B04A204A104A0049FFFFF04A504A404A3FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF04A904A804A704A604AD04AC04AB04AA -04B104B004AF04AE04B504B404B304B204B904B804B704B604BD04BC04BB04BA04C104C004BF04BE04C504C404C304C2 -04C904C804C704C604CD04CC04CB04CAFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF04D104D004CF04CE04D504D404D304D204D904D804D704D604DD04DC04DB04DA -04E104E004DF04DE04E504E404E304E204E904E804E704E604ED04EC04EB04EA04F104F004EF04EEFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF04F2FFFFFFFFFFFF -04F604F504F404F304FA04F904F804F704FDFFFF04FC04FB0501050004FF04FE05050504050305020509050805070506 -050CFFFF050B050A0510050F050E050D0513FFFF05120511FFFFFFFFFFFF05140518051705160515051C051B051A0519 -0520051F051E051D05240523052205210528052705260525052C052B052A05290530052F052E052D0534053305320531 -0538053705360535053C053B053A05390540053F053E053D0544054305420541FFFF054705460545FFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF054B054A05490548054F054E054D054C05530552055105500557055605550554 -055B055A05590558055F055E055D055C05630562056105600567056605650564FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF056B056A05690568056F056E056D056C05730572057105700577057605750574 -057B057A05790578057F057E057D057C05830582058105800587058605850584FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -05890588FFFFFFFF058D058C058B058A05910590058F058E05950594059305920599059805970596059D059C059B059A -05A105A0059F059E05A505A405A305A205A905A805A705A6FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF", -); -//10112 bytes -enum toLowerIndexTrieEntries = TrieEntry!(ushort, 8, 7, 6)(x" -000000000000000000000000000000200000000000000140", -x" -000000000000010000000000000004800000000000000EC0", -x" -040203020202010007020202060202050202020202020202020202020202020202020202020202020202020202020202 -020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202 -020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202 -020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202 -020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202 -02020202020202020202020202020202000200000001000000060005000400030000000000080007000B000A00090000 -000F000E000D000C00000012001100100000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -001400130000000000000000000000000000000000000000001600150000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -00000017000000000000000000000000001B001A00190018001F001E001D001C00000000000000000000002200210020 -000000000000000000000000000000000024002300000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000280027002600250000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000002A00290000002E002D002C002B00000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000002F0000000000000000000000000000000000000000000000000000000000000030 -000000000000000000000000000000000000000000000000000000000000000000330032000000310000003500340000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000360000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000370000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000003800000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000390000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -00000000000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -000200010000FFFF0006000500040003000A000900080007000E000D000C000B001200110010000F0016001500140013 -FFFF001900180017FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF001D001C001B001A00210020001F001E -00250024002300220029002800270026002D002C002B002AFFFF0030002F002E00340033003200310599003700360035 -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0039FFFF0038FFFF003BFFFF003AFFFF003DFFFF003CFFFF003FFFFF003E -FFFF0041FFFF0040FFFF0043FFFF0042FFFF0045FFFF0044FFFF0047FFFF0046FFFF0049FFFF0048FFFF004BFFFF004A -FFFF004DFFFF004CFFFF004FFFFF004EFFFF0051FFFF059AFFFF0053FFFF00520055FFFF0054FFFF0057FFFF0056FFFF -0059FFFF0058FFFF005BFFFF005AFFFFFFFF005C05A9FFFFFFFF005EFFFF005DFFFF0060FFFF005FFFFF0062FFFF0061 -FFFF0064FFFF0063FFFF0066FFFF0065FFFF0068FFFF0067FFFF006AFFFF0069FFFF006CFFFF006BFFFF006EFFFF006D -FFFF0070FFFF006FFFFF0072FFFF00710075FFFF00740073FFFFFFFF0076FFFFFFFF00780077FFFF007B007AFFFF0079 -007E007D007CFFFF0080007FFFFFFFFF0083FFFF0082008100860085FFFF0084FFFFFFFFFFFF0087008AFFFF00890088 -FFFF008CFFFF008B008F008EFFFF008DFFFFFFFF0090FFFF00930092FFFF0091009600950094FFFF0098FFFF0097FFFF -FFFFFFFFFFFF0099FFFFFFFFFFFF009AFFFFFFFFFFFFFFFF009DFFFF009C009B00A0009FFFFF009E00A2FFFF00A1FFFF -00A4FFFF00A3FFFF00A6FFFF00A5FFFF00A8FFFF00A7FFFFFFFF00A9FFFFFFFFFFFF00ABFFFF00AAFFFF00ADFFFF00AC -FFFF00AFFFFF00AEFFFF00B1FFFF00B0FFFF00B300B205AC00B600B5FFFF00B4FFFF00B8FFFF00B7FFFF00BAFFFF00B9 -FFFF00BCFFFF00BBFFFF00BEFFFF00BDFFFF00C0FFFF00BFFFFF00C2FFFF00C1FFFF00C4FFFF00C3FFFF00C6FFFF00C5 -FFFF00C8FFFF00C7FFFF00CAFFFF00C9FFFF00CCFFFF00CBFFFF00CEFFFF00CDFFFF00D0FFFF00CFFFFF00D2FFFF00D1 -FFFF00D4FFFF00D3FFFFFFFFFFFFFFFF00D600D5FFFFFFFFFFFF00D800D7FFFF00DAFFFF00D9FFFFFFFF00DD00DC00DB -FFFF00DFFFFF00DEFFFF00E1FFFF00E0FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00E3FFFF00E2FFFF00E4FFFFFFFFFFFFFFFFFFFFFFFF00E5FFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFF00E6FFFFFFFFFFFF00E900E800E700EC00EBFFFF00EA00EF00EE00ED05AA00F300F200F100F0 -00F700F600F500F400FB00FA00F900F800FEFFFF00FD00FC01020101010000FF0106010501040103FFFFFFFFFFFFFFFF -FFFFFFFFFFFF05ABFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFF0107FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0109FFFF0108FFFF010BFFFF010A -FFFF010DFFFF010CFFFF010FFFFF010EFFFF0111FFFF0110FFFF0113FFFF0112FFFFFFFFFFFFFFFF0115FFFFFFFF0114 -FFFF01170116FFFF011A01190118FFFF011E011D011C011B012201210120011F0126012501240123012A012901280127 -012E012D012C012B013201310130012F0136013501340133013A013901380137013E013D013C013B014201410140013F -0146014501440143014A014901480147FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF014CFFFF014BFFFF014EFFFF014DFFFF0150FFFF014FFFFF0152FFFF0151 -FFFF0154FFFF0153FFFF0156FFFF0155FFFF0158FFFF0157FFFF015AFFFF0159FFFFFFFFFFFF015BFFFFFFFFFFFFFFFF -FFFF015CFFFFFFFFFFFF015EFFFF015DFFFF0160FFFF015FFFFF0162FFFF0161FFFF0164FFFF0163FFFF0166FFFF0165 -FFFF0168FFFF0167FFFF016AFFFF0169FFFF016CFFFF016BFFFF016EFFFF016DFFFF0170FFFF016FFFFF0172FFFF0171 -FFFF0174FFFF0173FFFF0176FFFF01750179FFFF01780177017BFFFF017AFFFF017DFFFF017CFFFFFFFFFFFF017EFFFF -FFFF0180FFFF017FFFFF0182FFFF0181FFFF0184FFFF0183FFFF0186FFFF0185FFFF0188FFFF0187FFFF018AFFFF0189 -FFFF018CFFFF018BFFFF018EFFFF018DFFFF0190FFFF018FFFFF0192FFFF0191FFFF0194FFFF0193FFFF0196FFFF0195 -FFFF0198FFFF0197FFFF019AFFFF0199FFFF019CFFFF019BFFFF019EFFFF019DFFFF01A0FFFF019FFFFF01A2FFFF01A1 -FFFF01A4FFFF01A3FFFF01A6FFFF01A5FFFF01A8FFFF01A7FFFF01AAFFFF01A9FFFF01ACFFFF01ABFFFF01AEFFFF01AD -01B101B001AFFFFF01B501B401B301B201B901B801B701B601BD01BC01BB01BA01C101C001BF01BE01C501C401C301C2 -01C901C801C701C601CD01CC01CB01CA01D101D001CF01CEFFFF01D401D301D2FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF05A3FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF01D801D701D601D501DC01DB01DA01D901E001DF01DE01DD01E401E301E201E1 -01E801E701E601E501EC01EB01EA01E901F001EF01EE01ED01F401F301F201F101F801F701F601F501FBFFFF01FA01F9 -FFFFFFFFFFFFFFFFFFFFFFFF01FCFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF020001FF01FE01FD0204020302020201 -0208020702060205020C020B020A02090210020F020E020D02140213021202110218021702160215021C021B021A0219 -0220021F021E021D02240223022202210228022702260225022C022B022A02290230022F022E022D0234023302320231 -0238023702360235023C023B023A02390240023F023E023D02440243024202410248024702460245024C024B024A0249 -0250024F024E024DFFFFFFFF02520251FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0256025502540253025A025902580257025E025D025C025B026202610260025F -0266026502640263026A026902680267026E026D026C026B027202710270026F0276027502740273027A027902780277 -FFFF027D027C027B0280027F027EFFFFFFFF0282FFFF0281FFFF0284FFFF0283FFFF0286FFFF0285FFFF0288FFFF0287 -FFFF028AFFFF0289FFFF028CFFFF028BFFFF028EFFFF028DFFFF0290FFFF028FFFFF0292FFFF0291FFFF0294FFFF0293 -FFFF0296FFFF0295FFFF0298FFFF0297FFFF029AFFFF0299FFFF029CFFFF029BFFFF029EFFFF029DFFFF02A0FFFF029F -FFFF02A2FFFF02A1FFFF02A4FFFF02A3FFFF02A6FFFF02A5FFFF02A8FFFF02A7FFFF02AAFFFF02A9FFFF02ACFFFF02AB -FFFF02AEFFFF02ADFFFF02B0FFFF02AFFFFF02B2FFFF02B1FFFF02B4FFFF02B3FFFF02B6FFFF02B5FFFF02B8FFFF02B7 -FFFF02BAFFFF02B9FFFF02BCFFFF02BBFFFF02BEFFFF02BDFFFF02C0FFFF02BFFFFF02C2FFFF02C1FFFF02C4FFFF02C3 -FFFF02C6FFFF02C5FFFF02C8FFFF02C7FFFF02CAFFFF02C905AE05ADFFFF02CBFFFF05B105B005AFFFFF02CCFFFFFFFF -FFFF02CEFFFF02CDFFFF02D0FFFF02CFFFFF02D2FFFF02D1FFFF02D4FFFF02D3FFFF02D6FFFF02D5FFFF02D8FFFF02D7 -FFFF02DAFFFF02D9FFFF02DCFFFF02DBFFFF02DEFFFF02DDFFFF02E0FFFF02DFFFFF02E2FFFF02E1FFFF02E4FFFF02E3 -FFFF02E6FFFF02E5FFFF02E8FFFF02E7FFFF02EAFFFF02E9FFFF02ECFFFF02EBFFFF02EEFFFF02EDFFFF02F0FFFF02EF -FFFF02F2FFFF02F1FFFF02F4FFFF02F3FFFF02F6FFFF02F5FFFF02F8FFFF02F7FFFF02FAFFFF02F9FFFF02FCFFFF02FB -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF030002FF02FE02FD0304030303020301FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -0308030703060305FFFFFFFF030A0309FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF030E030D030C030B031203110310030F -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0316031503140313031A031903180317FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -031E031D031C031BFFFFFFFF0320031FFFFF05B3FFFF05B2FFFF05B5FFFF05B40322FFFF0321FFFF0324FFFF0323FFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0328032703260325032C032B032A0329FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF05C505C405C305C205C905C805C705C605CD05CC05CB05CA05D105D005CF05CE -05D505D405D305D205D905D805D705D605DD05DC05DB05DA05E105E005DF05DE05E505E405E305E205E905E805E705E6 -05ED05EC05EB05EA05F105F005EF05EE05F205F8FFFFFFFF05FE05B6FFFF05F90348034703460345FFFFFFFFFFFF05F3 -05F405FAFFFFFFFF05FF05B7FFFF05FB034D034C034B034AFFFFFFFFFFFF05F505B905B8FFFFFFFF05BB05BAFFFFFFFF -035203510350034FFFFFFFFFFFFFFFFF05BD05BCFFFFFFFF05C005BFFFFF05BE0356035503540353FFFFFFFFFFFF0357 -05F605FCFFFFFFFF060005C1FFFF05FD035B035A03590358FFFFFFFFFFFF05F7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFF035DFFFFFFFF035F035EFFFFFFFFFFFFFFFFFFFFFFFFFFFF0360FFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF03640363036203610368036703660365 -036C036B036A03690370036F036E036DFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -0371FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF03730372FFFFFFFF -0377037603750374037B037A03790378037F037E037D037C03830382038103800387038603850384038B038A03890388 -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -038F038E038D038C03930392039103900397039603950394039B039A03990398039F039E039D039C03A303A203A103A0 -03A703A603A503A403AB03AA03A903A803AF03AE03AD03AC03B303B203B103B003B703B603B503B403BB03BA03B903B8 -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -03BE03BDFFFF03BC03C0FFFFFFFF03BF03C2FFFF03C1FFFF03C503C403C3FFFFFFFF03C7FFFF03C6FFFFFFFF03C8FFFF -FFFFFFFFFFFFFFFF03CA03C9FFFFFFFFFFFF03CCFFFF03CBFFFF03CEFFFF03CDFFFF03D0FFFF03CFFFFF03D2FFFF03D1 -FFFF03D4FFFF03D3FFFF03D6FFFF03D5FFFF03D8FFFF03D7FFFF03DAFFFF03D9FFFF03DCFFFF03DBFFFF03DEFFFF03DD -FFFF03E0FFFF03DFFFFF03E2FFFF03E1FFFF03E4FFFF03E3FFFF03E6FFFF03E5FFFF03E8FFFF03E7FFFF03EAFFFF03E9 -FFFF03ECFFFF03EBFFFF03EEFFFF03EDFFFF03F0FFFF03EFFFFF03F2FFFF03F1FFFF03F4FFFF03F3FFFF03F6FFFF03F5 -FFFF03F8FFFF03F7FFFF03FAFFFF03F9FFFF03FCFFFF03FBFFFFFFFFFFFFFFFF03FDFFFFFFFFFFFFFFFFFFFF03FEFFFF -FFFF03FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0401FFFF0400FFFF0403FFFF0402 -FFFF0405FFFF0404FFFF0407FFFF0406FFFF0409FFFF0408FFFF040BFFFF040AFFFF040DFFFF040CFFFF040FFFFF040E -FFFF0411FFFF0410FFFF0413FFFF0412FFFF0415FFFF0414FFFFFFFFFFFF0416FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0418FFFF0417FFFF041AFFFF0419FFFF041CFFFF041BFFFF041EFFFF041D -FFFF0420FFFF041FFFFF0422FFFF0421FFFF0424FFFF0423FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0425FFFFFFFFFFFF0427FFFF0426FFFF0429FFFF0428FFFF042BFFFF042A -FFFF042CFFFFFFFFFFFF042EFFFF042DFFFF0430FFFF042FFFFF0432FFFF0431FFFF0434FFFF0433FFFF0436FFFF0435 -FFFF0438FFFF0437FFFF043AFFFF0439FFFF043CFFFF043BFFFF043EFFFF043DFFFF0440FFFF043FFFFF0442FFFF0441 -FFFF0444FFFF0443FFFF0446FFFF0445FFFF0448FFFF0447FFFF044AFFFF0449FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -044CFFFF044BFFFFFFFF044E044DFFFFFFFF0450FFFF044FFFFF0452FFFF04510453FFFFFFFFFFFFFFFFFFFF0454FFFF -FFFF0456FFFF0455FFFF0457FFFFFFFFFFFF0459FFFF0458FFFF045BFFFF045AFFFF045DFFFF045CFFFF045FFFFF045E -04620461FFFF0460FFFF0465046404630469046804670466FFFF046BFFFF046AFFFF046DFFFF046CFFFF046FFFFF046E -FFFF0471FFFF04700475047404730472FFFFFFFF0476FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0477FFFF0478FFFFFFFF -FFFFFFFFFFFF0479FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFF047AFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF059F059E059D059CFFFF05A205A105A0 -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF05A4FFFFFFFFFFFF05A805A705A605A5FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF047D047C047BFFFF04810480047F047E -04850484048304820489048804870486048D048C048B048A04910490048F048EFFFF049404930492FFFFFFFFFFFFFFFF -0498049704960495049C049B049A049904A0049F049E049D04A404A304A204A104A804A704A604A504AC04AB04AA04A9 -04B004AF04AE04AD04B404B304B204B104B804B704B604B504BC04BB04BA04B9FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF04C004BF04BE04BD04C404C304C204C1 -04C804C704C604C504CC04CB04CA04C904D004CF04CE04CD04D404D304D204D104D804D704D604D504DC04DB04DA04D9 -04E004DF04DE04DDFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -04E404E304E204E104E804E704E604E5FFFF04EB04EA04E904EF04EE04ED04EC04F304F204F104F004F704F604F504F4 -FFFF04FA04F904F804FE04FD04FC04FBFFFF0501050004FFFFFFFFFF05030502FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0507050605050504050B050A05090508050F050E050D050C0513051205110510 -0517051605150514051B051A05190518051F051E051D051C05230522052105200527052605250524052B052A05290528 -052F052E052D052C0533053205310530FFFF053605350534FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF053A053905380537053E053D053C053B054205410540053F0546054505440543 -054A054905480547054E054D054C054B055205510550054F0556055505540553055A055905580557055E055D055C055B -056205610560055F0566056505640563056A056905680567056E056D056C056B057205710570056F0576057505740573 -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF057A057905780577057E057D057C057B058205810580057F0586058505840583 -058A058905880587058E058D058C058B059205910590058F0596059505940593FFFFFFFF05980597FFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF", -); -//10496 bytes -enum toTitleIndexTrieEntries = TrieEntry!(ushort, 8, 7, 6)(x" -000000000000000000000000000000200000000000000140", -x" -000000000000010000000000000004800000000000000F80", -x" -040203020202010007020202060202050202020202020202020202020202020202020202020202020202020202020202 -020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202 -020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202 -020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202 -020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202 -02020202020202020202020202020202000300020001000000070006000500040000000A00090008000D000C000B0000 -00110010000F000E00000014001300120000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -001500000000000000000000000000000000000000000000001600000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -00000017000000000000001900180000001D001C001B001A00210020001F001E00000000000000000000002300220000 -000000000000000000000000000000000024000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000280027002600250000000000000029 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000002B002A0000002F002E002D002C00000000000000000000000000000000 -000000000000000000000031003000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000320000000000000000000000000000000000000000000000000000000000330000 -000000000000000000000000000000000000000000000000000000000000000000360000003500340000003700000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -003800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -003900000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000003A00000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000003C003B0000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -00000000000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF000200010000FFFF0006000500040003000A000900080007000E000D000C000B -001200110010000F0016001500140013FFFF001900180017FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF001AFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF05AEFFFFFFFFFFFF001E001D001C001B002200210020001F -0026002500240023002A002900280027002E002D002C002BFFFF00310030002F00350034003300320039003800370036 -003BFFFF003AFFFF003DFFFF003CFFFF003FFFFF003EFFFF0041FFFF0040FFFF0043FFFF0042FFFF0045FFFF0044FFFF -0047FFFF0046FFFF0049FFFF0048FFFF004BFFFF004AFFFF004DFFFF004CFFFF004FFFFF004EFFFF0051FFFF0050FFFF -0053FFFF005205B00055FFFF0054FFFFFFFF0056FFFFFFFFFFFF0058FFFF0057FFFF005AFFFF0059FFFF005CFFFF005B -005EFFFF05CD005D0060FFFF005FFFFF0062FFFF0061FFFF0064FFFF0063FFFF0066FFFF0065FFFF0068FFFF0067FFFF -006AFFFF0069FFFF006CFFFF006BFFFF006EFFFF006DFFFF0070FFFF006FFFFF0072FFFF0071FFFF0074FFFF0073FFFF -FFFF0075FFFFFFFF00780077FFFF0076007AFFFFFFFF0079FFFFFFFF007BFFFFFFFFFFFFFFFF007CFFFFFFFFFFFF007D -FFFF007EFFFFFFFFFFFFFFFF007FFFFFFFFF00810080FFFFFFFF0082FFFFFFFF0084FFFF0083FFFFFFFFFFFF0085FFFF -FFFFFFFFFFFF0086FFFFFFFF0087FFFFFFFFFFFFFFFF0088FFFF008AFFFF0089FFFFFFFF008BFFFF008DFFFF008CFFFF -FFFFFFFFFFFFFFFF00910090008F008E0095009400930092FFFF0097FFFF0096FFFF0099FFFF0098FFFF009BFFFF009A -FFFF009DFFFF009C00A0FFFF009F009E00A2FFFF00A1FFFF00A4FFFF00A3FFFF00A6FFFF00A5FFFF00A8FFFF00A7FFFF -00AB00AA00A905D5FFFFFFFF00ACFFFF00AEFFFF00ADFFFF00B0FFFF00AFFFFF00B2FFFF00B1FFFF00B4FFFF00B3FFFF -00B6FFFF00B5FFFF00B8FFFF00B7FFFF00BAFFFF00B9FFFF00BCFFFF00BBFFFF00BEFFFF00BDFFFF00C0FFFF00BFFFFF -00C1FFFFFFFFFFFF00C3FFFF00C2FFFF00C5FFFF00C4FFFF00C7FFFF00C6FFFF00C9FFFF00C8FFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFF00CBFFFFFFFF00CAFFFF00CDFFFF00CC00CEFFFFFFFFFFFF00D0FFFF00CFFFFF00D2FFFF00D1FFFF -00D600D500D400D300D900D8FFFF00D700DBFFFF00DAFFFFFFFFFFFFFFFF00DC00DFFFFF00DE00DDFFFF00E100E0FFFF -00E500E400E300E200E7FFFFFFFF00E6FFFF00E900E8FFFFFFFFFFFF00EAFFFFFFFFFFFFFFFFFFFFFFFFFFFF00EBFFFF -00EE00EDFFFF00EC00EFFFFFFFFFFFFF00F300F200F100F0FFFFFFFFFFFF00F4FFFF00F5FFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFF00F700F6FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00F8FFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00FAFFFF00F9FFFF00FBFFFFFFFFFFFF -00FCFFFFFFFFFFFFFFFFFFFF00FE00FDFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFF05CFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFF01020101010000FF01050104010305D20109010801070106010D010C010B010A01110110010F010E -01150114011301120119011801170116011D011C011B011AFFFF0120011F011EFFFFFFFF01220121012501240123FFFF -0127FFFF0126FFFF0129FFFF0128FFFF012BFFFF012AFFFF012DFFFF012CFFFF012FFFFF012EFFFF0131FFFF0130FFFF -0135013401330132FFFFFFFF0136FFFF0138FFFFFFFF0137FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF013C013B013A01390140013F013E013D -01440143014201410148014701460145014C014B014A01490150014F014E014D01540153015201510158015701560155 -015C015B015A01590160015F015E015D01640163016201610168016701660165016AFFFF0169FFFF016CFFFF016BFFFF -016EFFFF016DFFFF0170FFFF016FFFFF0172FFFF0171FFFF0174FFFF0173FFFF0176FFFF0175FFFF0178FFFF0177FFFF -FFFFFFFF0179FFFFFFFFFFFFFFFFFFFF017AFFFFFFFFFFFF017CFFFF017BFFFF017EFFFF017DFFFF0180FFFF017FFFFF -0182FFFF0181FFFF0184FFFF0183FFFF0186FFFF0185FFFF0188FFFF0187FFFF018AFFFF0189FFFF018CFFFF018BFFFF -018EFFFF018DFFFF0190FFFF018FFFFF0192FFFF0191FFFF0194FFFF0193FFFFFFFF0195FFFFFFFFFFFF0197FFFF0196 -FFFF0199FFFF0198019C019BFFFF019A019EFFFF019DFFFF01A0FFFF019FFFFF01A2FFFF01A1FFFF01A4FFFF01A3FFFF -01A6FFFF01A5FFFF01A8FFFF01A7FFFF01AAFFFF01A9FFFF01ACFFFF01ABFFFF01AEFFFF01ADFFFF01B0FFFF01AFFFFF -01B2FFFF01B1FFFF01B4FFFF01B3FFFF01B6FFFF01B5FFFF01B8FFFF01B7FFFF01BAFFFF01B9FFFF01BCFFFF01BBFFFF -01BEFFFF01BDFFFF01C0FFFF01BFFFFF01C2FFFF01C1FFFF01C4FFFF01C3FFFF01C6FFFF01C5FFFF01C8FFFF01C7FFFF -01CAFFFF01C9FFFF01CCFFFF01CBFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF01CF01CE01CDFFFF01D301D201D101D001D701D601D501D401DB01DA01D901D8 -01DF01DE01DD01DC01E301E201E101E001E701E601E501E401EB01EA01E901E801EF01EE01ED01EC05C101F201F101F0 -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -01F601F501F401F301FA01F901F801F701FE01FD01FC01FB02020201020001FF0206020502040203020A020902080207 -020E020D020C020B021202110210020F0216021502140213021A021902180217FFFF021D021C021B0220021F021EFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0224022302220221FFFFFFFF02260225022A022902280227022E022D022C022B -FFFFFFFFFFFF022FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0230FFFFFFFFFFFF0231FFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0232FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0234FFFF0233FFFF0236FFFF0235FFFF -0238FFFF0237FFFF023AFFFF0239FFFF023CFFFF023BFFFF023EFFFF023DFFFF0240FFFF023FFFFF0242FFFF0241FFFF -0244FFFF0243FFFF0246FFFF0245FFFF0248FFFF0247FFFF024AFFFF0249FFFF024CFFFF024BFFFF024EFFFF024DFFFF -0250FFFF024FFFFF0252FFFF0251FFFF0254FFFF0253FFFF0256FFFF0255FFFF0258FFFF0257FFFF025AFFFF0259FFFF -025CFFFF025BFFFF025EFFFF025DFFFF0260FFFF025FFFFF0262FFFF0261FFFF0264FFFF0263FFFF0266FFFF0265FFFF -0268FFFF0267FFFF026AFFFF0269FFFF026CFFFF026BFFFF026EFFFF026DFFFF0270FFFF026FFFFF0272FFFF0271FFFF -0274FFFF0273FFFF0276FFFF0275FFFF0278FFFF0277FFFF027AFFFF0279FFFF027CFFFF027BFFFF05D905D7027DFFFF -027E05DF05DD05DBFFFFFFFFFFFFFFFF0280FFFF027FFFFF0282FFFF0281FFFF0284FFFF0283FFFF0286FFFF0285FFFF -0288FFFF0287FFFF028AFFFF0289FFFF028CFFFF028BFFFF028EFFFF028DFFFF0290FFFF028FFFFF0292FFFF0291FFFF -0294FFFF0293FFFF0296FFFF0295FFFF0298FFFF0297FFFF029AFFFF0299FFFF029CFFFF029BFFFF029EFFFF029DFFFF -02A0FFFF029FFFFF02A2FFFF02A1FFFF02A4FFFF02A3FFFF02A6FFFF02A5FFFF02A8FFFF02A7FFFF02AAFFFF02A9FFFF -02ACFFFF02ABFFFF02AEFFFF02ADFFFF02B202B102B002AF02B602B502B402B3FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -02BA02B902B802B7FFFFFFFF02BC02BBFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF02C002BF02BE02BD02C402C302C202C1 -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF02C802C702C602C502CC02CB02CA02C9FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -02D002CF02CE02CDFFFFFFFF02D202D1FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF02D405E302D305E102D605E902D505E6 -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF02DA02D902D802D702DE02DD02DC02DBFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -02E202E102E002DF02E602E502E402E302EA02E902E802E7FFFFFFFF02EC02EB060D060C060B060A06110610060F060E -06150614061306120619061806170616061D061C061B061A06210620061F061E06250624062306220629062806270626 -062D062C062B062A06310630062F062E06350634063306320639063806370636063A064003060305064C05ECFFFF0642 -FFFFFFFFFFFFFFFFFFFF0308FFFF063B063C0644FFFFFFFF064F05EEFFFF0646FFFFFFFFFFFFFFFFFFFFFFFFFFFF063D -05F305F0030B030A05F805F6FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF05FE05FB030D030C06050603030E0601 -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF063E0648FFFFFFFF06520608FFFF064AFFFFFFFFFFFFFFFFFFFFFFFFFFFF063F -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0310FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -03140313031203110318031703160315031C031B031A03190320031F031E031DFFFFFFFFFFFFFFFFFFFFFFFFFFFF0321 -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -03250324032303220329032803270326032D032C032B032A03310330032F032E03350334033303320339033803370336 -FFFFFFFF033B033AFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -033F033E033D033C03430342034103400347034603450344034B034A03490348034F034E034D034C0353035203510350 -0357035603550354035B035A03590358035F035E035D035C03630362036103600367036603650364036B036A03690368 -FFFFFFFF036CFFFFFFFF036E036DFFFFFFFF0370FFFF036FFFFFFFFFFFFF03710372FFFFFFFFFFFFFFFF0373FFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0375FFFF0374FFFF0377FFFF0376FFFF0379FFFF0378FFFF037BFFFF037AFFFF -037DFFFF037CFFFF037FFFFF037EFFFF0381FFFF0380FFFF0383FFFF0382FFFF0385FFFF0384FFFF0387FFFF0386FFFF -0389FFFF0388FFFF038BFFFF038AFFFF038DFFFF038CFFFF038FFFFF038EFFFF0391FFFF0390FFFF0393FFFF0392FFFF -0395FFFF0394FFFF0397FFFF0396FFFF0399FFFF0398FFFF039BFFFF039AFFFF039DFFFF039CFFFF039FFFFF039EFFFF -03A1FFFF03A0FFFF03A3FFFF03A2FFFF03A5FFFF03A4FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF03A7FFFF03A6 -03A8FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF03AC03AB03AA03A903B003AF03AE03AD -03B403B303B203B103B803B703B603B503BC03BB03BA03B903C003BF03BE03BD03C403C303C203C103C803C703C603C5 -03CC03CB03CA03C903CFFFFF03CE03CDFFFFFFFFFFFFFFFFFFFFFFFF03D0FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF03D2FFFF03D1FFFF03D4FFFF03D3FFFF03D6FFFF03D5FFFF03D8FFFF03D7FFFF -03DAFFFF03D9FFFF03DCFFFF03DBFFFF03DEFFFF03DDFFFF03E0FFFF03DFFFFF03E2FFFF03E1FFFF03E4FFFF03E3FFFF -03E6FFFF03E5FFFFFFFFFFFF03E7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -03E9FFFF03E8FFFF03EBFFFF03EAFFFF03EDFFFF03ECFFFF03EFFFFF03EEFFFF03F1FFFF03F0FFFF03F3FFFF03F2FFFF -03F5FFFF03F4FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -03F6FFFFFFFFFFFF03F8FFFF03F7FFFF03FAFFFF03F9FFFF03FCFFFF03FBFFFF03FDFFFFFFFFFFFF03FFFFFF03FEFFFF -0401FFFF0400FFFF0403FFFF0402FFFF0405FFFF0404FFFF0407FFFF0406FFFF0409FFFF0408FFFF040BFFFF040AFFFF -040DFFFF040CFFFF040FFFFF040EFFFF0411FFFF0410FFFF0413FFFF0412FFFF0415FFFF0414FFFF0417FFFF0416FFFF -0419FFFF0418FFFF041BFFFF041AFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF041CFFFFFFFF041EFFFFFFFF041D -0420FFFF041FFFFF0422FFFF0421FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF04230425FFFF0424FFFF0427FFFFFFFF0426 -0429FFFF0428FFFF042BFFFF042AFFFF042DFFFF042CFFFF042FFFFF042EFFFFFFFFFFFF0430FFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFF0432FFFF0431FFFF0434FFFF0433FFFF0436FFFF0435FFFF0438FFFF0437FFFFFFFFFFFFFFFFFFFF -FFFF043AFFFF0439FFFFFFFFFFFFFFFFFFFFFFFF043BFFFF043CFFFFFFFFFFFFFFFFFFFF043DFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF043EFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -043FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF04430442044104400447044604450444044B044A04490448044F044E044D044C -04530452045104500457045604550454045B045A04590458045F045E045D045C04630462046104600467046604650464 -046B046A04690468046F046E046D046C04730472047104700477047604750474047B047A04790478047F047E047D047C -04830482048104800487048604850484048B048A04890488048F048E048D048C05B705B505B305B1FFFF05BF05BD05BA -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF05C3FFFFFFFFFFFF05CB05C905C705C5FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF049204910490FFFF0496049504940493049A049904980497049E049D049C049B -04A204A104A0049F04A604A504A404A3FFFF04A904A804A7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF04AD04AC04AB04AA04B104B004AF04AE -04B504B404B304B204B904B804B704B604BD04BC04BB04BA04C104C004BF04BE04C504C404C304C204C904C804C704C6 -04CD04CC04CB04CA04D104D004CF04CEFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF04D504D404D304D204D904D804D704D604DD04DC04DB04DA04E104E004DF04DE -04E504E404E304E204E904E804E704E604ED04EC04EB04EA04F104F004EF04EE04F504F404F304F2FFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF04F6FFFFFFFFFFFF -04FA04F904F804F704FE04FD04FC04FB0501FFFF050004FF05050504050305020509050805070506050D050C050B050A -0510FFFF050F050E05140513051205110517FFFF05160515FFFFFFFFFFFF0518051C051B051A05190520051F051E051D -05240523052205210528052705260525052C052B052A05290530052F052E052D05340533053205310538053705360535 -053C053B053A05390540053F053E053D05440543054205410548054705460545FFFF054B054A0549FFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF054F054E054D054C05530552055105500557055605550554055B055A05590558 -055F055E055D055C05630562056105600567056605650564056B056A05690568FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF056F056E056D056C05730572057105700577057605750574057B057A05790578 -057F057E057D057C05830582058105800587058605850584058B058A05890588FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -058D058CFFFFFFFF05910590058F058E05950594059305920599059805970596059D059C059B059A05A105A0059F059E -05A505A405A305A205A905A805A705A605AD05AC05AB05AAFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF", -); -//10368 bytes -enum toUpperSimpleIndexTrieEntries = TrieEntry!(ushort, 8, 7, 6)(x" -000000000000000000000000000000200000000000000140", -x" -000000000000010000000000000004800000000000000F40", -x" -040203020202010007020202060202050202020202020202020202020202020202020202020202020202020202020202 -020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202 -020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202 -020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202 -020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202 -02020202020202020202020202020202000300020001000000070006000500040000000A00090008000D000C000B0000 -00110010000F000E00000014001300120000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -001500000000000000000000000000000000000000000000001600000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -00000017000000000000001900180000001D001C001B001A00210020001F001E00000000000000000000002300220000 -000000000000000000000000000000000024000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000280027002600250000000000000029 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000002B002A0000002F002E002D002C00000000000000000000000000000000 -000000000000000000000031003000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000320000 -000000000000000000000000000000000000000000000000000000000000000000350000003400330000003600000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -003700000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -003800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000003900000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000003B003A0000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -00000000000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF000200010000FFFF0006000500040003000A000900080007000E000D000C000B -001200110010000F0016001500140013FFFF001900180017FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF001AFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF001E001D001C001B002200210020001F -0026002500240023002A002900280027002E002D002C002BFFFF00310030002F00350034003300320039003800370036 -003BFFFF003AFFFF003DFFFF003CFFFF003FFFFF003EFFFF0041FFFF0040FFFF0043FFFF0042FFFF0045FFFF0044FFFF -0047FFFF0046FFFF0049FFFF0048FFFF004BFFFF004AFFFF004DFFFF004CFFFF004FFFFF004EFFFF0051FFFF0050FFFF -0053FFFF0052FFFF0055FFFF0054FFFFFFFF0056FFFFFFFFFFFF0058FFFF0057FFFF005AFFFF0059FFFF005CFFFF005B -005EFFFFFFFF005D0060FFFF005FFFFF0062FFFF0061FFFF0064FFFF0063FFFF0066FFFF0065FFFF0068FFFF0067FFFF -006AFFFF0069FFFF006CFFFF006BFFFF006EFFFF006DFFFF0070FFFF006FFFFF0072FFFF0071FFFF0074FFFF0073FFFF -FFFF0075FFFFFFFF00780077FFFF0076007AFFFFFFFF0079FFFFFFFF007BFFFFFFFFFFFFFFFF007CFFFFFFFFFFFF007D -FFFF007EFFFFFFFFFFFFFFFF007FFFFFFFFF00810080FFFFFFFF0082FFFFFFFF0084FFFF0083FFFFFFFFFFFF0085FFFF -FFFFFFFFFFFF0086FFFFFFFF0087FFFFFFFFFFFFFFFF0088FFFF008AFFFF0089FFFFFFFF008BFFFF008DFFFF008CFFFF -FFFFFFFFFFFFFFFFFFFF008F008EFFFF0092FFFF00910090FFFF0094FFFF0093FFFF0096FFFF0095FFFF0098FFFF0097 -FFFF009AFFFF0099009DFFFF009C009B009FFFFF009EFFFF00A1FFFF00A0FFFF00A3FFFF00A2FFFF00A5FFFF00A4FFFF -00A700A6FFFFFFFFFFFFFFFF00A8FFFF00AAFFFF00A9FFFF00ACFFFF00ABFFFF00AEFFFF00ADFFFF00B0FFFF00AFFFFF -00B2FFFF00B1FFFF00B4FFFF00B3FFFF00B6FFFF00B5FFFF00B8FFFF00B7FFFF00BAFFFF00B9FFFF00BCFFFF00BBFFFF -00BDFFFFFFFFFFFF00BFFFFF00BEFFFF00C1FFFF00C0FFFF00C3FFFF00C2FFFF00C5FFFF00C4FFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFF00C7FFFFFFFF00C6FFFF00C9FFFF00C800CAFFFFFFFFFFFF00CCFFFF00CBFFFF00CEFFFF00CDFFFF -00D200D100D000CF00D500D4FFFF00D300D7FFFF00D6FFFFFFFFFFFFFFFF00D800DBFFFF00DA00D9FFFF00DD00DCFFFF -00E100E000DF00DE00E3FFFFFFFF00E2FFFF00E500E4FFFFFFFFFFFF00E6FFFFFFFFFFFFFFFFFFFFFFFFFFFF00E7FFFF -00EA00E9FFFF00E800EBFFFFFFFFFFFF00EF00EE00ED00ECFFFFFFFFFFFF00F0FFFF00F1FFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFF00F300F2FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00F4FFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00F6FFFF00F5FFFF00F7FFFFFFFFFFFF -00F8FFFFFFFFFFFFFFFFFFFF00FA00F9FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFF00FE00FD00FC00FB0101010000FFFFFF01050104010301020109010801070106010D010C010B010A -01110110010F010E01150114011301120119011801170116FFFF011C011B011AFFFFFFFF011E011D01210120011FFFFF -0123FFFF0122FFFF0125FFFF0124FFFF0127FFFF0126FFFF0129FFFF0128FFFF012BFFFF012AFFFF012DFFFF012CFFFF -01310130012F012EFFFFFFFF0132FFFF0134FFFFFFFF0133FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0138013701360135013C013B013A0139 -0140013F013E013D01440143014201410148014701460145014C014B014A01490150014F014E014D0154015301520151 -0158015701560155015C015B015A01590160015F015E015D01640163016201610166FFFF0165FFFF0168FFFF0167FFFF -016AFFFF0169FFFF016CFFFF016BFFFF016EFFFF016DFFFF0170FFFF016FFFFF0172FFFF0171FFFF0174FFFF0173FFFF -FFFFFFFF0175FFFFFFFFFFFFFFFFFFFF0176FFFFFFFFFFFF0178FFFF0177FFFF017AFFFF0179FFFF017CFFFF017BFFFF -017EFFFF017DFFFF0180FFFF017FFFFF0182FFFF0181FFFF0184FFFF0183FFFF0186FFFF0185FFFF0188FFFF0187FFFF -018AFFFF0189FFFF018CFFFF018BFFFF018EFFFF018DFFFF0190FFFF018FFFFFFFFF0191FFFFFFFFFFFF0193FFFF0192 -FFFF0195FFFF019401980197FFFF0196019AFFFF0199FFFF019CFFFF019BFFFF019EFFFF019DFFFF01A0FFFF019FFFFF -01A2FFFF01A1FFFF01A4FFFF01A3FFFF01A6FFFF01A5FFFF01A8FFFF01A7FFFF01AAFFFF01A9FFFF01ACFFFF01ABFFFF -01AEFFFF01ADFFFF01B0FFFF01AFFFFF01B2FFFF01B1FFFF01B4FFFF01B3FFFF01B6FFFF01B5FFFF01B8FFFF01B7FFFF -01BAFFFF01B9FFFF01BCFFFF01BBFFFF01BEFFFF01BDFFFF01C0FFFF01BFFFFF01C2FFFF01C1FFFF01C4FFFF01C3FFFF -01C6FFFF01C5FFFF01C8FFFF01C7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF01CB01CA01C9FFFF01CF01CE01CD01CC01D301D201D101D001D701D601D501D4 -01DB01DA01D901D801DF01DE01DD01DC01E301E201E101E001E701E601E501E401EB01EA01E901E8FFFF01EE01ED01EC -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -01F201F101F001EF01F601F501F401F301FA01F901F801F701FE01FD01FC01FB02020201020001FF0206020502040203 -020A020902080207020E020D020C020B021202110210020F0216021502140213FFFF021902180217021C021B021AFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0220021F021E021DFFFFFFFF022202210226022502240223022A022902280227 -FFFFFFFFFFFF022BFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF022CFFFFFFFFFFFF022DFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF022EFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0230FFFF022FFFFF0232FFFF0231FFFF -0234FFFF0233FFFF0236FFFF0235FFFF0238FFFF0237FFFF023AFFFF0239FFFF023CFFFF023BFFFF023EFFFF023DFFFF -0240FFFF023FFFFF0242FFFF0241FFFF0244FFFF0243FFFF0246FFFF0245FFFF0248FFFF0247FFFF024AFFFF0249FFFF -024CFFFF024BFFFF024EFFFF024DFFFF0250FFFF024FFFFF0252FFFF0251FFFF0254FFFF0253FFFF0256FFFF0255FFFF -0258FFFF0257FFFF025AFFFF0259FFFF025CFFFF025BFFFF025EFFFF025DFFFF0260FFFF025FFFFF0262FFFF0261FFFF -0264FFFF0263FFFF0266FFFF0265FFFF0268FFFF0267FFFF026AFFFF0269FFFF026CFFFF026BFFFF026EFFFF026DFFFF -0270FFFF026FFFFF0272FFFF0271FFFF0274FFFF0273FFFF0276FFFF0275FFFF0278FFFF0277FFFFFFFFFFFF0279FFFF -027AFFFFFFFFFFFFFFFFFFFFFFFFFFFF027CFFFF027BFFFF027EFFFF027DFFFF0280FFFF027FFFFF0282FFFF0281FFFF -0284FFFF0283FFFF0286FFFF0285FFFF0288FFFF0287FFFF028AFFFF0289FFFF028CFFFF028BFFFF028EFFFF028DFFFF -0290FFFF028FFFFF0292FFFF0291FFFF0294FFFF0293FFFF0296FFFF0295FFFF0298FFFF0297FFFF029AFFFF0299FFFF -029CFFFF029BFFFF029EFFFF029DFFFF02A0FFFF029FFFFF02A2FFFF02A1FFFF02A4FFFF02A3FFFF02A6FFFF02A5FFFF -02A8FFFF02A7FFFF02AAFFFF02A9FFFF02AE02AD02AC02AB02B202B102B002AFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -02B602B502B402B3FFFFFFFF02B802B7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF02BC02BB02BA02B902C002BF02BE02BD -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF02C402C302C202C102C802C702C602C5FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -02CC02CB02CA02C9FFFFFFFF02CE02CDFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF02D0FFFF02CFFFFF02D2FFFF02D1FFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF02D602D502D402D302DA02D902D802D7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -02DE02DD02DC02DB02E202E102E002DF02E602E502E402E3FFFFFFFF02E802E702EC02EB02EA02E902F002EF02EE02ED -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF02F402F302F202F102F802F702F602F5FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -02FC02FB02FA02F9030002FF02FE02FDFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0303FFFF03020301FFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFF0304FFFFFFFF0305FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFF03070306FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF03090308FFFFFFFF030AFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF030BFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF030CFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -0310030F030E030D03140313031203110318031703160315031C031B031A0319FFFFFFFFFFFFFFFFFFFFFFFFFFFF031D -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -03210320031F031E03250324032303220329032803270326032D032C032B032A03310330032F032E0335033403330332 -FFFFFFFF03370336FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -033B033A03390338033F033E033D033C03430342034103400347034603450344034B034A03490348034F034E034D034C -03530352035103500357035603550354035B035A03590358035F035E035D035C03630362036103600367036603650364 -FFFFFFFF0368FFFFFFFF036A0369FFFFFFFF036CFFFF036BFFFFFFFFFFFF036D036EFFFFFFFFFFFFFFFF036FFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0371FFFF0370FFFF0373FFFF0372FFFF0375FFFF0374FFFF0377FFFF0376FFFF -0379FFFF0378FFFF037BFFFF037AFFFF037DFFFF037CFFFF037FFFFF037EFFFF0381FFFF0380FFFF0383FFFF0382FFFF -0385FFFF0384FFFF0387FFFF0386FFFF0389FFFF0388FFFF038BFFFF038AFFFF038DFFFF038CFFFF038FFFFF038EFFFF -0391FFFF0390FFFF0393FFFF0392FFFF0395FFFF0394FFFF0397FFFF0396FFFF0399FFFF0398FFFF039BFFFF039AFFFF -039DFFFF039CFFFF039FFFFF039EFFFF03A1FFFF03A0FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF03A3FFFF03A2 -03A4FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF03A803A703A603A503AC03AB03AA03A9 -03B003AF03AE03AD03B403B303B203B103B803B703B603B503BC03BB03BA03B903C003BF03BE03BD03C403C303C203C1 -03C803C703C603C503CBFFFF03CA03C9FFFFFFFFFFFFFFFFFFFFFFFF03CCFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF03CEFFFF03CDFFFF03D0FFFF03CFFFFF03D2FFFF03D1FFFF03D4FFFF03D3FFFF -03D6FFFF03D5FFFF03D8FFFF03D7FFFF03DAFFFF03D9FFFF03DCFFFF03DBFFFF03DEFFFF03DDFFFF03E0FFFF03DFFFFF -03E2FFFF03E1FFFFFFFFFFFF03E3FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -03E5FFFF03E4FFFF03E7FFFF03E6FFFF03E9FFFF03E8FFFF03EBFFFF03EAFFFF03EDFFFF03ECFFFF03EFFFFF03EEFFFF -03F1FFFF03F0FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -03F2FFFFFFFFFFFF03F4FFFF03F3FFFF03F6FFFF03F5FFFF03F8FFFF03F7FFFF03F9FFFFFFFFFFFF03FBFFFF03FAFFFF -03FDFFFF03FCFFFF03FFFFFF03FEFFFF0401FFFF0400FFFF0403FFFF0402FFFF0405FFFF0404FFFF0407FFFF0406FFFF -0409FFFF0408FFFF040BFFFF040AFFFF040DFFFF040CFFFF040FFFFF040EFFFF0411FFFF0410FFFF0413FFFF0412FFFF -0415FFFF0414FFFF0417FFFF0416FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0418FFFFFFFF041AFFFFFFFF0419 -041CFFFF041BFFFF041EFFFF041DFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF041F0421FFFF0420FFFF0423FFFFFFFF0422 -0425FFFF0424FFFF0427FFFF0426FFFF0429FFFF0428FFFF042BFFFF042AFFFFFFFFFFFF042CFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFF042EFFFF042DFFFF0430FFFF042FFFFF0432FFFF0431FFFF0434FFFF0433FFFFFFFFFFFFFFFFFFFF -FFFF0436FFFF0435FFFFFFFFFFFFFFFFFFFFFFFF0437FFFF0438FFFFFFFFFFFFFFFFFFFF0439FFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF043AFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -043BFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF043F043E043D043C04430442044104400447044604450444044B044A04490448 -044F044E044D044C04530452045104500457045604550454045B045A04590458045F045E045D045C0463046204610460 -0467046604650464046B046A04690468046F046E046D046C04730472047104700477047604750474047B047A04790478 -047F047E047D047C04830482048104800487048604850484048B048A04890488048E048D048CFFFF049204910490048F -0496049504940493049A049904980497049E049D049C049B04A204A104A0049FFFFF04A504A404A3FFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -04A904A804A704A604AD04AC04AB04AA04B104B004AF04AE04B504B404B304B204B904B804B704B604BD04BC04BB04BA -04C104C004BF04BE04C504C404C304C204C904C804C704C604CD04CC04CB04CAFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF04D104D004CF04CE04D504D404D304D2 -04D904D804D704D604DD04DC04DB04DA04E104E004DF04DE04E504E404E304E204E904E804E704E604ED04EC04EB04EA -04F104F004EF04EEFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFF04F2FFFFFFFFFFFF04F604F504F404F304FA04F904F804F704FDFFFF04FC04FB0501050004FF04FE -05050504050305020509050805070506050CFFFF050B050A0510050F050E050D0513FFFF05120511FFFFFFFFFFFF0514 -0518051705160515051C051B051A05190520051F051E051D05240523052205210528052705260525052C052B052A0529 -0530052F052E052D05340533053205310538053705360535053C053B053A05390540053F053E053D0544054305420541 -FFFF054705460545FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF054B054A05490548054F054E054D054C -05530552055105500557055605550554055B055A05590558055F055E055D055C05630562056105600567056605650564 -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF056B056A05690568056F056E056D056C -05730572057105700577057605750574057B057A05790578057F057E057D057C05830582058105800587058605850584 -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF05890588FFFFFFFF058D058C058B058A05910590058F058E0595059405930592 -0599059805970596059D059C059B059A05A105A0059F059E05A505A405A305A205A905A805A705A6FFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF", -); -//9856 bytes -enum toLowerSimpleIndexTrieEntries = TrieEntry!(ushort, 8, 7, 6)(x" -000000000000000000000000000000200000000000000140", -x" -000000000000010000000000000004800000000000000E40", -x" -040203020202010007020202060202050202020202020202020202020202020202020202020202020202020202020202 -020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202 -020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202 -020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202 -020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202 -02020202020202020202020202020202000200000001000000060005000400030000000000080007000B000A00090000 -000F000E000D000C00000000001100100000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -001300120000000000000000000000000000000000000000001500140000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -00000016000000000000000000000000001A001900180017001E001D001C001B0000000000000000000000210020001F -000000000000000000000000000000000023002200000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000270026002500240000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000002900280000002D002C002B002A00000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002E -0000000000000000000000000000000000000000000000000000000000000000003100300000002F0000003300320000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000340000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000350000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000003600000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000370000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -00000000000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -000200010000FFFF0006000500040003000A000900080007000E000D000C000B001200110010000F0016001500140013 -FFFF001900180017FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF001D001C001B001A00210020001F001E -00250024002300220029002800270026002D002C002B002AFFFF0030002F002E0034003300320031FFFF003700360035 -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0039FFFF0038FFFF003BFFFF003AFFFF003DFFFF003CFFFF003FFFFF003E -FFFF0041FFFF0040FFFF0043FFFF0042FFFF0045FFFF0044FFFF0047FFFF0046FFFF0049FFFF0048FFFF004BFFFF004A -FFFF004DFFFF004CFFFF004FFFFF004EFFFF0051FFFF0050FFFF0053FFFF00520055FFFF0054FFFF0057FFFF0056FFFF -0059FFFF0058FFFF005BFFFF005AFFFFFFFF005CFFFFFFFFFFFF005EFFFF005DFFFF0060FFFF005FFFFF0062FFFF0061 -FFFF0064FFFF0063FFFF0066FFFF0065FFFF0068FFFF0067FFFF006AFFFF0069FFFF006CFFFF006BFFFF006EFFFF006D -FFFF0070FFFF006FFFFF0072FFFF00710075FFFF00740073FFFFFFFF0076FFFFFFFF00780077FFFF007B007AFFFF0079 -007E007D007CFFFF0080007FFFFFFFFF0083FFFF0082008100860085FFFF0084FFFFFFFFFFFF0087008AFFFF00890088 -FFFF008CFFFF008B008F008EFFFF008DFFFFFFFF0090FFFF00930092FFFF0091009600950094FFFF0098FFFF0097FFFF -FFFFFFFFFFFF0099FFFFFFFFFFFF009AFFFFFFFFFFFFFFFF009DFFFF009C009B00A0009FFFFF009E00A2FFFF00A1FFFF -00A4FFFF00A3FFFF00A6FFFF00A5FFFF00A8FFFF00A7FFFFFFFF00A9FFFFFFFFFFFF00ABFFFF00AAFFFF00ADFFFF00AC -FFFF00AFFFFF00AEFFFF00B1FFFF00B0FFFF00B300B2FFFF00B600B5FFFF00B4FFFF00B8FFFF00B7FFFF00BAFFFF00B9 -FFFF00BCFFFF00BBFFFF00BEFFFF00BDFFFF00C0FFFF00BFFFFF00C2FFFF00C1FFFF00C4FFFF00C3FFFF00C6FFFF00C5 -FFFF00C8FFFF00C7FFFF00CAFFFF00C9FFFF00CCFFFF00CBFFFF00CEFFFF00CDFFFF00D0FFFF00CFFFFF00D2FFFF00D1 -FFFF00D4FFFF00D3FFFFFFFFFFFFFFFF00D600D5FFFFFFFFFFFF00D800D7FFFF00DAFFFF00D9FFFFFFFF00DD00DC00DB -FFFF00DFFFFF00DEFFFF00E1FFFF00E0FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00E3FFFF00E2FFFF00E4FFFFFFFFFFFFFFFFFFFFFFFF00E5FFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFF00E6FFFFFFFFFFFF00E900E800E700EC00EBFFFF00EA00EF00EE00EDFFFF00F300F200F100F0 -00F700F600F500F400FB00FA00F900F800FEFFFF00FD00FC01020101010000FF0106010501040103FFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFF0107FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0109FFFF0108FFFF010BFFFF010A -FFFF010DFFFF010CFFFF010FFFFF010EFFFF0111FFFF0110FFFF0113FFFF0112FFFFFFFFFFFFFFFF0115FFFFFFFF0114 -FFFF01170116FFFF011A01190118FFFF011E011D011C011B012201210120011F0126012501240123012A012901280127 -012E012D012C012B013201310130012F0136013501340133013A013901380137013E013D013C013B014201410140013F -0146014501440143014A014901480147FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF014CFFFF014BFFFF014EFFFF014DFFFF0150FFFF014FFFFF0152FFFF0151 -FFFF0154FFFF0153FFFF0156FFFF0155FFFF0158FFFF0157FFFF015AFFFF0159FFFFFFFFFFFF015BFFFFFFFFFFFFFFFF -FFFF015CFFFFFFFFFFFF015EFFFF015DFFFF0160FFFF015FFFFF0162FFFF0161FFFF0164FFFF0163FFFF0166FFFF0165 -FFFF0168FFFF0167FFFF016AFFFF0169FFFF016CFFFF016BFFFF016EFFFF016DFFFF0170FFFF016FFFFF0172FFFF0171 -FFFF0174FFFF0173FFFF0176FFFF01750179FFFF01780177017BFFFF017AFFFF017DFFFF017CFFFFFFFFFFFF017EFFFF -FFFF0180FFFF017FFFFF0182FFFF0181FFFF0184FFFF0183FFFF0186FFFF0185FFFF0188FFFF0187FFFF018AFFFF0189 -FFFF018CFFFF018BFFFF018EFFFF018DFFFF0190FFFF018FFFFF0192FFFF0191FFFF0194FFFF0193FFFF0196FFFF0195 -FFFF0198FFFF0197FFFF019AFFFF0199FFFF019CFFFF019BFFFF019EFFFF019DFFFF01A0FFFF019FFFFF01A2FFFF01A1 -FFFF01A4FFFF01A3FFFF01A6FFFF01A5FFFF01A8FFFF01A7FFFF01AAFFFF01A9FFFF01ACFFFF01ABFFFF01AEFFFF01AD -01B101B001AFFFFF01B501B401B301B201B901B801B701B601BD01BC01BB01BA01C101C001BF01BE01C501C401C301C2 -01C901C801C701C601CD01CC01CB01CA01D101D001CF01CEFFFF01D401D301D2FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF01D801D701D601D501DC01DB01DA01D9 -01E001DF01DE01DD01E401E301E201E101E801E701E601E501EC01EB01EA01E901F001EF01EE01ED01F401F301F201F1 -01F801F701F601F501FBFFFF01FA01F9FFFFFFFFFFFFFFFFFFFFFFFF01FCFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -020001FF01FE01FD02040203020202010208020702060205020C020B020A02090210020F020E020D0214021302120211 -0218021702160215021C021B021A02190220021F021E021D02240223022202210228022702260225022C022B022A0229 -0230022F022E022D02340233023202310238023702360235023C023B023A02390240023F023E023D0244024302420241 -0248024702460245024C024B024A02490250024F024E024DFFFFFFFF02520251FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0256025502540253025A025902580257 -025E025D025C025B026202610260025F0266026502640263026A026902680267026E026D026C026B027202710270026F -0276027502740273027A027902780277FFFF027D027C027B0280027F027EFFFFFFFF0282FFFF0281FFFF0284FFFF0283 -FFFF0286FFFF0285FFFF0288FFFF0287FFFF028AFFFF0289FFFF028CFFFF028BFFFF028EFFFF028DFFFF0290FFFF028F -FFFF0292FFFF0291FFFF0294FFFF0293FFFF0296FFFF0295FFFF0298FFFF0297FFFF029AFFFF0299FFFF029CFFFF029B -FFFF029EFFFF029DFFFF02A0FFFF029FFFFF02A2FFFF02A1FFFF02A4FFFF02A3FFFF02A6FFFF02A5FFFF02A8FFFF02A7 -FFFF02AAFFFF02A9FFFF02ACFFFF02ABFFFF02AEFFFF02ADFFFF02B0FFFF02AFFFFF02B2FFFF02B1FFFF02B4FFFF02B3 -FFFF02B6FFFF02B5FFFF02B8FFFF02B7FFFF02BAFFFF02B9FFFF02BCFFFF02BBFFFF02BEFFFF02BDFFFF02C0FFFF02BF -FFFF02C2FFFF02C1FFFF02C4FFFF02C3FFFF02C6FFFF02C5FFFF02C8FFFF02C7FFFF02CAFFFF02C9FFFFFFFFFFFF02CB -FFFFFFFFFFFFFFFFFFFF02CCFFFFFFFFFFFF02CEFFFF02CDFFFF02D0FFFF02CFFFFF02D2FFFF02D1FFFF02D4FFFF02D3 -FFFF02D6FFFF02D5FFFF02D8FFFF02D7FFFF02DAFFFF02D9FFFF02DCFFFF02DBFFFF02DEFFFF02DDFFFF02E0FFFF02DF -FFFF02E2FFFF02E1FFFF02E4FFFF02E3FFFF02E6FFFF02E5FFFF02E8FFFF02E7FFFF02EAFFFF02E9FFFF02ECFFFF02EB -FFFF02EEFFFF02EDFFFF02F0FFFF02EFFFFF02F2FFFF02F1FFFF02F4FFFF02F3FFFF02F6FFFF02F5FFFF02F8FFFF02F7 -FFFF02FAFFFF02F9FFFF02FCFFFF02FBFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF030002FF02FE02FD0304030303020301 -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0308030703060305FFFFFFFF030A0309FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -030E030D030C030B031203110310030FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0316031503140313031A031903180317 -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF031E031D031C031BFFFFFFFF0320031FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -0322FFFF0321FFFF0324FFFF0323FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0328032703260325032C032B032A0329 -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -0330032F032E032D0334033303320331FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0338033703360335033C033B033A0339 -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0340033F033E033D0344034303420341FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -0348034703460345FFFFFFFFFFFF0349FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF034D034C034B034AFFFFFFFFFFFF034E -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF035203510350034FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -0356035503540353FFFFFFFFFFFF0357FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF035B035A03590358FFFFFFFFFFFF035C -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF035DFFFFFFFF035F035EFFFFFFFFFFFFFFFFFFFFFFFF -FFFF0360FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -03640363036203610368036703660365036C036B036A03690370036F036E036DFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0371FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFF03730372FFFFFFFF0377037603750374037B037A03790378037F037E037D037C0383038203810380 -0387038603850384038B038A03890388FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF038F038E038D038C03930392039103900397039603950394039B039A03990398 -039F039E039D039C03A303A203A103A003A703A603A503A403AB03AA03A903A803AF03AE03AD03AC03B303B203B103B0 -03B703B603B503B403BB03BA03B903B8FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF03BE03BDFFFF03BC03C0FFFFFFFF03BF03C2FFFF03C1FFFF03C503C403C3FFFF -FFFF03C7FFFF03C6FFFFFFFF03C8FFFFFFFFFFFFFFFFFFFF03CA03C9FFFFFFFFFFFF03CCFFFF03CBFFFF03CEFFFF03CD -FFFF03D0FFFF03CFFFFF03D2FFFF03D1FFFF03D4FFFF03D3FFFF03D6FFFF03D5FFFF03D8FFFF03D7FFFF03DAFFFF03D9 -FFFF03DCFFFF03DBFFFF03DEFFFF03DDFFFF03E0FFFF03DFFFFF03E2FFFF03E1FFFF03E4FFFF03E3FFFF03E6FFFF03E5 -FFFF03E8FFFF03E7FFFF03EAFFFF03E9FFFF03ECFFFF03EBFFFF03EEFFFF03EDFFFF03F0FFFF03EFFFFF03F2FFFF03F1 -FFFF03F4FFFF03F3FFFF03F6FFFF03F5FFFF03F8FFFF03F7FFFF03FAFFFF03F9FFFF03FCFFFF03FBFFFFFFFFFFFFFFFF -03FDFFFFFFFFFFFFFFFFFFFF03FEFFFFFFFF03FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFF0401FFFF0400FFFF0403FFFF0402FFFF0405FFFF0404FFFF0407FFFF0406FFFF0409FFFF0408FFFF040BFFFF040A -FFFF040DFFFF040CFFFF040FFFFF040EFFFF0411FFFF0410FFFF0413FFFF0412FFFF0415FFFF0414FFFFFFFFFFFF0416 -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0418FFFF0417FFFF041AFFFF0419 -FFFF041CFFFF041BFFFF041EFFFF041DFFFF0420FFFF041FFFFF0422FFFF0421FFFF0424FFFF0423FFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0425FFFFFFFFFFFF0427FFFF0426 -FFFF0429FFFF0428FFFF042BFFFF042AFFFF042CFFFFFFFFFFFF042EFFFF042DFFFF0430FFFF042FFFFF0432FFFF0431 -FFFF0434FFFF0433FFFF0436FFFF0435FFFF0438FFFF0437FFFF043AFFFF0439FFFF043CFFFF043BFFFF043EFFFF043D -FFFF0440FFFF043FFFFF0442FFFF0441FFFF0444FFFF0443FFFF0446FFFF0445FFFF0448FFFF0447FFFF044AFFFF0449 -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF044CFFFF044BFFFFFFFF044E044DFFFFFFFF0450FFFF044FFFFF0452FFFF0451 -0453FFFFFFFFFFFFFFFFFFFF0454FFFFFFFF0456FFFF0455FFFF0457FFFFFFFFFFFF0459FFFF0458FFFF045BFFFF045A -FFFF045DFFFF045CFFFF045FFFFF045E04620461FFFF0460FFFF0465046404630469046804670466FFFF046BFFFF046A -FFFF046DFFFF046CFFFF046FFFFF046EFFFF0471FFFF04700475047404730472FFFFFFFF0476FFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFF0477FFFF0478FFFFFFFFFFFFFFFFFFFF0479FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF047AFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF047D047C047BFFFF04810480047F047E04850484048304820489048804870486 -048D048C048B048A04910490048F048EFFFF049404930492FFFFFFFFFFFFFFFF0498049704960495049C049B049A0499 -04A0049F049E049D04A404A304A204A104A804A704A604A504AC04AB04AA04A904B004AF04AE04AD04B404B304B204B1 -04B804B704B604B504BC04BB04BA04B9FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF04C004BF04BE04BD04C404C304C204C104C804C704C604C504CC04CB04CA04C9 -04D004CF04CE04CD04D404D304D204D104D804D704D604D504DC04DB04DA04D904E004DF04DE04DDFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF04E404E304E204E104E804E704E604E5 -FFFF04EB04EA04E904EF04EE04ED04EC04F304F204F104F004F704F604F504F4FFFF04FA04F904F804FE04FD04FC04FB -FFFF0501050004FFFFFFFFFF05030502FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -0507050605050504050B050A05090508050F050E050D050C05130512051105100517051605150514051B051A05190518 -051F051E051D051C05230522052105200527052605250524052B052A05290528052F052E052D052C0533053205310530 -FFFF053605350534FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -053A053905380537053E053D053C053B054205410540053F0546054505440543054A054905480547054E054D054C054B -055205510550054F0556055505540553055A055905580557055E055D055C055B056205610560055F0566056505640563 -056A056905680567056E056D056C056B057205710570056F0576057505740573FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -057A057905780577057E057D057C057B058205810580057F0586058505840583058A058905880587058E058D058C058B -059205910590058F0596059505940593FFFFFFFF05980597FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF", -); -//10368 bytes -enum toTitleSimpleIndexTrieEntries = TrieEntry!(ushort, 8, 7, 6)(x" -000000000000000000000000000000200000000000000140", -x" -000000000000010000000000000004800000000000000F40", -x" -040203020202010007020202060202050202020202020202020202020202020202020202020202020202020202020202 -020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202 -020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202 -020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202 -020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202 -02020202020202020202020202020202000300020001000000070006000500040000000A00090008000D000C000B0000 -00110010000F000E00000014001300120000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -001500000000000000000000000000000000000000000000001600000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -00000017000000000000001900180000001D001C001B001A00210020001F001E00000000000000000000002300220000 -000000000000000000000000000000000024000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000280027002600250000000000000029 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000002B002A0000002F002E002D002C00000000000000000000000000000000 -000000000000000000000031003000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000320000 -000000000000000000000000000000000000000000000000000000000000000000350000003400330000003600000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -003700000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -003800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000003900000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000003B003A0000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -00000000000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF000200010000FFFF0006000500040003000A000900080007000E000D000C000B -001200110010000F0016001500140013FFFF001900180017FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF001AFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF001E001D001C001B002200210020001F -0026002500240023002A002900280027002E002D002C002BFFFF00310030002F00350034003300320039003800370036 -003BFFFF003AFFFF003DFFFF003CFFFF003FFFFF003EFFFF0041FFFF0040FFFF0043FFFF0042FFFF0045FFFF0044FFFF -0047FFFF0046FFFF0049FFFF0048FFFF004BFFFF004AFFFF004DFFFF004CFFFF004FFFFF004EFFFF0051FFFF0050FFFF -0053FFFF0052FFFF0055FFFF0054FFFFFFFF0056FFFFFFFFFFFF0058FFFF0057FFFF005AFFFF0059FFFF005CFFFF005B -005EFFFFFFFF005D0060FFFF005FFFFF0062FFFF0061FFFF0064FFFF0063FFFF0066FFFF0065FFFF0068FFFF0067FFFF -006AFFFF0069FFFF006CFFFF006BFFFF006EFFFF006DFFFF0070FFFF006FFFFF0072FFFF0071FFFF0074FFFF0073FFFF -FFFF0075FFFFFFFF00780077FFFF0076007AFFFFFFFF0079FFFFFFFF007BFFFFFFFFFFFFFFFF007CFFFFFFFFFFFF007D -FFFF007EFFFFFFFFFFFFFFFF007FFFFFFFFF00810080FFFFFFFF0082FFFFFFFF0084FFFF0083FFFFFFFFFFFF0085FFFF -FFFFFFFFFFFF0086FFFFFFFF0087FFFFFFFFFFFFFFFF0088FFFF008AFFFF0089FFFFFFFF008BFFFF008DFFFF008CFFFF -FFFFFFFFFFFFFFFF00910090008F008E0095009400930092FFFF0097FFFF0096FFFF0099FFFF0098FFFF009BFFFF009A -FFFF009DFFFF009C00A0FFFF009F009E00A2FFFF00A1FFFF00A4FFFF00A3FFFF00A6FFFF00A5FFFF00A8FFFF00A7FFFF -00AB00AA00A9FFFFFFFFFFFF00ACFFFF00AEFFFF00ADFFFF00B0FFFF00AFFFFF00B2FFFF00B1FFFF00B4FFFF00B3FFFF -00B6FFFF00B5FFFF00B8FFFF00B7FFFF00BAFFFF00B9FFFF00BCFFFF00BBFFFF00BEFFFF00BDFFFF00C0FFFF00BFFFFF -00C1FFFFFFFFFFFF00C3FFFF00C2FFFF00C5FFFF00C4FFFF00C7FFFF00C6FFFF00C9FFFF00C8FFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFF00CBFFFFFFFF00CAFFFF00CDFFFF00CC00CEFFFFFFFFFFFF00D0FFFF00CFFFFF00D2FFFF00D1FFFF -00D600D500D400D300D900D8FFFF00D700DBFFFF00DAFFFFFFFFFFFFFFFF00DC00DFFFFF00DE00DDFFFF00E100E0FFFF -00E500E400E300E200E7FFFFFFFF00E6FFFF00E900E8FFFFFFFFFFFF00EAFFFFFFFFFFFFFFFFFFFFFFFFFFFF00EBFFFF -00EE00EDFFFF00EC00EFFFFFFFFFFFFF00F300F200F100F0FFFFFFFFFFFF00F4FFFF00F5FFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFF00F700F6FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00F8FFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00FAFFFF00F9FFFF00FBFFFFFFFFFFFF -00FCFFFFFFFFFFFFFFFFFFFF00FE00FDFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFF01020101010000FF010501040103FFFF0109010801070106010D010C010B010A01110110010F010E -01150114011301120119011801170116011D011C011B011AFFFF0120011F011EFFFFFFFF01220121012501240123FFFF -0127FFFF0126FFFF0129FFFF0128FFFF012BFFFF012AFFFF012DFFFF012CFFFF012FFFFF012EFFFF0131FFFF0130FFFF -0135013401330132FFFFFFFF0136FFFF0138FFFFFFFF0137FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF013C013B013A01390140013F013E013D -01440143014201410148014701460145014C014B014A01490150014F014E014D01540153015201510158015701560155 -015C015B015A01590160015F015E015D01640163016201610168016701660165016AFFFF0169FFFF016CFFFF016BFFFF -016EFFFF016DFFFF0170FFFF016FFFFF0172FFFF0171FFFF0174FFFF0173FFFF0176FFFF0175FFFF0178FFFF0177FFFF -FFFFFFFF0179FFFFFFFFFFFFFFFFFFFF017AFFFFFFFFFFFF017CFFFF017BFFFF017EFFFF017DFFFF0180FFFF017FFFFF -0182FFFF0181FFFF0184FFFF0183FFFF0186FFFF0185FFFF0188FFFF0187FFFF018AFFFF0189FFFF018CFFFF018BFFFF -018EFFFF018DFFFF0190FFFF018FFFFF0192FFFF0191FFFF0194FFFF0193FFFFFFFF0195FFFFFFFFFFFF0197FFFF0196 -FFFF0199FFFF0198019C019BFFFF019A019EFFFF019DFFFF01A0FFFF019FFFFF01A2FFFF01A1FFFF01A4FFFF01A3FFFF -01A6FFFF01A5FFFF01A8FFFF01A7FFFF01AAFFFF01A9FFFF01ACFFFF01ABFFFF01AEFFFF01ADFFFF01B0FFFF01AFFFFF -01B2FFFF01B1FFFF01B4FFFF01B3FFFF01B6FFFF01B5FFFF01B8FFFF01B7FFFF01BAFFFF01B9FFFF01BCFFFF01BBFFFF -01BEFFFF01BDFFFF01C0FFFF01BFFFFF01C2FFFF01C1FFFF01C4FFFF01C3FFFF01C6FFFF01C5FFFF01C8FFFF01C7FFFF -01CAFFFF01C9FFFF01CCFFFF01CBFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF01CF01CE01CDFFFF01D301D201D101D001D701D601D501D401DB01DA01D901D8 -01DF01DE01DD01DC01E301E201E101E001E701E601E501E401EB01EA01E901E801EF01EE01ED01ECFFFF01F201F101F0 -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -01F601F501F401F301FA01F901F801F701FE01FD01FC01FB02020201020001FF0206020502040203020A020902080207 -020E020D020C020B021202110210020F0216021502140213021A021902180217FFFF021D021C021B0220021F021EFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0224022302220221FFFFFFFF02260225022A022902280227022E022D022C022B -FFFFFFFFFFFF022FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0230FFFFFFFFFFFF0231FFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0232FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0234FFFF0233FFFF0236FFFF0235FFFF -0238FFFF0237FFFF023AFFFF0239FFFF023CFFFF023BFFFF023EFFFF023DFFFF0240FFFF023FFFFF0242FFFF0241FFFF -0244FFFF0243FFFF0246FFFF0245FFFF0248FFFF0247FFFF024AFFFF0249FFFF024CFFFF024BFFFF024EFFFF024DFFFF -0250FFFF024FFFFF0252FFFF0251FFFF0254FFFF0253FFFF0256FFFF0255FFFF0258FFFF0257FFFF025AFFFF0259FFFF -025CFFFF025BFFFF025EFFFF025DFFFF0260FFFF025FFFFF0262FFFF0261FFFF0264FFFF0263FFFF0266FFFF0265FFFF -0268FFFF0267FFFF026AFFFF0269FFFF026CFFFF026BFFFF026EFFFF026DFFFF0270FFFF026FFFFF0272FFFF0271FFFF -0274FFFF0273FFFF0276FFFF0275FFFF0278FFFF0277FFFF027AFFFF0279FFFF027CFFFF027BFFFFFFFFFFFF027DFFFF -027EFFFFFFFFFFFFFFFFFFFFFFFFFFFF0280FFFF027FFFFF0282FFFF0281FFFF0284FFFF0283FFFF0286FFFF0285FFFF -0288FFFF0287FFFF028AFFFF0289FFFF028CFFFF028BFFFF028EFFFF028DFFFF0290FFFF028FFFFF0292FFFF0291FFFF -0294FFFF0293FFFF0296FFFF0295FFFF0298FFFF0297FFFF029AFFFF0299FFFF029CFFFF029BFFFF029EFFFF029DFFFF -02A0FFFF029FFFFF02A2FFFF02A1FFFF02A4FFFF02A3FFFF02A6FFFF02A5FFFF02A8FFFF02A7FFFF02AAFFFF02A9FFFF -02ACFFFF02ABFFFF02AEFFFF02ADFFFF02B202B102B002AF02B602B502B402B3FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -02BA02B902B802B7FFFFFFFF02BC02BBFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF02C002BF02BE02BD02C402C302C202C1 -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF02C802C702C602C502CC02CB02CA02C9FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -02D002CF02CE02CDFFFFFFFF02D202D1FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF02D4FFFF02D3FFFF02D6FFFF02D5FFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF02DA02D902D802D702DE02DD02DC02DBFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -02E202E102E002DF02E602E502E402E302EA02E902E802E7FFFFFFFF02EC02EB02F002EF02EE02ED02F402F302F202F1 -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF02F802F702F602F502FC02FB02FA02F9FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -030002FF02FE02FD0304030303020301FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0307FFFF03060305FFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFF0308FFFFFFFF0309FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFF030B030AFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF030D030CFFFFFFFF030EFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF030FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0310FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -03140313031203110318031703160315031C031B031A03190320031F031E031DFFFFFFFFFFFFFFFFFFFFFFFFFFFF0321 -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -03250324032303220329032803270326032D032C032B032A03310330032F032E03350334033303320339033803370336 -FFFFFFFF033B033AFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -033F033E033D033C03430342034103400347034603450344034B034A03490348034F034E034D034C0353035203510350 -0357035603550354035B035A03590358035F035E035D035C03630362036103600367036603650364036B036A03690368 -FFFFFFFF036CFFFFFFFF036E036DFFFFFFFF0370FFFF036FFFFFFFFFFFFF03710372FFFFFFFFFFFFFFFF0373FFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0375FFFF0374FFFF0377FFFF0376FFFF0379FFFF0378FFFF037BFFFF037AFFFF -037DFFFF037CFFFF037FFFFF037EFFFF0381FFFF0380FFFF0383FFFF0382FFFF0385FFFF0384FFFF0387FFFF0386FFFF -0389FFFF0388FFFF038BFFFF038AFFFF038DFFFF038CFFFF038FFFFF038EFFFF0391FFFF0390FFFF0393FFFF0392FFFF -0395FFFF0394FFFF0397FFFF0396FFFF0399FFFF0398FFFF039BFFFF039AFFFF039DFFFF039CFFFF039FFFFF039EFFFF -03A1FFFF03A0FFFF03A3FFFF03A2FFFF03A5FFFF03A4FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF03A7FFFF03A6 -03A8FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF03AC03AB03AA03A903B003AF03AE03AD -03B403B303B203B103B803B703B603B503BC03BB03BA03B903C003BF03BE03BD03C403C303C203C103C803C703C603C5 -03CC03CB03CA03C903CFFFFF03CE03CDFFFFFFFFFFFFFFFFFFFFFFFF03D0FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF03D2FFFF03D1FFFF03D4FFFF03D3FFFF03D6FFFF03D5FFFF03D8FFFF03D7FFFF -03DAFFFF03D9FFFF03DCFFFF03DBFFFF03DEFFFF03DDFFFF03E0FFFF03DFFFFF03E2FFFF03E1FFFF03E4FFFF03E3FFFF -03E6FFFF03E5FFFFFFFFFFFF03E7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -03E9FFFF03E8FFFF03EBFFFF03EAFFFF03EDFFFF03ECFFFF03EFFFFF03EEFFFF03F1FFFF03F0FFFF03F3FFFF03F2FFFF -03F5FFFF03F4FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -03F6FFFFFFFFFFFF03F8FFFF03F7FFFF03FAFFFF03F9FFFF03FCFFFF03FBFFFF03FDFFFFFFFFFFFF03FFFFFF03FEFFFF -0401FFFF0400FFFF0403FFFF0402FFFF0405FFFF0404FFFF0407FFFF0406FFFF0409FFFF0408FFFF040BFFFF040AFFFF -040DFFFF040CFFFF040FFFFF040EFFFF0411FFFF0410FFFF0413FFFF0412FFFF0415FFFF0414FFFF0417FFFF0416FFFF -0419FFFF0418FFFF041BFFFF041AFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF041CFFFFFFFF041EFFFFFFFF041D -0420FFFF041FFFFF0422FFFF0421FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF04230425FFFF0424FFFF0427FFFFFFFF0426 -0429FFFF0428FFFF042BFFFF042AFFFF042DFFFF042CFFFF042FFFFF042EFFFFFFFFFFFF0430FFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFF0432FFFF0431FFFF0434FFFF0433FFFF0436FFFF0435FFFF0438FFFF0437FFFFFFFFFFFFFFFFFFFF -FFFF043AFFFF0439FFFFFFFFFFFFFFFFFFFFFFFF043BFFFF043CFFFFFFFFFFFFFFFFFFFF043DFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF043EFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -043FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF04430442044104400447044604450444044B044A04490448044F044E044D044C -04530452045104500457045604550454045B045A04590458045F045E045D045C04630462046104600467046604650464 -046B046A04690468046F046E046D046C04730472047104700477047604750474047B047A04790478047F047E047D047C -04830482048104800487048604850484048B048A04890488048F048E048D048C049204910490FFFF0496049504940493 -049A049904980497049E049D049C049B04A204A104A0049F04A604A504A404A3FFFF04A904A804A7FFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -04AD04AC04AB04AA04B104B004AF04AE04B504B404B304B204B904B804B704B604BD04BC04BB04BA04C104C004BF04BE -04C504C404C304C204C904C804C704C604CD04CC04CB04CA04D104D004CF04CEFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF04D504D404D304D204D904D804D704D6 -04DD04DC04DB04DA04E104E004DF04DE04E504E404E304E204E904E804E704E604ED04EC04EB04EA04F104F004EF04EE -04F504F404F304F2FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFF04F6FFFFFFFFFFFF04FA04F904F804F704FE04FD04FC04FB0501FFFF050004FF0505050405030502 -0509050805070506050D050C050B050A0510FFFF050F050E05140513051205110517FFFF05160515FFFFFFFFFFFF0518 -051C051B051A05190520051F051E051D05240523052205210528052705260525052C052B052A05290530052F052E052D -05340533053205310538053705360535053C053B053A05390540053F053E053D05440543054205410548054705460545 -FFFF054B054A0549FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF054F054E054D054C0553055205510550 -0557055605550554055B055A05590558055F055E055D055C05630562056105600567056605650564056B056A05690568 -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF056F056E056D056C0573057205710570 -0577057605750574057B057A05790578057F057E057D057C05830582058105800587058605850584058B058A05890588 -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF058D058CFFFFFFFF05910590058F058E05950594059305920599059805970596 -059D059C059B059A05A105A0059F059E05A505A405A305A205A905A805A705A605AD05AC05AB05AAFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF", -); -immutable(uint)[] toUpperTable() nothrow @nogc pure @safe { -static immutable uint[] t = -x" -0000004100000042000000430000004400000045000000460000004700000048000000490000004A0000004B0000004C -0000004D0000004E0000004F000000500000005100000052000000530000005400000055000000560000005700000058 -000000590000005A0000039C000000C0000000C1000000C2000000C3000000C4000000C5000000C6000000C7000000C8 -000000C9000000CA000000CB000000CC000000CD000000CE000000CF000000D0000000D1000000D2000000D3000000D4 -000000D5000000D6000000D8000000D9000000DA000000DB000000DC000000DD000000DE000001780000010000000102 -0000010400000106000001080000010A0000010C0000010E00000110000001120000011400000116000001180000011A -0000011C0000011E00000120000001220000012400000126000001280000012A0000012C0000012E0000004900000132 -0000013400000136000001390000013B0000013D0000013F000001410000014300000145000001470000014A0000014C -0000014E00000150000001520000015400000156000001580000015A0000015C0000015E000001600000016200000164 -00000166000001680000016A0000016C0000016E00000170000001720000017400000176000001790000017B0000017D -00000053000002430000018200000184000001870000018B00000191000001F6000001980000023D00000220000001A0 -000001A2000001A4000001A7000001AC000001AF000001B3000001B5000001B8000001BC000001F7000001C4000001C4 -000001C7000001C7000001CA000001CA000001CD000001CF000001D1000001D3000001D5000001D7000001D9000001DB -0000018E000001DE000001E0000001E2000001E4000001E6000001E8000001EA000001EC000001EE000001F1000001F1 -000001F4000001F8000001FA000001FC000001FE00000200000002020000020400000206000002080000020A0000020C -0000020E00000210000002120000021400000216000002180000021A0000021C0000021E000002220000022400000226 -000002280000022A0000022C0000022E00000230000002320000023B00002C7E00002C7F000002410000024600000248 -0000024A0000024C0000024E00002C6F00002C6D00002C700000018100000186000001890000018A0000018F00000190 -0000A7AB000001930000A7AC000001940000A78D0000A7AA00000197000001960000A7AE00002C620000A7AD0000019C -00002C6E0000019D0000019F00002C64000001A60000A7C5000001A90000A7B1000001AE00000244000001B1000001B2 -00000245000001B70000A7B20000A7B000000399000003700000037200000376000003FD000003FE000003FF00000386 -00000388000003890000038A000003910000039200000393000003940000039500000396000003970000039800000399 -0000039A0000039B0000039C0000039D0000039E0000039F000003A0000003A1000003A3000003A3000003A4000003A5 -000003A6000003A7000003A8000003A9000003AA000003AB0000038C0000038E0000038F0000039200000398000003A6 -000003A0000003CF000003D8000003DA000003DC000003DE000003E0000003E2000003E4000003E6000003E8000003EA -000003EC000003EE0000039A000003A1000003F90000037F00000395000003F7000003FA000004100000041100000412 -000004130000041400000415000004160000041700000418000004190000041A0000041B0000041C0000041D0000041E -0000041F000004200000042100000422000004230000042400000425000004260000042700000428000004290000042A -0000042B0000042C0000042D0000042E0000042F00000400000004010000040200000403000004040000040500000406 -0000040700000408000004090000040A0000040B0000040C0000040D0000040E0000040F000004600000046200000464 -00000466000004680000046A0000046C0000046E00000470000004720000047400000476000004780000047A0000047C -0000047E000004800000048A0000048C0000048E00000490000004920000049400000496000004980000049A0000049C -0000049E000004A0000004A2000004A4000004A6000004A8000004AA000004AC000004AE000004B0000004B2000004B4 -000004B6000004B8000004BA000004BC000004BE000004C1000004C3000004C5000004C7000004C9000004CB000004CD -000004C0000004D0000004D2000004D4000004D6000004D8000004DA000004DC000004DE000004E0000004E2000004E4 -000004E6000004E8000004EA000004EC000004EE000004F0000004F2000004F4000004F6000004F8000004FA000004FC -000004FE00000500000005020000050400000506000005080000050A0000050C0000050E000005100000051200000514 -00000516000005180000051A0000051C0000051E00000520000005220000052400000526000005280000052A0000052C -0000052E0000053100000532000005330000053400000535000005360000053700000538000005390000053A0000053B -0000053C0000053D0000053E0000053F0000054000000541000005420000054300000544000005450000054600000547 -00000548000005490000054A0000054B0000054C0000054D0000054E0000054F00000550000005510000055200000553 -00000554000005550000055600001C9000001C9100001C9200001C9300001C9400001C9500001C9600001C9700001C98 -00001C9900001C9A00001C9B00001C9C00001C9D00001C9E00001C9F00001CA000001CA100001CA200001CA300001CA4 -00001CA500001CA600001CA700001CA800001CA900001CAA00001CAB00001CAC00001CAD00001CAE00001CAF00001CB0 -00001CB100001CB200001CB300001CB400001CB500001CB600001CB700001CB800001CB900001CBA00001CBD00001CBE -00001CBF000013F0000013F1000013F2000013F3000013F4000013F500000412000004140000041E0000042100000422 -000004220000042A000004620000A64A0000A77D00002C630000A7C600001E0000001E0200001E0400001E0600001E08 -00001E0A00001E0C00001E0E00001E1000001E1200001E1400001E1600001E1800001E1A00001E1C00001E1E00001E20 -00001E2200001E2400001E2600001E2800001E2A00001E2C00001E2E00001E3000001E3200001E3400001E3600001E38 -00001E3A00001E3C00001E3E00001E4000001E4200001E4400001E4600001E4800001E4A00001E4C00001E4E00001E50 -00001E5200001E5400001E5600001E5800001E5A00001E5C00001E5E00001E6000001E6200001E6400001E6600001E68 -00001E6A00001E6C00001E6E00001E7000001E7200001E7400001E7600001E7800001E7A00001E7C00001E7E00001E80 -00001E8200001E8400001E8600001E8800001E8A00001E8C00001E8E00001E9000001E9200001E9400001E6000001EA0 -00001EA200001EA400001EA600001EA800001EAA00001EAC00001EAE00001EB000001EB200001EB400001EB600001EB8 -00001EBA00001EBC00001EBE00001EC000001EC200001EC400001EC600001EC800001ECA00001ECC00001ECE00001ED0 -00001ED200001ED400001ED600001ED800001EDA00001EDC00001EDE00001EE000001EE200001EE400001EE600001EE8 -00001EEA00001EEC00001EEE00001EF000001EF200001EF400001EF600001EF800001EFA00001EFC00001EFE00001F08 -00001F0900001F0A00001F0B00001F0C00001F0D00001F0E00001F0F00001F1800001F1900001F1A00001F1B00001F1C -00001F1D00001F2800001F2900001F2A00001F2B00001F2C00001F2D00001F2E00001F2F00001F3800001F3900001F3A -00001F3B00001F3C00001F3D00001F3E00001F3F00001F4800001F4900001F4A00001F4B00001F4C00001F4D00001F59 -00001F5B00001F5D00001F5F00001F6800001F6900001F6A00001F6B00001F6C00001F6D00001F6E00001F6F00001FBA -00001FBB00001FC800001FC900001FCA00001FCB00001FDA00001FDB00001FF800001FF900001FEA00001FEB00001FFA -00001FFB00001F8800001F8900001F8A00001F8B00001F8C00001F8D00001F8E00001F8F00001F9800001F9900001F9A -00001F9B00001F9C00001F9D00001F9E00001F9F00001FA800001FA900001FAA00001FAB00001FAC00001FAD00001FAE -00001FAF00001FB800001FB900001FBC0000039900001FCC00001FD800001FD900001FE800001FE900001FEC00001FFC -00002132000021600000216100002162000021630000216400002165000021660000216700002168000021690000216A -0000216B0000216C0000216D0000216E0000216F00002183000024B6000024B7000024B8000024B9000024BA000024BB -000024BC000024BD000024BE000024BF000024C0000024C1000024C2000024C3000024C4000024C5000024C6000024C7 -000024C8000024C9000024CA000024CB000024CC000024CD000024CE000024CF00002C0000002C0100002C0200002C03 -00002C0400002C0500002C0600002C0700002C0800002C0900002C0A00002C0B00002C0C00002C0D00002C0E00002C0F -00002C1000002C1100002C1200002C1300002C1400002C1500002C1600002C1700002C1800002C1900002C1A00002C1B -00002C1C00002C1D00002C1E00002C1F00002C2000002C2100002C2200002C2300002C2400002C2500002C2600002C27 -00002C2800002C2900002C2A00002C2B00002C2C00002C2D00002C2E00002C2F00002C600000023A0000023E00002C67 -00002C6900002C6B00002C7200002C7500002C8000002C8200002C8400002C8600002C8800002C8A00002C8C00002C8E -00002C9000002C9200002C9400002C9600002C9800002C9A00002C9C00002C9E00002CA000002CA200002CA400002CA6 -00002CA800002CAA00002CAC00002CAE00002CB000002CB200002CB400002CB600002CB800002CBA00002CBC00002CBE -00002CC000002CC200002CC400002CC600002CC800002CCA00002CCC00002CCE00002CD000002CD200002CD400002CD6 -00002CD800002CDA00002CDC00002CDE00002CE000002CE200002CEB00002CED00002CF2000010A0000010A1000010A2 -000010A3000010A4000010A5000010A6000010A7000010A8000010A9000010AA000010AB000010AC000010AD000010AE -000010AF000010B0000010B1000010B2000010B3000010B4000010B5000010B6000010B7000010B8000010B9000010BA -000010BB000010BC000010BD000010BE000010BF000010C0000010C1000010C2000010C3000010C4000010C5000010C7 -000010CD0000A6400000A6420000A6440000A6460000A6480000A64A0000A64C0000A64E0000A6500000A6520000A654 -0000A6560000A6580000A65A0000A65C0000A65E0000A6600000A6620000A6640000A6660000A6680000A66A0000A66C -0000A6800000A6820000A6840000A6860000A6880000A68A0000A68C0000A68E0000A6900000A6920000A6940000A696 -0000A6980000A69A0000A7220000A7240000A7260000A7280000A72A0000A72C0000A72E0000A7320000A7340000A736 -0000A7380000A73A0000A73C0000A73E0000A7400000A7420000A7440000A7460000A7480000A74A0000A74C0000A74E -0000A7500000A7520000A7540000A7560000A7580000A75A0000A75C0000A75E0000A7600000A7620000A7640000A766 -0000A7680000A76A0000A76C0000A76E0000A7790000A77B0000A77E0000A7800000A7820000A7840000A7860000A78B -0000A7900000A7920000A7C40000A7960000A7980000A79A0000A79C0000A79E0000A7A00000A7A20000A7A40000A7A6 -0000A7A80000A7B40000A7B60000A7B80000A7BA0000A7BC0000A7BE0000A7C00000A7C20000A7C70000A7C90000A7D0 -0000A7D60000A7D80000A7F50000A7B3000013A0000013A1000013A2000013A3000013A4000013A5000013A6000013A7 -000013A8000013A9000013AA000013AB000013AC000013AD000013AE000013AF000013B0000013B1000013B2000013B3 -000013B4000013B5000013B6000013B7000013B8000013B9000013BA000013BB000013BC000013BD000013BE000013BF -000013C0000013C1000013C2000013C3000013C4000013C5000013C6000013C7000013C8000013C9000013CA000013CB -000013CC000013CD000013CE000013CF000013D0000013D1000013D2000013D3000013D4000013D5000013D6000013D7 -000013D8000013D9000013DA000013DB000013DC000013DD000013DE000013DF000013E0000013E1000013E2000013E3 -000013E4000013E5000013E6000013E7000013E8000013E9000013EA000013EB000013EC000013ED000013EE000013EF -0000FF210000FF220000FF230000FF240000FF250000FF260000FF270000FF280000FF290000FF2A0000FF2B0000FF2C -0000FF2D0000FF2E0000FF2F0000FF300000FF310000FF320000FF330000FF340000FF350000FF360000FF370000FF38 -0000FF390000FF3A00010400000104010001040200010403000104040001040500010406000104070001040800010409 -0001040A0001040B0001040C0001040D0001040E0001040F000104100001041100010412000104130001041400010415 -000104160001041700010418000104190001041A0001041B0001041C0001041D0001041E0001041F0001042000010421 -000104220001042300010424000104250001042600010427000104B0000104B1000104B2000104B3000104B4000104B5 -000104B6000104B7000104B8000104B9000104BA000104BB000104BC000104BD000104BE000104BF000104C0000104C1 -000104C2000104C3000104C4000104C5000104C6000104C7000104C8000104C9000104CA000104CB000104CC000104CD -000104CE000104CF000104D0000104D1000104D2000104D3000105700001057100010572000105730001057400010575 -000105760001057700010578000105790001057A0001057C0001057D0001057E0001057F000105800001058100010582 -000105830001058400010585000105860001058700010588000105890001058A0001058C0001058D0001058E0001058F -000105900001059100010592000105940001059500010C8000010C8100010C8200010C8300010C8400010C8500010C86 -00010C8700010C8800010C8900010C8A00010C8B00010C8C00010C8D00010C8E00010C8F00010C9000010C9100010C92 -00010C9300010C9400010C9500010C9600010C9700010C9800010C9900010C9A00010C9B00010C9C00010C9D00010C9E -00010C9F00010CA000010CA100010CA200010CA300010CA400010CA500010CA600010CA700010CA800010CA900010CAA -00010CAB00010CAC00010CAD00010CAE00010CAF00010CB000010CB100010CB2000118A0000118A1000118A2000118A3 -000118A4000118A5000118A6000118A7000118A8000118A9000118AA000118AB000118AC000118AD000118AE000118AF -000118B0000118B1000118B2000118B3000118B4000118B5000118B6000118B7000118B8000118B9000118BA000118BB -000118BC000118BD000118BE000118BF00016E4000016E4100016E4200016E4300016E4400016E4500016E4600016E47 -00016E4800016E4900016E4A00016E4B00016E4C00016E4D00016E4E00016E4F00016E5000016E5100016E5200016E53 -00016E5400016E5500016E5600016E5700016E5800016E5900016E5A00016E5B00016E5C00016E5D00016E5E00016E5F -0001E9000001E9010001E9020001E9030001E9040001E9050001E9060001E9070001E9080001E9090001E90A0001E90B -0001E90C0001E90D0001E90E0001E90F0001E9100001E9110001E9120001E9130001E9140001E9150001E9160001E917 -0001E9180001E9190001E91A0001E91B0001E91C0001E91D0001E91E0001E91F0001E9200001E9210200005300000053 -0000013002000046000000460200004600000049020000460000004C0300004600000046000000490300004600000046 -0000004C0200005300000054020000530000005402000535000005520200054400000546020005440000053502000544 -0000053B0200054E00000546020005440000053D020002BC0000004E030003990000030800000301030003A500000308 -000003010200004A0000030C02000048000003310200005400000308020000570000030A020000590000030A02000041 -000002BE020003A500000313030003A50000031300000300030003A50000031300000301030003A50000031300000342 -020003910000034202000397000003420300039900000308000003000300039900000308000003010200039900000342 -030003990000030800000342030003A50000030800000300030003A50000030800000301020003A100000313020003A5 -00000342030003A50000030800000342020003A90000034202001F080000039902001F090000039902001F0A00000399 -02001F0B0000039902001F0C0000039902001F0D0000039902001F0E0000039902001F0F0000039902001F0800000399 -02001F090000039902001F0A0000039902001F0B0000039902001F0C0000039902001F0D0000039902001F0E00000399 -02001F0F0000039902001F280000039902001F290000039902001F2A0000039902001F2B0000039902001F2C00000399 -02001F2D0000039902001F2E0000039902001F2F0000039902001F280000039902001F290000039902001F2A00000399 -02001F2B0000039902001F2C0000039902001F2D0000039902001F2E0000039902001F2F0000039902001F6800000399 -02001F690000039902001F6A0000039902001F6B0000039902001F6C0000039902001F6D0000039902001F6E00000399 -02001F6F0000039902001F680000039902001F690000039902001F6A0000039902001F6B0000039902001F6C00000399 -02001F6D0000039902001F6E0000039902001F6F00000399020003910000039902000391000003990200039700000399 -0200039700000399020003A900000399020003A90000039902001FBA00000399020003860000039902001FCA00000399 -020003890000039902001FFA000003990200038F00000399030003910000034200000399030003970000034200000399 -030003A90000034200000399"; -return t; -} -immutable(uint)[] toLowerTable() nothrow @nogc pure @safe { -static immutable uint[] t = -x" -0000006100000062000000630000006400000065000000660000006700000068000000690000006A0000006B0000006C -0000006D0000006E0000006F000000700000007100000072000000730000007400000075000000760000007700000078 -000000790000007A000000E0000000E1000000E2000000E3000000E4000000E5000000E6000000E7000000E8000000E9 -000000EA000000EB000000EC000000ED000000EE000000EF000000F0000000F1000000F2000000F3000000F4000000F5 -000000F6000000F8000000F9000000FA000000FB000000FC000000FD000000FE00000101000001030000010500000107 -000001090000010B0000010D0000010F00000111000001130000011500000117000001190000011B0000011D0000011F -00000121000001230000012500000127000001290000012B0000012D0000012F00000069000001330000013500000137 -0000013A0000013C0000013E00000140000001420000014400000146000001480000014B0000014D0000014F00000151 -000001530000015500000157000001590000015B0000015D0000015F0000016100000163000001650000016700000169 -0000016B0000016D0000016F00000171000001730000017500000177000000FF0000017A0000017C0000017E00000253 -0000018300000185000002540000018800000256000002570000018C000001DD000002590000025B0000019200000260 -000002630000026900000268000001990000026F0000027200000275000001A1000001A3000001A500000280000001A8 -00000283000001AD00000288000001B00000028A0000028B000001B4000001B600000292000001B9000001BD000001C6 -000001C6000001C9000001C9000001CC000001CC000001CE000001D0000001D2000001D4000001D6000001D8000001DA -000001DC000001DF000001E1000001E3000001E5000001E7000001E9000001EB000001ED000001EF000001F3000001F3 -000001F500000195000001BF000001F9000001FB000001FD000001FF0000020100000203000002050000020700000209 -0000020B0000020D0000020F00000211000002130000021500000217000002190000021B0000021D0000021F0000019E -000002230000022500000227000002290000022B0000022D0000022F000002310000023300002C650000023C0000019A -00002C660000024200000180000002890000028C00000247000002490000024B0000024D0000024F0000037100000373 -00000377000003F3000003AC000003AD000003AE000003AF000003CC000003CD000003CE000003B1000003B2000003B3 -000003B4000003B5000003B6000003B7000003B8000003B9000003BA000003BB000003BC000003BD000003BE000003BF -000003C0000003C1000003C3000003C4000003C5000003C6000003C7000003C8000003C9000003CA000003CB000003D7 -000003D9000003DB000003DD000003DF000003E1000003E3000003E5000003E7000003E9000003EB000003ED000003EF -000003B8000003F8000003F2000003FB0000037B0000037C0000037D0000045000000451000004520000045300000454 -00000455000004560000045700000458000004590000045A0000045B0000045C0000045D0000045E0000045F00000430 -0000043100000432000004330000043400000435000004360000043700000438000004390000043A0000043B0000043C -0000043D0000043E0000043F000004400000044100000442000004430000044400000445000004460000044700000448 -000004490000044A0000044B0000044C0000044D0000044E0000044F0000046100000463000004650000046700000469 -0000046B0000046D0000046F00000471000004730000047500000477000004790000047B0000047D0000047F00000481 -0000048B0000048D0000048F00000491000004930000049500000497000004990000049B0000049D0000049F000004A1 -000004A3000004A5000004A7000004A9000004AB000004AD000004AF000004B1000004B3000004B5000004B7000004B9 -000004BB000004BD000004BF000004CF000004C2000004C4000004C6000004C8000004CA000004CC000004CE000004D1 -000004D3000004D5000004D7000004D9000004DB000004DD000004DF000004E1000004E3000004E5000004E7000004E9 -000004EB000004ED000004EF000004F1000004F3000004F5000004F7000004F9000004FB000004FD000004FF00000501 -000005030000050500000507000005090000050B0000050D0000050F0000051100000513000005150000051700000519 -0000051B0000051D0000051F00000521000005230000052500000527000005290000052B0000052D0000052F00000561 -00000562000005630000056400000565000005660000056700000568000005690000056A0000056B0000056C0000056D -0000056E0000056F00000570000005710000057200000573000005740000057500000576000005770000057800000579 -0000057A0000057B0000057C0000057D0000057E0000057F000005800000058100000582000005830000058400000585 -0000058600002D0000002D0100002D0200002D0300002D0400002D0500002D0600002D0700002D0800002D0900002D0A -00002D0B00002D0C00002D0D00002D0E00002D0F00002D1000002D1100002D1200002D1300002D1400002D1500002D16 -00002D1700002D1800002D1900002D1A00002D1B00002D1C00002D1D00002D1E00002D1F00002D2000002D2100002D22 -00002D2300002D2400002D2500002D2700002D2D0000AB700000AB710000AB720000AB730000AB740000AB750000AB76 -0000AB770000AB780000AB790000AB7A0000AB7B0000AB7C0000AB7D0000AB7E0000AB7F0000AB800000AB810000AB82 -0000AB830000AB840000AB850000AB860000AB870000AB880000AB890000AB8A0000AB8B0000AB8C0000AB8D0000AB8E -0000AB8F0000AB900000AB910000AB920000AB930000AB940000AB950000AB960000AB970000AB980000AB990000AB9A -0000AB9B0000AB9C0000AB9D0000AB9E0000AB9F0000ABA00000ABA10000ABA20000ABA30000ABA40000ABA50000ABA6 -0000ABA70000ABA80000ABA90000ABAA0000ABAB0000ABAC0000ABAD0000ABAE0000ABAF0000ABB00000ABB10000ABB2 -0000ABB30000ABB40000ABB50000ABB60000ABB70000ABB80000ABB90000ABBA0000ABBB0000ABBC0000ABBD0000ABBE -0000ABBF000013F8000013F9000013FA000013FB000013FC000013FD000010D0000010D1000010D2000010D3000010D4 -000010D5000010D6000010D7000010D8000010D9000010DA000010DB000010DC000010DD000010DE000010DF000010E0 -000010E1000010E2000010E3000010E4000010E5000010E6000010E7000010E8000010E9000010EA000010EB000010EC -000010ED000010EE000010EF000010F0000010F1000010F2000010F3000010F4000010F5000010F6000010F7000010F8 -000010F9000010FA000010FD000010FE000010FF00001E0100001E0300001E0500001E0700001E0900001E0B00001E0D -00001E0F00001E1100001E1300001E1500001E1700001E1900001E1B00001E1D00001E1F00001E2100001E2300001E25 -00001E2700001E2900001E2B00001E2D00001E2F00001E3100001E3300001E3500001E3700001E3900001E3B00001E3D -00001E3F00001E4100001E4300001E4500001E4700001E4900001E4B00001E4D00001E4F00001E5100001E5300001E55 -00001E5700001E5900001E5B00001E5D00001E5F00001E6100001E6300001E6500001E6700001E6900001E6B00001E6D -00001E6F00001E7100001E7300001E7500001E7700001E7900001E7B00001E7D00001E7F00001E8100001E8300001E85 -00001E8700001E8900001E8B00001E8D00001E8F00001E9100001E9300001E95000000DF00001EA100001EA300001EA5 -00001EA700001EA900001EAB00001EAD00001EAF00001EB100001EB300001EB500001EB700001EB900001EBB00001EBD -00001EBF00001EC100001EC300001EC500001EC700001EC900001ECB00001ECD00001ECF00001ED100001ED300001ED5 -00001ED700001ED900001EDB00001EDD00001EDF00001EE100001EE300001EE500001EE700001EE900001EEB00001EED -00001EEF00001EF100001EF300001EF500001EF700001EF900001EFB00001EFD00001EFF00001F0000001F0100001F02 -00001F0300001F0400001F0500001F0600001F0700001F1000001F1100001F1200001F1300001F1400001F1500001F20 -00001F2100001F2200001F2300001F2400001F2500001F2600001F2700001F3000001F3100001F3200001F3300001F34 -00001F3500001F3600001F3700001F4000001F4100001F4200001F4300001F4400001F4500001F5100001F5300001F55 -00001F5700001F6000001F6100001F6200001F6300001F6400001F6500001F6600001F6700001F8000001F8100001F82 -00001F8300001F8400001F8500001F8600001F8700001F9000001F9100001F9200001F9300001F9400001F9500001F96 -00001F9700001FA000001FA100001FA200001FA300001FA400001FA500001FA600001FA700001FB000001FB100001F70 -00001F7100001FB300001F7200001F7300001F7400001F7500001FC300001FD000001FD100001F7600001F7700001FE0 -00001FE100001F7A00001F7B00001FE500001F7800001F7900001F7C00001F7D00001FF3000003C90000006B000000E5 -0000214E000021700000217100002172000021730000217400002175000021760000217700002178000021790000217A -0000217B0000217C0000217D0000217E0000217F00002184000024D0000024D1000024D2000024D3000024D4000024D5 -000024D6000024D7000024D8000024D9000024DA000024DB000024DC000024DD000024DE000024DF000024E0000024E1 -000024E2000024E3000024E4000024E5000024E6000024E7000024E8000024E900002C3000002C3100002C3200002C33 -00002C3400002C3500002C3600002C3700002C3800002C3900002C3A00002C3B00002C3C00002C3D00002C3E00002C3F -00002C4000002C4100002C4200002C4300002C4400002C4500002C4600002C4700002C4800002C4900002C4A00002C4B -00002C4C00002C4D00002C4E00002C4F00002C5000002C5100002C5200002C5300002C5400002C5500002C5600002C57 -00002C5800002C5900002C5A00002C5B00002C5C00002C5D00002C5E00002C5F00002C610000026B00001D7D0000027D -00002C6800002C6A00002C6C0000025100000271000002500000025200002C7300002C760000023F0000024000002C81 -00002C8300002C8500002C8700002C8900002C8B00002C8D00002C8F00002C9100002C9300002C9500002C9700002C99 -00002C9B00002C9D00002C9F00002CA100002CA300002CA500002CA700002CA900002CAB00002CAD00002CAF00002CB1 -00002CB300002CB500002CB700002CB900002CBB00002CBD00002CBF00002CC100002CC300002CC500002CC700002CC9 -00002CCB00002CCD00002CCF00002CD100002CD300002CD500002CD700002CD900002CDB00002CDD00002CDF00002CE1 -00002CE300002CEC00002CEE00002CF30000A6410000A6430000A6450000A6470000A6490000A64B0000A64D0000A64F -0000A6510000A6530000A6550000A6570000A6590000A65B0000A65D0000A65F0000A6610000A6630000A6650000A667 -0000A6690000A66B0000A66D0000A6810000A6830000A6850000A6870000A6890000A68B0000A68D0000A68F0000A691 -0000A6930000A6950000A6970000A6990000A69B0000A7230000A7250000A7270000A7290000A72B0000A72D0000A72F -0000A7330000A7350000A7370000A7390000A73B0000A73D0000A73F0000A7410000A7430000A7450000A7470000A749 -0000A74B0000A74D0000A74F0000A7510000A7530000A7550000A7570000A7590000A75B0000A75D0000A75F0000A761 -0000A7630000A7650000A7670000A7690000A76B0000A76D0000A76F0000A77A0000A77C00001D790000A77F0000A781 -0000A7830000A7850000A7870000A78C000002650000A7910000A7930000A7970000A7990000A79B0000A79D0000A79F -0000A7A10000A7A30000A7A50000A7A70000A7A9000002660000025C000002610000026C0000026A0000029E00000287 -0000029D0000AB530000A7B50000A7B70000A7B90000A7BB0000A7BD0000A7BF0000A7C10000A7C30000A79400000282 -00001D8E0000A7C80000A7CA0000A7D10000A7D70000A7D90000A7F60000FF410000FF420000FF430000FF440000FF45 -0000FF460000FF470000FF480000FF490000FF4A0000FF4B0000FF4C0000FF4D0000FF4E0000FF4F0000FF500000FF51 -0000FF520000FF530000FF540000FF550000FF560000FF570000FF580000FF590000FF5A00010428000104290001042A -0001042B0001042C0001042D0001042E0001042F00010430000104310001043200010433000104340001043500010436 -0001043700010438000104390001043A0001043B0001043C0001043D0001043E0001043F000104400001044100010442 -000104430001044400010445000104460001044700010448000104490001044A0001044B0001044C0001044D0001044E -0001044F000104D8000104D9000104DA000104DB000104DC000104DD000104DE000104DF000104E0000104E1000104E2 -000104E3000104E4000104E5000104E6000104E7000104E8000104E9000104EA000104EB000104EC000104ED000104EE -000104EF000104F0000104F1000104F2000104F3000104F4000104F5000104F6000104F7000104F8000104F9000104FA -000104FB0001059700010598000105990001059A0001059B0001059C0001059D0001059E0001059F000105A0000105A1 -000105A3000105A4000105A5000105A6000105A7000105A8000105A9000105AA000105AB000105AC000105AD000105AE -000105AF000105B0000105B1000105B3000105B4000105B5000105B6000105B7000105B8000105B9000105BB000105BC -00010CC000010CC100010CC200010CC300010CC400010CC500010CC600010CC700010CC800010CC900010CCA00010CCB -00010CCC00010CCD00010CCE00010CCF00010CD000010CD100010CD200010CD300010CD400010CD500010CD600010CD7 -00010CD800010CD900010CDA00010CDB00010CDC00010CDD00010CDE00010CDF00010CE000010CE100010CE200010CE3 -00010CE400010CE500010CE600010CE700010CE800010CE900010CEA00010CEB00010CEC00010CED00010CEE00010CEF -00010CF000010CF100010CF2000118C0000118C1000118C2000118C3000118C4000118C5000118C6000118C7000118C8 -000118C9000118CA000118CB000118CC000118CD000118CE000118CF000118D0000118D1000118D2000118D3000118D4 -000118D5000118D6000118D7000118D8000118D9000118DA000118DB000118DC000118DD000118DE000118DF00016E60 -00016E6100016E6200016E6300016E6400016E6500016E6600016E6700016E6800016E6900016E6A00016E6B00016E6C -00016E6D00016E6E00016E6F00016E7000016E7100016E7200016E7300016E7400016E7500016E7600016E7700016E78 -00016E7900016E7A00016E7B00016E7C00016E7D00016E7E00016E7F0001E9220001E9230001E9240001E9250001E926 -0001E9270001E9280001E9290001E92A0001E92B0001E92C0001E92D0001E92E0001E92F0001E9300001E9310001E932 -0001E9330001E9340001E9350001E9360001E9370001E9380001E9390001E93A0001E93B0001E93C0001E93D0001E93E -0001E93F0001E9400001E9410001E9420001E943000000DF02000069000003070000FB000000FB010000FB020000FB03 -0000FB040000FB050000FB06000005870000FB130000FB140000FB150000FB160000FB170000014900000390000003B0 -000001F000001E9600001E9700001E9800001E9900001E9A00001F5000001F5200001F5400001F5600001FB600001FC6 -00001FD200001FD300001FD600001FD700001FE200001FE300001FE400001FE600001FE700001FF600001F8000001F81 -00001F8200001F8300001F8400001F8500001F8600001F8700001F8000001F8100001F8200001F8300001F8400001F85 -00001F8600001F8700001F9000001F9100001F9200001F9300001F9400001F9500001F9600001F9700001F9000001F91 -00001F9200001F9300001F9400001F9500001F9600001F9700001FA000001FA100001FA200001FA300001FA400001FA5 -00001FA600001FA700001FA000001FA100001FA200001FA300001FA400001FA500001FA600001FA700001FB300001FB3 -00001FC300001FC300001FF300001FF300001FB200001FB400001FC200001FC400001FF200001FF400001FB700001FC7 -00001FF7"; -return t; -} -immutable(uint)[] toTitleTable() nothrow @nogc pure @safe { -static immutable uint[] t = -x" -0000004100000042000000430000004400000045000000460000004700000048000000490000004A0000004B0000004C -0000004D0000004E0000004F000000500000005100000052000000530000005400000055000000560000005700000058 -000000590000005A0000039C000000C0000000C1000000C2000000C3000000C4000000C5000000C6000000C7000000C8 -000000C9000000CA000000CB000000CC000000CD000000CE000000CF000000D0000000D1000000D2000000D3000000D4 -000000D5000000D6000000D8000000D9000000DA000000DB000000DC000000DD000000DE000001780000010000000102 -0000010400000106000001080000010A0000010C0000010E00000110000001120000011400000116000001180000011A -0000011C0000011E00000120000001220000012400000126000001280000012A0000012C0000012E0000004900000132 -0000013400000136000001390000013B0000013D0000013F000001410000014300000145000001470000014A0000014C -0000014E00000150000001520000015400000156000001580000015A0000015C0000015E000001600000016200000164 -00000166000001680000016A0000016C0000016E00000170000001720000017400000176000001790000017B0000017D -00000053000002430000018200000184000001870000018B00000191000001F6000001980000023D00000220000001A0 -000001A2000001A4000001A7000001AC000001AF000001B3000001B5000001B8000001BC000001F7000001C5000001C5 -000001C5000001C8000001C8000001C8000001CB000001CB000001CB000001CD000001CF000001D1000001D3000001D5 -000001D7000001D9000001DB0000018E000001DE000001E0000001E2000001E4000001E6000001E8000001EA000001EC -000001EE000001F2000001F2000001F2000001F4000001F8000001FA000001FC000001FE000002000000020200000204 -00000206000002080000020A0000020C0000020E00000210000002120000021400000216000002180000021A0000021C -0000021E000002220000022400000226000002280000022A0000022C0000022E00000230000002320000023B00002C7E -00002C7F0000024100000246000002480000024A0000024C0000024E00002C6F00002C6D00002C700000018100000186 -000001890000018A0000018F000001900000A7AB000001930000A7AC000001940000A78D0000A7AA0000019700000196 -0000A7AE00002C620000A7AD0000019C00002C6E0000019D0000019F00002C64000001A60000A7C5000001A90000A7B1 -000001AE00000244000001B1000001B200000245000001B70000A7B20000A7B000000399000003700000037200000376 -000003FD000003FE000003FF0000038600000388000003890000038A0000039100000392000003930000039400000395 -000003960000039700000398000003990000039A0000039B0000039C0000039D0000039E0000039F000003A0000003A1 -000003A3000003A3000003A4000003A5000003A6000003A7000003A8000003A9000003AA000003AB0000038C0000038E -0000038F0000039200000398000003A6000003A0000003CF000003D8000003DA000003DC000003DE000003E0000003E2 -000003E4000003E6000003E8000003EA000003EC000003EE0000039A000003A1000003F90000037F00000395000003F7 -000003FA000004100000041100000412000004130000041400000415000004160000041700000418000004190000041A -0000041B0000041C0000041D0000041E0000041F00000420000004210000042200000423000004240000042500000426 -0000042700000428000004290000042A0000042B0000042C0000042D0000042E0000042F000004000000040100000402 -000004030000040400000405000004060000040700000408000004090000040A0000040B0000040C0000040D0000040E -0000040F00000460000004620000046400000466000004680000046A0000046C0000046E000004700000047200000474 -00000476000004780000047A0000047C0000047E000004800000048A0000048C0000048E000004900000049200000494 -00000496000004980000049A0000049C0000049E000004A0000004A2000004A4000004A6000004A8000004AA000004AC -000004AE000004B0000004B2000004B4000004B6000004B8000004BA000004BC000004BE000004C1000004C3000004C5 -000004C7000004C9000004CB000004CD000004C0000004D0000004D2000004D4000004D6000004D8000004DA000004DC -000004DE000004E0000004E2000004E4000004E6000004E8000004EA000004EC000004EE000004F0000004F2000004F4 -000004F6000004F8000004FA000004FC000004FE00000500000005020000050400000506000005080000050A0000050C -0000050E00000510000005120000051400000516000005180000051A0000051C0000051E000005200000052200000524 -00000526000005280000052A0000052C0000052E00000531000005320000053300000534000005350000053600000537 -00000538000005390000053A0000053B0000053C0000053D0000053E0000053F00000540000005410000054200000543 -0000054400000545000005460000054700000548000005490000054A0000054B0000054C0000054D0000054E0000054F -00000550000005510000055200000553000005540000055500000556000010D0000010D1000010D2000010D3000010D4 -000010D5000010D6000010D7000010D8000010D9000010DA000010DB000010DC000010DD000010DE000010DF000010E0 -000010E1000010E2000010E3000010E4000010E5000010E6000010E7000010E8000010E9000010EA000010EB000010EC -000010ED000010EE000010EF000010F0000010F1000010F2000010F3000010F4000010F5000010F6000010F7000010F8 -000010F9000010FA000010FD000010FE000010FF000013F0000013F1000013F2000013F3000013F4000013F500000412 -000004140000041E0000042100000422000004220000042A000004620000A64A0000A77D00002C630000A7C600001E00 -00001E0200001E0400001E0600001E0800001E0A00001E0C00001E0E00001E1000001E1200001E1400001E1600001E18 -00001E1A00001E1C00001E1E00001E2000001E2200001E2400001E2600001E2800001E2A00001E2C00001E2E00001E30 -00001E3200001E3400001E3600001E3800001E3A00001E3C00001E3E00001E4000001E4200001E4400001E4600001E48 -00001E4A00001E4C00001E4E00001E5000001E5200001E5400001E5600001E5800001E5A00001E5C00001E5E00001E60 -00001E6200001E6400001E6600001E6800001E6A00001E6C00001E6E00001E7000001E7200001E7400001E7600001E78 -00001E7A00001E7C00001E7E00001E8000001E8200001E8400001E8600001E8800001E8A00001E8C00001E8E00001E90 -00001E9200001E9400001E6000001EA000001EA200001EA400001EA600001EA800001EAA00001EAC00001EAE00001EB0 -00001EB200001EB400001EB600001EB800001EBA00001EBC00001EBE00001EC000001EC200001EC400001EC600001EC8 -00001ECA00001ECC00001ECE00001ED000001ED200001ED400001ED600001ED800001EDA00001EDC00001EDE00001EE0 -00001EE200001EE400001EE600001EE800001EEA00001EEC00001EEE00001EF000001EF200001EF400001EF600001EF8 -00001EFA00001EFC00001EFE00001F0800001F0900001F0A00001F0B00001F0C00001F0D00001F0E00001F0F00001F18 -00001F1900001F1A00001F1B00001F1C00001F1D00001F2800001F2900001F2A00001F2B00001F2C00001F2D00001F2E -00001F2F00001F3800001F3900001F3A00001F3B00001F3C00001F3D00001F3E00001F3F00001F4800001F4900001F4A -00001F4B00001F4C00001F4D00001F5900001F5B00001F5D00001F5F00001F6800001F6900001F6A00001F6B00001F6C -00001F6D00001F6E00001F6F00001FBA00001FBB00001FC800001FC900001FCA00001FCB00001FDA00001FDB00001FF8 -00001FF900001FEA00001FEB00001FFA00001FFB00001F8800001F8900001F8A00001F8B00001F8C00001F8D00001F8E -00001F8F00001F9800001F9900001F9A00001F9B00001F9C00001F9D00001F9E00001F9F00001FA800001FA900001FAA -00001FAB00001FAC00001FAD00001FAE00001FAF00001FB800001FB900001FBC0000039900001FCC00001FD800001FD9 -00001FE800001FE900001FEC00001FFC0000213200002160000021610000216200002163000021640000216500002166 -0000216700002168000021690000216A0000216B0000216C0000216D0000216E0000216F00002183000024B6000024B7 -000024B8000024B9000024BA000024BB000024BC000024BD000024BE000024BF000024C0000024C1000024C2000024C3 -000024C4000024C5000024C6000024C7000024C8000024C9000024CA000024CB000024CC000024CD000024CE000024CF -00002C0000002C0100002C0200002C0300002C0400002C0500002C0600002C0700002C0800002C0900002C0A00002C0B -00002C0C00002C0D00002C0E00002C0F00002C1000002C1100002C1200002C1300002C1400002C1500002C1600002C17 -00002C1800002C1900002C1A00002C1B00002C1C00002C1D00002C1E00002C1F00002C2000002C2100002C2200002C23 -00002C2400002C2500002C2600002C2700002C2800002C2900002C2A00002C2B00002C2C00002C2D00002C2E00002C2F -00002C600000023A0000023E00002C6700002C6900002C6B00002C7200002C7500002C8000002C8200002C8400002C86 -00002C8800002C8A00002C8C00002C8E00002C9000002C9200002C9400002C9600002C9800002C9A00002C9C00002C9E -00002CA000002CA200002CA400002CA600002CA800002CAA00002CAC00002CAE00002CB000002CB200002CB400002CB6 -00002CB800002CBA00002CBC00002CBE00002CC000002CC200002CC400002CC600002CC800002CCA00002CCC00002CCE -00002CD000002CD200002CD400002CD600002CD800002CDA00002CDC00002CDE00002CE000002CE200002CEB00002CED -00002CF2000010A0000010A1000010A2000010A3000010A4000010A5000010A6000010A7000010A8000010A9000010AA -000010AB000010AC000010AD000010AE000010AF000010B0000010B1000010B2000010B3000010B4000010B5000010B6 -000010B7000010B8000010B9000010BA000010BB000010BC000010BD000010BE000010BF000010C0000010C1000010C2 -000010C3000010C4000010C5000010C7000010CD0000A6400000A6420000A6440000A6460000A6480000A64A0000A64C -0000A64E0000A6500000A6520000A6540000A6560000A6580000A65A0000A65C0000A65E0000A6600000A6620000A664 -0000A6660000A6680000A66A0000A66C0000A6800000A6820000A6840000A6860000A6880000A68A0000A68C0000A68E -0000A6900000A6920000A6940000A6960000A6980000A69A0000A7220000A7240000A7260000A7280000A72A0000A72C -0000A72E0000A7320000A7340000A7360000A7380000A73A0000A73C0000A73E0000A7400000A7420000A7440000A746 -0000A7480000A74A0000A74C0000A74E0000A7500000A7520000A7540000A7560000A7580000A75A0000A75C0000A75E -0000A7600000A7620000A7640000A7660000A7680000A76A0000A76C0000A76E0000A7790000A77B0000A77E0000A780 -0000A7820000A7840000A7860000A78B0000A7900000A7920000A7C40000A7960000A7980000A79A0000A79C0000A79E -0000A7A00000A7A20000A7A40000A7A60000A7A80000A7B40000A7B60000A7B80000A7BA0000A7BC0000A7BE0000A7C0 -0000A7C20000A7C70000A7C90000A7D00000A7D60000A7D80000A7F50000A7B3000013A0000013A1000013A2000013A3 -000013A4000013A5000013A6000013A7000013A8000013A9000013AA000013AB000013AC000013AD000013AE000013AF -000013B0000013B1000013B2000013B3000013B4000013B5000013B6000013B7000013B8000013B9000013BA000013BB -000013BC000013BD000013BE000013BF000013C0000013C1000013C2000013C3000013C4000013C5000013C6000013C7 -000013C8000013C9000013CA000013CB000013CC000013CD000013CE000013CF000013D0000013D1000013D2000013D3 -000013D4000013D5000013D6000013D7000013D8000013D9000013DA000013DB000013DC000013DD000013DE000013DF -000013E0000013E1000013E2000013E3000013E4000013E5000013E6000013E7000013E8000013E9000013EA000013EB -000013EC000013ED000013EE000013EF0000FF210000FF220000FF230000FF240000FF250000FF260000FF270000FF28 -0000FF290000FF2A0000FF2B0000FF2C0000FF2D0000FF2E0000FF2F0000FF300000FF310000FF320000FF330000FF34 -0000FF350000FF360000FF370000FF380000FF390000FF3A000104000001040100010402000104030001040400010405 -000104060001040700010408000104090001040A0001040B0001040C0001040D0001040E0001040F0001041000010411 -00010412000104130001041400010415000104160001041700010418000104190001041A0001041B0001041C0001041D -0001041E0001041F0001042000010421000104220001042300010424000104250001042600010427000104B0000104B1 -000104B2000104B3000104B4000104B5000104B6000104B7000104B8000104B9000104BA000104BB000104BC000104BD -000104BE000104BF000104C0000104C1000104C2000104C3000104C4000104C5000104C6000104C7000104C8000104C9 -000104CA000104CB000104CC000104CD000104CE000104CF000104D0000104D1000104D2000104D30001057000010571 -00010572000105730001057400010575000105760001057700010578000105790001057A0001057C0001057D0001057E -0001057F000105800001058100010582000105830001058400010585000105860001058700010588000105890001058A -0001058C0001058D0001058E0001058F000105900001059100010592000105940001059500010C8000010C8100010C82 -00010C8300010C8400010C8500010C8600010C8700010C8800010C8900010C8A00010C8B00010C8C00010C8D00010C8E -00010C8F00010C9000010C9100010C9200010C9300010C9400010C9500010C9600010C9700010C9800010C9900010C9A -00010C9B00010C9C00010C9D00010C9E00010C9F00010CA000010CA100010CA200010CA300010CA400010CA500010CA6 -00010CA700010CA800010CA900010CAA00010CAB00010CAC00010CAD00010CAE00010CAF00010CB000010CB100010CB2 -000118A0000118A1000118A2000118A3000118A4000118A5000118A6000118A7000118A8000118A9000118AA000118AB -000118AC000118AD000118AE000118AF000118B0000118B1000118B2000118B3000118B4000118B5000118B6000118B7 -000118B8000118B9000118BA000118BB000118BC000118BD000118BE000118BF00016E4000016E4100016E4200016E43 -00016E4400016E4500016E4600016E4700016E4800016E4900016E4A00016E4B00016E4C00016E4D00016E4E00016E4F -00016E5000016E5100016E5200016E5300016E5400016E5500016E5600016E5700016E5800016E5900016E5A00016E5B -00016E5C00016E5D00016E5E00016E5F0001E9000001E9010001E9020001E9030001E9040001E9050001E9060001E907 -0001E9080001E9090001E90A0001E90B0001E90C0001E90D0001E90E0001E90F0001E9100001E9110001E9120001E913 -0001E9140001E9150001E9160001E9170001E9180001E9190001E91A0001E91B0001E91C0001E91D0001E91E0001E91F -0001E9200001E92102000053000000730000013002000046000000660200004600000069020000460000006C03000046 -000000660000006903000046000000660000006C02000053000000740200005300000074020005350000058202000544 -000005760200054400000565020005440000056B0200054E00000576020005440000056D020002BC0000004E03000399 -0000030800000301030003A500000308000003010200004A0000030C0200004800000331020000540000030802000057 -0000030A020000590000030A02000041000002BE020003A500000313030003A50000031300000300030003A500000313 -00000301030003A500000313000003420200039100000342020003970000034203000399000003080000030003000399 -00000308000003010200039900000342030003990000030800000342030003A50000030800000300030003A500000308 -00000301020003A100000313020003A500000342030003A50000030800000342020003A90000034200001F8800001F89 -00001F8A00001F8B00001F8C00001F8D00001F8E00001F8F00001F8800001F8900001F8A00001F8B00001F8C00001F8D -00001F8E00001F8F00001F9800001F9900001F9A00001F9B00001F9C00001F9D00001F9E00001F9F00001F9800001F99 -00001F9A00001F9B00001F9C00001F9D00001F9E00001F9F00001FA800001FA900001FAA00001FAB00001FAC00001FAD -00001FAE00001FAF00001FA800001FA900001FAA00001FAB00001FAC00001FAD00001FAE00001FAF00001FBC00001FBC -00001FCC00001FCC00001FFC00001FFC02001FBA00000345020003860000034502001FCA000003450200038900000345 -02001FFA000003450200038F00000345030003910000034200000345030003970000034200000345030003A900000342 -00000345"; -return t; -} - -} - diff --git a/phobos/std/internal/windows/advapi32.d b/phobos/std/internal/windows/advapi32.d deleted file mode 100644 index 1b26f43..0000000 --- a/phobos/std/internal/windows/advapi32.d +++ /dev/null @@ -1,74 +0,0 @@ -// Written in the D programming language. - -/** - * The only purpose of this module is to do the static construction for - * std.windows.registry, to eliminate cyclic construction errors. - * - * License: $(HTTP www.boost.org/LICENSE_1_0.txt, Boost License 1.0). - * Authors: Kenji Hara - * Source: $(PHOBOSSRC std/internal/windows/advapi32.d) - */ -module std.internal.windows.advapi32; - -version (Windows): - -import core.sys.windows.winbase, core.sys.windows.winnt, core.sys.windows.winreg; - -pragma(lib, "advapi32.lib"); - -@property bool isWow64() -{ - // WOW64 is the x86 emulator that allows 32-bit Windows-based applications to run seamlessly on 64-bit Windows - // IsWow64Process Function - Minimum supported client - Windows Vista, Windows XP with SP2 - static int result = -1; // <0 if uninitialized, >0 if yes, ==0 if no - if (result >= 0) - return result > 0; // short path - // Will do this work once per thread to avoid importing std.concurrency - // or doing gnarly initonce work. - alias fptr_t = extern(Windows) BOOL function(HANDLE, PBOOL); - auto hKernel = GetModuleHandleA("kernel32"); - auto IsWow64Process = cast(fptr_t) GetProcAddress(hKernel, "IsWow64Process"); - BOOL bIsWow64; - result = IsWow64Process && IsWow64Process(GetCurrentProcess(), &bIsWow64) && bIsWow64; - return result > 0; -} - -HMODULE hAdvapi32 = null; -extern (Windows) -{ - LONG function( - scope const HKEY hkey, scope const LPCWSTR lpSubKey, - scope const REGSAM samDesired, scope const DWORD reserved) pRegDeleteKeyExW; -} - -void loadAdvapi32() -{ - if (!hAdvapi32) - { - hAdvapi32 = LoadLibraryA("Advapi32.dll"); - if (!hAdvapi32) - throw new Exception(`LoadLibraryA("Advapi32.dll")`); - - pRegDeleteKeyExW = cast(typeof(pRegDeleteKeyExW)) GetProcAddress(hAdvapi32 , "RegDeleteKeyExW"); - if (!pRegDeleteKeyExW) - throw new Exception(`GetProcAddress(hAdvapi32 , "RegDeleteKeyExW")`); - } -} - -// It will free Advapi32.dll, which may be loaded for RegDeleteKeyEx function -private void freeAdvapi32() -{ - if (hAdvapi32) - { - if (!FreeLibrary(hAdvapi32)) - throw new Exception(`FreeLibrary("Advapi32.dll")`); - hAdvapi32 = null; - - pRegDeleteKeyExW = null; - } -} - -static ~this() -{ - freeAdvapi32(); -} diff --git a/phobos/std/json.d b/phobos/std/json.d deleted file mode 100644 index 6e94a5d..0000000 --- a/phobos/std/json.d +++ /dev/null @@ -1,2452 +0,0 @@ -// Written in the D programming language. - -/** -Implements functionality to read and write JavaScript Object Notation values. - -JavaScript Object Notation is a lightweight data interchange format commonly used in web services and configuration files. -It's easy for humans to read and write, and it's easy for machines to parse and generate. - -$(RED Warning: While $(LREF JSONValue) is fine for small-scale use, at the range of hundreds of megabytes it is -known to cause and exacerbate GC problems. If you encounter problems, try replacing it with a stream parser. See -also $(LINK https://forum.dlang.org/post/dzfyaxypmkdrpakmycjv@forum.dlang.org).) - -Copyright: Copyright Jeremie Pelletier 2008 - 2009. -License: $(HTTP www.boost.org/LICENSE_1_0.txt, Boost License 1.0). -Authors: Jeremie Pelletier, David Herberth -References: $(LINK http://json.org/), $(LINK http://seriot.ch/parsing_json.html) -Source: $(PHOBOSSRC std/json.d) -*/ -/* - Copyright Jeremie Pelletier 2008 - 2009. -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.json; - -import std.array; -import std.conv; -import std.range; -import std.traits; - -/// -@system unittest -{ - import std.conv : to; - - // parse a file or string of json into a usable structure - string s = `{ "language": "D", "rating": 3.5, "code": "42" }`; - JSONValue j = parseJSON(s); - // j and j["language"] return JSONValue, - // j["language"].str returns a string - assert(j["language"].str == "D"); - assert(j["rating"].floating == 3.5); - - // check a type - long x; - if (const(JSONValue)* code = "code" in j) - { - if (code.type() == JSONType.integer) - x = code.integer; - else - x = to!int(code.str); - } - - // create a json struct - JSONValue jj = [ "language": "D" ]; - // rating doesnt exist yet, so use .object to assign - jj.object["rating"] = JSONValue(3.5); - // create an array to assign to list - jj.object["list"] = JSONValue( ["a", "b", "c"] ); - // list already exists, so .object optional - jj["list"].array ~= JSONValue("D"); - - string jjStr = `{"language":"D","list":["a","b","c","D"],"rating":3.5}`; - assert(jj.toString == jjStr); -} - -/** -String literals used to represent special float values within JSON strings. -*/ -enum JSONFloatLiteral : string -{ - nan = "NaN", /// String representation of floating-point NaN - inf = "Infinite", /// String representation of floating-point Infinity - negativeInf = "-Infinite", /// String representation of floating-point negative Infinity -} - -/** -Flags that control how JSON is encoded and parsed. -*/ -enum JSONOptions -{ - none, /// Standard parsing and encoding - specialFloatLiterals = 0x1, /// Encode NaN and Inf float values as strings - escapeNonAsciiChars = 0x2, /// Encode non-ASCII characters with a Unicode escape sequence - doNotEscapeSlashes = 0x4, /// Do not escape slashes ('/') - strictParsing = 0x8, /// Strictly follow RFC-8259 grammar when parsing -} - -/** -Enumeration of JSON types -*/ -enum JSONType : byte -{ - /// Indicates the type of a `JSONValue`. - null_, - string, /// ditto - integer, /// ditto - uinteger, /// ditto - float_, /// ditto - array, /// ditto - object, /// ditto - true_, /// ditto - false_, /// ditto - // FIXME: Find some way to deprecate the enum members below, which does NOT - // create lots of spam-like deprecation warnings, which can't be fixed - // by the user. See discussion on this issue at - // https://forum.dlang.org/post/feudrhtxkaxxscwhhhff@forum.dlang.org - /* deprecated("Use .null_") */ NULL = null_, - /* deprecated("Use .string") */ STRING = string, - /* deprecated("Use .integer") */ INTEGER = integer, - /* deprecated("Use .uinteger") */ UINTEGER = uinteger, - /* deprecated("Use .float_") */ FLOAT = float_, - /* deprecated("Use .array") */ ARRAY = array, - /* deprecated("Use .object") */ OBJECT = object, - /* deprecated("Use .true_") */ TRUE = true_, - /* deprecated("Use .false_") */ FALSE = false_, -} - -deprecated("Use JSONType and the new enum member names") alias JSON_TYPE = JSONType; - -/** -JSON value node -*/ -struct JSONValue -{ - import std.exception : enforce; - - union Store - { - string str; - long integer; - ulong uinteger; - double floating; - JSONValue[string] object; - JSONValue[] array; - } - private Store store; - private JSONType type_tag; - - /** - Returns the JSONType of the value stored in this structure. - */ - @property JSONType type() const pure nothrow @safe @nogc - { - return type_tag; - } - /// - @safe unittest - { - string s = "{ \"language\": \"D\" }"; - JSONValue j = parseJSON(s); - assert(j.type == JSONType.object); - assert(j["language"].type == JSONType.string); - } - - /*** - * Value getter/setter for `JSONType.string`. - * Throws: `JSONException` for read access if `type` is not - * `JSONType.string`. - */ - @property string str() const pure @trusted return scope - { - enforce!JSONException(type == JSONType.string, - "JSONValue is not a string"); - return store.str; - } - /// ditto - @property string str(return scope string v) pure nothrow @nogc @trusted return // TODO make @safe - { - assign(v); - return v; - } - /// - @safe unittest - { - JSONValue j = [ "language": "D" ]; - - // get value - assert(j["language"].str == "D"); - - // change existing key to new string - j["language"].str = "Perl"; - assert(j["language"].str == "Perl"); - } - - /*** - * Value getter/setter for `JSONType.integer`. - * Throws: `JSONException` for read access if `type` is not - * `JSONType.integer`. - */ - @property long integer() const pure @safe - { - enforce!JSONException(type == JSONType.integer, - "JSONValue is not an integer"); - return store.integer; - } - /// ditto - @property long integer(long v) pure nothrow @safe @nogc - { - assign(v); - return store.integer; - } - - /*** - * Value getter/setter for `JSONType.uinteger`. - * Throws: `JSONException` for read access if `type` is not - * `JSONType.uinteger`. - */ - @property ulong uinteger() const pure @safe - { - enforce!JSONException(type == JSONType.uinteger, - "JSONValue is not an unsigned integer"); - return store.uinteger; - } - /// ditto - @property ulong uinteger(ulong v) pure nothrow @safe @nogc - { - assign(v); - return store.uinteger; - } - - /*** - * Value getter/setter for `JSONType.float_`. Note that despite - * the name, this is a $(B 64)-bit `double`, not a 32-bit `float`. - * Throws: `JSONException` for read access if `type` is not - * `JSONType.float_`. - */ - @property double floating() const pure @safe - { - enforce!JSONException(type == JSONType.float_, - "JSONValue is not a floating type"); - return store.floating; - } - /// ditto - @property double floating(double v) pure nothrow @safe @nogc - { - assign(v); - return store.floating; - } - - /*** - * Value getter/setter for boolean stored in JSON. - * Throws: `JSONException` for read access if `this.type` is not - * `JSONType.true_` or `JSONType.false_`. - */ - @property bool boolean() const pure @safe - { - if (type == JSONType.true_) return true; - if (type == JSONType.false_) return false; - - throw new JSONException("JSONValue is not a boolean type"); - } - /// ditto - @property bool boolean(bool v) pure nothrow @safe @nogc - { - assign(v); - return v; - } - /// - @safe unittest - { - JSONValue j = true; - assert(j.boolean == true); - - j.boolean = false; - assert(j.boolean == false); - - j.integer = 12; - import std.exception : assertThrown; - assertThrown!JSONException(j.boolean); - } - - /*** - * Value getter/setter for `JSONType.object`. - * Throws: `JSONException` for read access if `type` is not - * `JSONType.object`. - * Note: This is @system because of the following pattern: - --- - auto a = &(json.object()); - json.uinteger = 0; // overwrite AA pointer - (*a)["hello"] = "world"; // segmentation fault - --- - */ - @property ref inout(JSONValue[string]) object() inout pure @system return - { - enforce!JSONException(type == JSONType.object, - "JSONValue is not an object"); - return store.object; - } - /// ditto - @property JSONValue[string] object(return scope JSONValue[string] v) pure nothrow @nogc @trusted // TODO make @safe - { - assign(v); - return v; - } - - /*** - * Value getter for `JSONType.object`. - * Unlike `object`, this retrieves the object by value - * and can be used in @safe code. - * - * One possible caveat is that, if the returned value is null, - * modifications will not be visible: - * --- - * JSONValue json; - * json.object = null; - * json.objectNoRef["hello"] = JSONValue("world"); - * assert("hello" !in json.object); - * --- - * - * Throws: `JSONException` for read access if `type` is not - * `JSONType.object`. - */ - @property inout(JSONValue[string]) objectNoRef() inout pure @trusted - { - enforce!JSONException(type == JSONType.object, - "JSONValue is not an object"); - return store.object; - } - - /*** - * Value getter/setter for `JSONType.array`. - * Throws: `JSONException` for read access if `type` is not - * `JSONType.array`. - * Note: This is @system because of the following pattern: - --- - auto a = &(json.array()); - json.uinteger = 0; // overwrite array pointer - (*a)[0] = "world"; // segmentation fault - --- - */ - @property ref inout(JSONValue[]) array() scope return inout pure @system - { - enforce!JSONException(type == JSONType.array, - "JSONValue is not an array"); - return store.array; - } - /// ditto - @property JSONValue[] array(return scope JSONValue[] v) pure nothrow @nogc @trusted scope // TODO make @safe - { - assign(v); - return v; - } - - /*** - * Value getter for `JSONType.array`. - * Unlike `array`, this retrieves the array by value and can be used in @safe code. - * - * One possible caveat is that, if you append to the returned array, - * the new values aren't visible in the `JSONValue`: - * --- - * JSONValue json; - * json.array = [JSONValue("hello")]; - * json.arrayNoRef ~= JSONValue("world"); - * assert(json.array.length == 1); - * --- - * - * Throws: `JSONException` for read access if `type` is not - * `JSONType.array`. - */ - @property inout(JSONValue[]) arrayNoRef() inout pure @trusted - { - enforce!JSONException(type == JSONType.array, - "JSONValue is not an array"); - return store.array; - } - - /// Test whether the type is `JSONType.null_` - @property bool isNull() const pure nothrow @safe @nogc - { - return type == JSONType.null_; - } - - /*** - * A convenience getter that returns this `JSONValue` as the specified D type. - * Note: Only numeric types, `bool`, `string`, `JSONValue[string]`, and `JSONValue[]` types are accepted - * Throws: `JSONException` if `T` cannot hold the contents of this `JSONValue` - * `ConvException` in case of integer overflow when converting to `T` - */ - @property inout(T) get(T)() inout const pure @safe - { - static if (is(immutable T == immutable string)) - { - return str; - } - else static if (is(immutable T == immutable bool)) - { - return boolean; - } - else static if (isFloatingPoint!T) - { - switch (type) - { - case JSONType.float_: - return cast(T) floating; - case JSONType.uinteger: - return cast(T) uinteger; - case JSONType.integer: - return cast(T) integer; - default: - throw new JSONException("JSONValue is not a number type"); - } - } - else static if (isIntegral!T) - { - switch (type) - { - case JSONType.uinteger: - return uinteger.to!T; - case JSONType.integer: - return integer.to!T; - default: - throw new JSONException("JSONValue is not a an integral type"); - } - } - else - { - static assert(false, "Unsupported type"); - } - } - // This specialization is needed because arrayNoRef requires inout - @property inout(T) get(T : JSONValue[])() inout pure @trusted /// ditto - { - return arrayNoRef; - } - /// ditto - @property inout(T) get(T : JSONValue[string])() inout pure @trusted - { - return object; - } - /// - @safe unittest - { - import std.exception; - import std.conv; - string s = - `{ - "a": 123, - "b": 3.1415, - "c": "text", - "d": true, - "e": [1, 2, 3], - "f": { "a": 1 }, - "g": -45, - "h": ` ~ ulong.max.to!string ~ `, - }`; - - struct a { } - - immutable json = parseJSON(s); - assert(json["a"].get!double == 123.0); - assert(json["a"].get!int == 123); - assert(json["a"].get!uint == 123); - assert(json["b"].get!double == 3.1415); - assertThrown!JSONException(json["b"].get!int); - assert(json["c"].get!string == "text"); - assert(json["d"].get!bool == true); - assertNotThrown(json["e"].get!(JSONValue[])); - assertNotThrown(json["f"].get!(JSONValue[string])); - static assert(!__traits(compiles, json["a"].get!a)); - assertThrown!JSONException(json["e"].get!float); - assertThrown!JSONException(json["d"].get!(JSONValue[string])); - assertThrown!JSONException(json["f"].get!(JSONValue[])); - assert(json["g"].get!int == -45); - assertThrown!ConvException(json["g"].get!uint); - assert(json["h"].get!ulong == ulong.max); - assertThrown!ConvException(json["h"].get!uint); - assertNotThrown(json["h"].get!float); - } - - private void assign(T)(T arg) - { - static if (is(T : typeof(null))) - { - type_tag = JSONType.null_; - } - else static if (is(T : string)) - { - type_tag = JSONType.string; - string t = arg; - () @trusted { store.str = t; }(); - } - // https://issues.dlang.org/show_bug.cgi?id=15884 - else static if (isSomeString!T) - { - type_tag = JSONType.string; - // FIXME: std.Array.Array(Range) is not deduced as 'pure' - () @trusted { - import std.utf : byUTF; - store.str = cast(immutable)(arg.byUTF!char.array); - }(); - } - else static if (is(T : bool)) - { - type_tag = arg ? JSONType.true_ : JSONType.false_; - } - else static if (is(T : ulong) && isUnsigned!T) - { - type_tag = JSONType.uinteger; - store.uinteger = arg; - } - else static if (is(T : long)) - { - type_tag = JSONType.integer; - store.integer = arg; - } - else static if (isFloatingPoint!T) - { - type_tag = JSONType.float_; - store.floating = arg; - } - else static if (is(T : Value[Key], Key, Value)) - { - static assert(is(Key : string), "AA key must be string"); - type_tag = JSONType.object; - static if (is(Value : JSONValue)) - { - JSONValue[string] t = arg; - () @trusted { store.object = t; }(); - } - else - { - JSONValue[string] aa; - foreach (key, value; arg) - aa[key] = JSONValue(value); - () @trusted { store.object = aa; }(); - } - } - else static if (isArray!T) - { - type_tag = JSONType.array; - static if (is(ElementEncodingType!T : JSONValue)) - { - JSONValue[] t = arg; - () @trusted { store.array = t; }(); - } - else - { - JSONValue[] new_arg = new JSONValue[arg.length]; - foreach (i, e; arg) - new_arg[i] = JSONValue(e); - () @trusted { store.array = new_arg; }(); - } - } - else static if (is(T : JSONValue)) - { - type_tag = arg.type; - store = arg.store; - } - else - { - static assert(false, text(`unable to convert type "`, T.stringof, `" to json`)); - } - } - - private void assignRef(T)(ref T arg) if (isStaticArray!T) - { - type_tag = JSONType.array; - static if (is(ElementEncodingType!T : JSONValue)) - { - store.array = arg; - } - else - { - JSONValue[] new_arg = new JSONValue[arg.length]; - foreach (i, e; arg) - new_arg[i] = JSONValue(e); - store.array = new_arg; - } - } - - /** - * Constructor for `JSONValue`. If `arg` is a `JSONValue` - * its value and type will be copied to the new `JSONValue`. - * Note that this is a shallow copy: if type is `JSONType.object` - * or `JSONType.array` then only the reference to the data will - * be copied. - * Otherwise, `arg` must be implicitly convertible to one of the - * following types: `typeof(null)`, `string`, `ulong`, - * `long`, `double`, an associative array `V[K]` for any `V` - * and `K` i.e. a JSON object, any array or `bool`. The type will - * be set accordingly. - */ - this(T)(T arg) if (!isStaticArray!T) - { - assign(arg); - } - /// Ditto - this(T)(ref T arg) if (isStaticArray!T) - { - assignRef(arg); - } - /// Ditto - this(T : JSONValue)(inout T arg) inout - { - store = arg.store; - type_tag = arg.type; - } - /// - @safe unittest - { - JSONValue j = JSONValue( "a string" ); - j = JSONValue(42); - - j = JSONValue( [1, 2, 3] ); - assert(j.type == JSONType.array); - - j = JSONValue( ["language": "D"] ); - assert(j.type == JSONType.object); - } - - /** - * An enum value that can be used to obtain a `JSONValue` representing - * an empty JSON object. - */ - enum emptyObject = JSONValue(string[string].init); - /// - @system unittest - { - JSONValue obj1 = JSONValue.emptyObject; - assert(obj1.type == JSONType.object); - obj1.object["a"] = JSONValue(1); - assert(obj1.object["a"] == JSONValue(1)); - - JSONValue obj2 = JSONValue.emptyObject; - assert("a" !in obj2.object); - obj2.object["b"] = JSONValue(5); - assert(obj1 != obj2); - } - - /** - * An enum value that can be used to obtain a `JSONValue` representing - * an empty JSON array. - */ - enum emptyArray = JSONValue(JSONValue[].init); - /// - @system unittest - { - JSONValue arr1 = JSONValue.emptyArray; - assert(arr1.type == JSONType.array); - assert(arr1.array.length == 0); - arr1.array ~= JSONValue("Hello"); - assert(arr1.array.length == 1); - assert(arr1.array[0] == JSONValue("Hello")); - - JSONValue arr2 = JSONValue.emptyArray; - assert(arr2.array.length == 0); - assert(arr1 != arr2); - } - - void opAssign(T)(T arg) if (!isStaticArray!T && !is(T : JSONValue)) - { - assign(arg); - } - - void opAssign(T)(ref T arg) if (isStaticArray!T) - { - assignRef(arg); - } - - /*** - * Array syntax for JSON arrays. - * Throws: `JSONException` if `type` is not `JSONType.array`. - */ - ref inout(JSONValue) opIndex(size_t i) inout pure @safe - { - auto a = this.arrayNoRef; - enforce!JSONException(i < a.length, - "JSONValue array index is out of range"); - return a[i]; - } - /// - @safe unittest - { - JSONValue j = JSONValue( [42, 43, 44] ); - assert( j[0].integer == 42 ); - assert( j[1].integer == 43 ); - } - - /*** - * Hash syntax for JSON objects. - * Throws: `JSONException` if `type` is not `JSONType.object`. - */ - ref inout(JSONValue) opIndex(return scope string k) inout pure @safe - { - auto o = this.objectNoRef; - return *enforce!JSONException(k in o, - "Key not found: " ~ k); - } - /// - @safe unittest - { - JSONValue j = JSONValue( ["language": "D"] ); - assert( j["language"].str == "D" ); - } - - /*** - * Provides support for index assignments, which sets the - * corresponding value of the JSON object's `key` field to `value`. - * - * If the `JSONValue` is `JSONType.null_`, then this function - * initializes it with a JSON object and then performs - * the index assignment. - * - * Throws: `JSONException` if `type` is not `JSONType.object` - * or `JSONType.null_`. - */ - void opIndexAssign(T)(auto ref T value, string key) - { - enforce!JSONException(type == JSONType.object || type == JSONType.null_, - "JSONValue must be object or null"); - JSONValue[string] aa = null; - if (type == JSONType.object) - { - aa = this.objectNoRef; - } - - aa[key] = value; - this.object = aa; - } - /// - @safe unittest - { - JSONValue j = JSONValue( ["language": "D"] ); - j["language"].str = "Perl"; - assert( j["language"].str == "Perl" ); - } - - /// ditto - void opIndexAssign(T)(T arg, size_t i) - { - auto a = this.arrayNoRef; - enforce!JSONException(i < a.length, - "JSONValue array index is out of range"); - a[i] = arg; - this.array = a; - } - /// - @safe unittest - { - JSONValue j = JSONValue( ["Perl", "C"] ); - j[1].str = "D"; - assert( j[1].str == "D" ); - } - - JSONValue opBinary(string op : "~", T)(T arg) - { - auto a = this.arrayNoRef; - static if (isArray!T) - { - return JSONValue(a ~ JSONValue(arg).arrayNoRef); - } - else static if (is(T : JSONValue)) - { - return JSONValue(a ~ arg.arrayNoRef); - } - else - { - static assert(false, "argument is not an array or a JSONValue array"); - } - } - - void opOpAssign(string op : "~", T)(T arg) - { - auto a = this.arrayNoRef; - static if (isArray!T) - { - a ~= JSONValue(arg).arrayNoRef; - } - else static if (is(T : JSONValue)) - { - a ~= arg.arrayNoRef; - } - else - { - static assert(false, "argument is not an array or a JSONValue array"); - } - this.array = a; - } - - /** - * Provides support for the `in` operator. - * - * Tests whether a key can be found in an object. - * - * Returns: - * When found, the `inout(JSONValue)*` that matches to the key, - * otherwise `null`. - * - * Throws: `JSONException` if the right hand side argument `JSONType` - * is not `object`. - */ - inout(JSONValue)* opBinaryRight(string op : "in")(string k) inout @safe - { - return k in this.objectNoRef; - } - /// - @safe unittest - { - JSONValue j = [ "language": "D", "author": "walter" ]; - string a = ("author" in j).str; - *("author" in j) = "Walter"; - assert(j["author"].str == "Walter"); - } - - /// - bool opEquals(const JSONValue rhs) const @nogc nothrow pure @safe - { - return opEquals(rhs); - } - - /// ditto - bool opEquals(ref const JSONValue rhs) const @nogc nothrow pure @trusted - { - // Default doesn't work well since store is a union. Compare only - // what should be in store. - // This is @trusted to remain nogc, nothrow, fast, and usable from @safe code. - - final switch (type_tag) - { - case JSONType.integer: - switch (rhs.type_tag) - { - case JSONType.integer: - return store.integer == rhs.store.integer; - case JSONType.uinteger: - return store.integer == rhs.store.uinteger; - case JSONType.float_: - return store.integer == rhs.store.floating; - default: - return false; - } - case JSONType.uinteger: - switch (rhs.type_tag) - { - case JSONType.integer: - return store.uinteger == rhs.store.integer; - case JSONType.uinteger: - return store.uinteger == rhs.store.uinteger; - case JSONType.float_: - return store.uinteger == rhs.store.floating; - default: - return false; - } - case JSONType.float_: - switch (rhs.type_tag) - { - case JSONType.integer: - return store.floating == rhs.store.integer; - case JSONType.uinteger: - return store.floating == rhs.store.uinteger; - case JSONType.float_: - return store.floating == rhs.store.floating; - default: - return false; - } - case JSONType.string: - return type_tag == rhs.type_tag && store.str == rhs.store.str; - case JSONType.object: - return type_tag == rhs.type_tag && store.object == rhs.store.object; - case JSONType.array: - return type_tag == rhs.type_tag && store.array == rhs.store.array; - case JSONType.true_: - case JSONType.false_: - case JSONType.null_: - return type_tag == rhs.type_tag; - } - } - - /// - @safe unittest - { - assert(JSONValue(0u) == JSONValue(0)); - assert(JSONValue(0u) == JSONValue(0.0)); - assert(JSONValue(0) == JSONValue(0.0)); - } - - /// Implements the foreach `opApply` interface for json arrays. - int opApply(scope int delegate(size_t index, ref JSONValue) dg) @system - { - int result; - - foreach (size_t index, ref value; array) - { - result = dg(index, value); - if (result) - break; - } - - return result; - } - - /// Implements the foreach `opApply` interface for json objects. - int opApply(scope int delegate(string key, ref JSONValue) dg) @system - { - enforce!JSONException(type == JSONType.object, - "JSONValue is not an object"); - int result; - - foreach (string key, ref value; object) - { - result = dg(key, value); - if (result) - break; - } - - return result; - } - - /*** - * Implicitly calls `toJSON` on this JSONValue. - * - * $(I options) can be used to tweak the conversion behavior. - */ - string toString(in JSONOptions options = JSONOptions.none) const @safe - { - return toJSON(this, false, options); - } - - /// - void toString(Out)(Out sink, in JSONOptions options = JSONOptions.none) const - { - toJSON(sink, this, false, options); - } - - /*** - * Implicitly calls `toJSON` on this JSONValue, like `toString`, but - * also passes $(I true) as $(I pretty) argument. - * - * $(I options) can be used to tweak the conversion behavior - */ - string toPrettyString(in JSONOptions options = JSONOptions.none) const @safe - { - return toJSON(this, true, options); - } - - /// - void toPrettyString(Out)(Out sink, in JSONOptions options = JSONOptions.none) const - { - toJSON(sink, this, true, options); - } -} - -// https://issues.dlang.org/show_bug.cgi?id=20874 -@system unittest -{ - static struct MyCustomType - { - public string toString () const @system { return null; } - alias toString this; - } - - static struct B - { - public JSONValue asJSON() const @system { return JSONValue.init; } - alias asJSON this; - } - - if (false) // Just checking attributes - { - JSONValue json; - MyCustomType ilovedlang; - json = ilovedlang; - json["foo"] = ilovedlang; - auto s = ilovedlang in json; - - B b; - json ~= b; - json ~ b; - } -} - -/** -Parses a serialized string and returns a tree of JSON values. -Throws: $(LREF JSONException) if string does not follow the JSON grammar or the depth exceeds the max depth, - $(LREF ConvException) if a number in the input cannot be represented by a native D type. -Params: - json = json-formatted string to parse - maxDepth = maximum depth of nesting allowed, -1 disables depth checking - options = enable decoding string representations of NaN/Inf as float values -*/ -JSONValue parseJSON(T)(T json, int maxDepth = -1, JSONOptions options = JSONOptions.none) -if (isSomeFiniteCharInputRange!T) -{ - import std.ascii : isDigit, isHexDigit, toUpper, toLower; - import std.typecons : Nullable, Yes; - JSONValue root; - root.type_tag = JSONType.null_; - - // Avoid UTF decoding when possible, as it is unnecessary when - // processing JSON. - static if (is(T : const(char)[])) - alias Char = char; - else - alias Char = Unqual!(ElementType!T); - - int depth = -1; - Nullable!Char next; - int line = 1, pos = 0; - immutable bool strict = (options & JSONOptions.strictParsing) != 0; - - void error(string msg) - { - throw new JSONException(msg, line, pos); - } - - if (json.empty) - { - if (strict) - { - error("Empty JSON body"); - } - return root; - } - - bool isWhite(dchar c) - { - if (strict) - { - // RFC 7159 has a stricter definition of whitespace than general ASCII. - return c == ' ' || c == '\t' || c == '\n' || c == '\r'; - } - import std.ascii : isWhite; - // Accept ASCII NUL as whitespace in non-strict mode. - return c == 0 || isWhite(c); - } - - Char popChar() - { - if (json.empty) error("Unexpected end of data."); - static if (is(T : const(char)[])) - { - Char c = json[0]; - json = json[1..$]; - } - else - { - Char c = json.front; - json.popFront(); - } - - if (c == '\n') - { - line++; - pos = 0; - } - else - { - pos++; - } - - return c; - } - - Char peekChar() - { - if (next.isNull) - { - if (json.empty) return '\0'; - next = popChar(); - } - return next.get; - } - - Nullable!Char peekCharNullable() - { - if (next.isNull && !json.empty) - { - next = popChar(); - } - return next; - } - - void skipWhitespace() - { - while (true) - { - auto c = peekCharNullable(); - if (c.isNull || - !isWhite(c.get)) - { - return; - } - next.nullify(); - } - } - - Char getChar(bool SkipWhitespace = false)() - { - static if (SkipWhitespace) skipWhitespace(); - - Char c; - if (!next.isNull) - { - c = next.get; - next.nullify(); - } - else - c = popChar(); - - return c; - } - - void checkChar(bool SkipWhitespace = true)(char c, bool caseSensitive = true) - { - static if (SkipWhitespace) skipWhitespace(); - auto c2 = getChar(); - if (!caseSensitive) c2 = toLower(c2); - - if (c2 != c) error(text("Found '", c2, "' when expecting '", c, "'.")); - } - - bool testChar(bool SkipWhitespace = true, bool CaseSensitive = true)(char c) - { - static if (SkipWhitespace) skipWhitespace(); - auto c2 = peekChar(); - static if (!CaseSensitive) c2 = toLower(c2); - - if (c2 != c) return false; - - getChar(); - return true; - } - - wchar parseWChar() - { - wchar val = 0; - foreach_reverse (i; 0 .. 4) - { - auto hex = toUpper(getChar()); - if (!isHexDigit(hex)) error("Expecting hex character"); - val += (isDigit(hex) ? hex - '0' : hex - ('A' - 10)) << (4 * i); - } - return val; - } - - string parseString() - { - import std.uni : isSurrogateHi, isSurrogateLo; - import std.utf : encode, decode; - - auto str = appender!string(); - - Next: - switch (peekChar()) - { - case '"': - getChar(); - break; - - case '\\': - getChar(); - auto c = getChar(); - switch (c) - { - case '"': str.put('"'); break; - case '\\': str.put('\\'); break; - case '/': str.put('/'); break; - case 'b': str.put('\b'); break; - case 'f': str.put('\f'); break; - case 'n': str.put('\n'); break; - case 'r': str.put('\r'); break; - case 't': str.put('\t'); break; - case 'u': - wchar wc = parseWChar(); - dchar val; - // Non-BMP characters are escaped as a pair of - // UTF-16 surrogate characters (see RFC 4627). - if (isSurrogateHi(wc)) - { - wchar[2] pair; - pair[0] = wc; - if (getChar() != '\\') error("Expected escaped low surrogate after escaped high surrogate"); - if (getChar() != 'u') error("Expected escaped low surrogate after escaped high surrogate"); - pair[1] = parseWChar(); - size_t index = 0; - val = decode(pair[], index); - if (index != 2) error("Invalid escaped surrogate pair"); - } - else - if (isSurrogateLo(wc)) - error(text("Unexpected low surrogate")); - else - val = wc; - - char[4] buf; - immutable len = encode!(Yes.useReplacementDchar)(buf, val); - str.put(buf[0 .. len]); - break; - - default: - error(text("Invalid escape sequence '\\", c, "'.")); - } - goto Next; - - default: - // RFC 7159 states that control characters U+0000 through - // U+001F must not appear unescaped in a JSON string. - // Note: std.ascii.isControl can't be used for this test - // because it considers ASCII DEL (0x7f) to be a control - // character but RFC 7159 does not. - // Accept unescaped ASCII NULs in non-strict mode. - auto c = getChar(); - if (c < 0x20 && (strict || c != 0)) - error("Illegal control character."); - str.put(c); - goto Next; - } - - return str.data.length ? str.data : ""; - } - - bool tryGetSpecialFloat(string str, out double val) { - switch (str) - { - case JSONFloatLiteral.nan: - val = double.nan; - return true; - case JSONFloatLiteral.inf: - val = double.infinity; - return true; - case JSONFloatLiteral.negativeInf: - val = -double.infinity; - return true; - default: - return false; - } - } - - void parseValue(ref JSONValue value) - { - depth++; - - if (maxDepth != -1 && depth > maxDepth) error("Nesting too deep."); - - auto c = getChar!true(); - - switch (c) - { - case '{': - if (testChar('}')) - { - value.object = null; - break; - } - - JSONValue[string] obj; - do - { - skipWhitespace(); - if (!strict && peekChar() == '}') - { - break; - } - checkChar('"'); - string name = parseString(); - checkChar(':'); - JSONValue member; - parseValue(member); - obj[name] = member; - } - while (testChar(',')); - value.object = obj; - - checkChar('}'); - break; - - case '[': - if (testChar(']')) - { - value.type_tag = JSONType.array; - break; - } - - JSONValue[] arr; - do - { - skipWhitespace(); - if (!strict && peekChar() == ']') - { - break; - } - JSONValue element; - parseValue(element); - arr ~= element; - } - while (testChar(',')); - - checkChar(']'); - value.array = arr; - break; - - case '"': - auto str = parseString(); - - // if special float parsing is enabled, check if string represents NaN/Inf - if ((options & JSONOptions.specialFloatLiterals) && - tryGetSpecialFloat(str, value.store.floating)) - { - // found a special float, its value was placed in value.store.floating - value.type_tag = JSONType.float_; - break; - } - - value.assign(str); - break; - - case '0': .. case '9': - case '-': - auto number = appender!string(); - bool isFloat, isNegative; - - void readInteger() - { - if (!isDigit(c)) error("Digit expected"); - - Next: number.put(c); - - if (isDigit(peekChar())) - { - c = getChar(); - goto Next; - } - } - - if (c == '-') - { - number.put('-'); - c = getChar(); - isNegative = true; - } - - if (strict && c == '0') - { - number.put('0'); - if (isDigit(peekChar())) - { - error("Additional digits not allowed after initial zero digit"); - } - } - else - { - readInteger(); - } - - if (testChar('.')) - { - isFloat = true; - number.put('.'); - c = getChar(); - readInteger(); - } - if (testChar!(false, false)('e')) - { - isFloat = true; - number.put('e'); - if (testChar('+')) number.put('+'); - else if (testChar('-')) number.put('-'); - c = getChar(); - readInteger(); - } - - string data = number.data; - if (isFloat) - { - value.type_tag = JSONType.float_; - value.store.floating = parse!double(data); - } - else - { - if (isNegative) - { - value.store.integer = parse!long(data); - value.type_tag = JSONType.integer; - } - else - { - // only set the correct union member to not confuse CTFE - ulong u = parse!ulong(data); - if (u & (1UL << 63)) - { - value.store.uinteger = u; - value.type_tag = JSONType.uinteger; - } - else - { - value.store.integer = u; - value.type_tag = JSONType.integer; - } - } - } - break; - - case 'T': - if (strict) goto default; - goto case; - case 't': - value.type_tag = JSONType.true_; - checkChar!false('r', strict); - checkChar!false('u', strict); - checkChar!false('e', strict); - break; - - case 'F': - if (strict) goto default; - goto case; - case 'f': - value.type_tag = JSONType.false_; - checkChar!false('a', strict); - checkChar!false('l', strict); - checkChar!false('s', strict); - checkChar!false('e', strict); - break; - - case 'N': - if (strict) goto default; - goto case; - case 'n': - value.type_tag = JSONType.null_; - checkChar!false('u', strict); - checkChar!false('l', strict); - checkChar!false('l', strict); - break; - - default: - error(text("Unexpected character '", c, "'.")); - } - - depth--; - } - - parseValue(root); - if (strict) - { - skipWhitespace(); - if (!peekCharNullable().isNull) error("Trailing non-whitespace characters"); - } - return root; -} - -@safe unittest -{ - enum issue15742objectOfObject = `{ "key1": { "key2": 1 }}`; - static assert(parseJSON(issue15742objectOfObject).type == JSONType.object); - - enum issue15742arrayOfArray = `[[1]]`; - static assert(parseJSON(issue15742arrayOfArray).type == JSONType.array); -} - -@safe unittest -{ - // Ensure we can parse and use JSON from @safe code - auto a = `{ "key1": { "key2": 1 }}`.parseJSON; - assert(a["key1"]["key2"].integer == 1); - assert(a.toString == `{"key1":{"key2":1}}`); -} - -@system unittest -{ - // Ensure we can parse JSON from a @system range. - struct Range - { - string s; - size_t index; - @system - { - bool empty() { return index >= s.length; } - void popFront() { index++; } - char front() { return s[index]; } - } - } - auto s = Range(`{ "key1": { "key2": 1 }}`); - auto json = parseJSON(s); - assert(json["key1"]["key2"].integer == 1); -} - -// https://issues.dlang.org/show_bug.cgi?id=20527 -@safe unittest -{ - static assert(parseJSON(`{"a" : 2}`)["a"].integer == 2); -} - -/** -Parses a serialized string and returns a tree of JSON values. -Throws: $(LREF JSONException) if the depth exceeds the max depth. -Params: - json = json-formatted string to parse - options = enable decoding string representations of NaN/Inf as float values -*/ -JSONValue parseJSON(T)(T json, JSONOptions options) -if (isSomeFiniteCharInputRange!T) -{ - return parseJSON!T(json, -1, options); -} - -/** -Takes a tree of JSON values and returns the serialized string. - -Any Object types will be serialized in a key-sorted order. - -If `pretty` is false no whitespaces are generated. -If `pretty` is true serialized string is formatted to be human-readable. -Set the $(LREF JSONOptions.specialFloatLiterals) flag is set in `options` to encode NaN/Infinity as strings. -*/ -string toJSON(const ref JSONValue root, in bool pretty = false, in JSONOptions options = JSONOptions.none) @safe -{ - auto json = appender!string(); - toJSON(json, root, pretty, options); - return json.data; -} - -/// -void toJSON(Out)( - auto ref Out json, - const ref JSONValue root, - in bool pretty = false, - in JSONOptions options = JSONOptions.none) -if (isOutputRange!(Out,char)) -{ - void toStringImpl(Char)(string str) - { - json.put('"'); - - foreach (Char c; str) - { - switch (c) - { - case '"': json.put("\\\""); break; - case '\\': json.put("\\\\"); break; - - case '/': - if (!(options & JSONOptions.doNotEscapeSlashes)) - json.put('\\'); - json.put('/'); - break; - - case '\b': json.put("\\b"); break; - case '\f': json.put("\\f"); break; - case '\n': json.put("\\n"); break; - case '\r': json.put("\\r"); break; - case '\t': json.put("\\t"); break; - default: - { - import std.ascii : isControl; - import std.utf : encode; - - // Make sure we do UTF decoding iff we want to - // escape Unicode characters. - assert(((options & JSONOptions.escapeNonAsciiChars) != 0) - == is(Char == dchar), "JSONOptions.escapeNonAsciiChars needs dchar strings"); - - with (JSONOptions) if (isControl(c) || - ((options & escapeNonAsciiChars) >= escapeNonAsciiChars && c >= 0x80)) - { - // Ensure non-BMP characters are encoded as a pair - // of UTF-16 surrogate characters, as per RFC 4627. - wchar[2] wchars; // 1 or 2 UTF-16 code units - size_t wNum = encode(wchars, c); // number of UTF-16 code units - foreach (wc; wchars[0 .. wNum]) - { - json.put("\\u"); - foreach_reverse (i; 0 .. 4) - { - char ch = (wc >>> (4 * i)) & 0x0f; - ch += ch < 10 ? '0' : 'A' - 10; - json.put(ch); - } - } - } - else - { - json.put(c); - } - } - } - } - - json.put('"'); - } - - void toString(string str) - { - // Avoid UTF decoding when possible, as it is unnecessary when - // processing JSON. - if (options & JSONOptions.escapeNonAsciiChars) - toStringImpl!dchar(str); - else - toStringImpl!char(str); - } - - /* make the function infer @system when json.put() is @system - */ - if (0) - json.put(' '); - - /* Mark as @trusted because json.put() may be @system. This has difficulty - * inferring @safe because it is recursive. - */ - void toValueImpl(ref const JSONValue value, ulong indentLevel) @trusted - { - void putTabs(ulong additionalIndent = 0) - { - if (pretty) - foreach (i; 0 .. indentLevel + additionalIndent) - json.put(" "); - } - void putEOL() - { - if (pretty) - json.put('\n'); - } - void putCharAndEOL(char ch) - { - json.put(ch); - putEOL(); - } - - final switch (value.type) - { - case JSONType.object: - auto obj = value.objectNoRef; - if (!obj.length) - { - json.put("{}"); - } - else - { - putCharAndEOL('{'); - bool first = true; - - void emit(R)(R names) - { - foreach (name; names) - { - auto member = obj[name]; - if (!first) - putCharAndEOL(','); - first = false; - putTabs(1); - toString(name); - json.put(':'); - if (pretty) - json.put(' '); - toValueImpl(member, indentLevel + 1); - } - } - - import std.algorithm.sorting : sort; - // https://issues.dlang.org/show_bug.cgi?id=14439 - // auto names = obj.keys; // aa.keys can't be called in @safe code - auto names = new string[obj.length]; - size_t i = 0; - foreach (k, v; obj) - { - names[i] = k; - i++; - } - sort(names); - emit(names); - - putEOL(); - putTabs(); - json.put('}'); - } - break; - - case JSONType.array: - auto arr = value.arrayNoRef; - if (arr.empty) - { - json.put("[]"); - } - else - { - putCharAndEOL('['); - foreach (i, el; arr) - { - if (i) - putCharAndEOL(','); - putTabs(1); - toValueImpl(el, indentLevel + 1); - } - putEOL(); - putTabs(); - json.put(']'); - } - break; - - case JSONType.string: - toString(value.str); - break; - - case JSONType.integer: - json.put(to!string(value.store.integer)); - break; - - case JSONType.uinteger: - json.put(to!string(value.store.uinteger)); - break; - - case JSONType.float_: - import std.math.traits : isNaN, isInfinity; - - auto val = value.store.floating; - - if (val.isNaN) - { - if (options & JSONOptions.specialFloatLiterals) - { - toString(JSONFloatLiteral.nan); - } - else - { - throw new JSONException( - "Cannot encode NaN. Consider passing the specialFloatLiterals flag."); - } - } - else if (val.isInfinity) - { - if (options & JSONOptions.specialFloatLiterals) - { - toString((val > 0) ? JSONFloatLiteral.inf : JSONFloatLiteral.negativeInf); - } - else - { - throw new JSONException( - "Cannot encode Infinity. Consider passing the specialFloatLiterals flag."); - } - } - else - { - import std.algorithm.searching : canFind; - import std.format : sformat; - // The correct formula for the number of decimal digits needed for lossless round - // trips is actually: - // ceil(log(pow(2.0, double.mant_dig - 1)) / log(10.0) + 1) == (double.dig + 2) - // Anything less will round off (1 + double.epsilon) - char[25] buf; - auto result = buf[].sformat!"%.18g"(val); - json.put(result); - if (!result.canFind('e') && !result.canFind('.')) - json.put(".0"); - } - break; - - case JSONType.true_: - json.put("true"); - break; - - case JSONType.false_: - json.put("false"); - break; - - case JSONType.null_: - json.put("null"); - break; - } - } - - toValueImpl(root, 0); -} - - // https://issues.dlang.org/show_bug.cgi?id=12897 -@safe unittest -{ - JSONValue jv0 = JSONValue("test测试"); - assert(toJSON(jv0, false, JSONOptions.escapeNonAsciiChars) == `"test\u6D4B\u8BD5"`); - JSONValue jv00 = JSONValue("test\u6D4B\u8BD5"); - assert(toJSON(jv00, false, JSONOptions.none) == `"test测试"`); - assert(toJSON(jv0, false, JSONOptions.none) == `"test测试"`); - JSONValue jv1 = JSONValue("ĂŠtĂŠ"); - assert(toJSON(jv1, false, JSONOptions.escapeNonAsciiChars) == `"\u00E9t\u00E9"`); - JSONValue jv11 = JSONValue("\u00E9t\u00E9"); - assert(toJSON(jv11, false, JSONOptions.none) == `"ĂŠtĂŠ"`); - assert(toJSON(jv1, false, JSONOptions.none) == `"ĂŠtĂŠ"`); -} - -// https://issues.dlang.org/show_bug.cgi?id=20511 -@system unittest -{ - import std.format.write : formattedWrite; - import std.range : nullSink, outputRangeObject; - - outputRangeObject!(const(char)[])(nullSink) - .formattedWrite!"%s"(JSONValue.init); -} - -// Issue 16432 - JSON incorrectly parses to string -@safe unittest -{ - // Floating points numbers are rounded to the nearest integer and thus get - // incorrectly parsed - - import std.math.operations : isClose; - - string s = "{\"rating\": 3.0 }"; - JSONValue j = parseJSON(s); - assert(j["rating"].type == JSONType.float_); - j = j.toString.parseJSON; - assert(j["rating"].type == JSONType.float_); - assert(isClose(j["rating"].floating, 3.0)); - - s = "{\"rating\": -3.0 }"; - j = parseJSON(s); - assert(j["rating"].type == JSONType.float_); - j = j.toString.parseJSON; - assert(j["rating"].type == JSONType.float_); - assert(isClose(j["rating"].floating, -3.0)); - - // https://issues.dlang.org/show_bug.cgi?id=13660 - auto jv1 = JSONValue(4.0); - auto textual = jv1.toString(); - auto jv2 = parseJSON(textual); - assert(jv1.type == JSONType.float_); - assert(textual == "4.0"); - assert(jv2.type == JSONType.float_); -} - -@safe unittest -{ - // Adapted from https://github.com/dlang/phobos/pull/5005 - // Result from toString is not checked here, because this - // might differ (%e-like or %f-like output) depending - // on OS and compiler optimization. - import std.math.operations : isClose; - - // test positive extreme values - JSONValue j; - j["rating"] = 1e18 - 65; - assert(isClose(j.toString.parseJSON["rating"].floating, 1e18 - 65)); - - j["rating"] = 1e18 - 64; - assert(isClose(j.toString.parseJSON["rating"].floating, 1e18 - 64)); - - // negative extreme values - j["rating"] = -1e18 + 65; - assert(isClose(j.toString.parseJSON["rating"].floating, -1e18 + 65)); - - j["rating"] = -1e18 + 64; - assert(isClose(j.toString.parseJSON["rating"].floating, -1e18 + 64)); -} - -/** -Exception thrown on JSON errors -*/ -class JSONException : Exception -{ - this(string msg, int line = 0, int pos = 0) pure nothrow @safe - { - if (line) - super(text(msg, " (Line ", line, ":", pos, ")")); - else - super(msg); - } - - this(string msg, string file, size_t line) pure nothrow @safe - { - super(msg, file, line); - } -} - - -@system unittest -{ - import std.exception; - JSONValue jv = "123"; - assert(jv.type == JSONType.string); - assertNotThrown(jv.str); - assertThrown!JSONException(jv.integer); - assertThrown!JSONException(jv.uinteger); - assertThrown!JSONException(jv.floating); - assertThrown!JSONException(jv.object); - assertThrown!JSONException(jv.array); - assertThrown!JSONException(jv["aa"]); - assertThrown!JSONException(jv[2]); - - jv = -3; - assert(jv.type == JSONType.integer); - assertNotThrown(jv.integer); - - jv = cast(uint) 3; - assert(jv.type == JSONType.uinteger); - assertNotThrown(jv.uinteger); - - jv = 3.0; - assert(jv.type == JSONType.float_); - assertNotThrown(jv.floating); - - jv = ["key" : "value"]; - assert(jv.type == JSONType.object); - assertNotThrown(jv.object); - assertNotThrown(jv["key"]); - assert("key" in jv); - assert("notAnElement" !in jv); - assertThrown!JSONException(jv["notAnElement"]); - const cjv = jv; - assert("key" in cjv); - assertThrown!JSONException(cjv["notAnElement"]); - - foreach (string key, value; jv) - { - static assert(is(typeof(value) == JSONValue)); - assert(key == "key"); - assert(value.type == JSONType.string); - assertNotThrown(value.str); - assert(value.str == "value"); - } - - jv = [3, 4, 5]; - assert(jv.type == JSONType.array); - assertNotThrown(jv.array); - assertNotThrown(jv[2]); - foreach (size_t index, value; jv) - { - static assert(is(typeof(value) == JSONValue)); - assert(value.type == JSONType.integer); - assertNotThrown(value.integer); - assert(index == (value.integer-3)); - } - - jv = null; - assert(jv.type == JSONType.null_); - assert(jv.isNull); - jv = "foo"; - assert(!jv.isNull); - - jv = JSONValue("value"); - assert(jv.type == JSONType.string); - assert(jv.str == "value"); - - JSONValue jv2 = JSONValue("value"); - assert(jv2.type == JSONType.string); - assert(jv2.str == "value"); - - JSONValue jv3 = JSONValue("\u001c"); - assert(jv3.type == JSONType.string); - assert(jv3.str == "\u001C"); -} - -// https://issues.dlang.org/show_bug.cgi?id=11504 -@system unittest -{ - JSONValue jv = 1; - assert(jv.type == JSONType.integer); - - jv.str = "123"; - assert(jv.type == JSONType.string); - assert(jv.str == "123"); - - jv.integer = 1; - assert(jv.type == JSONType.integer); - assert(jv.integer == 1); - - jv.uinteger = 2u; - assert(jv.type == JSONType.uinteger); - assert(jv.uinteger == 2u); - - jv.floating = 1.5; - assert(jv.type == JSONType.float_); - assert(jv.floating == 1.5); - - jv.object = ["key" : JSONValue("value")]; - assert(jv.type == JSONType.object); - assert(jv.object == ["key" : JSONValue("value")]); - - jv.array = [JSONValue(1), JSONValue(2), JSONValue(3)]; - assert(jv.type == JSONType.array); - assert(jv.array == [JSONValue(1), JSONValue(2), JSONValue(3)]); - - jv = true; - assert(jv.type == JSONType.true_); - - jv = false; - assert(jv.type == JSONType.false_); - - enum E{True = true} - jv = E.True; - assert(jv.type == JSONType.true_); -} - -@system pure unittest -{ - // Adding new json element via array() / object() directly - - JSONValue jarr = JSONValue([10]); - foreach (i; 0 .. 9) - jarr.array ~= JSONValue(i); - assert(jarr.array.length == 10); - - JSONValue jobj = JSONValue(["key" : JSONValue("value")]); - foreach (i; 0 .. 9) - jobj.object[text("key", i)] = JSONValue(text("value", i)); - assert(jobj.object.length == 10); -} - -@system pure unittest -{ - // Adding new json element without array() / object() access - - JSONValue jarr = JSONValue([10]); - foreach (i; 0 .. 9) - jarr ~= [JSONValue(i)]; - assert(jarr.array.length == 10); - - JSONValue jobj = JSONValue(["key" : JSONValue("value")]); - foreach (i; 0 .. 9) - jobj[text("key", i)] = JSONValue(text("value", i)); - assert(jobj.object.length == 10); - - // No array alias - auto jarr2 = jarr ~ [1,2,3]; - jarr2[0] = 999; - assert(jarr[0] == JSONValue(10)); -} - -@system unittest -{ - // @system because JSONValue.array is @system - import std.exception; - - // An overly simple test suite, if it can parse a serializated string and - // then use the resulting values tree to generate an identical - // serialization, both the decoder and encoder works. - - auto jsons = [ - `null`, - `true`, - `false`, - `0`, - `123`, - `-4321`, - `0.25`, - `-0.25`, - `""`, - `"hello\nworld"`, - `"\"\\\/\b\f\n\r\t"`, - `[]`, - `[12,"foo",true,false]`, - `{}`, - `{"a":1,"b":null}`, - `{"goodbye":[true,"or",false,["test",42,{"nested":{"a":23.5,"b":0.140625}}]],` - ~`"hello":{"array":[12,null,{}],"json":"is great"}}`, - ]; - - enum dbl1_844 = `1.8446744073709568`; - version (MinGW) - jsons ~= dbl1_844 ~ `e+019`; - else - jsons ~= dbl1_844 ~ `e+19`; - - JSONValue val; - string result; - foreach (json; jsons) - { - try - { - val = parseJSON(json); - enum pretty = false; - result = toJSON(val, pretty); - assert(result == json, text(result, " should be ", json)); - } - catch (JSONException e) - { - import std.stdio : writefln; - writefln(text(json, "\n", e.toString())); - } - } - - // Should be able to correctly interpret unicode entities - val = parseJSON(`"\u003C\u003E"`); - assert(toJSON(val) == "\"\<\>\""); - assert(val.to!string() == "\"\<\>\""); - val = parseJSON(`"\u0391\u0392\u0393"`); - assert(toJSON(val) == "\"\Α\Β\Γ\""); - assert(val.to!string() == "\"\Α\Β\Γ\""); - val = parseJSON(`"\u2660\u2666"`); - assert(toJSON(val) == "\"\♠\♦\""); - assert(val.to!string() == "\"\♠\♦\""); - - //0x7F is a control character (see Unicode spec) - val = parseJSON(`"\u007F"`); - assert(toJSON(val) == "\"\\u007F\""); - assert(val.to!string() == "\"\\u007F\""); - - with(parseJSON(`""`)) - assert(str == "" && str !is null); - with(parseJSON(`[]`)) - assert(!array.length); - - // Formatting - val = parseJSON(`{"a":[null,{"x":1},{},[]]}`); - assert(toJSON(val, true) == `{ - "a": [ - null, - { - "x": 1 - }, - {}, - [] - ] -}`); -} - -@safe unittest -{ - auto json = `"hello\nworld"`; - const jv = parseJSON(json); - assert(jv.toString == json); - assert(jv.toPrettyString == json); -} - -@system pure unittest -{ - // https://issues.dlang.org/show_bug.cgi?id=12969 - - JSONValue jv; - jv["int"] = 123; - - assert(jv.type == JSONType.object); - assert("int" in jv); - assert(jv["int"].integer == 123); - - jv["array"] = [1, 2, 3, 4, 5]; - - assert(jv["array"].type == JSONType.array); - assert(jv["array"][2].integer == 3); - - jv["str"] = "D language"; - assert(jv["str"].type == JSONType.string); - assert(jv["str"].str == "D language"); - - jv["bool"] = false; - assert(jv["bool"].type == JSONType.false_); - - assert(jv.object.length == 4); - - jv = [5, 4, 3, 2, 1]; - assert(jv.type == JSONType.array); - assert(jv[3].integer == 2); -} - -@safe unittest -{ - auto s = q"EOF -[ - 1, - 2, - 3, - potato -] -EOF"; - - import std.exception; - - auto e = collectException!JSONException(parseJSON(s)); - assert(e.msg == "Unexpected character 'p'. (Line 5:3)", e.msg); -} - -// handling of special float values (NaN, Inf, -Inf) -@safe unittest -{ - import std.exception : assertThrown; - import std.math.traits : isNaN, isInfinity; - - // expected representations of NaN and Inf - enum { - nanString = '"' ~ JSONFloatLiteral.nan ~ '"', - infString = '"' ~ JSONFloatLiteral.inf ~ '"', - negativeInfString = '"' ~ JSONFloatLiteral.negativeInf ~ '"', - } - - // with the specialFloatLiterals option, encode NaN/Inf as strings - assert(JSONValue(float.nan).toString(JSONOptions.specialFloatLiterals) == nanString); - assert(JSONValue(double.infinity).toString(JSONOptions.specialFloatLiterals) == infString); - assert(JSONValue(-real.infinity).toString(JSONOptions.specialFloatLiterals) == negativeInfString); - - // without the specialFloatLiterals option, throw on encoding NaN/Inf - assertThrown!JSONException(JSONValue(float.nan).toString); - assertThrown!JSONException(JSONValue(double.infinity).toString); - assertThrown!JSONException(JSONValue(-real.infinity).toString); - - // when parsing json with specialFloatLiterals option, decode special strings as floats - JSONValue jvNan = parseJSON(nanString, JSONOptions.specialFloatLiterals); - JSONValue jvInf = parseJSON(infString, JSONOptions.specialFloatLiterals); - JSONValue jvNegInf = parseJSON(negativeInfString, JSONOptions.specialFloatLiterals); - - assert(jvNan.floating.isNaN); - assert(jvInf.floating.isInfinity && jvInf.floating > 0); - assert(jvNegInf.floating.isInfinity && jvNegInf.floating < 0); - - // when parsing json without the specialFloatLiterals option, decode special strings as strings - jvNan = parseJSON(nanString); - jvInf = parseJSON(infString); - jvNegInf = parseJSON(negativeInfString); - - assert(jvNan.str == JSONFloatLiteral.nan); - assert(jvInf.str == JSONFloatLiteral.inf); - assert(jvNegInf.str == JSONFloatLiteral.negativeInf); -} - -pure nothrow @safe @nogc unittest -{ - JSONValue testVal; - testVal = "test"; - testVal = 10; - testVal = 10u; - testVal = 1.0; - testVal = (JSONValue[string]).init; - testVal = JSONValue[].init; - testVal = null; - assert(testVal.isNull); -} - -// https://issues.dlang.org/show_bug.cgi?id=15884 -pure nothrow @safe unittest -{ - import std.typecons; - void Test(C)() { - C[] a = ['x']; - JSONValue testVal = a; - assert(testVal.type == JSONType.string); - testVal = a.idup; - assert(testVal.type == JSONType.string); - } - Test!char(); - Test!wchar(); - Test!dchar(); -} - -// https://issues.dlang.org/show_bug.cgi?id=15885 -@safe unittest -{ - enum bool realInDoublePrecision = real.mant_dig == double.mant_dig; - - static bool test(const double num0) - { - import std.math.operations : feqrel; - const json0 = JSONValue(num0); - const num1 = to!double(toJSON(json0)); - static if (realInDoublePrecision) - return feqrel(num1, num0) >= (double.mant_dig - 1); - else - return num1 == num0; - } - - assert(test( 0.23)); - assert(test(-0.23)); - assert(test(1.223e+24)); - assert(test(23.4)); - assert(test(0.0012)); - assert(test(30738.22)); - - assert(test(1 + double.epsilon)); - assert(test(double.min_normal)); - static if (realInDoublePrecision) - assert(test(-double.max / 2)); - else - assert(test(-double.max)); - - const minSub = double.min_normal * double.epsilon; - assert(test(minSub)); - assert(test(3*minSub)); -} - -// https://issues.dlang.org/show_bug.cgi?id=17555 -@safe unittest -{ - import std.exception : assertThrown; - - assertThrown!JSONException(parseJSON("\"a\nb\"")); -} - -// https://issues.dlang.org/show_bug.cgi?id=17556 -@safe unittest -{ - auto v = JSONValue("\U0001D11E"); - auto j = toJSON(v, false, JSONOptions.escapeNonAsciiChars); - assert(j == `"\uD834\uDD1E"`); -} - -// https://issues.dlang.org/show_bug.cgi?id=5904 -@safe unittest -{ - string s = `"\uD834\uDD1E"`; - auto j = parseJSON(s); - assert(j.str == "\U0001D11E"); -} - -// https://issues.dlang.org/show_bug.cgi?id=17557 -@safe unittest -{ - assert(parseJSON("\"\xFF\"").str == "\xFF"); - assert(parseJSON("\"\U0001D11E\"").str == "\U0001D11E"); -} - -// https://issues.dlang.org/show_bug.cgi?id=17553 -@safe unittest -{ - auto v = JSONValue("\xFF"); - assert(toJSON(v) == "\"\xFF\""); -} - -@safe unittest -{ - import std.utf; - assert(parseJSON("\"\xFF\"".byChar).str == "\xFF"); - assert(parseJSON("\"\U0001D11E\"".byChar).str == "\U0001D11E"); -} - -// JSONOptions.doNotEscapeSlashes (https://issues.dlang.org/show_bug.cgi?id=17587) -@safe unittest -{ - assert(parseJSON(`"/"`).toString == `"\/"`); - assert(parseJSON(`"\/"`).toString == `"\/"`); - assert(parseJSON(`"/"`).toString(JSONOptions.doNotEscapeSlashes) == `"/"`); - assert(parseJSON(`"\/"`).toString(JSONOptions.doNotEscapeSlashes) == `"/"`); -} - -// JSONOptions.strictParsing (https://issues.dlang.org/show_bug.cgi?id=16639) -@safe unittest -{ - import std.exception : assertThrown; - - // Unescaped ASCII NULs - assert(parseJSON("[\0]").type == JSONType.array); - assertThrown!JSONException(parseJSON("[\0]", JSONOptions.strictParsing)); - assert(parseJSON("\"\0\"").str == "\0"); - assertThrown!JSONException(parseJSON("\"\0\"", JSONOptions.strictParsing)); - - // Unescaped ASCII DEL (0x7f) in strings - assert(parseJSON("\"\x7f\"").str == "\x7f"); - assert(parseJSON("\"\x7f\"", JSONOptions.strictParsing).str == "\x7f"); - - // "true", "false", "null" case sensitivity - assert(parseJSON("true").type == JSONType.true_); - assert(parseJSON("true", JSONOptions.strictParsing).type == JSONType.true_); - assert(parseJSON("True").type == JSONType.true_); - assertThrown!JSONException(parseJSON("True", JSONOptions.strictParsing)); - assert(parseJSON("tRUE").type == JSONType.true_); - assertThrown!JSONException(parseJSON("tRUE", JSONOptions.strictParsing)); - - assert(parseJSON("false").type == JSONType.false_); - assert(parseJSON("false", JSONOptions.strictParsing).type == JSONType.false_); - assert(parseJSON("False").type == JSONType.false_); - assertThrown!JSONException(parseJSON("False", JSONOptions.strictParsing)); - assert(parseJSON("fALSE").type == JSONType.false_); - assertThrown!JSONException(parseJSON("fALSE", JSONOptions.strictParsing)); - - assert(parseJSON("null").type == JSONType.null_); - assert(parseJSON("null", JSONOptions.strictParsing).type == JSONType.null_); - assert(parseJSON("Null").type == JSONType.null_); - assertThrown!JSONException(parseJSON("Null", JSONOptions.strictParsing)); - assert(parseJSON("nULL").type == JSONType.null_); - assertThrown!JSONException(parseJSON("nULL", JSONOptions.strictParsing)); - - // Whitespace characters - assert(parseJSON("[\f\v]").type == JSONType.array); - assertThrown!JSONException(parseJSON("[\f\v]", JSONOptions.strictParsing)); - assert(parseJSON("[ \t\r\n]").type == JSONType.array); - assert(parseJSON("[ \t\r\n]", JSONOptions.strictParsing).type == JSONType.array); - - // Empty input - assert(parseJSON("").type == JSONType.null_); - assertThrown!JSONException(parseJSON("", JSONOptions.strictParsing)); - - // Numbers with leading '0's - assert(parseJSON("01").integer == 1); - assertThrown!JSONException(parseJSON("01", JSONOptions.strictParsing)); - assert(parseJSON("-01").integer == -1); - assertThrown!JSONException(parseJSON("-01", JSONOptions.strictParsing)); - assert(parseJSON("0.01").floating == 0.01); - assert(parseJSON("0.01", JSONOptions.strictParsing).floating == 0.01); - assert(parseJSON("0e1").floating == 0); - assert(parseJSON("0e1", JSONOptions.strictParsing).floating == 0); - - // Trailing characters after JSON value - assert(parseJSON(`""asdf`).str == ""); - assertThrown!JSONException(parseJSON(`""asdf`, JSONOptions.strictParsing)); - assert(parseJSON("987\0").integer == 987); - assertThrown!JSONException(parseJSON("987\0", JSONOptions.strictParsing)); - assert(parseJSON("987\0\0").integer == 987); - assertThrown!JSONException(parseJSON("987\0\0", JSONOptions.strictParsing)); - assert(parseJSON("[]]").type == JSONType.array); - assertThrown!JSONException(parseJSON("[]]", JSONOptions.strictParsing)); - assert(parseJSON("123 \t\r\n").integer == 123); // Trailing whitespace is OK - assert(parseJSON("123 \t\r\n", JSONOptions.strictParsing).integer == 123); -} - -@system unittest -{ - import std.algorithm.iteration : map; - import std.array : array; - import std.exception : assertThrown; - - string s = `{ "a" : [1,2,3,], }`; - JSONValue j = parseJSON(s); - assert(j["a"].array().map!(i => i.integer()).array == [1,2,3]); - - assertThrown(parseJSON(s, -1, JSONOptions.strictParsing)); -} - -@system unittest -{ - import std.algorithm.iteration : map; - import std.array : array; - import std.exception : assertThrown; - - string s = `{ "a" : { } , }`; - JSONValue j = parseJSON(s); - assert("a" in j); - auto t = j["a"].object(); - assert(t.empty); - - assertThrown(parseJSON(s, -1, JSONOptions.strictParsing)); -} - -// https://issues.dlang.org/show_bug.cgi?id=20330 -@safe unittest -{ - import std.array : appender; - - string s = `{"a":[1,2,3]}`; - JSONValue j = parseJSON(s); - - auto app = appender!string(); - j.toString(app); - - assert(app.data == s, app.data); -} - -// https://issues.dlang.org/show_bug.cgi?id=20330 -@safe unittest -{ - import std.array : appender; - import std.format.write : formattedWrite; - - string s = -`{ - "a": [ - 1, - 2, - 3 - ] -}`; - JSONValue j = parseJSON(s); - - auto app = appender!string(); - j.toPrettyString(app); - - assert(app.data == s, app.data); -} diff --git a/phobos/std/logger/core.d b/phobos/std/logger/core.d deleted file mode 100644 index 285c830..0000000 --- a/phobos/std/logger/core.d +++ /dev/null @@ -1,3084 +0,0 @@ -// Written in the D programming language. -/** -Source: $(PHOBOSSRC std/logger/core.d) -*/ -module std.logger.core; - -import core.atomic : atomicLoad, atomicOp, atomicStore, MemoryOrder; -import core.sync.mutex : Mutex; -import std.datetime.date : DateTime; -import std.datetime.systime : Clock, SysTime; -import std.range.primitives; -import std.traits; - -import std.logger.filelogger; - -/** This functions is used at runtime to determine if a `LogLevel` is -active. The same previously defined version statements are used to disable -certain levels. Again the version statements are associated with a compile -unit and can therefore not disable logging in other compile units. -pure bool isLoggingEnabled()(LogLevel ll) @safe nothrow @nogc -*/ -bool isLoggingEnabled()(LogLevel ll, LogLevel loggerLL, - LogLevel globalLL, lazy bool condition = true) @safe -{ - return ll >= globalLL - && ll >= loggerLL - && ll != LogLevel.off - && globalLL != LogLevel.off - && loggerLL != LogLevel.off - && condition; -} - -/* This function formates a `SysTime` into an `OutputRange`. - -The `SysTime` is formatted similar to -$(LREF std.datatime.DateTime.toISOExtString) except the fractional second part. -The fractional second part is in milliseconds and is always 3 digits. -*/ -void systimeToISOString(OutputRange)(OutputRange o, const ref SysTime time) -if (isOutputRange!(OutputRange,string)) -{ - import std.format.write : formattedWrite; - - const auto dt = cast(DateTime) time; - const auto fsec = time.fracSecs.total!"msecs"; - - formattedWrite(o, "%04d-%02d-%02dT%02d:%02d:%02d.%03d", - dt.year, dt.month, dt.day, dt.hour, dt.minute, dt.second, - fsec); -} - -/** This function logs data. - -In order for the data to be processed, the `LogLevel` of the log call must -be greater or equal to the `LogLevel` of the `sharedLog` and the -`defaultLogLevel`; additionally the condition passed must be `true`. - -Params: - ll = The `LogLevel` used by this log call. - condition = The condition must be `true` for the data to be logged. - args = The data that should be logged. - -Example: --------------------- -log(LogLevel.warning, true, "Hello World", 3.1415); --------------------- -*/ -pragma(inline, true) // LDC: Must inline because of __FILE__ as template parameter -void log(int line = __LINE__, string file = __FILE__, - string funcName = __FUNCTION__, string prettyFuncName = __PRETTY_FUNCTION__, - string moduleName = __MODULE__, A...)(const LogLevel ll, - lazy bool condition, lazy A args) -if (args.length != 1) -{ - stdThreadLocalLog.log!(line, file, funcName, prettyFuncName, moduleName) - (ll, condition, args); -} - -/// Ditto -void log(T, string moduleName = __MODULE__)(const LogLevel ll, - lazy bool condition, lazy T arg, int line = __LINE__, string file = __FILE__, - string funcName = __FUNCTION__, string prettyFuncName = __PRETTY_FUNCTION__) -{ - stdThreadLocalLog.log!(T,moduleName)(ll, condition, arg, line, file, funcName, - prettyFuncName); -} - -/** This function logs data. - -In order for the data to be processed the `LogLevel` of the log call must -be greater or equal to the `LogLevel` of the `sharedLog`. - -Params: - ll = The `LogLevel` used by this log call. - args = The data that should be logged. - -Example: --------------------- -log(LogLevel.warning, "Hello World", 3.1415); --------------------- -*/ -pragma(inline, true) // LDC: Must inline because of __FILE__ as template parameter -void log(int line = __LINE__, string file = __FILE__, - string funcName = __FUNCTION__, string prettyFuncName = __PRETTY_FUNCTION__, - string moduleName = __MODULE__, A...)(const LogLevel ll, lazy A args) -if (args.length > 1 && !is(Unqual!(A[0]) : bool)) -{ - stdThreadLocalLog.log!(line, file, funcName, prettyFuncName, moduleName) - (ll, args); -} - -/// Ditto -void log(T, string moduleName = __MODULE__)(const LogLevel ll, lazy T arg, - int line = __LINE__, string file = __FILE__, string funcName = __FUNCTION__, - string prettyFuncName = __PRETTY_FUNCTION__) -{ - stdThreadLocalLog.log!T(ll, arg, line, file, funcName, prettyFuncName, - moduleName); -} - -/** This function logs data. - -In order for the data to be processed the `LogLevel` of the -`sharedLog` must be greater or equal to the `defaultLogLevel` -add the condition passed must be `true`. - -Params: - condition = The condition must be `true` for the data to be logged. - args = The data that should be logged. - -Example: --------------------- -log(true, "Hello World", 3.1415); --------------------- -*/ -pragma(inline, true) // LDC: Must inline because of __FILE__ as template parameter -void log(int line = __LINE__, string file = __FILE__, - string funcName = __FUNCTION__, string prettyFuncName = __PRETTY_FUNCTION__, - string moduleName = __MODULE__, A...)(lazy bool condition, lazy A args) -if (args.length != 1) -{ - stdThreadLocalLog.log!(line, file, funcName, prettyFuncName, moduleName) - (stdThreadLocalLog.logLevel, condition, args); -} - -/// Ditto -void log(T, string moduleName = __MODULE__)(lazy bool condition, lazy T arg, - int line = __LINE__, string file = __FILE__, string funcName = __FUNCTION__, - string prettyFuncName = __PRETTY_FUNCTION__) -{ - stdThreadLocalLog.log!(T,moduleName)(stdThreadLocalLog.logLevel, - condition, arg, line, file, funcName, prettyFuncName); -} - -/** This function logs data. - -In order for the data to be processed the `LogLevel` of the -`sharedLog` must be greater or equal to the `defaultLogLevel`. - -Params: - args = The data that should be logged. - -Example: --------------------- -log("Hello World", 3.1415); --------------------- -*/ -pragma(inline, true) // LDC: Must inline because of __FILE__ as template parameter -void log(int line = __LINE__, string file = __FILE__, - string funcName = __FUNCTION__, string prettyFuncName = __PRETTY_FUNCTION__, - string moduleName = __MODULE__, A...)(lazy A args) -if ((args.length > 1 && !is(Unqual!(A[0]) : bool) - && !is(Unqual!(A[0]) == LogLevel)) - || args.length == 0) -{ - stdThreadLocalLog.log!(line, file, funcName, - prettyFuncName, moduleName)(stdThreadLocalLog.logLevel, args); -} - -void log(T)(lazy T arg, int line = __LINE__, string file = __FILE__, - string funcName = __FUNCTION__, string prettyFuncName = __PRETTY_FUNCTION__, - string moduleName = __MODULE__) -{ - stdThreadLocalLog.log!T(stdThreadLocalLog.logLevel, arg, line, file, - funcName, prettyFuncName, moduleName); -} - -/** This function logs data in a `printf`-style manner. - -In order for the data to be processed the `LogLevel` of the log call must -be greater or equal to the `LogLevel` of the `sharedLog` and the -`defaultLogLevel` additionally the condition passed must be `true`. - -Params: - ll = The `LogLevel` used by this log call. - condition = The condition must be `true` for the data to be logged. - msg = The `printf`-style string. - args = The data that should be logged. - -Example: --------------------- -logf(LogLevel.warning, true, "Hello World %f", 3.1415); --------------------- -*/ -pragma(inline, true) // LDC: Must inline because of __FILE__ as template parameter -void logf(int line = __LINE__, string file = __FILE__, - string funcName = __FUNCTION__, - string prettyFuncName = __PRETTY_FUNCTION__, - string moduleName = __MODULE__, A...)(const LogLevel ll, - lazy bool condition, lazy string msg, lazy A args) -{ - stdThreadLocalLog.logf!(line, file, funcName, prettyFuncName, moduleName) - (ll, condition, msg, args); -} - -/** This function logs data in a `printf`-style manner. - -In order for the data to be processed the `LogLevel` of the log call must -be greater or equal to the `LogLevel` of the `sharedLog` and the -`defaultLogLevel`. - -Params: - ll = The `LogLevel` used by this log call. - msg = The `printf`-style string. - args = The data that should be logged. - -Example: --------------------- -logf(LogLevel.warning, "Hello World %f", 3.1415); --------------------- -*/ -pragma(inline, true) // LDC: Must inline because of __FILE__ as template parameter -void logf(int line = __LINE__, string file = __FILE__, - string funcName = __FUNCTION__, string prettyFuncName = __PRETTY_FUNCTION__, - string moduleName = __MODULE__, A...)(const LogLevel ll, lazy string msg, - lazy A args) -{ - stdThreadLocalLog.logf!(line, file, funcName, prettyFuncName, moduleName) - (ll, msg, args); -} - -/** This function logs data in a `printf`-style manner. - -In order for the data to be processed the `LogLevel` of the log call must -be greater or equal to the `defaultLogLevel` additionally the condition -passed must be `true`. - -Params: - condition = The condition must be `true` for the data to be logged. - msg = The `printf`-style string. - args = The data that should be logged. - -Example: --------------------- -logf(true, "Hello World %f", 3.1415); --------------------- -*/ -pragma(inline, true) // LDC: Must inline because of __FILE__ as template parameter -void logf(int line = __LINE__, string file = __FILE__, - string funcName = __FUNCTION__, string prettyFuncName = __PRETTY_FUNCTION__, - string moduleName = __MODULE__, A...)(lazy bool condition, - lazy string msg, lazy A args) -{ - stdThreadLocalLog.logf!(line, file, funcName, prettyFuncName, moduleName) - (stdThreadLocalLog.logLevel, condition, msg, args); -} - -/** This function logs data in a `printf`-style manner. - -In order for the data to be processed the `LogLevel` of the log call must -be greater or equal to the `defaultLogLevel`. - -Params: - msg = The `printf`-style string. - args = The data that should be logged. - -Example: --------------------- -logf("Hello World %f", 3.1415); --------------------- -*/ -pragma(inline, true) // LDC: Must inline because of __FILE__ as template parameter -void logf(int line = __LINE__, string file = __FILE__, - string funcName = __FUNCTION__, - string prettyFuncName = __PRETTY_FUNCTION__, - string moduleName = __MODULE__, A...)(lazy string msg, lazy A args) -{ - stdThreadLocalLog.logf!(line, file, funcName,prettyFuncName, moduleName) - (stdThreadLocalLog.logLevel, msg, args); -} - -/** This template provides the global log functions with the `LogLevel` -is encoded in the function name. - -The aliases following this template create the public names of these log -functions. -*/ -template defaultLogFunction(LogLevel ll) -{ - pragma(inline, true) // LDC: Must inline because of __FILE__ as template parameter - void defaultLogFunction(int line = __LINE__, string file = __FILE__, - string funcName = __FUNCTION__, - string prettyFuncName = __PRETTY_FUNCTION__, - string moduleName = __MODULE__, A...)(lazy A args) - if ((args.length > 0 && !is(Unqual!(A[0]) : bool)) || args.length == 0) - { - stdThreadLocalLog.memLogFunctions!(ll).logImpl!(line, file, funcName, - prettyFuncName, moduleName)(args); - } - - pragma(inline, true) // LDC: Must inline because of __FILE__ as template parameter - void defaultLogFunction(int line = __LINE__, string file = __FILE__, - string funcName = __FUNCTION__, - string prettyFuncName = __PRETTY_FUNCTION__, - string moduleName = __MODULE__, A...)(lazy bool condition, lazy A args) - { - stdThreadLocalLog.memLogFunctions!(ll).logImpl!(line, file, funcName, - prettyFuncName, moduleName)(condition, args); - } -} - -/** This function logs data to the `stdThreadLocalLog`, optionally depending -on a condition. - -In order for the resulting log message to be logged the `LogLevel` must -be greater or equal than the `LogLevel` of the `stdThreadLocalLog` and -must be greater or equal than the global `LogLevel`. -Additionally the `LogLevel` must be greater or equal than the `LogLevel` -of the `stdSharedLogger`. -If a condition is given, it must evaluate to `true`. - -Params: - condition = The condition must be `true` for the data to be logged. - args = The data that should be logged. - -Example: --------------------- -trace(1337, "is number"); -info(1337, "is number"); -error(1337, "is number"); -critical(1337, "is number"); -fatal(1337, "is number"); -trace(true, 1337, "is number"); -info(false, 1337, "is number"); -error(true, 1337, "is number"); -critical(false, 1337, "is number"); -fatal(true, 1337, "is number"); --------------------- -*/ -alias trace = defaultLogFunction!(LogLevel.trace); -/// Ditto -alias info = defaultLogFunction!(LogLevel.info); -/// Ditto -alias warning = defaultLogFunction!(LogLevel.warning); -/// Ditto -alias error = defaultLogFunction!(LogLevel.error); -/// Ditto -alias critical = defaultLogFunction!(LogLevel.critical); -/// Ditto -alias fatal = defaultLogFunction!(LogLevel.fatal); - -/** This template provides the global `printf`-style log functions with -the `LogLevel` is encoded in the function name. - -The aliases following this template create the public names of the log -functions. -*/ -template defaultLogFunctionf(LogLevel ll) -{ - pragma(inline, true) // LDC: Must inline because of __FILE__ as template parameter - void defaultLogFunctionf(int line = __LINE__, string file = __FILE__, - string funcName = __FUNCTION__, - string prettyFuncName = __PRETTY_FUNCTION__, - string moduleName = __MODULE__, A...)(lazy string msg, lazy A args) - { - stdThreadLocalLog.memLogFunctions!(ll).logImplf!(line, file, funcName, - prettyFuncName, moduleName)(msg, args); - } - - pragma(inline, true) // LDC: Must inline because of __FILE__ as template parameter - void defaultLogFunctionf(int line = __LINE__, string file = __FILE__, - string funcName = __FUNCTION__, - string prettyFuncName = __PRETTY_FUNCTION__, - string moduleName = __MODULE__, A...)(lazy bool condition, - lazy string msg, lazy A args) - { - stdThreadLocalLog.memLogFunctions!(ll).logImplf!(line, file, funcName, - prettyFuncName, moduleName)(condition, msg, args); - } -} - -/** This function logs data to the `sharedLog` in a `printf`-style -manner. - -In order for the resulting log message to be logged the `LogLevel` must -be greater or equal than the `LogLevel` of the `sharedLog` and -must be greater or equal than the global `LogLevel`. -Additionally the `LogLevel` must be greater or equal than the `LogLevel` -of the `stdSharedLogger`. - -Params: - msg = The `printf`-style string. - args = The data that should be logged. - -Example: --------------------- -tracef("is number %d", 1); -infof("is number %d", 2); -errorf("is number %d", 3); -criticalf("is number %d", 4); -fatalf("is number %d", 5); --------------------- - -The second version of the function logs data to the `sharedLog` in a $(D -printf)-style manner. - -In order for the resulting log message to be logged the `LogLevel` must -be greater or equal than the `LogLevel` of the `sharedLog` and -must be greater or equal than the global `LogLevel`. -Additionally the `LogLevel` must be greater or equal than the `LogLevel` -of the `stdSharedLogger`. - -Params: - condition = The condition must be `true` for the data to be logged. - msg = The `printf`-style string. - args = The data that should be logged. - -Example: --------------------- -tracef(false, "is number %d", 1); -infof(false, "is number %d", 2); -errorf(true, "is number %d", 3); -criticalf(true, "is number %d", 4); -fatalf(someFunct(), "is number %d", 5); --------------------- -*/ -alias tracef = defaultLogFunctionf!(LogLevel.trace); -/// Ditto -alias infof = defaultLogFunctionf!(LogLevel.info); -/// Ditto -alias warningf = defaultLogFunctionf!(LogLevel.warning); -/// Ditto -alias errorf = defaultLogFunctionf!(LogLevel.error); -/// Ditto -alias criticalf = defaultLogFunctionf!(LogLevel.critical); -/// Ditto -alias fatalf = defaultLogFunctionf!(LogLevel.fatal); - -private struct MsgRange -{ - import std.traits : isSomeString, isSomeChar; - - private Logger log; - - this(Logger log) @safe - { - this.log = log; - } - - void put(T)(T msg) @safe - if (isSomeString!T) - { - log.logMsgPart(msg); - } - - void put(dchar elem) @safe - { - import std.utf : encode; - char[4] buffer; - size_t len = encode(buffer, elem); - log.logMsgPart(buffer[0 .. len]); - } -} - -private void formatString(A...)(MsgRange oRange, A args) -{ - import std.format.write : formattedWrite; - - foreach (arg; args) - { - formattedWrite(oRange, "%s", arg); - } -} - -@system unittest -{ - void dummy() @safe - { - auto tl = new TestLogger(); - auto dst = MsgRange(tl); - formatString(dst, "aaa", "bbb"); - } - - dummy(); -} - -/** -There are eight usable logging level. These level are $(I all), $(I trace), -$(I info), $(I warning), $(I error), $(I critical), $(I fatal), and $(I off). -If a log function with `LogLevel.fatal` is called the shutdown handler of -that logger is called. -*/ -enum LogLevel : ubyte -{ - all = 1, /** Lowest possible assignable `LogLevel`. */ - trace = 32, /** `LogLevel` for tracing the execution of the program. */ - info = 64, /** This level is used to display information about the - program. */ - warning = 96, /** warnings about the program should be displayed with this - level. */ - error = 128, /** Information about errors should be logged with this - level.*/ - critical = 160, /** Messages that inform about critical errors should be - logged with this level. */ - fatal = 192, /** Log messages that describe fatal errors should use this - level. */ - off = ubyte.max /** Highest possible `LogLevel`. */ -} - -/** This class is the base of every logger. In order to create a new kind of -logger a deriving class needs to implement the `writeLogMsg` method. By -default this is not thread-safe. - -It is also possible to `override` the three methods `beginLogMsg`, -`logMsgPart` and `finishLogMsg` together, this option gives more -flexibility. -*/ -abstract class Logger -{ - import std.array : appender, Appender; - import std.concurrency : thisTid, Tid; - - /** LogEntry is a aggregation combining all information associated - with a log message. This aggregation will be passed to the method - writeLogMsg. - */ - protected struct LogEntry - { - /// the filename the log function was called from - string file; - /// the line number the log function was called from - int line; - /// the name of the function the log function was called from - string funcName; - /// the pretty formatted name of the function the log function was - /// called from - string prettyFuncName; - /// the name of the module the log message is coming from - string moduleName; - /// the `LogLevel` associated with the log message - LogLevel logLevel; - /// thread id of the log message - Tid threadId; - /// the time the message was logged - SysTime timestamp; - /// the message of the log message - string msg; - /// A refernce to the `Logger` used to create this `LogEntry` - Logger logger; - } - - /** - Every subclass of `Logger` has to call this constructor from their - constructor. It sets the `LogLevel`, and creates a fatal handler. The fatal - handler will throw an `Error` if a log call is made with level - `LogLevel.fatal`. - - Params: - lv = `LogLevel` to use for this `Logger` instance. - */ - this(this This)(LogLevel lv) - { - this.logLevel_ = lv; - this.fatalHandler_ = delegate() { - throw new Error("A fatal log message was logged"); - }; - - this.mutex = new typeof(mutex)(); - } - - /** A custom logger must implement this method in order to work in a - `MultiLogger` and `ArrayLogger`. - - Params: - payload = All information associated with call to log function. - - See_Also: beginLogMsg, logMsgPart, finishLogMsg - */ - abstract protected void writeLogMsg(ref LogEntry payload) @safe; - - /* The default implementation will use an `std.array.appender` - internally to construct the message string. This means dynamic, - GC memory allocation. A logger can avoid this allocation by - reimplementing `beginLogMsg`, `logMsgPart` and `finishLogMsg`. - `beginLogMsg` is always called first, followed by any number of calls - to `logMsgPart` and one call to `finishLogMsg`. - - As an example for such a custom `Logger` compare this: - ---------------- - class CLogger : Logger - { - override void beginLogMsg(string file, int line, string funcName, - string prettyFuncName, string moduleName, LogLevel logLevel, - Tid threadId, SysTime timestamp) - { - ... logic here - } - - override void logMsgPart(const(char)[] msg) - { - ... logic here - } - - override void finishLogMsg() - { - ... logic here - } - - void writeLogMsg(ref LogEntry payload) - { - this.beginLogMsg(payload.file, payload.line, payload.funcName, - payload.prettyFuncName, payload.moduleName, payload.logLevel, - payload.threadId, payload.timestamp, payload.logger); - - this.logMsgPart(payload.msg); - this.finishLogMsg(); - } - } - ---------------- - */ - protected void beginLogMsg(string file, int line, string funcName, - string prettyFuncName, string moduleName, LogLevel logLevel, - Tid threadId, SysTime timestamp, Logger logger) - @safe - { - msgAppender = appender!string(); - header = LogEntry(file, line, funcName, prettyFuncName, - moduleName, logLevel, threadId, timestamp, null, logger); - } - - /** Logs a part of the log message. */ - protected void logMsgPart(scope const(char)[] msg) @safe - { - msgAppender.put(msg); - } - - /** Signals that the message has been written and no more calls to - `logMsgPart` follow. */ - protected void finishLogMsg() @safe - { - header.msg = msgAppender.data; - this.writeLogMsg(header); - } - - /** The `LogLevel` determines if the log call are processed or dropped - by the `Logger`. In order for the log call to be processed the - `LogLevel` of the log call must be greater or equal to the `LogLevel` - of the `logger`. - - These two methods set and get the `LogLevel` of the used `Logger`. - - Example: - ----------- - auto f = new FileLogger(stdout); - f.logLevel = LogLevel.info; - assert(f.logLevel == LogLevel.info); - ----------- - */ - @property final LogLevel logLevel() const pure @safe @nogc - { - return trustedLoad(this.logLevel_); - } - - /// Ditto - @property final void logLevel(const LogLevel lv) @safe @nogc - { - atomicStore(this.logLevel_, lv); - } - - /** This `delegate` is called in case a log message with - `LogLevel.fatal` gets logged. - - By default an `Error` will be thrown. - */ - @property final void delegate() fatalHandler() @safe @nogc - { - synchronized (mutex) return this.fatalHandler_; - } - - /// Ditto - @property final void fatalHandler(void delegate() @safe fh) @safe @nogc - { - synchronized (mutex) this.fatalHandler_ = fh; - } - - /** This method allows forwarding log entries from one logger to another. - - `forwardMsg` will ensure proper synchronization and then call - `writeLogMsg`. This is an API for implementing your own loggers and - should not be called by normal user code. A notable difference from other - logging functions is that the `globalLogLevel` wont be evaluated again - since it is assumed that the caller already checked that. - */ - void forwardMsg(ref LogEntry payload) @trusted - { - if (isLoggingEnabled(payload.logLevel, this.logLevel_, - globalLogLevel)) - { - this.writeLogMsg(payload); - - if (payload.logLevel == LogLevel.fatal) - this.fatalHandler_(); - } - } - - /** This template provides the log functions for the `Logger` `class` - with the `LogLevel` encoded in the function name. - - For further information see the two functions defined inside of this - template. - - The aliases following this template create the public names of these log - functions. - */ - template memLogFunctions(LogLevel ll) - { - /** This function logs data to the used `Logger`. - - In order for the resulting log message to be logged the `LogLevel` - must be greater or equal than the `LogLevel` of the used `Logger` - and must be greater or equal than the global `LogLevel`. - - Params: - args = The data that should be logged. - - Example: - -------------------- - auto s = new FileLogger(stdout); - s.trace(1337, "is number"); - s.info(1337, "is number"); - s.error(1337, "is number"); - s.critical(1337, "is number"); - s.fatal(1337, "is number"); - -------------------- - */ - pragma(inline, true) // LDC: Must inline because of __FILE__ as template parameter - void logImpl(int line = __LINE__, string file = __FILE__, - string funcName = __FUNCTION__, - string prettyFuncName = __PRETTY_FUNCTION__, - string moduleName = __MODULE__, A...)(lazy A args) - if (args.length == 0 || (args.length > 0 && !is(A[0] : bool))) - { - synchronized (mutex) - { - if (isLoggingEnabled(ll, this.logLevel_, globalLogLevel)) - { - this.beginLogMsg(file, line, funcName, prettyFuncName, - moduleName, ll, thisTid, Clock.currTime, this); - - auto writer = MsgRange(this); - formatString(writer, args); - - this.finishLogMsg(); - - static if (ll == LogLevel.fatal) - this.fatalHandler_(); - } - } - } - - /** This function logs data to the used `Logger` depending on a - condition. - - In order for the resulting log message to be logged the `LogLevel` must - be greater or equal than the `LogLevel` of the used `Logger` and - must be greater or equal than the global `LogLevel` additionally the - condition passed must be `true`. - - Params: - condition = The condition must be `true` for the data to be logged. - args = The data that should be logged. - - Example: - -------------------- - auto s = new FileLogger(stdout); - s.trace(true, 1337, "is number"); - s.info(false, 1337, "is number"); - s.error(true, 1337, "is number"); - s.critical(false, 1337, "is number"); - s.fatal(true, 1337, "is number"); - -------------------- - */ - pragma(inline, true) // LDC: Must inline because of __FILE__ as template parameter - void logImpl(int line = __LINE__, string file = __FILE__, - string funcName = __FUNCTION__, - string prettyFuncName = __PRETTY_FUNCTION__, - string moduleName = __MODULE__, A...)(lazy bool condition, - lazy A args) - { - synchronized (mutex) - { - if (isLoggingEnabled(ll, this.logLevel_, globalLogLevel, - condition)) - { - this.beginLogMsg(file, line, funcName, prettyFuncName, - moduleName, ll, thisTid, Clock.currTime, this); - - auto writer = MsgRange(this); - formatString(writer, args); - - this.finishLogMsg(); - - static if (ll == LogLevel.fatal) - this.fatalHandler_(); - } - } - } - - /** This function logs data to the used `Logger` in a - `printf`-style manner. - - In order for the resulting log message to be logged the `LogLevel` - must be greater or equal than the `LogLevel` of the used `Logger` - and must be greater or equal than the global `LogLevel` additionally - the passed condition must be `true`. - - Params: - condition = The condition must be `true` for the data to be logged. - msg = The `printf`-style string. - args = The data that should be logged. - - Example: - -------------------- - auto s = new FileLogger(stderr); - s.tracef(true, "is number %d", 1); - s.infof(true, "is number %d", 2); - s.errorf(false, "is number %d", 3); - s.criticalf(someFunc(), "is number %d", 4); - s.fatalf(true, "is number %d", 5); - -------------------- - */ - pragma(inline, true) // LDC: Must inline because of __FILE__ as template parameter - void logImplf(int line = __LINE__, string file = __FILE__, - string funcName = __FUNCTION__, - string prettyFuncName = __PRETTY_FUNCTION__, - string moduleName = __MODULE__, A...)(lazy bool condition, - lazy string msg, lazy A args) - { - synchronized (mutex) - { - import std.format.write : formattedWrite; - - if (isLoggingEnabled(ll, this.logLevel_, globalLogLevel, - condition)) - { - this.beginLogMsg(file, line, funcName, prettyFuncName, - moduleName, ll, thisTid, Clock.currTime, this); - - auto writer = MsgRange(this); - formattedWrite(writer, msg, args); - - this.finishLogMsg(); - - static if (ll == LogLevel.fatal) - this.fatalHandler_(); - } - } - } - - /** This function logs data to the used `Logger` in a - `printf`-style manner. - - In order for the resulting log message to be logged the `LogLevel` must - be greater or equal than the `LogLevel` of the used `Logger` and - must be greater or equal than the global `LogLevel`. - - Params: - msg = The `printf`-style string. - args = The data that should be logged. - - Example: - -------------------- - auto s = new FileLogger(stderr); - s.tracef("is number %d", 1); - s.infof("is number %d", 2); - s.errorf("is number %d", 3); - s.criticalf("is number %d", 4); - s.fatalf("is number %d", 5); - -------------------- - */ - pragma(inline, true) // LDC: Must inline because of __FILE__ as template parameter - void logImplf(int line = __LINE__, string file = __FILE__, - string funcName = __FUNCTION__, - string prettyFuncName = __PRETTY_FUNCTION__, - string moduleName = __MODULE__, A...)(lazy string msg, lazy A args) - { - synchronized (mutex) - { - import std.format.write : formattedWrite; - - if (isLoggingEnabled(ll, this.logLevel_, globalLogLevel)) - { - this.beginLogMsg(file, line, funcName, prettyFuncName, - moduleName, ll, thisTid, Clock.currTime, this); - - auto writer = MsgRange(this); - formattedWrite(writer, msg, args); - - this.finishLogMsg(); - - static if (ll == LogLevel.fatal) - this.fatalHandler_(); - } - } - } - } - - /// Ditto - alias trace = memLogFunctions!(LogLevel.trace).logImpl; - /// Ditto - alias tracef = memLogFunctions!(LogLevel.trace).logImplf; - /// Ditto - alias info = memLogFunctions!(LogLevel.info).logImpl; - /// Ditto - alias infof = memLogFunctions!(LogLevel.info).logImplf; - /// Ditto - alias warning = memLogFunctions!(LogLevel.warning).logImpl; - /// Ditto - alias warningf = memLogFunctions!(LogLevel.warning).logImplf; - /// Ditto - alias error = memLogFunctions!(LogLevel.error).logImpl; - /// Ditto - alias errorf = memLogFunctions!(LogLevel.error).logImplf; - /// Ditto - alias critical = memLogFunctions!(LogLevel.critical).logImpl; - /// Ditto - alias criticalf = memLogFunctions!(LogLevel.critical).logImplf; - /// Ditto - alias fatal = memLogFunctions!(LogLevel.fatal).logImpl; - /// Ditto - alias fatalf = memLogFunctions!(LogLevel.fatal).logImplf; - - /** This method logs data with the `LogLevel` of the used `Logger`. - - This method takes a `bool` as first argument. In order for the - data to be processed the `bool` must be `true` and the `LogLevel` - of the Logger must be greater or equal to the global `LogLevel`. - - Params: - args = The data that should be logged. - condition = The condition must be `true` for the data to be logged. - args = The data that is to be logged. - - Returns: The logger used by the logging function as reference. - - Example: - -------------------- - auto l = new StdioLogger(); - l.log(1337); - -------------------- - */ - pragma(inline, true) // LDC: Must inline because of __FILE__ as template parameter - void log(int line = __LINE__, string file = __FILE__, - string funcName = __FUNCTION__, - string prettyFuncName = __PRETTY_FUNCTION__, - string moduleName = __MODULE__, A...)(const LogLevel ll, - lazy bool condition, lazy A args) - if (args.length != 1) - { - synchronized (mutex) - { - if (isLoggingEnabled(ll, this.logLevel_, globalLogLevel, condition)) - { - this.beginLogMsg(file, line, funcName, prettyFuncName, - moduleName, ll, thisTid, Clock.currTime, this); - - auto writer = MsgRange(this); - formatString(writer, args); - - this.finishLogMsg(); - - if (ll == LogLevel.fatal) - this.fatalHandler_(); - } - } - } - - /// Ditto - void log(T, string moduleName = __MODULE__)(const LogLevel ll, - lazy bool condition, lazy T args, int line = __LINE__, - string file = __FILE__, string funcName = __FUNCTION__, - string prettyFuncName = __PRETTY_FUNCTION__) - { - synchronized (mutex) - { - if (isLoggingEnabled(ll, this.logLevel_, globalLogLevel, condition)) - { - this.beginLogMsg(file, line, funcName, prettyFuncName, - moduleName, ll, thisTid, Clock.currTime, this); - auto writer = MsgRange(this); - formatString(writer, args); - - this.finishLogMsg(); - - if (ll == LogLevel.fatal) - this.fatalHandler_(); - } - } - } - - /** This function logs data to the used `Logger` with a specific - `LogLevel`. - - In order for the resulting log message to be logged the `LogLevel` - must be greater or equal than the `LogLevel` of the used `Logger` - and must be greater or equal than the global `LogLevel`. - - Params: - ll = The specific `LogLevel` used for logging the log message. - args = The data that should be logged. - - Example: - -------------------- - auto s = new FileLogger(stdout); - s.log(LogLevel.trace, 1337, "is number"); - s.log(LogLevel.info, 1337, "is number"); - s.log(LogLevel.warning, 1337, "is number"); - s.log(LogLevel.error, 1337, "is number"); - s.log(LogLevel.fatal, 1337, "is number"); - -------------------- - */ - pragma(inline, true) // LDC: Must inline because of __FILE__ as template parameter - void log(int line = __LINE__, string file = __FILE__, - string funcName = __FUNCTION__, - string prettyFuncName = __PRETTY_FUNCTION__, - string moduleName = __MODULE__, A...)(const LogLevel ll, lazy A args) - if ((args.length > 1 && !is(Unqual!(A[0]) : bool)) || args.length == 0) - { - synchronized (mutex) - { - if (isLoggingEnabled(ll, this.logLevel_, globalLogLevel)) - { - this.beginLogMsg(file, line, funcName, prettyFuncName, - moduleName, ll, thisTid, Clock.currTime, this); - - auto writer = MsgRange(this); - formatString(writer, args); - - this.finishLogMsg(); - - if (ll == LogLevel.fatal) - this.fatalHandler_(); - } - } - } - - /// Ditto - void log(T)(const LogLevel ll, lazy T args, int line = __LINE__, - string file = __FILE__, string funcName = __FUNCTION__, - string prettyFuncName = __PRETTY_FUNCTION__, - string moduleName = __MODULE__) - { - synchronized (mutex) - { - if (isLoggingEnabled(ll, this.logLevel_, globalLogLevel)) - { - this.beginLogMsg(file, line, funcName, prettyFuncName, - moduleName, ll, thisTid, Clock.currTime, this); - auto writer = MsgRange(this); - formatString(writer, args); - - this.finishLogMsg(); - - if (ll == LogLevel.fatal) - this.fatalHandler_(); - } - } - } - - /** This function logs data to the used `Logger` depending on a - explicitly passed condition with the `LogLevel` of the used - `Logger`. - - In order for the resulting log message to be logged the `LogLevel` - of the used `Logger` must be greater or equal than the global - `LogLevel` and the condition must be `true`. - - Params: - condition = The condition must be `true` for the data to be logged. - args = The data that should be logged. - - Example: - -------------------- - auto s = new FileLogger(stdout); - s.log(true, 1337, "is number"); - s.log(true, 1337, "is number"); - s.log(true, 1337, "is number"); - s.log(false, 1337, "is number"); - s.log(false, 1337, "is number"); - -------------------- - */ - pragma(inline, true) // LDC: Must inline because of __FILE__ as template parameter - void log(int line = __LINE__, string file = __FILE__, - string funcName = __FUNCTION__, - string prettyFuncName = __PRETTY_FUNCTION__, - string moduleName = __MODULE__, A...)(lazy bool condition, lazy A args) - if (args.length != 1) - { - synchronized (mutex) - { - if (isLoggingEnabled(this.logLevel_, this.logLevel_, - globalLogLevel, condition)) - { - this.beginLogMsg(file, line, funcName, prettyFuncName, - moduleName, this.logLevel_, thisTid, Clock.currTime, this); - - auto writer = MsgRange(this); - formatString(writer, args); - - this.finishLogMsg(); - - if (this.logLevel_ == LogLevel.fatal) - this.fatalHandler_(); - } - } - } - - /// Ditto - void log(T)(lazy bool condition, lazy T args, int line = __LINE__, - string file = __FILE__, string funcName = __FUNCTION__, - string prettyFuncName = __PRETTY_FUNCTION__, - string moduleName = __MODULE__) - { - synchronized (mutex) - { - if (isLoggingEnabled(this.logLevel_, this.logLevel_, globalLogLevel, - condition)) - { - this.beginLogMsg(file, line, funcName, prettyFuncName, - moduleName, this.logLevel_, thisTid, Clock.currTime, this); - auto writer = MsgRange(this); - formatString(writer, args); - - this.finishLogMsg(); - - if (this.logLevel_ == LogLevel.fatal) - this.fatalHandler_(); - } - } - } - - /** This function logs data to the used `Logger` with the `LogLevel` - of the used `Logger`. - - In order for the resulting log message to be logged the `LogLevel` - of the used `Logger` must be greater or equal than the global - `LogLevel`. - - Params: - args = The data that should be logged. - - Example: - -------------------- - auto s = new FileLogger(stdout); - s.log(1337, "is number"); - s.log(info, 1337, "is number"); - s.log(1337, "is number"); - s.log(1337, "is number"); - s.log(1337, "is number"); - -------------------- - */ - pragma(inline, true) // LDC: Must inline because of __FILE__ as template parameter - void log(int line = __LINE__, string file = __FILE__, - string funcName = __FUNCTION__, - string prettyFuncName = __PRETTY_FUNCTION__, - string moduleName = __MODULE__, A...)(lazy A args) - if ((args.length > 1 - && !is(Unqual!(A[0]) : bool) - && !is(immutable A[0] == immutable LogLevel)) - || args.length == 0) - { - synchronized (mutex) - { - if (isLoggingEnabled(this.logLevel_, this.logLevel_, - globalLogLevel)) - { - this.beginLogMsg(file, line, funcName, prettyFuncName, - moduleName, this.logLevel_, thisTid, Clock.currTime, this); - auto writer = MsgRange(this); - formatString(writer, args); - - this.finishLogMsg(); - - if (this.logLevel_ == LogLevel.fatal) - this.fatalHandler_(); - } - } - } - - /// Ditto - void log(T)(lazy T arg, int line = __LINE__, string file = __FILE__, - string funcName = __FUNCTION__, - string prettyFuncName = __PRETTY_FUNCTION__, - string moduleName = __MODULE__) - { - synchronized (mutex) - { - if (isLoggingEnabled(this.logLevel_, this.logLevel_, globalLogLevel)) - { - this.beginLogMsg(file, line, funcName, prettyFuncName, - moduleName, this.logLevel_, thisTid, Clock.currTime, this); - auto writer = MsgRange(this); - formatString(writer, arg); - - this.finishLogMsg(); - - if (this.logLevel_ == LogLevel.fatal) - this.fatalHandler_(); - } - } - } - - /** This function logs data to the used `Logger` with a specific - `LogLevel` and depending on a condition in a `printf`-style manner. - - In order for the resulting log message to be logged the `LogLevel` - must be greater or equal than the `LogLevel` of the used `Logger` - and must be greater or equal than the global `LogLevel` and the - condition must be `true`. - - Params: - ll = The specific `LogLevel` used for logging the log message. - condition = The condition must be `true` for the data to be logged. - msg = The format string used for this log call. - args = The data that should be logged. - - Example: - -------------------- - auto s = new FileLogger(stdout); - s.logf(LogLevel.trace, true ,"%d %s", 1337, "is number"); - s.logf(LogLevel.info, true ,"%d %s", 1337, "is number"); - s.logf(LogLevel.warning, true ,"%d %s", 1337, "is number"); - s.logf(LogLevel.error, false ,"%d %s", 1337, "is number"); - s.logf(LogLevel.fatal, true ,"%d %s", 1337, "is number"); - -------------------- - */ - pragma(inline, true) // LDC: Must inline because of __FILE__ as template parameter - void logf(int line = __LINE__, string file = __FILE__, - string funcName = __FUNCTION__, - string prettyFuncName = __PRETTY_FUNCTION__, - string moduleName = __MODULE__, A...)(const LogLevel ll, - lazy bool condition, lazy string msg, lazy A args) - { - synchronized (mutex) - { - import std.format.write : formattedWrite; - - if (isLoggingEnabled(ll, this.logLevel_, globalLogLevel, condition)) - { - this.beginLogMsg(file, line, funcName, prettyFuncName, - moduleName, ll, thisTid, Clock.currTime, this); - - auto writer = MsgRange(this); - formattedWrite(writer, msg, args); - - this.finishLogMsg(); - - if (ll == LogLevel.fatal) - this.fatalHandler_(); - } - } - } - - /** This function logs data to the used `Logger` with a specific - `LogLevel` in a `printf`-style manner. - - In order for the resulting log message to be logged the `LogLevel` - must be greater or equal than the `LogLevel` of the used `Logger` - and must be greater or equal than the global `LogLevel`. - - Params: - ll = The specific `LogLevel` used for logging the log message. - msg = The format string used for this log call. - args = The data that should be logged. - - Example: - -------------------- - auto s = new FileLogger(stdout); - s.logf(LogLevel.trace, "%d %s", 1337, "is number"); - s.logf(LogLevel.info, "%d %s", 1337, "is number"); - s.logf(LogLevel.warning, "%d %s", 1337, "is number"); - s.logf(LogLevel.error, "%d %s", 1337, "is number"); - s.logf(LogLevel.fatal, "%d %s", 1337, "is number"); - -------------------- - */ - pragma(inline, true) // LDC: Must inline because of __FILE__ as template parameter - void logf(int line = __LINE__, string file = __FILE__, - string funcName = __FUNCTION__, - string prettyFuncName = __PRETTY_FUNCTION__, - string moduleName = __MODULE__, A...)(const LogLevel ll, - lazy string msg, lazy A args) - { - synchronized (mutex) - { - import std.format.write : formattedWrite; - - if (isLoggingEnabled(ll, this.logLevel_, globalLogLevel)) - { - this.beginLogMsg(file, line, funcName, prettyFuncName, - moduleName, ll, thisTid, Clock.currTime, this); - - auto writer = MsgRange(this); - formattedWrite(writer, msg, args); - - this.finishLogMsg(); - - if (ll == LogLevel.fatal) - this.fatalHandler_(); - } - } - } - - /** This function logs data to the used `Logger` depending on a - condition with the `LogLevel` of the used `Logger` in a - `printf`-style manner. - - In order for the resulting log message to be logged the `LogLevel` - of the used `Logger` must be greater or equal than the global - `LogLevel` and the condition must be `true`. - - Params: - condition = The condition must be `true` for the data to be logged. - msg = The format string used for this log call. - args = The data that should be logged. - - Example: - -------------------- - auto s = new FileLogger(stdout); - s.logf(true ,"%d %s", 1337, "is number"); - s.logf(true ,"%d %s", 1337, "is number"); - s.logf(true ,"%d %s", 1337, "is number"); - s.logf(false ,"%d %s", 1337, "is number"); - s.logf(true ,"%d %s", 1337, "is number"); - -------------------- - */ - pragma(inline, true) // LDC: Must inline because of __FILE__ as template parameter - void logf(int line = __LINE__, string file = __FILE__, - string funcName = __FUNCTION__, - string prettyFuncName = __PRETTY_FUNCTION__, - string moduleName = __MODULE__, A...)(lazy bool condition, - lazy string msg, lazy A args) - { - synchronized (mutex) - { - import std.format.write : formattedWrite; - - if (isLoggingEnabled(this.logLevel_, this.logLevel_, globalLogLevel, - condition)) - { - this.beginLogMsg(file, line, funcName, prettyFuncName, - moduleName, this.logLevel_, thisTid, Clock.currTime, this); - - auto writer = MsgRange(this); - formattedWrite(writer, msg, args); - - this.finishLogMsg(); - - if (this.logLevel_ == LogLevel.fatal) - this.fatalHandler_(); - } - } - } - - /** This method logs data to the used `Logger` with the `LogLevel` - of the this `Logger` in a `printf`-style manner. - - In order for the data to be processed the `LogLevel` of the `Logger` - must be greater or equal to the global `LogLevel`. - - Params: - msg = The format string used for this log call. - args = The data that should be logged. - - Example: - -------------------- - auto s = new FileLogger(stdout); - s.logf("%d %s", 1337, "is number"); - s.logf("%d %s", 1337, "is number"); - s.logf("%d %s", 1337, "is number"); - s.logf("%d %s", 1337, "is number"); - s.logf("%d %s", 1337, "is number"); - -------------------- - */ - pragma(inline, true) // LDC: Must inline because of __FILE__ as template parameter - void logf(int line = __LINE__, string file = __FILE__, - string funcName = __FUNCTION__, - string prettyFuncName = __PRETTY_FUNCTION__, - string moduleName = __MODULE__, A...)(lazy string msg, lazy A args) - { - synchronized (mutex) - { - import std.format.write : formattedWrite; - - if (isLoggingEnabled(this.logLevel_, this.logLevel_, - globalLogLevel)) - { - this.beginLogMsg(file, line, funcName, prettyFuncName, - moduleName, this.logLevel_, thisTid, Clock.currTime, this); - - auto writer = MsgRange(this); - formattedWrite(writer, msg, args); - - this.finishLogMsg(); - - if (this.logLevel_ == LogLevel.fatal) - this.fatalHandler_(); - } - } - } - - private void delegate() @safe fatalHandler_; - private shared LogLevel logLevel_ = LogLevel.info; - private Mutex mutex; - - protected Appender!string msgAppender; - protected LogEntry header; -} - -// Thread Global - -private shared Logger stdSharedDefaultLogger; -private shared Logger stdSharedLogger; -private shared LogLevel stdLoggerGlobalLogLevel = LogLevel.all; - -/* This method returns the global default Logger. - * Marked @trusted because of excessive reliance on __gshared data - */ -private @property shared(Logger) defaultSharedLoggerImpl() @trusted -{ - import core.lifetime : emplace; - import std.stdio : stderr; - - __gshared align(__traits(classInstanceAlignment, FileLogger)) - void[__traits(classInstanceSize, FileLogger)] _buffer = void; - - import std.concurrency : initOnce; - initOnce!stdSharedDefaultLogger({ - auto buffer = cast(ubyte[]) _buffer; - return cast(shared) emplace!(FileLogger)(buffer, stderr, LogLevel.info); - }()); - - return atomicLoad(stdSharedDefaultLogger); -} - -/** This property sets and gets the default `Logger`. Unless set to another -logger by the user, the default logger's log level is LogLevel.info. - -Example: -------------- -sharedLog = new FileLogger(yourFile); -------------- -The example sets a new `FileLogger` as new `sharedLog`. - -If at some point you want to use the original default logger again, you can -use $(D sharedLog = null;). This will put back the original. - -Note: -While getting and setting `sharedLog` is thread-safe, it has to be considered -that the returned reference is only a current snapshot and in the following -code, you must make sure no other thread reassigns to it between reading and -writing `sharedLog`. - -`sharedLog` is only thread-safe if the used `Logger` is thread-safe. -The default `Logger` is thread-safe. -------------- -if (sharedLog !is myLogger) - sharedLog = new myLogger; -------------- -*/ -@property shared(Logger) sharedLog() @safe -{ - // If we have set up our own logger use that - if (auto logger = atomicLoad!(MemoryOrder.seq)(stdSharedLogger)) - { - return atomicLoad(logger); - } - else - { - // Otherwise resort to the default logger - return defaultSharedLoggerImpl; - } -} - -/// Ditto -@property void sharedLog(shared(Logger) logger) @safe -{ - atomicStore!(MemoryOrder.seq)(stdSharedLogger, atomicLoad(logger)); -} - -/** These methods get and set the global `LogLevel`. - -Every log message with a `LogLevel` lower than the global `LogLevel` -will be discarded before it reaches `writeLogMessage` method of any -`Logger`. -*/ -/* Implementation note: -For any public logging call, the global log level shall only be queried once on -entry. Otherwise when another thread changes the level, we would work with -different levels at different spots in the code. -*/ -@property LogLevel globalLogLevel() @safe @nogc -{ - return trustedLoad(stdLoggerGlobalLogLevel); -} - -/// Ditto -@property void globalLogLevel(LogLevel ll) @safe -{ - trustedStore(stdLoggerGlobalLogLevel, ll); -} - -// Thread Local - -/** The `StdForwardLogger` will always forward anything to the sharedLog. - -The `StdForwardLogger` will not throw if data is logged with $(D -LogLevel.fatal). -*/ -class StdForwardLogger : Logger -{ - /** The default constructor for the `StdForwardLogger`. - - Params: - lv = The `LogLevel` for the `MultiLogger`. By default the $(D - LogLevel) is `all`. - */ - this(const LogLevel lv = LogLevel.all) @safe - { - super(lv); - this.fatalHandler = delegate() {}; - } - - override protected void writeLogMsg(ref LogEntry payload) @trusted - { - synchronized (sharedLog.mutex) - { - (cast() sharedLog).forwardMsg(payload); - } - } -} - -/// -@safe unittest -{ - auto nl1 = new StdForwardLogger(LogLevel.all); -} - -@safe unittest -{ - import core.thread : Thread, msecs; - - static class RaceLogger : Logger - { - int value; - this() @safe shared - { - super(LogLevel.init); - } - override void writeLogMsg(ref LogEntry payload) @safe - { - import core.thread : Thread, msecs; - if (payload.msg == "foo") - { - value = 42; - () @trusted { Thread.sleep(100.msecs); }(); - assert(value == 42, "Another thread changed the value"); - } - else - { - () @trusted { Thread.sleep(50.msecs); } (); - value = 13; - } - } - } - - auto oldSharedLog = sharedLog; - - sharedLog = new shared RaceLogger; - scope(exit) - { - sharedLog = oldSharedLog; - } - Thread toWaitFor; - () @trusted { toWaitFor = new Thread(() { log("foo"); }).start(); }(); - log("bar"); - - () @trusted - { - toWaitFor.join(); - }(); -} - -/** This `LogLevel` is unqiue to every thread. - -The thread local `Logger` will use this `LogLevel` to filter log calls -every same way as presented earlier. -*/ -//public LogLevel threadLogLevel = LogLevel.all; -private Logger stdLoggerThreadLogger; -private Logger stdLoggerDefaultThreadLogger; - -/* This method returns the thread local default Logger. -*/ -private @property Logger stdThreadLocalLogImpl() @trusted -{ - import core.lifetime : emplace; - - static align(__traits(classInstanceAlignment, StdForwardLogger)) - void[__traits(classInstanceSize, StdForwardLogger)] buffer; - - if (stdLoggerDefaultThreadLogger is null) - { - stdLoggerDefaultThreadLogger = emplace!StdForwardLogger(buffer, LogLevel.all); - } - return stdLoggerDefaultThreadLogger; -} - -/** This function returns a thread unique `Logger`, that by default -propagates all data logged to it to the `sharedLog`. - -These properties can be used to set and get this `Logger`. Every -modification to this `Logger` will only be visible in the thread the -modification has been done from. - -This `Logger` is called by the free standing log functions. This allows to -create thread local redirections and still use the free standing log -functions. -*/ -@property Logger stdThreadLocalLog() @safe -{ - // If we have set up our own logger use that - if (auto logger = stdLoggerThreadLogger) - return logger; - else - // Otherwise resort to the default logger - return stdThreadLocalLogImpl; -} - -/// Ditto -@property void stdThreadLocalLog(Logger logger) @safe -{ - stdLoggerThreadLogger = logger; -} - -/// Ditto -@system unittest -{ - import std.logger.filelogger : FileLogger; - import std.file : deleteme, remove; - Logger l = stdThreadLocalLog; - stdThreadLocalLog = new FileLogger(deleteme ~ "-someFile.log"); - scope(exit) remove(deleteme ~ "-someFile.log"); - - auto tempLog = stdThreadLocalLog; - stdThreadLocalLog = l; - destroy(tempLog); -} - -@safe unittest -{ - LogLevel ll = globalLogLevel; - globalLogLevel = LogLevel.fatal; - assert(globalLogLevel == LogLevel.fatal); - globalLogLevel = ll; -} - -package class TestLogger : Logger -{ - int line = -1; - string file = null; - string func = null; - string prettyFunc = null; - string msg = null; - LogLevel lvl; - - this(const LogLevel lv = LogLevel.all) @safe - { - super(lv); - } - - override protected void writeLogMsg(ref LogEntry payload) @safe - { - this.line = payload.line; - this.file = payload.file; - this.func = payload.funcName; - this.prettyFunc = payload.prettyFuncName; - this.lvl = payload.logLevel; - this.msg = payload.msg; - } -} - -version (StdUnittest) private void testFuncNames(Logger logger) @safe -{ - string s = "I'm here"; - logger.log(s); -} - -@safe unittest -{ - auto tl1 = new TestLogger(); - testFuncNames(tl1); - assert(tl1.func == "std.logger.core.testFuncNames", tl1.func); - assert(tl1.prettyFunc == - "void std.logger.core.testFuncNames(Logger logger) @safe", - tl1.prettyFunc); - assert(tl1.msg == "I'm here", tl1.msg); -} - -@safe unittest -{ - auto tl1 = new TestLogger(LogLevel.all); - tl1.log(); - assert(tl1.line == __LINE__ - 1); - tl1.log(true); - assert(tl1.line == __LINE__ - 1); - tl1.log(false); - assert(tl1.line == __LINE__ - 3); - tl1.log(LogLevel.info); - assert(tl1.line == __LINE__ - 1); - tl1.log(LogLevel.off); - assert(tl1.line == __LINE__ - 3); - tl1.log(LogLevel.info, true); - assert(tl1.line == __LINE__ - 1); - tl1.log(LogLevel.info, false); - assert(tl1.line == __LINE__ - 3); - - auto oldunspecificLogger = sharedLog; - scope(exit) { - sharedLog = atomicLoad(oldunspecificLogger); - } - - () @trusted { - sharedLog = cast(shared) tl1; - }(); - - log(); - assert(tl1.line == __LINE__ - 1); - - log(LogLevel.info); - assert(tl1.line == __LINE__ - 1); - - log(true); - assert(tl1.line == __LINE__ - 1); - - log(LogLevel.warning, true); - assert(tl1.line == __LINE__ - 1); - - trace(); - assert(tl1.line == __LINE__ - 1); -} - -@safe unittest -{ - import std.logger.multilogger : MultiLogger; - - auto tl1 = new TestLogger; - auto tl2 = new TestLogger; - - auto ml = new MultiLogger(); - ml.insertLogger("one", tl1); - ml.insertLogger("two", tl2); - - string msg = "Hello Logger World"; - ml.log(msg); - int lineNumber = __LINE__ - 1; - assert(tl1.msg == msg); - assert(tl1.line == lineNumber); - assert(tl2.msg == msg); - assert(tl2.line == lineNumber); - - ml.removeLogger("one"); - ml.removeLogger("two"); - auto n = ml.removeLogger("one"); - assert(n is null); -} - -@safe unittest -{ - bool errorThrown = false; - auto tl = new TestLogger; - auto dele = delegate() { - errorThrown = true; - }; - tl.fatalHandler = dele; - tl.fatal(); - assert(errorThrown); -} - -@safe unittest -{ - import std.conv : to; - import std.exception : assertThrown, assertNotThrown; - import std.format : format; - - auto l = new TestLogger(LogLevel.all); - string msg = "Hello Logger World"; - l.log(msg); - int lineNumber = __LINE__ - 1; - assert(l.msg == msg); - assert(l.line == lineNumber); - assert(l.logLevel == LogLevel.all); - - l.log(true, msg); - lineNumber = __LINE__ - 1; - assert(l.msg == msg, l.msg); - assert(l.line == lineNumber); - assert(l.logLevel == LogLevel.all); - - l.log(false, msg); - assert(l.msg == msg); - assert(l.line == lineNumber, to!string(l.line)); - assert(l.logLevel == LogLevel.all); - - msg = "%s Another message"; - l.logf(msg, "Yet"); - lineNumber = __LINE__ - 1; - assert(l.msg == msg.format("Yet")); - assert(l.line == lineNumber); - assert(l.logLevel == LogLevel.all); - - l.logf(true, msg, "Yet"); - lineNumber = __LINE__ - 1; - assert(l.msg == msg.format("Yet")); - assert(l.line == lineNumber); - assert(l.logLevel == LogLevel.all); - - l.logf(false, msg, "Yet"); - assert(l.msg == msg.format("Yet")); - assert(l.line == lineNumber); - assert(l.logLevel == LogLevel.all); - - () @trusted { - assertThrown!Throwable(l.logf(LogLevel.fatal, msg, "Yet")); - } (); - lineNumber = __LINE__ - 2; - assert(l.msg == msg.format("Yet")); - assert(l.line == lineNumber); - assert(l.logLevel == LogLevel.all); - - () @trusted { - assertThrown!Throwable(l.logf(LogLevel.fatal, true, msg, "Yet")); - } (); - lineNumber = __LINE__ - 2; - assert(l.msg == msg.format("Yet")); - assert(l.line == lineNumber); - assert(l.logLevel == LogLevel.all); - - assertNotThrown(l.logf(LogLevel.fatal, false, msg, "Yet")); - assert(l.msg == msg.format("Yet")); - assert(l.line == lineNumber); - assert(l.logLevel == LogLevel.all); - - Logger oldunspecificLogger; - () @trusted { - oldunspecificLogger = cast() sharedLog; - }(); - - assert(oldunspecificLogger.logLevel == LogLevel.info, - to!string(oldunspecificLogger.logLevel)); - - assert(l.logLevel == LogLevel.all); - - () @trusted { - sharedLog = cast(shared) l; - }(); - - assert(globalLogLevel == LogLevel.all, - to!string(globalLogLevel)); - - scope(exit) - { - () @trusted { - sharedLog = atomicLoad(cast(shared) oldunspecificLogger); - }(); - } - - () @trusted { - assert((cast() sharedLog).logLevel == LogLevel.all); - }(); - - assert(stdThreadLocalLog.logLevel == LogLevel.all); - assert(globalLogLevel == LogLevel.all); - - msg = "Another message"; - log(msg); - lineNumber = __LINE__ - 1; - assert(l.logLevel == LogLevel.all); - assert(l.line == lineNumber, to!string(l.line)); - assert(l.msg == msg, l.msg); - - log(true, msg); - lineNumber = __LINE__ - 1; - assert(l.msg == msg); - assert(l.line == lineNumber); - assert(l.logLevel == LogLevel.all); - - log(false, msg); - assert(l.msg == msg); - assert(l.line == lineNumber); - assert(l.logLevel == LogLevel.all); - - msg = "%s Another message"; - logf(msg, "Yet"); - lineNumber = __LINE__ - 1; - assert(l.msg == msg.format("Yet")); - assert(l.line == lineNumber); - assert(l.logLevel == LogLevel.all); - - logf(true, msg, "Yet"); - lineNumber = __LINE__ - 1; - assert(l.msg == msg.format("Yet")); - assert(l.line == lineNumber); - assert(l.logLevel == LogLevel.all); - - logf(false, msg, "Yet"); - assert(l.msg == msg.format("Yet")); - assert(l.line == lineNumber); - assert(l.logLevel == LogLevel.all); - - msg = "%s Another message"; - () @trusted { - assertThrown!Throwable(logf(LogLevel.fatal, msg, "Yet")); - } (); - lineNumber = __LINE__ - 2; - assert(l.msg == msg.format("Yet"), l.msg); - assert(l.line == lineNumber); - assert(l.logLevel == LogLevel.all); - - () @trusted { - assertThrown!Throwable(logf(LogLevel.fatal, true, msg, "Yet")); - } (); - lineNumber = __LINE__ - 2; - assert(l.msg == msg.format("Yet")); - assert(l.line == lineNumber); - assert(l.logLevel == LogLevel.all); - - assertNotThrown(logf(LogLevel.fatal, false, msg, "Yet")); - assert(l.msg == msg.format("Yet")); - assert(l.line == lineNumber); - assert(l.logLevel == LogLevel.all); -} - -@system unittest // default logger -{ - import std.file : deleteme, exists, remove; - import std.stdio : File; - import std.string : indexOf; - - string filename = deleteme ~ __FUNCTION__ ~ ".tempLogFile"; - FileLogger l = new FileLogger(filename); - auto oldunspecificLogger = sharedLog; - - sharedLog = cast(shared) l; - - scope(exit) - { - remove(filename); - assert(!exists(filename)); - sharedLog = atomicLoad(oldunspecificLogger); - globalLogLevel = LogLevel.all; - } - - string notWritten = "this should not be written to file"; - string written = "this should be written to file"; - - globalLogLevel = LogLevel.critical; - assert(globalLogLevel == LogLevel.critical); - - log(LogLevel.warning, notWritten); - log(LogLevel.critical, written); - - l.file.flush(); - l.file.close(); - - auto file = File(filename, "r"); - assert(!file.eof); - - string readLine = file.readln(); - assert(readLine.indexOf(written) != -1, readLine); - assert(readLine.indexOf(notWritten) == -1, readLine); - file.close(); -} - -@system unittest -{ - import std.file : deleteme, remove; - import std.stdio : File; - import std.string : indexOf; - - string filename = deleteme ~ __FUNCTION__ ~ ".tempLogFile"; - auto oldunspecificLogger = sharedLog; - - scope(exit) - { - remove(filename); - sharedLog = atomicLoad(oldunspecificLogger); - globalLogLevel = LogLevel.all; - } - - string notWritten = "this should not be written to file"; - string written = "this should be written to file"; - - auto l = new FileLogger(filename); - sharedLog = cast(shared) l; - - () @trusted { - (cast() sharedLog).logLevel = LogLevel.critical; - }(); - - log(LogLevel.error, false, notWritten); - log(LogLevel.critical, true, written); - destroy(l); - - auto file = File(filename, "r"); - auto readLine = file.readln(); - assert(!readLine.empty, readLine); - assert(readLine.indexOf(written) != -1); - assert(readLine.indexOf(notWritten) == -1); - file.close(); -} - -@safe unittest -{ - import std.conv : to; - - auto tl = new TestLogger(LogLevel.all); - int l = __LINE__; - tl.info("a"); - assert(tl.line == l+1); - assert(tl.msg == "a"); - assert(tl.logLevel == LogLevel.all); - assert(globalLogLevel == LogLevel.all); - l = __LINE__; - tl.trace("b"); - assert(tl.msg == "b", tl.msg); - assert(tl.line == l+1, to!string(tl.line)); -} - -// testing possible log conditions -@safe unittest -{ - import std.conv : to; - import std.format : format; - import std.string : indexOf; - - auto oldunspecificLogger = sharedLog; - - auto mem = new TestLogger; - mem.fatalHandler = delegate() {}; - - () @trusted { - sharedLog = cast(shared) mem; - }(); - - scope(exit) - { - sharedLog = atomicLoad(oldunspecificLogger); - globalLogLevel = LogLevel.all; - } - - int value = 0; - foreach (gll; [cast(LogLevel) LogLevel.all, LogLevel.trace, - LogLevel.info, LogLevel.warning, LogLevel.error, - LogLevel.critical, LogLevel.fatal, LogLevel.off]) - { - - globalLogLevel = gll; - - foreach (ll; [cast(LogLevel) LogLevel.all, LogLevel.trace, - LogLevel.info, LogLevel.warning, LogLevel.error, - LogLevel.critical, LogLevel.fatal, LogLevel.off]) - { - - mem.logLevel = ll; - - foreach (cond; [true, false]) - { - foreach (condValue; [true, false]) - { - foreach (memOrG; [true, false]) - { - foreach (prntf; [true, false]) - { - foreach (ll2; [cast(LogLevel) LogLevel.all, LogLevel.trace, - LogLevel.info, LogLevel.warning, - LogLevel.error, LogLevel.critical, - LogLevel.fatal, LogLevel.off]) - { - foreach (singleMulti; 0 .. 2) - { - int lineCall; - mem.msg = "-1"; - if (memOrG) - { - if (prntf) - { - if (cond) - { - if (singleMulti == 0) - { - mem.logf(ll2, condValue, "%s", - value); - lineCall = __LINE__; - } - else - { - mem.logf(ll2, condValue, - "%d %d", value, value); - lineCall = __LINE__; - } - } - else - { - if (singleMulti == 0) - { - mem.logf(ll2, "%s", value); - lineCall = __LINE__; - } - else - { - mem.logf(ll2, "%d %d", - value, value); - lineCall = __LINE__; - } - } - } - else - { - if (cond) - { - if (singleMulti == 0) - { - mem.log(ll2, condValue, - to!string(value)); - lineCall = __LINE__; - } - else - { - mem.log(ll2, condValue, - to!string(value), value); - lineCall = __LINE__; - } - } - else - { - if (singleMulti == 0) - { - mem.log(ll2, to!string(value)); - lineCall = __LINE__; - } - else - { - mem.log(ll2, - to!string(value), - value); - lineCall = __LINE__; - } - } - } - } - else - { - if (prntf) - { - if (cond) - { - if (singleMulti == 0) - { - logf(ll2, condValue, "%s", - value); - lineCall = __LINE__; - } - else - { - logf(ll2, condValue, - "%s %d", value, value); - lineCall = __LINE__; - } - } - else - { - if (singleMulti == 0) - { - logf(ll2, "%s", value); - lineCall = __LINE__; - } - else - { - logf(ll2, "%s %s", value, - value); - lineCall = __LINE__; - } - } - } - else - { - if (cond) - { - if (singleMulti == 0) - { - log(ll2, condValue, - to!string(value)); - lineCall = __LINE__; - } - else - { - log(ll2, condValue, value, - to!string(value)); - lineCall = __LINE__; - } - } - else - { - if (singleMulti == 0) - { - log(ll2, to!string(value)); - lineCall = __LINE__; - } - else - { - log(ll2, value, - to!string(value)); - lineCall = __LINE__; - } - } - } - } - - string valueStr = to!string(value); - ++value; - - bool ll2Off = (ll2 != LogLevel.off); - bool gllOff = (gll != LogLevel.off); - bool llOff = (ll != LogLevel.off); - bool condFalse = (cond ? condValue : true); - bool ll2VSgll = (ll2 >= gll); - bool ll2VSll = (ll2 >= ll); - - bool shouldLog = ll2Off && gllOff && llOff - && condFalse && ll2VSgll && ll2VSll; - - /* - writefln( - "go(%b) ll2o(%b) c(%b) lg(%b) ll(%b) s(%b)" - , gll != LogLevel.off, ll2 != LogLevel.off, - cond ? condValue : true, - ll2 >= gll, ll2 >= ll, shouldLog); - */ - - - if (shouldLog) - { - assert(mem.msg.indexOf(valueStr) != -1, - format( - "lineCall(%d) ll2Off(%u) gll(%u) ll(%u) ll2(%u) " ~ - "cond(%b) condValue(%b)" ~ - " memOrG(%b) shouldLog(%b) %s == %s" ~ - " %b %b %b %b %b", - lineCall, ll2Off, gll, ll, ll2, cond, - condValue, memOrG, shouldLog, mem.msg, - valueStr, gllOff, llOff, condFalse, - ll2VSgll, ll2VSll - )); - } - else - { - assert(mem.msg.indexOf(valueStr), - format( - "lineCall(%d) ll2Off(%u) gll(%u) ll(%u) ll2(%u) " ~ - "cond(%b) condValue(%b)" ~ - " memOrG(%b) shouldLog(%b) %s == %s" ~ - " %b %b %b %b %b", - lineCall, ll2Off, gll, ll, ll2, cond, - condValue, memOrG, shouldLog, mem.msg, - valueStr, gllOff, llOff, condFalse, - ll2VSgll, ll2VSll - )); - } - } - } - } - } - } - } - } - } -} - -// more testing -@safe unittest -{ - import std.conv : to; - import std.format : format; - import std.string : indexOf; - - auto oldunspecificLogger = sharedLog; - - auto mem = new TestLogger; - mem.fatalHandler = delegate() {}; - - () @trusted { - sharedLog = cast(shared) mem; - }(); - - scope(exit) - { - sharedLog = atomicLoad(oldunspecificLogger); - globalLogLevel = LogLevel.all; - } - - int value = 0; - foreach (gll; [cast(LogLevel) LogLevel.all, LogLevel.trace, - LogLevel.info, LogLevel.warning, LogLevel.error, - LogLevel.critical, LogLevel.fatal, LogLevel.off]) - { - - globalLogLevel = gll; - - foreach (ll; [cast(LogLevel) LogLevel.all, LogLevel.trace, - LogLevel.info, LogLevel.warning, LogLevel.error, - LogLevel.critical, LogLevel.fatal, LogLevel.off]) - { - mem.logLevel = ll; - - foreach (tll; [cast(LogLevel) LogLevel.all, LogLevel.trace, - LogLevel.info, LogLevel.warning, LogLevel.error, - LogLevel.critical, LogLevel.fatal, LogLevel.off]) - { - stdThreadLocalLog.logLevel = tll; - - foreach (cond; [true, false]) - { - foreach (condValue; [true, false]) - { - foreach (memOrG; [true, false]) - { - foreach (prntf; [true, false]) - { - foreach (singleMulti; 0 .. 2) - { - int lineCall; - mem.msg = "-1"; - if (memOrG) - { - if (prntf) - { - if (cond) - { - if (singleMulti == 0) - { - mem.logf(condValue, "%s", - value); - lineCall = __LINE__; - } - else - { - mem.logf(condValue, - "%d %d", value, value); - lineCall = __LINE__; - } - } - else - { - if (singleMulti == 0) - { - mem.logf("%s", value); - lineCall = __LINE__; - } - else - { - mem.logf("%d %d", - value, value); - lineCall = __LINE__; - } - } - } - else - { - if (cond) - { - if (singleMulti == 0) - { - mem.log(condValue, - to!string(value)); - lineCall = __LINE__; - } - else - { - mem.log(condValue, - to!string(value), value); - lineCall = __LINE__; - } - } - else - { - if (singleMulti == 0) - { - mem.log(to!string(value)); - lineCall = __LINE__; - } - else - { - mem.log(to!string(value), - value); - lineCall = __LINE__; - } - } - } - } - else - { - if (prntf) - { - if (cond) - { - if (singleMulti == 0) - { - logf(condValue, "%s", value); - lineCall = __LINE__; - } - else - { - logf(condValue, "%s %d", value, - value); - lineCall = __LINE__; - } - } - else - { - if (singleMulti == 0) - { - logf("%s", value); - lineCall = __LINE__; - } - else - { - logf("%s %s", value, value); - lineCall = __LINE__; - } - } - } - else - { - if (cond) - { - if (singleMulti == 0) - { - log(condValue, - to!string(value)); - lineCall = __LINE__; - } - else - { - log(condValue, value, - to!string(value)); - lineCall = __LINE__; - } - } - else - { - if (singleMulti == 0) - { - log(to!string(value)); - lineCall = __LINE__; - } - else - { - log(value, to!string(value)); - lineCall = __LINE__; - } - } - } - } - - string valueStr = to!string(value); - ++value; - - bool gllOff = (gll != LogLevel.off); - bool llOff = (ll != LogLevel.off); - bool tllOff = (tll != LogLevel.off); - bool llVSgll = (ll >= gll); - bool tllVSll = - (stdThreadLocalLog.logLevel >= ll); - bool condFalse = (cond ? condValue : true); - - bool shouldLog = gllOff && llOff - && (memOrG ? true : tllOff) - && (memOrG ? - (ll >= gll) : - (tll >= gll && tll >= ll)) - && condFalse; - - if (shouldLog) - { - assert(mem.msg.indexOf(valueStr) != -1, - format("\ngll(%s) ll(%s) tll(%s) " ~ - "cond(%s) condValue(%s) " ~ - "memOrG(%s) prntf(%s) " ~ - "singleMulti(%s)", - gll, ll, tll, cond, condValue, - memOrG, prntf, singleMulti) - ~ format(" gllOff(%s) llOff(%s) " ~ - "llVSgll(%s) tllVSll(%s) " ~ - "tllOff(%s) condFalse(%s) " - ~ "shoudlLog(%s)", - gll != LogLevel.off, - ll != LogLevel.off, llVSgll, - tllVSll, tllOff, condFalse, - shouldLog) - ~ format("msg(%s) line(%s) " ~ - "lineCall(%s) valueStr(%s)", - mem.msg, mem.line, lineCall, - valueStr) - ); - } - else - { - assert(mem.msg.indexOf(valueStr) == -1, - format("\ngll(%s) ll(%s) tll(%s) " ~ - "cond(%s) condValue(%s) " ~ - "memOrG(%s) prntf(%s) " ~ - "singleMulti(%s)", - gll, ll, tll, cond, condValue, - memOrG, prntf, singleMulti) - ~ format(" gllOff(%s) llOff(%s) " ~ - "llVSgll(%s) tllVSll(%s) " ~ - "tllOff(%s) condFalse(%s) " - ~ "shoudlLog(%s)", - gll != LogLevel.off, - ll != LogLevel.off, llVSgll, - tllVSll, tllOff, condFalse, - shouldLog) - ~ format("msg(%s) line(%s) " ~ - "lineCall(%s) valueStr(%s)", - mem.msg, mem.line, lineCall, - valueStr) - ); - } - } - } - } - } - } - } - } - } -} - -// testing more possible log conditions -@safe unittest -{ - bool fatalLog; - auto mem = new TestLogger; - mem.fatalHandler = delegate() { fatalLog = true; }; - auto oldunspecificLogger = sharedLog; - - stdThreadLocalLog.logLevel = LogLevel.all; - - () @trusted { - sharedLog = cast(shared) mem; - }(); - - scope(exit) - { - sharedLog = atomicLoad(oldunspecificLogger); - globalLogLevel = LogLevel.all; - } - - foreach (gll; [cast(LogLevel) LogLevel.all, LogLevel.trace, - LogLevel.info, LogLevel.warning, LogLevel.error, - LogLevel.critical, LogLevel.fatal, LogLevel.off]) - { - - globalLogLevel = gll; - - foreach (ll; [cast(LogLevel) LogLevel.all, LogLevel.trace, - LogLevel.info, LogLevel.warning, LogLevel.error, - LogLevel.critical, LogLevel.fatal, LogLevel.off]) - { - mem.logLevel = ll; - - foreach (tll; [cast(LogLevel) LogLevel.all, LogLevel.trace, - LogLevel.info, LogLevel.warning, LogLevel.error, - LogLevel.critical, LogLevel.fatal, LogLevel.off]) - { - stdThreadLocalLog.logLevel = tll; - - foreach (cond; [true, false]) - { - assert(globalLogLevel == gll); - assert(mem.logLevel == ll); - - bool gllVSll = LogLevel.trace >= globalLogLevel; - bool llVSgll = ll >= globalLogLevel; - bool lVSll = LogLevel.trace >= ll; - bool gllOff = globalLogLevel != LogLevel.off; - bool llOff = mem.logLevel != LogLevel.off; - bool tllOff = stdThreadLocalLog.logLevel != LogLevel.off; - bool tllVSll = tll >= ll; - bool tllVSgll = tll >= gll; - bool lVSgll = LogLevel.trace >= tll; - - bool test = llVSgll && gllVSll && lVSll && gllOff && llOff && cond; - bool testG = gllOff && llOff && tllOff && lVSgll && tllVSll && tllVSgll && cond; - - mem.line = -1; - /* - writefln("gll(%3u) ll(%3u) cond(%b) test(%b)", - gll, ll, cond, test); - writefln("%b %b %b %b %b %b test2(%b)", llVSgll, gllVSll, lVSll, - gllOff, llOff, cond, test2); - */ - - mem.trace(__LINE__); int line = __LINE__; - assert(test ? mem.line == line : true); line = -1; - - trace(__LINE__); line = __LINE__; - assert(testG ? mem.line == line : true); line = -1; - - mem.trace(cond, __LINE__); line = __LINE__; - assert(test ? mem.line == line : true); line = -1; - - trace(cond, __LINE__); line = __LINE__; - assert(testG ? mem.line == line : true); line = -1; - - mem.tracef("%d", __LINE__); line = __LINE__; - assert(test ? mem.line == line : true); line = -1; - - tracef("%d", __LINE__); line = __LINE__; - assert(testG ? mem.line == line : true); line = -1; - - mem.tracef(cond, "%d", __LINE__); line = __LINE__; - assert(test ? mem.line == line : true); line = -1; - - tracef(cond, "%d", __LINE__); line = __LINE__; - assert(testG ? mem.line == line : true); line = -1; - - llVSgll = ll >= globalLogLevel; - lVSll = LogLevel.info >= ll; - lVSgll = LogLevel.info >= tll; - test = llVSgll && gllVSll && lVSll && gllOff && llOff && cond; - testG = gllOff && llOff && tllOff && tllVSll && tllVSgll && - lVSgll && cond; - - mem.info(__LINE__); line = __LINE__; - assert(test ? mem.line == line : true); line = -1; - - info(__LINE__); line = __LINE__; - assert(testG ? mem.line == line : true); line = -1; - - mem.info(cond, __LINE__); line = __LINE__; - assert(test ? mem.line == line : true); line = -1; - - info(cond, __LINE__); line = __LINE__; - assert(testG ? mem.line == line : true); line = -1; - - mem.infof("%d", __LINE__); line = __LINE__; - assert(test ? mem.line == line : true); line = -1; - - infof("%d", __LINE__); line = __LINE__; - assert(testG ? mem.line == line : true); line = -1; - - mem.infof(cond, "%d", __LINE__); line = __LINE__; - assert(test ? mem.line == line : true); line = -1; - - infof(cond, "%d", __LINE__); line = __LINE__; - assert(testG ? mem.line == line : true); line = -1; - - llVSgll = ll >= globalLogLevel; - lVSll = LogLevel.warning >= ll; - lVSgll = LogLevel.warning >= tll; - test = llVSgll && gllVSll && lVSll && gllOff && llOff && cond; - testG = gllOff && llOff && tllOff && tllVSll && tllVSgll && - lVSgll && cond; - - mem.warning(__LINE__); line = __LINE__; - assert(test ? mem.line == line : true); line = -1; - - warning(__LINE__); line = __LINE__; - assert(testG ? mem.line == line : true); line = -1; - - mem.warning(cond, __LINE__); line = __LINE__; - assert(test ? mem.line == line : true); line = -1; - - warning(cond, __LINE__); line = __LINE__; - assert(testG ? mem.line == line : true); line = -1; - - mem.warningf("%d", __LINE__); line = __LINE__; - assert(test ? mem.line == line : true); line = -1; - - warningf("%d", __LINE__); line = __LINE__; - assert(testG ? mem.line == line : true); line = -1; - - mem.warningf(cond, "%d", __LINE__); line = __LINE__; - assert(test ? mem.line == line : true); line = -1; - - warningf(cond, "%d", __LINE__); line = __LINE__; - assert(testG ? mem.line == line : true); line = -1; - - llVSgll = ll >= globalLogLevel; - lVSll = LogLevel.critical >= ll; - lVSgll = LogLevel.critical >= tll; - test = llVSgll && gllVSll && lVSll && gllOff && llOff && cond; - testG = gllOff && llOff && tllOff && tllVSll && tllVSgll && - lVSgll && cond; - - mem.critical(__LINE__); line = __LINE__; - assert(test ? mem.line == line : true); line = -1; - - critical(__LINE__); line = __LINE__; - assert(testG ? mem.line == line : true); line = -1; - - mem.critical(cond, __LINE__); line = __LINE__; - assert(test ? mem.line == line : true); line = -1; - - critical(cond, __LINE__); line = __LINE__; - assert(testG ? mem.line == line : true); line = -1; - - mem.criticalf("%d", __LINE__); line = __LINE__; - assert(test ? mem.line == line : true); line = -1; - - criticalf("%d", __LINE__); line = __LINE__; - assert(testG ? mem.line == line : true); line = -1; - - mem.criticalf(cond, "%d", __LINE__); line = __LINE__; - assert(test ? mem.line == line : true); line = -1; - - criticalf(cond, "%d", __LINE__); line = __LINE__; - assert(testG ? mem.line == line : true); line = -1; - - llVSgll = ll >= globalLogLevel; - lVSll = LogLevel.fatal >= ll; - lVSgll = LogLevel.fatal >= tll; - test = llVSgll && gllVSll && lVSll && gllOff && llOff && cond; - testG = gllOff && llOff && tllOff && tllVSll && tllVSgll && - lVSgll && cond; - - mem.fatal(__LINE__); line = __LINE__; - assert(test ? mem.line == line : true); line = -1; - assert(test ? fatalLog : true); - fatalLog = false; - - fatal(__LINE__); line = __LINE__; - assert(testG ? mem.line == line : true); line = -1; - assert(testG ? fatalLog : true); - fatalLog = false; - - mem.fatal(cond, __LINE__); line = __LINE__; - assert(test ? mem.line == line : true); line = -1; - assert(test ? fatalLog : true); - fatalLog = false; - - fatal(cond, __LINE__); line = __LINE__; - assert(testG ? mem.line == line : true); line = -1; - assert(testG ? fatalLog : true); - fatalLog = false; - - mem.fatalf("%d", __LINE__); line = __LINE__; - assert(test ? mem.line == line : true); line = -1; - assert(test ? fatalLog : true); - fatalLog = false; - - fatalf("%d", __LINE__); line = __LINE__; - assert(testG ? mem.line == line : true); line = -1; - assert(testG ? fatalLog : true); - fatalLog = false; - - mem.fatalf(cond, "%d", __LINE__); line = __LINE__; - assert(test ? mem.line == line : true); line = -1; - assert(test ? fatalLog : true); - fatalLog = false; - - fatalf(cond, "%d", __LINE__); line = __LINE__; - assert(testG ? mem.line == line : true); line = -1; - assert(testG ? fatalLog : true); - fatalLog = false; - } - } - } - } -} - -// Issue #5 -@safe unittest -{ - import std.string : indexOf; - - auto oldunspecificLogger = sharedLog; - - scope(exit) - { - sharedLog = atomicLoad(oldunspecificLogger); - globalLogLevel = LogLevel.all; - } - - auto tl = new TestLogger(LogLevel.info); - - () @trusted { - sharedLog = cast(shared) tl; - }(); - - trace("trace"); - assert(tl.msg.indexOf("trace") == -1); -} - -// Issue #5 -@safe unittest -{ - import std.logger.multilogger : MultiLogger; - import std.string : indexOf; - - stdThreadLocalLog.logLevel = LogLevel.all; - - auto oldunspecificLogger = sharedLog; - - scope(exit) - { - sharedLog = atomicLoad(oldunspecificLogger); - globalLogLevel = LogLevel.all; - } - - auto logger = new MultiLogger(LogLevel.error); - - auto tl = new TestLogger(LogLevel.info); - logger.insertLogger("required", tl); - - () @trusted { - sharedLog = cast(shared) logger; - }(); - - trace("trace"); - assert(tl.msg.indexOf("trace") == -1); - info("info"); - assert(tl.msg.indexOf("info") == -1); - error("error"); - assert(tl.msg.indexOf("error") == 0); -} - -@system unittest -{ - import std.exception : assertThrown; - auto tl = new TestLogger(); - assertThrown!Throwable(tl.fatal("fatal")); -} - -// log objects with non-safe toString -@system unittest -{ - struct Test - { - string toString() const @system - { - return "test"; - } - } - - auto tl = new TestLogger(); - tl.info(Test.init); - assert(tl.msg == "test"); -} - -// Workaround for atomics not allowed in @safe code -private auto trustedLoad(T)(ref shared T value) @trusted -{ - return atomicLoad!(MemoryOrder.acq)(value); -} - -// ditto -private void trustedStore(T)(ref shared T dst, ref T src) @trusted -{ - atomicStore!(MemoryOrder.rel)(dst, src); -} - -// check that thread-local logging does not propagate -// to shared logger -@system unittest -{ - import core.thread, std.concurrency; - - static shared logged_count = 0; - - class TestLog : Logger - { - Tid tid; - - this() - { - super (LogLevel.trace); - this.tid = thisTid; - } - - override void writeLogMsg(ref LogEntry payload) @trusted - { - assert(thisTid == this.tid); - atomicOp!"+="(logged_count, 1); - } - } - - class IgnoredLog : Logger - { - this() - { - super (LogLevel.trace); - } - - override void writeLogMsg(ref LogEntry payload) @trusted - { - assert(false); - } - } - - auto oldSharedLog = sharedLog; - scope(exit) - { - sharedLog = atomicLoad(oldSharedLog); - } - - () @trusted { - sharedLog = cast(shared) new IgnoredLog; - }(); - - Thread[] spawned; - - foreach (i; 0 .. 4) - { - spawned ~= new Thread({ - stdThreadLocalLog = new TestLog; - trace("zzzzzzzzzz"); - }); - spawned[$-1].start(); - } - - foreach (t; spawned) - t.join(); - - assert(atomicOp!"=="(logged_count, 4)); -} - -@safe unittest -{ - auto dl = () @trusted { - return cast(FileLogger) cast() sharedLog; - }(); - assert(dl !is null); - assert(dl.logLevel == LogLevel.info); - assert(globalLogLevel == LogLevel.all); - - auto tl = cast(StdForwardLogger) stdThreadLocalLog; - assert(tl !is null); - stdThreadLocalLog.logLevel = LogLevel.all; -} - -// https://issues.dlang.org/show_bug.cgi?id=14940 -@safe unittest -{ - import std.typecons : Nullable; - - Nullable!int a = 1; - auto l = new TestLogger(); - l.infof("log: %s", a); - assert(l.msg == "log: 1"); -} - -// Ensure @system toString methods work -@system unittest -{ - enum SystemToStringMsg = "SystemToString"; - static struct SystemToString - { - string toString() @system - { - return SystemToStringMsg; - } - } - - auto tl = new TestLogger(); - - SystemToString sts; - tl.logf("%s", sts); - assert(tl.msg == SystemToStringMsg); -} - -// https://issues.dlang.org/show_bug.cgi?id=17328 -@safe unittest -{ - import std.format : format; - - ubyte[] data = [0]; - string s = format("%(%02x%)", data); // format 00 - assert(s == "00"); - - auto tl = new TestLogger(); - - tl.infof("%(%02x%)", data); // infof 000 - - size_t i; - string fs = tl.msg; - for (; i < s.length; ++i) - { - assert(s[s.length - 1 - i] == fs[fs.length - 1 - i], fs); - } - assert(fs.length == 2); -} - -// https://issues.dlang.org/show_bug.cgi?id=15954 -@safe unittest -{ - import std.conv : to; - auto tl = new TestLogger(); - tl.log("123456789".to!wstring); - assert(tl.msg == "123456789"); -} - -// https://issues.dlang.org/show_bug.cgi?id=16256 -@safe unittest -{ - import std.conv : to; - auto tl = new TestLogger(); - tl.log("123456789"d); - assert(tl.msg == "123456789"); -} - -// https://issues.dlang.org/show_bug.cgi?id=15517 -@system unittest -{ - import std.file : exists, remove, tempDir; - import std.path : buildPath; - import std.stdio : File; - import std.string : indexOf; - - string fn = tempDir.buildPath("logfile.log"); - if (exists(fn)) - { - remove(fn); - } - - auto oldShared = sharedLog; - scope(exit) - { - sharedLog = atomicLoad(oldShared); - if (exists(fn)) - { - remove(fn); - } - } - - auto ts = [ "Test log 1", "Test log 2", "Test log 3"]; - - auto fl = new FileLogger(fn); - - () @trusted { - sharedLog = cast(shared) fl; - }(); - - assert(exists(fn)); - - foreach (t; ts) - { - log(t); - } - - auto f = File(fn); - auto l = f.byLine(); - assert(!l.empty); - size_t idx; - foreach (it; l) - { - assert(it.indexOf(ts[idx]) != -1, it); - ++idx; - } - - assert(exists(fn)); - fl.file.close(); -} diff --git a/phobos/std/logger/filelogger.d b/phobos/std/logger/filelogger.d deleted file mode 100644 index c662ca7..0000000 --- a/phobos/std/logger/filelogger.d +++ /dev/null @@ -1,272 +0,0 @@ -// Written in the D programming language. -/** -Source: $(PHOBOSSRC std/logger/filelogger.d) -*/ -module std.logger.filelogger; - -import std.logger.core; -import std.stdio; - -import std.typecons : Flag; - -/** An option to create $(LREF FileLogger) directory if it is non-existent. -*/ -alias CreateFolder = Flag!"CreateFolder"; - -/** This `Logger` implementation writes log messages to the associated -file. The name of the file has to be passed on construction time. If the file -is already present new log messages will be append at its end. -*/ -class FileLogger : Logger -{ - import std.concurrency : Tid; - import std.datetime.systime : SysTime; - import std.format.write : formattedWrite; - - /** A constructor for the `FileLogger` Logger. - - Params: - fn = The filename of the output file of the `FileLogger`. If that - file can not be opened for writting an exception will be thrown. - lv = The `LogLevel` for the `FileLogger`. By default the - - Example: - ------------- - auto l1 = new FileLogger("logFile"); - auto l2 = new FileLogger("logFile", LogLevel.fatal); - auto l3 = new FileLogger("logFile", LogLevel.fatal, CreateFolder.yes); - ------------- - */ - this(const string fn, const LogLevel lv = LogLevel.all) @safe - { - this(fn, lv, CreateFolder.yes); - } - - /** A constructor for the `FileLogger` Logger that takes a reference to - a `File`. - - The `File` passed must be open for all the log call to the - `FileLogger`. If the `File` gets closed, using the `FileLogger` - for logging will result in undefined behaviour. - - Params: - fn = The file used for logging. - lv = The `LogLevel` for the `FileLogger`. By default the - `LogLevel` for `FileLogger` is `LogLevel.all`. - createFileNameFolder = if yes and fn contains a folder name, this - folder will be created. - - Example: - ------------- - auto file = File("logFile.log", "w"); - auto l1 = new FileLogger(file); - auto l2 = new FileLogger(file, LogLevel.fatal); - ------------- - */ - this(const string fn, const LogLevel lv, CreateFolder createFileNameFolder) @safe - { - import std.file : exists, mkdirRecurse; - import std.path : dirName; - import std.conv : text; - - super(lv); - this.filename = fn; - - if (createFileNameFolder) - { - auto d = dirName(this.filename); - mkdirRecurse(d); - assert(exists(d), text("The folder the FileLogger should have", - " created in '", d,"' could not be created.")); - } - - this.file_.open(this.filename, "a"); - } - - /** A constructor for the `FileLogger` Logger that takes a reference to - a `File`. - - The `File` passed must be open for all the log call to the - `FileLogger`. If the `File` gets closed, using the `FileLogger` - for logging will result in undefined behaviour. - - Params: - file = The file used for logging. - lv = The `LogLevel` for the `FileLogger`. By default the - `LogLevel` for `FileLogger` is `LogLevel.all`. - - Example: - ------------- - auto file = File("logFile.log", "w"); - auto l1 = new FileLogger(file); - auto l2 = new FileLogger(file, LogLevel.fatal); - ------------- - */ - this(File file, const LogLevel lv = LogLevel.all) @safe - { - super(lv); - this.file_ = file; - } - - /** If the `FileLogger` is managing the `File` it logs to, this - method will return a reference to this File. - */ - @property File file() @safe - { - return this.file_; - } - - /* This method overrides the base class method in order to log to a file - without requiring heap allocated memory. Additionally, the `FileLogger` - local mutex is logged to serialize the log calls. - */ - override protected void beginLogMsg(string file, int line, string funcName, - string prettyFuncName, string moduleName, LogLevel logLevel, - Tid threadId, SysTime timestamp, Logger logger) - @safe - { - import std.string : lastIndexOf; - ptrdiff_t fnIdx = file.lastIndexOf('/') + 1; - ptrdiff_t funIdx = funcName.lastIndexOf('.') + 1; - - auto lt = this.file_.lockingTextWriter(); - systimeToISOString(lt, timestamp); - import std.conv : to; - formattedWrite(lt, " [%s] %s:%u:%s ", logLevel.to!string, - file[fnIdx .. $], line, funcName[funIdx .. $]); - } - - /* This methods overrides the base class method and writes the parts of - the log call directly to the file. - */ - override protected void logMsgPart(scope const(char)[] msg) - { - formattedWrite(this.file_.lockingTextWriter(), "%s", msg); - } - - /* This methods overrides the base class method and finalizes the active - log call. This requires flushing the `File` and releasing the - `FileLogger` local mutex. - */ - override protected void finishLogMsg() - { - this.file_.lockingTextWriter().put("\n"); - this.file_.flush(); - } - - /* This methods overrides the base class method and delegates the - `LogEntry` data to the actual implementation. - */ - override protected void writeLogMsg(ref LogEntry payload) - { - this.beginLogMsg(payload.file, payload.line, payload.funcName, - payload.prettyFuncName, payload.moduleName, payload.logLevel, - payload.threadId, payload.timestamp, payload.logger); - this.logMsgPart(payload.msg); - this.finishLogMsg(); - } - - /** If the `FileLogger` was constructed with a filename, this method - returns this filename. Otherwise an empty `string` is returned. - */ - string getFilename() - { - return this.filename; - } - - /** The `File` log messages are written to. */ - protected File file_; - - /** The filename of the `File` log messages are written to. */ - protected string filename; -} - -@system unittest -{ - import std.array : empty; - import std.file : deleteme, remove; - import std.string : indexOf; - - string filename = deleteme ~ __FUNCTION__ ~ ".tempLogFile"; - auto l = new FileLogger(filename); - - scope(exit) - { - remove(filename); - } - - string notWritten = "this should not be written to file"; - string written = "this should be written to file"; - - l.logLevel = LogLevel.critical; - l.log(LogLevel.warning, notWritten); - l.log(LogLevel.critical, written); - destroy(l); - - auto file = File(filename, "r"); - string readLine = file.readln(); - assert(readLine.indexOf(written) != -1, readLine); - readLine = file.readln(); - assert(readLine.indexOf(notWritten) == -1, readLine); -} - -@safe unittest -{ - import std.file : rmdirRecurse, exists, deleteme; - import std.path : dirName; - - const string tmpFolder = dirName(deleteme); - const string filepath = tmpFolder ~ "/bug15771/minas/oops/"; - const string filename = filepath ~ "output.txt"; - assert(!exists(filepath)); - - auto f = new FileLogger(filename, LogLevel.all, CreateFolder.yes); - scope(exit) () @trusted { rmdirRecurse(tmpFolder ~ "/bug15771"); }(); - - f.log("Hello World!"); - assert(exists(filepath)); - f.file.close(); -} - -@system unittest -{ - import std.array : empty; - import std.file : deleteme, remove; - import std.string : indexOf; - - string filename = deleteme ~ __FUNCTION__ ~ ".tempLogFile"; - auto file = File(filename, "w"); - auto l = new FileLogger(file); - - scope(exit) - { - remove(filename); - } - - string notWritten = "this should not be written to file"; - string written = "this should be written to file"; - - l.logLevel = LogLevel.critical; - l.log(LogLevel.warning, notWritten); - l.log(LogLevel.critical, written); - file.close(); - - file = File(filename, "r"); - string readLine = file.readln(); - assert(readLine.indexOf(written) != -1, readLine); - readLine = file.readln(); - assert(readLine.indexOf(notWritten) == -1, readLine); - file.close(); -} - -@system unittest -{ - auto dl = cast(FileLogger) sharedLog; - assert(dl !is null); - assert(dl.logLevel == LogLevel.info); - assert(globalLogLevel == LogLevel.all); - - auto tl = cast(StdForwardLogger) stdThreadLocalLog; - assert(tl !is null); - stdThreadLocalLog.logLevel = LogLevel.all; -} diff --git a/phobos/std/logger/multilogger.d b/phobos/std/logger/multilogger.d deleted file mode 100644 index eda922d..0000000 --- a/phobos/std/logger/multilogger.d +++ /dev/null @@ -1,200 +0,0 @@ -// Written in the D programming language. -/** -Source: $(PHOBOSSRC std/logger/multilogger.d) -*/ -module std.logger.multilogger; - -import std.logger.core; -import std.logger.filelogger; - -/** This Element is stored inside the `MultiLogger` and associates a -`Logger` to a `string`. -*/ -struct MultiLoggerEntry -{ - string name; /// The name if the `Logger` - Logger logger; /// The stored `Logger` -} - -/** MultiLogger logs to multiple `Logger`. The `Logger`s are stored in an -`Logger[]` in their order of insertion. - -Every data logged to this `MultiLogger` will be distributed to all the $(D -Logger)s inserted into it. This `MultiLogger` implementation can -hold multiple `Logger`s with the same name. If the method `removeLogger` -is used to remove a `Logger` only the first occurrence with that name will -be removed. -*/ -class MultiLogger : Logger -{ - /** A constructor for the `MultiLogger` Logger. - - Params: - lv = The `LogLevel` for the `MultiLogger`. By default the - `LogLevel` for `MultiLogger` is `LogLevel.all`. - - Example: - ------------- - auto l1 = new MultiLogger(LogLevel.trace); - ------------- - */ - this(const LogLevel lv = LogLevel.all) @safe - { - super(lv); - } - - /** This member holds all `Logger`s stored in the `MultiLogger`. - - When inheriting from `MultiLogger` this member can be used to gain - access to the stored `Logger`. - */ - protected MultiLoggerEntry[] logger; - - /** This method inserts a new Logger into the `MultiLogger`. - - Params: - name = The name of the `Logger` to insert. - newLogger = The `Logger` to insert. - */ - void insertLogger(string name, Logger newLogger) @safe - { - this.logger ~= MultiLoggerEntry(name, newLogger); - } - - /** This method removes a Logger from the `MultiLogger`. - - Params: - toRemove = The name of the `Logger` to remove. If the `Logger` - is not found `null` will be returned. Only the first occurrence of - a `Logger` with the given name will be removed. - - Returns: The removed `Logger`. - */ - Logger removeLogger(in char[] toRemove) @safe - { - import std.algorithm.mutation : copy; - import std.range.primitives : back, popBack; - for (size_t i = 0; i < this.logger.length; ++i) - { - if (this.logger[i].name == toRemove) - { - Logger ret = this.logger[i].logger; - this.logger[i] = this.logger.back; - this.logger.popBack(); - - return ret; - } - } - - return null; - } - - /* The override to pass the payload to all children of the - `MultiLoggerBase`. - */ - override protected void writeLogMsg(ref LogEntry payload) @safe - { - foreach (it; this.logger) - { - /* We don't perform any checks here to avoid race conditions. - Instead the child will check on its own if its log level matches - and assume LogLevel.all for the globalLogLevel (since we already - know the message passes this test). - */ - it.logger.forwardMsg(payload); - } - } -} - -@safe unittest -{ - import std.exception : assertThrown; - import std.logger.nulllogger; - auto a = new MultiLogger; - auto n0 = new NullLogger(); - auto n1 = new NullLogger(); - a.insertLogger("zero", n0); - a.insertLogger("one", n1); - - auto n0_1 = a.removeLogger("zero"); - assert(n0_1 is n0); - auto n = a.removeLogger("zero"); - assert(n is null); - - auto n1_1 = a.removeLogger("one"); - assert(n1_1 is n1); - n = a.removeLogger("one"); - assert(n is null); -} - -@safe unittest -{ - auto a = new MultiLogger; - auto n0 = new TestLogger; - auto n1 = new TestLogger; - a.insertLogger("zero", n0); - a.insertLogger("one", n1); - - a.log("Hello TestLogger"); int line = __LINE__; - assert(n0.msg == "Hello TestLogger"); - assert(n0.line == line); - assert(n1.msg == "Hello TestLogger"); - assert(n1.line == line); -} - -// Issue #16 -@system unittest -{ - import std.file : deleteme; - import std.stdio : File; - import std.string : indexOf; - string logName = deleteme ~ __FUNCTION__ ~ ".log"; - auto logFileOutput = File(logName, "w"); - scope(exit) - { - import std.file : remove; - logFileOutput.close(); - remove(logName); - } - auto traceLog = new FileLogger(logFileOutput, LogLevel.all); - auto infoLog = new TestLogger(LogLevel.info); - - auto root = new MultiLogger(LogLevel.all); - root.insertLogger("fileLogger", traceLog); - root.insertLogger("stdoutLogger", infoLog); - - string tMsg = "A trace message"; - root.trace(tMsg); int line1 = __LINE__; - - assert(infoLog.line != line1); - assert(infoLog.msg != tMsg); - - string iMsg = "A info message"; - root.info(iMsg); int line2 = __LINE__; - - assert(infoLog.line == line2); - assert(infoLog.msg == iMsg, infoLog.msg ~ ":" ~ iMsg); - - logFileOutput.close(); - logFileOutput = File(logName, "r"); - assert(logFileOutput.isOpen); - assert(!logFileOutput.eof); - - auto line = logFileOutput.readln(); - assert(line.indexOf(tMsg) != -1, line ~ ":" ~ tMsg); - assert(!logFileOutput.eof); - line = logFileOutput.readln(); - assert(line.indexOf(iMsg) != -1, line ~ ":" ~ tMsg); -} - -@system unittest -{ - auto dl = cast(FileLogger) sharedLog; - assert(dl !is null); - assert(dl.logLevel == LogLevel.info); - assert(globalLogLevel == LogLevel.all); - - auto tl = cast(StdForwardLogger) stdThreadLocalLog; - assert(tl !is null); - stdThreadLocalLog.logLevel = LogLevel.all; -} diff --git a/phobos/std/logger/nulllogger.d b/phobos/std/logger/nulllogger.d deleted file mode 100644 index fd48b85..0000000 --- a/phobos/std/logger/nulllogger.d +++ /dev/null @@ -1,41 +0,0 @@ -// Written in the D programming language. -/** -Source: $(PHOBOSSRC std/logger/nulllogger.d) -*/ -module std.logger.nulllogger; - -import std.logger.core; - -/** The `NullLogger` will not process any log messages. - -In case of a log message with `LogLevel.fatal` nothing will happen. -*/ -class NullLogger : Logger -{ - /** The default constructor for the `NullLogger`. - - Independent of the parameter this Logger will never log a message. - - Params: - lv = The `LogLevel` for the `NullLogger`. By default the `LogLevel` - for `NullLogger` is `LogLevel.all`. - */ - this(const LogLevel lv = LogLevel.all) @safe - { - super(lv); - this.fatalHandler = delegate() {}; - } - - override protected void writeLogMsg(ref LogEntry payload) @safe @nogc - { - } -} - -/// -@safe unittest -{ - import std.logger.core : LogLevel; - auto nl1 = new NullLogger(LogLevel.all); - nl1.info("You will never read this."); - nl1.fatal("You will never read this, either and it will not throw"); -} diff --git a/phobos/std/logger/package.d b/phobos/std/logger/package.d deleted file mode 100644 index 14a4394..0000000 --- a/phobos/std/logger/package.d +++ /dev/null @@ -1,169 +0,0 @@ -// Written in the D programming language. -/** -Implements logging facilities. - -Copyright: Copyright Robert "burner" Schadek 2013 -- -License: Boost License 1.0. -Authors: $(HTTP www.svs.informatik.uni-oldenburg.de/60865.html, Robert burner Schadek) - -$(H3 Basic Logging) - -Message logging is a common approach to expose runtime information of a -program. Logging should be easy, but also flexible and powerful, therefore -`D` provides a standard interface for logging. - -The easiest way to create a log message is to write: -------------- -import std.logger; - -void main() { - log("Hello World"); -} -------------- -This will print a message to the `stderr` device. The message will contain -the filename, the line number, the name of the surrounding function, the time -and the message. - -More complex log call can go along the lines like: -------------- -log("Logging to the sharedLog with its default LogLevel"); -logf(LogLevel.info, 5 < 6, "%s to the sharedLog with its LogLevel.info", "Logging"); -info("Logging to the sharedLog with its info LogLevel"); -warning(5 < 6, "Logging to the sharedLog with its LogLevel.warning if 5 is less than 6"); -error("Logging to the sharedLog with its error LogLevel"); -errorf("Logging %s the sharedLog %s its error LogLevel", "to", "with"); -critical("Logging to the"," sharedLog with its error LogLevel"); -fatal("Logging to the sharedLog with its fatal LogLevel"); - -auto fLogger = new FileLogger("NameOfTheLogFile"); -fLogger.log("Logging to the fileLogger with its default LogLevel"); -fLogger.info("Logging to the fileLogger with its default LogLevel"); -fLogger.warning(5 < 6, "Logging to the fileLogger with its LogLevel.warning if 5 is less than 6"); -fLogger.warningf(5 < 6, "Logging to the fileLogger with its LogLevel.warning if %s is %s than 6", 5, "less"); -fLogger.critical("Logging to the fileLogger with its info LogLevel"); -fLogger.log(LogLevel.trace, 5 < 6, "Logging to the fileLogger"," with its default LogLevel if 5 is less than 6"); -fLogger.fatal("Logging to the fileLogger with its warning LogLevel"); -------------- -Additionally, this example shows how a new `FileLogger` is created. -Individual `Logger` and the global log functions share commonly named -functions to log data. - -The names of the functions are as follows: -$(UL - $(LI `log`) - $(LI `trace`) - $(LI `info`) - $(LI `warning`) - $(LI `error`) - $(LI `critical`) - $(LI `fatal`) -) -The default `Logger` will by default log to `stderr` and has a default -`LogLevel` of `LogLevel.all`. The default Logger can be accessed by -using the property called `sharedLog`. This property is a reference to the -current default `Logger`. This reference can be used to assign a new -default `Logger`. -------------- -sharedLog = new FileLogger("New_Default_Log_File.log"); -------------- - -Additional `Logger` can be created by creating a new instance of the -required `Logger`. - -$(H3 Logging Fundamentals) -$(H4 LogLevel) -The `LogLevel` of a log call can be defined in two ways. The first is by -calling `log` and passing the `LogLevel` explicitly as the first argument. -The second way of setting the `LogLevel` of a -log call, is by calling either `trace`, `info`, `warning`, -`critical`, or `fatal`. The log call will then have the respective -`LogLevel`. If no `LogLevel` is defined the log call will use the -current `LogLevel` of the used `Logger`. If data is logged with -`LogLevel` `fatal` by default an `Error` will be thrown. -This behaviour can be modified by using the member `fatalHandler` to -assign a custom delegate to handle log call with `LogLevel` `fatal`. - -$(H4 Conditional Logging) -Conditional logging can be achieved be passing a `bool` as first -argument to a log function. If conditional logging is used the condition must -be `true` in order to have the log message logged. - -In order to combine an explicit `LogLevel` passing with conditional -logging, the `LogLevel` has to be passed as first argument followed by the -`bool`. - -$(H4 Filtering Log Messages) -Messages are logged if the `LogLevel` of the log message is greater than or -equal to the `LogLevel` of the used `Logger` and additionally if the -`LogLevel` of the log message is greater than or equal to the global `LogLevel`. -If a condition is passed into the log call, this condition must be true. - -The global `LogLevel` is accessible by using `globalLogLevel`. -To assign a `LogLevel` of a `Logger` use the `logLevel` property of -the logger. - -$(H4 Printf Style Logging) -If `printf`-style logging is needed add a $(B f) to the logging call, such as -$(D myLogger.infof("Hello %s", "world");) or $(D fatalf("errno %d", 1337)). -The additional $(B f) appended to the function name enables `printf`-style -logging for all combinations of explicit `LogLevel` and conditional -logging functions and methods. - -$(H4 Thread Local Redirection) -Calls to the free standing log functions are not directly forwarded to the -global `Logger` `sharedLog`. Actually, a thread local `Logger` of -type `StdForwardLogger` processes the log call and then, by default, forwards -the created `Logger.LogEntry` to the `sharedLog` `Logger`. -The thread local `Logger` is accessible by the `stdThreadLocalLog` -property. This property allows to assign user defined `Logger`. The default -`LogLevel` of the `stdThreadLocalLog` `Logger` is `LogLevel.all` -and it will therefore forward all messages to the `sharedLog` `Logger`. -The `LogLevel` of the `stdThreadLocalLog` can be used to filter log -calls before they reach the `sharedLog` `Logger`. - -$(H3 User Defined Logger) -To customize the `Logger` behavior, create a new `class` that inherits from -the abstract `Logger` `class`, and implements the `writeLogMsg` -method. -------------- -class MyCustomLogger : Logger -{ - this(LogLevel lv) @safe - { - super(lv); - } - - override void writeLogMsg(ref LogEntry payload) - { - // log message in my custom way - } -} - -auto logger = new MyCustomLogger(LogLevel.info); -logger.log("Awesome log message with LogLevel.info"); -------------- - -To gain more precise control over the logging process, additionally to -overriding the `writeLogMsg` method the methods `beginLogMsg`, -`logMsgPart` and `finishLogMsg` can be overridden. - -$(H3 Provided Logger) -By default four `Logger` implementations are given. The `FileLogger` -logs data to files. It can also be used to log to `stdout` and `stderr` -as these devices are files as well. A `Logger` that logs to `stdout` can -therefore be created by $(D new FileLogger(stdout)). -The `MultiLogger` is basically an associative array of `string`s to -`Logger`. It propagates log calls to its stored `Logger`. The -`ArrayLogger` contains an array of `Logger` and also propagates log -calls to its stored `Logger`. The `NullLogger` does not do anything. It -will never log a message and will never throw on a log call with `LogLevel` -`error`. - -Source: $(PHOBOSSRC std/logger/package.d) -*/ -module std.logger; - -public import std.logger.core; -public import std.logger.filelogger; -public import std.logger.multilogger; -public import std.logger.nulllogger; diff --git a/phobos/std/math/algebraic.d b/phobos/std/math/algebraic.d deleted file mode 100644 index 9882029..0000000 --- a/phobos/std/math/algebraic.d +++ /dev/null @@ -1,1021 +0,0 @@ -// Written in the D programming language. - -/** -This is a submodule of $(MREF std, math). - -It contains classical algebraic functions like `abs`, `sqrt`, and `poly`. - -Copyright: Copyright The D Language Foundation 2000 - 2011. -License: $(HTTP www.boost.org/LICENSE_1_0.txt, Boost License 1.0). -Authors: $(HTTP digitalmars.com, Walter Bright), Don Clugston, - Conversion of CEPHES math library to D by Iain Buclaw and David Nadlinger -Source: $(PHOBOSSRC std/math/algebraic.d) - -Macros: - TABLE_SV = - - $0
Special Values
- NAN = $(RED NAN) - POWER = $1$2 - SUB = $1$2 - PLUSMN = ± - INFIN = ∞ - PLUSMNINF = ±∞ - LT = < - - */ - -module std.math.algebraic; - -static import core.math; -static import core.stdc.math; -import std.traits : CommonType, isFloatingPoint, isIntegral, isSigned, Unqual; - -/*********************************** - * Calculates the absolute value of a number. - * - * Params: - * Num = (template parameter) type of number - * x = real number value - * - * Returns: - * The absolute value of the number. If floating-point or integral, - * the return type will be the same as the input. - * - * Limitations: - * When x is a signed integral equal to `Num.min` the value of x will be returned instead. - * Note for 2's complement; `-Num.min` (= `Num.max + 1`) is not representable due to overflow. - */ -auto abs(Num)(Num x) @nogc nothrow pure -if (isIntegral!Num || (is(typeof(Num.init >= 0)) && is(typeof(-Num.init)))) -{ - static if (isFloatingPoint!(Num)) - return fabs(x); - else - { - static if (isIntegral!Num) - return x >= 0 ? x : cast(Num) -x; - else - return x >= 0 ? x : -x; - } -} - -/// -@safe pure nothrow @nogc unittest -{ - import std.math.traits : isIdentical, isNaN; - - assert(isIdentical(abs(-0.0L), 0.0L)); - assert(isNaN(abs(real.nan))); - assert(abs(-real.infinity) == real.infinity); - assert(abs(-56) == 56); - assert(abs(2321312L) == 2321312L); - assert(abs(23u) == 23u); -} - -@safe pure nothrow @nogc unittest -{ - assert(abs(byte(-8)) == 8); - assert(abs(ubyte(8u)) == 8); - assert(abs(short(-8)) == 8); - assert(abs(ushort(8u)) == 8); - assert(abs(int(-8)) == 8); - assert(abs(uint(8u)) == 8); - assert(abs(long(-8)) == 8); - assert(abs(ulong(8u)) == 8); - assert(is(typeof(abs(byte(-8))) == byte)); - assert(is(typeof(abs(ubyte(8u))) == ubyte)); - assert(is(typeof(abs(short(-8))) == short)); - assert(is(typeof(abs(ushort(8u))) == ushort)); - assert(is(typeof(abs(int(-8))) == int)); - assert(is(typeof(abs(uint(8u))) == uint)); - assert(is(typeof(abs(long(-8))) == long)); - assert(is(typeof(abs(ulong(8u))) == ulong)); -} - -@safe pure nothrow @nogc unittest -{ - import std.meta : AliasSeq; - static foreach (T; AliasSeq!(float, double, real)) - {{ - T f = 3; - assert(abs(f) == f); - assert(abs(-f) == f); - }} -} - -// see https://issues.dlang.org/show_bug.cgi?id=20205 -// to avoid falling into the trap again -@safe pure nothrow @nogc unittest -{ - assert(50 - abs(-100) == -50); -} - -// https://issues.dlang.org/show_bug.cgi?id=19162 -@safe unittest -{ - struct Vector(T, int size) - { - T x, y, z; - } - - static auto abs(T, int size)(auto ref const Vector!(T, size) v) - { - return v; - } - Vector!(int, 3) v; - assert(abs(v) == v); -} - -/******************************* - * Returns |x| - * - * $(TABLE_SV - * $(TR $(TH x) $(TH fabs(x))) - * $(TR $(TD $(PLUSMN)0.0) $(TD +0.0) ) - * $(TR $(TD $(PLUSMN)$(INFIN)) $(TD +$(INFIN)) ) - * ) - */ -pragma(inline, true) -real fabs(real x) @safe pure nothrow @nogc { return core.math.fabs(x); } - -///ditto -pragma(inline, true) -double fabs(double x) @safe pure nothrow @nogc { return core.math.fabs(x); } - -///ditto -pragma(inline, true) -float fabs(float x) @safe pure nothrow @nogc { return core.math.fabs(x); } - -/// -@safe unittest -{ - import std.math.traits : isIdentical; - - assert(isIdentical(fabs(0.0f), 0.0f)); - assert(isIdentical(fabs(-0.0f), 0.0f)); - assert(fabs(-10.0f) == 10.0f); - - assert(isIdentical(fabs(0.0), 0.0)); - assert(isIdentical(fabs(-0.0), 0.0)); - assert(fabs(-10.0) == 10.0); - - assert(isIdentical(fabs(0.0L), 0.0L)); - assert(isIdentical(fabs(-0.0L), 0.0L)); - assert(fabs(-10.0L) == 10.0L); -} - -@safe unittest -{ - real function(real) pfabs = &fabs; - assert(pfabs != null); -} - -@safe pure nothrow @nogc unittest -{ - float f = fabs(-2.0f); - assert(f == 2); - - double d = fabs(-2.0); - assert(d == 2); - - real r = fabs(-2.0L); - assert(r == 2); -} - -/*************************************** - * Compute square root of x. - * - * $(TABLE_SV - * $(TR $(TH x) $(TH sqrt(x)) $(TH invalid?)) - * $(TR $(TD -0.0) $(TD -0.0) $(TD no)) - * $(TR $(TD $(LT)0.0) $(TD $(NAN)) $(TD yes)) - * $(TR $(TD +$(INFIN)) $(TD +$(INFIN)) $(TD no)) - * ) - */ -pragma(inline, true) -float sqrt(float x) @nogc @safe pure nothrow { return core.math.sqrt(x); } - -/// ditto -pragma(inline, true) -double sqrt(double x) @nogc @safe pure nothrow { return core.math.sqrt(x); } - -/// ditto -pragma(inline, true) -real sqrt(real x) @nogc @safe pure nothrow { return core.math.sqrt(x); } - -/// -@safe pure nothrow @nogc unittest -{ - import std.math.operations : feqrel; - import std.math.traits : isNaN; - - assert(sqrt(2.0).feqrel(1.4142) > 16); - assert(sqrt(9.0).feqrel(3.0) > 16); - - assert(isNaN(sqrt(-1.0f))); - assert(isNaN(sqrt(-1.0))); - assert(isNaN(sqrt(-1.0L))); -} - -@safe unittest -{ - // https://issues.dlang.org/show_bug.cgi?id=5305 - float function(float) psqrtf = &sqrt; - assert(psqrtf != null); - double function(double) psqrtd = &sqrt; - assert(psqrtd != null); - real function(real) psqrtr = &sqrt; - assert(psqrtr != null); - - //ctfe - enum ZX80 = sqrt(7.0f); - enum ZX81 = sqrt(7.0); - enum ZX82 = sqrt(7.0L); -} - -@safe pure nothrow @nogc unittest -{ - float f = sqrt(2.0f); - assert(fabs(f * f - 2.0f) < .00001); - - double d = sqrt(2.0); - assert(fabs(d * d - 2.0) < .00001); - - real r = sqrt(2.0L); - assert(fabs(r * r - 2.0) < .00001); -} - -/*************** - * Calculates the cube root of x. - * - * $(TABLE_SV - * $(TR $(TH $(I x)) $(TH cbrt(x)) $(TH invalid?)) - * $(TR $(TD $(PLUSMN)0.0) $(TD $(PLUSMN)0.0) $(TD no) ) - * $(TR $(TD $(NAN)) $(TD $(NAN)) $(TD yes) ) - * $(TR $(TD $(PLUSMN)$(INFIN)) $(TD $(PLUSMN)$(INFIN)) $(TD no) ) - * ) - */ -real cbrt(real x) @trusted nothrow @nogc -{ - version (CRuntime_Microsoft) - { - import std.math.traits : copysign; - import std.math.exponential : exp2; - - version (INLINE_YL2X) - return copysign(exp2(core.math.yl2x(fabs(x), 1.0L/3.0L)), x); - else - return core.stdc.math.cbrtl(x); - } - else - return core.stdc.math.cbrtl(x); -} - -/// -@safe unittest -{ - import std.math.operations : feqrel; - - assert(cbrt(1.0).feqrel(1.0) > 16); - assert(cbrt(27.0).feqrel(3.0) > 16); - assert(cbrt(15.625).feqrel(2.5) > 16); -} - -/*********************************************************************** - * Calculates the length of the - * hypotenuse of a right-angled triangle with sides of length x and y. - * The hypotenuse is the value of the square root of - * the sums of the squares of x and y: - * - * sqrt($(POWER x, 2) + $(POWER y, 2)) - * - * Note that hypot(x, y), hypot(y, x) and - * hypot(x, -y) are equivalent. - * - * $(TABLE_SV - * $(TR $(TH x) $(TH y) $(TH hypot(x, y)) $(TH invalid?)) - * $(TR $(TD x) $(TD $(PLUSMN)0.0) $(TD |x|) $(TD no)) - * $(TR $(TD $(PLUSMNINF)) $(TD y) $(TD +$(INFIN)) $(TD no)) - * $(TR $(TD $(PLUSMNINF)) $(TD $(NAN)) $(TD +$(INFIN)) $(TD no)) - * ) - */ -T hypot(T)(const T x, const T y) @safe pure nothrow @nogc -if (isFloatingPoint!T) -{ - // Scale x and y to avoid underflow and overflow. - // If one is huge and the other tiny, return the larger. - // If both are huge, avoid overflow by scaling by 2^^-N. - // If both are tiny, avoid underflow by scaling by 2^^N. - import core.math : fabs, sqrt; - import std.math.traits : floatTraits, RealFormat; - - alias F = floatTraits!T; - - T u = fabs(x); - T v = fabs(y); - if (!(u >= v)) // check for NaN as well. - { - v = u; - u = fabs(y); - if (u == T.infinity) return u; // hypot(inf, nan) == inf - if (v == T.infinity) return v; // hypot(nan, inf) == inf - } - - static if (F.realFormat == RealFormat.ieeeSingle) - { - enum SQRTMIN = 0x1p-60f; - enum SQRTMAX = 0x1p+60f; - enum SCALE_UNDERFLOW = 0x1p+90f; - enum SCALE_OVERFLOW = 0x1p-90f; - } - else static if (F.realFormat == RealFormat.ieeeDouble || - F.realFormat == RealFormat.ieeeExtended53 || - F.realFormat == RealFormat.ibmExtended) - { - enum SQRTMIN = 0x1p-450L; - enum SQRTMAX = 0x1p+500L; - enum SCALE_UNDERFLOW = 0x1p+600L; - enum SCALE_OVERFLOW = 0x1p-600L; - } - else static if (F.realFormat == RealFormat.ieeeExtended || - F.realFormat == RealFormat.ieeeQuadruple) - { - enum SQRTMIN = 0x1p-8000L; - enum SQRTMAX = 0x1p+8000L; - enum SCALE_UNDERFLOW = 0x1p+10000L; - enum SCALE_OVERFLOW = 0x1p-10000L; - } - else - assert(0, "hypot not implemented"); - - // Now u >= v, or else one is NaN. - T ratio = 1.0; - if (v >= SQRTMAX) - { - // hypot(huge, huge) -- avoid overflow - ratio = SCALE_UNDERFLOW; - u *= SCALE_OVERFLOW; - v *= SCALE_OVERFLOW; - } - else if (u <= SQRTMIN) - { - // hypot (tiny, tiny) -- avoid underflow - // This is only necessary to avoid setting the underflow - // flag. - ratio = SCALE_OVERFLOW; - u *= SCALE_UNDERFLOW; - v *= SCALE_UNDERFLOW; - } - - if (u * T.epsilon > v) - { - // hypot (huge, tiny) = huge - return u; - } - - // both are in the normal range - return ratio * sqrt(u*u + v*v); -} - -/// -@safe unittest -{ - import std.math.operations : feqrel; - - assert(hypot(1.0, 1.0).feqrel(1.4142) > 16); - assert(hypot(3.0, 4.0).feqrel(5.0) > 16); - assert(hypot(real.infinity, 1.0L) == real.infinity); - assert(hypot(real.infinity, real.nan) == real.infinity); -} - -@safe unittest -{ - import std.math.operations : feqrel; - - assert(hypot(1.0f, 1.0f).feqrel(1.4142f) > 16); - assert(hypot(3.0f, 4.0f).feqrel(5.0f) > 16); - assert(hypot(float.infinity, 1.0f) == float.infinity); - assert(hypot(float.infinity, float.nan) == float.infinity); - - assert(hypot(1.0L, 1.0L).feqrel(1.4142L) > 16); - assert(hypot(3.0L, 4.0L).feqrel(5.0L) > 16); - assert(hypot(double.infinity, 1.0) == double.infinity); - assert(hypot(double.infinity, double.nan) == double.infinity); -} - -@safe unittest -{ - import std.math.operations : feqrel; - import std.math.traits : isIdentical; - import std.meta : AliasSeq; - - static foreach (T; AliasSeq!(float, double, real)) - {{ - static T[3][] vals = // x,y,hypot - [ - [ 0.0, 0.0, 0.0], - [ 0.0, -0.0, 0.0], - [ -0.0, -0.0, 0.0], - [ 3.0, 4.0, 5.0], - [ -300, -400, 500], - [0.0, 7.0, 7.0], - [9.0, 9*T.epsilon, 9.0], - [88/(64*sqrt(T.min_normal)), 105/(64*sqrt(T.min_normal)), 137/(64*sqrt(T.min_normal))], - [88/(128*sqrt(T.min_normal)), 105/(128*sqrt(T.min_normal)), 137/(128*sqrt(T.min_normal))], - [3*T.min_normal*T.epsilon, 4*T.min_normal*T.epsilon, 5*T.min_normal*T.epsilon], - [ T.min_normal, T.min_normal, sqrt(2.0L)*T.min_normal], - [ T.max/sqrt(2.0L), T.max/sqrt(2.0L), T.max], - [ T.infinity, T.nan, T.infinity], - [ T.nan, T.infinity, T.infinity], - [ T.nan, T.nan, T.nan], - [ T.nan, T.max, T.nan], - [ T.max, T.nan, T.nan], - ]; - for (int i = 0; i < vals.length; i++) - { - T x = vals[i][0]; - T y = vals[i][1]; - T z = vals[i][2]; - T h = hypot(x, y); - assert(isIdentical(z,h) || feqrel(z, h) >= T.mant_dig - 1); - } - }} -} - -/*********************************************************************** - * Calculates the distance of the point (x, y, z) from the origin (0, 0, 0) - * in three-dimensional space. - * The distance is the value of the square root of the sums of the squares - * of x, y, and z: - * - * sqrt($(POWER x, 2) + $(POWER y, 2) + $(POWER z, 2)) - * - * Note that the distance between two points (x1, y1, z1) and (x2, y2, z2) - * in three-dimensional space can be calculated as hypot(x2-x1, y2-y1, z2-z1). - * - * Params: - * x = floating point value - * y = floating point value - * z = floating point value - * - * Returns: - * The square root of the sum of the squares of the given arguments. - */ -T hypot(T)(const T x, const T y, const T z) @safe pure nothrow @nogc -if (isFloatingPoint!T) -{ - import core.math : fabs, sqrt; - import std.math.operations : fmax; - const absx = fabs(x); - const absy = fabs(y); - const absz = fabs(z); - - // Scale all parameters to avoid overflow. - const ratio = fmax(absx, fmax(absy, absz)); - if (ratio == 0.0) - return ratio; - - return ratio * sqrt((absx / ratio) * (absx / ratio) - + (absy / ratio) * (absy / ratio) - + (absz / ratio) * (absz / ratio)); -} - -/// -@safe unittest -{ - import std.math.operations : isClose; - - assert(isClose(hypot(1.0, 2.0, 2.0), 3.0)); - assert(isClose(hypot(2.0, 3.0, 6.0), 7.0)); - assert(isClose(hypot(1.0, 4.0, 8.0), 9.0)); -} - -@safe unittest -{ - import std.meta : AliasSeq; - import std.math.traits : isIdentical; - import std.math.operations : isClose; - static foreach (T; AliasSeq!(float, double, real)) - {{ - static T[4][] vals = [ - [ 0.0L, 0.0L, 0.0L, 0.0L ], - [ 0.0L, 1.0L, 1.0L, sqrt(2.0L) ], - [ 1.0L, 1.0L, 1.0L, sqrt(3.0L) ], - [ 1.0L, 2.0L, 2.0L, 3.0L ], - [ 2.0L, 3.0L, 6.0L, 7.0L ], - [ 1.0L, 4.0L, 8.0L, 9.0L ], - [ 4.0L, 4.0L, 7.0L, 9.0L ], - [ 12.0L, 16.0L, 21.0L, 29.0L ], - [ 1e+8L, 1.0L, 1e-8L, 1e+8L+5e-9L ], - [ 1.0L, 1e+8L, 1e-8L, 1e+8L+5e-9L ], - [ 1e-8L, 1.0L, 1e+8L, 1e+8L+5e-9L ], - [ 1e-2L, 1e-4L, 1e-4L, 0.010000999950004999375L ], - [ 2147483647.0L, 2147483647.0L, 2147483647.0L, 3719550785.027307813987L ] - ]; - for (int i = 0; i < vals.length; i++) - { - T x = vals[i][0]; - T y = vals[i][1]; - T z = vals[i][2]; - T r = vals[i][3]; - T a = hypot(x, y, z); - assert(isIdentical(r, a) || isClose(r, a)); - } - }} -} - -/*********************************** - * Evaluate polynomial A(x) = $(SUB a, 0) + $(SUB a, 1)x + $(SUB a, 2)$(POWER x,2) + - * $(SUB a,3)$(POWER x,3); ... - * - * Uses Horner's rule A(x) = $(SUB a, 0) + x($(SUB a, 1) + x($(SUB a, 2) + - * x($(SUB a, 3) + ...))) - * Params: - * x = the value to evaluate. - * A = array of coefficients $(SUB a, 0), $(SUB a, 1), etc. - */ -Unqual!(CommonType!(T1, T2)) poly(T1, T2)(T1 x, in T2[] A) @trusted pure nothrow @nogc -if (isFloatingPoint!T1 && isFloatingPoint!T2) -in -{ - assert(A.length > 0); -} -do -{ - static if (is(immutable T2 == immutable real)) - { - return polyImpl(x, A); - } - else - { - return polyImplBase(x, A); - } -} - -/// ditto -Unqual!(CommonType!(T1, T2)) poly(T1, T2, int N)(T1 x, ref const T2[N] A) @safe pure nothrow @nogc -if (isFloatingPoint!T1 && isFloatingPoint!T2 && N > 0 && N <= 10) -{ - // statically unrolled version for up to 10 coefficients - typeof(return) r = A[N - 1]; - static foreach (i; 1 .. N) - { - r *= x; - r += A[N - 1 - i]; - } - return r; -} - -/// -@safe nothrow @nogc unittest -{ - real x = 3.1L; - static real[] pp = [56.1L, 32.7L, 6]; - - assert(poly(x, pp) == (56.1L + (32.7L + 6.0L * x) * x)); -} - -@safe nothrow @nogc unittest -{ - double x = 3.1; - static double[] pp = [56.1, 32.7, 6]; - double y = x; - y *= 6.0; - y += 32.7; - y *= x; - y += 56.1; - assert(poly(x, pp) == y); -} - -@safe unittest -{ - static assert(poly(3.0, [1.0, 2.0, 3.0]) == 34); -} - -private Unqual!(CommonType!(T1, T2)) polyImplBase(T1, T2)(T1 x, in T2[] A) @trusted pure nothrow @nogc -if (isFloatingPoint!T1 && isFloatingPoint!T2) -{ - ptrdiff_t i = A.length - 1; - typeof(return) r = A[i]; - while (--i >= 0) - { - r *= x; - r += A[i]; - } - return r; -} - -version (linux) version = GenericPosixVersion; -else version (FreeBSD) version = GenericPosixVersion; -else version (OpenBSD) version = GenericPosixVersion; -else version (Solaris) version = GenericPosixVersion; -else version (DragonFlyBSD) version = GenericPosixVersion; - -pragma(inline, true) // LDC -private real polyImpl(real x, in real[] A) @trusted pure nothrow @nogc -{ - version (LDC) - { - return polyImplBase(x, A); - } - else version (D_InlineAsm_X86) - { - if (__ctfe) - { - return polyImplBase(x, A); - } - version (Windows) - { - // BUG: This code assumes a frame pointer in EBP. - asm pure nothrow @nogc // assembler by W. Bright - { - // EDX = (A.length - 1) * real.sizeof - mov ECX,A[EBP] ; // ECX = A.length - dec ECX ; - lea EDX,[ECX][ECX*8] ; - add EDX,ECX ; - add EDX,A+4[EBP] ; - fld real ptr [EDX] ; // ST0 = coeff[ECX] - jecxz return_ST ; - fld x[EBP] ; // ST0 = x - fxch ST(1) ; // ST1 = x, ST0 = r - align 4 ; - L2: fmul ST,ST(1) ; // r *= x - fld real ptr -10[EDX] ; - sub EDX,10 ; // deg-- - faddp ST(1),ST ; - dec ECX ; - jne L2 ; - fxch ST(1) ; // ST1 = r, ST0 = x - fstp ST(0) ; // dump x - align 4 ; - return_ST: ; - } - } - else version (GenericPosixVersion) - { - asm pure nothrow @nogc // assembler by W. Bright - { - // EDX = (A.length - 1) * real.sizeof - mov ECX,A[EBP] ; // ECX = A.length - dec ECX ; - lea EDX,[ECX*8] ; - lea EDX,[EDX][ECX*4] ; - add EDX,A+4[EBP] ; - fld real ptr [EDX] ; // ST0 = coeff[ECX] - jecxz return_ST ; - fld x[EBP] ; // ST0 = x - fxch ST(1) ; // ST1 = x, ST0 = r - align 4 ; - L2: fmul ST,ST(1) ; // r *= x - fld real ptr -12[EDX] ; - sub EDX,12 ; // deg-- - faddp ST(1),ST ; - dec ECX ; - jne L2 ; - fxch ST(1) ; // ST1 = r, ST0 = x - fstp ST(0) ; // dump x - align 4 ; - return_ST: ; - } - } - else version (OSX) - { - asm pure nothrow @nogc // assembler by W. Bright - { - // EDX = (A.length - 1) * real.sizeof - mov ECX,A[EBP] ; // ECX = A.length - dec ECX ; - lea EDX,[ECX*8] ; - add EDX,EDX ; - add EDX,A+4[EBP] ; - fld real ptr [EDX] ; // ST0 = coeff[ECX] - jecxz return_ST ; - fld x[EBP] ; // ST0 = x - fxch ST(1) ; // ST1 = x, ST0 = r - align 4 ; - L2: fmul ST,ST(1) ; // r *= x - fld real ptr -16[EDX] ; - sub EDX,16 ; // deg-- - faddp ST(1),ST ; - dec ECX ; - jne L2 ; - fxch ST(1) ; // ST1 = r, ST0 = x - fstp ST(0) ; // dump x - align 4 ; - return_ST: ; - } - } - else - { - static assert(0); - } - } - else - { - return polyImplBase(x, A); - } -} - -/** - * Gives the next power of two after `val`. `T` can be any built-in - * numerical type. - * - * If the operation would lead to an over/underflow, this function will - * return `0`. - * - * Params: - * val = any number - * - * Returns: - * the next power of two after `val` - */ -T nextPow2(T)(const T val) -if (isIntegral!T) -{ - return powIntegralImpl!(PowType.ceil)(val); -} - -/// ditto -T nextPow2(T)(const T val) -if (isFloatingPoint!T) -{ - return powFloatingPointImpl!(PowType.ceil)(val); -} - -/// -@safe @nogc pure nothrow unittest -{ - assert(nextPow2(2) == 4); - assert(nextPow2(10) == 16); - assert(nextPow2(4000) == 4096); - - assert(nextPow2(-2) == -4); - assert(nextPow2(-10) == -16); - - assert(nextPow2(uint.max) == 0); - assert(nextPow2(uint.min) == 0); - assert(nextPow2(size_t.max) == 0); - assert(nextPow2(size_t.min) == 0); - - assert(nextPow2(int.max) == 0); - assert(nextPow2(int.min) == 0); - assert(nextPow2(long.max) == 0); - assert(nextPow2(long.min) == 0); -} - -/// -@safe @nogc pure nothrow unittest -{ - assert(nextPow2(2.1) == 4.0); - assert(nextPow2(-2.0) == -4.0); - assert(nextPow2(0.25) == 0.5); - assert(nextPow2(-4.0) == -8.0); - - assert(nextPow2(double.max) == 0.0); - assert(nextPow2(double.infinity) == double.infinity); -} - -@safe @nogc pure nothrow unittest -{ - assert(nextPow2(ubyte(2)) == 4); - assert(nextPow2(ubyte(10)) == 16); - - assert(nextPow2(byte(2)) == 4); - assert(nextPow2(byte(10)) == 16); - - assert(nextPow2(short(2)) == 4); - assert(nextPow2(short(10)) == 16); - assert(nextPow2(short(4000)) == 4096); - - assert(nextPow2(ushort(2)) == 4); - assert(nextPow2(ushort(10)) == 16); - assert(nextPow2(ushort(4000)) == 4096); -} - -@safe @nogc pure nothrow unittest -{ - foreach (ulong i; 1 .. 62) - { - assert(nextPow2(1UL << i) == 2UL << i); - assert(nextPow2((1UL << i) - 1) == 1UL << i); - assert(nextPow2((1UL << i) + 1) == 2UL << i); - assert(nextPow2((1UL << i) + (1UL<<(i-1))) == 2UL << i); - } -} - -@safe @nogc pure nothrow unittest -{ - import std.math.traits : isNaN; - import std.meta : AliasSeq; - - static foreach (T; AliasSeq!(float, double, real)) - {{ - enum T subNormal = T.min_normal / 2; - - static if (subNormal) assert(nextPow2(subNormal) == T.min_normal); - - assert(nextPow2(T(0.0)) == 0.0); - - assert(nextPow2(T(2.0)) == 4.0); - assert(nextPow2(T(2.1)) == 4.0); - assert(nextPow2(T(3.1)) == 4.0); - assert(nextPow2(T(4.0)) == 8.0); - assert(nextPow2(T(0.25)) == 0.5); - - assert(nextPow2(T(-2.0)) == -4.0); - assert(nextPow2(T(-2.1)) == -4.0); - assert(nextPow2(T(-3.1)) == -4.0); - assert(nextPow2(T(-4.0)) == -8.0); - assert(nextPow2(T(-0.25)) == -0.5); - - assert(nextPow2(T.max) == 0); - assert(nextPow2(-T.max) == 0); - - assert(nextPow2(T.infinity) == T.infinity); - assert(nextPow2(T.init).isNaN); - }} -} - -// https://issues.dlang.org/show_bug.cgi?id=15973 -@safe @nogc pure nothrow unittest -{ - assert(nextPow2(uint.max / 2) == uint.max / 2 + 1); - assert(nextPow2(uint.max / 2 + 2) == 0); - assert(nextPow2(int.max / 2) == int.max / 2 + 1); - assert(nextPow2(int.max / 2 + 2) == 0); - assert(nextPow2(int.min + 1) == int.min); -} - -/** - * Gives the last power of two before `val`. $(T) can be any built-in - * numerical type. - * - * Params: - * val = any number - * - * Returns: - * the last power of two before `val` - */ -T truncPow2(T)(const T val) -if (isIntegral!T) -{ - return powIntegralImpl!(PowType.floor)(val); -} - -/// ditto -T truncPow2(T)(const T val) -if (isFloatingPoint!T) -{ - return powFloatingPointImpl!(PowType.floor)(val); -} - -/// -@safe @nogc pure nothrow unittest -{ - assert(truncPow2(3) == 2); - assert(truncPow2(4) == 4); - assert(truncPow2(10) == 8); - assert(truncPow2(4000) == 2048); - - assert(truncPow2(-5) == -4); - assert(truncPow2(-20) == -16); - - assert(truncPow2(uint.max) == int.max + 1); - assert(truncPow2(uint.min) == 0); - assert(truncPow2(ulong.max) == long.max + 1); - assert(truncPow2(ulong.min) == 0); - - assert(truncPow2(int.max) == (int.max / 2) + 1); - version (LDC) - { - // this test relies on undefined behaviour, i.e. (1 << 63) == int.min - // that fails for LDC with optimizations enabled - } - else - { - assert(truncPow2(int.min) == int.min); - } - assert(truncPow2(long.max) == (long.max / 2) + 1); - assert(truncPow2(long.min) == long.min); -} - -/// -@safe @nogc pure nothrow unittest -{ - assert(truncPow2(2.1) == 2.0); - assert(truncPow2(7.0) == 4.0); - assert(truncPow2(-1.9) == -1.0); - assert(truncPow2(0.24) == 0.125); - assert(truncPow2(-7.0) == -4.0); - - assert(truncPow2(double.infinity) == double.infinity); -} - -@safe @nogc pure nothrow unittest -{ - assert(truncPow2(ubyte(3)) == 2); - assert(truncPow2(ubyte(4)) == 4); - assert(truncPow2(ubyte(10)) == 8); - - assert(truncPow2(byte(3)) == 2); - assert(truncPow2(byte(4)) == 4); - assert(truncPow2(byte(10)) == 8); - - assert(truncPow2(ushort(3)) == 2); - assert(truncPow2(ushort(4)) == 4); - assert(truncPow2(ushort(10)) == 8); - assert(truncPow2(ushort(4000)) == 2048); - - assert(truncPow2(short(3)) == 2); - assert(truncPow2(short(4)) == 4); - assert(truncPow2(short(10)) == 8); - assert(truncPow2(short(4000)) == 2048); -} - -@safe @nogc pure nothrow unittest -{ - foreach (ulong i; 1 .. 62) - { - assert(truncPow2(2UL << i) == 2UL << i); - assert(truncPow2((2UL << i) + 1) == 2UL << i); - assert(truncPow2((2UL << i) - 1) == 1UL << i); - assert(truncPow2((2UL << i) - (2UL<<(i-1))) == 1UL << i); - } -} - -@safe @nogc pure nothrow unittest -{ - import std.math.traits : isNaN; - import std.meta : AliasSeq; - - static foreach (T; AliasSeq!(float, double, real)) - { - assert(truncPow2(T(0.0)) == 0.0); - - assert(truncPow2(T(4.0)) == 4.0); - assert(truncPow2(T(2.1)) == 2.0); - assert(truncPow2(T(3.5)) == 2.0); - assert(truncPow2(T(7.0)) == 4.0); - assert(truncPow2(T(0.24)) == 0.125); - - assert(truncPow2(T(-2.0)) == -2.0); - assert(truncPow2(T(-2.1)) == -2.0); - assert(truncPow2(T(-3.1)) == -2.0); - assert(truncPow2(T(-7.0)) == -4.0); - assert(truncPow2(T(-0.24)) == -0.125); - - assert(truncPow2(T.infinity) == T.infinity); - assert(truncPow2(T.init).isNaN); - } -} - -private enum PowType -{ - floor, - ceil -} - -pragma(inline, true) -private T powIntegralImpl(PowType type, T)(T val) -{ - import core.bitop : bsr; - - if (val == 0 || (type == PowType.ceil && (val > T.max / 2 || val == T.min))) - return 0; - else - { - static if (isSigned!T) - return cast() cast(T) (val < 0 ? -(T(1) << bsr(0 - val) + type) : T(1) << bsr(val) + type); - else - return cast() cast(T) (T(1) << bsr(val) + type); - } -} - -private T powFloatingPointImpl(PowType type, T)(T x) -{ - import std.math.traits : copysign, isFinite; - import std.math.exponential : frexp; - - if (!x.isFinite) - return x; - - if (!x) - return x; - - int exp; - auto y = frexp(x, exp); - - static if (type == PowType.ceil) - y = core.math.ldexp(cast(T) 0.5, exp + 1); - else - y = core.math.ldexp(cast(T) 0.5, exp); - - if (!y.isFinite) - return cast(T) 0.0; - - y = copysign(y, x); - - return y; -} diff --git a/phobos/std/math/constants.d b/phobos/std/math/constants.d deleted file mode 100644 index 0c0da0d..0000000 --- a/phobos/std/math/constants.d +++ /dev/null @@ -1,38 +0,0 @@ -// Written in the D programming language. - -/** -This is a submodule of $(MREF std, math). - -It contains several useful mathematical constants. - -Copyright: Copyright The D Language Foundation 2000 - 2011. -License: $(HTTP www.boost.org/LICENSE_1_0.txt, Boost License 1.0). -Authors: $(HTTP digitalmars.com, Walter Bright), Don Clugston -Source: $(PHOBOSSRC std/math/constants.d) - -Macros: - SUB = $1$2 - PI = π - SQRT = √ - HALF = ½ - */ -module std.math.constants; - -// Values obtained from Wolfram Alpha. 116 bits ought to be enough for anybody. -// Wolfram Alpha LLC. 2011. Wolfram|Alpha. http://www.wolframalpha.com/input/?i=e+in+base+16 (access July 6, 2011). -enum real E = 0x1.5bf0a8b1457695355fb8ac404e7a8p+1L; /** e = 2.718281... */ -enum real LOG2T = 0x1.a934f0979a3715fc9257edfe9b5fbp+1L; /** $(SUB log, 2)10 = 3.321928... */ -enum real LOG2E = 0x1.71547652b82fe1777d0ffda0d23a8p+0L; /** $(SUB log, 2)e = 1.442695... */ -enum real LOG2 = 0x1.34413509f79fef311f12b35816f92p-2L; /** $(SUB log, 10)2 = 0.301029... */ -enum real LOG10E = 0x1.bcb7b1526e50e32a6ab7555f5a67cp-2L; /** $(SUB log, 10)e = 0.434294... */ -enum real LN2 = 0x1.62e42fefa39ef35793c7673007e5fp-1L; /** ln 2 = 0.693147... */ -enum real LN10 = 0x1.26bb1bbb5551582dd4adac5705a61p+1L; /** ln 10 = 2.302585... */ -enum real PI = 0x1.921fb54442d18469898cc51701b84p+1L; /** π = 3.141592... */ -enum real PI_2 = PI/2; /** $(PI) / 2 = 1.570796... */ -enum real PI_4 = PI/4; /** $(PI) / 4 = 0.785398... */ -enum real M_1_PI = 0x1.45f306dc9c882a53f84eafa3ea69cp-2L; /** 1 / $(PI) = 0.318309... */ -enum real M_2_PI = 2*M_1_PI; /** 2 / $(PI) = 0.636619... */ -enum real M_2_SQRTPI = 0x1.20dd750429b6d11ae3a914fed7fd8p+0L; /** 2 / $(SQRT)$(PI) = 1.128379... */ -enum real SQRT2 = 0x1.6a09e667f3bcc908b2fb1366ea958p+0L; /** $(SQRT)2 = 1.414213... */ -enum real SQRT1_2 = SQRT2/2; /** $(SQRT)$(HALF) = 0.707106... */ -// Note: Make sure the magic numbers in compiler backend for x87 match these. diff --git a/phobos/std/math/exponential.d b/phobos/std/math/exponential.d deleted file mode 100644 index 2d3be4a..0000000 --- a/phobos/std/math/exponential.d +++ /dev/null @@ -1,4373 +0,0 @@ -// Written in the D programming language. - -/** -This is a submodule of $(MREF std, math). - -It contains several exponential and logarithm functions. - -Copyright: Copyright The D Language Foundation 2000 - 2011. - D implementations of exp, expm1, exp2, log, log10, log1p, and log2 - functions are based on the CEPHES math library, which is Copyright - (C) 2001 Stephen L. Moshier $(LT)steve@moshier.net$(GT) and are - incorporated herein by permission of the author. The author reserves - the right to distribute this material elsewhere under different - copying permissions. These modifications are distributed here under - the following terms: -License: $(HTTP www.boost.org/LICENSE_1_0.txt, Boost License 1.0). -Authors: $(HTTP digitalmars.com, Walter Bright), Don Clugston, - Conversion of CEPHES math library to D by Iain Buclaw and David Nadlinger -Source: $(PHOBOSSRC std/math/exponential.d) - -Macros: - TABLE_SV = - - $0
Special Values
- NAN = $(RED NAN) - PLUSMN = ± - INFIN = ∞ - PLUSMNINF = ±∞ - LT = < - GT = > - */ - -module std.math.exponential; - -import std.traits : isFloatingPoint, isIntegral, isSigned, isUnsigned, Largest, Unqual; - -static import core.math; -static import core.stdc.math; - -version (LDC) -{ - import ldc.intrinsics; - - version (CRuntime_Microsoft) version = LDC_MSVCRT; - - version (LDC_MSVCRT) {} - else version (Android) {} - else - { - version (X86) version = INLINE_YL2X; - version (X86_64) version = INLINE_YL2X; - } -} - -version (DigitalMars) -{ - version (OSX) { } // macOS 13 (M1) has issues emulating instruction - else version = INLINE_YL2X; // x87 has opcodes for these -} - -version (D_InlineAsm_X86) version = InlineAsm_X86_Any; -version (D_InlineAsm_X86_64) version = InlineAsm_X86_Any; - -version (LDC_MSVCRT) {} -else version (Android) {} -else version (InlineAsm_X86_Any) version = InlineAsm_X87; -version (InlineAsm_X87) -{ - static assert(real.mant_dig == 64); - version (CRuntime_Microsoft) version = InlineAsm_X87_MSVC; -} - -version (D_HardFloat) -{ - // FloatingPointControl.clearExceptions() depends on version IeeeFlagsSupport - version (IeeeFlagsSupport) version = FloatingPointControlSupport; -} - -/** - * Compute the value of x $(SUPERSCRIPT n), where n is an integer - */ -Unqual!F pow(F, G)(F x, G n) @nogc @trusted pure nothrow -if (isFloatingPoint!(F) && isIntegral!(G)) -{ - version (none) - { - // LDC: Leads to linking error on MSVC x64 as the intrinsic maps to - // MSVC++ function `pow(double/float, int)` (a C++ template for - // Visual Studio 2015). - // Most likely not worth the effort anyway (and hindering CTFE). - pragma(inline, true); - return llvm_powi!(Unqual!F)(x, cast(int) n); - } - else - { - import std.traits : Unsigned; - - real p = 1.0, v = void; - Unsigned!(Unqual!G) m = n; - - if (n < 0) - { - if (n == -1) return 1 / x; - - m = cast(typeof(m))(0 - n); - v = p / x; - } - else - { - switch (n) - { - case 0: - return 1.0; - case 1: - return x; - case 2: - return x * x; - default: - } - - v = x; - } - - while (1) - { - if (m & 1) - p *= v; - m >>= 1; - if (!m) - break; - v *= v; - } - return p; - } // !none -} - -/// -@safe pure nothrow @nogc unittest -{ - import std.math.operations : feqrel; - - assert(pow(2.0, 5) == 32.0); - assert(pow(1.5, 9).feqrel(38.4433) > 16); - assert(pow(real.nan, 2) is real.nan); - assert(pow(real.infinity, 2) == real.infinity); -} - -@safe pure nothrow @nogc unittest -{ - import std.math.operations : isClose, feqrel; - - // Make sure it instantiates and works properly on immutable values and - // with various integer and float types. - immutable real x = 46; - immutable float xf = x; - immutable double xd = x; - immutable uint one = 1; - immutable ushort two = 2; - immutable ubyte three = 3; - immutable ulong eight = 8; - - immutable int neg1 = -1; - immutable short neg2 = -2; - immutable byte neg3 = -3; - immutable long neg8 = -8; - - - assert(pow(x,0) == 1.0); - assert(pow(xd,one) == x); - assert(pow(xf,two) == x * x); - assert(pow(x,three) == x * x * x); - assert(pow(x,eight) == (x * x) * (x * x) * (x * x) * (x * x)); - - assert(pow(x, neg1) == 1 / x); - - assert(isClose(pow(xd, neg2), cast(double) (1 / (x * x)), 1e-15)); - assert(isClose(pow(xf, neg8), cast(float) (1 / ((x * x) * (x * x) * (x * x) * (x * x))), 1e-15)); - - assert(feqrel(pow(x, neg3), 1 / (x * x * x)) >= real.mant_dig - 1); -} - -@safe @nogc nothrow unittest -{ - import std.math.operations : isClose; - - assert(isClose(pow(2.0L, 10L), 1024, 1e-18)); -} - -// https://issues.dlang.org/show_bug.cgi?id=21601 -@safe @nogc nothrow pure unittest -{ - // When reals are large enough the results of pow(b, e) can be - // calculated correctly, if b is of type float or double and e is - // not too large. - static if (real.mant_dig >= 64) - { - // expected result: 3.790e-42 - assert(pow(-513645318757045764096.0f, -2) > 0.0); - - // expected result: 3.763915357831797e-309 - assert(pow(-1.6299717435255677e+154, -2) > 0.0); - } -} - -@safe @nogc nothrow unittest -{ - import std.math.operations : isClose; - import std.math.traits : isInfinity; - - static float f1 = 19100.0f; - static float f2 = 0.000012f; - - assert(isClose(pow(f1,9), 3.3829868e+38f)); - assert(isInfinity(pow(f1,10))); - assert(pow(f2,9) > 0.0f); - assert(isClose(pow(f2,10), 0.0f, 0.0, float.min_normal)); - - static double d1 = 21800.0; - static double d2 = 0.000012; - - assert(isClose(pow(d1,71), 1.0725339442974e+308)); - assert(isInfinity(pow(d1,72))); - assert(pow(d2,65) > 0.0f); - assert(isClose(pow(d2,66), 0.0, 0.0, double.min_normal)); - - static if (real.mant_dig == 64) // x87 - { - static real r1 = 21950.0L; - static real r2 = 0.000011L; - - assert(isClose(pow(r1,1136), 7.4066175654969242752260330529e+4931L)); - assert(isInfinity(pow(r1,1137))); - assert(pow(r2,998) > 0.0L); - assert(isClose(pow(r2,999), 0.0L, 0.0, real.min_normal)); - } -} - -@safe @nogc nothrow pure unittest -{ - import std.math.operations : isClose; - - enum f1 = 19100.0f; - enum f2 = 0.000012f; - - static assert(isClose(pow(f1,9), 3.3829868e+38f)); - static assert(pow(f1,10) > float.max); - static assert(pow(f2,9) > 0.0f); - static assert(isClose(pow(f2,10), 0.0f, 0.0, float.min_normal)); - - enum d1 = 21800.0; - enum d2 = 0.000012; - - static assert(isClose(pow(d1,71), 1.0725339442974e+308)); - static assert(pow(d1,72) > double.max); - static assert(pow(d2,65) > 0.0f); - static assert(isClose(pow(d2,66), 0.0, 0.0, double.min_normal)); - - static if (real.mant_dig == 64) // x87 - { - enum r1 = 21950.0L; - enum r2 = 0.000011L; - - static assert(isClose(pow(r1,1136), 7.4066175654969242752260330529e+4931L)); - static assert(pow(r1,1137) > real.max); - static assert(pow(r2,998) > 0.0L); - static assert(isClose(pow(r2,999), 0.0L, 0.0, real.min_normal)); - } -} - -/** - * Compute the power of two integral numbers. - * - * Params: - * x = base - * n = exponent - * - * Returns: - * x raised to the power of n. If n is negative the result is 1 / pow(x, -n), - * which is calculated as integer division with remainder. This may result in - * a division by zero error. - * - * If both x and n are 0, the result is 1. - * - * Throws: - * If x is 0 and n is negative, the result is the same as the result of a - * division by zero. - */ -typeof(Unqual!(F).init * Unqual!(G).init) pow(F, G)(F x, G n) @nogc @trusted pure nothrow -if (isIntegral!(F) && isIntegral!(G)) -{ - import std.traits : isSigned; - - typeof(return) p, v = void; - Unqual!G m = n; - - static if (isSigned!(F)) - { - if (x == -1) return cast(typeof(return)) (m & 1 ? -1 : 1); - } - static if (isSigned!(G)) - { - if (x == 0 && m <= -1) return x / 0; - } - if (x == 1) return 1; - static if (isSigned!(G)) - { - if (m < 0) return 0; - } - - switch (m) - { - case 0: - p = 1; - break; - - case 1: - p = x; - break; - - case 2: - p = x * x; - break; - - default: - v = x; - p = 1; - while (1) - { - if (m & 1) - p *= v; - m >>= 1; - if (!m) - break; - v *= v; - } - break; - } - return p; -} - -/// -@safe pure nothrow @nogc unittest -{ - assert(pow(2, 3) == 8); - assert(pow(3, 2) == 9); - - assert(pow(2, 10) == 1_024); - assert(pow(2, 20) == 1_048_576); - assert(pow(2, 30) == 1_073_741_824); - - assert(pow(0, 0) == 1); - - assert(pow(1, -5) == 1); - assert(pow(1, -6) == 1); - assert(pow(-1, -5) == -1); - assert(pow(-1, -6) == 1); - - assert(pow(-2, 5) == -32); - assert(pow(-2, -5) == 0); - assert(pow(cast(double) -2, -5) == -0.03125); -} - -@safe pure nothrow @nogc unittest -{ - immutable int one = 1; - immutable byte two = 2; - immutable ubyte three = 3; - immutable short four = 4; - immutable long ten = 10; - - assert(pow(two, three) == 8); - assert(pow(two, ten) == 1024); - assert(pow(one, ten) == 1); - assert(pow(ten, four) == 10_000); - assert(pow(four, 10) == 1_048_576); - assert(pow(three, four) == 81); -} - -// https://issues.dlang.org/show_bug.cgi?id=7006 -@safe pure nothrow @nogc unittest -{ - assert(pow(5, -1) == 0); - assert(pow(-5, -1) == 0); - assert(pow(5, -2) == 0); - assert(pow(-5, -2) == 0); - assert(pow(-1, int.min) == 1); - assert(pow(-2, int.min) == 0); - - assert(pow(4294967290UL,2) == 18446744022169944100UL); - assert(pow(0,uint.max) == 0); -} - -/**Computes integer to floating point powers.*/ -real pow(I, F)(I x, F y) @nogc @trusted pure nothrow -if (isIntegral!I && isFloatingPoint!F) -{ - return pow(cast(real) x, cast(Unqual!F) y); -} - -/// -@safe pure nothrow @nogc unittest -{ - assert(pow(2, 5.0) == 32.0); - assert(pow(7, 3.0) == 343.0); - assert(pow(2, real.nan) is real.nan); - assert(pow(2, real.infinity) == real.infinity); -} - -/** - * Calculates x$(SUPERSCRIPT y). - * - * $(TABLE_SV - * $(TR $(TH x) $(TH y) $(TH pow(x, y)) - * $(TH div 0) $(TH invalid?)) - * $(TR $(TD anything) $(TD $(PLUSMN)0.0) $(TD 1.0) - * $(TD no) $(TD no) ) - * $(TR $(TD |x| $(GT) 1) $(TD +$(INFIN)) $(TD +$(INFIN)) - * $(TD no) $(TD no) ) - * $(TR $(TD |x| $(LT) 1) $(TD +$(INFIN)) $(TD +0.0) - * $(TD no) $(TD no) ) - * $(TR $(TD |x| $(GT) 1) $(TD -$(INFIN)) $(TD +0.0) - * $(TD no) $(TD no) ) - * $(TR $(TD |x| $(LT) 1) $(TD -$(INFIN)) $(TD +$(INFIN)) - * $(TD no) $(TD no) ) - * $(TR $(TD +$(INFIN)) $(TD $(GT) 0.0) $(TD +$(INFIN)) - * $(TD no) $(TD no) ) - * $(TR $(TD +$(INFIN)) $(TD $(LT) 0.0) $(TD +0.0) - * $(TD no) $(TD no) ) - * $(TR $(TD -$(INFIN)) $(TD odd integer $(GT) 0.0) $(TD -$(INFIN)) - * $(TD no) $(TD no) ) - * $(TR $(TD -$(INFIN)) $(TD $(GT) 0.0, not odd integer) $(TD +$(INFIN)) - * $(TD no) $(TD no)) - * $(TR $(TD -$(INFIN)) $(TD odd integer $(LT) 0.0) $(TD -0.0) - * $(TD no) $(TD no) ) - * $(TR $(TD -$(INFIN)) $(TD $(LT) 0.0, not odd integer) $(TD +0.0) - * $(TD no) $(TD no) ) - * $(TR $(TD $(PLUSMN)1.0) $(TD $(PLUSMN)$(INFIN)) $(TD -$(NAN)) - * $(TD no) $(TD yes) ) - * $(TR $(TD $(LT) 0.0) $(TD finite, nonintegral) $(TD $(NAN)) - * $(TD no) $(TD yes)) - * $(TR $(TD $(PLUSMN)0.0) $(TD odd integer $(LT) 0.0) $(TD $(PLUSMNINF)) - * $(TD yes) $(TD no) ) - * $(TR $(TD $(PLUSMN)0.0) $(TD $(LT) 0.0, not odd integer) $(TD +$(INFIN)) - * $(TD yes) $(TD no)) - * $(TR $(TD $(PLUSMN)0.0) $(TD odd integer $(GT) 0.0) $(TD $(PLUSMN)0.0) - * $(TD no) $(TD no) ) - * $(TR $(TD $(PLUSMN)0.0) $(TD $(GT) 0.0, not odd integer) $(TD +0.0) - * $(TD no) $(TD no) ) - * ) - */ -Unqual!(Largest!(F, G)) pow(F, G)(F x, G y) @nogc @trusted pure nothrow -if (isFloatingPoint!(F) && isFloatingPoint!(G)) -{ - import core.math : fabs, sqrt; - import std.math.traits : isInfinity, isNaN, signbit; - - alias Float = typeof(return); - - version (none) // LDC FIXME: Use of this LLVM intrinsic causes a unit test failure - { - pragma(inline, true); - return llvm_pow!(Float)(x, y); - } - else - { - static real impl(real x, real y) @nogc pure nothrow - { - // Special cases. - if (isNaN(y)) - return y; - if (isNaN(x) && y != 0.0) - return x; - - // Even if x is NaN. - if (y == 0.0) - return 1.0; - if (y == 1.0) - return x; - - if (isInfinity(y)) - { - if (isInfinity(x)) - { - if (!signbit(y) && !signbit(x)) - return F.infinity; - else - return F.nan; - } - else if (fabs(x) > 1) - { - if (signbit(y)) - return +0.0; - else - return F.infinity; - } - else if (fabs(x) == 1) - { - return F.nan; - } - else // < 1 - { - if (signbit(y)) - return F.infinity; - else - return +0.0; - } - } - if (isInfinity(x)) - { - if (signbit(x)) - { - long i = cast(long) y; - if (y > 0.0) - { - if (i == y && i & 1) - return -F.infinity; - else if (i == y) - return F.infinity; - else - return -F.nan; - } - else if (y < 0.0) - { - if (i == y && i & 1) - return -0.0; - else if (i == y) - return +0.0; - else - return F.nan; - } - } - else - { - if (y > 0.0) - return F.infinity; - else if (y < 0.0) - return +0.0; - } - } - - if (x == 0.0) - { - if (signbit(x)) - { - long i = cast(long) y; - if (y > 0.0) - { - if (i == y && i & 1) - return -0.0; - else - return +0.0; - } - else if (y < 0.0) - { - if (i == y && i & 1) - return -F.infinity; - else - return F.infinity; - } - } - else - { - if (y > 0.0) - return +0.0; - else if (y < 0.0) - return F.infinity; - } - } - if (x == 1.0) - return 1.0; - - if (y >= F.max) - { - if ((x > 0.0 && x < 1.0) || (x > -1.0 && x < 0.0)) - return 0.0; - if (x > 1.0 || x < -1.0) - return F.infinity; - } - if (y <= -F.max) - { - if ((x > 0.0 && x < 1.0) || (x > -1.0 && x < 0.0)) - return F.infinity; - if (x > 1.0 || x < -1.0) - return 0.0; - } - - if (x >= F.max) - { - if (y > 0.0) - return F.infinity; - else - return 0.0; - } - if (x <= -F.max) - { - long i = cast(long) y; - if (y > 0.0) - { - if (i == y && i & 1) - return -F.infinity; - else - return F.infinity; - } - else if (y < 0.0) - { - if (i == y && i & 1) - return -0.0; - else - return +0.0; - } - } - - // Integer power of x. - long iy = cast(long) y; - if (iy == y && fabs(y) < 32_768.0) - return pow(x, iy); - - real sign = 1.0; - if (x < 0) - { - // Result is real only if y is an integer - // Check for a non-zero fractional part - enum maxOdd = pow(2.0L, real.mant_dig) - 1.0L; - static if (maxOdd > ulong.max) - { - // Generic method, for any FP type - import std.math.rounding : floor; - if (floor(y) != y) - return sqrt(x); // Complex result -- create a NaN - - const hy = 0.5 * y; - if (floor(hy) != hy) - sign = -1.0; - } - else - { - // Much faster, if ulong has enough precision - const absY = fabs(y); - if (absY <= maxOdd) - { - const uy = cast(ulong) absY; - if (uy != absY) - return sqrt(x); // Complex result -- create a NaN - - if (uy & 1) - sign = -1.0; - } - } - x = -x; - } - version (INLINE_YL2X) - { - // If x > 0, x ^^ y == 2 ^^ ( y * log2(x) ) - // TODO: This is not accurate in practice. A fast and accurate - // (though complicated) method is described in: - // "An efficient rounding boundary test for pow(x, y) - // in double precision", C.Q. Lauter and V. Lefèvre, INRIA (2007). - return sign * exp2( core.math.yl2x(x, y) ); - } - else - { - // If x > 0, x ^^ y == 2 ^^ ( y * log2(x) ) - // TODO: This is not accurate in practice. A fast and accurate - // (though complicated) method is described in: - // "An efficient rounding boundary test for pow(x, y) - // in double precision", C.Q. Lauter and V. Lefèvre, INRIA (2007). - Float w = exp2(y * log2(x)); - return sign * w; - } - } - return impl(x, y); - } // !none -} - -/// -@safe pure nothrow @nogc unittest -{ - import std.math.operations : isClose; - - assert(isClose(pow(2.0, 3.0), 8.0)); - assert(isClose(pow(1.5, 10.0), 57.6650390625)); - - // square root of 9 - assert(isClose(pow(9.0, 0.5), 3.0)); - // 10th root of 1024 - assert(isClose(pow(1024.0, 0.1), 2.0)); - - assert(isClose(pow(-4.0, 3.0), -64.0)); - - // reciprocal of 4 ^^ 2 - assert(isClose(pow(4.0, -2.0), 0.0625)); - // reciprocal of (-2) ^^ 3 - assert(isClose(pow(-2.0, -3.0), -0.125)); - - assert(isClose(pow(-2.5, 3.0), -15.625)); - // reciprocal of 2.5 ^^ 3 - assert(isClose(pow(2.5, -3.0), 0.064)); - // reciprocal of (-2.5) ^^ 3 - assert(isClose(pow(-2.5, -3.0), -0.064)); - - // reciprocal of square root of 4 - assert(isClose(pow(4.0, -0.5), 0.5)); - - // per definition - assert(isClose(pow(0.0, 0.0), 1.0)); -} - -/// -@safe pure nothrow @nogc unittest -{ - import std.math.operations : isClose; - - // the result is a complex number - // which cannot be represented as floating point number - import std.math.traits : isNaN; - assert(isNaN(pow(-2.5, -1.5))); - - // use the ^^-operator of std.complex instead - import std.complex : complex; - auto c1 = complex(-2.5, 0.0); - auto c2 = complex(-1.5, 0.0); - auto result = c1 ^^ c2; - // exact result apparently depends on `real` precision => increased tolerance - assert(isClose(result.re, -4.64705438e-17, 2e-4)); - assert(isClose(result.im, 2.52982e-1, 2e-4)); -} - -@safe pure nothrow @nogc unittest -{ - import std.math.traits : isNaN; - - assert(pow(1.5, real.infinity) == real.infinity); - assert(pow(0.5, real.infinity) == 0.0); - assert(pow(1.5, -real.infinity) == 0.0); - assert(pow(0.5, -real.infinity) == real.infinity); - assert(pow(real.infinity, 1.0) == real.infinity); - assert(pow(real.infinity, -1.0) == 0.0); - assert(pow(real.infinity, real.infinity) == real.infinity); - assert(pow(-real.infinity, 1.0) == -real.infinity); - assert(pow(-real.infinity, 2.0) == real.infinity); - assert(pow(-real.infinity, -1.0) == -0.0); - assert(pow(-real.infinity, -2.0) == 0.0); - assert(isNaN(pow(1.0, real.infinity))); - assert(pow(0.0, -1.0) == real.infinity); - assert(pow(real.nan, 0.0) == 1.0); - assert(isNaN(pow(real.nan, 3.0))); - assert(isNaN(pow(3.0, real.nan))); -} - -@safe @nogc nothrow unittest -{ - import std.math.operations : isClose; - - assert(isClose(pow(2.0L, 10.0L), 1024, 1e-18)); -} - -@safe pure nothrow @nogc unittest -{ - import std.math.operations : isClose; - import std.math.traits : isIdentical, isNaN; - import std.math.constants : PI; - - // Test all the special values. These unittests can be run on Windows - // by temporarily changing the version (linux) to version (all). - immutable float zero = 0; - immutable real one = 1; - immutable double two = 2; - immutable float three = 3; - immutable float fnan = float.nan; - immutable double dnan = double.nan; - immutable real rnan = real.nan; - immutable dinf = double.infinity; - immutable rninf = -real.infinity; - - assert(pow(fnan, zero) == 1); - assert(pow(dnan, zero) == 1); - assert(pow(rnan, zero) == 1); - - assert(pow(two, dinf) == double.infinity); - assert(isIdentical(pow(0.2f, dinf), +0.0)); - assert(pow(0.99999999L, rninf) == real.infinity); - assert(isIdentical(pow(1.000000001, rninf), +0.0)); - assert(pow(dinf, 0.001) == dinf); - assert(isIdentical(pow(dinf, -0.001), +0.0)); - assert(pow(rninf, 3.0L) == rninf); - assert(pow(rninf, 2.0L) == real.infinity); - assert(isIdentical(pow(rninf, -3.0), -0.0)); - assert(isIdentical(pow(rninf, -2.0), +0.0)); - - // @@@BUG@@@ somewhere - version (OSX) {} else assert(isNaN(pow(one, dinf))); - version (OSX) {} else assert(isNaN(pow(-one, dinf))); - assert(isNaN(pow(-0.2, PI))); - // boundary cases. Note that epsilon == 2^^-n for some n, - // so 1/epsilon == 2^^n is always even. - assert(pow(-1.0L, 1/real.epsilon - 1.0L) == -1.0L); - static if (LLVM_version >= 1300) { /* LDC: on x86, yields -1 with enabled optimizations */ } else - assert(pow(-1.0L, 1/real.epsilon) == 1.0L); - assert(isNaN(pow(-1.0L, 1/real.epsilon-0.5L))); - assert(isNaN(pow(-1.0L, -1/real.epsilon+0.5L))); - - assert(pow(0.0, -3.0) == double.infinity); - assert(pow(-0.0, -3.0) == -double.infinity); - assert(pow(0.0, -PI) == double.infinity); - assert(pow(-0.0, -PI) == double.infinity); - assert(isIdentical(pow(0.0, 5.0), 0.0)); - assert(isIdentical(pow(-0.0, 5.0), -0.0)); - assert(isIdentical(pow(0.0, 6.0), 0.0)); - assert(isIdentical(pow(-0.0, 6.0), 0.0)); - - // https://issues.dlang.org/show_bug.cgi?id=14786 fixed - immutable real maxOdd = pow(2.0L, real.mant_dig) - 1.0L; - assert(pow(-1.0L, maxOdd) == -1.0L); - assert(pow(-1.0L, -maxOdd) == -1.0L); - assert(pow(-1.0L, maxOdd + 1.0L) == 1.0L); - assert(pow(-1.0L, -maxOdd + 1.0L) == 1.0L); - assert(pow(-1.0L, maxOdd - 1.0L) == 1.0L); - assert(pow(-1.0L, -maxOdd - 1.0L) == 1.0L); - - // Now, actual numbers. - assert(isClose(pow(two, three), 8.0)); - assert(isClose(pow(two, -2.5), 0.1767766953)); - - // Test integer to float power. - immutable uint twoI = 2; - assert(isClose(pow(twoI, three), 8.0)); -} - -// https://issues.dlang.org/show_bug.cgi?id=20508 -@safe pure nothrow @nogc unittest -{ - import std.math.traits : isNaN; - - assert(isNaN(pow(-double.infinity, 0.5))); - - assert(isNaN(pow(-real.infinity, real.infinity))); - assert(isNaN(pow(-real.infinity, -real.infinity))); - assert(isNaN(pow(-real.infinity, 1.234))); - assert(isNaN(pow(-real.infinity, -0.751))); - assert(pow(-real.infinity, 0.0) == 1.0); -} - -/** Computes the value of a positive integer `x`, raised to the power `n`, modulo `m`. - * - * Params: - * x = base - * n = exponent - * m = modulus - * - * Returns: - * `x` to the power `n`, modulo `m`. - * The return type is the largest of `x`'s and `m`'s type. - * - * The function requires that all values have unsigned types. - */ -Unqual!(Largest!(F, H)) powmod(F, G, H)(F x, G n, H m) -if (isUnsigned!F && isUnsigned!G && isUnsigned!H) -{ - import std.meta : AliasSeq; - - alias T = Unqual!(Largest!(F, H)); - static if (T.sizeof <= 4) - { - alias DoubleT = AliasSeq!(void, ushort, uint, void, ulong)[T.sizeof]; - } - - static T mulmod(T a, T b, T c) - { - static if (T.sizeof == 8) - { - static T addmod(T a, T b, T c) - { - b = c - b; - if (a >= b) - return a - b; - else - return c - b + a; - } - - T result = 0, tmp; - - b %= c; - while (a > 0) - { - if (a & 1) - result = addmod(result, b, c); - - a >>= 1; - b = addmod(b, b, c); - } - - return result; - } - else - { - DoubleT result = cast(DoubleT) (cast(DoubleT) a * cast(DoubleT) b); - return result % c; - } - } - - T base = x, result = 1, modulus = m; - Unqual!G exponent = n; - - while (exponent > 0) - { - if (exponent & 1) - result = mulmod(result, base, modulus); - - base = mulmod(base, base, modulus); - exponent >>= 1; - } - - return result; -} - -/// -@safe pure nothrow @nogc unittest -{ - assert(powmod(1U, 10U, 3U) == 1); - assert(powmod(3U, 2U, 6U) == 3); - assert(powmod(5U, 5U, 15U) == 5); - assert(powmod(2U, 3U, 5U) == 3); - assert(powmod(2U, 4U, 5U) == 1); - assert(powmod(2U, 5U, 5U) == 2); -} - -@safe pure nothrow @nogc unittest -{ - ulong a = 18446744073709551615u, b = 20u, c = 18446744073709551610u; - assert(powmod(a, b, c) == 95367431640625u); - a = 100; b = 7919; c = 18446744073709551557u; - assert(powmod(a, b, c) == 18223853583554725198u); - a = 117; b = 7919; c = 18446744073709551557u; - assert(powmod(a, b, c) == 11493139548346411394u); - a = 134; b = 7919; c = 18446744073709551557u; - assert(powmod(a, b, c) == 10979163786734356774u); - a = 151; b = 7919; c = 18446744073709551557u; - assert(powmod(a, b, c) == 7023018419737782840u); - a = 168; b = 7919; c = 18446744073709551557u; - assert(powmod(a, b, c) == 58082701842386811u); - a = 185; b = 7919; c = 18446744073709551557u; - assert(powmod(a, b, c) == 17423478386299876798u); - a = 202; b = 7919; c = 18446744073709551557u; - assert(powmod(a, b, c) == 5522733478579799075u); - a = 219; b = 7919; c = 18446744073709551557u; - assert(powmod(a, b, c) == 15230218982491623487u); - a = 236; b = 7919; c = 18446744073709551557u; - assert(powmod(a, b, c) == 5198328724976436000u); - - a = 0; b = 7919; c = 18446744073709551557u; - assert(powmod(a, b, c) == 0); - a = 123; b = 0; c = 18446744073709551557u; - assert(powmod(a, b, c) == 1); - - immutable ulong a1 = 253, b1 = 7919, c1 = 18446744073709551557u; - assert(powmod(a1, b1, c1) == 3883707345459248860u); - - uint x = 100 ,y = 7919, z = 1844674407u; - assert(powmod(x, y, z) == 1613100340u); - x = 134; y = 7919; z = 1844674407u; - assert(powmod(x, y, z) == 734956622u); - x = 151; y = 7919; z = 1844674407u; - assert(powmod(x, y, z) == 1738696945u); - x = 168; y = 7919; z = 1844674407u; - assert(powmod(x, y, z) == 1247580927u); - x = 185; y = 7919; z = 1844674407u; - assert(powmod(x, y, z) == 1293855176u); - x = 202; y = 7919; z = 1844674407u; - assert(powmod(x, y, z) == 1566963682u); - x = 219; y = 7919; z = 1844674407u; - assert(powmod(x, y, z) == 181227807u); - x = 236; y = 7919; z = 1844674407u; - assert(powmod(x, y, z) == 217988321u); - x = 253; y = 7919; z = 1844674407u; - assert(powmod(x, y, z) == 1588843243u); - - x = 0; y = 7919; z = 184467u; - assert(powmod(x, y, z) == 0); - x = 123; y = 0; z = 1844674u; - assert(powmod(x, y, z) == 1); - - immutable ubyte x1 = 117; - immutable uint y1 = 7919; - immutable uint z1 = 1844674407u; - auto res = powmod(x1, y1, z1); - assert(is(typeof(res) == uint)); - assert(res == 9479781u); - - immutable ushort x2 = 123; - immutable uint y2 = 203; - immutable ubyte z2 = 113; - auto res2 = powmod(x2, y2, z2); - assert(is(typeof(res2) == ushort)); - assert(res2 == 42u); -} - -/** - * Calculates e$(SUPERSCRIPT x). - * - * $(TABLE_SV - * $(TR $(TH x) $(TH e$(SUPERSCRIPT x)) ) - * $(TR $(TD +$(INFIN)) $(TD +$(INFIN)) ) - * $(TR $(TD -$(INFIN)) $(TD +0.0) ) - * $(TR $(TD $(NAN)) $(TD $(NAN)) ) - * ) - */ -version (none) // LDC FIXME: Use of this LLVM intrinsic causes a unit test failure -{ - pragma(inline, true): - real exp(real x) @safe pure nothrow @nogc { return llvm_exp(x); } - ///ditto - double exp(double x) @safe pure nothrow @nogc { return llvm_exp(x); } - ///ditto - float exp(float x) @safe pure nothrow @nogc { return llvm_exp(x); } -} -else -{ - -pragma(inline, true) -real exp(real x) @trusted pure nothrow @nogc // TODO: @safe -{ - version (InlineAsm_X87) - { - import std.math.constants : LOG2E; - - // e^^x = 2^^(LOG2E*x) - // (This is valid because the overflow & underflow limits for exp - // and exp2 are so similar). - if (!__ctfe) - return exp2Asm(LOG2E*x); - } - return expImpl(x); -} - -/// ditto -pragma(inline, true) -double exp(double x) @safe pure nothrow @nogc { return __ctfe ? cast(double) exp(cast(real) x) : expImpl(x); } - -/// ditto -pragma(inline, true) -float exp(float x) @safe pure nothrow @nogc { return __ctfe ? cast(float) exp(cast(real) x) : expImpl(x); } - -} // !none - -/// -@safe unittest -{ - import std.math.operations : feqrel; - import std.math.constants : E; - - assert(exp(0.0) == 1.0); - assert(exp(3.0).feqrel(E * E * E) > 16); -} - -private T expImpl(T)(T x) @safe pure nothrow @nogc -{ - import std.math.traits : floatTraits, RealFormat, isNaN; - import std.math.rounding : floor; - import std.math.algebraic : poly; - import std.math.constants : LOG2E; - - alias F = floatTraits!T; - static if (F.realFormat == RealFormat.ieeeSingle) - { - static immutable T[6] P = [ - 5.0000001201E-1, - 1.6666665459E-1, - 4.1665795894E-2, - 8.3334519073E-3, - 1.3981999507E-3, - 1.9875691500E-4, - ]; - - enum T C1 = 0.693359375; - enum T C2 = -2.12194440e-4; - - // Overflow and Underflow limits. - enum T OF = 88.72283905206835; - enum T UF = -103.278929903431851103; // ln(2^-149) - } - else static if (F.realFormat == RealFormat.ieeeDouble) - { - // Coefficients for exp(x) - static immutable T[3] P = [ - 9.99999999999999999910E-1L, - 3.02994407707441961300E-2L, - 1.26177193074810590878E-4L, - ]; - static immutable T[4] Q = [ - 2.00000000000000000009E0L, - 2.27265548208155028766E-1L, - 2.52448340349684104192E-3L, - 3.00198505138664455042E-6L, - ]; - - // C1 + C2 = LN2. - enum T C1 = 6.93145751953125E-1; - enum T C2 = 1.42860682030941723212E-6; - - // Overflow and Underflow limits. - enum T OF = 7.09782712893383996732E2; // ln((1-2^-53) * 2^1024) - enum T UF = -7.451332191019412076235E2; // ln(2^-1075) - } - else static if (F.realFormat == RealFormat.ieeeExtended || - F.realFormat == RealFormat.ieeeExtended53) - { - // Coefficients for exp(x) - static immutable T[3] P = [ - 9.9999999999999999991025E-1L, - 3.0299440770744196129956E-2L, - 1.2617719307481059087798E-4L, - ]; - static immutable T[4] Q = [ - 2.0000000000000000000897E0L, - 2.2726554820815502876593E-1L, - 2.5244834034968410419224E-3L, - 3.0019850513866445504159E-6L, - ]; - - // C1 + C2 = LN2. - enum T C1 = 6.9314575195312500000000E-1L; - enum T C2 = 1.4286068203094172321215E-6L; - - // Overflow and Underflow limits. - enum T OF = 1.1356523406294143949492E4L; // ln((1-2^-64) * 2^16384) - enum T UF = -1.13994985314888605586758E4L; // ln(2^-16446) - } - else static if (F.realFormat == RealFormat.ieeeQuadruple) - { - // Coefficients for exp(x) - 1 - static immutable T[5] P = [ - 9.999999999999999999999999999999999998502E-1L, - 3.508710990737834361215404761139478627390E-2L, - 2.708775201978218837374512615596512792224E-4L, - 6.141506007208645008909088812338454698548E-7L, - 3.279723985560247033712687707263393506266E-10L - ]; - static immutable T[6] Q = [ - 2.000000000000000000000000000000000000150E0, - 2.368408864814233538909747618894558968880E-1L, - 3.611828913847589925056132680618007270344E-3L, - 1.504792651814944826817779302637284053660E-5L, - 1.771372078166251484503904874657985291164E-8L, - 2.980756652081995192255342779918052538681E-12L - ]; - - // C1 + C2 = LN2. - enum T C1 = 6.93145751953125E-1L; - enum T C2 = 1.428606820309417232121458176568075500134E-6L; - - // Overflow and Underflow limits. - enum T OF = 1.135583025911358400418251384584930671458833e4L; - enum T UF = -1.143276959615573793352782661133116431383730e4L; - } - else - static assert(0, "Not implemented for this architecture"); - - // Special cases. - if (isNaN(x)) - return x; - if (x > OF) - return real.infinity; - if (x < UF) - return 0.0; - - // Express: e^^x = e^^g * 2^^n - // = e^^g * e^^(n * LOG2E) - // = e^^(g + n * LOG2E) - T xx = floor((cast(T) LOG2E) * x + cast(T) 0.5); - const int n = cast(int) xx; - x -= xx * C1; - x -= xx * C2; - - static if (F.realFormat == RealFormat.ieeeSingle) - { - xx = x * x; - x = poly(x, P) * xx + x + 1.0f; - } - else - { - // Rational approximation for exponential of the fractional part: - // e^^x = 1 + 2x P(x^^2) / (Q(x^^2) - P(x^^2)) - xx = x * x; - const T px = x * poly(xx, P); - x = px / (poly(xx, Q) - px); - x = (cast(T) 1.0) + (cast(T) 2.0) * x; - } - - // Scale by power of 2. - x = core.math.ldexp(x, n); - - return x; -} - -@safe @nogc nothrow unittest -{ - import std.math.traits : floatTraits, RealFormat; - import std.math.operations : NaN, feqrel, isClose; - import std.math.constants : E; - import std.math.traits : isIdentical; - import std.math.algebraic : abs; - - version (IeeeFlagsSupport) import std.math.hardware : IeeeFlags, resetIeeeFlags, ieeeFlags; - version (FloatingPointControlSupport) - { - import std.math.hardware : FloatingPointControl; - - FloatingPointControl ctrl; - if (FloatingPointControl.hasExceptionTraps) - ctrl.disableExceptions(FloatingPointControl.allExceptions); - ctrl.rounding = FloatingPointControl.roundToNearest; - } - - static void testExp(T)() - { - enum realFormat = floatTraits!T.realFormat; - static if (realFormat == RealFormat.ieeeQuadruple) - { - static immutable T[2][] exptestpoints = - [ // x exp(x) - [ 1.0L, E ], - [ 0.5L, 0x1.a61298e1e069bc972dfefab6df34p+0L ], - [ 3.0L, E*E*E ], - [ 0x1.6p+13L, 0x1.6e509d45728655cdb4840542acb5p+16250L ], // near overflow - [ 0x1.7p+13L, T.infinity ], // close overflow - [ 0x1p+80L, T.infinity ], // far overflow - [ T.infinity, T.infinity ], - [-0x1.18p+13L, 0x1.5e4bf54b4807034ea97fef0059a6p-12927L ], // near underflow - [-0x1.625p+13L, 0x1.a6bd68a39d11fec3a250cd97f524p-16358L ], // ditto - [-0x1.62dafp+13L, 0x0.cb629e9813b80ed4d639e875be6cp-16382L ], // near underflow - subnormal - [-0x1.6549p+13L, 0x0.0000000000000000000000000001p-16382L ], // ditto - [-0x1.655p+13L, 0 ], // close underflow - [-0x1p+30L, 0 ], // far underflow - ]; - } - else static if (realFormat == RealFormat.ieeeExtended || - realFormat == RealFormat.ieeeExtended53) - { - static immutable T[2][] exptestpoints = - [ // x exp(x) - [ 1.0L, E ], - [ 0.5L, 0x1.a61298e1e069bc97p+0L ], - [ 3.0L, E*E*E ], - [ 0x1.1p+13L, 0x1.29aeffefc8ec645p+12557L ], // near overflow - [ 0x1.7p+13L, T.infinity ], // close overflow - [ 0x1p+80L, T.infinity ], // far overflow - [ T.infinity, T.infinity ], - [-0x1.18p+13L, 0x1.5e4bf54b4806db9p-12927L ], // near underflow - [-0x1.625p+13L, 0x1.a6bd68a39d11f35cp-16358L ], // ditto - [-0x1.62dafp+13L, 0x1.96c53d30277021dp-16383L ], // near underflow - subnormal - [-0x1.643p+13L, 0x1p-16444L ], // ditto - [-0x1.645p+13L, 0 ], // close underflow - [-0x1p+30L, 0 ], // far underflow - ]; - } - else static if (realFormat == RealFormat.ieeeDouble) - { - static immutable T[2][] exptestpoints = - [ // x, exp(x) - [ 1.0L, E ], - [ 0.5L, 0x1.a61298e1e069cp+0L ], - [ 3.0L, E*E*E ], - [ 0x1.6p+9L, 0x1.93bf4ec282efbp+1015L ], // near overflow - [ 0x1.7p+9L, T.infinity ], // close overflow - [ 0x1p+80L, T.infinity ], // far overflow - [ T.infinity, T.infinity ], - [-0x1.6p+9L, 0x1.44a3824e5285fp-1016L ], // near underflow - [-0x1.64p+9L, 0x0.06f84920bb2d4p-1022L ], // near underflow - subnormal - [-0x1.743p+9L, 0x0.0000000000001p-1022L ], // ditto - [-0x1.8p+9L, 0 ], // close underflow - [-0x1p+30L, 0 ], // far underflow - ]; - } - else static if (realFormat == RealFormat.ieeeSingle) - { - static immutable T[2][] exptestpoints = - [ // x, exp(x) - [ 1.0L, E ], - [ 0.5L, 0x1.a61299p+0L ], - [ 3.0L, E*E*E ], - [ 0x1.62p+6L, 0x1.99b988p+127L ], // near overflow - [ 0x1.7p+6L, T.infinity ], // close overflow - [ 0x1p+80L, T.infinity ], // far overflow - [ T.infinity, T.infinity ], - [-0x1.5cp+6L, 0x1.666d0ep-126L ], // near underflow - [-0x1.7p+6L, 0x0.026a42p-126L ], // near underflow - subnormal - [-0x1.9cp+6L, 0x0.000002p-126L ], // ditto - [-0x1.ap+6L, 0 ], // close underflow - [-0x1p+30L, 0 ], // far underflow - ]; - } - else - static assert(0, "No exp() tests for real type!"); - - const minEqualMantissaBits = T.mant_dig - 2; - T x; - version (IeeeFlagsSupport) IeeeFlags f; - foreach (ref pair; exptestpoints) - { - version (IeeeFlagsSupport) resetIeeeFlags(); - x = exp(pair[0]); - //printf("exp(%La) = %La, should be %La\n", cast(real) pair[0], cast(real) x, cast(real) pair[1]); - assert(feqrel(x, pair[1]) >= minEqualMantissaBits); - } - - // Ideally, exp(0) would not set the inexact flag. - // Unfortunately, fldl2e sets it! - // So it's not realistic to avoid setting it. - assert(exp(cast(T) 0.0) == 1.0); - - // NaN propagation. Doesn't set flags, bcos was already NaN. - version (IeeeFlagsSupport) - { - resetIeeeFlags(); - x = exp(T.nan); - f = ieeeFlags; - assert(isIdentical(abs(x), T.nan)); - assert(f.flags == 0); - - resetIeeeFlags(); - x = exp(-T.nan); - f = ieeeFlags; - assert(isIdentical(abs(x), T.nan)); - assert(f.flags == 0); - } - else - { - x = exp(T.nan); - assert(isIdentical(abs(x), T.nan)); - - x = exp(-T.nan); - assert(isIdentical(abs(x), T.nan)); - } - - x = exp(NaN(0x123)); - assert(isIdentical(x, NaN(0x123))); - } - - import std.meta : AliasSeq; - foreach (T; AliasSeq!(real, double, float)) - testExp!T(); - - // High resolution test (verified against GNU MPFR/Mathematica). - assert(exp(0.5L) == 0x1.A612_98E1_E069_BC97_2DFE_FAB6_DF34p+0L); - - assert(isClose(exp(3.0L), E * E * E, real.sizeof > double.sizeof ? 1e-15 : 1e-14)); -} - -/** - * Calculates the value of the natural logarithm base (e) - * raised to the power of x, minus 1. - * - * For very small x, expm1(x) is more accurate - * than exp(x)-1. - * - * $(TABLE_SV - * $(TR $(TH x) $(TH e$(SUPERSCRIPT x)-1) ) - * $(TR $(TD $(PLUSMN)0.0) $(TD $(PLUSMN)0.0) ) - * $(TR $(TD +$(INFIN)) $(TD +$(INFIN)) ) - * $(TR $(TD -$(INFIN)) $(TD -1.0) ) - * $(TR $(TD $(NAN)) $(TD $(NAN)) ) - * ) - */ -pragma(inline, true) -real expm1(real x) @trusted pure nothrow @nogc // TODO: @safe -{ - version (InlineAsm_X87) - { - if (!__ctfe) - return expm1Asm(x); - } - return expm1Impl(x); -} - -/// ditto -pragma(inline, true) -double expm1(double x) @safe pure nothrow @nogc -{ - return __ctfe ? cast(double) expm1(cast(real) x) : expm1Impl(x); -} - -/// ditto -pragma(inline, true) -float expm1(float x) @safe pure nothrow @nogc -{ - // no single-precision version in Cephes => use double precision - return __ctfe ? cast(float) expm1(cast(real) x) : cast(float) expm1Impl(cast(double) x); -} - -/// -@safe unittest -{ - import std.math.traits : isIdentical; - import std.math.operations : feqrel; - - assert(isIdentical(expm1(0.0), 0.0)); - assert(expm1(1.0).feqrel(1.71828) > 16); - assert(expm1(2.0).feqrel(6.3890) > 16); -} - -version (InlineAsm_X87) -private real expm1Asm(real x) @trusted pure nothrow @nogc -{ - version (X86) - { - enum PARAMSIZE = (real.sizeof+3)&(0xFFFF_FFFC); // always a multiple of 4 - asm pure nothrow @nogc - { - /* expm1() for x87 80-bit reals, IEEE754-2008 conformant. - * Author: Don Clugston. - * - * expm1(x) = 2^^(rndint(y))* 2^^(y-rndint(y)) - 1 where y = LN2*x. - * = 2rndy * 2ym1 + 2rndy - 1, where 2rndy = 2^^(rndint(y)) - * and 2ym1 = (2^^(y-rndint(y))-1). - * If 2rndy < 0.5*real.epsilon, result is -1. - * Implementation is otherwise the same as for exp2() - */ - naked; - fld real ptr [ESP+4] ; // x - mov AX, [ESP+4+8]; // AX = exponent and sign - sub ESP, 12+8; // Create scratch space on the stack - // [ESP,ESP+2] = scratchint - // [ESP+4..+6, +8..+10, +10] = scratchreal - // set scratchreal mantissa = 1.0 - mov dword ptr [ESP+8], 0; - mov dword ptr [ESP+8+4], 0x80000000; - and AX, 0x7FFF; // drop sign bit - cmp AX, 0x401D; // avoid InvalidException in fist - jae L_extreme; - fldl2e; - fmulp ST(1), ST; // y = x*log2(e) - fist dword ptr [ESP]; // scratchint = rndint(y) - fisub dword ptr [ESP]; // y - rndint(y) - // and now set scratchreal exponent - mov EAX, [ESP]; - add EAX, 0x3fff; - jle short L_largenegative; - cmp EAX,0x8000; - jge short L_largepositive; - mov [ESP+8+8],AX; - f2xm1; // 2ym1 = 2^^(y-rndint(y)) -1 - fld real ptr [ESP+8] ; // 2rndy = 2^^rndint(y) - fmul ST(1), ST; // ST=2rndy, ST(1)=2rndy*2ym1 - fld1; - fsubp ST(1), ST; // ST = 2rndy-1, ST(1) = 2rndy * 2ym1 - 1 - faddp ST(1), ST; // ST = 2rndy * 2ym1 + 2rndy - 1 - add ESP,12+8; - ret PARAMSIZE; - -L_extreme: // Extreme exponent. X is very large positive, very - // large negative, infinity, or NaN. - fxam; - fstsw AX; - test AX, 0x0400; // NaN_or_zero, but we already know x != 0 - jz L_was_nan; // if x is NaN, returns x - test AX, 0x0200; - jnz L_largenegative; -L_largepositive: - // Set scratchreal = real.max. - // squaring it will create infinity, and set overflow flag. - mov word ptr [ESP+8+8], 0x7FFE; - fstp ST(0); - fld real ptr [ESP+8]; // load scratchreal - fmul ST(0), ST; // square it, to create havoc! -L_was_nan: - add ESP,12+8; - ret PARAMSIZE; -L_largenegative: - fstp ST(0); - fld1; - fchs; // return -1. Underflow flag is not set. - add ESP,12+8; - ret PARAMSIZE; - } - } - else version (X86_64) - { - asm pure nothrow @nogc - { - naked; - } - version (Win64) - { - asm pure nothrow @nogc - { - fld real ptr [RCX]; // x - mov AX,[RCX+8]; // AX = exponent and sign - } - } - else - { - asm pure nothrow @nogc - { - fld real ptr [RSP+8]; // x - mov AX,[RSP+8+8]; // AX = exponent and sign - } - } - asm pure nothrow @nogc - { - /* expm1() for x87 80-bit reals, IEEE754-2008 conformant. - * Author: Don Clugston. - * - * expm1(x) = 2^(rndint(y))* 2^(y-rndint(y)) - 1 where y = LN2*x. - * = 2rndy * 2ym1 + 2rndy - 1, where 2rndy = 2^(rndint(y)) - * and 2ym1 = (2^(y-rndint(y))-1). - * If 2rndy < 0.5*real.epsilon, result is -1. - * Implementation is otherwise the same as for exp2() - */ - sub RSP, 24; // Create scratch space on the stack - // [RSP,RSP+2] = scratchint - // [RSP+4..+6, +8..+10, +10] = scratchreal - // set scratchreal mantissa = 1.0 - mov dword ptr [RSP+8], 0; - mov dword ptr [RSP+8+4], 0x80000000; - and AX, 0x7FFF; // drop sign bit - cmp AX, 0x401D; // avoid InvalidException in fist - jae L_extreme; - fldl2e; - fmul ; // y = x*log2(e) - fist dword ptr [RSP]; // scratchint = rndint(y) - fisub dword ptr [RSP]; // y - rndint(y) - // and now set scratchreal exponent - mov EAX, [RSP]; - add EAX, 0x3fff; - jle short L_largenegative; - cmp EAX,0x8000; - jge short L_largepositive; - mov [RSP+8+8],AX; - f2xm1; // 2^(y-rndint(y)) -1 - fld real ptr [RSP+8] ; // 2^rndint(y) - fmul ST(1), ST; - fld1; - fsubp ST(1), ST; - fadd; - add RSP,24; - ret; - -L_extreme: // Extreme exponent. X is very large positive, very - // large negative, infinity, or NaN. - fxam; - fstsw AX; - test AX, 0x0400; // NaN_or_zero, but we already know x != 0 - jz L_was_nan; // if x is NaN, returns x - test AX, 0x0200; - jnz L_largenegative; -L_largepositive: - // Set scratchreal = real.max. - // squaring it will create infinity, and set overflow flag. - mov word ptr [RSP+8+8], 0x7FFE; - fstp ST(0); - fld real ptr [RSP+8]; // load scratchreal - fmul ST(0), ST; // square it, to create havoc! -L_was_nan: - add RSP,24; - ret; - -L_largenegative: - fstp ST(0); - fld1; - fchs; // return -1. Underflow flag is not set. - add RSP,24; - ret; - } - } - else - static assert(0); -} - -private T expm1Impl(T)(T x) @safe pure nothrow @nogc -{ - import std.math.traits : floatTraits, RealFormat; - import std.math.rounding : floor; - import std.math.algebraic : poly; - import std.math.constants : LN2; - - // Coefficients for exp(x) - 1 and overflow/underflow limits. - enum realFormat = floatTraits!T.realFormat; - static if (realFormat == RealFormat.ieeeQuadruple) - { - static immutable T[8] P = [ - 2.943520915569954073888921213330863757240E8L, - -5.722847283900608941516165725053359168840E7L, - 8.944630806357575461578107295909719817253E6L, - -7.212432713558031519943281748462837065308E5L, - 4.578962475841642634225390068461943438441E4L, - -1.716772506388927649032068540558788106762E3L, - 4.401308817383362136048032038528753151144E1L, - -4.888737542888633647784737721812546636240E-1L - ]; - - static immutable T[9] Q = [ - 1.766112549341972444333352727998584753865E9L, - -7.848989743695296475743081255027098295771E8L, - 1.615869009634292424463780387327037251069E8L, - -2.019684072836541751428967854947019415698E7L, - 1.682912729190313538934190635536631941751E6L, - -9.615511549171441430850103489315371768998E4L, - 3.697714952261803935521187272204485251835E3L, - -8.802340681794263968892934703309274564037E1L, - 1.0 - ]; - - enum T OF = 1.1356523406294143949491931077970764891253E4L; - enum T UF = -1.143276959615573793352782661133116431383730e4L; - } - else static if (realFormat == RealFormat.ieeeExtended) - { - static immutable T[5] P = [ - -1.586135578666346600772998894928250240826E4L, - 2.642771505685952966904660652518429479531E3L, - -3.423199068835684263987132888286791620673E2L, - 1.800826371455042224581246202420972737840E1L, - -5.238523121205561042771939008061958820811E-1L, - ]; - static immutable T[6] Q = [ - -9.516813471998079611319047060563358064497E4L, - 3.964866271411091674556850458227710004570E4L, - -7.207678383830091850230366618190187434796E3L, - 7.206038318724600171970199625081491823079E2L, - -4.002027679107076077238836622982900945173E1L, - 1.0 - ]; - - enum T OF = 1.1356523406294143949492E4L; - enum T UF = -4.5054566736396445112120088E1L; - } - else static if (realFormat == RealFormat.ieeeDouble) - { - static immutable T[3] P = [ - 9.9999999999999999991025E-1, - 3.0299440770744196129956E-2, - 1.2617719307481059087798E-4, - ]; - static immutable T[4] Q = [ - 2.0000000000000000000897E0, - 2.2726554820815502876593E-1, - 2.5244834034968410419224E-3, - 3.0019850513866445504159E-6, - ]; - } - else - static assert(0, "no coefficients for expm1()"); - - static if (realFormat == RealFormat.ieeeDouble) // special case for double precision - { - if (x < -0.5 || x > 0.5) - return exp(x) - 1.0; - if (x == 0.0) - return x; - - const T xx = x * x; - x = x * poly(xx, P); - x = x / (poly(xx, Q) - x); - return x + x; - } - else - { - // C1 + C2 = LN2. - enum T C1 = 6.9314575195312500000000E-1L; - enum T C2 = 1.428606820309417232121458176568075500134E-6L; - - // Special cases. - if (x > OF) - return real.infinity; - if (x == cast(T) 0.0) - return x; - if (x < UF) - return -1.0; - - // Express x = LN2 (n + remainder), remainder not exceeding 1/2. - int n = cast(int) floor((cast(T) 0.5) + x / cast(T) LN2); - x -= n * C1; - x -= n * C2; - - // Rational approximation: - // exp(x) - 1 = x + 0.5 x^^2 + x^^3 P(x) / Q(x) - T px = x * poly(x, P); - T qx = poly(x, Q); - const T xx = x * x; - qx = x + ((cast(T) 0.5) * xx + xx * px / qx); - - // We have qx = exp(remainder LN2) - 1, so: - // exp(x) - 1 = 2^^n (qx + 1) - 1 = 2^^n qx + 2^^n - 1. - px = core.math.ldexp(cast(T) 1.0, n); - x = px * qx + (px - cast(T) 1.0); - - return x; - } -} - -@safe @nogc nothrow unittest -{ - import std.math.traits : isNaN; - import std.math.operations : isClose, CommonDefaultFor; - - static void testExpm1(T)() - { - // NaN - assert(isNaN(expm1(cast(T) T.nan))); - - static immutable T[] xs = [ -2, -0.75, -0.3, 0.0, 0.1, 0.2, 0.5, 1.0 ]; - foreach (x; xs) - { - const T e = expm1(x); - const T r = exp(x) - 1; - - //printf("expm1(%Lg) = %Lg, should approximately be %Lg\n", cast(real) x, cast(real) e, cast(real) r); - assert(isClose(r, e, CommonDefaultFor!(T,T), CommonDefaultFor!(T,T))); - } - } - - import std.meta : AliasSeq; - foreach (T; AliasSeq!(real, double)) - testExpm1!T(); -} - -/** - * Calculates 2$(SUPERSCRIPT x). - * - * $(TABLE_SV - * $(TR $(TH x) $(TH exp2(x)) ) - * $(TR $(TD +$(INFIN)) $(TD +$(INFIN)) ) - * $(TR $(TD -$(INFIN)) $(TD +0.0) ) - * $(TR $(TD $(NAN)) $(TD $(NAN)) ) - * ) - */ -version (none) // LDC FIXME: Use of this LLVM intrinsic causes a unit test failure -{ - pragma(inline, true): - real exp2(real x) @safe pure nothrow @nogc { return llvm_exp2(x); } - ///ditto - double exp2(double x) @safe pure nothrow @nogc { return llvm_exp2(x); } - ///ditto - float exp2(float x) @safe pure nothrow @nogc { return llvm_exp2(x); } -} -else -{ - -pragma(inline, true) -real exp2(real x) @nogc @trusted pure nothrow // TODO: @safe -{ - version (InlineAsm_X87) - { - if (!__ctfe) - return exp2Asm(x); - } - return exp2Impl(x); -} - -/// ditto -pragma(inline, true) -double exp2(double x) @nogc @safe pure nothrow { return __ctfe ? cast(double) exp2(cast(real) x) : exp2Impl(x); } - -/// ditto -pragma(inline, true) -float exp2(float x) @nogc @safe pure nothrow { return __ctfe ? cast(float) exp2(cast(real) x) : exp2Impl(x); } - -} // !none - -/// -@safe unittest -{ - import std.math.traits : isIdentical; - import std.math.operations : feqrel; - - assert(isIdentical(exp2(0.0), 1.0)); - assert(exp2(2.0).feqrel(4.0) > 16); - assert(exp2(8.0).feqrel(256.0) > 16); -} - -@safe unittest -{ - version (CRuntime_Microsoft) {} else // aexp2/exp2f/exp2l not implemented - { - assert( core.stdc.math.exp2f(0.0f) == 1 ); - assert( core.stdc.math.exp2 (0.0) == 1 ); - assert( core.stdc.math.exp2l(0.0L) == 1 ); - } -} - -version (InlineAsm_X87) -private real exp2Asm(real x) @nogc @trusted pure nothrow -{ - version (X86) - { - enum PARAMSIZE = (real.sizeof+3)&(0xFFFF_FFFC); // always a multiple of 4 - - asm pure nothrow @nogc - { - /* exp2() for x87 80-bit reals, IEEE754-2008 conformant. - * Author: Don Clugston. - * - * exp2(x) = 2^^(rndint(x))* 2^^(y-rndint(x)) - * The trick for high performance is to avoid the fscale(28cycles on core2), - * frndint(19 cycles), leaving f2xm1(19 cycles) as the only slow instruction. - * - * We can do frndint by using fist. BUT we can't use it for huge numbers, - * because it will set the Invalid Operation flag if overflow or NaN occurs. - * Fortunately, whenever this happens the result would be zero or infinity. - * - * We can perform fscale by directly poking into the exponent. BUT this doesn't - * work for the (very rare) cases where the result is subnormal. So we fall back - * to the slow method in that case. - */ - naked; - fld real ptr [ESP+4] ; // x - mov AX, [ESP+4+8]; // AX = exponent and sign - sub ESP, 12+8; // Create scratch space on the stack - // [ESP,ESP+2] = scratchint - // [ESP+4..+6, +8..+10, +10] = scratchreal - // set scratchreal mantissa = 1.0 - mov dword ptr [ESP+8], 0; - mov dword ptr [ESP+8+4], 0x80000000; - and AX, 0x7FFF; // drop sign bit - cmp AX, 0x401D; // avoid InvalidException in fist - jae L_extreme; - fist dword ptr [ESP]; // scratchint = rndint(x) - fisub dword ptr [ESP]; // x - rndint(x) - // and now set scratchreal exponent - mov EAX, [ESP]; - add EAX, 0x3fff; - jle short L_subnormal; - cmp EAX,0x8000; - jge short L_overflow; - mov [ESP+8+8],AX; -L_normal: - f2xm1; - fld1; - faddp ST(1), ST; // 2^^(x-rndint(x)) - fld real ptr [ESP+8] ; // 2^^rndint(x) - add ESP,12+8; - fmulp ST(1), ST; - ret PARAMSIZE; - -L_subnormal: - // Result will be subnormal. - // In this rare case, the simple poking method doesn't work. - // The speed doesn't matter, so use the slow fscale method. - fild dword ptr [ESP]; // scratchint - fld1; - fscale; - fstp real ptr [ESP+8]; // scratchreal = 2^^scratchint - fstp ST(0); // drop scratchint - jmp L_normal; - -L_extreme: // Extreme exponent. X is very large positive, very - // large negative, infinity, or NaN. - fxam; - fstsw AX; - test AX, 0x0400; // NaN_or_zero, but we already know x != 0 - jz L_was_nan; // if x is NaN, returns x - // set scratchreal = real.min_normal - // squaring it will return 0, setting underflow flag - mov word ptr [ESP+8+8], 1; - test AX, 0x0200; - jnz L_waslargenegative; -L_overflow: - // Set scratchreal = real.max. - // squaring it will create infinity, and set overflow flag. - mov word ptr [ESP+8+8], 0x7FFE; -L_waslargenegative: - fstp ST(0); - fld real ptr [ESP+8]; // load scratchreal - fmul ST(0), ST; // square it, to create havoc! -L_was_nan: - add ESP,12+8; - ret PARAMSIZE; - } - } - else version (X86_64) - { - asm pure nothrow @nogc - { - naked; - } - version (Win64) - { - asm pure nothrow @nogc - { - fld real ptr [RCX]; // x - mov AX,[RCX+8]; // AX = exponent and sign - } - } - else - { - asm pure nothrow @nogc - { - fld real ptr [RSP+8]; // x - mov AX,[RSP+8+8]; // AX = exponent and sign - } - } - asm pure nothrow @nogc - { - /* exp2() for x87 80-bit reals, IEEE754-2008 conformant. - * Author: Don Clugston. - * - * exp2(x) = 2^(rndint(x))* 2^(y-rndint(x)) - * The trick for high performance is to avoid the fscale(28cycles on core2), - * frndint(19 cycles), leaving f2xm1(19 cycles) as the only slow instruction. - * - * We can do frndint by using fist. BUT we can't use it for huge numbers, - * because it will set the Invalid Operation flag is overflow or NaN occurs. - * Fortunately, whenever this happens the result would be zero or infinity. - * - * We can perform fscale by directly poking into the exponent. BUT this doesn't - * work for the (very rare) cases where the result is subnormal. So we fall back - * to the slow method in that case. - */ - sub RSP, 24; // Create scratch space on the stack - // [RSP,RSP+2] = scratchint - // [RSP+4..+6, +8..+10, +10] = scratchreal - // set scratchreal mantissa = 1.0 - mov dword ptr [RSP+8], 0; - mov dword ptr [RSP+8+4], 0x80000000; - and AX, 0x7FFF; // drop sign bit - cmp AX, 0x401D; // avoid InvalidException in fist - jae L_extreme; - fist dword ptr [RSP]; // scratchint = rndint(x) - fisub dword ptr [RSP]; // x - rndint(x) - // and now set scratchreal exponent - mov EAX, [RSP]; - add EAX, 0x3fff; - jle short L_subnormal; - cmp EAX,0x8000; - jge short L_overflow; - mov [RSP+8+8],AX; -L_normal: - f2xm1; - fld1; - fadd; // 2^(x-rndint(x)) - fld real ptr [RSP+8] ; // 2^rndint(x) - add RSP,24; - fmulp ST(1), ST; - ret; - -L_subnormal: - // Result will be subnormal. - // In this rare case, the simple poking method doesn't work. - // The speed doesn't matter, so use the slow fscale method. - fild dword ptr [RSP]; // scratchint - fld1; - fscale; - fstp real ptr [RSP+8]; // scratchreal = 2^scratchint - fstp ST(0); // drop scratchint - jmp L_normal; - -L_extreme: // Extreme exponent. X is very large positive, very - // large negative, infinity, or NaN. - fxam; - fstsw AX; - test AX, 0x0400; // NaN_or_zero, but we already know x != 0 - jz L_was_nan; // if x is NaN, returns x - // set scratchreal = real.min - // squaring it will return 0, setting underflow flag - mov word ptr [RSP+8+8], 1; - test AX, 0x0200; - jnz L_waslargenegative; -L_overflow: - // Set scratchreal = real.max. - // squaring it will create infinity, and set overflow flag. - mov word ptr [RSP+8+8], 0x7FFE; -L_waslargenegative: - fstp ST(0); - fld real ptr [RSP+8]; // load scratchreal - fmul ST(0), ST; // square it, to create havoc! -L_was_nan: - add RSP,24; - ret; - } - } - else - static assert(0); -} - -private T exp2Impl(T)(T x) @nogc @safe pure nothrow -{ - import std.math.traits : floatTraits, RealFormat, isNaN; - import std.math.rounding : floor; - import std.math.algebraic : poly; - - // Coefficients for exp2(x) - enum realFormat = floatTraits!T.realFormat; - static if (realFormat == RealFormat.ieeeQuadruple) - { - static immutable T[5] P = [ - 9.079594442980146270952372234833529694788E12L, - 1.530625323728429161131811299626419117557E11L, - 5.677513871931844661829755443994214173883E8L, - 6.185032670011643762127954396427045467506E5L, - 1.587171580015525194694938306936721666031E2L - ]; - - static immutable T[6] Q = [ - 2.619817175234089411411070339065679229869E13L, - 1.490560994263653042761789432690793026977E12L, - 1.092141473886177435056423606755843616331E10L, - 2.186249607051644894762167991800811827835E7L, - 1.236602014442099053716561665053645270207E4L, - 1.0 - ]; - } - else static if (realFormat == RealFormat.ieeeExtended) - { - static immutable T[3] P = [ - 2.0803843631901852422887E6L, - 3.0286971917562792508623E4L, - 6.0614853552242266094567E1L, - ]; - static immutable T[4] Q = [ - 6.0027204078348487957118E6L, - 3.2772515434906797273099E5L, - 1.7492876999891839021063E3L, - 1.0000000000000000000000E0L, - ]; - } - else static if (realFormat == RealFormat.ieeeDouble) - { - static immutable T[3] P = [ - 1.51390680115615096133E3L, - 2.02020656693165307700E1L, - 2.30933477057345225087E-2L, - ]; - static immutable T[3] Q = [ - 4.36821166879210612817E3L, - 2.33184211722314911771E2L, - 1.00000000000000000000E0L, - ]; - } - else static if (realFormat == RealFormat.ieeeSingle) - { - static immutable T[6] P = [ - 6.931472028550421E-001L, - 2.402264791363012E-001L, - 5.550332471162809E-002L, - 9.618437357674640E-003L, - 1.339887440266574E-003L, - 1.535336188319500E-004L, - ]; - } - else - static assert(0, "no coefficients for exp2()"); - - // Overflow and Underflow limits. - enum T OF = T.max_exp; - enum T UF = T.min_exp - 1; - - // Special cases. - if (isNaN(x)) - return x; - if (x > OF) - return real.infinity; - if (x < UF) - return 0.0; - - static if (realFormat == RealFormat.ieeeSingle) // special case for single precision - { - // The following is necessary because range reduction blows up. - if (x == 0.0f) - return 1.0f; - - // Separate into integer and fractional parts. - const T i = floor(x); - int n = cast(int) i; - x -= i; - if (x > 0.5f) - { - n += 1; - x -= 1.0f; - } - - // Rational approximation: - // exp2(x) = 1.0 + x P(x) - x = 1.0f + x * poly(x, P); - } - else - { - // Separate into integer and fractional parts. - const T i = floor(x + cast(T) 0.5); - int n = cast(int) i; - x -= i; - - // Rational approximation: - // exp2(x) = 1.0 + 2x P(x^^2) / (Q(x^^2) - P(x^^2)) - const T xx = x * x; - const T px = x * poly(xx, P); - x = px / (poly(xx, Q) - px); - x = (cast(T) 1.0) + (cast(T) 2.0) * x; - } - - // Scale by power of 2. - x = core.math.ldexp(x, n); - - return x; -} - -@safe @nogc nothrow unittest -{ - import std.math.operations : feqrel, NaN, isClose; - import std.math.traits : isIdentical; - import std.math.constants : SQRT2; - - assert(feqrel(exp2(0.5L), SQRT2) >= real.mant_dig -1); - assert(exp2(8.0L) == 256.0); - assert(exp2(-9.0L)== 1.0L/512.0); - - static void testExp2(T)() - { - // NaN - const T specialNaN = NaN(0x0123L); - assert(isIdentical(exp2(specialNaN), specialNaN)); - - // over-/underflow - enum T OF = T.max_exp; - enum T UF = T.min_exp - T.mant_dig; - assert(isIdentical(exp2(OF + 1), cast(T) T.infinity)); - assert(isIdentical(exp2(UF - 1), cast(T) 0.0)); - - static immutable T[2][] vals = - [ - // x, exp2(x) - [ 0.0, 1.0 ], - [ -0.0, 1.0 ], - [ 0.5, SQRT2 ], - [ 8.0, 256.0 ], - [ -9.0, 1.0 / 512 ], - ]; - - foreach (ref val; vals) - { - const T x = val[0]; - const T r = val[1]; - const T e = exp2(x); - - //printf("exp2(%Lg) = %Lg, should be %Lg\n", cast(real) x, cast(real) e, cast(real) r); - assert(isClose(r, e)); - } - } - - import std.meta : AliasSeq; - foreach (T; AliasSeq!(real, double, float)) - testExp2!T(); -} - -/********************************************************************* - * Separate floating point value into significand and exponent. - * - * Returns: - * Calculate and return $(I x) and $(I exp) such that - * value =$(I x)*2$(SUPERSCRIPT exp) and - * .5 $(LT)= |$(I x)| $(LT) 1.0 - * - * $(I x) has same sign as value. - * - * $(TABLE_SV - * $(TR $(TH value) $(TH returns) $(TH exp)) - * $(TR $(TD $(PLUSMN)0.0) $(TD $(PLUSMN)0.0) $(TD 0)) - * $(TR $(TD +$(INFIN)) $(TD +$(INFIN)) $(TD int.max)) - * $(TR $(TD -$(INFIN)) $(TD -$(INFIN)) $(TD int.min)) - * $(TR $(TD $(PLUSMN)$(NAN)) $(TD $(PLUSMN)$(NAN)) $(TD int.min)) - * ) - */ -T frexp(T)(const T value, out int exp) @trusted pure nothrow @nogc -if (isFloatingPoint!T) -{ - import std.math.traits : floatTraits, RealFormat, MANTISSA_MSB, MANTISSA_LSB, isSubnormal; - - if (__ctfe) - { - // Handle special cases. - if (value == 0) { exp = 0; return value; } - if (value == T.infinity) { exp = int.max; return value; } - if (value == -T.infinity || value != value) { exp = int.min; return value; } - // Handle ordinary cases. - // In CTFE there is no performance advantage for having separate - // paths for different floating point types. - T absValue = value < 0 ? -value : value; - int expCount; - static if (T.mant_dig > double.mant_dig) - { - for (; absValue >= 0x1.0p+1024L; absValue *= 0x1.0p-1024L) - expCount += 1024; - for (; absValue < 0x1.0p-1021L; absValue *= 0x1.0p+1021L) - expCount -= 1021; - } - const double dval = cast(double) absValue; - int dexp = cast(int) (((*cast(const long*) &dval) >>> 52) & 0x7FF) + double.min_exp - 2; - dexp++; - expCount += dexp; - absValue *= 2.0 ^^ -dexp; - // If the original value was subnormal or if it was a real - // then absValue can still be outside the [0.5, 1.0) range. - if (absValue < 0.5) - { - assert(T.mant_dig > double.mant_dig || isSubnormal(value)); - do - { - absValue += absValue; - expCount--; - } while (absValue < 0.5); - } - else - { - assert(absValue < 1 || T.mant_dig > double.mant_dig); - for (; absValue >= 1; absValue *= T(0.5)) - expCount++; - } - exp = expCount; - return value < 0 ? -absValue : absValue; - } - - Unqual!T vf = value; - ushort* vu = cast(ushort*)&vf; - static if (is(immutable T == immutable float)) - int* vi = cast(int*)&vf; - else - long* vl = cast(long*)&vf; - int ex; - alias F = floatTraits!T; - - ex = vu[F.EXPPOS_SHORT] & F.EXPMASK; - static if (F.realFormat == RealFormat.ieeeExtended || - F.realFormat == RealFormat.ieeeExtended53) - { - if (ex) - { // If exponent is non-zero - if (ex == F.EXPMASK) // infinity or NaN - { - if (*vl & 0x7FFF_FFFF_FFFF_FFFF) // NaN - { - *vl |= 0xC000_0000_0000_0000; // convert NaNS to NaNQ - exp = int.min; - } - else if (vu[F.EXPPOS_SHORT] & 0x8000) // negative infinity - exp = int.min; - else // positive infinity - exp = int.max; - - } - else - { - exp = ex - F.EXPBIAS; - vu[F.EXPPOS_SHORT] = (0x8000 & vu[F.EXPPOS_SHORT]) | 0x3FFE; - } - } - else if (!*vl) - { - // vf is +-0.0 - exp = 0; - } - else - { - // subnormal - - vf *= F.RECIP_EPSILON; - ex = vu[F.EXPPOS_SHORT] & F.EXPMASK; - exp = ex - F.EXPBIAS - T.mant_dig + 1; - vu[F.EXPPOS_SHORT] = ((-1 - F.EXPMASK) & vu[F.EXPPOS_SHORT]) | 0x3FFE; - } - return vf; - } - else static if (F.realFormat == RealFormat.ieeeQuadruple) - { - if (ex) // If exponent is non-zero - { - if (ex == F.EXPMASK) - { - // infinity or NaN - if (vl[MANTISSA_LSB] | - (vl[MANTISSA_MSB] & 0x0000_FFFF_FFFF_FFFF)) // NaN - { - // convert NaNS to NaNQ - vl[MANTISSA_MSB] |= 0x0000_8000_0000_0000; - exp = int.min; - } - else if (vu[F.EXPPOS_SHORT] & 0x8000) // negative infinity - exp = int.min; - else // positive infinity - exp = int.max; - } - else - { - exp = ex - F.EXPBIAS; - vu[F.EXPPOS_SHORT] = F.EXPBIAS | (0x8000 & vu[F.EXPPOS_SHORT]); - } - } - else if ((vl[MANTISSA_LSB] | - (vl[MANTISSA_MSB] & 0x0000_FFFF_FFFF_FFFF)) == 0) - { - // vf is +-0.0 - exp = 0; - } - else - { - // subnormal - vf *= F.RECIP_EPSILON; - ex = vu[F.EXPPOS_SHORT] & F.EXPMASK; - exp = ex - F.EXPBIAS - T.mant_dig + 1; - vu[F.EXPPOS_SHORT] = F.EXPBIAS | (0x8000 & vu[F.EXPPOS_SHORT]); - } - return vf; - } - else static if (F.realFormat == RealFormat.ieeeDouble) - { - if (ex) // If exponent is non-zero - { - if (ex == F.EXPMASK) // infinity or NaN - { - if (*vl == 0x7FF0_0000_0000_0000) // positive infinity - { - exp = int.max; - } - else if (*vl == 0xFFF0_0000_0000_0000) // negative infinity - exp = int.min; - else - { // NaN - *vl |= 0x0008_0000_0000_0000; // convert NaNS to NaNQ - exp = int.min; - } - } - else - { - exp = (ex - F.EXPBIAS) >> 4; - vu[F.EXPPOS_SHORT] = cast(ushort)((0x800F & vu[F.EXPPOS_SHORT]) | 0x3FE0); - } - } - else if (!(*vl & 0x7FFF_FFFF_FFFF_FFFF)) - { - // vf is +-0.0 - exp = 0; - } - else - { - // subnormal - vf *= F.RECIP_EPSILON; - ex = vu[F.EXPPOS_SHORT] & F.EXPMASK; - exp = ((ex - F.EXPBIAS) >> 4) - T.mant_dig + 1; - vu[F.EXPPOS_SHORT] = - cast(ushort)(((-1 - F.EXPMASK) & vu[F.EXPPOS_SHORT]) | 0x3FE0); - } - return vf; - } - else static if (F.realFormat == RealFormat.ieeeSingle) - { - if (ex) // If exponent is non-zero - { - if (ex == F.EXPMASK) // infinity or NaN - { - if (*vi == 0x7F80_0000) // positive infinity - { - exp = int.max; - } - else if (*vi == 0xFF80_0000) // negative infinity - exp = int.min; - else - { // NaN - *vi |= 0x0040_0000; // convert NaNS to NaNQ - exp = int.min; - } - } - else - { - exp = (ex - F.EXPBIAS) >> 7; - vu[F.EXPPOS_SHORT] = cast(ushort)((0x807F & vu[F.EXPPOS_SHORT]) | 0x3F00); - } - } - else if (!(*vi & 0x7FFF_FFFF)) - { - // vf is +-0.0 - exp = 0; - } - else - { - // subnormal - vf *= F.RECIP_EPSILON; - ex = vu[F.EXPPOS_SHORT] & F.EXPMASK; - exp = ((ex - F.EXPBIAS) >> 7) - T.mant_dig + 1; - vu[F.EXPPOS_SHORT] = - cast(ushort)(((-1 - F.EXPMASK) & vu[F.EXPPOS_SHORT]) | 0x3F00); - } - return vf; - } - else // static if (F.realFormat == RealFormat.ibmExtended) - { - assert(0, "frexp not implemented"); - } -} - -/// -@safe unittest -{ - import std.math.operations : isClose; - - int exp; - real mantissa = frexp(123.456L, exp); - - assert(isClose(mantissa * pow(2.0L, cast(real) exp), 123.456L)); - - assert(frexp(-real.nan, exp) && exp == int.min); - assert(frexp(real.nan, exp) && exp == int.min); - assert(frexp(-real.infinity, exp) == -real.infinity && exp == int.min); - assert(frexp(real.infinity, exp) == real.infinity && exp == int.max); - assert(frexp(-0.0, exp) == -0.0 && exp == 0); - assert(frexp(0.0, exp) == 0.0 && exp == 0); -} - -@safe @nogc nothrow unittest -{ - import std.math.operations : isClose; - - int exp; - real mantissa = frexp(123.456L, exp); - - // check if values are equal to 19 decimal digits of precision - assert(isClose(mantissa * pow(2.0L, cast(real) exp), 123.456L, 1e-18)); -} - -@safe unittest -{ - import std.math.traits : floatTraits, RealFormat, isIdentical; - import std.meta : AliasSeq; - import std.typecons : tuple, Tuple; - - static foreach (T; AliasSeq!(real, double, float)) - {{ - Tuple!(T, T, int)[] vals = [ // x,frexp,exp - tuple(T(0.0), T( 0.0 ), 0), - tuple(T(-0.0), T( -0.0), 0), - tuple(T(1.0), T( .5 ), 1), - tuple(T(-1.0), T( -.5 ), 1), - tuple(T(2.0), T( .5 ), 2), - tuple(T(float.min_normal/2.0f), T(.5), -126), - tuple(T.infinity, T.infinity, int.max), - tuple(-T.infinity, -T.infinity, int.min), - tuple(T.nan, T.nan, int.min), - tuple(-T.nan, -T.nan, int.min), - - // https://issues.dlang.org/show_bug.cgi?id=16026: - tuple(3 * (T.min_normal * T.epsilon), T( .75), (T.min_exp - T.mant_dig) + 2) - ]; - - foreach (elem; vals) - { - T x = elem[0]; - T e = elem[1]; - int exp = elem[2]; - int eptr; - T v = frexp(x, eptr); - assert(isIdentical(e, v)); - assert(exp == eptr); - } - - static if (floatTraits!(T).realFormat == RealFormat.ieeeExtended) - { - static T[3][] extendedvals = [ // x,frexp,exp - [0x1.a5f1c2eb3fe4efp+73L, 0x1.A5F1C2EB3FE4EFp-1L, 74], // normal - [0x1.fa01712e8f0471ap-1064L, 0x1.fa01712e8f0471ap-1L, -1063], - [T.min_normal, .5, -16381], - [T.min_normal/2.0L, .5, -16382] // subnormal - ]; - foreach (elem; extendedvals) - { - T x = elem[0]; - T e = elem[1]; - int exp = cast(int) elem[2]; - int eptr; - T v = frexp(x, eptr); - assert(isIdentical(e, v)); - assert(exp == eptr); - } - } - }} - - // CTFE - alias CtfeFrexpResult= Tuple!(real, int); - static CtfeFrexpResult ctfeFrexp(T)(const T value) - { - int exp; - auto significand = frexp(value, exp); - return CtfeFrexpResult(significand, exp); - } - static foreach (T; AliasSeq!(real, double, float)) - {{ - enum Tuple!(T, T, int)[] vals = [ // x,frexp,exp - tuple(T(0.0), T( 0.0 ), 0), - tuple(T(-0.0), T( -0.0), 0), - tuple(T(1.0), T( .5 ), 1), - tuple(T(-1.0), T( -.5 ), 1), - tuple(T(2.0), T( .5 ), 2), - tuple(T(float.min_normal/2.0f), T(.5), -126), - tuple(T.infinity, T.infinity, int.max), - tuple(-T.infinity, -T.infinity, int.min), - tuple(T.nan, T.nan, int.min), - tuple(-T.nan, -T.nan, int.min), - - // https://issues.dlang.org/show_bug.cgi?id=16026: - tuple(3 * (T.min_normal * T.epsilon), T( .75), (T.min_exp - T.mant_dig) + 2) - ]; - - static foreach (elem; vals) - { - static assert(ctfeFrexp(elem[0]) is CtfeFrexpResult(elem[1], elem[2])); - } - - static if (floatTraits!(T).realFormat == RealFormat.ieeeExtended) - { - enum T[3][] extendedvals = [ // x,frexp,exp - [0x1.a5f1c2eb3fe4efp+73L, 0x1.A5F1C2EB3FE4EFp-1L, 74], // normal - [0x1.fa01712e8f0471ap-1064L, 0x1.fa01712e8f0471ap-1L, -1063], - [T.min_normal, .5, -16381], - [T.min_normal/2.0L, .5, -16382] // subnormal - ]; - static foreach (elem; extendedvals) - { - static assert(ctfeFrexp(elem[0]) is CtfeFrexpResult(elem[1], cast(int) elem[2])); - } - } - }} -} - -@safe unittest -{ - import std.meta : AliasSeq; - void foo() { - static foreach (T; AliasSeq!(real, double, float)) - {{ - int exp; - const T a = 1; - immutable T b = 2; - auto c = frexp(a, exp); - auto d = frexp(b, exp); - }} - } -} - -/****************************************** - * Extracts the exponent of x as a signed integral value. - * - * If x is not a special value, the result is the same as - * $(D cast(int) logb(x)). - * - * $(TABLE_SV - * $(TR $(TH x) $(TH ilogb(x)) $(TH Range error?)) - * $(TR $(TD 0) $(TD FP_ILOGB0) $(TD yes)) - * $(TR $(TD $(PLUSMN)$(INFIN)) $(TD int.max) $(TD no)) - * $(TR $(TD $(NAN)) $(TD FP_ILOGBNAN) $(TD no)) - * ) - */ -int ilogb(T)(const T x) @trusted pure nothrow @nogc -if (isFloatingPoint!T) -{ - import std.math.traits : floatTraits, RealFormat, MANTISSA_MSB, MANTISSA_LSB; - - import core.bitop : bsr; - alias F = floatTraits!T; - - union floatBits - { - T rv; - ushort[T.sizeof/2] vu; - uint[T.sizeof/4] vui; - static if (T.sizeof >= 8) - ulong[T.sizeof/8] vul; - } - floatBits y = void; - y.rv = x; - - int ex = y.vu[F.EXPPOS_SHORT] & F.EXPMASK; - static if (F.realFormat == RealFormat.ieeeExtended || - F.realFormat == RealFormat.ieeeExtended53) - { - if (ex) - { - // If exponent is non-zero - if (ex == F.EXPMASK) // infinity or NaN - { - if (y.vul[0] & 0x7FFF_FFFF_FFFF_FFFF) // NaN - return FP_ILOGBNAN; - else // +-infinity - return int.max; - } - else - { - return ex - F.EXPBIAS - 1; - } - } - else if (!y.vul[0]) - { - // vf is +-0.0 - return FP_ILOGB0; - } - else - { - // subnormal - return ex - F.EXPBIAS - T.mant_dig + 1 + bsr(y.vul[0]); - } - } - else static if (F.realFormat == RealFormat.ieeeQuadruple) - { - if (ex) // If exponent is non-zero - { - if (ex == F.EXPMASK) - { - // infinity or NaN - if (y.vul[MANTISSA_LSB] | ( y.vul[MANTISSA_MSB] & 0x0000_FFFF_FFFF_FFFF)) // NaN - return FP_ILOGBNAN; - else // +- infinity - return int.max; - } - else - { - return ex - F.EXPBIAS - 1; - } - } - else if ((y.vul[MANTISSA_LSB] | (y.vul[MANTISSA_MSB] & 0x0000_FFFF_FFFF_FFFF)) == 0) - { - // vf is +-0.0 - return FP_ILOGB0; - } - else - { - // subnormal - const ulong msb = y.vul[MANTISSA_MSB] & 0x0000_FFFF_FFFF_FFFF; - const ulong lsb = y.vul[MANTISSA_LSB]; - if (msb) - return ex - F.EXPBIAS - T.mant_dig + 1 + bsr(msb) + 64; - else - return ex - F.EXPBIAS - T.mant_dig + 1 + bsr(lsb); - } - } - else static if (F.realFormat == RealFormat.ieeeDouble) - { - if (ex) // If exponent is non-zero - { - if (ex == F.EXPMASK) // infinity or NaN - { - if ((y.vul[0] & 0x7FFF_FFFF_FFFF_FFFF) == 0x7FF0_0000_0000_0000) // +- infinity - return int.max; - else // NaN - return FP_ILOGBNAN; - } - else - { - return ((ex - F.EXPBIAS) >> 4) - 1; - } - } - else if (!(y.vul[0] & 0x7FFF_FFFF_FFFF_FFFF)) - { - // vf is +-0.0 - return FP_ILOGB0; - } - else - { - // subnormal - enum MANTISSAMASK_64 = ((cast(ulong) F.MANTISSAMASK_INT) << 32) | 0xFFFF_FFFF; - return ((ex - F.EXPBIAS) >> 4) - T.mant_dig + 1 + bsr(y.vul[0] & MANTISSAMASK_64); - } - } - else static if (F.realFormat == RealFormat.ieeeSingle) - { - if (ex) // If exponent is non-zero - { - if (ex == F.EXPMASK) // infinity or NaN - { - if ((y.vui[0] & 0x7FFF_FFFF) == 0x7F80_0000) // +- infinity - return int.max; - else // NaN - return FP_ILOGBNAN; - } - else - { - return ((ex - F.EXPBIAS) >> 7) - 1; - } - } - else if (!(y.vui[0] & 0x7FFF_FFFF)) - { - // vf is +-0.0 - return FP_ILOGB0; - } - else - { - // subnormal - const uint mantissa = y.vui[0] & F.MANTISSAMASK_INT; - return ((ex - F.EXPBIAS) >> 7) - T.mant_dig + 1 + bsr(mantissa); - } - } - else // static if (F.realFormat == RealFormat.ibmExtended) - { - assert(0, "ilogb not implemented"); - } -} -/// ditto -int ilogb(T)(const T x) @safe pure nothrow @nogc -if (isIntegral!T && isUnsigned!T) -{ - import core.bitop : bsr; - if (x == 0) - return FP_ILOGB0; - else - { - static assert(T.sizeof <= ulong.sizeof, "integer size too large for the current ilogb implementation"); - return bsr(x); - } -} -/// ditto -int ilogb(T)(const T x) @safe pure nothrow @nogc -if (isIntegral!T && isSigned!T) -{ - import std.traits : Unsigned; - // Note: abs(x) can not be used because the return type is not Unsigned and - // the return value would be wrong for x == int.min - Unsigned!T absx = x >= 0 ? x : -x; - return ilogb(absx); -} - -/// -@safe pure unittest -{ - assert(ilogb(1) == 0); - assert(ilogb(3) == 1); - assert(ilogb(3.0) == 1); - assert(ilogb(100_000_000) == 26); - - assert(ilogb(0) == FP_ILOGB0); - assert(ilogb(0.0) == FP_ILOGB0); - assert(ilogb(double.nan) == FP_ILOGBNAN); - assert(ilogb(double.infinity) == int.max); -} - -/** -Special return values of $(LREF ilogb). - */ -alias FP_ILOGB0 = core.stdc.math.FP_ILOGB0; -/// ditto -alias FP_ILOGBNAN = core.stdc.math.FP_ILOGBNAN; - -/// -@safe pure unittest -{ - assert(ilogb(0) == FP_ILOGB0); - assert(ilogb(0.0) == FP_ILOGB0); - assert(ilogb(double.nan) == FP_ILOGBNAN); -} - -@safe nothrow @nogc unittest -{ - import std.math.traits : floatTraits, RealFormat; - import std.math.operations : nextUp; - import std.meta : AliasSeq; - import std.typecons : Tuple; - static foreach (F; AliasSeq!(float, double, real)) - {{ - alias T = Tuple!(F, int); - T[13] vals = // x, ilogb(x) - [ - T( F.nan , FP_ILOGBNAN ), - T( -F.nan , FP_ILOGBNAN ), - T( F.infinity, int.max ), - T( -F.infinity, int.max ), - T( 0.0 , FP_ILOGB0 ), - T( -0.0 , FP_ILOGB0 ), - T( 2.0 , 1 ), - T( 2.0001 , 1 ), - T( 1.9999 , 0 ), - T( 0.5 , -1 ), - T( 123.123 , 6 ), - T( -123.123 , 6 ), - T( 0.123 , -4 ), - ]; - - foreach (elem; vals) - { - assert(ilogb(elem[0]) == elem[1]); - } - }} - - // min_normal and subnormals - assert(ilogb(-float.min_normal) == -126); - assert(ilogb(nextUp(-float.min_normal)) == -127); - assert(ilogb(nextUp(-float(0.0))) == -149); - assert(ilogb(-double.min_normal) == -1022); - assert(ilogb(nextUp(-double.min_normal)) == -1023); - assert(ilogb(nextUp(-double(0.0))) == -1074); - static if (floatTraits!(real).realFormat == RealFormat.ieeeExtended) - { - assert(ilogb(-real.min_normal) == -16382); - assert(ilogb(nextUp(-real.min_normal)) == -16383); - assert(ilogb(nextUp(-real(0.0))) == -16445); - } - else static if (floatTraits!(real).realFormat == RealFormat.ieeeDouble) - { - assert(ilogb(-real.min_normal) == -1022); - assert(ilogb(nextUp(-real.min_normal)) == -1023); - assert(ilogb(nextUp(-real(0.0))) == -1074); - } - - // test integer types - assert(ilogb(0) == FP_ILOGB0); - assert(ilogb(int.max) == 30); - assert(ilogb(int.min) == 31); - assert(ilogb(uint.max) == 31); - assert(ilogb(long.max) == 62); - assert(ilogb(long.min) == 63); - assert(ilogb(ulong.max) == 63); -} - -/******************************************* - * Compute n * 2$(SUPERSCRIPT exp) - * References: frexp - */ - -pragma(inline, true) -real ldexp(real n, int exp) @safe pure nothrow @nogc { return core.math.ldexp(n, exp); } -///ditto -pragma(inline, true) -double ldexp(double n, int exp) @safe pure nothrow @nogc { return core.math.ldexp(n, exp); } -///ditto -pragma(inline, true) -float ldexp(float n, int exp) @safe pure nothrow @nogc { return core.math.ldexp(n, exp); } - -/// -@nogc @safe pure nothrow unittest -{ - import std.meta : AliasSeq; - static foreach (T; AliasSeq!(float, double, real)) - {{ - T r; - - r = ldexp(3.0L, 3); - assert(r == 24); - - r = ldexp(cast(T) 3.0, cast(int) 3); - assert(r == 24); - - T n = 3.0; - int exp = 3; - r = ldexp(n, exp); - assert(r == 24); - }} -} - -@safe pure nothrow @nogc unittest -{ - import std.math.traits : floatTraits, RealFormat; - - static if (floatTraits!(real).realFormat == RealFormat.ieeeExtended || - floatTraits!(real).realFormat == RealFormat.ieeeExtended53 || - floatTraits!(real).realFormat == RealFormat.ieeeQuadruple) - { - assert(ldexp(1.0L, -16384) == 0x1p-16384L); - assert(ldexp(1.0L, -16382) == 0x1p-16382L); - int x; - real n = frexp(0x1p-16384L, x); - assert(n == 0.5L); - assert(x==-16383); - assert(ldexp(n, x)==0x1p-16384L); - } - else static if (floatTraits!(real).realFormat == RealFormat.ieeeDouble) - { - assert(ldexp(1.0L, -1024) == 0x1p-1024L); - assert(ldexp(1.0L, -1022) == 0x1p-1022L); - int x; - real n = frexp(0x1p-1024L, x); - assert(n == 0.5L); - assert(x==-1023); - assert(ldexp(n, x)==0x1p-1024L); - } - else static assert(false, "Floating point type real not supported"); -} - -/* workaround https://issues.dlang.org/show_bug.cgi?id=14718 - float parsing depends on platform strtold -@safe pure nothrow @nogc unittest -{ - assert(ldexp(1.0, -1024) == 0x1p-1024); - assert(ldexp(1.0, -1022) == 0x1p-1022); - int x; - double n = frexp(0x1p-1024, x); - assert(n == 0.5); - assert(x==-1023); - assert(ldexp(n, x)==0x1p-1024); -} - -@safe pure nothrow @nogc unittest -{ - assert(ldexp(1.0f, -128) == 0x1p-128f); - assert(ldexp(1.0f, -126) == 0x1p-126f); - int x; - float n = frexp(0x1p-128f, x); - assert(n == 0.5f); - assert(x==-127); - assert(ldexp(n, x)==0x1p-128f); -} -*/ - -@safe @nogc nothrow unittest -{ - import std.math.operations : isClose; - - static real[3][] vals = // value,exp,ldexp - [ - [ 0, 0, 0], - [ 1, 0, 1], - [ -1, 0, -1], - [ 1, 1, 2], - [ 123, 10, 125952], - [ real.max, int.max, real.infinity], - [ real.max, -int.max, 0], - [ real.min_normal, -int.max, 0], - ]; - int i; - - for (i = 0; i < vals.length; i++) - { - real x = vals[i][0]; - int exp = cast(int) vals[i][1]; - real z = vals[i][2]; - real l = ldexp(x, exp); - - assert(isClose(z, l, 1e-6)); - } - - real function(real, int) pldexp = &ldexp; - assert(pldexp != null); -} - -private -{ - // Coefficients shared across log(), log2(), log10(), log1p(). - template LogCoeffs(T) - { - import std.math.traits : floatTraits, RealFormat; - - static if (floatTraits!T.realFormat == RealFormat.ieeeQuadruple) - { - // Coefficients for log(1 + x) = x - x^^2/2 + x^^3 P(x)/Q(x) - // Theoretical peak relative error = 5.3e-37 - static immutable real[13] logP = [ - 1.313572404063446165910279910527789794488E4L, - 7.771154681358524243729929227226708890930E4L, - 2.014652742082537582487669938141683759923E5L, - 3.007007295140399532324943111654767187848E5L, - 2.854829159639697837788887080758954924001E5L, - 1.797628303815655343403735250238293741397E5L, - 7.594356839258970405033155585486712125861E4L, - 2.128857716871515081352991964243375186031E4L, - 3.824952356185897735160588078446136783779E3L, - 4.114517881637811823002128927449878962058E2L, - 2.321125933898420063925789532045674660756E1L, - 4.998469661968096229986658302195402690910E-1L, - 1.538612243596254322971797716843006400388E-6L - ]; - static immutable real[13] logQ = [ - 3.940717212190338497730839731583397586124E4L, - 2.626900195321832660448791748036714883242E5L, - 7.777690340007566932935753241556479363645E5L, - 1.347518538384329112529391120390701166528E6L, - 1.514882452993549494932585972882995548426E6L, - 1.158019977462989115839826904108208787040E6L, - 6.132189329546557743179177159925690841200E5L, - 2.248234257620569139969141618556349415120E5L, - 5.605842085972455027590989944010492125825E4L, - 9.147150349299596453976674231612674085381E3L, - 9.104928120962988414618126155557301584078E2L, - 4.839208193348159620282142911143429644326E1L, - 1.0 - ]; - - // log2 uses the same coefficients as log. - alias log2P = logP; - alias log2Q = logQ; - - // log10 uses the same coefficients as log. - alias log10P = logP; - alias log10Q = logQ; - - // Coefficients for log(x) = z + z^^3 R(z^^2)/S(z^^2) - // where z = 2(x-1)/(x+1) - // Theoretical peak relative error = 1.1e-35 - static immutable real[6] logR = [ - 1.418134209872192732479751274970992665513E5L, - -8.977257995689735303686582344659576526998E4L, - 2.048819892795278657810231591630928516206E4L, - -2.024301798136027039250415126250455056397E3L, - 8.057002716646055371965756206836056074715E1L, - -8.828896441624934385266096344596648080902E-1L - ]; - static immutable real[7] logS = [ - 1.701761051846631278975701529965589676574E6L, - -1.332535117259762928288745111081235577029E6L, - 4.001557694070773974936904547424676279307E5L, - -5.748542087379434595104154610899551484314E4L, - 3.998526750980007367835804959888064681098E3L, - -1.186359407982897997337150403816839480438E2L, - 1.0 - ]; - } - else static if (floatTraits!T.realFormat == RealFormat.ieeeExtended || - floatTraits!T.realFormat == RealFormat.ieeeExtended53) - { - // Coefficients for log(1 + x) = x - x^^2/2 + x^^3 P(x)/Q(x) - // Theoretical peak relative error = 2.32e-20 - static immutable real[7] logP = [ - 2.0039553499201281259648E1L, - 5.7112963590585538103336E1L, - 6.0949667980987787057556E1L, - 2.9911919328553073277375E1L, - 6.5787325942061044846969E0L, - 4.9854102823193375972212E-1L, - 4.5270000862445199635215E-5L, - ]; - static immutable real[7] logQ = [ - 6.0118660497603843919306E1L, - 2.1642788614495947685003E2L, - 3.0909872225312059774938E2L, - 2.2176239823732856465394E2L, - 8.3047565967967209469434E1L, - 1.5062909083469192043167E1L, - 1.0000000000000000000000E0L, - ]; - - // Coefficients for log(1 + x) = x - x^^2/2 + x^^3 P(x)/Q(x) - // Theoretical peak relative error = 6.2e-22 - static immutable real[7] log2P = [ - 1.0747524399916215149070E2L, - 3.4258224542413922935104E2L, - 4.2401812743503691187826E2L, - 2.5620629828144409632571E2L, - 7.7671073698359539859595E1L, - 1.0767376367209449010438E1L, - 4.9962495940332550844739E-1L, - ]; - static immutable real[8] log2Q = [ - 3.2242573199748645407652E2L, - 1.2695660352705325274404E3L, - 2.0307734695595183428202E3L, - 1.6911722418503949084863E3L, - 7.7952888181207260646090E2L, - 1.9444210022760132894510E2L, - 2.3479774160285863271658E1L, - 1.0000000000000000000000E0, - ]; - - // log10 uses the same coefficients as log2. - alias log10P = log2P; - alias log10Q = log2Q; - - // Coefficients for log(x) = z + z^^3 R(z^^2)/S(z^^2) - // where z = 2(x-1)/(x+1) - // Theoretical peak relative error = 6.16e-22 - static immutable real[4] logR = [ - -3.5717684488096787370998E1L, - 1.0777257190312272158094E1L, - -7.1990767473014147232598E-1L, - 1.9757429581415468984296E-3L, - ]; - static immutable real[4] logS = [ - -4.2861221385716144629696E2L, - 1.9361891836232102174846E2L, - -2.6201045551331104417768E1L, - 1.0000000000000000000000E0L, - ]; - } - else static if (floatTraits!T.realFormat == RealFormat.ieeeDouble) - { - // Coefficients for log(1 + x) = x - x^^2/2 + x^^3 P(x)/Q(x) - static immutable double[6] logP = [ - 7.70838733755885391666E0, - 1.79368678507819816313E1, - 1.44989225341610930846E1, - 4.70579119878881725854E0, - 4.97494994976747001425E-1, - 1.01875663804580931796E-4, - ]; - static immutable double[6] logQ = [ - 2.31251620126765340583E1, - 7.11544750618563894466E1, - 8.29875266912776603211E1, - 4.52279145837532221105E1, - 1.12873587189167450590E1, - 1.00000000000000000000E0, - ]; - - // log2 uses the same coefficients as log. - alias log2P = logP; - alias log2Q = logQ; - - // Coefficients for log(1 + x) = x - x^^2/2 + x^^3 P(x)/Q(x) - static immutable double[7] logp1P = [ - 2.0039553499201281259648E1, - 5.7112963590585538103336E1, - 6.0949667980987787057556E1, - 2.9911919328553073277375E1, - 6.5787325942061044846969E0, - 4.9854102823193375972212E-1, - 4.5270000862445199635215E-5, - ]; - static immutable double[7] logp1Q = [ - 6.0118660497603843919306E1, - 2.1642788614495947685003E2, - 3.0909872225312059774938E2, - 2.2176239823732856465394E2, - 8.3047565967967209469434E1, - 1.5062909083469192043167E1, - 1.0000000000000000000000E0, - ]; - - static immutable double[7] log10P = [ - 1.98892446572874072159E1, - 5.67349287391754285487E1, - 6.06127134467767258030E1, - 2.97877425097986925891E1, - 6.56312093769992875930E0, - 4.98531067254050724270E-1, - 4.58482948458143443514E-5, - ]; - static immutable double[7] log10Q = [ - 5.96677339718622216300E1, - 2.14955586696422947765E2, - 3.07254189979530058263E2, - 2.20664384982121929218E2, - 8.27410449222435217021E1, - 1.50314182634250003249E1, - 1.00000000000000000000E0, - ]; - - // Coefficients for log(x) = z + z^^3 R(z)/S(z) - // where z = 2(x-1)/(x+1) - static immutable double[3] logR = [ - -6.41409952958715622951E1, - 1.63866645699558079767E1, - -7.89580278884799154124E-1, - ]; - static immutable double[4] logS = [ - -7.69691943550460008604E2, - 3.12093766372244180303E2, - -3.56722798256324312549E1, - 1.00000000000000000000E0, - ]; - } - else static if (floatTraits!T.realFormat == RealFormat.ieeeSingle) - { - // Coefficients for log(1 + x) = x - x^^2/2 + x^^3 P(x) - static immutable float[9] logP = [ - 3.3333331174E-1, - -2.4999993993E-1, - 2.0000714765E-1, - -1.6668057665E-1, - 1.4249322787E-1, - -1.2420140846E-1, - 1.1676998740E-1, - -1.1514610310E-1, - 7.0376836292E-2, - ]; - - // Coefficients for log(1 + x) = x - x^^2/2 + x^^3 P(x)/Q(x) - static immutable float[7] logp1P = [ - 2.0039553499E1, - 5.7112963590E1, - 6.0949667980E1, - 2.9911919328E1, - 6.5787325942E0, - 4.9854102823E-1, - 4.5270000862E-5, - ]; - static immutable float[7] logp1Q = [ - 6.01186604976E1, - 2.16427886144E2, - 3.09098722253E2, - 2.21762398237E2, - 8.30475659679E1, - 1.50629090834E1, - 1.00000000000E0, - ]; - - // log2 and log10 uses the same coefficients as log. - alias log2P = logP; - alias log10P = logP; - } - else - static assert(0, "no coefficients for log()"); - } -} - -/************************************** - * Calculate the natural logarithm of x. - * - * $(TABLE_SV - * $(TR $(TH x) $(TH log(x)) $(TH divide by 0?) $(TH invalid?)) - * $(TR $(TD $(PLUSMN)0.0) $(TD -$(INFIN)) $(TD yes) $(TD no)) - * $(TR $(TD $(LT)0.0) $(TD $(NAN)) $(TD no) $(TD yes)) - * $(TR $(TD +$(INFIN)) $(TD +$(INFIN)) $(TD no) $(TD no)) - * ) - */ -pragma(inline, true) -real log(real x) @safe pure nothrow @nogc -{ - version (INLINE_YL2X) - { - import std.math.constants : LN2; - return core.math.yl2x(x, LN2); - } - else - return logImpl(x); -} - -/// ditto -pragma(inline, true) -double log(double x) @safe pure nothrow @nogc { return __ctfe ? cast(double) log(cast(real) x) : logImpl(x); } - -/// ditto -pragma(inline, true) -float log(float x) @safe pure nothrow @nogc { return __ctfe ? cast(float) log(cast(real) x) : logImpl(x); } - -// @@@DEPRECATED_[2.112.0]@@@ -deprecated("`std.math.exponential.log` called with argument types `(int)` matches both " - ~ "`log(real)`, `log(double)`, and `log(float)`. Cast argument to floating point type instead.") -real log(int x) @safe pure nothrow @nogc { return log(cast(real) x); } -// @@@DEPRECATED_[2.112.0]@@@ -deprecated("`std.math.exponential.log` called with argument types `(uint)` matches both " - ~ "`log(real)`, `log(double)`, and `log(float)`. Cast argument to floating point type instead.") -real log(uint x) @safe pure nothrow @nogc { return log(cast(real) x); } -// @@@DEPRECATED_[2.112.0]@@@ -deprecated("`std.math.exponential.log` called with argument types `(long)` matches both " - ~ "`log(real)`, `log(double)`, and `log(float)`. Cast argument to floating point type instead.") -real log(long x) @safe pure nothrow @nogc { return log(cast(real) x); } -// @@@DEPRECATED_[2.112.0]@@@ -deprecated("`std.math.exponential.log` called with argument types `(ulong)` matches both " - ~ "`log(real)`, `log(double)`, and `log(float)`. Cast argument to floating point type instead.") -real log(ulong x) @safe pure nothrow @nogc { return log(cast(real) x); } - -/// -@safe pure nothrow @nogc unittest -{ - import std.math.operations : feqrel; - import std.math.constants : E; - - assert(feqrel(log(E), 1) >= real.mant_dig - 1); -} - -private T logImpl(T, bool LOG1P = false)(T x) @safe pure nothrow @nogc -{ - import std.math.constants : SQRT1_2; - import std.math.algebraic : poly; - import std.math.traits : isInfinity, isNaN, signbit, floatTraits, RealFormat; - - alias coeffs = LogCoeffs!T; - alias F = floatTraits!T; - - static if (LOG1P) - { - const T xm1 = x; - x = x + 1.0; - } - - static if (F.realFormat == RealFormat.ieeeExtended || - F.realFormat == RealFormat.ieeeExtended53 || - F.realFormat == RealFormat.ieeeQuadruple) - { - // C1 + C2 = LN2. - enum T C1 = 6.93145751953125E-1L; - enum T C2 = 1.428606820309417232121458176568075500134E-6L; - } - else static if (F.realFormat == RealFormat.ieeeDouble) - { - enum T C1 = 0.693359375; - enum T C2 = -2.121944400546905827679e-4; - } - else static if (F.realFormat == RealFormat.ieeeSingle) - { - enum T C1 = 0.693359375; - enum T C2 = -2.12194440e-4; - } - else - static assert(0, "Not implemented for this architecture"); - - // Special cases. - if (isNaN(x)) - return x; - if (isInfinity(x) && !signbit(x)) - return x; - if (x == 0.0) - return -T.infinity; - if (x < 0.0) - return T.nan; - - // Separate mantissa from exponent. - // Note, frexp is used so that denormal numbers will be handled properly. - T y, z; - int exp; - - x = frexp(x, exp); - - static if (F.realFormat == RealFormat.ieeeDouble || - F.realFormat == RealFormat.ieeeExtended || - F.realFormat == RealFormat.ieeeExtended53 || - F.realFormat == RealFormat.ieeeQuadruple) - { - // Logarithm using log(x) = z + z^^3 R(z) / S(z), - // where z = 2(x - 1)/(x + 1) - if ((exp > 2) || (exp < -2)) - { - if (x < SQRT1_2) - { // 2(2x - 1)/(2x + 1) - exp -= 1; - z = x - 0.5; - y = 0.5 * z + 0.5; - } - else - { // 2(x - 1)/(x + 1) - z = x - 0.5; - z -= 0.5; - y = 0.5 * x + 0.5; - } - x = z / y; - z = x * x; - z = x * (z * poly(z, coeffs.logR) / poly(z, coeffs.logS)); - z += exp * C2; - z += x; - z += exp * C1; - - return z; - } - } - - // Logarithm using log(1 + x) = x - .5x^^2 + x^^3 P(x) / Q(x) - if (x < SQRT1_2) - { - exp -= 1; - static if (LOG1P) - { - if (exp != 0) - x = 2.0 * x - 1.0; - else - x = xm1; - } - else - x = 2.0 * x - 1.0; - - } - else - { - static if (LOG1P) - { - if (exp != 0) - x = x - 1.0; - else - x = xm1; - } - else - x = x - 1.0; - } - z = x * x; - static if (F.realFormat == RealFormat.ieeeSingle) - y = x * (z * poly(x, coeffs.logP)); - else - y = x * (z * poly(x, coeffs.logP) / poly(x, coeffs.logQ)); - y += exp * C2; - z = y - 0.5 * z; - - // Note, the sum of above terms does not exceed x/4, - // so it contributes at most about 1/4 lsb to the error. - z += x; - z += exp * C1; - - return z; -} - -@safe @nogc nothrow unittest -{ - import std.math.traits : floatTraits, RealFormat; - import std.meta : AliasSeq; - - static void testLog(T)(T[2][] vals) - { - import std.math.operations : isClose; - import std.math.traits : isNaN; - foreach (ref pair; vals) - { - if (isNaN(pair[1])) - assert(isNaN(log(pair[0]))); - else - assert(isClose(log(pair[0]), pair[1])); - } - } - static foreach (F; AliasSeq!(float, double, real)) - {{ - scope F[2][] vals = [ - [F(1), F(0x0p+0)], [F(2), F(0x1.62e42fefa39ef358p-1)], - [F(4), F(0x1.62e42fefa39ef358p+0)], [F(8), F(0x1.0a2b23f3bab73682p+1)], - [F(16), F(0x1.62e42fefa39ef358p+1)], [F(32), F(0x1.bb9d3beb8c86b02ep+1)], - [F(64), F(0x1.0a2b23f3bab73682p+2)], [F(128), F(0x1.3687a9f1af2b14ecp+2)], - [F(256), F(0x1.62e42fefa39ef358p+2)], [F(512), F(0x1.8f40b5ed9812d1c2p+2)], - [F(1024), F(0x1.bb9d3beb8c86b02ep+2)], [F(2048), F(0x1.e7f9c1e980fa8e98p+2)], - [F(3), F(0x1.193ea7aad030a976p+0)], [F(5), F(0x1.9c041f7ed8d336bp+0)], - [F(7), F(0x1.f2272ae325a57546p+0)], [F(15), F(0x1.5aa16394d481f014p+1)], - [F(17), F(0x1.6aa6bc1fa7f79cfp+1)], [F(31), F(0x1.b78ce48912b59f12p+1)], - [F(33), F(0x1.bf8d8f4d5b8d1038p+1)], [F(63), F(0x1.09291e8e3181b20ep+2)], - [F(65), F(0x1.0b292939429755ap+2)], [F(-0), -F.infinity], [F(0), -F.infinity], - [F(0.1), F(-0x1.26bb1bbb5551582ep+1)], [F(0.25), F(-0x1.62e42fefa39ef358p+0)], - [F(0.75), F(-0x1.269621134db92784p-2)], [F(0.875), F(-0x1.1178e8227e47bde4p-3)], - [F(10), F(0x1.26bb1bbb5551582ep+1)], [F(100), F(0x1.26bb1bbb5551582ep+2)], - [F(10000), F(0x1.26bb1bbb5551582ep+3)], - ]; - testLog(vals); - }} - { - float[2][16] vals = [ - [float.nan, float.nan],[-float.nan, float.nan], - [float.infinity, float.infinity], [-float.infinity, float.nan], - [float.min_normal, -0x1.5d58ap+6f], [-float.min_normal, float.nan], - [float.max, 0x1.62e43p+6f], [-float.max, float.nan], - [float.min_normal / 2, -0x1.601e68p+6f], [-float.min_normal / 2, float.nan], - [float.max / 2, 0x1.601e68p+6f], [-float.max / 2, float.nan], - [float.min_normal / 3, -0x1.61bd9ap+6f], [-float.min_normal / 3, float.nan], - [float.max / 3, 0x1.5e7f36p+6f], [-float.max / 3, float.nan], - ]; - testLog(vals); - } - { - double[2][16] vals = [ - [double.nan, double.nan],[-double.nan, double.nan], - [double.infinity, double.infinity], [-double.infinity, double.nan], - [double.min_normal, -0x1.6232bdd7abcd2p+9], [-double.min_normal, double.nan], - [double.max, 0x1.62e42fefa39efp+9], [-double.max, double.nan], - [double.min_normal / 2, -0x1.628b76e3a7b61p+9], [-double.min_normal / 2, double.nan], - [double.max / 2, 0x1.628b76e3a7b61p+9], [-double.max / 2, double.nan], - [double.min_normal / 3, -0x1.62bf5d2b81354p+9], [-double.min_normal / 3, double.nan], - [double.max / 3, 0x1.6257909bce36ep+9], [-double.max / 3, double.nan], - ]; - testLog(vals); - } - alias F = floatTraits!real; - static if (F.realFormat == RealFormat.ieeeExtended || F.realFormat == RealFormat.ieeeQuadruple) - {{ - real[2][16] vals = [ - [real.nan, real.nan],[-real.nan, real.nan], - [real.infinity, real.infinity], [-real.infinity, real.nan], - [real.min_normal, -0x1.62d918ce2421d66p+13L], [-real.min_normal, real.nan], - [real.max, 0x1.62e42fefa39ef358p+13L], [-real.max, real.nan], - [real.min_normal / 2, -0x1.62dea45ee3e064dcp+13L], [-real.min_normal / 2, real.nan], - [real.max / 2, 0x1.62dea45ee3e064dcp+13L], [-real.max / 2, real.nan], - [real.min_normal / 3, -0x1.62e1e2c3617857e6p+13L], [-real.min_normal / 3, real.nan], - [real.max / 3, 0x1.62db65fa664871d2p+13L], [-real.max / 3, real.nan], - ]; - testLog(vals); - }} -} - -/************************************** - * Calculate the base-10 logarithm of x. - * - * $(TABLE_SV - * $(TR $(TH x) $(TH log10(x)) $(TH divide by 0?) $(TH invalid?)) - * $(TR $(TD $(PLUSMN)0.0) $(TD -$(INFIN)) $(TD yes) $(TD no)) - * $(TR $(TD $(LT)0.0) $(TD $(NAN)) $(TD no) $(TD yes)) - * $(TR $(TD +$(INFIN)) $(TD +$(INFIN)) $(TD no) $(TD no)) - * ) - */ -pragma(inline, true) -real log10(real x) @safe pure nothrow @nogc -{ - version (INLINE_YL2X) - { - import std.math.constants : LOG2; - return core.math.yl2x(x, LOG2); - } - else - return log10Impl(x); -} - -/// ditto -pragma(inline, true) -double log10(double x) @safe pure nothrow @nogc { return __ctfe ? cast(double) log10(cast(real) x) : log10Impl(x); } - -/// ditto -pragma(inline, true) -float log10(float x) @safe pure nothrow @nogc { return __ctfe ? cast(float) log10(cast(real) x) : log10Impl(x); } - -// @@@DEPRECATED_[2.112.0]@@@ -deprecated("`std.math.exponential.log10` called with argument types `(int)` matches both " - ~ "`log10(real)`, `log10(double)`, and `log10(float)`. Cast argument to floating point type instead.") -real log10(int x) @safe pure nothrow @nogc { return log10(cast(real) x); } -// @@@DEPRECATED_[2.112.0]@@@ -deprecated("`std.math.exponential.log10` called with argument types `(uint)` matches both " - ~ "`log10(real)`, `log10(double)`, and `log10(float)`. Cast argument to floating point type instead.") -real log10(uint x) @safe pure nothrow @nogc { return log10(cast(real) x); } -// @@@DEPRECATED_[2.112.0]@@@ -deprecated("`std.math.exponential.log10` called with argument types `(long)` matches both " - ~ "`log10(real)`, `log10(double)`, and `log10(float)`. Cast argument to floating point type instead.") -real log10(long x) @safe pure nothrow @nogc { return log10(cast(real) x); } -// @@@DEPRECATED_[2.112.0]@@@ -deprecated("`std.math.exponential.log10` called with argument types `(ulong)` matches both " - ~ "`log10(real)`, `log10(double)`, and `log10(float)`. Cast argument to floating point type instead.") -real log10(ulong x) @safe pure nothrow @nogc { return log10(cast(real) x); } - -/// -@safe pure nothrow @nogc unittest -{ - import std.math.algebraic : fabs; - - assert(fabs(log10(1000.0L) - 3) < .000001); -} - -@safe pure nothrow @nogc unittest -{ - import std.math.algebraic : fabs; - - assert(fabs(log10(1000.0) - 3) < .000001); - assert(fabs(log10(1000.0f) - 3) < .000001); -} - -private T log10Impl(T)(T x) @safe pure nothrow @nogc -{ - import std.math.constants : SQRT1_2; - import std.math.algebraic : poly; - import std.math.traits : isNaN, isInfinity, signbit, floatTraits, RealFormat; - - alias coeffs = LogCoeffs!T; - alias F = floatTraits!T; - - static if (F.realFormat == RealFormat.ieeeExtended || - F.realFormat == RealFormat.ieeeExtended53 || - F.realFormat == RealFormat.ieeeQuadruple) - { - // log10(2) split into two parts. - enum T L102A = 0.3125L; - enum T L102B = -1.14700043360188047862611052755069732318101185E-2L; - - // log10(e) split into two parts. - enum T L10EA = 0.5L; - enum T L10EB = -6.570551809674817234887108108339491770560299E-2L; - } - else static if (F.realFormat == RealFormat.ieeeDouble || - F.realFormat == RealFormat.ieeeSingle) - { - enum T L102A = 3.0078125E-1; - enum T L102B = 2.48745663981195213739E-4; - - enum T L10EA = 4.3359375E-1; - enum T L10EB = 7.00731903251827651129E-4; - } - else - static assert(0, "Not implemented for this architecture"); - - // Special cases are the same as for log. - if (isNaN(x)) - return x; - if (isInfinity(x) && !signbit(x)) - return x; - if (x == 0.0) - return -T.infinity; - if (x < 0.0) - return T.nan; - - // Separate mantissa from exponent. - // Note, frexp is used so that denormal numbers will be handled properly. - T y, z; - int exp; - - x = frexp(x, exp); - - static if (F.realFormat == RealFormat.ieeeExtended || - F.realFormat == RealFormat.ieeeExtended53 || - F.realFormat == RealFormat.ieeeQuadruple) - { - // Logarithm using log(x) = z + z^^3 R(z) / S(z), - // where z = 2(x - 1)/(x + 1) - if ((exp > 2) || (exp < -2)) - { - if (x < SQRT1_2) - { // 2(2x - 1)/(2x + 1) - exp -= 1; - z = x - 0.5; - y = 0.5 * z + 0.5; - } - else - { // 2(x - 1)/(x + 1) - z = x - 0.5; - z -= 0.5; - y = 0.5 * x + 0.5; - } - x = z / y; - z = x * x; - y = x * (z * poly(z, coeffs.logR) / poly(z, coeffs.logS)); - goto Ldone; - } - } - - // Logarithm using log(1 + x) = x - .5x^^2 + x^^3 P(x) / Q(x) - if (x < SQRT1_2) - { - exp -= 1; - x = 2.0 * x - 1.0; - } - else - x = x - 1.0; - - z = x * x; - static if (F.realFormat == RealFormat.ieeeSingle) - y = x * (z * poly(x, coeffs.log10P)); - else - y = x * (z * poly(x, coeffs.log10P) / poly(x, coeffs.log10Q)); - y = y - 0.5 * z; - - // Multiply log of fraction by log10(e) and base 2 exponent by log10(2). - // This sequence of operations is critical and it may be horribly - // defeated by some compiler optimizers. -Ldone: - z = y * L10EB; - z += x * L10EB; - z += exp * L102B; - z += y * L10EA; - z += x * L10EA; - z += exp * L102A; - - return z; -} - -@safe @nogc nothrow unittest -{ - import std.math.traits : floatTraits, RealFormat; - import std.meta : AliasSeq; - - static void testLog10(T)(T[2][] vals) - { - import std.math.operations : isClose; - import std.math.traits : isNaN; - foreach (ref pair; vals) - { - if (isNaN(pair[1])) - assert(isNaN(log10(pair[0]))); - else - assert(isClose(log10(pair[0]), pair[1])); - } - } - static foreach (F; AliasSeq!(float, double, real)) - {{ - scope F[2][] vals = [ - [F(1), F(0x0p+0)], [F(2), F(0x1.34413509f79fef32p-2)], - [F(4), F(0x1.34413509f79fef32p-1)], [F(8), F(0x1.ce61cf8ef36fe6cap-1)], - [F(16), F(0x1.34413509f79fef32p+0)], [F(32), F(0x1.8151824c7587eafep+0)], - [F(64), F(0x1.ce61cf8ef36fe6cap+0)], [F(128), F(0x1.0db90e68b8abf14cp+1)], - [F(256), F(0x1.34413509f79fef32p+1)], [F(512), F(0x1.5ac95bab3693ed18p+1)], - [F(1024), F(0x1.8151824c7587eafep+1)], [F(2048), F(0x1.a7d9a8edb47be8e4p+1)], - [F(3), F(0x1.e8927964fd5fd08cp-2)], [F(5), F(0x1.65df657b04300868p-1)], - [F(7), F(0x1.b0b0b0b78cc3f296p-1)], [F(15), F(0x1.2d145116c16ff856p+0)], - [F(17), F(0x1.3afeb354b7d9731ap+0)], [F(31), F(0x1.7dc9e145867e62eap+0)], - [F(33), F(0x1.84bd545e4baeddp+0)], [F(63), F(0x1.cca1950e4511e192p+0)], - [F(65), F(0x1.d01b16f9433cf7b8p+0)], [F(-0), -F.infinity], [F(0), -F.infinity], - [F(0.1), F(-0x1p+0)], [F(0.25), F(-0x1.34413509f79fef32p-1)], - [F(0.75), F(-0x1.ffbfc2bbc7803758p-4)], [F(0.875), F(-0x1.db11ed766abf432ep-5)], - [F(10), F(0x1p+0)], [F(100), F(0x1p+1)], [F(10000), F(0x1p+2)], - ]; - testLog10(vals); - }} - { - float[2][16] vals = [ - [float.nan, float.nan],[-float.nan, float.nan], - [float.infinity, float.infinity], [-float.infinity, float.nan], - [float.min_normal, -0x1.2f703p+5f], [-float.min_normal, float.nan], - [float.max, 0x1.344136p+5f], [-float.max, float.nan], - [float.min_normal / 2, -0x1.31d8b2p+5f], [-float.min_normal / 2, float.nan], - [float.max / 2, 0x1.31d8b2p+5f], [-float.max / 2, float.nan], - [float.min_normal / 3, -0x1.334156p+5f], [-float.min_normal / 3, float.nan], - [float.max / 3, 0x1.30701p+5f], [-float.max / 3, float.nan], - ]; - testLog10(vals); - } - { - double[2][16] vals = [ - [double.nan, double.nan],[-double.nan, double.nan], - [double.infinity, double.infinity], [-double.infinity, double.nan], - [double.min_normal, -0x1.33a7146f72a42p+8], [-double.min_normal, double.nan], - [double.max, 0x1.34413509f79ffp+8], [-double.max, double.nan], - [double.min_normal / 2, -0x1.33f424bcb522p+8], [-double.min_normal / 2, double.nan], - [double.max / 2, 0x1.33f424bcb522p+8], [-double.max / 2, double.nan], - [double.min_normal / 3, -0x1.3421390dcbe37p+8], [-double.min_normal / 3, double.nan], - [double.max / 3, 0x1.33c7106b9e609p+8], [-double.max / 3, double.nan], - ]; - testLog10(vals); - } - alias F = floatTraits!real; - static if (F.realFormat == RealFormat.ieeeExtended || F.realFormat == RealFormat.ieeeQuadruple) - {{ - real[2][16] vals = [ - [real.nan, real.nan],[-real.nan, real.nan], - [real.infinity, real.infinity], [-real.infinity, real.nan], - [real.min_normal, -0x1.343793004f503232p+12L], [-real.min_normal, real.nan], - [real.max, 0x1.34413509f79fef32p+12L], [-real.max, real.nan], - [real.min_normal / 2, -0x1.343c6405237810b2p+12L], [-real.min_normal / 2, real.nan], - [real.max / 2, 0x1.343c6405237810b2p+12L], [-real.max / 2, real.nan], - [real.min_normal / 3, -0x1.343f354a34e427bp+12L], [-real.min_normal / 3, real.nan], - [real.max / 3, 0x1.343992c0120bf9b2p+12L], [-real.max / 3, real.nan], - ]; - testLog10(vals); - }} -} - -/** - * Calculates the natural logarithm of 1 + x. - * - * For very small x, log1p(x) will be more accurate than - * log(1 + x). - * - * $(TABLE_SV - * $(TR $(TH x) $(TH log1p(x)) $(TH divide by 0?) $(TH invalid?)) - * $(TR $(TD $(PLUSMN)0.0) $(TD $(PLUSMN)0.0) $(TD no) $(TD no)) - * $(TR $(TD -1.0) $(TD -$(INFIN)) $(TD yes) $(TD no)) - * $(TR $(TD $(LT)-1.0) $(TD -$(NAN)) $(TD no) $(TD yes)) - * $(TR $(TD +$(INFIN)) $(TD +$(INFIN)) $(TD no) $(TD no)) - * ) - */ -pragma(inline, true) -real log1p(real x) @safe pure nothrow @nogc -{ - version (INLINE_YL2X) - { - // On x87, yl2xp1 is valid if and only if -0.5 <= lg(x) <= 0.5, - // ie if -0.29 <= x <= 0.414 - import std.math.constants : LN2; - return (core.math.fabs(x) <= 0.25) ? core.math.yl2xp1(x, LN2) : core.math.yl2x(x+1, LN2); - } - else - return log1pImpl(x); -} - -/// ditto -pragma(inline, true) -double log1p(double x) @safe pure nothrow @nogc { return __ctfe ? cast(double) log1p(cast(real) x) : log1pImpl(x); } - -/// ditto -pragma(inline, true) -float log1p(float x) @safe pure nothrow @nogc { return __ctfe ? cast(float) log1p(cast(real) x) : log1pImpl(x); } - -// @@@DEPRECATED_[2.112.0]@@@ -deprecated("`std.math.exponential.log1p` called with argument types `(int)` matches both " - ~ "`log1p(real)`, `log1p(double)`, and `log1p(float)`. Cast argument to floating point type instead.") -real log1p(int x) @safe pure nothrow @nogc { return log1p(cast(real) x); } -// @@@DEPRECATED_[2.112.0]@@@ -deprecated("`std.math.exponential.log1p` called with argument types `(uint)` matches both " - ~ "`log1p(real)`, `log1p(double)`, and `log1p(float)`. Cast argument to floating point type instead.") -real log1p(uint x) @safe pure nothrow @nogc { return log1p(cast(real) x); } -// @@@DEPRECATED_[2.112.0]@@@ -deprecated("`std.math.exponential.log1p` called with argument types `(long)` matches both " - ~ "`log1p(real)`, `log1p(double)`, and `log1p(float)`. Cast argument to floating point type instead.") -real log1p(long x) @safe pure nothrow @nogc { return log1p(cast(real) x); } -// @@@DEPRECATED_[2.112.0]@@@ -deprecated("`std.math.exponential.log1p` called with argument types `(ulong)` matches both " - ~ "`log1p(real)`, `log1p(double)`, and `log1p(float)`. Cast argument to floating point type instead.") -real log1p(ulong x) @safe pure nothrow @nogc { return log1p(cast(real) x); } - -/// -@safe pure unittest -{ - import std.math.traits : isIdentical, isNaN; - import std.math.operations : feqrel; - - assert(isIdentical(log1p(0.0), 0.0)); - assert(log1p(1.0).feqrel(0.69314) > 16); - - assert(log1p(-1.0) == -real.infinity); - assert(isNaN(log1p(-2.0))); - assert(log1p(real.nan) is real.nan); - assert(log1p(-real.nan) is -real.nan); - assert(log1p(real.infinity) == real.infinity); -} - -private T log1pImpl(T)(T x) @safe pure nothrow @nogc -{ - import std.math.traits : isNaN, isInfinity, signbit; - import std.math.algebraic : poly; - import std.math.constants : SQRT1_2, SQRT2; - import std.math.traits : floatTraits, RealFormat; - - // Special cases. - if (isNaN(x) || x == 0.0) - return x; - if (isInfinity(x) && !signbit(x)) - return x; - if (x == -1.0) - return -T.infinity; - if (x < -1.0) - return T.nan; - - alias F = floatTraits!T; - static if (F.realFormat == RealFormat.ieeeSingle || - F.realFormat == RealFormat.ieeeDouble) - { - // When the input is within the range 1/sqrt(2) <= x+1 <= sqrt(2), compute - // log1p inline. Forwarding to log() would otherwise result in inaccuracies. - const T xp1 = x + 1.0; - if (xp1 >= SQRT1_2 && xp1 <= SQRT2) - { - alias coeffs = LogCoeffs!T; - - T px = poly(x, coeffs.logp1P); - T qx = poly(x, coeffs.logp1Q); - const T xx = x * x; - qx = x + ((cast(T) -0.5) * xx + x * (xx * px / qx)); - return qx; - } - } - - return logImpl!(T, true)(x); -} - -@safe @nogc nothrow unittest -{ - import std.math.traits : floatTraits, RealFormat; - import std.meta : AliasSeq; - - static void testLog1p(T)(T[2][] vals) - { - import std.math.operations : isClose; - import std.math.traits : isNaN; - foreach (ref pair; vals) - { - if (isNaN(pair[1])) - assert(isNaN(log1p(pair[0]))); - else - assert(isClose(log1p(pair[0]), pair[1])); - } - } - static foreach (F; AliasSeq!(float, double, real)) - {{ - scope F[2][] vals = [ - [F(1), F(0x1.62e42fefa39ef358p-1)], [F(2), F(0x1.193ea7aad030a976p+0)], - [F(4), F(0x1.9c041f7ed8d336bp+0)], [F(8), F(0x1.193ea7aad030a976p+1)], - [F(16), F(0x1.6aa6bc1fa7f79cfp+1)], [F(32), F(0x1.bf8d8f4d5b8d1038p+1)], - [F(64), F(0x1.0b292939429755ap+2)], [F(128), F(0x1.37072a9b5b6cb31p+2)], - [F(256), F(0x1.63241004e9010ad8p+2)], [F(512), F(0x1.8f60adf041bde2a8p+2)], - [F(1024), F(0x1.bbad39ebe1cc08b6p+2)], [F(2048), F(0x1.e801c1698ba4395cp+2)], - [F(3), F(0x1.62e42fefa39ef358p+0)], [F(5), F(0x1.cab0bfa2a2002322p+0)], - [F(7), F(0x1.0a2b23f3bab73682p+1)], [F(15), F(0x1.62e42fefa39ef358p+1)], - [F(17), F(0x1.71f7b3a6b918664cp+1)], [F(31), F(0x1.bb9d3beb8c86b02ep+1)], - [F(33), F(0x1.c35fc81b90df59c6p+1)], [F(63), F(0x1.0a2b23f3bab73682p+2)], - [F(65), F(0x1.0c234da4a23a6686p+2)], [F(-0), F(-0x0p+0)], [F(0), F(0x0p+0)], - [F(0.1), F(0x1.8663f793c46c69cp-4)], [F(0.25), F(0x1.c8ff7c79a9a21ac4p-3)], - [F(0.75), F(0x1.1e85f5e7040d03ep-1)], [F(0.875), F(0x1.41d8fe84672ae646p-1)], - [F(10), F(0x1.32ee3b77f374bb7cp+1)], [F(100), F(0x1.275e2271bba30be4p+2)], - [F(10000), F(0x1.26bbed6fbd84182ep+3)], - ]; - testLog1p(vals); - }} - { - float[2][16] vals = [ - [float.nan, float.nan],[-float.nan, float.nan], - [float.infinity, float.infinity], [-float.infinity, float.nan], - [float.min_normal, 0x1p-126f], [-float.min_normal, -0x1p-126f], - [float.max, 0x1.62e43p+6f], [-float.max, float.nan], - [float.min_normal / 2, 0x0.8p-126f], [-float.min_normal / 2, -0x0.8p-126f], - [float.max / 2, 0x1.601e68p+6f], [-float.max / 2, float.nan], - [float.min_normal / 3, 0x0.555556p-126f], [-float.min_normal / 3, -0x0.555556p-126f], - [float.max / 3, 0x1.5e7f36p+6f], [-float.max / 3, float.nan], - ]; - testLog1p(vals); - } - { - double[2][16] vals = [ - [double.nan, double.nan],[-double.nan, double.nan], - [double.infinity, double.infinity], [-double.infinity, double.nan], - [double.min_normal, 0x1p-1022], [-double.min_normal, -0x1p-1022], - [double.max, 0x1.62e42fefa39efp+9], [-double.max, double.nan], - [double.min_normal / 2, 0x0.8p-1022], [-double.min_normal / 2, -0x0.8p-1022], - [double.max / 2, 0x1.628b76e3a7b61p+9], [-double.max / 2, double.nan], - [double.min_normal / 3, 0x0.5555555555555p-1022], [-double.min_normal / 3, -0x0.5555555555555p-1022], - [double.max / 3, 0x1.6257909bce36ep+9], [-double.max / 3, double.nan], - ]; - testLog1p(vals); - } - alias F = floatTraits!real; - static if (F.realFormat == RealFormat.ieeeExtended || F.realFormat == RealFormat.ieeeQuadruple) - {{ - real[2][16] vals = [ - [real.nan, real.nan],[-real.nan, real.nan], - [real.infinity, real.infinity], [-real.infinity, real.nan], - [real.min_normal, 0x1p-16382L], [-real.min_normal, -0x1p-16382L], - [real.max, 0x1.62e42fefa39ef358p+13L], [-real.max, real.nan], - [real.min_normal / 2, 0x0.8p-16382L], [-real.min_normal / 2, -0x0.8p-16382L], - [real.max / 2, 0x1.62dea45ee3e064dcp+13L], [-real.max / 2, real.nan], - [real.min_normal / 3, 0x0.5555555555555556p-16382L], [-real.min_normal / 3, -0x0.5555555555555556p-16382L], - [real.max / 3, 0x1.62db65fa664871d2p+13L], [-real.max / 3, real.nan], - ]; - testLog1p(vals); - }} -} - -/*************************************** - * Calculates the base-2 logarithm of x: - * $(SUB log, 2)x - * - * $(TABLE_SV - * $(TR $(TH x) $(TH log2(x)) $(TH divide by 0?) $(TH invalid?)) - * $(TR $(TD $(PLUSMN)0.0) $(TD -$(INFIN)) $(TD yes) $(TD no) ) - * $(TR $(TD $(LT)0.0) $(TD $(NAN)) $(TD no) $(TD yes) ) - * $(TR $(TD +$(INFIN)) $(TD +$(INFIN)) $(TD no) $(TD no) ) - * ) - */ -pragma(inline, true) -real log2(real x) @safe pure nothrow @nogc -{ - version (INLINE_YL2X) - return core.math.yl2x(x, 1.0L); - else - return log2Impl(x); -} - -/// ditto -pragma(inline, true) -double log2(double x) @safe pure nothrow @nogc { return __ctfe ? cast(double) log2(cast(real) x) : log2Impl(x); } - -/// ditto -pragma(inline, true) -float log2(float x) @safe pure nothrow @nogc { return __ctfe ? cast(float) log2(cast(real) x) : log2Impl(x); } - -// @@@DEPRECATED_[2.112.0]@@@ -deprecated("`std.math.exponential.log2` called with argument types `(int)` matches both " - ~ "`log2(real)`, `log2(double)`, and `log2(float)`. Cast argument to floating point type instead.") -real log2(int x) @safe pure nothrow @nogc { return log2(cast(real) x); } -// @@@DEPRECATED_[2.112.0]@@@ -deprecated("`std.math.exponential.log2` called with argument types `(uint)` matches both " - ~ "`log2(real)`, `log2(double)`, and `log2(float)`. Cast argument to floating point type instead.") -real log2(uint x) @safe pure nothrow @nogc { return log2(cast(real) x); } -// @@@DEPRECATED_[2.112.0]@@@ -deprecated("`std.math.exponential.log2` called with argument types `(long)` matches both " - ~ "`log2(real)`, `log2(double)`, and `log2(float)`. Cast argument to floating point type instead.") -real log2(long x) @safe pure nothrow @nogc { return log2(cast(real) x); } -// @@@DEPRECATED_[2.112.0]@@@ -deprecated("`std.math.exponential.log2` called with argument types `(ulong)` matches both " - ~ "`log2(real)`, `log2(double)`, and `log2(float)`. Cast argument to floating point type instead.") -real log2(ulong x) @safe pure nothrow @nogc { return log2(cast(real) x); } - -/// -@safe unittest -{ - import std.math.operations : isClose; - - assert(isClose(log2(1024.0L), 10)); -} - -@safe @nogc nothrow unittest -{ - import std.math.operations : isClose; - - // check if values are equal to 19 decimal digits of precision - assert(isClose(log2(1024.0L), 10, 1e-18)); -} - -private T log2Impl(T)(T x) @safe pure nothrow @nogc -{ - import std.math.traits : isNaN, isInfinity, signbit; - import std.math.constants : SQRT1_2, LOG2E; - import std.math.algebraic : poly; - import std.math.traits : floatTraits, RealFormat; - - alias coeffs = LogCoeffs!T; - alias F = floatTraits!T; - - // Special cases are the same as for log. - if (isNaN(x)) - return x; - if (isInfinity(x) && !signbit(x)) - return x; - if (x == 0.0) - return -T.infinity; - if (x < 0.0) - return T.nan; - - // Separate mantissa from exponent. - // Note, frexp is used so that denormal numbers will be handled properly. - T y, z; - int exp; - - x = frexp(x, exp); - - static if (F.realFormat == RealFormat.ieeeDouble || - F.realFormat == RealFormat.ieeeExtended || - F.realFormat == RealFormat.ieeeExtended53 || - F.realFormat == RealFormat.ieeeQuadruple) - { - // Logarithm using log(x) = z + z^^3 R(z) / S(z), - // where z = 2(x - 1)/(x + 1) - if ((exp > 2) || (exp < -2)) - { - if (x < SQRT1_2) - { // 2(2x - 1)/(2x + 1) - exp -= 1; - z = x - 0.5; - y = 0.5 * z + 0.5; - } - else - { // 2(x - 1)/(x + 1) - z = x - 0.5; - z -= 0.5; - y = 0.5 * x + 0.5; - } - x = z / y; - z = x * x; - y = x * (z * poly(z, coeffs.logR) / poly(z, coeffs.logS)); - goto Ldone; - } - } - - // Logarithm using log(1 + x) = x - .5x^^2 + x^^3 P(x) / Q(x) - if (x < SQRT1_2) - { - exp -= 1; - x = 2.0 * x - 1.0; - } - else - x = x - 1.0; - - z = x * x; - static if (F.realFormat == RealFormat.ieeeSingle) - y = x * (z * poly(x, coeffs.log2P)); - else - y = x * (z * poly(x, coeffs.log2P) / poly(x, coeffs.log2Q)); - y = y - 0.5 * z; - - // Multiply log of fraction by log10(e) and base 2 exponent by log10(2). - // This sequence of operations is critical and it may be horribly - // defeated by some compiler optimizers. -Ldone: - z = y * (LOG2E - 1.0); - z += x * (LOG2E - 1.0); - z += y; - z += x; - z += exp; - - return z; -} - -@safe @nogc nothrow unittest -{ - import std.math.traits : floatTraits, RealFormat; - import std.meta : AliasSeq; - - static void testLog2(T)(T[2][] vals) - { - import std.math.operations : isClose; - import std.math.traits : isNaN; - foreach (ref pair; vals) - { - if (isNaN(pair[1])) - assert(isNaN(log2(pair[0]))); - else - assert(isClose(log2(pair[0]), pair[1])); - } - } - static foreach (F; AliasSeq!(float, double, real)) - {{ - scope F[2][] vals = [ - [F(1), F(0x0p+0)], [F(2), F(0x1p+0)], - [F(4), F(0x1p+1)], [F(8), F(0x1.8p+1)], - [F(16), F(0x1p+2)], [F(32), F(0x1.4p+2)], - [F(64), F(0x1.8p+2)], [F(128), F(0x1.cp+2)], - [F(256), F(0x1p+3)], [F(512), F(0x1.2p+3)], - [F(1024), F(0x1.4p+3)], [F(2048), F(0x1.6p+3)], - [F(3), F(0x1.95c01a39fbd687ap+0)], [F(5), F(0x1.2934f0979a3715fcp+1)], - [F(7), F(0x1.675767f54042cd9ap+1)], [F(15), F(0x1.f414fdb4982259ccp+1)], - [F(17), F(0x1.0598fdbeb244c5ap+2)], [F(31), F(0x1.3d118d66c4d4e554p+2)], - [F(33), F(0x1.42d75a6eb1dfb0e6p+2)], [F(63), F(0x1.7e8bc1179e0caa9cp+2)], - [F(65), F(0x1.816e79685c2d2298p+2)], [F(-0), -F.infinity], [F(0), -F.infinity], - [F(0.1), F(-0x1.a934f0979a3715fcp+1)], [F(0.25), F(-0x1p+1)], - [F(0.75), F(-0x1.a8ff971810a5e182p-2)], [F(0.875), F(-0x1.8a8980abfbd32668p-3)], - [F(10), F(0x1.a934f0979a3715fcp+1)], [F(100), F(0x1.a934f0979a3715fcp+2)], - [F(10000), F(0x1.a934f0979a3715fcp+3)], - ]; - testLog2(vals); - }} - { - float[2][16] vals = [ - [float.nan, float.nan],[-float.nan, float.nan], - [float.infinity, float.infinity], [-float.infinity, float.nan], - [float.min_normal, -0x1.f8p+6f], [-float.min_normal, float.nan], - [float.max, 0x1p+7f], [-float.max, float.nan], - [float.min_normal / 2, -0x1.fcp+6f], [-float.min_normal / 2, float.nan], - [float.max / 2, 0x1.fcp+6f], [-float.max / 2, float.nan], - [float.min_normal / 3, -0x1.fe57p+6f], [-float.min_normal / 3, float.nan], - [float.max / 3, 0x1.f9a9p+6f], [-float.max / 3, float.nan], - ]; - testLog2(vals); - } - { - double[2][16] vals = [ - [double.nan, double.nan],[-double.nan, double.nan], - [double.infinity, double.infinity], [-double.infinity, double.nan], - [double.min_normal, -0x1.ffp+9], [-double.min_normal, double.nan], - [double.max, 0x1p+10], [-double.max, double.nan], - [double.min_normal / 2, -0x1.ff8p+9], [-double.min_normal / 2, double.nan], - [double.max / 2, 0x1.ff8p+9], [-double.max / 2, double.nan], - [double.min_normal / 3, -0x1.ffcae00d1cfdfp+9], [-double.min_normal / 3, double.nan], - [double.max / 3, 0x1.ff351ff2e3021p+9], [-double.max / 3, double.nan], - ]; - testLog2(vals); - } - alias F = floatTraits!real; - static if (F.realFormat == RealFormat.ieeeExtended || F.realFormat == RealFormat.ieeeQuadruple) - {{ - real[2][16] vals = [ - [real.nan, real.nan],[-real.nan, real.nan], - [real.infinity, real.infinity], [-real.infinity, real.nan], - [real.min_normal, -0x1.fffp+13L], [-real.min_normal, real.nan], - [real.max, 0x1p+14L], [-real.max, real.nan], - [real.min_normal / 2, -0x1.fff8p+13L], [-real.min_normal / 2, real.nan], - [real.max / 2, 0x1.fff8p+13L], [-real.max / 2, real.nan], - [real.min_normal / 3, -0x1.fffcae00d1cfdeb4p+13L], [-real.min_normal / 3, real.nan], - [real.max / 3, 0x1.fff351ff2e30214cp+13L], [-real.max / 3, real.nan], - ]; - testLog2(vals); - }} -} - -/***************************************** - * Extracts the exponent of x as a signed integral value. - * - * If x is subnormal, it is treated as if it were normalized. - * For a positive, finite x: - * - * 1 $(LT)= $(I x) * FLT_RADIX$(SUPERSCRIPT -logb(x)) $(LT) FLT_RADIX - * - * $(TABLE_SV - * $(TR $(TH x) $(TH logb(x)) $(TH divide by 0?) ) - * $(TR $(TD $(PLUSMN)$(INFIN)) $(TD +$(INFIN)) $(TD no)) - * $(TR $(TD $(PLUSMN)0.0) $(TD -$(INFIN)) $(TD yes) ) - * ) - */ -pragma(inline, true) -real logb(real x) @trusted pure nothrow @nogc -{ - version (InlineAsm_X87_MSVC) - return logbAsm(x); - else - return logbImpl(x); -} - -/// ditto -pragma(inline, true) -double logb(double x) @trusted pure nothrow @nogc { return logbImpl(x); } - -/// ditto -pragma(inline, true) -float logb(float x) @trusted pure nothrow @nogc { return logbImpl(x); } - -/// -@safe @nogc nothrow unittest -{ - assert(logb(1.0) == 0); - assert(logb(100.0) == 6); - - assert(logb(0.0) == -real.infinity); - assert(logb(real.infinity) == real.infinity); - assert(logb(-real.infinity) == real.infinity); -} - -@safe @nogc nothrow unittest -{ - import std.meta : AliasSeq; - import std.typecons : Tuple; - import std.math.traits : isNaN; - static foreach (F; AliasSeq!(float, double, real)) - {{ - alias T = Tuple!(F, F); - T[17] vals = // x, logb(x) - [ - T(1.0 , 0 ), - T(100.0 , 6 ), - T(0.0 , -F.infinity), - T(-0.0 , -F.infinity), - T(1024 , 10 ), - T(-2000 , 10 ), - T(0x0.1p-127 , -131 ), - T(0x0.01p-127 , -135 ), - T(0x0.011p-127 , -135 ), - T(F.nan , F.nan ), - T(-F.nan , F.nan ), - T(F.infinity , F.infinity ), - T(-F.infinity , F.infinity ), - T(F.min_normal , F.min_exp-1), - T(-F.min_normal, F.min_exp-1), - T(F.max , F.max_exp-1), - T(-F.max , F.max_exp-1), - ]; - - foreach (elem; vals) - { - if (isNaN(elem[1])) - assert(isNaN(logb(elem[1]))); - else - assert(logb(elem[0]) == elem[1]); - } - }} -} - -version (InlineAsm_X87_MSVC) -private T logbAsm(T)(T x) @trusted pure nothrow @nogc -{ - version (X86_64) - { - asm pure nothrow @nogc - { - naked ; - fld real ptr [RCX] ; - fxtract ; - fstp ST(0) ; - ret ; - } - } - else - { - asm pure nothrow @nogc - { - fld x ; - fxtract ; - fstp ST(0) ; - } - } -} - -private T logbImpl(T)(T x) @trusted pure nothrow @nogc -{ - import std.math.traits : isFinite; - - // Handle special cases. - if (!isFinite(x)) - return x * x; - if (x == 0) - return -1 / (x * x); - - return ilogb(x); -} - -@safe @nogc nothrow unittest -{ - import std.math.traits : floatTraits, RealFormat; - import std.meta : AliasSeq; - - static void testLogb(T)(T[2][] vals) - { - import std.math.operations : isClose; - import std.math.traits : isNaN; - foreach (ref pair; vals) - { - if (isNaN(pair[1])) - assert(isNaN(logb(pair[0]))); - else - assert(isClose(logb(pair[0]), pair[1])); - } - } - static foreach (F; AliasSeq!(float, double, real)) - {{ - scope F[2][] vals = [ - [F(1), F(0x0p+0)], [F(2), F(0x1p+0)], - [F(4), F(0x1p+1)], [F(8), F(0x1.8p+1)], - [F(16), F(0x1p+2)], [F(32), F(0x1.4p+2)], - [F(64), F(0x1.8p+2)], [F(128), F(0x1.cp+2)], - [F(256), F(0x1p+3)], [F(512), F(0x1.2p+3)], - [F(1024), F(0x1.4p+3)], [F(2048), F(0x1.6p+3)], - [F(3), F(0x1p+0)], [F(5), F(0x1p+1)], - [F(7), F(0x1p+1)], [F(15), F(0x1.8p+1)], - [F(17), F(0x1p+2)], [F(31), F(0x1p+2)], - [F(33), F(0x1.4p+2)], [F(63), F(0x1.4p+2)], - [F(65), F(0x1.8p+2)], [F(-0), -F.infinity], [F(0), -F.infinity], - [F(0.1), F(-0x1p+2)], [F(0.25), F(-0x1p+1)], - [F(0.75), F(-0x1p+0)], [F(0.875), F(-0x1p+0)], - [F(10), F(0x1.8p+1)], [F(100), F(0x1.8p+2)], - [F(10000), F(0x1.ap+3)], - ]; - testLogb(vals); - }} - { - float[2][16] vals = [ - [float.nan, float.nan],[-float.nan, float.nan], - [float.infinity, float.infinity], [-float.infinity, float.infinity], - [float.min_normal, -0x1.f8p+6f], [-float.min_normal, -0x1.f8p+6f], - [float.max, 0x1.fcp+6f], [-float.max, 0x1.fcp+6f], - [float.min_normal / 2, -0x1.fcp+6f], [-float.min_normal / 2, -0x1.fcp+6f], - [float.max / 2, 0x1.f8p+6f], [-float.max / 2, 0x1.f8p+6f], - [float.min_normal / 3, -0x1p+7f], [-float.min_normal / 3, -0x1p+7f], - [float.max / 3, 0x1.f8p+6f], [-float.max / 3, 0x1.f8p+6f], - ]; - testLogb(vals); - } - { - double[2][16] vals = [ - [double.nan, double.nan],[-double.nan, double.nan], - [double.infinity, double.infinity], [-double.infinity, double.infinity], - [double.min_normal, -0x1.ffp+9], [-double.min_normal, -0x1.ffp+9], - [double.max, 0x1.ff8p+9], [-double.max, 0x1.ff8p+9], - [double.min_normal / 2, -0x1.ff8p+9], [-double.min_normal / 2, -0x1.ff8p+9], - [double.max / 2, 0x1.ffp+9], [-double.max / 2, 0x1.ffp+9], - [double.min_normal / 3, -0x1p+10], [-double.min_normal / 3, -0x1p+10], - [double.max / 3, 0x1.ffp+9], [-double.max / 3, 0x1.ffp+9], - ]; - testLogb(vals); - } - alias F = floatTraits!real; - static if (F.realFormat == RealFormat.ieeeExtended || F.realFormat == RealFormat.ieeeQuadruple) - {{ - real[2][16] vals = [ - [real.nan, real.nan],[-real.nan, real.nan], - [real.infinity, real.infinity], [-real.infinity, real.infinity], - [real.min_normal, -0x1.fffp+13L], [-real.min_normal, -0x1.fffp+13L], - [real.max, 0x1.fff8p+13L], [-real.max, 0x1.fff8p+13L], - [real.min_normal / 2, -0x1.fff8p+13L], [-real.min_normal / 2, -0x1.fff8p+13L], - [real.max / 2, 0x1.fffp+13L], [-real.max / 2, 0x1.fffp+13L], - [real.min_normal / 3, -0x1p+14L], [-real.min_normal / 3, -0x1p+14L], - [real.max / 3, 0x1.fffp+13L], [-real.max / 3, 0x1.fffp+13L], - ]; - testLogb(vals); - }} -} - -/************************************* - * Efficiently calculates x * 2$(SUPERSCRIPT n). - * - * scalbn handles underflow and overflow in - * the same fashion as the basic arithmetic operators. - * - * $(TABLE_SV - * $(TR $(TH x) $(TH scalb(x))) - * $(TR $(TD $(PLUSMNINF)) $(TD $(PLUSMNINF)) ) - * $(TR $(TD $(PLUSMN)0.0) $(TD $(PLUSMN)0.0) ) - * ) - */ -pragma(inline, true) -real scalbn(real x, int n) @safe pure nothrow @nogc { return _scalbn(x,n); } - -/// ditto -pragma(inline, true) -double scalbn(double x, int n) @safe pure nothrow @nogc { return _scalbn(x,n); } - -/// ditto -pragma(inline, true) -float scalbn(float x, int n) @safe pure nothrow @nogc { return _scalbn(x,n); } - -/// -@safe pure nothrow @nogc unittest -{ - assert(scalbn(0x1.2345678abcdefp0L, 999) == 0x1.2345678abcdefp999L); - assert(scalbn(-real.infinity, 5) == -real.infinity); - assert(scalbn(2.0,10) == 2048.0); - assert(scalbn(2048.0f,-10) == 2.0f); -} - -pragma(inline, true) -private F _scalbn(F)(F x, int n) -{ - import std.math.traits : isInfinity; - - if (__ctfe) - { - // Handle special cases. - if (x == F(0.0) || isInfinity(x)) - return x; - } - return core.math.ldexp(x, n); -} - -@safe pure nothrow @nogc unittest -{ - // CTFE-able test - static assert(scalbn(0x1.2345678abcdefp0L, 999) == 0x1.2345678abcdefp999L); - static assert(scalbn(-real.infinity, 5) == -real.infinity); - // Test with large exponent delta n where the result is in bounds but 2.0L ^^ n is not. - enum initialExponent = real.min_exp + 2, resultExponent = real.max_exp - 2; - enum int n = resultExponent - initialExponent; - enum real x = 0x1.2345678abcdefp0L * (2.0L ^^ initialExponent); - enum staticResult = scalbn(x, n); - static assert(staticResult == 0x1.2345678abcdefp0L * (2.0L ^^ resultExponent)); - assert(scalbn(x, n) == staticResult); -} - diff --git a/phobos/std/math/hardware.d b/phobos/std/math/hardware.d deleted file mode 100644 index a9697b8..0000000 --- a/phobos/std/math/hardware.d +++ /dev/null @@ -1,1348 +0,0 @@ -// Written in the D programming language. - -/** -This is a submodule of $(MREF std, math). - -It contains hardware support for floating point numbers. - -Copyright: Copyright The D Language Foundation 2000 - 2011. -License: $(HTTP www.boost.org/LICENSE_1_0.txt, Boost License 1.0). -Authors: $(HTTP digitalmars.com, Walter Bright), Don Clugston, - Conversion of CEPHES math library to D by Iain Buclaw and David Nadlinger -Source: $(PHOBOSSRC std/math/hardware.d) - */ - -module std.math.hardware; - -static import core.stdc.fenv; - -version (LDC) import ldc.attributes; - -version (X86) version = X86_Any; -version (X86_64) version = X86_Any; -version (PPC) version = PPC_Any; -version (PPC64) version = PPC_Any; -version (MIPS32) version = MIPS_Any; -version (MIPS64) version = MIPS_Any; -version (AArch64) version = ARM_Any; -version (ARM) version = ARM_Any; -version (S390) version = IBMZ_Any; -version (SPARC) version = SPARC_Any; -version (SPARC64) version = SPARC_Any; -version (SystemZ) version = IBMZ_Any; -version (RISCV32) version = RISCV_Any; -version (RISCV64) version = RISCV_Any; -version (LoongArch64) version = LoongArch_Any; - -version (D_InlineAsm_X86) version = InlineAsm_X86_Any; -version (D_InlineAsm_X86_64) version = InlineAsm_X86_Any; - -version (X86_64) version = StaticallyHaveSSE; -version (X86) version (OSX) version = StaticallyHaveSSE; - -version (StaticallyHaveSSE) -{ - private enum bool haveSSE = true; -} -else version (X86) -{ - static import core.cpuid; - private alias haveSSE = core.cpuid.sse; -} - -version (D_SoftFloat) -{ - // Some soft float implementations may support IEEE floating flags. - // The implementation here supports hardware flags only and is so currently - // only available for supported targets. -} -else version (X86_Any) version = IeeeFlagsSupport; -else version (PPC_Any) version = IeeeFlagsSupport; -else version (RISCV_Any) version = IeeeFlagsSupport; -else version (MIPS_Any) version = IeeeFlagsSupport; -else version (LoongArch_Any) version = IeeeFlagsSupport; -else version (ARM_Any) version = IeeeFlagsSupport; - -// Struct FloatingPointControl is only available if hardware FP units are available. -version (D_HardFloat) -{ - // FloatingPointControl.clearExceptions() depends on version IeeeFlagsSupport - version (IeeeFlagsSupport) version = FloatingPointControlSupport; -} - -version (IeeeFlagsSupport) -{ - -/** IEEE exception status flags ('sticky bits') - - These flags indicate that an exceptional floating-point condition has occurred. - They indicate that a NaN or an infinity has been generated, that a result - is inexact, or that a signalling NaN has been encountered. If floating-point - exceptions are enabled (unmasked), a hardware exception will be generated - instead of setting these flags. - */ -struct IeeeFlags -{ -nothrow @nogc: - -private: - // The x87 FPU status register is 16 bits. - // The Pentium SSE2 status register is 32 bits. - // The ARM and PowerPC FPSCR is a 32-bit register. - // The SPARC FSR is a 32bit register (64 bits for SPARC 7 & 8, but high bits are uninteresting). - // The RISC-V (32 & 64 bit) fcsr is 32-bit register. - // THe LoongArch fcsr (fcsr0) is a 32-bit register. - uint flags; - - version (CRuntime_Microsoft) - { - // Microsoft uses hardware-incompatible custom constants in fenv.h (core.stdc.fenv). - // Applies to both x87 status word (16 bits) and SSE2 status word(32 bits). - enum : int - { - INEXACT_MASK = 0x20, - UNDERFLOW_MASK = 0x10, - OVERFLOW_MASK = 0x08, - DIVBYZERO_MASK = 0x04, - INVALID_MASK = 0x01, - - EXCEPTIONS_MASK = 0b11_1111 - } - // Don't bother about subnormals, they are not supported on most CPUs. - // SUBNORMAL_MASK = 0x02; - } - else - { - enum : int - { - INEXACT_MASK = core.stdc.fenv.FE_INEXACT, - UNDERFLOW_MASK = core.stdc.fenv.FE_UNDERFLOW, - OVERFLOW_MASK = core.stdc.fenv.FE_OVERFLOW, - DIVBYZERO_MASK = core.stdc.fenv.FE_DIVBYZERO, - INVALID_MASK = core.stdc.fenv.FE_INVALID, - EXCEPTIONS_MASK = core.stdc.fenv.FE_ALL_EXCEPT, - } - } - - static uint getIeeeFlags() @trusted pure - { - version (InlineAsm_X86_Any) - { - ushort sw; - version (LDC) - { - asm pure nothrow @nogc { "fstsw %0" : "=m" (sw); } - } - else - { - asm pure nothrow @nogc { fstsw sw; } - } - - // OR the result with the SSE2 status register (MXCSR). - if (haveSSE) - { - uint mxcsr; - version (LDC) - { - asm pure nothrow @nogc { "stmxcsr %0" : "=m" (mxcsr); } - } - else - { - asm pure nothrow @nogc { stmxcsr mxcsr; } - } - return (sw | mxcsr) & EXCEPTIONS_MASK; - } - else return sw & EXCEPTIONS_MASK; - } - else version (LDC) - { - version (PPC_Any) - { - return FloatingPointControl.getControlState(); - } - else version (MIPS_Any) - { - return FloatingPointControl.getControlState(); - } - else version (AArch64) - { - uint fpsr; - asm pure nothrow @nogc { "mrs %0, FPSR" : "=r" (fpsr); } - return fpsr & 0x1F; - } - else version (ARM) - { - const fpscr = FloatingPointControl.getControlState(); - return fpscr & 0x1F; - } - else version (RISCV_Any) - { - uint result; - asm pure nothrow @nogc { "frflags %0" : "=r" (result); } - return result; - } - else version (LoongArch_Any) - { - uint result; - asm pure nothrow @nogc { "movfcsr2gr %0, $fcsr2" : "=r" (result); } - return result & EXCEPTIONS_MASK; - } - else - assert(0, "Not yet supported"); - } - else version (SPARC) - { - /* - int retval; - asm pure nothrow @nogc { st %fsr, retval; } - return retval; - */ - assert(0, "Not yet supported"); - } - else version (ARM) - { - assert(false, "Not yet supported."); - } - else version (RISCV_Any) - { - uint result = void; - asm pure nothrow @nogc - { - "frflags %0" : "=r" (result); - } - return result; - } - else version (LoongArch_Any) - { - uint result = void; - asm pure nothrow @nogc - { - "movfcsr2gr %0, $fcsr2" : "=r" (result); - } - return result & EXCEPTIONS_MASK; - } - else - assert(0, "Not yet supported"); - } - - static void resetIeeeFlags() @trusted - { - version (InlineAsm_X86_Any) - { - version (LDC) - { - asm nothrow @nogc { "fnclex" : : : "fpsw"; } - } - else - { - asm nothrow @nogc - { - fnclex; - } - } - - // Also clear exception flags in MXCSR, SSE's control register. - if (haveSSE) - { - uint mxcsr; - version (LDC) - { - asm nothrow @nogc { "stmxcsr %0" : "=m" (mxcsr); } - mxcsr &= ~EXCEPTIONS_MASK; - asm nothrow @nogc { "ldmxcsr %0" : : "m" (mxcsr) : "flags"; } - } - else - { - asm nothrow @nogc { stmxcsr mxcsr; } - mxcsr &= ~EXCEPTIONS_MASK; - asm nothrow @nogc { ldmxcsr mxcsr; } - } - } - } - else version (RISCV_Any) - { - uint newValues = 0x0; - asm pure nothrow @nogc - { - "fsflags %0" : : "r" (newValues); - } - } - else version (LoongArch_Any) - { - asm nothrow @nogc - { - "movgr2fcsr $fcsr2,$r0"; - } - } - else version (LDC) - { - version (PPC_Any) - { - asm pure nothrow @nogc - { - `mtfsb0 3 - mtfsb0 4 - mtfsb0 5 - mtfsb0 6 - mtfsb0 7 - mtfsb0 8 - mtfsb0 9 - mtfsb0 10 - mtfsb0 11 - mtfsb0 12`; - } - } - else version (MIPS_Any) - { - version (D_LP64) enum mask = 0xFFFFFF80u; - else enum mask = 0xFF80u; - - const newState = FloatingPointControl.getControlState() & mask; - FloatingPointControl.setControlState(newState); - } - else version (AArch64) - { - uint fpsr; - asm pure nothrow @nogc { "mrs %0, FPSR" : "=r" (fpsr); } - fpsr &= ~0x1F; - asm pure nothrow @nogc { "msr FPSR, %0" : : "r" (fpsr); } - } - else version (ARM) - { - const fpscr = FloatingPointControl.getControlState(); - FloatingPointControl.setControlState(fpscr & ~0x1F); - } - else - assert(0, "Not yet supported"); - } - else - { - /* SPARC: - int tmpval; - asm pure nothrow @nogc { st %fsr, tmpval; } - tmpval &=0xFFFF_FC00; - asm pure nothrow @nogc { ld tmpval, %fsr; } - */ - assert(0, "Not yet supported"); - } - } - -public: - /** - * The result cannot be represented exactly, so rounding occurred. - * Example: `x = sin(0.1);` - */ - @property bool inexact() @safe const { return (flags & INEXACT_MASK) != 0; } - - /** - * A zero was generated by underflow - * Example: `x = real.min*real.epsilon/2;` - */ - @property bool underflow() @safe const { return (flags & UNDERFLOW_MASK) != 0; } - - /** - * An infinity was generated by overflow - * Example: `x = real.max*2;` - */ - @property bool overflow() @safe const { return (flags & OVERFLOW_MASK) != 0; } - - /** - * An infinity was generated by division by zero - * Example: `x = 3/0.0;` - */ - @property bool divByZero() @safe const { return (flags & DIVBYZERO_MASK) != 0; } - - /** - * A machine NaN was generated. - * Example: `x = real.infinity * 0.0;` - */ - @property bool invalid() @safe const { return (flags & INVALID_MASK) != 0; } -} - -/// -version (StdDdoc) -@safe unittest -{ - import std.math.traits : isNaN; - - static void func() { - int a = 10 * 10; - } - real a = 3.5; - // Set all the flags to zero - resetIeeeFlags(); - assert(!ieeeFlags.divByZero); - // Perform a division by zero. - a /= 0.0L; - assert(a == real.infinity); - assert(ieeeFlags.divByZero); - // Create a NaN - a *= 0.0L; - assert(ieeeFlags.invalid); - assert(isNaN(a)); - - // Check that calling func() has no effect on the - // status flags. - IeeeFlags f = ieeeFlags; - func(); - assert(ieeeFlags == f); -} - -@optStrategy("none") // LDC -@safe unittest -{ - import std.math.traits : isNaN; - - static void func() { - int a = 10 * 10; - } - real a = 3.5; - // Set all the flags to zero - resetIeeeFlags(); - assert(!ieeeFlags.divByZero); - // Perform a division by zero. - a = forceDivOp(a, 0.0L); - assert(a == real.infinity); - assert(ieeeFlags.divByZero); - // Create a NaN - a = forceMulOp(a, 0.0L); - assert(ieeeFlags.invalid); - assert(isNaN(a)); - - // Check that calling func() has no effect on the - // status flags. - IeeeFlags f = ieeeFlags; - func(); - assert(ieeeFlags == f); -} - -version (LDC) -{ - unittest - { - pragma(msg, "ieeeFlags test disabled, see LDC Issue #888"); - } -} -else -@safe unittest -{ - import std.meta : AliasSeq; - - static struct Test - { - void delegate() @trusted action; - bool function() @trusted ieeeCheck; - } - - static foreach (T; AliasSeq!(float, double, real)) - {{ - T x; // Needs to be here to avoid `call without side effects` warning. - auto tests = [ - Test( - () { x = forceAddOp!T(1, 0.1L); }, - () => ieeeFlags.inexact - ), - Test( - () { x = forceDivOp!T(T.min_normal, T.max); }, - () => ieeeFlags.underflow - ), - Test( - () { x = forceAddOp!T(T.max, T.max); }, - () => ieeeFlags.overflow - ), - Test( - () { x = forceDivOp!T(1, 0); }, - () => ieeeFlags.divByZero - ), - Test( - () { x = forceDivOp!T(0, 0); }, - () => ieeeFlags.invalid - ) - ]; - foreach (test; tests) - { - resetIeeeFlags(); - assert(!test.ieeeCheck()); - test.action(); - assert(test.ieeeCheck()); - } - }} -} - -/// Set all of the floating-point status flags to false. -void resetIeeeFlags() @trusted nothrow @nogc -{ - IeeeFlags.resetIeeeFlags(); -} - -/// -version (StdDdoc) -@safe unittest -{ - resetIeeeFlags(); - real a = 3.5; - a /= 0.0L; - assert(a == real.infinity); - assert(ieeeFlags.divByZero); - - resetIeeeFlags(); - assert(!ieeeFlags.divByZero); -} - -@optStrategy("none") // LDC, required for the IEEE flags check -@safe unittest -{ - resetIeeeFlags(); - real a = 3.5; - a = forceDivOp(a, 0.0L); - assert(a == real.infinity); - assert(ieeeFlags.divByZero); - - resetIeeeFlags(); - assert(!ieeeFlags.divByZero); -} - -/// Returns: snapshot of the current state of the floating-point status flags -@property IeeeFlags ieeeFlags() @trusted pure nothrow @nogc -{ - return IeeeFlags(IeeeFlags.getIeeeFlags()); -} - -/// -version (StdDdoc) -@safe nothrow unittest -{ - import std.math.traits : isNaN; - - resetIeeeFlags(); - real a = 3.5; - - a /= 0.0L; - assert(a == real.infinity); - assert(ieeeFlags.divByZero); - - a *= 0.0L; - assert(isNaN(a)); - assert(ieeeFlags.invalid); -} - -@optStrategy("none") // LDC, required for the IEEE flags check -@safe nothrow unittest -{ - import std.math.traits : isNaN; - - resetIeeeFlags(); - real a = 3.5; - - a = forceDivOp(a, 0.0L); - assert(a == real.infinity); - assert(ieeeFlags.divByZero); - - a = forceMulOp(a, 0.0L); - assert(isNaN(a)); - assert(ieeeFlags.invalid); -} - -} // IeeeFlagsSupport - - -version (FloatingPointControlSupport) -{ - -/** Control the Floating point hardware - - Change the IEEE754 floating-point rounding mode and the floating-point - hardware exceptions. - - By default, the rounding mode is roundToNearest and all hardware exceptions - are disabled. For most applications, debugging is easier if the $(I division - by zero), $(I overflow), and $(I invalid operation) exceptions are enabled. - These three are combined into a $(I severeExceptions) value for convenience. - Note in particular that if $(I invalidException) is enabled, a hardware trap - will be generated whenever an uninitialized floating-point variable is used. - - All changes are temporary. The previous state is restored at the - end of the scope. - - -Example: ----- -{ - FloatingPointControl fpctrl; - - // Enable hardware exceptions for division by zero, overflow to infinity, - // invalid operations, and uninitialized floating-point variables. - fpctrl.enableExceptions(FloatingPointControl.severeExceptions); - - // This will generate a hardware exception, if x is a - // default-initialized floating point variable: - real x; // Add `= 0` or even `= real.nan` to not throw the exception. - real y = x * 3.0; - - // The exception is only thrown for default-uninitialized NaN-s. - // NaN-s with other payload are valid: - real z = y * real.nan; // ok - - // The set hardware exceptions and rounding modes will be disabled when - // leaving this scope. -} ----- - - */ -struct FloatingPointControl -{ -nothrow @nogc: - - alias RoundingMode = uint; /// - - version (StdDdoc) - { - enum : RoundingMode - { - /** IEEE rounding modes. - * The default mode is roundToNearest. - * - * roundingMask = A mask of all rounding modes. - */ - roundToNearest, - roundDown, /// ditto - roundUp, /// ditto - roundToZero, /// ditto - roundingMask, /// ditto - } - } - else version (CRuntime_Microsoft) - { - // Microsoft uses hardware-incompatible custom constants in fenv.h (core.stdc.fenv). - enum : RoundingMode - { - roundToNearest = 0x0000, - roundDown = 0x0400, - roundUp = 0x0800, - roundToZero = 0x0C00, - roundingMask = roundToNearest | roundDown - | roundUp | roundToZero, - } - } - else - { - enum : RoundingMode - { - roundToNearest = core.stdc.fenv.FE_TONEAREST, - roundDown = core.stdc.fenv.FE_DOWNWARD, - roundUp = core.stdc.fenv.FE_UPWARD, - roundToZero = core.stdc.fenv.FE_TOWARDZERO, - roundingMask = roundToNearest | roundDown - | roundUp | roundToZero, - } - } - - /*** - * Change the floating-point hardware rounding mode - * - * Changing the rounding mode in the middle of a function can interfere - * with optimizations of floating point expressions, as the optimizer assumes - * that the rounding mode does not change. - * It is best to change the rounding mode only at the - * beginning of the function, and keep it until the function returns. - * It is also best to add the line: - * --- - * pragma(inline, false); - * --- - * as the first line of the function so it will not get inlined. - * Params: - * newMode = the new rounding mode - */ - @property void rounding(RoundingMode newMode) @trusted - { - initialize(); - setControlState((getControlState() & (-1 - roundingMask)) | (newMode & roundingMask)); - } - - /// Returns: the currently active rounding mode - @property static RoundingMode rounding() @trusted pure - { - return cast(RoundingMode)(getControlState() & roundingMask); - } - - alias ExceptionMask = uint; /// - - version (StdDdoc) - { - enum : ExceptionMask - { - /** IEEE hardware exceptions. - * By default, all exceptions are masked (disabled). - * - * severeExceptions = The overflow, division by zero, and invalid - * exceptions. - */ - subnormalException, - inexactException, /// ditto - underflowException, /// ditto - overflowException, /// ditto - divByZeroException, /// ditto - invalidException, /// ditto - severeExceptions, /// ditto - allExceptions, /// ditto - } - } - else version (ARM_Any) - { - enum : ExceptionMask - { - subnormalException = 0x8000, - inexactException = 0x1000, - underflowException = 0x0800, - overflowException = 0x0400, - divByZeroException = 0x0200, - invalidException = 0x0100, - severeExceptions = overflowException | divByZeroException - | invalidException, - allExceptions = severeExceptions | underflowException - | inexactException | subnormalException, - } - } - else version (PPC_Any) - { - enum : ExceptionMask - { - inexactException = 0x0008, - divByZeroException = 0x0010, - underflowException = 0x0020, - overflowException = 0x0040, - invalidException = 0x0080, - severeExceptions = overflowException | divByZeroException - | invalidException, - allExceptions = severeExceptions | underflowException - | inexactException, - } - } - else version (RISCV_Any) - { - enum : ExceptionMask - { - inexactException = 0x01, - divByZeroException = 0x08, - underflowException = 0x02, - overflowException = 0x04, - invalidException = 0x10, - severeExceptions = overflowException | divByZeroException - | invalidException, - allExceptions = severeExceptions | underflowException - | inexactException, - } - } - else version (HPPA) - { - enum : ExceptionMask - { - inexactException = 0x01, - underflowException = 0x02, - overflowException = 0x04, - divByZeroException = 0x08, - invalidException = 0x10, - severeExceptions = overflowException | divByZeroException - | invalidException, - allExceptions = severeExceptions | underflowException - | inexactException, - } - } - else version (LoongArch_Any) - { - enum : ExceptionMask - { - inexactException = 0x00, - divByZeroException = 0x01, - overflowException = 0x02, - underflowException = 0x04, - invalidException = 0x08, - severeExceptions = overflowException | divByZeroException - | invalidException, - allExceptions = severeExceptions | underflowException - | inexactException, - } - } - else version (MIPS_Any) - { - enum : ExceptionMask - { - inexactException = 0x0080, - divByZeroException = 0x0400, - overflowException = 0x0200, - underflowException = 0x0100, - invalidException = 0x0800, - severeExceptions = overflowException | divByZeroException - | invalidException, - allExceptions = severeExceptions | underflowException - | inexactException, - } - } - else version (SPARC_Any) - { - enum : ExceptionMask - { - inexactException = 0x0800000, - divByZeroException = 0x1000000, - overflowException = 0x4000000, - underflowException = 0x2000000, - invalidException = 0x8000000, - severeExceptions = overflowException | divByZeroException - | invalidException, - allExceptions = severeExceptions | underflowException - | inexactException, - } - } - else version (IBMZ_Any) - { - enum : ExceptionMask - { - inexactException = 0x08000000, - divByZeroException = 0x40000000, - overflowException = 0x20000000, - underflowException = 0x10000000, - invalidException = 0x80000000, - severeExceptions = overflowException | divByZeroException - | invalidException, - allExceptions = severeExceptions | underflowException - | inexactException, - } - } - else version (X86_Any) - { - enum : ExceptionMask - { - inexactException = 0x20, - underflowException = 0x10, - overflowException = 0x08, - divByZeroException = 0x04, - subnormalException = 0x02, - invalidException = 0x01, - severeExceptions = overflowException | divByZeroException - | invalidException, - allExceptions = severeExceptions | underflowException - | inexactException | subnormalException, - } - } - else - static assert(false, "Not implemented for this architecture"); - - version (ARM_Any) - { - static bool hasExceptionTraps_impl() @safe - { - auto oldState = getControlState(); - // If exceptions are not supported, we set the bit but read it back as zero - // https://sourceware.org/ml/libc-ports/2012-06/msg00091.html - setControlState(oldState | divByZeroException); - immutable result = (getControlState() & allExceptions) != 0; - setControlState(oldState); - return result; - } - } - - /// Returns: true if the current FPU supports exception trapping - @property static bool hasExceptionTraps() @safe pure - { - version (X86_Any) - return true; - else version (PPC_Any) - return true; - else version (MIPS_Any) - return true; - else version (LoongArch_Any) - return true; - else version (ARM_Any) - { - // The hasExceptionTraps_impl function is basically pure, - // as it restores all global state - auto fptr = ( () @trusted => cast(bool function() @safe - pure nothrow @nogc)&hasExceptionTraps_impl)(); - return fptr(); - } - else - assert(0, "Not yet supported"); - } - - /// Enable (unmask) specific hardware exceptions. Multiple exceptions may be ORed together. - void enableExceptions(ExceptionMask exceptions) @trusted - { - assert(hasExceptionTraps); - initialize(); - version (X86_Any) - setControlState(getControlState() & ~(exceptions & allExceptions)); - else - setControlState(getControlState() | (exceptions & allExceptions)); - } - - /// Disable (mask) specific hardware exceptions. Multiple exceptions may be ORed together. - void disableExceptions(ExceptionMask exceptions) @trusted - { - assert(hasExceptionTraps); - initialize(); - version (X86_Any) - setControlState(getControlState() | (exceptions & allExceptions)); - else - setControlState(getControlState() & ~(exceptions & allExceptions)); - } - - /// Returns: the exceptions which are currently enabled (unmasked) - @property static ExceptionMask enabledExceptions() @trusted pure - { - assert(hasExceptionTraps); - version (X86_Any) - return (getControlState() & allExceptions) ^ allExceptions; - else - return (getControlState() & allExceptions); - } - - /// Clear all pending exceptions, then restore the original exception state and rounding mode. - ~this() @trusted - { - clearExceptions(); - if (initialized) - setControlState(savedState); - } - -private: - ControlState savedState; - - bool initialized = false; - - version (ARM_Any) - { - alias ControlState = uint; - } - else version (HPPA) - { - alias ControlState = uint; - } - else version (PPC_Any) - { - alias ControlState = uint; - } - else version (RISCV_Any) - { - alias ControlState = uint; - } - else version (LoongArch_Any) - { - alias ControlState = uint; - } - else version (MIPS_Any) - { - alias ControlState = uint; - } - else version (SPARC_Any) - { - alias ControlState = ulong; - } - else version (IBMZ_Any) - { - alias ControlState = uint; - } - else version (X86_Any) - { - alias ControlState = ushort; - } - else - static assert(false, "Not implemented for this architecture"); - - void initialize() @safe - { - // BUG: This works around the absence of this() constructors. - if (initialized) return; - clearExceptions(); - savedState = getControlState(); - initialized = true; - } - - // Clear all pending exceptions - static void clearExceptions() @safe - { - version (LDC) - { - version (X86_Any) - { - resetIeeeFlags(); - } - else version (PPC_Any) - { - asm pure nothrow @nogc @trusted - { - `mtfsb0 24 - mtfsb0 25 - mtfsb0 26 - mtfsb0 27 - mtfsb0 28`; - } - } - else version (MIPS_Any) - { - version (D_LP64) enum mask = 0xFFFFF07Fu; - else enum mask = 0xF07Fu; - - const cs = getControlState(); - setControlState(cs & mask); - } - else version (ARM_Any) - { - resetIeeeFlags(); - } - else version (LoongArch_Any) - { - resetIeeeFlags(); - } - else - static assert(false, "Not implemented for this architecture"); - } - else version (IeeeFlagsSupport) - resetIeeeFlags(); - else - static assert(false, "Not implemented for this architecture"); - } - - // Read from the control register - package(std.math) static ControlState getControlState() @trusted pure - { - version (LDC) - { - ControlState cont; - - version (X86) - { - asm pure nothrow @nogc - { - `xor %%eax, %%eax - fstcw %0` - : "=m" (cont) - : - : "eax"; - } - } - else version (X86_64) - { - asm pure nothrow @nogc - { - `xor %%rax, %%rax - fstcw %0` - : "=m" (cont) - : - : "rax"; - } - } - else version (PPC_Any) - { - double fspr; - asm pure nothrow @nogc { "mffs %0" : "=f" (fspr); } - cont = cast(ControlState) *cast(ulong*) &fspr; - } - else version (MIPS_Any) - { - asm pure nothrow @nogc - { - `.set noat - cfc1 %0, $31 - .set at` - : "=r" (cont); - } - } - else version (AArch64) - { - asm pure nothrow @nogc { "mrs %0, FPCR" : "=r" (cont); } - } - else version (ARM) - { - asm pure nothrow @nogc { "vmrs %0, FPSCR" : "=r" (cont); } - } - else version (RISCV_Any) - { - asm pure nothrow @nogc { "frcsr %0" : "=r" (cont); } - } - else version (LoongArch_Any) - { - asm pure nothrow @nogc { "movfcsr2gr %0, $fcsr0" : "=r" (cont); } - cont &= (roundingMask | allExceptions); - } - else - assert(0, "Not yet supported"); - - return cont; - } - else version (D_InlineAsm_X86) - { - short cont; - asm pure nothrow @nogc - { - xor EAX, EAX; - fstcw cont; - } - return cont; - } - else version (D_InlineAsm_X86_64) - { - short cont; - asm pure nothrow @nogc - { - xor RAX, RAX; - fstcw cont; - } - return cont; - } - else version (RISCV_Any) - { - ControlState cont; - asm pure nothrow @nogc - { - "frcsr %0" : "=r" (cont); - } - return cont; - } - else version (LoongArch_Any) - { - ControlState cont; - asm pure nothrow @nogc - { - "movfcsr2gr %0, $fcsr0" : "=r" (cont); - } - cont &= (roundingMask | allExceptions); - return cont; - } - else - assert(0, "Not yet supported"); - } - - // Set the control register - package(std.math) static void setControlState(ControlState newState) @trusted - { - version (InlineAsm_X86_Any) - { - version (LDC) - { - asm nothrow @nogc - { - `fclex - fldcw %0` - : - : "m" (newState) - : "fpsw"; - } - } - else - { - asm nothrow @nogc - { - fclex; - fldcw newState; - } - } - - // Also update MXCSR, SSE's control register. - if (haveSSE) - { - uint mxcsr; - version (LDC) - { - asm nothrow @nogc { "stmxcsr %0" : "=m" (mxcsr); } - } - else - { - asm nothrow @nogc { stmxcsr mxcsr; } - } - - /* In the FPU control register, rounding mode is in bits 10 and - 11. In MXCSR it's in bits 13 and 14. */ - mxcsr &= ~(roundingMask << 3); // delete old rounding mode - mxcsr |= (newState & roundingMask) << 3; // write new rounding mode - - /* In the FPU control register, masks are bits 0 through 5. - In MXCSR they're 7 through 12. */ - mxcsr &= ~(allExceptions << 7); // delete old masks - mxcsr |= (newState & allExceptions) << 7; // write new exception masks - - version (LDC) - { - asm nothrow @nogc { "ldmxcsr %0" : : "m" (mxcsr) : "flags"; } - } - else - { - asm nothrow @nogc { ldmxcsr mxcsr; } - } - } - } - else version (RISCV_Any) - { - asm pure nothrow @nogc - { - "fscsr %0" : : "r" (newState); - } - } - else version (LoongArch_Any) - { - asm nothrow @nogc - { - "movgr2fcsr $fcsr0,%0" : - : "r" (newState & (roundingMask | allExceptions)); - } - } - else version (LDC) - { - version (PPC_Any) - { - ulong tmpState = newState; - double fspr = *cast(double*) &tmpState; - asm nothrow @nogc { "mtfsf 0x0f, %0" : : "f" (fspr); } - } - else version (MIPS_Any) - { - asm nothrow @nogc - { - `.set noat - ctc1 %0, $31 - .set at` - : - : "r" (newState); - } - } - else version (AArch64) - { - asm nothrow @nogc { "msr FPCR, %0" : : "r" (newState); } - } - else version (ARM) - { - asm nothrow @nogc { "vmsr FPSCR, %0" : : "r" (newState); } - } - else - assert(0, "Not yet supported"); - } - else - assert(0, "Not yet supported"); - } -} - -/// -@optStrategy("none") // LDC -@safe unittest -{ - import std.math.rounding : lrint; - - FloatingPointControl fpctrl; - - fpctrl.rounding = FloatingPointControl.roundDown; - assert(lrint(1.5) == 1.0); - - fpctrl.rounding = FloatingPointControl.roundUp; - assert(lrint(1.4) == 2.0); - - fpctrl.rounding = FloatingPointControl.roundToNearest; - assert(lrint(1.5) == 2.0); -} - -@safe unittest -{ - void ensureDefaults() - { - assert(FloatingPointControl.rounding - == FloatingPointControl.roundToNearest); - if (FloatingPointControl.hasExceptionTraps) - assert(FloatingPointControl.enabledExceptions == 0); - } - - { - FloatingPointControl ctrl; - } - ensureDefaults(); - - { - FloatingPointControl ctrl; - ctrl.rounding = FloatingPointControl.roundDown; - assert(FloatingPointControl.rounding == FloatingPointControl.roundDown); - } - ensureDefaults(); - - if (FloatingPointControl.hasExceptionTraps) - { - FloatingPointControl ctrl; - ctrl.enableExceptions(FloatingPointControl.divByZeroException - | FloatingPointControl.overflowException); - assert(ctrl.enabledExceptions == - (FloatingPointControl.divByZeroException - | FloatingPointControl.overflowException)); - - ctrl.rounding = FloatingPointControl.roundUp; - assert(FloatingPointControl.rounding == FloatingPointControl.roundUp); - } - ensureDefaults(); -} - -version (LDC) -{ - // TODO: most likely issue #888 again, verify - // Linux x86_64: debug works, release fails - // Win64: debug and release fail -} -else -@safe unittest // rounding -{ - import std.meta : AliasSeq; - - static T addRound(T)(uint rm) - { - pragma(inline, false); - FloatingPointControl fpctrl; - fpctrl.rounding = rm; - T x = 1; - x = forceAddOp(x, 0.1L); - return x; - } - - static T subRound(T)(uint rm) - { - pragma(inline, false); - FloatingPointControl fpctrl; - fpctrl.rounding = rm; - T x = -1; - x = forceSubOp(x, 0.1L); - return x; - } - - static foreach (T; AliasSeq!(float, double, real)) - {{ - /* Be careful with changing the rounding mode, it interferes - * with common subexpressions. Changing rounding modes should - * be done with separate functions that are not inlined. - */ - - { - T u = addRound!(T)(FloatingPointControl.roundUp); - T d = addRound!(T)(FloatingPointControl.roundDown); - T z = addRound!(T)(FloatingPointControl.roundToZero); - - assert(u > d); - assert(z == d); - } - - { - T u = subRound!(T)(FloatingPointControl.roundUp); - T d = subRound!(T)(FloatingPointControl.roundDown); - T z = subRound!(T)(FloatingPointControl.roundToZero); - - assert(u > d); - assert(z == u); - } - }} -} - -} // FloatingPointControlSupport - -version (StdUnittest) -{ - // These helpers are intended to avoid constant propagation by the optimizer. - pragma(inline, false) private @safe - { - T forceAddOp(T)(T x, T y) { return x + y; } - T forceSubOp(T)(T x, T y) { return x - y; } - T forceMulOp(T)(T x, T y) { return x * y; } - T forceDivOp(T)(T x, T y) { return x / y; } - } -} diff --git a/phobos/std/math/operations.d b/phobos/std/math/operations.d deleted file mode 100644 index bd77a8e..0000000 --- a/phobos/std/math/operations.d +++ /dev/null @@ -1,2044 +0,0 @@ -// Written in the D programming language. - -/** -This is a submodule of $(MREF std, math). - -It contains several functions for work with floating point numbers. - -Copyright: Copyright The D Language Foundation 2000 - 2011. -License: $(HTTP www.boost.org/LICENSE_1_0.txt, Boost License 1.0). -Authors: $(HTTP digitalmars.com, Walter Bright), Don Clugston, - Conversion of CEPHES math library to D by Iain Buclaw and David Nadlinger -Source: $(PHOBOSSRC std/math/operations.d) - -Macros: - TABLE_SV = - - $0
Special Values
- SVH = $(TR $(TH $1) $(TH $2)) - SV = $(TR $(TD $1) $(TD $2)) - NAN = $(RED NAN) - PLUSMN = ± - INFIN = ∞ - LT = < - GT = > - */ - -module std.math.operations; - -import std.traits : CommonType, isFloatingPoint, isIntegral, Unqual; - -version (LDC) import ldc.intrinsics; - -// Functions for NaN payloads -/* - * A 'payload' can be stored in the significand of a $(NAN). One bit is required - * to distinguish between a quiet and a signalling $(NAN). This leaves 22 bits - * of payload for a float; 51 bits for a double; 62 bits for an 80-bit real; - * and 111 bits for a 128-bit quad. -*/ -/** - * Create a quiet $(NAN), storing an integer inside the payload. - * - * For floats, the largest possible payload is 0x3F_FFFF. - * For doubles, it is 0x3_FFFF_FFFF_FFFF. - * For 80-bit or 128-bit reals, it is 0x3FFF_FFFF_FFFF_FFFF. - */ -real NaN(ulong payload) @trusted pure nothrow @nogc -{ - import std.math.traits : floatTraits, RealFormat; - - alias F = floatTraits!(real); - static if (F.realFormat == RealFormat.ieeeExtended || - F.realFormat == RealFormat.ieeeExtended53) - { - // real80 (in x86 real format, the implied bit is actually - // not implied but a real bit which is stored in the real) - ulong v = 3; // implied bit = 1, quiet bit = 1 - } - else - { - ulong v = 1; // no implied bit. quiet bit = 1 - } - if (__ctfe) - { - v = 1; // We use a double in CTFE. - assert(payload >>> 51 == 0, - "Cannot set more than 51 bits of NaN payload in CTFE."); - } - - - ulong a = payload; - - // 22 Float bits - ulong w = a & 0x3F_FFFF; - a -= w; - - v <<=22; - v |= w; - a >>=22; - - // 29 Double bits - v <<=29; - w = a & 0xFFF_FFFF; - v |= w; - a -= w; - a >>=29; - - if (__ctfe) - { - v |= 0x7FF0_0000_0000_0000; - return *cast(double*) &v; - } - else static if (F.realFormat == RealFormat.ieeeDouble) - { - v |= 0x7FF0_0000_0000_0000; - real x; - * cast(ulong *)(&x) = v; - return x; - } - else - { - v <<=11; - a &= 0x7FF; - v |= a; - real x = real.nan; - - // Extended real bits - static if (F.realFormat == RealFormat.ieeeQuadruple) - { - v <<= 1; // there's no implicit bit - - version (LittleEndian) - { - *cast(ulong*)(6+cast(ubyte*)(&x)) = v; - } - else - { - *cast(ulong*)(2+cast(ubyte*)(&x)) = v; - } - } - else - { - *cast(ulong *)(&x) = v; - } - return x; - } -} - -/// -@safe @nogc pure nothrow unittest -{ - import std.math.traits : isNaN; - - real a = NaN(1_000_000); - assert(isNaN(a)); - assert(getNaNPayload(a) == 1_000_000); -} - -@system pure nothrow @nogc unittest // not @safe because taking address of local. -{ - import std.math.traits : floatTraits, RealFormat; - - static if (floatTraits!(real).realFormat == RealFormat.ieeeDouble) - { - auto x = NaN(1); - auto xl = *cast(ulong*)&x; - assert(xl & 0x8_0000_0000_0000UL); //non-signaling bit, bit 52 - assert((xl & 0x7FF0_0000_0000_0000UL) == 0x7FF0_0000_0000_0000UL); //all exp bits set - } -} - -/** - * Extract an integral payload from a $(NAN). - * - * Returns: - * the integer payload as a ulong. - * - * For floats, the largest possible payload is 0x3F_FFFF. - * For doubles, it is 0x3_FFFF_FFFF_FFFF. - * For 80-bit or 128-bit reals, it is 0x3FFF_FFFF_FFFF_FFFF. - */ -ulong getNaNPayload(real x) @trusted pure nothrow @nogc -{ - import std.math.traits : floatTraits, RealFormat; - - // assert(isNaN(x)); - alias F = floatTraits!(real); - ulong m = void; - if (__ctfe) - { - double y = x; - m = *cast(ulong*) &y; - // Make it look like an 80-bit significand. - // Skip exponent, and quiet bit - m &= 0x0007_FFFF_FFFF_FFFF; - m <<= 11; - } - else static if (F.realFormat == RealFormat.ieeeDouble) - { - m = *cast(ulong*)(&x); - // Make it look like an 80-bit significand. - // Skip exponent, and quiet bit - m &= 0x0007_FFFF_FFFF_FFFF; - m <<= 11; - } - else static if (F.realFormat == RealFormat.ieeeQuadruple) - { - version (LittleEndian) - { - m = *cast(ulong*)(6+cast(ubyte*)(&x)); - } - else - { - m = *cast(ulong*)(2+cast(ubyte*)(&x)); - } - - m >>= 1; // there's no implicit bit - } - else - { - m = *cast(ulong*)(&x); - } - - // ignore implicit bit and quiet bit - - const ulong f = m & 0x3FFF_FF00_0000_0000L; - - ulong w = f >>> 40; - w |= (m & 0x00FF_FFFF_F800L) << (22 - 11); - w |= (m & 0x7FF) << 51; - return w; -} - -/// -@safe @nogc pure nothrow unittest -{ - import std.math.traits : isNaN; - - real a = NaN(1_000_000); - assert(isNaN(a)); - assert(getNaNPayload(a) == 1_000_000); -} - -@safe @nogc pure nothrow unittest -{ - import std.math.traits : isIdentical, isNaN; - - enum real a = NaN(1_000_000); - static assert(isNaN(a)); - static assert(getNaNPayload(a) == 1_000_000); - real b = NaN(1_000_000); - assert(isIdentical(b, a)); - // The CTFE version of getNaNPayload relies on it being impossible - // for a CTFE-constructed NaN to have more than 51 bits of payload. - enum nanNaN = NaN(getNaNPayload(real.nan)); - assert(isIdentical(real.nan, nanNaN)); - static if (real.init != real.init) - { - enum initNaN = NaN(getNaNPayload(real.init)); - assert(isIdentical(real.init, initNaN)); - } -} - -debug(UnitTest) -{ - @safe pure nothrow @nogc unittest - { - real nan4 = NaN(0x789_ABCD_EF12_3456); - static if (floatTraits!(real).realFormat == RealFormat.ieeeExtended - || floatTraits!(real).realFormat == RealFormat.ieeeQuadruple) - { - assert(getNaNPayload(nan4) == 0x789_ABCD_EF12_3456); - } - else - { - assert(getNaNPayload(nan4) == 0x1_ABCD_EF12_3456); - } - double nan5 = nan4; - assert(getNaNPayload(nan5) == 0x1_ABCD_EF12_3456); - float nan6 = nan4; - assert(getNaNPayload(nan6) == 0x12_3456); - nan4 = NaN(0xFABCD); - assert(getNaNPayload(nan4) == 0xFABCD); - nan6 = nan4; - assert(getNaNPayload(nan6) == 0xFABCD); - nan5 = NaN(0x100_0000_0000_3456); - assert(getNaNPayload(nan5) == 0x0000_0000_3456); - } -} - -/** - * Calculate the next largest floating point value after x. - * - * Return the least number greater than x that is representable as a real; - * thus, it gives the next point on the IEEE number line. - * - * $(TABLE_SV - * $(SVH x, nextUp(x) ) - * $(SV -$(INFIN), -real.max ) - * $(SV $(PLUSMN)0.0, real.min_normal*real.epsilon ) - * $(SV real.max, $(INFIN) ) - * $(SV $(INFIN), $(INFIN) ) - * $(SV $(NAN), $(NAN) ) - * ) - */ -real nextUp(real x) @trusted pure nothrow @nogc -{ - import std.math.traits : floatTraits, RealFormat, MANTISSA_MSB, MANTISSA_LSB; - - alias F = floatTraits!(real); - static if (F.realFormat != RealFormat.ieeeDouble) - { - if (__ctfe) - { - if (x == -real.infinity) - return -real.max; - if (!(x < real.infinity)) // Infinity or NaN. - return x; - real delta; - // Start with a decent estimate of delta. - if (x <= 0x1.ffffffffffffep+1023 && x >= -double.max) - { - const double d = cast(double) x; - delta = (cast(real) nextUp(d) - cast(real) d) * 0x1p-11L; - while (x + (delta * 0x1p-100L) > x) - delta *= 0x1p-100L; - } - else - { - delta = 0x1p960L; - while (!(x + delta > x) && delta < real.max * 0x1p-100L) - delta *= 0x1p100L; - } - if (x + delta > x) - { - while (x + (delta / 2) > x) - delta /= 2; - } - else - { - do { delta += delta; } while (!(x + delta > x)); - } - if (x < 0 && x + delta == 0) - return -0.0L; - return x + delta; - } - } - static if (F.realFormat == RealFormat.ieeeDouble) - { - return nextUp(cast(double) x); - } - else static if (F.realFormat == RealFormat.ieeeQuadruple) - { - ushort e = F.EXPMASK & (cast(ushort *)&x)[F.EXPPOS_SHORT]; - if (e == F.EXPMASK) - { - // NaN or Infinity - if (x == -real.infinity) return -real.max; - return x; // +Inf and NaN are unchanged. - } - - auto ps = cast(ulong *)&x; - if (ps[MANTISSA_MSB] & 0x8000_0000_0000_0000) - { - // Negative number - if (ps[MANTISSA_LSB] == 0 && ps[MANTISSA_MSB] == 0x8000_0000_0000_0000) - { - // it was negative zero, change to smallest subnormal - ps[MANTISSA_LSB] = 1; - ps[MANTISSA_MSB] = 0; - return x; - } - if (ps[MANTISSA_LSB] == 0) --ps[MANTISSA_MSB]; - --ps[MANTISSA_LSB]; - } - else - { - // Positive number - ++ps[MANTISSA_LSB]; - if (ps[MANTISSA_LSB] == 0) ++ps[MANTISSA_MSB]; - } - return x; - } - else static if (F.realFormat == RealFormat.ieeeExtended || - F.realFormat == RealFormat.ieeeExtended53) - { - // For 80-bit reals, the "implied bit" is a nuisance... - ushort *pe = cast(ushort *)&x; - ulong *ps = cast(ulong *)&x; - // EPSILON is 1 for 64-bit, and 2048 for 53-bit precision reals. - enum ulong EPSILON = 2UL ^^ (64 - real.mant_dig); - - if ((pe[F.EXPPOS_SHORT] & F.EXPMASK) == F.EXPMASK) - { - // First, deal with NANs and infinity - if (x == -real.infinity) return -real.max; - return x; // +Inf and NaN are unchanged. - } - if (pe[F.EXPPOS_SHORT] & 0x8000) - { - // Negative number -- need to decrease the significand - *ps -= EPSILON; - // Need to mask with 0x7FFF... so subnormals are treated correctly. - if ((*ps & 0x7FFF_FFFF_FFFF_FFFF) == 0x7FFF_FFFF_FFFF_FFFF) - { - if (pe[F.EXPPOS_SHORT] == 0x8000) // it was negative zero - { - *ps = 1; - pe[F.EXPPOS_SHORT] = 0; // smallest subnormal. - return x; - } - - --pe[F.EXPPOS_SHORT]; - - if (pe[F.EXPPOS_SHORT] == 0x8000) - return x; // it's become a subnormal, implied bit stays low. - - *ps = 0xFFFF_FFFF_FFFF_FFFF; // set the implied bit - return x; - } - return x; - } - else - { - // Positive number -- need to increase the significand. - // Works automatically for positive zero. - *ps += EPSILON; - if ((*ps & 0x7FFF_FFFF_FFFF_FFFF) == 0) - { - // change in exponent - ++pe[F.EXPPOS_SHORT]; - *ps = 0x8000_0000_0000_0000; // set the high bit - } - } - return x; - } - else // static if (F.realFormat == RealFormat.ibmExtended) - { - assert(0, "nextUp not implemented"); - } -} - -/** ditto */ -double nextUp(double x) @trusted pure nothrow @nogc -{ - ulong s = *cast(ulong *)&x; - - if ((s & 0x7FF0_0000_0000_0000) == 0x7FF0_0000_0000_0000) - { - // First, deal with NANs and infinity - if (x == -x.infinity) return -x.max; - return x; // +INF and NAN are unchanged. - } - if (s & 0x8000_0000_0000_0000) // Negative number - { - if (s == 0x8000_0000_0000_0000) // it was negative zero - { - s = 0x0000_0000_0000_0001; // change to smallest subnormal - return *cast(double*) &s; - } - --s; - } - else - { // Positive number - ++s; - } - return *cast(double*) &s; -} - -/** ditto */ -float nextUp(float x) @trusted pure nothrow @nogc -{ - uint s = *cast(uint *)&x; - - if ((s & 0x7F80_0000) == 0x7F80_0000) - { - // First, deal with NANs and infinity - if (x == -x.infinity) return -x.max; - - return x; // +INF and NAN are unchanged. - } - if (s & 0x8000_0000) // Negative number - { - if (s == 0x8000_0000) // it was negative zero - { - s = 0x0000_0001; // change to smallest subnormal - return *cast(float*) &s; - } - - --s; - } - else - { - // Positive number - ++s; - } - return *cast(float*) &s; -} - -/// -@safe @nogc pure nothrow unittest -{ - assert(nextUp(1.0 - 1.0e-6).feqrel(0.999999) > 16); - assert(nextUp(1.0 - real.epsilon).feqrel(1.0) > 16); -} - -/** - * Calculate the next smallest floating point value before x. - * - * Return the greatest number less than x that is representable as a real; - * thus, it gives the previous point on the IEEE number line. - * - * $(TABLE_SV - * $(SVH x, nextDown(x) ) - * $(SV $(INFIN), real.max ) - * $(SV $(PLUSMN)0.0, -real.min_normal*real.epsilon ) - * $(SV -real.max, -$(INFIN) ) - * $(SV -$(INFIN), -$(INFIN) ) - * $(SV $(NAN), $(NAN) ) - * ) - */ -real nextDown(real x) @safe pure nothrow @nogc -{ - return -nextUp(-x); -} - -/** ditto */ -double nextDown(double x) @safe pure nothrow @nogc -{ - return -nextUp(-x); -} - -/** ditto */ -float nextDown(float x) @safe pure nothrow @nogc -{ - return -nextUp(-x); -} - -/// -@safe pure nothrow @nogc unittest -{ - assert( nextDown(1.0 + real.epsilon) == 1.0); -} - -@safe pure nothrow @nogc unittest -{ - import std.math.traits : floatTraits, RealFormat, isIdentical; - - static if (floatTraits!(real).realFormat == RealFormat.ieeeExtended || - floatTraits!(real).realFormat == RealFormat.ieeeDouble || - floatTraits!(real).realFormat == RealFormat.ieeeExtended53 || - floatTraits!(real).realFormat == RealFormat.ieeeQuadruple) - { - // Tests for reals - assert(isIdentical(nextUp(NaN(0xABC)), NaN(0xABC))); - //static assert(isIdentical(nextUp(NaN(0xABC)), NaN(0xABC))); - // negative numbers - assert( nextUp(-real.infinity) == -real.max ); - assert( nextUp(-1.0L-real.epsilon) == -1.0 ); - assert( nextUp(-2.0L) == -2.0 + real.epsilon); - static assert( nextUp(-real.infinity) == -real.max ); - static assert( nextUp(-1.0L-real.epsilon) == -1.0 ); - static assert( nextUp(-2.0L) == -2.0 + real.epsilon); - // subnormals and zero - assert( nextUp(-real.min_normal) == -real.min_normal*(1-real.epsilon) ); - assert( nextUp(-real.min_normal*(1-real.epsilon)) == -real.min_normal*(1-2*real.epsilon) ); - assert( isIdentical(-0.0L, nextUp(-real.min_normal*real.epsilon)) ); - assert( nextUp(-0.0L) == real.min_normal*real.epsilon ); - assert( nextUp(0.0L) == real.min_normal*real.epsilon ); - assert( nextUp(real.min_normal*(1-real.epsilon)) == real.min_normal ); - assert( nextUp(real.min_normal) == real.min_normal*(1+real.epsilon) ); - static assert( nextUp(-real.min_normal) == -real.min_normal*(1-real.epsilon) ); - static assert( nextUp(-real.min_normal*(1-real.epsilon)) == -real.min_normal*(1-2*real.epsilon) ); - static assert( -0.0L is nextUp(-real.min_normal*real.epsilon) ); - static assert( nextUp(-0.0L) == real.min_normal*real.epsilon ); - static assert( nextUp(0.0L) == real.min_normal*real.epsilon ); - static assert( nextUp(real.min_normal*(1-real.epsilon)) == real.min_normal ); - static assert( nextUp(real.min_normal) == real.min_normal*(1+real.epsilon) ); - // positive numbers - assert( nextUp(1.0L) == 1.0 + real.epsilon ); - assert( nextUp(2.0L-real.epsilon) == 2.0 ); - assert( nextUp(real.max) == real.infinity ); - assert( nextUp(real.infinity)==real.infinity ); - static assert( nextUp(1.0L) == 1.0 + real.epsilon ); - static assert( nextUp(2.0L-real.epsilon) == 2.0 ); - static assert( nextUp(real.max) == real.infinity ); - static assert( nextUp(real.infinity)==real.infinity ); - // ctfe near double.max boundary - static assert(nextUp(nextDown(cast(real) double.max)) == cast(real) double.max); - } - - double n = NaN(0xABC); - assert(isIdentical(nextUp(n), n)); - // negative numbers - assert( nextUp(-double.infinity) == -double.max ); - assert( nextUp(-1-double.epsilon) == -1.0 ); - assert( nextUp(-2.0) == -2.0 + double.epsilon); - // subnormals and zero - - assert( nextUp(-double.min_normal) == -double.min_normal*(1-double.epsilon) ); - assert( nextUp(-double.min_normal*(1-double.epsilon)) == -double.min_normal*(1-2*double.epsilon) ); - assert( isIdentical(-0.0, nextUp(-double.min_normal*double.epsilon)) ); - assert( nextUp(0.0) == double.min_normal*double.epsilon ); - assert( nextUp(-0.0) == double.min_normal*double.epsilon ); - assert( nextUp(double.min_normal*(1-double.epsilon)) == double.min_normal ); - assert( nextUp(double.min_normal) == double.min_normal*(1+double.epsilon) ); - // positive numbers - assert( nextUp(1.0) == 1.0 + double.epsilon ); - assert( nextUp(2.0-double.epsilon) == 2.0 ); - assert( nextUp(double.max) == double.infinity ); - - float fn = NaN(0xABC); - assert(isIdentical(nextUp(fn), fn)); - float f = -float.min_normal*(1-float.epsilon); - float f1 = -float.min_normal; - assert( nextUp(f1) == f); - f = 1.0f+float.epsilon; - f1 = 1.0f; - assert( nextUp(f1) == f ); - f1 = -0.0f; - assert( nextUp(f1) == float.min_normal*float.epsilon); - assert( nextUp(float.infinity)==float.infinity ); - - assert(nextDown(1.0L+real.epsilon)==1.0); - assert(nextDown(1.0+double.epsilon)==1.0); - f = 1.0f+float.epsilon; - assert(nextDown(f)==1.0); - assert(nextafter(1.0+real.epsilon, -real.infinity)==1.0); - - // CTFE - - enum double ctfe_n = NaN(0xABC); - //static assert(isIdentical(nextUp(ctfe_n), ctfe_n)); // FIXME: https://issues.dlang.org/show_bug.cgi?id=20197 - static assert(nextUp(double.nan) is double.nan); - // negative numbers - static assert( nextUp(-double.infinity) == -double.max ); - static assert( nextUp(-1-double.epsilon) == -1.0 ); - static assert( nextUp(-2.0) == -2.0 + double.epsilon); - // subnormals and zero - - static assert( nextUp(-double.min_normal) == -double.min_normal*(1-double.epsilon) ); - static assert( nextUp(-double.min_normal*(1-double.epsilon)) == -double.min_normal*(1-2*double.epsilon) ); - static assert( -0.0 is nextUp(-double.min_normal*double.epsilon) ); - static assert( nextUp(0.0) == double.min_normal*double.epsilon ); - static assert( nextUp(-0.0) == double.min_normal*double.epsilon ); - static assert( nextUp(double.min_normal*(1-double.epsilon)) == double.min_normal ); - static assert( nextUp(double.min_normal) == double.min_normal*(1+double.epsilon) ); - // positive numbers - static assert( nextUp(1.0) == 1.0 + double.epsilon ); - static assert( nextUp(2.0-double.epsilon) == 2.0 ); - static assert( nextUp(double.max) == double.infinity ); - - enum float ctfe_fn = NaN(0xABC); - //static assert(isIdentical(nextUp(ctfe_fn), ctfe_fn)); // FIXME: https://issues.dlang.org/show_bug.cgi?id=20197 - static assert(nextUp(float.nan) is float.nan); - static assert(nextUp(-float.min_normal) == -float.min_normal*(1-float.epsilon)); - static assert(nextUp(1.0f) == 1.0f+float.epsilon); - static assert(nextUp(-0.0f) == float.min_normal*float.epsilon); - static assert(nextUp(float.infinity)==float.infinity); - static assert(nextDown(1.0L+real.epsilon)==1.0); - static assert(nextDown(1.0+double.epsilon)==1.0); - static assert(nextDown(1.0f+float.epsilon)==1.0); - static assert(nextafter(1.0+real.epsilon, -real.infinity)==1.0); -} - - - -/****************************************** - * Calculates the next representable value after x in the direction of y. - * - * If y > x, the result will be the next largest floating-point value; - * if y < x, the result will be the next smallest value. - * If x == y, the result is y. - * If x or y is a NaN, the result is a NaN. - * - * Remarks: - * This function is not generally very useful; it's almost always better to use - * the faster functions nextUp() or nextDown() instead. - * - * The FE_INEXACT and FE_OVERFLOW exceptions will be raised if x is finite and - * the function result is infinite. The FE_INEXACT and FE_UNDERFLOW - * exceptions will be raised if the function value is subnormal, and x is - * not equal to y. - */ -T nextafter(T)(const T x, const T y) @safe pure nothrow @nogc -{ - import std.math.traits : isNaN; - - if (x == y || isNaN(y)) - { - return y; - } - - if (isNaN(x)) - { - return x; - } - - return ((y>x) ? nextUp(x) : nextDown(x)); -} - -/// -@safe pure nothrow @nogc unittest -{ - import std.math.traits : isNaN; - - float a = 1; - assert(is(typeof(nextafter(a, a)) == float)); - assert(nextafter(a, a.infinity) > a); - assert(isNaN(nextafter(a, a.nan))); - assert(isNaN(nextafter(a.nan, a))); - - double b = 2; - assert(is(typeof(nextafter(b, b)) == double)); - assert(nextafter(b, b.infinity) > b); - assert(isNaN(nextafter(b, b.nan))); - assert(isNaN(nextafter(b.nan, b))); - - real c = 3; - assert(is(typeof(nextafter(c, c)) == real)); - assert(nextafter(c, c.infinity) > c); - assert(isNaN(nextafter(c, c.nan))); - assert(isNaN(nextafter(c.nan, c))); -} - -@safe pure nothrow @nogc unittest -{ - import std.math.traits : isNaN, signbit; - - // CTFE - enum float a = 1; - static assert(is(typeof(nextafter(a, a)) == float)); - static assert(nextafter(a, a.infinity) > a); - static assert(isNaN(nextafter(a, a.nan))); - static assert(isNaN(nextafter(a.nan, a))); - - enum double b = 2; - static assert(is(typeof(nextafter(b, b)) == double)); - static assert(nextafter(b, b.infinity) > b); - static assert(isNaN(nextafter(b, b.nan))); - static assert(isNaN(nextafter(b.nan, b))); - - enum real c = 3; - static assert(is(typeof(nextafter(c, c)) == real)); - static assert(nextafter(c, c.infinity) > c); - static assert(isNaN(nextafter(c, c.nan))); - static assert(isNaN(nextafter(c.nan, c))); - - enum real negZero = nextafter(+0.0L, -0.0L); - static assert(negZero == -0.0L); - static assert(signbit(negZero)); - - static assert(nextafter(c, c) == c); -} - -//real nexttoward(real x, real y) { return core.stdc.math.nexttowardl(x, y); } - -/** - * Returns the positive difference between x and y. - * - * Equivalent to `fmax(x-y, 0)`. - * - * Returns: - * $(TABLE_SV - * $(TR $(TH x, y) $(TH fdim(x, y))) - * $(TR $(TD x $(GT) y) $(TD x - y)) - * $(TR $(TD x $(LT)= y) $(TD +0.0)) - * ) - */ -real fdim(real x, real y) @safe pure nothrow @nogc -{ - return (x < y) ? +0.0 : x - y; -} - -/// -@safe pure nothrow @nogc unittest -{ - import std.math.traits : isNaN; - - assert(fdim(2.0, 0.0) == 2.0); - assert(fdim(-2.0, 0.0) == 0.0); - assert(fdim(real.infinity, 2.0) == real.infinity); - assert(isNaN(fdim(real.nan, 2.0))); - assert(isNaN(fdim(2.0, real.nan))); - assert(isNaN(fdim(real.nan, real.nan))); -} - -/** - * Returns the larger of `x` and `y`. - * - * If one of the arguments is a `NaN`, the other is returned. - * - * See_Also: $(REF max, std,algorithm,comparison) is faster because it does not perform the `isNaN` test. - */ -pragma(inline, true) // LDC -F fmax(F)(const F x, const F y) @safe pure nothrow @nogc -if (__traits(isFloating, F)) -{ - version (LDC) - { - return llvm_maxnum!F(x, y); - } - else - { - import std.math.traits : isNaN; - - // Do the more predictable test first. Generates 0 branches with ldc and 1 branch with gdc. - // See https://godbolt.org/z/erxrW9 - if (isNaN(x)) return y; - return y > x ? y : x; - } -} - -/// -@safe pure nothrow @nogc unittest -{ - import std.meta : AliasSeq; - static foreach (F; AliasSeq!(float, double, real)) - { - assert(fmax(F(0.0), F(2.0)) == 2.0); - assert(fmax(F(-2.0), 0.0) == F(0.0)); - assert(fmax(F.infinity, F(2.0)) == F.infinity); - assert(fmax(F.nan, F(2.0)) == F(2.0)); - assert(fmax(F(2.0), F.nan) == F(2.0)); - } -} - -/** - * Returns the smaller of `x` and `y`. - * - * If one of the arguments is a `NaN`, the other is returned. - * - * See_Also: $(REF min, std,algorithm,comparison) is faster because it does not perform the `isNaN` test. - */ -pragma(inline, true) // LDC -F fmin(F)(const F x, const F y) @safe pure nothrow @nogc -if (__traits(isFloating, F)) -{ - version (LDC) - { - return llvm_minnum!F(x, y); - } - else - { - import std.math.traits : isNaN; - - // Do the more predictable test first. Generates 0 branches with ldc and 1 branch with gdc. - // See https://godbolt.org/z/erxrW9 - if (isNaN(x)) return y; - return y < x ? y : x; - } -} - -/// -@safe pure nothrow @nogc unittest -{ - import std.meta : AliasSeq; - static foreach (F; AliasSeq!(float, double, real)) - { - assert(fmin(F(0.0), F(2.0)) == 0.0); - assert(fmin(F(-2.0), F(0.0)) == -2.0); - assert(fmin(F.infinity, F(2.0)) == 2.0); - assert(fmin(F.nan, F(2.0)) == 2.0); - assert(fmin(F(2.0), F.nan) == 2.0); - } -} - -/************************************** - * Returns (x * y) + z, rounding only once according to the - * current rounding mode. - * - * BUGS: Not currently implemented - rounds twice. - */ -version (LDC) -{ - pragma(inline, true): - real fma(real x, real y, real z) @safe pure nothrow @nogc { return llvm_fma(x, y, z); } - //double fma(double x, double y, double z) @safe pure nothrow @nogc { return llvm_fma(x, y, z); } - //float fma(float x, float y, float z) @safe pure nothrow @nogc { return llvm_fma(x, y, z); } -} -else -pragma(inline, true) -real fma(real x, real y, real z) @safe pure nothrow @nogc { return (x * y) + z; } - -/// -@safe pure nothrow @nogc unittest -{ - assert(fma(0.0, 2.0, 2.0) == 2.0); - assert(fma(2.0, 2.0, 2.0) == 6.0); - assert(fma(real.infinity, 2.0, 2.0) == real.infinity); - assert(fma(real.nan, 2.0, 2.0) is real.nan); - assert(fma(2.0, 2.0, real.nan) is real.nan); -} - -/************************************** - * To what precision is x equal to y? - * - * Returns: the number of mantissa bits which are equal in x and y. - * eg, 0x1.F8p+60 and 0x1.F1p+60 are equal to 5 bits of precision. - * - * $(TABLE_SV - * $(TR $(TH x) $(TH y) $(TH feqrel(x, y))) - * $(TR $(TD x) $(TD x) $(TD real.mant_dig)) - * $(TR $(TD x) $(TD $(GT)= 2*x) $(TD 0)) - * $(TR $(TD x) $(TD $(LT)= x/2) $(TD 0)) - * $(TR $(TD $(NAN)) $(TD any) $(TD 0)) - * $(TR $(TD any) $(TD $(NAN)) $(TD 0)) - * ) - */ -int feqrel(X)(const X x, const X y) @trusted pure nothrow @nogc -if (isFloatingPoint!(X)) -{ - import std.math.traits : floatTraits, RealFormat; - import core.math : fabs; - - /* Public Domain. Author: Don Clugston, 18 Aug 2005. - */ - alias F = floatTraits!(X); - static if (F.realFormat == RealFormat.ieeeSingle - || F.realFormat == RealFormat.ieeeDouble - || F.realFormat == RealFormat.ieeeExtended - || F.realFormat == RealFormat.ieeeExtended53 - || F.realFormat == RealFormat.ieeeQuadruple) - { - if (x == y) - return X.mant_dig; // ensure diff != 0, cope with INF. - - Unqual!X diff = fabs(x - y); - - ushort *pa = cast(ushort *)(&x); - ushort *pb = cast(ushort *)(&y); - ushort *pd = cast(ushort *)(&diff); - - - // The difference in abs(exponent) between x or y and abs(x-y) - // is equal to the number of significand bits of x which are - // equal to y. If negative, x and y have different exponents. - // If positive, x and y are equal to 'bitsdiff' bits. - // AND with 0x7FFF to form the absolute value. - // To avoid out-by-1 errors, we subtract 1 so it rounds down - // if the exponents were different. This means 'bitsdiff' is - // always 1 lower than we want, except that if bitsdiff == 0, - // they could have 0 or 1 bits in common. - - int bitsdiff = ((( (pa[F.EXPPOS_SHORT] & F.EXPMASK) - + (pb[F.EXPPOS_SHORT] & F.EXPMASK) - - (1 << F.EXPSHIFT)) >> 1) - - (pd[F.EXPPOS_SHORT] & F.EXPMASK)) >> F.EXPSHIFT; - if ( (pd[F.EXPPOS_SHORT] & F.EXPMASK) == 0) - { // Difference is subnormal - // For subnormals, we need to add the number of zeros that - // lie at the start of diff's significand. - // We do this by multiplying by 2^^real.mant_dig - diff *= F.RECIP_EPSILON; - return bitsdiff + X.mant_dig - ((pd[F.EXPPOS_SHORT] & F.EXPMASK) >> F.EXPSHIFT); - } - - if (bitsdiff > 0) - return bitsdiff + 1; // add the 1 we subtracted before - - // Avoid out-by-1 errors when factor is almost 2. - if (bitsdiff == 0 - && ((pa[F.EXPPOS_SHORT] ^ pb[F.EXPPOS_SHORT]) & F.EXPMASK) == 0) - { - return 1; - } else return 0; - } - else - { - static assert(false, "Not implemented for this architecture"); - } -} - -/// -@safe pure unittest -{ - assert(feqrel(2.0, 2.0) == 53); - assert(feqrel(2.0f, 2.0f) == 24); - assert(feqrel(2.0, double.nan) == 0); - - // Test that numbers are within n digits of each - // other by testing if feqrel > n * log2(10) - - // five digits - assert(feqrel(2.0, 2.00001) > 16); - // ten digits - assert(feqrel(2.0, 2.00000000001) > 33); -} - -@safe pure nothrow @nogc unittest -{ - void testFeqrel(F)() - { - // Exact equality - assert(feqrel(F.max, F.max) == F.mant_dig); - assert(feqrel!(F)(0.0, 0.0) == F.mant_dig); - assert(feqrel(F.infinity, F.infinity) == F.mant_dig); - - // a few bits away from exact equality - F w=1; - for (int i = 1; i < F.mant_dig - 1; ++i) - { - assert(feqrel!(F)(1.0 + w * F.epsilon, 1.0) == F.mant_dig-i); - assert(feqrel!(F)(1.0 - w * F.epsilon, 1.0) == F.mant_dig-i); - assert(feqrel!(F)(1.0, 1 + (w-1) * F.epsilon) == F.mant_dig - i + 1); - w*=2; - } - - assert(feqrel!(F)(1.5+F.epsilon, 1.5) == F.mant_dig-1); - assert(feqrel!(F)(1.5-F.epsilon, 1.5) == F.mant_dig-1); - assert(feqrel!(F)(1.5-F.epsilon, 1.5+F.epsilon) == F.mant_dig-2); - - - // Numbers that are close - assert(feqrel!(F)(0x1.Bp+84, 0x1.B8p+84) == 5); - assert(feqrel!(F)(0x1.8p+10, 0x1.Cp+10) == 2); - assert(feqrel!(F)(1.5 * (1 - F.epsilon), 1.0L) == 2); - assert(feqrel!(F)(1.5, 1.0) == 1); - assert(feqrel!(F)(2 * (1 - F.epsilon), 1.0L) == 1); - - // Factors of 2 - assert(feqrel(F.max, F.infinity) == 0); - assert(feqrel!(F)(2 * (1 - F.epsilon), 1.0L) == 1); - assert(feqrel!(F)(1.0, 2.0) == 0); - assert(feqrel!(F)(4.0, 1.0) == 0); - - // Extreme inequality - assert(feqrel(F.nan, F.nan) == 0); - assert(feqrel!(F)(0.0L, -F.nan) == 0); - assert(feqrel(F.nan, F.infinity) == 0); - assert(feqrel(F.infinity, -F.infinity) == 0); - assert(feqrel(F.max, -F.max) == 0); - - assert(feqrel(F.min_normal / 8, F.min_normal / 17) == 3); - - const F Const = 2; - immutable F Immutable = 2; - auto Compiles = feqrel(Const, Immutable); - } - - assert(feqrel(7.1824L, 7.1824L) == real.mant_dig); - - testFeqrel!(real)(); - testFeqrel!(double)(); - testFeqrel!(float)(); -} - -/** - Computes whether a values is approximately equal to a reference value, - admitting a maximum relative difference, and a maximum absolute difference. - - Warning: - This template is considered out-dated. It will be removed from - Phobos in 2.106.0. Please use $(LREF isClose) instead. To achieve - a similar behaviour to `approxEqual(a, b)` use - `isClose(a, b, 1e-2, 1e-5)`. In case of comparing to 0.0, - `isClose(a, b, 0.0, eps)` should be used, where `eps` - represents the accepted deviation from 0.0." - - Params: - value = Value to compare. - reference = Reference value. - maxRelDiff = Maximum allowable difference relative to `reference`. - Setting to 0.0 disables this check. Defaults to `1e-2`. - maxAbsDiff = Maximum absolute difference. This is mainly usefull - for comparing values to zero. Setting to 0.0 disables this check. - Defaults to `1e-5`. - - Returns: - `true` if `value` is approximately equal to `reference` under - either criterium. It is sufficient, when `value ` satisfies - one of the two criteria. - - If one item is a range, and the other is a single value, then - the result is the logical and-ing of calling `approxEqual` on - each element of the ranged item against the single item. If - both items are ranges, then `approxEqual` returns `true` if - and only if the ranges have the same number of elements and if - `approxEqual` evaluates to `true` for each pair of elements. - - See_Also: - Use $(LREF feqrel) to get the number of equal bits in the mantissa. - */ -deprecated("approxEqual will be removed in 2.106.0. Please use isClose instead.") -bool approxEqual(T, U, V)(T value, U reference, V maxRelDiff = 1e-2, V maxAbsDiff = 1e-5) -{ - import core.math : fabs; - import std.range.primitives : empty, front, isInputRange, popFront; - static if (isInputRange!T) - { - static if (isInputRange!U) - { - // Two ranges - for (;; value.popFront(), reference.popFront()) - { - if (value.empty) return reference.empty; - if (reference.empty) return value.empty; - if (!approxEqual(value.front, reference.front, maxRelDiff, maxAbsDiff)) - return false; - } - } - else static if (isIntegral!U) - { - // convert reference to real - return approxEqual(value, real(reference), maxRelDiff, maxAbsDiff); - } - else - { - // value is range, reference is number - for (; !value.empty; value.popFront()) - { - if (!approxEqual(value.front, reference, maxRelDiff, maxAbsDiff)) - return false; - } - return true; - } - } - else - { - static if (isInputRange!U) - { - // value is number, reference is range - for (; !reference.empty; reference.popFront()) - { - if (!approxEqual(value, reference.front, maxRelDiff, maxAbsDiff)) - return false; - } - return true; - } - else static if (isIntegral!T || isIntegral!U) - { - // convert both value and reference to real - return approxEqual(real(value), real(reference), maxRelDiff, maxAbsDiff); - } - else - { - // two numbers - //static assert(is(T : real) && is(U : real)); - if (reference == 0) - { - return fabs(value) <= maxAbsDiff; - } - static if (is(typeof(value.infinity)) && is(typeof(reference.infinity))) - { - if (value == value.infinity && reference == reference.infinity || - value == -value.infinity && reference == -reference.infinity) return true; - } - return fabs((value - reference) / reference) <= maxRelDiff - || maxAbsDiff != 0 && fabs(value - reference) <= maxAbsDiff; - } - } -} - -deprecated @safe pure nothrow unittest -{ - assert(approxEqual(1.0, 1.0099)); - assert(!approxEqual(1.0, 1.011)); - assert(approxEqual(0.00001, 0.0)); - assert(!approxEqual(0.00002, 0.0)); - - assert(approxEqual(3.0, [3, 3.01, 2.99])); // several reference values is strange - assert(approxEqual([3, 3.01, 2.99], 3.0)); // better - - float[] arr1 = [ 1.0, 2.0, 3.0 ]; - double[] arr2 = [ 1.001, 1.999, 3 ]; - assert(approxEqual(arr1, arr2)); -} - -deprecated @safe pure nothrow unittest -{ - // relative comparison depends on reference, make sure proper - // side is used when comparing range to single value. Based on - // https://issues.dlang.org/show_bug.cgi?id=15763 - auto a = [2e-3 - 1e-5]; - auto b = 2e-3 + 1e-5; - assert(a[0].approxEqual(b)); - assert(!b.approxEqual(a[0])); - assert(a.approxEqual(b)); - assert(!b.approxEqual(a)); -} - -deprecated @safe pure nothrow @nogc unittest -{ - assert(!approxEqual(0.0,1e-15,1e-9,0.0)); - assert(approxEqual(0.0,1e-15,1e-9,1e-9)); - assert(!approxEqual(1.0,3.0,0.0,1.0)); - - assert(approxEqual(1.00000000099,1.0,1e-9,0.0)); - assert(!approxEqual(1.0000000011,1.0,1e-9,0.0)); -} - -deprecated @safe pure nothrow @nogc unittest -{ - // maybe unintuitive behavior - assert(approxEqual(1000.0,1010.0)); - assert(approxEqual(9_090_000_000.0,9_000_000_000.0)); - assert(approxEqual(0.0,1e30,1.0)); - assert(approxEqual(0.00001,1e-30)); - assert(!approxEqual(-1e-30,1e-30,1e-2,0.0)); -} - -deprecated @safe pure nothrow @nogc unittest -{ - int a = 10; - assert(approxEqual(10, a)); - - assert(!approxEqual(3, 0)); - assert(approxEqual(3, 3)); - assert(approxEqual(3.0, 3)); - assert(approxEqual(3, 3.0)); - - assert(approxEqual(0.0,0.0)); - assert(approxEqual(-0.0,0.0)); - assert(approxEqual(0.0f,0.0)); -} - -deprecated @safe pure nothrow @nogc unittest -{ - real num = real.infinity; - assert(num == real.infinity); - assert(approxEqual(num, real.infinity)); - num = -real.infinity; - assert(num == -real.infinity); - assert(approxEqual(num, -real.infinity)); - - assert(!approxEqual(1,real.nan)); - assert(!approxEqual(real.nan,real.max)); - assert(!approxEqual(real.nan,real.nan)); -} - -deprecated @safe pure nothrow unittest -{ - assert(!approxEqual([1.0,2.0,3.0],[1.0,2.0])); - assert(!approxEqual([1.0,2.0],[1.0,2.0,3.0])); - - assert(approxEqual!(real[],real[])([],[])); - assert(approxEqual(cast(real[])[],cast(real[])[])); -} - - -/** - Computes whether two values are approximately equal, admitting a maximum - relative difference, and a maximum absolute difference. - - Params: - lhs = First item to compare. - rhs = Second item to compare. - maxRelDiff = Maximum allowable relative difference. - Setting to 0.0 disables this check. Default depends on the type of - `lhs` and `rhs`: It is approximately half the number of decimal digits of - precision of the smaller type. - maxAbsDiff = Maximum absolute difference. This is mainly usefull - for comparing values to zero. Setting to 0.0 disables this check. - Defaults to `0.0`. - - Returns: - `true` if the two items are approximately equal under either criterium. - It is sufficient, when `value ` satisfies one of the two criteria. - - If one item is a range, and the other is a single value, then - the result is the logical and-ing of calling `isClose` on - each element of the ranged item against the single item. If - both items are ranges, then `isClose` returns `true` if - and only if the ranges have the same number of elements and if - `isClose` evaluates to `true` for each pair of elements. - - See_Also: - Use $(LREF feqrel) to get the number of equal bits in the mantissa. - */ -bool isClose(T, U, V = CommonType!(FloatingPointBaseType!T,FloatingPointBaseType!U)) - (T lhs, U rhs, V maxRelDiff = CommonDefaultFor!(T,U), V maxAbsDiff = 0.0) -{ - import std.range.primitives : empty, front, isInputRange, popFront; - import std.complex : Complex; - static if (isInputRange!T) - { - static if (isInputRange!U) - { - // Two ranges - for (;; lhs.popFront(), rhs.popFront()) - { - if (lhs.empty) return rhs.empty; - if (rhs.empty) return lhs.empty; - if (!isClose(lhs.front, rhs.front, maxRelDiff, maxAbsDiff)) - return false; - } - } - else - { - // lhs is range, rhs is number - for (; !lhs.empty; lhs.popFront()) - { - if (!isClose(lhs.front, rhs, maxRelDiff, maxAbsDiff)) - return false; - } - return true; - } - } - else static if (isInputRange!U) - { - // lhs is number, rhs is range - for (; !rhs.empty; rhs.popFront()) - { - if (!isClose(lhs, rhs.front, maxRelDiff, maxAbsDiff)) - return false; - } - return true; - } - else static if (is(T TE == Complex!TE)) - { - static if (is(U UE == Complex!UE)) - { - // Two complex numbers - return isClose(lhs.re, rhs.re, maxRelDiff, maxAbsDiff) - && isClose(lhs.im, rhs.im, maxRelDiff, maxAbsDiff); - } - else - { - // lhs is complex, rhs is number - return isClose(lhs.re, rhs, maxRelDiff, maxAbsDiff) - && isClose(lhs.im, 0.0, maxRelDiff, maxAbsDiff); - } - } - else static if (is(U UE == Complex!UE)) - { - // lhs is number, rhs is complex - return isClose(lhs, rhs.re, maxRelDiff, maxAbsDiff) - && isClose(0.0, rhs.im, maxRelDiff, maxAbsDiff); - } - else - { - // two numbers - if (lhs == rhs) return true; - - static if (is(typeof(lhs.infinity))) - if (lhs == lhs.infinity || lhs == -lhs.infinity) - return false; - static if (is(typeof(rhs.infinity))) - if (rhs == rhs.infinity || rhs == -rhs.infinity) - return false; - - import std.math.algebraic : abs; - - auto diff = abs(lhs - rhs); - - return diff <= maxRelDiff*abs(lhs) - || diff <= maxRelDiff*abs(rhs) - || diff <= maxAbsDiff; - } -} - -/// -@safe pure nothrow @nogc unittest -{ - assert(isClose(1.0,0.999_999_999)); - assert(isClose(0.001, 0.000_999_999_999)); - assert(isClose(1_000_000_000.0,999_999_999.0)); - - assert(isClose(17.123_456_789, 17.123_456_78)); - assert(!isClose(17.123_456_789, 17.123_45)); - - // use explicit 3rd parameter for less (or more) accuracy - assert(isClose(17.123_456_789, 17.123_45, 1e-6)); - assert(!isClose(17.123_456_789, 17.123_45, 1e-7)); - - // use 4th parameter when comparing close to zero - assert(!isClose(1e-100, 0.0)); - assert(isClose(1e-100, 0.0, 0.0, 1e-90)); - assert(!isClose(1e-10, -1e-10)); - assert(isClose(1e-10, -1e-10, 0.0, 1e-9)); - assert(!isClose(1e-300, 1e-298)); - assert(isClose(1e-300, 1e-298, 0.0, 1e-200)); - - // different default limits for different floating point types - assert(isClose(1.0f, 0.999_99f)); - assert(!isClose(1.0, 0.999_99)); - static if (real.sizeof > double.sizeof) - assert(!isClose(1.0L, 0.999_999_999L)); -} - -/// -@safe pure nothrow unittest -{ - assert(isClose([1.0, 2.0, 3.0], [0.999_999_999, 2.000_000_001, 3.0])); - assert(!isClose([1.0, 2.0], [0.999_999_999, 2.000_000_001, 3.0])); - assert(!isClose([1.0, 2.0, 3.0], [0.999_999_999, 2.000_000_001])); - - assert(isClose([2.0, 1.999_999_999, 2.000_000_001], 2.0)); - assert(isClose(2.0, [2.0, 1.999_999_999, 2.000_000_001])); -} - -@safe pure nothrow unittest -{ - assert(!isClose([1.0, 2.0, 3.0], [0.999_999_999, 3.0, 3.0])); - assert(!isClose([2.0, 1.999_999, 2.000_000_001], 2.0)); - assert(!isClose(2.0, [2.0, 1.999_999_999, 2.000_000_999])); -} - -@safe pure nothrow @nogc unittest -{ - immutable a = 1.00001f; - const b = 1.000019; - assert(isClose(a,b)); - - assert(isClose(1.00001f,1.000019f)); - assert(isClose(1.00001f,1.000019)); - assert(isClose(1.00001,1.000019f)); - assert(!isClose(1.00001,1.000019)); - - real a1 = 1e-300L; - real a2 = a1.nextUp; - assert(isClose(a1,a2)); -} - -@safe pure nothrow unittest -{ - float[] arr1 = [ 1.0, 2.0, 3.0 ]; - double[] arr2 = [ 1.00001, 1.99999, 3 ]; - assert(isClose(arr1, arr2)); -} - -@safe pure nothrow @nogc unittest -{ - assert(!isClose(1000.0,1010.0)); - assert(!isClose(9_090_000_000.0,9_000_000_000.0)); - assert(isClose(0.0,1e30,1.0)); - assert(!isClose(0.00001,1e-30)); - assert(!isClose(-1e-30,1e-30,1e-2,0.0)); -} - -@safe pure nothrow @nogc unittest -{ - assert(!isClose(3, 0)); - assert(isClose(3, 3)); - assert(isClose(3.0, 3)); - assert(isClose(3, 3.0)); - - assert(isClose(0.0,0.0)); - assert(isClose(-0.0,0.0)); - assert(isClose(0.0f,0.0)); -} - -@safe pure nothrow @nogc unittest -{ - real num = real.infinity; - assert(num == real.infinity); - assert(isClose(num, real.infinity)); - num = -real.infinity; - assert(num == -real.infinity); - assert(isClose(num, -real.infinity)); - - assert(!isClose(1,real.nan)); - assert(!isClose(real.nan,real.max)); - assert(!isClose(real.nan,real.nan)); - - assert(!isClose(-double.infinity, 1)); -} - -@safe pure nothrow @nogc unittest -{ - assert(isClose!(real[],real[],real)([],[])); - assert(isClose(cast(real[])[],cast(real[])[])); -} - -@safe pure nothrow @nogc unittest -{ - import std.conv : to; - - float f = 31.79f; - double d = 31.79; - double f2d = f.to!double; - - assert(isClose(f,f2d)); - assert(!isClose(d,f2d)); -} - -@safe pure nothrow @nogc unittest -{ - import std.conv : to; - - double d = 31.79; - float f = d.to!float; - double f2d = f.to!double; - - assert(isClose(f,f2d)); - assert(!isClose(d,f2d)); - assert(isClose(d,f2d,1e-4)); -} - -package(std.math) template CommonDefaultFor(T,U) -{ - import std.algorithm.comparison : min; - - alias baseT = FloatingPointBaseType!T; - alias baseU = FloatingPointBaseType!U; - - enum CommonType!(baseT, baseU) CommonDefaultFor = 10.0L ^^ -((min(baseT.dig, baseU.dig) + 1) / 2 + 1); -} - -private template FloatingPointBaseType(T) -{ - import std.range.primitives : ElementType; - static if (isFloatingPoint!T) - { - alias FloatingPointBaseType = Unqual!T; - } - else static if (isFloatingPoint!(ElementType!(Unqual!T))) - { - alias FloatingPointBaseType = Unqual!(ElementType!(Unqual!T)); - } - else - { - alias FloatingPointBaseType = real; - } -} - -/*********************************** - * Defines a total order on all floating-point numbers. - * - * The order is defined as follows: - * $(UL - * $(LI All numbers in [-$(INFIN), +$(INFIN)] are ordered - * the same way as by built-in comparison, with the exception of - * -0.0, which is less than +0.0;) - * $(LI If the sign bit is set (that is, it's 'negative'), $(NAN) is less - * than any number; if the sign bit is not set (it is 'positive'), - * $(NAN) is greater than any number;) - * $(LI $(NAN)s of the same sign are ordered by the payload ('negative' - * ones - in reverse order).) - * ) - * - * Returns: - * negative value if `x` precedes `y` in the order specified above; - * 0 if `x` and `y` are identical, and positive value otherwise. - * - * See_Also: - * $(MYREF isIdentical) - * Standards: Conforms to IEEE 754-2008 - */ -int cmp(T)(const(T) x, const(T) y) @nogc @trusted pure nothrow -if (isFloatingPoint!T) -{ - import std.math.traits : floatTraits, RealFormat; - - alias F = floatTraits!T; - - static if (F.realFormat == RealFormat.ieeeSingle - || F.realFormat == RealFormat.ieeeDouble) - { - static if (T.sizeof == 4) - alias UInt = uint; - else - alias UInt = ulong; - - union Repainter - { - T number; - UInt bits; - } - - enum msb = ~(UInt.max >>> 1); - - import std.typecons : Tuple; - Tuple!(Repainter, Repainter) vars = void; - vars[0].number = x; - vars[1].number = y; - - foreach (ref var; vars) - if (var.bits & msb) - var.bits = ~var.bits; - else - var.bits |= msb; - - if (vars[0].bits < vars[1].bits) - return -1; - else if (vars[0].bits > vars[1].bits) - return 1; - else - return 0; - } - else static if (F.realFormat == RealFormat.ieeeExtended53 - || F.realFormat == RealFormat.ieeeExtended - || F.realFormat == RealFormat.ieeeQuadruple) - { - static if (F.realFormat == RealFormat.ieeeQuadruple) - alias RemT = ulong; - else - alias RemT = ushort; - - struct Bits - { - ulong bulk; - RemT rem; - } - - union Repainter - { - T number; - Bits bits; - ubyte[T.sizeof] bytes; - } - - import std.typecons : Tuple; - Tuple!(Repainter, Repainter) vars = void; - vars[0].number = x; - vars[1].number = y; - - foreach (ref var; vars) - if (var.bytes[F.SIGNPOS_BYTE] & 0x80) - { - var.bits.bulk = ~var.bits.bulk; - var.bits.rem = cast(typeof(var.bits.rem))(-1 - var.bits.rem); // ~var.bits.rem - } - else - { - var.bytes[F.SIGNPOS_BYTE] |= 0x80; - } - - version (LittleEndian) - { - if (vars[0].bits.rem < vars[1].bits.rem) - return -1; - else if (vars[0].bits.rem > vars[1].bits.rem) - return 1; - else if (vars[0].bits.bulk < vars[1].bits.bulk) - return -1; - else if (vars[0].bits.bulk > vars[1].bits.bulk) - return 1; - else - return 0; - } - else - { - if (vars[0].bits.bulk < vars[1].bits.bulk) - return -1; - else if (vars[0].bits.bulk > vars[1].bits.bulk) - return 1; - else if (vars[0].bits.rem < vars[1].bits.rem) - return -1; - else if (vars[0].bits.rem > vars[1].bits.rem) - return 1; - else - return 0; - } - } - else - { - // IBM Extended doubledouble does not follow the general - // sign-exponent-significand layout, so has to be handled generically - - import std.math.traits : signbit, isNaN; - - const int xSign = signbit(x), - ySign = signbit(y); - - if (xSign == 1 && ySign == 1) - return cmp(-y, -x); - else if (xSign == 1) - return -1; - else if (ySign == 1) - return 1; - else if (x < y) - return -1; - else if (x == y) - return 0; - else if (x > y) - return 1; - else if (isNaN(x) && !isNaN(y)) - return 1; - else if (isNaN(y) && !isNaN(x)) - return -1; - else if (getNaNPayload(x) < getNaNPayload(y)) - return -1; - else if (getNaNPayload(x) > getNaNPayload(y)) - return 1; - else - return 0; - } -} - -/// Most numbers are ordered naturally. -@safe unittest -{ - assert(cmp(-double.infinity, -double.max) < 0); - assert(cmp(-double.max, -100.0) < 0); - assert(cmp(-100.0, -0.5) < 0); - assert(cmp(-0.5, 0.0) < 0); - assert(cmp(0.0, 0.5) < 0); - assert(cmp(0.5, 100.0) < 0); - assert(cmp(100.0, double.max) < 0); - assert(cmp(double.max, double.infinity) < 0); - - assert(cmp(1.0, 1.0) == 0); -} - -/// Positive and negative zeroes are distinct. -@safe unittest -{ - assert(cmp(-0.0, +0.0) < 0); - assert(cmp(+0.0, -0.0) > 0); -} - -/// Depending on the sign, $(NAN)s go to either end of the spectrum. -@safe unittest -{ - assert(cmp(-double.nan, -double.infinity) < 0); - assert(cmp(double.infinity, double.nan) < 0); - assert(cmp(-double.nan, double.nan) < 0); -} - -version (LDC) version (Win32) version = LDC_Win32; - -/// $(NAN)s of the same sign are ordered by the payload. -@safe unittest -{ - assert(cmp(NaN(10), NaN(20)) < 0); - version (LDC_Win32) - { - // somehow fails with LLVM 8.0 + disabled optimizations - } - else - { - assert(cmp(-NaN(20), -NaN(10)) < 0); - } -} - -@safe unittest -{ - import std.meta : AliasSeq; - static foreach (T; AliasSeq!(float, double, real)) - {{ - T[] values = [-cast(T) NaN(20), -cast(T) NaN(10), -T.nan, -T.infinity, - -T.max, -T.max / 2, T(-16.0), T(-1.0).nextDown, - T(-1.0), T(-1.0).nextUp, - T(-0.5), -T.min_normal, (-T.min_normal).nextUp, - -2 * T.min_normal * T.epsilon, - -T.min_normal * T.epsilon, - T(-0.0), T(0.0), - T.min_normal * T.epsilon, - 2 * T.min_normal * T.epsilon, - T.min_normal.nextDown, T.min_normal, T(0.5), - T(1.0).nextDown, T(1.0), - T(1.0).nextUp, T(16.0), T.max / 2, T.max, - T.infinity, T.nan, cast(T) NaN(10), cast(T) NaN(20)]; - - foreach (i, x; values) - { - foreach (y; values[i + 1 .. $]) - { - version (LDC_Win32) - { - // LLVM 8.0 + disabled optimizations: - // failures for 64-bit negated NaNs with custom payload - static if (T.sizeof == 8) - if (i < 2) - continue; - } - assert(cmp(x, y) < 0); - assert(cmp(y, x) > 0); - } - assert(cmp(x, x) == 0); - } - }} -} - -package(std): // not yet public - -struct FloatingPointBitpattern(T) -if (isFloatingPoint!T) -{ - static if (T.mant_dig <= 64) - { - ulong mantissa; - } - else - { - ulong mantissa_lsb; - ulong mantissa_msb; - } - - int exponent; - bool negative; -} - -FloatingPointBitpattern!T extractBitpattern(T)(const(T) value) @trusted -if (isFloatingPoint!T) -{ - import std.math.traits : floatTraits, RealFormat; - - T val = value; - FloatingPointBitpattern!T ret; - - alias F = floatTraits!T; - static if (F.realFormat == RealFormat.ieeeExtended) - { - if (__ctfe) - { - import core.math : fabs, ldexp; - import std.math.rounding : floor; - import std.math.traits : isInfinity, isNaN, signbit; - import std.math.exponential : log2; - - if (isNaN(val) || isInfinity(val)) - ret.exponent = 32767; - else if (fabs(val) < real.min_normal) - ret.exponent = 0; - else if (fabs(val) >= nextUp(real.max / 2)) - ret.exponent = 32766; - else - ret.exponent = cast(int) (val.fabs.log2.floor() + 16383); - - if (ret.exponent == 32767) - { - // NaN or infinity - ret.mantissa = isNaN(val) ? ((1L << 63) - 1) : 0; - } - else - { - auto delta = 16382 + 64 // bias + bits of ulong - - (ret.exponent == 0 ? 1 : ret.exponent); // -1 in case of subnormals - val = ldexp(val, delta); // val *= 2^^delta - - ulong tmp = cast(ulong) fabs(val); - if (ret.exponent != 32767 && ret.exponent > 0 && tmp <= ulong.max / 2) - { - // correction, due to log2(val) being rounded up: - ret.exponent--; - val *= 2; - tmp = cast(ulong) fabs(val); - } - - ret.mantissa = tmp & long.max; - } - - ret.negative = (signbit(val) == 1); - } - else - { - ushort* vs = cast(ushort*) &val; - ret.mantissa = (cast(ulong*) vs)[0] & long.max; - ret.exponent = vs[4] & short.max; - ret.negative = (vs[4] >> 15) & 1; - } - } - else - { - static if (F.realFormat == RealFormat.ieeeSingle) - { - ulong ival = *cast(uint*) &val; - } - else static if (F.realFormat == RealFormat.ieeeDouble) - { - ulong ival = *cast(ulong*) &val; - } - else - { - static assert(false, "Floating point type `" ~ F.realFormat ~ "` not supported."); - } - - import std.math.exponential : log2; - enum log2_max_exp = cast(int) log2(T(T.max_exp)); - - ret.mantissa = ival & ((1L << (T.mant_dig - 1)) - 1); - ret.exponent = (ival >> (T.mant_dig - 1)) & ((1L << (log2_max_exp + 1)) - 1); - ret.negative = (ival >> (T.mant_dig + log2_max_exp)) & 1; - } - - // add leading 1 for normalized values and correct exponent for denormalied values - if (ret.exponent != 0 && ret.exponent != 2 * T.max_exp - 1) - ret.mantissa |= 1L << (T.mant_dig - 1); - else if (ret.exponent == 0) - ret.exponent = 1; - - ret.exponent -= T.max_exp - 1; - - return ret; -} - -@safe pure unittest -{ - float f = 1.0f; - auto bp = extractBitpattern(f); - assert(bp.mantissa == 0x80_0000); - assert(bp.exponent == 0); - assert(bp.negative == false); - - f = float.max; - bp = extractBitpattern(f); - assert(bp.mantissa == 0xff_ffff); - assert(bp.exponent == 127); - assert(bp.negative == false); - - f = -1.5432e-17f; - bp = extractBitpattern(f); - assert(bp.mantissa == 0x8e_55c8); - assert(bp.exponent == -56); - assert(bp.negative == true); - - // using double literal due to https://issues.dlang.org/show_bug.cgi?id=20361 - f = 2.3822073893521890206e-44; - bp = extractBitpattern(f); - assert(bp.mantissa == 0x00_0011); - assert(bp.exponent == -126); - assert(bp.negative == false); - - f = -float.infinity; - bp = extractBitpattern(f); - assert(bp.mantissa == 0); - assert(bp.exponent == 128); - assert(bp.negative == true); - - f = float.nan; - bp = extractBitpattern(f); - assert(bp.mantissa != 0); // we don't guarantee payloads - assert(bp.exponent == 128); - assert(bp.negative == false); -} - -@safe pure unittest -{ - double d = 1.0; - auto bp = extractBitpattern(d); - assert(bp.mantissa == 0x10_0000_0000_0000L); - assert(bp.exponent == 0); - assert(bp.negative == false); - - d = double.max; - bp = extractBitpattern(d); - assert(bp.mantissa == 0x1f_ffff_ffff_ffffL); - assert(bp.exponent == 1023); - assert(bp.negative == false); - - d = -1.5432e-222; - bp = extractBitpattern(d); - assert(bp.mantissa == 0x11_d9b6_a401_3b04L); - assert(bp.exponent == -737); - assert(bp.negative == true); - - d = 0.0.nextUp; - bp = extractBitpattern(d); - assert(bp.mantissa == 0x00_0000_0000_0001L); - assert(bp.exponent == -1022); - assert(bp.negative == false); - - d = -double.infinity; - bp = extractBitpattern(d); - assert(bp.mantissa == 0); - assert(bp.exponent == 1024); - assert(bp.negative == true); - - d = double.nan; - bp = extractBitpattern(d); - assert(bp.mantissa != 0); // we don't guarantee payloads - assert(bp.exponent == 1024); - assert(bp.negative == false); -} - -@safe pure unittest -{ - import std.math.traits : floatTraits, RealFormat; - - alias F = floatTraits!real; - static if (F.realFormat == RealFormat.ieeeExtended) - { - real r = 1.0L; - auto bp = extractBitpattern(r); - assert(bp.mantissa == 0x8000_0000_0000_0000L); - assert(bp.exponent == 0); - assert(bp.negative == false); - - r = real.max; - bp = extractBitpattern(r); - assert(bp.mantissa == 0xffff_ffff_ffff_ffffL); - assert(bp.exponent == 16383); - assert(bp.negative == false); - - r = -1.5432e-3333L; - bp = extractBitpattern(r); - assert(bp.mantissa == 0xc768_a2c7_a616_cc22L); - assert(bp.exponent == -11072); - assert(bp.negative == true); - - r = 0.0L.nextUp; - bp = extractBitpattern(r); - assert(bp.mantissa == 0x0000_0000_0000_0001L); - assert(bp.exponent == -16382); - assert(bp.negative == false); - - r = -float.infinity; - bp = extractBitpattern(r); - assert(bp.mantissa == 0); - assert(bp.exponent == 16384); - assert(bp.negative == true); - - r = float.nan; - bp = extractBitpattern(r); - assert(bp.mantissa != 0); // we don't guarantee payloads - assert(bp.exponent == 16384); - assert(bp.negative == false); - - r = nextDown(0x1p+16383L); - bp = extractBitpattern(r); - assert(bp.mantissa == 0xffff_ffff_ffff_ffffL); - assert(bp.exponent == 16382); - assert(bp.negative == false); - } -} - -@safe pure unittest -{ - import std.math.traits : floatTraits, RealFormat; - import std.math.exponential : log2; - - alias F = floatTraits!real; - - // log2 is broken for x87-reals on some computers in CTFE - // the following test excludes these computers from the test - // (https://issues.dlang.org/show_bug.cgi?id=21757) - enum test = cast(int) log2(3.05e2312L); - static if (F.realFormat == RealFormat.ieeeExtended && test == 7681) - { - enum r1 = 1.0L; - enum bp1 = extractBitpattern(r1); - static assert(bp1.mantissa == 0x8000_0000_0000_0000L); - static assert(bp1.exponent == 0); - static assert(bp1.negative == false); - - enum r2 = real.max; - enum bp2 = extractBitpattern(r2); - static assert(bp2.mantissa == 0xffff_ffff_ffff_ffffL); - static assert(bp2.exponent == 16383); - static assert(bp2.negative == false); - - enum r3 = -1.5432e-3333L; - enum bp3 = extractBitpattern(r3); - static assert(bp3.mantissa == 0xc768_a2c7_a616_cc22L); - static assert(bp3.exponent == -11072); - static assert(bp3.negative == true); - - enum r4 = 0.0L.nextUp; - enum bp4 = extractBitpattern(r4); - static assert(bp4.mantissa == 0x0000_0000_0000_0001L); - static assert(bp4.exponent == -16382); - static assert(bp4.negative == false); - - enum r5 = -real.infinity; - enum bp5 = extractBitpattern(r5); - static assert(bp5.mantissa == 0); - static assert(bp5.exponent == 16384); - static assert(bp5.negative == true); - - enum r6 = real.nan; - enum bp6 = extractBitpattern(r6); - static assert(bp6.mantissa != 0); // we don't guarantee payloads - static assert(bp6.exponent == 16384); - static assert(bp6.negative == false); - - enum r7 = nextDown(0x1p+16383L); - enum bp7 = extractBitpattern(r7); - static assert(bp7.mantissa == 0xffff_ffff_ffff_ffffL); - static assert(bp7.exponent == 16382); - static assert(bp7.negative == false); - } -} diff --git a/phobos/std/math/package.d b/phobos/std/math/package.d deleted file mode 100644 index 0d1ecc3..0000000 --- a/phobos/std/math/package.d +++ /dev/null @@ -1,323 +0,0 @@ -// Written in the D programming language. - -/** - * Contains the elementary mathematical functions (powers, roots, - * and trigonometric functions), and low-level floating-point operations. - * Mathematical special functions are available in $(MREF std, mathspecial). - * -$(SCRIPT inhibitQuickIndex = 1;) - -$(DIVC quickindex, -$(BOOKTABLE , -$(TR $(TH Category) $(TH Members) ) -$(TR $(TDNW $(SUBMODULE Constants, constants)) $(TD - $(SUBREF constants, E) - $(SUBREF constants, PI) - $(SUBREF constants, PI_2) - $(SUBREF constants, PI_4) - $(SUBREF constants, M_1_PI) - $(SUBREF constants, M_2_PI) - $(SUBREF constants, M_2_SQRTPI) - $(SUBREF constants, LN10) - $(SUBREF constants, LN2) - $(SUBREF constants, LOG2) - $(SUBREF constants, LOG2E) - $(SUBREF constants, LOG2T) - $(SUBREF constants, LOG10E) - $(SUBREF constants, SQRT2) - $(SUBREF constants, SQRT1_2) -)) -$(TR $(TDNW $(SUBMODULE Algebraic, algebraic)) $(TD - $(SUBREF algebraic, abs) - $(SUBREF algebraic, fabs) - $(SUBREF algebraic, sqrt) - $(SUBREF algebraic, cbrt) - $(SUBREF algebraic, hypot) - $(SUBREF algebraic, poly) - $(SUBREF algebraic, nextPow2) - $(SUBREF algebraic, truncPow2) -)) -$(TR $(TDNW $(SUBMODULE Trigonometry, trigonometry)) $(TD - $(SUBREF trigonometry, sin) - $(SUBREF trigonometry, cos) - $(SUBREF trigonometry, tan) - $(SUBREF trigonometry, asin) - $(SUBREF trigonometry, acos) - $(SUBREF trigonometry, atan) - $(SUBREF trigonometry, atan2) - $(SUBREF trigonometry, sinh) - $(SUBREF trigonometry, cosh) - $(SUBREF trigonometry, tanh) - $(SUBREF trigonometry, asinh) - $(SUBREF trigonometry, acosh) - $(SUBREF trigonometry, atanh) -)) -$(TR $(TDNW $(SUBMODULE Rounding, rounding)) $(TD - $(SUBREF rounding, ceil) - $(SUBREF rounding, floor) - $(SUBREF rounding, round) - $(SUBREF rounding, lround) - $(SUBREF rounding, trunc) - $(SUBREF rounding, rint) - $(SUBREF rounding, lrint) - $(SUBREF rounding, nearbyint) - $(SUBREF rounding, rndtol) - $(SUBREF rounding, quantize) -)) -$(TR $(TDNW $(SUBMODULE Exponentiation & Logarithms, exponential)) $(TD - $(SUBREF exponential, pow) - $(SUBREF exponential, powmod) - $(SUBREF exponential, exp) - $(SUBREF exponential, exp2) - $(SUBREF exponential, expm1) - $(SUBREF exponential, ldexp) - $(SUBREF exponential, frexp) - $(SUBREF exponential, log) - $(SUBREF exponential, log2) - $(SUBREF exponential, log10) - $(SUBREF exponential, logb) - $(SUBREF exponential, ilogb) - $(SUBREF exponential, log1p) - $(SUBREF exponential, scalbn) -)) -$(TR $(TDNW $(SUBMODULE Remainder, remainder)) $(TD - $(SUBREF remainder, fmod) - $(SUBREF remainder, modf) - $(SUBREF remainder, remainder) - $(SUBREF remainder, remquo) -)) -$(TR $(TDNW $(SUBMODULE Floating-point operations, operations)) $(TD - $(SUBREF operations, approxEqual) - $(SUBREF operations, feqrel) - $(SUBREF operations, fdim) - $(SUBREF operations, fmax) - $(SUBREF operations, fmin) - $(SUBREF operations, fma) - $(SUBREF operations, isClose) - $(SUBREF operations, nextDown) - $(SUBREF operations, nextUp) - $(SUBREF operations, nextafter) - $(SUBREF operations, NaN) - $(SUBREF operations, getNaNPayload) - $(SUBREF operations, cmp) -)) -$(TR $(TDNW $(SUBMODULE Introspection, traits)) $(TD - $(SUBREF traits, isFinite) - $(SUBREF traits, isIdentical) - $(SUBREF traits, isInfinity) - $(SUBREF traits, isNaN) - $(SUBREF traits, isNormal) - $(SUBREF traits, isSubnormal) - $(SUBREF traits, signbit) - $(SUBREF traits, sgn) - $(SUBREF traits, copysign) - $(SUBREF traits, isPowerOf2) -)) -$(TR $(TDNW $(SUBMODULE Hardware Control, hardware)) $(TD - $(SUBREF hardware, IeeeFlags) - $(SUBREF hardware, ieeeFlags) - $(SUBREF hardware, resetIeeeFlags) - $(SUBREF hardware, FloatingPointControl) -)) -) -) - - * The functionality closely follows the IEEE754-2008 standard for - * floating-point arithmetic, including the use of camelCase names rather - * than C99-style lower case names. All of these functions behave correctly - * when presented with an infinity or NaN. - * - * The following IEEE 'real' formats are currently supported: - * $(UL - * $(LI 64 bit Big-endian 'double' (eg PowerPC)) - * $(LI 128 bit Big-endian 'quadruple' (eg SPARC)) - * $(LI 64 bit Little-endian 'double' (eg x86-SSE2)) - * $(LI 80 bit Little-endian, with implied bit 'real80' (eg x87, Itanium)) - * $(LI 128 bit Little-endian 'quadruple' (not implemented on any known processor!)) - * $(LI Non-IEEE 128 bit Big-endian 'doubledouble' (eg PowerPC) has partial support) - * ) - * Unlike C, there is no global 'errno' variable. Consequently, almost all of - * these functions are pure nothrow. - * - * Macros: - * SUBMODULE = $(MREF_ALTTEXT $1, std, math, $2) - * SUBREF = $(REF_ALTTEXT $(TT $2), $2, std, math, $1)$(NBSP) - * - * Copyright: Copyright The D Language Foundation 2000 - 2011. - * D implementations of tan, atan, atan2, exp, expm1, exp2, log, log10, log1p, - * log2, floor, ceil and lrint functions are based on the CEPHES math library, - * which is Copyright (C) 2001 Stephen L. Moshier $(LT)steve@moshier.net$(GT) - * and are incorporated herein by permission of the author. The author - * reserves the right to distribute this material elsewhere under different - * copying permissions. These modifications are distributed here under - * the following terms: - * License: $(HTTP www.boost.org/LICENSE_1_0.txt, Boost License 1.0). - * Authors: $(HTTP digitalmars.com, Walter Bright), Don Clugston, - * Conversion of CEPHES math library to D by Iain Buclaw and David Nadlinger - * Source: $(PHOBOSSRC std/math/package.d) - */ -module std.math; - -public import std.math.algebraic; -public import std.math.constants; -public import std.math.exponential; -public import std.math.operations; -public import std.math.hardware; -public import std.math.remainder; -public import std.math.rounding; -public import std.math.traits; -public import std.math.trigonometry; - -package(std): // Not public yet -/* Return the value that lies halfway between x and y on the IEEE number line. - * - * Formally, the result is the arithmetic mean of the binary significands of x - * and y, multiplied by the geometric mean of the binary exponents of x and y. - * x and y must have the same sign, and must not be NaN. - * Note: this function is useful for ensuring O(log n) behaviour in algorithms - * involving a 'binary chop'. - * - * Special cases: - * If x and y are within a factor of 2, (ie, feqrel(x, y) > 0), the return value - * is the arithmetic mean (x + y) / 2. - * If x and y are even powers of 2, the return value is the geometric mean, - * ieeeMean(x, y) = sqrt(x * y). - * - */ -T ieeeMean(T)(const T x, const T y) @trusted pure nothrow @nogc -in -{ - // both x and y must have the same sign, and must not be NaN. - assert(signbit(x) == signbit(y)); - assert(x == x && y == y); -} -do -{ - // Runtime behaviour for contract violation: - // If signs are opposite, or one is a NaN, return 0. - if (!((x >= 0 && y >= 0) || (x <= 0 && y <= 0))) return 0.0; - - // The implementation is simple: cast x and y to integers, - // average them (avoiding overflow), and cast the result back to a floating-point number. - - alias F = floatTraits!(T); - T u; - static if (F.realFormat == RealFormat.ieeeExtended || - F.realFormat == RealFormat.ieeeExtended53) - { - // There's slight additional complexity because they are actually - // 79-bit reals... - ushort *ue = cast(ushort *)&u; - ulong *ul = cast(ulong *)&u; - ushort *xe = cast(ushort *)&x; - ulong *xl = cast(ulong *)&x; - ushort *ye = cast(ushort *)&y; - ulong *yl = cast(ulong *)&y; - - // Ignore the useless implicit bit. (Bonus: this prevents overflows) - ulong m = ((*xl) & 0x7FFF_FFFF_FFFF_FFFFL) + ((*yl) & 0x7FFF_FFFF_FFFF_FFFFL); - - // @@@ BUG? @@@ - // Cast shouldn't be here - ushort e = cast(ushort) ((xe[F.EXPPOS_SHORT] & F.EXPMASK) - + (ye[F.EXPPOS_SHORT] & F.EXPMASK)); - if (m & 0x8000_0000_0000_0000L) - { - ++e; - m &= 0x7FFF_FFFF_FFFF_FFFFL; - } - // Now do a multi-byte right shift - const uint c = e & 1; // carry - e >>= 1; - m >>>= 1; - if (c) - m |= 0x4000_0000_0000_0000L; // shift carry into significand - if (e) - *ul = m | 0x8000_0000_0000_0000L; // set implicit bit... - else - *ul = m; // ... unless exponent is 0 (subnormal or zero). - - ue[4]= e | (xe[F.EXPPOS_SHORT]& 0x8000); // restore sign bit - } - else static if (F.realFormat == RealFormat.ieeeQuadruple) - { - // This would be trivial if 'ucent' were implemented... - ulong *ul = cast(ulong *)&u; - ulong *xl = cast(ulong *)&x; - ulong *yl = cast(ulong *)&y; - - // Multi-byte add, then multi-byte right shift. - import core.checkedint : addu; - bool carry; - ulong ml = addu(xl[MANTISSA_LSB], yl[MANTISSA_LSB], carry); - - ulong mh = carry + (xl[MANTISSA_MSB] & 0x7FFF_FFFF_FFFF_FFFFL) + - (yl[MANTISSA_MSB] & 0x7FFF_FFFF_FFFF_FFFFL); - - ul[MANTISSA_MSB] = (mh >>> 1) | (xl[MANTISSA_MSB] & 0x8000_0000_0000_0000); - ul[MANTISSA_LSB] = (ml >>> 1) | (mh & 1) << 63; - } - else static if (F.realFormat == RealFormat.ieeeDouble) - { - ulong *ul = cast(ulong *)&u; - ulong *xl = cast(ulong *)&x; - ulong *yl = cast(ulong *)&y; - ulong m = (((*xl) & 0x7FFF_FFFF_FFFF_FFFFL) - + ((*yl) & 0x7FFF_FFFF_FFFF_FFFFL)) >>> 1; - m |= ((*xl) & 0x8000_0000_0000_0000L); - *ul = m; - } - else static if (F.realFormat == RealFormat.ieeeSingle) - { - uint *ul = cast(uint *)&u; - uint *xl = cast(uint *)&x; - uint *yl = cast(uint *)&y; - uint m = (((*xl) & 0x7FFF_FFFF) + ((*yl) & 0x7FFF_FFFF)) >>> 1; - m |= ((*xl) & 0x8000_0000); - *ul = m; - } - else - { - assert(0, "Not implemented"); - } - return u; -} - -@safe pure nothrow @nogc unittest -{ - assert(ieeeMean(-0.0,-1e-20)<0); - assert(ieeeMean(0.0,1e-20)>0); - - assert(ieeeMean(1.0L,4.0L)==2L); - assert(ieeeMean(2.0*1.013,8.0*1.013)==4*1.013); - assert(ieeeMean(-1.0L,-4.0L)==-2L); - assert(ieeeMean(-1.0,-4.0)==-2); - assert(ieeeMean(-1.0f,-4.0f)==-2f); - assert(ieeeMean(-1.0,-2.0)==-1.5); - assert(ieeeMean(-1*(1+8*real.epsilon),-2*(1+8*real.epsilon)) - ==-1.5*(1+5*real.epsilon)); - assert(ieeeMean(0x1p60,0x1p-10)==0x1p25); - - static if (floatTraits!(real).realFormat == RealFormat.ieeeExtended) - { - assert(ieeeMean(1.0L,real.infinity)==0x1p8192L); - assert(ieeeMean(0.0L,real.infinity)==1.5); - } - assert(ieeeMean(0.5*real.min_normal*(1-4*real.epsilon),0.5*real.min_normal) - == 0.5*real.min_normal*(1-2*real.epsilon)); -} - - -// The following IEEE 'real' formats are currently supported. -version (LittleEndian) -{ - static assert(real.mant_dig == 53 || real.mant_dig == 64 - || real.mant_dig == 113, - "Only 64-bit, 80-bit, and 128-bit reals"~ - " are supported for LittleEndian CPUs"); -} -else -{ - static assert(real.mant_dig == 53 || real.mant_dig == 113, - "Only 64-bit and 128-bit reals are supported for BigEndian CPUs."); -} diff --git a/phobos/std/math/remainder.d b/phobos/std/math/remainder.d deleted file mode 100644 index 8766713..0000000 --- a/phobos/std/math/remainder.d +++ /dev/null @@ -1,155 +0,0 @@ -// Written in the D programming language. - -/** -This is a submodule of $(MREF std, math). - -It contains several versions of remainder calculation. - -Copyright: Copyright The D Language Foundation 2000 - 2011. -License: $(HTTP www.boost.org/LICENSE_1_0.txt, Boost License 1.0). -Authors: $(HTTP digitalmars.com, Walter Bright), Don Clugston, - Conversion of CEPHES math library to D by Iain Buclaw and David Nadlinger -Source: $(PHOBOSSRC std/math/remainder.d) - -Macros: - TABLE_SV = - - $0
Special Values
- NAN = $(RED NAN) - PLUSMN = ± - PLUSMNINF = ±∞ - */ - -module std.math.remainder; - -static import core.stdc.math; - -/************************************ - * Calculates the remainder from the calculation x/y. - * Returns: - * The value of x - i * y, where i is the number of times that y can - * be completely subtracted from x. The result has the same sign as x. - * - * $(TABLE_SV - * $(TR $(TH x) $(TH y) $(TH fmod(x, y)) $(TH invalid?)) - * $(TR $(TD $(PLUSMN)0.0) $(TD not 0.0) $(TD $(PLUSMN)0.0) $(TD no)) - * $(TR $(TD $(PLUSMNINF)) $(TD anything) $(TD $(NAN)) $(TD yes)) - * $(TR $(TD anything) $(TD $(PLUSMN)0.0) $(TD $(NAN)) $(TD yes)) - * $(TR $(TD !=$(PLUSMNINF)) $(TD $(PLUSMNINF)) $(TD x) $(TD no)) - * ) - */ -real fmod(real x, real y) @trusted nothrow @nogc -{ - version (CRuntime_Microsoft) - { - return x % y; - } - else - return core.stdc.math.fmodl(x, y); -} - -/// -@safe unittest -{ - import std.math.operations : feqrel; - import std.math.traits : isIdentical, isNaN; - - assert(isIdentical(fmod(0.0, 1.0), 0.0)); - assert(fmod(5.0, 3.0).feqrel(2.0) > 16); - assert(isNaN(fmod(5.0, 0.0))); -} - -/************************************ - * Breaks x into an integral part and a fractional part, each of which has - * the same sign as x. The integral part is stored in i. - * Returns: - * The fractional part of x. - * - * $(TABLE_SV - * $(TR $(TH x) $(TH i (on input)) $(TH modf(x, i)) $(TH i (on return))) - * $(TR $(TD $(PLUSMNINF)) $(TD anything) $(TD $(PLUSMN)0.0) $(TD $(PLUSMNINF))) - * ) - */ -real modf(real x, ref real i) @trusted nothrow @nogc -{ - version (CRuntime_Microsoft) - { - import std.math.traits : copysign, isInfinity; - import std.math.rounding : trunc; - - i = trunc(x); - return copysign(isInfinity(x) ? 0.0 : x - i, x); - } - else - return core.stdc.math.modfl(x,&i); -} - -/// -@safe unittest -{ - import std.math.operations : feqrel; - - real frac; - real intpart; - - frac = modf(3.14159, intpart); - assert(intpart.feqrel(3.0) > 16); - assert(frac.feqrel(0.14159) > 16); -} - -/**************************************************** - * Calculate the remainder x REM y, following IEC 60559. - * - * REM is the value of x - y * n, where n is the integer nearest the exact - * value of x / y. - * If |n - x / y| == 0.5, n is even. - * If the result is zero, it has the same sign as x. - * Otherwise, the sign of the result is the sign of x / y. - * Precision mode has no effect on the remainder functions. - * - * remquo returns `n` in the parameter `n`. - * - * $(TABLE_SV - * $(TR $(TH x) $(TH y) $(TH remainder(x, y)) $(TH n) $(TH invalid?)) - * $(TR $(TD $(PLUSMN)0.0) $(TD not 0.0) $(TD $(PLUSMN)0.0) $(TD 0.0) $(TD no)) - * $(TR $(TD $(PLUSMNINF)) $(TD anything) $(TD -$(NAN)) $(TD ?) $(TD yes)) - * $(TR $(TD anything) $(TD $(PLUSMN)0.0) $(TD $(PLUSMN)$(NAN)) $(TD ?) $(TD yes)) - * $(TR $(TD != $(PLUSMNINF)) $(TD $(PLUSMNINF)) $(TD x) $(TD ?) $(TD no)) - * ) - */ -real remainder(real x, real y) @trusted nothrow @nogc -{ - return core.stdc.math.remainderl(x, y); -} - -/// ditto -real remquo(real x, real y, out int n) @trusted nothrow @nogc /// ditto -{ - return core.stdc.math.remquol(x, y, &n); -} - -/// -@safe @nogc nothrow unittest -{ - import std.math.operations : feqrel; - import std.math.traits : isNaN; - - assert(remainder(5.1, 3.0).feqrel(-0.9) > 16); - assert(remainder(-5.1, 3.0).feqrel(0.9) > 16); - assert(remainder(0.0, 3.0) == 0.0); - - assert(isNaN(remainder(1.0, 0.0))); - assert(isNaN(remainder(-1.0, 0.0))); -} - -/// -@safe @nogc nothrow unittest -{ - import std.math.operations : feqrel; - - int n; - - assert(remquo(5.1, 3.0, n).feqrel(-0.9) > 16 && n == 2); - assert(remquo(-5.1, 3.0, n).feqrel(0.9) > 16 && n == -2); - assert(remquo(0.0, 3.0, n) == 0.0 && n == 0); -} diff --git a/phobos/std/math/rounding.d b/phobos/std/math/rounding.d deleted file mode 100644 index f181f39..0000000 --- a/phobos/std/math/rounding.d +++ /dev/null @@ -1,1089 +0,0 @@ -// Written in the D programming language. - -/** -This is a submodule of $(MREF std, math). - -It contains several functions for rounding floating point numbers. - -Copyright: Copyright The D Language Foundation 2000 - 2011. - D implementations of floor, ceil, and lrint functions are based on the - CEPHES math library, which is Copyright (C) 2001 Stephen L. Moshier - $(LT)steve@moshier.net$(GT) and are incorporated herein by permission - of the author. The author reserves the right to distribute this - material elsewhere under different copying permissions. - These modifications are distributed here under the following terms: -License: $(HTTP www.boost.org/LICENSE_1_0.txt, Boost License 1.0). -Authors: $(HTTP digitalmars.com, Walter Bright), Don Clugston, - Conversion of CEPHES math library to D by Iain Buclaw and David Nadlinger -Source: $(PHOBOSSRC std/math/rounding.d) - */ - -module std.math.rounding; - -static import core.math; -static import core.stdc.math; - -import std.traits : isFloatingPoint, isIntegral, Unqual; - -version (LDC) import ldc.intrinsics; - -version (D_InlineAsm_X86) version = InlineAsm_X86_Any; -version (D_InlineAsm_X86_64) version = InlineAsm_X86_Any; - -version (LDC) version (CRuntime_Microsoft) version = LDC_MSVCRT; - -version (LDC_MSVCRT) {} -else version (Android) {} -else version (InlineAsm_X86_Any) version = InlineAsm_X87; -version (InlineAsm_X87) -{ - static assert(real.mant_dig == 64); - version (CRuntime_Microsoft) version = InlineAsm_X87_MSVC; -} - -/************************************** - * Returns the value of x rounded upward to the next integer - * (toward positive infinity). - */ -pragma(inline, true) // LDC -real ceil(real x) @trusted pure nothrow @nogc -{ - version (LDC) - { - return llvm_ceil(x); - } - else version (InlineAsm_X87_MSVC) - { - version (X86_64) - { - asm pure nothrow @nogc - { - naked ; - fld real ptr [RCX] ; - fstcw 8[RSP] ; - mov AL,9[RSP] ; - mov DL,AL ; - and AL,0xC3 ; - or AL,0x08 ; // round to +infinity - mov 9[RSP],AL ; - fldcw 8[RSP] ; - frndint ; - mov 9[RSP],DL ; - fldcw 8[RSP] ; - ret ; - } - } - else - { - short cw; - asm pure nothrow @nogc - { - fld x ; - fstcw cw ; - mov AL,byte ptr cw+1 ; - mov DL,AL ; - and AL,0xC3 ; - or AL,0x08 ; // round to +infinity - mov byte ptr cw+1,AL ; - fldcw cw ; - frndint ; - mov byte ptr cw+1,DL ; - fldcw cw ; - } - } - } - else - { - import std.math.traits : isInfinity, isNaN; - - // Special cases. - if (isNaN(x) || isInfinity(x)) - return x; - - real y = floorImpl(x); - if (y < x) - y += 1.0; - - return y; - } -} - -/// -@safe pure nothrow @nogc unittest -{ - import std.math.traits : isNaN; - - assert(ceil(+123.456L) == +124); - assert(ceil(-123.456L) == -123); - assert(ceil(-1.234L) == -1); - assert(ceil(-0.123L) == 0); - assert(ceil(0.0L) == 0); - assert(ceil(+0.123L) == 1); - assert(ceil(+1.234L) == 2); - assert(ceil(real.infinity) == real.infinity); - assert(isNaN(ceil(real.nan))); - assert(isNaN(ceil(real.init))); -} - -/// ditto -pragma(inline, true) // LDC -double ceil(double x) @trusted pure nothrow @nogc -{ - version (LDC) - { - return llvm_ceil(x); - } - else - { - import std.math.traits : isInfinity, isNaN; - - // Special cases. - if (isNaN(x) || isInfinity(x)) - return x; - - double y = floorImpl(x); - if (y < x) - y += 1.0; - - return y; - } -} - -@safe pure nothrow @nogc unittest -{ - import std.math.traits : isNaN; - - assert(ceil(+123.456) == +124); - assert(ceil(-123.456) == -123); - assert(ceil(-1.234) == -1); - assert(ceil(-0.123) == 0); - assert(ceil(0.0) == 0); - assert(ceil(+0.123) == 1); - assert(ceil(+1.234) == 2); - assert(ceil(double.infinity) == double.infinity); - assert(isNaN(ceil(double.nan))); - assert(isNaN(ceil(double.init))); -} - -/// ditto -pragma(inline, true) // LDC -float ceil(float x) @trusted pure nothrow @nogc -{ - version (LDC) - { - return llvm_ceil(x); - } - else - { - import std.math.traits : isInfinity, isNaN; - - // Special cases. - if (isNaN(x) || isInfinity(x)) - return x; - - float y = floorImpl(x); - if (y < x) - y += 1.0; - - return y; - } -} - -@safe pure nothrow @nogc unittest -{ - import std.math.traits : isNaN; - - assert(ceil(+123.456f) == +124); - assert(ceil(-123.456f) == -123); - assert(ceil(-1.234f) == -1); - assert(ceil(-0.123f) == 0); - assert(ceil(0.0f) == 0); - assert(ceil(+0.123f) == 1); - assert(ceil(+1.234f) == 2); - assert(ceil(float.infinity) == float.infinity); - assert(isNaN(ceil(float.nan))); - assert(isNaN(ceil(float.init))); -} - -/************************************** - * Returns the value of x rounded downward to the next integer - * (toward negative infinity). - */ -pragma(inline, true) // LDC -real floor(real x) @trusted pure nothrow @nogc -{ - version (LDC) - { - return llvm_floor(x); - } - else version (InlineAsm_X87_MSVC) - { - version (X86_64) - { - asm pure nothrow @nogc - { - naked ; - fld real ptr [RCX] ; - fstcw 8[RSP] ; - mov AL,9[RSP] ; - mov DL,AL ; - and AL,0xC3 ; - or AL,0x04 ; // round to -infinity - mov 9[RSP],AL ; - fldcw 8[RSP] ; - frndint ; - mov 9[RSP],DL ; - fldcw 8[RSP] ; - ret ; - } - } - else - { - short cw; - asm pure nothrow @nogc - { - fld x ; - fstcw cw ; - mov AL,byte ptr cw+1 ; - mov DL,AL ; - and AL,0xC3 ; - or AL,0x04 ; // round to -infinity - mov byte ptr cw+1,AL ; - fldcw cw ; - frndint ; - mov byte ptr cw+1,DL ; - fldcw cw ; - } - } - } - else - { - import std.math.traits : isInfinity, isNaN; - - // Special cases. - if (isNaN(x) || isInfinity(x) || x == 0.0) - return x; - - return floorImpl(x); - } -} - -/// -@safe pure nothrow @nogc unittest -{ - import std.math.traits : isNaN; - - assert(floor(+123.456L) == +123); - assert(floor(-123.456L) == -124); - assert(floor(+123.0L) == +123); - assert(floor(-124.0L) == -124); - assert(floor(-1.234L) == -2); - assert(floor(-0.123L) == -1); - assert(floor(0.0L) == 0); - assert(floor(+0.123L) == 0); - assert(floor(+1.234L) == 1); - assert(floor(real.infinity) == real.infinity); - assert(isNaN(floor(real.nan))); - assert(isNaN(floor(real.init))); -} - -/// ditto -pragma(inline, true) // LDC -double floor(double x) @trusted pure nothrow @nogc -{ - version (LDC) - { - return llvm_floor(x); - } - else - { - import std.math.traits : isInfinity, isNaN; - - // Special cases. - if (isNaN(x) || isInfinity(x) || x == 0.0) - return x; - - return floorImpl(x); - } -} - -@safe pure nothrow @nogc unittest -{ - import std.math.traits : isNaN; - - assert(floor(+123.456) == +123); - assert(floor(-123.456) == -124); - assert(floor(+123.0) == +123); - assert(floor(-124.0) == -124); - assert(floor(-1.234) == -2); - assert(floor(-0.123) == -1); - assert(floor(0.0) == 0); - assert(floor(+0.123) == 0); - assert(floor(+1.234) == 1); - assert(floor(double.infinity) == double.infinity); - assert(isNaN(floor(double.nan))); - assert(isNaN(floor(double.init))); -} - -/// ditto -pragma(inline, true) // LDC -float floor(float x) @trusted pure nothrow @nogc -{ - version (LDC) - { - return llvm_floor(x); - } - else - { - import std.math.traits : isInfinity, isNaN; - - // Special cases. - if (isNaN(x) || isInfinity(x) || x == 0.0) - return x; - - return floorImpl(x); - } -} - -@safe pure nothrow @nogc unittest -{ - import std.math.traits : isNaN; - - assert(floor(+123.456f) == +123); - assert(floor(-123.456f) == -124); - assert(floor(+123.0f) == +123); - assert(floor(-124.0f) == -124); - assert(floor(-1.234f) == -2); - assert(floor(-0.123f) == -1); - assert(floor(0.0f) == 0); - assert(floor(+0.123f) == 0); - assert(floor(+1.234f) == 1); - assert(floor(float.infinity) == float.infinity); - assert(isNaN(floor(float.nan))); - assert(isNaN(floor(float.init))); -} - -// https://issues.dlang.org/show_bug.cgi?id=6381 -// floor/ceil should be usable in pure function. -@safe pure nothrow unittest -{ - auto x = floor(1.2); - auto y = ceil(1.2); -} - -/** - * Round `val` to a multiple of `unit`. `rfunc` specifies the rounding - * function to use; by default this is `rint`, which uses the current - * rounding mode. - */ -Unqual!F quantize(alias rfunc = rint, F)(const F val, const F unit) -if (is(typeof(rfunc(F.init)) : F) && isFloatingPoint!F) -{ - import std.math.traits : isInfinity; - - typeof(return) ret = val; - if (unit != 0) - { - const scaled = val / unit; - if (!scaled.isInfinity) - ret = rfunc(scaled) * unit; - } - return ret; -} - -/// -@safe pure nothrow @nogc unittest -{ - import std.math.operations : isClose; - - assert(isClose(12345.6789L.quantize(0.01L), 12345.68L)); - assert(isClose(12345.6789L.quantize!floor(0.01L), 12345.67L)); - assert(isClose(12345.6789L.quantize(22.0L), 12342.0L)); -} - -/// -@safe pure nothrow @nogc unittest -{ - import std.math.operations : isClose; - import std.math.traits : isNaN; - - assert(isClose(12345.6789L.quantize(0), 12345.6789L)); - assert(12345.6789L.quantize(real.infinity).isNaN); - assert(12345.6789L.quantize(real.nan).isNaN); - assert(real.infinity.quantize(0.01L) == real.infinity); - assert(real.infinity.quantize(real.nan).isNaN); - assert(real.nan.quantize(0.01L).isNaN); - assert(real.nan.quantize(real.infinity).isNaN); - assert(real.nan.quantize(real.nan).isNaN); -} - -/** - * Round `val` to a multiple of `pow(base, exp)`. `rfunc` specifies the - * rounding function to use; by default this is `rint`, which uses the - * current rounding mode. - */ -Unqual!F quantize(real base, alias rfunc = rint, F, E)(const F val, const E exp) -if (is(typeof(rfunc(F.init)) : F) && isFloatingPoint!F && isIntegral!E) -{ - import std.math.exponential : pow; - - // TODO: Compile-time optimization for power-of-two bases? - return quantize!rfunc(val, pow(cast(F) base, exp)); -} - -/// ditto -Unqual!F quantize(real base, long exp = 1, alias rfunc = rint, F)(const F val) -if (is(typeof(rfunc(F.init)) : F) && isFloatingPoint!F) -{ - import std.math.exponential : pow; - - enum unit = cast(F) pow(base, exp); - return quantize!rfunc(val, unit); -} - -/// -@safe pure nothrow @nogc unittest -{ - import std.math.operations : isClose; - - assert(isClose(12345.6789L.quantize!10(-2), 12345.68L)); - assert(isClose(12345.6789L.quantize!(10, -2), 12345.68L)); - assert(isClose(12345.6789L.quantize!(10, floor)(-2), 12345.67L)); - assert(isClose(12345.6789L.quantize!(10, -2, floor), 12345.67L)); - - assert(isClose(12345.6789L.quantize!22(1), 12342.0L)); - assert(isClose(12345.6789L.quantize!22, 12342.0L)); -} - -@safe pure nothrow @nogc unittest -{ - import std.math.exponential : log10, pow; - import std.math.operations : isClose; - import std.meta : AliasSeq; - - static foreach (F; AliasSeq!(real, double, float)) - {{ - const maxL10 = cast(int) F.max.log10.floor; - const maxR10 = pow(cast(F) 10, maxL10); - assert(isClose((cast(F) 0.9L * maxR10).quantize!10(maxL10), maxR10)); - assert(isClose((cast(F)-0.9L * maxR10).quantize!10(maxL10), -maxR10)); - - assert(F.max.quantize(F.min_normal) == F.max); - assert((-F.max).quantize(F.min_normal) == -F.max); - assert(F.min_normal.quantize(F.max) == 0); - assert((-F.min_normal).quantize(F.max) == 0); - assert(F.min_normal.quantize(F.min_normal) == F.min_normal); - assert((-F.min_normal).quantize(F.min_normal) == -F.min_normal); - }} -} - -/****************************************** - * Rounds x to the nearest integer value, using the current rounding - * mode. - * - * Unlike the rint functions, nearbyint does not raise the - * FE_INEXACT exception. - */ -version (LDC) -{ - pragma(inline, true): - real nearbyint(real x) @safe pure nothrow @nogc { return llvm_nearbyint(x); } - //double nearbyint(double x) @safe pure nothrow @nogc { return llvm_nearbyint(x); } - //float nearbyint(float x) @safe pure nothrow @nogc { return llvm_nearbyint(x); } -} -else -{ - -pragma(inline, true) -real nearbyint(real x) @safe pure nothrow @nogc -{ - return core.stdc.math.nearbyintl(x); -} - -} // !LDC - -/// -@safe pure unittest -{ - import std.math.traits : isNaN; - - assert(nearbyint(0.4) == 0); - assert(nearbyint(0.5) == 0); - assert(nearbyint(0.6) == 1); - assert(nearbyint(100.0) == 100); - - assert(isNaN(nearbyint(real.nan))); - assert(nearbyint(real.infinity) == real.infinity); - assert(nearbyint(-real.infinity) == -real.infinity); -} - -/********************************** - * Rounds x to the nearest integer value, using the current rounding - * mode. - * - * If the return value is not equal to x, the FE_INEXACT - * exception is raised. - * - * $(LREF nearbyint) performs the same operation, but does - * not set the FE_INEXACT exception. - */ -pragma(inline, true) -real rint(real x) @safe pure nothrow @nogc -{ - return core.math.rint(x); -} -///ditto -pragma(inline, true) -double rint(double x) @safe pure nothrow @nogc -{ - return core.math.rint(x); -} -///ditto -pragma(inline, true) -float rint(float x) @safe pure nothrow @nogc -{ - return core.math.rint(x); -} - -/// -@safe unittest -{ - import std.math.traits : isNaN; - - version (IeeeFlagsSupport) resetIeeeFlags(); - assert(rint(0.4) == 0); - version (LDC) { /* inexact bit not set with enabled optimizations */ } else - version (IeeeFlagsSupport) assert(ieeeFlags.inexact); - - assert(rint(0.5) == 0); - assert(rint(0.6) == 1); - assert(rint(100.0) == 100); - - assert(isNaN(rint(real.nan))); - assert(rint(real.infinity) == real.infinity); - assert(rint(-real.infinity) == -real.infinity); -} - -@safe unittest -{ - real function(real) print = &rint; - assert(print != null); -} - -/*************************************** - * Rounds x to the nearest integer value, using the current rounding - * mode. - * - * This is generally the fastest method to convert a floating-point number - * to an integer. Note that the results from this function - * depend on the rounding mode, if the fractional part of x is exactly 0.5. - * If using the default rounding mode (ties round to even integers) - * lrint(4.5) == 4, lrint(5.5)==6. - */ -long lrint(real x) @trusted pure nothrow @nogc -{ - version (InlineAsm_X87) - { - version (Win64) - { - asm pure nothrow @nogc - { - naked; - fld real ptr [RCX]; - fistp qword ptr 8[RSP]; - mov RAX,8[RSP]; - ret; - } - } - else - { - long n; - asm pure nothrow @nogc - { - fld x; - fistp n; - } - return n; - } - } - else - { - import std.math.traits : floatTraits, RealFormat, MANTISSA_MSB, MANTISSA_LSB; - - alias F = floatTraits!(real); - static if (F.realFormat == RealFormat.ieeeDouble) - { - long result; - - // Rounding limit when casting from real(double) to ulong. - enum real OF = 4.50359962737049600000E15L; - - uint* vi = cast(uint*)(&x); - - // Find the exponent and sign - uint msb = vi[MANTISSA_MSB]; - uint lsb = vi[MANTISSA_LSB]; - int exp = ((msb >> 20) & 0x7ff) - 0x3ff; - const int sign = msb >> 31; - msb &= 0xfffff; - msb |= 0x100000; - - if (exp < 63) - { - if (exp >= 52) - result = (cast(long) msb << (exp - 20)) | (lsb << (exp - 52)); - else - { - // Adjust x and check result. - const real j = sign ? -OF : OF; - x = (j + x) - j; - msb = vi[MANTISSA_MSB]; - lsb = vi[MANTISSA_LSB]; - exp = ((msb >> 20) & 0x7ff) - 0x3ff; - msb &= 0xfffff; - msb |= 0x100000; - - if (exp < 0) - result = 0; - else if (exp < 20) - result = cast(long) msb >> (20 - exp); - else if (exp == 20) - result = cast(long) msb; - else - result = (cast(long) msb << (exp - 20)) | (lsb >> (52 - exp)); - } - } - else - { - // It is left implementation defined when the number is too large. - return cast(long) x; - } - - return sign ? -result : result; - } - else static if (F.realFormat == RealFormat.ieeeExtended || - F.realFormat == RealFormat.ieeeExtended53) - { - long result; - - // Rounding limit when casting from real(80-bit) to ulong. - static if (F.realFormat == RealFormat.ieeeExtended) - enum real OF = 9.22337203685477580800E18L; - else - enum real OF = 4.50359962737049600000E15L; - - ushort* vu = cast(ushort*)(&x); - uint* vi = cast(uint*)(&x); - - // Find the exponent and sign - int exp = (vu[F.EXPPOS_SHORT] & 0x7fff) - 0x3fff; - const int sign = (vu[F.EXPPOS_SHORT] >> 15) & 1; - - if (exp < 63) - { - // Adjust x and check result. - const real j = sign ? -OF : OF; - x = (j + x) - j; - exp = (vu[F.EXPPOS_SHORT] & 0x7fff) - 0x3fff; - - version (LittleEndian) - { - if (exp < 0) - result = 0; - else if (exp <= 31) - result = vi[1] >> (31 - exp); - else - result = (cast(long) vi[1] << (exp - 31)) | (vi[0] >> (63 - exp)); - } - else - { - if (exp < 0) - result = 0; - else if (exp <= 31) - result = vi[1] >> (31 - exp); - else - result = (cast(long) vi[1] << (exp - 31)) | (vi[2] >> (63 - exp)); - } - } - else - { - // It is left implementation defined when the number is too large - // to fit in a 64bit long. - return cast(long) x; - } - - return sign ? -result : result; - } - else static if (F.realFormat == RealFormat.ieeeQuadruple) - { - const vu = cast(ushort*)(&x); - - // Find the exponent and sign - const sign = (vu[F.EXPPOS_SHORT] >> 15) & 1; - if ((vu[F.EXPPOS_SHORT] & F.EXPMASK) - (F.EXPBIAS + 1) > 63) - { - // The result is left implementation defined when the number is - // too large to fit in a 64 bit long. - return cast(long) x; - } - - // Force rounding of lower bits according to current rounding - // mode by adding Âą2^-112 and subtracting it again. - enum OF = 5.19229685853482762853049632922009600E33L; - const j = sign ? -OF : OF; - x = (j + x) - j; - - const exp = (vu[F.EXPPOS_SHORT] & F.EXPMASK) - (F.EXPBIAS + 1); - const implicitOne = 1UL << 48; - auto vl = cast(ulong*)(&x); - vl[MANTISSA_MSB] &= implicitOne - 1; - vl[MANTISSA_MSB] |= implicitOne; - - long result; - - if (exp < 0) - result = 0; - else if (exp <= 48) - result = vl[MANTISSA_MSB] >> (48 - exp); - else - result = (vl[MANTISSA_MSB] << (exp - 48)) | (vl[MANTISSA_LSB] >> (112 - exp)); - - return sign ? -result : result; - } - else - { - static assert(false, "real type not supported by lrint()"); - } - } -} - -/// -@safe pure nothrow @nogc unittest -{ - assert(lrint(4.5) == 4); - assert(lrint(5.5) == 6); - assert(lrint(-4.5) == -4); - assert(lrint(-5.5) == -6); - - assert(lrint(int.max - 0.5) == 2147483646L); - assert(lrint(int.max + 0.5) == 2147483648L); - assert(lrint(int.min - 0.5) == -2147483648L); - assert(lrint(int.min + 0.5) == -2147483648L); -} - -static if (real.mant_dig >= long.sizeof * 8) -{ - @safe pure nothrow @nogc unittest - { - assert(lrint(long.max - 1.5L) == long.max - 1); - assert(lrint(long.max - 0.5L) == long.max - 1); - assert(lrint(long.min + 0.5L) == long.min); - assert(lrint(long.min + 1.5L) == long.min + 2); - } -} - -/******************************************* - * Return the value of x rounded to the nearest integer. - * If the fractional part of x is exactly 0.5, the return value is - * rounded away from zero. - * - * Returns: - * A `real`. - */ -version (LDC) -{ - pragma(inline, true): - real round(real x) @safe pure nothrow @nogc { return llvm_round(x); } - //double round(double x) @safe pure nothrow @nogc { return llvm_round(x); } - //float round(float x) @safe pure nothrow @nogc { return llvm_round(x); } -} -else -{ - -auto round(real x) @trusted nothrow @nogc -{ - version (CRuntime_Microsoft) - { - import std.math.hardware : FloatingPointControl; - - auto old = FloatingPointControl.getControlState(); - FloatingPointControl.setControlState( - (old & (-1 - FloatingPointControl.roundingMask)) | FloatingPointControl.roundToZero - ); - x = core.math.rint((x >= 0) ? x + 0.5 : x - 0.5); - FloatingPointControl.setControlState(old); - return x; - } - else - { - return core.stdc.math.roundl(x); - } -} - -} // !LDC - -/// -@safe nothrow @nogc unittest -{ - assert(round(4.5) == 5); - assert(round(5.4) == 5); - assert(round(-4.5) == -5); - assert(round(-5.1) == -5); -} - -// assure purity on Posix -version (Posix) -{ - @safe pure nothrow @nogc unittest - { - assert(round(4.5) == 5); - } -} - -/********************************************** - * Return the value of x rounded to the nearest integer. - * - * If the fractional part of x is exactly 0.5, the return value is rounded - * away from zero. - * - * $(BLUE This function is not implemented for Digital Mars C runtime.) - */ -long lround(real x) @trusted nothrow @nogc -{ - version (CRuntime_DigitalMars) - assert(0, "lround not implemented"); - else - return core.stdc.math.llroundl(x); -} - -/// -@safe nothrow @nogc unittest -{ - version (CRuntime_DigitalMars) {} - else - { - assert(lround(0.49) == 0); - assert(lround(0.5) == 1); - assert(lround(1.5) == 2); - } -} - -/** - Returns the integer portion of x, dropping the fractional portion. - This is also known as "chop" rounding. - `pure` on all platforms. - */ -version (LDC) -{ - pragma(inline, true): - real trunc(real x) @safe pure nothrow @nogc { return llvm_trunc(x); } - //double trunc(double x) @safe pure nothrow @nogc { return llvm_trunc(x); } - //float trunc(float x) @safe pure nothrow @nogc { return llvm_trunc(x); } -} -else -{ - -real trunc(real x) @trusted nothrow @nogc pure -{ - version (InlineAsm_X87_MSVC) - { - version (X86_64) - { - asm pure nothrow @nogc - { - naked ; - fld real ptr [RCX] ; - fstcw 8[RSP] ; - mov AL,9[RSP] ; - mov DL,AL ; - and AL,0xC3 ; - or AL,0x0C ; // round to 0 - mov 9[RSP],AL ; - fldcw 8[RSP] ; - frndint ; - mov 9[RSP],DL ; - fldcw 8[RSP] ; - ret ; - } - } - else - { - short cw; - asm pure nothrow @nogc - { - fld x ; - fstcw cw ; - mov AL,byte ptr cw+1 ; - mov DL,AL ; - and AL,0xC3 ; - or AL,0x0C ; // round to 0 - mov byte ptr cw+1,AL ; - fldcw cw ; - frndint ; - mov byte ptr cw+1,DL ; - fldcw cw ; - } - } - } - else - { - return core.stdc.math.truncl(x); - } -} - -} // !LDC - -/// -@safe pure unittest -{ - assert(trunc(0.01) == 0); - assert(trunc(0.49) == 0); - assert(trunc(0.5) == 0); - assert(trunc(1.5) == 1); -} - -/***************************************** - * Returns x rounded to a long value using the current rounding mode. - * If the integer value of x is - * greater than long.max, the result is - * indeterminate. - */ -pragma(inline, true) -long rndtol(real x) @nogc @safe pure nothrow { return core.math.rndtol(x); } -//FIXME -///ditto -pragma(inline, true) -long rndtol(double x) @safe pure nothrow @nogc { return rndtol(cast(real) x); } -//FIXME -///ditto -pragma(inline, true) -long rndtol(float x) @safe pure nothrow @nogc { return rndtol(cast(real) x); } - -/// -@safe unittest -{ - assert(rndtol(1.0) == 1L); - assert(rndtol(1.2) == 1L); - assert(rndtol(1.7) == 2L); - assert(rndtol(1.0001) == 1L); -} - -@safe unittest -{ - long function(real) prndtol = &rndtol; - assert(prndtol != null); -} - -// Helper for floor/ceil -T floorImpl(T)(const T x) @trusted pure nothrow @nogc -{ - import std.math.traits : floatTraits, RealFormat; - - alias F = floatTraits!(T); - // Take care not to trigger library calls from the compiler, - // while ensuring that we don't get defeated by some optimizers. - union floatBits - { - T rv; - ushort[T.sizeof/2] vu; - - // Other kinds of extractors for real formats. - static if (F.realFormat == RealFormat.ieeeSingle) - uint vi; - else static if (F.realFormat == RealFormat.ieeeDouble) - ulong vi; - } - floatBits y = void; - y.rv = x; - - // Find the exponent (power of 2) - // Do this by shifting the raw value so that the exponent lies in the low bits, - // then mask out the sign bit, and subtract the bias. - static if (F.realFormat == RealFormat.ieeeSingle) - { - int exp = ((y.vi >> (T.mant_dig - 1)) & 0xff) - 0x7f; - enum mantissa_mask = F.MANTISSAMASK_INT; - enum sign_shift = 31; - } - else static if (F.realFormat == RealFormat.ieeeDouble) - { - long exp = ((y.vi >> (T.mant_dig - 1)) & 0x7ff) - 0x3ff; - enum mantissa_mask = F.MANTISSAMASK_LONG; - enum sign_shift = 63; - } - else static if (F.realFormat == RealFormat.ieeeExtended || - F.realFormat == RealFormat.ieeeExtended53) - { - int exp = (y.vu[F.EXPPOS_SHORT] & 0x7fff) - 0x3fff; - - version (LittleEndian) - int pos = 0; - else - int pos = 4; - } - else static if (F.realFormat == RealFormat.ieeeQuadruple) - { - int exp = (y.vu[F.EXPPOS_SHORT] & 0x7fff) - 0x3fff; - - version (LittleEndian) - int pos = 0; - else - int pos = 7; - } - else - static assert(false, "Not implemented for this architecture"); - - if (exp < 0) - { - if (x < 0.0) - return -1.0; - else - return 0.0; - } - - static if (F.realFormat == RealFormat.ieeeSingle || - F.realFormat == RealFormat.ieeeDouble) - { - if (exp < (T.mant_dig - 1)) - { - // Clear all bits representing the fraction part. - // Note: the fraction mask represents the floating point number 0.999999... - // i.e: `2.0 ^^ (exp - T.mant_dig + 1) * (fraction_mask + 1) == 1.0` - const fraction_mask = mantissa_mask >> exp; - - if ((y.vi & fraction_mask) != 0) - { - // If 'x' is negative, then first substract (1.0 - T.epsilon) from the value. - if (y.vi >> sign_shift) - y.vi += fraction_mask; - y.vi &= ~fraction_mask; - } - } - } - else - { - static if (F.realFormat == RealFormat.ieeeExtended53) - exp = (T.mant_dig + 11 - 1) - exp; // mant_dig is really 64 - else - exp = (T.mant_dig - 1) - exp; - - // Zero 16 bits at a time. - while (exp >= 16) - { - version (LittleEndian) - y.vu[pos++] = 0; - else - y.vu[pos--] = 0; - exp -= 16; - } - - // Clear the remaining bits. - if (exp > 0) - y.vu[pos] &= 0xffff ^ ((1 << exp) - 1); - - if ((x < 0.0) && (x != y.rv)) - y.rv -= 1.0; - } - - return y.rv; -} diff --git a/phobos/std/math/traits.d b/phobos/std/math/traits.d deleted file mode 100644 index 1127259..0000000 --- a/phobos/std/math/traits.d +++ /dev/null @@ -1,1048 +0,0 @@ -// Written in the D programming language. - -/** -This is a submodule of $(MREF std, math). - -It contains several functions for introspection on numerical values. - -Copyright: Copyright The D Language Foundation 2000 - 2011. -License: $(HTTP www.boost.org/LICENSE_1_0.txt, Boost License 1.0). -Authors: $(HTTP digitalmars.com, Walter Bright), Don Clugston, - Conversion of CEPHES math library to D by Iain Buclaw and David Nadlinger -Source: $(PHOBOSSRC std/math/traits.d) - -Macros: - NAN = $(RED NAN) - PLUSMN = ± - INFIN = ∞ - */ - -module std.math.traits; - -import std.traits : isFloatingPoint, isIntegral, isNumeric, isSigned; - -version (LDC) import ldc.intrinsics; - -/********************************* - * Determines if $(D_PARAM x) is NaN. - * Params: - * x = a floating point number. - * Returns: - * `true` if $(D_PARAM x) is Nan. - */ -bool isNaN(X)(X x) @nogc @trusted pure nothrow -if (isFloatingPoint!(X)) -{ - version (all) - { - return x != x; - } - else - { - /* - Code kept for historical context. At least on Intel, the simple test - x != x uses one dedicated instruction (ucomiss/ucomisd) that runs in one - cycle. Code for 80- and 128-bits is larger but still smaller than the - integrals-based solutions below. Future revisions may enable the code - below conditionally depending on hardware. - */ - alias F = floatTraits!(X); - static if (F.realFormat == RealFormat.ieeeSingle) - { - const uint p = *cast(uint *)&x; - // Sign bit (MSB) is irrelevant so mask it out. - // Next 8 bits should be all set. - // At least one bit among the least significant 23 bits should be set. - return (p & 0x7FFF_FFFF) > 0x7F80_0000; - } - else static if (F.realFormat == RealFormat.ieeeDouble) - { - const ulong p = *cast(ulong *)&x; - // Sign bit (MSB) is irrelevant so mask it out. - // Next 11 bits should be all set. - // At least one bit among the least significant 52 bits should be set. - return (p & 0x7FFF_FFFF_FFFF_FFFF) > 0x7FF0_0000_0000_0000; - } - else static if (F.realFormat == RealFormat.ieeeExtended || - F.realFormat == RealFormat.ieeeExtended53) - { - const ushort e = F.EXPMASK & (cast(ushort *)&x)[F.EXPPOS_SHORT]; - const ulong ps = *cast(ulong *)&x; - return e == F.EXPMASK && - ps & 0x7FFF_FFFF_FFFF_FFFF; // not infinity - } - else static if (F.realFormat == RealFormat.ieeeQuadruple) - { - const ushort e = F.EXPMASK & (cast(ushort *)&x)[F.EXPPOS_SHORT]; - const ulong psLsb = (cast(ulong *)&x)[MANTISSA_LSB]; - const ulong psMsb = (cast(ulong *)&x)[MANTISSA_MSB]; - return e == F.EXPMASK && - (psLsb | (psMsb& 0x0000_FFFF_FFFF_FFFF)) != 0; - } - else - { - return x != x; - } - } -} - -/// -@safe pure nothrow @nogc unittest -{ - assert( isNaN(float.init)); - assert( isNaN(-double.init)); - assert( isNaN(real.nan)); - assert( isNaN(-real.nan)); - assert(!isNaN(cast(float) 53.6)); - assert(!isNaN(cast(real)-53.6)); -} - -@safe pure nothrow @nogc unittest -{ - import std.meta : AliasSeq; - - static foreach (T; AliasSeq!(float, double, real)) - {{ - // CTFE-able tests - assert(isNaN(T.init)); - assert(isNaN(-T.init)); - assert(isNaN(T.nan)); - assert(isNaN(-T.nan)); - assert(!isNaN(T.infinity)); - assert(!isNaN(-T.infinity)); - assert(!isNaN(cast(T) 53.6)); - assert(!isNaN(cast(T)-53.6)); - - // Runtime tests - shared T f; - f = T.init; - assert(isNaN(f)); - assert(isNaN(-f)); - f = T.nan; - assert(isNaN(f)); - assert(isNaN(-f)); - f = T.infinity; - assert(!isNaN(f)); - assert(!isNaN(-f)); - f = cast(T) 53.6; - assert(!isNaN(f)); - assert(!isNaN(-f)); - }} -} - -/********************************* - * Determines if $(D_PARAM x) is finite. - * Params: - * x = a floating point number. - * Returns: - * `true` if $(D_PARAM x) is finite. - */ -bool isFinite(X)(X x) @trusted pure nothrow @nogc -{ - import std.math.traits : floatTraits, RealFormat; - - static if (__traits(isFloating, X)) - if (__ctfe) - return x == x && x != X.infinity && x != -X.infinity; - alias F = floatTraits!(X); - ushort* pe = cast(ushort *)&x; - return (pe[F.EXPPOS_SHORT] & F.EXPMASK) != F.EXPMASK; -} - -/// -@safe pure nothrow @nogc unittest -{ - assert( isFinite(1.23f)); - assert( isFinite(float.max)); - assert( isFinite(float.min_normal)); - assert(!isFinite(float.nan)); - assert(!isFinite(float.infinity)); -} - -@safe pure nothrow @nogc unittest -{ - assert(isFinite(1.23)); - assert(isFinite(double.max)); - assert(isFinite(double.min_normal)); - assert(!isFinite(double.nan)); - assert(!isFinite(double.infinity)); - - assert(isFinite(1.23L)); - assert(isFinite(real.max)); - assert(isFinite(real.min_normal)); - assert(!isFinite(real.nan)); - assert(!isFinite(real.infinity)); - - //CTFE - static assert(isFinite(1.23)); - static assert(isFinite(double.max)); - static assert(isFinite(double.min_normal)); - static assert(!isFinite(double.nan)); - static assert(!isFinite(double.infinity)); - - static assert(isFinite(1.23L)); - static assert(isFinite(real.max)); - static assert(isFinite(real.min_normal)); - static assert(!isFinite(real.nan)); - static assert(!isFinite(real.infinity)); -} - - -/********************************* - * Determines if $(D_PARAM x) is normalized. - * - * A normalized number must not be zero, subnormal, infinite nor $(NAN). - * - * Params: - * x = a floating point number. - * Returns: - * `true` if $(D_PARAM x) is normalized. - */ - -/* Need one for each format because subnormal floats might - * be converted to normal reals. - */ -bool isNormal(X)(X x) @trusted pure nothrow @nogc -{ - import std.math.traits : floatTraits, RealFormat; - - static if (__traits(isFloating, X)) - if (__ctfe) - return (x <= -X.min_normal && x != -X.infinity) || (x >= X.min_normal && x != X.infinity); - alias F = floatTraits!(X); - ushort e = F.EXPMASK & (cast(ushort *)&x)[F.EXPPOS_SHORT]; - return (e != F.EXPMASK && e != 0); -} - -/// -@safe pure nothrow @nogc unittest -{ - float f = 3; - double d = 500; - real e = 10e+48; - - assert(isNormal(f)); - assert(isNormal(d)); - assert(isNormal(e)); - f = d = e = 0; - assert(!isNormal(f)); - assert(!isNormal(d)); - assert(!isNormal(e)); - assert(!isNormal(real.infinity)); - assert(isNormal(-real.max)); - assert(!isNormal(real.min_normal/4)); - -} - -@safe pure nothrow @nogc unittest -{ - // CTFE - enum float f = 3; - enum double d = 500; - enum real e = 10e+48; - - static assert(isNormal(f)); - static assert(isNormal(d)); - static assert(isNormal(e)); - - static assert(!isNormal(0.0f)); - static assert(!isNormal(0.0)); - static assert(!isNormal(0.0L)); - static assert(!isNormal(real.infinity)); - static assert(isNormal(-real.max)); - static assert(!isNormal(real.min_normal/4)); -} - -/********************************* - * Determines if $(D_PARAM x) is subnormal. - * - * Subnormals (also known as "denormal number"), have a 0 exponent - * and a 0 most significant mantissa bit. - * - * Params: - * x = a floating point number. - * Returns: - * `true` if $(D_PARAM x) is a denormal number. - */ -bool isSubnormal(X)(X x) @trusted pure nothrow @nogc -{ - import std.math.traits : floatTraits, RealFormat, MANTISSA_MSB, MANTISSA_LSB; - - static if (__traits(isFloating, X)) - if (__ctfe) - return -X.min_normal < x && x < X.min_normal; - /* - Need one for each format because subnormal floats might - be converted to normal reals. - */ - alias F = floatTraits!(X); - static if (F.realFormat == RealFormat.ieeeSingle) - { - uint *p = cast(uint *)&x; - return (*p & F.EXPMASK_INT) == 0 && *p & F.MANTISSAMASK_INT; - } - else static if (F.realFormat == RealFormat.ieeeDouble) - { - uint *p = cast(uint *)&x; - return (p[MANTISSA_MSB] & F.EXPMASK_INT) == 0 - && (p[MANTISSA_LSB] || p[MANTISSA_MSB] & F.MANTISSAMASK_INT); - } - else static if (F.realFormat == RealFormat.ieeeQuadruple) - { - ushort e = F.EXPMASK & (cast(ushort *)&x)[F.EXPPOS_SHORT]; - long* ps = cast(long *)&x; - return (e == 0 && - ((ps[MANTISSA_LSB]|(ps[MANTISSA_MSB]& 0x0000_FFFF_FFFF_FFFF)) != 0)); - } - else static if (F.realFormat == RealFormat.ieeeExtended || - F.realFormat == RealFormat.ieeeExtended53) - { - ushort* pe = cast(ushort *)&x; - long* ps = cast(long *)&x; - - return (pe[F.EXPPOS_SHORT] & F.EXPMASK) == 0 && *ps > 0; - } - else - { - static assert(false, "Not implemented for this architecture"); - } -} - -/// -@safe pure nothrow @nogc unittest -{ - import std.meta : AliasSeq; - - static foreach (T; AliasSeq!(float, double, real)) - {{ - T f; - for (f = 1.0; !isSubnormal(f); f /= 2) - assert(f != 0); - }} -} - -@safe pure nothrow @nogc unittest -{ - static bool subnormalTest(T)() - { - T f; - for (f = 1.0; !isSubnormal(f); f /= 2) - if (f == 0) - return false; - return true; - } - static assert(subnormalTest!float()); - static assert(subnormalTest!double()); - static assert(subnormalTest!real()); -} - -/********************************* - * Determines if $(D_PARAM x) is $(PLUSMN)$(INFIN). - * Params: - * x = a floating point number. - * Returns: - * `true` if $(D_PARAM x) is $(PLUSMN)$(INFIN). - */ -bool isInfinity(X)(X x) @nogc @trusted pure nothrow -if (isFloatingPoint!(X)) -{ - import std.math.traits : floatTraits, RealFormat, MANTISSA_MSB, MANTISSA_LSB; - - alias F = floatTraits!(X); - static if (F.realFormat == RealFormat.ieeeSingle) - { - return ((*cast(uint *)&x) & 0x7FFF_FFFF) == 0x7F80_0000; - } - else static if (F.realFormat == RealFormat.ieeeDouble) - { - return ((*cast(ulong *)&x) & 0x7FFF_FFFF_FFFF_FFFF) - == 0x7FF0_0000_0000_0000; - } - else static if (F.realFormat == RealFormat.ieeeExtended || - F.realFormat == RealFormat.ieeeExtended53) - { - const ushort e = cast(ushort)(F.EXPMASK & (cast(ushort *)&x)[F.EXPPOS_SHORT]); - const ulong ps = *cast(ulong *)&x; - - // On Motorola 68K, infinity can have hidden bit = 1 or 0. On x86, it is always 1. - return e == F.EXPMASK && (ps & 0x7FFF_FFFF_FFFF_FFFF) == 0; - } - else static if (F.realFormat == RealFormat.ieeeQuadruple) - { - const long psLsb = (cast(long *)&x)[MANTISSA_LSB]; - const long psMsb = (cast(long *)&x)[MANTISSA_MSB]; - return (psLsb == 0) - && (psMsb & 0x7FFF_FFFF_FFFF_FFFF) == 0x7FFF_0000_0000_0000; - } - else - { - return (x < -X.max) || (X.max < x); - } -} - -/// -@nogc @safe pure nothrow unittest -{ - assert(!isInfinity(float.init)); - assert(!isInfinity(-float.init)); - assert(!isInfinity(float.nan)); - assert(!isInfinity(-float.nan)); - assert(isInfinity(float.infinity)); - assert(isInfinity(-float.infinity)); - assert(isInfinity(-1.0f / 0.0f)); -} - -@safe pure nothrow @nogc unittest -{ - // CTFE-able tests - assert(!isInfinity(double.init)); - assert(!isInfinity(-double.init)); - assert(!isInfinity(double.nan)); - assert(!isInfinity(-double.nan)); - assert(isInfinity(double.infinity)); - assert(isInfinity(-double.infinity)); - assert(isInfinity(-1.0 / 0.0)); - - assert(!isInfinity(real.init)); - assert(!isInfinity(-real.init)); - assert(!isInfinity(real.nan)); - assert(!isInfinity(-real.nan)); - assert(isInfinity(real.infinity)); - assert(isInfinity(-real.infinity)); - assert(isInfinity(-1.0L / 0.0L)); - - // Runtime tests - shared float f; - f = float.init; - assert(!isInfinity(f)); - assert(!isInfinity(-f)); - f = float.nan; - assert(!isInfinity(f)); - assert(!isInfinity(-f)); - f = float.infinity; - assert(isInfinity(f)); - assert(isInfinity(-f)); - f = (-1.0f / 0.0f); - assert(isInfinity(f)); - - shared double d; - d = double.init; - assert(!isInfinity(d)); - assert(!isInfinity(-d)); - d = double.nan; - assert(!isInfinity(d)); - assert(!isInfinity(-d)); - d = double.infinity; - assert(isInfinity(d)); - assert(isInfinity(-d)); - d = (-1.0 / 0.0); - assert(isInfinity(d)); - - shared real e; - e = real.init; - assert(!isInfinity(e)); - assert(!isInfinity(-e)); - e = real.nan; - assert(!isInfinity(e)); - assert(!isInfinity(-e)); - e = real.infinity; - assert(isInfinity(e)); - assert(isInfinity(-e)); - e = (-1.0L / 0.0L); - assert(isInfinity(e)); -} - -@nogc @safe pure nothrow unittest -{ - import std.meta : AliasSeq; - static bool foo(T)(inout T x) { return isInfinity(x); } - foreach (T; AliasSeq!(float, double, real)) - { - assert(!foo(T(3.14f))); - assert(foo(T.infinity)); - } -} - -/********************************* - * Is the binary representation of x identical to y? - */ -bool isIdentical(real x, real y) @trusted pure nothrow @nogc -{ - import std.math.traits : floatTraits, RealFormat; - - // We're doing a bitwise comparison so the endianness is irrelevant. - long* pxs = cast(long *)&x; - long* pys = cast(long *)&y; - alias F = floatTraits!(real); - static if (F.realFormat == RealFormat.ieeeDouble) - { - return pxs[0] == pys[0]; - } - else static if (F.realFormat == RealFormat.ieeeQuadruple) - { - return pxs[0] == pys[0] && pxs[1] == pys[1]; - } - else static if (F.realFormat == RealFormat.ieeeExtended) - { - ushort* pxe = cast(ushort *)&x; - ushort* pye = cast(ushort *)&y; - return pxe[4] == pye[4] && pxs[0] == pys[0]; - } - else - { - assert(0, "isIdentical not implemented"); - } -} - -/// -@safe @nogc pure nothrow unittest -{ - assert( isIdentical(0.0, 0.0)); - assert( isIdentical(1.0, 1.0)); - assert( isIdentical(real.infinity, real.infinity)); - assert( isIdentical(-real.infinity, -real.infinity)); - - assert(!isIdentical(0.0, -0.0)); - assert(!isIdentical(real.nan, -real.nan)); - assert(!isIdentical(real.infinity, -real.infinity)); -} - -/********************************* - * Return 1 if sign bit of e is set, 0 if not. - */ -int signbit(X)(X x) @nogc @trusted pure nothrow -{ - import std.math.traits : floatTraits, RealFormat; - - if (__ctfe) - { - double dval = cast(double) x; // Precision can increase or decrease but sign won't change (even NaN). - return 0 > *cast(long*) &dval; - } - - alias F = floatTraits!(X); - return ((cast(ubyte *)&x)[F.SIGNPOS_BYTE] & 0x80) != 0; -} - -/// -@nogc @safe pure nothrow unittest -{ - assert(!signbit(float.nan)); - assert(signbit(-float.nan)); - assert(!signbit(168.1234f)); - assert(signbit(-168.1234f)); - assert(!signbit(0.0f)); - assert(signbit(-0.0f)); - assert(signbit(-float.max)); - assert(!signbit(float.max)); - - assert(!signbit(double.nan)); - assert(signbit(-double.nan)); - assert(!signbit(168.1234)); - assert(signbit(-168.1234)); - assert(!signbit(0.0)); - assert(signbit(-0.0)); - assert(signbit(-double.max)); - assert(!signbit(double.max)); - - assert(!signbit(real.nan)); - assert(signbit(-real.nan)); - assert(!signbit(168.1234L)); - assert(signbit(-168.1234L)); - assert(!signbit(0.0L)); - assert(signbit(-0.0L)); - assert(signbit(-real.max)); - assert(!signbit(real.max)); -} - -version (LDC) version (AArch64) version = LDC_AArch64; - -@nogc @safe pure nothrow unittest -{ - // CTFE - static assert(!signbit(float.nan)); - version (LDC_AArch64) { pragma(msg, "signbit(-NaN) CTFE test disabled for AArch64"); } else - static assert(signbit(-float.nan)); - static assert(!signbit(168.1234f)); - static assert(signbit(-168.1234f)); - static assert(!signbit(0.0f)); - static assert(signbit(-0.0f)); - static assert(signbit(-float.max)); - static assert(!signbit(float.max)); - - static assert(!signbit(double.nan)); - version (LDC_AArch64) { pragma(msg, "signbit(-NaN) CTFE test disabled for AArch64"); } else - static assert(signbit(-double.nan)); - static assert(!signbit(168.1234)); - static assert(signbit(-168.1234)); - static assert(!signbit(0.0)); - static assert(signbit(-0.0)); - static assert(signbit(-double.max)); - static assert(!signbit(double.max)); - - static assert(!signbit(real.nan)); - version (LDC_AArch64) { pragma(msg, "signbit(-NaN) CTFE test disabled for AArch64"); } else - static assert(signbit(-real.nan)); - static assert(!signbit(168.1234L)); - static assert(signbit(-168.1234L)); - static assert(!signbit(0.0L)); - static assert(signbit(-0.0L)); - static assert(signbit(-real.max)); - static assert(!signbit(real.max)); -} - -version (LDC) version (Android) version (X86_64) version = LDC_Android_X86_64; - -/** -Params: - to = the numeric value to use - from = the sign value to use -Returns: - a value composed of to with from's sign bit. - */ -pragma(inline, true) // LDC -R copysign(R, X)(R to, X from) @trusted pure nothrow @nogc -if (isFloatingPoint!(R) && isFloatingPoint!(X)) -{ - import std.math.traits : floatTraits, RealFormat; - - if (__ctfe) - { - return signbit(to) == signbit(from) ? to : -to; - } - - version (LDC) - { - version (LDC_Android_X86_64) - { - static if (is(Unqual!R == real)) - { - // LLVM gets confused by the llvm.copysign.f128 intrinsic on x64, so call - // copysignl directly for reals instead. - return core.stdc.math.copysignl(to, cast(R) from); - } - else - return llvm_copysign(to, cast(R) from); - } - else - return llvm_copysign(to, cast(R) from); - } - else - { - ubyte* pto = cast(ubyte *)&to; - const ubyte* pfrom = cast(ubyte *)&from; - - alias T = floatTraits!(R); - alias F = floatTraits!(X); - pto[T.SIGNPOS_BYTE] &= 0x7F; - pto[T.SIGNPOS_BYTE] |= pfrom[F.SIGNPOS_BYTE] & 0x80; - return to; - } -} - -/// ditto -R copysign(R, X)(X to, R from) @trusted pure nothrow @nogc -if (isIntegral!(X) && isFloatingPoint!(R)) -{ - return copysign(cast(R) to, from); -} - -/// -@safe pure nothrow @nogc unittest -{ - assert(copysign(1.0, 1.0) == 1.0); - assert(copysign(1.0, -0.0) == -1.0); - assert(copysign(1UL, -1.0) == -1.0); - assert(copysign(-1.0, -1.0) == -1.0); - - assert(copysign(real.infinity, -1.0) == -real.infinity); - assert(copysign(real.nan, 1.0) is real.nan); - assert(copysign(-real.nan, 1.0) is real.nan); - assert(copysign(real.nan, -1.0) is -real.nan); -} - -@safe pure nothrow @nogc unittest -{ - import std.meta : AliasSeq; - - static foreach (X; AliasSeq!(float, double, real, int, long)) - { - static foreach (Y; AliasSeq!(float, double, real)) - {{ - X x = 21; - Y y = 23.8; - Y e = void; - - e = copysign(x, y); - assert(e == 21.0); - - e = copysign(-x, y); - assert(e == 21.0); - - e = copysign(x, -y); - assert(e == -21.0); - - e = copysign(-x, -y); - assert(e == -21.0); - - static if (isFloatingPoint!X) - { - e = copysign(X.nan, y); - assert(isNaN(e) && !signbit(e)); - - e = copysign(X.nan, -y); - assert(isNaN(e) && signbit(e)); - } - }} - } - // CTFE - static foreach (X; AliasSeq!(float, double, real, int, long)) - { - static foreach (Y; AliasSeq!(float, double, real)) - {{ - enum X x = 21; - enum Y y = 23.8; - - assert(21.0 == copysign(x, y)); - assert(21.0 == copysign(-x, y)); - assert(-21.0 == copysign(x, -y)); - assert(-21.0 == copysign(-x, -y)); - - static if (isFloatingPoint!X) - { - static assert(isNaN(copysign(X.nan, y)) && !signbit(copysign(X.nan, y))); - assert(isNaN(copysign(X.nan, -y)) && signbit(copysign(X.nan, -y))); - } - }} - } -} - -/********************************* -Returns `-1` if $(D x < 0), `x` if $(D x == 0), `1` if -$(D x > 0), and $(NAN) if x==$(NAN). - */ -F sgn(F)(F x) @safe pure nothrow @nogc -if (isFloatingPoint!F || isIntegral!F) -{ - // @@@TODO@@@: make this faster - return x > 0 ? 1 : x < 0 ? -1 : x; -} - -/// -@safe pure nothrow @nogc unittest -{ - assert(sgn(168.1234) == 1); - assert(sgn(-168.1234) == -1); - assert(sgn(0.0) == 0); - assert(sgn(-0.0) == 0); -} - -/** -Check whether a number is an integer power of two. - -Note that only positive numbers can be integer powers of two. This -function always return `false` if `x` is negative or zero. - -Params: - x = the number to test - -Returns: - `true` if `x` is an integer power of two. -*/ -bool isPowerOf2(X)(const X x) pure @safe nothrow @nogc -if (isNumeric!X) -{ - import std.math.exponential : frexp; - - static if (isFloatingPoint!X) - { - int exp; - const X sig = frexp(x, exp); - - return (exp != int.min) && (sig is cast(X) 0.5L); - } - else - { - static if (isSigned!X) - { - auto y = cast(typeof(x + 0))x; - return y > 0 && !(y & (y - 1)); - } - else - { - auto y = cast(typeof(x + 0u))x; - return (y & -y) > (y - 1); - } - } -} -/// -@safe unittest -{ - import std.math.exponential : pow; - - assert( isPowerOf2(1.0L)); - assert( isPowerOf2(2.0L)); - assert( isPowerOf2(0.5L)); - assert( isPowerOf2(pow(2.0L, 96))); - assert( isPowerOf2(pow(2.0L, -77))); - - assert(!isPowerOf2(-2.0L)); - assert(!isPowerOf2(-0.5L)); - assert(!isPowerOf2(0.0L)); - assert(!isPowerOf2(4.315)); - assert(!isPowerOf2(1.0L / 3.0L)); - - assert(!isPowerOf2(real.nan)); - assert(!isPowerOf2(real.infinity)); -} -/// -@safe unittest -{ - assert( isPowerOf2(1)); - assert( isPowerOf2(2)); - assert( isPowerOf2(1uL << 63)); - - assert(!isPowerOf2(-4)); - assert(!isPowerOf2(0)); - assert(!isPowerOf2(1337u)); -} - -@safe unittest -{ - import std.math.exponential : pow; - import std.meta : AliasSeq; - - enum smallP2 = pow(2.0L, -62); - enum bigP2 = pow(2.0L, 50); - enum smallP7 = pow(7.0L, -35); - enum bigP7 = pow(7.0L, 30); - - static foreach (X; AliasSeq!(float, double, real)) - {{ - immutable min_sub = X.min_normal * X.epsilon; - - foreach (x; [smallP2, min_sub, X.min_normal, .25L, 0.5L, 1.0L, - 2.0L, 8.0L, pow(2.0L, X.max_exp - 1), bigP2]) - { - assert( isPowerOf2(cast(X) x)); - assert(!isPowerOf2(cast(X)-x)); - } - - foreach (x; [0.0L, 3 * min_sub, smallP7, 0.1L, 1337.0L, bigP7, X.max, real.nan, real.infinity]) - { - assert(!isPowerOf2(cast(X) x)); - assert(!isPowerOf2(cast(X)-x)); - } - }} - - static foreach (X; AliasSeq!(byte, ubyte, short, ushort, int, uint, long, ulong)) - {{ - foreach (x; [1, 2, 4, 8, (X.max >>> 1) + 1]) - { - assert( isPowerOf2(cast(X) x)); - static if (isSigned!X) - assert(!isPowerOf2(cast(X)-x)); - } - - foreach (x; [0, 3, 5, 13, 77, X.min, X.max]) - assert(!isPowerOf2(cast(X) x)); - }} - - // CTFE - static foreach (X; AliasSeq!(float, double, real)) - {{ - enum min_sub = X.min_normal * X.epsilon; - - static foreach (x; [smallP2, min_sub, X.min_normal, .25L, 0.5L, 1.0L, - 2.0L, 8.0L, pow(2.0L, X.max_exp - 1), bigP2]) - { - static assert( isPowerOf2(cast(X) x)); - static assert(!isPowerOf2(cast(X)-x)); - } - - static foreach (x; [0.0L, 3 * min_sub, smallP7, 0.1L, 1337.0L, bigP7, X.max, real.nan, real.infinity]) - { - static assert(!isPowerOf2(cast(X) x)); - static assert(!isPowerOf2(cast(X)-x)); - } - }} - - static foreach (X; AliasSeq!(byte, ubyte, short, ushort, int, uint, long, ulong)) - {{ - static foreach (x; [1, 2, 4, 8, (X.max >>> 1) + 1]) - { - static assert( isPowerOf2(cast(X) x)); - static if (isSigned!X) - static assert(!isPowerOf2(cast(X)-x)); - } - - static foreach (x; [0, 3, 5, 13, 77, X.min, X.max]) - static assert(!isPowerOf2(cast(X) x)); - }} -} - -// Underlying format exposed through floatTraits -enum RealFormat -{ - ieeeHalf, - ieeeSingle, - ieeeDouble, - ieeeExtended, // x87 80-bit real - ieeeExtended53, // x87 real rounded to precision of double. - ibmExtended, // IBM 128-bit extended - ieeeQuadruple, -} - -// Constants used for extracting the components of the representation. -// They supplement the built-in floating point properties. -template floatTraits(T) -{ - import std.traits : Unqual; - - // EXPMASK is a ushort mask to select the exponent portion (without sign) - // EXPSHIFT is the number of bits the exponent is left-shifted by in its ushort - // EXPBIAS is the exponent bias - 1 (exp == EXPBIAS yields ×2^-1). - // EXPPOS_SHORT is the index of the exponent when represented as a ushort array. - // SIGNPOS_BYTE is the index of the sign when represented as a ubyte array. - // RECIP_EPSILON is the value such that (smallest_subnormal) * RECIP_EPSILON == T.min_normal - enum Unqual!T RECIP_EPSILON = (1/T.epsilon); - static if (T.mant_dig == 24) - { - // Single precision float - enum ushort EXPMASK = 0x7F80; - enum ushort EXPSHIFT = 7; - enum ushort EXPBIAS = 0x3F00; - enum uint EXPMASK_INT = 0x7F80_0000; - enum uint MANTISSAMASK_INT = 0x007F_FFFF; - enum realFormat = RealFormat.ieeeSingle; - version (LittleEndian) - { - enum EXPPOS_SHORT = 1; - enum SIGNPOS_BYTE = 3; - } - else - { - enum EXPPOS_SHORT = 0; - enum SIGNPOS_BYTE = 0; - } - } - else static if (T.mant_dig == 53) - { - static if (T.sizeof == 8) - { - // Double precision float, or real == double - enum ushort EXPMASK = 0x7FF0; - enum ushort EXPSHIFT = 4; - enum ushort EXPBIAS = 0x3FE0; - enum uint EXPMASK_INT = 0x7FF0_0000; - enum uint MANTISSAMASK_INT = 0x000F_FFFF; // for the MSB only - enum ulong MANTISSAMASK_LONG = 0x000F_FFFF_FFFF_FFFF; - enum realFormat = RealFormat.ieeeDouble; - version (LittleEndian) - { - enum EXPPOS_SHORT = 3; - enum SIGNPOS_BYTE = 7; - } - else - { - enum EXPPOS_SHORT = 0; - enum SIGNPOS_BYTE = 0; - } - } - else static if (T.sizeof == 12) - { - // Intel extended real80 rounded to double - enum ushort EXPMASK = 0x7FFF; - enum ushort EXPSHIFT = 0; - enum ushort EXPBIAS = 0x3FFE; - enum realFormat = RealFormat.ieeeExtended53; - version (LittleEndian) - { - enum EXPPOS_SHORT = 4; - enum SIGNPOS_BYTE = 9; - } - else - { - enum EXPPOS_SHORT = 0; - enum SIGNPOS_BYTE = 0; - } - } - else - static assert(false, "No traits support for " ~ T.stringof); - } - else static if (T.mant_dig == 64) - { - // Intel extended real80 - enum ushort EXPMASK = 0x7FFF; - enum ushort EXPSHIFT = 0; - enum ushort EXPBIAS = 0x3FFE; - enum realFormat = RealFormat.ieeeExtended; - version (LittleEndian) - { - enum EXPPOS_SHORT = 4; - enum SIGNPOS_BYTE = 9; - } - else - { - enum EXPPOS_SHORT = 0; - enum SIGNPOS_BYTE = 0; - } - } - else static if (T.mant_dig == 113) - { - // Quadruple precision float - enum ushort EXPMASK = 0x7FFF; - enum ushort EXPSHIFT = 0; - enum ushort EXPBIAS = 0x3FFE; - enum realFormat = RealFormat.ieeeQuadruple; - version (LittleEndian) - { - enum EXPPOS_SHORT = 7; - enum SIGNPOS_BYTE = 15; - } - else - { - enum EXPPOS_SHORT = 0; - enum SIGNPOS_BYTE = 0; - } - } - else static if (T.mant_dig == 106) - { - // IBM Extended doubledouble - enum ushort EXPMASK = 0x7FF0; - enum ushort EXPSHIFT = 4; - enum realFormat = RealFormat.ibmExtended; - - // For IBM doubledouble the larger magnitude double comes first. - // It's really a double[2] and arrays don't index differently - // between little and big-endian targets. - enum DOUBLEPAIR_MSB = 0; - enum DOUBLEPAIR_LSB = 1; - - // The exponent/sign byte is for most significant part. - version (LittleEndian) - { - enum EXPPOS_SHORT = 3; - enum SIGNPOS_BYTE = 7; - } - else - { - enum EXPPOS_SHORT = 0; - enum SIGNPOS_BYTE = 0; - } - } - else - static assert(false, "No traits support for " ~ T.stringof); -} - -// These apply to all floating-point types -version (LittleEndian) -{ - enum MANTISSA_LSB = 0; - enum MANTISSA_MSB = 1; -} -else -{ - enum MANTISSA_LSB = 1; - enum MANTISSA_MSB = 0; -} diff --git a/phobos/std/math/trigonometry.d b/phobos/std/math/trigonometry.d deleted file mode 100644 index 5faf649..0000000 --- a/phobos/std/math/trigonometry.d +++ /dev/null @@ -1,1442 +0,0 @@ -// Written in the D programming language. - -/** -This is a submodule of $(MREF std, math). - -It contains several trigonometric functions. - -Copyright: Copyright The D Language Foundation 2000 - 2011. - D implementations of tan, atan, and atan2 functions are based on the - CEPHES math library, which is Copyright (C) 2001 Stephen L. Moshier - $(LT)steve@moshier.net$(GT) and are incorporated herein by permission - of the author. The author reserves the right to distribute this - material elsewhere under different copying permissions. - These modifications are distributed here under the following terms: -License: $(HTTP www.boost.org/LICENSE_1_0.txt, Boost License 1.0). -Authors: $(HTTP digitalmars.com, Walter Bright), Don Clugston, - Conversion of CEPHES math library to D by Iain Buclaw and David Nadlinger -Source: $(PHOBOSSRC std/math/trigonometry.d) - -Macros: - TABLE_SV = - - $0
Special Values
- SVH = $(TR $(TH $1) $(TH $2)) - SV = $(TR $(TD $1) $(TD $2)) - TH3 = $(TR $(TH $1) $(TH $2) $(TH $3)) - TD3 = $(TR $(TD $1) $(TD $2) $(TD $3)) - TABLE_DOMRG = - $(SVH Domain X, Range Y) - $(SV $1, $2) -
- DOMAIN=$1 - RANGE=$1 - POWER = $1$2 - NAN = $(RED NAN) - PLUSMN = ± - INFIN = ∞ - PLUSMNINF = ±∞ - */ - -module std.math.trigonometry; - -static import core.math; - -version (D_InlineAsm_X86) version = InlineAsm_X86_Any; -version (D_InlineAsm_X86_64) version = InlineAsm_X86_Any; - -version (LDC) version (CRuntime_Microsoft) version = LDC_MSVCRT; - -version (LDC_MSVCRT) {} -else version (Android) {} -else version (InlineAsm_X86_Any) version = InlineAsm_X87; -version (InlineAsm_X87) -{ - static assert(real.mant_dig == 64); - version (CRuntime_Microsoft) version = InlineAsm_X87_MSVC; -} - -/*********************************** - * Returns cosine of x. x is in radians. - * - * $(TABLE_SV - * $(TR $(TH x) $(TH cos(x)) $(TH invalid?)) - * $(TR $(TD $(NAN)) $(TD $(NAN)) $(TD yes) ) - * $(TR $(TD $(PLUSMN)$(INFIN)) $(TD $(NAN)) $(TD yes) ) - * ) - * Bugs: - * Results are undefined if |x| >= $(POWER 2,64). - */ -pragma(inline, true) -real cos(real x) @safe pure nothrow @nogc { return core.math.cos(x); } -///ditto -pragma(inline, true) -double cos(double x) @safe pure nothrow @nogc { return core.math.cos(x); } -///ditto -pragma(inline, true) -float cos(float x) @safe pure nothrow @nogc { return core.math.cos(x); } - -/// -@safe unittest -{ - import std.math.operations : isClose; - - assert(cos(0.0) == 1.0); - assert(cos(1.0).isClose(0.5403023059)); - assert(cos(3.0).isClose(-0.9899924966)); -} - -@safe unittest -{ - real function(real) pcos = &cos; - assert(pcos != null); -} - -@safe pure nothrow @nogc unittest -{ - import std.math.algebraic : fabs; - - float f = cos(-2.0f); - assert(fabs(f - -0.416147f) < .00001); - - double d = cos(-2.0); - assert(fabs(d - -0.416147f) < .00001); - - real r = cos(-2.0L); - assert(fabs(r - -0.416147f) < .00001); -} - -/*********************************** - * Returns $(HTTP en.wikipedia.org/wiki/Sine, sine) of x. x is in $(HTTP en.wikipedia.org/wiki/Radian, radians). - * - * $(TABLE_SV - * $(TH3 x , sin(x) , invalid?) - * $(TD3 $(NAN) , $(NAN) , yes ) - * $(TD3 $(PLUSMN)0.0, $(PLUSMN)0.0, no ) - * $(TD3 $(PLUSMNINF), $(NAN) , yes ) - * ) - * - * Params: - * x = angle in radians (not degrees) - * Returns: - * sine of x - * See_Also: - * $(MYREF cos), $(MYREF tan), $(MYREF asin) - * Bugs: - * Results are undefined if |x| >= $(POWER 2,64). - */ -pragma(inline, true) -real sin(real x) @safe pure nothrow @nogc { return core.math.sin(x); } -///ditto -pragma(inline, true) -double sin(double x) @safe pure nothrow @nogc { return core.math.sin(x); } -///ditto -pragma(inline, true) -float sin(float x) @safe pure nothrow @nogc { return core.math.sin(x); } - -/// -@safe unittest -{ - import std.math.constants : PI; - import std.stdio : writefln; - - void someFunc() - { - real x = 30.0; - auto result = sin(x * (PI / 180)); // convert degrees to radians - writefln("The sine of %s degrees is %s", x, result); - } -} - -@safe unittest -{ - real function(real) psin = &sin; - assert(psin != null); -} - -@safe pure nothrow @nogc unittest -{ - import std.math.algebraic : fabs; - - float f = sin(-2.0f); - assert(fabs(f - -0.909297f) < .00001); - - double d = sin(-2.0); - assert(fabs(d - -0.909297f) < .00001); - - real r = sin(-2.0L); - assert(fabs(r - -0.909297f) < .00001); -} - -/**************************************************************************** - * Returns tangent of x. x is in radians. - * - * $(TABLE_SV - * $(TR $(TH x) $(TH tan(x)) $(TH invalid?)) - * $(TR $(TD $(NAN)) $(TD $(NAN)) $(TD yes)) - * $(TR $(TD $(PLUSMN)0.0) $(TD $(PLUSMN)0.0) $(TD no)) - * $(TR $(TD $(PLUSMNINF)) $(TD $(NAN)) $(TD yes)) - * ) - */ -pragma(inline, true) -real tan(real x) @safe pure nothrow @nogc -{ - version (InlineAsm_X87) - { - if (!__ctfe) - return tanAsm(x); - } - return tanImpl(x); -} - -/// ditto -pragma(inline, true) -double tan(double x) @safe pure nothrow @nogc { return __ctfe ? cast(double) tan(cast(real) x) : tanImpl(x); } - -/// ditto -pragma(inline, true) -float tan(float x) @safe pure nothrow @nogc { return __ctfe ? cast(float) tan(cast(real) x) : tanImpl(x); } - -/// -@safe unittest -{ - import std.math.operations : isClose; - import std.math.traits : isIdentical; - import std.math.constants : PI; - import std.math.algebraic : sqrt; - - assert(isIdentical(tan(0.0), 0.0)); - assert(tan(PI).isClose(0, 0.0, 1e-10)); - assert(tan(PI / 3).isClose(sqrt(3.0))); -} - -// LDC: pass `real.nan` as extra param -version (InlineAsm_X87) -private real tanAsm(real x, real nan = real.nan) @trusted pure nothrow @nogc -{ - version (LDC) {} else - { - // Separating `return real.nan` from the asm block on LDC produces unintended - // behaviour as additional instructions are generated, invalidating the asm - // logic inside the previous block. To circumvent this, we can push rnan - // manually by creating an immutable variable in the stack. - immutable rnan = real.nan; - } - - version (X86) - { - asm pure nothrow @nogc - { - naked ; - fld real ptr [ESP+4] ; // load theta - fxam ; // test for oddball values - fstsw AX ; - sahf ; - jc trigerr ; // x is NAN, infinity, or empty - // 387's can handle subnormals -SC18: fptan ; - fstsw AX ; - sahf ; - jnp Clear1 ; // C2 = 1 (x is out of range) - - // Do argument reduction to bring x into range - fldpi ; - fxch ; -SC17: fprem1 ; - fstsw AX ; - sahf ; - jp SC17 ; - fstp ST(1) ; // remove pi from stack - jmp SC18 ; - -trigerr: - jnp Lret ; // if theta is NAN, return theta - fstp ST(0) ; // dump theta - fld real ptr [ESP+16] ; // load nan param - jmp Lret ; -Clear1: - fstp ST(0) ; // dump X, which is always 1 -Lret: - ret 2 * x.sizeof ; - } - } - else version (X86_64) - { - version (Win64) - { - asm pure nothrow @nogc - { - naked ; - fld real ptr [RCX] ; // load theta - } - } - else - { - asm pure nothrow @nogc - { - naked ; - fld real ptr [RSP+8]; // load theta - } - } - asm pure nothrow @nogc - { - fxam ; // test for oddball values - fstsw AX ; - test AH,1 ; - jnz trigerr ; // x is NAN, infinity, or empty - // 387's can handle subnormals -SC18: fptan ; - fstsw AX ; - test AH,4 ; - jz Clear1 ; // C2 = 1 (x is out of range) - - // Do argument reduction to bring x into range - fldpi ; - fxch ; -SC17: fprem1 ; - fstsw AX ; - test AH,4 ; - jnz SC17 ; - fstp ST(1) ; // remove pi from stack - jmp SC18 ; - -trigerr: - test AH,4 ; - jz Lret ; // if theta is NAN, return theta - fstp ST(0) ; // dump theta - } - // load nan param - version (Win64) - asm pure nothrow @nogc { fld real ptr [RDX]; } - else - asm pure nothrow @nogc { fld real ptr [RSP+24]; } - asm pure nothrow @nogc - { - jmp Lret ; -Clear1: - fstp ST(0) ; // dump X, which is always 1 -Lret: - ret ; - } - } - else - static assert(0); -} - -private T tanImpl(T)(T x) @safe pure nothrow @nogc -{ - import std.math.traits : floatTraits, RealFormat; - import std.math.constants : PI, PI_4; - import std.math.rounding : floor; - import std.math.algebraic : poly; - import std.math.traits : isInfinity, isNaN, signbit; - - // Coefficients for tan(x) and PI/4 split into three parts. - enum realFormat = floatTraits!T.realFormat; - static if (realFormat == RealFormat.ieeeQuadruple) - { - static immutable T[6] P = [ - 2.883414728874239697964612246732416606301E10L, - -2.307030822693734879744223131873392503321E9L, - 5.160188250214037865511600561074819366815E7L, - -4.249691853501233575668486667664718192660E5L, - 1.272297782199996882828849455156962260810E3L, - -9.889929415807650724957118893791829849557E-1L - ]; - static immutable T[7] Q = [ - 8.650244186622719093893836740197250197602E10L, - -4.152206921457208101480801635640958361612E10L, - 2.758476078803232151774723646710890525496E9L, - -5.733709132766856723608447733926138506824E7L, - 4.529422062441341616231663543669583527923E5L, - -1.317243702830553658702531997959756728291E3L, - 1.0 - ]; - - enum T P1 = - 7.853981633974483067550664827649598009884357452392578125E-1L; - enum T P2 = - 2.8605943630549158983813312792950660807511260829685741796657E-18L; - enum T P3 = - 2.1679525325309452561992610065108379921905808E-35L; - } - else static if (realFormat == RealFormat.ieeeExtended || - realFormat == RealFormat.ieeeDouble) - { - static immutable T[3] P = [ - -1.7956525197648487798769E7L, - 1.1535166483858741613983E6L, - -1.3093693918138377764608E4L, - ]; - static immutable T[5] Q = [ - -5.3869575592945462988123E7L, - 2.5008380182335791583922E7L, - -1.3208923444021096744731E6L, - 1.3681296347069295467845E4L, - 1.0000000000000000000000E0L, - ]; - - enum T P1 = 7.853981554508209228515625E-1L; - enum T P2 = 7.946627356147928367136046290398E-9L; - enum T P3 = 3.061616997868382943065164830688E-17L; - } - else static if (realFormat == RealFormat.ieeeSingle) - { - static immutable T[6] P = [ - 3.33331568548E-1, - 1.33387994085E-1, - 5.34112807005E-2, - 2.44301354525E-2, - 3.11992232697E-3, - 9.38540185543E-3, - ]; - - enum T P1 = 0.78515625; - enum T P2 = 2.4187564849853515625E-4; - enum T P3 = 3.77489497744594108E-8; - } - else - static assert(0, "no coefficients for tan()"); - - // Special cases. - if (x == cast(T) 0.0 || isNaN(x)) - return x; - if (isInfinity(x)) - return T.nan; - - // Make argument positive but save the sign. - bool sign = false; - if (signbit(x)) - { - sign = true; - x = -x; - } - - // Compute x mod PI/4. - static if (realFormat == RealFormat.ieeeSingle) - { - enum T FOPI = 4 / PI; - int j = cast(int) (FOPI * x); - T y = j; - T z; - } - else - { - T y = floor(x / cast(T) PI_4); - // Strip high bits of integer part. - enum T highBitsFactor = (realFormat == RealFormat.ieeeDouble ? 0x1p3 : 0x1p4); - enum T highBitsInv = 1.0 / highBitsFactor; - T z = y * highBitsInv; - // Compute y - 2^numHighBits * (y / 2^numHighBits). - z = y - highBitsFactor * floor(z); - - // Integer and fraction part modulo one octant. - int j = cast(int)(z); - } - - // Map zeros and singularities to origin. - if (j & 1) - { - j += 1; - y += cast(T) 1.0; - } - - z = ((x - y * P1) - y * P2) - y * P3; - const T zz = z * z; - - enum T zzThreshold = (realFormat == RealFormat.ieeeSingle ? 1.0e-4L : - realFormat == RealFormat.ieeeDouble ? 1.0e-14L : 1.0e-20L); - if (zz > zzThreshold) - { - static if (realFormat == RealFormat.ieeeSingle) - y = z + z * (zz * poly(zz, P)); - else - y = z + z * (zz * poly(zz, P) / poly(zz, Q)); - } - else - y = z; - - if (j & 2) - y = (cast(T) -1.0) / y; - - return (sign) ? -y : y; -} - -@safe @nogc nothrow unittest -{ - static void testTan(T)() - { - import std.math.operations : CommonDefaultFor, isClose, NaN; - import std.math.traits : isIdentical, isNaN; - import std.math.constants : PI, PI_4; - - // Âą0 - const T zero = 0.0; - assert(isIdentical(tan(zero), zero)); - assert(isIdentical(tan(-zero), -zero)); - // ±∞ - const T inf = T.infinity; - assert(isNaN(tan(inf))); - assert(isNaN(tan(-inf))); - // NaN - const T specialNaN = NaN(0x0123L); - assert(isIdentical(tan(specialNaN), specialNaN)); - - static immutable T[2][] vals = - [ - // angle, tan - [ .5, .546302489843790513255L], - [ 1, 1.55740772465490223050L], - [ 1.5, 14.1014199471717193876L], - [ 2, -2.18503986326151899164L], - [ 2.5,-.747022297238660279355L], - [ 3, -.142546543074277805295L], - [ 3.5, .374585640158594666330L], - [ 4, 1.15782128234957758313L], - [ 4.5, 4.63733205455118446831L], - [ 5, -3.38051500624658563698L], - [ 5.5,-.995584052213885017701L], - [ 6, -.291006191384749157053L], - [ 6.5, .220277200345896811825L], - [ 10, .648360827459086671259L], - - // special angles - [ PI_4, 1], - //[ PI_2, T.infinity], // PI_2 is not _exactly_ pi/2. - [ 3*PI_4, -1], - [ PI, 0], - [ 5*PI_4, 1], - //[ 3*PI_2, -T.infinity], - [ 7*PI_4, -1], - [ 2*PI, 0], - ]; - - foreach (ref val; vals) - { - T x = val[0]; - T r = val[1]; - T t = tan(x); - - //printf("tan(%Lg) = %Lg, should be %Lg\n", cast(real) x, cast(real) t, cast(real) r); - assert(isClose(r, t, CommonDefaultFor!(T,T), CommonDefaultFor!(T,T))); - - x = -x; - r = -r; - t = tan(x); - //printf("tan(%Lg) = %Lg, should be %Lg\n", cast(real) x, cast(real) t, cast(real) r); - assert(isClose(r, t, CommonDefaultFor!(T,T), CommonDefaultFor!(T,T))); - } - } - - import std.meta : AliasSeq; - foreach (T; AliasSeq!(real, double, float)) - testTan!T(); - - import std.math.operations : isClose; - import std.math.constants : PI; - import std.math.algebraic : sqrt; - assert(isClose(tan(PI / 3), sqrt(3.0L), real.sizeof > double.sizeof ? 1e-15 : 1e-14)); -} - -@safe pure nothrow @nogc unittest -{ - import std.math.algebraic : fabs; - import std.math.traits : isNaN; - - float f = tan(-2.0f); - assert(fabs(f - 2.18504f) < .00001); - - double d = tan(-2.0); - assert(fabs(d - 2.18504f) < .00001); - - real r = tan(-2.0L); - assert(fabs(r - 2.18504f) < .00001); - - // Verify correct behavior for large inputs - assert(!isNaN(tan(0x1p63))); - assert(!isNaN(tan(-0x1p63))); - static if (real.mant_dig >= 64) - { - assert(!isNaN(tan(0x1p300L))); - assert(!isNaN(tan(-0x1p300L))); - } -} - -/*************** - * Calculates the arc cosine of x, - * returning a value ranging from 0 to $(PI). - * - * $(TABLE_SV - * $(TR $(TH x) $(TH acos(x)) $(TH invalid?)) - * $(TR $(TD $(GT)1.0) $(TD $(NAN)) $(TD yes)) - * $(TR $(TD $(LT)-1.0) $(TD $(NAN)) $(TD yes)) - * $(TR $(TD $(NAN)) $(TD $(NAN)) $(TD yes)) - * ) - */ -real acos(real x) @safe pure nothrow @nogc -{ - import core.math : sqrt; - - return atan2(sqrt(1-x*x), x); -} - -/// ditto -double acos(double x) @safe pure nothrow @nogc { return acos(cast(real) x); } - -/// ditto -float acos(float x) @safe pure nothrow @nogc { return acos(cast(real) x); } - -/// -@safe unittest -{ - import std.math.operations : isClose; - import std.math.traits : isNaN; - import std.math.constants : PI; - - assert(acos(0.0).isClose(1.570796327)); - assert(acos(0.5).isClose(PI / 3)); - assert(acos(PI).isNaN); -} - -@safe @nogc nothrow unittest -{ - import std.math.operations : isClose; - import std.math.constants : PI; - - assert(isClose(acos(0.5), PI / 3, real.sizeof > double.sizeof ? 1e-15 : 1e-14)); -} - -/*************** - * Calculates the arc sine of x, - * returning a value ranging from -$(PI)/2 to $(PI)/2. - * - * $(TABLE_SV - * $(TR $(TH x) $(TH asin(x)) $(TH invalid?)) - * $(TR $(TD $(PLUSMN)0.0) $(TD $(PLUSMN)0.0) $(TD no)) - * $(TR $(TD $(GT)1.0) $(TD $(NAN)) $(TD yes)) - * $(TR $(TD $(LT)-1.0) $(TD $(NAN)) $(TD yes)) - * ) - */ -real asin(real x) @safe pure nothrow @nogc -{ - import core.math : sqrt; - - return atan2(x, sqrt(1-x*x)); -} - -/// ditto -double asin(double x) @safe pure nothrow @nogc { return asin(cast(real) x); } - -/// ditto -float asin(float x) @safe pure nothrow @nogc { return asin(cast(real) x); } - -/// -@safe unittest -{ - import std.math.operations : isClose; - import std.math.traits : isIdentical, isNaN; - import std.math.constants : PI; - - assert(isIdentical(asin(0.0), 0.0)); - assert(asin(0.5).isClose(PI / 6)); - assert(asin(PI).isNaN); -} - -@safe @nogc nothrow unittest -{ - import std.math.operations : isClose; - import std.math.constants : PI; - - assert(isClose(asin(0.5), PI / 6, real.sizeof > double.sizeof ? 1e-15 : 1e-14)); -} - -/*************** - * Calculates the arc tangent of x, - * returning a value ranging from -$(PI)/2 to $(PI)/2. - * - * $(TABLE_SV - * $(TR $(TH x) $(TH atan(x)) $(TH invalid?)) - * $(TR $(TD $(PLUSMN)0.0) $(TD $(PLUSMN)0.0) $(TD no)) - * $(TR $(TD $(PLUSMN)$(INFIN)) $(TD $(NAN)) $(TD yes)) - * ) - */ -pragma(inline, true) -real atan(real x) @safe pure nothrow @nogc -{ - version (InlineAsm_X87) - { - if (!__ctfe) - return atan2Asm(x, 1.0L); - } - return atanImpl(x); -} - -/// ditto -pragma(inline, true) -double atan(double x) @safe pure nothrow @nogc { return __ctfe ? cast(double) atan(cast(real) x) : atanImpl(x); } - -/// ditto -pragma(inline, true) -float atan(float x) @safe pure nothrow @nogc { return __ctfe ? cast(float) atan(cast(real) x) : atanImpl(x); } - -/// -@safe unittest -{ - import std.math.operations : isClose; - import std.math.traits : isIdentical; - import std.math.constants : PI; - import std.math.algebraic : sqrt; - - assert(isIdentical(atan(0.0), 0.0)); - assert(atan(sqrt(3.0)).isClose(PI / 3)); -} - -private T atanImpl(T)(T x) @safe pure nothrow @nogc -{ - import std.math.traits : floatTraits, RealFormat, copysign, isInfinity, signbit; - import std.math.constants : PI_2, PI_4; - import std.math.algebraic : poly; - - // Coefficients for atan(x) - enum realFormat = floatTraits!T.realFormat; - static if (realFormat == RealFormat.ieeeQuadruple) - { - static immutable T[9] P = [ - -6.880597774405940432145577545328795037141E2L, - -2.514829758941713674909996882101723647996E3L, - -3.696264445691821235400930243493001671932E3L, - -2.792272753241044941703278827346430350236E3L, - -1.148164399808514330375280133523543970854E3L, - -2.497759878476618348858065206895055957104E2L, - -2.548067867495502632615671450650071218995E1L, - -8.768423468036849091777415076702113400070E-1L, - -6.635810778635296712545011270011752799963E-4L - ]; - static immutable T[9] Q = [ - 2.064179332321782129643673263598686441900E3L, - 8.782996876218210302516194604424986107121E3L, - 1.547394317752562611786521896296215170819E4L, - 1.458510242529987155225086911411015961174E4L, - 7.928572347062145288093560392463784743935E3L, - 2.494680540950601626662048893678584497900E3L, - 4.308348370818927353321556740027020068897E2L, - 3.566239794444800849656497338030115886153E1L, - 1.0 - ]; - } - else static if (realFormat == RealFormat.ieeeExtended) - { - static immutable T[5] P = [ - -5.0894116899623603312185E1L, - -9.9988763777265819915721E1L, - -6.3976888655834347413154E1L, - -1.4683508633175792446076E1L, - -8.6863818178092187535440E-1L, - ]; - static immutable T[6] Q = [ - 1.5268235069887081006606E2L, - 3.9157570175111990631099E2L, - 3.6144079386152023162701E2L, - 1.4399096122250781605352E2L, - 2.2981886733594175366172E1L, - 1.0000000000000000000000E0L, - ]; - } - else static if (realFormat == RealFormat.ieeeDouble) - { - static immutable T[5] P = [ - -6.485021904942025371773E1L, - -1.228866684490136173410E2L, - -7.500855792314704667340E1L, - -1.615753718733365076637E1L, - -8.750608600031904122785E-1L, - ]; - static immutable T[6] Q = [ - 1.945506571482613964425E2L, - 4.853903996359136964868E2L, - 4.328810604912902668951E2L, - 1.650270098316988542046E2L, - 2.485846490142306297962E1L, - 1.000000000000000000000E0L, - ]; - - enum T MOREBITS = 6.123233995736765886130E-17L; - } - else static if (realFormat == RealFormat.ieeeSingle) - { - static immutable T[4] P = [ - -3.33329491539E-1, - 1.99777106478E-1, - -1.38776856032E-1, - 8.05374449538E-2, - ]; - } - else - static assert(0, "no coefficients for atan()"); - - // tan(PI/8) - enum T TAN_PI_8 = 0.414213562373095048801688724209698078569672L; - // tan(3 * PI/8) - enum T TAN3_PI_8 = 2.414213562373095048801688724209698078569672L; - - // Special cases. - if (x == cast(T) 0.0) - return x; - if (isInfinity(x)) - return copysign(cast(T) PI_2, x); - - // Make argument positive but save the sign. - bool sign = false; - if (signbit(x)) - { - sign = true; - x = -x; - } - - static if (realFormat == RealFormat.ieeeDouble) // special case for double precision - { - short flag = 0; - T y; - if (x > TAN3_PI_8) - { - y = PI_2; - flag = 1; - x = -(1.0 / x); - } - else if (x <= 0.66) - { - y = 0.0; - } - else - { - y = PI_4; - flag = 2; - x = (x - 1.0)/(x + 1.0); - } - - T z = x * x; - z = z * poly(z, P) / poly(z, Q); - z = x * z + x; - if (flag == 2) - z += 0.5 * MOREBITS; - else if (flag == 1) - z += MOREBITS; - y = y + z; - } - else - { - // Range reduction. - T y; - if (x > TAN3_PI_8) - { - y = PI_2; - x = -((cast(T) 1.0) / x); - } - else if (x > TAN_PI_8) - { - y = PI_4; - x = (x - cast(T) 1.0)/(x + cast(T) 1.0); - } - else - y = 0.0; - - // Rational form in x^^2. - const T z = x * x; - static if (realFormat == RealFormat.ieeeSingle) - y += poly(z, P) * z * x + x; - else - y = y + (poly(z, P) / poly(z, Q)) * z * x + x; - } - - return (sign) ? -y : y; -} - -@safe @nogc nothrow unittest -{ - static void testAtan(T)() - { - import std.math.operations : CommonDefaultFor, isClose, NaN; - import std.math.traits : isIdentical; - import std.math.constants : PI_2, PI_4; - - // Âą0 - const T zero = 0.0; - assert(isIdentical(atan(zero), zero)); - assert(isIdentical(atan(-zero), -zero)); - // ±∞ - const T inf = T.infinity; - assert(isClose(atan(inf), cast(T) PI_2)); - assert(isClose(atan(-inf), cast(T) -PI_2)); - // NaN - const T specialNaN = NaN(0x0123L); - assert(isIdentical(atan(specialNaN), specialNaN)); - - static immutable T[2][] vals = - [ - // x, atan(x) - [ 0.25, 0.244978663126864154172L ], - [ 0.5, 0.463647609000806116214L ], - [ 1, PI_4 ], - [ 1.5, 0.982793723247329067985L ], - [ 10, 1.471127674303734591852L ], - ]; - - foreach (ref val; vals) - { - T x = val[0]; - T r = val[1]; - T a = atan(x); - - //printf("atan(%Lg) = %Lg, should be %Lg\n", cast(real) x, cast(real) a, cast(real) r); - assert(isClose(r, a, CommonDefaultFor!(T,T), CommonDefaultFor!(T,T))); - - x = -x; - r = -r; - a = atan(x); - //printf("atan(%Lg) = %Lg, should be %Lg\n", cast(real) x, cast(real) a, cast(real) r); - assert(isClose(r, a, CommonDefaultFor!(T,T), CommonDefaultFor!(T,T))); - } - } - - import std.meta : AliasSeq; - foreach (T; AliasSeq!(real, double, float)) - testAtan!T(); - - import std.math.operations : isClose; - import std.math.algebraic : sqrt; - import std.math.constants : PI; - assert(isClose(atan(sqrt(3.0L)), PI / 3, real.sizeof > double.sizeof ? 1e-15 : 1e-14)); -} - -/*************** - * Calculates the arc tangent of y / x, - * returning a value ranging from -$(PI) to $(PI). - * - * $(TABLE_SV - * $(TR $(TH y) $(TH x) $(TH atan(y, x))) - * $(TR $(TD $(NAN)) $(TD anything) $(TD $(NAN)) ) - * $(TR $(TD anything) $(TD $(NAN)) $(TD $(NAN)) ) - * $(TR $(TD $(PLUSMN)0.0) $(TD $(GT)0.0) $(TD $(PLUSMN)0.0) ) - * $(TR $(TD $(PLUSMN)0.0) $(TD +0.0) $(TD $(PLUSMN)0.0) ) - * $(TR $(TD $(PLUSMN)0.0) $(TD $(LT)0.0) $(TD $(PLUSMN)$(PI))) - * $(TR $(TD $(PLUSMN)0.0) $(TD -0.0) $(TD $(PLUSMN)$(PI))) - * $(TR $(TD $(GT)0.0) $(TD $(PLUSMN)0.0) $(TD $(PI)/2) ) - * $(TR $(TD $(LT)0.0) $(TD $(PLUSMN)0.0) $(TD -$(PI)/2) ) - * $(TR $(TD $(GT)0.0) $(TD $(INFIN)) $(TD $(PLUSMN)0.0) ) - * $(TR $(TD $(PLUSMN)$(INFIN)) $(TD anything) $(TD $(PLUSMN)$(PI)/2)) - * $(TR $(TD $(GT)0.0) $(TD -$(INFIN)) $(TD $(PLUSMN)$(PI)) ) - * $(TR $(TD $(PLUSMN)$(INFIN)) $(TD $(INFIN)) $(TD $(PLUSMN)$(PI)/4)) - * $(TR $(TD $(PLUSMN)$(INFIN)) $(TD -$(INFIN)) $(TD $(PLUSMN)3$(PI)/4)) - * ) - */ -pragma(inline, true) -real atan2(real y, real x) @trusted pure nothrow @nogc // TODO: @safe -{ - version (InlineAsm_X87) - { - if (!__ctfe) - return atan2Asm(y, x); - } - return atan2Impl(y, x); -} - -/// ditto -pragma(inline, true) -double atan2(double y, double x) @safe pure nothrow @nogc -{ - return __ctfe ? cast(double) atan2(cast(real) y, cast(real) x) : atan2Impl(y, x); -} - -/// ditto -pragma(inline, true) -float atan2(float y, float x) @safe pure nothrow @nogc -{ - return __ctfe ? cast(float) atan2(cast(real) y, cast(real) x) : atan2Impl(y, x); -} - -/// -@safe unittest -{ - import std.math.operations : isClose; - import std.math.constants : PI; - import std.math.algebraic : sqrt; - - assert(atan2(1.0, sqrt(3.0)).isClose(PI / 6)); -} - -version (InlineAsm_X87) -private real atan2Asm(real y, real x) @trusted pure nothrow @nogc -{ - version (Win64) - { - asm pure nothrow @nogc { - naked; - fld real ptr [RCX]; // y - fld real ptr [RDX]; // x - fpatan; - ret; - } - } - else - { - asm pure nothrow @nogc { - fld y; - fld x; - fpatan; - } - } -} - -private T atan2Impl(T)(T y, T x) @safe pure nothrow @nogc -{ - import std.math.traits : copysign, isInfinity, isNaN, signbit; - import std.math.constants : PI, PI_2, PI_4; - - // Special cases. - if (isNaN(x) || isNaN(y)) - return T.nan; - if (y == cast(T) 0.0) - { - if (x >= 0 && !signbit(x)) - return copysign(0, y); - else - return copysign(cast(T) PI, y); - } - if (x == cast(T) 0.0) - return copysign(cast(T) PI_2, y); - if (isInfinity(x)) - { - if (signbit(x)) - { - if (isInfinity(y)) - return copysign(3 * cast(T) PI_4, y); - else - return copysign(cast(T) PI, y); - } - else - { - if (isInfinity(y)) - return copysign(cast(T) PI_4, y); - else - return copysign(cast(T) 0.0, y); - } - } - if (isInfinity(y)) - return copysign(cast(T) PI_2, y); - - // Call atan and determine the quadrant. - T z = atan(y / x); - - if (signbit(x)) - { - if (signbit(y)) - z = z - cast(T) PI; - else - z = z + cast(T) PI; - } - - if (z == cast(T) 0.0) - return copysign(z, y); - - return z; -} - -@safe @nogc nothrow unittest -{ - static void testAtan2(T)() - { - import std.math.operations : isClose; - import std.math.traits : isIdentical, isNaN; - import std.math.constants : PI, PI_2, PI_4; - - // NaN - const T nan = T.nan; - assert(isNaN(atan2(nan, cast(T) 1))); - assert(isNaN(atan2(cast(T) 1, nan))); - - const T inf = T.infinity; - static immutable T[3][] vals = - [ - // y, x, atan2(y, x) - - // Âą0 - [ 0.0, 1.0, 0.0 ], - [ -0.0, 1.0, -0.0 ], - [ 0.0, 0.0, 0.0 ], - [ -0.0, 0.0, -0.0 ], - [ 0.0, -1.0, PI ], - [ -0.0, -1.0, -PI ], - [ 0.0, -0.0, PI ], - [ -0.0, -0.0, -PI ], - [ 1.0, 0.0, PI_2 ], - [ 1.0, -0.0, PI_2 ], - [ -1.0, 0.0, -PI_2 ], - [ -1.0, -0.0, -PI_2 ], - - // ±∞ - [ 1.0, inf, 0.0 ], - [ -1.0, inf, -0.0 ], - [ 1.0, -inf, PI ], - [ -1.0, -inf, -PI ], - [ inf, 1.0, PI_2 ], - [ inf, -1.0, PI_2 ], - [ -inf, 1.0, -PI_2 ], - [ -inf, -1.0, -PI_2 ], - [ inf, inf, PI_4 ], - [ -inf, inf, -PI_4 ], - [ inf, -inf, 3 * PI_4 ], - [ -inf, -inf, -3 * PI_4 ], - - [ 1.0, 1.0, PI_4 ], - [ -2.0, 2.0, -PI_4 ], - [ 3.0, -3.0, 3 * PI_4 ], - [ -4.0, -4.0, -3 * PI_4 ], - - [ 0.75, 0.25, 1.2490457723982544258299L ], - [ -0.5, 0.375, -0.9272952180016122324285L ], - [ 0.5, -0.125, 1.8157749899217607734034L ], - [ -0.75, -0.5, -2.1587989303424641704769L ], - ]; - - foreach (ref val; vals) - { - const T y = val[0]; - const T x = val[1]; - const T r = val[2]; - const T a = atan2(y, x); - - //printf("atan2(%Lg, %Lg) = %Lg, should be %Lg\n", cast(real) y, cast(real) x, cast(real) a, cast(real) r); - if (r == 0) - assert(isIdentical(r, a)); // check sign - else - assert(isClose(r, a)); - } - } - - import std.meta : AliasSeq; - foreach (T; AliasSeq!(real, double, float)) - testAtan2!T(); - - import std.math.operations : isClose; - import std.math.algebraic : sqrt; - import std.math.constants : PI; - assert(isClose(atan2(1.0L, sqrt(3.0L)), PI / 6, real.sizeof > double.sizeof ? 1e-15 : 1e-14)); -} - -/*********************************** - * Calculates the hyperbolic cosine of x. - * - * $(TABLE_SV - * $(TR $(TH x) $(TH cosh(x)) $(TH invalid?)) - * $(TR $(TD $(PLUSMN)$(INFIN)) $(TD $(PLUSMN)0.0) $(TD no) ) - * ) - */ -real cosh(real x) @safe pure nothrow @nogc -{ - import std.math.exponential : exp; - - // cosh = (exp(x)+exp(-x))/2. - // The naive implementation works correctly. - const real y = exp(x); - return (y + 1.0/y) * 0.5; -} - -/// ditto -double cosh(double x) @safe pure nothrow @nogc { return cosh(cast(real) x); } - -/// ditto -float cosh(float x) @safe pure nothrow @nogc { return cosh(cast(real) x); } - -/// -@safe unittest -{ - import std.math.constants : E; - import std.math.operations : isClose; - - assert(cosh(0.0) == 1.0); - assert(cosh(1.0).isClose((E + 1.0 / E) / 2)); -} - -@safe @nogc nothrow unittest -{ - import std.math.constants : E; - import std.math.operations : isClose; - - assert(isClose(cosh(1.0), (E + 1.0 / E) / 2, real.sizeof > double.sizeof ? 1e-15 : 1e-14)); -} - -/*********************************** - * Calculates the hyperbolic sine of x. - * - * $(TABLE_SV - * $(TR $(TH x) $(TH sinh(x)) $(TH invalid?)) - * $(TR $(TD $(PLUSMN)0.0) $(TD $(PLUSMN)0.0) $(TD no)) - * $(TR $(TD $(PLUSMN)$(INFIN)) $(TD $(PLUSMN)$(INFIN)) $(TD no)) - * ) - */ -real sinh(real x) @safe pure nothrow @nogc { return _sinh(x); } - -/// ditto -double sinh(double x) @safe pure nothrow @nogc { return _sinh(x); } - -/// ditto -float sinh(float x) @safe pure nothrow @nogc { return _sinh(x); } - -/// -@safe unittest -{ - import std.math.constants : E; - import std.math.operations : isClose; - import std.math.traits : isIdentical; - - enum sinh1 = (E - 1.0 / E) / 2; - import std.meta : AliasSeq; - static foreach (F; AliasSeq!(float, double, real)) - { - assert(isIdentical(sinh(F(0.0)), F(0.0))); - assert(sinh(F(1.0)).isClose(F(sinh1))); - } -} - -private F _sinh(F)(F x) -{ - import std.math.traits : copysign; - import std.math.exponential : exp, expm1; - import core.math : fabs; - import std.math.constants : LN2; - - // sinh(x) = (exp(x)-exp(-x))/2; - // Very large arguments could cause an overflow, but - // the maximum value of x for which exp(x) + exp(-x)) != exp(x) - // is x = 0.5 * (real.mant_dig) * LN2. // = 22.1807 for real80. - if (fabs(x) > F.mant_dig * F(LN2)) - { - return copysign(F(0.5) * exp(fabs(x)), x); - } - - const y = expm1(x); - return F(0.5) * y / (y+1) * (y+2); -} - -@safe @nogc nothrow unittest -{ - import std.math.constants : E; - import std.math.operations : isClose; - - assert(isClose(sinh(1.0L), real((E - 1.0 / E) / 2), real.sizeof > double.sizeof ? 1e-15 : 1e-14)); -} -/*********************************** - * Calculates the hyperbolic tangent of x. - * - * $(TABLE_SV - * $(TR $(TH x) $(TH tanh(x)) $(TH invalid?)) - * $(TR $(TD $(PLUSMN)0.0) $(TD $(PLUSMN)0.0) $(TD no) ) - * $(TR $(TD $(PLUSMN)$(INFIN)) $(TD $(PLUSMN)1.0) $(TD no)) - * ) - */ -real tanh(real x) @safe pure nothrow @nogc { return _tanh(x); } - -/// ditto -double tanh(double x) @safe pure nothrow @nogc { return _tanh(x); } - -/// ditto -float tanh(float x) @safe pure nothrow @nogc { return _tanh(x); } - -/// -@safe unittest -{ - import std.math.operations : isClose; - import std.math.traits : isIdentical; - - assert(isIdentical(tanh(0.0), 0.0)); - assert(tanh(1.0).isClose(sinh(1.0) / cosh(1.0))); -} - -private F _tanh(F)(F x) -{ - import std.math.traits : copysign; - import std.math.exponential : expm1; - import core.math : fabs; - import std.math.constants : LN2; - - // tanh(x) = (exp(x) - exp(-x))/(exp(x)+exp(-x)) - if (fabs(x) > F.mant_dig * F(LN2)) - { - return copysign(1, x); - } - - const y = expm1(2*x); - return y / (y + 2); -} - -@safe @nogc nothrow unittest -{ - import std.math.operations : isClose; - - assert(isClose(tanh(1.0L), sinh(1.0L) / cosh(1.0L), real.sizeof > double.sizeof ? 1e-15 : 1e-14)); -} - -/*********************************** - * Calculates the inverse hyperbolic cosine of x. - * - * Mathematically, acosh(x) = log(x + sqrt( x*x - 1)) - * - * $(TABLE_DOMRG - * $(DOMAIN 1..$(INFIN)), - * $(RANGE 0..$(INFIN)) - * ) - * - * $(TABLE_SV - * $(SVH x, acosh(x) ) - * $(SV $(NAN), $(NAN) ) - * $(SV $(LT)1, $(NAN) ) - * $(SV 1, 0 ) - * $(SV +$(INFIN),+$(INFIN)) - * ) - */ -real acosh(real x) @safe pure nothrow @nogc { return _acosh(x); } - -/// ditto -double acosh(double x) @safe pure nothrow @nogc { return _acosh(x); } - -/// ditto -float acosh(float x) @safe pure nothrow @nogc { return _acosh(x); } - -/// -@safe @nogc nothrow unittest -{ - import std.math.traits : isIdentical, isNaN; - - assert(isNaN(acosh(0.9))); - assert(isNaN(acosh(real.nan))); - assert(isIdentical(acosh(1.0), 0.0)); - assert(acosh(real.infinity) == real.infinity); - assert(isNaN(acosh(0.5))); -} - -private F _acosh(F)(F x) @safe pure nothrow @nogc -{ - import std.math.constants : LN2; - import std.math.exponential : log; - import core.math : sqrt; - - if (x > 1/F.epsilon) - return F(LN2) + log(x); - else - return log(x + sqrt(x*x - 1)); -} - -@safe @nogc nothrow unittest -{ - import std.math.operations : isClose; - - assert(isClose(acosh(cosh(3.0L)), 3.0L, real.sizeof > double.sizeof ? 1e-15 : 1e-14)); -} - -/*********************************** - * Calculates the inverse hyperbolic sine of x. - * - * Mathematically, - * --------------- - * asinh(x) = log( x + sqrt( x*x + 1 )) // if x >= +0 - * asinh(x) = -log(-x + sqrt( x*x + 1 )) // if x <= -0 - * ------------- - * - * $(TABLE_SV - * $(SVH x, asinh(x) ) - * $(SV $(NAN), $(NAN) ) - * $(SV $(PLUSMN)0, $(PLUSMN)0 ) - * $(SV $(PLUSMN)$(INFIN),$(PLUSMN)$(INFIN)) - * ) - */ -real asinh(real x) @safe pure nothrow @nogc { return _asinh(x); } - -/// ditto -double asinh(double x) @safe pure nothrow @nogc { return _asinh(x); } - -/// ditto -float asinh(float x) @safe pure nothrow @nogc { return _asinh(x); } - -/// -@safe @nogc nothrow unittest -{ - import std.math.traits : isIdentical, isNaN; - - assert(isIdentical(asinh(0.0), 0.0)); - assert(isIdentical(asinh(-0.0), -0.0)); - assert(asinh(real.infinity) == real.infinity); - assert(asinh(-real.infinity) == -real.infinity); - assert(isNaN(asinh(real.nan))); -} - -private F _asinh(F)(F x) -{ - import std.math.traits : copysign; - import core.math : fabs, sqrt; - import std.math.exponential : log, log1p; - import std.math.constants : LN2; - - return (fabs(x) > 1 / F.epsilon) - // beyond this point, x*x + 1 == x*x - ? copysign(F(LN2) + log(fabs(x)), x) - // sqrt(x*x + 1) == 1 + x * x / ( 1 + sqrt(x*x + 1) ) - : copysign(log1p(fabs(x) + x*x / (1 + sqrt(x*x + 1)) ), x); -} - -@safe unittest -{ - import std.math.operations : isClose; - - assert(isClose(asinh(sinh(3.0L)), 3.0L, real.sizeof > double.sizeof ? 1e-15 : 1e-14)); -} - -/*********************************** - * Calculates the inverse hyperbolic tangent of x, - * returning a value from ranging from -1 to 1. - * - * Mathematically, atanh(x) = log( (1+x)/(1-x) ) / 2 - * - * $(TABLE_DOMRG - * $(DOMAIN -$(INFIN)..$(INFIN)), - * $(RANGE -1 .. 1) - * ) - * $(BR) - * $(TABLE_SV - * $(SVH x, acosh(x) ) - * $(SV $(NAN), $(NAN) ) - * $(SV $(PLUSMN)0, $(PLUSMN)0) - * $(SV -$(INFIN), -0) - * ) - */ -real atanh(real x) @safe pure nothrow @nogc -{ - import std.math.exponential : log1p; - - // log( (1+x)/(1-x) ) == log ( 1 + (2*x)/(1-x) ) - return 0.5 * log1p( 2 * x / (1 - x) ); -} - -/// ditto -double atanh(double x) @safe pure nothrow @nogc { return atanh(cast(real) x); } - -/// ditto -float atanh(float x) @safe pure nothrow @nogc { return atanh(cast(real) x); } - -/// -@safe @nogc nothrow unittest -{ - import std.math.traits : isIdentical, isNaN; - - assert(isIdentical(atanh(0.0), 0.0)); - assert(isIdentical(atanh(-0.0),-0.0)); - assert(isNaN(atanh(real.nan))); - assert(isNaN(atanh(-real.infinity))); - assert(atanh(0.0) == 0); -} - -@safe unittest -{ - import std.math.operations : isClose; - - assert(isClose(atanh(tanh(0.5L)), 0.5, real.sizeof > double.sizeof ? 1e-15 : 1e-14)); -} diff --git a/phobos/std/mathspecial.d b/phobos/std/mathspecial.d deleted file mode 100644 index 64ab9bf..0000000 --- a/phobos/std/mathspecial.d +++ /dev/null @@ -1,370 +0,0 @@ -// Written in the D programming language. - -/** - * Mathematical Special Functions - * - * The technical term 'Special Functions' includes several families of - * transcendental functions, which have important applications in particular - * branches of mathematics and physics. - * - * The gamma and related functions, and the error function are crucial for - * mathematical statistics. - * The Bessel and related functions arise in problems involving wave propagation - * (especially in optics). - * Other major categories of special functions include the elliptic integrals - * (related to the arc length of an ellipse), and the hypergeometric functions. - * - * Status: - * Many more functions will be added to this module. - * The naming convention for the distribution functions (gammaIncomplete, etc) - * is not yet finalized and will probably change. - * - * Macros: - * TABLE_SV = - * - * $0
Special Values
- * SVH = $(TR $(TH $1) $(TH $2)) - * SV = $(TR $(TD $1) $(TD $2)) - * - * NAN = $(RED NAN) - * SUP = $0 - * GAMMA = Γ - * THETA = θ - * INTEGRAL = ∫ - * INTEGRATE = $(BIG ∫$(SMALL $1)$2) - * POWER = $1$2 - * SUB = $1$2 - * BIGSUM = $(BIG Σ $2$(SMALL $1)) - * CHOOSE = $(BIG () $(SMALL $1)$(SMALL $2) $(BIG )) - * PLUSMN = ± - * INFIN = ∞ - * PLUSMNINF = ±∞ - * PI = π - * LT = < - * GT = > - * SQRT = √ - * HALF = ½ - * - * - * Copyright: Based on the CEPHES math library, which is - * Copyright (C) 1994 Stephen L. Moshier (moshier@world.std.com). - * License: $(HTTP www.boost.org/LICENSE_1_0.txt, Boost License 1.0). - * Authors: Stephen L. Moshier (original C code). Conversion to D by Don Clugston - * Source: $(PHOBOSSRC std/mathspecial.d) - */ -module std.mathspecial; -import std.internal.math.errorfunction; -import std.internal.math.gammafunction; -public import std.math; - -/* *********************************************** - * GAMMA AND RELATED FUNCTIONS * - * ***********************************************/ - -pure: -nothrow: -@safe: -@nogc: - -/** The Gamma function, $(GAMMA)(x) - * - * $(GAMMA)(x) is a generalisation of the factorial function - * to real and complex numbers. - * Like x!, $(GAMMA)(x+1) = x * $(GAMMA)(x). - * - * Mathematically, if z.re > 0 then - * $(GAMMA)(z) = $(INTEGRATE 0, $(INFIN)) $(POWER t, z-1)$(POWER e, -t) dt - * - * $(TABLE_SV - * $(SVH x, $(GAMMA)(x) ) - * $(SV $(NAN), $(NAN) ) - * $(SV $(PLUSMN)0.0, $(PLUSMNINF)) - * $(SV integer > 0, (x-1)! ) - * $(SV integer < 0, $(NAN) ) - * $(SV +$(INFIN), +$(INFIN) ) - * $(SV -$(INFIN), $(NAN) ) - * ) - */ -real gamma(real x) -{ - return std.internal.math.gammafunction.gamma(x); -} - -/** Natural logarithm of the gamma function, $(GAMMA)(x) - * - * Returns the base e (2.718...) logarithm of the absolute - * value of the gamma function of the argument. - * - * For reals, logGamma is equivalent to log(fabs(gamma(x))). - * - * $(TABLE_SV - * $(SVH x, logGamma(x) ) - * $(SV $(NAN), $(NAN) ) - * $(SV integer <= 0, +$(INFIN) ) - * $(SV $(PLUSMNINF), +$(INFIN) ) - * ) - */ -real logGamma(real x) -{ - return std.internal.math.gammafunction.logGamma(x); -} - -/** The sign of $(GAMMA)(x). - * - * Returns -1 if $(GAMMA)(x) < 0, +1 if $(GAMMA)(x) > 0, - * $(NAN) if sign is indeterminate. - * - * Note that this function can be used in conjunction with logGamma(x) to - * evaluate gamma for very large values of x. - */ -real sgnGamma(real x) -{ - import core.math : rndtol; - /* Author: Don Clugston. */ - if (isNaN(x)) return x; - if (x > 0) return 1.0; - if (x < -1/real.epsilon) - { - // Large negatives lose all precision - return real.nan; - } - long n = rndtol(x); - if (x == n) - { - return x == 0 ? copysign(1, x) : real.nan; - } - return n & 1 ? 1.0 : -1.0; -} - -@safe unittest -{ - assert(sgnGamma(5.0) == 1.0); - assert(isNaN(sgnGamma(-3.0))); - assert(sgnGamma(-0.1) == -1.0); - assert(sgnGamma(-55.1) == 1.0); - assert(isNaN(sgnGamma(-real.infinity))); - assert(isIdentical(sgnGamma(NaN(0xABC)), NaN(0xABC))); -} - -/** Beta function - * - * The beta function is defined as - * - * beta(x, y) = ($(GAMMA)(x) * $(GAMMA)(y)) / $(GAMMA)(x + y) - */ -real beta(real x, real y) -{ - if ((x+y)> MAXGAMMA) - { - return exp(logGamma(x) + logGamma(y) - logGamma(x+y)); - } else return gamma(x) * gamma(y) / gamma(x+y); -} - -@safe unittest -{ - assert(isIdentical(beta(NaN(0xABC), 4), NaN(0xABC))); - assert(isIdentical(beta(2, NaN(0xABC)), NaN(0xABC))); -} - -/** Digamma function - * - * The digamma function is the logarithmic derivative of the gamma function. - * - * digamma(x) = d/dx logGamma(x) - * - * See_Also: $(LREF logmdigamma), $(LREF logmdigammaInverse). - */ -real digamma(real x) -{ - return std.internal.math.gammafunction.digamma(x); -} - -/** Log Minus Digamma function - * - * logmdigamma(x) = log(x) - digamma(x) - * - * See_Also: $(LREF digamma), $(LREF logmdigammaInverse). - */ -real logmdigamma(real x) -{ - return std.internal.math.gammafunction.logmdigamma(x); -} - -/** Inverse of the Log Minus Digamma function - * - * Given y, the function finds x such log(x) - digamma(x) = y. - * - * See_Also: $(LREF logmdigamma), $(LREF digamma). - */ -real logmdigammaInverse(real x) -{ - return std.internal.math.gammafunction.logmdigammaInverse(x); -} - -/** Incomplete beta integral - * - * Returns regularized incomplete beta integral of the arguments, evaluated - * from zero to x. The regularized incomplete beta function is defined as - * - * betaIncomplete(a, b, x) = $(GAMMA)(a + b) / ( $(GAMMA)(a) $(GAMMA)(b) ) * - * $(INTEGRATE 0, x) $(POWER t, a-1)$(POWER (1-t), b-1) dt - * - * and is the same as the cumulative distribution function of the Beta - * distribution. - * - * The domain of definition is 0 <= x <= 1. In this - * implementation a and b are restricted to positive values. - * The integral from x to 1 may be obtained by the symmetry - * relation - * - * betaIncompleteCompl(a, b, x ) = betaIncomplete( b, a, 1-x ) - * - * The integral is evaluated by a continued fraction expansion - * or, when b * x is small, by a power series. - */ -real betaIncomplete(real a, real b, real x ) -{ - return std.internal.math.gammafunction.betaIncomplete(a, b, x); -} - -/** Inverse of incomplete beta integral - * - * Given y, the function finds x such that - * - * betaIncomplete(a, b, x) == y - * - * Newton iterations or interval halving is used. - */ -real betaIncompleteInverse(real a, real b, real y ) -{ - return std.internal.math.gammafunction.betaIncompleteInv(a, b, y); -} - -/** Incomplete gamma integral and its complement - * - * These functions are defined by - * - * gammaIncomplete = ( $(INTEGRATE 0, x) $(POWER e, -t) $(POWER t, a-1) dt )/ $(GAMMA)(a) - * - * gammaIncompleteCompl(a,x) = 1 - gammaIncomplete(a,x) - * = ($(INTEGRATE x, $(INFIN)) $(POWER e, -t) $(POWER t, a-1) dt )/ $(GAMMA)(a) - * - * In this implementation both arguments must be positive. - * The integral is evaluated by either a power series or - * continued fraction expansion, depending on the relative - * values of a and x. - */ -real gammaIncomplete(real a, real x ) -in -{ - assert(x >= 0); - assert(a > 0); -} -do -{ - return std.internal.math.gammafunction.gammaIncomplete(a, x); -} - -/** ditto */ -real gammaIncompleteCompl(real a, real x ) -in -{ - assert(x >= 0); - assert(a > 0); -} -do -{ - return std.internal.math.gammafunction.gammaIncompleteCompl(a, x); -} - -/** Inverse of complemented incomplete gamma integral - * - * Given a and p, the function finds x such that - * - * gammaIncompleteCompl( a, x ) = p. - */ -real gammaIncompleteComplInverse(real a, real p) -in -{ - assert(p >= 0 && p <= 1); - assert(a > 0); -} -do -{ - return std.internal.math.gammafunction.gammaIncompleteComplInv(a, p); -} - - -/* *********************************************** - * ERROR FUNCTIONS & NORMAL DISTRIBUTION * - * ***********************************************/ - - /** Error function - * - * The integral is - * - * erf(x) = 2/ $(SQRT)($(PI)) - * $(INTEGRATE 0, x) exp( - $(POWER t, 2)) dt - * - * The magnitude of x is limited to about 106.56 for IEEE 80-bit - * arithmetic; 1 or -1 is returned outside this range. - */ -real erf(real x) -{ - return std.internal.math.errorfunction.erf(x); -} - -/** Complementary error function - * - * erfc(x) = 1 - erf(x) - * = 2/ $(SQRT)($(PI)) - * $(INTEGRATE x, $(INFIN)) exp( - $(POWER t, 2)) dt - * - * This function has high relative accuracy for - * values of x far from zero. (For values near zero, use erf(x)). - */ -real erfc(real x) -{ - return std.internal.math.errorfunction.erfc(x); -} - - -/** Standard normal distribution function. - * - * The normal (or Gaussian, or bell-shaped) distribution is - * defined as: - * - * normalDist(x) = 1/$(SQRT)(2$(PI)) $(INTEGRATE -$(INFIN), x) exp( - $(POWER t, 2)/2) dt - * = 0.5 + 0.5 * erf(x/sqrt(2)) - * = 0.5 * erfc(- x/sqrt(2)) - * - * To maintain accuracy at values of x near 1.0, use - * normalDistribution(x) = 1.0 - normalDistribution(-x). - * - * References: - * $(LINK http://www.netlib.org/cephes/ldoubdoc.html), - * G. Marsaglia, "Evaluating the Normal Distribution", - * Journal of Statistical Software 11, (July 2004). - */ -real normalDistribution(real x) -{ - return std.internal.math.errorfunction.normalDistributionImpl(x); -} - -/** Inverse of Standard normal distribution function - * - * Returns the argument, x, for which the area under the - * Normal probability density function (integrated from - * minus infinity to x) is equal to p. - * - * Note: This function is only implemented to 80 bit precision. - */ -real normalDistributionInverse(real p) -in -{ - assert(p >= 0.0L && p <= 1.0L, "Domain error"); -} -do -{ - return std.internal.math.errorfunction.normalDistributionInvImpl(p); -} diff --git a/phobos/std/meta.d b/phobos/std/meta.d deleted file mode 100644 index 2db341d..0000000 --- a/phobos/std/meta.d +++ /dev/null @@ -1,1534 +0,0 @@ -// Written in the D programming language. - -/** - * Templates to manipulate - * $(DDSUBLINK spec/template, variadic-templates, template parameter sequences) - * (also known as $(I alias sequences)). - * - * Some operations on alias sequences are built into the language, - * such as `S[i]`, which accesses the element at index `i` in the - * sequence. `S[low .. high]` returns a new alias - * sequence that is a slice of the old one. - * - * For more information, see $(DDLINK ctarguments, Compile-time Sequences, Compile-time Sequences). - * - * $(B Note:) Several templates in this module use or operate on eponymous templates that - * take a single argument and evaluate to a boolean constant. Such templates - * are referred to as $(I template predicates). - * - * $(SCRIPT inhibitQuickIndex = 1;) - * $(DIVC quickindex, - * $(BOOKTABLE , - * $(TR $(TH Category) $(TH Templates)) - * $(TR $(TD Building blocks) $(TD - * $(LREF Alias) - * $(LREF AliasSeq) - * $(LREF aliasSeqOf) - * )) - * $(TR $(TD Alias sequence filtering) $(TD - * $(LREF Erase) - * $(LREF EraseAll) - * $(LREF Filter) - * $(LREF NoDuplicates) - * $(LREF Stride) - * )) - * $(TR $(TD Alias sequence type hierarchy) $(TD - * $(LREF DerivedToFront) - * $(LREF MostDerived) - * )) - * $(TR $(TD Alias sequence transformation) $(TD - * $(LREF Repeat) - * $(LREF Replace) - * $(LREF ReplaceAll) - * $(LREF Reverse) - * $(LREF staticMap) - * $(LREF staticSort) - * )) - * $(TR $(TD Alias sequence searching) $(TD - * $(LREF allSatisfy) - * $(LREF anySatisfy) - * $(LREF staticIndexOf) - * )) - * $(TR $(TD Template predicates) $(TD - * $(LREF templateAnd) - * $(LREF templateNot) - * $(LREF templateOr) - * $(LREF staticIsSorted) - * )) - * $(TR $(TD Template instantiation) $(TD - * $(LREF ApplyLeft) - * $(LREF ApplyRight) - * $(LREF Instantiate) - * )) - * )) - * - * References: - * Based on ideas in Table 3.1 from - * $(LINK2 http://amazon.com/exec/obidos/ASIN/0201704315/ref=ase_classicempire/102-2957199-2585768, - * Modern C++ Design), - * Andrei Alexandrescu (Addison-Wesley Professional, 2001) - * Copyright: Copyright The D Language Foundation 2005 - 2015. - * License: $(HTTP www.boost.org/LICENSE_1_0.txt, Boost License 1.0). - * Authors: - * $(HTTP digitalmars.com, Walter Bright), - * $(HTTP klickverbot.at, David Nadlinger) - * Source: $(PHOBOSSRC std/meta.d) - */ - -module std.meta; - -import std.traits : isAggregateType, Unqual, isIterable; -import std.range.primitives : isInfinite; - -/** - * Creates a sequence of zero or more aliases. This is most commonly - * used as template parameters or arguments. - * - * In previous versions of Phobos, this was known as `TypeTuple`. - */ -alias AliasSeq(TList...) = TList; - -/// -@safe unittest -{ - import std.meta; - alias TL = AliasSeq!(int, double); - - int foo(TL td) // same as int foo(int, double); - { - return td[0] + cast(int) td[1]; - } -} - -/// -@safe unittest -{ - alias TL = AliasSeq!(int, double); - - alias Types = AliasSeq!(TL, char); - static assert(is(Types == AliasSeq!(int, double, char))); -} - - -/// -@safe unittest -{ - // Creates a compile-time sequence of function call expressions - // that each call `func` with the next variadic template argument - template Map(alias func, args...) - { - auto ref lazyItem() {return func(args[0]);} - - static if (args.length == 1) - { - alias Map = lazyItem; - } - else - { - // recurse - alias Map = AliasSeq!(lazyItem, Map!(func, args[1 .. $])); - } - } - - static void test(int a, int b) - { - assert(a == 4); - assert(b == 16); - } - - static int a = 2; - static int b = 4; - - test(Map!(i => i ^^ 2, a, b)); - assert(a == 2); - assert(b == 4); - - test(Map!((ref i) => i *= i, a, b)); - assert(a == 4); - assert(b == 16); - - static void testRef(ref int a, ref int b) - { - assert(a++ == 16); - assert(b++ == 256); - } - - testRef(Map!(function ref(ref i) => i *= i, a, b)); - assert(a == 17); - assert(b == 257); -} - -/** - * Allows `alias`ing of any single symbol, type or compile-time expression. - * - * Not everything can be directly aliased. An alias cannot be declared - * of - for example - a literal: - * --- - * alias a = 4; //Error - * --- - * With this template any single entity can be aliased: - * --- - * alias b = Alias!4; //OK - * --- - * See_Also: - * To alias more than one thing at once, use $(LREF AliasSeq). - */ -alias Alias(alias a) = a; - -/// Ditto -alias Alias(T) = T; - -/// -@safe unittest -{ - // Without Alias this would fail if Args[0] was e.g. a value and - // some logic would be needed to detect when to use enum instead - alias Head(Args...) = Alias!(Args[0]); - alias Tail(Args...) = Args[1 .. $]; - - alias Blah = AliasSeq!(3, int, "hello"); - static assert(Head!Blah == 3); - static assert(is(Head!(Tail!Blah) == int)); - static assert((Tail!Blah)[1] == "hello"); -} - -/// -@safe unittest -{ - alias a = Alias!(123); - static assert(a == 123); - - enum abc = 1; - alias b = Alias!(abc); - static assert(b == 1); - - alias c = Alias!(3 + 4); - static assert(c == 7); - - alias concat = (s0, s1) => s0 ~ s1; - alias d = Alias!(concat("Hello", " World!")); - static assert(d == "Hello World!"); - - alias e = Alias!(int); - static assert(is(e == int)); - - alias f = Alias!(AliasSeq!(int)); - static assert(!is(typeof(f[0]))); //not an AliasSeq - static assert(is(f == int)); - - auto g = 6; - alias h = Alias!g; - ++h; - assert(g == 7); -} - -package template OldAlias(alias a) -{ - static if (__traits(compiles, { alias x = a; })) - alias OldAlias = a; - else static if (__traits(compiles, { enum x = a; })) - enum OldAlias = a; - else - static assert(0, "Cannot alias " ~ a.stringof); -} - -package template OldAlias(T) -if (!isAggregateType!T || is(Unqual!T == T)) -{ - alias OldAlias = T; -} - -@safe unittest -{ - static struct Foo {} - //static assert(is(OldAlias!(const(Foo)) == const Foo)); - static assert(is(OldAlias!(const(int)) == const(int))); - static assert(OldAlias!123 == 123); - enum abc = 123; - static assert(OldAlias!abc == 123); -} - -/** - * Returns the index of the first occurrence of `args[0]` in the - * sequence `args[1 .. $]`. `args` may be types or compile-time values. - * If not found, `-1` is returned. - */ -template staticIndexOf(args...) -if (args.length >= 1) -{ - enum staticIndexOf = - { - static foreach (idx, arg; args[1 .. $]) - static if (isSame!(args[0], arg)) - // `if (__ctfe)` is redundant here but avoids the "Unreachable code" warning. - if (__ctfe) return idx; - return -1; - }(); -} - -/// -@safe unittest -{ - import std.stdio; - - void foo() - { - writefln("The index of long is %s", - staticIndexOf!(long, AliasSeq!(int, long, double))); - // prints: The index of long is 1 - } -} - -@safe unittest -{ - static assert(staticIndexOf!( byte, byte, short, int, long) == 0); - static assert(staticIndexOf!(short, byte, short, int, long) == 1); - static assert(staticIndexOf!( int, byte, short, int, long) == 2); - static assert(staticIndexOf!( long, byte, short, int, long) == 3); - static assert(staticIndexOf!( char, byte, short, int, long) == -1); - static assert(staticIndexOf!( -1, byte, short, int, long) == -1); - static assert(staticIndexOf!(void) == -1); - - static assert(staticIndexOf!("abc", "abc", "def", "ghi", "jkl") == 0); - static assert(staticIndexOf!("def", "abc", "def", "ghi", "jkl") == 1); - static assert(staticIndexOf!("ghi", "abc", "def", "ghi", "jkl") == 2); - static assert(staticIndexOf!("jkl", "abc", "def", "ghi", "jkl") == 3); - static assert(staticIndexOf!("mno", "abc", "def", "ghi", "jkl") == -1); - static assert(staticIndexOf!( void, "abc", "def", "ghi", "jkl") == -1); - static assert(staticIndexOf!(42) == -1); - - static assert(staticIndexOf!(void, 0, "void", void) == 2); - static assert(staticIndexOf!("void", 0, void, "void") == 2); -} - -/** - * Returns an `AliasSeq` created from `args[1 .. $]` with the first occurrence, - * if any, of `args[0]` removed. - */ -template Erase(args...) -if (args.length >= 1) -{ - private enum pos = staticIndexOf!(args[0], args[1 .. $]); - static if (pos < 0) - alias Erase = args[1 .. $]; - else - alias Erase = AliasSeq!(args[1 .. pos + 1], args[pos + 2 .. $]); -} - -/// -@safe unittest -{ - alias Types = AliasSeq!(int, long, double, char); - alias TL = Erase!(long, Types); - static assert(is(TL == AliasSeq!(int, double, char))); -} - -@safe unittest -{ - static assert(Pack!(Erase!(int, - short, int, int, 4)). - equals!(short, int, 4)); - - static assert(Pack!(Erase!(1, - real, 3, 1, 4, 1, 5, 9)). - equals!(real, 3, 4, 1, 5, 9)); -} - - -/** - * Returns an `AliasSeq` created from `args[1 .. $]` with all occurrences, - * if any, of `args[0]` removed. - */ -template EraseAll(args...) -if (args.length >= 1) -{ - alias EraseAll = AliasSeq!(); - static foreach (arg; args[1 .. $]) - static if (!isSame!(args[0], arg)) - EraseAll = AliasSeq!(EraseAll, arg); -} - -/// -@safe unittest -{ - alias Types = AliasSeq!(int, long, long, int); - static assert(is(EraseAll!(long, Types) == AliasSeq!(int, int))); -} - -@safe unittest -{ - static assert(Pack!(EraseAll!(int, - short, int, int, 4)). - equals!(short, 4)); - - static assert(Pack!(EraseAll!(1, - real, 3, 1, 4, 1, 5, 9)). - equals!(real, 3, 4, 5, 9)); -} - -/* - * Returns `items[0 .. $ - 1]` if `item[$ - 1]` found in `items[0 .. $ - 1]`, and - * `items` otherwise. - * - * Params: - * items = list to be processed - * - * See_Also: $(LREF NoDuplicates) - */ -private template AppendUnique(items...) -{ - alias head = items[0 .. $ - 1]; - static if (staticIndexOf!(items[$ - 1], head) >= 0) - alias AppendUnique = head; - else - alias AppendUnique = items; -} - -/** - * Returns an `AliasSeq` created from `args` with all duplicate - * types removed. - */ -template NoDuplicates(args...) -{ - alias NoDuplicates = AliasSeq!(); - static foreach (arg; args) - NoDuplicates = AppendUnique!(NoDuplicates, arg); -} - -/// -@safe unittest -{ - alias Types = AliasSeq!(int, long, long, int, float); - - alias TL = NoDuplicates!(Types); - static assert(is(TL == AliasSeq!(int, long, float))); -} - -@safe unittest -{ - import std.range : iota; - - // https://issues.dlang.org/show_bug.cgi?id=14561: huge enums - alias LongList = Repeat!(1500, int); - static assert(NoDuplicates!LongList.length == 1); - // https://issues.dlang.org/show_bug.cgi?id=17995: huge enums, revisited - - alias a = NoDuplicates!(AliasSeq!(1, Repeat!(1000, 3))); - alias b = NoDuplicates!(AliasSeq!(1, Repeat!(10, 3))); - static assert(a.length == b.length); - - static assert(NoDuplicates!(aliasSeqOf!(iota(7)), aliasSeqOf!(iota(7))) == aliasSeqOf!(iota(7))); - static assert(NoDuplicates!(aliasSeqOf!(iota(8)), aliasSeqOf!(iota(8))) == aliasSeqOf!(iota(8))); -} - -@safe unittest -{ - static assert( - Pack!( - NoDuplicates!(1, int, 1, NoDuplicates, int, NoDuplicates, real)) - .equals!(1, int, NoDuplicates, real)); -} - - -/** - * Returns an `AliasSeq` created from TList with the first occurrence - * of T, if found, replaced with U. - */ -template Replace(T, U, TList...) -{ - alias Replace = GenericReplace!(T, U, TList).result; -} - -/// Ditto -template Replace(alias T, U, TList...) -{ - alias Replace = GenericReplace!(T, U, TList).result; -} - -/// Ditto -template Replace(T, alias U, TList...) -{ - alias Replace = GenericReplace!(T, U, TList).result; -} - -/// Ditto -template Replace(alias T, alias U, TList...) -{ - alias Replace = GenericReplace!(T, U, TList).result; -} - -/// -@safe unittest -{ - alias Types = AliasSeq!(int, long, long, int, float); - - alias TL = Replace!(long, char, Types); - static assert(is(TL == AliasSeq!(int, char, long, int, float))); -} - -// [internal] -private template GenericReplace(args...) -if (args.length >= 2) -{ - alias from = OldAlias!(args[0]); - alias to = OldAlias!(args[1]); - alias tuple = args[2 .. $]; - - static if (tuple.length) - { - alias head = OldAlias!(tuple[0]); - alias tail = tuple[1 .. $]; - - static if (isSame!(from, head)) - alias result = AliasSeq!(to, tail); - else - alias result = AliasSeq!(head, - GenericReplace!(from, to, tail).result); - } - else - { - alias result = AliasSeq!(); - } - } - -@safe unittest -{ - static assert(Pack!(Replace!(byte, ubyte, - short, byte, byte, byte)). - equals!(short, ubyte, byte, byte)); - - static assert(Pack!(Replace!(1111, byte, - 2222, 1111, 1111, 1111)). - equals!(2222, byte, 1111, 1111)); - - static assert(Pack!(Replace!(byte, 1111, - short, byte, byte, byte)). - equals!(short, 1111, byte, byte)); - - static assert(Pack!(Replace!(1111, "11", - 2222, 1111, 1111, 1111)). - equals!(2222, "11", 1111, 1111)); -} - -/** - * Returns an `AliasSeq` created from `args[2 .. $]` with all occurrences - * of `args[0]`, if any, replaced with `args[1]`. - */ -template ReplaceAll(args...) -{ - alias ReplaceAll = AliasSeq!(); - static foreach (arg; args[2 .. $]) - { - static if (isSame!(args[0], arg)) - ReplaceAll = AliasSeq!(ReplaceAll, args[1]); - else - ReplaceAll = AliasSeq!(ReplaceAll, arg); - } -} - -/// -@safe unittest -{ - alias Types = AliasSeq!(int, long, long, int, float); - - alias TL = ReplaceAll!(long, char, Types); - static assert(is(TL == AliasSeq!(int, char, char, int, float))); -} - -@safe unittest -{ - static assert(Pack!(ReplaceAll!(byte, ubyte, - byte, short, byte, byte)). - equals!(ubyte, short, ubyte, ubyte)); - - static assert(Pack!(ReplaceAll!(1111, byte, - 1111, 2222, 1111, 1111)). - equals!(byte, 2222, byte, byte)); - - static assert(Pack!(ReplaceAll!(byte, 1111, - byte, short, byte, byte)). - equals!(1111, short, 1111, 1111)); - - static assert(Pack!(ReplaceAll!(1111, "11", - 1111, 2222, 1111, 1111)). - equals!("11", 2222, "11", "11")); -} - -/** - * Returns an `AliasSeq` created from `args` with the order reversed. - */ -template Reverse(args...) -{ - alias Reverse = AliasSeq!(); - static foreach_reverse (arg; args) - Reverse = AliasSeq!(Reverse, arg); -} - -/// -@safe unittest -{ - alias Types = AliasSeq!(int, long, long, int, float, byte, ubyte, short, ushort, uint); - - alias TL = Reverse!(Types); - static assert(is(TL == AliasSeq!(uint, ushort, short, ubyte, byte, float, int, long, long, int))); -} - -/** - * Returns the type from `TList` that is the most derived from type `T`. - * If no such type is found, `T` is returned. - */ -template MostDerived(T, TList...) -{ - import std.traits : Select; - alias MostDerived = T; - static foreach (U; TList) - MostDerived = Select!(is(U : MostDerived), U, MostDerived); -} - -/// -@safe unittest -{ - class A { } - class B : A { } - class C : B { } - alias Types = AliasSeq!(A, C, B); - - MostDerived!(Object, Types) x; // x is declared as type C - static assert(is(typeof(x) == C)); -} - -/** - * Returns an `AliasSeq` with the elements of TList sorted so that the most - * derived types come first. - */ -template DerivedToFront(TList...) -{ - private enum cmp(T, U) = is(T : U); - alias DerivedToFront = staticSort!(cmp, TList); -} - -/// -@safe unittest -{ - class A { } - class B : A { } - class C : B { } - alias Types = AliasSeq!(A, C, B); - - alias TL = DerivedToFront!(Types); - static assert(is(TL == AliasSeq!(C, B, A))); - - alias TL2 = DerivedToFront!(A, A, A, B, B, B, C, C, C); - static assert(is(TL2 == AliasSeq!(C, C, C, B, B, B, A, A, A))); -} - -/** -Evaluates to `AliasSeq!(fun!(args[0]), fun!(args[1]), ..., fun!(args[$ - 1]))`. - */ -template staticMap(alias fun, args...) -{ - alias staticMap = AliasSeq!(); - static foreach (arg; args) - staticMap = AliasSeq!(staticMap, fun!arg); -} - -/// -@safe unittest -{ - import std.traits : Unqual; - alias TL = staticMap!(Unqual, int, const int, immutable int, uint, ubyte, byte, short, ushort); - static assert(is(TL == AliasSeq!(int, int, int, uint, ubyte, byte, short, ushort))); -} - -@safe unittest -{ - import std.traits : Unqual; - - // empty - alias Empty = staticMap!(Unqual); - static assert(Empty.length == 0); - - // single - alias Single = staticMap!(Unqual, const int); - static assert(is(Single == AliasSeq!int)); - - alias T = staticMap!(Unqual, int, const int, immutable int, uint, ubyte, byte, short, ushort, long); - static assert(is(T == AliasSeq!(int, int, int, uint, ubyte, byte, short, ushort, long))); - - // @@@ BUG @@@ The test below exposes failure of the straightforward use. - // See @adamdruppe's comment to https://github.com/dlang/phobos/pull/8039 - template id(alias what) { - enum id = __traits(identifier, what); - } - enum A { a } - static assert(staticMap!(id, A.a) == AliasSeq!("a")); -} - -// regression test for https://issues.dlang.org/show_bug.cgi?id=21088 -@system unittest // typeid opEquals is @system -{ - enum getTypeId(T) = typeid(T); - alias A = staticMap!(getTypeId, int); - - assert(A == typeid(int)); -} - -version (StdDdoc) -{ - /** - Tests whether all given items satisfy a template predicate, i.e. evaluates to - $(D F!(T[0]) && F!(T[1]) && ... && F!(T[$ - 1])). - - Evaluation is $(I not) short-circuited if a false result is encountered; the - template predicate must be instantiable with all the given items. - */ - template allSatisfy(alias F, T...) - { - import core.internal.traits : allSat = allSatisfy; - alias allSatisfy = allSat!(F, T); - } -} -else -{ - import core.internal.traits : allSat = allSatisfy; - alias allSatisfy = allSat; -} - -/// -@safe unittest -{ - import std.traits : isIntegral; - - static assert(!allSatisfy!(isIntegral, int, double)); - static assert( allSatisfy!(isIntegral, int, long)); -} - -version (StdDdoc) -{ - /** - Tests whether any given items satisfy a template predicate, i.e. evaluates to - $(D F!(T[0]) || F!(T[1]) || ... || F!(T[$ - 1])). - - Evaluation is short-circuited if a true result is encountered; the - template predicate must be instantiable with one of the given items. - */ - template anySatisfy(alias F, T...) - { - import core.internal.traits : anySat = anySatisfy; - alias anySatisfy = anySat!(F, T); - } -} -else -{ - import core.internal.traits : anySat = anySatisfy; - alias anySatisfy = anySat; -} - -/// -@safe unittest -{ - import std.traits : isIntegral; - - static assert(!anySatisfy!(isIntegral, string, double)); - static assert( anySatisfy!(isIntegral, int, double)); -} - -/** - * Filters an `AliasSeq` using a template predicate. Returns an - * `AliasSeq` of the elements which satisfy the predicate. - */ -template Filter(alias pred, args...) -{ - alias Filter = AliasSeq!(); - static foreach (arg; args) - static if (pred!arg) - Filter = AliasSeq!(Filter, arg); -} - -/// -@safe unittest -{ - import std.traits : isNarrowString, isUnsigned; - - alias Types1 = AliasSeq!(string, wstring, dchar[], char[], dstring, int); - alias TL1 = Filter!(isNarrowString, Types1); - static assert(is(TL1 == AliasSeq!(string, wstring, char[]))); - - alias Types2 = AliasSeq!(int, byte, ubyte, dstring, dchar, uint, ulong); - alias TL2 = Filter!(isUnsigned, Types2); - static assert(is(TL2 == AliasSeq!(ubyte, uint, ulong))); -} - -@safe unittest -{ - import std.traits : isPointer; - - static assert(is(Filter!(isPointer, int, void*, char[], int*) == AliasSeq!(void*, int*))); - static assert(is(Filter!isPointer == AliasSeq!())); -} - -@safe unittest -{ - enum Yes(T) = true; - static struct S {} - static assert(is(Filter!(Yes, const(int), const(S)) == AliasSeq!(const(int), const(S)))); -} - -// Used in template predicate unit tests below. -private version (StdUnittest) -{ - template testAlways(T...) - { - enum testAlways = true; - } - - template testNever(T...) - { - enum testNever = false; - } - - template testError(T...) - { - static assert(false, "Should never be instantiated."); - } -} - - -/** - * Negates the passed template predicate. - */ -template templateNot(alias pred) -{ - enum templateNot(T...) = !pred!T; -} - -/// -@safe unittest -{ - import std.traits : isPointer; - - alias isNoPointer = templateNot!isPointer; - static assert(!isNoPointer!(int*)); - static assert(allSatisfy!(isNoPointer, string, char, float)); -} - -version (StdUnittest) -@safe unittest -{ - static foreach (T; AliasSeq!(int, staticMap, 42)) - { - static assert(!Instantiate!(templateNot!testAlways, T)); - static assert(Instantiate!(templateNot!testNever, T)); - } -} - - -/** - * Combines several template predicates using logical AND, i.e. constructs a new - * predicate which evaluates to true for a given input T if and only if all of - * the passed predicates are true for T. - * - * The predicates are evaluated from left to right, aborting evaluation in a - * short-cut manner if a false result is encountered, in which case the latter - * instantiations do not need to compile. - */ -template templateAnd(Preds...) -{ - template templateAnd(T...) - { - static if (Preds.length == 0) - { - enum templateAnd = true; - } - else - { - static if (Instantiate!(Preds[0], T)) - alias templateAnd = Instantiate!(.templateAnd!(Preds[1 .. $]), T); - else - enum templateAnd = false; - } - } -} - -/// -@safe unittest -{ - import std.traits : isNumeric, isUnsigned; - - alias storesNegativeNumbers = templateAnd!(isNumeric, templateNot!isUnsigned); - static assert(storesNegativeNumbers!int); - static assert(!storesNegativeNumbers!string && !storesNegativeNumbers!uint); - - // An empty sequence of predicates always yields true. - alias alwaysTrue = templateAnd!(); - static assert(alwaysTrue!int); -} - -version (StdUnittest) -@safe unittest -{ - static foreach (T; AliasSeq!(int, staticMap, 42)) - { - static assert( Instantiate!(templateAnd!(), T)); - static assert( Instantiate!(templateAnd!(testAlways), T)); - static assert( Instantiate!(templateAnd!(testAlways, testAlways), T)); - static assert(!Instantiate!(templateAnd!(testNever), T)); - static assert(!Instantiate!(templateAnd!(testAlways, testNever), T)); - static assert(!Instantiate!(templateAnd!(testNever, testAlways), T)); - - static assert(!Instantiate!(templateAnd!(testNever, testError), T)); - static assert(!is(typeof(Instantiate!(templateAnd!(testAlways, testError), T)))); - } -} - - -/** - * Combines several template predicates using logical OR, i.e. constructs a new - * predicate which evaluates to true for a given input T if and only at least - * one of the passed predicates is true for T. - * - * The predicates are evaluated from left to right, aborting evaluation in a - * short-cut manner if a true result is encountered, in which case the latter - * instantiations do not need to compile. - */ -template templateOr(Preds...) -{ - template templateOr(T...) - { - static if (Preds.length == 0) - { - enum templateOr = false; - } - else - { - static if (Instantiate!(Preds[0], T)) - enum templateOr = true; - else - alias templateOr = Instantiate!(.templateOr!(Preds[1 .. $]), T); - } - } -} - -/// -@safe unittest -{ - import std.traits : isPointer, isUnsigned; - - alias isPtrOrUnsigned = templateOr!(isPointer, isUnsigned); - static assert( isPtrOrUnsigned!uint && isPtrOrUnsigned!(short*)); - static assert(!isPtrOrUnsigned!int && !isPtrOrUnsigned!(string)); - - // An empty sequence of predicates never yields true. - alias alwaysFalse = templateOr!(); - static assert(!alwaysFalse!int); -} - -version (StdUnittest) -@safe unittest -{ - static foreach (T; AliasSeq!(int, staticMap, 42)) - { - static assert( Instantiate!(templateOr!(testAlways), T)); - static assert( Instantiate!(templateOr!(testAlways, testAlways), T)); - static assert( Instantiate!(templateOr!(testAlways, testNever), T)); - static assert( Instantiate!(templateOr!(testNever, testAlways), T)); - static assert(!Instantiate!(templateOr!(), T)); - static assert(!Instantiate!(templateOr!(testNever), T)); - - static assert( Instantiate!(templateOr!(testAlways, testError), T)); - static assert( Instantiate!(templateOr!(testNever, testAlways, testError), T)); - // DMD @@BUG@@: Assertion fails for int, seems like a error gagging - // problem. The bug goes away when removing some of the other template - // instantiations in the module. - // static assert(!is(typeof(Instantiate!(templateOr!(testNever, testError), T)))); - } -} - -/** - * Converts any foreach-iterable entity (e.g. an input range) to an alias sequence. - * - * Params: - * iter = the entity to convert into an `AliasSeq`. It must be able to be able to be iterated over using - * a $(LINK2 https://dlang.org/spec/statement.html#foreach-statement, foreach-statement). - * - * Returns: - * An `AliasSeq` containing the values produced by iterating over `iter`. - */ -template aliasSeqOf(alias iter) -if (isIterable!(typeof(iter)) && !isInfinite!(typeof(iter))) -{ - import std.array : array; - - struct Impl - { - static foreach (size_t i, el; iter.array) - mixin(`auto e` ~ i.stringof ~ ` = el;`); - } - enum aliasSeqOf = Impl.init.tupleof; -} - -/// -@safe unittest -{ - import std.algorithm.iteration : map; - import std.algorithm.sorting : sort; - import std.string : capitalize; - - struct S - { - int a; - int c; - int b; - } - - alias capMembers = aliasSeqOf!([__traits(allMembers, S)].sort().map!capitalize()); - static assert(capMembers[0] == "A"); - static assert(capMembers[1] == "B"); - static assert(capMembers[2] == "C"); -} - -/// -@safe unittest -{ - static immutable REF = [0, 1, 2, 3]; - foreach (I, V; aliasSeqOf!([0, 1, 2, 3])) - { - static assert(V == I); - static assert(V == REF[I]); - } -} - -@safe unittest -{ - import std.conv : to, octal; - import std.range : iota; - //Testing compile time octal - foreach (I2; aliasSeqOf!(iota(0, 8))) - foreach (I1; aliasSeqOf!(iota(0, 8))) - { - enum oct = I2 * 8 + I1; - enum dec = I2 * 10 + I1; - enum str = to!string(dec); - static assert(octal!dec == oct); - static assert(octal!str == oct); - } -} - -@safe unittest -{ - enum REF = "日本語"d; - foreach (I, V; aliasSeqOf!"日本語"c) - { - static assert(V == REF[I]); - } -} - -@safe unittest -{ - struct S - { - int opApply(scope int delegate(ref int) dg) - { - foreach (int i; 3 .. 5) - if (auto r = dg(i)) - return r; - return 0; - } - } - static assert(aliasSeqOf!(S.init) == AliasSeq!(3, 4)); -} - -@safe unittest -{ - struct Infinite - { - int front(); - void popFront(); - enum empty = false; - } - enum infinite = Infinite(); - static assert(isInfinite!Infinite); - static assert(!__traits(compiles, aliasSeqOf!infinite)); -} - -/** - * $(LINK2 http://en.wikipedia.org/wiki/Partial_application, Partially applies) - * $(D_PARAM Template) by binding its first (left) or last (right) arguments - * to $(D_PARAM args). - * - * Behaves like the identity function when $(D_PARAM args) is empty. - * Params: - * Template = template to partially apply - * args = arguments to bind - * Returns: - * _Template with arity smaller than or equal to $(D_PARAM Template) - */ -template ApplyLeft(alias Template, args...) -{ - alias ApplyLeft(right...) = SmartAlias!(Template!(args, right)); -} - -/// Ditto -template ApplyRight(alias Template, args...) -{ - alias ApplyRight(left...) = SmartAlias!(Template!(left, args)); -} - -/// -@safe unittest -{ - // enum bool isImplicitlyConvertible(From, To) - import std.traits : isImplicitlyConvertible; - - static assert(allSatisfy!( - ApplyLeft!(isImplicitlyConvertible, ubyte), - short, ushort, int, uint, long, ulong)); - - static assert(is(Filter!(ApplyRight!(isImplicitlyConvertible, short), - ubyte, string, short, float, int) == AliasSeq!(ubyte, short))); -} - -/// -@safe unittest -{ - import std.traits : hasMember, ifTestable; - - struct T1 - { - bool foo; - } - - struct T2 - { - struct Test - { - bool opCast(T : bool)() { return true; } - } - - Test foo; - } - - static assert(allSatisfy!(ApplyRight!(hasMember, "foo"), T1, T2)); - static assert(allSatisfy!(ApplyRight!(ifTestable, a => a.foo), T1, T2)); -} - -/// -@safe unittest -{ - import std.traits : Largest; - - alias Types = AliasSeq!(byte, short, int, long); - - static assert(is(staticMap!(ApplyLeft!(Largest, short), Types) == - AliasSeq!(short, short, int, long))); - static assert(is(staticMap!(ApplyLeft!(Largest, int), Types) == - AliasSeq!(int, int, int, long))); -} - -/// -@safe unittest -{ - import std.traits : FunctionAttribute, SetFunctionAttributes; - - static void foo() @system; - static int bar(int) @system; - - alias SafeFunctions = AliasSeq!( - void function() @safe, - int function(int) @safe); - - static assert(is(staticMap!(ApplyRight!( - SetFunctionAttributes, "D", FunctionAttribute.safe), - typeof(&foo), typeof(&bar)) == SafeFunctions)); -} - -private template SmartAlias(T...) -{ - static if (T.length == 1) - { - alias SmartAlias = Alias!T; - } - else - { - alias SmartAlias = T; - } -} - -@safe unittest -{ - static assert(is(typeof({ - alias T(T0, int a, double b, alias T1, string c) = AliasSeq!(T0, a, b, T1, c); - alias T0 = ApplyRight!(ApplyLeft, ApplyRight); - alias T1 = T0!ApplyLeft; - alias T2 = T1!T; - alias T3 = T2!(3, "foo"); - alias T4 = T3!(short, 3, 3.3); - static assert(Pack!T4.equals!(short, 3, 3.3, 3, "foo")); - - import std.traits : isImplicitlyConvertible; - alias U1 = ApplyLeft!(ApplyRight, isImplicitlyConvertible); - alias U2 = U1!int; - enum U3 = U2!short; - static assert(U3); - }))); -} - -/** - * Creates an `AliasSeq` which repeats `items` exactly `n` times. - */ -template Repeat(size_t n, items...) -{ - static if (n == 0) - { - alias Repeat = AliasSeq!(); - } - else - { - alias Repeat = items; - enum log2n = - { - uint result = 0; - auto x = n; - while (x >>= 1) - ++result; - return result; - }(); - static foreach (i; 0 .. log2n) - { - Repeat = AliasSeq!(Repeat, Repeat); - } - Repeat = AliasSeq!(Repeat, Repeat!(n - (1u << log2n), items)); - } -} - -/// -@safe unittest -{ - alias ImInt0 = Repeat!(0, int); - static assert(is(ImInt0 == AliasSeq!())); - - alias ImInt1 = Repeat!(1, immutable(int)); - static assert(is(ImInt1 == AliasSeq!(immutable(int)))); - - alias Real3 = Repeat!(3, real); - static assert(is(Real3 == AliasSeq!(real, real, real))); - - alias Real12 = Repeat!(4, Real3); - static assert(is(Real12 == AliasSeq!(real, real, real, real, real, real, - real, real, real, real, real, real))); - - alias Composite = AliasSeq!(uint, int); - alias Composite2 = Repeat!(2, Composite); - static assert(is(Composite2 == AliasSeq!(uint, int, uint, int))); - - alias ImInt10 = Repeat!(10, int); - static assert(is(ImInt10 == AliasSeq!(int, int, int, int, int, int, int, int, int, int))); - - alias Big = Repeat!(1_000_000, int); -} - - -/// -@safe unittest -{ - auto staticArray(T, size_t n)(Repeat!(n, T) elems) - { - T[n] a = [elems]; - return a; - } - - auto a = staticArray!(long, 3)(3, 1, 4); - assert(is(typeof(a) == long[3])); - assert(a == [3, 1, 4]); -} - -/** - * Sorts an $(LREF AliasSeq) using `cmp`. - * - * Parameters: - * cmp = A template that returns a `bool` (if its first argument is less than the second one) - * or an `int` (-1 means less than, 0 means equal, 1 means greater than) - * - * items = The $(LREF AliasSeq) to sort - * - * Returns: The sorted alias sequence - */ -template staticSort(alias cmp, items...) -{ - static if (items.length < 2) - alias staticSort = items; - else - alias staticSort = staticMerge!(cmp, items.length / 2, - staticSort!(cmp, items[0 .. $ / 2]), - staticSort!(cmp, items[$ / 2 .. $])); -} - -/// -@safe unittest -{ - alias Nums = AliasSeq!(7, 2, 3, 23); - enum Comp(int N1, int N2) = N1 < N2; - static assert(AliasSeq!(2, 3, 7, 23) == staticSort!(Comp, Nums)); -} - -/// -@safe unittest -{ - alias Types = AliasSeq!(uint, short, ubyte, long, ulong); - enum Comp(T1, T2) = __traits(isUnsigned, T2) - __traits(isUnsigned, T1); - static assert(is(AliasSeq!(uint, ubyte, ulong, short, long) == staticSort!(Comp, - Types))); -} - -private template staticMerge(alias cmp, uint mid, items...) -{ - enum run = - { - static if (mid < items.length) - static foreach (i, item; items[0 .. mid]) - static if (!isLessEq!(cmp, item, items[mid])) - if (__ctfe) return i; - return mid; - }(); - static if (run == mid) - alias staticMerge = items; - else - alias staticMerge = AliasSeq!(items[0 .. run], items[mid], - staticMerge!(cmp, mid - run, items[run .. mid], items[mid + 1 .. $])); -} - -private template isLessEq(alias cmp, Seq...) -if (Seq.length == 2) -{ - private enum Result = cmp!(Seq[1], Seq[0]); - static if (is(typeof(Result) == bool)) - enum isLessEq = !Result; - else static if (is(typeof(Result) : int)) - enum isLessEq = Result >= 0; - else - static assert(0, typeof(Result).stringof ~ " is not a value comparison type"); -} - -/** - * Checks if an $(LREF AliasSeq) is sorted according to `cmp`. - * - * Parameters: - * cmp = A template that returns a `bool` (if its first argument is less than the second one) - * or an `int` (-1 means less than, 0 means equal, 1 means greater than) - * - * Seq = The $(LREF AliasSeq) to check - * - * Returns: `true` if `Seq` is sorted; otherwise `false` - */ -enum staticIsSorted(alias cmp, items...) = - { - static if (items.length > 1) - static foreach (i, item; items[1 .. $]) - static if (!isLessEq!(cmp, items[i], item)) - if (__ctfe) return false; - return true; - }(); - -/// -@safe unittest -{ - enum Comp(int N1, int N2) = N1 < N2; - static assert( staticIsSorted!(Comp, 2, 2)); - static assert( staticIsSorted!(Comp, 2, 3, 7, 23)); - static assert(!staticIsSorted!(Comp, 7, 2, 3, 23)); -} - -/// -@safe unittest -{ - enum Comp(T1, T2) = __traits(isUnsigned, T2) - __traits(isUnsigned, T1); - static assert( staticIsSorted!(Comp, uint, ubyte, ulong, short, long)); - static assert(!staticIsSorted!(Comp, uint, short, ubyte, long, ulong)); -} - -/** -Selects a subset of `Args` by stepping with fixed `stepSize` over the sequence. -A negative `stepSize` starts iteration with the last element. - -Params: - stepSize = Number of elements to increment on each iteration. Can't be `0`. - Args = Template arguments. - -Returns: An `AliasSeq` filtered by the selected stride. -*/ -template Stride(int stepSize, Args...) -if (stepSize != 0) -{ - alias Stride = AliasSeq!(); - static if (stepSize > 0) - { - static foreach (i; 0 .. (Args.length + stepSize - 1) / stepSize) - Stride = AliasSeq!(Stride, Args[i * stepSize]); - } - else - { - static foreach (i; 0 .. (Args.length - stepSize - 1) / -stepSize) - Stride = AliasSeq!(Stride, Args[$ - 1 + i * stepSize]); - } -} - -/// -@safe unittest -{ - static assert(is(Stride!(1, short, int, long) == AliasSeq!(short, int, long))); - static assert(is(Stride!(2, short, int, long) == AliasSeq!(short, long))); - static assert(is(Stride!(-1, short, int, long) == AliasSeq!(long, int, short))); - static assert(is(Stride!(-2, short, int, long) == AliasSeq!(long, short))); - - alias attribs = AliasSeq!(short, int, long, ushort, uint, ulong); - static assert(is(Stride!(3, attribs) == AliasSeq!(short, ushort))); - static assert(is(Stride!(3, attribs[1 .. $]) == AliasSeq!(int, uint))); - static assert(is(Stride!(-3, attribs) == AliasSeq!(ulong, long))); -} - -@safe unittest -{ - static assert(Pack!(Stride!(5, int)).equals!(int)); - static assert(Pack!(Stride!(-5, int)).equals!(int)); - static assert(!__traits(compiles, Stride!(0, int))); -} - -/** - * Instantiates the given template with the given parameters. - * - * Used to work around syntactic limitations of D with regard to instantiating - * a template from an alias sequence (e.g. `T[0]!(...)` is not valid) or a - * template returning another template (e.g. `Foo!(Bar)!(Baz)` is not allowed). - * - * Params: - * Template = The template to instantiate. - * Params = The parameters with which to instantiate the template. - * Returns: - * The instantiated template. - */ -alias Instantiate(alias Template, Params...) = Template!Params; - -/// -@safe unittest -{ - // ApplyRight combined with Instantiate can be used to apply various - // templates to the same parameters. - import std.string : leftJustify, center, rightJustify; - alias functions = staticMap!(ApplyRight!(Instantiate, string), - leftJustify, center, rightJustify); - string result = ""; - static foreach (f; functions) - { - { - auto x = &f; // not a template, but a function instantiation - result ~= x("hello", 7); - result ~= ";"; - } - } - - assert(result == "hello ; hello ; hello;"); -} - -// : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : // -private: - -/* - * [internal] Returns true if a and b are the same thing, or false if - * not. Both a and b can be types, literals, or symbols. - * - * How: When: - * a == b - at least one rvalue (literals, enums, function calls) - * __traits(isSame, a, b) - other cases (types, variables, functions, templates, etc.) - */ -private template isSame(alias a, alias b) -{ - static if (!is(typeof(&a && &b)) // at least one is an rvalue - && __traits(compiles, { enum isSame = a == b; })) // c-t comparable - { - enum isSame = a == b; - } - else - { - enum isSame = __traits(isSame, a, b); - } -} -// TODO: remove after https://github.com/dlang/dmd/pull/11320 and https://issues.dlang.org/show_bug.cgi?id=21889 are fixed -private template isSame(A, B) -{ - enum isSame = is(A == B); -} - -@safe unittest -{ - static assert(!isSame!(Object, const Object)); - static assert(!isSame!(Object, immutable Object)); - - static struct S {} - static assert(!isSame!(S, const S)); - static assert( isSame!(S(), S())); - - static class C {} - static assert(!isSame!(C, const C)); - - static assert( isSame!(int, int)); - static assert(!isSame!(int, const int)); - static assert(!isSame!(const int, immutable int)); - static assert(!isSame!(int, short)); - - enum a = 1, b = 1, c = 2, s = "a", t = "a"; - static assert( isSame!(1, 1)); - static assert( isSame!(a, 1)); - static assert( isSame!(a, b)); - static assert(!isSame!(b, c)); - static assert( isSame!("a", "a")); - static assert( isSame!(s, "a")); - static assert( isSame!(s, t)); - static assert(!isSame!(1, "1")); - static assert(!isSame!(a, "a")); - static assert( isSame!(isSame, isSame)); - static assert(!isSame!(isSame, a)); - - static assert(!isSame!(byte, a)); - static assert(!isSame!(short, isSame)); - static assert(!isSame!(a, int)); - static assert(!isSame!(long, isSame)); - - static immutable X = 1, Y = 1, Z = 2; - static assert( isSame!(X, X)); - static assert(!isSame!(X, Y)); - static assert(!isSame!(Y, Z)); - static assert( isSame!(X, 1)); - static assert( isSame!(1, X)); - - int foo(); - int bar(); - real baz(int); - static assert( isSame!(foo, foo)); - static assert(!isSame!(foo, bar)); - static assert(!isSame!(bar, baz)); - static assert( isSame!(baz, baz)); - static assert(!isSame!(foo, 0)); - - int x, y; - real z; - static assert( isSame!(x, x)); - static assert(!isSame!(x, y)); - static assert(!isSame!(y, z)); - static assert( isSame!(z, z)); - static assert(!isSame!(x, 0)); -} - -/* - * [internal] Wraps a sequence in a template. Used only in unittests. - */ -private template Pack(T...) -{ - alias Expand = T; - enum equals(U...) = isSame!(Pack!T, Pack!U); -} - -@safe unittest -{ - static assert( Pack!(1, int, "abc").equals!(1, int, "abc")); - static assert(!Pack!(1, int, "abc").equals!(1, int, "cba")); -} diff --git a/phobos/std/mmfile.d b/phobos/std/mmfile.d deleted file mode 100644 index b2cab31..0000000 --- a/phobos/std/mmfile.d +++ /dev/null @@ -1,855 +0,0 @@ -// Written in the D programming language. - -/** - * Read and write memory mapped files. - * - * Memory mapped files are a mechanism in operating systems that allows - * file access through virtual memory. After opening a file with `MmFile`, - * the contents can be read from or written to with standard slice / pointer operations. - * Changes to the memory are automatically reflected in the underlying file. - * - * Memory mapping can increase I/O performance of large files, compared to buffered - * read / write operations from `std.file` and `std.stdio`. However, I/O errors are - * not handled as safely: when for example the disk that the file is on gets removed, - * reading from it may result in a segfault. - * - * Copyright: Copyright The D Language Foundation 2004 - 2009. - * License: $(HTTP www.boost.org/LICENSE_1_0.txt, Boost License 1.0). - * Authors: $(HTTP digitalmars.com, Walter Bright), - * Matthew Wilson - * References: $(LINK https://en.wikipedia.org/wiki/Memory-mapped_file) - * Source: $(PHOBOSSRC std/mmfile.d) - * - * $(SCRIPT inhibitQuickIndex = 1;) - */ -/* Copyright The D Language Foundation 2004 - 2009. - * 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.mmfile; - -import core.stdc.errno; -import core.stdc.stdio; -import core.stdc.stdlib; -import std.conv, std.exception, std.stdio; -import std.file; -import std.path; -import std.string; - -import std.internal.cstring; - -//debug = MMFILE; - -version (Windows) -{ - import core.sys.windows.winbase; - import core.sys.windows.winnt; - import std.utf; - import std.windows.syserror; -} -else version (Posix) -{ - import core.sys.posix.fcntl; - import core.sys.posix.sys.mman; - import core.sys.posix.sys.stat; - import core.sys.posix.unistd; -} -else -{ - static assert(0); -} - -/** - * MmFile objects control the memory mapped file resource. - */ -class MmFile -{ - /** - * The mode the memory mapped file is opened with. - */ - enum Mode - { - read, /// Read existing file - readWriteNew, /// Delete existing file, write new file - readWrite, /// Read/Write existing file, create if not existing - readCopyOnWrite, /// Read/Write existing file, copy on write - } - - /** - * Open memory mapped file filename for reading. - * File is closed when the object instance is deleted. - * Throws: - * - On POSIX, $(REF ErrnoException, std, exception). - * - On Windows, $(REF WindowsException, std, windows, syserror). - */ - this(string filename) scope - { - this(filename, Mode.read, 0, null); - } - - version (linux) this(File file, Mode mode = Mode.read, ulong size = 0, - void* address = null, size_t window = 0) scope - { - // Save a copy of the File to make sure the fd stays open. - this.file = file; - this(file.fileno, mode, size, address, window); - } - - version (linux) private this(int fildes, Mode mode, ulong size, - void* address, size_t window) scope - { - int oflag; - int fmode; - - final switch (mode) - { - case Mode.read: - flags = MAP_SHARED; - prot = PROT_READ; - oflag = O_RDONLY; - fmode = 0; - break; - - case Mode.readWriteNew: - assert(size != 0); - flags = MAP_SHARED; - prot = PROT_READ | PROT_WRITE; - oflag = O_CREAT | O_RDWR | O_TRUNC; - fmode = octal!660; - break; - - case Mode.readWrite: - flags = MAP_SHARED; - prot = PROT_READ | PROT_WRITE; - oflag = O_CREAT | O_RDWR; - fmode = octal!660; - break; - - case Mode.readCopyOnWrite: - flags = MAP_PRIVATE; - prot = PROT_READ | PROT_WRITE; - oflag = O_RDWR; - fmode = 0; - break; - } - - fd = fildes; - - // Adjust size - stat_t statbuf = void; - errnoEnforce(fstat(fd, &statbuf) == 0); - if (prot & PROT_WRITE && size > statbuf.st_size) - { - // Need to make the file size bytes big - lseek(fd, cast(off_t)(size - 1), SEEK_SET); - char c = 0; - core.sys.posix.unistd.write(fd, &c, 1); - } - else if (prot & PROT_READ && size == 0) - size = statbuf.st_size; - this.size = size; - - // Map the file into memory! - size_t initial_map = (window && 2*window> 32); - hFileMap = CreateFileMappingW(hFile, null, flProtect, - hi, cast(uint) size, null); - wenforce(hFileMap, "CreateFileMapping"); - scope(failure) - { - CloseHandle(hFileMap); - hFileMap = null; - } - - if (size == 0 && filename != null) - { - uint sizehi; - uint sizelow = GetFileSize(hFile, &sizehi); - wenforce(sizelow != INVALID_FILE_SIZE || GetLastError() != ERROR_SUCCESS, - "GetFileSize"); - size = (cast(ulong) sizehi << 32) + sizelow; - } - this.size = size; - - size_t initial_map = (window && 2*window statbuf.st_size) - { - // Need to make the file size bytes big - .lseek(fd, cast(off_t)(size - 1), SEEK_SET); - char c = 0; - core.sys.posix.unistd.write(fd, &c, 1); - } - else if (prot & PROT_READ && size == 0) - size = statbuf.st_size; - } - else - { - fd = -1; - flags |= MAP_ANON; - } - this.size = size; - size_t initial_map = (window && 2*window= start && i < start+data.length; - } - - // unmap the current range - private void unmap() - { - debug (MMFILE) printf("MmFile.unmap()\n"); - version (Windows) - { - wenforce(!data.ptr || UnmapViewOfFile(data.ptr) != FALSE, "UnmapViewOfFile"); - } - else - { - errnoEnforce(!data.ptr || munmap(cast(void*) data, data.length) == 0, - "munmap failed"); - } - data = null; - } - - // map range - private void map(ulong start, size_t len) - { - debug (MMFILE) printf("MmFile.map(%lld, %d)\n", start, len); - void* p; - if (start+len > size) - len = cast(size_t)(size-start); - version (Windows) - { - uint hi = cast(uint)(start >> 32); - p = MapViewOfFileEx(hFileMap, dwDesiredAccess, hi, cast(uint) start, len, address); - wenforce(p, "MapViewOfFileEx"); - } - else - { - p = mmap(address, len, prot, flags, fd, cast(off_t) start); - errnoEnforce(p != MAP_FAILED); - } - data = p[0 .. len]; - this.start = start; - } - - // ensure a given position is mapped - private void ensureMapped(ulong i) - { - debug (MMFILE) printf("MmFile.ensureMapped(%lld)\n", i); - if (!mapped(i)) - { - unmap(); - if (window == 0) - { - map(0,cast(size_t) size); - } - else - { - ulong block = i/window; - if (block == 0) - map(0,2*window); - else - map(window*(block-1),3*window); - } - } - } - - // ensure a given range is mapped - private void ensureMapped(ulong i, ulong j) - { - debug (MMFILE) printf("MmFile.ensureMapped(%lld, %lld)\n", i, j); - if (!mapped(i) || !mapped(j-1)) - { - unmap(); - if (window == 0) - { - map(0,cast(size_t) size); - } - else - { - ulong iblock = i/window; - ulong jblock = (j-1)/window; - if (iblock == 0) - { - map(0,cast(size_t)(window*(jblock+2))); - } - else - { - map(window*(iblock-1),cast(size_t)(window*(jblock-iblock+3))); - } - } - } - } - -private: - string filename; - void[] data; - ulong start; - size_t window; - ulong size; - Mode mMode; - void* address; - version (linux) File file; - - version (Windows) - { - HANDLE hFile = INVALID_HANDLE_VALUE; - HANDLE hFileMap = null; - uint dwDesiredAccess; - } - else version (Posix) - { - int fd; - int prot; - int flags; - int fmode; - } - else - { - static assert(0); - } -} - -/// Read an existing file -@system unittest -{ - import std.file; - std.file.write(deleteme, "hello"); // deleteme is a temporary filename - scope(exit) remove(deleteme); - - // Use a scope class so the file will be closed at the end of this function - scope mmfile = new MmFile(deleteme); - - assert(mmfile.length == "hello".length); - - // Access file contents with the slice operator - // This is typed as `void[]`, so cast to `char[]` or `ubyte[]` to use it - const data = cast(const(char)[]) mmfile[]; - - // At this point, the file content may not have been read yet. - // In that case, the following memory access will intentionally - // trigger a page fault, causing the kernel to load the file contents - assert(data[0 .. 5] == "hello"); -} - -/// Write a new file -@system unittest -{ - import std.file; - scope(exit) remove(deleteme); - - scope mmfile = new MmFile(deleteme, MmFile.Mode.readWriteNew, 5, null); - assert(mmfile.length == 5); - - auto data = cast(ubyte[]) mmfile[]; - - // This write to memory will be reflected in the file contents - data[] = '\n'; - - mmfile.flush(); - - assert(std.file.read(deleteme) == "\n\n\n\n\n"); -} - -@system unittest -{ - import core.memory : GC; - import std.file : deleteme; - - const size_t K = 1024; - size_t win = 64*K; // assume the page size is 64K - version (Windows) - { - /+ these aren't defined in core.sys.windows.windows so let's use default - SYSTEM_INFO sysinfo; - GetSystemInfo(&sysinfo); - win = sysinfo.dwAllocationGranularity; - +/ - } - else version (Posix) - { - import core.sys.posix.unistd; - win = cast(size_t) sysconf(_SC_PAGESIZE); - } - string test_file = std.file.deleteme ~ "-testing.txt"; - MmFile mf = new MmFile(test_file,MmFile.Mode.readWriteNew, - 100*K,null,win); - ubyte[] str = cast(ubyte[])"1234567890"; - ubyte[] data = cast(ubyte[]) mf[0 .. 10]; - data[] = str[]; - assert( mf[0 .. 10] == str ); - data = cast(ubyte[]) mf[50 .. 60]; - data[] = str[]; - assert( mf[50 .. 60] == str ); - ubyte[] data2 = cast(ubyte[]) mf[20*K .. 60*K]; - assert( data2.length == 40*K ); - assert( data2[$-1] == 0 ); - mf[100*K-1] = cast(ubyte)'b'; - data2 = cast(ubyte[]) mf[21*K .. 100*K]; - assert( data2.length == 79*K ); - assert( data2[$-1] == 'b' ); - - destroy(mf); - - std.file.remove(test_file); - // Create anonymous mapping - auto test = new MmFile(null, MmFile.Mode.readWriteNew, 1024*1024, null); -} - -version (linux) -@system unittest // https://issues.dlang.org/show_bug.cgi?id=14868 -{ - import std.file : deleteme; - import std.typecons : scoped; - - // Test retaining ownership of File/fd - - auto fn = std.file.deleteme ~ "-testing.txt"; - scope(exit) std.file.remove(fn); - File(fn, "wb").writeln("Testing!"); - scoped!MmFile(File(fn)); - - // Test that unique ownership of File actually leads to the fd being closed - - auto f = File(fn); - auto fd = f.fileno; - { - auto mf = scoped!MmFile(f); - f = File.init; - } - assert(.close(fd) == -1); -} - -// https://issues.dlang.org/show_bug.cgi?id=14994 -// https://issues.dlang.org/show_bug.cgi?id=14995 -@system unittest -{ - import std.file : deleteme; - import std.typecons : scoped; - - // Zero-length map may or may not be valid on OSX and NetBSD - version (OSX) - import std.exception : verifyThrown = collectException; - version (NetBSD) - import std.exception : verifyThrown = collectException; - else - import std.exception : verifyThrown = assertThrown; - - auto fn = std.file.deleteme ~ "-testing.txt"; - scope(exit) std.file.remove(fn); - verifyThrown(scoped!MmFile(fn, MmFile.Mode.readWrite, 0, null)); -} - -@system unittest -{ - MmFile shar = new MmFile(null, MmFile.Mode.readWrite, 10, null, 0); - void[] output = shar[0 .. $]; -} - -@system unittest -{ - import std.file : deleteme; - auto name = std.file.deleteme ~ "-test.tmp"; - scope(exit) std.file.remove(name); - - std.file.write(name, "abcd"); - { - scope MmFile mmf = new MmFile(name); - string p; - - assert(mmf[0] == 'a'); - p = cast(string) mmf[]; - assert(p[1] == 'b'); - p = cast(string) mmf[0 .. 4]; - assert(p[2] == 'c'); - } - { - scope MmFile mmf = new MmFile(name, MmFile.Mode.read, 0, null); - string p; - - assert(mmf[0] == 'a'); - p = cast(string) mmf[]; - assert(mmf.length == 4); - assert(p[1] == 'b'); - p = cast(string) mmf[0 .. 4]; - assert(p[2] == 'c'); - } - std.file.remove(name); - { - scope MmFile mmf = new MmFile(name, MmFile.Mode.readWriteNew, 4, null); - char[] p = cast(char[]) mmf[]; - p[] = "1234"; - mmf[3] = '5'; - assert(mmf[2] == '3'); - assert(mmf[3] == '5'); - } - { - string p = cast(string) std.file.read(name); - assert(p[] == "1235"); - } - { - scope MmFile mmf = new MmFile(name, MmFile.Mode.readWriteNew, 4, null); - char[] p = cast(char[]) mmf[]; - p[] = "5678"; - mmf[3] = '5'; - assert(mmf[2] == '7'); - assert(mmf[3] == '5'); - assert(cast(string) mmf[] == "5675"); - } - { - string p = cast(string) std.file.read(name); - assert(p[] == "5675"); - } - { - scope MmFile mmf = new MmFile(name, MmFile.Mode.readWrite, 4, null); - char[] p = cast(char[]) mmf[]; - assert(cast(char[]) mmf[] == "5675"); - p[] = "9102"; - mmf[2] = '5'; - assert(cast(string) mmf[] == "9152"); - } - { - string p = cast(string) std.file.read(name); - assert(p[] == "9152"); - } - std.file.remove(name); - { - scope MmFile mmf = new MmFile(name, MmFile.Mode.readWrite, 4, null); - char[] p = cast(char[]) mmf[]; - p[] = "abcd"; - mmf[2] = '5'; - assert(cast(string) mmf[] == "ab5d"); - } - { - string p = cast(string) std.file.read(name); - assert(p[] == "ab5d"); - } - { - scope MmFile mmf = new MmFile(name, MmFile.Mode.readCopyOnWrite, 4, null); - char[] p = cast(char[]) mmf[]; - assert(cast(string) mmf[] == "ab5d"); - p[] = "9102"; - mmf[2] = '5'; - assert(cast(string) mmf[] == "9152"); - } - { - string p = cast(string) std.file.read(name); - assert(p[] == "ab5d"); - } -} diff --git a/phobos/std/net/curl.d b/phobos/std/net/curl.d deleted file mode 100644 index 3f82301..0000000 --- a/phobos/std/net/curl.d +++ /dev/null @@ -1,5305 +0,0 @@ -// Written in the D programming language. - -/** -Networking client functionality as provided by $(HTTP curl.haxx.se/libcurl, -libcurl). The libcurl library must be installed on the system in order to use -this module. - -$(SCRIPT inhibitQuickIndex = 1;) - -$(DIVC quickindex, -$(BOOKTABLE , -$(TR $(TH Category) $(TH Functions) -) -$(TR $(TDNW High level) $(TD $(MYREF download) $(MYREF upload) $(MYREF get) -$(MYREF post) $(MYREF put) $(MYREF del) $(MYREF options) $(MYREF trace) -$(MYREF connect) $(MYREF byLine) $(MYREF byChunk) -$(MYREF byLineAsync) $(MYREF byChunkAsync) ) -) -$(TR $(TDNW Low level) $(TD $(MYREF HTTP) $(MYREF FTP) $(MYREF -SMTP) ) -) -) -) - -Note: -You may need to link with the $(B curl) library, e.g. by adding $(D "libs": ["curl"]) -to your $(B dub.json) file if you are using $(LINK2 http://code.dlang.org, DUB). - -Windows x86 note: -A DMD compatible libcurl static library can be downloaded from the dlang.org -$(LINK2 https://downloads.dlang.org/other/index.html, download archive page). - -This module is not available for iOS, tvOS or watchOS. - -Compared to using libcurl directly, this module allows simpler client code for -common uses, requires no unsafe operations, and integrates better with the rest -of the language. Furthermore it provides $(MREF_ALTTEXT range, std,range) -access to protocols supported by libcurl both synchronously and asynchronously. - -A high level and a low level API are available. The high level API is built -entirely on top of the low level one. - -The high level API is for commonly used functionality such as HTTP/FTP get. The -$(LREF byLineAsync) and $(LREF byChunkAsync) functions asynchronously -perform the request given, outputting the fetched content into a $(MREF_ALTTEXT range, std,range). - -The low level API allows for streaming, setting request headers and cookies, and other advanced features. - -$(BOOKTABLE Cheat Sheet, -$(TR $(TH Function Name) $(TH Description) -) -$(LEADINGROW High level) -$(TR $(TDNW $(LREF download)) $(TD $(D -download("ftp.digitalmars.com/sieve.ds", "/tmp/downloaded-ftp-file")) -downloads file from URL to file system.) -) -$(TR $(TDNW $(LREF upload)) $(TD $(D -upload("/tmp/downloaded-ftp-file", "ftp.digitalmars.com/sieve.ds");) -uploads file from file system to URL.) -) -$(TR $(TDNW $(LREF get)) $(TD $(D -get("dlang.org")) returns a char[] containing the dlang.org web page.) -) -$(TR $(TDNW $(LREF put)) $(TD $(D -put("dlang.org", "Hi")) returns a char[] containing -the dlang.org web page. after a HTTP PUT of "hi") -) -$(TR $(TDNW $(LREF post)) $(TD $(D -post("dlang.org", "Hi")) returns a char[] containing -the dlang.org web page. after a HTTP POST of "hi") -) -$(TR $(TDNW $(LREF byLine)) $(TD $(D -byLine("dlang.org")) returns a range of char[] containing the -dlang.org web page.) -) -$(TR $(TDNW $(LREF byChunk)) $(TD $(D -byChunk("dlang.org", 10)) returns a range of ubyte[10] containing the -dlang.org web page.) -) -$(TR $(TDNW $(LREF byLineAsync)) $(TD $(D -byLineAsync("dlang.org")) asynchronously returns a range of char[] containing the dlang.org web - page.) -) -$(TR $(TDNW $(LREF byChunkAsync)) $(TD $(D -byChunkAsync("dlang.org", 10)) asynchronously returns a range of ubyte[10] containing the -dlang.org web page.) -) -$(LEADINGROW Low level -) -$(TR $(TDNW $(LREF HTTP)) $(TD Struct for advanced HTTP usage)) -$(TR $(TDNW $(LREF FTP)) $(TD Struct for advanced FTP usage)) -$(TR $(TDNW $(LREF SMTP)) $(TD Struct for advanced SMTP usage)) -) - - -Example: ---- -import std.net.curl, std.stdio; - -// Return a char[] containing the content specified by a URL -auto content = get("dlang.org"); - -// Post data and return a char[] containing the content specified by a URL -auto content = post("mydomain.com/here.cgi", ["name1" : "value1", "name2" : "value2"]); - -// Get content of file from ftp server -auto content = get("ftp.digitalmars.com/sieve.ds"); - -// Post and print out content line by line. The request is done in another thread. -foreach (line; byLineAsync("dlang.org", "Post data")) - writeln(line); - -// Get using a line range and proxy settings -auto client = HTTP(); -client.proxy = "1.2.3.4"; -foreach (line; byLine("dlang.org", client)) - writeln(line); ---- - -For more control than the high level functions provide, use the low level API: - -Example: ---- -import std.net.curl, std.stdio; - -// GET with custom data receivers -auto http = HTTP("dlang.org"); -http.onReceiveHeader = - (in char[] key, in char[] value) { writeln(key, ": ", value); }; -http.onReceive = (ubyte[] data) { /+ drop +/ return data.length; }; -http.perform(); ---- - -First, an instance of the reference-counted HTTP struct is created. Then the -custom delegates are set. These will be called whenever the HTTP instance -receives a header and a data buffer, respectively. In this simple example, the -headers are written to stdout and the data is ignored. If the request is -stopped before it has finished then return something less than data.length from -the onReceive callback. See $(LREF onReceiveHeader)/$(LREF onReceive) for more -information. Finally, the HTTP request is performed by calling perform(), which is -synchronous. - -Source: $(PHOBOSSRC std/net/curl.d) - -Copyright: Copyright Jonas Drewsen 2011-2012 -License: $(HTTP www.boost.org/LICENSE_1_0.txt, Boost License 1.0). -Authors: Jonas Drewsen. Some of the SMTP code contributed by Jimmy Cao. - -Credits: The functionality is based on $(HTTP curl.haxx.se/libcurl, libcurl). - libcurl is licensed under an MIT/X derivative license. -*/ -/* - Copyright Jonas Drewsen 2011 - 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.net.curl; - -public import etc.c.curl : CurlOption; -import core.time : dur; -import etc.c.curl : CURLcode; -import std.range.primitives; -import std.encoding : EncodingScheme; -import std.traits : isSomeChar; -import std.typecons : Flag, Yes, No, Tuple; - -version (iOS) - version = iOSDerived; -else version (TVOS) - version = iOSDerived; -else version (WatchOS) - version = iOSDerived; - -version (iOSDerived) {} -else: - -version (StdUnittest) -{ - import std.socket : Socket, SocketShutdown; - - private struct TestServer - { - import std.concurrency : Tid; - - import std.socket : Socket, TcpSocket; - - string addr() { return _addr; } - - void handle(void function(Socket s) dg) - { - import std.concurrency : send; - tid.send(dg); - } - - private: - string _addr; - Tid tid; - TcpSocket sock; - - static void loop(shared TcpSocket listener) - { - import std.concurrency : OwnerTerminated, receiveOnly; - import std.stdio : stderr; - - try while (true) - { - void function(Socket) handler = void; - try - handler = receiveOnly!(typeof(handler)); - catch (OwnerTerminated) - return; - handler((cast() listener).accept); - } - catch (Throwable e) - { - // https://issues.dlang.org/show_bug.cgi?id=7018 - stderr.writeln(e); - } - } - } - - private TestServer startServer() - { - import std.concurrency : spawn; - import std.socket : INADDR_LOOPBACK, InternetAddress, TcpSocket; - - tlsInit = true; - auto sock = new TcpSocket; - sock.bind(new InternetAddress(INADDR_LOOPBACK, InternetAddress.PORT_ANY)); - sock.listen(1); - auto addr = sock.localAddress.toString(); - auto tid = spawn(&TestServer.loop, cast(shared) sock); - return TestServer(addr, tid, sock); - } - - /** Test server */ - __gshared TestServer server; - /** Thread-local storage init */ - bool tlsInit; - - private ref TestServer testServer() - { - import std.concurrency : initOnce; - return initOnce!server(startServer()); - } - - static ~this() - { - // terminate server from a thread local dtor of the thread that started it, - // because thread_joinall is called before shared module dtors - if (tlsInit && server.sock) - { - server.sock.shutdown(SocketShutdown.RECEIVE); - server.sock.close(); - } - } - - private struct Request(T) - { - string hdrs; - immutable(T)[] bdy; - } - - private Request!T recvReq(T=char)(Socket s) - { - import std.algorithm.comparison : min; - import std.algorithm.searching : find, canFind; - import std.conv : to; - import std.regex : ctRegex, matchFirst; - - ubyte[1024] tmp=void; - ubyte[] buf; - - while (true) - { - auto nbytes = s.receive(tmp[]); - assert(nbytes >= 0); - - immutable beg = buf.length > 3 ? buf.length - 3 : 0; - buf ~= tmp[0 .. nbytes]; - auto bdy = buf[beg .. $].find(cast(ubyte[])"\r\n\r\n"); - if (bdy.empty) - continue; - - auto hdrs = cast(string) buf[0 .. $ - bdy.length]; - bdy.popFrontN(4); - // no support for chunked transfer-encoding - if (auto m = hdrs.matchFirst(ctRegex!(`Content-Length: ([0-9]+)`, "i"))) - { - import std.uni : asUpperCase; - if (hdrs.asUpperCase.canFind("EXPECT: 100-CONTINUE")) - s.send(httpContinue); - - size_t remain = m.captures[1].to!size_t - bdy.length; - while (remain) - { - nbytes = s.receive(tmp[0 .. min(remain, $)]); - assert(nbytes >= 0); - buf ~= tmp[0 .. nbytes]; - remain -= nbytes; - } - } - else - { - assert(bdy.empty); - } - bdy = buf[hdrs.length + 4 .. $]; - return typeof(return)(hdrs, cast(immutable(T)[])bdy); - } - } - - private string httpOK(string msg) - { - import std.conv : to; - - return "HTTP/1.1 200 OK\r\n"~ - "Content-Type: text/plain\r\n"~ - "Content-Length: "~msg.length.to!string~"\r\n"~ - "\r\n"~ - msg; - } - - private string httpOK() - { - return "HTTP/1.1 200 OK\r\n"~ - "Content-Length: 0\r\n"~ - "\r\n"; - } - - private string httpNotFound() - { - return "HTTP/1.1 404 Not Found\r\n"~ - "Content-Length: 0\r\n"~ - "\r\n"; - } - - private enum httpContinue = "HTTP/1.1 100 Continue\r\n\r\n"; -} -version (StdDdoc) import std.stdio; - -// Default data timeout for Protocols -private enum _defaultDataTimeout = dur!"minutes"(2); - -/** -Macros: - -CALLBACK_PARAMS = $(TABLE , - $(DDOC_PARAM_ROW - $(DDOC_PARAM_ID $(DDOC_PARAM dlTotal)) - $(DDOC_PARAM_DESC total bytes to download) - ) - $(DDOC_PARAM_ROW - $(DDOC_PARAM_ID $(DDOC_PARAM dlNow)) - $(DDOC_PARAM_DESC currently downloaded bytes) - ) - $(DDOC_PARAM_ROW - $(DDOC_PARAM_ID $(DDOC_PARAM ulTotal)) - $(DDOC_PARAM_DESC total bytes to upload) - ) - $(DDOC_PARAM_ROW - $(DDOC_PARAM_ID $(DDOC_PARAM ulNow)) - $(DDOC_PARAM_DESC currently uploaded bytes) - ) -) -*/ - -/** Connection type used when the URL should be used to auto detect the protocol. - * - * This struct is used as placeholder for the connection parameter when calling - * the high level API and the connection type (HTTP/FTP) should be guessed by - * inspecting the URL parameter. - * - * The rules for guessing the protocol are: - * 1, if URL starts with ftp://, ftps:// or ftp. then FTP connection is assumed. - * 2, HTTP connection otherwise. - * - * Example: - * --- - * import std.net.curl; - * // Two requests below will do the same. - * char[] content; - * - * // Explicit connection provided - * content = get!HTTP("dlang.org"); - * - * // Guess connection type by looking at the URL - * content = get!AutoProtocol("ftp://foo.com/file"); - * // and since AutoProtocol is default this is the same as - * content = get("ftp://foo.com/file"); - * // and will end up detecting FTP from the url and be the same as - * content = get!FTP("ftp://foo.com/file"); - * --- - */ -struct AutoProtocol { } - -// Returns true if the url points to an FTP resource -private bool isFTPUrl(const(char)[] url) -{ - import std.algorithm.searching : startsWith; - import std.uni : toLower; - - return startsWith(url.toLower(), "ftp://", "ftps://", "ftp.") != 0; -} - -// Is true if the Conn type is a valid Curl Connection type. -private template isCurlConn(Conn) -{ - enum auto isCurlConn = is(Conn : HTTP) || - is(Conn : FTP) || is(Conn : AutoProtocol); -} - -/** HTTP/FTP download to local file system. - * - * Params: - * url = resource to download - * saveToPath = path to store the downloaded content on local disk - * conn = connection to use e.g. FTP or HTTP. The default AutoProtocol will - * guess connection type and create a new instance for this call only. - * - * Example: - * ---- - * import std.net.curl; - * download("https://httpbin.org/get", "/tmp/downloaded-http-file"); - * ---- - */ -void download(Conn = AutoProtocol)(const(char)[] url, string saveToPath, Conn conn = Conn()) -if (isCurlConn!Conn) -{ - static if (is(Conn : HTTP) || is(Conn : FTP)) - { - import std.stdio : File; - conn.url = url; - auto f = File(saveToPath, "wb"); - conn.onReceive = (ubyte[] data) { f.rawWrite(data); return data.length; }; - conn.perform(); - } - else - { - if (isFTPUrl(url)) - return download!FTP(url, saveToPath, FTP()); - else - return download!HTTP(url, saveToPath, HTTP()); - } -} - -@system unittest -{ - import std.algorithm.searching : canFind; - static import std.file; - - foreach (host; [testServer.addr, "http://"~testServer.addr]) - { - testServer.handle((s) { - assert(s.recvReq.hdrs.canFind("GET /")); - s.send(httpOK("Hello world")); - }); - auto fn = std.file.deleteme; - scope (exit) - { - if (std.file.exists(fn)) - std.file.remove(fn); - } - download(host, fn); - assert(std.file.readText(fn) == "Hello world"); - } -} - -/** Upload file from local files system using the HTTP or FTP protocol. - * - * Params: - * loadFromPath = path load data from local disk. - * url = resource to upload to - * conn = connection to use e.g. FTP or HTTP. The default AutoProtocol will - * guess connection type and create a new instance for this call only. - * - * Example: - * ---- - * import std.net.curl; - * upload("/tmp/downloaded-ftp-file", "ftp.digitalmars.com/sieve.ds"); - * upload("/tmp/downloaded-http-file", "https://httpbin.org/post"); - * ---- - */ -void upload(Conn = AutoProtocol)(string loadFromPath, const(char)[] url, Conn conn = Conn()) -if (isCurlConn!Conn) -{ - static if (is(Conn : HTTP)) - { - conn.url = url; - conn.method = HTTP.Method.put; - } - else static if (is(Conn : FTP)) - { - conn.url = url; - conn.handle.set(CurlOption.upload, 1L); - } - else - { - if (isFTPUrl(url)) - return upload!FTP(loadFromPath, url, FTP()); - else - return upload!HTTP(loadFromPath, url, HTTP()); - } - - static if (is(Conn : HTTP) || is(Conn : FTP)) - { - import std.stdio : File; - auto f = File(loadFromPath, "rb"); - conn.onSend = buf => f.rawRead(buf).length; - immutable sz = f.size; - if (sz != ulong.max) - conn.contentLength = sz; - conn.perform(); - } -} - -@system unittest -{ - import std.algorithm.searching : canFind; - static import std.file; - - foreach (host; [testServer.addr, "http://"~testServer.addr]) - { - auto fn = std.file.deleteme; - scope (exit) - { - if (std.file.exists(fn)) - std.file.remove(fn); - } - std.file.write(fn, "upload data\n"); - testServer.handle((s) { - auto req = s.recvReq; - assert(req.hdrs.canFind("PUT /path")); - assert(req.bdy.canFind("upload data")); - s.send(httpOK()); - }); - upload(fn, host ~ "/path"); - } -} - -/** HTTP/FTP get content. - * - * Params: - * url = resource to get - * conn = connection to use e.g. FTP or HTTP. The default AutoProtocol will - * guess connection type and create a new instance for this call only. - * - * The template parameter `T` specifies the type to return. Possible values - * are `char` and `ubyte` to return `char[]` or `ubyte[]`. If asking - * for `char`, content will be converted from the connection character set - * (specified in HTTP response headers or FTP connection properties, both ISO-8859-1 - * by default) to UTF-8. - * - * Example: - * ---- - * import std.net.curl; - * auto content = get("https://httpbin.org/get"); - * ---- - * - * Returns: - * A T[] range containing the content of the resource pointed to by the URL. - * - * Throws: - * - * `CurlException` on error. - * - * See_Also: $(LREF HTTP.Method) - */ -T[] get(Conn = AutoProtocol, T = char)(const(char)[] url, Conn conn = Conn()) -if ( isCurlConn!Conn && (is(T == char) || is(T == ubyte)) ) -{ - static if (is(Conn : HTTP)) - { - conn.method = HTTP.Method.get; - return _basicHTTP!(T)(url, "", conn); - - } - else static if (is(Conn : FTP)) - { - return _basicFTP!(T)(url, "", conn); - } - else - { - if (isFTPUrl(url)) - return get!(FTP,T)(url, FTP()); - else - return get!(HTTP,T)(url, HTTP()); - } -} - -@system unittest -{ - import std.algorithm.searching : canFind; - - foreach (host; [testServer.addr, "http://"~testServer.addr]) - { - testServer.handle((s) { - assert(s.recvReq.hdrs.canFind("GET /path")); - s.send(httpOK("GETRESPONSE")); - }); - auto res = get(host ~ "/path"); - assert(res == "GETRESPONSE"); - } -} - - -/** HTTP post content. - * - * Params: - * url = resource to post to - * postDict = data to send as the body of the request. An associative array - * of `string` is accepted and will be encoded using - * www-form-urlencoding - * postData = data to send as the body of the request. An array - * of an arbitrary type is accepted and will be cast to ubyte[] - * before sending it. - * conn = HTTP connection to use - * T = The template parameter `T` specifies the type to return. Possible values - * are `char` and `ubyte` to return `char[]` or `ubyte[]`. If asking - * for `char`, content will be converted from the connection character set - * (specified in HTTP response headers or FTP connection properties, both ISO-8859-1 - * by default) to UTF-8. - * - * Examples: - * ---- - * import std.net.curl; - * - * auto content1 = post("https://httpbin.org/post", ["name1" : "value1", "name2" : "value2"]); - * auto content2 = post("https://httpbin.org/post", [1,2,3,4]); - * ---- - * - * Returns: - * A T[] range containing the content of the resource pointed to by the URL. - * - * See_Also: $(LREF HTTP.Method) - */ -T[] post(T = char, PostUnit)(const(char)[] url, const(PostUnit)[] postData, HTTP conn = HTTP()) -if (is(T == char) || is(T == ubyte)) -{ - conn.method = HTTP.Method.post; - return _basicHTTP!(T)(url, postData, conn); -} - -@system unittest -{ - import std.algorithm.searching : canFind; - - foreach (host; [testServer.addr, "http://"~testServer.addr]) - { - testServer.handle((s) { - auto req = s.recvReq; - assert(req.hdrs.canFind("POST /path")); - assert(req.bdy.canFind("POSTBODY")); - s.send(httpOK("POSTRESPONSE")); - }); - auto res = post(host ~ "/path", "POSTBODY"); - assert(res == "POSTRESPONSE"); - } -} - -@system unittest -{ - import std.algorithm.searching : canFind; - - auto data = new ubyte[](256); - foreach (i, ref ub; data) - ub = cast(ubyte) i; - - testServer.handle((s) { - auto req = s.recvReq!ubyte; - assert(req.bdy.canFind(cast(ubyte[])[0, 1, 2, 3, 4])); - assert(req.bdy.canFind(cast(ubyte[])[253, 254, 255])); - s.send(httpOK(cast(ubyte[])[17, 27, 35, 41])); - }); - auto res = post!ubyte(testServer.addr, data); - assert(res == cast(ubyte[])[17, 27, 35, 41]); -} - -/// ditto -T[] post(T = char)(const(char)[] url, string[string] postDict, HTTP conn = HTTP()) -if (is(T == char) || is(T == ubyte)) -{ - import std.uri : urlEncode; - - return post!T(url, urlEncode(postDict), conn); -} - -@system unittest -{ - import std.algorithm.searching : canFind; - import std.meta : AliasSeq; - - static immutable expected = ["name1=value1&name2=value2", "name2=value2&name1=value1"]; - - foreach (host; [testServer.addr, "http://" ~ testServer.addr]) - { - foreach (T; AliasSeq!(char, ubyte)) - { - testServer.handle((s) { - auto req = s.recvReq!char; - s.send(httpOK(req.bdy)); - }); - auto res = post!T(host ~ "/path", ["name1" : "value1", "name2" : "value2"]); - assert(canFind(expected, res)); - } - } -} - -/** HTTP/FTP put content. - * - * Params: - * url = resource to put - * putData = data to send as the body of the request. An array - * of an arbitrary type is accepted and will be cast to ubyte[] - * before sending it. - * conn = connection to use e.g. FTP or HTTP. The default AutoProtocol will - * guess connection type and create a new instance for this call only. - * - * The template parameter `T` specifies the type to return. Possible values - * are `char` and `ubyte` to return `char[]` or `ubyte[]`. If asking - * for `char`, content will be converted from the connection character set - * (specified in HTTP response headers or FTP connection properties, both ISO-8859-1 - * by default) to UTF-8. - * - * Example: - * ---- - * import std.net.curl; - * auto content = put("https://httpbin.org/put", - * "Putting this data"); - * ---- - * - * Returns: - * A T[] range containing the content of the resource pointed to by the URL. - * - * See_Also: $(LREF HTTP.Method) - */ -T[] put(Conn = AutoProtocol, T = char, PutUnit)(const(char)[] url, const(PutUnit)[] putData, - Conn conn = Conn()) -if ( isCurlConn!Conn && (is(T == char) || is(T == ubyte)) ) -{ - static if (is(Conn : HTTP)) - { - conn.method = HTTP.Method.put; - return _basicHTTP!(T)(url, putData, conn); - } - else static if (is(Conn : FTP)) - { - return _basicFTP!(T)(url, putData, conn); - } - else - { - if (isFTPUrl(url)) - return put!(FTP,T)(url, putData, FTP()); - else - return put!(HTTP,T)(url, putData, HTTP()); - } -} - -@system unittest -{ - import std.algorithm.searching : canFind; - - foreach (host; [testServer.addr, "http://"~testServer.addr]) - { - testServer.handle((s) { - auto req = s.recvReq; - assert(req.hdrs.canFind("PUT /path")); - assert(req.bdy.canFind("PUTBODY")); - s.send(httpOK("PUTRESPONSE")); - }); - auto res = put(host ~ "/path", "PUTBODY"); - assert(res == "PUTRESPONSE"); - } -} - - -/** HTTP/FTP delete content. - * - * Params: - * url = resource to delete - * conn = connection to use e.g. FTP or HTTP. The default AutoProtocol will - * guess connection type and create a new instance for this call only. - * - * Example: - * ---- - * import std.net.curl; - * del("https://httpbin.org/delete"); - * ---- - * - * See_Also: $(LREF HTTP.Method) - */ -void del(Conn = AutoProtocol)(const(char)[] url, Conn conn = Conn()) -if (isCurlConn!Conn) -{ - static if (is(Conn : HTTP)) - { - conn.method = HTTP.Method.del; - _basicHTTP!char(url, cast(void[]) null, conn); - } - else static if (is(Conn : FTP)) - { - import std.algorithm.searching : findSplitAfter; - import std.conv : text; - import std.exception : enforce; - - auto trimmed = url.findSplitAfter("ftp://")[1]; - auto t = trimmed.findSplitAfter("/"); - enum minDomainNameLength = 3; - enforce!CurlException(t[0].length > minDomainNameLength, - text("Invalid FTP URL for delete ", url)); - conn.url = t[0]; - - enforce!CurlException(!t[1].empty, - text("No filename specified to delete for URL ", url)); - conn.addCommand("DELE " ~ t[1]); - conn.perform(); - } - else - { - if (isFTPUrl(url)) - return del!FTP(url, FTP()); - else - return del!HTTP(url, HTTP()); - } -} - -@system unittest -{ - import std.algorithm.searching : canFind; - - foreach (host; [testServer.addr, "http://"~testServer.addr]) - { - testServer.handle((s) { - auto req = s.recvReq; - assert(req.hdrs.canFind("DELETE /path")); - s.send(httpOK()); - }); - del(host ~ "/path"); - } -} - - -/** HTTP options request. - * - * Params: - * url = resource make a option call to - * conn = connection to use e.g. FTP or HTTP. The default AutoProtocol will - * guess connection type and create a new instance for this call only. - * - * The template parameter `T` specifies the type to return. Possible values - * are `char` and `ubyte` to return `char[]` or `ubyte[]`. - * - * Example: - * ---- - * import std.net.curl; - * auto http = HTTP(); - * options("https://httpbin.org/headers", http); - * writeln("Allow set to " ~ http.responseHeaders["Allow"]); - * ---- - * - * Returns: - * A T[] range containing the options of the resource pointed to by the URL. - * - * See_Also: $(LREF HTTP.Method) - */ -T[] options(T = char)(const(char)[] url, HTTP conn = HTTP()) -if (is(T == char) || is(T == ubyte)) -{ - conn.method = HTTP.Method.options; - return _basicHTTP!(T)(url, null, conn); -} - -@system unittest -{ - import std.algorithm.searching : canFind; - - testServer.handle((s) { - auto req = s.recvReq; - assert(req.hdrs.canFind("OPTIONS /path")); - s.send(httpOK("OPTIONSRESPONSE")); - }); - auto res = options(testServer.addr ~ "/path"); - assert(res == "OPTIONSRESPONSE"); -} - - -/** HTTP trace request. - * - * Params: - * url = resource make a trace call to - * conn = connection to use e.g. FTP or HTTP. The default AutoProtocol will - * guess connection type and create a new instance for this call only. - * - * The template parameter `T` specifies the type to return. Possible values - * are `char` and `ubyte` to return `char[]` or `ubyte[]`. - * - * Example: - * ---- - * import std.net.curl; - * trace("https://httpbin.org/headers"); - * ---- - * - * Returns: - * A T[] range containing the trace info of the resource pointed to by the URL. - * - * See_Also: $(LREF HTTP.Method) - */ -T[] trace(T = char)(const(char)[] url, HTTP conn = HTTP()) -if (is(T == char) || is(T == ubyte)) -{ - conn.method = HTTP.Method.trace; - return _basicHTTP!(T)(url, cast(void[]) null, conn); -} - -@system unittest -{ - import std.algorithm.searching : canFind; - - testServer.handle((s) { - auto req = s.recvReq; - assert(req.hdrs.canFind("TRACE /path")); - s.send(httpOK("TRACERESPONSE")); - }); - auto res = trace(testServer.addr ~ "/path"); - assert(res == "TRACERESPONSE"); -} - - -/** HTTP connect request. - * - * Params: - * url = resource make a connect to - * conn = HTTP connection to use - * - * The template parameter `T` specifies the type to return. Possible values - * are `char` and `ubyte` to return `char[]` or `ubyte[]`. - * - * Example: - * ---- - * import std.net.curl; - * connect("https://httpbin.org/headers"); - * ---- - * - * Returns: - * A T[] range containing the connect info of the resource pointed to by the URL. - * - * See_Also: $(LREF HTTP.Method) - */ -T[] connect(T = char)(const(char)[] url, HTTP conn = HTTP()) -if (is(T == char) || is(T == ubyte)) -{ - conn.method = HTTP.Method.connect; - return _basicHTTP!(T)(url, cast(void[]) null, conn); -} - -@system unittest -{ - import std.algorithm.searching : canFind; - - testServer.handle((s) { - auto req = s.recvReq; - assert(req.hdrs.canFind("CONNECT /path")); - s.send(httpOK("CONNECTRESPONSE")); - }); - auto res = connect(testServer.addr ~ "/path"); - assert(res == "CONNECTRESPONSE"); -} - - -/** HTTP patch content. - * - * Params: - * url = resource to patch - * patchData = data to send as the body of the request. An array - * of an arbitrary type is accepted and will be cast to ubyte[] - * before sending it. - * conn = HTTP connection to use - * - * The template parameter `T` specifies the type to return. Possible values - * are `char` and `ubyte` to return `char[]` or `ubyte[]`. - * - * Example: - * ---- - * auto http = HTTP(); - * http.addRequestHeader("Content-Type", "application/json"); - * auto content = patch("https://httpbin.org/patch", `{"title": "Patched Title"}`, http); - * ---- - * - * Returns: - * A T[] range containing the content of the resource pointed to by the URL. - * - * See_Also: $(LREF HTTP.Method) - */ -T[] patch(T = char, PatchUnit)(const(char)[] url, const(PatchUnit)[] patchData, - HTTP conn = HTTP()) -if (is(T == char) || is(T == ubyte)) -{ - conn.method = HTTP.Method.patch; - return _basicHTTP!(T)(url, patchData, conn); -} - -@system unittest -{ - import std.algorithm.searching : canFind; - - testServer.handle((s) { - auto req = s.recvReq; - assert(req.hdrs.canFind("PATCH /path")); - assert(req.bdy.canFind("PATCHBODY")); - s.send(httpOK("PATCHRESPONSE")); - }); - auto res = patch(testServer.addr ~ "/path", "PATCHBODY"); - assert(res == "PATCHRESPONSE"); -} - - -/* - * Helper function for the high level interface. - * - * It performs an HTTP request using the client which must have - * been setup correctly before calling this function. - */ -private auto _basicHTTP(T)(const(char)[] url, const(void)[] sendData, HTTP client) -{ - import std.algorithm.comparison : min; - import std.format : format; - import std.exception : enforce; - import etc.c.curl : CurlSeek, CurlSeekPos; - - immutable doSend = sendData !is null && - (client.method == HTTP.Method.post || - client.method == HTTP.Method.put || - client.method == HTTP.Method.patch); - - scope (exit) - { - client.onReceiveHeader = null; - client.onReceiveStatusLine = null; - client.onReceive = null; - - if (doSend) - { - client.onSend = null; - client.handle.onSeek = null; - client.contentLength = 0; - } - } - client.url = url; - HTTP.StatusLine statusLine; - import std.array : appender; - auto content = appender!(ubyte[])(); - client.onReceive = (ubyte[] data) - { - content ~= data; - return data.length; - }; - - if (doSend) - { - client.contentLength = sendData.length; - auto remainingData = sendData; - client.onSend = delegate size_t(void[] buf) - { - size_t minLen = min(buf.length, remainingData.length); - if (minLen == 0) return 0; - buf[0 .. minLen] = remainingData[0 .. minLen]; - remainingData = remainingData[minLen..$]; - return minLen; - }; - client.handle.onSeek = delegate(long offset, CurlSeekPos mode) - { - switch (mode) - { - case CurlSeekPos.set: - remainingData = sendData[cast(size_t) offset..$]; - return CurlSeek.ok; - default: - // As of curl 7.18.0, libcurl will not pass - // anything other than CurlSeekPos.set. - return CurlSeek.cantseek; - } - }; - } - - client.onReceiveHeader = (in char[] key, - in char[] value) - { - if (key == "content-length") - { - import std.conv : to; - content.reserve(value.to!size_t); - } - }; - client.onReceiveStatusLine = (HTTP.StatusLine l) { statusLine = l; }; - client.perform(); - enforce(statusLine.code / 100 == 2, new HTTPStatusException(statusLine.code, - format("HTTP request returned status code %d (%s)", statusLine.code, statusLine.reason))); - - return _decodeContent!T(content.data, client.p.charset); -} - -@system unittest -{ - import std.algorithm.searching : canFind; - import std.exception : collectException; - - testServer.handle((s) { - auto req = s.recvReq; - assert(req.hdrs.canFind("GET /path")); - s.send(httpNotFound()); - }); - auto e = collectException!HTTPStatusException(get(testServer.addr ~ "/path")); - assert(e.msg == "HTTP request returned status code 404 (Not Found)"); - assert(e.status == 404); -} - -// Content length must be reset after post -// https://issues.dlang.org/show_bug.cgi?id=14760 -@system unittest -{ - import std.algorithm.searching : canFind; - - testServer.handle((s) { - auto req = s.recvReq; - assert(req.hdrs.canFind("POST /")); - assert(req.bdy.canFind("POSTBODY")); - s.send(httpOK("POSTRESPONSE")); - - req = s.recvReq; - assert(req.hdrs.canFind("TRACE /")); - assert(req.bdy.empty); - s.blocking = false; - ubyte[6] buf = void; - assert(s.receive(buf[]) < 0); - s.send(httpOK("TRACERESPONSE")); - }); - auto http = HTTP(); - auto res = post(testServer.addr, "POSTBODY", http); - assert(res == "POSTRESPONSE"); - res = trace(testServer.addr, http); - assert(res == "TRACERESPONSE"); -} - -@system unittest // charset detection and transcoding to T -{ - testServer.handle((s) { - s.send("HTTP/1.1 200 OK\r\n"~ - "Content-Length: 4\r\n"~ - "Content-Type: text/plain; charset=utf-8\r\n" ~ - "\r\n" ~ - "äbc"); - }); - auto client = HTTP(); - auto result = _basicHTTP!char(testServer.addr, "", client); - assert(result == "äbc"); - - testServer.handle((s) { - s.send("HTTP/1.1 200 OK\r\n"~ - "Content-Length: 3\r\n"~ - "Content-Type: text/plain; charset=iso-8859-1\r\n" ~ - "\r\n" ~ - 0xE4 ~ "bc"); - }); - client = HTTP(); - result = _basicHTTP!char(testServer.addr, "", client); - assert(result == "äbc"); -} - -/* - * Helper function for the high level interface. - * - * It performs an FTP request using the client which must have - * been setup correctly before calling this function. - */ -private auto _basicFTP(T)(const(char)[] url, const(void)[] sendData, FTP client) -{ - import std.algorithm.comparison : min; - - scope (exit) - { - client.onReceive = null; - if (!sendData.empty) - client.onSend = null; - } - - ubyte[] content; - - if (client.encoding.empty) - client.encoding = "ISO-8859-1"; - - client.url = url; - client.onReceive = (ubyte[] data) - { - content ~= data; - return data.length; - }; - - if (!sendData.empty) - { - client.handle.set(CurlOption.upload, 1L); - client.onSend = delegate size_t(void[] buf) - { - size_t minLen = min(buf.length, sendData.length); - if (minLen == 0) return 0; - buf[0 .. minLen] = sendData[0 .. minLen]; - sendData = sendData[minLen..$]; - return minLen; - }; - } - - client.perform(); - - return _decodeContent!T(content, client.encoding); -} - -/* Used by _basicHTTP() and _basicFTP() to decode ubyte[] to - * correct string format - */ -private auto _decodeContent(T)(ubyte[] content, string encoding) -{ - static if (is(T == ubyte)) - { - return content; - } - else - { - import std.exception : enforce; - import std.format : format; - - // Optimally just return the utf8 encoded content - if (encoding == "UTF-8") - return cast(char[])(content); - - // The content has to be re-encoded to utf8 - auto scheme = EncodingScheme.create(encoding); - enforce!CurlException(scheme !is null, - format("Unknown encoding '%s'", encoding)); - - auto strInfo = decodeString(content, scheme); - enforce!CurlException(strInfo[0] != size_t.max, - format("Invalid encoding sequence for encoding '%s'", - encoding)); - - return strInfo[1]; - } -} - -alias KeepTerminator = Flag!"keepTerminator"; -/+ -struct ByLineBuffer(Char) -{ - bool linePresent; - bool EOF; - Char[] buffer; - ubyte[] decodeRemainder; - - bool append(const(ubyte)[] data) - { - byLineBuffer ~= data; - } - - @property bool linePresent() - { - return byLinePresent; - } - - Char[] get() - { - if (!linePresent) - { - // Decode ubyte[] into Char[] until a Terminator is found. - // If not Terminator is found and EOF is false then raise an - // exception. - } - return byLineBuffer; - } - -} -++/ -/** HTTP/FTP fetch content as a range of lines. - * - * A range of lines is returned when the request is complete. If the method or - * other request properties is to be customized then set the `conn` parameter - * with a HTTP/FTP instance that has these properties set. - * - * Example: - * ---- - * import std.net.curl, std.stdio; - * foreach (line; byLine("dlang.org")) - * writeln(line); - * ---- - * - * Params: - * url = The url to receive content from - * keepTerminator = `Yes.keepTerminator` signals that the line terminator should be - * returned as part of the lines in the range. - * terminator = The character that terminates a line - * conn = The connection to use e.g. HTTP or FTP. - * - * Returns: - * A range of Char[] with the content of the resource pointer to by the URL - */ -auto byLine(Conn = AutoProtocol, Terminator = char, Char = char) - (const(char)[] url, KeepTerminator keepTerminator = No.keepTerminator, - Terminator terminator = '\n', Conn conn = Conn()) -if (isCurlConn!Conn && isSomeChar!Char && isSomeChar!Terminator) -{ - static struct SyncLineInputRange - { - - private Char[] lines; - private Char[] current; - private bool currentValid; - private bool keepTerminator; - private Terminator terminator; - - this(Char[] lines, bool kt, Terminator terminator) - { - this.lines = lines; - this.keepTerminator = kt; - this.terminator = terminator; - currentValid = true; - popFront(); - } - - @property @safe bool empty() - { - return !currentValid; - } - - @property @safe Char[] front() - { - import std.exception : enforce; - enforce!CurlException(currentValid, "Cannot call front() on empty range"); - return current; - } - - void popFront() - { - import std.algorithm.searching : findSplitAfter, findSplit; - import std.exception : enforce; - - enforce!CurlException(currentValid, "Cannot call popFront() on empty range"); - if (lines.empty) - { - currentValid = false; - return; - } - - if (keepTerminator) - { - auto r = findSplitAfter(lines, [ terminator ]); - if (r[0].empty) - { - current = r[1]; - lines = r[0]; - } - else - { - current = r[0]; - lines = r[1]; - } - } - else - { - auto r = findSplit(lines, [ terminator ]); - current = r[0]; - lines = r[2]; - } - } - } - - auto result = _getForRange!Char(url, conn); - return SyncLineInputRange(result, keepTerminator == Yes.keepTerminator, terminator); -} - -@system unittest -{ - import std.algorithm.comparison : equal; - - foreach (host; [testServer.addr, "http://"~testServer.addr]) - { - testServer.handle((s) { - auto req = s.recvReq; - s.send(httpOK("Line1\nLine2\nLine3")); - }); - assert(byLine(host).equal(["Line1", "Line2", "Line3"])); - } -} - -/** HTTP/FTP fetch content as a range of chunks. - * - * A range of chunks is returned when the request is complete. If the method or - * other request properties is to be customized then set the `conn` parameter - * with a HTTP/FTP instance that has these properties set. - * - * Example: - * ---- - * import std.net.curl, std.stdio; - * foreach (chunk; byChunk("dlang.org", 100)) - * writeln(chunk); // chunk is ubyte[100] - * ---- - * - * Params: - * url = The url to receive content from - * chunkSize = The size of each chunk - * conn = The connection to use e.g. HTTP or FTP. - * - * Returns: - * A range of ubyte[chunkSize] with the content of the resource pointer to by the URL - */ -auto byChunk(Conn = AutoProtocol) - (const(char)[] url, size_t chunkSize = 1024, Conn conn = Conn()) -if (isCurlConn!(Conn)) -{ - static struct SyncChunkInputRange - { - private size_t chunkSize; - private ubyte[] _bytes; - private size_t offset; - - this(ubyte[] bytes, size_t chunkSize) - { - this._bytes = bytes; - this.chunkSize = chunkSize; - } - - @property @safe auto empty() - { - return offset == _bytes.length; - } - - @property ubyte[] front() - { - size_t nextOffset = offset + chunkSize; - if (nextOffset > _bytes.length) nextOffset = _bytes.length; - return _bytes[offset .. nextOffset]; - } - - @safe void popFront() - { - offset += chunkSize; - if (offset > _bytes.length) offset = _bytes.length; - } - } - - auto result = _getForRange!ubyte(url, conn); - return SyncChunkInputRange(result, chunkSize); -} - -@system unittest -{ - import std.algorithm.comparison : equal; - - foreach (host; [testServer.addr, "http://"~testServer.addr]) - { - testServer.handle((s) { - auto req = s.recvReq; - s.send(httpOK(cast(ubyte[])[0, 1, 2, 3, 4, 5])); - }); - assert(byChunk(host, 2).equal([[0, 1], [2, 3], [4, 5]])); - } -} - -private T[] _getForRange(T,Conn)(const(char)[] url, Conn conn) -{ - static if (is(Conn : HTTP)) - { - conn.method = conn.method == HTTP.Method.undefined ? HTTP.Method.get : conn.method; - return _basicHTTP!(T)(url, null, conn); - } - else static if (is(Conn : FTP)) - { - return _basicFTP!(T)(url, null, conn); - } - else - { - if (isFTPUrl(url)) - return get!(FTP,T)(url, FTP()); - else - return get!(HTTP,T)(url, HTTP()); - } -} - -/* - Main thread part of the message passing protocol used for all async - curl protocols. - */ -private mixin template WorkerThreadProtocol(Unit, alias units) -{ - import core.time : Duration; - - @property bool empty() - { - tryEnsureUnits(); - return state == State.done; - } - - @property Unit[] front() - { - import std.format : format; - tryEnsureUnits(); - assert(state == State.gotUnits, - format("Expected %s but got $s", - State.gotUnits, state)); - return units; - } - - void popFront() - { - import std.concurrency : send; - import std.format : format; - - tryEnsureUnits(); - assert(state == State.gotUnits, - format("Expected %s but got $s", - State.gotUnits, state)); - state = State.needUnits; - // Send to worker thread for buffer reuse - workerTid.send(cast(immutable(Unit)[]) units); - units = null; - } - - /** Wait for duration or until data is available and return true if data is - available - */ - bool wait(Duration d) - { - import core.time : dur; - import std.datetime.stopwatch : StopWatch; - import std.concurrency : receiveTimeout; - - if (state == State.gotUnits) - return true; - - enum noDur = dur!"hnsecs"(0); - StopWatch sw; - sw.start(); - while (state != State.gotUnits && d > noDur) - { - final switch (state) - { - case State.needUnits: - receiveTimeout(d, - (Tid origin, CurlMessage!(immutable(Unit)[]) _data) - { - if (origin != workerTid) - return false; - units = cast(Unit[]) _data.data; - state = State.gotUnits; - return true; - }, - (Tid origin, CurlMessage!bool f) - { - if (origin != workerTid) - return false; - state = state.done; - return true; - } - ); - break; - case State.gotUnits: return true; - case State.done: - return false; - } - d -= sw.peek(); - sw.reset(); - } - return state == State.gotUnits; - } - - enum State - { - needUnits, - gotUnits, - done - } - State state; - - void tryEnsureUnits() - { - import std.concurrency : receive; - while (true) - { - final switch (state) - { - case State.needUnits: - receive( - (Tid origin, CurlMessage!(immutable(Unit)[]) _data) - { - if (origin != workerTid) - return false; - units = cast(Unit[]) _data.data; - state = State.gotUnits; - return true; - }, - (Tid origin, CurlMessage!bool f) - { - if (origin != workerTid) - return false; - state = state.done; - return true; - } - ); - break; - case State.gotUnits: return; - case State.done: - return; - } - } - } -} - -/** HTTP/FTP fetch content as a range of lines asynchronously. - * - * A range of lines is returned immediately and the request that fetches the - * lines is performed in another thread. If the method or other request - * properties is to be customized then set the `conn` parameter with a - * HTTP/FTP instance that has these properties set. - * - * If `postData` is non-_null the method will be set to `post` for HTTP - * requests. - * - * The background thread will buffer up to transmitBuffers number of lines - * before it stops receiving data from network. When the main thread reads the - * lines from the range it frees up buffers and allows for the background thread - * to receive more data from the network. - * - * If no data is available and the main thread accesses the range it will block - * until data becomes available. An exception to this is the `wait(Duration)` method on - * the $(LREF LineInputRange). This method will wait at maximum for the - * specified duration and return true if data is available. - * - * Example: - * ---- - * import std.net.curl, std.stdio; - * // Get some pages in the background - * auto range1 = byLineAsync("www.google.com"); - * auto range2 = byLineAsync("www.wikipedia.org"); - * foreach (line; byLineAsync("dlang.org")) - * writeln(line); - * - * // Lines already fetched in the background and ready - * foreach (line; range1) writeln(line); - * foreach (line; range2) writeln(line); - * ---- - * - * ---- - * import std.net.curl, std.stdio; - * // Get a line in a background thread and wait in - * // main thread for 2 seconds for it to arrive. - * auto range3 = byLineAsync("dlang.com"); - * if (range3.wait(dur!"seconds"(2))) - * writeln(range3.front); - * else - * writeln("No line received after 2 seconds!"); - * ---- - * - * Params: - * url = The url to receive content from - * postData = Data to HTTP Post - * keepTerminator = `Yes.keepTerminator` signals that the line terminator should be - * returned as part of the lines in the range. - * terminator = The character that terminates a line - * transmitBuffers = The number of lines buffered asynchronously - * conn = The connection to use e.g. HTTP or FTP. - * - * Returns: - * A range of Char[] with the content of the resource pointer to by the - * URL. - */ -auto byLineAsync(Conn = AutoProtocol, Terminator = char, Char = char, PostUnit) - (const(char)[] url, const(PostUnit)[] postData, - KeepTerminator keepTerminator = No.keepTerminator, - Terminator terminator = '\n', - size_t transmitBuffers = 10, Conn conn = Conn()) -if (isCurlConn!Conn && isSomeChar!Char && isSomeChar!Terminator) -{ - static if (is(Conn : AutoProtocol)) - { - if (isFTPUrl(url)) - return byLineAsync(url, postData, keepTerminator, - terminator, transmitBuffers, FTP()); - else - return byLineAsync(url, postData, keepTerminator, - terminator, transmitBuffers, HTTP()); - } - else - { - import std.concurrency : OnCrowding, send, setMaxMailboxSize, spawn, thisTid, Tid; - // 50 is just an arbitrary number for now - setMaxMailboxSize(thisTid, 50, OnCrowding.block); - auto tid = spawn(&_async!().spawn!(Conn, Char, Terminator)); - tid.send(thisTid); - tid.send(terminator); - tid.send(keepTerminator == Yes.keepTerminator); - - _async!().duplicateConnection(url, conn, postData, tid); - - return _async!().LineInputRange!Char(tid, transmitBuffers, - Conn.defaultAsyncStringBufferSize); - } -} - -/// ditto -auto byLineAsync(Conn = AutoProtocol, Terminator = char, Char = char) - (const(char)[] url, KeepTerminator keepTerminator = No.keepTerminator, - Terminator terminator = '\n', - size_t transmitBuffers = 10, Conn conn = Conn()) -{ - static if (is(Conn : AutoProtocol)) - { - if (isFTPUrl(url)) - return byLineAsync(url, cast(void[]) null, keepTerminator, - terminator, transmitBuffers, FTP()); - else - return byLineAsync(url, cast(void[]) null, keepTerminator, - terminator, transmitBuffers, HTTP()); - } - else - { - return byLineAsync(url, cast(void[]) null, keepTerminator, - terminator, transmitBuffers, conn); - } -} - -@system unittest -{ - import std.algorithm.comparison : equal; - - foreach (host; [testServer.addr, "http://"~testServer.addr]) - { - testServer.handle((s) { - auto req = s.recvReq; - s.send(httpOK("Line1\nLine2\nLine3")); - }); - assert(byLineAsync(host).equal(["Line1", "Line2", "Line3"])); - } -} - -/** HTTP/FTP fetch content as a range of chunks asynchronously. - * - * A range of chunks is returned immediately and the request that fetches the - * chunks is performed in another thread. If the method or other request - * properties is to be customized then set the `conn` parameter with a - * HTTP/FTP instance that has these properties set. - * - * If `postData` is non-_null the method will be set to `post` for HTTP - * requests. - * - * The background thread will buffer up to transmitBuffers number of chunks - * before is stops receiving data from network. When the main thread reads the - * chunks from the range it frees up buffers and allows for the background - * thread to receive more data from the network. - * - * If no data is available and the main thread access the range it will block - * until data becomes available. An exception to this is the `wait(Duration)` - * method on the $(LREF ChunkInputRange). This method will wait at maximum for the specified - * duration and return true if data is available. - * - * Example: - * ---- - * import std.net.curl, std.stdio; - * // Get some pages in the background - * auto range1 = byChunkAsync("www.google.com", 100); - * auto range2 = byChunkAsync("www.wikipedia.org"); - * foreach (chunk; byChunkAsync("dlang.org")) - * writeln(chunk); // chunk is ubyte[100] - * - * // Chunks already fetched in the background and ready - * foreach (chunk; range1) writeln(chunk); - * foreach (chunk; range2) writeln(chunk); - * ---- - * - * ---- - * import std.net.curl, std.stdio; - * // Get a line in a background thread and wait in - * // main thread for 2 seconds for it to arrive. - * auto range3 = byChunkAsync("dlang.com", 10); - * if (range3.wait(dur!"seconds"(2))) - * writeln(range3.front); - * else - * writeln("No chunk received after 2 seconds!"); - * ---- - * - * Params: - * url = The url to receive content from - * postData = Data to HTTP Post - * chunkSize = The size of the chunks - * transmitBuffers = The number of chunks buffered asynchronously - * conn = The connection to use e.g. HTTP or FTP. - * - * Returns: - * A range of ubyte[chunkSize] with the content of the resource pointer to by - * the URL. - */ -auto byChunkAsync(Conn = AutoProtocol, PostUnit) - (const(char)[] url, const(PostUnit)[] postData, - size_t chunkSize = 1024, size_t transmitBuffers = 10, - Conn conn = Conn()) -if (isCurlConn!(Conn)) -{ - static if (is(Conn : AutoProtocol)) - { - if (isFTPUrl(url)) - return byChunkAsync(url, postData, chunkSize, - transmitBuffers, FTP()); - else - return byChunkAsync(url, postData, chunkSize, - transmitBuffers, HTTP()); - } - else - { - import std.concurrency : OnCrowding, send, setMaxMailboxSize, spawn, thisTid, Tid; - // 50 is just an arbitrary number for now - setMaxMailboxSize(thisTid, 50, OnCrowding.block); - auto tid = spawn(&_async!().spawn!(Conn, ubyte)); - tid.send(thisTid); - - _async!().duplicateConnection(url, conn, postData, tid); - - return _async!().ChunkInputRange(tid, transmitBuffers, chunkSize); - } -} - -/// ditto -auto byChunkAsync(Conn = AutoProtocol) - (const(char)[] url, - size_t chunkSize = 1024, size_t transmitBuffers = 10, - Conn conn = Conn()) -if (isCurlConn!(Conn)) -{ - static if (is(Conn : AutoProtocol)) - { - if (isFTPUrl(url)) - return byChunkAsync(url, cast(void[]) null, chunkSize, - transmitBuffers, FTP()); - else - return byChunkAsync(url, cast(void[]) null, chunkSize, - transmitBuffers, HTTP()); - } - else - { - return byChunkAsync(url, cast(void[]) null, chunkSize, - transmitBuffers, conn); - } -} - -@system unittest -{ - import std.algorithm.comparison : equal; - - foreach (host; [testServer.addr, "http://"~testServer.addr]) - { - testServer.handle((s) { - auto req = s.recvReq; - s.send(httpOK(cast(ubyte[])[0, 1, 2, 3, 4, 5])); - }); - assert(byChunkAsync(host, 2).equal([[0, 1], [2, 3], [4, 5]])); - } -} - - -/* - Mixin template for all supported curl protocols. This is the commom - functionallity such as timeouts and network interface settings. This should - really be in the HTTP/FTP/SMTP structs but the documentation tool does not - support a mixin to put its doc strings where a mixin is done. Therefore docs - in this template is copied into each of HTTP/FTP/SMTP below. -*/ -private mixin template Protocol() -{ - import etc.c.curl : CurlReadFunc, RawCurlProxy = CurlProxy; - import core.time : Duration; - import std.socket : InternetAddress; - - /// Value to return from `onSend`/`onReceive` delegates in order to - /// pause a request - alias requestPause = CurlReadFunc.pause; - - /// Value to return from onSend delegate in order to abort a request - alias requestAbort = CurlReadFunc.abort; - - static uint defaultAsyncStringBufferSize = 100; - - /** - The curl handle used by this connection. - */ - @property ref Curl handle() return - { - return p.curl; - } - - /** - True if the instance is stopped. A stopped instance is not usable. - */ - @property bool isStopped() - { - return p.curl.stopped; - } - - /// Stop and invalidate this instance. - void shutdown() - { - p.curl.shutdown(); - } - - /** Set verbose. - This will print request information to stderr. - */ - @property void verbose(bool on) - { - p.curl.set(CurlOption.verbose, on ? 1L : 0L); - } - - // Connection settings - - /// Set timeout for activity on connection. - @property void dataTimeout(Duration d) - { - p.curl.set(CurlOption.low_speed_limit, 1); - p.curl.set(CurlOption.low_speed_time, d.total!"seconds"); - } - - /** Set maximum time an operation is allowed to take. - This includes dns resolution, connecting, data transfer, etc. - */ - @property void operationTimeout(Duration d) - { - p.curl.set(CurlOption.timeout_ms, d.total!"msecs"); - } - - /// Set timeout for connecting. - @property void connectTimeout(Duration d) - { - p.curl.set(CurlOption.connecttimeout_ms, d.total!"msecs"); - } - - // Network settings - - /** Proxy - * See: $(HTTP curl.haxx.se/libcurl/c/curl_easy_setopt.html#CURLOPTPROXY, _proxy) - */ - @property void proxy(const(char)[] host) - { - p.curl.set(CurlOption.proxy, host); - } - - /** Proxy port - * See: $(HTTP curl.haxx.se/libcurl/c/curl_easy_setopt.html#CURLOPTPROXYPORT, _proxy_port) - */ - @property void proxyPort(ushort port) - { - p.curl.set(CurlOption.proxyport, cast(long) port); - } - - /// Type of proxy - alias CurlProxy = RawCurlProxy; - - /** Proxy type - * See: $(HTTP curl.haxx.se/libcurl/c/curl_easy_setopt.html#CURLOPTPROXY, _proxy_type) - */ - @property void proxyType(CurlProxy type) - { - p.curl.set(CurlOption.proxytype, cast(long) type); - } - - /// DNS lookup timeout. - @property void dnsTimeout(Duration d) - { - p.curl.set(CurlOption.dns_cache_timeout, d.total!"msecs"); - } - - /** - * The network interface to use in form of the IP of the interface. - * - * Example: - * ---- - * theprotocol.netInterface = "192.168.1.32"; - * theprotocol.netInterface = [ 192, 168, 1, 32 ]; - * ---- - * - * See: $(REF InternetAddress, std,socket) - */ - @property void netInterface(const(char)[] i) - { - p.curl.set(CurlOption.intrface, i); - } - - /// ditto - @property void netInterface(const(ubyte)[4] i) - { - import std.format : format; - const str = format("%d.%d.%d.%d", i[0], i[1], i[2], i[3]); - netInterface = str; - } - - /// ditto - @property void netInterface(InternetAddress i) - { - netInterface = i.toAddrString(); - } - - /** - Set the local outgoing port to use. - Params: - port = the first outgoing port number to try and use - */ - @property void localPort(ushort port) - { - p.curl.set(CurlOption.localport, cast(long) port); - } - - /** - Set the no proxy flag for the specified host names. - Params: - test = a list of comma host names that do not require - proxy to get reached - */ - void setNoProxy(string hosts) - { - p.curl.set(CurlOption.noproxy, hosts); - } - - /** - Set the local outgoing port range to use. - This can be used together with the localPort property. - Params: - range = if the first port is occupied then try this many - port number forwards - */ - @property void localPortRange(ushort range) - { - p.curl.set(CurlOption.localportrange, cast(long) range); - } - - /** Set the tcp no-delay socket option on or off. - See: $(HTTP curl.haxx.se/libcurl/c/curl_easy_setopt.html#CURLOPTTCPNODELAY, nodelay) - */ - @property void tcpNoDelay(bool on) - { - p.curl.set(CurlOption.tcp_nodelay, cast(long) (on ? 1 : 0) ); - } - - /** Sets whether SSL peer certificates should be verified. - See: $(HTTP curl.haxx.se/libcurl/c/curl_easy_setopt.html#CURLOPTSSLVERIFYPEER, verifypeer) - */ - @property void verifyPeer(bool on) - { - p.curl.set(CurlOption.ssl_verifypeer, on ? 1 : 0); - } - - /** Sets whether the host within an SSL certificate should be verified. - See: $(HTTP curl.haxx.se/libcurl/c/curl_easy_setopt.html#CURLOPTSSLVERIFYHOST, verifypeer) - */ - @property void verifyHost(bool on) - { - p.curl.set(CurlOption.ssl_verifyhost, on ? 2 : 0); - } - - // Authentication settings - - /** - Set the user name, password and optionally domain for authentication - purposes. - - Some protocols may need authentication in some cases. Use this - function to provide credentials. - - Params: - username = the username - password = the password - domain = used for NTLM authentication only and is set to the NTLM domain - name - */ - void setAuthentication(const(char)[] username, const(char)[] password, - const(char)[] domain = "") - { - import std.format : format; - if (!domain.empty) - username = format("%s/%s", domain, username); - p.curl.set(CurlOption.userpwd, format("%s:%s", username, password)); - } - - @system unittest - { - import std.algorithm.searching : canFind; - - testServer.handle((s) { - auto req = s.recvReq; - assert(req.hdrs.canFind("GET /")); - assert(req.hdrs.canFind("Basic dXNlcjpwYXNz")); - s.send(httpOK()); - }); - - auto http = HTTP(testServer.addr); - http.onReceive = (ubyte[] data) { return data.length; }; - http.setAuthentication("user", "pass"); - http.perform(); - - // https://issues.dlang.org/show_bug.cgi?id=17540 - http.setNoProxy("www.example.com"); - } - - /** - Set the user name and password for proxy authentication. - - Params: - username = the username - password = the password - */ - void setProxyAuthentication(const(char)[] username, const(char)[] password) - { - import std.array : replace; - import std.format : format; - - p.curl.set(CurlOption.proxyuserpwd, - format("%s:%s", - username.replace(":", "%3A"), - password.replace(":", "%3A")) - ); - } - - /** - * The event handler that gets called when data is needed for sending. The - * length of the `void[]` specifies the maximum number of bytes that can - * be sent. - * - * Returns: - * The callback returns the number of elements in the buffer that have been - * filled and are ready to send. - * The special value `.abortRequest` can be returned in order to abort the - * current request. - * The special value `.pauseRequest` can be returned in order to pause the - * current request. - * - * Example: - * ---- - * import std.net.curl; - * string msg = "Hello world"; - * auto client = HTTP("dlang.org"); - * client.onSend = delegate size_t(void[] data) - * { - * auto m = cast(void[]) msg; - * size_t length = m.length > data.length ? data.length : m.length; - * if (length == 0) return 0; - * data[0 .. length] = m[0 .. length]; - * msg = msg[length..$]; - * return length; - * }; - * client.perform(); - * ---- - */ - @property void onSend(size_t delegate(void[]) callback) - { - p.curl.clear(CurlOption.postfields); // cannot specify data when using callback - p.curl.onSend = callback; - } - - /** - * The event handler that receives incoming data. Be sure to copy the - * incoming ubyte[] since it is not guaranteed to be valid after the - * callback returns. - * - * Returns: - * The callback returns the number of incoming bytes read. If the entire array is - * not read the request will abort. - * The special value .pauseRequest can be returned in order to pause the - * current request. - * - * Example: - * ---- - * import std.net.curl, std.stdio, std.conv; - * auto client = HTTP("dlang.org"); - * client.onReceive = (ubyte[] data) - * { - * writeln("Got data", to!(const(char)[])(data)); - * return data.length; - * }; - * client.perform(); - * ---- - */ - @property void onReceive(size_t delegate(ubyte[]) callback) - { - p.curl.onReceive = callback; - } - - /** - * The event handler that gets called to inform of upload/download progress. - * - * Params: - * dlTotal = total bytes to download - * dlNow = currently downloaded bytes - * ulTotal = total bytes to upload - * ulNow = currently uploaded bytes - * - * Returns: - * Return 0 from the callback to signal success, return non-zero to abort - * transfer - * - * Example: - * ---- - * import std.net.curl, std.stdio; - * auto client = HTTP("dlang.org"); - * client.onProgress = delegate int(size_t dl, size_t dln, size_t ul, size_t uln) - * { - * writeln("Progress: downloaded ", dln, " of ", dl); - * writeln("Progress: uploaded ", uln, " of ", ul); - * return 0; - * }; - * client.perform(); - * ---- - */ - @property void onProgress(int delegate(size_t dlTotal, size_t dlNow, - size_t ulTotal, size_t ulNow) callback) - { - p.curl.onProgress = callback; - } -} - -/* - Decode `ubyte[]` array using the provided EncodingScheme up to maxChars - Returns: Tuple of ubytes read and the `Char[]` characters decoded. - Not all ubytes are guaranteed to be read in case of decoding error. -*/ -private Tuple!(size_t,Char[]) -decodeString(Char = char)(const(ubyte)[] data, - EncodingScheme scheme, - size_t maxChars = size_t.max) -{ - import std.encoding : INVALID_SEQUENCE; - Char[] res; - immutable startLen = data.length; - size_t charsDecoded = 0; - while (data.length && charsDecoded < maxChars) - { - immutable dchar dc = scheme.safeDecode(data); - if (dc == INVALID_SEQUENCE) - { - return typeof(return)(size_t.max, cast(Char[]) null); - } - charsDecoded++; - res ~= dc; - } - return typeof(return)(startLen-data.length, res); -} - -/* - Decode `ubyte[]` array using the provided `EncodingScheme` until a the - line terminator specified is found. The basesrc parameter is effectively - prepended to src as the first thing. - - This function is used for decoding as much of the src buffer as - possible until either the terminator is found or decoding fails. If - it fails as the last data in the src it may mean that the src buffer - were missing some bytes in order to represent a correct code - point. Upon the next call to this function more bytes have been - received from net and the failing bytes should be given as the - basesrc parameter. It is done this way to minimize data copying. - - Returns: true if a terminator was found - Not all ubytes are guaranteed to be read in case of decoding error. - any decoded chars will be inserted into dst. -*/ -private bool decodeLineInto(Terminator, Char = char)(ref const(ubyte)[] basesrc, - ref const(ubyte)[] src, - ref Char[] dst, - EncodingScheme scheme, - Terminator terminator) -{ - import std.algorithm.searching : endsWith; - import std.encoding : INVALID_SEQUENCE; - import std.exception : enforce; - - // if there is anything in the basesrc then try to decode that - // first. - if (basesrc.length != 0) - { - // Try to ensure 4 entries in the basesrc by copying from src. - immutable blen = basesrc.length; - immutable len = (basesrc.length + src.length) >= 4 ? - 4 : basesrc.length + src.length; - basesrc.length = len; - - immutable dchar dc = scheme.safeDecode(basesrc); - if (dc == INVALID_SEQUENCE) - { - enforce!CurlException(len != 4, "Invalid code sequence"); - return false; - } - dst ~= dc; - src = src[len-basesrc.length-blen .. $]; // remove used ubytes from src - basesrc.length = 0; - } - - while (src.length) - { - const lsrc = src; - dchar dc = scheme.safeDecode(src); - if (dc == INVALID_SEQUENCE) - { - if (src.empty) - { - // The invalid sequence was in the end of the src. Maybe there - // just need to be more bytes available so these last bytes are - // put back to src for later use. - src = lsrc; - return false; - } - dc = '?'; - } - dst ~= dc; - - if (dst.endsWith(terminator)) - return true; - } - return false; // no terminator found -} - -/** - * HTTP client functionality. - * - * Example: - * - * Get with custom data receivers: - * - * --- - * import std.net.curl, std.stdio; - * - * auto http = HTTP("https://dlang.org"); - * http.onReceiveHeader = - * (in char[] key, in char[] value) { writeln(key ~ ": " ~ value); }; - * http.onReceive = (ubyte[] data) { /+ drop +/ return data.length; }; - * http.perform(); - * --- - * - */ - -/** - * Put with data senders: - * - * --- - * import std.net.curl, std.stdio; - * - * auto http = HTTP("https://dlang.org"); - * auto msg = "Hello world"; - * http.contentLength = msg.length; - * http.onSend = (void[] data) - * { - * auto m = cast(void[]) msg; - * size_t len = m.length > data.length ? data.length : m.length; - * if (len == 0) return len; - * data[0 .. len] = m[0 .. len]; - * msg = msg[len..$]; - * return len; - * }; - * http.perform(); - * --- - * - */ - -/** - * Tracking progress: - * - * --- - * import std.net.curl, std.stdio; - * - * auto http = HTTP(); - * http.method = HTTP.Method.get; - * http.url = "http://upload.wikimedia.org/wikipedia/commons/" ~ - * "5/53/Wikipedia-logo-en-big.png"; - * http.onReceive = (ubyte[] data) { return data.length; }; - * http.onProgress = (size_t dltotal, size_t dlnow, - * size_t ultotal, size_t ulnow) - * { - * writeln("Progress ", dltotal, ", ", dlnow, ", ", ultotal, ", ", ulnow); - * return 0; - * }; - * http.perform(); - * --- - * - * See_Also: $(LINK2 http://www.ietf.org/rfc/rfc2616.txt, RFC2616) - * - */ -struct HTTP -{ - mixin Protocol; - - import std.datetime.systime : SysTime; - import std.typecons : RefCounted; - import etc.c.curl : CurlAuth, CurlInfo, curl_slist, CURLVERSION_NOW, curl_off_t; - - /// Authentication method equal to $(REF CurlAuth, etc,c,curl) - alias AuthMethod = CurlAuth; - - static private uint defaultMaxRedirects = 10; - - private struct Impl - { - ~this() - { - if (headersOut !is null) - Curl.curl.slist_free_all(headersOut); - if (curl.handle !is null) // work around RefCounted/emplace bug - curl.shutdown(); - } - Curl curl; - curl_slist* headersOut; - string[string] headersIn; - string charset; - - /// The status line of the final sub-request in a request. - StatusLine status; - private void delegate(StatusLine) onReceiveStatusLine; - - /// The HTTP method to use. - Method method = Method.undefined; - - @system @property void onReceiveHeader(void delegate(in char[] key, - in char[] value) callback) - { - import std.algorithm.searching : findSplit, startsWith; - import std.string : indexOf, chomp; - import std.uni : toLower; - import std.exception : assumeUnique; - - // Wrap incoming callback in order to separate http status line from - // http headers. On redirected requests there may be several such - // status lines. The last one is the one recorded. - auto dg = (in char[] header) - { - import std.utf : UTFException; - try - { - if (header.empty) - { - // header delimiter - return; - } - if (header.startsWith("HTTP/")) - { - headersIn.clear(); - if (parseStatusLine(header, status)) - { - if (onReceiveStatusLine != null) - onReceiveStatusLine(status); - } - return; - } - - auto m = header.findSplit(": "); - const(char)[] lowerFieldName = m[0].toLower(); - ///Fixes https://issues.dlang.org/show_bug.cgi?id=24458 - string fieldName = lowerFieldName is m[0] ? lowerFieldName.idup : assumeUnique(lowerFieldName); - auto fieldContent = m[2].chomp; - if (fieldName == "content-type") - { - auto io = indexOf(fieldContent, "charset=", No.caseSensitive); - if (io != -1) - charset = fieldContent[io + "charset=".length .. $].findSplit(";")[0].idup; - } - if (!m[1].empty && callback !is null) - callback(fieldName, fieldContent); - headersIn[fieldName] = fieldContent.idup; - } - catch (UTFException e) - { - //munch it - a header should be all ASCII, any "wrong UTF" is broken header - } - }; - - curl.onReceiveHeader = dg; - } - } - - private RefCounted!Impl p; - import etc.c.curl : CurlTimeCond; - - /// Parse status line, as received from / generated by cURL. - private static bool parseStatusLine(const char[] header, out StatusLine status) @safe - { - import std.algorithm.searching : findSplit, startsWith; - import std.conv : to, ConvException; - - if (!header.startsWith("HTTP/")) - return false; - - try - { - const m = header["HTTP/".length .. $].findSplit(" "); - const v = m[0].findSplit("."); - status.majorVersion = to!ushort(v[0]); - status.minorVersion = v[1].length ? to!ushort(v[2]) : 0; - const s2 = m[2].findSplit(" "); - status.code = to!ushort(s2[0]); - status.reason = s2[2].idup; - return true; - } - catch (ConvException e) - { - return false; - } - } - - @safe unittest - { - StatusLine status; - assert(parseStatusLine("HTTP/1.1 200 OK", status) - && status == StatusLine(1, 1, 200, "OK")); - assert(parseStatusLine("HTTP/1.0 304 Not Modified", status) - && status == StatusLine(1, 0, 304, "Not Modified")); - // The HTTP2 protocol is binary; cURL generates this fake text header. - assert(parseStatusLine("HTTP/2 200", status) - && status == StatusLine(2, 0, 200, null)); - - assert(!parseStatusLine("HTTP/2", status)); - assert(!parseStatusLine("HTTP/2 -1", status)); - assert(!parseStatusLine("HTTP/2 200", status)); - assert(!parseStatusLine("HTTP/2.X 200", status)); - assert(!parseStatusLine("HTTP|2 200", status)); - } - - /** Time condition enumeration as an alias of $(REF CurlTimeCond, etc,c,curl) - - $(HTTP www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.25, _RFC2616 Section 14.25) - */ - alias TimeCond = CurlTimeCond; - - /** - Constructor taking the url as parameter. - */ - static HTTP opCall(const(char)[] url) - { - HTTP http; - http.initialize(); - http.url = url; - return http; - } - - /// - static HTTP opCall() - { - HTTP http; - http.initialize(); - return http; - } - - /// - HTTP dup() - { - HTTP copy; - copy.initialize(); - copy.p.method = p.method; - curl_slist* cur = p.headersOut; - curl_slist* newlist = null; - while (cur) - { - newlist = Curl.curl.slist_append(newlist, cur.data); - cur = cur.next; - } - copy.p.headersOut = newlist; - copy.p.curl.set(CurlOption.httpheader, copy.p.headersOut); - copy.p.curl = p.curl.dup(); - copy.dataTimeout = _defaultDataTimeout; - copy.onReceiveHeader = null; - return copy; - } - - private void initialize() - { - p.curl.initialize(); - maxRedirects = HTTP.defaultMaxRedirects; - p.charset = "ISO-8859-1"; // Default charset defined in HTTP RFC - p.method = Method.undefined; - setUserAgent(HTTP.defaultUserAgent); - dataTimeout = _defaultDataTimeout; - onReceiveHeader = null; - verifyPeer = true; - verifyHost = true; - } - - /** - Perform a http request. - - After the HTTP client has been setup and possibly assigned callbacks the - `perform()` method will start performing the request towards the - specified server. - - Params: - throwOnError = whether to throw an exception or return a CurlCode on error - */ - CurlCode perform(ThrowOnError throwOnError = Yes.throwOnError) - { - p.status.reset(); - - CurlOption opt; - final switch (p.method) - { - case Method.head: - p.curl.set(CurlOption.nobody, 1L); - opt = CurlOption.nobody; - break; - case Method.undefined: - case Method.get: - p.curl.set(CurlOption.httpget, 1L); - opt = CurlOption.httpget; - break; - case Method.post: - p.curl.set(CurlOption.post, 1L); - opt = CurlOption.post; - break; - case Method.put: - p.curl.set(CurlOption.upload, 1L); - opt = CurlOption.upload; - break; - case Method.del: - p.curl.set(CurlOption.customrequest, "DELETE"); - opt = CurlOption.customrequest; - break; - case Method.options: - p.curl.set(CurlOption.customrequest, "OPTIONS"); - opt = CurlOption.customrequest; - break; - case Method.trace: - p.curl.set(CurlOption.customrequest, "TRACE"); - opt = CurlOption.customrequest; - break; - case Method.connect: - p.curl.set(CurlOption.customrequest, "CONNECT"); - opt = CurlOption.customrequest; - break; - case Method.patch: - p.curl.set(CurlOption.customrequest, "PATCH"); - opt = CurlOption.customrequest; - break; - } - - scope (exit) p.curl.clear(opt); - return p.curl.perform(throwOnError); - } - - /// The URL to specify the location of the resource. - @property void url(const(char)[] url) - { - import std.algorithm.searching : startsWith; - import std.uni : toLower; - if (!startsWith(url.toLower(), "http://", "https://")) - url = "http://" ~ url; - p.curl.set(CurlOption.url, url); - } - - /// Set the CA certificate bundle file to use for SSL peer verification - @property void caInfo(const(char)[] caFile) - { - p.curl.set(CurlOption.cainfo, caFile); - } - - // This is a workaround for mixed in content not having its - // docs mixed in. - version (StdDdoc) - { - static import etc.c.curl; - - /// Value to return from `onSend`/`onReceive` delegates in order to - /// pause a request - alias requestPause = CurlReadFunc.pause; - - /// Value to return from onSend delegate in order to abort a request - alias requestAbort = CurlReadFunc.abort; - - /** - True if the instance is stopped. A stopped instance is not usable. - */ - @property bool isStopped(); - - /// Stop and invalidate this instance. - void shutdown(); - - /** Set verbose. - This will print request information to stderr. - */ - @property void verbose(bool on); - - // Connection settings - - /// Set timeout for activity on connection. - @property void dataTimeout(Duration d); - - /** Set maximum time an operation is allowed to take. - This includes dns resolution, connecting, data transfer, etc. - */ - @property void operationTimeout(Duration d); - - /// Set timeout for connecting. - @property void connectTimeout(Duration d); - - // Network settings - - /** Proxy - * See: $(HTTP curl.haxx.se/libcurl/c/curl_easy_setopt.html#CURLOPTPROXY, _proxy) - */ - @property void proxy(const(char)[] host); - - /** Proxy port - * See: $(HTTP curl.haxx.se/libcurl/c/curl_easy_setopt.html#CURLOPTPROXYPORT, _proxy_port) - */ - @property void proxyPort(ushort port); - - /// Type of proxy - alias CurlProxy = etc.c.curl.CurlProxy; - - /** Proxy type - * See: $(HTTP curl.haxx.se/libcurl/c/curl_easy_setopt.html#CURLOPTPROXY, _proxy_type) - */ - @property void proxyType(CurlProxy type); - - /// DNS lookup timeout. - @property void dnsTimeout(Duration d); - - /** - * The network interface to use in form of the IP of the interface. - * - * Example: - * ---- - * theprotocol.netInterface = "192.168.1.32"; - * theprotocol.netInterface = [ 192, 168, 1, 32 ]; - * ---- - * - * See: $(REF InternetAddress, std,socket) - */ - @property void netInterface(const(char)[] i); - - /// ditto - @property void netInterface(const(ubyte)[4] i); - - /// ditto - @property void netInterface(InternetAddress i); - - /** - Set the local outgoing port to use. - Params: - port = the first outgoing port number to try and use - */ - @property void localPort(ushort port); - - /** - Set the local outgoing port range to use. - This can be used together with the localPort property. - Params: - range = if the first port is occupied then try this many - port number forwards - */ - @property void localPortRange(ushort range); - - /** Set the tcp no-delay socket option on or off. - See: $(HTTP curl.haxx.se/libcurl/c/curl_easy_setopt.html#CURLOPTTCPNODELAY, nodelay) - */ - @property void tcpNoDelay(bool on); - - // Authentication settings - - /** - Set the user name, password and optionally domain for authentication - purposes. - - Some protocols may need authentication in some cases. Use this - function to provide credentials. - - Params: - username = the username - password = the password - domain = used for NTLM authentication only and is set to the NTLM domain - name - */ - void setAuthentication(const(char)[] username, const(char)[] password, - const(char)[] domain = ""); - - /** - Set the user name and password for proxy authentication. - - Params: - username = the username - password = the password - */ - void setProxyAuthentication(const(char)[] username, const(char)[] password); - - /** - * The event handler that gets called when data is needed for sending. The - * length of the `void[]` specifies the maximum number of bytes that can - * be sent. - * - * Returns: - * The callback returns the number of elements in the buffer that have been - * filled and are ready to send. - * The special value `.abortRequest` can be returned in order to abort the - * current request. - * The special value `.pauseRequest` can be returned in order to pause the - * current request. - * - * Example: - * ---- - * import std.net.curl; - * string msg = "Hello world"; - * auto client = HTTP("dlang.org"); - * client.onSend = delegate size_t(void[] data) - * { - * auto m = cast(void[]) msg; - * size_t length = m.length > data.length ? data.length : m.length; - * if (length == 0) return 0; - * data[0 .. length] = m[0 .. length]; - * msg = msg[length..$]; - * return length; - * }; - * client.perform(); - * ---- - */ - @property void onSend(size_t delegate(void[]) callback); - - /** - * The event handler that receives incoming data. Be sure to copy the - * incoming ubyte[] since it is not guaranteed to be valid after the - * callback returns. - * - * Returns: - * The callback returns the incoming bytes read. If not the entire array is - * the request will abort. - * The special value .pauseRequest can be returned in order to pause the - * current request. - * - * Example: - * ---- - * import std.net.curl, std.stdio, std.conv; - * auto client = HTTP("dlang.org"); - * client.onReceive = (ubyte[] data) - * { - * writeln("Got data", to!(const(char)[])(data)); - * return data.length; - * }; - * client.perform(); - * ---- - */ - @property void onReceive(size_t delegate(ubyte[]) callback); - - /** - * Register an event handler that gets called to inform of - * upload/download progress. - * - * Callback_parameters: - * $(CALLBACK_PARAMS) - * - * Callback_returns: Return 0 to signal success, return non-zero to - * abort transfer. - * - * Example: - * ---- - * import std.net.curl, std.stdio; - * auto client = HTTP("dlang.org"); - * client.onProgress = delegate int(size_t dl, size_t dln, size_t ul, size_t uln) - * { - * writeln("Progress: downloaded ", dln, " of ", dl); - * writeln("Progress: uploaded ", uln, " of ", ul); - * return 0; - * }; - * client.perform(); - * ---- - */ - @property void onProgress(int delegate(size_t dlTotal, size_t dlNow, - size_t ulTotal, size_t ulNow) callback); - } - - /** Clear all outgoing headers. - */ - void clearRequestHeaders() - { - if (p.headersOut !is null) - Curl.curl.slist_free_all(p.headersOut); - p.headersOut = null; - p.curl.clear(CurlOption.httpheader); - } - - /** Add a header e.g. "X-CustomField: Something is fishy". - * - * There is no remove header functionality. Do a $(LREF clearRequestHeaders) - * and set the needed headers instead. - * - * Example: - * --- - * import std.net.curl; - * auto client = HTTP(); - * client.addRequestHeader("X-Custom-ABC", "This is the custom value"); - * auto content = get("dlang.org", client); - * --- - */ - void addRequestHeader(const(char)[] name, const(char)[] value) - { - import std.format : format; - import std.internal.cstring : tempCString; - import std.uni : icmp; - - if (icmp(name, "User-Agent") == 0) - return setUserAgent(value); - string nv = format("%s: %s", name, value); - p.headersOut = Curl.curl.slist_append(p.headersOut, - nv.tempCString().buffPtr); - p.curl.set(CurlOption.httpheader, p.headersOut); - } - - /** - * The default "User-Agent" value send with a request. - * It has the form "Phobos-std.net.curl/$(I PHOBOS_VERSION) (libcurl/$(I CURL_VERSION))" - */ - static string defaultUserAgent() @property - { - import std.compiler : version_major, version_minor; - import std.format : format, sformat; - - // http://curl.haxx.se/docs/versions.html - enum fmt = "Phobos-std.net.curl/%d.%03d (libcurl/%d.%d.%d)"; - enum maxLen = fmt.length - "%d%03d%d%d%d".length + 10 + 10 + 3 + 3 + 3; - - static char[maxLen] buf = void; - static string userAgent; - - if (!userAgent.length) - { - auto curlVer = Curl.curl.version_info(CURLVERSION_NOW).version_num; - userAgent = cast(immutable) sformat( - buf, fmt, version_major, version_minor, - curlVer >> 16 & 0xFF, curlVer >> 8 & 0xFF, curlVer & 0xFF); - } - return userAgent; - } - - /** Set the value of the user agent request header field. - * - * By default a request has it's "User-Agent" field set to $(LREF - * defaultUserAgent) even if `setUserAgent` was never called. Pass - * an empty string to suppress the "User-Agent" field altogether. - */ - void setUserAgent(const(char)[] userAgent) - { - p.curl.set(CurlOption.useragent, userAgent); - } - - /** - * Get various timings defined in $(REF CurlInfo, etc, c, curl). - * The value is usable only if the return value is equal to `etc.c.curl.CurlError.ok`. - * - * Params: - * timing = one of the timings defined in $(REF CurlInfo, etc, c, curl). - * The values are: - * `etc.c.curl.CurlInfo.namelookup_time`, - * `etc.c.curl.CurlInfo.connect_time`, - * `etc.c.curl.CurlInfo.pretransfer_time`, - * `etc.c.curl.CurlInfo.starttransfer_time`, - * `etc.c.curl.CurlInfo.redirect_time`, - * `etc.c.curl.CurlInfo.appconnect_time`, - * `etc.c.curl.CurlInfo.total_time`. - * val = the actual value of the inquired timing. - * - * Returns: - * The return code of the operation. The value stored in val - * should be used only if the return value is `etc.c.curl.CurlInfo.ok`. - * - * Example: - * --- - * import std.net.curl; - * import etc.c.curl : CurlError, CurlInfo; - * - * auto client = HTTP("dlang.org"); - * client.perform(); - * - * double val; - * CurlCode code; - * - * code = client.getTiming(CurlInfo.namelookup_time, val); - * assert(code == CurlError.ok); - * --- - */ - CurlCode getTiming(CurlInfo timing, ref double val) - { - return p.curl.getTiming(timing, val); - } - - /** The headers read from a successful response. - * - */ - @property string[string] responseHeaders() - { - return p.headersIn; - } - - /// HTTP method used. - @property void method(Method m) - { - p.method = m; - } - - /// ditto - @property Method method() - { - return p.method; - } - - /** - HTTP status line of last response. One call to perform may - result in several requests because of redirection. - */ - @property StatusLine statusLine() - { - return p.status; - } - - /// Set the active cookie string e.g. "name1=value1;name2=value2" - void setCookie(const(char)[] cookie) - { - p.curl.set(CurlOption.cookie, cookie); - } - - /// Set a file path to where a cookie jar should be read/stored. - void setCookieJar(const(char)[] path) - { - p.curl.set(CurlOption.cookiefile, path); - if (path.length) - p.curl.set(CurlOption.cookiejar, path); - } - - /// Flush cookie jar to disk. - void flushCookieJar() - { - p.curl.set(CurlOption.cookielist, "FLUSH"); - } - - /// Clear session cookies. - void clearSessionCookies() - { - p.curl.set(CurlOption.cookielist, "SESS"); - } - - /// Clear all cookies. - void clearAllCookies() - { - p.curl.set(CurlOption.cookielist, "ALL"); - } - - /** - Set time condition on the request. - - Params: - cond = `CurlTimeCond.{none,ifmodsince,ifunmodsince,lastmod}` - timestamp = Timestamp for the condition - - $(HTTP www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.25, _RFC2616 Section 14.25) - */ - void setTimeCondition(HTTP.TimeCond cond, SysTime timestamp) - { - p.curl.set(CurlOption.timecondition, cond); - p.curl.set(CurlOption.timevalue, timestamp.toUnixTime()); - } - - /** Specifying data to post when not using the onSend callback. - * - * The data is NOT copied by the library. Content-Type will default to - * application/octet-stream. Data is not converted or encoded by this - * method. - * - * Example: - * ---- - * import std.net.curl, std.stdio, std.conv; - * auto http = HTTP("http://www.mydomain.com"); - * http.onReceive = (ubyte[] data) { writeln(to!(const(char)[])(data)); return data.length; }; - * http.postData = [1,2,3,4,5]; - * http.perform(); - * ---- - */ - @property void postData(const(void)[] data) - { - setPostData(data, "application/octet-stream"); - } - - /** Specifying data to post when not using the onSend callback. - * - * The data is NOT copied by the library. Content-Type will default to - * text/plain. Data is not converted or encoded by this method. - * - * Example: - * ---- - * import std.net.curl, std.stdio, std.conv; - * auto http = HTTP("http://www.mydomain.com"); - * http.onReceive = (ubyte[] data) { writeln(to!(const(char)[])(data)); return data.length; }; - * http.postData = "The quick...."; - * http.perform(); - * ---- - */ - @property void postData(const(char)[] data) - { - setPostData(data, "text/plain"); - } - - /** - * Specify data to post when not using the onSend callback, with - * user-specified Content-Type. - * Params: - * data = Data to post. - * contentType = MIME type of the data, for example, "text/plain" or - * "application/octet-stream". See also: - * $(LINK2 http://en.wikipedia.org/wiki/Internet_media_type, - * Internet media type) on Wikipedia. - * ----- - * import std.net.curl; - * auto http = HTTP("http://onlineform.example.com"); - * auto data = "app=login&username=bob&password=s00perS3kret"; - * http.setPostData(data, "application/x-www-form-urlencoded"); - * http.onReceive = (ubyte[] data) { return data.length; }; - * http.perform(); - * ----- - */ - void setPostData(const(void)[] data, string contentType) - { - // cannot use callback when specifying data directly so it is disabled here. - p.curl.clear(CurlOption.readfunction); - addRequestHeader("Content-Type", contentType); - p.curl.set(CurlOption.postfields, cast(void*) data.ptr); - p.curl.set(CurlOption.postfieldsize, data.length); - if (method == Method.undefined) - method = Method.post; - } - - @system unittest - { - import std.algorithm.searching : canFind; - - testServer.handle((s) { - auto req = s.recvReq!ubyte; - assert(req.hdrs.canFind("POST /path")); - assert(req.bdy.canFind(cast(ubyte[])[0, 1, 2, 3, 4])); - assert(req.bdy.canFind(cast(ubyte[])[253, 254, 255])); - s.send(httpOK(cast(ubyte[])[17, 27, 35, 41])); - }); - auto data = new ubyte[](256); - foreach (i, ref ub; data) - ub = cast(ubyte) i; - - auto http = HTTP(testServer.addr~"/path"); - http.postData = data; - ubyte[] res; - http.onReceive = (data) { res ~= data; return data.length; }; - http.perform(); - assert(res == cast(ubyte[])[17, 27, 35, 41]); - } - - /** - * Set the event handler that receives incoming headers. - * - * The callback will receive a header field key, value as parameter. The - * `const(char)[]` arrays are not valid after the delegate has returned. - * - * Example: - * ---- - * import std.net.curl, std.stdio, std.conv; - * auto http = HTTP("dlang.org"); - * http.onReceive = (ubyte[] data) { writeln(to!(const(char)[])(data)); return data.length; }; - * http.onReceiveHeader = (in char[] key, in char[] value) { writeln(key, " = ", value); }; - * http.perform(); - * ---- - */ - @property void onReceiveHeader(void delegate(in char[] key, - in char[] value) callback) - { - p.onReceiveHeader = callback; - } - - /** - Callback for each received StatusLine. - - Notice that several callbacks can be done for each call to - `perform()` due to redirections. - - See_Also: $(LREF StatusLine) - */ - @property void onReceiveStatusLine(void delegate(StatusLine) callback) - { - p.onReceiveStatusLine = callback; - } - - /** - The content length in bytes when using request that has content - e.g. POST/PUT and not using chunked transfer. Is set as the - "Content-Length" header. Set to ulong.max to reset to chunked transfer. - */ - @property void contentLength(ulong len) - { - import std.conv : to; - - CurlOption lenOpt; - - // Force post if necessary - if (p.method != Method.put && p.method != Method.post && - p.method != Method.patch) - p.method = Method.post; - - if (p.method == Method.post || p.method == Method.patch) - lenOpt = CurlOption.postfieldsize_large; - else - lenOpt = CurlOption.infilesize_large; - - if (size_t.max != ulong.max && len == size_t.max) - len = ulong.max; // check size_t.max for backwards compat, turn into error - - if (len == ulong.max) - { - // HTTP 1.1 supports requests with no length header set. - addRequestHeader("Transfer-Encoding", "chunked"); - addRequestHeader("Expect", "100-continue"); - } - else - { - p.curl.set(lenOpt, to!curl_off_t(len)); - } - } - - /** - Authentication method as specified in $(LREF AuthMethod). - */ - @property void authenticationMethod(AuthMethod authMethod) - { - p.curl.set(CurlOption.httpauth, cast(long) authMethod); - } - - /** - Set max allowed redirections using the location header. - uint.max for infinite. - */ - @property void maxRedirects(uint maxRedirs) - { - if (maxRedirs == uint.max) - { - // Disable - p.curl.set(CurlOption.followlocation, 0); - } - else - { - p.curl.set(CurlOption.followlocation, 1); - p.curl.set(CurlOption.maxredirs, maxRedirs); - } - } - - /** The standard HTTP methods : - * $(HTTP www.w3.org/Protocols/rfc2616/rfc2616-sec5.html#sec5.1.1, _RFC2616 Section 5.1.1) - */ - enum Method - { - undefined, - head, /// - get, /// - post, /// - put, /// - del, /// - options, /// - trace, /// - connect, /// - patch, /// - } - - /** - HTTP status line ie. the first line returned in an HTTP response. - - If authentication or redirections are done then the status will be for - the last response received. - */ - struct StatusLine - { - ushort majorVersion; /// Major HTTP version ie. 1 in HTTP/1.0. - ushort minorVersion; /// Minor HTTP version ie. 0 in HTTP/1.0. - ushort code; /// HTTP status line code e.g. 200. - string reason; /// HTTP status line reason string. - - /// Reset this status line - @safe void reset() - { - majorVersion = 0; - minorVersion = 0; - code = 0; - reason = ""; - } - - /// - string toString() const - { - import std.format : format; - return format("%s %s (%s.%s)", - code, reason, majorVersion, minorVersion); - } - } - -} // HTTP - -@system unittest // charset/Charset/CHARSET/... -{ - import etc.c.curl; - - static foreach (c; ["charset", "Charset", "CHARSET", "CharSet", "charSet", - "ChArSeT", "cHaRsEt"]) - {{ - testServer.handle((s) { - s.send("HTTP/1.1 200 OK\r\n"~ - "Content-Length: 0\r\n"~ - "Content-Type: text/plain; " ~ c ~ "=foo\r\n" ~ - "\r\n"); - }); - - auto http = HTTP(testServer.addr); - http.perform(); - assert(http.p.charset == "foo"); - - // https://issues.dlang.org/show_bug.cgi?id=16736 - double val; - CurlCode code; - - code = http.getTiming(CurlInfo.total_time, val); - assert(code == CurlError.ok); - code = http.getTiming(CurlInfo.namelookup_time, val); - assert(code == CurlError.ok); - code = http.getTiming(CurlInfo.connect_time, val); - assert(code == CurlError.ok); - code = http.getTiming(CurlInfo.pretransfer_time, val); - assert(code == CurlError.ok); - code = http.getTiming(CurlInfo.starttransfer_time, val); - assert(code == CurlError.ok); - code = http.getTiming(CurlInfo.redirect_time, val); - assert(code == CurlError.ok); - code = http.getTiming(CurlInfo.appconnect_time, val); - assert(code == CurlError.ok); - }} -} - -/** - FTP client functionality. - - See_Also: $(HTTP tools.ietf.org/html/rfc959, RFC959) -*/ -struct FTP -{ - - mixin Protocol; - - import std.typecons : RefCounted; - import etc.c.curl : CurlError, CurlInfo, curl_off_t, curl_slist; - - private struct Impl - { - ~this() - { - if (commands !is null) - Curl.curl.slist_free_all(commands); - if (curl.handle !is null) // work around RefCounted/emplace bug - curl.shutdown(); - } - curl_slist* commands; - Curl curl; - string encoding; - } - - private RefCounted!Impl p; - - /** - FTP access to the specified url. - */ - static FTP opCall(const(char)[] url) - { - FTP ftp; - ftp.initialize(); - ftp.url = url; - return ftp; - } - - /// - static FTP opCall() - { - FTP ftp; - ftp.initialize(); - return ftp; - } - - /// - FTP dup() - { - FTP copy = FTP(); - copy.initialize(); - copy.p.encoding = p.encoding; - copy.p.curl = p.curl.dup(); - curl_slist* cur = p.commands; - curl_slist* newlist = null; - while (cur) - { - newlist = Curl.curl.slist_append(newlist, cur.data); - cur = cur.next; - } - copy.p.commands = newlist; - copy.p.curl.set(CurlOption.postquote, copy.p.commands); - copy.dataTimeout = _defaultDataTimeout; - return copy; - } - - private void initialize() - { - p.curl.initialize(); - p.encoding = "ISO-8859-1"; - dataTimeout = _defaultDataTimeout; - } - - /** - Performs the ftp request as it has been configured. - - After a FTP client has been setup and possibly assigned callbacks the $(D - perform()) method will start performing the actual communication with the - server. - - Params: - throwOnError = whether to throw an exception or return a CurlCode on error - */ - CurlCode perform(ThrowOnError throwOnError = Yes.throwOnError) - { - return p.curl.perform(throwOnError); - } - - /// The URL to specify the location of the resource. - @property void url(const(char)[] url) - { - import std.algorithm.searching : startsWith; - import std.uni : toLower; - - if (!startsWith(url.toLower(), "ftp://", "ftps://")) - url = "ftp://" ~ url; - p.curl.set(CurlOption.url, url); - } - - // This is a workaround for mixed in content not having its - // docs mixed in. - version (StdDdoc) - { - static import etc.c.curl; - - /// Value to return from `onSend`/`onReceive` delegates in order to - /// pause a request - alias requestPause = CurlReadFunc.pause; - - /// Value to return from onSend delegate in order to abort a request - alias requestAbort = CurlReadFunc.abort; - - /** - True if the instance is stopped. A stopped instance is not usable. - */ - @property bool isStopped(); - - /// Stop and invalidate this instance. - void shutdown(); - - /** Set verbose. - This will print request information to stderr. - */ - @property void verbose(bool on); - - // Connection settings - - /// Set timeout for activity on connection. - @property void dataTimeout(Duration d); - - /** Set maximum time an operation is allowed to take. - This includes dns resolution, connecting, data transfer, etc. - */ - @property void operationTimeout(Duration d); - - /// Set timeout for connecting. - @property void connectTimeout(Duration d); - - // Network settings - - /** Proxy - * See: $(HTTP curl.haxx.se/libcurl/c/curl_easy_setopt.html#CURLOPTPROXY, _proxy) - */ - @property void proxy(const(char)[] host); - - /** Proxy port - * See: $(HTTP curl.haxx.se/libcurl/c/curl_easy_setopt.html#CURLOPTPROXYPORT, _proxy_port) - */ - @property void proxyPort(ushort port); - - /// Type of proxy - alias CurlProxy = etc.c.curl.CurlProxy; - - /** Proxy type - * See: $(HTTP curl.haxx.se/libcurl/c/curl_easy_setopt.html#CURLOPTPROXY, _proxy_type) - */ - @property void proxyType(CurlProxy type); - - /// DNS lookup timeout. - @property void dnsTimeout(Duration d); - - /** - * The network interface to use in form of the IP of the interface. - * - * Example: - * ---- - * theprotocol.netInterface = "192.168.1.32"; - * theprotocol.netInterface = [ 192, 168, 1, 32 ]; - * ---- - * - * See: $(REF InternetAddress, std,socket) - */ - @property void netInterface(const(char)[] i); - - /// ditto - @property void netInterface(const(ubyte)[4] i); - - /// ditto - @property void netInterface(InternetAddress i); - - /** - Set the local outgoing port to use. - Params: - port = the first outgoing port number to try and use - */ - @property void localPort(ushort port); - - /** - Set the local outgoing port range to use. - This can be used together with the localPort property. - Params: - range = if the first port is occupied then try this many - port number forwards - */ - @property void localPortRange(ushort range); - - /** Set the tcp no-delay socket option on or off. - See: $(HTTP curl.haxx.se/libcurl/c/curl_easy_setopt.html#CURLOPTTCPNODELAY, nodelay) - */ - @property void tcpNoDelay(bool on); - - // Authentication settings - - /** - Set the user name, password and optionally domain for authentication - purposes. - - Some protocols may need authentication in some cases. Use this - function to provide credentials. - - Params: - username = the username - password = the password - domain = used for NTLM authentication only and is set to the NTLM domain - name - */ - void setAuthentication(const(char)[] username, const(char)[] password, - const(char)[] domain = ""); - - /** - Set the user name and password for proxy authentication. - - Params: - username = the username - password = the password - */ - void setProxyAuthentication(const(char)[] username, const(char)[] password); - - /** - * The event handler that gets called when data is needed for sending. The - * length of the `void[]` specifies the maximum number of bytes that can - * be sent. - * - * Returns: - * The callback returns the number of elements in the buffer that have been - * filled and are ready to send. - * The special value `.abortRequest` can be returned in order to abort the - * current request. - * The special value `.pauseRequest` can be returned in order to pause the - * current request. - * - */ - @property void onSend(size_t delegate(void[]) callback); - - /** - * The event handler that receives incoming data. Be sure to copy the - * incoming ubyte[] since it is not guaranteed to be valid after the - * callback returns. - * - * Returns: - * The callback returns the incoming bytes read. If not the entire array is - * the request will abort. - * The special value .pauseRequest can be returned in order to pause the - * current request. - * - */ - @property void onReceive(size_t delegate(ubyte[]) callback); - - /** - * The event handler that gets called to inform of upload/download progress. - * - * Callback_parameters: - * $(CALLBACK_PARAMS) - * - * Callback_returns: - * Return 0 from the callback to signal success, return non-zero to - * abort transfer. - */ - @property void onProgress(int delegate(size_t dlTotal, size_t dlNow, - size_t ulTotal, size_t ulNow) callback); - } - - /** Clear all commands send to ftp server. - */ - void clearCommands() - { - if (p.commands !is null) - Curl.curl.slist_free_all(p.commands); - p.commands = null; - p.curl.clear(CurlOption.postquote); - } - - /** Add a command to send to ftp server. - * - * There is no remove command functionality. Do a $(LREF clearCommands) and - * set the needed commands instead. - * - * Example: - * --- - * import std.net.curl; - * auto client = FTP(); - * client.addCommand("RNFR my_file.txt"); - * client.addCommand("RNTO my_renamed_file.txt"); - * upload("my_file.txt", "ftp.digitalmars.com", client); - * --- - */ - void addCommand(const(char)[] command) - { - import std.internal.cstring : tempCString; - p.commands = Curl.curl.slist_append(p.commands, - command.tempCString().buffPtr); - p.curl.set(CurlOption.postquote, p.commands); - } - - /// Connection encoding. Defaults to ISO-8859-1. - @property void encoding(string name) - { - p.encoding = name; - } - - /// ditto - @property string encoding() - { - return p.encoding; - } - - /** - The content length in bytes of the ftp data. - */ - @property void contentLength(ulong len) - { - import std.conv : to; - p.curl.set(CurlOption.infilesize_large, to!curl_off_t(len)); - } - - /** - * Get various timings defined in $(REF CurlInfo, etc, c, curl). - * The value is usable only if the return value is equal to `etc.c.curl.CurlError.ok`. - * - * Params: - * timing = one of the timings defined in $(REF CurlInfo, etc, c, curl). - * The values are: - * `etc.c.curl.CurlInfo.namelookup_time`, - * `etc.c.curl.CurlInfo.connect_time`, - * `etc.c.curl.CurlInfo.pretransfer_time`, - * `etc.c.curl.CurlInfo.starttransfer_time`, - * `etc.c.curl.CurlInfo.redirect_time`, - * `etc.c.curl.CurlInfo.appconnect_time`, - * `etc.c.curl.CurlInfo.total_time`. - * val = the actual value of the inquired timing. - * - * Returns: - * The return code of the operation. The value stored in val - * should be used only if the return value is `etc.c.curl.CurlInfo.ok`. - * - * Example: - * --- - * import std.net.curl; - * import etc.c.curl : CurlError, CurlInfo; - * - * auto client = FTP(); - * client.addCommand("RNFR my_file.txt"); - * client.addCommand("RNTO my_renamed_file.txt"); - * upload("my_file.txt", "ftp.digitalmars.com", client); - * - * double val; - * CurlCode code; - * - * code = client.getTiming(CurlInfo.namelookup_time, val); - * assert(code == CurlError.ok); - * --- - */ - CurlCode getTiming(CurlInfo timing, ref double val) - { - return p.curl.getTiming(timing, val); - } - - @system unittest - { - auto client = FTP(); - - double val; - CurlCode code; - - code = client.getTiming(CurlInfo.total_time, val); - assert(code == CurlError.ok); - code = client.getTiming(CurlInfo.namelookup_time, val); - assert(code == CurlError.ok); - code = client.getTiming(CurlInfo.connect_time, val); - assert(code == CurlError.ok); - code = client.getTiming(CurlInfo.pretransfer_time, val); - assert(code == CurlError.ok); - code = client.getTiming(CurlInfo.starttransfer_time, val); - assert(code == CurlError.ok); - code = client.getTiming(CurlInfo.redirect_time, val); - assert(code == CurlError.ok); - code = client.getTiming(CurlInfo.appconnect_time, val); - assert(code == CurlError.ok); - } -} - -/** - * Basic SMTP protocol support. - * - * Example: - * --- - * import std.net.curl; - * - * // Send an email with SMTPS - * auto smtp = SMTP("smtps://smtp.gmail.com"); - * smtp.setAuthentication("from.addr@gmail.com", "password"); - * smtp.mailTo = [""]; - * smtp.mailFrom = ""; - * smtp.message = "Example Message"; - * smtp.perform(); - * --- - * - * See_Also: $(HTTP www.ietf.org/rfc/rfc2821.txt, RFC2821) - */ -struct SMTP -{ - mixin Protocol; - import std.typecons : RefCounted; - import etc.c.curl : CurlUseSSL, curl_slist; - - private struct Impl - { - ~this() - { - if (curl.handle !is null) // work around RefCounted/emplace bug - curl.shutdown(); - } - Curl curl; - - @property void message(string msg) - { - import std.algorithm.comparison : min; - - auto _message = msg; - /** - This delegate reads the message text and copies it. - */ - curl.onSend = delegate size_t(void[] data) - { - if (!msg.length) return 0; - size_t to_copy = min(data.length, _message.length); - data[0 .. to_copy] = (cast(void[])_message)[0 .. to_copy]; - _message = _message[to_copy..$]; - return to_copy; - }; - } - } - - private RefCounted!Impl p; - - /** - Sets to the URL of the SMTP server. - */ - static SMTP opCall(const(char)[] url) - { - SMTP smtp; - smtp.initialize(); - smtp.url = url; - return smtp; - } - - /// - static SMTP opCall() - { - SMTP smtp; - smtp.initialize(); - return smtp; - } - - /+ TODO: The other structs have this function. - SMTP dup() - { - SMTP copy = SMTP(); - copy.initialize(); - copy.p.encoding = p.encoding; - copy.p.curl = p.curl.dup(); - curl_slist* cur = p.commands; - curl_slist* newlist = null; - while (cur) - { - newlist = Curl.curl.slist_append(newlist, cur.data); - cur = cur.next; - } - copy.p.commands = newlist; - copy.p.curl.set(CurlOption.postquote, copy.p.commands); - copy.dataTimeout = _defaultDataTimeout; - return copy; - } - +/ - - /** - Performs the request as configured. - Params: - throwOnError = whether to throw an exception or return a CurlCode on error - */ - CurlCode perform(ThrowOnError throwOnError = Yes.throwOnError) - { - return p.curl.perform(throwOnError); - } - - /// The URL to specify the location of the resource. - @property void url(const(char)[] url) - { - import std.algorithm.searching : startsWith; - import std.exception : enforce; - import std.uni : toLower; - - auto lowered = url.toLower(); - - if (lowered.startsWith("smtps://")) - { - p.curl.set(CurlOption.use_ssl, CurlUseSSL.all); - } - else - { - enforce!CurlException(lowered.startsWith("smtp://"), - "The url must be for the smtp protocol."); - } - p.curl.set(CurlOption.url, url); - } - - private void initialize() - { - p.curl.initialize(); - p.curl.set(CurlOption.upload, 1L); - dataTimeout = _defaultDataTimeout; - verifyPeer = true; - verifyHost = true; - } - - // This is a workaround for mixed in content not having its - // docs mixed in. - version (StdDdoc) - { - static import etc.c.curl; - - /// Value to return from `onSend`/`onReceive` delegates in order to - /// pause a request - alias requestPause = CurlReadFunc.pause; - - /// Value to return from onSend delegate in order to abort a request - alias requestAbort = CurlReadFunc.abort; - - /** - True if the instance is stopped. A stopped instance is not usable. - */ - @property bool isStopped(); - - /// Stop and invalidate this instance. - void shutdown(); - - /** Set verbose. - This will print request information to stderr. - */ - @property void verbose(bool on); - - // Connection settings - - /// Set timeout for activity on connection. - @property void dataTimeout(Duration d); - - /** Set maximum time an operation is allowed to take. - This includes dns resolution, connecting, data transfer, etc. - */ - @property void operationTimeout(Duration d); - - /// Set timeout for connecting. - @property void connectTimeout(Duration d); - - // Network settings - - /** Proxy - * See: $(HTTP curl.haxx.se/libcurl/c/curl_easy_setopt.html#CURLOPTPROXY, _proxy) - */ - @property void proxy(const(char)[] host); - - /** Proxy port - * See: $(HTTP curl.haxx.se/libcurl/c/curl_easy_setopt.html#CURLOPTPROXYPORT, _proxy_port) - */ - @property void proxyPort(ushort port); - - /// Type of proxy - alias CurlProxy = etc.c.curl.CurlProxy; - - /** Proxy type - * See: $(HTTP curl.haxx.se/libcurl/c/curl_easy_setopt.html#CURLOPTPROXY, _proxy_type) - */ - @property void proxyType(CurlProxy type); - - /// DNS lookup timeout. - @property void dnsTimeout(Duration d); - - /** - * The network interface to use in form of the IP of the interface. - * - * Example: - * ---- - * theprotocol.netInterface = "192.168.1.32"; - * theprotocol.netInterface = [ 192, 168, 1, 32 ]; - * ---- - * - * See: $(REF InternetAddress, std,socket) - */ - @property void netInterface(const(char)[] i); - - /// ditto - @property void netInterface(const(ubyte)[4] i); - - /// ditto - @property void netInterface(InternetAddress i); - - /** - Set the local outgoing port to use. - Params: - port = the first outgoing port number to try and use - */ - @property void localPort(ushort port); - - /** - Set the local outgoing port range to use. - This can be used together with the localPort property. - Params: - range = if the first port is occupied then try this many - port number forwards - */ - @property void localPortRange(ushort range); - - /** Set the tcp no-delay socket option on or off. - See: $(HTTP curl.haxx.se/libcurl/c/curl_easy_setopt.html#CURLOPTTCPNODELAY, nodelay) - */ - @property void tcpNoDelay(bool on); - - // Authentication settings - - /** - Set the user name, password and optionally domain for authentication - purposes. - - Some protocols may need authentication in some cases. Use this - function to provide credentials. - - Params: - username = the username - password = the password - domain = used for NTLM authentication only and is set to the NTLM domain - name - */ - void setAuthentication(const(char)[] username, const(char)[] password, - const(char)[] domain = ""); - - /** - Set the user name and password for proxy authentication. - - Params: - username = the username - password = the password - */ - void setProxyAuthentication(const(char)[] username, const(char)[] password); - - /** - * The event handler that gets called when data is needed for sending. The - * length of the `void[]` specifies the maximum number of bytes that can - * be sent. - * - * Returns: - * The callback returns the number of elements in the buffer that have been - * filled and are ready to send. - * The special value `.abortRequest` can be returned in order to abort the - * current request. - * The special value `.pauseRequest` can be returned in order to pause the - * current request. - */ - @property void onSend(size_t delegate(void[]) callback); - - /** - * The event handler that receives incoming data. Be sure to copy the - * incoming ubyte[] since it is not guaranteed to be valid after the - * callback returns. - * - * Returns: - * The callback returns the incoming bytes read. If not the entire array is - * the request will abort. - * The special value .pauseRequest can be returned in order to pause the - * current request. - */ - @property void onReceive(size_t delegate(ubyte[]) callback); - - /** - * The event handler that gets called to inform of upload/download progress. - * - * Callback_parameters: - * $(CALLBACK_PARAMS) - * - * Callback_returns: - * Return 0 from the callback to signal success, return non-zero to - * abort transfer. - */ - @property void onProgress(int delegate(size_t dlTotal, size_t dlNow, - size_t ulTotal, size_t ulNow) callback); - } - - /** - Setter for the sender's email address. - */ - @property void mailFrom()(const(char)[] sender) - { - assert(!sender.empty, "Sender must not be empty"); - p.curl.set(CurlOption.mail_from, sender); - } - - /** - Setter for the recipient email addresses. - */ - void mailTo()(const(char)[][] recipients...) - { - import std.internal.cstring : tempCString; - assert(!recipients.empty, "Recipient must not be empty"); - curl_slist* recipients_list = null; - foreach (recipient; recipients) - { - recipients_list = - Curl.curl.slist_append(recipients_list, - recipient.tempCString().buffPtr); - } - p.curl.set(CurlOption.mail_rcpt, recipients_list); - } - - /** - Sets the message body text. - */ - - @property void message(string msg) - { - p.message = msg; - } -} - -@system unittest -{ - import std.net.curl; - - // Send an email with SMTPS - auto smtp = SMTP("smtps://smtp.gmail.com"); - smtp.setAuthentication("from.addr@gmail.com", "password"); - smtp.mailTo = [""]; - smtp.mailFrom = ""; - smtp.message = "Example Message"; - //smtp.perform(); -} - - -/++ - Exception thrown on errors in std.net.curl functions. -+/ -class CurlException : Exception -{ - /++ - Params: - msg = The message for the exception. - file = The file where the exception occurred. - line = The line number where the exception occurred. - next = The previous exception in the chain of exceptions, if any. - +/ - @safe pure nothrow - this(string msg, - string file = __FILE__, - size_t line = __LINE__, - Throwable next = null) - { - super(msg, file, line, next); - } -} - -/++ - Exception thrown on timeout errors in std.net.curl functions. -+/ -class CurlTimeoutException : CurlException -{ - /++ - Params: - msg = The message for the exception. - file = The file where the exception occurred. - line = The line number where the exception occurred. - next = The previous exception in the chain of exceptions, if any. - +/ - @safe pure nothrow - this(string msg, - string file = __FILE__, - size_t line = __LINE__, - Throwable next = null) - { - super(msg, file, line, next); - } -} - -/++ - Exception thrown on HTTP request failures, e.g. 404 Not Found. -+/ -class HTTPStatusException : CurlException -{ - /++ - Params: - status = The HTTP status code. - msg = The message for the exception. - file = The file where the exception occurred. - line = The line number where the exception occurred. - next = The previous exception in the chain of exceptions, if any. - +/ - @safe pure nothrow - this(int status, - string msg, - string file = __FILE__, - size_t line = __LINE__, - Throwable next = null) - { - super(msg, file, line, next); - this.status = status; - } - - immutable int status; /// The HTTP status code -} - -/// Equal to $(REF CURLcode, etc,c,curl) -alias CurlCode = CURLcode; - -/// Flag to specify whether or not an exception is thrown on error. -alias ThrowOnError = Flag!"throwOnError"; - -private struct CurlAPI -{ - import etc.c.curl : CurlGlobal; - static struct API - { - import etc.c.curl : curl_version_info, curl_version_info_data, - CURL, CURLcode, CURLINFO, CURLoption, CURLversion, curl_slist; - extern(C): - import core.stdc.config : c_long; - CURLcode function(c_long flags) global_init; - void function() global_cleanup; - curl_version_info_data * function(CURLversion) version_info; - CURL* function() easy_init; - CURLcode function(CURL *curl, CURLoption option,...) easy_setopt; - CURLcode function(CURL *curl) easy_perform; - CURLcode function(CURL *curl, CURLINFO info,...) easy_getinfo; - CURL* function(CURL *curl) easy_duphandle; - char* function(CURLcode) easy_strerror; - CURLcode function(CURL *handle, int bitmask) easy_pause; - void function(CURL *curl) easy_cleanup; - curl_slist* function(curl_slist *, char *) slist_append; - void function(curl_slist *) slist_free_all; - } - __gshared API _api; - __gshared void* _handle; - - static ref API instance() @property - { - import std.concurrency : initOnce; - initOnce!_handle(loadAPI()); - return _api; - } - - static void* loadAPI() - { - import std.exception : enforce; - - version (Posix) - { - import core.sys.posix.dlfcn : dlsym, dlopen, dlclose, RTLD_LAZY; - alias loadSym = dlsym; - } - else version (Windows) - { - import core.sys.windows.winbase : GetProcAddress, GetModuleHandleA, - LoadLibraryA; - alias loadSym = GetProcAddress; - } - else - static assert(0, "unimplemented"); - - void* handle; - version (Posix) - handle = dlopen(null, RTLD_LAZY); - else version (Windows) - handle = GetModuleHandleA(null); - assert(handle !is null); - - // try to load curl from the executable to allow static linking - if (loadSym(handle, "curl_global_init") is null) - { - import std.format : format; - version (Posix) - dlclose(handle); - - version (LibcurlPath) - { - import std.string : strip; - static immutable names = [strip(import("LibcurlPathFile"))]; - } - else version (OSX) - static immutable names = ["libcurl.4.dylib"]; - else version (Posix) - { - static immutable names = ["libcurl.so", "libcurl.so.4", - "libcurl-gnutls.so.4", "libcurl-nss.so.4", "libcurl.so.3"]; - } - else version (Windows) - static immutable names = ["libcurl.dll", "curl.dll"]; - - foreach (name; names) - { - version (Posix) - handle = dlopen(name.ptr, RTLD_LAZY); - else version (Windows) - handle = LoadLibraryA(name.ptr); - if (handle !is null) break; - } - - enforce!CurlException(handle !is null, "Failed to load curl, tried %(%s, %).".format(names)); - } - - foreach (i, FP; typeof(API.tupleof)) - { - enum name = __traits(identifier, _api.tupleof[i]); - auto p = enforce!CurlException(loadSym(handle, "curl_"~name), - "Couldn't load curl_"~name~" from libcurl."); - _api.tupleof[i] = cast(FP) p; - } - - enforce!CurlException(!_api.global_init(CurlGlobal.all), - "Failed to initialize libcurl"); - - static extern(C) void cleanup() - { - if (_handle is null) return; - _api.global_cleanup(); - version (Posix) - { - import core.sys.posix.dlfcn : dlclose; - dlclose(_handle); - } - else version (Windows) - { - import core.sys.windows.winbase : FreeLibrary; - FreeLibrary(_handle); - } - else - static assert(0, "unimplemented"); - _api = API.init; - _handle = null; - } - - import core.stdc.stdlib : atexit; - atexit(&cleanup); - - return handle; - } -} - -/** - Wrapper to provide a better interface to libcurl than using the plain C API. - It is recommended to use the `HTTP`/`FTP` etc. structs instead unless - raw access to libcurl is needed. - - Warning: This struct uses interior pointers for callbacks. Only allocate it - on the stack if you never move or copy it. This also means passing by reference - when passing Curl to other functions. Otherwise always allocate on - the heap. -*/ -struct Curl -{ - import etc.c.curl : CURL, CurlError, CurlPause, CurlSeek, CurlSeekPos, - curl_socket_t, CurlSockType, - CurlReadFunc, CurlInfo, curlsocktype, curl_off_t, - LIBCURL_VERSION_MAJOR, LIBCURL_VERSION_MINOR, LIBCURL_VERSION_PATCH; - - alias OutData = void[]; - alias InData = ubyte[]; - private bool _stopped; - - private static auto ref curl() @property { return CurlAPI.instance; } - - // A handle should not be used by two threads simultaneously - private CURL* handle; - - // May also return `CURL_READFUNC_ABORT` or `CURL_READFUNC_PAUSE` - private size_t delegate(OutData) _onSend; - private size_t delegate(InData) _onReceive; - private void delegate(in char[]) _onReceiveHeader; - private CurlSeek delegate(long,CurlSeekPos) _onSeek; - private int delegate(curl_socket_t,CurlSockType) _onSocketOption; - private int delegate(size_t dltotal, size_t dlnow, - size_t ultotal, size_t ulnow) _onProgress; - - alias requestPause = CurlReadFunc.pause; - alias requestAbort = CurlReadFunc.abort; - - /** - Initialize the instance by creating a working curl handle. - */ - void initialize() - { - import std.exception : enforce; - enforce!CurlException(!handle, "Curl instance already initialized"); - handle = curl.easy_init(); - enforce!CurlException(handle, "Curl instance couldn't be initialized"); - _stopped = false; - set(CurlOption.nosignal, 1); - } - - /// - @property bool stopped() const - { - return _stopped; - } - - /** - Duplicate this handle. - - The new handle will have all options set as the one it was duplicated - from. An exception to this is that all options that cannot be shared - across threads are reset thereby making it safe to use the duplicate - in a new thread. - */ - Curl dup() - { - import std.meta : AliasSeq; - Curl copy; - copy.handle = curl.easy_duphandle(handle); - copy._stopped = false; - - with (CurlOption) { - auto tt = AliasSeq!(file, writefunction, writeheader, - headerfunction, infile, readfunction, ioctldata, ioctlfunction, - seekdata, seekfunction, sockoptdata, sockoptfunction, - opensocketdata, opensocketfunction, progressdata, - progressfunction, debugdata, debugfunction, interleavedata, - interleavefunction, chunk_data, chunk_bgn_function, - chunk_end_function, fnmatch_data, fnmatch_function, cookiejar, postfields); - - foreach (option; tt) - copy.clear(option); - } - - // The options are only supported by libcurl when it has been built - // against certain versions of OpenSSL - if your libcurl uses an old - // OpenSSL, or uses an entirely different SSL engine, attempting to - // clear these normally will raise an exception - copy.clearIfSupported(CurlOption.ssl_ctx_function); - copy.clearIfSupported(CurlOption.ssh_keydata); - - // Enable for curl version > 7.21.7 - static if (LIBCURL_VERSION_MAJOR >= 7 && - LIBCURL_VERSION_MINOR >= 21 && - LIBCURL_VERSION_PATCH >= 7) - { - copy.clear(CurlOption.closesocketdata); - copy.clear(CurlOption.closesocketfunction); - } - - copy.set(CurlOption.nosignal, 1); - - // copy.clear(CurlOption.ssl_ctx_data); Let ssl function be shared - // copy.clear(CurlOption.ssh_keyfunction); Let key function be shared - - /* - Allow sharing of conv functions - copy.clear(CurlOption.conv_to_network_function); - copy.clear(CurlOption.conv_from_network_function); - copy.clear(CurlOption.conv_from_utf8_function); - */ - - return copy; - } - - private void _check(CurlCode code) - { - import std.exception : enforce; - enforce!CurlTimeoutException(code != CurlError.operation_timedout, - errorString(code)); - - enforce!CurlException(code == CurlError.ok, - errorString(code)); - } - - private string errorString(CurlCode code) - { - import core.stdc.string : strlen; - import std.format : format; - - auto msgZ = curl.easy_strerror(code); - // doing the following (instead of just using std.conv.to!string) avoids 1 allocation - return format("%s on handle %s", msgZ[0 .. strlen(msgZ)], handle); - } - - private void throwOnStopped(string message = null) - { - import std.exception : enforce; - auto def = "Curl instance called after being cleaned up"; - enforce!CurlException(!stopped, - message == null ? def : message); - } - - /** - Stop and invalidate this curl instance. - Warning: Do not call this from inside a callback handler e.g. `onReceive`. - */ - void shutdown() - { - throwOnStopped(); - _stopped = true; - curl.easy_cleanup(this.handle); - this.handle = null; - } - - /** - Pausing and continuing transfers. - */ - void pause(bool sendingPaused, bool receivingPaused) - { - throwOnStopped(); - _check(curl.easy_pause(this.handle, - (sendingPaused ? CurlPause.send_cont : CurlPause.send) | - (receivingPaused ? CurlPause.recv_cont : CurlPause.recv))); - } - - /** - Set a string curl option. - Params: - option = A $(REF CurlOption, etc,c,curl) as found in the curl documentation - value = The string - */ - void set(CurlOption option, const(char)[] value) - { - import std.internal.cstring : tempCString; - throwOnStopped(); - _check(curl.easy_setopt(this.handle, option, value.tempCString().buffPtr)); - } - - /** - Set a long curl option. - Params: - option = A $(REF CurlOption, etc,c,curl) as found in the curl documentation - value = The long - */ - void set(CurlOption option, long value) - { - throwOnStopped(); - _check(curl.easy_setopt(this.handle, option, value)); - } - - /** - Set a void* curl option. - Params: - option = A $(REF CurlOption, etc,c,curl) as found in the curl documentation - value = The pointer - */ - void set(CurlOption option, void* value) - { - throwOnStopped(); - _check(curl.easy_setopt(this.handle, option, value)); - } - - /** - Clear a pointer option. - Params: - option = A $(REF CurlOption, etc,c,curl) as found in the curl documentation - */ - void clear(CurlOption option) - { - throwOnStopped(); - _check(curl.easy_setopt(this.handle, option, null)); - } - - /** - Clear a pointer option. Does not raise an exception if the underlying - libcurl does not support the option. Use sparingly. - Params: - option = A $(REF CurlOption, etc,c,curl) as found in the curl documentation - */ - void clearIfSupported(CurlOption option) - { - throwOnStopped(); - auto rval = curl.easy_setopt(this.handle, option, null); - if (rval != CurlError.unknown_option && rval != CurlError.not_built_in) - _check(rval); - } - - /** - perform the curl request by doing the HTTP,FTP etc. as it has - been setup beforehand. - - Params: - throwOnError = whether to throw an exception or return a CurlCode on error - */ - CurlCode perform(ThrowOnError throwOnError = Yes.throwOnError) - { - throwOnStopped(); - CurlCode code = curl.easy_perform(this.handle); - if (throwOnError) - _check(code); - return code; - } - - /** - Get the various timings like name lookup time, total time, connect time etc. - The timed category is passed through the timing parameter while the timing - value is stored at val. The value is usable only if res is equal to - `etc.c.curl.CurlError.ok`. - */ - CurlCode getTiming(CurlInfo timing, ref double val) - { - CurlCode code; - code = curl.easy_getinfo(handle, timing, &val); - return code; - } - - /** - * The event handler that receives incoming data. - * - * Params: - * callback = the callback that receives the `ubyte[]` data. - * Be sure to copy the incoming data and not store - * a slice. - * - * Returns: - * The callback returns the incoming bytes read. If not the entire array is - * the request will abort. - * The special value HTTP.pauseRequest can be returned in order to pause the - * current request. - * - * Example: - * ---- - * import std.net.curl, std.stdio, std.conv; - * Curl curl; - * curl.initialize(); - * curl.set(CurlOption.url, "http://dlang.org"); - * curl.onReceive = (ubyte[] data) { writeln("Got data", to!(const(char)[])(data)); return data.length;}; - * curl.perform(); - * ---- - */ - @property void onReceive(size_t delegate(InData) callback) - { - _onReceive = (InData id) - { - throwOnStopped("Receive callback called on cleaned up Curl instance"); - return callback(id); - }; - set(CurlOption.file, cast(void*) &this); - set(CurlOption.writefunction, cast(void*) &Curl._receiveCallback); - } - - /** - * The event handler that receives incoming headers for protocols - * that uses headers. - * - * Params: - * callback = the callback that receives the header string. - * Make sure the callback copies the incoming params if - * it needs to store it because they are references into - * the backend and may very likely change. - * - * Example: - * ---- - * import std.net.curl, std.stdio; - * Curl curl; - * curl.initialize(); - * curl.set(CurlOption.url, "http://dlang.org"); - * curl.onReceiveHeader = (in char[] header) { writeln(header); }; - * curl.perform(); - * ---- - */ - @property void onReceiveHeader(void delegate(in char[]) callback) - { - _onReceiveHeader = (in char[] od) - { - throwOnStopped("Receive header callback called on "~ - "cleaned up Curl instance"); - callback(od); - }; - set(CurlOption.writeheader, cast(void*) &this); - set(CurlOption.headerfunction, - cast(void*) &Curl._receiveHeaderCallback); - } - - /** - * The event handler that gets called when data is needed for sending. - * - * Params: - * callback = the callback that has a `void[]` buffer to be filled - * - * Returns: - * The callback returns the number of elements in the buffer that have been - * filled and are ready to send. - * The special value `Curl.abortRequest` can be returned in - * order to abort the current request. - * The special value `Curl.pauseRequest` can be returned in order to - * pause the current request. - * - * Example: - * ---- - * import std.net.curl; - * Curl curl; - * curl.initialize(); - * curl.set(CurlOption.url, "http://dlang.org"); - * - * string msg = "Hello world"; - * curl.onSend = (void[] data) - * { - * auto m = cast(void[]) msg; - * size_t length = m.length > data.length ? data.length : m.length; - * if (length == 0) return 0; - * data[0 .. length] = m[0 .. length]; - * msg = msg[length..$]; - * return length; - * }; - * curl.perform(); - * ---- - */ - @property void onSend(size_t delegate(OutData) callback) - { - _onSend = (OutData od) - { - throwOnStopped("Send callback called on cleaned up Curl instance"); - return callback(od); - }; - set(CurlOption.infile, cast(void*) &this); - set(CurlOption.readfunction, cast(void*) &Curl._sendCallback); - } - - /** - * The event handler that gets called when the curl backend needs to seek - * the data to be sent. - * - * Params: - * callback = the callback that receives a seek offset and a seek position - * $(REF CurlSeekPos, etc,c,curl) - * - * Returns: - * The callback returns the success state of the seeking - * $(REF CurlSeek, etc,c,curl) - * - * Example: - * ---- - * import std.net.curl; - * Curl curl; - * curl.initialize(); - * curl.set(CurlOption.url, "http://dlang.org"); - * curl.onSeek = (long p, CurlSeekPos sp) - * { - * return CurlSeek.cantseek; - * }; - * curl.perform(); - * ---- - */ - @property void onSeek(CurlSeek delegate(long, CurlSeekPos) callback) - { - _onSeek = (long ofs, CurlSeekPos sp) - { - throwOnStopped("Seek callback called on cleaned up Curl instance"); - return callback(ofs, sp); - }; - set(CurlOption.seekdata, cast(void*) &this); - set(CurlOption.seekfunction, cast(void*) &Curl._seekCallback); - } - - /** - * The event handler that gets called when the net socket has been created - * but a `connect()` call has not yet been done. This makes it possible to set - * misc. socket options. - * - * Params: - * callback = the callback that receives the socket and socket type - * $(REF CurlSockType, etc,c,curl) - * - * Returns: - * Return 0 from the callback to signal success, return 1 to signal error - * and make curl close the socket - * - * Example: - * ---- - * import std.net.curl; - * Curl curl; - * curl.initialize(); - * curl.set(CurlOption.url, "http://dlang.org"); - * curl.onSocketOption = delegate int(curl_socket_t s, CurlSockType t) { /+ do stuff +/ }; - * curl.perform(); - * ---- - */ - @property void onSocketOption(int delegate(curl_socket_t, - CurlSockType) callback) - { - _onSocketOption = (curl_socket_t sock, CurlSockType st) - { - throwOnStopped("Socket option callback called on "~ - "cleaned up Curl instance"); - return callback(sock, st); - }; - set(CurlOption.sockoptdata, cast(void*) &this); - set(CurlOption.sockoptfunction, - cast(void*) &Curl._socketOptionCallback); - } - - /** - * The event handler that gets called to inform of upload/download progress. - * - * Params: - * callback = the callback that receives the (total bytes to download, - * currently downloaded bytes, total bytes to upload, currently uploaded - * bytes). - * - * Returns: - * Return 0 from the callback to signal success, return non-zero to abort - * transfer - * - * Example: - * ---- - * import std.net.curl, std.stdio; - * Curl curl; - * curl.initialize(); - * curl.set(CurlOption.url, "http://dlang.org"); - * curl.onProgress = delegate int(size_t dltotal, size_t dlnow, size_t ultotal, size_t ulnow) - * { - * writeln("Progress: downloaded bytes ", dlnow, " of ", dltotal); - * writeln("Progress: uploaded bytes ", ulnow, " of ", ultotal); - * return 0; - * }; - * curl.perform(); - * ---- - */ - @property void onProgress(int delegate(size_t dlTotal, - size_t dlNow, - size_t ulTotal, - size_t ulNow) callback) - { - _onProgress = (size_t dlt, size_t dln, size_t ult, size_t uln) - { - throwOnStopped("Progress callback called on cleaned "~ - "up Curl instance"); - return callback(dlt, dln, ult, uln); - }; - set(CurlOption.noprogress, 0); - set(CurlOption.progressdata, cast(void*) &this); - set(CurlOption.progressfunction, cast(void*) &Curl._progressCallback); - } - - // Internal C callbacks to register with libcurl - extern (C) private static - size_t _receiveCallback(const char* str, - size_t size, size_t nmemb, void* ptr) - { - auto b = cast(Curl*) ptr; - if (b._onReceive != null) - return b._onReceive(cast(InData)(str[0 .. size*nmemb])); - return size*nmemb; - } - - extern (C) private static - size_t _receiveHeaderCallback(const char* str, - size_t size, size_t nmemb, void* ptr) - { - import std.string : chomp; - - auto b = cast(Curl*) ptr; - auto s = str[0 .. size*nmemb].chomp(); - if (b._onReceiveHeader != null) - b._onReceiveHeader(s); - - return size*nmemb; - } - - extern (C) private static - size_t _sendCallback(char *str, size_t size, size_t nmemb, void *ptr) - { - Curl* b = cast(Curl*) ptr; - auto a = cast(void[]) str[0 .. size*nmemb]; - if (b._onSend == null) - return 0; - return b._onSend(a); - } - - extern (C) private static - int _seekCallback(void *ptr, curl_off_t offset, int origin) - { - auto b = cast(Curl*) ptr; - if (b._onSeek == null) - return CurlSeek.cantseek; - - // origin: CurlSeekPos.set/current/end - // return: CurlSeek.ok/fail/cantseek - return b._onSeek(cast(long) offset, cast(CurlSeekPos) origin); - } - - extern (C) private static - int _socketOptionCallback(void *ptr, - curl_socket_t curlfd, curlsocktype purpose) - { - auto b = cast(Curl*) ptr; - if (b._onSocketOption == null) - return 0; - - // return: 0 ok, 1 fail - return b._onSocketOption(curlfd, cast(CurlSockType) purpose); - } - - extern (C) private static - int _progressCallback(void *ptr, - double dltotal, double dlnow, - double ultotal, double ulnow) - { - auto b = cast(Curl*) ptr; - if (b._onProgress == null) - return 0; - - // return: 0 ok, 1 fail - return b._onProgress(cast(size_t) dltotal, cast(size_t) dlnow, - cast(size_t) ultotal, cast(size_t) ulnow); - } - -} - -// Internal messages send between threads. -// The data is wrapped in this struct in order to ensure that -// other std.concurrency.receive calls does not pick up our messages -// by accident. -private struct CurlMessage(T) -{ - public T data; -} - -private static CurlMessage!T curlMessage(T)(T data) -{ - return CurlMessage!T(data); -} - -// Pool of to be used for reusing buffers -private struct Pool(Data) -{ - private struct Entry - { - Data data; - Entry* next; - } - private Entry* root; - private Entry* freeList; - - @safe @property bool empty() - { - return root == null; - } - - @safe nothrow void push(Data d) - { - if (freeList == null) - { - // Allocate new Entry since there is no one - // available in the freeList - freeList = new Entry; - } - freeList.data = d; - Entry* oldroot = root; - root = freeList; - freeList = freeList.next; - root.next = oldroot; - } - - @safe Data pop() - { - import std.exception : enforce; - enforce!Exception(root != null, "pop() called on empty pool"); - auto d = root.data; - auto n = root.next; - root.next = freeList; - freeList = root; - root = n; - return d; - } -} - -// Lazily-instantiated namespace to avoid importing std.concurrency until needed. -private struct _async() -{ -static: - // https://issues.dlang.org/show_bug.cgi?id=15831 - // this should be inside byLineAsync - // Range that reads one chunk at a time asynchronously. - private struct ChunkInputRange - { - import std.concurrency : Tid, send; - - private ubyte[] chunk; - mixin WorkerThreadProtocol!(ubyte, chunk); - - private Tid workerTid; - private State running; - - private this(Tid tid, size_t transmitBuffers, size_t chunkSize) - { - workerTid = tid; - state = State.needUnits; - - // Send buffers to other thread for it to use. Since no mechanism is in - // place for moving ownership a cast to shared is done here and a cast - // back to non-shared in the receiving end. - foreach (i ; 0 .. transmitBuffers) - { - ubyte[] arr = new ubyte[](chunkSize); - workerTid.send(cast(immutable(ubyte[]))arr); - } - } - } - - // https://issues.dlang.org/show_bug.cgi?id=15831 - // this should be inside byLineAsync - // Range that reads one line at a time asynchronously. - private static struct LineInputRange(Char) - { - private Char[] line; - mixin WorkerThreadProtocol!(Char, line); - - private Tid workerTid; - private State running; - - private this(Tid tid, size_t transmitBuffers, size_t bufferSize) - { - import std.concurrency : send; - - workerTid = tid; - state = State.needUnits; - - // Send buffers to other thread for it to use. Since no mechanism is in - // place for moving ownership a cast to shared is done here and casted - // back to non-shared in the receiving end. - foreach (i ; 0 .. transmitBuffers) - { - auto arr = new Char[](bufferSize); - workerTid.send(cast(immutable(Char[]))arr); - } - } - } - - import std.concurrency : Tid; - - // Shared function for reading incoming chunks of data and - // sending the to a parent thread - private size_t receiveChunks(ubyte[] data, ref ubyte[] outdata, - Pool!(ubyte[]) freeBuffers, - ref ubyte[] buffer, Tid fromTid, - ref bool aborted) - { - import std.concurrency : receive, send, thisTid; - - immutable datalen = data.length; - - // Copy data to fill active buffer - while (!data.empty) - { - - // Make sure a buffer is present - while ( outdata.empty && freeBuffers.empty) - { - // Active buffer is invalid and there are no - // available buffers in the pool. Wait for buffers - // to return from main thread in order to reuse - // them. - receive((immutable(ubyte)[] buf) - { - buffer = cast(ubyte[]) buf; - outdata = buffer[]; - }, - (bool flag) { aborted = true; } - ); - if (aborted) return cast(size_t) 0; - } - if (outdata.empty) - { - buffer = freeBuffers.pop(); - outdata = buffer[]; - } - - // Copy data - auto copyBytes = outdata.length < data.length ? - outdata.length : data.length; - - outdata[0 .. copyBytes] = data[0 .. copyBytes]; - outdata = outdata[copyBytes..$]; - data = data[copyBytes..$]; - - if (outdata.empty) - fromTid.send(thisTid, curlMessage(cast(immutable(ubyte)[])buffer)); - } - - return datalen; - } - - // ditto - private void finalizeChunks(ubyte[] outdata, ref ubyte[] buffer, - Tid fromTid) - { - import std.concurrency : send, thisTid; - if (!outdata.empty) - { - // Resize the last buffer - buffer.length = buffer.length - outdata.length; - fromTid.send(thisTid, curlMessage(cast(immutable(ubyte)[])buffer)); - } - } - - - // Shared function for reading incoming lines of data and sending the to a - // parent thread - private static size_t receiveLines(Terminator, Unit) - (const(ubyte)[] data, ref EncodingScheme encodingScheme, - bool keepTerminator, Terminator terminator, - ref const(ubyte)[] leftOverBytes, ref bool bufferValid, - ref Pool!(Unit[]) freeBuffers, ref Unit[] buffer, - Tid fromTid, ref bool aborted) - { - import std.concurrency : prioritySend, receive, send, thisTid; - import std.exception : enforce; - import std.format : format; - import std.traits : isArray; - - immutable datalen = data.length; - - // Terminator is specified and buffers should be resized as determined by - // the terminator - - // Copy data to active buffer until terminator is found. - - // Decode as many lines as possible - while (true) - { - - // Make sure a buffer is present - while (!bufferValid && freeBuffers.empty) - { - // Active buffer is invalid and there are no available buffers in - // the pool. Wait for buffers to return from main thread in order to - // reuse them. - receive((immutable(Unit)[] buf) - { - buffer = cast(Unit[]) buf; - buffer.length = 0; - buffer.assumeSafeAppend(); - bufferValid = true; - }, - (bool flag) { aborted = true; } - ); - if (aborted) return cast(size_t) 0; - } - if (!bufferValid) - { - buffer = freeBuffers.pop(); - bufferValid = true; - } - - // Try to read a line from left over bytes from last onReceive plus the - // newly received bytes. - try - { - if (decodeLineInto(leftOverBytes, data, buffer, - encodingScheme, terminator)) - { - if (keepTerminator) - { - fromTid.send(thisTid, - curlMessage(cast(immutable(Unit)[])buffer)); - } - else - { - static if (isArray!Terminator) - fromTid.send(thisTid, - curlMessage(cast(immutable(Unit)[]) - buffer[0..$-terminator.length])); - else - fromTid.send(thisTid, - curlMessage(cast(immutable(Unit)[]) - buffer[0..$-1])); - } - bufferValid = false; - } - else - { - // Could not decode an entire line. Save - // bytes left in data for next call to - // onReceive. Can be up to a max of 4 bytes. - enforce!CurlException(data.length <= 4, - format( - "Too many bytes left not decoded %s"~ - " > 4. Maybe the charset specified in"~ - " headers does not match "~ - "the actual content downloaded?", - data.length)); - leftOverBytes ~= data; - break; - } - } - catch (CurlException ex) - { - prioritySend(fromTid, cast(immutable(CurlException))ex); - return cast(size_t) 0; - } - } - return datalen; - } - - // ditto - private static - void finalizeLines(Unit)(bool bufferValid, Unit[] buffer, Tid fromTid) - { - import std.concurrency : send, thisTid; - if (bufferValid && buffer.length != 0) - fromTid.send(thisTid, curlMessage(cast(immutable(Unit)[])buffer[0..$])); - } - - /* Used by byLineAsync/byChunkAsync to duplicate an existing connection - * that can be used exclusively in a spawned thread. - */ - private void duplicateConnection(Conn, PostData) - (const(char)[] url, Conn conn, PostData postData, Tid tid) - { - import std.concurrency : send; - import std.exception : enforce; - - // no move semantic available in std.concurrency ie. must use casting. - auto connDup = conn.dup(); - connDup.url = url; - - static if ( is(Conn : HTTP) ) - { - connDup.p.headersOut = null; - connDup.method = conn.method == HTTP.Method.undefined ? - HTTP.Method.get : conn.method; - if (postData !is null) - { - if (connDup.method == HTTP.Method.put) - { - connDup.handle.set(CurlOption.infilesize_large, - postData.length); - } - else - { - // post - connDup.method = HTTP.Method.post; - connDup.handle.set(CurlOption.postfieldsize_large, - postData.length); - } - connDup.handle.set(CurlOption.copypostfields, - cast(void*) postData.ptr); - } - tid.send(cast(ulong) connDup.handle.handle); - tid.send(connDup.method); - } - else - { - enforce!CurlException(postData is null, - "Cannot put ftp data using byLineAsync()"); - tid.send(cast(ulong) connDup.handle.handle); - tid.send(HTTP.Method.undefined); - } - connDup.p.curl.handle = null; // make sure handle is not freed - } - - // Spawn a thread for handling the reading of incoming data in the - // background while the delegate is executing. This will optimize - // throughput by allowing simultaneous input (this struct) and - // output (e.g. AsyncHTTPLineOutputRange). - private static void spawn(Conn, Unit, Terminator = void)() - { - import std.concurrency : Tid, prioritySend, receiveOnly, send, thisTid; - import etc.c.curl : CURL, CurlError; - Tid fromTid = receiveOnly!Tid(); - - // Get buffer to read into - Pool!(Unit[]) freeBuffers; // Free list of buffer objects - - // Number of bytes filled into active buffer - Unit[] buffer; - bool aborted = false; - - EncodingScheme encodingScheme; - static if ( !is(Terminator == void)) - { - // Only lines reading will receive a terminator - const terminator = receiveOnly!Terminator(); - const keepTerminator = receiveOnly!bool(); - - // max number of bytes to carry over from an onReceive - // callback. This is 4 because it is the max code units to - // decode a code point in the supported encodings. - auto leftOverBytes = new const(ubyte)[4]; - leftOverBytes.length = 0; - auto bufferValid = false; - } - else - { - Unit[] outdata; - } - - // no move semantic available in std.concurrency ie. must use casting. - auto connDup = cast(CURL*) receiveOnly!ulong(); - auto client = Conn(); - client.p.curl.handle = connDup; - - // receive a method for both ftp and http but just use it for http - auto method = receiveOnly!(HTTP.Method)(); - - client.onReceive = (ubyte[] data) - { - // If no terminator is specified the chunk size is fixed. - static if ( is(Terminator == void) ) - return receiveChunks(data, outdata, freeBuffers, buffer, - fromTid, aborted); - else - return receiveLines(data, encodingScheme, - keepTerminator, terminator, leftOverBytes, - bufferValid, freeBuffers, buffer, - fromTid, aborted); - }; - - static if ( is(Conn == HTTP) ) - { - client.method = method; - // register dummy header handler - client.onReceiveHeader = (in char[] key, in char[] value) - { - if (key == "content-type") - encodingScheme = EncodingScheme.create(client.p.charset); - }; - } - else - { - encodingScheme = EncodingScheme.create(client.encoding); - } - - // Start the request - CurlCode code; - try - { - code = client.perform(No.throwOnError); - } - catch (Exception ex) - { - prioritySend(fromTid, cast(immutable(Exception)) ex); - fromTid.send(thisTid, curlMessage(true)); // signal done - return; - } - - if (code != CurlError.ok) - { - if (aborted && (code == CurlError.aborted_by_callback || - code == CurlError.write_error)) - { - fromTid.send(thisTid, curlMessage(true)); // signal done - return; - } - prioritySend(fromTid, cast(immutable(CurlException)) - new CurlException(client.p.curl.errorString(code))); - - fromTid.send(thisTid, curlMessage(true)); // signal done - return; - } - - // Send remaining data that is not a full chunk size - static if ( is(Terminator == void) ) - finalizeChunks(outdata, buffer, fromTid); - else - finalizeLines(bufferValid, buffer, fromTid); - - fromTid.send(thisTid, curlMessage(true)); // signal done - } -} diff --git a/phobos/std/net/isemail.d b/phobos/std/net/isemail.d deleted file mode 100644 index 2234f35..0000000 --- a/phobos/std/net/isemail.d +++ /dev/null @@ -1,1952 +0,0 @@ -/** - * Validates an email address according to RFCs 5321, 5322 and others. - * - * Authors: Dominic Sayers $(LT)dominic@sayers.cc$(GT), Jacob Carlborg - * Copyright: Dominic Sayers, Jacob Carlborg 2008-. - * Test schema documentation: Copyright Š 2011, Daniel Marschall - * License: $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost Software License 1.0) - * Dominic Sayers graciously granted permission to use the Boost license via email on Feb 22, 2011. - * Version: 3.0.13 - Version 3.0 of the original PHP implementation: $(LINK http://www.dominicsayers.com/isemail) - * - * Standards: - * $(UL - * $(LI RFC 5321) - * $(LI RFC 5322) - * ) - * - * References: - * $(UL - * $(LI $(LINK http://www.dominicsayers.com/isemail)) - * $(LI $(LINK http://tools.ietf.org/html/rfc5321)) - * $(LI $(LINK http://tools.ietf.org/html/rfc5322)) - * ) - * - * Source: $(PHOBOSSRC std/net/isemail.d) - */ -module std.net.isemail; - -import std.range.primitives : back, front, ElementType, popFront, popBack; -import std.traits; -import std.typecons : Flag, Yes, No; - -/** - * Check that an email address conforms to RFCs 5321, 5322 and others. - * - * Distinguishes between a Mailbox as defined by RFC 5321 and an addr-spec as - * defined by RFC 5322. Depending on the context, either can be regarded as a - * valid email address. - * - * Note: The DNS check is currently not implemented. - * - * Params: - * email = The email address to check - * checkDNS = If `Yes.checkDns` then a DNS check for MX records will be made - * errorLevel = Determines the boundary between valid and invalid addresses. - * Status codes above this number will be returned as-is, - * status codes below will be returned as EmailStatusCode.valid. - * Thus the calling program can simply look for EmailStatusCode.valid - * if it is only interested in whether an address is valid or not. The - * $(D_PARAM errorLevel) will determine how "picky" isEmail() is about - * the address. - * - * If omitted or passed as EmailStatusCode.none then isEmail() will - * not perform any finer grained error checking and an address is - * either considered valid or not. Email status code will either be - * EmailStatusCode.valid or EmailStatusCode.error. - * - * Returns: - * An $(LREF EmailStatus), indicating the status of the email address. - */ -EmailStatus isEmail(Char)(const(Char)[] email, CheckDns checkDNS = No.checkDns, -EmailStatusCode errorLevel = EmailStatusCode.none) -if (isSomeChar!(Char)) -{ - import std.algorithm.iteration : uniq, filter, map; - import std.algorithm.searching : canFind, maxElement; - import std.array : array, split; - import std.conv : to; - import std.exception : enforce; - import std.string : indexOf, lastIndexOf; - import std.uni : isNumber; - - alias tstring = const(Char)[]; - alias Token = TokenImpl!(Char); - - enum defaultThreshold = 16; - int threshold; - bool diagnose; - - if (errorLevel == EmailStatusCode.any) - { - threshold = EmailStatusCode.valid; - diagnose = true; - } - - else if (errorLevel == EmailStatusCode.none) - threshold = defaultThreshold; - - else - { - diagnose = true; - - switch (errorLevel) - { - case EmailStatusCode.warning: threshold = defaultThreshold; break; - case EmailStatusCode.error: threshold = EmailStatusCode.valid; break; - default: threshold = errorLevel; - } - } - - auto returnStatus = [EmailStatusCode.valid]; - auto context = EmailPart.componentLocalPart; - auto contextStack = [context]; - auto contextPrior = context; - tstring token = ""; - tstring tokenPrior = ""; - tstring[EmailPart] parseData = [EmailPart.componentLocalPart : "", EmailPart.componentDomain : ""]; - tstring[][EmailPart] atomList = [EmailPart.componentLocalPart : [""], EmailPart.componentDomain : [""]]; - auto elementCount = 0; - auto elementLength = 0; - auto hyphenFlag = false; - auto endOrDie = false; - auto crlfCount = int.min; // int.min == not defined - - for (size_t i; i < email.length; i++) - { - auto e = email[i]; - token = email.get(i, e); - - switch (context) - { - case EmailPart.componentLocalPart: - switch (token) - { - case Token.openParenthesis: - if (elementLength == 0) - returnStatus ~= elementCount == 0 ? EmailStatusCode.comment : - EmailStatusCode.deprecatedComment; - - else - { - returnStatus ~= EmailStatusCode.comment; - endOrDie = true; - } - - contextStack ~= context; - context = EmailPart.contextComment; - break; - - case Token.dot: - if (elementLength == 0) - returnStatus ~= elementCount == 0 ? EmailStatusCode.errorDotStart : - EmailStatusCode.errorConsecutiveDots; - - else - { - if (endOrDie) - returnStatus ~= EmailStatusCode.deprecatedLocalPart; - } - - endOrDie = false; - elementLength = 0; - elementCount++; - parseData[EmailPart.componentLocalPart] ~= token; - - if (elementCount >= atomList[EmailPart.componentLocalPart].length) - atomList[EmailPart.componentLocalPart] ~= ""; - - else - atomList[EmailPart.componentLocalPart][elementCount] = ""; - break; - - case Token.doubleQuote: - if (elementLength == 0) - { - returnStatus ~= elementCount == 0 ? EmailStatusCode.rfc5321QuotedString : - EmailStatusCode.deprecatedLocalPart; - - parseData[EmailPart.componentLocalPart] ~= token; - atomList[EmailPart.componentLocalPart][elementCount] ~= token; - elementLength++; - endOrDie = true; - contextStack ~= context; - context = EmailPart.contextQuotedString; - } - - else - returnStatus ~= EmailStatusCode.errorExpectingText; - break; - - case Token.cr: - case Token.space: - case Token.tab: - if ((token == Token.cr) && ((++i == email.length) || (email.get(i, e) != Token.lf))) - { - returnStatus ~= EmailStatusCode.errorCrNoLf; - break; - } - - if (elementLength == 0) - returnStatus ~= elementCount == 0 ? EmailStatusCode.foldingWhitespace : - EmailStatusCode.deprecatedFoldingWhitespace; - - else - endOrDie = true; - - contextStack ~= context; - context = EmailPart.contextFoldingWhitespace; - tokenPrior = token; - break; - - case Token.at: - enforce(contextStack.length == 1, "Unexpected item on context stack"); - - if (parseData[EmailPart.componentLocalPart] == "") - returnStatus ~= EmailStatusCode.errorNoLocalPart; - - else if (elementLength == 0) - returnStatus ~= EmailStatusCode.errorDotEnd; - - else if (parseData[EmailPart.componentLocalPart].length > 64) - returnStatus ~= EmailStatusCode.rfc5322LocalTooLong; - - else if (contextPrior == EmailPart.contextComment || - contextPrior == EmailPart.contextFoldingWhitespace) - returnStatus ~= EmailStatusCode.deprecatedCommentFoldingWhitespaceNearAt; - - context = EmailPart.componentDomain; - contextStack = [context]; - elementCount = 0; - elementLength = 0; - endOrDie = false; - break; - - default: - if (endOrDie) - { - switch (contextPrior) - { - case EmailPart.contextComment: - case EmailPart.contextFoldingWhitespace: - returnStatus ~= EmailStatusCode.errorTextAfterCommentFoldingWhitespace; - break; - - case EmailPart.contextQuotedString: - returnStatus ~= EmailStatusCode.errorTextAfterQuotedString; - break; - - default: - throw new Exception("More text found where none is allowed, but " - ~"unrecognised prior context: " ~ to!(string)(contextPrior)); - } - } - - else - { - contextPrior = context; - immutable c = token.front; - - if (c < '!' || c > '~' || c == '\n' || Token.specials.canFind(token)) - returnStatus ~= EmailStatusCode.errorExpectingText; - - parseData[EmailPart.componentLocalPart] ~= token; - atomList[EmailPart.componentLocalPart][elementCount] ~= token; - elementLength++; - } - } - break; - - case EmailPart.componentDomain: - switch (token) - { - case Token.openParenthesis: - if (elementLength == 0) - { - returnStatus ~= elementCount == 0 ? - EmailStatusCode.deprecatedCommentFoldingWhitespaceNearAt - : EmailStatusCode.deprecatedComment; - } - else - { - returnStatus ~= EmailStatusCode.comment; - endOrDie = true; - } - - contextStack ~= context; - context = EmailPart.contextComment; - break; - - case Token.dot: - if (elementLength == 0) - returnStatus ~= elementCount == 0 ? EmailStatusCode.errorDotStart : - EmailStatusCode.errorConsecutiveDots; - - else if (hyphenFlag) - returnStatus ~= EmailStatusCode.errorDomainHyphenEnd; - - else - { - if (elementLength > 63) - returnStatus ~= EmailStatusCode.rfc5322LabelTooLong; - } - - endOrDie = false; - elementLength = 0; - elementCount++; - - //atomList[EmailPart.componentDomain][elementCount] = ""; - atomList[EmailPart.componentDomain] ~= ""; - parseData[EmailPart.componentDomain] ~= token; - break; - - case Token.openBracket: - if (parseData[EmailPart.componentDomain] == "") - { - endOrDie = true; - elementLength++; - contextStack ~= context; - context = EmailPart.componentLiteral; - parseData[EmailPart.componentDomain] ~= token; - atomList[EmailPart.componentDomain][elementCount] ~= token; - parseData[EmailPart.componentLiteral] = ""; - } - - else - returnStatus ~= EmailStatusCode.errorExpectingText; - break; - - case Token.cr: - case Token.space: - case Token.tab: - if (token == Token.cr && (++i == email.length || email.get(i, e) != Token.lf)) - { - returnStatus ~= EmailStatusCode.errorCrNoLf; - break; - } - - if (elementLength == 0) - { - returnStatus ~= elementCount == 0 ? - EmailStatusCode.deprecatedCommentFoldingWhitespaceNearAt - : EmailStatusCode.deprecatedFoldingWhitespace; - } - else - { - returnStatus ~= EmailStatusCode.foldingWhitespace; - endOrDie = true; - } - - contextStack ~= context; - context = EmailPart.contextFoldingWhitespace; - tokenPrior = token; - break; - - default: - if (endOrDie) - { - switch (contextPrior) - { - case EmailPart.contextComment: - case EmailPart.contextFoldingWhitespace: - returnStatus ~= EmailStatusCode.errorTextAfterCommentFoldingWhitespace; - break; - - case EmailPart.componentLiteral: - returnStatus ~= EmailStatusCode.errorTextAfterDomainLiteral; - break; - - default: - throw new Exception("More text found where none is allowed, but " - ~"unrecognised prior context: " ~ to!(string)(contextPrior)); - } - - } - - immutable c = token.front; - hyphenFlag = false; - - if (c < '!' || c > '~' || Token.specials.canFind(token)) - returnStatus ~= EmailStatusCode.errorExpectingText; - - else if (token == Token.hyphen) - { - if (elementLength == 0) - returnStatus ~= EmailStatusCode.errorDomainHyphenStart; - - hyphenFlag = true; - } - - else if (!((c > '/' && c < ':') || (c > '@' && c < '[') || (c > '`' && c < '{'))) - returnStatus ~= EmailStatusCode.rfc5322Domain; - - parseData[EmailPart.componentDomain] ~= token; - atomList[EmailPart.componentDomain][elementCount] ~= token; - elementLength++; - } - break; - - case EmailPart.componentLiteral: - switch (token) - { - case Token.closeBracket: - if (returnStatus.maxElement() < EmailStatusCode.deprecated_) - { - auto maxGroups = 8; - size_t index = -1; - auto addressLiteral = parseData[EmailPart.componentLiteral]; - const(Char)[] ipSuffix = matchIPSuffix(addressLiteral); - - if (ipSuffix.length) - { - index = addressLiteral.length - ipSuffix.length; - if (index != 0) - addressLiteral = addressLiteral[0 .. index] ~ "0:0"; - } - - if (index == 0) - returnStatus ~= EmailStatusCode.rfc5321AddressLiteral; - - else if (addressLiteral.compareFirstN(Token.ipV6Tag, 5)) - returnStatus ~= EmailStatusCode.rfc5322DomainLiteral; - - else - { - auto ipV6 = addressLiteral[5 .. $]; - auto matchesIp = ipV6.split(Token.colon); - immutable groupCount = matchesIp.length; - index = ipV6.indexOf(Token.doubleColon); - - if (index == -1) - { - if (groupCount != maxGroups) - returnStatus ~= EmailStatusCode.rfc5322IpV6GroupCount; - } - - else - { - if (index != ipV6.lastIndexOf(Token.doubleColon)) - returnStatus ~= EmailStatusCode.rfc5322IpV6TooManyDoubleColons; - - else - { - if (index == 0 || index == (ipV6.length - 2)) - maxGroups++; - - if (groupCount > maxGroups) - returnStatus ~= EmailStatusCode.rfc5322IpV6MaxGroups; - - else if (groupCount == maxGroups) - returnStatus ~= EmailStatusCode.rfc5321IpV6Deprecated; - } - } - - if (ipV6[0 .. 1] == Token.colon && ipV6[1 .. 2] != Token.colon) - returnStatus ~= EmailStatusCode.rfc5322IpV6ColonStart; - - else if (ipV6[$ - 1 .. $] == Token.colon && ipV6[$ - 2 .. $ - 1] != Token.colon) - returnStatus ~= EmailStatusCode.rfc5322IpV6ColonEnd; - - else if (!matchesIp - .filter!(a => !isUpToFourHexChars(a)) - .empty) - returnStatus ~= EmailStatusCode.rfc5322IpV6BadChar; - - else - returnStatus ~= EmailStatusCode.rfc5321AddressLiteral; - } - } - - else - returnStatus ~= EmailStatusCode.rfc5322DomainLiteral; - - parseData[EmailPart.componentDomain] ~= token; - atomList[EmailPart.componentDomain][elementCount] ~= token; - elementLength++; - contextPrior = context; - context = contextStack.pop(); - break; - - case Token.backslash: - returnStatus ~= EmailStatusCode.rfc5322DomainLiteralObsoleteText; - contextStack ~= context; - context = EmailPart.contextQuotedPair; - break; - - case Token.cr: - case Token.space: - case Token.tab: - if (token == Token.cr && (++i == email.length || email.get(i, e) != Token.lf)) - { - returnStatus ~= EmailStatusCode.errorCrNoLf; - break; - } - - returnStatus ~= EmailStatusCode.foldingWhitespace; - contextStack ~= context; - context = EmailPart.contextFoldingWhitespace; - tokenPrior = token; - break; - - default: - immutable c = token.front; - - if (c > AsciiToken.delete_ || c == '\0' || token == Token.openBracket) - { - returnStatus ~= EmailStatusCode.errorExpectingDomainText; - break; - } - - else if (c < '!' || c == AsciiToken.delete_ ) - returnStatus ~= EmailStatusCode.rfc5322DomainLiteralObsoleteText; - - parseData[EmailPart.componentLiteral] ~= token; - parseData[EmailPart.componentDomain] ~= token; - atomList[EmailPart.componentDomain][elementCount] ~= token; - elementLength++; - } - break; - - case EmailPart.contextQuotedString: - switch (token) - { - case Token.backslash: - contextStack ~= context; - context = EmailPart.contextQuotedPair; - break; - - case Token.cr: - case Token.tab: - if (token == Token.cr && (++i == email.length || email.get(i, e) != Token.lf)) - { - returnStatus ~= EmailStatusCode.errorCrNoLf; - break; - } - - parseData[EmailPart.componentLocalPart] ~= Token.space; - atomList[EmailPart.componentLocalPart][elementCount] ~= Token.space; - elementLength++; - - returnStatus ~= EmailStatusCode.foldingWhitespace; - contextStack ~= context; - context = EmailPart.contextFoldingWhitespace; - tokenPrior = token; - break; - - case Token.doubleQuote: - parseData[EmailPart.componentLocalPart] ~= token; - atomList[EmailPart.componentLocalPart][elementCount] ~= token; - elementLength++; - contextPrior = context; - context = contextStack.pop(); - break; - - default: - immutable c = token.front; - - if (c > AsciiToken.delete_ || c == '\0' || c == '\n') - returnStatus ~= EmailStatusCode.errorExpectingQuotedText; - - else if (c < ' ' || c == AsciiToken.delete_) - returnStatus ~= EmailStatusCode.deprecatedQuotedText; - - parseData[EmailPart.componentLocalPart] ~= token; - atomList[EmailPart.componentLocalPart][elementCount] ~= token; - elementLength++; - } - break; - - case EmailPart.contextQuotedPair: - immutable c = token.front; - - if (c > AsciiToken.delete_) - returnStatus ~= EmailStatusCode.errorExpectingQuotedPair; - - else if (c < AsciiToken.unitSeparator && c != AsciiToken.horizontalTab || c == AsciiToken.delete_) - returnStatus ~= EmailStatusCode.deprecatedQuotedPair; - - contextPrior = context; - context = contextStack.pop(); - token = Token.backslash ~ token; - - switch (context) - { - case EmailPart.contextComment: break; - - case EmailPart.contextQuotedString: - parseData[EmailPart.componentLocalPart] ~= token; - atomList[EmailPart.componentLocalPart][elementCount] ~= token; - elementLength += 2; - break; - - case EmailPart.componentLiteral: - parseData[EmailPart.componentDomain] ~= token; - atomList[EmailPart.componentDomain][elementCount] ~= token; - elementLength += 2; - break; - - default: - throw new Exception("Quoted pair logic invoked in an invalid context: " ~ to!(string)(context)); - } - break; - - case EmailPart.contextComment: - switch (token) - { - case Token.openParenthesis: - contextStack ~= context; - context = EmailPart.contextComment; - break; - - case Token.closeParenthesis: - contextPrior = context; - context = contextStack.pop(); - break; - - case Token.backslash: - contextStack ~= context; - context = EmailPart.contextQuotedPair; - break; - - case Token.cr: - case Token.space: - case Token.tab: - if (token == Token.cr && (++i == email.length || email.get(i, e) != Token.lf)) - { - returnStatus ~= EmailStatusCode.errorCrNoLf; - break; - } - - returnStatus ~= EmailStatusCode.foldingWhitespace; - - contextStack ~= context; - context = EmailPart.contextFoldingWhitespace; - tokenPrior = token; - break; - - default: - immutable c = token.front; - - if (c > AsciiToken.delete_ || c == '\0' || c == '\n') - { - returnStatus ~= EmailStatusCode.errorExpectingCommentText; - break; - } - - else if (c < ' ' || c == AsciiToken.delete_) - returnStatus ~= EmailStatusCode.deprecatedCommentText; - } - break; - - case EmailPart.contextFoldingWhitespace: - if (tokenPrior == Token.cr) - { - if (token == Token.cr) - { - returnStatus ~= EmailStatusCode.errorFoldingWhitespaceCrflX2; - break; - } - - if (crlfCount != int.min) // int.min == not defined - { - if (++crlfCount > 1) - returnStatus ~= EmailStatusCode.deprecatedFoldingWhitespace; - } - - else - crlfCount = 1; - } - - switch (token) - { - case Token.cr: - if (++i == email.length || email.get(i, e) != Token.lf) - returnStatus ~= EmailStatusCode.errorCrNoLf; - break; - - case Token.space: - case Token.tab: - break; - - default: - if (tokenPrior == Token.cr) - { - returnStatus ~= EmailStatusCode.errorFoldingWhitespaceCrLfEnd; - break; - } - - crlfCount = int.min; // int.min == not defined - contextPrior = context; - context = contextStack.pop(); - i--; - break; - } - - tokenPrior = token; - break; - - default: - throw new Exception("Unkown context: " ~ to!(string)(context)); - } - - if (returnStatus.maxElement() > EmailStatusCode.rfc5322) - break; - } - - if (returnStatus.maxElement() < EmailStatusCode.rfc5322) - { - if (context == EmailPart.contextQuotedString) - returnStatus ~= EmailStatusCode.errorUnclosedQuotedString; - - else if (context == EmailPart.contextQuotedPair) - returnStatus ~= EmailStatusCode.errorBackslashEnd; - - else if (context == EmailPart.contextComment) - returnStatus ~= EmailStatusCode.errorUnclosedComment; - - else if (context == EmailPart.componentLiteral) - returnStatus ~= EmailStatusCode.errorUnclosedDomainLiteral; - - else if (token == Token.cr) - returnStatus ~= EmailStatusCode.errorFoldingWhitespaceCrLfEnd; - - else if (parseData[EmailPart.componentDomain] == "") - returnStatus ~= EmailStatusCode.errorNoDomain; - - else if (elementLength == 0) - returnStatus ~= EmailStatusCode.errorDotEnd; - - else if (hyphenFlag) - returnStatus ~= EmailStatusCode.errorDomainHyphenEnd; - - else if (parseData[EmailPart.componentDomain].length > 255) - returnStatus ~= EmailStatusCode.rfc5322DomainTooLong; - - else if ((parseData[EmailPart.componentLocalPart] ~ Token.at ~ parseData[EmailPart.componentDomain]).length > - 254) - returnStatus ~= EmailStatusCode.rfc5322TooLong; - - else if (elementLength > 63) - returnStatus ~= EmailStatusCode.rfc5322LabelTooLong; - } - - auto dnsChecked = false; - - if (checkDNS == Yes.checkDns && returnStatus.maxElement() < EmailStatusCode.dnsWarning) - { - assert(false, "DNS check is currently not implemented"); - } - - if (!dnsChecked && returnStatus.maxElement() < EmailStatusCode.dnsWarning) - { - if (elementCount == 0) - returnStatus ~= EmailStatusCode.rfc5321TopLevelDomain; - - if (isNumber(atomList[EmailPart.componentDomain][elementCount].front)) - returnStatus ~= EmailStatusCode.rfc5321TopLevelDomainNumeric; - } - - returnStatus = array(uniq(returnStatus)); - auto finalStatus = returnStatus.maxElement(); - - if (returnStatus.length != 1) - returnStatus.popFront(); - - parseData[EmailPart.status] = to!(tstring)(returnStatus); - - if (finalStatus < threshold) - finalStatus = EmailStatusCode.valid; - - if (!diagnose) - finalStatus = finalStatus < threshold ? EmailStatusCode.valid : EmailStatusCode.error; - - auto valid = finalStatus == EmailStatusCode.valid; - tstring localPart = ""; - tstring domainPart = ""; - - if (auto value = EmailPart.componentLocalPart in parseData) - localPart = *value; - - if (auto value = EmailPart.componentDomain in parseData) - domainPart = *value; - - return EmailStatus(valid, to!(string)(localPart), to!(string)(domainPart), finalStatus); -} - -@safe unittest -{ - assert(`test.test@iana.org`.isEmail(No.checkDns).statusCode == EmailStatusCode.valid); - assert(`test.test@iana.org`.isEmail(No.checkDns, EmailStatusCode.none).statusCode == EmailStatusCode.valid); - - assert(`test@[IPv6:1111:2222:3333:4444:5555:6666::8888]`.isEmail(No.checkDns, - EmailStatusCode.none).statusCode == EmailStatusCode.valid); - - assert(`test`.isEmail(No.checkDns, EmailStatusCode.none).statusCode == EmailStatusCode.error); - assert(`(comment)test@iana.org`.isEmail(No.checkDns, EmailStatusCode.none).statusCode == EmailStatusCode.error); - - assert(``.isEmail(No.checkDns, EmailStatusCode.any).statusCode == EmailStatusCode.errorNoDomain); - assert(`test`.isEmail(No.checkDns, EmailStatusCode.any).statusCode == EmailStatusCode.errorNoDomain); - assert(`@`.isEmail(No.checkDns, EmailStatusCode.any).statusCode == EmailStatusCode.errorNoLocalPart); - assert(`test@`.isEmail(No.checkDns, EmailStatusCode.any).statusCode == EmailStatusCode.errorNoDomain); - - // assert(`test@io`.isEmail(No.checkDns, EmailStatusCode.any).statusCode == EmailStatusCode.valid, - // `io. currently has an MX-record (Feb 2011). Some DNS setups seem to find it, some don't.` - // ` If you don't see the MX for io. then try setting your DNS server to 8.8.8.8 (the Google DNS server)`); - - assert(`@io`.isEmail(No.checkDns, EmailStatusCode.any).statusCode == EmailStatusCode.errorNoLocalPart, - `io. currently has an MX-record (Feb 2011)`); - - assert(`@iana.org`.isEmail(No.checkDns, EmailStatusCode.any).statusCode == EmailStatusCode.errorNoLocalPart); - assert(`test@iana.org`.isEmail(No.checkDns, EmailStatusCode.any).statusCode == EmailStatusCode.valid); - assert(`test@nominet.org.uk`.isEmail(No.checkDns, EmailStatusCode.any).statusCode == EmailStatusCode.valid); - assert(`test@about.museum`.isEmail(No.checkDns, EmailStatusCode.any).statusCode == EmailStatusCode.valid); - assert(`a@iana.org`.isEmail(No.checkDns, EmailStatusCode.any).statusCode == EmailStatusCode.valid); - - //assert(`test@e.com`.isEmail(No.checkDns, EmailStatusCode.any).statusCode == EmailStatusCode.dnsWarningNoRecord); - // DNS check is currently not implemented - - //assert(`test@iana.a`.isEmail(No.checkDns, EmailStatusCode.any).statusCode == EmailStatusCode.dnsWarningNoRecord); - // DNS check is currently not implemented - - assert(`test.test@iana.org`.isEmail(No.checkDns, EmailStatusCode.any).statusCode == EmailStatusCode.valid); - assert(`.test@iana.org`.isEmail(No.checkDns, EmailStatusCode.any).statusCode == EmailStatusCode.errorDotStart); - assert(`test.@iana.org`.isEmail(No.checkDns, EmailStatusCode.any).statusCode == EmailStatusCode.errorDotEnd); - - assert(`test .. iana.org`.isEmail(No.checkDns, EmailStatusCode.any).statusCode == - EmailStatusCode.errorConsecutiveDots); - - assert(`test_exa-mple.com`.isEmail(No.checkDns, EmailStatusCode.any).statusCode == EmailStatusCode.errorNoDomain); - assert("!#$%&`*+/=?^`{|}~@iana.org".isEmail(No.checkDns, EmailStatusCode.any).statusCode == EmailStatusCode.valid); - - assert(`test\@test@iana.org`.isEmail(No.checkDns, EmailStatusCode.any).statusCode == - EmailStatusCode.errorExpectingText); - - assert(`123@iana.org`.isEmail(No.checkDns, EmailStatusCode.any).statusCode == EmailStatusCode.valid); - assert(`test@123.com`.isEmail(No.checkDns, EmailStatusCode.any).statusCode == EmailStatusCode.valid); - - assert(`test@iana.123`.isEmail(No.checkDns, EmailStatusCode.any).statusCode == - EmailStatusCode.rfc5321TopLevelDomainNumeric); - assert(`test@255.255.255.255`.isEmail(No.checkDns, EmailStatusCode.any).statusCode == - EmailStatusCode.rfc5321TopLevelDomainNumeric); - - assert(`abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghiklm@iana.org`.isEmail(No.checkDns, - EmailStatusCode.any).statusCode == EmailStatusCode.valid); - - assert(`abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghiklmn@iana.org`.isEmail(No.checkDns, - EmailStatusCode.any).statusCode == EmailStatusCode.rfc5322LocalTooLong); - - // assert(`test@abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghikl.com`.isEmail(No.checkDns, - // EmailStatusCode.any).statusCode == EmailStatusCode.dnsWarningNoRecord); - // DNS check is currently not implemented - - assert(`test@abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghiklm.com`.isEmail(No.checkDns, - EmailStatusCode.any).statusCode == EmailStatusCode.rfc5322LabelTooLong); - - assert(`test@mason-dixon.com`.isEmail(No.checkDns, EmailStatusCode.any).statusCode == EmailStatusCode.valid); - - assert(`test@-iana.org`.isEmail(No.checkDns, EmailStatusCode.any).statusCode == - EmailStatusCode.errorDomainHyphenStart); - - assert(`test@iana-.com`.isEmail(No.checkDns, EmailStatusCode.any).statusCode == - EmailStatusCode.errorDomainHyphenEnd); - - assert(`test@g--a.com`.isEmail(No.checkDns, EmailStatusCode.any).statusCode == EmailStatusCode.valid); - - //assert(`test@iana.co-uk`.isEmail(No.checkDns, EmailStatusCode.any).statusCode == - //EmailStatusCode.dnsWarningNoRecord); // DNS check is currently not implemented - - assert(`test@.iana.org`.isEmail(No.checkDns, EmailStatusCode.any).statusCode == EmailStatusCode.errorDotStart); - assert(`test@iana.org.`.isEmail(No.checkDns, EmailStatusCode.any).statusCode == EmailStatusCode.errorDotEnd); - assert(`test@iana .. com`.isEmail(No.checkDns, EmailStatusCode.any).statusCode == - EmailStatusCode.errorConsecutiveDots); - - //assert(`a@a.b.c.d.e.f.g.h.i.j.k.l.m.n.o.p.q.r.s.t.u.v.w.x.y.z.a.b.c.d.e.f.g.h.i.j.k.l.m.n.o.p.q.r.s.t.u.v.w.x.y.z` - // `.a.b.c.d.e.f.g.h.i.j.k.l.m.n.o.p.q.r.s.t.u.v.w.x.y.z.a.b.c.d.e.f.g.h.i.j.k.l.m.n.o.p.q.r.s.t.u.v.w.x.y.z` - // `.a.b.c.d.e.f.g.h.i.j.k.l.m.n.o.p.q.r.s.t.u.v`.isEmail(No.checkDns, EmailStatusCode.any).statusCode == - // EmailStatusCode.dnsWarningNoRecord); // DNS check is currently not implemented - - // assert(`abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghiklm@abcdefghijklmnopqrstuvwxyz` - // `abcdefghijklmnopqrstuvwxyzabcdefghikl.abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghikl.` - // `abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghi`.isEmail(No.checkDns, - // EmailStatusCode.any).statusCode == EmailStatusCode.dnsWarningNoRecord); - // DNS check is currently not implemented - - assert((`abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghiklm@abcdefghijklmnopqrstuvwxyz`~ - `abcdefghijklmnopqrstuvwxyzabcdefghikl.abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghikl.`~ - `abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghij`).isEmail(No.checkDns, - EmailStatusCode.any).statusCode == EmailStatusCode.rfc5322TooLong); - - assert((`a@abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghikl.abcdefghijklmnopqrstuvwxyz`~ - `abcdefghijklmnopqrstuvwxyzabcdefghikl.abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghikl.`~ - `abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefg.hij`).isEmail(No.checkDns, - EmailStatusCode.any).statusCode == EmailStatusCode.rfc5322TooLong); - - assert((`a@abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghikl.abcdefghijklmnopqrstuvwxyz`~ - `abcdefghijklmnopqrstuvwxyzabcdefghikl.abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghikl.`~ - `abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefg.hijk`).isEmail(No.checkDns, - EmailStatusCode.any).statusCode == EmailStatusCode.rfc5322DomainTooLong); - - assert(`"test"@iana.org`.isEmail(No.checkDns, EmailStatusCode.any).statusCode == - EmailStatusCode.rfc5321QuotedString); - - assert(`""@iana.org`.isEmail(No.checkDns, EmailStatusCode.any).statusCode == EmailStatusCode.rfc5321QuotedString); - assert(`"""@iana.org`.isEmail(No.checkDns, EmailStatusCode.any).statusCode == EmailStatusCode.errorExpectingText); - assert(`"\a"@iana.org`.isEmail(No.checkDns, EmailStatusCode.any).statusCode == EmailStatusCode.rfc5321QuotedString); - assert(`"\""@iana.org`.isEmail(No.checkDns, EmailStatusCode.any).statusCode == EmailStatusCode.rfc5321QuotedString); - - assert(`"\"@iana.org`.isEmail(No.checkDns, EmailStatusCode.any).statusCode == - EmailStatusCode.errorUnclosedQuotedString); - - assert(`"\\"@iana.org`.isEmail(No.checkDns, EmailStatusCode.any).statusCode == EmailStatusCode.rfc5321QuotedString); - assert(`test"@iana.org`.isEmail(No.checkDns, EmailStatusCode.any).statusCode == EmailStatusCode.errorExpectingText); - - assert(`"test@iana.org`.isEmail(No.checkDns, EmailStatusCode.any).statusCode == - EmailStatusCode.errorUnclosedQuotedString); - - assert(`"test"test@iana.org`.isEmail(No.checkDns, EmailStatusCode.any).statusCode == - EmailStatusCode.errorTextAfterQuotedString); - - assert(`test"text"@iana.org`.isEmail(No.checkDns, EmailStatusCode.any).statusCode == - EmailStatusCode.errorExpectingText); - - assert(`"test""test"@iana.org`.isEmail(No.checkDns, EmailStatusCode.any).statusCode == - EmailStatusCode.errorExpectingText); - - assert(`"test"."test"@iana.org`.isEmail(No.checkDns, EmailStatusCode.any).statusCode == - EmailStatusCode.deprecatedLocalPart); - - assert(`"test\ test"@iana.org`.isEmail(No.checkDns, EmailStatusCode.any).statusCode == - EmailStatusCode.rfc5321QuotedString); - - assert(`"test".test@iana.org`.isEmail(No.checkDns, EmailStatusCode.any).statusCode == - EmailStatusCode.deprecatedLocalPart); - - assert("\"test\u0000\"@iana.org".isEmail(No.checkDns, EmailStatusCode.any).statusCode == - EmailStatusCode.errorExpectingQuotedText); - - assert("\"test\\\u0000\"@iana.org".isEmail(No.checkDns, EmailStatusCode.any).statusCode == - EmailStatusCode.deprecatedQuotedPair); - - assert(`"abcdefghijklmnopqrstuvwxyz abcdefghijklmnopqrstuvwxyz abcdefghj"@iana.org`.isEmail(No.checkDns, - EmailStatusCode.any).statusCode == EmailStatusCode.rfc5322LocalTooLong, - `Quotes are still part of the length restriction`); - - assert(`"abcdefghijklmnopqrstuvwxyz abcdefghijklmnopqrstuvwxyz abcdefg\h"@iana.org`.isEmail(No.checkDns, - EmailStatusCode.any).statusCode == EmailStatusCode.rfc5322LocalTooLong, - `Quoted pair is still part of the length restriction`); - - assert(`test@[255.255.255.255]`.isEmail(No.checkDns, EmailStatusCode.any).statusCode == - EmailStatusCode.rfc5321AddressLiteral); - - assert(`test@a[255.255.255.255]`.isEmail(No.checkDns, EmailStatusCode.any).statusCode == - EmailStatusCode.errorExpectingText); - - assert(`test@[255.255.255]`.isEmail(No.checkDns, EmailStatusCode.any).statusCode == - EmailStatusCode.rfc5322DomainLiteral); - - assert(`test@[255.255.255.255.255]`.isEmail(No.checkDns, EmailStatusCode.any).statusCode == - EmailStatusCode.rfc5322DomainLiteral); - - assert(`test@[255.255.255.256]`.isEmail(No.checkDns, EmailStatusCode.any).statusCode == - EmailStatusCode.rfc5322DomainLiteral); - - assert(`test@[1111:2222:3333:4444:5555:6666:7777:8888]`.isEmail(No.checkDns, EmailStatusCode.any).statusCode == - EmailStatusCode.rfc5322DomainLiteral); - - assert(`test@[IPv6:1111:2222:3333:4444:5555:6666:7777]`.isEmail(No.checkDns, EmailStatusCode.any).statusCode == - EmailStatusCode.rfc5322IpV6GroupCount); - - assert(`test@[IPv6:1111:2222:3333:4444:5555:6666:7777:8888]`.isEmail(No.checkDns, EmailStatusCode.any).statusCode - == EmailStatusCode.rfc5321AddressLiteral); - - assert(`test@[IPv6:1111:2222:3333:4444:5555:6666:7777:8888:9999]`.isEmail(No.checkDns, - EmailStatusCode.any).statusCode == EmailStatusCode.rfc5322IpV6GroupCount); - - assert(`test@[IPv6:1111:2222:3333:4444:5555:6666:7777:888G]`.isEmail(No.checkDns, - EmailStatusCode.any).statusCode == EmailStatusCode.rfc5322IpV6BadChar); - - assert(`test@[IPv6:1111:2222:3333:4444:5555:6666::8888]`.isEmail(No.checkDns, - EmailStatusCode.any).statusCode == EmailStatusCode.rfc5321IpV6Deprecated); - - assert(`test@[IPv6:1111:2222:3333:4444:5555::8888]`.isEmail(No.checkDns, EmailStatusCode.any).statusCode == - EmailStatusCode.rfc5321AddressLiteral); - - assert(`test@[IPv6:1111:2222:3333:4444:5555:6666::7777:8888]`.isEmail(No.checkDns, - EmailStatusCode.any).statusCode == EmailStatusCode.rfc5322IpV6MaxGroups); - - assert(`test@[IPv6::3333:4444:5555:6666:7777:8888]`.isEmail(No.checkDns, EmailStatusCode.any).statusCode == - EmailStatusCode.rfc5322IpV6ColonStart); - - assert(`test@[IPv6:::3333:4444:5555:6666:7777:8888]`.isEmail(No.checkDns, EmailStatusCode.any).statusCode == - EmailStatusCode.rfc5321AddressLiteral); - - assert(`test@[IPv6:1111::4444:5555::8888]`.isEmail(No.checkDns, EmailStatusCode.any).statusCode == - EmailStatusCode.rfc5322IpV6TooManyDoubleColons); - - assert(`test@[IPv6:::]`.isEmail(No.checkDns, EmailStatusCode.any).statusCode == - EmailStatusCode.rfc5321AddressLiteral); - - assert(`test@[IPv6:1111:2222:3333:4444:5555:255.255.255.255]`.isEmail(No.checkDns, - EmailStatusCode.any).statusCode == EmailStatusCode.rfc5322IpV6GroupCount); - - assert(`test@[IPv6:1111:2222:3333:4444:5555:6666:255.255.255.255]`.isEmail(No.checkDns, - EmailStatusCode.any).statusCode == EmailStatusCode.rfc5321AddressLiteral); - - assert(`test@[IPv6:1111:2222:3333:4444:5555:6666:7777:255.255.255.255]`.isEmail(No.checkDns, - EmailStatusCode.any).statusCode == EmailStatusCode.rfc5322IpV6GroupCount); - - assert(`test@[IPv6:1111:2222:3333:4444::255.255.255.255]`.isEmail(No.checkDns, - EmailStatusCode.any).statusCode == EmailStatusCode.rfc5321AddressLiteral); - - assert(`test@[IPv6:1111:2222:3333:4444:5555:6666::255.255.255.255]`.isEmail(No.checkDns, - EmailStatusCode.any).statusCode == EmailStatusCode.rfc5322IpV6MaxGroups); - - assert(`test@[IPv6:1111:2222:3333:4444:::255.255.255.255]`.isEmail(No.checkDns, EmailStatusCode.any).statusCode - == EmailStatusCode.rfc5322IpV6TooManyDoubleColons); - - assert(`test@[IPv6::255.255.255.255]`.isEmail(No.checkDns, EmailStatusCode.any).statusCode == - EmailStatusCode.rfc5322IpV6ColonStart); - - assert(` test @iana.org`.isEmail(No.checkDns, EmailStatusCode.any).statusCode == - EmailStatusCode.deprecatedCommentFoldingWhitespaceNearAt); - - assert(`test@ iana .com`.isEmail(No.checkDns, EmailStatusCode.any).statusCode == - EmailStatusCode.deprecatedCommentFoldingWhitespaceNearAt); - - assert(`test . test@iana.org`.isEmail(No.checkDns, EmailStatusCode.any).statusCode == - EmailStatusCode.deprecatedFoldingWhitespace); - - assert("\u000D\u000A test@iana.org".isEmail(No.checkDns, EmailStatusCode.any).statusCode == - EmailStatusCode.foldingWhitespace, `Folding whitespace`); - - assert("\u000D\u000A \u000D\u000A test@iana.org".isEmail(No.checkDns, EmailStatusCode.any).statusCode == - EmailStatusCode.deprecatedFoldingWhitespace, `FWS with one line composed entirely of WSP`~ - ` -- only allowed as obsolete FWS (someone might allow only non-obsolete FWS)`); - - assert(`(comment)test@iana.org`.isEmail(No.checkDns, EmailStatusCode.any).statusCode == EmailStatusCode.comment); - assert(`((comment)test@iana.org`.isEmail(No.checkDns, EmailStatusCode.any).statusCode == - EmailStatusCode.errorUnclosedComment); - - assert(`(comment(comment))test@iana.org`.isEmail(No.checkDns, EmailStatusCode.any).statusCode == - EmailStatusCode.comment); - - assert(`test@(comment)iana.org`.isEmail(No.checkDns, EmailStatusCode.any).statusCode == - EmailStatusCode.deprecatedCommentFoldingWhitespaceNearAt); - - assert(`test(comment)test@iana.org`.isEmail(No.checkDns, EmailStatusCode.any).statusCode == - EmailStatusCode.errorTextAfterCommentFoldingWhitespace); - - assert(`test@(comment)[255.255.255.255]`.isEmail(No.checkDns, EmailStatusCode.any).statusCode == - EmailStatusCode.deprecatedCommentFoldingWhitespaceNearAt); - - assert(`(comment)abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghiklm@iana.org`.isEmail(No.checkDns, - EmailStatusCode.any).statusCode == EmailStatusCode.comment); - - assert(`test@(comment)abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghikl.com`.isEmail(No.checkDns, - EmailStatusCode.any).statusCode == EmailStatusCode.deprecatedCommentFoldingWhitespaceNearAt); - - assert((`(comment)test@abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghik.abcdefghijklmnopqrstuvwxyz`~ - `abcdefghijklmnopqrstuvwxyzabcdefghik.abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijk.`~ - `abcdefghijklmnopqrstuvwxyzabcdefghijk.abcdefghijklmnopqrstu`).isEmail(No.checkDns, - EmailStatusCode.any).statusCode == EmailStatusCode.comment); - - assert("test@iana.org\u000A".isEmail(No.checkDns, EmailStatusCode.any).statusCode == - EmailStatusCode.errorExpectingText); - - assert(`test@xn--hxajbheg2az3al.xn--jxalpdlp`.isEmail(No.checkDns, EmailStatusCode.any).statusCode == - EmailStatusCode.valid, `A valid IDN from ICANN's `~ - `IDN TLD evaluation gateway`); - - assert(`xn--test@iana.org`.isEmail(No.checkDns, EmailStatusCode.any).statusCode == EmailStatusCode.valid, - `RFC 3490: "unless the email standards are revised to invite the use of IDNA for local parts, a domain label`~ - ` that holds the local part of an email address SHOULD NOT begin with the ACE prefix, and even if it does,`~ - ` it is to be interpreted literally as a local part that happens to begin with the ACE prefix"`); - - assert(`test@iana.org-`.isEmail(No.checkDns, EmailStatusCode.any).statusCode == - EmailStatusCode.errorDomainHyphenEnd); - - assert(`"test@iana.org`.isEmail(No.checkDns, EmailStatusCode.any).statusCode == - EmailStatusCode.errorUnclosedQuotedString); - - assert(`(test@iana.org`.isEmail(No.checkDns, EmailStatusCode.any).statusCode == - EmailStatusCode.errorUnclosedComment); - - assert(`test@(iana.org`.isEmail(No.checkDns, EmailStatusCode.any).statusCode == - EmailStatusCode.errorUnclosedComment); - - assert(`test@[1.2.3.4`.isEmail(No.checkDns, EmailStatusCode.any).statusCode == - EmailStatusCode.errorUnclosedDomainLiteral); - - assert(`"test\"@iana.org`.isEmail(No.checkDns, EmailStatusCode.any).statusCode == - EmailStatusCode.errorUnclosedQuotedString); - - assert(`(comment\)test@iana.org`.isEmail(No.checkDns, EmailStatusCode.any).statusCode == - EmailStatusCode.errorUnclosedComment); - - assert(`test@iana.org(comment\)`.isEmail(No.checkDns, EmailStatusCode.any).statusCode == - EmailStatusCode.errorUnclosedComment); - - assert(`test@iana.org(comment\`.isEmail(No.checkDns, EmailStatusCode.any).statusCode == - EmailStatusCode.errorBackslashEnd); - - assert(`test@[RFC-5322-domain-literal]`.isEmail(No.checkDns, EmailStatusCode.any).statusCode == - EmailStatusCode.rfc5322DomainLiteral); - - assert(`test@[RFC-5322]-domain-literal]`.isEmail(No.checkDns, EmailStatusCode.any).statusCode == - EmailStatusCode.errorTextAfterDomainLiteral); - - assert(`test@[RFC-5322-[domain-literal]`.isEmail(No.checkDns, EmailStatusCode.any).statusCode == - EmailStatusCode.errorExpectingDomainText); - - assert("test@[RFC-5322-\\\u0007-domain-literal]".isEmail(No.checkDns, EmailStatusCode.any).statusCode == - EmailStatusCode.rfc5322DomainLiteralObsoleteText, `obs-dtext and obs-qp`); - - assert("test@[RFC-5322-\\\u0009-domain-literal]".isEmail(No.checkDns, EmailStatusCode.any).statusCode == - EmailStatusCode.rfc5322DomainLiteralObsoleteText); - - assert(`test@[RFC-5322-\]-domain-literal]`.isEmail(No.checkDns, EmailStatusCode.any).statusCode == - EmailStatusCode.rfc5322DomainLiteralObsoleteText); - - assert(`test@[RFC-5322-domain-literal\]`.isEmail(No.checkDns, EmailStatusCode.any).statusCode == - EmailStatusCode.errorUnclosedDomainLiteral); - - assert(`test@[RFC-5322-domain-literal\`.isEmail(No.checkDns, EmailStatusCode.any).statusCode == - EmailStatusCode.errorBackslashEnd); - - assert(`test@[RFC 5322 domain literal]`.isEmail(No.checkDns, EmailStatusCode.any).statusCode == - EmailStatusCode.rfc5322DomainLiteral, `Spaces are FWS in a domain literal`); - - assert(`test@[RFC-5322-domain-literal] (comment)`.isEmail(No.checkDns, EmailStatusCode.any).statusCode == - EmailStatusCode.rfc5322DomainLiteral); - - assert("\u007F@iana.org".isEmail(No.checkDns, EmailStatusCode.any).statusCode == - EmailStatusCode.errorExpectingText); - assert("test@\u007F.org".isEmail(No.checkDns, EmailStatusCode.any).statusCode == - EmailStatusCode.errorExpectingText); - assert("\"\u007F\"@iana.org".isEmail(No.checkDns, EmailStatusCode.any).statusCode == - EmailStatusCode.deprecatedQuotedText); - - assert("\"\\\u007F\"@iana.org".isEmail(No.checkDns, EmailStatusCode.any).statusCode == - EmailStatusCode.deprecatedQuotedPair); - - assert("(\u007F)test@iana.org".isEmail(No.checkDns, EmailStatusCode.any).statusCode == - EmailStatusCode.deprecatedCommentText); - - assert("test@iana.org\u000D".isEmail(No.checkDns, EmailStatusCode.any).statusCode == EmailStatusCode.errorCrNoLf, - `No LF after the CR`); - - assert("\u000Dtest@iana.org".isEmail(No.checkDns, EmailStatusCode.any).statusCode == EmailStatusCode.errorCrNoLf, - `No LF after the CR`); - - assert("\"\u000Dtest\"@iana.org".isEmail(No.checkDns, EmailStatusCode.any).statusCode == - EmailStatusCode.errorCrNoLf, `No LF after the CR`); - - assert("(\u000D)test@iana.org".isEmail(No.checkDns, EmailStatusCode.any).statusCode == EmailStatusCode.errorCrNoLf, - `No LF after the CR`); - - assert("(\u000D".isEmail(No.checkDns, EmailStatusCode.any).statusCode == EmailStatusCode.errorCrNoLf, - `No LF after the CR`); - - assert("test@iana.org(\u000D)".isEmail(No.checkDns, EmailStatusCode.any).statusCode == EmailStatusCode.errorCrNoLf, - `No LF after the CR`); - - assert("\u000Atest@iana.org".isEmail(No.checkDns, EmailStatusCode.any).statusCode == - EmailStatusCode.errorExpectingText); - - assert("\"\u000A\"@iana.org".isEmail(No.checkDns, EmailStatusCode.any).statusCode == - EmailStatusCode.errorExpectingQuotedText); - - assert("\"\\\u000A\"@iana.org".isEmail(No.checkDns, EmailStatusCode.any).statusCode == - EmailStatusCode.deprecatedQuotedPair); - - assert("(\u000A)test@iana.org".isEmail(No.checkDns, EmailStatusCode.any).statusCode == - EmailStatusCode.errorExpectingCommentText); - - assert("\u0007@iana.org".isEmail(No.checkDns, EmailStatusCode.any).statusCode == - EmailStatusCode.errorExpectingText); - - assert("test@\u0007.org".isEmail(No.checkDns, EmailStatusCode.any).statusCode == - EmailStatusCode.errorExpectingText); - - assert("\"\u0007\"@iana.org".isEmail(No.checkDns, EmailStatusCode.any).statusCode == - EmailStatusCode.deprecatedQuotedText); - - assert("\"\\\u0007\"@iana.org".isEmail(No.checkDns, EmailStatusCode.any).statusCode == - EmailStatusCode.deprecatedQuotedPair); - - assert("(\u0007)test@iana.org".isEmail(No.checkDns, EmailStatusCode.any).statusCode == - EmailStatusCode.deprecatedCommentText); - - assert("\u000D\u000Atest@iana.org".isEmail(No.checkDns, EmailStatusCode.any).statusCode == - EmailStatusCode.errorFoldingWhitespaceCrLfEnd, `Not FWS because no actual white space`); - - assert("\u000D\u000A \u000D\u000Atest@iana.org".isEmail(No.checkDns, EmailStatusCode.any).statusCode == - EmailStatusCode.errorFoldingWhitespaceCrLfEnd, `Not obs-FWS because there must be white space on each "fold"`); - - assert(" \u000D\u000Atest@iana.org".isEmail(No.checkDns, EmailStatusCode.any).statusCode == - EmailStatusCode.errorFoldingWhitespaceCrLfEnd, `Not FWS because no white space after the fold`); - - assert(" \u000D\u000A test@iana.org".isEmail(No.checkDns, EmailStatusCode.any).statusCode == - EmailStatusCode.foldingWhitespace, `FWS`); - - assert(" \u000D\u000A \u000D\u000Atest@iana.org".isEmail(No.checkDns, EmailStatusCode.any).statusCode == - EmailStatusCode.errorFoldingWhitespaceCrLfEnd, `Not FWS because no white space after the second fold`); - - assert(" \u000D\u000A\u000D\u000Atest@iana.org".isEmail(No.checkDns, EmailStatusCode.any).statusCode == - EmailStatusCode.errorFoldingWhitespaceCrflX2, `Not FWS because no white space after either fold`); - - assert(" \u000D\u000A\u000D\u000A test@iana.org".isEmail(No.checkDns, EmailStatusCode.any).statusCode == - EmailStatusCode.errorFoldingWhitespaceCrflX2, `Not FWS because no white space after the first fold`); - - assert("test@iana.org\u000D\u000A ".isEmail(No.checkDns, EmailStatusCode.any).statusCode == - EmailStatusCode.foldingWhitespace, `FWS`); - - assert("test@iana.org\u000D\u000A \u000D\u000A ".isEmail(No.checkDns, EmailStatusCode.any).statusCode == - EmailStatusCode.deprecatedFoldingWhitespace, `FWS with one line composed entirely of WSP -- `~ - `only allowed as obsolete FWS (someone might allow only non-obsolete FWS)`); - - assert("test@iana.org\u000D\u000A".isEmail(No.checkDns, EmailStatusCode.any).statusCode == - EmailStatusCode.errorFoldingWhitespaceCrLfEnd, `Not FWS because no actual white space`); - - assert("test@iana.org\u000D\u000A \u000D\u000A".isEmail(No.checkDns, EmailStatusCode.any).statusCode == - EmailStatusCode.errorFoldingWhitespaceCrLfEnd, `Not obs-FWS because there must be white space on each "fold"`); - - assert("test@iana.org \u000D\u000A".isEmail(No.checkDns, EmailStatusCode.any).statusCode == - EmailStatusCode.errorFoldingWhitespaceCrLfEnd, `Not FWS because no white space after the fold`); - - assert("test@iana.org \u000D\u000A ".isEmail(No.checkDns, EmailStatusCode.any).statusCode == - EmailStatusCode.foldingWhitespace, `FWS`); - - assert("test@iana.org \u000D\u000A \u000D\u000A".isEmail(No.checkDns, EmailStatusCode.any).statusCode == - EmailStatusCode.errorFoldingWhitespaceCrLfEnd, `Not FWS because no white space after the second fold`); - - assert("test@iana.org \u000D\u000A\u000D\u000A".isEmail(No.checkDns, EmailStatusCode.any).statusCode == - EmailStatusCode.errorFoldingWhitespaceCrflX2, `Not FWS because no white space after either fold`); - - assert("test@iana.org \u000D\u000A\u000D\u000A ".isEmail(No.checkDns, EmailStatusCode.any).statusCode == - EmailStatusCode.errorFoldingWhitespaceCrflX2, `Not FWS because no white space after the first fold`); - - assert(" test@iana.org".isEmail(No.checkDns, EmailStatusCode.any).statusCode == EmailStatusCode.foldingWhitespace); - assert(`test@iana.org `.isEmail(No.checkDns, EmailStatusCode.any).statusCode == EmailStatusCode.foldingWhitespace); - - assert(`test@[IPv6:1::2:]`.isEmail(No.checkDns, EmailStatusCode.any).statusCode == - EmailStatusCode.rfc5322IpV6ColonEnd); - - assert("\"test\\\u00A9\"@iana.org".isEmail(No.checkDns, EmailStatusCode.any).statusCode == - EmailStatusCode.errorExpectingQuotedPair); - - assert(`test@iana/icann.org`.isEmail(No.checkDns, EmailStatusCode.any).statusCode == EmailStatusCode.rfc5322Domain); - - assert(`test.(comment)test@iana.org`.isEmail(No.checkDns, EmailStatusCode.any).statusCode == - EmailStatusCode.deprecatedComment); - - assert(`test@org`.isEmail(No.checkDns, EmailStatusCode.any).statusCode == EmailStatusCode.rfc5321TopLevelDomain); - - // assert(`test@test.com`.isEmail(No.checkDns, EmailStatusCode.any).statusCode == - //EmailStatusCode.dnsWarningNoMXRecord, `test.com has an A-record but not an MX-record`); - // DNS check is currently not implemented - // - // assert(`test@nic.no`.isEmail(No.checkDns, EmailStatusCode.any).statusCode == EmailStatusCode.dnsWarningNoRecord, - // `nic.no currently has no MX-records or A-records (Feb 2011). If you are seeing an A-record for nic.io then` - // ` try setting your DNS server to 8.8.8.8 (the Google DNS server) - your DNS server may be faking an A-record` - // ` (OpenDNS does this, for instance).`); // DNS check is currently not implemented -} - -// https://issues.dlang.org/show_bug.cgi?id=17217 -@safe unittest -{ - wstring a = `test.test@iana.org`w; - dstring b = `test.test@iana.org`d; - const(wchar)[] c = `test.test@iana.org`w; - const(dchar)[] d = `test.test@iana.org`d; - - assert(a.isEmail(No.checkDns).statusCode == EmailStatusCode.valid); - assert(b.isEmail(No.checkDns).statusCode == EmailStatusCode.valid); - assert(c.isEmail(No.checkDns).statusCode == EmailStatusCode.valid); - assert(d.isEmail(No.checkDns).statusCode == EmailStatusCode.valid); -} - -/** - * Flag for indicating if the isEmail function should perform a DNS check or not. - * - * If set to `CheckDns.no`, isEmail does not perform DNS checking. - * - * Otherwise if set to `CheckDns.yes`, isEmail performs DNS checking. - */ -alias CheckDns = Flag!"checkDns"; - -/// Represents the status of an email address -struct EmailStatus -{ - private - { - bool valid_; - string localPart_; - string domainPart_; - EmailStatusCode statusCode_; - } - - /// Self aliases to a `bool` representing if the email is valid or not - alias valid this; - - /* - * Params: - * valid = indicates if the email address is valid or not - * localPart = the local part of the email address - * domainPart = the domain part of the email address - * statusCode = the status code - */ - private this (bool valid, string localPart, string domainPart, EmailStatusCode statusCode) @safe @nogc pure nothrow - { - this.valid_ = valid; - this.localPart_ = localPart; - this.domainPart_ = domainPart; - this.statusCode_ = statusCode; - } - - /// Returns: If the email address is valid or not. - @property bool valid() const @safe @nogc pure nothrow scope - { - return valid_; - } - - /// Returns: The local part of the email address, that is, the part before the @ sign. - @property string localPart() const @safe @nogc pure nothrow return scope - { - return localPart_; - } - - /// Returns: The domain part of the email address, that is, the part after the @ sign. - @property string domainPart() const @safe @nogc pure nothrow return scope - { - return domainPart_; - } - - /// Returns: The email status code - @property EmailStatusCode statusCode() const @safe @nogc pure nothrow scope - { - return statusCode_; - } - - /// Returns: A describing string of the status code - @property string status() const @safe @nogc pure nothrow scope - { - return statusCodeDescription(statusCode_); - } - - /// Returns: A textual representation of the email status - string toString() const @safe pure scope - { - import std.format : format; - return format("EmailStatus\n{\n\tvalid: %s\n\tlocalPart: %s\n\tdomainPart: %s\n\tstatusCode: %s\n}", valid, - localPart, domainPart, statusCode); - } -} - -/** - * Params: - * statusCode = The $(LREF EmailStatusCode) to read - * Returns: - * A detailed string describing the given status code - */ -string statusCodeDescription(EmailStatusCode statusCode) @safe @nogc pure nothrow -{ - final switch (statusCode) - { - // Categories - case EmailStatusCode.validCategory: return "Address is valid"; - case EmailStatusCode.dnsWarning: return "Address is valid but a DNS check was not successful"; - case EmailStatusCode.rfc5321: return "Address is valid for SMTP but has unusual elements"; - - case EmailStatusCode.cFoldingWhitespace: return "Address is valid within the message but cannot be used"~ - " unmodified for the envelope"; - - case EmailStatusCode.deprecated_: return "Address contains deprecated elements but may still be valid in"~ - " restricted contexts"; - - case EmailStatusCode.rfc5322: return "The address is only valid according to the broad definition of RFC 5322."~ - " It is otherwise invalid"; - - case EmailStatusCode.any: return ""; - case EmailStatusCode.none: return ""; - case EmailStatusCode.warning: return ""; - case EmailStatusCode.error: return "Address is invalid for any purpose"; - - // Diagnoses - case EmailStatusCode.valid: return "Address is valid"; - - // Address is valid but a DNS check was not successful - case EmailStatusCode.dnsWarningNoMXRecord: return "Could not find an MX record for this domain but an A-record"~ - " does exist"; - - case EmailStatusCode.dnsWarningNoRecord: return "Could not find an MX record or an A-record for this domain"; - - // Address is valid for SMTP but has unusual elements - case EmailStatusCode.rfc5321TopLevelDomain: return "Address is valid but at a Top Level Domain"; - - case EmailStatusCode.rfc5321TopLevelDomainNumeric: return "Address is valid but the Top Level Domain begins"~ - " with a number"; - - case EmailStatusCode.rfc5321QuotedString: return "Address is valid but contains a quoted string"; - case EmailStatusCode.rfc5321AddressLiteral: return "Address is valid but at a literal address not a domain"; - - case EmailStatusCode.rfc5321IpV6Deprecated: return "Address is valid but contains a :: that only elides one"~ - " zero group"; - - - // Address is valid within the message but cannot be used unmodified for the envelope - case EmailStatusCode.comment: return "Address contains comments"; - case EmailStatusCode.foldingWhitespace: return "Address contains Folding White Space"; - - // Address contains deprecated elements but may still be valid in restricted contexts - case EmailStatusCode.deprecatedLocalPart: return "The local part is in a deprecated form"; - - case EmailStatusCode.deprecatedFoldingWhitespace: return "Address contains an obsolete form of"~ - " Folding White Space"; - - case EmailStatusCode.deprecatedQuotedText: return "A quoted string contains a deprecated character"; - case EmailStatusCode.deprecatedQuotedPair: return "A quoted pair contains a deprecated character"; - case EmailStatusCode.deprecatedComment: return "Address contains a comment in a position that is deprecated"; - case EmailStatusCode.deprecatedCommentText: return "A comment contains a deprecated character"; - - case EmailStatusCode.deprecatedCommentFoldingWhitespaceNearAt: return "Address contains a comment or"~ - " Folding White Space around the @ sign"; - - // The address is only valid according to the broad definition of RFC 5322 - case EmailStatusCode.rfc5322Domain: return "Address is RFC 5322 compliant but contains domain characters that"~ - " are not allowed by DNS"; - - case EmailStatusCode.rfc5322TooLong: return "Address is too long"; - case EmailStatusCode.rfc5322LocalTooLong: return "The local part of the address is too long"; - case EmailStatusCode.rfc5322DomainTooLong: return "The domain part is too long"; - case EmailStatusCode.rfc5322LabelTooLong: return "The domain part contains an element that is too long"; - case EmailStatusCode.rfc5322DomainLiteral: return "The domain literal is not a valid RFC 5321 address literal"; - - case EmailStatusCode.rfc5322DomainLiteralObsoleteText: return "The domain literal is not a valid RFC 5321"~ - " address literal and it contains obsolete characters"; - - case EmailStatusCode.rfc5322IpV6GroupCount: - return "The IPv6 literal address contains the wrong number of groups"; - - case EmailStatusCode.rfc5322IpV6TooManyDoubleColons: - return "The IPv6 literal address contains too many :: sequences"; - - case EmailStatusCode.rfc5322IpV6BadChar: return "The IPv6 address contains an illegal group of characters"; - case EmailStatusCode.rfc5322IpV6MaxGroups: return "The IPv6 address has too many groups"; - case EmailStatusCode.rfc5322IpV6ColonStart: return "IPv6 address starts with a single colon"; - case EmailStatusCode.rfc5322IpV6ColonEnd: return "IPv6 address ends with a single colon"; - - // Address is invalid for any purpose - case EmailStatusCode.errorExpectingDomainText: - return "A domain literal contains a character that is not allowed"; - - case EmailStatusCode.errorNoLocalPart: return "Address has no local part"; - case EmailStatusCode.errorNoDomain: return "Address has no domain part"; - case EmailStatusCode.errorConsecutiveDots: return "The address may not contain consecutive dots"; - - case EmailStatusCode.errorTextAfterCommentFoldingWhitespace: - return "Address contains text after a comment or Folding White Space"; - - case EmailStatusCode.errorTextAfterQuotedString: return "Address contains text after a quoted string"; - - case EmailStatusCode.errorTextAfterDomainLiteral: return "Extra characters were found after the end of"~ - " the domain literal"; - - case EmailStatusCode.errorExpectingQuotedPair: - return "The address contains a character that is not allowed in a quoted pair"; - - case EmailStatusCode.errorExpectingText: return "Address contains a character that is not allowed"; - - case EmailStatusCode.errorExpectingQuotedText: - return "A quoted string contains a character that is not allowed"; - - case EmailStatusCode.errorExpectingCommentText: return "A comment contains a character that is not allowed"; - case EmailStatusCode.errorBackslashEnd: return "The address cannot end with a backslash"; - case EmailStatusCode.errorDotStart: return "Neither part of the address may begin with a dot"; - case EmailStatusCode.errorDotEnd: return "Neither part of the address may end with a dot"; - case EmailStatusCode.errorDomainHyphenStart: return "A domain or subdomain cannot begin with a hyphen"; - case EmailStatusCode.errorDomainHyphenEnd: return "A domain or subdomain cannot end with a hyphen"; - case EmailStatusCode.errorUnclosedQuotedString: return "Unclosed quoted string"; - case EmailStatusCode.errorUnclosedComment: return "Unclosed comment"; - case EmailStatusCode.errorUnclosedDomainLiteral: return "Domain literal is missing its closing bracket"; - - case EmailStatusCode.errorFoldingWhitespaceCrflX2: - return "Folding White Space contains consecutive CRLF sequences"; - - case EmailStatusCode.errorFoldingWhitespaceCrLfEnd: return "Folding White Space ends with a CRLF sequence"; - - case EmailStatusCode.errorCrNoLf: - return "Address contains a carriage return that is not followed by a line feed"; - } -} - -/** - * An email status code, indicating if an email address is valid or not. - * If it is invalid it also indicates why. - */ -enum EmailStatusCode -{ - // Categories - - /// Address is valid - validCategory = 1, - - /// Address is valid but a DNS check was not successful - dnsWarning = 7, - - /// Address is valid for SMTP but has unusual elements - rfc5321 = 15, - - /// Address is valid within the message but cannot be used unmodified for the envelope - cFoldingWhitespace = 31, - - /// Address contains deprecated elements but may still be valid in restricted contexts - deprecated_ = 63, - - /// The address is only valid according to the broad definition of RFC 5322. It is otherwise invalid - rfc5322 = 127, - - /** - * All finer grained error checking is turned on. Address containing errors or - * warnings is considered invalid. A specific email status code will be - * returned indicating the error/warning of the address. - */ - any = 252, - - /** - * Address is either considered valid or not, no finer grained error checking - * is performed. Returned email status code will be either Error or Valid. - */ - none = 253, - - /** - * Address containing warnings is considered valid, that is, - * any status code below 16 is considered valid. - */ - warning = 254, - - /// Address is invalid for any purpose - error = 255, - - - - // Diagnoses - - /// Address is valid - valid = 0, - - // Address is valid but a DNS check was not successful - - /// Could not find an MX record for this domain but an A-record does exist - dnsWarningNoMXRecord = 5, - - /// Could not find an MX record or an A-record for this domain - dnsWarningNoRecord = 6, - - - - // Address is valid for SMTP but has unusual elements - - /// Address is valid but at a Top Level Domain - rfc5321TopLevelDomain = 9, - - /// Address is valid but the Top Level Domain begins with a number - rfc5321TopLevelDomainNumeric = 10, - - /// Address is valid but contains a quoted string - rfc5321QuotedString = 11, - - /// Address is valid but at a literal address not a domain - rfc5321AddressLiteral = 12, - - /// Address is valid but contains a :: that only elides one zero group - rfc5321IpV6Deprecated = 13, - - - - // Address is valid within the message but cannot be used unmodified for the envelope - - /// Address contains comments - comment = 17, - - /// Address contains Folding White Space - foldingWhitespace = 18, - - - - // Address contains deprecated elements but may still be valid in restricted contexts - - /// The local part is in a deprecated form - deprecatedLocalPart = 33, - - /// Address contains an obsolete form of Folding White Space - deprecatedFoldingWhitespace = 34, - - /// A quoted string contains a deprecated character - deprecatedQuotedText = 35, - - /// A quoted pair contains a deprecated character - deprecatedQuotedPair = 36, - - /// Address contains a comment in a position that is deprecated - deprecatedComment = 37, - - /// A comment contains a deprecated character - deprecatedCommentText = 38, - - /// Address contains a comment or Folding White Space around the @ sign - deprecatedCommentFoldingWhitespaceNearAt = 49, - - - - // The address is only valid according to the broad definition of RFC 5322 - - /// Address is RFC 5322 compliant but contains domain characters that are not allowed by DNS - rfc5322Domain = 65, - - /// Address is too long - rfc5322TooLong = 66, - - /// The local part of the address is too long - rfc5322LocalTooLong = 67, - - /// The domain part is too long - rfc5322DomainTooLong = 68, - - /// The domain part contains an element that is too long - rfc5322LabelTooLong = 69, - - /// The domain literal is not a valid RFC 5321 address literal - rfc5322DomainLiteral = 70, - - /// The domain literal is not a valid RFC 5321 address literal and it contains obsolete characters - rfc5322DomainLiteralObsoleteText = 71, - - /// The IPv6 literal address contains the wrong number of groups - rfc5322IpV6GroupCount = 72, - - /// The IPv6 literal address contains too many :: sequences - rfc5322IpV6TooManyDoubleColons = 73, - - /// The IPv6 address contains an illegal group of characters - rfc5322IpV6BadChar = 74, - - /// The IPv6 address has too many groups - rfc5322IpV6MaxGroups = 75, - - /// IPv6 address starts with a single colon - rfc5322IpV6ColonStart = 76, - - /// IPv6 address ends with a single colon - rfc5322IpV6ColonEnd = 77, - - - - // Address is invalid for any purpose - - /// A domain literal contains a character that is not allowed - errorExpectingDomainText = 129, - - /// Address has no local part - errorNoLocalPart = 130, - - /// Address has no domain part - errorNoDomain = 131, - - /// The address may not contain consecutive dots - errorConsecutiveDots = 132, - - /// Address contains text after a comment or Folding White Space - errorTextAfterCommentFoldingWhitespace = 133, - - /// Address contains text after a quoted string - errorTextAfterQuotedString = 134, - - /// Extra characters were found after the end of the domain literal - errorTextAfterDomainLiteral = 135, - - /// The address contains a character that is not allowed in a quoted pair - errorExpectingQuotedPair = 136, - - /// Address contains a character that is not allowed - errorExpectingText = 137, - - /// A quoted string contains a character that is not allowed - errorExpectingQuotedText = 138, - - /// A comment contains a character that is not allowed - errorExpectingCommentText = 139, - - /// The address cannot end with a backslash - errorBackslashEnd = 140, - - /// Neither part of the address may begin with a dot - errorDotStart = 141, - - /// Neither part of the address may end with a dot - errorDotEnd = 142, - - /// A domain or subdomain cannot begin with a hyphen - errorDomainHyphenStart = 143, - - /// A domain or subdomain cannot end with a hyphen - errorDomainHyphenEnd = 144, - - /// Unclosed quoted string - errorUnclosedQuotedString = 145, - - /// Unclosed comment - errorUnclosedComment = 146, - - /// Domain literal is missing its closing bracket - errorUnclosedDomainLiteral = 147, - - /// Folding White Space contains consecutive CRLF sequences - errorFoldingWhitespaceCrflX2 = 148, - - /// Folding White Space ends with a CRLF sequence - errorFoldingWhitespaceCrLfEnd = 149, - - /// Address contains a carriage return that is not followed by a line feed - errorCrNoLf = 150, -} - -private: - -// Email parts for the isEmail function -enum EmailPart -{ - // The local part of the email address, that is, the part before the @ sign - componentLocalPart, - - // The domain part of the email address, that is, the part after the @ sign. - componentDomain, - - componentLiteral, - contextComment, - contextFoldingWhitespace, - contextQuotedString, - contextQuotedPair, - status -} - -// Miscellaneous string constants -struct TokenImpl(Char) -{ - enum : const(Char)[] - { - at = "@", - backslash = `\`, - dot = ".", - doubleQuote = `"`, - openParenthesis = "(", - closeParenthesis = ")", - openBracket = "[", - closeBracket = "]", - hyphen = "-", - colon = ":", - doubleColon = "::", - space = " ", - tab = "\t", - cr = "\r", - lf = "\n", - ipV6Tag = "IPV6:", - - // US-ASCII visible characters not valid for atext (http://tools.ietf.org/html/rfc5322#section-3.2.3) - specials = `()<>[]:;@\\,."` - } -} - -enum AsciiToken -{ - horizontalTab = 9, - unitSeparator = 31, - delete_ = 127 -} - -/* - * Compare the two given strings lexicographically. An upper limit of the number of - * characters, that will be used in the comparison, can be specified. Supports both - * case-sensitive and case-insensitive comparison. - * - * Params: - * s1 = the first string to be compared - * s2 = the second string to be compared - * length = the length of strings to be used in the comparison. - * caseInsensitive = if true, a case-insensitive comparison will be made, - * otherwise a case-sensitive comparison will be made - * - * Returns: (for $(D pred = "a < b")): - * - * $(BOOKTABLE, - * $(TR $(TD $(D < 0)) $(TD $(D s1 < s2) )) - * $(TR $(TD $(D = 0)) $(TD $(D s1 == s2))) - * $(TR $(TD $(D > 0)) $(TD $(D s1 > s2))) - * ) - */ -int compareFirstN(alias pred = "a < b", S1, S2) (S1 s1, S2 s2, size_t length) -if (is(immutable ElementType!(S1) == immutable dchar) && is(immutable ElementType!(S2) == immutable dchar)) -{ - import std.uni : icmp; - auto s1End = length <= s1.length ? length : s1.length; - auto s2End = length <= s2.length ? length : s2.length; - - auto slice1 = s1[0 .. s1End]; - auto slice2 = s2[0 .. s2End]; - - return slice1.icmp(slice2); -} - -@safe unittest -{ - assert("abc".compareFirstN("abcdef", 3) == 0); - assert("abc".compareFirstN("Abc", 3) == 0); - assert("abc".compareFirstN("abcdef", 6) < 0); - assert("abcdef".compareFirstN("abc", 6) > 0); -} - -/* - * Pops the last element of the given range and returns the element. - * - * Params: - * range = the range to pop the element from - * - * Returns: the popped element - */ -ElementType!(A) pop (A) (ref A a) -if (isDynamicArray!(A) && !isNarrowString!(A) && isMutable!(A) && !is(A == void[])) -{ - auto e = a.back; - a.popBack(); - return e; -} - -@safe unittest -{ - auto array = [0, 1, 2, 3]; - auto result = array.pop(); - - assert(array == [0, 1, 2]); - assert(result == 3); -} - -/* - * Returns the character at the given index as a string. The returned string will be a - * slice of the original string. - * - * Params: - * str = the string to get the character from - * index = the index of the character to get - * c = the character to return, or any other of the same length - * - * Returns: the character at the given index as a string - */ -const(T)[] get (T) (const(T)[] str, size_t index, dchar c) -{ - import std.utf : codeLength; - return str[index .. index + codeLength!(T)(c)]; -} - -@safe unittest -{ - assert("abc".get(1, 'b') == "b"); - assert("lĂśv".get(1, 'Ăś') == "Ăś"); -} - -@safe unittest -{ - assert("abc".get(1, 'b') == "b"); - assert("lĂśv".get(1, 'Ăś') == "Ăś"); -} - -/+ -Replacement for: ---- -static fourChars = ctRegex!(`^[0-9A-Fa-f]{0,4}$`.to!(const(Char)[])); -... -a => a.matchFirst(fourChars).empty ---- -+/ -bool isUpToFourHexChars(Char)(scope const(Char)[] s) -{ - import std.ascii : isHexDigit; - if (s.length > 4) return false; - foreach (c; s) - if (!isHexDigit(c)) return false; - return true; -} - -@nogc nothrow pure @safe unittest -{ - assert(!isUpToFourHexChars("12345")); - assert(!isUpToFourHexChars("defg")); - assert(isUpToFourHexChars("1A0a")); -} - -/+ -Replacement for: ---- -static ipRegex = ctRegex!(`\b(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}`~ - `(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$`.to!(const(Char)[])); -... -auto matchesIp = addressLiteral.matchAll(ipRegex).map!(a => a.hit).array; ----- -Note that only the first item of "matchAll" was ever used in practice -so we can return `const(Char)[]` instead of `const(Char)[][]` using a -zero-length string to indicate no match. -+/ -const(Char)[] matchIPSuffix(Char)(return scope const(Char)[] s) @nogc nothrow pure @safe -{ - size_t end = s.length; - if (end < 7) return null; - // Check the first three `[.]\d{1,3}` - foreach (_; 0 .. 3) - { - size_t start = void; - if (end >= 2 && s[end-2] == '.') - start = end - 2; - else if (end >= 3 && s[end-3] == '.') - start = end - 3; - else if (end >= 4 && s[end-4] == '.') - start = end - 4; - else - return null; - uint x = 0; - foreach (i; start + 1 .. end) - { - uint c = cast(uint) s[i] - '0'; - if (c > 9) return null; - x = x * 10 + c; - } - if (x > 255) return null; - end = start; - } - // Check the final `\d{1,3}`. - if (end < 1) return null; - size_t start = end - 1; - uint x = cast(uint) s[start] - '0'; - if (x > 9) return null; - if (start > 0 && cast(uint) s[start-1] - '0' <= 9) - { - --start; - x += 10 * (cast(uint) s[start] - '0'); - if (start > 0 && cast(uint) s[start-1] - '0' <= 9) - { - --start; - x += 100 * (cast(uint) s[start] - '0'); - } - } - if (x > 255) return null; - // Must either be at start of string or preceded by a non-word character. - // (TO DETERMINE: is the definition of "word character" ASCII only?) - if (start == 0) return s; - const b = s[start - 1]; - import std.ascii : isAlphaNum; - if (isAlphaNum(b) || b == '_') return null; - return s[start .. $]; -} - -@nogc nothrow pure @safe unittest -{ - assert(matchIPSuffix("255.255.255.255") == "255.255.255.255"); - assert(matchIPSuffix("babaev 176.16.0.1") == "176.16.0.1"); -} diff --git a/phobos/std/numeric.d b/phobos/std/numeric.d deleted file mode 100644 index 648b70e..0000000 --- a/phobos/std/numeric.d +++ /dev/null @@ -1,4147 +0,0 @@ -// Written in the D programming language. - -/** -This module is a port of a growing fragment of the $(D_PARAM numeric) -header in Alexander Stepanov's $(LINK2 https://en.wikipedia.org/wiki/Standard_Template_Library, -Standard Template Library), with a few additions. - -Macros: -Copyright: Copyright Andrei Alexandrescu 2008 - 2009. -License: $(HTTP www.boost.org/LICENSE_1_0.txt, Boost License 1.0). -Authors: $(HTTP erdani.org, Andrei Alexandrescu), - Don Clugston, Robert Jacques, Ilya Yaroshenko -Source: $(PHOBOSSRC std/numeric.d) -*/ -/* - Copyright Andrei Alexandrescu 2008 - 2009. -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.numeric; - -import std.complex; -import std.math; -import core.math : fabs, ldexp, sin, sqrt; -import std.range.primitives; -import std.traits; -import std.typecons; - -/// Format flags for CustomFloat. -public enum CustomFloatFlags -{ - /// Adds a sign bit to allow for signed numbers. - signed = 1, - - /** - * Store values in normalized form by default. The actual precision of the - * significand is extended by 1 bit by assuming an implicit leading bit of 1 - * instead of 0. i.e. `1.nnnn` instead of `0.nnnn`. - * True for all $(LINK2 https://en.wikipedia.org/wiki/IEEE_floating_point, IEE754) types - */ - storeNormalized = 2, - - /** - * Stores the significand in $(LINK2 https://en.wikipedia.org/wiki/IEEE_754-1985#Denormalized_numbers, - * IEEE754 denormalized) form when the exponent is 0. Required to express the value 0. - */ - allowDenorm = 4, - - /** - * Allows the storage of $(LINK2 https://en.wikipedia.org/wiki/IEEE_754-1985#Positive_and_negative_infinity, - * IEEE754 _infinity) values. - */ - infinity = 8, - - /// Allows the storage of $(LINK2 https://en.wikipedia.org/wiki/NaN, IEEE754 Not a Number) values. - nan = 16, - - /** - * If set, select an exponent bias such that max_exp = 1. - * i.e. so that the maximum value is >= 1.0 and < 2.0. - * Ignored if the exponent bias is manually specified. - */ - probability = 32, - - /// If set, unsigned custom floats are assumed to be negative. - negativeUnsigned = 64, - - /**If set, 0 is the only allowed $(LINK2 https://en.wikipedia.org/wiki/IEEE_754-1985#Denormalized_numbers, - * IEEE754 denormalized) number. - * Requires allowDenorm and storeNormalized. - */ - allowDenormZeroOnly = 128 | allowDenorm | storeNormalized, - - /// Include _all of the $(LINK2 https://en.wikipedia.org/wiki/IEEE_floating_point, IEEE754) options. - ieee = signed | storeNormalized | allowDenorm | infinity | nan , - - /// Include none of the above options. - none = 0 -} - -private enum isIEEEQuadruple = floatTraits!real.realFormat == RealFormat.ieeeQuadruple; - -private template CustomFloatParams(uint bits) -{ - enum CustomFloatFlags flags = CustomFloatFlags.ieee - ^ ((bits == 80 && !isIEEEQuadruple) ? CustomFloatFlags.storeNormalized : CustomFloatFlags.none); - static if (bits == 8) alias CustomFloatParams = CustomFloatParams!( 4, 3, flags); - static if (bits == 16) alias CustomFloatParams = CustomFloatParams!(10, 5, flags); - static if (bits == 32) alias CustomFloatParams = CustomFloatParams!(23, 8, flags); - static if (bits == 64) alias CustomFloatParams = CustomFloatParams!(52, 11, flags); - static if (bits == 80) alias CustomFloatParams = CustomFloatParams!(64, 15, flags); -} - -private template CustomFloatParams(uint precision, uint exponentWidth, CustomFloatFlags flags) -{ - import std.meta : AliasSeq; - alias CustomFloatParams = - AliasSeq!( - precision, - exponentWidth, - flags, - (1 << (exponentWidth - ((flags & flags.probability) == 0))) - - ((flags & (flags.nan | flags.infinity)) != 0) - ((flags & flags.probability) != 0) - ); // ((flags & CustomFloatFlags.probability) == 0) -} - -/** - * Allows user code to define custom floating-point formats. These formats are - * for storage only; all operations on them are performed by first implicitly - * extracting them to `real` first. After the operation is completed the - * result can be stored in a custom floating-point value via assignment. - */ -template CustomFloat(uint bits) -if (bits == 8 || bits == 16 || bits == 32 || bits == 64 || bits == 80) -{ - alias CustomFloat = CustomFloat!(CustomFloatParams!(bits)); -} - -/// ditto -template CustomFloat(uint precision, uint exponentWidth, CustomFloatFlags flags = CustomFloatFlags.ieee) -if (((flags & flags.signed) + precision + exponentWidth) % 8 == 0 && precision + exponentWidth > 0) -{ - alias CustomFloat = CustomFloat!(CustomFloatParams!(precision, exponentWidth, flags)); -} - -/// -@safe unittest -{ - import std.math.trigonometry : sin, cos; - - // Define a 16-bit floating point values - CustomFloat!16 x; // Using the number of bits - CustomFloat!(10, 5) y; // Using the precision and exponent width - CustomFloat!(10, 5,CustomFloatFlags.ieee) z; // Using the precision, exponent width and format flags - CustomFloat!(10, 5,CustomFloatFlags.ieee, 15) w; // Using the precision, exponent width, format flags and exponent offset bias - - // Use the 16-bit floats mostly like normal numbers - w = x*y - 1; - - // Functions calls require conversion - z = sin(+x) + cos(+y); // Use unary plus to concisely convert to a real - z = sin(x.get!float) + cos(y.get!float); // Or use get!T - z = sin(cast(float) x) + cos(cast(float) y); // Or use cast(T) to explicitly convert - - // Define a 8-bit custom float for storing probabilities - alias Probability = CustomFloat!(4, 4, CustomFloatFlags.ieee^CustomFloatFlags.probability^CustomFloatFlags.signed ); - auto p = Probability(0.5); -} - -// Facilitate converting numeric types to custom float -private union ToBinary(F) -if (is(typeof(CustomFloatParams!(F.sizeof*8))) || is(F == real)) -{ - F set; - - // If on Linux or Mac, where 80-bit reals are padded, ignore the - // padding. - import std.algorithm.comparison : min; - CustomFloat!(CustomFloatParams!(min(F.sizeof*8, 80))) get; - - // Convert F to the correct binary type. - static typeof(get) opCall(F value) - { - ToBinary r; - r.set = value; - return r.get; - } - alias get this; -} - -/// ditto -struct CustomFloat(uint precision, // fraction bits (23 for float) - uint exponentWidth, // exponent bits (8 for float) Exponent width - CustomFloatFlags flags, - uint bias) -if (isCorrectCustomFloat(precision, exponentWidth, flags)) -{ - import std.bitmanip : bitfields; - import std.meta : staticIndexOf; -private: - // get the correct unsigned bitfield type to support > 32 bits - template uType(uint bits) - { - static if (bits <= size_t.sizeof*8) alias uType = size_t; - else alias uType = ulong ; - } - - // get the correct signed bitfield type to support > 32 bits - template sType(uint bits) - { - static if (bits <= ptrdiff_t.sizeof*8-1) alias sType = ptrdiff_t; - else alias sType = long; - } - - alias T_sig = uType!precision; - alias T_exp = uType!exponentWidth; - alias T_signed_exp = sType!exponentWidth; - - alias Flags = CustomFloatFlags; - - // Perform IEEE rounding with round to nearest detection - void roundedShift(T,U)(ref T sig, U shift) - { - if (shift >= T.sizeof*8) - { - // avoid illegal shift - sig = 0; - } - else if (sig << (T.sizeof*8 - shift) == cast(T) 1uL << (T.sizeof*8 - 1)) - { - // round to even - sig >>= shift; - sig += sig & 1; - } - else - { - sig >>= shift - 1; - sig += sig & 1; - // Perform standard rounding - sig >>= 1; - } - } - - // Convert the current value to signed exponent, normalized form - void toNormalized(T,U)(ref T sig, ref U exp) - { - sig = significand; - auto shift = (T.sizeof*8) - precision; - exp = exponent; - static if (flags&(Flags.infinity|Flags.nan)) - { - // Handle inf or nan - if (exp == exponent_max) - { - exp = exp.max; - sig <<= shift; - static if (flags&Flags.storeNormalized) - { - // Save inf/nan in denormalized format - sig >>= 1; - sig += cast(T) 1uL << (T.sizeof*8 - 1); - } - return; - } - } - if ((~flags&Flags.storeNormalized) || - // Convert denormalized form to normalized form - ((flags&Flags.allowDenorm) && exp == 0)) - { - if (sig > 0) - { - import core.bitop : bsr; - auto shift2 = precision - bsr(sig); - exp -= shift2-1; - shift += shift2; - } - else // value = 0.0 - { - exp = exp.min; - return; - } - } - sig <<= shift; - exp -= bias; - } - - // Set the current value from signed exponent, normalized form - void fromNormalized(T,U)(ref T sig, ref U exp) - { - auto shift = (T.sizeof*8) - precision; - if (exp == exp.max) - { - // infinity or nan - exp = exponent_max; - static if (flags & Flags.storeNormalized) - sig <<= 1; - - // convert back to normalized form - static if (~flags & Flags.infinity) - // No infinity support? - assert(sig != 0, "Infinity floating point value assigned to a " - ~ typeof(this).stringof ~ " (no infinity support)."); - - static if (~flags & Flags.nan) // No NaN support? - assert(sig == 0, "NaN floating point value assigned to a " ~ - typeof(this).stringof ~ " (no nan support)."); - sig >>= shift; - return; - } - if (exp == exp.min) // 0.0 - { - exp = 0; - sig = 0; - return; - } - - exp += bias; - if (exp <= 0) - { - static if ((flags&Flags.allowDenorm) || - // Convert from normalized form to denormalized - (~flags&Flags.storeNormalized)) - { - shift += -exp; - roundedShift(sig,1); - sig += cast(T) 1uL << (T.sizeof*8 - 1); - // Add the leading 1 - exp = 0; - } - else - assert((flags&Flags.storeNormalized) && exp == 0, - "Underflow occured assigning to a " ~ - typeof(this).stringof ~ " (no denormal support)."); - } - else - { - static if (~flags&Flags.storeNormalized) - { - // Convert from normalized form to denormalized - roundedShift(sig,1); - sig += cast(T) 1uL << (T.sizeof*8 - 1); - // Add the leading 1 - } - } - - if (shift > 0) - roundedShift(sig,shift); - if (sig > significand_max) - { - // handle significand overflow (should only be 1 bit) - static if (~flags&Flags.storeNormalized) - { - sig >>= 1; - } - else - sig &= significand_max; - exp++; - } - static if ((flags&Flags.allowDenormZeroOnly)==Flags.allowDenormZeroOnly) - { - // disallow non-zero denormals - if (exp == 0) - { - sig <<= 1; - if (sig > significand_max && (sig&significand_max) > 0) - // Check and round to even - exp++; - sig = 0; - } - } - - if (exp >= exponent_max) - { - static if (flags&(Flags.infinity|Flags.nan)) - { - sig = 0; - exp = exponent_max; - static if (~flags&(Flags.infinity)) - assert(0, "Overflow occured assigning to a " ~ - typeof(this).stringof ~ " (no infinity support)."); - } - else - assert(exp == exponent_max, "Overflow occured assigning to a " - ~ typeof(this).stringof ~ " (no infinity support)."); - } - } - -public: - static if (precision == 64) // CustomFloat!80 support hack - { - static if (isIEEEQuadruple) - { - // Only use highest 64 significand bits from 112 explicitly stored - align (1): - enum ulong significand_max = ulong.max; - version (LittleEndian) - { - private ubyte[6] _padding; // 48-bit of padding - ulong significand; - mixin(bitfields!( - T_exp , "exponent", exponentWidth, - bool , "sign" , flags & flags.signed )); - } - else - { - mixin(bitfields!( - T_exp , "exponent", exponentWidth, - bool , "sign" , flags & flags.signed )); - ulong significand; - private ubyte[6] _padding; // 48-bit of padding - } - } - else - { - ulong significand; - enum ulong significand_max = ulong.max; - mixin(bitfields!( - T_exp , "exponent", exponentWidth, - bool , "sign" , flags & flags.signed )); - } - } - else - { - mixin(bitfields!( - T_sig, "significand", precision, - T_exp, "exponent" , exponentWidth, - bool , "sign" , flags & flags.signed )); - } - - /// Returns: infinity value - static if (flags & Flags.infinity) - static @property CustomFloat infinity() - { - CustomFloat value; - static if (flags & Flags.signed) - value.sign = 0; - value.significand = 0; - value.exponent = exponent_max; - return value; - } - - /// Returns: NaN value - static if (flags & Flags.nan) - static @property CustomFloat nan() - { - CustomFloat value; - static if (flags & Flags.signed) - value.sign = 0; - value.significand = cast(typeof(significand_max)) 1L << (precision-1); - value.exponent = exponent_max; - return value; - } - - /// Returns: number of decimal digits of precision - static @property size_t dig() - { - auto shiftcnt = precision - ((flags&Flags.storeNormalized) == 0); - return shiftcnt == 64 ? 19 : cast(size_t) log10(real(1uL << shiftcnt)); - } - - /// Returns: smallest increment to the value 1 - static @property CustomFloat epsilon() - { - CustomFloat one = CustomFloat(1); - CustomFloat onePlusEpsilon = one; - onePlusEpsilon.significand = onePlusEpsilon.significand | 1; // |= does not work here - - return CustomFloat(onePlusEpsilon - one); - } - - /// the number of bits in mantissa - enum mant_dig = precision + ((flags&Flags.storeNormalized) != 0); - - /// Returns: maximum int value such that 10max_10_exp is representable - static @property int max_10_exp(){ return cast(int) log10( +max ); } - - /// maximum int value such that 2max_exp-1 is representable - enum max_exp = exponent_max - bias - ((flags & (Flags.infinity | Flags.nan)) != 0) + 1; - - /// Returns: minimum int value such that 10min_10_exp is representable - static @property int min_10_exp(){ return cast(int) log10( +min_normal ); } - - /// minimum int value such that 2min_exp-1 is representable as a normalized value - enum min_exp = cast(T_signed_exp) -(cast(long) bias) + 1 + ((flags & Flags.allowDenorm) != 0); - - /// Returns: largest representable value that's not infinity - static @property CustomFloat max() - { - CustomFloat value; - static if (flags & Flags.signed) - value.sign = 0; - value.exponent = exponent_max - ((flags&(flags.infinity|flags.nan)) != 0); - value.significand = significand_max; - return value; - } - - /// Returns: smallest representable normalized value that's not 0 - static @property CustomFloat min_normal() - { - CustomFloat value; - static if (flags & Flags.signed) - value.sign = 0; - value.exponent = (flags & Flags.allowDenorm) != 0; - static if (flags & Flags.storeNormalized) - value.significand = 0; - else - value.significand = cast(T_sig) 1uL << (precision - 1); - return value; - } - - /// Returns: real part - @property CustomFloat re() { return this; } - - /// Returns: imaginary part - static @property CustomFloat im() { return CustomFloat(0.0f); } - - /// Initialize from any `real` compatible type. - this(F)(F input) if (__traits(compiles, cast(real) input )) - { - this = input; - } - - /// Self assignment - void opAssign(F:CustomFloat)(F input) - { - static if (flags & Flags.signed) - sign = input.sign; - exponent = input.exponent; - significand = input.significand; - } - - /// Assigns from any `real` compatible type. - void opAssign(F)(F input) - if (__traits(compiles, cast(real) input)) - { - import std.conv : text; - - static if (staticIndexOf!(immutable F, immutable float, immutable double, immutable real) >= 0) - auto value = ToBinary!(Unqual!F)(input); - else - auto value = ToBinary!(real )(input); - - // Assign the sign bit - static if (~flags & Flags.signed) - assert((!value.sign) ^ ((flags&flags.negativeUnsigned) > 0), - "Incorrectly signed floating point value assigned to a " ~ - typeof(this).stringof ~ " (no sign support)."); - else - sign = value.sign; - - CommonType!(T_signed_exp ,value.T_signed_exp) exp = value.exponent; - CommonType!(T_sig, value.T_sig ) sig = value.significand; - - value.toNormalized(sig,exp); - fromNormalized(sig,exp); - - assert(exp <= exponent_max, text(typeof(this).stringof ~ - " exponent too large: " ,exp," > ",exponent_max, "\t",input,"\t",sig)); - assert(sig <= significand_max, text(typeof(this).stringof ~ - " significand too large: ",sig," > ",significand_max, - "\t",input,"\t",exp," ",exponent_max)); - exponent = cast(T_exp) exp; - significand = cast(T_sig) sig; - } - - /// Fetches the stored value either as a `float`, `double` or `real`. - @property F get(F)() - if (staticIndexOf!(immutable F, immutable float, immutable double, immutable real) >= 0) - { - import std.conv : text; - - ToBinary!F result; - - static if (flags&Flags.signed) - result.sign = sign; - else - result.sign = (flags&flags.negativeUnsigned) > 0; - - CommonType!(T_signed_exp ,result.get.T_signed_exp ) exp = exponent; // Assign the exponent and fraction - CommonType!(T_sig, result.get.T_sig ) sig = significand; - - toNormalized(sig,exp); - result.fromNormalized(sig,exp); - assert(exp <= result.exponent_max, text("get exponent too large: " ,exp," > ",result.exponent_max) ); - assert(sig <= result.significand_max, text("get significand too large: ",sig," > ",result.significand_max) ); - result.exponent = cast(result.get.T_exp) exp; - result.significand = cast(result.get.T_sig) sig; - return result.set; - } - - ///ditto - alias opCast = get; - - /// Convert the CustomFloat to a real and perform the relevant operator on the result - real opUnary(string op)() - if (__traits(compiles, mixin(op~`(get!real)`)) || op=="++" || op=="--") - { - static if (op=="++" || op=="--") - { - auto result = get!real; - this = mixin(op~`result`); - return result; - } - else - return mixin(op~`get!real`); - } - - /// ditto - // Define an opBinary `CustomFloat op CustomFloat` so that those below - // do not match equally, which is disallowed by the spec: - // https://dlang.org/spec/operatoroverloading.html#binary - real opBinary(string op,T)(T b) - if (__traits(compiles, mixin(`get!real`~op~`b.get!real`))) - { - return mixin(`get!real`~op~`b.get!real`); - } - - /// ditto - real opBinary(string op,T)(T b) - if ( __traits(compiles, mixin(`get!real`~op~`b`)) && - !__traits(compiles, mixin(`get!real`~op~`b.get!real`))) - { - return mixin(`get!real`~op~`b`); - } - - /// ditto - real opBinaryRight(string op,T)(T a) - if ( __traits(compiles, mixin(`a`~op~`get!real`)) && - !__traits(compiles, mixin(`get!real`~op~`b`)) && - !__traits(compiles, mixin(`get!real`~op~`b.get!real`))) - { - return mixin(`a`~op~`get!real`); - } - - /// ditto - int opCmp(T)(auto ref T b) - if (__traits(compiles, cast(real) b)) - { - auto x = get!real; - auto y = cast(real) b; - return (x >= y)-(x <= y); - } - - /// ditto - void opOpAssign(string op, T)(auto ref T b) - if (__traits(compiles, mixin(`get!real`~op~`cast(real) b`))) - { - return mixin(`this = this `~op~` cast(real) b`); - } - - /// ditto - template toString() - { - import std.format.spec : FormatSpec; - import std.format.write : formatValue; - // Needs to be a template because of https://issues.dlang.org/show_bug.cgi?id=13737. - void toString()(scope void delegate(const(char)[]) sink, scope const ref FormatSpec!char fmt) - { - sink.formatValue(get!real, fmt); - } - } -} - -@safe unittest -{ - import std.meta; - alias FPTypes = - AliasSeq!( - CustomFloat!(5, 10), - CustomFloat!(5, 11, CustomFloatFlags.ieee ^ CustomFloatFlags.signed), - CustomFloat!(1, 7, CustomFloatFlags.ieee ^ CustomFloatFlags.signed), - CustomFloat!(4, 3, CustomFloatFlags.ieee | CustomFloatFlags.probability ^ CustomFloatFlags.signed) - ); - - foreach (F; FPTypes) - { - auto x = F(0.125); - assert(x.get!float == 0.125F); - assert(x.get!double == 0.125); - assert(x.get!real == 0.125L); - - x -= 0.0625; - assert(x.get!float == 0.0625F); - assert(x.get!double == 0.0625); - assert(x.get!real == 0.0625L); - - x *= 2; - assert(x.get!float == 0.125F); - assert(x.get!double == 0.125); - assert(x.get!real == 0.125L); - - x /= 4; - assert(x.get!float == 0.03125); - assert(x.get!double == 0.03125); - assert(x.get!real == 0.03125L); - - x = 0.5; - x ^^= 4; - assert(x.get!float == 1 / 16.0F); - assert(x.get!double == 1 / 16.0); - assert(x.get!real == 1 / 16.0L); - } -} - -@system unittest -{ - // @system due to to!string(CustomFloat) - import std.conv; - CustomFloat!(5, 10) y = CustomFloat!(5, 10)(0.125); - assert(y.to!string == "0.125"); -} - -@safe unittest -{ - alias cf = CustomFloat!(5, 2); - - auto a = cf.infinity; - assert(a.sign == 0); - assert(a.exponent == 3); - assert(a.significand == 0); - - auto b = cf.nan; - assert(b.exponent == 3); - assert(b.significand != 0); - - assert(cf.dig == 1); - - auto c = cf.epsilon; - assert(c.sign == 0); - assert(c.exponent == 0); - assert(c.significand == 1); - - assert(cf.mant_dig == 6); - - assert(cf.max_10_exp == 0); - assert(cf.max_exp == 2); - assert(cf.min_10_exp == 0); - assert(cf.min_exp == 1); - - auto d = cf.max; - assert(d.sign == 0); - assert(d.exponent == 2); - assert(d.significand == 31); - - auto e = cf.min_normal; - assert(e.sign == 0); - assert(e.exponent == 1); - assert(e.significand == 0); - - assert(e.re == e); - assert(e.im == cf(0.0)); -} - -// check whether CustomFloats identical to float/double behave like float/double -@safe unittest -{ - import std.conv : to; - - alias myFloat = CustomFloat!(23, 8); - - static assert(myFloat.dig == float.dig); - static assert(myFloat.mant_dig == float.mant_dig); - assert(myFloat.max_10_exp == float.max_10_exp); - static assert(myFloat.max_exp == float.max_exp); - assert(myFloat.min_10_exp == float.min_10_exp); - static assert(myFloat.min_exp == float.min_exp); - assert(to!float(myFloat.epsilon) == float.epsilon); - assert(to!float(myFloat.max) == float.max); - assert(to!float(myFloat.min_normal) == float.min_normal); - - alias myDouble = CustomFloat!(52, 11); - - static assert(myDouble.dig == double.dig); - static assert(myDouble.mant_dig == double.mant_dig); - assert(myDouble.max_10_exp == double.max_10_exp); - static assert(myDouble.max_exp == double.max_exp); - assert(myDouble.min_10_exp == double.min_10_exp); - static assert(myDouble.min_exp == double.min_exp); - assert(to!double(myDouble.epsilon) == double.epsilon); - assert(to!double(myDouble.max) == double.max); - assert(to!double(myDouble.min_normal) == double.min_normal); -} - -// testing .dig -@safe unittest -{ - static assert(CustomFloat!(1, 6).dig == 0); - static assert(CustomFloat!(9, 6).dig == 2); - static assert(CustomFloat!(10, 5).dig == 3); - static assert(CustomFloat!(10, 6, CustomFloatFlags.none).dig == 2); - static assert(CustomFloat!(11, 5, CustomFloatFlags.none).dig == 3); - static assert(CustomFloat!(64, 7).dig == 19); -} - -// testing .mant_dig -@safe unittest -{ - static assert(CustomFloat!(10, 5).mant_dig == 11); - static assert(CustomFloat!(10, 6, CustomFloatFlags.none).mant_dig == 10); -} - -// testing .max_exp -@safe unittest -{ - static assert(CustomFloat!(1, 6).max_exp == 2^^5); - static assert(CustomFloat!(2, 6, CustomFloatFlags.none).max_exp == 2^^5); - static assert(CustomFloat!(5, 10).max_exp == 2^^9); - static assert(CustomFloat!(6, 10, CustomFloatFlags.none).max_exp == 2^^9); - static assert(CustomFloat!(2, 6, CustomFloatFlags.nan).max_exp == 2^^5); - static assert(CustomFloat!(6, 10, CustomFloatFlags.nan).max_exp == 2^^9); -} - -// testing .min_exp -@safe unittest -{ - static assert(CustomFloat!(1, 6).min_exp == -2^^5+3); - static assert(CustomFloat!(5, 10).min_exp == -2^^9+3); - static assert(CustomFloat!(2, 6, CustomFloatFlags.none).min_exp == -2^^5+1); - static assert(CustomFloat!(6, 10, CustomFloatFlags.none).min_exp == -2^^9+1); - static assert(CustomFloat!(2, 6, CustomFloatFlags.nan).min_exp == -2^^5+2); - static assert(CustomFloat!(6, 10, CustomFloatFlags.nan).min_exp == -2^^9+2); - static assert(CustomFloat!(2, 6, CustomFloatFlags.allowDenorm).min_exp == -2^^5+2); - static assert(CustomFloat!(6, 10, CustomFloatFlags.allowDenorm).min_exp == -2^^9+2); -} - -// testing .max_10_exp -@safe unittest -{ - assert(CustomFloat!(1, 6).max_10_exp == 9); - assert(CustomFloat!(5, 10).max_10_exp == 154); - assert(CustomFloat!(2, 6, CustomFloatFlags.none).max_10_exp == 9); - assert(CustomFloat!(6, 10, CustomFloatFlags.none).max_10_exp == 154); - assert(CustomFloat!(2, 6, CustomFloatFlags.nan).max_10_exp == 9); - assert(CustomFloat!(6, 10, CustomFloatFlags.nan).max_10_exp == 154); -} - -// testing .min_10_exp -@safe unittest -{ - assert(CustomFloat!(1, 6).min_10_exp == -9); - assert(CustomFloat!(5, 10).min_10_exp == -153); - assert(CustomFloat!(2, 6, CustomFloatFlags.none).min_10_exp == -9); - assert(CustomFloat!(6, 10, CustomFloatFlags.none).min_10_exp == -154); - assert(CustomFloat!(2, 6, CustomFloatFlags.nan).min_10_exp == -9); - assert(CustomFloat!(6, 10, CustomFloatFlags.nan).min_10_exp == -153); - assert(CustomFloat!(2, 6, CustomFloatFlags.allowDenorm).min_10_exp == -9); - assert(CustomFloat!(6, 10, CustomFloatFlags.allowDenorm).min_10_exp == -153); -} - -// testing .epsilon -@safe unittest -{ - assert(CustomFloat!(1,6).epsilon.sign == 0); - assert(CustomFloat!(1,6).epsilon.exponent == 30); - assert(CustomFloat!(1,6).epsilon.significand == 0); - assert(CustomFloat!(2,5).epsilon.sign == 0); - assert(CustomFloat!(2,5).epsilon.exponent == 13); - assert(CustomFloat!(2,5).epsilon.significand == 0); - assert(CustomFloat!(3,4).epsilon.sign == 0); - assert(CustomFloat!(3,4).epsilon.exponent == 4); - assert(CustomFloat!(3,4).epsilon.significand == 0); - // the following epsilons are only available, when denormalized numbers are allowed: - assert(CustomFloat!(4,3).epsilon.sign == 0); - assert(CustomFloat!(4,3).epsilon.exponent == 0); - assert(CustomFloat!(4,3).epsilon.significand == 4); - assert(CustomFloat!(5,2).epsilon.sign == 0); - assert(CustomFloat!(5,2).epsilon.exponent == 0); - assert(CustomFloat!(5,2).epsilon.significand == 1); -} - -// testing .max -@safe unittest -{ - static assert(CustomFloat!(5,2).max.sign == 0); - static assert(CustomFloat!(5,2).max.exponent == 2); - static assert(CustomFloat!(5,2).max.significand == 31); - static assert(CustomFloat!(4,3).max.sign == 0); - static assert(CustomFloat!(4,3).max.exponent == 6); - static assert(CustomFloat!(4,3).max.significand == 15); - static assert(CustomFloat!(3,4).max.sign == 0); - static assert(CustomFloat!(3,4).max.exponent == 14); - static assert(CustomFloat!(3,4).max.significand == 7); - static assert(CustomFloat!(2,5).max.sign == 0); - static assert(CustomFloat!(2,5).max.exponent == 30); - static assert(CustomFloat!(2,5).max.significand == 3); - static assert(CustomFloat!(1,6).max.sign == 0); - static assert(CustomFloat!(1,6).max.exponent == 62); - static assert(CustomFloat!(1,6).max.significand == 1); - static assert(CustomFloat!(3,5, CustomFloatFlags.none).max.exponent == 31); - static assert(CustomFloat!(3,5, CustomFloatFlags.none).max.significand == 7); -} - -// testing .min_normal -@safe unittest -{ - static assert(CustomFloat!(5,2).min_normal.sign == 0); - static assert(CustomFloat!(5,2).min_normal.exponent == 1); - static assert(CustomFloat!(5,2).min_normal.significand == 0); - static assert(CustomFloat!(4,3).min_normal.sign == 0); - static assert(CustomFloat!(4,3).min_normal.exponent == 1); - static assert(CustomFloat!(4,3).min_normal.significand == 0); - static assert(CustomFloat!(3,4).min_normal.sign == 0); - static assert(CustomFloat!(3,4).min_normal.exponent == 1); - static assert(CustomFloat!(3,4).min_normal.significand == 0); - static assert(CustomFloat!(2,5).min_normal.sign == 0); - static assert(CustomFloat!(2,5).min_normal.exponent == 1); - static assert(CustomFloat!(2,5).min_normal.significand == 0); - static assert(CustomFloat!(1,6).min_normal.sign == 0); - static assert(CustomFloat!(1,6).min_normal.exponent == 1); - static assert(CustomFloat!(1,6).min_normal.significand == 0); - static assert(CustomFloat!(3,5, CustomFloatFlags.none).min_normal.exponent == 0); - static assert(CustomFloat!(3,5, CustomFloatFlags.none).min_normal.significand == 4); -} - -@safe unittest -{ - import std.math.traits : isNaN; - - alias cf = CustomFloat!(5, 2); - - auto f = cf.nan.get!float(); - assert(isNaN(f)); - - cf a; - a = real.max; - assert(a == cf.infinity); - - a = 0.015625; - assert(a.exponent == 0); - assert(a.significand == 0); - - a = 0.984375; - assert(a.exponent == 1); - assert(a.significand == 0); -} - -@system unittest -{ - import std.exception : assertThrown; - import core.exception : AssertError; - - alias cf = CustomFloat!(3, 5, CustomFloatFlags.none); - - cf a; - assertThrown!AssertError(a = real.max); -} - -@system unittest -{ - import std.exception : assertThrown; - import core.exception : AssertError; - - alias cf = CustomFloat!(3, 5, CustomFloatFlags.nan); - - cf a; - assertThrown!AssertError(a = real.max); -} - -@system unittest -{ - import std.exception : assertThrown; - import core.exception : AssertError; - - alias cf = CustomFloat!(24, 8, CustomFloatFlags.none); - - cf a; - assertThrown!AssertError(a = float.infinity); -} - -private bool isCorrectCustomFloat(uint precision, uint exponentWidth, CustomFloatFlags flags) @safe pure nothrow @nogc -{ - // Restrictions from bitfield - // due to CustomFloat!80 support hack precision with 64 bits is handled specially - auto length = (flags & flags.signed) + exponentWidth + ((precision == 64) ? 0 : precision); - if (length != 8 && length != 16 && length != 32 && length != 64) return false; - - // mantissa needs to fit into real mantissa - if (precision > real.mant_dig - 1 && precision != 64) return false; - - // exponent needs to fit into real exponent - if (1L << exponentWidth - 1 > real.max_exp) return false; - - // mantissa should have at least one bit - if (precision == 0) return false; - - // exponent should have at least one bit, in some cases two - if (exponentWidth <= ((flags & (flags.allowDenorm | flags.infinity | flags.nan)) != 0)) return false; - - return true; -} - -@safe pure nothrow @nogc unittest -{ - assert(isCorrectCustomFloat(3,4,CustomFloatFlags.ieee)); - assert(isCorrectCustomFloat(3,5,CustomFloatFlags.none)); - assert(!isCorrectCustomFloat(3,3,CustomFloatFlags.ieee)); - assert(isCorrectCustomFloat(64,7,CustomFloatFlags.ieee)); - assert(!isCorrectCustomFloat(64,4,CustomFloatFlags.ieee)); - assert(!isCorrectCustomFloat(508,3,CustomFloatFlags.ieee)); - assert(!isCorrectCustomFloat(3,100,CustomFloatFlags.ieee)); - assert(!isCorrectCustomFloat(0,7,CustomFloatFlags.ieee)); - assert(!isCorrectCustomFloat(6,1,CustomFloatFlags.ieee)); - assert(isCorrectCustomFloat(7,1,CustomFloatFlags.none)); - assert(!isCorrectCustomFloat(8,0,CustomFloatFlags.none)); -} - -/** -Defines the fastest type to use when storing temporaries of a -calculation intended to ultimately yield a result of type `F` -(where `F` must be one of `float`, `double`, or $(D -real)). When doing a multi-step computation, you may want to store -intermediate results as `FPTemporary!F`. - -The necessity of `FPTemporary` stems from the optimized -floating-point operations and registers present in virtually all -processors. When adding numbers in the example above, the addition may -in fact be done in `real` precision internally. In that case, -storing the intermediate `result` in $(D double format) is not only -less precise, it is also (surprisingly) slower, because a conversion -from `real` to `double` is performed every pass through the -loop. This being a lose-lose situation, `FPTemporary!F` has been -defined as the $(I fastest) type to use for calculations at precision -`F`. There is no need to define a type for the $(I most accurate) -calculations, as that is always `real`. - -Finally, there is no guarantee that using `FPTemporary!F` will -always be fastest, as the speed of floating-point calculations depends -on very many factors. - */ -template FPTemporary(F) -if (isFloatingPoint!F) -{ - version (X86) - alias FPTemporary = real; - else - alias FPTemporary = Unqual!F; -} - -/// -@safe unittest -{ - import std.math.operations : isClose; - - // Average numbers in an array - double avg(in double[] a) - { - if (a.length == 0) return 0; - FPTemporary!double result = 0; - foreach (e; a) result += e; - return result / a.length; - } - - auto a = [1.0, 2.0, 3.0]; - assert(isClose(avg(a), 2)); -} - -/** -Implements the $(HTTP tinyurl.com/2zb9yr, secant method) for finding a -root of the function `fun` starting from points $(D [xn_1, x_n]) -(ideally close to the root). `Num` may be `float`, `double`, -or `real`. -*/ -template secantMethod(alias fun) -{ - import std.functional : unaryFun; - Num secantMethod(Num)(Num xn_1, Num xn) - { - auto fxn = unaryFun!(fun)(xn_1), d = xn_1 - xn; - typeof(fxn) fxn_1; - - xn = xn_1; - while (!isClose(d, 0, 0.0, 1e-5) && isFinite(d)) - { - xn_1 = xn; - xn -= d; - fxn_1 = fxn; - fxn = unaryFun!(fun)(xn); - d *= -fxn / (fxn - fxn_1); - } - return xn; - } -} - -/// -@safe unittest -{ - import std.math.operations : isClose; - import std.math.trigonometry : cos; - - float f(float x) - { - return cos(x) - x*x*x; - } - auto x = secantMethod!(f)(0f, 1f); - assert(isClose(x, 0.865474)); -} - -@system unittest -{ - // @system because of __gshared stderr - import std.stdio; - scope(failure) stderr.writeln("Failure testing secantMethod"); - float f(float x) - { - return cos(x) - x*x*x; - } - immutable x = secantMethod!(f)(0f, 1f); - assert(isClose(x, 0.865474)); - auto d = &f; - immutable y = secantMethod!(d)(0f, 1f); - assert(isClose(y, 0.865474)); -} - - -/** - * Return true if a and b have opposite sign. - */ -private bool oppositeSigns(T1, T2)(T1 a, T2 b) -{ - return signbit(a) != signbit(b); -} - -public: - -/** Find a real root of a real function f(x) via bracketing. - * - * Given a function `f` and a range `[a .. b]` such that `f(a)` - * and `f(b)` have opposite signs or at least one of them equals Âą0, - * returns the value of `x` in - * the range which is closest to a root of `f(x)`. If `f(x)` - * has more than one root in the range, one will be chosen - * arbitrarily. If `f(x)` returns NaN, NaN will be returned; - * otherwise, this algorithm is guaranteed to succeed. - * - * Uses an algorithm based on TOMS748, which uses inverse cubic - * interpolation whenever possible, otherwise reverting to parabolic - * or secant interpolation. Compared to TOMS748, this implementation - * improves worst-case performance by a factor of more than 100, and - * typical performance by a factor of 2. For 80-bit reals, most - * problems require 8 to 15 calls to `f(x)` to achieve full machine - * precision. The worst-case performance (pathological cases) is - * approximately twice the number of bits. - * - * References: "On Enclosing Simple Roots of Nonlinear Equations", - * G. Alefeld, F.A. Potra, Yixun Shi, Mathematics of Computation 61, - * pp733-744 (1993). Fortran code available from - * $(HTTP www.netlib.org,www.netlib.org) as algorithm TOMS478. - * - */ -T findRoot(T, DF, DT)(scope DF f, const T a, const T b, - scope DT tolerance) //= (T a, T b) => false) -if ( - isFloatingPoint!T && - is(typeof(tolerance(T.init, T.init)) : bool) && - is(typeof(f(T.init)) == R, R) && isFloatingPoint!R - ) -{ - immutable fa = f(a); - if (fa == 0) - return a; - immutable fb = f(b); - if (fb == 0) - return b; - immutable r = findRoot(f, a, b, fa, fb, tolerance); - // Return the first value if it is smaller or NaN - return !(fabs(r[2]) > fabs(r[3])) ? r[0] : r[1]; -} - -///ditto -T findRoot(T, DF)(scope DF f, const T a, const T b) -{ - return findRoot(f, a, b, (T a, T b) => false); -} - -/** Find root of a real function f(x) by bracketing, allowing the - * termination condition to be specified. - * - * Params: - * - * f = Function to be analyzed - * - * ax = Left bound of initial range of `f` known to contain the - * root. - * - * bx = Right bound of initial range of `f` known to contain the - * root. - * - * fax = Value of `f(ax)`. - * - * fbx = Value of `f(bx)`. `fax` and `fbx` should have opposite signs. - * (`f(ax)` and `f(bx)` are commonly known in advance.) - * - * - * tolerance = Defines an early termination condition. Receives the - * current upper and lower bounds on the root. The - * delegate must return `true` when these bounds are - * acceptable. If this function always returns `false`, - * full machine precision will be achieved. - * - * Returns: - * - * A tuple consisting of two ranges. The first two elements are the - * range (in `x`) of the root, while the second pair of elements - * are the corresponding function values at those points. If an exact - * root was found, both of the first two elements will contain the - * root, and the second pair of elements will be 0. - */ -Tuple!(T, T, R, R) findRoot(T, R, DF, DT)(scope DF f, - const T ax, const T bx, const R fax, const R fbx, - scope DT tolerance) // = (T a, T b) => false) -if ( - isFloatingPoint!T && - is(typeof(tolerance(T.init, T.init)) : bool) && - is(typeof(f(T.init)) == R) && isFloatingPoint!R - ) -in -{ - assert(!ax.isNaN() && !bx.isNaN(), "Limits must not be NaN"); - assert(signbit(fax) != signbit(fbx), "Parameters must bracket the root."); -} -do -{ - // Author: Don Clugston. This code is (heavily) modified from TOMS748 - // (www.netlib.org). The changes to improve the worst-cast performance are - // entirely original. - - T a, b, d; // [a .. b] is our current bracket. d is the third best guess. - R fa, fb, fd; // Values of f at a, b, d. - bool done = false; // Has a root been found? - - // Allow ax and bx to be provided in reverse order - if (ax <= bx) - { - a = ax; fa = fax; - b = bx; fb = fbx; - } - else - { - a = bx; fa = fbx; - b = ax; fb = fax; - } - - // Test the function at point c; update brackets accordingly - void bracket(T c) - { - R fc = f(c); - if (fc == 0 || fc.isNaN()) // Exact solution, or NaN - { - a = c; - fa = fc; - d = c; - fd = fc; - done = true; - return; - } - - // Determine new enclosing interval - if (signbit(fa) != signbit(fc)) - { - d = b; - fd = fb; - b = c; - fb = fc; - } - else - { - d = a; - fd = fa; - a = c; - fa = fc; - } - } - - /* Perform a secant interpolation. If the result would lie on a or b, or if - a and b differ so wildly in magnitude that the result would be meaningless, - perform a bisection instead. - */ - static T secant_interpolate(T a, T b, R fa, R fb) - { - if (( ((a - b) == a) && b != 0) || (a != 0 && ((b - a) == b))) - { - // Catastrophic cancellation - if (a == 0) - a = copysign(T(0), b); - else if (b == 0) - b = copysign(T(0), a); - else if (signbit(a) != signbit(b)) - return 0; - T c = ieeeMean(a, b); - return c; - } - // avoid overflow - if (b - a > T.max) - return b / 2 + a / 2; - if (fb - fa > R.max) - return a - (b - a) / 2; - T c = a - (fa / (fb - fa)) * (b - a); - if (c == a || c == b) - return (a + b) / 2; - return c; - } - - /* Uses 'numsteps' newton steps to approximate the zero in [a .. b] of the - quadratic polynomial interpolating f(x) at a, b, and d. - Returns: - The approximate zero in [a .. b] of the quadratic polynomial. - */ - T newtonQuadratic(int numsteps) - { - // Find the coefficients of the quadratic polynomial. - immutable T a0 = fa; - immutable T a1 = (fb - fa)/(b - a); - immutable T a2 = ((fd - fb)/(d - b) - a1)/(d - a); - - // Determine the starting point of newton steps. - T c = oppositeSigns(a2, fa) ? a : b; - - // start the safeguarded newton steps. - foreach (int i; 0 .. numsteps) - { - immutable T pc = a0 + (a1 + a2 * (c - b))*(c - a); - immutable T pdc = a1 + a2*((2 * c) - (a + b)); - if (pdc == 0) - return a - a0 / a1; - else - c = c - pc / pdc; - } - return c; - } - - // On the first iteration we take a secant step: - if (fa == 0 || fa.isNaN()) - { - done = true; - b = a; - fb = fa; - } - else if (fb == 0 || fb.isNaN()) - { - done = true; - a = b; - fa = fb; - } - else - { - bracket(secant_interpolate(a, b, fa, fb)); - } - - // Starting with the second iteration, higher-order interpolation can - // be used. - int itnum = 1; // Iteration number - int baditer = 1; // Num bisections to take if an iteration is bad. - T c, e; // e is our fourth best guess - R fe; - -whileloop: - while (!done && (b != nextUp(a)) && !tolerance(a, b)) - { - T a0 = a, b0 = b; // record the brackets - - // Do two higher-order (cubic or parabolic) interpolation steps. - foreach (int QQ; 0 .. 2) - { - // Cubic inverse interpolation requires that - // all four function values fa, fb, fd, and fe are distinct; - // otherwise use quadratic interpolation. - bool distinct = (fa != fb) && (fa != fd) && (fa != fe) - && (fb != fd) && (fb != fe) && (fd != fe); - // The first time, cubic interpolation is impossible. - if (itnum<2) distinct = false; - bool ok = distinct; - if (distinct) - { - // Cubic inverse interpolation of f(x) at a, b, d, and e - immutable q11 = (d - e) * fd / (fe - fd); - immutable q21 = (b - d) * fb / (fd - fb); - immutable q31 = (a - b) * fa / (fb - fa); - immutable d21 = (b - d) * fd / (fd - fb); - immutable d31 = (a - b) * fb / (fb - fa); - - immutable q22 = (d21 - q11) * fb / (fe - fb); - immutable q32 = (d31 - q21) * fa / (fd - fa); - immutable d32 = (d31 - q21) * fd / (fd - fa); - immutable q33 = (d32 - q22) * fa / (fe - fa); - c = a + (q31 + q32 + q33); - if (c.isNaN() || (c <= a) || (c >= b)) - { - // DAC: If the interpolation predicts a or b, it's - // probable that it's the actual root. Only allow this if - // we're already close to the root. - if (c == a && a - b != a) - { - c = nextUp(a); - } - else if (c == b && a - b != -b) - { - c = nextDown(b); - } - else - { - ok = false; - } - } - } - if (!ok) - { - // DAC: Alefeld doesn't explain why the number of newton steps - // should vary. - c = newtonQuadratic(distinct ? 3 : 2); - if (c.isNaN() || (c <= a) || (c >= b)) - { - // Failure, try a secant step: - c = secant_interpolate(a, b, fa, fb); - } - } - ++itnum; - e = d; - fe = fd; - bracket(c); - if (done || ( b == nextUp(a)) || tolerance(a, b)) - break whileloop; - if (itnum == 2) - continue whileloop; - } - - // Now we take a double-length secant step: - T u; - R fu; - if (fabs(fa) < fabs(fb)) - { - u = a; - fu = fa; - } - else - { - u = b; - fu = fb; - } - c = u - 2 * (fu / (fb - fa)) * (b - a); - - // DAC: If the secant predicts a value equal to an endpoint, it's - // probably false. - if (c == a || c == b || c.isNaN() || fabs(c - u) > (b - a) / 2) - { - if ((a-b) == a || (b-a) == b) - { - if ((a>0 && b<0) || (a<0 && b>0)) - c = 0; - else - { - if (a == 0) - c = ieeeMean(copysign(T(0), b), b); - else if (b == 0) - c = ieeeMean(copysign(T(0), a), a); - else - c = ieeeMean(a, b); - } - } - else - { - c = a + (b - a) / 2; - } - } - e = d; - fe = fd; - bracket(c); - if (done || (b == nextUp(a)) || tolerance(a, b)) - break; - - // IMPROVE THE WORST-CASE PERFORMANCE - // We must ensure that the bounds reduce by a factor of 2 - // in binary space! every iteration. If we haven't achieved this - // yet, or if we don't yet know what the exponent is, - // perform a binary chop. - - if ((a == 0 || b == 0 || - (fabs(a) >= T(0.5) * fabs(b) && fabs(b) >= T(0.5) * fabs(a))) - && (b - a) < T(0.25) * (b0 - a0)) - { - baditer = 1; - continue; - } - - // DAC: If this happens on consecutive iterations, we probably have a - // pathological function. Perform a number of bisections equal to the - // total number of consecutive bad iterations. - - if ((b - a) < T(0.25) * (b0 - a0)) - baditer = 1; - foreach (int QQ; 0 .. baditer) - { - e = d; - fe = fd; - - T w; - if ((a>0 && b<0) || (a<0 && b>0)) - w = 0; - else - { - T usea = a; - T useb = b; - if (a == 0) - usea = copysign(T(0), b); - else if (b == 0) - useb = copysign(T(0), a); - w = ieeeMean(usea, useb); - } - bracket(w); - } - ++baditer; - } - return Tuple!(T, T, R, R)(a, b, fa, fb); -} - -///ditto -Tuple!(T, T, R, R) findRoot(T, R, DF)(scope DF f, - const T ax, const T bx, const R fax, const R fbx) -{ - return findRoot(f, ax, bx, fax, fbx, (T a, T b) => false); -} - -///ditto -T findRoot(T, R)(scope R delegate(T) f, const T a, const T b, - scope bool delegate(T lo, T hi) tolerance = (T a, T b) => false) -{ - return findRoot!(T, R delegate(T), bool delegate(T lo, T hi))(f, a, b, tolerance); -} - -@safe nothrow unittest -{ - int numProblems = 0; - int numCalls; - - void testFindRoot(real delegate(real) @nogc @safe nothrow pure f , real x1, real x2) @nogc @safe nothrow pure - { - //numCalls=0; - //++numProblems; - assert(!x1.isNaN() && !x2.isNaN()); - assert(signbit(f(x1)) != signbit(f(x2))); - auto result = findRoot(f, x1, x2, f(x1), f(x2), - (real lo, real hi) { return false; }); - - auto flo = f(result[0]); - auto fhi = f(result[1]); - if (flo != 0) - { - assert(oppositeSigns(flo, fhi)); - } - } - - // Test functions - real cubicfn(real x) @nogc @safe nothrow pure - { - //++numCalls; - if (x>float.max) - x = float.max; - if (x<-float.max) - x = -float.max; - // This has a single real root at -59.286543284815 - return 0.386*x*x*x + 23*x*x + 15.7*x + 525.2; - } - // Test a function with more than one root. - real multisine(real x) { ++numCalls; return sin(x); } - testFindRoot( &multisine, 6, 90); - testFindRoot(&cubicfn, -100, 100); - testFindRoot( &cubicfn, -double.max, real.max); - - -/* Tests from the paper: - * "On Enclosing Simple Roots of Nonlinear Equations", G. Alefeld, F.A. Potra, - * Yixun Shi, Mathematics of Computation 61, pp733-744 (1993). - */ - // Parameters common to many alefeld tests. - int n; - real ale_a, ale_b; - - int powercalls = 0; - - real power(real x) - { - ++powercalls; - ++numCalls; - return pow(x, n) + double.min_normal; - } - int [] power_nvals = [3, 5, 7, 9, 19, 25]; - // Alefeld paper states that pow(x,n) is a very poor case, where bisection - // outperforms his method, and gives total numcalls = - // 921 for bisection (2.4 calls per bit), 1830 for Alefeld (4.76/bit), - // 2624 for brent (6.8/bit) - // ... but that is for double, not real80. - // This poor performance seems mainly due to catastrophic cancellation, - // which is avoided here by the use of ieeeMean(). - // I get: 231 (0.48/bit). - // IE this is 10X faster in Alefeld's worst case - numProblems=0; - foreach (k; power_nvals) - { - n = k; - testFindRoot(&power, -1, 10); - } - - int powerProblems = numProblems; - - // Tests from Alefeld paper - - int [9] alefeldSums; - real alefeld0(real x) - { - ++alefeldSums[0]; - ++numCalls; - real q = sin(x) - x/2; - for (int i=1; i<20; ++i) - q+=(2*i-5.0)*(2*i-5.0)/((x-i*i)*(x-i*i)*(x-i*i)); - return q; - } - real alefeld1(real x) - { - ++numCalls; - ++alefeldSums[1]; - return ale_a*x + exp(ale_b * x); - } - real alefeld2(real x) - { - ++numCalls; - ++alefeldSums[2]; - return pow(x, n) - ale_a; - } - real alefeld3(real x) - { - ++numCalls; - ++alefeldSums[3]; - return (1.0 +pow(1.0L-n, 2))*x - pow(1.0L-n*x, 2); - } - real alefeld4(real x) - { - ++numCalls; - ++alefeldSums[4]; - return x*x - pow(1-x, n); - } - real alefeld5(real x) - { - ++numCalls; - ++alefeldSums[5]; - return (1+pow(1.0L-n, 4))*x - pow(1.0L-n*x, 4); - } - real alefeld6(real x) - { - ++numCalls; - ++alefeldSums[6]; - return exp(-n*x)*(x-1.01L) + pow(x, n); - } - real alefeld7(real x) - { - ++numCalls; - ++alefeldSums[7]; - return (n*x-1)/((n-1)*x); - } - - numProblems=0; - testFindRoot(&alefeld0, PI_2, PI); - for (n=1; n <= 10; ++n) - { - testFindRoot(&alefeld0, n*n+1e-9L, (n+1)*(n+1)-1e-9L); - } - ale_a = -40; ale_b = -1; - testFindRoot(&alefeld1, -9, 31); - ale_a = -100; ale_b = -2; - testFindRoot(&alefeld1, -9, 31); - ale_a = -200; ale_b = -3; - testFindRoot(&alefeld1, -9, 31); - int [] nvals_3 = [1, 2, 5, 10, 15, 20]; - int [] nvals_5 = [1, 2, 4, 5, 8, 15, 20]; - int [] nvals_6 = [1, 5, 10, 15, 20]; - int [] nvals_7 = [2, 5, 15, 20]; - - for (int i=4; i<12; i+=2) - { - n = i; - ale_a = 0.2; - testFindRoot(&alefeld2, 0, 5); - ale_a=1; - testFindRoot(&alefeld2, 0.95, 4.05); - testFindRoot(&alefeld2, 0, 1.5); - } - foreach (i; nvals_3) - { - n=i; - testFindRoot(&alefeld3, 0, 1); - } - foreach (i; nvals_3) - { - n=i; - testFindRoot(&alefeld4, 0, 1); - } - foreach (i; nvals_5) - { - n=i; - testFindRoot(&alefeld5, 0, 1); - } - foreach (i; nvals_6) - { - n=i; - testFindRoot(&alefeld6, 0, 1); - } - foreach (i; nvals_7) - { - n=i; - testFindRoot(&alefeld7, 0.01L, 1); - } - real worstcase(real x) - { - ++numCalls; - return x<0.3*real.max? -0.999e-3 : 1.0; - } - testFindRoot(&worstcase, -real.max, real.max); - - // just check that the double + float cases compile - findRoot((double x){ return 0.0; }, -double.max, double.max); - findRoot((float x){ return 0.0f; }, -float.max, float.max); - -/* - int grandtotal=0; - foreach (calls; alefeldSums) - { - grandtotal+=calls; - } - grandtotal-=2*numProblems; - printf("\nALEFELD TOTAL = %d avg = %f (alefeld avg=19.3 for double)\n", - grandtotal, (1.0*grandtotal)/numProblems); - powercalls -= 2*powerProblems; - printf("POWER TOTAL = %d avg = %f ", powercalls, - (1.0*powercalls)/powerProblems); -*/ - // https://issues.dlang.org/show_bug.cgi?id=14231 - auto xp = findRoot((float x) => x, 0f, 1f); - auto xn = findRoot((float x) => x, -1f, -0f); -} - -//regression control -@system unittest -{ - // @system due to the case in the 2nd line - static assert(__traits(compiles, findRoot((float x)=>cast(real) x, float.init, float.init))); - static assert(__traits(compiles, findRoot!real((x)=>cast(double) x, real.init, real.init))); - static assert(__traits(compiles, findRoot((real x)=>cast(double) x, real.init, real.init))); -} - -/++ -Find a real minimum of a real function `f(x)` via bracketing. -Given a function `f` and a range `(ax .. bx)`, -returns the value of `x` in the range which is closest to a minimum of `f(x)`. -`f` is never evaluted at the endpoints of `ax` and `bx`. -If `f(x)` has more than one minimum in the range, one will be chosen arbitrarily. -If `f(x)` returns NaN or -Infinity, `(x, f(x), NaN)` will be returned; -otherwise, this algorithm is guaranteed to succeed. - -Params: - f = Function to be analyzed - ax = Left bound of initial range of f known to contain the minimum. - bx = Right bound of initial range of f known to contain the minimum. - relTolerance = Relative tolerance. - absTolerance = Absolute tolerance. - -Preconditions: - `ax` and `bx` shall be finite reals. $(BR) - `relTolerance` shall be normal positive real. $(BR) - `absTolerance` shall be normal positive real no less then `T.epsilon*2`. - -Returns: - A tuple consisting of `x`, `y = f(x)` and `error = 3 * (absTolerance * fabs(x) + relTolerance)`. - - The method used is a combination of golden section search and -successive parabolic interpolation. Convergence is never much slower -than that for a Fibonacci search. - -References: - "Algorithms for Minimization without Derivatives", Richard Brent, Prentice-Hall, Inc. (1973) - -See_Also: $(LREF findRoot), $(REF isNormal, std,math) -+/ -Tuple!(T, "x", Unqual!(ReturnType!DF), "y", T, "error") -findLocalMin(T, DF)( - scope DF f, - const T ax, - const T bx, - const T relTolerance = sqrt(T.epsilon), - const T absTolerance = sqrt(T.epsilon), - ) -if (isFloatingPoint!T - && __traits(compiles, {T _ = DF.init(T.init);})) -in -{ - assert(isFinite(ax), "ax is not finite"); - assert(isFinite(bx), "bx is not finite"); - assert(isNormal(relTolerance), "relTolerance is not normal floating point number"); - assert(isNormal(absTolerance), "absTolerance is not normal floating point number"); - assert(relTolerance >= 0, "absTolerance is not positive"); - assert(absTolerance >= T.epsilon*2, "absTolerance is not greater then `2*T.epsilon`"); -} -out (result) -{ - assert(isFinite(result.x)); -} -do -{ - alias R = Unqual!(CommonType!(ReturnType!DF, T)); - // c is the squared inverse of the golden ratio - // (3 - sqrt(5))/2 - // Value obtained from Wolfram Alpha. - enum T c = 0x0.61c8864680b583ea0c633f9fa31237p+0L; - enum T cm1 = 0x0.9e3779b97f4a7c15f39cc0605cedc8p+0L; - R tolerance; - T a = ax > bx ? bx : ax; - T b = ax > bx ? ax : bx; - // sequence of declarations suitable for SIMD instructions - T v = a * cm1 + b * c; - assert(isFinite(v)); - R fv = f(v); - if (isNaN(fv) || fv == -T.infinity) - { - return typeof(return)(v, fv, T.init); - } - T w = v; - R fw = fv; - T x = v; - R fx = fv; - size_t i; - for (R d = 0, e = 0;;) - { - i++; - T m = (a + b) / 2; - // This fix is not part of the original algorithm - if (!isFinite(m)) // fix infinity loop. Issue can be reproduced in R. - { - m = a / 2 + b / 2; - if (!isFinite(m)) // fast-math compiler switch is enabled - { - //SIMD instructions can be used by compiler, do not reduce declarations - int a_exp = void; - int b_exp = void; - immutable an = frexp(a, a_exp); - immutable bn = frexp(b, b_exp); - immutable am = ldexp(an, a_exp-1); - immutable bm = ldexp(bn, b_exp-1); - m = am + bm; - if (!isFinite(m)) // wrong input: constraints are disabled in release mode - { - return typeof(return).init; - } - } - } - tolerance = absTolerance * fabs(x) + relTolerance; - immutable t2 = tolerance * 2; - // check stopping criterion - if (!(fabs(x - m) > t2 - (b - a) / 2)) - { - break; - } - R p = 0; - R q = 0; - R r = 0; - // fit parabola - if (fabs(e) > tolerance) - { - immutable xw = x - w; - immutable fxw = fx - fw; - immutable xv = x - v; - immutable fxv = fx - fv; - immutable xwfxv = xw * fxv; - immutable xvfxw = xv * fxw; - p = xv * xvfxw - xw * xwfxv; - q = (xvfxw - xwfxv) * 2; - if (q > 0) - p = -p; - else - q = -q; - r = e; - e = d; - } - T u; - // a parabolic-interpolation step - if (fabs(p) < fabs(q * r / 2) && p > q * (a - x) && p < q * (b - x)) - { - d = p / q; - u = x + d; - // f must not be evaluated too close to a or b - if (u - a < t2 || b - u < t2) - d = x < m ? tolerance : -tolerance; - } - // a golden-section step - else - { - e = (x < m ? b : a) - x; - d = c * e; - } - // f must not be evaluated too close to x - u = x + (fabs(d) >= tolerance ? d : d > 0 ? tolerance : -tolerance); - immutable fu = f(u); - if (isNaN(fu) || fu == -T.infinity) - { - return typeof(return)(u, fu, T.init); - } - // update a, b, v, w, and x - if (fu <= fx) - { - (u < x ? b : a) = x; - v = w; fv = fw; - w = x; fw = fx; - x = u; fx = fu; - } - else - { - (u < x ? a : b) = u; - if (fu <= fw || w == x) - { - v = w; fv = fw; - w = u; fw = fu; - } - else if (fu <= fv || v == x || v == w) - { // do not remove this braces - v = u; fv = fu; - } - } - } - return typeof(return)(x, fx, tolerance * 3); -} - -/// -@safe unittest -{ - import std.math.operations : isClose; - - auto ret = findLocalMin((double x) => (x-4)^^2, -1e7, 1e7); - assert(ret.x.isClose(4.0)); - assert(ret.y.isClose(0.0, 0.0, 1e-10)); -} - -@safe unittest -{ - import std.meta : AliasSeq; - static foreach (T; AliasSeq!(double, float, real)) - { - { - auto ret = findLocalMin!T((T x) => (x-4)^^2, T.min_normal, 1e7); - assert(ret.x.isClose(T(4))); - assert(ret.y.isClose(T(0), 0.0, T.epsilon)); - } - { - auto ret = findLocalMin!T((T x) => fabs(x-1), -T.max/4, T.max/4, T.min_normal, 2*T.epsilon); - assert(isClose(ret.x, T(1))); - assert(isClose(ret.y, T(0), 0.0, T.epsilon)); - assert(ret.error <= 10 * T.epsilon); - } - { - auto ret = findLocalMin!T((T x) => T.init, 0, 1, T.min_normal, 2*T.epsilon); - assert(!ret.x.isNaN); - assert(ret.y.isNaN); - assert(ret.error.isNaN); - } - { - auto ret = findLocalMin!T((T x) => log(x), 0, 1, T.min_normal, 2*T.epsilon); - assert(ret.error < 3.00001 * ((2*T.epsilon)*fabs(ret.x)+ T.min_normal)); - assert(ret.x >= 0 && ret.x <= ret.error); - } - { - auto ret = findLocalMin!T((T x) => log(x), 0, T.max, T.min_normal, 2*T.epsilon); - assert(ret.y < -18); - assert(ret.error < 5e-08); - assert(ret.x >= 0 && ret.x <= ret.error); - } - { - auto ret = findLocalMin!T((T x) => -fabs(x), -1, 1, T.min_normal, 2*T.epsilon); - assert(ret.x.fabs.isClose(T(1))); - assert(ret.y.fabs.isClose(T(1))); - assert(ret.error.isClose(T(0), 0.0, 100*T.epsilon)); - } - } -} - -/** -Computes $(LINK2 https://en.wikipedia.org/wiki/Euclidean_distance, -Euclidean distance) between input ranges `a` and -`b`. The two ranges must have the same length. The three-parameter -version stops computation as soon as the distance is greater than or -equal to `limit` (this is useful to save computation if a small -distance is sought). - */ -CommonType!(ElementType!(Range1), ElementType!(Range2)) -euclideanDistance(Range1, Range2)(Range1 a, Range2 b) -if (isInputRange!(Range1) && isInputRange!(Range2)) -{ - enum bool haveLen = hasLength!(Range1) && hasLength!(Range2); - static if (haveLen) assert(a.length == b.length); - Unqual!(typeof(return)) result = 0; - for (; !a.empty; a.popFront(), b.popFront()) - { - immutable t = a.front - b.front; - result += t * t; - } - static if (!haveLen) assert(b.empty); - return sqrt(result); -} - -/// Ditto -CommonType!(ElementType!(Range1), ElementType!(Range2)) -euclideanDistance(Range1, Range2, F)(Range1 a, Range2 b, F limit) -if (isInputRange!(Range1) && isInputRange!(Range2)) -{ - limit *= limit; - enum bool haveLen = hasLength!(Range1) && hasLength!(Range2); - static if (haveLen) assert(a.length == b.length); - Unqual!(typeof(return)) result = 0; - for (; ; a.popFront(), b.popFront()) - { - if (a.empty) - { - static if (!haveLen) assert(b.empty); - break; - } - immutable t = a.front - b.front; - result += t * t; - if (result >= limit) break; - } - return sqrt(result); -} - -@safe unittest -{ - import std.meta : AliasSeq; - static foreach (T; AliasSeq!(double, const double, immutable double)) - {{ - T[] a = [ 1.0, 2.0, ]; - T[] b = [ 4.0, 6.0, ]; - assert(euclideanDistance(a, b) == 5); - assert(euclideanDistance(a, b, 6) == 5); - assert(euclideanDistance(a, b, 5) == 5); - assert(euclideanDistance(a, b, 4) == 5); - assert(euclideanDistance(a, b, 2) == 3); - }} -} - -/** -Computes the $(LINK2 https://en.wikipedia.org/wiki/Dot_product, -dot product) of input ranges `a` and $(D -b). The two ranges must have the same length. If both ranges define -length, the check is done once; otherwise, it is done at each -iteration. - */ -CommonType!(ElementType!(Range1), ElementType!(Range2)) -dotProduct(Range1, Range2)(Range1 a, Range2 b) -if (isInputRange!(Range1) && isInputRange!(Range2) && - !(isArray!(Range1) && isArray!(Range2))) -{ - enum bool haveLen = hasLength!(Range1) && hasLength!(Range2); - static if (haveLen) assert(a.length == b.length); - Unqual!(typeof(return)) result = 0; - for (; !a.empty; a.popFront(), b.popFront()) - { - result += a.front * b.front; - } - static if (!haveLen) assert(b.empty); - return result; -} - -/// Ditto -CommonType!(F1, F2) -dotProduct(F1, F2)(in F1[] avector, in F2[] bvector) -{ - immutable n = avector.length; - assert(n == bvector.length); - auto avec = avector.ptr, bvec = bvector.ptr; - Unqual!(typeof(return)) sum0 = 0, sum1 = 0; - - const all_endp = avec + n; - const smallblock_endp = avec + (n & ~3); - const bigblock_endp = avec + (n & ~15); - - for (; avec != bigblock_endp; avec += 16, bvec += 16) - { - sum0 += avec[0] * bvec[0]; - sum1 += avec[1] * bvec[1]; - sum0 += avec[2] * bvec[2]; - sum1 += avec[3] * bvec[3]; - sum0 += avec[4] * bvec[4]; - sum1 += avec[5] * bvec[5]; - sum0 += avec[6] * bvec[6]; - sum1 += avec[7] * bvec[7]; - sum0 += avec[8] * bvec[8]; - sum1 += avec[9] * bvec[9]; - sum0 += avec[10] * bvec[10]; - sum1 += avec[11] * bvec[11]; - sum0 += avec[12] * bvec[12]; - sum1 += avec[13] * bvec[13]; - sum0 += avec[14] * bvec[14]; - sum1 += avec[15] * bvec[15]; - } - - for (; avec != smallblock_endp; avec += 4, bvec += 4) - { - sum0 += avec[0] * bvec[0]; - sum1 += avec[1] * bvec[1]; - sum0 += avec[2] * bvec[2]; - sum1 += avec[3] * bvec[3]; - } - - sum0 += sum1; - - /* Do trailing portion in naive loop. */ - while (avec != all_endp) - { - sum0 += *avec * *bvec; - ++avec; - ++bvec; - } - - return sum0; -} - -/// ditto -F dotProduct(F, uint N)(const ref scope F[N] a, const ref scope F[N] b) -if (N <= 16) -{ - F sum0 = 0; - F sum1 = 0; - static foreach (i; 0 .. N / 2) - { - sum0 += a[i*2] * b[i*2]; - sum1 += a[i*2+1] * b[i*2+1]; - } - static if (N % 2 == 1) - { - sum0 += a[N-1] * b[N-1]; - } - return sum0 + sum1; -} - -@system unittest -{ - // @system due to dotProduct and assertCTFEable - import std.exception : assertCTFEable; - import std.meta : AliasSeq; - static foreach (T; AliasSeq!(double, const double, immutable double)) - {{ - T[] a = [ 1.0, 2.0, ]; - T[] b = [ 4.0, 6.0, ]; - assert(dotProduct(a, b) == 16); - assert(dotProduct([1, 3, -5], [4, -2, -1]) == 3); - // Test with fixed-length arrays. - T[2] c = [ 1.0, 2.0, ]; - T[2] d = [ 4.0, 6.0, ]; - assert(dotProduct(c, d) == 16); - T[3] e = [1, 3, -5]; - T[3] f = [4, -2, -1]; - assert(dotProduct(e, f) == 3); - }} - - // Make sure the unrolled loop codepath gets tested. - static const x = - [1.0, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22]; - static const y = - [2.0, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23]; - assertCTFEable!({ assert(dotProduct(x, y) == 4048); }); -} - -/** -Computes the $(LINK2 https://en.wikipedia.org/wiki/Cosine_similarity, -cosine similarity) of input ranges `a` and $(D -b). The two ranges must have the same length. If both ranges define -length, the check is done once; otherwise, it is done at each -iteration. If either range has all-zero elements, return 0. - */ -CommonType!(ElementType!(Range1), ElementType!(Range2)) -cosineSimilarity(Range1, Range2)(Range1 a, Range2 b) -if (isInputRange!(Range1) && isInputRange!(Range2)) -{ - enum bool haveLen = hasLength!(Range1) && hasLength!(Range2); - static if (haveLen) assert(a.length == b.length); - Unqual!(typeof(return)) norma = 0, normb = 0, dotprod = 0; - for (; !a.empty; a.popFront(), b.popFront()) - { - immutable t1 = a.front, t2 = b.front; - norma += t1 * t1; - normb += t2 * t2; - dotprod += t1 * t2; - } - static if (!haveLen) assert(b.empty); - if (norma == 0 || normb == 0) return 0; - return dotprod / sqrt(norma * normb); -} - -@safe unittest -{ - import std.meta : AliasSeq; - static foreach (T; AliasSeq!(double, const double, immutable double)) - {{ - T[] a = [ 1.0, 2.0, ]; - T[] b = [ 4.0, 3.0, ]; - assert(isClose( - cosineSimilarity(a, b), 10.0 / sqrt(5.0 * 25), - 0.01)); - }} -} - -/** -Normalizes values in `range` by multiplying each element with a -number chosen such that values sum up to `sum`. If elements in $(D -range) sum to zero, assigns $(D sum / range.length) to -all. Normalization makes sense only if all elements in `range` are -positive. `normalize` assumes that is the case without checking it. - -Returns: `true` if normalization completed normally, `false` if -all elements in `range` were zero or if `range` is empty. - */ -bool normalize(R)(R range, ElementType!(R) sum = 1) -if (isForwardRange!(R)) -{ - ElementType!(R) s = 0; - // Step 1: Compute sum and length of the range - static if (hasLength!(R)) - { - const length = range.length; - foreach (e; range) - { - s += e; - } - } - else - { - uint length = 0; - foreach (e; range) - { - s += e; - ++length; - } - } - // Step 2: perform normalization - if (s == 0) - { - if (length) - { - immutable f = sum / range.length; - foreach (ref e; range) e = f; - } - return false; - } - // The path most traveled - assert(s >= 0); - immutable f = sum / s; - foreach (ref e; range) - e *= f; - return true; -} - -/// -@safe unittest -{ - double[] a = []; - assert(!normalize(a)); - a = [ 1.0, 3.0 ]; - assert(normalize(a)); - assert(a == [ 0.25, 0.75 ]); - assert(normalize!(typeof(a))(a, 50)); // a = [12.5, 37.5] - a = [ 0.0, 0.0 ]; - assert(!normalize(a)); - assert(a == [ 0.5, 0.5 ]); -} - -/** -Compute the sum of binary logarithms of the input range `r`. -The error of this method is much smaller than with a naive sum of log2. - */ -ElementType!Range sumOfLog2s(Range)(Range r) -if (isInputRange!Range && isFloatingPoint!(ElementType!Range)) -{ - long exp = 0; - Unqual!(typeof(return)) x = 1; - foreach (e; r) - { - if (e < 0) - return typeof(return).nan; - int lexp = void; - x *= frexp(e, lexp); - exp += lexp; - if (x < 0.5) - { - x *= 2; - exp--; - } - } - return exp + log2(x); -} - -/// -@safe unittest -{ - import std.math.traits : isNaN; - - assert(sumOfLog2s(new double[0]) == 0); - assert(sumOfLog2s([0.0L]) == -real.infinity); - assert(sumOfLog2s([-0.0L]) == -real.infinity); - assert(sumOfLog2s([2.0L]) == 1); - assert(sumOfLog2s([-2.0L]).isNaN()); - assert(sumOfLog2s([real.nan]).isNaN()); - assert(sumOfLog2s([-real.nan]).isNaN()); - assert(sumOfLog2s([real.infinity]) == real.infinity); - assert(sumOfLog2s([-real.infinity]).isNaN()); - assert(sumOfLog2s([ 0.25, 0.25, 0.25, 0.125 ]) == -9); -} - -/** -Computes $(LINK2 https://en.wikipedia.org/wiki/Entropy_(information_theory), -_entropy) of input range `r` in bits. This -function assumes (without checking) that the values in `r` are all -in $(D [0, 1]). For the entropy to be meaningful, often `r` should -be normalized too (i.e., its values should sum to 1). The -two-parameter version stops evaluating as soon as the intermediate -result is greater than or equal to `max`. - */ -ElementType!Range entropy(Range)(Range r) -if (isInputRange!Range) -{ - Unqual!(typeof(return)) result = 0.0; - for (;!r.empty; r.popFront) - { - if (!r.front) continue; - result -= r.front * log2(r.front); - } - return result; -} - -/// Ditto -ElementType!Range entropy(Range, F)(Range r, F max) -if (isInputRange!Range && - !is(CommonType!(ElementType!Range, F) == void)) -{ - Unqual!(typeof(return)) result = 0.0; - for (;!r.empty; r.popFront) - { - if (!r.front) continue; - result -= r.front * log2(r.front); - if (result >= max) break; - } - return result; -} - -@safe unittest -{ - import std.meta : AliasSeq; - static foreach (T; AliasSeq!(double, const double, immutable double)) - {{ - T[] p = [ 0.0, 0, 0, 1 ]; - assert(entropy(p) == 0); - p = [ 0.25, 0.25, 0.25, 0.25 ]; - assert(entropy(p) == 2); - assert(entropy(p, 1) == 1); - }} -} - -/** -Computes the $(LINK2 https://en.wikipedia.org/wiki/Kullback%E2%80%93Leibler_divergence, -Kullback-Leibler divergence) between input ranges -`a` and `b`, which is the sum $(D ai * log(ai / bi)). The base -of logarithm is 2. The ranges are assumed to contain elements in $(D -[0, 1]). Usually the ranges are normalized probability distributions, -but this is not required or checked by $(D -kullbackLeiblerDivergence). If any element `bi` is zero and the -corresponding element `ai` nonzero, returns infinity. (Otherwise, -if $(D ai == 0 && bi == 0), the term $(D ai * log(ai / bi)) is -considered zero.) If the inputs are normalized, the result is -positive. - */ -CommonType!(ElementType!Range1, ElementType!Range2) -kullbackLeiblerDivergence(Range1, Range2)(Range1 a, Range2 b) -if (isInputRange!(Range1) && isInputRange!(Range2)) -{ - enum bool haveLen = hasLength!(Range1) && hasLength!(Range2); - static if (haveLen) assert(a.length == b.length); - Unqual!(typeof(return)) result = 0; - for (; !a.empty; a.popFront(), b.popFront()) - { - immutable t1 = a.front; - if (t1 == 0) continue; - immutable t2 = b.front; - if (t2 == 0) return result.infinity; - assert(t1 > 0 && t2 > 0); - result += t1 * log2(t1 / t2); - } - static if (!haveLen) assert(b.empty); - return result; -} - -/// -@safe unittest -{ - import std.math.operations : isClose; - - double[] p = [ 0.0, 0, 0, 1 ]; - assert(kullbackLeiblerDivergence(p, p) == 0); - double[] p1 = [ 0.25, 0.25, 0.25, 0.25 ]; - assert(kullbackLeiblerDivergence(p1, p1) == 0); - assert(kullbackLeiblerDivergence(p, p1) == 2); - assert(kullbackLeiblerDivergence(p1, p) == double.infinity); - double[] p2 = [ 0.2, 0.2, 0.2, 0.4 ]; - assert(isClose(kullbackLeiblerDivergence(p1, p2), 0.0719281, 1e-5)); - assert(isClose(kullbackLeiblerDivergence(p2, p1), 0.0780719, 1e-5)); -} - -/** -Computes the $(LINK2 https://en.wikipedia.org/wiki/Jensen%E2%80%93Shannon_divergence, -Jensen-Shannon divergence) between `a` and $(D -b), which is the sum $(D (ai * log(2 * ai / (ai + bi)) + bi * log(2 * -bi / (ai + bi))) / 2). The base of logarithm is 2. The ranges are -assumed to contain elements in $(D [0, 1]). Usually the ranges are -normalized probability distributions, but this is not required or -checked by `jensenShannonDivergence`. If the inputs are normalized, -the result is bounded within $(D [0, 1]). The three-parameter version -stops evaluations as soon as the intermediate result is greater than -or equal to `limit`. - */ -CommonType!(ElementType!Range1, ElementType!Range2) -jensenShannonDivergence(Range1, Range2)(Range1 a, Range2 b) -if (isInputRange!Range1 && isInputRange!Range2 && - is(CommonType!(ElementType!Range1, ElementType!Range2))) -{ - enum bool haveLen = hasLength!(Range1) && hasLength!(Range2); - static if (haveLen) assert(a.length == b.length); - Unqual!(typeof(return)) result = 0; - for (; !a.empty; a.popFront(), b.popFront()) - { - immutable t1 = a.front; - immutable t2 = b.front; - immutable avg = (t1 + t2) / 2; - if (t1 != 0) - { - result += t1 * log2(t1 / avg); - } - if (t2 != 0) - { - result += t2 * log2(t2 / avg); - } - } - static if (!haveLen) assert(b.empty); - return result / 2; -} - -/// Ditto -CommonType!(ElementType!Range1, ElementType!Range2) -jensenShannonDivergence(Range1, Range2, F)(Range1 a, Range2 b, F limit) -if (isInputRange!Range1 && isInputRange!Range2 && - is(typeof(CommonType!(ElementType!Range1, ElementType!Range2).init - >= F.init) : bool)) -{ - enum bool haveLen = hasLength!(Range1) && hasLength!(Range2); - static if (haveLen) assert(a.length == b.length); - Unqual!(typeof(return)) result = 0; - limit *= 2; - for (; !a.empty; a.popFront(), b.popFront()) - { - immutable t1 = a.front; - immutable t2 = b.front; - immutable avg = (t1 + t2) / 2; - if (t1 != 0) - { - result += t1 * log2(t1 / avg); - } - if (t2 != 0) - { - result += t2 * log2(t2 / avg); - } - if (result >= limit) break; - } - static if (!haveLen) assert(b.empty); - return result / 2; -} - -/// -@safe unittest -{ - import std.math.operations : isClose; - - double[] p = [ 0.0, 0, 0, 1 ]; - assert(jensenShannonDivergence(p, p) == 0); - double[] p1 = [ 0.25, 0.25, 0.25, 0.25 ]; - assert(jensenShannonDivergence(p1, p1) == 0); - assert(isClose(jensenShannonDivergence(p1, p), 0.548795, 1e-5)); - double[] p2 = [ 0.2, 0.2, 0.2, 0.4 ]; - assert(isClose(jensenShannonDivergence(p1, p2), 0.0186218, 1e-5)); - assert(isClose(jensenShannonDivergence(p2, p1), 0.0186218, 1e-5)); - assert(isClose(jensenShannonDivergence(p2, p1, 0.005), 0.00602366, 1e-5)); -} - -/** -The so-called "all-lengths gap-weighted string kernel" computes a -similarity measure between `s` and `t` based on all of their -common subsequences of all lengths. Gapped subsequences are also -included. - -To understand what $(D gapWeightedSimilarity(s, t, lambda)) computes, -consider first the case $(D lambda = 1) and the strings $(D s = -["Hello", "brave", "new", "world"]) and $(D t = ["Hello", "new", -"world"]). In that case, `gapWeightedSimilarity` counts the -following matches: - -$(OL $(LI three matches of length 1, namely `"Hello"`, `"new"`, -and `"world"`;) $(LI three matches of length 2, namely ($(D -"Hello", "new")), ($(D "Hello", "world")), and ($(D "new", "world"));) -$(LI one match of length 3, namely ($(D "Hello", "new", "world")).)) - -The call $(D gapWeightedSimilarity(s, t, 1)) simply counts all of -these matches and adds them up, returning 7. - ----- -string[] s = ["Hello", "brave", "new", "world"]; -string[] t = ["Hello", "new", "world"]; -assert(gapWeightedSimilarity(s, t, 1) == 7); ----- - -Note how the gaps in matching are simply ignored, for example ($(D -"Hello", "new")) is deemed as good a match as ($(D "new", -"world")). This may be too permissive for some applications. To -eliminate gapped matches entirely, use $(D lambda = 0): - ----- -string[] s = ["Hello", "brave", "new", "world"]; -string[] t = ["Hello", "new", "world"]; -assert(gapWeightedSimilarity(s, t, 0) == 4); ----- - -The call above eliminated the gapped matches ($(D "Hello", "new")), -($(D "Hello", "world")), and ($(D "Hello", "new", "world")) from the -tally. That leaves only 4 matches. - -The most interesting case is when gapped matches still participate in -the result, but not as strongly as ungapped matches. The result will -be a smooth, fine-grained similarity measure between the input -strings. This is where values of `lambda` between 0 and 1 enter -into play: gapped matches are $(I exponentially penalized with the -number of gaps) with base `lambda`. This means that an ungapped -match adds 1 to the return value; a match with one gap in either -string adds `lambda` to the return value; ...; a match with a total -of `n` gaps in both strings adds $(D pow(lambda, n)) to the return -value. In the example above, we have 4 matches without gaps, 2 matches -with one gap, and 1 match with three gaps. The latter match is ($(D -"Hello", "world")), which has two gaps in the first string and one gap -in the second string, totaling to three gaps. Summing these up we get -$(D 4 + 2 * lambda + pow(lambda, 3)). - ----- -string[] s = ["Hello", "brave", "new", "world"]; -string[] t = ["Hello", "new", "world"]; -assert(gapWeightedSimilarity(s, t, 0.5) == 4 + 0.5 * 2 + 0.125); ----- - -`gapWeightedSimilarity` is useful wherever a smooth similarity -measure between sequences allowing for approximate matches is -needed. The examples above are given with words, but any sequences -with elements comparable for equality are allowed, e.g. characters or -numbers. `gapWeightedSimilarity` uses a highly optimized dynamic -programming implementation that needs $(D 16 * min(s.length, -t.length)) extra bytes of memory and $(BIGOH s.length * t.length) time -to complete. - */ -F gapWeightedSimilarity(alias comp = "a == b", R1, R2, F)(R1 s, R2 t, F lambda) -if (isRandomAccessRange!(R1) && hasLength!(R1) && - isRandomAccessRange!(R2) && hasLength!(R2)) -{ - import core.exception : onOutOfMemoryError; - import core.stdc.stdlib : malloc, free; - import std.algorithm.mutation : swap; - import std.functional : binaryFun; - - if (s.length < t.length) return gapWeightedSimilarity(t, s, lambda); - if (!t.length) return 0; - - auto dpvi = cast(F*) malloc(F.sizeof * 2 * t.length); - if (!dpvi) - onOutOfMemoryError(); - - auto dpvi1 = dpvi + t.length; - scope(exit) free(dpvi < dpvi1 ? dpvi : dpvi1); - dpvi[0 .. t.length] = 0; - dpvi1[0] = 0; - immutable lambda2 = lambda * lambda; - - F result = 0; - foreach (i; 0 .. s.length) - { - const si = s[i]; - for (size_t j = 0;;) - { - F dpsij = void; - if (binaryFun!(comp)(si, t[j])) - { - dpsij = 1 + dpvi[j]; - result += dpsij; - } - else - { - dpsij = 0; - } - immutable j1 = j + 1; - if (j1 == t.length) break; - dpvi1[j1] = dpsij + lambda * (dpvi1[j] + dpvi[j1]) - - lambda2 * dpvi[j]; - j = j1; - } - swap(dpvi, dpvi1); - } - return result; -} - -@system unittest -{ - string[] s = ["Hello", "brave", "new", "world"]; - string[] t = ["Hello", "new", "world"]; - assert(gapWeightedSimilarity(s, t, 1) == 7); - assert(gapWeightedSimilarity(s, t, 0) == 4); - assert(gapWeightedSimilarity(s, t, 0.5) == 4 + 2 * 0.5 + 0.125); -} - -/** -The similarity per `gapWeightedSimilarity` has an issue in that it -grows with the lengths of the two strings, even though the strings are -not actually very similar. For example, the range $(D ["Hello", -"world"]) is increasingly similar with the range $(D ["Hello", -"world", "world", "world",...]) as more instances of `"world"` are -appended. To prevent that, `gapWeightedSimilarityNormalized` -computes a normalized version of the similarity that is computed as -$(D gapWeightedSimilarity(s, t, lambda) / -sqrt(gapWeightedSimilarity(s, t, lambda) * gapWeightedSimilarity(s, t, -lambda))). The function `gapWeightedSimilarityNormalized` (a -so-called normalized kernel) is bounded in $(D [0, 1]), reaches `0` -only for ranges that don't match in any position, and `1` only for -identical ranges. - -The optional parameters `sSelfSim` and `tSelfSim` are meant for -avoiding duplicate computation. Many applications may have already -computed $(D gapWeightedSimilarity(s, s, lambda)) and/or $(D -gapWeightedSimilarity(t, t, lambda)). In that case, they can be passed -as `sSelfSim` and `tSelfSim`, respectively. - */ -Select!(isFloatingPoint!(F), F, double) -gapWeightedSimilarityNormalized(alias comp = "a == b", R1, R2, F) - (R1 s, R2 t, F lambda, F sSelfSim = F.init, F tSelfSim = F.init) -if (isRandomAccessRange!(R1) && hasLength!(R1) && - isRandomAccessRange!(R2) && hasLength!(R2)) -{ - static bool uncomputed(F n) - { - static if (isFloatingPoint!(F)) - return isNaN(n); - else - return n == n.init; - } - if (uncomputed(sSelfSim)) - sSelfSim = gapWeightedSimilarity!(comp)(s, s, lambda); - if (sSelfSim == 0) return 0; - if (uncomputed(tSelfSim)) - tSelfSim = gapWeightedSimilarity!(comp)(t, t, lambda); - if (tSelfSim == 0) return 0; - - return gapWeightedSimilarity!(comp)(s, t, lambda) / - sqrt(cast(typeof(return)) sSelfSim * tSelfSim); -} - -/// -@system unittest -{ - import std.math.operations : isClose; - import std.math.algebraic : sqrt; - - string[] s = ["Hello", "brave", "new", "world"]; - string[] t = ["Hello", "new", "world"]; - assert(gapWeightedSimilarity(s, s, 1) == 15); - assert(gapWeightedSimilarity(t, t, 1) == 7); - assert(gapWeightedSimilarity(s, t, 1) == 7); - assert(isClose(gapWeightedSimilarityNormalized(s, t, 1), - 7.0 / sqrt(15.0 * 7), 0.01)); -} - -/** -Similar to `gapWeightedSimilarity`, just works in an incremental -manner by first revealing the matches of length 1, then gapped matches -of length 2, and so on. The memory requirement is $(BIGOH s.length * -t.length). The time complexity is $(BIGOH s.length * t.length) time -for computing each step. Continuing on the previous example: - -The implementation is based on the pseudocode in Fig. 4 of the paper -$(HTTP jmlr.csail.mit.edu/papers/volume6/rousu05a/rousu05a.pdf, -"Efcient Computation of Gapped Substring Kernels on Large Alphabets") -by Rousu et al., with additional algorithmic and systems-level -optimizations. - */ -struct GapWeightedSimilarityIncremental(Range, F = double) -if (isRandomAccessRange!(Range) && hasLength!(Range)) -{ - import core.stdc.stdlib : malloc, realloc, alloca, free; - -private: - Range s, t; - F currentValue = 0; - F* kl; - size_t gram = void; - F lambda = void, lambda2 = void; - -public: -/** -Constructs an object given two ranges `s` and `t` and a penalty -`lambda`. Constructor completes in $(BIGOH s.length * t.length) -time and computes all matches of length 1. - */ - this(Range s, Range t, F lambda) - { - import core.exception : onOutOfMemoryError; - - assert(lambda > 0); - this.gram = 0; - this.lambda = lambda; - this.lambda2 = lambda * lambda; // for efficiency only - - size_t iMin = size_t.max, jMin = size_t.max, - iMax = 0, jMax = 0; - /* initialize */ - Tuple!(size_t, size_t) * k0; - size_t k0len; - scope(exit) free(k0); - currentValue = 0; - foreach (i, si; s) - { - foreach (j; 0 .. t.length) - { - if (si != t[j]) continue; - k0 = cast(typeof(k0)) realloc(k0, ++k0len * (*k0).sizeof); - with (k0[k0len - 1]) - { - field[0] = i; - field[1] = j; - } - // Maintain the minimum and maximum i and j - if (iMin > i) iMin = i; - if (iMax < i) iMax = i; - if (jMin > j) jMin = j; - if (jMax < j) jMax = j; - } - } - - if (iMin > iMax) return; - assert(k0len); - - currentValue = k0len; - // Chop strings down to the useful sizes - s = s[iMin .. iMax + 1]; - t = t[jMin .. jMax + 1]; - this.s = s; - this.t = t; - - kl = cast(F*) malloc(s.length * t.length * F.sizeof); - if (!kl) - onOutOfMemoryError(); - - kl[0 .. s.length * t.length] = 0; - foreach (pos; 0 .. k0len) - { - with (k0[pos]) - { - kl[(field[0] - iMin) * t.length + field[1] -jMin] = lambda2; - } - } - } - - /** - Returns: `this`. - */ - ref GapWeightedSimilarityIncremental opSlice() - { - return this; - } - - /** - Computes the match of the popFront length. Completes in $(BIGOH s.length * - t.length) time. - */ - void popFront() - { - import std.algorithm.mutation : swap; - - // This is a large source of optimization: if similarity at - // the gram-1 level was 0, then we can safely assume - // similarity at the gram level is 0 as well. - if (empty) return; - - // Now attempt to match gapped substrings of length `gram' - ++gram; - currentValue = 0; - - auto Si = cast(F*) alloca(t.length * F.sizeof); - Si[0 .. t.length] = 0; - foreach (i; 0 .. s.length) - { - const si = s[i]; - F Sij_1 = 0; - F Si_1j_1 = 0; - auto kli = kl + i * t.length; - for (size_t j = 0;;) - { - const klij = kli[j]; - const Si_1j = Si[j]; - const tmp = klij + lambda * (Si_1j + Sij_1) - lambda2 * Si_1j_1; - // now update kl and currentValue - if (si == t[j]) - currentValue += kli[j] = lambda2 * Si_1j_1; - else - kli[j] = 0; - // commit to Si - Si[j] = tmp; - if (++j == t.length) break; - // get ready for the popFront step; virtually increment j, - // so essentially stuffj_1 <-- stuffj - Si_1j_1 = Si_1j; - Sij_1 = tmp; - } - } - currentValue /= pow(lambda, 2 * (gram + 1)); - - version (none) - { - Si_1[0 .. t.length] = 0; - kl[0 .. min(t.length, maxPerimeter + 1)] = 0; - foreach (i; 1 .. min(s.length, maxPerimeter + 1)) - { - auto kli = kl + i * t.length; - assert(s.length > i); - const si = s[i]; - auto kl_1i_1 = kl_1 + (i - 1) * t.length; - kli[0] = 0; - F lastS = 0; - foreach (j; 1 .. min(maxPerimeter - i + 1, t.length)) - { - immutable j_1 = j - 1; - immutable tmp = kl_1i_1[j_1] - + lambda * (Si_1[j] + lastS) - - lambda2 * Si_1[j_1]; - kl_1i_1[j_1] = float.nan; - Si_1[j_1] = lastS; - lastS = tmp; - if (si == t[j]) - { - currentValue += kli[j] = lambda2 * lastS; - } - else - { - kli[j] = 0; - } - } - Si_1[t.length - 1] = lastS; - } - currentValue /= pow(lambda, 2 * (gram + 1)); - // get ready for the popFront computation - swap(kl, kl_1); - } - } - - /** - Returns: The gapped similarity at the current match length (initially - 1, grows with each call to `popFront`). - */ - @property F front() { return currentValue; } - - /** - Returns: Whether there are more matches. - */ - @property bool empty() - { - if (currentValue) return false; - if (kl) - { - free(kl); - kl = null; - } - return true; - } -} - -/** -Ditto - */ -GapWeightedSimilarityIncremental!(R, F) gapWeightedSimilarityIncremental(R, F) -(R r1, R r2, F penalty) -{ - return typeof(return)(r1, r2, penalty); -} - -/// -@system unittest -{ - string[] s = ["Hello", "brave", "new", "world"]; - string[] t = ["Hello", "new", "world"]; - auto simIter = gapWeightedSimilarityIncremental(s, t, 1.0); - assert(simIter.front == 3); // three 1-length matches - simIter.popFront(); - assert(simIter.front == 3); // three 2-length matches - simIter.popFront(); - assert(simIter.front == 1); // one 3-length match - simIter.popFront(); - assert(simIter.empty); // no more match -} - -@system unittest -{ - import std.conv : text; - string[] s = ["Hello", "brave", "new", "world"]; - string[] t = ["Hello", "new", "world"]; - auto simIter = gapWeightedSimilarityIncremental(s, t, 1.0); - //foreach (e; simIter) writeln(e); - assert(simIter.front == 3); // three 1-length matches - simIter.popFront(); - assert(simIter.front == 3, text(simIter.front)); // three 2-length matches - simIter.popFront(); - assert(simIter.front == 1); // one 3-length matches - simIter.popFront(); - assert(simIter.empty); // no more match - - s = ["Hello"]; - t = ["bye"]; - simIter = gapWeightedSimilarityIncremental(s, t, 0.5); - assert(simIter.empty); - - s = ["Hello"]; - t = ["Hello"]; - simIter = gapWeightedSimilarityIncremental(s, t, 0.5); - assert(simIter.front == 1); // one match - simIter.popFront(); - assert(simIter.empty); - - s = ["Hello", "world"]; - t = ["Hello"]; - simIter = gapWeightedSimilarityIncremental(s, t, 0.5); - assert(simIter.front == 1); // one match - simIter.popFront(); - assert(simIter.empty); - - s = ["Hello", "world"]; - t = ["Hello", "yah", "world"]; - simIter = gapWeightedSimilarityIncremental(s, t, 0.5); - assert(simIter.front == 2); // two 1-gram matches - simIter.popFront(); - assert(simIter.front == 0.5, text(simIter.front)); // one 2-gram match, 1 gap -} - -@system unittest -{ - GapWeightedSimilarityIncremental!(string[]) sim = - GapWeightedSimilarityIncremental!(string[])( - ["nyuk", "I", "have", "no", "chocolate", "giba"], - ["wyda", "I", "have", "I", "have", "have", "I", "have", "hehe"], - 0.5); - double[] witness = [ 7.0, 4.03125, 0, 0 ]; - foreach (e; sim) - { - //writeln(e); - assert(e == witness.front); - witness.popFront(); - } - witness = [ 3.0, 1.3125, 0.25 ]; - sim = GapWeightedSimilarityIncremental!(string[])( - ["I", "have", "no", "chocolate"], - ["I", "have", "some", "chocolate"], - 0.5); - foreach (e; sim) - { - //writeln(e); - assert(e == witness.front); - witness.popFront(); - } - assert(witness.empty); -} - -/** -Computes the greatest common divisor of `a` and `b` by using -an efficient algorithm such as $(HTTPS en.wikipedia.org/wiki/Euclidean_algorithm, Euclid's) -or $(HTTPS en.wikipedia.org/wiki/Binary_GCD_algorithm, Stein's) algorithm. - -Params: - a = Integer value of any numerical type that supports the modulo operator `%`. - If bit-shifting `<<` and `>>` are also supported, Stein's algorithm will - be used; otherwise, Euclid's algorithm is used as _a fallback. - b = Integer value of any equivalent numerical type. - -Returns: - The greatest common divisor of the given arguments. - */ -typeof(Unqual!(T).init % Unqual!(U).init) gcd(T, U)(T a, U b) -if (isIntegral!T && isIntegral!U) -{ - // Operate on a common type between the two arguments. - alias UCT = Unsigned!(CommonType!(Unqual!T, Unqual!U)); - - // `std.math.abs` doesn't support unsigned integers, and `T.min` is undefined. - static if (is(T : immutable short) || is(T : immutable byte)) - UCT ax = (isUnsigned!T || a >= 0) ? a : cast(UCT) -int(a); - else - UCT ax = (isUnsigned!T || a >= 0) ? a : -UCT(a); - - static if (is(U : immutable short) || is(U : immutable byte)) - UCT bx = (isUnsigned!U || b >= 0) ? b : cast(UCT) -int(b); - else - UCT bx = (isUnsigned!U || b >= 0) ? b : -UCT(b); - - // Special cases. - if (ax == 0) - return bx; - if (bx == 0) - return ax; - - return gcdImpl(ax, bx); -} - -private typeof(T.init % T.init) gcdImpl(T)(T a, T b) -if (isIntegral!T) -{ - pragma(inline, true); - import core.bitop : bsf; - import std.algorithm.mutation : swap; - - immutable uint shift = bsf(a | b); - a >>= a.bsf; - do - { - b >>= b.bsf; - if (a > b) - swap(a, b); - b -= a; - } while (b); - - return a << shift; -} - -/// -@safe unittest -{ - assert(gcd(2 * 5 * 7 * 7, 5 * 7 * 11) == 5 * 7); - const int a = 5 * 13 * 23 * 23, b = 13 * 59; - assert(gcd(a, b) == 13); -} - -@safe unittest -{ - import std.meta : AliasSeq; - static foreach (T; AliasSeq!(byte, ubyte, short, ushort, int, uint, long, ulong, - const byte, const short, const int, const long, - immutable ubyte, immutable ushort, immutable uint, immutable ulong)) - { - static foreach (U; AliasSeq!(byte, ubyte, short, ushort, int, uint, long, ulong, - const ubyte, const ushort, const uint, const ulong, - immutable byte, immutable short, immutable int, immutable long)) - { - // Signed and unsigned tests. - static if (T.max > byte.max && U.max > byte.max) - assert(gcd(T(200), U(200)) == 200); - static if (T.max > ubyte.max) - { - assert(gcd(T(2000), U(20)) == 20); - assert(gcd(T(2011), U(17)) == 1); - } - static if (T.max > ubyte.max && U.max > ubyte.max) - assert(gcd(T(1071), U(462)) == 21); - - assert(gcd(T(0), U(13)) == 13); - assert(gcd(T(29), U(0)) == 29); - assert(gcd(T(0), U(0)) == 0); - assert(gcd(T(1), U(2)) == 1); - assert(gcd(T(9), U(6)) == 3); - assert(gcd(T(3), U(4)) == 1); - assert(gcd(T(32), U(24)) == 8); - assert(gcd(T(5), U(6)) == 1); - assert(gcd(T(54), U(36)) == 18); - - // Int and Long tests. - static if (T.max > short.max && U.max > short.max) - assert(gcd(T(46391), U(62527)) == 2017); - static if (T.max > ushort.max && U.max > ushort.max) - assert(gcd(T(63245986), U(39088169)) == 1); - static if (T.max > uint.max && U.max > uint.max) - { - assert(gcd(T(77160074263), U(47687519812)) == 1); - assert(gcd(T(77160074264), U(47687519812)) == 4); - } - - // Negative tests. - static if (T.min < 0) - { - assert(gcd(T(-21), U(28)) == 7); - assert(gcd(T(-3), U(4)) == 1); - } - static if (U.min < 0) - { - assert(gcd(T(1), U(-2)) == 1); - assert(gcd(T(33), U(-44)) == 11); - } - static if (T.min < 0 && U.min < 0) - { - assert(gcd(T(-5), U(-6)) == 1); - assert(gcd(T(-50), U(-60)) == 10); - } - } - } -} - -// https://issues.dlang.org/show_bug.cgi?id=21834 -@safe unittest -{ - assert(gcd(-120, 10U) == 10); - assert(gcd(120U, -10) == 10); - assert(gcd(int.min, 0L) == 1L + int.max); - assert(gcd(0L, int.min) == 1L + int.max); - assert(gcd(int.min, 0L + int.min) == 1L + int.max); - assert(gcd(int.min, 1L + int.max) == 1L + int.max); - assert(gcd(short.min, 1U + short.max) == 1U + short.max); -} - -// This overload is for non-builtin numerical types like BigInt or -// user-defined types. -/// ditto -auto gcd(T)(T a, T b) -if (!isIntegral!T && - is(typeof(T.init % T.init)) && - is(typeof(T.init == 0 || T.init > 0))) -{ - static if (!is(T == Unqual!T)) - { - return gcd!(Unqual!T)(a, b); - } - else - { - // Ensure arguments are unsigned. - a = a >= 0 ? a : -a; - b = b >= 0 ? b : -b; - - // Special cases. - if (a == 0) - return b; - if (b == 0) - return a; - - return gcdImpl(a, b); - } -} - -private auto gcdImpl(T)(T a, T b) -if (!isIntegral!T) -{ - pragma(inline, true); - import std.algorithm.mutation : swap; - enum canUseBinaryGcd = is(typeof(() { - T t, u; - t <<= 1; - t >>= 1; - t -= u; - bool b = (t & 1) == 0; - swap(t, u); - })); - - static if (canUseBinaryGcd) - { - uint shift = 0; - while ((a & 1) == 0 && (b & 1) == 0) - { - a >>= 1; - b >>= 1; - shift++; - } - - if ((a & 1) == 0) swap(a, b); - - do - { - assert((a & 1) != 0); - while ((b & 1) == 0) - b >>= 1; - if (a > b) - swap(a, b); - b -= a; - } while (b); - - return a << shift; - } - else - { - // The only thing we have is %; fallback to Euclidean algorithm. - while (b != 0) - { - auto t = b; - b = a % b; - a = t; - } - return a; - } -} - -// https://issues.dlang.org/show_bug.cgi?id=7102 -@system pure unittest -{ - import std.bigint : BigInt; - assert(gcd(BigInt("71_000_000_000_000_000_000"), - BigInt("31_000_000_000_000_000_000")) == - BigInt("1_000_000_000_000_000_000")); - - assert(gcd(BigInt(0), BigInt(1234567)) == BigInt(1234567)); - assert(gcd(BigInt(1234567), BigInt(0)) == BigInt(1234567)); -} - -@safe pure nothrow unittest -{ - // A numerical type that only supports % and - (to force gcd implementation - // to use Euclidean algorithm). - struct CrippledInt - { - int impl; - CrippledInt opBinary(string op : "%")(CrippledInt i) - { - return CrippledInt(impl % i.impl); - } - CrippledInt opUnary(string op : "-")() - { - return CrippledInt(-impl); - } - int opEquals(CrippledInt i) { return impl == i.impl; } - int opEquals(int i) { return impl == i; } - int opCmp(int i) { return (impl < i) ? -1 : (impl > i) ? 1 : 0; } - } - assert(gcd(CrippledInt(2310), CrippledInt(1309)) == CrippledInt(77)); - assert(gcd(CrippledInt(-120), CrippledInt(10U)) == CrippledInt(10)); - assert(gcd(CrippledInt(120U), CrippledInt(-10)) == CrippledInt(10)); -} - -// https://issues.dlang.org/show_bug.cgi?id=19514 -@system pure unittest -{ - import std.bigint : BigInt; - assert(gcd(BigInt(2), BigInt(1)) == BigInt(1)); -} - -// Issue 20924 -@safe unittest -{ - import std.bigint : BigInt; - const a = BigInt("123143238472389492934020"); - const b = BigInt("902380489324729338420924"); - assert(__traits(compiles, gcd(a, b))); -} - -// https://issues.dlang.org/show_bug.cgi?id=21834 -@safe unittest -{ - import std.bigint : BigInt; - assert(gcd(BigInt(-120), BigInt(10U)) == BigInt(10)); - assert(gcd(BigInt(120U), BigInt(-10)) == BigInt(10)); - assert(gcd(BigInt(int.min), BigInt(0L)) == BigInt(1L + int.max)); - assert(gcd(BigInt(0L), BigInt(int.min)) == BigInt(1L + int.max)); - assert(gcd(BigInt(int.min), BigInt(0L + int.min)) == BigInt(1L + int.max)); - assert(gcd(BigInt(int.min), BigInt(1L + int.max)) == BigInt(1L + int.max)); - assert(gcd(BigInt(short.min), BigInt(1U + short.max)) == BigInt(1U + short.max)); -} - - -/** -Computes the least common multiple of `a` and `b`. -Arguments are the same as $(MYREF gcd). - -Returns: - The least common multiple of the given arguments. - */ -typeof(Unqual!(T).init % Unqual!(U).init) lcm(T, U)(T a, U b) -if (isIntegral!T && isIntegral!U) -{ - // Operate on a common type between the two arguments. - alias UCT = Unsigned!(CommonType!(Unqual!T, Unqual!U)); - - // `std.math.abs` doesn't support unsigned integers, and `T.min` is undefined. - static if (is(T : immutable short) || is(T : immutable byte)) - UCT ax = (isUnsigned!T || a >= 0) ? a : cast(UCT) -int(a); - else - UCT ax = (isUnsigned!T || a >= 0) ? a : -UCT(a); - - static if (is(U : immutable short) || is(U : immutable byte)) - UCT bx = (isUnsigned!U || b >= 0) ? b : cast(UCT) -int(b); - else - UCT bx = (isUnsigned!U || b >= 0) ? b : -UCT(b); - - // Special cases. - if (ax == 0) - return ax; - if (bx == 0) - return bx; - - return (ax / gcdImpl(ax, bx)) * bx; -} - -/// -@safe unittest -{ - assert(lcm(1, 2) == 2); - assert(lcm(3, 4) == 12); - assert(lcm(5, 6) == 30); -} - -@safe unittest -{ - import std.meta : AliasSeq; - static foreach (T; AliasSeq!(byte, ubyte, short, ushort, int, uint, long, ulong, - const byte, const short, const int, const long, - immutable ubyte, immutable ushort, immutable uint, immutable ulong)) - { - static foreach (U; AliasSeq!(byte, ubyte, short, ushort, int, uint, long, ulong, - const ubyte, const ushort, const uint, const ulong, - immutable byte, immutable short, immutable int, immutable long)) - { - assert(lcm(T(21), U(6)) == 42); - assert(lcm(T(41), U(0)) == 0); - assert(lcm(T(0), U(7)) == 0); - assert(lcm(T(0), U(0)) == 0); - assert(lcm(T(1U), U(2)) == 2); - assert(lcm(T(3), U(4U)) == 12); - assert(lcm(T(5U), U(6U)) == 30); - static if (T.min < 0) - assert(lcm(T(-42), U(21U)) == 42); - } - } -} - -/// ditto -auto lcm(T)(T a, T b) -if (!isIntegral!T && - is(typeof(T.init % T.init)) && - is(typeof(T.init == 0 || T.init > 0))) -{ - // Ensure arguments are unsigned. - a = a >= 0 ? a : -a; - b = b >= 0 ? b : -b; - - // Special cases. - if (a == 0) - return a; - if (b == 0) - return b; - - return (a / gcdImpl(a, b)) * b; -} - -@safe unittest -{ - import std.bigint : BigInt; - assert(lcm(BigInt(21), BigInt(6)) == BigInt(42)); - assert(lcm(BigInt(41), BigInt(0)) == BigInt(0)); - assert(lcm(BigInt(0), BigInt(7)) == BigInt(0)); - assert(lcm(BigInt(0), BigInt(0)) == BigInt(0)); - assert(lcm(BigInt(1U), BigInt(2)) == BigInt(2)); - assert(lcm(BigInt(3), BigInt(4U)) == BigInt(12)); - assert(lcm(BigInt(5U), BigInt(6U)) == BigInt(30)); - assert(lcm(BigInt(-42), BigInt(21U)) == BigInt(42)); -} - -// This is to make tweaking the speed/size vs. accuracy tradeoff easy, -// though floats seem accurate enough for all practical purposes, since -// they pass the "isClose(inverseFft(fft(arr)), arr)" test even for -// size 2 ^^ 22. -private alias lookup_t = float; - -/**A class for performing fast Fourier transforms of power of two sizes. - * This class encapsulates a large amount of state that is reusable when - * performing multiple FFTs of sizes smaller than or equal to that specified - * in the constructor. This results in substantial speedups when performing - * multiple FFTs with a known maximum size. However, - * a free function API is provided for convenience if you need to perform a - * one-off FFT. - * - * References: - * $(HTTP en.wikipedia.org/wiki/Cooley%E2%80%93Tukey_FFT_algorithm) - */ -final class Fft -{ - import core.bitop : bsf; - import std.algorithm.iteration : map; - import std.array : uninitializedArray; - -private: - immutable lookup_t[][] negSinLookup; - - void enforceSize(R)(R range) const - { - import std.conv : text; - assert(range.length <= size, text( - "FFT size mismatch. Expected ", size, ", got ", range.length)); - } - - void fftImpl(Ret, R)(Stride!R range, Ret buf) const - in - { - assert(range.length >= 4); - assert(isPowerOf2(range.length)); - } - do - { - auto recurseRange = range; - recurseRange.doubleSteps(); - - if (buf.length > 4) - { - fftImpl(recurseRange, buf[0..$ / 2]); - recurseRange.popHalf(); - fftImpl(recurseRange, buf[$ / 2..$]); - } - else - { - // Do this here instead of in another recursion to save on - // recursion overhead. - slowFourier2(recurseRange, buf[0..$ / 2]); - recurseRange.popHalf(); - slowFourier2(recurseRange, buf[$ / 2..$]); - } - - butterfly(buf); - } - - // This algorithm works by performing the even and odd parts of our FFT - // using the "two for the price of one" method mentioned at - // http://www.engineeringproductivitytools.com/stuff/T0001/PT10.HTM#Head521 - // by making the odd terms into the imaginary components of our new FFT, - // and then using symmetry to recombine them. - void fftImplPureReal(Ret, R)(R range, Ret buf) const - in - { - assert(range.length >= 4); - assert(isPowerOf2(range.length)); - } - do - { - alias E = ElementType!R; - - // Converts odd indices of range to the imaginary components of - // a range half the size. The even indices become the real components. - static if (isArray!R && isFloatingPoint!E) - { - // Then the memory layout of complex numbers provides a dirt - // cheap way to convert. This is a common case, so take advantage. - auto oddsImag = cast(Complex!E[]) range; - } - else - { - // General case: Use a higher order range. We can assume - // source.length is even because it has to be a power of 2. - static struct OddToImaginary - { - R source; - alias C = Complex!(CommonType!(E, typeof(buf[0].re))); - - @property - { - C front() - { - return C(source[0], source[1]); - } - - C back() - { - immutable n = source.length; - return C(source[n - 2], source[n - 1]); - } - - typeof(this) save() - { - return typeof(this)(source.save); - } - - bool empty() - { - return source.empty; - } - - size_t length() - { - return source.length / 2; - } - } - - void popFront() - { - source.popFront(); - source.popFront(); - } - - void popBack() - { - source.popBack(); - source.popBack(); - } - - C opIndex(size_t index) - { - return C(source[index * 2], source[index * 2 + 1]); - } - - typeof(this) opSlice(size_t lower, size_t upper) - { - return typeof(this)(source[lower * 2 .. upper * 2]); - } - } - - auto oddsImag = OddToImaginary(range); - } - - fft(oddsImag, buf[0..$ / 2]); - auto evenFft = buf[0..$ / 2]; - auto oddFft = buf[$ / 2..$]; - immutable halfN = evenFft.length; - oddFft[0].re = buf[0].im; - oddFft[0].im = 0; - evenFft[0].im = 0; - // evenFft[0].re is already right b/c it's aliased with buf[0].re. - - foreach (k; 1 .. halfN / 2 + 1) - { - immutable bufk = buf[k]; - immutable bufnk = buf[buf.length / 2 - k]; - evenFft[k].re = 0.5 * (bufk.re + bufnk.re); - evenFft[halfN - k].re = evenFft[k].re; - evenFft[k].im = 0.5 * (bufk.im - bufnk.im); - evenFft[halfN - k].im = -evenFft[k].im; - - oddFft[k].re = 0.5 * (bufk.im + bufnk.im); - oddFft[halfN - k].re = oddFft[k].re; - oddFft[k].im = 0.5 * (bufnk.re - bufk.re); - oddFft[halfN - k].im = -oddFft[k].im; - } - - butterfly(buf); - } - - void butterfly(R)(R buf) const - in - { - assert(isPowerOf2(buf.length)); - } - do - { - immutable n = buf.length; - immutable localLookup = negSinLookup[bsf(n)]; - assert(localLookup.length == n); - - immutable cosMask = n - 1; - immutable cosAdd = n / 4 * 3; - - lookup_t negSinFromLookup(size_t index) pure nothrow - { - return localLookup[index]; - } - - lookup_t cosFromLookup(size_t index) pure nothrow - { - // cos is just -sin shifted by PI * 3 / 2. - return localLookup[(index + cosAdd) & cosMask]; - } - - immutable halfLen = n / 2; - - // This loop is unrolled and the two iterations are interleaved - // relative to the textbook FFT to increase ILP. This gives roughly 5% - // speedups on DMD. - for (size_t k = 0; k < halfLen; k += 2) - { - immutable cosTwiddle1 = cosFromLookup(k); - immutable sinTwiddle1 = negSinFromLookup(k); - immutable cosTwiddle2 = cosFromLookup(k + 1); - immutable sinTwiddle2 = negSinFromLookup(k + 1); - - immutable realLower1 = buf[k].re; - immutable imagLower1 = buf[k].im; - immutable realLower2 = buf[k + 1].re; - immutable imagLower2 = buf[k + 1].im; - - immutable upperIndex1 = k + halfLen; - immutable upperIndex2 = upperIndex1 + 1; - immutable realUpper1 = buf[upperIndex1].re; - immutable imagUpper1 = buf[upperIndex1].im; - immutable realUpper2 = buf[upperIndex2].re; - immutable imagUpper2 = buf[upperIndex2].im; - - immutable realAdd1 = cosTwiddle1 * realUpper1 - - sinTwiddle1 * imagUpper1; - immutable imagAdd1 = sinTwiddle1 * realUpper1 - + cosTwiddle1 * imagUpper1; - immutable realAdd2 = cosTwiddle2 * realUpper2 - - sinTwiddle2 * imagUpper2; - immutable imagAdd2 = sinTwiddle2 * realUpper2 - + cosTwiddle2 * imagUpper2; - - buf[k].re += realAdd1; - buf[k].im += imagAdd1; - buf[k + 1].re += realAdd2; - buf[k + 1].im += imagAdd2; - - buf[upperIndex1].re = realLower1 - realAdd1; - buf[upperIndex1].im = imagLower1 - imagAdd1; - buf[upperIndex2].re = realLower2 - realAdd2; - buf[upperIndex2].im = imagLower2 - imagAdd2; - } - } - - // This constructor is used within this module for allocating the - // buffer space elsewhere besides the GC heap. It's definitely **NOT** - // part of the public API and definitely **IS** subject to change. - // - // Also, this is unsafe because the memSpace buffer will be cast - // to immutable. - // - // Public b/c of https://issues.dlang.org/show_bug.cgi?id=4636. - public this(lookup_t[] memSpace) - { - immutable size = memSpace.length / 2; - - /* Create a lookup table of all negative sine values at a resolution of - * size and all smaller power of two resolutions. This may seem - * inefficient, but having all the lookups be next to each other in - * memory at every level of iteration is a huge win performance-wise. - */ - if (size == 0) - { - return; - } - - assert(isPowerOf2(size), - "Can only do FFTs on ranges with a size that is a power of two."); - - auto table = new lookup_t[][bsf(size) + 1]; - - table[$ - 1] = memSpace[$ - size..$]; - memSpace = memSpace[0 .. size]; - - auto lastRow = table[$ - 1]; - lastRow[0] = 0; // -sin(0) == 0. - foreach (ptrdiff_t i; 1 .. size) - { - // The hard coded cases are for improved accuracy and to prevent - // annoying non-zeroness when stuff should be zero. - - if (i == size / 4) - lastRow[i] = -1; // -sin(pi / 2) == -1. - else if (i == size / 2) - lastRow[i] = 0; // -sin(pi) == 0. - else if (i == size * 3 / 4) - lastRow[i] = 1; // -sin(pi * 3 / 2) == 1 - else - lastRow[i] = -sin(i * 2.0L * PI / size); - } - - // Fill in all the other rows with strided versions. - foreach (i; 1 .. table.length - 1) - { - immutable strideLength = size / (2 ^^ i); - auto strided = Stride!(lookup_t[])(lastRow, strideLength); - table[i] = memSpace[$ - strided.length..$]; - memSpace = memSpace[0..$ - strided.length]; - - size_t copyIndex; - foreach (elem; strided) - { - table[i][copyIndex++] = elem; - } - } - - negSinLookup = cast(immutable) table; - } - -public: - /**Create an `Fft` object for computing fast Fourier transforms of - * power of two sizes of `size` or smaller. `size` must be a - * power of two. - */ - this(size_t size) - { - // Allocate all twiddle factor buffers in one contiguous block so that, - // when one is done being used, the next one is next in cache. - auto memSpace = uninitializedArray!(lookup_t[])(2 * size); - this(memSpace); - } - - @property size_t size() const - { - return (negSinLookup is null) ? 0 : negSinLookup[$ - 1].length; - } - - /**Compute the Fourier transform of range using the $(BIGOH N log N) - * Cooley-Tukey Algorithm. `range` must be a random-access range with - * slicing and a length equal to `size` as provided at the construction of - * this object. The contents of range can be either numeric types, - * which will be interpreted as pure real values, or complex types with - * properties or members `.re` and `.im` that can be read. - * - * Note: Pure real FFTs are automatically detected and the relevant - * optimizations are performed. - * - * Returns: An array of complex numbers representing the transformed data in - * the frequency domain. - * - * Conventions: The exponent is negative and the factor is one, - * i.e., output[j] := sum[ exp(-2 PI i j k / N) input[k] ]. - */ - Complex!F[] fft(F = double, R)(R range) const - if (isFloatingPoint!F && isRandomAccessRange!R) - { - enforceSize(range); - Complex!F[] ret; - if (range.length == 0) - { - return ret; - } - - // Don't waste time initializing the memory for ret. - ret = uninitializedArray!(Complex!F[])(range.length); - - fft(range, ret); - return ret; - } - - /**Same as the overload, but allows for the results to be stored in a user- - * provided buffer. The buffer must be of the same length as range, must be - * a random-access range, must have slicing, and must contain elements that are - * complex-like. This means that they must have a .re and a .im member or - * property that can be both read and written and are floating point numbers. - */ - void fft(Ret, R)(R range, Ret buf) const - if (isRandomAccessRange!Ret && isComplexLike!(ElementType!Ret) && hasSlicing!Ret) - { - assert(buf.length == range.length); - enforceSize(range); - - if (range.length == 0) - { - return; - } - else if (range.length == 1) - { - buf[0] = range[0]; - return; - } - else if (range.length == 2) - { - slowFourier2(range, buf); - return; - } - else - { - alias E = ElementType!R; - static if (is(E : real)) - { - return fftImplPureReal(range, buf); - } - else - { - static if (is(R : Stride!R)) - return fftImpl(range, buf); - else - return fftImpl(Stride!R(range, 1), buf); - } - } - } - - /** - * Computes the inverse Fourier transform of a range. The range must be a - * random access range with slicing, have a length equal to the size - * provided at construction of this object, and contain elements that are - * either of type std.complex.Complex or have essentially - * the same compile-time interface. - * - * Returns: The time-domain signal. - * - * Conventions: The exponent is positive and the factor is 1/N, i.e., - * output[j] := (1 / N) sum[ exp(+2 PI i j k / N) input[k] ]. - */ - Complex!F[] inverseFft(F = double, R)(R range) const - if (isRandomAccessRange!R && isComplexLike!(ElementType!R) && isFloatingPoint!F) - { - enforceSize(range); - Complex!F[] ret; - if (range.length == 0) - { - return ret; - } - - // Don't waste time initializing the memory for ret. - ret = uninitializedArray!(Complex!F[])(range.length); - - inverseFft(range, ret); - return ret; - } - - /** - * Inverse FFT that allows a user-supplied buffer to be provided. The buffer - * must be a random access range with slicing, and its elements - * must be some complex-like type. - */ - void inverseFft(Ret, R)(R range, Ret buf) const - if (isRandomAccessRange!Ret && isComplexLike!(ElementType!Ret) && hasSlicing!Ret) - { - enforceSize(range); - - auto swapped = map!swapRealImag(range); - fft(swapped, buf); - - immutable lenNeg1 = 1.0 / buf.length; - foreach (ref elem; buf) - { - immutable temp = elem.re * lenNeg1; - elem.re = elem.im * lenNeg1; - elem.im = temp; - } - } -} - -// This mixin creates an Fft object in the scope it's mixed into such that all -// memory owned by the object is deterministically destroyed at the end of that -// scope. -private enum string MakeLocalFft = q{ - import core.stdc.stdlib; - import core.exception : onOutOfMemoryError; - - auto lookupBuf = (cast(lookup_t*) malloc(range.length * 2 * lookup_t.sizeof)) - [0 .. 2 * range.length]; - if (!lookupBuf.ptr) - onOutOfMemoryError(); - - scope(exit) free(cast(void*) lookupBuf.ptr); - auto fftObj = scoped!Fft(lookupBuf); -}; - -/**Convenience functions that create an `Fft` object, run the FFT or inverse - * FFT and return the result. Useful for one-off FFTs. - * - * Note: In addition to convenience, these functions are slightly more - * efficient than manually creating an Fft object for a single use, - * as the Fft object is deterministically destroyed before these - * functions return. - */ -Complex!F[] fft(F = double, R)(R range) -{ - mixin(MakeLocalFft); - return fftObj.fft!(F, R)(range); -} - -/// ditto -void fft(Ret, R)(R range, Ret buf) -{ - mixin(MakeLocalFft); - return fftObj.fft!(Ret, R)(range, buf); -} - -/// ditto -Complex!F[] inverseFft(F = double, R)(R range) -{ - mixin(MakeLocalFft); - return fftObj.inverseFft!(F, R)(range); -} - -/// ditto -void inverseFft(Ret, R)(R range, Ret buf) -{ - mixin(MakeLocalFft); - return fftObj.inverseFft!(Ret, R)(range, buf); -} - -@system unittest -{ - import std.algorithm; - import std.conv; - import std.range; - // Test values from R and Octave. - auto arr = [1,2,3,4,5,6,7,8]; - auto fft1 = fft(arr); - assert(isClose(map!"a.re"(fft1), - [36.0, -4, -4, -4, -4, -4, -4, -4], 1e-4)); - assert(isClose(map!"a.im"(fft1), - [0, 9.6568, 4, 1.6568, 0, -1.6568, -4, -9.6568], 1e-4)); - - auto fft1Retro = fft(retro(arr)); - assert(isClose(map!"a.re"(fft1Retro), - [36.0, 4, 4, 4, 4, 4, 4, 4], 1e-4)); - assert(isClose(map!"a.im"(fft1Retro), - [0, -9.6568, -4, -1.6568, 0, 1.6568, 4, 9.6568], 1e-4)); - - auto fft1Float = fft(to!(float[])(arr)); - assert(isClose(map!"a.re"(fft1), map!"a.re"(fft1Float))); - assert(isClose(map!"a.im"(fft1), map!"a.im"(fft1Float))); - - alias C = Complex!float; - auto arr2 = [C(1,2), C(3,4), C(5,6), C(7,8), C(9,10), - C(11,12), C(13,14), C(15,16)]; - auto fft2 = fft(arr2); - assert(isClose(map!"a.re"(fft2), - [64.0, -27.3137, -16, -11.3137, -8, -4.6862, 0, 11.3137], 1e-4)); - assert(isClose(map!"a.im"(fft2), - [72, 11.3137, 0, -4.686, -8, -11.3137, -16, -27.3137], 1e-4)); - - auto inv1 = inverseFft(fft1); - assert(isClose(map!"a.re"(inv1), arr, 1e-6)); - assert(reduce!max(map!"a.im"(inv1)) < 1e-10); - - auto inv2 = inverseFft(fft2); - assert(isClose(map!"a.re"(inv2), map!"a.re"(arr2))); - assert(isClose(map!"a.im"(inv2), map!"a.im"(arr2))); - - // FFTs of size 0, 1 and 2 are handled as special cases. Test them here. - ushort[] empty; - assert(fft(empty) == null); - assert(inverseFft(fft(empty)) == null); - - real[] oneElem = [4.5L]; - auto oneFft = fft(oneElem); - assert(oneFft.length == 1); - assert(oneFft[0].re == 4.5L); - assert(oneFft[0].im == 0); - - auto oneInv = inverseFft(oneFft); - assert(oneInv.length == 1); - assert(isClose(oneInv[0].re, 4.5)); - assert(isClose(oneInv[0].im, 0, 0.0, 1e-10)); - - long[2] twoElems = [8, 4]; - auto twoFft = fft(twoElems[]); - assert(twoFft.length == 2); - assert(isClose(twoFft[0].re, 12)); - assert(isClose(twoFft[0].im, 0, 0.0, 1e-10)); - assert(isClose(twoFft[1].re, 4)); - assert(isClose(twoFft[1].im, 0, 0.0, 1e-10)); - auto twoInv = inverseFft(twoFft); - assert(isClose(twoInv[0].re, 8)); - assert(isClose(twoInv[0].im, 0, 0.0, 1e-10)); - assert(isClose(twoInv[1].re, 4)); - assert(isClose(twoInv[1].im, 0, 0.0, 1e-10)); -} - -// Swaps the real and imaginary parts of a complex number. This is useful -// for inverse FFTs. -C swapRealImag(C)(C input) -{ - return C(input.im, input.re); -} - -/** This function transforms `decimal` value into a value in the factorial number -system stored in `fac`. - -A factorial number is constructed as: -$(D fac[0] * 0! + fac[1] * 1! + ... fac[20] * 20!) - -Params: - decimal = The decimal value to convert into the factorial number system. - fac = The array to store the factorial number. The array is of size 21 as - `ulong.max` requires 21 digits in the factorial number system. -Returns: - A variable storing the number of digits of the factorial number stored in - `fac`. -*/ -size_t decimalToFactorial(ulong decimal, ref ubyte[21] fac) - @safe pure nothrow @nogc -{ - import std.algorithm.mutation : reverse; - size_t idx; - - for (ulong i = 1; decimal != 0; ++i) - { - auto temp = decimal % i; - decimal /= i; - fac[idx++] = cast(ubyte)(temp); - } - - if (idx == 0) - { - fac[idx++] = cast(ubyte) 0; - } - - reverse(fac[0 .. idx]); - - // first digit of the number in factorial will always be zero - assert(fac[idx - 1] == 0); - - return idx; -} - -/// -@safe pure @nogc unittest -{ - ubyte[21] fac; - size_t idx = decimalToFactorial(2982, fac); - - assert(fac[0] == 4); - assert(fac[1] == 0); - assert(fac[2] == 4); - assert(fac[3] == 1); - assert(fac[4] == 0); - assert(fac[5] == 0); - assert(fac[6] == 0); -} - -@safe pure unittest -{ - ubyte[21] fac; - size_t idx = decimalToFactorial(0UL, fac); - assert(idx == 1); - assert(fac[0] == 0); - - fac[] = 0; - idx = 0; - idx = decimalToFactorial(ulong.max, fac); - assert(idx == 21); - auto t = [7, 11, 12, 4, 3, 15, 3, 5, 3, 5, 0, 8, 3, 5, 0, 0, 0, 2, 1, 1, 0]; - foreach (i, it; fac[0 .. 21]) - { - assert(it == t[i]); - } - - fac[] = 0; - idx = decimalToFactorial(2982, fac); - - assert(idx == 7); - t = [4, 0, 4, 1, 0, 0, 0]; - foreach (i, it; fac[0 .. idx]) - { - assert(it == t[i]); - } -} - -private: -// The reasons I couldn't use std.algorithm were b/c its stride length isn't -// modifiable on the fly and because range has grown some performance hacks -// for powers of 2. -struct Stride(R) -{ - import core.bitop : bsf; - Unqual!R range; - size_t _nSteps; - size_t _length; - alias E = ElementType!(R); - - this(R range, size_t nStepsIn) - { - this.range = range; - _nSteps = nStepsIn; - _length = (range.length + _nSteps - 1) / nSteps; - } - - size_t length() const @property - { - return _length; - } - - typeof(this) save() @property - { - auto ret = this; - ret.range = ret.range.save; - return ret; - } - - E opIndex(size_t index) - { - return range[index * _nSteps]; - } - - E front() @property - { - return range[0]; - } - - void popFront() - { - if (range.length >= _nSteps) - { - range = range[_nSteps .. range.length]; - _length--; - } - else - { - range = range[0 .. 0]; - _length = 0; - } - } - - // Pops half the range's stride. - void popHalf() - { - range = range[_nSteps / 2 .. range.length]; - } - - bool empty() const @property - { - return length == 0; - } - - size_t nSteps() const @property - { - return _nSteps; - } - - void doubleSteps() - { - _nSteps *= 2; - _length /= 2; - } - - size_t nSteps(size_t newVal) @property - { - _nSteps = newVal; - - // Using >> bsf(nSteps) is a few cycles faster than / nSteps. - _length = (range.length + _nSteps - 1) >> bsf(nSteps); - return newVal; - } -} - -// Hard-coded base case for FFT of size 2. This is actually a TON faster than -// using a generic slow DFT. This seems to be the best base case. (Size 1 -// can be coded inline as buf[0] = range[0]). -void slowFourier2(Ret, R)(R range, Ret buf) -{ - assert(range.length == 2); - assert(buf.length == 2); - buf[0] = range[0] + range[1]; - buf[1] = range[0] - range[1]; -} - -// Hard-coded base case for FFT of size 4. Doesn't work as well as the size -// 2 case. -void slowFourier4(Ret, R)(R range, Ret buf) -{ - alias C = ElementType!Ret; - - assert(range.length == 4); - assert(buf.length == 4); - buf[0] = range[0] + range[1] + range[2] + range[3]; - buf[1] = range[0] - range[1] * C(0, 1) - range[2] + range[3] * C(0, 1); - buf[2] = range[0] - range[1] + range[2] - range[3]; - buf[3] = range[0] + range[1] * C(0, 1) - range[2] - range[3] * C(0, 1); -} - -N roundDownToPowerOf2(N)(N num) -if (isScalarType!N && !isFloatingPoint!N) -{ - import core.bitop : bsr; - return num & (cast(N) 1 << bsr(num)); -} - -@safe unittest -{ - assert(roundDownToPowerOf2(7) == 4); - assert(roundDownToPowerOf2(4) == 4); -} - -template isComplexLike(T) -{ - enum bool isComplexLike = is(typeof(T.init.re)) && - is(typeof(T.init.im)); -} - -@safe unittest -{ - static assert(isComplexLike!(Complex!double)); - static assert(!isComplexLike!(uint)); -} diff --git a/phobos/std/outbuffer.d b/phobos/std/outbuffer.d deleted file mode 100644 index 92af9a9..0000000 --- a/phobos/std/outbuffer.d +++ /dev/null @@ -1,514 +0,0 @@ -// Written in the D programming language. - -/** -Serialize data to `ubyte` arrays. - - * Copyright: Copyright The D Language Foundation 2000 - 2015. - * License: $(HTTP www.boost.org/LICENSE_1_0.txt, Boost License 1.0). - * Authors: $(HTTP digitalmars.com, Walter Bright) - * Source: $(PHOBOSSRC std/outbuffer.d) - * - * $(SCRIPT inhibitQuickIndex = 1;) - */ -module std.outbuffer; - -import core.stdc.stdarg; -import std.traits : isSomeString; - -/********************************************* - * OutBuffer provides a way to build up an array of bytes out - * of raw data. It is useful for things like preparing an - * array of bytes to write out to a file. - * OutBuffer's byte order is the format native to the computer. - * To control the byte order (endianness), use a class derived - * from OutBuffer. - * OutBuffer's internal buffer is allocated with the GC. Pointers - * stored into the buffer are scanned by the GC, but you have to - * ensure proper alignment, e.g. by using alignSize((void*).sizeof). - */ - -class OutBuffer -{ - ubyte[] data; - size_t offset; - - invariant() - { - assert(offset <= data.length); - } - - pure nothrow @safe - { - /********************************* - * Convert to array of bytes. - */ - inout(ubyte)[] toBytes() scope inout { return data[0 .. offset]; } - - /*********************************** - * Preallocate nbytes more to the size of the internal buffer. - * - * This is a - * speed optimization, a good guess at the maximum size of the resulting - * buffer will improve performance by eliminating reallocations and copying. - */ - void reserve(size_t nbytes) @trusted - in - { - assert(offset + nbytes >= offset); - } - out - { - assert(offset + nbytes <= data.length); - } - do - { - if (data.length < offset + nbytes) - { - void[] vdata = data; - vdata.length = (offset + nbytes + 7) * 2; // allocates as void[] to not set BlkAttr.NO_SCAN - data = cast(ubyte[]) vdata; - } - } - - /********************************** - * put enables OutBuffer to be used as an OutputRange. - */ - alias put = write; - - /************************************* - * Append data to the internal buffer. - */ - - void write(scope const(ubyte)[] bytes) - { - reserve(bytes.length); - data[offset .. offset + bytes.length] = bytes[]; - offset += bytes.length; - } - - void write(scope const(wchar)[] chars) @trusted - { - write(cast(ubyte[]) chars); - } - - void write(scope const(dchar)[] chars) @trusted - { - write(cast(ubyte[]) chars); - } - - void write(ubyte b) /// ditto - { - reserve(ubyte.sizeof); - this.data[offset] = b; - offset += ubyte.sizeof; - } - - void write(byte b) { write(cast(ubyte) b); } /// ditto - void write(char c) { write(cast(ubyte) c); } /// ditto - void write(dchar c) { write(cast(uint) c); } /// ditto - - void write(ushort w) @trusted /// ditto - { - reserve(ushort.sizeof); - *cast(ushort *)&data[offset] = w; - offset += ushort.sizeof; - } - - void write(short s) { write(cast(ushort) s); } /// ditto - - void write(wchar c) @trusted /// ditto - { - reserve(wchar.sizeof); - *cast(wchar *)&data[offset] = c; - offset += wchar.sizeof; - } - - void write(uint w) @trusted /// ditto - { - reserve(uint.sizeof); - *cast(uint *)&data[offset] = w; - offset += uint.sizeof; - } - - void write(int i) { write(cast(uint) i); } /// ditto - - void write(ulong l) @trusted /// ditto - { - reserve(ulong.sizeof); - *cast(ulong *)&data[offset] = l; - offset += ulong.sizeof; - } - - void write(long l) { write(cast(ulong) l); } /// ditto - - void write(float f) @trusted /// ditto - { - reserve(float.sizeof); - *cast(float *)&data[offset] = f; - offset += float.sizeof; - } - - void write(double f) @trusted /// ditto - { - reserve(double.sizeof); - *cast(double *)&data[offset] = f; - offset += double.sizeof; - } - - void write(real f) @trusted /// ditto - { - reserve(real.sizeof); - *cast(real *)&data[offset] = f; - offset += real.sizeof; - } - - void write(scope const(char)[] s) @trusted /// ditto - { - write(cast(ubyte[]) s); - } - - void write(scope const OutBuffer buf) /// ditto - { - write(buf.toBytes()); - } - - /**************************************** - * Append nbytes of val to the internal buffer. - * Params: - * nbytes = Number of bytes to fill. - * val = Value to fill, defaults to 0. - */ - - void fill(size_t nbytes, ubyte val = 0) - { - reserve(nbytes); - data[offset .. offset + nbytes] = val; - offset += nbytes; - } - - /**************************************** - * Append nbytes of 0 to the internal buffer. - * Param: - * nbytes - number of bytes to fill. - */ - void fill0(size_t nbytes) - { - fill(nbytes); - } - - /********************************** - * Append bytes until the buffer aligns on a power of 2 boundary. - * - * By default fills with 0 bytes. - * - * Params: - * alignsize = Alignment value. Must be power of 2. - * val = Value to fill, defaults to 0. - */ - - void alignSize(size_t alignsize, ubyte val = 0) - in - { - assert(alignsize && (alignsize & (alignsize - 1)) == 0); - } - out - { - assert((offset & (alignsize - 1)) == 0); - } - do - { - auto nbytes = offset & (alignsize - 1); - if (nbytes) - fill(alignsize - nbytes, val); - } - /// - @safe unittest - { - OutBuffer buf = new OutBuffer(); - buf.write(cast(ubyte) 1); - buf.align2(); - assert(buf.toBytes() == "\x01\x00"); - buf.write(cast(ubyte) 2); - buf.align4(); - assert(buf.toBytes() == "\x01\x00\x02\x00"); - buf.write(cast(ubyte) 3); - buf.alignSize(8); - assert(buf.toBytes() == "\x01\x00\x02\x00\x03\x00\x00\x00"); - } - /// ditto - @safe unittest - { - OutBuffer buf = new OutBuffer(); - buf.write(cast(ubyte) 1); - buf.align2(0x55); - assert(buf.toBytes() == "\x01\x55"); - buf.write(cast(ubyte) 2); - buf.align4(0x55); - assert(buf.toBytes() == "\x01\x55\x02\x55"); - buf.write(cast(ubyte) 3); - buf.alignSize(8, 0x55); - assert(buf.toBytes() == "\x01\x55\x02\x55\x03\x55\x55\x55"); - } - - /// Clear the data in the buffer - void clear() - { - offset = 0; - } - - /**************************************** - * Optimize common special case alignSize(2) - * Params: - * val = Value to fill, defaults to 0. - */ - - void align2(ubyte val = 0) - { - if (offset & 1) - write(cast(byte) val); - } - - /**************************************** - * Optimize common special case alignSize(4) - * Params: - * val = Value to fill, defaults to 0. - */ - - void align4(ubyte val = 0) - { - if (offset & 3) - { auto nbytes = (4 - offset) & 3; - fill(nbytes, val); - } - } - - /************************************** - * Convert internal buffer to array of chars. - */ - - override string toString() const - { - //printf("OutBuffer.toString()\n"); - return cast(string) data[0 .. offset].idup; - } - } - - /***************************************** - * Append output of C's vprintf() to internal buffer. - */ - - void vprintf(scope string format, va_list args) @trusted nothrow - { - import core.stdc.stdio : vsnprintf; - import core.stdc.stdlib : alloca; - import std.string : toStringz; - - version (StdUnittest) - char[3] buffer = void; // trigger reallocation - else - char[128] buffer = void; - int count; - - // Can't use `tempCString()` here as it will result in compilation error: - // "cannot mix core.std.stdlib.alloca() and exception handling". - auto f = toStringz(format); - auto p = buffer.ptr; - auto psize = buffer.length; - for (;;) - { - va_list args2; - va_copy(args2, args); - count = vsnprintf(p, psize, f, args2); - va_end(args2); - if (count == -1) - { - if (psize > psize.max / 2) assert(0); // overflow check - psize *= 2; - } - else if (count >= psize) - { - if (count == count.max) assert(0); // overflow check - psize = count + 1; - } - else - break; - - p = cast(char *) alloca(psize); // buffer too small, try again with larger size - } - write(cast(ubyte[]) p[0 .. count]); - } - - /***************************************** - * Append output of C's printf() to internal buffer. - */ - - void printf(scope string format, ...) @trusted - { - va_list ap; - va_start(ap, format); - vprintf(format, ap); - va_end(ap); - } - - /** - * Formats and writes its arguments in text format to the OutBuffer. - * - * Params: - * fmt = format string as described in $(REF formattedWrite, std,format) - * args = arguments to be formatted - * - * See_Also: - * $(REF _writef, std,stdio); - * $(REF formattedWrite, std,format); - */ - void writef(Char, A...)(scope const(Char)[] fmt, A args) - { - import std.format.write : formattedWrite; - formattedWrite(this, fmt, args); - } - - /// - @safe unittest - { - OutBuffer b = new OutBuffer(); - b.writef("a%sb", 16); - assert(b.toString() == "a16b"); - } - - /// ditto - void writef(alias fmt, A...)(A args) - if (isSomeString!(typeof(fmt))) - { - import std.format : checkFormatException; - - alias e = checkFormatException!(fmt, A); - static assert(!e, e); - return this.writef(fmt, args); - } - - /// - @safe unittest - { - OutBuffer b = new OutBuffer(); - b.writef!"a%sb"(16); - assert(b.toString() == "a16b"); - } - - /** - * Formats and writes its arguments in text format to the OutBuffer, - * followed by a newline. - * - * Params: - * fmt = format string as described in $(REF formattedWrite, std,format) - * args = arguments to be formatted - * - * See_Also: - * $(REF _writefln, std,stdio); - * $(REF formattedWrite, std,format); - */ - void writefln(Char, A...)(scope const(Char)[] fmt, A args) - { - import std.format.write : formattedWrite; - formattedWrite(this, fmt, args); - put('\n'); - } - - /// - @safe unittest - { - OutBuffer b = new OutBuffer(); - b.writefln("a%sb", 16); - assert(b.toString() == "a16b\n"); - } - - /// ditto - void writefln(alias fmt, A...)(A args) - if (isSomeString!(typeof(fmt))) - { - import std.format : checkFormatException; - - alias e = checkFormatException!(fmt, A); - static assert(!e, e); - return this.writefln(fmt, args); - } - - /// - @safe unittest - { - OutBuffer b = new OutBuffer(); - b.writefln!"a%sb"(16); - assert(b.toString() == "a16b\n"); - } - - /***************************************** - * At offset index into buffer, create nbytes of space by shifting upwards - * all data past index. - */ - - void spread(size_t index, size_t nbytes) pure nothrow @safe - in - { - assert(index <= offset); - } - do - { - reserve(nbytes); - - // This is an overlapping copy - should use memmove() - for (size_t i = offset; i > index; ) - { - --i; - data[i + nbytes] = data[i]; - } - offset += nbytes; - } -} - -/// -@safe unittest -{ - import std.string : cmp; - - OutBuffer buf = new OutBuffer(); - - assert(buf.offset == 0); - buf.write("hello"); - buf.write(cast(byte) 0x20); - buf.write("world"); - buf.printf(" %d", 62665); - assert(cmp(buf.toString(), "hello world 62665") == 0); - - buf.clear(); - assert(cmp(buf.toString(), "") == 0); - buf.write("New data"); - assert(cmp(buf.toString(),"New data") == 0); -} - -@safe unittest -{ - import std.range; - static assert(isOutputRange!(OutBuffer, char)); - - import std.algorithm; - { - OutBuffer buf = new OutBuffer(); - "hello".copy(buf); - assert(buf.toBytes() == "hello"); - } - { - OutBuffer buf = new OutBuffer(); - "hello"w.copy(buf); - version (LittleEndian) - assert(buf.toBytes() == "h\x00e\x00l\x00l\x00o\x00"); - version (BigEndian) - assert(buf.toBytes() == "\x00h\x00e\x00l\x00l\x00o"); - } - { - OutBuffer buf = new OutBuffer(); - "hello"d.copy(buf); - version (LittleEndian) - assert(buf.toBytes() == "h\x00\x00\x00e\x00\x00\x00l\x00\x00\x00l\x00\x00\x00o\x00\x00\x00"); - version (BigEndian) - assert(buf.toBytes() == "\x00\x00\x00h\x00\x00\x00e\x00\x00\x00l\x00\x00\x00l\x00\x00\x00o"); - } -} diff --git a/phobos/std/package.d b/phobos/std/package.d deleted file mode 100644 index e2eca7b..0000000 --- a/phobos/std/package.d +++ /dev/null @@ -1,85 +0,0 @@ -/++ -Convenience file that allows to import entire Phobos in one import. -+/ -module std; - -/// -@safe unittest -{ - import std; - - int len; - const r = 6.iota - .filter!(a => a % 2) // 1 3 5 - .map!(a => a * 2) // 2 6 10 - .tee!(_ => len++) - .substitute(6, -6) // 2 -6 10 - .sum - .reverseArgs!format("Sum: %d"); - - assert(len == 3); - assert(r == "Sum: 6"); -} - -/// -@safe unittest -{ - import std; - assert(10.iota.map!(a => pow(2, a)).sum == 1023); -} - -public import - std.algorithm, - std.array, - std.ascii, - std.base64, - std.bigint, - std.bitmanip, - std.checkedint, - std.compiler, - std.complex, - std.concurrency, - std.container, - std.conv, - std.csv, - std.datetime, - std.demangle, - std.digest, - std.encoding, - std.exception, - std.file, - std.format, - std.functional, - std.getopt, - std.int128, - std.json, - std.logger, - std.math, - std.mathspecial, - std.meta, - std.mmfile, - std.net.curl, - std.net.isemail, - std.numeric, - std.parallelism, - std.path, - std.process, - std.random, - std.range, - std.regex, - std.signals, - std.socket, - std.stdint, - std.stdio, - std.string, - std.sumtype, - std.system, - std.traits, - std.typecons, - std.uni, - std.uri, - std.utf, - std.uuid, - std.variant, - std.zip, - std.zlib; diff --git a/phobos/std/parallelism.d b/phobos/std/parallelism.d deleted file mode 100644 index fadb4c1..0000000 --- a/phobos/std/parallelism.d +++ /dev/null @@ -1,4925 +0,0 @@ -/** -`std.parallelism` implements high-level primitives for SMP parallelism. -These include parallel foreach, parallel reduce, parallel eager map, pipelining -and future/promise parallelism. `std.parallelism` is recommended when the -same operation is to be executed in parallel on different data, or when a -function is to be executed in a background thread and its result returned to a -well-defined main thread. For communication between arbitrary threads, see -`std.concurrency`. - -`std.parallelism` is based on the concept of a `Task`. A `Task` is an -object that represents the fundamental unit of work in this library and may be -executed in parallel with any other `Task`. Using `Task` -directly allows programming with a future/promise paradigm. All other -supported parallelism paradigms (parallel foreach, map, reduce, pipelining) -represent an additional level of abstraction over `Task`. They -automatically create one or more `Task` objects, or closely related types -that are conceptually identical but not part of the public API. - -After creation, a `Task` may be executed in a new thread, or submitted -to a `TaskPool` for execution. A `TaskPool` encapsulates a task queue -and its worker threads. Its purpose is to efficiently map a large -number of `Task`s onto a smaller number of threads. A task queue is a -FIFO queue of `Task` objects that have been submitted to the -`TaskPool` and are awaiting execution. A worker thread is a thread that -is associated with exactly one task queue. It executes the `Task` at the -front of its queue when the queue has work available, or sleeps when -no work is available. Each task queue is associated with zero or -more worker threads. If the result of a `Task` is needed before execution -by a worker thread has begun, the `Task` can be removed from the task queue -and executed immediately in the thread where the result is needed. - -Warning: Unless marked as `@trusted` or `@safe`, artifacts in - this module allow implicit data sharing between threads and cannot - guarantee that client code is free from low level data races. - -Source: $(PHOBOSSRC std/parallelism.d) -Author: David Simcha -Copyright: Copyright (c) 2009-2011, David Simcha. -License: $(HTTP boost.org/LICENSE_1_0.txt, Boost License 1.0) -*/ -module std.parallelism; - -version (OSX) - version = Darwin; -else version (iOS) - version = Darwin; -else version (TVOS) - version = Darwin; -else version (WatchOS) - version = Darwin; - -/// -@system unittest -{ - import std.algorithm.iteration : map; - import std.math.operations : isClose; - import std.parallelism : taskPool; - import std.range : iota; - - // Parallel reduce can be combined with - // std.algorithm.iteration.map to interesting effect. - // The following example (thanks to Russel Winder) - // calculates pi by quadrature using - // std.algorithm.map and TaskPool.reduce. - // getTerm is evaluated in parallel as needed by - // TaskPool.reduce. - // - // Timings on an Intel i5-3450 quad core machine - // for n = 1_000_000_000: - // - // TaskPool.reduce: 1.067 s - // std.algorithm.reduce: 4.011 s - - enum n = 1_000_000; - enum delta = 1.0 / n; - - alias getTerm = (int i) - { - immutable x = ( i - 0.5 ) * delta; - return delta / ( 1.0 + x * x ) ; - }; - - immutable pi = 4.0 * taskPool.reduce!"a + b"(n.iota.map!getTerm); - - assert(pi.isClose(3.14159, 1e-5)); -} - -import core.atomic; -import core.memory; -import core.sync.condition; -import core.thread; - -import std.functional; -import std.meta; -import std.range.primitives; -import std.traits; - -/* -(For now public undocumented with reserved name.) - -A lazily initialized global constant. The underlying value is a shared global -statically initialized to `outOfBandValue` which must not be a legit value of -the constant. Upon the first call the situation is detected and the global is -initialized by calling `initializer`. The initializer is assumed to be pure -(even if not marked as such), i.e. return the same value upon repeated calls. -For that reason, no special precautions are taken so `initializer` may be called -more than one time leading to benign races on the cached value. - -In the quiescent state the cost of the function is an atomic load from a global. - -Params: - T = The type of the pseudo-constant (may be qualified) - outOfBandValue = A value that cannot be valid, it is used for initialization - initializer = The function performing initialization; must be `nothrow` - -Returns: - The lazily initialized value -*/ -@property pure -T __lazilyInitializedConstant(T, alias outOfBandValue, alias initializer)() -if (is(Unqual!T : T) - && is(typeof(initializer()) : T) - && is(typeof(outOfBandValue) : T)) -{ - static T impl() nothrow - { - // Thread-local cache - static Unqual!T tls = outOfBandValue; - auto local = tls; - // Shortest path, no atomic operations - if (local != outOfBandValue) return local; - // Process-level cache - static shared Unqual!T result = outOfBandValue; - // Initialize both process-level cache and tls - local = atomicLoad(result); - if (local == outOfBandValue) - { - local = initializer(); - atomicStore(result, local); - } - tls = local; - return local; - } - - import std.traits : SetFunctionAttributes; - alias Fun = SetFunctionAttributes!(typeof(&impl), "D", - functionAttributes!(typeof(&impl)) | FunctionAttribute.pure_); - auto purified = (() @trusted => cast(Fun) &impl)(); - return purified(); -} - -// Returns the size of a cache line. -alias cacheLineSize = - __lazilyInitializedConstant!(immutable(size_t), size_t.max, cacheLineSizeImpl); - -private size_t cacheLineSizeImpl() @nogc nothrow @trusted -{ - size_t result = 0; - import core.cpuid : datacache; - foreach (ref const cachelevel; datacache) - { - if (cachelevel.lineSize > result && cachelevel.lineSize < uint.max) - { - result = cachelevel.lineSize; - } - } - return result; -} - -@nogc @safe nothrow unittest -{ - assert(cacheLineSize == cacheLineSizeImpl); -} - -/* Atomics code. These forward to core.atomic, but are written like this - for two reasons: - - 1. They used to actually contain ASM code and I don' want to have to change - to directly calling core.atomic in a zillion different places. - - 2. core.atomic has some misc. issues that make my use cases difficult - without wrapping it. If I didn't wrap it, casts would be required - basically everywhere. -*/ -private void atomicSetUbyte(T)(ref T stuff, T newVal) -if (__traits(isIntegral, T) && is(T : ubyte)) -{ - //core.atomic.cas(cast(shared) &stuff, stuff, newVal); - atomicStore(*(cast(shared) &stuff), newVal); -} - -private ubyte atomicReadUbyte(T)(ref T val) -if (__traits(isIntegral, T) && is(T : ubyte)) -{ - return atomicLoad(*(cast(shared) &val)); -} - -// This gets rid of the need for a lot of annoying casts in other parts of the -// code, when enums are involved. -private bool atomicCasUbyte(T)(ref T stuff, T testVal, T newVal) -if (__traits(isIntegral, T) && is(T : ubyte)) -{ - return core.atomic.cas(cast(shared) &stuff, testVal, newVal); -} - -/*--------------------- Generic helper functions, etc.------------------------*/ -private template MapType(R, functions...) -{ - static assert(functions.length); - - ElementType!R e = void; - alias MapType = - typeof(adjoin!(staticMap!(unaryFun, functions))(e)); -} - -private template ReduceType(alias fun, R, E) -{ - alias ReduceType = typeof(binaryFun!fun(E.init, ElementType!R.init)); -} - -private template noUnsharedAliasing(T) -{ - enum bool noUnsharedAliasing = !hasUnsharedAliasing!T; -} - -// This template tests whether a function may be executed in parallel from -// @safe code via Task.executeInNewThread(). There is an additional -// requirement for executing it via a TaskPool. (See isSafeReturn). -private template isSafeTask(F) -{ - enum bool isSafeTask = - (functionAttributes!F & (FunctionAttribute.safe | FunctionAttribute.trusted)) != 0 && - (functionAttributes!F & FunctionAttribute.ref_) == 0 && - (isFunctionPointer!F || !hasUnsharedAliasing!F) && - allSatisfy!(noUnsharedAliasing, Parameters!F); -} - -@safe unittest -{ - alias F1 = void function() @safe; - alias F2 = void function(); - alias F3 = void function(uint, string) @trusted; - alias F4 = void function(uint, char[]); - - static assert( isSafeTask!F1); - static assert(!isSafeTask!F2); - static assert( isSafeTask!F3); - static assert(!isSafeTask!F4); - - alias F5 = uint[] function(uint, string) pure @trusted; - static assert( isSafeTask!F5); -} - -// This function decides whether Tasks that meet all of the other requirements -// for being executed from @safe code can be executed on a TaskPool. -// When executing via TaskPool, it's theoretically possible -// to return a value that is also pointed to by a worker thread's thread local -// storage. When executing from executeInNewThread(), the thread that executed -// the Task is terminated by the time the return value is visible in the calling -// thread, so this is a non-issue. It's also a non-issue for pure functions -// since they can't read global state. -private template isSafeReturn(T) -{ - static if (!hasUnsharedAliasing!(T.ReturnType)) - { - enum isSafeReturn = true; - } - else static if (T.isPure) - { - enum isSafeReturn = true; - } - else - { - enum isSafeReturn = false; - } -} - -private template randAssignable(R) -{ - enum randAssignable = isRandomAccessRange!R && hasAssignableElements!R; -} - -private enum TaskStatus : ubyte -{ - notStarted, - inProgress, - done -} - -private template AliasReturn(alias fun, T...) -{ - alias AliasReturn = typeof({ T args; return fun(args); }); -} - -// Should be private, but std.algorithm.reduce is used in the zero-thread case -// and won't work w/ private. -template reduceAdjoin(functions...) -{ - static if (functions.length == 1) - { - alias reduceAdjoin = binaryFun!(functions[0]); - } - else - { - T reduceAdjoin(T, U)(T lhs, U rhs) - { - alias funs = staticMap!(binaryFun, functions); - - foreach (i, Unused; typeof(lhs.expand)) - { - lhs.expand[i] = funs[i](lhs.expand[i], rhs); - } - - return lhs; - } - } -} - -private template reduceFinish(functions...) -{ - static if (functions.length == 1) - { - alias reduceFinish = binaryFun!(functions[0]); - } - else - { - T reduceFinish(T)(T lhs, T rhs) - { - alias funs = staticMap!(binaryFun, functions); - - foreach (i, Unused; typeof(lhs.expand)) - { - lhs.expand[i] = funs[i](lhs.expand[i], rhs.expand[i]); - } - - return lhs; - } - } -} - -private template isRoundRobin(R : RoundRobinBuffer!(C1, C2), C1, C2) -{ - enum isRoundRobin = true; -} - -private template isRoundRobin(T) -{ - enum isRoundRobin = false; -} - -@safe unittest -{ - static assert( isRoundRobin!(RoundRobinBuffer!(void delegate(char[]), bool delegate()))); - static assert(!isRoundRobin!(uint)); -} - -// This is the base "class" for all of the other tasks. Using C-style -// polymorphism to allow more direct control over memory allocation, etc. -private struct AbstractTask -{ - AbstractTask* prev; - AbstractTask* next; - - // Pointer to a function that executes this task. - void function(void*) runTask; - - Throwable exception; - ubyte taskStatus = TaskStatus.notStarted; - - bool done() @property - { - if (atomicReadUbyte(taskStatus) == TaskStatus.done) - { - if (exception) - { - throw exception; - } - - return true; - } - - return false; - } - - void job() - { - runTask(&this); - } -} - -/** -`Task` represents the fundamental unit of work. A `Task` may be -executed in parallel with any other `Task`. Using this struct directly -allows future/promise parallelism. In this paradigm, a function (or delegate -or other callable) is executed in a thread other than the one it was called -from. The calling thread does not block while the function is being executed. -A call to `workForce`, `yieldForce`, or `spinForce` is used to -ensure that the `Task` has finished executing and to obtain the return -value, if any. These functions and `done` also act as full memory barriers, -meaning that any memory writes made in the thread that executed the `Task` -are guaranteed to be visible in the calling thread after one of these functions -returns. - -The $(REF task, std,parallelism) and $(REF scopedTask, std,parallelism) functions can -be used to create an instance of this struct. See `task` for usage examples. - -Function results are returned from `yieldForce`, `spinForce` and -`workForce` by ref. If `fun` returns by ref, the reference will point -to the returned reference of `fun`. Otherwise it will point to a -field in this struct. - -Copying of this struct is disabled, since it would provide no useful semantics. -If you want to pass this struct around, you should do so by reference or -pointer. - -Bugs: Changes to `ref` and `out` arguments are not propagated to the - call site, only to `args` in this struct. -*/ -struct Task(alias fun, Args...) -{ - private AbstractTask base = {runTask : &impl}; - private alias base this; - - private @property AbstractTask* basePtr() - { - return &base; - } - - private static void impl(void* myTask) - { - import std.algorithm.internal : addressOf; - - Task* myCastedTask = cast(typeof(this)*) myTask; - static if (is(ReturnType == void)) - { - fun(myCastedTask._args); - } - else static if (is(typeof(&(fun(myCastedTask._args))))) - { - myCastedTask.returnVal = addressOf(fun(myCastedTask._args)); - } - else - { - myCastedTask.returnVal = fun(myCastedTask._args); - } - } - - private TaskPool pool; - private bool isScoped; // True if created with scopedTask. - - Args _args; - - /** - The arguments the function was called with. Changes to `out` and - `ref` arguments will be visible here. - */ - static if (__traits(isSame, fun, run)) - { - alias args = _args[1..$]; - } - else - { - alias args = _args; - } - - - // The purpose of this code is to decide whether functions whose - // return values have unshared aliasing can be executed via - // TaskPool from @safe code. See isSafeReturn. - static if (__traits(isSame, fun, run)) - { - static if (isFunctionPointer!(_args[0])) - { - private enum bool isPure = - (functionAttributes!(Args[0]) & FunctionAttribute.pure_) != 0; - } - else - { - // BUG: Should check this for delegates too, but std.traits - // apparently doesn't allow this. isPure is irrelevant - // for delegates, at least for now since shared delegates - // don't work. - private enum bool isPure = false; - } - - } - else - { - // We already know that we can't execute aliases in @safe code, so - // just put a dummy value here. - private enum bool isPure = false; - } - - - /** - The return type of the function called by this `Task`. This can be - `void`. - */ - alias ReturnType = typeof(fun(_args)); - - static if (!is(ReturnType == void)) - { - static if (is(typeof(&fun(_args)))) - { - // Ref return. - ReturnType* returnVal; - - ref ReturnType fixRef(ReturnType* val) - { - return *val; - } - - } - else - { - ReturnType returnVal; - - ref ReturnType fixRef(ref ReturnType val) - { - return val; - } - } - } - - private void enforcePool() - { - import std.exception : enforce; - enforce(this.pool !is null, "Job not submitted yet."); - } - - static if (Args.length > 0) - { - private this(Args args) - { - _args = args; - } - } - - // Work around DMD bug https://issues.dlang.org/show_bug.cgi?id=6588, - // allow immutable elements. - static if (allSatisfy!(isAssignable, Args)) - { - typeof(this) opAssign(typeof(this) rhs) - { - foreach (i, Type; typeof(this.tupleof)) - { - this.tupleof[i] = rhs.tupleof[i]; - } - return this; - } - } - else - { - @disable typeof(this) opAssign(typeof(this) rhs); - } - - /** - If the `Task` isn't started yet, execute it in the current thread. - If it's done, return its return value, if any. If it's in progress, - busy spin until it's done, then return the return value. If it threw - an exception, rethrow that exception. - - This function should be used when you expect the result of the - `Task` to be available on a timescale shorter than that of an OS - context switch. - */ - @property ref ReturnType spinForce() @trusted - { - enforcePool(); - - this.pool.tryDeleteExecute(basePtr); - - while (atomicReadUbyte(this.taskStatus) != TaskStatus.done) {} - - if (exception) - { - throw exception; - } - - static if (!is(ReturnType == void)) - { - return fixRef(this.returnVal); - } - } - - /** - If the `Task` isn't started yet, execute it in the current thread. - If it's done, return its return value, if any. If it's in progress, - wait on a condition variable. If it threw an exception, rethrow that - exception. - - This function should be used for expensive functions, as waiting on a - condition variable introduces latency, but avoids wasted CPU cycles. - */ - @property ref ReturnType yieldForce() @trusted - { - enforcePool(); - this.pool.tryDeleteExecute(basePtr); - - if (done) - { - static if (is(ReturnType == void)) - { - return; - } - else - { - return fixRef(this.returnVal); - } - } - - pool.waiterLock(); - scope(exit) pool.waiterUnlock(); - - while (atomicReadUbyte(this.taskStatus) != TaskStatus.done) - { - pool.waitUntilCompletion(); - } - - if (exception) - { - throw exception; // nocoverage - } - - static if (!is(ReturnType == void)) - { - return fixRef(this.returnVal); - } - } - - /** - If this `Task` was not started yet, execute it in the current - thread. If it is finished, return its result. If it is in progress, - execute any other `Task` from the `TaskPool` instance that - this `Task` was submitted to until this one - is finished. If it threw an exception, rethrow that exception. - If no other tasks are available or this `Task` was executed using - `executeInNewThread`, wait on a condition variable. - */ - @property ref ReturnType workForce() @trusted - { - enforcePool(); - this.pool.tryDeleteExecute(basePtr); - - while (true) - { - if (done) // done() implicitly checks for exceptions. - { - static if (is(ReturnType == void)) - { - return; - } - else - { - return fixRef(this.returnVal); - } - } - - AbstractTask* job; - { - // Locking explicitly and calling popNoSync() because - // pop() waits on a condition variable if there are no Tasks - // in the queue. - - pool.queueLock(); - scope(exit) pool.queueUnlock(); - job = pool.popNoSync(); - } - - - if (job !is null) - { - - version (verboseUnittest) - { - stderr.writeln("Doing workForce work."); - } - - pool.doJob(job); - - if (done) - { - static if (is(ReturnType == void)) - { - return; - } - else - { - return fixRef(this.returnVal); - } - } - } - else - { - version (verboseUnittest) - { - stderr.writeln("Yield from workForce."); - } - - return yieldForce; - } - } - } - - /** - Returns `true` if the `Task` is finished executing. - - Throws: Rethrows any exception thrown during the execution of the - `Task`. - */ - @property bool done() @trusted - { - // Explicitly forwarded for documentation purposes. - return base.done; - } - - /** - Create a new thread for executing this `Task`, execute it in the - newly created thread, then terminate the thread. This can be used for - future/promise parallelism. An explicit priority may be given - to the `Task`. If one is provided, its value is forwarded to - `core.thread.Thread.priority`. See $(REF task, std,parallelism) for - usage example. - */ - void executeInNewThread() @trusted - { - pool = new TaskPool(basePtr); - } - - /// Ditto - void executeInNewThread(int priority) @trusted - { - pool = new TaskPool(basePtr, priority); - } - - @safe ~this() - { - if (isScoped && pool !is null && taskStatus != TaskStatus.done) - { - yieldForce; - } - } - - // When this is uncommented, it somehow gets called on returning from - // scopedTask even though the struct shouldn't be getting copied. - //@disable this(this) {} -} - -// Calls `fpOrDelegate` with `args`. This is an -// adapter that makes `Task` work with delegates, function pointers and -// functors instead of just aliases. -ReturnType!F run(F, Args...)(F fpOrDelegate, ref Args args) -{ - return fpOrDelegate(args); -} - -/** -Creates a `Task` on the GC heap that calls an alias. This may be executed -via `Task.executeInNewThread` or by submitting to a -$(REF TaskPool, std,parallelism). A globally accessible instance of -`TaskPool` is provided by $(REF taskPool, std,parallelism). - -Returns: A pointer to the `Task`. - -Example: ---- -// Read two files into memory at the same time. -import std.file; - -void main() -{ - // Create and execute a Task for reading - // foo.txt. - auto file1Task = task!read("foo.txt"); - file1Task.executeInNewThread(); - - // Read bar.txt in parallel. - auto file2Data = read("bar.txt"); - - // Get the results of reading foo.txt. - auto file1Data = file1Task.yieldForce; -} ---- - ---- -// Sorts an array using a parallel quick sort algorithm. -// The first partition is done serially. Both recursion -// branches are then executed in parallel. -// -// Timings for sorting an array of 1,000,000 doubles on -// an Athlon 64 X2 dual core machine: -// -// This implementation: 176 milliseconds. -// Equivalent serial implementation: 280 milliseconds -void parallelSort(T)(T[] data) -{ - // Sort small subarrays serially. - if (data.length < 100) - { - std.algorithm.sort(data); - return; - } - - // Partition the array. - swap(data[$ / 2], data[$ - 1]); - auto pivot = data[$ - 1]; - bool lessThanPivot(T elem) { return elem < pivot; } - - auto greaterEqual = partition!lessThanPivot(data[0..$ - 1]); - swap(data[$ - greaterEqual.length - 1], data[$ - 1]); - - auto less = data[0..$ - greaterEqual.length - 1]; - greaterEqual = data[$ - greaterEqual.length..$]; - - // Execute both recursion branches in parallel. - auto recurseTask = task!parallelSort(greaterEqual); - taskPool.put(recurseTask); - parallelSort(less); - recurseTask.yieldForce; -} ---- -*/ -auto task(alias fun, Args...)(Args args) -{ - return new Task!(fun, Args)(args); -} - -/** -Creates a `Task` on the GC heap that calls a function pointer, delegate, or -class/struct with overloaded opCall. - -Example: ---- -// Read two files in at the same time again, -// but this time use a function pointer instead -// of an alias to represent std.file.read. -import std.file; - -void main() -{ - // Create and execute a Task for reading - // foo.txt. - auto file1Task = task(&read!string, "foo.txt", size_t.max); - file1Task.executeInNewThread(); - - // Read bar.txt in parallel. - auto file2Data = read("bar.txt"); - - // Get the results of reading foo.txt. - auto file1Data = file1Task.yieldForce; -} ---- - -Notes: This function takes a non-scope delegate, meaning it can be - used with closures. If you can't allocate a closure due to objects - on the stack that have scoped destruction, see `scopedTask`, which - takes a scope delegate. - */ -auto task(F, Args...)(F delegateOrFp, Args args) -if (is(typeof(delegateOrFp(args))) && !isSafeTask!F) -{ - return new Task!(run, F, Args)(delegateOrFp, args); -} - -/** -Version of `task` usable from `@safe` code. Usage mechanics are -identical to the non-@safe case, but safety introduces some restrictions: - -1. `fun` must be @safe or @trusted. - -2. `F` must not have any unshared aliasing as defined by - $(REF hasUnsharedAliasing, std,traits). This means it - may not be an unshared delegate or a non-shared class or struct - with overloaded `opCall`. This also precludes accepting template - alias parameters. - -3. `Args` must not have unshared aliasing. - -4. `fun` must not return by reference. - -5. The return type must not have unshared aliasing unless `fun` is - `pure` or the `Task` is executed via `executeInNewThread` instead - of using a `TaskPool`. - -*/ -@trusted auto task(F, Args...)(F fun, Args args) -if (is(typeof(fun(args))) && isSafeTask!F) -{ - return new Task!(run, F, Args)(fun, args); -} - -/** -These functions allow the creation of `Task` objects on the stack rather -than the GC heap. The lifetime of a `Task` created by `scopedTask` -cannot exceed the lifetime of the scope it was created in. - -`scopedTask` might be preferred over `task`: - -1. When a `Task` that calls a delegate is being created and a closure - cannot be allocated due to objects on the stack that have scoped - destruction. The delegate overload of `scopedTask` takes a `scope` - delegate. - -2. As a micro-optimization, to avoid the heap allocation associated with - `task` or with the creation of a closure. - -Usage is otherwise identical to `task`. - -Notes: `Task` objects created using `scopedTask` will automatically -call `Task.yieldForce` in their destructor if necessary to ensure -the `Task` is complete before the stack frame they reside on is destroyed. -*/ -auto scopedTask(alias fun, Args...)(Args args) -{ - auto ret = Task!(fun, Args)(args); - ret.isScoped = true; - return ret; -} - -/// Ditto -auto scopedTask(F, Args...)(scope F delegateOrFp, Args args) -if (is(typeof(delegateOrFp(args))) && !isSafeTask!F) -{ - auto ret = Task!(run, F, Args)(delegateOrFp, args); - ret.isScoped = true; - return ret; -} - -/// Ditto -@trusted auto scopedTask(F, Args...)(F fun, Args args) -if (is(typeof(fun(args))) && isSafeTask!F) -{ - auto ret = Task!(run, F, Args)(fun, args); - ret.isScoped = true; - return ret; -} - -/** -The total number of CPU cores available on the current machine, as reported by -the operating system. -*/ -alias totalCPUs = - __lazilyInitializedConstant!(immutable(uint), uint.max, totalCPUsImpl); - -uint totalCPUsImpl() @nogc nothrow @trusted -{ - version (Windows) - { - // BUGS: Only works on Windows 2000 and above. - import core.sys.windows.winbase : SYSTEM_INFO, GetSystemInfo; - import std.algorithm.comparison : max; - SYSTEM_INFO si; - GetSystemInfo(&si); - return max(1, cast(uint) si.dwNumberOfProcessors); - } - else version (linux) - { - import core.stdc.stdlib : calloc; - import core.stdc.string : memset; - import core.sys.linux.sched : CPU_ALLOC_SIZE, CPU_FREE, CPU_COUNT, CPU_COUNT_S, cpu_set_t, sched_getaffinity; - import core.sys.posix.unistd : _SC_NPROCESSORS_ONLN, sysconf; - - int count = 0; - - /** - * According to ruby's source code, CPU_ALLOC() doesn't work as expected. - * see: https://github.com/ruby/ruby/commit/7d9e04de496915dd9e4544ee18b1a0026dc79242 - * - * The hardcode number also comes from ruby's source code. - * see: https://github.com/ruby/ruby/commit/0fa75e813ecf0f5f3dd01f89aa76d0e25ab4fcd4 - */ - for (int n = 64; n <= 16384; n *= 2) - { - size_t size = CPU_ALLOC_SIZE(count); - if (size >= 0x400) - { - auto cpuset = cast(cpu_set_t*) calloc(1, size); - if (cpuset is null) break; - if (sched_getaffinity(0, size, cpuset) == 0) - { - count = CPU_COUNT_S(size, cpuset); - } - CPU_FREE(cpuset); - } - else - { - cpu_set_t cpuset; - if (sched_getaffinity(0, cpu_set_t.sizeof, &cpuset) == 0) - { - count = CPU_COUNT(&cpuset); - } - } - - if (count > 0) - return cast(uint) count; - } - - return cast(uint) sysconf(_SC_NPROCESSORS_ONLN); - } - else version (Darwin) - { - import core.sys.darwin.sys.sysctl : sysctlbyname; - uint result; - size_t len = result.sizeof; - sysctlbyname("hw.physicalcpu", &result, &len, null, 0); - return result; - } - else version (DragonFlyBSD) - { - import core.sys.dragonflybsd.sys.sysctl : sysctlbyname; - uint result; - size_t len = result.sizeof; - sysctlbyname("hw.ncpu", &result, &len, null, 0); - return result; - } - else version (FreeBSD) - { - import core.sys.freebsd.sys.sysctl : sysctlbyname; - uint result; - size_t len = result.sizeof; - sysctlbyname("hw.ncpu", &result, &len, null, 0); - return result; - } - else version (NetBSD) - { - import core.sys.netbsd.sys.sysctl : sysctlbyname; - uint result; - size_t len = result.sizeof; - sysctlbyname("hw.ncpu", &result, &len, null, 0); - return result; - } - else version (Solaris) - { - import core.sys.posix.unistd : _SC_NPROCESSORS_ONLN, sysconf; - return cast(uint) sysconf(_SC_NPROCESSORS_ONLN); - } - else version (OpenBSD) - { - import core.sys.posix.unistd : _SC_NPROCESSORS_ONLN, sysconf; - return cast(uint) sysconf(_SC_NPROCESSORS_ONLN); - } - else version (Hurd) - { - import core.sys.posix.unistd : _SC_NPROCESSORS_ONLN, sysconf; - return cast(uint) sysconf(_SC_NPROCESSORS_ONLN); - } - else - { - static assert(0, "Don't know how to get N CPUs on this OS."); - } -} - -/* -This class serves two purposes: - -1. It distinguishes std.parallelism threads from other threads so that - the std.parallelism daemon threads can be terminated. - -2. It adds a reference to the pool that the thread is a member of, - which is also necessary to allow the daemon threads to be properly - terminated. -*/ -private final class ParallelismThread : Thread -{ - this(void delegate() dg) - { - super(dg); - } - - TaskPool pool; -} - -// Kill daemon threads. -shared static ~this() -{ - foreach (ref thread; Thread) - { - auto pthread = cast(ParallelismThread) thread; - if (pthread is null) continue; - auto pool = pthread.pool; - if (!pool.isDaemon) continue; - pool.stop(); - pthread.join(); - } -} - -/** -This class encapsulates a task queue and a set of worker threads. Its purpose -is to efficiently map a large number of `Task`s onto a smaller number of -threads. A task queue is a FIFO queue of `Task` objects that have been -submitted to the `TaskPool` and are awaiting execution. A worker thread is a -thread that executes the `Task` at the front of the queue when one is -available and sleeps when the queue is empty. - -This class should usually be used via the global instantiation -available via the $(REF taskPool, std,parallelism) property. -Occasionally it is useful to explicitly instantiate a `TaskPool`: - -1. When you want `TaskPool` instances with multiple priorities, for example - a low priority pool and a high priority pool. - -2. When the threads in the global task pool are waiting on a synchronization - primitive (for example a mutex), and you want to parallelize the code that - needs to run before these threads can be resumed. - -Note: The worker threads in this pool will not stop until - `stop` or `finish` is called, even if the main thread - has finished already. This may lead to programs that - never end. If you do not want this behaviour, you can set `isDaemon` - to true. - */ -final class TaskPool -{ -private: - - // A pool can either be a regular pool or a single-task pool. A - // single-task pool is a dummy pool that's fired up for - // Task.executeInNewThread(). - bool isSingleTask; - - ParallelismThread[] pool; - Thread singleTaskThread; - - AbstractTask* head; - AbstractTask* tail; - PoolState status = PoolState.running; - Condition workerCondition; - Condition waiterCondition; - Mutex queueMutex; - Mutex waiterMutex; // For waiterCondition - - // The instanceStartIndex of the next instance that will be created. - __gshared size_t nextInstanceIndex = 1; - - // The index of the current thread. - static size_t threadIndex; - - // The index of the first thread in this instance. - immutable size_t instanceStartIndex; - - // The index that the next thread to be initialized in this pool will have. - size_t nextThreadIndex; - - enum PoolState : ubyte - { - running, - finishing, - stopNow - } - - void doJob(AbstractTask* job) - { - assert(job.taskStatus == TaskStatus.inProgress); - assert(job.next is null); - assert(job.prev is null); - - scope(exit) - { - if (!isSingleTask) - { - waiterLock(); - scope(exit) waiterUnlock(); - notifyWaiters(); - } - } - - try - { - job.job(); - } - catch (Throwable e) - { - job.exception = e; - } - - atomicSetUbyte(job.taskStatus, TaskStatus.done); - } - - // This function is used for dummy pools created by Task.executeInNewThread(). - void doSingleTask() - { - // No synchronization. Pool is guaranteed to only have one thread, - // and the queue is submitted to before this thread is created. - assert(head); - auto t = head; - t.next = t.prev = head = null; - doJob(t); - } - - // This function performs initialization for each thread that affects - // thread local storage and therefore must be done from within the - // worker thread. It then calls executeWorkLoop(). - void startWorkLoop() - { - // Initialize thread index. - { - queueLock(); - scope(exit) queueUnlock(); - threadIndex = nextThreadIndex; - nextThreadIndex++; - } - - executeWorkLoop(); - } - - // This is the main work loop that worker threads spend their time in - // until they terminate. It's also entered by non-worker threads when - // finish() is called with the blocking variable set to true. - void executeWorkLoop() - { - while (atomicReadUbyte(status) != PoolState.stopNow) - { - AbstractTask* task = pop(); - if (task is null) - { - if (atomicReadUbyte(status) == PoolState.finishing) - { - atomicSetUbyte(status, PoolState.stopNow); - return; - } - } - else - { - doJob(task); - } - } - } - - // Pop a task off the queue. - AbstractTask* pop() - { - queueLock(); - scope(exit) queueUnlock(); - auto ret = popNoSync(); - while (ret is null && status == PoolState.running) - { - wait(); - ret = popNoSync(); - } - return ret; - } - - AbstractTask* popNoSync() - out(returned) - { - /* If task.prev and task.next aren't null, then another thread - * can try to delete this task from the pool after it's - * alreadly been deleted/popped. - */ - if (returned !is null) - { - assert(returned.next is null); - assert(returned.prev is null); - } - } - do - { - if (isSingleTask) return null; - - AbstractTask* returned = head; - if (head !is null) - { - head = head.next; - returned.prev = null; - returned.next = null; - returned.taskStatus = TaskStatus.inProgress; - } - if (head !is null) - { - head.prev = null; - } - - return returned; - } - - // Push a task onto the queue. - void abstractPut(AbstractTask* task) - { - queueLock(); - scope(exit) queueUnlock(); - abstractPutNoSync(task); - } - - void abstractPutNoSync(AbstractTask* task) - in - { - assert(task); - } - out - { - import std.conv : text; - - assert(tail.prev !is tail); - assert(tail.next is null, text(tail.prev, '\t', tail.next)); - if (tail.prev !is null) - { - assert(tail.prev.next is tail, text(tail.prev, '\t', tail.next)); - } - } - do - { - // Not using enforce() to save on function call overhead since this - // is a performance critical function. - if (status != PoolState.running) - { - throw new Error( - "Cannot submit a new task to a pool after calling " ~ - "finish() or stop()." - ); - } - - task.next = null; - if (head is null) //Queue is empty. - { - head = task; - tail = task; - tail.prev = null; - } - else - { - assert(tail); - task.prev = tail; - tail.next = task; - tail = task; - } - notify(); - } - - void abstractPutGroupNoSync(AbstractTask* h, AbstractTask* t) - { - if (status != PoolState.running) - { - throw new Error( - "Cannot submit a new task to a pool after calling " ~ - "finish() or stop()." - ); - } - - if (head is null) - { - head = h; - tail = t; - } - else - { - h.prev = tail; - tail.next = h; - tail = t; - } - - notifyAll(); - } - - void tryDeleteExecute(AbstractTask* toExecute) - { - if (isSingleTask) return; - - if ( !deleteItem(toExecute) ) - { - return; - } - - try - { - toExecute.job(); - } - catch (Exception e) - { - toExecute.exception = e; - } - - atomicSetUbyte(toExecute.taskStatus, TaskStatus.done); - } - - bool deleteItem(AbstractTask* item) - { - queueLock(); - scope(exit) queueUnlock(); - return deleteItemNoSync(item); - } - - bool deleteItemNoSync(AbstractTask* item) - { - if (item.taskStatus != TaskStatus.notStarted) - { - return false; - } - item.taskStatus = TaskStatus.inProgress; - - if (item is head) - { - // Make sure head gets set properly. - popNoSync(); - return true; - } - if (item is tail) - { - tail = tail.prev; - if (tail !is null) - { - tail.next = null; - } - item.next = null; - item.prev = null; - return true; - } - if (item.next !is null) - { - assert(item.next.prev is item); // Check queue consistency. - item.next.prev = item.prev; - } - if (item.prev !is null) - { - assert(item.prev.next is item); // Check queue consistency. - item.prev.next = item.next; - } - item.next = null; - item.prev = null; - return true; - } - - void queueLock() - { - assert(queueMutex); - if (!isSingleTask) queueMutex.lock(); - } - - void queueUnlock() - { - assert(queueMutex); - if (!isSingleTask) queueMutex.unlock(); - } - - void waiterLock() - { - if (!isSingleTask) waiterMutex.lock(); - } - - void waiterUnlock() - { - if (!isSingleTask) waiterMutex.unlock(); - } - - void wait() - { - if (!isSingleTask) workerCondition.wait(); - } - - void notify() - { - if (!isSingleTask) workerCondition.notify(); - } - - void notifyAll() - { - if (!isSingleTask) workerCondition.notifyAll(); - } - - void waitUntilCompletion() - { - if (isSingleTask) - { - singleTaskThread.join(); - } - else - { - waiterCondition.wait(); - } - } - - void notifyWaiters() - { - if (!isSingleTask) waiterCondition.notifyAll(); - } - - // Private constructor for creating dummy pools that only have one thread, - // only execute one Task, and then terminate. This is used for - // Task.executeInNewThread(). - this(AbstractTask* task, int priority = int.max) - { - assert(task); - - // Dummy value, not used. - instanceStartIndex = 0; - - this.isSingleTask = true; - task.taskStatus = TaskStatus.inProgress; - this.head = task; - singleTaskThread = new Thread(&doSingleTask); - singleTaskThread.start(); - - // Disabled until writing code to support - // running thread with specified priority - // See https://issues.dlang.org/show_bug.cgi?id=8960 - - /*if (priority != int.max) - { - singleTaskThread.priority = priority; - }*/ - } - -public: - // This is used in parallel_algorithm but is too unstable to document - // as public API. - size_t defaultWorkUnitSize(size_t rangeLen) const @safe pure nothrow - { - import std.algorithm.comparison : max; - - if (this.size == 0) - { - return max(rangeLen, 1); - } - - immutable size_t eightSize = 4 * (this.size + 1); - auto ret = (rangeLen / eightSize) + ((rangeLen % eightSize == 0) ? 0 : 1); - return max(ret, 1); - } - - /** - Default constructor that initializes a `TaskPool` with - `totalCPUs` - 1 worker threads. The minus 1 is included because the - main thread will also be available to do work. - - Note: On single-core machines, the primitives provided by `TaskPool` - operate transparently in single-threaded mode. - */ - this() @trusted - { - this(totalCPUs - 1); - } - - /** - Allows for custom number of worker threads. - */ - this(size_t nWorkers) @trusted - { - synchronized(typeid(TaskPool)) - { - instanceStartIndex = nextInstanceIndex; - - // The first worker thread to be initialized will have this index, - // and will increment it. The second worker to be initialized will - // have this index plus 1. - nextThreadIndex = instanceStartIndex; - nextInstanceIndex += nWorkers; - } - - queueMutex = new Mutex(this); - waiterMutex = new Mutex(); - workerCondition = new Condition(queueMutex); - waiterCondition = new Condition(waiterMutex); - - pool = new ParallelismThread[nWorkers]; - foreach (ref poolThread; pool) - { - poolThread = new ParallelismThread(&startWorkLoop); - poolThread.pool = this; - poolThread.start(); - } - } - - /** - Implements a parallel foreach loop over a range. This works by implicitly - creating and submitting one `Task` to the `TaskPool` for each worker - thread. A work unit is a set of consecutive elements of `range` to - be processed by a worker thread between communication with any other - thread. The number of elements processed per work unit is controlled by the - `workUnitSize` parameter. Smaller work units provide better load - balancing, but larger work units avoid the overhead of communicating - with other threads frequently to fetch the next work unit. Large work - units also avoid false sharing in cases where the range is being modified. - The less time a single iteration of the loop takes, the larger - `workUnitSize` should be. For very expensive loop bodies, - `workUnitSize` should be 1. An overload that chooses a default work - unit size is also available. - - Example: - --- - // Find the logarithm of every number from 1 to - // 10_000_000 in parallel. - auto logs = new double[10_000_000]; - - // Parallel foreach works with or without an index - // variable. It can iterate by ref if range.front - // returns by ref. - - // Iterate over logs using work units of size 100. - foreach (i, ref elem; taskPool.parallel(logs, 100)) - { - elem = log(i + 1.0); - } - - // Same thing, but use the default work unit size. - // - // Timings on an Athlon 64 X2 dual core machine: - // - // Parallel foreach: 388 milliseconds - // Regular foreach: 619 milliseconds - foreach (i, ref elem; taskPool.parallel(logs)) - { - elem = log(i + 1.0); - } - --- - - Notes: - - The memory usage of this implementation is guaranteed to be constant - in `range.length`. - - Breaking from a parallel foreach loop via a break, labeled break, - labeled continue, return or goto statement throws a - `ParallelForeachError`. - - In the case of non-random access ranges, parallel foreach buffers lazily - to an array of size `workUnitSize` before executing the parallel portion - of the loop. The exception is that, if a parallel foreach is executed - over a range returned by `asyncBuf` or `map`, the copying is elided - and the buffers are simply swapped. In this case `workUnitSize` is - ignored and the work unit size is set to the buffer size of `range`. - - A memory barrier is guaranteed to be executed on exit from the loop, - so that results produced by all threads are visible in the calling thread. - - $(B Exception Handling): - - When at least one exception is thrown from inside a parallel foreach loop, - the submission of additional `Task` objects is terminated as soon as - possible, in a non-deterministic manner. All executing or - enqueued work units are allowed to complete. Then, all exceptions that - were thrown by any work unit are chained using `Throwable.next` and - rethrown. The order of the exception chaining is non-deterministic. - */ - ParallelForeach!R parallel(R)(R range, size_t workUnitSize) - { - import std.exception : enforce; - enforce(workUnitSize > 0, "workUnitSize must be > 0."); - alias RetType = ParallelForeach!R; - return RetType(this, range, workUnitSize); - } - - - /// Ditto - ParallelForeach!R parallel(R)(R range) - { - static if (hasLength!R) - { - // Default work unit size is such that we would use 4x as many - // slots as are in this thread pool. - size_t workUnitSize = defaultWorkUnitSize(range.length); - return parallel(range, workUnitSize); - } - else - { - // Just use a really, really dumb guess if the user is too lazy to - // specify. - return parallel(range, 512); - } - } - - /// - template amap(functions...) - { - /** - Eager parallel map. The eagerness of this function means it has less - overhead than the lazily evaluated `TaskPool.map` and should be - preferred where the memory requirements of eagerness are acceptable. - `functions` are the functions to be evaluated, passed as template - alias parameters in a style similar to - $(REF map, std,algorithm,iteration). - The first argument must be a random access range. For performance - reasons, amap will assume the range elements have not yet been - initialized. Elements will be overwritten without calling a destructor - nor doing an assignment. As such, the range must not contain meaningful - data$(DDOC_COMMENT not a section): either un-initialized objects, or - objects in their `.init` state. - - --- - auto numbers = iota(100_000_000.0); - - // Find the square roots of numbers. - // - // Timings on an Athlon 64 X2 dual core machine: - // - // Parallel eager map: 0.802 s - // Equivalent serial implementation: 1.768 s - auto squareRoots = taskPool.amap!sqrt(numbers); - --- - - Immediately after the range argument, an optional work unit size argument - may be provided. Work units as used by `amap` are identical to those - defined for parallel foreach. If no work unit size is provided, the - default work unit size is used. - - --- - // Same thing, but make work unit size 100. - auto squareRoots = taskPool.amap!sqrt(numbers, 100); - --- - - An output range for returning the results may be provided as the last - argument. If one is not provided, an array of the proper type will be - allocated on the garbage collected heap. If one is provided, it must be a - random access range with assignable elements, must have reference - semantics with respect to assignment to its elements, and must have the - same length as the input range. Writing to adjacent elements from - different threads must be safe. - - --- - // Same thing, but explicitly allocate an array - // to return the results in. The element type - // of the array may be either the exact type - // returned by functions or an implicit conversion - // target. - auto squareRoots = new float[numbers.length]; - taskPool.amap!sqrt(numbers, squareRoots); - - // Multiple functions, explicit output range, and - // explicit work unit size. - auto results = new Tuple!(float, real)[numbers.length]; - taskPool.amap!(sqrt, log)(numbers, 100, results); - --- - - Note: - - A memory barrier is guaranteed to be executed after all results are written - but before returning so that results produced by all threads are visible - in the calling thread. - - Tips: - - To perform the mapping operation in place, provide the same range for the - input and output range. - - To parallelize the copying of a range with expensive to evaluate elements - to an array, pass an identity function (a function that just returns - whatever argument is provided to it) to `amap`. - - $(B Exception Handling): - - When at least one exception is thrown from inside the map functions, - the submission of additional `Task` objects is terminated as soon as - possible, in a non-deterministic manner. All currently executing or - enqueued work units are allowed to complete. Then, all exceptions that - were thrown from any work unit are chained using `Throwable.next` and - rethrown. The order of the exception chaining is non-deterministic. - */ - auto amap(Args...)(Args args) - if (isRandomAccessRange!(Args[0])) - { - import core.internal.lifetime : emplaceRef; - - alias fun = adjoin!(staticMap!(unaryFun, functions)); - - alias range = args[0]; - immutable len = range.length; - - static if ( - Args.length > 1 && - randAssignable!(Args[$ - 1]) && - is(MapType!(Args[0], functions) : ElementType!(Args[$ - 1])) - ) - { - import std.conv : text; - import std.exception : enforce; - - alias buf = args[$ - 1]; - alias args2 = args[0..$ - 1]; - alias Args2 = Args[0..$ - 1]; - enforce(buf.length == len, - text("Can't use a user supplied buffer that's the wrong ", - "size. (Expected :", len, " Got: ", buf.length)); - } - else static if (randAssignable!(Args[$ - 1]) && Args.length > 1) - { - static assert(0, "Wrong buffer type."); - } - else - { - import std.array : uninitializedArray; - - auto buf = uninitializedArray!(MapType!(Args[0], functions)[])(len); - alias args2 = args; - alias Args2 = Args; - } - - if (!len) return buf; - - static if (isIntegral!(Args2[$ - 1])) - { - static assert(args2.length == 2); - auto workUnitSize = cast(size_t) args2[1]; - } - else - { - static assert(args2.length == 1, Args); - auto workUnitSize = defaultWorkUnitSize(range.length); - } - - alias R = typeof(range); - - if (workUnitSize > len) - { - workUnitSize = len; - } - - // Handle as a special case: - if (size == 0) - { - size_t index = 0; - foreach (elem; range) - { - emplaceRef(buf[index++], fun(elem)); - } - return buf; - } - - // Effectively -1: chunkIndex + 1 == 0: - shared size_t workUnitIndex = size_t.max; - shared bool shouldContinue = true; - - void doIt() - { - import std.algorithm.comparison : min; - - scope(failure) - { - // If an exception is thrown, all threads should bail. - atomicStore(shouldContinue, false); - } - - while (atomicLoad(shouldContinue)) - { - immutable myUnitIndex = atomicOp!"+="(workUnitIndex, 1); - immutable start = workUnitSize * myUnitIndex; - if (start >= len) - { - atomicStore(shouldContinue, false); - break; - } - - immutable end = min(len, start + workUnitSize); - - static if (hasSlicing!R) - { - auto subrange = range[start .. end]; - foreach (i; start .. end) - { - emplaceRef(buf[i], fun(subrange.front)); - subrange.popFront(); - } - } - else - { - foreach (i; start .. end) - { - emplaceRef(buf[i], fun(range[i])); - } - } - } - } - - submitAndExecute(this, &doIt); - return buf; - } - } - - /// - template map(functions...) - { - /** - A semi-lazy parallel map that can be used for pipelining. The map - functions are evaluated for the first `bufSize` elements and stored in a - buffer and made available to `popFront`. Meanwhile, in the - background a second buffer of the same size is filled. When the first - buffer is exhausted, it is swapped with the second buffer and filled while - the values from what was originally the second buffer are read. This - implementation allows for elements to be written to the buffer without - the need for atomic operations or synchronization for each write, and - enables the mapping function to be evaluated efficiently in parallel. - - `map` has more overhead than the simpler procedure used by `amap` - but avoids the need to keep all results in memory simultaneously and works - with non-random access ranges. - - Params: - - source = The $(REF_ALTTEXT input range, isInputRange, std,range,primitives) - to be mapped. If `source` is not random - access it will be lazily buffered to an array of size `bufSize` before - the map function is evaluated. (For an exception to this rule, see Notes.) - - bufSize = The size of the buffer to store the evaluated elements. - - workUnitSize = The number of elements to evaluate in a single - `Task`. Must be less than or equal to `bufSize`, and - should be a fraction of `bufSize` such that all worker threads can be - used. If the default of size_t.max is used, workUnitSize will be set to - the pool-wide default. - - Returns: An input range representing the results of the map. This range - has a length iff `source` has a length. - - Notes: - - If a range returned by `map` or `asyncBuf` is used as an input to - `map`, then as an optimization the copying from the output buffer - of the first range to the input buffer of the second range is elided, even - though the ranges returned by `map` and `asyncBuf` are non-random - access ranges. This means that the `bufSize` parameter passed to the - current call to `map` will be ignored and the size of the buffer - will be the buffer size of `source`. - - Example: - --- - // Pipeline reading a file, converting each line - // to a number, taking the logarithms of the numbers, - // and performing the additions necessary to find - // the sum of the logarithms. - - auto lineRange = File("numberList.txt").byLine(); - auto dupedLines = std.algorithm.map!"a.idup"(lineRange); - auto nums = taskPool.map!(to!double)(dupedLines); - auto logs = taskPool.map!log10(nums); - - double sum = 0; - foreach (elem; logs) - { - sum += elem; - } - --- - - $(B Exception Handling): - - Any exceptions thrown while iterating over `source` - or computing the map function are re-thrown on a call to `popFront` or, - if thrown during construction, are simply allowed to propagate to the - caller. In the case of exceptions thrown while computing the map function, - the exceptions are chained as in `TaskPool.amap`. - */ - auto - map(S)(S source, size_t bufSize = 100, size_t workUnitSize = size_t.max) - if (isInputRange!S) - { - import std.exception : enforce; - - enforce(workUnitSize == size_t.max || workUnitSize <= bufSize, - "Work unit size must be smaller than buffer size."); - alias fun = adjoin!(staticMap!(unaryFun, functions)); - - static final class Map - { - // This is a class because the task needs to be located on the - // heap and in the non-random access case source needs to be on - // the heap, too. - - private: - enum bufferTrick = is(typeof(source.buf1)) && - is(typeof(source.bufPos)) && - is(typeof(source.doBufSwap())); - - alias E = MapType!(S, functions); - E[] buf1, buf2; - S source; - TaskPool pool; - Task!(run, E[] delegate(E[]), E[]) nextBufTask; - size_t workUnitSize; - size_t bufPos; - bool lastTaskWaited; - - static if (isRandomAccessRange!S) - { - alias FromType = S; - - void popSource() - { - import std.algorithm.comparison : min; - - static if (__traits(compiles, source[0 .. source.length])) - { - source = source[min(buf1.length, source.length)..source.length]; - } - else static if (__traits(compiles, source[0..$])) - { - source = source[min(buf1.length, source.length)..$]; - } - else - { - static assert(0, "S must have slicing for Map." - ~ " " ~ S.stringof ~ " doesn't."); - } - } - } - else static if (bufferTrick) - { - // Make sure we don't have the buffer recycling overload of - // asyncBuf. - static if ( - is(typeof(source.source)) && - isRoundRobin!(typeof(source.source)) - ) - { - static assert(0, "Cannot execute a parallel map on " ~ - "the buffer recycling overload of asyncBuf." - ); - } - - alias FromType = typeof(source.buf1); - FromType from; - - // Just swap our input buffer with source's output buffer. - // No need to copy element by element. - FromType dumpToFrom() - { - import std.algorithm.mutation : swap; - - assert(source.buf1.length <= from.length); - from.length = source.buf1.length; - swap(source.buf1, from); - - // Just in case this source has been popped before - // being sent to map: - from = from[source.bufPos..$]; - - static if (is(typeof(source._length))) - { - source._length -= (from.length - source.bufPos); - } - - source.doBufSwap(); - - return from; - } - } - else - { - alias FromType = ElementType!S[]; - - // The temporary array that data is copied to before being - // mapped. - FromType from; - - FromType dumpToFrom() - { - assert(from !is null); - - size_t i; - for (; !source.empty && i < from.length; source.popFront()) - { - from[i++] = source.front; - } - - from = from[0 .. i]; - return from; - } - } - - static if (hasLength!S) - { - size_t _length; - - public @property size_t length() const @safe pure nothrow - { - return _length; - } - } - - this(S source, size_t bufSize, size_t workUnitSize, TaskPool pool) - { - static if (bufferTrick) - { - bufSize = source.buf1.length; - } - - buf1.length = bufSize; - buf2.length = bufSize; - - static if (!isRandomAccessRange!S) - { - from.length = bufSize; - } - - this.workUnitSize = (workUnitSize == size_t.max) ? - pool.defaultWorkUnitSize(bufSize) : workUnitSize; - this.source = source; - this.pool = pool; - - static if (hasLength!S) - { - _length = source.length; - } - - buf1 = fillBuf(buf1); - submitBuf2(); - } - - // The from parameter is a dummy and ignored in the random access - // case. - E[] fillBuf(E[] buf) - { - import std.algorithm.comparison : min; - - static if (isRandomAccessRange!S) - { - import std.range : take; - auto toMap = take(source, buf.length); - scope(success) popSource(); - } - else - { - auto toMap = dumpToFrom(); - } - - buf = buf[0 .. min(buf.length, toMap.length)]; - - // Handle as a special case: - if (pool.size == 0) - { - size_t index = 0; - foreach (elem; toMap) - { - buf[index++] = fun(elem); - } - return buf; - } - - pool.amap!functions(toMap, workUnitSize, buf); - - return buf; - } - - void submitBuf2() - in - { - assert(nextBufTask.prev is null); - assert(nextBufTask.next is null); - } - do - { - // Hack to reuse the task object. - - nextBufTask = typeof(nextBufTask).init; - nextBufTask._args[0] = &fillBuf; - nextBufTask._args[1] = buf2; - pool.put(nextBufTask); - } - - void doBufSwap() - { - if (lastTaskWaited) - { - // Then the source is empty. Signal it here. - buf1 = null; - buf2 = null; - - static if (!isRandomAccessRange!S) - { - from = null; - } - - return; - } - - buf2 = buf1; - buf1 = nextBufTask.yieldForce; - bufPos = 0; - - if (source.empty) - { - lastTaskWaited = true; - } - else - { - submitBuf2(); - } - } - - public: - @property auto front() - { - return buf1[bufPos]; - } - - void popFront() - { - static if (hasLength!S) - { - _length--; - } - - bufPos++; - if (bufPos >= buf1.length) - { - doBufSwap(); - } - } - - static if (isInfinite!S) - { - enum bool empty = false; - } - else - { - - bool empty() const @property - { - // popFront() sets this when source is empty - return buf1.length == 0; - } - } - } - return new Map(source, bufSize, workUnitSize, this); - } - } - - /** - Given a `source` range that is expensive to iterate over, returns an - $(REF_ALTTEXT input range, isInputRange, std,range,primitives) that - asynchronously buffers the contents of `source` into a buffer of `bufSize` elements in a worker thread, - while making previously buffered elements from a second buffer, also of size - `bufSize`, available via the range interface of the returned - object. The returned range has a length iff `hasLength!S`. - `asyncBuf` is useful, for example, when performing expensive operations - on the elements of ranges that represent data on a disk or network. - - Example: - --- - import std.conv, std.stdio; - - void main() - { - // Fetch lines of a file in a background thread - // while processing previously fetched lines, - // dealing with byLine's buffer recycling by - // eagerly duplicating every line. - auto lines = File("foo.txt").byLine(); - auto duped = std.algorithm.map!"a.idup"(lines); - - // Fetch more lines in the background while we - // process the lines already read into memory - // into a matrix of doubles. - double[][] matrix; - auto asyncReader = taskPool.asyncBuf(duped); - - foreach (line; asyncReader) - { - auto ls = line.split("\t"); - matrix ~= to!(double[])(ls); - } - } - --- - - $(B Exception Handling): - - Any exceptions thrown while iterating over `source` are re-thrown on a - call to `popFront` or, if thrown during construction, simply - allowed to propagate to the caller. - */ - auto asyncBuf(S)(S source, size_t bufSize = 100) if (isInputRange!S) - { - static final class AsyncBuf - { - // This is a class because the task and source both need to be on - // the heap. - - // The element type of S. - alias E = ElementType!S; // Needs to be here b/c of forward ref bugs. - - private: - E[] buf1, buf2; - S source; - TaskPool pool; - Task!(run, E[] delegate(E[]), E[]) nextBufTask; - size_t bufPos; - bool lastTaskWaited; - - static if (hasLength!S) - { - size_t _length; - - // Available if hasLength!S. - public @property size_t length() const @safe pure nothrow - { - return _length; - } - } - - this(S source, size_t bufSize, TaskPool pool) - { - buf1.length = bufSize; - buf2.length = bufSize; - - this.source = source; - this.pool = pool; - - static if (hasLength!S) - { - _length = source.length; - } - - buf1 = fillBuf(buf1); - submitBuf2(); - } - - E[] fillBuf(E[] buf) - { - assert(buf !is null); - - size_t i; - for (; !source.empty && i < buf.length; source.popFront()) - { - buf[i++] = source.front; - } - - buf = buf[0 .. i]; - return buf; - } - - void submitBuf2() - in - { - assert(nextBufTask.prev is null); - assert(nextBufTask.next is null); - } - do - { - // Hack to reuse the task object. - - nextBufTask = typeof(nextBufTask).init; - nextBufTask._args[0] = &fillBuf; - nextBufTask._args[1] = buf2; - pool.put(nextBufTask); - } - - void doBufSwap() - { - if (lastTaskWaited) - { - // Then source is empty. Signal it here. - buf1 = null; - buf2 = null; - return; - } - - buf2 = buf1; - buf1 = nextBufTask.yieldForce; - bufPos = 0; - - if (source.empty) - { - lastTaskWaited = true; - } - else - { - submitBuf2(); - } - } - - public: - E front() @property - { - return buf1[bufPos]; - } - - void popFront() - { - static if (hasLength!S) - { - _length--; - } - - bufPos++; - if (bufPos >= buf1.length) - { - doBufSwap(); - } - } - - static if (isInfinite!S) - { - enum bool empty = false; - } - - else - { - /// - bool empty() @property - { - // popFront() sets this when source is empty: - return buf1.length == 0; - } - } - } - return new AsyncBuf(source, bufSize, this); - } - - /** - Given a callable object `next` that writes to a user-provided buffer and - a second callable object `empty` that determines whether more data is - available to write via `next`, returns an input range that - asynchronously calls `next` with a set of size `nBuffers` of buffers - and makes the results available in the order they were obtained via the - input range interface of the returned object. Similarly to the - input range overload of `asyncBuf`, the first half of the buffers - are made available via the range interface while the second half are - filled and vice-versa. - - Params: - - next = A callable object that takes a single argument that must be an array - with mutable elements. When called, `next` writes data to - the array provided by the caller. - - empty = A callable object that takes no arguments and returns a type - implicitly convertible to `bool`. This is used to signify - that no more data is available to be obtained by calling `next`. - - initialBufSize = The initial size of each buffer. If `next` takes its - array by reference, it may resize the buffers. - - nBuffers = The number of buffers to cycle through when calling `next`. - - Example: - --- - // Fetch lines of a file in a background - // thread while processing previously fetched - // lines, without duplicating any lines. - auto file = File("foo.txt"); - - void next(ref char[] buf) - { - file.readln(buf); - } - - // Fetch more lines in the background while we - // process the lines already read into memory - // into a matrix of doubles. - double[][] matrix; - auto asyncReader = taskPool.asyncBuf(&next, &file.eof); - - foreach (line; asyncReader) - { - auto ls = line.split("\t"); - matrix ~= to!(double[])(ls); - } - --- - - $(B Exception Handling): - - Any exceptions thrown while iterating over `range` are re-thrown on a - call to `popFront`. - - Warning: - - Using the range returned by this function in a parallel foreach loop - will not work because buffers may be overwritten while the task that - processes them is in queue. This is checked for at compile time - and will result in a static assertion failure. - */ - auto asyncBuf(C1, C2)(C1 next, C2 empty, size_t initialBufSize = 0, size_t nBuffers = 100) - if (is(typeof(C2.init()) : bool) && - Parameters!C1.length == 1 && - Parameters!C2.length == 0 && - isArray!(Parameters!C1[0]) - ) { - auto roundRobin = RoundRobinBuffer!(C1, C2)(next, empty, initialBufSize, nBuffers); - return asyncBuf(roundRobin, nBuffers / 2); - } - - /// - template reduce(functions...) - { - /** - Parallel reduce on a random access range. Except as otherwise noted, - usage is similar to $(REF _reduce, std,algorithm,iteration). There is - also $(LREF fold) which does the same thing with a different parameter - order. - - This function works by splitting the range to be reduced into work - units, which are slices to be reduced in parallel. Once the results - from all work units are computed, a final serial reduction is performed - on these results to compute the final answer. Therefore, care must be - taken to choose the seed value appropriately. - - Because the reduction is being performed in parallel, `functions` - must be associative. For notational simplicity, let # be an - infix operator representing `functions`. Then, (a # b) # c must equal - a # (b # c). Floating point addition is not associative - even though addition in exact arithmetic is. Summing floating - point numbers using this function may give different results than summing - serially. However, for many practical purposes floating point addition - can be treated as associative. - - Note that, since `functions` are assumed to be associative, - additional optimizations are made to the serial portion of the reduction - algorithm. These take advantage of the instruction level parallelism of - modern CPUs, in addition to the thread-level parallelism that the rest - of this module exploits. This can lead to better than linear speedups - relative to $(REF _reduce, std,algorithm,iteration), especially for - fine-grained benchmarks like dot products. - - An explicit seed may be provided as the first argument. If - provided, it is used as the seed for all work units and for the final - reduction of results from all work units. Therefore, if it is not the - identity value for the operation being performed, results may differ - from those generated by $(REF _reduce, std,algorithm,iteration) or - depending on how many work units are used. The next argument must be - the range to be reduced. - --- - // Find the sum of squares of a range in parallel, using - // an explicit seed. - // - // Timings on an Athlon 64 X2 dual core machine: - // - // Parallel reduce: 72 milliseconds - // Using std.algorithm.reduce instead: 181 milliseconds - auto nums = iota(10_000_000.0f); - auto sumSquares = taskPool.reduce!"a + b"( - 0.0, std.algorithm.map!"a * a"(nums) - ); - --- - - If no explicit seed is provided, the first element of each work unit - is used as a seed. For the final reduction, the result from the first - work unit is used as the seed. - --- - // Find the sum of a range in parallel, using the first - // element of each work unit as the seed. - auto sum = taskPool.reduce!"a + b"(nums); - --- - - An explicit work unit size may be specified as the last argument. - Specifying too small a work unit size will effectively serialize the - reduction, as the final reduction of the result of each work unit will - dominate computation time. If `TaskPool.size` for this instance - is zero, this parameter is ignored and one work unit is used. - --- - // Use a work unit size of 100. - auto sum2 = taskPool.reduce!"a + b"(nums, 100); - - // Work unit size of 100 and explicit seed. - auto sum3 = taskPool.reduce!"a + b"(0.0, nums, 100); - --- - - Parallel reduce supports multiple functions, like - `std.algorithm.reduce`. - --- - // Find both the min and max of nums. - auto minMax = taskPool.reduce!(min, max)(nums); - assert(minMax[0] == reduce!min(nums)); - assert(minMax[1] == reduce!max(nums)); - --- - - $(B Exception Handling): - - After this function is finished executing, any exceptions thrown - are chained together via `Throwable.next` and rethrown. The chaining - order is non-deterministic. - - See_Also: - - $(LREF fold) is functionally equivalent to $(LREF _reduce) except the - range parameter comes first and there is no need to use - $(REF_ALTTEXT `tuple`,tuple,std,typecons) for multiple seeds. - */ - auto reduce(Args...)(Args args) - { - import core.exception : OutOfMemoryError; - import core.internal.lifetime : emplaceRef; - import std.exception : enforce; - - alias fun = reduceAdjoin!functions; - alias finishFun = reduceFinish!functions; - - static if (isIntegral!(Args[$ - 1])) - { - size_t workUnitSize = cast(size_t) args[$ - 1]; - alias args2 = args[0..$ - 1]; - alias Args2 = Args[0..$ - 1]; - } - else - { - alias args2 = args; - alias Args2 = Args; - } - - auto makeStartValue(Type)(Type e) - { - static if (functions.length == 1) - { - return e; - } - else - { - typeof(adjoin!(staticMap!(binaryFun, functions))(e, e)) seed = void; - foreach (i, T; seed.Types) - { - emplaceRef(seed.expand[i], e); - } - - return seed; - } - } - - static if (args2.length == 2) - { - static assert(isInputRange!(Args2[1])); - alias range = args2[1]; - alias seed = args2[0]; - enum explicitSeed = true; - - static if (!is(typeof(workUnitSize))) - { - size_t workUnitSize = defaultWorkUnitSize(range.length); - } - } - else - { - static assert(args2.length == 1); - alias range = args2[0]; - - static if (!is(typeof(workUnitSize))) - { - size_t workUnitSize = defaultWorkUnitSize(range.length); - } - - enforce(!range.empty, - "Cannot reduce an empty range with first element as start value."); - - auto seed = makeStartValue(range.front); - enum explicitSeed = false; - range.popFront(); - } - - alias E = typeof(seed); - alias R = typeof(range); - - E reduceOnRange(R range, size_t lowerBound, size_t upperBound) - { - // This is for exploiting instruction level parallelism by - // using multiple accumulator variables within each thread, - // since we're assuming functions are associative anyhow. - - // This is so that loops can be unrolled automatically. - enum ilpTuple = AliasSeq!(0, 1, 2, 3, 4, 5); - enum nILP = ilpTuple.length; - immutable subSize = (upperBound - lowerBound) / nILP; - - if (subSize <= 1) - { - // Handle as a special case. - static if (explicitSeed) - { - E result = seed; - } - else - { - E result = makeStartValue(range[lowerBound]); - lowerBound++; - } - - foreach (i; lowerBound .. upperBound) - { - result = fun(result, range[i]); - } - - return result; - } - - assert(subSize > 1); - E[nILP] results; - size_t[nILP] offsets; - - foreach (i; ilpTuple) - { - offsets[i] = lowerBound + subSize * i; - - static if (explicitSeed) - { - results[i] = seed; - } - else - { - results[i] = makeStartValue(range[offsets[i]]); - offsets[i]++; - } - } - - immutable nLoop = subSize - (!explicitSeed); - foreach (i; 0 .. nLoop) - { - foreach (j; ilpTuple) - { - results[j] = fun(results[j], range[offsets[j]]); - offsets[j]++; - } - } - - // Finish the remainder. - foreach (i; nILP * subSize + lowerBound .. upperBound) - { - results[$ - 1] = fun(results[$ - 1], range[i]); - } - - foreach (i; ilpTuple[1..$]) - { - results[0] = finishFun(results[0], results[i]); - } - - return results[0]; - } - - immutable len = range.length; - if (len == 0) - { - return seed; - } - - if (this.size == 0) - { - return finishFun(seed, reduceOnRange(range, 0, len)); - } - - // Unlike the rest of the functions here, I can't use the Task object - // recycling trick here because this has to work on non-commutative - // operations. After all the tasks are done executing, fun() has to - // be applied on the results of these to get a final result, but - // it can't be evaluated out of order. - - if (workUnitSize > len) - { - workUnitSize = len; - } - - immutable size_t nWorkUnits = (len / workUnitSize) + ((len % workUnitSize == 0) ? 0 : 1); - assert(nWorkUnits * workUnitSize >= len); - - alias RTask = Task!(run, typeof(&reduceOnRange), R, size_t, size_t); - RTask[] tasks; - - // Can't use alloca() due to https://issues.dlang.org/show_bug.cgi?id=3753 - // Use a fixed buffer backed by malloc(). - enum maxStack = 2_048; - byte[maxStack] buf = void; - immutable size_t nBytesNeeded = nWorkUnits * RTask.sizeof; - - import core.stdc.stdlib : malloc, free; - if (nBytesNeeded <= maxStack) - { - tasks = (cast(RTask*) buf.ptr)[0 .. nWorkUnits]; - } - else - { - auto ptr = cast(RTask*) malloc(nBytesNeeded); - if (!ptr) - { - throw new OutOfMemoryError( - "Out of memory in std.parallelism." - ); - } - - tasks = ptr[0 .. nWorkUnits]; - } - - scope(exit) - { - if (nBytesNeeded > maxStack) - { - free(tasks.ptr); - } - } - - // Hack to take the address of a nested function w/o - // making a closure. - static auto scopedAddress(D)(scope D del) @system - { - auto tmp = del; - return tmp; - } - - size_t curPos = 0; - void useTask(ref RTask task) - { - import std.algorithm.comparison : min; - import core.lifetime : emplace; - - // Private constructor, so can't feed it's arguments directly - // to emplace - emplace(&task, RTask - ( - scopedAddress(&reduceOnRange), - range, - curPos, // lower bound. - cast() min(len, curPos + workUnitSize) // upper bound. - )); - - task.pool = this; - - curPos += workUnitSize; - } - - foreach (ref task; tasks) - { - useTask(task); - } - - foreach (i; 1 .. tasks.length - 1) - { - tasks[i].next = tasks[i + 1].basePtr; - tasks[i + 1].prev = tasks[i].basePtr; - } - - if (tasks.length > 1) - { - queueLock(); - scope(exit) queueUnlock(); - - abstractPutGroupNoSync( - tasks[1].basePtr, - tasks[$ - 1].basePtr - ); - } - - if (tasks.length > 0) - { - try - { - tasks[0].job(); - } - catch (Throwable e) - { - tasks[0].exception = e; - } - tasks[0].taskStatus = TaskStatus.done; - - // Try to execute each of these in the current thread - foreach (ref task; tasks[1..$]) - { - tryDeleteExecute(task.basePtr); - } - } - - // Now that we've tried to execute every task, they're all either - // done or in progress. Force all of them. - E result = seed; - - Throwable firstException; - - foreach (ref task; tasks) - { - try - { - task.yieldForce; - } - catch (Throwable e) - { - /* Chain e to front because order doesn't matter and because - * e is not likely to be a chain itself (so fewer traversals) - */ - firstException = Throwable.chainTogether(e, firstException); - continue; - } - - if (!firstException) result = finishFun(result, task.returnVal); - } - - if (firstException) throw firstException; - - return result; - } - } - - /// - template fold(functions...) - { - /** Implements the homonym function (also known as `accumulate`, `compress`, - `inject`, or `foldl`) present in various programming languages of - functional flavor. - - `fold` is functionally equivalent to $(LREF reduce) except the range - parameter comes first and there is no need to use $(REF_ALTTEXT - `tuple`,tuple,std,typecons) for multiple seeds. - - There may be one or more callable entities (`functions` argument) to - apply. - - Params: - args = Just the range to _fold over; or the range and one seed - per function; or the range, one seed per function, and - the work unit size - - Returns: - The accumulated result as a single value for single function and - as a tuple of values for multiple functions - - See_Also: - Similar to $(REF _fold, std,algorithm,iteration), `fold` is a wrapper around $(LREF reduce). - - Example: - --- - static int adder(int a, int b) - { - return a + b; - } - static int multiplier(int a, int b) - { - return a * b; - } - - // Just the range - auto x = taskPool.fold!adder([1, 2, 3, 4]); - assert(x == 10); - - // The range and the seeds (0 and 1 below; also note multiple - // functions in this example) - auto y = taskPool.fold!(adder, multiplier)([1, 2, 3, 4], 0, 1); - assert(y[0] == 10); - assert(y[1] == 24); - - // The range, the seed (0), and the work unit size (20) - auto z = taskPool.fold!adder([1, 2, 3, 4], 0, 20); - assert(z == 10); - --- - */ - auto fold(Args...)(Args args) - { - static assert(isInputRange!(Args[0]), "First argument must be an InputRange"); - - alias range = args[0]; - - static if (Args.length == 1) - { - // Just the range - return reduce!functions(range); - } - else static if (Args.length == 1 + functions.length || - Args.length == 1 + functions.length + 1) - { - static if (functions.length == 1) - { - alias seeds = args[1]; - } - else - { - auto seeds() - { - import std.typecons : tuple; - return tuple(args[1 .. functions.length+1]); - } - } - - static if (Args.length == 1 + functions.length) - { - // The range and the seeds - return reduce!functions(seeds, range); - } - else static if (Args.length == 1 + functions.length + 1) - { - // The range, the seeds, and the work unit size - static assert(isIntegral!(Args[$-1]), "Work unit size must be an integral type"); - return reduce!functions(seeds, range, args[$-1]); - } - } - else - { - import std.conv : text; - static assert(0, "Invalid number of arguments (" ~ Args.length.text ~ "): Should be an input range, " - ~ functions.length.text ~ " optional seed(s), and an optional work unit size."); - } - } - } - - // This test is not included in the documentation because even though these - // examples are for the inner fold() template, with their current location, - // they would appear under the outer one. (We can't move this inside the - // outer fold() template because then dmd runs out of memory possibly due to - // recursive template instantiation, which is surprisingly not caught.) - @system unittest - { - // Just the range - auto x = taskPool.fold!"a + b"([1, 2, 3, 4]); - assert(x == 10); - - // The range and the seeds (0 and 1 below; also note multiple - // functions in this example) - auto y = taskPool.fold!("a + b", "a * b")([1, 2, 3, 4], 0, 1); - assert(y[0] == 10); - assert(y[1] == 24); - - // The range, the seed (0), and the work unit size (20) - auto z = taskPool.fold!"a + b"([1, 2, 3, 4], 0, 20); - assert(z == 10); - } - - /** - Gets the index of the current thread relative to this `TaskPool`. Any - thread not in this pool will receive an index of 0. The worker threads in - this pool receive unique indices of 1 through `this.size`. - - This function is useful for maintaining worker-local resources. - - Example: - --- - // Execute a loop that computes the greatest common - // divisor of every number from 0 through 999 with - // 42 in parallel. Write the results out to - // a set of files, one for each thread. This allows - // results to be written out without any synchronization. - - import std.conv, std.range, std.numeric, std.stdio; - - void main() - { - auto filesHandles = new File[taskPool.size + 1]; - scope(exit) { - foreach (ref handle; fileHandles) - { - handle.close(); - } - } - - foreach (i, ref handle; fileHandles) - { - handle = File("workerResults" ~ to!string(i) ~ ".txt"); - } - - foreach (num; parallel(iota(1_000))) - { - auto outHandle = fileHandles[taskPool.workerIndex]; - outHandle.writeln(num, '\t', gcd(num, 42)); - } - } - --- - */ - size_t workerIndex() @property @safe const nothrow - { - immutable rawInd = threadIndex; - return (rawInd >= instanceStartIndex && rawInd < instanceStartIndex + size) ? - (rawInd - instanceStartIndex + 1) : 0; - } - - /** - Struct for creating worker-local storage. Worker-local storage is - thread-local storage that exists only for worker threads in a given - `TaskPool` plus a single thread outside the pool. It is allocated on the - garbage collected heap in a way that avoids _false sharing, and doesn't - necessarily have global scope within any thread. It can be accessed from - any worker thread in the `TaskPool` that created it, and one thread - outside this `TaskPool`. All threads outside the pool that created a - given instance of worker-local storage share a single slot. - - Since the underlying data for this struct is heap-allocated, this struct - has reference semantics when passed between functions. - - The main uses cases for `WorkerLocalStorage` are: - - 1. Performing parallel reductions with an imperative, as opposed to - functional, programming style. In this case, it's useful to treat - `WorkerLocalStorage` as local to each thread for only the parallel - portion of an algorithm. - - 2. Recycling temporary buffers across iterations of a parallel foreach loop. - - Example: - --- - // Calculate pi as in our synopsis example, but - // use an imperative instead of a functional style. - immutable n = 1_000_000_000; - immutable delta = 1.0L / n; - - auto sums = taskPool.workerLocalStorage(0.0L); - foreach (i; parallel(iota(n))) - { - immutable x = ( i - 0.5L ) * delta; - immutable toAdd = delta / ( 1.0 + x * x ); - sums.get += toAdd; - } - - // Add up the results from each worker thread. - real pi = 0; - foreach (threadResult; sums.toRange) - { - pi += 4.0L * threadResult; - } - --- - */ - static struct WorkerLocalStorage(T) - { - private: - TaskPool pool; - size_t size; - - size_t elemSize; - bool* stillThreadLocal; - - static size_t roundToLine(size_t num) pure nothrow - { - if (num % cacheLineSize == 0) - { - return num; - } - else - { - return ((num / cacheLineSize) + 1) * cacheLineSize; - } - } - - void* data; - - void initialize(TaskPool pool) - { - this.pool = pool; - size = pool.size + 1; - stillThreadLocal = new bool; - *stillThreadLocal = true; - - // Determines whether the GC should scan the array. - auto blkInfo = (typeid(T).flags & 1) ? - cast(GC.BlkAttr) 0 : - GC.BlkAttr.NO_SCAN; - - immutable nElem = pool.size + 1; - elemSize = roundToLine(T.sizeof); - - // The + 3 is to pad one full cache line worth of space on either side - // of the data structure to make sure false sharing with completely - // unrelated heap data is prevented, and to provide enough padding to - // make sure that data is cache line-aligned. - data = GC.malloc(elemSize * (nElem + 3), blkInfo) + elemSize; - - // Cache line align data ptr. - data = cast(void*) roundToLine(cast(size_t) data); - - foreach (i; 0 .. nElem) - { - this.opIndex(i) = T.init; - } - } - - ref opIndex(this Qualified)(size_t index) - { - import std.conv : text; - assert(index < size, text(index, '\t', uint.max)); - return *(cast(CopyTypeQualifiers!(Qualified, T)*) (data + elemSize * index)); - } - - void opIndexAssign(T val, size_t index) - { - assert(index < size); - *(cast(T*) (data + elemSize * index)) = val; - } - - public: - /** - Get the current thread's instance. Returns by ref. - Note that calling `get` from any thread - outside the `TaskPool` that created this instance will return the - same reference, so an instance of worker-local storage should only be - accessed from one thread outside the pool that created it. If this - rule is violated, undefined behavior will result. - - If assertions are enabled and `toRange` has been called, then this - WorkerLocalStorage instance is no longer worker-local and an assertion - failure will result when calling this method. This is not checked - when assertions are disabled for performance reasons. - */ - ref get(this Qualified)() @property - { - assert(*stillThreadLocal, - "Cannot call get() on this instance of WorkerLocalStorage " ~ - "because it is no longer worker-local." - ); - return opIndex(pool.workerIndex); - } - - /** - Assign a value to the current thread's instance. This function has - the same caveats as its overload. - */ - void get(T val) @property - { - assert(*stillThreadLocal, - "Cannot call get() on this instance of WorkerLocalStorage " ~ - "because it is no longer worker-local." - ); - - opIndexAssign(val, pool.workerIndex); - } - - /** - Returns a range view of the values for all threads, which can be used - to further process the results of each thread after running the parallel - part of your algorithm. Do not use this method in the parallel portion - of your algorithm. - - Calling this function sets a flag indicating that this struct is no - longer worker-local, and attempting to use the `get` method again - will result in an assertion failure if assertions are enabled. - */ - WorkerLocalStorageRange!T toRange() @property - { - if (*stillThreadLocal) - { - *stillThreadLocal = false; - - // Make absolutely sure results are visible to all threads. - // This is probably not necessary since some other - // synchronization primitive will be used to signal that the - // parallel part of the algorithm is done, but the - // performance impact should be negligible, so it's better - // to be safe. - ubyte barrierDummy; - atomicSetUbyte(barrierDummy, 1); - } - - return WorkerLocalStorageRange!T(this); - } - } - - /** - Range primitives for worker-local storage. The purpose of this is to - access results produced by each worker thread from a single thread once you - are no longer using the worker-local storage from multiple threads. - Do not use this struct in the parallel portion of your algorithm. - - The proper way to instantiate this object is to call - `WorkerLocalStorage.toRange`. Once instantiated, this object behaves - as a finite random-access range with assignable, lvalue elements and - a length equal to the number of worker threads in the `TaskPool` that - created it plus 1. - */ - static struct WorkerLocalStorageRange(T) - { - private: - WorkerLocalStorage!T workerLocalStorage; - - size_t _length; - size_t beginOffset; - - this(WorkerLocalStorage!T wl) - { - this.workerLocalStorage = wl; - _length = wl.size; - } - - public: - ref front(this Qualified)() @property - { - return this[0]; - } - - ref back(this Qualified)() @property - { - return this[_length - 1]; - } - - void popFront() - { - if (_length > 0) - { - beginOffset++; - _length--; - } - } - - void popBack() - { - if (_length > 0) - { - _length--; - } - } - - typeof(this) save() @property - { - return this; - } - - ref opIndex(this Qualified)(size_t index) - { - assert(index < _length); - return workerLocalStorage[index + beginOffset]; - } - - void opIndexAssign(T val, size_t index) - { - assert(index < _length); - workerLocalStorage[index] = val; - } - - typeof(this) opSlice(size_t lower, size_t upper) - { - assert(upper <= _length); - auto newWl = this.workerLocalStorage; - newWl.data += lower * newWl.elemSize; - newWl.size = upper - lower; - return typeof(this)(newWl); - } - - bool empty() const @property - { - return length == 0; - } - - size_t length() const @property - { - return _length; - } - } - - /** - Creates an instance of worker-local storage, initialized with a given - value. The value is `lazy` so that you can, for example, easily - create one instance of a class for each worker. For usage example, - see the `WorkerLocalStorage` struct. - */ - WorkerLocalStorage!T workerLocalStorage(T)(lazy T initialVal = T.init) - { - WorkerLocalStorage!T ret; - ret.initialize(this); - foreach (i; 0 .. size + 1) - { - ret[i] = initialVal; - } - - // Memory barrier to make absolutely sure that what we wrote is - // visible to worker threads. - ubyte barrierDummy; - atomicSetUbyte(barrierDummy, 0); - - return ret; - } - - /** - Signals to all worker threads to terminate as soon as they are finished - with their current `Task`, or immediately if they are not executing a - `Task`. `Task`s that were in queue will not be executed unless - a call to `Task.workForce`, `Task.yieldForce` or `Task.spinForce` - causes them to be executed. - - Use only if you have waited on every `Task` and therefore know the - queue is empty, or if you speculatively executed some tasks and no longer - need the results. - */ - void stop() @trusted - { - queueLock(); - scope(exit) queueUnlock(); - atomicSetUbyte(status, PoolState.stopNow); - notifyAll(); - } - - /** - Signals worker threads to terminate when the queue becomes empty. - - If blocking argument is true, wait for all worker threads to terminate - before returning. This option might be used in applications where - task results are never consumed-- e.g. when `TaskPool` is employed as a - rudimentary scheduler for tasks which communicate by means other than - return values. - - Warning: Calling this function with $(D blocking = true) from a worker - thread that is a member of the same `TaskPool` that - `finish` is being called on will result in a deadlock. - */ - void finish(bool blocking = false) @trusted - { - { - queueLock(); - scope(exit) queueUnlock(); - atomicCasUbyte(status, PoolState.running, PoolState.finishing); - notifyAll(); - } - if (blocking) - { - // Use this thread as a worker until everything is finished. - executeWorkLoop(); - - foreach (t; pool) - { - // Maybe there should be something here to prevent a thread - // from calling join() on itself if this function is called - // from a worker thread in the same pool, but: - // - // 1. Using an if statement to skip join() would result in - // finish() returning without all tasks being finished. - // - // 2. If an exception were thrown, it would bubble up to the - // Task from which finish() was called and likely be - // swallowed. - t.join(); - } - } - } - - /// Returns the number of worker threads in the pool. - @property size_t size() @safe const pure nothrow - { - return pool.length; - } - - /** - Put a `Task` object on the back of the task queue. The `Task` - object may be passed by pointer or reference. - - Example: - --- - import std.file; - - // Create a task. - auto t = task!read("foo.txt"); - - // Add it to the queue to be executed. - taskPool.put(t); - --- - - Notes: - - @trusted overloads of this function are called for `Task`s if - $(REF hasUnsharedAliasing, std,traits) is false for the `Task`'s - return type or the function the `Task` executes is `pure`. - `Task` objects that meet all other requirements specified in the - `@trusted` overloads of `task` and `scopedTask` may be created - and executed from `@safe` code via `Task.executeInNewThread` but - not via `TaskPool`. - - While this function takes the address of variables that may - be on the stack, some overloads are marked as @trusted. - `Task` includes a destructor that waits for the task to complete - before destroying the stack frame it is allocated on. Therefore, - it is impossible for the stack frame to be destroyed before the task is - complete and no longer referenced by a `TaskPool`. - */ - void put(alias fun, Args...)(ref Task!(fun, Args) task) - if (!isSafeReturn!(typeof(task))) - { - task.pool = this; - abstractPut(task.basePtr); - } - - /// Ditto - void put(alias fun, Args...)(Task!(fun, Args)* task) - if (!isSafeReturn!(typeof(*task))) - { - import std.exception : enforce; - enforce(task !is null, "Cannot put a null Task on a TaskPool queue."); - put(*task); - } - - @trusted void put(alias fun, Args...)(ref Task!(fun, Args) task) - if (isSafeReturn!(typeof(task))) - { - task.pool = this; - abstractPut(task.basePtr); - } - - @trusted void put(alias fun, Args...)(Task!(fun, Args)* task) - if (isSafeReturn!(typeof(*task))) - { - import std.exception : enforce; - enforce(task !is null, "Cannot put a null Task on a TaskPool queue."); - put(*task); - } - - /** - These properties control whether the worker threads are daemon threads. - A daemon thread is automatically terminated when all non-daemon threads - have terminated. A non-daemon thread will prevent a program from - terminating as long as it has not terminated. - - If any `TaskPool` with non-daemon threads is active, either `stop` - or `finish` must be called on it before the program can terminate. - - The worker treads in the `TaskPool` instance returned by the - `taskPool` property are daemon by default. The worker threads of - manually instantiated task pools are non-daemon by default. - - Note: For a size zero pool, the getter arbitrarily returns true and the - setter has no effect. - */ - bool isDaemon() @property @trusted - { - queueLock(); - scope(exit) queueUnlock(); - return (size == 0) ? true : pool[0].isDaemon; - } - - /// Ditto - void isDaemon(bool newVal) @property @trusted - { - queueLock(); - scope(exit) queueUnlock(); - foreach (thread; pool) - { - thread.isDaemon = newVal; - } - } - - /** - These functions allow getting and setting the OS scheduling priority of - the worker threads in this `TaskPool`. They forward to - `core.thread.Thread.priority`, so a given priority value here means the - same thing as an identical priority value in `core.thread`. - - Note: For a size zero pool, the getter arbitrarily returns - `core.thread.Thread.PRIORITY_MIN` and the setter has no effect. - */ - int priority() @property @trusted - { - return (size == 0) ? core.thread.Thread.PRIORITY_MIN : - pool[0].priority; - } - - /// Ditto - void priority(int newPriority) @property @trusted - { - if (size > 0) - { - foreach (t; pool) - { - t.priority = newPriority; - } - } - } -} - -@system unittest -{ - import std.algorithm.iteration : sum; - import std.range : iota; - import std.typecons : tuple; - - enum N = 100; - auto r = iota(1, N + 1); - const expected = r.sum(); - - // Just the range - assert(taskPool.fold!"a + b"(r) == expected); - - // Range and seeds - assert(taskPool.fold!"a + b"(r, 0) == expected); - assert(taskPool.fold!("a + b", "a + b")(r, 0, 0) == tuple(expected, expected)); - - // Range, seeds, and work unit size - assert(taskPool.fold!"a + b"(r, 0, 42) == expected); - assert(taskPool.fold!("a + b", "a + b")(r, 0, 0, 42) == tuple(expected, expected)); -} - -// Issue 16705 -@system unittest -{ - struct MyIota - { - size_t front; - void popFront()(){front++;} - auto empty(){return front >= 25;} - auto opIndex(size_t i){return front+i;} - auto length(){return 25-front;} - } - - auto mySum = taskPool.reduce!"a + b"(MyIota()); -} - -/** -Returns a lazily initialized global instantiation of `TaskPool`. -This function can safely be called concurrently from multiple non-worker -threads. The worker threads in this pool are daemon threads, meaning that it -is not necessary to call `TaskPool.stop` or `TaskPool.finish` before -terminating the main thread. -*/ -@property TaskPool taskPool() @trusted -{ - import std.concurrency : initOnce; - __gshared TaskPool pool; - return initOnce!pool({ - auto p = new TaskPool(defaultPoolThreads); - p.isDaemon = true; - return p; - }()); -} - -private shared uint _defaultPoolThreads = uint.max; - -/** -These properties get and set the number of worker threads in the `TaskPool` -instance returned by `taskPool`. The default value is `totalCPUs` - 1. -Calling the setter after the first call to `taskPool` does not changes -number of worker threads in the instance returned by `taskPool`. -*/ -@property uint defaultPoolThreads() @trusted -{ - const local = atomicLoad(_defaultPoolThreads); - return local < uint.max ? local : totalCPUs - 1; -} - -/// Ditto -@property void defaultPoolThreads(uint newVal) @trusted -{ - atomicStore(_defaultPoolThreads, newVal); -} - -/** -Convenience functions that forwards to `taskPool.parallel`. The -purpose of these is to make parallel foreach less verbose and more -readable. - -Example: ---- -// Find the logarithm of every number from -// 1 to 1_000_000 in parallel, using the -// default TaskPool instance. -auto logs = new double[1_000_000]; - -foreach (i, ref elem; parallel(logs)) -{ - elem = log(i + 1.0); -} ---- - -*/ -ParallelForeach!R parallel(R)(R range) -{ - return taskPool.parallel(range); -} - -/// Ditto -ParallelForeach!R parallel(R)(R range, size_t workUnitSize) -{ - return taskPool.parallel(range, workUnitSize); -} - -// `each` should be usable with parallel -// https://issues.dlang.org/show_bug.cgi?id=17019 -@system unittest -{ - import std.algorithm.iteration : each, sum; - import std.range : iota; - - // check behavior with parallel - auto arr = new int[10]; - parallel(arr).each!((ref e) => e += 1); - assert(arr.sum == 10); - - auto arrIndex = new int[10]; - parallel(arrIndex).each!((i, ref e) => e += i); - assert(arrIndex.sum == 10.iota.sum); -} - -// https://issues.dlang.org/show_bug.cgi?id=22745 -@system unittest -{ - auto pool = new TaskPool(0); - int[] empty; - foreach (i; pool.parallel(empty)) {} - pool.finish(); -} - -// Thrown when a parallel foreach loop is broken from. -class ParallelForeachError : Error -{ - this() - { - super("Cannot break from a parallel foreach loop using break, return, " - ~ "labeled break/continue or goto statements."); - } -} - -/*------Structs that implement opApply for parallel foreach.------------------*/ -private template randLen(R) -{ - enum randLen = isRandomAccessRange!R && hasLength!R; -} - -private void submitAndExecute( - TaskPool pool, - scope void delegate() doIt -) -{ - import core.exception : OutOfMemoryError; - immutable nThreads = pool.size + 1; - - alias PTask = typeof(scopedTask(doIt)); - import core.stdc.stdlib : malloc, free; - import core.stdc.string : memcpy; - - // The logical thing to do would be to just use alloca() here, but that - // causes problems on Windows for reasons that I don't understand - // (tentatively a compiler bug) and definitely doesn't work on Posix due - // to https://issues.dlang.org/show_bug.cgi?id=3753. - // Therefore, allocate a fixed buffer and fall back to `malloc()` if - // someone's using a ridiculous amount of threads. - // Also, the using a byte array instead of a PTask array as the fixed buffer - // is to prevent d'tors from being called on uninitialized excess PTask - // instances. - enum nBuf = 64; - byte[nBuf * PTask.sizeof] buf = void; - PTask[] tasks; - if (nThreads <= nBuf) - { - tasks = (cast(PTask*) buf.ptr)[0 .. nThreads]; - } - else - { - auto ptr = cast(PTask*) malloc(nThreads * PTask.sizeof); - if (!ptr) throw new OutOfMemoryError("Out of memory in std.parallelism."); - tasks = ptr[0 .. nThreads]; - } - - scope(exit) - { - if (nThreads > nBuf) - { - free(tasks.ptr); - } - } - - foreach (ref t; tasks) - { - import core.stdc.string : memcpy; - - // This silly looking code is necessary to prevent d'tors from being - // called on uninitialized objects. - auto temp = scopedTask(doIt); - memcpy(&t, &temp, PTask.sizeof); - - // This has to be done to t after copying, not temp before copying. - // Otherwise, temp's destructor will sit here and wait for the - // task to finish. - t.pool = pool; - } - - foreach (i; 1 .. tasks.length - 1) - { - tasks[i].next = tasks[i + 1].basePtr; - tasks[i + 1].prev = tasks[i].basePtr; - } - - if (tasks.length > 1) - { - pool.queueLock(); - scope(exit) pool.queueUnlock(); - - pool.abstractPutGroupNoSync( - tasks[1].basePtr, - tasks[$ - 1].basePtr - ); - } - - if (tasks.length > 0) - { - try - { - tasks[0].job(); - } - catch (Throwable e) - { - tasks[0].exception = e; // nocoverage - } - tasks[0].taskStatus = TaskStatus.done; - - // Try to execute each of these in the current thread - foreach (ref task; tasks[1..$]) - { - pool.tryDeleteExecute(task.basePtr); - } - } - - Throwable firstException; - - foreach (i, ref task; tasks) - { - try - { - task.yieldForce; - } - catch (Throwable e) - { - /* Chain e to front because order doesn't matter and because - * e is not likely to be a chain itself (so fewer traversals) - */ - firstException = Throwable.chainTogether(e, firstException); - continue; - } - } - - if (firstException) throw firstException; -} - -void foreachErr() -{ - throw new ParallelForeachError(); -} - -int doSizeZeroCase(R, Delegate)(ref ParallelForeach!R p, Delegate dg) -{ - with(p) - { - int res = 0; - size_t index = 0; - - // The explicit ElementType!R in the foreach loops is necessary for - // correct behavior when iterating over strings. - static if (hasLvalueElements!R) - { - foreach (ref ElementType!R elem; range) - { - static if (Parameters!dg.length == 2) - { - res = dg(index, elem); - } - else - { - res = dg(elem); - } - if (res) break; - index++; - } - } - else - { - foreach (ElementType!R elem; range) - { - static if (Parameters!dg.length == 2) - { - res = dg(index, elem); - } - else - { - res = dg(elem); - } - if (res) break; - index++; - } - } - if (res) foreachErr; - return res; - } -} - -private enum string parallelApplyMixinRandomAccess = q{ - // Handle empty thread pool as special case. - if (pool.size == 0) - { - return doSizeZeroCase(this, dg); - } - - // Whether iteration is with or without an index variable. - enum withIndex = Parameters!(typeof(dg)).length == 2; - - shared size_t workUnitIndex = size_t.max; // Effectively -1: chunkIndex + 1 == 0 - immutable len = range.length; - if (!len) return 0; - - shared bool shouldContinue = true; - - void doIt() - { - import std.algorithm.comparison : min; - - scope(failure) - { - // If an exception is thrown, all threads should bail. - atomicStore(shouldContinue, false); - } - - while (atomicLoad(shouldContinue)) - { - immutable myUnitIndex = atomicOp!"+="(workUnitIndex, 1); - immutable start = workUnitSize * myUnitIndex; - if (start >= len) - { - atomicStore(shouldContinue, false); - break; - } - - immutable end = min(len, start + workUnitSize); - - foreach (i; start .. end) - { - static if (withIndex) - { - if (dg(i, range[i])) foreachErr(); - } - else - { - if (dg(range[i])) foreachErr(); - } - } - } - } - - submitAndExecute(pool, &doIt); - - return 0; -}; - -enum string parallelApplyMixinInputRange = q{ - // Handle empty thread pool as special case. - if (pool.size == 0) - { - return doSizeZeroCase(this, dg); - } - - // Whether iteration is with or without an index variable. - enum withIndex = Parameters!(typeof(dg)).length == 2; - - // This protects the range while copying it. - auto rangeMutex = new Mutex(); - - shared bool shouldContinue = true; - - // The total number of elements that have been popped off range. - // This is updated only while protected by rangeMutex; - size_t nPopped = 0; - - static if ( - is(typeof(range.buf1)) && - is(typeof(range.bufPos)) && - is(typeof(range.doBufSwap())) - ) - { - // Make sure we don't have the buffer recycling overload of - // asyncBuf. - static if ( - is(typeof(range.source)) && - isRoundRobin!(typeof(range.source)) - ) - { - static assert(0, "Cannot execute a parallel foreach loop on " ~ - "the buffer recycling overload of asyncBuf."); - } - - enum bool bufferTrick = true; - } - else - { - enum bool bufferTrick = false; - } - - void doIt() - { - scope(failure) - { - // If an exception is thrown, all threads should bail. - atomicStore(shouldContinue, false); - } - - static if (hasLvalueElements!R) - { - alias Temp = ElementType!R*[]; - Temp temp; - - // Returns: The previous value of nPopped. - size_t makeTemp() - { - import std.algorithm.internal : addressOf; - import std.array : uninitializedArray; - - if (temp is null) - { - temp = uninitializedArray!Temp(workUnitSize); - } - - rangeMutex.lock(); - scope(exit) rangeMutex.unlock(); - - size_t i = 0; - for (; i < workUnitSize && !range.empty; range.popFront(), i++) - { - temp[i] = addressOf(range.front); - } - - temp = temp[0 .. i]; - auto ret = nPopped; - nPopped += temp.length; - return ret; - } - - } - else - { - - alias Temp = ElementType!R[]; - Temp temp; - - // Returns: The previous value of nPopped. - static if (!bufferTrick) size_t makeTemp() - { - import std.array : uninitializedArray; - - if (temp is null) - { - temp = uninitializedArray!Temp(workUnitSize); - } - - rangeMutex.lock(); - scope(exit) rangeMutex.unlock(); - - size_t i = 0; - for (; i < workUnitSize && !range.empty; range.popFront(), i++) - { - temp[i] = range.front; - } - - temp = temp[0 .. i]; - auto ret = nPopped; - nPopped += temp.length; - return ret; - } - - static if (bufferTrick) size_t makeTemp() - { - import std.algorithm.mutation : swap; - rangeMutex.lock(); - scope(exit) rangeMutex.unlock(); - - // Elide copying by just swapping buffers. - temp.length = range.buf1.length; - swap(range.buf1, temp); - - // This is necessary in case popFront() has been called on - // range before entering the parallel foreach loop. - temp = temp[range.bufPos..$]; - - static if (is(typeof(range._length))) - { - range._length -= (temp.length - range.bufPos); - } - - range.doBufSwap(); - auto ret = nPopped; - nPopped += temp.length; - return ret; - } - } - - while (atomicLoad(shouldContinue)) - { - auto overallIndex = makeTemp(); - if (temp.empty) - { - atomicStore(shouldContinue, false); - break; - } - - foreach (i; 0 .. temp.length) - { - scope(success) overallIndex++; - - static if (hasLvalueElements!R) - { - static if (withIndex) - { - if (dg(overallIndex, *temp[i])) foreachErr(); - } - else - { - if (dg(*temp[i])) foreachErr(); - } - } - else - { - static if (withIndex) - { - if (dg(overallIndex, temp[i])) foreachErr(); - } - else - { - if (dg(temp[i])) foreachErr(); - } - } - } - } - } - - submitAndExecute(pool, &doIt); - - return 0; -}; - - -private struct ParallelForeach(R) -{ - TaskPool pool; - R range; - size_t workUnitSize; - alias E = ElementType!R; - - static if (hasLvalueElements!R) - { - alias NoIndexDg = int delegate(ref E); - alias IndexDg = int delegate(size_t, ref E); - } - else - { - alias NoIndexDg = int delegate(E); - alias IndexDg = int delegate(size_t, E); - } - - int opApply(scope NoIndexDg dg) - { - static if (randLen!R) - { - mixin(parallelApplyMixinRandomAccess); - } - else - { - mixin(parallelApplyMixinInputRange); - } - } - - int opApply(scope IndexDg dg) - { - static if (randLen!R) - { - mixin(parallelApplyMixinRandomAccess); - } - else - { - mixin(parallelApplyMixinInputRange); - } - } -} - -/* -This struct buffers the output of a callable that outputs data into a -user-supplied buffer into a set of buffers of some fixed size. It allows these -buffers to be accessed with an input range interface. This is used internally -in the buffer-recycling overload of TaskPool.asyncBuf, which creates an -instance and forwards it to the input range overload of asyncBuf. -*/ -private struct RoundRobinBuffer(C1, C2) -{ - // No need for constraints because they're already checked for in asyncBuf. - - alias Array = Parameters!(C1.init)[0]; - alias T = typeof(Array.init[0]); - - T[][] bufs; - size_t index; - C1 nextDel; - C2 emptyDel; - bool _empty; - bool primed; - - this( - C1 nextDel, - C2 emptyDel, - size_t initialBufSize, - size_t nBuffers - ) { - this.nextDel = nextDel; - this.emptyDel = emptyDel; - bufs.length = nBuffers; - - foreach (ref buf; bufs) - { - buf.length = initialBufSize; - } - } - - void prime() - in - { - assert(!empty); - } - do - { - scope(success) primed = true; - nextDel(bufs[index]); - } - - - T[] front() @property - in - { - assert(!empty); - } - do - { - if (!primed) prime(); - return bufs[index]; - } - - void popFront() - { - if (empty || emptyDel()) - { - _empty = true; - return; - } - - index = (index + 1) % bufs.length; - primed = false; - } - - bool empty() @property const @safe pure nothrow - { - return _empty; - } -} - -version (StdUnittest) -{ - // This was the only way I could get nested maps to work. - private __gshared TaskPool poolInstance; -} - -// These test basic functionality but don't stress test for threading bugs. -// These are the tests that should be run every time Phobos is compiled. -@system unittest -{ - import std.algorithm.comparison : equal, min, max; - import std.algorithm.iteration : filter, map, reduce; - import std.array : split; - import std.conv : text; - import std.exception : assertThrown; - import std.math.operations : isClose; - import std.math.algebraic : sqrt, abs; - import std.math.exponential : log; - import std.range : indexed, iota, join; - import std.typecons : Tuple, tuple; - import std.stdio; - - poolInstance = new TaskPool(2); - scope(exit) poolInstance.stop(); - - // The only way this can be verified is manually. - debug(std_parallelism) stderr.writeln("totalCPUs = ", totalCPUs); - - auto oldPriority = poolInstance.priority; - poolInstance.priority = Thread.PRIORITY_MAX; - assert(poolInstance.priority == Thread.PRIORITY_MAX); - - poolInstance.priority = Thread.PRIORITY_MIN; - assert(poolInstance.priority == Thread.PRIORITY_MIN); - - poolInstance.priority = oldPriority; - assert(poolInstance.priority == oldPriority); - - static void refFun(ref uint num) - { - num++; - } - - uint x; - - // Test task(). - auto t = task!refFun(x); - poolInstance.put(t); - t.yieldForce; - assert(t.args[0] == 1); - - auto t2 = task(&refFun, x); - poolInstance.put(t2); - t2.yieldForce; - assert(t2.args[0] == 1); - - // Test scopedTask(). - auto st = scopedTask!refFun(x); - poolInstance.put(st); - st.yieldForce; - assert(st.args[0] == 1); - - auto st2 = scopedTask(&refFun, x); - poolInstance.put(st2); - st2.yieldForce; - assert(st2.args[0] == 1); - - // Test executeInNewThread(). - auto ct = scopedTask!refFun(x); - ct.executeInNewThread(Thread.PRIORITY_MAX); - ct.yieldForce; - assert(ct.args[0] == 1); - - // Test ref return. - uint toInc = 0; - static ref T makeRef(T)(ref T num) - { - return num; - } - - auto t3 = task!makeRef(toInc); - taskPool.put(t3); - assert(t3.args[0] == 0); - t3.spinForce++; - assert(t3.args[0] == 1); - - static void testSafe() @safe { - static int bump(int num) - { - return num + 1; - } - - auto safePool = new TaskPool(0); - auto t = task(&bump, 1); - taskPool.put(t); - assert(t.yieldForce == 2); - - auto st = scopedTask(&bump, 1); - taskPool.put(st); - assert(st.yieldForce == 2); - safePool.stop(); - } - - auto arr = [1,2,3,4,5]; - auto nums = new uint[5]; - auto nums2 = new uint[5]; - - foreach (i, ref elem; poolInstance.parallel(arr)) - { - elem++; - nums[i] = cast(uint) i + 2; - nums2[i] = elem; - } - - assert(nums == [2,3,4,5,6], text(nums)); - assert(nums2 == nums, text(nums2)); - assert(arr == nums, text(arr)); - - // Test const/immutable arguments. - static int add(int lhs, int rhs) - { - return lhs + rhs; - } - immutable addLhs = 1; - immutable addRhs = 2; - auto addTask = task(&add, addLhs, addRhs); - auto addScopedTask = scopedTask(&add, addLhs, addRhs); - poolInstance.put(addTask); - poolInstance.put(addScopedTask); - assert(addTask.yieldForce == 3); - assert(addScopedTask.yieldForce == 3); - - // Test parallel foreach with non-random access range. - auto range = filter!"a != 666"([0, 1, 2, 3, 4]); - - foreach (i, elem; poolInstance.parallel(range)) - { - nums[i] = cast(uint) i; - } - - assert(nums == [0,1,2,3,4]); - - auto logs = new double[1_000_000]; - foreach (i, ref elem; poolInstance.parallel(logs)) - { - elem = log(i + 1.0); - } - - foreach (i, elem; logs) - { - assert(isClose(elem, log(double(i + 1)))); - } - - assert(poolInstance.amap!"a * a"([1,2,3,4,5]) == [1,4,9,16,25]); - assert(poolInstance.amap!"a * a"([1,2,3,4,5], new long[5]) == [1,4,9,16,25]); - assert(poolInstance.amap!("a * a", "-a")([1,2,3]) == - [tuple(1, -1), tuple(4, -2), tuple(9, -3)]); - - auto tupleBuf = new Tuple!(int, int)[3]; - poolInstance.amap!("a * a", "-a")([1,2,3], tupleBuf); - assert(tupleBuf == [tuple(1, -1), tuple(4, -2), tuple(9, -3)]); - poolInstance.amap!("a * a", "-a")([1,2,3], 5, tupleBuf); - assert(tupleBuf == [tuple(1, -1), tuple(4, -2), tuple(9, -3)]); - - // Test amap with a non-array buffer. - auto toIndex = new int[5]; - auto ind = indexed(toIndex, [3, 1, 4, 0, 2]); - poolInstance.amap!"a * 2"([1, 2, 3, 4, 5], ind); - assert(equal(ind, [2, 4, 6, 8, 10])); - assert(equal(toIndex, [8, 4, 10, 2, 6])); - poolInstance.amap!"a / 2"(ind, ind); - assert(equal(ind, [1, 2, 3, 4, 5])); - assert(equal(toIndex, [4, 2, 5, 1, 3])); - - auto buf = new int[5]; - poolInstance.amap!"a * a"([1,2,3,4,5], buf); - assert(buf == [1,4,9,16,25]); - poolInstance.amap!"a * a"([1,2,3,4,5], 4, buf); - assert(buf == [1,4,9,16,25]); - - assert(poolInstance.reduce!"a + b"([1]) == 1); - assert(poolInstance.reduce!"a + b"([1,2,3,4]) == 10); - assert(poolInstance.reduce!"a + b"(0.0, [1,2,3,4]) == 10); - assert(poolInstance.reduce!"a + b"(0.0, [1,2,3,4], 1) == 10); - assert(poolInstance.reduce!(min, max)([1,2,3,4]) == tuple(1, 4)); - assert(poolInstance.reduce!("a + b", "a * b")(tuple(0, 1), [1,2,3,4]) == - tuple(10, 24)); - - immutable serialAns = reduce!"a + b"(iota(1000)); - assert(poolInstance.reduce!"a + b"(0, iota(1000)) == serialAns); - assert(poolInstance.reduce!"a + b"(iota(1000)) == serialAns); - - // Test worker-local storage. - auto wl = poolInstance.workerLocalStorage(0); - foreach (i; poolInstance.parallel(iota(1000), 1)) - { - wl.get = wl.get + i; - } - - auto wlRange = wl.toRange; - auto parallelSum = poolInstance.reduce!"a + b"(wlRange); - assert(parallelSum == 499500); - assert(wlRange[0 .. 1][0] == wlRange[0]); - assert(wlRange[1 .. 2][0] == wlRange[1]); - - // Test finish() - { - static void slowFun() { Thread.sleep(dur!"msecs"(1)); } - - auto pool1 = new TaskPool(); - auto tSlow = task!slowFun(); - pool1.put(tSlow); - pool1.finish(); - tSlow.yieldForce; - // Can't assert that pool1.status == PoolState.stopNow because status - // doesn't change until after the "done" flag is set and the waiting - // thread is woken up. - - auto pool2 = new TaskPool(); - auto tSlow2 = task!slowFun(); - pool2.put(tSlow2); - pool2.finish(true); // blocking - assert(tSlow2.done); - - // Test fix for https://issues.dlang.org/show_bug.cgi?id=8582 by making pool size zero. - auto pool3 = new TaskPool(0); - auto tSlow3 = task!slowFun(); - pool3.put(tSlow3); - pool3.finish(true); // blocking - assert(tSlow3.done); - - // This is correct because no thread will terminate unless pool2.status - // and pool3.status have already been set to stopNow. - assert(pool2.status == TaskPool.PoolState.stopNow); - assert(pool3.status == TaskPool.PoolState.stopNow); - } - - // Test default pool stuff. - assert(taskPool.size == totalCPUs - 1); - - nums = new uint[1000]; - foreach (i; parallel(iota(1000))) - { - nums[i] = cast(uint) i; - } - assert(equal(nums, iota(1000))); - - assert(equal( - poolInstance.map!"a * a"(iota(3_000_001), 10_000), - map!"a * a"(iota(3_000_001)) - )); - - // The filter is to kill random access and test the non-random access - // branch. - assert(equal( - poolInstance.map!"a * a"( - filter!"a == a"(iota(3_000_001) - ), 10_000, 1000), - map!"a * a"(iota(3_000_001)) - )); - - assert( - reduce!"a + b"(0UL, - poolInstance.map!"a * a"(iota(300_001), 10_000) - ) == - reduce!"a + b"(0UL, - map!"a * a"(iota(300_001)) - ) - ); - - assert(equal( - iota(1_000_002), - poolInstance.asyncBuf(filter!"a == a"(iota(1_000_002))) - )); - - { - import std.conv : to; - import std.file : deleteme; - - string temp_file = deleteme ~ "-tempDelMe.txt"; - auto file = File(temp_file, "wb"); - scope(exit) - { - file.close(); - import std.file; - remove(temp_file); - } - - auto written = [[1.0, 2, 3], [4.0, 5, 6], [7.0, 8, 9]]; - foreach (row; written) - { - file.writeln(join(to!(string[])(row), "\t")); - } - - file = File(temp_file); - - void next(ref char[] buf) - { - file.readln(buf); - import std.string : chomp; - buf = chomp(buf); - } - - double[][] read; - auto asyncReader = taskPool.asyncBuf(&next, &file.eof); - - foreach (line; asyncReader) - { - if (line.length == 0) continue; - auto ls = line.split("\t"); - read ~= to!(double[])(ls); - } - - assert(read == written); - file.close(); - } - - // Test Map/AsyncBuf chaining. - - auto abuf = poolInstance.asyncBuf(iota(-1.0, 3_000_000), 100); - auto temp = poolInstance.map!sqrt( - abuf, 100, 5 - ); - auto lmchain = poolInstance.map!"a * a"(temp, 100, 5); - lmchain.popFront(); - - int ii; - foreach ( elem; (lmchain)) - { - if (!isClose(elem, ii)) - { - stderr.writeln(ii, '\t', elem); - } - ii++; - } - - // Test buffer trick in parallel foreach. - abuf = poolInstance.asyncBuf(iota(-1.0, 1_000_000), 100); - abuf.popFront(); - auto bufTrickTest = new size_t[abuf.length]; - foreach (i, elem; parallel(abuf)) - { - bufTrickTest[i] = i; - } - - assert(equal(iota(1_000_000), bufTrickTest)); - - auto myTask = task!(abs)(-1); - taskPool.put(myTask); - assert(myTask.spinForce == 1); - - // Test that worker local storage from one pool receives an index of 0 - // when the index is queried w.r.t. another pool. The only way to do this - // is non-deterministically. - foreach (i; parallel(iota(1000), 1)) - { - assert(poolInstance.workerIndex == 0); - } - - foreach (i; poolInstance.parallel(iota(1000), 1)) - { - assert(taskPool.workerIndex == 0); - } - - // Test exception handling. - static void parallelForeachThrow() - { - foreach (elem; parallel(iota(10))) - { - throw new Exception(""); - } - } - - assertThrown!Exception(parallelForeachThrow()); - - static int reduceException(int a, int b) - { - throw new Exception(""); - } - - assertThrown!Exception( - poolInstance.reduce!reduceException(iota(3)) - ); - - static int mapException(int a) - { - throw new Exception(""); - } - - assertThrown!Exception( - poolInstance.amap!mapException(iota(3)) - ); - - static void mapThrow() - { - auto m = poolInstance.map!mapException(iota(3)); - m.popFront(); - } - - assertThrown!Exception(mapThrow()); - - struct ThrowingRange - { - @property int front() - { - return 1; - } - void popFront() - { - throw new Exception(""); - } - enum bool empty = false; - } - - assertThrown!Exception(poolInstance.asyncBuf(ThrowingRange.init)); -} - -//version = parallelismStressTest; - -// These are more like stress tests than real unit tests. They print out -// tons of stuff and should not be run every time make unittest is run. -version (parallelismStressTest) -{ - @system unittest - { - import std.stdio : stderr, writeln, readln; - import std.range : iota; - import std.algorithm.iteration : filter, reduce; - - size_t attempt; - for (; attempt < 10; attempt++) - foreach (poolSize; [0, 4]) - { - - poolInstance = new TaskPool(poolSize); - - uint[] numbers = new uint[1_000]; - - foreach (i; poolInstance.parallel( iota(0, numbers.length)) ) - { - numbers[i] = cast(uint) i; - } - - // Make sure it works. - foreach (i; 0 .. numbers.length) - { - assert(numbers[i] == i); - } - - stderr.writeln("Done creating nums."); - - - auto myNumbers = filter!"a % 7 > 0"( iota(0, 1000)); - foreach (num; poolInstance.parallel(myNumbers)) - { - assert(num % 7 > 0 && num < 1000); - } - stderr.writeln("Done modulus test."); - - uint[] squares = poolInstance.amap!"a * a"(numbers, 100); - assert(squares.length == numbers.length); - foreach (i, number; numbers) - { - assert(squares[i] == number * number); - } - stderr.writeln("Done squares."); - - auto sumFuture = task!( reduce!"a + b" )(numbers); - poolInstance.put(sumFuture); - - ulong sumSquares = 0; - foreach (elem; numbers) - { - sumSquares += elem * elem; - } - - uint mySum = sumFuture.spinForce(); - assert(mySum == 999 * 1000 / 2); - - auto mySumParallel = poolInstance.reduce!"a + b"(numbers); - assert(mySum == mySumParallel); - stderr.writeln("Done sums."); - - auto myTask = task( - { - synchronized writeln("Our lives are parallel...Our lives are parallel."); - }); - poolInstance.put(myTask); - - auto nestedOuter = "abcd"; - auto nestedInner = iota(0, 10, 2); - - foreach (i, letter; poolInstance.parallel(nestedOuter, 1)) - { - foreach (j, number; poolInstance.parallel(nestedInner, 1)) - { - synchronized writeln(i, ": ", letter, " ", j, ": ", number); - } - } - - poolInstance.stop(); - } - - assert(attempt == 10); - writeln("Press enter to go to next round of unittests."); - readln(); - } - - // These unittests are intended more for actual testing and not so much - // as examples. - @system unittest - { - import std.stdio : stderr; - import std.range : iota; - import std.algorithm.iteration : filter, reduce; - import std.math.algebraic : sqrt; - import std.math.operations : isClose; - import std.math.traits : isNaN; - import std.conv : text; - - foreach (attempt; 0 .. 10) - foreach (poolSize; [0, 4]) - { - poolInstance = new TaskPool(poolSize); - - // Test indexing. - stderr.writeln("Creator Raw Index: ", poolInstance.threadIndex); - assert(poolInstance.workerIndex() == 0); - - // Test worker-local storage. - auto workerLocalStorage = poolInstance.workerLocalStorage!uint(1); - foreach (i; poolInstance.parallel(iota(0U, 1_000_000))) - { - workerLocalStorage.get++; - } - assert(reduce!"a + b"(workerLocalStorage.toRange) == - 1_000_000 + poolInstance.size + 1); - - // Make sure work is reasonably balanced among threads. This test is - // non-deterministic and is more of a sanity check than something that - // has an absolute pass/fail. - shared(uint)[void*] nJobsByThread; - foreach (thread; poolInstance.pool) - { - nJobsByThread[cast(void*) thread] = 0; - } - nJobsByThread[ cast(void*) Thread.getThis()] = 0; - - foreach (i; poolInstance.parallel( iota(0, 1_000_000), 100 )) - { - atomicOp!"+="( nJobsByThread[ cast(void*) Thread.getThis() ], 1); - } - - stderr.writeln("\nCurrent thread is: ", - cast(void*) Thread.getThis()); - stderr.writeln("Workload distribution: "); - foreach (k, v; nJobsByThread) - { - stderr.writeln(k, '\t', v); - } - - // Test whether amap can be nested. - real[][] matrix = new real[][](1000, 1000); - foreach (i; poolInstance.parallel( iota(0, matrix.length) )) - { - foreach (j; poolInstance.parallel( iota(0, matrix[0].length) )) - { - matrix[i][j] = i * j; - } - } - - // Get around weird bugs having to do w/ sqrt being an intrinsic: - static real mySqrt(real num) - { - return sqrt(num); - } - - static real[] parallelSqrt(real[] nums) - { - return poolInstance.amap!mySqrt(nums); - } - - real[][] sqrtMatrix = poolInstance.amap!parallelSqrt(matrix); - - foreach (i, row; sqrtMatrix) - { - foreach (j, elem; row) - { - real shouldBe = sqrt( cast(real) i * j); - assert(isClose(shouldBe, elem)); - sqrtMatrix[i][j] = shouldBe; - } - } - - auto saySuccess = task( - { - stderr.writeln( - "Success doing matrix stuff that involves nested pool use."); - }); - poolInstance.put(saySuccess); - saySuccess.workForce(); - - // A more thorough test of amap, reduce: Find the sum of the square roots of - // matrix. - - static real parallelSum(real[] input) - { - return poolInstance.reduce!"a + b"(input); - } - - auto sumSqrt = poolInstance.reduce!"a + b"( - poolInstance.amap!parallelSum( - sqrtMatrix - ) - ); - - assert(isClose(sumSqrt, 4.437e8, 1e-2)); - stderr.writeln("Done sum of square roots."); - - // Test whether tasks work with function pointers. - /+ // This part is buggy and needs to be fixed... - auto nanTask = task(&isNaN, 1.0L); - poolInstance.put(nanTask); - assert(nanTask.spinForce == false); - - if (poolInstance.size > 0) - { - // Test work waiting. - static void uselessFun() - { - foreach (i; 0 .. 1_000_000) {} - } - - auto uselessTasks = new typeof(task(&uselessFun))[1000]; - foreach (ref uselessTask; uselessTasks) - { - uselessTask = task(&uselessFun); - } - foreach (ref uselessTask; uselessTasks) - { - poolInstance.put(uselessTask); - } - foreach (ref uselessTask; uselessTasks) - { - uselessTask.workForce(); - } - } - +/ - - // Test the case of non-random access + ref returns. - int[] nums = [1,2,3,4,5]; - static struct RemoveRandom - { - int[] arr; - - ref int front() - { - return arr.front; - } - void popFront() - { - arr.popFront(); - } - bool empty() - { - return arr.empty; - } - } - - auto refRange = RemoveRandom(nums); - foreach (ref elem; poolInstance.parallel(refRange)) - { - elem++; - } - assert(nums == [2,3,4,5,6], text(nums)); - stderr.writeln("Nums: ", nums); - - poolInstance.stop(); - } - } -} - -@system unittest -{ - static struct __S_12733 - { - invariant() { assert(checksum == 1_234_567_890); } - this(ulong u){n = u;} - void opAssign(__S_12733 s){this.n = s.n;} - ulong n; - ulong checksum = 1_234_567_890; - } - - static auto __genPair_12733(ulong n) { return __S_12733(n); } - immutable ulong[] data = [ 2UL^^59-1, 2UL^^59-1, 2UL^^59-1, 112_272_537_195_293UL ]; - - auto result = taskPool.amap!__genPair_12733(data); -} - -@safe unittest -{ - import std.range : iota; - - // this test was in std.range, but caused cycles. - assert(__traits(compiles, { foreach (i; iota(0, 100UL).parallel) {} })); -} - -@safe unittest -{ - import std.algorithm.iteration : each; - - long[] arr; - static assert(is(typeof({ - arr.parallel.each!"a++"; - }))); -} - -// https://issues.dlang.org/show_bug.cgi?id=17539 -@system unittest -{ - import std.random : rndGen; - // ensure compilation - try foreach (rnd; rndGen.parallel) break; - catch (ParallelForeachError e) {} -} diff --git a/phobos/std/path.d b/phobos/std/path.d deleted file mode 100644 index a45865a..0000000 --- a/phobos/std/path.d +++ /dev/null @@ -1,4311 +0,0 @@ -// Written in the D programming language. - -/** This module is used to manipulate path strings. - - All functions, with the exception of $(LREF expandTilde) (and in some - cases $(LREF absolutePath) and $(LREF relativePath)), are pure - string manipulation functions; they don't depend on any state outside - the program, nor do they perform any actual file system actions. - This has the consequence that the module does not make any distinction - between a path that points to a directory and a path that points to a - file, and it does not know whether or not the object pointed to by the - path actually exists in the file system. - To differentiate between these cases, use $(REF isDir, std,file) and - $(REF exists, std,file). - - Note that on Windows, both the backslash ($(D `\`)) and the slash ($(D `/`)) - are in principle valid directory separators. This module treats them - both on equal footing, but in cases where a $(I new) separator is - added, a backslash will be used. Furthermore, the $(LREF buildNormalizedPath) - function will replace all slashes with backslashes on that platform. - - In general, the functions in this module assume that the input paths - are well-formed. (That is, they should not contain invalid characters, - they should follow the file system's path format, etc.) The result - of calling a function on an ill-formed path is undefined. When there - is a chance that a path or a file name is invalid (for instance, when it - has been input by the user), it may sometimes be desirable to use the - $(LREF isValidFilename) and $(LREF isValidPath) functions to check - this. - - Most functions do not perform any memory allocations, and if a string is - returned, it is usually a slice of an input string. If a function - allocates, this is explicitly mentioned in the documentation. - -$(SCRIPT inhibitQuickIndex = 1;) -$(DIVC quickindex, -$(BOOKTABLE, -$(TR $(TH Category) $(TH Functions)) -$(TR $(TD Normalization) $(TD - $(LREF absolutePath) - $(LREF asAbsolutePath) - $(LREF asNormalizedPath) - $(LREF asRelativePath) - $(LREF buildNormalizedPath) - $(LREF buildPath) - $(LREF chainPath) - $(LREF expandTilde) -)) -$(TR $(TD Partitioning) $(TD - $(LREF baseName) - $(LREF dirName) - $(LREF dirSeparator) - $(LREF driveName) - $(LREF pathSeparator) - $(LREF pathSplitter) - $(LREF relativePath) - $(LREF rootName) - $(LREF stripDrive) -)) -$(TR $(TD Validation) $(TD - $(LREF isAbsolute) - $(LREF isDirSeparator) - $(LREF isRooted) - $(LREF isValidFilename) - $(LREF isValidPath) -)) -$(TR $(TD Extension) $(TD - $(LREF defaultExtension) - $(LREF extension) - $(LREF setExtension) - $(LREF stripExtension) - $(LREF withDefaultExtension) - $(LREF withExtension) -)) -$(TR $(TD Other) $(TD - $(LREF filenameCharCmp) - $(LREF filenameCmp) - $(LREF globMatch) - $(LREF CaseSensitive) -)) -)) - - Authors: - Lars Tandle Kyllingstad, - $(HTTP digitalmars.com, Walter Bright), - Grzegorz Adam Hankiewicz, - Thomas K$(UUML)hne, - $(HTTP erdani.org, Andrei Alexandrescu) - Copyright: - Copyright (c) 2000-2014, the authors. All rights reserved. - License: - $(HTTP boost.org/LICENSE_1_0.txt, Boost License 1.0) - Source: - $(PHOBOSSRC std/path.d) -*/ -module std.path; - - -import std.file : getcwd; -static import std.meta; -import std.range; -import std.traits; - -version (OSX) - version = Darwin; -else version (iOS) - version = Darwin; -else version (TVOS) - version = Darwin; -else version (WatchOS) - version = Darwin; - -version (StdUnittest) -{ -private: - struct TestAliasedString - { - string get() @safe @nogc pure nothrow return scope { return _s; } - alias get this; - @disable this(this); - string _s; - } - - bool testAliasedString(alias func, Args...)(scope string s, scope Args args) - { - return func(TestAliasedString(s), args) == func(s, args); - } -} - -/** String used to separate directory names in a path. Under - POSIX this is a slash, under Windows a backslash. -*/ -version (Posix) enum string dirSeparator = "/"; -else version (Windows) enum string dirSeparator = "\\"; -else static assert(0, "unsupported platform"); - - - - -/** Path separator string. A colon under POSIX, a semicolon - under Windows. -*/ -version (Posix) enum string pathSeparator = ":"; -else version (Windows) enum string pathSeparator = ";"; -else static assert(0, "unsupported platform"); - - - - -/** Determines whether the given character is a directory separator. - - On Windows, this includes both $(D `\`) and $(D `/`). - On POSIX, it's just $(D `/`). -*/ -bool isDirSeparator(dchar c) @safe pure nothrow @nogc -{ - if (c == '/') return true; - version (Windows) if (c == '\\') return true; - return false; -} - -/// -@safe pure nothrow @nogc unittest -{ - version (Windows) - { - assert( '/'.isDirSeparator); - assert( '\\'.isDirSeparator); - } - else - { - assert( '/'.isDirSeparator); - assert(!'\\'.isDirSeparator); - } -} - - -/* Determines whether the given character is a drive separator. - - On Windows, this is true if c is the ':' character that separates - the drive letter from the rest of the path. On POSIX, this always - returns false. -*/ -private bool isDriveSeparator(dchar c) @safe pure nothrow @nogc -{ - version (Windows) return c == ':'; - else return false; -} - - -/* Combines the isDirSeparator and isDriveSeparator tests. */ -version (Windows) private bool isSeparator(dchar c) @safe pure nothrow @nogc -{ - return isDirSeparator(c) || isDriveSeparator(c); -} -version (Posix) private alias isSeparator = isDirSeparator; - - -/* Helper function that determines the position of the last - drive/directory separator in a string. Returns -1 if none - is found. -*/ -private ptrdiff_t lastSeparator(R)(R path) -if (isRandomAccessRange!R && isSomeChar!(ElementType!R) || - isNarrowString!R) -{ - auto i = (cast(ptrdiff_t) path.length) - 1; - while (i >= 0 && !isSeparator(path[i])) --i; - return i; -} - - -version (Windows) -{ - private bool isUNC(R)(R path) - if (isRandomAccessRange!R && isSomeChar!(ElementType!R) || - isNarrowString!R) - { - return path.length >= 3 && isDirSeparator(path[0]) && isDirSeparator(path[1]) - && !isDirSeparator(path[2]); - } - - private ptrdiff_t uncRootLength(R)(R path) - if (isRandomAccessRange!R && isSomeChar!(ElementType!R) || - isNarrowString!R) - in { assert(isUNC(path)); } - do - { - ptrdiff_t i = 3; - while (i < path.length && !isDirSeparator(path[i])) ++i; - if (i < path.length) - { - auto j = i; - do { ++j; } while (j < path.length && isDirSeparator(path[j])); - if (j < path.length) - { - do { ++j; } while (j < path.length && !isDirSeparator(path[j])); - i = j; - } - } - return i; - } - - private bool hasDrive(R)(R path) - if (isRandomAccessRange!R && isSomeChar!(ElementType!R) || - isNarrowString!R) - { - return path.length >= 2 && isDriveSeparator(path[1]); - } - - private bool isDriveRoot(R)(R path) - if (isRandomAccessRange!R && isSomeChar!(ElementType!R) || - isNarrowString!R) - { - return path.length >= 3 && isDriveSeparator(path[1]) - && isDirSeparator(path[2]); - } -} - - -/* Helper functions that strip leading/trailing slashes and backslashes - from a path. -*/ -private auto ltrimDirSeparators(R)(R path) -if (isSomeFiniteCharInputRange!R || isNarrowString!R) -{ - static if (isRandomAccessRange!R && hasSlicing!R || isNarrowString!R) - { - int i = 0; - while (i < path.length && isDirSeparator(path[i])) - ++i; - return path[i .. path.length]; - } - else - { - while (!path.empty && isDirSeparator(path.front)) - path.popFront(); - return path; - } -} - -@safe unittest -{ - import std.array; - import std.utf : byDchar; - - assert(ltrimDirSeparators("//abc//").array == "abc//"); - assert(ltrimDirSeparators("//abc//"d).array == "abc//"d); - assert(ltrimDirSeparators("//abc//".byDchar).array == "abc//"d); -} - -private auto rtrimDirSeparators(R)(R path) -if (isBidirectionalRange!R && isSomeChar!(ElementType!R) || - isNarrowString!R) -{ - static if (isRandomAccessRange!R && hasSlicing!R && hasLength!R || isNarrowString!R) - { - auto i = (cast(ptrdiff_t) path.length) - 1; - while (i >= 0 && isDirSeparator(path[i])) - --i; - return path[0 .. i+1]; - } - else - { - while (!path.empty && isDirSeparator(path.back)) - path.popBack(); - return path; - } -} - -@safe unittest -{ - import std.array; - import std.utf : byDchar; - - assert(rtrimDirSeparators("//abc//").array == "//abc"); - assert(rtrimDirSeparators("//abc//"d).array == "//abc"d); - - assert(rtrimDirSeparators(MockBiRange!char("//abc//")).array == "//abc"); -} - -private auto trimDirSeparators(R)(R path) -if (isBidirectionalRange!R && isSomeChar!(ElementType!R) || - isNarrowString!R) -{ - return ltrimDirSeparators(rtrimDirSeparators(path)); -} - -@safe unittest -{ - import std.array; - import std.utf : byDchar; - - assert(trimDirSeparators("//abc//").array == "abc"); - assert(trimDirSeparators("//abc//"d).array == "abc"d); - - assert(trimDirSeparators(MockBiRange!char("//abc//")).array == "abc"); -} - -/** This `enum` is used as a template argument to functions which - compare file names, and determines whether the comparison is - case sensitive or not. -*/ -enum CaseSensitive : bool -{ - /// File names are case insensitive - no = false, - - /// File names are case sensitive - yes = true, - - /** The default (or most common) setting for the current platform. - That is, `no` on Windows and Mac OS X, and `yes` on all - POSIX systems except Darwin (Linux, *BSD, etc.). - */ - osDefault = osDefaultCaseSensitivity -} - -/// -@safe unittest -{ - assert(baseName!(CaseSensitive.no)("dir/file.EXT", ".ext") == "file"); - assert(baseName!(CaseSensitive.yes)("dir/file.EXT", ".ext") != "file"); - - version (Posix) - assert(relativePath!(CaseSensitive.no)("/FOO/bar", "/foo/baz") == "../bar"); - else - assert(relativePath!(CaseSensitive.no)(`c:\FOO\bar`, `c:\foo\baz`) == `..\bar`); -} - -version (Windows) private enum osDefaultCaseSensitivity = false; -else version (Darwin) private enum osDefaultCaseSensitivity = false; -else version (Posix) private enum osDefaultCaseSensitivity = true; -else static assert(0); - -/** - Params: - cs = Whether or not suffix matching is case-sensitive. - path = A path name. It can be a string, or any random-access range of - characters. - suffix = An optional suffix to be removed from the file name. - Returns: The name of the file in the path name, without any leading - directory and with an optional suffix chopped off. - - If `suffix` is specified, it will be compared to `path` - using `filenameCmp!cs`, - where `cs` is an optional template parameter determining whether - the comparison is case sensitive or not. See the - $(LREF filenameCmp) documentation for details. - - Note: - This function $(I only) strips away the specified suffix, which - doesn't necessarily have to represent an extension. - To remove the extension from a path, regardless of what the extension - is, use $(LREF stripExtension). - To obtain the filename without leading directories and without - an extension, combine the functions like this: - --- - assert(baseName(stripExtension("dir/file.ext")) == "file"); - --- - - Standards: - This function complies with - $(LINK2 http://pubs.opengroup.org/onlinepubs/9699919799/utilities/basename.html, - the POSIX requirements for the 'basename' shell utility) - (with suitable adaptations for Windows paths). -*/ -auto baseName(R)(return scope R path) -if (isRandomAccessRange!R && hasSlicing!R && isSomeChar!(ElementType!R) && !isSomeString!R) -{ - return _baseName(path); -} - -/// ditto -auto baseName(C)(return scope C[] path) -if (isSomeChar!C) -{ - return _baseName(path); -} - -/// ditto -inout(C)[] baseName(CaseSensitive cs = CaseSensitive.osDefault, C, C1) - (return scope inout(C)[] path, in C1[] suffix) - @safe pure //TODO: nothrow (because of filenameCmp()) -if (isSomeChar!C && isSomeChar!C1) -{ - auto p = baseName(path); - if (p.length > suffix.length - && filenameCmp!cs(cast(const(C)[])p[$-suffix.length .. $], suffix) == 0) - { - return p[0 .. $-suffix.length]; - } - else return p; -} - -/// -@safe unittest -{ - assert(baseName("dir/file.ext") == "file.ext"); - assert(baseName("dir/file.ext", ".ext") == "file"); - assert(baseName("dir/file.ext", ".xyz") == "file.ext"); - assert(baseName("dir/filename", "name") == "file"); - assert(baseName("dir/subdir/") == "subdir"); - - version (Windows) - { - assert(baseName(`d:file.ext`) == "file.ext"); - assert(baseName(`d:\dir\file.ext`) == "file.ext"); - } -} - -@safe unittest -{ - assert(baseName("").empty); - assert(baseName("file.ext"w) == "file.ext"); - assert(baseName("file.ext"d, ".ext") == "file"); - assert(baseName("file", "file"w.dup) == "file"); - assert(baseName("dir/file.ext"d.dup) == "file.ext"); - assert(baseName("dir/file.ext", ".ext"d) == "file"); - assert(baseName("dir/file"w, "file"d) == "file"); - assert(baseName("dir///subdir////") == "subdir"); - assert(baseName("dir/subdir.ext/", ".ext") == "subdir"); - assert(baseName("dir/subdir/".dup, "subdir") == "subdir"); - assert(baseName("/"w.dup) == "/"); - assert(baseName("//"d.dup) == "/"); - assert(baseName("///") == "/"); - - assert(baseName!(CaseSensitive.yes)("file.ext", ".EXT") == "file.ext"); - assert(baseName!(CaseSensitive.no)("file.ext", ".EXT") == "file"); - - { - auto r = MockRange!(immutable(char))(`dir/file.ext`); - auto s = r.baseName(); - foreach (i, c; `file`) - assert(s[i] == c); - } - - version (Windows) - { - assert(baseName(`dir\file.ext`) == `file.ext`); - assert(baseName(`dir\file.ext`, `.ext`) == `file`); - assert(baseName(`dir\file`, `file`) == `file`); - assert(baseName(`d:file.ext`) == `file.ext`); - assert(baseName(`d:file.ext`, `.ext`) == `file`); - assert(baseName(`d:file`, `file`) == `file`); - assert(baseName(`dir\\subdir\\\`) == `subdir`); - assert(baseName(`dir\subdir.ext\`, `.ext`) == `subdir`); - assert(baseName(`dir\subdir\`, `subdir`) == `subdir`); - assert(baseName(`\`) == `\`); - assert(baseName(`\\`) == `\`); - assert(baseName(`\\\`) == `\`); - assert(baseName(`d:\`) == `\`); - assert(baseName(`d:`).empty); - assert(baseName(`\\server\share\file`) == `file`); - assert(baseName(`\\server\share\`) == `\`); - assert(baseName(`\\server\share`) == `\`); - - auto r = MockRange!(immutable(char))(`\\server\share`); - auto s = r.baseName(); - foreach (i, c; `\`) - assert(s[i] == c); - } - - assert(baseName(stripExtension("dir/file.ext")) == "file"); - - static assert(baseName("dir/file.ext") == "file.ext"); - static assert(baseName("dir/file.ext", ".ext") == "file"); - - static struct DirEntry { string s; alias s this; } - assert(baseName(DirEntry("dir/file.ext")) == "file.ext"); -} - -@safe unittest -{ - assert(testAliasedString!baseName("file")); - - enum S : string { a = "file/path/to/test" } - assert(S.a.baseName == "test"); - - char[S.a.length] sa = S.a[]; - assert(sa.baseName == "test"); -} - -private R _baseName(R)(return scope R path) -if (isRandomAccessRange!R && hasSlicing!R && isSomeChar!(ElementType!R) || isNarrowString!R) -{ - auto p1 = stripDrive(path); - if (p1.empty) - { - version (Windows) if (isUNC(path)) - return path[0 .. 1]; - static if (isSomeString!R) - return null; - else - return p1; // which is empty - } - - auto p2 = rtrimDirSeparators(p1); - if (p2.empty) return p1[0 .. 1]; - - return p2[lastSeparator(p2)+1 .. p2.length]; -} - -/** Returns the parent directory of `path`. On Windows, this - includes the drive letter if present. If `path` is a relative path and - the parent directory is the current working directory, returns `"."`. - - Params: - path = A path name. - - Returns: - A slice of `path` or `"."`. - - Standards: - This function complies with - $(LINK2 http://pubs.opengroup.org/onlinepubs/9699919799/utilities/dirname.html, - the POSIX requirements for the 'dirname' shell utility) - (with suitable adaptations for Windows paths). -*/ -auto dirName(R)(return scope R path) -if (isRandomAccessRange!R && hasSlicing!R && hasLength!R && isSomeChar!(ElementType!R) && !isSomeString!R) -{ - return _dirName(path); -} - -/// ditto -auto dirName(C)(return scope C[] path) -if (isSomeChar!C) -{ - return _dirName(path); -} - -/// -@safe unittest -{ - assert(dirName("") == "."); - assert(dirName("file"w) == "."); - assert(dirName("dir/"d) == "."); - assert(dirName("dir///") == "."); - assert(dirName("dir/file"w.dup) == "dir"); - assert(dirName("dir///file"d.dup) == "dir"); - assert(dirName("dir/subdir/") == "dir"); - assert(dirName("/dir/file"w) == "/dir"); - assert(dirName("/file"d) == "/"); - assert(dirName("/") == "/"); - assert(dirName("///") == "/"); - - version (Windows) - { - assert(dirName(`dir\`) == `.`); - assert(dirName(`dir\\\`) == `.`); - assert(dirName(`dir\file`) == `dir`); - assert(dirName(`dir\\\file`) == `dir`); - assert(dirName(`dir\subdir\`) == `dir`); - assert(dirName(`\dir\file`) == `\dir`); - assert(dirName(`\file`) == `\`); - assert(dirName(`\`) == `\`); - assert(dirName(`\\\`) == `\`); - assert(dirName(`d:`) == `d:`); - assert(dirName(`d:file`) == `d:`); - assert(dirName(`d:\`) == `d:\`); - assert(dirName(`d:\file`) == `d:\`); - assert(dirName(`d:\dir\file`) == `d:\dir`); - assert(dirName(`\\server\share\dir\file`) == `\\server\share\dir`); - assert(dirName(`\\server\share\file`) == `\\server\share`); - assert(dirName(`\\server\share\`) == `\\server\share`); - assert(dirName(`\\server\share`) == `\\server\share`); - } -} - -@safe unittest -{ - assert(testAliasedString!dirName("file")); - - enum S : string { a = "file/path/to/test" } - assert(S.a.dirName == "file/path/to"); - - char[S.a.length] sa = S.a[]; - assert(sa.dirName == "file/path/to"); -} - -@safe unittest -{ - static assert(dirName("dir/file") == "dir"); - - import std.array; - import std.utf : byChar, byWchar, byDchar; - - assert(dirName("".byChar).array == "."); - assert(dirName("file"w.byWchar).array == "."w); - assert(dirName("dir/"d.byDchar).array == "."d); - assert(dirName("dir///".byChar).array == "."); - assert(dirName("dir/subdir/".byChar).array == "dir"); - assert(dirName("/dir/file"w.byWchar).array == "/dir"w); - assert(dirName("/file"d.byDchar).array == "/"d); - assert(dirName("/".byChar).array == "/"); - assert(dirName("///".byChar).array == "/"); - - version (Windows) - { - assert(dirName(`dir\`.byChar).array == `.`); - assert(dirName(`dir\\\`.byChar).array == `.`); - assert(dirName(`dir\file`.byChar).array == `dir`); - assert(dirName(`dir\\\file`.byChar).array == `dir`); - assert(dirName(`dir\subdir\`.byChar).array == `dir`); - assert(dirName(`\dir\file`.byChar).array == `\dir`); - assert(dirName(`\file`.byChar).array == `\`); - assert(dirName(`\`.byChar).array == `\`); - assert(dirName(`\\\`.byChar).array == `\`); - assert(dirName(`d:`.byChar).array == `d:`); - assert(dirName(`d:file`.byChar).array == `d:`); - assert(dirName(`d:\`.byChar).array == `d:\`); - assert(dirName(`d:\file`.byChar).array == `d:\`); - assert(dirName(`d:\dir\file`.byChar).array == `d:\dir`); - assert(dirName(`\\server\share\dir\file`.byChar).array == `\\server\share\dir`); - assert(dirName(`\\server\share\file`) == `\\server\share`); - assert(dirName(`\\server\share\`.byChar).array == `\\server\share`); - assert(dirName(`\\server\share`.byChar).array == `\\server\share`); - } - - //static assert(dirName("dir/file".byChar).array == "dir"); -} - -private auto _dirName(R)(return scope R path) -{ - static auto result(bool dot, typeof(path[0 .. 1]) p) - { - static if (isSomeString!R) - return dot ? "." : p; - else - { - import std.range : choose, only; - return choose(dot, only(cast(ElementEncodingType!R)'.'), p); - } - } - - if (path.empty) - return result(true, path[0 .. 0]); - - auto p = rtrimDirSeparators(path); - if (p.empty) - return result(false, path[0 .. 1]); - - version (Windows) - { - if (isUNC(p) && uncRootLength(p) == p.length) - return result(false, p); - - if (p.length == 2 && isDriveSeparator(p[1]) && path.length > 2) - return result(false, path[0 .. 3]); - } - - auto i = lastSeparator(p); - if (i == -1) - return result(true, p); - if (i == 0) - return result(false, p[0 .. 1]); - - version (Windows) - { - // If the directory part is either d: or d:\ - // do not chop off the last symbol. - if (isDriveSeparator(p[i]) || isDriveSeparator(p[i-1])) - return result(false, p[0 .. i+1]); - } - // Remove any remaining trailing (back)slashes. - return result(false, rtrimDirSeparators(p[0 .. i])); -} - -/** Returns the root directory of the specified path, or `null` if the - path is not rooted. - - Params: - path = A path name. - - Returns: - A slice of `path`. -*/ -auto rootName(R)(R path) -if (isRandomAccessRange!R && hasSlicing!R && hasLength!R && isSomeChar!(ElementType!R) && !isSomeString!R) -{ - return _rootName(path); -} - -/// ditto -auto rootName(C)(C[] path) -if (isSomeChar!C) -{ - return _rootName(path); -} - -/// -@safe unittest -{ - assert(rootName("") is null); - assert(rootName("foo") is null); - assert(rootName("/") == "/"); - assert(rootName("/foo/bar") == "/"); - - version (Windows) - { - assert(rootName("d:foo") is null); - assert(rootName(`d:\foo`) == `d:\`); - assert(rootName(`\\server\share\foo`) == `\\server\share`); - assert(rootName(`\\server\share`) == `\\server\share`); - } -} - -@safe unittest -{ - assert(testAliasedString!rootName("/foo/bar")); - - enum S : string { a = "/foo/bar" } - assert(S.a.rootName == "/"); - - char[S.a.length] sa = S.a[]; - assert(sa.rootName == "/"); -} - -@safe unittest -{ - import std.array; - import std.utf : byChar; - - assert(rootName("".byChar).array == ""); - assert(rootName("foo".byChar).array == ""); - assert(rootName("/".byChar).array == "/"); - assert(rootName("/foo/bar".byChar).array == "/"); - - version (Windows) - { - assert(rootName("d:foo".byChar).array == ""); - assert(rootName(`d:\foo`.byChar).array == `d:\`); - assert(rootName(`\\server\share\foo`.byChar).array == `\\server\share`); - assert(rootName(`\\server\share`.byChar).array == `\\server\share`); - } -} - -private auto _rootName(R)(R path) -{ - if (path.empty) - goto Lnull; - - version (Posix) - { - if (isDirSeparator(path[0])) return path[0 .. 1]; - } - else version (Windows) - { - if (isDirSeparator(path[0])) - { - if (isUNC(path)) return path[0 .. uncRootLength(path)]; - else return path[0 .. 1]; - } - else if (path.length >= 3 && isDriveSeparator(path[1]) && - isDirSeparator(path[2])) - { - return path[0 .. 3]; - } - } - else static assert(0, "unsupported platform"); - - assert(!isRooted(path)); -Lnull: - static if (is(StringTypeOf!R)) - return null; // legacy code may rely on null return rather than slice - else - return path[0 .. 0]; -} - -/** - Get the drive portion of a path. - - Params: - path = string or range of characters - - Returns: - A slice of `path` that is the drive, or an empty range if the drive - is not specified. In the case of UNC paths, the network share - is returned. - - Always returns an empty range on POSIX. -*/ -auto driveName(R)(R path) -if (isRandomAccessRange!R && hasSlicing!R && hasLength!R && isSomeChar!(ElementType!R) && !isSomeString!R) -{ - return _driveName(path); -} - -/// ditto -auto driveName(C)(C[] path) -if (isSomeChar!C) -{ - return _driveName(path); -} - -/// -@safe unittest -{ - import std.range : empty; - version (Posix) assert(driveName("c:/foo").empty); - version (Windows) - { - assert(driveName(`dir\file`).empty); - assert(driveName(`d:file`) == "d:"); - assert(driveName(`d:\file`) == "d:"); - assert(driveName("d:") == "d:"); - assert(driveName(`\\server\share\file`) == `\\server\share`); - assert(driveName(`\\server\share\`) == `\\server\share`); - assert(driveName(`\\server\share`) == `\\server\share`); - - static assert(driveName(`d:\file`) == "d:"); - } -} - -@safe unittest -{ - assert(testAliasedString!driveName("d:/file")); - - version (Posix) - immutable result = ""; - else version (Windows) - immutable result = "d:"; - - enum S : string { a = "d:/file" } - assert(S.a.driveName == result); - - char[S.a.length] sa = S.a[]; - assert(sa.driveName == result); -} - -@safe unittest -{ - import std.array; - import std.utf : byChar; - - version (Posix) assert(driveName("c:/foo".byChar).empty); - version (Windows) - { - assert(driveName(`dir\file`.byChar).empty); - assert(driveName(`d:file`.byChar).array == "d:"); - assert(driveName(`d:\file`.byChar).array == "d:"); - assert(driveName("d:".byChar).array == "d:"); - assert(driveName(`\\server\share\file`.byChar).array == `\\server\share`); - assert(driveName(`\\server\share\`.byChar).array == `\\server\share`); - assert(driveName(`\\server\share`.byChar).array == `\\server\share`); - - static assert(driveName(`d:\file`).array == "d:"); - } -} - -private auto _driveName(R)(R path) -{ - version (Windows) - { - if (hasDrive(path)) - return path[0 .. 2]; - else if (isUNC(path)) - return path[0 .. uncRootLength(path)]; - } - static if (isSomeString!R) - return cast(ElementEncodingType!R[]) null; // legacy code may rely on null return rather than slice - else - return path[0 .. 0]; -} - -/** Strips the drive from a Windows path. On POSIX, the path is returned - unaltered. - - Params: - path = A pathname - - Returns: A slice of path without the drive component. -*/ -auto stripDrive(R)(R path) -if (isRandomAccessRange!R && hasSlicing!R && isSomeChar!(ElementType!R) && !isSomeString!R) -{ - return _stripDrive(path); -} - -/// ditto -auto stripDrive(C)(C[] path) -if (isSomeChar!C) -{ - return _stripDrive(path); -} - -/// -@safe unittest -{ - version (Windows) - { - assert(stripDrive(`d:\dir\file`) == `\dir\file`); - assert(stripDrive(`\\server\share\dir\file`) == `\dir\file`); - } -} - -@safe unittest -{ - assert(testAliasedString!stripDrive("d:/dir/file")); - - version (Posix) - immutable result = "d:/dir/file"; - else version (Windows) - immutable result = "/dir/file"; - - enum S : string { a = "d:/dir/file" } - assert(S.a.stripDrive == result); - - char[S.a.length] sa = S.a[]; - assert(sa.stripDrive == result); -} - -@safe unittest -{ - version (Windows) - { - assert(stripDrive(`d:\dir\file`) == `\dir\file`); - assert(stripDrive(`\\server\share\dir\file`) == `\dir\file`); - static assert(stripDrive(`d:\dir\file`) == `\dir\file`); - - auto r = MockRange!(immutable(char))(`d:\dir\file`); - auto s = r.stripDrive(); - foreach (i, c; `\dir\file`) - assert(s[i] == c); - } - version (Posix) - { - assert(stripDrive(`d:\dir\file`) == `d:\dir\file`); - - auto r = MockRange!(immutable(char))(`d:\dir\file`); - auto s = r.stripDrive(); - foreach (i, c; `d:\dir\file`) - assert(s[i] == c); - } -} - -private auto _stripDrive(R)(R path) -{ - version (Windows) - { - if (hasDrive!(BaseOf!R)(path)) return path[2 .. path.length]; - else if (isUNC!(BaseOf!R)(path)) return path[uncRootLength!(BaseOf!R)(path) .. path.length]; - } - return path; -} - - -/* Helper function that returns the position of the filename/extension - separator dot in path. - - Params: - path = file spec as string or indexable range - Returns: - index of extension separator (the dot), or -1 if not found -*/ -private ptrdiff_t extSeparatorPos(R)(const R path) -if (isRandomAccessRange!R && hasLength!R && isSomeChar!(ElementType!R) || - isNarrowString!R) -{ - for (auto i = path.length; i-- > 0 && !isSeparator(path[i]); ) - { - if (path[i] == '.' && i > 0 && !isSeparator(path[i-1])) - return i; - } - return -1; -} - -@safe unittest -{ - assert(extSeparatorPos("file") == -1); - assert(extSeparatorPos("file.ext"w) == 4); - assert(extSeparatorPos("file.ext1.ext2"d) == 9); - assert(extSeparatorPos(".foo".dup) == -1); - assert(extSeparatorPos(".foo.ext"w.dup) == 4); -} - -@safe unittest -{ - assert(extSeparatorPos("dir/file"d.dup) == -1); - assert(extSeparatorPos("dir/file.ext") == 8); - assert(extSeparatorPos("dir/file.ext1.ext2"w) == 13); - assert(extSeparatorPos("dir/.foo"d) == -1); - assert(extSeparatorPos("dir/.foo.ext".dup) == 8); - - version (Windows) - { - assert(extSeparatorPos("dir\\file") == -1); - assert(extSeparatorPos("dir\\file.ext") == 8); - assert(extSeparatorPos("dir\\file.ext1.ext2") == 13); - assert(extSeparatorPos("dir\\.foo") == -1); - assert(extSeparatorPos("dir\\.foo.ext") == 8); - - assert(extSeparatorPos("d:file") == -1); - assert(extSeparatorPos("d:file.ext") == 6); - assert(extSeparatorPos("d:file.ext1.ext2") == 11); - assert(extSeparatorPos("d:.foo") == -1); - assert(extSeparatorPos("d:.foo.ext") == 6); - } - - static assert(extSeparatorPos("file") == -1); - static assert(extSeparatorPos("file.ext"w) == 4); -} - - -/** - Params: path = A path name. - Returns: The _extension part of a file name, including the dot. - - If there is no _extension, `null` is returned. -*/ -auto extension(R)(R path) -if (isRandomAccessRange!R && hasSlicing!R && isSomeChar!(ElementType!R) || - is(StringTypeOf!R)) -{ - auto i = extSeparatorPos!(BaseOf!R)(path); - if (i == -1) - { - static if (is(StringTypeOf!R)) - return StringTypeOf!R.init[]; // which is null - else - return path[0 .. 0]; - } - else return path[i .. path.length]; -} - -/// -@safe unittest -{ - import std.range : empty; - assert(extension("file").empty); - assert(extension("file.") == "."); - assert(extension("file.ext"w) == ".ext"); - assert(extension("file.ext1.ext2"d) == ".ext2"); - assert(extension(".foo".dup).empty); - assert(extension(".foo.ext"w.dup) == ".ext"); - - static assert(extension("file").empty); - static assert(extension("file.ext") == ".ext"); -} - -@safe unittest -{ - { - auto r = MockRange!(immutable(char))(`file.ext1.ext2`); - auto s = r.extension(); - foreach (i, c; `.ext2`) - assert(s[i] == c); - } - - static struct DirEntry { string s; alias s this; } - assert(extension(DirEntry("file")).empty); -} - - -/** Remove extension from path. - - Params: - path = string or range to be sliced - - Returns: - slice of path with the extension (if any) stripped off -*/ -auto stripExtension(R)(R path) -if (isRandomAccessRange!R && hasSlicing!R && hasLength!R && isSomeChar!(ElementType!R) && !isSomeString!R) -{ - return _stripExtension(path); -} - -/// Ditto -auto stripExtension(C)(C[] path) -if (isSomeChar!C) -{ - return _stripExtension(path); -} - -/// -@safe unittest -{ - assert(stripExtension("file") == "file"); - assert(stripExtension("file.ext") == "file"); - assert(stripExtension("file.ext1.ext2") == "file.ext1"); - assert(stripExtension("file.") == "file"); - assert(stripExtension(".file") == ".file"); - assert(stripExtension(".file.ext") == ".file"); - assert(stripExtension("dir/file.ext") == "dir/file"); -} - -@safe unittest -{ - assert(testAliasedString!stripExtension("file")); - - enum S : string { a = "foo.bar" } - assert(S.a.stripExtension == "foo"); - - char[S.a.length] sa = S.a[]; - assert(sa.stripExtension == "foo"); -} - -@safe unittest -{ - assert(stripExtension("file.ext"w) == "file"); - assert(stripExtension("file.ext1.ext2"d) == "file.ext1"); - - import std.array; - import std.utf : byChar, byWchar, byDchar; - - assert(stripExtension("file".byChar).array == "file"); - assert(stripExtension("file.ext"w.byWchar).array == "file"); - assert(stripExtension("file.ext1.ext2"d.byDchar).array == "file.ext1"); -} - -private auto _stripExtension(R)(R path) -{ - immutable i = extSeparatorPos(path); - return i == -1 ? path : path[0 .. i]; -} - -/** Sets or replaces an extension. - - If the filename already has an extension, it is replaced. If not, the - extension is simply appended to the filename. Including a leading dot - in `ext` is optional. - - If the extension is empty, this function is equivalent to - $(LREF stripExtension). - - This function normally allocates a new string (the possible exception - being the case when path is immutable and doesn't already have an - extension). - - Params: - path = A path name - ext = The new extension - - Returns: A string containing the path given by `path`, but where - the extension has been set to `ext`. - - See_Also: - $(LREF withExtension) which does not allocate and returns a lazy range. -*/ -immutable(C1)[] setExtension(C1, C2)(in C1[] path, in C2[] ext) -if (isSomeChar!C1 && !is(C1 == immutable) && is(immutable C1 == immutable C2)) -{ - try - { - import std.conv : to; - return withExtension(path, ext).to!(typeof(return)); - } - catch (Exception e) - { - assert(0); - } -} - -///ditto -immutable(C1)[] setExtension(C1, C2)(immutable(C1)[] path, const(C2)[] ext) -if (isSomeChar!C1 && is(immutable C1 == immutable C2)) -{ - if (ext.length == 0) - return stripExtension(path); - - try - { - import std.conv : to; - return withExtension(path, ext).to!(typeof(return)); - } - catch (Exception e) - { - assert(0); - } -} - -/// -@safe unittest -{ - assert(setExtension("file", "ext") == "file.ext"); - assert(setExtension("file"w, ".ext"w) == "file.ext"); - assert(setExtension("file."d, "ext"d) == "file.ext"); - assert(setExtension("file.", ".ext") == "file.ext"); - assert(setExtension("file.old"w, "new"w) == "file.new"); - assert(setExtension("file.old"d, ".new"d) == "file.new"); -} - -@safe unittest -{ - assert(setExtension("file"w.dup, "ext"w) == "file.ext"); - assert(setExtension("file"w.dup, ".ext"w) == "file.ext"); - assert(setExtension("file."w, "ext"w.dup) == "file.ext"); - assert(setExtension("file."w, ".ext"w.dup) == "file.ext"); - assert(setExtension("file.old"d.dup, "new"d) == "file.new"); - assert(setExtension("file.old"d.dup, ".new"d) == "file.new"); - - static assert(setExtension("file", "ext") == "file.ext"); - static assert(setExtension("file.old", "new") == "file.new"); - - static assert(setExtension("file"w.dup, "ext"w) == "file.ext"); - static assert(setExtension("file.old"d.dup, "new"d) == "file.new"); - - // https://issues.dlang.org/show_bug.cgi?id=10601 - assert(setExtension("file", "") == "file"); - assert(setExtension("file.ext", "") == "file"); -} - -/************ - * Replace existing extension on filespec with new one. - * - * Params: - * path = string or random access range representing a filespec - * ext = the new extension - * Returns: - * Range with `path`'s extension (if any) replaced with `ext`. - * The element encoding type of the returned range will be the same as `path`'s. - * See_Also: - * $(LREF setExtension) - */ -auto withExtension(R, C)(R path, C[] ext) -if (isRandomAccessRange!R && hasSlicing!R && hasLength!R && isSomeChar!(ElementType!R) && - !isSomeString!R && isSomeChar!C) -{ - return _withExtension(path, ext); -} - -/// Ditto -auto withExtension(C1, C2)(C1[] path, C2[] ext) -if (isSomeChar!C1 && isSomeChar!C2) -{ - return _withExtension(path, ext); -} - -/// -@safe unittest -{ - import std.array; - assert(withExtension("file", "ext").array == "file.ext"); - assert(withExtension("file"w, ".ext"w).array == "file.ext"); - assert(withExtension("file.ext"w, ".").array == "file."); - - import std.utf : byChar, byWchar; - assert(withExtension("file".byChar, "ext").array == "file.ext"); - assert(withExtension("file"w.byWchar, ".ext"w).array == "file.ext"w); - assert(withExtension("file.ext"w.byWchar, ".").array == "file."w); -} - -@safe unittest -{ - import std.algorithm.comparison : equal; - - assert(testAliasedString!withExtension("file", "ext")); - - enum S : string { a = "foo.bar" } - assert(equal(S.a.withExtension(".txt"), "foo.txt")); - - char[S.a.length] sa = S.a[]; - assert(equal(sa.withExtension(".txt"), "foo.txt")); -} - -private auto _withExtension(R, C)(R path, C[] ext) -{ - import std.range : only, chain; - import std.utf : byUTF; - - alias CR = Unqual!(ElementEncodingType!R); - auto dot = only(CR('.')); - if (ext.length == 0 || ext[0] == '.') - dot.popFront(); // so dot is an empty range, too - return chain(stripExtension(path).byUTF!CR, dot, ext.byUTF!CR); -} - -/** Params: - path = A path name. - ext = The default extension to use. - - Returns: The path given by `path`, with the extension given by `ext` - appended if the path doesn't already have one. - - Including the dot in the extension is optional. - - This function always allocates a new string, except in the case when - path is immutable and already has an extension. -*/ -immutable(C1)[] defaultExtension(C1, C2)(in C1[] path, in C2[] ext) -if (isSomeChar!C1 && is(immutable C1 == immutable C2)) -{ - import std.conv : to; - return withDefaultExtension(path, ext).to!(typeof(return)); -} - -/// -@safe unittest -{ - assert(defaultExtension("file", "ext") == "file.ext"); - assert(defaultExtension("file", ".ext") == "file.ext"); - assert(defaultExtension("file.", "ext") == "file."); - assert(defaultExtension("file.old", "new") == "file.old"); - assert(defaultExtension("file.old", ".new") == "file.old"); -} - -@safe unittest -{ - assert(defaultExtension("file"w.dup, "ext"w) == "file.ext"); - assert(defaultExtension("file.old"d.dup, "new"d) == "file.old"); - - static assert(defaultExtension("file", "ext") == "file.ext"); - static assert(defaultExtension("file.old", "new") == "file.old"); - - static assert(defaultExtension("file"w.dup, "ext"w) == "file.ext"); - static assert(defaultExtension("file.old"d.dup, "new"d) == "file.old"); -} - - -/******************************** - * Set the extension of `path` to `ext` if `path` doesn't have one. - * - * Params: - * path = filespec as string or range - * ext = extension, may have leading '.' - * Returns: - * range with the result - */ -auto withDefaultExtension(R, C)(R path, C[] ext) -if (isRandomAccessRange!R && hasSlicing!R && hasLength!R && isSomeChar!(ElementType!R) && - !isSomeString!R && isSomeChar!C) -{ - return _withDefaultExtension(path, ext); -} - -/// Ditto -auto withDefaultExtension(C1, C2)(C1[] path, C2[] ext) -if (isSomeChar!C1 && isSomeChar!C2) -{ - return _withDefaultExtension(path, ext); -} - -/// -@safe unittest -{ - import std.array; - assert(withDefaultExtension("file", "ext").array == "file.ext"); - assert(withDefaultExtension("file"w, ".ext").array == "file.ext"w); - assert(withDefaultExtension("file.", "ext").array == "file."); - assert(withDefaultExtension("file", "").array == "file."); - - import std.utf : byChar, byWchar; - assert(withDefaultExtension("file".byChar, "ext").array == "file.ext"); - assert(withDefaultExtension("file"w.byWchar, ".ext").array == "file.ext"w); - assert(withDefaultExtension("file.".byChar, "ext"d).array == "file."); - assert(withDefaultExtension("file".byChar, "").array == "file."); -} - -@safe unittest -{ - import std.algorithm.comparison : equal; - - assert(testAliasedString!withDefaultExtension("file", "ext")); - - enum S : string { a = "foo" } - assert(equal(S.a.withDefaultExtension(".txt"), "foo.txt")); - - char[S.a.length] sa = S.a[]; - assert(equal(sa.withDefaultExtension(".txt"), "foo.txt")); -} - -private auto _withDefaultExtension(R, C)(R path, C[] ext) -{ - import std.range : only, chain; - import std.utf : byUTF; - - alias CR = Unqual!(ElementEncodingType!R); - auto dot = only(CR('.')); - immutable i = extSeparatorPos(path); - if (i == -1) - { - if (ext.length > 0 && ext[0] == '.') - ext = ext[1 .. $]; // remove any leading . from ext[] - } - else - { - // path already has an extension, so make these empty - ext = ext[0 .. 0]; - dot.popFront(); - } - return chain(path.byUTF!CR, dot, ext.byUTF!CR); -} - -/** Combines one or more path segments. - - This function takes a set of path segments, given as an input - range of string elements or as a set of string arguments, - and concatenates them with each other. Directory separators - are inserted between segments if necessary. If any of the - path segments are absolute (as defined by $(LREF isAbsolute)), the - preceding segments will be dropped. - - On Windows, if one of the path segments are rooted, but not absolute - (e.g. $(D `\foo`)), all preceding path segments down to the previous - root will be dropped. (See below for an example.) - - This function always allocates memory to hold the resulting path. - The variadic overload is guaranteed to only perform a single - allocation, as is the range version if `paths` is a forward - range. - - Params: - segments = An $(REF_ALTTEXT input range, isInputRange, std,range,primitives) - of segments to assemble the path from. - Returns: The assembled path. -*/ -immutable(ElementEncodingType!(ElementType!Range))[] - buildPath(Range)(scope Range segments) - if (isInputRange!Range && !isInfinite!Range && isSomeString!(ElementType!Range)) -{ - if (segments.empty) return null; - - // If this is a forward range, we can pre-calculate a maximum length. - static if (isForwardRange!Range) - { - auto segments2 = segments.save; - size_t precalc = 0; - foreach (segment; segments2) precalc += segment.length + 1; - } - // Otherwise, just venture a guess and resize later if necessary. - else size_t precalc = 255; - - auto buf = new Unqual!(ElementEncodingType!(ElementType!Range))[](precalc); - size_t pos = 0; - foreach (segment; segments) - { - if (segment.empty) continue; - static if (!isForwardRange!Range) - { - immutable neededLength = pos + segment.length + 1; - if (buf.length < neededLength) - buf.length = reserve(buf, neededLength + buf.length/2); - } - auto r = chainPath(buf[0 .. pos], segment); - size_t i; - foreach (c; r) - { - buf[i] = c; - ++i; - } - pos = i; - } - static U trustedCast(U, V)(V v) @trusted pure nothrow { return cast(U) v; } - return trustedCast!(typeof(return))(buf[0 .. pos]); -} - -/// ditto -immutable(C)[] buildPath(C)(const(C)[][] paths...) - @safe pure nothrow -if (isSomeChar!C) -{ - return buildPath!(typeof(paths))(paths); -} - -/// -@safe unittest -{ - version (Posix) - { - assert(buildPath("foo", "bar", "baz") == "foo/bar/baz"); - assert(buildPath("/foo/", "bar/baz") == "/foo/bar/baz"); - assert(buildPath("/foo", "/bar") == "/bar"); - } - - version (Windows) - { - assert(buildPath("foo", "bar", "baz") == `foo\bar\baz`); - assert(buildPath(`c:\foo`, `bar\baz`) == `c:\foo\bar\baz`); - assert(buildPath("foo", `d:\bar`) == `d:\bar`); - assert(buildPath("foo", `\bar`) == `\bar`); - assert(buildPath(`c:\foo`, `\bar`) == `c:\bar`); - } -} - -@system unittest // non-documented -{ - import std.range; - // ir() wraps an array in a plain (i.e. non-forward) input range, so that - // we can test both code paths - InputRange!(C[]) ir(C)(C[][] p...) { return inputRangeObject(p.dup); } - version (Posix) - { - assert(buildPath("foo") == "foo"); - assert(buildPath("/foo/") == "/foo/"); - assert(buildPath("foo", "bar") == "foo/bar"); - assert(buildPath("foo", "bar", "baz") == "foo/bar/baz"); - assert(buildPath("foo/".dup, "bar") == "foo/bar"); - assert(buildPath("foo///", "bar".dup) == "foo///bar"); - assert(buildPath("/foo"w, "bar"w) == "/foo/bar"); - assert(buildPath("foo"w.dup, "/bar"w) == "/bar"); - assert(buildPath("foo"w, "bar/"w.dup) == "foo/bar/"); - assert(buildPath("/"d, "foo"d) == "/foo"); - assert(buildPath(""d.dup, "foo"d) == "foo"); - assert(buildPath("foo"d, ""d.dup) == "foo"); - assert(buildPath("foo", "bar".dup, "baz") == "foo/bar/baz"); - assert(buildPath("foo"w, "/bar"w, "baz"w.dup) == "/bar/baz"); - - static assert(buildPath("foo", "bar", "baz") == "foo/bar/baz"); - static assert(buildPath("foo", "/bar", "baz") == "/bar/baz"); - - // The following are mostly duplicates of the above, except that the - // range version does not accept mixed constness. - assert(buildPath(ir("foo")) == "foo"); - assert(buildPath(ir("/foo/")) == "/foo/"); - assert(buildPath(ir("foo", "bar")) == "foo/bar"); - assert(buildPath(ir("foo", "bar", "baz")) == "foo/bar/baz"); - assert(buildPath(ir("foo/".dup, "bar".dup)) == "foo/bar"); - assert(buildPath(ir("foo///".dup, "bar".dup)) == "foo///bar"); - assert(buildPath(ir("/foo"w, "bar"w)) == "/foo/bar"); - assert(buildPath(ir("foo"w.dup, "/bar"w.dup)) == "/bar"); - assert(buildPath(ir("foo"w.dup, "bar/"w.dup)) == "foo/bar/"); - assert(buildPath(ir("/"d, "foo"d)) == "/foo"); - assert(buildPath(ir(""d.dup, "foo"d.dup)) == "foo"); - assert(buildPath(ir("foo"d, ""d)) == "foo"); - assert(buildPath(ir("foo", "bar", "baz")) == "foo/bar/baz"); - assert(buildPath(ir("foo"w.dup, "/bar"w.dup, "baz"w.dup)) == "/bar/baz"); - } - version (Windows) - { - assert(buildPath("foo") == "foo"); - assert(buildPath(`\foo/`) == `\foo/`); - assert(buildPath("foo", "bar", "baz") == `foo\bar\baz`); - assert(buildPath("foo", `\bar`) == `\bar`); - assert(buildPath(`c:\foo`, "bar") == `c:\foo\bar`); - assert(buildPath("foo"w, `d:\bar`w.dup) == `d:\bar`); - assert(buildPath(`c:\foo\bar`, `\baz`) == `c:\baz`); - assert(buildPath(`\\foo\bar\baz`d, `foo`d, `\bar`d) == `\\foo\bar\bar`d); - - static assert(buildPath("foo", "bar", "baz") == `foo\bar\baz`); - static assert(buildPath("foo", `c:\bar`, "baz") == `c:\bar\baz`); - - assert(buildPath(ir("foo")) == "foo"); - assert(buildPath(ir(`\foo/`)) == `\foo/`); - assert(buildPath(ir("foo", "bar", "baz")) == `foo\bar\baz`); - assert(buildPath(ir("foo", `\bar`)) == `\bar`); - assert(buildPath(ir(`c:\foo`, "bar")) == `c:\foo\bar`); - assert(buildPath(ir("foo"w.dup, `d:\bar`w.dup)) == `d:\bar`); - assert(buildPath(ir(`c:\foo\bar`, `\baz`)) == `c:\baz`); - assert(buildPath(ir(`\\foo\bar\baz`d, `foo`d, `\bar`d)) == `\\foo\bar\bar`d); - } - - // Test that allocation works as it should. - auto manyShort = "aaa".repeat(1000).array(); - auto manyShortCombined = join(manyShort, dirSeparator); - assert(buildPath(manyShort) == manyShortCombined); - assert(buildPath(ir(manyShort)) == manyShortCombined); - - auto fewLong = 'b'.repeat(500).array().repeat(10).array(); - auto fewLongCombined = join(fewLong, dirSeparator); - assert(buildPath(fewLong) == fewLongCombined); - assert(buildPath(ir(fewLong)) == fewLongCombined); -} - -@safe unittest -{ - // Test for https://issues.dlang.org/show_bug.cgi?id=7397 - string[] ary = ["a", "b"]; - version (Posix) - { - assert(buildPath(ary) == "a/b"); - } - else version (Windows) - { - assert(buildPath(ary) == `a\b`); - } -} - - -/** - * Concatenate path segments together to form one path. - * - * Params: - * r1 = first segment - * r2 = second segment - * ranges = 0 or more segments - * Returns: - * Lazy range which is the concatenation of r1, r2 and ranges with path separators. - * The resulting element type is that of r1. - * See_Also: - * $(LREF buildPath) - */ -auto chainPath(R1, R2, Ranges...)(R1 r1, R2 r2, Ranges ranges) -if ((isRandomAccessRange!R1 && hasSlicing!R1 && hasLength!R1 && isSomeChar!(ElementType!R1) || - isNarrowString!R1 && - !isConvertibleToString!R1) && - (isRandomAccessRange!R2 && hasSlicing!R2 && hasLength!R2 && isSomeChar!(ElementType!R2) || - isNarrowString!R2 && - !isConvertibleToString!R2) && - (Ranges.length == 0 || is(typeof(chainPath(r2, ranges)))) - ) -{ - static if (Ranges.length) - { - return chainPath(chainPath(r1, r2), ranges); - } - else - { - import std.range : only, chain; - import std.utf : byUTF; - - alias CR = Unqual!(ElementEncodingType!R1); - auto sep = only(CR(dirSeparator[0])); - bool usesep = false; - - auto pos = r1.length; - - if (pos) - { - if (isRooted(r2)) - { - version (Posix) - { - pos = 0; - } - else version (Windows) - { - if (isAbsolute(r2)) - pos = 0; - else - { - pos = rootName(r1).length; - if (pos > 0 && isDirSeparator(r1[pos - 1])) - --pos; - } - } - else - static assert(0); - } - else if (!isDirSeparator(r1[pos - 1])) - usesep = true; - } - if (!usesep) - sep.popFront(); - // Return r1 ~ '/' ~ r2 - return chain(r1[0 .. pos].byUTF!CR, sep, r2.byUTF!CR); - } -} - -/// -@safe unittest -{ - import std.array; - version (Posix) - { - assert(chainPath("foo", "bar", "baz").array == "foo/bar/baz"); - assert(chainPath("/foo/", "bar/baz").array == "/foo/bar/baz"); - assert(chainPath("/foo", "/bar").array == "/bar"); - } - - version (Windows) - { - assert(chainPath("foo", "bar", "baz").array == `foo\bar\baz`); - assert(chainPath(`c:\foo`, `bar\baz`).array == `c:\foo\bar\baz`); - assert(chainPath("foo", `d:\bar`).array == `d:\bar`); - assert(chainPath("foo", `\bar`).array == `\bar`); - assert(chainPath(`c:\foo`, `\bar`).array == `c:\bar`); - } - - import std.utf : byChar; - version (Posix) - { - assert(chainPath("foo", "bar", "baz").array == "foo/bar/baz"); - assert(chainPath("/foo/".byChar, "bar/baz").array == "/foo/bar/baz"); - assert(chainPath("/foo", "/bar".byChar).array == "/bar"); - } - - version (Windows) - { - assert(chainPath("foo", "bar", "baz").array == `foo\bar\baz`); - assert(chainPath(`c:\foo`.byChar, `bar\baz`).array == `c:\foo\bar\baz`); - assert(chainPath("foo", `d:\bar`).array == `d:\bar`); - assert(chainPath("foo", `\bar`.byChar).array == `\bar`); - assert(chainPath(`c:\foo`, `\bar`w).array == `c:\bar`); - } -} - -auto chainPath(Ranges...)(auto ref Ranges ranges) -if (Ranges.length >= 2 && - std.meta.anySatisfy!(isConvertibleToString, Ranges)) -{ - import std.meta : staticMap; - alias Types = staticMap!(convertToString, Ranges); - return chainPath!Types(ranges); -} - -@safe unittest -{ - assert(chainPath(TestAliasedString(null), TestAliasedString(null), TestAliasedString(null)).empty); - assert(chainPath(TestAliasedString(null), TestAliasedString(null), "").empty); - assert(chainPath(TestAliasedString(null), "", TestAliasedString(null)).empty); - static struct S { string s; } - static assert(!__traits(compiles, chainPath(TestAliasedString(null), S(""), TestAliasedString(null)))); -} - -/** Performs the same task as $(LREF buildPath), - while at the same time resolving current/parent directory - symbols (`"."` and `".."`) and removing superfluous - directory separators. - It will return "." if the path leads to the starting directory. - On Windows, slashes are replaced with backslashes. - - Using buildNormalizedPath on null paths will always return null. - - Note that this function does not resolve symbolic links. - - This function always allocates memory to hold the resulting path. - Use $(LREF asNormalizedPath) to not allocate memory. - - Params: - paths = An array of paths to assemble. - - Returns: The assembled path. -*/ -immutable(C)[] buildNormalizedPath(C)(const(C[])[] paths...) - @safe pure nothrow -if (isSomeChar!C) -{ - import std.array : array; - - const(C)[] chained; - foreach (path; paths) - { - if (chained) - chained = chainPath(chained, path).array; - else - chained = path; - } - auto result = asNormalizedPath(chained); - // .array returns a copy, so it is unique - return result.array; -} - -/// -@safe unittest -{ - assert(buildNormalizedPath("foo", "..") == "."); - - version (Posix) - { - assert(buildNormalizedPath("/foo/./bar/..//baz/") == "/foo/baz"); - assert(buildNormalizedPath("../foo/.") == "../foo"); - assert(buildNormalizedPath("/foo", "bar/baz/") == "/foo/bar/baz"); - assert(buildNormalizedPath("/foo", "/bar/..", "baz") == "/baz"); - assert(buildNormalizedPath("foo/./bar", "../../", "../baz") == "../baz"); - assert(buildNormalizedPath("/foo/./bar", "../../baz") == "/baz"); - } - - version (Windows) - { - assert(buildNormalizedPath(`c:\foo\.\bar/..\\baz\`) == `c:\foo\baz`); - assert(buildNormalizedPath(`..\foo\.`) == `..\foo`); - assert(buildNormalizedPath(`c:\foo`, `bar\baz\`) == `c:\foo\bar\baz`); - assert(buildNormalizedPath(`c:\foo`, `bar/..`) == `c:\foo`); - assert(buildNormalizedPath(`\\server\share\foo`, `..\bar`) == - `\\server\share\bar`); - } -} - -@safe unittest -{ - assert(buildNormalizedPath(".", ".") == "."); - assert(buildNormalizedPath("foo", "..") == "."); - assert(buildNormalizedPath("", "") is null); - assert(buildNormalizedPath("", ".") == "."); - assert(buildNormalizedPath(".", "") == "."); - assert(buildNormalizedPath(null, "foo") == "foo"); - assert(buildNormalizedPath("", "foo") == "foo"); - assert(buildNormalizedPath("", "") == ""); - assert(buildNormalizedPath("", null) == ""); - assert(buildNormalizedPath(null, "") == ""); - assert(buildNormalizedPath!(char)(null, null) == ""); - - version (Posix) - { - assert(buildNormalizedPath("/", "foo", "bar") == "/foo/bar"); - assert(buildNormalizedPath("foo", "bar", "baz") == "foo/bar/baz"); - assert(buildNormalizedPath("foo", "bar/baz") == "foo/bar/baz"); - assert(buildNormalizedPath("foo", "bar//baz///") == "foo/bar/baz"); - assert(buildNormalizedPath("/foo", "bar/baz") == "/foo/bar/baz"); - assert(buildNormalizedPath("/foo", "/bar/baz") == "/bar/baz"); - assert(buildNormalizedPath("/foo/..", "/bar/./baz") == "/bar/baz"); - assert(buildNormalizedPath("/foo/..", "bar/baz") == "/bar/baz"); - assert(buildNormalizedPath("/foo/../../", "bar/baz") == "/bar/baz"); - assert(buildNormalizedPath("/foo/bar", "../baz") == "/foo/baz"); - assert(buildNormalizedPath("/foo/bar", "../../baz") == "/baz"); - assert(buildNormalizedPath("/foo/bar", ".././/baz/..", "wee/") == "/foo/wee"); - assert(buildNormalizedPath("//foo/bar", "baz///wee") == "/foo/bar/baz/wee"); - static assert(buildNormalizedPath("/foo/..", "/bar/./baz") == "/bar/baz"); - } - else version (Windows) - { - assert(buildNormalizedPath(`\`, `foo`, `bar`) == `\foo\bar`); - assert(buildNormalizedPath(`foo`, `bar`, `baz`) == `foo\bar\baz`); - assert(buildNormalizedPath(`foo`, `bar\baz`) == `foo\bar\baz`); - assert(buildNormalizedPath(`foo`, `bar\\baz\\\`) == `foo\bar\baz`); - assert(buildNormalizedPath(`\foo`, `bar\baz`) == `\foo\bar\baz`); - assert(buildNormalizedPath(`\foo`, `\bar\baz`) == `\bar\baz`); - assert(buildNormalizedPath(`\foo\..`, `\bar\.\baz`) == `\bar\baz`); - assert(buildNormalizedPath(`\foo\..`, `bar\baz`) == `\bar\baz`); - assert(buildNormalizedPath(`\foo\..\..\`, `bar\baz`) == `\bar\baz`); - assert(buildNormalizedPath(`\foo\bar`, `..\baz`) == `\foo\baz`); - assert(buildNormalizedPath(`\foo\bar`, `../../baz`) == `\baz`); - assert(buildNormalizedPath(`\foo\bar`, `..\.\/baz\..`, `wee\`) == `\foo\wee`); - - assert(buildNormalizedPath(`c:\`, `foo`, `bar`) == `c:\foo\bar`); - assert(buildNormalizedPath(`c:foo`, `bar`, `baz`) == `c:foo\bar\baz`); - assert(buildNormalizedPath(`c:foo`, `bar\baz`) == `c:foo\bar\baz`); - assert(buildNormalizedPath(`c:foo`, `bar\\baz\\\`) == `c:foo\bar\baz`); - assert(buildNormalizedPath(`c:\foo`, `bar\baz`) == `c:\foo\bar\baz`); - assert(buildNormalizedPath(`c:\foo`, `\bar\baz`) == `c:\bar\baz`); - assert(buildNormalizedPath(`c:\foo\..`, `\bar\.\baz`) == `c:\bar\baz`); - assert(buildNormalizedPath(`c:\foo\..`, `bar\baz`) == `c:\bar\baz`); - assert(buildNormalizedPath(`c:\foo\..\..\`, `bar\baz`) == `c:\bar\baz`); - assert(buildNormalizedPath(`c:\foo\bar`, `..\baz`) == `c:\foo\baz`); - assert(buildNormalizedPath(`c:\foo\bar`, `..\..\baz`) == `c:\baz`); - assert(buildNormalizedPath(`c:\foo\bar`, `..\.\\baz\..`, `wee\`) == `c:\foo\wee`); - - assert(buildNormalizedPath(`\\server\share`, `foo`, `bar`) == `\\server\share\foo\bar`); - assert(buildNormalizedPath(`\\server\share\`, `foo`, `bar`) == `\\server\share\foo\bar`); - assert(buildNormalizedPath(`\\server\share\foo`, `bar\baz`) == `\\server\share\foo\bar\baz`); - assert(buildNormalizedPath(`\\server\share\foo`, `\bar\baz`) == `\\server\share\bar\baz`); - assert(buildNormalizedPath(`\\server\share\foo\..`, `\bar\.\baz`) == `\\server\share\bar\baz`); - assert(buildNormalizedPath(`\\server\share\foo\..`, `bar\baz`) == `\\server\share\bar\baz`); - assert(buildNormalizedPath(`\\server\share\foo\..\..\`, `bar\baz`) == `\\server\share\bar\baz`); - assert(buildNormalizedPath(`\\server\share\foo\bar`, `..\baz`) == `\\server\share\foo\baz`); - assert(buildNormalizedPath(`\\server\share\foo\bar`, `..\..\baz`) == `\\server\share\baz`); - assert(buildNormalizedPath(`\\server\share\foo\bar`, `..\.\\baz\..`, `wee\`) == `\\server\share\foo\wee`); - - static assert(buildNormalizedPath(`\foo\..\..\`, `bar\baz`) == `\bar\baz`); - } - else static assert(0); -} - -@safe unittest -{ - // Test for https://issues.dlang.org/show_bug.cgi?id=7397 - string[] ary = ["a", "b"]; - version (Posix) - { - assert(buildNormalizedPath(ary) == "a/b"); - } - else version (Windows) - { - assert(buildNormalizedPath(ary) == `a\b`); - } -} - - -/** Normalize a path by resolving current/parent directory - symbols (`"."` and `".."`) and removing superfluous - directory separators. - It will return "." if the path leads to the starting directory. - On Windows, slashes are replaced with backslashes. - - Using asNormalizedPath on empty paths will always return an empty path. - - Does not resolve symbolic links. - - This function always allocates memory to hold the resulting path. - Use $(LREF buildNormalizedPath) to allocate memory and return a string. - - Params: - path = string or random access range representing the path to normalize - - Returns: - normalized path as a forward range -*/ - -auto asNormalizedPath(R)(return scope R path) -if (isSomeChar!(ElementEncodingType!R) && - (isRandomAccessRange!R && hasSlicing!R && hasLength!R || isNarrowString!R) && - !isConvertibleToString!R) -{ - alias C = Unqual!(ElementEncodingType!R); - alias S = typeof(path[0 .. 0]); - - static struct Result - { - @property bool empty() - { - return c == c.init; - } - - @property C front() - { - return c; - } - - void popFront() - { - C lastc = c; - c = c.init; - if (!element.empty) - { - c = getElement0(); - return; - } - L1: - while (1) - { - if (elements.empty) - { - element = element[0 .. 0]; - return; - } - element = elements.front; - elements.popFront(); - if (isDot(element) || (rooted && isDotDot(element))) - continue; - - if (rooted || !isDotDot(element)) - { - int n = 1; - auto elements2 = elements.save; - while (!elements2.empty) - { - auto e = elements2.front; - elements2.popFront(); - if (isDot(e)) - continue; - if (isDotDot(e)) - { - --n; - if (n == 0) - { - elements = elements2; - element = element[0 .. 0]; - continue L1; - } - } - else - ++n; - } - } - break; - } - - static assert(dirSeparator.length == 1); - if (lastc == dirSeparator[0] || lastc == lastc.init) - c = getElement0(); - else - c = dirSeparator[0]; - } - - static if (isForwardRange!R) - { - @property auto save() - { - auto result = this; - result.element = element.save; - result.elements = elements.save; - return result; - } - } - - private: - this(R path) - { - element = rootName(path); - auto i = element.length; - while (i < path.length && isDirSeparator(path[i])) - ++i; - rooted = i > 0; - elements = pathSplitter(path[i .. $]); - popFront(); - if (c == c.init && path.length) - c = C('.'); - } - - C getElement0() - { - static if (isNarrowString!S) // avoid autodecode - { - C c = element[0]; - element = element[1 .. $]; - } - else - { - C c = element.front; - element.popFront(); - } - version (Windows) - { - if (c == '/') // can appear in root element - c = '\\'; // use native Windows directory separator - } - return c; - } - - // See if elem is "." - static bool isDot(S elem) - { - return elem.length == 1 && elem[0] == '.'; - } - - // See if elem is ".." - static bool isDotDot(S elem) - { - return elem.length == 2 && elem[0] == '.' && elem[1] == '.'; - } - - bool rooted; // the path starts with a root directory - C c; - S element; - typeof(pathSplitter(path[0 .. 0])) elements; - } - - return Result(path); -} - -/// -@safe unittest -{ - import std.array; - assert(asNormalizedPath("foo/..").array == "."); - - version (Posix) - { - assert(asNormalizedPath("/foo/./bar/..//baz/").array == "/foo/baz"); - assert(asNormalizedPath("../foo/.").array == "../foo"); - assert(asNormalizedPath("/foo/bar/baz/").array == "/foo/bar/baz"); - assert(asNormalizedPath("/foo/./bar/../../baz").array == "/baz"); - } - - version (Windows) - { - assert(asNormalizedPath(`c:\foo\.\bar/..\\baz\`).array == `c:\foo\baz`); - assert(asNormalizedPath(`..\foo\.`).array == `..\foo`); - assert(asNormalizedPath(`c:\foo\bar\baz\`).array == `c:\foo\bar\baz`); - assert(asNormalizedPath(`c:\foo\bar/..`).array == `c:\foo`); - assert(asNormalizedPath(`\\server\share\foo\..\bar`).array == - `\\server\share\bar`); - } -} - -auto asNormalizedPath(R)(return scope auto ref R path) -if (isConvertibleToString!R) -{ - return asNormalizedPath!(StringTypeOf!R)(path); -} - -@safe unittest -{ - assert(testAliasedString!asNormalizedPath(null)); -} - -@safe unittest -{ - import std.array; - import std.utf : byChar; - - assert(asNormalizedPath("").array is null); - assert(asNormalizedPath("foo").array == "foo"); - assert(asNormalizedPath(".").array == "."); - assert(asNormalizedPath("./.").array == "."); - assert(asNormalizedPath("foo/..").array == "."); - - auto save = asNormalizedPath("fob").save; - save.popFront(); - assert(save.front == 'o'); - - version (Posix) - { - assert(asNormalizedPath("/foo/bar").array == "/foo/bar"); - assert(asNormalizedPath("foo/bar/baz").array == "foo/bar/baz"); - assert(asNormalizedPath("foo/bar/baz").array == "foo/bar/baz"); - assert(asNormalizedPath("foo/bar//baz///").array == "foo/bar/baz"); - assert(asNormalizedPath("/foo/bar/baz").array == "/foo/bar/baz"); - assert(asNormalizedPath("/foo/../bar/baz").array == "/bar/baz"); - assert(asNormalizedPath("/foo/../..//bar/baz").array == "/bar/baz"); - assert(asNormalizedPath("/foo/bar/../baz").array == "/foo/baz"); - assert(asNormalizedPath("/foo/bar/../../baz").array == "/baz"); - assert(asNormalizedPath("/foo/bar/.././/baz/../wee/").array == "/foo/wee"); - assert(asNormalizedPath("//foo/bar/baz///wee").array == "/foo/bar/baz/wee"); - - assert(asNormalizedPath("foo//bar").array == "foo/bar"); - assert(asNormalizedPath("foo/bar").array == "foo/bar"); - - //Curent dir path - assert(asNormalizedPath("./").array == "."); - assert(asNormalizedPath("././").array == "."); - assert(asNormalizedPath("./foo/..").array == "."); - assert(asNormalizedPath("foo/..").array == "."); - } - else version (Windows) - { - assert(asNormalizedPath(`\foo\bar`).array == `\foo\bar`); - assert(asNormalizedPath(`foo\bar\baz`).array == `foo\bar\baz`); - assert(asNormalizedPath(`foo\bar\baz`).array == `foo\bar\baz`); - assert(asNormalizedPath(`foo\bar\\baz\\\`).array == `foo\bar\baz`); - assert(asNormalizedPath(`\foo\bar\baz`).array == `\foo\bar\baz`); - assert(asNormalizedPath(`\foo\..\\bar\.\baz`).array == `\bar\baz`); - assert(asNormalizedPath(`\foo\..\bar\baz`).array == `\bar\baz`); - assert(asNormalizedPath(`\foo\..\..\\bar\baz`).array == `\bar\baz`); - - assert(asNormalizedPath(`\foo\bar\..\baz`).array == `\foo\baz`); - assert(asNormalizedPath(`\foo\bar\../../baz`).array == `\baz`); - assert(asNormalizedPath(`\foo\bar\..\.\/baz\..\wee\`).array == `\foo\wee`); - - assert(asNormalizedPath(`c:\foo\bar`).array == `c:\foo\bar`); - assert(asNormalizedPath(`c:foo\bar\baz`).array == `c:foo\bar\baz`); - assert(asNormalizedPath(`c:foo\bar\baz`).array == `c:foo\bar\baz`); - assert(asNormalizedPath(`c:foo\bar\\baz\\\`).array == `c:foo\bar\baz`); - assert(asNormalizedPath(`c:\foo\bar\baz`).array == `c:\foo\bar\baz`); - - assert(asNormalizedPath(`c:\foo\..\\bar\.\baz`).array == `c:\bar\baz`); - assert(asNormalizedPath(`c:\foo\..\bar\baz`).array == `c:\bar\baz`); - assert(asNormalizedPath(`c:\foo\..\..\\bar\baz`).array == `c:\bar\baz`); - assert(asNormalizedPath(`c:\foo\bar\..\baz`).array == `c:\foo\baz`); - assert(asNormalizedPath(`c:\foo\bar\..\..\baz`).array == `c:\baz`); - assert(asNormalizedPath(`c:\foo\bar\..\.\\baz\..\wee\`).array == `c:\foo\wee`); - assert(asNormalizedPath(`\\server\share\foo\bar`).array == `\\server\share\foo\bar`); - assert(asNormalizedPath(`\\server\share\\foo\bar`).array == `\\server\share\foo\bar`); - assert(asNormalizedPath(`\\server\share\foo\bar\baz`).array == `\\server\share\foo\bar\baz`); - assert(asNormalizedPath(`\\server\share\foo\..\\bar\.\baz`).array == `\\server\share\bar\baz`); - assert(asNormalizedPath(`\\server\share\foo\..\bar\baz`).array == `\\server\share\bar\baz`); - assert(asNormalizedPath(`\\server\share\foo\..\..\\bar\baz`).array == `\\server\share\bar\baz`); - assert(asNormalizedPath(`\\server\share\foo\bar\..\baz`).array == `\\server\share\foo\baz`); - assert(asNormalizedPath(`\\server\share\foo\bar\..\..\baz`).array == `\\server\share\baz`); - assert(asNormalizedPath(`\\server\share\foo\bar\..\.\\baz\..\wee\`).array == `\\server\share\foo\wee`); - - static assert(asNormalizedPath(`\foo\..\..\\bar\baz`).array == `\bar\baz`); - - assert(asNormalizedPath("foo//bar").array == `foo\bar`); - - //Curent dir path - assert(asNormalizedPath(`.\`).array == "."); - assert(asNormalizedPath(`.\.\`).array == "."); - assert(asNormalizedPath(`.\foo\..`).array == "."); - assert(asNormalizedPath(`foo\..`).array == "."); - } - else static assert(0); -} - -@safe unittest -{ - import std.array; - - version (Posix) - { - // Trivial - assert(asNormalizedPath("").empty); - assert(asNormalizedPath("foo/bar").array == "foo/bar"); - - // Correct handling of leading slashes - assert(asNormalizedPath("/").array == "/"); - assert(asNormalizedPath("///").array == "/"); - assert(asNormalizedPath("////").array == "/"); - assert(asNormalizedPath("/foo/bar").array == "/foo/bar"); - assert(asNormalizedPath("//foo/bar").array == "/foo/bar"); - assert(asNormalizedPath("///foo/bar").array == "/foo/bar"); - assert(asNormalizedPath("////foo/bar").array == "/foo/bar"); - - // Correct handling of single-dot symbol (current directory) - assert(asNormalizedPath("/./foo").array == "/foo"); - assert(asNormalizedPath("/foo/./bar").array == "/foo/bar"); - - assert(asNormalizedPath("./foo").array == "foo"); - assert(asNormalizedPath("././foo").array == "foo"); - assert(asNormalizedPath("foo/././bar").array == "foo/bar"); - - // Correct handling of double-dot symbol (previous directory) - assert(asNormalizedPath("/foo/../bar").array == "/bar"); - assert(asNormalizedPath("/foo/../../bar").array == "/bar"); - assert(asNormalizedPath("/../foo").array == "/foo"); - assert(asNormalizedPath("/../../foo").array == "/foo"); - assert(asNormalizedPath("/foo/..").array == "/"); - assert(asNormalizedPath("/foo/../..").array == "/"); - - assert(asNormalizedPath("foo/../bar").array == "bar"); - assert(asNormalizedPath("foo/../../bar").array == "../bar"); - assert(asNormalizedPath("../foo").array == "../foo"); - assert(asNormalizedPath("../../foo").array == "../../foo"); - assert(asNormalizedPath("../foo/../bar").array == "../bar"); - assert(asNormalizedPath(".././../foo").array == "../../foo"); - assert(asNormalizedPath("foo/bar/..").array == "foo"); - assert(asNormalizedPath("/foo/../..").array == "/"); - - // The ultimate path - assert(asNormalizedPath("/foo/../bar//./../...///baz//").array == "/.../baz"); - static assert(asNormalizedPath("/foo/../bar//./../...///baz//").array == "/.../baz"); - } - else version (Windows) - { - // Trivial - assert(asNormalizedPath("").empty); - assert(asNormalizedPath(`foo\bar`).array == `foo\bar`); - assert(asNormalizedPath("foo/bar").array == `foo\bar`); - - // Correct handling of absolute paths - assert(asNormalizedPath("/").array == `\`); - assert(asNormalizedPath(`\`).array == `\`); - assert(asNormalizedPath(`\\\`).array == `\`); - assert(asNormalizedPath(`\\\\`).array == `\`); - assert(asNormalizedPath(`\foo\bar`).array == `\foo\bar`); - assert(asNormalizedPath(`\\foo`).array == `\\foo`); - assert(asNormalizedPath(`\\foo\\`).array == `\\foo`); - assert(asNormalizedPath(`\\foo/bar`).array == `\\foo\bar`); - assert(asNormalizedPath(`\\\foo\bar`).array == `\foo\bar`); - assert(asNormalizedPath(`\\\\foo\bar`).array == `\foo\bar`); - assert(asNormalizedPath(`c:\`).array == `c:\`); - assert(asNormalizedPath(`c:\foo\bar`).array == `c:\foo\bar`); - assert(asNormalizedPath(`c:\\foo\bar`).array == `c:\foo\bar`); - - // Correct handling of single-dot symbol (current directory) - assert(asNormalizedPath(`\./foo`).array == `\foo`); - assert(asNormalizedPath(`\foo/.\bar`).array == `\foo\bar`); - - assert(asNormalizedPath(`.\foo`).array == `foo`); - assert(asNormalizedPath(`./.\foo`).array == `foo`); - assert(asNormalizedPath(`foo\.\./bar`).array == `foo\bar`); - - // Correct handling of double-dot symbol (previous directory) - assert(asNormalizedPath(`\foo\..\bar`).array == `\bar`); - assert(asNormalizedPath(`\foo\../..\bar`).array == `\bar`); - assert(asNormalizedPath(`\..\foo`).array == `\foo`); - assert(asNormalizedPath(`\..\..\foo`).array == `\foo`); - assert(asNormalizedPath(`\foo\..`).array == `\`); - assert(asNormalizedPath(`\foo\../..`).array == `\`); - - assert(asNormalizedPath(`foo\..\bar`).array == `bar`); - assert(asNormalizedPath(`foo\..\../bar`).array == `..\bar`); - - assert(asNormalizedPath(`..\foo`).array == `..\foo`); - assert(asNormalizedPath(`..\..\foo`).array == `..\..\foo`); - assert(asNormalizedPath(`..\foo\..\bar`).array == `..\bar`); - assert(asNormalizedPath(`..\.\..\foo`).array == `..\..\foo`); - assert(asNormalizedPath(`foo\bar\..`).array == `foo`); - assert(asNormalizedPath(`\foo\..\..`).array == `\`); - assert(asNormalizedPath(`c:\foo\..\..`).array == `c:\`); - - // Correct handling of non-root path with drive specifier - assert(asNormalizedPath(`c:foo`).array == `c:foo`); - assert(asNormalizedPath(`c:..\foo\.\..\bar`).array == `c:..\bar`); - - // The ultimate path - assert(asNormalizedPath(`c:\foo\..\bar\\.\..\...\\\baz\\`).array == `c:\...\baz`); - static assert(asNormalizedPath(`c:\foo\..\bar\\.\..\...\\\baz\\`).array == `c:\...\baz`); - } - else static assert(false); -} - -/** Slice up a path into its elements. - - Params: - path = string or slicable random access range - - Returns: - bidirectional range of slices of `path` -*/ -auto pathSplitter(R)(R path) -if ((isRandomAccessRange!R && hasSlicing!R || - isNarrowString!R) && - !isConvertibleToString!R) -{ - static struct PathSplitter - { - @property bool empty() const { return pe == 0; } - - @property R front() - { - assert(!empty); - return _path[fs .. fe]; - } - - void popFront() - { - assert(!empty); - if (ps == pe) - { - if (fs == bs && fe == be) - { - pe = 0; - } - else - { - fs = bs; - fe = be; - } - } - else - { - fs = ps; - fe = fs; - while (fe < pe && !isDirSeparator(_path[fe])) - ++fe; - ps = ltrim(fe, pe); - } - } - - @property R back() - { - assert(!empty); - return _path[bs .. be]; - } - - void popBack() - { - assert(!empty); - if (ps == pe) - { - if (fs == bs && fe == be) - { - pe = 0; - } - else - { - bs = fs; - be = fe; - } - } - else - { - bs = pe; - be = bs; - while (bs > ps && !isDirSeparator(_path[bs - 1])) - --bs; - pe = rtrim(ps, bs); - } - } - @property auto save() { return this; } - - - private: - R _path; - size_t ps, pe; - size_t fs, fe; - size_t bs, be; - - this(R p) - { - if (p.empty) - { - pe = 0; - return; - } - _path = p; - - ps = 0; - pe = _path.length; - - // If path is rooted, first element is special - version (Windows) - { - if (isUNC(_path)) - { - auto i = uncRootLength(_path); - fs = 0; - fe = i; - ps = ltrim(fe, pe); - } - else if (isDriveRoot(_path)) - { - fs = 0; - fe = 3; - ps = ltrim(fe, pe); - } - else if (_path.length >= 1 && isDirSeparator(_path[0])) - { - fs = 0; - fe = 1; - ps = ltrim(fe, pe); - } - else - { - assert(!isRooted(_path)); - popFront(); - } - } - else version (Posix) - { - if (_path.length >= 1 && isDirSeparator(_path[0])) - { - fs = 0; - fe = 1; - ps = ltrim(fe, pe); - } - else - { - popFront(); - } - } - else static assert(0); - - if (ps == pe) - { - bs = fs; - be = fe; - } - else - { - pe = rtrim(ps, pe); - popBack(); - } - } - - size_t ltrim(size_t s, size_t e) - { - while (s < e && isDirSeparator(_path[s])) - ++s; - return s; - } - - size_t rtrim(size_t s, size_t e) - { - while (s < e && isDirSeparator(_path[e - 1])) - --e; - return e; - } - } - - return PathSplitter(path); -} - -/// -@safe unittest -{ - import std.algorithm.comparison : equal; - import std.conv : to; - - assert(equal(pathSplitter("/"), ["/"])); - assert(equal(pathSplitter("/foo/bar"), ["/", "foo", "bar"])); - assert(equal(pathSplitter("foo/../bar//./"), ["foo", "..", "bar", "."])); - - version (Posix) - { - assert(equal(pathSplitter("//foo/bar"), ["/", "foo", "bar"])); - } - - version (Windows) - { - assert(equal(pathSplitter(`foo\..\bar\/.\`), ["foo", "..", "bar", "."])); - assert(equal(pathSplitter("c:"), ["c:"])); - assert(equal(pathSplitter(`c:\foo\bar`), [`c:\`, "foo", "bar"])); - assert(equal(pathSplitter(`c:foo\bar`), ["c:foo", "bar"])); - } -} - -auto pathSplitter(R)(auto ref R path) -if (isConvertibleToString!R) -{ - return pathSplitter!(StringTypeOf!R)(path); -} - -@safe unittest -{ - import std.algorithm.comparison : equal; - assert(testAliasedString!pathSplitter("/")); -} - -@safe unittest -{ - // equal2 verifies that the range is the same both ways, i.e. - // through front/popFront and back/popBack. - import std.algorithm; - import std.range; - bool equal2(R1, R2)(R1 r1, R2 r2) - { - static assert(isBidirectionalRange!R1); - return equal(r1, r2) && equal(retro(r1), retro(r2)); - } - - assert(pathSplitter("").empty); - - // Root directories - assert(equal2(pathSplitter("/"), ["/"])); - assert(equal2(pathSplitter("//"), ["/"])); - assert(equal2(pathSplitter("///"w), ["/"w])); - - // Absolute paths - assert(equal2(pathSplitter("/foo/bar".dup), ["/", "foo", "bar"])); - - // General - assert(equal2(pathSplitter("foo/bar"d.dup), ["foo"d, "bar"d])); - assert(equal2(pathSplitter("foo//bar"), ["foo", "bar"])); - assert(equal2(pathSplitter("foo/bar//"w), ["foo"w, "bar"w])); - assert(equal2(pathSplitter("foo/../bar//./"d), ["foo"d, ".."d, "bar"d, "."d])); - - // save() - auto ps1 = pathSplitter("foo/bar/baz"); - auto ps2 = ps1.save; - ps1.popFront(); - assert(equal2(ps1, ["bar", "baz"])); - assert(equal2(ps2, ["foo", "bar", "baz"])); - - // Platform specific - version (Posix) - { - assert(equal2(pathSplitter("//foo/bar"w.dup), ["/"w, "foo"w, "bar"w])); - } - version (Windows) - { - assert(equal2(pathSplitter(`\`), [`\`])); - assert(equal2(pathSplitter(`foo\..\bar\/.\`), ["foo", "..", "bar", "."])); - assert(equal2(pathSplitter("c:"), ["c:"])); - assert(equal2(pathSplitter(`c:\foo\bar`), [`c:\`, "foo", "bar"])); - assert(equal2(pathSplitter(`c:foo\bar`), ["c:foo", "bar"])); - assert(equal2(pathSplitter(`\\foo\bar`), [`\\foo\bar`])); - assert(equal2(pathSplitter(`\\foo\bar\\`), [`\\foo\bar`])); - assert(equal2(pathSplitter(`\\foo\bar\baz`), [`\\foo\bar`, "baz"])); - } - - import std.exception; - assertCTFEable!( - { - assert(equal(pathSplitter("/foo/bar".dup), ["/", "foo", "bar"])); - }); - - static assert(is(typeof(pathSplitter!(const(char)[])(null).front) == const(char)[])); - - import std.utf : byDchar; - assert(equal2(pathSplitter("foo/bar"d.byDchar), ["foo"d, "bar"d])); -} - - - - -/** Determines whether a path starts at a root directory. - -Params: - path = A path name. -Returns: - Whether a path starts at a root directory. - - On POSIX, this function returns true if and only if the path starts - with a slash (/). - - On Windows, this function returns true if the path starts at - the root directory of the current drive, of some other drive, - or of a network drive. -*/ -bool isRooted(R)(R path) -if (isRandomAccessRange!R && isSomeChar!(ElementType!R) || - is(StringTypeOf!R)) -{ - if (path.length >= 1 && isDirSeparator(path[0])) return true; - version (Posix) return false; - else version (Windows) return isAbsolute!(BaseOf!R)(path); -} - -/// -@safe unittest -{ - version (Posix) - { - assert( isRooted("/")); - assert( isRooted("/foo")); - assert(!isRooted("foo")); - assert(!isRooted("../foo")); - } - - version (Windows) - { - assert( isRooted(`\`)); - assert( isRooted(`\foo`)); - assert( isRooted(`d:\foo`)); - assert( isRooted(`\\foo\bar`)); - assert(!isRooted("foo")); - assert(!isRooted("d:foo")); - } -} - -@safe unittest -{ - assert(isRooted("/")); - assert(isRooted("/foo")); - assert(!isRooted("foo")); - assert(!isRooted("../foo")); - - version (Windows) - { - assert(isRooted(`\`)); - assert(isRooted(`\foo`)); - assert(isRooted(`d:\foo`)); - assert(isRooted(`\\foo\bar`)); - assert(!isRooted("foo")); - assert(!isRooted("d:foo")); - } - - static assert(isRooted("/foo")); - static assert(!isRooted("foo")); - - static struct DirEntry { string s; alias s this; } - assert(!isRooted(DirEntry("foo"))); -} - -/** Determines whether a path is absolute or not. - - Params: path = A path name. - - Returns: Whether a path is absolute or not. - - Example: - On POSIX, an absolute path starts at the root directory. - (In fact, `_isAbsolute` is just an alias for $(LREF isRooted).) - --- - version (Posix) - { - assert(isAbsolute("/")); - assert(isAbsolute("/foo")); - assert(!isAbsolute("foo")); - assert(!isAbsolute("../foo")); - } - --- - - On Windows, an absolute path starts at the root directory of - a specific drive. Hence, it must start with $(D `d:\`) or $(D `d:/`), - where `d` is the drive letter. Alternatively, it may be a - network path, i.e. a path starting with a double (back)slash. - --- - version (Windows) - { - assert(isAbsolute(`d:\`)); - assert(isAbsolute(`d:\foo`)); - assert(isAbsolute(`\\foo\bar`)); - assert(!isAbsolute(`\`)); - assert(!isAbsolute(`\foo`)); - assert(!isAbsolute("d:foo")); - } - --- -*/ -version (StdDdoc) -{ - bool isAbsolute(R)(R path) pure nothrow @safe - if (isRandomAccessRange!R && isSomeChar!(ElementType!R) || - is(StringTypeOf!R)); -} -else version (Windows) -{ - bool isAbsolute(R)(R path) - if (isRandomAccessRange!R && isSomeChar!(ElementType!R) || - is(StringTypeOf!R)) - { - return isDriveRoot!(BaseOf!R)(path) || isUNC!(BaseOf!R)(path); - } -} -else version (Posix) -{ - alias isAbsolute = isRooted; -} - - -@safe unittest -{ - assert(!isAbsolute("foo")); - assert(!isAbsolute("../foo"w)); - static assert(!isAbsolute("foo")); - - version (Posix) - { - assert(isAbsolute("/"d)); - assert(isAbsolute("/foo".dup)); - static assert(isAbsolute("/foo")); - } - - version (Windows) - { - assert(isAbsolute("d:\\"w)); - assert(isAbsolute("d:\\foo"d)); - assert(isAbsolute("\\\\foo\\bar")); - assert(!isAbsolute("\\"w.dup)); - assert(!isAbsolute("\\foo"d.dup)); - assert(!isAbsolute("d:")); - assert(!isAbsolute("d:foo")); - static assert(isAbsolute(`d:\foo`)); - } - - { - auto r = MockRange!(immutable(char))(`../foo`); - assert(!r.isAbsolute()); - } - - static struct DirEntry { string s; alias s this; } - assert(!isAbsolute(DirEntry("foo"))); -} - - - - -/** Transforms `path` into an absolute path. - - The following algorithm is used: - $(OL - $(LI If `path` is empty, return `null`.) - $(LI If `path` is already absolute, return it.) - $(LI Otherwise, append `path` to `base` and return - the result. If `base` is not specified, the current - working directory is used.) - ) - The function allocates memory if and only if it gets to the third stage - of this algorithm. - - Note that `absolutePath` will not normalize `..` segments. - Use `buildNormalizedPath(absolutePath(path))` if that is desired. - - Params: - path = the relative path to transform - base = the base directory of the relative path - - Returns: - string of transformed path - - Throws: - `Exception` if the specified _base directory is not absolute. - - See_Also: - $(LREF asAbsolutePath) which does not allocate -*/ -string absolutePath(return scope const string path, lazy string base = getcwd()) - @safe pure -{ - import std.array : array; - if (path.empty) return null; - if (isAbsolute(path)) return path; - auto baseVar = base; - if (!isAbsolute(baseVar)) throw new Exception("Base directory must be absolute"); - return chainPath(baseVar, path).array; -} - -/// -@safe unittest -{ - version (Posix) - { - assert(absolutePath("some/file", "/foo/bar") == "/foo/bar/some/file"); - assert(absolutePath("../file", "/foo/bar") == "/foo/bar/../file"); - assert(absolutePath("/some/file", "/foo/bar") == "/some/file"); - } - - version (Windows) - { - assert(absolutePath(`some\file`, `c:\foo\bar`) == `c:\foo\bar\some\file`); - assert(absolutePath(`..\file`, `c:\foo\bar`) == `c:\foo\bar\..\file`); - assert(absolutePath(`c:\some\file`, `c:\foo\bar`) == `c:\some\file`); - assert(absolutePath(`\`, `c:\`) == `c:\`); - assert(absolutePath(`\some\file`, `c:\foo\bar`) == `c:\some\file`); - } -} - -@safe unittest -{ - version (Posix) - { - static assert(absolutePath("some/file", "/foo/bar") == "/foo/bar/some/file"); - } - - version (Windows) - { - static assert(absolutePath(`some\file`, `c:\foo\bar`) == `c:\foo\bar\some\file`); - } - - import std.exception; - assertThrown(absolutePath("bar", "foo")); -} - -// Ensure that we can call absolute path with scope paramaters -@safe unittest -{ - string testAbsPath(scope const string path, scope const string base) { - return absolutePath(path, base); - } - - version (Posix) - assert(testAbsPath("some/file", "/foo/bar") == "/foo/bar/some/file"); - version (Windows) - assert(testAbsPath(`some\file`, `c:\foo\bar`) == `c:\foo\bar\some\file`); -} - -/** Transforms `path` into an absolute path. - - The following algorithm is used: - $(OL - $(LI If `path` is empty, return `null`.) - $(LI If `path` is already absolute, return it.) - $(LI Otherwise, append `path` to the current working directory, - which allocates memory.) - ) - - Note that `asAbsolutePath` will not normalize `..` segments. - Use `asNormalizedPath(asAbsolutePath(path))` if that is desired. - - Params: - path = the relative path to transform - - Returns: - the transformed path as a lazy range - - See_Also: - $(LREF absolutePath) which returns an allocated string -*/ -auto asAbsolutePath(R)(R path) -if ((isRandomAccessRange!R && isSomeChar!(ElementType!R) || - isNarrowString!R) && - !isConvertibleToString!R) -{ - import std.file : getcwd; - string base = null; - if (!path.empty && !isAbsolute(path)) - base = getcwd(); - return chainPath(base, path); -} - -/// -@system unittest -{ - import std.array; - assert(asAbsolutePath(cast(string) null).array == ""); - version (Posix) - { - assert(asAbsolutePath("/foo").array == "/foo"); - } - version (Windows) - { - assert(asAbsolutePath("c:/foo").array == "c:/foo"); - } - asAbsolutePath("foo"); -} - -auto asAbsolutePath(R)(auto ref R path) -if (isConvertibleToString!R) -{ - return asAbsolutePath!(StringTypeOf!R)(path); -} - -@system unittest -{ - assert(testAliasedString!asAbsolutePath(null)); -} - -/** Translates `path` into a relative path. - - The returned path is relative to `base`, which is by default - taken to be the current working directory. If specified, - `base` must be an absolute path, and it is always assumed - to refer to a directory. If `path` and `base` refer to - the same directory, the function returns $(D `.`). - - The following algorithm is used: - $(OL - $(LI If `path` is a relative directory, return it unaltered.) - $(LI Find a common root between `path` and `base`. - If there is no common root, return `path` unaltered.) - $(LI Prepare a string with as many $(D `../`) or $(D `..\`) as - necessary to reach the common root from base path.) - $(LI Append the remaining segments of `path` to the string - and return.) - ) - - In the second step, path components are compared using `filenameCmp!cs`, - where `cs` is an optional template parameter determining whether - the comparison is case sensitive or not. See the - $(LREF filenameCmp) documentation for details. - - This function allocates memory. - - Params: - cs = Whether matching path name components against the base path should - be case-sensitive or not. - path = A path name. - base = The base path to construct the relative path from. - - Returns: The relative path. - - See_Also: - $(LREF asRelativePath) which does not allocate memory - - Throws: - `Exception` if the specified _base directory is not absolute. -*/ -string relativePath(CaseSensitive cs = CaseSensitive.osDefault) - (string path, lazy string base = getcwd()) -{ - if (!isAbsolute(path)) - return path; - auto baseVar = base; - if (!isAbsolute(baseVar)) - throw new Exception("Base directory must be absolute"); - - import std.conv : to; - return asRelativePath!cs(path, baseVar).to!string; -} - -/// -@safe unittest -{ - assert(relativePath("foo") == "foo"); - - version (Posix) - { - assert(relativePath("foo", "/bar") == "foo"); - assert(relativePath("/foo/bar", "/foo/bar") == "."); - assert(relativePath("/foo/bar", "/foo/baz") == "../bar"); - assert(relativePath("/foo/bar/baz", "/foo/woo/wee") == "../../bar/baz"); - assert(relativePath("/foo/bar/baz", "/foo/bar") == "baz"); - } - version (Windows) - { - assert(relativePath("foo", `c:\bar`) == "foo"); - assert(relativePath(`c:\foo\bar`, `c:\foo\bar`) == "."); - assert(relativePath(`c:\foo\bar`, `c:\foo\baz`) == `..\bar`); - assert(relativePath(`c:\foo\bar\baz`, `c:\foo\woo\wee`) == `..\..\bar\baz`); - assert(relativePath(`c:\foo\bar\baz`, `c:\foo\bar`) == "baz"); - assert(relativePath(`c:\foo\bar`, `d:\foo`) == `c:\foo\bar`); - } -} - -@safe unittest -{ - import std.exception; - assert(relativePath("foo") == "foo"); - version (Posix) - { - relativePath("/foo"); - assert(relativePath("/foo/bar", "/foo/baz") == "../bar"); - assertThrown(relativePath("/foo", "bar")); - } - else version (Windows) - { - relativePath(`\foo`); - assert(relativePath(`c:\foo\bar\baz`, `c:\foo\bar`) == "baz"); - assertThrown(relativePath(`c:\foo`, "bar")); - } - else static assert(0); -} - -/** Transforms `path` into a path relative to `base`. - - The returned path is relative to `base`, which is usually - the current working directory. - `base` must be an absolute path, and it is always assumed - to refer to a directory. If `path` and `base` refer to - the same directory, the function returns `'.'`. - - The following algorithm is used: - $(OL - $(LI If `path` is a relative directory, return it unaltered.) - $(LI Find a common root between `path` and `base`. - If there is no common root, return `path` unaltered.) - $(LI Prepare a string with as many `../` or `..\` as - necessary to reach the common root from base path.) - $(LI Append the remaining segments of `path` to the string - and return.) - ) - - In the second step, path components are compared using `filenameCmp!cs`, - where `cs` is an optional template parameter determining whether - the comparison is case sensitive or not. See the - $(LREF filenameCmp) documentation for details. - - Params: - path = path to transform - base = absolute path - cs = whether filespec comparisons are sensitive or not; defaults to - `CaseSensitive.osDefault` - - Returns: - a random access range of the transformed path - - See_Also: - $(LREF relativePath) -*/ -auto asRelativePath(CaseSensitive cs = CaseSensitive.osDefault, R1, R2) - (R1 path, R2 base) -if ((isNarrowString!R1 || - (isRandomAccessRange!R1 && hasSlicing!R1 && isSomeChar!(ElementType!R1)) && - !isConvertibleToString!R1) && - (isNarrowString!R2 || - (isRandomAccessRange!R2 && hasSlicing!R2 && isSomeChar!(ElementType!R2)) && - !isConvertibleToString!R2)) -{ - bool choosePath = !isAbsolute(path); - - // Find common root with current working directory - - auto basePS = pathSplitter(base); - auto pathPS = pathSplitter(path); - choosePath |= filenameCmp!cs(basePS.front, pathPS.front) != 0; - - basePS.popFront(); - pathPS.popFront(); - - import std.algorithm.comparison : mismatch; - import std.algorithm.iteration : joiner; - import std.array : array; - import std.range.primitives : walkLength; - import std.range : repeat, chain, choose; - import std.utf : byCodeUnit, byChar; - - // Remove matching prefix from basePS and pathPS - auto tup = mismatch!((a, b) => filenameCmp!cs(a, b) == 0)(basePS, pathPS); - basePS = tup[0]; - pathPS = tup[1]; - - string sep; - if (basePS.empty && pathPS.empty) - sep = "."; // if base == path, this is the return - else if (!basePS.empty && !pathPS.empty) - sep = dirSeparator; - - // Append as many "../" as necessary to reach common base from path - auto r1 = ".." - .byChar - .repeat(basePS.walkLength()) - .joiner(dirSeparator.byChar); - - auto r2 = pathPS - .joiner(dirSeparator.byChar) - .byChar; - - // Return (r1 ~ sep ~ r2) - return choose(choosePath, path.byCodeUnit, chain(r1, sep.byChar, r2)); -} - -/// -@safe unittest -{ - import std.array; - version (Posix) - { - assert(asRelativePath("foo", "/bar").array == "foo"); - assert(asRelativePath("/foo/bar", "/foo/bar").array == "."); - assert(asRelativePath("/foo/bar", "/foo/baz").array == "../bar"); - assert(asRelativePath("/foo/bar/baz", "/foo/woo/wee").array == "../../bar/baz"); - assert(asRelativePath("/foo/bar/baz", "/foo/bar").array == "baz"); - } - else version (Windows) - { - assert(asRelativePath("foo", `c:\bar`).array == "foo"); - assert(asRelativePath(`c:\foo\bar`, `c:\foo\bar`).array == "."); - assert(asRelativePath(`c:\foo\bar`, `c:\foo\baz`).array == `..\bar`); - assert(asRelativePath(`c:\foo\bar\baz`, `c:\foo\woo\wee`).array == `..\..\bar\baz`); - assert(asRelativePath(`c:/foo/bar/baz`, `c:\foo\woo\wee`).array == `..\..\bar\baz`); - assert(asRelativePath(`c:\foo\bar\baz`, `c:\foo\bar`).array == "baz"); - assert(asRelativePath(`c:\foo\bar`, `d:\foo`).array == `c:\foo\bar`); - assert(asRelativePath(`\\foo\bar`, `c:\foo`).array == `\\foo\bar`); - } - else - static assert(0); -} - -@safe unittest -{ - version (Posix) - { - assert(isBidirectionalRange!(typeof(asRelativePath("foo/bar/baz", "/foo/woo/wee")))); - } - - version (Windows) - { - assert(isBidirectionalRange!(typeof(asRelativePath(`c:\foo\bar`, `c:\foo\baz`)))); - } -} - -auto asRelativePath(CaseSensitive cs = CaseSensitive.osDefault, R1, R2) - (auto ref R1 path, auto ref R2 base) -if (isConvertibleToString!R1 || isConvertibleToString!R2) -{ - import std.meta : staticMap; - alias Types = staticMap!(convertToString, R1, R2); - return asRelativePath!(cs, Types)(path, base); -} - -@safe unittest -{ - import std.array; - version (Posix) - assert(asRelativePath(TestAliasedString("foo"), TestAliasedString("/bar")).array == "foo"); - else version (Windows) - assert(asRelativePath(TestAliasedString("foo"), TestAliasedString(`c:\bar`)).array == "foo"); - assert(asRelativePath(TestAliasedString("foo"), "bar").array == "foo"); - assert(asRelativePath("foo", TestAliasedString("bar")).array == "foo"); - assert(asRelativePath(TestAliasedString("foo"), TestAliasedString("bar")).array == "foo"); - import std.utf : byDchar; - assert(asRelativePath("foo"d.byDchar, TestAliasedString("bar")).array == "foo"); -} - -@safe unittest -{ - import std.array, std.utf : bCU=byCodeUnit; - version (Posix) - { - assert(asRelativePath("/foo/bar/baz".bCU, "/foo/bar".bCU).array == "baz"); - assert(asRelativePath("/foo/bar/baz"w.bCU, "/foo/bar"w.bCU).array == "baz"w); - assert(asRelativePath("/foo/bar/baz"d.bCU, "/foo/bar"d.bCU).array == "baz"d); - } - else version (Windows) - { - assert(asRelativePath(`\\foo\bar`.bCU, `c:\foo`.bCU).array == `\\foo\bar`); - assert(asRelativePath(`\\foo\bar`w.bCU, `c:\foo`w.bCU).array == `\\foo\bar`w); - assert(asRelativePath(`\\foo\bar`d.bCU, `c:\foo`d.bCU).array == `\\foo\bar`d); - } -} - -/** Compares filename characters. - - This function can perform a case-sensitive or a case-insensitive - comparison. This is controlled through the `cs` template parameter - which, if not specified, is given by $(LREF CaseSensitive)`.osDefault`. - - On Windows, the backslash and slash characters ($(D `\`) and $(D `/`)) - are considered equal. - - Params: - cs = Case-sensitivity of the comparison. - a = A filename character. - b = A filename character. - - Returns: - $(D < 0) if $(D a < b), - `0` if $(D a == b), and - $(D > 0) if $(D a > b). -*/ -int filenameCharCmp(CaseSensitive cs = CaseSensitive.osDefault)(dchar a, dchar b) - @safe pure nothrow -{ - if (isDirSeparator(a) && isDirSeparator(b)) return 0; - static if (!cs) - { - import std.uni : toLower; - a = toLower(a); - b = toLower(b); - } - return cast(int)(a - b); -} - -/// -@safe unittest -{ - assert(filenameCharCmp('a', 'a') == 0); - assert(filenameCharCmp('a', 'b') < 0); - assert(filenameCharCmp('b', 'a') > 0); - - version (linux) - { - // Same as calling filenameCharCmp!(CaseSensitive.yes)(a, b) - assert(filenameCharCmp('A', 'a') < 0); - assert(filenameCharCmp('a', 'A') > 0); - } - version (Windows) - { - // Same as calling filenameCharCmp!(CaseSensitive.no)(a, b) - assert(filenameCharCmp('a', 'A') == 0); - assert(filenameCharCmp('a', 'B') < 0); - assert(filenameCharCmp('A', 'b') < 0); - } -} - -@safe unittest -{ - assert(filenameCharCmp!(CaseSensitive.yes)('A', 'a') < 0); - assert(filenameCharCmp!(CaseSensitive.yes)('a', 'A') > 0); - - assert(filenameCharCmp!(CaseSensitive.no)('a', 'a') == 0); - assert(filenameCharCmp!(CaseSensitive.no)('a', 'b') < 0); - assert(filenameCharCmp!(CaseSensitive.no)('b', 'a') > 0); - assert(filenameCharCmp!(CaseSensitive.no)('A', 'a') == 0); - assert(filenameCharCmp!(CaseSensitive.no)('a', 'A') == 0); - assert(filenameCharCmp!(CaseSensitive.no)('a', 'B') < 0); - assert(filenameCharCmp!(CaseSensitive.no)('B', 'a') > 0); - assert(filenameCharCmp!(CaseSensitive.no)('A', 'b') < 0); - assert(filenameCharCmp!(CaseSensitive.no)('b', 'A') > 0); - - version (Posix) assert(filenameCharCmp('\\', '/') != 0); - version (Windows) assert(filenameCharCmp('\\', '/') == 0); -} - - -/** Compares file names and returns - - Individual characters are compared using `filenameCharCmp!cs`, - where `cs` is an optional template parameter determining whether - the comparison is case sensitive or not. - - Treatment of invalid UTF encodings is implementation defined. - - Params: - cs = case sensitivity - filename1 = range for first file name - filename2 = range for second file name - - Returns: - $(D < 0) if $(D filename1 < filename2), - `0` if $(D filename1 == filename2) and - $(D > 0) if $(D filename1 > filename2). - - See_Also: - $(LREF filenameCharCmp) -*/ -int filenameCmp(CaseSensitive cs = CaseSensitive.osDefault, Range1, Range2) - (Range1 filename1, Range2 filename2) -if (isSomeFiniteCharInputRange!Range1 && !isConvertibleToString!Range1 && - isSomeFiniteCharInputRange!Range2 && !isConvertibleToString!Range2) -{ - alias C1 = Unqual!(ElementEncodingType!Range1); - alias C2 = Unqual!(ElementEncodingType!Range2); - - static if (!cs && (C1.sizeof < 4 || C2.sizeof < 4) || - C1.sizeof != C2.sizeof) - { - // Case insensitive - decode so case is checkable - // Different char sizes - decode to bring to common type - import std.utf : byDchar; - return filenameCmp!cs(filename1.byDchar, filename2.byDchar); - } - else static if (isSomeString!Range1 && C1.sizeof < 4 || - isSomeString!Range2 && C2.sizeof < 4) - { - // Avoid autodecoding - import std.utf : byCodeUnit; - return filenameCmp!cs(filename1.byCodeUnit, filename2.byCodeUnit); - } - else - { - for (;;) - { - if (filename1.empty) return -(cast(int) !filename2.empty); - if (filename2.empty) return 1; - const c = filenameCharCmp!cs(filename1.front, filename2.front); - if (c != 0) return c; - filename1.popFront(); - filename2.popFront(); - } - } -} - -/// -@safe unittest -{ - assert(filenameCmp("abc", "abc") == 0); - assert(filenameCmp("abc", "abd") < 0); - assert(filenameCmp("abc", "abb") > 0); - assert(filenameCmp("abc", "abcd") < 0); - assert(filenameCmp("abcd", "abc") > 0); - - version (linux) - { - // Same as calling filenameCmp!(CaseSensitive.yes)(filename1, filename2) - assert(filenameCmp("Abc", "abc") < 0); - assert(filenameCmp("abc", "Abc") > 0); - } - version (Windows) - { - // Same as calling filenameCmp!(CaseSensitive.no)(filename1, filename2) - assert(filenameCmp("Abc", "abc") == 0); - assert(filenameCmp("abc", "Abc") == 0); - assert(filenameCmp("Abc", "abD") < 0); - assert(filenameCmp("abc", "AbB") > 0); - } -} - -int filenameCmp(CaseSensitive cs = CaseSensitive.osDefault, Range1, Range2) - (auto ref Range1 filename1, auto ref Range2 filename2) -if (isConvertibleToString!Range1 || isConvertibleToString!Range2) -{ - import std.meta : staticMap; - alias Types = staticMap!(convertToString, Range1, Range2); - return filenameCmp!(cs, Types)(filename1, filename2); -} - -@safe unittest -{ - assert(filenameCmp!(CaseSensitive.yes)(TestAliasedString("Abc"), "abc") < 0); - assert(filenameCmp!(CaseSensitive.yes)("Abc", TestAliasedString("abc")) < 0); - assert(filenameCmp!(CaseSensitive.yes)(TestAliasedString("Abc"), TestAliasedString("abc")) < 0); -} - -@safe unittest -{ - assert(filenameCmp!(CaseSensitive.yes)("Abc", "abc") < 0); - assert(filenameCmp!(CaseSensitive.yes)("abc", "Abc") > 0); - - assert(filenameCmp!(CaseSensitive.no)("abc", "abc") == 0); - assert(filenameCmp!(CaseSensitive.no)("abc", "abd") < 0); - assert(filenameCmp!(CaseSensitive.no)("abc", "abb") > 0); - assert(filenameCmp!(CaseSensitive.no)("abc", "abcd") < 0); - assert(filenameCmp!(CaseSensitive.no)("abcd", "abc") > 0); - assert(filenameCmp!(CaseSensitive.no)("Abc", "abc") == 0); - assert(filenameCmp!(CaseSensitive.no)("abc", "Abc") == 0); - assert(filenameCmp!(CaseSensitive.no)("Abc", "abD") < 0); - assert(filenameCmp!(CaseSensitive.no)("abc", "AbB") > 0); - - version (Posix) assert(filenameCmp(`abc\def`, `abc/def`) != 0); - version (Windows) assert(filenameCmp(`abc\def`, `abc/def`) == 0); -} - -/** Matches a pattern against a path. - - Some characters of pattern have a special meaning (they are - $(I meta-characters)) and can't be escaped. These are: - - $(BOOKTABLE, - $(TR $(TD `*`) - $(TD Matches 0 or more instances of any character.)) - $(TR $(TD `?`) - $(TD Matches exactly one instance of any character.)) - $(TR $(TD `[`$(I chars)`]`) - $(TD Matches one instance of any character that appears - between the brackets.)) - $(TR $(TD `[!`$(I chars)`]`) - $(TD Matches one instance of any character that does not - appear between the brackets after the exclamation mark.)) - $(TR $(TD `{`$(I string1)`,`$(I string2)`,`…`}`) - $(TD Matches either of the specified strings.)) - ) - - Individual characters are compared using `filenameCharCmp!cs`, - where `cs` is an optional template parameter determining whether - the comparison is case sensitive or not. See the - $(LREF filenameCharCmp) documentation for details. - - Note that directory - separators and dots don't stop a meta-character from matching - further portions of the path. - - Params: - cs = Whether the matching should be case-sensitive - path = The path to be matched against - pattern = The glob pattern - - Returns: - `true` if pattern matches path, `false` otherwise. - - See_also: - $(LINK2 http://en.wikipedia.org/wiki/Glob_%28programming%29,Wikipedia: _glob (programming)) - */ -bool globMatch(CaseSensitive cs = CaseSensitive.osDefault, C, Range) - (Range path, const(C)[] pattern) - @safe pure nothrow -if (isForwardRange!Range && !isInfinite!Range && - isSomeChar!(ElementEncodingType!Range) && !isConvertibleToString!Range && - isSomeChar!C && is(immutable C == immutable ElementEncodingType!Range)) -in -{ - // Verify that pattern[] is valid - import std.algorithm.searching : balancedParens; - import std.utf : byUTF; - - assert(balancedParens(pattern.byUTF!C, '[', ']', 0)); - assert(balancedParens(pattern.byUTF!C, '{', '}', 0)); -} -do -{ - alias RC = Unqual!(ElementEncodingType!Range); - - static if (RC.sizeof == 1 && isSomeString!Range) - { - import std.utf : byChar; - return globMatch!cs(path.byChar, pattern); - } - else static if (RC.sizeof == 2 && isSomeString!Range) - { - import std.utf : byWchar; - return globMatch!cs(path.byWchar, pattern); - } - else - { - C[] pattmp; - for (size_t pi = 0; pi < pattern.length; pi++) - { - const pc = pattern[pi]; - switch (pc) - { - case '*': - if (pi + 1 == pattern.length) - return true; - for (; !path.empty; path.popFront()) - { - auto p = path.save; - if (globMatch!(cs, C)(p, - pattern[pi + 1 .. pattern.length])) - return true; - } - return false; - - case '?': - if (path.empty) - return false; - path.popFront(); - break; - - case '[': - if (path.empty) - return false; - auto nc = path.front; - path.popFront(); - auto not = false; - ++pi; - if (pattern[pi] == '!') - { - not = true; - ++pi; - } - auto anymatch = false; - while (1) - { - const pc2 = pattern[pi]; - if (pc2 == ']') - break; - if (!anymatch && (filenameCharCmp!cs(nc, pc2) == 0)) - anymatch = true; - ++pi; - } - if (anymatch == not) - return false; - break; - - case '{': - // find end of {} section - auto piRemain = pi; - for (; piRemain < pattern.length - && pattern[piRemain] != '}'; ++piRemain) - { } - - if (piRemain < pattern.length) - ++piRemain; - ++pi; - - while (pi < pattern.length) - { - const pi0 = pi; - C pc3 = pattern[pi]; - // find end of current alternative - for (; pi < pattern.length && pc3 != '}' && pc3 != ','; ++pi) - { - pc3 = pattern[pi]; - } - - auto p = path.save; - if (pi0 == pi) - { - if (globMatch!(cs, C)(p, pattern[piRemain..$])) - { - return true; - } - ++pi; - } - else - { - /* Match for: - * pattern[pi0 .. pi-1] ~ pattern[piRemain..$] - */ - if (pattmp is null) - // Allocate this only once per function invocation. - // Should do it with malloc/free, but that would make it impure. - pattmp = new C[pattern.length]; - - const len1 = pi - 1 - pi0; - pattmp[0 .. len1] = pattern[pi0 .. pi - 1]; - - const len2 = pattern.length - piRemain; - pattmp[len1 .. len1 + len2] = pattern[piRemain .. $]; - - if (globMatch!(cs, C)(p, pattmp[0 .. len1 + len2])) - { - return true; - } - } - if (pc3 == '}') - { - break; - } - } - return false; - - default: - if (path.empty) - return false; - if (filenameCharCmp!cs(pc, path.front) != 0) - return false; - path.popFront(); - break; - } - } - return path.empty; - } -} - -/// -@safe unittest -{ - assert(globMatch("foo.bar", "*")); - assert(globMatch("foo.bar", "*.*")); - assert(globMatch(`foo/foo\bar`, "f*b*r")); - assert(globMatch("foo.bar", "f???bar")); - assert(globMatch("foo.bar", "[fg]???bar")); - assert(globMatch("foo.bar", "[!gh]*bar")); - assert(globMatch("bar.fooz", "bar.{foo,bif}z")); - assert(globMatch("bar.bifz", "bar.{foo,bif}z")); - - version (Windows) - { - // Same as calling globMatch!(CaseSensitive.no)(path, pattern) - assert(globMatch("foo", "Foo")); - assert(globMatch("Goo.bar", "[fg]???bar")); - } - version (linux) - { - // Same as calling globMatch!(CaseSensitive.yes)(path, pattern) - assert(!globMatch("foo", "Foo")); - assert(!globMatch("Goo.bar", "[fg]???bar")); - } -} - -bool globMatch(CaseSensitive cs = CaseSensitive.osDefault, C, Range) - (auto ref Range path, const(C)[] pattern) - @safe pure nothrow -if (isConvertibleToString!Range) -{ - return globMatch!(cs, C, StringTypeOf!Range)(path, pattern); -} - -@safe unittest -{ - assert(testAliasedString!globMatch("foo.bar", "*")); -} - -@safe unittest -{ - assert(globMatch!(CaseSensitive.no)("foo", "Foo")); - assert(!globMatch!(CaseSensitive.yes)("foo", "Foo")); - - assert(globMatch("foo", "*")); - assert(globMatch("foo.bar"w, "*"w)); - assert(globMatch("foo.bar"d, "*.*"d)); - assert(globMatch("foo.bar", "foo*")); - assert(globMatch("foo.bar"w, "f*bar"w)); - assert(globMatch("foo.bar"d, "f*b*r"d)); - assert(globMatch("foo.bar", "f???bar")); - assert(globMatch("foo.bar"w, "[fg]???bar"w)); - assert(globMatch("foo.bar"d, "[!gh]*bar"d)); - - assert(!globMatch("foo", "bar")); - assert(!globMatch("foo"w, "*.*"w)); - assert(!globMatch("foo.bar"d, "f*baz"d)); - assert(!globMatch("foo.bar", "f*b*x")); - assert(!globMatch("foo.bar", "[gh]???bar")); - assert(!globMatch("foo.bar"w, "[!fg]*bar"w)); - assert(!globMatch("foo.bar"d, "[fg]???baz"d)); - // https://issues.dlang.org/show_bug.cgi?id=6634 - assert(!globMatch("foo.di", "*.d")); // triggered bad assertion - - assert(globMatch("foo.bar", "{foo,bif}.bar")); - assert(globMatch("bif.bar"w, "{foo,bif}.bar"w)); - - assert(globMatch("bar.foo"d, "bar.{foo,bif}"d)); - assert(globMatch("bar.bif", "bar.{foo,bif}")); - - assert(globMatch("bar.fooz"w, "bar.{foo,bif}z"w)); - assert(globMatch("bar.bifz"d, "bar.{foo,bif}z"d)); - - assert(globMatch("bar.foo", "bar.{biz,,baz}foo")); - assert(globMatch("bar.foo"w, "bar.{biz,}foo"w)); - assert(globMatch("bar.foo"d, "bar.{,biz}foo"d)); - assert(globMatch("bar.foo", "bar.{}foo")); - - assert(globMatch("bar.foo"w, "bar.{ar,,fo}o"w)); - assert(globMatch("bar.foo"d, "bar.{,ar,fo}o"d)); - assert(globMatch("bar.o", "bar.{,ar,fo}o")); - - assert(!globMatch("foo", "foo?")); - assert(!globMatch("foo", "foo[]")); - assert(!globMatch("foo", "foob")); - assert(!globMatch("foo", "foo{b}")); - - - static assert(globMatch("foo.bar", "[!gh]*bar")); -} - - - - -/** Checks that the given file or directory name is valid. - - The maximum length of `filename` is given by the constant - `core.stdc.stdio.FILENAME_MAX`. (On Windows, this number is - defined as the maximum number of UTF-16 code points, and the - test will therefore only yield strictly correct results when - `filename` is a string of `wchar`s.) - - On Windows, the following criteria must be satisfied - ($(LINK2 http://msdn.microsoft.com/en-us/library/aa365247(v=vs.85).aspx,source)): - $(UL - $(LI `filename` must not contain any characters whose integer - representation is in the range 0-31.) - $(LI `filename` must not contain any of the following $(I reserved - characters): `<>:"/\|?*`) - $(LI `filename` may not end with a space ($(D ' ')) or a period - (`'.'`).) - ) - - On POSIX, `filename` may not contain a forward slash (`'/'`) or - the null character (`'\0'`). - - Params: - filename = string to check - - Returns: - `true` if and only if `filename` is not - empty, not too long, and does not contain invalid characters. - -*/ -bool isValidFilename(Range)(Range filename) -if ((isRandomAccessRange!Range && hasLength!Range && hasSlicing!Range && isSomeChar!(ElementEncodingType!Range) || - isNarrowString!Range) && - !isConvertibleToString!Range) -{ - import core.stdc.stdio : FILENAME_MAX; - if (filename.length == 0 || filename.length >= FILENAME_MAX) return false; - foreach (c; filename) - { - version (Windows) - { - switch (c) - { - case 0: - .. - case 31: - case '<': - case '>': - case ':': - case '"': - case '/': - case '\\': - case '|': - case '?': - case '*': - return false; - - default: - break; - } - } - else version (Posix) - { - if (c == 0 || c == '/') return false; - } - else static assert(0); - } - version (Windows) - { - auto last = filename[filename.length - 1]; - if (last == '.' || last == ' ') return false; - } - - // All criteria passed - return true; -} - -/// -@safe pure @nogc nothrow -unittest -{ - import std.utf : byCodeUnit; - - assert(isValidFilename("hello.exe".byCodeUnit)); -} - -bool isValidFilename(Range)(auto ref Range filename) -if (isConvertibleToString!Range) -{ - return isValidFilename!(StringTypeOf!Range)(filename); -} - -@safe unittest -{ - assert(testAliasedString!isValidFilename("hello.exe")); -} - -@safe pure -unittest -{ - import std.conv; - auto valid = ["foo"]; - auto invalid = ["", "foo\0bar", "foo/bar"]; - auto pfdep = [`foo\bar`, "*.txt"]; - version (Windows) invalid ~= pfdep; - else version (Posix) valid ~= pfdep; - else static assert(0); - - import std.meta : AliasSeq; - static foreach (T; AliasSeq!(char[], const(char)[], string, wchar[], - const(wchar)[], wstring, dchar[], const(dchar)[], dstring)) - { - foreach (fn; valid) - assert(isValidFilename(to!T(fn))); - foreach (fn; invalid) - assert(!isValidFilename(to!T(fn))); - } - - { - auto r = MockRange!(immutable(char))(`dir/file.d`); - assert(!isValidFilename(r)); - } - - static struct DirEntry { string s; alias s this; } - assert(isValidFilename(DirEntry("file.ext"))); - - version (Windows) - { - immutable string cases = "<>:\"/\\|?*"; - foreach (i; 0 .. 31 + cases.length) - { - char[3] buf; - buf[0] = 'a'; - buf[1] = i <= 31 ? cast(char) i : cases[i - 32]; - buf[2] = 'b'; - assert(!isValidFilename(buf[])); - } - } -} - - - -/** Checks whether `path` is a valid path. - - Generally, this function checks that `path` is not empty, and that - each component of the path either satisfies $(LREF isValidFilename) - or is equal to `"."` or `".."`. - - $(B It does $(I not) check whether the path points to an existing file - or directory; use $(REF exists, std,file) for this purpose.) - - On Windows, some special rules apply: - $(UL - $(LI If the second character of `path` is a colon (`':'`), - the first character is interpreted as a drive letter, and - must be in the range A-Z (case insensitive).) - $(LI If `path` is on the form $(D `\\$(I server)\$(I share)\...`) - (UNC path), $(LREF isValidFilename) is applied to $(I server) - and $(I share) as well.) - $(LI If `path` starts with $(D `\\?\`) (long UNC path), the - only requirement for the rest of the string is that it does - not contain the null character.) - $(LI If `path` starts with $(D `\\.\`) (Win32 device namespace) - this function returns `false`; such paths are beyond the scope - of this module.) - ) - - Params: - path = string or Range of characters to check - - Returns: - true if `path` is a valid path. -*/ -bool isValidPath(Range)(Range path) -if ((isRandomAccessRange!Range && hasLength!Range && hasSlicing!Range && isSomeChar!(ElementEncodingType!Range) || - isNarrowString!Range) && - !isConvertibleToString!Range) -{ - alias C = Unqual!(ElementEncodingType!Range); - - if (path.empty) return false; - - // Check whether component is "." or "..", or whether it satisfies - // isValidFilename. - bool isValidComponent(Range component) - { - assert(component.length > 0); - if (component[0] == '.') - { - if (component.length == 1) return true; - else if (component.length == 2 && component[1] == '.') return true; - } - return isValidFilename(component); - } - - if (path.length == 1) - return isDirSeparator(path[0]) || isValidComponent(path); - - Range remainder; - version (Windows) - { - if (isDirSeparator(path[0]) && isDirSeparator(path[1])) - { - // Some kind of UNC path - if (path.length < 5) - { - // All valid UNC paths must have at least 5 characters - return false; - } - else if (path[2] == '?') - { - // Long UNC path - if (!isDirSeparator(path[3])) return false; - foreach (c; path[4 .. $]) - { - if (c == '\0') return false; - } - return true; - } - else if (path[2] == '.') - { - // Win32 device namespace not supported - return false; - } - else - { - // Normal UNC path, i.e. \\server\share\... - size_t i = 2; - while (i < path.length && !isDirSeparator(path[i])) ++i; - if (i == path.length || !isValidFilename(path[2 .. i])) - return false; - ++i; // Skip a single dir separator - size_t j = i; - while (j < path.length && !isDirSeparator(path[j])) ++j; - if (!isValidFilename(path[i .. j])) return false; - remainder = path[j .. $]; - } - } - else if (isDriveSeparator(path[1])) - { - import std.ascii : isAlpha; - if (!isAlpha(path[0])) return false; - remainder = path[2 .. $]; - } - else - { - remainder = path; - } - } - else version (Posix) - { - remainder = path; - } - else static assert(0); - remainder = ltrimDirSeparators(remainder); - - // Check that each component satisfies isValidComponent. - while (!remainder.empty) - { - size_t i = 0; - while (i < remainder.length && !isDirSeparator(remainder[i])) ++i; - assert(i > 0); - if (!isValidComponent(remainder[0 .. i])) return false; - remainder = ltrimDirSeparators(remainder[i .. $]); - } - - // All criteria passed - return true; -} - -/// -@safe pure @nogc nothrow -unittest -{ - assert(isValidPath("/foo/bar")); - assert(!isValidPath("/foo\0/bar")); - assert(isValidPath("/")); - assert(isValidPath("a")); - - version (Windows) - { - assert(isValidPath(`c:\`)); - assert(isValidPath(`c:\foo`)); - assert(isValidPath(`c:\foo\.\bar\\\..\`)); - assert(!isValidPath(`!:\foo`)); - assert(!isValidPath(`c::\foo`)); - assert(!isValidPath(`c:\foo?`)); - assert(!isValidPath(`c:\foo.`)); - - assert(isValidPath(`\\server\share`)); - assert(isValidPath(`\\server\share\foo`)); - assert(isValidPath(`\\server\share\\foo`)); - assert(!isValidPath(`\\\server\share\foo`)); - assert(!isValidPath(`\\server\\share\foo`)); - assert(!isValidPath(`\\ser*er\share\foo`)); - assert(!isValidPath(`\\server\sha?e\foo`)); - assert(!isValidPath(`\\server\share\|oo`)); - - assert(isValidPath(`\\?\<>:"?*|/\..\.`)); - assert(!isValidPath("\\\\?\\foo\0bar")); - - assert(!isValidPath(`\\.\PhysicalDisk1`)); - assert(!isValidPath(`\\`)); - } - - import std.utf : byCodeUnit; - assert(isValidPath("/foo/bar".byCodeUnit)); -} - -bool isValidPath(Range)(auto ref Range path) -if (isConvertibleToString!Range) -{ - return isValidPath!(StringTypeOf!Range)(path); -} - -@safe unittest -{ - assert(testAliasedString!isValidPath("/foo/bar")); -} - -/** Performs tilde expansion in paths on POSIX systems. - On Windows, this function does nothing. - - There are two ways of using tilde expansion in a path. One - involves using the tilde alone or followed by a path separator. In - this case, the tilde will be expanded with the value of the - environment variable `HOME`. The second way is putting - a username after the tilde (i.e. `~john/Mail`). Here, - the username will be searched for in the user database - (i.e. `/etc/passwd` on Unix systems) and will expand to - whatever path is stored there. The username is considered the - string after the tilde ending at the first instance of a path - separator. - - Note that using the `~user` syntax may give different - values from just `~` if the environment variable doesn't - match the value stored in the user database. - - When the environment variable version is used, the path won't - be modified if the environment variable doesn't exist or it - is empty. When the database version is used, the path won't be - modified if the user doesn't exist in the database or there is - not enough memory to perform the query. - - This function performs several memory allocations. - - Params: - inputPath = The path name to expand. - - Returns: - `inputPath` with the tilde expanded, or just `inputPath` - if it could not be expanded. - For Windows, `expandTilde` merely returns its argument `inputPath`. - - Example: - ----- - void processFile(string path) - { - // Allow calling this function with paths such as ~/foo - auto fullPath = expandTilde(path); - ... - } - ----- -*/ -string expandTilde(return scope const string inputPath) @safe nothrow -{ - version (Posix) - { - import core.exception : onOutOfMemoryError; - import core.stdc.errno : errno, EBADF, ENOENT, EPERM, ERANGE, ESRCH; - import core.stdc.stdlib : malloc, free, realloc; - - /* Joins a path from a C string to the remainder of path. - - The last path separator from c_path is discarded. The result - is joined to path[char_pos .. length] if char_pos is smaller - than length, otherwise path is not appended to c_path. - */ - static string combineCPathWithDPath(char* c_path, string path, size_t char_pos) @trusted nothrow - { - import core.stdc.string : strlen; - import std.exception : assumeUnique; - - assert(c_path != null); - assert(path.length > 0); - assert(char_pos >= 0); - - // Search end of C string - size_t end = strlen(c_path); - - const cPathEndsWithDirSep = end && isDirSeparator(c_path[end - 1]); - - string cp; - if (char_pos < path.length) - { - // Remove trailing path separator, if any (with special care for root /) - if (cPathEndsWithDirSep && (end > 1 || isDirSeparator(path[char_pos]))) - end--; - - // Append something from path - cp = assumeUnique(c_path[0 .. end] ~ path[char_pos .. $]); - } - else - { - // Remove trailing path separator, if any (except for root /) - if (cPathEndsWithDirSep && end > 1) - end--; - - // Create our own copy, as lifetime of c_path is undocumented - cp = c_path[0 .. end].idup; - } - - return cp; - } - - // Replaces the tilde from path with the environment variable HOME. - static string expandFromEnvironment(string path) @safe nothrow - { - import core.stdc.stdlib : getenv; - - assert(path.length >= 1); - assert(path[0] == '~'); - - // Get HOME and use that to replace the tilde. - auto home = () @trusted { return getenv("HOME"); } (); - if (home == null) - return path; - - return combineCPathWithDPath(home, path, 1); - } - - // Replaces the tilde from path with the path from the user database. - static string expandFromDatabase(string path) @safe nothrow - { - // bionic doesn't really support this, as getpwnam_r - // isn't provided and getpwnam is basically just a stub - version (CRuntime_Bionic) - { - return path; - } - else - { - import core.sys.posix.pwd : passwd, getpwnam_r; - import std.string : indexOf; - - assert(path.length > 2 || (path.length == 2 && !isDirSeparator(path[1]))); - assert(path[0] == '~'); - - // Extract username, searching for path separator. - auto last_char = indexOf(path, dirSeparator[0]); - - size_t username_len = (last_char == -1) ? path.length : last_char; - char[] username = new char[username_len * char.sizeof]; - - if (last_char == -1) - { - username[0 .. username_len - 1] = path[1 .. $]; - last_char = path.length + 1; - } - else - { - username[0 .. username_len - 1] = path[1 .. last_char]; - } - username[username_len - 1] = 0; - - assert(last_char > 1); - - // Reserve C memory for the getpwnam_r() function. - version (StdUnittest) - uint extra_memory_size = 2; - else - uint extra_memory_size = 5 * 1024; - char[] extra_memory; - - passwd result; - loop: while (1) - { - extra_memory.length += extra_memory_size; - - // Obtain info from database. - passwd *verify; - errno = 0; - auto passResult = () @trusted { return getpwnam_r( - &username[0], - &result, - &extra_memory[0], - extra_memory.length, - &verify - ); } (); - if (passResult == 0) - { - // Succeeded if verify points at result - if (verify == () @trusted { return &result; } ()) - // username is found - path = combineCPathWithDPath(result.pw_dir, path, last_char); - break; - } - - switch (errno) - { - case ERANGE: - // On BSD and OSX, errno can be left at 0 instead of set to ERANGE - case 0: - break; - - case ENOENT: - case ESRCH: - case EBADF: - case EPERM: - // The given name or uid was not found. - break loop; - - default: - onOutOfMemoryError(); - } - - // extra_memory isn't large enough - import core.checkedint : mulu; - bool overflow; - extra_memory_size = mulu(extra_memory_size, 2, overflow); - if (overflow) assert(0); - } - return path; - } - } - - // Return early if there is no tilde in path. - if (inputPath.length < 1 || inputPath[0] != '~') - return inputPath; - - if (inputPath.length == 1 || isDirSeparator(inputPath[1])) - return expandFromEnvironment(inputPath); - else - return expandFromDatabase(inputPath); - } - else version (Windows) - { - // Put here real windows implementation. - return inputPath; - } - else - { - static assert(0); // Guard. Implement on other platforms. - } -} - -/// -@safe unittest -{ - version (Posix) - { - import std.process : environment; - - auto oldHome = environment["HOME"]; - scope(exit) environment["HOME"] = oldHome; - - environment["HOME"] = "dmd/test"; - assert(expandTilde("~/") == "dmd/test/"); - assert(expandTilde("~") == "dmd/test"); - } -} - -@safe unittest -{ - version (Posix) - { - static if (__traits(compiles, { import std.process : executeShell; })) - import std.process : executeShell; - - import std.process : environment; - import std.string : strip; - - // Retrieve the current home variable. - auto oldHome = environment.get("HOME"); - - // Testing when there is no environment variable. - environment.remove("HOME"); - assert(expandTilde("~/") == "~/"); - assert(expandTilde("~") == "~"); - - // Testing when an environment variable is set. - environment["HOME"] = "dmd/test"; - assert(expandTilde("~/") == "dmd/test/"); - assert(expandTilde("~") == "dmd/test"); - - // The same, but with a variable ending in a slash. - environment["HOME"] = "dmd/test/"; - assert(expandTilde("~/") == "dmd/test/"); - assert(expandTilde("~") == "dmd/test"); - - // The same, but with a variable set to root. - environment["HOME"] = "/"; - assert(expandTilde("~/") == "/"); - assert(expandTilde("~") == "/"); - - // Recover original HOME variable before continuing. - if (oldHome !is null) environment["HOME"] = oldHome; - else environment.remove("HOME"); - - static if (is(typeof(executeShell))) - { - immutable tildeUser = "~" ~ environment.get("USER"); - immutable path = executeShell("echo " ~ tildeUser).output.strip(); - immutable expTildeUser = expandTilde(tildeUser); - assert(expTildeUser == path, expTildeUser); - immutable expTildeUserSlash = expandTilde(tildeUser ~ "/"); - immutable pathSlash = path[$-1] == '/' ? path : path ~ "/"; - assert(expTildeUserSlash == pathSlash, expTildeUserSlash); - } - - assert(expandTilde("~Idontexist/hey") == "~Idontexist/hey"); - } -} - -@safe unittest -{ - version (Posix) - { - import std.process : environment; - - string testPath(scope const string source_path) { - return source_path.expandTilde; - } - - auto oldHome = environment["HOME"]; - scope(exit) environment["HOME"] = oldHome; - - environment["HOME"] = "dmd/test"; - assert(testPath("~/") == "dmd/test/"); - assert(testPath("~") == "dmd/test"); - } -} - - -version (StdUnittest) -{ -private: - /* Define a mock RandomAccessRange to use for unittesting. - */ - - struct MockRange(C) - { - this(C[] array) { this.array = array; } - const - { - @property size_t length() { return array.length; } - @property bool empty() { return array.length == 0; } - @property C front() { return array[0]; } - @property C back() { return array[$ - 1]; } - alias opDollar = length; - C opIndex(size_t i) { return array[i]; } - } - void popFront() { array = array[1 .. $]; } - void popBack() { array = array[0 .. $-1]; } - MockRange!C opSlice( size_t lwr, size_t upr) const - { - return MockRange!C(array[lwr .. upr]); - } - @property MockRange save() { return this; } - private: - C[] array; - } - - /* Define a mock BidirectionalRange to use for unittesting. - */ - - struct MockBiRange(C) - { - this(const(C)[] array) { this.array = array; } - const - { - @property bool empty() { return array.length == 0; } - @property C front() { return array[0]; } - @property C back() { return array[$ - 1]; } - @property size_t opDollar() { return array.length; } - } - void popFront() { array = array[1 .. $]; } - void popBack() { array = array[0 .. $-1]; } - @property MockBiRange save() { return this; } - private: - const(C)[] array; - } - -} - -@safe unittest -{ - static assert( isRandomAccessRange!(MockRange!(const(char))) ); - static assert( isBidirectionalRange!(MockBiRange!(const(char))) ); -} - -private template BaseOf(R) -{ - static if (isRandomAccessRange!R && isSomeChar!(ElementType!R)) - alias BaseOf = R; - else - alias BaseOf = StringTypeOf!R; -} diff --git a/phobos/std/process.d b/phobos/std/process.d deleted file mode 100644 index c53a5b1..0000000 --- a/phobos/std/process.d +++ /dev/null @@ -1,4576 +0,0 @@ -// Written in the D programming language. - -/** -Functions for starting and interacting with other processes, and for -working with the current process' execution environment. - -Process_handling: -$(UL $(LI - $(LREF spawnProcess) spawns a new process, optionally assigning it an - arbitrary set of standard input, output, and error streams. - The function returns immediately, leaving the child process to execute - in parallel with its parent. All other functions in this module that - spawn processes are built around `spawnProcess`.) -$(LI - $(LREF wait) makes the parent process wait for a child process to - terminate. In general one should always do this, to avoid - child processes becoming "zombies" when the parent process exits. - Scope guards are perfect for this – see the $(LREF spawnProcess) - documentation for examples. $(LREF tryWait) is similar to `wait`, - but does not block if the process has not yet terminated.) -$(LI - $(LREF pipeProcess) also spawns a child process which runs - in parallel with its parent. However, instead of taking - arbitrary streams, it automatically creates a set of - pipes that allow the parent to communicate with the child - through the child's standard input, output, and/or error streams. - This function corresponds roughly to C's `popen` function.) -$(LI - $(LREF execute) starts a new process and waits for it - to complete before returning. Additionally, it captures - the process' standard output and error streams and returns - the output of these as a string.) -$(LI - $(LREF spawnShell), $(LREF pipeShell) and $(LREF executeShell) work like - `spawnProcess`, `pipeProcess` and `execute`, respectively, - except that they take a single command string and run it through - the current user's default command interpreter. - `executeShell` corresponds roughly to C's `system` function.) -$(LI - $(LREF kill) attempts to terminate a running process.) -) - -The following table compactly summarises the different process creation -functions and how they relate to each other: -$(BOOKTABLE, - $(TR $(TH ) - $(TH Runs program directly) - $(TH Runs shell command)) - $(TR $(TD Low-level process creation) - $(TD $(LREF spawnProcess)) - $(TD $(LREF spawnShell))) - $(TR $(TD Automatic input/output redirection using pipes) - $(TD $(LREF pipeProcess)) - $(TD $(LREF pipeShell))) - $(TR $(TD Execute and wait for completion, collect output) - $(TD $(LREF execute)) - $(TD $(LREF executeShell))) -) - -Other_functionality: -$(UL -$(LI - $(LREF pipe) is used to create unidirectional pipes.) -$(LI - $(LREF environment) is an interface through which the current process' - environment variables can be read and manipulated.) -$(LI - $(LREF escapeShellCommand) and $(LREF escapeShellFileName) are useful - for constructing shell command lines in a portable way.) -) - -Authors: - $(LINK2 https://github.com/kyllingstad, Lars Tandle Kyllingstad), - $(LINK2 https://github.com/schveiguy, Steven Schveighoffer), - $(HTTP thecybershadow.net, Vladimir Panteleev) -Copyright: - Copyright (c) 2013, the authors. All rights reserved. -License: - $(HTTP www.boost.org/LICENSE_1_0.txt, Boost License 1.0). -Source: - $(PHOBOSSRC std/process.d) -Macros: - OBJECTREF=$(REF1 $0, object) - -Note: -Most of the functionality in this module is not available on iOS, tvOS -and watchOS. The only functions available on those platforms are: -$(LREF environment), $(LREF thisProcessID) and $(LREF thisThreadID). -*/ -module std.process; - -import core.thread : ThreadID; - -version (Posix) -{ - import core.sys.posix.sys.wait; - import core.sys.posix.unistd; -} -version (Windows) -{ - import core.stdc.stdio; - import core.sys.windows.winbase; - import core.sys.windows.winnt; - import std.utf; - import std.windows.syserror; -} - -import std.internal.cstring; -import std.range; -import std.stdio; - -version (OSX) - version = Darwin; -else version (iOS) -{ - version = Darwin; - version = iOSDerived; -} -else version (TVOS) -{ - version = Darwin; - version = iOSDerived; -} -else version (WatchOS) -{ - version = Darwin; - version = iOSDerived; -} - -// When the DMC runtime is used, we have to use some custom functions -// to convert between Windows file handles and FILE*s. -version (Win32) version (CRuntime_DigitalMars) version = DMC_RUNTIME; - - -// Some of the following should be moved to druntime. -private -{ - // Microsoft Visual C Runtime (MSVCRT) declarations. - version (Windows) - { - version (DMC_RUNTIME) { } else - { - import core.stdc.stdint; - enum - { - STDIN_FILENO = 0, - STDOUT_FILENO = 1, - STDERR_FILENO = 2, - } - } - } - - // POSIX API declarations. - version (Posix) - { - version (Darwin) - { - extern(C) char*** _NSGetEnviron() nothrow; - const(char**) getEnvironPtr() @trusted - { - return *_NSGetEnviron; - } - } - else - { - // Made available by the C runtime: - extern(C) extern __gshared const char** environ; - const(char**) getEnvironPtr() @trusted - { - return environ; - } - } - - @system unittest - { - import core.thread : Thread; - new Thread({assert(getEnvironPtr !is null);}).start(); - } - } -} // private - -// ============================================================================= -// Environment variable manipulation. -// ============================================================================= - -/** -Manipulates _environment variables using an associative-array-like -interface. - -This class contains only static methods, and cannot be instantiated. -See below for examples of use. -*/ -abstract final class environment -{ - static import core.sys.posix.stdlib; - import core.stdc.errno : errno, EINVAL; - -static: - /** - Retrieves the value of the environment variable with the given `name`. - --- - auto path = environment["PATH"]; - --- - - Throws: - $(OBJECTREF Exception) if the environment variable does not exist, - or $(REF UTFException, std,utf) if the variable contains invalid UTF-16 - characters (Windows only). - - See_also: - $(LREF environment.get), which doesn't throw on failure. - */ - string opIndex(scope const(char)[] name) @safe - { - import std.exception : enforce; - return get(name, null).enforce("Environment variable not found: "~name); - } - - /** - Retrieves the value of the environment variable with the given `name`, - or a default value if the variable doesn't exist. - - Unlike $(LREF environment.opIndex), this function never throws on Posix. - --- - auto sh = environment.get("SHELL", "/bin/sh"); - --- - This function is also useful in checking for the existence of an - environment variable. - --- - auto myVar = environment.get("MYVAR"); - if (myVar is null) - { - // Environment variable doesn't exist. - // Note that we have to use 'is' for the comparison, since - // myVar == null is also true if the variable exists but is - // empty. - } - --- - Params: - name = name of the environment variable to retrieve - defaultValue = default value to return if the environment variable doesn't exist. - - Returns: - the value of the environment variable if found, otherwise - `null` if the environment doesn't exist. - - Throws: - $(REF UTFException, std,utf) if the variable contains invalid UTF-16 - characters (Windows only). - */ - string get(scope const(char)[] name, string defaultValue = null) @safe - { - string value; - getImpl(name, (result) { value = result ? cachedToString(result) : defaultValue; }); - return value; - } - - /** - Assigns the given `value` to the environment variable with the given - `name`. - If `value` is null the variable is removed from environment. - - If the variable does not exist, it will be created. If it already exists, - it will be overwritten. - --- - environment["foo"] = "bar"; - --- - - Throws: - $(OBJECTREF Exception) if the environment variable could not be added - (e.g. if the name is invalid). - - Note: - On some platforms, modifying environment variables may not be allowed in - multi-threaded programs. See e.g. - $(LINK2 https://www.gnu.org/software/libc/manual/html_node/Environment-Access.html#Environment-Access, glibc). - */ - inout(char)[] opIndexAssign(return scope inout char[] value, scope const(char)[] name) @trusted - { - version (Posix) - { - import std.exception : enforce, errnoEnforce; - if (value is null) - { - remove(name); - return value; - } - if (core.sys.posix.stdlib.setenv(name.tempCString(), value.tempCString(), 1) != -1) - { - return value; - } - // The default errno error message is very uninformative - // in the most common case, so we handle it manually. - enforce(errno != EINVAL, - "Invalid environment variable name: '"~name~"'"); - errnoEnforce(false, - "Failed to add environment variable"); - assert(0); - } - else version (Windows) - { - import std.windows.syserror : wenforce; - wenforce( - SetEnvironmentVariableW(name.tempCStringW(), value.tempCStringW()), - ); - return value; - } - else static assert(0); - } - - /** - Removes the environment variable with the given `name`. - - If the variable isn't in the environment, this function returns - successfully without doing anything. - - Note: - On some platforms, modifying environment variables may not be allowed in - multi-threaded programs. See e.g. - $(LINK2 https://www.gnu.org/software/libc/manual/html_node/Environment-Access.html#Environment-Access, glibc). - */ - void remove(scope const(char)[] name) @trusted nothrow @nogc - { - version (Windows) SetEnvironmentVariableW(name.tempCStringW(), null); - else version (Posix) core.sys.posix.stdlib.unsetenv(name.tempCString()); - else static assert(0); - } - - /** - Identify whether a variable is defined in the environment. - - Because it doesn't return the value, this function is cheaper than `get`. - However, if you do need the value as well, you should just check the - return of `get` for `null` instead of using this function first. - - Example: - ------------- - // good usage - if ("MY_ENV_FLAG" in environment) - doSomething(); - - // bad usage - if ("MY_ENV_VAR" in environment) - doSomething(environment["MY_ENV_VAR"]); - - // do this instead - if (auto var = environment.get("MY_ENV_VAR")) - doSomething(var); - ------------- - */ - bool opBinaryRight(string op : "in")(scope const(char)[] name) @trusted - { - if (name is null) - return false; - version (Posix) - return core.sys.posix.stdlib.getenv(name.tempCString()) !is null; - else version (Windows) - { - SetLastError(NO_ERROR); - if (GetEnvironmentVariableW(name.tempCStringW, null, 0) > 0) - return true; - immutable err = GetLastError(); - if (err == NO_ERROR) - return true; // zero-length environment variable on Wine / XP - if (err == ERROR_ENVVAR_NOT_FOUND) - return false; - // Some other Windows error, throw. - throw new WindowsException(err); - } - else static assert(0); - } - - /** - Copies all environment variables into an associative array. - - Windows_specific: - While Windows environment variable names are case insensitive, D's - built-in associative arrays are not. This function will store all - variable names in uppercase (e.g. `PATH`). - - Throws: - $(OBJECTREF Exception) if the environment variables could not - be retrieved (Windows only). - */ - string[string] toAA() @trusted - { - import std.conv : to; - string[string] aa; - version (Posix) - { - auto environ = getEnvironPtr; - for (int i=0; environ[i] != null; ++i) - { - import std.string : indexOf; - - immutable varDef = to!string(environ[i]); - immutable eq = indexOf(varDef, '='); - assert(eq >= 0); - - immutable name = varDef[0 .. eq]; - immutable value = varDef[eq+1 .. $]; - - // In POSIX, environment variables may be defined more - // than once. This is a security issue, which we avoid - // by checking whether the key already exists in the array. - // For more info: - // http://www.dwheeler.com/secure-programs/Secure-Programs-HOWTO/environment-variables.html - if (name !in aa) aa[name] = value; - } - } - else version (Windows) - { - import std.exception : enforce; - import std.uni : toUpper; - auto envBlock = GetEnvironmentStringsW(); - enforce(envBlock, "Failed to retrieve environment variables."); - scope(exit) FreeEnvironmentStringsW(envBlock); - - for (int i=0; envBlock[i] != '\0'; ++i) - { - auto start = i; - while (envBlock[i] != '=') ++i; - immutable name = toUTF8(toUpper(envBlock[start .. i])); - - start = i+1; - while (envBlock[i] != '\0') ++i; - - // Ignore variables with empty names. These are used internally - // by Windows to keep track of each drive's individual current - // directory. - if (!name.length) - continue; - - // Just like in POSIX systems, environment variables may be - // defined more than once in an environment block on Windows, - // and it is just as much of a security issue there. Moreso, - // in fact, due to the case insensensitivity of variable names, - // which is not handled correctly by all programs. - auto val = toUTF8(envBlock[start .. i]); - if (name !in aa) aa[name] = val is null ? "" : val; - } - } - else static assert(0); - return aa; - } - -private: - version (Windows) alias OSChar = WCHAR; - else version (Posix) alias OSChar = char; - - // Retrieves the environment variable. Calls `sink` with a - // temporary buffer of OS characters, or `null` if the variable - // doesn't exist. - void getImpl(scope const(char)[] name, scope void delegate(const(OSChar)[]) @safe sink) @trusted - { - // fix issue https://issues.dlang.org/show_bug.cgi?id=24549 - if (name is null) - return sink(null); - - version (Windows) - { - // first we ask windows how long the environment variable is, - // then we try to read it in to a buffer of that length. Lots - // of error conditions because the windows API is nasty. - - import std.conv : to; - const namezTmp = name.tempCStringW(); - WCHAR[] buf; - - // clear error because GetEnvironmentVariable only says it sets it - // if the environment variable is missing, not on other errors. - SetLastError(NO_ERROR); - // len includes terminating null - immutable len = GetEnvironmentVariableW(namezTmp, null, 0); - if (len == 0) - { - immutable err = GetLastError(); - if (err == ERROR_ENVVAR_NOT_FOUND) - return sink(null); - if (err != NO_ERROR) // Some other Windows error, throw. - throw new WindowsException(err); - } - if (len <= 1) - return sink(""); - buf.length = len; - - while (true) - { - // lenRead is either the number of bytes read w/o null - if buf was long enough - or - // the number of bytes necessary *including* null if buf wasn't long enough - immutable lenRead = GetEnvironmentVariableW(namezTmp, buf.ptr, to!DWORD(buf.length)); - if (lenRead == 0) - { - immutable err = GetLastError(); - if (err == NO_ERROR) // sucessfully read a 0-length variable - return sink(""); - if (err == ERROR_ENVVAR_NOT_FOUND) // variable didn't exist - return sink(null); - // some other windows error - throw new WindowsException(err); - } - assert(lenRead != buf.length, "impossible according to msft docs"); - if (lenRead < buf.length) // the buffer was long enough - return sink(buf[0 .. lenRead]); - // resize and go around again, because the environment variable grew - buf.length = lenRead; - } - } - else version (Posix) - { - import core.stdc.string : strlen; - - const vz = core.sys.posix.stdlib.getenv(name.tempCString()); - if (vz == null) return sink(null); - return sink(vz[0 .. strlen(vz)]); - } - else static assert(0); - } - - string cachedToString(C)(scope const(C)[] v) @safe - { - import std.algorithm.comparison : equal; - - // Cache the last call's result. - static string lastResult; - if (v.empty) - { - // Return non-null array for blank result to distinguish from - // not-present result. - lastResult = ""; - } - else if (!v.equal(lastResult)) - { - import std.conv : to; - lastResult = v.to!string; - } - return lastResult; - } -} - -@safe unittest -{ - import std.exception : assertThrown; - // New variable - environment["std_process"] = "foo"; - assert(environment["std_process"] == "foo"); - assert("std_process" in environment); - - // Set variable again (also tests length 1 case) - environment["std_process"] = "b"; - assert(environment["std_process"] == "b"); - assert("std_process" in environment); - - // Remove variable - environment.remove("std_process"); - assert("std_process" !in environment); - - // Remove again, should succeed - environment.remove("std_process"); - assert("std_process" !in environment); - - // Throw on not found. - assertThrown(environment["std_process"]); - - // get() without default value - assert(environment.get("std_process") is null); - - // get() with default value - assert(environment.get("std_process", "baz") == "baz"); - - // get() on an empty (but present) value - environment["std_process"] = ""; - auto res = environment.get("std_process"); - assert(res !is null); - assert(res == ""); - assert("std_process" in environment); - - // Important to do the following round-trip after the previous test - // because it tests toAA with an empty var - - // Convert to associative array - auto aa = environment.toAA(); - assert(aa.length > 0); - foreach (n, v; aa) - { - // Wine has some bugs related to environment variables: - // - Wine allows the existence of an env. variable with the name - // "\0", but GetEnvironmentVariable refuses to retrieve it. - // As of 2.067 we filter these out anyway (see comment in toAA). - - assert(v == environment[n]); - } - - // ... and back again. - foreach (n, v; aa) - environment[n] = v; - - // Complete the roundtrip - auto aa2 = environment.toAA(); - import std.conv : text; - assert(aa == aa2, text(aa, " != ", aa2)); - assert("std_process" in environment); - - // Setting null must have the same effect as remove - environment["std_process"] = null; - assert("std_process" !in environment); -} - -// https://issues.dlang.org/show_bug.cgi?id=24549 -@safe unittest -{ - import std.exception : assertThrown; - assert(environment.get(null) is null); - assertThrown(environment[null]); - assert(!(null in environment)); -} - -// ============================================================================= -// Functions and classes for process management. -// ============================================================================= - -/** - * Returns the process ID of the current process, - * which is guaranteed to be unique on the system. - * - * Example: - * --- - * writefln("Current process ID: %d", thisProcessID); - * --- - */ -@property int thisProcessID() @trusted nothrow @nogc //TODO: @safe -{ - version (Windows) return GetCurrentProcessId(); - else version (Posix) return core.sys.posix.unistd.getpid(); -} - - -/** - * Returns the process ID of the current thread, - * which is guaranteed to be unique within the current process. - * - * Returns: - * A $(REF ThreadID, core,thread) value for the calling thread. - * - * Example: - * --- - * writefln("Current thread ID: %s", thisThreadID); - * --- - */ -@property ThreadID thisThreadID() @trusted nothrow @nogc //TODO: @safe -{ - version (Windows) - return GetCurrentThreadId(); - else - version (Posix) - { - import core.sys.posix.pthread : pthread_self; - return pthread_self(); - } -} - - -@system unittest -{ - int pidA, pidB; - ThreadID tidA, tidB; - pidA = thisProcessID; - tidA = thisThreadID; - - import core.thread; - auto t = new Thread({ - pidB = thisProcessID; - tidB = thisThreadID; - }); - t.start(); - t.join(); - - assert(pidA == pidB); - assert(tidA != tidB); -} - - -package(std) string uniqueTempPath() @safe -{ - import std.file : tempDir; - import std.path : buildPath; - import std.uuid : randomUUID; - // Path should contain spaces to test escaping whitespace - return buildPath(tempDir(), "std.process temporary file " ~ - randomUUID().toString()); -} - - -version (iOSDerived) {} -else: - -/** -Spawns a new process, optionally assigning it an arbitrary set of standard -input, output, and error streams. - -The function returns immediately, leaving the child process to execute -in parallel with its parent. It is recommended to always call $(LREF wait) -on the returned $(LREF Pid) unless the process was spawned with -`Config.detached` flag, as detailed in the documentation for `wait`. - -Command_line: -There are four overloads of this function. The first two take an array -of strings, `args`, which should contain the program name as the -zeroth element and any command-line arguments in subsequent elements. -The third and fourth versions are included for convenience, and may be -used when there are no command-line arguments. They take a single string, -`program`, which specifies the program name. - -Unless a directory is specified in `args[0]` or `program`, -`spawnProcess` will search for the program in a platform-dependent -manner. On POSIX systems, it will look for the executable in the -directories listed in the PATH environment variable, in the order -they are listed. On Windows, it will search for the executable in -the following sequence: -$(OL - $(LI The directory from which the application loaded.) - $(LI The current directory for the parent process.) - $(LI The 32-bit Windows system directory.) - $(LI The 16-bit Windows system directory.) - $(LI The Windows directory.) - $(LI The directories listed in the PATH environment variable.) -) ---- -// Run an executable called "prog" located in the current working -// directory: -auto pid = spawnProcess("./prog"); -scope(exit) wait(pid); -// We can do something else while the program runs. The scope guard -// ensures that the process is waited for at the end of the scope. -... - -// Run DMD on the file "myprog.d", specifying a few compiler switches: -auto dmdPid = spawnProcess(["dmd", "-O", "-release", "-inline", "myprog.d" ]); -if (wait(dmdPid) != 0) - writeln("Compilation failed!"); ---- - -Environment_variables: -By default, the child process inherits the environment of the parent -process, along with any additional variables specified in the `env` -parameter. If the same variable exists in both the parent's environment -and in `env`, the latter takes precedence. - -If the $(LREF Config.newEnv) flag is set in `config`, the child -process will $(I not) inherit the parent's environment. Its entire -environment will then be determined by `env`. ---- -wait(spawnProcess("myapp", ["foo" : "bar"], Config.newEnv)); ---- - -Standard_streams: -The optional arguments `stdin`, `stdout` and `stderr` may -be used to assign arbitrary $(REF File, std,stdio) objects as the standard -input, output and error streams, respectively, of the child process. The -former must be opened for reading, while the latter two must be opened for -writing. The default is for the child process to inherit the standard -streams of its parent. ---- -// Run DMD on the file myprog.d, logging any error messages to a -// file named errors.log. -auto logFile = File("errors.log", "w"); -auto pid = spawnProcess(["dmd", "myprog.d"], - std.stdio.stdin, - std.stdio.stdout, - logFile); -if (wait(pid) != 0) - writeln("Compilation failed. See errors.log for details."); ---- - -Note that if you pass a `File` object that is $(I not) -one of the standard input/output/error streams of the parent process, -that stream will by default be $(I closed) in the parent process when -this function returns. See the $(LREF Config) documentation below for -information about how to disable this behaviour. - -Beware of buffering issues when passing `File` objects to -`spawnProcess`. The child process will inherit the low-level raw -read/write offset associated with the underlying file descriptor, but -it will not be aware of any buffered data. In cases where this matters -(e.g. when a file should be aligned before being passed on to the -child process), it may be a good idea to use unbuffered streams, or at -least ensure all relevant buffers are flushed. - -Params: -args = An array which contains the program name as the zeroth element - and any command-line arguments in the following elements. -stdin = The standard input stream of the child process. - This can be any $(REF File, std,stdio) that is opened for reading. - By default the child process inherits the parent's input - stream. -stdout = The standard output stream of the child process. - This can be any $(REF File, std,stdio) that is opened for writing. - By default the child process inherits the parent's output stream. -stderr = The standard error stream of the child process. - This can be any $(REF File, std,stdio) that is opened for writing. - By default the child process inherits the parent's error stream. -env = Additional environment variables for the child process. -config = Flags that control process creation. See $(LREF Config) - for an overview of available flags. -workDir = The working directory for the new process. - By default the child process inherits the parent's working - directory. - -Returns: -A $(LREF Pid) object that corresponds to the spawned process. - -Throws: -$(LREF ProcessException) on failure to start the process.$(BR) -$(REF StdioException, std,stdio) on failure to pass one of the streams - to the child process (Windows only).$(BR) -$(REF RangeError, core,exception) if `args` is empty. -*/ -Pid spawnProcess(scope const(char[])[] args, - File stdin = std.stdio.stdin, - File stdout = std.stdio.stdout, - File stderr = std.stdio.stderr, - const string[string] env = null, - Config config = Config.none, - scope const char[] workDir = null) - @safe -{ - version (Windows) - { - const commandLine = escapeShellArguments(args); - const program = args.length ? args[0] : null; - return spawnProcessWin(commandLine, program, stdin, stdout, stderr, env, config, workDir); - } - else version (Posix) - { - return spawnProcessPosix(args, stdin, stdout, stderr, env, config, workDir); - } - else - static assert(0); -} - -/// ditto -Pid spawnProcess(scope const(char[])[] args, - const string[string] env, - Config config = Config.none, - scope const(char)[] workDir = null) - @trusted // TODO: Should be @safe -{ - return spawnProcess(args, - std.stdio.stdin, - std.stdio.stdout, - std.stdio.stderr, - env, - config, - workDir); -} - -/// ditto -Pid spawnProcess(scope const(char)[] program, - File stdin = std.stdio.stdin, - File stdout = std.stdio.stdout, - File stderr = std.stdio.stderr, - const string[string] env = null, - Config config = Config.none, - scope const(char)[] workDir = null) - @trusted -{ - return spawnProcess((&program)[0 .. 1], - stdin, stdout, stderr, env, config, workDir); -} - -/// ditto -Pid spawnProcess(scope const(char)[] program, - const string[string] env, - Config config = Config.none, - scope const(char)[] workDir = null) - @trusted -{ - return spawnProcess((&program)[0 .. 1], env, config, workDir); -} - -version (Posix) private enum InternalError : ubyte -{ - noerror, - exec, - chdir, - getrlimit, - doubleFork, - malloc, - preExec, -} - -/* -Implementation of spawnProcess() for POSIX. - -envz should be a zero-terminated array of zero-terminated strings -on the form "var=value". -*/ -version (Posix) -private Pid spawnProcessPosix(scope const(char[])[] args, - File stdin, - File stdout, - File stderr, - scope const string[string] env, - Config config, - scope const(char)[] workDir) - @trusted // TODO: Should be @safe -{ - import core.exception : RangeError; - import std.algorithm.searching : any; - import std.conv : text; - import std.path : isDirSeparator; - import std.string : toStringz; - - if (args.empty) throw new RangeError(); - const(char)[] name = args[0]; - if (!any!isDirSeparator(name)) - { - name = searchPathFor(name); - if (name is null) - throw new ProcessException(text("Executable file not found: ", args[0])); - } - - // Convert program name and arguments to C-style strings. - auto argz = new const(char)*[args.length+1]; - argz[0] = toStringz(name); - foreach (i; 1 .. args.length) argz[i] = toStringz(args[i]); - argz[$-1] = null; - - // Prepare environment. - auto envz = createEnv(env, !(config.flags & Config.Flags.newEnv)); - - // Open the working directory. - // We use open in the parent and fchdir in the child - // so that most errors (directory doesn't exist, not a directory) - // can be propagated as exceptions before forking. - int workDirFD = -1; - scope(exit) if (workDirFD >= 0) close(workDirFD); - if (workDir.length) - { - import core.sys.posix.fcntl : open, O_RDONLY, stat_t, fstat, S_ISDIR; - workDirFD = open(workDir.tempCString(), O_RDONLY); - if (workDirFD < 0) - throw ProcessException.newFromErrno("Failed to open working directory"); - stat_t s; - if (fstat(workDirFD, &s) < 0) - throw ProcessException.newFromErrno("Failed to stat working directory"); - if (!S_ISDIR(s.st_mode)) - throw new ProcessException("Not a directory: " ~ cast(string) workDir); - } - - static int getFD(ref File f) { return core.stdc.stdio.fileno(f.getFP()); } - - // Get the file descriptors of the streams. - // These could potentially be invalid, but that is OK. If so, later calls - // to dup2() and close() will just silently fail without causing any harm. - auto stdinFD = getFD(stdin); - auto stdoutFD = getFD(stdout); - auto stderrFD = getFD(stderr); - - // We don't have direct access to the errors that may happen in a child process. - // So we use this pipe to deliver them. - int[2] forkPipe; - if (core.sys.posix.unistd.pipe(forkPipe) == 0) - setCLOEXEC(forkPipe[1], true); - else - throw ProcessException.newFromErrno("Could not create pipe to check startup of child"); - scope(exit) close(forkPipe[0]); - - /* - To create detached process, we use double fork technique - but we don't have a direct access to the second fork pid from the caller side thus use a pipe. - We also can't reuse forkPipe for that purpose - because we can't predict the order in which pid and possible error will be written - since the first and the second forks will run in parallel. - */ - int[2] pidPipe; - if (config.flags & Config.Flags.detached) - { - if (core.sys.posix.unistd.pipe(pidPipe) != 0) - throw ProcessException.newFromErrno("Could not create pipe to get process pid"); - setCLOEXEC(pidPipe[1], true); - } - scope(exit) if (config.flags & Config.Flags.detached) close(pidPipe[0]); - - static void abortOnError(int forkPipeOut, InternalError errorType, int error) nothrow - { - core.sys.posix.unistd.write(forkPipeOut, &errorType, errorType.sizeof); - core.sys.posix.unistd.write(forkPipeOut, &error, error.sizeof); - close(forkPipeOut); - core.sys.posix.unistd._exit(1); - assert(0); - } - - void closePipeWriteEnds() - { - close(forkPipe[1]); - if (config.flags & Config.Flags.detached) - close(pidPipe[1]); - } - - auto id = core.sys.posix.unistd.fork(); - if (id < 0) - { - closePipeWriteEnds(); - throw ProcessException.newFromErrno("Failed to spawn new process"); - } - - void forkChild() nothrow @nogc - { - static import core.sys.posix.stdio; - - // Child process - - // no need for the read end of pipe on child side - if (config.flags & Config.Flags.detached) - close(pidPipe[0]); - close(forkPipe[0]); - immutable forkPipeOut = forkPipe[1]; - immutable pidPipeOut = pidPipe[1]; - - // Set the working directory. - if (workDirFD >= 0) - { - if (fchdir(workDirFD) < 0) - { - // Fail. It is dangerous to run a program - // in an unexpected working directory. - abortOnError(forkPipeOut, InternalError.chdir, .errno); - } - close(workDirFD); - } - - void execProcess() - { - // Redirect streams and close the old file descriptors. - // In the case that stderr is redirected to stdout, we need - // to backup the file descriptor since stdout may be redirected - // as well. - if (stderrFD == STDOUT_FILENO) stderrFD = dup(stderrFD); - dup2(stdinFD, STDIN_FILENO); - dup2(stdoutFD, STDOUT_FILENO); - dup2(stderrFD, STDERR_FILENO); - - // Ensure that the standard streams aren't closed on execute, and - // optionally close all other file descriptors. - setCLOEXEC(STDIN_FILENO, false); - setCLOEXEC(STDOUT_FILENO, false); - setCLOEXEC(STDERR_FILENO, false); - - if (!(config.flags & Config.Flags.inheritFDs)) - { - // NOTE: malloc() and getrlimit() are not on the POSIX async - // signal safe functions list, but practically this should - // not be a problem. Java VM and CPython also use malloc() - // in its own implementation via opendir(). - import core.stdc.stdlib : malloc; - import core.sys.posix.poll : pollfd, poll, POLLNVAL; - import core.sys.posix.sys.resource : rlimit, getrlimit, RLIMIT_NOFILE; - - // Get the maximum number of file descriptors that could be open. - rlimit r; - if (getrlimit(RLIMIT_NOFILE, &r) != 0) - { - abortOnError(forkPipeOut, InternalError.getrlimit, .errno); - } - immutable maxDescriptors = cast(int) r.rlim_cur; - - // The above, less stdin, stdout, and stderr - immutable maxToClose = maxDescriptors - 3; - - // Call poll() to see which ones are actually open: - auto pfds = cast(pollfd*) malloc(pollfd.sizeof * maxToClose); - if (pfds is null) - { - abortOnError(forkPipeOut, InternalError.malloc, .errno); - } - foreach (i; 0 .. maxToClose) - { - pfds[i].fd = i + 3; - pfds[i].events = 0; - pfds[i].revents = 0; - } - if (poll(pfds, maxToClose, 0) >= 0) - { - foreach (i; 0 .. maxToClose) - { - // don't close pipe write end - if (pfds[i].fd == forkPipeOut) continue; - // POLLNVAL will be set if the file descriptor is invalid. - if (!(pfds[i].revents & POLLNVAL)) close(pfds[i].fd); - } - } - else - { - // Fall back to closing everything. - foreach (i; 3 .. maxDescriptors) - { - if (i == forkPipeOut) continue; - close(i); - } - } - } - else // This is already done if we don't inherit descriptors. - { - // Close the old file descriptors, unless they are - // either of the standard streams. - if (stdinFD > STDERR_FILENO) close(stdinFD); - if (stdoutFD > STDERR_FILENO) close(stdoutFD); - if (stderrFD > STDERR_FILENO) close(stderrFD); - } - - if (config.preExecFunction !is null) - { - if (config.preExecFunction() != true) - { - abortOnError(forkPipeOut, InternalError.preExec, .errno); - } - } - - if (config.preExecDelegate !is null) - { - if (config.preExecDelegate() != true) - { - abortOnError(forkPipeOut, InternalError.preExec, .errno); - } - } - - // Execute program. - core.sys.posix.unistd.execve(argz[0], argz.ptr, envz); - - // If execution fails, exit as quickly as possible. - abortOnError(forkPipeOut, InternalError.exec, .errno); - } - - if (config.flags & Config.Flags.detached) - { - auto secondFork = core.sys.posix.unistd.fork(); - if (secondFork == 0) - { - close(pidPipeOut); - execProcess(); - } - else if (secondFork == -1) - { - auto secondForkErrno = .errno; - close(pidPipeOut); - abortOnError(forkPipeOut, InternalError.doubleFork, secondForkErrno); - } - else - { - core.sys.posix.unistd.write(pidPipeOut, &secondFork, pid_t.sizeof); - close(pidPipeOut); - close(forkPipeOut); - _exit(0); - } - } - else - { - execProcess(); - } - } - - if (id == 0) - { - forkChild(); - assert(0); - } - else - { - closePipeWriteEnds(); - auto status = InternalError.noerror; - auto readExecResult = core.sys.posix.unistd.read(forkPipe[0], &status, status.sizeof); - // Save error number just in case if subsequent "waitpid" fails and overrides errno - immutable lastError = .errno; - - if (config.flags & Config.Flags.detached) - { - // Forked child exits right after creating second fork. So it should be safe to wait here. - import core.sys.posix.sys.wait : waitpid; - int waitResult; - waitpid(id, &waitResult, 0); - } - - if (readExecResult == -1) - throw ProcessException.newFromErrno(lastError, "Could not read from pipe to get child status"); - - bool owned = true; - if (status != InternalError.noerror) - { - int error; - readExecResult = read(forkPipe[0], &error, error.sizeof); - string errorMsg; - final switch (status) - { - case InternalError.chdir: - errorMsg = "Failed to set working directory"; - break; - case InternalError.getrlimit: - errorMsg = "getrlimit failed"; - break; - case InternalError.exec: - errorMsg = "Failed to execute '" ~ cast(string) name ~ "'"; - break; - case InternalError.doubleFork: - // Can happen only when starting detached process - assert(config.flags & Config.Flags.detached); - errorMsg = "Failed to fork twice"; - break; - case InternalError.malloc: - errorMsg = "Failed to allocate memory"; - break; - case InternalError.preExec: - errorMsg = "Failed to execute preExecFunction or preExecDelegate"; - break; - case InternalError.noerror: - assert(false); - } - if (readExecResult == error.sizeof) - throw ProcessException.newFromErrno(error, errorMsg); - throw new ProcessException(errorMsg); - } - else if (config.flags & Config.Flags.detached) - { - owned = false; - if (read(pidPipe[0], &id, id.sizeof) != id.sizeof) - throw ProcessException.newFromErrno("Could not read from pipe to get detached process id"); - } - - // Parent process: Close streams and return. - if (!(config.flags & Config.Flags.retainStdin ) && stdinFD > STDERR_FILENO - && stdinFD != getFD(std.stdio.stdin )) - stdin.close(); - if (!(config.flags & Config.Flags.retainStdout) && stdoutFD > STDERR_FILENO - && stdoutFD != getFD(std.stdio.stdout)) - stdout.close(); - if (!(config.flags & Config.Flags.retainStderr) && stderrFD > STDERR_FILENO - && stderrFD != getFD(std.stdio.stderr)) - stderr.close(); - return new Pid(id, owned); - } -} - -version (Posix) -@system unittest -{ - import std.concurrency : ownerTid, receiveTimeout, send, spawn; - import std.datetime : seconds; - - sigset_t ss; - sigemptyset(&ss); - sigaddset(&ss, SIGINT); - pthread_sigmask(SIG_BLOCK, &ss, null); - - Config config = { - preExecFunction: () @trusted @nogc nothrow { - // Reset signal handlers - sigset_t ss; - if (sigfillset(&ss) != 0) - { - return false; - } - if (sigprocmask(SIG_UNBLOCK, &ss, null) != 0) - { - return false; - } - return true; - }, - }; - - auto pid = spawnProcess(["sleep", "10000"], - std.stdio.stdin, - std.stdio.stdout, - std.stdio.stderr, - null, - config, - null); - scope(failure) - { - kill(pid, SIGKILL); - wait(pid); - } - - // kill the spawned process with SIGINT - // and send its return code - spawn((shared Pid pid) { - auto p = cast() pid; - kill(p, SIGINT); - auto code = wait(p); - assert(code < 0); - send(ownerTid, code); - }, cast(shared) pid); - - auto received = receiveTimeout(3.seconds, (int) {}); - assert(received); -} - -version (Posix) -@system unittest -{ - __gshared int j; - foreach (i; 0 .. 3) - { - auto config = Config( - preExecFunction: function() @trusted { - j = 1; - return true; - }, - preExecDelegate: delegate() @trusted { - // j should now be 1, as preExecFunction is called before - // preExecDelegate is. - _Exit(i + j); - return true; - }, - ); - auto pid = spawnProcess(["false"], config: config); - assert(wait(pid) == i + 1); - } -} - -/* -Implementation of spawnProcess() for Windows. - -commandLine must contain the entire command line, properly -quoted/escaped as required by CreateProcessW(). - -envz must be a pointer to a block of UTF-16 characters on the form -"var1=value1\0var2=value2\0...varN=valueN\0\0". -*/ -version (Windows) -private Pid spawnProcessWin(scope const(char)[] commandLine, - scope const(char)[] program, - File stdin, - File stdout, - File stderr, - scope const string[string] env, - Config config, - scope const(char)[] workDir) - @trusted -{ - import core.exception : RangeError; - import std.conv : text; - - if (commandLine.empty) throw new RangeError("Command line is empty"); - - // Prepare environment. - auto envz = createEnv(env, !(config.flags & Config.Flags.newEnv)); - - // Startup info for CreateProcessW(). - STARTUPINFO_W startinfo; - startinfo.cb = startinfo.sizeof; - static int getFD(ref File f) { return f.isOpen ? f.fileno : -1; } - - // Extract file descriptors and HANDLEs from the streams and make the - // handles inheritable. - static void prepareStream(ref File file, DWORD stdHandle, string which, - out int fileDescriptor, out HANDLE handle) - { - enum _NO_CONSOLE_FILENO = cast(HANDLE)-2; - fileDescriptor = getFD(file); - handle = null; - if (fileDescriptor >= 0) - handle = file.windowsHandle; - // Windows GUI applications have a fd but not a valid Windows HANDLE. - if (handle is null || handle == INVALID_HANDLE_VALUE || handle == _NO_CONSOLE_FILENO) - handle = GetStdHandle(stdHandle); - - DWORD dwFlags; - if (GetHandleInformation(handle, &dwFlags)) - { - if (!(dwFlags & HANDLE_FLAG_INHERIT)) - { - if (!SetHandleInformation(handle, - HANDLE_FLAG_INHERIT, - HANDLE_FLAG_INHERIT)) - { - throw new StdioException( - "Failed to make "~which~" stream inheritable by child process (" - ~generateSysErrorMsg() ~ ')', - 0); - } - } - } - } - int stdinFD = -1, stdoutFD = -1, stderrFD = -1; - prepareStream(stdin, STD_INPUT_HANDLE, "stdin" , stdinFD, startinfo.hStdInput ); - prepareStream(stdout, STD_OUTPUT_HANDLE, "stdout", stdoutFD, startinfo.hStdOutput); - prepareStream(stderr, STD_ERROR_HANDLE, "stderr", stderrFD, startinfo.hStdError ); - - if ((startinfo.hStdInput != null && startinfo.hStdInput != INVALID_HANDLE_VALUE) - || (startinfo.hStdOutput != null && startinfo.hStdOutput != INVALID_HANDLE_VALUE) - || (startinfo.hStdError != null && startinfo.hStdError != INVALID_HANDLE_VALUE)) - startinfo.dwFlags = STARTF_USESTDHANDLES; - - // Create process. - PROCESS_INFORMATION pi; - DWORD dwCreationFlags = - CREATE_UNICODE_ENVIRONMENT | - ((config.flags & Config.Flags.suppressConsole) ? CREATE_NO_WINDOW : 0); - // workaround until https://issues.dlang.org/show_bug.cgi?id=14696 is fixed - auto pworkDir = workDir.tempCStringW(); - if (!CreateProcessW(null, commandLine.tempCStringW().buffPtr, - null, null, true, dwCreationFlags, - envz, workDir.length ? pworkDir : null, &startinfo, &pi)) - throw ProcessException.newFromLastError("Failed to spawn process \"" ~ cast(string) program ~ '"'); - - // figure out if we should close any of the streams - if (!(config.flags & Config.Flags.retainStdin ) && stdinFD > STDERR_FILENO - && stdinFD != getFD(std.stdio.stdin )) - stdin.close(); - if (!(config.flags & Config.Flags.retainStdout) && stdoutFD > STDERR_FILENO - && stdoutFD != getFD(std.stdio.stdout)) - stdout.close(); - if (!(config.flags & Config.Flags.retainStderr) && stderrFD > STDERR_FILENO - && stderrFD != getFD(std.stdio.stderr)) - stderr.close(); - - // close the thread handle in the process info structure - CloseHandle(pi.hThread); - if (config.flags & Config.Flags.detached) - { - CloseHandle(pi.hProcess); - return new Pid(pi.dwProcessId); - } - return new Pid(pi.dwProcessId, pi.hProcess); -} - -// Converts childEnv to a zero-terminated array of zero-terminated strings -// on the form "name=value", optionally adding those of the current process' -// environment strings that are not present in childEnv. If the parent's -// environment should be inherited without modification, this function -// returns environ directly. -version (Posix) -private const(char*)* createEnv(const string[string] childEnv, - bool mergeWithParentEnv) -{ - // Determine the number of strings in the parent's environment. - int parentEnvLength = 0; - auto environ = getEnvironPtr; - if (mergeWithParentEnv) - { - if (childEnv.length == 0) return environ; - while (environ[parentEnvLength] != null) ++parentEnvLength; - } - - // Convert the "new" variables to C-style strings. - auto envz = new const(char)*[parentEnvLength + childEnv.length + 1]; - int pos = 0; - foreach (var, val; childEnv) - envz[pos++] = (var~'='~val~'\0').ptr; - - // Add the parent's environment. - foreach (environStr; environ[0 .. parentEnvLength]) - { - int eqPos = 0; - while (environStr[eqPos] != '=' && environStr[eqPos] != '\0') ++eqPos; - if (environStr[eqPos] != '=') continue; - auto var = environStr[0 .. eqPos]; - if (var in childEnv) continue; - envz[pos++] = environStr; - } - envz[pos] = null; - return envz.ptr; -} - -version (Posix) @system unittest -{ - auto e1 = createEnv(null, false); - assert(e1 != null && *e1 == null); - - auto e2 = createEnv(null, true); - assert(e2 != null); - int i = 0; - auto environ = getEnvironPtr; - for (; environ[i] != null; ++i) - { - assert(e2[i] != null); - import core.stdc.string : strcmp; - assert(strcmp(e2[i], environ[i]) == 0); - } - assert(e2[i] == null); - - auto e3 = createEnv(["foo" : "bar", "hello" : "world"], false); - assert(e3 != null && e3[0] != null && e3[1] != null && e3[2] == null); - assert((e3[0][0 .. 8] == "foo=bar\0" && e3[1][0 .. 12] == "hello=world\0") - || (e3[0][0 .. 12] == "hello=world\0" && e3[1][0 .. 8] == "foo=bar\0")); -} - - -// Converts childEnv to a Windows environment block, which is on the form -// "name1=value1\0name2=value2\0...nameN=valueN\0\0", optionally adding -// those of the current process' environment strings that are not present -// in childEnv. Returns null if the parent's environment should be -// inherited without modification, as this is what is expected by -// CreateProcess(). -version (Windows) -private LPVOID createEnv(const string[string] childEnv, - bool mergeWithParentEnv) -{ - if (mergeWithParentEnv && childEnv.length == 0) return null; - import std.array : appender; - import std.uni : toUpper; - auto envz = appender!(wchar[])(); - void put(string var, string val) - { - envz.put(var); - envz.put('='); - envz.put(val); - envz.put(cast(wchar) '\0'); - } - - // Add the variables in childEnv, removing them from parentEnv - // if they exist there too. - auto parentEnv = mergeWithParentEnv ? environment.toAA() : null; - foreach (k, v; childEnv) - { - auto uk = toUpper(k); - put(uk, v); - if (uk in parentEnv) parentEnv.remove(uk); - } - - // Add remaining parent environment variables. - foreach (k, v; parentEnv) put(k, v); - - // Two final zeros are needed in case there aren't any environment vars, - // and the last one does no harm when there are. - envz.put("\0\0"w); - return envz.data.ptr; -} - -version (Windows) @system unittest -{ - assert(createEnv(null, true) == null); - assert((cast(wchar*) createEnv(null, false))[0 .. 2] == "\0\0"w); - auto e1 = (cast(wchar*) createEnv(["foo":"bar", "ab":"c"], false))[0 .. 14]; - assert(e1 == "FOO=bar\0AB=c\0\0"w || e1 == "AB=c\0FOO=bar\0\0"w); -} - -// Searches the PATH variable for the given executable file, -// (checking that it is in fact executable). -version (Posix) -package(std) string searchPathFor(scope const(char)[] executable) - @safe -{ - import std.algorithm.iteration : splitter; - import std.conv : to; - import std.path : chainPath; - - typeof(return) result; - - environment.getImpl("PATH", - (scope const(char)[] path) - { - if (!path) - return; - - foreach (dir; splitter(path, ":")) - { - auto execPath = chainPath(dir, executable); - if (isExecutable(execPath)) - { - result = execPath.to!(typeof(result)); - return; - } - } - }); - - return result; -} - -// Checks whether the file exists and can be executed by the -// current user. -version (Posix) -private bool isExecutable(R)(R path) @trusted nothrow @nogc -if (isSomeFiniteCharInputRange!R) -{ - return (access(path.tempCString(), X_OK) == 0); -} - -version (Posix) @safe unittest -{ - import std.algorithm; - auto lsPath = searchPathFor("ls"); - assert(!lsPath.empty); - assert(lsPath[0] == '/'); - assert(lsPath.endsWith("ls")); - auto unlikely = searchPathFor("lkmqwpoialhggyaofijadsohufoiqezm"); - assert(unlikely is null, "Are you kidding me?"); -} - -// Sets or unsets the FD_CLOEXEC flag on the given file descriptor. -version (Posix) -private void setCLOEXEC(int fd, bool on) nothrow @nogc -{ - import core.sys.posix.fcntl : fcntl, F_GETFD, FD_CLOEXEC, F_SETFD; - auto flags = fcntl(fd, F_GETFD); - if (flags >= 0) - { - if (on) flags |= FD_CLOEXEC; - else flags &= ~(cast(typeof(flags)) FD_CLOEXEC); - flags = fcntl(fd, F_SETFD, flags); - } - assert(flags != -1 || .errno == EBADF); -} - -@system unittest // Command line arguments in spawnProcess(). -{ - version (Windows) TestScript prog = - "if not [%~1]==[foo] ( exit 1 ) - if not [%~2]==[bar] ( exit 2 ) - exit 0"; - else version (Posix) TestScript prog = - `if test "$1" != "foo"; then exit 1; fi - if test "$2" != "bar"; then exit 2; fi - exit 0`; - assert(wait(spawnProcess(prog.path)) == 1); - assert(wait(spawnProcess([prog.path])) == 1); - assert(wait(spawnProcess([prog.path, "foo"])) == 2); - assert(wait(spawnProcess([prog.path, "foo", "baz"])) == 2); - assert(wait(spawnProcess([prog.path, "foo", "bar"])) == 0); -} - -// test that file descriptors are correctly closed / left open. -// ideally this would be done by the child process making libc -// calls, but we make do... -version (Posix) @system unittest -{ - import core.stdc.errno : errno; - import core.sys.posix.fcntl : open, O_RDONLY; - import core.sys.posix.unistd : close; - import std.algorithm.searching : canFind, findSplitBefore; - import std.array : split; - import std.conv : to; - static import std.file; - import std.functional : reverseArgs; - import std.path : buildPath; - - auto directory = uniqueTempPath(); - std.file.mkdir(directory); - scope(exit) std.file.rmdirRecurse(directory); - auto path = buildPath(directory, "tmp"); - std.file.write(path, null); - errno = 0; - auto fd = open(path.tempCString, O_RDONLY); - if (fd == -1) - { - import core.stdc.string : strerror; - import std.stdio : stderr; - import std.string : fromStringz; - - // For the CI logs - stderr.writefln("%s: could not open '%s': %s", - __FUNCTION__, path, strerror(errno).fromStringz); - // TODO: should we retry here instead? - return; - } - scope(exit) close(fd); - - // command >&2 (or any other number) checks whethether that number - // file descriptor is open. - // Can't use this for arbitrary descriptors as many shells only support - // single digit fds. - TestScript testDefaults = `command >&0 && command >&1 && command >&2`; - assert(execute(testDefaults.path).status == 0); - assert(execute(testDefaults.path, null, Config.inheritFDs).status == 0); - - // Try a few different methods to check whether there are any - // incorrectly-open files. - void testFDs() - { - // try /proc//fd/ on linux - version (linux) - { - TestScript proc = "ls /proc/$$/fd"; - auto procRes = execute(proc.path, null); - if (procRes.status == 0) - { - auto fdStr = fd.to!string; - assert(!procRes.output.split.canFind(fdStr)); - assert(execute(proc.path, null, Config.inheritFDs) - .output.split.canFind(fdStr)); - return; - } - } - - // try fuser (might sometimes need permissions) - TestScript fuser = "echo $$ && fuser -f " ~ path; - auto fuserRes = execute(fuser.path, null); - if (fuserRes.status == 0) - { - assert(!reverseArgs!canFind(fuserRes - .output.findSplitBefore("\n").expand)); - assert(reverseArgs!canFind(execute(fuser.path, null, Config.inheritFDs) - .output.findSplitBefore("\n").expand)); - return; - } - - // last resort, try lsof (not available on all Posix) - TestScript lsof = "lsof -p$$"; - auto lsofRes = execute(lsof.path, null); - if (lsofRes.status == 0) - { - assert(!lsofRes.output.canFind(path)); - auto lsofOut = execute(lsof.path, null, Config.inheritFDs).output; - if (!lsofOut.canFind(path)) - { - std.stdio.stderr.writeln(__FILE__, ':', __LINE__, - ": Warning: unexpected lsof output:", lsofOut); - } - return; - } - - std.stdio.stderr.writeln(__FILE__, ':', __LINE__, - ": Warning: Couldn't find any way to check open files"); - } - testFDs(); -} - -@system unittest // Environment variables in spawnProcess(). -{ - // We really should use set /a on Windows, but Wine doesn't support it. - version (Windows) TestScript envProg = - `if [%STD_PROCESS_UNITTEST1%] == [1] ( - if [%STD_PROCESS_UNITTEST2%] == [2] (exit 3) - exit 1 - ) - if [%STD_PROCESS_UNITTEST1%] == [4] ( - if [%STD_PROCESS_UNITTEST2%] == [2] (exit 6) - exit 4 - ) - if [%STD_PROCESS_UNITTEST2%] == [2] (exit 2) - exit 0`; - version (Posix) TestScript envProg = - `if test "$std_process_unittest1" = ""; then - std_process_unittest1=0 - fi - if test "$std_process_unittest2" = ""; then - std_process_unittest2=0 - fi - exit $(($std_process_unittest1+$std_process_unittest2))`; - - environment.remove("std_process_unittest1"); // Just in case. - environment.remove("std_process_unittest2"); - assert(wait(spawnProcess(envProg.path)) == 0); - assert(wait(spawnProcess(envProg.path, null, Config.newEnv)) == 0); - - environment["std_process_unittest1"] = "1"; - assert(wait(spawnProcess(envProg.path)) == 1); - assert(wait(spawnProcess(envProg.path, null, Config.newEnv)) == 0); - - auto env = ["std_process_unittest2" : "2"]; - assert(wait(spawnProcess(envProg.path, env)) == 3); - assert(wait(spawnProcess(envProg.path, env, Config.newEnv)) == 2); - - env["std_process_unittest1"] = "4"; - assert(wait(spawnProcess(envProg.path, env)) == 6); - assert(wait(spawnProcess(envProg.path, env, Config.newEnv)) == 6); - - environment.remove("std_process_unittest1"); - assert(wait(spawnProcess(envProg.path, env)) == 6); - assert(wait(spawnProcess(envProg.path, env, Config.newEnv)) == 6); -} - -@system unittest // Stream redirection in spawnProcess(). -{ - import std.path : buildPath; - import std.string; - version (Windows) TestScript prog = - "set /p INPUT= - echo %INPUT% output %~1 - echo %INPUT% error %~2 1>&2 - echo done > %3"; - else version (Posix) TestScript prog = - "read INPUT - echo $INPUT output $1 - echo $INPUT error $2 >&2 - echo done > \"$3\""; - - // Pipes - void testPipes(Config config) - { - import std.file : tempDir, exists, remove; - import std.uuid : randomUUID; - import std.exception : collectException; - auto pipei = pipe(); - auto pipeo = pipe(); - auto pipee = pipe(); - auto done = buildPath(tempDir(), randomUUID().toString()); - auto pid = spawnProcess([prog.path, "foo", "bar", done], - pipei.readEnd, pipeo.writeEnd, pipee.writeEnd, null, config); - pipei.writeEnd.writeln("input"); - pipei.writeEnd.flush(); - assert(pipeo.readEnd.readln().chomp() == "input output foo"); - assert(pipee.readEnd.readln().chomp().stripRight() == "input error bar"); - if (config.flags & Config.Flags.detached) - while (!done.exists) Thread.sleep(10.msecs); - else - wait(pid); - while (remove(done).collectException) Thread.sleep(10.msecs); - } - - // Files - void testFiles(Config config) - { - import std.ascii : newline; - import std.file : tempDir, exists, remove, readText, write; - import std.uuid : randomUUID; - import std.exception : collectException; - auto pathi = buildPath(tempDir(), randomUUID().toString()); - auto patho = buildPath(tempDir(), randomUUID().toString()); - auto pathe = buildPath(tempDir(), randomUUID().toString()); - write(pathi, "INPUT" ~ newline); - auto filei = File(pathi, "r"); - auto fileo = File(patho, "w"); - auto filee = File(pathe, "w"); - auto done = buildPath(tempDir(), randomUUID().toString()); - auto pid = spawnProcess([prog.path, "bar", "baz", done], filei, fileo, filee, null, config); - if (config.flags & Config.Flags.detached) - while (!done.exists) Thread.sleep(10.msecs); - else - wait(pid); - assert(readText(patho).chomp() == "INPUT output bar"); - assert(readText(pathe).chomp().stripRight() == "INPUT error baz"); - while (remove(pathi).collectException) Thread.sleep(10.msecs); - while (remove(patho).collectException) Thread.sleep(10.msecs); - while (remove(pathe).collectException) Thread.sleep(10.msecs); - while (remove(done).collectException) Thread.sleep(10.msecs); - } - - testPipes(Config.none); - testFiles(Config.none); - testPipes(Config.detached); - testFiles(Config.detached); -} - -@system unittest // Error handling in spawnProcess() -{ - import std.algorithm.searching : canFind; - import std.exception : assertThrown, collectExceptionMsg; - - static void testNotFoundException(string program) - { - assert(collectExceptionMsg!ProcessException(spawnProcess(program)).canFind(program)); - assert(collectExceptionMsg!ProcessException(spawnProcess(program, null, Config.detached)).canFind(program)); - } - testNotFoundException("ewrgiuhrifuheiohnmnvqweoijwf"); - testNotFoundException("./rgiuhrifuheiohnmnvqweoijwf"); - - // can't execute malformed file with executable permissions - version (Posix) - { - import std.path : buildPath; - import std.file : remove, write, setAttributes, tempDir; - import core.sys.posix.sys.stat : S_IRUSR, S_IWUSR, S_IXUSR, S_IRGRP, S_IXGRP, S_IROTH, S_IXOTH; - import std.conv : to; - string deleteme = buildPath(tempDir(), "deleteme.std.process.unittest.pid") ~ to!string(thisProcessID); - write(deleteme, ""); - scope(exit) remove(deleteme); - setAttributes(deleteme, S_IRUSR|S_IWUSR|S_IXUSR|S_IRGRP|S_IXGRP|S_IROTH|S_IXOTH); - assertThrown!ProcessException(spawnProcess(deleteme)); - assertThrown!ProcessException(spawnProcess(deleteme, null, Config.detached)); - } -} - -@system unittest // Specifying a working directory. -{ - import std.path; - import std.file; - TestScript prog = "echo foo>bar"; - - auto directory = uniqueTempPath(); - mkdir(directory); - scope(exit) rmdirRecurse(directory); - - auto pid = spawnProcess([prog.path], null, Config.none, directory); - wait(pid); - assert(exists(buildPath(directory, "bar"))); -} - -@system unittest // Specifying a bad working directory. -{ - import std.exception : assertThrown; - import std.file; - TestScript prog = "echo"; - - auto directory = uniqueTempPath(); - assertThrown!ProcessException(spawnProcess([prog.path], null, Config.none, directory)); - assertThrown!ProcessException(spawnProcess([prog.path], null, Config.detached, directory)); - - std.file.write(directory, "foo"); - scope(exit) remove(directory); - assertThrown!ProcessException(spawnProcess([prog.path], null, Config.none, directory)); - assertThrown!ProcessException(spawnProcess([prog.path], null, Config.detached, directory)); - - // can't run in directory if user does not have search permission on this directory - version (Posix) - { - if (core.sys.posix.unistd.getuid() != 0) - { - import core.sys.posix.sys.stat : S_IRUSR; - auto directoryNoSearch = uniqueTempPath(); - mkdir(directoryNoSearch); - scope(exit) rmdirRecurse(directoryNoSearch); - setAttributes(directoryNoSearch, S_IRUSR); - assertThrown!ProcessException(spawnProcess(prog.path, null, Config.none, directoryNoSearch)); - assertThrown!ProcessException(spawnProcess(prog.path, null, Config.detached, directoryNoSearch)); - } - } -} - -@system unittest // Specifying empty working directory. -{ - TestScript prog = ""; - - string directory = ""; - assert(directory.ptr && !directory.length); - spawnProcess([prog.path], null, Config.none, directory).wait(); -} - -// Reopening the standard streams (https://issues.dlang.org/show_bug.cgi?id=13258) -@system unittest -{ - import std.string; - import std.file; - void fun() - { - spawnShell("echo foo").wait(); - spawnShell("echo bar").wait(); - } - - auto tmpFile = uniqueTempPath(); - scope(exit) if (exists(tmpFile)) remove(tmpFile); - - { - auto oldOut = std.stdio.stdout; - scope(exit) std.stdio.stdout = oldOut; - - std.stdio.stdout = File(tmpFile, "w"); - fun(); - std.stdio.stdout.close(); - } - - auto lines = readText(tmpFile).splitLines(); - assert(lines == ["foo", "bar"]); -} - -// MSVCRT workaround (https://issues.dlang.org/show_bug.cgi?id=14422) -version (Windows) -@system unittest -{ - auto fn = uniqueTempPath(); - scope(exit) if (exists(fn)) remove(fn); - std.file.write(fn, "AAAAAAAAAA"); - - auto f = File(fn, "a"); - spawnProcess(["cmd", "/c", "echo BBBBB"], std.stdio.stdin, f).wait(); - - auto data = readText(fn); - assert(data == "AAAAAAAAAABBBBB\r\n", data); -} - -// https://issues.dlang.org/show_bug.cgi?id=20765 -// Test that running processes with relative path works in conjunction -// with indicating a workDir. -version (Posix) @system unittest -{ - import std.file : mkdir, write, setAttributes, rmdirRecurse; - import std.conv : octal; - - auto dir = uniqueTempPath(); - mkdir(dir); - scope(exit) rmdirRecurse(dir); - write(dir ~ "/program", "#!/bin/sh\necho Hello"); - setAttributes(dir ~ "/program", octal!700); - - assert(execute(["./program"], null, Config.none, size_t.max, dir).output == "Hello\n"); -} - -/** -A variation on $(LREF spawnProcess) that runs the given _command through -the current user's preferred _command interpreter (aka. shell). - -The string `command` is passed verbatim to the shell, and is therefore -subject to its rules about _command structure, argument/filename quoting -and escaping of special characters. -The path to the shell executable defaults to $(LREF nativeShell). - -In all other respects this function works just like `spawnProcess`. -Please refer to the $(LREF spawnProcess) documentation for descriptions -of the other function parameters, the return value and any exceptions -that may be thrown. ---- -// Run the command/program "foo" on the file named "my file.txt", and -// redirect its output into foo.log. -auto pid = spawnShell(`foo "my file.txt" > foo.log`); -wait(pid); ---- - -See_also: -$(LREF escapeShellCommand), which may be helpful in constructing a -properly quoted and escaped shell _command line for the current platform. -*/ -Pid spawnShell(scope const(char)[] command, - File stdin = std.stdio.stdin, - File stdout = std.stdio.stdout, - File stderr = std.stdio.stderr, - scope const string[string] env = null, - Config config = Config.none, - scope const(char)[] workDir = null, - scope string shellPath = nativeShell) - @trusted // See reason below -{ - version (Windows) - { - // CMD does not parse its arguments like other programs. - // It does not use CommandLineToArgvW. - // Instead, it treats the first and last quote specially. - // See CMD.EXE /? for details. - const commandLine = escapeShellFileName(shellPath) - ~ ` ` ~ shellSwitch ~ ` "` ~ command ~ `"`; - return spawnProcessWin(commandLine, shellPath, stdin, stdout, stderr, env, config, workDir); - } - else version (Posix) - { - const(char)[][3] args; - args[0] = shellPath; - args[1] = shellSwitch; - args[2] = command; - /* The passing of args converts the static array, which is initialized with `scope` pointers, - * to a dynamic array, which is also a scope parameter. So, it is a scope pointer to a - * scope pointer, which although is safely used here, D doesn't allow transitive scope. - * See https://github.com/dlang/dmd/pull/10951 - */ - return spawnProcessPosix(args, stdin, stdout, stderr, env, config, workDir); - } - else - static assert(0); -} - -/// ditto -Pid spawnShell(scope const(char)[] command, - scope const string[string] env, - Config config = Config.none, - scope const(char)[] workDir = null, - scope string shellPath = nativeShell) - @trusted // TODO: Should be @safe -{ - return spawnShell(command, - std.stdio.stdin, - std.stdio.stdout, - std.stdio.stderr, - env, - config, - workDir, - shellPath); -} - -@system unittest -{ - version (Windows) - auto cmd = "echo %FOO%"; - else version (Posix) - auto cmd = "echo $foo"; - import std.file; - auto tmpFile = uniqueTempPath(); - scope(exit) if (exists(tmpFile)) remove(tmpFile); - auto redir = "> \""~tmpFile~'"'; - auto env = ["foo" : "bar"]; - assert(wait(spawnShell(cmd~redir, env)) == 0); - auto f = File(tmpFile, "a"); - version (CRuntime_Microsoft) f.seek(0, SEEK_END); // MSVCRT probably seeks to the end when writing, not before - assert(wait(spawnShell(cmd, std.stdio.stdin, f, std.stdio.stderr, env)) == 0); - f.close(); - auto output = std.file.readText(tmpFile); - assert(output == "bar\nbar\n" || output == "bar\r\nbar\r\n"); -} - -version (Windows) -@system unittest -{ - import std.string; - import std.conv : text; - TestScript prog = "echo %0 %*"; - auto outputFn = uniqueTempPath(); - scope(exit) if (exists(outputFn)) remove(outputFn); - auto args = [`a b c`, `a\b\c\`, `a"b"c"`]; - auto result = executeShell( - escapeShellCommand([prog.path] ~ args) - ~ " > " ~ - escapeShellFileName(outputFn)); - assert(result.status == 0); - auto args2 = outputFn.readText().strip().parseCommandLine()[1..$]; - assert(args == args2, text(args2)); -} - - -/** -Options that control the behaviour of process creation functions in this -module. Most options only apply to $(LREF spawnProcess) and -$(LREF spawnShell). - -Example: ---- -auto logFile = File("myapp_error.log", "w"); - -// Start program, suppressing the console window (Windows only), -// redirect its error stream to logFile, and leave logFile open -// in the parent process as well. -auto pid = spawnProcess("myapp", stdin, stdout, logFile, - Config.retainStderr | Config.suppressConsole); -scope(exit) -{ - auto exitCode = wait(pid); - logFile.writeln("myapp exited with code ", exitCode); - logFile.close(); -} ---- -*/ -struct Config -{ - /** - Flag options. - Use bitwise OR to combine flags. - **/ - enum Flags - { - none = 0, - - /** - By default, the child process inherits the parent's environment, - and any environment variables passed to $(LREF spawnProcess) will - be added to it. If this flag is set, the only variables in the - child process' environment will be those given to spawnProcess. - */ - newEnv = 1, - - /** - Unless the child process inherits the standard input/output/error - streams of its parent, one almost always wants the streams closed - in the parent when $(LREF spawnProcess) returns. Therefore, by - default, this is done. If this is not desirable, pass any of these - options to spawnProcess. - */ - retainStdin = 2, - retainStdout = 4, /// ditto - retainStderr = 8, /// ditto - - /** - On Windows, if the child process is a console application, this - flag will prevent the creation of a console window. Otherwise, - it will be ignored. On POSIX, `suppressConsole` has no effect. - */ - suppressConsole = 16, - - /** - On POSIX, open $(LINK2 http://en.wikipedia.org/wiki/File_descriptor,file descriptors) - are by default inherited by the child process. As this may lead - to subtle bugs when pipes or multiple threads are involved, - $(LREF spawnProcess) ensures that all file descriptors except the - ones that correspond to standard input/output/error are closed - in the child process when it starts. Use `inheritFDs` to prevent - this. - - On Windows, this option has no effect, and any handles which have been - explicitly marked as inheritable will always be inherited by the child - process. - */ - inheritFDs = 32, - - /** - Spawn process in detached state. This removes the need in calling - $(LREF wait) to clean up the process resources. - - Note: - Calling $(LREF wait) or $(LREF kill) with the resulting `Pid` is invalid. - */ - detached = 64, - - /** - By default, the $(LREF execute) and $(LREF executeShell) functions - will capture child processes' both stdout and stderr. This can be - undesirable if the standard output is to be processed or otherwise - used by the invoking program, as `execute`'s result would then - contain a mix of output and warning/error messages. - - Specify this flag when calling `execute` or `executeShell` to - cause invoked processes' stderr stream to be sent to $(REF stderr, - std,stdio), and only capture and return standard output. - - This flag has no effect on $(LREF spawnProcess) or $(LREF spawnShell). - */ - stderrPassThrough = 128, - } - Flags flags; /// ditto - - /** - For backwards compatibility, and cases when only flags need to - be specified in the `Config`, these allow building `Config` - instances using flag names only. - */ - enum Config none = Config.init; - enum Config newEnv = Config(Flags.newEnv); /// ditto - enum Config retainStdin = Config(Flags.retainStdin); /// ditto - enum Config retainStdout = Config(Flags.retainStdout); /// ditto - enum Config retainStderr = Config(Flags.retainStderr); /// ditto - enum Config suppressConsole = Config(Flags.suppressConsole); /// ditto - enum Config inheritFDs = Config(Flags.inheritFDs); /// ditto - enum Config detached = Config(Flags.detached); /// ditto - enum Config stderrPassThrough = Config(Flags.stderrPassThrough); /// ditto - Config opUnary(string op)() - if (is(typeof(mixin(op ~ q{flags})))) - { - return Config(mixin(op ~ q{flags})); - } /// ditto - Config opBinary(string op)(Config other) - if (is(typeof(mixin(q{flags} ~ op ~ q{other.flags})))) - { - return Config(mixin(q{flags} ~ op ~ q{other.flags})); - } /// ditto - Config opOpAssign(string op)(Config other) - if (is(typeof(mixin(q{flags} ~ op ~ q{=other.flags})))) - { - return Config(mixin(q{flags} ~ op ~ q{=other.flags})); - } /// ditto - - version (StdDdoc) - { - /** - A function that is called before `exec` in $(LREF spawnProcess). - It returns `true` if succeeded and otherwise returns `false`. - - $(RED Warning: - Please note that the code in this function must only use - async-signal-safe functions.) - - If $(LREF preExecDelegate) is also set, it is called last. - - On Windows, this member is not available. - */ - bool function() nothrow @nogc @safe preExecFunction; - - /** - A delegate that is called before `exec` in $(LREF spawnProcess). - It returns `true` if succeeded and otherwise returns `false`. - - $(RED Warning: - Please note that the code in this function must only use - async-signal-safe functions.) - - If $(LREF preExecFunction) is also set, it is called first. - - On Windows, this member is not available. - */ - bool delegate() nothrow @nogc @safe preExecDelegate; - } - else version (Posix) - { - bool function() nothrow @nogc @safe preExecFunction; - bool delegate() nothrow @nogc @safe preExecDelegate; - } -} - -// https://issues.dlang.org/show_bug.cgi?id=22125 -@safe unittest -{ - Config c = Config.retainStdin; - c |= Config.retainStdout; - c |= Config.retainStderr; - c &= ~Config.retainStderr; - assert(c == (Config.retainStdin | Config.retainStdout)); -} - -/// A handle that corresponds to a spawned process. -final class Pid -{ - /** - The process ID number. - - This is a number that uniquely identifies the process on the operating - system, for at least as long as the process is running. Once $(LREF wait) - has been called on the $(LREF Pid), this method will return an - invalid (negative) process ID. - */ - @property int processID() const @safe pure nothrow - { - return _processID; - } - - /** - An operating system handle to the process. - - This handle is used to specify the process in OS-specific APIs. - On POSIX, this function returns a `core.sys.posix.sys.types.pid_t` - with the same value as $(LREF Pid.processID), while on Windows it returns - a `core.sys.windows.windows.HANDLE`. - - Once $(LREF wait) has been called on the $(LREF Pid), this method - will return an invalid handle. - */ - // Note: Since HANDLE is a reference, this function cannot be const. - version (Windows) - @property HANDLE osHandle() @nogc @safe pure nothrow - { - return _handle; - } - else version (Posix) - @property pid_t osHandle() @nogc @safe pure nothrow - { - return _processID; - } - -private: - /* - Pid.performWait() does the dirty work for wait() and nonBlockingWait(). - - If block == true, this function blocks until the process terminates, - sets _processID to terminated, and returns the exit code or terminating - signal as described in the wait() documentation. - - If block == false, this function returns immediately, regardless - of the status of the process. If the process has terminated, the - function has the exact same effect as the blocking version. If not, - it returns 0 and does not modify _processID. - */ - version (Posix) - int performWait(bool block) @trusted - { - import std.exception : enforce; - enforce!ProcessException(owned, "Can't wait on a detached process"); - if (_processID == terminated) return _exitCode; - int exitCode; - while (true) - { - int status; - auto check = waitpid(_processID, &status, block ? 0 : WNOHANG); - if (check == -1) - { - if (errno == ECHILD) - { - throw new ProcessException( - "Process does not exist or is not a child process."); - } - else - { - // waitpid() was interrupted by a signal. We simply - // restart it. - assert(errno == EINTR); - continue; - } - } - if (!block && check == 0) return 0; - if (WIFEXITED(status)) - { - exitCode = WEXITSTATUS(status); - break; - } - else if (WIFSIGNALED(status)) - { - exitCode = -WTERMSIG(status); - break; - } - // We check again whether the call should be blocking, - // since we don't care about other status changes besides - // "exited" and "terminated by signal". - if (!block) return 0; - - // Process has stopped, but not terminated, so we continue waiting. - } - // Mark Pid as terminated, and cache and return exit code. - _processID = terminated; - _exitCode = exitCode; - return exitCode; - } - else version (Windows) - { - int performWait(const bool block, const DWORD timeout = INFINITE) @trusted - { - import std.exception : enforce; - enforce!ProcessException(owned, "Can't wait on a detached process"); - if (_processID == terminated) return _exitCode; - assert(_handle != INVALID_HANDLE_VALUE); - if (block) - { - auto result = WaitForSingleObject(_handle, timeout); - if (result != WAIT_OBJECT_0) - { - // Wait time exceeded `timeout` milliseconds? - if (result == WAIT_TIMEOUT && timeout != INFINITE) - return 0; - - throw ProcessException.newFromLastError("Wait failed."); - } - } - if (!GetExitCodeProcess(_handle, cast(LPDWORD)&_exitCode)) - throw ProcessException.newFromLastError(); - if (!block && _exitCode == STILL_ACTIVE) return 0; - CloseHandle(_handle); - _handle = INVALID_HANDLE_VALUE; - _processID = terminated; - return _exitCode; - } - - int performWait(Duration timeout) @safe - { - import std.exception : enforce; - const msecs = timeout.total!"msecs"; - - // Limit this implementation the maximum wait time offered by - // WaitForSingleObject. One could theoretically break up larger - // durations into multiple waits but (DWORD.max - 1).msecs - // (> 7 weeks, 17 hours) should be enough for the usual case. - // DWORD.max is reserved for INFINITE - enforce!ProcessException(msecs < DWORD.max, "Timeout exceeds maximum wait time!"); - return performWait(true, cast(DWORD) msecs); - } - - ~this() - { - if (_handle != INVALID_HANDLE_VALUE) - { - CloseHandle(_handle); - _handle = INVALID_HANDLE_VALUE; - } - } - } - - // Special values for _processID. - enum invalid = -1, terminated = -2; - - // OS process ID number. Only nonnegative IDs correspond to - // running processes. - int _processID = invalid; - - // Exit code cached by wait(). This is only expected to hold a - // sensible value if _processID == terminated. - int _exitCode; - - // Whether the process can be waited for by wait() for or killed by kill(). - // False if process was started as detached. True otherwise. - bool owned; - - // Pids are only meant to be constructed inside this module, so - // we make the constructor private. - version (Windows) - { - HANDLE _handle = INVALID_HANDLE_VALUE; - this(int pid, HANDLE handle) @safe pure nothrow - { - _processID = pid; - _handle = handle; - this.owned = true; - } - this(int pid) @safe pure nothrow - { - _processID = pid; - this.owned = false; - } - } - else - { - this(int id, bool owned) @safe pure nothrow - { - _processID = id; - this.owned = owned; - } - } -} - - -/** -Waits for the process associated with `pid` to terminate, and returns -its exit status. - -In general one should always _wait for child processes to terminate -before exiting the parent process unless the process was spawned as detached -(that was spawned with `Config.detached` flag). -Otherwise, they may become "$(HTTP en.wikipedia.org/wiki/Zombie_process,zombies)" -– processes that are defunct, yet still occupy a slot in the OS process table. -You should not and must not wait for detached processes, since you don't own them. - -If the process has already terminated, this function returns directly. -The exit code is cached, so that if wait() is called multiple times on -the same $(LREF Pid) it will always return the same value. - -POSIX_specific: -If the process is terminated by a signal, this function returns a -negative number whose absolute value is the signal number. -Since POSIX restricts normal exit codes to the range 0-255, a -negative return value will always indicate termination by signal. -Signal codes are defined in the `core.sys.posix.signal` module -(which corresponds to the `signal.h` POSIX header). - -Throws: -$(LREF ProcessException) on failure or on attempt to wait for detached process. - -Example: -See the $(LREF spawnProcess) documentation. - -See_also: -$(LREF tryWait), for a non-blocking function. -*/ -int wait(Pid pid) @safe -{ - assert(pid !is null, "Called wait on a null Pid."); - return pid.performWait(true); -} - - -@system unittest // Pid and wait() -{ - version (Windows) TestScript prog = "exit %~1"; - else version (Posix) TestScript prog = "exit $1"; - assert(wait(spawnProcess([prog.path, "0"])) == 0); - assert(wait(spawnProcess([prog.path, "123"])) == 123); - auto pid = spawnProcess([prog.path, "10"]); - assert(pid.processID > 0); - version (Windows) assert(pid.osHandle != INVALID_HANDLE_VALUE); - else version (Posix) assert(pid.osHandle == pid.processID); - assert(wait(pid) == 10); - assert(wait(pid) == 10); // cached exit code - assert(pid.processID < 0); - version (Windows) assert(pid.osHandle == INVALID_HANDLE_VALUE); - else version (Posix) assert(pid.osHandle < 0); -} - -private import std.typecons : Tuple; - -/** -Waits until either the process associated with `pid` terminates or the -elapsed time exceeds the given timeout. - -If the process terminates within the given duration it behaves exactly like -`wait`, except that it returns a tuple `(true, exit code)`. - -If the process does not terminate within the given duration it will stop -waiting and return `(false, 0).` - -The timeout may not exceed `(uint.max - 1).msecs` (~ 7 weeks, 17 hours). - -$(BLUE This function is Windows-Only.) - -Returns: -An $(D std.typecons.Tuple!(bool, "terminated", int, "status")). - -Throws: -$(LREF ProcessException) on failure or on attempt to wait for detached process. - -Example: -See the $(LREF spawnProcess) documentation. - -See_also: -$(LREF wait), for a blocking function without timeout. -$(LREF tryWait), for a non-blocking function without timeout. -*/ -version (StdDdoc) -Tuple!(bool, "terminated", int, "status") waitTimeout(Pid pid, Duration timeout) @safe; - -else version (Windows) -Tuple!(bool, "terminated", int, "status") waitTimeout(Pid pid, Duration timeout) @safe -{ - assert(pid !is null, "Called wait on a null Pid."); - auto code = pid.performWait(timeout); - return typeof(return)(pid._processID == Pid.terminated, code); -} - -version (Windows) -@system unittest // Pid and waitTimeout() -{ - import std.exception : collectException; - import std.typecons : tuple; - - TestScript prog = ":Loop\r\n" ~ "goto Loop"; - auto pid = spawnProcess(prog.path); - - // Doesn't block longer than one second - assert(waitTimeout(pid, 1.seconds) == tuple(false, 0)); - - kill(pid); - assert(waitTimeout(pid, 1.seconds) == tuple(true, 1)); // exit 1 because the process is killed - - // Rejects timeouts exceeding the Windows API capabilities - const dur = DWORD.max.msecs; - const ex = collectException!ProcessException(waitTimeout(pid, dur)); - assert(ex); - assert(ex.msg == "Timeout exceeds maximum wait time!"); -} - -/** -A non-blocking version of $(LREF wait). - -If the process associated with `pid` has already terminated, -`tryWait` has the exact same effect as `wait`. -In this case, it returns a tuple where the `terminated` field -is set to `true` and the `status` field has the same -interpretation as the return value of `wait`. - -If the process has $(I not) yet terminated, this function differs -from `wait` in that does not wait for this to happen, but instead -returns immediately. The `terminated` field of the returned -tuple will then be set to `false`, while the `status` field -will always be 0 (zero). `wait` or `tryWait` should then be -called again on the same `Pid` at some later time; not only to -get the exit code, but also to avoid the process becoming a "zombie" -when it finally terminates. (See $(LREF wait) for details). - -Returns: -An $(D std.typecons.Tuple!(bool, "terminated", int, "status")). - -Throws: -$(LREF ProcessException) on failure or on attempt to wait for detached process. - -Example: ---- -auto pid = spawnProcess("dmd myapp.d"); -scope(exit) wait(pid); -... -auto dmd = tryWait(pid); -if (dmd.terminated) -{ - if (dmd.status == 0) writeln("Compilation succeeded!"); - else writeln("Compilation failed"); -} -else writeln("Still compiling..."); -... ---- -Note that in this example, the first `wait` call will have no -effect if the process has already terminated by the time `tryWait` -is called. In the opposite case, however, the `scope` statement -ensures that we always wait for the process if it hasn't terminated -by the time we reach the end of the scope. -*/ -auto tryWait(Pid pid) @safe -{ - import std.typecons : Tuple; - assert(pid !is null, "Called tryWait on a null Pid."); - auto code = pid.performWait(false); - return Tuple!(bool, "terminated", int, "status")(pid._processID == Pid.terminated, code); -} -// unittest: This function is tested together with kill() below. - - -/** -Attempts to terminate the process associated with `pid`. - -The effect of this function, as well as the meaning of `codeOrSignal`, -is highly platform dependent. Details are given below. Common to all -platforms is that this function only $(I initiates) termination of the process, -and returns immediately. It does not wait for the process to end, -nor does it guarantee that the process does in fact get terminated. - -Always call $(LREF wait) to wait for a process to complete, even if `kill` -has been called on it. - -Windows_specific: -The process will be -$(LINK2 http://msdn.microsoft.com/en-us/library/windows/desktop/ms686714%28v=vs.100%29.aspx, -forcefully and abruptly terminated). If `codeOrSignal` is specified, it -must be a nonnegative number which will be used as the exit code of the process. -If not, the process wil exit with code 1. Do not use $(D codeOrSignal = 259), -as this is a special value (aka. $(LINK2 http://msdn.microsoft.com/en-us/library/windows/desktop/ms683189.aspx,STILL_ACTIVE)) -used by Windows to signal that a process has in fact $(I not) terminated yet. ---- -auto pid = spawnProcess("some_app"); -kill(pid, 10); -assert(wait(pid) == 10); ---- - -POSIX_specific: -A $(LINK2 http://en.wikipedia.org/wiki/Unix_signal,signal) will be sent to -the process, whose value is given by `codeOrSignal`. Depending on the -signal sent, this may or may not terminate the process. Symbolic constants -for various $(LINK2 http://en.wikipedia.org/wiki/Unix_signal#POSIX_signals, -POSIX signals) are defined in `core.sys.posix.signal`, which corresponds to the -$(LINK2 http://pubs.opengroup.org/onlinepubs/9699919799/basedefs/signal.h.html, -`signal.h` POSIX header). If `codeOrSignal` is omitted, the -`SIGTERM` signal will be sent. (This matches the behaviour of the -$(LINK2 http://pubs.opengroup.org/onlinepubs/9699919799/utilities/kill.html, -`_kill`) shell command.) ---- -import core.sys.posix.signal : SIGKILL; -auto pid = spawnProcess("some_app"); -kill(pid, SIGKILL); -assert(wait(pid) == -SIGKILL); // Negative return value on POSIX! ---- - -Throws: -$(LREF ProcessException) on error (e.g. if codeOrSignal is invalid). - or on attempt to kill detached process. - Note that failure to terminate the process is considered a "normal" - outcome, not an error.$(BR) -*/ -void kill(Pid pid) -{ - version (Windows) kill(pid, 1); - else version (Posix) - { - import core.sys.posix.signal : SIGTERM; - kill(pid, SIGTERM); - } -} - -/// ditto -void kill(Pid pid, int codeOrSignal) -{ - import std.exception : enforce; - enforce!ProcessException(pid.owned, "Can't kill detached process"); - version (Windows) - { - if (codeOrSignal < 0) throw new ProcessException("Invalid exit code"); - // On Windows, TerminateProcess() appears to terminate the - // *current* process if it is passed an invalid handle... - if (pid.osHandle == INVALID_HANDLE_VALUE) - throw new ProcessException("Invalid process handle"); - if (!TerminateProcess(pid.osHandle, codeOrSignal)) - throw ProcessException.newFromLastError(); - } - else version (Posix) - { - import core.sys.posix.signal : kill; - if (kill(pid.osHandle, codeOrSignal) == -1) - throw ProcessException.newFromErrno(); - } -} - -@system unittest // tryWait() and kill() -{ - import core.thread; - import std.exception : assertThrown; - // The test script goes into an infinite loop. - version (Windows) - { - TestScript prog = ":loop - goto loop"; - } - else version (Posix) - { - import core.sys.posix.signal : SIGTERM, SIGKILL; - TestScript prog = "while true; do sleep 1; done"; - } - auto pid = spawnProcess(prog.path); - // Android appears to automatically kill sleeping processes very quickly, - // so shorten the wait before killing here. - version (Android) - Thread.sleep(dur!"msecs"(5)); - else - Thread.sleep(dur!"msecs"(500)); - kill(pid); - version (Windows) assert(wait(pid) == 1); - else version (Posix) assert(wait(pid) == -SIGTERM); - - pid = spawnProcess(prog.path); - Thread.sleep(dur!"msecs"(500)); - auto s = tryWait(pid); - assert(!s.terminated && s.status == 0); - assertThrown!ProcessException(kill(pid, -123)); // Negative code not allowed. - version (Windows) kill(pid, 123); - else version (Posix) kill(pid, SIGKILL); - do { s = tryWait(pid); } while (!s.terminated); - version (Windows) assert(s.status == 123); - else version (Posix) assert(s.status == -SIGKILL); - assertThrown!ProcessException(kill(pid)); -} - -@system unittest // wait() and kill() detached process -{ - import core.thread; - import std.exception : assertThrown; - TestScript prog = "exit 0"; - auto pid = spawnProcess([prog.path], null, Config.detached); - /* - This sleep is needed because we can't wait() for detached process to end - and therefore TestScript destructor may run at the same time as /bin/sh tries to start the script. - This leads to the annoying message like "/bin/sh: 0: Can't open /tmp/std.process temporary file" to appear when running tests. - It does not happen in unittests with non-detached processes because we always wait() for them to finish. - */ - Thread.sleep(500.msecs); - assert(!pid.owned); - version (Windows) assert(pid.osHandle == INVALID_HANDLE_VALUE); - assertThrown!ProcessException(wait(pid)); - assertThrown!ProcessException(kill(pid)); -} - - -/** -Creates a unidirectional _pipe. - -Data is written to one end of the _pipe and read from the other. ---- -auto p = pipe(); -p.writeEnd.writeln("Hello World"); -p.writeEnd.flush(); -assert(p.readEnd.readln().chomp() == "Hello World"); ---- -Pipes can, for example, be used for interprocess communication -by spawning a new process and passing one end of the _pipe to -the child, while the parent uses the other end. -(See also $(LREF pipeProcess) and $(LREF pipeShell) for an easier -way of doing this.) ---- -// Use cURL to download the dlang.org front page, pipe its -// output to grep to extract a list of links to ZIP files, -// and write the list to the file "D downloads.txt": -auto p = pipe(); -auto outFile = File("D downloads.txt", "w"); -auto cpid = spawnProcess(["curl", "http://dlang.org/download.html"], - std.stdio.stdin, p.writeEnd); -scope(exit) wait(cpid); -auto gpid = spawnProcess(["grep", "-o", `http://\S*\.zip`], - p.readEnd, outFile); -scope(exit) wait(gpid); ---- - -Returns: -A $(LREF Pipe) object that corresponds to the created _pipe. - -Throws: -$(REF StdioException, std,stdio) on failure. -*/ -version (Posix) -Pipe pipe() @trusted //TODO: @safe -{ - import core.sys.posix.stdio : fdopen; - int[2] fds; - if (core.sys.posix.unistd.pipe(fds) != 0) - throw new StdioException("Unable to create pipe"); - Pipe p; - auto readFP = fdopen(fds[0], "r"); - if (readFP == null) - throw new StdioException("Cannot open read end of pipe"); - p._read = File(readFP, null); - auto writeFP = fdopen(fds[1], "w"); - if (writeFP == null) - throw new StdioException("Cannot open write end of pipe"); - p._write = File(writeFP, null); - return p; -} -else version (Windows) -Pipe pipe() @trusted //TODO: @safe -{ - // use CreatePipe to create an anonymous pipe - HANDLE readHandle; - HANDLE writeHandle; - if (!CreatePipe(&readHandle, &writeHandle, null, 0)) - { - throw new StdioException( - "Error creating pipe (" ~ generateSysErrorMsg() ~ ')', - 0); - } - - scope(failure) - { - CloseHandle(readHandle); - CloseHandle(writeHandle); - } - - try - { - Pipe p; - p._read .windowsHandleOpen(readHandle , "r"); - p._write.windowsHandleOpen(writeHandle, "a"); - return p; - } - catch (Exception e) - { - throw new StdioException("Error attaching pipe (" ~ e.msg ~ ")", - 0); - } -} - - -/// An interface to a pipe created by the $(LREF pipe) function. -struct Pipe -{ - /// The read end of the pipe. - @property File readEnd() @safe nothrow { return _read; } - - - /// The write end of the pipe. - @property File writeEnd() @safe nothrow { return _write; } - - - /** - Closes both ends of the pipe. - - Normally it is not necessary to do this manually, as $(REF File, std,stdio) - objects are automatically closed when there are no more references - to them. - - Note that if either end of the pipe has been passed to a child process, - it will only be closed in the parent process. (What happens in the - child process is platform dependent.) - - Throws: - $(REF ErrnoException, std,exception) if an error occurs. - */ - void close() @safe - { - _read.close(); - _write.close(); - } - -private: - File _read, _write; -} - -@system unittest -{ - import std.string; - auto p = pipe(); - p.writeEnd.writeln("Hello World"); - p.writeEnd.flush(); - assert(p.readEnd.readln().chomp() == "Hello World"); - p.close(); - assert(!p.readEnd.isOpen); - assert(!p.writeEnd.isOpen); -} - - -/** -Starts a new process, creating pipes to redirect its standard -input, output and/or error streams. - -`pipeProcess` and `pipeShell` are convenient wrappers around -$(LREF spawnProcess) and $(LREF spawnShell), respectively, and -automate the task of redirecting one or more of the child process' -standard streams through pipes. Like the functions they wrap, -these functions return immediately, leaving the child process to -execute in parallel with the invoking process. It is recommended -to always call $(LREF wait) on the returned $(LREF ProcessPipes.pid), -as detailed in the documentation for `wait`. - -The `args`/`program`/`command`, `env` and `config` -parameters are forwarded straight to the underlying spawn functions, -and we refer to their documentation for details. - -Params: -args = An array which contains the program name as the zeroth element - and any command-line arguments in the following elements. - (See $(LREF spawnProcess) for details.) -program = The program name, $(I without) command-line arguments. - (See $(LREF spawnProcess) for details.) -command = A shell command which is passed verbatim to the command - interpreter. (See $(LREF spawnShell) for details.) -redirect = Flags that determine which streams are redirected, and - how. See $(LREF Redirect) for an overview of available - flags. -env = Additional environment variables for the child process. - (See $(LREF spawnProcess) for details.) -config = Flags that control process creation. See $(LREF Config) - for an overview of available flags, and note that the - `retainStd...` flags have no effect in this function. -workDir = The working directory for the new process. - By default the child process inherits the parent's working - directory. -shellPath = The path to the shell to use to run the specified program. - By default this is $(LREF nativeShell). - -Returns: -A $(LREF ProcessPipes) object which contains $(REF File, std,stdio) -handles that communicate with the redirected streams of the child -process, along with a $(LREF Pid) object that corresponds to the -spawned process. - -Throws: -$(LREF ProcessException) on failure to start the process.$(BR) -$(REF StdioException, std,stdio) on failure to redirect any of the streams.$(BR) - -Example: ---- -// my_application writes to stdout and might write to stderr -auto pipes = pipeProcess("my_application", Redirect.stdout | Redirect.stderr); -scope(exit) wait(pipes.pid); - -// Store lines of output. -string[] output; -foreach (line; pipes.stdout.byLine) output ~= line.idup; - -// Store lines of errors. -string[] errors; -foreach (line; pipes.stderr.byLine) errors ~= line.idup; - - -// sendmail expects to read from stdin -pipes = pipeProcess(["/usr/bin/sendmail", "-t"], Redirect.stdin); -pipes.stdin.writeln("To: you"); -pipes.stdin.writeln("From: me"); -pipes.stdin.writeln("Subject: dlang"); -pipes.stdin.writeln(""); -pipes.stdin.writeln(message); - -// a single period tells sendmail we are finished -pipes.stdin.writeln("."); - -// but at this point sendmail might not see it, we need to flush -pipes.stdin.flush(); - -// sendmail happens to exit on ".", but some you have to close the file: -pipes.stdin.close(); - -// otherwise this wait will wait forever -wait(pipes.pid); - ---- -*/ -ProcessPipes pipeProcess(scope const(char[])[] args, - Redirect redirect = Redirect.all, - const string[string] env = null, - Config config = Config.none, - scope const(char)[] workDir = null) - @safe -{ - return pipeProcessImpl!spawnProcess(args, redirect, env, config, workDir); -} - -/// ditto -ProcessPipes pipeProcess(scope const(char)[] program, - Redirect redirect = Redirect.all, - const string[string] env = null, - Config config = Config.none, - scope const(char)[] workDir = null) - @safe -{ - return pipeProcessImpl!spawnProcess(program, redirect, env, config, workDir); -} - -/// ditto -ProcessPipes pipeShell(scope const(char)[] command, - Redirect redirect = Redirect.all, - const string[string] env = null, - Config config = Config.none, - scope const(char)[] workDir = null, - string shellPath = nativeShell) - @safe -{ - return pipeProcessImpl!spawnShell(command, - redirect, - env, - config, - workDir, - shellPath); -} - -// Implementation of the pipeProcess() family of functions. -private ProcessPipes pipeProcessImpl(alias spawnFunc, Cmd, ExtraSpawnFuncArgs...) - (scope Cmd command, - Redirect redirectFlags, - const string[string] env = null, - Config config = Config.none, - scope const(char)[] workDir = null, - ExtraSpawnFuncArgs extraArgs = ExtraSpawnFuncArgs.init) - @trusted //TODO: @safe -{ - File childStdin, childStdout, childStderr; - ProcessPipes pipes; - pipes._redirectFlags = redirectFlags; - - if (redirectFlags & Redirect.stdin) - { - auto p = pipe(); - childStdin = p.readEnd; - pipes._stdin = p.writeEnd; - } - else - { - childStdin = std.stdio.stdin; - } - - if (redirectFlags & Redirect.stdout) - { - if ((redirectFlags & Redirect.stdoutToStderr) != 0) - throw new StdioException("Cannot create pipe for stdout AND " - ~"redirect it to stderr", 0); - auto p = pipe(); - childStdout = p.writeEnd; - pipes._stdout = p.readEnd; - } - else - { - childStdout = std.stdio.stdout; - } - - if (redirectFlags & Redirect.stderr) - { - if ((redirectFlags & Redirect.stderrToStdout) != 0) - throw new StdioException("Cannot create pipe for stderr AND " - ~"redirect it to stdout", 0); - auto p = pipe(); - childStderr = p.writeEnd; - pipes._stderr = p.readEnd; - } - else - { - childStderr = std.stdio.stderr; - } - - if (redirectFlags & Redirect.stdoutToStderr) - { - if (redirectFlags & Redirect.stderrToStdout) - { - // We know that neither of the other options have been - // set, so we assign the std.stdio.std* streams directly. - childStdout = std.stdio.stderr; - childStderr = std.stdio.stdout; - } - else - { - childStdout = childStderr; - } - } - else if (redirectFlags & Redirect.stderrToStdout) - { - childStderr = childStdout; - } - - config.flags &= ~(Config.Flags.retainStdin | Config.Flags.retainStdout | Config.Flags.retainStderr); - pipes._pid = spawnFunc(command, childStdin, childStdout, childStderr, - env, config, workDir, extraArgs); - return pipes; -} - - -/** -Flags that can be passed to $(LREF pipeProcess) and $(LREF pipeShell) -to specify which of the child process' standard streams are redirected. -Use bitwise OR to combine flags. -*/ -enum Redirect -{ - /// Redirect the standard input, output or error streams, respectively. - stdin = 1, - stdout = 2, /// ditto - stderr = 4, /// ditto - - /** - Redirect _all three streams. This is equivalent to - $(D Redirect.stdin | Redirect.stdout | Redirect.stderr). - */ - all = stdin | stdout | stderr, - - /** - Redirect the standard error stream into the standard output stream. - This can not be combined with `Redirect.stderr`. - */ - stderrToStdout = 8, - - /** - Redirect the standard output stream into the standard error stream. - This can not be combined with `Redirect.stdout`. - */ - stdoutToStderr = 16, -} - -@system unittest -{ - import std.string; - version (Windows) TestScript prog = - "call :sub %~1 %~2 0 - call :sub %~1 %~2 1 - call :sub %~1 %~2 2 - call :sub %~1 %~2 3 - exit 3 - - :sub - set /p INPUT= - if -%INPUT%-==-stop- ( exit %~3 ) - echo %INPUT% %~1 - echo %INPUT% %~2 1>&2"; - else version (Posix) TestScript prog = - `for EXITCODE in 0 1 2 3; do - read INPUT - if test "$INPUT" = stop; then break; fi - echo "$INPUT $1" - echo "$INPUT $2" >&2 - done - exit $EXITCODE`; - auto pp = pipeProcess([prog.path, "bar", "baz"]); - pp.stdin.writeln("foo"); - pp.stdin.flush(); - assert(pp.stdout.readln().chomp() == "foo bar"); - assert(pp.stderr.readln().chomp().stripRight() == "foo baz"); - pp.stdin.writeln("1234567890"); - pp.stdin.flush(); - assert(pp.stdout.readln().chomp() == "1234567890 bar"); - assert(pp.stderr.readln().chomp().stripRight() == "1234567890 baz"); - pp.stdin.writeln("stop"); - pp.stdin.flush(); - assert(wait(pp.pid) == 2); - - pp = pipeProcess([prog.path, "12345", "67890"], - Redirect.stdin | Redirect.stdout | Redirect.stderrToStdout); - pp.stdin.writeln("xyz"); - pp.stdin.flush(); - assert(pp.stdout.readln().chomp() == "xyz 12345"); - assert(pp.stdout.readln().chomp().stripRight() == "xyz 67890"); - pp.stdin.writeln("stop"); - pp.stdin.flush(); - assert(wait(pp.pid) == 1); - - pp = pipeShell(escapeShellCommand(prog.path, "AAAAA", "BBB"), - Redirect.stdin | Redirect.stdoutToStderr | Redirect.stderr); - pp.stdin.writeln("ab"); - pp.stdin.flush(); - assert(pp.stderr.readln().chomp() == "ab AAAAA"); - assert(pp.stderr.readln().chomp().stripRight() == "ab BBB"); - pp.stdin.writeln("stop"); - pp.stdin.flush(); - assert(wait(pp.pid) == 1); -} - -@system unittest -{ - import std.exception : assertThrown; - TestScript prog = "exit 0"; - assertThrown!StdioException(pipeProcess( - prog.path, - Redirect.stdout | Redirect.stdoutToStderr)); - assertThrown!StdioException(pipeProcess( - prog.path, - Redirect.stderr | Redirect.stderrToStdout)); - auto p = pipeProcess(prog.path, Redirect.stdin); - assertThrown!Error(p.stdout); - assertThrown!Error(p.stderr); - wait(p.pid); - p = pipeProcess(prog.path, Redirect.stderr); - assertThrown!Error(p.stdin); - assertThrown!Error(p.stdout); - wait(p.pid); -} - -/** -Object which contains $(REF File, std,stdio) handles that allow communication -with a child process through its standard streams. -*/ -struct ProcessPipes -{ - /// The $(LREF Pid) of the child process. - @property Pid pid() @safe nothrow - { - return _pid; - } - - /** - An $(REF File, std,stdio) that allows writing to the child process' - standard input stream. - - Throws: - $(OBJECTREF Error) if the child process' standard input stream hasn't - been redirected. - */ - @property File stdin() @safe nothrow - { - if ((_redirectFlags & Redirect.stdin) == 0) - throw new Error("Child process' standard input stream hasn't " - ~"been redirected."); - return _stdin; - } - - /** - An $(REF File, std,stdio) that allows reading from the child process' - standard output stream. - - Throws: - $(OBJECTREF Error) if the child process' standard output stream hasn't - been redirected. - */ - @property File stdout() @safe nothrow - { - if ((_redirectFlags & Redirect.stdout) == 0) - throw new Error("Child process' standard output stream hasn't " - ~"been redirected."); - return _stdout; - } - - /** - An $(REF File, std,stdio) that allows reading from the child process' - standard error stream. - - Throws: - $(OBJECTREF Error) if the child process' standard error stream hasn't - been redirected. - */ - @property File stderr() @safe nothrow - { - if ((_redirectFlags & Redirect.stderr) == 0) - throw new Error("Child process' standard error stream hasn't " - ~"been redirected."); - return _stderr; - } - -private: - Redirect _redirectFlags; - Pid _pid; - File _stdin, _stdout, _stderr; -} - - - -/** -Executes the given program or shell command and returns its exit -code and output. - -`execute` and `executeShell` start a new process using -$(LREF spawnProcess) and $(LREF spawnShell), respectively, and wait -for the process to complete before returning. The functions capture -what the child process prints to both its standard output and -standard error streams, and return this together with its exit code. ---- -auto dmd = execute(["dmd", "myapp.d"]); -if (dmd.status != 0) writeln("Compilation failed:\n", dmd.output); - -auto ls = executeShell("ls -l"); -if (ls.status != 0) writeln("Failed to retrieve file listing"); -else writeln(ls.output); ---- - -The `args`/`program`/`command`, `env` and `config` -parameters are forwarded straight to the underlying spawn functions, -and we refer to their documentation for details. - -Params: -args = An array which contains the program name as the zeroth element - and any command-line arguments in the following elements. - (See $(LREF spawnProcess) for details.) -program = The program name, $(I without) command-line arguments. - (See $(LREF spawnProcess) for details.) -command = A shell command which is passed verbatim to the command - interpreter. (See $(LREF spawnShell) for details.) -env = Additional environment variables for the child process. - (See $(LREF spawnProcess) for details.) -config = Flags that control process creation. See $(LREF Config) - for an overview of available flags, and note that the - `retainStd...` flags have no effect in this function. -maxOutput = The maximum number of bytes of output that should be - captured. -workDir = The working directory for the new process. - By default the child process inherits the parent's working - directory. -shellPath = The path to the shell to use to run the specified program. - By default this is $(LREF nativeShell). - - -Returns: -An $(D std.typecons.Tuple!(int, "status", string, "output")). - -POSIX_specific: -If the process is terminated by a signal, the `status` field of -the return value will contain a negative number whose absolute -value is the signal number. (See $(LREF wait) for details.) - -Throws: -$(LREF ProcessException) on failure to start the process.$(BR) -$(REF StdioException, std,stdio) on failure to capture output. -*/ -auto execute(scope const(char[])[] args, - const string[string] env = null, - Config config = Config.none, - size_t maxOutput = size_t.max, - scope const(char)[] workDir = null) - @safe -{ - return executeImpl!pipeProcess(args, env, config, maxOutput, workDir); -} - -/// ditto -auto execute(scope const(char)[] program, - const string[string] env = null, - Config config = Config.none, - size_t maxOutput = size_t.max, - scope const(char)[] workDir = null) - @safe -{ - return executeImpl!pipeProcess(program, env, config, maxOutput, workDir); -} - -/// ditto -auto executeShell(scope const(char)[] command, - const string[string] env = null, - Config config = Config.none, - size_t maxOutput = size_t.max, - scope const(char)[] workDir = null, - string shellPath = nativeShell) - @safe -{ - return executeImpl!pipeShell(command, - env, - config, - maxOutput, - workDir, - shellPath); -} - -// Does the actual work for execute() and executeShell(). -private auto executeImpl(alias pipeFunc, Cmd, ExtraPipeFuncArgs...)( - Cmd commandLine, - const string[string] env = null, - Config config = Config.none, - size_t maxOutput = size_t.max, - scope const(char)[] workDir = null, - ExtraPipeFuncArgs extraArgs = ExtraPipeFuncArgs.init) - @trusted //TODO: @safe -{ - import std.algorithm.comparison : min; - import std.array : appender; - import std.typecons : Tuple; - - auto redirect = (config.flags & Config.Flags.stderrPassThrough) - ? Redirect.stdout - : Redirect.stdout | Redirect.stderrToStdout; - - auto p = pipeFunc(commandLine, redirect, - env, config, workDir, extraArgs); - - auto a = appender!string; - enum size_t defaultChunkSize = 4096; - immutable chunkSize = min(maxOutput, defaultChunkSize); - - // Store up to maxOutput bytes in a. - foreach (ubyte[] chunk; p.stdout.byChunk(chunkSize)) - { - immutable size_t remain = maxOutput - a.data.length; - - if (chunk.length < remain) a.put(chunk); - else - { - a.put(chunk[0 .. remain]); - break; - } - } - // Exhaust the stream, if necessary. - foreach (ubyte[] chunk; p.stdout.byChunk(defaultChunkSize)) { } - - return Tuple!(int, "status", string, "output")(wait(p.pid), a.data); -} - -@system unittest -{ - import std.string; - // To avoid printing the newline characters, we use the echo|set trick on - // Windows, and printf on POSIX (neither echo -n nor echo \c are portable). - version (Windows) TestScript prog = - "echo|set /p=%~1 - echo|set /p=%~2 1>&2 - exit 123"; - else version (Android) TestScript prog = - `echo -n $1 - echo -n $2 >&2 - exit 123`; - else version (Posix) TestScript prog = - `printf '%s' $1 - printf '%s' $2 >&2 - exit 123`; - auto r = execute([prog.path, "foo", "bar"]); - assert(r.status == 123); - assert(r.output.stripRight() == "foobar"); - auto s = execute([prog.path, "Hello", "World"]); - assert(s.status == 123); - assert(s.output.stripRight() == "HelloWorld"); -} - -@safe unittest -{ - import std.string; - auto r1 = executeShell("echo foo"); - assert(r1.status == 0); - assert(r1.output.chomp() == "foo"); - auto r2 = executeShell("echo bar 1>&2"); - assert(r2.status == 0); - assert(r2.output.chomp().stripRight() == "bar"); - auto r3 = executeShell("exit 123"); - assert(r3.status == 123); - assert(r3.output.empty); -} - -@system unittest -{ - // Temporarily disable output to stderr so as to not spam the build log. - import std.stdio : stderr; - import std.typecons : Tuple; - import std.file : readText, exists, remove; - import std.traits : ReturnType; - - ReturnType!executeShell r; - auto tmpname = uniqueTempPath; - scope(exit) if (exists(tmpname)) remove(tmpname); - auto t = stderr; - // Open a new scope to minimize code ran with stderr redirected. - { - stderr.open(tmpname, "w"); - scope(exit) stderr = t; - r = executeShell("echo D rox>&2", null, Config.stderrPassThrough); - } - assert(r.status == 0); - assert(r.output.empty); - auto witness = readText(tmpname); - import std.ascii : newline; - assert(witness == "D rox" ~ newline, "'" ~ witness ~ "'"); -} - -@safe unittest -{ - import std.typecons : Tuple; - void foo() //Just test the compilation - { - auto ret1 = execute(["dummy", "arg"]); - auto ret2 = executeShell("dummy arg"); - static assert(is(typeof(ret1) == typeof(ret2))); - - Tuple!(int, string) ret3 = execute(["dummy", "arg"]); - } -} - -/// An exception that signals a problem with starting or waiting for a process. -class ProcessException : Exception -{ - import std.exception : basicExceptionCtors; - mixin basicExceptionCtors; - - // Creates a new ProcessException based on errno. - static ProcessException newFromErrno(string customMsg = null, - string file = __FILE__, - size_t line = __LINE__) - { - import core.stdc.errno : errno; - return newFromErrno(errno, customMsg, file, line); - } - - // ditto, but error number is provided by caller - static ProcessException newFromErrno(int error, - string customMsg = null, - string file = __FILE__, - size_t line = __LINE__) - { - import std.exception : errnoString; - auto errnoMsg = errnoString(error); - auto msg = customMsg.empty ? errnoMsg - : customMsg ~ " (" ~ errnoMsg ~ ')'; - return new ProcessException(msg, file, line); - } - - // Creates a new ProcessException based on GetLastError() (Windows only). - version (Windows) - static ProcessException newFromLastError(string customMsg = null, - string file = __FILE__, - size_t line = __LINE__) - { - auto lastMsg = generateSysErrorMsg(); - auto msg = customMsg.empty ? lastMsg - : customMsg ~ " (" ~ lastMsg ~ ')'; - return new ProcessException(msg, file, line); - } -} - - -/** -Determines the path to the current user's preferred command interpreter. - -On Windows, this function returns the contents of the COMSPEC environment -variable, if it exists. Otherwise, it returns the result of $(LREF nativeShell). - -On POSIX, `userShell` returns the contents of the SHELL environment -variable, if it exists and is non-empty. Otherwise, it returns the result of -$(LREF nativeShell). -*/ -@property string userShell() @safe -{ - version (Windows) return environment.get("COMSPEC", nativeShell); - else version (Posix) return environment.get("SHELL", nativeShell); -} - -/** -The platform-specific native shell path. - -This function returns `"cmd.exe"` on Windows, `"/bin/sh"` on POSIX, and -`"/system/bin/sh"` on Android. -*/ -@property string nativeShell() @safe @nogc pure nothrow -{ - version (Windows) return "cmd.exe"; - else version (Android) return "/system/bin/sh"; - else version (Posix) return "/bin/sh"; -} - -// A command-line switch that indicates to the shell that it should -// interpret the following argument as a command to be executed. -version (Posix) private immutable string shellSwitch = "-c"; -version (Windows) private immutable string shellSwitch = "/C"; - -// Unittest support code: TestScript takes a string that contains a -// shell script for the current platform, and writes it to a temporary -// file. On Windows the file name gets a .cmd extension, while on -// POSIX its executable permission bit is set. The file is -// automatically deleted when the object goes out of scope. -version (StdUnittest) -private struct TestScript -{ - this(string code) @system - { - // @system due to chmod - import std.ascii : newline; - import std.file : write; - version (Windows) - { - auto ext = ".cmd"; - auto firstLine = "@echo off"; - } - else version (Posix) - { - auto ext = ""; - auto firstLine = "#!" ~ nativeShell; - } - path = uniqueTempPath()~ext; - write(path, firstLine ~ newline ~ code ~ newline); - version (Posix) - { - import core.sys.posix.sys.stat : chmod; - import std.conv : octal; - chmod(path.tempCString(), octal!777); - } - } - - ~this() - { - import std.file : remove, exists; - if (!path.empty && exists(path)) - { - try { remove(path); } - catch (Exception e) - { - debug std.stdio.stderr.writeln(e.msg); - } - } - } - - string path; -} - - -// ============================================================================= -// Functions for shell command quoting/escaping. -// ============================================================================= - - -/* - Command line arguments exist in three forms: - 1) string or char* array, as received by main. - Also used internally on POSIX systems. - 2) Command line string, as used in Windows' - CreateProcess and CommandLineToArgvW functions. - A specific quoting and escaping algorithm is used - to distinguish individual arguments. - 3) Shell command string, as written at a shell prompt - or passed to cmd /C - this one may contain shell - control characters, e.g. > or | for redirection / - piping - thus, yet another layer of escaping is - used to distinguish them from program arguments. - - Except for escapeWindowsArgument, the intermediary - format (2) is hidden away from the user in this module. -*/ - -/** -Escapes an argv-style argument array to be used with $(LREF spawnShell), -$(LREF pipeShell) or $(LREF executeShell). ---- -string url = "http://dlang.org/"; -executeShell(escapeShellCommand("wget", url, "-O", "dlang-index.html")); ---- - -Concatenate multiple `escapeShellCommand` and -$(LREF escapeShellFileName) results to use shell redirection or -piping operators. ---- -executeShell( - escapeShellCommand("curl", "http://dlang.org/download.html") ~ - "|" ~ - escapeShellCommand("grep", "-o", `http://\S*\.zip`) ~ - ">" ~ - escapeShellFileName("D download links.txt")); ---- - -Throws: -$(OBJECTREF Exception) if any part of the command line contains unescapable -characters (NUL on all platforms, as well as CR and LF on Windows). -*/ -string escapeShellCommand(scope const(char[])[] args...) @safe pure -{ - if (args.empty) - return null; - version (Windows) - { - // Do not ^-escape the first argument (the program path), - // as the shell parses it differently from parameters. - // ^-escaping a program path that contains spaces will fail. - string result = escapeShellFileName(args[0]); - if (args.length > 1) - { - result ~= " " ~ escapeShellCommandString( - escapeShellArguments(args[1..$])); - } - return result; - } - version (Posix) - { - return escapeShellCommandString(escapeShellArguments(args)); - } -} - -@safe unittest -{ - // This is a simple unit test without any special requirements, - // in addition to the unittest_burnin one below which requires - // special preparation. - - struct TestVector { string[] args; string windows, posix; } - TestVector[] tests = - [ - { - args : ["foo bar"], - windows : `"foo bar"`, - posix : `'foo bar'` - }, - { - args : ["foo bar", "hello"], - windows : `"foo bar" hello`, - posix : `'foo bar' hello` - }, - { - args : ["foo bar", "hello world"], - windows : `"foo bar" ^"hello world^"`, - posix : `'foo bar' 'hello world'` - }, - { - args : ["foo bar", "hello", "world"], - windows : `"foo bar" hello world`, - posix : `'foo bar' hello world` - }, - { - args : ["foo bar", `'"^\`], - windows : `"foo bar" ^"'\^"^^\\^"`, - posix : `'foo bar' ''\''"^\'` - }, - { - args : ["foo bar", ""], - windows : `"foo bar" ^"^"`, - posix : `'foo bar' ''` - }, - { - args : ["foo bar", "2"], - windows : `"foo bar" ^"2^"`, - posix : `'foo bar' '2'` - }, - ]; - - foreach (test; tests) - { - auto actual = escapeShellCommand(test.args); - version (Windows) - string expected = test.windows; - else - string expected = test.posix; - assert(actual == expected, "\nExpected: " ~ expected ~ "\nGot: " ~ actual); - } -} - -private string escapeShellCommandString(return scope string command) @safe pure -{ - version (Windows) - return escapeWindowsShellCommand(command); - else - return command; -} - -private string escapeWindowsShellCommand(scope const(char)[] command) @safe pure -{ - import std.array : appender; - auto result = appender!string(); - result.reserve(command.length); - - foreach (c; command) - switch (c) - { - case '\0': - throw new Exception("Cannot put NUL in command line"); - case '\r': - case '\n': - throw new Exception("CR/LF are not escapable"); - case '\x01': .. case '\x09': - case '\x0B': .. case '\x0C': - case '\x0E': .. case '\x1F': - case '"': - case '^': - case '&': - case '<': - case '>': - case '|': - result.put('^'); - goto default; - default: - result.put(c); - } - return result.data; -} - -private string escapeShellArguments(scope const(char[])[] args...) - @trusted pure nothrow -{ - import std.exception : assumeUnique; - char[] buf; - - @safe nothrow - char[] allocator(size_t size) - { - if (buf.length == 0) - return buf = new char[size]; - else - { - auto p = buf.length; - buf.length = buf.length + 1 + size; - buf[p++] = ' '; - return buf[p .. p+size]; - } - } - - foreach (arg; args) - escapeShellArgument!allocator(arg); - return assumeUnique(buf); -} - -private auto escapeShellArgument(alias allocator)(scope const(char)[] arg) @safe nothrow -{ - // The unittest for this function requires special - // preparation - see below. - - version (Windows) - return escapeWindowsArgumentImpl!allocator(arg); - else - return escapePosixArgumentImpl!allocator(arg); -} - -/** -Quotes a command-line argument in a manner conforming to the behavior of -$(LINK2 http://msdn.microsoft.com/en-us/library/windows/desktop/bb776391(v=vs.85).aspx, -CommandLineToArgvW). -*/ -string escapeWindowsArgument(scope const(char)[] arg) @trusted pure nothrow -{ - // Rationale for leaving this function as public: - // this algorithm of escaping paths is also used in other software, - // e.g. DMD's response files. - import std.exception : assumeUnique; - auto buf = escapeWindowsArgumentImpl!charAllocator(arg); - return assumeUnique(buf); -} - - -private char[] charAllocator(size_t size) @safe pure nothrow -{ - return new char[size]; -} - - -private char[] escapeWindowsArgumentImpl(alias allocator)(scope const(char)[] arg) - @safe nothrow -if (is(typeof(allocator(size_t.init)[0] = char.init))) -{ - // References: - // * http://msdn.microsoft.com/en-us/library/windows/desktop/bb776391(v=vs.85).aspx - // * http://blogs.msdn.com/b/oldnewthing/archive/2010/09/17/10063629.aspx - - // Check if the string needs to be escaped, - // and calculate the total string size. - - // Trailing backslashes must be escaped - bool escaping = true; - bool needEscape = false; - // Result size = input size + 2 for surrounding quotes + 1 for the - // backslash for each escaped character. - size_t size = 1 + arg.length + 1; - - foreach_reverse (char c; arg) - { - if (c == '"') - { - needEscape = true; - escaping = true; - size++; - } - else - if (c == '\\') - { - if (escaping) - size++; - } - else - { - if (c == ' ' || c == '\t') - needEscape = true; - escaping = false; - } - } - - import std.ascii : isDigit; - // Empty arguments need to be specified as "" - if (!arg.length) - needEscape = true; - else - // Arguments ending with digits need to be escaped, - // to disambiguate with 1>file redirection syntax - if (isDigit(arg[$-1])) - needEscape = true; - - if (!needEscape) - return allocator(arg.length)[] = arg; - - // Construct result string. - - auto buf = allocator(size); - size_t p = size; - buf[--p] = '"'; - escaping = true; - foreach_reverse (char c; arg) - { - if (c == '"') - escaping = true; - else - if (c != '\\') - escaping = false; - - buf[--p] = c; - if (escaping) - buf[--p] = '\\'; - } - buf[--p] = '"'; - assert(p == 0); - - return buf; -} - -version (Windows) version (StdUnittest) -{ -private: - import core.stdc.stddef; - import core.stdc.wchar_ : wcslen; - import core.sys.windows.shellapi : CommandLineToArgvW; - import core.sys.windows.winbase; - import core.sys.windows.winnt; - import std.array; - - string[] parseCommandLine(string line) - { - import std.algorithm.iteration : map; - import std.array : array; - import std.conv : to; - auto lpCommandLine = (to!(WCHAR[])(line) ~ '\0').ptr; - int numArgs; - auto args = CommandLineToArgvW(lpCommandLine, &numArgs); - scope(exit) LocalFree(args); - return args[0 .. numArgs] - .map!(arg => to!string(arg[0 .. wcslen(arg)])) - .array(); - } - - @system unittest - { - import std.conv : text; - string[] testStrings = [ - `Hello`, - `Hello, world`, - `Hello, "world"`, - `C:\`, - `C:\dmd`, - `C:\Program Files\`, - ]; - - enum CHARS = `_x\" *&^` ~ "\t"; // _ is placeholder for nothing - foreach (c1; CHARS) - foreach (c2; CHARS) - foreach (c3; CHARS) - foreach (c4; CHARS) - testStrings ~= [c1, c2, c3, c4].replace("_", ""); - - foreach (s; testStrings) - { - auto q = escapeWindowsArgument(s); - auto args = parseCommandLine("Dummy.exe " ~ q); - assert(args.length == 2, s ~ " => " ~ q ~ " #" ~ text(args.length-1)); - assert(args[1] == s, s ~ " => " ~ q ~ " => " ~ args[1]); - } - } -} - -private string escapePosixArgument(scope const(char)[] arg) @trusted pure nothrow -{ - import std.exception : assumeUnique; - auto buf = escapePosixArgumentImpl!charAllocator(arg); - return assumeUnique(buf); -} - -private char[] escapePosixArgumentImpl(alias allocator)(scope const(char)[] arg) - @safe nothrow -if (is(typeof(allocator(size_t.init)[0] = char.init))) -{ - bool needQuoting = { - import std.ascii : isAlphaNum, isDigit; - import std.algorithm.comparison : among; - - // Empty arguments need to be specified as '' - if (arg.length == 0) - return true; - // Arguments ending with digits need to be escaped, - // to disambiguate with 1>file redirection syntax - if (isDigit(arg[$-1])) - return true; - - // Obtained using: - // for n in $(seq 1 255) ; do - // c=$(printf \\$(printf "%o" $n)) - // q=$(/bin/printf '%q' "$c") - // if [[ "$q" == "$c" ]] ; then printf "%s, " "'$c'" ; fi - // done - // printf '\n' - foreach (char c; arg) - if (!isAlphaNum(c) && !c.among('%', '+', ',', '-', '.', '/', ':', '@', ']', '_')) - return true; - return false; - }(); - if (!needQuoting) - { - auto buf = allocator(arg.length); - buf[] = arg; - return buf; - } - - // '\'' means: close quoted part of argument, append an escaped - // single quote, and reopen quotes - - // Below code is equivalent to: - // return `'` ~ std.array.replace(arg, `'`, `'\''`) ~ `'`; - - size_t size = 1 + arg.length + 1; - foreach (char c; arg) - if (c == '\'') - size += 3; - - auto buf = allocator(size); - size_t p = 0; - buf[p++] = '\''; - foreach (char c; arg) - if (c == '\'') - { - buf[p .. p+4] = `'\''`; - p += 4; - } - else - buf[p++] = c; - buf[p++] = '\''; - assert(p == size); - - return buf; -} - -/** -Escapes a filename to be used for shell redirection with $(LREF spawnShell), -$(LREF pipeShell) or $(LREF executeShell). -*/ -string escapeShellFileName(scope const(char)[] fileName) @trusted pure nothrow -{ - // The unittest for this function requires special - // preparation - see below. - - version (Windows) - { - // If a file starts with &, it can cause cmd.exe to misinterpret - // the file name as the stream redirection syntax: - // command > "&foo.txt" - // gets interpreted as - // command >&foo.txt - // Prepend .\ to disambiguate. - - if (fileName.length && fileName[0] == '&') - return cast(string)(`".\` ~ fileName ~ '"'); - - return cast(string)('"' ~ fileName ~ '"'); - } - else - return escapePosixArgument(fileName); -} - -// Loop generating strings with random characters -//version = unittest_burnin; - -version (unittest_burnin) -@system unittest -{ - // There are no readily-available commands on all platforms suitable - // for properly testing command escaping. The behavior of CMD's "echo" - // built-in differs from the POSIX program, and Windows ports of POSIX - // environments (Cygwin, msys, gnuwin32) may interfere with their own - // "echo" ports. - - // To run this unit test, create std_process_unittest_helper.d with the - // following content and compile it: - // import std.stdio, std.array; void main(string[] args) { write(args.join("\0")); } - // Then, test this module with: - // rdmd --main -unittest -version=unittest_burnin process.d - - import std.file : readText, remove; - import std.format : format; - import std.path : absolutePath; - import std.random : uniform; - - auto helper = absolutePath("std_process_unittest_helper"); - assert(executeShell(helper ~ " hello").output.split("\0")[1..$] == ["hello"], "Helper malfunction"); - - void test(string[] s, string fn) - { - string e; - string[] g; - - e = escapeShellCommand(helper ~ s); - { - scope(failure) writefln("executeShell() failed.\nExpected:\t%s\nEncoded:\t%s", s, [e]); - auto result = executeShell(e); - assert(result.status == 0, "std_process_unittest_helper failed"); - g = result.output.split("\0")[1..$]; - } - assert(s == g, format("executeShell() test failed.\nExpected:\t%s\nGot:\t\t%s\nEncoded:\t%s", s, g, [e])); - - e = escapeShellCommand(helper ~ s) ~ ">" ~ escapeShellFileName(fn); - { - scope(failure) writefln( - "executeShell() with redirect failed.\nExpected:\t%s\nFilename:\t%s\nEncoded:\t%s", s, [fn], [e]); - auto result = executeShell(e); - assert(result.status == 0, "std_process_unittest_helper failed"); - assert(!result.output.length, "No output expected, got:\n" ~ result.output); - g = readText(fn).split("\0")[1..$]; - } - remove(fn); - assert(s == g, - format("executeShell() with redirect test failed.\nExpected:\t%s\nGot:\t\t%s\nEncoded:\t%s", s, g, [e])); - } - - while (true) - { - string[] args; - foreach (n; 0 .. uniform(1, 4)) - { - string arg; - foreach (l; 0 .. uniform(0, 10)) - { - dchar c; - while (true) - { - version (Windows) - { - // As long as DMD's system() uses CreateProcessA, - // we can't reliably pass Unicode - c = uniform(0, 128); - } - else - c = uniform!ubyte(); - - if (c == 0) - continue; // argv-strings are zero-terminated - version (Windows) - if (c == '\r' || c == '\n') - continue; // newlines are unescapable on Windows - break; - } - arg ~= c; - } - args ~= arg; - } - - // generate filename - string fn; - foreach (l; 0 .. uniform(1, 10)) - { - dchar c; - while (true) - { - version (Windows) - c = uniform(0, 128); // as above - else - c = uniform!ubyte(); - - if (c == 0 || c == '/') - continue; // NUL and / are the only characters - // forbidden in POSIX filenames - version (Windows) - if (c < '\x20' || c == '<' || c == '>' || c == ':' || - c == '"' || c == '\\' || c == '|' || c == '?' || c == '*') - continue; // http://msdn.microsoft.com/en-us/library/aa365247(VS.85).aspx - break; - } - - fn ~= c; - } - fn = fn[0..$/2] ~ "_testfile_" ~ fn[$/2..$]; - - test(args, fn); - } -} - -// ============================================================================= -// Everything below this line was part of the old std.process, and most of -// it will be deprecated and removed. -// ============================================================================= - - -/* -Copyright: Copyright The D Language Foundation 2007 - 2009. -License: $(HTTP www.boost.org/LICENSE_1_0.txt, Boost License 1.0). -Authors: $(HTTP digitalmars.com, Walter Bright), - $(HTTP erdani.org, Andrei Alexandrescu), - $(HTTP thecybershadow.net, Vladimir Panteleev) -Source: $(PHOBOSSRC std/_process.d) -*/ -/* - Copyright The D Language Foundation 2007 - 2009. -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) -*/ - - -import core.stdc.errno; -import core.stdc.stdlib; -import core.stdc.string; -import core.thread; - -version (Windows) -{ - import std.file, std.format, std.random; -} -version (Posix) -{ - import core.sys.posix.stdlib; -} - -private void toAStringz(in string[] a, const(char)**az) -{ - import std.string : toStringz; - foreach (string s; a) - { - *az++ = toStringz(s); - } - *az = null; -} - - -/* ========================================================== */ - -//version (Windows) -//{ -// int spawnvp(int mode, string pathname, string[] argv) -// { -// char** argv_ = cast(char**) core.stdc.stdlib.malloc((char*).sizeof * (1 + argv.length)); -// scope(exit) core.stdc.stdlib.free(argv_); -// -// toAStringz(argv, argv_); -// -// return spawnvp(mode, pathname.tempCString(), argv_); -// } -//} - -// Incorporating idea (for spawnvp() on Posix) from Dave Fladebo - -enum { _P_WAIT, _P_NOWAIT, _P_OVERLAY } -version (Windows) extern(C) int spawnvp(int, scope const(char) *, scope const(char*)*); -alias P_WAIT = _P_WAIT; -alias P_NOWAIT = _P_NOWAIT; - -/* ========================================================== */ - -version (StdDdoc) -{ - /** - Replaces the current process by executing a command, `pathname`, with - the arguments in `argv`. - - $(BLUE This function is Posix-Only.) - - Typically, the first element of `argv` is - the command being executed, i.e. $(D argv[0] == pathname). The 'p' - versions of `exec` search the PATH environment variable for $(D - pathname). The 'e' versions additionally take the new process' - environment variables as an array of strings of the form key=value. - - Does not return on success (the current process will have been - replaced). Returns -1 on failure with no indication of the - underlying error. - - Windows_specific: - These functions are only supported on POSIX platforms, as the Windows - operating systems do not provide the ability to overwrite the current - process image with another. In single-threaded programs it is possible - to approximate the effect of `execv*` by using $(LREF spawnProcess) - and terminating the current process once the child process has returned. - For example: - --- - auto commandLine = [ "program", "arg1", "arg2" ]; - version (Posix) - { - execv(commandLine[0], commandLine); - throw new Exception("Failed to execute program"); - } - else version (Windows) - { - import core.stdc.stdlib : _Exit; - _Exit(wait(spawnProcess(commandLine))); - } - --- - This is, however, NOT equivalent to POSIX' `execv*`. For one thing, the - executed program is started as a separate process, with all this entails. - Secondly, in a multithreaded program, other threads will continue to do - work while the current thread is waiting for the child process to complete. - - A better option may sometimes be to terminate the current program immediately - after spawning the child process. This is the behaviour exhibited by the - $(LINK2 http://msdn.microsoft.com/en-us/library/431x4c1w.aspx,`__exec`) - functions in Microsoft's C runtime library, and it is how D's now-deprecated - Windows `execv*` functions work. Example: - --- - auto commandLine = [ "program", "arg1", "arg2" ]; - version (Posix) - { - execv(commandLine[0], commandLine); - throw new Exception("Failed to execute program"); - } - else version (Windows) - { - spawnProcess(commandLine); - import core.stdc.stdlib : _exit; - _exit(0); - } - --- - */ - int execv(in string pathname, in string[] argv); - ///ditto - int execve(in string pathname, in string[] argv, in string[] envp); - /// ditto - int execvp(in string pathname, in string[] argv); - /// ditto - int execvpe(in string pathname, in string[] argv, in string[] envp); -} -else version (Posix) -{ - int execv(in string pathname, in string[] argv) - { - return execv_(pathname, argv); - } - int execve(in string pathname, in string[] argv, in string[] envp) - { - return execve_(pathname, argv, envp); - } - int execvp(in string pathname, in string[] argv) - { - return execvp_(pathname, argv); - } - int execvpe(in string pathname, in string[] argv, in string[] envp) - { - return execvpe_(pathname, argv, envp); - } -} - -// Move these C declarations to druntime if we decide to keep the D wrappers -extern(C) -{ - int execv(scope const(char) *, scope const(char *)*); - int execve(scope const(char)*, scope const(char*)*, scope const(char*)*); - int execvp(scope const(char)*, scope const(char*)*); - version (Windows) int execvpe(scope const(char)*, scope const(char*)*, scope const(char*)*); -} - -private int execv_(in string pathname, in string[] argv) -{ - import core.exception : OutOfMemoryError; - import std.exception : enforce; - auto argv_ = cast(const(char)**)core.stdc.stdlib.malloc((char*).sizeof * (1 + argv.length)); - enforce!OutOfMemoryError(argv_ !is null, "Out of memory in std.process."); - scope(exit) core.stdc.stdlib.free(argv_); - - toAStringz(argv, argv_); - - return execv(pathname.tempCString(), argv_); -} - -private int execve_(in string pathname, in string[] argv, in string[] envp) -{ - import core.exception : OutOfMemoryError; - import std.exception : enforce; - auto argv_ = cast(const(char)**)core.stdc.stdlib.malloc((char*).sizeof * (1 + argv.length)); - enforce!OutOfMemoryError(argv_ !is null, "Out of memory in std.process."); - scope(exit) core.stdc.stdlib.free(argv_); - auto envp_ = cast(const(char)**)core.stdc.stdlib.malloc((char*).sizeof * (1 + envp.length)); - enforce!OutOfMemoryError(envp_ !is null, "Out of memory in std.process."); - scope(exit) core.stdc.stdlib.free(envp_); - - toAStringz(argv, argv_); - toAStringz(envp, envp_); - - return execve(pathname.tempCString(), argv_, envp_); -} - -private int execvp_(in string pathname, in string[] argv) -{ - import core.exception : OutOfMemoryError; - import std.exception : enforce; - auto argv_ = cast(const(char)**)core.stdc.stdlib.malloc((char*).sizeof * (1 + argv.length)); - enforce!OutOfMemoryError(argv_ !is null, "Out of memory in std.process."); - scope(exit) core.stdc.stdlib.free(argv_); - - toAStringz(argv, argv_); - - return execvp(pathname.tempCString(), argv_); -} - -private int execvpe_(in string pathname, in string[] argv, in string[] envp) -{ -version (Posix) -{ - import std.array : split; - import std.conv : to; - // Is pathname rooted? - if (pathname[0] == '/') - { - // Yes, so just call execve() - return execve(pathname, argv, envp); - } - else - { - // No, so must traverse PATHs, looking for first match - string[] envPaths = split( - to!string(core.stdc.stdlib.getenv("PATH")), ":"); - int iRet = 0; - - // Note: if any call to execve() succeeds, this process will cease - // execution, so there's no need to check the execve() result through - // the loop. - - foreach (string pathDir; envPaths) - { - string composite = cast(string) (pathDir ~ "/" ~ pathname); - - iRet = execve(composite, argv, envp); - } - if (0 != iRet) - { - iRet = execve(pathname, argv, envp); - } - - return iRet; - } -} -else version (Windows) -{ - import core.exception : OutOfMemoryError; - import std.exception : enforce; - auto argv_ = cast(const(char)**)core.stdc.stdlib.malloc((char*).sizeof * (1 + argv.length)); - enforce!OutOfMemoryError(argv_ !is null, "Out of memory in std.process."); - scope(exit) core.stdc.stdlib.free(argv_); - auto envp_ = cast(const(char)**)core.stdc.stdlib.malloc((char*).sizeof * (1 + envp.length)); - enforce!OutOfMemoryError(envp_ !is null, "Out of memory in std.process."); - scope(exit) core.stdc.stdlib.free(envp_); - - toAStringz(argv, argv_); - toAStringz(envp, envp_); - - return execvpe(pathname.tempCString(), argv_, envp_); -} -else -{ - static assert(0); -} // version -} - -version (StdDdoc) -{ - /**************************************** - * Start up the browser and set it to viewing the page at url. - */ - void browse(scope const(char)[] url); -} -else -version (Windows) -{ - import core.sys.windows.shellapi, core.sys.windows.winuser; - - pragma(lib,"shell32.lib"); - - void browse(scope const(char)[] url) nothrow @nogc @trusted - { - ShellExecuteW(null, "open", url.tempCStringW(), null, null, SW_SHOWNORMAL); - } -} -else version (Posix) -{ - import core.stdc.stdio; - import core.stdc.string; - import core.sys.posix.unistd; - - void browse(scope const(char)[] url) nothrow @nogc @safe - { - const buffer = url.tempCString(); // Retain buffer until end of scope - const(char)*[3] args; - - // Trusted because it's called with a zero-terminated literal - const(char)* browser = (() @trusted => core.stdc.stdlib.getenv("BROWSER"))(); - if (browser) - { - // String already zero-terminated - browser = (() @trusted => strdup(browser))(); - args[0] = browser; - } - else - { - version (OSX) - { - args[0] = "open"; - } - else - { - //args[0] = "x-www-browser"; // doesn't work on some systems - args[0] = "xdg-open"; - } - } - - args[1] = buffer; - args[2] = null; - - auto childpid = core.sys.posix.unistd.fork(); - if (childpid == 0) - { - // Trusted because args and all entries are always zero-terminated - (() @trusted => - core.sys.posix.unistd.execvp(args[0], &args[0]) || - perror(args[0]) // failed to execute - )(); - return; - } - if (browser) - // Trusted because it's allocated via strdup above - (() @trusted => free(cast(void*) browser))(); - - version (StdUnittest) - { - // Verify that the test script actually suceeds - int status; - const check = (() @trusted => waitpid(childpid, &status, 0))(); - assert(check != -1); - assert(status == 0); - } - } -} -else - static assert(0, "os not supported"); - -// Verify attributes are consistent between all implementations -@safe @nogc nothrow unittest -{ - if (false) - browse(""); -} - -version (Windows) { /* Doesn't use BROWSER */ } -else -@system unittest -{ - import std.conv : text; - import std.range : repeat; - immutable string url = text("http://", repeat('x', 249)); - - TestScript prog = `if [ "$1" != "` ~ url ~ `" ]; then exit 1; fi`; - environment["BROWSER"] = prog.path; - browse(url); -} diff --git a/phobos/std/random.d b/phobos/std/random.d deleted file mode 100644 index 87e63f3..0000000 --- a/phobos/std/random.d +++ /dev/null @@ -1,4336 +0,0 @@ -// Written in the D programming language. - -/** -Facilities for random number generation. - -$(SCRIPT inhibitQuickIndex = 1;) -$(DIVC quickindex, -$(BOOKTABLE, -$(TR $(TH Category) $(TH Functions)) -$(TR $(TD Uniform sampling) $(TD - $(LREF uniform) - $(LREF uniform01) - $(LREF uniformDistribution) -)) -$(TR $(TD Element sampling) $(TD - $(LREF choice) - $(LREF dice) -)) -$(TR $(TD Range sampling) $(TD - $(LREF randomCover) - $(LREF randomSample) -)) -$(TR $(TD Default Random Engines) $(TD - $(LREF rndGen) - $(LREF Random) - $(LREF unpredictableSeed) -)) -$(TR $(TD Linear Congruential Engines) $(TD - $(LREF MinstdRand) - $(LREF MinstdRand0) - $(LREF LinearCongruentialEngine) -)) -$(TR $(TD Mersenne Twister Engines) $(TD - $(LREF Mt19937) - $(LREF Mt19937_64) - $(LREF MersenneTwisterEngine) -)) -$(TR $(TD Xorshift Engines) $(TD - $(LREF Xorshift) - $(LREF XorshiftEngine) - $(LREF Xorshift32) - $(LREF Xorshift64) - $(LREF Xorshift96) - $(LREF Xorshift128) - $(LREF Xorshift160) - $(LREF Xorshift192) -)) -$(TR $(TD Shuffle) $(TD - $(LREF partialShuffle) - $(LREF randomShuffle) -)) -$(TR $(TD Traits) $(TD - $(LREF isSeedable) - $(LREF isUniformRNG) -)) -)) - -$(RED Disclaimer:) The random number generators and API provided in this -module are not designed to be cryptographically secure, and are therefore -unsuitable for cryptographic or security-related purposes such as generating -authentication tokens or network sequence numbers. For such needs, please use a -reputable cryptographic library instead. - -The new-style generator objects hold their own state so they are -immune of threading issues. The generators feature a number of -well-known and well-documented methods of generating random -numbers. An overall fast and reliable means to generate random numbers -is the $(D_PARAM Mt19937) generator, which derives its name from -"$(LINK2 https://en.wikipedia.org/wiki/Mersenne_Twister, Mersenne Twister) -with a period of 2 to the power of -19937". In memory-constrained situations, -$(LINK2 https://en.wikipedia.org/wiki/Linear_congruential_generator, -linear congruential generators) such as `MinstdRand0` and `MinstdRand` might be -useful. The standard library provides an alias $(D_PARAM Random) for -whichever generator it considers the most fit for the target -environment. - -In addition to random number generators, this module features -distributions, which skew a generator's output statistical -distribution in various ways. So far the uniform distribution for -integers and real numbers have been implemented. - -Source: $(PHOBOSSRC std/random.d) - -Macros: - -Copyright: Copyright Andrei Alexandrescu 2008 - 2009, Joseph Rushton Wakeling 2012. -License: $(HTTP www.boost.org/LICENSE_1_0.txt, Boost License 1.0). -Authors: $(HTTP erdani.org, Andrei Alexandrescu) - Masahiro Nakagawa (Xorshift random generator) - $(HTTP braingam.es, Joseph Rushton Wakeling) (Algorithm D for random sampling) - Ilya Yaroshenko (Mersenne Twister implementation, adapted from $(HTTPS github.com/libmir/mir-random, mir-random)) -Credits: The entire random number library architecture is derived from the - excellent $(HTTP open-std.org/jtc1/sc22/wg21/docs/papers/2007/n2461.pdf, C++0X) - random number facility proposed by Jens Maurer and contributed to by - researchers at the Fermi laboratory (excluding Xorshift). -*/ -/* - Copyright Andrei Alexandrescu 2008 - 2009. -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.random; - - -import std.range.primitives; -import std.traits; - -/// -@safe unittest -{ - import std.algorithm.comparison : among, equal; - import std.range : iota; - - // seed a random generator with a constant - auto rnd = Random(42); - - // Generate a uniformly-distributed integer in the range [0, 14] - // If no random generator is passed, the global `rndGen` would be used - auto i = uniform(0, 15, rnd); - assert(i >= 0 && i < 15); - - // Generate a uniformly-distributed real in the range [0, 100) - auto r = uniform(0.0L, 100.0L, rnd); - assert(r >= 0 && r < 100); - - // Sample from a custom type - enum Fruit { apple, mango, pear } - auto f = rnd.uniform!Fruit; - with(Fruit) - assert(f.among(apple, mango, pear)); - - // Generate a 32-bit random number - auto u = uniform!uint(rnd); - static assert(is(typeof(u) == uint)); - - // Generate a random number in the range in the range [0, 1) - auto u2 = uniform01(rnd); - assert(u2 >= 0 && u2 < 1); - - // Select an element randomly - auto el = 10.iota.choice(rnd); - assert(0 <= el && el < 10); - - // Throw a dice with custom proportions - // 0: 20%, 1: 10%, 2: 60% - auto val = rnd.dice(0.2, 0.1, 0.6); - assert(0 <= val && val <= 2); - - auto rnd2 = MinstdRand0(42); - - // Select a random subsample from a range - assert(10.iota.randomSample(3, rnd2).equal([7, 8, 9])); - - // Cover all elements in an array in random order - version (D_LP64) // https://issues.dlang.org/show_bug.cgi?id=15147 - assert(10.iota.randomCover(rnd2).equal([7, 4, 2, 0, 1, 6, 8, 3, 9, 5])); - else - assert(10.iota.randomCover(rnd2).equal([4, 8, 7, 3, 5, 9, 2, 6, 0, 1])); - - // Shuffle an array - version (D_LP64) // https://issues.dlang.org/show_bug.cgi?id=15147 - assert([0, 1, 2, 4, 5].randomShuffle(rnd2).equal([2, 0, 4, 5, 1])); - else - assert([0, 1, 2, 4, 5].randomShuffle(rnd2).equal([4, 2, 5, 0, 1])); -} - -version (OSX) - version = Darwin; -else version (iOS) - version = Darwin; -else version (TVOS) - version = Darwin; -else version (WatchOS) - version = Darwin; - -version (D_InlineAsm_X86) version = InlineAsm_X86_Any; -version (D_InlineAsm_X86_64) version = InlineAsm_X86_Any; - -version (StdUnittest) -{ - static import std.meta; - package alias Xorshift64_64 = XorshiftEngine!(ulong, 64, -12, 25, -27); - package alias Xorshift128_64 = XorshiftEngine!(ulong, 128, 23, -18, -5); - package alias PseudoRngTypes = std.meta.AliasSeq!(MinstdRand0, MinstdRand, Mt19937, Xorshift32, Xorshift64, - Xorshift96, Xorshift128, Xorshift160, Xorshift192, - Xorshift64_64, Xorshift128_64); -} - -// Segments of the code in this file Copyright (c) 1997 by Rick Booth -// From "Inner Loops" by Rick Booth, Addison-Wesley - -// Work derived from: - -/* - A C-program for MT19937, with initialization improved 2002/1/26. - Coded by Takuji Nishimura and Makoto Matsumoto. - - Before using, initialize the state by using init_genrand(seed) - or init_by_array(init_key, key_length). - - Copyright (C) 1997 - 2002, Makoto Matsumoto and Takuji Nishimura, - All rights reserved. - - Redistribution and use in source and binary forms, with or without - modification, are permitted provided that the following conditions - are met: - - 1. Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. - - 2. Redistributions in binary form must reproduce the above copyright - notice, this list of conditions and the following disclaimer in the - documentation and/or other materials provided with the distribution. - - 3. The names of its contributors may not be used to endorse or promote - products derived from this software without specific prior written - permission. - - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR - CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, - EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR - PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF - LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING - NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - - - Any feedback is very welcome. - http://www.math.sci.hiroshima-u.ac.jp/~m-mat/MT/emt.html - email: m-mat @ math.sci.hiroshima-u.ac.jp (remove space) -*/ - -/** - * Test if Rng is a random-number generator. The overload - * taking a ElementType also makes sure that the Rng generates - * values of that type. - * - * A random-number generator has at least the following features: - * $(UL - * $(LI it's an InputRange) - * $(LI it has a 'bool isUniformRandom' field readable in CTFE) - * ) - */ -template isUniformRNG(Rng, ElementType) -{ - enum bool isUniformRNG = .isUniformRNG!Rng && - is(std.range.primitives.ElementType!Rng == ElementType); -} - -/** - * ditto - */ -template isUniformRNG(Rng) -{ - enum bool isUniformRNG = isInputRange!Rng && - is(typeof( - { - static assert(Rng.isUniformRandom); //tag - })); -} - -/// -@safe unittest -{ - struct NoRng - { - @property uint front() {return 0;} - @property bool empty() {return false;} - void popFront() {} - } - static assert(!isUniformRNG!(NoRng)); - - struct validRng - { - @property uint front() {return 0;} - @property bool empty() {return false;} - void popFront() {} - - enum isUniformRandom = true; - } - static assert(isUniformRNG!(validRng, uint)); - static assert(isUniformRNG!(validRng)); -} - -@safe unittest -{ - // two-argument predicate should not require @property on `front` - // https://issues.dlang.org/show_bug.cgi?id=19837 - struct Rng - { - float front() {return 0;} - void popFront() {} - enum empty = false; - enum isUniformRandom = true; - } - static assert(isUniformRNG!(Rng, float)); -} - -/** - * Test if Rng is seedable. The overload - * taking a SeedType also makes sure that the Rng can be seeded with SeedType. - * - * A seedable random-number generator has the following additional features: - * $(UL - * $(LI it has a 'seed(ElementType)' function) - * ) - */ -template isSeedable(Rng, SeedType) -{ - enum bool isSeedable = isUniformRNG!(Rng) && - is(typeof( - { - Rng r = void; // can define a Rng object - SeedType s = void; - r.seed(s); // can seed a Rng - })); -} - -///ditto -template isSeedable(Rng) -{ - enum bool isSeedable = isUniformRNG!Rng && - is(typeof( - { - Rng r = void; // can define a Rng object - alias SeedType = typeof(r.front); - SeedType s = void; - r.seed(s); // can seed a Rng - })); -} - -/// -@safe unittest -{ - struct validRng - { - @property uint front() {return 0;} - @property bool empty() {return false;} - void popFront() {} - - enum isUniformRandom = true; - } - static assert(!isSeedable!(validRng, uint)); - static assert(!isSeedable!(validRng)); - - struct seedRng - { - @property uint front() {return 0;} - @property bool empty() {return false;} - void popFront() {} - void seed(uint val){} - enum isUniformRandom = true; - } - static assert(isSeedable!(seedRng, uint)); - static assert(!isSeedable!(seedRng, ulong)); - static assert(isSeedable!(seedRng)); -} - -@safe @nogc pure nothrow unittest -{ - struct NoRng - { - @property uint front() {return 0;} - @property bool empty() {return false;} - void popFront() {} - } - static assert(!isUniformRNG!(NoRng, uint)); - static assert(!isUniformRNG!(NoRng)); - static assert(!isSeedable!(NoRng, uint)); - static assert(!isSeedable!(NoRng)); - - struct NoRng2 - { - @property uint front() {return 0;} - @property bool empty() {return false;} - void popFront() {} - - enum isUniformRandom = false; - } - static assert(!isUniformRNG!(NoRng2, uint)); - static assert(!isUniformRNG!(NoRng2)); - static assert(!isSeedable!(NoRng2, uint)); - static assert(!isSeedable!(NoRng2)); - - struct NoRng3 - { - @property bool empty() {return false;} - void popFront() {} - - enum isUniformRandom = true; - } - static assert(!isUniformRNG!(NoRng3, uint)); - static assert(!isUniformRNG!(NoRng3)); - static assert(!isSeedable!(NoRng3, uint)); - static assert(!isSeedable!(NoRng3)); - - struct validRng - { - @property uint front() {return 0;} - @property bool empty() {return false;} - void popFront() {} - - enum isUniformRandom = true; - } - static assert(isUniformRNG!(validRng, uint)); - static assert(isUniformRNG!(validRng)); - static assert(!isSeedable!(validRng, uint)); - static assert(!isSeedable!(validRng)); - - struct seedRng - { - @property uint front() {return 0;} - @property bool empty() {return false;} - void popFront() {} - void seed(uint val){} - enum isUniformRandom = true; - } - static assert(isUniformRNG!(seedRng, uint)); - static assert(isUniformRNG!(seedRng)); - static assert(isSeedable!(seedRng, uint)); - static assert(!isSeedable!(seedRng, ulong)); - static assert(isSeedable!(seedRng)); -} - -/** -Linear Congruential generator. When m = 0, no modulus is used. - */ -struct LinearCongruentialEngine(UIntType, UIntType a, UIntType c, UIntType m) -if (isUnsigned!UIntType) -{ - ///Mark this as a Rng - enum bool isUniformRandom = true; - /// Does this generator have a fixed range? ($(D_PARAM true)). - enum bool hasFixedRange = true; - /// Lowest generated value (`1` if $(D c == 0), `0` otherwise). - enum UIntType min = ( c == 0 ? 1 : 0 ); - /// Highest generated value ($(D modulus - 1)). - enum UIntType max = m - 1; -/** -The parameters of this distribution. The random number is $(D_PARAM x -= (x * multipler + increment) % modulus). - */ - enum UIntType multiplier = a; - ///ditto - enum UIntType increment = c; - ///ditto - enum UIntType modulus = m; - - static assert(isIntegral!(UIntType)); - static assert(m == 0 || a < m); - static assert(m == 0 || c < m); - static assert(m == 0 || - (cast(ulong) a * (m-1) + c) % m == (c < a ? c - a + m : c - a)); - - // Check for maximum range - private static ulong gcd(ulong a, ulong b) @safe pure nothrow @nogc - { - while (b) - { - auto t = b; - b = a % b; - a = t; - } - return a; - } - - private static ulong primeFactorsOnly(ulong n) @safe pure nothrow @nogc - { - ulong result = 1; - ulong iter = 2; - for (; n >= iter * iter; iter += 2 - (iter == 2)) - { - if (n % iter) continue; - result *= iter; - do - { - n /= iter; - } while (n % iter == 0); - } - return result * n; - } - - @safe pure nothrow unittest - { - static assert(primeFactorsOnly(100) == 10); - //writeln(primeFactorsOnly(11)); - static assert(primeFactorsOnly(11) == 11); - static assert(primeFactorsOnly(7 * 7 * 7 * 11 * 15 * 11) == 7 * 11 * 15); - static assert(primeFactorsOnly(129 * 2) == 129 * 2); - // enum x = primeFactorsOnly(7 * 7 * 7 * 11 * 15); - // static assert(x == 7 * 11 * 15); - } - - private static bool properLinearCongruentialParameters(ulong m, - ulong a, ulong c) @safe pure nothrow @nogc - { - if (m == 0) - { - static if (is(UIntType == uint)) - { - // Assume m is uint.max + 1 - m = (1uL << 32); - } - else - { - return false; - } - } - // Bounds checking - if (a == 0 || a >= m || c >= m) return false; - // c and m are relatively prime - if (c > 0 && gcd(c, m) != 1) return false; - // a - 1 is divisible by all prime factors of m - if ((a - 1) % primeFactorsOnly(m)) return false; - // if a - 1 is multiple of 4, then m is a multiple of 4 too. - if ((a - 1) % 4 == 0 && m % 4) return false; - // Passed all tests - return true; - } - - // check here - static assert(c == 0 || properLinearCongruentialParameters(m, a, c), - "Incorrect instantiation of LinearCongruentialEngine"); - -/** -Constructs a $(D_PARAM LinearCongruentialEngine) generator seeded with -`x0`. - */ - this(UIntType x0) @safe pure nothrow @nogc - { - seed(x0); - } - -/** - (Re)seeds the generator. -*/ - void seed(UIntType x0 = 1) @safe pure nothrow @nogc - { - _x = modulus ? (x0 % modulus) : x0; - static if (c == 0) - { - //Necessary to prevent generator from outputting an endless series of zeroes. - if (_x == 0) - _x = max; - } - popFront(); - } - -/** - Advances the random sequence. -*/ - void popFront() @safe pure nothrow @nogc - { - static if (m) - { - static if (is(UIntType == uint) && m == uint.max) - { - immutable ulong - x = (cast(ulong) a * _x + c), - v = x >> 32, - w = x & uint.max; - immutable y = cast(uint)(v + w); - _x = (y < v || y == uint.max) ? (y + 1) : y; - } - else static if (is(UIntType == uint) && m == int.max) - { - immutable ulong - x = (cast(ulong) a * _x + c), - v = x >> 31, - w = x & int.max; - immutable uint y = cast(uint)(v + w); - _x = (y >= int.max) ? (y - int.max) : y; - } - else - { - _x = cast(UIntType) ((cast(ulong) a * _x + c) % m); - } - } - else - { - _x = a * _x + c; - } - } - -/** - Returns the current number in the random sequence. -*/ - @property UIntType front() const @safe pure nothrow @nogc - { - return _x; - } - -/// - @property typeof(this) save() const @safe pure nothrow @nogc - { - return this; - } - -/** -Always `false` (random generators are infinite ranges). - */ - enum bool empty = false; - - // https://issues.dlang.org/show_bug.cgi?id=21610 - static if (m) - { - private UIntType _x = (a + c) % m; - } - else - { - private UIntType _x = a + c; - } -} - -/// Declare your own linear congruential engine -@safe unittest -{ - alias CPP11LCG = LinearCongruentialEngine!(uint, 48271, 0, 2_147_483_647); - - // seed with a constant - auto rnd = CPP11LCG(42); - auto n = rnd.front; // same for each run - assert(n == 2027382); -} - -/// Declare your own linear congruential engine -@safe unittest -{ - // glibc's LCG - alias GLibcLCG = LinearCongruentialEngine!(uint, 1103515245, 12345, 2_147_483_648); - - // Seed with an unpredictable value - auto rnd = GLibcLCG(unpredictableSeed); - auto n = rnd.front; // different across runs -} - -/// Declare your own linear congruential engine -@safe unittest -{ - // Visual C++'s LCG - alias MSVCLCG = LinearCongruentialEngine!(uint, 214013, 2531011, 0); - - // seed with a constant - auto rnd = MSVCLCG(1); - auto n = rnd.front; // same for each run - assert(n == 2745024); -} - -// Ensure that unseeded LCGs produce correct values -@safe unittest -{ - alias LGE = LinearCongruentialEngine!(uint, 10000, 19682, 19683); - - LGE rnd; - assert(rnd.front == 9999); -} - -/** -Define $(D_PARAM LinearCongruentialEngine) generators with well-chosen -parameters. `MinstdRand0` implements Park and Miller's "minimal -standard" $(HTTP -wikipedia.org/wiki/Park%E2%80%93Miller_random_number_generator, -generator) that uses 16807 for the multiplier. `MinstdRand` -implements a variant that has slightly better spectral behavior by -using the multiplier 48271. Both generators are rather simplistic. - */ -alias MinstdRand0 = LinearCongruentialEngine!(uint, 16_807, 0, 2_147_483_647); -/// ditto -alias MinstdRand = LinearCongruentialEngine!(uint, 48_271, 0, 2_147_483_647); - -/// -@safe @nogc unittest -{ - // seed with a constant - auto rnd0 = MinstdRand0(1); - auto n = rnd0.front; - // same for each run - assert(n == 16807); - - // Seed with an unpredictable value - rnd0.seed(unpredictableSeed); - n = rnd0.front; // different across runs -} - -@safe @nogc unittest -{ - import std.range; - static assert(isForwardRange!MinstdRand); - static assert(isUniformRNG!MinstdRand); - static assert(isUniformRNG!MinstdRand0); - static assert(isUniformRNG!(MinstdRand, uint)); - static assert(isUniformRNG!(MinstdRand0, uint)); - static assert(isSeedable!MinstdRand); - static assert(isSeedable!MinstdRand0); - static assert(isSeedable!(MinstdRand, uint)); - static assert(isSeedable!(MinstdRand0, uint)); - - // The correct numbers are taken from The Database of Integer Sequences - // http://www.research.att.com/~njas/sequences/eisBTfry00128.txt - enum ulong[20] checking0 = [ - 16807UL,282475249,1622650073,984943658,1144108930,470211272, - 101027544,1457850878,1458777923,2007237709,823564440,1115438165, - 1784484492,74243042,114807987,1137522503,1441282327,16531729, - 823378840,143542612 ]; - //auto rnd0 = MinstdRand0(1); - MinstdRand0 rnd0; - - foreach (e; checking0) - { - assert(rnd0.front == e); - rnd0.popFront(); - } - // Test the 10000th invocation - // Correct value taken from: - // http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2007/n2461.pdf - rnd0.seed(); - popFrontN(rnd0, 9999); - assert(rnd0.front == 1043618065); - - // Test MinstdRand - enum ulong[6] checking = [48271UL,182605794,1291394886,1914720637,2078669041, - 407355683]; - //auto rnd = MinstdRand(1); - MinstdRand rnd; - foreach (e; checking) - { - assert(rnd.front == e); - rnd.popFront(); - } - - // Test the 10000th invocation - // Correct value taken from: - // http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2007/n2461.pdf - rnd.seed(); - popFrontN(rnd, 9999); - assert(rnd.front == 399268537); - - // Check .save works - static foreach (Type; std.meta.AliasSeq!(MinstdRand0, MinstdRand)) - {{ - auto rnd1 = Type(123_456_789); - rnd1.popFront(); - // https://issues.dlang.org/show_bug.cgi?id=15853 - auto rnd2 = ((const ref Type a) => a.save())(rnd1); - assert(rnd1 == rnd2); - // Enable next test when RNGs are reference types - version (none) { assert(rnd1 !is rnd2); } - for (auto i = 0; i < 3; i++, rnd1.popFront, rnd2.popFront) - assert(rnd1.front() == rnd2.front()); - }} -} - -@safe @nogc unittest -{ - auto rnd0 = MinstdRand0(MinstdRand0.modulus); - auto n = rnd0.front; - rnd0.popFront(); - assert(n != rnd0.front); -} - -/** -The $(LINK2 https://en.wikipedia.org/wiki/Mersenne_Twister, Mersenne Twister) generator. - */ -struct MersenneTwisterEngine(UIntType, size_t w, size_t n, size_t m, size_t r, - UIntType a, size_t u, UIntType d, size_t s, - UIntType b, size_t t, - UIntType c, size_t l, UIntType f) -if (isUnsigned!UIntType) -{ - static assert(0 < w && w <= UIntType.sizeof * 8); - static assert(1 <= m && m <= n); - static assert(0 <= r && 0 <= u && 0 <= s && 0 <= t && 0 <= l); - static assert(r <= w && u <= w && s <= w && t <= w && l <= w); - static assert(0 <= a && 0 <= b && 0 <= c); - static assert(n <= ptrdiff_t.max); - - ///Mark this as a Rng - enum bool isUniformRandom = true; - -/** -Parameters for the generator. -*/ - enum size_t wordSize = w; - enum size_t stateSize = n; /// ditto - enum size_t shiftSize = m; /// ditto - enum size_t maskBits = r; /// ditto - enum UIntType xorMask = a; /// ditto - enum size_t temperingU = u; /// ditto - enum UIntType temperingD = d; /// ditto - enum size_t temperingS = s; /// ditto - enum UIntType temperingB = b; /// ditto - enum size_t temperingT = t; /// ditto - enum UIntType temperingC = c; /// ditto - enum size_t temperingL = l; /// ditto - enum UIntType initializationMultiplier = f; /// ditto - - /// Smallest generated value (0). - enum UIntType min = 0; - /// Largest generated value. - enum UIntType max = UIntType.max >> (UIntType.sizeof * 8u - w); - // note, `max` also serves as a bitmask for the lowest `w` bits - static assert(a <= max && b <= max && c <= max && f <= max); - - /// The default seed value. - enum UIntType defaultSeed = 5489u; - - // Bitmasks used in the 'twist' part of the algorithm - private enum UIntType lowerMask = (cast(UIntType) 1u << r) - 1, - upperMask = (~lowerMask) & max; - - /* - Collection of all state variables - used by the generator - */ - private struct State - { - /* - State array of the generator. This - is iterated through backwards (from - last element to first), providing a - few extra compiler optimizations by - comparison to the forward iteration - used in most implementations. - */ - UIntType[n] data; - - /* - Cached copy of most recently updated - element of `data` state array, ready - to be tempered to generate next - `front` value - */ - UIntType z; - - /* - Most recently generated random variate - */ - UIntType front; - - /* - Index of the entry in the `data` - state array that will be twisted - in the next `popFront()` call - */ - size_t index; - } - - /* - State variables used by the generator; - initialized to values equivalent to - explicitly seeding the generator with - `defaultSeed` - */ - private State state = defaultState(); - /* NOTE: the above is a workaround to ensure - backwards compatibility with the original - implementation, which permitted implicit - construction. With `@disable this();` - it would not be necessary. */ - -/** - Constructs a MersenneTwisterEngine object. -*/ - this(UIntType value) @safe pure nothrow @nogc - { - seed(value); - } - - /** - Generates the default initial state for a Mersenne - Twister; equivalent to the internal state obtained - by calling `seed(defaultSeed)` - */ - private static State defaultState() @safe pure nothrow @nogc - { - if (!__ctfe) assert(false); - State mtState; - seedImpl(defaultSeed, mtState); - return mtState; - } - -/** - Seeds a MersenneTwisterEngine object. - Note: - This seed function gives 2^w starting points (the lowest w bits of - the value provided will be used). To allow the RNG to be started - in any one of its internal states use the seed overload taking an - InputRange. -*/ - void seed()(UIntType value = defaultSeed) @safe pure nothrow @nogc - { - this.seedImpl(value, this.state); - } - - /** - Implementation of the seeding mechanism, which - can be used with an arbitrary `State` instance - */ - private static void seedImpl(UIntType value, ref State mtState) @nogc - { - mtState.data[$ - 1] = value; - static if (max != UIntType.max) - { - mtState.data[$ - 1] &= max; - } - - foreach_reverse (size_t i, ref e; mtState.data[0 .. $ - 1]) - { - e = f * (mtState.data[i + 1] ^ (mtState.data[i + 1] >> (w - 2))) + cast(UIntType)(n - (i + 1)); - static if (max != UIntType.max) - { - e &= max; - } - } - - mtState.index = n - 1; - - /* double popFront() to guarantee both `mtState.z` - and `mtState.front` are derived from the newly - set values in `mtState.data` */ - MersenneTwisterEngine.popFrontImpl(mtState); - MersenneTwisterEngine.popFrontImpl(mtState); - } - -/** - Seeds a MersenneTwisterEngine object using an InputRange. - - Throws: - `Exception` if the InputRange didn't provide enough elements to seed the generator. - The number of elements required is the 'n' template parameter of the MersenneTwisterEngine struct. - */ - void seed(T)(T range) if (isInputRange!T && is(immutable ElementType!T == immutable UIntType)) - { - this.seedImpl(range, this.state); - } - - /** - Implementation of the range-based seeding mechanism, - which can be used with an arbitrary `State` instance - */ - private static void seedImpl(T)(T range, ref State mtState) - if (isInputRange!T && is(immutable ElementType!T == immutable UIntType)) - { - size_t j; - for (j = 0; j < n && !range.empty; ++j, range.popFront()) - { - ptrdiff_t idx = n - j - 1; - mtState.data[idx] = range.front; - } - - mtState.index = n - 1; - - if (range.empty && j < n) - { - import core.internal.string : UnsignedStringBuf, unsignedToTempString; - - UnsignedStringBuf buf = void; - string s = "MersenneTwisterEngine.seed: Input range didn't provide enough elements: Need "; - s ~= unsignedToTempString(n, buf) ~ " elements."; - throw new Exception(s); - } - - /* double popFront() to guarantee both `mtState.z` - and `mtState.front` are derived from the newly - set values in `mtState.data` */ - MersenneTwisterEngine.popFrontImpl(mtState); - MersenneTwisterEngine.popFrontImpl(mtState); - } - -/** - Advances the generator. -*/ - void popFront() @safe pure nothrow @nogc - { - this.popFrontImpl(this.state); - } - - /* - Internal implementation of `popFront()`, which - can be used with an arbitrary `State` instance - */ - private static void popFrontImpl(ref State mtState) @nogc - { - /* This function blends two nominally independent - processes: (i) calculation of the next random - variate `mtState.front` from the cached previous - `data` entry `z`, and (ii) updating the value - of `data[index]` and `mtState.z` and advancing - the `index` value to the next in sequence. - - By interweaving the steps involved in these - procedures, rather than performing each of - them separately in sequence, the variables - are kept 'hot' in CPU registers, allowing - for significantly faster performance. */ - ptrdiff_t index = mtState.index; - ptrdiff_t next = index - 1; - if (next < 0) - next = n - 1; - auto z = mtState.z; - ptrdiff_t conj = index - m; - if (conj < 0) - conj = index - m + n; - - static if (d == UIntType.max) - { - z ^= (z >> u); - } - else - { - z ^= (z >> u) & d; - } - - auto q = mtState.data[index] & upperMask; - auto p = mtState.data[next] & lowerMask; - z ^= (z << s) & b; - auto y = q | p; - auto x = y >> 1; - z ^= (z << t) & c; - if (y & 1) - x ^= a; - auto e = mtState.data[conj] ^ x; - z ^= (z >> l); - mtState.z = mtState.data[index] = e; - mtState.index = next; - - /* technically we should take the lowest `w` - bits here, but if the tempering bitmasks - `b` and `c` are set correctly, this should - be unnecessary */ - mtState.front = z; - } - -/** - Returns the current random value. - */ - @property UIntType front() @safe const pure nothrow @nogc - { - return this.state.front; - } - -/// - @property typeof(this) save() @safe const pure nothrow @nogc - { - return this; - } - -/** -Always `false`. - */ - enum bool empty = false; -} - -/// -@safe unittest -{ - // seed with a constant - Mt19937 gen; - auto n = gen.front; // same for each run - assert(n == 3499211612); - - // Seed with an unpredictable value - gen.seed(unpredictableSeed); - n = gen.front; // different across runs -} - -/** -A `MersenneTwisterEngine` instantiated with the parameters of the -original engine $(HTTP math.sci.hiroshima-u.ac.jp/~m-mat/MT/emt.html, -MT19937), generating uniformly-distributed 32-bit numbers with a -period of 2 to the power of 19937. Recommended for random number -generation unless memory is severely restricted, in which case a $(LREF -LinearCongruentialEngine) would be the generator of choice. - */ -alias Mt19937 = MersenneTwisterEngine!(uint, 32, 624, 397, 31, - 0x9908b0df, 11, 0xffffffff, 7, - 0x9d2c5680, 15, - 0xefc60000, 18, 1_812_433_253); - -/// -@safe @nogc unittest -{ - // seed with a constant - Mt19937 gen; - auto n = gen.front; // same for each run - assert(n == 3499211612); - - // Seed with an unpredictable value - gen.seed(unpredictableSeed); - n = gen.front; // different across runs -} - -@safe nothrow unittest -{ - import std.algorithm; - import std.range; - static assert(isUniformRNG!Mt19937); - static assert(isUniformRNG!(Mt19937, uint)); - static assert(isSeedable!Mt19937); - static assert(isSeedable!(Mt19937, uint)); - static assert(isSeedable!(Mt19937, typeof(map!((a) => unpredictableSeed)(repeat(0))))); - Mt19937 gen; - assert(gen.front == 3499211612); - popFrontN(gen, 9999); - assert(gen.front == 4123659995); - try { gen.seed(iota(624u)); } catch (Exception) { assert(false); } - assert(gen.front == 3708921088u); - popFrontN(gen, 9999); - assert(gen.front == 165737292u); -} - -/** -A `MersenneTwisterEngine` instantiated with the parameters of the -original engine $(HTTP en.wikipedia.org/wiki/Mersenne_Twister, -MT19937-64), generating uniformly-distributed 64-bit numbers with a -period of 2 to the power of 19937. -*/ -alias Mt19937_64 = MersenneTwisterEngine!(ulong, 64, 312, 156, 31, - 0xb5026f5aa96619e9, 29, 0x5555555555555555, 17, - 0x71d67fffeda60000, 37, - 0xfff7eee000000000, 43, 6_364_136_223_846_793_005); - -/// -@safe @nogc unittest -{ - // Seed with a constant - auto gen = Mt19937_64(12345); - auto n = gen.front; // same for each run - assert(n == 6597103971274460346); - - // Seed with an unpredictable value - gen.seed(unpredictableSeed!ulong); - n = gen.front; // different across runs -} - -@safe nothrow unittest -{ - import std.algorithm; - import std.range; - static assert(isUniformRNG!Mt19937_64); - static assert(isUniformRNG!(Mt19937_64, ulong)); - static assert(isSeedable!Mt19937_64); - static assert(isSeedable!(Mt19937_64, ulong)); - static assert(isSeedable!(Mt19937_64, typeof(map!((a) => unpredictableSeed!ulong)(repeat(0))))); - Mt19937_64 gen; - assert(gen.front == 14514284786278117030uL); - popFrontN(gen, 9999); - assert(gen.front == 9981545732273789042uL); - try { gen.seed(iota(312uL)); } catch (Exception) { assert(false); } - assert(gen.front == 14660652410669508483uL); - popFrontN(gen, 9999); - assert(gen.front == 15956361063660440239uL); -} - -@safe unittest -{ - import std.algorithm; - import std.exception; - import std.range; - - Mt19937 gen; - - assertThrown(gen.seed(map!((a) => 123_456_789U)(repeat(0, 623)))); - - gen.seed(123_456_789U.repeat(624)); - //infinite Range - gen.seed(123_456_789U.repeat); -} - -@safe @nogc pure nothrow unittest -{ - uint a, b; - { - Mt19937 gen; - a = gen.front; - } - { - Mt19937 gen; - gen.popFront(); - //popFrontN(gen, 1); // skip 1 element - b = gen.front; - } - assert(a != b); -} - -@safe @nogc unittest -{ - // Check .save works - static foreach (Type; std.meta.AliasSeq!(Mt19937, Mt19937_64)) - {{ - auto gen1 = Type(123_456_789); - gen1.popFront(); - // https://issues.dlang.org/show_bug.cgi?id=15853 - auto gen2 = ((const ref Type a) => a.save())(gen1); - assert(gen1 == gen2); // Danger, Will Robinson -- no opEquals for MT - // Enable next test when RNGs are reference types - version (none) { assert(gen1 !is gen2); } - for (auto i = 0; i < 100; i++, gen1.popFront, gen2.popFront) - assert(gen1.front() == gen2.front()); - }} -} - -// https://issues.dlang.org/show_bug.cgi?id=11690 -@safe @nogc pure nothrow unittest -{ - alias MT(UIntType, uint w) = MersenneTwisterEngine!(UIntType, w, 624, 397, 31, - 0x9908b0df, 11, 0xffffffff, 7, - 0x9d2c5680, 15, - 0xefc60000, 18, 1812433253); - - static immutable ulong[] expectedFirstValue = [3499211612uL, 3499211612uL, - 171143175841277uL, 1145028863177033374uL]; - - static immutable ulong[] expected10kValue = [4123659995uL, 4123659995uL, - 51991688252792uL, 3031481165133029945uL]; - - static foreach (i, R; std.meta.AliasSeq!(MT!(uint, 32), MT!(ulong, 32), MT!(ulong, 48), MT!(ulong, 64))) - {{ - auto a = R(); - a.seed(a.defaultSeed); // checks that some alternative paths in `seed` are utilized - assert(a.front == expectedFirstValue[i]); - a.popFrontN(9999); - assert(a.front == expected10kValue[i]); - }} -} - -/++ -Xorshift generator. -Implemented according to $(HTTP www.jstatsoft.org/v08/i14/paper, Xorshift RNGs) -(Marsaglia, 2003) when the size is small. For larger sizes the generator -uses Sebastino Vigna's optimization of using an index to avoid needing -to rotate the internal array. - -Period is `2 ^^ nbits - 1` except for a legacy 192-bit uint version (see -note below). - -Params: - UIntType = Word size of this xorshift generator and the return type - of `opCall`. - nbits = The number of bits of state of this generator. This must be - a positive multiple of the size in bits of UIntType. If - nbits is large this struct may occupy slightly more memory - than this so it can use a circular counter instead of - shifting the entire array. - sa = The direction and magnitude of the 1st shift. Positive - means left, negative means right. - sb = The direction and magnitude of the 2nd shift. Positive - means left, negative means right. - sc = The direction and magnitude of the 3rd shift. Positive - means left, negative means right. - -Note: -For historical compatibility when `nbits == 192` and `UIntType` is `uint` -a legacy hybrid PRNG is used consisting of a 160-bit xorshift combined -with a 32-bit counter. This combined generator has period equal to the -least common multiple of `2^^160 - 1` and `2^^32`. - -Previous versions of `XorshiftEngine` did not provide any mechanism to specify -the directions of the shifts, taking each shift as an unsigned magnitude. -For backwards compatibility, because three shifts in the same direction -cannot result in a full-period XorshiftEngine, when all three of `sa`, `sb`, -`sc, are positive `XorshiftEngine` treats them as unsigned magnitudes and -uses shift directions to match the old behavior of `XorshiftEngine`. - -Not every set of shifts results in a full-period xorshift generator. -The template does not currently at compile-time perform a full check -for maximum period but in a future version might reject parameters -resulting in shorter periods. -+/ -struct XorshiftEngine(UIntType, uint nbits, int sa, int sb, int sc) -if (isUnsigned!UIntType && !(sa > 0 && sb > 0 && sc > 0)) -{ - static assert(nbits > 0 && nbits % (UIntType.sizeof * 8) == 0, - "nbits must be an even multiple of "~UIntType.stringof - ~".sizeof * 8, not "~nbits.stringof~"."); - - static assert(!((sa >= 0) == (sb >= 0) && (sa >= 0) == (sc >= 0)) - && (sa * sb * sc != 0), - "shifts cannot be zero and cannot all be in same direction: cannot be [" - ~sa.stringof~", "~sb.stringof~", "~sc.stringof~"]."); - - static assert(sa != sb && sb != sc, - "consecutive shifts with the same magnitude and direction would partially or completely cancel!"); - - static assert(UIntType.sizeof == uint.sizeof || UIntType.sizeof == ulong.sizeof, - "XorshiftEngine currently does not support type " ~ UIntType.sizeof - ~ " because it does not have a `seed` implementation for arrays " - ~ " of element types other than uint and ulong."); - - public: - ///Mark this as a Rng - enum bool isUniformRandom = true; - /// Always `false` (random generators are infinite ranges). - enum empty = false; - /// Smallest generated value. - enum UIntType min = _state.length == 1 ? 1 : 0; - /// Largest generated value. - enum UIntType max = UIntType.max; - - - private: - // Legacy 192 bit uint hybrid counter/xorshift. - enum bool isLegacy192Bit = UIntType.sizeof == uint.sizeof && nbits == 192; - - // Shift magnitudes. - enum a = (sa < 0 ? -sa : sa); - enum b = (sb < 0 ? -sb : sb); - enum c = (sc < 0 ? -sc : sc); - - // Shift expressions to mix in. - enum shiftA(string expr) = `((`~expr~`) `~(sa > 0 ? `<< a)` : ` >>> a)`); - enum shiftB(string expr) = `((`~expr~`) `~(sb > 0 ? `<< b)` : ` >>> b)`); - enum shiftC(string expr) = `((`~expr~`) `~(sc > 0 ? `<< c)` : ` >>> c)`); - - enum N = nbits / (UIntType.sizeof * 8); - - // For N <= 2 it is strictly worse to use an index. - // Informal third-party benchmarks suggest that for `ulong` it is - // faster to use an index when N == 4. For `uint` we err on the side - // of not increasing the struct's size and only switch to the other - // implementation when N > 4. - enum useIndex = !isLegacy192Bit && (UIntType.sizeof >= ulong.sizeof ? N > 3 : N > 4); - static if (useIndex) - { - enum initialIndex = N - 1; - uint _index = initialIndex; - } - - static if (N == 1 && UIntType.sizeof <= uint.sizeof) - { - UIntType[N] _state = [cast(UIntType) 2_463_534_242]; - } - else static if (isLegacy192Bit) - { - UIntType[N] _state = [123_456_789, 362_436_069, 521_288_629, 88_675_123, 5_783_321, 6_615_241]; - UIntType value_; - } - else static if (N <= 5 && UIntType.sizeof <= uint.sizeof) - { - UIntType[N] _state = [ - cast(UIntType) 123_456_789, - cast(UIntType) 362_436_069, - cast(UIntType) 521_288_629, - cast(UIntType) 88_675_123, - cast(UIntType) 5_783_321][0 .. N]; - } - else - { - UIntType[N] _state = () - { - static if (UIntType.sizeof < ulong.sizeof) - { - uint x0 = 123_456_789; - enum uint m = 1_812_433_253U; - } - else static if (UIntType.sizeof <= ulong.sizeof) - { - ulong x0 = 123_456_789; - enum ulong m = 6_364_136_223_846_793_005UL; - } - else - { - static assert(0, "Phobos Error: Xorshift has no instantiation rule for " - ~ UIntType.stringof); - } - enum uint rshift = typeof(x0).sizeof * 8 - 2; - UIntType[N] result = void; - foreach (i, ref e; result) - { - e = cast(UIntType) (x0 = (m * (x0 ^ (x0 >>> rshift)) + i + 1)); - if (e == 0) - e = cast(UIntType) (i + 1); - } - return result; - }(); - } - - - public: - /++ - Constructs a `XorshiftEngine` generator seeded with $(D_PARAM x0). - - Params: - x0 = value used to deterministically initialize internal state - +/ - this()(UIntType x0) @safe pure nothrow @nogc - { - seed(x0); - } - - - /++ - (Re)seeds the generator. - - Params: - x0 = value used to deterministically initialize internal state - +/ - void seed()(UIntType x0) @safe pure nothrow @nogc - { - static if (useIndex) - _index = initialIndex; - - static if (UIntType.sizeof == uint.sizeof) - { - // Initialization routine from MersenneTwisterEngine. - foreach (uint i, ref e; _state) - { - e = (x0 = (1_812_433_253U * (x0 ^ (x0 >> 30)) + i + 1)); - // Xorshift requires merely that not every word of the internal - // array is 0. For historical compatibility the 32-bit word version - // has the stronger requirement that not any word of the state - // array is 0 after initial seeding. - if (e == 0) - e = (i + 1); - } - } - else static if (UIntType.sizeof == ulong.sizeof) - { - static if (N > 1) - { - // Initialize array using splitmix64 as recommended by Sebastino Vigna. - // By construction the array will not be all zeroes. - // http://xoroshiro.di.unimi.it/splitmix64.c - foreach (ref e; _state) - { - ulong z = (x0 += 0x9e37_79b9_7f4a_7c15UL); - z = (z ^ (z >>> 30)) * 0xbf58_476d_1ce4_e5b9UL; - z = (z ^ (z >>> 27)) * 0x94d0_49bb_1331_11ebUL; - e = z ^ (z >>> 31); - } - } - else - { - // Apply a transformation when N == 1 instead of just copying x0 - // directly because it's not unlikely that a user might initialize - // a PRNG with small counting numbers (e.g. 1, 2, 3) that have the - // statistically rare property of having only 1 or 2 non-zero bits. - // Additionally we need to ensure that the internal state is not - // entirely zero. - if (x0 != 0) - _state[0] = x0 * 6_364_136_223_846_793_005UL; - else - _state[0] = typeof(this).init._state[0]; - } - } - else - { - static assert(0, "Phobos Error: Xorshift has no `seed` implementation for " - ~ UIntType.stringof); - } - - popFront(); - } - - - /** - * Returns the current number in the random sequence. - */ - @property - UIntType front() const @safe pure nothrow @nogc - { - static if (isLegacy192Bit) - return value_; - else static if (!useIndex) - return _state[N-1]; - else - return _state[_index]; - } - - - /** - * Advances the random sequence. - */ - void popFront() @safe pure nothrow @nogc - { - alias s = _state; - static if (isLegacy192Bit) - { - auto x = _state[0] ^ mixin(shiftA!`s[0]`); - static foreach (i; 0 .. N-2) - s[i] = s[i + 1]; - s[N-2] = s[N-2] ^ mixin(shiftC!`s[N-2]`) ^ x ^ mixin(shiftB!`x`); - value_ = s[N-2] + (s[N-1] += 362_437); - } - else static if (N == 1) - { - s[0] ^= mixin(shiftA!`s[0]`); - s[0] ^= mixin(shiftB!`s[0]`); - s[0] ^= mixin(shiftC!`s[0]`); - } - else static if (!useIndex) - { - auto x = s[0] ^ mixin(shiftA!`s[0]`); - static foreach (i; 0 .. N-1) - s[i] = s[i + 1]; - s[N-1] = s[N-1] ^ mixin(shiftC!`s[N-1]`) ^ x ^ mixin(shiftB!`x`); - } - else - { - assert(_index < N); // Invariant. - const sIndexMinus1 = s[_index]; - static if ((N & (N - 1)) == 0) - _index = (_index + 1) & typeof(_index)(N - 1); - else - { - if (++_index >= N) - _index = 0; - } - auto x = s[_index]; - x ^= mixin(shiftA!`x`); - s[_index] = sIndexMinus1 ^ mixin(shiftC!`sIndexMinus1`) ^ x ^ mixin(shiftB!`x`); - } - } - - - /** - * Captures a range state. - */ - @property - typeof(this) save() const @safe pure nothrow @nogc - { - return this; - } - -private: - // Workaround for a DScanner bug. If we remove this `private:` DScanner - // gives erroneous warnings about missing documentation for public symbols - // later in the module. -} - -/// ditto -template XorshiftEngine(UIntType, int bits, int a, int b, int c) -if (isUnsigned!UIntType && a > 0 && b > 0 && c > 0) -{ - // Compatibility with old parameterizations without explicit shift directions. - static if (bits == UIntType.sizeof * 8) - alias XorshiftEngine = .XorshiftEngine!(UIntType, bits, a, -b, c);//left, right, left - else static if (bits == 192 && UIntType.sizeof == uint.sizeof) - alias XorshiftEngine = .XorshiftEngine!(UIntType, bits, -a, b, c);//right, left, left - else - alias XorshiftEngine = .XorshiftEngine!(UIntType, bits, a, -b, -c);//left, right, right -} - -/// -@safe unittest -{ - alias Xorshift96 = XorshiftEngine!(uint, 96, 10, 5, 26); - auto rnd = Xorshift96(42); - auto num = rnd.front; // same for each run - assert(num == 2704588748); -} - - -/** - * Define `XorshiftEngine` generators with well-chosen parameters. See each bits examples of "Xorshift RNGs". - * `Xorshift` is a Xorshift128's alias because 128bits implementation is mostly used. - */ -alias Xorshift32 = XorshiftEngine!(uint, 32, 13, 17, 15) ; -alias Xorshift64 = XorshiftEngine!(uint, 64, 10, 13, 10); /// ditto -alias Xorshift96 = XorshiftEngine!(uint, 96, 10, 5, 26); /// ditto -alias Xorshift128 = XorshiftEngine!(uint, 128, 11, 8, 19); /// ditto -alias Xorshift160 = XorshiftEngine!(uint, 160, 2, 1, 4); /// ditto -alias Xorshift192 = XorshiftEngine!(uint, 192, 2, 1, 4); /// ditto -alias Xorshift = Xorshift128; /// ditto - -/// -@safe @nogc unittest -{ - // Seed with a constant - auto rnd = Xorshift(1); - auto num = rnd.front; // same for each run - assert(num == 1405313047); - - // Seed with an unpredictable value - rnd.seed(unpredictableSeed); - num = rnd.front; // different across rnd -} - -@safe @nogc unittest -{ - import std.range; - static assert(isForwardRange!Xorshift); - static assert(isUniformRNG!Xorshift); - static assert(isUniformRNG!(Xorshift, uint)); - static assert(isSeedable!Xorshift); - static assert(isSeedable!(Xorshift, uint)); - - static assert(Xorshift32.min == 1); - - // Result from reference implementation. - static ulong[][] checking = [ - [2463534242UL, 901999875, 3371835698, 2675058524, 1053936272, 3811264849, - 472493137, 3856898176, 2131710969, 2312157505], - [362436069UL, 2113136921, 19051112, 3010520417, 951284840, 1213972223, - 3173832558, 2611145638, 2515869689, 2245824891], - [521288629UL, 1950277231, 185954712, 1582725458, 3580567609, 2303633688, - 2394948066, 4108622809, 1116800180, 3357585673], - [88675123UL, 3701687786, 458299110, 2500872618, 3633119408, 516391518, - 2377269574, 2599949379, 717229868, 137866584], - [5783321UL, 393427209, 1947109840, 565829276, 1006220149, 971147905, - 1436324242, 2800460115, 1484058076, 3823330032], - [0UL, 246875399, 3690007200, 1264581005, 3906711041, 1866187943, 2481925219, - 2464530826, 1604040631, 3653403911], - [16749904790159980466UL, 14489774923612894650UL, 148813570191443371UL, - 6529783008780612886UL, 10182425759614080046UL, 16549659571055687844UL, - 542957868271744939UL, 9459127085596028450UL, 16001049981702441780UL, - 7351634712593111741], - [14750058843113249055UL, 17731577314455387619UL, 1314705253499959044UL, - 3113030620614841056UL, 9468075444678629182UL, 13962152036600088141UL, - 9030205609946043947UL, 1856726150434672917UL, 8098922200110395314UL, - 2772699174618556175UL], - ]; - - alias XorshiftTypes = std.meta.AliasSeq!(Xorshift32, Xorshift64, Xorshift96, - Xorshift128, Xorshift160, Xorshift192, Xorshift64_64, Xorshift128_64); - - foreach (I, Type; XorshiftTypes) - { - Type rnd; - - foreach (e; checking[I]) - { - assert(rnd.front == e); - rnd.popFront(); - } - } - - // Check .save works - foreach (Type; XorshiftTypes) - { - auto rnd1 = Type(123_456_789); - rnd1.popFront(); - // https://issues.dlang.org/show_bug.cgi?id=15853 - auto rnd2 = ((const ref Type a) => a.save())(rnd1); - assert(rnd1 == rnd2); - // Enable next test when RNGs are reference types - version (none) { assert(rnd1 !is rnd2); } - for (auto i = 0; i <= Type.sizeof / 4; i++, rnd1.popFront, rnd2.popFront) - assert(rnd1.front() == rnd2.front()); - } -} - - -/* A complete list of all pseudo-random number generators implemented in - * std.random. This can be used to confirm that a given function or - * object is compatible with all the pseudo-random number generators - * available. It is enabled only in unittest mode. - */ -@safe @nogc unittest -{ - foreach (Rng; PseudoRngTypes) - { - static assert(isUniformRNG!Rng); - auto rng = Rng(123_456_789); - } -} - -version (CRuntime_Bionic) - version = SecureARC4Random; // ChaCha20 -version (Darwin) - version = SecureARC4Random; // AES -version (OpenBSD) - version = SecureARC4Random; // ChaCha20 -version (NetBSD) - version = SecureARC4Random; // ChaCha20 - -version (CRuntime_UClibc) - version = LegacyARC4Random; // ARC4 -version (FreeBSD) - version = LegacyARC4Random; // ARC4 -version (DragonFlyBSD) - version = LegacyARC4Random; // ARC4 -version (BSD) - version = LegacyARC4Random; // Unknown implementation - -// For the current purpose of unpredictableSeed the difference between -// a secure arc4random implementation and a legacy implementation is -// unimportant. The source code documents this distinction in case in the -// future Phobos is altered to require cryptographically secure sources -// of randomness, and also so other people reading this source code (as -// Phobos is often looked to as an example of good D programming practices) -// do not mistakenly use insecure versions of arc4random in contexts where -// cryptographically secure sources of randomness are needed. - -// Performance note: ChaCha20 is about 70% faster than ARC4, contrary to -// what one might assume from it being more secure. - -version (SecureARC4Random) - version = AnyARC4Random; -version (LegacyARC4Random) - version = AnyARC4Random; - -version (AnyARC4Random) -{ - extern(C) private @nogc nothrow - { - uint arc4random() @safe; - void arc4random_buf(scope void* buf, size_t nbytes) @system; - } -} -else -{ - private ulong bootstrapSeed() @nogc nothrow - { - // https://issues.dlang.org/show_bug.cgi?id=19580 - // previously used `ulong result = void` to start with an arbitary value - // but using an uninitialized variable's value is undefined behavior - // and enabled unwanted optimizations on non-DMD compilers. - ulong result; - enum ulong m = 0xc6a4_a793_5bd1_e995UL; // MurmurHash2_64A constant. - void updateResult(ulong x) - { - x *= m; - x = (x ^ (x >>> 47)) * m; - result = (result ^ x) * m; - } - import core.thread : getpid, Thread; - import core.time : MonoTime; - - updateResult(cast(ulong) cast(void*) Thread.getThis()); - updateResult(cast(ulong) getpid()); - updateResult(cast(ulong) MonoTime.currTime.ticks); - result = (result ^ (result >>> 47)) * m; - return result ^ (result >>> 47); - } - - // If we don't have arc4random and we don't have RDRAND fall back to this. - private ulong fallbackSeed() @nogc nothrow - { - // Bit avalanche function from MurmurHash3. - static ulong fmix64(ulong k) @nogc nothrow pure @safe - { - k = (k ^ (k >>> 33)) * 0xff51afd7ed558ccd; - k = (k ^ (k >>> 33)) * 0xc4ceb9fe1a85ec53; - return k ^ (k >>> 33); - } - // Using SplitMix algorithm with constant gamma. - // Chosen gamma is the odd number closest to 2^^64 - // divided by the silver ratio (1.0L + sqrt(2.0L)). - enum gamma = 0x6a09e667f3bcc909UL; - import core.atomic : has64BitCAS; - static if (has64BitCAS) - { - import core.atomic : MemoryOrder, atomicLoad, atomicOp, atomicStore, cas; - shared static ulong seed; - shared static bool initialized; - if (0 == atomicLoad!(MemoryOrder.raw)(initialized)) - { - cas(&seed, 0UL, fmix64(bootstrapSeed())); - atomicStore!(MemoryOrder.rel)(initialized, true); - } - return fmix64(atomicOp!"+="(seed, gamma)); - } - else - { - static ulong seed; - static bool initialized; - if (!initialized) - { - seed = fmix64(bootstrapSeed()); - initialized = true; - } - return fmix64(seed += gamma); - } - } -} - -/** -A "good" seed for initializing random number engines. Initializing -with $(D_PARAM unpredictableSeed) makes engines generate different -random number sequences every run. - -Returns: -A single unsigned integer seed value, different on each successive call -Note: -In general periodically 'reseeding' a PRNG does not improve its quality -and in some cases may harm it. For an extreme example the Mersenne -Twister has `2 ^^ 19937 - 1` distinct states but after `seed(uint)` is -called it can only be in one of `2 ^^ 32` distinct states regardless of -how excellent the source of entropy is. -*/ -@property uint unpredictableSeed() @trusted nothrow @nogc -{ - version (AnyARC4Random) - { - return arc4random(); - } - else - { - version (InlineAsm_X86_Any) - { - import core.cpuid : hasRdrand; - if (hasRdrand) - { - uint result; - asm @nogc nothrow - { - db 0x0f, 0xc7, 0xf0; // rdrand EAX - jnc LnotUsingRdrand; - mov result, EAX; - // Some AMD CPUs shipped with bugs where RDRAND could fail - // but still set the carry flag to 1. In those cases the - // output will be -1. - cmp EAX, 0xffff_ffff; - jne LusingRdrand; - // If result was -1 verify RDAND isn't constantly returning -1. - db 0x0f, 0xc7, 0xf0; - jnc LusingRdrand; - cmp EAX, 0xffff_ffff; - je LnotUsingRdrand; - } - LusingRdrand: - return result; - } - LnotUsingRdrand: - } - return cast(uint) fallbackSeed(); - } -} - -/// ditto -template unpredictableSeed(UIntType) -if (isUnsigned!UIntType) -{ - static if (is(UIntType == uint)) - alias unpredictableSeed = .unpredictableSeed; - else static if (!is(Unqual!UIntType == UIntType)) - alias unpredictableSeed = .unpredictableSeed!(Unqual!UIntType); - else - /// ditto - @property UIntType unpredictableSeed() @nogc nothrow @trusted - { - version (AnyARC4Random) - { - static if (UIntType.sizeof <= uint.sizeof) - { - return cast(UIntType) arc4random(); - } - else - { - UIntType result = void; - arc4random_buf(&result, UIntType.sizeof); - return result; - } - } - else - { - version (InlineAsm_X86_Any) - { - import core.cpuid : hasRdrand; - if (hasRdrand) - { - static if (UIntType.sizeof <= uint.sizeof) - { - uint result; - asm @nogc nothrow - { - db 0x0f, 0xc7, 0xf0; // rdrand EAX - jnc LnotUsingRdrand; - mov result, EAX; - // Some AMD CPUs shipped with bugs where RDRAND could fail - // but still set the carry flag to 1. In those cases the - // output will be -1. - cmp EAX, 0xffff_ffff; - jne LusingRdrand; - // If result was -1 verify RDAND isn't constantly returning -1. - db 0x0f, 0xc7, 0xf0; - jnc LusingRdrand; - cmp EAX, 0xffff_ffff; - je LnotUsingRdrand; - } - LusingRdrand: - return cast(UIntType) result; - } - else version (D_InlineAsm_X86_64) - { - ulong result; - asm @nogc nothrow - { - db 0x48, 0x0f, 0xc7, 0xf0; // rdrand RAX - jnc LnotUsingRdrand; - mov result, RAX; - // Some AMD CPUs shipped with bugs where RDRAND could fail - // but still set the carry flag to 1. In those cases the - // output will be -1. - cmp RAX, 0xffff_ffff_ffff_ffff; - jne LusingRdrand; - // If result was -1 verify RDAND isn't constantly returning -1. - db 0x48, 0x0f, 0xc7, 0xf0; - jnc LusingRdrand; - cmp RAX, 0xffff_ffff_ffff_ffff; - je LnotUsingRdrand; - } - LusingRdrand: - return result; - } - else - { - uint resultLow, resultHigh; - asm @nogc nothrow - { - db 0x0f, 0xc7, 0xf0; // rdrand EAX - jnc LnotUsingRdrand; - mov resultLow, EAX; - db 0x0f, 0xc7, 0xf0; // rdrand EAX - jnc LnotUsingRdrand; - mov resultHigh, EAX; - } - if (resultLow != uint.max || resultHigh != uint.max) // Protect against AMD RDRAND bug. - return ((cast(ulong) resultHigh) << 32) ^ resultLow; - } - } - LnotUsingRdrand: - } - return cast(UIntType) fallbackSeed(); - } - } -} - -/// -@safe @nogc unittest -{ - auto rnd = Random(unpredictableSeed); - auto n = rnd.front; - static assert(is(typeof(n) == uint)); -} - -/** -The "default", "favorite", "suggested" random number generator type on -the current platform. It is an alias for one of the previously-defined -generators. You may want to use it if (1) you need to generate some -nice random numbers, and (2) you don't care for the minutiae of the -method being used. - */ - -alias Random = Mt19937; - -@safe @nogc unittest -{ - static assert(isUniformRNG!Random); - static assert(isUniformRNG!(Random, uint)); - static assert(isSeedable!Random); - static assert(isSeedable!(Random, uint)); -} - -/** -Global random number generator used by various functions in this -module whenever no generator is specified. It is allocated per-thread -and initialized to an unpredictable value for each thread. - -Returns: -A singleton instance of the default random number generator - */ -@property ref Random rndGen() @safe nothrow @nogc -{ - static Random result; - static bool initialized; - if (!initialized) - { - static if (isSeedable!(Random, ulong)) - result.seed(unpredictableSeed!ulong); // Avoid unnecessary copy. - else static if (is(Random : MersenneTwisterEngine!Params, Params...)) - initMTEngine(result); - else static if (isSeedable!(Random, uint)) - result.seed(unpredictableSeed!uint); // Avoid unnecessary copy. - else - result = Random(unpredictableSeed); - initialized = true; - } - return result; -} - -/// -@safe nothrow @nogc unittest -{ - import std.algorithm.iteration : sum; - import std.range : take; - auto rnd = rndGen; - assert(rnd.take(3).sum > 0); -} - -/+ -Initialize a 32-bit MersenneTwisterEngine from 64 bits of entropy. -This is private and accepts no seed as a parameter, freeing the internal -implementaton from any need for stability across releases. -+/ -private void initMTEngine(MTEngine)(scope ref MTEngine mt) -if (is(MTEngine : MersenneTwisterEngine!Params, Params...)) -{ - pragma(inline, false); // Called no more than once per thread by rndGen. - ulong seed = unpredictableSeed!ulong; - static if (is(typeof(mt.seed(seed)))) - { - mt.seed(seed); - } - else - { - alias UIntType = typeof(mt.front()); - if (seed == 0) seed = -1; // Any number but 0 is fine. - uint s0 = cast(uint) seed; - uint s1 = cast(uint) (seed >> 32); - foreach (ref e; mt.state.data) - { - //http://xoshiro.di.unimi.it/xoroshiro64starstar.c - const tmp = s0 * 0x9E3779BB; - e = ((tmp << 5) | (tmp >> (32 - 5))) * 5; - static if (MTEngine.max != UIntType.max) { e &= MTEngine.max; } - - const tmp1 = s0 ^ s1; - s0 = ((s0 << 26) | (s0 >> (32 - 26))) ^ tmp1 ^ (tmp1 << 9); - s1 = (tmp1 << 13) | (tmp1 >> (32 - 13)); - } - - mt.state.index = mt.state.data.length - 1; - // double popFront() to guarantee both `mt.state.z` - // and `mt.state.front` are derived from the newly - // set values in `mt.state.data`. - mt.popFront(); - mt.popFront(); - } -} - -/** -Generates a number between `a` and `b`. The `boundaries` -parameter controls the shape of the interval (open vs. closed on -either side). Valid values for `boundaries` are `"[]"`, $(D -"$(LPAREN)]"), `"[$(RPAREN)"`, and `"()"`. The default interval -is closed to the left and open to the right. The version that does not -take `urng` uses the default generator `rndGen`. - -Params: - a = lower bound of the _uniform distribution - b = upper bound of the _uniform distribution - urng = (optional) random number generator to use; - if not specified, defaults to `rndGen` - -Returns: - A single random variate drawn from the _uniform distribution - between `a` and `b`, whose type is the common type of - these parameters - */ -auto uniform(string boundaries = "[)", T1, T2) -(T1 a, T2 b) -if (!is(CommonType!(T1, T2) == void)) -{ - return uniform!(boundaries, T1, T2, Random)(a, b, rndGen); -} - -/// -@safe unittest -{ - auto rnd = Random(unpredictableSeed); - - // Generate an integer in [0, 1023] - auto a = uniform(0, 1024, rnd); - assert(0 <= a && a < 1024); - - // Generate a float in [0, 1) - auto b = uniform(0.0f, 1.0f, rnd); - assert(0 <= b && b < 1); - - // Generate a float in [0, 1] - b = uniform!"[]"(0.0f, 1.0f, rnd); - assert(0 <= b && b <= 1); - - // Generate a float in (0, 1) - b = uniform!"()"(0.0f, 1.0f, rnd); - assert(0 < b && b < 1); -} - -/// Create an array of random numbers using range functions and UFCS -@safe unittest -{ - import std.array : array; - import std.range : generate, takeExactly; - - int[] arr = generate!(() => uniform(0, 100)).takeExactly(10).array; - assert(arr.length == 10); - assert(arr[0] >= 0 && arr[0] < 100); -} - -@safe unittest -{ - MinstdRand0 gen; - foreach (i; 0 .. 20) - { - auto x = uniform(0.0, 15.0, gen); - assert(0 <= x && x < 15); - } - foreach (i; 0 .. 20) - { - auto x = uniform!"[]"('a', 'z', gen); - assert('a' <= x && x <= 'z'); - } - - foreach (i; 0 .. 20) - { - auto x = uniform('a', 'z', gen); - assert('a' <= x && x < 'z'); - } - - foreach (i; 0 .. 20) - { - immutable ubyte a = 0; - immutable ubyte b = 15; - auto x = uniform(a, b, gen); - assert(a <= x && x < b); - } -} - -// Implementation of uniform for floating-point types -/// ditto -auto uniform(string boundaries = "[)", - T1, T2, UniformRandomNumberGenerator) -(T1 a, T2 b, ref UniformRandomNumberGenerator urng) -if (isFloatingPoint!(CommonType!(T1, T2)) && isUniformRNG!UniformRandomNumberGenerator) -{ - import std.conv : text; - import std.exception : enforce; - alias NumberType = Unqual!(CommonType!(T1, T2)); - static if (boundaries[0] == '(') - { - import std.math.operations : nextafter; - NumberType _a = nextafter(cast(NumberType) a, NumberType.infinity); - } - else - { - NumberType _a = a; - } - static if (boundaries[1] == ')') - { - import std.math.operations : nextafter; - NumberType _b = nextafter(cast(NumberType) b, -NumberType.infinity); - } - else - { - NumberType _b = b; - } - enforce(_a <= _b, - text("std.random.uniform(): invalid bounding interval ", - boundaries[0], a, ", ", b, boundaries[1])); - NumberType result = - _a + (_b - _a) * cast(NumberType) (urng.front - urng.min) - / (urng.max - urng.min); - urng.popFront(); - return result; -} - -// Implementation of uniform for integral types -/+ Description of algorithm and suggestion of correctness: - -The modulus operator maps an integer to a small, finite space. For instance, `x -% 3` will map whatever x is into the range [0 .. 3). 0 maps to 0, 1 maps to 1, 2 -maps to 2, 3 maps to 0, and so on infinitely. As long as the integer is -uniformly chosen from the infinite space of all non-negative integers then `x % -3` will uniformly fall into that range. - -(Non-negative is important in this case because some definitions of modulus, -namely the one used in computers generally, map negative numbers differently to -(-3 .. 0]. `uniform` does not use negative number modulus, thus we can safely -ignore that fact.) - -The issue with computers is that integers have a finite space they must fit in, -and our uniformly chosen random number is picked in that finite space. So, that -method is not sufficient. You can look at it as the integer space being divided -into "buckets" and every bucket after the first bucket maps directly into that -first bucket. `[0, 1, 2]`, `[3, 4, 5]`, ... When integers are finite, then the -last bucket has the chance to be "incomplete": `[uint.max - 3, uint.max - 2, -uint.max - 1]`, `[uint.max]` ... (the last bucket only has 1!). The issue here -is that _every_ bucket maps _completely_ to the first bucket except for that -last one. The last one doesn't have corresponding mappings to 1 or 2, in this -case, which makes it unfair. - -So, the answer is to simply "reroll" if you're in that last bucket, since it's -the only unfair one. Eventually you'll roll into a fair bucket. Simply, instead -of the meaning of the last bucket being "maps to `[0]`", it changes to "maps to -`[0, 1, 2]`", which is precisely what we want. - -To generalize, `upperDist` represents the size of our buckets (and, thus, the -exclusive upper bound for our desired uniform number). `rnum` is a uniformly -random number picked from the space of integers that a computer can hold (we'll -say `UpperType` represents that type). - -We'll first try to do the mapping into the first bucket by doing `offset = rnum -% upperDist`. We can figure out the position of the front of the bucket we're in -by `bucketFront = rnum - offset`. - -If we start at `UpperType.max` and walk backwards `upperDist - 1` spaces, then -the space we land on is the last acceptable position where a full bucket can -fit: - ---- - bucketFront UpperType.max - v v -[..., 0, 1, 2, ..., upperDist - 1] - ^~~ upperDist - 1 ~~^ ---- - -If the bucket starts any later, then it must have lost at least one number and -at least that number won't be represented fairly. - ---- - bucketFront UpperType.max - v v -[..., upperDist - 1, 0, 1, 2, ..., upperDist - 2] - ^~~~~~~~ upperDist - 1 ~~~~~~~^ ---- - -Hence, our condition to reroll is -`bucketFront > (UpperType.max - (upperDist - 1))` -+/ -auto uniform(string boundaries = "[)", T1, T2, RandomGen) -(T1 a, T2 b, ref RandomGen rng) -if ((isIntegral!(CommonType!(T1, T2)) || isSomeChar!(CommonType!(T1, T2))) && - isUniformRNG!RandomGen) -{ - import std.conv : text, unsigned; - import std.exception : enforce; - alias ResultType = Unqual!(CommonType!(T1, T2)); - static if (boundaries[0] == '(') - { - enforce(a < ResultType.max, - text("std.random.uniform(): invalid left bound ", a)); - ResultType lower = cast(ResultType) (a + 1); - } - else - { - ResultType lower = a; - } - - static if (boundaries[1] == ']') - { - enforce(lower <= b, - text("std.random.uniform(): invalid bounding interval ", - boundaries[0], a, ", ", b, boundaries[1])); - /* Cannot use this next optimization with dchar, as dchar - * only partially uses its full bit range - */ - static if (!is(ResultType == dchar)) - { - if (b == ResultType.max && lower == ResultType.min) - { - // Special case - all bits are occupied - return std.random.uniform!ResultType(rng); - } - } - auto upperDist = unsigned(b - lower) + 1u; - } - else - { - enforce(lower < b, - text("std.random.uniform(): invalid bounding interval ", - boundaries[0], a, ", ", b, boundaries[1])); - auto upperDist = unsigned(b - lower); - } - - assert(upperDist != 0); - - alias UpperType = typeof(upperDist); - static assert(UpperType.min == 0); - - UpperType offset, rnum, bucketFront; - do - { - rnum = uniform!UpperType(rng); - offset = rnum % upperDist; - bucketFront = rnum - offset; - } // while we're in an unfair bucket... - while (bucketFront > (UpperType.max - (upperDist - 1))); - - return cast(ResultType)(lower + offset); -} - -@safe unittest -{ - import std.conv : to; - auto gen = Mt19937(123_456_789); - static assert(isForwardRange!(typeof(gen))); - - auto a = uniform(0, 1024, gen); - assert(0 <= a && a <= 1024); - auto b = uniform(0.0f, 1.0f, gen); - assert(0 <= b && b < 1, to!string(b)); - auto c = uniform(0.0, 1.0); - assert(0 <= c && c < 1); - - static foreach (T; std.meta.AliasSeq!(char, wchar, dchar, byte, ubyte, short, ushort, - int, uint, long, ulong, float, double, real)) - {{ - T lo = 0, hi = 100; - - // Try tests with each of the possible bounds - { - T init = uniform(lo, hi); - size_t i = 50; - while (--i && uniform(lo, hi) == init) {} - assert(i > 0); - } - { - T init = uniform!"[)"(lo, hi); - size_t i = 50; - while (--i && uniform(lo, hi) == init) {} - assert(i > 0); - } - { - T init = uniform!"(]"(lo, hi); - size_t i = 50; - while (--i && uniform(lo, hi) == init) {} - assert(i > 0); - } - { - T init = uniform!"()"(lo, hi); - size_t i = 50; - while (--i && uniform(lo, hi) == init) {} - assert(i > 0); - } - { - T init = uniform!"[]"(lo, hi); - size_t i = 50; - while (--i && uniform(lo, hi) == init) {} - assert(i > 0); - } - - /* Test case with closed boundaries covering whole range - * of integral type - */ - static if (isIntegral!T || isSomeChar!T) - { - foreach (immutable _; 0 .. 100) - { - auto u = uniform!"[]"(T.min, T.max); - static assert(is(typeof(u) == T)); - assert(T.min <= u, "Lower bound violation for uniform!\"[]\" with " ~ T.stringof); - assert(u <= T.max, "Upper bound violation for uniform!\"[]\" with " ~ T.stringof); - } - } - }} - - auto reproRng = Xorshift(239842); - - static foreach (T; std.meta.AliasSeq!(char, wchar, dchar, byte, ubyte, short, - ushort, int, uint, long, ulong)) - {{ - T lo = T.min + 10, hi = T.max - 10; - T init = uniform(lo, hi, reproRng); - size_t i = 50; - while (--i && uniform(lo, hi, reproRng) == init) {} - assert(i > 0); - }} - - { - bool sawLB = false, sawUB = false; - foreach (i; 0 .. 50) - { - auto x = uniform!"[]"('a', 'd', reproRng); - if (x == 'a') sawLB = true; - if (x == 'd') sawUB = true; - assert('a' <= x && x <= 'd'); - } - assert(sawLB && sawUB); - } - - { - bool sawLB = false, sawUB = false; - foreach (i; 0 .. 50) - { - auto x = uniform('a', 'd', reproRng); - if (x == 'a') sawLB = true; - if (x == 'c') sawUB = true; - assert('a' <= x && x < 'd'); - } - assert(sawLB && sawUB); - } - - { - bool sawLB = false, sawUB = false; - foreach (i; 0 .. 50) - { - immutable int lo = -2, hi = 2; - auto x = uniform!"()"(lo, hi, reproRng); - if (x == (lo+1)) sawLB = true; - if (x == (hi-1)) sawUB = true; - assert(lo < x && x < hi); - } - assert(sawLB && sawUB); - } - - { - bool sawLB = false, sawUB = false; - foreach (i; 0 .. 50) - { - immutable ubyte lo = 0, hi = 5; - auto x = uniform(lo, hi, reproRng); - if (x == lo) sawLB = true; - if (x == (hi-1)) sawUB = true; - assert(lo <= x && x < hi); - } - assert(sawLB && sawUB); - } - - { - foreach (i; 0 .. 30) - { - assert(i == uniform(i, i+1, reproRng)); - } - } -} - -/+ -Generates an unsigned integer in the half-open range `[0, k)`. -Non-public because we locally guarantee `k > 0`. - -Params: - k = unsigned exclusive upper bound; caller guarantees this is non-zero - rng = random number generator to use - -Returns: - Pseudo-random unsigned integer strictly less than `k`. -+/ -private UInt _uniformIndex(UniformRNG, UInt = size_t)(const UInt k, ref UniformRNG rng) -if (isUnsigned!UInt && isUniformRNG!UniformRNG) -{ - alias ResultType = UInt; - alias UpperType = Unsigned!(typeof(k - 0)); - alias upperDist = k; - - assert(upperDist != 0); - - // For backwards compatibility use same algorithm as uniform(0, k, rng). - UpperType offset, rnum, bucketFront; - do - { - rnum = uniform!UpperType(rng); - offset = rnum % upperDist; - bucketFront = rnum - offset; - } // while we're in an unfair bucket... - while (bucketFront > (UpperType.max - (upperDist - 1))); - - return cast(ResultType) offset; -} - -pure @safe unittest -{ - // For backwards compatibility check that _uniformIndex(k, rng) - // has the same result as uniform(0, k, rng). - auto rng1 = Xorshift(123_456_789); - auto rng2 = rng1.save(); - const size_t k = (1U << 31) - 1; - assert(_uniformIndex(k, rng1) == uniform(0, k, rng2)); -} - -/** -Generates a uniformly-distributed number in the range $(D [T.min, -T.max]) for any integral or character type `T`. If no random -number generator is passed, uses the default `rndGen`. - -If an `enum` is used as type, the random variate is drawn with -equal probability from any of the possible values of the enum `E`. - -Params: - urng = (optional) random number generator to use; - if not specified, defaults to `rndGen` - -Returns: - Random variate drawn from the _uniform distribution across all - possible values of the integral, character or enum type `T`. - */ -auto uniform(T, UniformRandomNumberGenerator) -(ref UniformRandomNumberGenerator urng) -if (!is(T == enum) && (isIntegral!T || isSomeChar!T) && isUniformRNG!UniformRandomNumberGenerator) -{ - /* dchar does not use its full bit range, so we must - * revert to the uniform with specified bounds - */ - static if (is(immutable T == immutable dchar)) - { - return uniform!"[]"(T.min, T.max, urng); - } - else - { - auto r = urng.front; - urng.popFront(); - static if (T.sizeof <= r.sizeof) - { - return cast(T) r; - } - else - { - static assert(T.sizeof == 8 && r.sizeof == 4); - T r1 = urng.front | (cast(T) r << 32); - urng.popFront(); - return r1; - } - } -} - -/// Ditto -auto uniform(T)() -if (!is(T == enum) && (isIntegral!T || isSomeChar!T)) -{ - return uniform!T(rndGen); -} - -/// -@safe unittest -{ - auto rnd = MinstdRand0(42); - - assert(rnd.uniform!ubyte == 102); - assert(rnd.uniform!ulong == 4838462006927449017); - - enum Fruit { apple, mango, pear } - version (D_LP64) // https://issues.dlang.org/show_bug.cgi?id=15147 - assert(rnd.uniform!Fruit == Fruit.mango); -} - -@safe unittest -{ - // https://issues.dlang.org/show_bug.cgi?id=21383 - auto rng1 = Xorshift32(123456789); - auto rng2 = rng1.save; - assert(rng1.uniform!dchar == rng2.uniform!dchar); - // https://issues.dlang.org/show_bug.cgi?id=21384 - assert(rng1.uniform!(const shared dchar) <= dchar.max); - // https://issues.dlang.org/show_bug.cgi?id=8671 - double t8671 = 1.0 - uniform(0.0, 1.0); -} - -@safe unittest -{ - static foreach (T; std.meta.AliasSeq!(char, wchar, dchar, byte, ubyte, short, ushort, - int, uint, long, ulong)) - {{ - T init = uniform!T(); - size_t i = 50; - while (--i && uniform!T() == init) {} - assert(i > 0); - - foreach (immutable _; 0 .. 100) - { - auto u = uniform!T(); - static assert(is(typeof(u) == T)); - assert(T.min <= u, "Lower bound violation for uniform!" ~ T.stringof); - assert(u <= T.max, "Upper bound violation for uniform!" ~ T.stringof); - } - }} -} - -/// ditto -auto uniform(E, UniformRandomNumberGenerator) -(ref UniformRandomNumberGenerator urng) -if (is(E == enum) && isUniformRNG!UniformRandomNumberGenerator) -{ - static immutable E[EnumMembers!E.length] members = [EnumMembers!E]; - return members[std.random.uniform(0, members.length, urng)]; -} - -/// Ditto -auto uniform(E)() -if (is(E == enum)) -{ - return uniform!E(rndGen); -} - -@safe unittest -{ - enum Fruit { Apple = 12, Mango = 29, Pear = 72 } - foreach (_; 0 .. 100) - { - foreach (f; [uniform!Fruit(), rndGen.uniform!Fruit()]) - { - assert(f == Fruit.Apple || f == Fruit.Mango || f == Fruit.Pear); - } - } -} - -/** - * Generates a uniformly-distributed floating point number of type - * `T` in the range [0, 1$(RPAREN). If no random number generator is - * specified, the default RNG `rndGen` will be used as the source - * of randomness. - * - * `uniform01` offers a faster generation of random variates than - * the equivalent $(D uniform!"[$(RPAREN)"(0.0, 1.0)) and so may be preferred - * for some applications. - * - * Params: - * rng = (optional) random number generator to use; - * if not specified, defaults to `rndGen` - * - * Returns: - * Floating-point random variate of type `T` drawn from the _uniform - * distribution across the half-open interval [0, 1$(RPAREN). - * - */ -T uniform01(T = double)() -if (isFloatingPoint!T) -{ - return uniform01!T(rndGen); -} - -/// ditto -T uniform01(T = double, UniformRNG)(ref UniformRNG rng) -if (isFloatingPoint!T && isUniformRNG!UniformRNG) -out (result) -{ - assert(0 <= result); - assert(result < 1); -} -do -{ - alias R = typeof(rng.front); - static if (isIntegral!R) - { - enum T factor = 1 / (T(1) + rng.max - rng.min); - } - else static if (isFloatingPoint!R) - { - enum T factor = 1 / (rng.max - rng.min); - } - else - { - static assert(false); - } - - while (true) - { - immutable T u = (rng.front - rng.min) * factor; - rng.popFront(); - - static if (isIntegral!R && T.mant_dig >= (8 * R.sizeof)) - { - /* If RNG variates are integral and T has enough precision to hold - * R without loss, we're guaranteed by the definition of factor - * that precisely u < 1. - */ - return u; - } - else - { - /* Otherwise we have to check whether u is beyond the assumed range - * because of the loss of precision, or for another reason, a - * floating-point RNG can return a variate that is exactly equal to - * its maximum. - */ - if (u < 1) - { - return u; - } - } - } - - // Shouldn't ever get here. - assert(false); -} - -/// -@safe @nogc unittest -{ - import std.math.operations : feqrel; - - auto rnd = MinstdRand0(42); - - // Generate random numbers in the range in the range [0, 1) - auto u1 = uniform01(rnd); - assert(u1 >= 0 && u1 < 1); - - auto u2 = rnd.uniform01!float; - assert(u2 >= 0 && u2 < 1); - - // Confirm that the random values with the initial seed 42 are 0.000328707 and 0.524587 - assert(u1.feqrel(0.000328707) > 20); - assert(u2.feqrel(0.524587) > 20); -} - -@safe @nogc unittest -{ - import std.meta; - static foreach (UniformRNG; PseudoRngTypes) - {{ - - static foreach (T; std.meta.AliasSeq!(float, double, real)) - {{ - UniformRNG rng = UniformRNG(123_456_789); - - auto a = uniform01(); - assert(is(typeof(a) == double)); - assert(0 <= a && a < 1); - - auto b = uniform01(rng); - assert(is(typeof(a) == double)); - assert(0 <= b && b < 1); - - auto c = uniform01!T(); - assert(is(typeof(c) == T)); - assert(0 <= c && c < 1); - - auto d = uniform01!T(rng); - assert(is(typeof(d) == T)); - assert(0 <= d && d < 1); - - T init = uniform01!T(rng); - size_t i = 50; - while (--i && uniform01!T(rng) == init) {} - assert(i > 0); - assert(i < 50); - }} - }} -} - -/** -Generates a uniform probability distribution of size `n`, i.e., an -array of size `n` of positive numbers of type `F` that sum to -`1`. If `useThis` is provided, it is used as storage. - */ -F[] uniformDistribution(F = double)(size_t n, F[] useThis = null) -if (isFloatingPoint!F) -{ - import std.numeric : normalize; - useThis.length = n; - foreach (ref e; useThis) - { - e = uniform(0.0, 1); - } - normalize(useThis); - return useThis; -} - -/// -@safe unittest -{ - import std.algorithm.iteration : reduce; - import std.math.operations : isClose; - - auto a = uniformDistribution(5); - assert(a.length == 5); - assert(isClose(reduce!"a + b"(a), 1)); - - a = uniformDistribution(10, a); - assert(a.length == 10); - assert(isClose(reduce!"a + b"(a), 1)); -} - -/** -Returns a random, uniformly chosen, element `e` from the supplied -$(D Range range). If no random number generator is passed, the default -`rndGen` is used. - -Params: - range = a random access range that has the `length` property defined - urng = (optional) random number generator to use; - if not specified, defaults to `rndGen` - -Returns: - A single random element drawn from the `range`. If it can, it will - return a `ref` to the $(D range element), otherwise it will return - a copy. - */ -auto ref choice(Range, RandomGen = Random)(Range range, ref RandomGen urng) -if (isRandomAccessRange!Range && hasLength!Range && isUniformRNG!RandomGen) -{ - assert(range.length > 0, - __PRETTY_FUNCTION__ ~ ": invalid Range supplied. Range cannot be empty"); - - return range[uniform(size_t(0), $, urng)]; -} - -/// ditto -auto ref choice(Range)(Range range) -{ - return choice(range, rndGen); -} - -/// ditto -auto ref choice(Range, RandomGen = Random)(ref Range range, ref RandomGen urng) -if (isRandomAccessRange!Range && hasLength!Range && isUniformRNG!RandomGen) -{ - assert(range.length > 0, - __PRETTY_FUNCTION__ ~ ": invalid Range supplied. Range cannot be empty"); - return range[uniform(size_t(0), $, urng)]; -} - -/// ditto -auto ref choice(Range)(ref Range range) -{ - return choice(range, rndGen); -} - -/// -@safe unittest -{ - auto rnd = MinstdRand0(42); - - auto elem = [1, 2, 3, 4, 5].choice(rnd); - version (D_LP64) // https://issues.dlang.org/show_bug.cgi?id=15147 - assert(elem == 3); -} - -@safe unittest -{ - import std.algorithm.searching : canFind; - - static class MyTestClass - { - int x; - - this(int x) - { - this.x = x; - } - } - - MyTestClass[] testClass; - foreach (i; 0 .. 5) - { - testClass ~= new MyTestClass(i); - } - - auto elem = choice(testClass); - - assert(canFind!((ref MyTestClass a, ref MyTestClass b) => a.x == b.x)(testClass, elem), - "Choice did not return a valid element from the given Range"); -} - -@system unittest -{ - import std.algorithm.iteration : map; - import std.algorithm.searching : canFind; - - auto array = [1, 2, 3, 4, 5]; - auto elemAddr = &choice(array); - - assert(array.map!((ref e) => &e).canFind(elemAddr), - "Choice did not return a ref to an element from the given Range"); - assert(array.canFind(*(cast(int *)(elemAddr))), - "Choice did not return a valid element from the given Range"); -} - -@safe unittest // https://issues.dlang.org/show_bug.cgi?id=18631 -{ - auto rng = MinstdRand0(42); - const a = [0,1,2]; - const(int[]) b = [0, 1, 2]; - auto x = choice(a); - auto y = choice(b); - auto z = choice(cast(const)[1, 2, 3]); - auto x1 = choice(a, rng); - auto y1 = choice(b, rng); - auto z1 = choice(cast(const)[1, 2, 3], rng); -} - -@safe unittest // Ref range (https://issues.dlang.org/show_bug.cgi?id=18631 PR) -{ - struct TestRange - { - int x; - ref int front() return {return x;} - ref int back() return {return x;} - void popFront() {} - void popBack() {} - bool empty = false; - TestRange save() {return this;} - size_t length = 10; - alias opDollar = length; - ref int opIndex(size_t i) return {return x;} - } - - TestRange r = TestRange(10); - int* s = &choice(r); -} - -/** -Shuffles elements of `r` using `gen` as a shuffler. `r` must be -a random-access range with length. If no RNG is specified, `rndGen` -will be used. - -Params: - r = random-access range whose elements are to be shuffled - gen = (optional) random number generator to use; if not - specified, defaults to `rndGen` -Returns: - The shuffled random-access range. -*/ - -Range randomShuffle(Range, RandomGen)(Range r, ref RandomGen gen) -if (isRandomAccessRange!Range && isUniformRNG!RandomGen) -{ - import std.algorithm.mutation : swapAt; - const n = r.length; - foreach (i; 0 .. n) - { - r.swapAt(i, i + _uniformIndex(n - i, gen)); - } - return r; -} - -/// ditto -Range randomShuffle(Range)(Range r) -if (isRandomAccessRange!Range) -{ - return randomShuffle(r, rndGen); -} - -/// -@safe unittest -{ - auto rnd = MinstdRand0(42); - - auto arr = [1, 2, 3, 4, 5].randomShuffle(rnd); - version (D_LP64) // https://issues.dlang.org/show_bug.cgi?id=15147 - assert(arr == [3, 5, 2, 4, 1]); -} - -@safe unittest -{ - int[10] sa = void; - int[10] sb = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]; - import std.algorithm.sorting : sort; - foreach (RandomGen; PseudoRngTypes) - { - sa[] = sb[]; - auto a = sa[]; - auto b = sb[]; - auto gen = RandomGen(123_456_789); - randomShuffle(a, gen); - sort(a); - assert(a == b); - randomShuffle(a); - sort(a); - assert(a == b); - } - // For backwards compatibility verify randomShuffle(r, gen) - // is equivalent to partialShuffle(r, 0, r.length, gen). - auto gen1 = Xorshift(123_456_789); - auto gen2 = gen1.save(); - sa[] = sb[]; - // @nogc std.random.randomShuffle. - // https://issues.dlang.org/show_bug.cgi?id=19156 - () @nogc nothrow pure { randomShuffle(sa[], gen1); }(); - partialShuffle(sb[], sb.length, gen2); - assert(sa[] == sb[]); -} - -// https://issues.dlang.org/show_bug.cgi?id=18501 -@safe unittest -{ - import std.algorithm.comparison : among; - auto r = randomShuffle([0,1]); - assert(r.among([0,1],[1,0])); -} - -/** -Partially shuffles the elements of `r` such that upon returning $(D r[0 .. n]) -is a random subset of `r` and is randomly ordered. $(D r[n .. r.length]) -will contain the elements not in $(D r[0 .. n]). These will be in an undefined -order, but will not be random in the sense that their order after -`partialShuffle` returns will not be independent of their order before -`partialShuffle` was called. - -`r` must be a random-access range with length. `n` must be less than -or equal to `r.length`. If no RNG is specified, `rndGen` will be used. - -Params: - r = random-access range whose elements are to be shuffled - n = number of elements of `r` to shuffle (counting from the beginning); - must be less than `r.length` - gen = (optional) random number generator to use; if not - specified, defaults to `rndGen` -Returns: - The shuffled random-access range. -*/ -Range partialShuffle(Range, RandomGen)(Range r, in size_t n, ref RandomGen gen) -if (isRandomAccessRange!Range && isUniformRNG!RandomGen) -{ - import std.algorithm.mutation : swapAt; - import std.exception : enforce; - enforce(n <= r.length, "n must be <= r.length for partialShuffle."); - foreach (i; 0 .. n) - { - r.swapAt(i, uniform(i, r.length, gen)); - } - return r; -} - -/// ditto -Range partialShuffle(Range)(Range r, in size_t n) -if (isRandomAccessRange!Range) -{ - return partialShuffle(r, n, rndGen); -} - -/// -@safe unittest -{ - auto rnd = MinstdRand0(42); - - auto arr = [1, 2, 3, 4, 5, 6]; - arr = arr.dup.partialShuffle(1, rnd); - - version (D_LP64) // https://issues.dlang.org/show_bug.cgi?id=15147 - assert(arr == [2, 1, 3, 4, 5, 6]); // 1<->2 - - arr = arr.dup.partialShuffle(2, rnd); - version (D_LP64) // https://issues.dlang.org/show_bug.cgi?id=15147 - assert(arr == [1, 4, 3, 2, 5, 6]); // 1<->2, 2<->4 - - arr = arr.dup.partialShuffle(3, rnd); - version (D_LP64) // https://issues.dlang.org/show_bug.cgi?id=15147 - assert(arr == [5, 4, 6, 2, 1, 3]); // 1<->5, 2<->4, 3<->6 -} - -@safe unittest -{ - import std.algorithm; - foreach (RandomGen; PseudoRngTypes) - { - auto a = [0, 1, 1, 2, 3]; - auto b = a.dup; - - // Pick a fixed seed so that the outcome of the statistical - // test below is deterministic. - auto gen = RandomGen(12345); - - // NUM times, pick LEN elements from the array at random. - immutable int LEN = 2; - immutable int NUM = 750; - int[][] chk; - foreach (step; 0 .. NUM) - { - partialShuffle(a, LEN, gen); - chk ~= a[0 .. LEN].dup; - } - - // Check that each possible a[0 .. LEN] was produced at least once. - // For a perfectly random RandomGen, the probability that each - // particular combination failed to appear would be at most - // 0.95 ^^ NUM which is approximately 1,962e-17. - // As long as hardware failure (e.g. bit flip) probability - // is higher, we are fine with this unittest. - sort(chk); - assert(equal(uniq(chk), [ [0,1], [0,2], [0,3], - [1,0], [1,1], [1,2], [1,3], - [2,0], [2,1], [2,3], - [3,0], [3,1], [3,2], ])); - - // Check that all the elements are still there. - sort(a); - assert(equal(a, b)); - } -} - -/** -Get a random index into a list of weights corresponding to each index - -Similar to rolling a die with relative probabilities stored in `proportions`. -Returns the index in `proportions` that was chosen. - -Note: - Usually, dice are 'fair', meaning that each side has equal probability - to come up, in which case `1 + uniform(0, 6)` can simply be used. - In future Phobos versions, this function might get renamed to something like - `weightedChoice` to avoid confusion. - -Params: - rnd = (optional) random number generator to use; if not - specified, defaults to `rndGen` - proportions = forward range or list of individual values - whose elements correspond to the probabilities - with which to choose the corresponding index - value - -Returns: - Random variate drawn from the index values - [0, ... `proportions.length` - 1], with the probability - of getting an individual index value `i` being proportional to - `proportions[i]`. -*/ -size_t dice(Rng, Num)(ref Rng rnd, Num[] proportions...) -if (isNumeric!Num && isForwardRange!Rng) -{ - return diceImpl(rnd, proportions); -} - -/// Ditto -size_t dice(R, Range)(ref R rnd, Range proportions) -if (isForwardRange!Range && isNumeric!(ElementType!Range) && !isArray!Range) -{ - return diceImpl(rnd, proportions); -} - -/// Ditto -size_t dice(Range)(Range proportions) -if (isForwardRange!Range && isNumeric!(ElementType!Range) && !isArray!Range) -{ - return diceImpl(rndGen, proportions); -} - -/// Ditto -size_t dice(Num)(Num[] proportions...) -if (isNumeric!Num) -{ - return diceImpl(rndGen, proportions); -} - -/// -@safe unittest -{ - auto d6 = 1 + dice(1, 1, 1, 1, 1, 1); // fair dice roll - auto d6b = 1 + dice(2, 1, 1, 1, 1, 1); // double the chance to roll '1' - - auto x = dice(0.5, 0.5); // x is 0 or 1 in equal proportions - auto y = dice(50, 50); // y is 0 or 1 in equal proportions - auto z = dice(70, 20, 10); // z is 0 70% of the time, 1 20% of the time, - // and 2 10% of the time -} - -/// -@safe unittest -{ - auto rnd = MinstdRand0(42); - auto z = rnd.dice(70, 20, 10); - assert(z == 0); - z = rnd.dice(30, 20, 40, 10); - assert(z == 2); -} - -private size_t diceImpl(Rng, Range)(ref Rng rng, scope Range proportions) -if (isForwardRange!Range && isNumeric!(ElementType!Range) && isForwardRange!Rng) -in -{ - import std.algorithm.searching : all; - assert(proportions.save.all!"a >= 0"); -} -do -{ - import std.algorithm.iteration : reduce; - import std.exception : enforce; - double sum = reduce!"a + b"(0.0, proportions.save); - enforce(sum > 0, "Proportions in a dice cannot sum to zero"); - immutable point = uniform(0.0, sum, rng); - assert(point < sum); - auto mass = 0.0; - - size_t i = 0; - foreach (e; proportions) - { - mass += e; - if (point < mass) return i; - i++; - } - // this point should not be reached - assert(false); -} - -/// -@safe unittest -{ - auto rnd = Xorshift(123_456_789); - auto i = dice(rnd, 0.0, 100.0); - assert(i == 1); - i = dice(rnd, 100.0, 0.0); - assert(i == 0); - - i = dice(100U, 0U); - assert(i == 0); -} - -/+ @nogc bool array designed for RandomCover. -- constructed with an invariable length -- small length means 0 alloc and bit field (if up to 32(x86) or 64(x64) choices to cover) -- bigger length means non-GC heap allocation(s) and dealloc. +/ -private struct RandomCoverChoices -{ - private size_t* buffer; - private immutable size_t _length; - private immutable bool hasPackedBits; - private enum BITS_PER_WORD = typeof(buffer[0]).sizeof * 8; - - void opAssign(T)(T) @disable; - - this(this) pure nothrow @nogc @trusted - { - import core.stdc.string : memcpy; - import std.internal.memory : enforceMalloc; - - if (!hasPackedBits && buffer !is null) - { - const nBytesToAlloc = size_t.sizeof * (_length / BITS_PER_WORD + int(_length % BITS_PER_WORD != 0)); - void* nbuffer = enforceMalloc(nBytesToAlloc); - buffer = cast(size_t*) memcpy(nbuffer, buffer, nBytesToAlloc); - } - } - - this(size_t numChoices) pure nothrow @nogc @trusted - { - import std.internal.memory : enforceCalloc; - - _length = numChoices; - hasPackedBits = _length <= size_t.sizeof * 8; - if (!hasPackedBits) - { - const nWordsToAlloc = _length / BITS_PER_WORD + int(_length % BITS_PER_WORD != 0); - buffer = cast(size_t*) enforceCalloc(nWordsToAlloc, BITS_PER_WORD / 8); - } - } - - size_t length() const pure nothrow @nogc @safe @property {return _length;} - - ~this() pure nothrow @nogc @trusted - { - import core.memory : pureFree; - - if (!hasPackedBits && buffer !is null) - pureFree(buffer); - } - - bool opIndex(size_t index) const pure nothrow @nogc @trusted - { - assert(index < _length); - import core.bitop : bt; - if (!hasPackedBits) - return cast(bool) bt(buffer, index); - else - return ((cast(size_t) buffer) >> index) & size_t(1); - } - - void opIndexAssign(bool value, size_t index) pure nothrow @nogc @trusted - { - assert(index < _length); - if (!hasPackedBits) - { - import core.bitop : btr, bts; - if (value) - bts(buffer, index); - else - btr(buffer, index); - } - else - { - if (value) - (*cast(size_t*) &buffer) |= size_t(1) << index; - else - (*cast(size_t*) &buffer) &= ~(size_t(1) << index); - } - } -} - -@safe @nogc nothrow unittest -{ - static immutable lengths = [3, 32, 65, 256]; - foreach (length; lengths) - { - RandomCoverChoices c = RandomCoverChoices(length); - assert(c.hasPackedBits == (length <= size_t.sizeof * 8)); - c[0] = true; - c[2] = true; - assert(c[0]); - assert(!c[1]); - assert(c[2]); - c[0] = false; - c[1] = true; - c[2] = false; - assert(!c[0]); - assert(c[1]); - assert(!c[2]); - } -} - -/** -Covers a given range `r` in a random manner, i.e. goes through each -element of `r` once and only once, just in a random order. `r` -must be a random-access range with length. - -If no random number generator is passed to `randomCover`, the -thread-global RNG rndGen will be used internally. - -Params: - r = random-access range to cover - rng = (optional) random number generator to use; - if not specified, defaults to `rndGen` - -Returns: - Range whose elements consist of the elements of `r`, - in random order. Will be a forward range if both `r` and - `rng` are forward ranges, an - $(REF_ALTTEXT input range, isInputRange, std,range,primitives) otherwise. -*/ -struct RandomCover(Range, UniformRNG = void) -if (isRandomAccessRange!Range && (isUniformRNG!UniformRNG || is(UniformRNG == void))) -{ - private Range _input; - private RandomCoverChoices _chosen; - private size_t _current; - private size_t _alreadyChosen = 0; - private bool _isEmpty = false; - - static if (is(UniformRNG == void)) - { - this(Range input) - { - _input = input; - _chosen = RandomCoverChoices(_input.length); - if (_input.empty) - { - _isEmpty = true; - } - else - { - _current = _uniformIndex(_chosen.length, rndGen); - } - } - } - else - { - private UniformRNG _rng; - - this(Range input, ref UniformRNG rng) - { - _input = input; - _rng = rng; - _chosen = RandomCoverChoices(_input.length); - if (_input.empty) - { - _isEmpty = true; - } - else - { - _current = _uniformIndex(_chosen.length, rng); - } - } - - this(Range input, UniformRNG rng) - { - this(input, rng); - } - } - - static if (hasLength!Range) - { - @property size_t length() - { - return _input.length - _alreadyChosen; - } - } - - @property auto ref front() - { - assert(!_isEmpty); - return _input[_current]; - } - - void popFront() - { - assert(!_isEmpty); - - size_t k = _input.length - _alreadyChosen - 1; - if (k == 0) - { - _isEmpty = true; - ++_alreadyChosen; - return; - } - - size_t i; - foreach (e; _input) - { - if (_chosen[i] || i == _current) { ++i; continue; } - // Roll a dice with k faces - static if (is(UniformRNG == void)) - { - auto chooseMe = _uniformIndex(k, rndGen) == 0; - } - else - { - auto chooseMe = _uniformIndex(k, _rng) == 0; - } - assert(k > 1 || chooseMe); - if (chooseMe) - { - _chosen[_current] = true; - _current = i; - ++_alreadyChosen; - return; - } - --k; - ++i; - } - } - - static if (isForwardRange!UniformRNG) - { - @property typeof(this) save() - { - auto ret = this; - ret._input = _input.save; - ret._rng = _rng.save; - return ret; - } - } - - @property bool empty() const { return _isEmpty; } -} - -/// Ditto -auto randomCover(Range, UniformRNG)(Range r, auto ref UniformRNG rng) -if (isRandomAccessRange!Range && isUniformRNG!UniformRNG) -{ - return RandomCover!(Range, UniformRNG)(r, rng); -} - -/// Ditto -auto randomCover(Range)(Range r) -if (isRandomAccessRange!Range) -{ - return RandomCover!(Range, void)(r); -} - -/// -@safe unittest -{ - import std.algorithm.comparison : equal; - import std.range : iota; - auto rnd = MinstdRand0(42); - - version (D_LP64) // https://issues.dlang.org/show_bug.cgi?id=15147 - assert(10.iota.randomCover(rnd).equal([7, 4, 2, 0, 1, 6, 8, 3, 9, 5])); -} - -@safe unittest // cover RandomCoverChoices postblit for heap storage -{ - import std.array : array; - import std.range : iota; - auto a = 1337.iota.randomCover().array; - assert(a.length == 1337); -} - -@nogc nothrow pure @safe unittest -{ - // Optionally @nogc std.random.randomCover - // https://issues.dlang.org/show_bug.cgi?id=14001 - auto rng = Xorshift(123_456_789); - static immutable int[] sa = [1, 2, 3, 4, 5]; - auto r = randomCover(sa, rng); - assert(!r.empty); - const x = r.front; - r.popFront(); - assert(!r.empty); - const y = r.front; - assert(x != y); -} - -@safe unittest -{ - import std.algorithm; - import std.conv; - int[] a = [ 0, 1, 2, 3, 4, 5, 6, 7, 8 ]; - int[] c; - static foreach (UniformRNG; std.meta.AliasSeq!(void, PseudoRngTypes)) - {{ - static if (is(UniformRNG == void)) - { - auto rc = randomCover(a); - static assert(isInputRange!(typeof(rc))); - static assert(!isForwardRange!(typeof(rc))); - } - else - { - auto rng = UniformRNG(123_456_789); - auto rc = randomCover(a, rng); - static assert(isForwardRange!(typeof(rc))); - // check for constructor passed a value-type RNG - auto rc2 = RandomCover!(int[], UniformRNG)(a, UniformRNG(987_654_321)); - static assert(isForwardRange!(typeof(rc2))); - auto rcEmpty = randomCover(c, rng); - assert(rcEmpty.length == 0); - } - - int[] b = new int[9]; - uint i; - foreach (e; rc) - { - //writeln(e); - b[i++] = e; - } - sort(b); - assert(a == b, text(b)); - }} -} - -@safe unittest -{ - // https://issues.dlang.org/show_bug.cgi?id=12589 - int[] r = []; - auto rc = randomCover(r); - assert(rc.length == 0); - assert(rc.empty); - - // https://issues.dlang.org/show_bug.cgi?id=16724 - import std.range : iota; - auto range = iota(10); - auto randy = range.randomCover; - - for (int i=1; i <= range.length; i++) - { - randy.popFront; - assert(randy.length == range.length - i); - } -} - -// RandomSample -/** -Selects a random subsample out of `r`, containing exactly `n` -elements. The order of elements is the same as in the original -range. The total length of `r` must be known. If `total` is -passed in, the total number of sample is considered to be $(D -total). Otherwise, `RandomSample` uses `r.length`. - -Params: - r = range to sample from - n = number of elements to include in the sample; - must be less than or equal to the total number - of elements in `r` and/or the parameter - `total` (if provided) - total = (semi-optional) number of elements of `r` - from which to select the sample (counting from - the beginning); must be less than or equal to - the total number of elements in `r` itself. - May be omitted if `r` has the `.length` - property and the sample is to be drawn from - all elements of `r`. - rng = (optional) random number generator to use; - if not specified, defaults to `rndGen` - -Returns: - Range whose elements consist of a randomly selected subset of - the elements of `r`, in the same order as these elements - appear in `r` itself. Will be a forward range if both `r` - and `rng` are forward ranges, an input range otherwise. - -`RandomSample` implements Jeffrey Scott Vitter's Algorithm D -(see Vitter $(HTTP dx.doi.org/10.1145/358105.893, 1984), $(HTTP -dx.doi.org/10.1145/23002.23003, 1987)), which selects a sample -of size `n` in O(n) steps and requiring O(n) random variates, -regardless of the size of the data being sampled. The exception -to this is if traversing k elements on the input range is itself -an O(k) operation (e.g. when sampling lines from an input file), -in which case the sampling calculation will inevitably be of -O(total). - -RandomSample will throw an exception if `total` is verifiably -less than the total number of elements available in the input, -or if $(D n > total). - -If no random number generator is passed to `randomSample`, the -thread-global RNG rndGen will be used internally. -*/ -struct RandomSample(Range, UniformRNG = void) -if (isInputRange!Range && (isUniformRNG!UniformRNG || is(UniformRNG == void))) -{ - private size_t _available, _toSelect; - private enum ushort _alphaInverse = 13; // Vitter's recommended value. - private double _Vprime; - private Range _input; - private size_t _index; - private enum Skip { None, A, D } - private Skip _skip = Skip.None; - - // If we're using the default thread-local random number generator then - // we shouldn't store a copy of it here. UniformRNG == void is a sentinel - // for this. If we're using a user-specified generator then we have no - // choice but to store a copy. - static if (is(UniformRNG == void)) - { - static if (hasLength!Range) - { - this(Range input, size_t howMany) - { - _input = input; - initialize(howMany, input.length); - } - } - - this(Range input, size_t howMany, size_t total) - { - _input = input; - initialize(howMany, total); - } - } - else - { - UniformRNG _rng; - - static if (hasLength!Range) - { - this(Range input, size_t howMany, ref scope UniformRNG rng) - { - _rng = rng; - _input = input; - initialize(howMany, input.length); - } - - this(Range input, size_t howMany, UniformRNG rng) - { - this(input, howMany, rng); - } - } - - this(Range input, size_t howMany, size_t total, ref scope UniformRNG rng) - { - _rng = rng; - _input = input; - initialize(howMany, total); - } - - this(Range input, size_t howMany, size_t total, UniformRNG rng) - { - this(input, howMany, total, rng); - } - } - - private void initialize(size_t howMany, size_t total) - { - import std.conv : text; - import std.exception : enforce; - _available = total; - _toSelect = howMany; - enforce(_toSelect <= _available, - text("RandomSample: cannot sample ", _toSelect, - " items when only ", _available, " are available")); - static if (hasLength!Range) - { - enforce(_available <= _input.length, - text("RandomSample: specified ", _available, - " items as available when input contains only ", - _input.length)); - } - } - - private void initializeFront() - { - assert(_skip == Skip.None); - // We can save ourselves a random variate by checking right - // at the beginning if we should use Algorithm A. - if ((_alphaInverse * _toSelect) > _available) - { - _skip = Skip.A; - } - else - { - _skip = Skip.D; - _Vprime = newVprime(_toSelect); - } - prime(); - } - -/** - Range primitives. -*/ - @property bool empty() const - { - return _toSelect == 0; - } - -/// Ditto - @property auto ref front() - { - assert(!empty); - // The first sample point must be determined here to avoid - // having it always correspond to the first element of the - // input. The rest of the sample points are determined each - // time we call popFront(). - if (_skip == Skip.None) - { - initializeFront(); - } - return _input.front; - } - -/// Ditto - void popFront() - { - // First we need to check if the sample has - // been initialized in the first place. - if (_skip == Skip.None) - { - initializeFront(); - } - - _input.popFront(); - --_available; - --_toSelect; - ++_index; - prime(); - } - -/// Ditto - static if (isForwardRange!Range && isForwardRange!UniformRNG) - { - static if (is(typeof(((const UniformRNG* p) => (*p).save)(null)) : UniformRNG) - && is(typeof(((const Range* p) => (*p).save)(null)) : Range)) - { - @property typeof(this) save() const - { - auto ret = RandomSample.init; - foreach (fieldIndex, ref val; this.tupleof) - { - static if (is(typeof(val) == const(Range)) || is(typeof(val) == const(UniformRNG))) - ret.tupleof[fieldIndex] = val.save; - else - ret.tupleof[fieldIndex] = val; - } - return ret; - } - } - else - { - @property typeof(this) save() - { - auto ret = this; - ret._input = _input.save; - ret._rng = _rng.save; - return ret; - } - } - } - -/// Ditto - @property size_t length() const - { - return _toSelect; - } - -/** -Returns the index of the visited record. - */ - @property size_t index() - { - if (_skip == Skip.None) - { - initializeFront(); - } - return _index; - } - - private size_t skip() - { - assert(_skip != Skip.None); - - // Step D1: if the number of points still to select is greater - // than a certain proportion of the remaining data points, i.e. - // if n >= alpha * N where alpha = 1/13, we carry out the - // sampling with Algorithm A. - if (_skip == Skip.A) - { - return skipA(); - } - else if ((_alphaInverse * _toSelect) > _available) - { - // We shouldn't get here unless the current selected - // algorithm is D. - assert(_skip == Skip.D); - _skip = Skip.A; - return skipA(); - } - else - { - assert(_skip == Skip.D); - return skipD(); - } - } - -/* -Vitter's Algorithm A, used when the ratio of needed sample values -to remaining data values is sufficiently large. -*/ - private size_t skipA() - { - size_t s; - double v, quot, top; - - if (_toSelect == 1) - { - static if (is(UniformRNG == void)) - { - s = uniform(0, _available); - } - else - { - s = uniform(0, _available, _rng); - } - } - else - { - v = 0; - top = _available - _toSelect; - quot = top / _available; - - static if (is(UniformRNG == void)) - { - v = uniform!"()"(0.0, 1.0); - } - else - { - v = uniform!"()"(0.0, 1.0, _rng); - } - - while (quot > v) - { - ++s; - quot *= (top - s) / (_available - s); - } - } - - return s; - } - -/* -Randomly reset the value of _Vprime. -*/ - private double newVprime(size_t remaining) - { - static if (is(UniformRNG == void)) - { - double r = uniform!"()"(0.0, 1.0); - } - else - { - double r = uniform!"()"(0.0, 1.0, _rng); - } - - return r ^^ (1.0 / remaining); - } - -/* -Vitter's Algorithm D. For an extensive description of the algorithm -and its rationale, see: - - * Vitter, J.S. (1984), "Faster methods for random sampling", - Commun. ACM 27(7): 703--718 - - * Vitter, J.S. (1987) "An efficient algorithm for sequential random - sampling", ACM Trans. Math. Softw. 13(1): 58-67. - -Variable names are chosen to match those in Vitter's paper. -*/ - private size_t skipD() - { - import std.math.traits : isNaN; - import std.math.rounding : trunc; - // Confirm that the check in Step D1 is valid and we - // haven't been sent here by mistake - assert((_alphaInverse * _toSelect) <= _available); - - // Now it's safe to use the standard Algorithm D mechanism. - if (_toSelect > 1) - { - size_t s; - size_t qu1 = 1 + _available - _toSelect; - double x, y1; - - assert(!_Vprime.isNaN()); - - while (true) - { - // Step D2: set values of x and u. - while (1) - { - x = _available * (1-_Vprime); - s = cast(size_t) trunc(x); - if (s < qu1) - break; - _Vprime = newVprime(_toSelect); - } - - static if (is(UniformRNG == void)) - { - double u = uniform!"()"(0.0, 1.0); - } - else - { - double u = uniform!"()"(0.0, 1.0, _rng); - } - - y1 = (u * (cast(double) _available) / qu1) ^^ (1.0/(_toSelect - 1)); - - _Vprime = y1 * ((-x/_available)+1.0) * ( qu1/( (cast(double) qu1) - s ) ); - - // Step D3: if _Vprime <= 1.0 our work is done and we return S. - // Otherwise ... - if (_Vprime > 1.0) - { - size_t top = _available - 1, limit; - double y2 = 1.0, bottom; - - if (_toSelect > (s+1)) - { - bottom = _available - _toSelect; - limit = _available - s; - } - else - { - bottom = _available - (s+1); - limit = qu1; - } - - foreach (size_t t; limit .. _available) - { - y2 *= top/bottom; - top--; - bottom--; - } - - // Step D4: decide whether or not to accept the current value of S. - if (_available/(_available-x) < y1 * (y2 ^^ (1.0/(_toSelect-1)))) - { - // If it's not acceptable, we generate a new value of _Vprime - // and go back to the start of the for (;;) loop. - _Vprime = newVprime(_toSelect); - } - else - { - // If it's acceptable we generate a new value of _Vprime - // based on the remaining number of sample points needed, - // and return S. - _Vprime = newVprime(_toSelect-1); - return s; - } - } - else - { - // Return if condition D3 satisfied. - return s; - } - } - } - else - { - // If only one sample point remains to be taken ... - return cast(size_t) trunc(_available * _Vprime); - } - } - - private void prime() - { - if (empty) - { - return; - } - assert(_available && _available >= _toSelect); - immutable size_t s = skip(); - assert(s + _toSelect <= _available); - static if (hasLength!Range) - { - assert(s + _toSelect <= _input.length); - } - assert(!_input.empty); - _input.popFrontExactly(s); - _index += s; - _available -= s; - assert(_available > 0); - } -} - -/// Ditto -auto randomSample(Range)(Range r, size_t n, size_t total) -if (isInputRange!Range) -{ - return RandomSample!(Range, void)(r, n, total); -} - -/// Ditto -auto randomSample(Range)(Range r, size_t n) -if (isInputRange!Range && hasLength!Range) -{ - return RandomSample!(Range, void)(r, n, r.length); -} - -/// Ditto -auto randomSample(Range, UniformRNG)(Range r, size_t n, size_t total, auto ref UniformRNG rng) -if (isInputRange!Range && isUniformRNG!UniformRNG) -{ - return RandomSample!(Range, UniformRNG)(r, n, total, rng); -} - -/// Ditto -auto randomSample(Range, UniformRNG)(Range r, size_t n, auto ref UniformRNG rng) -if (isInputRange!Range && hasLength!Range && isUniformRNG!UniformRNG) -{ - return RandomSample!(Range, UniformRNG)(r, n, r.length, rng); -} - -/// -@safe unittest -{ - import std.algorithm.comparison : equal; - import std.range : iota; - auto rnd = MinstdRand0(42); - assert(10.iota.randomSample(3, rnd).equal([7, 8, 9])); -} - -@system unittest -{ - // @system because it takes the address of a local - import std.conv : text; - import std.exception; - import std.range; - // For test purposes, an infinite input range - struct TestInputRange - { - private auto r = recurrence!"a[n-1] + 1"(0); - bool empty() @property const pure nothrow { return r.empty; } - auto front() @property pure nothrow { return r.front; } - void popFront() pure nothrow { r.popFront(); } - } - static assert(isInputRange!TestInputRange); - static assert(!isForwardRange!TestInputRange); - - const(int)[] a = [ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 ]; - - foreach (UniformRNG; PseudoRngTypes) - (){ // avoid workaround optimizations for large functions - // https://issues.dlang.org/show_bug.cgi?id=2396 - auto rng = UniformRNG(1234); - /* First test the most general case: randomSample of input range, with and - * without a specified random number generator. - */ - static assert(isInputRange!(typeof(randomSample(TestInputRange(), 5, 10)))); - static assert(isInputRange!(typeof(randomSample(TestInputRange(), 5, 10, rng)))); - static assert(!isForwardRange!(typeof(randomSample(TestInputRange(), 5, 10)))); - static assert(!isForwardRange!(typeof(randomSample(TestInputRange(), 5, 10, rng)))); - // test case with range initialized by direct call to struct - { - auto sample = - RandomSample!(TestInputRange, UniformRNG) - (TestInputRange(), 5, 10, UniformRNG(987_654_321)); - static assert(isInputRange!(typeof(sample))); - static assert(!isForwardRange!(typeof(sample))); - } - - /* Now test the case of an input range with length. We ignore the cases - * already covered by the previous tests. - */ - static assert(isInputRange!(typeof(randomSample(TestInputRange().takeExactly(10), 5)))); - static assert(isInputRange!(typeof(randomSample(TestInputRange().takeExactly(10), 5, rng)))); - static assert(!isForwardRange!(typeof(randomSample(TestInputRange().takeExactly(10), 5)))); - static assert(!isForwardRange!(typeof(randomSample(TestInputRange().takeExactly(10), 5, rng)))); - // test case with range initialized by direct call to struct - { - auto sample = - RandomSample!(typeof(TestInputRange().takeExactly(10)), UniformRNG) - (TestInputRange().takeExactly(10), 5, 10, UniformRNG(654_321_987)); - static assert(isInputRange!(typeof(sample))); - static assert(!isForwardRange!(typeof(sample))); - } - - // Now test the case of providing a forward range as input. - static assert(!isForwardRange!(typeof(randomSample(a, 5)))); - static if (isForwardRange!UniformRNG) - { - static assert(isForwardRange!(typeof(randomSample(a, 5, rng)))); - // ... and test with range initialized directly - { - auto sample = - RandomSample!(const(int)[], UniformRNG) - (a, 5, UniformRNG(321_987_654)); - static assert(isForwardRange!(typeof(sample))); - } - } - else - { - static assert(isInputRange!(typeof(randomSample(a, 5, rng)))); - static assert(!isForwardRange!(typeof(randomSample(a, 5, rng)))); - // ... and test with range initialized directly - { - auto sample = - RandomSample!(const(int)[], UniformRNG) - (a, 5, UniformRNG(789_123_456)); - static assert(isInputRange!(typeof(sample))); - static assert(!isForwardRange!(typeof(sample))); - } - } - - /* Check that randomSample will throw an error if we claim more - * items are available than there actually are, or if we try to - * sample more items than are available. */ - assert(collectExceptionMsg( - randomSample(a, 5, 15) - ) == "RandomSample: specified 15 items as available when input contains only 10"); - assert(collectExceptionMsg( - randomSample(a, 15) - ) == "RandomSample: cannot sample 15 items when only 10 are available"); - assert(collectExceptionMsg( - randomSample(a, 9, 8) - ) == "RandomSample: cannot sample 9 items when only 8 are available"); - assert(collectExceptionMsg( - randomSample(TestInputRange(), 12, 11) - ) == "RandomSample: cannot sample 12 items when only 11 are available"); - - /* Check that sampling algorithm never accidentally overruns the end of - * the input range. If input is an InputRange without .length, this - * relies on the user specifying the total number of available items - * correctly. - */ - { - uint i = 0; - foreach (e; randomSample(a, a.length)) - { - assert(e == i); - ++i; - } - assert(i == a.length); - - i = 0; - foreach (e; randomSample(TestInputRange(), 17, 17)) - { - assert(e == i); - ++i; - } - assert(i == 17); - } - - - // Check length properties of random samples. - assert(randomSample(a, 5).length == 5); - assert(randomSample(a, 5, 10).length == 5); - assert(randomSample(a, 5, rng).length == 5); - assert(randomSample(a, 5, 10, rng).length == 5); - assert(randomSample(TestInputRange(), 5, 10).length == 5); - assert(randomSample(TestInputRange(), 5, 10, rng).length == 5); - - // ... and emptiness! - assert(randomSample(a, 0).empty); - assert(randomSample(a, 0, 5).empty); - assert(randomSample(a, 0, rng).empty); - assert(randomSample(a, 0, 5, rng).empty); - assert(randomSample(TestInputRange(), 0, 10).empty); - assert(randomSample(TestInputRange(), 0, 10, rng).empty); - - /* Test that the (lazy) evaluation of random samples works correctly. - * - * We cover 2 different cases: a sample where the ratio of sample points - * to total points is greater than the threshold for using Algorithm, and - * one where the ratio is small enough (< 1/13) for Algorithm D to be used. - * - * For each, we also cover the case with and without a specified RNG. - */ - { - // Small sample/source ratio, no specified RNG. - uint i = 0; - foreach (e; randomSample(randomCover(a), 5)) - { - ++i; - } - assert(i == 5); - - // Small sample/source ratio, specified RNG. - i = 0; - foreach (e; randomSample(randomCover(a), 5, rng)) - { - ++i; - } - assert(i == 5); - - // Large sample/source ratio, no specified RNG. - i = 0; - foreach (e; randomSample(TestInputRange(), 123, 123_456)) - { - ++i; - } - assert(i == 123); - - // Large sample/source ratio, specified RNG. - i = 0; - foreach (e; randomSample(TestInputRange(), 123, 123_456, rng)) - { - ++i; - } - assert(i == 123); - - /* Sample/source ratio large enough to start with Algorithm D, - * small enough to switch to Algorithm A. - */ - i = 0; - foreach (e; randomSample(TestInputRange(), 10, 131)) - { - ++i; - } - assert(i == 10); - } - - // Test that the .index property works correctly - { - auto sample1 = randomSample(TestInputRange(), 654, 654_321); - for (; !sample1.empty; sample1.popFront()) - { - assert(sample1.front == sample1.index); - } - - auto sample2 = randomSample(TestInputRange(), 654, 654_321, rng); - for (; !sample2.empty; sample2.popFront()) - { - assert(sample2.front == sample2.index); - } - - /* Check that it also works if .index is called before .front. - * See: https://issues.dlang.org/show_bug.cgi?id=10322 - */ - auto sample3 = randomSample(TestInputRange(), 654, 654_321); - for (; !sample3.empty; sample3.popFront()) - { - assert(sample3.index == sample3.front); - } - - auto sample4 = randomSample(TestInputRange(), 654, 654_321, rng); - for (; !sample4.empty; sample4.popFront()) - { - assert(sample4.index == sample4.front); - } - } - - /* Test behaviour if .popFront() is called before sample is read. - * This is a rough-and-ready check that the statistical properties - * are in the ballpark -- not a proper validation of statistical - * quality! This incidentally also checks for reference-type - * initialization bugs, as the foreach () loop will operate on a - * copy of the popFronted (and hence initialized) sample. - */ - { - size_t count0, count1, count99; - foreach (_; 0 .. 50_000) - { - auto sample = randomSample(iota(100), 5, &rng); - sample.popFront(); - foreach (s; sample) - { - if (s == 0) - { - ++count0; - } - else if (s == 1) - { - ++count1; - } - else if (s == 99) - { - ++count99; - } - } - } - /* Statistical assumptions here: this is a sequential sampling process - * so (i) 0 can only be the first sample point, so _can't_ be in the - * remainder of the sample after .popFront() is called. (ii) By similar - * token, 1 can only be in the remainder if it's the 2nd point of the - * whole sample, and hence if 0 was the first; probability of 0 being - * first and 1 second is 5/100 * 4/99 (thank you, Algorithm S:-) and - * so the mean count of 1 should be about 202. Finally, 99 can only - * be the _last_ sample point to be picked, so its probability of - * inclusion should be independent of the .popFront() and it should - * occur with frequency 5/100, hence its count should be about 5000. - * Unfortunately we have to set quite a high tolerance because with - * sample size small enough for unittests to run in reasonable time, - * the variance can be quite high. - */ - assert(count0 == 0); - assert(count1 < 150, text("1: ", count1, " > 150.")); - assert(2_200 < count99, text("99: ", count99, " < 2200.")); - assert(count99 < 2_800, text("99: ", count99, " > 2800.")); - } - - /* Odd corner-cases: RandomSample has 2 constructors that are not called - * by the randomSample() helper functions, but that can be used if the - * constructor is called directly. These cover the case of the user - * specifying input but not input length. - */ - { - auto input1 = TestInputRange().takeExactly(456_789); - static assert(hasLength!(typeof(input1))); - auto sample1 = RandomSample!(typeof(input1), void)(input1, 789); - static assert(isInputRange!(typeof(sample1))); - static assert(!isForwardRange!(typeof(sample1))); - assert(sample1.length == 789); - assert(sample1._available == 456_789); - uint i = 0; - for (; !sample1.empty; sample1.popFront()) - { - assert(sample1.front == sample1.index); - ++i; - } - assert(i == 789); - - auto input2 = TestInputRange().takeExactly(456_789); - static assert(hasLength!(typeof(input2))); - auto sample2 = RandomSample!(typeof(input2), typeof(rng))(input2, 789, rng); - static assert(isInputRange!(typeof(sample2))); - static assert(!isForwardRange!(typeof(sample2))); - assert(sample2.length == 789); - assert(sample2._available == 456_789); - i = 0; - for (; !sample2.empty; sample2.popFront()) - { - assert(sample2.front == sample2.index); - ++i; - } - assert(i == 789); - } - - /* Test that the save property works where input is a forward range, - * and RandomSample is using a (forward range) random number generator - * that is not rndGen. - */ - static if (isForwardRange!UniformRNG) - { - auto sample1 = randomSample(a, 5, rng); - // https://issues.dlang.org/show_bug.cgi?id=15853 - auto sample2 = ((const ref typeof(sample1) a) => a.save)(sample1); - assert(sample1.array() == sample2.array()); - } - - // https://issues.dlang.org/show_bug.cgi?id=8314 - { - auto sample(RandomGen)(uint seed) { return randomSample(a, 1, RandomGen(seed)).front; } - - // Start from 1 because not all RNGs accept 0 as seed. - immutable fst = sample!UniformRNG(1); - uint n = 1; - while (sample!UniformRNG(++n) == fst && n < n.max) {} - assert(n < n.max); - } - }(); -} diff --git a/phobos/std/range/interfaces.d b/phobos/std/range/interfaces.d deleted file mode 100644 index 6d55d41..0000000 --- a/phobos/std/range/interfaces.d +++ /dev/null @@ -1,618 +0,0 @@ -/** -This module is a submodule of $(MREF std, range). - -The main $(MREF std, range) module provides template-based tools for working with -ranges, but sometimes an object-based interface for ranges is needed, such as -when runtime polymorphism is required. For this purpose, this submodule -provides a number of object and `interface` definitions that can be used to -wrap around range objects created by the $(MREF std, range) templates. - -$(SCRIPT inhibitQuickIndex = 1;) -$(DIVC quickindex, -$(BOOKTABLE , - $(TR $(TD $(LREF InputRange)) - $(TD Wrapper for input ranges. - )) - $(TR $(TD $(LREF InputAssignable)) - $(TD Wrapper for input ranges with assignable elements. - )) - $(TR $(TD $(LREF ForwardRange)) - $(TD Wrapper for forward ranges. - )) - $(TR $(TD $(LREF ForwardAssignable)) - $(TD Wrapper for forward ranges with assignable elements. - )) - $(TR $(TD $(LREF BidirectionalRange)) - $(TD Wrapper for bidirectional ranges. - )) - $(TR $(TD $(LREF BidirectionalAssignable)) - $(TD Wrapper for bidirectional ranges with assignable elements. - )) - $(TR $(TD $(LREF RandomAccessFinite)) - $(TD Wrapper for finite random-access ranges. - )) - $(TR $(TD $(LREF RandomAccessAssignable)) - $(TD Wrapper for finite random-access ranges with assignable elements. - )) - $(TR $(TD $(LREF RandomAccessInfinite)) - $(TD Wrapper for infinite random-access ranges. - )) - $(TR $(TD $(LREF OutputRange)) - $(TD Wrapper for output ranges. - )) - $(TR $(TD $(LREF OutputRangeObject)) - $(TD Class that implements the `OutputRange` interface and wraps the - `put` methods in virtual functions. - )) - $(TR $(TD $(LREF outputRangeObject)) - $(TD Convenience function for creating an `OutputRangeObject` with a base - range of type R that accepts types E. - )) - $(TR $(TD $(LREF InputRangeObject)) - $(TD Class that implements the `InputRange` interface and wraps the - input range methods in virtual functions. - )) - $(TR $(TD $(LREF inputRangeObject)) - $(TD Convenience function for creating an `InputRangeObject` - of the proper type. - )) - $(TR $(TD $(LREF MostDerivedInputRange)) - $(TD Returns the interface type that best matches the range. - )) -)) - - -Source: $(PHOBOSSRC std/range/interfaces.d) - -License: $(HTTP boost.org/LICENSE_1_0.txt, Boost License 1.0). - -Authors: $(HTTP erdani.com, Andrei Alexandrescu), David Simcha, and - $(HTTP jmdavisprog.com, Jonathan M Davis). Credit for some of the ideas - in building this module goes to - $(HTTP fantascienza.net/leonardo/so/, Leonardo Maffi). -*/ -module std.range.interfaces; - -import std.meta; -import std.range.primitives; -import std.traits; - -/**These interfaces are intended to provide virtual function-based wrappers - * around input ranges with element type E. This is useful where a well-defined - * binary interface is required, such as when a DLL function or virtual function - * needs to accept a generic range as a parameter. Note that - * $(REF_ALTTEXT isInputRange, isInputRange, std, range, primitives) - * and friends check for conformance to structural interfaces - * not for implementation of these `interface` types. - * - * Limitations: - * - * These interfaces are not capable of forwarding `ref` access to elements. - * - * Infiniteness of the wrapped range is not propagated. - * - * Length is not propagated in the case of non-random access ranges. - * - * See_Also: - * $(LREF inputRangeObject) - */ -interface InputRange(E) { - /// - @property E front(); - - /**Calls $(REF moveFront, std, range, primitives) on the wrapped range, if - * possible. Otherwise, throws an $(LREF UnsupportedRangeMethod) exception. - */ - E moveFront(); - - /// - void popFront(); - - /// - @property bool empty(); - - /* Measurements of the benefits of using opApply instead of range primitives - * for foreach, using timings for iterating over an iota(100_000_000) range - * with an empty loop body, using the same hardware in each case: - * - * Bare Iota struct, range primitives: 278 milliseconds - * InputRangeObject, opApply: 436 milliseconds (1.57x penalty) - * InputRangeObject, range primitives: 877 milliseconds (3.15x penalty) - */ - - /**`foreach` iteration uses opApply, since one delegate call per loop - * iteration is faster than three virtual function calls. - */ - int opApply(scope int delegate(E)); - - /// Ditto - int opApply(scope int delegate(size_t, E)); - -} - -/// -@safe unittest -{ - import std.algorithm.iteration : map; - import std.range : iota; - - void useRange(InputRange!int range) { - // Function body. - } - - // Create a range type. - auto squares = map!"a * a"(iota(10)); - - // Wrap it in an interface. - auto squaresWrapped = inputRangeObject(squares); - - // Use it. - useRange(squaresWrapped); -} - -/**Interface for a forward range of type `E`.*/ -interface ForwardRange(E) : InputRange!E { - /// - @property ForwardRange!E save(); -} - -/**Interface for a bidirectional range of type `E`.*/ -interface BidirectionalRange(E) : ForwardRange!(E) { - /// - @property BidirectionalRange!E save(); - - /// - @property E back(); - - /**Calls $(REF moveBack, std, range, primitives) on the wrapped range, if - * possible. Otherwise, throws an $(LREF UnsupportedRangeMethod) exception - */ - E moveBack(); - - /// - void popBack(); -} - -/**Interface for a finite random access range of type `E`.*/ -interface RandomAccessFinite(E) : BidirectionalRange!(E) { - /// - @property RandomAccessFinite!E save(); - - /// - E opIndex(size_t); - - /// - E moveAt(size_t); - - /// - @property size_t length(); - - /// - alias opDollar = length; - - // Can't support slicing until issues with requiring slicing for all - // finite random access ranges are fully resolved. - version (none) - { - /// - RandomAccessFinite!E opSlice(size_t, size_t); - } -} - -/**Interface for an infinite random access range of type `E`.*/ -interface RandomAccessInfinite(E) : ForwardRange!E { - /// - enum bool empty = false; - - /**Calls $(REF moveAt, std, range, primitives) on the wrapped range, if - * possible. Otherwise, throws an $(LREF UnsupportedRangeMethod) exception. - */ - E moveAt(size_t); - - /// - @property RandomAccessInfinite!E save(); - - /// - E opIndex(size_t); -} - -// https://issues.dlang.org/show_bug.cgi?id=22608 -@safe unittest -{ - static assert(isRandomAccessRange!(RandomAccessInfinite!int)); -} - -/**Adds assignable elements to InputRange.*/ -interface InputAssignable(E) : InputRange!E { - /// - @property void front(E newVal); - - alias front = InputRange!E.front; // overload base interface method -} - -@safe unittest -{ - static assert(isInputRange!(InputAssignable!int)); -} - -/**Adds assignable elements to ForwardRange.*/ -interface ForwardAssignable(E) : InputAssignable!E, ForwardRange!E { - /// - @property ForwardAssignable!E save(); -} - -/**Adds assignable elements to BidirectionalRange.*/ -interface BidirectionalAssignable(E) : ForwardAssignable!E, BidirectionalRange!E { - /// - @property BidirectionalAssignable!E save(); - - /// - @property void back(E newVal); -} - -/**Adds assignable elements to RandomAccessFinite.*/ -interface RandomFiniteAssignable(E) : RandomAccessFinite!E, BidirectionalAssignable!E { - /// - @property RandomFiniteAssignable!E save(); - - /// - void opIndexAssign(E val, size_t index); -} - -/**Interface for an output range of type `E`. Usage is similar to the - * `InputRange` interface and descendants.*/ -interface OutputRange(E) { - /// - void put(E); -} - -// https://issues.dlang.org/show_bug.cgi?id=6973 -@safe unittest -{ - static assert(isOutputRange!(OutputRange!int, int)); -} - - -// CTFE function that generates mixin code for one put() method for each -// type E. -private string putMethods(E...)() -{ - import std.conv : to; - - string ret; - - foreach (ti, Unused; E) - { - ret ~= "void put(E[" ~ to!string(ti) ~ "] e) { .put(_range, e); }"; - } - - return ret; -} - -/**Implements the `OutputRange` interface for all types E and wraps the - * `put` method for each type `E` in a virtual function. - */ -class OutputRangeObject(R, E...) : staticMap!(OutputRange, E) { - // @BUG 4689: There should be constraints on this template class, but - // DMD won't let me put them in. - private R _range; - - /// - this(R range) { - this._range = range; - } - - mixin(putMethods!E()); -} - - -/**Returns the interface type that best matches `R`.*/ -template MostDerivedInputRange(R) -if (isInputRange!(Unqual!R)) -{ - private alias E = ElementType!R; - - static if (isRandomAccessRange!R) - { - static if (isInfinite!R) - { - alias MostDerivedInputRange = RandomAccessInfinite!E; - } - else static if (hasAssignableElements!R) - { - alias MostDerivedInputRange = RandomFiniteAssignable!E; - } - else - { - alias MostDerivedInputRange = RandomAccessFinite!E; - } - } - else static if (isBidirectionalRange!R) - { - static if (hasAssignableElements!R) - { - alias MostDerivedInputRange = BidirectionalAssignable!E; - } - else - { - alias MostDerivedInputRange = BidirectionalRange!E; - } - } - else static if (isForwardRange!R) - { - static if (hasAssignableElements!R) - { - alias MostDerivedInputRange = ForwardAssignable!E; - } - else - { - alias MostDerivedInputRange = ForwardRange!E; - } - } - else - { - static if (hasAssignableElements!R) - { - alias MostDerivedInputRange = InputAssignable!E; - } - else - { - alias MostDerivedInputRange = InputRange!E; - } - } -} - -/**Implements the most derived interface that `R` works with and wraps - * all relevant range primitives in virtual functions. If `R` is already - * derived from the `InputRange` interface, aliases itself away. - */ -template InputRangeObject(R) -if (isInputRange!(Unqual!R)) -{ - static if (is(R : InputRange!(ElementType!R))) - { - alias InputRangeObject = R; - } - else static if (!is(Unqual!R == R)) - { - alias InputRangeObject = InputRangeObject!(Unqual!R); - } - else - { - - /// - class InputRangeObject : MostDerivedInputRange!(R) { - private R _range; - private alias E = ElementType!R; - - this(R range) { - this._range = range; - } - - @property E front() { return _range.front; } - - E moveFront() { - static if (__traits(compiles, _range.moveFront())) - return _range.moveFront(); - else - throw new UnsupportedRangeMethod( - "Cannot move the front of a(n) `" ~ R.stringof ~ "`"); - } - - void popFront() { _range.popFront(); } - @property bool empty() { return _range.empty; } - - static if (isForwardRange!R) - { - @property typeof(this) save() { - return new typeof(this)(_range.save); - } - } - - static if (hasAssignableElements!R) - { - @property void front(E newVal) { - _range.front = newVal; - } - } - - static if (isBidirectionalRange!R) - { - @property E back() { return _range.back; } - - E moveBack() { - static if (__traits(compiles, _range.moveFront())) - return _range.moveBack(); - else - throw new UnsupportedRangeMethod( - "Cannot move the back of a(n) `" ~ R.stringof ~ "`"); - } - - void popBack() { return _range.popBack(); } - - static if (hasAssignableElements!R) - { - @property void back(E newVal) { - _range.back = newVal; - } - } - } - - static if (isRandomAccessRange!R) - { - E opIndex(size_t index) { - return _range[index]; - } - - E moveAt(size_t index) { - static if (__traits(compiles, _range.moveAt(index))) - return _range.moveAt(index); - else - throw new UnsupportedRangeMethod( - "Cannot move an element of a(n) `" ~ R.stringof ~ "`"); - } - - static if (hasAssignableElements!R) - { - void opIndexAssign(E val, size_t index) { - _range[index] = val; - } - } - - static if (!isInfinite!R) - { - @property size_t length() { - return _range.length; - } - - alias opDollar = length; - - // Can't support slicing until all the issues with - // requiring slicing support for finite random access - // ranges are resolved. - version (none) - { - typeof(this) opSlice(size_t lower, size_t upper) { - return new typeof(this)(_range[lower .. upper]); - } - } - } - } - - // Optimization: One delegate call is faster than three virtual - // function calls. Use opApply for foreach syntax. - int opApply(scope int delegate(E) dg) { - int res; - - for (auto r = _range; !r.empty; r.popFront()) - { - res = dg(r.front); - if (res) break; - } - - return res; - } - - int opApply(scope int delegate(size_t, E) dg) { - int res; - - size_t i = 0; - for (auto r = _range; !r.empty; r.popFront()) - { - res = dg(i, r.front); - if (res) break; - i++; - } - - return res; - } - } - } -} - -/**Convenience function for creating an `InputRangeObject` of the proper type. - * See $(LREF InputRange) for an example. - */ -InputRangeObject!R inputRangeObject(R)(R range) -if (isInputRange!R) -{ - static if (is(R : InputRange!(ElementType!R))) - { - return range; - } - else - { - return new InputRangeObject!R(range); - } -} - -/**Convenience function for creating an `OutputRangeObject` with a base range - * of type `R` that accepts types `E`. -*/ -template outputRangeObject(E...) { - - /// - OutputRangeObject!(R, E) outputRangeObject(R)(R range) { - return new OutputRangeObject!(R, E)(range); - } -} - -/// -@safe unittest -{ - import std.array; - auto app = appender!(uint[])(); - auto appWrapped = outputRangeObject!(uint, uint[])(app); - static assert(is(typeof(appWrapped) : OutputRange!(uint[]))); - static assert(is(typeof(appWrapped) : OutputRange!(uint))); -} - -/// Thrown when an interface method is not supported by the wrapped range -class UnsupportedRangeMethod : Exception -{ - import std.exception : basicExceptionCtors; - - mixin basicExceptionCtors; -} - -@system unittest -{ - import std.algorithm.comparison : equal; - import std.array; - import std.internal.test.dummyrange; - - static void testEquality(R)(iInputRange r1, R r2) { - assert(equal(r1, r2)); - } - - auto arr = [1,2,3,4]; - RandomFiniteAssignable!int arrWrapped = inputRangeObject(arr); - static assert(isRandomAccessRange!(typeof(arrWrapped))); - // static assert(hasSlicing!(typeof(arrWrapped))); - static assert(hasLength!(typeof(arrWrapped))); - arrWrapped[0] = 0; - assert(arr[0] == 0); - assert(arr.moveFront() == 0); - assert(arr.moveBack() == 4); - assert(arr.moveAt(1) == 2); - - foreach (elem; arrWrapped) {} - foreach (i, elem; arrWrapped) {} - - assert(inputRangeObject(arrWrapped) is arrWrapped); - - foreach (DummyType; AllDummyRanges) - { - auto d = DummyType.init; - static assert(propagatesRangeType!(DummyType, - typeof(inputRangeObject(d)))); - static assert(propagatesRangeType!(DummyType, - MostDerivedInputRange!DummyType)); - InputRange!uint wrapped = inputRangeObject(d); - assert(equal(wrapped, d)); - } - - // Test output range stuff. - auto app = appender!(uint[])(); - auto appWrapped = outputRangeObject!(uint, uint[])(app); - static assert(is(typeof(appWrapped) : OutputRange!(uint[]))); - static assert(is(typeof(appWrapped) : OutputRange!(uint))); - - appWrapped.put(1); - appWrapped.put([2, 3]); - assert(app.data.length == 3); - assert(equal(app.data, [1,2,3])); -} - -// https://issues.dlang.org/show_bug.cgi?id=19544 -@safe unittest -{ - import std.range : repeat; - - static struct HasCC - { - inout this(ref inout typeof(this)) {} - } - - auto r = repeat(HasCC.init).inputRangeObject; -} diff --git a/phobos/std/range/package.d b/phobos/std/range/package.d deleted file mode 100644 index 995bf1e..0000000 --- a/phobos/std/range/package.d +++ /dev/null @@ -1,14370 +0,0 @@ -// Written in the D programming language. - -/** -This module defines the notion of a range. Ranges generalize the concept of -arrays, lists, or anything that involves sequential access. This abstraction -enables the same set of algorithms (see $(MREF std, algorithm)) to be used -with a vast variety of different concrete types. For example, -a linear search algorithm such as $(REF find, std, algorithm, searching) -works not just for arrays, but for linked-lists, input files, -incoming network data, etc. - -Guides: - -There are many articles available that can bolster understanding ranges: - -$(UL - $(LI Ali Çehreli's $(HTTP ddili.org/ders/d.en/ranges.html, tutorial on ranges) - for the basics of working with and creating range-based code.) - $(LI Jonathan M. Davis $(LINK2 http://dconf.org/2015/talks/davis.html, $(I Introduction to Ranges)) - talk at DConf 2015 a vivid introduction from its core constructs to practical advice.) - $(LI The DLang Tour's $(LINK2 http://tour.dlang.org/tour/en/basics/ranges, chapter on ranges) - for an interactive introduction.) - $(LI H. S. Teoh's $(LINK2 http://wiki.dlang.org/Component_programming_with_ranges, tutorial on - component programming with ranges) for a real-world showcase of the influence - of range-based programming on complex algorithms.) - $(LI Andrei Alexandrescu's article - $(LINK2 http://www.informit.com/articles/printerfriendly.aspx?p=1407357$(AMP)rll=1, - $(I On Iteration)) for conceptual aspect of ranges and the motivation - ) -) - -Submodules: - -This module has two submodules: - -The $(MREF std, range, primitives) submodule -provides basic range functionality. It defines several templates for testing -whether a given object is a range, what kind of range it is, and provides -some common range operations. - -The $(MREF std, range, interfaces) submodule -provides object-based interfaces for working with ranges via runtime -polymorphism. - -The remainder of this module provides a rich set of range creation and -composition templates that let you construct new ranges out of existing ranges: - - -$(SCRIPT inhibitQuickIndex = 1;) -$(DIVC quickindex, -$(BOOKTABLE , - $(TR $(TD $(LREF chain)) - $(TD Concatenates several ranges into a single range. - )) - $(TR $(TD $(LREF choose)) - $(TD Chooses one of two ranges at runtime based on a boolean condition. - )) - $(TR $(TD $(LREF chooseAmong)) - $(TD Chooses one of several ranges at runtime based on an index. - )) - $(TR $(TD $(LREF chunks)) - $(TD Creates a range that returns fixed-size chunks of the original - range. - )) - $(TR $(TD $(LREF cycle)) - $(TD Creates an infinite range that repeats the given forward range - indefinitely. Good for implementing circular buffers. - )) - $(TR $(TD $(LREF drop)) - $(TD Creates the range that results from discarding the first $(I n) - elements from the given range. - )) - $(TR $(TD $(LREF dropBack)) - $(TD Creates the range that results from discarding the last $(I n) - elements from the given range. - )) - $(TR $(TD $(LREF dropExactly)) - $(TD Creates the range that results from discarding exactly $(I n) - of the first elements from the given range. - )) - $(TR $(TD $(LREF dropBackExactly)) - $(TD Creates the range that results from discarding exactly $(I n) - of the last elements from the given range. - )) - $(TR $(TD $(LREF dropOne)) - $(TD Creates the range that results from discarding - the first element from the given range. - )) - $(TR $(TD $(D $(LREF dropBackOne))) - $(TD Creates the range that results from discarding - the last element from the given range. - )) - $(TR $(TD $(LREF enumerate)) - $(TD Iterates a range with an attached index variable. - )) - $(TR $(TD $(LREF evenChunks)) - $(TD Creates a range that returns a number of chunks of - approximately equal length from the original range. - )) - $(TR $(TD $(LREF frontTransversal)) - $(TD Creates a range that iterates over the first elements of the - given ranges. - )) - $(TR $(TD $(LREF generate)) - $(TD Creates a range by successive calls to a given function. This - allows to create ranges as a single delegate. - )) - $(TR $(TD $(LREF indexed)) - $(TD Creates a range that offers a view of a given range as though - its elements were reordered according to a given range of indices. - )) - $(TR $(TD $(LREF iota)) - $(TD Creates a range consisting of numbers between a starting point - and ending point, spaced apart by a given interval. - )) - $(TR $(TD $(LREF lockstep)) - $(TD Iterates $(I n) ranges in lockstep, for use in a `foreach` - loop. Similar to `zip`, except that `lockstep` is designed - especially for `foreach` loops. - )) - $(TR $(TD $(LREF nullSink)) - $(TD An output range that discards the data it receives. - )) - $(TR $(TD $(LREF only)) - $(TD Creates a range that iterates over the given arguments. - )) - $(TR $(TD $(LREF padLeft)) - $(TD Pads a range to a specified length by adding a given element to - the front of the range. Is lazy if the range has a known length. - )) - $(TR $(TD $(LREF padRight)) - $(TD Lazily pads a range to a specified length by adding a given element to - the back of the range. - )) - $(TR $(TD $(LREF radial)) - $(TD Given a random-access range and a starting point, creates a - range that alternately returns the next left and next right element to - the starting point. - )) - $(TR $(TD $(LREF recurrence)) - $(TD Creates a forward range whose values are defined by a - mathematical recurrence relation. - )) - $(TR $(TD $(LREF refRange)) - $(TD Pass a range by reference. Both the original range and the RefRange - will always have the exact same elements. - Any operation done on one will affect the other. - )) - $(TR $(TD $(LREF repeat)) - $(TD Creates a range that consists of a single element repeated $(I n) - times, or an infinite range repeating that element indefinitely. - )) - $(TR $(TD $(LREF retro)) - $(TD Iterates a bidirectional range backwards. - )) - $(TR $(TD $(LREF roundRobin)) - $(TD Given $(I n) ranges, creates a new range that return the $(I n) - first elements of each range, in turn, then the second element of each - range, and so on, in a round-robin fashion. - )) - $(TR $(TD $(LREF sequence)) - $(TD Similar to `recurrence`, except that a random-access range is - created. - )) - $(TR $(TD $(D $(LREF slide))) - $(TD Creates a range that returns a fixed-size sliding window - over the original range. Unlike chunks, - it advances a configurable number of items at a time, - not one chunk at a time. - )) - $(TR $(TD $(LREF stride)) - $(TD Iterates a range with stride $(I n). - )) - $(TR $(TD $(LREF tail)) - $(TD Return a range advanced to within `n` elements of the end of - the given range. - )) - $(TR $(TD $(LREF take)) - $(TD Creates a sub-range consisting of only up to the first $(I n) - elements of the given range. - )) - $(TR $(TD $(LREF takeExactly)) - $(TD Like `take`, but assumes the given range actually has $(I n) - elements, and therefore also defines the `length` property. - )) - $(TR $(TD $(LREF takeNone)) - $(TD Creates a random-access range consisting of zero elements of the - given range. - )) - $(TR $(TD $(LREF takeOne)) - $(TD Creates a random-access range consisting of exactly the first - element of the given range. - )) - $(TR $(TD $(LREF tee)) - $(TD Creates a range that wraps a given range, forwarding along - its elements while also calling a provided function with each element. - )) - $(TR $(TD $(LREF transposed)) - $(TD Transposes a range of ranges. - )) - $(TR $(TD $(LREF transversal)) - $(TD Creates a range that iterates over the $(I n)'th elements of the - given random-access ranges. - )) - $(TR $(TD $(LREF zip)) - $(TD Given $(I n) ranges, creates a range that successively returns a - tuple of all the first elements, a tuple of all the second elements, - etc. - )) -)) - -Sortedness: - -Ranges whose elements are sorted afford better efficiency with certain -operations. For this, the $(LREF assumeSorted) function can be used to -construct a $(LREF SortedRange) from a pre-sorted range. The $(REF -sort, std, algorithm, sorting) function also conveniently -returns a $(LREF SortedRange). $(LREF SortedRange) objects provide some additional -range operations that take advantage of the fact that the range is sorted. - -Source: $(PHOBOSSRC std/range/package.d) - -License: $(HTTP boost.org/LICENSE_1_0.txt, Boost License 1.0). - -Authors: $(HTTP erdani.com, Andrei Alexandrescu), David Simcha, - $(HTTP jmdavisprog.com, Jonathan M Davis), and Jack Stouffer. Credit - for some of the ideas in building this module goes to - $(HTTP fantascienza.net/leonardo/so/, Leonardo Maffi). - */ -module std.range; - -public import std.array; -public import std.range.interfaces; -public import std.range.primitives; -public import std.typecons : Flag, Yes, No, Rebindable, rebindable; - -import std.internal.attributes : betterC; -import std.meta : aliasSeqOf, allSatisfy, anySatisfy, staticMap; -import std.traits : CommonType, isCallable, isFloatingPoint, isIntegral, - isPointer, isSomeFunction, isStaticArray, Unqual, isInstanceOf; - - -/** -Iterates a bidirectional range backwards. The original range can be -accessed by using the `source` property. Applying retro twice to -the same range yields the original range. - -Params: - r = the bidirectional range to iterate backwards - -Returns: - A bidirectional range with length if `r` also provides a length. Or, - if `r` is a random access range, then the return value will be random - access as well. -See_Also: - $(REF reverse, std,algorithm,mutation) for mutating the source range directly. - */ -auto retro(Range)(Range r) -if (isBidirectionalRange!(Unqual!Range)) -{ - // Check for retro(retro(r)) and just return r in that case - static if (is(typeof(retro(r.source)) == Range)) - { - return r.source; - } - else - { - static struct Result() - { - private alias R = Unqual!Range; - - // User code can get and set source, too - R source; - - static if (hasLength!R) - { - size_t retroIndex(size_t n) - { - return source.length - n - 1; - } - } - - public: - alias Source = R; - - @property bool empty() { return source.empty; } - @property auto save() - { - return Result(source.save); - } - @property auto ref front() { return source.back; } - void popFront() { source.popBack(); } - @property auto ref back() { return source.front; } - void popBack() { source.popFront(); } - - static if (is(typeof(source.moveBack()))) - { - ElementType!R moveFront() - { - return source.moveBack(); - } - } - - static if (is(typeof(source.moveFront()))) - { - ElementType!R moveBack() - { - return source.moveFront(); - } - } - - static if (hasAssignableElements!R) - { - @property void front(ElementType!R val) - { - import core.lifetime : forward; - - // __ctfe check is workaround for https://issues.dlang.org/show_bug.cgi?id=21542 - source.back = __ctfe ? val : forward!val; - } - - @property void back(ElementType!R val) - { - import core.lifetime : forward; - - // __ctfe check is workaround for https://issues.dlang.org/show_bug.cgi?id=21542 - source.front = __ctfe ? val : forward!val; - } - } - - static if (isRandomAccessRange!(R) && hasLength!(R)) - { - auto ref opIndex(size_t n) { return source[retroIndex(n)]; } - - static if (hasAssignableElements!R) - { - void opIndexAssign(ElementType!R val, size_t n) - { - import core.lifetime : forward; - - // __ctfe check is workaround for https://issues.dlang.org/show_bug.cgi?id=21542 - source[retroIndex(n)] = __ctfe ? val : forward!val; - } - } - - static if (is(typeof(source.moveAt(0)))) - { - ElementType!R moveAt(size_t index) - { - return source.moveAt(retroIndex(index)); - } - } - - static if (hasSlicing!R) - typeof(this) opSlice(size_t a, size_t b) - { - return typeof(this)(source[source.length - b .. source.length - a]); - } - } - - mixin ImplementLength!source; - } - - return Result!()(r); - } -} - - -/// -pure @safe nothrow @nogc unittest -{ - import std.algorithm.comparison : equal; - int[5] a = [ 1, 2, 3, 4, 5 ]; - int[5] b = [ 5, 4, 3, 2, 1 ]; - assert(equal(retro(a[]), b[])); - assert(retro(a[]).source is a[]); - assert(retro(retro(a[])) is a[]); -} - -pure @safe nothrow unittest -{ - import std.algorithm.comparison : equal; - static assert(isBidirectionalRange!(typeof(retro("hello")))); - int[] a; - static assert(is(typeof(a) == typeof(retro(retro(a))))); - assert(retro(retro(a)) is a); - static assert(isRandomAccessRange!(typeof(retro([1, 2, 3])))); - void test(int[] input, int[] witness) - { - auto r = retro(input); - assert(r.front == witness.front); - assert(r.back == witness.back); - assert(equal(r, witness)); - } - test([ 1 ], [ 1 ]); - test([ 1, 2 ], [ 2, 1 ]); - test([ 1, 2, 3 ], [ 3, 2, 1 ]); - test([ 1, 2, 3, 4 ], [ 4, 3, 2, 1 ]); - test([ 1, 2, 3, 4, 5 ], [ 5, 4, 3, 2, 1 ]); - test([ 1, 2, 3, 4, 5, 6 ], [ 6, 5, 4, 3, 2, 1 ]); - - immutable foo = [1,2,3].idup; - auto r = retro(foo); - assert(equal(r, [3, 2, 1])); -} - -pure @safe nothrow unittest -{ - import std.internal.test.dummyrange : AllDummyRanges, propagatesRangeType, - ReturnBy; - - foreach (DummyType; AllDummyRanges) - { - static if (!isBidirectionalRange!DummyType) - { - static assert(!__traits(compiles, Retro!DummyType)); - } - else - { - DummyType dummyRange; - dummyRange.reinit(); - - auto myRetro = retro(dummyRange); - static assert(propagatesRangeType!(typeof(myRetro), DummyType)); - assert(myRetro.front == 10); - assert(myRetro.back == 1); - assert(myRetro.moveFront() == 10); - assert(myRetro.moveBack() == 1); - - static if (isRandomAccessRange!DummyType && hasLength!DummyType) - { - assert(myRetro[0] == myRetro.front); - assert(myRetro.moveAt(2) == 8); - - static if (DummyType.r == ReturnBy.Reference) - { - { - myRetro[9]++; - scope(exit) myRetro[9]--; - assert(dummyRange[0] == 2); - myRetro.front++; - scope(exit) myRetro.front--; - assert(myRetro.front == 11); - myRetro.back++; - scope(exit) myRetro.back--; - assert(myRetro.back == 3); - } - - { - myRetro.front = 0xFF; - scope(exit) myRetro.front = 10; - assert(dummyRange.back == 0xFF); - - myRetro.back = 0xBB; - scope(exit) myRetro.back = 1; - assert(dummyRange.front == 0xBB); - - myRetro[1] = 11; - scope(exit) myRetro[1] = 8; - assert(dummyRange[8] == 11); - } - } - } - } - } -} - -pure @safe nothrow @nogc unittest -{ - import std.algorithm.comparison : equal; - auto LL = iota(1L, 4L); - auto r = retro(LL); - long[3] excepted = [3, 2, 1]; - assert(equal(r, excepted[])); -} - -// https://issues.dlang.org/show_bug.cgi?id=12662 -pure @safe nothrow @nogc unittest -{ - int[3] src = [1,2,3]; - int[] data = src[]; - foreach_reverse (x; data) {} - foreach (x; data.retro) {} -} - -pure @safe nothrow unittest -{ - import std.algorithm.comparison : equal; - - static struct S { - int v; - @disable this(this); - } - - immutable foo = [S(1), S(2), S(3)]; - auto r = retro(foo); - assert(equal(r, [S(3), S(2), S(1)])); -} - -// https://issues.dlang.org/show_bug.cgi?id=24481 -@safe unittest -{ - bool called; - struct Handle - { - int entry; - void opAssign()(auto ref const(typeof(this)) that) const { called = true; } - } - - const(Handle)[5] arr = [Handle(0), Handle(1), Handle(2), Handle(3), Handle(4)]; - auto range = arr[].retro(); - - called = false; - range.front = Handle(42); - assert(called); - - called = false; - range.back = Handle(42); - assert(called); - - called = false; - range[2] = Handle(42); - assert(called); -} - -/** -Iterates range `r` with stride `n`. If the range is a -random-access range, moves by indexing into the range; otherwise, -moves by successive calls to `popFront`. Applying stride twice to -the same range results in a stride with a step that is the -product of the two applications. It is an error for `n` to be 0. - -Params: - r = the $(REF_ALTTEXT input range, isInputRange, std,range,primitives) to stride over - n = the number of elements to skip over - -Returns: - At minimum, an input range. The resulting range will adopt the - range primitives of the underlying range as long as - $(REF hasLength, std,range,primitives) is `true`. - */ -auto stride(Range)(Range r, size_t n) -if (isInputRange!(Unqual!Range)) -in -{ - assert(n != 0, "stride cannot have step zero."); -} -do -{ - import std.algorithm.comparison : min; - - static if (is(typeof(stride(r.source, n)) == Range)) - { - // stride(stride(r, n1), n2) is stride(r, n1 * n2) - return stride(r.source, r._n * n); - } - else - { - static struct Result - { - private alias R = Unqual!Range; - public R source; - private size_t _n; - - // Chop off the slack elements at the end - static if (hasLength!R && - (isRandomAccessRange!R && hasSlicing!R - || isBidirectionalRange!R)) - private void eliminateSlackElements() - { - auto slack = source.length % _n; - - if (slack) - { - slack--; - } - else if (!source.empty) - { - slack = min(_n, source.length) - 1; - } - else - { - slack = 0; - } - if (!slack) return; - static if (isRandomAccessRange!R && hasLength!R && hasSlicing!R) - { - source = source[0 .. source.length - slack]; - } - else static if (isBidirectionalRange!R) - { - foreach (i; 0 .. slack) - { - source.popBack(); - } - } - } - - static if (isForwardRange!R) - { - @property auto save() - { - return Result(source.save, _n); - } - } - - static if (isInfinite!R) - { - enum bool empty = false; - } - else - { - @property bool empty() - { - return source.empty; - } - } - - @property auto ref front() - { - return source.front; - } - - static if (is(typeof(.moveFront(source)))) - { - ElementType!R moveFront() - { - return source.moveFront(); - } - } - - static if (hasAssignableElements!R) - { - @property void front(ElementType!R val) - { - import core.lifetime : forward; - - // __ctfe check is workaround for https://issues.dlang.org/show_bug.cgi?id=21542 - source.front = __ctfe ? val : forward!val; - } - } - - void popFront() - { - source.popFrontN(_n); - } - - static if (isBidirectionalRange!R && hasLength!R) - { - void popBack() - { - popBackN(source, _n); - } - - @property auto ref back() - { - eliminateSlackElements(); - return source.back; - } - - static if (is(typeof(.moveBack(source)))) - { - ElementType!R moveBack() - { - eliminateSlackElements(); - return source.moveBack(); - } - } - - static if (hasAssignableElements!R) - { - @property void back(ElementType!R val) - { - eliminateSlackElements(); - source.back = val; - } - } - } - - static if (isRandomAccessRange!R && hasLength!R) - { - auto ref opIndex(size_t n) - { - return source[_n * n]; - } - - /** - Forwards to $(D moveAt(source, n)). - */ - static if (is(typeof(source.moveAt(0)))) - { - ElementType!R moveAt(size_t n) - { - return source.moveAt(_n * n); - } - } - - static if (hasAssignableElements!R) - { - void opIndexAssign(ElementType!R val, size_t n) - { - source[_n * n] = val; - } - } - } - - static if (hasSlicing!R && hasLength!R) - typeof(this) opSlice(size_t lower, size_t upper) - { - assert(upper >= lower && upper <= length, - "Attempt to get out-of-bounds slice of `stride` range"); - immutable translatedUpper = (upper == 0) ? 0 : - (upper * _n - (_n - 1)); - immutable translatedLower = min(lower * _n, translatedUpper); - - assert(translatedLower <= translatedUpper, - "Overflow when calculating slice of `stride` range"); - - return typeof(this)(source[translatedLower .. translatedUpper], _n); - } - - static if (hasLength!R) - { - @property auto length() - { - return (source.length + _n - 1) / _n; - } - - alias opDollar = length; - } - } - return Result(r, n); - } -} - -/// -pure @safe nothrow unittest -{ - import std.algorithm.comparison : equal; - - int[] a = [ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11 ]; - assert(equal(stride(a, 3), [ 1, 4, 7, 10 ][])); - assert(stride(stride(a, 2), 3) == stride(a, 6)); -} - -pure @safe nothrow @nogc unittest -{ - import std.algorithm.comparison : equal; - - int[4] testArr = [1,2,3,4]; - static immutable result = [1, 3]; - assert(equal(testArr[].stride(2), result)); -} - -debug pure nothrow @system unittest -{//check the contract - int[4] testArr = [1,2,3,4]; - bool passed = false; - scope (success) assert(passed); - import core.exception : AssertError; - //std.exception.assertThrown won't do because it can't infer nothrow - // https://issues.dlang.org/show_bug.cgi?id=12647 - try - { - auto unused = testArr[].stride(0); - } - catch (AssertError unused) - { - passed = true; - } -} - -pure @safe nothrow unittest -{ - import std.algorithm.comparison : equal; - import std.internal.test.dummyrange : AllDummyRanges, propagatesRangeType, - ReturnBy; - - static assert(isRandomAccessRange!(typeof(stride([1, 2, 3], 2)))); - void test(size_t n, int[] input, int[] witness) - { - assert(equal(stride(input, n), witness)); - } - test(1, [], []); - int[] arr = [ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 ]; - assert(stride(stride(arr, 2), 3) is stride(arr, 6)); - test(1, arr, arr); - test(2, arr, [1, 3, 5, 7, 9]); - test(3, arr, [1, 4, 7, 10]); - test(4, arr, [1, 5, 9]); - - // Test slicing. - auto s1 = stride(arr, 1); - assert(equal(s1[1 .. 4], [2, 3, 4])); - assert(s1[1 .. 4].length == 3); - assert(equal(s1[1 .. 5], [2, 3, 4, 5])); - assert(s1[1 .. 5].length == 4); - assert(s1[0 .. 0].empty); - assert(s1[3 .. 3].empty); - // assert(s1[$ .. $].empty); - assert(s1[s1.opDollar .. s1.opDollar].empty); - - auto s2 = stride(arr, 2); - assert(equal(s2[0 .. 2], [1,3])); - assert(s2[0 .. 2].length == 2); - assert(equal(s2[1 .. 5], [3, 5, 7, 9])); - assert(s2[1 .. 5].length == 4); - assert(s2[0 .. 0].empty); - assert(s2[3 .. 3].empty); - // assert(s2[$ .. $].empty); - assert(s2[s2.opDollar .. s2.opDollar].empty); - - // Test fix for https://issues.dlang.org/show_bug.cgi?id=5035 - auto m = [1, 2, 3, 4, 1, 2, 3, 4, 1, 2, 3, 4]; // 3 rows, 4 columns - auto col = stride(m, 4); - assert(equal(col, [1, 1, 1])); - assert(equal(retro(col), [1, 1, 1])); - - immutable int[] immi = [ 1, 2, 3 ]; - static assert(isRandomAccessRange!(typeof(stride(immi, 1)))); - - // Check for infiniteness propagation. - static assert(isInfinite!(typeof(stride(repeat(1), 3)))); - - foreach (DummyType; AllDummyRanges) - { - DummyType dummyRange; - dummyRange.reinit(); - - auto myStride = stride(dummyRange, 4); - - // Should fail if no length and bidirectional b/c there's no way - // to know how much slack we have. - static if (hasLength!DummyType || !isBidirectionalRange!DummyType) - { - static assert(propagatesRangeType!(typeof(myStride), DummyType)); - } - assert(myStride.front == 1); - assert(myStride.moveFront() == 1); - assert(equal(myStride, [1, 5, 9])); - - static if (hasLength!DummyType) - { - assert(myStride.length == 3); - } - - static if (isBidirectionalRange!DummyType && hasLength!DummyType) - { - assert(myStride.back == 9); - assert(myStride.moveBack() == 9); - } - - static if (isRandomAccessRange!DummyType && hasLength!DummyType) - { - assert(myStride[0] == 1); - assert(myStride[1] == 5); - assert(myStride.moveAt(1) == 5); - assert(myStride[2] == 9); - - static assert(hasSlicing!(typeof(myStride))); - } - - static if (DummyType.r == ReturnBy.Reference) - { - // Make sure reference is propagated. - - { - myStride.front++; - scope(exit) myStride.front--; - assert(dummyRange.front == 2); - } - { - myStride.front = 4; - scope(exit) myStride.front = 1; - assert(dummyRange.front == 4); - } - - static if (isBidirectionalRange!DummyType && hasLength!DummyType) - { - { - myStride.back++; - scope(exit) myStride.back--; - assert(myStride.back == 10); - } - { - myStride.back = 111; - scope(exit) myStride.back = 9; - assert(myStride.back == 111); - } - - static if (isRandomAccessRange!DummyType) - { - { - myStride[1]++; - scope(exit) myStride[1]--; - assert(dummyRange[4] == 6); - } - { - myStride[1] = 55; - scope(exit) myStride[1] = 5; - assert(dummyRange[4] == 55); - } - } - } - } - } -} - -pure @safe nothrow unittest -{ - import std.algorithm.comparison : equal; - - auto LL = iota(1L, 10L); - auto s = stride(LL, 3); - assert(equal(s, [1L, 4L, 7L])); -} - -pure @safe nothrow unittest -{ - import std.algorithm.comparison : equal; - - static struct S { - int v; - @disable this(this); - } - - immutable foo = [S(1), S(2), S(3), S(4), S(5)]; - auto r = stride(foo, 3); - assert(equal(r, [S(1), S(4)])); -} - -// https://issues.dlang.org/show_bug.cgi?id=24481 -@safe unittest -{ - bool called; - struct Handle - { - int entry; - void opAssign()(auto ref const(typeof(this)) that) const { called = true; } - } - - const(Handle)[5] arr = [Handle(0), Handle(1), Handle(2), Handle(3), Handle(4)]; - auto range = arr[].stride(2); - - called = false; - range.front = Handle(42); - assert(called); -} - -/** -Spans multiple ranges in sequence. The function `chain` takes any -number of ranges and returns a $(D Chain!(R1, R2,...)) object. The -ranges may be different, but they must have the same element type. The -result is a range that offers the `front`, `popFront`, and $(D -empty) primitives. If all input ranges offer random access and $(D -length), `Chain` offers them as well. - -Note that repeated random access of the resulting range is likely -to perform somewhat badly since lengths of the ranges in the chain have to be -added up for each random access operation. Random access to elements of -the first remaining range is still efficient. - -If only one range is offered to `Chain` or `chain`, the $(D -Chain) type exits the picture by aliasing itself directly to that -range's type. - -Params: - rs = the $(REF_ALTTEXT input ranges, isInputRange, std,range,primitives) to chain together - -Returns: - An input range at minimum. If all of the ranges in `rs` provide - a range primitive, the returned range will also provide that range - primitive. - -See_Also: $(LREF only) to chain values to a range - */ -auto chain(Ranges...)(Ranges rs) -if (Ranges.length > 0 && - allSatisfy!(isInputRange, staticMap!(Unqual, Ranges)) && - !is(CommonType!(staticMap!(ElementType, staticMap!(Unqual, Ranges))) == void)) -{ - static if (Ranges.length == 1) - { - return rs[0]; - } - else - { - static struct Result - { - private: - alias R = staticMap!(Unqual, Ranges); - alias RvalueElementType = CommonType!(staticMap!(.ElementType, R)); - template sameET(A) - { - enum sameET = is(.ElementType!A == RvalueElementType); - } - - enum bool allSameType = allSatisfy!(sameET, R), - bidirectional = allSatisfy!(isBidirectionalRange, R), - mobileElements = allSatisfy!(hasMobileElements, R), - assignableElements = allSameType - && allSatisfy!(hasAssignableElements, R); - - alias ElementType = RvalueElementType; - - static if (allSameType && allSatisfy!(hasLvalueElements, R)) - { - static ref RvalueElementType fixRef(ref RvalueElementType val) - { - return val; - } - } - else - { - static RvalueElementType fixRef(RvalueElementType val) - { - return val; - } - } - - R source; - size_t frontIndex; - // Always points to index one past the last non-empty range, - // because otherwise decrementing while pointing to first range - // would overflow to size_t.max. - static if (bidirectional) size_t backIndex; - else enum backIndex = source.length; - - this(typeof(Result.tupleof) fields) - { - this.tupleof = fields; - } - - public: - this(R input) - { - frontIndex = source.length; - static if (bidirectional) backIndex = 0; - - foreach (i, ref v; input) source[i] = v; - - // We do this separately to avoid invoking `empty` needlessly. - // While not recommended, a range may depend on side effects of - // `empty` call. - foreach (i, ref v; input) if (!v.empty) - { - frontIndex = i; - static if (bidirectional) backIndex = i+1; - break; - } - - // backIndex is already set in the first loop to - // as frontIndex+1, so we'll use that if we don't find a - // non-empty range here. - static if (bidirectional) - static foreach_reverse (i; 1 .. R.length + 1) - { - if (i <= frontIndex + 1) return; - if (!input[i-1].empty) - { - backIndex = i; - return; - } - } - } - - import std.meta : anySatisfy; - - static if (anySatisfy!(isInfinite, R)) - { - // Propagate infiniteness. - enum bool empty = false; - } - else - { - @property bool empty() - { - if (frontIndex == 0) - { - // special handling: we might be in Range.init state! - // For instance, `format!"%s"` uses Range.init to ensure - // that formatting is possible. - // In that case, we must still behave in an internally consistent way. - return source[0].empty; - } - return frontIndex >= backIndex; - } - } - - static if (allSatisfy!(isForwardRange, R)) - { - @property auto save() - { - auto saveI(size_t i)() => source[i].save; - - // TODO: this has the constructor needlessly refind - // frontIndex and backIndex. It'd be better to just copy - // those from `.this`. - auto saveResult = - Result(staticMap!(saveI, aliasSeqOf!(R.length.iota))); - - return saveResult; - } - } - - void popFront() - { - sw1: switch (frontIndex) - { - static foreach (i; 0 .. R.length) - { - case i: - source[i].popFront(); - break sw1; - } - - case R.length: - assert(0, "Attempt to `popFront` of empty `chain` range"); - - default: - assert(0, "Internal library error. Please report it."); - } - - sw2: switch (frontIndex) - { - static foreach (i; 0 .. R.length) - { - case i: - if (source[i].empty) - { - frontIndex++; - goto case; - } - else break sw2; - } - - // Only possible to reach from goto of previous case. - case R.length: - break; - - default: - assert(0, "Internal library error. Please report it."); - } - } - - @property auto ref front() - { - switch (frontIndex) - { - static foreach (i; 0 .. R.length) - { - case i: - return fixRef(source[i].front); - } - - case R.length: - assert(0, "Attempt to get `front` of empty `chain` range"); - - default: - assert(0, "Internal library error. Please report it."); - } - } - - static if (assignableElements) - { - // @@@BUG@@@ - //@property void front(T)(T v) if (is(T : RvalueElementType)) - - @property void front(RvalueElementType v) - { - import core.lifetime : forward; - - sw: switch (frontIndex) - { - static foreach (i; 0 .. R.length) - { - case i: - // __ctfe check is workaround for https://issues.dlang.org/show_bug.cgi?id=21542 - source[i].front = __ctfe ? v : forward!v; - break sw; - } - - case R.length: - assert(0, "Attempt to set `front` of empty `chain` range"); - - default: - assert(0, "Internal library error. Please report it."); - } - } - } - - static if (mobileElements) - { - RvalueElementType moveFront() - { - switch (frontIndex) - { - static foreach (i; 0 .. R.length) - { - case i: - return source[i].moveFront(); - } - - case R.length: - assert(0, "Attempt to `moveFront` of empty `chain` range"); - - default: - assert(0, "Internal library error. Please report it."); - } - } - } - - static if (bidirectional) - { - @property auto ref back() - { - switch (backIndex) - { - static foreach_reverse (i; 1 .. R.length + 1) - { - case i: - return fixRef(source[i-1].back); - } - - case 0: - assert(0, "Attempt to get `back` of empty `chain` range"); - - default: - assert(0, "Internal library error. Please report it."); - } - } - - void popBack() - { - sw1: switch (backIndex) - { - static foreach_reverse (i; 1 .. R.length + 1) - { - case i: - source[i-1].popBack(); - break sw1; - } - - case 0: - assert(0, "Attempt to `popFront` of empty `chain` range"); - - default: - assert(0, "Internal library error. Please report it."); - } - - sw2: switch (backIndex) - { - static foreach_reverse (i; 1 .. R.length + 1) - { - case i: - if (source[i-1].empty) - { - backIndex--; - goto case; - } - else break sw2; - } - - // Only possible to reach from goto of previous case. - case 0: - break; - - default: - assert(0, "Internal library error. Please report it."); - } - } - - static if (mobileElements) - { - RvalueElementType moveBack() - { - switch (backIndex) - { - static foreach_reverse (i; 1 .. R.length + 1) - { - case i: - return source[i-1].moveBack(); - } - - case 0: - assert(0, "Attempt to `moveBack` of empty `chain` range"); - - default: - assert(0, "Internal library error. Please report it."); - } - } - } - - static if (allSameType && allSatisfy!(hasAssignableElements, R)) - { - @property void back(RvalueElementType v) - { - import core.lifetime : forward; - - sw: switch (backIndex) - { - static foreach_reverse (i; 1 .. R.length + 1) - { - case i: - // __ctfe check is workaround for https://issues.dlang.org/show_bug.cgi?id=21542 - source[i - 1].back = __ctfe ? v : forward!v; - break sw; - } - - case 0: - assert(0, "Attempt to set `back` of empty `chain` range"); - - default: - assert(0, "Internal library error. Please report it."); - } - } - } - } - - static if (allSatisfy!(hasLength, R)) - { - @property size_t length() - { - size_t result = 0; - sw: switch (frontIndex) - { - static foreach (i; 0 .. R.length) - { - case i: - result += source[i].length; - if (backIndex == i+1) break sw; - else goto case; - } - - case R.length: - break; - - default: - assert(0, "Internal library error. Please report it."); - } - - return result; - } - - alias opDollar = length; - } - - static if (allSatisfy!(isRandomAccessRange, R)) - { - auto ref opIndex(size_t index) - { - switch (frontIndex) - { - static foreach (i; 0 .. R.length) - { - case i: - static if (!isInfinite!(R[i])) - { - immutable length = source[i].length; - if (index >= length) - { - index -= length; - goto case; - } - } - - return fixRef(source[i][index]); - } - - case R.length: - assert(0, "Attempt to access out-of-bounds index of `chain` range"); - - default: - assert(0, "Internal library error. Please report it."); - } - } - - static if (mobileElements) - { - RvalueElementType moveAt(size_t index) - { - switch (frontIndex) - { - static foreach (i; 0 .. R.length) - { - case i: - static if (!isInfinite!(R[i])) - { - immutable length = source[i].length; - if (index >= length) - { - index -= length; - goto case; - } - } - - return source[i].moveAt(index); - } - - case R.length: - assert(0, "Attempt to move out-of-bounds index of `chain` range"); - - default: - assert(0, "Internal library error. Please report it."); - } - } - } - - static if (allSameType && allSatisfy!(hasAssignableElements, R)) - void opIndexAssign(ElementType v, size_t index) - { - import core.lifetime : forward; - - sw: switch (frontIndex) - { - static foreach (i; 0 .. R.length) - { - case i: - static if (!isInfinite!(R[i])) - { - immutable length = source[i].length; - if (index >= length) - { - index -= length; - goto case; - } - } - - // __ctfe check is workaround for https://issues.dlang.org/show_bug.cgi?id=21542 - source[i][index] = __ctfe ? v : forward!v; - break sw; - } - - case R.length: - assert(0, "Attempt to write out-of-bounds index of `chain` range"); - - default: - assert(0, "Internal library error. Please report it."); - } - } - } - - static if (allSatisfy!(hasLength, R) && allSatisfy!(hasSlicing, R)) - auto opSlice(size_t begin, size_t end) return scope - { - // force staticMap type conversion to Rebindable - static struct ResultRanges - { - staticMap!(Rebindable, typeof(source)) fields; - } - auto sourceI(size_t i)() => rebindable(this.source[i]); - auto resultRanges = ResultRanges(staticMap!(sourceI, aliasSeqOf!(R.length.iota))).fields; - size_t resultFrontIndex = this.frontIndex; - static if (bidirectional) - size_t resultBackIndex = this.backIndex; - - sw: switch (frontIndex) - { - static foreach (i; 0 .. R.length) - { - case i: - immutable len = resultRanges[i].length; - if (len <= begin) - { - resultRanges[i] = resultRanges[i] - [len .. len]; - begin -= len; - resultFrontIndex++; - goto case; - } - else - { - resultRanges[i] = resultRanges[i] - [begin .. len]; - break sw; - } - } - - case R.length: - assert(begin == 0, - "Attempt to access out-of-bounds slice of `chain` range"); - break; - - default: - assert(0, "Internal library error. Please report it."); - } - - // Overflow intentional if end index too big. - // This will trigger the bounds check failure below. - auto cut = length - end; - - sw2: switch (backIndex) - { - static foreach_reverse (i; 1 .. R.length + 1) - { - case i: - immutable len = resultRanges[i-1].length; - if (len <= cut) - { - resultRanges[i-1] = resultRanges[i-1] - [0 .. 0]; - cut -= len; - resultBackIndex--; - goto case; - } - else - { - resultRanges[i-1] = resultRanges[i-1] - [0 .. len - cut]; - break sw2; - } - } - - case 0: - assert(cut == 0, end > length? - "Attempt to access out-of-bounds slice of `chain` range": - "Attempt to access negative length slice of `chain` range"); - break sw2; - - default: - assert(0, "Internal library error. Please report it."); - } - - static if (bidirectional) - return Result(resultRanges, resultFrontIndex, resultBackIndex); - else - return Result(resultRanges, resultFrontIndex); - } - } - return Result(rs); - } -} - -/// -pure @safe nothrow unittest -{ - import std.algorithm.comparison : equal; - - int[] arr1 = [ 1, 2, 3, 4 ]; - int[] arr2 = [ 5, 6 ]; - int[] arr3 = [ 7 ]; - auto s = chain(arr1, arr2, arr3); - assert(s.length == 7); - assert(s[5] == 6); - assert(equal(s, [1, 2, 3, 4, 5, 6, 7][])); -} - -/** - * Range primitives are carried over to the returned range if - * all of the ranges provide them - */ -pure @safe nothrow unittest -{ - import std.algorithm.comparison : equal; - import std.algorithm.sorting : sort; - - int[] arr1 = [5, 2, 8]; - int[] arr2 = [3, 7, 9]; - int[] arr3 = [1, 4, 6]; - - // in-place sorting across all of the arrays - auto s = arr1.chain(arr2, arr3).sort; - - assert(s.equal([1, 2, 3, 4, 5, 6, 7, 8, 9])); - assert(arr1.equal([1, 2, 3])); - assert(arr2.equal([4, 5, 6])); - assert(arr3.equal([7, 8, 9])); -} - -/** -Due to safe type promotion in D, chaining together different -character ranges results in a `uint` range. - -Use $(REF_ALTTEXT byChar, byChar,std,utf), $(REF_ALTTEXT byWchar, byWchar,std,utf), -and $(REF_ALTTEXT byDchar, byDchar,std,utf) on the ranges -to get the type you need. - */ -pure @safe nothrow unittest -{ - import std.utf : byChar, byCodeUnit; - - auto s1 = "string one"; - auto s2 = "string two"; - // s1 and s2 front is dchar because of auto-decoding - static assert(is(typeof(s1.front) == dchar) && is(typeof(s2.front) == dchar)); - - auto r1 = s1.chain(s2); - // chains of ranges of the same character type give that same type - static assert(is(typeof(r1.front) == dchar)); - - auto s3 = "string three".byCodeUnit; - static assert(is(typeof(s3.front) == immutable char)); - auto r2 = s1.chain(s3); - // chaining ranges of mixed character types gives `dchar` - static assert(is(typeof(r2.front) == dchar)); - - // use byChar on character ranges to correctly convert them to UTF-8 - auto r3 = s1.byChar.chain(s3); - static assert(is(typeof(r3.front) == immutable char)); -} - -pure @safe nothrow unittest -{ - import std.algorithm.comparison : equal; - import std.internal.test.dummyrange : AllDummyRanges, dummyLength, - propagatesRangeType; - - { - int[] arr1 = [ 1, 2, 3, 4 ]; - int[] arr2 = [ 5, 6 ]; - int[] arr3 = [ 7 ]; - int[] witness = [ 1, 2, 3, 4, 5, 6, 7 ]; - auto s1 = chain(arr1); - static assert(isRandomAccessRange!(typeof(s1))); - auto s2 = chain(arr1, arr2); - static assert(isBidirectionalRange!(typeof(s2))); - static assert(isRandomAccessRange!(typeof(s2))); - s2.front = 1; - auto s = chain(arr1, arr2, arr3); - assert(s[5] == 6); - assert(equal(s, witness)); - assert(s[4 .. 6].equal(arr2)); - assert(s[2 .. 5].equal([3, 4, 5])); - assert(s[0 .. 0].empty); - assert(s[7 .. $].empty); - assert(s[5] == 6); - } - { - int[] arr1 = [ 1, 2, 3, 4 ]; - int[] witness = [ 1, 2, 3, 4 ]; - assert(equal(chain(arr1), witness)); - } - { - uint[] foo = [1,2,3,4,5]; - uint[] bar = [1,2,3,4,5]; - auto c = chain(foo, bar); - c[3] = 42; - assert(c[3] == 42); - assert(c.moveFront() == 1); - assert(c.moveBack() == 5); - assert(c.moveAt(4) == 5); - assert(c.moveAt(5) == 1); - } - - - // Make sure https://issues.dlang.org/show_bug.cgi?id=3311 is fixed. - // elements are mutable. - assert(equal(chain(iota(0, 3), iota(0, 3)), [0, 1, 2, 0, 1, 2])); - - // Test the case where infinite ranges are present. - auto inf = chain([0,1,2][], cycle([4,5,6][]), [7,8,9][]); // infinite range - assert(inf[0] == 0); - assert(inf[3] == 4); - assert(inf[6] == 4); - assert(inf[7] == 5); - static assert(isInfinite!(typeof(inf))); - - immutable int[] immi = [ 1, 2, 3 ]; - immutable float[] immf = [ 1, 2, 3 ]; - static assert(is(typeof(chain(immi, immf)))); - - // Check that chain at least instantiates and compiles with every possible - // pair of DummyRange types, in either order. - - foreach (DummyType1; AllDummyRanges) - (){ // workaround slow optimizations for large functions - // https://issues.dlang.org/show_bug.cgi?id=2396 - DummyType1 dummy1; - foreach (DummyType2; AllDummyRanges) - { - DummyType2 dummy2; - auto myChain = chain(dummy1, dummy2); - - static assert( - propagatesRangeType!(typeof(myChain), DummyType1, DummyType2) - ); - - assert(myChain.front == 1); - foreach (i; 0 .. dummyLength) - { - myChain.popFront(); - } - assert(myChain.front == 1); - - static if (isBidirectionalRange!DummyType1 && - isBidirectionalRange!DummyType2) { - assert(myChain.back == 10); - } - - static if (isRandomAccessRange!DummyType1 && - isRandomAccessRange!DummyType2) { - assert(myChain[0] == 1); - } - - static if (hasLvalueElements!DummyType1 && hasLvalueElements!DummyType2) - { - static assert(hasLvalueElements!(typeof(myChain))); - } - else - { - static assert(!hasLvalueElements!(typeof(myChain))); - } - } - }(); -} - -pure @safe nothrow @nogc unittest -{ - class Foo{} - immutable(Foo)[] a; - immutable(Foo)[] b; - assert(chain(a, b).empty); -} - -// https://issues.dlang.org/show_bug.cgi?id=18657 -pure @safe unittest -{ - import std.algorithm.comparison : equal; - string s = "foo"; - auto r = refRange(&s).chain("bar"); - assert(equal(r.save, "foobar")); - assert(equal(r, "foobar")); -} - -// https://issues.dlang.org/show_bug.cgi?id=23844 -pure @safe unittest -{ - struct S - { - immutable int value; - } - - auto range = chain(only(S(5)), only(S(6))); - assert(range.array == [S(5), S(6)]); -} - -// https://issues.dlang.org/show_bug.cgi?id=24064 -pure @safe nothrow unittest -{ - import std.algorithm.comparison : equal; - import std.typecons : Nullable; - - immutable Nullable!string foo = "b"; - string[] bar = ["a"]; - assert(chain(bar, foo).equal(["a", "b"])); -} - -pure @safe nothrow @nogc unittest -{ - // support non-copyable items - - static struct S { - int v; - @disable this(this); - } - - S[2] s0, s1; - foreach (ref el; chain(s0[], s1[])) - { - int n = el.v; - } - - S[] s2, s3; - foreach (ref el; chain(s2, s3)) - { - int n = el.v; - } -} - -// https://issues.dlang.org/show_bug.cgi?id=24243 -pure @safe nothrow unittest -{ - import std.algorithm.iteration : filter; - - auto range = chain([2], [3].filter!"a"); - - // This might happen in format!"%s"(range), for instance. - assert(typeof(range).init.empty); -} - -// https://issues.dlang.org/show_bug.cgi?id=24481 -@safe unittest -{ - bool called; - struct Handle - { - int entry; - void opAssign()(auto ref const(typeof(this)) that) const { called = true; } - } - - const(Handle)[5] arr = [Handle(0), Handle(1), Handle(2), Handle(3), Handle(4)]; - auto range = arr[0 .. 2].chain(arr[4 .. 5]); - - called = false; - range.front = Handle(42); - assert(called); - - called = false; - range.back = Handle(42); - assert(called); - - called = false; - range[2] = Handle(42); - assert(called); -} - -/** -Choose one of two ranges at runtime depending on a Boolean condition. - -The ranges may be different, but they must have compatible element types (i.e. -`CommonType` must exist for the two element types). The result is a range -that offers the weakest capabilities of the two (e.g. `ForwardRange` if $(D -R1) is a random-access range and `R2` is a forward range). - -Params: - condition = which range to choose: `r1` if `true`, `r2` otherwise - r1 = the "true" range - r2 = the "false" range - -Returns: - A range type dependent on `R1` and `R2`. - */ -auto choose(R1, R2)(bool condition, return scope R1 r1, return scope R2 r2) -if (isInputRange!(Unqual!R1) && isInputRange!(Unqual!R2) && - !is(CommonType!(ElementType!(Unqual!R1), ElementType!(Unqual!R2)) == void)) -{ - size_t choice = condition? 0: 1; - return ChooseResult!(R1, R2)(choice, r1, r2); -} - -/// -@safe nothrow pure @nogc unittest -{ - import std.algorithm.comparison : equal; - import std.algorithm.iteration : filter, map; - - auto data1 = only(1, 2, 3, 4).filter!(a => a != 3); - auto data2 = only(5, 6, 7, 8).map!(a => a + 1); - - // choose() is primarily useful when you need to select one of two ranges - // with different types at runtime. - static assert(!is(typeof(data1) == typeof(data2))); - - auto chooseRange(bool pickFirst) - { - // The returned range is a common wrapper type that can be used for - // returning or storing either range without running into a type error. - return choose(pickFirst, data1, data2); - - // Simply returning the chosen range without using choose() does not - // work, because map() and filter() return different types. - //return pickFirst ? data1 : data2; // does not compile - } - - auto result = chooseRange(true); - assert(result.equal(only(1, 2, 4))); - - result = chooseRange(false); - assert(result.equal(only(6, 7, 8, 9))); -} - - -private struct ChooseResult(Ranges...) -{ - import std.meta : aliasSeqOf, ApplyLeft; - import std.traits : hasElaborateCopyConstructor, hasElaborateDestructor, - lvalueOf; - - private union - { - Ranges rs; - } - private size_t chosenI; - - private static auto ref actOnChosen(alias foo, ExtraArgs ...) - (ref ChooseResult r, auto ref ExtraArgs extraArgs) - { - ref getI(size_t i)(return ref ChooseResult r) @trusted { return r.rs[i]; } - - switch (r.chosenI) - { - static foreach (candI; 0 .. rs.length) - { - case candI: return foo(getI!candI(r), extraArgs); - } - - default: assert(false); - } - } - - // @trusted because of assignment of r which overlap each other - this(size_t chosen, return scope Ranges rs) @trusted - { - import core.lifetime : emplace; - - // This should be the only place chosenI is ever assigned - // independently - this.chosenI = chosen; - - // Otherwise the compiler will complain about skipping these fields - static foreach (i; 0 .. rs.length) - { - this.rs[i] = Ranges[i].init; - } - - // The relevant field needs to be initialized last so it will overwrite - // the other initializations and not the other way around. - sw: switch (chosenI) - { - static foreach (i; 0 .. rs.length) - { - case i: - emplace(&this.rs[i], rs[i]); - break sw; - } - - default: assert(false); - } - } - - // Some legacy code may still call this with typeof(choose(/*...*/))(/*...*/) - // without this overload the regular constructor would invert the meaning of - // the boolean - static if (rs.length == 2) - pragma(inline, true) - deprecated("Call with size_t (0 = first), or use the choose function") - this(bool firstChosen, Ranges rs) - { - import core.lifetime : move; - this(cast(size_t)(firstChosen? 0: 1), rs[0].move, rs[1].move); - } - - void opAssign(ChooseResult r) - { - ref getI(size_t i)(return ref ChooseResult r) @trusted { return r.rs[i]; } - - static if (anySatisfy!(hasElaborateDestructor, Ranges)) - if (chosenI != r.chosenI) - { - // destroy the current item - actOnChosen!((ref r) => destroy(r))(this); - } - chosenI = r.chosenI; - - sw: switch (chosenI) - { - static foreach (candI; 0 .. rs.length) - { - case candI: getI!candI(this) = getI!candI(r); - break sw; - } - - default: assert(false); - } - } - - // Carefully defined postblit to postblit the appropriate range - static if (anySatisfy!(hasElaborateCopyConstructor, Ranges)) - this(this) - { - actOnChosen!((ref r) { - static if (hasElaborateCopyConstructor!(typeof(r))) r.__xpostblit(); - })(this); - } - - static if (anySatisfy!(hasElaborateDestructor, Ranges)) - ~this() - { - actOnChosen!((ref r) => destroy(r))(this); - } - - // Propagate infiniteness. - static if (allSatisfy!(isInfinite, Ranges)) enum bool empty = false; - else @property bool empty() - { - return actOnChosen!(r => r.empty)(this); - } - - @property auto ref front() - { - static auto ref getFront(R)(ref R r) { return r.front; } - return actOnChosen!getFront(this); - } - - void popFront() - { - return actOnChosen!((ref r) { r.popFront; })(this); - } - - static if (allSatisfy!(isForwardRange, Ranges)) - @property auto save() // return scope inferred - { - auto saveOrInit(size_t i)() - { - ref getI() @trusted { return rs[i]; } - if (i == chosenI) return getI().save; - else return Ranges[i].init; - } - - return typeof(this)(chosenI, staticMap!(saveOrInit, - aliasSeqOf!(rs.length.iota))); - } - - template front(T) - { - private enum overloadValidFor(alias r) = is(typeof(r.front = T.init)); - - static if (allSatisfy!(overloadValidFor, rs)) - void front(T v) - { - actOnChosen!((ref r, T v) { r.front = v; })(this, v); - } - } - - static if (allSatisfy!(hasMobileElements, Ranges)) - auto moveFront() - { - return actOnChosen!((ref r) => r.moveFront)(this); - } - - static if (allSatisfy!(isBidirectionalRange, Ranges)) - { - @property auto ref back() - { - static auto ref getBack(R)(ref R r) { return r.back; } - return actOnChosen!getBack(this); - } - - void popBack() - { - actOnChosen!((ref r) { r.popBack; })(this); - } - - static if (allSatisfy!(hasMobileElements, Ranges)) - auto moveBack() - { - return actOnChosen!((ref r) => r.moveBack)(this); - } - - template back(T) - { - private enum overloadValidFor(alias r) = is(typeof(r.back = T.init)); - - static if (allSatisfy!(overloadValidFor, rs)) - void back(T v) - { - actOnChosen!((ref r, T v) { r.back = v; })(this, v); - } - } - } - - static if (allSatisfy!(hasLength, Ranges)) - { - @property size_t length() - { - return actOnChosen!(r => r.length)(this); - } - alias opDollar = length; - } - - static if (allSatisfy!(isRandomAccessRange, Ranges)) - { - auto ref opIndex(size_t index) - { - static auto ref get(R)(ref R r, size_t index) { return r[index]; } - return actOnChosen!get(this, index); - } - - static if (allSatisfy!(hasMobileElements, Ranges)) - auto moveAt(size_t index) - { - return actOnChosen!((ref r, size_t index) => r.moveAt(index)) - (this, index); - } - - private enum indexAssignable(T, R) = is(typeof(lvalueOf!R[1] = T.init)); - - template opIndexAssign(T) - if (allSatisfy!(ApplyLeft!(indexAssignable, T), Ranges)) - { - void opIndexAssign(T v, size_t index) - { - return actOnChosen!((ref r, size_t index, T v) { r[index] = v; }) - (this, index, v); - } - } - } - - static if (allSatisfy!(hasSlicing, Ranges)) - auto opSlice(size_t begin, size_t end) - { - alias Slice(R) = typeof(R.init[0 .. 1]); - alias Slices = staticMap!(Slice, Ranges); - - auto sliceOrInit(size_t i)() - { - ref getI() @trusted { return rs[i]; } - return i == chosenI? getI()[begin .. end]: Slices[i].init; - } - - return chooseAmong(chosenI, staticMap!(sliceOrInit, - aliasSeqOf!(rs.length.iota))); - } -} - -// https://issues.dlang.org/show_bug.cgi?id=18657 -pure @safe unittest -{ - import std.algorithm.comparison : equal; - string s = "foo"; - auto r = choose(true, refRange(&s), "bar"); - assert(equal(r.save, "foo")); - assert(equal(r, "foo")); -} - -@safe unittest -{ - static void* p; - static struct R - { - void* q; - int front; - bool empty; - void popFront() {} - // `p = q;` is only there to prevent inference of `scope return`. - @property @safe R save() { p = q; return this; } - - } - R r; - choose(true, r, r).save; -} - -// Make sure ChooseResult.save doesn't trust @system user code. -@system unittest // copy is @system -{ - static struct R - { - int front; - bool empty; - void popFront() {} - this(this) @system {} - @property R save() { return R(front, empty); } - } - choose(true, R(), R()).save; - choose(true, [0], R()).save; - choose(true, R(), [0]).save; -} - -@safe unittest // copy is @system -{ - static struct R - { - int front; - bool empty; - void popFront() {} - this(this) @system {} - @property R save() { return R(front, empty); } - } - static assert(!__traits(compiles, choose(true, R(), R()).save)); - static assert(!__traits(compiles, choose(true, [0], R()).save)); - static assert(!__traits(compiles, choose(true, R(), [0]).save)); -} - -@system unittest // .save is @system -{ - static struct R - { - int front; - bool empty; - void popFront() {} - @property R save() @system { return this; } - } - choose(true, R(), R()).save; - choose(true, [0], R()).save; - choose(true, R(), [0]).save; -} - -@safe unittest // .save is @system -{ - static struct R - { - int front; - bool empty; - void popFront() {} - @property R save() @system { return this; } - } - static assert(!__traits(compiles, choose(true, R(), R()).save)); - static assert(!__traits(compiles, choose(true, [0], R()).save)); - static assert(!__traits(compiles, choose(true, R(), [0]).save)); -} - -//https://issues.dlang.org/show_bug.cgi?id=19738 -@safe nothrow pure @nogc unittest -{ - static struct EvilRange - { - enum empty = true; - int front; - void popFront() @safe {} - auto opAssign(const ref EvilRange other) - { - *(cast(uint*) 0xcafebabe) = 0xdeadbeef; - return this; - } - } - - static assert(!__traits(compiles, () @safe - { - auto c1 = choose(true, EvilRange(), EvilRange()); - auto c2 = c1; - c1 = c2; - })); -} - - -// https://issues.dlang.org/show_bug.cgi?id=20495 -@safe unittest -{ - static struct KillableRange - { - int *item; - ref int front() { return *item; } - bool empty() { return *item > 10; } - void popFront() { ++(*item); } - this(this) - { - assert(item is null || cast(size_t) item > 1000); - item = new int(*item); - } - KillableRange save() { return this; } - } - - auto kr = KillableRange(new int(1)); - int[] x = [1,2,3,4,5]; // length is first - - auto chosen = choose(true, x, kr); - auto chosen2 = chosen.save; -} - -pure @safe nothrow unittest -{ - static struct S { - int v; - @disable this(this); - } - - auto a = [S(1), S(2), S(3)]; - auto b = [S(4), S(5), S(6)]; - - auto chosen = choose(true, a, b); - assert(chosen.front.v == 1); - - auto chosen2 = choose(false, a, b); - assert(chosen2.front.v == 4); -} - -// https://issues.dlang.org/show_bug.cgi?id=15708 -@safe unittest -{ - static struct HasPostblit - { - this(this) {} - } - - static struct Range - { - bool empty; - int front; - void popFront() {} - HasPostblit member; - } - - Range range; - int[] arr; - - auto chosen = choose(true, range, arr); - auto copy = chosen; -} - -/** -Choose one of multiple ranges at runtime. - -The ranges may be different, but they must have compatible element types. The -result is a range that offers the weakest capabilities of all `Ranges`. - -Params: - index = which range to choose, must be less than the number of ranges - rs = two or more ranges - -Returns: - The indexed range. If rs consists of only one range, the return type is an - alias of that range's type. - */ -auto chooseAmong(Ranges...)(size_t index, return scope Ranges rs) -if (Ranges.length >= 2 - && allSatisfy!(isInputRange, staticMap!(Unqual, Ranges)) - && !is(CommonType!(staticMap!(ElementType, Ranges)) == void)) -{ - return ChooseResult!Ranges(index, rs); -} - -/// -@safe nothrow pure @nogc unittest -{ - auto test() - { - import std.algorithm.comparison : equal; - - int[4] sarr1 = [1, 2, 3, 4]; - int[2] sarr2 = [5, 6]; - int[1] sarr3 = [7]; - auto arr1 = sarr1[]; - auto arr2 = sarr2[]; - auto arr3 = sarr3[]; - - { - auto s = chooseAmong(0, arr1, arr2, arr3); - auto t = s.save; - assert(s.length == 4); - assert(s[2] == 3); - s.popFront(); - assert(equal(t, only(1, 2, 3, 4))); - } - { - auto s = chooseAmong(1, arr1, arr2, arr3); - assert(s.length == 2); - s.front = 8; - assert(equal(s, only(8, 6))); - } - { - auto s = chooseAmong(1, arr1, arr2, arr3); - assert(s.length == 2); - s[1] = 9; - assert(equal(s, only(8, 9))); - } - { - auto s = chooseAmong(1, arr2, arr1, arr3)[1 .. 3]; - assert(s.length == 2); - assert(equal(s, only(2, 3))); - } - { - auto s = chooseAmong(0, arr1, arr2, arr3); - assert(s.length == 4); - assert(s.back == 4); - s.popBack(); - s.back = 5; - assert(equal(s, only(1, 2, 5))); - s.back = 3; - assert(equal(s, only(1, 2, 3))); - } - { - uint[5] foo = [1, 2, 3, 4, 5]; - uint[5] bar = [6, 7, 8, 9, 10]; - auto c = chooseAmong(1, foo[], bar[]); - assert(c[3] == 9); - c[3] = 42; - assert(c[3] == 42); - assert(c.moveFront() == 6); - assert(c.moveBack() == 10); - assert(c.moveAt(4) == 10); - } - { - import std.range : cycle; - auto s = chooseAmong(0, cycle(arr2), cycle(arr3)); - assert(isInfinite!(typeof(s))); - assert(!s.empty); - assert(s[100] == 8); - assert(s[101] == 9); - assert(s[0 .. 3].equal(only(8, 9, 8))); - } - return 0; - } - // works at runtime - auto a = test(); - // and at compile time - static b = test(); -} - -@safe nothrow pure @nogc unittest -{ - int[3] a = [1, 2, 3]; - long[3] b = [4, 5, 6]; - auto c = chooseAmong(0, a[], b[]); - c[0] = 42; - assert(c[0] == 42); -} - -@safe nothrow pure @nogc unittest -{ - static struct RefAccessRange - { - int[] r; - ref front() @property { return r[0]; } - ref back() @property { return r[$ - 1]; } - void popFront() { r = r[1 .. $]; } - void popBack() { r = r[0 .. $ - 1]; } - auto empty() @property { return r.empty; } - ref opIndex(size_t i) { return r[i]; } - auto length() @property { return r.length; } - alias opDollar = length; - auto save() { return this; } - } - static assert(isRandomAccessRange!RefAccessRange); - static assert(isRandomAccessRange!RefAccessRange); - int[4] a = [4, 3, 2, 1]; - int[2] b = [6, 5]; - auto c = chooseAmong(0, RefAccessRange(a[]), RefAccessRange(b[])); - - void refFunc(ref int a, int target) { assert(a == target); } - - refFunc(c[2], 2); - refFunc(c.front, 4); - refFunc(c.back, 1); -} - - -/** -$(D roundRobin(r1, r2, r3)) yields `r1.front`, then `r2.front`, -then `r3.front`, after which it pops off one element from each and -continues again from `r1`. For example, if two ranges are involved, -it alternately yields elements off the two ranges. `roundRobin` -stops after it has consumed all ranges (skipping over the ones that -finish early). - */ -auto roundRobin(Rs...)(Rs rs) -if (Rs.length > 1 && allSatisfy!(isInputRange, staticMap!(Unqual, Rs))) -{ - struct Result - { - import std.conv : to; - - public Rs source; - private size_t _current = size_t.max; - - @property bool empty() - { - foreach (i, Unused; Rs) - { - if (!source[i].empty) return false; - } - return true; - } - - @property auto ref front() - { - final switch (_current) - { - foreach (i, R; Rs) - { - case i: - assert( - !source[i].empty, - "Attempting to fetch the front of an empty roundRobin" - ); - return source[i].front; - } - } - assert(0); - } - - void popFront() - { - final switch (_current) - { - foreach (i, R; Rs) - { - case i: - source[i].popFront(); - break; - } - } - - auto next = _current == (Rs.length - 1) ? 0 : (_current + 1); - final switch (next) - { - foreach (i, R; Rs) - { - case i: - if (!source[i].empty) - { - _current = i; - return; - } - if (i == _current) - { - _current = _current.max; - return; - } - goto case (i + 1) % Rs.length; - } - } - } - - static if (allSatisfy!(isForwardRange, staticMap!(Unqual, Rs))) - @property auto save() - { - auto saveSource(size_t len)() - { - import std.typecons : tuple; - static assert(len > 0); - static if (len == 1) - { - return tuple(source[0].save); - } - else - { - return saveSource!(len - 1)() ~ - tuple(source[len - 1].save); - } - } - return Result(saveSource!(Rs.length).expand, _current); - } - - static if (allSatisfy!(hasLength, Rs)) - { - @property size_t length() - { - size_t result; - foreach (i, R; Rs) - { - result += source[i].length; - } - return result; - } - - alias opDollar = length; - } - } - - size_t firstNonEmpty = size_t.max; - static foreach (i; 0 .. Rs.length) - { - if (firstNonEmpty == size_t.max && !rs[i].empty) - firstNonEmpty = i; - } - - return Result(rs, firstNonEmpty); -} - -/// -@safe unittest -{ - import std.algorithm.comparison : equal; - - int[] a = [ 1, 2, 3 ]; - int[] b = [ 10, 20, 30, 40 ]; - auto r = roundRobin(a, b); - assert(equal(r, [ 1, 10, 2, 20, 3, 30, 40 ])); -} - -/** - * roundRobin can be used to create "interleave" functionality which inserts - * an element between each element in a range. - */ -@safe unittest -{ - import std.algorithm.comparison : equal; - - auto interleave(R, E)(R range, E element) - if ((isInputRange!R && hasLength!R) || isForwardRange!R) - { - static if (hasLength!R) - immutable len = range.length; - else - immutable len = range.save.walkLength; - - return roundRobin( - range, - element.repeat(len - 1) - ); - } - - assert(interleave([1, 2, 3], 0).equal([1, 0, 2, 0, 3])); -} - -pure @safe unittest -{ - import std.algorithm.comparison : equal; - string f = "foo", b = "bar"; - auto r = roundRobin(refRange(&f), refRange(&b)); - assert(equal(r.save, "fboaor")); - assert(equal(r.save, "fboaor")); -} -pure @safe nothrow unittest -{ - import std.algorithm.comparison : equal; - - static struct S { - int v; - @disable this(this); - } - - S[] a = [ S(1), S(2) ]; - S[] b = [ S(10), S(20) ]; - auto r = roundRobin(a, b); - assert(equal(r, [ S(1), S(10), S(2), S(20) ])); -} - -// https://issues.dlang.org/show_bug.cgi?id=24384 -@safe unittest -{ - auto r = roundRobin("", "a"); - assert(!r.empty); - auto e = r.front; -} - -/** -Iterates a random-access range starting from a given point and -progressively extending left and right from that point. If no initial -point is given, iteration starts from the middle of the -range. Iteration spans the entire range. - -When `startingIndex` is 0 the range will be fully iterated in order -and in reverse order when `r.length` is given. - -Params: - r = a random access range with length and slicing - startingIndex = the index to begin iteration from - -Returns: - A forward range with length - */ -auto radial(Range, I)(Range r, I startingIndex) -if (isRandomAccessRange!(Unqual!Range) && hasLength!(Unqual!Range) && hasSlicing!(Unqual!Range) && isIntegral!I) -{ - if (startingIndex != r.length) ++startingIndex; - return roundRobin(retro(r[0 .. startingIndex]), r[startingIndex .. r.length]); -} - -/// Ditto -auto radial(R)(R r) -if (isRandomAccessRange!(Unqual!R) && hasLength!(Unqual!R) && hasSlicing!(Unqual!R)) -{ - return .radial(r, (r.length - !r.empty) / 2); -} - -/// -@safe unittest -{ - import std.algorithm.comparison : equal; - int[] a = [ 1, 2, 3, 4, 5 ]; - assert(equal(radial(a), [ 3, 4, 2, 5, 1 ])); - a = [ 1, 2, 3, 4 ]; - assert(equal(radial(a), [ 2, 3, 1, 4 ])); - - // If the left end is reached first, the remaining elements on the right - // are concatenated in order: - a = [ 0, 1, 2, 3, 4, 5 ]; - assert(equal(radial(a, 1), [ 1, 2, 0, 3, 4, 5 ])); - - // If the right end is reached first, the remaining elements on the left - // are concatenated in reverse order: - assert(equal(radial(a, 4), [ 4, 5, 3, 2, 1, 0 ])); -} - -@safe unittest -{ - import std.algorithm.comparison : equal; - import std.conv : text; - import std.exception : enforce; - import std.internal.test.dummyrange : DummyRange, Length, RangeType, ReturnBy; - - void test(int[] input, int[] witness) - { - enforce(equal(radial(input), witness), - text(radial(input), " vs. ", witness)); - } - test([], []); - test([ 1 ], [ 1 ]); - test([ 1, 2 ], [ 1, 2 ]); - test([ 1, 2, 3 ], [ 2, 3, 1 ]); - test([ 1, 2, 3, 4 ], [ 2, 3, 1, 4 ]); - test([ 1, 2, 3, 4, 5 ], [ 3, 4, 2, 5, 1 ]); - test([ 1, 2, 3, 4, 5, 6 ], [ 3, 4, 2, 5, 1, 6 ]); - - int[] a = [ 1, 2, 3, 4, 5 ]; - assert(equal(radial(a, 1), [ 2, 3, 1, 4, 5 ])); - assert(equal(radial(a, 0), [ 1, 2, 3, 4, 5 ])); // only right subrange - assert(equal(radial(a, a.length), [ 5, 4, 3, 2, 1 ])); // only left subrange - static assert(isForwardRange!(typeof(radial(a, 1)))); - - auto r = radial([1,2,3,4,5]); - for (auto rr = r.save; !rr.empty; rr.popFront()) - { - assert(rr.front == moveFront(rr)); - } - r.front = 5; - assert(r.front == 5); - - // Test instantiation without lvalue elements. - DummyRange!(ReturnBy.Value, Length.Yes, RangeType.Random) dummy; - assert(equal(radial(dummy, 4), [5, 6, 4, 7, 3, 8, 2, 9, 1, 10])); - - // immutable int[] immi = [ 1, 2 ]; - // static assert(is(typeof(radial(immi)))); -} - -@safe unittest -{ - import std.algorithm.comparison : equal; - - auto LL = iota(1L, 6L); - auto r = radial(LL); - assert(equal(r, [3L, 4L, 2L, 5L, 1L])); -} - -/** -Lazily takes only up to `n` elements of a range. This is -particularly useful when using with infinite ranges. - -Unlike $(LREF takeExactly), `take` does not require that there -are `n` or more elements in `input`. As a consequence, length -information is not applied to the result unless `input` also has -length information. - -Params: - input = an $(REF_ALTTEXT input range, isInputRange, std,range,primitives) - to iterate over up to `n` times - n = the number of elements to take - -Returns: - At minimum, an input range. If the range offers random access - and `length`, `take` offers them as well. - */ -Take!R take(R)(R input, size_t n) -if (isInputRange!(Unqual!R)) -{ - alias U = Unqual!R; - static if (is(R T == Take!T)) - { - import std.algorithm.comparison : min; - return R(input.source, min(n, input._maxAvailable)); - } - else static if (!isInfinite!U && hasSlicing!U) - { - import std.algorithm.comparison : min; - return input[0 .. min(n, input.length)]; - } - else - { - return Take!R(input, n); - } -} - -/// ditto -struct Take(Range) -if (isInputRange!(Unqual!Range) && - //take _cannot_ test hasSlicing on infinite ranges, because hasSlicing uses - //take for slicing infinite ranges. - !((!isInfinite!(Unqual!Range) && hasSlicing!(Unqual!Range)) || is(Range T == Take!T))) -{ - private alias R = Unqual!Range; - - /// User accessible in read and write - public R source; - - private size_t _maxAvailable; - - alias Source = R; - - /// Range primitives - @property bool empty() - { - return _maxAvailable == 0 || source.empty; - } - - /// ditto - @property auto ref front() - { - assert(!empty, - "Attempting to fetch the front of an empty " - ~ Take.stringof); - return source.front; - } - - /// ditto - void popFront() - { - assert(!empty, - "Attempting to popFront() past the end of a " - ~ Take.stringof); - source.popFront(); - --_maxAvailable; - } - - static if (isForwardRange!R) - /// ditto - @property Take save() - { - return Take(source.save, _maxAvailable); - } - - static if (hasAssignableElements!R) - /// ditto - @property void front(ElementType!R v) - { - import core.lifetime : forward; - - assert(!empty, - "Attempting to assign to the front of an empty " - ~ Take.stringof); - - // __ctfe check is workaround for https://issues.dlang.org/show_bug.cgi?id=21542 - source.front = __ctfe ? v : forward!v; - } - - static if (hasMobileElements!R) - { - /// ditto - auto moveFront() - { - assert(!empty, - "Attempting to move the front of an empty " - ~ Take.stringof); - return source.moveFront(); - } - } - - static if (isInfinite!R) - { - /// ditto - @property size_t length() const - { - return _maxAvailable; - } - - /// ditto - alias opDollar = length; - - //Note: Due to Take/hasSlicing circular dependency, - //This needs to be a restrained template. - /// ditto - auto opSlice()(size_t i, size_t j) - if (hasSlicing!R) - { - assert(i <= j, "Invalid slice bounds"); - assert(j <= length, "Attempting to slice past the end of a " - ~ Take.stringof); - return source[i .. j]; - } - } - else static if (hasLength!R) - { - /// ditto - @property size_t length() - { - import std.algorithm.comparison : min; - return min(_maxAvailable, source.length); - } - - alias opDollar = length; - } - - static if (isRandomAccessRange!R) - { - /// ditto - void popBack() - { - assert(!empty, - "Attempting to popBack() past the beginning of a " - ~ Take.stringof); - --_maxAvailable; - } - - /// ditto - @property auto ref back() - { - assert(!empty, - "Attempting to fetch the back of an empty " - ~ Take.stringof); - return source[this.length - 1]; - } - - /// ditto - auto ref opIndex(size_t index) - { - assert(index < length, - "Attempting to index out of the bounds of a " - ~ Take.stringof); - return source[index]; - } - - static if (hasAssignableElements!R) - { - /// ditto - @property void back(ElementType!R v) - { - // This has to return auto instead of void because of - // https://issues.dlang.org/show_bug.cgi?id=4706 - assert(!empty, - "Attempting to assign to the back of an empty " - ~ Take.stringof); - source[this.length - 1] = v; - } - - /// ditto - void opIndexAssign(ElementType!R v, size_t index) - { - assert(index < length, - "Attempting to index out of the bounds of a " - ~ Take.stringof); - source[index] = v; - } - } - - static if (hasMobileElements!R) - { - /// ditto - auto moveBack() - { - assert(!empty, - "Attempting to move the back of an empty " - ~ Take.stringof); - return source.moveAt(this.length - 1); - } - - /// ditto - auto moveAt(size_t index) - { - assert(index < length, - "Attempting to index out of the bounds of a " - ~ Take.stringof); - return source.moveAt(index); - } - } - } - - /** - Access to maximal length of the range. - Note: the actual length of the range depends on the underlying range. - If it has fewer elements, it will stop before maxLength is reached. - */ - @property size_t maxLength() const - { - return _maxAvailable; - } -} - -/// ditto -template Take(R) -if (isInputRange!(Unqual!R) && - ((!isInfinite!(Unqual!R) && hasSlicing!(Unqual!R)) || is(R T == Take!T))) -{ - alias Take = R; -} - -/// -pure @safe nothrow unittest -{ - import std.algorithm.comparison : equal; - - int[] arr1 = [ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 ]; - auto s = take(arr1, 5); - assert(s.length == 5); - assert(s[4] == 5); - assert(equal(s, [ 1, 2, 3, 4, 5 ][])); -} - -/** - * If the range runs out before `n` elements, `take` simply returns the entire - * range (unlike $(LREF takeExactly), which will cause an assertion failure if - * the range ends prematurely): - */ -pure @safe nothrow unittest -{ - import std.algorithm.comparison : equal; - - int[] arr2 = [ 1, 2, 3 ]; - auto t = take(arr2, 5); - assert(t.length == 3); - assert(equal(t, [ 1, 2, 3 ])); -} - -pure @safe nothrow unittest -{ - import std.algorithm.comparison : equal; - import std.internal.test.dummyrange : AllDummyRanges; - - int[] arr1 = [ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 ]; - auto s = take(arr1, 5); - assert(s.length == 5); - assert(s[4] == 5); - assert(equal(s, [ 1, 2, 3, 4, 5 ][])); - assert(equal(retro(s), [ 5, 4, 3, 2, 1 ][])); - - // Test fix for bug 4464. - static assert(is(typeof(s) == Take!(int[]))); - static assert(is(typeof(s) == int[])); - - // Test using narrow strings. - import std.exception : assumeWontThrow; - - auto myStr = "This is a string."; - auto takeMyStr = take(myStr, 7); - assert(assumeWontThrow(equal(takeMyStr, "This is"))); - // Test fix for bug 5052. - auto takeMyStrAgain = take(takeMyStr, 4); - assert(assumeWontThrow(equal(takeMyStrAgain, "This"))); - static assert(is (typeof(takeMyStrAgain) == typeof(takeMyStr))); - takeMyStrAgain = take(takeMyStr, 10); - assert(assumeWontThrow(equal(takeMyStrAgain, "This is"))); - - foreach (DummyType; AllDummyRanges) - { - DummyType dummy; - auto t = take(dummy, 5); - alias T = typeof(t); - - static if (isRandomAccessRange!DummyType) - { - static assert(isRandomAccessRange!T); - assert(t[4] == 5); - - assert(moveAt(t, 1) == t[1]); - assert(t.back == moveBack(t)); - } - else static if (isForwardRange!DummyType) - { - static assert(isForwardRange!T); - } - - for (auto tt = t; !tt.empty; tt.popFront()) - { - assert(tt.front == moveFront(tt)); - } - - // Bidirectional ranges can't be propagated properly if they don't - // also have random access. - - assert(equal(t, [1,2,3,4,5])); - - //Test that take doesn't wrap the result of take. - assert(take(t, 4) == take(dummy, 4)); - } - - immutable myRepeat = repeat(1); - static assert(is(Take!(typeof(myRepeat)))); -} - -pure @safe nothrow @nogc unittest -{ - //check for correct slicing of Take on an infinite range - import std.algorithm.comparison : equal; - foreach (start; 0 .. 4) - foreach (stop; start .. 4) - assert(iota(4).cycle.take(4)[start .. stop] - .equal(iota(start, stop))); -} - -pure @safe nothrow @nogc unittest -{ - // Check that one can declare variables of all Take types, - // and that they match the return type of the corresponding - // take(). - // See https://issues.dlang.org/show_bug.cgi?id=4464 - int[] r1; - Take!(int[]) t1; - t1 = take(r1, 1); - assert(t1.empty); - - string r2; - Take!string t2; - t2 = take(r2, 1); - assert(t2.empty); - - Take!(Take!string) t3; - t3 = take(t2, 1); - assert(t3.empty); -} - -pure @safe nothrow @nogc unittest -{ - alias R1 = typeof(repeat(1)); - alias R2 = typeof(cycle([1])); - alias TR1 = Take!R1; - alias TR2 = Take!R2; - static assert(isBidirectionalRange!TR1); - static assert(isBidirectionalRange!TR2); -} - -// https://issues.dlang.org/show_bug.cgi?id=12731 -pure @safe nothrow @nogc unittest -{ - auto a = repeat(1); - auto s = a[1 .. 5]; - s = s[1 .. 3]; - assert(s.length == 2); - assert(s[0] == 1); - assert(s[1] == 1); -} - -// https://issues.dlang.org/show_bug.cgi?id=13151 -pure @safe nothrow @nogc unittest -{ - import std.algorithm.comparison : equal; - - auto r = take(repeat(1, 4), 3); - assert(r.take(2).equal(repeat(1, 2))); -} - -// https://issues.dlang.org/show_bug.cgi?id=24481 -@safe unittest -{ - import std.algorithm.iteration : filter; - - bool called; - struct Handle - { - int entry; - void opAssign()(auto ref const(typeof(this)) that) const { called = true; } - } - - const(Handle)[5] arr = [Handle(0), Handle(1), Handle(2), Handle(3), Handle(4)]; - auto range = arr[].filter!(a => true)().take(3); - - called = false; - range.front = Handle(42); - assert(called); -} - -/** -Similar to $(LREF take), but assumes that `range` has at least $(D -n) elements. Consequently, the result of $(D takeExactly(range, n)) -always defines the `length` property (and initializes it to `n`) -even when `range` itself does not define `length`. - -The result of `takeExactly` is identical to that of $(LREF take) in -cases where the original range defines `length` or is infinite. - -Unlike $(LREF take), however, it is illegal to pass a range with less than -`n` elements to `takeExactly`; this will cause an assertion failure. - */ -auto takeExactly(R)(R range, size_t n) -if (isInputRange!R) -{ - static if (is(typeof(takeExactly(range._input, n)) == R)) - { - assert(n <= range._n, - "Attempted to take more than the length of the range with takeExactly."); - // takeExactly(takeExactly(r, n1), n2) has the same type as - // takeExactly(r, n1) and simply returns takeExactly(r, n2) - range._n = n; - return range; - } - //Also covers hasSlicing!R for finite ranges. - else static if (hasLength!R) - { - assert(n <= range.length, - "Attempted to take more than the length of the range with takeExactly."); - return take(range, n); - } - else static if (isInfinite!R) - return Take!R(range, n); - else - { - static struct Result - { - R _input; - private size_t _n; - - @property bool empty() const { return !_n; } - @property auto ref front() - { - assert(_n > 0, "front() on an empty " ~ Result.stringof); - return _input.front; - } - void popFront() { _input.popFront(); --_n; } - @property size_t length() const { return _n; } - alias opDollar = length; - - @property auto _takeExactly_Result_asTake() - { - return take(_input, _n); - } - - alias _takeExactly_Result_asTake this; - - static if (isForwardRange!R) - @property auto save() - { - return Result(_input.save, _n); - } - - static if (hasMobileElements!R) - { - auto moveFront() - { - assert(!empty, - "Attempting to move the front of an empty " - ~ typeof(this).stringof); - return _input.moveFront(); - } - } - - static if (hasAssignableElements!R) - { - @property auto ref front(ElementType!R v) - { - import core.lifetime : forward; - - assert(!empty, - "Attempting to assign to the front of an empty " - ~ typeof(this).stringof); - - // __ctfe check is workaround for https://issues.dlang.org/show_bug.cgi?id=21542 - return _input.front = __ctfe ? v : forward!v; - } - } - } - - return Result(range, n); - } -} - -/// -pure @safe nothrow unittest -{ - import std.algorithm.comparison : equal; - - auto a = [ 1, 2, 3, 4, 5 ]; - - auto b = takeExactly(a, 3); - assert(equal(b, [1, 2, 3])); - static assert(is(typeof(b.length) == size_t)); - assert(b.length == 3); - assert(b.front == 1); - assert(b.back == 3); -} - -pure @safe nothrow unittest -{ - import std.algorithm.comparison : equal; - import std.algorithm.iteration : filter; - - auto a = [ 1, 2, 3, 4, 5 ]; - auto b = takeExactly(a, 3); - assert(equal(b, [1, 2, 3])); - auto c = takeExactly(b, 2); - assert(equal(c, [1, 2])); - - - - auto d = filter!"a > 2"(a); - auto e = takeExactly(d, 3); - assert(equal(e, [3, 4, 5])); - static assert(is(typeof(e.length) == size_t)); - assert(e.length == 3); - assert(e.front == 3); - - assert(equal(takeExactly(e, 3), [3, 4, 5])); -} - -pure @safe nothrow unittest -{ - import std.algorithm.comparison : equal; - import std.internal.test.dummyrange : AllDummyRanges; - - auto a = [ 1, 2, 3, 4, 5 ]; - //Test that take and takeExactly are the same for ranges which define length - //but aren't sliceable. - struct L - { - @property auto front() { return _arr[0]; } - @property bool empty() { return _arr.empty; } - void popFront() { _arr.popFront(); } - @property size_t length() { return _arr.length; } - int[] _arr; - } - static assert(is(typeof(take(L(a), 3)) == typeof(takeExactly(L(a), 3)))); - assert(take(L(a), 3) == takeExactly(L(a), 3)); - - //Test that take and takeExactly are the same for ranges which are sliceable. - static assert(is(typeof(take(a, 3)) == typeof(takeExactly(a, 3)))); - assert(take(a, 3) == takeExactly(a, 3)); - - //Test that take and takeExactly are the same for infinite ranges. - auto inf = repeat(1); - static assert(is(typeof(take(inf, 5)) == Take!(typeof(inf)))); - assert(take(inf, 5) == takeExactly(inf, 5)); - - //Test that take and takeExactly are _not_ the same for ranges which don't - //define length. - static assert(!is(typeof(take(filter!"true"(a), 3)) == typeof(takeExactly(filter!"true"(a), 3)))); - - foreach (DummyType; AllDummyRanges) - { - { - DummyType dummy; - auto t = takeExactly(dummy, 5); - - //Test that takeExactly doesn't wrap the result of takeExactly. - assert(takeExactly(t, 4) == takeExactly(dummy, 4)); - } - - static if (hasMobileElements!DummyType) - { - { - auto t = takeExactly(DummyType.init, 4); - assert(t.moveFront() == 1); - assert(equal(t, [1, 2, 3, 4])); - } - } - - static if (hasAssignableElements!DummyType) - { - { - auto t = takeExactly(DummyType.init, 4); - t.front = 9; - assert(equal(t, [9, 2, 3, 4])); - } - } - } -} - -pure @safe nothrow unittest -{ - import std.algorithm.comparison : equal; - import std.internal.test.dummyrange : DummyRange, Length, RangeType, ReturnBy; - - alias DummyType = DummyRange!(ReturnBy.Value, Length.No, RangeType.Forward); - auto te = takeExactly(DummyType(), 5); - Take!DummyType t = te; - assert(equal(t, [1, 2, 3, 4, 5])); - assert(equal(t, te)); -} - -// https://issues.dlang.org/show_bug.cgi?id=18092 -// can't combine take and takeExactly -@safe unittest -{ - import std.algorithm.comparison : equal; - import std.internal.test.dummyrange : AllDummyRanges; - - static foreach (Range; AllDummyRanges) - {{ - Range r; - assert(r.take(6).takeExactly(2).equal([1, 2])); - assert(r.takeExactly(6).takeExactly(2).equal([1, 2])); - assert(r.takeExactly(6).take(2).equal([1, 2])); - }} -} - -// https://issues.dlang.org/show_bug.cgi?id=24481 -@safe unittest -{ - import std.algorithm.iteration : filter; - - bool called; - struct Handle - { - int entry; - void opAssign()(auto ref const(typeof(this)) that) const { called = true; } - } - - const(Handle)[5] arr = [Handle(0), Handle(1), Handle(2), Handle(3), Handle(4)]; - auto range = arr[].filter!(a => true)().takeExactly(3); - - called = false; - range.front = Handle(42); - assert(called); -} - -/** -Returns a range with at most one element; for example, $(D -takeOne([42, 43, 44])) returns a range consisting of the integer $(D -42). Calling `popFront()` off that range renders it empty. - -In effect `takeOne(r)` is somewhat equivalent to $(D take(r, 1)) but in -certain interfaces it is important to know statically that the range may only -have at most one element. - -The type returned by `takeOne` is a random-access range with length -regardless of `R`'s capabilities, as long as it is a forward range. -(another feature that distinguishes `takeOne` from `take`). If -(D R) is an input range but not a forward range, return type is an input -range with all random-access capabilities except save. - */ -auto takeOne(R)(R source) -if (isInputRange!R) -{ - static if (hasSlicing!R) - { - return source[0 .. !source.empty]; - } - else - { - static struct Result - { - private R _source; - private bool _empty = true; - @property bool empty() const { return _empty; } - @property auto ref front() - { - assert(!empty, "Attempting to fetch the front of an empty takeOne"); - return _source.front; - } - void popFront() - { - assert(!empty, "Attempting to popFront an empty takeOne"); - _source.popFront(); - _empty = true; - } - void popBack() - { - assert(!empty, "Attempting to popBack an empty takeOne"); - _source.popFront(); - _empty = true; - } - static if (isForwardRange!(Unqual!R)) - { - @property auto save() { return Result(_source.save, empty); } - } - @property auto ref back() - { - assert(!empty, "Attempting to fetch the back of an empty takeOne"); - return _source.front; - } - @property size_t length() const { return !empty; } - alias opDollar = length; - auto ref opIndex(size_t n) - { - assert(n < length, "Attempting to index a takeOne out of bounds"); - return _source.front; - } - auto opSlice(size_t m, size_t n) - { - assert( - m <= n, - "Attempting to slice a takeOne range with a larger first argument than the second." - ); - assert( - n <= length, - "Attempting to slice using an out of bounds index on a takeOne range." - ); - return n > m ? this : Result(_source, true); - } - // Non-standard property - @property R source() { return _source; } - } - - return Result(source, source.empty); - } -} - -/// -pure @safe nothrow unittest -{ - auto s = takeOne([42, 43, 44]); - static assert(isRandomAccessRange!(typeof(s))); - assert(s.length == 1); - assert(!s.empty); - assert(s.front == 42); - s.front = 43; - assert(s.front == 43); - assert(s.back == 43); - assert(s[0] == 43); - s.popFront(); - assert(s.length == 0); - assert(s.empty); -} - -pure @safe nothrow @nogc unittest -{ - struct NonForwardRange - { - enum empty = false; - int front() { return 42; } - void popFront() {} - } - - static assert(!isForwardRange!NonForwardRange); - - auto s = takeOne(NonForwardRange()); - assert(s.length == 1); - assert(!s.empty); - assert(s.front == 42); - assert(s.back == 42); - assert(s[0] == 42); - - auto t = s[0 .. 0]; - assert(t.empty); - assert(t.length == 0); - - auto u = s[1 .. 1]; - assert(u.empty); - assert(u.length == 0); - - auto v = s[0 .. 1]; - s.popFront(); - assert(s.length == 0); - assert(s.empty); - assert(!v.empty); - assert(v.front == 42); - v.popBack(); - assert(v.empty); - assert(v.length == 0); -} - -pure @safe nothrow @nogc unittest -{ - struct NonSlicingForwardRange - { - enum empty = false; - int front() { return 42; } - void popFront() {} - @property auto save() { return this; } - } - - static assert(isForwardRange!NonSlicingForwardRange); - static assert(!hasSlicing!NonSlicingForwardRange); - - auto s = takeOne(NonSlicingForwardRange()); - assert(s.length == 1); - assert(!s.empty); - assert(s.front == 42); - assert(s.back == 42); - assert(s[0] == 42); - auto t = s.save; - s.popFront(); - assert(s.length == 0); - assert(s.empty); - assert(!t.empty); - assert(t.front == 42); - t.popBack(); - assert(t.empty); - assert(t.length == 0); -} - -// Test that asserts trigger correctly -@system unittest -{ - import std.exception : assertThrown; - import core.exception : AssertError; - - struct NonForwardRange - { - enum empty = false; - int front() { return 42; } - void popFront() {} - } - - auto s = takeOne(NonForwardRange()); - - assertThrown!AssertError(s[1]); - assertThrown!AssertError(s[0 .. 2]); - - size_t one = 1; // Avoid style warnings triggered by literals - size_t zero = 0; - assertThrown!AssertError(s[one .. zero]); - - s.popFront; - assert(s.empty); - assertThrown!AssertError(s.front); - assertThrown!AssertError(s.back); - assertThrown!AssertError(s.popFront); - assertThrown!AssertError(s.popBack); -} - -// https://issues.dlang.org/show_bug.cgi?id=16999 -pure @safe unittest -{ - auto myIota = new class - { - int front = 0; - @safe void popFront(){front++;} - enum empty = false; - }; - auto iotaPart = myIota.takeOne; - int sum; - foreach (var; chain(iotaPart, iotaPart, iotaPart)) - { - sum += var; - } - assert(sum == 3); - assert(iotaPart.front == 3); -} - -/++ - Returns an empty range which is statically known to be empty and is - guaranteed to have `length` and be random access regardless of `R`'s - capabilities. - +/ -auto takeNone(R)() -if (isInputRange!R) -{ - return typeof(takeOne(R.init)).init; -} - -/// -pure @safe nothrow @nogc unittest -{ - auto range = takeNone!(int[])(); - assert(range.length == 0); - assert(range.empty); -} - -pure @safe nothrow @nogc unittest -{ - enum ctfe = takeNone!(int[])(); - static assert(ctfe.length == 0); - static assert(ctfe.empty); -} - - -/++ - Creates an empty range from the given range in $(BIGOH 1). If it can, it - will return the same range type. If not, it will return - $(D takeExactly(range, 0)). - +/ -auto takeNone(R)(R range) -if (isInputRange!R) -{ - import std.traits : isDynamicArray; - //Makes it so that calls to takeNone which don't use UFCS still work with a - //member version if it's defined. - static if (is(typeof(R.takeNone))) - auto retval = range.takeNone(); - // https://issues.dlang.org/show_bug.cgi?id=8339 - else static if (isDynamicArray!R)/+ || - (is(R == struct) && __traits(compiles, {auto r = R.init;}) && R.init.empty))+/ - { - auto retval = R.init; - } - //An infinite range sliced at [0 .. 0] would likely still not be empty... - else static if (hasSlicing!R && !isInfinite!R) - auto retval = range[0 .. 0]; - else - auto retval = takeExactly(range, 0); - - // https://issues.dlang.org/show_bug.cgi?id=7892 prevents this from being - // done in an out block. - assert(retval.empty); - return retval; -} - -/// -pure @safe nothrow unittest -{ - import std.algorithm.iteration : filter; - assert(takeNone([42, 27, 19]).empty); - assert(takeNone("dlang.org").empty); - assert(takeNone(filter!"true"([42, 27, 19])).empty); -} - -@safe unittest -{ - import std.algorithm.iteration : filter; - import std.meta : AliasSeq; - - struct Dummy - { - mixin template genInput() - { - @safe: - @property bool empty() { return _arr.empty; } - @property auto front() { return _arr.front; } - void popFront() { _arr.popFront(); } - static assert(isInputRange!(typeof(this))); - } - } - alias genInput = Dummy.genInput; - - static struct NormalStruct - { - //Disabled to make sure that the takeExactly version is used. - @disable this(); - this(int[] arr) { _arr = arr; } - mixin genInput; - int[] _arr; - } - - static struct SliceStruct - { - @disable this(); - this(int[] arr) { _arr = arr; } - mixin genInput; - @property auto save() { return this; } - auto opSlice(size_t i, size_t j) { return typeof(this)(_arr[i .. j]); } - @property size_t length() { return _arr.length; } - int[] _arr; - } - - static struct InitStruct - { - mixin genInput; - int[] _arr; - } - - static struct TakeNoneStruct - { - this(int[] arr) { _arr = arr; } - @disable this(); - mixin genInput; - auto takeNone() { return typeof(this)(null); } - int[] _arr; - } - - static class NormalClass - { - this(int[] arr) {_arr = arr;} - mixin genInput; - int[] _arr; - } - - static class SliceClass - { - @safe: - this(int[] arr) { _arr = arr; } - mixin genInput; - @property auto save() { return new typeof(this)(_arr); } - auto opSlice(size_t i, size_t j) { return new typeof(this)(_arr[i .. j]); } - @property size_t length() { return _arr.length; } - int[] _arr; - } - - static class TakeNoneClass - { - @safe: - this(int[] arr) { _arr = arr; } - mixin genInput; - auto takeNone() { return new typeof(this)(null); } - int[] _arr; - } - - import std.format : format; - - static foreach (range; AliasSeq!([1, 2, 3, 4, 5], - "hello world", - "hello world"w, - "hello world"d, - SliceStruct([1, 2, 3]), - // https://issues.dlang.org/show_bug.cgi?id=8339 - // forces this to be takeExactly `InitStruct([1, 2, 3]), - TakeNoneStruct([1, 2, 3]))) - { - static assert(takeNone(range).empty, typeof(range).stringof); - assert(takeNone(range).empty); - static assert(is(typeof(range) == typeof(takeNone(range))), typeof(range).stringof); - } - - static foreach (range; AliasSeq!(NormalStruct([1, 2, 3]), - InitStruct([1, 2, 3]))) - { - static assert(takeNone(range).empty, typeof(range).stringof); - assert(takeNone(range).empty); - static assert(is(typeof(takeExactly(range, 0)) == typeof(takeNone(range))), typeof(range).stringof); - } - - //Don't work in CTFE. - auto normal = new NormalClass([1, 2, 3]); - assert(takeNone(normal).empty); - static assert(is(typeof(takeExactly(normal, 0)) == typeof(takeNone(normal))), typeof(normal).stringof); - - auto slice = new SliceClass([1, 2, 3]); - assert(takeNone(slice).empty); - static assert(is(SliceClass == typeof(takeNone(slice))), typeof(slice).stringof); - - auto taken = new TakeNoneClass([1, 2, 3]); - assert(takeNone(taken).empty); - static assert(is(TakeNoneClass == typeof(takeNone(taken))), typeof(taken).stringof); - - auto filtered = filter!"true"([1, 2, 3, 4, 5]); - assert(takeNone(filtered).empty); - // https://issues.dlang.org/show_bug.cgi?id=8339 and - // https://issues.dlang.org/show_bug.cgi?id=5941 force this to be takeExactly - //static assert(is(typeof(filtered) == typeof(takeNone(filtered))), typeof(filtered).stringof); -} - -/++ - + Return a range advanced to within `_n` elements of the end of - + `range`. - + - + Intended as the range equivalent of the Unix - + $(HTTP en.wikipedia.org/wiki/Tail_%28Unix%29, _tail) utility. When the length - + of `range` is less than or equal to `_n`, `range` is returned - + as-is. - + - + Completes in $(BIGOH 1) steps for ranges that support slicing and have - + length. Completes in $(BIGOH range.length) time for all other ranges. - + - + Params: - + range = range to get _tail of - + n = maximum number of elements to include in _tail - + - + Returns: - + Returns the _tail of `range` augmented with length information - +/ -auto tail(Range)(Range range, size_t n) -if (isInputRange!Range && !isInfinite!Range && - (hasLength!Range || isForwardRange!Range)) -{ - static if (hasLength!Range) - { - immutable length = range.length; - if (n >= length) - return range.takeExactly(length); - else - return range.drop(length - n).takeExactly(n); - } - else - { - Range scout = range.save; - foreach (immutable i; 0 .. n) - { - if (scout.empty) - return range.takeExactly(i); - scout.popFront(); - } - - auto tail = range.save; - while (!scout.empty) - { - assert(!tail.empty); - scout.popFront(); - tail.popFront(); - } - - return tail.takeExactly(n); - } -} - -/// -pure @safe nothrow unittest -{ - // tail -c n - assert([1, 2, 3].tail(1) == [3]); - assert([1, 2, 3].tail(2) == [2, 3]); - assert([1, 2, 3].tail(3) == [1, 2, 3]); - assert([1, 2, 3].tail(4) == [1, 2, 3]); - assert([1, 2, 3].tail(0).length == 0); - - // tail --lines=n - import std.algorithm.comparison : equal; - import std.algorithm.iteration : joiner; - import std.exception : assumeWontThrow; - import std.string : lineSplitter; - assert("one\ntwo\nthree" - .lineSplitter - .tail(2) - .joiner("\n") - .equal("two\nthree") - .assumeWontThrow); -} - -// @nogc prevented by https://issues.dlang.org/show_bug.cgi?id=15408 -pure nothrow @safe /+@nogc+/ unittest -{ - import std.algorithm.comparison : equal; - import std.internal.test.dummyrange : AllDummyRanges, DummyRange, Length, - RangeType, ReturnBy; - - static immutable cheatsheet = [6, 7, 8, 9, 10]; - - foreach (R; AllDummyRanges) - { - static if (isInputRange!R && !isInfinite!R && - (hasLength!R || isForwardRange!R)) - { - assert(R.init.tail(5).equal(cheatsheet)); - static assert(R.init.tail(5).equal(cheatsheet)); - - assert(R.init.tail(0).length == 0); - assert(R.init.tail(10).equal(R.init)); - assert(R.init.tail(11).equal(R.init)); - } - } - - // Infinite ranges are not supported - static assert(!__traits(compiles, repeat(0).tail(0))); - - // Neither are non-forward ranges without length - static assert(!__traits(compiles, DummyRange!(ReturnBy.Value, Length.No, - RangeType.Input).init.tail(5))); -} - -pure @safe nothrow @nogc unittest -{ - static immutable input = [1, 2, 3]; - static immutable expectedOutput = [2, 3]; - assert(input.tail(2) == expectedOutput); -} - -/++ - Convenience function which calls - $(REF popFrontN, std, range, primitives)`(range, n)` and returns `range`. - `drop` makes it easier to pop elements from a range - and then pass it to another function within a single expression, - whereas `popFrontN` would require multiple statements. - - `dropBack` provides the same functionality but instead calls - $(REF popBackN, std, range, primitives)`(range, n)` - - Note: `drop` and `dropBack` will only pop $(I up to) - `n` elements but will stop if the range is empty first. - In other languages this is sometimes called `skip`. - - Params: - range = the $(REF_ALTTEXT input range, isInputRange, std,range,primitives) to drop from - n = the number of elements to drop - - Returns: - `range` with up to `n` elements dropped - - See_Also: - $(REF popFront, std, range, primitives), $(REF popBackN, std, range, primitives) - +/ -R drop(R)(R range, size_t n) -if (isInputRange!R) -{ - range.popFrontN(n); - return range; -} - -/// -@safe unittest -{ - import std.algorithm.comparison : equal; - - assert([0, 2, 1, 5, 0, 3].drop(3) == [5, 0, 3]); - assert("hello world".drop(6) == "world"); - assert("hello world".drop(50).empty); - assert("hello world".take(6).drop(3).equal("lo ")); -} - -/// ditto -R dropBack(R)(R range, size_t n) -if (isBidirectionalRange!R) -{ - range.popBackN(n); - return range; -} - -/// -@safe unittest -{ - import std.algorithm.comparison : equal; - - assert([0, 2, 1, 5, 0, 3].dropBack(3) == [0, 2, 1]); - assert("hello world".dropBack(6) == "hello"); - assert("hello world".dropBack(50).empty); - assert("hello world".drop(4).dropBack(4).equal("o w")); -} - -@safe unittest -{ - import std.algorithm.comparison : equal; - import std.container.dlist : DList; - - //Remove all but the first two elements - auto a = DList!int(0, 1, 9, 9, 9, 9); - a.remove(a[].drop(2)); - assert(a[].equal(a[].take(2))); -} - -@safe unittest -{ - import std.algorithm.comparison : equal; - import std.algorithm.iteration : filter; - - assert(drop("", 5).empty); - assert(equal(drop(filter!"true"([0, 2, 1, 5, 0, 3]), 3), [5, 0, 3])); -} - -@safe unittest -{ - import std.algorithm.comparison : equal; - import std.container.dlist : DList; - - //insert before the last two elements - auto a = DList!int(0, 1, 2, 5, 6); - a.insertAfter(a[].dropBack(2), [3, 4]); - assert(a[].equal(iota(0, 7))); -} - -/++ - Similar to $(LREF drop) and `dropBack` but they call - $(D range.$(LREF popFrontExactly)(n)) and `range.popBackExactly(n)` - instead. - - Note: Unlike `drop`, `dropExactly` will assume that the - range holds at least `n` elements. This makes `dropExactly` - faster than `drop`, but it also means that if `range` does - not contain at least `n` elements, it will attempt to call `popFront` - on an empty range, which is undefined behavior. So, only use - `popFrontExactly` when it is guaranteed that `range` holds at least - `n` elements. - - Params: - range = the $(REF_ALTTEXT input range, isInputRange, std,range,primitives) to drop from - n = the number of elements to drop - - Returns: - `range` with `n` elements dropped - - See_Also: - $(REF popFrontExcatly, std, range, primitives), - $(REF popBackExcatly, std, range, primitives) -+/ -R dropExactly(R)(R range, size_t n) -if (isInputRange!R) -{ - popFrontExactly(range, n); - return range; -} -/// ditto -R dropBackExactly(R)(R range, size_t n) -if (isBidirectionalRange!R) -{ - popBackExactly(range, n); - return range; -} - -/// -@safe unittest -{ - import std.algorithm.comparison : equal; - import std.algorithm.iteration : filterBidirectional; - - auto a = [1, 2, 3]; - assert(a.dropExactly(2) == [3]); - assert(a.dropBackExactly(2) == [1]); - - string s = "日本語"; - assert(s.dropExactly(2) == "語"); - assert(s.dropBackExactly(2) == "日"); - - auto bd = filterBidirectional!"true"([1, 2, 3]); - assert(bd.dropExactly(2).equal([3])); - assert(bd.dropBackExactly(2).equal([1])); -} - -/++ - Convenience function which calls - `range.popFront()` and returns `range`. `dropOne` - makes it easier to pop an element from a range - and then pass it to another function within a single expression, - whereas `popFront` would require multiple statements. - - `dropBackOne` provides the same functionality but instead calls - `range.popBack()`. -+/ -R dropOne(R)(R range) -if (isInputRange!R) -{ - range.popFront(); - return range; -} -/// ditto -R dropBackOne(R)(R range) -if (isBidirectionalRange!R) -{ - range.popBack(); - return range; -} - -/// -pure @safe nothrow unittest -{ - import std.algorithm.comparison : equal; - import std.algorithm.iteration : filterBidirectional; - import std.container.dlist : DList; - - auto dl = DList!int(9, 1, 2, 3, 9); - assert(dl[].dropOne().dropBackOne().equal([1, 2, 3])); - - auto a = [1, 2, 3]; - assert(a.dropOne() == [2, 3]); - assert(a.dropBackOne() == [1, 2]); - - string s = "日本語"; - import std.exception : assumeWontThrow; - assert(assumeWontThrow(s.dropOne() == "本語")); - assert(assumeWontThrow(s.dropBackOne() == "日本")); - - auto bd = filterBidirectional!"true"([1, 2, 3]); - assert(bd.dropOne().equal([2, 3])); - assert(bd.dropBackOne().equal([1, 2])); -} - -/** -Create a range which repeats one value. - -Params: - value = the _value to repeat - n = the number of times to repeat `value` - -Returns: - If `n` is not defined, an infinite random access range - with slicing. - - If `n` is defined, a random access range with slicing. -*/ -struct Repeat(T) -{ -private: - import std.typecons : Rebindable2; - - // Store a rebindable T to make Repeat assignable. - Rebindable2!T _value; - -public: - /// Range primitives - @property inout(T) front() inout { return _value.get; } - - /// ditto - @property inout(T) back() inout { return _value.get; } - - /// ditto - enum bool empty = false; - - /// ditto - void popFront() {} - - /// ditto - void popBack() {} - - /// ditto - @property auto save() inout { return this; } - - /// ditto - inout(T) opIndex(size_t) inout { return _value.get; } - - /// ditto - auto opSlice(size_t i, size_t j) - in - { - assert( - i <= j, - "Attempting to slice a Repeat with a larger first argument than the second." - ); - } - do - { - return this.takeExactly(j - i); - } - private static struct DollarToken {} - - /// ditto - enum opDollar = DollarToken.init; - - /// ditto - auto opSlice(size_t, DollarToken) inout { return this; } -} - -/// Ditto -Repeat!T repeat(T)(T value) -{ - import std.typecons : Rebindable2; - - return Repeat!T(Rebindable2!T(value)); -} - -/// -pure @safe nothrow unittest -{ - import std.algorithm.comparison : equal; - - assert(5.repeat().take(4).equal([5, 5, 5, 5])); -} - -pure @safe nothrow unittest -{ - import std.algorithm.comparison : equal; - - auto r = repeat(5); - alias R = typeof(r); - static assert(isBidirectionalRange!R); - static assert(isForwardRange!R); - static assert(isInfinite!R); - static assert(hasSlicing!R); - - assert(r.back == 5); - assert(r.front == 5); - assert(r.take(4).equal([ 5, 5, 5, 5 ])); - assert(r[0 .. 4].equal([ 5, 5, 5, 5 ])); - - R r2 = r[5 .. $]; - assert(r2.back == 5); - assert(r2.front == 5); -} - -/// ditto -Take!(Repeat!T) repeat(T)(T value, size_t n) -{ - return take(repeat(value), n); -} - -/// -pure @safe nothrow unittest -{ - import std.algorithm.comparison : equal; - - assert(5.repeat(4).equal([5, 5, 5, 5])); -} - -// https://issues.dlang.org/show_bug.cgi?id=12007 -pure @safe nothrow unittest -{ - static class C{} - Repeat!(immutable int) ri; - ri = ri.save; - Repeat!(immutable C) rc; - rc = rc.save; - - import std.algorithm.setops : cartesianProduct; - import std.algorithm.comparison : equal; - import std.typecons : tuple; - immutable int[] A = [1,2,3]; - immutable int[] B = [4,5,6]; - - assert(equal(cartesianProduct(A,B), - [ - tuple(1, 4), tuple(1, 5), tuple(1, 6), - tuple(2, 4), tuple(2, 5), tuple(2, 6), - tuple(3, 4), tuple(3, 5), tuple(3, 6), - ])); -} - -/** -Given callable ($(REF isCallable, std,traits)) `fun`, create as a range -whose front is defined by successive calls to `fun()`. -This is especially useful to call function with global side effects (random -functions), or to create ranges expressed as a single delegate, rather than -an entire `front`/`popFront`/`empty` structure. -`fun` maybe be passed either a template alias parameter (existing -function, delegate, struct type defining `static opCall`) or -a run-time value argument (delegate, function object). -The result range models an InputRange -($(REF isInputRange, std,range,primitives)). -The resulting range will call `fun()` on construction, and every call to -`popFront`, and the cached value will be returned when `front` is called. - -Returns: an `inputRange` where each element represents another call to fun. -*/ -auto generate(Fun)(Fun fun) -if (isCallable!fun) -{ - auto gen = Generator!(Fun)(fun); - gen.popFront(); // prime the first element - return gen; -} - -/// ditto -auto generate(alias fun)() -if (isCallable!fun) -{ - auto gen = Generator!(fun)(); - gen.popFront(); // prime the first element - return gen; -} - -/// -@safe pure nothrow unittest -{ - import std.algorithm.comparison : equal; - import std.algorithm.iteration : map; - - int i = 1; - auto powersOfTwo = generate!(() => i *= 2)().take(10); - assert(equal(powersOfTwo, iota(1, 11).map!"2^^a"())); -} - -/// -@safe pure nothrow unittest -{ - import std.algorithm.comparison : equal; - - //Returns a run-time delegate - auto infiniteIota(T)(T low, T high) - { - T i = high; - return (){if (i == high) i = low; return i++;}; - } - //adapted as a range. - assert(equal(generate(infiniteIota(1, 4)).take(10), [1, 2, 3, 1, 2, 3, 1, 2, 3, 1])); -} - -/// -@safe unittest -{ - import std.format : format; - import std.random : uniform; - - auto r = generate!(() => uniform(0, 6)).take(10); - format("%(%s %)", r); -} - -private struct Generator(Fun...) -{ - static assert(Fun.length == 1); - static assert(isInputRange!Generator); - import std.traits : FunctionAttribute, functionAttributes, ReturnType; - -private: - static if (is(Fun[0])) - Fun[0] fun; - else - alias fun = Fun[0]; - - enum returnByRef_ = (functionAttributes!fun & FunctionAttribute.ref_) ? true : false; - - import std.traits : hasIndirections; - static if (!hasIndirections!(ReturnType!fun)) - alias RetType = Unqual!(ReturnType!fun); - else - alias RetType = ReturnType!fun; - - static if (returnByRef_) - RetType *elem_; - else - RetType elem_; -public: - /// Range primitives - enum empty = false; - - static if (returnByRef_) - { - /// ditto - ref front() @property - { - return *elem_; - } - /// ditto - void popFront() - { - elem_ = &fun(); - } - } - else - { - /// ditto - auto front() @property - { - return elem_; - } - /// ditto - void popFront() - { - elem_ = fun(); - } - } -} - -@safe nothrow unittest -{ - import std.algorithm.comparison : equal; - - struct StaticOpCall - { - static ubyte opCall() { return 5 ; } - } - - assert(equal(generate!StaticOpCall().take(10), repeat(5).take(10))); -} - -@safe pure unittest -{ - import std.algorithm.comparison : equal; - - struct OpCall - { - ubyte opCall() @safe pure { return 5 ; } - } - - OpCall op; - assert(equal(generate(op).take(10), repeat(5).take(10))); -} - -// verify ref mechanism works -@system nothrow unittest -{ - int[10] arr; - int idx; - - ref int fun() { - auto x = idx++; - idx %= arr.length; - return arr[x]; - } - int y = 1; - foreach (ref x; generate!(fun).take(20)) - { - x += y++; - } - import std.algorithm.comparison : equal; - assert(equal(arr[], iota(12, 32, 2))); -} - -// assure front isn't the mechanism to make generate go to the next element. -@safe unittest -{ - int i; - auto g = generate!(() => ++i); - auto f = g.front; - assert(f == g.front); - g = g.drop(5); // reassign because generate caches - assert(g.front == f + 5); -} - -// https://issues.dlang.org/show_bug.cgi?id=23319 -@safe pure nothrow unittest -{ - auto b = generate!(() => const(int)(42)); - assert(b.front == 42); -} - -/** -Repeats the given forward range ad infinitum. If the original range is -infinite (fact that would make `Cycle` the identity application), -`Cycle` detects that and aliases itself to the range type -itself. That works for non-forward ranges too. -If the original range has random access, `Cycle` offers -random access and also offers a constructor taking an initial position -`index`. `Cycle` works with static arrays in addition to ranges, -mostly for performance reasons. - -Note: The input range must not be empty. - -Tip: This is a great way to implement simple circular buffers. -*/ -struct Cycle(R) -if (isForwardRange!R && !isInfinite!R) -{ - static if (isRandomAccessRange!R && hasLength!R) - { - private R _original; - private size_t _index; - - /// Range primitives - this(R input, size_t index = 0) - { - _original = input; - _index = index % _original.length; - } - - /// ditto - @property auto ref front() - { - return _original[_index]; - } - - static if (is(typeof((cast(const R)_original)[_index]))) - { - /// ditto - @property auto ref front() const - { - return _original[_index]; - } - } - - static if (hasAssignableElements!R) - { - /// ditto - @property void front(ElementType!R val) - { - import core.lifetime : forward; - - // __ctfe check is workaround for https://issues.dlang.org/show_bug.cgi?id=21542 - _original[_index] = __ctfe ? val : forward!val; - } - } - - /// ditto - enum bool empty = false; - - /// ditto - void popFront() - { - ++_index; - if (_index >= _original.length) - _index = 0; - } - - /// ditto - auto ref opIndex(size_t n) - { - return _original[(n + _index) % _original.length]; - } - - static if (is(typeof((cast(const R)_original)[_index])) && - is(typeof((cast(const R)_original).length))) - { - /// ditto - auto ref opIndex(size_t n) const - { - return _original[(n + _index) % _original.length]; - } - } - - static if (hasAssignableElements!R) - { - /// ditto - void opIndexAssign(ElementType!R val, size_t n) - { - _original[(n + _index) % _original.length] = val; - } - } - - /// ditto - @property Cycle save() - { - //No need to call _original.save, because Cycle never actually modifies _original - return Cycle(_original, _index); - } - - private static struct DollarToken {} - - /// ditto - enum opDollar = DollarToken.init; - - static if (hasSlicing!R) - { - /// ditto - auto opSlice(size_t i, size_t j) - in - { - assert(i <= j); - } - do - { - return this[i .. $].takeExactly(j - i); - } - - /// ditto - auto opSlice(size_t i, DollarToken) - { - return typeof(this)(_original, _index + i); - } - } - } - else - { - private R _original; - private R _current; - - /// ditto - this(R input) - { - _original = input; - _current = input.save; - } - - private this(R original, R current) - { - _original = original; - _current = current; - } - - /// ditto - @property auto ref front() - { - return _current.front; - } - - static if (is(typeof((cast(const R)_current).front))) - { - /// ditto - @property auto ref front() const - { - return _current.front; - } - } - - static if (hasAssignableElements!R) - { - /// ditto - @property auto front(ElementType!R val) - { - import core.lifetime : forward; - - // __ctfe check is workaround for https://issues.dlang.org/show_bug.cgi?id=21542 - return _current.front = __ctfe ? val : forward!val; - } - } - - /// ditto - enum bool empty = false; - - /// ditto - void popFront() - { - _current.popFront(); - if (_current.empty) - _current = _original.save; - } - - /// ditto - @property Cycle save() - { - //No need to call _original.save, because Cycle never actually modifies _original - return Cycle(_original, _current.save); - } - } -} - -/// ditto -template Cycle(R) -if (isInfinite!R) -{ - alias Cycle = R; -} - -/// ditto -struct Cycle(R) -if (isStaticArray!R) -{ - private alias ElementType = typeof(R.init[0]); - private ElementType* _ptr; - private size_t _index; - -nothrow: - - /// Range primitives - this(ref R input, size_t index = 0) @system - { - _ptr = input.ptr; - _index = index % R.length; - } - - /// ditto - @property ref inout(ElementType) front() inout @safe - { - static ref auto trustedPtrIdx(typeof(_ptr) p, size_t idx) @trusted - { - return p[idx]; - } - return trustedPtrIdx(_ptr, _index); - } - - /// ditto - enum bool empty = false; - - /// ditto - void popFront() @safe - { - ++_index; - if (_index >= R.length) - _index = 0; - } - - /// ditto - ref inout(ElementType) opIndex(size_t n) inout @safe - { - static ref auto trustedPtrIdx(typeof(_ptr) p, size_t idx) @trusted - { - return p[idx % R.length]; - } - return trustedPtrIdx(_ptr, n + _index); - } - - /// ditto - @property inout(Cycle) save() inout @safe - { - return this; - } - - private static struct DollarToken {} - /// ditto - enum opDollar = DollarToken.init; - - /// ditto - auto opSlice(size_t i, size_t j) @safe - in - { - assert( - i <= j, - "Attempting to slice a Repeat with a larger first argument than the second." - ); - } - do - { - return this[i .. $].takeExactly(j - i); - } - - /// ditto - inout(typeof(this)) opSlice(size_t i, DollarToken) inout @safe - { - static auto trustedCtor(typeof(_ptr) p, size_t idx) @trusted - { - return cast(inout) Cycle(*cast(R*)(p), idx); - } - return trustedCtor(_ptr, _index + i); - } -} - -/// Ditto -auto cycle(R)(R input) -if (isInputRange!R) -{ - static assert(isForwardRange!R || isInfinite!R, - "Cycle requires a forward range argument unless it's statically known" - ~ " to be infinite"); - assert(!input.empty, "Attempting to pass an empty input to cycle"); - static if (isInfinite!R) return input; - else return Cycle!R(input); -} - -/// -@safe unittest -{ - import std.algorithm.comparison : equal; - import std.range : cycle, take; - - // Here we create an infinitive cyclic sequence from [1, 2] - // (i.e. get here [1, 2, 1, 2, 1, 2 and so on]) then - // take 5 elements of this sequence (so we have [1, 2, 1, 2, 1]) - // and compare them with the expected values for equality. - assert(cycle([1, 2]).take(5).equal([ 1, 2, 1, 2, 1 ])); -} - -/// Ditto -Cycle!R cycle(R)(R input, size_t index = 0) -if (isRandomAccessRange!R && !isInfinite!R) -{ - assert(!input.empty, "Attempting to pass an empty input to cycle"); - return Cycle!R(input, index); -} - -/// Ditto -Cycle!R cycle(R)(ref R input, size_t index = 0) @system -if (isStaticArray!R) -{ - return Cycle!R(input, index); -} - -@safe nothrow unittest -{ - import std.algorithm.comparison : equal; - import std.internal.test.dummyrange : AllDummyRanges; - - static assert(isForwardRange!(Cycle!(uint[]))); - - // Make sure ref is getting propagated properly. - int[] nums = [1,2,3]; - auto c2 = cycle(nums); - c2[3]++; - assert(nums[0] == 2); - - immutable int[] immarr = [1, 2, 3]; - - foreach (DummyType; AllDummyRanges) - { - static if (isForwardRange!DummyType) - { - DummyType dummy; - auto cy = cycle(dummy); - static assert(isForwardRange!(typeof(cy))); - auto t = take(cy, 20); - assert(equal(t, [1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10])); - - const cRange = cy; - assert(cRange.front == 1); - - static if (hasAssignableElements!DummyType) - { - { - cy.front = 66; - scope(exit) cy.front = 1; - assert(dummy.front == 66); - } - - static if (isRandomAccessRange!DummyType) - { - { - cy[10] = 66; - scope(exit) cy[10] = 1; - assert(dummy.front == 66); - } - - assert(cRange[10] == 1); - } - } - - static if (hasSlicing!DummyType) - { - auto slice = cy[5 .. 15]; - assert(equal(slice, [6, 7, 8, 9, 10, 1, 2, 3, 4, 5])); - static assert(is(typeof(slice) == typeof(takeExactly(cy, 5)))); - - auto infSlice = cy[7 .. $]; - assert(equal(take(infSlice, 5), [8, 9, 10, 1, 2])); - static assert(isInfinite!(typeof(infSlice))); - } - } - } -} - -@system nothrow unittest // For static arrays. -{ - import std.algorithm.comparison : equal; - - int[3] a = [ 1, 2, 3 ]; - static assert(isStaticArray!(typeof(a))); - auto c = cycle(a); - assert(a.ptr == c._ptr); - assert(equal(take(cycle(a), 5), [ 1, 2, 3, 1, 2 ][])); - static assert(isForwardRange!(typeof(c))); - - // Test qualifiers on slicing. - alias C = typeof(c); - static assert(is(typeof(c[1 .. $]) == C)); - const cConst = c; - static assert(is(typeof(cConst[1 .. $]) == const(C))); -} - -@safe nothrow unittest // For infinite ranges -{ - struct InfRange - { - void popFront() { } - @property int front() { return 0; } - enum empty = false; - auto save() { return this; } - } - struct NonForwardInfRange - { - void popFront() { } - @property int front() { return 0; } - enum empty = false; - } - - InfRange i; - NonForwardInfRange j; - auto c = cycle(i); - assert(c == i); - //make sure it can alias out even non-forward infinite ranges - static assert(is(typeof(j.cycle) == typeof(j))); -} - -@safe unittest -{ - import std.algorithm.comparison : equal; - - int[5] arr = [0, 1, 2, 3, 4]; - auto cleD = cycle(arr[]); //Dynamic - assert(equal(cleD[5 .. 10], arr[])); - - //n is a multiple of 5 worth about 3/4 of size_t.max - auto n = size_t.max/4 + size_t.max/2; - n -= n % 5; - - //Test index overflow - foreach (_ ; 0 .. 10) - { - cleD = cleD[n .. $]; - assert(equal(cleD[5 .. 10], arr[])); - } -} - -@system @nogc nothrow unittest -{ - import std.algorithm.comparison : equal; - - int[5] arr = [0, 1, 2, 3, 4]; - auto cleS = cycle(arr); //Static - assert(equal(cleS[5 .. 10], arr[])); - - //n is a multiple of 5 worth about 3/4 of size_t.max - auto n = size_t.max/4 + size_t.max/2; - n -= n % 5; - - //Test index overflow - foreach (_ ; 0 .. 10) - { - cleS = cleS[n .. $]; - assert(equal(cleS[5 .. 10], arr[])); - } -} - -@system unittest -{ - import std.algorithm.comparison : equal; - - int[1] arr = [0]; - auto cleS = cycle(arr); - cleS = cleS[10 .. $]; - assert(equal(cleS[5 .. 10], 0.repeat(5))); - assert(cleS.front == 0); -} - -// https://issues.dlang.org/show_bug.cgi?id=10845 -@system unittest -{ - import std.algorithm.comparison : equal; - import std.algorithm.iteration : filter; - - auto a = inputRangeObject(iota(3).filter!"true"); - assert(equal(cycle(a).take(10), [0, 1, 2, 0, 1, 2, 0, 1, 2, 0])); -} - -// https://issues.dlang.org/show_bug.cgi?id=12177 -@safe unittest -{ - static assert(__traits(compiles, recurrence!q{a[n - 1] ~ a[n - 2]}("1", "0"))); -} - -// https://issues.dlang.org/show_bug.cgi?id=13390 -@system unittest -{ - import core.exception : AssertError; - import std.exception : assertThrown; - assertThrown!AssertError(cycle([0, 1, 2][0 .. 0])); -} - -// https://issues.dlang.org/show_bug.cgi?id=18657 -pure @safe unittest -{ - import std.algorithm.comparison : equal; - string s = "foo"; - auto r = refRange(&s).cycle.take(4); - assert(equal(r.save, "foof")); - assert(equal(r.save, "foof")); -} - -// https://issues.dlang.org/show_bug.cgi?id=24481 -@safe unittest -{ - import std.algorithm.iteration : filter; - - bool called; - struct Handle - { - int entry; - void opAssign()(auto ref const(typeof(this)) that) const { called = true; } - } - - const(Handle)[3] arr = [Handle(0), Handle(1), Handle(2)]; - { - auto range = arr[].cycle().take(5); - - called = false; - range.front = Handle(42); - assert(called); - } - { - auto range = arr[].filter!(a => true)().cycle().take(5); - - called = false; - range.front = Handle(42); - assert(called); - } -} - -private alias lengthType(R) = typeof(R.init.length.init); - -/** - Iterate several ranges in lockstep. The element type is a proxy tuple - that allows accessing the current element in the `n`th range by - using `e[n]`. - - `zip` is similar to $(LREF lockstep), but `lockstep` doesn't - bundle its elements and uses the `opApply` protocol. - `lockstep` allows reference access to the elements in - `foreach` iterations. - - Params: - sp = controls what `zip` will do if the ranges are different lengths - ranges = the ranges to zip together - Returns: - At minimum, an input range. `Zip` offers the lowest range facilities - of all components, e.g. it offers random access iff all ranges offer - random access, and also offers mutation and swapping if all ranges offer - it. Due to this, `Zip` is extremely powerful because it allows manipulating - several ranges in lockstep. - Throws: - An `Exception` if all of the ranges are not the same length and - `sp` is set to `StoppingPolicy.requireSameLength`. - - Limitations: The `@nogc` and `nothrow` attributes cannot be inferred for - the `Zip` struct because $(LREF StoppingPolicy) can vary at runtime. This - limitation is not shared by the anonymous range returned by the `zip` - function when not given an explicit `StoppingPolicy` as an argument. -*/ -struct Zip(Ranges...) -if (Ranges.length && allSatisfy!(isInputRange, Ranges)) -{ - import std.format : format; //for generic mixins - import std.typecons : Tuple; - - alias R = Ranges; - private R ranges; - alias ElementType = Tuple!(staticMap!(.ElementType, R)); - private StoppingPolicy stoppingPolicy = StoppingPolicy.shortest; - -/** - Builds an object. Usually this is invoked indirectly by using the - $(LREF zip) function. - */ - this(R rs, StoppingPolicy s = StoppingPolicy.shortest) - { - ranges[] = rs[]; - stoppingPolicy = s; - } - -/** - Returns `true` if the range is at end. The test depends on the - stopping policy. -*/ - static if (allSatisfy!(isInfinite, R)) - { - // BUG: Doesn't propagate infiniteness if only some ranges are infinite - // and s == StoppingPolicy.longest. This isn't fixable in the - // current design since StoppingPolicy is known only at runtime. - enum bool empty = false; - } - else - { - /// - @property bool empty() - { - import std.exception : enforce; - import std.meta : anySatisfy; - - final switch (stoppingPolicy) - { - case StoppingPolicy.shortest: - foreach (i, Unused; R) - { - if (ranges[i].empty) return true; - } - return false; - case StoppingPolicy.longest: - static if (anySatisfy!(isInfinite, R)) - { - return false; - } - else - { - foreach (i, Unused; R) - { - if (!ranges[i].empty) return false; - } - return true; - } - case StoppingPolicy.requireSameLength: - foreach (i, Unused; R[1 .. $]) - { - enforce(ranges[0].empty == - ranges[i + 1].empty, - "Inequal-length ranges passed to Zip"); - } - return ranges[0].empty; - } - assert(false); - } - } - - static if (allSatisfy!(isForwardRange, R)) - { - /// - @property Zip save() - { - //Zip(ranges[0].save, ranges[1].save, ..., stoppingPolicy) - return mixin (q{Zip(%(ranges[%s].save%|, %), stoppingPolicy)}.format(iota(0, R.length))); - } - } - - private .ElementType!(R[i]) tryGetInit(size_t i)() - { - alias E = .ElementType!(R[i]); - static if (!is(typeof({static E i;}))) - throw new Exception("Range with non-default constructable elements exhausted."); - else - return E.init; - } - -/** - Returns the current iterated element. -*/ - @property ElementType front() - { - @property tryGetFront(size_t i)(){return ranges[i].empty ? tryGetInit!i() : ranges[i].front;} - //ElementType(tryGetFront!0, tryGetFront!1, ...) - return mixin(q{ElementType(%(tryGetFront!%s, %))}.format(iota(0, R.length))); - } - -/** - Sets the front of all iterated ranges. -*/ - static if (allSatisfy!(hasAssignableElements, R)) - { - @property void front(ElementType v) - { - foreach (i, Unused; R) - { - if (!ranges[i].empty) - { - ranges[i].front = v[i]; - } - } - } - } - -/** - Moves out the front. -*/ - static if (allSatisfy!(hasMobileElements, R)) - { - ElementType moveFront() - { - @property tryMoveFront(size_t i)(){return ranges[i].empty ? tryGetInit!i() : ranges[i].moveFront();} - //ElementType(tryMoveFront!0, tryMoveFront!1, ...) - return mixin(q{ElementType(%(tryMoveFront!%s, %))}.format(iota(0, R.length))); - } - } - -/** - Returns the rightmost element. -*/ - static if (allSatisfy!(isBidirectionalRange, R)) - { - @property ElementType back() - { - //TODO: Fixme! BackElement != back of all ranges in case of jagged-ness - - @property tryGetBack(size_t i)(){return ranges[i].empty ? tryGetInit!i() : ranges[i].back;} - //ElementType(tryGetBack!0, tryGetBack!1, ...) - return mixin(q{ElementType(%(tryGetBack!%s, %))}.format(iota(0, R.length))); - } - -/** - Moves out the back. -*/ - static if (allSatisfy!(hasMobileElements, R)) - { - ElementType moveBack() - { - //TODO: Fixme! BackElement != back of all ranges in case of jagged-ness - - @property tryMoveBack(size_t i)(){return ranges[i].empty ? tryGetInit!i() : ranges[i].moveBack();} - //ElementType(tryMoveBack!0, tryMoveBack!1, ...) - return mixin(q{ElementType(%(tryMoveBack!%s, %))}.format(iota(0, R.length))); - } - } - -/** - Returns the current iterated element. -*/ - static if (allSatisfy!(hasAssignableElements, R)) - { - @property void back(ElementType v) - { - //TODO: Fixme! BackElement != back of all ranges in case of jagged-ness. - //Not sure the call is even legal for StoppingPolicy.longest - - foreach (i, Unused; R) - { - if (!ranges[i].empty) - { - ranges[i].back = v[i]; - } - } - } - } - } - -/** - Advances to the next element in all controlled ranges. -*/ - void popFront() - { - import std.exception : enforce; - - final switch (stoppingPolicy) - { - case StoppingPolicy.shortest: - foreach (i, Unused; R) - { - assert(!ranges[i].empty); - ranges[i].popFront(); - } - break; - case StoppingPolicy.longest: - foreach (i, Unused; R) - { - if (!ranges[i].empty) ranges[i].popFront(); - } - break; - case StoppingPolicy.requireSameLength: - foreach (i, Unused; R) - { - enforce(!ranges[i].empty, "Invalid Zip object"); - ranges[i].popFront(); - } - break; - } - } - -/** - Calls `popBack` for all controlled ranges. -*/ - static if (allSatisfy!(isBidirectionalRange, R)) - { - void popBack() - { - //TODO: Fixme! In case of jaggedness, this is wrong. - import std.exception : enforce; - - final switch (stoppingPolicy) - { - case StoppingPolicy.shortest: - foreach (i, Unused; R) - { - assert(!ranges[i].empty); - ranges[i].popBack(); - } - break; - case StoppingPolicy.longest: - foreach (i, Unused; R) - { - if (!ranges[i].empty) ranges[i].popBack(); - } - break; - case StoppingPolicy.requireSameLength: - foreach (i, Unused; R) - { - enforce(!ranges[i].empty, "Invalid Zip object"); - ranges[i].popBack(); - } - break; - } - } - } - -/** - Returns the length of this range. Defined only if all ranges define - `length`. -*/ - static if (allSatisfy!(hasLength, R)) - { - @property auto length() - { - static if (Ranges.length == 1) - return ranges[0].length; - else - { - if (stoppingPolicy == StoppingPolicy.requireSameLength) - return ranges[0].length; - - //[min|max](ranges[0].length, ranges[1].length, ...) - import std.algorithm.comparison : min, max; - if (stoppingPolicy == StoppingPolicy.shortest) - return mixin(q{min(%(ranges[%s].length%|, %))}.format(iota(0, R.length))); - else - return mixin(q{max(%(ranges[%s].length%|, %))}.format(iota(0, R.length))); - } - } - - alias opDollar = length; - } - -/** - Returns a slice of the range. Defined only if all range define - slicing. -*/ - static if (allSatisfy!(hasSlicing, R)) - { - auto opSlice(size_t from, size_t to) - { - //Slicing an infinite range yields the type Take!R - //For finite ranges, the type Take!R aliases to R - alias ZipResult = Zip!(staticMap!(Take, R)); - - //ZipResult(ranges[0][from .. to], ranges[1][from .. to], ..., stoppingPolicy) - return mixin (q{ZipResult(%(ranges[%s][from .. to]%|, %), stoppingPolicy)}.format(iota(0, R.length))); - } - } - -/** - Returns the `n`th element in the composite range. Defined if all - ranges offer random access. -*/ - static if (allSatisfy!(isRandomAccessRange, R)) - { - ElementType opIndex(size_t n) - { - //TODO: Fixme! This may create an out of bounds access - //for StoppingPolicy.longest - - //ElementType(ranges[0][n], ranges[1][n], ...) - return mixin (q{ElementType(%(ranges[%s][n]%|, %))}.format(iota(0, R.length))); - } - -/** - Assigns to the `n`th element in the composite range. Defined if - all ranges offer random access. -*/ - static if (allSatisfy!(hasAssignableElements, R)) - { - void opIndexAssign(ElementType v, size_t n) - { - //TODO: Fixme! Not sure the call is even legal for StoppingPolicy.longest - foreach (i, Range; R) - { - ranges[i][n] = v[i]; - } - } - } - -/** - Destructively reads the `n`th element in the composite - range. Defined if all ranges offer random access. -*/ - static if (allSatisfy!(hasMobileElements, R)) - { - ElementType moveAt(size_t n) - { - //TODO: Fixme! This may create an out of bounds access - //for StoppingPolicy.longest - - //ElementType(ranges[0].moveAt(n), ranges[1].moveAt(n), ..., ) - return mixin (q{ElementType(%(ranges[%s].moveAt(n)%|, %))}.format(iota(0, R.length))); - } - } - } -} - -/// Ditto -auto zip(Ranges...)(Ranges ranges) -if (Ranges.length && allSatisfy!(isInputRange, Ranges)) -{ - import std.meta : anySatisfy, templateOr; - static if (allSatisfy!(isInfinite, Ranges) || Ranges.length == 1) - { - return ZipShortest!(Ranges)(ranges); - } - else static if (allSatisfy!(isBidirectionalRange, Ranges)) - { - static if (allSatisfy!(templateOr!(isInfinite, hasLength), Ranges) - && allSatisfy!(templateOr!(isInfinite, hasSlicing), Ranges) - && allSatisfy!(isBidirectionalRange, staticMap!(Take, Ranges))) - { - // If all the ranges are bidirectional, if possible slice them to - // the same length to simplify the implementation. - static assert(anySatisfy!(hasLength, Ranges)); - static foreach (i, Range; Ranges) - static if (hasLength!Range) - { - static if (!is(typeof(minLen) == size_t)) - size_t minLen = ranges[i].length; - else - {{ - const x = ranges[i].length; - if (x < minLen) minLen = x; - }} - } - import std.format : format; - static if (!anySatisfy!(isInfinite, Ranges)) - return mixin(`ZipShortest!(Yes.allKnownSameLength, staticMap!(Take, Ranges))`~ - `(%(ranges[%s][0 .. minLen]%|, %))`.format(iota(0, Ranges.length))); - else - return mixin(`ZipShortest!(Yes.allKnownSameLength, staticMap!(Take, Ranges))`~ - `(%(take(ranges[%s], minLen)%|, %))`.format(iota(0, Ranges.length))); - } - else static if (allSatisfy!(isRandomAccessRange, Ranges)) - { - // We can't slice but we can still use random access to ensure - // "back" is retrieving the same index for each range. - return ZipShortest!(Ranges)(ranges); - } - else - { - // If bidirectional range operations would not be supported by - // ZipShortest that might have actually been a bug since Zip - // supported `back` without verifying that each range had the - // same length, but for the sake of backwards compatibility - // use the old Zip to continue supporting them. - return Zip!Ranges(ranges); - } - } - else - { - return ZipShortest!(Ranges)(ranges); - } -} - -/// -@nogc nothrow pure @safe unittest -{ - import std.algorithm.comparison : equal; - import std.algorithm.iteration : map; - - // pairwise sum - auto arr = only(0, 1, 2); - auto part1 = zip(arr, arr.dropOne).map!"a[0] + a[1]"; - assert(part1.equal(only(1, 3))); -} - -/// -nothrow pure @safe unittest -{ - import std.conv : to; - - int[] a = [ 1, 2, 3 ]; - string[] b = [ "a", "b", "c" ]; - string[] result; - - foreach (tup; zip(a, b)) - { - result ~= tup[0].to!string ~ tup[1]; - } - - assert(result == [ "1a", "2b", "3c" ]); - - size_t idx = 0; - // unpacking tuple elements with foreach - foreach (e1, e2; zip(a, b)) - { - assert(e1 == a[idx]); - assert(e2 == b[idx]); - ++idx; - } -} - -/// `zip` is powerful - the following code sorts two arrays in parallel: -nothrow pure @safe unittest -{ - import std.algorithm.sorting : sort; - - int[] a = [ 1, 2, 3 ]; - string[] b = [ "a", "c", "b" ]; - zip(a, b).sort!((t1, t2) => t1[0] > t2[0]); - - assert(a == [ 3, 2, 1 ]); - // b is sorted according to a's sorting - assert(b == [ "b", "c", "a" ]); -} - -/// Ditto -auto zip(Ranges...)(StoppingPolicy sp, Ranges ranges) -if (Ranges.length && allSatisfy!(isInputRange, Ranges)) -{ - return Zip!Ranges(ranges, sp); -} - -/** - Dictates how iteration in a $(LREF zip) and $(LREF lockstep) should stop. - By default stop at the end of the shortest of all ranges. -*/ -enum StoppingPolicy -{ - /// Stop when the shortest range is exhausted - shortest, - /// Stop when the longest range is exhausted - longest, - /// Require that all ranges are equal - requireSameLength, -} - -/// -pure @safe unittest -{ - import std.algorithm.comparison : equal; - import std.exception : assertThrown; - import std.range.primitives; - import std.typecons : tuple; - - auto a = [1, 2, 3]; - auto b = [4, 5, 6, 7]; - - auto shortest = zip(StoppingPolicy.shortest, a, b); - assert(shortest.equal([ - tuple(1, 4), - tuple(2, 5), - tuple(3, 6) - ])); - - auto longest = zip(StoppingPolicy.longest, a, b); - assert(longest.equal([ - tuple(1, 4), - tuple(2, 5), - tuple(3, 6), - tuple(0, 7) - ])); - - auto same = zip(StoppingPolicy.requireSameLength, a, b); - same.popFrontN(3); - assertThrown!Exception(same.popFront); -} - -/+ -Non-public. Like $(LREF Zip) with `StoppingPolicy.shortest` -except it properly implements `back` and `popBack` in the -case of uneven ranges or disables those operations when -it is not possible to guarantee they are correct. -+/ -package template ZipShortest(Ranges...) -if (Ranges.length && __traits(compiles, - { - static assert(allSatisfy!(isInputRange, Ranges)); - })) -{ - alias ZipShortest = .ZipShortest!( - Ranges.length == 1 || allSatisfy!(isInfinite, Ranges) - ? Yes.allKnownSameLength - : No.allKnownSameLength, - Ranges); -} -/+ non-public, ditto +/ -package struct ZipShortest(Flag!"allKnownSameLength" allKnownSameLength, Ranges...) -if (Ranges.length && allSatisfy!(isInputRange, Ranges)) -{ - import std.format : format; //for generic mixins - import std.meta : anySatisfy, templateOr; - import std.typecons : Tuple; - - deprecated("Use of an undocumented alias R.") - alias R = Ranges; // Unused here but defined in case library users rely on it. - private Ranges ranges; - alias ElementType = Tuple!(staticMap!(.ElementType, Ranges)); - - /+ - Builds an object. Usually this is invoked indirectly by using the - $(LREF zip) function. - +/ - this(Ranges rs) - { - ranges[] = rs[]; - } - - /+ - Returns `true` if the range is at end. - +/ - static if (allKnownSameLength ? anySatisfy!(isInfinite, Ranges) - : allSatisfy!(isInfinite, Ranges)) - { - enum bool empty = false; - } - else - { - @property bool empty() - { - static if (allKnownSameLength) - { - return ranges[0].empty; - } - else - { - static foreach (i; 0 .. Ranges.length) - { - if (ranges[i].empty) - return true; - } - return false; - } - } - } - - /+ - Forward range primitive. Only present if each constituent range is a - forward range. - +/ - static if (allSatisfy!(isForwardRange, Ranges)) - @property typeof(this) save() - { - return mixin(`typeof(return)(%(ranges[%s].save%|, %))`.format(iota(0, Ranges.length))); - } - - /+ - Returns the current iterated element. - +/ - @property ElementType front() - { - return mixin(`typeof(return)(%(ranges[%s].front%|, %))`.format(iota(0, Ranges.length))); - } - - /+ - Sets the front of all iterated ranges. Only present if each constituent - range has assignable elements. - +/ - static if (allSatisfy!(hasAssignableElements, Ranges)) - @property void front()(ElementType v) - { - static foreach (i; 0 .. Ranges.length) - ranges[i].front = v[i]; - } - - /+ - Moves out the front. Present if each constituent range has mobile elements. - +/ - static if (allSatisfy!(hasMobileElements, Ranges)) - ElementType moveFront()() - { - return mixin(`typeof(return)(%(ranges[%s].moveFront()%|, %))`.format(iota(0, Ranges.length))); - } - - private enum bool isBackWellDefined = allSatisfy!(isBidirectionalRange, Ranges) - && (allKnownSameLength - || allSatisfy!(isRandomAccessRange, Ranges) - // Could also add the case where there is one non-infinite bidirectional - // range that defines `length` and all others are infinite random access - // ranges. Adding this would require appropriate branches in - // back/moveBack/popBack. - ); - - /+ - Returns the rightmost element. Present if all constituent ranges are - bidirectional and either there is a compile-time guarantee that all - ranges have the same length (in `allKnownSameLength`) or all ranges - provide random access to elements. - +/ - static if (isBackWellDefined) - @property ElementType back() - { - static if (allKnownSameLength) - { - return mixin(`typeof(return)(%(ranges[%s].back()%|, %))`.format(iota(0, Ranges.length))); - } - else - { - const backIndex = length - 1; - return mixin(`typeof(return)(%(ranges[%s][backIndex]%|, %))`.format(iota(0, Ranges.length))); - } - } - - /+ - Moves out the back. Present if `back` is defined and - each constituent range has mobile elements. - +/ - static if (isBackWellDefined && allSatisfy!(hasMobileElements, Ranges)) - ElementType moveBack()() - { - static if (allKnownSameLength) - { - return mixin(`typeof(return)(%(ranges[%s].moveBack()%|, %))`.format(iota(0, Ranges.length))); - } - else - { - const backIndex = length - 1; - return mixin(`typeof(return)(%(ranges[%s].moveAt(backIndex)%|, %))`.format(iota(0, Ranges.length))); - } - } - - /+ - Sets the rightmost element. Only present if `back` is defined and - each constituent range has assignable elements. - +/ - static if (isBackWellDefined && allSatisfy!(hasAssignableElements, Ranges)) - @property void back()(ElementType v) - { - static if (allKnownSameLength) - { - static foreach (i; 0 .. Ranges.length) - ranges[i].back = v[i]; - } - else - { - const backIndex = length - 1; - static foreach (i; 0 .. Ranges.length) - ranges[i][backIndex] = v[i]; - } - } - - /+ - Calls `popFront` on each constituent range. - +/ - void popFront() - { - static foreach (i; 0 .. Ranges.length) - ranges[i].popFront(); - } - - /+ - Pops the rightmost element. Present if `back` is defined. - +/ - static if (isBackWellDefined) - void popBack() - { - static if (allKnownSameLength) - { - static foreach (i; 0 .. Ranges.length) - ranges[i].popBack; - } - else - { - const len = length; - static foreach (i; 0 .. Ranges.length) - static if (!isInfinite!(Ranges[i])) - if (ranges[i].length == len) - ranges[i].popBack(); - } - } - - /+ - Returns the length of this range. Defined if at least one - constituent range defines `length` and the other ranges all also - define `length` or are infinite, or if at least one constituent - range defines `length` and there is a compile-time guarantee that - all ranges have the same length (in `allKnownSameLength`). - +/ - static if (allKnownSameLength - ? anySatisfy!(hasLength, Ranges) - : (anySatisfy!(hasLength, Ranges) - && allSatisfy!(templateOr!(isInfinite, hasLength), Ranges))) - { - @property size_t length() - { - static foreach (i, Range; Ranges) - { - static if (hasLength!Range) - { - static if (!is(typeof(minLen) == size_t)) - size_t minLen = ranges[i].length; - else static if (!allKnownSameLength) - {{ - const x = ranges[i].length; - if (x < minLen) minLen = x; - }} - } - } - return minLen; - } - - alias opDollar = length; - } - - /+ - Returns a slice of the range. Defined if all constituent ranges - support slicing. - +/ - static if (allSatisfy!(hasSlicing, Ranges)) - { - // Note: we will know that all elements of the resultant range - // will have the same length but we cannot change `allKnownSameLength` - // because the `hasSlicing` predicate tests that the result returned - // by `opSlice` has the same type as the receiver. - auto opSlice()(size_t from, size_t to) - { - //(ranges[0][from .. to], ranges[1][from .. to], ...) - enum sliceArgs = `(%(ranges[%s][from .. to]%|, %))`.format(iota(0, Ranges.length)); - static if (__traits(compiles, mixin(`typeof(this)`~sliceArgs))) - return mixin(`typeof(this)`~sliceArgs); - else - // The type is different anyway so we might as well - // explicitly set allKnownSameLength. - return mixin(`ZipShortest!(Yes.allKnownSameLength, staticMap!(Take, Ranges))` - ~sliceArgs); - } - } - - /+ - Returns the `n`th element in the composite range. Defined if all - constituent ranges offer random access. - +/ - static if (allSatisfy!(isRandomAccessRange, Ranges)) - ElementType opIndex()(size_t n) - { - return mixin(`typeof(return)(%(ranges[%s][n]%|, %))`.format(iota(0, Ranges.length))); - } - - /+ - Sets the `n`th element in the composite range. Defined if all - constituent ranges offer random access and have assignable elements. - +/ - static if (allSatisfy!(isRandomAccessRange, Ranges) - && allSatisfy!(hasAssignableElements, Ranges)) - void opIndexAssign()(ElementType v, size_t n) - { - static foreach (i; 0 .. Ranges.length) - ranges[i][n] = v[i]; - } - - /+ - Destructively reads the `n`th element in the composite - range. Defined if all constituent ranges offer random - access and have mobile elements. - +/ - static if (allSatisfy!(isRandomAccessRange, Ranges) - && allSatisfy!(hasMobileElements, Ranges)) - ElementType moveAt()(size_t n) - { - return mixin(`typeof(return)(%(ranges[%s].moveAt(n)%|, %))`.format(iota(0, Ranges.length))); - } -} - -pure @system unittest -{ - import std.algorithm.comparison : equal; - import std.algorithm.iteration : filter, map; - import std.algorithm.mutation : swap; - import std.algorithm.sorting : sort; - - import std.exception : assertThrown, assertNotThrown; - import std.typecons : tuple; - - int[] a = [ 1, 2, 3 ]; - float[] b = [ 1.0, 2.0, 3.0 ]; - foreach (e; zip(a, b)) - { - assert(e[0] == e[1]); - } - - swap(a[0], a[1]); - { - auto z = zip(a, b); - } - //swap(z.front(), z.back()); - sort!("a[0] < b[0]")(zip(a, b)); - assert(a == [1, 2, 3]); - assert(b == [2.0, 1.0, 3.0]); - - auto z = zip(StoppingPolicy.requireSameLength, a, b); - assertNotThrown(z.popBack()); - assertNotThrown(z.popBack()); - assertNotThrown(z.popBack()); - assert(z.empty); - assertThrown(z.popBack()); - - a = [ 1, 2, 3 ]; - b = [ 1.0, 2.0, 3.0 ]; - sort!("a[0] > b[0]")(zip(StoppingPolicy.requireSameLength, a, b)); - assert(a == [3, 2, 1]); - assert(b == [3.0, 2.0, 1.0]); - - a = []; - b = []; - assert(zip(StoppingPolicy.requireSameLength, a, b).empty); - - // Test infiniteness propagation. - static assert(isInfinite!(typeof(zip(repeat(1), repeat(1))))); - - // Test stopping policies with both value and reference. - auto a1 = [1, 2]; - auto a2 = [1, 2, 3]; - auto stuff = tuple(tuple(a1, a2), - tuple(filter!"a"(a1), filter!"a"(a2))); - - alias FOO = Zip!(immutable(int)[], immutable(float)[]); - - foreach (t; stuff.expand) - { - auto arr1 = t[0]; - auto arr2 = t[1]; - auto zShortest = zip(arr1, arr2); - assert(equal(map!"a[0]"(zShortest), [1, 2])); - assert(equal(map!"a[1]"(zShortest), [1, 2])); - - try { - auto zSame = zip(StoppingPolicy.requireSameLength, arr1, arr2); - foreach (elem; zSame) {} - assert(0); - } catch (Throwable) { /* It's supposed to throw.*/ } - - auto zLongest = zip(StoppingPolicy.longest, arr1, arr2); - assert(!zLongest.ranges[0].empty); - assert(!zLongest.ranges[1].empty); - - zLongest.popFront(); - zLongest.popFront(); - assert(!zLongest.empty); - assert(zLongest.ranges[0].empty); - assert(!zLongest.ranges[1].empty); - - zLongest.popFront(); - assert(zLongest.empty); - } - - // https://issues.dlang.org/show_bug.cgi?id=8900 - assert(zip([1, 2], repeat('a')).array == [tuple(1, 'a'), tuple(2, 'a')]); - assert(zip(repeat('a'), [1, 2]).array == [tuple('a', 1), tuple('a', 2)]); - - // https://issues.dlang.org/show_bug.cgi?id=18524 - // moveBack instead performs moveFront - { - auto r = zip([1,2,3]); - assert(r.moveBack()[0] == 3); - assert(r.moveFront()[0] == 1); - } - - // Doesn't work yet. Issues w/ emplace. - // static assert(is(Zip!(immutable int[], immutable float[]))); - - - // These unittests pass, but make the compiler consume an absurd amount - // of RAM and time. Therefore, they should only be run if explicitly - // uncommented when making changes to Zip. Also, running them using - // make -fwin32.mak unittest makes the compiler completely run out of RAM. - // You need to test just this module. - /+ - foreach (DummyType1; AllDummyRanges) - { - DummyType1 d1; - foreach (DummyType2; AllDummyRanges) - { - DummyType2 d2; - auto r = zip(d1, d2); - assert(equal(map!"a[0]"(r), [1,2,3,4,5,6,7,8,9,10])); - assert(equal(map!"a[1]"(r), [1,2,3,4,5,6,7,8,9,10])); - - static if (isForwardRange!DummyType1 && isForwardRange!DummyType2) - { - static assert(isForwardRange!(typeof(r))); - } - - static if (isBidirectionalRange!DummyType1 && - isBidirectionalRange!DummyType2) { - static assert(isBidirectionalRange!(typeof(r))); - } - static if (isRandomAccessRange!DummyType1 && - isRandomAccessRange!DummyType2) { - static assert(isRandomAccessRange!(typeof(r))); - } - } - } - +/ -} - -nothrow pure @safe unittest -{ - import std.algorithm.sorting : sort; - - auto a = [5,4,3,2,1]; - auto b = [3,1,2,5,6]; - auto z = zip(a, b); - - sort!"a[0] < b[0]"(z); - - assert(a == [1, 2, 3, 4, 5]); - assert(b == [6, 5, 2, 1, 3]); -} - -nothrow pure @safe unittest -{ - import std.algorithm.comparison : equal; - import std.typecons : tuple; - - auto LL = iota(1L, 1000L); - auto z = zip(LL, [4]); - - assert(equal(z, [tuple(1L,4)])); - - auto LL2 = iota(0L, 500L); - auto z2 = zip([7], LL2); - assert(equal(z2, [tuple(7, 0L)])); -} - -// Test for https://issues.dlang.org/show_bug.cgi?id=11196 -@safe pure unittest -{ - import std.exception : assertThrown; - - static struct S { @disable this(); } - assert(zip((S[5]).init[]).length == 5); - assert(zip(StoppingPolicy.longest, cast(S[]) null, new int[1]).length == 1); - assertThrown(zip(StoppingPolicy.longest, cast(S[]) null, new int[1]).front); -} - -// https://issues.dlang.org/show_bug.cgi?id=12007 -@nogc nothrow @safe pure unittest -{ - static struct R - { - enum empty = false; - void popFront(){} - int front(){return 1;} @property - R save(){return this;} @property - void opAssign(R) @disable; - } - R r; - auto z = zip(r, r); - assert(z.save == z); -} - -nothrow pure @system unittest -{ - import std.typecons : tuple; - - auto r1 = [0,1,2]; - auto r2 = [1,2,3]; - auto z1 = zip(refRange(&r1), refRange(&r2)); - auto z2 = z1.save; - z1.popFront(); - assert(z1.front == tuple(1,2)); - assert(z2.front == tuple(0,1)); -} - -@nogc nothrow pure @safe unittest -{ - // Test zip's `back` and `length` with non-equal ranges. - static struct NonSliceableRandomAccess - { - private int[] a; - @property ref front() - { - return a.front; - } - @property ref back() - { - return a.back; - } - ref opIndex(size_t i) - { - return a[i]; - } - void popFront() - { - a.popFront(); - } - void popBack() - { - a.popBack(); - } - auto moveFront() - { - return a.moveFront(); - } - auto moveBack() - { - return a.moveBack(); - } - auto moveAt(size_t i) - { - return a.moveAt(i); - } - bool empty() const - { - return a.empty; - } - size_t length() const - { - return a.length; - } - typeof(this) save() - { - return this; - } - } - static assert(isRandomAccessRange!NonSliceableRandomAccess); - static assert(!hasSlicing!NonSliceableRandomAccess); - static foreach (iteration; 0 .. 2) - {{ - int[5] data = [101, 102, 103, 201, 202]; - static if (iteration == 0) - { - auto r1 = NonSliceableRandomAccess(data[0 .. 3]); - auto r2 = NonSliceableRandomAccess(data[3 .. 5]); - } - else - { - auto r1 = data[0 .. 3]; - auto r2 = data[3 .. 5]; - } - auto z = zip(r1, r2); - static assert(isRandomAccessRange!(typeof(z))); - assert(z.length == 2); - assert(z.back[0] == 102 && z.back[1] == 202); - z.back = typeof(z.back)(-102, -202);// Assign to back. - assert(z.back[0] == -102 && z.back[1] == -202); - z.popBack(); - assert(z.length == 1); - assert(z.back[0] == 101 && z.back[1] == 201); - z.front = typeof(z.front)(-101, -201); - assert(z.moveBack() == typeof(z.back)(-101, -201)); - z.popBack(); - assert(z.empty); - }} -} - -@nogc nothrow pure @safe unittest -{ - // Test opSlice on infinite `zip`. - auto z = zip(repeat(1), repeat(2)); - assert(hasSlicing!(typeof(z))); - auto slice = z[10 .. 20]; - assert(slice.length == 10); - static assert(!is(typeof(z) == typeof(slice))); -} - -/* - Generate lockstep's opApply function as a mixin string. - If withIndex is true prepend a size_t index to the delegate. -*/ -private string lockstepMixin(Ranges...)(bool withIndex, bool reverse) -{ - import std.format : format; - - string[] params; - string[] emptyChecks; - string[] dgArgs; - string[] popFronts; - string indexDef; - string indexInc; - - if (withIndex) - { - params ~= "size_t"; - dgArgs ~= "index"; - if (reverse) - { - indexDef = q{ - size_t index = ranges[0].length-1; - enforce(_stoppingPolicy == StoppingPolicy.requireSameLength, - "lockstep can only be used with foreach_reverse when stoppingPolicy == requireSameLength"); - - foreach (range; ranges[1..$]) - enforce(range.length == ranges[0].length); - }; - indexInc = "--index;"; - } - else - { - indexDef = "size_t index = 0;"; - indexInc = "++index;"; - } - } - - foreach (idx, Range; Ranges) - { - params ~= format("%sElementType!(Ranges[%s])", hasLvalueElements!Range ? "ref " : "", idx); - emptyChecks ~= format("!ranges[%s].empty", idx); - if (reverse) - { - dgArgs ~= format("ranges[%s].back", idx); - popFronts ~= format("ranges[%s].popBack();", idx); - } - else - { - dgArgs ~= format("ranges[%s].front", idx); - popFronts ~= format("ranges[%s].popFront();", idx); - } - } - - string name = reverse ? "opApplyReverse" : "opApply"; - - return format( - q{ - int %s(scope int delegate(%s) dg) - { - import std.exception : enforce; - - auto ranges = _ranges; - int res; - %s - - while (%s) - { - res = dg(%s); - if (res) break; - %s - %s - } - - if (_stoppingPolicy == StoppingPolicy.requireSameLength) - { - foreach (range; ranges) - enforce(range.empty); - } - return res; - } - }, name, params.join(", "), indexDef, - emptyChecks.join(" && "), dgArgs.join(", "), - popFronts.join("\n "), - indexInc); -} - -/** - Iterate multiple ranges in lockstep using a `foreach` loop. In contrast to - $(LREF zip) it allows reference access to its elements. If only a single - range is passed in, the `Lockstep` aliases itself away. If the - ranges are of different lengths and `s` == `StoppingPolicy.shortest` - stop after the shortest range is empty. If the ranges are of different - lengths and `s` == `StoppingPolicy.requireSameLength`, throw an - exception. `s` may not be `StoppingPolicy.longest`, and passing this - will throw an exception. - - Iterating over `Lockstep` in reverse and with an index is only possible - when `s` == `StoppingPolicy.requireSameLength`, in order to preserve - indexes. If an attempt is made at iterating in reverse when `s` == - `StoppingPolicy.shortest`, an exception will be thrown. - - By default `StoppingPolicy` is set to `StoppingPolicy.shortest`. - - Limitations: The `pure`, `@safe`, `@nogc`, or `nothrow` attributes cannot be - inferred for `lockstep` iteration. $(LREF zip) can infer the first two due to - a different implementation. - - See_Also: $(LREF zip) - - `lockstep` is similar to $(LREF zip), but `zip` bundles its - elements and returns a range. - `lockstep` also supports reference access. - Use `zip` if you want to pass the result to a range function. -*/ -struct Lockstep(Ranges...) -if (Ranges.length > 1 && allSatisfy!(isInputRange, Ranges)) -{ - /// - this(R ranges, StoppingPolicy sp = StoppingPolicy.shortest) - { - import std.exception : enforce; - - _ranges = ranges; - enforce(sp != StoppingPolicy.longest, - "Can't use StoppingPolicy.Longest on Lockstep."); - _stoppingPolicy = sp; - } - - mixin(lockstepMixin!Ranges(false, false)); - mixin(lockstepMixin!Ranges(true, false)); - static if (allSatisfy!(isBidirectionalRange, Ranges)) - { - mixin(lockstepMixin!Ranges(false, true)); - static if (allSatisfy!(hasLength, Ranges)) - { - mixin(lockstepMixin!Ranges(true, true)); - } - else - { - mixin(lockstepReverseFailMixin!Ranges(true)); - } - } - else - { - mixin(lockstepReverseFailMixin!Ranges(false)); - mixin(lockstepReverseFailMixin!Ranges(true)); - } - -private: - alias R = Ranges; - R _ranges; - StoppingPolicy _stoppingPolicy; -} - -/// Ditto -Lockstep!(Ranges) lockstep(Ranges...)(Ranges ranges) -if (allSatisfy!(isInputRange, Ranges)) -{ - return Lockstep!(Ranges)(ranges); -} -/// Ditto -Lockstep!(Ranges) lockstep(Ranges...)(Ranges ranges, StoppingPolicy s) -if (allSatisfy!(isInputRange, Ranges)) -{ - static if (Ranges.length > 1) - return Lockstep!Ranges(ranges, s); - else - return ranges[0]; -} - -/// -@system unittest -{ - auto arr1 = [1,2,3,4,5,100]; - auto arr2 = [6,7,8,9,10]; - - foreach (ref a, b; lockstep(arr1, arr2)) - { - a += b; - } - - assert(arr1 == [7,9,11,13,15,100]); - - /// Lockstep also supports iterating with an index variable: - foreach (index, a, b; lockstep(arr1, arr2)) - { - assert(arr1[index] == a); - assert(arr2[index] == b); - } -} - -// https://issues.dlang.org/show_bug.cgi?id=15860: foreach_reverse on lockstep -@system unittest -{ - auto arr1 = [0, 1, 2, 3]; - auto arr2 = [4, 5, 6, 7]; - - size_t n = arr1.length -1; - foreach_reverse (index, a, b; lockstep(arr1, arr2, StoppingPolicy.requireSameLength)) - { - assert(n == index); - assert(index == a); - assert(arr1[index] == a); - assert(arr2[index] == b); - n--; - } - - auto arr3 = [4, 5]; - n = 1; - foreach_reverse (a, b; lockstep(arr1, arr3)) - { - assert(a == arr1[$-n] && b == arr3[$-n]); - n++; - } -} - -@system unittest -{ - import std.algorithm.iteration : filter; - import std.conv : to; - - // The filters are to make these the lowest common forward denominator ranges, - // i.e. w/o ref return, random access, length, etc. - auto foo = filter!"a"([1,2,3,4,5]); - immutable bar = [6f,7f,8f,9f,10f].idup; - auto l = lockstep(foo, bar); - - // Should work twice. These are forward ranges with implicit save. - foreach (i; 0 .. 2) - { - uint[] res1; - float[] res2; - - foreach (a, ref b; l) - { - res1 ~= a; - res2 ~= b; - } - - assert(res1 == [1,2,3,4,5]); - assert(res2 == [6,7,8,9,10]); - assert(bar == [6f,7f,8f,9f,10f]); - } - - // Doc example. - auto arr1 = [1,2,3,4,5]; - auto arr2 = [6,7,8,9,10]; - - foreach (ref a, ref b; lockstep(arr1, arr2)) - { - a += b; - } - - assert(arr1 == [7,9,11,13,15]); - - // Make sure StoppingPolicy.requireSameLength doesn't throw. - auto ls = lockstep(arr1, arr2, StoppingPolicy.requireSameLength); - - int k = 1; - foreach (a, b; ls) - { - assert(a - b == k); - ++k; - } - - // Make sure StoppingPolicy.requireSameLength throws. - arr2.popBack(); - ls = lockstep(arr1, arr2, StoppingPolicy.requireSameLength); - - try { - foreach (a, b; ls) {} - assert(0); - } catch (Exception) {} - - // Just make sure 1-range case instantiates. This hangs the compiler - // when no explicit stopping policy is specified due to - // https://issues.dlang.org/show_bug.cgi?id=4652 - auto stuff = lockstep([1,2,3,4,5], StoppingPolicy.shortest); - foreach (i, a; stuff) - { - assert(stuff[i] == a); - } - - // Test with indexing. - uint[] res1; - float[] res2; - size_t[] indices; - foreach (i, a, b; lockstep(foo, bar)) - { - indices ~= i; - res1 ~= a; - res2 ~= b; - } - - assert(indices == to!(size_t[])([0, 1, 2, 3, 4])); - assert(res1 == [1,2,3,4,5]); - assert(res2 == [6f,7f,8f,9f,10f]); - - // Make sure we've worked around the relevant compiler bugs and this at least - // compiles w/ >2 ranges. - lockstep(foo, foo, foo); - - // Make sure it works with const. - const(int[])[] foo2 = [[1, 2, 3]]; - const(int[])[] bar2 = [[4, 5, 6]]; - auto c = chain(foo2, bar2); - - foreach (f, b; lockstep(c, c)) {} - - // Regression 10468 - foreach (x, y; lockstep(iota(0, 10), iota(0, 10))) { } -} - -@system unittest -{ - struct RvalueRange - { - int[] impl; - @property bool empty() { return impl.empty; } - @property int front() { return impl[0]; } // N.B. non-ref - void popFront() { impl.popFront(); } - } - auto data1 = [ 1, 2, 3, 4 ]; - auto data2 = [ 5, 6, 7, 8 ]; - auto r1 = RvalueRange(data1); - auto r2 = data2; - foreach (a, ref b; lockstep(r1, r2)) - { - a++; - b++; - } - assert(data1 == [ 1, 2, 3, 4 ]); // changes to a do not propagate to data - assert(data2 == [ 6, 7, 8, 9 ]); // but changes to b do. - - // Since r1 is by-value only, the compiler should reject attempts to - // foreach over it with ref. - static assert(!__traits(compiles, { - foreach (ref a, ref b; lockstep(r1, r2)) { a++; } - })); -} - -private string lockstepReverseFailMixin(Ranges...)(bool withIndex) -{ - import std.format : format; - string[] params; - string message; - - if (withIndex) - { - message = "Indexed reverse iteration with lockstep is only supported" - ~"if all ranges are bidirectional and have a length.\n"; - } - else - { - message = "Reverse iteration with lockstep is only supported if all ranges are bidirectional.\n"; - } - - if (withIndex) - { - params ~= "size_t"; - } - - foreach (idx, Range; Ranges) - { - params ~= format("%sElementType!(Ranges[%s])", hasLvalueElements!Range ? "ref " : "", idx); - } - - return format( - q{ - int opApplyReverse()(scope int delegate(%s) dg) - { - static assert(false, "%s"); - } - }, params.join(", "), message); -} - -// For generic programming, make sure Lockstep!(Range) is well defined for a -// single range. -template Lockstep(Range) -{ - alias Lockstep = Range; -} - -/** -Creates a mathematical sequence given the initial values and a -recurrence function that computes the next value from the existing -values. The sequence comes in the form of an infinite forward -range. The type `Recurrence` itself is seldom used directly; most -often, recurrences are obtained by calling the function $(D -recurrence). - -When calling `recurrence`, the function that computes the next -value is specified as a template argument, and the initial values in -the recurrence are passed as regular arguments. For example, in a -Fibonacci sequence, there are two initial values (and therefore a -state size of 2) because computing the next Fibonacci value needs the -past two values. - -The signature of this function should be: ----- -auto fun(R)(R state, size_t n) ----- -where `n` will be the index of the current value, and `state` will be an -opaque state vector that can be indexed with array-indexing notation -`state[i]`, where valid values of `i` range from $(D (n - 1)) to -$(D (n - State.length)). - -If the function is passed in string form, the state has name `"a"` -and the zero-based index in the recurrence has name `"n"`. The -given string must return the desired value for `a[n]` given -`a[n - 1]`, `a[n - 2]`, `a[n - 3]`,..., `a[n - stateSize]`. The -state size is dictated by the number of arguments passed to the call -to `recurrence`. The `Recurrence` struct itself takes care of -managing the recurrence's state and shifting it appropriately. - */ -struct Recurrence(alias fun, StateType, size_t stateSize) -{ - import std.functional : binaryFun; - - StateType[stateSize] _state; - size_t _n; - - this(StateType[stateSize] initial) { _state = initial; } - - void popFront() - { - static auto trustedCycle(ref typeof(_state) s) @trusted - { - return cycle(s); - } - // The cast here is reasonable because fun may cause integer - // promotion, but needs to return a StateType to make its operation - // closed. Therefore, we have no other choice. - _state[_n % stateSize] = cast(StateType) binaryFun!(fun, "a", "n")( - trustedCycle(_state), _n + stateSize); - ++_n; - } - - @property StateType front() - { - return _state[_n % stateSize]; - } - - @property typeof(this) save() - { - return this; - } - - enum bool empty = false; -} - -/// -pure @safe nothrow unittest -{ - import std.algorithm.comparison : equal; - - // The Fibonacci numbers, using function in string form: - // a[0] = 1, a[1] = 1, and compute a[n+1] = a[n-1] + a[n] - auto fib = recurrence!("a[n-1] + a[n-2]")(1, 1); - assert(fib.take(10).equal([1, 1, 2, 3, 5, 8, 13, 21, 34, 55])); - - // The factorials, using function in lambda form: - auto fac = recurrence!((a,n) => a[n-1] * n)(1); - assert(take(fac, 10).equal([ - 1, 1, 2, 6, 24, 120, 720, 5040, 40320, 362880 - ])); - - // The triangular numbers, using function in explicit form: - static size_t genTriangular(R)(R state, size_t n) - { - return state[n-1] + n; - } - auto tri = recurrence!genTriangular(0); - assert(take(tri, 10).equal([0, 1, 3, 6, 10, 15, 21, 28, 36, 45])); -} - -/// Ditto -Recurrence!(fun, CommonType!(State), State.length) -recurrence(alias fun, State...)(State initial) -{ - CommonType!(State)[State.length] state; - foreach (i, Unused; State) - { - state[i] = initial[i]; - } - return typeof(return)(state); -} - -pure @safe nothrow unittest -{ - import std.algorithm.comparison : equal; - - auto fib = recurrence!("a[n-1] + a[n-2]")(1, 1); - static assert(isForwardRange!(typeof(fib))); - - int[] witness = [1, 1, 2, 3, 5, 8, 13, 21, 34, 55 ]; - assert(equal(take(fib, 10), witness)); - foreach (e; take(fib, 10)) {} - auto fact = recurrence!("n * a[n-1]")(1); - assert( equal(take(fact, 10), [1, 1, 2, 2*3, 2*3*4, 2*3*4*5, 2*3*4*5*6, - 2*3*4*5*6*7, 2*3*4*5*6*7*8, 2*3*4*5*6*7*8*9][]) ); - auto piapprox = recurrence!("a[n] + (n & 1 ? 4.0 : -4.0) / (2 * n + 3)")(4.0); - foreach (e; take(piapprox, 20)) {} - // Thanks to yebblies for this test and the associated fix - auto r = recurrence!"a[n-2]"(1, 2); - witness = [1, 2, 1, 2, 1]; - assert(equal(take(r, 5), witness)); -} - -/** - `Sequence` is similar to `Recurrence` except that iteration is - presented in the so-called $(HTTP en.wikipedia.org/wiki/Closed_form, - closed form). This means that the `n`th element in the series is - computable directly from the initial values and `n` itself. This - implies that the interface offered by `Sequence` is a random-access - range, as opposed to the regular `Recurrence`, which only offers - forward iteration. - - The state of the sequence is stored as a `Tuple` so it can be - heterogeneous. -*/ -struct Sequence(alias fun, State) -{ -private: - import std.functional : binaryFun; - - alias compute = binaryFun!(fun, "a", "n"); - alias ElementType = typeof(compute(State.init, cast(size_t) 1)); - State _state; - size_t _n; - - static struct DollarToken{} - -public: - this(State initial, size_t n = 0) - { - _state = initial; - _n = n; - } - - @property ElementType front() - { - return compute(_state, _n); - } - - void popFront() - { - ++_n; - } - - enum opDollar = DollarToken(); - - auto opSlice(size_t lower, size_t upper) - in - { - assert( - upper >= lower, - "Attempting to slice a Sequence with a larger first argument than the second." - ); - } - do - { - return typeof(this)(_state, _n + lower).take(upper - lower); - } - - auto opSlice(size_t lower, DollarToken) - { - return typeof(this)(_state, _n + lower); - } - - ElementType opIndex(size_t n) - { - return compute(_state, n + _n); - } - - enum bool empty = false; - - @property Sequence save() { return this; } -} - -/// Ditto -auto sequence(alias fun, State...)(State args) -{ - import std.typecons : Tuple, tuple; - alias Return = Sequence!(fun, Tuple!State); - return Return(tuple(args)); -} - -/// Odd numbers, using function in string form: -pure @safe nothrow @nogc unittest -{ - auto odds = sequence!("a[0] + n * a[1]")(1, 2); - assert(odds.front == 1); - odds.popFront(); - assert(odds.front == 3); - odds.popFront(); - assert(odds.front == 5); -} - -/// Triangular numbers, using function in lambda form: -pure @safe nothrow @nogc unittest -{ - auto tri = sequence!((a,n) => n*(n+1)/2)(); - - // Note random access - assert(tri[0] == 0); - assert(tri[3] == 6); - assert(tri[1] == 1); - assert(tri[4] == 10); - assert(tri[2] == 3); -} - -/// Fibonacci numbers, using function in explicit form: -@safe nothrow @nogc unittest -{ - import std.math.exponential : pow; - import std.math.rounding : round; - import std.math.algebraic : sqrt; - static ulong computeFib(S)(S state, size_t n) - { - // Binet's formula - return cast(ulong)(round((pow(state[0], n+1) - pow(state[1], n+1)) / - state[2])); - } - auto fib = sequence!computeFib( - (1.0 + sqrt(5.0)) / 2.0, // Golden Ratio - (1.0 - sqrt(5.0)) / 2.0, // Conjugate of Golden Ratio - sqrt(5.0)); - - // Note random access with [] operator - assert(fib[1] == 1); - assert(fib[4] == 5); - assert(fib[3] == 3); - assert(fib[2] == 2); - assert(fib[9] == 55); -} - -pure @safe nothrow @nogc unittest -{ - import std.typecons : Tuple, tuple; - auto y = Sequence!("a[0] + n * a[1]", Tuple!(int, int))(tuple(0, 4)); - static assert(isForwardRange!(typeof(y))); - - //@@BUG - //auto y = sequence!("a[0] + n * a[1]")(0, 4); - //foreach (e; take(y, 15)) - {} //writeln(e); - - auto odds = Sequence!("a[0] + n * a[1]", Tuple!(int, int))( - tuple(1, 2)); - for (int currentOdd = 1; currentOdd <= 21; currentOdd += 2) - { - assert(odds.front == odds[0]); - assert(odds[0] == currentOdd); - odds.popFront(); - } -} - -pure @safe nothrow @nogc unittest -{ - import std.algorithm.comparison : equal; - - auto odds = sequence!("a[0] + n * a[1]")(1, 2); - static assert(hasSlicing!(typeof(odds))); - - //Note: don't use drop or take as the target of an equal, - //since they'll both just forward to opSlice, making the tests irrelevant - - // static slicing tests - assert(equal(odds[0 .. 5], only(1, 3, 5, 7, 9))); - assert(equal(odds[3 .. 7], only(7, 9, 11, 13))); - - // relative slicing test, testing slicing is NOT agnostic of state - auto odds_less5 = odds.drop(5); //this should actually call odds[5 .. $] - assert(equal(odds_less5[0 .. 3], only(11, 13, 15))); - assert(equal(odds_less5[0 .. 10], odds[5 .. 15])); - - //Infinite slicing tests - odds = odds[10 .. $]; - assert(equal(odds.take(3), only(21, 23, 25))); -} - -// https://issues.dlang.org/show_bug.cgi?id=5036 -pure @safe nothrow unittest -{ - auto s = sequence!((a, n) => new int)(0); - assert(s.front != s.front); // no caching -} - -// iota -/** - Creates a range of values that span the given starting and stopping - values. - - Params: - begin = The starting value. - end = The value that serves as the stopping criterion. This value is not - included in the range. - step = The value to add to the current value at each iteration. - - Returns: - A range that goes through the numbers `begin`, $(D begin + step), - $(D begin + 2 * step), `...`, up to and excluding `end`. - - The two-argument overloads have $(D step = 1). If $(D begin < end && step < - 0) or $(D begin > end && step > 0) or $(D begin == end), then an empty range - is returned. If $(D step == 0) then $(D begin == end) is an error. - - For built-in types, the range returned is a random access range. For - user-defined types that support `++`, the range is an input - range. - - An integral iota also supports `in` operator from the right. It takes - the stepping into account, the integral won't be considered - contained if it falls between two consecutive values of the range. - `contains` does the same as in, but from lefthand side. - - Example: - --- - void main() - { - import std.stdio; - - // The following groups all produce the same output of: - // 0 1 2 3 4 - - foreach (i; 0 .. 5) - writef("%s ", i); - writeln(); - - import std.range : iota; - foreach (i; iota(0, 5)) - writef("%s ", i); - writeln(); - - writefln("%(%s %|%)", iota(0, 5)); - - import std.algorithm.iteration : map; - import std.algorithm.mutation : copy; - import std.format; - iota(0, 5).map!(i => format("%s ", i)).copy(stdout.lockingTextWriter()); - writeln(); - } - --- -*/ -auto iota(B, E, S)(B begin, E end, S step) -if ((isIntegral!(CommonType!(B, E)) || isPointer!(CommonType!(B, E))) - && isIntegral!S) -{ - import std.conv : unsigned; - - alias Value = CommonType!(Unqual!B, Unqual!E); - alias StepType = Unqual!S; - - assert(step != 0 || begin == end); - - static struct Result - { - private Value current, last; - private StepType step; // by convention, 0 if range is empty - - this(Value current, Value pastLast, StepType step) - { - if (current < pastLast && step > 0) - { - // Iterating upward - assert(unsigned((pastLast - current) / step) <= size_t.max); - // Cast below can't fail because current < pastLast - this.last = cast(Value) (pastLast - 1); - this.last -= unsigned(this.last - current) % step; - } - else if (current > pastLast && step < 0) - { - // Iterating downward - assert(unsigned((current - pastLast) / (0 - step)) <= size_t.max); - // Cast below can't fail because current > pastLast - this.last = cast(Value) (pastLast + 1); - this.last += unsigned(current - this.last) % (0 - step); - } - else - { - // Initialize an empty range - this.step = 0; - return; - } - this.step = step; - this.current = current; - } - - @property bool empty() const { return step == 0; } - @property inout(Value) front() inout { assert(!empty); return current; } - void popFront() - { - assert(!empty); - if (current == last) step = 0; - else current += step; - } - - @property inout(Value) back() inout - { - assert(!empty); - return last; - } - void popBack() - { - assert(!empty); - if (current == last) step = 0; - else last -= step; - } - - @property auto save() { return this; } - - inout(Value) opIndex(ulong n) inout - { - assert(n < this.length); - - // Just cast to Value here because doing so gives overflow behavior - // consistent with calling popFront() n times. - return cast(inout Value) (current + step * n); - } - auto opBinaryRight(string op)(Value val) const - if (op == "in") - { - if (empty) return false; - //cast to avoid becoming unsigned - auto supposedIndex = cast(StepType)(val - current) / step; - return supposedIndex < length && supposedIndex * step + current == val; - } - auto contains(Value x){return x in this;} - inout(Result) opSlice() inout { return this; } - inout(Result) opSlice(ulong lower, ulong upper) inout - { - assert(upper >= lower && upper <= this.length); - - return cast(inout Result) Result( - cast(Value)(current + lower * step), - cast(Value)(current + upper * step), - step); - } - @property size_t length() const - { - if (step > 0) - return 1 + cast(size_t) (unsigned(last - current) / step); - if (step < 0) - return 1 + cast(size_t) (unsigned(current - last) / (0 - step)); - return 0; - } - - alias opDollar = length; - } - - return Result(begin, end, step); -} - -/// Ditto -auto iota(B, E)(B begin, E end) -if (isFloatingPoint!(CommonType!(B, E))) -{ - return iota(begin, end, CommonType!(B, E)(1)); -} - -/// Ditto -auto iota(B, E)(B begin, E end) -if (isIntegral!(CommonType!(B, E)) || isPointer!(CommonType!(B, E))) -{ - import std.conv : unsigned; - - alias Value = CommonType!(Unqual!B, Unqual!E); - - static struct Result - { - private Value current, pastLast; - - this(Value current, Value pastLast) - { - if (current < pastLast) - { - assert(unsigned(pastLast - current) <= size_t.max, - "`iota` range is too long"); - - this.current = current; - this.pastLast = pastLast; - } - else - { - // Initialize an empty range - this.current = this.pastLast = current; - } - } - - @property bool empty() const { return current == pastLast; } - @property inout(Value) front() inout - { - assert(!empty, "Attempt to access `front` of empty `iota` range"); - return current; - } - void popFront() - { - assert(!empty, "Attempt to `popFront` of empty `iota` range"); - ++current; - } - - @property inout(Value) back() inout - { - assert(!empty, "Attempt to access `back` of empty `iota` range"); - return cast(inout(Value))(pastLast - 1); - } - void popBack() - { - assert(!empty, "Attempt to `popBack` of empty `iota` range"); - --pastLast; - } - - @property auto save() { return this; } - - inout(Value) opIndex(size_t n) inout - { - assert(n < this.length, - "Attempt to read out-of-bounds index of `iota` range"); - - // Just cast to Value here because doing so gives overflow behavior - // consistent with calling popFront() n times. - return cast(inout Value) (current + n); - } - auto opBinaryRight(string op)(Value val) const - if (op == "in") - { - return current <= val && val < pastLast; - } - auto contains(Value x){return x in this;} - inout(Result) opSlice() inout { return this; } - inout(Result) opSlice(ulong lower, ulong upper) inout - { - assert(upper >= lower && upper <= this.length, - "Attempt to get out-of-bounds slice of `iota` range"); - - return cast(inout Result) Result(cast(Value)(current + lower), - cast(Value)(pastLast - (length - upper))); - } - @property size_t length() const - { - return cast(size_t)(pastLast - current); - } - - alias opDollar = length; - } - - return Result(begin, end); -} - -/// Ditto -auto iota(E)(E end) -if (is(typeof(iota(E(0), end)))) -{ - E begin = E(0); - return iota(begin, end); -} - -/// Ditto -// Specialization for floating-point types -auto iota(B, E, S)(B begin, E end, S step) -if (isFloatingPoint!(CommonType!(B, E, S))) -in -{ - assert(step != 0, "iota: step must not be 0"); - assert((end - begin) / step >= 0, "iota: incorrect startup parameters"); -} -do -{ - alias Value = Unqual!(CommonType!(B, E, S)); - static struct Result - { - private Value start, step; - private size_t index, count; - - this(Value start, Value end, Value step) - { - import std.conv : to; - - this.start = start; - this.step = step; - immutable fcount = (end - start) / step; - count = to!size_t(fcount); - auto pastEnd = start + count * step; - if (step > 0) - { - if (pastEnd < end) ++count; - assert(start + count * step >= end); - } - else - { - if (pastEnd > end) ++count; - assert(start + count * step <= end); - } - } - - @property bool empty() const { return index == count; } - @property Value front() const { assert(!empty); return start + step * index; } - void popFront() - { - assert(!empty); - ++index; - } - @property Value back() const - { - assert(!empty); - return start + step * (count - 1); - } - void popBack() - { - assert(!empty); - --count; - } - - @property auto save() { return this; } - - Value opIndex(size_t n) const - { - assert(n < count); - return start + step * (n + index); - } - inout(Result) opSlice() inout - { - return this; - } - inout(Result) opSlice(size_t lower, size_t upper) inout - { - assert(upper >= lower && upper <= count); - - Result ret = this; - ret.index += lower; - ret.count = upper - lower + ret.index; - return cast(inout Result) ret; - } - @property size_t length() const - { - return count - index; - } - - alias opDollar = length; - } - - return Result(begin, end, step); -} - -/// -pure @safe unittest -{ - import std.algorithm.comparison : equal; - import std.math.operations : isClose; - - auto r = iota(0, 10, 1); - assert(equal(r, [0, 1, 2, 3, 4, 5, 6, 7, 8, 9])); - assert(equal(r, [0, 1, 2, 3, 4, 5, 6, 7, 8, 9])); - assert(3 in r); - assert(r.contains(3)); //Same as above - assert(!(10 in r)); - assert(!(-8 in r)); - r = iota(0, 11, 3); - assert(equal(r, [0, 3, 6, 9])); - assert(r[2] == 6); - assert(!(2 in r)); - auto rf = iota(0.0, 0.5, 0.1); - assert(isClose(rf, [0.0, 0.1, 0.2, 0.3, 0.4])); -} - -pure nothrow @nogc @safe unittest -{ - import std.traits : Signed; - //float overloads use std.conv.to so can't be @nogc or nothrow - alias ssize_t = Signed!size_t; - assert(iota(ssize_t.max, 0, -1).length == ssize_t.max); - assert(iota(ssize_t.max, ssize_t.min, -1).length == size_t.max); - assert(iota(ssize_t.max, ssize_t.min, -2).length == 1 + size_t.max / 2); - assert(iota(ssize_t.min, ssize_t.max, 2).length == 1 + size_t.max / 2); - assert(iota(ssize_t.max, ssize_t.min, -3).length == size_t.max / 3); -} - -debug @system unittest -{//check the contracts - import core.exception : AssertError; - import std.exception : assertThrown; - assertThrown!AssertError(iota(1,2,0)); - assertThrown!AssertError(iota(0f,1f,0f)); - assertThrown!AssertError(iota(1f,0f,0.1f)); - assertThrown!AssertError(iota(0f,1f,-0.1f)); -} - -pure @system nothrow unittest -{ - int[] a = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]; - auto r1 = iota(a.ptr, a.ptr + a.length, 1); - assert(r1.front == a.ptr); - assert(r1.back == a.ptr + a.length - 1); - assert(&a[4] in r1); -} - -pure @safe nothrow @nogc unittest -{ - assert(iota(1UL, 0UL).length == 0); - assert(iota(1UL, 0UL, 1).length == 0); - assert(iota(0, 1, 1).length == 1); - assert(iota(1, 0, -1).length == 1); - assert(iota(0, 1, -1).length == 0); - assert(iota(ulong.max, 0).length == 0); -} - -pure @safe unittest -{ - import std.algorithm.comparison : equal; - import std.algorithm.searching : count; - import std.math.operations : isClose, nextUp, nextDown; - import std.meta : AliasSeq; - - static assert(is(ElementType!(typeof(iota(0f))) == float)); - - static assert(hasLength!(typeof(iota(0, 2)))); - auto r = iota(0, 10, 1); - assert(r[$ - 1] == 9); - assert(equal(r, [0, 1, 2, 3, 4, 5, 6, 7, 8, 9][])); - - auto rSlice = r[2 .. 8]; - assert(equal(rSlice, [2, 3, 4, 5, 6, 7])); - - rSlice.popFront(); - assert(rSlice[0] == rSlice.front); - assert(rSlice.front == 3); - - rSlice.popBack(); - assert(rSlice[rSlice.length - 1] == rSlice.back); - assert(rSlice.back == 6); - - rSlice = r[0 .. 4]; - assert(equal(rSlice, [0, 1, 2, 3])); - assert(3 in rSlice); - assert(!(4 in rSlice)); - - auto rr = iota(10); - assert(equal(rr, [0, 1, 2, 3, 4, 5, 6, 7, 8, 9][])); - - r = iota(0, -10, -1); - assert(equal(r, [0, -1, -2, -3, -4, -5, -6, -7, -8, -9][])); - rSlice = r[3 .. 9]; - assert(equal(rSlice, [-3, -4, -5, -6, -7, -8])); - - r = iota(0, -6, -3); - assert(equal(r, [0, -3][])); - rSlice = r[1 .. 2]; - assert(equal(rSlice, [-3])); - - r = iota(0, -7, -3); - assert(equal(r, [0, -3, -6][])); - assert(0 in r); - assert(-6 in r); - rSlice = r[1 .. 3]; - assert(equal(rSlice, [-3, -6])); - assert(!(0 in rSlice)); - assert(!(-2 in rSlice)); - assert(!(-5 in rSlice)); - assert(!(3 in rSlice)); - assert(!(-9 in rSlice)); - - r = iota(0, 11, 3); - assert(equal(r, [0, 3, 6, 9][])); - assert(r[2] == 6); - rSlice = r[1 .. 3]; - assert(equal(rSlice, [3, 6])); - - auto rf = iota(0.0, 0.5, 0.1); - assert(isClose(rf, [0.0, 0.1, 0.2, 0.3, 0.4][])); - assert(rf.length == 5); - - rf.popFront(); - assert(rf.length == 4); - - auto rfSlice = rf[1 .. 4]; - assert(rfSlice.length == 3); - assert(isClose(rfSlice, [0.2, 0.3, 0.4])); - - rfSlice.popFront(); - assert(isClose(rfSlice[0], 0.3)); - - rf.popFront(); - assert(rf.length == 3); - - rfSlice = rf[1 .. 3]; - assert(rfSlice.length == 2); - assert(isClose(rfSlice, [0.3, 0.4])); - assert(isClose(rfSlice[0], 0.3)); - - // With something just above 0.5 - rf = iota(0.0, nextUp(0.5), 0.1); - assert(isClose(rf, [0.0, 0.1, 0.2, 0.3, 0.4, 0.5][])); - rf.popBack(); - assert(rf[rf.length - 1] == rf.back); - assert(isClose(rf.back, 0.4)); - assert(rf.length == 5); - - // going down - rf = iota(0.0, -0.5, -0.1); - assert(isClose(rf, [0.0, -0.1, -0.2, -0.3, -0.4][])); - rfSlice = rf[2 .. 5]; - assert(isClose(rfSlice, [-0.2, -0.3, -0.4])); - - rf = iota(0.0, nextDown(-0.5), -0.1); - assert(isClose(rf, [0.0, -0.1, -0.2, -0.3, -0.4, -0.5][])); - - // iota of longs - auto rl = iota(5_000_000L); - assert(rl.length == 5_000_000L); - assert(0 in rl); - assert(4_000_000L in rl); - assert(!(-4_000_000L in rl)); - assert(!(5_000_000L in rl)); - - // iota of longs with steps - auto iota_of_longs_with_steps = iota(50L, 101L, 10); - assert(iota_of_longs_with_steps.length == 6); - assert(equal(iota_of_longs_with_steps, [50L, 60L, 70L, 80L, 90L, 100L])); - - // iota of unsigned zero length (https://issues.dlang.org/show_bug.cgi?id=6222) - // Actually trying to consume it is the only way to find something is wrong - // because the public properties are all correct. - auto iota_zero_unsigned = iota(0, 0u, 3); - assert(count(iota_zero_unsigned) == 0); - - // https://issues.dlang.org/show_bug.cgi?id=7982 - // unsigned reverse iota can be buggy if `.length` doesn't - // take them into account - assert(iota(10u, 0u, -1).length == 10); - assert(iota(10u, 0u, -2).length == 5); - assert(iota(uint.max, uint.max-10, -1).length == 10); - assert(iota(uint.max, uint.max-10, -2).length == 5); - assert(iota(uint.max, 0u, -1).length == uint.max); - - assert(20 in iota(20u, 10u, -2)); - assert(16 in iota(20u, 10u, -2)); - assert(!(15 in iota(20u, 10u, -2))); - assert(!(10 in iota(20u, 10u, -2))); - assert(!(uint.max in iota(20u, 10u, -1))); - assert(!(int.min in iota(20u, 10u, -1))); - assert(!(int.max in iota(20u, 10u, -1))); - - - // https://issues.dlang.org/show_bug.cgi?id=8920 - static foreach (Type; AliasSeq!(byte, ubyte, short, ushort, - int, uint, long, ulong)) - {{ - Type val; - foreach (i; iota(cast(Type) 0, cast(Type) 10)) { val++; } - assert(val == 10); - }} -} - -pure @safe nothrow unittest -{ - import std.algorithm.mutation : copy; - auto idx = new size_t[100]; - copy(iota(0, idx.length), idx); -} - -@safe unittest -{ - import std.meta : AliasSeq; - static foreach (range; AliasSeq!(iota(2, 27, 4), - iota(3, 9), - iota(2.7, 12.3, .1), - iota(3.2, 9.7))) - {{ - const cRange = range; - const e = cRange.empty; - const f = cRange.front; - const b = cRange.back; - const i = cRange[2]; - const s1 = cRange[]; - const s2 = cRange[0 .. 3]; - const l = cRange.length; - }} -} - -@system unittest -{ - //The ptr stuff can't be done at compile time, so we unfortunately end - //up with some code duplication here. - auto arr = [0, 5, 3, 5, 5, 7, 9, 2, 0, 42, 7, 6]; - - { - const cRange = iota(arr.ptr, arr.ptr + arr.length, 3); - const e = cRange.empty; - const f = cRange.front; - const b = cRange.back; - const i = cRange[2]; - const s1 = cRange[]; - const s2 = cRange[0 .. 3]; - const l = cRange.length; - } - - { - const cRange = iota(arr.ptr, arr.ptr + arr.length); - const e = cRange.empty; - const f = cRange.front; - const b = cRange.back; - const i = cRange[2]; - const s1 = cRange[]; - const s2 = cRange[0 .. 3]; - const l = cRange.length; - } -} - -@nogc nothrow pure @safe unittest -{ - { - ushort start = 0, end = 10, step = 2; - foreach (i; iota(start, end, step)) - static assert(is(typeof(i) == ushort)); - } - { - ubyte start = 0, end = 255, step = 128; - uint x; - foreach (i; iota(start, end, step)) - { - static assert(is(typeof(i) == ubyte)); - ++x; - } - assert(x == 2); - } -} - -/* Generic overload that handles arbitrary types that support arithmetic - * operations. - * - * User-defined types such as $(REF BigInt, std,bigint) are also supported, as long - * as they can be incremented with `++` and compared with `<` or `==`. - */ -/// ditto -auto iota(B, E)(B begin, E end) -if (!isIntegral!(CommonType!(B, E)) && - !isFloatingPoint!(CommonType!(B, E)) && - !isPointer!(CommonType!(B, E)) && - is(typeof((ref B b) { ++b; })) && - (is(typeof(B.init < E.init)) || is(typeof(B.init == E.init))) ) -{ - static struct Result - { - B current; - E end; - - @property bool empty() - { - static if (is(typeof(B.init < E.init))) - return !(current < end); - else static if (is(typeof(B.init != E.init))) - return current == end; - else - static assert(0); - } - @property auto front() { return current; } - void popFront() - { - assert(!empty); - ++current; - } - @property auto save() { return this; } - } - return Result(begin, end); -} - -@safe unittest -{ - import std.algorithm.comparison : equal; - - // Test iota() for a type that only supports ++ and != but does not have - // '<'-ordering. - struct Cyclic(int wrapAround) - { - int current; - - this(int start) { current = start % wrapAround; } - - bool opEquals(Cyclic c) const { return current == c.current; } - bool opEquals(int i) const { return current == i; } - void opUnary(string op)() if (op == "++") - { - current = (current + 1) % wrapAround; - } - } - alias Cycle5 = Cyclic!5; - - // Easy case - auto i1 = iota(Cycle5(1), Cycle5(4)); - assert(i1.equal([1, 2, 3])); - - // Wraparound case - auto i2 = iota(Cycle5(3), Cycle5(2)); - assert(i2.equal([3, 4, 0, 1 ])); -} - -// https://issues.dlang.org/show_bug.cgi?id=23453 -@safe unittest -{ - auto r = iota('a', 'z'); - static assert(isForwardRange!(typeof(r))); -} - -/** - Options for the $(LREF FrontTransversal) and $(LREF Transversal) ranges - (below). -*/ -enum TransverseOptions -{ -/** - When transversed, the elements of a range of ranges are assumed to - have different lengths (e.g. a jagged array). -*/ - assumeJagged, //default - /** - The transversal enforces that the elements of a range of ranges have - all the same length (e.g. an array of arrays, all having the same - length). Checking is done once upon construction of the transversal - range. - */ - enforceNotJagged, - /** - The transversal assumes, without verifying, that the elements of a - range of ranges have all the same length. This option is useful if - checking was already done from the outside of the range. - */ - assumeNotJagged, -} - -/// -@safe pure unittest -{ - import std.algorithm.comparison : equal; - import std.exception : assertThrown; - - auto arr = [[1, 2], [3, 4, 5]]; - - auto r1 = arr.frontTransversal!(TransverseOptions.assumeJagged); - assert(r1.equal([1, 3])); - - // throws on construction - assertThrown!Exception(arr.frontTransversal!(TransverseOptions.enforceNotJagged)); - - auto r2 = arr.frontTransversal!(TransverseOptions.assumeNotJagged); - assert(r2.equal([1, 3])); - - // either assuming or checking for equal lengths makes - // the result a random access range - assert(r2[0] == 1); - static assert(!__traits(compiles, r1[0])); -} - -/** - Given a range of ranges, iterate transversally through the first - elements of each of the enclosed ranges. -*/ -struct FrontTransversal(Ror, - TransverseOptions opt = TransverseOptions.assumeJagged) -{ - alias RangeOfRanges = Unqual!(Ror); - alias RangeType = .ElementType!RangeOfRanges; - alias ElementType = .ElementType!RangeType; - - private void prime() - { - static if (opt == TransverseOptions.assumeJagged) - { - while (!_input.empty && _input.front.empty) - { - _input.popFront(); - } - static if (isBidirectionalRange!RangeOfRanges) - { - while (!_input.empty && _input.back.empty) - { - _input.popBack(); - } - } - } - } - -/** - Construction from an input. -*/ - this(RangeOfRanges input) - { - _input = input; - prime(); - static if (opt == TransverseOptions.enforceNotJagged) - // (isRandomAccessRange!RangeOfRanges - // && hasLength!RangeType) - { - import std.exception : enforce; - - if (empty) return; - immutable commonLength = _input.front.length; - foreach (e; _input) - { - enforce(e.length == commonLength); - } - } - } - -/** - Forward range primitives. -*/ - static if (isInfinite!RangeOfRanges) - { - enum bool empty = false; - } - else - { - @property bool empty() - { - static if (opt != TransverseOptions.assumeJagged) - { - if (!_input.empty) - return _input.front.empty; - } - - return _input.empty; - } - } - - /// Ditto - @property auto ref front() - { - assert(!empty, "Attempting to fetch the front of an empty FrontTransversal"); - return _input.front.front; - } - - /// Ditto - static if (hasMobileElements!RangeType) - { - ElementType moveFront() - { - return _input.front.moveFront(); - } - } - - static if (hasAssignableElements!RangeType) - { - @property void front(ElementType val) - { - import core.lifetime : forward; - - // __ctfe check is workaround for https://issues.dlang.org/show_bug.cgi?id=21542 - _input.front.front = __ctfe ? val : forward!val; - } - } - - /// Ditto - void popFront() - { - assert(!empty, "Attempting to popFront an empty FrontTransversal"); - _input.popFront(); - prime(); - } - -/** - Duplicates this `frontTransversal`. Note that only the encapsulating - range of range will be duplicated. Underlying ranges will not be - duplicated. -*/ - static if (isForwardRange!RangeOfRanges) - { - @property FrontTransversal save() - { - return FrontTransversal(_input.save); - } - } - - static if (isBidirectionalRange!RangeOfRanges) - { -/** - Bidirectional primitives. They are offered if $(D - isBidirectionalRange!RangeOfRanges). -*/ - @property auto ref back() - { - assert(!empty, "Attempting to fetch the back of an empty FrontTransversal"); - return _input.back.front; - } - /// Ditto - void popBack() - { - assert(!empty, "Attempting to popBack an empty FrontTransversal"); - _input.popBack(); - prime(); - } - - /// Ditto - static if (hasMobileElements!RangeType) - { - ElementType moveBack() - { - return _input.back.moveFront(); - } - } - - static if (hasAssignableElements!RangeType) - { - @property void back(ElementType val) - { - import core.lifetime : forward; - - // __ctfe check is workaround for https://issues.dlang.org/show_bug.cgi?id=21542 - _input.back.front = __ctfe ? val : forward!val; - } - } - } - - static if (isRandomAccessRange!RangeOfRanges && - (opt == TransverseOptions.assumeNotJagged || - opt == TransverseOptions.enforceNotJagged)) - { -/** - Random-access primitive. It is offered if $(D - isRandomAccessRange!RangeOfRanges && (opt == - TransverseOptions.assumeNotJagged || opt == - TransverseOptions.enforceNotJagged)). -*/ - auto ref opIndex(size_t n) - { - return _input[n].front; - } - - /// Ditto - static if (hasMobileElements!RangeType) - { - ElementType moveAt(size_t n) - { - return _input[n].moveFront(); - } - } - /// Ditto - static if (hasAssignableElements!RangeType) - { - void opIndexAssign(ElementType val, size_t n) - { - import core.lifetime : forward; - - // __ctfe check is workaround for https://issues.dlang.org/show_bug.cgi?id=21542 - _input[n].front = __ctfe ? val : forward!val; - } - } - mixin ImplementLength!_input; - -/** - Slicing if offered if `RangeOfRanges` supports slicing and all the - conditions for supporting indexing are met. -*/ - static if (hasSlicing!RangeOfRanges) - { - typeof(this) opSlice(size_t lower, size_t upper) - { - return typeof(this)(_input[lower .. upper]); - } - } - } - - auto opSlice() { return this; } - -private: - RangeOfRanges _input; -} - -/// Ditto -FrontTransversal!(RangeOfRanges, opt) frontTransversal( - TransverseOptions opt = TransverseOptions.assumeJagged, - RangeOfRanges) -(RangeOfRanges rr) -{ - return typeof(return)(rr); -} - -/// -pure @safe nothrow unittest -{ - import std.algorithm.comparison : equal; - int[][] x = new int[][2]; - x[0] = [1, 2]; - x[1] = [3, 4]; - auto ror = frontTransversal(x); - assert(equal(ror, [ 1, 3 ][])); -} - -@safe unittest -{ - import std.algorithm.comparison : equal; - import std.internal.test.dummyrange : AllDummyRanges, DummyRange, ReturnBy; - - static assert(is(FrontTransversal!(immutable int[][]))); - - foreach (DummyType; AllDummyRanges) - { - auto dummies = - [DummyType.init, DummyType.init, DummyType.init, DummyType.init]; - - foreach (i, ref elem; dummies) - { - // Just violate the DummyRange abstraction to get what I want. - elem.arr = elem.arr[i..$ - (3 - i)]; - } - - auto ft = frontTransversal!(TransverseOptions.assumeNotJagged)(dummies); - static if (isForwardRange!DummyType) - { - static assert(isForwardRange!(typeof(ft))); - } - - assert(equal(ft, [1, 2, 3, 4])); - - // Test slicing. - assert(equal(ft[0 .. 2], [1, 2])); - assert(equal(ft[1 .. 3], [2, 3])); - - assert(ft.front == ft.moveFront()); - assert(ft.back == ft.moveBack()); - assert(ft.moveAt(1) == ft[1]); - - - // Test infiniteness propagation. - static assert(isInfinite!(typeof(frontTransversal(repeat("foo"))))); - - static if (DummyType.r == ReturnBy.Reference) - { - { - ft.front++; - scope(exit) ft.front--; - assert(dummies.front.front == 2); - } - - { - ft.front = 5; - scope(exit) ft.front = 1; - assert(dummies[0].front == 5); - } - - { - ft.back = 88; - scope(exit) ft.back = 4; - assert(dummies.back.front == 88); - } - - { - ft[1] = 99; - scope(exit) ft[1] = 2; - assert(dummies[1].front == 99); - } - } - } -} - -// https://issues.dlang.org/show_bug.cgi?id=16363 -pure @safe nothrow unittest -{ - import std.algorithm.comparison : equal; - - int[][] darr = [[0, 1], [4, 5]]; - auto ft = frontTransversal!(TransverseOptions.assumeNotJagged)(darr); - - assert(equal(ft, [0, 4])); - static assert(isRandomAccessRange!(typeof(ft))); -} - -// https://issues.dlang.org/show_bug.cgi?id=16442 -pure @safe nothrow unittest -{ - int[][] arr = [[], []]; - - auto ft = frontTransversal!(TransverseOptions.assumeNotJagged)(arr); - assert(ft.empty); -} - -// ditto -pure @safe unittest -{ - int[][] arr = [[], []]; - - auto ft = frontTransversal!(TransverseOptions.enforceNotJagged)(arr); - assert(ft.empty); -} - -// https://issues.dlang.org/show_bug.cgi?id=24481 -@safe unittest -{ - bool called; - struct Handle - { - int entry; - void opAssign()(auto ref const(typeof(this)) that) const { called = true; } - } - - const(Handle)[][] arr = [[Handle(0), Handle(10)], - [Handle(1), Handle(11)], - [Handle(2), Handle(12)], - [Handle(3), Handle(13)], - [Handle(4), Handle(14)]]; - - { - auto range = arr.frontTransversal(); - - called = false; - range.front = Handle(42); - assert(called == true); - - called = false; - range.back = Handle(42); - assert(called == true); - } - { - auto range = arr.frontTransversal!(TransverseOptions.assumeNotJagged)(); - - called = false; - range.front = Handle(42); - assert(called == true); - - called = false; - range.back = Handle(42); - assert(called == true); - - called = false; - range[0] = Handle(42); - assert(called == true); - } -} - -/** - Given a range of ranges, iterate transversally through the - `n`th element of each of the enclosed ranges. This function - is similar to `unzip` in other languages. - - Params: - opt = Controls the assumptions the function makes about the lengths - of the ranges - rr = An input range of random access ranges - Returns: - At minimum, an input range. Range primitives such as bidirectionality - and random access are given if the element type of `rr` provides them. -*/ -struct Transversal(Ror, - TransverseOptions opt = TransverseOptions.assumeJagged) -{ - private alias RangeOfRanges = Unqual!Ror; - private alias InnerRange = ElementType!RangeOfRanges; - private alias E = ElementType!InnerRange; - - private void prime() - { - static if (opt == TransverseOptions.assumeJagged) - { - while (!_input.empty && _input.front.length <= _n) - { - _input.popFront(); - } - static if (isBidirectionalRange!RangeOfRanges) - { - while (!_input.empty && _input.back.length <= _n) - { - _input.popBack(); - } - } - } - } - -/** - Construction from an input and an index. -*/ - this(RangeOfRanges input, size_t n) - { - _input = input; - _n = n; - prime(); - static if (opt == TransverseOptions.enforceNotJagged) - { - import std.exception : enforce; - - if (empty) return; - immutable commonLength = _input.front.length; - foreach (e; _input) - { - enforce(e.length == commonLength); - } - } - } - -/** - Forward range primitives. -*/ - static if (isInfinite!(RangeOfRanges)) - { - enum bool empty = false; - } - else - { - @property bool empty() - { - return _input.empty; - } - } - - /// Ditto - @property auto ref front() - { - assert(!empty, "Attempting to fetch the front of an empty Transversal"); - return _input.front[_n]; - } - - /// Ditto - static if (hasMobileElements!InnerRange) - { - E moveFront() - { - return _input.front.moveAt(_n); - } - } - - /// Ditto - static if (hasAssignableElements!InnerRange) - { - @property void front(E val) - { - _input.front[_n] = val; - } - } - - - /// Ditto - void popFront() - { - assert(!empty, "Attempting to popFront an empty Transversal"); - _input.popFront(); - prime(); - } - - /// Ditto - static if (isForwardRange!RangeOfRanges) - { - @property typeof(this) save() - { - auto ret = this; - ret._input = _input.save; - return ret; - } - } - - static if (isBidirectionalRange!RangeOfRanges) - { -/** - Bidirectional primitives. They are offered if $(D - isBidirectionalRange!RangeOfRanges). -*/ - @property auto ref back() - { - assert(!empty, "Attempting to fetch the back of an empty Transversal"); - return _input.back[_n]; - } - - /// Ditto - void popBack() - { - assert(!empty, "Attempting to popBack an empty Transversal"); - _input.popBack(); - prime(); - } - - /// Ditto - static if (hasMobileElements!InnerRange) - { - E moveBack() - { - return _input.back.moveAt(_n); - } - } - - /// Ditto - static if (hasAssignableElements!InnerRange) - { - @property void back(E val) - { - _input.back[_n] = val; - } - } - - } - - static if (isRandomAccessRange!RangeOfRanges && - (opt == TransverseOptions.assumeNotJagged || - opt == TransverseOptions.enforceNotJagged)) - { -/** - Random-access primitive. It is offered if $(D - isRandomAccessRange!RangeOfRanges && (opt == - TransverseOptions.assumeNotJagged || opt == - TransverseOptions.enforceNotJagged)). -*/ - auto ref opIndex(size_t n) - { - return _input[n][_n]; - } - - /// Ditto - static if (hasMobileElements!InnerRange) - { - E moveAt(size_t n) - { - return _input[n].moveAt(_n); - } - } - - /// Ditto - static if (hasAssignableElements!InnerRange) - { - void opIndexAssign(E val, size_t n) - { - _input[n][_n] = val; - } - } - - mixin ImplementLength!_input; - -/** - Slicing if offered if `RangeOfRanges` supports slicing and all the - conditions for supporting indexing are met. -*/ - static if (hasSlicing!RangeOfRanges) - { - typeof(this) opSlice(size_t lower, size_t upper) - { - return typeof(this)(_input[lower .. upper], _n); - } - } - } - - auto opSlice() { return this; } - -private: - RangeOfRanges _input; - size_t _n; -} - -/// Ditto -Transversal!(RangeOfRanges, opt) transversal -(TransverseOptions opt = TransverseOptions.assumeJagged, RangeOfRanges) -(RangeOfRanges rr, size_t n) -{ - return typeof(return)(rr, n); -} - -/// -@safe unittest -{ - import std.algorithm.comparison : equal; - int[][] x = new int[][2]; - x[0] = [1, 2]; - x[1] = [3, 4]; - auto ror = transversal(x, 1); - assert(equal(ror, [ 2, 4 ])); -} - -/// The following code does a full unzip -@safe unittest -{ - import std.algorithm.comparison : equal; - import std.algorithm.iteration : map; - int[][] y = [[1, 2, 3], [4, 5, 6]]; - auto z = y.front.walkLength.iota.map!(i => transversal(y, i)); - assert(equal!equal(z, [[1, 4], [2, 5], [3, 6]])); -} - -@safe unittest -{ - import std.internal.test.dummyrange : DummyRange, Length, RangeType, ReturnBy; - - int[][] x = new int[][2]; - x[0] = [ 1, 2 ]; - x[1] = [3, 4]; - auto ror = transversal!(TransverseOptions.assumeNotJagged)(x, 1); - auto witness = [ 2, 4 ]; - uint i; - foreach (e; ror) assert(e == witness[i++]); - assert(i == 2); - assert(ror.length == 2); - - static assert(is(Transversal!(immutable int[][]))); - - // Make sure ref, assign is being propagated. - { - ror.front++; - scope(exit) ror.front--; - assert(x[0][1] == 3); - } - { - ror.front = 5; - scope(exit) ror.front = 2; - assert(x[0][1] == 5); - assert(ror.moveFront() == 5); - } - { - ror.back = 999; - scope(exit) ror.back = 4; - assert(x[1][1] == 999); - assert(ror.moveBack() == 999); - } - { - ror[0] = 999; - scope(exit) ror[0] = 2; - assert(x[0][1] == 999); - assert(ror.moveAt(0) == 999); - } - - // Test w/o ref return. - alias D = DummyRange!(ReturnBy.Value, Length.Yes, RangeType.Random); - auto drs = [D.init, D.init]; - foreach (num; 0 .. 10) - { - auto t = transversal!(TransverseOptions.enforceNotJagged)(drs, num); - assert(t[0] == t[1]); - assert(t[1] == num + 1); - } - - static assert(isInfinite!(typeof(transversal(repeat([1,2,3]), 1)))); - - // Test slicing. - auto mat = [[1, 2, 3, 4], [5, 6, 7, 8], [9, 10, 11, 12], [13, 14, 15, 16]]; - auto mat1 = transversal!(TransverseOptions.assumeNotJagged)(mat, 1)[1 .. 3]; - assert(mat1[0] == 6); - assert(mat1[1] == 10); -} - -struct Transposed(RangeOfRanges, - TransverseOptions opt = TransverseOptions.assumeJagged) -if (isForwardRange!RangeOfRanges && - isInputRange!(ElementType!RangeOfRanges) && - hasAssignableElements!RangeOfRanges) -{ - this(RangeOfRanges input) - { - this._input = input; - static if (opt == TransverseOptions.enforceNotJagged) - { - import std.exception : enforce; - - if (empty) return; - immutable commonLength = _input.front.length; - foreach (e; _input) - { - enforce(e.length == commonLength); - } - } - } - - @property auto front() - { - import std.algorithm.iteration : filter, map; - return _input.save - .filter!(a => !a.empty) - .map!(a => a.front); - } - - void popFront() - { - // Advance the position of each subrange. - auto r = _input.save; - while (!r.empty) - { - auto e = r.front; - if (!e.empty) - { - e.popFront(); - r.front = e; - } - - r.popFront(); - } - } - - static if (isRandomAccessRange!(ElementType!RangeOfRanges)) - { - auto ref opIndex(size_t n) - { - return transversal!opt(_input, n); - } - } - - @property bool empty() - { - if (_input.empty) return true; - foreach (e; _input.save) - { - if (!e.empty) return false; - } - return true; - } - - auto opSlice() { return this; } - -private: - RangeOfRanges _input; -} - -@safe unittest -{ - // Boundary case: transpose of empty range should be empty - int[][] ror = []; - assert(transposed(ror).empty); -} - -// https://issues.dlang.org/show_bug.cgi?id=9507 -@safe unittest -{ - import std.algorithm.comparison : equal; - - auto r = [[1,2], [3], [4,5], [], [6]]; - assert(r.transposed.equal!equal([ - [1, 3, 4, 6], - [2, 5] - ])); -} - -// https://issues.dlang.org/show_bug.cgi?id=17742 -@safe unittest -{ - import std.algorithm.iteration : map; - import std.algorithm.comparison : equal; - auto ror = 5.iota.map!(y => 5.iota.map!(x => x * y).array).array; - assert(ror[3][2] == 6); - auto result = transposed!(TransverseOptions.assumeNotJagged)(ror); - assert(result[2][3] == 6); - - auto x = [[1,2,3],[4,5,6]]; - auto y = transposed!(TransverseOptions.assumeNotJagged)(x); - assert(y.front.equal([1,4])); - assert(y[0].equal([1,4])); - assert(y[0][0] == 1); - assert(y[1].equal([2,5])); - assert(y[1][1] == 5); - - auto yy = transposed!(TransverseOptions.enforceNotJagged)(x); - assert(yy.front.equal([1,4])); - assert(yy[0].equal([1,4])); - assert(yy[0][0] == 1); - assert(yy[1].equal([2,5])); - assert(yy[1][1] == 5); - - auto z = x.transposed; // assumeJagged - assert(z.front.equal([1,4])); - assert(z[0].equal([1,4])); - assert(!is(typeof(z[0][0]))); -} - -@safe unittest -{ - import std.exception : assertThrown; - - auto r = [[1,2], [3], [4,5], [], [6]]; - assertThrown(r.transposed!(TransverseOptions.enforceNotJagged)); -} - -/** -Given a range of ranges, returns a range of ranges where the $(I i)'th subrange -contains the $(I i)'th elements of the original subranges. - -Params: - opt = Controls the assumptions the function makes about the lengths of the ranges (i.e. jagged or not) - rr = Range of ranges - */ -Transposed!(RangeOfRanges, opt) transposed -(TransverseOptions opt = TransverseOptions.assumeJagged, RangeOfRanges) -(RangeOfRanges rr) -if (isForwardRange!RangeOfRanges && - isInputRange!(ElementType!RangeOfRanges) && - hasAssignableElements!RangeOfRanges) -{ - return Transposed!(RangeOfRanges, opt)(rr); -} - -/// -@safe unittest -{ - import std.algorithm.comparison : equal; - int[][] ror = [ - [1, 2, 3], - [4, 5, 6] - ]; - auto xp = transposed(ror); - assert(equal!"a.equal(b)"(xp, [ - [1, 4], - [2, 5], - [3, 6] - ])); -} - -/// -@safe unittest -{ - int[][] x = new int[][2]; - x[0] = [1, 2]; - x[1] = [3, 4]; - auto tr = transposed(x); - int[][] witness = [ [ 1, 3 ], [ 2, 4 ] ]; - uint i; - - foreach (e; tr) - { - assert(array(e) == witness[i++]); - } -} - -// https://issues.dlang.org/show_bug.cgi?id=8764 -@safe unittest -{ - import std.algorithm.comparison : equal; - ulong[] t0 = [ 123 ]; - - assert(!hasAssignableElements!(typeof(t0[].chunks(1)))); - assert(!is(typeof(transposed(t0[].chunks(1))))); - assert(is(typeof(transposed(t0[].chunks(1).array())))); - - auto t1 = transposed(t0[].chunks(1).array()); - assert(equal!"a.equal(b)"(t1, [[123]])); -} - -/** -This struct takes two ranges, `source` and `indices`, and creates a view -of `source` as if its elements were reordered according to `indices`. -`indices` may include only a subset of the elements of `source` and -may also repeat elements. - -`Source` must be a random access range. The returned range will be -bidirectional or random-access if `Indices` is bidirectional or -random-access, respectively. -*/ -struct Indexed(Source, Indices) -if (isRandomAccessRange!Source && isInputRange!Indices && - is(typeof(Source.init[ElementType!(Indices).init]))) -{ - this(Source source, Indices indices) - { - this._source = source; - this._indices = indices; - } - - /// Range primitives - @property auto ref front() - { - assert(!empty, "Attempting to fetch the front of an empty Indexed"); - return _source[_indices.front]; - } - - /// Ditto - void popFront() - { - assert(!empty, "Attempting to popFront an empty Indexed"); - _indices.popFront(); - } - - static if (isInfinite!Indices) - { - enum bool empty = false; - } - else - { - /// Ditto - @property bool empty() - { - return _indices.empty; - } - } - - static if (isForwardRange!Indices) - { - /// Ditto - @property typeof(this) save() - { - // Don't need to save _source because it's never consumed. - return typeof(this)(_source, _indices.save); - } - } - - /// Ditto - static if (hasAssignableElements!Source) - { - @property auto ref front(ElementType!Source newVal) - { - assert(!empty); - return _source[_indices.front] = newVal; - } - } - - - static if (hasMobileElements!Source) - { - /// Ditto - auto moveFront() - { - assert(!empty); - return _source.moveAt(_indices.front); - } - } - - static if (isBidirectionalRange!Indices) - { - /// Ditto - @property auto ref back() - { - assert(!empty, "Attempting to fetch the back of an empty Indexed"); - return _source[_indices.back]; - } - - /// Ditto - void popBack() - { - assert(!empty, "Attempting to popBack an empty Indexed"); - _indices.popBack(); - } - - /// Ditto - static if (hasAssignableElements!Source) - { - @property auto ref back(ElementType!Source newVal) - { - assert(!empty); - return _source[_indices.back] = newVal; - } - } - - - static if (hasMobileElements!Source) - { - /// Ditto - auto moveBack() - { - assert(!empty); - return _source.moveAt(_indices.back); - } - } - } - - mixin ImplementLength!_indices; - - static if (isRandomAccessRange!Indices) - { - /// Ditto - auto ref opIndex(size_t index) - { - return _source[_indices[index]]; - } - - static if (hasSlicing!Indices) - { - /// Ditto - typeof(this) opSlice(size_t a, size_t b) - { - return typeof(this)(_source, _indices[a .. b]); - } - } - - - static if (hasAssignableElements!Source) - { - /// Ditto - auto opIndexAssign(ElementType!Source newVal, size_t index) - { - return _source[_indices[index]] = newVal; - } - } - - - static if (hasMobileElements!Source) - { - /// Ditto - auto moveAt(size_t index) - { - return _source.moveAt(_indices[index]); - } - } - } - - // All this stuff is useful if someone wants to index an Indexed - // without adding a layer of indirection. - - /** - Returns the source range. - */ - @property Source source() - { - return _source; - } - - /** - Returns the indices range. - */ - @property Indices indices() - { - return _indices; - } - - static if (isRandomAccessRange!Indices) - { - /** - Returns the physical index into the source range corresponding to a - given logical index. This is useful, for example, when indexing - an `Indexed` without adding another layer of indirection. - */ - size_t physicalIndex(size_t logicalIndex) - { - return _indices[logicalIndex]; - } - - /// - @safe unittest - { - auto ind = indexed([1, 2, 3, 4, 5], [1, 3, 4]); - assert(ind.physicalIndex(0) == 1); - } - } - -private: - Source _source; - Indices _indices; - -} - -/// Ditto -Indexed!(Source, Indices) indexed(Source, Indices)(Source source, Indices indices) -{ - return typeof(return)(source, indices); -} - -/// -@safe unittest -{ - import std.algorithm.comparison : equal; - auto source = [1, 2, 3, 4, 5]; - auto indices = [4, 3, 1, 2, 0, 4]; - auto ind = indexed(source, indices); - assert(equal(ind, [5, 4, 2, 3, 1, 5])); - assert(equal(retro(ind), [5, 1, 3, 2, 4, 5])); -} - -@safe unittest -{ - { - auto ind = indexed([1, 2, 3, 4, 5], [1, 3, 4]); - assert(ind.physicalIndex(0) == 1); - } - - auto source = [1, 2, 3, 4, 5]; - auto indices = [4, 3, 1, 2, 0, 4]; - auto ind = indexed(source, indices); - - // When elements of indices are duplicated and Source has lvalue elements, - // these are aliased in ind. - ind[0]++; - assert(ind[0] == 6); - assert(ind[5] == 6); -} - -@safe unittest -{ - import std.internal.test.dummyrange : AllDummyRanges, propagatesLength, - propagatesRangeType, RangeType; - - foreach (DummyType; AllDummyRanges) - { - auto d = DummyType.init; - auto r = indexed([1, 2, 3, 4, 5], d); - static assert(propagatesRangeType!(DummyType, typeof(r))); - static assert(propagatesLength!(DummyType, typeof(r))); - } -} - -/** -This range iterates over fixed-sized chunks of size `chunkSize` of a -`source` range. `Source` must be an $(REF_ALTTEXT input range, isInputRange, std,range,primitives). -`chunkSize` must be greater than zero. - -If `!isInfinite!Source` and `source.walkLength` is not evenly -divisible by `chunkSize`, the back element of this range will contain -fewer than `chunkSize` elements. - -If `Source` is a forward range, the resulting range will be forward ranges as -well. Otherwise, the resulting chunks will be input ranges consuming the same -input: iterating over `front` will shrink the chunk such that subsequent -invocations of `front` will no longer return the full chunk, and calling -`popFront` on the outer range will invalidate any lingering references to -previous values of `front`. - -Params: - source = Range from which the chunks will be selected - chunkSize = Chunk size - -See_Also: $(LREF slide) - -Returns: Range of chunks. -*/ -struct Chunks(Source) -if (isInputRange!Source) -{ - static if (isForwardRange!Source) - { - /// Standard constructor - this(Source source, size_t chunkSize) - { - assert(chunkSize != 0, "Cannot create a Chunk with an empty chunkSize"); - _source = source; - _chunkSize = chunkSize; - } - - /// Input range primitives. Always present. - @property auto front() - { - assert(!empty, "Attempting to fetch the front of an empty Chunks"); - return _source.save.take(_chunkSize); - } - - /// Ditto - void popFront() - { - assert(!empty, "Attempting to popFront and empty Chunks"); - _source.popFrontN(_chunkSize); - } - - static if (!isInfinite!Source) - /// Ditto - @property bool empty() - { - return _source.empty; - } - else - // undocumented - enum empty = false; - - /// Forward range primitives. Only present if `Source` is a forward range. - @property typeof(this) save() - { - return typeof(this)(_source.save, _chunkSize); - } - - static if (hasLength!Source) - { - /// Length. Only if `hasLength!Source` is `true` - @property size_t length() - { - // Note: _source.length + _chunkSize may actually overflow. - // We cast to ulong to mitigate the problem on x86 machines. - // For x64 machines, we just suppose we'll never overflow. - // The "safe" code would require either an extra branch, or a - // modulo operation, which is too expensive for such a rare case - return cast(size_t)((cast(ulong)(_source.length) + _chunkSize - 1) / _chunkSize); - } - //Note: No point in defining opDollar here without slicing. - //opDollar is defined below in the hasSlicing!Source section - } - - static if (hasSlicing!Source) - { - //Used for various purposes - private enum hasSliceToEnd = is(typeof(Source.init[_chunkSize .. $]) == Source); - - /** - Indexing and slicing operations. Provided only if - `hasSlicing!Source` is `true`. - */ - auto opIndex(size_t index) - { - immutable start = index * _chunkSize; - immutable end = start + _chunkSize; - - static if (isInfinite!Source) - return _source[start .. end]; - else - { - import std.algorithm.comparison : min; - immutable len = _source.length; - assert(start < len, "chunks index out of bounds"); - return _source[start .. min(end, len)]; - } - } - - /// Ditto - static if (hasLength!Source) - typeof(this) opSlice(size_t lower, size_t upper) - { - import std.algorithm.comparison : min; - assert(lower <= upper && upper <= length, "chunks slicing index out of bounds"); - immutable len = _source.length; - return chunks(_source[min(lower * _chunkSize, len) .. min(upper * _chunkSize, len)], _chunkSize); - } - else static if (hasSliceToEnd) - //For slicing an infinite chunk, we need to slice the source to the end. - typeof(takeExactly(this, 0)) opSlice(size_t lower, size_t upper) - { - assert(lower <= upper, "chunks slicing index out of bounds"); - return chunks(_source[lower * _chunkSize .. $], _chunkSize).takeExactly(upper - lower); - } - - static if (isInfinite!Source) - { - static if (hasSliceToEnd) - { - private static struct DollarToken{} - DollarToken opDollar() - { - return DollarToken(); - } - //Slice to dollar - typeof(this) opSlice(size_t lower, DollarToken) - { - return typeof(this)(_source[lower * _chunkSize .. $], _chunkSize); - } - } - } - else - { - //Dollar token carries a static type, with no extra information. - //It can lazily transform into _source.length on algorithmic - //operations such as : chunks[$/2, $-1]; - private static struct DollarToken - { - Chunks!Source* mom; - @property size_t momLength() - { - return mom.length; - } - alias momLength this; - } - DollarToken opDollar() - { - return DollarToken(&this); - } - - //Slice overloads optimized for using dollar. Without this, to slice to end, we would... - //1. Evaluate chunks.length - //2. Multiply by _chunksSize - //3. To finally just compare it (with min) to the original length of source (!) - //These overloads avoid that. - typeof(this) opSlice(DollarToken, DollarToken) - { - static if (hasSliceToEnd) - return chunks(_source[$ .. $], _chunkSize); - else - { - immutable len = _source.length; - return chunks(_source[len .. len], _chunkSize); - } - } - typeof(this) opSlice(size_t lower, DollarToken) - { - import std.algorithm.comparison : min; - assert(lower <= length, "chunks slicing index out of bounds"); - static if (hasSliceToEnd) - return chunks(_source[min(lower * _chunkSize, _source.length) .. $], _chunkSize); - else - { - immutable len = _source.length; - return chunks(_source[min(lower * _chunkSize, len) .. len], _chunkSize); - } - } - typeof(this) opSlice(DollarToken, size_t upper) - { - assert(upper == length, "chunks slicing index out of bounds"); - return this[$ .. $]; - } - } - } - - //Bidirectional range primitives - static if (hasSlicing!Source && hasLength!Source) - { - /** - Bidirectional range primitives. Provided only if both - `hasSlicing!Source` and `hasLength!Source` are `true`. - */ - @property auto back() - { - assert(!empty, "back called on empty chunks"); - immutable len = _source.length; - immutable start = (len - 1) / _chunkSize * _chunkSize; - return _source[start .. len]; - } - - /// Ditto - void popBack() - { - assert(!empty, "popBack() called on empty chunks"); - immutable end = (_source.length - 1) / _chunkSize * _chunkSize; - _source = _source[0 .. end]; - } - } - - private: - Source _source; - size_t _chunkSize; - } - else // is input range only - { - import std.typecons : RefCounted; - - static struct Chunk - { - private RefCounted!Impl impl; - - @property bool empty() { return impl.curSizeLeft == 0 || impl.r.empty; } - @property auto front() { return impl.r.front; } - void popFront() - { - assert(impl.curSizeLeft > 0 && !impl.r.empty); - impl.curSizeLeft--; - impl.r.popFront(); - } - } - - static struct Impl - { - private Source r; - private size_t chunkSize; - private size_t curSizeLeft; - } - - private RefCounted!Impl impl; - - private this(Source r, size_t chunkSize) - { - impl = RefCounted!Impl(r, r.empty ? 0 : chunkSize, chunkSize); - } - - @property bool empty() { return impl.chunkSize == 0; } - @property Chunk front() return { return Chunk(impl); } - - void popFront() - { - impl.curSizeLeft -= impl.r.popFrontN(impl.curSizeLeft); - if (!impl.r.empty) - impl.curSizeLeft = impl.chunkSize; - else - impl.chunkSize = 0; - } - - static assert(isInputRange!(typeof(this))); - } -} - -/// Ditto -Chunks!Source chunks(Source)(Source source, size_t chunkSize) -if (isInputRange!Source) -{ - return typeof(return)(source, chunkSize); -} - -/// -@safe unittest -{ - import std.algorithm.comparison : equal; - auto source = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]; - auto chunks = chunks(source, 4); - assert(chunks[0] == [1, 2, 3, 4]); - assert(chunks[1] == [5, 6, 7, 8]); - assert(chunks[2] == [9, 10]); - assert(chunks.back == chunks[2]); - assert(chunks.front == chunks[0]); - assert(chunks.length == 3); - assert(equal(retro(array(chunks)), array(retro(chunks)))); -} - -/// Non-forward input ranges are supported, but with limited semantics. -@system /*@safe*/ unittest // FIXME: can't be @safe because RefCounted isn't. -{ - import std.algorithm.comparison : equal; - - int i; - - // The generator doesn't save state, so it cannot be a forward range. - auto inputRange = generate!(() => ++i).take(10); - - // We can still process it in chunks, but it will be single-pass only. - auto chunked = inputRange.chunks(2); - - assert(chunked.front.equal([1, 2])); - assert(chunked.front.empty); // Iterating the chunk has consumed it - chunked.popFront; - assert(chunked.front.equal([3, 4])); -} - -@system /*@safe*/ unittest -{ - import std.algorithm.comparison : equal; - import std.internal.test.dummyrange : ReferenceInputRange; - - auto data = [ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 ]; - auto r = new ReferenceInputRange!int(data).chunks(3); - assert(r.equal!equal([ - [ 1, 2, 3 ], - [ 4, 5, 6 ], - [ 7, 8, 9 ], - [ 10 ] - ])); - - auto data2 = [ 1, 2, 3, 4, 5, 6 ]; - auto r2 = new ReferenceInputRange!int(data2).chunks(3); - assert(r2.equal!equal([ - [ 1, 2, 3 ], - [ 4, 5, 6 ] - ])); - - auto data3 = [ 1, 2, 3, 4, 5 ]; - auto r3 = new ReferenceInputRange!int(data3).chunks(2); - assert(r3.front.equal([1, 2])); - r3.popFront(); - assert(!r3.empty); - r3.popFront(); - assert(r3.front.equal([5])); -} - -@safe unittest -{ - auto source = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]; - auto chunks = chunks(source, 4); - auto chunks2 = chunks.save; - chunks.popFront(); - assert(chunks[0] == [5, 6, 7, 8]); - assert(chunks[1] == [9, 10]); - chunks2.popBack(); - assert(chunks2[1] == [5, 6, 7, 8]); - assert(chunks2.length == 2); - - static assert(isRandomAccessRange!(typeof(chunks))); -} - -@safe unittest -{ - import std.algorithm.comparison : equal; - - //Extra toying with slicing and indexing. - auto chunks1 = [0, 0, 1, 1, 2, 2, 3, 3, 4].chunks(2); - auto chunks2 = [0, 0, 1, 1, 2, 2, 3, 3, 4, 4].chunks(2); - - assert(chunks1.length == 5); - assert(chunks2.length == 5); - assert(chunks1[4] == [4]); - assert(chunks2[4] == [4, 4]); - assert(chunks1.back == [4]); - assert(chunks2.back == [4, 4]); - - assert(chunks1[0 .. 1].equal([[0, 0]])); - assert(chunks1[0 .. 2].equal([[0, 0], [1, 1]])); - assert(chunks1[4 .. 5].equal([[4]])); - assert(chunks2[4 .. 5].equal([[4, 4]])); - - assert(chunks1[0 .. 0].equal((int[][]).init)); - assert(chunks1[5 .. 5].equal((int[][]).init)); - assert(chunks2[5 .. 5].equal((int[][]).init)); - - //Fun with opDollar - assert(chunks1[$ .. $].equal((int[][]).init)); //Quick - assert(chunks2[$ .. $].equal((int[][]).init)); //Quick - assert(chunks1[$ - 1 .. $].equal([[4]])); //Semiquick - assert(chunks2[$ - 1 .. $].equal([[4, 4]])); //Semiquick - assert(chunks1[$ .. 5].equal((int[][]).init)); //Semiquick - assert(chunks2[$ .. 5].equal((int[][]).init)); //Semiquick - - assert(chunks1[$ / 2 .. $ - 1].equal([[2, 2], [3, 3]])); //Slow -} - -@safe unittest -{ - import std.algorithm.comparison : equal; - import std.algorithm.iteration : filter; - - //ForwardRange - auto r = filter!"true"([1, 2, 3, 4, 5]).chunks(2); - assert(equal!"equal(a, b)"(r, [[1, 2], [3, 4], [5]])); - - //InfiniteRange w/o RA - auto fibsByPairs = recurrence!"a[n-1] + a[n-2]"(1, 1).chunks(2); - assert(equal!`equal(a, b)`(fibsByPairs.take(2), [[ 1, 1], [ 2, 3]])); - - //InfiniteRange w/ RA and slicing - auto odds = sequence!("a[0] + n * a[1]")(1, 2); - auto oddsByPairs = odds.chunks(2); - assert(equal!`equal(a, b)`(oddsByPairs.take(2), [[ 1, 3], [ 5, 7]])); - - //Requires phobos#991 for Sequence to have slice to end - static assert(hasSlicing!(typeof(odds))); - assert(equal!`equal(a, b)`(oddsByPairs[3 .. 5], [[13, 15], [17, 19]])); - assert(equal!`equal(a, b)`(oddsByPairs[3 .. $].take(2), [[13, 15], [17, 19]])); -} - - - -/** -This range splits a `source` range into `chunkCount` chunks of -approximately equal length. `Source` must be a forward range with -known length. - -Unlike $(LREF chunks), `evenChunks` takes a chunk count (not size). -The returned range will contain zero or more $(D source.length / -chunkCount + 1) elements followed by $(D source.length / chunkCount) -elements. If $(D source.length < chunkCount), some chunks will be empty. - -`chunkCount` must not be zero, unless `source` is also empty. -*/ -struct EvenChunks(Source) -if (isForwardRange!Source && hasLength!Source) -{ - /// Standard constructor - this(Source source, size_t chunkCount) - { - assert(chunkCount != 0 || source.empty, "Cannot create EvenChunks with a zero chunkCount"); - _source = source; - _chunkCount = chunkCount; - } - - /// Forward range primitives. Always present. - @property auto front() - { - assert(!empty, "Attempting to fetch the front of an empty evenChunks"); - return _source.save.take(_chunkPos(1)); - } - - /// Ditto - void popFront() - { - assert(!empty, "Attempting to popFront an empty evenChunks"); - _source.popFrontN(_chunkPos(1)); - _chunkCount--; - } - - /// Ditto - @property bool empty() - { - return _chunkCount == 0; - } - - /// Ditto - @property typeof(this) save() - { - return typeof(this)(_source.save, _chunkCount); - } - - /// Length - @property size_t length() const - { - return _chunkCount; - } - //Note: No point in defining opDollar here without slicing. - //opDollar is defined below in the hasSlicing!Source section - - static if (hasSlicing!Source) - { - /** - Indexing, slicing and bidirectional operations and range primitives. - Provided only if `hasSlicing!Source` is `true`. - */ - auto opIndex(size_t index) - { - assert(index < _chunkCount, "evenChunks index out of bounds"); - return _source[_chunkPos(index) .. _chunkPos(index+1)]; - } - - /// Ditto - typeof(this) opSlice(size_t lower, size_t upper) - { - assert(lower <= upper && upper <= length, "evenChunks slicing index out of bounds"); - return evenChunks(_source[_chunkPos(lower) .. _chunkPos(upper)], upper - lower); - } - - /// Ditto - @property auto back() - { - assert(!empty, "back called on empty evenChunks"); - return _source[_chunkPos(_chunkCount - 1) .. _source.length]; - } - - /// Ditto - void popBack() - { - assert(!empty, "popBack() called on empty evenChunks"); - _source = _source[0 .. _chunkPos(_chunkCount - 1)]; - _chunkCount--; - } - } - -private: - Source _source; - size_t _chunkCount; - - size_t _chunkPos(size_t i) - { - /* - _chunkCount = 5, _source.length = 13: - - chunk0 - | chunk3 - | | - v v - +-+-+-+-+-+ ^ - |0|3|.| | | | - +-+-+-+-+-+ | div - |1|4|.| | | | - +-+-+-+-+-+ v - |2|5|.| - +-+-+-+ - - <-----> - mod - - <---------> - _chunkCount - - One column is one chunk. - popFront and popBack pop the left-most - and right-most column, respectively. - */ - - auto div = _source.length / _chunkCount; - auto mod = _source.length % _chunkCount; - auto pos = i <= mod - ? i * (div+1) - : mod * (div+1) + (i-mod) * div - ; - //auto len = i < mod - // ? div+1 - // : div - //; - return pos; - } -} - -/// Ditto -EvenChunks!Source evenChunks(Source)(Source source, size_t chunkCount) -if (isForwardRange!Source && hasLength!Source) -{ - return typeof(return)(source, chunkCount); -} - -/// -@safe unittest -{ - import std.algorithm.comparison : equal; - auto source = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]; - auto chunks = evenChunks(source, 3); - assert(chunks[0] == [1, 2, 3, 4]); - assert(chunks[1] == [5, 6, 7]); - assert(chunks[2] == [8, 9, 10]); -} - -@safe unittest -{ - import std.algorithm.comparison : equal; - - auto source = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]; - auto chunks = evenChunks(source, 3); - assert(chunks.back == chunks[2]); - assert(chunks.front == chunks[0]); - assert(chunks.length == 3); - assert(equal(retro(array(chunks)), array(retro(chunks)))); - - auto chunks2 = chunks.save; - chunks.popFront(); - assert(chunks[0] == [5, 6, 7]); - assert(chunks[1] == [8, 9, 10]); - chunks2.popBack(); - assert(chunks2[1] == [5, 6, 7]); - assert(chunks2.length == 2); - - static assert(isRandomAccessRange!(typeof(chunks))); -} - -@safe unittest -{ - import std.algorithm.comparison : equal; - - int[] source = []; - auto chunks = source.evenChunks(0); - assert(chunks.length == 0); - chunks = source.evenChunks(3); - assert(equal(chunks, [[], [], []])); - chunks = [1, 2, 3].evenChunks(5); - assert(equal(chunks, [[1], [2], [3], [], []])); -} - -/** -A fixed-sized sliding window iteration -of size `windowSize` over a `source` range by a custom `stepSize`. - -The `Source` range must be at least a $(REF_ALTTEXT ForwardRange, isForwardRange, std,range,primitives) -and the `windowSize` must be greater than zero. - -For `windowSize = 1` it splits the range into single element groups (aka `unflatten`) -For `windowSize = 2` it is similar to `zip(source, source.save.dropOne)`. - -Params: - f = Whether the last element has fewer elements than `windowSize` - it should be be ignored (`No.withPartial`) or added (`Yes.withPartial`) - source = Range from which the slide will be selected - windowSize = Sliding window size - stepSize = Steps between the windows (by default 1) - -Returns: Range of all sliding windows with propagated bi-directionality, - forwarding, random access, and slicing. - -Note: To avoid performance overhead, $(REF_ALTTEXT bi-directionality, isBidirectionalRange, std,range,primitives) - is only available when $(REF hasSlicing, std,range,primitives) - and $(REF hasLength, std,range,primitives) are true. - -See_Also: $(LREF chunks) -*/ -auto slide(Flag!"withPartial" f = Yes.withPartial, - Source)(Source source, size_t windowSize, size_t stepSize = 1) -if (isForwardRange!Source) -{ - return Slides!(f, Source)(source, windowSize, stepSize); -} - -/// Iterate over ranges with windows -@safe pure nothrow unittest -{ - import std.algorithm.comparison : equal; - - assert([0, 1, 2, 3].slide(2).equal!equal( - [[0, 1], [1, 2], [2, 3]] - )); - - assert(5.iota.slide(3).equal!equal( - [[0, 1, 2], [1, 2, 3], [2, 3, 4]] - )); -} - -/// set a custom stepsize (default 1) -@safe pure nothrow unittest -{ - import std.algorithm.comparison : equal; - - assert(6.iota.slide(1, 2).equal!equal( - [[0], [2], [4]] - )); - - assert(6.iota.slide(2, 4).equal!equal( - [[0, 1], [4, 5]] - )); - - assert(iota(7).slide(2, 2).equal!equal( - [[0, 1], [2, 3], [4, 5], [6]] - )); - - assert(iota(12).slide(2, 4).equal!equal( - [[0, 1], [4, 5], [8, 9]] - )); -} - -/// Allow the last slide to have fewer elements than windowSize -@safe pure nothrow unittest -{ - import std.algorithm.comparison : equal; - - assert(3.iota.slide!(No.withPartial)(4).empty); - assert(3.iota.slide!(Yes.withPartial)(4).equal!equal( - [[0, 1, 2]] - )); -} - -/// Count all the possible substrings of length 2 -@safe pure nothrow unittest -{ - import std.algorithm.iteration : each; - - int[dstring] d; - "AGAGA"d.slide!(Yes.withPartial)(2).each!(a => d[a]++); - assert(d == ["AG"d: 2, "GA"d: 2]); -} - -/// withPartial only has an effect if last element in the range doesn't have the full size -@safe pure nothrow unittest -{ - import std.algorithm.comparison : equal; - - assert(5.iota.slide!(Yes.withPartial)(3, 4).equal!equal([[0, 1, 2], [4]])); - assert(6.iota.slide!(Yes.withPartial)(3, 4).equal!equal([[0, 1, 2], [4, 5]])); - assert(7.iota.slide!(Yes.withPartial)(3, 4).equal!equal([[0, 1, 2], [4, 5, 6]])); - - assert(5.iota.slide!(No.withPartial)(3, 4).equal!equal([[0, 1, 2]])); - assert(6.iota.slide!(No.withPartial)(3, 4).equal!equal([[0, 1, 2]])); - assert(7.iota.slide!(No.withPartial)(3, 4).equal!equal([[0, 1, 2], [4, 5, 6]])); -} - -private struct Slides(Flag!"withPartial" withPartial = Yes.withPartial, Source) -if (isForwardRange!Source) -{ -private: - Source source; - size_t windowSize; - size_t stepSize; - - static if (hasLength!Source) - { - enum needsEndTracker = false; - } - else - { - // If there's no information about the length, track needs to be kept manually - Source nextSource; - enum needsEndTracker = true; - } - - bool _empty; - - static if (hasSlicing!Source) - enum hasSliceToEnd = hasSlicing!Source && is(typeof(Source.init[0 .. $]) == Source); - - static if (withPartial) - bool hasShownPartialBefore; - -public: - /// Standard constructor - this(Source source, size_t windowSize, size_t stepSize) - { - assert(windowSize > 0, "windowSize must be greater than zero"); - assert(stepSize > 0, "stepSize must be greater than zero"); - this.source = source; - this.windowSize = windowSize; - this.stepSize = stepSize; - - static if (needsEndTracker) - { - // `nextSource` is used to "look one step into the future" and check for the end - // this means `nextSource` is advanced by `stepSize` on every `popFront` - nextSource = source.save; - auto poppedElems = nextSource.popFrontN(windowSize); - } - - if (source.empty) - { - _empty = true; - return; - } - - static if (withPartial) - { - static if (needsEndTracker) - { - if (nextSource.empty) - hasShownPartialBefore = true; - } - else - { - if (source.length <= windowSize) - hasShownPartialBefore = true; - } - } - else - { - // empty source range is needed, s.t. length, slicing etc. works properly - static if (needsEndTracker) - { - if (poppedElems < windowSize) - _empty = true; - } - else - { - if (source.length < windowSize) - _empty = true; - } - } - } - - /// Forward range primitives. Always present. - @property auto front() - { - assert(!empty, "Attempting to access front on an empty slide."); - static if (hasSlicing!Source && hasLength!Source) - { - static if (withPartial) - { - import std.algorithm.comparison : min; - return source[0 .. min(windowSize, source.length)]; - } - else - { - assert(windowSize <= source.length, "The last element is smaller than the current windowSize."); - return source[0 .. windowSize]; - } - } - else - { - static if (withPartial) - return source.save.take(windowSize); - else - return source.save.takeExactly(windowSize); - } - } - - /// Ditto - void popFront() - { - assert(!empty, "Attempting to call popFront() on an empty slide."); - source.popFrontN(stepSize); - - if (source.empty) - { - _empty = true; - return; - } - - static if (withPartial) - { - if (hasShownPartialBefore) - _empty = true; - } - - static if (needsEndTracker) - { - // Check the upcoming slide - auto poppedElements = nextSource.popFrontN(stepSize); - static if (withPartial) - { - if (poppedElements < stepSize || nextSource.empty) - hasShownPartialBefore = true; - } - else - { - if (poppedElements < stepSize) - _empty = true; - } - } - else - { - static if (withPartial) - { - if (source.length <= windowSize) - hasShownPartialBefore = true; - } - else - { - if (source.length < windowSize) - _empty = true; - } - } - } - - static if (!isInfinite!Source) - { - /// Ditto - @property bool empty() const - { - return _empty; - } - } - else - { - // undocumented - enum empty = false; - } - - /// Ditto - @property typeof(this) save() - { - return typeof(this)(source.save, windowSize, stepSize); - } - - static if (hasLength!Source) - { - // gaps between the last element and the end of the range - private size_t gap() - { - /* - * Note: - * - In the following `end` is the exclusive end as used in opSlice - * - For the trivial case with `stepSize = 1` `end` is at `len`: - * - * iota(4).slide(2) = [[0, 1], [1, 2], [2, 3]] (end = 4) - * iota(4).slide(3) = [[0, 1, 2], [1, 2, 3]] (end = 4) - * - * - For the non-trivial cases, we need to calculate the gap - * between `len` and `end` - this is the number of missing elements - * from the input range: - * - * iota(7).slide(2, 3) = [[0, 1], [3, 4]] || 6 - * iota(7).slide(2, 4) = [[0, 1], [4, 5]] || 6 - * iota(7).slide(1, 5) = [[0], [5]] || 6 - * - * As it can be seen `gap` can be at most `stepSize - 1` - * More generally the elements of the sliding window with - * `w = windowSize` and `s = stepSize` are: - * - * [0, w], [s, s + w], [2 * s, 2 * s + w], ... [n * s, n * s + w] - * - * We can thus calculate the gap between the `end` and `len` as: - * - * gap = len - (n * s + w) = len - w - (n * s) - * - * As we aren't interested in exact value of `n`, but the best - * minimal `gap` value, we can use modulo to "cut" `len - w` optimally: - * - * gap = len - w - (s - s ... - s) = (len - w) % s - * - * So for example: - * - * iota(7).slide(2, 3) = [[0, 1], [3, 4]] - * gap: (7 - 2) % 3 = 5 % 3 = 2 - * end: 7 - 2 = 5 - * - * iota(7).slide(4, 2) = [[0, 1, 2, 3], [2, 3, 4, 5]] - * gap: (7 - 4) % 2 = 3 % 2 = 1 - * end: 7 - 1 = 6 - */ - return (source.length - windowSize) % stepSize; - } - - private size_t numberOfFullFrames() - { - /** - 5.iota.slides(2, 1) => [0, 1], [1, 2], [2, 3], [3, 4] (4) - 7.iota.slides(2, 2) => [0, 1], [2, 3], [4, 5], [6] (3) - 7.iota.slides(2, 3) => [0, 1], [3, 4], [6] (2) - 6.iota.slides(3, 2) => [0, 1, 2], [2, 3, 4], [4, 5] (2) - 7.iota.slides(3, 3) => [0, 1, 2], [3, 4, 5], [6] (2) - - As the last window is only added iff its complete, - we don't count the last window except if it's full due to integer rounding. - */ - return 1 + (source.length - windowSize) / stepSize; - } - - // Whether the last slide frame size is less than windowSize - private bool hasPartialElements() - { - static if (withPartial) - return gap != 0 && source.length > numberOfFullFrames * stepSize; - else - return 0; - } - - /// Length. Only if `hasLength!Source` is `true` - @property size_t length() - { - if (source.length < windowSize) - { - static if (withPartial) - return source.length > 0; - else - return 0; - } - else - { - /*** - We bump the pointer by stepSize for every element. - If withPartial, we don't count the last element if its size - isn't windowSize - - At most: - [p, p + stepSize, ..., p + stepSize * n] - - 5.iota.slides(2, 1) => [0, 1], [1, 2], [2, 3], [3, 4] (4) - 7.iota.slides(2, 2) => [0, 1], [2, 3], [4, 5], [6] (4) - 7.iota.slides(2, 3) => [0, 1], [3, 4], [6] (3) - 7.iota.slides(3, 2) => [0, 1, 2], [2, 3, 4], [4, 5, 6] (3) - 7.iota.slides(3, 3) => [0, 1, 2], [3, 4, 5], [6] (3) - */ - return numberOfFullFrames + hasPartialElements; - } - } - } - - static if (hasSlicing!Source) - { - /** - Indexing and slicing operations. Provided only if - `hasSlicing!Source` is `true`. - */ - auto opIndex(size_t index) - { - immutable start = index * stepSize; - - static if (isInfinite!Source) - { - immutable end = start + windowSize; - } - else - { - import std.algorithm.comparison : min; - - immutable len = source.length; - assert(start < len, "slide index out of bounds"); - immutable end = min(start + windowSize, len); - } - - return source[start .. end]; - } - - static if (!isInfinite!Source) - { - /// ditto - typeof(this) opSlice(size_t lower, size_t upper) - { - import std.algorithm.comparison : min; - - assert(upper <= length, "slide slicing index out of bounds"); - assert(lower <= upper, "slide slicing index out of bounds"); - - lower *= stepSize; - upper *= stepSize; - - immutable len = source.length; - - static if (withPartial) - { - import std.algorithm.comparison : max; - - if (lower == upper) - return this[$ .. $]; - - /* - A) If `stepSize` >= `windowSize` => `rightPos = upper` - - [0, 1, 2, 3, 4, 5, 6].slide(2, 3) -> s = [[0, 1], [3, 4], [6]] - rightPos for s[0 .. 2]: (upper=2) * (stepSize=3) = 6 - 6.iota.slide(2, 3) = [[0, 1], [3, 4]] - - B) If `stepSize` < `windowSize` => add `windowSize - stepSize` to `upper` - - [0, 1, 2, 3].slide(2) = [[0, 1], [1, 2], [2, 3]] - rightPos for s[0 .. 1]: = (upper=1) * (stepSize=1) = 1 - 1.iota.slide(2) = [[0]] - - rightPos for s[0 .. 1]: = (upper=1) * (stepSize=1) + (windowSize-stepSize=1) = 2 - 1.iota.slide(2) = [[0, 1]] - - More complex: - - 20.iota.slide(7, 6)[0 .. 2] - rightPos: (upper=2) * (stepSize=6) = 12.iota - 12.iota.slide(7, 6) = [[0, 1, 2, 3, 4, 5, 6], [6, 7, 8, 9, 10, 11]] - - Now we add up for the difference between `windowSize` and `stepSize`: - - rightPos: (upper=2) * (stepSize=6) + (windowSize-stepSize=1) = 13.iota - 13.iota.slide(7, 6) = [[0, 1, 2, 3, 4, 5, 6], [6, 7, 8, 9, 10, 11, 12]] - */ - immutable rightPos = min(len, upper + max(0, windowSize - stepSize)); - } - else - { - /* - After we have normalized `lower` and `upper` by `stepSize`, - we only need to look at the case of `stepSize=1`. - As `leftPos`, is equal to `lower`, we will only look `rightPos`. - Notice that starting from `upper`, - we only need to move for `windowSize - 1` to the right: - - - [0, 1, 2, 3].slide(2) -> s = [[0, 1], [1, 2], [2, 3]] - rightPos for s[0 .. 3]: (upper=3) + (windowSize=2) - 1 = 4 - - - [0, 1, 2, 3].slide(3) -> s = [[0, 1, 2], [1, 2, 3]] - rightPos for s[0 .. 2]: (upper=2) + (windowSize=3) - 1 = 4 - - - [0, 1, 2, 3, 4].slide(4) -> s = [[0, 1, 2, 3], [1, 2, 3, 4]] - rightPos for s[0 .. 2]: (upper=2) + (windowSize=4) - 1 = 5 - */ - immutable rightPos = min(upper + windowSize - 1, len); - } - - return typeof(this)(source[min(lower, len) .. rightPos], windowSize, stepSize); - } - } - else static if (hasSliceToEnd) - { - // For slicing an infinite chunk, we need to slice the source to the infinite end. - auto opSlice(size_t lower, size_t upper) - { - assert(lower <= upper, "slide slicing index out of bounds"); - return typeof(this)(source[lower * stepSize .. $], windowSize, stepSize) - .takeExactly(upper - lower); - } - } - - static if (isInfinite!Source) - { - static if (hasSliceToEnd) - { - private static struct DollarToken{} - DollarToken opDollar() - { - return DollarToken(); - } - //Slice to dollar - typeof(this) opSlice(size_t lower, DollarToken) - { - return typeof(this)(source[lower * stepSize .. $], windowSize, stepSize); - } - } - } - else - { - // Dollar token carries a static type, with no extra information. - // It can lazily transform into source.length on algorithmic - // operations such as : slide[$/2, $-1]; - private static struct DollarToken - { - private size_t _length; - alias _length this; - } - - DollarToken opDollar() - { - return DollarToken(this.length); - } - - // Optimized slice overloads optimized for using dollar. - typeof(this) opSlice(DollarToken, DollarToken) - { - static if (hasSliceToEnd) - { - return typeof(this)(source[$ .. $], windowSize, stepSize); - } - else - { - immutable len = source.length; - return typeof(this)(source[len .. len], windowSize, stepSize); - } - } - - // Optimized slice overloads optimized for using dollar. - typeof(this) opSlice(size_t lower, DollarToken) - { - import std.algorithm.comparison : min; - assert(lower <= length, "slide slicing index out of bounds"); - lower *= stepSize; - static if (hasSliceToEnd) - { - return typeof(this)(source[min(lower, source.length) .. $], windowSize, stepSize); - } - else - { - immutable len = source.length; - return typeof(this)(source[min(lower, len) .. len], windowSize, stepSize); - } - } - - // Optimized slice overloads optimized for using dollar. - typeof(this) opSlice(DollarToken, size_t upper) - { - assert(upper == length, "slide slicing index out of bounds"); - return this[$ .. $]; - } - } - - // Bidirectional range primitives - static if (!isInfinite!Source) - { - /** - Bidirectional range primitives. Provided only if both - `hasSlicing!Source` and `!isInfinite!Source` are `true`. - */ - @property auto back() - { - import std.algorithm.comparison : max; - - assert(!empty, "Attempting to access front on an empty slide"); - - immutable len = source.length; - - static if (withPartial) - { - if (source.length <= windowSize) - return source[0 .. source.length]; - - if (hasPartialElements) - return source[numberOfFullFrames * stepSize .. len]; - } - - // check for underflow - immutable start = (len > windowSize + gap) ? len - windowSize - gap : 0; - return source[start .. len - gap]; - } - - /// Ditto - void popBack() - { - assert(!empty, "Attempting to call popBack() on an empty slide"); - - // Move by stepSize - immutable end = source.length > stepSize ? source.length - stepSize : 0; - - static if (withPartial) - { - if (hasShownPartialBefore || source.empty) - { - _empty = true; - return; - } - - // pop by stepSize, except for the partial frame at the end - if (hasPartialElements) - source = source[0 .. source.length - gap]; - else - source = source[0 .. end]; - } - else - { - source = source[0 .. end]; - } - - if (source.length < windowSize) - _empty = true; - } - } - } -} - -// test @nogc -@safe pure nothrow @nogc unittest -{ - import std.algorithm.comparison : equal; - - static immutable res1 = [[0], [1], [2], [3]]; - assert(4.iota.slide!(Yes.withPartial)(1).equal!equal(res1)); - - static immutable res2 = [[0, 1], [1, 2], [2, 3]]; - assert(4.iota.slide!(Yes.withPartial)(2).equal!equal(res2)); -} - -// test different window sizes -@safe pure nothrow unittest -{ - import std.array : array; - import std.algorithm.comparison : equal; - - assert([0, 1, 2, 3].slide!(Yes.withPartial)(1).array == [[0], [1], [2], [3]]); - assert([0, 1, 2, 3].slide!(Yes.withPartial)(2).array == [[0, 1], [1, 2], [2, 3]]); - assert([0, 1, 2, 3].slide!(Yes.withPartial)(3).array == [[0, 1, 2], [1, 2, 3]]); - assert([0, 1, 2, 3].slide!(Yes.withPartial)(4).array == [[0, 1, 2, 3]]); - assert([0, 1, 2, 3].slide!(No.withPartial)(5).walkLength == 0); - assert([0, 1, 2, 3].slide!(Yes.withPartial)(5).array == [[0, 1, 2, 3]]); - - assert(iota(2).slide!(Yes.withPartial)(2).front.equal([0, 1])); - assert(iota(3).slide!(Yes.withPartial)(2).equal!equal([[0, 1],[1, 2]])); - assert(iota(3).slide!(Yes.withPartial)(3).equal!equal([[0, 1, 2]])); - assert(iota(3).slide!(No.withPartial)(4).walkLength == 0); - assert(iota(3).slide!(Yes.withPartial)(4).equal!equal([[0, 1, 2]])); - assert(iota(1, 4).slide!(Yes.withPartial)(1).equal!equal([[1], [2], [3]])); - assert(iota(1, 4).slide!(Yes.withPartial)(3).equal!equal([[1, 2, 3]])); -} - -// test combinations -@safe pure nothrow unittest -{ - import std.algorithm.comparison : equal; - import std.typecons : tuple; - - alias t = tuple; - auto list = [ - t(t(1, 1), [[0], [1], [2], [3], [4], [5]]), - t(t(1, 2), [[0], [2], [4]]), - t(t(1, 3), [[0], [3]]), - t(t(1, 4), [[0], [4]]), - t(t(1, 5), [[0], [5]]), - t(t(2, 1), [[0, 1], [1, 2], [2, 3], [3, 4], [4, 5]]), - t(t(2, 2), [[0, 1], [2, 3], [4, 5]]), - t(t(2, 3), [[0, 1], [3, 4]]), - t(t(2, 4), [[0, 1], [4, 5]]), - t(t(3, 1), [[0, 1, 2], [1, 2, 3], [2, 3, 4], [3, 4, 5]]), - t(t(3, 3), [[0, 1, 2], [3, 4, 5]]), - t(t(4, 1), [[0, 1, 2, 3], [1, 2, 3, 4], [2, 3, 4, 5]]), - t(t(4, 2), [[0, 1, 2, 3], [2, 3, 4, 5]]), - t(t(5, 1), [[0, 1, 2, 3, 4], [1, 2, 3, 4, 5]]), - ]; - - static foreach (Partial; [Yes.withPartial, No.withPartial]) - foreach (e; list) - assert(6.iota.slide!Partial(e[0].expand).equal!equal(e[1])); - - auto listSpecial = [ - t(t(2, 5), [[0, 1], [5]]), - t(t(3, 2), [[0, 1, 2], [2, 3, 4], [4, 5]]), - t(t(3, 4), [[0, 1, 2], [4, 5]]), - t(t(4, 3), [[0, 1, 2, 3], [3, 4, 5]]), - t(t(5, 2), [[0, 1, 2, 3, 4], [2, 3, 4, 5]]), - t(t(5, 3), [[0, 1, 2, 3, 4], [3, 4, 5]]), - ]; - foreach (e; listSpecial) - { - assert(6.iota.slide!(Yes.withPartial)(e[0].expand).equal!equal(e[1])); - assert(6.iota.slide!(No.withPartial)(e[0].expand).equal!equal(e[1].dropBackOne)); - } -} - -// test emptiness and copyability -@safe pure nothrow unittest -{ - import std.algorithm.comparison : equal; - import std.algorithm.iteration : map; - - // check with empty input - int[] d; - assert(d.slide!(Yes.withPartial)(2).empty); - assert(d.slide!(Yes.withPartial)(2, 2).empty); - - // is copyable? - auto e = iota(5).slide!(Yes.withPartial)(2); - e.popFront; - assert(e.save.equal!equal([[1, 2], [2, 3], [3, 4]])); - assert(e.save.equal!equal([[1, 2], [2, 3], [3, 4]])); - assert(e.map!"a.array".array == [[1, 2], [2, 3], [3, 4]]); -} - -// test with strings -@safe pure nothrow unittest -{ - import std.algorithm.iteration : each; - - int[dstring] f; - "AGAGA"d.slide!(Yes.withPartial)(3).each!(a => f[a]++); - assert(f == ["AGA"d: 2, "GAG"d: 1]); - - int[dstring] g; - "ABCDEFG"d.slide!(Yes.withPartial)(3, 3).each!(a => g[a]++); - assert(g == ["ABC"d:1, "DEF"d:1, "G": 1]); - g = null; - "ABCDEFG"d.slide!(No.withPartial)(3, 3).each!(a => g[a]++); - assert(g == ["ABC"d:1, "DEF"d:1]); -} - -// test with utf8 strings -@safe unittest -{ - import std.stdio; - import std.algorithm.comparison : equal; - - assert("ä.Ăś.Ăź.".slide!(Yes.withPartial)(3, 2).equal!equal(["ä.Ăś", "Ăś.Ăź", "Ăź."])); - assert("ä.Ăś.Ăź.".slide!(No.withPartial)(3, 2).equal!equal(["ä.Ăś", "Ăś.Ăź"])); - - "😄😅😆😇😈😄😅😆😇😈".slide!(Yes.withPartial)(2, 4).equal!equal(["😄😅", "😈😄", "😇😈"]); - "😄😅😆😇😈😄😅😆😇😈".slide!(No.withPartial)(2, 4).equal!equal(["😄😅", "😈😄", "😇😈"]); - "😄😅😆😇😈😄😅😆😇😈".slide!(Yes.withPartial)(3, 3).equal!equal(["😄😅😆", "😇😈😄", "😅😆😇", "😈"]); - "😄😅😆😇😈😄😅😆😇😈".slide!(No.withPartial)(3, 3).equal!equal(["😄😅😆", "😇😈😄", "😅😆😇"]); -} - -// test length -@safe pure nothrow unittest -{ - // Slides with fewer elements are empty or 1 for Yes.withPartial - static foreach (expectedLength, Partial; [No.withPartial, Yes.withPartial]) - {{ - assert(3.iota.slide!(Partial)(4, 2).walkLength == expectedLength); - assert(3.iota.slide!(Partial)(4).walkLength == expectedLength); - assert(3.iota.slide!(Partial)(4, 3).walkLength == expectedLength); - }} - - static immutable list = [ - // iota slide expected - [4, 2, 1, 3, 3], - [5, 3, 1, 3, 3], - [7, 2, 2, 4, 3], - [12, 2, 4, 3, 3], - [6, 1, 2, 3, 3], - [6, 2, 4, 2, 2], - [3, 2, 4, 1, 1], - [5, 2, 1, 4, 4], - [7, 2, 2, 4, 3], - [7, 2, 3, 3, 2], - [7, 3, 2, 3, 3], - [7, 3, 3, 3, 2], - ]; - foreach (e; list) - { - assert(e[0].iota.slide!(Yes.withPartial)(e[1], e[2]).length == e[3]); - assert(e[0].iota.slide!(No.withPartial)(e[1], e[2]).length == e[4]); - } -} - -// test index and slicing -@safe pure nothrow unittest -{ - import std.algorithm.comparison : equal; - import std.array : array; - - static foreach (Partial; [Yes.withPartial, No.withPartial]) - { - foreach (s; [5, 7, 10, 15, 20]) - foreach (windowSize; 1 .. 10) - foreach (stepSize; 1 .. 10) - { - auto r = s.iota.slide!Partial(windowSize, stepSize); - auto arr = r.array; - assert(r.length == arr.length); - - // test indexing - foreach (i; 0 .. arr.length) - assert(r[i] == arr[i]); - - // test slicing - foreach (i; 0 .. arr.length) - { - foreach (j; i .. arr.length) - assert(r[i .. j].equal(arr[i .. j])); - - assert(r[i .. $].equal(arr[i .. $])); - } - - // test opDollar slicing - assert(r[$/2 .. $].equal(arr[$/2 .. $])); - assert(r[$ .. $].empty); - if (arr.empty) - { - assert(r[$ .. 0].empty); - assert(r[$/2 .. $].empty); - - } - } - } -} - -// test with infinite ranges -@safe pure nothrow unittest -{ - import std.algorithm.comparison : equal; - - static foreach (Partial; [Yes.withPartial, No.withPartial]) - {{ - // InfiniteRange without RandomAccess - auto fibs = recurrence!"a[n-1] + a[n-2]"(1, 1); - assert(fibs.slide!Partial(2).take(2).equal!equal([[1, 1], [1, 2]])); - assert(fibs.slide!Partial(2, 3).take(2).equal!equal([[1, 1], [3, 5]])); - - // InfiniteRange with RandomAccess and slicing - auto odds = sequence!("a[0] + n * a[1]")(1, 2); - auto oddsByPairs = odds.slide!Partial(2); - assert(oddsByPairs.take(2).equal!equal([[ 1, 3], [ 3, 5]])); - assert(oddsByPairs[1].equal([3, 5])); - assert(oddsByPairs[4].equal([9, 11])); - - static assert(hasSlicing!(typeof(odds))); - assert(oddsByPairs[3 .. 5].equal!equal([[7, 9], [9, 11]])); - assert(oddsByPairs[3 .. $].take(2).equal!equal([[7, 9], [9, 11]])); - - auto oddsWithGaps = odds.slide!Partial(2, 4); - assert(oddsWithGaps.take(3).equal!equal([[1, 3], [9, 11], [17, 19]])); - assert(oddsWithGaps[2].equal([17, 19])); - assert(oddsWithGaps[1 .. 3].equal!equal([[9, 11], [17, 19]])); - assert(oddsWithGaps[1 .. $].take(2).equal!equal([[9, 11], [17, 19]])); - }} -} - -// test reverse -@safe pure nothrow unittest -{ - import std.algorithm.comparison : equal; - - static foreach (Partial; [Yes.withPartial, No.withPartial]) - {{ - foreach (windowSize; 1 .. 15) - foreach (stepSize; 1 .. 15) - { - auto r = 20.iota.slide!Partial(windowSize, stepSize); - auto rArr = r.array.retro; - auto rRetro = r.retro; - - assert(rRetro.length == rArr.length); - assert(rRetro.equal(rArr)); - assert(rRetro.array.retro.equal(r)); - } - }} -} - -// test with dummy ranges -@safe pure nothrow unittest -{ - import std.algorithm.comparison : equal; - import std.internal.test.dummyrange : AllDummyRanges; - import std.meta : Filter; - - static foreach (Range; Filter!(isForwardRange, AllDummyRanges)) - {{ - Range r; - - static foreach (Partial; [Yes.withPartial, No.withPartial]) - { - assert(r.slide!Partial(1).equal!equal( - [[1], [2], [3], [4], [5], [6], [7], [8], [9], [10]] - )); - assert(r.slide!Partial(2).equal!equal( - [[1, 2], [2, 3], [3, 4], [4, 5], [5, 6], [6, 7], [7, 8], [8, 9], [9, 10]] - )); - assert(r.slide!Partial(3).equal!equal( - [[1, 2, 3], [2, 3, 4], [3, 4, 5], [4, 5, 6], - [5, 6, 7], [6, 7, 8], [7, 8, 9], [8, 9, 10]] - )); - assert(r.slide!Partial(6).equal!equal( - [[1, 2, 3, 4, 5, 6], [2, 3, 4, 5, 6, 7], [3, 4, 5, 6, 7, 8], - [4, 5, 6, 7, 8, 9], [5, 6, 7, 8, 9, 10]] - )); - } - - // special cases - assert(r.slide!(Yes.withPartial)(15).equal!equal(iota(1, 11).only)); - assert(r.slide!(Yes.withPartial)(15).walkLength == 1); - assert(r.slide!(No.withPartial)(15).empty); - assert(r.slide!(No.withPartial)(15).walkLength == 0); - }} -} - -// test with dummy ranges -@safe pure nothrow unittest -{ - import std.algorithm.comparison : equal; - import std.internal.test.dummyrange : AllDummyRanges; - import std.meta : Filter; - import std.typecons : tuple; - - alias t = tuple; - static immutable list = [ - // iota slide expected - t(6, t(4, 2), [[1, 2, 3, 4], [3, 4, 5, 6]]), - t(6, t(4, 6), [[1, 2, 3, 4]]), - t(6, t(4, 1), [[1, 2, 3, 4], [2, 3, 4, 5], [3, 4, 5, 6]]), - t(7, t(4, 1), [[1, 2, 3, 4], [2, 3, 4, 5], [3, 4, 5, 6], [4, 5, 6, 7]]), - t(7, t(4, 3), [[1, 2, 3, 4], [4, 5, 6, 7]]), - t(8, t(4, 2), [[1, 2, 3, 4], [3, 4, 5, 6], [5, 6, 7, 8]]), - t(8, t(4, 1), [[1, 2, 3, 4], [2, 3, 4, 5], [3, 4, 5, 6], [4, 5, 6, 7], [5, 6, 7, 8]]), - t(8, t(3, 4), [[1, 2, 3], [5, 6, 7]]), - t(10, t(3, 7), [[1, 2, 3], [8, 9, 10]]), - ]; - - static foreach (Range; Filter!(isForwardRange, AllDummyRanges)) - static foreach (Partial; [Yes.withPartial, No.withPartial]) - foreach (e; list) - assert(Range().take(e[0]).slide!Partial(e[1].expand).equal!equal(e[2])); - - static immutable listSpecial = [ - // iota slide expected - t(6, t(4, 3), [[1, 2, 3, 4], [4, 5, 6]]), - t(7, t(4, 5), [[1, 2, 3, 4], [6, 7]]), - t(7, t(4, 4), [[1, 2, 3, 4], [5, 6, 7]]), - t(7, t(4, 2), [[1, 2, 3, 4], [3, 4, 5, 6], [5, 6, 7]]), - t(8, t(4, 3), [[1, 2, 3, 4], [4, 5, 6, 7], [7, 8]]), - t(8, t(3, 3), [[1, 2, 3], [4, 5, 6], [7, 8]]), - t(8, t(3, 6), [[1, 2, 3], [7, 8]]), - t(10, t(7, 6), [[1, 2, 3, 4, 5, 6, 7], [7, 8, 9, 10]]), - t(10, t(3, 8), [[1, 2, 3], [9, 10]]), - ]; - static foreach (Range; Filter!(isForwardRange, AllDummyRanges)) - static foreach (Partial; [Yes.withPartial, No.withPartial]) - foreach (e; listSpecial) - { - Range r; - assert(r.take(e[0]).slide!(Yes.withPartial)(e[1].expand).equal!equal(e[2])); - assert(r.take(e[0]).slide!(No.withPartial)(e[1].expand).equal!equal(e[2].dropBackOne)); - } -} - -// test reverse with dummy ranges -@safe pure nothrow unittest -{ - import std.algorithm.comparison : equal; - import std.internal.test.dummyrange : AllDummyRanges; - import std.meta : Filter, templateAnd; - import std.typecons : tuple; - alias t = tuple; - - static immutable list = [ - // slide expected - t(1, 1, [[10], [9], [8], [7], [6], [5], [4], [3], [2], [1]]), - t(2, 1, [[9, 10], [8, 9], [7, 8], [6, 7], [5, 6], [4, 5], [3, 4], [2, 3], [1, 2]]), - t(5, 1, [[6, 7, 8, 9, 10], [5, 6, 7, 8, 9], [4, 5, 6, 7, 8], - [3, 4, 5, 6, 7], [2, 3, 4, 5, 6], [1, 2, 3, 4, 5]]), - t(2, 2, [[9, 10], [7, 8], [5, 6], [3, 4], [1, 2]]), - t(2, 4, [[9, 10], [5, 6], [1, 2]]), - ]; - - static foreach (Range; Filter!(templateAnd!(hasSlicing, hasLength, isBidirectionalRange), AllDummyRanges)) - {{ - Range r; - static foreach (Partial; [Yes.withPartial, No.withPartial]) - { - foreach (e; list) - assert(r.slide!Partial(e[0], e[1]).retro.equal!equal(e[2])); - - // front = back - foreach (windowSize; 1 .. 10) - foreach (stepSize; 1 .. 10) - { - auto slider = r.slide!Partial(windowSize, stepSize); - auto sliderRetro = slider.retro.array; - assert(slider.length == sliderRetro.length); - assert(sliderRetro.retro.equal!equal(slider)); - } - } - - // special cases - assert(r.slide!(No.withPartial)(15).retro.walkLength == 0); - assert(r.slide!(Yes.withPartial)(15).retro.equal!equal(iota(1, 11).only)); - }} -} - -// test different sliceable ranges -@safe pure nothrow unittest -{ - import std.algorithm.comparison : equal; - import std.internal.test.dummyrange : AllDummyRanges; - import std.meta : AliasSeq; - - struct SliceableRange(Range, Flag!"withOpDollar" withOpDollar = No.withOpDollar, - Flag!"withInfiniteness" withInfiniteness = No.withInfiniteness) - { - Range arr = 10.iota.array; // similar to DummyRange - @property auto save() { return typeof(this)(arr); } - @property auto front() { return arr[0]; } - void popFront() { arr.popFront(); } - auto opSlice(size_t i, size_t j) - { - // subslices can't be infinite - return SliceableRange!(Range, withOpDollar, No.withInfiniteness)(arr[i .. j]); - } - - static if (withInfiniteness) - { - enum empty = false; - } - else - { - @property bool empty() { return arr.empty; } - @property auto length() { return arr.length; } - } - - static if (withOpDollar) - { - static if (withInfiniteness) - { - struct Dollar {} - Dollar opDollar() const { return Dollar.init; } - - // Slice to dollar - typeof(this) opSlice(size_t lower, Dollar) - { - return typeof(this)(arr[lower .. $]); - } - - } - else - { - alias opDollar = length; - } - } - } - - import std.meta : Filter, templateNot; - alias SliceableDummyRanges = Filter!(hasSlicing, AllDummyRanges); - - static foreach (Partial; [Yes.withPartial, No.withPartial]) - {{ - static foreach (Range; SliceableDummyRanges) - {{ - Range r; - r.reinit; - r.arr[] -= 1; // use a 0-based array (for clarity) - - assert(r.slide!Partial(2)[0].equal([0, 1])); - assert(r.slide!Partial(2)[1].equal([1, 2])); - - // saveable - auto s = r.slide!Partial(2); - assert(s[0 .. 2].equal!equal([[0, 1], [1, 2]])); - s.save.popFront; - assert(s[0 .. 2].equal!equal([[0, 1], [1, 2]])); - - assert(r.slide!Partial(3)[1 .. 3].equal!equal([[1, 2, 3], [2, 3, 4]])); - }} - - static foreach (Range; Filter!(templateNot!isInfinite, SliceableDummyRanges)) - {{ - Range r; - r.reinit; - r.arr[] -= 1; // use a 0-based array (for clarity) - - assert(r.slide!(No.withPartial)(6).equal!equal( - [[0, 1, 2, 3, 4, 5], [1, 2, 3, 4, 5, 6], [2, 3, 4, 5, 6, 7], - [3, 4, 5, 6, 7, 8], [4, 5, 6, 7, 8, 9]] - )); - assert(r.slide!(No.withPartial)(16).empty); - - assert(r.slide!Partial(4)[0 .. $].equal(r.slide!Partial(4))); - assert(r.slide!Partial(2)[$/2 .. $].equal!equal([[4, 5], [5, 6], [6, 7], [7, 8], [8, 9]])); - assert(r.slide!Partial(2)[$ .. $].empty); - - assert(r.slide!Partial(3).retro.equal!equal( - [[7, 8, 9], [6, 7, 8], [5, 6, 7], [4, 5, 6], [3, 4, 5], [2, 3, 4], [1, 2, 3], [0, 1, 2]] - )); - }} - - alias T = int[]; - - // separate checks for infinity - auto infIndex = SliceableRange!(T, No.withOpDollar, Yes.withInfiniteness)([0, 1, 2, 3]); - assert(infIndex.slide!Partial(2)[0].equal([0, 1])); - assert(infIndex.slide!Partial(2)[1].equal([1, 2])); - - auto infDollar = SliceableRange!(T, Yes.withOpDollar, Yes.withInfiniteness)(); - assert(infDollar.slide!Partial(2)[1 .. $].front.equal([1, 2])); - assert(infDollar.slide!Partial(4)[0 .. $].front.equal([0, 1, 2, 3])); - assert(infDollar.slide!Partial(4)[2 .. $].front.equal([2, 3, 4, 5])); - }} -} - -// https://issues.dlang.org/show_bug.cgi?id=19082 -@safe unittest -{ - import std.algorithm.comparison : equal; - import std.algorithm.iteration : map; - assert([1].map!(x => x).slide(2).equal!equal([[1]])); -} - -// https://issues.dlang.org/show_bug.cgi?id=19642 -@safe unittest -{ - import std.algorithm.comparison : equal; - import std.algorithm.iteration : splitter; - - assert("ab cd".splitter(' ').slide!(No.withPartial)(2).equal!equal([["ab", "cd"]])); -} - -// https://issues.dlang.org/show_bug.cgi?id=23976 -@safe unittest -{ - import std.algorithm.comparison : equal; - import std.algorithm.iteration : splitter; - - assert("1<2".splitter('<').slide(2).equal!equal([["1", "2"]])); -} - -private struct OnlyResult(Values...) -if (Values.length > 1) -{ - import std.meta : ApplyRight; - import std.traits : isAssignable; - - private enum arity = Values.length; - - private alias UnqualValues = staticMap!(Unqual, Values); - - private enum canAssignElements = allSatisfy!( - ApplyRight!(isAssignable, CommonType!Values), - Values - ); - - private this(return scope ref Values values) - { - ref @trusted unqual(T)(ref T x){return cast() x;} - - // TODO: this calls any possible copy constructors without qualifiers. - // Find a way to initialize values using qualified copy constructors. - static foreach (i; 0 .. Values.length) - { - this.values[i] = unqual(values[i]); - } - this.backIndex = arity; - } - - bool empty() @property - { - return frontIndex >= backIndex; - } - - CommonType!Values front() @property - { - assert(!empty, "Attempting to fetch the front of an empty Only range"); - return this[0]; - } - - static if (canAssignElements) - { - void front(CommonType!Values value) @property - { - assert(!empty, "Attempting to assign the front of an empty Only range"); - this[0] = value; - } - } - - void popFront() - { - assert(!empty, "Attempting to popFront an empty Only range"); - ++frontIndex; - } - - CommonType!Values back() @property - { - assert(!empty, "Attempting to fetch the back of an empty Only range"); - return this[$ - 1]; - } - - static if (canAssignElements) - { - void back(CommonType!Values value) @property - { - assert(!empty, "Attempting to assign the back of an empty Only range"); - this[$ - 1] = value; - } - } - - void popBack() - { - assert(!empty, "Attempting to popBack an empty Only range"); - --backIndex; - } - - OnlyResult save() @property - { - return this; - } - - size_t length() const @property - { - return backIndex - frontIndex; - } - - alias opDollar = length; - - @trusted CommonType!Values opIndex(size_t idx) - { - // when i + idx points to elements popped - // with popBack - assert(idx < length, "Attempting to fetch an out of bounds index from an Only range"); - final switch (frontIndex + idx) - static foreach (i, T; Values) - case i: - return cast(T) values[i]; - } - - static if (canAssignElements) - { - void opIndexAssign(CommonType!Values value, size_t idx) - { - assert(idx < length, "Attempting to assign to an out of bounds index of an Only range"); - final switch (frontIndex + idx) - static foreach (i; 0 .. Values.length) - case i: - values[i] = value; - } - } - - OnlyResult opSlice() - { - return this; - } - - OnlyResult opSlice(size_t from, size_t to) - { - OnlyResult result = this; - result.frontIndex += from; - result.backIndex = this.frontIndex + to; - assert( - from <= to, - "Attempting to slice an Only range with a larger first argument than the second." - ); - assert( - to <= length, - "Attempting to slice using an out of bounds index on an Only range" - ); - return result; - } - - private size_t frontIndex = 0; - private size_t backIndex = 0; - - // https://issues.dlang.org/show_bug.cgi?id=10643 - version (none) - { - import std.traits : hasElaborateAssign; - static if (hasElaborateAssign!T) - private UnqualValues values; - else - private UnqualValues values = void; - } - else - // These may alias to shared or immutable data. Do not let the user - // to access these directly, and do not allow mutation without checking - // the qualifier. - private UnqualValues values; -} - -// Specialize for single-element results -private struct OnlyResult(T) -{ - import std.traits : isAssignable; - - @property T front() - { - assert(!empty, "Attempting to fetch the front of an empty Only range"); - return fetchFront(); - } - static if (isAssignable!T) - { - @property void front(T value) - { - assert(!empty, "Attempting to assign the front of an empty Only range"); - assignFront(value); - } - } - @property T back() - { - assert(!empty, "Attempting to fetch the back of an empty Only range"); - return fetchFront(); - } - static if (isAssignable!T) - { - @property void back(T value) - { - assert(!empty, "Attempting to assign the front of an empty Only range"); - assignFront(value); - } - } - @property bool empty() const { return _empty; } - @property size_t length() const { return !_empty; } - @property auto save() { return this; } - void popFront() - { - assert(!_empty, "Attempting to popFront an empty Only range"); - _empty = true; - } - void popBack() - { - assert(!_empty, "Attempting to popBack an empty Only range"); - _empty = true; - } - alias opDollar = length; - - // FIXME Workaround for https://issues.dlang.org/show_bug.cgi?id=24415 - import std.traits : hasElaborateCopyConstructor; - static if (hasElaborateCopyConstructor!T) - { - private static struct WorkaroundBugzilla24415 {} - public this()(WorkaroundBugzilla24415) {} - } - - private this()(return scope auto ref T value) - { - ref @trusted unqual(ref T x){return cast() x;} - // TODO: this calls the possible copy constructor without qualifiers. - // Find a way to initialize value using a qualified copy constructor. - this._value = unqual(value); - this._empty = false; - } - - T opIndex(size_t i) - { - assert(!_empty && i == 0, "Attempting to fetch an out of bounds index from an Only range"); - return fetchFront(); - } - - static if (isAssignable!T) - { - void opIndexAssign(T value, size_t i) - { - assert(!_empty && i == 0, "Attempting to assign an out of bounds index of an Only range"); - assignFront(value); - } - } - - OnlyResult opSlice() - { - return this; - } - - OnlyResult opSlice(size_t from, size_t to) - { - assert( - from <= to, - "Attempting to slice an Only range with a larger first argument than the second." - ); - assert( - to <= length, - "Attempting to slice using an out of bounds index on an Only range" - ); - OnlyResult copy = this; - copy._empty = _empty || from == to; - return copy; - } - - // This may alias to shared or immutable data. Do not let the user - // to access this directly, and do not allow mutation without checking - // the qualifier. - private Unqual!T _value; - private bool _empty = true; - private @trusted T fetchFront() - { - return *cast(T*)&_value; - } - static if (isAssignable!T) - { - private @trusted void assignFront(T newValue) - { - *cast(T*) &_value = newValue; - } - } -} - -/** -Assemble `values` into a range that carries all its -elements in-situ. - -Useful when a single value or multiple disconnected values -must be passed to an algorithm expecting a range, without -having to perform dynamic memory allocation. - -As copying the range means copying all elements, it can be -safely returned from functions. For the same reason, copying -the returned range may be expensive for a large number of arguments. - -Params: - values = the values to assemble together - -Returns: - A `RandomAccessRange` of the assembled values. - - The returned range can be sliced. Its elements can be assigned to if every - type in `Values` supports assignment from the range's element type. - -See_Also: $(LREF chain) to chain ranges - */ -auto only(Values...)(return scope Values values) -if (!is(CommonType!Values == void)) -{ - return OnlyResult!Values(values); -} - -/// ditto -auto only()() -{ - // cannot use noreturn due to https://issues.dlang.org/show_bug.cgi?id=22383 - struct EmptyElementType {} - EmptyElementType[] result; - return result; -} - -/// -@safe unittest -{ - import std.algorithm.comparison : equal; - import std.algorithm.iteration : filter, joiner, map; - import std.algorithm.searching : findSplitBefore; - import std.uni : isUpper; - - assert(equal(only('♡'), "♡")); - assert([1, 2, 3, 4].findSplitBefore(only(3))[0] == [1, 2]); - - assert(only("one", "two", "three").joiner(" ").equal("one two three")); - - string title = "The D Programming Language"; - assert(title - .filter!isUpper // take the upper case letters - .map!only // make each letter its own range - .joiner(".") // join the ranges together lazily - .equal("T.D.P.L")); -} - -// https://issues.dlang.org/show_bug.cgi?id=20314 -@safe unittest -{ - import std.algorithm.iteration : joiner; - - const string s = "foo", t = "bar"; - - assert([only(s, t), only(t, s)].joiner(only(", ")).join == "foobar, barfoo"); -} - -// Tests the zero-element result -@safe unittest -{ - import std.algorithm.comparison : equal; - - auto emptyRange = only(); - - alias EmptyRange = typeof(emptyRange); - static assert(isInputRange!EmptyRange); - static assert(isForwardRange!EmptyRange); - static assert(isBidirectionalRange!EmptyRange); - static assert(isRandomAccessRange!EmptyRange); - static assert(hasLength!EmptyRange); - static assert(hasSlicing!EmptyRange); - - assert(emptyRange.empty); - assert(emptyRange.length == 0); - assert(emptyRange.equal(emptyRange[])); - assert(emptyRange.equal(emptyRange.save)); - assert(emptyRange[0 .. 0].equal(emptyRange)); -} - -// Tests the single-element result -@safe unittest -{ - import std.algorithm.comparison : equal; - import std.typecons : tuple; - foreach (x; tuple(1, '1', 1.0, "1", [1])) - { - auto a = only(x); - typeof(x)[] e = []; - assert(a.front == x); - assert(a.back == x); - assert(!a.empty); - assert(a.length == 1); - assert(equal(a, a[])); - assert(equal(a, a[0 .. 1])); - assert(equal(a[0 .. 0], e)); - assert(equal(a[1 .. 1], e)); - assert(a[0] == x); - - auto b = a.save; - assert(equal(a, b)); - a.popFront(); - assert(a.empty && a.length == 0 && a[].empty); - b.popBack(); - assert(b.empty && b.length == 0 && b[].empty); - - alias A = typeof(a); - static assert(isInputRange!A); - static assert(isForwardRange!A); - static assert(isBidirectionalRange!A); - static assert(isRandomAccessRange!A); - static assert(hasLength!A); - static assert(hasSlicing!A); - } - - auto imm = only!(immutable int)(1); - immutable int[] imme = []; - assert(imm.front == 1); - assert(imm.back == 1); - assert(!imm.empty); - assert(imm.init.empty); // https://issues.dlang.org/show_bug.cgi?id=13441 - assert(imm.length == 1); - assert(equal(imm, imm[])); - assert(equal(imm, imm[0 .. 1])); - assert(equal(imm[0 .. 0], imme)); - assert(equal(imm[1 .. 1], imme)); - assert(imm[0] == 1); -} - -// Tests multiple-element results -@safe unittest -{ - import std.algorithm.comparison : equal; - import std.algorithm.iteration : joiner; - import std.meta : AliasSeq; - static assert(!__traits(compiles, only(1, "1"))); - - auto nums = only!(byte, uint, long)(1, 2, 3); - static assert(is(ElementType!(typeof(nums)) == long)); - assert(nums.length == 3); - - foreach (i; 0 .. 3) - assert(nums[i] == i + 1); - - auto saved = nums.save; - - foreach (i; 1 .. 4) - { - assert(nums.front == nums[0]); - assert(nums.front == i); - nums.popFront(); - assert(nums.length == 3 - i); - } - - assert(nums.empty); - - assert(saved.equal(only(1, 2, 3))); - assert(saved.equal(saved[])); - assert(saved[0 .. 1].equal(only(1))); - assert(saved[0 .. 2].equal(only(1, 2))); - assert(saved[0 .. 3].equal(saved)); - assert(saved[1 .. 3].equal(only(2, 3))); - assert(saved[2 .. 3].equal(only(3))); - assert(saved[0 .. 0].empty); - assert(saved[3 .. 3].empty); - - alias data = AliasSeq!("one", "two", "three", "four"); - static joined = - ["one two", "one two three", "one two three four"]; - string[] joinedRange = joined; - - static foreach (argCount; 2 .. 5) - {{ - auto values = only(data[0 .. argCount]); - alias Values = typeof(values); - static assert(is(ElementType!Values == string)); - static assert(isInputRange!Values); - static assert(isForwardRange!Values); - static assert(isBidirectionalRange!Values); - static assert(isRandomAccessRange!Values); - static assert(hasSlicing!Values); - static assert(hasLength!Values); - - assert(values.length == argCount); - assert(values[0 .. $].equal(values[0 .. values.length])); - assert(values.joiner(" ").equal(joinedRange.front)); - joinedRange.popFront(); - }} - - assert(saved.retro.equal(only(3, 2, 1))); - assert(saved.length == 3); - - assert(saved.back == 3); - saved.popBack(); - assert(saved.length == 2); - assert(saved.back == 2); - - assert(saved.front == 1); - saved.popFront(); - assert(saved.length == 1); - assert(saved.front == 2); - - saved.popBack(); - assert(saved.empty); - - auto imm = only!(immutable int, immutable int)(42, 24); - alias Imm = typeof(imm); - static assert(is(ElementType!Imm == immutable(int))); - assert(!imm.empty); - assert(imm.init.empty); // https://issues.dlang.org/show_bug.cgi?id=13441 - assert(imm.front == 42); - imm.popFront(); - assert(imm.front == 24); - imm.popFront(); - assert(imm.empty); - - static struct Test { int* a; } - immutable(Test) test; - cast(void) only(test, test); // Works with mutable indirection -} - -// https://issues.dlang.org/show_bug.cgi?id=21129 -@safe unittest -{ - auto range = () @safe { - const(char)[5] staticStr = "Hello"; - - // `only` must store a char[5] - not a char[]! - return only(staticStr, " World"); - } (); - - assert(range.join == "Hello World"); -} - -// https://issues.dlang.org/show_bug.cgi?id=21129 -@safe unittest -{ - struct AliasedString - { - const(char)[5] staticStr = "Hello"; - - @property const(char)[] slice() const - { - return staticStr[]; - } - alias slice this; - } - - auto range = () @safe { - auto hello = AliasedString(); - - // a copy of AliasedString is stored in the range. - return only(hello, " World"); - } (); - - assert(range.join == "Hello World"); -} - -// https://issues.dlang.org/show_bug.cgi?id=21022 -@safe pure nothrow unittest -{ - struct S - { - int* mem; - } - - immutable S x; - immutable(S)[] arr; - auto r1 = arr.chain(x.only, only(x, x)); -} - -// https://issues.dlang.org/show_bug.cgi?id=24382 -@safe unittest -{ - auto r1 = only(123); - r1.front = 456; - r1.back = 456; - r1[0] = 456; - - auto r2 = only(123, 456); - r2.front = 789; - r2.back = 789; - r2[0] = 789; - - auto r3 = only(1.23, 456); - // Can't assign double to int - static assert(!__traits(compiles, r3.front = 7.89)); - static assert(!__traits(compiles, r3.back = 7.89)); - // Workaround https://issues.dlang.org/show_bug.cgi?id=24383 - static assert(!__traits(compiles, () { r3[0] = 7.89; })); - // Can't assign type other than element type (even if compatible) - static assert(!__traits(compiles, r3.front = 789)); - static assert(!__traits(compiles, r3.back = 789)); - // Workaround https://issues.dlang.org/show_bug.cgi?id=24383 - static assert(!__traits(compiles, () { r3[0] = 789; })); -} - -/** -Iterate over `range` with an attached index variable. - -Each element is a $(REF Tuple, std,typecons) containing the index -and the element, in that order, where the index member is named `index` -and the element member is named `value`. - -The index starts at `start` and is incremented by one on every iteration. - -Overflow: - If `range` has length, then it is an error to pass a value for `start` - so that `start + range.length` is bigger than `Enumerator.max`, thus - it is ensured that overflow cannot happen. - - If `range` does not have length, and `popFront` is called when - `front.index == Enumerator.max`, the index will overflow and - continue from `Enumerator.min`. - -Params: - range = the $(REF_ALTTEXT input range, isInputRange, std,range,primitives) to attach indexes to - start = the number to start the index counter from - -Returns: - At minimum, an input range. All other range primitives are given in the - resulting range if `range` has them. The exceptions are the bidirectional - primitives, which are propagated only if `range` has length. - -Example: -Useful for using `foreach` with an index loop variable: ----- - import std.stdio : stdin, stdout; - import std.range : enumerate; - - foreach (lineNum, line; stdin.byLine().enumerate(1)) - stdout.writefln("line #%s: %s", lineNum, line); ----- -*/ -auto enumerate(Enumerator = size_t, Range)(Range range, Enumerator start = 0) -if (isIntegral!Enumerator && isInputRange!Range) -in -{ - static if (hasLength!Range) - { - // TODO: core.checkedint supports mixed signedness yet? - import core.checkedint : adds, addu; - import std.conv : ConvException, to; - import std.traits : isSigned, Largest, Signed; - - alias LengthType = typeof(range.length); - bool overflow; - static if (isSigned!Enumerator && isSigned!LengthType) - auto result = adds(start, range.length, overflow); - else static if (isSigned!Enumerator) - { - alias signed_t = Largest!(Enumerator, Signed!LengthType); - signed_t signedLength; - //This is to trick the compiler because if length is enum - //the compiler complains about unreachable code. - auto getLength() - { - return range.length; - } - //Can length fit in the signed type - assert(getLength() < signed_t.max, - "a signed length type is required but the range's length() is too great"); - signedLength = range.length; - auto result = adds(start, signedLength, overflow); - } - else - { - static if (isSigned!LengthType) - assert(range.length >= 0); - auto result = addu(start, range.length, overflow); - } - - assert(!overflow && result <= Enumerator.max); - } -} -do -{ - // TODO: Relax isIntegral!Enumerator to allow user-defined integral types - static struct Result - { - import std.typecons : Tuple; - - private: - alias ElemType = Tuple!(Enumerator, "index", ElementType!Range, "value"); - Range range; - Unqual!Enumerator index; - - public: - ElemType front() @property - { - assert(!range.empty, "Attempting to fetch the front of an empty enumerate"); - return typeof(return)(index, range.front); - } - - static if (isInfinite!Range) - enum bool empty = false; - else - { - bool empty() @property - { - return range.empty; - } - } - - void popFront() - { - assert(!range.empty, "Attempting to popFront an empty enumerate"); - range.popFront(); - ++index; // When !hasLength!Range, overflow is expected - } - - static if (isForwardRange!Range) - { - Result save() @property - { - return typeof(return)(range.save, index); - } - } - - static if (hasLength!Range) - { - mixin ImplementLength!range; - - static if (isBidirectionalRange!Range) - { - ElemType back() @property - { - assert(!range.empty, "Attempting to fetch the back of an empty enumerate"); - return typeof(return)(cast(Enumerator)(index + range.length - 1), range.back); - } - - void popBack() - { - assert(!range.empty, "Attempting to popBack an empty enumerate"); - range.popBack(); - } - } - } - - static if (isRandomAccessRange!Range) - { - ElemType opIndex(size_t i) - { - return typeof(return)(cast(Enumerator)(index + i), range[i]); - } - } - - static if (hasSlicing!Range) - { - static if (hasLength!Range) - { - Result opSlice(size_t i, size_t j) - { - return typeof(return)(range[i .. j], cast(Enumerator)(index + i)); - } - } - else - { - static struct DollarToken {} - enum opDollar = DollarToken.init; - - Result opSlice(size_t i, DollarToken) - { - return typeof(return)(range[i .. $], cast(Enumerator)(index + i)); - } - - auto opSlice(size_t i, size_t j) - { - return this[i .. $].takeExactly(j - 1); - } - } - } - } - - return Result(range, start); -} - -/// Can start enumeration from a negative position: -pure @safe nothrow unittest -{ - import std.array : assocArray; - import std.range : enumerate; - - bool[int] aa = true.repeat(3).enumerate(-1).assocArray(); - assert(aa[-1]); - assert(aa[0]); - assert(aa[1]); -} - -// Make sure passing qualified types works -pure @safe nothrow unittest -{ - char[4] v; - immutable start = 2; - v[2 .. $].enumerate(start); -} - -pure @safe nothrow unittest -{ - import std.internal.test.dummyrange : AllDummyRanges; - import std.meta : AliasSeq; - import std.typecons : tuple; - - static struct HasSlicing - { - typeof(this) front() @property { return typeof(this).init; } - bool empty() @property { return true; } - void popFront() {} - - typeof(this) opSlice(size_t, size_t) - { - return typeof(this)(); - } - } - - static foreach (DummyType; AliasSeq!(AllDummyRanges, HasSlicing)) - {{ - alias R = typeof(enumerate(DummyType.init)); - static assert(isInputRange!R); - static assert(isForwardRange!R == isForwardRange!DummyType); - static assert(isRandomAccessRange!R == isRandomAccessRange!DummyType); - static assert(!hasAssignableElements!R); - - static if (hasLength!DummyType) - { - static assert(hasLength!R); - static assert(isBidirectionalRange!R == - isBidirectionalRange!DummyType); - } - - static assert(hasSlicing!R == hasSlicing!DummyType); - }} - - static immutable values = ["zero", "one", "two", "three"]; - auto enumerated = values[].enumerate(); - assert(!enumerated.empty); - assert(enumerated.front == tuple(0, "zero")); - assert(enumerated.back == tuple(3, "three")); - - typeof(enumerated) saved = enumerated.save; - saved.popFront(); - assert(enumerated.front == tuple(0, "zero")); - assert(saved.front == tuple(1, "one")); - assert(saved.length == enumerated.length - 1); - saved.popBack(); - assert(enumerated.back == tuple(3, "three")); - assert(saved.back == tuple(2, "two")); - saved.popFront(); - assert(saved.front == tuple(2, "two")); - assert(saved.back == tuple(2, "two")); - saved.popFront(); - assert(saved.empty); - - size_t control = 0; - foreach (i, v; enumerated) - { - static assert(is(typeof(i) == size_t)); - static assert(is(typeof(v) == typeof(values[0]))); - assert(i == control); - assert(v == values[i]); - assert(tuple(i, v) == enumerated[i]); - ++control; - } - - assert(enumerated[0 .. $].front == tuple(0, "zero")); - assert(enumerated[$ - 1 .. $].front == tuple(3, "three")); - - foreach (i; 0 .. 10) - { - auto shifted = values[0 .. 2].enumerate(i); - assert(shifted.front == tuple(i, "zero")); - assert(shifted[0] == shifted.front); - - auto next = tuple(i + 1, "one"); - assert(shifted[1] == next); - shifted.popFront(); - assert(shifted.front == next); - shifted.popFront(); - assert(shifted.empty); - } - - static foreach (T; AliasSeq!(ubyte, byte, uint, int)) - {{ - auto inf = 42.repeat().enumerate(T.max); - alias Inf = typeof(inf); - static assert(isInfinite!Inf); - static assert(hasSlicing!Inf); - - // test overflow - assert(inf.front == tuple(T.max, 42)); - inf.popFront(); - assert(inf.front == tuple(T.min, 42)); - - // test slicing - inf = inf[42 .. $]; - assert(inf.front == tuple(T.min + 42, 42)); - auto window = inf[0 .. 2]; - assert(window.length == 1); - assert(window.front == inf.front); - window.popFront(); - assert(window.empty); - }} -} - -pure @safe unittest -{ - import std.algorithm.comparison : equal; - import std.meta : AliasSeq; - static immutable int[] values = [0, 1, 2, 3, 4]; - static foreach (T; AliasSeq!(ubyte, ushort, uint, ulong)) - {{ - auto enumerated = values.enumerate!T(); - static assert(is(typeof(enumerated.front.index) == T)); - assert(enumerated.equal(values[].zip(values))); - - foreach (T i; 0 .. 5) - { - auto subset = values[cast(size_t) i .. $]; - auto offsetEnumerated = subset.enumerate(i); - static assert(is(typeof(enumerated.front.index) == T)); - assert(offsetEnumerated.equal(subset.zip(subset))); - } - }} -} -@nogc @safe unittest -{ - const val = iota(1, 100).enumerate(1); -} -@nogc @safe unittest -{ - import core.exception : AssertError; - import std.exception : assertThrown; - struct RangePayload { - enum length = size_t.max; - void popFront() {} - int front() { return 0; } - bool empty() { return true; } - } - RangePayload thePayload; - //Assertion won't happen when contracts are disabled for -release. - debug assertThrown!AssertError(enumerate(thePayload, -10)); -} -// https://issues.dlang.org/show_bug.cgi?id=10939 -version (none) -{ - // Re-enable (or remove) if 10939 is resolved. - /+pure+/ @safe unittest // Impure because of std.conv.to - { - import core.exception : RangeError; - import std.exception : assertNotThrown, assertThrown; - import std.meta : AliasSeq; - - static immutable values = [42]; - - static struct SignedLengthRange - { - immutable(int)[] _values = values; - - int front() @property { assert(false); } - bool empty() @property { assert(false); } - void popFront() { assert(false); } - - int length() @property - { - return cast(int)_values.length; - } - } - - SignedLengthRange svalues; - static foreach (Enumerator; AliasSeq!(ubyte, byte, ushort, short, uint, int, ulong, long)) - { - assertThrown!RangeError(values[].enumerate!Enumerator(Enumerator.max)); - assertNotThrown!RangeError(values[].enumerate!Enumerator(Enumerator.max - values.length)); - assertThrown!RangeError(values[].enumerate!Enumerator(Enumerator.max - values.length + 1)); - - assertThrown!RangeError(svalues.enumerate!Enumerator(Enumerator.max)); - assertNotThrown!RangeError(svalues.enumerate!Enumerator(Enumerator.max - values.length)); - assertThrown!RangeError(svalues.enumerate!Enumerator(Enumerator.max - values.length + 1)); - } - - static foreach (Enumerator; AliasSeq!(byte, short, int)) - { - assertThrown!RangeError(repeat(0, uint.max).enumerate!Enumerator()); - } - - assertNotThrown!RangeError(repeat(0, uint.max).enumerate!long()); - } -} - -/** - Returns true if `fn` accepts variables of type T1 and T2 in any order. - The following code should compile: - --- - (ref T1 a, ref T2 b) - { - fn(a, b); - fn(b, a); - } - --- -*/ -template isTwoWayCompatible(alias fn, T1, T2) -{ - enum isTwoWayCompatible = is(typeof((ref T1 a, ref T2 b) - { - cast(void) fn(a, b); - cast(void) fn(b, a); - } - )); -} - -/// -@safe unittest -{ - void func1(int a, int b); - void func2(int a, float b); - - static assert(isTwoWayCompatible!(func1, int, int)); - static assert(isTwoWayCompatible!(func1, short, int)); - static assert(!isTwoWayCompatible!(func2, int, float)); - - void func3(ref int a, ref int b); - static assert( isTwoWayCompatible!(func3, int, int)); - static assert(!isTwoWayCompatible!(func3, short, int)); -} - - -/** - Policy used with the searching primitives `lowerBound`, $(D - upperBound), and `equalRange` of $(LREF SortedRange) below. - */ -enum SearchPolicy -{ - /** - Searches in a linear fashion. - */ - linear, - - /** - Searches with a step that is grows linearly (1, 2, 3,...) - leading to a quadratic search schedule (indexes tried are 0, 1, - 3, 6, 10, 15, 21, 28,...) Once the search overshoots its target, - the remaining interval is searched using binary search. The - search is completed in $(BIGOH sqrt(n)) time. Use it when you - are reasonably confident that the value is around the beginning - of the range. - */ - trot, - - /** - Performs a $(LINK2 https://en.wikipedia.org/wiki/Exponential_search, - galloping search algorithm), i.e. searches - with a step that doubles every time, (1, 2, 4, 8, ...) leading - to an exponential search schedule (indexes tried are 0, 1, 3, - 7, 15, 31, 63,...) Once the search overshoots its target, the - remaining interval is searched using binary search. A value is - found in $(BIGOH log(n)) time. - */ - gallop, - - /** - Searches using a classic interval halving policy. The search - starts in the middle of the range, and each search step cuts - the range in half. This policy finds a value in $(BIGOH log(n)) - time but is less cache friendly than `gallop` for large - ranges. The `binarySearch` policy is used as the last step - of `trot`, `gallop`, `trotBackwards`, and $(D - gallopBackwards) strategies. - */ - binarySearch, - - /** - Similar to `trot` but starts backwards. Use it when - confident that the value is around the end of the range. - */ - trotBackwards, - - /** - Similar to `gallop` but starts backwards. Use it when - confident that the value is around the end of the range. - */ - gallopBackwards -} - -/// -@safe unittest -{ - import std.algorithm.comparison : equal; - - auto a = assumeSorted([0, 1, 2, 3, 4, 5, 6, 7, 8, 9]); - auto p1 = a.upperBound!(SearchPolicy.binarySearch)(3); - assert(p1.equal([4, 5, 6, 7, 8, 9])); - - auto p2 = a.lowerBound!(SearchPolicy.gallop)(4); - assert(p2.equal([0, 1, 2, 3])); -} - -/** - Options for $(LREF SortedRange) ranges (below). -*/ -enum SortedRangeOptions -{ - /** - Assume, that the range is sorted without checking. - */ - assumeSorted, - - /** - All elements of the range are checked to be sorted. - The check is performed in O(n) time. - */ - checkStrictly, - - /** - Some elements of the range are checked to be sorted. - For ranges with random order, this will almost surely - detect, that it is not sorted. For almost sorted ranges - it's more likely to fail. The checked elements are choosen - in a deterministic manner, which makes this check reproducable. - The check is performed in O(log(n)) time. - */ - checkRoughly, -} - -/// -@safe pure unittest -{ - // create a SortedRange, that's checked strictly - SortedRange!(int[],"a < b", SortedRangeOptions.checkStrictly)([ 1, 3, 5, 7, 9 ]); -} - -/** - Represents a sorted range. In addition to the regular range - primitives, supports additional operations that take advantage of the - ordering, such as merge and binary search. To obtain a $(D - SortedRange) from an unsorted range `r`, use - $(REF sort, std,algorithm,sorting) which sorts `r` in place and returns the - corresponding `SortedRange`. To construct a `SortedRange` from a range - `r` that is known to be already sorted, use $(LREF assumeSorted). - - Params: - pred: The predicate used to define the sortedness - opt: Controls how strongly the range is checked for sortedness. - Will only be used for `RandomAccessRanges`. - Will not be used in CTFE. -*/ -struct SortedRange(Range, alias pred = "a < b", - SortedRangeOptions opt = SortedRangeOptions.assumeSorted) -if (isInputRange!Range && !isInstanceOf!(SortedRange, Range)) -{ - import std.functional : binaryFun; - - private alias predFun = binaryFun!pred; - private bool geq(L, R)(L lhs, R rhs) - { - return !predFun(lhs, rhs); - } - private bool gt(L, R)(L lhs, R rhs) - { - return predFun(rhs, lhs); - } - private Range _input; - - // Undocummented because a clearer way to invoke is by calling - // assumeSorted. - this(Range input) - { - static if (opt == SortedRangeOptions.checkRoughly) - { - roughlyVerifySorted(input); - } - static if (opt == SortedRangeOptions.checkStrictly) - { - strictlyVerifySorted(input); - } - this._input = input; - } - - // Assertion only. - static if (opt == SortedRangeOptions.checkRoughly) - private void roughlyVerifySorted(Range r) - { - if (!__ctfe) - { - static if (isRandomAccessRange!Range && hasLength!Range) - { - import core.bitop : bsr; - import std.algorithm.sorting : isSorted; - import std.exception : enforce; - - // Check the sortedness of the input - if (r.length < 2) return; - - immutable size_t msb = bsr(r.length) + 1; - assert(msb > 0 && msb <= r.length); - immutable step = r.length / msb; - auto st = stride(r, step); - - enforce(isSorted!pred(st), "Range is not sorted"); - } - } - } - - // Assertion only. - static if (opt == SortedRangeOptions.checkStrictly) - private void strictlyVerifySorted(Range r) - { - if (!__ctfe) - { - static if (isRandomAccessRange!Range && hasLength!Range) - { - import std.algorithm.sorting : isSorted; - import std.exception : enforce; - - enforce(isSorted!pred(r), "Range is not sorted"); - } - } - } - - /// Range primitives. - @property bool empty() //const - { - return this._input.empty; - } - - /// Ditto - static if (isForwardRange!Range) - @property auto save() - { - // Avoid the constructor - typeof(this) result = this; - result._input = _input.save; - return result; - } - - /// Ditto - @property auto ref front() - { - return _input.front; - } - - /// Ditto - void popFront() - { - _input.popFront(); - } - - /// Ditto - static if (isBidirectionalRange!Range) - { - @property auto ref back() - { - return _input.back; - } - - /// Ditto - void popBack() - { - _input.popBack(); - } - } - - /// Ditto - static if (isRandomAccessRange!Range) - auto ref opIndex(size_t i) - { - return _input[i]; - } - - /// Ditto - static if (hasSlicing!Range) - auto opSlice(size_t a, size_t b) return scope - { - assert( - a <= b, - "Attempting to slice a SortedRange with a larger first argument than the second." - ); - typeof(this) result = this; - result._input = _input[a .. b];// skip checking - return result; - } - - mixin ImplementLength!_input; - -/** - Releases the controlled range and returns it. - - This does the opposite of $(LREF assumeSorted): instead of turning a range - into a `SortedRange`, it extracts the original range back out of the `SortedRange` - using $(REF, move, std,algorithm,mutation). -*/ - auto release() return scope - { - import std.algorithm.mutation : move; - return move(_input); - } - - /// - static if (is(Range : int[])) - @safe unittest - { - import std.algorithm.sorting : sort; - int[3] data = [ 1, 2, 3 ]; - auto a = assumeSorted(data[]); - assert(a == sort!"a < b"(data[])); - int[] p = a.release(); - assert(p == [ 1, 2, 3 ]); - } - - // Assuming a predicate "test" that returns 0 for a left portion - // of the range and then 1 for the rest, returns the index at - // which the first 1 appears. Used internally by the search routines. - private size_t getTransitionIndex(SearchPolicy sp, alias test, V)(V v) - if (sp == SearchPolicy.binarySearch && isRandomAccessRange!Range && hasLength!Range) - { - size_t first = 0, count = _input.length; - while (count > 0) - { - immutable step = count / 2, it = first + step; - if (!test(_input[it], v)) - { - first = it + 1; - count -= step + 1; - } - else - { - count = step; - } - } - return first; - } - - // Specialization for trot and gallop - private size_t getTransitionIndex(SearchPolicy sp, alias test, V)(V v) - if ((sp == SearchPolicy.trot || sp == SearchPolicy.gallop) - && isRandomAccessRange!Range) - { - if (empty || test(front, v)) return 0; - immutable count = length; - if (count == 1) return 1; - size_t below = 0, above = 1, step = 2; - while (!test(_input[above], v)) - { - // Still too small, update below and increase gait - below = above; - immutable next = above + step; - if (next >= count) - { - // Overshot - the next step took us beyond the end. So - // now adjust next and simply exit the loop to do the - // binary search thingie. - above = count; - break; - } - // Still in business, increase step and continue - above = next; - static if (sp == SearchPolicy.trot) - ++step; - else - step <<= 1; - } - return below + this[below .. above].getTransitionIndex!( - SearchPolicy.binarySearch, test, V)(v); - } - - // Specialization for trotBackwards and gallopBackwards - private size_t getTransitionIndex(SearchPolicy sp, alias test, V)(V v) - if ((sp == SearchPolicy.trotBackwards || sp == SearchPolicy.gallopBackwards) - && isRandomAccessRange!Range) - { - immutable count = length; - if (empty || !test(back, v)) return count; - if (count == 1) return 0; - size_t below = count - 2, above = count - 1, step = 2; - while (test(_input[below], v)) - { - // Still too large, update above and increase gait - above = below; - if (below < step) - { - // Overshot - the next step took us beyond the end. So - // now adjust next and simply fall through to do the - // binary search thingie. - below = 0; - break; - } - // Still in business, increase step and continue - below -= step; - static if (sp == SearchPolicy.trot) - ++step; - else - step <<= 1; - } - return below + this[below .. above].getTransitionIndex!( - SearchPolicy.binarySearch, test, V)(v); - } - -// lowerBound -/** - This function uses a search with policy `sp` to find the - largest left subrange on which $(D pred(x, value)) is `true` for - all `x` (e.g., if `pred` is "less than", returns the portion of - the range with elements strictly smaller than `value`). The search - schedule and its complexity are documented in - $(LREF SearchPolicy). -*/ - auto lowerBound(SearchPolicy sp = SearchPolicy.binarySearch, V)(V value) - if (isTwoWayCompatible!(predFun, ElementType!Range, V) - && hasSlicing!Range) - { - return this[0 .. getTransitionIndex!(sp, geq)(value)]; - } - - /// - static if (is(Range : int[])) - @safe unittest - { - import std.algorithm.comparison : equal; - auto a = assumeSorted([ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 ]); - auto p = a.lowerBound(4); - assert(equal(p, [ 0, 1, 2, 3 ])); - } - -// upperBound -/** -This function searches with policy `sp` to find the largest right -subrange on which $(D pred(value, x)) is `true` for all `x` -(e.g., if `pred` is "less than", returns the portion of the range -with elements strictly greater than `value`). The search schedule -and its complexity are documented in $(LREF SearchPolicy). - -For ranges that do not offer random access, `SearchPolicy.linear` -is the only policy allowed (and it must be specified explicitly lest it exposes -user code to unexpected inefficiencies). For random-access searches, all -policies are allowed, and `SearchPolicy.binarySearch` is the default. -*/ - auto upperBound(SearchPolicy sp = SearchPolicy.binarySearch, V)(V value) - if (isTwoWayCompatible!(predFun, ElementType!Range, V)) - { - static assert(hasSlicing!Range || sp == SearchPolicy.linear, - "Specify SearchPolicy.linear explicitly for " - ~ typeof(this).stringof); - static if (sp == SearchPolicy.linear) - { - for (; !_input.empty && !predFun(value, _input.front); - _input.popFront()) - { - } - return this; - } - else - { - return this[getTransitionIndex!(sp, gt)(value) .. length]; - } - } - - /// - static if (is(Range : int[])) - @safe unittest - { - import std.algorithm.comparison : equal; - auto a = assumeSorted([ 1, 2, 3, 3, 3, 4, 4, 5, 6 ]); - auto p = a.upperBound(3); - assert(equal(p, [4, 4, 5, 6])); - } - - -// equalRange -/** - Returns the subrange containing all elements `e` for which both $(D - pred(e, value)) and $(D pred(value, e)) evaluate to `false` (e.g., - if `pred` is "less than", returns the portion of the range with - elements equal to `value`). Uses a classic binary search with - interval halving until it finds a value that satisfies the condition, - then uses `SearchPolicy.gallopBackwards` to find the left boundary - and `SearchPolicy.gallop` to find the right boundary. These - policies are justified by the fact that the two boundaries are likely - to be near the first found value (i.e., equal ranges are relatively - small). Completes the entire search in $(BIGOH log(n)) time. -*/ - auto equalRange(V)(V value) - if (isTwoWayCompatible!(predFun, ElementType!Range, V) - && isRandomAccessRange!Range) - { - size_t first = 0, count = _input.length; - while (count > 0) - { - immutable step = count / 2; - auto it = first + step; - if (predFun(_input[it], value)) - { - // Less than value, bump left bound up - first = it + 1; - count -= step + 1; - } - else if (predFun(value, _input[it])) - { - // Greater than value, chop count - count = step; - } - else - { - // Equal to value, do binary searches in the - // leftover portions - // Gallop towards the left end as it's likely nearby - immutable left = first - + this[first .. it] - .lowerBound!(SearchPolicy.gallopBackwards)(value).length; - first += count; - // Gallop towards the right end as it's likely nearby - immutable right = first - - this[it + 1 .. first] - .upperBound!(SearchPolicy.gallop)(value).length; - return this[left .. right]; - } - } - return this.init; - } - - /// - static if (is(Range : int[])) - @safe unittest - { - import std.algorithm.comparison : equal; - auto a = [ 1, 2, 3, 3, 3, 4, 4, 5, 6 ]; - auto r = a.assumeSorted.equalRange(3); - assert(equal(r, [ 3, 3, 3 ])); - } - -// trisect -/** -Returns a tuple `r` such that `r[0]` is the same as the result -of `lowerBound(value)`, `r[1]` is the same as the result of $(D -equalRange(value)), and `r[2]` is the same as the result of $(D -upperBound(value)). The call is faster than computing all three -separately. Uses a search schedule similar to $(D -equalRange). Completes the entire search in $(BIGOH log(n)) time. -*/ - auto trisect(V)(V value) - if (isTwoWayCompatible!(predFun, ElementType!Range, V) - && isRandomAccessRange!Range && hasLength!Range) - { - import std.typecons : tuple; - size_t first = 0, count = _input.length; - while (count > 0) - { - immutable step = count / 2; - auto it = first + step; - if (predFun(_input[it], value)) - { - // Less than value, bump left bound up - first = it + 1; - count -= step + 1; - } - else if (predFun(value, _input[it])) - { - // Greater than value, chop count - count = step; - } - else - { - // Equal to value, do binary searches in the - // leftover portions - // Gallop towards the left end as it's likely nearby - immutable left = first - + this[first .. it] - .lowerBound!(SearchPolicy.gallopBackwards)(value).length; - first += count; - // Gallop towards the right end as it's likely nearby - immutable right = first - - this[it + 1 .. first] - .upperBound!(SearchPolicy.gallop)(value).length; - return tuple(this[0 .. left], this[left .. right], - this[right .. length]); - } - } - // No equal element was found - return tuple(this[0 .. first], this.init, this[first .. length]); - } - - /// - static if (is(Range : int[])) - @safe unittest - { - import std.algorithm.comparison : equal; - auto a = [ 1, 2, 3, 3, 3, 4, 4, 5, 6 ]; - auto r = assumeSorted(a).trisect(3); - assert(equal(r[0], [ 1, 2 ])); - assert(equal(r[1], [ 3, 3, 3 ])); - assert(equal(r[2], [ 4, 4, 5, 6 ])); - } - -// contains -/** -Returns `true` if and only if `value` can be found in $(D -range), which is assumed to be sorted. Performs $(BIGOH log(r.length)) -evaluations of `pred`. - */ - - bool contains(V)(V value) - if (isRandomAccessRange!Range) - { - if (empty) return false; - immutable i = getTransitionIndex!(SearchPolicy.binarySearch, geq)(value); - if (i >= length) return false; - return !predFun(value, _input[i]); - } - -/** -Like `contains`, but the value is specified before the range. -*/ - bool opBinaryRight(string op, V)(V value) - if (op == "in" && isRandomAccessRange!Range) - { - return contains(value); - } - -// groupBy -/** -Returns a range of subranges of elements that are equivalent according to the -sorting relation. - */ - auto groupBy()() - { - import std.algorithm.iteration : chunkBy; - return _input.chunkBy!((a, b) => !predFun(a, b) && !predFun(b, a)); - } -} - -/// ditto -template SortedRange(Range, alias pred = "a < b", - SortedRangeOptions opt = SortedRangeOptions.assumeSorted) -if (isInstanceOf!(SortedRange, Range)) -{ - // Avoid nesting SortedRange types (see https://issues.dlang.org/show_bug.cgi?id=18933); - alias SortedRange = SortedRange!(Unqual!(typeof(Range._input)), pred, opt); -} - -/// -@safe unittest -{ - import std.algorithm.sorting : sort; - auto a = [ 1, 2, 3, 42, 52, 64 ]; - auto r = assumeSorted(a); - assert(r.contains(3)); - assert(!(32 in r)); - auto r1 = sort!"a > b"(a); - assert(3 in r1); - assert(!r1.contains(32)); - assert(r1.release() == [ 64, 52, 42, 3, 2, 1 ]); -} - -/** -`SortedRange` could accept ranges weaker than random-access, but it -is unable to provide interesting functionality for them. Therefore, -`SortedRange` is currently restricted to random-access ranges. - -No copy of the original range is ever made. If the underlying range is -changed concurrently with its corresponding `SortedRange` in ways -that break its sorted-ness, `SortedRange` will work erratically. -*/ -@safe unittest -{ - import std.algorithm.mutation : swap; - auto a = [ 1, 2, 3, 42, 52, 64 ]; - auto r = assumeSorted(a); - assert(r.contains(42)); - swap(a[3], a[5]); // illegal to break sortedness of original range - assert(!r.contains(42)); // passes although it shouldn't -} - -/** -`SortedRange` can be searched with predicates that do not take -two elements of the underlying range as arguments. - -This is useful, if a range of structs is sorted by a member and you -want to search in that range by only providing a value for that member. - -*/ -@safe unittest -{ - import std.algorithm.comparison : equal; - static struct S { int i; } - static bool byI(A, B)(A a, B b) - { - static if (is(A == S)) - return a.i < b; - else - return a < b.i; - } - auto r = assumeSorted!byI([S(1), S(2), S(3)]); - auto lessThanTwo = r.lowerBound(2); - assert(equal(lessThanTwo, [S(1)])); -} - -@safe unittest -{ - import std.exception : assertThrown, assertNotThrown; - - assertNotThrown(SortedRange!(int[])([ 1, 3, 10, 5, 7 ])); - assertThrown(SortedRange!(int[],"a < b", SortedRangeOptions.checkStrictly)([ 1, 3, 10, 5, 7 ])); - - // these two checks are implementation depended - assertNotThrown(SortedRange!(int[],"a < b", SortedRangeOptions.checkRoughly)([ 1, 3, 10, 5, 12, 2 ])); - assertThrown(SortedRange!(int[],"a < b", SortedRangeOptions.checkRoughly)([ 1, 3, 10, 5, 2, 12 ])); -} - -@safe unittest -{ - import std.algorithm.comparison : equal; - - auto a = [ 10, 20, 30, 30, 30, 40, 40, 50, 60 ]; - auto r = assumeSorted(a).trisect(30); - assert(equal(r[0], [ 10, 20 ])); - assert(equal(r[1], [ 30, 30, 30 ])); - assert(equal(r[2], [ 40, 40, 50, 60 ])); - - r = assumeSorted(a).trisect(35); - assert(equal(r[0], [ 10, 20, 30, 30, 30 ])); - assert(r[1].empty); - assert(equal(r[2], [ 40, 40, 50, 60 ])); -} - -@safe unittest -{ - import std.algorithm.comparison : equal; - auto a = [ "A", "AG", "B", "E", "F" ]; - auto r = assumeSorted!"cmp(a,b) < 0"(a).trisect("B"w); - assert(equal(r[0], [ "A", "AG" ])); - assert(equal(r[1], [ "B" ])); - assert(equal(r[2], [ "E", "F" ])); - r = assumeSorted!"cmp(a,b) < 0"(a).trisect("A"d); - assert(r[0].empty); - assert(equal(r[1], [ "A" ])); - assert(equal(r[2], [ "AG", "B", "E", "F" ])); -} - -@safe unittest -{ - import std.algorithm.comparison : equal; - static void test(SearchPolicy pol)() - { - auto a = [ 1, 2, 3, 42, 52, 64 ]; - auto r = assumeSorted(a); - assert(equal(r.lowerBound(42), [1, 2, 3])); - - assert(equal(r.lowerBound!(pol)(42), [1, 2, 3])); - assert(equal(r.lowerBound!(pol)(41), [1, 2, 3])); - assert(equal(r.lowerBound!(pol)(43), [1, 2, 3, 42])); - assert(equal(r.lowerBound!(pol)(51), [1, 2, 3, 42])); - assert(equal(r.lowerBound!(pol)(3), [1, 2])); - assert(equal(r.lowerBound!(pol)(55), [1, 2, 3, 42, 52])); - assert(equal(r.lowerBound!(pol)(420), a)); - assert(equal(r.lowerBound!(pol)(0), a[0 .. 0])); - - assert(equal(r.upperBound!(pol)(42), [52, 64])); - assert(equal(r.upperBound!(pol)(41), [42, 52, 64])); - assert(equal(r.upperBound!(pol)(43), [52, 64])); - assert(equal(r.upperBound!(pol)(51), [52, 64])); - assert(equal(r.upperBound!(pol)(53), [64])); - assert(equal(r.upperBound!(pol)(55), [64])); - assert(equal(r.upperBound!(pol)(420), a[0 .. 0])); - assert(equal(r.upperBound!(pol)(0), a)); - } - - test!(SearchPolicy.trot)(); - test!(SearchPolicy.gallop)(); - test!(SearchPolicy.trotBackwards)(); - test!(SearchPolicy.gallopBackwards)(); - test!(SearchPolicy.binarySearch)(); -} - -@safe unittest -{ - // Check for small arrays - int[] a; - auto r = assumeSorted(a); - a = [ 1 ]; - r = assumeSorted(a); - a = [ 1, 2 ]; - r = assumeSorted(a); - a = [ 1, 2, 3 ]; - r = assumeSorted(a); -} - -@safe unittest -{ - import std.algorithm.mutation : swap; - auto a = [ 1, 2, 3, 42, 52, 64 ]; - auto r = assumeSorted(a); - assert(r.contains(42)); - swap(a[3], a[5]); // illegal to break sortedness of original range - assert(!r.contains(42)); // passes although it shouldn't -} - -@betterC @nogc nothrow @safe unittest -{ - static immutable(int)[] arr = [ 1, 2, 3 ]; - auto s = assumeSorted(arr); -} - -@system unittest -{ - import std.algorithm.comparison : equal; - int[] arr = [100, 101, 102, 200, 201, 300]; - auto s = assumeSorted!((a, b) => a / 100 < b / 100)(arr); - assert(s.groupBy.equal!equal([[100, 101, 102], [200, 201], [300]])); -} - -// Test on an input range -@system unittest -{ - import std.conv : text; - import std.file : exists, remove, tempDir; - import std.path : buildPath; - import std.stdio : File; - import std.uuid : randomUUID; - auto name = buildPath(tempDir(), "test.std.range.line-" ~ text(__LINE__) ~ - "." ~ randomUUID().toString()); - auto f = File(name, "w"); - scope(exit) if (exists(name)) remove(name); - // write a sorted range of lines to the file - f.write("abc\ndef\nghi\njkl"); - f.close(); - f.open(name, "r"); - auto r = assumeSorted(f.byLine()); - auto r1 = r.upperBound!(SearchPolicy.linear)("def"); - assert(r1.front == "ghi", r1.front); - f.close(); -} - -// https://issues.dlang.org/show_bug.cgi?id=19337 -@safe unittest -{ - import std.algorithm.sorting : sort; - auto a = [ 1, 2, 3, 42, 52, 64 ]; - a.sort.sort!"a > b"; -} - -/** -Assumes `r` is sorted by predicate `pred` and returns the -corresponding $(D SortedRange!(pred, R)) having `r` as support. -To check for sorted-ness at -cost $(BIGOH n), use $(REF isSorted, std,algorithm,sorting). - */ -auto assumeSorted(alias pred = "a < b", R)(R r) -if (isInputRange!(Unqual!R)) -{ - // Avoid senseless `SortedRange!(SortedRange!(...), pred)` nesting. - static if (is(R == SortedRange!(RRange, RPred), RRange, alias RPred)) - { - static if (isInputRange!R && __traits(isSame, pred, RPred)) - // If the predicate is the same and we don't need to cast away - // constness for the result to be an input range. - return r; - else - return SortedRange!(Unqual!(typeof(r._input)), pred)(r._input); - } - else - { - return SortedRange!(Unqual!R, pred)(r); - } -} - -/// -@safe unittest -{ - import std.algorithm.comparison : equal; - - int[] a = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]; - auto p = assumeSorted(a); - - assert(equal(p.lowerBound(4), [0, 1, 2, 3])); - assert(equal(p.lowerBound(5), [0, 1, 2, 3, 4])); - assert(equal(p.lowerBound(6), [0, 1, 2, 3, 4, 5])); - assert(equal(p.lowerBound(6.9), [0, 1, 2, 3, 4, 5, 6])); -} - -@safe unittest -{ - import std.algorithm.comparison : equal; - static assert(isRandomAccessRange!(SortedRange!(int[]))); - int[] a = [ 1, 2, 3, 3, 3, 4, 4, 5, 6 ]; - auto p = assumeSorted(a).upperBound(3); - assert(equal(p, [4, 4, 5, 6 ])); - p = assumeSorted(a).upperBound(4.2); - assert(equal(p, [ 5, 6 ])); - - // https://issues.dlang.org/show_bug.cgi?id=18933 - // don't create senselessly nested SortedRange types. - assert(is(typeof(assumeSorted(a)) == typeof(assumeSorted(assumeSorted(a))))); - assert(is(typeof(assumeSorted(a)) == typeof(assumeSorted(assumeSorted!"a > b"(a))))); -} - -@safe unittest -{ - import std.algorithm.comparison : equal; - import std.conv : text; - - int[] a = [ 1, 2, 3, 3, 3, 4, 4, 5, 6 ]; - auto p = assumeSorted(a).equalRange(3); - assert(equal(p, [ 3, 3, 3 ]), text(p)); - p = assumeSorted(a).equalRange(4); - assert(equal(p, [ 4, 4 ]), text(p)); - p = assumeSorted(a).equalRange(2); - assert(equal(p, [ 2 ])); - p = assumeSorted(a).equalRange(0); - assert(p.empty); - p = assumeSorted(a).equalRange(7); - assert(p.empty); - p = assumeSorted(a).equalRange(3.0); - assert(equal(p, [ 3, 3, 3])); -} - -@safe unittest -{ - int[] a = [ 1, 2, 3, 3, 3, 4, 4, 5, 6 ]; - if (a.length) - { - auto b = a[a.length / 2]; - //auto r = sort(a); - //assert(r.contains(b)); - } -} - -@safe unittest -{ - auto a = [ 5, 7, 34, 345, 677 ]; - auto r = assumeSorted(a); - a = null; - r = assumeSorted(a); - a = [ 1 ]; - r = assumeSorted(a); -} - -// https://issues.dlang.org/show_bug.cgi?id=15003 -@nogc @safe unittest -{ - static immutable a = [1, 2, 3, 4]; - auto r = a.assumeSorted; -} - -/++ - Wrapper which effectively makes it possible to pass a range by reference. - Both the original range and the RefRange will always have the exact same - elements. Any operation done on one will affect the other. So, for instance, - if it's passed to a function which would implicitly copy the original range - if it were passed to it, the original range is $(I not) copied but is - consumed as if it were a reference type. - - Note: - `save` works as normal and operates on a new range, so if - `save` is ever called on the `RefRange`, then no operations on the - saved range will affect the original. - - Params: - range = the range to construct the `RefRange` from - - Returns: - A `RefRange`. If the given range is a class type - (and thus is already a reference type), then the original - range is returned rather than a `RefRange`. - +/ -struct RefRange(R) -if (isInputRange!R) -{ -public: - - /++ +/ - this(R* range) @safe pure nothrow - { - _range = range; - } - - - /++ - This does not assign the pointer of `rhs` to this `RefRange`. - Rather it assigns the range pointed to by `rhs` to the range pointed - to by this `RefRange`. This is because $(I any) operation on a - `RefRange` is the same is if it occurred to the original range. The - one exception is when a `RefRange` is assigned `null` either - directly or because `rhs` is `null`. In that case, `RefRange` - no longer refers to the original range but is `null`. - +/ - auto opAssign(RefRange rhs) - { - if (_range && rhs._range) - *_range = *rhs._range; - else - _range = rhs._range; - - return this; - } - - /++ +/ - void opAssign(typeof(null) rhs) - { - _range = null; - } - - - /++ - A pointer to the wrapped range. - +/ - @property inout(R*) ptr() @safe inout pure nothrow - { - return _range; - } - - - version (StdDdoc) - { - /++ +/ - @property auto front() {assert(0);} - /++ Ditto +/ - @property auto front() const {assert(0);} - /++ Ditto +/ - @property auto front(ElementType!R value) {assert(0);} - } - else - { - @property auto front() - { - return (*_range).front; - } - - static if (is(typeof((*(cast(const R*)_range)).front))) @property auto front() const - { - return (*_range).front; - } - - static if (is(typeof((*_range).front = (*_range).front))) @property auto front(ElementType!R value) - { - return (*_range).front = value; - } - } - - - version (StdDdoc) - { - @property bool empty(); /// - @property bool empty() const; ///Ditto - } - else static if (isInfinite!R) - enum empty = false; - else - { - @property bool empty() - { - return (*_range).empty; - } - - static if (is(typeof((*cast(const R*)_range).empty))) @property bool empty() const - { - return (*_range).empty; - } - } - - - /++ +/ - void popFront() - { - return (*_range).popFront(); - } - - - version (StdDdoc) - { - /++ - Only defined if `isForwardRange!R` is `true`. - +/ - @property auto save() {assert(0);} - /++ Ditto +/ - @property auto save() const {assert(0);} - /++ Ditto +/ - auto opSlice() {assert(0);} - /++ Ditto +/ - auto opSlice() const {assert(0);} - } - else static if (isForwardRange!R) - { - import std.traits : isSafe; - private alias S = typeof((*_range).save); - - static if (is(typeof((*cast(const R*)_range).save))) - private alias CS = typeof((*cast(const R*)_range).save); - - static if (isSafe!((R* r) => (*r).save)) - { - @property RefRange!S save() @trusted - { - mixin(_genSave()); - } - - static if (is(typeof((*cast(const R*)_range).save))) @property RefRange!CS save() @trusted const - { - mixin(_genSave()); - } - } - else - { - @property RefRange!S save() - { - mixin(_genSave()); - } - - static if (is(typeof((*cast(const R*)_range).save))) @property RefRange!CS save() const - { - mixin(_genSave()); - } - } - - auto opSlice()() - { - return save; - } - - auto opSlice()() const - { - return save; - } - - private static string _genSave() @safe pure nothrow - { - return `import core.lifetime : emplace;` ~ - `alias S = typeof((*_range).save);` ~ - `static assert(isForwardRange!S, S.stringof ~ " is not a forward range.");` ~ - `auto mem = new void[S.sizeof];` ~ - `emplace!S(mem, cast(S)(*_range).save);` ~ - `return RefRange!S(cast(S*) mem.ptr);`; - } - - static assert(isForwardRange!RefRange); - } - - - version (StdDdoc) - { - /++ - Only defined if `isBidirectionalRange!R` is `true`. - +/ - @property auto back() {assert(0);} - /++ Ditto +/ - @property auto back() const {assert(0);} - /++ Ditto +/ - @property auto back(ElementType!R value) {assert(0);} - } - else static if (isBidirectionalRange!R) - { - @property auto back() - { - return (*_range).back; - } - - static if (is(typeof((*(cast(const R*)_range)).back))) @property auto back() const - { - return (*_range).back; - } - - static if (is(typeof((*_range).back = (*_range).back))) @property auto back(ElementType!R value) - { - return (*_range).back = value; - } - } - - - /++ Ditto +/ - static if (isBidirectionalRange!R) void popBack() - { - return (*_range).popBack(); - } - - - version (StdDdoc) - { - /++ - Only defined if `isRandomAccessRange!R` is `true`. - +/ - auto ref opIndex(IndexType)(IndexType index) {assert(0);} - - /++ Ditto +/ - auto ref opIndex(IndexType)(IndexType index) const {assert(0);} - } - else static if (isRandomAccessRange!R) - { - auto ref opIndex(IndexType)(IndexType index) - if (is(typeof((*_range)[index]))) - { - return (*_range)[index]; - } - - auto ref opIndex(IndexType)(IndexType index) const - if (is(typeof((*cast(const R*)_range)[index]))) - { - return (*_range)[index]; - } - } - - - /++ - Only defined if `hasMobileElements!R` and `isForwardRange!R` are - `true`. - +/ - static if (hasMobileElements!R && isForwardRange!R) auto moveFront() - { - return (*_range).moveFront(); - } - - - /++ - Only defined if `hasMobileElements!R` and `isBidirectionalRange!R` - are `true`. - +/ - static if (hasMobileElements!R && isBidirectionalRange!R) auto moveBack() - { - return (*_range).moveBack(); - } - - - /++ - Only defined if `hasMobileElements!R` and `isRandomAccessRange!R` - are `true`. - +/ - static if (hasMobileElements!R && isRandomAccessRange!R) auto moveAt(size_t index) - { - return (*_range).moveAt(index); - } - - - version (StdDdoc) - { - /// Only defined if `hasLength!R` is `true`. - @property size_t length(); - /// ditto - @property size_t length() const; - /// Ditto - alias opDollar = length; - } - else static if (hasLength!R) - { - @property auto length() - { - return (*_range).length; - } - static if (is(typeof((*cast(const R*)_range).length))) @property auto length() const - { - return (*_range).length; - } - alias opDollar = length; - } - - - version (StdDdoc) - { - /++ - Only defined if `hasSlicing!R` is `true`. - +/ - auto opSlice(IndexType1, IndexType2) - (IndexType1 begin, IndexType2 end) {assert(0);} - - /++ Ditto +/ - auto opSlice(IndexType1, IndexType2) - (IndexType1 begin, IndexType2 end) const {assert(0);} - } - else static if (hasSlicing!R) - { - private alias T = typeof((*_range)[1 .. 2]); - static if (is(typeof((*cast(const R*)_range)[1 .. 2]))) - { - private alias CT = typeof((*cast(const R*)_range)[1 .. 2]); - } - - RefRange!T opSlice(IndexType1, IndexType2) - (IndexType1 begin, IndexType2 end) - if (is(typeof((*_range)[begin .. end]))) - { - mixin(_genOpSlice()); - } - - RefRange!CT opSlice(IndexType1, IndexType2) - (IndexType1 begin, IndexType2 end) const - if (is(typeof((*cast(const R*)_range)[begin .. end]))) - { - mixin(_genOpSlice()); - } - - private static string _genOpSlice() @safe pure nothrow - { - return `import core.lifetime : emplace;` ~ - `alias S = typeof((*_range)[begin .. end]);` ~ - `static assert(hasSlicing!S, S.stringof ~ " is not sliceable.");` ~ - `auto mem = new void[S.sizeof];` ~ - `emplace!S(mem, cast(S)(*_range)[begin .. end]);` ~ - `return RefRange!S(cast(S*) mem.ptr);`; - } - } - - -private: - - R* _range; -} - -/// Basic Example -@system unittest -{ - import std.algorithm.searching : find; - ubyte[] buffer = [1, 9, 45, 12, 22]; - auto found1 = find(buffer, 45); - assert(found1 == [45, 12, 22]); - assert(buffer == [1, 9, 45, 12, 22]); - - auto wrapped1 = refRange(&buffer); - auto found2 = find(wrapped1, 45); - assert(*found2.ptr == [45, 12, 22]); - assert(buffer == [45, 12, 22]); - - auto found3 = find(wrapped1.save, 22); - assert(*found3.ptr == [22]); - assert(buffer == [45, 12, 22]); - - string str = "hello world"; - auto wrappedStr = refRange(&str); - assert(str.front == 'h'); - str.popFrontN(5); - assert(str == " world"); - assert(wrappedStr.front == ' '); - assert(*wrappedStr.ptr == " world"); -} - -/// opAssign Example. -@system unittest -{ - ubyte[] buffer1 = [1, 2, 3, 4, 5]; - ubyte[] buffer2 = [6, 7, 8, 9, 10]; - auto wrapped1 = refRange(&buffer1); - auto wrapped2 = refRange(&buffer2); - assert(wrapped1.ptr is &buffer1); - assert(wrapped2.ptr is &buffer2); - assert(wrapped1.ptr !is wrapped2.ptr); - assert(buffer1 != buffer2); - - wrapped1 = wrapped2; - - //Everything points to the same stuff as before. - assert(wrapped1.ptr is &buffer1); - assert(wrapped2.ptr is &buffer2); - assert(wrapped1.ptr !is wrapped2.ptr); - - //But buffer1 has changed due to the assignment. - assert(buffer1 == [6, 7, 8, 9, 10]); - assert(buffer2 == [6, 7, 8, 9, 10]); - - buffer2 = [11, 12, 13, 14, 15]; - - //Everything points to the same stuff as before. - assert(wrapped1.ptr is &buffer1); - assert(wrapped2.ptr is &buffer2); - assert(wrapped1.ptr !is wrapped2.ptr); - - //But buffer2 has changed due to the assignment. - assert(buffer1 == [6, 7, 8, 9, 10]); - assert(buffer2 == [11, 12, 13, 14, 15]); - - wrapped2 = null; - - //The pointer changed for wrapped2 but not wrapped1. - assert(wrapped1.ptr is &buffer1); - assert(wrapped2.ptr is null); - assert(wrapped1.ptr !is wrapped2.ptr); - - //buffer2 is not affected by the assignment. - assert(buffer1 == [6, 7, 8, 9, 10]); - assert(buffer2 == [11, 12, 13, 14, 15]); -} - -@system unittest -{ - import std.algorithm.iteration : filter; - { - ubyte[] buffer = [1, 2, 3, 4, 5]; - auto wrapper = refRange(&buffer); - auto p = wrapper.ptr; - auto f = wrapper.front; - wrapper.front = f; - auto e = wrapper.empty; - wrapper.popFront(); - auto s = wrapper.save; - auto b = wrapper.back; - wrapper.back = b; - wrapper.popBack(); - auto i = wrapper[0]; - wrapper.moveFront(); - wrapper.moveBack(); - wrapper.moveAt(0); - auto l = wrapper.length; - auto sl = wrapper[0 .. 1]; - assert(wrapper[0 .. $].length == buffer[0 .. $].length); - } - - { - ubyte[] buffer = [1, 2, 3, 4, 5]; - const wrapper = refRange(&buffer); - const p = wrapper.ptr; - const f = wrapper.front; - const e = wrapper.empty; - const s = wrapper.save; - const b = wrapper.back; - const i = wrapper[0]; - const l = wrapper.length; - const sl = wrapper[0 .. 1]; - } - - { - ubyte[] buffer = [1, 2, 3, 4, 5]; - auto filtered = filter!"true"(buffer); - auto wrapper = refRange(&filtered); - auto p = wrapper.ptr; - auto f = wrapper.front; - wrapper.front = f; - auto e = wrapper.empty; - wrapper.popFront(); - auto s = wrapper.save; - wrapper.moveFront(); - } - - { - ubyte[] buffer = [1, 2, 3, 4, 5]; - auto filtered = filter!"true"(buffer); - const wrapper = refRange(&filtered); - const p = wrapper.ptr; - - //Cannot currently be const. filter needs to be updated to handle const. - /+ - const f = wrapper.front; - const e = wrapper.empty; - const s = wrapper.save; - +/ - } - - { - string str = "hello world"; - auto wrapper = refRange(&str); - auto p = wrapper.ptr; - auto f = wrapper.front; - auto e = wrapper.empty; - wrapper.popFront(); - auto s = wrapper.save; - auto b = wrapper.back; - wrapper.popBack(); - } - - { - // https://issues.dlang.org/show_bug.cgi?id=16534 - // opDollar should be defined if the wrapped range defines length. - auto range = 10.iota.takeExactly(5); - auto wrapper = refRange(&range); - assert(wrapper.length == 5); - assert(wrapper[0 .. $ - 1].length == 4); - } -} - -//Test assignment. -@system unittest -{ - ubyte[] buffer1 = [1, 2, 3, 4, 5]; - ubyte[] buffer2 = [6, 7, 8, 9, 10]; - RefRange!(ubyte[]) wrapper1; - RefRange!(ubyte[]) wrapper2 = refRange(&buffer2); - assert(wrapper1.ptr is null); - assert(wrapper2.ptr is &buffer2); - - wrapper1 = refRange(&buffer1); - assert(wrapper1.ptr is &buffer1); - - wrapper1 = wrapper2; - assert(wrapper1.ptr is &buffer1); - assert(buffer1 == buffer2); - - wrapper1 = RefRange!(ubyte[]).init; - assert(wrapper1.ptr is null); - assert(wrapper2.ptr is &buffer2); - assert(buffer1 == buffer2); - assert(buffer1 == [6, 7, 8, 9, 10]); - - wrapper2 = null; - assert(wrapper2.ptr is null); - assert(buffer2 == [6, 7, 8, 9, 10]); -} - -@system unittest -{ - import std.algorithm.comparison : equal; - import std.algorithm.mutation : bringToFront; - import std.algorithm.searching : commonPrefix, find, until; - import std.algorithm.sorting : sort; - - //Test that ranges are properly consumed. - { - int[] arr = [1, 42, 2, 41, 3, 40, 4, 42, 9]; - auto wrapper = refRange(&arr); - - assert(*find(wrapper, 41).ptr == [41, 3, 40, 4, 42, 9]); - assert(arr == [41, 3, 40, 4, 42, 9]); - - assert(*drop(wrapper, 2).ptr == [40, 4, 42, 9]); - assert(arr == [40, 4, 42, 9]); - - assert(equal(until(wrapper, 42), [40, 4])); - assert(arr == [42, 9]); - - assert(find(wrapper, 12).empty); - assert(arr.empty); - } - - { - string str = "Hello, world-like object."; - auto wrapper = refRange(&str); - - assert(*find(wrapper, "l").ptr == "llo, world-like object."); - assert(str == "llo, world-like object."); - - assert(equal(take(wrapper, 5), "llo, ")); - assert(str == "world-like object."); - } - - //Test that operating on saved ranges does not consume the original. - { - int[] arr = [1, 42, 2, 41, 3, 40, 4, 42, 9]; - auto wrapper = refRange(&arr); - auto saved = wrapper.save; - saved.popFrontN(3); - assert(*saved.ptr == [41, 3, 40, 4, 42, 9]); - assert(arr == [1, 42, 2, 41, 3, 40, 4, 42, 9]); - } - - { - string str = "Hello, world-like object."; - auto wrapper = refRange(&str); - auto saved = wrapper.save; - saved.popFrontN(13); - assert(*saved.ptr == "like object."); - assert(str == "Hello, world-like object."); - } - - //Test that functions which use save work properly. - { - int[] arr = [1, 42]; - auto wrapper = refRange(&arr); - assert(equal(commonPrefix(wrapper, [1, 27]), [1])); - } - - { - int[] arr = [4, 5, 6, 7, 1, 2, 3]; - auto wrapper = refRange(&arr); - assert(bringToFront(wrapper[0 .. 4], wrapper[4 .. arr.length]) == 3); - assert(arr == [1, 2, 3, 4, 5, 6, 7]); - } - - //Test bidirectional functions. - { - int[] arr = [1, 42, 2, 41, 3, 40, 4, 42, 9]; - auto wrapper = refRange(&arr); - - assert(wrapper.back == 9); - assert(arr == [1, 42, 2, 41, 3, 40, 4, 42, 9]); - - wrapper.popBack(); - assert(arr == [1, 42, 2, 41, 3, 40, 4, 42]); - } - - { - string str = "Hello, world-like object."; - auto wrapper = refRange(&str); - - assert(wrapper.back == '.'); - assert(str == "Hello, world-like object."); - - wrapper.popBack(); - assert(str == "Hello, world-like object"); - } - - //Test random access functions. - { - int[] arr = [1, 42, 2, 41, 3, 40, 4, 42, 9]; - auto wrapper = refRange(&arr); - - assert(wrapper[2] == 2); - assert(arr == [1, 42, 2, 41, 3, 40, 4, 42, 9]); - - assert(*wrapper[3 .. 6].ptr != null, [41, 3, 40]); - assert(arr == [1, 42, 2, 41, 3, 40, 4, 42, 9]); - } - - //Test move functions. - { - int[] arr = [1, 42, 2, 41, 3, 40, 4, 42, 9]; - auto wrapper = refRange(&arr); - - auto t1 = wrapper.moveFront(); - auto t2 = wrapper.moveBack(); - wrapper.front = t2; - wrapper.back = t1; - assert(arr == [9, 42, 2, 41, 3, 40, 4, 42, 1]); - - sort(wrapper.save); - assert(arr == [1, 2, 3, 4, 9, 40, 41, 42, 42]); - } -} - -@system unittest -{ - struct S - { - @property int front() @safe const pure nothrow { return 0; } - enum bool empty = false; - void popFront() @safe pure nothrow { } - @property auto save() @safe pure nothrow return scope { return this; } - } - - S s; - auto wrapper = refRange(&s); - static assert(isInfinite!(typeof(wrapper))); -} - -@system unittest -{ - class C - { - @property int front() @safe const pure nothrow { return 0; } - @property bool empty() @safe const pure nothrow { return false; } - void popFront() @safe pure nothrow { } - @property auto save() @safe pure nothrow return scope { return this; } - } - static assert(isForwardRange!C); - - auto c = new C; - auto cWrapper = refRange(&c); - static assert(is(typeof(cWrapper) == C)); - assert(cWrapper is c); -} - -// https://issues.dlang.org/show_bug.cgi?id=14373 -@system unittest -{ - static struct R - { - @property int front() {return 0;} - void popFront() {empty = true;} - bool empty = false; - } - R r; - refRange(&r).popFront(); - assert(r.empty); -} - -// https://issues.dlang.org/show_bug.cgi?id=14575 -@system unittest -{ - struct R - { - Object front; - alias back = front; - bool empty = false; - void popFront() {empty = true;} - alias popBack = popFront; - @property R save() {return this;} - } - static assert(isBidirectionalRange!R); - R r; - auto rr = refRange(&r); - - struct R2 - { - @property Object front() {return null;} - @property const(Object) front() const {return null;} - alias back = front; - bool empty = false; - void popFront() {empty = true;} - alias popBack = popFront; - @property R2 save() {return this;} - } - static assert(isBidirectionalRange!R2); - R2 r2; - auto rr2 = refRange(&r2); -} - -/// ditto -auto refRange(R)(R* range) -if (isInputRange!R) -{ - static if (!is(R == class)) - return RefRange!R(range); - else - return *range; -} - -// https://issues.dlang.org/show_bug.cgi?id=9060 -@safe unittest -{ - import std.algorithm.iteration : map, joiner, group; - import std.algorithm.searching : until; - // fix for std.algorithm - auto r = map!(x => 0)([1]); - chain(r, r); - zip(r, r); - roundRobin(r, r); - - struct NRAR { - typeof(r) input; - @property empty() { return input.empty; } - @property front() { return input.front; } - void popFront() { input.popFront(); } - @property save() { return NRAR(input.save); } - } - auto n1 = NRAR(r); - cycle(n1); // non random access range version - - assumeSorted(r); - - // fix for std.range - joiner([r], [9]); - - struct NRAR2 { - NRAR input; - @property empty() { return true; } - @property front() { return input; } - void popFront() { } - @property save() { return NRAR2(input.save); } - } - auto n2 = NRAR2(n1); - joiner(n2); - - group(r); - - until(r, 7); - static void foo(R)(R r) { until!(x => x > 7)(r); } - foo(r); -} - -private struct Bitwise(R) -if (isInputRange!R && isIntegral!(ElementType!R)) -{ - import std.traits : Unsigned; -private: - alias ElemType = ElementType!R; - alias UnsignedElemType = Unsigned!ElemType; - - R parent; - enum bitsNum = ElemType.sizeof * 8; - size_t maskPos = 1; - - static if (isBidirectionalRange!R) - { - size_t backMaskPos = bitsNum; - } - -public: - this()(auto ref R range) - { - parent = range; - } - - static if (isInfinite!R) - { - enum empty = false; - } - else - { - /** - * Check if the range is empty - * - * Returns: a boolean true or false - */ - bool empty() - { - static if (hasLength!R) - { - return length == 0; - } - else static if (isBidirectionalRange!R) - { - if (parent.empty) - { - return true; - } - else - { - /* - If we have consumed the last element of the range both from - the front and the back, then the masks positions will overlap - */ - return parent.save.dropOne.empty && (maskPos > backMaskPos); - } - } - else - { - /* - If we consumed the last element of the range, but not all the - bits in the last element - */ - return parent.empty; - } - } - } - - bool front() - { - assert(!empty); - return (parent.front & mask(maskPos)) != 0; - } - - void popFront() - { - assert(!empty); - ++maskPos; - if (maskPos > bitsNum) - { - parent.popFront; - maskPos = 1; - } - } - - static if (hasLength!R) - { - size_t length() - { - auto len = parent.length * bitsNum - (maskPos - 1); - static if (isBidirectionalRange!R) - { - len -= bitsNum - backMaskPos; - } - return len; - } - - alias opDollar = length; - } - - static if (isForwardRange!R) - { - typeof(this) save() - { - auto result = this; - result.parent = parent.save; - return result; - } - } - - static if (isBidirectionalRange!R) - { - bool back() - { - assert(!empty); - return (parent.back & mask(backMaskPos)) != 0; - } - - void popBack() - { - assert(!empty); - --backMaskPos; - if (backMaskPos == 0) - { - parent.popBack; - backMaskPos = bitsNum; - } - } - } - - static if (isRandomAccessRange!R) - { - /** - Return the `n`th bit within the range - */ - bool opIndex(size_t n) - in - { - /* - If it does not have the length property, it means that R is - an infinite range - */ - static if (hasLength!R) - { - assert(n < length, "Index out of bounds"); - } - } - do - { - immutable size_t remainingBits = bitsNum - maskPos + 1; - // If n >= maskPos, then the bit sign will be 1, otherwise 0 - immutable ptrdiff_t sign = (remainingBits - n - 1) >> (ptrdiff_t.sizeof * 8 - 1); - /* - By truncating n with remainingBits bits we have skipped the - remaining bits in parent[0], so we need to add 1 to elemIndex. - - Because bitsNum is a power of 2, n / bitsNum == n >> bitsNum.bsf - */ - import core.bitop : bsf; - immutable size_t elemIndex = sign * (((n - remainingBits) >> bitsNum.bsf) + 1); - - /* - Since the indexing is from LSB to MSB, we need to index at the - remainder of (n - remainingBits). - - Because bitsNum is a power of 2, n % bitsNum == n & (bitsNum - 1) - */ - immutable size_t elemMaskPos = (sign ^ 1) * (maskPos + n) - + sign * (1 + ((n - remainingBits) & (bitsNum - 1))); - - return (parent[elemIndex] & mask(elemMaskPos)) != 0; - } - - static if (hasAssignableElements!R) - { - /** - Assigns `flag` to the `n`th bit within the range - */ - void opIndexAssign(bool flag, size_t n) - in - { - static if (hasLength!R) - { - assert(n < length, "Index out of bounds"); - } - } - do - { - import core.bitop : bsf; - - immutable size_t remainingBits = bitsNum - maskPos + 1; - immutable ptrdiff_t sign = (remainingBits - n - 1) >> (ptrdiff_t.sizeof * 8 - 1); - immutable size_t elemIndex = sign * (((n - remainingBits) >> bitsNum.bsf) + 1); - immutable size_t elemMaskPos = (sign ^ 1) * (maskPos + n) - + sign * (1 + ((n - remainingBits) & (bitsNum - 1))); - - auto elem = parent[elemIndex]; - auto elemMask = mask(elemMaskPos); - parent[elemIndex] = cast(UnsignedElemType)(flag * (elem | elemMask) - + (flag ^ 1) * (elem & ~elemMask)); - } - } - - Bitwise!R opSlice() - { - return this.save; - } - - Bitwise!R opSlice(size_t start, size_t end) - in - { - assert(start < end, "Invalid bounds: end <= start"); - } - do - { - import core.bitop : bsf; - - size_t remainingBits = bitsNum - maskPos + 1; - ptrdiff_t sign = (remainingBits - start - 1) >> (ptrdiff_t.sizeof * 8 - 1); - immutable size_t startElemIndex = sign * (((start - remainingBits) >> bitsNum.bsf) + 1); - immutable size_t startElemMaskPos = (sign ^ 1) * (maskPos + start) - + sign * (1 + ((start - remainingBits) & (bitsNum - 1))); - - immutable size_t sliceLen = end - start - 1; - remainingBits = bitsNum - startElemMaskPos + 1; - sign = (remainingBits - sliceLen - 1) >> (ptrdiff_t.sizeof * 8 - 1); - immutable size_t endElemIndex = startElemIndex - + sign * (((sliceLen - remainingBits) >> bitsNum.bsf) + 1); - immutable size_t endElemMaskPos = (sign ^ 1) * (startElemMaskPos + sliceLen) - + sign * (1 + ((sliceLen - remainingBits) & (bitsNum - 1))); - - typeof(return) result; - // Get the slice to be returned from the parent - result.parent = (parent[startElemIndex .. endElemIndex + 1]).save; - result.maskPos = startElemMaskPos; - static if (isBidirectionalRange!R) - { - result.backMaskPos = endElemMaskPos; - } - return result; - } - } - -private: - auto mask(size_t maskPos) - { - return (1UL << (maskPos - 1UL)); - } -} - -/** -Bitwise adapter over an integral type range. Consumes the range elements bit by -bit, from the least significant bit to the most significant bit. - -Params: - R = an integral $(REF_ALTTEXT input range, isInputRange, std,range,primitives) to iterate over - range = range to consume bit by by - -Returns: - A `Bitwise` input range with propagated forward, bidirectional - and random access capabilities -*/ -auto bitwise(R)(auto ref R range) -if (isInputRange!R && isIntegral!(ElementType!R)) -{ - return Bitwise!R(range); -} - -/// -@safe pure unittest -{ - import std.algorithm.comparison : equal; - import std.format : format; - - // 00000011 00001001 - ubyte[] arr = [3, 9]; - auto r = arr.bitwise; - - // iterate through it as with any other range - assert(format("%(%d%)", r) == "1100000010010000"); - assert(format("%(%d%)", r.retro).equal("1100000010010000".retro)); - - auto r2 = r[5 .. $]; - // set a bit - r[2] = 1; - assert(arr[0] == 7); - assert(r[5] == r2[0]); -} - -/// You can use bitwise to implement an uniform bool generator -@safe unittest -{ - import std.algorithm.comparison : equal; - import std.random : rndGen; - - auto rb = rndGen.bitwise; - static assert(isInfinite!(typeof(rb))); - - auto rb2 = rndGen.bitwise; - // Don't forget that structs are passed by value - assert(rb.take(10).equal(rb2.take(10))); -} - -// Test nogc inference -@safe @nogc unittest -{ - static ubyte[] arr = [3, 9]; - auto bw = arr.bitwise; - auto bw2 = bw[]; - auto bw3 = bw[8 .. $]; - bw3[2] = true; - - assert(arr[1] == 13); - assert(bw[$ - 6]); - assert(bw[$ - 6] == bw2[$ - 6]); - assert(bw[$ - 6] == bw3[$ - 6]); -} - -// Test all range types over all integral types -@safe pure nothrow unittest -{ - import std.meta : AliasSeq; - import std.internal.test.dummyrange; - - alias IntegralTypes = AliasSeq!(byte, ubyte, short, ushort, int, uint, - long, ulong); - foreach (IntegralType; IntegralTypes) - { - foreach (T; AllDummyRangesType!(IntegralType[])) - { - T a; - auto bw = Bitwise!T(a); - - static if (isForwardRange!T) - { - auto bwFwdSave = bw.save; - } - - static if (isBidirectionalRange!T) - { - auto bwBack = bw.save; - auto bwBackSave = bw.save; - } - - static if (hasLength!T) - { - auto bwLength = bw.length; - assert(bw.length == (IntegralType.sizeof * 8 * a.length)); - static if (isForwardRange!T) - { - assert(bw.length == bwFwdSave.length); - } - } - - // Make sure front and back are not the mechanisms that modify the range - long numCalls = 42; - bool initialFrontValue; - - if (!bw.empty) - { - initialFrontValue = bw.front; - } - - while (!bw.empty && (--numCalls)) - { - bw.front; - assert(bw.front == initialFrontValue); - } - - /* - Check that empty works properly and that popFront does not get called - more times than it should - */ - numCalls = 0; - while (!bw.empty) - { - ++numCalls; - - static if (hasLength!T) - { - assert(bw.length == bwLength); - --bwLength; - } - - static if (isForwardRange!T) - { - assert(bw.front == bwFwdSave.front); - bwFwdSave.popFront(); - } - - static if (isBidirectionalRange!T) - { - assert(bwBack.front == bwBackSave.front); - bwBack.popBack(); - bwBackSave.popBack(); - } - bw.popFront(); - } - - auto rangeLen = numCalls / (IntegralType.sizeof * 8); - assert(numCalls == (IntegralType.sizeof * 8 * rangeLen)); - assert(bw.empty); - static if (isForwardRange!T) - { - assert(bwFwdSave.empty); - } - - static if (isBidirectionalRange!T) - { - assert(bwBack.empty); - } - } - } -} - -// Test opIndex and opSlice -@system unittest -{ - import std.meta : AliasSeq; - alias IntegralTypes = AliasSeq!(byte, ubyte, short, ushort, int, uint, - long, ulong); - foreach (IntegralType; IntegralTypes) - { - size_t bitsNum = IntegralType.sizeof * 8; - - auto first = cast(IntegralType)(1); - - // 2 ^ (bitsNum - 1) - auto second = cast(IntegralType)(cast(IntegralType)(1) << (bitsNum - 2)); - - IntegralType[] a = [first, second]; - auto bw = Bitwise!(IntegralType[])(a); - - // Check against lsb of a[0] - assert(bw[0] == true); - // Check against msb - 1 of a[1] - assert(bw[2 * bitsNum - 2] == true); - - bw.popFront(); - assert(bw[2 * bitsNum - 3] == true); - - import std.exception : assertThrown; - - version (D_NoBoundsChecks) {} - else - { - // Check out of bounds error - assertThrown!Error(bw[2 * bitsNum - 1]); - } - - bw[2] = true; - assert(bw[2] == true); - bw.popFront(); - assert(bw[1] == true); - - auto bw2 = bw[0 .. $ - 5]; - auto bw3 = bw2[]; - assert(bw2.length == (bw.length - 5)); - assert(bw2.length == bw3.length); - bw2.popFront(); - assert(bw2.length != bw3.length); - } -} - -/********************************* - * An OutputRange that discards the data it receives. - */ -struct NullSink -{ - void put(E)(scope const E) pure @safe @nogc nothrow {} -} - -/// ditto -auto ref nullSink() -{ - static NullSink sink; - return sink; -} - -/// -@safe nothrow unittest -{ - import std.algorithm.iteration : map; - import std.algorithm.mutation : copy; - [4, 5, 6].map!(x => x * 2).copy(nullSink); // data is discarded -} - -/// -@safe unittest -{ - import std.csv : csvNextToken; - - string line = "a,b,c"; - - // ignore the first column - line.csvNextToken(nullSink, ',', '"'); - line.popFront; - - // look at the second column - Appender!string app; - line.csvNextToken(app, ',', '"'); - assert(app.data == "b"); -} - -@safe unittest -{ - auto r = 10.iota - .tee(nullSink) - .dropOne; - - assert(r.front == 1); -} - -/++ - - Implements a "tee" style pipe, wrapping an input range so that elements of the - range can be passed to a provided function or $(LREF OutputRange) as they are - iterated over. This is useful for printing out intermediate values in a long - chain of range code, performing some operation with side-effects on each call - to `front` or `popFront`, or diverting the elements of a range into an - auxiliary $(LREF OutputRange). - - It is important to note that as the resultant range is evaluated lazily, - in the case of the version of `tee` that takes a function, the function - will not actually be executed until the range is "walked" using functions - that evaluate ranges, such as $(REF array, std,array) or - $(REF fold, std,algorithm,iteration). - - Params: - pipeOnPop = If `Yes.pipeOnPop`, simply iterating the range without ever - calling `front` is enough to have `tee` mirror elements to `outputRange` (or, - respectively, `fun`). Note that each `popFront()` call will mirror the - old `front` value, not the new one. This means that the last value will - not be forwarded if the range isn't iterated until empty. If - `No.pipeOnPop`, only elements for which `front` does get called will be - also sent to `outputRange`/`fun`. If `front` is called twice for the same - element, it will still be sent only once. If this caching is undesired, - consider using $(REF map, std,algorithm,iteration) instead. - inputRange = The input range being passed through. - outputRange = This range will receive elements of `inputRange` progressively - as iteration proceeds. - fun = This function will be called with elements of `inputRange` - progressively as iteration proceeds. - - Returns: - An input range that offers the elements of `inputRange`. Regardless of - whether `inputRange` is a more powerful range (forward, bidirectional etc), - the result is always an input range. Reading this causes `inputRange` to be - iterated and returns its elements in turn. In addition, the same elements - will be passed to `outputRange` or `fun` as well. - - See_Also: $(REF each, std,algorithm,iteration) -+/ -auto tee(Flag!"pipeOnPop" pipeOnPop = Yes.pipeOnPop, R1, R2)(R1 inputRange, R2 outputRange) -if (isInputRange!R1 && isOutputRange!(R2, ElementType!R1)) -{ - static struct Result - { - private R1 _input; - private R2 _output; - static if (!pipeOnPop) - { - private bool _frontAccessed; - } - - mixin ImplementLength!_input; - - static if (isInfinite!R1) - { - enum bool empty = false; - } - else - { - @property bool empty() { return _input.empty; } - } - - void popFront() - { - assert(!_input.empty, "Attempting to popFront an empty tee"); - static if (pipeOnPop) - { - put(_output, _input.front); - } - else - { - _frontAccessed = false; - } - _input.popFront(); - } - - @property auto ref front() - { - assert(!_input.empty, "Attempting to fetch the front of an empty tee"); - static if (!pipeOnPop) - { - if (!_frontAccessed) - { - _frontAccessed = true; - put(_output, _input.front); - } - } - return _input.front; - } - } - - return Result(inputRange, outputRange); -} - -/// Ditto -auto tee(alias fun, Flag!"pipeOnPop" pipeOnPop = Yes.pipeOnPop, R1)(R1 inputRange) -if (is(typeof(fun) == void) || isSomeFunction!fun) -{ - import std.traits : isDelegate, isFunctionPointer; - /* - Distinguish between function literals and template lambdas - when using either as an $(LREF OutputRange). Since a template - has no type, typeof(template) will always return void. - If it's a template lambda, it's first necessary to instantiate - it with `ElementType!R1`. - */ - static if (is(typeof(fun) == void)) - alias _fun = fun!(ElementType!R1); - else - alias _fun = fun; - - static if (isFunctionPointer!_fun || isDelegate!_fun) - { - return tee!pipeOnPop(inputRange, _fun); - } - else - { - return tee!pipeOnPop(inputRange, &_fun); - } -} - -/// -@safe unittest -{ - import std.algorithm.comparison : equal; - import std.algorithm.iteration : filter, map; - - // Sum values while copying - int[] values = [1, 4, 9, 16, 25]; - int sum = 0; - auto newValues = values.tee!(a => sum += a).array; - assert(equal(newValues, values)); - assert(sum == 1 + 4 + 9 + 16 + 25); - - // Count values that pass the first filter - int count = 0; - auto newValues4 = values.filter!(a => a < 10) - .tee!(a => count++) - .map!(a => a + 1) - .filter!(a => a < 10); - - //Fine, equal also evaluates any lazy ranges passed to it. - //count is not 3 until equal evaluates newValues4 - assert(equal(newValues4, [2, 5])); - assert(count == 3); -} - -// -@safe unittest -{ - import std.algorithm.comparison : equal; - import std.algorithm.iteration : filter, map; - - int[] values = [1, 4, 9, 16, 25]; - - int count = 0; - auto newValues = values.filter!(a => a < 10) - .tee!(a => count++, No.pipeOnPop) - .map!(a => a + 1) - .filter!(a => a < 10); - - auto val = newValues.front; - assert(count == 1); - //front is only evaluated once per element - val = newValues.front; - assert(count == 1); - - //popFront() called, fun will be called - //again on the next access to front - newValues.popFront(); - newValues.front; - assert(count == 2); - - int[] preMap = new int[](3), postMap = []; - auto mappedValues = values.filter!(a => a < 10) - //Note the two different ways of using tee - .tee(preMap) - .map!(a => a + 1) - .tee!(a => postMap ~= a) - .filter!(a => a < 10); - assert(equal(mappedValues, [2, 5])); - assert(equal(preMap, [1, 4, 9])); - assert(equal(postMap, [2, 5, 10])); -} - -// -@safe unittest -{ - import std.algorithm.comparison : equal; - import std.algorithm.iteration : filter, map; - - char[] txt = "Line one, Line 2".dup; - - bool isVowel(dchar c) - { - import std.string : indexOf; - return "AaEeIiOoUu".indexOf(c) != -1; - } - - int vowelCount = 0; - int shiftedCount = 0; - auto removeVowels = txt.tee!(c => isVowel(c) ? vowelCount++ : 0) - .filter!(c => !isVowel(c)) - .map!(c => (c == ' ') ? c : c + 1) - .tee!(c => isVowel(c) ? shiftedCount++ : 0); - assert(equal(removeVowels, "Mo o- Mo 3")); - assert(vowelCount == 6); - assert(shiftedCount == 3); -} - -@safe unittest -{ - // Manually stride to test different pipe behavior. - void testRange(Range)(Range r) - { - const int strideLen = 3; - int i = 0; - ElementType!Range elem1; - ElementType!Range elem2; - while (!r.empty) - { - if (i % strideLen == 0) - { - //Make sure front is only - //evaluated once per item - elem1 = r.front; - elem2 = r.front; - assert(elem1 == elem2); - } - r.popFront(); - i++; - } - } - - string txt = "abcdefghijklmnopqrstuvwxyz"; - - int popCount = 0; - auto pipeOnPop = txt.tee!(a => popCount++); - testRange(pipeOnPop); - assert(popCount == 26); - - int frontCount = 0; - auto pipeOnFront = txt.tee!(a => frontCount++, No.pipeOnPop); - testRange(pipeOnFront); - assert(frontCount == 9); -} - -@safe unittest -{ - import std.algorithm.comparison : equal; - import std.meta : AliasSeq; - - //Test diverting elements to an OutputRange - string txt = "abcdefghijklmnopqrstuvwxyz"; - - dchar[] asink1 = []; - auto fsink = (dchar c) { asink1 ~= c; }; - auto result1 = txt.tee(fsink).array; - assert(equal(txt, result1) && (equal(result1, asink1))); - - dchar[] _asink1 = []; - auto _result1 = txt.tee!((dchar c) { _asink1 ~= c; })().array; - assert(equal(txt, _result1) && (equal(_result1, _asink1))); - - dchar[] asink2 = new dchar[](txt.length); - void fsink2(dchar c) { static int i = 0; asink2[i] = c; i++; } - auto result2 = txt.tee(&fsink2).array; - assert(equal(txt, result2) && equal(result2, asink2)); - - dchar[] asink3 = new dchar[](txt.length); - auto result3 = txt.tee(asink3).array; - assert(equal(txt, result3) && equal(result3, asink3)); - - static foreach (CharType; AliasSeq!(char, wchar, dchar)) - {{ - auto appSink = appender!(CharType[])(); - auto appResult = txt.tee(appSink).array; - assert(equal(txt, appResult) && equal(appResult, appSink.data)); - }} - - static foreach (StringType; AliasSeq!(string, wstring, dstring)) - {{ - auto appSink = appender!StringType(); - auto appResult = txt.tee(appSink).array; - assert(equal(txt, appResult) && equal(appResult, appSink.data)); - }} -} - -// https://issues.dlang.org/show_bug.cgi?id=13483 -@safe unittest -{ - static void func1(T)(T x) {} - void func2(int x) {} - - auto r = [1, 2, 3, 4].tee!func1.tee!func2; -} - -/** -Extends the length of the input range `r` by padding out the start of the -range with the element `e`. The element `e` must be of a common type with -the element type of the range `r` as defined by $(REF CommonType, std, traits). -If `n` is less than the length of of `r`, then `r` is returned unmodified. - -If `r` is a string with Unicode characters in it, `padLeft` follows D's rules -about length for strings, which is not the number of characters, or -graphemes, but instead the number of encoding units. If you want to treat each -grapheme as only one encoding unit long, then call -$(REF byGrapheme, std, uni) before calling this function. - -If `r` has a length, then this is $(BIGOH 1). Otherwise, it's $(BIGOH r.length). - -Params: - r = an $(REF_ALTTEXT input range, isInputRange, std,range,primitives) with a length, or a forward range - e = element to pad the range with - n = the length to pad to - -Returns: - A range containing the elements of the original range with the extra padding - -See Also: - $(REF leftJustifier, std, string) -*/ -auto padLeft(R, E)(R r, E e, size_t n) -if ( - ((isInputRange!R && hasLength!R) || isForwardRange!R) && - !is(CommonType!(ElementType!R, E) == void) -) -{ - static if (hasLength!R) - auto dataLength = r.length; - else - auto dataLength = r.save.walkLength(n); - - return e.repeat(n > dataLength ? n - dataLength : 0).chain(r); -} - -/// -@safe pure unittest -{ - import std.algorithm.comparison : equal; - - assert([1, 2, 3, 4].padLeft(0, 6).equal([0, 0, 1, 2, 3, 4])); - assert([1, 2, 3, 4].padLeft(0, 3).equal([1, 2, 3, 4])); - - assert("abc".padLeft('_', 6).equal("___abc")); -} - -@safe pure nothrow unittest -{ - import std.algorithm.comparison : equal; - import std.internal.test.dummyrange : DummyRange, Length, RangeType, ReturnBy; - import std.meta : AliasSeq; - - alias DummyRanges = AliasSeq!( - DummyRange!(ReturnBy.Reference, Length.Yes, RangeType.Input), - DummyRange!(ReturnBy.Reference, Length.Yes, RangeType.Forward), - DummyRange!(ReturnBy.Reference, Length.Yes, RangeType.Bidirectional), - DummyRange!(ReturnBy.Reference, Length.Yes, RangeType.Random), - DummyRange!(ReturnBy.Reference, Length.No, RangeType.Forward), - DummyRange!(ReturnBy.Value, Length.Yes, RangeType.Input), - DummyRange!(ReturnBy.Value, Length.Yes, RangeType.Forward), - DummyRange!(ReturnBy.Value, Length.Yes, RangeType.Bidirectional), - DummyRange!(ReturnBy.Value, Length.Yes, RangeType.Random), - DummyRange!(ReturnBy.Value, Length.No, RangeType.Forward) - ); - - foreach (Range; DummyRanges) - { - Range r; - assert(r - .padLeft(0, 12) - .equal([0, 0, 1U, 2U, 3U, 4U, 5U, 6U, 7U, 8U, 9U, 10U]) - ); - } -} - -// Test nogc inference -@safe @nogc pure unittest -{ - import std.algorithm.comparison : equal; - - static immutable r1 = [1, 2, 3, 4]; - static immutable r2 = [0, 0, 1, 2, 3, 4]; - assert(r1.padLeft(0, 6).equal(r2)); -} - -/** -Extend the length of the input range `r` by padding out the end of the range -with the element `e`. The element `e` must be of a common type with the -element type of the range `r` as defined by $(REF CommonType, std, traits). -If `n` is less than the length of of `r`, then the contents of `r` are -returned. - -The range primitives that the resulting range provides depends whether or not `r` -provides them. Except the functions `back` and `popBack`, which also require -the range to have a length as well as `back` and `popBack` - -Params: - r = an $(REF_ALTTEXT input range, isInputRange, std,range,primitives) with a length - e = element to pad the range with - n = the length to pad to - -Returns: - A range containing the elements of the original range with the extra padding - -See Also: - $(REF rightJustifier, std, string) -*/ -auto padRight(R, E)(R r, E e, size_t n) -if ( - isInputRange!R && - !isInfinite!R && - !is(CommonType!(ElementType!R, E) == void)) -{ - static struct Result - { - private: - R data; - E element; - static if (hasLength!R) - { - size_t padLength; - } - else - { - size_t minLength; - size_t consumed; - } - - public: - bool empty() @property - { - static if (hasLength!R) - { - return data.empty && padLength == 0; - } - else - { - return data.empty && consumed >= minLength; - } - } - - auto front() @property - { - assert(!empty, "Attempting to fetch the front of an empty padRight"); - return data.empty ? element : data.front; - } - - void popFront() - { - assert(!empty, "Attempting to popFront an empty padRight"); - - static if (hasLength!R) - { - if (!data.empty) - { - data.popFront; - } - else - { - --padLength; - } - } - else - { - ++consumed; - if (!data.empty) - { - data.popFront; - } - } - } - - static if (hasLength!R) - { - size_t length() @property - { - return data.length + padLength; - } - } - - static if (isForwardRange!R) - { - auto save() @property - { - typeof(this) result = this; - data = data.save; - return result; - } - } - - static if (isBidirectionalRange!R && hasLength!R) - { - auto back() @property - { - assert(!empty, "Attempting to fetch the back of an empty padRight"); - return padLength > 0 ? element : data.back; - } - - void popBack() - { - assert(!empty, "Attempting to popBack an empty padRight"); - if (padLength > 0) - { - --padLength; - } - else - { - data.popBack; - } - } - } - - static if (isRandomAccessRange!R && hasLength!R) - { - E opIndex(size_t index) - { - assert(index <= this.length, "Index out of bounds"); - return index >= data.length ? element : data[index]; - } - } - - static if (hasSlicing!R && hasLength!R) - { - auto opSlice(size_t a, size_t b) - { - assert( - a <= b, - "Attempting to slice a padRight with a larger first argument than the second." - ); - assert( - b <= length, - "Attempting to slice using an out of bounds index on a padRight" - ); - return Result( - a >= data.length ? data[0 .. 0] : b <= data.length ? data[a .. b] : data[a .. data.length], - element, b - a); - } - - alias opDollar = length; - } - - this(R r, E e, size_t n) - { - data = r; - element = e; - static if (hasLength!R) - { - padLength = n > data.length ? n - data.length : 0; - } - else - { - minLength = n; - } - } - - @disable this(); - } - - return Result(r, e, n); -} - -/// -@safe pure unittest -{ - import std.algorithm.comparison : equal; - - assert([1, 2, 3, 4].padRight(0, 6).equal([1, 2, 3, 4, 0, 0])); - assert([1, 2, 3, 4].padRight(0, 4).equal([1, 2, 3, 4])); - - assert("abc".padRight('_', 6).equal("abc___")); -} - -pure @safe unittest -{ - import std.algorithm.comparison : equal; - import std.internal.test.dummyrange : AllDummyRanges, ReferenceInputRange; - import std.meta : AliasSeq; - - auto string_input_range = new ReferenceInputRange!dchar(['a', 'b', 'c']); - dchar padding = '_'; - assert(string_input_range.padRight(padding, 6).equal("abc___")); - - foreach (RangeType; AllDummyRanges) - { - RangeType r1; - assert(r1 - .padRight(0, 12) - .equal([1U, 2U, 3U, 4U, 5U, 6U, 7U, 8U, 9U, 10U, 0, 0]) - ); - - // test if Result properly uses random access ranges - static if (isRandomAccessRange!RangeType) - { - RangeType r3; - assert(r3.padRight(0, 12)[0] == 1); - assert(r3.padRight(0, 12)[2] == 3); - assert(r3.padRight(0, 12)[9] == 10); - assert(r3.padRight(0, 12)[10] == 0); - assert(r3.padRight(0, 12)[11] == 0); - } - - // test if Result properly uses slicing and opDollar - static if (hasSlicing!RangeType) - { - RangeType r4; - assert(r4 - .padRight(0, 12)[0 .. 3] - .equal([1, 2, 3]) - ); - assert(r4 - .padRight(0, 12)[0 .. 10] - .equal([1U, 2U, 3U, 4U, 5U, 6U, 7U, 8U, 9U, 10U]) - ); - assert(r4 - .padRight(0, 12)[0 .. 11] - .equal([1U, 2U, 3U, 4U, 5U, 6U, 7U, 8U, 9U, 10U, 0]) - ); - assert(r4 - .padRight(0, 12)[2 .. $] - .equal([3U, 4U, 5U, 6U, 7U, 8U, 9U, 10U, 0, 0]) - ); - assert(r4 - .padRight(0, 12)[0 .. $] - .equal([1U, 2U, 3U, 4U, 5U, 6U, 7U, 8U, 9U, 10U, 0, 0]) - ); - } - - // drop & dropBack test opslice ranges when available, popFront/popBack otherwise - RangeType r5; - foreach (i; 1 .. 13) assert(r5.padRight(0, 12).drop(i).walkLength == 12 - i); - } -} - -// Test nogc inference -@safe @nogc pure unittest -{ - import std.algorithm.comparison : equal; - - static immutable r1 = [1, 2, 3, 4]; - static immutable r2 = [1, 2, 3, 4, 0, 0]; - assert(r1.padRight(0, 6).equal(r2)); -} - -// Test back, popBack, and save -@safe pure unittest -{ - import std.algorithm.comparison : equal; - - auto r1 = [1, 2, 3, 4].padRight(0, 6); - assert(r1.back == 0); - - r1.popBack; - auto r2 = r1.save; - assert(r1.equal([1, 2, 3, 4, 0])); - assert(r2.equal([1, 2, 3, 4, 0])); - - r1.popBackN(2); - assert(r1.back == 3); - assert(r1.length == 3); - assert(r2.length == 5); - assert(r2.equal([1, 2, 3, 4, 0])); - - r2.popFront; - assert(r2.length == 4); - assert(r2[0] == 2); - assert(r2[1] == 3); - assert(r2[2] == 4); - assert(r2[3] == 0); - assert(r2.equal([2, 3, 4, 0])); - - r2.popBack; - assert(r2.equal([2, 3, 4])); - - auto r3 = [1, 2, 3, 4].padRight(0, 6); - size_t len = 0; - while (!r3.empty) - { - ++len; - r3.popBack; - } - assert(len == 6); -} - -// https://issues.dlang.org/show_bug.cgi?id=19042 -@safe pure unittest -{ - import std.algorithm.comparison : equal; - - assert([2, 5, 13].padRight(42, 10).chunks(5) - .equal!equal([[2, 5, 13, 42, 42], [42, 42, 42, 42, 42]])); - - assert([1, 2, 3, 4].padRight(0, 10)[7 .. 9].equal([0, 0])); -} - -/** -This simplifies a commonly used idiom in phobos for accepting any kind of string -parameter. The type `R` can for example be a simple string, chained string using -$(REF chain, std,range), $(REF chainPath, std,path) or any other input range of -characters. - -Only finite length character ranges are allowed with this constraint. - -This template is equivalent to: ---- -isInputRange!R && !isInfinite!R && isSomeChar!(ElementEncodingType!R) ---- - -See_Also: -$(REF isInputRange, std,range,primitives), -$(REF isInfinite, std,range,primitives), -$(LREF isSomeChar), -$(REF ElementEncodingType, std,range,primitives) -*/ -template isSomeFiniteCharInputRange(R) -{ - import std.traits : isSomeChar; - - enum isSomeFiniteCharInputRange = isInputRange!R && !isInfinite!R - && isSomeChar!(ElementEncodingType!R); -} - -/// -@safe unittest -{ - import std.path : chainPath; - import std.range : chain; - - void someLibraryMethod(R)(R argument) - if (isSomeFiniteCharInputRange!R) - { - // implementation detail, would iterate over each character of argument - } - - someLibraryMethod("simple strings work"); - someLibraryMethod(chain("chained", " ", "strings", " ", "work")); - someLibraryMethod(chainPath("chained", "paths", "work")); - // you can also use custom structs implementing a char range -} - diff --git a/phobos/std/range/primitives.d b/phobos/std/range/primitives.d deleted file mode 100644 index dddcae9..0000000 --- a/phobos/std/range/primitives.d +++ /dev/null @@ -1,2717 +0,0 @@ -/** -This module is a submodule of $(MREF std, range). - -It defines the bidirectional and forward range primitives for arrays: -$(LREF empty), $(LREF front), $(LREF back), $(LREF popFront), $(LREF popBack) and $(LREF save). - -It provides basic range functionality by defining several templates for testing -whether a given object is a range, and what kind of range it is: - -$(SCRIPT inhibitQuickIndex = 1;) -$(DIVC quickindex, -$(BOOKTABLE , - $(TR $(TD $(LREF isInputRange)) - $(TD Tests if something is an $(I input range), defined to be - something from which one can sequentially read data using the - primitives `front`, `popFront`, and `empty`. - )) - $(TR $(TD $(LREF isOutputRange)) - $(TD Tests if something is an $(I output range), defined to be - something to which one can sequentially write data using the - $(LREF put) primitive. - )) - $(TR $(TD $(LREF isForwardRange)) - $(TD Tests if something is a $(I forward range), defined to be an - input range with the additional capability that one can save one's - current position with the `save` primitive, thus allowing one to - iterate over the same range multiple times. - )) - $(TR $(TD $(LREF isBidirectionalRange)) - $(TD Tests if something is a $(I bidirectional range), that is, a - forward range that allows reverse traversal using the primitives $(D - back) and `popBack`. - )) - $(TR $(TD $(LREF isRandomAccessRange)) - $(TD Tests if something is a $(I random access range), which is a - bidirectional range that also supports the array subscripting - operation via the primitive `opIndex`. - )) -)) - -It also provides number of templates that test for various range capabilities: - -$(BOOKTABLE , - $(TR $(TD $(LREF hasMobileElements)) - $(TD Tests if a given range's elements can be moved around using the - primitives `moveFront`, `moveBack`, or `moveAt`. - )) - $(TR $(TD $(LREF ElementType)) - $(TD Returns the element type of a given range. - )) - $(TR $(TD $(LREF ElementEncodingType)) - $(TD Returns the encoding element type of a given range. - )) - $(TR $(TD $(LREF hasSwappableElements)) - $(TD Tests if a range is a forward range with swappable elements. - )) - $(TR $(TD $(LREF hasAssignableElements)) - $(TD Tests if a range is a forward range with mutable elements. - )) - $(TR $(TD $(LREF hasLvalueElements)) - $(TD Tests if a range is a forward range with elements that can be - passed by reference and have their address taken. - )) - $(TR $(TD $(LREF hasLength)) - $(TD Tests if a given range has the `length` attribute. - )) - $(TR $(TD $(LREF isInfinite)) - $(TD Tests if a given range is an $(I infinite range). - )) - $(TR $(TD $(LREF hasSlicing)) - $(TD Tests if a given range supports the array slicing operation $(D - R[x .. y]). - )) -) - -Finally, it includes some convenience functions for manipulating ranges: - -$(BOOKTABLE , - $(TR $(TD $(LREF popFrontN)) - $(TD Advances a given range by up to $(I n) elements. - )) - $(TR $(TD $(LREF popBackN)) - $(TD Advances a given bidirectional range from the right by up to - $(I n) elements. - )) - $(TR $(TD $(LREF popFrontExactly)) - $(TD Advances a given range by up exactly $(I n) elements. - )) - $(TR $(TD $(LREF popBackExactly)) - $(TD Advances a given bidirectional range from the right by exactly - $(I n) elements. - )) - $(TR $(TD $(LREF moveFront)) - $(TD Removes the front element of a range. - )) - $(TR $(TD $(LREF moveBack)) - $(TD Removes the back element of a bidirectional range. - )) - $(TR $(TD $(LREF moveAt)) - $(TD Removes the $(I i)'th element of a random-access range. - )) - $(TR $(TD $(LREF walkLength)) - $(TD Computes the length of any range in O(n) time. - )) - $(TR $(TD $(LREF put)) - $(TD Outputs element `e` to a range. - )) -) - -Source: $(PHOBOSSRC std/range/primitives.d) - -License: $(HTTP boost.org/LICENSE_1_0.txt, Boost License 1.0). - -Authors: $(HTTP erdani.com, Andrei Alexandrescu), David Simcha, and - $(HTTP jmdavisprog.com, Jonathan M Davis). Credit for some of the ideas - in building this module goes to - $(HTTP fantascienza.net/leonardo/so/, Leonardo Maffi). -*/ -module std.range.primitives; - -import std.traits; - -/** -Returns `true` if `R` is an input range. An input range must -define the primitives `empty`, `popFront`, and `front`. The -following code should compile for any input range. - ----- -R r; // can define a range object -if (r.empty) {} // can test for empty -r.popFront(); // can invoke popFront() -auto h = r.front; // can get the front of the range of non-void type ----- - -The following are rules of input ranges are assumed to hold true in all -Phobos code. These rules are not checkable at compile-time, so not conforming -to these rules when writing ranges or range based code will result in -undefined behavior. - -$(UL - $(LI `r.empty` returns `false` if and only if there is more data - available in the range.) - $(LI `r.empty` evaluated multiple times, without calling - `r.popFront`, or otherwise mutating the range object or the - underlying data, yields the same result for every evaluation.) - $(LI `r.front` returns the current element in the range. - It may return by value or by reference.) - $(LI `r.front` can be legally evaluated if and only if evaluating - `r.empty` has, or would have, equaled `false`.) - $(LI `r.front` evaluated multiple times, without calling - `r.popFront`, or otherwise mutating the range object or the - underlying data, yields the same result for every evaluation.) - $(LI `r.popFront` advances to the next element in the range.) - $(LI `r.popFront` can be called if and only if evaluating `r.empty` - has, or would have, equaled `false`.) -) - -Also, note that Phobos code assumes that the primitives `r.front` and -`r.empty` are $(BIGOH 1) time complexity wise or "cheap" in terms of -running time. $(BIGOH) statements in the documentation of range functions -are made with this assumption. - -See_Also: - The header of $(MREF std,range) for tutorials on ranges. - -Params: - R = type to be tested - E = if present, the elements of the range must be - $(DDSUBLINK spec/const3, implicit_qualifier_conversions, qualifier-convertible) - to this type - -Returns: - `true` if R is an input range (possibly with element type `E`), `false` if not - */ -enum bool isInputRange(R) = - is(typeof(R.init) == R) - && is(typeof((R r) { return r.empty; } (R.init)) == bool) - && (is(typeof((return ref R r) => r.front)) || is(typeof(ref (return ref R r) => r.front))) - && !is(typeof((R r) { return r.front; } (R.init)) == void) - && is(typeof((R r) => r.popFront)); - -/// ditto -enum bool isInputRange(R, E) = - .isInputRange!R && isQualifierConvertible!(ElementType!R, E); - -/// -@safe unittest -{ - struct A {} - struct B - { - void popFront(); - @property bool empty(); - @property int front(); - } - static assert(!isInputRange!A); - static assert( isInputRange!B); - static assert( isInputRange!(int[])); - static assert( isInputRange!(char[])); - static assert(!isInputRange!(char[4])); - static assert( isInputRange!(inout(int)[])); - static assert(!isInputRange!(int[], string)); - static assert( isInputRange!(int[], int)); - static assert( isInputRange!(int[], const int)); - static assert(!isInputRange!(int[], immutable int)); - - static assert(!isInputRange!(const(int)[], int)); - static assert( isInputRange!(const(int)[], const int)); - static assert(!isInputRange!(const(int)[], immutable int)); - - static assert(!isInputRange!(immutable(int)[], int)); - static assert( isInputRange!(immutable(int)[], const int)); - static assert( isInputRange!(immutable(int)[], immutable int)); - - static struct NotDefaultConstructible - { - @disable this(); - void popFront(); - @property bool empty(); - @property int front(); - } - static assert( isInputRange!NotDefaultConstructible); - - static struct NotDefaultConstructibleOrCopyable - { - @disable this(); - @disable this(this); - void popFront(); - @property bool empty(); - @property int front(); - } - static assert(isInputRange!NotDefaultConstructibleOrCopyable); - - static struct Frontless - { - void popFront(); - @property bool empty(); - } - static assert(!isInputRange!Frontless); - - static struct VoidFront - { - void popFront(); - @property bool empty(); - void front(); - } - static assert(!isInputRange!VoidFront); -} -// https://issues.dlang.org/show_bug.cgi?id=16034 -@safe unittest -{ - struct One - { - int entry = 1; - @disable this(this); - } - - assert(isInputRange!(One[])); -} - -@safe unittest -{ - import std.algorithm.comparison : equal; - - static struct R - { - static struct Front - { - R* impl; - @property int value() { return impl._front; } - alias value this; - } - - int _front; - - @property bool empty() { return _front >= 3; } - @property auto front() { return Front(&this); } - void popFront() { _front++; } - } - R r; - - static assert(isInputRange!R); - assert(r.equal([ 0, 1, 2 ])); -} - -/+ -puts the whole raw element `e` into `r`. doPut will not attempt to -iterate, slice or transcode `e` in any way shape or form. It will $(B only) -call the correct primitive (`r.put(e)`, $(D r.front = e) or -`r(e)` once. - -This can be important when `e` needs to be placed in `r` unchanged. -Furthermore, it can be useful when working with `InputRange`s, as doPut -guarantees that no more than a single element will be placed. -+/ -private void doPut(R, E)(ref R r, auto ref E e) -{ - static if (is(PointerTarget!R == struct)) - enum usingPut = hasMember!(PointerTarget!R, "put"); - else - enum usingPut = hasMember!(R, "put"); - - static if (usingPut) - { - static assert(is(typeof(r.put(e))), - "Cannot put a " ~ E.stringof ~ " into a " ~ R.stringof ~ "."); - r.put(e); - } - else static if (isNarrowString!R && is(const(E) == const(typeof(r[0])))) - { - // one character, we can put it - r[0] = e; - r = r[1 .. $]; - } - else static if (isNarrowString!R && isNarrowString!E && is(typeof(r[] = e))) - { - // slice assign. Note that this is a duplicate from put, but because - // putChar uses doPut exclusively, we have to copy it here. - immutable len = e.length; - r[0 .. len] = e; - r = r[len .. $]; - } - else static if (isInputRange!R) - { - static assert(is(typeof(r.front = e)), - "Cannot put a " ~ E.stringof ~ " into a " ~ R.stringof ~ "."); - r.front = e; - r.popFront(); - } - else static if (is(typeof(r(e)))) - { - r(e); - } - else - { - static assert(false, - "Cannot put a " ~ E.stringof ~ " into a " ~ R.stringof ~ "."); - } -} - -@safe unittest -{ - static assert(!isNativeOutputRange!(int, int)); - static assert( isNativeOutputRange!(int[], int)); - static assert(!isNativeOutputRange!(int[][], int)); - - static assert(!isNativeOutputRange!(int, int[])); - static assert(!isNativeOutputRange!(int[], int[])); - static assert( isNativeOutputRange!(int[][], int[])); - - static assert(!isNativeOutputRange!(int, int[][])); - static assert(!isNativeOutputRange!(int[], int[][])); - static assert(!isNativeOutputRange!(int[][], int[][])); - - static assert(!isNativeOutputRange!(int[4], int)); - static assert( isNativeOutputRange!(int[4][], int)); //Scary! - static assert( isNativeOutputRange!(int[4][], int[4])); - - static assert( isNativeOutputRange!( char[], char)); - static assert(!isNativeOutputRange!( char[], dchar)); - static assert( isNativeOutputRange!(dchar[], char)); - static assert( isNativeOutputRange!(dchar[], dchar)); - -} - -/++ -Outputs `e` to `r`. The exact effect is dependent upon the two -types. Several cases are accepted, as described below. The code snippets -are attempted in order, and the first to compile "wins" and gets -evaluated. - -In this table "doPut" is a method that places `e` into `r`, using the -correct primitive: `r.put(e)` if `R` defines `put`, $(D r.front = e) -if `r` is an input range (followed by `r.popFront()`), or `r(e)` -otherwise. - -$(BOOKTABLE , - $(TR - $(TH Code Snippet) - $(TH Scenario) - ) - $(TR - $(TD `r.doPut(e);`) - $(TD `R` specifically accepts an `E`.) - ) - $(TR - $(TD $(D r.doPut([ e ]);)) - $(TD `R` specifically accepts an `E[]`.) - ) - $(TR - $(TD `r.putChar(e);`) - $(TD `R` accepts some form of string or character. put will - transcode the character `e` accordingly.) - ) - $(TR - $(TD $(D for (; !e.empty; e.popFront()) put(r, e.front);)) - $(TD Copying range `E` into `R`.) - ) -) - -Tip: `put` should $(I not) be used "UFCS-style", e.g. `r.put(e)`. -Doing this may call `R.put` directly, by-passing any transformation -feature provided by `Range.put`. $(D put(r, e)) is prefered. - +/ -void put(R, E)(ref R r, E e) -{ - //First level: simply straight up put. - static if (is(typeof(doPut(r, e)))) - { - doPut(r, e); - } - //Optional optimization block for straight up array to array copy. - else static if (isDynamicArray!R && - !isAutodecodableString!R && - isDynamicArray!E && - is(typeof(r[] = e[]))) - { - immutable len = e.length; - r[0 .. len] = e[]; - r = r[len .. $]; - } - //Accepts E[] ? - else static if (is(typeof(doPut(r, [e]))) && !isDynamicArray!R) - { - if (__ctfe) - { - E[1] arr = [e]; - doPut(r, arr[]); - } - else - doPut(r, (ref e) @trusted { return (&e)[0 .. 1]; }(e)); - } - //special case for char to string. - else static if (isSomeChar!E && is(typeof(putChar(r, e)))) - { - putChar(r, e); - } - //Extract each element from the range - //We can use "put" here, so we can recursively test a RoR of E. - else static if (isInputRange!E && is(typeof(put(r, e.front)))) - { - //Special optimization: If E is a narrow string, and r accepts characters no-wider than the string's - //Then simply feed the characters 1 by 1. - static if (isAutodecodableString!E && !isAggregateType!E && ( - (is(E : const char[]) && is(typeof(doPut(r, char.max))) && !is(typeof(doPut(r, dchar.max))) && - !is(typeof(doPut(r, wchar.max)))) || - (is(E : const wchar[]) && is(typeof(doPut(r, wchar.max))) && !is(typeof(doPut(r, dchar.max)))) ) ) - { - foreach (c; e) - doPut(r, c); - } - else - { - for (; !e.empty; e.popFront()) - put(r, e.front); - } - } - else - { - static assert(false, "Cannot put a " ~ E.stringof ~ " into a " ~ R.stringof ~ "."); - } -} - -/** - * When an output range's `put` method only accepts elements of type - * `T`, use the global `put` to handle outputting a `T[]` to the range - * or vice-versa. - */ -@safe pure unittest -{ - import std.traits : isSomeChar; - - static struct A - { - string data; - - void put(C)(C c) if (isSomeChar!C) - { - data ~= c; - } - } - static assert(isOutputRange!(A, char)); - - auto a = A(); - put(a, "Hello"); - assert(a.data == "Hello"); -} - -/** - * `put` treats dynamic arrays as array slices, and will call `popFront` - * on the slice after an element has been copied. - * - * Be sure to save the position of the array before calling `put`. - */ -@safe pure nothrow unittest -{ - int[] a = [1, 2, 3], b = [10, 20]; - auto c = a; - put(a, b); - assert(c == [10, 20, 3]); - // at this point, a was advanced twice, so it only contains - // its last element while c represents the whole array - assert(a == [3]); -} - -/** - * It's also possible to `put` any width strings or characters into narrow - * strings -- put does the conversion for you. - * - * Note that putting the same width character as the target buffer type is - * `nothrow`, but transcoding can throw a $(REF UTFException, std, utf). - */ -@safe pure unittest -{ - // the elements must be mutable, so using string or const(char)[] - // won't compile - char[] s1 = new char[13]; - auto r1 = s1; - put(r1, "Hello, World!"w); - assert(s1 == "Hello, World!"); -} - -@safe pure nothrow unittest -{ - // same thing, just using same character width. - char[] s1 = new char[13]; - auto r1 = s1; - put(r1, "Hello, World!"); - assert(s1 == "Hello, World!"); -} - - -@safe pure nothrow @nogc unittest -{ - static struct R() { void put(scope const(char)[]) {} } - R!() r; - put(r, 'a'); -} - -//Helper function to handle chars as quickly and as elegantly as possible -//Assumes r.put(e)/r(e) has already been tested -private void putChar(R, E)(ref R r, E e) -if (isSomeChar!E) -{ - // https://issues.dlang.org/show_bug.cgi?id=9186: Can't use (E[]).init - enum csCond = is(typeof(doPut(r, (){ ref const( char)[] cstringInit(); return cstringInit(); }()))); - enum wsCond = is(typeof(doPut(r, (){ ref const(wchar)[] wstringInit(); return wstringInit(); }()))); - enum dsCond = is(typeof(doPut(r, (){ ref const(dchar)[] dstringInit(); return dstringInit(); }()))); - - //Use "max" to avoid static type demotion - enum ccCond = is(typeof(doPut(r, char.max))); - enum wcCond = is(typeof(doPut(r, wchar.max))); - //enum dcCond = is(typeof(doPut(r, dchar.max))); - - //Fast transform a narrow char into a wider string - static if ((wsCond && E.sizeof < wchar.sizeof) || (dsCond && E.sizeof < dchar.sizeof)) - { - enum w = wsCond && E.sizeof < wchar.sizeof; - Select!(w, wchar, dchar) c = e; - typeof(c)[1] arr = [c]; - doPut(r, arr[]); - } - //Encode a wide char into a narrower string - else static if (wsCond || csCond) - { - import std.utf : encode; - /+static+/ Select!(wsCond, wchar[2], char[4]) buf; //static prevents purity. - doPut(r, buf[0 .. encode(buf, e)]); - } - //Slowly encode a wide char into a series of narrower chars - else static if (wcCond || ccCond) - { - import std.encoding : encode; - alias C = Select!(wcCond, wchar, char); - encode!(C, R)(e, r); - } - else - { - static assert(false, "Cannot put a " ~ E.stringof ~ " into a " ~ R.stringof ~ "."); - } -} - -pure @safe unittest -{ - auto f = delegate (const(char)[]) {}; - putChar(f, cast(dchar)'a'); -} - - -@safe pure unittest -{ - static struct R() { void put(scope const(char)[]) {} } - R!() r; - putChar(r, 'a'); -} - -@safe unittest -{ - struct A {} - static assert(!isInputRange!(A)); - struct B - { - void put(int) {} - } - B b; - put(b, 5); -} - -@safe unittest -{ - int[] a = new int[10]; - int b; - static assert(isInputRange!(typeof(a))); - put(a, b); -} - -@safe unittest -{ - void myprint(scope const(char)[] s) { } - auto r = &myprint; - put(r, 'a'); -} - -@safe unittest -{ - int[] a = new int[10]; - static assert(!__traits(compiles, put(a, 1.0L))); - put(a, 1); - assert(a.length == 9); - /* - * a[0] = 65; // OK - * a[0] = 'A'; // OK - * a[0] = "ABC"[0]; // OK - * put(a, "ABC"); // OK - */ - put(a, "ABC"); - assert(a.length == 6); -} - -@safe unittest -{ - char[] a = new char[10]; - static assert(!__traits(compiles, put(a, 1.0L))); - static assert(!__traits(compiles, put(a, 1))); - //char[] is now an output range for char, wchar, dchar, and ranges of such. - static assert(__traits(compiles, putChar(a, 'a'))); - static assert(__traits(compiles, put(a, wchar('a')))); - static assert(__traits(compiles, put(a, dchar('a')))); - static assert(__traits(compiles, put(a, "ABC"))); - static assert(__traits(compiles, put(a, "ABC"w))); - static assert(__traits(compiles, put(a, "ABC"d))); -} - -@safe unittest -{ - // attempt putting into narrow strings by transcoding - char[] a = new char[10]; - auto b = a; - put(a, "ABC"w); - assert(b[0 .. 3] == "ABC"); - assert(a.length == 7); - - a = b; // reset - put(a, 'Îť'); - assert(b[0 .. 2] == "Îť"); - assert(a.length == 8); - - a = b; // reset - put(a, "ABC"d); - assert(b[0 .. 3] == "ABC"); - assert(a.length == 7); - - a = b; // reset - put(a, '𐐡'); - assert(b[0 .. 4] == "𐐡"); - assert(a.length == 6); - - wchar[] aw = new wchar[10]; - auto bw = aw; - put(aw, "ABC"); - assert(bw[0 .. 3] == "ABC"w); - assert(aw.length == 7); - - aw = bw; // reset - put(aw, 'Îť'); - assert(bw[0 .. 1] == "Îť"w); - assert(aw.length == 9); - - aw = bw; // reset - put(aw, "ABC"d); - assert(bw[0 .. 3] == "ABC"w); - assert(aw.length == 7); - - aw = bw; // reset - put(aw, '𐐡'); - assert(bw[0 .. 2] == "𐐡"w); - assert(aw.length == 8); - - aw = bw; // reset - put(aw, "𐐡"); // try transcoding from char[] - assert(bw[0 .. 2] == "𐐡"w); - assert(aw.length == 8); -} - -@safe unittest -{ - int[][] a = new int[][10]; - int[] b = new int[10]; - int c; - put(b, c); - assert(b.length == 9); - put(a, b); - assert(a.length == 9); - static assert(!__traits(compiles, put(a, c))); -} - -@safe unittest -{ - int[][] a = new int[][](3); - int[] b = [1]; - auto aa = a; - put(aa, b); - assert(aa == [[], []]); - assert(a == [[1], [], []]); - int[][3] c = [2]; - aa = a; - put(aa, c[]); - assert(aa.empty); - assert(a == [[2], [2], [2]]); -} - -@safe unittest -{ - // Test fix for bug 7476. - struct LockingTextWriter - { - void put(dchar c){} - } - struct RetroResult - { - bool end = false; - @property bool empty() const { return end; } - @property dchar front(){ return 'a'; } - void popFront(){ end = true; } - } - LockingTextWriter w; - RetroResult re; - put(w, re); -} - -@system unittest -{ - import std.conv : to; - import std.meta : AliasSeq; - import std.typecons : tuple; - - static struct PutC(C) - { - string result; - void put(const(C) c) { result ~= to!string((&c)[0 .. 1]); } - } - static struct PutS(C) - { - string result; - void put(const(C)[] s) { result ~= to!string(s); } - } - static struct PutSS(C) - { - string result; - void put(const(C)[][] ss) - { - foreach (s; ss) - result ~= to!string(s); - } - } - - PutS!char p; - putChar(p, cast(dchar)'a'); - - //Source Char - static foreach (SC; AliasSeq!(char, wchar, dchar)) - {{ - SC ch = 'I'; - dchar dh = '♥'; - immutable(SC)[] s = "日本語!"; - immutable(SC)[][] ss = ["日本語", "が", "好き", "ですか", "?"]; - - //Target Char - static foreach (TC; AliasSeq!(char, wchar, dchar)) - { - //Testing PutC and PutS - static foreach (Type; AliasSeq!(PutC!TC, PutS!TC)) - {{ - Type type; - auto sink = new Type(); - - //Testing put and sink - foreach (value ; tuple(type, sink)) - { - put(value, ch); - assert(value.result == "I"); - put(value, dh); - assert(value.result == "I♥"); - put(value, s); - assert(value.result == "I♥日本語!"); - put(value, ss); - assert(value.result == "I♥日本語!日本語が好きですか?"); - } - }} - } - }} -} - -@safe unittest -{ - static struct CharRange - { - char c; - enum empty = false; - void popFront(){} - ref char front() return @property - { - return c; - } - } - CharRange c; - put(c, cast(dchar)'H'); - put(c, "hello"d); -} - -// https://issues.dlang.org/show_bug.cgi?id=9823 -@system unittest -{ - const(char)[] r; - void delegate(const(char)[]) dg = (s) { r = s; }; - put(dg, ["ABC"]); - assert(r == "ABC"); -} - -// https://issues.dlang.org/show_bug.cgi?id=10571 -@safe unittest -{ - import std.format.write : formattedWrite; - string buf; - formattedWrite((scope const(char)[] s) { buf ~= s; }, "%s", "hello"); - assert(buf == "hello"); -} - -@safe unittest -{ - import std.format.write : formattedWrite; - import std.meta : AliasSeq; - struct PutC(C) - { - void put(C){} - } - struct PutS(C) - { - void put(const(C)[]){} - } - struct CallC(C) - { - void opCall(C){} - } - struct CallS(C) - { - void opCall(const(C)[]){} - } - struct FrontC(C) - { - enum empty = false; - auto front()@property{return C.init;} - void front(C)@property{} - void popFront(){} - } - struct FrontS(C) - { - enum empty = false; - auto front()@property{return C[].init;} - void front(const(C)[])@property{} - void popFront(){} - } - void foo() - { - static foreach (C; AliasSeq!(char, wchar, dchar)) - {{ - formattedWrite((C c){}, "", 1, 'a', cast(wchar)'a', cast(dchar)'a', "a"c, "a"w, "a"d); - formattedWrite((const(C)[]){}, "", 1, 'a', cast(wchar)'a', cast(dchar)'a', "a"c, "a"w, "a"d); - formattedWrite(PutC!C(), "", 1, 'a', cast(wchar)'a', cast(dchar)'a', "a"c, "a"w, "a"d); - formattedWrite(PutS!C(), "", 1, 'a', cast(wchar)'a', cast(dchar)'a', "a"c, "a"w, "a"d); - CallC!C callC; - CallS!C callS; - formattedWrite(callC, "", 1, 'a', cast(wchar)'a', cast(dchar)'a', "a"c, "a"w, "a"d); - formattedWrite(callS, "", 1, 'a', cast(wchar)'a', cast(dchar)'a', "a"c, "a"w, "a"d); - formattedWrite(FrontC!C(), "", 1, 'a', cast(wchar)'a', cast(dchar)'a', "a"c, "a"w, "a"d); - formattedWrite(FrontS!C(), "", 1, 'a', cast(wchar)'a', cast(dchar)'a', "a"c, "a"w, "a"d); - }} - formattedWrite((dchar[]).init, "", 1, 'a', cast(wchar)'a', cast(dchar)'a', "a"c, "a"w, "a"d); - } -} - -/+ -Returns `true` if `R` is a native output range for elements of type -`E`. An output range is defined functionally as a range that -supports the operation $(D doPut(r, e)) as defined above. if $(D doPut(r, e)) -is valid, then `put(r,e)` will have the same behavior. - -The two guarantees isNativeOutputRange gives over the larger `isOutputRange` -are: -1: `e` is $(B exactly) what will be placed (not `[e]`, for example). -2: if `E` is a non $(empty) `InputRange`, then placing `e` is -guaranteed to not overflow the range. - +/ -package(std) enum bool isNativeOutputRange(R, E) = - is(typeof(doPut(lvalueOf!R, lvalueOf!E))); - -@safe unittest -{ - int[] r = new int[](4); - static assert(isInputRange!(int[])); - static assert( isNativeOutputRange!(int[], int)); - static assert(!isNativeOutputRange!(int[], int[])); - static assert( isOutputRange!(int[], int[])); - - if (!r.empty) - put(r, 1); //guaranteed to succeed - if (!r.empty) - put(r, [1, 2]); //May actually error out. -} - -/++ -Returns `true` if `R` is an output range for elements of type -`E`. An output range is defined functionally as a range that -supports the operation $(D put(r, e)) as defined above. - -See_Also: - The header of $(MREF std,range) for tutorials on ranges. - +/ -enum bool isOutputRange(R, E) = - is(typeof(put(lvalueOf!R, lvalueOf!E))); - -/// -@safe unittest -{ - void myprint(scope const(char)[] s) { } - static assert(isOutputRange!(typeof(&myprint), char)); - - static assert( isOutputRange!(char[], char)); - static assert( isOutputRange!(dchar[], wchar)); - static assert( isOutputRange!(dchar[], dchar)); -} - -@safe unittest -{ - import std.array; - import std.stdio : writeln; - - auto app = appender!string(); - string s; - static assert( isOutputRange!(Appender!string, string)); - static assert( isOutputRange!(Appender!string*, string)); - static assert(!isOutputRange!(Appender!string, int)); - static assert( isOutputRange!(wchar[], wchar)); - static assert( isOutputRange!(dchar[], char)); - static assert( isOutputRange!(dchar[], string)); - static assert( isOutputRange!(dchar[], wstring)); - static assert( isOutputRange!(dchar[], dstring)); - - static assert(!isOutputRange!(const(int)[], int)); - static assert(!isOutputRange!(inout(int)[], int)); -} - - -/** -Returns `true` if `R` is a forward range. A forward range is an -input range `r` that can save "checkpoints" by saving `r.save` -to another value of type `R`. Notable examples of input ranges that -are $(I not) forward ranges are file/socket ranges; copying such a -range will not save the position in the stream, and they most likely -reuse an internal buffer as the entire stream does not sit in -memory. Subsequently, advancing either the original or the copy will -advance the stream, so the copies are not independent. - -The following code should compile for any forward range. - ----- -static assert(isInputRange!R); -R r1; -auto s1 = r1.save; -static assert(is(typeof(s1) == R)); ----- - -Saving a range is not duplicating it; in the example above, `r1` -and `r2` still refer to the same underlying data. They just -navigate that data independently. - -The semantics of a forward range (not checkable during compilation) -are the same as for an input range, with the additional requirement -that backtracking must be possible by saving a copy of the range -object with `save` and using it later. - -`save` behaves in many ways like a copy constructor, and its -implementation typically is done using copy construction. - -The existence of a copy constructor, however, does not imply -the range is a forward range. For example, a range that reads -from a TTY consumes its input and cannot save its place and -read it again, and so cannot be a forward range and cannot -have a `save` function. - - -See_Also: - The header of $(MREF std,range) for tutorials on ranges. - -Params: - R = type to be tested - E = if present, the elements of the range must be - $(DDSUBLINK spec/const3, implicit_qualifier_conversions, qualifier-convertible) - to this type - -Returns: - `true` if R is a forward range (possibly with element type `E`), `false` if not - */ -enum bool isForwardRange(R) = isInputRange!R - && is(typeof((R r) { return r.save; } (R.init)) == R); - -/// ditto -enum bool isForwardRange(R, E) = - .isForwardRange!R && isQualifierConvertible!(ElementType!R, E); - -/// -@safe unittest -{ - static assert(!isForwardRange!(int)); - static assert( isForwardRange!(int[])); - static assert( isForwardRange!(inout(int)[])); - - static assert( isForwardRange!(int[], const int)); - static assert(!isForwardRange!(int[], immutable int)); - - static assert(!isForwardRange!(const(int)[], int)); - static assert( isForwardRange!(const(int)[], const int)); - static assert(!isForwardRange!(const(int)[], immutable int)); - - static assert(!isForwardRange!(immutable(int)[], int)); - static assert( isForwardRange!(immutable(int)[], const int)); - static assert( isForwardRange!(immutable(int)[], immutable int)); -} - -@safe unittest -{ - // BUG 14544 - struct R14544 - { - int front() { return 0;} - void popFront() {} - bool empty() { return false; } - R14544 save() {return this;} - } - - static assert( isForwardRange!R14544 ); -} - -/** -Returns `true` if `R` is a bidirectional range. A bidirectional -range is a forward range that also offers the primitives `back` and -`popBack`. The following code should compile for any bidirectional -range. - -The semantics of a bidirectional range (not checkable during -compilation) are assumed to be the following (`r` is an object of -type `R`): - -$(UL $(LI `r.back` returns (possibly a reference to) the last -element in the range. Calling `r.back` is allowed only if calling -`r.empty` has, or would have, returned `false`.)) - -See_Also: - The header of $(MREF std,range) for tutorials on ranges. - -Params: - R = type to be tested - E = if present, the elements of the range must be - $(DDSUBLINK spec/const3, implicit_qualifier_conversions, qualifier-convertible) - to this type - -Returns: - `true` if R is a bidirectional range (possibly with element type `E`), `false` if not - */ -enum bool isBidirectionalRange(R) = isForwardRange!R - && is(typeof((R r) => r.popBack)) - && (is(typeof((return ref R r) => r.back)) || is(typeof(ref (return ref R r) => r.back))) - && is(typeof(R.init.back.init) == ElementType!R); - -/// ditto -enum bool isBidirectionalRange(R, E) = - .isBidirectionalRange!R && isQualifierConvertible!(ElementType!R, E); - -/// -@safe unittest -{ - alias R = int[]; - R r = [0,1]; - static assert(isForwardRange!R); // is forward range - r.popBack(); // can invoke popBack - auto t = r.back; // can get the back of the range - auto w = r.front; - static assert(is(typeof(t) == typeof(w))); // same type for front and back - - // Checking the element type - static assert( isBidirectionalRange!(int[], const int)); - static assert(!isBidirectionalRange!(int[], immutable int)); - - static assert(!isBidirectionalRange!(const(int)[], int)); - static assert( isBidirectionalRange!(const(int)[], const int)); - static assert(!isBidirectionalRange!(const(int)[], immutable int)); - - static assert(!isBidirectionalRange!(immutable(int)[], int)); - static assert( isBidirectionalRange!(immutable(int)[], const int)); - static assert( isBidirectionalRange!(immutable(int)[], immutable int)); -} - -@safe unittest -{ - struct A {} - struct B - { - void popFront(); - @property bool empty(); - @property int front(); - } - struct C - { - @property bool empty(); - @property C save(); - void popFront(); - @property int front(); - void popBack(); - @property int back(); - } - static assert(!isBidirectionalRange!(A)); - static assert(!isBidirectionalRange!(B)); - static assert( isBidirectionalRange!(C)); - static assert( isBidirectionalRange!(int[])); - static assert( isBidirectionalRange!(char[])); - static assert( isBidirectionalRange!(inout(int)[])); -} - -/** -Returns `true` if `R` is a random-access range. A random-access -range is a bidirectional range that also offers the primitive $(D -opIndex), OR an infinite forward range that offers `opIndex`. In -either case, the range must either offer `length` or be -infinite. The following code should compile for any random-access -range. - -The semantics of a random-access range (not checkable during -compilation) are assumed to be the following (`r` is an object of -type `R`): $(UL $(LI `r.opIndex(n)` returns a reference to the -`n`th element in the range.)) - -Although `char[]` and `wchar[]` (as well as their qualified -versions including `string` and `wstring`) are arrays, $(D -isRandomAccessRange) yields `false` for them because they use -variable-length encodings (UTF-8 and UTF-16 respectively). These types -are bidirectional ranges only. - -See_Also: - The header of $(MREF std,range) for tutorials on ranges. - -Params: - R = type to be tested - E = if present, the elements of the range must be - $(DDSUBLINK spec/const3, implicit_qualifier_conversions, qualifier-convertible) - to this type - -Returns: - `true` if R is a random-access range (possibly with element type `E`), `false` if not - */ -enum bool isRandomAccessRange(R) = - is(typeof(lvalueOf!R[1]) == ElementType!R) - && !(isAutodecodableString!R && !isAggregateType!R) - && isForwardRange!R - && (isBidirectionalRange!R || isInfinite!R) - && (hasLength!R || isInfinite!R) - && (isInfinite!R || !is(typeof(lvalueOf!R[$ - 1])) - || is(typeof(lvalueOf!R[$ - 1]) == ElementType!R)); - -/// ditto -enum bool isRandomAccessRange(R, E) = - .isRandomAccessRange!R && isQualifierConvertible!(ElementType!R, E); - -/// -@safe unittest -{ - import std.traits : isAggregateType, isAutodecodableString; - - alias R = int[]; - - // range is finite and bidirectional or infinite and forward. - static assert(isBidirectionalRange!R || - isForwardRange!R && isInfinite!R); - - R r = [0,1]; - auto e = r[1]; // can index - auto f = r.front; - static assert(is(typeof(e) == typeof(f))); // same type for indexed and front - static assert(!(isAutodecodableString!R && !isAggregateType!R)); // narrow strings cannot be indexed as ranges - static assert(hasLength!R || isInfinite!R); // must have length or be infinite - - // $ must work as it does with arrays if opIndex works with $ - static if (is(typeof(r[$]))) - { - static assert(is(typeof(f) == typeof(r[$]))); - - // $ - 1 doesn't make sense with infinite ranges but needs to work - // with finite ones. - static if (!isInfinite!R) - static assert(is(typeof(f) == typeof(r[$ - 1]))); - } - - // Checking the element type - static assert( isRandomAccessRange!(int[], const int)); - static assert(!isRandomAccessRange!(int[], immutable int)); - - static assert(!isRandomAccessRange!(const(int)[], int)); - static assert( isRandomAccessRange!(const(int)[], const int)); - static assert(!isRandomAccessRange!(const(int)[], immutable int)); - - static assert(!isRandomAccessRange!(immutable(int)[], int)); - static assert( isRandomAccessRange!(immutable(int)[], const int)); - static assert( isRandomAccessRange!(immutable(int)[], immutable int)); -} - -@safe unittest -{ - struct A {} - struct B - { - void popFront(); - @property bool empty(); - @property int front(); - } - struct C - { - void popFront(); - @property bool empty(); - @property int front(); - void popBack(); - @property int back(); - } - struct D - { - @property bool empty(); - @property D save(); - @property int front(); - void popFront(); - @property int back(); - void popBack(); - ref int opIndex(uint); - @property size_t length(); - alias opDollar = length; - //int opSlice(uint, uint); - } - struct E - { - bool empty(); - E save(); - int front(); - void popFront(); - int back(); - void popBack(); - ref int opIndex(uint); - size_t length(); - alias opDollar = length; - //int opSlice(uint, uint); - } - static assert(!isRandomAccessRange!(A)); - static assert(!isRandomAccessRange!(B)); - static assert(!isRandomAccessRange!(C)); - static assert( isRandomAccessRange!(D)); - static assert( isRandomAccessRange!(E)); - static assert( isRandomAccessRange!(int[])); - static assert( isRandomAccessRange!(inout(int)[])); -} - -@safe unittest -{ - // Test fix for bug 6935. - struct R - { - @disable this(); - - @property bool empty() const { return false; } - @property int front() const { return 0; } - void popFront() {} - - @property R save() { return this; } - - @property int back() const { return 0; } - void popBack(){} - - int opIndex(size_t n) const { return 0; } - @property size_t length() const { return 0; } - alias opDollar = length; - - void put(int e){ } - } - static assert(isInputRange!R); - static assert(isForwardRange!R); - static assert(isBidirectionalRange!R); - static assert(isRandomAccessRange!R); - static assert(isOutputRange!(R, int)); -} - -/** -Returns `true` iff `R` is an input range that supports the -`moveFront` primitive, as well as `moveBack` and `moveAt` if it's a -bidirectional or random access range. These may be explicitly implemented, or -may work via the default behavior of the module level functions `moveFront` -and friends. The following code should compile for any range -with mobile elements. - ----- -alias E = ElementType!R; -R r; -static assert(isInputRange!R); -static assert(is(typeof(moveFront(r)) == E)); -static if (isBidirectionalRange!R) - static assert(is(typeof(moveBack(r)) == E)); -static if (isRandomAccessRange!R) - static assert(is(typeof(moveAt(r, 0)) == E)); ----- - */ -enum bool hasMobileElements(R) = - isInputRange!R - && is(typeof(moveFront(lvalueOf!R)) == ElementType!R) - && (!isBidirectionalRange!R - || is(typeof(moveBack(lvalueOf!R)) == ElementType!R)) - && (!isRandomAccessRange!R - || is(typeof(moveAt(lvalueOf!R, 0)) == ElementType!R)); - -/// -@safe unittest -{ - import std.algorithm.iteration : map; - import std.range : iota, repeat; - - static struct HasPostblit - { - this(this) {} - } - - auto nonMobile = map!"a"(repeat(HasPostblit.init)); - static assert(!hasMobileElements!(typeof(nonMobile))); - static assert( hasMobileElements!(int[])); - static assert( hasMobileElements!(inout(int)[])); - static assert( hasMobileElements!(typeof(iota(1000)))); - - static assert( hasMobileElements!( string)); - static assert( hasMobileElements!(dstring)); - static assert( hasMobileElements!( char[])); - static assert( hasMobileElements!(dchar[])); -} - -/** -The element type of `R`. `R` does not have to be a range. The -element type is determined as the type yielded by `r.front` for an -object `r` of type `R`. For example, `ElementType!(T[])` is -`T` if `T[]` isn't a narrow string; if it is, the element type is -`dchar`. If `R` doesn't have `front`, `ElementType!R` is -`void`. - */ -template ElementType(R) -{ - static if (is(typeof(R.init.front.init) T)) - alias ElementType = T; - else - alias ElementType = void; -} - -/// -@safe unittest -{ - import std.range : iota; - - // Standard arrays: returns the type of the elements of the array - static assert(is(ElementType!(int[]) == int)); - - // Accessing .front retrieves the decoded dchar - static assert(is(ElementType!(char[]) == dchar)); // rvalue - static assert(is(ElementType!(dchar[]) == dchar)); // lvalue - - // Ditto - static assert(is(ElementType!(string) == dchar)); - static assert(is(ElementType!(dstring) == immutable(dchar))); - - // For ranges it gets the type of .front. - auto range = iota(0, 10); - static assert(is(ElementType!(typeof(range)) == int)); -} - -@safe unittest -{ - static assert(is(ElementType!(byte[]) == byte)); - static assert(is(ElementType!(wchar[]) == dchar)); // rvalue - static assert(is(ElementType!(wstring) == dchar)); -} - -@safe unittest -{ - enum XYZ : string { a = "foo" } - auto x = XYZ.a.front; - immutable char[3] a = "abc"; - int[] i; - void[] buf; - static assert(is(ElementType!(XYZ) == dchar)); - static assert(is(ElementType!(typeof(a)) == dchar)); - static assert(is(ElementType!(typeof(i)) == int)); - static assert(is(ElementType!(typeof(buf)) == void)); - static assert(is(ElementType!(inout(int)[]) == inout(int))); - static assert(is(ElementType!(inout(int[])) == inout(int))); -} - -@safe unittest -{ - static assert(is(ElementType!(int[5]) == int)); - static assert(is(ElementType!(int[0]) == int)); - static assert(is(ElementType!(char[5]) == dchar)); - static assert(is(ElementType!(char[0]) == dchar)); -} - -// https://issues.dlang.org/show_bug.cgi?id=11336 -@safe unittest -{ - static struct S - { - this(this) @disable; - } - static assert(is(ElementType!(S[]) == S)); -} - -// https://issues.dlang.org/show_bug.cgi?id=11401 -@safe unittest -{ - // ElementType should also work for non-@propety 'front' - struct E { ushort id; } - struct R - { - E front() { return E.init; } - } - static assert(is(ElementType!R == E)); -} - -/** -The encoding element type of `R`. For narrow strings (`char[]`, -`wchar[]` and their qualified variants including `string` and -`wstring`), `ElementEncodingType` is the character type of the -string. For all other types, `ElementEncodingType` is the same as -`ElementType`. - */ -template ElementEncodingType(R) -{ - static if (is(StringTypeOf!R) && is(R : E[], E)) - alias ElementEncodingType = E; - else - alias ElementEncodingType = ElementType!R; -} - -/// -@safe unittest -{ - import std.range : iota; - // internally the range stores the encoded type - static assert(is(ElementEncodingType!(char[]) == char)); - - static assert(is(ElementEncodingType!(wstring) == immutable(wchar))); - - static assert(is(ElementEncodingType!(byte[]) == byte)); - - auto range = iota(0, 10); - static assert(is(ElementEncodingType!(typeof(range)) == int)); -} - -@safe unittest -{ - static assert(is(ElementEncodingType!(wchar[]) == wchar)); - static assert(is(ElementEncodingType!(dchar[]) == dchar)); - static assert(is(ElementEncodingType!(string) == immutable(char))); - static assert(is(ElementEncodingType!(dstring) == immutable(dchar))); - static assert(is(ElementEncodingType!(int[]) == int)); -} - -@safe unittest -{ - enum XYZ : string { a = "foo" } - auto x = XYZ.a.front; - immutable char[3] a = "abc"; - int[] i; - void[] buf; - static assert(is(ElementType!(XYZ) : dchar)); - static assert(is(ElementEncodingType!(char[]) == char)); - static assert(is(ElementEncodingType!(string) == immutable char)); - static assert(is(ElementType!(typeof(a)) : dchar)); - static assert(is(ElementType!(typeof(i)) == int)); - static assert(is(ElementEncodingType!(typeof(i)) == int)); - static assert(is(ElementType!(typeof(buf)) : void)); - - static assert(is(ElementEncodingType!(inout char[]) : inout(char))); -} - -@safe unittest -{ - static assert(is(ElementEncodingType!(int[5]) == int)); - static assert(is(ElementEncodingType!(int[0]) == int)); - static assert(is(ElementEncodingType!(char[5]) == char)); - static assert(is(ElementEncodingType!(char[0]) == char)); -} - -/** -Returns `true` if `R` is an input range and has swappable -elements. The following code should compile for any range -with swappable elements. - ----- -R r; -static assert(isInputRange!R); -swap(r.front, r.front); -static if (isBidirectionalRange!R) swap(r.back, r.front); -static if (isRandomAccessRange!R) swap(r[0], r.front); ----- - */ -template hasSwappableElements(R) -{ - import std.algorithm.mutation : swap; - enum bool hasSwappableElements = isInputRange!R - && is(typeof((ref R r) => swap(r.front, r.front))) - && (!isBidirectionalRange!R - || is(typeof((ref R r) => swap(r.back, r.front)))) - && (!isRandomAccessRange!R - || is(typeof((ref R r) => swap(r[0], r.front)))); -} - -/// -@safe unittest -{ - static assert(!hasSwappableElements!(const int[])); - static assert(!hasSwappableElements!(const(int)[])); - static assert(!hasSwappableElements!(inout(int)[])); - static assert( hasSwappableElements!(int[])); - - static assert(!hasSwappableElements!( string)); - static assert(!hasSwappableElements!(dstring)); - static assert(!hasSwappableElements!( char[])); - static assert( hasSwappableElements!(dchar[])); -} - -/** -Returns `true` if `R` is an input range and has mutable -elements. The following code should compile for any range -with assignable elements. - ----- -R r; -static assert(isInputRange!R); -r.front = r.front; -static if (isBidirectionalRange!R) r.back = r.front; -static if (isRandomAccessRange!R) r[0] = r.front; ----- - */ -enum bool hasAssignableElements(R) = isInputRange!R - && is(typeof(lvalueOf!R.front = lvalueOf!R.front)) - && (!isBidirectionalRange!R - || is(typeof(lvalueOf!R.back = lvalueOf!R.back))) - && (!isRandomAccessRange!R - || is(typeof(lvalueOf!R[0] = lvalueOf!R.front))); - -/// -@safe unittest -{ - static assert(!hasAssignableElements!(const int[])); - static assert(!hasAssignableElements!(const(int)[])); - static assert( hasAssignableElements!(int[])); - static assert(!hasAssignableElements!(inout(int)[])); - - static assert(!hasAssignableElements!( string)); - static assert(!hasAssignableElements!(dstring)); - static assert(!hasAssignableElements!( char[])); - static assert( hasAssignableElements!(dchar[])); -} - -/** -Tests whether the range `R` has lvalue elements. These are defined as -elements that can be passed by reference and have their address taken. -The following code should compile for any range with lvalue elements. ----- -void passByRef(ref ElementType!R stuff); -... -static assert(isInputRange!R); -passByRef(r.front); -static if (isBidirectionalRange!R) passByRef(r.back); -static if (isRandomAccessRange!R) passByRef(r[0]); ----- -*/ -enum bool hasLvalueElements(R) = isInputRange!R - && is(typeof(isLvalue(lvalueOf!R.front))) - && (!isBidirectionalRange!R - || is(typeof(isLvalue(lvalueOf!R.back)))) - && (!isRandomAccessRange!R - || is(typeof(isLvalue(lvalueOf!R[0])))); - -/* Compile successfully if argument of type T is an lvalue - */ -private void isLvalue(T)(T) -if (0); - -private void isLvalue(T)(ref T) -if (1); - -/// -@safe unittest -{ - import std.range : iota, chain; - - static assert( hasLvalueElements!(int[])); - static assert( hasLvalueElements!(const(int)[])); - static assert( hasLvalueElements!(inout(int)[])); - static assert( hasLvalueElements!(immutable(int)[])); - static assert(!hasLvalueElements!(typeof(iota(3)))); - - static assert(!hasLvalueElements!( string)); - static assert( hasLvalueElements!(dstring)); - static assert(!hasLvalueElements!( char[])); - static assert( hasLvalueElements!(dchar[])); - - auto c = chain([1, 2, 3], [4, 5, 6]); - static assert( hasLvalueElements!(typeof(c))); -} - -@safe unittest -{ - // bugfix 6336 - struct S { immutable int value; } - static assert( isInputRange!(S[])); - static assert( hasLvalueElements!(S[])); -} - -/** -Yields `true` if `R` has a `length` member that returns a value of `size_t` -type. `R` does not have to be a range. If `R` is a range, algorithms in the -standard library are only guaranteed to support `length` with type `size_t`. - -Note that `length` is an optional primitive as no range must implement it. Some -ranges do not store their length explicitly, some cannot compute it without -actually exhausting the range (e.g. socket streams), and some other ranges may -be infinite. - -Although narrow string types (`char[]`, `wchar[]`, and their qualified -derivatives) do define a `length` property, `hasLength` yields `false` for them. -This is because a narrow string's length does not reflect the number of -characters, but instead the number of encoding units, and as such is not useful -with range-oriented algorithms. To use strings as random-access ranges with -length, use $(REF representation, std, string) or $(REF byCodeUnit, std, utf). -*/ -template hasLength(R) -{ - static if (is(typeof(((R* r) => r.length)(null)) Length)) - enum bool hasLength = is(Length == size_t) && - !(isAutodecodableString!R && !isAggregateType!R); - else - enum bool hasLength = false; -} - -/// -@safe unittest -{ - static assert(!hasLength!(char[])); - static assert( hasLength!(int[])); - static assert( hasLength!(inout(int)[])); - - struct A { size_t length() { return 0; } } - struct B { @property size_t length() { return 0; } } - static assert( hasLength!(A)); - static assert( hasLength!(B)); -} - -// test combinations which are invalid on some platforms -@safe unittest -{ - struct A { ulong length; } - struct B { @property uint length() { return 0; } } - - static if (is(size_t == uint)) - { - static assert(!hasLength!(A)); - static assert(hasLength!(B)); - } - else static if (is(size_t == ulong)) - { - static assert(hasLength!(A)); - static assert(!hasLength!(B)); - } -} - -// test combinations which are invalid on all platforms -@safe unittest -{ - struct A { long length; } - struct B { int length; } - struct C { ubyte length; } - struct D { char length; } - static assert(!hasLength!(A)); - static assert(!hasLength!(B)); - static assert(!hasLength!(C)); - static assert(!hasLength!(D)); -} - -/** -Returns `true` if `R` is an infinite input range. An -infinite input range is an input range that has a statically-defined -enumerated member called `empty` that is always `false`, -for example: - ----- -struct MyInfiniteRange -{ - enum bool empty = false; - ... -} ----- - */ - -template isInfinite(R) -{ - static if (isInputRange!R && __traits(compiles, { enum e = R.empty; })) - enum bool isInfinite = !R.empty; - else - enum bool isInfinite = false; -} - -/// -@safe unittest -{ - import std.range : Repeat; - static assert(!isInfinite!(int[])); - static assert( isInfinite!(Repeat!(int))); -} - -/** -Returns `true` if `R` offers a slicing operator with integral boundaries -that returns a forward range type. - -For finite ranges, the result of `opSlice` must be of the same type as the -original range type. If the range defines `opDollar`, then it must support -subtraction. - -For infinite ranges, when $(I not) using `opDollar`, the result of `opSlice` -may be a forward range of any type. However, when using `opDollar`, the result -of `opSlice` must be of the same type as the original range type. - -The following expression must be true for `hasSlicing` to be `true`: - ----- - isForwardRange!R - && !(isAutodecodableString!R && !isAggregateType!R) - && is(typeof((R r) { return r[1 .. 1].length; } (R.init)) == size_t) - && (is(typeof(lvalueOf!R[1 .. 1]) == R) || isInfinite!R) - && (!is(typeof(lvalueOf!R[0 .. $])) || is(typeof(lvalueOf!R[0 .. $]) == R)) - && (!is(typeof(lvalueOf!R[0 .. $])) || isInfinite!R - || is(typeof(lvalueOf!R[0 .. $ - 1]) == R)) - && is(typeof((ref R r) - { - static assert(isForwardRange!(typeof(r[1 .. 2]))); - })); ----- - */ -enum bool hasSlicing(R) = isForwardRange!R - && !(isAutodecodableString!R && !isAggregateType!R) - && is(typeof((R r) { return r[1 .. 1].length; } (R.init)) == size_t) - && (is(typeof(lvalueOf!R[1 .. 1]) == R) || isInfinite!R) - && (!is(typeof(lvalueOf!R[0 .. $])) || is(typeof(lvalueOf!R[0 .. $]) == R)) - && (!is(typeof(lvalueOf!R[0 .. $])) || isInfinite!R - || is(typeof(lvalueOf!R[0 .. $ - 1]) == R)) - && is(typeof((ref R r) - { - static assert(isForwardRange!(typeof(r[1 .. 2]))); - })); - -/// -@safe unittest -{ - import std.range : takeExactly; - static assert( hasSlicing!(int[])); - static assert( hasSlicing!(const(int)[])); - static assert(!hasSlicing!(const int[])); - static assert( hasSlicing!(inout(int)[])); - static assert(!hasSlicing!(inout int [])); - static assert( hasSlicing!(immutable(int)[])); - static assert(!hasSlicing!(immutable int[])); - static assert(!hasSlicing!string); - static assert( hasSlicing!dstring); - - enum rangeFuncs = "@property int front();" ~ - "void popFront();" ~ - "@property bool empty();" ~ - "@property auto save() { return this; }" ~ - "@property size_t length();"; - - struct A { mixin(rangeFuncs); int opSlice(size_t, size_t); } - struct B { mixin(rangeFuncs); B opSlice(size_t, size_t); } - struct C { mixin(rangeFuncs); @disable this(); C opSlice(size_t, size_t); } - struct D { mixin(rangeFuncs); int[] opSlice(size_t, size_t); } - static assert(!hasSlicing!(A)); - static assert( hasSlicing!(B)); - static assert( hasSlicing!(C)); - static assert(!hasSlicing!(D)); - - struct InfOnes - { - enum empty = false; - void popFront() {} - @property int front() { return 1; } - @property InfOnes save() { return this; } - auto opSlice(size_t i, size_t j) { return takeExactly(this, j - i); } - auto opSlice(size_t i, Dollar d) { return this; } - - struct Dollar {} - Dollar opDollar() const { return Dollar.init; } - } - - static assert(hasSlicing!InfOnes); -} - -// https://issues.dlang.org/show_bug.cgi?id=24348 -@safe unittest -{ - static struct Slice - { - size_t length; - bool empty() => length == 0; - int front() => 0; - void popFront() { --length; } - Slice save() => this; - } - - static struct InfZeros - { - enum empty = false; - int front() => 0; - void popFront() {} - InfZeros save() => this; - - Slice opIndex(size_t[2] bounds) - { - size_t i = bounds[0], j = bounds[1]; - size_t length = i <= j ? j - i : 0; - return Slice(length); - } - - size_t[2] opSlice(size_t dim : 0)(size_t i, size_t j) => [i, j]; - } - - static assert(hasSlicing!InfZeros); -} - -/** -This is a best-effort implementation of `length` for any kind of -range. - -If `hasLength!Range`, simply returns `range.length` without -checking `upTo` (when specified). - -Otherwise, walks the range through its length and returns the number -of elements seen. Performes $(BIGOH n) evaluations of `range.empty` -and `range.popFront()`, where `n` is the effective length of $(D -range). - -The `upTo` parameter is useful to "cut the losses" in case -the interest is in seeing whether the range has at least some number -of elements. If the parameter `upTo` is specified, stops if $(D -upTo) steps have been taken and returns `upTo`. - -Infinite ranges are compatible, provided the parameter `upTo` is -specified, in which case the implementation simply returns upTo. - */ -auto walkLength(Range)(Range range) -if (isInputRange!Range && !isInfinite!Range) -{ - static if (hasLength!Range) - return range.length; - else - { - size_t result; - static if (autodecodeStrings && isNarrowString!Range) - { - import std.utf : codeUnitLimit; - result = range.length; - foreach (const i, const c; range) - { - if (c >= codeUnitLimit!Range) - { - result = i; - break; - } - } - range = range[result .. $]; - } - for ( ; !range.empty ; range.popFront() ) - ++result; - return result; - } -} -/// ditto -auto walkLength(Range)(Range range, const size_t upTo) -if (isInputRange!Range) -{ - static if (hasLength!Range) - return range.length; - else static if (isInfinite!Range) - return upTo; - else - { - size_t result; - static if (autodecodeStrings && isNarrowString!Range) - { - import std.utf : codeUnitLimit; - result = upTo > range.length ? range.length : upTo; - foreach (const i, const c; range[0 .. result]) - { - if (c >= codeUnitLimit!Range) - { - result = i; - break; - } - } - range = range[result .. $]; - } - for ( ; result < upTo && !range.empty ; range.popFront() ) - ++result; - return result; - } -} - -/// -@safe unittest -{ - import std.range : iota; - - assert(10.iota.walkLength == 10); - // iota has a length function, and therefore the - // doesn't have to be walked, and the upTo - // parameter is ignored - assert(10.iota.walkLength(5) == 10); -} - -@safe unittest -{ - import std.algorithm.iteration : filter; - import std.range : recurrence, take; - - //hasLength Range - int[] a = [ 1, 2, 3 ]; - assert(walkLength(a) == 3); - assert(walkLength(a, 0) == 3); - assert(walkLength(a, 2) == 3); - assert(walkLength(a, 4) == 3); - - //Forward Range - auto b = filter!"true"([1, 2, 3, 4]); - assert(b.walkLength() == 4); - assert(b.walkLength(0) == 0); - assert(b.walkLength(2) == 2); - assert(b.walkLength(4) == 4); - assert(b.walkLength(6) == 4); - - //Infinite Range - auto fibs = recurrence!"a[n-1] + a[n-2]"(1, 1); - assert(!__traits(compiles, fibs.walkLength())); - assert(fibs.take(10).walkLength() == 10); - assert(fibs.walkLength(55) == 55); -} - -/** - `popFrontN` eagerly advances `r` itself (not a copy) up to `n` times - (by calling `r.popFront`). `popFrontN` takes `r` by `ref`, - so it mutates the original range. Completes in $(BIGOH 1) steps for ranges - that support slicing and have length. - Completes in $(BIGOH n) time for all other ranges. - - `popBackN` behaves the same as `popFrontN` but instead removes - elements from the back of the (bidirectional) range instead of the front. - - Returns: - How much `r` was actually advanced, which may be less than `n` if - `r` did not have at least `n` elements. - - See_Also: $(REF drop, std, range), $(REF dropBack, std, range) -*/ -size_t popFrontN(Range)(ref Range r, size_t n) -if (isInputRange!Range) -{ - static if (hasLength!Range) - { - n = cast(size_t) (n < r.length ? n : r.length); - } - - static if (hasSlicing!Range && is(typeof(r = r[n .. $]))) - { - r = r[n .. $]; - } - else static if (hasSlicing!Range && hasLength!Range) //TODO: Remove once hasSlicing forces opDollar. - { - r = r[n .. r.length]; - } - else - { - static if (hasLength!Range) - { - foreach (i; 0 .. n) - r.popFront(); - } - else - { - foreach (i; 0 .. n) - { - if (r.empty) return i; - r.popFront(); - } - } - } - return n; -} - -/// ditto -size_t popBackN(Range)(ref Range r, size_t n) -if (isBidirectionalRange!Range) -{ - static if (hasLength!Range) - { - n = cast(size_t) (n < r.length ? n : r.length); - } - - static if (hasSlicing!Range && is(typeof(r = r[0 .. $ - n]))) - { - r = r[0 .. $ - n]; - } - else static if (hasSlicing!Range && hasLength!Range) //TODO: Remove once hasSlicing forces opDollar. - { - r = r[0 .. r.length - n]; - } - else - { - static if (hasLength!Range) - { - foreach (i; 0 .. n) - r.popBack(); - } - else - { - foreach (i; 0 .. n) - { - if (r.empty) return i; - r.popBack(); - } - } - } - return n; -} - -/// -@safe unittest -{ - int[] a = [ 1, 2, 3, 4, 5 ]; - a.popFrontN(2); - assert(a == [ 3, 4, 5 ]); - a.popFrontN(7); - assert(a == [ ]); -} - -/// -@safe unittest -{ - import std.algorithm.comparison : equal; - import std.range : iota; - auto LL = iota(1L, 7L); - auto r = popFrontN(LL, 2); - assert(equal(LL, [3L, 4L, 5L, 6L])); - assert(r == 2); -} - -/// -@safe unittest -{ - int[] a = [ 1, 2, 3, 4, 5 ]; - a.popBackN(2); - assert(a == [ 1, 2, 3 ]); - a.popBackN(7); - assert(a == [ ]); -} - -/// -@safe unittest -{ - import std.algorithm.comparison : equal; - import std.range : iota; - auto LL = iota(1L, 7L); - auto r = popBackN(LL, 2); - assert(equal(LL, [1L, 2L, 3L, 4L])); - assert(r == 2); -} - -/** - Eagerly advances `r` itself (not a copy) exactly `n` times (by - calling `r.popFront`). `popFrontExactly` takes `r` by `ref`, - so it mutates the original range. Completes in $(BIGOH 1) steps for ranges - that support slicing, and have either length or are infinite. - Completes in $(BIGOH n) time for all other ranges. - - Note: Unlike $(LREF popFrontN), `popFrontExactly` will assume that the - range holds at least `n` elements. This makes `popFrontExactly` - faster than `popFrontN`, but it also means that if `range` does - not contain at least `n` elements, it will attempt to call `popFront` - on an empty range, which is undefined behavior. So, only use - `popFrontExactly` when it is guaranteed that `range` holds at least - `n` elements. - - `popBackExactly` will behave the same but instead removes elements from - the back of the (bidirectional) range instead of the front. - - See_Also: $(REF dropExactly, std, range), $(REF dropBackExactly, std, range) -*/ -void popFrontExactly(Range)(ref Range r, size_t n) -if (isInputRange!Range) -{ - static if (hasLength!Range) - assert(n <= r.length, "range is smaller than amount of items to pop"); - - static if (hasSlicing!Range && is(typeof(r = r[n .. $]))) - r = r[n .. $]; - else static if (hasSlicing!Range && hasLength!Range) //TODO: Remove once hasSlicing forces opDollar. - r = r[n .. r.length]; - else - foreach (i; 0 .. n) - r.popFront(); -} - -/// ditto -void popBackExactly(Range)(ref Range r, size_t n) -if (isBidirectionalRange!Range) -{ - static if (hasLength!Range) - assert(n <= r.length, "range is smaller than amount of items to pop"); - - static if (hasSlicing!Range && is(typeof(r = r[0 .. $ - n]))) - r = r[0 .. $ - n]; - else static if (hasSlicing!Range && hasLength!Range) //TODO: Remove once hasSlicing forces opDollar. - r = r[0 .. r.length - n]; - else - foreach (i; 0 .. n) - r.popBack(); -} - -/// -@safe unittest -{ - import std.algorithm.comparison : equal; - import std.algorithm.iteration : filterBidirectional; - - auto a = [1, 2, 3]; - a.popFrontExactly(1); - assert(a == [2, 3]); - a.popBackExactly(1); - assert(a == [2]); - - string s = "日本語"; - s.popFrontExactly(1); - assert(s == "本語"); - s.popBackExactly(1); - assert(s == "本"); - - auto bd = filterBidirectional!"true"([1, 2, 3]); - bd.popFrontExactly(1); - assert(bd.equal([2, 3])); - bd.popBackExactly(1); - assert(bd.equal([2])); -} - -/** - Moves the front of `r` out and returns it. - - If `r.front` is a struct with a destructor or copy constructor defined, it - is reset to its `.init` value after its value is moved. Otherwise, it is - left unchanged. - - In either case, `r.front` is left in a destroyable state that does not - allocate any resources. -*/ -ElementType!R moveFront(R)(R r) -{ - static if (is(typeof(&r.moveFront))) - { - return r.moveFront(); - } - else static if (!hasElaborateCopyConstructor!(ElementType!R)) - { - return r.front; - } - else static if (is(typeof(&(r.front())) == ElementType!R*)) - { - import std.algorithm.mutation : move; - return move(r.front); - } - else - { - static assert(0, - "Cannot move front of a range with a postblit and an rvalue front."); - } -} - -/// -@safe unittest -{ - auto a = [ 1, 2, 3 ]; - assert(moveFront(a) == 1); - assert(a.length == 3); - - // define a perfunctory input range - struct InputRange - { - enum bool empty = false; - enum int front = 7; - void popFront() {} - int moveFront() { return 43; } - } - InputRange r; - // calls r.moveFront - assert(moveFront(r) == 43); -} - -@safe unittest -{ - struct R - { - @property ref int front() { static int x = 42; return x; } - this(this){} - } - R r; - assert(moveFront(r) == 42); -} - -/** - Moves the back of `r` out and returns it. Leaves `r.back` in a - destroyable state that does not allocate any resources (usually equal - to its `.init` value). -*/ -ElementType!R moveBack(R)(R r) -{ - static if (is(typeof(&r.moveBack))) - { - return r.moveBack(); - } - else static if (!hasElaborateCopyConstructor!(ElementType!R)) - { - return r.back; - } - else static if (is(typeof(&(r.back())) == ElementType!R*)) - { - import std.algorithm.mutation : move; - return move(r.back); - } - else - { - static assert(0, - "Cannot move back of a range with a postblit and an rvalue back."); - } -} - -/// -@safe unittest -{ - struct TestRange - { - int payload = 5; - @property bool empty() { return false; } - @property TestRange save() { return this; } - @property ref int front() return { return payload; } - @property ref int back() return { return payload; } - void popFront() { } - void popBack() { } - } - static assert(isBidirectionalRange!TestRange); - TestRange r; - auto x = moveBack(r); - assert(x == 5); -} - -/** - Moves element at index `i` of `r` out and returns it. Leaves $(D - r[i]) in a destroyable state that does not allocate any resources - (usually equal to its `.init` value). -*/ -ElementType!R moveAt(R)(R r, size_t i) -{ - static if (is(typeof(&r.moveAt))) - { - return r.moveAt(i); - } - else static if (!hasElaborateCopyConstructor!(ElementType!(R))) - { - return r[i]; - } - else static if (is(typeof(&r[i]) == ElementType!R*)) - { - import std.algorithm.mutation : move; - return move(r[i]); - } - else - { - static assert(0, - "Cannot move element of a range with a postblit and rvalue elements."); - } -} - -/// -@safe unittest -{ - auto a = [1,2,3,4]; - foreach (idx, it; a) - { - assert(it == moveAt(a, idx)); - } -} - -@safe unittest -{ - import std.internal.test.dummyrange; - - foreach (DummyType; AllDummyRanges) - { - auto d = DummyType.init; - assert(moveFront(d) == 1); - - static if (isBidirectionalRange!DummyType) - { - assert(moveBack(d) == 10); - } - - static if (isRandomAccessRange!DummyType) - { - assert(moveAt(d, 2) == 3); - } - } -} - -/** -Implements the range interface primitive `empty` for types that -obey $(LREF hasLength) property and for narrow strings. Due to the -fact that nonmember functions can be called with the first argument -using the dot notation, `a.empty` is equivalent to `empty(a)`. - */ -@property bool empty(T)(auto ref scope T a) -if (is(typeof(a.length) : size_t)) -{ - return !a.length; -} - -/// -@safe pure nothrow unittest -{ - auto a = [ 1, 2, 3 ]; - assert(!a.empty); - assert(a[3 .. $].empty); - - int[string] b; - assert(b.empty); - b["zero"] = 0; - assert(!b.empty); -} - -/** -Implements the range interface primitive `save` for built-in -arrays. Due to the fact that nonmember functions can be called with -the first argument using the dot notation, `array.save` is -equivalent to `save(array)`. The function does not duplicate the -content of the array, it simply returns its argument. - */ -@property inout(T)[] save(T)(return scope inout(T)[] a) @safe pure nothrow @nogc -{ - return a; -} - -/// -@safe pure nothrow unittest -{ - auto a = [ 1, 2, 3 ]; - auto b = a.save; - assert(b is a); -} - -/** -Implements the range interface primitive `popFront` for built-in -arrays. Due to the fact that nonmember functions can be called with -the first argument using the dot notation, `array.popFront` is -equivalent to `popFront(array)`. For $(GLOSSARY narrow strings), -`popFront` automatically advances to the next $(GLOSSARY code -point). -*/ -void popFront(T)(scope ref inout(T)[] a) @safe pure nothrow @nogc -if (!isAutodecodableString!(T[]) && !is(T[] == void[])) -{ - assert(a.length, "Attempting to popFront() past the end of an array of " ~ T.stringof); - a = a[1 .. $]; -} - -/// -@safe pure nothrow unittest -{ - auto a = [ 1, 2, 3 ]; - a.popFront(); - assert(a == [ 2, 3 ]); -} - -@safe unittest -{ - static assert(!is(typeof({ int[4] a; popFront(a); }))); - static assert(!is(typeof({ immutable int[] a; popFront(a); }))); - static assert(!is(typeof({ void[] a; popFront(a); }))); -} - -/// ditto -void popFront(C)(scope ref inout(C)[] str) @trusted pure nothrow -if (isAutodecodableString!(C[])) -{ - import std.algorithm.comparison : min; - - assert(str.length, "Attempting to popFront() past the end of an array of " ~ C.stringof); - - static if (is(immutable C == immutable char)) - { - static immutable ubyte[] charWidthTab = [ - 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, - 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, - 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, - 4, 4, 4, 4, 4, 4, 4, 4, 5, 5, 5, 5, 6, 6, 1, 1 - ]; - - immutable c = str[0]; - immutable charWidth = c < 192 ? 1 : charWidthTab.ptr[c - 192]; - str = str.ptr[min(str.length, charWidth) .. str.length]; - } - else static if (is(immutable C == immutable wchar)) - { - immutable u = str[0]; - immutable seqLen = 1 + (u >= 0xD800 && u <= 0xDBFF); - str = str.ptr[min(seqLen, str.length) .. str.length]; - } - else static assert(0, "Bad template constraint."); -} - -@safe pure unittest -{ - import std.meta : AliasSeq; - - static foreach (S; AliasSeq!(string, wstring, dstring)) - {{ - S s = "\xC2\xA9hello"; - s.popFront(); - assert(s == "hello"); - - S str = "hello\U00010143\u0100\U00010143"; - foreach (dchar c; ['h', 'e', 'l', 'l', 'o', '\U00010143', '\u0100', '\U00010143']) - { - assert(str.front == c); - str.popFront(); - } - assert(str.empty); - - static assert(!is(typeof({ immutable S a; popFront(a); }))); - static assert(!is(typeof({ typeof(S.init[0])[4] a; popFront(a); }))); - }} - - C[] _eatString(C)(C[] str) - { - while (!str.empty) - str.popFront(); - - return str; - } - enum checkCTFE = _eatString("ウェブサイト@La_VeritĂŠ.com"); - static assert(checkCTFE.empty); - enum checkCTFEW = _eatString("ウェブサイト@La_VeritĂŠ.com"w); - static assert(checkCTFEW.empty); -} - -// https://issues.dlang.org/show_bug.cgi?id=16090 -@safe unittest -{ - string s = "\u00E4"; - assert(s.length == 2); - s = s[0 .. 1]; - assert(s.length == 1); - s.popFront; - assert(s.empty); -} - -@safe unittest -{ - wstring s = "\U00010000"; - assert(s.length == 2); - s = s[0 .. 1]; - assert(s.length == 1); - s.popFront; - assert(s.empty); -} - -/** -Implements the range interface primitive `popBack` for built-in -arrays. Due to the fact that nonmember functions can be called with -the first argument using the dot notation, `array.popBack` is -equivalent to `popBack(array)`. For $(GLOSSARY narrow strings), $(D -popFront) automatically eliminates the last $(GLOSSARY code point). -*/ -void popBack(T)(scope ref inout(T)[] a) @safe pure nothrow @nogc -if (!isAutodecodableString!(T[]) && !is(T[] == void[])) -{ - assert(a.length); - a = a[0 .. $ - 1]; -} - -/// -@safe pure nothrow unittest -{ - auto a = [ 1, 2, 3 ]; - a.popBack(); - assert(a == [ 1, 2 ]); -} - -@safe unittest -{ - static assert(!is(typeof({ immutable int[] a; popBack(a); }))); - static assert(!is(typeof({ int[4] a; popBack(a); }))); - static assert(!is(typeof({ void[] a; popBack(a); }))); -} - -/// ditto -void popBack(T)(scope ref inout(T)[] a) @safe pure -if (isAutodecodableString!(T[])) -{ - import std.utf : strideBack; - assert(a.length, "Attempting to popBack() past the front of an array of " ~ T.stringof); - a = a[0 .. $ - strideBack(a, $)]; -} - -@safe pure unittest -{ - import std.meta : AliasSeq; - - static foreach (S; AliasSeq!(string, wstring, dstring)) - {{ - S s = "hello\xE2\x89\xA0"; - s.popBack(); - assert(s == "hello"); - S s3 = "\xE2\x89\xA0"; - auto c = s3.back; - assert(c == cast(dchar)'\u2260'); - s3.popBack(); - assert(s3 == ""); - - S str = "\U00010143\u0100\U00010143hello"; - foreach (dchar ch; ['o', 'l', 'l', 'e', 'h', '\U00010143', '\u0100', '\U00010143']) - { - assert(str.back == ch); - str.popBack(); - } - assert(str.empty); - - static assert(!is(typeof({ immutable S a; popBack(a); }))); - static assert(!is(typeof({ typeof(S.init[0])[4] a; popBack(a); }))); - }} -} - -/** -EXPERIMENTAL: to try out removing autodecoding, set the version -`NoAutodecodeStrings`. Most things are expected to fail with this version -currently. -*/ -version (NoAutodecodeStrings) -{ - enum autodecodeStrings = false; -} -else -{ - /// - enum autodecodeStrings = true; -} - -/** -Implements the range interface primitive `front` for built-in -arrays. Due to the fact that nonmember functions can be called with -the first argument using the dot notation, `array.front` is -equivalent to `front(array)`. For $(GLOSSARY narrow strings), $(D -front) automatically returns the first $(GLOSSARY code point) as _a $(D -dchar). -*/ -@property ref inout(T) front(T)(return scope inout(T)[] a) @safe pure nothrow @nogc -if (!isAutodecodableString!(T[]) && !is(T[] == void[])) -{ - assert(a.length, "Attempting to fetch the front of an empty array of " ~ T.stringof); - return a[0]; -} - -/// -@safe pure nothrow unittest -{ - int[] a = [ 1, 2, 3 ]; - assert(a.front == 1); -} - -@safe pure nothrow unittest -{ - auto a = [ 1, 2 ]; - a.front = 4; - assert(a.front == 4); - assert(a == [ 4, 2 ]); - - immutable b = [ 1, 2 ]; - assert(b.front == 1); - - int[2] c = [ 1, 2 ]; - assert(c.front == 1); -} - -/// ditto -@property dchar front(T)(scope const(T)[] a) @safe pure -if (isAutodecodableString!(T[])) -{ - import std.utf : decode; - assert(a.length, "Attempting to fetch the front of an empty array of " ~ T.stringof); - size_t i = 0; - return decode(a, i); -} - -/** -Implements the range interface primitive `back` for built-in -arrays. Due to the fact that nonmember functions can be called with -the first argument using the dot notation, `array.back` is -equivalent to `back(array)`. For $(GLOSSARY narrow strings), $(D -back) automatically returns the last $(GLOSSARY code point) as _a $(D -dchar). -*/ -@property ref inout(T) back(T)(return scope inout(T)[] a) @safe pure nothrow @nogc -if (!isAutodecodableString!(T[]) && !is(T[] == void[])) -{ - assert(a.length, "Attempting to fetch the back of an empty array of " ~ T.stringof); - return a[$ - 1]; -} - -/// -@safe pure nothrow unittest -{ - int[] a = [ 1, 2, 3 ]; - assert(a.back == 3); - a.back += 4; - assert(a.back == 7); -} - -@safe pure nothrow unittest -{ - immutable b = [ 1, 2, 3 ]; - assert(b.back == 3); - - int[3] c = [ 1, 2, 3 ]; - assert(c.back == 3); -} - -/// ditto -// Specialization for strings -@property dchar back(T)(scope const(T)[] a) @safe pure -if (isAutodecodableString!(T[])) -{ - import std.utf : decode, strideBack; - assert(a.length, "Attempting to fetch the back of an empty array of " ~ T.stringof); - size_t i = a.length - strideBack(a, a.length); - return decode(a, i); -} - -/* -Implements `length` for a range by forwarding it to `member`. -*/ -package(std) mixin template ImplementLength(alias member) -{ - static if (hasLength!(typeof(member))) - { - @property auto length() - { - return member.length; - } - alias opDollar = length; - } -} - -@safe unittest -{ - import std.meta : AliasSeq; - - foreach (alias E; AliasSeq!(noreturn, const(noreturn), immutable(noreturn) )) - { - alias R = E[]; - - static assert(isInputRange!R); - static assert(isForwardRange!R); - static assert(isBidirectionalRange!R); - static assert(isRandomAccessRange!R); - } - - static assert(isOutputRange!(noreturn[], noreturn)); -} diff --git a/phobos/std/regex/internal/backtracking.d b/phobos/std/regex/internal/backtracking.d deleted file mode 100644 index a488e06..0000000 --- a/phobos/std/regex/internal/backtracking.d +++ /dev/null @@ -1,1514 +0,0 @@ -/* - Implementation of backtracking std.regex engine. - Contains both compile-time and run-time versions. -*/ -module std.regex.internal.backtracking; - -package(std.regex): - -import core.stdc.stdlib, std.range.primitives, std.traits, std.typecons; -import std.regex.internal.ir; - -import core.memory : pureMalloc, pureFree; - -/+ - BacktrackingMatcher implements backtracking scheme of matching - regular expressions. -+/ -@trusted class BacktrackingMatcher(Char, Stream = Input!Char) : Matcher!Char -if (is(Char : dchar)) -{ - alias DataIndex = Stream.DataIndex; - struct State - {//top bit in pc is set if saved along with matches - DataIndex index; - uint pc, counter, infiniteNesting; - } - static assert(State.sizeof % size_t.sizeof == 0); - enum stateSize = State.sizeof / size_t.sizeof; - enum initialStack = 1 << 11; // items in a block of segmented stack - alias String = const(Char)[]; - alias RegEx = Regex!Char; - alias MatchFn = bool function(BacktrackingMatcher) pure; - const RegEx re; // regex program - MatchFn nativeFn; // native code for that program - // Stream state - Stream s; - DataIndex index; - dchar front; - bool exhausted; - // Backtracking machine state - uint pc, counter; - DataIndex lastState = 0; // Top of state stack - uint infiniteNesting; - size_t[] memory; - Trace[] merge; - static struct Trace - { - ulong mask; - size_t offset; - - bool mark(size_t idx) - { - immutable d = idx - offset; - if (d < 64) // including overflow - { - immutable p = mask & (1UL << d); - mask |= 1UL << d; - return p != 0; - } - else - { - offset = idx; - mask = 1; - return false; - } - } - } - //local slice of matches, global for backref - Group!DataIndex[] matches, backrefed; - size_t _refCount; -final: - - override @property ref size_t refCount() { return _refCount; } - override @property ref const(RegEx) pattern(){ return re; } - - enum kicked = __traits(hasMember, Stream, "search"); - - static size_t initialMemory(const ref RegEx re) - { - return stackSize(re)*size_t.sizeof + re.hotspotTableSize*Trace.sizeof; - } - - static size_t stackSize(const ref RegEx re) - { - size_t itemSize = stateSize - + re.ngroup * (Group!DataIndex).sizeof / size_t.sizeof; - return initialStack * itemSize + 2; - } - - @property bool atStart(){ return index == 0; } - - @property bool atEnd(){ return index == s.lastIndex && s.atEnd; } - - void next() - { - if (!s.nextChar(front, index)) - index = s.lastIndex; - } - - void search() - { - static if (kicked) - { - if (!s.search(re.kickstart, front, index)) - { - index = s.lastIndex; - } - } - else - next(); - } - - // - void newStack() - { - auto chunk = mallocArray!(size_t)(stackSize(re)); - chunk[0] = cast(size_t)(memory.ptr); - chunk[1] = lastState; - memory = chunk[2..$]; - lastState = 0; - } - - bool prevStack() - { - // pointer to previous block - size_t* prev = cast(size_t*) memory.ptr[-2]; - if (!prev) - { - // The last segment is freed in RegexMatch - return false; - } - else - { - import core.memory : pureFree; - // memory used in previous block - size_t size = memory.ptr[-1]; - pureFree(memory.ptr-2); - memory = prev[0 .. size]; - lastState = size; - return true; - } - } - - void initExternalMemory(void[] memBlock) - { - merge = arrayInChunk!(Trace)(re.hotspotTableSize, memBlock); - merge[] = Trace.init; - memory = cast(size_t[]) memBlock; - memory[0] = 0; // hidden pointer - memory[1] = 0; // used size - memory = memory[2..$]; - } - - void initialize(ref const RegEx program, Stream stream, void[] memBlock) - { - s = stream; - exhausted = false; - initExternalMemory(memBlock); - backrefed = null; - } - - override void dupTo(Matcher!Char m, void[] memBlock) - { - auto backtracking = cast(BacktrackingMatcher) m; - backtracking.s = s; - backtracking.front = front; - backtracking.index = index; - backtracking.exhausted = exhausted; - backtracking.initExternalMemory(memBlock); - } - - override Matcher!Char rearm(in Char[] data) - { - merge[] = Trace.init; - exhausted = false; - s = Stream(data); - next(); - return this; - } - - this(ref const RegEx program, Stream stream, void[] memBlock, dchar ch, DataIndex idx) - { - _refCount = 1; - re = program; - nativeFn = null; - initialize(program, stream, memBlock); - front = ch; - index = idx; - } - - this(ref const RegEx program, MatchFn func, Stream stream, void[] memBlock) - { - _refCount = 1; - re = program; - initialize(program, stream, memBlock); - nativeFn = func; - next(); - } - - this(ref const RegEx program, Stream stream, void[] memBlock) - { - _refCount = 1; - re = program; - nativeFn = null; - initialize(program, stream, memBlock); - next(); - } - - auto fwdMatcher(ref const RegEx re, void[] memBlock) - { - alias BackMatcher = BacktrackingMatcher!(Char, Stream); - auto fwdMatcher = new BackMatcher(re, s, memBlock, front, index); - return fwdMatcher; - } - - auto bwdMatcher(ref const RegEx re, void[] memBlock) - { - alias BackMatcher = BacktrackingMatcher!(Char, typeof(s.loopBack(index))); - auto fwdMatcher = - new BackMatcher(re, s.loopBack(index), memBlock); - return fwdMatcher; - } - - // - int matchFinalize() - { - immutable start = index; - immutable val = matchImpl(); - if (val) - {//stream is updated here - matches[0].begin = start; - matches[0].end = index; - if (!(re.flags & RegexOption.global) || atEnd) - exhausted = true; - if (start == index)//empty match advances input - next(); - return val; - } - else - return 0; - } - - //lookup next match, fill matches with indices into input - override int match(Group!DataIndex[] matches) - { - debug(std_regex_matcher) - { - writeln("------------------------------------------"); - } - if (exhausted) //all matches collected - return false; - this.matches = matches; - if (re.flags & RegexInfo.oneShot) - { - exhausted = true; - const DataIndex start = index; - immutable m = matchImpl(); - if (m) - { - matches[0].begin = start; - matches[0].end = index; - } - return m; - } - static if (kicked) - { - if (!re.kickstart.empty) - { - for (;;) - { - immutable val = matchFinalize(); - if (val) - return val; - else - { - if (atEnd) - break; - search(); - if (atEnd) - { - exhausted = true; - return matchFinalize(); - } - } - } - exhausted = true; - return 0; //early return - } - } - //no search available - skip a char at a time - for (;;) - { - immutable val = matchFinalize(); - if (val) - return val; - else - { - if (atEnd) - break; - next(); - if (atEnd) - { - exhausted = true; - return matchFinalize(); - } - } - } - exhausted = true; - return 0; - } - - /+ - match subexpression against input, - results are stored in matches - +/ - int matchImpl() pure - { - if (nativeFn) - { - debug(std_regex_ctr) writeln("using C-T matcher"); - return nativeFn(this); - } - else - { - pc = 0; - counter = 0; - lastState = 0; - infiniteNesting = 0; - matches[] = Group!DataIndex.init; - auto start = s._index; - debug(std_regex_matcher) - writeln("Try match starting at ", s[index .. s.lastIndex]); - for (;;) - { - debug(std_regex_matcher) - writefln("PC: %s\tCNT: %s\t%s \tfront: %s src: %s", - pc, counter, disassemble(re.ir, pc, re.dict), - front, s._index); - switch (re.ir[pc].code) - { - case IR.OrChar://assumes IRL!(OrChar) == 1 - if (atEnd) - goto L_backtrack; - uint len = re.ir[pc].sequence; - uint end = pc + len; - if (re.ir[pc].data != front && re.ir[pc+1].data != front) - { - for (pc = pc+2; pc < end; pc++) - if (re.ir[pc].data == front) - break; - if (pc == end) - goto L_backtrack; - } - pc = end; - next(); - break; - case IR.Char: - if (atEnd || front != re.ir[pc].data) - goto L_backtrack; - pc += IRL!(IR.Char); - next(); - break; - case IR.Any: - if (atEnd) - goto L_backtrack; - pc += IRL!(IR.Any); - next(); - break; - case IR.CodepointSet: - if (atEnd || !re.charsets[re.ir[pc].data].scanFor(front)) - goto L_backtrack; - next(); - pc += IRL!(IR.CodepointSet); - break; - case IR.Trie: - if (atEnd || !re.matchers[re.ir[pc].data][front]) - goto L_backtrack; - next(); - pc += IRL!(IR.Trie); - break; - case IR.Wordboundary: - dchar back; - DataIndex bi; - //at start & end of input - if (atStart && wordMatcher[front]) - { - pc += IRL!(IR.Wordboundary); - break; - } - else if (atEnd && s.loopBack(index).nextChar(back, bi) - && wordMatcher[back]) - { - pc += IRL!(IR.Wordboundary); - break; - } - else if (s.loopBack(index).nextChar(back, bi)) - { - immutable af = wordMatcher[front]; - immutable ab = wordMatcher[back]; - if (af ^ ab) - { - pc += IRL!(IR.Wordboundary); - break; - } - } - goto L_backtrack; - case IR.Notwordboundary: - dchar back; - DataIndex bi; - //at start & end of input - if (atStart && wordMatcher[front]) - goto L_backtrack; - else if (atEnd && s.loopBack(index).nextChar(back, bi) - && wordMatcher[back]) - goto L_backtrack; - else if (s.loopBack(index).nextChar(back, bi)) - { - immutable af = wordMatcher[front]; - immutable ab = wordMatcher[back]; - if (af ^ ab) - goto L_backtrack; - } - pc += IRL!(IR.Wordboundary); - break; - case IR.Bof: - if (atStart) - pc += IRL!(IR.Bol); - else - goto L_backtrack; - break; - case IR.Bol: - dchar back; - DataIndex bi; - if (atStart) - pc += IRL!(IR.Bol); - else if (s.loopBack(index).nextChar(back,bi) - && endOfLine(back, front == '\n')) - { - pc += IRL!(IR.Bol); - } - else - goto L_backtrack; - break; - case IR.Eof: - if (atEnd) - pc += IRL!(IR.Eol); - else - goto L_backtrack; - break; - case IR.Eol: - dchar back; - DataIndex bi; - debug(std_regex_matcher) writefln("EOL (front 0x%x) %s", front, s[index .. s.lastIndex]); - //no matching inside \r\n - if (atEnd || (endOfLine(front, s.loopBack(index).nextChar(back,bi) - && back == '\r'))) - { - pc += IRL!(IR.Eol); - } - else - goto L_backtrack; - break; - case IR.InfiniteStart, IR.InfiniteQStart: - pc += re.ir[pc].data + IRL!(IR.InfiniteStart); - //now pc is at end IR.Infinite(Q)End - uint len = re.ir[pc].data; - if (re.ir[pc].code == IR.InfiniteEnd) - { - pushState(pc+IRL!(IR.InfiniteEnd), counter); - pc -= len; - } - else - { - pushState(pc - len, counter); - pc += IRL!(IR.InfiniteEnd); - } - break; - case IR.InfiniteBloomStart: - pc += re.ir[pc].data + IRL!(IR.InfiniteBloomStart); - //now pc is at end IR.InfiniteBloomEnd - immutable len = re.ir[pc].data; - immutable filterIdx = re.ir[pc+2].raw; - if (re.filters[filterIdx][front]) - pushState(pc+IRL!(IR.InfiniteBloomEnd), counter); - pc -= len; - break; - case IR.RepeatStart, IR.RepeatQStart: - pc += re.ir[pc].data + IRL!(IR.RepeatStart); - break; - case IR.RepeatEnd: - case IR.RepeatQEnd: - if (merge[re.ir[pc + 1].raw+counter].mark(index)) - { - // merged! - goto L_backtrack; - } - //len, step, min, max - immutable len = re.ir[pc].data; - immutable step = re.ir[pc+2].raw; - immutable min = re.ir[pc+3].raw; - immutable max = re.ir[pc+4].raw; - if (counter < min) - { - counter += step; - pc -= len; - } - else if (counter < max) - { - if (re.ir[pc].code == IR.RepeatEnd) - { - pushState(pc + IRL!(IR.RepeatEnd), counter%step); - counter += step; - pc -= len; - } - else - { - pushState(pc - len, counter + step); - counter = counter%step; - pc += IRL!(IR.RepeatEnd); - } - } - else - { - counter = counter%step; - pc += IRL!(IR.RepeatEnd); - } - break; - case IR.InfiniteEnd: - case IR.InfiniteQEnd: - debug(std_regex_matcher) writeln("Infinited nesting:", infiniteNesting); - if (merge[re.ir[pc + 1].raw+counter].mark(index)) - { - // merged! - goto L_backtrack; - } - immutable len = re.ir[pc].data; - if (re.ir[pc].code == IR.InfiniteEnd) - { - pushState(pc + IRL!(IR.InfiniteEnd), counter); - pc -= len; - } - else - { - pushState(pc-len, counter); - pc += IRL!(IR.InfiniteEnd); - } - break; - case IR.InfiniteBloomEnd: - debug(std_regex_matcher) writeln("Infinited nesting:", infiniteNesting); - if (merge[re.ir[pc + 1].raw+counter].mark(index)) - { - // merged! - goto L_backtrack; - } - immutable len = re.ir[pc].data; - immutable filterIdx = re.ir[pc+2].raw; - if (re.filters[filterIdx][front]) - { - infiniteNesting--; - pushState(pc + IRL!(IR.InfiniteBloomEnd), counter); - infiniteNesting++; - } - pc -= len; - break; - case IR.OrEnd: - if (merge[re.ir[pc + 1].raw+counter].mark(index)) - { - // merged! - goto L_backtrack; - } - pc += IRL!(IR.OrEnd); - break; - case IR.OrStart: - pc += IRL!(IR.OrStart); - goto case; - case IR.Option: - immutable len = re.ir[pc].data; - if (re.ir[pc+len].code == IR.GotoEndOr)//not a last one - { - pushState(pc + len + IRL!(IR.Option), counter); //remember 2nd branch - } - pc += IRL!(IR.Option); - break; - case IR.GotoEndOr: - pc = pc + re.ir[pc].data + IRL!(IR.GotoEndOr); - break; - case IR.GroupStart: - immutable n = re.ir[pc].data; - matches[n].begin = index; - debug(std_regex_matcher) writefln("IR group #%u starts at %u", n, index); - pc += IRL!(IR.GroupStart); - break; - case IR.GroupEnd: - immutable n = re.ir[pc].data; - matches[n].end = index; - debug(std_regex_matcher) writefln("IR group #%u ends at %u", n, index); - pc += IRL!(IR.GroupEnd); - break; - case IR.LookaheadStart: - case IR.NeglookaheadStart: - immutable len = re.ir[pc].data; - auto save = index; - immutable ms = re.ir[pc+1].raw, me = re.ir[pc+2].raw; - auto mem = pureMalloc(initialMemory(re))[0 .. initialMemory(re)]; - scope(exit) pureFree(mem.ptr); - auto slicedRe = re.withCode(re.ir[ - pc+IRL!(IR.LookaheadStart) .. pc+IRL!(IR.LookaheadStart)+len+IRL!(IR.LookaheadEnd) - ]); - static if (Stream.isLoopback) - { - auto matcher = bwdMatcher(slicedRe, mem); - } - else - { - auto matcher = fwdMatcher(slicedRe, mem); - } - matcher.matches = matches[ms .. me]; - matcher.backrefed = backrefed.empty ? matches : backrefed; - immutable match = (matcher.matchImpl() != 0) ^ (re.ir[pc].code == IR.NeglookaheadStart); - s.reset(save); - next(); - if (!match) - goto L_backtrack; - else - { - pc += IRL!(IR.LookaheadStart)+len+IRL!(IR.LookaheadEnd); - } - break; - case IR.LookbehindStart: - case IR.NeglookbehindStart: - immutable len = re.ir[pc].data; - immutable ms = re.ir[pc+1].raw, me = re.ir[pc+2].raw; - auto mem = pureMalloc(initialMemory(re))[0 .. initialMemory(re)]; - scope(exit) pureFree(mem.ptr); - auto slicedRe = re.withCode(re.ir[ - pc + IRL!(IR.LookbehindStart) .. pc + IRL!(IR.LookbehindStart) + len + IRL!(IR.LookbehindEnd) - ]); - static if (Stream.isLoopback) - { - alias Matcher = BacktrackingMatcher!(Char, Stream); - auto matcher = new Matcher(slicedRe, s, mem, front, index); - } - else - { - alias Matcher = BacktrackingMatcher!(Char, typeof(s.loopBack(index))); - auto matcher = new Matcher(slicedRe, s.loopBack(index), mem); - } - matcher.matches = matches[ms .. me]; - matcher.backrefed = backrefed.empty ? matches : backrefed; - immutable match = (matcher.matchImpl() != 0) ^ (re.ir[pc].code == IR.NeglookbehindStart); - if (!match) - goto L_backtrack; - else - { - pc += IRL!(IR.LookbehindStart)+len+IRL!(IR.LookbehindEnd); - } - break; - case IR.Backref: - immutable n = re.ir[pc].data; - auto referenced = re.ir[pc].localRef - ? s[matches[n].begin .. matches[n].end] - : s[backrefed[n].begin .. backrefed[n].end]; - while (!atEnd && !referenced.empty && front == referenced.front) - { - next(); - referenced.popFront(); - } - if (referenced.empty) - pc++; - else - goto L_backtrack; - break; - case IR.Nop: - pc += IRL!(IR.Nop); - break; - case IR.LookaheadEnd: - case IR.NeglookaheadEnd: - case IR.LookbehindEnd: - case IR.NeglookbehindEnd: - case IR.End: - // cleanup stale stack blocks if any - while (prevStack()) {} - return re.ir[pc].data; - default: - debug(std_regex_debug) printBytecode(re.ir[0..$]); - assert(0); - L_backtrack: - if (!popState()) - { - s.reset(start); - return 0; - } - } - } - } - assert(0); - } - - @property size_t stackAvail() - { - return memory.length - lastState; - } - - void stackPush(T)(T val) - if (!isDynamicArray!T) - { - *cast(T*)&memory[lastState] = val; - enum delta = (T.sizeof+size_t.sizeof/2)/size_t.sizeof; - lastState += delta; - debug(std_regex_matcher) writeln("push element SP= ", lastState); - } - - void stackPush(T)(T[] val) - { - static assert(T.sizeof % size_t.sizeof == 0); - (cast(T*)&memory[lastState])[0 .. val.length] - = val[0..$]; - lastState += val.length*(T.sizeof/size_t.sizeof); - debug(std_regex_matcher) writeln("push array SP= ", lastState); - } - - void stackPop(T)(ref T val) - if (!isDynamicArray!T) - { - enum delta = (T.sizeof+size_t.sizeof/2)/size_t.sizeof; - lastState -= delta; - val = *cast(T*)&memory[lastState]; - debug(std_regex_matcher) writeln("pop element SP= ", lastState); - } - - void stackPop(T)(T[] val) - { - stackPop(val); // call ref version - } - void stackPop(T)(ref T[] val) - { - lastState -= val.length*(T.sizeof/size_t.sizeof); - val[0..$] = (cast(T*)&memory[lastState])[0 .. val.length]; - debug(std_regex_matcher) writeln("pop array SP= ", lastState); - } - //helper function, saves engine state - void pushState(uint pc, uint counter) - { - if (stateSize + 2 * matches.length > stackAvail) - { - newStack(); - } - *cast(State*)&memory[lastState] = - State(index, pc, counter, infiniteNesting); - lastState += stateSize; - memory[lastState .. lastState + 2 * matches.length] = (cast(size_t[]) matches)[]; - lastState += 2*matches.length; - debug(std_regex_matcher) - writefln("Saved(pc=%s) front: %s src: %s", - pc, front, s[index .. s.lastIndex]); - } - - //helper function, restores engine state - bool popState() - { - if (!lastState && !prevStack()) - return false; - lastState -= 2*matches.length; - auto pm = cast(size_t[]) matches; - pm[] = memory[lastState .. lastState + 2 * matches.length]; - lastState -= stateSize; - State* state = cast(State*)&memory[lastState]; - index = state.index; - pc = state.pc; - counter = state.counter; - infiniteNesting = state.infiniteNesting; - debug(std_regex_matcher) - { - writefln("Restored matches", front, s[index .. s.lastIndex]); - foreach (i, m; matches) - writefln("Sub(%d) : %s..%s", i, m.begin, m.end); - } - s.reset(index); - next(); - debug(std_regex_matcher) - writefln("Backtracked (pc=%s) front: %s src: %s", - pc, front, s[index .. s.lastIndex]); - return true; - } -} - -//very shitty string formatter, $$ replaced with next argument converted to string -@trusted string ctSub( U...)(string format, U args) -{ - import std.conv : to; - bool seenDollar; - foreach (i, ch; format) - { - if (ch == '$') - { - if (seenDollar) - { - static if (args.length > 0) - { - return format[0 .. i - 1] ~ to!string(args[0]) - ~ ctSub(format[i + 1 .. $], args[1 .. $]); - } - else - assert(0); - } - else - seenDollar = true; - } - else - seenDollar = false; - - } - return format; -} - -struct CtContext -{ - import std.conv : to, text; - //dirty flags - bool counter; - //to mark the portion of matches to save - int match, total_matches; - int reserved; - const(CodepointInterval)[][] charsets; - - - //state of codegenerator - static struct CtState - { - string code; - int addr; - } - - this(Char)(ref const Regex!Char re) - { - match = 1; - reserved = 1; //first match is skipped - total_matches = re.ngroup; - foreach (ref set; re.charsets) - { - charsets ~= set.intervals; - } - } - - CtContext lookaround(uint s, uint e) - { - CtContext ct; - ct.total_matches = e - s; - ct.match = 1; - return ct; - } - - //restore state having current context - string restoreCode() - { - string text; - //stack is checked in L_backtrack - text ~= counter - ? " - stackPop(counter);" - : " - counter = 0;"; - if (match < total_matches) - { - text ~= ctSub(" - stackPop(matches[$$..$$]);", reserved, match); - text ~= ctSub(" - matches[$$..$] = typeof(matches[0]).init;", match); - } - else - text ~= ctSub(" - stackPop(matches[$$..$]);", reserved); - return text; - } - - //save state having current context - string saveCode(uint pc, string count_expr="counter") - { - string text = ctSub(" - if (stackAvail < $$*(Group!(DataIndex)).sizeof/size_t.sizeof + $$) - { - newStack(); - }", match - reserved, cast(int) counter + 2); - if (match < total_matches) - text ~= ctSub(" - stackPush(matches[$$..$$]);", reserved, match); - else - text ~= ctSub(" - stackPush(matches[$$..$]);", reserved); - text ~= counter ? ctSub(" - stackPush($$);", count_expr) : ""; - text ~= ctSub(" - stackPush(index); stackPush($$); \n", pc); - return text; - } - - // - CtState ctGenBlock(const(Bytecode)[] ir, int addr) - { - CtState result; - result.addr = addr; - while (!ir.empty) - { - auto n = ctGenGroup(ir, result.addr); - result.code ~= n.code; - result.addr = n.addr; - } - return result; - } - - // - CtState ctGenGroup(ref const(Bytecode)[] ir, int addr) - { - import std.algorithm.comparison : max; - auto bailOut = "goto L_backtrack;"; - auto nextInstr = ctSub("goto case $$;", addr+1); - CtState r; - assert(!ir.empty); - switch (ir[0].code) - { - case IR.InfiniteStart, IR.InfiniteBloomStart,IR.InfiniteQStart, IR.RepeatStart, IR.RepeatQStart: - immutable infLoop = - ir[0].code == IR.InfiniteStart || ir[0].code == IR.InfiniteQStart || - ir[0].code == IR.InfiniteBloomStart; - - counter = counter || - ir[0].code == IR.RepeatStart || ir[0].code == IR.RepeatQStart; - immutable len = ir[0].data; - auto nir = ir[ir[0].length .. ir[0].length+len]; - r = ctGenBlock(nir, addr+1); - //start/end codegen - //r.addr is at last test+ jump of loop, addr+1 is body of loop - nir = ir[ir[0].length + len .. $]; - r.code = ctGenFixupCode(ir[0 .. ir[0].length], addr, r.addr) ~ r.code; - r.code ~= ctGenFixupCode(nir, r.addr, addr+1); - r.addr += 2; //account end instruction + restore state - ir = nir; - break; - case IR.OrStart: - immutable len = ir[0].data; - auto nir = ir[ir[0].length .. ir[0].length+len]; - r = ctGenAlternation(nir, addr); - ir = ir[ir[0].length + len .. $]; - assert(ir[0].code == IR.OrEnd); - ir = ir[ir[0].length..$]; - break; - case IR.LookaheadStart: - case IR.NeglookaheadStart: - case IR.LookbehindStart: - case IR.NeglookbehindStart: - immutable len = ir[0].data; - immutable behind = ir[0].code == IR.LookbehindStart || ir[0].code == IR.NeglookbehindStart; - immutable negative = ir[0].code == IR.NeglookaheadStart || ir[0].code == IR.NeglookbehindStart; - string fwdType = "typeof(fwdMatcher(re, []))"; - string bwdType = "typeof(bwdMatcher(re, []))"; - string fwdCreate = "fwdMatcher(re, mem)"; - string bwdCreate = "bwdMatcher(re, mem)"; - immutable start = IRL!(IR.LookbehindStart); - immutable end = IRL!(IR.LookbehindStart)+len+IRL!(IR.LookaheadEnd); - CtContext context = lookaround(ir[1].raw, ir[2].raw); //split off new context - auto slice = ir[start .. end]; - r.code ~= ctSub(` - case $$: //fake lookaround "atom" - static if (typeof(matcher.s).isLoopback) - alias Lookaround = $$; - else - alias Lookaround = $$; - static bool matcher_$$(Lookaround matcher) @trusted - { - //(neg)lookaround piece start - $$ - //(neg)lookaround piece ends - } - auto save = index; - auto mem = pureMalloc(initialMemory(re))[0 .. initialMemory(re)]; - scope(exit) pureFree(mem.ptr); - static if (typeof(matcher.s).isLoopback) - auto lookaround = $$; - else - auto lookaround = $$; - lookaround.matches = matches[$$..$$]; - lookaround.backrefed = backrefed.empty ? matches : backrefed; - lookaround.nativeFn = &matcher_$$; //hookup closure's binary code - int match = $$; - s.reset(save); - next(); - if (match) - $$ - else - $$`, addr, - behind ? fwdType : bwdType, behind ? bwdType : fwdType, - addr, context.ctGenRegEx(slice), - behind ? fwdCreate : bwdCreate, behind ? bwdCreate : fwdCreate, - ir[1].raw, ir[2].raw, //start - end of matches slice - addr, - negative ? "!lookaround.matchImpl()" : "lookaround.matchImpl()", - nextInstr, bailOut); - ir = ir[end .. $]; - r.addr = addr + 1; - break; - case IR.LookaheadEnd: case IR.NeglookaheadEnd: - case IR.LookbehindEnd: case IR.NeglookbehindEnd: - ir = ir[IRL!(IR.LookaheadEnd) .. $]; - r.addr = addr; - break; - default: - assert(ir[0].isAtom, text(ir[0].mnemonic)); - r = ctGenAtom(ir, addr); - } - return r; - } - - //generate source for bytecode contained in OrStart ... OrEnd - CtState ctGenAlternation(const(Bytecode)[] ir, int addr) - { - CtState[] pieces; - CtState r; - enum optL = IRL!(IR.Option); - for (;;) - { - assert(ir[0].code == IR.Option); - auto len = ir[0].data; - if (optL+len < ir.length && ir[optL+len].code == IR.Option)//not a last option - { - auto nir = ir[optL .. optL+len-IRL!(IR.GotoEndOr)]; - r = ctGenBlock(nir, addr+2);//space for Option + restore state - //r.addr+1 to account GotoEndOr at end of branch - r.code = ctGenFixupCode(ir[0 .. ir[0].length], addr, r.addr+1) ~ r.code; - addr = r.addr+1;//leave space for GotoEndOr - pieces ~= r; - ir = ir[optL + len .. $]; - } - else - { - pieces ~= ctGenBlock(ir[optL..$], addr); - addr = pieces[$-1].addr; - break; - } - } - r = pieces[0]; - for (uint i = 1; i < pieces.length; i++) - { - r.code ~= ctSub(` - case $$: - goto case $$; `, pieces[i-1].addr, addr); - r.code ~= pieces[i].code; - } - r.addr = addr; - return r; - } - - // generate fixup code for instruction in ir, - // fixup means it has an alternative way for control flow - string ctGenFixupCode(const(Bytecode)[] ir, int addr, int fixup) - { - return ctGenFixupCode(ir, addr, fixup); // call ref Bytecode[] version - } - string ctGenFixupCode(ref const(Bytecode)[] ir, int addr, int fixup) - { - string r; - string testCode; - r = ctSub(` - case $$: debug(std_regex_matcher) writeln("#$$");`, - addr, addr); - switch (ir[0].code) - { - case IR.InfiniteStart, IR.InfiniteQStart, IR.InfiniteBloomStart: - r ~= ctSub( ` - goto case $$;`, fixup); - ir = ir[ir[0].length..$]; - break; - case IR.InfiniteEnd: - testCode = ctQuickTest(ir[IRL!(IR.InfiniteEnd) .. $],addr + 1); - r ~= ctSub( ` - if (merge[$$+counter].mark(index)) - { - // merged! - goto L_backtrack; - } - - $$ - { - $$ - } - goto case $$; - case $$: //restore state and go out of loop - $$ - goto case;`, ir[1].raw, testCode, saveCode(addr+1), fixup, - addr+1, restoreCode()); - ir = ir[ir[0].length..$]; - break; - case IR.InfiniteBloomEnd: - //TODO: check bloom filter and skip on failure - testCode = ctQuickTest(ir[IRL!(IR.InfiniteBloomEnd) .. $],addr + 1); - r ~= ctSub( ` - if (merge[$$+counter].mark(index)) - { - // merged! - goto L_backtrack; - } - - $$ - { - $$ - } - goto case $$; - case $$: //restore state and go out of loop - $$ - goto case;`, ir[1].raw, testCode, saveCode(addr+1), fixup, - addr+1, restoreCode()); - ir = ir[ir[0].length..$]; - break; - case IR.InfiniteQEnd: - testCode = ctQuickTest(ir[IRL!(IR.InfiniteEnd) .. $],addr + 1); - auto altCode = testCode.length ? ctSub("else goto case $$;", fixup) : ""; - r ~= ctSub( ` - if (merge[$$+counter].mark(index)) - { - // merged! - goto L_backtrack; - } - - $$ - { - $$ - goto case $$; - } - $$ - case $$://restore state and go inside loop - $$ - goto case $$;`, ir[1].raw, - testCode, saveCode(addr+1), addr+2, altCode, - addr+1, restoreCode(), fixup); - ir = ir[ir[0].length..$]; - break; - case IR.RepeatStart, IR.RepeatQStart: - r ~= ctSub( ` - goto case $$;`, fixup); - ir = ir[ir[0].length..$]; - break; - case IR.RepeatEnd, IR.RepeatQEnd: - //len, step, min, max - immutable len = ir[0].data; - immutable step = ir[2].raw; - immutable min = ir[3].raw; - immutable max = ir[4].raw; - r ~= ctSub(` - if (merge[$$+counter].mark(index)) - { - // merged! - goto L_backtrack; - } - if (counter < $$) - { - debug(std_regex_matcher) writeln("RepeatEnd min case pc=", $$); - counter += $$; - goto case $$; - }`, ir[1].raw, min, addr, step, fixup); - if (ir[0].code == IR.RepeatEnd) - { - string counter_expr = ctSub("counter % $$", step); - r ~= ctSub(` - else if (counter < $$) - { - $$ - counter += $$; - goto case $$; - }`, max, saveCode(addr+1, counter_expr), step, fixup); - } - else - { - string counter_expr = ctSub("counter % $$", step); - r ~= ctSub(` - else if (counter < $$) - { - $$ - counter = counter % $$; - goto case $$; - }`, max, saveCode(addr+1,counter_expr), step, addr+2); - } - r ~= ctSub(` - else - { - counter = counter % $$; - goto case $$; - } - case $$: //restore state - $$ - goto case $$;`, step, addr+2, addr+1, restoreCode(), - ir[0].code == IR.RepeatEnd ? addr+2 : fixup ); - ir = ir[ir[0].length..$]; - break; - case IR.Option: - r ~= ctSub( ` - { - $$ - } - goto case $$; - case $$://restore thunk to go to the next group - $$ - goto case $$;`, saveCode(addr+1), addr+2, - addr+1, restoreCode(), fixup); - ir = ir[ir[0].length..$]; - break; - default: - assert(0, text(ir[0].mnemonic)); - } - return r; - } - - - string ctQuickTest(const(Bytecode)[] ir, int id) - { - uint pc = 0; - while (pc < ir.length && ir[pc].isAtom) - { - if (ir[pc].code == IR.GroupStart || ir[pc].code == IR.GroupEnd) - { - pc++; - } - else if (ir[pc].code == IR.Backref) - break; - else - { - auto code = ctAtomCode(ir[pc..$], -1); - return ctSub(` - int test_$$() - { - $$ //$$ - } - if (test_$$() >= 0)`, id, code.ptr ? code : "return 0;", - ir[pc].mnemonic, id); - } - } - return ""; - } - - //process & generate source for simple bytecodes at front of ir using address addr - CtState ctGenAtom(ref const(Bytecode)[] ir, int addr) - { - CtState result; - result.code = ctAtomCode(ir, addr); - ir.popFrontN(ir[0].code == IR.OrChar ? ir[0].sequence : ir[0].length); - result.addr = addr + 1; - return result; - } - - //D code for atom at ir using address addr, addr < 0 means quickTest - string ctAtomCode(const(Bytecode)[] ir, int addr) - { - string code; - string bailOut, nextInstr; - if (addr < 0) - { - bailOut = "return -1;"; - nextInstr = "return 0;"; - } - else - { - bailOut = "goto L_backtrack;"; - nextInstr = ctSub("goto case $$;", addr+1); - code ~= ctSub( ` - case $$: debug(std_regex_matcher) writeln("#$$"); - `, addr, addr); - } - switch (ir[0].code) - { - case IR.OrChar://assumes IRL!(OrChar) == 1 - code ~= ctSub(` - if (atEnd) - $$`, bailOut); - immutable len = ir[0].sequence; - for (uint i = 0; i < len; i++) - { - code ~= ctSub( ` - if (front == $$) - { - $$ - $$ - }`, ir[i].data, addr >= 0 ? "next();" :"", nextInstr); - } - code ~= ctSub( ` - $$`, bailOut); - break; - case IR.Char: - code ~= ctSub( ` - if (atEnd || front != $$) - $$ - $$ - $$`, ir[0].data, bailOut, addr >= 0 ? "next();" :"", nextInstr); - break; - case IR.Any: - code ~= ctSub( ` - if (atEnd || (!(re.flags & RegexOption.singleline) - && (front == '\r' || front == '\n'))) - $$ - $$ - $$`, bailOut, addr >= 0 ? "next();" :"",nextInstr); - break; - case IR.CodepointSet: - if (charsets.length) - { - string name = `func_`~to!string(addr+1); - string funcCode = CodepointSet.toSourceCode(charsets[ir[0].data], name); - code ~= ctSub( ` - static $$ - if (atEnd || !$$(front)) - $$ - $$ - $$`, funcCode, name, bailOut, addr >= 0 ? "next();" :"", nextInstr); - } - else - code ~= ctSub( ` - if (atEnd || !re.charsets[$$].scanFor(front)) - $$ - $$ - $$`, ir[0].data, bailOut, addr >= 0 ? "next();" :"", nextInstr); - break; - case IR.Trie: - if (charsets.length && charsets[ir[0].data].length <= 8) - goto case IR.CodepointSet; - code ~= ctSub( ` - if (atEnd || !re.matchers[$$][front]) - $$ - $$ - $$`, ir[0].data, bailOut, addr >= 0 ? "next();" :"", nextInstr); - break; - case IR.Wordboundary: - code ~= ctSub( ` - dchar back; - DataIndex bi; - if (atStart && wordMatcher[front]) - { - $$ - } - else if (atEnd && s.loopBack(index).nextChar(back, bi) - && wordMatcher[back]) - { - $$ - } - else if (s.loopBack(index).nextChar(back, bi)) - { - bool af = wordMatcher[front]; - bool ab = wordMatcher[back]; - if (af ^ ab) - { - $$ - } - } - $$`, nextInstr, nextInstr, nextInstr, bailOut); - break; - case IR.Notwordboundary: - code ~= ctSub( ` - dchar back; - DataIndex bi; - //at start & end of input - if (atStart && wordMatcher[front]) - $$ - else if (atEnd && s.loopBack(index).nextChar(back, bi) - && wordMatcher[back]) - $$ - else if (s.loopBack(index).nextChar(back, bi)) - { - bool af = wordMatcher[front]; - bool ab = wordMatcher[back]; - if (af ^ ab) - $$ - } - $$`, bailOut, bailOut, bailOut, nextInstr); - - break; - case IR.Bol: - code ~= ctSub(` - dchar back; - DataIndex bi; - if (atStart || (s.loopBack(index).nextChar(back,bi) - && endOfLine(back, front == '\n'))) - { - debug(std_regex_matcher) writeln("BOL matched"); - $$ - } - else - $$`, nextInstr, bailOut); - - break; - case IR.Bof: - code ~= ctSub(` - if (atStart) - { - debug(std_regex_matcher) writeln("BOF matched"); - $$ - } - else - $$`, nextInstr, bailOut); - break; - case IR.Eol: - code ~= ctSub(` - dchar back; - DataIndex bi; - debug(std_regex_matcher) writefln("EOL (front 0x%x) %s", front, s[index .. s.lastIndex]); - //no matching inside \r\n - if (atEnd || (endOfLine(front, s.loopBack(index).nextChar(back,bi) - && back == '\r'))) - { - debug(std_regex_matcher) writeln("EOL matched"); - $$ - } - else - $$`, nextInstr, bailOut); - break; - case IR.Eof: - code ~= ctSub(` - if (atEnd) - { - debug(std_regex_matcher) writeln("BOF matched"); - $$ - } - else - $$`, nextInstr, bailOut); - break; - case IR.GroupStart: - code ~= ctSub(` - matches[$$].begin = index; - $$`, ir[0].data, nextInstr); - match = ir[0].data+1; - break; - case IR.GroupEnd: - code ~= ctSub(` - matches[$$].end = index; - $$`, ir[0].data, nextInstr); - break; - case IR.Backref: - string mStr = "auto referenced = "; - mStr ~= ir[0].localRef - ? ctSub("s[matches[$$].begin .. matches[$$].end];", - ir[0].data, ir[0].data) - : ctSub("s[backrefed[$$].begin .. backrefed[$$].end];", - ir[0].data, ir[0].data); - code ~= ctSub( ` - $$ - while (!atEnd && !referenced.empty && front == referenced.front) - { - next(); - referenced.popFront(); - } - if (referenced.empty) - $$ - else - $$`, mStr, nextInstr, bailOut); - break; - case IR.Nop: - case IR.End: - break; - default: - assert(0, text(ir[0].mnemonic, " is not supported yet")); - } - return code; - } - - //generate D code for the whole regex - public string ctGenRegEx(const(Bytecode)[] ir) - { - auto bdy = ctGenBlock(ir, 0); - auto r = ` - import core.memory : pureMalloc, pureFree; - with(matcher) - { - pc = 0; - counter = 0; - lastState = 0; - matches[] = Group!DataIndex.init; - auto start = s._index;`; - r ~= ` - goto StartLoop; - debug(std_regex_matcher) writeln("Try CT matching starting at ",s[index .. s.lastIndex]); - L_backtrack: - if (lastState || prevStack()) - { - stackPop(pc); - stackPop(index); - s.reset(index); - next(); - } - else - { - s.reset(start); - return false; - } - StartLoop: - switch (pc) - { - `; - r ~= bdy.code; - r ~= ctSub(` - case $$: break;`,bdy.addr); - r ~= ` - default: - assert(0); - } - // cleanup stale stack blocks - while (prevStack()) {} - return true; - } - `; - return r; - } - -} - -string ctGenRegExCode(Char)(const Regex!Char re) -{ - auto context = CtContext(re); - return context.ctGenRegEx(re.ir); -} diff --git a/phobos/std/regex/internal/generator.d b/phobos/std/regex/internal/generator.d deleted file mode 100644 index c1fe4cd..0000000 --- a/phobos/std/regex/internal/generator.d +++ /dev/null @@ -1,187 +0,0 @@ -/* - Generators - components that generate strings for a given regex pattern. - - For the moment undocumented, and is subject to change. -*/ -module std.regex.internal.generator; - -/* - Useful utility for self-testing, an infinite range of string samples - that _have_ to match given compiled regex. - Caveats: supports only a simple subset of bytecode. -*/ -@trusted private struct SampleGenerator(Char) -{ - import std.array : appender, Appender; - import std.format.write : formattedWrite; - import std.random : Xorshift; - import std.regex.internal.ir : Regex, IR, IRL; - import std.utf : isValidDchar, byChar; - Regex!Char re; - Appender!(char[]) app; - uint limit, seed; - Xorshift gen; - //generator for pattern r, with soft maximum of threshold elements - //and a given random seed - this(ref Regex!Char r, uint threshold, uint randomSeed) - { - re = r; - limit = threshold; - seed = randomSeed; - app = appender!(Char[])(); - compose(); - } - - uint rand(uint x) - { - uint r = gen.front % x; - gen.popFront(); - return r; - } - - void compose() - { - uint pc = 0, counter = 0, dataLenOld = uint.max; - for (;;) - { - switch (re.ir[pc].code) - { - case IR.Char: - formattedWrite(app,"%s", cast(dchar) re.ir[pc].data); - pc += IRL!(IR.Char); - break; - case IR.OrChar: - uint len = re.ir[pc].sequence; - formattedWrite(app, "%s", cast(dchar) re.ir[pc + rand(len)].data); - pc += len; - break; - case IR.CodepointSet: - case IR.Trie: - auto set = re.charsets[re.ir[pc].data]; - auto x = rand(cast(uint) set.byInterval.length); - auto y = rand(set.byInterval[x].b - set.byInterval[x].a); - formattedWrite(app, "%s", cast(dchar)(set.byInterval[x].a+y)); - pc += IRL!(IR.CodepointSet); - break; - case IR.Any: - uint x; - do - { - x = rand(0x11_000); - }while (x == '\r' || x == '\n' || !isValidDchar(x)); - formattedWrite(app, "%s", cast(dchar) x); - pc += IRL!(IR.Any); - break; - case IR.GotoEndOr: - pc += IRL!(IR.GotoEndOr)+re.ir[pc].data; - assert(re.ir[pc].code == IR.OrEnd); - goto case; - case IR.OrEnd: - pc += IRL!(IR.OrEnd); - break; - case IR.OrStart: - pc += IRL!(IR.OrStart); - goto case; - case IR.Option: - uint next = pc + re.ir[pc].data + IRL!(IR.Option); - uint nOpt = 0; - //queue next Option - while (re.ir[next].code == IR.Option) - { - nOpt++; - next += re.ir[next].data + IRL!(IR.Option); - } - nOpt++; - nOpt = rand(nOpt); - for (;nOpt; nOpt--) - { - pc += re.ir[pc].data + IRL!(IR.Option); - } - assert(re.ir[pc].code == IR.Option); - pc += IRL!(IR.Option); - break; - case IR.RepeatStart:case IR.RepeatQStart: - pc += IRL!(IR.RepeatStart)+re.ir[pc].data; - goto case IR.RepeatEnd; - case IR.RepeatEnd: - case IR.RepeatQEnd: - uint len = re.ir[pc].data; - uint step = re.ir[pc+2].raw; - uint min = re.ir[pc+3].raw; - if (counter < min) - { - counter += step; - pc -= len; - break; - } - uint max = re.ir[pc+4].raw; - if (counter < max) - { - if (app.data.length < limit && rand(3) > 0) - { - pc -= len; - counter += step; - } - else - { - counter = counter%step; - pc += IRL!(IR.RepeatEnd); - } - } - else - { - counter = counter%step; - pc += IRL!(IR.RepeatEnd); - } - break; - case IR.InfiniteStart, IR.InfiniteBloomStart, IR.InfiniteQStart: - pc += re.ir[pc].data + IRL!(IR.InfiniteStart); - goto case IR.InfiniteEnd; //both Q and non-Q - case IR.InfiniteEnd, IR.InfiniteBloomEnd, IR.InfiniteQEnd: - uint len = re.ir[pc].data; - if (app.data.length == dataLenOld) - { - pc += IRL!(IR.InfiniteEnd); - break; - } - dataLenOld = cast(uint) app.data.length; - if (app.data.length < limit && rand(3) > 0) - pc = pc - len; - else - pc = pc + re.ir[pc].length; - break; - case IR.GroupStart, IR.GroupEnd: - pc += IRL!(IR.GroupStart); - break; - case IR.Bol, IR.Wordboundary, IR.Notwordboundary: - case IR.LookaheadStart, IR.NeglookaheadStart, IR.LookbehindStart, IR.NeglookbehindStart: - default: - return; - } - } - } - - @property Char[] front() - { - return app.data; - } - - enum empty = false; - - void popFront() - { - app.shrinkTo(0); - compose(); - } -} - -@system unittest -{ - import std.range, std.regex; - auto re = regex(`P[a-z]{3,}q`); - auto gen = SampleGenerator!char(re, 20, 3141592); - static assert(isInputRange!(typeof(gen))); - //@@@BUG@@@ somehow gen.take(1_000) doesn't work - foreach (v; take(gen, 1_000)) - assert(v.match(re)); -} diff --git a/phobos/std/regex/internal/ir.d b/phobos/std/regex/internal/ir.d deleted file mode 100644 index 04b902f..0000000 --- a/phobos/std/regex/internal/ir.d +++ /dev/null @@ -1,1212 +0,0 @@ -/* - Implementation of std.regex IR, an intermediate representation - of a regular expression pattern. - - This is a common ground between frontend regex component (parser) - and backend components - generators, matchers and other "filters". -*/ -module std.regex.internal.ir; - -package(std.regex): - -import std.exception, std.meta, std.range.primitives, std.traits, std.uni; - -debug(std_regex_parser) import std.stdio; -// just a common trait, may be moved elsewhere -alias BasicElementOf(Range) = Unqual!(ElementEncodingType!Range); - -enum privateUseStart = '\U000F0000', privateUseEnd ='\U000FFFFD'; - -// heuristic value determines maximum CodepointSet length suitable for linear search -enum maxCharsetUsed = 6; - -// another variable to tweak behavior of caching generated Tries for character classes -enum maxCachedMatchers = 8; - -alias Trie = CodepointSetTrie!(13, 8); -alias makeTrie = codepointSetTrie!(13, 8); - -CharMatcher[CodepointSet] matcherCache; - -//accessor with caching -@trusted CharMatcher getMatcher(CodepointSet set) -{ - // almost all properties of AA are not @safe - // https://issues.dlang.org/show_bug.cgi?id=6357 - if (__ctfe || maxCachedMatchers == 0) - return CharMatcher(set); - else - { - auto p = set in matcherCache; - if (p) - return *p; - if (matcherCache.length == maxCachedMatchers) - { - // flush enmatchers in trieCache - matcherCache = null; - } - return (matcherCache[set] = CharMatcher(set)); - } -} - -// Force pure because that is needed -// Templated so that we don't pull in std.uni wordCharacter unnecessarily. -@property ref wordMatcher()() pure -{ - static auto actual() - { - static CharMatcher matcher; - static bool haveMatcher; - - if (!haveMatcher) - { - matcher = CharMatcher(wordCharacter); - haveMatcher = true; - } - - return &matcher; - } - - // WORKAROUND: if the compiler won't memoize the output of the function for us, - // we'll do it with pure and there will be casts and it'll be happy about it. - // This is unfortunately needed to make std.regex as a whole faster to import & use - // in build times ~500ms. - return *(cast(immutable(CharMatcher)* function() @safe nothrow @nogc pure)&actual)(); -} - -// some special Unicode white space characters -private enum NEL = '\u0085', LS = '\u2028', PS = '\u2029'; - -//Regular expression engine/parser options: -// global - search all nonoverlapping matches in input -// casefold - case insensitive matching, do casefolding on match in unicode mode -// freeform - ignore whitespace in pattern, to match space use [ ] or \s -// multiline - switch ^, $ detect start and end of linesinstead of just start and end of input -enum RegexOption: uint { - global = 0x1, - casefold = 0x2, - freeform = 0x4, - nonunicode = 0x8, - multiline = 0x10, - singleline = 0x20 -} -//do not reorder this list -alias RegexOptionNames = AliasSeq!('g', 'i', 'x', 'U', 'm', 's'); -static assert( RegexOption.max < 0x80); - -package(std) string regexOptionsToString()(uint flags) nothrow pure @safe -{ - flags &= (RegexOption.max << 1) - 1; - if (!flags) - return ""; - char[RegexOptionNames.length] buffer = void; - size_t pos = 0; - foreach (i, flag; __traits(allMembers, RegexOption)) - if (flags & __traits(getMember, RegexOption, flag)) - buffer[pos++] = RegexOptionNames[i]; - return buffer[0 .. pos].idup; -} - -// flags that allow guide execution of engine -enum RegexInfo : uint { oneShot = 0x80 } - -// IR bit pattern: 0b1_xxxxx_yy -// where yy indicates class of instruction, xxxxx for actual operation code -// 00: atom, a normal instruction -// 01: open, opening of a group, has length of contained IR in the low bits -// 10: close, closing of a group, has length of contained IR in the low bits -// 11 unused -// -// Loops with Q (non-greedy, with ? mark) must have the same size / other properties as non Q version -// Possible changes: -//* merge group, option, infinite/repeat start (to never copy during parsing of (a|b){1,2}) -//* reorganize groups to make n args easier to find, or simplify the check for groups of similar ops -// (like lookaround), or make it easier to identify hotspots. - -enum IR:uint { - Char = 0b1_00000_00, //a character - Any = 0b1_00001_00, //any character - CodepointSet = 0b1_00010_00, //a most generic CodepointSet [...] - Trie = 0b1_00011_00, //CodepointSet implemented as Trie - //match with any of a consecutive OrChar's in this sequence - //(used for case insensitive match) - //OrChar holds in upper two bits of data total number of OrChars in this _sequence_ - //the drawback of this representation is that it is difficult - // to detect a jump in the middle of it - OrChar = 0b1_00100_00, - Nop = 0b1_00101_00, //no operation (padding) - End = 0b1_00110_00, //end of program - Bol = 0b1_00111_00, //beginning of a line ^ - Eol = 0b1_01000_00, //end of a line $ - Wordboundary = 0b1_01001_00, //boundary of a word - Notwordboundary = 0b1_01010_00, //not a word boundary - Backref = 0b1_01011_00, //backreference to a group (that has to be pinned, i.e. locally unique) (group index) - GroupStart = 0b1_01100_00, //start of a group (x) (groupIndex+groupPinning(1bit)) - GroupEnd = 0b1_01101_00, //end of a group (x) (groupIndex+groupPinning(1bit)) - Option = 0b1_01110_00, //start of an option within an alternation x | y (length) - GotoEndOr = 0b1_01111_00, //end of an option (length of the rest) - Bof = 0b1_10000_00, //begining of "file" (string) ^ - Eof = 0b1_10001_00, //end of "file" (string) $ - //... any additional atoms here - - OrStart = 0b1_00000_01, //start of alternation group (length) - OrEnd = 0b1_00000_10, //end of the or group (length,mergeIndex) - //with this instruction order - //bit mask 0b1_00001_00 could be used to test/set greediness - InfiniteStart = 0b1_00001_01, //start of an infinite repetition x* (length) - InfiniteEnd = 0b1_00001_10, //end of infinite repetition x* (length,mergeIndex) - InfiniteQStart = 0b1_00010_01, //start of a non eager infinite repetition x*? (length) - InfiniteQEnd = 0b1_00010_10, //end of non eager infinite repetition x*? (length,mergeIndex) - InfiniteBloomStart = 0b1_00011_01, //start of an filtered infinite repetition x* (length) - InfiniteBloomEnd = 0b1_00011_10, //end of filtered infinite repetition x* (length,mergeIndex) - RepeatStart = 0b1_00100_01, //start of a {n,m} repetition (length) - RepeatEnd = 0b1_00100_10, //end of x{n,m} repetition (length,step,minRep,maxRep) - RepeatQStart = 0b1_00101_01, //start of a non eager x{n,m}? repetition (length) - RepeatQEnd = 0b1_00101_10, //end of non eager x{n,m}? repetition (length,step,minRep,maxRep) - - // - LookaheadStart = 0b1_00110_01, //begin of the lookahead group (length) - LookaheadEnd = 0b1_00110_10, //end of a lookahead group (length) - NeglookaheadStart = 0b1_00111_01, //start of a negative lookahead (length) - NeglookaheadEnd = 0b1_00111_10, //end of a negative lookahead (length) - LookbehindStart = 0b1_01000_01, //start of a lookbehind (length) - LookbehindEnd = 0b1_01000_10, //end of a lookbehind (length) - NeglookbehindStart = 0b1_01001_01, //start of a negative lookbehind (length) - NeglookbehindEnd = 0b1_01001_10, //end of negative lookbehind (length) -} - -//a shorthand for IR length - full length of specific opcode evaluated at compile time -template IRL(IR code) -{ - enum uint IRL = lengthOfIR(code); -} -static assert(IRL!(IR.LookaheadStart) == 3); - -//how many parameters follow the IR, should be optimized fixing some IR bits -int immediateParamsIR(IR i) @safe pure nothrow @nogc -{ - switch (i) - { - case IR.OrEnd,IR.InfiniteEnd,IR.InfiniteQEnd: - return 1; // merge table index - case IR.InfiniteBloomEnd: - return 2; // bloom filter index + merge table index - case IR.RepeatEnd, IR.RepeatQEnd: - return 4; - case IR.LookaheadStart, IR.NeglookaheadStart, IR.LookbehindStart, IR.NeglookbehindStart: - return 2; // start-end of captures used - default: - return 0; - } -} - -//full length of IR instruction inlcuding all parameters that might follow it -int lengthOfIR(IR i) @safe pure nothrow @nogc -{ - return 1 + immediateParamsIR(i); -} - -//full length of the paired IR instruction inlcuding all parameters that might follow it -int lengthOfPairedIR(IR i) @safe pure nothrow @nogc -{ - return 1 + immediateParamsIR(pairedIR(i)); -} - -//if the operation has a merge point (this relies on the order of the ops) -bool hasMerge(IR i) @safe pure nothrow @nogc -{ - return (i&0b11)==0b10 && i <= IR.RepeatQEnd; -} - -//is an IR that opens a "group" -bool isStartIR(IR i) @safe pure nothrow @nogc -{ - return (i&0b11)==0b01; -} - -//is an IR that ends a "group" -bool isEndIR(IR i) @safe pure nothrow @nogc -{ - return (i&0b11)==0b10; -} - -//is a standalone IR -bool isAtomIR(IR i) @safe pure nothrow @nogc -{ - return (i&0b11)==0b00; -} - -//makes respective pair out of IR i, swapping start/end bits of instruction -IR pairedIR(IR i) @safe pure nothrow @nogc -{ - assert(isStartIR(i) || isEndIR(i)); - return cast(IR) (i ^ 0b11); -} - -//encoded IR instruction -@safe pure -struct Bytecode -{ - uint raw; - //natural constraints - enum maxSequence = 2+4; - enum maxData = 1 << 22; - enum maxRaw = 1 << 31; - -@safe pure: - this(IR code, uint data) - { - assert(data < (1 << 22) && code < 256); - raw = code << 24 | data; - } - - this(IR code, uint data, uint seq) - { - assert(data < (1 << 22) && code < 256 ); - assert(seq >= 2 && seq < maxSequence); - raw = code << 24 | (seq - 2)<<22 | data; - } - - //store raw data - static Bytecode fromRaw(uint data) - { - Bytecode t; - t.raw = data; - return t; - } - - // bit twiddling helpers - // 0-arg template due to https://issues.dlang.org/show_bug.cgi?id=10985 - @property uint data()() const { return raw & 0x003f_ffff; } - - @property void data()(uint val) - { - raw = (raw & ~0x003f_ffff) | (val & 0x003f_ffff); - } - - // ditto - // 0-arg template due to https://issues.dlang.org/show_bug.cgi?id=10985 - @property uint sequence()() const { return 2 + (raw >> 22 & 0x3); } - - // ditto - // 0-arg template due to https://issues.dlang.org/show_bug.cgi?id=10985 - @property IR code()() const { return cast(IR)(raw >> 24); } - - //ditto - @property bool hotspot() const { return hasMerge(code); } - - //test the class of this instruction - @property bool isAtom() const { return isAtomIR(code); } - - //ditto - @property bool isStart() const { return isStartIR(code); } - - //ditto - @property bool isEnd() const { return isEndIR(code); } - - //number of arguments for this instruction - @property int args() const { return immediateParamsIR(code); } - - //mark this GroupStart or GroupEnd as referenced in backreference - void setBackrefence() - { - assert(code == IR.GroupStart || code == IR.GroupEnd); - raw = raw | 1 << 23; - } - - //is referenced - @property bool backreference() const - { - assert(code == IR.GroupStart || code == IR.GroupEnd); - return cast(bool)(raw & 1 << 23); - } - - //mark as local reference (for backrefs in lookarounds) - void setLocalRef() - { - assert(code == IR.Backref); - raw = raw | 1 << 23; - } - - //is a local ref - @property bool localRef() const - { - assert(code == IR.Backref); - return cast(bool)(raw & 1 << 23); - } - - //human readable name of instruction - @trusted @property string mnemonic()() const - {//@@@BUG@@@ to is @system - import std.conv : to; - return to!string(code); - } - - //full length of instruction - @property uint length() const - { - return lengthOfIR(code); - } - - //full length of respective start/end of this instruction - @property uint pairedLength() const - { - return lengthOfPairedIR(code); - } - - //returns bytecode of paired instruction (assuming this one is start or end) - @property Bytecode paired() const - {//depends on bit and struct layout order - assert(isStart || isEnd); - return Bytecode.fromRaw(raw ^ 0b11 << 24); - } - - //gets an index into IR block of the respective pair - uint indexOfPair(uint pc) const - { - assert(isStart || isEnd); - return isStart ? pc + data + length : pc - data - lengthOfPairedIR(code); - } -} - -static assert(Bytecode.sizeof == 4); - - -//index entry structure for name --> number of submatch -struct NamedGroup -{ - string name; - uint group; -} - -//holds pair of start-end markers for a submatch -struct Group(DataIndex) -{ - DataIndex begin = DataIndex.max; - DataIndex end = DataIndex.min; - - bool opCast(T : bool)() const - { - return begin <= end; - } - - @trusted string toString()() const - { - if (begin < end) - return "(unmatched)"; - import std.array : appender; - import std.format.write : formattedWrite; - auto a = appender!string(); - formattedWrite(a, "%s..%s", begin, end); - return a.data; - } -} - -//debugging tool, prints out instruction along with opcodes -debug(std_regex_parser) @trusted string disassemble(in Bytecode[] irb, uint pc, in NamedGroup[] dict=[]) -{ - import std.array : appender; - import std.format.write : formattedWrite; - auto output = appender!string(); - formattedWrite(output,"%s", irb[pc].mnemonic); - switch (irb[pc].code) - { - case IR.Char: - formattedWrite(output, " %s (0x%x)",cast(dchar) irb[pc].data, irb[pc].data); - break; - case IR.OrChar: - formattedWrite(output, " %s (0x%x) seq=%d", cast(dchar) irb[pc].data, irb[pc].data, irb[pc].sequence); - break; - case IR.RepeatStart, IR.InfiniteStart, IR.InfiniteBloomStart, - IR.Option, IR.GotoEndOr, IR.OrStart: - //forward-jump instructions - uint len = irb[pc].data; - formattedWrite(output, " pc=>%u", pc+len+IRL!(IR.RepeatStart)); - break; - case IR.RepeatEnd, IR.RepeatQEnd: //backward-jump instructions - uint len = irb[pc].data; - formattedWrite(output, " pc=>%u min=%u max=%u step=%u", - pc - len, irb[pc + 3].raw, irb[pc + 4].raw, irb[pc + 2].raw); - break; - case IR.InfiniteEnd, IR.InfiniteQEnd, IR.InfiniteBloomEnd, IR.OrEnd: //ditto - uint len = irb[pc].data; - formattedWrite(output, " pc=>%u", pc-len); - break; - case IR.LookaheadEnd, IR.NeglookaheadEnd: //ditto - uint len = irb[pc].data; - formattedWrite(output, " pc=>%u", pc-len); - break; - case IR.GroupStart, IR.GroupEnd: - uint n = irb[pc].data; - string name; - foreach (v;dict) - if (v.group == n) - { - name = "'"~v.name~"'"; - break; - } - formattedWrite(output, " %s #%u " ~ (irb[pc].backreference ? "referenced" : ""), - name, n); - break; - case IR.LookaheadStart, IR.NeglookaheadStart, IR.LookbehindStart, IR.NeglookbehindStart: - uint len = irb[pc].data; - uint start = irb[pc+1].raw, end = irb[pc+2].raw; - formattedWrite(output, " pc=>%u [%u..%u]", pc + len + IRL!(IR.LookaheadStart), start, end); - break; - case IR.Backref: case IR.CodepointSet: case IR.Trie: - uint n = irb[pc].data; - formattedWrite(output, " %u", n); - if (irb[pc].code == IR.Backref) - formattedWrite(output, " %s", irb[pc].localRef ? "local" : "global"); - break; - default://all data-free instructions - } - if (irb[pc].hotspot) - formattedWrite(output, " Hotspot %u", irb[pc+1].raw); - return output.data; -} - -//disassemble the whole chunk -debug(std_regex_parser) @trusted void printBytecode()(in Bytecode[] slice, in NamedGroup[] dict=[]) -{ - import std.stdio : writeln; - for (uint pc=0; pc= end; } - @property size_t length() { return end - start; } - alias opDollar = length; - @property NamedGroupRange save() - { - return NamedGroupRange(groups, start, end); - } - void popFront() { assert(!empty); start++; } - void popBack() { assert(!empty); end--; } - string opIndex()(size_t i) - { - assert(start + i < end, - "Requested named group is out of range."); - return groups[start+i].name; - } - NamedGroupRange opSlice(size_t low, size_t high) { - assert(low <= high); - assert(start + high <= end); - return NamedGroupRange(groups, start + low, start + high); - } - NamedGroupRange opSlice() { return this.save; } - } - return NamedGroupRange(dict, 0, dict.length); - } - -package(std.regex): - import std.regex.internal.kickstart : Kickstart; //TODO: get rid of this dependency - const(NamedGroup)[] dict; // maps name -> user group number - uint ngroup; // number of internal groups - uint maxCounterDepth; // max depth of nested {n,m} repetitions - uint hotspotTableSize; // number of entries in merge table - uint threadCount; // upper bound on number of Thompson VM threads - uint flags; // global regex flags - public const(CharMatcher)[] matchers; // tables that represent character sets - public const(BitTable)[] filters; // bloom filters for conditional loops - uint[] backrefed; // bit array of backreferenced submatches - Kickstart!Char kickstart; - MatcherFactory!Char factory; // produces optimal matcher for this pattern - immutable(Char)[] pattern; // copy of pattern to serve as cache key - - const(Regex) withFactory(MatcherFactory!Char factory) pure const @trusted - { - auto r = cast() this; - r.factory = factory; - return r; - } - - const(Regex) withFlags(uint newFlags) pure const @trusted - { - auto r = cast() this; - r.flags = newFlags; - return r; - } - - const(Regex) withCode(const(Bytecode)[] code) pure const @trusted - { - auto r = cast() this; - r.ir = code.dup; // TODO: sidestep const instead? - return r; - } - - const(Regex) withNGroup(uint nGroup) pure const @trusted - { - auto r = cast() this; - r.ngroup = nGroup; - return r; - } - - //bit access helper - uint isBackref(uint n) - { - if (n/32 >= backrefed.length) - return 0; - return backrefed[n / 32] & (1 << (n & 31)); - } - - //check if searching is not needed - void checkIfOneShot() - { - L_CheckLoop: - for (uint i = 0; i < ir.length; i += ir[i].length) - { - switch (ir[i].code) - { - case IR.Bof: - flags |= RegexInfo.oneShot; - break L_CheckLoop; - case IR.GroupStart, IR.GroupEnd, IR.Bol, IR.Eol, IR.Eof, - IR.Wordboundary, IR.Notwordboundary: - break; - default: - break L_CheckLoop; - } - } - } - - //print out disassembly a program's IR - @trusted debug(std_regex_parser) void print() const - {//@@@BUG@@@ write is system - for (uint i = 0; i < ir.length; i += ir[i].length) - { - writefln("%d\t%s ", i, disassemble(ir, i, dict)); - } - writeln("Total merge table size: ", hotspotTableSize); - writeln("Max counter nesting depth: ", maxCounterDepth); - } - - public string toString()() const - { - import std.format : format; - static if (is(typeof(pattern) : string)) - alias patternString = pattern; - else - { - import std.conv : to; - auto patternString = conv.to!string(pattern); - } - auto quotedEscapedPattern = format("%(%s %)", [patternString]); - auto flagString = regexOptionsToString(flags); - return "Regex!" ~ Char.stringof ~ "(" ~ quotedEscapedPattern ~ ", \"" ~ flagString ~ "\")"; - } -} - -// The stuff below this point is temporarrily part of IR module -// but may need better place in the future (all internals) -package(std.regex): - -//Simple UTF-string abstraction compatible with stream interface -struct Input(Char) -if (is(Char :dchar)) -{ - import std.utf : decode; - alias DataIndex = size_t; - enum bool isLoopback = false; - alias String = const(Char)[]; - String _origin; - size_t _index; - - //constructs Input object out of plain string - this(String input, size_t idx = 0) - { - _origin = input; - _index = idx; - } - - //codepoint at current stream position - pragma(inline, true) bool nextChar(ref dchar res, ref size_t pos) - { - pos = _index; - // DMD's inliner hates multiple return functions - // but can live with single statement if/else bodies - bool n = !(_index == _origin.length); - if (n) - res = decode(_origin, _index); - return n; - } - @property bool atEnd(){ - return _index == _origin.length; - } - bool search(Kickstart)(ref const Kickstart kick, ref dchar res, ref size_t pos) - { - size_t idx = kick.search(_origin, _index); - _index = idx; - return nextChar(res, pos); - } - - //index of at End position - @property size_t lastIndex(){ return _origin.length; } - - //support for backtracker engine, might not be present - void reset(size_t index){ _index = index; } - - String opSlice(size_t start, size_t end){ return _origin[start .. end]; } - - auto loopBack(size_t index){ return BackLooper!Input(this, index); } -} - -struct BackLooperImpl(Input) -{ - import std.utf : strideBack; - alias DataIndex = size_t; - alias String = Input.String; - enum bool isLoopback = true; - String _origin; - size_t _index; - this(Input input, size_t index) - { - _origin = input._origin; - _index = index; - } - this(String input) - { - _origin = input; - _index = input.length; - } - @trusted bool nextChar(ref dchar res,ref size_t pos) - { - pos = _index; - if (_index == 0) - return false; - - res = _origin[0.._index].back; - _index -= strideBack(_origin, _index); - - return true; - } - @property atEnd(){ return _index == 0 || _index == strideBack(_origin, _index); } - auto loopBack(size_t index){ return Input(_origin, index); } - - //support for backtracker engine, might not be present - //void reset(size_t index){ _index = index ? index-std.utf.strideBack(_origin, index) : 0; } - void reset(size_t index){ _index = index; } - - String opSlice(size_t start, size_t end){ return _origin[end .. start]; } - //index of at End position - @property size_t lastIndex(){ return 0; } -} - -template BackLooper(E) -{ - static if (is(E : BackLooperImpl!U, U)) - { - alias BackLooper = U; - } - else - { - alias BackLooper = BackLooperImpl!E; - } -} - -//both helpers below are internal, on its own are quite "explosive" -//unsafe, no initialization of elements -@system pure T[] mallocArray(T)(size_t len) -{ - import core.memory : pureMalloc; - return (cast(T*) pureMalloc(len * T.sizeof))[0 .. len]; -} - -//very unsafe, no initialization -@system T[] arrayInChunk(T)(size_t len, ref void[] chunk) -{ - auto ret = (cast(T*) chunk.ptr)[0 .. len]; - chunk = chunk[len * T.sizeof .. $]; - return ret; -} - -// -@trusted uint lookupNamedGroup(String)(const(NamedGroup)[] dict, String name) -{//equal is @system? - import std.algorithm.comparison : equal; - import std.algorithm.iteration : map; - import std.conv : text; - import std.range : assumeSorted; - - auto fnd = assumeSorted!"cmp(a,b) < 0"(map!"a.name"(dict)).lowerBound(name).length; - enforce(fnd < dict.length && equal(dict[fnd].name, name), - text("no submatch named ", name)); - return dict[fnd].group; -} - -// whether ch is one of unicode newline sequences -// 0-arg template due to https://issues.dlang.org/show_bug.cgi?id=10985 -bool endOfLine()(dchar front, bool seenCr) -{ - return ((front == '\n') ^ seenCr) || front == '\r' - || front == NEL || front == LS || front == PS; -} - -// 0-arg template due to https://issues.dlang.org/show_bug.cgi?id=10985 -bool startOfLine()(dchar back, bool seenNl) -{ - return ((back == '\r') ^ seenNl) || back == '\n' - || back == NEL || back == LS || back == PS; -} - -///Exception object thrown in case of errors during regex compilation. -public class RegexException : Exception -{ - mixin basicExceptionCtors; -} - -// simple 128-entry bit-table used with a hash function -struct BitTable { - uint[4] filter; - - this(CodepointSet set){ - foreach (iv; set.byInterval) - { - foreach (v; iv.a .. iv.b) - add(v); - } - } - - void add()(dchar ch){ - immutable i = index(ch); - filter[i >> 5] |= 1<<(i & 31); - } - // non-zero -> might be present, 0 -> absent - bool opIndex()(dchar ch) const{ - immutable i = index(ch); - return (filter[i >> 5]>>(i & 31)) & 1; - } - - static uint index()(dchar ch){ - return ((ch >> 7) ^ ch) & 0x7F; - } -} - -struct CharMatcher { - BitTable ascii; // fast path for ASCII - Trie trie; // slow path for Unicode - - this(CodepointSet set) - { - auto asciiSet = set & unicode.ASCII; - ascii = BitTable(asciiSet); - trie = makeTrie(set); - } - - bool opIndex()(dchar ch) const - { - if (ch < 0x80) - return ascii[ch]; - else - return trie[ch]; - } -} - -// Internal non-resizeble array, switches between inline storage and CoW -// POD-only -struct SmallFixedArray(T, uint SMALL=3) -if (!hasElaborateDestructor!T) -{ - import std.internal.memory : enforceMalloc; - import core.memory : pureFree; - static struct Payload - { - size_t refcount; - T[0] placeholder; - inout(T)* ptr() inout { return placeholder.ptr; } - } - static assert(Payload.sizeof == size_t.sizeof); - union - { - Payload* big; - T[SMALL] small; - } - size_t _sizeMask; - enum BIG_MASK = size_t(1)<<(8*size_t.sizeof-1); - enum SIZE_MASK = ~BIG_MASK; - - @property bool isBig() const { return (_sizeMask & BIG_MASK) != 0; } - @property size_t length() const { return _sizeMask & SIZE_MASK; } - - this(size_t size) - { - if (size <= SMALL) - { - small[] = T.init; - _sizeMask = size; - } - else - { - big = cast(Payload*) enforceMalloc(Payload.sizeof + T.sizeof*size); - big.refcount = 1; - _sizeMask = size | BIG_MASK; - } - } - - private @trusted @property inout(T)[] internalSlice() inout - { - return isBig ? big.ptr[0 .. length] : small[0 .. length]; - } - - this(this) @trusted - { - if (isBig) - { - big.refcount++; - } - } - - bool opEquals(SmallFixedArray a) - { - return internalSlice[] == a.internalSlice[]; - } - - size_t toHash() const - { - return hashOf(internalSlice[]); - } - - ref inout(T) opIndex(size_t idx) inout - { - return internalSlice[idx]; - } - - // accesses big to test self-referencing so not @safe - @trusted ref opAssign(SmallFixedArray arr) - { - if (isBig) - { - if (arr.isBig) - { - if (big is arr.big) return this; // self-assign - else - { - abandonRef(); - _sizeMask = arr._sizeMask; - big = arr.big; - big.refcount++; - } - } - else - { - abandonRef(); - _sizeMask = arr._sizeMask; - small = arr.small; - } - } - else - { - if (arr.isBig) - { - _sizeMask = arr._sizeMask; - big = arr.big; - big.refcount++; - } - else - { - _sizeMask = arr._sizeMask; - small = arr.small; - } - } - return this; - } - - void mutate(scope void delegate(T[]) pure filler) - { - if (isBig && big.refcount != 1) // copy on write - { - auto oldSizeMask = _sizeMask; - auto newbig = cast(Payload*) enforceMalloc(Payload.sizeof + T.sizeof*length); - newbig.refcount = 1; - abandonRef(); - big = newbig; - _sizeMask = oldSizeMask; - } - filler(internalSlice); - } - - ~this() - { - if (isBig) - { - abandonRef(); - } - } - - @trusted private void abandonRef() - { - assert(isBig); - if (--big.refcount == 0) - { - pureFree(big); - _sizeMask = 0; - assert(!isBig); - } - } -} - -@system unittest -{ - alias SA = SmallFixedArray!(int, 2); - SA create(int[] data) - { - SA a = SA(data.length); - a.mutate((slice) { slice[] = data[]; }); - assert(a.internalSlice == data); - return a; - } - - { - SA a; - a = SA(1); - assert(a.length == 1); - a = SA.init; - assert(a.length == 0); - } - - { - SA a, b, c, d; - assert(a.length == 0); - assert(a.internalSlice == b.internalSlice); - a = create([1]); - assert(a.internalSlice == [1]); - b = create([2, 3]); - assert(b.internalSlice == [2, 3]); - c = create([3, 4, 5]); - d = create([5, 6, 7, 8]); - assert(c.isBig); - a = c; - assert(a.isBig); - assert(a.big is c.big); - assert(a.big.refcount == 2); - assert(a.internalSlice == [3, 4, 5]); - assert(c.internalSlice == [3, 4, 5]); - a = b; - assert(!a.isBig); - assert(a.internalSlice == [2, 3]); - assert(c.big.refcount == 1); - a = c; - assert(c.big.refcount == 2); - - // mutate copies on write if ref-count is not 1 - a.mutate((slice){ slice[] = 1; }); - assert(a.internalSlice == [1, 1, 1]); - assert(c.internalSlice == [3, 4, 5]); - assert(a.isBig && c.isBig); - assert(a.big.refcount == 1); - assert(c.big.refcount == 1); - - auto e = d; - assert(e.big.refcount == 2); - auto f = d; - f = a; - assert(f.isBig); - assert(f.internalSlice == [1, 1, 1]); - assert(f.big.refcount == 2); // a & f - assert(e.big.refcount == 2); // d & e - a = c; - assert(f.big.refcount == 1); // f - assert(e.big.refcount == 2); // d & e - a = a; - a = a; - a = a; - assert(a.big.refcount == 2); // a & c - } -} diff --git a/phobos/std/regex/internal/kickstart.d b/phobos/std/regex/internal/kickstart.d deleted file mode 100644 index 4529205..0000000 --- a/phobos/std/regex/internal/kickstart.d +++ /dev/null @@ -1,579 +0,0 @@ -/* - Kickstart is a coarse-grained "filter" engine that finds likely matches - to be verified by full-blown matcher. -*/ -module std.regex.internal.kickstart; - -package(std.regex): - -import std.range.primitives, std.utf; -import std.regex.internal.ir; - -//utility for shiftOr, returns a minimum number of bytes to test in a Char -uint effectiveSize(Char)() -{ - static if (is(Char == char)) - return 1; - else static if (is(Char == wchar)) - return 2; - else static if (is(Char == dchar)) - return 3; - else - static assert(0); -} - -/* - Kickstart engine using ShiftOr algorithm, - a bit parallel technique for inexact string searching. -*/ -struct ShiftOr(Char) -{ -private: - uint[] table; - uint fChar; - uint n_length; - enum charSize = effectiveSize!Char(); - //maximum number of chars in CodepointSet to process - enum uint charsetThreshold = 32_000; - static struct ShiftThread - { - uint[] tab; - uint mask; - uint idx; - uint pc, counter, hops; - this(uint newPc, uint newCounter, uint[] table) - { - pc = newPc; - counter = newCounter; - mask = 1; - idx = 0; - hops = 0; - tab = table; - } - - void setMask(uint idx, uint mask) - { - tab[idx] |= mask; - } - - void setInvMask(uint idx, uint mask) - { - tab[idx] &= ~mask; - } - - void set(alias setBits = setInvMask)(dchar ch) - { - static if (charSize == 3) - { - uint val = ch, tmask = mask; - setBits(val&0xFF, tmask); - tmask <<= 1; - val >>= 8; - setBits(val&0xFF, tmask); - tmask <<= 1; - val >>= 8; - assert(val <= 0x10); - setBits(val, tmask); - tmask <<= 1; - } - else - { - Char[dchar.sizeof/Char.sizeof] buf; - uint tmask = mask; - size_t total = encode(buf, ch); - for (size_t i = 0; i < total; i++, tmask<<=1) - { - static if (charSize == 1) - setBits(buf[i], tmask); - else static if (charSize == 2) - { - setBits(buf[i]&0xFF, tmask); - tmask <<= 1; - setBits(buf[i]>>8, tmask); - } - } - } - } - void add(dchar ch){ return set!setInvMask(ch); } - void advance(uint s) - { - mask <<= s; - idx += s; - } - @property bool full(){ return !mask; } - } - - static ShiftThread fork(ShiftThread t, uint newPc, uint newCounter) - { - ShiftThread nt = t; - nt.pc = newPc; - nt.counter = newCounter; - return nt; - } - - @trusted static ShiftThread fetch(ref ShiftThread[] worklist) - { - auto t = worklist[$-1]; - worklist.length -= 1; - if (!__ctfe) - cast(void) worklist.assumeSafeAppend(); - return t; - } - - static uint charLen(uint ch) - { - assert(ch <= 0x10FFFF); - return codeLength!Char(cast(dchar) ch)*charSize; - } - -public: - @trusted this(ref Regex!Char re, uint[] memory) - { - static import std.algorithm.comparison; - import std.algorithm.searching : countUntil; - import std.conv : text; - import std.range : assumeSorted; - assert(memory.length == 256); - fChar = uint.max; - // FNV-1a flavored hash (uses 32bits at a time) - ulong hash(uint[] tab) - { - ulong h = 0xcbf29ce484222325; - foreach (v; tab) - { - h ^= v; - h *= 0x100000001b3; - } - return h; - } - L_FindChar: - for (size_t i = 0;;) - { - switch (re.ir[i].code) - { - case IR.Char: - fChar = re.ir[i].data; - static if (charSize != 3) - { - Char[dchar.sizeof/Char.sizeof] buf; - encode(buf, fChar); - fChar = buf[0]; - } - fChar = fChar & 0xFF; - break L_FindChar; - case IR.GroupStart, IR.GroupEnd: - i += IRL!(IR.GroupStart); - break; - case IR.Bof, IR.Bol, IR.Wordboundary, IR.Notwordboundary: - i += IRL!(IR.Bol); - break; - default: - break L_FindChar; - } - } - table = memory; - table[] = uint.max; - alias MergeTab = bool[ulong]; - // use reasonably complex hash to identify equivalent tables - auto merge = new MergeTab[re.hotspotTableSize]; - ShiftThread[] trs; - ShiftThread t = ShiftThread(0, 0, table); - //locate first fixed char if any - n_length = 32; - for (;;) - { - L_Eval_Thread: - for (;;) - { - switch (re.ir[t.pc].code) - { - case IR.Char: - uint s = charLen(re.ir[t.pc].data); - if (t.idx+s > n_length) - goto L_StopThread; - t.add(re.ir[t.pc].data); - t.advance(s); - t.pc += IRL!(IR.Char); - break; - case IR.OrChar://assumes IRL!(OrChar) == 1 - uint len = re.ir[t.pc].sequence; - uint end = t.pc + len; - uint[Bytecode.maxSequence] s; - uint numS; - for (uint i = 0; i < len; i++) - { - auto x = charLen(re.ir[t.pc+i].data); - if (countUntil(s[0 .. numS], x) < 0) - s[numS++] = x; - } - for (uint i = t.pc; i < end; i++) - { - t.add(re.ir[i].data); - } - for (uint i = 0; i < numS; i++) - { - auto tx = fork(t, t.pc + len, t.counter); - if (tx.idx + s[i] <= n_length) - { - tx.advance(s[i]); - trs ~= tx; - } - } - if (!trs.empty) - t = fetch(trs); - else - goto L_StopThread; - break; - case IR.CodepointSet: - case IR.Trie: - auto set = re.charsets[re.ir[t.pc].data]; - uint[4] s; - uint numS; - static if (charSize == 3) - { - s[0] = charSize; - numS = 1; - } - else - { - - static if (charSize == 1) - static immutable codeBounds = [0x0, 0x7F, 0x80, 0x7FF, 0x800, 0xFFFF, 0x10000, 0x10FFFF]; - else //== 2 - static immutable codeBounds = [0x0, 0xFFFF, 0x10000, 0x10FFFF]; - uint[] arr = new uint[set.byInterval.length * 2]; - size_t ofs = 0; - foreach (ival; set.byInterval) - { - arr[ofs++] = ival.a; - arr[ofs++] = ival.b; - } - auto srange = assumeSorted!"a <= b"(arr); - for (uint i = 0; i < codeBounds.length/2; i++) - { - auto start = srange.lowerBound(codeBounds[2*i]).length; - auto end = srange.lowerBound(codeBounds[2*i+1]).length; - if (end > start || (end == start && (end & 1))) - s[numS++] = (i+1)*charSize; - } - } - if (numS == 0 || t.idx + s[numS-1] > n_length) - goto L_StopThread; - auto chars = set.length; - if (chars > charsetThreshold) - goto L_StopThread; - foreach (ch; set.byCodepoint) - { - //avoid surrogate pairs - if (0xD800 <= ch && ch <= 0xDFFF) - continue; - t.add(ch); - } - for (uint i = 0; i < numS; i++) - { - auto tx = fork(t, t.pc + IRL!(IR.CodepointSet), t.counter); - tx.advance(s[i]); - trs ~= tx; - } - if (!trs.empty) - t = fetch(trs); - else - goto L_StopThread; - break; - case IR.Any: - goto L_StopThread; - - case IR.GotoEndOr: - t.pc += IRL!(IR.GotoEndOr)+re.ir[t.pc].data; - assert(re.ir[t.pc].code == IR.OrEnd); - goto case; - case IR.OrEnd: - auto slot = re.ir[t.pc+1].raw+t.counter; - auto val = hash(t.tab); - if (val in merge[slot]) - goto L_StopThread; // merge equivalent - merge[slot][val] = true; - t.pc += IRL!(IR.OrEnd); - break; - case IR.OrStart: - t.pc += IRL!(IR.OrStart); - goto case; - case IR.Option: - uint next = t.pc + re.ir[t.pc].data + IRL!(IR.Option); - //queue next Option - if (re.ir[next].code == IR.Option) - { - trs ~= fork(t, next, t.counter); - } - t.pc += IRL!(IR.Option); - break; - case IR.RepeatStart:case IR.RepeatQStart: - t.pc += IRL!(IR.RepeatStart)+re.ir[t.pc].data; - goto case IR.RepeatEnd; - case IR.RepeatEnd: - case IR.RepeatQEnd: - auto slot = re.ir[t.pc+1].raw+t.counter; - auto val = hash(t.tab); - if (val in merge[slot]) - goto L_StopThread; // merge equivalent - merge[slot][val] = true; - uint len = re.ir[t.pc].data; - uint step = re.ir[t.pc+2].raw; - uint min = re.ir[t.pc+3].raw; - if (t.counter < min) - { - t.counter += step; - t.pc -= len; - break; - } - uint max = re.ir[t.pc+4].raw; - if (t.counter < max) - { - trs ~= fork(t, t.pc - len, t.counter + step); - t.counter = t.counter%step; - t.pc += IRL!(IR.RepeatEnd); - } - else - { - t.counter = t.counter%step; - t.pc += IRL!(IR.RepeatEnd); - } - break; - case IR.InfiniteStart, IR.InfiniteQStart: - t.pc += re.ir[t.pc].data + IRL!(IR.InfiniteStart); - goto case IR.InfiniteEnd; //both Q and non-Q - case IR.InfiniteEnd: - case IR.InfiniteQEnd: - auto slot = re.ir[t.pc+1].raw+t.counter; - auto val = hash(t.tab); - if (val in merge[slot]) - goto L_StopThread; // merge equivalent - merge[slot][val] = true; - uint len = re.ir[t.pc].data; - uint pc1, pc2; //branches to take in priority order - if (++t.hops == 32) - goto L_StopThread; - pc1 = t.pc + IRL!(IR.InfiniteEnd); - pc2 = t.pc - len; - trs ~= fork(t, pc2, t.counter); - t.pc = pc1; - break; - case IR.GroupStart, IR.GroupEnd: - t.pc += IRL!(IR.GroupStart); - break; - case IR.Bof, IR.Bol, IR.Wordboundary, IR.Notwordboundary: - t.pc += IRL!(IR.Bol); - break; - case IR.LookaheadStart, IR.NeglookaheadStart, IR.LookbehindStart, IR.NeglookbehindStart: - t.pc += IRL!(IR.LookaheadStart) + IRL!(IR.LookaheadEnd) + re.ir[t.pc].data; - break; - default: - L_StopThread: - assert(re.ir[t.pc].code >= 0x80, text(re.ir[t.pc].code)); - debug (fred_search) writeln("ShiftOr stumbled on ",re.ir[t.pc].mnemonic); - n_length = std.algorithm.comparison.min(t.idx, n_length); - break L_Eval_Thread; - } - } - if (trs.empty) - break; - t = fetch(trs); - } - debug(std_regex_search) - { - writeln("Min length: ", n_length); - } - } - - @property bool empty() const { return n_length == 0; } - - @property uint length() const{ return n_length/charSize; } - - // lookup compatible bit pattern in haystack, return starting index - // has a useful trait: if supplied with valid UTF indexes, - // returns only valid UTF indexes - // (that given the haystack in question is valid UTF string) - @trusted size_t search(const(Char)[] haystack, size_t idx) const - {//@BUG: apparently assumes little endian machines - import core.stdc.string : memchr; - import std.conv : text; - assert(!empty); - auto p = cast(const(ubyte)*)(haystack.ptr+idx); - uint state = uint.max; - uint limit = 1u<<(n_length - 1u); - debug(std_regex_search) writefln("Limit: %32b",limit); - if (fChar != uint.max) - { - const(ubyte)* end = cast(ubyte*)(haystack.ptr + haystack.length); - const orginalAlign = cast(size_t) p & (Char.sizeof-1); - while (p != end) - { - if (!~state) - {//speed up seeking first matching place - for (;;) - { - assert(p <= end, text(p," vs ", end)); - p = cast(ubyte*) memchr(p, fChar, end - p); - if (!p) - return haystack.length; - if ((cast(size_t) p & (Char.sizeof-1)) == orginalAlign) - break; - if (++p == end) - return haystack.length; - } - state = ~1u; - assert((cast(size_t) p & (Char.sizeof-1)) == orginalAlign); - static if (charSize == 3) - { - state = (state << 1) | table[p[1]]; - state = (state << 1) | table[p[2]]; - p += 4; - } - else - p++; - //first char is tested, see if that's all - if (!(state & limit)) - return (p-cast(ubyte*) haystack.ptr)/Char.sizeof - -length; - } - else - {//have some bits/states for possible matches, - //use the usual shift-or cycle - static if (charSize == 3) - { - state = (state << 1) | table[p[0]]; - state = (state << 1) | table[p[1]]; - state = (state << 1) | table[p[2]]; - p += 4; - } - else - { - state = (state << 1) | table[p[0]]; - p++; - } - if (!(state & limit)) - return (p-cast(ubyte*) haystack.ptr)/Char.sizeof - -length; - } - debug(std_regex_search) writefln("State: %32b", state); - } - } - else - { - //normal path, partially unrolled for char/wchar - static if (charSize == 3) - { - const(ubyte)* end = cast(ubyte*)(haystack.ptr + haystack.length); - while (p != end) - { - state = (state << 1) | table[p[0]]; - state = (state << 1) | table[p[1]]; - state = (state << 1) | table[p[2]]; - p += 4; - if (!(state & limit))//division rounds down for dchar - return (p-cast(ubyte*) haystack.ptr)/Char.sizeof - -length; - } - } - else - { - auto len = cast(ubyte*)(haystack.ptr + haystack.length) - p; - size_t i = 0; - if (len & 1) - { - state = (state << 1) | table[p[i++]]; - if (!(state & limit)) - return idx+i/Char.sizeof-length; - } - while (i < len) - { - state = (state << 1) | table[p[i++]]; - if (!(state & limit)) - return idx+i/Char.sizeof - -length; - state = (state << 1) | table[p[i++]]; - if (!(state & limit)) - return idx+i/Char.sizeof - -length; - debug(std_regex_search) writefln("State: %32b", state); - } - } - } - return haystack.length; - } - - @system debug static void dump(uint[] table) - {//@@@BUG@@@ writef(ln) is @system - import std.stdio : writefln; - for (size_t i = 0; i < table.length; i += 4) - { - writefln("%32b %32b %32b %32b",table[i], table[i+1], table[i+2], table[i+3]); - } - } -} - -@system unittest -{ - import std.conv, std.regex; - @trusted void test_fixed(alias Kick)() - { - static foreach (i, v; AliasSeq!(char, wchar, dchar)) - {{ - alias Char = v; - alias String = immutable(v)[]; - auto r = regex(to!String(`abc$`)); - auto kick = Kick!Char(r, new uint[256]); - assert(kick.length == 3, text(Kick.stringof," ",v.stringof, " == ", kick.length)); - auto r2 = regex(to!String(`(abc){2}a+`)); - kick = Kick!Char(r2, new uint[256]); - assert(kick.length == 7, text(Kick.stringof,v.stringof," == ", kick.length)); - auto r3 = regex(to!String(`\b(a{2}b{3}){2,4}`)); - kick = Kick!Char(r3, new uint[256]); - assert(kick.length == 10, text(Kick.stringof,v.stringof," == ", kick.length)); - auto r4 = regex(to!String(`\ba{2}c\bxyz`)); - kick = Kick!Char(r4, new uint[256]); - assert(kick.length == 6, text(Kick.stringof,v.stringof, " == ", kick.length)); - auto r5 = regex(to!String(`\ba{2}c\b`)); - kick = Kick!Char(r5, new uint[256]); - size_t x = kick.search("aabaacaa", 0); - assert(x == 3, text(Kick.stringof,v.stringof," == ", kick.length)); - x = kick.search("aabaacaa", x+1); - assert(x == 8, text(Kick.stringof,v.stringof," == ", kick.length)); - }} - } - @trusted void test_flex(alias Kick)() - { - static foreach (i, v; AliasSeq!(char, wchar, dchar)) - {{ - alias Char = v; - alias String = immutable(v)[]; - auto r = regex(to!String(`abc[a-z]`)); - auto kick = Kick!Char(r, new uint[256]); - auto x = kick.search(to!String("abbabca"), 0); - assert(x == 3, text("real x is ", x, " ",v.stringof)); - - auto r2 = regex(to!String(`(ax|bd|cdy)`)); - String s2 = to!String("abdcdyabax"); - kick = Kick!Char(r2, new uint[256]); - x = kick.search(s2, 0); - assert(x == 1, text("real x is ", x)); - x = kick.search(s2, x+1); - assert(x == 3, text("real x is ", x)); - x = kick.search(s2, x+1); - assert(x == 8, text("real x is ", x)); - auto rdot = regex(to!String(`...`)); - kick = Kick!Char(rdot, new uint[256]); - assert(kick.length == 0); - auto rN = regex(to!String(`a(b+|c+)x`)); - kick = Kick!Char(rN, new uint[256]); - assert(kick.length == 3, to!string(kick.length)); - assert(kick.search("ababx",0) == 2); - assert(kick.search("abaacba",0) == 3);//expected inexact - - }} - } - test_fixed!(ShiftOr)(); - test_flex!(ShiftOr)(); -} - -alias Kickstart = ShiftOr; diff --git a/phobos/std/regex/internal/parser.d b/phobos/std/regex/internal/parser.d deleted file mode 100644 index ab2b297..0000000 --- a/phobos/std/regex/internal/parser.d +++ /dev/null @@ -1,1217 +0,0 @@ -//Written in the D programming language -/* - Regular expression pattern parser. -*/ -module std.regex.internal.parser; - -import std.regex.internal.ir; -import std.range.primitives, std.uni, std.meta, - std.traits, std.typecons, std.exception; -static import std.ascii; - -// package relevant info from parser into a regex object -auto makeRegex(S, CG)(Parser!(S, CG) p) -{ - import std.regex.internal.backtracking : BacktrackingMatcher; - import std.regex.internal.thompson : ThompsonMatcher; - import std.algorithm.searching : canFind; - alias Char = BasicElementOf!S; - Regex!Char re; - auto g = p.g; - with(re) - { - ir = g.ir; - dict = g.dict; - ngroup = g.ngroup; - maxCounterDepth = g.counterDepth; - flags = p.re_flags; - charsets = g.charsets; - matchers = g.matchers; - backrefed = g.backrefed; - re.pattern = p.origin.idup; - re.postprocess(); - // check if we have backreferences, if so - use backtracking - if (__ctfe) factory = null; // allows us to use the awful enum re = regex(...); - else if (re.backrefed.canFind!"a != 0") - factory = new RuntimeFactory!(BacktrackingMatcher, Char); - else - factory = new RuntimeFactory!(ThompsonMatcher, Char); - debug(std_regex_parser) - { - __ctfe || print(); - } - //@@@BUG@@@ (not reduced) - //somehow just using validate _collides_ with std.utf.validate (!) - version (assert) re.validateRe(); - } - return re; -} - -// helper for unittest -auto makeRegex(S)(S arg) -if (isSomeString!S) -{ - return makeRegex(Parser!(S, CodeGen)(arg, "")); -} - -@system unittest -{ - import std.algorithm.comparison : equal; - auto re = makeRegex(`(?P\w+) = (?P\d+)`); - auto nc = re.namedCaptures; - static assert(isRandomAccessRange!(typeof(nc))); - assert(!nc.empty); - assert(nc.length == 2); - assert(nc.equal(["name", "var"])); - assert(nc[0] == "name"); - assert(nc[1..$].equal(["var"])); - - re = makeRegex(`(\w+) (?P\w+) (\w+)`); - nc = re.namedCaptures; - assert(nc.length == 1); - assert(nc[0] == "named"); - assert(nc.front == "named"); - assert(nc.back == "named"); - - re = makeRegex(`(\w+) (\w+)`); - nc = re.namedCaptures; - assert(nc.empty); - - re = makeRegex(`(?P\d{4})/(?P\d{2})/(?P\d{2})/`); - nc = re.namedCaptures; - auto cp = nc.save; - assert(nc.equal(cp)); - nc.popFront(); - assert(nc.equal(cp[1..$])); - nc.popBack(); - assert(nc.equal(cp[1 .. $ - 1])); -} - - -@trusted void reverseBytecode()(Bytecode[] code) -{ - Bytecode[] rev = new Bytecode[code.length]; - uint revPc = cast(uint) rev.length; - Stack!(Tuple!(uint, uint, uint)) stack; - uint start = 0; - uint end = cast(uint) code.length; - for (;;) - { - for (uint pc = start; pc < end; ) - { - immutable len = code[pc].length; - if (code[pc].code == IR.GotoEndOr) - break; //pick next alternation branch - if (code[pc].isAtom) - { - rev[revPc - len .. revPc] = code[pc .. pc + len]; - revPc -= len; - pc += len; - } - else if (code[pc].isStart || code[pc].isEnd) - { - //skip over other embedded lookbehinds they are reversed - if (code[pc].code == IR.LookbehindStart - || code[pc].code == IR.NeglookbehindStart) - { - immutable blockLen = len + code[pc].data - + code[pc].pairedLength; - rev[revPc - blockLen .. revPc] = code[pc .. pc + blockLen]; - pc += blockLen; - revPc -= blockLen; - continue; - } - immutable second = code[pc].indexOfPair(pc); - immutable secLen = code[second].length; - rev[revPc - secLen .. revPc] = code[second .. second + secLen]; - revPc -= secLen; - if (code[pc].code == IR.OrStart) - { - //we pass len bytes forward, but secLen in reverse - immutable revStart = revPc - (second + len - secLen - pc); - uint r = revStart; - uint i = pc + IRL!(IR.OrStart); - while (code[i].code == IR.Option) - { - if (code[i - 1].code != IR.OrStart) - { - assert(code[i - 1].code == IR.GotoEndOr); - rev[r - 1] = code[i - 1]; - } - rev[r] = code[i]; - auto newStart = i + IRL!(IR.Option); - auto newEnd = newStart + code[i].data; - auto newRpc = r + code[i].data + IRL!(IR.Option); - if (code[newEnd].code != IR.OrEnd) - { - newRpc--; - } - stack.push(tuple(newStart, newEnd, newRpc)); - r += code[i].data + IRL!(IR.Option); - i += code[i].data + IRL!(IR.Option); - } - pc = i; - revPc = revStart; - assert(code[pc].code == IR.OrEnd); - } - else - pc += len; - } - } - if (stack.empty) - break; - start = stack.top[0]; - end = stack.top[1]; - revPc = stack.top[2]; - stack.pop(); - } - code[] = rev[]; -} - -struct CodeGen -{ - Bytecode[] ir; // resulting bytecode - Stack!(uint) fixupStack; // stack of opened start instructions - NamedGroup[] dict; // maps name -> user group number - Stack!(uint) groupStack; // stack of current number of group - uint nesting = 0; // group nesting level and repetitions step - uint lookaroundNest = 0; // nesting of lookaround - uint counterDepth = 0; // current depth of nested counted repetitions - CodepointSet[] charsets; // sets for char classes - const(CharMatcher)[] matchers; // matchers for char classes - uint[] backrefed; // bitarray for groups refered by backref - uint ngroup; // final number of groups (of all patterns) - - void start(uint length) - { - if (!__ctfe) - ir.reserve((length*5+2)/4); - fixupStack.push(0); - groupStack.push(1);//0 - whole match - } - - //mark referenced groups for latter processing - void markBackref(uint n) - { - if (n/32 >= backrefed.length) - backrefed.length = n/32 + 1; - backrefed[n / 32] |= 1 << (n & 31); - } - - bool isOpenGroup(uint n) - { - import std.algorithm.searching : canFind; - // walk the fixup stack and see if there are groups labeled 'n' - // fixup '0' is reserved for alternations - return fixupStack.data[1..$]. - canFind!(fix => ir[fix].code == IR.GroupStart && ir[fix].data == n)(); - } - - void put(Bytecode code) - { - enforce(ir.length < maxCompiledLength, - "maximum compiled pattern length is exceeded"); - ir ~= code; - } - - void putRaw(uint number) - { - enforce(ir.length < maxCompiledLength, - "maximum compiled pattern length is exceeded"); - ir ~= Bytecode.fromRaw(number); - } - - //try to generate optimal IR code for this CodepointSet - @trusted void charsetToIr(CodepointSet set) - {//@@@BUG@@@ writeln is @system - uint chars = cast(uint) set.length; - if (chars < Bytecode.maxSequence) - { - switch (chars) - { - case 1: - put(Bytecode(IR.Char, set.byCodepoint.front)); - break; - case 0: - throw new RegexException("empty CodepointSet not allowed"); - default: - foreach (ch; set.byCodepoint) - put(Bytecode(IR.OrChar, ch, chars)); - } - } - else - { - import std.algorithm.searching : countUntil; - const ivals = set.byInterval; - immutable n = charsets.countUntil(set); - if (n >= 0) - { - if (ivals.length*2 > maxCharsetUsed) - put(Bytecode(IR.Trie, cast(uint) n)); - else - put(Bytecode(IR.CodepointSet, cast(uint) n)); - return; - } - if (ivals.length*2 > maxCharsetUsed) - { - auto t = getMatcher(set); - put(Bytecode(IR.Trie, cast(uint) matchers.length)); - matchers ~= t; - debug(std_regex_allocation) writeln("Trie generated"); - } - else - { - put(Bytecode(IR.CodepointSet, cast(uint) charsets.length)); - matchers ~= CharMatcher.init; - } - charsets ~= set; - assert(charsets.length == matchers.length); - } - } - - void genLogicGroup() - { - nesting++; - pushFixup(length); - put(Bytecode(IR.Nop, 0)); - } - - void genGroup() - { - nesting++; - pushFixup(length); - immutable nglob = groupStack.top++; - enforce(groupStack.top <= maxGroupNumber, "limit on number of submatches is exceeded"); - put(Bytecode(IR.GroupStart, nglob)); - } - - void genNamedGroup(string name) - { - import std.array : insertInPlace; - import std.range : assumeSorted; - nesting++; - pushFixup(length); - immutable nglob = groupStack.top++; - enforce(groupStack.top <= maxGroupNumber, "limit on submatches is exceeded"); - auto t = NamedGroup(name, nglob); - auto d = assumeSorted!"a.name < b.name"(dict); - immutable ind = d.lowerBound(t).length; - insertInPlace(dict, ind, t); - put(Bytecode(IR.GroupStart, nglob)); - } - - //generate code for start of lookaround: (?= (?! (?<= (? fix && ir[fix].code == IR.Option) - { - ir[fix] = Bytecode(ir[fix].code, cast(uint) ir.length - fix); - put(Bytecode(IR.GotoEndOr, 0)); - fixupStack.top = cast(uint) ir.length; //replace latest fixup for Option - put(Bytecode(IR.Option, 0)); - return; - } - uint len, orStart; - //start a new option - if (fixupStack.length == 1) - {//only root entry, effectively no fixup - len = cast(uint) ir.length + IRL!(IR.GotoEndOr); - orStart = 0; - } - else - {//IR.lookahead, etc. fixups that have length > 1, thus check ir[x].length - len = cast(uint) ir.length - fix - (ir[fix].length - 1); - orStart = fix + ir[fix].length; - } - insertInPlace(ir, orStart, Bytecode(IR.OrStart, 0), Bytecode(IR.Option, len)); - assert(ir[orStart].code == IR.OrStart); - put(Bytecode(IR.GotoEndOr, 0)); - fixupStack.push(orStart); //fixup for StartOR - fixupStack.push(cast(uint) ir.length); //for second Option - put(Bytecode(IR.Option, 0)); - } - - // finalizes IR.Option, fix points to the first option of sequence - void finishAlternation(uint fix) - { - enforce(ir[fix].code == IR.Option, "no matching ')'"); - ir[fix] = Bytecode(ir[fix].code, cast(uint) ir.length - fix - IRL!(IR.OrStart)); - fix = fixupStack.pop(); - enforce(ir[fix].code == IR.OrStart, "no matching ')'"); - ir[fix] = Bytecode(IR.OrStart, cast(uint) ir.length - fix - IRL!(IR.OrStart)); - put(Bytecode(IR.OrEnd, cast(uint) ir.length - fix - IRL!(IR.OrStart))); - uint pc = fix + IRL!(IR.OrStart); - while (ir[pc].code == IR.Option) - { - pc = pc + ir[pc].data; - if (ir[pc].code != IR.GotoEndOr) - break; - ir[pc] = Bytecode(IR.GotoEndOr, cast(uint)(ir.length - pc - IRL!(IR.OrEnd))); - pc += IRL!(IR.GotoEndOr); - } - put(Bytecode.fromRaw(0)); - } - - // returns: (flag - repetition possible?, fixup of the start of this "group") - Tuple!(bool, uint) onClose() - { - nesting--; - uint fix = popFixup(); - switch (ir[fix].code) - { - case IR.GroupStart: - put(Bytecode(IR.GroupEnd, ir[fix].data)); - return tuple(true, fix); - case IR.LookaheadStart, IR.NeglookaheadStart, IR.LookbehindStart, IR.NeglookbehindStart: - assert(lookaroundNest); - fixLookaround(fix); - return tuple(false, 0u); - case IR.Option: //| xxx ) - //two fixups: last option + full OR - finishAlternation(fix); - fix = topFixup; - switch (ir[fix].code) - { - case IR.GroupStart: - popFixup(); - put(Bytecode(IR.GroupEnd, ir[fix].data)); - return tuple(true, fix); - case IR.LookaheadStart, IR.NeglookaheadStart, IR.LookbehindStart, IR.NeglookbehindStart: - assert(lookaroundNest); - fix = popFixup(); - fixLookaround(fix); - return tuple(false, 0u); - default://(?:xxx) - popFixup(); - return tuple(true, fix); - } - default://(?:xxx) - return tuple(true, fix); - } - } - - uint popFixup(){ return fixupStack.pop(); } - - void pushFixup(uint val){ return fixupStack.push(val); } - - @property uint topFixup(){ return fixupStack.top; } - - @property size_t fixupLength(){ return fixupStack.data.length; } - - @property uint length(){ return cast(uint) ir.length; } -} - -// safety limits -enum maxGroupNumber = 2^^19; -enum maxLookaroundDepth = 16; -// *Bytecode.sizeof, i.e. 1Mb of bytecode alone -enum maxCompiledLength = 2^^18; -// amounts to up to 4 Mb of auxilary table for matching -enum maxCumulativeRepetitionLength = 2^^20; -// marker to indicate infinite repetition -enum infinite = ~0u; - -struct Parser(R, Generator) -if (isForwardRange!R && is(ElementType!R : dchar)) -{ - dchar front; - bool empty; - R pat, origin; //keep full pattern for pretty printing error messages - uint re_flags = 0; //global flags e.g. multiline + internal ones - Generator g; - - @trusted this(S)(R pattern, S flags) - if (isSomeString!S) - { - pat = origin = pattern; - //reserve slightly more then avg as sampled from unittests - parseFlags(flags); - front = ' ';//a safe default for freeform parsing - popFront(); - g.start(cast(uint) pat.length); - try - { - parseRegex(); - } - catch (Exception e) - { - error(e.msg);//also adds pattern location - } - g.endPattern(1); - } - - void _popFront() - { - if (pat.empty) - { - empty = true; - } - else - { - front = pat.front; - pat.popFront(); - } - } - - void skipSpace() - { - while (!empty && isWhite(front)) _popFront(); - } - - void popFront() - { - _popFront(); - if (re_flags & RegexOption.freeform) skipSpace(); - } - - auto save(){ return this; } - - //parsing number with basic overflow check - uint parseDecimal() - { - uint r = 0; - while (std.ascii.isDigit(front)) - { - if (r >= (uint.max/10)) - error("Overflow in decimal number"); - r = 10*r + cast(uint)(front-'0'); - popFront(); - if (empty) break; - } - return r; - } - - // - @trusted void parseFlags(S)(S flags) - {//@@@BUG@@@ text is @system - import std.conv : text; - foreach (ch; flags)//flags are ASCII anyway - { - L_FlagSwitch: - switch (ch) - { - - foreach (i, op; __traits(allMembers, RegexOption)) - { - case RegexOptionNames[i]: - if (re_flags & mixin("RegexOption."~op)) - throw new RegexException(text("redundant flag specified: ",ch)); - re_flags |= mixin("RegexOption."~op); - break L_FlagSwitch; - } - default: - throw new RegexException(text("unknown regex flag '",ch,"'")); - } - } - } - - //parse and store IR for regex pattern - @trusted void parseRegex() - { - uint fix;//fixup pointer - - while (!empty) - { - debug(std_regex_parser) - __ctfe || writeln("*LR*\nSource: ", pat, "\nStack: ",fixupStack.data); - switch (front) - { - case '(': - popFront(); - if (front == '?') - { - popFront(); - switch (front) - { - case '#': - for (;;) - { - popFront(); - enforce(!empty, "Unexpected end of pattern"); - if (front == ')') - { - popFront(); - break; - } - } - break; - case ':': - g.genLogicGroup(); - popFront(); - break; - case '=': - g.genLookaround(IR.LookaheadStart); - popFront(); - break; - case '!': - g.genLookaround(IR.NeglookaheadStart); - popFront(); - break; - case 'P': - popFront(); - enforce(front == '<', "Expected '<' in named group"); - string name; - popFront(); - if (empty || !(isAlpha(front) || front == '_')) - error("Expected alpha starting a named group"); - name ~= front; - popFront(); - while (!empty && (isAlpha(front) || - front == '_' || std.ascii.isDigit(front))) - { - name ~= front; - popFront(); - } - enforce(front == '>', "Expected '>' closing named group"); - popFront(); - g.genNamedGroup(name); - break; - case '<': - popFront(); - if (front == '=') - g.genLookaround(IR.LookbehindStart); - else if (front == '!') - g.genLookaround(IR.NeglookbehindStart); - else - error("'!' or '=' expected after '<'"); - popFront(); - break; - default: - uint enableFlags, disableFlags; - bool enable = true; - do - { - switch (front) - { - case 's': - if (enable) - enableFlags |= RegexOption.singleline; - else - disableFlags |= RegexOption.singleline; - break; - case 'x': - if (enable) - enableFlags |= RegexOption.freeform; - else - disableFlags |= RegexOption.freeform; - break; - case 'i': - if (enable) - enableFlags |= RegexOption.casefold; - else - disableFlags |= RegexOption.casefold; - break; - case 'm': - if (enable) - enableFlags |= RegexOption.multiline; - else - disableFlags |= RegexOption.multiline; - break; - case '-': - if (!enable) - error(" unexpected second '-' in flags"); - enable = false; - break; - default: - error(" 's', 'x', 'i', 'm' or '-' expected after '(?' "); - } - popFront(); - }while (front != ')'); - popFront(); - re_flags |= enableFlags; - re_flags &= ~disableFlags; - } - } - else - { - g.genGroup(); - } - break; - case ')': - enforce(g.nesting, "Unmatched ')'"); - popFront(); - auto pair = g.onClose(); - if (pair[0]) - parseQuantifier(pair[1]); - break; - case '|': - popFront(); - g.fixAlternation(); - break; - default://no groups or whatever - immutable start = g.length; - parseAtom(); - parseQuantifier(start); - } - } - - if (g.fixupLength != 1) - { - fix = g.popFixup(); - g.finishAlternation(fix); - enforce(g.fixupLength == 1, "no matching ')'"); - } - } - - - //parse and store IR for atom-quantifier pair - @trusted void parseQuantifier(uint offset) - {//copy is @system - if (empty) - return g.fixRepetition(offset); - uint min, max; - switch (front) - { - case '*': - min = 0; - max = infinite; - break; - case '?': - min = 0; - max = 1; - break; - case '+': - min = 1; - max = infinite; - break; - case '{': - popFront(); - enforce(!empty, "Unexpected end of regex pattern"); - enforce(std.ascii.isDigit(front), "First number required in repetition"); - min = parseDecimal(); - if (front == '}') - max = min; - else if (front == ',') - { - popFront(); - if (std.ascii.isDigit(front)) - max = parseDecimal(); - else if (front == '}') - max = infinite; - else - error("Unexpected symbol in regex pattern"); - skipSpace(); - enforce(front == '}', "Unmatched '{' in regex pattern"); - } - else - error("Unexpected symbol in regex pattern"); - enforce(min <= max, "Illegal {n,m} quantifier"); - break; - default: - g.fixRepetition(offset); - return; - } - bool greedy = true; - //check only if we managed to get new symbol - popFront(); - if (!empty && front == '?') - { - greedy = false; - popFront(); - } - g.fixRepetition(offset, min, max, greedy); - } - - //parse and store IR for atom - void parseAtom() - { - if (empty) - return; - switch (front) - { - case '*', '?', '+', '|', '{', '}': - return error("'*', '+', '?', '{', '}' not allowed in atom"); - case '.': - if (re_flags & RegexOption.singleline) - g.put(Bytecode(IR.Any, 0)); - else - { - CodepointSet set; - g.charsetToIr(set.add('\n','\n'+1).add('\r', '\r'+1).inverted); - } - popFront(); - break; - case '[': - parseCharset(); - break; - case '\\': - _popFront(); - enforce(!empty, "Unfinished escape sequence"); - parseEscape(); - break; - case '^': - if (re_flags & RegexOption.multiline) - g.put(Bytecode(IR.Bol, 0)); - else - g.put(Bytecode(IR.Bof, 0)); - popFront(); - break; - case '$': - if (re_flags & RegexOption.multiline) - g.put(Bytecode(IR.Eol, 0)); - else - g.put(Bytecode(IR.Eof, 0)); - popFront(); - break; - default: - if (re_flags & RegexOption.casefold) - { - auto range = simpleCaseFoldings(front); - assert(range.length <= 5); - if (range.length == 1) - g.put(Bytecode(IR.Char, range.front)); - else - foreach (v; range) - g.put(Bytecode(IR.OrChar, v, cast(uint) range.length)); - } - else - g.put(Bytecode(IR.Char, front)); - popFront(); - } - } - - //parse and store IR for CodepointSet - void parseCharset() - { - const save = re_flags; - re_flags &= ~RegexOption.freeform; // stop ignoring whitespace if we did - bool casefold = cast(bool)(re_flags & RegexOption.casefold); - g.charsetToIr(unicode.parseSet(this, casefold)); - re_flags = save; - // Last next() in parseCharset is executed w/o freeform flag - if (re_flags & RegexOption.freeform) skipSpace(); - } - - //parse and generate IR for escape stand alone escape sequence - @trusted void parseEscape() - {//accesses array of appender - import std.algorithm.iteration : sum; - switch (front) - { - case 'f': popFront(); g.put(Bytecode(IR.Char, '\f')); break; - case 'n': popFront(); g.put(Bytecode(IR.Char, '\n')); break; - case 'r': popFront(); g.put(Bytecode(IR.Char, '\r')); break; - case 't': popFront(); g.put(Bytecode(IR.Char, '\t')); break; - case 'v': popFront(); g.put(Bytecode(IR.Char, '\v')); break; - - case 'd': - popFront(); - g.charsetToIr(unicode.Nd); - break; - case 'D': - popFront(); - g.charsetToIr(unicode.Nd.inverted); - break; - case 'b': popFront(); g.put(Bytecode(IR.Wordboundary, 0)); break; - case 'B': popFront(); g.put(Bytecode(IR.Notwordboundary, 0)); break; - case 's': - popFront(); - g.charsetToIr(unicode.White_Space); - break; - case 'S': - popFront(); - g.charsetToIr(unicode.White_Space.inverted); - break; - case 'w': - popFront(); - g.charsetToIr(wordCharacter); - break; - case 'W': - popFront(); - g.charsetToIr(wordCharacter.inverted); - break; - case 'p': case 'P': - bool casefold = cast(bool)(re_flags & RegexOption.casefold); - auto set = unicode.parsePropertySpec(this, front == 'P', casefold); - g.charsetToIr(set); - break; - case 'x': - immutable code = parseUniHex(pat, 2); - popFront(); - g.put(Bytecode(IR.Char,code)); - break; - case 'u': case 'U': - immutable code = parseUniHex(pat, front == 'u' ? 4 : 8); - popFront(); - g.put(Bytecode(IR.Char, code)); - break; - case 'c': //control codes - Bytecode code = Bytecode(IR.Char, unicode.parseControlCode(this)); - popFront(); - g.put(code); - break; - case '0': - popFront(); - g.put(Bytecode(IR.Char, 0));//NUL character - break; - case '1': .. case '9': - uint nref = cast(uint) front - '0'; - immutable maxBackref = sum(g.groupStack.data); - enforce(nref < maxBackref, "Backref to unseen group"); - //perl's disambiguation rule i.e. - //get next digit only if there is such group number - popFront(); - while (nref < maxBackref && !empty && std.ascii.isDigit(front)) - { - nref = nref * 10 + front - '0'; - popFront(); - } - if (nref >= maxBackref) - nref /= 10; - enforce(!g.isOpenGroup(nref), "Backref to open group"); - uint localLimit = maxBackref - g.groupStack.top; - if (nref >= localLimit) - { - g.put(Bytecode(IR.Backref, nref-localLimit)); - g.ir[$-1].setLocalRef(); - } - else - g.put(Bytecode(IR.Backref, nref)); - g.markBackref(nref); - break; - default: - if (front == '\\' && !pat.empty) - { - if (pat.front >= privateUseStart && pat.front <= privateUseEnd) - enforce(false, "invalid escape sequence"); - } - if (front >= privateUseStart && front <= privateUseEnd) - { - g.endPattern(front - privateUseStart + 1); - break; - } - auto op = Bytecode(IR.Char, front); - popFront(); - g.put(op); - } - } - - // - @trusted void error(string msg) - { - import std.conv : text; - string app = msg; - app ~= "\nPattern with error: `"; - app ~= origin[0..$-pat.length].text; - app ~= "` <--HERE-- `"; - app ~= pat.text; - app ~= "`"; - throw new RegexException(app); - } - - alias Char = BasicElementOf!R; - - @property program() - { - return makeRegex(this); - } -} - -/+ - Postproces the IR, then optimize. -+/ -@trusted void postprocess(Char)(ref Regex!Char zis) -{//@@@BUG@@@ write is @system - with(zis) - { - struct FixedStack(T) - { - T[] arr; - uint _top; - //this(T[] storage){ arr = storage; _top = -1; } - @property ref T top(){ assert(!empty); return arr[_top]; } - void push(T x){ arr[++_top] = x; } - T pop() { assert(!empty); return arr[_top--]; } - @property bool empty(){ return _top == -1; } - } - auto counterRange = FixedStack!uint(new uint[maxCounterDepth+1], -1); - counterRange.push(1); - ulong cumRange = 0; - for (uint i = 0; i < ir.length; i += ir[i].length) - { - if (ir[i].hotspot) - { - assert(i + 1 < ir.length, - "unexpected end of IR while looking for hotspot"); - ir[i+1] = Bytecode.fromRaw(hotspotTableSize); - hotspotTableSize += counterRange.top; - } - switch (ir[i].code) - { - case IR.RepeatStart, IR.RepeatQStart: - uint repEnd = cast(uint)(i + ir[i].data + IRL!(IR.RepeatStart)); - assert(ir[repEnd].code == ir[i].paired.code); - immutable max = ir[repEnd + 4].raw; - ir[repEnd+2].raw = counterRange.top; - ir[repEnd+3].raw *= counterRange.top; - ir[repEnd+4].raw *= counterRange.top; - ulong cntRange = cast(ulong)(max)*counterRange.top; - cumRange += cntRange; - enforce(cumRange < maxCumulativeRepetitionLength, - "repetition length limit is exceeded"); - counterRange.push(cast(uint) cntRange + counterRange.top); - threadCount += counterRange.top; - break; - case IR.RepeatEnd, IR.RepeatQEnd: - threadCount += counterRange.top; - counterRange.pop(); - break; - case IR.GroupStart: - if (isBackref(ir[i].data)) - ir[i].setBackrefence(); - threadCount += counterRange.top; - break; - case IR.GroupEnd: - if (isBackref(ir[i].data)) - ir[i].setBackrefence(); - threadCount += counterRange.top; - break; - default: - threadCount += counterRange.top; - } - } - checkIfOneShot(); - if (!(flags & RegexInfo.oneShot)) - kickstart = Kickstart!Char(zis, new uint[](256)); - debug(std_regex_allocation) writefln("IR processed, max threads: %d", threadCount); - optimize(zis); - } -} - -void fixupBytecode()(Bytecode[] ir) -{ - Stack!uint fixups; - - with(IR) for (uint i=0; i\\d+)/(?P\\d+)", "2/3", "y", "${d}/${q}", "3/2"), -//set operations: - TestVectors( "[a-z--d-f]", " dfa", "y", "$&", "a"), - TestVectors( "[abc[pq--acq]]{2}", "bqpaca", "y", "$&", "pa"), - TestVectors( "[a-z9&&abc0-9]{3}", "z90a0abc", "y", "$&", "abc"), - TestVectors( "[0-9a-f~~0-5a-z]{2}", "g0a58x", "y", "$&", "8x"), - TestVectors( "[abc[pq]xyz[rs]]{4}", "cqxr", "y", "$&", "cqxr"), - TestVectors( "[abcdf--[ab&&[bcd]][acd]]", "abcdefgh", "y", "$&", "f"), - TestVectors( "[a-c||d-f]+", "abcdef", "y", "$&", "abcdef"), - TestVectors( "[a-f--a-c]+", "abcdef", "y", "$&", "def"), - TestVectors( "[a-c&&b-f]+", "abcdef", "y", "$&", "bc"), - TestVectors( "[a-c~~b-f]+", "abcdef", "y", "$&", "a"), -//unicode blocks & properties: - TestVectors( `\P{Inlatin1suppl ement}`, "\u00c2!", "y", "$&", "!"), - TestVectors( `\p{InLatin-1 Supplement}\p{in-mathematical-operators}\P{Inlatin1suppl ement}`, - "\u00c2\u2200\u00c3\u2203.", "y", "$&", "\u00c3\u2203."), - TestVectors( `[-+*/\p{in-mathematical-operators}]{2}`, "a+\u2212", "y", "$&", "+\u2212"), - TestVectors( `\p{Ll}+`, "XabcD", "y", "$&", "abc"), - TestVectors( `\p{Lu}+`, "абвГДЕ", "y", "$&", "ГДЕ"), - TestVectors( `^\p{Currency Symbol}\p{Sc}`, "$₤", "y", "$&", "$₤"), - TestVectors( `\p{Common}\p{Thai}`, "!ฆ", "y", "$&", "!ฆ"), - TestVectors( `[\d\s]*\D`, "12 \t3\U00001680\u0F20_2", "y", "$&", "12 \t3\U00001680\u0F20_"), - TestVectors( `[c-wф]фф`, "ффф", "y", "$&", "ффф"), -//case insensitive: - TestVectors( `^abcdEf$`, "AbCdEF", "y", "$&", "AbCdEF", "i"), - TestVectors( `РусскиК язык`, "рУсскИй ЯСЍк", "y", "$&", "рУсскИй ЯСЍк", "i"), - TestVectors( `ⒶⒷⓒ` , "ⓐⓑⒸ", "y", "$&", "ⓐⓑⒸ", "i"), - TestVectors( "\U00010400{2}", "\U00010428\U00010400 ", "y", "$&", "\U00010428\U00010400", "i"), - TestVectors( `[adzĐŁ-ĐŻ]{4}`, "DzюЯ", "y", "$&", "DzюЯ", "i"), - TestVectors( `\p{L}\p{Lu}{10}`, "абвгдеЖЗИКЛ", "y", "$&", "абвгдеЖЗИКЛ", "i"), - TestVectors( `(?:DĂĽb){3}`, "DĂĽbDÅBdÅb", "y", "$&", "DĂĽbDÅBdÅb", "i"), -//escapes: - TestVectors( `\u0041\u005a\U00000065\u0001`, "AZe\u0001", "y", "$&", "AZe\u0001"), - TestVectors( `\u`, "", "c", "-", "-"), - TestVectors( `\U`, "", "c", "-", "-"), - TestVectors( `\u003`, "", "c", "-", "-"), - TestVectors( `[\x00-\x7f]{4}`, "\x00\x09ab", "y", "$&", "\x00\x09ab"), - TestVectors( `[\cJ\cK\cA-\cD]{3}\cQ`, "\x01\x0B\x0A\x11", "y", "$&", "\x01\x0B\x0A\x11"), - TestVectors( `\r\n\v\t\f\\`, "\r\n\v\t\f\\", "y", "$&", "\r\n\v\t\f\\"), - TestVectors( `[\u0003\u0001]{2}`, "\u0001\u0003", "y", "$&", "\u0001\u0003"), - TestVectors( `^[\u0020-\u0080\u0001\n-\r]{8}`, "abc\u0001\v\f\r\n", "y", "$&", "abc\u0001\v\f\r\n"), - TestVectors( `\w+\S\w+`, "ab7!44c", "y", "$&", "ab7!44c"), - TestVectors( `\b\w+\b`, " abde4 ", "y", "$&", "abde4"), - TestVectors( `\b\w+\b`, " abde4", "y", "$&", "abde4"), - TestVectors( `\b\w+\b`, "abde4 ", "y", "$&", "abde4"), - TestVectors( `\pL\pS`, "a\u02DA", "y", "$&", "a\u02DA"), - TestVectors( `\pX`, "", "c", "-", "-"), -// ^, $, \b, \B, multiline : - TestVectors( `\r.*?$`, "abc\r\nxy", "y", "$&", "\r\nxy", "sm"), - TestVectors( `^a$^b$`, "a\r\nb\n", "n", "$&", "-", "m"), - TestVectors( `^a$\r\n^b$`,"a\r\nb\n", "y", "$&", "a\r\nb", "m"), - TestVectors( `^$`, "\r\n", "y", "$&", "", "m"), - TestVectors( `^a$\nx$`, "a\nx\u2028","y", "$&", "a\nx", "m"), - TestVectors( `^a$\nx$`, "a\nx\u2029","y", "$&", "a\nx", "m"), - TestVectors( `^a$\nx$`, "a\nx\u0085","y", "$&", "a\nx","m"), - TestVectors( `^x$`, "\u2028x", "y", "$&", "x", "m"), - TestVectors( `^x$`, "\u2029x", "y", "$&", "x", "m"), - TestVectors( `^x$`, "\u0085x", "y", "$&", "x", "m"), - TestVectors( `\b^.`, "ab", "y", "$&", "a"), - TestVectors( `\B^.`, "ab", "n", "-", "-"), - TestVectors( `^ab\Bc\B`, "\r\nabcd", "y", "$&", "abc", "m"), - TestVectors( `^.*$`, "12345678", "y", "$&", "12345678"), - -// luckily obtained regression on incremental matching in backtracker - TestVectors( `^(?:(?:([0-9A-F]+)\.\.([0-9A-F]+)|([0-9A-F]+))\s*;\s*([^ ]*)\s*#|# (?:\w|_)+=((?:\w|_)+))`, - "0020 ; White_Space # ", "y", "$1-$2-$3", "--0020"), -//lookahead - TestVectors( "(foo.)(?=(bar))", "foobar foodbar", "y", "$&-$1-$2", "food-food-bar" ), - TestVectors( `\b(\d+)[a-z](?=\1)`, "123a123", "y", "$&-$1", "123a-123" ), - TestVectors( `\$(?!\d{3})\w+`, "$123 $abc", "y", "$&", "$abc"), - TestVectors( `(abc)(?=(ed(f))\3)`, "abcedff", "y", "-", "-"), - TestVectors( `\b[A-Za-z0-9.]+(?=(@(?!gmail)))`, "a@gmail,x@com", "y", "$&-$1", "x-@"), - TestVectors( `x()(abc)(?=(d)(e)(f)\2)`, "xabcdefabc", "y", "$&", "xabc"), - TestVectors( `x()(abc)(?=(d)(e)(f)()\3\4\5)`, "xabcdefdef", "y", "$&", "xabc"), -//lookback - TestVectors( `(?<=(ab))\d`, "12ba3ab4", "y", "$&-$1", "4-ab", "i"), - TestVectors( `\w(?"); - assert(bmatch("texttext", greed).hit - == "text"); -} - -@safe unittest -{ - import std.algorithm.comparison : equal; - auto cr8 = ctRegex!("^(a)(b)?(c*)"); - auto m8 = bmatch("abcc",cr8); - assert(m8); - assert(m8.captures[1] == "a"); - assert(m8.captures[2] == "b"); - assert(m8.captures[3] == "cc"); - auto cr9 = ctRegex!("q(a|b)*q"); - auto m9 = match("xxqababqyy",cr9); - assert(m9); - assert(equal(bmatch("xxqababqyy",cr9).captures, ["qababq", "b"])); -} - -@safe unittest -{ - import std.algorithm.comparison : equal; - auto rtr = regex("a|b|c"); - static ctr = regex("a|b|c"); - assert(equal(rtr.ir,ctr.ir)); - //CTFE parser BUG is triggered by group - //in the middle of alternation (at least not first and not last) - static testCT = regex(`abc|(edf)|xyz`); - auto testRT = regex(`abc|(edf)|xyz`); - assert(equal(testCT.ir,testRT.ir)); -} - -@safe unittest -{ - import std.algorithm.comparison : equal; - import std.algorithm.iteration : map; - enum cx = ctRegex!"(A|B|C)"; - auto mx = match("B",cx); - assert(mx); - assert(equal(mx.captures, [ "B", "B"])); - enum cx2 = ctRegex!"(A|B)*"; - assert(match("BAAA",cx2)); - - enum cx3 = ctRegex!("a{3,4}","i"); - auto mx3 = match("AaA",cx3); - assert(mx3); - assert(mx3.captures[0] == "AaA"); - enum cx4 = ctRegex!(`^a{3,4}?[a-zA-Z0-9~]{1,2}`,"i"); - auto mx4 = match("aaaabc", cx4); - assert(mx4); - assert(mx4.captures[0] == "aaaab"); - auto cr8 = ctRegex!("(a)(b)?(c*)"); - auto m8 = bmatch("abcc",cr8); - assert(m8); - assert(m8.captures[1] == "a"); - assert(m8.captures[2] == "b"); - assert(m8.captures[3] == "cc"); - auto cr9 = ctRegex!(".*$", "gm"); - auto m9 = match("First\rSecond", cr9); - assert(m9); - assert(equal(map!"a.hit"(m9), ["First", "", "Second"])); -} - -@safe unittest -{ - import std.algorithm.comparison : equal; - import std.algorithm.iteration : map; -//global matching - void test_body(alias matchFn)() - { - string s = "a quick brown fox jumps over a lazy dog"; - auto r1 = regex("\\b[a-z]+\\b","g"); - string[] test; - foreach (m; matchFn(s, r1)) - test ~= m.hit; - assert(equal(test, [ "a", "quick", "brown", "fox", "jumps", "over", "a", "lazy", "dog"])); - auto free_reg = regex(` - - abc - \s+ - " - ( - [^"]+ - | \\ " - )+ - " - z - `, "x"); - auto m = match(`abc "quoted string with \" inside"z`,free_reg); - assert(m); - string mails = " hey@you.com no@spam.net "; - auto rm = regex(`@(?<=\S+@)\S+`,"g"); - assert(equal(map!"a[0]"(matchFn(mails, rm)), ["@you.com", "@spam.net"])); - auto m2 = matchFn("First line\nSecond line",regex(".*$","gm")); - assert(equal(map!"a[0]"(m2), ["First line", "", "Second line"])); - auto m2a = matchFn("First line\nSecond line",regex(".+$","gm")); - assert(equal(map!"a[0]"(m2a), ["First line", "Second line"])); - auto m2b = matchFn("First line\nSecond line",regex(".+?$","gm")); - assert(equal(map!"a[0]"(m2b), ["First line", "Second line"])); - debug(std_regex_test) writeln("!!! FReD FLAGS test done "~matchFn.stringof~" !!!"); - } - test_body!bmatch(); - test_body!match(); -} - -//tests for accumulated std.regex issues and other regressions -@safe unittest -{ - import std.algorithm.comparison : equal; - import std.algorithm.iteration : map; - void test_body(alias matchFn)() - { - // https://issues.dlang.org/show_bug.cgi?id=5857 - //matching goes out of control if ... in (...){x} has .*/.+ - auto c = matchFn("axxxzayyyyyzd",regex("(a.*z){2}d")).captures; - assert(c[0] == "axxxzayyyyyzd"); - assert(c[1] == "ayyyyyz"); - auto c2 = matchFn("axxxayyyyyd",regex("(a.*){2}d")).captures; - assert(c2[0] == "axxxayyyyyd"); - assert(c2[1] == "ayyyyy"); - // https://issues.dlang.org/show_bug.cgi?id=2108 - //greedy vs non-greedy - auto nogreed = regex(""); - assert(matchFn("texttext", nogreed).hit - == "text"); - auto greed = regex(""); - assert(matchFn("texttext", greed).hit - == "texttext"); - // https://issues.dlang.org/show_bug.cgi?id=4574 - //empty successful match still advances the input - string[] pres, posts, hits; - foreach (m; matchFn("abcabc", regex("","g"))) - { - pres ~= m.pre; - posts ~= m.post; - assert(m.hit.empty); - - } - auto heads = [ - "abcabc", - "abcab", - "abca", - "abc", - "ab", - "a", - "" - ]; - auto tails = [ - "abcabc", - "bcabc", - "cabc", - "abc", - "bc", - "c", - "" - ]; - assert(pres == array(retro(heads))); - assert(posts == tails); - // https://issues.dlang.org/show_bug.cgi?id=6076 - //regression on .* - auto re = regex("c.*|d"); - auto m = matchFn("mm", re); - assert(!m); - debug(std_regex_test) writeln("!!! FReD REGRESSION test done "~matchFn.stringof~" !!!"); - auto rprealloc = regex(`((.){5}.{1,10}){5}`); - auto arr = array(repeat('0',100)); - auto m2 = matchFn(arr, rprealloc); - assert(m2); - assert(collectException( - regex(r"^(import|file|binary|config)\s+([^\(]+)\(?([^\)]*)\)?\s*$") - ) is null); - foreach (ch; [Escapables]) - { - assert(match(to!string(ch),regex(`[\`~ch~`]`))); - assert(!match(to!string(ch),regex(`[^\`~ch~`]`))); - assert(match(to!string(ch),regex(`[\`~ch~`-\`~ch~`]`))); - } - // https://issues.dlang.org/show_bug.cgi?id=7718 - string strcmd = "./myApp.rb -os OSX -path \"/GIT/Ruby Apps/sec\" -conf 'notimer'"; - auto reStrCmd = regex (`(".*")|('.*')`, "g"); - assert(equal(map!"a[0]"(matchFn(strcmd, reStrCmd)), - [`"/GIT/Ruby Apps/sec"`, `'notimer'`])); - } - test_body!bmatch(); - test_body!match(); -} - -// tests for replace -@safe unittest -{ - void test(alias matchFn)() - { - import std.uni : toUpper; - - static foreach (i, v; AliasSeq!(string, wstring, dstring)) - {{ - auto baz(Cap)(Cap m) - if (is(Cap == Captures!(Cap.String))) - { - return toUpper(m.hit); - } - alias String = v; - assert(std.regex.replace!(matchFn)(to!String("ark rapacity"), regex(to!String("r")), to!String("c")) - == to!String("ack rapacity")); - assert(std.regex.replace!(matchFn)(to!String("ark rapacity"), regex(to!String("r"), "g"), to!String("c")) - == to!String("ack capacity")); - assert(std.regex.replace!(matchFn)(to!String("noon"), regex(to!String("^n")), to!String("[$&]")) - == to!String("[n]oon")); - assert(std.regex.replace!(matchFn)( - to!String("test1 test2"), regex(to!String(`\w+`),"g"), to!String("$`:$'") - ) == to!String(": test2 test1 :")); - auto s = std.regex.replace!(baz!(Captures!(String)))(to!String("Strap a rocket engine on a chicken."), - regex(to!String("[ar]"), "g")); - assert(s == "StRAp A Rocket engine on A chicken."); - }} - debug(std_regex_test) writeln("!!! Replace test done "~matchFn.stringof~" !!!"); - } - test!(bmatch)(); - test!(match)(); -} - -// tests for splitter -@safe unittest -{ - import std.algorithm.comparison : equal; - auto s1 = ", abc, de, fg, hi, "; - auto sp1 = splitter(s1, regex(", *")); - auto w1 = ["", "abc", "de", "fg", "hi", ""]; - assert(equal(sp1, w1)); - - auto s2 = ", abc, de, fg, hi"; - auto sp2 = splitter(s2, regex(", *")); - auto w2 = ["", "abc", "de", "fg", "hi"]; - - uint cnt; - foreach (e; sp2) - { - assert(w2[cnt++] == e); - } - assert(equal(sp2, w2)); -} - -@safe unittest -{ - char[] s1 = ", abc, de, fg, hi, ".dup; - auto sp2 = splitter(s1, regex(", *")); -} - -@safe unittest -{ - import std.algorithm.comparison : equal; - auto s1 = ", abc, de, fg, hi, "; - auto w1 = ["", "abc", "de", "fg", "hi", ""]; - assert(equal(split(s1, regex(", *")), w1[])); -} - -// https://issues.dlang.org/show_bug.cgi?id=7141 -@safe unittest -{ - string pattern = `[a\--b]`; - assert(match("-", pattern)); - assert(match("b", pattern)); - string pattern2 = `[&-z]`; - assert(match("b", pattern2)); -} - -// https://issues.dlang.org/show_bug.cgi?id=7111 -@safe unittest -{ - assert(match("", regex("^"))); -} - -// https://issues.dlang.org/show_bug.cgi?id=7300 -@safe unittest -{ - assert(!match("a"d, "aa"d)); -} - -// https://issues.dlang.org/show_bug.cgi?id=7551 -@safe unittest -{ - auto r = regex("[]abc]*"); - assert("]ab".matchFirst(r).hit == "]ab"); - assertThrown(regex("[]")); - auto r2 = regex("[]abc--ab]*"); - assert("]ac".matchFirst(r2).hit == "]"); -} - -// https://issues.dlang.org/show_bug.cgi?id=7674 -@safe unittest -{ - assert("1234".replace(regex("^"), "$$") == "$1234"); - assert("hello?".replace(regex(r"\?", "g"), r"\?") == r"hello\?"); - assert("hello?".replace(regex(r"\?", "g"), r"\\?") != r"hello\?"); -} - -// https://issues.dlang.org/show_bug.cgi?id=7679 -@safe unittest -{ - import std.algorithm.comparison : equal; - static foreach (S; AliasSeq!(string, wstring, dstring)) - {{ - enum re = ctRegex!(to!S(r"\.")); - auto str = to!S("a.b"); - assert(equal(std.regex.splitter(str, re), [to!S("a"), to!S("b")])); - assert(split(str, re) == [to!S("a"), to!S("b")]); - }} -} - -// https://issues.dlang.org/show_bug.cgi?id=8203 -@safe unittest -{ - string data = " - NAME = XPAW01_STA:STATION - NAME = XPAW01_STA - "; - auto uniFileOld = data; - auto r = regex( - r"^NAME = (?P[a-zA-Z0-9_]+):*(?P[a-zA-Z0-9_]*)","gm"); - auto uniCapturesNew = match(uniFileOld, r); - for (int i = 0; i < 20; i++) - foreach (matchNew; uniCapturesNew) {} - //a second issue with same symptoms - auto r2 = regex(`([Đ°-яА-ĐŻ\-_]+\s*)+(?<=[\s\.,\^])`); - match("аллея Театральная", r2); -} - -// https://issues.dlang.org/show_bug.cgi?id=8637 purity of enforce -@safe unittest -{ - auto m = match("hello world", regex("world")); - enforce(m); -} - -// https://issues.dlang.org/show_bug.cgi?id=8725 -@safe unittest -{ - static italic = regex( r"\* - (?!\s+) - (.*?) - (?!\s+) - \*", "gx" ); - string input = "this * is* interesting, *very* interesting"; - assert(replace(input, italic, "$1") == - "this * is* interesting, very interesting"); -} - -// https://issues.dlang.org/show_bug.cgi?id=8349 -@safe unittest -{ - enum peakRegexStr = r"\>(wgEncode.*Tfbs.*\.(?:narrow)|(?:broad)Peak.gz)"; - enum peakRegex = ctRegex!(peakRegexStr); - //note that the regex pattern itself is probably bogus - assert(match(r"\>wgEncode-blah-Tfbs.narrow", peakRegex)); -} - -// https://issues.dlang.org/show_bug.cgi?id=9211 -@safe unittest -{ - import std.algorithm.comparison : equal; - auto rx_1 = regex(r"^(\w)*(\d)"); - auto m = match("1234", rx_1); - assert(equal(m.front, ["1234", "3", "4"])); - auto rx_2 = regex(r"^([0-9])*(\d)"); - auto m2 = match("1234", rx_2); - assert(equal(m2.front, ["1234", "3", "4"])); -} - -// https://issues.dlang.org/show_bug.cgi?id=9280 -@safe unittest -{ - string tomatch = "a!b@c"; - static r = regex(r"^(?P.*?)!(?P.*?)@(?P.*?)$"); - auto nm = match(tomatch, r); - assert(nm); - auto c = nm.captures; - assert(c[1] == "a"); - assert(c["nick"] == "a"); -} - - -// https://issues.dlang.org/show_bug.cgi?id=9579 -@safe unittest -{ - char[] input = ['a', 'b', 'c']; - string format = "($1)"; - // used to give a compile error: - auto re = regex(`(a)`, "g"); - auto r = replace(input, re, format); - assert(r == "(a)bc"); -} - -// https://issues.dlang.org/show_bug.cgi?id=9634 -@safe unittest -{ - auto re = ctRegex!"(?:a+)"; - assert(match("aaaa", re).hit == "aaaa"); -} - -// https://issues.dlang.org/show_bug.cgi?id=10798 -@safe unittest -{ - auto cr = ctRegex!("[abcd--c]*"); - auto m = "abc".match(cr); - assert(m); - assert(m.hit == "ab"); -} - -// https://issues.dlang.org/show_bug.cgi?id=10913 -@system unittest -{ - @system static string foo(const(char)[] s) - { - return s.dup; - } - @safe static string bar(const(char)[] s) - { - return s.dup; - } - () @system { - replace!((a) => foo(a.hit))("blah", regex(`a`)); - }(); - () @safe { - replace!((a) => bar(a.hit))("blah", regex(`a`)); - }(); -} - -// https://issues.dlang.org/show_bug.cgi?id=11262 -@safe unittest -{ - enum reg = ctRegex!(r",", "g"); - auto str = "This,List"; - str = str.replace(reg, "-"); - assert(str == "This-List"); -} - -// https://issues.dlang.org/show_bug.cgi?id=11775 -@safe unittest -{ - assert(collectException(regex("a{1,0}"))); -} - -// https://issues.dlang.org/show_bug.cgi?id=11839 -@safe unittest -{ - import std.algorithm.comparison : equal; - assert(regex(`(?P\w+)`).namedCaptures.equal(["var1"])); - assert(collectException(regex(`(?P<1>\w+)`))); - assert(regex(`(?P\w+)`).namedCaptures.equal(["v1"])); - assert(regex(`(?P<__>\w+)`).namedCaptures.equal(["__"])); - assert(regex(`(?P<я>\w+)`).namedCaptures.equal(["я"])); -} - -// https://issues.dlang.org/show_bug.cgi?id=12076 -@safe unittest -{ - auto RE = ctRegex!(r"(?abc)`); - assert(collectException("abc".matchFirst(r)["b"])); -} - -// https://issues.dlang.org/show_bug.cgi?id=12691 -@safe unittest -{ - assert(bmatch("e@", "^([a-z]|)*$").empty); - assert(bmatch("e@", ctRegex!`^([a-z]|)*$`).empty); -} - -// https://issues.dlang.org/show_bug.cgi?id=12713 -@safe unittest -{ - assertThrown(regex("[[a-z]([a-z]|(([[a-z])))")); -} - -// https://issues.dlang.org/show_bug.cgi?id=12747 -@safe unittest -{ - assertThrown(regex(`^x(\1)`)); - assertThrown(regex(`^(x(\1))`)); - assertThrown(regex(`^((x)(?=\1))`)); -} - -// https://issues.dlang.org/show_bug.cgi?id=13532 -version (none) // TODO: revist once we have proper benchmark framework -@safe unittest -{ - import std.datetime.stopwatch : StopWatch, AutoStart; - import std.math.algebraic : abs; - import std.conv : to; - enum re1 = ctRegex!`[0-9][0-9]`; - immutable static re2 = ctRegex!`[0-9][0-9]`; - immutable iterations = 1_000_000; - size_t result1 = 0, result2 = 0; - auto sw = StopWatch(AutoStart.yes); - foreach (_; 0 .. iterations) - { - result1 += matchFirst("12345678", re1).length; - } - const staticTime = sw.peek(); - sw.reset(); - foreach (_; 0 .. iterations) - { - result2 += matchFirst("12345678", re2).length; - } - const enumTime = sw.peek(); - assert(result1 == result2); - auto ratio = 1.0 * enumTime.total!"usecs" / staticTime.total!"usecs"; - // enum is faster or the diff is less < 30% - assert(ratio < 1.0 || abs(ratio - 1.0) < 0.75, - "enum regex to static regex ratio "~to!string(ratio)); -} - -// https://issues.dlang.org/show_bug.cgi?id=14504 -@safe unittest -{ - auto p = ctRegex!("a?a?a?a?a?a?a?a?a?a?a?a?a?a?a?a?a?a?a?a?a?a?a?a?a?a?a?a?a?a?" ~ - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"); -} - -// https://issues.dlang.org/show_bug.cgi?id=14529 -@safe unittest -{ - auto ctPat2 = regex(r"^[CDF]$", "i"); - foreach (v; ["C", "c", "D", "d", "F", "f"]) - assert(matchAll(v, ctPat2).front.hit == v); -} - -// https://issues.dlang.org/show_bug.cgi?id=14615 -@safe unittest -{ - import std.array : appender; - import std.regex : replaceFirst, replaceFirstInto, regex; - import std.stdio : writeln; - - auto example = "Hello, world!"; - auto pattern = regex("^Hello, (bug)"); // won't find this one - auto result = replaceFirst(example, pattern, "$1 Sponge Bob"); - assert(result == "Hello, world!"); // Ok. - - auto sink = appender!string; - replaceFirstInto(sink, example, pattern, "$1 Sponge Bob"); - assert(sink.data == "Hello, world!"); - replaceAllInto(sink, example, pattern, "$1 Sponge Bob"); - assert(sink.data == "Hello, world!Hello, world!"); -} - -// https://issues.dlang.org/show_bug.cgi?id=15573 -@safe unittest -{ - auto rx = regex("[c d]", "x"); - assert("a b".matchFirst(rx)); -} - -// https://issues.dlang.org/show_bug.cgi?id=15864 -@safe unittest -{ - regex(`((.+)`; - static titleRegex = ctRegex!titlePattern; - string input = "" ~ "<".repeat(100_000).join; - assert(input.matchFirst(titleRegex).empty); -} - -// https://issues.dlang.org/show_bug.cgi?id=17212 -@safe unittest -{ - auto r = regex(" [a] ", "x"); - assert("a".matchFirst(r)); -} - -// https://issues.dlang.org/show_bug.cgi?id=17157 -@safe unittest -{ - import std.algorithm.comparison : equal; - auto ctr = ctRegex!"(a)|(b)|(c)|(d)"; - auto r = regex("(a)|(b)|(c)|(d)", "g"); - auto s = "--a--b--c--d--"; - auto outcomes = [ - ["a", "a", "", "", ""], - ["b", "", "b", "", ""], - ["c", "", "", "c", ""], - ["d", "", "", "", "d"] - ]; - assert(equal!equal(s.matchAll(ctr), outcomes)); - assert(equal!equal(s.bmatch(r), outcomes)); -} - -// https://issues.dlang.org/show_bug.cgi?id=17667 -@safe unittest -{ - import std.algorithm.searching : canFind; - void willThrow(T, size_t line = __LINE__)(T arg, string msg) - { - auto e = collectException(regex(arg)); - assert(e.msg.canFind(msg), to!string(line) ~ ": " ~ e.msg); - } - willThrow([r".", r"[\(\{[\]\}\)]"], "no matching ']' found while parsing character class"); - willThrow([r"[\", r"123"], "no matching ']' found while parsing character class"); - willThrow([r"[a-", r"123"], "no matching ']' found while parsing character class"); - willThrow([r"[a-\", r"123"], "no matching ']' found while parsing character class"); - willThrow([r"\", r"123"], "invalid escape sequence"); -} - -// https://issues.dlang.org/show_bug.cgi?id=17668 -@safe unittest -{ - import std.algorithm.searching; - auto e = collectException!RegexException(regex(q"<[^]>")); - assert(e.msg.canFind("no operand for '^'"), e.msg); -} - -// https://issues.dlang.org/show_bug.cgi?id=17673 -@safe unittest -{ - string str = `<">`; - string[] regexps = ["abc", "\"|x"]; - auto regexp = regex(regexps); - auto c = matchFirst(str, regexp); - assert(c); - assert(c.whichPattern == 2); -} - -// https://issues.dlang.org/show_bug.cgi?id=18692 -@safe unittest -{ - auto rx = regex("()()()"); - auto ma = "".matchFirst(rx); - auto ma2 = ma; - ma = ma2; - assert(ma[1] == ""); -} diff --git a/phobos/std/regex/internal/thompson.d b/phobos/std/regex/internal/thompson.d deleted file mode 100644 index f4643ae..0000000 --- a/phobos/std/regex/internal/thompson.d +++ /dev/null @@ -1,1211 +0,0 @@ -//Written in the D programming language -/* - Implementation of Thompson NFA std.regex engine. - Key point is evaluation of all possible threads (state) at each step - in a breadth-first manner, thereby geting some nice properties: - - looking at each character only once - - merging of equivalent threads, that gives matching process linear time complexity -*/ -module std.regex.internal.thompson; - -package(std.regex): - -import std.range.primitives; -import std.regex.internal.ir; - -//State of VM thread -struct Thread(DataIndex) -{ - Thread* next; //intrusive linked list - uint pc; - uint counter; //loop counter - uint uopCounter; //counts micro operations inside one macro instruction (e.g. BackRef) - Group!DataIndex[1] matches; -} - -//head-tail singly-linked list -struct ThreadList(DataIndex) -{ - Thread!DataIndex* tip = null, toe = null; - //add new thread to the start of list - void insertFront(Thread!DataIndex* t) - { - if (tip) - { - t.next = tip; - tip = t; - } - else - { - t.next = null; - tip = toe = t; - } - } - //add new thread to the end of list - void insertBack(Thread!DataIndex* t) - { - if (toe) - { - toe.next = t; - toe = t; - } - else - tip = toe = t; - toe.next = null; - } - //move head element out of list - Thread!DataIndex* fetch() - { - auto t = tip; - if (tip == toe) - tip = toe = null; - else - tip = tip.next; - return t; - } - //non-destructive iteration of ThreadList - struct ThreadRange - { - const(Thread!DataIndex)* ct; - this(ThreadList tlist){ ct = tlist.tip; } - @property bool empty(){ return ct is null; } - @property const(Thread!DataIndex)* front(){ return ct; } - void popFront() - { - assert(ct); - ct = ct.next; - } - } - @property bool empty() - { - return tip == null; - } - ThreadRange opSlice() - { - return ThreadRange(this); - } -} - -template ThompsonOps(E, S, bool withInput:true) -{ -@trusted: - static bool op(IR code:IR.End)(E e, S* state) - { - with(e) with(state) - { - finish(t, matches, re.ir[t.pc].data); - //fix endpoint of the whole match - matches[0].end = index; - recycle(t); - //cut off low priority threads - recycle(clist); - recycle(worklist); - debug(std_regex_matcher) writeln("Finished thread ", matches); - return false; // no more state to eval - } - } - - static bool op(IR code:IR.Wordboundary)(E e, S* state) - { - with(e) with(state) - { - dchar back; - DataIndex bi; - //at start & end of input - if (atStart && wordMatcher[front]) - { - t.pc += IRL!(IR.Wordboundary); - return true; - } - else if (atEnd && s.loopBack(index).nextChar(back, bi) - && wordMatcher[back]) - { - t.pc += IRL!(IR.Wordboundary); - return true; - } - else if (s.loopBack(index).nextChar(back, bi)) - { - bool af = wordMatcher[front]; - bool ab = wordMatcher[back]; - if (af ^ ab) - { - t.pc += IRL!(IR.Wordboundary); - return true; - } - } - return popState(e); - } - } - - static bool op(IR code:IR.Notwordboundary)(E e, S* state) - { - with(e) with(state) - { - dchar back; - DataIndex bi; - //at start & end of input - if (atStart && wordMatcher[front]) - { - return popState(e); - } - else if (atEnd && s.loopBack(index).nextChar(back, bi) - && wordMatcher[back]) - { - return popState(e); - } - else if (s.loopBack(index).nextChar(back, bi)) - { - bool af = wordMatcher[front]; - bool ab = wordMatcher[back] != 0; - if (af ^ ab) - { - return popState(e); - } - } - t.pc += IRL!(IR.Notwordboundary); - } - return true; - } - - static bool op(IR code:IR.Bof)(E e, S* state) - { - with(e) with(state) - { - if (atStart) - { - t.pc += IRL!(IR.Bof); - return true; - } - else - { - return popState(e); - } - } - } - - static bool op(IR code:IR.Bol)(E e, S* state) - { - with(e) with(state) - { - dchar back; - DataIndex bi; - if (atStart - ||(s.loopBack(index).nextChar(back,bi) - && startOfLine(back, front == '\n'))) - { - t.pc += IRL!(IR.Bol); - return true; - } - else - { - return popState(e); - } - } - } - - static bool op(IR code:IR.Eof)(E e, S* state) - { - with(e) with(state) - { - if (atEnd) - { - t.pc += IRL!(IR.Eol); - return true; - } - else - { - return popState(e); - } - } - } - - static bool op(IR code:IR.Eol)(E e, S* state) - { - with(e) with(state) - { - dchar back; - DataIndex bi; - //no matching inside \r\n - if (atEnd || (endOfLine(front, s.loopBack(index).nextChar(back, bi) - && back == '\r'))) - { - t.pc += IRL!(IR.Eol); - return true; - } - else - { - return popState(e); - } - - } - } - - static bool op(IR code:IR.InfiniteStart)(E e, S* state) - { - with(e) with(state) - t.pc += re.ir[t.pc].data + IRL!(IR.InfiniteStart); - return op!(IR.InfiniteEnd)(e,state); - } - - static bool op(IR code:IR.InfiniteBloomStart)(E e, S* state) - { - with(e) with(state) - t.pc += re.ir[t.pc].data + IRL!(IR.InfiniteBloomStart); - return op!(IR.InfiniteBloomEnd)(e,state); - } - - static bool op(IR code:IR.InfiniteQStart)(E e, S* state) - { - with(e) with(state) - t.pc += re.ir[t.pc].data + IRL!(IR.InfiniteQStart); - return op!(IR.InfiniteQEnd)(e,state); - } - - static bool op(IR code:IR.RepeatStart)(E e, S* state) - { - with(e) with(state) - t.pc += re.ir[t.pc].data + IRL!(IR.RepeatStart); - return op!(IR.RepeatEnd)(e,state); - } - - static bool op(IR code:IR.RepeatQStart)(E e, S* state) - { - with(e) with(state) - t.pc += re.ir[t.pc].data + IRL!(IR.RepeatQStart); - return op!(IR.RepeatQEnd)(e,state); - } - - static bool op(IR code)(E e, S* state) - if (code == IR.RepeatEnd || code == IR.RepeatQEnd) - { - with(e) with(state) - { - //len, step, min, max - uint len = re.ir[t.pc].data; - uint step = re.ir[t.pc+2].raw; - uint min = re.ir[t.pc+3].raw; - if (t.counter < min) - { - t.counter += step; - t.pc -= len; - return true; - } - if (merge[re.ir[t.pc + 1].raw+t.counter] < genCounter) - { - debug(std_regex_matcher) writefln("A thread(pc=%s) passed there : %s ; GenCounter=%s mergetab=%s", - t.pc, index, genCounter, merge[re.ir[t.pc + 1].raw+t.counter] ); - merge[re.ir[t.pc + 1].raw+t.counter] = genCounter; - } - else - { - debug(std_regex_matcher) - writefln("A thread(pc=%s) got merged there : %s ; GenCounter=%s mergetab=%s", - t.pc, index, genCounter, merge[re.ir[t.pc + 1].raw+t.counter] ); - return popState(e); - } - uint max = re.ir[t.pc+4].raw; - if (t.counter < max) - { - if (re.ir[t.pc].code == IR.RepeatEnd) - { - //queue out-of-loop thread - worklist.insertFront(fork(t, t.pc + IRL!(IR.RepeatEnd), t.counter % step)); - t.counter += step; - t.pc -= len; - } - else - { - //queue into-loop thread - worklist.insertFront(fork(t, t.pc - len, t.counter + step)); - t.counter %= step; - t.pc += IRL!(IR.RepeatEnd); - } - } - else - { - t.counter %= step; - t.pc += IRL!(IR.RepeatEnd); - } - return true; - } - } - - static bool op(IR code)(E e, S* state) - if (code == IR.InfiniteEnd || code == IR.InfiniteQEnd) - { - with(e) with(state) - { - if (merge[re.ir[t.pc + 1].raw+t.counter] < genCounter) - { - debug(std_regex_matcher) writefln("A thread(pc=%s) passed there : %s ; GenCounter=%s mergetab=%s", - t.pc, index, genCounter, merge[re.ir[t.pc + 1].raw+t.counter] ); - merge[re.ir[t.pc + 1].raw+t.counter] = genCounter; - } - else - { - debug(std_regex_matcher) writefln("A thread(pc=%s) got merged there : %s ; GenCounter=%s mergetab=%s", - t.pc, index, genCounter, merge[re.ir[t.pc + 1].raw+t.counter] ); - return popState(e); - } - uint len = re.ir[t.pc].data; - uint pc1, pc2; //branches to take in priority order - if (re.ir[t.pc].code == IR.InfiniteEnd) - { - pc1 = t.pc - len; - pc2 = t.pc + IRL!(IR.InfiniteEnd); - } - else - { - pc1 = t.pc + IRL!(IR.InfiniteEnd); - pc2 = t.pc - len; - } - worklist.insertFront(fork(t, pc2, t.counter)); - t.pc = pc1; - return true; - } - } - - static bool op(IR code)(E e, S* state) - if (code == IR.InfiniteBloomEnd) - { - with(e) with(state) - { - if (merge[re.ir[t.pc + 1].raw+t.counter] < genCounter) - { - debug(std_regex_matcher) writefln("A thread(pc=%s) passed there : %s ; GenCounter=%s mergetab=%s", - t.pc, index, genCounter, merge[re.ir[t.pc + 1].raw+t.counter] ); - merge[re.ir[t.pc + 1].raw+t.counter] = genCounter; - } - else - { - debug(std_regex_matcher) writefln("A thread(pc=%s) got merged there : %s ; GenCounter=%s mergetab=%s", - t.pc, index, genCounter, merge[re.ir[t.pc + 1].raw+t.counter] ); - return popState(e); - } - uint len = re.ir[t.pc].data; - uint pc1, pc2; //branches to take in priority order - pc1 = t.pc - len; - pc2 = t.pc + IRL!(IR.InfiniteBloomEnd); - uint filterIndex = re.ir[t.pc + 2].raw; - if (re.filters[filterIndex][front]) - worklist.insertFront(fork(t, pc2, t.counter)); - t.pc = pc1; - return true; - } - } - - static bool op(IR code:IR.OrEnd)(E e, S* state) - { - with(e) with(state) - { - if (merge[re.ir[t.pc + 1].raw+t.counter] < genCounter) - { - debug(std_regex_matcher) writefln("A thread(pc=%s) passed there : %s ; GenCounter=%s mergetab=%s", - t.pc, s[index .. s.lastIndex], genCounter, merge[re.ir[t.pc + 1].raw + t.counter] ); - merge[re.ir[t.pc + 1].raw+t.counter] = genCounter; - t.pc += IRL!(IR.OrEnd); - } - else - { - debug(std_regex_matcher) writefln("A thread(pc=%s) got merged there : %s ; GenCounter=%s mergetab=%s", - t.pc, s[index .. s.lastIndex], genCounter, merge[re.ir[t.pc + 1].raw + t.counter] ); - return popState(e); - } - return true; - } - } - - static bool op(IR code:IR.OrStart)(E e, S* state) - { - with(e) with(state) - { - t.pc += IRL!(IR.OrStart); - return op!(IR.Option)(e,state); - } - } - - static bool op(IR code:IR.Option)(E e, S* state) - { - with(e) with(state) - { - uint next = t.pc + re.ir[t.pc].data + IRL!(IR.Option); - //queue next Option - if (re.ir[next].code == IR.Option) - { - worklist.insertFront(fork(t, next, t.counter)); - } - t.pc += IRL!(IR.Option); - return true; - } - } - - static bool op(IR code:IR.GotoEndOr)(E e, S* state) - { - with(e) with(state) - { - t.pc = t.pc + re.ir[t.pc].data + IRL!(IR.GotoEndOr); - return op!(IR.OrEnd)(e, state); - } - } - - static bool op(IR code:IR.GroupStart)(E e, S* state) - { - with(e) with(state) - { - uint n = re.ir[t.pc].data; - t.matches.ptr[n].begin = index; - t.pc += IRL!(IR.GroupStart); - return true; - } - } - static bool op(IR code:IR.GroupEnd)(E e, S* state) - { - with(e) with(state) - { - uint n = re.ir[t.pc].data; - t.matches.ptr[n].end = index; - t.pc += IRL!(IR.GroupEnd); - return true; - } - } - - static bool op(IR code:IR.Backref)(E e, S* state) - { - with(e) with(state) - { - uint n = re.ir[t.pc].data; - Group!DataIndex* source = re.ir[t.pc].localRef ? t.matches.ptr : backrefed.ptr; - assert(source); - if (source[n].begin == source[n].end)//zero-width Backref! - { - t.pc += IRL!(IR.Backref); - return true; - } - else - { - size_t idx = source[n].begin + t.uopCounter; - size_t end = source[n].end; - if (s[idx .. end].front == front) - { - import std.utf : stride; - - t.uopCounter += stride(s[idx .. end], 0); - if (t.uopCounter + source[n].begin == source[n].end) - {//last codepoint - t.pc += IRL!(IR.Backref); - t.uopCounter = 0; - } - nlist.insertBack(t); - } - else - recycle(t); - t = worklist.fetch(); - return t != null; - } - } - } - - - static bool op(IR code)(E e, S* state) - if (code == IR.LookbehindStart || code == IR.NeglookbehindStart) - { - with(e) with(state) - { - uint len = re.ir[t.pc].data; - uint ms = re.ir[t.pc + 1].raw, me = re.ir[t.pc + 2].raw; - uint end = t.pc + len + IRL!(IR.LookbehindEnd) + IRL!(IR.LookbehindStart); - bool positive = re.ir[t.pc].code == IR.LookbehindStart; - static if (Stream.isLoopback) - auto matcher = fwdMatcher(t.pc, end, me - ms, subCounters.get(t.pc, 0)); - else - auto matcher = bwdMatcher(t.pc, end, me - ms, subCounters.get(t.pc, 0)); - matcher.backrefed = backrefed.empty ? t.matches : backrefed; - //backMatch - auto mRes = matcher.matchOneShot(t.matches.ptr[ms .. me], IRL!(IR.LookbehindStart)); - freelist = matcher.freelist; - subCounters[t.pc] = matcher.genCounter; - if ((mRes != 0 ) ^ positive) - { - return popState(e); - } - t.pc = end; - return true; - } - } - - static bool op(IR code)(E e, S* state) - if (code == IR.LookaheadStart || code == IR.NeglookaheadStart) - { - with(e) with(state) - { - auto save = index; - uint len = re.ir[t.pc].data; - uint ms = re.ir[t.pc+1].raw, me = re.ir[t.pc+2].raw; - uint end = t.pc+len+IRL!(IR.LookaheadEnd)+IRL!(IR.LookaheadStart); - bool positive = re.ir[t.pc].code == IR.LookaheadStart; - static if (Stream.isLoopback) - auto matcher = bwdMatcher(t.pc, end, me - ms, subCounters.get(t.pc, 0)); - else - auto matcher = fwdMatcher(t.pc, end, me - ms, subCounters.get(t.pc, 0)); - matcher.backrefed = backrefed.empty ? t.matches : backrefed; - auto mRes = matcher.matchOneShot(t.matches.ptr[ms .. me], IRL!(IR.LookaheadStart)); - freelist = matcher.freelist; - subCounters[t.pc] = matcher.genCounter; - s.reset(index); - next(); - if ((mRes != 0) ^ positive) - { - return popState(e); - } - t.pc = end; - return true; - } - } - - static bool op(IR code)(E e, S* state) - if (code == IR.LookaheadEnd || code == IR.NeglookaheadEnd || - code == IR.LookbehindEnd || code == IR.NeglookbehindEnd) - { - with(e) with(state) - { - finish(t, matches.ptr[0 .. re.ngroup], re.ir[t.pc].data); - recycle(t); - //cut off low priority threads - recycle(clist); - recycle(worklist); - return false; // no more state - } - } - - static bool op(IR code:IR.Nop)(E e, S* state) - { - with(state) t.pc += IRL!(IR.Nop); - return true; - } - - static bool op(IR code:IR.OrChar)(E e, S* state) - { - with(e) with(state) - { - uint len = re.ir[t.pc].sequence; - uint end = t.pc + len; - static assert(IRL!(IR.OrChar) == 1); - for (; t.pc < end; t.pc++) - if (re.ir[t.pc].data == front) - break; - if (t.pc != end) - { - t.pc = end; - nlist.insertBack(t); - } - else - recycle(t); - t = worklist.fetch(); - return t != null; - } - } - - static bool op(IR code:IR.Char)(E e, S* state) - { - with(e) with(state) - { - if (front == re.ir[t.pc].data) - { - t.pc += IRL!(IR.Char); - nlist.insertBack(t); - } - else - recycle(t); - t = worklist.fetch(); - return t != null; - } - } - - static bool op(IR code:IR.Any)(E e, S* state) - { - with(e) with(state) - { - t.pc += IRL!(IR.Any); - nlist.insertBack(t); - t = worklist.fetch(); - return t != null; - } - } - - static bool op(IR code:IR.CodepointSet)(E e, S* state) - { - with(e) with(state) - { - if (re.charsets[re.ir[t.pc].data].scanFor(front)) - { - t.pc += IRL!(IR.CodepointSet); - nlist.insertBack(t); - } - else - { - recycle(t); - } - t = worklist.fetch(); - return t != null; - } - } - - static bool op(IR code:IR.Trie)(E e, S* state) - { - with(e) with(state) - { - if (re.matchers[re.ir[t.pc].data][front]) - { - t.pc += IRL!(IR.Trie); - nlist.insertBack(t); - } - else - { - recycle(t); - } - t = worklist.fetch(); - return t != null; - } - } - -} - -template ThompsonOps(E,S, bool withInput:false) -{ -@trusted: - // can't match these without input - static bool op(IR code)(E e, S* state) - if (code == IR.Char || code == IR.OrChar || code == IR.CodepointSet - || code == IR.Trie || code == IR.Char || code == IR.Any) - { - return state.popState(e); - } - - // special case of zero-width backref - static bool op(IR code:IR.Backref)(E e, S* state) - { - with(e) with(state) - { - uint n = re.ir[t.pc].data; - Group!DataIndex* source = re.ir[t.pc].localRef ? t.matches.ptr : backrefed.ptr; - assert(source); - if (source[n].begin == source[n].end)//zero-width Backref! - { - t.pc += IRL!(IR.Backref); - return true; - } - else - return popState(e); - } - } - - // forward all control flow to normal versions - static bool op(IR code)(E e, S* state) - if (code != IR.Char && code != IR.OrChar && code != IR.CodepointSet - && code != IR.Trie && code != IR.Char && code != IR.Any && code != IR.Backref) - { - return ThompsonOps!(E,S,true).op!code(e,state); - } -} - -/+ - Thomspon matcher does all matching in lockstep, - never looking at the same char twice -+/ -@trusted class ThompsonMatcher(Char, StreamType = Input!Char): Matcher!Char -if (is(Char : dchar)) -{ - alias DataIndex = Stream.DataIndex; - alias Stream = StreamType; - alias OpFunc = bool function(ThompsonMatcher, State*) pure; - alias BackMatcher = ThompsonMatcher!(Char, BackLooper!(Stream)); - alias OpBackFunc = bool function(BackMatcher, BackMatcher.State*) pure; - Thread!DataIndex* freelist; - ThreadList!DataIndex clist, nlist; - DataIndex[] merge; - Group!DataIndex[] backrefed; - const Regex!Char re; //regex program - Stream s; - dchar front; - DataIndex index; - DataIndex genCounter; //merge trace counter, goes up on every dchar - size_t[size_t] subCounters; //a table of gen counter per sub-engine: PC -> counter - OpFunc[] opCacheTrue; // pointers to Op!(IR.xyz) for each bytecode - OpFunc[] opCacheFalse; // ditto - OpBackFunc[] opCacheBackTrue; // ditto - OpBackFunc[] opCacheBackFalse; // ditto - size_t threadSize; - size_t _refCount; - int matched; - bool exhausted; - -final: - pure - static struct State - { - Thread!DataIndex* t; - ThreadList!DataIndex worklist; - Group!DataIndex[] matches; - - bool popState(E)(E e) - { - with(e) - { - recycle(t); - t = worklist.fetch(); - return t != null; - } - } - - } - - enum kicked = __traits(hasMember, Stream, "search"); - - static size_t getThreadSize(const ref Regex!Char re) - { - return re.ngroup - ? (Thread!DataIndex).sizeof + (re.ngroup-1)*(Group!DataIndex).sizeof - : (Thread!DataIndex).sizeof - (Group!DataIndex).sizeof; - } - - static size_t initialMemory(const ref Regex!Char re) - { - return getThreadSize(re)*re.threadCount + re.hotspotTableSize*size_t.sizeof - +4*OpFunc.sizeof*re.ir.length; - } - - //true if it's start of input - @property bool atStart(){ return index == 0; } - - //true if it's end of input - @property bool atEnd(){ return index == s.lastIndex && s.atEnd; } - - override @property ref size_t refCount() @safe { return _refCount; } - - override @property ref const(Regex!Char) pattern() @safe { return re; } - - bool next() - { - if (!s.nextChar(front, index)) - { - index = s.lastIndex; - return false; - } - return true; - } - - static if (kicked) - { - bool search() - { - - if (!s.search(re.kickstart, front, index)) - { - index = s.lastIndex; - return false; - } - return true; - } - } - - void initExternalMemory(void[] memory) - { - threadSize = getThreadSize(re); - prepareFreeList(re.threadCount, memory); - if (re.hotspotTableSize) - { - merge = arrayInChunk!(DataIndex)(re.hotspotTableSize, memory); - merge[] = 0; - } - opCacheTrue = arrayInChunk!(OpFunc)(re.ir.length, memory); - opCacheFalse = arrayInChunk!(OpFunc)(re.ir.length, memory); - opCacheBackTrue = arrayInChunk!(OpBackFunc)(re.ir.length, memory); - opCacheBackFalse = arrayInChunk!(OpBackFunc)(re.ir.length, memory); - - for (uint pc = 0; pc<re.ir.length; pc += re.ir[pc].length) - { - L_dispatch: - switch (re.ir[pc].code) - { - foreach (e; __traits(allMembers, IR)) - { - mixin(`case IR.`~e~`: - opCacheTrue[pc] = &Ops!(true).op!(IR.`~e~`); - opCacheBackTrue[pc] = &BackOps!(true).op!(IR.`~e~`); - opCacheFalse[pc] = &Ops!(false).op!(IR.`~e~`); - opCacheBackFalse[pc] = &BackOps!(false).op!(IR.`~e~`); - break L_dispatch; - `); - } - default: - assert(0, "Unrecognized instruction "~re.ir[pc].mnemonic); - } - } - } - - override Matcher!Char rearm(in Char[] data) - { - exhausted = false; - matched = 0; - s = Stream(data); - return this; - } - - this()(ref const Regex!Char program, Stream stream, void[] memory) - { - // We are emplace'd to malloced memory w/o blitting T.init over it\ - // make sure we initialize all fields explicitly - _refCount = 1; - subCounters = null; - backrefed = null; - exhausted = false; - matched = 0; - re = program; - s = stream; - initExternalMemory(memory); - genCounter = 0; - } - - this(ThompsonMatcher matcher, size_t lo, size_t hi, uint nGroup, Stream stream) - { - _refCount = 1; - subCounters = matcher.subCounters; - s = stream; - auto code = matcher.re.ir[lo .. hi]; - re = matcher.re.withCode(code).withNGroup(nGroup); - threadSize = matcher.threadSize; - merge = matcher.merge; - freelist = matcher.freelist; - opCacheTrue = matcher.opCacheTrue[lo .. hi]; - opCacheBackTrue = matcher.opCacheBackTrue[lo .. hi]; - opCacheFalse = matcher.opCacheFalse[lo .. hi]; - opCacheBackFalse = matcher.opCacheBackFalse[lo .. hi]; - front = matcher.front; - index = matcher.index; - } - - this(BackMatcher matcher, size_t lo, size_t hi, uint nGroup, Stream stream) - { - _refCount = 1; - subCounters = matcher.subCounters; - s = stream; - auto code = matcher.re.ir[lo .. hi]; - re = matcher.re.withCode(code).withNGroup(nGroup); - threadSize = matcher.threadSize; - merge = matcher.merge; - freelist = matcher.freelist; - opCacheTrue = matcher.opCacheBackTrue[lo .. hi]; - opCacheBackTrue = matcher.opCacheTrue[lo .. hi]; - opCacheFalse = matcher.opCacheBackFalse[lo .. hi]; - opCacheBackFalse = matcher.opCacheFalse[lo .. hi]; - front = matcher.front; - index = matcher.index; - } - - auto fwdMatcher()(size_t lo, size_t hi, uint nGroup, size_t counter) - { - auto m = new ThompsonMatcher!(Char, Stream)(this, lo, hi, nGroup, s); - m.genCounter = counter; - return m; - } - - auto bwdMatcher()(size_t lo, size_t hi, uint nGroup, size_t counter) - { - alias BackLooper = typeof(s.loopBack(index)); - auto m = new ThompsonMatcher!(Char, BackLooper)(this, lo, hi, nGroup, s.loopBack(index)); - m.genCounter = counter; - m.next(); - return m; - } - - override void dupTo(Matcher!Char engine, void[] memory) - { - auto thompson = cast(ThompsonMatcher) engine; - thompson.s = s; - thompson.subCounters = null; - thompson.front = front; - thompson.index = index; - thompson.matched = matched; - thompson.exhausted = exhausted; - thompson.initExternalMemory(memory); - } - - override int match(Group!DataIndex[] matches) - { - debug(std_regex_matcher) - writeln("------------------------------------------"); - if (exhausted) - { - return false; - } - if (re.flags & RegexInfo.oneShot) - { - next(); - exhausted = true; - return matchOneShot(matches); - } - static if (kicked) - if (!re.kickstart.empty) - return matchImpl!(true)(matches); - return matchImpl!(false)(matches); - } - - //match the input and fill matches - int matchImpl(bool withSearch)(Group!DataIndex[] matches) - { - if (!matched && clist.empty) - { - static if (withSearch) - search(); - else - next(); - } - else//char in question is fetched in prev call to match - { - matched = 0; - } - State state; - state.matches = matches; - - if (!atEnd)//if no char - for (;;) - { - genCounter++; - debug(std_regex_matcher) - { - writefln("Threaded matching threads at %s", s[index .. s.lastIndex]); - foreach (t; clist[]) - { - assert(t); - writef("pc=%s ",t.pc); - write(t.matches); - writeln(); - } - } - for (state.t = clist.fetch(); state.t; state.t = clist.fetch()) - { - eval!true(&state); - } - //if we already have match no need to push the engine - if (!matched) - { - state.t = createStart(index); - eval!true(&state);//new thread staring at this position - } - else if (nlist.empty) - { - debug(std_regex_matcher) writeln("Stopped matching before consuming full input"); - break;//not a partial match for sure - } - clist = nlist; - nlist = (ThreadList!DataIndex).init; - if (clist.tip is null) - { - static if (withSearch) - { - if (!search()) - break; - } - else - { - if (!next()) - break; - } - } - else if (!next()) - { - if (!atEnd) return false; - exhausted = true; - break; - } - } - - genCounter++; //increment also on each end - debug(std_regex_matcher) writefln("Threaded matching threads at end"); - //try out all zero-width posibilities - for (state.t = clist.fetch(); state.t; state.t = clist.fetch()) - { - eval!false(&state); - } - if (!matched) - { - state.t = createStart(index); - eval!false(&state);//new thread starting at end of input - } - if (matched) - {//in case NFA found match along the way - //and last possible longer alternative ultimately failed - s.reset(matches[0].end);//reset to last successful match - next();//and reload front character - //--- here the exact state of stream was restored --- - exhausted = atEnd || !(re.flags & RegexOption.global); - //+ empty match advances the input - if (!exhausted && matches[0].begin == matches[0].end) - next(); - } - return matched; - } - - /+ - handle succesful threads - +/ - void finish(const(Thread!DataIndex)* t, Group!DataIndex[] matches, int code) - { - matches.ptr[0 .. re.ngroup] = t.matches.ptr[0 .. re.ngroup]; - debug(std_regex_matcher) - { - writef("FOUND pc=%s prog_len=%s", - t.pc, re.ir.length); - if (!matches.empty) - writefln(": %s..%s", matches[0].begin, matches[0].end); - foreach (v; matches) - writefln("%d .. %d", v.begin, v.end); - } - matched = code; - } - - alias Ops(bool withInput) = ThompsonOps!(ThompsonMatcher, State, withInput); - alias BackOps(bool withInput) = ThompsonOps!(BackMatcher, BackMatcher.State, withInput); - - /+ - match thread against codepoint, cutting trough all 0-width instructions - and taking care of control flow, then add it to nlist - +/ - void eval(bool withInput)(State* state) - { - debug(std_regex_matcher) writeln("---- Evaluating thread"); - static if (withInput) - while (opCacheTrue.ptr[state.t.pc](this, state)){} - else - while (opCacheFalse.ptr[state.t.pc](this, state)){} - } - enum uint RestartPc = uint.max; - //match the input, evaluating IR without searching - int matchOneShot(Group!DataIndex[] matches, uint startPc = 0) - { - debug(std_regex_matcher) - { - writefln("---------------single shot match ----------------- "); - } - alias evalFn = eval; - assert(clist == (ThreadList!DataIndex).init || startPc == RestartPc); // incorrect after a partial match - assert(nlist == (ThreadList!DataIndex).init || startPc == RestartPc); - State state; - state.matches = matches; - if (!atEnd)//if no char - { - debug(std_regex_matcher) - { - writefln("-- Threaded matching threads at %s", s[index .. s.lastIndex]); - } - if (startPc != RestartPc) - { - state.t = createStart(index, startPc); - genCounter++; - evalFn!true(&state); - } - for (;;) - { - debug(std_regex_matcher) writeln("\n-- Started iteration of main cycle"); - genCounter++; - debug(std_regex_matcher) - { - foreach (t; clist[]) - { - assert(t); - } - } - for (state.t = clist.fetch(); state.t; state.t = clist.fetch()) - { - evalFn!true(&state); - } - if (nlist.empty) - { - debug(std_regex_matcher) writeln("Stopped matching before consuming full input"); - break;//not a partial match for sure - } - clist = nlist; - nlist = (ThreadList!DataIndex).init; - if (!next()) - break; - debug(std_regex_matcher) writeln("-- Ended iteration of main cycle\n"); - } - } - genCounter++; //increment also on each end - debug(std_regex_matcher) writefln("-- Matching threads at end"); - //try out all zero-width posibilities - for (state.t = clist.fetch(); state.t; state.t = clist.fetch()) - { - evalFn!false(&state); - } - if (!matched) - { - state.t = createStart(index, startPc); - evalFn!false(&state); - } - return matched; - } - - //get a dirty recycled Thread - Thread!DataIndex* allocate() - { - assert(freelist, "not enough preallocated memory"); - Thread!DataIndex* t = freelist; - freelist = freelist.next; - return t; - } - - //link memory into a free list of Threads - void prepareFreeList(size_t size, ref void[] memory) - { - void[] mem = memory[0 .. threadSize*size]; - memory = memory[threadSize * size .. $]; - freelist = cast(Thread!DataIndex*)&mem[0]; - size_t i; - for (i = threadSize; i < threadSize*size; i += threadSize) - (cast(Thread!DataIndex*)&mem[i-threadSize]).next = cast(Thread!DataIndex*)&mem[i]; - (cast(Thread!DataIndex*)&mem[i-threadSize]).next = null; - } - - //dispose a thread - void recycle(Thread!DataIndex* t) - { - t.next = freelist; - freelist = t; - } - - //dispose list of threads - void recycle(ref ThreadList!DataIndex list) - { - if (list.tip) - { - // just put this head-tail list in front of freelist - list.toe.next = freelist; - freelist = list.tip; - list = list.init; - } - } - - //creates a copy of master thread with given pc - Thread!DataIndex* fork(Thread!DataIndex* master, uint pc, uint counter) - { - auto t = allocate(); - t.matches.ptr[0 .. re.ngroup] = master.matches.ptr[0 .. re.ngroup]; - t.pc = pc; - t.counter = counter; - t.uopCounter = 0; - return t; - } - - //creates a start thread - Thread!DataIndex* createStart(DataIndex index, uint pc = 0) - { - auto t = allocate(); - t.matches.ptr[0 .. re.ngroup] = (Group!DataIndex).init; - t.matches[0].begin = index; - t.pc = pc; - t.counter = 0; - t.uopCounter = 0; - return t; - } -} diff --git a/phobos/std/regex/package.d b/phobos/std/regex/package.d deleted file mode 100644 index d6a01e2..0000000 --- a/phobos/std/regex/package.d +++ /dev/null @@ -1,1773 +0,0 @@ -/++ - $(LINK2 https://en.wikipedia.org/wiki/Regular_expression, Regular expressions) - are a commonly used method of pattern matching - on strings, with $(I regex) being a catchy word for a pattern in this domain - specific language. Typical problems usually solved by regular expressions - include validation of user input and the ubiquitous find $(AMP) replace - in text processing utilities. - -$(SCRIPT inhibitQuickIndex = 1;) -$(DIVC quickindex, -$(BOOKTABLE, -$(TR $(TH Category) $(TH Functions)) -$(TR $(TD Matching) $(TD - $(LREF bmatch) - $(LREF match) - $(LREF matchAll) - $(LREF matchFirst) -)) -$(TR $(TD Building) $(TD - $(LREF ctRegex) - $(LREF escaper) - $(LREF regex) -)) -$(TR $(TD Replace) $(TD - $(LREF replace) - $(LREF replaceAll) - $(LREF replaceAllInto) - $(LREF replaceFirst) - $(LREF replaceFirstInto) -)) -$(TR $(TD Split) $(TD - $(LREF split) - $(LREF splitter) -)) -$(TR $(TD Objects) $(TD - $(LREF Captures) - $(LREF Regex) - $(LREF RegexException) - $(LREF RegexMatch) - $(LREF Splitter) - $(LREF StaticRegex) -)) -)) - - $(SECTION Synopsis) - - Create a regex at runtime: - $(RUNNABLE_EXAMPLE - $(RUNNABLE_EXAMPLE_STDIN -They met on 24/01/1970. -7/8/99 wasn't as hot as 7/8/2022. -) - --- - import std.regex; - import std.stdio; - // Print out all possible dd/mm/yy(yy) dates found in user input. - auto r = regex(r"\b[0-9][0-9]?/[0-9][0-9]?/[0-9][0-9](?:[0-9][0-9])?\b"); - foreach (line; stdin.byLine) - { - // matchAll() returns a range that can be iterated - // to get all subsequent matches. - foreach (c; matchAll(line, r)) - writeln(c.hit); - } - --- - ) - Create a static regex at compile-time, which contains fast native code: - $(RUNNABLE_EXAMPLE - --- - import std.regex; - auto ctr = ctRegex!(`^.*/([^/]+)/?$`); - - // It works just like a normal regex: - auto c2 = matchFirst("foo/bar", ctr); // First match found here, if any - assert(!c2.empty); // Be sure to check if there is a match before examining contents! - assert(c2[1] == "bar"); // Captures is a range of submatches: 0 = full match. - --- - ) - Multi-pattern regex: - $(RUNNABLE_EXAMPLE - --- - import std.regex; - auto multi = regex([`\d+,\d+`, `([a-z]+):(\d+)`]); - auto m = "abc:43 12,34".matchAll(multi); - assert(m.front.whichPattern == 2); - assert(m.front[1] == "abc"); - assert(m.front[2] == "43"); - m.popFront(); - assert(m.front.whichPattern == 1); - assert(m.front[0] == "12,34"); - --- - ) - $(LREF Captures) and `opCast!bool`: - $(RUNNABLE_EXAMPLE - --- - import std.regex; - // The result of `matchAll/matchFirst` is directly testable with `if/assert/while`, - // e.g. test if a string consists of letters only: - assert(matchFirst("LettersOnly", `^\p{L}+$`)); - - // And we can take advantage of the ability to define a variable in the IfCondition: - if (const captures = matchFirst("At l34st one digit, but maybe more...", `((\d)(\d*))`)) - { - assert(captures[2] == "3"); - assert(captures[3] == "4"); - assert(captures[1] == "34"); - } - --- - ) - See_Also: $(LINK2 https://dlang.org/spec/statement.html#IfCondition, `IfCondition`). - - $(SECTION Syntax and general information) - The general usage guideline is to keep regex complexity on the side of simplicity, - as its capabilities reside in purely character-level manipulation. - As such it's ill-suited for tasks involving higher level invariants - like matching an integer number $(U bounded) in an [a,b] interval. - Checks of this sort of are better addressed by additional post-processing. - - The basic syntax shouldn't surprise experienced users of regular expressions. - For an introduction to `std.regex` see a - $(HTTP dlang.org/regular-expression.html, short tour) of the module API - and its abilities. - - There are other web resources on regular expressions to help newcomers, - and a good $(HTTP www.regular-expressions.info, reference with tutorial) - can easily be found. - - This library uses a remarkably common ECMAScript syntax flavor - with the following extensions: - $(UL - $(LI Named subexpressions, with Python syntax. ) - $(LI Unicode properties such as Scripts, Blocks and common binary properties e.g Alphabetic, White_Space, Hex_Digit etc.) - $(LI Arbitrary length and complexity lookbehind, including lookahead in lookbehind and vise-versa.) - ) - - $(REG_START Pattern syntax ) - $(I std.regex operates on codepoint level, - 'character' in this table denotes a single Unicode codepoint.) - $(REG_TABLE - $(REG_TITLE Pattern element, Semantics ) - $(REG_TITLE Atoms, Match single characters ) - $(REG_ROW any character except [{|*+?()^$, Matches the character itself. ) - $(REG_ROW ., In single line mode matches any character. - Otherwise it matches any character except '\n' and '\r'. ) - $(REG_ROW [class], Matches a single character - that belongs to this character class. ) - $(REG_ROW [^class], Matches a single character that - does $(U not) belong to this character class.) - $(REG_ROW \cC, Matches the control character corresponding to letter C) - $(REG_ROW \xXX, Matches a character with hexadecimal value of XX. ) - $(REG_ROW \uXXXX, Matches a character with hexadecimal value of XXXX. ) - $(REG_ROW \U00YYYYYY, Matches a character with hexadecimal value of YYYYYY. ) - $(REG_ROW \f, Matches a formfeed character. ) - $(REG_ROW \n, Matches a linefeed character. ) - $(REG_ROW \r, Matches a carriage return character. ) - $(REG_ROW \t, Matches a tab character. ) - $(REG_ROW \v, Matches a vertical tab character. ) - $(REG_ROW \d, Matches any Unicode digit. ) - $(REG_ROW \D, Matches any character except Unicode digits. ) - $(REG_ROW \w, Matches any word character (note: this includes numbers).) - $(REG_ROW \W, Matches any non-word character.) - $(REG_ROW \s, Matches whitespace, same as \p{White_Space}.) - $(REG_ROW \S, Matches any character except those recognized as $(I \s ). ) - $(REG_ROW \\\\, Matches \ character. ) - $(REG_ROW \c where c is one of [|*+?(), Matches the character c itself. ) - $(REG_ROW \p{PropertyName}, Matches a character that belongs - to the Unicode PropertyName set. - Single letter abbreviations can be used without surrounding {,}. ) - $(REG_ROW \P{PropertyName}, Matches a character that does not belong - to the Unicode PropertyName set. - Single letter abbreviations can be used without surrounding {,}. ) - $(REG_ROW \p{InBasicLatin}, Matches any character that is part of - the BasicLatin Unicode $(U block).) - $(REG_ROW \P{InBasicLatin}, Matches any character except ones in - the BasicLatin Unicode $(U block).) - $(REG_ROW \p{Cyrillic}, Matches any character that is part of - Cyrillic $(U script).) - $(REG_ROW \P{Cyrillic}, Matches any character except ones in - Cyrillic $(U script).) - $(REG_TITLE Quantifiers, Specify repetition of other elements) - $(REG_ROW *, Matches previous character/subexpression 0 or more times. - Greedy version - tries as many times as possible.) - $(REG_ROW *?, Matches previous character/subexpression 0 or more times. - Lazy version - stops as early as possible.) - $(REG_ROW +, Matches previous character/subexpression 1 or more times. - Greedy version - tries as many times as possible.) - $(REG_ROW +?, Matches previous character/subexpression 1 or more times. - Lazy version - stops as early as possible.) - $(REG_ROW ?, Matches previous character/subexpression 0 or 1 time. - Greedy version - tries as many times as possible.) - $(REG_ROW ??, Matches previous character/subexpression 0 or 1 time. - Lazy version - stops as early as possible.) - $(REG_ROW {n}, Matches previous character/subexpression exactly n times. ) - $(REG_ROW {n$(COMMA)}, Matches previous character/subexpression n times or more. - Greedy version - tries as many times as possible. ) - $(REG_ROW {n$(COMMA)}?, Matches previous character/subexpression n times or more. - Lazy version - stops as early as possible.) - $(REG_ROW {n$(COMMA)m}, Matches previous character/subexpression n to m times. - Greedy version - tries as many times as possible, but no more than m times. ) - $(REG_ROW {n$(COMMA)m}?, Matches previous character/subexpression n to m times. - Lazy version - stops as early as possible, but no less then n times.) - $(REG_TITLE Other, Subexpressions $(AMP) alternations ) - $(REG_ROW (regex), Matches subexpression regex, - saving matched portion of text for later retrieval. ) - $(REG_ROW (?#comment), An inline comment that is ignored while matching.) - $(REG_ROW (?:regex), Matches subexpression regex, - $(U not) saving matched portion of text. Useful to speed up matching. ) - $(REG_ROW A|B, Matches subexpression A, or failing that, matches B. ) - $(REG_ROW (?P$(LT)name$(GT)regex), Matches named subexpression - regex labeling it with name 'name'. - When referring to a matched portion of text, - names work like aliases in addition to direct numbers. - ) - $(REG_TITLE Assertions, Match position rather than character ) - $(REG_ROW ^, Matches at the beginning of input or line (in multiline mode).) - $(REG_ROW $, Matches at the end of input or line (in multiline mode). ) - $(REG_ROW \b, Matches at word boundary. ) - $(REG_ROW \B, Matches when $(U not) at word boundary. ) - $(REG_ROW (?=regex), Zero-width lookahead assertion. - Matches at a point where the subexpression - regex could be matched starting from the current position. - ) - $(REG_ROW (?!regex), Zero-width negative lookahead assertion. - Matches at a point where the subexpression - regex could $(U not) be matched starting from the current position. - ) - $(REG_ROW (?<=regex), Zero-width lookbehind assertion. Matches at a point - where the subexpression regex could be matched ending - at the current position (matching goes backwards). - ) - $(REG_ROW (?<!regex), Zero-width negative lookbehind assertion. - Matches at a point where the subexpression regex could $(U not) - be matched ending at the current position (matching goes backwards). - ) - ) - - $(REG_START Character classes ) - $(REG_TABLE - $(REG_TITLE Pattern element, Semantics ) - $(REG_ROW Any atom, Has the same meaning as outside of a character class, - except for ] which must be written as \\]) - $(REG_ROW a-z, Includes characters a, b, c, ..., z. ) - $(REG_ROW [a||b]$(COMMA) [a--b]$(COMMA) [a~~b]$(COMMA) [a$(AMP)$(AMP)b], - Where a, b are arbitrary classes, means union, set difference, - symmetric set difference, and intersection respectively. - $(I Any sequence of character class elements implicitly forms a union.) ) - ) - - $(REG_START Regex flags ) - $(REG_TABLE - $(REG_TITLE Flag, Semantics ) - $(REG_ROW g, Global regex, repeat over the whole input. ) - $(REG_ROW i, Case insensitive matching. ) - $(REG_ROW m, Multi-line mode, match ^, $ on start and end line separators - as well as start and end of input.) - $(REG_ROW s, Single-line mode, makes . match '\n' and '\r' as well. ) - $(REG_ROW x, Free-form syntax, ignores whitespace in pattern, - useful for formatting complex regular expressions. ) - ) - - $(SECTION Unicode support) - - This library provides full Level 1 support* according to - $(HTTP unicode.org/reports/tr18/, UTS 18). Specifically: - $(UL - $(LI 1.1 Hex notation via any of \uxxxx, \U00YYYYYY, \xZZ.) - $(LI 1.2 Unicode properties.) - $(LI 1.3 Character classes with set operations.) - $(LI 1.4 Word boundaries use the full set of "word" characters.) - $(LI 1.5 Using simple casefolding to match case - insensitively across the full range of codepoints.) - $(LI 1.6 Respecting line breaks as any of - \u000A | \u000B | \u000C | \u000D | \u0085 | \u2028 | \u2029 | \u000D\u000A.) - $(LI 1.7 Operating on codepoint level.) - ) - *With exception of point 1.1.1, as of yet, normalization of input - is expected to be enforced by user. - - $(SECTION Replace format string) - - A set of functions in this module that do the substitution rely - on a simple format to guide the process. In particular the table below - applies to the `format` argument of - $(LREF replaceFirst) and $(LREF replaceAll). - - The format string can reference parts of match using the following notation. - $(REG_TABLE - $(REG_TITLE Format specifier, Replaced by ) - $(REG_ROW $(DOLLAR)$(AMP), the whole match. ) - $(REG_ROW $(DOLLAR)$(BACKTICK), part of input $(I preceding) the match. ) - $(REG_ROW $', part of input $(I following) the match. ) - $(REG_ROW $$, '$' character. ) - $(REG_ROW \c $(COMMA) where c is any character, the character c itself. ) - $(REG_ROW \\\\, '\\' character. ) - $(REG_ROW $(DOLLAR)1 .. $(DOLLAR)99, submatch number 1 to 99 respectively. ) - ) - - $(SECTION Slicing and zero memory allocations orientation) - - All matches returned by pattern matching functionality in this library - are slices of the original input. The notable exception is the `replace` - family of functions that generate a new string from the input. - - In cases where producing the replacement is the ultimate goal - $(LREF replaceFirstInto) and $(LREF replaceAllInto) could come in handy - as functions that avoid allocations even for replacement. - - Copyright: Copyright Dmitry Olshansky, 2011- - - License: $(HTTP boost.org/LICENSE_1_0.txt, Boost License 1.0). - - Authors: Dmitry Olshansky, - - API and utility constructs are modeled after the original `std.regex` - by Walter Bright and Andrei Alexandrescu. - - Source: $(PHOBOSSRC std/regex/package.d) - -Macros: - REG_ROW = $(TR $(TD $(I $1 )) $(TD $+) ) - REG_TITLE = $(TR $(TD $(B $1)) $(TD $(B $2)) ) - REG_TABLE = <table border="1" cellspacing="0" cellpadding="5" > $0 </table> - REG_START = <h3><div align="center"> $0 </div></h3> - SECTION = <h3><a id="$1" href="#$1" class="anchor">$0</a></h3> - S_LINK = <a href="#$1">$+</a> - +/ -module std.regex; - -import std.range.primitives, std.traits; -import std.regex.internal.ir; -import std.typecons : Flag, Yes, No; - -/++ - `Regex` object holds regular expression pattern in compiled form. - - Instances of this object are constructed via calls to `regex`. - This is an intended form for caching and storage of frequently - used regular expressions. - - Example: - - Test if this object doesn't contain any compiled pattern. - --- - Regex!char r; - assert(r.empty); - r = regex(""); // Note: "" is a valid regex pattern. - assert(!r.empty); - --- - - Getting a range of all the named captures in the regex. - ---- - import std.range; - import std.algorithm; - - auto re = regex(`(?P<name>\w+) = (?P<var>\d+)`); - auto nc = re.namedCaptures; - static assert(isRandomAccessRange!(typeof(nc))); - assert(!nc.empty); - assert(nc.length == 2); - assert(nc.equal(["name", "var"])); - assert(nc[0] == "name"); - assert(nc[1..$].equal(["var"])); - ---- -+/ -public alias Regex(Char) = std.regex.internal.ir.Regex!(Char); - -/++ - A `StaticRegex` is `Regex` object that contains D code specially - generated at compile-time to speed up matching. - - No longer used, kept as alias to Regex for backwards compatibility. -+/ -public alias StaticRegex = Regex; - -/++ - Compile regular expression pattern for the later execution. - Returns: `Regex` object that works on inputs having - the same character width as `pattern`. - - Params: - pattern = A single regular expression to match. - patterns = An array of regular expression strings. - The resulting `Regex` object will match any expression; - use $(LREF whichPattern) to know which. - flags = The _attributes (g, i, m, s and x accepted) - - Throws: `RegexException` if there were any errors during compilation. -+/ -@trusted public auto regex(S : C[], C)(const S[] patterns, const(char)[] flags="") -if (isSomeString!(S)) -{ - import std.array : appender; - import std.functional : memoize; - enum cacheSize = 8; //TODO: invent nice interface to control regex caching - const(C)[] pat; - if (patterns.length > 1) - { - auto app = appender!S(); - foreach (i, p; patterns) - { - if (i != 0) - app.put("|"); - app.put("(?:"); - app.put(patterns[i]); - // terminator for the pattern - // to detect if the pattern unexpectedly ends - app.put("\\"); - app.put(cast(dchar)(privateUseStart+i)); - app.put(")"); - // another one to return correct whichPattern - // for all of potential alternatives in the patterns[i] - app.put("\\"); - app.put(cast(dchar)(privateUseStart+i)); - } - pat = app.data; - } - else - pat = patterns[0]; - - if (__ctfe) - return regexImpl(pat, flags); - return memoize!(regexImpl!S, cacheSize)(pat, flags); -} - -///ditto -@trusted public auto regex(S)(S pattern, const(char)[] flags="") -if (isSomeString!(S)) -{ - return regex([pattern], flags); -} - -/// -@system unittest -{ - void test(S)() - { - // multi-pattern regex example - S[] arr = [`([a-z]+):(\d+)`, `(\d+),\d+`]; - auto multi = regex(arr); // multi regex - S str = "abc:43 12,34"; - auto m = str.matchAll(multi); - assert(m.front.whichPattern == 1); - assert(m.front[1] == "abc"); - assert(m.front[2] == "43"); - m.popFront(); - assert(m.front.whichPattern == 2); - assert(m.front[1] == "12"); - } - - import std.meta : AliasSeq; - static foreach (C; AliasSeq!(string, wstring, dstring)) - // Test with const array of patterns - see https://issues.dlang.org/show_bug.cgi?id=20301 - static foreach (S; AliasSeq!(C, const C, immutable C)) - test!S(); -} - -@system unittest -{ - import std.conv : to; - import std.string : indexOf; - - immutable pattern = "s+"; - auto regexString = to!string(regex(pattern, "U")); - assert(regexString.length <= pattern.length + 100, "String representation shouldn't be unreasonably bloated."); - assert(indexOf(regexString, "s+") >= 0, "String representation should include pattern."); - assert(indexOf(regexString, 'U') >= 0, "String representation should include flags."); -} - -public auto regexImpl(S)(const S pattern, const(char)[] flags="") -if (isSomeString!(typeof(pattern))) -{ - import std.regex.internal.parser : Parser, CodeGen; - auto parser = Parser!(Unqual!(typeof(pattern)), CodeGen)(pattern, flags); - auto r = parser.program; - return r; -} - - -private struct CTRegexWrapper(Char) -{ - private immutable(Regex!Char)* re; - - // allow code that expects mutable Regex to still work - // we stay "logically const" - @property @trusted ref getRe() const { return *cast(Regex!Char*) re; } - alias getRe this; -} - -template ctRegexImpl(alias pattern, string flags="") -{ - import std.regex.internal.backtracking, std.regex.internal.parser; - static immutable r = cast(immutable) regex(pattern, flags); - alias Char = BasicElementOf!(typeof(pattern)); - enum source = ctGenRegExCode(r); - @trusted pure bool func(BacktrackingMatcher!Char matcher) - { - debug(std_regex_ctr) pragma(msg, source); - cast(void) matcher; - mixin(source); - } - static immutable staticRe = - cast(immutable) r.withFactory(new CtfeFactory!(BacktrackingMatcher, Char, func)); - enum wrapper = CTRegexWrapper!Char(&staticRe); -} - -@safe pure unittest -{ - // test compat for logical const workaround - static void test(StaticRegex!char) - { - } - enum re = ctRegex!``; - test(re); -} - -@safe pure unittest -{ - auto re = ctRegex!`foo`; - assert(matchFirst("foo", re)); - - // test reassignment - re = ctRegex!`bar`; - assert(matchFirst("bar", re)); - assert(!matchFirst("bar", ctRegex!`foo`)); -} - -/++ - Compile regular expression using CTFE - and generate optimized native machine code for matching it. - - Returns: StaticRegex object for faster matching. - - Params: - pattern = Regular expression - flags = The _attributes (g, i, m, s and x accepted) -+/ -public enum ctRegex(alias pattern, string flags="") = ctRegexImpl!(pattern, flags).wrapper; - -enum isRegexFor(RegEx, R) = is(immutable RegEx == immutable Regex!(BasicElementOf!R)) - || is(RegEx : const(Regex!(BasicElementOf!R))) - || is(immutable RegEx == immutable StaticRegex!(BasicElementOf!R)); - - -/++ - `Captures` object contains submatches captured during a call - to `match` or iteration over `RegexMatch` range. - - First element of range is the whole match. -+/ -@trusted public struct Captures(R) -if (isSomeString!R) -{//@trusted because of union inside - alias DataIndex = size_t; - alias String = R; - alias Store = SmallFixedArray!(Group!DataIndex, 3); -private: - import std.conv : text; - Store matches; - const(NamedGroup)[] _names; - R _input; - int _nMatch; - uint _f, _b; - - this(R input, uint n, const(NamedGroup)[] named) - { - _input = input; - _names = named; - matches = Store(n); - _b = n; - _f = 0; - } - - this(ref RegexMatch!R rmatch) - { - _input = rmatch._input; - _names = rmatch._engine.pattern.dict; - immutable n = rmatch._engine.pattern.ngroup; - matches = Store(n); - _b = n; - _f = 0; - } - - inout(R) getMatch(size_t index) inout - { - auto m = &matches[index]; - return *m ? _input[m.begin .. m.end] : null; - } - -public: - ///Slice of input prior to the match. - @property R pre() - { - return _nMatch == 0 ? _input[] : _input[0 .. matches[0].begin]; - } - - ///Slice of input immediately after the match. - @property R post() - { - return _nMatch == 0 ? _input[] : _input[matches[0].end .. $]; - } - - ///Slice of matched portion of input. - @property R hit() - { - assert(_nMatch, "attempted to get hit of an empty match"); - return _input[matches[0].begin .. matches[0].end]; - } - - ///Range interface. - @property R front() - { - assert(_nMatch, "attempted to get front of an empty match"); - return getMatch(_f); - } - - ///ditto - @property R back() - { - assert(_nMatch, "attempted to get back of an empty match"); - return getMatch(_b - 1); - } - - ///ditto - void popFront() - { - assert(!empty); - ++_f; - } - - ///ditto - void popBack() - { - assert(!empty); - --_b; - } - - ///ditto - @property bool empty() const { return _nMatch == 0 || _f >= _b; } - - ///ditto - inout(R) opIndex()(size_t i) inout - { - assert(_f + i < _b,text("requested submatch number ", i," is out of range")); - return getMatch(_f + i); - } - - /++ - Explicit cast to bool. - Useful as a shorthand for !(x.empty) in if and assert statements. - - --- - import std.regex; - - assert(!matchFirst("nothing", "something")); - --- - +/ - - @safe bool opCast(T:bool)() const nothrow { return _nMatch != 0; } - - /++ - Number of pattern matched counting, where 1 - the first pattern. - Returns 0 on no match. - +/ - - @safe @property int whichPattern() const nothrow { return _nMatch; } - - /// - @system unittest - { - import std.regex; - assert(matchFirst("abc", "[0-9]+", "[a-z]+").whichPattern == 2); - } - - /++ - Lookup named submatch. - - --- - import std.regex; - import std.range; - - auto c = matchFirst("a = 42;", regex(`(?P<var>\w+)\s*=\s*(?P<value>\d+);`)); - assert(c["var"] == "a"); - assert(c["value"] == "42"); - popFrontN(c, 2); - //named groups are unaffected by range primitives - assert(c["var"] =="a"); - assert(c.front == "42"); - ---- - +/ - R opIndex(String)(String i) /*const*/ //@@@BUG@@@ - if (isSomeString!String) - { - size_t index = lookupNamedGroup(_names, i); - return getMatch(index); - } - - ///Number of matches in this object. - @property size_t length() const { return _nMatch == 0 ? 0 : _b - _f; } - - ///A hook for compatibility with original std.regex. - @property ref captures(){ return this; } -} - -/// -@system unittest -{ - import std.range.primitives : popFrontN; - - auto c = matchFirst("@abc#", regex(`(\w)(\w)(\w)`)); - assert(c.pre == "@"); // Part of input preceding match - assert(c.post == "#"); // Immediately after match - assert(c.hit == c[0] && c.hit == "abc"); // The whole match - assert(c[2] == "b"); - assert(c.front == "abc"); - c.popFront(); - assert(c.front == "a"); - assert(c.back == "c"); - c.popBack(); - assert(c.back == "b"); - popFrontN(c, 2); - assert(c.empty); - - assert(!matchFirst("nothing", "something")); - - // Captures that are not matched will be null. - c = matchFirst("ac", regex(`a(b)?c`)); - assert(c); - assert(!c[1]); -} - -@system unittest -{ - Captures!string c; - string s = "abc"; - assert(cast(bool)(c = matchFirst(s, regex("d"))) - || cast(bool)(c = matchFirst(s, regex("a")))); -} - -// https://issues.dlang.org/show_bug.cgi?id=19979 -@system unittest -{ - auto c = matchFirst("bad", regex(`(^)(not )?bad($)`)); - assert(c[0] && c[0].length == "bad".length); - assert(c[1] && !c[1].length); - assert(!c[2]); - assert(c[3] && !c[3].length); -} - -/++ - A regex engine state, as returned by `match` family of functions. - - Effectively it's a forward range of Captures!R, produced - by lazily searching for matches in a given input. -+/ -@trusted public struct RegexMatch(R) -if (isSomeString!R) -{ - import std.typecons : Rebindable; -private: - alias Char = BasicElementOf!R; - Matcher!Char _engine; - Rebindable!(const MatcherFactory!Char) _factory; - R _input; - Captures!R _captures; - - this(RegEx)(R input, RegEx prog) - { - import std.exception : enforce; - _input = input; - if (prog.factory is null) _factory = defaultFactory!Char(prog); - else _factory = prog.factory; - _engine = _factory.create(prog, input); - assert(_engine.refCount == 1); - _captures = Captures!R(this); - _captures.matches.mutate((slice) pure { _captures._nMatch = _engine.match(slice); }); - } - -public: - this(this) - { - if (_engine) _factory.incRef(_engine); - } - - ~this() - { - if (_engine) _factory.decRef(_engine); - } - - ///Shorthands for front.pre, front.post, front.hit. - @property R pre() - { - return _captures.pre; - } - - ///ditto - @property R post() - { - return _captures.post; - } - - ///ditto - @property R hit() - { - return _captures.hit; - } - - /++ - Functionality for processing subsequent matches of global regexes via range interface: - --- - import std.regex; - auto m = matchAll("Hello, world!", regex(`\w+`)); - assert(m.front.hit == "Hello"); - m.popFront(); - assert(m.front.hit == "world"); - m.popFront(); - assert(m.empty); - --- - +/ - @property inout(Captures!R) front() inout - { - return _captures; - } - - ///ditto - void popFront() - { - import std.exception : enforce; - // CoW - if refCount is not 1, we are aliased by somebody else - if (_engine.refCount != 1) - { - // we create a new engine & abandon this reference - auto old = _engine; - _engine = _factory.dup(old, _input); - _factory.decRef(old); - } - _captures.matches.mutate((slice) { _captures._nMatch = _engine.match(slice); }); - } - - ///ditto - auto save(){ return this; } - - ///Test if this match object is empty. - @property bool empty() const { return _captures._nMatch == 0; } - - ///Same as !(x.empty), provided for its convenience in conditional statements. - T opCast(T:bool)(){ return !empty; } - - /// Same as .front, provided for compatibility with original std.regex. - @property inout(Captures!R) captures() inout { return _captures; } -} - -private auto matchOnceImpl(RegEx, R)(R input, const auto ref RegEx prog) @trusted -{ - alias Char = BasicElementOf!R; - static struct Key - { - immutable(Char)[] pattern; - uint flags; - } - static Key cacheKey = Key("", -1); - static Matcher!Char cache; - auto factory = prog.factory is null ? defaultFactory!Char(prog) : prog.factory; - auto key = Key(prog.pattern, prog.flags); - Matcher!Char engine; - if (cacheKey == key) - { - engine = cache; - engine.rearm(input); - } - else - { - engine = factory.create(prog, input); - if (cache) factory.decRef(cache); // destroy cached engine *after* building a new one - cache = engine; - cacheKey = key; - } - auto captures = Captures!R(input, prog.ngroup, prog.dict); - captures.matches.mutate((slice) pure { captures._nMatch = engine.match(slice); }); - return captures; -} - -// matchOnce is constructed as a safe, pure wrapper over matchOnceImpl. It can be -// faked as pure because the static mutable variables are used to cache the key and -// character matcher. The technique used avoids delegates and GC. -private @safe auto matchOnce(RegEx, R)(R input, const auto ref RegEx prog) pure -{ - static auto impl(R input, const ref RegEx prog) - { - return matchOnceImpl(input, prog); - } - - static @trusted auto pureImpl(R input, const ref RegEx prog) - { - auto p = assumePureFunction(&impl); - return p(input, prog); - } - - return pureImpl(input, prog); -} - -private auto matchMany(RegEx, R)(R input, auto ref RegEx re) @safe -{ - return RegexMatch!R(input, re.withFlags(re.flags | RegexOption.global)); -} - -@system unittest -{ - //sanity checks for new API - auto re = regex("abc"); - assert(!"abc".matchOnce(re).empty); - assert("abc".matchOnce(re)[0] == "abc"); -} - -// https://issues.dlang.org/show_bug.cgi?id=18135 -@system unittest -{ - static struct MapResult { RegexMatch!string m; } - MapResult m; - m = MapResult(); - assert(m == m); -} - -private enum isReplaceFunctor(alias fun, R) = - __traits(compiles, (Captures!R c) { fun(c); }); - -// the lowest level - just stuff replacements into the sink -private @trusted void replaceCapturesInto(alias output, Sink, R, T) - (ref Sink sink, R input, T captures) -if (isOutputRange!(Sink, dchar) && isSomeString!R) -{ - if (captures.empty) - { - sink.put(input); - return; - } - sink.put(captures.pre); - // a hack to get around bogus errors, should be simply output(captures, sink) - // "is a nested function and cannot be accessed from" - static if (isReplaceFunctor!(output, R)) - sink.put(output(captures)); //"mutator" type of function - else - output(captures, sink); //"output" type of function - sink.put(captures.post); -} - -// ditto for a range of captures -private void replaceMatchesInto(alias output, Sink, R, T) - (ref Sink sink, R input, T matches) -if (isOutputRange!(Sink, dchar) && isSomeString!R) -{ - size_t offset = 0; - foreach (cap; matches) - { - sink.put(cap.pre[offset .. $]); - // same hack, see replaceCapturesInto - static if (isReplaceFunctor!(output, R)) - sink.put(output(cap)); //"mutator" type of function - else - output(cap, sink); //"output" type of function - offset = cap.pre.length + cap.hit.length; - } - sink.put(input[offset .. $]); -} - -// a general skeleton of replaceFirst -private R replaceFirstWith(alias output, R, RegEx)(R input, RegEx re) -if (isSomeString!R && isRegexFor!(RegEx, R)) -{ - import std.array : appender; - auto data = matchFirst(input, re); - if (data.empty) - return input; - auto app = appender!(R)(); - replaceCapturesInto!output(app, input, data); - return app.data; -} - -// ditto for replaceAll -// the method parameter allows old API to ride on the back of the new one -private R replaceAllWith(alias output, - alias method=matchAll, R, RegEx)(R input, RegEx re) -if (isSomeString!R && isRegexFor!(RegEx, R)) -{ - import std.array : appender; - auto matches = method(input, re); //inout(C)[] fails - if (matches.empty) - return input; - auto app = appender!(R)(); - replaceMatchesInto!output(app, input, matches); - return app.data; -} - - -/++ - Start matching `input` to regex pattern `re`, - using Thompson NFA matching scheme. - - The use of this function is $(RED discouraged) - use either of - $(LREF matchAll) or $(LREF matchFirst). - - Delegating the kind of operation - to "g" flag is soon to be phased out along with the - ability to choose the exact matching scheme. The choice of - matching scheme to use depends highly on the pattern kind and - can done automatically on case by case basis. - - Returns: a `RegexMatch` object holding engine state after first match. -+/ - -public auto match(R, RegEx)(R input, RegEx re) -if (isSomeString!R && isRegexFor!(RegEx,R)) -{ - return RegexMatch!(Unqual!(typeof(input)))(input, re); -} - -///ditto -public auto match(R, String)(R input, String re) -if (isSomeString!R && isSomeString!String) -{ - return RegexMatch!(Unqual!(typeof(input)))(input, regex(re)); -} - -/++ - Find the first (leftmost) slice of the `input` that - matches the pattern `re`. This function picks the most suitable - regular expression engine depending on the pattern properties. - - `re` parameter can be one of three types: - $(UL - $(LI Plain string(s), in which case it's compiled to bytecode before matching. ) - $(LI Regex!char (wchar/dchar) that contains a pattern in the form of - compiled bytecode. ) - $(LI StaticRegex!char (wchar/dchar) that contains a pattern in the form of - compiled native machine code. ) - ) - - Returns: - $(LREF Captures) containing the extent of a match together with all submatches - if there was a match, otherwise an empty $(LREF Captures) object. -+/ -public auto matchFirst(R, RegEx)(R input, RegEx re) -if (isSomeString!R && isRegexFor!(RegEx, R)) -{ - return matchOnce(input, re); -} - -///ditto -public auto matchFirst(R, String)(R input, String re) -if (isSomeString!R && isSomeString!String) -{ - return matchOnce(input, regex(re)); -} - -///ditto -public auto matchFirst(R, String)(R input, String[] re...) -if (isSomeString!R && isSomeString!String) -{ - return matchOnce(input, regex(re)); -} - -/++ - Initiate a search for all non-overlapping matches to the pattern `re` - in the given `input`. The result is a lazy range of matches generated - as they are encountered in the input going left to right. - - This function picks the most suitable regular expression engine - depending on the pattern properties. - - `re` parameter can be one of three types: - $(UL - $(LI Plain string(s), in which case it's compiled to bytecode before matching. ) - $(LI Regex!char (wchar/dchar) that contains a pattern in the form of - compiled bytecode. ) - $(LI StaticRegex!char (wchar/dchar) that contains a pattern in the form of - compiled native machine code. ) - ) - - Returns: - $(LREF RegexMatch) object that represents matcher state - after the first match was found or an empty one if not present. -+/ -public auto matchAll(R, RegEx)(R input, RegEx re) -if (isSomeString!R && isRegexFor!(RegEx, R)) -{ - return matchMany(input, re); -} - -///ditto -public auto matchAll(R, String)(R input, String re) -if (isSomeString!R && isSomeString!String) -{ - return matchMany(input, regex(re)); -} - -///ditto -public auto matchAll(R, String)(R input, String[] re...) -if (isSomeString!R && isSomeString!String) -{ - return matchMany(input, regex(re)); -} - -// another set of tests just to cover the new API -@system unittest -{ - import std.algorithm.comparison : equal; - import std.algorithm.iteration : map; - import std.conv : to; - - static foreach (String; AliasSeq!(string, wstring, const(dchar)[])) - {{ - auto str1 = "blah-bleh".to!String(); - auto pat1 = "bl[ae]h".to!String(); - auto mf = matchFirst(str1, pat1); - assert(mf.equal(["blah".to!String()])); - auto mAll = matchAll(str1, pat1); - assert(mAll.equal!((a,b) => a.equal(b)) - ([["blah".to!String()], ["bleh".to!String()]])); - - auto str2 = "1/03/12 - 3/03/12".to!String(); - auto pat2 = regex([r"(\d+)/(\d+)/(\d+)".to!String(), "abc".to!String]); - auto mf2 = matchFirst(str2, pat2); - assert(mf2.equal(["1/03/12", "1", "03", "12"].map!(to!String)())); - auto mAll2 = matchAll(str2, pat2); - assert(mAll2.front.equal(mf2)); - mAll2.popFront(); - assert(mAll2.front.equal(["3/03/12", "3", "03", "12"].map!(to!String)())); - mf2.popFrontN(3); - assert(mf2.equal(["12".to!String()])); - - auto ctPat = ctRegex!(`(?P<Quot>\d+)/(?P<Denom>\d+)`.to!String()); - auto str = "2 + 34/56 - 6/1".to!String(); - auto cmf = matchFirst(str, ctPat); - assert(cmf.equal(["34/56", "34", "56"].map!(to!String)())); - assert(cmf["Quot"] == "34".to!String()); - assert(cmf["Denom"] == "56".to!String()); - - auto cmAll = matchAll(str, ctPat); - assert(cmAll.front.equal(cmf)); - cmAll.popFront(); - assert(cmAll.front.equal(["6/1", "6", "1"].map!(to!String)())); - }} -} - -/++ - Start matching of `input` to regex pattern `re`, - using traditional $(LINK2 https://en.wikipedia.org/wiki/Backtracking, - backtracking) matching scheme. - - The use of this function is $(RED discouraged) - use either of - $(LREF matchAll) or $(LREF matchFirst). - - Delegating the kind of operation - to "g" flag is soon to be phased out along with the - ability to choose the exact matching scheme. The choice of - matching scheme to use depends highly on the pattern kind and - can done automatically on case by case basis. - - Returns: a `RegexMatch` object holding engine - state after first match. - -+/ -public auto bmatch(R, RegEx)(R input, RegEx re) -if (isSomeString!R && isRegexFor!(RegEx, R)) -{ - return RegexMatch!(Unqual!(typeof(input)))(input, re); -} - -///ditto -public auto bmatch(R, String)(R input, String re) -if (isSomeString!R && isSomeString!String) -{ - return RegexMatch!(Unqual!(typeof(input)))(input, regex(re)); -} - -// produces replacement string from format using captures for substitution -package void replaceFmt(R, Capt, OutR) - (R format, Capt captures, OutR sink, bool ignoreBadSubs = false) -if (isOutputRange!(OutR, ElementEncodingType!R[]) && - isOutputRange!(OutR, ElementEncodingType!(Capt.String)[])) -{ - import std.algorithm.searching : find; - import std.ascii : isDigit, isAlpha; - import std.conv : text, parse; - import std.exception : enforce; - enum State { Normal, Dollar } - auto state = State.Normal; - size_t offset; -L_Replace_Loop: - while (!format.empty) - final switch (state) - { - case State.Normal: - for (offset = 0; offset < format.length; offset++)//no decoding - { - if (format[offset] == '$') - { - state = State.Dollar; - sink.put(format[0 .. offset]); - format = format[offset+1 .. $];//ditto - continue L_Replace_Loop; - } - } - sink.put(format[0 .. offset]); - format = format[offset .. $]; - break; - case State.Dollar: - if (isDigit(format[0])) - { - uint digit = parse!uint(format); - enforce(ignoreBadSubs || digit < captures.length, text("invalid submatch number ", digit)); - if (digit < captures.length) - sink.put(captures[digit]); - } - else if (format[0] == '{') - { - auto x = find!(a => !isAlpha(a))(format[1..$]); - enforce(!x.empty && x[0] == '}', "no matching '}' in replacement format"); - auto name = format[1 .. $ - x.length]; - format = x[1..$]; - enforce(!name.empty, "invalid name in ${...} replacement format"); - sink.put(captures[name]); - } - else if (format[0] == '&') - { - sink.put(captures[0]); - format = format[1 .. $]; - } - else if (format[0] == '`') - { - sink.put(captures.pre); - format = format[1 .. $]; - } - else if (format[0] == '\'') - { - sink.put(captures.post); - format = format[1 .. $]; - } - else if (format[0] == '$') - { - sink.put(format[0 .. 1]); - format = format[1 .. $]; - } - state = State.Normal; - break; - } - enforce(state == State.Normal, "invalid format string in regex replace"); -} - -/++ - Construct a new string from `input` by replacing the first match with - a string generated from it according to the `format` specifier. - - To replace all matches use $(LREF replaceAll). - - Params: - input = string to search - re = compiled regular expression to use - format = _format string to generate replacements from, - see $(S_LINK Replace _format string, the _format string). - - Returns: - A string of the same type with the first match (if any) replaced. - If no match is found returns the input string itself. -+/ -public R replaceFirst(R, C, RegEx)(R input, RegEx re, const(C)[] format) -if (isSomeString!R && is(C : dchar) && isRegexFor!(RegEx, R)) -{ - return replaceFirstWith!((m, sink) => replaceFmt(format, m, sink))(input, re); -} - -/// -@system unittest -{ - assert(replaceFirst("noon", regex("n"), "[$&]") == "[n]oon"); -} - -/++ - This is a general replacement tool that construct a new string by replacing - matches of pattern `re` in the `input`. Unlike the other overload - there is no format string instead captures are passed to - to a user-defined functor `fun` that returns a new string - to use as replacement. - - This version replaces the first match in `input`, - see $(LREF replaceAll) to replace the all of the matches. - - Returns: - A new string of the same type as `input` with all matches - replaced by return values of `fun`. If no matches found - returns the `input` itself. -+/ -public R replaceFirst(alias fun, R, RegEx)(R input, RegEx re) -if (isSomeString!R && isRegexFor!(RegEx, R)) -{ - return replaceFirstWith!((m, sink) => sink.put(fun(m)))(input, re); -} - -/// -@system unittest -{ - import std.conv : to; - string list = "#21 out of 46"; - string newList = replaceFirst!(cap => to!string(to!int(cap.hit)+1)) - (list, regex(`[0-9]+`)); - assert(newList == "#22 out of 46"); -} - -/++ - A variation on $(LREF replaceFirst) that instead of allocating a new string - on each call outputs the result piece-wise to the `sink`. In particular - this enables efficient construction of a final output incrementally. - - Like in $(LREF replaceFirst) family of functions there is an overload - for the substitution guided by the `format` string - and the one with the user defined callback. -+/ -public @trusted void replaceFirstInto(Sink, R, C, RegEx) - (ref Sink sink, R input, RegEx re, const(C)[] format) -if (isOutputRange!(Sink, dchar) && isSomeString!R - && is(C : dchar) && isRegexFor!(RegEx, R)) - { - replaceCapturesInto!((m, sink) => replaceFmt(format, m, sink)) - (sink, input, matchFirst(input, re)); - } - -///ditto -public @trusted void replaceFirstInto(alias fun, Sink, R, RegEx) - (Sink sink, R input, RegEx re) -if (isOutputRange!(Sink, dchar) && isSomeString!R && isRegexFor!(RegEx, R)) -{ - replaceCapturesInto!fun(sink, input, matchFirst(input, re)); -} - -/// -@system unittest -{ - import std.array; - string m1 = "first message\n"; - string m2 = "second message\n"; - auto result = appender!string(); - replaceFirstInto(result, m1, regex(`([a-z]+) message`), "$1"); - //equivalent of the above with user-defined callback - replaceFirstInto!(cap=>cap[1])(result, m2, regex(`([a-z]+) message`)); - assert(result.data == "first\nsecond\n"); -} - -//examples for replaceFirst -@system unittest -{ - import std.conv; - string list = "#21 out of 46"; - string newList = replaceFirst!(cap => to!string(to!int(cap.hit)+1)) - (list, regex(`[0-9]+`)); - assert(newList == "#22 out of 46"); - import std.array; - string m1 = "first message\n"; - string m2 = "second message\n"; - auto result = appender!string(); - replaceFirstInto(result, m1, regex(`([a-z]+) message`), "$1"); - //equivalent of the above with user-defined callback - replaceFirstInto!(cap=>cap[1])(result, m2, regex(`([a-z]+) message`)); - assert(result.data == "first\nsecond\n"); -} - -/++ - Construct a new string from `input` by replacing all of the - fragments that match a pattern `re` with a string generated - from the match according to the `format` specifier. - - To replace only the first match use $(LREF replaceFirst). - - Params: - input = string to search - re = compiled regular expression to use - format = _format string to generate replacements from, - see $(S_LINK Replace _format string, the _format string). - - Returns: - A string of the same type as `input` with the all - of the matches (if any) replaced. - If no match is found returns the input string itself. -+/ -public @trusted R replaceAll(R, C, RegEx)(R input, RegEx re, const(C)[] format) -if (isSomeString!R && is(C : dchar) && isRegexFor!(RegEx, R)) -{ - return replaceAllWith!((m, sink) => replaceFmt(format, m, sink))(input, re); -} - -/// -@system unittest -{ - // insert comma as thousands delimiter - auto re = regex(r"(?<=\d)(?=(\d\d\d)+\b)","g"); - assert(replaceAll("12000 + 42100 = 54100", re, ",") == "12,000 + 42,100 = 54,100"); -} - -/++ - This is a general replacement tool that construct a new string by replacing - matches of pattern `re` in the `input`. Unlike the other overload - there is no format string instead captures are passed to - to a user-defined functor `fun` that returns a new string - to use as replacement. - - This version replaces all of the matches found in `input`, - see $(LREF replaceFirst) to replace the first match only. - - Returns: - A new string of the same type as `input` with all matches - replaced by return values of `fun`. If no matches found - returns the `input` itself. - - Params: - input = string to search - re = compiled regular expression - fun = delegate to use -+/ -public @trusted R replaceAll(alias fun, R, RegEx)(R input, RegEx re) -if (isSomeString!R && isRegexFor!(RegEx, R)) -{ - return replaceAllWith!((m, sink) => sink.put(fun(m)))(input, re); -} - -/// -@system unittest -{ - string baz(Captures!(string) m) - { - import std.string : toUpper; - return toUpper(m.hit); - } - // Capitalize the letters 'a' and 'r': - auto s = replaceAll!(baz)("Strap a rocket engine on a chicken.", - regex("[ar]")); - assert(s == "StRAp A Rocket engine on A chicken."); -} - -/++ - A variation on $(LREF replaceAll) that instead of allocating a new string - on each call outputs the result piece-wise to the `sink`. In particular - this enables efficient construction of a final output incrementally. - - As with $(LREF replaceAll) there are 2 overloads - one with a format string, - the other one with a user defined functor. -+/ -public @trusted void replaceAllInto(Sink, R, C, RegEx) - (Sink sink, R input, RegEx re, const(C)[] format) -if (isOutputRange!(Sink, dchar) && isSomeString!R - && is(C : dchar) && isRegexFor!(RegEx, R)) - { - replaceMatchesInto!((m, sink) => replaceFmt(format, m, sink)) - (sink, input, matchAll(input, re)); - } - -///ditto -public @trusted void replaceAllInto(alias fun, Sink, R, RegEx) - (Sink sink, R input, RegEx re) -if (isOutputRange!(Sink, dchar) && isSomeString!R && isRegexFor!(RegEx, R)) -{ - replaceMatchesInto!fun(sink, input, matchAll(input, re)); -} - -/// -@system unittest -{ - // insert comma as thousands delimiter in fifty randomly produced big numbers - import std.array, std.conv, std.random, std.range; - static re = regex(`(?<=\d)(?=(\d\d\d)+\b)`, "g"); - auto sink = appender!(char [])(); - enum ulong min = 10UL ^^ 10, max = 10UL ^^ 19; - foreach (i; 0 .. 50) - { - sink.clear(); - replaceAllInto(sink, text(uniform(min, max)), re, ","); - foreach (pos; iota(sink.data.length - 4, 0, -4)) - assert(sink.data[pos] == ','); - } -} - -// exercise all of the replace APIs -@system unittest -{ - import std.array : appender; - import std.conv; - // try and check first/all simple substitution - static foreach (S; AliasSeq!(string, wstring, dstring, char[], wchar[], dchar[])) - {{ - S s1 = "curt trial".to!S(); - S s2 = "round dome".to!S(); - S t1F = "court trial".to!S(); - S t2F = "hound dome".to!S(); - S t1A = "court trial".to!S(); - S t2A = "hound home".to!S(); - auto re1 = regex("curt".to!S()); - auto re2 = regex("[dr]o".to!S()); - - assert(replaceFirst(s1, re1, "court") == t1F); - assert(replaceFirst(s2, re2, "ho") == t2F); - assert(replaceAll(s1, re1, "court") == t1A); - assert(replaceAll(s2, re2, "ho") == t2A); - - auto rep1 = replaceFirst!(cap => cap[0][0]~"o".to!S()~cap[0][1..$])(s1, re1); - assert(rep1 == t1F); - assert(replaceFirst!(cap => "ho".to!S())(s2, re2) == t2F); - auto rep1A = replaceAll!(cap => cap[0][0]~"o".to!S()~cap[0][1..$])(s1, re1); - assert(rep1A == t1A); - assert(replaceAll!(cap => "ho".to!S())(s2, re2) == t2A); - - auto sink = appender!S(); - replaceFirstInto(sink, s1, re1, "court"); - assert(sink.data == t1F); - replaceFirstInto(sink, s2, re2, "ho"); - assert(sink.data == t1F~t2F); - replaceAllInto(sink, s1, re1, "court"); - assert(sink.data == t1F~t2F~t1A); - replaceAllInto(sink, s2, re2, "ho"); - assert(sink.data == t1F~t2F~t1A~t2A); - }} -} - -/++ - Old API for replacement, operation depends on flags of pattern `re`. - With "g" flag it performs the equivalent of $(LREF replaceAll) otherwise it - works the same as $(LREF replaceFirst). - - The use of this function is $(RED discouraged), please use $(LREF replaceAll) - or $(LREF replaceFirst) explicitly. -+/ -public R replace(alias scheme = match, R, C, RegEx)(R input, RegEx re, const(C)[] format) -if (isSomeString!R && isRegexFor!(RegEx, R)) -{ - return replaceAllWith!((m, sink) => replaceFmt(format, m, sink), match)(input, re); -} - -///ditto -public R replace(alias fun, R, RegEx)(R input, RegEx re) -if (isSomeString!R && isRegexFor!(RegEx, R)) -{ - return replaceAllWith!(fun, match)(input, re); -} - -/** -Splits a string `r` using a regular expression `pat` as a separator. - -Params: - keepSeparators = flag to specify if the matches should be in the resulting range - r = the string to split - pat = the pattern to split on -Returns: - A lazy range of strings -*/ -public struct Splitter(Flag!"keepSeparators" keepSeparators = No.keepSeparators, Range, alias RegEx = Regex) -if (isSomeString!Range && isRegexFor!(RegEx, Range)) -{ -private: - Range _input; - size_t _offset; - alias Rx = typeof(match(Range.init,RegEx.init)); - Rx _match; - - static if (keepSeparators) bool onMatch = false; - - @trusted this(Range input, RegEx separator) - {//@@@BUG@@@ generated opAssign of RegexMatch is not @trusted - _input = input; - const re = separator.withFlags(separator.flags | RegexOption.global); - if (_input.empty) - { - //there is nothing to match at all, make _offset > 0 - _offset = 1; - } - else - { - _match = Rx(_input, re); - - static if (keepSeparators) - if (_match.pre.empty) - popFront(); - } - } - -public: - auto ref opSlice() - { - return this.save; - } - - ///Forward range primitives. - @property Range front() - { - import std.algorithm.comparison : min; - - assert(!empty && _offset <= _match.pre.length - && _match.pre.length <= _input.length); - - static if (keepSeparators) - { - if (!onMatch) - return _input[_offset .. min($, _match.pre.length)]; - else - return _match.hit(); - } - else - { - return _input[_offset .. min($, _match.pre.length)]; - } - } - - ///ditto - @property bool empty() - { - static if (keepSeparators) - return _offset >= _input.length; - else - return _offset > _input.length; - } - - ///ditto - void popFront() - { - assert(!empty); - if (_match.empty) - { - //No more separators, work is done here - _offset = _input.length + 1; - } - else - { - static if (keepSeparators) - { - if (!onMatch) - { - //skip past the separator - _offset = _match.pre.length; - } - else - { - _offset += _match.hit.length; - _match.popFront(); - } - - onMatch = !onMatch; - } - else - { - //skip past the separator - _offset = _match.pre.length + _match.hit.length; - _match.popFront(); - } - } - } - - ///ditto - @property auto save() - { - return this; - } -} - -/// ditto -public Splitter!(keepSeparators, Range, RegEx) splitter( - Flag!"keepSeparators" keepSeparators = No.keepSeparators, Range, RegEx)(Range r, RegEx pat) -if ( - is(BasicElementOf!Range : dchar) && isRegexFor!(RegEx, Range)) -{ - return Splitter!(keepSeparators, Range, RegEx)(r, pat); -} - -/// -@system unittest -{ - import std.algorithm.comparison : equal; - auto s1 = ", abc, de, fg, hi, "; - assert(equal(splitter(s1, regex(", *")), - ["", "abc", "de", "fg", "hi", ""])); -} - -/// Split on a pattern, but keep the matches in the resulting range -@system unittest -{ - import std.algorithm.comparison : equal; - import std.typecons : Yes; - - auto pattern = regex(`([\.,])`); - - assert("2003.04.05" - .splitter!(Yes.keepSeparators)(pattern) - .equal(["2003", ".", "04", ".", "05"])); - - assert(",1,2,3" - .splitter!(Yes.keepSeparators)(pattern) - .equal([",", "1", ",", "2", ",", "3"])); -} - -///An eager version of `splitter` that creates an array with splitted slices of `input`. -public @trusted String[] split(String, RegEx)(String input, RegEx rx) -if (isSomeString!String && isRegexFor!(RegEx, String)) -{ - import std.array : appender; - auto a = appender!(String[])(); - foreach (e; splitter(input, rx)) - a.put(e); - return a.data; -} - -///Exception object thrown in case of errors during regex compilation. -public alias RegexException = std.regex.internal.ir.RegexException; - -/++ - A range that lazily produces a string output escaped - to be used inside of a regular expression. -+/ -auto escaper(Range)(Range r) -{ - import std.algorithm.searching : find; - static immutable escapables = [Escapables]; - static struct Escaper // template to deduce attributes - { - Range r; - bool escaped; - - @property ElementType!Range front(){ - if (escaped) - return '\\'; - else - return r.front; - } - - @property bool empty(){ return r.empty; } - - void popFront(){ - if (escaped) escaped = false; - else - { - r.popFront(); - if (!r.empty && !escapables.find(r.front).empty) - escaped = true; - } - } - - @property auto save(){ return Escaper(r.save, escaped); } - } - - bool escaped = !r.empty && !escapables.find(r.front).empty; - return Escaper(r, escaped); -} - -/// -@system unittest -{ - import std.algorithm.comparison; - import std.regex; - string s = `This is {unfriendly} to *regex*`; - assert(s.escaper.equal(`This is \{unfriendly\} to \*regex\*`)); -} - -@system unittest -{ - import std.algorithm.comparison; - import std.conv; - static foreach (S; AliasSeq!(string, wstring, dstring)) - {{ - auto s = "^".to!S; - assert(s.escaper.equal(`\^`)); - auto s2 = ""; - assert(s2.escaper.equal("")); - }} -} - -@system unittest -{ - assert("ab".matchFirst(regex(`a?b?`)).hit == "ab"); - assert("ab".matchFirst(regex(`a??b?`)).hit == ""); -} diff --git a/phobos/std/signals.d b/phobos/std/signals.d deleted file mode 100644 index 97004d5..0000000 --- a/phobos/std/signals.d +++ /dev/null @@ -1,776 +0,0 @@ -// Written in the D programming language. - -/** - * Signals and Slots are an implementation of the Observer Pattern. - * Essentially, when a Signal is emitted, a list of connected Observers - * (called slots) are called. - * - * There have been several D implementations of Signals and Slots. - * This version makes use of several new features in D, which make - * using it simpler and less error prone. In particular, it is no - * longer necessary to instrument the slots. - * - * References: - * $(LUCKY A Deeper Look at Signals and Slots)$(BR) - * $(LINK2 http://en.wikipedia.org/wiki/Observer_pattern, Observer pattern)$(BR) - * $(LINK2 http://en.wikipedia.org/wiki/Signals_and_slots, Wikipedia)$(BR) - * $(LINK2 http://boost.org/doc/html/$(SIGNALS).html, Boost Signals)$(BR) - * $(LINK2 http://qt-project.org/doc/qt-5/signalsandslots.html, Qt)$(BR) - * - * There has been a great deal of discussion in the D newsgroups - * over this, and several implementations: - * - * $(LINK2 http://www.digitalmars.com/d/archives/digitalmars/D/announce/signal_slots_library_4825.html, signal slots library)$(BR) - * $(LINK2 http://www.digitalmars.com/d/archives/digitalmars/D/Signals_and_Slots_in_D_42387.html, Signals and Slots in D)$(BR) - * $(LINK2 http://www.digitalmars.com/d/archives/digitalmars/D/Dynamic_binding_--_Qt_s_Signals_and_Slots_vs_Objective-C_42260.html, Dynamic binding -- Qt's Signals and Slots vs Objective-C)$(BR) - * $(LINK2 http://www.digitalmars.com/d/archives/digitalmars/D/Dissecting_the_SS_42377.html, Dissecting the SS)$(BR) - * $(LINK2 http://www.digitalmars.com/d/archives/digitalmars/D/dwt/about_harmonia_454.html, about harmonia)$(BR) - * $(LINK2 http://www.digitalmars.com/d/archives/digitalmars/D/announce/1502.html, Another event handling module)$(BR) - * $(LINK2 http://www.digitalmars.com/d/archives/digitalmars/D/41825.html, Suggestion: signal/slot mechanism)$(BR) - * $(LINK2 http://www.digitalmars.com/d/archives/digitalmars/D/13251.html, Signals and slots?)$(BR) - * $(LINK2 http://www.digitalmars.com/d/archives/digitalmars/D/10714.html, Signals and slots ready for evaluation)$(BR) - * $(LINK2 http://www.digitalmars.com/d/archives/digitalmars/D/1393.html, Signals & Slots for Walter)$(BR) - * $(LINK2 http://www.digitalmars.com/d/archives/28456.html, Signal/Slot mechanism?)$(BR) - * $(LINK2 http://www.digitalmars.com/d/archives/19470.html, Modern Features?)$(BR) - * $(LINK2 http://www.digitalmars.com/d/archives/16592.html, Delegates vs interfaces)$(BR) - * $(LINK2 http://www.digitalmars.com/d/archives/16583.html, The importance of component programming (properties$(COMMA) signals and slots$(COMMA) etc))$(BR) - * $(LINK2 http://www.digitalmars.com/d/archives/16368.html, signals and slots)$(BR) - * - * Bugs: - * $(RED Slots can only be delegates referring directly to - * class or interface member functions. If a delegate to something else - * is passed to connect(), such as a struct member function, - * a nested function, a COM interface, a closure, undefined behavior - * will result.) - * - * Not safe for multiple threads operating on the same signals - * or slots. - * Macros: - * SIGNALS=signals - * - * Copyright: Copyright The D Language Foundation 2000 - 2009. - * License: $(HTTP www.boost.org/LICENSE_1_0.txt, Boost License 1.0). - * Authors: $(HTTP digitalmars.com, Walter Bright) - * Source: $(PHOBOSSRC std/signals.d) - * - * $(SCRIPT inhibitQuickIndex = 1;) - */ -/* Copyright The D Language Foundation 2000 - 2009. - * 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.signals; - -import core.exception : onOutOfMemoryError; -import core.stdc.stdlib : calloc, realloc, free; -import std.stdio; - -// Special function for internal use only. -// Use of this is where the slot had better be a delegate -// to an object or an interface that is part of an object. -extern (C) Object _d_toObject(void* p); - -// Used in place of Object.notifyRegister and Object.notifyUnRegister. -alias DisposeEvt = void delegate(Object); -extern (C) void rt_attachDisposeEvent( Object obj, DisposeEvt evt ); -extern (C) void rt_detachDisposeEvent( Object obj, DisposeEvt evt ); -//debug=signal; - -/************************ - * Mixin to create a signal within a class object. - * - * Different signals can be added to a class by naming the mixins. - */ - -mixin template Signal(T1...) -{ - static import core.exception; - static import core.stdc.stdlib; - /*** - * A slot is implemented as a delegate. - * The slot_t is the type of the delegate. - * The delegate must be to an instance of a class or an interface - * to a class instance. - * Delegates to struct instances or nested functions must not be - * used as slots. This applies even if the nested function does not access - * it's parent function variables. - */ - alias slot_t = void delegate(T1); - - /*** - * Call each of the connected slots, passing the argument(s) i to them. - * Nested call will be ignored. - */ - final void emit( T1 i ) - { - if (status >= ST.inemitting || !slots.length) - return; // should not nest - - status = ST.inemitting; - scope (exit) - status = ST.idle; - - foreach (slot; slots[0 .. slots_idx]) - { if (slot) - slot(i); - } - - assert(status >= ST.inemitting); - if (status == ST.inemitting_disconnected) - { - for (size_t j = 0; j < slots_idx;) - { - if (slots[j] is null) - { - slots_idx--; - slots[j] = slots[slots_idx]; - } - else - j++; - } - } - } - - /*** - * Add a slot to the list of slots to be called when emit() is called. - */ - final void connect(slot_t slot) - { - /* Do this: - * slots ~= slot; - * but use malloc() and friends instead - */ - auto len = slots.length; - if (slots_idx == len) - { - if (slots.length == 0) - { - len = 4; - auto p = core.stdc.stdlib.calloc(slot_t.sizeof, len); - if (!p) - core.exception.onOutOfMemoryError(); - slots = (cast(slot_t*) p)[0 .. len]; - } - else - { - import core.checkedint : addu, mulu; - bool overflow; - len = addu(mulu(len, 2, overflow), 4, overflow); // len = len * 2 + 4 - const nbytes = mulu(len, slot_t.sizeof, overflow); - if (overflow) assert(0); - - auto p = core.stdc.stdlib.realloc(slots.ptr, nbytes); - if (!p) - core.exception.onOutOfMemoryError(); - slots = (cast(slot_t*) p)[0 .. len]; - slots[slots_idx + 1 .. $] = null; - } - } - slots[slots_idx++] = slot; - - L1: - Object o = _d_toObject(slot.ptr); - rt_attachDisposeEvent(o, &unhook); - } - - /*** - * Remove a slot from the list of slots to be called when emit() is called. - */ - final void disconnect(slot_t slot) - { - debug (signal) writefln("Signal.disconnect(slot)"); - size_t disconnectedSlots = 0; - size_t instancePreviousSlots = 0; - if (status >= ST.inemitting) - { - foreach (i, sloti; slots[0 .. slots_idx]) - { - if (sloti.ptr == slot.ptr && - ++instancePreviousSlots && - sloti == slot) - { - disconnectedSlots++; - slots[i] = null; - status = ST.inemitting_disconnected; - } - } - } - else - { - for (size_t i = 0; i < slots_idx; ) - { - if (slots[i].ptr == slot.ptr && - ++instancePreviousSlots && - slots[i] == slot) - { - slots_idx--; - disconnectedSlots++; - slots[i] = slots[slots_idx]; - slots[slots_idx] = null; // not strictly necessary - } - else - i++; - } - } - - // detach object from dispose event if all its slots have been removed - if (instancePreviousSlots == disconnectedSlots) - { - Object o = _d_toObject(slot.ptr); - rt_detachDisposeEvent(o, &unhook); - } - } - - /*** - * Disconnect all the slots. - */ - final void disconnectAll() - { - debug (signal) writefln("Signal.disconnectAll"); - __dtor(); - slots_idx = 0; - status = ST.idle; - } - - /* ** - * Special function called when o is destroyed. - * It causes any slots dependent on o to be removed from the list - * of slots to be called by emit(). - */ - final void unhook(Object o) - in { assert( status == ST.idle ); } - do - { - debug (signal) writefln("Signal.unhook(o = %s)", cast(void*) o); - for (size_t i = 0; i < slots_idx; ) - { - if (_d_toObject(slots[i].ptr) is o) - { slots_idx--; - slots[i] = slots[slots_idx]; - slots[slots_idx] = null; // not strictly necessary - } - else - i++; - } - } - - /* ** - * There can be multiple destructors inserted by mixins. - */ - ~this() - { - /* ** - * When this object is destroyed, need to let every slot - * know that this object is destroyed so they are not left - * with dangling references to it. - */ - if (slots.length) - { - foreach (slot; slots[0 .. slots_idx]) - { - if (slot) - { Object o = _d_toObject(slot.ptr); - rt_detachDisposeEvent(o, &unhook); - } - } - core.stdc.stdlib.free(slots.ptr); - slots = null; - } - } - - private: - slot_t[] slots; // the slots to call from emit() - size_t slots_idx; // used length of slots[] - - enum ST { idle, inemitting, inemitting_disconnected } - ST status; -} - -/// -@system unittest -{ - import std.signals; - - int observedMessageCounter = 0; - - class Observer - { // our slot - void watch(string msg, int value) - { - switch (observedMessageCounter++) - { - case 0: - assert(msg == "setting new value"); - assert(value == 4); - break; - case 1: - assert(msg == "setting new value"); - assert(value == 6); - break; - default: - assert(0, "Unknown observation"); - } - } - } - - class Observer2 - { // our slot - void watch(string msg, int value) - { - } - } - - class Foo - { - int value() { return _value; } - - int value(int v) - { - if (v != _value) - { _value = v; - // call all the connected slots with the two parameters - emit("setting new value", v); - } - return v; - } - - // Mix in all the code we need to make Foo into a signal - mixin Signal!(string, int); - - private : - int _value; - } - - Foo a = new Foo; - Observer o = new Observer; - auto o2 = new Observer2; - auto o3 = new Observer2; - auto o4 = new Observer2; - auto o5 = new Observer2; - - a.value = 3; // should not call o.watch() - a.connect(&o.watch); // o.watch is the slot - a.connect(&o2.watch); - a.connect(&o3.watch); - a.connect(&o4.watch); - a.connect(&o5.watch); - a.value = 4; // should call o.watch() - a.disconnect(&o.watch); // o.watch is no longer a slot - a.disconnect(&o3.watch); - a.disconnect(&o5.watch); - a.disconnect(&o4.watch); - a.disconnect(&o2.watch); - a.value = 5; // so should not call o.watch() - a.connect(&o2.watch); - a.connect(&o.watch); // connect again - a.value = 6; // should call o.watch() - destroy(o); // destroying o should automatically disconnect it - a.value = 7; // should not call o.watch() - - assert(observedMessageCounter == 2); -} - -// A function whose sole purpose is to get this module linked in -// so the unittest will run. -void linkin() { } - -@system unittest -{ - class Observer - { - void watch(string msg, int i) - { - //writefln("Observed msg '%s' and value %s", msg, i); - captured_value = i; - captured_msg = msg; - } - - int captured_value; - string captured_msg; - } - - class Foo - { - @property int value() { return _value; } - - @property int value(int v) - { - if (v != _value) - { _value = v; - emit("setting new value", v); - } - return v; - } - - mixin Signal!(string, int); - - private: - int _value; - } - - Foo a = new Foo; - Observer o = new Observer; - - // check initial condition - assert(o.captured_value == 0); - assert(o.captured_msg == ""); - - // set a value while no observation is in place - a.value = 3; - assert(o.captured_value == 0); - assert(o.captured_msg == ""); - - // connect the watcher and trigger it - a.connect(&o.watch); - a.value = 4; - assert(o.captured_value == 4); - assert(o.captured_msg == "setting new value"); - - // disconnect the watcher and make sure it doesn't trigger - a.disconnect(&o.watch); - a.value = 5; - assert(o.captured_value == 4); - assert(o.captured_msg == "setting new value"); - - // reconnect the watcher and make sure it triggers - a.connect(&o.watch); - a.value = 6; - assert(o.captured_value == 6); - assert(o.captured_msg == "setting new value"); - - // destroy the underlying object and make sure it doesn't cause - // a crash or other problems - destroy(o); - a.value = 7; -} - -@system unittest -{ - class Observer - { - int i; - long l; - string str; - - void watchInt(string str, int i) - { - this.str = str; - this.i = i; - } - - void watchLong(string str, long l) - { - this.str = str; - this.l = l; - } - } - - class Bar - { - @property void value1(int v) { s1.emit("str1", v); } - @property void value2(int v) { s2.emit("str2", v); } - @property void value3(long v) { s3.emit("str3", v); } - - mixin Signal!(string, int) s1; - mixin Signal!(string, int) s2; - mixin Signal!(string, long) s3; - } - - void test(T)(T a) { - auto o1 = new Observer; - auto o2 = new Observer; - auto o3 = new Observer; - - // connect the watcher and trigger it - a.s1.connect(&o1.watchInt); - a.s2.connect(&o2.watchInt); - a.s3.connect(&o3.watchLong); - - assert(!o1.i && !o1.l && o1.str == null); - assert(!o2.i && !o2.l && o2.str == null); - assert(!o3.i && !o3.l && o3.str == null); - - a.value1 = 11; - assert(o1.i == 11 && !o1.l && o1.str == "str1"); - assert(!o2.i && !o2.l && o2.str == null); - assert(!o3.i && !o3.l && o3.str == null); - o1.i = -11; o1.str = "x1"; - - a.value2 = 12; - assert(o1.i == -11 && !o1.l && o1.str == "x1"); - assert(o2.i == 12 && !o2.l && o2.str == "str2"); - assert(!o3.i && !o3.l && o3.str == null); - o2.i = -12; o2.str = "x2"; - - a.value3 = 13; - assert(o1.i == -11 && !o1.l && o1.str == "x1"); - assert(o2.i == -12 && !o1.l && o2.str == "x2"); - assert(!o3.i && o3.l == 13 && o3.str == "str3"); - o3.l = -13; o3.str = "x3"; - - // disconnect the watchers and make sure it doesn't trigger - a.s1.disconnect(&o1.watchInt); - a.s2.disconnect(&o2.watchInt); - a.s3.disconnect(&o3.watchLong); - - a.value1 = 21; - a.value2 = 22; - a.value3 = 23; - assert(o1.i == -11 && !o1.l && o1.str == "x1"); - assert(o2.i == -12 && !o1.l && o2.str == "x2"); - assert(!o3.i && o3.l == -13 && o3.str == "x3"); - - // reconnect the watcher and make sure it triggers - a.s1.connect(&o1.watchInt); - a.s2.connect(&o2.watchInt); - a.s3.connect(&o3.watchLong); - - a.value1 = 31; - a.value2 = 32; - a.value3 = 33; - assert(o1.i == 31 && !o1.l && o1.str == "str1"); - assert(o2.i == 32 && !o1.l && o2.str == "str2"); - assert(!o3.i && o3.l == 33 && o3.str == "str3"); - - // destroy observers - destroy(o1); - destroy(o2); - destroy(o3); - a.value1 = 41; - a.value2 = 42; - a.value3 = 43; - } - - test(new Bar); - - class BarDerived: Bar - { - @property void value4(int v) { s4.emit("str4", v); } - @property void value5(int v) { s5.emit("str5", v); } - @property void value6(long v) { s6.emit("str6", v); } - - mixin Signal!(string, int) s4; - mixin Signal!(string, int) s5; - mixin Signal!(string, long) s6; - } - - auto a = new BarDerived; - - test!Bar(a); - test!BarDerived(a); - - auto o4 = new Observer; - auto o5 = new Observer; - auto o6 = new Observer; - - // connect the watcher and trigger it - a.s4.connect(&o4.watchInt); - a.s5.connect(&o5.watchInt); - a.s6.connect(&o6.watchLong); - - assert(!o4.i && !o4.l && o4.str == null); - assert(!o5.i && !o5.l && o5.str == null); - assert(!o6.i && !o6.l && o6.str == null); - - a.value4 = 44; - assert(o4.i == 44 && !o4.l && o4.str == "str4"); - assert(!o5.i && !o5.l && o5.str == null); - assert(!o6.i && !o6.l && o6.str == null); - o4.i = -44; o4.str = "x4"; - - a.value5 = 45; - assert(o4.i == -44 && !o4.l && o4.str == "x4"); - assert(o5.i == 45 && !o5.l && o5.str == "str5"); - assert(!o6.i && !o6.l && o6.str == null); - o5.i = -45; o5.str = "x5"; - - a.value6 = 46; - assert(o4.i == -44 && !o4.l && o4.str == "x4"); - assert(o5.i == -45 && !o4.l && o5.str == "x5"); - assert(!o6.i && o6.l == 46 && o6.str == "str6"); - o6.l = -46; o6.str = "x6"; - - // disconnect the watchers and make sure it doesn't trigger - a.s4.disconnect(&o4.watchInt); - a.s5.disconnect(&o5.watchInt); - a.s6.disconnect(&o6.watchLong); - - a.value4 = 54; - a.value5 = 55; - a.value6 = 56; - assert(o4.i == -44 && !o4.l && o4.str == "x4"); - assert(o5.i == -45 && !o4.l && o5.str == "x5"); - assert(!o6.i && o6.l == -46 && o6.str == "x6"); - - // reconnect the watcher and make sure it triggers - a.s4.connect(&o4.watchInt); - a.s5.connect(&o5.watchInt); - a.s6.connect(&o6.watchLong); - - a.value4 = 64; - a.value5 = 65; - a.value6 = 66; - assert(o4.i == 64 && !o4.l && o4.str == "str4"); - assert(o5.i == 65 && !o4.l && o5.str == "str5"); - assert(!o6.i && o6.l == 66 && o6.str == "str6"); - - // destroy observers - destroy(o4); - destroy(o5); - destroy(o6); - a.value4 = 44; - a.value5 = 45; - a.value6 = 46; -} - -// Triggers bug from https://issues.dlang.org/show_bug.cgi?id=15341 -@system unittest -{ - class Observer - { - void watch() { } - void watch2() { } - } - - class Bar - { - mixin Signal!(); - } - - auto a = new Bar; - auto o = new Observer; - - //Connect both observer methods for the same instance - a.connect(&o.watch); - a.connect(&o.watch2); // not connecting watch2() or disconnecting it manually fixes the issue - - //Disconnect a single method of the two - a.disconnect(&o.watch); // NOT disconnecting watch() fixes the issue - - destroy(o); // destroying o should automatically call unhook and disconnect the slot for watch2 - a.emit(); // should not raise segfault since &o.watch2 is no longer connected -} - -version (none) // Disabled because of https://issues.dlang.org/show_bug.cgi?id=5028 -@system unittest -{ - class A - { - mixin Signal!(string, int) s1; - } - - class B : A - { - mixin Signal!(string, int) s2; - } -} - -// Triggers bug from https://issues.dlang.org/show_bug.cgi?id=16249 -@system unittest -{ - class myLINE - { - mixin Signal!( myLINE, int ); - - void value( int v ) - { - if ( v >= 0 ) emit( this, v ); - else emit( new myLINE, v ); - } - } - - class Dot - { - int value; - - myLINE line_; - void line( myLINE line_x ) - { - if ( line_ is line_x ) return; - - if ( line_ !is null ) - { - line_.disconnect( &watch ); - } - line_ = line_x; - line_.connect( &watch ); - } - - void watch( myLINE line_x, int value_x ) - { - line = line_x; - value = value_x; - } - } - - auto dot1 = new Dot; - auto dot2 = new Dot; - auto line = new myLINE; - dot1.line = line; - dot2.line = line; - - line.value = 11; - assert( dot1.value == 11 ); - assert( dot2.value == 11 ); - - line.value = -22; - assert( dot1.value == -22 ); - assert( dot2.value == -22 ); -} - -@system unittest -{ - import std.signals; - - class Observer - { // our slot - void watch(string msg, int value) - { - if (value != 0) - { - assert(msg == "setting new value"); - assert(value == 1); - } - } - } - - class Foo - { - int value() { return _value; } - - int value(int v) - { - if (v != _value) - { - _value = v; - // call all the connected slots with the parameters - emit("setting new value", v); - } - return v; - } - - // Mix in all the code we need to make Foo into a signal - mixin Signal!(string, int); - - private : - int _value; - } - - Foo a = new Foo; - Observer o = new Observer; - auto o2 = new Observer; - - a.value = 3; // should not call o.watch() - a.connect(&o.watch); // o.watch is the slot - a.connect(&o2.watch); - a.value = 1; // should call o.watch() - a.disconnectAll(); - a.value = 5; // so should not call o.watch() - a.connect(&o.watch); // connect again - a.connect(&o2.watch); - a.value = 1; // should call o.watch() - destroy(o); // destroying o should automatically disconnect it - destroy(o2); - a.value = 7; // should not call o.watch() -} diff --git a/phobos/std/socket.d b/phobos/std/socket.d deleted file mode 100644 index e86a51f..0000000 --- a/phobos/std/socket.d +++ /dev/null @@ -1,3826 +0,0 @@ -// Written in the D programming language - -// NOTE: When working on this module, be sure to run tests with -debug=std_socket -// E.g.: dmd -version=StdUnittest -debug=std_socket -unittest -main -run socket -// This will enable some tests which are too slow or flaky to run as part of CI. - -/* - Copyright (C) 2004-2011 Christopher E. Miller - - socket.d 1.4 - Jan 2011 - - Thanks to Benjamin Herr for his assistance. - */ - -/** - * Socket primitives. - * Example: See $(SAMPLESRC listener.d) and $(SAMPLESRC htmlget.d) - * License: $(HTTP www.boost.org/LICENSE_1_0.txt, Boost License 1.0). - * Authors: Christopher E. Miller, $(HTTP klickverbot.at, David Nadlinger), - * $(HTTP thecybershadow.net, Vladimir Panteleev) - * Source: $(PHOBOSSRC std/socket.d) - */ - -module std.socket; - -import core.stdc.stdint, core.stdc.stdlib, core.stdc.string, std.conv, std.string; - -import core.stdc.config; -import core.time : dur, Duration; -import std.exception; - -import std.internal.cstring; - -version (iOS) - version = iOSDerived; -else version (TVOS) - version = iOSDerived; -else version (WatchOS) - version = iOSDerived; - -@safe: - -version (Windows) -{ - pragma (lib, "ws2_32.lib"); - pragma (lib, "wsock32.lib"); - - import core.sys.windows.winbase, std.windows.syserror; - public import core.sys.windows.winsock2; - private alias _ctimeval = core.sys.windows.winsock2.timeval; - private alias _clinger = core.sys.windows.winsock2.linger; - - enum socket_t : SOCKET { INVALID_SOCKET } - private const int _SOCKET_ERROR = SOCKET_ERROR; - - - private int _lasterr() nothrow @nogc - { - return WSAGetLastError(); - } -} -else version (Posix) -{ - version (linux) - { - enum : int - { - TCP_KEEPIDLE = 4, - TCP_KEEPINTVL = 5 - } - } - - public import core.sys.posix.netinet.in_; - import core.sys.posix.arpa.inet; - import core.sys.posix.fcntl; - import core.sys.posix.netdb; - import core.sys.posix.netinet.tcp; - import core.sys.posix.sys.select; - import core.sys.posix.sys.socket; - import core.sys.posix.sys.time; - import core.sys.posix.sys.un : sockaddr_un; - import core.sys.posix.unistd; - private alias _ctimeval = core.sys.posix.sys.time.timeval; - private alias _clinger = core.sys.posix.sys.socket.linger; - - import core.stdc.errno; - - enum socket_t : int32_t { _init = -1 } - private const int _SOCKET_ERROR = -1; - - private enum : int - { - SD_RECEIVE = SHUT_RD, - SD_SEND = SHUT_WR, - SD_BOTH = SHUT_RDWR - } - - private int _lasterr() nothrow @nogc - { - return errno; - } -} -else -{ - static assert(0, "No socket support for this platform yet."); -} - -version (StdUnittest) -{ - // Print a message on exception instead of failing the unittest. - private void softUnittest(void delegate() @safe test, int line = __LINE__) @trusted - { - debug (std_socket) - test(); - else - { - import std.stdio : writefln; - try - test(); - catch (Throwable e) - writefln("Ignoring std.socket(%d) test failure (likely caused by flaky environment): %s", line, e.msg); - } - } - - // Without debug=std_socket, still compile the slow tests, just don't run them. - debug (std_socket) - private enum runSlowTests = true; - else - private enum runSlowTests = false; -} - -/// Base exception thrown by `std.socket`. -class SocketException: Exception -{ - mixin basicExceptionCtors; -} - -version (CRuntime_Glibc) version = GNU_STRERROR; -version (CRuntime_UClibc) version = GNU_STRERROR; - -/* - * Needs to be public so that SocketOSException can be thrown outside of - * std.socket (since it uses it as a default argument), but it probably doesn't - * need to actually show up in the docs, since there's not really any public - * need for it outside of being a default argument. - */ -string formatSocketError(int err) @trusted -{ - version (Posix) - { - char[80] buf; - const(char)* cs; - version (GNU_STRERROR) - { - cs = strerror_r(err, buf.ptr, buf.length); - } - else - { - auto errs = strerror_r(err, buf.ptr, buf.length); - if (errs == 0) - cs = buf.ptr; - else - return "Socket error " ~ to!string(err); - } - - auto len = strlen(cs); - - if (cs[len - 1] == '\n') - len--; - if (cs[len - 1] == '\r') - len--; - return cs[0 .. len].idup; - } - else - version (Windows) - { - return generateSysErrorMsg(err); - } - else - return "Socket error " ~ to!string(err); -} - -/// Returns the error message of the most recently encountered network error. -@property string lastSocketError() -{ - return formatSocketError(_lasterr()); -} - -/// Socket exception representing network errors reported by the operating system. -class SocketOSException: SocketException -{ - int errorCode; /// Platform-specific error code. - - /// - this(string msg, - string file = __FILE__, - size_t line = __LINE__, - Throwable next = null, - int err = _lasterr(), - string function(int) @trusted errorFormatter = &formatSocketError) - { - errorCode = err; - - if (msg.length) - super(msg ~ ": " ~ errorFormatter(err), file, line, next); - else - super(errorFormatter(err), file, line, next); - } - - /// - this(string msg, - Throwable next, - string file = __FILE__, - size_t line = __LINE__, - int err = _lasterr(), - string function(int) @trusted errorFormatter = &formatSocketError) - { - this(msg, file, line, next, err, errorFormatter); - } - - /// - this(string msg, - int err, - string function(int) @trusted errorFormatter = &formatSocketError, - string file = __FILE__, - size_t line = __LINE__, - Throwable next = null) - { - this(msg, file, line, next, err, errorFormatter); - } -} - -/// Socket exception representing invalid parameters specified by user code. -class SocketParameterException: SocketException -{ - mixin basicExceptionCtors; -} - -/** - * Socket exception representing attempts to use network capabilities not - * available on the current system. - */ -class SocketFeatureException: SocketException -{ - mixin basicExceptionCtors; -} - - -/** - * Returns: - * `true` if the last socket operation failed because the socket - * was in non-blocking mode and the operation would have blocked, - * or if the socket is in blocking mode and set a `SNDTIMEO` or `RCVTIMEO`, - * and the operation timed out. - */ -bool wouldHaveBlocked() nothrow @nogc -{ - version (Windows) - return _lasterr() == WSAEWOULDBLOCK || _lasterr() == WSAETIMEDOUT; - else version (Posix) - return _lasterr() == EAGAIN; - else - static assert(0, "No socket support for this platform yet."); -} - -@safe unittest -{ - auto sockets = socketPair(); - auto s = sockets[0]; - s.setOption(SocketOptionLevel.SOCKET, SocketOption.RCVTIMEO, dur!"msecs"(10)); - ubyte[] buffer = new ubyte[](16); - auto rec = s.receive(buffer); - assert(rec == -1 && wouldHaveBlocked()); -} - - -private immutable -{ - typeof(&getnameinfo) getnameinfoPointer; - typeof(&getaddrinfo) getaddrinfoPointer; - typeof(&freeaddrinfo) freeaddrinfoPointer; -} - -shared static this() @system -{ - version (Windows) - { - WSADATA wd; - - // Winsock will still load if an older version is present. - // The version is just a request. - int val; - val = WSAStartup(0x2020, &wd); - if (val) // Request Winsock 2.2 for IPv6. - throw new SocketOSException("Unable to initialize socket library", val); - - // These functions may not be present on older Windows versions. - // See the comment in InternetAddress.toHostNameString() for details. - auto ws2Lib = GetModuleHandleA("ws2_32.dll"); - if (ws2Lib) - { - getnameinfoPointer = cast(typeof(getnameinfoPointer)) - GetProcAddress(ws2Lib, "getnameinfo"); - getaddrinfoPointer = cast(typeof(getaddrinfoPointer)) - GetProcAddress(ws2Lib, "getaddrinfo"); - freeaddrinfoPointer = cast(typeof(freeaddrinfoPointer)) - GetProcAddress(ws2Lib, "freeaddrinfo"); - } - } - else version (Posix) - { - getnameinfoPointer = &getnameinfo; - getaddrinfoPointer = &getaddrinfo; - freeaddrinfoPointer = &freeaddrinfo; - } -} - - -shared static ~this() @system nothrow @nogc -{ - version (Windows) - { - WSACleanup(); - } -} - -/** - * The communication domain used to resolve an address. - */ -enum AddressFamily: ushort -{ - UNSPEC = AF_UNSPEC, /// Unspecified address family - UNIX = AF_UNIX, /// Local communication (Unix socket) - INET = AF_INET, /// Internet Protocol version 4 - IPX = AF_IPX, /// Novell IPX - APPLETALK = AF_APPLETALK, /// AppleTalk - INET6 = AF_INET6, /// Internet Protocol version 6 -} - - -/** - * Communication semantics - */ -enum SocketType: int -{ - STREAM = SOCK_STREAM, /// Sequenced, reliable, two-way communication-based byte streams - DGRAM = SOCK_DGRAM, /// Connectionless, unreliable datagrams with a fixed maximum length; data may be lost or arrive out of order - RAW = SOCK_RAW, /// Raw protocol access - RDM = SOCK_RDM, /// Reliably-delivered message datagrams - SEQPACKET = SOCK_SEQPACKET, /// Sequenced, reliable, two-way connection-based datagrams with a fixed maximum length -} - - -/** - * Protocol - */ -enum ProtocolType: int -{ - IP = IPPROTO_IP, /// Internet Protocol version 4 - ICMP = IPPROTO_ICMP, /// Internet Control Message Protocol - IGMP = IPPROTO_IGMP, /// Internet Group Management Protocol - GGP = IPPROTO_GGP, /// Gateway to Gateway Protocol - TCP = IPPROTO_TCP, /// Transmission Control Protocol - PUP = IPPROTO_PUP, /// PARC Universal Packet Protocol - UDP = IPPROTO_UDP, /// User Datagram Protocol - IDP = IPPROTO_IDP, /// Xerox NS protocol - RAW = IPPROTO_RAW, /// Raw IP packets - IPV6 = IPPROTO_IPV6, /// Internet Protocol version 6 -} - - -/** - * Class for retrieving protocol information. - * - * Example: - * --- - * auto proto = new Protocol; - * writeln("About protocol TCP:"); - * if (proto.getProtocolByType(ProtocolType.TCP)) - * { - * writefln(" Name: %s", proto.name); - * foreach (string s; proto.aliases) - * writefln(" Alias: %s", s); - * } - * else - * writeln(" No information found"); - * --- - */ -class Protocol -{ - /// These members are populated when one of the following functions are called successfully: - ProtocolType type; - string name; /// ditto - string[] aliases; /// ditto - - - void populate(protoent* proto) @system pure nothrow - { - type = cast(ProtocolType) proto.p_proto; - name = to!string(proto.p_name); - - int i; - for (i = 0;; i++) - { - if (!proto.p_aliases[i]) - break; - } - - if (i) - { - aliases = new string[i]; - for (i = 0; i != aliases.length; i++) - { - aliases[i] = - to!string(proto.p_aliases[i]); - } - } - else - { - aliases = null; - } - } - - /** Returns: false on failure */ - bool getProtocolByName(scope const(char)[] name) @trusted nothrow - { - protoent* proto; - proto = getprotobyname(name.tempCString()); - if (!proto) - return false; - populate(proto); - return true; - } - - - /** Returns: false on failure */ - // Same as getprotobynumber(). - bool getProtocolByType(ProtocolType type) @trusted nothrow - { - protoent* proto; - proto = getprotobynumber(type); - if (!proto) - return false; - populate(proto); - return true; - } -} - - -// Skip this test on Android because getprotobyname/number are -// unimplemented in bionic. -version (CRuntime_Bionic) {} else -@safe unittest -{ - // import std.stdio : writefln; - softUnittest({ - Protocol proto = new Protocol; - assert(proto.getProtocolByType(ProtocolType.TCP)); - //writeln("About protocol TCP:"); - //writefln("\tName: %s", proto.name); - // foreach (string s; proto.aliases) - // { - // writefln("\tAlias: %s", s); - // } - assert(proto.name == "tcp"); - assert(proto.aliases.length == 1 && proto.aliases[0] == "TCP"); - }); -} - - -/** - * Class for retrieving service information. - * - * Example: - * --- - * auto serv = new Service; - * writeln("About service epmap:"); - * if (serv.getServiceByName("epmap", "tcp")) - * { - * writefln(" Service: %s", serv.name); - * writefln(" Port: %d", serv.port); - * writefln(" Protocol: %s", serv.protocolName); - * foreach (string s; serv.aliases) - * writefln(" Alias: %s", s); - * } - * else - * writefln(" No service for epmap."); - * --- - */ -class Service -{ - /// These members are populated when one of the following functions are called successfully: - string name; - string[] aliases; /// ditto - ushort port; /// ditto - string protocolName; /// ditto - - - void populate(servent* serv) @system pure nothrow - { - name = to!string(serv.s_name); - port = ntohs(cast(ushort) serv.s_port); - protocolName = to!string(serv.s_proto); - - int i; - for (i = 0;; i++) - { - if (!serv.s_aliases[i]) - break; - } - - if (i) - { - aliases = new string[i]; - for (i = 0; i != aliases.length; i++) - { - aliases[i] = - to!string(serv.s_aliases[i]); - } - } - else - { - aliases = null; - } - } - - /** - * If a protocol name is omitted, any protocol will be matched. - * Returns: false on failure. - */ - bool getServiceByName(scope const(char)[] name, scope const(char)[] protocolName = null) @trusted nothrow - { - servent* serv; - serv = getservbyname(name.tempCString(), protocolName.tempCString()); - if (!serv) - return false; - populate(serv); - return true; - } - - - /// ditto - bool getServiceByPort(ushort port, scope const(char)[] protocolName = null) @trusted nothrow - { - servent* serv; - serv = getservbyport(port, protocolName.tempCString()); - if (!serv) - return false; - populate(serv); - return true; - } -} - - -@safe unittest -{ - import std.stdio : writefln; - softUnittest({ - Service serv = new Service; - if (serv.getServiceByName("epmap", "tcp")) - { - // writefln("About service epmap:"); - // writefln("\tService: %s", serv.name); - // writefln("\tPort: %d", serv.port); - // writefln("\tProtocol: %s", serv.protocolName); - // foreach (string s; serv.aliases) - // { - // writefln("\tAlias: %s", s); - // } - // For reasons unknown this is loc-srv on Wine and epmap on Windows - assert(serv.name == "loc-srv" || serv.name == "epmap", serv.name); - assert(serv.port == 135); - assert(serv.protocolName == "tcp"); - } - else - { - writefln("No service for epmap."); - } - }); -} - - -private mixin template socketOSExceptionCtors() -{ - /// - this(string msg, string file = __FILE__, size_t line = __LINE__, - Throwable next = null, int err = _lasterr()) - { - super(msg, file, line, next, err); - } - - /// - this(string msg, Throwable next, string file = __FILE__, - size_t line = __LINE__, int err = _lasterr()) - { - super(msg, next, file, line, err); - } - - /// - this(string msg, int err, string file = __FILE__, size_t line = __LINE__, - Throwable next = null) - { - super(msg, next, file, line, err); - } -} - - -/** - * Class for exceptions thrown from an `InternetHost`. - */ -class HostException: SocketOSException -{ - mixin socketOSExceptionCtors; -} - -/** - * Class for resolving IPv4 addresses. - * - * Consider using `getAddress`, `parseAddress` and `Address` methods - * instead of using this class directly. - */ -class InternetHost -{ - /// These members are populated when one of the following functions are called successfully: - string name; - string[] aliases; /// ditto - uint[] addrList; /// ditto - - - void validHostent(in hostent* he) - { - if (he.h_addrtype != cast(int) AddressFamily.INET || he.h_length != 4) - throw new HostException("Address family mismatch"); - } - - - void populate(hostent* he) @system pure nothrow - { - int i; - char* p; - - name = to!string(he.h_name); - - for (i = 0;; i++) - { - p = he.h_aliases[i]; - if (!p) - break; - } - - if (i) - { - aliases = new string[i]; - for (i = 0; i != aliases.length; i++) - { - aliases[i] = - to!string(he.h_aliases[i]); - } - } - else - { - aliases = null; - } - - for (i = 0;; i++) - { - p = he.h_addr_list[i]; - if (!p) - break; - } - - if (i) - { - addrList = new uint[i]; - for (i = 0; i != addrList.length; i++) - { - addrList[i] = ntohl(*(cast(uint*) he.h_addr_list[i])); - } - } - else - { - addrList = null; - } - } - - private bool getHostNoSync(string opMixin, T)(T param) @system - { - mixin(opMixin); - if (!he) - return false; - validHostent(he); - populate(he); - return true; - } - - version (Windows) - alias getHost = getHostNoSync; - else - { - // posix systems use global state for return value, so we - // must synchronize across all threads - private bool getHost(string opMixin, T)(T param) @system - { - synchronized(this.classinfo) - return getHostNoSync!(opMixin, T)(param); - } - } - - /** - * Resolve host name. - * Returns: false if unable to resolve. - */ - bool getHostByName(scope const(char)[] name) @trusted - { - static if (is(typeof(gethostbyname_r))) - { - return getHostNoSync!q{ - hostent he_v; - hostent* he; - ubyte[256] buffer_v = void; - auto buffer = buffer_v[]; - auto param_zTmp = param.tempCString(); - while (true) - { - he = &he_v; - int errno; - if (gethostbyname_r(param_zTmp, he, buffer.ptr, buffer.length, &he, &errno) == ERANGE) - buffer.length = buffer.length * 2; - else - break; - } - }(name); - } - else - { - return getHost!q{ - auto he = gethostbyname(param.tempCString()); - }(name); - } - } - - /** - * Resolve IPv4 address number. - * - * Params: - * addr = The IPv4 address to resolve, in host byte order. - * Returns: - * false if unable to resolve. - */ - bool getHostByAddr(uint addr) @trusted - { - return getHost!q{ - auto x = htonl(param); - auto he = gethostbyaddr(&x, 4, cast(int) AddressFamily.INET); - }(addr); - } - - /** - * Same as previous, but addr is an IPv4 address string in the - * dotted-decimal form $(I a.b.c.d). - * Returns: false if unable to resolve. - */ - bool getHostByAddr(scope const(char)[] addr) @trusted - { - return getHost!q{ - auto x = inet_addr(param.tempCString()); - enforce(x != INADDR_NONE, - new SocketParameterException("Invalid IPv4 address")); - auto he = gethostbyaddr(&x, 4, cast(int) AddressFamily.INET); - }(addr); - } -} - -/// -@safe unittest -{ - InternetHost ih = new InternetHost; - - ih.getHostByAddr(0x7F_00_00_01); - assert(ih.addrList[0] == 0x7F_00_00_01); - ih.getHostByAddr("127.0.0.1"); - assert(ih.addrList[0] == 0x7F_00_00_01); - - if (!ih.getHostByName("www.digitalmars.com")) - return; // don't fail if not connected to internet - - assert(ih.addrList.length); - InternetAddress ia = new InternetAddress(ih.addrList[0], InternetAddress.PORT_ANY); - assert(ih.name == "www.digitalmars.com" || ih.name == "digitalmars.com", - ih.name); - - /* The following assert randomly fails in the test suite. - * https://issues.dlang.org/show_bug.cgi?id=22791 - * So just ignore it when it fails. - */ - //assert(ih.getHostByAddr(ih.addrList[0])); - if (ih.getHostByAddr(ih.addrList[0])) - { - string getHostNameFromInt = ih.name.dup; - - // This randomly fails in the compiler test suite - //assert(ih.getHostByAddr(ia.toAddrString())); - - if (ih.getHostByAddr(ia.toAddrString())) - { - string getHostNameFromStr = ih.name.dup; - assert(getHostNameFromInt == getHostNameFromStr); - } - } -} - - -/// Holds information about a socket _address retrieved by `getAddressInfo`. -struct AddressInfo -{ - AddressFamily family; /// Address _family - SocketType type; /// Socket _type - ProtocolType protocol; /// Protocol - Address address; /// Socket _address - string canonicalName; /// Canonical name, when `AddressInfoFlags.CANONNAME` is used. -} - -/** - * A subset of flags supported on all platforms with getaddrinfo. - * Specifies option flags for `getAddressInfo`. - */ -enum AddressInfoFlags: int -{ - /// The resulting addresses will be used in a call to `Socket.bind`. - PASSIVE = AI_PASSIVE, - - /// The canonical name is returned in `canonicalName` member in the first `AddressInfo`. - CANONNAME = AI_CANONNAME, - - /** - * The `node` parameter passed to `getAddressInfo` must be a numeric string. - * This will suppress any potentially lengthy network host address lookups. - */ - NUMERICHOST = AI_NUMERICHOST, -} - - -/** - * On POSIX, getaddrinfo uses its own error codes, and thus has its own - * formatting function. - */ -private string formatGaiError(int err) @trusted -{ - version (Windows) - { - return generateSysErrorMsg(err); - } - else - { - synchronized - return to!string(gai_strerror(err)); - } -} - -/** - * Provides _protocol-independent translation from host names to socket - * addresses. If advanced functionality is not required, consider using - * `getAddress` for compatibility with older systems. - * - * Returns: Array with one `AddressInfo` per socket address. - * - * Throws: `SocketOSException` on failure, or `SocketFeatureException` - * if this functionality is not available on the current system. - * - * Params: - * node = string containing host name or numeric address - * options = optional additional parameters, identified by type: - * $(UL $(LI `string` - service name or port number) - * $(LI `AddressInfoFlags` - option flags) - * $(LI `AddressFamily` - address family to filter by) - * $(LI `SocketType` - socket type to filter by) - * $(LI `ProtocolType` - protocol to filter by)) - * - * Example: - * --- - * // Roundtrip DNS resolution - * auto results = getAddressInfo("www.digitalmars.com"); - * assert(results[0].address.toHostNameString() == - * "digitalmars.com"); - * - * // Canonical name - * results = getAddressInfo("www.digitalmars.com", - * AddressInfoFlags.CANONNAME); - * assert(results[0].canonicalName == "digitalmars.com"); - * - * // IPv6 resolution - * results = getAddressInfo("ipv6.google.com"); - * assert(results[0].family == AddressFamily.INET6); - * - * // Multihomed resolution - * results = getAddressInfo("google.com"); - * assert(results.length > 1); - * - * // Parsing IPv4 - * results = getAddressInfo("127.0.0.1", - * AddressInfoFlags.NUMERICHOST); - * assert(results.length && results[0].family == - * AddressFamily.INET); - * - * // Parsing IPv6 - * results = getAddressInfo("::1", - * AddressInfoFlags.NUMERICHOST); - * assert(results.length && results[0].family == - * AddressFamily.INET6); - * --- - */ -AddressInfo[] getAddressInfo(T...)(scope const(char)[] node, scope T options) -{ - const(char)[] service = null; - addrinfo hints; - hints.ai_family = AF_UNSPEC; - - foreach (i, option; options) - { - static if (is(typeof(option) : const(char)[])) - service = options[i]; - else - static if (is(typeof(option) == AddressInfoFlags)) - hints.ai_flags |= option; - else - static if (is(typeof(option) == AddressFamily)) - hints.ai_family = option; - else - static if (is(typeof(option) == SocketType)) - hints.ai_socktype = option; - else - static if (is(typeof(option) == ProtocolType)) - hints.ai_protocol = option; - else - static assert(0, "Unknown getAddressInfo option type: " ~ typeof(option).stringof); - } - - return () @trusted { return getAddressInfoImpl(node, service, &hints); }(); -} - -@system unittest -{ - struct Oops - { - const(char[]) breakSafety() - { - *cast(int*) 0xcafebabe = 0xdeadbeef; - return null; - } - alias breakSafety this; - } - assert(!__traits(compiles, () { - getAddressInfo("", Oops.init); - }), "getAddressInfo breaks @safe"); -} - -private AddressInfo[] getAddressInfoImpl(scope const(char)[] node, scope const(char)[] service, addrinfo* hints) @system -{ - import std.array : appender; - - if (getaddrinfoPointer && freeaddrinfoPointer) - { - addrinfo* ai_res; - - int ret = getaddrinfoPointer( - node.tempCString(), - service.tempCString(), - hints, &ai_res); - enforce(ret == 0, new SocketOSException("getaddrinfo error", ret, &formatGaiError)); - scope(exit) freeaddrinfoPointer(ai_res); - - auto result = appender!(AddressInfo[])(); - - // Use const to force UnknownAddressReference to copy the sockaddr. - for (const(addrinfo)* ai = ai_res; ai; ai = ai.ai_next) - result ~= AddressInfo( - cast(AddressFamily) ai.ai_family, - cast(SocketType ) ai.ai_socktype, - cast(ProtocolType ) ai.ai_protocol, - new UnknownAddressReference(ai.ai_addr, cast(socklen_t) ai.ai_addrlen), - ai.ai_canonname ? to!string(ai.ai_canonname) : null); - - assert(result.data.length > 0); - return result.data; - } - - throw new SocketFeatureException("Address info lookup is not available " ~ - "on this system."); -} - - -@safe unittest -{ - softUnittest({ - if (getaddrinfoPointer) - { - // Roundtrip DNS resolution - auto results = getAddressInfo("www.digitalmars.com"); - assert(results[0].address.toHostNameString() == "digitalmars.com"); - - // Canonical name - results = getAddressInfo("www.digitalmars.com", - AddressInfoFlags.CANONNAME); - assert(results[0].canonicalName == "digitalmars.com"); - - // IPv6 resolution - //results = getAddressInfo("ipv6.google.com"); - //assert(results[0].family == AddressFamily.INET6); - - // Multihomed resolution - //results = getAddressInfo("google.com"); - //assert(results.length > 1); - - // Parsing IPv4 - results = getAddressInfo("127.0.0.1", AddressInfoFlags.NUMERICHOST); - assert(results.length && results[0].family == AddressFamily.INET); - - // Parsing IPv6 - results = getAddressInfo("::1", AddressInfoFlags.NUMERICHOST); - assert(results.length && results[0].family == AddressFamily.INET6); - } - }); - - if (getaddrinfoPointer) - { - auto results = getAddressInfo(null, "1234", AddressInfoFlags.PASSIVE, - SocketType.STREAM, ProtocolType.TCP, AddressFamily.INET); - assert(results.length == 1 && results[0].address.toString() == "0.0.0.0:1234"); - } -} - - -private ushort serviceToPort(scope const(char)[] service) -{ - if (service == "") - return InternetAddress.PORT_ANY; - else - if (isNumeric(service)) - return to!ushort(service); - else - { - auto s = new Service(); - s.getServiceByName(service); - return s.port; - } -} - -/** - * Provides _protocol-independent translation from host names to socket - * addresses. Uses `getAddressInfo` if the current system supports it, - * and `InternetHost` otherwise. - * - * Returns: Array with one `Address` instance per socket address. - * - * Throws: `SocketOSException` on failure. - * - * Example: - * --- - * writeln("Resolving www.digitalmars.com:"); - * try - * { - * auto addresses = getAddress("www.digitalmars.com"); - * foreach (address; addresses) - * writefln(" IP: %s", address.toAddrString()); - * } - * catch (SocketException e) - * writefln(" Lookup failed: %s", e.msg); - * --- - */ -Address[] getAddress(scope const(char)[] hostname, scope const(char)[] service = null) -{ - if (getaddrinfoPointer && freeaddrinfoPointer) - { - // use getAddressInfo - auto infos = getAddressInfo(hostname, service); - Address[] results; - results.length = infos.length; - foreach (i, ref result; results) - result = infos[i].address; - return results; - } - else - return getAddress(hostname, serviceToPort(service)); -} - -/// ditto -Address[] getAddress(scope const(char)[] hostname, ushort port) -{ - if (getaddrinfoPointer && freeaddrinfoPointer) - return getAddress(hostname, to!string(port)); - else - { - // use getHostByName - auto ih = new InternetHost; - if (!ih.getHostByName(hostname)) - throw new AddressException( - text("Unable to resolve host '", hostname, "'")); - - Address[] results; - foreach (uint addr; ih.addrList) - results ~= new InternetAddress(addr, port); - return results; - } -} - - -@safe unittest -{ - softUnittest({ - auto addresses = getAddress("63.105.9.61"); - assert(addresses.length && addresses[0].toAddrString() == "63.105.9.61"); - - if (getaddrinfoPointer) - { - // test via gethostbyname - auto getaddrinfoPointerBackup = getaddrinfoPointer; - cast() getaddrinfoPointer = null; - scope(exit) () @trusted { cast() getaddrinfoPointer = getaddrinfoPointerBackup; }(); - - addresses = getAddress("63.105.9.61"); - assert(addresses.length && addresses[0].toAddrString() == "63.105.9.61"); - } - }); -} - - -/** - * Provides _protocol-independent parsing of network addresses. Does not - * attempt name resolution. Uses `getAddressInfo` with - * `AddressInfoFlags.NUMERICHOST` if the current system supports it, and - * `InternetAddress` otherwise. - * - * Returns: An `Address` instance representing specified address. - * - * Throws: `SocketException` on failure. - * - * Example: - * --- - * writeln("Enter IP address:"); - * string ip = readln().chomp(); - * try - * { - * Address address = parseAddress(ip); - * writefln("Looking up reverse of %s:", - * address.toAddrString()); - * try - * { - * string reverse = address.toHostNameString(); - * if (reverse) - * writefln(" Reverse name: %s", reverse); - * else - * writeln(" Reverse hostname not found."); - * } - * catch (SocketException e) - * writefln(" Lookup error: %s", e.msg); - * } - * catch (SocketException e) - * { - * writefln(" %s is not a valid IP address: %s", - * ip, e.msg); - * } - * --- - */ -Address parseAddress(scope const(char)[] hostaddr, scope const(char)[] service = null) -{ - if (getaddrinfoPointer && freeaddrinfoPointer) - return getAddressInfo(hostaddr, service, AddressInfoFlags.NUMERICHOST)[0].address; - else - return parseAddress(hostaddr, serviceToPort(service)); -} - -/// ditto -Address parseAddress(scope const(char)[] hostaddr, ushort port) -{ - if (getaddrinfoPointer && freeaddrinfoPointer) - return parseAddress(hostaddr, to!string(port)); - else - { - auto in4_addr = InternetAddress.parse(hostaddr); - enforce(in4_addr != InternetAddress.ADDR_NONE, - new SocketParameterException("Invalid IP address")); - return new InternetAddress(in4_addr, port); - } -} - - -@safe unittest -{ - softUnittest({ - auto address = parseAddress("63.105.9.61"); - assert(address.toAddrString() == "63.105.9.61"); - - if (getaddrinfoPointer) - { - // test via inet_addr - auto getaddrinfoPointerBackup = getaddrinfoPointer; - cast() getaddrinfoPointer = null; - scope(exit) () @trusted { cast() getaddrinfoPointer = getaddrinfoPointerBackup; }(); - - address = parseAddress("63.105.9.61"); - assert(address.toAddrString() == "63.105.9.61"); - } - - assert(collectException!SocketException(parseAddress("Invalid IP address"))); - }); -} - - -/** - * Class for exceptions thrown from an `Address`. - */ -class AddressException: SocketOSException -{ - mixin socketOSExceptionCtors; -} - - -/** - * Abstract class for representing a socket address. - * - * Example: - * --- - * writeln("About www.google.com port 80:"); - * try - * { - * Address[] addresses = getAddress("www.google.com", 80); - * writefln(" %d addresses found.", addresses.length); - * foreach (int i, Address a; addresses) - * { - * writefln(" Address %d:", i+1); - * writefln(" IP address: %s", a.toAddrString()); - * writefln(" Hostname: %s", a.toHostNameString()); - * writefln(" Port: %s", a.toPortString()); - * writefln(" Service name: %s", - * a.toServiceNameString()); - * } - * } - * catch (SocketException e) - * writefln(" Lookup error: %s", e.msg); - * --- - */ -abstract class Address -{ - /// Returns pointer to underlying `sockaddr` structure. - abstract @property sockaddr* name() pure nothrow @nogc; - abstract @property const(sockaddr)* name() const pure nothrow @nogc; /// ditto - - /// Returns actual size of underlying `sockaddr` structure. - abstract @property socklen_t nameLen() const pure nothrow @nogc; - - // Socket.remoteAddress, Socket.localAddress, and Socket.receiveFrom - // use setNameLen to set the actual size of the address as returned by - // getsockname, getpeername, and recvfrom, respectively. - // The following implementation is sufficient for fixed-length addresses, - // and ensures that the length is not changed. - // Must be overridden for variable-length addresses. - protected void setNameLen(socklen_t len) - { - if (len != this.nameLen) - throw new AddressException( - format("%s expects address of length %d, not %d", typeid(this), - this.nameLen, len), 0); - } - - /// Family of this address. - @property AddressFamily addressFamily() const pure nothrow @nogc - { - return cast(AddressFamily) name.sa_family; - } - - // Common code for toAddrString and toHostNameString - private string toHostString(bool numeric) @trusted const - { - // getnameinfo() is the recommended way to perform a reverse (name) - // lookup on both Posix and Windows. However, it is only available - // on Windows XP and above, and not included with the WinSock import - // libraries shipped with DMD. Thus, we check for getnameinfo at - // runtime in the shared module constructor, and use it if it's - // available in the base class method. Classes for specific network - // families (e.g. InternetHost) override this method and use a - // deprecated, albeit commonly-available method when getnameinfo() - // is not available. - // http://technet.microsoft.com/en-us/library/aa450403.aspx - if (getnameinfoPointer) - { - auto buf = new char[NI_MAXHOST]; - auto ret = getnameinfoPointer( - name, nameLen, - buf.ptr, cast(uint) buf.length, - null, 0, - numeric ? NI_NUMERICHOST : NI_NAMEREQD); - - if (!numeric) - { - if (ret == EAI_NONAME) - return null; - version (Windows) - if (ret == WSANO_DATA) - return null; - } - - enforce(ret == 0, new AddressException("Could not get " ~ - (numeric ? "host address" : "host name"))); - return assumeUnique(buf[0 .. strlen(buf.ptr)]); - } - - throw new SocketFeatureException((numeric ? "Host address" : "Host name") ~ - " lookup for this address family is not available on this system."); - } - - // Common code for toPortString and toServiceNameString - private string toServiceString(bool numeric) @trusted const - { - // See toHostNameString() for details about getnameinfo(). - if (getnameinfoPointer) - { - auto buf = new char[NI_MAXSERV]; - enforce(getnameinfoPointer( - name, nameLen, - null, 0, - buf.ptr, cast(uint) buf.length, - numeric ? NI_NUMERICSERV : NI_NAMEREQD - ) == 0, new AddressException("Could not get " ~ - (numeric ? "port number" : "service name"))); - return assumeUnique(buf[0 .. strlen(buf.ptr)]); - } - - throw new SocketFeatureException((numeric ? "Port number" : "Service name") ~ - " lookup for this address family is not available on this system."); - } - - /** - * Attempts to retrieve the host address as a human-readable string. - * - * Throws: `AddressException` on failure, or `SocketFeatureException` - * if address retrieval for this address family is not available on the - * current system. - */ - string toAddrString() const - { - return toHostString(true); - } - - /** - * Attempts to retrieve the host name as a fully qualified domain name. - * - * Returns: The FQDN corresponding to this `Address`, or `null` if - * the host name did not resolve. - * - * Throws: `AddressException` on error, or `SocketFeatureException` - * if host name lookup for this address family is not available on the - * current system. - */ - string toHostNameString() const - { - return toHostString(false); - } - - /** - * Attempts to retrieve the numeric port number as a string. - * - * Throws: `AddressException` on failure, or `SocketFeatureException` - * if port number retrieval for this address family is not available on the - * current system. - */ - string toPortString() const - { - return toServiceString(true); - } - - /** - * Attempts to retrieve the service name as a string. - * - * Throws: `AddressException` on failure, or `SocketFeatureException` - * if service name lookup for this address family is not available on the - * current system. - */ - string toServiceNameString() const - { - return toServiceString(false); - } - - /// Human readable string representing this address. - override string toString() const - { - try - { - string host = toAddrString(); - string port = toPortString(); - if (host.indexOf(':') >= 0) - return "[" ~ host ~ "]:" ~ port; - else - return host ~ ":" ~ port; - } - catch (SocketException) - return "Unknown"; - } -} - -/** - * Encapsulates an unknown socket address. - */ -class UnknownAddress: Address -{ -protected: - sockaddr sa; - - -public: - override @property sockaddr* name() return - { - return &sa; - } - - override @property const(sockaddr)* name() const return - { - return &sa; - } - - - override @property socklen_t nameLen() const - { - return cast(socklen_t) sa.sizeof; - } - -} - - -/** - * Encapsulates a reference to an arbitrary - * socket address. - */ -class UnknownAddressReference: Address -{ -protected: - sockaddr* sa; - socklen_t len; - -public: - /// Constructs an `Address` with a reference to the specified `sockaddr`. - this(sockaddr* sa, socklen_t len) pure nothrow @nogc - { - this.sa = sa; - this.len = len; - } - - /// Constructs an `Address` with a copy of the specified `sockaddr`. - this(const(sockaddr)* sa, socklen_t len) @system pure nothrow - { - this.sa = cast(sockaddr*) (cast(ubyte*) sa)[0 .. len].dup.ptr; - this.len = len; - } - - override @property sockaddr* name() - { - return sa; - } - - override @property const(sockaddr)* name() const - { - return sa; - } - - - override @property socklen_t nameLen() const - { - return cast(socklen_t) len; - } -} - - -/** - * Encapsulates an IPv4 (Internet Protocol version 4) socket address. - * - * Consider using `getAddress`, `parseAddress` and `Address` methods - * instead of using this class directly. - */ -class InternetAddress: Address -{ -protected: - sockaddr_in sin; - - - this() pure nothrow @nogc - { - } - - -public: - override @property sockaddr* name() return - { - return cast(sockaddr*)&sin; - } - - override @property const(sockaddr)* name() const return - { - return cast(const(sockaddr)*)&sin; - } - - - override @property socklen_t nameLen() const - { - return cast(socklen_t) sin.sizeof; - } - - - enum uint ADDR_ANY = INADDR_ANY; /// Any IPv4 host address. - enum uint ADDR_NONE = INADDR_NONE; /// An invalid IPv4 host address. - enum ushort PORT_ANY = 0; /// Any IPv4 port number. - - /// Returns the IPv4 _port number (in host byte order). - @property ushort port() const pure nothrow @nogc - { - return ntohs(sin.sin_port); - } - - /// Returns the IPv4 address number (in host byte order). - @property uint addr() const pure nothrow @nogc - { - return ntohl(sin.sin_addr.s_addr); - } - - /** - * Construct a new `InternetAddress`. - * Params: - * addr = an IPv4 address string in the dotted-decimal form a.b.c.d, - * or a host name which will be resolved using an `InternetHost` - * object. - * port = port number, may be `PORT_ANY`. - */ - this(scope const(char)[] addr, ushort port) - { - uint uiaddr = parse(addr); - if (ADDR_NONE == uiaddr) - { - InternetHost ih = new InternetHost; - if (!ih.getHostByName(addr)) - //throw new AddressException("Invalid internet address"); - throw new AddressException( - text("Unable to resolve host '", addr, "'")); - uiaddr = ih.addrList[0]; - } - sin.sin_family = AddressFamily.INET; - sin.sin_addr.s_addr = htonl(uiaddr); - sin.sin_port = htons(port); - } - - /** - * Construct a new `InternetAddress`. - * Params: - * addr = (optional) an IPv4 address in host byte order, may be `ADDR_ANY`. - * port = port number, may be `PORT_ANY`. - */ - this(uint addr, ushort port) pure nothrow @nogc - { - sin.sin_family = AddressFamily.INET; - sin.sin_addr.s_addr = htonl(addr); - sin.sin_port = htons(port); - } - - /// ditto - this(ushort port) pure nothrow @nogc - { - sin.sin_family = AddressFamily.INET; - sin.sin_addr.s_addr = ADDR_ANY; - sin.sin_port = htons(port); - } - - /** - * Construct a new `InternetAddress`. - * Params: - * addr = A sockaddr_in as obtained from lower-level API calls such as getifaddrs. - */ - this(sockaddr_in addr) pure nothrow @nogc - { - assert(addr.sin_family == AddressFamily.INET, "Socket address is not of INET family."); - sin = addr; - } - - /// Human readable string representing the IPv4 address in dotted-decimal form. - override string toAddrString() @trusted const - { - return to!string(inet_ntoa(sin.sin_addr)); - } - - /// Human readable string representing the IPv4 port. - override string toPortString() const - { - return std.conv.to!string(port); - } - - /** - * Attempts to retrieve the host name as a fully qualified domain name. - * - * Returns: The FQDN corresponding to this `InternetAddress`, or - * `null` if the host name did not resolve. - * - * Throws: `AddressException` on error. - */ - override string toHostNameString() const - { - // getnameinfo() is the recommended way to perform a reverse (name) - // lookup on both Posix and Windows. However, it is only available - // on Windows XP and above, and not included with the WinSock import - // libraries shipped with DMD. Thus, we check for getnameinfo at - // runtime in the shared module constructor, and fall back to the - // deprecated getHostByAddr() if it could not be found. See also: - // http://technet.microsoft.com/en-us/library/aa450403.aspx - - if (getnameinfoPointer) - return super.toHostNameString(); - else - { - auto host = new InternetHost(); - if (!host.getHostByAddr(ntohl(sin.sin_addr.s_addr))) - return null; - return host.name; - } - } - - /** - * Provides support for comparing equality with another - * InternetAddress of the same type. - * Returns: true if the InternetAddresses share the same address and - * port number. - */ - override bool opEquals(Object o) const - { - auto other = cast(InternetAddress) o; - return other && this.sin.sin_addr.s_addr == other.sin.sin_addr.s_addr && - this.sin.sin_port == other.sin.sin_port; - } - - /// - @system unittest - { - auto addr1 = new InternetAddress("127.0.0.1", 80); - auto addr2 = new InternetAddress("127.0.0.2", 80); - - assert(addr1 == addr1); - assert(addr1 != addr2); - } - - /** - * Parse an IPv4 address string in the dotted-decimal form $(I a.b.c.d) - * and return the number. - * Returns: If the string is not a legitimate IPv4 address, - * `ADDR_NONE` is returned. - */ - static uint parse(scope const(char)[] addr) @trusted nothrow - { - return ntohl(inet_addr(addr.tempCString())); - } - - /** - * Convert an IPv4 address number in host byte order to a human readable - * string representing the IPv4 address in dotted-decimal form. - */ - static string addrToString(uint addr) @trusted nothrow - { - in_addr sin_addr; - sin_addr.s_addr = htonl(addr); - return to!string(inet_ntoa(sin_addr)); - } -} - - -@safe unittest -{ - softUnittest({ - const InternetAddress ia = new InternetAddress("63.105.9.61", 80); - assert(ia.toString() == "63.105.9.61:80"); - }); - - softUnittest({ - // test construction from a sockaddr_in - sockaddr_in sin; - - sin.sin_addr.s_addr = htonl(0x7F_00_00_01); // 127.0.0.1 - sin.sin_family = AddressFamily.INET; - sin.sin_port = htons(80); - - const InternetAddress ia = new InternetAddress(sin); - assert(ia.toString() == "127.0.0.1:80"); - }); - - softUnittest({ - // test reverse lookup - auto ih = new InternetHost; - if (ih.getHostByName("digitalmars.com")) - { - const ia = new InternetAddress(ih.addrList[0], 80); - assert(ia.toHostNameString() == "digitalmars.com"); - - if (getnameinfoPointer) - { - // test reverse lookup, via gethostbyaddr - auto getnameinfoPointerBackup = getnameinfoPointer; - cast() getnameinfoPointer = null; - scope(exit) () @trusted { cast() getnameinfoPointer = getnameinfoPointerBackup; }(); - - assert(ia.toHostNameString() == "digitalmars.com"); - } - } - }); - - if (runSlowTests) - softUnittest({ - // test failing reverse lookup - const InternetAddress ia = new InternetAddress("255.255.255.255", 80); - assert(ia.toHostNameString() is null); - - if (getnameinfoPointer) - { - // test failing reverse lookup, via gethostbyaddr - auto getnameinfoPointerBackup = getnameinfoPointer; - cast() getnameinfoPointer = null; - scope(exit) () @trusted { cast() getnameinfoPointer = getnameinfoPointerBackup; }(); - - assert(ia.toHostNameString() is null); - } - }); -} - - -/** - * Encapsulates an IPv6 (Internet Protocol version 6) socket address. - * - * Consider using `getAddress`, `parseAddress` and `Address` methods - * instead of using this class directly. - */ -class Internet6Address: Address -{ -protected: - sockaddr_in6 sin6; - - - this() pure nothrow @nogc - { - } - - -public: - override @property sockaddr* name() return - { - return cast(sockaddr*)&sin6; - } - - override @property const(sockaddr)* name() const return - { - return cast(const(sockaddr)*)&sin6; - } - - - override @property socklen_t nameLen() const - { - return cast(socklen_t) sin6.sizeof; - } - - - /// Any IPv6 host address. - static @property ref const(ubyte)[16] ADDR_ANY() pure nothrow @nogc - { - static if (is(typeof(IN6ADDR_ANY))) - { - version (Windows) - { - static immutable addr = IN6ADDR_ANY.s6_addr; - return addr; - } - else - return IN6ADDR_ANY.s6_addr; - } - else static if (is(typeof(in6addr_any))) - { - return in6addr_any.s6_addr; - } - else - static assert(0); - } - - /// Any IPv6 port number. - enum ushort PORT_ANY = 0; - - /// Returns the IPv6 port number. - @property ushort port() const pure nothrow @nogc - { - return ntohs(sin6.sin6_port); - } - - /// Returns the IPv6 address. - @property ubyte[16] addr() const pure nothrow @nogc - { - return sin6.sin6_addr.s6_addr; - } - - /** - * Construct a new `Internet6Address`. - * Params: - * addr = an IPv6 host address string in the form described in RFC 2373, - * or a host name which will be resolved using `getAddressInfo`. - * service = (optional) service name. - */ - this(scope const(char)[] addr, scope const(char)[] service = null) @trusted - { - auto results = getAddressInfo(addr, service, AddressFamily.INET6); - assert(results.length && results[0].family == AddressFamily.INET6); - sin6 = *cast(sockaddr_in6*) results[0].address.name; - } - - /** - * Construct a new `Internet6Address`. - * Params: - * addr = an IPv6 host address string in the form described in RFC 2373, - * or a host name which will be resolved using `getAddressInfo`. - * port = port number, may be `PORT_ANY`. - */ - this(scope const(char)[] addr, ushort port) - { - if (port == PORT_ANY) - this(addr); - else - this(addr, to!string(port)); - } - - /** - * Construct a new `Internet6Address`. - * Params: - * addr = (optional) an IPv6 host address in host byte order, or - * `ADDR_ANY`. - * port = port number, may be `PORT_ANY`. - */ - this(ubyte[16] addr, ushort port) pure nothrow @nogc - { - sin6.sin6_family = AddressFamily.INET6; - sin6.sin6_addr.s6_addr = addr; - sin6.sin6_port = htons(port); - } - - /// ditto - this(ushort port) pure nothrow @nogc - { - sin6.sin6_family = AddressFamily.INET6; - sin6.sin6_addr.s6_addr = ADDR_ANY; - sin6.sin6_port = htons(port); - } - - /** - * Construct a new `Internet6Address`. - * Params: - * addr = A sockaddr_in6 as obtained from lower-level API calls such as getifaddrs. - */ - this(sockaddr_in6 addr) pure nothrow @nogc - { - assert(addr.sin6_family == AddressFamily.INET6); - sin6 = addr; - } - - /** - * Parse an IPv6 host address string as described in RFC 2373, and return the - * address. - * Throws: `SocketException` on error. - */ - static ubyte[16] parse(scope const(char)[] addr) @trusted - { - // Although we could use inet_pton here, it's only available on Windows - // versions starting with Vista, so use getAddressInfo with NUMERICHOST - // instead. - auto results = getAddressInfo(addr, AddressInfoFlags.NUMERICHOST); - if (results.length && results[0].family == AddressFamily.INET6) - return (cast(sockaddr_in6*) results[0].address.name).sin6_addr.s6_addr; - throw new AddressException("Not an IPv6 address", 0); - } -} - - -@safe unittest -{ - softUnittest({ - const Internet6Address ia = new Internet6Address("::1", 80); - assert(ia.toString() == "[::1]:80"); - }); - - softUnittest({ - // test construction from a sockaddr_in6 - sockaddr_in6 sin; - - sin.sin6_addr.s6_addr = [ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1]; // [::1] - sin.sin6_family = AddressFamily.INET6; - sin.sin6_port = htons(80); - - const Internet6Address ia = new Internet6Address(sin); - assert(ia.toString() == "[::1]:80"); - }); -} - - -version (StdDdoc) -{ - static if (!is(sockaddr_un)) - { - // This exists only to allow the constructor taking - // a sockaddr_un to be compilable for documentation - // on platforms that don't supply a sockaddr_un. - struct sockaddr_un - { - } - } - - /** - * Encapsulates an address for a Unix domain socket (`AF_UNIX`), - * i.e. a socket bound to a path name in the file system. - * Available only on supported systems. - * - * Linux also supports an abstract address namespace, in which addresses - * are independent of the file system. A socket address is abstract - * iff `path` starts with a _null byte (`'\0'`). Null bytes in other - * positions of an abstract address are allowed and have no special - * meaning. - * - * Example: - * --- - * auto addr = new UnixAddress("/var/run/dbus/system_bus_socket"); - * auto abstractAddr = new UnixAddress("\0/tmp/dbus-OtHLWmCLPR"); - * --- - * - * See_Also: $(HTTP man7.org/linux/man-pages/man7/unix.7.html, UNIX(7)) - */ - class UnixAddress: Address - { - private this() pure nothrow @nogc {} - - /// Construct a new `UnixAddress` from the specified path. - this(scope const(char)[] path) { } - - /** - * Construct a new `UnixAddress`. - * Params: - * addr = A sockaddr_un as obtained from lower-level API calls. - */ - this(sockaddr_un addr) pure nothrow @nogc { } - - /// Get the underlying _path. - @property string path() const { return null; } - - /// ditto - override string toString() const { return null; } - - override @property sockaddr* name() { return null; } - override @property const(sockaddr)* name() const { return null; } - override @property socklen_t nameLen() const { return 0; } - } -} -else -static if (is(sockaddr_un)) -{ - class UnixAddress: Address - { - protected: - socklen_t _nameLen; - - struct - { - align (1): - sockaddr_un sun; - char unused = '\0'; // placeholder for a terminating '\0' - } - - this() pure nothrow @nogc - { - sun.sun_family = AddressFamily.UNIX; - sun.sun_path = '?'; - _nameLen = sun.sizeof; - } - - override void setNameLen(socklen_t len) @trusted - { - if (len > sun.sizeof) - throw new SocketParameterException("Not enough socket address storage"); - _nameLen = len; - } - - public: - override @property sockaddr* name() return - { - return cast(sockaddr*)&sun; - } - - override @property const(sockaddr)* name() const return - { - return cast(const(sockaddr)*)&sun; - } - - override @property socklen_t nameLen() @trusted const - { - return _nameLen; - } - - this(scope const(char)[] path) @trusted pure - { - enforce(path.length <= sun.sun_path.sizeof, new SocketParameterException("Path too long")); - sun.sun_family = AddressFamily.UNIX; - sun.sun_path.ptr[0 .. path.length] = (cast(byte[]) path)[]; - _nameLen = cast(socklen_t) - { - auto len = sockaddr_un.init.sun_path.offsetof + path.length; - // Pathname socket address must be terminated with '\0' - // which must be included in the address length. - if (sun.sun_path.ptr[0]) - { - sun.sun_path.ptr[path.length] = 0; - ++len; - } - return len; - }(); - } - - this(sockaddr_un addr) pure nothrow @nogc - { - assert(addr.sun_family == AddressFamily.UNIX); - sun = addr; - } - - @property string path() @trusted const pure - { - auto len = _nameLen - sockaddr_un.init.sun_path.offsetof; - if (len == 0) - return null; // An empty path may be returned from getpeername - // For pathname socket address we need to strip off the terminating '\0' - if (sun.sun_path.ptr[0]) - --len; - return (cast(const(char)*) sun.sun_path.ptr)[0 .. len].idup; - } - - override string toString() const pure - { - return path; - } - } - - @safe unittest - { - import core.stdc.stdio : remove; - - version (iOSDerived) - { - // Slightly different version of `std.file.deleteme` to reduce the path - // length on iOS derived platforms. Due to the sandbox, the length - // of paths can quickly become too long. - static string deleteme() - { - import std.conv : text; - import std.process : thisProcessID; - import std.file : tempDir; - - return text(tempDir, thisProcessID); - } - } - - else - import std.file : deleteme; - - immutable ubyte[] data = [1, 2, 3, 4]; - Socket[2] pair; - - const basePath = deleteme; - auto names = [ basePath ~ "-socket" ]; - version (linux) - names ~= "\0" ~ basePath ~ "-abstract\0unix\0socket"; - - foreach (name; names) - { - auto address = new UnixAddress(name); - - auto listener = new Socket(AddressFamily.UNIX, SocketType.STREAM); - scope(exit) listener.close(); - listener.bind(address); - scope(exit) () @trusted { if (name[0]) remove(name.tempCString()); } (); - assert(listener.localAddress.toString == name); - - listener.listen(1); - - pair[0] = new Socket(AddressFamily.UNIX, SocketType.STREAM); - scope(exit) listener.close(); - - pair[0].connect(address); - scope(exit) pair[0].close(); - - pair[1] = listener.accept(); - scope(exit) pair[1].close(); - - pair[0].send(data); - - auto buf = new ubyte[data.length]; - pair[1].receive(buf); - assert(buf == data); - - // getpeername is free to return an empty name for a unix - // domain socket pair or unbound socket. Let's confirm it - // returns successfully and doesn't throw anything. - // See https://issues.dlang.org/show_bug.cgi?id=20544 - assertNotThrown(pair[1].remoteAddress().toString()); - } - } -} - - -/** - * Exception thrown by `Socket.accept`. - */ -class SocketAcceptException: SocketOSException -{ - mixin socketOSExceptionCtors; -} - -/// How a socket is shutdown: -enum SocketShutdown: int -{ - RECEIVE = SD_RECEIVE, /// socket receives are disallowed - SEND = SD_SEND, /// socket sends are disallowed - BOTH = SD_BOTH, /// both RECEIVE and SEND -} - - -/// Socket flags that may be OR'ed together: -enum SocketFlags: int -{ - NONE = 0, /// no flags specified - - OOB = MSG_OOB, /// out-of-band stream data - PEEK = MSG_PEEK, /// peek at incoming data without removing it from the queue, only for receiving - DONTROUTE = MSG_DONTROUTE, /// data should not be subject to routing; this flag may be ignored. Only for sending -} - - -/// Duration timeout value. -struct TimeVal -{ - _ctimeval ctimeval; - alias tv_sec_t = typeof(ctimeval.tv_sec); - alias tv_usec_t = typeof(ctimeval.tv_usec); - - /// Number of _seconds. - pure nothrow @nogc @property - ref inout(tv_sec_t) seconds() inout return - { - return ctimeval.tv_sec; - } - - /// Number of additional _microseconds. - pure nothrow @nogc @property - ref inout(tv_usec_t) microseconds() inout return - { - return ctimeval.tv_usec; - } -} - - -/** - * A collection of sockets for use with `Socket.select`. - * - * `SocketSet` wraps the platform `fd_set` type. However, unlike - * `fd_set`, `SocketSet` is not statically limited to `FD_SETSIZE` - * or any other limit, and grows as needed. - */ -class SocketSet -{ -private: - version (Windows) - { - // On Windows, fd_set is an array of socket handles, - // following a word containing the fd_set instance size. - // We use one dynamic array for everything, and use its first - // element(s) for the count. - - alias fd_set_count_type = typeof(fd_set.init.fd_count); - alias fd_set_type = typeof(fd_set.init.fd_array[0]); - static assert(fd_set_type.sizeof == socket_t.sizeof); - - // Number of fd_set_type elements at the start of our array that are - // used for the socket count and alignment - - enum FD_SET_OFFSET = fd_set.fd_array.offsetof / fd_set_type.sizeof; - static assert(FD_SET_OFFSET); - static assert(fd_set.fd_count.offsetof % fd_set_type.sizeof == 0); - - fd_set_type[] set; - - void resize(size_t size) pure nothrow - { - set.length = FD_SET_OFFSET + size; - } - - ref inout(fd_set_count_type) count() @trusted @property inout pure nothrow @nogc - { - assert(set.length); - return *cast(inout(fd_set_count_type)*)set.ptr; - } - - size_t capacity() @property const pure nothrow @nogc - { - return set.length - FD_SET_OFFSET; - } - - inout(socket_t)[] fds() @trusted inout @property pure nothrow @nogc - { - return cast(inout(socket_t)[])set[FD_SET_OFFSET .. FD_SET_OFFSET+count]; - } - } - else - version (Posix) - { - // On Posix, fd_set is a bit array. We assume that the fd_set - // type (declared in core.sys.posix.sys.select) is a structure - // containing a single field, a static array. - - static assert(fd_set.tupleof.length == 1); - - // This is the type used in the fd_set array. - // Using the type of the correct size is important for big-endian - // architectures. - - alias fd_set_type = typeof(fd_set.init.tupleof[0][0]); - - // Number of file descriptors represented by one fd_set_type - - enum FD_NFDBITS = 8 * fd_set_type.sizeof; - - static fd_set_type mask(uint n) pure nothrow @nogc - { - return (cast(fd_set_type) 1) << (n % FD_NFDBITS); - } - - // Array size to fit that many sockets - - static size_t lengthFor(size_t size) pure nothrow @nogc - { - return (size + (FD_NFDBITS-1)) / FD_NFDBITS; - } - - fd_set_type[] set; - - void resize(size_t size) pure nothrow - { - set.length = lengthFor(size); - } - - // Make sure we can fit that many sockets - - void setMinCapacity(size_t size) pure nothrow - { - auto length = lengthFor(size); - if (set.length < length) - set.length = length; - } - - size_t capacity() @property const pure nothrow @nogc - { - return set.length * FD_NFDBITS; - } - - int maxfd; - } - else - static assert(false, "Unknown platform"); - -public: - - /** - * Create a SocketSet with a specific initial capacity (defaults to - * `FD_SETSIZE`, the system's default capacity). - */ - this(size_t size = FD_SETSIZE) pure nothrow - { - resize(size); - reset(); - } - - /// Reset the `SocketSet` so that there are 0 `Socket`s in the collection. - void reset() pure nothrow @nogc - { - version (Windows) - count = 0; - else - { - set[] = 0; - maxfd = -1; - } - } - - - void add(socket_t s) @trusted pure nothrow - { - version (Windows) - { - if (count == capacity) - { - set.length *= 2; - set.length = set.capacity; - } - ++count; - fds[$-1] = s; - } - else - { - auto index = s / FD_NFDBITS; - auto length = set.length; - if (index >= length) - { - while (index >= length) - length *= 2; - set.length = length; - set.length = set.capacity; - } - set[index] |= mask(s); - if (maxfd < s) - maxfd = s; - } - } - - /** - * Add a `Socket` to the collection. - * The socket must not already be in the collection. - */ - void add(Socket s) pure nothrow - { - add(s.sock); - } - - void remove(socket_t s) pure nothrow - { - version (Windows) - { - import std.algorithm.searching : countUntil; - auto fds = fds; - auto p = fds.countUntil(s); - if (p >= 0) - fds[p] = fds[--count]; - } - else - { - auto index = s / FD_NFDBITS; - if (index >= set.length) - return; - set[index] &= ~mask(s); - // note: adjusting maxfd would require scanning the set, not worth it - } - } - - - /** - * Remove this `Socket` from the collection. - * Does nothing if the socket is not in the collection already. - */ - void remove(Socket s) pure nothrow - { - remove(s.sock); - } - - int isSet(socket_t s) const pure nothrow @nogc - { - version (Windows) - { - import std.algorithm.searching : canFind; - return fds.canFind(s) ? 1 : 0; - } - else - { - if (s > maxfd) - return 0; - auto index = s / FD_NFDBITS; - return (set[index] & mask(s)) ? 1 : 0; - } - } - - - /// Return nonzero if this `Socket` is in the collection. - int isSet(Socket s) const pure nothrow @nogc - { - return isSet(s.sock); - } - - - /** - * Returns: - * The current capacity of this `SocketSet`. The exact - * meaning of the return value varies from platform to platform. - * - * Note: - * Since D 2.065, this value does not indicate a - * restriction, and `SocketSet` will grow its capacity as - * needed automatically. - */ - @property uint max() const pure nothrow @nogc - { - return cast(uint) capacity; - } - - - fd_set* toFd_set() @trusted pure nothrow @nogc - { - return cast(fd_set*) set.ptr; - } - - - int selectn() const pure nothrow @nogc - { - version (Windows) - { - return count; - } - else version (Posix) - { - return maxfd + 1; - } - } -} - -@safe unittest -{ - auto fds = cast(socket_t[]) - [cast(socket_t) 1, 2, 0, 1024, 17, 42, 1234, 77, 77+32, 77+64]; - auto set = new SocketSet(); - foreach (fd; fds) assert(!set.isSet(fd)); - foreach (fd; fds) set.add(fd); - foreach (fd; fds) assert(set.isSet(fd)); - - // Make sure SocketSet reimplements fd_set correctly - auto fdset = set.toFd_set(); - foreach (fd; fds[0]..cast(socket_t)(fds[$-1]+1)) - assert(cast(bool) set.isSet(fd) == cast(bool)(() @trusted => FD_ISSET(fd, fdset))()); - - foreach (fd; fds) - { - assert(set.isSet(fd)); - set.remove(fd); - assert(!set.isSet(fd)); - } -} - -@safe unittest -{ - version (iOSDerived) - { - enum PAIRS = 256; - enum LIMIT = 1024; - } - else - { - enum PAIRS = 768; - enum LIMIT = 2048; - } - - softUnittest({ - version (Posix) - () @trusted - { - static assert(LIMIT > PAIRS*2); - import core.sys.posix.sys.resource; - rlimit fileLimit; - getrlimit(RLIMIT_NOFILE, &fileLimit); - assert(fileLimit.rlim_max > LIMIT, "Open file hard limit too low"); - fileLimit.rlim_cur = LIMIT; - setrlimit(RLIMIT_NOFILE, &fileLimit); - } (); - - Socket[2][PAIRS] pairs; - foreach (ref pair; pairs) - pair = socketPair(); - scope(exit) - { - foreach (pair; pairs) - { - pair[0].close(); - pair[1].close(); - } - } - - import std.random; - auto rng = Xorshift(42); - pairs[].randomShuffle(rng); - - auto readSet = new SocketSet(); - auto writeSet = new SocketSet(); - auto errorSet = new SocketSet(); - - foreach (testPair; pairs) - { - void fillSets() - { - readSet.reset(); - writeSet.reset(); - errorSet.reset(); - foreach (ref pair; pairs) - foreach (s; pair[]) - { - readSet.add(s); - writeSet.add(s); - errorSet.add(s); - } - } - - fillSets(); - auto n = Socket.select(readSet, writeSet, errorSet); - assert(n == PAIRS*2); // All in writeSet - assert(writeSet.isSet(testPair[0])); - assert(writeSet.isSet(testPair[1])); - assert(!readSet.isSet(testPair[0])); - assert(!readSet.isSet(testPair[1])); - assert(!errorSet.isSet(testPair[0])); - assert(!errorSet.isSet(testPair[1])); - - ubyte[1] b; - // Socket.send can't be marked with `scope` - // -> @safe DIP1000 code can't use it - see https://github.com/dlang/phobos/pull/6204 - () @trusted { - testPair[0].send(b[]); - }(); - fillSets(); - n = Socket.select(readSet, null, null); - assert(n == 1); // testPair[1] - assert(readSet.isSet(testPair[1])); - assert(!readSet.isSet(testPair[0])); - // Socket.receive can't be marked with `scope` - // -> @safe DIP1000 code can't use it - see https://github.com/dlang/phobos/pull/6204 - () @trusted { - testPair[1].receive(b[]); - }(); - } - }); -} - -// https://issues.dlang.org/show_bug.cgi?id=14012 -// https://issues.dlang.org/show_bug.cgi?id=14013 -@safe unittest -{ - auto set = new SocketSet(1); - assert(set.max >= 0); - - enum LIMIT = 4096; - foreach (n; 0 .. LIMIT) - set.add(cast(socket_t) n); - assert(set.max >= LIMIT); -} - -/// The level at which a socket option is defined: -enum SocketOptionLevel: int -{ - SOCKET = SOL_SOCKET, /// Socket level - IP = ProtocolType.IP, /// Internet Protocol version 4 level - ICMP = ProtocolType.ICMP, /// Internet Control Message Protocol level - IGMP = ProtocolType.IGMP, /// Internet Group Management Protocol level - GGP = ProtocolType.GGP, /// Gateway to Gateway Protocol level - TCP = ProtocolType.TCP, /// Transmission Control Protocol level - PUP = ProtocolType.PUP, /// PARC Universal Packet Protocol level - UDP = ProtocolType.UDP, /// User Datagram Protocol level - IDP = ProtocolType.IDP, /// Xerox NS protocol level - RAW = ProtocolType.RAW, /// Raw IP packet level - IPV6 = ProtocolType.IPV6, /// Internet Protocol version 6 level -} - -/// _Linger information for use with SocketOption.LINGER. -struct Linger -{ - _clinger clinger; - - private alias l_onoff_t = typeof(_clinger.init.l_onoff ); - private alias l_linger_t = typeof(_clinger.init.l_linger); - - /// Nonzero for _on. - pure nothrow @nogc @property - ref inout(l_onoff_t) on() inout return - { - return clinger.l_onoff; - } - - /// Linger _time. - pure nothrow @nogc @property - ref inout(l_linger_t) time() inout return - { - return clinger.l_linger; - } -} - -/// Specifies a socket option: -enum SocketOption: int -{ - DEBUG = SO_DEBUG, /// Record debugging information - BROADCAST = SO_BROADCAST, /// Allow transmission of broadcast messages - REUSEADDR = SO_REUSEADDR, /// Allow local reuse of address - LINGER = SO_LINGER, /// Linger on close if unsent data is present - OOBINLINE = SO_OOBINLINE, /// Receive out-of-band data in band - SNDBUF = SO_SNDBUF, /// Send buffer size - RCVBUF = SO_RCVBUF, /// Receive buffer size - DONTROUTE = SO_DONTROUTE, /// Do not route - SNDTIMEO = SO_SNDTIMEO, /// Send timeout - RCVTIMEO = SO_RCVTIMEO, /// Receive timeout - ERROR = SO_ERROR, /// Retrieve and clear error status - KEEPALIVE = SO_KEEPALIVE, /// Enable keep-alive packets - ACCEPTCONN = SO_ACCEPTCONN, /// Listen - RCVLOWAT = SO_RCVLOWAT, /// Minimum number of input bytes to process - SNDLOWAT = SO_SNDLOWAT, /// Minimum number of output bytes to process - TYPE = SO_TYPE, /// Socket type - - // SocketOptionLevel.TCP: - TCP_NODELAY = .TCP_NODELAY, /// Disable the Nagle algorithm for send coalescing - - // SocketOptionLevel.IPV6: - IPV6_UNICAST_HOPS = .IPV6_UNICAST_HOPS, /// IP unicast hop limit - IPV6_MULTICAST_IF = .IPV6_MULTICAST_IF, /// IP multicast interface - IPV6_MULTICAST_LOOP = .IPV6_MULTICAST_LOOP, /// IP multicast loopback - IPV6_MULTICAST_HOPS = .IPV6_MULTICAST_HOPS, /// IP multicast hops - IPV6_JOIN_GROUP = .IPV6_JOIN_GROUP, /// Add an IP group membership - IPV6_LEAVE_GROUP = .IPV6_LEAVE_GROUP, /// Drop an IP group membership - IPV6_V6ONLY = .IPV6_V6ONLY, /// Treat wildcard bind as AF_INET6-only -} - - -/** - * Class that creates a network communication endpoint using - * the Berkeley sockets interface. - */ -class Socket -{ -private: - socket_t sock; - AddressFamily _family; - - version (Windows) - bool _blocking = true; /// Property to get or set whether the socket is blocking or nonblocking. - - // The WinSock timeouts seem to be effectively skewed by a constant - // offset of about half a second (value in milliseconds). This has - // been confirmed on updated (as of Jun 2011) Windows XP, Windows 7 - // and Windows Server 2008 R2 boxes. The unittest below tests this - // behavior. - enum WINSOCK_TIMEOUT_SKEW = 500; - - @safe unittest - { - if (runSlowTests) - softUnittest({ - import std.datetime.stopwatch : StopWatch; - import std.typecons : Yes; - - enum msecs = 1000; - auto pair = socketPair(); - auto testSock = pair[0]; - testSock.setOption(SocketOptionLevel.SOCKET, - SocketOption.RCVTIMEO, dur!"msecs"(msecs)); - - auto sw = StopWatch(Yes.autoStart); - ubyte[1] buf; - testSock.receive(buf); - sw.stop(); - - Duration readBack = void; - testSock.getOption(SocketOptionLevel.SOCKET, SocketOption.RCVTIMEO, readBack); - - assert(readBack.total!"msecs" == msecs); - assert(sw.peek().total!"msecs" > msecs - 100 && sw.peek().total!"msecs" < msecs + 100); - }); - } - - void setSock(socket_t handle) - { - assert(handle != socket_t.init); - sock = handle; - - // Set the option to disable SIGPIPE on send() if the platform - // has it (e.g. on OS X). - static if (is(typeof(SO_NOSIGPIPE))) - { - setOption(SocketOptionLevel.SOCKET, cast(SocketOption) SO_NOSIGPIPE, true); - } - } - - - // For use with accepting(). - protected this() pure nothrow @nogc - { - } - - -public: - - /** - * Create a blocking socket. If a single protocol type exists to support - * this socket type within the address family, the `ProtocolType` may be - * omitted. - */ - this(AddressFamily af, SocketType type, ProtocolType protocol) @trusted - { - _family = af; - auto handle = cast(socket_t) socket(af, type, protocol); - if (handle == socket_t.init) - throw new SocketOSException("Unable to create socket"); - setSock(handle); - } - - /// ditto - this(AddressFamily af, SocketType type) - { - /* A single protocol exists to support this socket type within the - * protocol family, so the ProtocolType is assumed. - */ - this(af, type, cast(ProtocolType) 0); // Pseudo protocol number. - } - - - /// ditto - this(AddressFamily af, SocketType type, scope const(char)[] protocolName) @trusted - { - protoent* proto; - proto = getprotobyname(protocolName.tempCString()); - if (!proto) - throw new SocketOSException("Unable to find the protocol"); - this(af, type, cast(ProtocolType) proto.p_proto); - } - - - /** - * Create a blocking socket using the parameters from the specified - * `AddressInfo` structure. - */ - this(const scope AddressInfo info) - { - this(info.family, info.type, info.protocol); - } - - /// Use an existing socket handle. - this(socket_t sock, AddressFamily af) pure nothrow @nogc - { - assert(sock != socket_t.init); - this.sock = sock; - this._family = af; - } - - - ~this() nothrow @nogc - { - close(); - } - - - /// Get underlying socket handle. - @property socket_t handle() const pure nothrow @nogc - { - return sock; - } - - /** - * Releases the underlying socket handle from the Socket object. Once it - * is released, you cannot use the Socket object's methods anymore. This - * also means the Socket destructor will no longer close the socket - it - * becomes your responsibility. - * - * To get the handle without releasing it, use the `handle` property. - */ - @property socket_t release() pure nothrow @nogc - { - auto h = sock; - this.sock = socket_t.init; - return h; - } - - /** - * Get/set socket's blocking flag. - * - * When a socket is blocking, calls to receive(), accept(), and send() - * will block and wait for data/action. - * A non-blocking socket will immediately return instead of blocking. - */ - @property bool blocking() @trusted const nothrow @nogc - { - version (Windows) - { - return _blocking; - } - else version (Posix) - { - return !(fcntl(handle, F_GETFL, 0) & O_NONBLOCK); - } - } - - /// ditto - @property void blocking(bool byes) @trusted - { - version (Windows) - { - uint num = !byes; - if (_SOCKET_ERROR == ioctlsocket(sock, FIONBIO, &num)) - goto err; - _blocking = byes; - } - else version (Posix) - { - int x = fcntl(sock, F_GETFL, 0); - if (-1 == x) - goto err; - if (byes) - x &= ~O_NONBLOCK; - else - x |= O_NONBLOCK; - if (-1 == fcntl(sock, F_SETFL, x)) - goto err; - } - return; // Success. - - err: - throw new SocketOSException("Unable to set socket blocking"); - } - - - /// Get the socket's address family. - @property AddressFamily addressFamily() - { - return _family; - } - - /// Property that indicates if this is a valid, alive socket. - @property bool isAlive() @trusted const - { - int type; - socklen_t typesize = cast(socklen_t) type.sizeof; - return !getsockopt(sock, SOL_SOCKET, SO_TYPE, cast(char*)&type, &typesize); - } - - /** - * Associate a local address with this socket. - * - * Params: - * addr = The $(LREF Address) to associate this socket with. - * - * Throws: $(LREF SocketOSException) when unable to bind the socket. - */ - void bind(Address addr) @trusted - { - if (_SOCKET_ERROR == .bind(sock, addr.name, addr.nameLen)) - throw new SocketOSException("Unable to bind socket"); - } - - /** - * Establish a connection. If the socket is blocking, connect waits for - * the connection to be made. If the socket is nonblocking, connect - * returns immediately and the connection attempt is still in progress. - */ - void connect(Address to) @trusted - { - if (_SOCKET_ERROR == .connect(sock, to.name, to.nameLen)) - { - int err; - err = _lasterr(); - - if (!blocking) - { - version (Windows) - { - if (WSAEWOULDBLOCK == err) - return; - } - else version (Posix) - { - if (EINPROGRESS == err) - return; - } - else - { - static assert(0); - } - } - throw new SocketOSException("Unable to connect socket", err); - } - } - - /** - * Listen for an incoming connection. `bind` must be called before you - * can `listen`. The `backlog` is a request of how many pending - * incoming connections are queued until `accept`ed. - */ - void listen(int backlog) @trusted - { - if (_SOCKET_ERROR == .listen(sock, backlog)) - throw new SocketOSException("Unable to listen on socket"); - } - - /** - * Called by `accept` when a new `Socket` must be created for a new - * connection. To use a derived class, override this method and return an - * instance of your class. The returned `Socket`'s handle must not be - * set; `Socket` has a protected constructor `this()` to use in this - * situation. - * - * Override to use a derived class. - * The returned socket's handle must not be set. - */ - protected Socket accepting() pure nothrow - { - return new Socket; - } - - /** - * Accept an incoming connection. If the socket is blocking, `accept` - * waits for a connection request. Throws `SocketAcceptException` if - * unable to _accept. See `accepting` for use with derived classes. - */ - Socket accept() @trusted - { - auto newsock = cast(socket_t).accept(sock, null, null); - if (socket_t.init == newsock) - throw new SocketAcceptException("Unable to accept socket connection"); - - Socket newSocket; - try - { - newSocket = accepting(); - assert(newSocket.sock == socket_t.init); - - newSocket.setSock(newsock); - version (Windows) - newSocket._blocking = _blocking; //inherits blocking mode - newSocket._family = _family; //same family - } - catch (Throwable o) - { - _close(newsock); - throw o; - } - - return newSocket; - } - - /// Disables sends and/or receives. - void shutdown(SocketShutdown how) @trusted nothrow @nogc - { - .shutdown(sock, cast(int) how); - } - - - private static void _close(socket_t sock) @system nothrow @nogc - { - version (Windows) - { - .closesocket(sock); - } - else version (Posix) - { - .close(sock); - } - } - - - /** - * Immediately drop any connections and release socket resources. - * The `Socket` object is no longer usable after `close`. - * Calling `shutdown` before `close` is recommended - * for connection-oriented sockets. - */ - void close() scope @trusted nothrow @nogc - { - _close(sock); - sock = socket_t.init; - } - - - /** - * Returns: The local machine's host name - */ - static @property string hostName() @trusted // getter - { - char[256] result; // Host names are limited to 255 chars. - if (_SOCKET_ERROR == .gethostname(result.ptr, result.length)) - throw new SocketOSException("Unable to obtain host name"); - return to!string(result.ptr); - } - - /// Remote endpoint `Address`. - @property Address remoteAddress() @trusted - { - Address addr = createAddress(); - socklen_t nameLen = addr.nameLen; - if (_SOCKET_ERROR == .getpeername(sock, addr.name, &nameLen)) - throw new SocketOSException("Unable to obtain remote socket address"); - addr.setNameLen(nameLen); - assert(addr.addressFamily == _family); - return addr; - } - - /// Local endpoint `Address`. - @property Address localAddress() @trusted - { - Address addr = createAddress(); - socklen_t nameLen = addr.nameLen; - if (_SOCKET_ERROR == .getsockname(sock, addr.name, &nameLen)) - throw new SocketOSException("Unable to obtain local socket address"); - addr.setNameLen(nameLen); - assert(addr.addressFamily == _family); - return addr; - } - - /** - * Send or receive error code. See `wouldHaveBlocked`, - * `lastSocketError` and `Socket.getErrorText` for obtaining more - * information about the error. - */ - enum int ERROR = _SOCKET_ERROR; - - private static int capToInt(size_t size) nothrow @nogc - { - // Windows uses int instead of size_t for length arguments. - // Luckily, the send/recv functions make no guarantee that - // all the data is sent, so we use that to send at most - // int.max bytes. - return size > size_t(int.max) ? int.max : cast(int) size; - } - - /** - * Send data on the connection. If the socket is blocking and there is no - * buffer space left, `send` waits. - * Returns: The number of bytes actually sent, or `Socket.ERROR` on - * failure. - */ - ptrdiff_t send(scope const(void)[] buf, SocketFlags flags) @trusted - { - static if (is(typeof(MSG_NOSIGNAL))) - { - flags = cast(SocketFlags)(flags | MSG_NOSIGNAL); - } - version (Windows) - auto sent = .send(sock, buf.ptr, capToInt(buf.length), cast(int) flags); - else - auto sent = .send(sock, buf.ptr, buf.length, cast(int) flags); - return sent; - } - - /// ditto - ptrdiff_t send(scope const(void)[] buf) - { - return send(buf, SocketFlags.NONE); - } - - /** - * Send data to a specific destination Address. If the destination address is - * not specified, a connection must have been made and that address is used. - * If the socket is blocking and there is no buffer space left, `sendTo` waits. - * Returns: The number of bytes actually sent, or `Socket.ERROR` on - * failure. - */ - ptrdiff_t sendTo(scope const(void)[] buf, SocketFlags flags, Address to) @trusted - { - static if (is(typeof(MSG_NOSIGNAL))) - { - flags = cast(SocketFlags)(flags | MSG_NOSIGNAL); - } - version (Windows) - return .sendto( - sock, buf.ptr, capToInt(buf.length), - cast(int) flags, to.name, to.nameLen - ); - else - return .sendto(sock, buf.ptr, buf.length, cast(int) flags, to.name, to.nameLen); - } - - /// ditto - ptrdiff_t sendTo(scope const(void)[] buf, Address to) - { - return sendTo(buf, SocketFlags.NONE, to); - } - - - //assumes you connect()ed - /// ditto - ptrdiff_t sendTo(scope const(void)[] buf, SocketFlags flags) @trusted - { - static if (is(typeof(MSG_NOSIGNAL))) - { - flags = cast(SocketFlags)(flags | MSG_NOSIGNAL); - } - version (Windows) - return .sendto(sock, buf.ptr, capToInt(buf.length), cast(int) flags, null, 0); - else - return .sendto(sock, buf.ptr, buf.length, cast(int) flags, null, 0); - } - - - //assumes you connect()ed - /// ditto - ptrdiff_t sendTo(scope const(void)[] buf) - { - return sendTo(buf, SocketFlags.NONE); - } - - - /** - * Receive data on the connection. If the socket is blocking, `receive` - * waits until there is data to be received. - * Returns: The number of bytes actually received, `0` if the remote side - * has closed the connection, or `Socket.ERROR` on failure. - */ - ptrdiff_t receive(scope void[] buf, SocketFlags flags) @trusted - { - version (Windows) // Does not use size_t - { - return buf.length - ? .recv(sock, buf.ptr, capToInt(buf.length), cast(int) flags) - : 0; - } - else - { - return buf.length - ? .recv(sock, buf.ptr, buf.length, cast(int) flags) - : 0; - } - } - - /// ditto - ptrdiff_t receive(scope void[] buf) - { - return receive(buf, SocketFlags.NONE); - } - - /** - * Receive data and get the remote endpoint `Address`. - * If the socket is blocking, `receiveFrom` waits until there is data to - * be received. - * Returns: The number of bytes actually received, `0` if the remote side - * has closed the connection, or `Socket.ERROR` on failure. - */ - ptrdiff_t receiveFrom(scope void[] buf, SocketFlags flags, ref Address from) @trusted - { - if (!buf.length) //return 0 and don't think the connection closed - return 0; - if (from is null || from.addressFamily != _family) - from = createAddress(); - socklen_t nameLen = from.nameLen; - version (Windows) - auto read = .recvfrom(sock, buf.ptr, capToInt(buf.length), cast(int) flags, from.name, &nameLen); - - else - auto read = .recvfrom(sock, buf.ptr, buf.length, cast(int) flags, from.name, &nameLen); - - if (read >= 0) - { - from.setNameLen(nameLen); - assert(from.addressFamily == _family); - } - return read; - } - - - /// ditto - ptrdiff_t receiveFrom(scope void[] buf, ref Address from) - { - return receiveFrom(buf, SocketFlags.NONE, from); - } - - - //assumes you connect()ed - /// ditto - ptrdiff_t receiveFrom(scope void[] buf, SocketFlags flags) @trusted - { - if (!buf.length) //return 0 and don't think the connection closed - return 0; - version (Windows) - { - auto read = .recvfrom(sock, buf.ptr, capToInt(buf.length), cast(int) flags, null, null); - // if (!read) //connection closed - return read; - } - else - { - auto read = .recvfrom(sock, buf.ptr, buf.length, cast(int) flags, null, null); - // if (!read) //connection closed - return read; - } - } - - - //assumes you connect()ed - /// ditto - ptrdiff_t receiveFrom(scope void[] buf) - { - return receiveFrom(buf, SocketFlags.NONE); - } - - - /** - * Get a socket option. - * Returns: The number of bytes written to `result`. - * The length, in bytes, of the actual result - very different from getsockopt() - */ - int getOption(SocketOptionLevel level, SocketOption option, scope void[] result) @trusted - { - socklen_t len = cast(socklen_t) result.length; - if (_SOCKET_ERROR == .getsockopt(sock, cast(int) level, cast(int) option, result.ptr, &len)) - throw new SocketOSException("Unable to get socket option"); - return len; - } - - - /// Common case of getting integer and boolean options. - int getOption(SocketOptionLevel level, SocketOption option, out int32_t result) @trusted - { - return getOption(level, option, (&result)[0 .. 1]); - } - - - /// Get the linger option. - int getOption(SocketOptionLevel level, SocketOption option, out Linger result) @trusted - { - //return getOption(cast(SocketOptionLevel) SocketOptionLevel.SOCKET, SocketOption.LINGER, (&result)[0 .. 1]); - return getOption(level, option, (&result.clinger)[0 .. 1]); - } - - /// Get a timeout (duration) option. - void getOption(SocketOptionLevel level, SocketOption option, out Duration result) @trusted - { - enforce(option == SocketOption.SNDTIMEO || option == SocketOption.RCVTIMEO, - new SocketParameterException("Not a valid timeout option: " ~ to!string(option))); - // WinSock returns the timeout values as a milliseconds DWORD, - // while Linux and BSD return a timeval struct. - version (Windows) - { - int msecs; - getOption(level, option, (&msecs)[0 .. 1]); - if (option == SocketOption.RCVTIMEO) - msecs += WINSOCK_TIMEOUT_SKEW; - result = dur!"msecs"(msecs); - } - else version (Posix) - { - TimeVal tv; - getOption(level, option, (&tv.ctimeval)[0 .. 1]); - result = dur!"seconds"(tv.seconds) + dur!"usecs"(tv.microseconds); - } - else static assert(false); - } - - /// Set a socket option. - void setOption(SocketOptionLevel level, SocketOption option, scope void[] value) @trusted - { - if (_SOCKET_ERROR == .setsockopt(sock, cast(int) level, - cast(int) option, value.ptr, cast(uint) value.length)) - throw new SocketOSException("Unable to set socket option"); - } - - - /// Common case for setting integer and boolean options. - void setOption(SocketOptionLevel level, SocketOption option, int32_t value) @trusted - { - setOption(level, option, (&value)[0 .. 1]); - } - - - /// Set the linger option. - void setOption(SocketOptionLevel level, SocketOption option, Linger value) @trusted - { - //setOption(cast(SocketOptionLevel) SocketOptionLevel.SOCKET, SocketOption.LINGER, (&value)[0 .. 1]); - setOption(level, option, (&value.clinger)[0 .. 1]); - } - - /** - * Sets a timeout (duration) option, i.e. `SocketOption.SNDTIMEO` or - * `RCVTIMEO`. Zero indicates no timeout. - * - * In a typical application, you might also want to consider using - * a non-blocking socket instead of setting a timeout on a blocking one. - * - * Note: While the receive timeout setting is generally quite accurate - * on *nix systems even for smaller durations, there are two issues to - * be aware of on Windows: First, although undocumented, the effective - * timeout duration seems to be the one set on the socket plus half - * a second. `setOption()` tries to compensate for that, but still, - * timeouts under 500ms are not possible on Windows. Second, be aware - * that the actual amount of time spent until a blocking call returns - * randomly varies on the order of 10ms. - * - * Params: - * level = The level at which a socket option is defined. - * option = Either `SocketOption.SNDTIMEO` or `SocketOption.RCVTIMEO`. - * value = The timeout duration to set. Must not be negative. - * - * Throws: `SocketException` if setting the options fails. - * - * Example: - * --- - * import std.datetime; - * import std.typecons; - * auto pair = socketPair(); - * scope(exit) foreach (s; pair) s.close(); - * - * // Set a receive timeout, and then wait at one end of - * // the socket pair, knowing that no data will arrive. - * pair[0].setOption(SocketOptionLevel.SOCKET, - * SocketOption.RCVTIMEO, dur!"seconds"(1)); - * - * auto sw = StopWatch(Yes.autoStart); - * ubyte[1] buffer; - * pair[0].receive(buffer); - * writefln("Waited %s ms until the socket timed out.", - * sw.peek.msecs); - * --- - */ - void setOption(SocketOptionLevel level, SocketOption option, Duration value) @trusted - { - enforce(option == SocketOption.SNDTIMEO || option == SocketOption.RCVTIMEO, - new SocketParameterException("Not a valid timeout option: " ~ to!string(option))); - - enforce(value >= dur!"hnsecs"(0), new SocketParameterException( - "Timeout duration must not be negative.")); - - version (Windows) - { - import std.algorithm.comparison : max; - - auto msecs = to!int(value.total!"msecs"); - if (msecs != 0 && option == SocketOption.RCVTIMEO) - msecs = max(1, msecs - WINSOCK_TIMEOUT_SKEW); - setOption(level, option, msecs); - } - else version (Posix) - { - _ctimeval tv; - value.split!("seconds", "usecs")(tv.tv_sec, tv.tv_usec); - setOption(level, option, (&tv)[0 .. 1]); - } - else static assert(false); - } - - /** - * Get a text description of this socket's error status, and clear the - * socket's error status. - */ - string getErrorText() - { - int32_t error; - getOption(SocketOptionLevel.SOCKET, SocketOption.ERROR, error); - return formatSocketError(error); - } - - /** - * Enables TCP keep-alive with the specified parameters. - * - * Params: - * time = Number of seconds with no activity until the first - * keep-alive packet is sent. - * interval = Number of seconds between when successive keep-alive - * packets are sent if no acknowledgement is received. - * - * Throws: `SocketOSException` if setting the options fails, or - * `SocketFeatureException` if setting keep-alive parameters is - * unsupported on the current platform. - */ - void setKeepAlive(int time, int interval) @trusted - { - version (Windows) - { - tcp_keepalive options; - options.onoff = 1; - options.keepalivetime = time * 1000; - options.keepaliveinterval = interval * 1000; - uint cbBytesReturned; - enforce(WSAIoctl(sock, SIO_KEEPALIVE_VALS, - &options, options.sizeof, - null, 0, - &cbBytesReturned, null, null) == 0, - new SocketOSException("Error setting keep-alive")); - } - else - static if (is(typeof(TCP_KEEPIDLE)) && is(typeof(TCP_KEEPINTVL))) - { - setOption(SocketOptionLevel.TCP, cast(SocketOption) TCP_KEEPIDLE, time); - setOption(SocketOptionLevel.TCP, cast(SocketOption) TCP_KEEPINTVL, interval); - setOption(SocketOptionLevel.SOCKET, SocketOption.KEEPALIVE, true); - } - else - throw new SocketFeatureException("Setting keep-alive options " ~ - "is not supported on this platform"); - } - - /** - * Wait for a socket to change status. A wait timeout of $(REF Duration, core, time) or - * `TimeVal`, may be specified; if a timeout is not specified or the - * `TimeVal` is `null`, the maximum timeout is used. The `TimeVal` - * timeout has an unspecified value when `select` returns. - * Returns: The number of sockets with status changes, `0` on timeout, - * or `-1` on interruption. If the return value is greater than `0`, - * the `SocketSets` are updated to only contain the sockets having status - * changes. For a connecting socket, a write status change means the - * connection is established and it's able to send. For a listening socket, - * a read status change means there is an incoming connection request and - * it's able to accept. - * - * `SocketSet`'s updated to include only those sockets which an event occured. - * For a `connect()`ing socket, writeability means connected. - * For a `listen()`ing socket, readability means listening - * `Winsock`; possibly internally limited to 64 sockets per set. - * - * Returns: - * the number of events, 0 on timeout, or -1 on interruption - */ - static int select(SocketSet checkRead, SocketSet checkWrite, SocketSet checkError, Duration timeout) @trusted - { - auto vals = timeout.split!("seconds", "usecs")(); - TimeVal tv; - tv.seconds = cast(tv.tv_sec_t ) vals.seconds; - tv.microseconds = cast(tv.tv_usec_t) vals.usecs; - return select(checkRead, checkWrite, checkError, &tv); - } - - /// ditto - //maximum timeout - static int select(SocketSet checkRead, SocketSet checkWrite, SocketSet checkError) - { - return select(checkRead, checkWrite, checkError, null); - } - - /// Ditto - static int select(SocketSet checkRead, SocketSet checkWrite, SocketSet checkError, TimeVal* timeout) @trusted - in - { - //make sure none of the SocketSet's are the same object - if (checkRead) - { - assert(checkRead !is checkWrite); - assert(checkRead !is checkError); - } - if (checkWrite) - { - assert(checkWrite !is checkError); - } - } - do - { - fd_set* fr, fw, fe; - int n = 0; - - version (Windows) - { - // Windows has a problem with empty fd_set`s that aren't null. - fr = checkRead && checkRead.count ? checkRead.toFd_set() : null; - fw = checkWrite && checkWrite.count ? checkWrite.toFd_set() : null; - fe = checkError && checkError.count ? checkError.toFd_set() : null; - } - else - { - if (checkRead) - { - fr = checkRead.toFd_set(); - n = checkRead.selectn(); - } - else - { - fr = null; - } - - if (checkWrite) - { - fw = checkWrite.toFd_set(); - int _n; - _n = checkWrite.selectn(); - if (_n > n) - n = _n; - } - else - { - fw = null; - } - - if (checkError) - { - fe = checkError.toFd_set(); - int _n; - _n = checkError.selectn(); - if (_n > n) - n = _n; - } - else - { - fe = null; - } - - // Make sure the sets' capacity matches, to avoid select reading - // out of bounds just because one set was bigger than another - if (checkRead ) checkRead .setMinCapacity(n); - if (checkWrite) checkWrite.setMinCapacity(n); - if (checkError) checkError.setMinCapacity(n); - } - - int result = .select(n, fr, fw, fe, &timeout.ctimeval); - - version (Windows) - { - if (_SOCKET_ERROR == result && WSAGetLastError() == WSAEINTR) - return -1; - } - else version (Posix) - { - if (_SOCKET_ERROR == result && errno == EINTR) - return -1; - } - else - { - static assert(0); - } - - if (_SOCKET_ERROR == result) - throw new SocketOSException("Socket select error"); - - return result; - } - - - /** - * Can be overridden to support other addresses. - * Returns: A new `Address` object for the current address family. - */ - protected Address createAddress() pure nothrow - { - Address result; - switch (_family) - { - static if (is(sockaddr_un)) - { - case AddressFamily.UNIX: - result = new UnixAddress; - break; - } - - case AddressFamily.INET: - result = new InternetAddress; - break; - - case AddressFamily.INET6: - result = new Internet6Address; - break; - - default: - result = new UnknownAddress; - } - return result; - } - -} - - -/// Shortcut class for a TCP Socket. -class TcpSocket: Socket -{ - /// Constructs a blocking TCP Socket. - this(AddressFamily family) - { - super(family, SocketType.STREAM, ProtocolType.TCP); - } - - /// Constructs a blocking IPv4 TCP Socket. - this() - { - this(AddressFamily.INET); - } - - - //shortcut - /// Constructs a blocking TCP Socket and connects to the given `Address`. - this(Address connectTo) - { - this(connectTo.addressFamily); - connect(connectTo); - } -} - - -/// Shortcut class for a UDP Socket. -class UdpSocket: Socket -{ - /// Constructs a blocking UDP Socket. - this(AddressFamily family) - { - super(family, SocketType.DGRAM, ProtocolType.UDP); - } - - - /// Constructs a blocking IPv4 UDP Socket. - this() - { - this(AddressFamily.INET); - } -} - -@safe unittest -{ - byte[] buf; - buf.length = 1; - Address addr; - auto s = new UdpSocket; - s.blocking = false; - s.bind(new InternetAddress(InternetAddress.PORT_ANY)); - s.receiveFrom(buf, addr); -} - -// https://issues.dlang.org/show_bug.cgi?id=16514 -@safe unittest -{ - void checkAttributes(string attributes)() - { - mixin(attributes ~ q{ void function() fun = {};}); - fun(); - } - - class TestSocket : Socket - { - override - { - @property pure nothrow @nogc @safe socket_t handle() const - { - checkAttributes!q{pure nothrow @nogc @safe}; assert(0); - } - @property nothrow @nogc @trusted bool blocking() const - { - checkAttributes!q{nothrow @nogc @trusted}; assert(0); - } - @property @trusted void blocking(bool byes) - { - checkAttributes!q{@trusted}; - } - @property @safe AddressFamily addressFamily() - { - checkAttributes!q{@safe}; assert(0); - } - @property @trusted bool isAlive() const - { - checkAttributes!q{@trusted}; assert(0); - } - @trusted void bind(Address addr) - { - checkAttributes!q{@trusted}; - } - @trusted void connect(Address to) - { - checkAttributes!q{@trusted}; - } - @trusted void listen(int backlog) - { - checkAttributes!q{@trusted}; - } - protected pure nothrow @safe Socket accepting() - { - checkAttributes!q{pure nothrow @safe}; assert(0); - } - @trusted Socket accept() - { - checkAttributes!q{@trusted}; assert(0); - } - nothrow @nogc @trusted void shutdown(SocketShutdown how) - { - checkAttributes!q{nothrow @nogc @trusted}; - } - nothrow @nogc @trusted scope void close() - { - checkAttributes!q{nothrow @nogc @trusted}; - } - @property @trusted Address remoteAddress() - { - checkAttributes!q{@trusted}; assert(0); - } - @property @trusted Address localAddress() - { - checkAttributes!q{@trusted}; assert(0); - } - @trusted ptrdiff_t send(scope const(void)[] buf, SocketFlags flags) - { - checkAttributes!q{@trusted}; assert(0); - } - @safe ptrdiff_t send(scope const(void)[] buf) - { - checkAttributes!q{@safe}; assert(0); - } - @trusted ptrdiff_t sendTo(scope const(void)[] buf, SocketFlags flags, Address to) - { - checkAttributes!q{@trusted}; assert(0); - } - @safe ptrdiff_t sendTo(scope const(void)[] buf, Address to) - { - checkAttributes!q{@safe}; assert(0); - } - @trusted ptrdiff_t sendTo(scope const(void)[] buf, SocketFlags flags) - { - checkAttributes!q{@trusted}; assert(0); - } - @safe ptrdiff_t sendTo(scope const(void)[] buf) - { - checkAttributes!q{@safe}; assert(0); - } - @trusted ptrdiff_t receive(scope void[] buf, SocketFlags flags) - { - checkAttributes!q{@trusted}; assert(0); - } - @safe ptrdiff_t receive(scope void[] buf) - { - checkAttributes!q{@safe}; assert(0); - } - @trusted ptrdiff_t receiveFrom(scope void[] buf, SocketFlags flags, ref Address from) - { - checkAttributes!q{@trusted}; assert(0); - } - @safe ptrdiff_t receiveFrom(scope void[] buf, ref Address from) - { - checkAttributes!q{@safe}; assert(0); - } - @trusted ptrdiff_t receiveFrom(scope void[] buf, SocketFlags flags) - { - checkAttributes!q{@trusted}; assert(0); - } - @safe ptrdiff_t receiveFrom(scope void[] buf) - { - checkAttributes!q{@safe}; assert(0); - } - @trusted int getOption(SocketOptionLevel level, SocketOption option, scope void[] result) - { - checkAttributes!q{@trusted}; assert(0); - } - @trusted int getOption(SocketOptionLevel level, SocketOption option, out int32_t result) - { - checkAttributes!q{@trusted}; assert(0); - } - @trusted int getOption(SocketOptionLevel level, SocketOption option, out Linger result) - { - checkAttributes!q{@trusted}; assert(0); - } - @trusted void getOption(SocketOptionLevel level, SocketOption option, out Duration result) - { - checkAttributes!q{@trusted}; - } - @trusted void setOption(SocketOptionLevel level, SocketOption option, scope void[] value) - { - checkAttributes!q{@trusted}; - } - @trusted void setOption(SocketOptionLevel level, SocketOption option, int32_t value) - { - checkAttributes!q{@trusted}; - } - @trusted void setOption(SocketOptionLevel level, SocketOption option, Linger value) - { - checkAttributes!q{@trusted}; - } - @trusted void setOption(SocketOptionLevel level, SocketOption option, Duration value) - { - checkAttributes!q{@trusted}; - } - @safe string getErrorText() - { - checkAttributes!q{@safe}; assert(0); - } - @trusted void setKeepAlive(int time, int interval) - { - checkAttributes!q{@trusted}; - } - protected pure nothrow @safe Address createAddress() - { - checkAttributes!q{pure nothrow @safe}; assert(0); - } - } - } -} - -/** - * Creates a pair of connected sockets. - * - * The two sockets are indistinguishable. - * - * Throws: `SocketException` if creation of the sockets fails. - */ -Socket[2] socketPair() @trusted -{ - version (Posix) - { - int[2] socks; - if (socketpair(AF_UNIX, SOCK_STREAM, 0, socks) == -1) - throw new SocketOSException("Unable to create socket pair"); - - Socket toSocket(size_t id) - { - auto s = new Socket; - s.setSock(cast(socket_t) socks[id]); - s._family = AddressFamily.UNIX; - return s; - } - - return [toSocket(0), toSocket(1)]; - } - else version (Windows) - { - // We do not have socketpair() on Windows, just manually create a - // pair of sockets connected over some localhost port. - Socket[2] result; - - auto listener = new TcpSocket(); - listener.setOption(SocketOptionLevel.SOCKET, SocketOption.REUSEADDR, true); - listener.bind(new InternetAddress(INADDR_LOOPBACK, InternetAddress.PORT_ANY)); - auto addr = listener.localAddress; - listener.listen(1); - - result[0] = new TcpSocket(addr); - result[1] = listener.accept(); - - listener.close(); - return result; - } - else - static assert(false); -} - -/// -@safe unittest -{ - immutable ubyte[4] data = [1, 2, 3, 4]; - auto pair = socketPair(); - scope(exit) foreach (s; pair) s.close(); - - pair[0].send(data[]); - - auto buf = new ubyte[data.length]; - pair[1].receive(buf); - assert(buf == data); -} diff --git a/phobos/std/stdint.d b/phobos/std/stdint.d deleted file mode 100644 index 88f4a22..0000000 --- a/phobos/std/stdint.d +++ /dev/null @@ -1,131 +0,0 @@ -// Written in the D programming language. - -/** - * - D constrains integral types to specific sizes. But efficiency - of different sizes varies from machine to machine, - pointer sizes vary, and the maximum integer size varies. - <b>stdint</b> offers a portable way of trading off size - vs efficiency, in a manner compatible with the <tt>stdint.h</tt> - definitions in C. - - In the table below, the $(B exact alias)es are types of exactly the - specified number of bits. - The $(B at least alias)es are at least the specified number of bits - large, and can be larger. - The $(B fast alias)es are the fastest integral type supported by the - processor that is at least as wide as the specified number of bits. - - The aliases are: - - $(ATABLE $(TR - $(TH Exact Alias) - $(TH Description) - $(TH At Least Alias) - $(TH Description) - $(TH Fast Alias) - $(TH Description) - )$(TR - $(TD int8_t) - $(TD exactly 8 bits signed) - $(TD int_least8_t) - $(TD at least 8 bits signed) - $(TD int_fast8_t) - $(TD fast 8 bits signed) - )$(TR - $(TD uint8_t) - $(TD exactly 8 bits unsigned) - $(TD uint_least8_t) - $(TD at least 8 bits unsigned) - $(TD uint_fast8_t) - $(TD fast 8 bits unsigned) - - )$(TR - $(TD int16_t) - $(TD exactly 16 bits signed) - $(TD int_least16_t) - $(TD at least 16 bits signed) - $(TD int_fast16_t) - $(TD fast 16 bits signed) - )$(TR - $(TD uint16_t) - $(TD exactly 16 bits unsigned) - $(TD uint_least16_t) - $(TD at least 16 bits unsigned) - $(TD uint_fast16_t) - $(TD fast 16 bits unsigned) - - )$(TR - $(TD int32_t) - $(TD exactly 32 bits signed) - $(TD int_least32_t) - $(TD at least 32 bits signed) - $(TD int_fast32_t) - $(TD fast 32 bits signed) - )$(TR - $(TD uint32_t) - $(TD exactly 32 bits unsigned) - $(TD uint_least32_t) - $(TD at least 32 bits unsigned) - $(TD uint_fast32_t) - $(TD fast 32 bits unsigned) - - )$(TR - $(TD int64_t) - $(TD exactly 64 bits signed) - $(TD int_least64_t) - $(TD at least 64 bits signed) - $(TD int_fast64_t) - $(TD fast 64 bits signed) - )$(TR - $(TD uint64_t) - $(TD exactly 64 bits unsigned) - $(TD uint_least64_t) - $(TD at least 64 bits unsigned) - $(TD uint_fast64_t) - $(TD fast 64 bits unsigned) - )) - - The ptr aliases are integral types guaranteed to be large enough - to hold a pointer without losing bits: - - $(ATABLE $(TR - $(TH Alias) - $(TH Description) - )$(TR - $(TD intptr_t) - $(TD signed integral type large enough to hold a pointer) - )$(TR - $(TD uintptr_t) - $(TD unsigned integral type large enough to hold a pointer) - )) - - The max aliases are the largest integral types: - - $(ATABLE $(TR - $(TH Alias) - $(TH Description) - )$(TR - $(TD intmax_t) - $(TD the largest signed integral type) - )$(TR - $(TD uintmax_t) - $(TD the largest unsigned integral type) - )) - - * Macros: - * ATABLE=<table border="1" cellspacing="0" cellpadding="5">$0</table> - * - * Copyright: Copyright The D Language Foundation 2000 - 2009. - * License: $(HTTP www.boost.org/LICENSE_1_0.txt, Boost License 1.0). - * Authors: $(HTTP digitalmars.com, Walter Bright) - * Source: $(PHOBOSSRC std/stdint.d) - */ -/* Copyright The D Language Foundation 2000 - 2009. - * 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.stdint; - -public import core.stdc.stdint; diff --git a/phobos/std/stdio.d b/phobos/std/stdio.d deleted file mode 100644 index 761afa7..0000000 --- a/phobos/std/stdio.d +++ /dev/null @@ -1,6064 +0,0 @@ -// Written in the D programming language. - -/** -$(SCRIPT inhibitQuickIndex = 1;) -$(DIVC quickindex, -$(BOOKTABLE, -$(TR $(TH Category) $(TH Symbols)) -$(TR $(TD File handles) $(TD - $(MYREF __popen) - $(MYREF File) - $(MYREF isFileHandle) - $(MYREF openNetwork) - $(MYREF stderr) - $(MYREF stdin) - $(MYREF stdout) -)) -$(TR $(TD Reading) $(TD - $(MYREF chunks) - $(MYREF lines) - $(MYREF readf) - $(MYREF readln) -)) -$(TR $(TD Writing) $(TD - $(MYREF toFile) - $(MYREF write) - $(MYREF writef) - $(MYREF writefln) - $(MYREF writeln) -)) -$(TR $(TD Misc) $(TD - $(MYREF KeepTerminator) - $(MYREF LockType) - $(MYREF StdioException) -)) -)) - -Standard I/O functions that extend $(LINK2 https://dlang.org/phobos/core_stdc_stdio.html, core.stdc.stdio). $(B core.stdc.stdio) -is $(D_PARAM public)ally imported when importing $(B std.stdio). - -There are three layers of I/O: -$(OL -$(LI The lowest layer is the operating system layer. The two main schemes are Windows and Posix.) -$(LI C's $(TT stdio.h) which unifies the two operating system schemes.) -$(LI $(TT std.stdio), this module, unifies the various $(TT stdio.h) implementations into -a high level package for D programs.) -) - -Source: $(PHOBOSSRC std/stdio.d) -Copyright: Copyright The D Language Foundation 2007-. -License: $(HTTP boost.org/LICENSE_1_0.txt, Boost License 1.0). -Authors: $(HTTP digitalmars.com, Walter Bright), - $(HTTP erdani.org, Andrei Alexandrescu), - Alex Rønne Petersen -Macros: -CSTDIO=$(HTTP cplusplus.com/reference/cstdio/$1/, $1) - */ -module std.stdio; - -/* -# Glossary - -The three layers have many terms for their data structures and types. -Here we try to bring some sanity to them for the intrepid code spelunker. - -## Windows - -Handle - - A Windows handle is an opaque object of type HANDLE. - The `HANDLE` for standard devices can be retrieved with - Windows `GetStdHandle()`. - -## Posix - -file descriptor, aka fileno, aka fildes - - An int from 0..`FOPEN_MAX`, which is an index into some internal data - structure. - 0 is for `stdin`, 1 for `stdout`, 2 for `stderr`. - Negative values usually indicate an error. - -## stdio.h - -`FILE` - - A struct that encapsulates the C library's view of the operating system - files. A `FILE` should only be referred to via a pointer. - -`fileno` - - A field of `FILE` which is the Posix file descriptor for Posix systems, and - and an index into an array of file `HANDLE`s for Windows. - This array is how Posix behavior is emulated on Windows. - For Digital Mars C, that array is `__osfhnd[]`, and is initialized - at program start by the C runtime library. - In this module, they are typed as `fileno_t`. - -`stdin`, `stdout`, `stderr` - - Global pointers to `FILE` representing standard input, output, and error streams. - Being global means there are synchronization issues when multiple threads - are doing I/O on the same streams. - -## std.stdio - -*/ - -import core.stdc.stddef : wchar_t; -public import core.stdc.stdio; -import std.algorithm.mutation : copy; -import std.meta : allSatisfy; -import std.range : ElementEncodingType, empty, front, isBidirectionalRange, - isInputRange, isSomeFiniteCharInputRange, put; -import std.traits : isSomeChar, isSomeString, Unqual; -import std.typecons : Flag, No, Yes; - -/++ -If flag `KeepTerminator` is set to `KeepTerminator.yes`, then the delimiter -is included in the strings returned. -+/ -alias KeepTerminator = Flag!"keepTerminator"; - -version (CRuntime_Microsoft) -{ -} -else version (CRuntime_DigitalMars) -{ -} -else version (MinGW) // LDC -{ - version = MINGW_IO; -} -else version (CRuntime_Glibc) -{ -} -else version (CRuntime_Bionic) -{ - version = GENERIC_IO; -} -else version (CRuntime_Musl) -{ - version = GENERIC_IO; -} -else version (CRuntime_UClibc) -{ - version = GENERIC_IO; -} -else version (OSX) -{ - version = GENERIC_IO; - version = Darwin; -} -else version (iOS) -{ - version = GENERIC_IO; - version = Darwin; -} -else version (TVOS) -{ - version = GENERIC_IO; - version = Darwin; -} -else version (WatchOS) -{ - version = GENERIC_IO; - version = Darwin; -} -else version (FreeBSD) -{ - version = GENERIC_IO; -} -else version (NetBSD) -{ - version = GENERIC_IO; -} -else version (OpenBSD) -{ - version = GENERIC_IO; -} -else version (DragonFlyBSD) -{ - version = GENERIC_IO; -} -else version (Solaris) -{ - version = GENERIC_IO; -} -else -{ - static assert(0, "unsupported operating system"); -} - -// Character type used for operating system filesystem APIs -version (Windows) -{ - private alias FSChar = wchar; -} -else -{ - private alias FSChar = char; -} - -private alias fileno_t = int; // file descriptor, fildes, fileno - -version (Windows) -{ - // core.stdc.stdio.fopen expects file names to be - // encoded in CP_ACP on Windows instead of UTF-8. - /+ Waiting for druntime pull 299 - +/ - extern (C) nothrow @nogc FILE* _wfopen(scope const wchar* filename, scope const wchar* mode); - extern (C) nothrow @nogc FILE* _wfreopen(scope const wchar* filename, scope const wchar* mode, FILE* fp); - - import core.sys.windows.basetsd : HANDLE; -} - -version (Posix) -{ - static import core.sys.posix.stdio; // getdelim, flockfile -} - -version (CRuntime_DigitalMars) -{ - private alias _FPUTC = _fputc_nlock; - private alias _FPUTWC = _fputwc_nlock; - private alias _FGETC = _fgetc_nlock; - private alias _FGETWC = _fgetwc_nlock; - private alias _FLOCK = __fp_lock; - private alias _FUNLOCK = __fp_unlock; - - // Alias for CRuntime_Microsoft compatibility. - // @@@DEPRECATED_2.107@@@ - // Rename this back to _setmode once the deprecation phase has ended. - private alias __setmode = setmode; - - // @@@DEPRECATED_2.107@@@ - deprecated("internal alias FPUTC was unintentionally available from " - ~ "std.stdio and will be removed afer 2.107") - alias FPUTC = _fputc_nlock; - // @@@DEPRECATED_2.107@@@ - deprecated("internal alias FPUTWC was unintentionally available from " - ~ "std.stdio and will be removed afer 2.107") - alias FPUTWC = _fputwc_nlock; - // @@@DEPRECATED_2.107@@@ - deprecated("internal alias FGETC was unintentionally available from " - ~ "std.stdio and will be removed afer 2.107") - alias FGETC = _fgetc_nlock; - // @@@DEPRECATED_2.107@@@ - deprecated("internal alias FGETWC was unintentionally available from " - ~ "std.stdio and will be removed afer 2.107") - alias FGETWC = _fgetwc_nlock; - // @@@DEPRECATED_2.107@@@ - deprecated("internal alias FLOCK was unintentionally available from " - ~ "std.stdio and will be removed afer 2.107") - alias FLOCK = __fp_lock; - // @@@DEPRECATED_2.107@@@ - deprecated("internal alias FUNLOCK was unintentionally available from " - ~ "std.stdio and will be removed afer 2.107") - alias FUNLOCK = __fp_unlock; - // @@@DEPRECATED_2.107@@@ - deprecated("internal alias _setmode was unintentionally available from " - ~ "std.stdio and will be removed afer 2.107") - alias _setmode = setmode; - // @@@DEPRECATED_2.107@@@ - deprecated("internal function _fileno was unintentionally available from " - ~ "std.stdio and will be removed afer 2.107") - fileno_t _fileno(FILE* f) { return f._file; } -} -else version (CRuntime_Microsoft) -{ - private alias _FPUTC = _fputc_nolock; - private alias _FPUTWC = _fputwc_nolock; - private alias _FGETC = _fgetc_nolock; - private alias _FGETWC = _fgetwc_nolock; - private alias _FLOCK = _lock_file; - private alias _FUNLOCK = _unlock_file; - - // @@@DEPRECATED_2.107@@@ - // Remove this once the deprecation phase for CRuntime_DigitalMars has ended. - private alias __setmode = _setmode; - - // @@@DEPRECATED_2.107@@@ - deprecated("internal alias FPUTC was unintentionally available from " - ~ "std.stdio and will be removed afer 2.107") - alias FPUTC = _fputc_nolock; - // @@@DEPRECATED_2.107@@@ - deprecated("internal alias FPUTWC was unintentionally available from " - ~ "std.stdio and will be removed afer 2.107") - alias FPUTWC = _fputwc_nolock; - // @@@DEPRECATED_2.107@@@ - deprecated("internal alias FGETC was unintentionally available from " - ~ "std.stdio and will be removed afer 2.107") - alias FGETC = _fgetc_nolock; - // @@@DEPRECATED_2.107@@@ - deprecated("internal alias FGETWC was unintentionally available from " - ~ "std.stdio and will be removed afer 2.107") - alias FGETWC = _fgetwc_nolock; - // @@@DEPRECATED_2.107@@@ - deprecated("internal alias FLOCK was unintentionally available from " - ~ "std.stdio and will be removed afer 2.107") - alias FLOCK = _lock_file; - // @@@DEPRECATED_2.107@@@ - deprecated("internal alias FUNLOCK was unintentionally available from " - ~ "std.stdio and will be removed afer 2.107") - alias FUNLOCK = _unlock_file; -} -else version (CRuntime_Glibc) -{ - private alias _FPUTC = fputc_unlocked; - private alias _FPUTWC = fputwc_unlocked; - private alias _FGETC = fgetc_unlocked; - private alias _FGETWC = fgetwc_unlocked; - private alias _FLOCK = core.sys.posix.stdio.flockfile; - private alias _FUNLOCK = core.sys.posix.stdio.funlockfile; - - // @@@DEPRECATED_2.107@@@ - deprecated("internal alias FPUTC was unintentionally available from " - ~ "std.stdio and will be removed afer 2.107") - alias FPUTC = fputc_unlocked; - // @@@DEPRECATED_2.107@@@ - deprecated("internal alias FPUTWC was unintentionally available from " - ~ "std.stdio and will be removed afer 2.107") - alias FPUTWC = fputwc_unlocked; - // @@@DEPRECATED_2.107@@@ - deprecated("internal alias FGETC was unintentionally available from " - ~ "std.stdio and will be removed afer 2.107") - alias FGETC = fgetc_unlocked; - // @@@DEPRECATED_2.107@@@ - deprecated("internal alias FGETWC was unintentionally available from " - ~ "std.stdio and will be removed afer 2.107") - alias FGETWC = fgetwc_unlocked; - // @@@DEPRECATED_2.107@@@ - deprecated("internal alias FLOCK was unintentionally available from " - ~ "std.stdio and will be removed afer 2.107") - alias FLOCK = core.sys.posix.stdio.flockfile; - // @@@DEPRECATED_2.107@@@ - deprecated("internal alias FUNLOCK was unintentionally available from " - ~ "std.stdio and will be removed afer 2.107") - alias FUNLOCK = core.sys.posix.stdio.funlockfile; -} -else version (MINGW_IO) -{ - extern (C) - { - int setmode(int, int); - } - - import core.sync.mutex; - - __gshared Mutex lockMutex; - __gshared Mutex[uint] fileLocks; - - void flockfile(FILE* fp) - { - Mutex mutex; - - if (lockMutex is null) - lockMutex = new Mutex; - - lockMutex.lock(); - - if (fp._file in fileLocks) - { - mutex = fileLocks[fp._file]; - } - else - { - mutex = new Mutex(); - fileLocks[fp._file] = mutex; - } - mutex.lock(); - - lockMutex.unlock(); - } - - void funlockfile(FILE* fp) - { - Mutex mutex; - - if (lockMutex is null) - lockMutex = new Mutex; - lockMutex.lock(); - - if (fp._file in fileLocks) - { - mutex = fileLocks[fp._file]; - mutex.unlock(); - } else - { /* Should this be an error */ } - lockMutex.unlock(); - } - - - int fputc_unlocked(int c, _iobuf* fp) { return fputc(c, cast(shared) fp); } - int fputwc_unlocked(int c, _iobuf* fp) - { - return fputwc(cast(wchar_t)c, cast(shared) fp); - } - int fgetc_unlocked(_iobuf* fp) { return fgetc(cast(shared) fp); } - int fgetwc_unlocked(_iobuf* fp) { return fgetwc(cast(shared) fp); } - - extern (C) - { - nothrow: - @nogc: - FILE* _fdopen(int, const (char)*); - } - - alias fputc_unlocked FPUTC; - alias fputwc_unlocked FPUTWC; - alias fgetc_unlocked FGETC; - alias fgetwc_unlocked FGETWC; - - alias flockfile FLOCK; - alias funlockfile FUNLOCK; - - alias setmode _setmode; - int _fileno(FILE* f) { return f._file; } - alias _fileno fileno; - - enum - { - _O_RDONLY = 0x0000, - _O_APPEND = 0x0008, - _O_TEXT = 0x4000, - _O_BINARY = 0x8000, - } -} -else version (GENERIC_IO) -{ - nothrow: - @nogc: - - extern (C) private - { - static import core.stdc.wchar_; - - pragma(mangle, fputc.mangleof) int _FPUTC(int c, _iobuf* fp); - pragma(mangle, core.stdc.wchar_.fputwc.mangleof) int _FPUTWC(wchar_t c, _iobuf* fp); - pragma(mangle, fgetc.mangleof) int _FGETC(_iobuf* fp); - pragma(mangle, core.stdc.wchar_.fgetwc.mangleof) int _FGETWC(_iobuf* fp); - } - - version (Posix) - { - private alias _FLOCK = core.sys.posix.stdio.flockfile; - private alias _FUNLOCK = core.sys.posix.stdio.funlockfile; - } - else - { - static assert(0, "don't know how to lock files on GENERIC_IO"); - } - - // @@@DEPRECATED_2.107@@@ - deprecated("internal function fputc_unlocked was unintentionally available " - ~ "from std.stdio and will be removed afer 2.107") - extern (C) pragma(mangle, fputc.mangleof) int fputc_unlocked(int c, _iobuf* fp); - // @@@DEPRECATED_2.107@@@ - deprecated("internal function fputwc_unlocked was unintentionally available " - ~ "from std.stdio and will be removed afer 2.107") - extern (C) pragma(mangle, core.stdc.wchar_.fputwc.mangleof) int fputwc_unlocked(wchar_t c, _iobuf* fp); - // @@@DEPRECATED_2.107@@@ - deprecated("internal function fgetc_unlocked was unintentionally available " - ~ "from std.stdio and will be removed afer 2.107") - extern (C) pragma(mangle, fgetc.mangleof) int fgetc_unlocked(_iobuf* fp); - // @@@DEPRECATED_2.107@@@ - deprecated("internal function fgetwc_unlocked was unintentionally available " - ~ "from std.stdio and will be removed afer 2.107") - extern (C) pragma(mangle, core.stdc.wchar_.fgetwc.mangleof) int fgetwc_unlocked(_iobuf* fp); - - // @@@DEPRECATED_2.107@@@ - deprecated("internal alias FPUTC was unintentionally available from " - ~ "std.stdio and will be removed afer 2.107") - alias FPUTC = fputc_unlocked; - // @@@DEPRECATED_2.107@@@ - deprecated("internal alias FPUTWC was unintentionally available from " - ~ "std.stdio and will be removed afer 2.107") - alias FPUTWC = fputwc_unlocked; - // @@@DEPRECATED_2.107@@@ - deprecated("internal alias FGETC was unintentionally available from " - ~ "std.stdio and will be removed afer 2.107") - alias FGETC = fgetc_unlocked; - // @@@DEPRECATED_2.107@@@ - deprecated("internal alias FGETWC was unintentionally available from " - ~ "std.stdio and will be removed afer 2.107") - alias FGETWC = fgetwc_unlocked; - - version (Posix) - { - // @@@DEPRECATED_2.107@@@ - deprecated("internal alias FLOCK was unintentionally available from " - ~ "std.stdio and will be removed afer 2.107") - alias FLOCK = core.sys.posix.stdio.flockfile; - // @@@DEPRECATED_2.107@@@ - deprecated("internal alias FUNLOCK was unintentionally available from " - ~ "std.stdio and will be removed afer 2.107") - alias FUNLOCK = core.sys.posix.stdio.funlockfile; - } -} -else -{ - static assert(0, "unsupported C I/O system"); -} - -private extern (C) @nogc nothrow -{ - pragma(mangle, _FPUTC.mangleof) int trustedFPUTC(int ch, _iobuf* h) @trusted; - - version (CRuntime_DigitalMars) - pragma(mangle, _FPUTWC.mangleof) int trustedFPUTWC(int ch, _iobuf* h) @trusted; - else - pragma(mangle, _FPUTWC.mangleof) int trustedFPUTWC(wchar_t ch, _iobuf* h) @trusted; -} - -//------------------------------------------------------------------------------ -private struct ByRecordImpl(Fields...) -{ -private: - import std.typecons : Tuple; - - File file; - char[] line; - Tuple!(Fields) current; - string format; - -public: - this(File f, string format) - { - assert(f.isOpen); - file = f; - this.format = format; - popFront(); // prime the range - } - - /// Range primitive implementations. - @property bool empty() - { - return !file.isOpen; - } - - /// Ditto - @property ref Tuple!(Fields) front() - { - return current; - } - - /// Ditto - void popFront() - { - import std.conv : text; - import std.exception : enforce; - import std.format.read : formattedRead; - import std.string : chomp; - - enforce(file.isOpen, "ByRecord: File must be open"); - file.readln(line); - if (!line.length) - { - file.detach(); - } - else - { - line = chomp(line); - formattedRead(line, format, ¤t); - enforce(line.empty, text("Leftover characters in record: `", - line, "'")); - } - } -} - -template byRecord(Fields...) -{ - auto byRecord(File f, string format) - { - return typeof(return)(f, format); - } -} - -/** -Encapsulates a `FILE*`. Generally D does not attempt to provide -thin wrappers over equivalent functions in the C standard library, but -manipulating `FILE*` values directly is unsafe and error-prone in -many ways. The `File` type ensures safe manipulation, automatic -file closing, and a lot of convenience. - -The underlying `FILE*` handle is maintained in a reference-counted -manner, such that as soon as the last `File` variable bound to a -given `FILE*` goes out of scope, the underlying `FILE*` is -automatically closed. - -Example: ----- -// test.d -import std.stdio; - -void main(string[] args) -{ - auto f = File("test.txt", "w"); // open for writing - f.write("Hello"); - if (args.length > 1) - { - auto g = f; // now g and f write to the same file - // internal reference count is 2 - g.write(", ", args[1]); - // g exits scope, reference count decreases to 1 - } - f.writeln("!"); - // f exits scope, reference count falls to zero, - // underlying `FILE*` is closed. -} ----- -$(CONSOLE -% rdmd test.d Jimmy -% cat test.txt -Hello, Jimmy! -% __ -) - */ -struct File -{ - import core.atomic : atomicOp, atomicStore, atomicLoad; - import std.range.primitives : ElementEncodingType; - import std.traits : isScalarType, isArray; - enum Orientation { unknown, narrow, wide } - - private struct Impl - { - FILE * handle = null; // Is null iff this Impl is closed by another File - shared uint refs = uint.max / 2; - bool isPopened; // true iff the stream has been created by popen() - Orientation orientation; - } - private Impl* _p; - private string _name; - - package this(FILE* handle, string name, uint refs = 1, bool isPopened = false) @trusted @nogc nothrow - { - import core.stdc.stdlib : malloc; - - assert(!_p); - _p = cast(Impl*) malloc(Impl.sizeof); - if (!_p) - { - import core.exception : onOutOfMemoryError; - onOutOfMemoryError(); - } - initImpl(handle, name, refs, isPopened); - } - - private void initImpl(FILE* handle, string name, uint refs = 1, bool isPopened = false) @nogc nothrow pure @safe - { - assert(_p); - _p.handle = handle; - atomicStore(_p.refs, refs); - _p.isPopened = isPopened; - _p.orientation = Orientation.unknown; - _name = name; - } - -/** -Constructor taking the name of the file to open and the open mode. - -Copying one `File` object to another results in the two `File` -objects referring to the same underlying file. - -The destructor automatically closes the file as soon as no `File` -object refers to it anymore. - -Params: - name = range or string representing the file _name - stdioOpenmode = range or string represting the open mode - (with the same semantics as in the C standard library - $(CSTDIO fopen) function) - -Throws: `ErrnoException` if the file could not be opened. - */ - this(string name, scope const(char)[] stdioOpenmode = "rb") @safe - { - import std.conv : text; - import std.exception : errnoEnforce; - - this(errnoEnforce(_fopen(name, stdioOpenmode), - text("Cannot open file `", name, "' in mode `", - stdioOpenmode, "'")), - name); - - // MSVCRT workaround (https://issues.dlang.org/show_bug.cgi?id=14422) - version (CRuntime_Microsoft) - { - setAppendWin(stdioOpenmode); - } - } - - /// ditto - this(R1, R2)(R1 name) - if (isSomeFiniteCharInputRange!R1) - { - import std.conv : to; - this(name.to!string, "rb"); - } - - /// ditto - this(R1, R2)(R1 name, R2 mode) - if (isSomeFiniteCharInputRange!R1 && - isSomeFiniteCharInputRange!R2) - { - import std.conv : to; - this(name.to!string, mode.to!string); - } - - @safe unittest - { - static import std.file; - import std.utf : byChar; - auto deleteme = testFilename(); - auto f = File(deleteme.byChar, "w".byChar); - f.close(); - std.file.remove(deleteme); - } - - ~this() @safe - { - detach(); - } - - this(this) @safe pure nothrow @nogc - { - if (!_p) return; - assert(atomicLoad(_p.refs)); - atomicOp!"+="(_p.refs, 1); - } - -/** -Assigns a file to another. The target of the assignment gets detached -from whatever file it was attached to, and attaches itself to the new -file. - */ - ref File opAssign(File rhs) @safe return - { - import std.algorithm.mutation : swap; - - swap(this, rhs); - return this; - } - - // https://issues.dlang.org/show_bug.cgi?id=20129 - @safe unittest - { - File[int] aa; - aa.require(0, File.init); - } - -/** -Detaches from the current file (throwing on failure), and then attempts to -_open file `name` with mode `stdioOpenmode`. The mode has the -same semantics as in the C standard library $(CSTDIO fopen) function. - -Throws: `ErrnoException` in case of error. - */ - void open(string name, scope const(char)[] stdioOpenmode = "rb") @trusted - { - resetFile(name, stdioOpenmode, false); - } - - // https://issues.dlang.org/show_bug.cgi?id=20585 - @system unittest - { - File f; - try - f.open("doesn't exist"); - catch (Exception _e) - { - } - - assert(!f.isOpen); - - f.close(); // to check not crash here - } - - private void resetFile(string name, scope const(char)[] stdioOpenmode, bool isPopened) @trusted - { - import core.stdc.stdlib : malloc; - import std.exception : enforce; - import std.conv : text; - import std.exception : errnoEnforce; - - if (_p !is null) - { - detach(); - } - - FILE* handle; - version (Posix) - { - if (isPopened) - { - errnoEnforce(handle = _popen(name, stdioOpenmode), - "Cannot run command `"~name~"'"); - } - else - { - errnoEnforce(handle = _fopen(name, stdioOpenmode), - text("Cannot open file `", name, "' in mode `", - stdioOpenmode, "'")); - } - } - else - { - assert(isPopened == false); - errnoEnforce(handle = _fopen(name, stdioOpenmode), - text("Cannot open file `", name, "' in mode `", - stdioOpenmode, "'")); - } - _p = cast(Impl*) enforce(malloc(Impl.sizeof), "Out of memory"); - initImpl(handle, name, 1, isPopened); - version (CRuntime_Microsoft) - { - setAppendWin(stdioOpenmode); - } - } - - private void closeHandles() @trusted - { - assert(_p); - import std.exception : errnoEnforce; - - version (Posix) - { - import core.sys.posix.stdio : pclose; - import std.format : format; - - if (_p.isPopened) - { - auto res = pclose(_p.handle); - errnoEnforce(res != -1, - "Could not close pipe `"~_name~"'"); - _p.handle = null; - return; - } - } - if (_p.handle) - { - auto handle = _p.handle; - _p.handle = null; - // fclose disassociates the FILE* even in case of error (https://issues.dlang.org/show_bug.cgi?id=19751) - errnoEnforce(.fclose(handle) == 0, - "Could not close file `"~_name~"'"); - } - } - - version (CRuntime_Microsoft) - { - private void setAppendWin(scope const(char)[] stdioOpenmode) @safe - { - bool append, update; - foreach (c; stdioOpenmode) - if (c == 'a') - append = true; - else - if (c == '+') - update = true; - if (append && !update) - seek(size); - } - } - -/** -Reuses the `File` object to either open a different file, or change -the file mode. If `name` is `null`, the mode of the currently open -file is changed; otherwise, a new file is opened, reusing the C -`FILE*`. The function has the same semantics as in the C standard -library $(CSTDIO freopen) function. - -Note: Calling `reopen` with a `null` `name` is not implemented -in all C runtimes. - -Throws: `ErrnoException` in case of error. - */ - void reopen(string name, scope const(char)[] stdioOpenmode = "rb") @trusted - { - import std.conv : text; - import std.exception : enforce, errnoEnforce; - import std.internal.cstring : tempCString; - - enforce(isOpen, "Attempting to reopen() an unopened file"); - - auto namez = (name == null ? _name : name).tempCString!FSChar(); - auto modez = stdioOpenmode.tempCString!FSChar(); - - FILE* fd = _p.handle; - version (Windows) - fd = _wfreopen(namez, modez, fd); - else - fd = freopen(namez, modez, fd); - - errnoEnforce(fd, name - ? text("Cannot reopen file `", name, "' in mode `", stdioOpenmode, "'") - : text("Cannot reopen file in mode `", stdioOpenmode, "'")); - - if (name !is null) - _name = name; - } - - @safe unittest // Test changing filename - { - import std.exception : assertThrown, assertNotThrown; - static import std.file; - - auto deleteme = testFilename(); - std.file.write(deleteme, "foo"); - scope(exit) std.file.remove(deleteme); - auto f = File(deleteme); - assert(f.readln() == "foo"); - - auto deleteme2 = testFilename(); - std.file.write(deleteme2, "bar"); - scope(exit) std.file.remove(deleteme2); - f.reopen(deleteme2); - assert(f.name == deleteme2); - assert(f.readln() == "bar"); - f.close(); - } - - version (CRuntime_DigitalMars) {} else // Not implemented - version (CRuntime_Microsoft) {} else // Not implemented - @safe unittest // Test changing mode - { - import std.exception : assertThrown, assertNotThrown; - static import std.file; - - auto deleteme = testFilename(); - std.file.write(deleteme, "foo"); - scope(exit) std.file.remove(deleteme); - auto f = File(deleteme, "r+"); - assert(f.readln() == "foo"); - f.reopen(null, "w"); - f.write("bar"); - f.seek(0); - f.reopen(null, "a"); - f.write("baz"); - assert(f.name == deleteme); - f.close(); - assert(std.file.readText(deleteme) == "barbaz"); - } - -/** -Detaches from the current file (throwing on failure), and then runs a command -by calling the C standard library function $(HTTP -opengroup.org/onlinepubs/007908799/xsh/_popen.html, _popen). - -Throws: `ErrnoException` in case of error. - */ - version (Posix) void popen(string command, scope const(char)[] stdioOpenmode = "r") @safe - { - resetFile(command, stdioOpenmode ,true); - } - -/** -First calls `detach` (throwing on failure), then attempts to -associate the given file descriptor with the `File`, and sets the file's name to `null`. - -The mode must be compatible with the mode of the file descriptor. - -Throws: `ErrnoException` in case of error. -Params: - fd = File descriptor to associate with this `File`. - stdioOpenmode = Mode to associate with this File. The mode has the same semantics - semantics as in the C standard library $(CSTDIO fdopen) function, - and must be compatible with `fd`. - */ - void fdopen(int fd, scope const(char)[] stdioOpenmode = "rb") @safe - { - fdopen(fd, stdioOpenmode, null); - } - - package void fdopen(int fd, scope const(char)[] stdioOpenmode, string name) @trusted - { - import std.exception : errnoEnforce; - import std.internal.cstring : tempCString; - - auto modez = stdioOpenmode.tempCString(); - detach(); - - version (CRuntime_DigitalMars) - { - // This is a re-implementation of DMC's fdopen, but without the - // mucking with the file descriptor. POSIX standard requires the - // new fdopen'd file to retain the given file descriptor's - // position. - auto fp = fopen("NUL", modez); - errnoEnforce(fp, "Cannot open placeholder NUL stream"); - _FLOCK(fp); - auto iob = cast(_iobuf*) fp; - .close(iob._file); - iob._file = fd; - iob._flag &= ~_IOTRAN; - _FUNLOCK(fp); - } - else version (CRuntime_Microsoft) - { - auto fp = _fdopen(fd, modez); - errnoEnforce(fp); - } - else version (Posix) - { - import core.sys.posix.stdio : fdopen; - auto fp = fdopen(fd, modez); - errnoEnforce(fp); - } - else - static assert(0, "no fdopen() available"); - - this = File(fp, name); - } - - // Declare a dummy HANDLE to allow generating documentation - // for Windows-only methods. - version (StdDdoc) { version (Windows) {} else alias HANDLE = int; } - -/** -First calls `detach` (throwing on failure), and then attempts to -associate the given Windows `HANDLE` with the `File`. The mode must -be compatible with the access attributes of the handle. Windows only. - -Throws: `ErrnoException` in case of error. -*/ - version (StdDdoc) - void windowsHandleOpen(HANDLE handle, scope const(char)[] stdioOpenmode); - - version (Windows) - void windowsHandleOpen(HANDLE handle, scope const(char)[] stdioOpenmode) - { - import core.stdc.stdint : intptr_t; - import std.exception : errnoEnforce; - import std.format : format; - - // Create file descriptors from the handles - version (CRuntime_DigitalMars) - auto fd = _handleToFD(handle, FHND_DEVICE); - else // MSVCRT - { - int mode; - modeLoop: - foreach (c; stdioOpenmode) - switch (c) - { - case 'r': mode |= _O_RDONLY; break; - case '+': mode &=~_O_RDONLY; break; - case 'a': mode |= _O_APPEND; break; - case 'b': mode |= _O_BINARY; break; - case 't': mode |= _O_TEXT; break; - case ',': break modeLoop; - default: break; - } - - auto fd = _open_osfhandle(cast(intptr_t) handle, mode); - } - - errnoEnforce(fd >= 0, "Cannot open Windows HANDLE"); - fdopen(fd, stdioOpenmode, "HANDLE(%s)".format(handle)); - } - - -/** Returns `true` if the file is opened. */ - @property bool isOpen() const @safe pure nothrow - { - return _p !is null && _p.handle; - } - -/** -Returns `true` if the file is at end (see $(CSTDIO feof)). - -Throws: `Exception` if the file is not opened. - */ - @property bool eof() const @trusted pure - { - import std.exception : enforce; - - enforce(_p && _p.handle, "Calling eof() against an unopened file."); - return .feof(cast(FILE*) _p.handle) != 0; - } - -/** - Returns the name last used to initialize this `File`, if any. - - Some functions that create or initialize the `File` set the name field to `null`. - Examples include $(LREF tmpfile), $(LREF wrapFile), and $(LREF fdopen). See the - documentation of those functions for details. - - Returns: The name last used to initialize this this file, or `null` otherwise. - */ - @property string name() const @safe pure nothrow return - { - return _name; - } - -/** -If the file is closed or not yet opened, returns `true`. Otherwise, returns -$(CSTDIO ferror) for the file handle. - */ - @property bool error() const @trusted pure nothrow - { - return !isOpen || .ferror(cast(FILE*) _p.handle); - } - - @safe unittest - { - // https://issues.dlang.org/show_bug.cgi?id=12349 - static import std.file; - auto deleteme = testFilename(); - auto f = File(deleteme, "w"); - scope(exit) std.file.remove(deleteme); - - f.close(); - assert(f.error); - } - -/** -Detaches from the underlying file. If the sole owner, calls `close`. - -Throws: `ErrnoException` on failure if closing the file. - */ - void detach() @trusted - { - import core.stdc.stdlib : free; - - if (!_p) return; - scope(exit) _p = null; - - if (atomicOp!"-="(_p.refs, 1) == 0) - { - scope(exit) free(_p); - closeHandles(); - } - } - - @safe unittest - { - static import std.file; - - auto deleteme = testFilename(); - scope(exit) std.file.remove(deleteme); - auto f = File(deleteme, "w"); - { - auto f2 = f; - f2.detach(); - } - assert(f._p.refs == 1); - f.close(); - } - -/** -If the file was closed or not yet opened, succeeds vacuously. Otherwise -closes the file (by calling $(CSTDIO fclose)), -throwing on error. Even if an exception is thrown, afterwards the $(D -File) object is empty. This is different from `detach` in that it -always closes the file; consequently, all other `File` objects -referring to the same handle will see a closed file henceforth. - -Throws: `ErrnoException` on error. - */ - void close() @trusted - { - import core.stdc.stdlib : free; - import std.exception : errnoEnforce; - - if (!_p) return; // succeed vacuously - scope(exit) - { - if (atomicOp!"-="(_p.refs, 1) == 0) - free(_p); - _p = null; // start a new life - } - if (!_p.handle) return; // Impl is closed by another File - - scope(exit) _p.handle = null; // nullify the handle anyway - closeHandles(); - } - -/** -If the file is closed or not yet opened, succeeds vacuously. Otherwise, returns -$(CSTDIO clearerr) for the file handle. - */ - void clearerr() @safe pure nothrow - { - _p is null || _p.handle is null || - .clearerr(_p.handle); - } - -/** -Flushes the C `FILE` buffers. - -Calls $(CSTDIO fflush) for the file handle. - -Throws: `Exception` if the file is not opened or if the call to `fflush` fails. - */ - void flush() @trusted - { - import std.exception : enforce, errnoEnforce; - - enforce(isOpen, "Attempting to flush() in an unopened file"); - errnoEnforce(.fflush(_p.handle) == 0); - } - - @safe unittest - { - // https://issues.dlang.org/show_bug.cgi?id=12349 - import std.exception : assertThrown; - static import std.file; - - auto deleteme = testFilename(); - auto f = File(deleteme, "w"); - scope(exit) std.file.remove(deleteme); - - f.close(); - assertThrown(f.flush()); - } - -/** -Forces any data buffered by the OS to be written to disk. -Call $(LREF flush) before calling this function to flush the C `FILE` buffers first. - -This function calls -$(HTTP msdn.microsoft.com/en-us/library/windows/desktop/aa364439%28v=vs.85%29.aspx, -`FlushFileBuffers`) on Windows, -$(HTTP developer.apple.com/library/archive/documentation/System/Conceptual/ManPages_iPhoneOS/man2/fcntl.2.html, -`F_FULLFSYNC fcntl`) on Darwin and -$(HTTP pubs.opengroup.org/onlinepubs/7908799/xsh/fsync.html, -`fsync`) on POSIX for the file handle. - -Throws: `Exception` if the file is not opened or if the OS call fails. - */ - void sync() @trusted - { - import std.exception : enforce; - - enforce(isOpen, "Attempting to sync() an unopened file"); - - version (Windows) - { - import core.sys.windows.winbase : FlushFileBuffers; - wenforce(FlushFileBuffers(windowsHandle), "FlushFileBuffers failed"); - } - else version (Darwin) - { - import core.sys.darwin.fcntl : fcntl, F_FULLFSYNC; - import std.exception : errnoEnforce; - errnoEnforce(fcntl(fileno, F_FULLFSYNC, 0) != -1, "fcntl failed"); - } - else - { - import core.sys.posix.unistd : fsync; - import std.exception : errnoEnforce; - errnoEnforce(fsync(fileno) == 0, "fsync failed"); - } - } - -/** -Calls $(CSTDIO fread) for the -file handle. The number of items to read and the size of -each item is inferred from the size and type of the input array, respectively. - -Returns: The slice of `buffer` containing the data that was actually read. -This will be shorter than `buffer` if EOF was reached before the buffer -could be filled. If the buffer is empty, it will be returned. - -Throws: `ErrnoException` if the file is not opened or the call to `fread` fails. - -`rawRead` always reads in binary mode on Windows. - */ - T[] rawRead(T)(T[] buffer) - { - import std.exception : enforce, errnoEnforce; - - if (!buffer.length) - return buffer; - enforce(isOpen, "Attempting to read from an unopened file"); - version (Windows) - { - immutable fileno_t fd = .fileno(_p.handle); - immutable mode = .__setmode(fd, _O_BINARY); - scope(exit) .__setmode(fd, mode); - version (CRuntime_DigitalMars) - { - import core.atomic : atomicOp; - - // https://issues.dlang.org/show_bug.cgi?id=4243 - immutable info = __fhnd_info[fd]; - atomicOp!"&="(__fhnd_info[fd], ~FHND_TEXT); - scope(exit) __fhnd_info[fd] = info; - } - } - immutable freadResult = trustedFread(_p.handle, buffer); - assert(freadResult <= buffer.length); // fread return guarantee - if (freadResult != buffer.length) // error or eof - { - errnoEnforce(!error); - return buffer[0 .. freadResult]; - } - return buffer; - } - - /// - @system unittest - { - static import std.file; - - auto testFile = std.file.deleteme(); - std.file.write(testFile, "\r\n\n\r\n"); - scope(exit) std.file.remove(testFile); - - auto f = File(testFile, "r"); - auto buf = f.rawRead(new char[5]); - f.close(); - assert(buf == "\r\n\n\r\n"); - } - - // https://issues.dlang.org/show_bug.cgi?id=21729 - @system unittest - { - import std.exception : assertThrown; - - File f; - ubyte[1] u; - assertThrown(f.rawRead(u)); - } - - // https://issues.dlang.org/show_bug.cgi?id=21728 - @system unittest - { - static if (__traits(compiles, { import std.process : pipe; })) // not available for iOS - { - import std.process : pipe; - import std.exception : assertThrown; - - auto p = pipe(); - p.readEnd.close; - ubyte[1] u; - assertThrown(p.readEnd.rawRead(u)); - } - } - - // https://issues.dlang.org/show_bug.cgi?id=13893 - @system unittest - { - import std.exception : assertNotThrown; - - File f; - ubyte[0] u; - assertNotThrown(f.rawRead(u)); - } - -/** -Calls $(CSTDIO fwrite) for the file -handle. The number of items to write and the size of each -item is inferred from the size and type of the input array, respectively. An -error is thrown if the buffer could not be written in its entirety. - -`rawWrite` always writes in binary mode on Windows. - -Throws: `ErrnoException` if the file is not opened or if the call to `fwrite` fails. - */ - void rawWrite(T)(in T[] buffer) - { - import std.conv : text; - import std.exception : errnoEnforce; - - version (Windows) - { - immutable fileno_t fd = .fileno(_p.handle); - immutable oldMode = .__setmode(fd, _O_BINARY); - - if (oldMode != _O_BINARY) - { - // need to flush the data that was written with the original mode - .__setmode(fd, oldMode); - flush(); // before changing translation mode .__setmode(fd, _O_BINARY); - .__setmode(fd, _O_BINARY); - } - - version (CRuntime_DigitalMars) - { - import core.atomic : atomicOp; - - // https://issues.dlang.org/show_bug.cgi?id=4243 - immutable info = __fhnd_info[fd]; - atomicOp!"&="(__fhnd_info[fd], ~FHND_TEXT); - scope (exit) __fhnd_info[fd] = info; - } - - scope (exit) - { - if (oldMode != _O_BINARY) - { - flush(); - .__setmode(fd, oldMode); - } - } - } - - auto result = trustedFwrite(_p.handle, buffer); - if (result == result.max) result = 0; - errnoEnforce(result == buffer.length, - text("Wrote ", result, " instead of ", buffer.length, - " objects of type ", T.stringof, " to file `", - _name, "'")); - } - - /// - @system unittest - { - static import std.file; - - auto testFile = std.file.deleteme(); - auto f = File(testFile, "w"); - scope(exit) std.file.remove(testFile); - - f.rawWrite("\r\n\n\r\n"); - f.close(); - assert(std.file.read(testFile) == "\r\n\n\r\n"); - } - -/** -Calls $(CSTDIO fseek) -for the file handle to move its position indicator. - -Params: - offset = Binary files: Number of bytes to offset from origin.$(BR) - Text files: Either zero, or a value returned by $(LREF tell). - origin = Binary files: Position used as reference for the offset, must be - one of $(REF_ALTTEXT SEEK_SET, SEEK_SET, core,stdc,stdio), - $(REF_ALTTEXT SEEK_CUR, SEEK_CUR, core,stdc,stdio) or - $(REF_ALTTEXT SEEK_END, SEEK_END, core,stdc,stdio).$(BR) - Text files: Shall necessarily be - $(REF_ALTTEXT SEEK_SET, SEEK_SET, core,stdc,stdio). - -Throws: `Exception` if the file is not opened. - `ErrnoException` if the call to `fseek` fails. - */ - void seek(long offset, int origin = SEEK_SET) @trusted - { - import std.conv : to, text; - import std.exception : enforce, errnoEnforce; - - // Some libc sanitize the whence input (e.g. glibc), but some don't, - // e.g. Microsoft runtime crashes on an invalid origin, - // and Musl additionally accept SEEK_DATA & SEEK_HOLE (Linux extension). - // To provide a consistent behavior cross platform, we use the glibc check - // See also https://issues.dlang.org/show_bug.cgi?id=19797 - enforce(origin == SEEK_SET || origin == SEEK_CUR || origin == SEEK_END, - "Invalid `origin` argument passed to `seek`, must be one of: SEEK_SET, SEEK_CUR, SEEK_END"); - - enforce(isOpen, "Attempting to seek() in an unopened file"); - version (Windows) - { - version (CRuntime_Microsoft) - { - alias fseekFun = _fseeki64; - alias off_t = long; - } - else - { - alias fseekFun = fseek; - alias off_t = int; - } - } - else version (Posix) - { - import core.sys.posix.stdio : fseeko, off_t; - alias fseekFun = fseeko; - } - errnoEnforce(fseekFun(_p.handle, to!off_t(offset), origin) == 0, - "Could not seek in file `"~_name~"'"); - } - - @system unittest - { - import std.conv : text; - static import std.file; - import std.exception; - - auto deleteme = testFilename(); - auto f = File(deleteme, "w+"); - scope(exit) { f.close(); std.file.remove(deleteme); } - f.rawWrite("abcdefghijklmnopqrstuvwxyz"); - f.seek(7); - assert(f.readln() == "hijklmnopqrstuvwxyz"); - - version (CRuntime_DigitalMars) - auto bigOffset = int.max - 100; - else - version (CRuntime_Bionic) - auto bigOffset = int.max - 100; - else - auto bigOffset = cast(ulong) int.max + 100; - f.seek(bigOffset); - assert(f.tell == bigOffset, text(f.tell)); - // Uncomment the tests below only if you want to wait for - // a long time - // f.rawWrite("abcdefghijklmnopqrstuvwxyz"); - // f.seek(-3, SEEK_END); - // assert(f.readln() == "xyz"); - - assertThrown(f.seek(0, ushort.max)); - } - -/** -Calls $(CSTDIO ftell) -for the managed file handle, which returns the current value of -the position indicator of the file handle. - -Throws: `Exception` if the file is not opened. - `ErrnoException` if the call to `ftell` fails. - */ - @property ulong tell() const @trusted - { - import std.exception : enforce, errnoEnforce; - - enforce(isOpen, "Attempting to tell() in an unopened file"); - version (Windows) - { - version (CRuntime_Microsoft) - immutable result = _ftelli64(cast(FILE*) _p.handle); - else - immutable result = ftell(cast(FILE*) _p.handle); - } - else version (Posix) - { - import core.sys.posix.stdio : ftello; - immutable result = ftello(cast(FILE*) _p.handle); - } - errnoEnforce(result != -1, - "Query ftell() failed for file `"~_name~"'"); - return result; - } - - /// - @system unittest - { - import std.conv : text; - static import std.file; - - auto testFile = std.file.deleteme(); - std.file.write(testFile, "abcdefghijklmnopqrstuvwqxyz"); - scope(exit) { std.file.remove(testFile); } - - auto f = File(testFile); - auto a = new ubyte[4]; - f.rawRead(a); - assert(f.tell == 4, text(f.tell)); - } - -/** -Calls $(CSTDIO rewind) for the file handle. - -Throws: `Exception` if the file is not opened. - */ - void rewind() @safe - { - import std.exception : enforce; - - enforce(isOpen, "Attempting to rewind() an unopened file"); - .rewind(_p.handle); - } - -/** -Calls $(CSTDIO setvbuf) for the file handle. - -Throws: `Exception` if the file is not opened. - `ErrnoException` if the call to `setvbuf` fails. - */ - void setvbuf(size_t size, int mode = _IOFBF) @trusted - { - import std.exception : enforce, errnoEnforce; - - enforce(isOpen, "Attempting to call setvbuf() on an unopened file"); - errnoEnforce(.setvbuf(_p.handle, null, mode, size) == 0, - "Could not set buffering for file `"~_name~"'"); - } - -/** -Calls $(CSTDIO setvbuf) for the file handle. - -Throws: `Exception` if the file is not opened. - `ErrnoException` if the call to `setvbuf` fails. -*/ - void setvbuf(void[] buf, int mode = _IOFBF) @trusted - { - import std.exception : enforce, errnoEnforce; - - enforce(isOpen, "Attempting to call setvbuf() on an unopened file"); - errnoEnforce(.setvbuf(_p.handle, - cast(char*) buf.ptr, mode, buf.length) == 0, - "Could not set buffering for file `"~_name~"'"); - } - - - version (Windows) - { - import core.sys.windows.winbase : OVERLAPPED; - import core.sys.windows.winnt : BOOL, ULARGE_INTEGER; - import std.windows.syserror : wenforce; - - private BOOL lockImpl(alias F, Flags...)(ulong start, ulong length, - Flags flags) - { - if (!start && !length) - length = ulong.max; - ULARGE_INTEGER liStart = void, liLength = void; - liStart.QuadPart = start; - liLength.QuadPart = length; - OVERLAPPED overlapped; - overlapped.Offset = liStart.LowPart; - overlapped.OffsetHigh = liStart.HighPart; - overlapped.hEvent = null; - return F(windowsHandle, flags, 0, liLength.LowPart, - liLength.HighPart, &overlapped); - } - } - version (Posix) - { - private int lockImpl(int operation, short l_type, - ulong start, ulong length) - { - import core.sys.posix.fcntl : fcntl, flock, off_t; - import core.sys.posix.unistd : getpid; - import std.conv : to; - - flock fl = void; - fl.l_type = l_type; - fl.l_whence = SEEK_SET; - fl.l_start = to!off_t(start); - fl.l_len = to!off_t(length); - fl.l_pid = getpid(); - return fcntl(fileno, operation, &fl); - } - } - -/** -Locks the specified file segment. If the file segment is already locked -by another process, waits until the existing lock is released. -If both `start` and `length` are zero, the entire file is locked. - -Locks created using `lock` and `tryLock` have the following properties: -$(UL - $(LI All locks are automatically released when the process terminates.) - $(LI Locks are not inherited by child processes.) - $(LI Closing a file will release all locks associated with the file. On POSIX, - even locks acquired via a different `File` will be released as well.) - $(LI Not all NFS implementations correctly implement file locking.) -) - */ - void lock(LockType lockType = LockType.readWrite, - ulong start = 0, ulong length = 0) - { - import std.exception : enforce; - - enforce(isOpen, "Attempting to call lock() on an unopened file"); - version (Posix) - { - import core.sys.posix.fcntl : F_RDLCK, F_SETLKW, F_WRLCK; - import std.exception : errnoEnforce; - immutable short type = lockType == LockType.readWrite - ? F_WRLCK : F_RDLCK; - errnoEnforce(lockImpl(F_SETLKW, type, start, length) != -1, - "Could not set lock for file `"~_name~"'"); - } - else - version (Windows) - { - import core.sys.windows.winbase : LockFileEx, LOCKFILE_EXCLUSIVE_LOCK; - immutable type = lockType == LockType.readWrite ? - LOCKFILE_EXCLUSIVE_LOCK : 0; - wenforce(lockImpl!LockFileEx(start, length, type), - "Could not set lock for file `"~_name~"'"); - } - else - static assert(false); - } - -/** -Attempts to lock the specified file segment. -If both `start` and `length` are zero, the entire file is locked. -Returns: `true` if the lock was successful, and `false` if the -specified file segment was already locked. - */ - bool tryLock(LockType lockType = LockType.readWrite, - ulong start = 0, ulong length = 0) - { - import std.exception : enforce; - - enforce(isOpen, "Attempting to call tryLock() on an unopened file"); - version (Posix) - { - import core.stdc.errno : EACCES, EAGAIN, errno; - import core.sys.posix.fcntl : F_RDLCK, F_SETLK, F_WRLCK; - import std.exception : errnoEnforce; - immutable short type = lockType == LockType.readWrite - ? F_WRLCK : F_RDLCK; - immutable res = lockImpl(F_SETLK, type, start, length); - if (res == -1 && (errno == EACCES || errno == EAGAIN)) - return false; - errnoEnforce(res != -1, "Could not set lock for file `"~_name~"'"); - return true; - } - else - version (Windows) - { - import core.sys.windows.winbase : GetLastError, LockFileEx, LOCKFILE_EXCLUSIVE_LOCK, - LOCKFILE_FAIL_IMMEDIATELY; - import core.sys.windows.winerror : ERROR_IO_PENDING, ERROR_LOCK_VIOLATION; - immutable type = lockType == LockType.readWrite - ? LOCKFILE_EXCLUSIVE_LOCK : 0; - immutable res = lockImpl!LockFileEx(start, length, - type | LOCKFILE_FAIL_IMMEDIATELY); - if (!res && (GetLastError() == ERROR_IO_PENDING - || GetLastError() == ERROR_LOCK_VIOLATION)) - return false; - wenforce(res, "Could not set lock for file `"~_name~"'"); - return true; - } - else - static assert(false); - } - -/** -Removes the lock over the specified file segment. - */ - void unlock(ulong start = 0, ulong length = 0) - { - import std.exception : enforce; - - enforce(isOpen, "Attempting to call unlock() on an unopened file"); - version (Posix) - { - import core.sys.posix.fcntl : F_SETLK, F_UNLCK; - import std.exception : errnoEnforce; - errnoEnforce(lockImpl(F_SETLK, F_UNLCK, start, length) != -1, - "Could not remove lock for file `"~_name~"'"); - } - else - version (Windows) - { - import core.sys.windows.winbase : UnlockFileEx; - wenforce(lockImpl!UnlockFileEx(start, length), - "Could not remove lock for file `"~_name~"'"); - } - else - static assert(false); - } - - version (Windows) - @system unittest - { - static import std.file; - auto deleteme = testFilename(); - scope(exit) std.file.remove(deleteme); - auto f = File(deleteme, "wb"); - assert(f.tryLock()); - auto g = File(deleteme, "wb"); - assert(!g.tryLock()); - assert(!g.tryLock(LockType.read)); - f.unlock(); - f.lock(LockType.read); - assert(!g.tryLock()); - assert(g.tryLock(LockType.read)); - f.unlock(); - g.unlock(); - } - - version (Posix) - @system unittest - { - static if (__traits(compiles, { import std.process : spawnProcess; })) - { - static import std.file; - auto deleteme = testFilename(); - scope(exit) std.file.remove(deleteme); - - // Since locks are per-process, we cannot test lock failures within - // the same process. fork() is used to create a second process. - static void runForked(void delegate() code) - { - import core.sys.posix.sys.wait : waitpid; - import core.sys.posix.unistd : fork, _exit; - int child, status; - if ((child = fork()) == 0) - { - code(); - _exit(0); - } - else - { - assert(waitpid(child, &status, 0) != -1); - assert(status == 0, "Fork crashed"); - } - } - - auto f = File(deleteme, "w+b"); - - runForked - ({ - auto g = File(deleteme, "a+b"); - assert(g.tryLock()); - g.unlock(); - assert(g.tryLock(LockType.read)); - }); - - assert(f.tryLock()); - runForked - ({ - auto g = File(deleteme, "a+b"); - assert(!g.tryLock()); - assert(!g.tryLock(LockType.read)); - }); - f.unlock(); - - f.lock(LockType.read); - runForked - ({ - auto g = File(deleteme, "a+b"); - assert(!g.tryLock()); - assert(g.tryLock(LockType.read)); - g.unlock(); - }); - f.unlock(); - } // static if - } // unittest - - -/** -Writes its arguments in text format to the file. - -Throws: `Exception` if the file is not opened. - `ErrnoException` on an error writing to the file. -*/ - void write(S...)(S args) - { - import std.traits : isBoolean, isIntegral, isAggregateType; - import std.utf : UTFException; - auto w = lockingTextWriter(); - foreach (arg; args) - { - try - { - alias A = typeof(arg); - static if (isAggregateType!A || is(A == enum)) - { - import std.format.write : formattedWrite; - - formattedWrite(w, "%s", arg); - } - else static if (isSomeString!A) - { - put(w, arg); - } - else static if (isIntegral!A) - { - import std.conv : toTextRange; - - toTextRange(arg, w); - } - else static if (isBoolean!A) - { - put(w, arg ? "true" : "false"); - } - else static if (isSomeChar!A) - { - put(w, arg); - } - else - { - import std.format.write : formattedWrite; - - // Most general case - formattedWrite(w, "%s", arg); - } - } - catch (UTFException e) - { - /* Reset the writer so that it doesn't throw another - UTFException on destruction. */ - w.highSurrogate = '\0'; - throw e; - } - } - } - -/** -Writes its arguments in text format to the file, followed by a newline. - -Throws: `Exception` if the file is not opened. - `ErrnoException` on an error writing to the file. -*/ - void writeln(S...)(S args) - { - write(args, '\n'); - } - -/** -Writes its arguments in text format to the file, according to the -format string fmt. - -Params: -fmt = The $(REF_ALTTEXT format string, formattedWrite, std, _format). -When passed as a compile-time argument, the string will be statically checked -against the argument types passed. -args = Items to write. - -Throws: `Exception` if the file is not opened. - `ErrnoException` on an error writing to the file. -*/ - void writef(alias fmt, A...)(A args) - if (isSomeString!(typeof(fmt))) - { - import std.format : checkFormatException; - - alias e = checkFormatException!(fmt, A); - static assert(!e, e); - return this.writef(fmt, args); - } - - /// ditto - void writef(Char, A...)(in Char[] fmt, A args) - { - import std.format.write : formattedWrite; - - formattedWrite(lockingTextWriter(), fmt, args); - } - - /// Equivalent to `file.writef(fmt, args, '\n')`. - void writefln(alias fmt, A...)(A args) - if (isSomeString!(typeof(fmt))) - { - import std.format : checkFormatException; - - alias e = checkFormatException!(fmt, A); - static assert(!e, e); - return this.writefln(fmt, args); - } - - /// ditto - void writefln(Char, A...)(in Char[] fmt, A args) - { - import std.format.write : formattedWrite; - - auto w = lockingTextWriter(); - formattedWrite(w, fmt, args); - w.put('\n'); - } - -/** -Read line from the file handle and return it as a specified type. - -This version manages its own read buffer, which means one memory allocation per call. If you are not -retaining a reference to the read data, consider the `File.readln(buf)` version, which may offer -better performance as it can reuse its read buffer. - -Params: - S = Template parameter; the type of the allocated buffer, and the type returned. Defaults to `string`. - terminator = Line terminator (by default, `'\n'`). - -Note: - String terminators are not supported due to ambiguity with readln(buf) below. - -Returns: - The line that was read, including the line terminator character. - -Throws: - `StdioException` on I/O error, or `UnicodeException` on Unicode conversion error. - -Example: ---- -// Reads `stdin` and writes it to `stdout`. -import std.stdio; - -void main() -{ - string line; - while ((line = stdin.readln()) !is null) - write(line); -} ---- -*/ - S readln(S = string)(dchar terminator = '\n') @safe - if (isSomeString!S) - { - Unqual!(ElementEncodingType!S)[] buf; - readln(buf, terminator); - return (() @trusted => cast(S) buf)(); - } - - @safe unittest - { - import std.algorithm.comparison : equal; - static import std.file; - import std.meta : AliasSeq; - - auto deleteme = testFilename(); - std.file.write(deleteme, "hello\nworld\n"); - scope(exit) std.file.remove(deleteme); - static foreach (String; AliasSeq!(string, char[], wstring, wchar[], dstring, dchar[])) - {{ - auto witness = [ "hello\n", "world\n" ]; - auto f = File(deleteme); - uint i = 0; - String buf; - while ((buf = f.readln!String()).length) - { - assert(i < witness.length); - assert(equal(buf, witness[i++])); - } - assert(i == witness.length); - }} - } - - @safe unittest - { - static import std.file; - import std.typecons : Tuple; - - auto deleteme = testFilename(); - std.file.write(deleteme, "cześć \U0002000D"); - scope(exit) std.file.remove(deleteme); - uint[] lengths = [12,8,7]; - static foreach (uint i, C; Tuple!(char, wchar, dchar).Types) - {{ - immutable(C)[] witness = "cześć \U0002000D"; - auto buf = File(deleteme).readln!(immutable(C)[])(); - assert(buf.length == lengths[i]); - assert(buf == witness); - }} - } - -/** -Read line from the file handle and write it to `buf[]`, including -terminating character. - -This can be faster than $(D line = File.readln()) because you can reuse -the buffer for each call. Note that reusing the buffer means that you -must copy the previous contents if you wish to retain them. - -Params: -buf = Buffer used to store the resulting line data. buf is -enlarged if necessary, then set to the slice exactly containing the line. -terminator = Line terminator (by default, `'\n'`). Use -$(REF newline, std,ascii) for portability (unless the file was opened in -text mode). - -Returns: -0 for end of file, otherwise number of characters read. -The return value will always be equal to `buf.length`. - -Throws: `StdioException` on I/O error, or `UnicodeException` on Unicode -conversion error. - -Example: ---- -// Read lines from `stdin` into a string -// Ignore lines starting with '#' -// Write the string to `stdout` -import std.stdio; - -void main() -{ - string output; - char[] buf; - - while (stdin.readln(buf)) - { - if (buf[0] == '#') - continue; - - output ~= buf; - } - - write(output); -} ---- - -This method can be more efficient than the one in the previous example -because `stdin.readln(buf)` reuses (if possible) memory allocated -for `buf`, whereas $(D line = stdin.readln()) makes a new memory allocation -for every line. - -For even better performance you can help `readln` by passing in a -large buffer to avoid memory reallocations. This can be done by reusing the -largest buffer returned by `readln`: - -Example: ---- -// Read lines from `stdin` and count words -import std.array, std.stdio; - -void main() -{ - char[] buf; - size_t words = 0; - - while (!stdin.eof) - { - char[] line = buf; - stdin.readln(line); - if (line.length > buf.length) - buf = line; - - words += line.split.length; - } - - writeln(words); -} ---- -This is actually what $(LREF byLine) does internally, so its usage -is recommended if you want to process a complete file. -*/ - size_t readln(C)(ref C[] buf, dchar terminator = '\n') @safe - if (isSomeChar!C && is(Unqual!C == C) && !is(C == enum)) - { - import std.exception : enforce; - - static if (is(C == char)) - { - enforce(_p && _p.handle, "Attempt to read from an unopened file."); - if (_p.orientation == Orientation.unknown) - { - import core.stdc.wchar_ : fwide; - auto w = fwide(_p.handle, 0); - if (w < 0) _p.orientation = Orientation.narrow; - else if (w > 0) _p.orientation = Orientation.wide; - } - return readlnImpl(_p.handle, buf, terminator, _p.orientation); - } - else - { - string s = readln(terminator); - if (!s.length) - { - buf = buf[0 .. 0]; - return 0; - } - - import std.utf : codeLength; - buf.length = codeLength!C(s); - size_t idx; - foreach (C c; s) - buf[idx++] = c; - - return buf.length; - } - } - - @safe unittest - { - static import std.file; - auto deleteme = testFilename(); - std.file.write(deleteme, "123\n456789"); - scope(exit) std.file.remove(deleteme); - - auto file = File(deleteme); - char[] buffer = new char[10]; - char[] line = buffer; - file.readln(line); - auto beyond = line.length; - buffer[beyond] = 'a'; - file.readln(line); // should not write buffer beyond line - assert(buffer[beyond] == 'a'); - } - - // https://issues.dlang.org/show_bug.cgi?id=15293 - @safe unittest - { - // @system due to readln - static import std.file; - auto deleteme = testFilename(); - std.file.write(deleteme, "a\n\naa"); - scope(exit) std.file.remove(deleteme); - - auto file = File(deleteme); - char[] buffer; - char[] line; - - file.readln(buffer, '\n'); - - line = buffer; - file.readln(line, '\n'); - - line = buffer; - file.readln(line, '\n'); - - assert(line[0 .. 1].capacity == 0); - } - -/** ditto */ - size_t readln(C, R)(ref C[] buf, R terminator) @safe - if (isSomeChar!C && is(Unqual!C == C) && !is(C == enum) && - isBidirectionalRange!R && is(typeof(terminator.front == dchar.init))) - { - import std.algorithm.mutation : swap; - import std.algorithm.searching : endsWith; - import std.range.primitives : back; - - auto last = terminator.back; - C[] buf2; - swap(buf, buf2); - for (;;) - { - if (!readln(buf2, last) || endsWith(buf2, terminator)) - { - if (buf.empty) - { - buf = buf2; - } - else - { - buf ~= buf2; - } - break; - } - buf ~= buf2; - } - return buf.length; - } - - @safe unittest - { - static import std.file; - import std.typecons : Tuple; - - auto deleteme = testFilename(); - std.file.write(deleteme, "hello\n\rworld\nhow\n\rare ya"); - scope(exit) std.file.remove(deleteme); - foreach (C; Tuple!(char, wchar, dchar).Types) - { - immutable(C)[][] witness = [ "hello\n\r", "world\nhow\n\r", "are ya" ]; - auto f = File(deleteme); - uint i = 0; - C[] buf; - while (f.readln(buf, "\n\r")) - { - assert(i < witness.length); - assert(buf == witness[i++]); - } - assert(buf.length == 0); - } - } - - /** - * Reads formatted _data from the file using $(REF formattedRead, std,_format). - * Params: - * format = The $(REF_ALTTEXT format string, formattedWrite, std, _format). - * When passed as a compile-time argument, the string will be statically checked - * against the argument types passed. - * data = Items to be read. - * Returns: - * Same as `formattedRead`: The number of variables filled. If the input range `r` ends early, - * this number will be less than the number of variables provided. - * Example: ----- -// test.d -void main() -{ - import std.stdio; - auto f = File("input"); - foreach (_; 0 .. 3) - { - int a; - f.readf!" %d"(a); - writeln(++a); - } -} ----- -$(CONSOLE -% echo "1 2 3" > input -% rdmd test.d -2 -3 -4 -) - */ - uint readf(alias format, Data...)(auto ref Data data) - if (isSomeString!(typeof(format))) - { - import std.format : checkFormatException; - - alias e = checkFormatException!(format, Data); - static assert(!e, e); - return this.readf(format, data); - } - - /// ditto - uint readf(Data...)(scope const(char)[] format, auto ref Data data) - { - import std.format.read : formattedRead; - - assert(isOpen); - auto input = LockingTextReader(this); - return formattedRead(input, format, data); - } - - /// - @system unittest - { - static import std.file; - - auto deleteme = std.file.deleteme(); - std.file.write(deleteme, "hello\nworld\ntrue\nfalse\n"); - scope(exit) std.file.remove(deleteme); - string s; - auto f = File(deleteme); - f.readf!"%s\n"(s); - assert(s == "hello", "["~s~"]"); - f.readf("%s\n", s); - assert(s == "world", "["~s~"]"); - - bool b1, b2; - f.readf("%s\n%s\n", b1, b2); - assert(b1 == true && b2 == false); - } - - // backwards compatibility with pointers - @system unittest - { - // @system due to readf - static import std.file; - - auto deleteme = testFilename(); - std.file.write(deleteme, "hello\nworld\ntrue\nfalse\n"); - scope(exit) std.file.remove(deleteme); - string s; - auto f = File(deleteme); - f.readf("%s\n", &s); - assert(s == "hello", "["~s~"]"); - f.readf("%s\n", &s); - assert(s == "world", "["~s~"]"); - - // https://issues.dlang.org/show_bug.cgi?id=11698 - bool b1, b2; - f.readf("%s\n%s\n", &b1, &b2); - assert(b1 == true && b2 == false); - } - - // backwards compatibility (mixed) - @system unittest - { - // @system due to readf - static import std.file; - - auto deleteme = testFilename(); - std.file.write(deleteme, "hello\nworld\ntrue\nfalse\n"); - scope(exit) std.file.remove(deleteme); - string s1, s2; - auto f = File(deleteme); - f.readf("%s\n%s\n", s1, &s2); - assert(s1 == "hello"); - assert(s2 == "world"); - - // https://issues.dlang.org/show_bug.cgi?id=11698 - bool b1, b2; - f.readf("%s\n%s\n", &b1, b2); - assert(b1 == true && b2 == false); - } - - // Nice error of std.stdio.readf with newlines - // https://issues.dlang.org/show_bug.cgi?id=12260 - @system unittest - { - static import std.file; - - auto deleteme = testFilename(); - std.file.write(deleteme, "1\n2"); - scope(exit) std.file.remove(deleteme); - int input; - auto f = File(deleteme); - f.readf("%s", &input); - - import std.conv : ConvException; - import std.exception : collectException; - assert(collectException!ConvException(f.readf("%s", &input)).msg == - "Unexpected '\\n' when converting from type LockingTextReader to type int"); - } - -/** - Returns a temporary file by calling $(CSTDIO tmpfile). - Note that the created file has no $(LREF name).*/ - static File tmpfile() @safe - { - import std.exception : errnoEnforce; - - return File(errnoEnforce(.tmpfile(), - "Could not create temporary file with tmpfile()"), - null); - } - -/** -Unsafe function that wraps an existing `FILE*`. The resulting $(D -File) never takes the initiative in closing the file. -Note that the created file has no $(LREF name)*/ - /*private*/ static File wrapFile(FILE* f) @safe - { - import std.exception : enforce; - - return File(enforce(f, "Could not wrap null FILE*"), - null, /*uint.max / 2*/ 9999); - } - -/** -Returns the `FILE*` corresponding to this object. - */ - FILE* getFP() @safe pure - { - import std.exception : enforce; - - enforce(_p && _p.handle, - "Attempting to call getFP() on an unopened file"); - return _p.handle; - } - - @system unittest - { - static import core.stdc.stdio; - assert(stdout.getFP() == core.stdc.stdio.stdout); - } - -/** -Returns the file number corresponding to this object. - */ - @property fileno_t fileno() const @trusted - { - import std.exception : enforce; - - enforce(isOpen, "Attempting to call fileno() on an unopened file"); - return .fileno(cast(FILE*) _p.handle); - } - -/** -Returns the underlying operating system `HANDLE` (Windows only). -*/ - version (StdDdoc) - @property HANDLE windowsHandle(); - - version (Windows) - @property HANDLE windowsHandle() - { - version (CRuntime_DigitalMars) - return _fdToHandle(fileno); - else - return cast(HANDLE)_get_osfhandle(fileno); - } - - -// Note: This was documented until 2013/08 -/* -Range that reads one line at a time. Returned by $(LREF byLine). - -Allows to directly use range operations on lines of a file. -*/ - private struct ByLineImpl(Char, Terminator) - { - private: - import std.typecons : RefCounted, RefCountedAutoInitialize; - - /* Ref-counting stops the source range's Impl - * from getting out of sync after the range is copied, e.g. - * when accessing range.front, then using std.range.take, - * then accessing range.front again. */ - alias PImpl = RefCounted!(Impl, RefCountedAutoInitialize.no); - PImpl impl; - - static if (isScalarType!Terminator) - enum defTerm = '\n'; - else - enum defTerm = cast(Terminator)"\n"; - - public: - this(File f, KeepTerminator kt = No.keepTerminator, - Terminator terminator = defTerm) - { - impl = PImpl(f, kt, terminator); - } - - @property bool empty() - { - return impl.refCountedPayload.empty; - } - - @property Char[] front() - { - return impl.refCountedPayload.front; - } - - void popFront() - { - impl.refCountedPayload.popFront(); - } - - private: - struct Impl - { - private: - File file; - Char[] line; - Char[] buffer; - Terminator terminator; - KeepTerminator keepTerminator; - bool haveLine; - - public: - this(File f, KeepTerminator kt, Terminator terminator) - { - file = f; - this.terminator = terminator; - keepTerminator = kt; - } - - // Range primitive implementations. - @property bool empty() - { - needLine(); - return line is null; - } - - @property Char[] front() - { - needLine(); - return line; - } - - void popFront() - { - needLine(); - haveLine = false; - } - - private: - void needLine() - { - if (haveLine) - return; - import std.algorithm.searching : endsWith; - assert(file.isOpen); - line = buffer; - file.readln(line, terminator); - if (line.length > buffer.length) - { - buffer = line; - } - if (line.empty) - { - file.detach(); - line = null; - } - else if (keepTerminator == No.keepTerminator - && endsWith(line, terminator)) - { - static if (isScalarType!Terminator) - enum tlen = 1; - else static if (isArray!Terminator) - { - static assert( - is(immutable ElementEncodingType!Terminator == immutable Char)); - const tlen = terminator.length; - } - else - static assert(false); - line = line[0 .. line.length - tlen]; - } - haveLine = true; - } - } - } - -/** -Returns an $(REF_ALTTEXT input range, isInputRange, std,range,primitives) -set up to read from the file handle one line at a time. - -The element type for the range will be `Char[]`. Range primitives -may throw `StdioException` on I/O error. - -Note: -Each `front` will not persist after $(D -popFront) is called, so the caller must copy its contents (e.g. by -calling `to!string`) when retention is needed. If the caller needs -to retain a copy of every line, use the $(LREF byLineCopy) function -instead. - -Params: -Char = Character type for each line, defaulting to `char`. -keepTerminator = Use `Yes.keepTerminator` to include the -terminator at the end of each line. -terminator = Line separator (`'\n'` by default). Use -$(REF newline, std,ascii) for portability (unless the file was opened in -text mode). - -Example: ----- -import std.algorithm, std.stdio, std.string; -// Count words in a file using ranges. -void main() -{ - auto file = File("file.txt"); // Open for reading - const wordCount = file.byLine() // Read lines - .map!split // Split into words - .map!(a => a.length) // Count words per line - .sum(); // Total word count - writeln(wordCount); -} ----- - -Example: ----- -import std.range, std.stdio; -// Read lines using foreach. -void main() -{ - auto file = File("file.txt"); // Open for reading - auto range = file.byLine(); - // Print first three lines - foreach (line; range.take(3)) - writeln(line); - // Print remaining lines beginning with '#' - foreach (line; range) - { - if (!line.empty && line[0] == '#') - writeln(line); - } -} ----- -Notice that neither example accesses the line data returned by -`front` after the corresponding `popFront` call is made (because -the contents may well have changed). -*/ - auto byLine(Terminator = char, Char = char) - (KeepTerminator keepTerminator = No.keepTerminator, - Terminator terminator = '\n') - if (isScalarType!Terminator) - { - return ByLineImpl!(Char, Terminator)(this, keepTerminator, terminator); - } - -/// ditto - auto byLine(Terminator, Char = char) - (KeepTerminator keepTerminator, Terminator terminator) - if (is(immutable ElementEncodingType!Terminator == immutable Char)) - { - return ByLineImpl!(Char, Terminator)(this, keepTerminator, terminator); - } - - @system unittest - { - static import std.file; - auto deleteme = testFilename(); - std.file.write(deleteme, "hi"); - scope(success) std.file.remove(deleteme); - - import std.meta : AliasSeq; - static foreach (T; AliasSeq!(char, wchar, dchar)) - {{ - auto blc = File(deleteme).byLine!(T, T); - assert(blc.front == "hi"); - // check front is cached - assert(blc.front is blc.front); - }} - } - - // https://issues.dlang.org/show_bug.cgi?id=19980 - @system unittest - { - static import std.file; - auto deleteme = testFilename(); - std.file.write(deleteme, "Line 1\nLine 2\nLine 3\n"); - scope(success) std.file.remove(deleteme); - - auto f = File(deleteme); - f.byLine(); - f.byLine(); - assert(f.byLine().front == "Line 1"); - } - - private struct ByLineCopy(Char, Terminator) - { - private: - import std.typecons : RefCounted, RefCountedAutoInitialize; - - /* Ref-counting stops the source range's ByLineCopyImpl - * from getting out of sync after the range is copied, e.g. - * when accessing range.front, then using std.range.take, - * then accessing range.front again. */ - alias Impl = RefCounted!(ByLineCopyImpl!(Char, Terminator), - RefCountedAutoInitialize.no); - Impl impl; - - public: - this(File f, KeepTerminator kt, Terminator terminator) - { - impl = Impl(f, kt, terminator); - } - - @property bool empty() - { - return impl.refCountedPayload.empty; - } - - @property Char[] front() - { - return impl.refCountedPayload.front; - } - - void popFront() - { - impl.refCountedPayload.popFront(); - } - } - - private struct ByLineCopyImpl(Char, Terminator) - { - ByLineImpl!(Unqual!Char, Terminator).Impl impl; - bool gotFront; - Char[] line; - - public: - this(File f, KeepTerminator kt, Terminator terminator) - { - impl = ByLineImpl!(Unqual!Char, Terminator).Impl(f, kt, terminator); - } - - @property bool empty() - { - return impl.empty; - } - - @property front() - { - if (!gotFront) - { - line = impl.front.dup; - gotFront = true; - } - return line; - } - - void popFront() - { - impl.popFront(); - gotFront = false; - } - } - -/** -Returns an $(REF_ALTTEXT input range, isInputRange, std,range,primitives) -set up to read from the file handle one line -at a time. Each line will be newly allocated. `front` will cache -its value to allow repeated calls without unnecessary allocations. - -Note: Due to caching byLineCopy can be more memory-efficient than -`File.byLine.map!idup`. - -The element type for the range will be `Char[]`. Range -primitives may throw `StdioException` on I/O error. - -Params: -Char = Character type for each line, defaulting to $(D immutable char). -keepTerminator = Use `Yes.keepTerminator` to include the -terminator at the end of each line. -terminator = Line separator (`'\n'` by default). Use -$(REF newline, std,ascii) for portability (unless the file was opened in -text mode). - -Example: ----- -import std.algorithm, std.array, std.stdio; -// Print sorted lines of a file. -void main() -{ - auto sortedLines = File("file.txt") // Open for reading - .byLineCopy() // Read persistent lines - .array() // into an array - .sort(); // then sort them - foreach (line; sortedLines) - writeln(line); -} ----- -See_Also: -$(REF readText, std,file) -*/ - auto byLineCopy(Terminator = char, Char = immutable char) - (KeepTerminator keepTerminator = No.keepTerminator, - Terminator terminator = '\n') - if (isScalarType!Terminator) - { - return ByLineCopy!(Char, Terminator)(this, keepTerminator, terminator); - } - -/// ditto - auto byLineCopy(Terminator, Char = immutable char) - (KeepTerminator keepTerminator, Terminator terminator) - if (is(immutable ElementEncodingType!Terminator == immutable Char)) - { - return ByLineCopy!(Char, Terminator)(this, keepTerminator, terminator); - } - - @safe unittest - { - static assert(is(typeof(File("").byLine.front) == char[])); - static assert(is(typeof(File("").byLineCopy.front) == string)); - static assert( - is(typeof(File("").byLineCopy!(char, char).front) == char[])); - } - - @system unittest - { - import std.algorithm.comparison : equal; - static import std.file; - - scope(failure) printf("Failed test at line %d\n", __LINE__); - auto deleteme = testFilename(); - std.file.write(deleteme, ""); - scope(success) std.file.remove(deleteme); - - // Test empty file - auto f = File(deleteme); - foreach (line; f.byLine()) - { - assert(false); - } - f.detach(); - assert(!f.isOpen); - - void test(Terminator)(string txt, in string[] witness, - KeepTerminator kt, Terminator term, bool popFirstLine = false) - { - import std.algorithm.sorting : sort; - import std.array : array; - import std.conv : text; - import std.range.primitives : walkLength; - - uint i; - std.file.write(deleteme, txt); - auto f = File(deleteme); - scope(exit) - { - f.close(); - assert(!f.isOpen); - } - auto lines = f.byLine(kt, term); - if (popFirstLine) - { - lines.popFront(); - i = 1; - } - assert(lines.empty || lines.front is lines.front); - foreach (line; lines) - { - assert(line == witness[i++]); - } - assert(i == witness.length, text(i, " != ", witness.length)); - - // https://issues.dlang.org/show_bug.cgi?id=11830 - auto walkedLength = File(deleteme).byLine(kt, term).walkLength; - assert(walkedLength == witness.length, text(walkedLength, " != ", witness.length)); - - // test persistent lines - assert(File(deleteme).byLineCopy(kt, term).array.sort() == witness.dup.sort()); - } - - KeepTerminator kt = No.keepTerminator; - test("", null, kt, '\n'); - test("\n", [ "" ], kt, '\n'); - test("asd\ndef\nasdf", [ "asd", "def", "asdf" ], kt, '\n'); - test("asd\ndef\nasdf", [ "asd", "def", "asdf" ], kt, '\n', true); - test("asd\ndef\nasdf\n", [ "asd", "def", "asdf" ], kt, '\n'); - test("foo", [ "foo" ], kt, '\n', true); - test("bob\r\nmarge\r\nsteve\r\n", ["bob", "marge", "steve"], - kt, "\r\n"); - test("sue\r", ["sue"], kt, '\r'); - - kt = Yes.keepTerminator; - test("", null, kt, '\n'); - test("\n", [ "\n" ], kt, '\n'); - test("asd\ndef\nasdf", [ "asd\n", "def\n", "asdf" ], kt, '\n'); - test("asd\ndef\nasdf\n", [ "asd\n", "def\n", "asdf\n" ], kt, '\n'); - test("asd\ndef\nasdf\n", [ "asd\n", "def\n", "asdf\n" ], kt, '\n', true); - test("foo", [ "foo" ], kt, '\n'); - test("bob\r\nmarge\r\nsteve\r\n", ["bob\r\n", "marge\r\n", "steve\r\n"], - kt, "\r\n"); - test("sue\r", ["sue\r"], kt, '\r'); - } - - @system unittest - { - import std.algorithm.comparison : equal; - import std.range : drop, take; - - version (Win64) - { - static import std.file; - - /* the C function tmpfile doesn't seem to work, even when called from C */ - auto deleteme = testFilename(); - auto file = File(deleteme, "w+"); - scope(success) std.file.remove(deleteme); - } - else version (CRuntime_Bionic) - { - static import std.file; - - /* the C function tmpfile doesn't work when called from a shared - library apk: - https://code.google.com/p/android/issues/detail?id=66815 */ - auto deleteme = testFilename(); - auto file = File(deleteme, "w+"); - scope(success) std.file.remove(deleteme); - } - else - auto file = File.tmpfile(); - file.write("1\n2\n3\n"); - - // https://issues.dlang.org/show_bug.cgi?id=9599 - file.rewind(); - File.ByLineImpl!(char, char) fbl = file.byLine(); - auto fbl2 = fbl; - assert(fbl.front == "1"); - assert(fbl.front is fbl2.front); - assert(fbl.take(1).equal(["1"])); - assert(fbl.equal(["2", "3"])); - assert(fbl.empty); - assert(file.isOpen); // we still have a valid reference - - file.rewind(); - fbl = file.byLine(); - assert(!fbl.drop(2).empty); - assert(fbl.equal(["3"])); - assert(fbl.empty); - assert(file.isOpen); - - file.detach(); - assert(!file.isOpen); - } - - @system unittest - { - static import std.file; - auto deleteme = testFilename(); - std.file.write(deleteme, "hi"); - scope(success) std.file.remove(deleteme); - - auto blc = File(deleteme).byLineCopy; - assert(!blc.empty); - // check front is cached - assert(blc.front is blc.front); - } - - /** - Creates an $(REF_ALTTEXT input range, isInputRange, std,range,primitives) - set up to parse one line at a time from the file into a tuple. - - Range primitives may throw `StdioException` on I/O error. - - Params: - format = tuple record $(REF_ALTTEXT _format, formattedRead, std, _format) - - Returns: - The input range set up to parse one line at a time into a record tuple. - - See_Also: - - It is similar to $(LREF byLine) and uses - $(REF_ALTTEXT _format, formattedRead, std, _format) under the hood. - */ - template byRecord(Fields...) - { - auto byRecord(string format) - { - return ByRecordImpl!(Fields)(this, format); - } - } - - /// - @system unittest - { - static import std.file; - import std.typecons : tuple; - - // prepare test file - auto testFile = std.file.deleteme(); - scope(failure) printf("Failed test at line %d\n", __LINE__); - std.file.write(testFile, "1 2\n4 1\n5 100"); - scope(exit) std.file.remove(testFile); - - File f = File(testFile); - scope(exit) f.close(); - - auto expected = [tuple(1, 2), tuple(4, 1), tuple(5, 100)]; - uint i; - foreach (e; f.byRecord!(int, int)("%s %s")) - { - assert(e == expected[i++]); - } - } - - // Note: This was documented until 2013/08 - /* - * Range that reads a chunk at a time. - */ - private struct ByChunkImpl - { - private: - File file_; - ubyte[] chunk_; - - void prime() - { - chunk_ = file_.rawRead(chunk_); - if (chunk_.length == 0) - file_.detach(); - } - - public: - this(File file, size_t size) - { - this(file, new ubyte[](size)); - } - - this(File file, ubyte[] buffer) - { - import std.exception : enforce; - enforce(buffer.length, "size must be larger than 0"); - file_ = file; - chunk_ = buffer; - prime(); - } - - // `ByChunk`'s input range primitive operations. - @property nothrow - bool empty() const - { - return !file_.isOpen; - } - - /// Ditto - @property nothrow - ubyte[] front() - { - version (assert) - { - import core.exception : RangeError; - if (empty) - throw new RangeError(); - } - return chunk_; - } - - /// Ditto - void popFront() - { - version (assert) - { - import core.exception : RangeError; - if (empty) - throw new RangeError(); - } - prime(); - } - } - -/** -Returns an $(REF_ALTTEXT input range, isInputRange, std,range,primitives) -set up to read from the file handle a chunk at a time. - -The element type for the range will be `ubyte[]`. Range primitives -may throw `StdioException` on I/O error. - -Example: ---------- -void main() -{ - // Read standard input 4KB at a time - foreach (ubyte[] buffer; stdin.byChunk(4096)) - { - ... use buffer ... - } -} ---------- - -The parameter may be a number (as shown in the example above) dictating the -size of each chunk. Alternatively, `byChunk` accepts a -user-provided buffer that it uses directly. - -Example: ---------- -void main() -{ - // Read standard input 4KB at a time - foreach (ubyte[] buffer; stdin.byChunk(new ubyte[4096])) - { - ... use buffer ... - } -} ---------- - -In either case, the content of the buffer is reused across calls. That means -`front` will not persist after `popFront` is called, so if retention is -needed, the caller must copy its contents (e.g. by calling `buffer.dup`). - -In the example above, `buffer.length` is 4096 for all iterations, except -for the last one, in which case `buffer.length` may be less than 4096 (but -always greater than zero). - -With the mentioned limitations, `byChunk` works with any algorithm -compatible with input ranges. - -Example: ---- -// Efficient file copy, 1MB at a time. -import std.algorithm, std.stdio; -void main() -{ - stdin.byChunk(1024 * 1024).copy(stdout.lockingTextWriter()); -} ---- - -$(REF joiner, std,algorithm,iteration) can be used to join chunks together into -a single range lazily. -Example: ---- -import std.algorithm, std.stdio; -void main() -{ - //Range of ranges - static assert(is(typeof(stdin.byChunk(4096).front) == ubyte[])); - //Range of elements - static assert(is(typeof(stdin.byChunk(4096).joiner.front) == ubyte)); -} ---- - -Returns: A call to `byChunk` returns a range initialized with the `File` -object and the appropriate buffer. - -Throws: If the user-provided size is zero or the user-provided buffer -is empty, throws an `Exception`. In case of an I/O error throws -`StdioException`. - */ - auto byChunk(size_t chunkSize) - { - return ByChunkImpl(this, chunkSize); - } -/// Ditto - auto byChunk(ubyte[] buffer) - { - return ByChunkImpl(this, buffer); - } - - @system unittest - { - static import std.file; - - scope(failure) printf("Failed test at line %d\n", __LINE__); - - auto deleteme = testFilename(); - std.file.write(deleteme, "asd\ndef\nasdf"); - - auto witness = ["asd\n", "def\n", "asdf" ]; - auto f = File(deleteme); - scope(exit) - { - f.close(); - assert(!f.isOpen); - std.file.remove(deleteme); - } - - uint i; - foreach (chunk; f.byChunk(4)) - assert(chunk == cast(ubyte[]) witness[i++]); - - assert(i == witness.length); - } - - @system unittest - { - static import std.file; - - scope(failure) printf("Failed test at line %d\n", __LINE__); - - auto deleteme = testFilename(); - std.file.write(deleteme, "asd\ndef\nasdf"); - - auto witness = ["asd\n", "def\n", "asdf" ]; - auto f = File(deleteme); - scope(exit) - { - f.close(); - assert(!f.isOpen); - std.file.remove(deleteme); - } - - uint i; - foreach (chunk; f.byChunk(new ubyte[4])) - assert(chunk == cast(ubyte[]) witness[i++]); - - assert(i == witness.length); - } - - // Note: This was documented until 2013/08 -/* -`Range` that locks the file and allows fast writing to it. - */ - struct LockingTextWriter - { - private: - import std.range.primitives : ElementType, isInfinite, isInputRange; - // Access the FILE* handle through the 'file_' member - // to keep the object alive through refcounting - File file_; - - // the unshared version of FILE* handle, extracted from the File object - @property _iobuf* handle_() @trusted { return cast(_iobuf*) file_._p.handle; } - - // the file's orientation (byte- or wide-oriented) - int orientation_; - - // Buffers for when we need to transcode. - wchar highSurrogate = '\0'; // '\0' indicates empty - void highSurrogateShouldBeEmpty() @safe - { - import std.utf : UTFException; - if (highSurrogate != '\0') - throw new UTFException("unpaired surrogate UTF-16 value"); - } - char[4] rbuf8; - size_t rbuf8Filled = 0; - public: - - this(ref File f) @trusted - { - import std.exception : enforce; - - enforce(f._p && f._p.handle, "Attempting to write to closed File"); - file_ = f; - FILE* fps = f._p.handle; - - version (CRuntime_Microsoft) - { - // Microsoft doesn't implement fwide. Instead, there's the - // concept of ANSI/UNICODE mode. fputc doesn't work in UNICODE - // mode; fputwc has to be used. So that essentially means - // "wide-oriented" for us. - immutable int mode = __setmode(f.fileno, _O_TEXT); - // Set some arbitrary mode to obtain the previous one. - if (mode != -1) // __setmode() succeeded - { - __setmode(f.fileno, mode); // Restore previous mode. - if (mode & (_O_WTEXT | _O_U16TEXT | _O_U8TEXT)) - { - orientation_ = 1; // wide - } - } - } - else - { - import core.stdc.wchar_ : fwide; - orientation_ = fwide(fps, 0); - } - - _FLOCK(fps); - } - - ~this() @trusted - { - if (auto p = file_._p) - { - if (p.handle) _FUNLOCK(p.handle); - } - file_ = File.init; - /* Destroy file_ before possibly throwing. Else it wouldn't be - destroyed, and its reference count would be wrong. */ - highSurrogateShouldBeEmpty(); - } - - this(this) @trusted - { - if (auto p = file_._p) - { - if (p.handle) _FLOCK(p.handle); - } - } - - /// Range primitive implementations. - void put(A)(scope A writeme) - if ((isSomeChar!(ElementType!A) || - is(ElementType!A : const(ubyte))) && - isInputRange!A && - !isInfinite!A) - { - import std.exception : errnoEnforce; - - alias C = ElementEncodingType!A; - static assert(!is(C == void)); - static if (isSomeString!A && C.sizeof == 1 || is(A : const(ubyte)[])) - { - if (orientation_ <= 0) - { - //file.write(writeme); causes infinite recursion!!! - //file.rawWrite(writeme); - auto result = trustedFwrite(file_._p.handle, writeme); - if (result != writeme.length) errnoEnforce(0); - return; - } - } - - // put each element in turn. - foreach (c; writeme) - { - put(c); - } - } - - /// ditto - void put(C)(scope C c) @safe if (isSomeChar!C || is(C : const(ubyte))) - { - import std.utf : decodeFront, encode, stride; - - static if (c.sizeof == 1) - { - highSurrogateShouldBeEmpty(); - if (orientation_ <= 0) trustedFPUTC(c, handle_); - else if (c <= 0x7F) trustedFPUTWC(c, handle_); - else if (c >= 0b1100_0000) // start byte of multibyte sequence - { - rbuf8[0] = c; - rbuf8Filled = 1; - } - else // continuation byte of multibyte sequence - { - rbuf8[rbuf8Filled] = c; - ++rbuf8Filled; - if (stride(rbuf8[]) == rbuf8Filled) // sequence is complete - { - char[] str = rbuf8[0 .. rbuf8Filled]; - immutable dchar d = decodeFront(str); - wchar_t[4 / wchar_t.sizeof] wbuf; - immutable size = encode(wbuf, d); - foreach (i; 0 .. size) - trustedFPUTWC(wbuf[i], handle_); - rbuf8Filled = 0; - } - } - } - else static if (c.sizeof == 2) - { - import std.utf : decode; - - if (c <= 0x7F) - { - highSurrogateShouldBeEmpty(); - if (orientation_ <= 0) trustedFPUTC(c, handle_); - else trustedFPUTWC(c, handle_); - } - else if (0xD800 <= c && c <= 0xDBFF) // high surrogate - { - highSurrogateShouldBeEmpty(); - highSurrogate = c; - } - else // standalone or low surrogate - { - dchar d = c; - if (highSurrogate != '\0') - { - immutable wchar[2] rbuf = [highSurrogate, c]; - size_t index = 0; - d = decode(rbuf[], index); - highSurrogate = 0; - } - if (orientation_ <= 0) - { - char[4] wbuf; - immutable size = encode(wbuf, d); - foreach (i; 0 .. size) - trustedFPUTC(wbuf[i], handle_); - } - else - { - wchar_t[4 / wchar_t.sizeof] wbuf; - immutable size = encode(wbuf, d); - foreach (i; 0 .. size) - trustedFPUTWC(wbuf[i], handle_); - } - rbuf8Filled = 0; - } - } - else // 32-bit characters - { - import std.utf : encode; - - highSurrogateShouldBeEmpty(); - if (orientation_ <= 0) - { - if (c <= 0x7F) - { - trustedFPUTC(c, handle_); - } - else - { - char[4] buf = void; - immutable len = encode(buf, c); - foreach (i ; 0 .. len) - trustedFPUTC(buf[i], handle_); - } - } - else - { - version (Windows) - { - import std.utf : isValidDchar; - - assert(isValidDchar(c)); - if (c <= 0xFFFF) - { - trustedFPUTWC(cast(wchar_t) c, handle_); - } - else - { - trustedFPUTWC(cast(wchar_t) - ((((c - 0x10000) >> 10) & 0x3FF) - + 0xD800), handle_); - trustedFPUTWC(cast(wchar_t) - (((c - 0x10000) & 0x3FF) + 0xDC00), - handle_); - } - } - else version (Posix) - { - trustedFPUTWC(cast(wchar_t) c, handle_); - } - else - { - static assert(0); - } - } - } - } - } - - /** - * Output range which locks the file when created, and unlocks the file when it goes - * out of scope. - * - * Returns: An $(REF_ALTTEXT output range, isOutputRange, std, range, primitives) - * which accepts string types, `ubyte[]`, individual character types, and - * individual `ubyte`s. - * - * Note: Writing either arrays of `char`s or `ubyte`s is faster than - * writing each character individually from a range. For large amounts of data, - * writing the contents in chunks using an intermediary array can result - * in a speed increase. - * - * Throws: $(REF UTFException, std, utf) if the data given is a `char` range - * and it contains malformed UTF data. - * - * See_Also: $(LREF byChunk) for an example. - */ - auto lockingTextWriter() @safe - { - return LockingTextWriter(this); - } - - // An output range which optionally locks the file and puts it into - // binary mode (similar to rawWrite). Because it needs to restore - // the file mode on destruction, it is RefCounted on Windows. - struct BinaryWriterImpl(bool locking) - { - import std.traits : hasIndirections; - private: - // Access the FILE* handle through the 'file_' member - // to keep the object alive through refcounting - File file_; - string name; - - version (Windows) - { - fileno_t fd; - int oldMode; - version (CRuntime_DigitalMars) - ubyte oldInfo; - } - - public: - // Don't use this, but `File.lockingBinaryWriter()` instead. - // Must be public for RefCounted and emplace() in druntime. - this(scope ref File f) - { - import std.exception : enforce; - file_ = f; - enforce(f._p && f._p.handle); - name = f._name; - FILE* fps = f._p.handle; - static if (locking) - _FLOCK(fps); - - version (Windows) - { - .fflush(fps); // before changing translation mode - fd = .fileno(fps); - oldMode = .__setmode(fd, _O_BINARY); - version (CRuntime_DigitalMars) - { - import core.atomic : atomicOp; - - // https://issues.dlang.org/show_bug.cgi?id=4243 - oldInfo = __fhnd_info[fd]; - atomicOp!"&="(__fhnd_info[fd], ~FHND_TEXT); - } - } - } - - ~this() - { - if (!file_._p || !file_._p.handle) - return; - - FILE* fps = file_._p.handle; - - version (Windows) - { - .fflush(fps); // before restoring translation mode - version (CRuntime_DigitalMars) - { - // https://issues.dlang.org/show_bug.cgi?id=4243 - __fhnd_info[fd] = oldInfo; - } - .__setmode(fd, oldMode); - } - - _FUNLOCK(fps); - } - - void rawWrite(T)(in T[] buffer) - { - import std.conv : text; - import std.exception : errnoEnforce; - - auto result = trustedFwrite(file_._p.handle, buffer); - if (result == result.max) result = 0; - errnoEnforce(result == buffer.length, - text("Wrote ", result, " instead of ", buffer.length, - " objects of type ", T.stringof, " to file `", - name, "'")); - } - - version (Windows) - { - @disable this(this); - } - else - { - this(this) - { - if (auto p = file_._p) - { - if (p.handle) _FLOCK(p.handle); - } - } - } - - void put(T)(auto ref scope const T value) - if (!hasIndirections!T && - !isInputRange!T) - { - rawWrite((&value)[0 .. 1]); - } - - void put(T)(scope const(T)[] array) - if (!hasIndirections!T && - !isInputRange!T) - { - rawWrite(array); - } - } - -/** Returns an output range that locks the file and allows fast writing to it. - -Example: -Produce a grayscale image of the $(LINK2 https://en.wikipedia.org/wiki/Mandelbrot_set, Mandelbrot set) -in binary $(LINK2 https://en.wikipedia.org/wiki/Netpbm_format, Netpbm format) to standard output. ---- -import std.algorithm, std.complex, std.range, std.stdio; - -void main() -{ - enum size = 500; - writef("P5\n%d %d %d\n", size, size, ubyte.max); - - iota(-1, 3, 2.0/size).map!(y => - iota(-1.5, 0.5, 2.0/size).map!(x => - cast(ubyte)(1+ - recurrence!((a, n) => x + y * complex(0, 1) + a[n-1]^^2)(complex(0)) - .take(ubyte.max) - .countUntil!(z => z.re^^2 + z.im^^2 > 4)) - ) - ) - .copy(stdout.lockingBinaryWriter); -} ---- -*/ - auto lockingBinaryWriter() - { - alias LockingBinaryWriterImpl = BinaryWriterImpl!true; - - version (Windows) - { - import std.typecons : RefCounted; - alias LockingBinaryWriter = RefCounted!LockingBinaryWriterImpl; - } - else - alias LockingBinaryWriter = LockingBinaryWriterImpl; - - return LockingBinaryWriter(this); - } - - @system unittest - { - import std.algorithm.mutation : reverse; - import std.exception : collectException; - static import std.file; - import std.range : only, retro; - import std.string : format; - - auto deleteme = testFilename(); - scope(exit) collectException(std.file.remove(deleteme)); - - { - auto writer = File(deleteme, "wb").lockingBinaryWriter(); - auto input = File(deleteme, "rb"); - - ubyte[1] byteIn = [42]; - writer.rawWrite(byteIn); - destroy(writer); - - ubyte[1] byteOut = input.rawRead(new ubyte[1]); - assert(byteIn[0] == byteOut[0]); - } - - auto output = File(deleteme, "wb"); - auto writer = output.lockingBinaryWriter(); - auto input = File(deleteme, "rb"); - - T[] readExact(T)(T[] buf) - { - auto result = input.rawRead(buf); - assert(result.length == buf.length, - "Read %d out of %d bytes" - .format(result.length, buf.length)); - return result; - } - - // test raw values - ubyte byteIn = 42; - byteIn.only.copy(writer); output.flush(); - ubyte byteOut = readExact(new ubyte[1])[0]; - assert(byteIn == byteOut); - - // test arrays - ubyte[] bytesIn = [1, 2, 3, 4, 5]; - bytesIn.copy(writer); output.flush(); - ubyte[] bytesOut = readExact(new ubyte[bytesIn.length]); - scope(failure) .writeln(bytesOut); - assert(bytesIn == bytesOut); - - // test ranges of values - bytesIn.retro.copy(writer); output.flush(); - bytesOut = readExact(bytesOut); - bytesOut.reverse(); - assert(bytesIn == bytesOut); - - // test string - "foobar".copy(writer); output.flush(); - char[] charsOut = readExact(new char[6]); - assert(charsOut == "foobar"); - - // test ranges of arrays - only("foo", "bar").copy(writer); output.flush(); - charsOut = readExact(charsOut); - assert(charsOut == "foobar"); - - // test that we are writing arrays as is, - // without UTF-8 transcoding - "foo"d.copy(writer); output.flush(); - dchar[] dcharsOut = readExact(new dchar[3]); - assert(dcharsOut == "foo"); - } - -/** Returns the size of the file in bytes, ulong.max if file is not searchable or throws if the operation fails. -Example: ---- -import std.stdio, std.file; - -void main() -{ - string deleteme = "delete.me"; - auto file_handle = File(deleteme, "w"); - file_handle.write("abc"); //create temporary file - scope(exit) deleteme.remove; //remove temporary file at scope exit - - assert(file_handle.size() == 3); //check if file size is 3 bytes -} ---- -*/ - @property ulong size() @safe - { - import std.exception : collectException; - - ulong pos = void; - if (collectException(pos = tell)) return ulong.max; - scope(exit) seek(pos); - seek(0, SEEK_END); - return tell; - } -} - -@system unittest -{ - @system struct SystemToString - { - string toString() - { - return "system"; - } - } - - @trusted struct TrustedToString - { - string toString() - { - return "trusted"; - } - } - - @safe struct SafeToString - { - string toString() - { - return "safe"; - } - } - - @system void systemTests() - { - //system code can write to files/stdout with anything! - if (false) - { - auto f = File(); - - f.write("just a string"); - f.write("string with arg: ", 47); - f.write(SystemToString()); - f.write(TrustedToString()); - f.write(SafeToString()); - - write("just a string"); - write("string with arg: ", 47); - write(SystemToString()); - write(TrustedToString()); - write(SafeToString()); - - f.writeln("just a string"); - f.writeln("string with arg: ", 47); - f.writeln(SystemToString()); - f.writeln(TrustedToString()); - f.writeln(SafeToString()); - - writeln("just a string"); - writeln("string with arg: ", 47); - writeln(SystemToString()); - writeln(TrustedToString()); - writeln(SafeToString()); - - f.writef("string with arg: %s", 47); - f.writef("%s", SystemToString()); - f.writef("%s", TrustedToString()); - f.writef("%s", SafeToString()); - - writef("string with arg: %s", 47); - writef("%s", SystemToString()); - writef("%s", TrustedToString()); - writef("%s", SafeToString()); - - f.writefln("string with arg: %s", 47); - f.writefln("%s", SystemToString()); - f.writefln("%s", TrustedToString()); - f.writefln("%s", SafeToString()); - - writefln("string with arg: %s", 47); - writefln("%s", SystemToString()); - writefln("%s", TrustedToString()); - writefln("%s", SafeToString()); - } - } - - @safe void safeTests() - { - auto f = File(); - - //safe code can write to files only with @safe and @trusted code... - if (false) - { - f.write("just a string"); - f.write("string with arg: ", 47); - f.write(TrustedToString()); - f.write(SafeToString()); - - write("just a string"); - write("string with arg: ", 47); - write(TrustedToString()); - write(SafeToString()); - - f.writeln("just a string"); - f.writeln("string with arg: ", 47); - f.writeln(TrustedToString()); - f.writeln(SafeToString()); - - writeln("just a string"); - writeln("string with arg: ", 47); - writeln(TrustedToString()); - writeln(SafeToString()); - - f.writef("string with arg: %s", 47); - f.writef("%s", TrustedToString()); - f.writef("%s", SafeToString()); - - writef("string with arg: %s", 47); - writef("%s", TrustedToString()); - writef("%s", SafeToString()); - - f.writefln("string with arg: %s", 47); - f.writefln("%s", TrustedToString()); - f.writefln("%s", SafeToString()); - - writefln("string with arg: %s", 47); - writefln("%s", TrustedToString()); - writefln("%s", SafeToString()); - } - - static assert(!__traits(compiles, f.write(SystemToString().toString()))); - static assert(!__traits(compiles, f.writeln(SystemToString()))); - static assert(!__traits(compiles, f.writef("%s", SystemToString()))); - static assert(!__traits(compiles, f.writefln("%s", SystemToString()))); - - static assert(!__traits(compiles, write(SystemToString().toString()))); - static assert(!__traits(compiles, writeln(SystemToString()))); - static assert(!__traits(compiles, writef("%s", SystemToString()))); - static assert(!__traits(compiles, writefln("%s", SystemToString()))); - } - - systemTests(); - safeTests(); -} - -@safe unittest -{ - import std.exception : collectException; - static import std.file; - - auto deleteme = testFilename(); - scope(exit) collectException(std.file.remove(deleteme)); - std.file.write(deleteme, "1 2 3"); - auto f = File(deleteme); - assert(f.size == 5); - assert(f.tell == 0); -} - -@safe unittest -{ - static import std.file; - import std.range : chain, only, repeat; - import std.range.primitives : isOutputRange; - - auto deleteme = testFilename(); - scope(exit) std.file.remove(deleteme); - - { - auto writer = File(deleteme, "w").lockingTextWriter(); - static assert(isOutputRange!(typeof(writer), dchar)); - writer.put("日本語"); - writer.put("日本語"w); - writer.put("日本語"d); - writer.put('日'); - writer.put(chain(only('本'), only('語'))); - // https://issues.dlang.org/show_bug.cgi?id=11945 - writer.put(repeat('#', 12)); - // https://issues.dlang.org/show_bug.cgi?id=17229 - writer.put(cast(immutable(ubyte)[])"日本語"); - } - assert(File(deleteme).readln() == "日本語日本語日本語日本語############日本語"); -} - -@safe unittest // wchar -> char -{ - static import std.file; - import std.exception : assertThrown; - import std.utf : UTFException; - - auto deleteme = testFilename(); - scope(exit) std.file.remove(deleteme); - - { - auto writer = File(deleteme, "w").lockingTextWriter(); - writer.put("\U0001F608"w); - } - assert(std.file.readText!string(deleteme) == "\U0001F608"); - - // Test invalid input: unpaired high surrogate - { - immutable wchar surr = "\U0001F608"w[0]; - auto f = File(deleteme, "w"); - assertThrown!UTFException(() { - auto writer = f.lockingTextWriter(); - writer.put('x'); - writer.put(surr); - assertThrown!UTFException(writer.put(char('y'))); - assertThrown!UTFException(writer.put(wchar('y'))); - assertThrown!UTFException(writer.put(dchar('y'))); - assertThrown!UTFException(writer.put(surr)); - // First `surr` is still unpaired at this point. `writer` gets - // destroyed now, and the destructor throws a UTFException for - // the unpaired surrogate. - } ()); - } - assert(std.file.readText!string(deleteme) == "x"); - - // Test invalid input: unpaired low surrogate - { - immutable wchar surr = "\U0001F608"w[1]; - auto writer = File(deleteme, "w").lockingTextWriter(); - assertThrown!UTFException(writer.put(surr)); - writer.put('y'); - assertThrown!UTFException(writer.put(surr)); - } - assert(std.file.readText!string(deleteme) == "y"); -} - -@safe unittest // https://issues.dlang.org/show_bug.cgi?id=18801 -{ - static import std.file; - import std.string : stripLeft; - - auto deleteme = testFilename(); - scope(exit) std.file.remove(deleteme); - - { - auto writer = File(deleteme, "w,ccs=UTF-8").lockingTextWriter(); - writer.put("foo"); - } - assert(std.file.readText!string(deleteme).stripLeft("\uFEFF") == "foo"); - - { - auto writer = File(deleteme, "a,ccs=UTF-8").lockingTextWriter(); - writer.put("bar"); - } - assert(std.file.readText!string(deleteme).stripLeft("\uFEFF") == "foobar"); -} -@safe unittest // char/wchar -> wchar_t -{ - import core.stdc.locale : LC_CTYPE, setlocale; - import core.stdc.wchar_ : fwide; - import core.stdc.string : strlen; - import std.algorithm.searching : any, endsWith; - import std.conv : text; - import std.meta : AliasSeq; - import std.string : fromStringz, stripLeft; - static import std.file; - auto deleteme = testFilename(); - scope(exit) std.file.remove(deleteme); - const char* oldCt = () @trusted { - const(char)* p = setlocale(LC_CTYPE, null); - // Subsequent calls to `setlocale` might invalidate this return value, - // so duplicate it. - // See: https://github.com/dlang/phobos/pull/7660 - return p ? p[0 .. strlen(p) + 1].idup.ptr : null; - }(); - const utf8 = ["en_US.UTF-8", "C.UTF-8", ".65001"].any!((loc) @trusted { - return setlocale(LC_CTYPE, loc.ptr).fromStringz.endsWith(loc); - }); - scope(exit) () @trusted { setlocale(LC_CTYPE, oldCt); } (); - version (CRuntime_DigitalMars) // DM can't handle Unicode above U+07FF. - { - alias strs = AliasSeq!("xä\u07FE", "yĂś\u07FF"w); - } - else - { - alias strs = AliasSeq!("xä\U0001F607", "yĂś\U0001F608"w); - } - { - auto f = File(deleteme, "w"); - version (CRuntime_Microsoft) - { - () @trusted { __setmode(fileno(f.getFP()), _O_U8TEXT); } (); - } - else - { - assert(fwide(f.getFP(), 1) == 1); - } - auto writer = f.lockingTextWriter(); - assert(writer.orientation_ == 1); - static foreach (s; strs) writer.put(s); - } - assert(std.file.readText!string(deleteme).stripLeft("\uFEFF") == - text(strs)); -} -@safe unittest // https://issues.dlang.org/show_bug.cgi?id=18789 -{ - static import std.file; - auto deleteme = testFilename(); - scope(exit) std.file.remove(deleteme); - // converting to char - { - auto f = File(deleteme, "w"); - f.writeln("\U0001F608"w); // UTFException - } - // converting to wchar_t - { - auto f = File(deleteme, "w,ccs=UTF-16LE"); - // from char - f.writeln("Ăś"); // writes garbage - f.writeln("\U0001F608"); // ditto - // from wchar - f.writeln("\U0001F608"w); // leads to ErrnoException - } -} - -@safe unittest -{ - import std.exception : collectException; - auto e = collectException({ File f; f.writeln("Hello!"); }()); - assert(e && e.msg == "Attempting to write to closed File"); -} - -@safe unittest // https://issues.dlang.org/show_bug.cgi?id=21592 -{ - import std.exception : collectException; - import std.utf : UTFException; - static import std.file; - auto deleteme = testFilename(); - scope(exit) std.file.remove(deleteme); - auto f = File(deleteme, "w"); - auto e = collectException!UTFException(f.writeln(wchar(0xD801))); - assert(e.next is null); -} - -version (StdStressTest) -{ - // https://issues.dlang.org/show_bug.cgi?id=15768 - @system unittest - { - import std.parallelism : parallel; - import std.range : iota; - - auto deleteme = testFilename(); - stderr = File(deleteme, "w"); - - foreach (t; 1_000_000.iota.parallel) - { - stderr.write("aaa"); - } - } -} - -/// Used to specify the lock type for `File.lock` and `File.tryLock`. -enum LockType -{ - /** - * Specifies a _read (shared) lock. A _read lock denies all processes - * write access to the specified region of the file, including the - * process that first locks the region. All processes can _read the - * locked region. Multiple simultaneous _read locks are allowed, as - * long as there are no exclusive locks. - */ - read, - - /** - * Specifies a read/write (exclusive) lock. A read/write lock denies all - * other processes both read and write access to the locked file region. - * If a segment has an exclusive lock, it may not have any shared locks - * or other exclusive locks. - */ - readWrite -} - -struct LockingTextReader -{ - private File _f; - private char _front; - private bool _hasChar; - - this(File f) - { - import std.exception : enforce; - enforce(f.isOpen, "LockingTextReader: File must be open"); - _f = f; - _FLOCK(_f._p.handle); - } - - this(this) - { - _FLOCK(_f._p.handle); - } - - ~this() - { - if (_hasChar) - ungetc(_front, cast(FILE*)_f._p.handle); - - // File locking has its own reference count - if (_f.isOpen) _FUNLOCK(_f._p.handle); - } - - void opAssign(LockingTextReader r) - { - import std.algorithm.mutation : swap; - swap(this, r); - } - - @property bool empty() - { - if (!_hasChar) - { - if (!_f.isOpen || _f.eof) - return true; - immutable int c = _FGETC(cast(_iobuf*) _f._p.handle); - if (c == EOF) - { - .destroy(_f); - return true; - } - _front = cast(char) c; - _hasChar = true; - } - return false; - } - - @property char front() - { - if (!_hasChar) - { - version (assert) - { - import core.exception : RangeError; - if (empty) - throw new RangeError(); - } - else - { - empty; - } - } - return _front; - } - - void popFront() - { - if (!_hasChar) - empty; - _hasChar = false; - } -} - -@system unittest -{ - // @system due to readf - static import std.file; - import std.range.primitives : isInputRange; - - static assert(isInputRange!LockingTextReader); - auto deleteme = testFilename(); - std.file.write(deleteme, "1 2 3"); - scope(exit) std.file.remove(deleteme); - int x; - auto f = File(deleteme); - f.readf("%s ", &x); - assert(x == 1); - f.readf("%d ", &x); - assert(x == 2); - f.readf("%d ", &x); - assert(x == 3); -} - -// https://issues.dlang.org/show_bug.cgi?id=13686 -@system unittest -{ - import std.algorithm.comparison : equal; - static import std.file; - import std.utf : byDchar; - - auto deleteme = testFilename(); - std.file.write(deleteme, "Тест"); - scope(exit) std.file.remove(deleteme); - - string s; - File(deleteme).readf("%s", &s); - assert(s == "Тест"); - - auto ltr = LockingTextReader(File(deleteme)).byDchar; - assert(equal(ltr, "Тест".byDchar)); -} - -// https://issues.dlang.org/show_bug.cgi?id=12320 -@system unittest -{ - static import std.file; - auto deleteme = testFilename(); - std.file.write(deleteme, "ab"); - scope(exit) std.file.remove(deleteme); - auto ltr = LockingTextReader(File(deleteme)); - assert(ltr.front == 'a'); - ltr.popFront(); - assert(ltr.front == 'b'); - ltr.popFront(); - assert(ltr.empty); -} - -// https://issues.dlang.org/show_bug.cgi?id=14861 -@system unittest -{ - // @system due to readf - static import std.file; - auto deleteme = testFilename(); - File fw = File(deleteme, "w"); - for (int i; i != 5000; i++) - fw.writeln(i, ";", "ИванОв;Пётр;Петрович"); - fw.close(); - scope(exit) std.file.remove(deleteme); - // Test read - File fr = File(deleteme, "r"); - scope (exit) fr.close(); - int nom; string fam, nam, ot; - // Error format read - while (!fr.eof) - fr.readf("%s;%s;%s;%s\n", &nom, &fam, &nam, &ot); -} - -/** - * Indicates whether `T` is a file handle, i.e. the type - * is implicitly convertable to $(LREF File) or a pointer to a - * $(REF FILE, core,stdc,stdio). - * - * Returns: - * `true` if `T` is a file handle, `false` otherwise. - */ -template isFileHandle(T) -{ - enum isFileHandle = is(T : FILE*) || - is(T : File); -} - -/// -@safe unittest -{ - static assert(isFileHandle!(FILE*)); - static assert(isFileHandle!(File)); -} - -/** - * Property used by writeln/etc. so it can infer @safe since stdout is __gshared - */ -private @property File trustedStdout() @trusted -{ - return stdout; -} - -/*********************************** -Writes its arguments in text format to standard output (without a trailing newline). - -Params: - args = the items to write to `stdout` - -Throws: In case of an I/O error, throws an `StdioException`. - -Example: - Reads `stdin` and writes it to `stdout` with an argument - counter. ---- -import std.stdio; - -void main() -{ - string line; - - for (size_t count = 0; (line = readln) !is null; count++) - { - write("Input ", count, ": ", line, "\n"); - } -} ---- - */ -void write(T...)(T args) -if (!is(T[0] : File)) -{ - trustedStdout.write(args); -} - -@system unittest -{ - static import std.file; - - scope(failure) printf("Failed test at line %d\n", __LINE__); - void[] buf; - if (false) write(buf); - // test write - auto deleteme = testFilename(); - auto f = File(deleteme, "w"); - f.write("Hello, ", "world number ", 42, "!"); - f.close(); - scope(exit) { std.file.remove(deleteme); } - assert(cast(char[]) std.file.read(deleteme) == "Hello, world number 42!"); -} - -/*********************************** - * Equivalent to `write(args, '\n')`. Calling `writeln` without - * arguments is valid and just prints a newline to the standard - * output. - * - * Params: - * args = the items to write to `stdout` - * - * Throws: - * In case of an I/O error, throws an $(LREF StdioException). - * Example: - * Reads `stdin` and writes it to `stdout` with an argument - * counter. ---- -import std.stdio; - -void main() -{ - string line; - - for (size_t count = 0; (line = readln) !is null; count++) - { - writeln("Input ", count, ": ", line); - } -} ---- - */ -void writeln(T...)(T args) -{ - static if (T.length == 0) - { - import std.exception : enforce; - - enforce(fputc('\n', .trustedStdout._p.handle) != EOF, "fputc failed"); - } - else static if (T.length == 1 && - is(T[0] : const(char)[]) && - (is(T[0] == U[], U) || __traits(isStaticArray, T[0]))) - { - // Specialization for strings - a very frequent case - auto w = .trustedStdout.lockingTextWriter(); - - static if (__traits(isStaticArray, T[0])) - { - w.put(args[0][]); - } - else - { - w.put(args[0]); - } - w.put('\n'); - } - else - { - // Most general instance - trustedStdout.write(args, '\n'); - } -} - -@safe unittest -{ - // Just make sure the call compiles - if (false) writeln(); - - if (false) writeln("wyda"); - - // https://issues.dlang.org/show_bug.cgi?id=8040 - if (false) writeln(null); - if (false) writeln(">", null, "<"); - - // https://issues.dlang.org/show_bug.cgi?id=14041 - if (false) - { - char[8] a; - writeln(a); - immutable b = a; - b.writeln; - const c = a[]; - c.writeln; - } -} - -@system unittest -{ - static import std.file; - - scope(failure) printf("Failed test at line %d\n", __LINE__); - - // test writeln - auto deleteme = testFilename(); - auto f = File(deleteme, "w"); - scope(exit) { std.file.remove(deleteme); } - f.writeln("Hello, ", "world number ", 42, "!"); - f.close(); - version (Windows) - assert(cast(char[]) std.file.read(deleteme) == - "Hello, world number 42!\r\n"); - else - assert(cast(char[]) std.file.read(deleteme) == - "Hello, world number 42!\n"); - - // test writeln on stdout - auto saveStdout = stdout; - scope(exit) stdout = saveStdout; - stdout.open(deleteme, "w"); - writeln("Hello, ", "world number ", 42, "!"); - stdout.close(); - version (Windows) - assert(cast(char[]) std.file.read(deleteme) == - "Hello, world number 42!\r\n"); - else - assert(cast(char[]) std.file.read(deleteme) == - "Hello, world number 42!\n"); - - stdout.open(deleteme, "w"); - writeln("Hello!"c); - writeln("Hello!"w); // https://issues.dlang.org/show_bug.cgi?id=8386 - writeln("Hello!"d); // https://issues.dlang.org/show_bug.cgi?id=8386 - writeln("embedded\0null"c); // https://issues.dlang.org/show_bug.cgi?id=8730 - stdout.close(); - version (Windows) - assert(cast(char[]) std.file.read(deleteme) == - "Hello!\r\nHello!\r\nHello!\r\nembedded\0null\r\n"); - else - assert(cast(char[]) std.file.read(deleteme) == - "Hello!\nHello!\nHello!\nembedded\0null\n"); -} - -@system unittest -{ - static import std.file; - - auto deleteme = testFilename(); - auto f = File(deleteme, "w"); - scope(exit) { std.file.remove(deleteme); } - - enum EI : int { A, B } - enum ED : double { A = 0, B } // NOTE: explicit initialization to 0 required during Enum init deprecation cycle - enum EC : char { A = 0, B } // NOTE: explicit initialization to 0 required during Enum init deprecation cycle - enum ES : string { A = "aaa", B = "bbb" } - - f.writeln(EI.A); // false, but A on 2.058 - f.writeln(EI.B); // true, but B on 2.058 - - f.writeln(ED.A); // A - f.writeln(ED.B); // B - - f.writeln(EC.A); // A - f.writeln(EC.B); // B - - f.writeln(ES.A); // A - f.writeln(ES.B); // B - - f.close(); - version (Windows) - assert(cast(char[]) std.file.read(deleteme) == - "A\r\nB\r\nA\r\nB\r\nA\r\nB\r\nA\r\nB\r\n"); - else - assert(cast(char[]) std.file.read(deleteme) == - "A\nB\nA\nB\nA\nB\nA\nB\n"); -} - -@system unittest -{ - static auto useInit(T)(T ltw) - { - T val; - val = ltw; - val = T.init; - return val; - } - useInit(stdout.lockingTextWriter()); -} - -@system unittest -{ - // https://issues.dlang.org/show_bug.cgi?id=21920 - void function(string) printer = &writeln!string; - if (false) printer("Hello"); -} - - -/*********************************** -Writes formatted data to standard output (without a trailing newline). - -Params: -fmt = The $(REF_ALTTEXT format string, formattedWrite, std, _format). -When passed as a compile-time argument, the string will be statically checked -against the argument types passed. -args = Items to write. - -Note: In older versions of Phobos, it used to be possible to write: - ------- -writef(stderr, "%s", "message"); ------- - -to print a message to `stderr`. This syntax is no longer supported, and has -been superceded by: - ------- -stderr.writef("%s", "message"); ------- - -*/ -void writef(alias fmt, A...)(A args) -if (isSomeString!(typeof(fmt))) -{ - import std.format : checkFormatException; - - alias e = checkFormatException!(fmt, A); - static assert(!e, e); - return .writef(fmt, args); -} - -/// ditto -void writef(Char, A...)(in Char[] fmt, A args) -{ - trustedStdout.writef(fmt, args); -} - -@system unittest -{ - static import std.file; - - scope(failure) printf("Failed test at line %d\n", __LINE__); - - // test writef - auto deleteme = testFilename(); - auto f = File(deleteme, "w"); - scope(exit) { std.file.remove(deleteme); } - f.writef!"Hello, %s world number %s!"("nice", 42); - f.close(); - assert(cast(char[]) std.file.read(deleteme) == "Hello, nice world number 42!"); - // test write on stdout - auto saveStdout = stdout; - scope(exit) stdout = saveStdout; - stdout.open(deleteme, "w"); - writef!"Hello, %s world number %s!"("nice", 42); - stdout.close(); - assert(cast(char[]) std.file.read(deleteme) == "Hello, nice world number 42!"); -} - -/*********************************** - * Equivalent to $(D writef(fmt, args, '\n')). - */ -void writefln(alias fmt, A...)(A args) -if (isSomeString!(typeof(fmt))) -{ - import std.format : checkFormatException; - - alias e = checkFormatException!(fmt, A); - static assert(!e, e); - return .writefln(fmt, args); -} - -/// ditto -void writefln(Char, A...)(in Char[] fmt, A args) -{ - trustedStdout.writefln(fmt, args); -} - -@system unittest -{ - static import std.file; - - scope(failure) printf("Failed test at line %d\n", __LINE__); - - // test File.writefln - auto deleteme = testFilename(); - auto f = File(deleteme, "w"); - scope(exit) { std.file.remove(deleteme); } - f.writefln!"Hello, %s world number %s!"("nice", 42); - f.close(); - version (Windows) - assert(cast(char[]) std.file.read(deleteme) == - "Hello, nice world number 42!\r\n"); - else - assert(cast(char[]) std.file.read(deleteme) == - "Hello, nice world number 42!\n", - cast(char[]) std.file.read(deleteme)); - - // test writefln - auto saveStdout = stdout; - scope(exit) stdout = saveStdout; - stdout.open(deleteme, "w"); - writefln!"Hello, %s world number %s!"("nice", 42); - stdout.close(); - version (Windows) - assert(cast(char[]) std.file.read(deleteme) == - "Hello, nice world number 42!\r\n"); - else - assert(cast(char[]) std.file.read(deleteme) == - "Hello, nice world number 42!\n"); -} - -/** - * Reads formatted data from `stdin` using $(REF formattedRead, std,_format). - * Params: - * format = The $(REF_ALTTEXT format string, formattedWrite, std, _format). - * When passed as a compile-time argument, the string will be statically checked - * against the argument types passed. - * args = Items to be read. - * Returns: - * Same as `formattedRead`: The number of variables filled. If the input range `r` ends early, - * this number will be less than the number of variables provided. - * Example: ----- -// test.d -void main() -{ - import std.stdio; - foreach (_; 0 .. 3) - { - int a; - readf!" %d"(a); - writeln(++a); - } -} ----- -$(CONSOLE -% echo "1 2 3" | rdmd test.d -2 -3 -4 -) - */ -uint readf(alias format, A...)(auto ref A args) -if (isSomeString!(typeof(format))) -{ - import std.format : checkFormatException; - - alias e = checkFormatException!(format, A); - static assert(!e, e); - return .readf(format, args); -} - -/// ditto -uint readf(A...)(scope const(char)[] format, auto ref A args) -{ - return stdin.readf(format, args); -} - -@system unittest -{ - float f; - if (false) readf("%s", &f); - - char a; - wchar b; - dchar c; - if (false) readf("%s %s %s", a, b, c); - // backwards compatibility with pointers - if (false) readf("%s %s %s", a, &b, c); - if (false) readf("%s %s %s", &a, &b, &c); -} - -/********************************** - * Read line from `stdin`. - * - * This version manages its own read buffer, which means one memory allocation per call. If you are not - * retaining a reference to the read data, consider the `readln(buf)` version, which may offer - * better performance as it can reuse its read buffer. - * - * Returns: - * The line that was read, including the line terminator character. - * Params: - * S = Template parameter; the type of the allocated buffer, and the type returned. Defaults to `string`. - * terminator = Line terminator (by default, `'\n'`). - * Note: - * String terminators are not supported due to ambiguity with readln(buf) below. - * Throws: - * `StdioException` on I/O error, or `UnicodeException` on Unicode conversion error. - * Example: - * Reads `stdin` and writes it to `stdout`. ---- -import std.stdio; - -void main() -{ - string line; - while ((line = readln()) !is null) - write(line); -} ---- -*/ -S readln(S = string)(dchar terminator = '\n') -if (isSomeString!S) -{ - return stdin.readln!S(terminator); -} - -/********************************** - * Read line from `stdin` and write it to buf[], including terminating character. - * - * This can be faster than $(D line = readln()) because you can reuse - * the buffer for each call. Note that reusing the buffer means that you - * must copy the previous contents if you wish to retain them. - * - * Returns: - * `size_t` 0 for end of file, otherwise number of characters read - * Params: - * buf = Buffer used to store the resulting line data. buf is resized as necessary. - * terminator = Line terminator (by default, `'\n'`). Use $(REF newline, std,ascii) - * for portability (unless the file was opened in text mode). - * Throws: - * `StdioException` on I/O error, or `UnicodeException` on Unicode conversion error. - * Example: - * Reads `stdin` and writes it to `stdout`. ---- -import std.stdio; - -void main() -{ - char[] buf; - while (readln(buf)) - write(buf); -} ---- -*/ -size_t readln(C)(ref C[] buf, dchar terminator = '\n') -if (isSomeChar!C && is(Unqual!C == C) && !is(C == enum)) -{ - return stdin.readln(buf, terminator); -} - -/** ditto */ -size_t readln(C, R)(ref C[] buf, R terminator) -if (isSomeChar!C && is(Unqual!C == C) && !is(C == enum) && - isBidirectionalRange!R && is(typeof(terminator.front == dchar.init))) -{ - return stdin.readln(buf, terminator); -} - -@safe unittest -{ - import std.meta : AliasSeq; - - //we can't actually test readln, so at the very least, - //we test compilability - void foo() - { - readln(); - readln('\t'); - static foreach (String; AliasSeq!(string, char[], wstring, wchar[], dstring, dchar[])) - { - readln!String(); - readln!String('\t'); - } - static foreach (String; AliasSeq!(char[], wchar[], dchar[])) - {{ - String buf; - readln(buf); - readln(buf, '\t'); - readln(buf, "<br />"); - }} - } -} - -/* - * Convenience function that forwards to `core.sys.posix.stdio.fopen` - * (to `_wfopen` on Windows) - * with appropriately-constructed C-style strings. - */ -private FILE* _fopen(R1, R2)(R1 name, R2 mode = "r") -if ((isSomeFiniteCharInputRange!R1 || isSomeString!R1) && - (isSomeFiniteCharInputRange!R2 || isSomeString!R2)) -{ - import std.internal.cstring : tempCString; - - auto namez = name.tempCString!FSChar(); - auto modez = mode.tempCString!FSChar(); - - static _fopenImpl(scope const(FSChar)* namez, scope const(FSChar)* modez) @trusted nothrow @nogc - { - version (Windows) - { - return _wfopen(namez, modez); - } - else version (Posix) - { - /* - * The new opengroup large file support API is transparently - * included in the normal C bindings. http://opengroup.org/platform/lfs.html#1.0 - * if _FILE_OFFSET_BITS in druntime is 64, off_t is 64 bit and - * the normal functions work fine. If not, then large file support - * probably isn't available. Do not use the old transitional API - * (the native extern(C) fopen64, http://www.unix.org/version2/whatsnew/lfs20mar.html#3.0) - */ - import core.sys.posix.stdio : fopen; - return fopen(namez, modez); - } - else - { - return fopen(namez, modez); - } - } - return _fopenImpl(namez, modez); -} - -version (Posix) -{ - /*********************************** - * Convenience function that forwards to `core.sys.posix.stdio.popen` - * with appropriately-constructed C-style strings. - */ - FILE* _popen(R1, R2)(R1 name, R2 mode = "r") @trusted nothrow @nogc - if ((isSomeFiniteCharInputRange!R1 || isSomeString!R1) && - (isSomeFiniteCharInputRange!R2 || isSomeString!R2)) - { - import std.internal.cstring : tempCString; - - auto namez = name.tempCString!FSChar(); - auto modez = mode.tempCString!FSChar(); - - static popenImpl(const(FSChar)* namez, const(FSChar)* modez) @trusted nothrow @nogc - { - import core.sys.posix.stdio : popen; - return popen(namez, modez); - } - return popenImpl(namez, modez); - } -} - -/* - * Convenience function that forwards to `core.stdc.stdio.fwrite` - */ -private auto trustedFwrite(T)(FILE* f, const T[] obj) @trusted -{ - return fwrite(obj.ptr, T.sizeof, obj.length, f); -} - -/* - * Convenience function that forwards to `core.stdc.stdio.fread` - */ -private auto trustedFread(T)(FILE* f, T[] obj) @trusted -{ - return fread(obj.ptr, T.sizeof, obj.length, f); -} - -/** - * Iterates through the lines of a file by using `foreach`. - * - * Example: - * ---------- -void main() -{ - foreach (string line; lines(stdin)) - { - ... use line ... - } -} ---------- -The line terminator (`'\n'` by default) is part of the string read (it -could be missing in the last line of the file). Several types are -supported for `line`, and the behavior of `lines` -changes accordingly: - -$(OL $(LI If `line` has type `string`, $(D -wstring), or `dstring`, a new string of the respective type -is allocated every read.) $(LI If `line` has type $(D -char[]), `wchar[]`, `dchar[]`, the line's content -will be reused (overwritten) across reads.) $(LI If `line` -has type `immutable(ubyte)[]`, the behavior is similar to -case (1), except that no UTF checking is attempted upon input.) $(LI -If `line` has type `ubyte[]`, the behavior is -similar to case (2), except that no UTF checking is attempted upon -input.)) - -In all cases, a two-symbols versions is also accepted, in which case -the first symbol (of integral type, e.g. `ulong` or $(D -uint)) tracks the zero-based number of the current line. - -Example: ----- - foreach (ulong i, string line; lines(stdin)) - { - ... use line ... - } ----- - - In case of an I/O error, an `StdioException` is thrown. - -See_Also: -$(LREF byLine) - */ - -struct lines -{ - private File f; - private dchar terminator = '\n'; - - /** - Constructor. - Params: - f = File to read lines from. - terminator = Line separator (`'\n'` by default). - */ - this(File f, dchar terminator = '\n') - { - this.f = f; - this.terminator = terminator; - } - - int opApply(D)(scope D dg) - { - import std.traits : Parameters; - alias Parms = Parameters!(dg); - static if (isSomeString!(Parms[$ - 1])) - { - int result = 0; - static if (is(Parms[$ - 1] : const(char)[])) - alias C = char; - else static if (is(Parms[$ - 1] : const(wchar)[])) - alias C = wchar; - else static if (is(Parms[$ - 1] : const(dchar)[])) - alias C = dchar; - C[] line; - static if (Parms.length == 2) - Parms[0] i = 0; - for (;;) - { - import std.conv : to; - - if (!f.readln(line, terminator)) break; - auto copy = to!(Parms[$ - 1])(line); - static if (Parms.length == 2) - { - result = dg(i, copy); - ++i; - } - else - { - result = dg(copy); - } - if (result != 0) break; - } - return result; - } - else - { - // raw read - return opApplyRaw(dg); - } - } - // no UTF checking - int opApplyRaw(D)(scope D dg) - { - import std.conv : to; - import std.exception : assumeUnique; - import std.traits : Parameters; - - alias Parms = Parameters!(dg); - enum duplicate = is(Parms[$ - 1] : immutable(ubyte)[]); - int result = 1; - int c = void; - _FLOCK(f._p.handle); - scope(exit) _FUNLOCK(f._p.handle); - ubyte[] buffer; - static if (Parms.length == 2) - Parms[0] line = 0; - while ((c = _FGETC(cast(_iobuf*) f._p.handle)) != -1) - { - buffer ~= to!(ubyte)(c); - if (c == terminator) - { - static if (duplicate) - auto arg = assumeUnique(buffer); - else - alias arg = buffer; - // unlock the file while calling the delegate - _FUNLOCK(f._p.handle); - scope(exit) _FLOCK(f._p.handle); - static if (Parms.length == 1) - { - result = dg(arg); - } - else - { - result = dg(line, arg); - ++line; - } - if (result) break; - static if (!duplicate) - buffer.length = 0; - } - } - // can only reach when _FGETC returned -1 - if (!f.eof) throw new StdioException("Error in reading file"); // error occured - return result; - } -} - -@system unittest -{ - static import std.file; - import std.meta : AliasSeq; - - scope(failure) printf("Failed test at line %d\n", __LINE__); - - auto deleteme = testFilename(); - scope(exit) { std.file.remove(deleteme); } - - alias TestedWith = - AliasSeq!(string, wstring, dstring, - char[], wchar[], dchar[]); - foreach (T; TestedWith) - { - // test looping with an empty file - std.file.write(deleteme, ""); - auto f = File(deleteme, "r"); - foreach (T line; lines(f)) - { - assert(false); - } - f.close(); - - // test looping with a file with three lines - std.file.write(deleteme, "Line one\nline two\nline three\n"); - f.open(deleteme, "r"); - uint i = 0; - foreach (T line; lines(f)) - { - if (i == 0) assert(line == "Line one\n"); - else if (i == 1) assert(line == "line two\n"); - else if (i == 2) assert(line == "line three\n"); - else assert(false); - ++i; - } - f.close(); - - // test looping with a file with three lines, last without a newline - std.file.write(deleteme, "Line one\nline two\nline three"); - f.open(deleteme, "r"); - i = 0; - foreach (T line; lines(f)) - { - if (i == 0) assert(line == "Line one\n"); - else if (i == 1) assert(line == "line two\n"); - else if (i == 2) assert(line == "line three"); - else assert(false); - ++i; - } - f.close(); - } - - // test with ubyte[] inputs - alias TestedWith2 = AliasSeq!(immutable(ubyte)[], ubyte[]); - foreach (T; TestedWith2) - { - // test looping with an empty file - std.file.write(deleteme, ""); - auto f = File(deleteme, "r"); - foreach (T line; lines(f)) - { - assert(false); - } - f.close(); - - // test looping with a file with three lines - std.file.write(deleteme, "Line one\nline two\nline three\n"); - f.open(deleteme, "r"); - uint i = 0; - foreach (T line; lines(f)) - { - if (i == 0) assert(cast(char[]) line == "Line one\n"); - else if (i == 1) assert(cast(char[]) line == "line two\n", - T.stringof ~ " " ~ cast(char[]) line); - else if (i == 2) assert(cast(char[]) line == "line three\n"); - else assert(false); - ++i; - } - f.close(); - - // test looping with a file with three lines, last without a newline - std.file.write(deleteme, "Line one\nline two\nline three"); - f.open(deleteme, "r"); - i = 0; - foreach (T line; lines(f)) - { - if (i == 0) assert(cast(char[]) line == "Line one\n"); - else if (i == 1) assert(cast(char[]) line == "line two\n"); - else if (i == 2) assert(cast(char[]) line == "line three"); - else assert(false); - ++i; - } - f.close(); - - } - - static foreach (T; AliasSeq!(ubyte[])) - { - // test looping with a file with three lines, last without a newline - // using a counter too this time - std.file.write(deleteme, "Line one\nline two\nline three"); - auto f = File(deleteme, "r"); - uint i = 0; - foreach (ulong j, T line; lines(f)) - { - if (i == 0) assert(cast(char[]) line == "Line one\n"); - else if (i == 1) assert(cast(char[]) line == "line two\n"); - else if (i == 2) assert(cast(char[]) line == "line three"); - else assert(false); - ++i; - } - f.close(); - } -} - -/** -Iterates through a file a chunk at a time by using `foreach`. - -Example: - ---------- -void main() -{ - foreach (ubyte[] buffer; chunks(stdin, 4096)) - { - ... use buffer ... - } -} ---------- - -The content of `buffer` is reused across calls. In the - example above, `buffer.length` is 4096 for all iterations, - except for the last one, in which case `buffer.length` may - be less than 4096 (but always greater than zero). - - In case of an I/O error, an `StdioException` is thrown. -*/ -auto chunks(File f, size_t size) -{ - return ChunksImpl(f, size); -} -private struct ChunksImpl -{ - private File f; - private size_t size; - // private string fileName; // Currently, no use - - this(File f, size_t size) - in - { - assert(size, "size must be larger than 0"); - } - do - { - this.f = f; - this.size = size; - } - - int opApply(D)(scope D dg) - { - import core.stdc.stdlib : alloca; - import std.exception : enforce; - - enforce(f.isOpen, "Attempting to read from an unopened file"); - enum maxStackSize = 1024 * 16; - ubyte[] buffer = void; - if (size < maxStackSize) - buffer = (cast(ubyte*) alloca(size))[0 .. size]; - else - buffer = new ubyte[size]; - size_t r = void; - int result = 1; - uint tally = 0; - while ((r = trustedFread(f._p.handle, buffer)) > 0) - { - assert(r <= size); - if (r != size) - { - // error occured - if (!f.eof) throw new StdioException(null); - buffer.length = r; - } - static if (is(typeof(dg(tally, buffer)))) - { - if ((result = dg(tally, buffer)) != 0) break; - } - else - { - if ((result = dg(buffer)) != 0) break; - } - ++tally; - } - return result; - } -} - -@system unittest -{ - static import std.file; - - scope(failure) printf("Failed test at line %d\n", __LINE__); - - auto deleteme = testFilename(); - scope(exit) { std.file.remove(deleteme); } - - // test looping with an empty file - std.file.write(deleteme, ""); - auto f = File(deleteme, "r"); - foreach (ubyte[] line; chunks(f, 4)) - { - assert(false); - } - f.close(); - - // test looping with a file with three lines - std.file.write(deleteme, "Line one\nline two\nline three\n"); - f = File(deleteme, "r"); - uint i = 0; - foreach (ubyte[] line; chunks(f, 3)) - { - if (i == 0) assert(cast(char[]) line == "Lin"); - else if (i == 1) assert(cast(char[]) line == "e o"); - else if (i == 2) assert(cast(char[]) line == "ne\n"); - else break; - ++i; - } - f.close(); -} - -// Issue 21730 - null ptr dereferenced in ChunksImpl.opApply (SIGSEGV) -@system unittest -{ - import std.exception : assertThrown; - static import std.file; - - auto deleteme = testFilename(); - scope(exit) { if (std.file.exists(deleteme)) std.file.remove(deleteme); } - - auto err1 = File(deleteme, "w+x"); - err1.close; - std.file.remove(deleteme); - assertThrown(() {foreach (ubyte[] buf; chunks(err1, 4096)) {}}()); -} - -/** -Writes an array or range to a file. -Shorthand for $(D data.copy(File(fileName, "wb").lockingBinaryWriter)). -Similar to $(REF write, std,file), strings are written as-is, -rather than encoded according to the `File`'s $(HTTP -en.cppreference.com/w/c/io#Narrow_and_wide_orientation, -orientation). -*/ -void toFile(T)(T data, string fileName) -if (is(typeof(copy(data, stdout.lockingBinaryWriter)))) -{ - copy(data, File(fileName, "wb").lockingBinaryWriter); -} - -@system unittest -{ - static import std.file; - - auto deleteme = testFilename(); - scope(exit) { std.file.remove(deleteme); } - - "Test".toFile(deleteme); - assert(std.file.readText(deleteme) == "Test"); -} - -/********************* - * Thrown if I/O errors happen. - */ -class StdioException : Exception -{ - static import core.stdc.errno; - /// Operating system error code. - uint errno; - -/** -Initialize with a message and an error code. -*/ - this(string message, uint e = core.stdc.errno.errno) @trusted - { - import std.exception : errnoString; - errno = e; - auto sysmsg = errnoString(errno); - // If e is 0, we don't use the system error message. (The message - // is "Success", which is rather pointless for an exception.) - super(e == 0 ? message - : (message ? message ~ " (" ~ sysmsg ~ ")" : sysmsg)); - } - -/** Convenience functions that throw an `StdioException`. */ - static void opCall(string msg) @safe - { - throw new StdioException(msg); - } - -/// ditto - static void opCall() @safe - { - throw new StdioException(null, core.stdc.errno.errno); - } -} - -enum StdFileHandle: string -{ - stdin = "core.stdc.stdio.stdin", - stdout = "core.stdc.stdio.stdout", - stderr = "core.stdc.stdio.stderr", -} - -// Undocumented but public because the std* handles are aliasing it. -@property ref File makeGlobal(StdFileHandle _iob)() -{ - __gshared File.Impl impl; - __gshared File result; - - // Use an inline spinlock to make sure the initializer is only run once. - // We assume there will be at most uint.max / 2 threads trying to initialize - // `handle` at once and steal the high bit to indicate that the globals have - // been initialized. - static shared uint spinlock; - import core.atomic : atomicLoad, atomicOp, MemoryOrder; - if (atomicLoad!(MemoryOrder.acq)(spinlock) <= uint.max / 2) - { - for (;;) - { - if (atomicLoad!(MemoryOrder.acq)(spinlock) > uint.max / 2) - break; - if (atomicOp!"+="(spinlock, 1) == 1) - { - with (StdFileHandle) - assert(_iob == stdin || _iob == stdout || _iob == stderr); - impl.handle = cast() mixin(_iob); - result._p = &impl; - atomicOp!"+="(spinlock, uint.max / 2); - break; - } - atomicOp!"-="(spinlock, 1); - } - } - return result; -} - -/** The standard input stream. - - Returns: - stdin as a $(LREF File). - - Note: - The returned $(LREF File) wraps $(REF stdin,core,stdc,stdio), and - is therefore thread global. Reassigning `stdin` to a different - `File` must be done in a single-threaded or locked context in - order to avoid race conditions. - - All reading from `stdin` automatically locks the file globally, - and will cause all other threads calling `read` to wait until - the lock is released. -*/ -alias stdin = makeGlobal!(StdFileHandle.stdin); - -/// -@safe unittest -{ - // Read stdin, sort lines, write to stdout - import std.algorithm.mutation : copy; - import std.algorithm.sorting : sort; - import std.array : array; - import std.typecons : Yes; - - void main() - { - stdin // read from stdin - .byLineCopy(Yes.keepTerminator) // copying each line - .array() // convert to array of lines - .sort() // sort the lines - .copy( // copy output of .sort to an OutputRange - stdout.lockingTextWriter()); // the OutputRange - } -} - -/** - The standard output stream. - - Returns: - stdout as a $(LREF File). - - Note: - The returned $(LREF File) wraps $(REF stdout,core,stdc,stdio), and - is therefore thread global. Reassigning `stdout` to a different - `File` must be done in a single-threaded or locked context in - order to avoid race conditions. - - All writing to `stdout` automatically locks the file globally, - and will cause all other threads calling `write` to wait until - the lock is released. -*/ -alias stdout = makeGlobal!(StdFileHandle.stdout); - -/// -@safe unittest -{ - void main() - { - stdout.writeln("Write a message to stdout."); - } -} - -/// -@safe unittest -{ - void main() - { - import std.algorithm.iteration : filter, map, sum; - import std.format : format; - import std.range : iota, tee; - - int len; - const r = 6.iota - .filter!(a => a % 2) // 1 3 5 - .map!(a => a * 2) // 2 6 10 - .tee!(_ => stdout.writefln("len: %d", len++)) - .sum; - - assert(r == 18); - } -} - -/// -@safe unittest -{ - void main() - { - import std.algorithm.mutation : copy; - import std.algorithm.iteration : map; - import std.format : format; - import std.range : iota; - - 10.iota - .map!(e => "N: %d".format(e)) - .copy(stdout.lockingTextWriter()); // the OutputRange - } -} - -/** - The standard error stream. - - Returns: - stderr as a $(LREF File). - - Note: - The returned $(LREF File) wraps $(REF stderr,core,stdc,stdio), and - is therefore thread global. Reassigning `stderr` to a different - `File` must be done in a single-threaded or locked context in - order to avoid race conditions. - - All writing to `stderr` automatically locks the file globally, - and will cause all other threads calling `write` to wait until - the lock is released. -*/ -alias stderr = makeGlobal!(StdFileHandle.stderr); - -/// -@safe unittest -{ - void main() - { - stderr.writeln("Write a message to stderr."); - } -} - -@system unittest -{ - static import std.file; - import std.typecons : tuple; - - scope(failure) printf("Failed test at line %d\n", __LINE__); - auto deleteme = testFilename(); - - std.file.write(deleteme, "1 2\n4 1\n5 100"); - scope(exit) std.file.remove(deleteme); - { - File f = File(deleteme); - scope(exit) f.close(); - auto t = [ tuple(1, 2), tuple(4, 1), tuple(5, 100) ]; - uint i; - foreach (e; f.byRecord!(int, int)("%s %s")) - { - //writeln(e); - assert(e == t[i++]); - } - assert(i == 3); - } -} - -@safe unittest -{ - // Retain backwards compatibility - // https://issues.dlang.org/show_bug.cgi?id=17472 - static assert(is(typeof(stdin) == File)); - static assert(is(typeof(stdout) == File)); - static assert(is(typeof(stderr) == File)); -} - -// roll our own appender, but with "safe" arrays -private struct ReadlnAppender -{ - char[] buf; - size_t pos; - bool safeAppend = false; - - void initialize(char[] b) @safe - { - buf = b; - pos = 0; - } - @property char[] data() @trusted - { - if (safeAppend) - assumeSafeAppend(buf.ptr[0 .. pos]); - return buf.ptr[0 .. pos]; - } - - bool reserveWithoutAllocating(size_t n) - { - if (buf.length >= pos + n) // buf is already large enough - return true; - - immutable curCap = buf.capacity; - if (curCap >= pos + n) - { - buf.length = curCap; - /* Any extra capacity we end up not using can safely be claimed - by someone else. */ - safeAppend = true; - return true; - } - - return false; - } - void reserve(size_t n) @trusted - { - import core.stdc.string : memcpy; - if (!reserveWithoutAllocating(n)) - { - size_t ncap = buf.length * 2 + 128 + n; - char[] nbuf = new char[ncap]; - memcpy(nbuf.ptr, buf.ptr, pos); - buf = nbuf; - // Allocated a new buffer. No one else knows about it. - safeAppend = true; - } - } - void putchar(char c) @trusted - { - reserve(1); - buf.ptr[pos++] = c; - } - void putdchar(dchar dc) @trusted - { - import std.utf : encode, UseReplacementDchar; - - char[4] ubuf; - immutable size = encode!(UseReplacementDchar.yes)(ubuf, dc); - reserve(size); - foreach (c; ubuf) - buf.ptr[pos++] = c; - } - void putonly(const char[] b) @trusted - { - import core.stdc.string : memcpy; - assert(pos == 0); // assume this is the only put call - if (reserveWithoutAllocating(b.length)) - memcpy(buf.ptr + pos, b.ptr, b.length); - else - buf = b.dup; - pos = b.length; - } -} - -private struct LockedFile -{ - private @system _iobuf* fp; - - this(FILE* fps) @trusted - { - _FLOCK(fps); - // Since fps is now locked, we can cast away shared - fp = cast(_iobuf*) fps; - } - - @disable this(); - @disable this(this); - @disable void opAssign(LockedFile); - - // these use unlocked fgetc calls - @trusted fgetc() { return _FGETC(fp); } - @trusted fgetwc() { return _FGETWC(fp); } - - ~this() @trusted - { - _FUNLOCK(cast(FILE*) fp); - } -} - -@safe unittest -{ - void f() @safe - { - FILE* fps; - auto lf = LockedFile(fps); - static assert(!__traits(compiles, lf = LockedFile(fps))); - version (ShouldFail) - { - lf.fps = null; // error with -preview=systemVariables - } - } -} - -// Private implementation of readln -private size_t readlnImpl(FILE* fps, ref char[] buf, dchar terminator, File.Orientation orientation) @safe -{ - version (CRuntime_DigitalMars) - return () @trusted { - auto lf = LockedFile(fps); - ReadlnAppender app; - app.initialize(buf); - - if (__fhnd_info[lf.fp._file] & FHND_WCHAR) - { /* Stream is in wide characters. - * Read them and convert to chars. - */ - static assert(wchar_t.sizeof == 2); - for (int c = void; (c = lf.fgetwc()) != -1; ) - { - if ((c & ~0x7F) == 0) - { - app.putchar(cast(char) c); - if (c == terminator) - break; - } - else - { - if (c >= 0xD800 && c <= 0xDBFF) - { - int c2 = void; - if ((c2 = lf.fgetwc()) != -1 || - c2 < 0xDC00 && c2 > 0xDFFF) - { - StdioException("unpaired UTF-16 surrogate"); - } - c = ((c - 0xD7C0) << 10) + (c2 - 0xDC00); - } - app.putdchar(cast(dchar) c); - } - } - if (ferror(fps)) - StdioException(); - } - else if (lf.fp._flag & _IONBF) - { - /* Use this for unbuffered I/O, when running - * across buffer boundaries, or for any but the common - * cases. - */ - L1: - int c; - while ((c = lf.fgetc()) != -1) - { - app.putchar(cast(char) c); - if (c == terminator) - { - buf = app.data; - return buf.length; - } - - } - - if (ferror(fps)) - StdioException(); - } - else - { - int u = lf.fp._cnt; - char* p = lf.fp._ptr; - int i; - if (lf.fp._flag & _IOTRAN) - { /* Translated mode ignores \r and treats ^Z as end-of-file - */ - char c; - while (1) - { - if (i == u) // if end of buffer - goto L1; // give up - c = p[i]; - i++; - if (c != '\r') - { - if (c == terminator) - break; - if (c != 0x1A) - continue; - goto L1; - } - else - { if (i != u && p[i] == terminator) - break; - goto L1; - } - } - app.putonly(p[0 .. i]); - app.buf[i - 1] = cast(char) terminator; - if (terminator == '\n' && c == '\r') - i++; - } - else - { - while (1) - { - if (i == u) // if end of buffer - goto L1; // give up - auto c = p[i]; - i++; - if (c == terminator) - break; - } - app.putonly(p[0 .. i]); - } - lf.fp._cnt -= i; - lf.fp._ptr += i; - } - - buf = app.data; - return buf.length; - }(); - else version (CRuntime_Microsoft) - { - auto lf = LockedFile(fps); - - ReadlnAppender app; - app.initialize(buf); - - int c; - while ((c = lf.fgetc()) != -1) - { - app.putchar(cast(char) c); - if (c == terminator) - { - buf = app.data; - return buf.length; - } - - } - - if (ferror(fps)) - StdioException(); - buf = app.data; - return buf.length; - } - else static if (__traits(compiles, core.sys.posix.stdio.getdelim)) - { - if (orientation == File.Orientation.wide) - { - import core.stdc.wchar_ : fwide; - - auto lf = LockedFile(fps); - /* Stream is in wide characters. - * Read them and convert to chars. - */ - version (Windows) - { - buf.length = 0; - for (int c = void; (c = lf.fgetwc()) != -1; ) - { - if ((c & ~0x7F) == 0) - { buf ~= c; - if (c == terminator) - break; - } - else - { - if (c >= 0xD800 && c <= 0xDBFF) - { - int c2 = void; - if ((c2 = lf.fgetwc()) != -1 || - c2 < 0xDC00 && c2 > 0xDFFF) - { - StdioException("unpaired UTF-16 surrogate"); - } - c = ((c - 0xD7C0) << 10) + (c2 - 0xDC00); - } - import std.utf : encode; - encode(buf, c); - } - } - if (ferror(fps)) - StdioException(); - return buf.length; - } - else version (Posix) - { - buf.length = 0; - for (int c; (c = lf.fgetwc()) != -1; ) - { - import std.utf : encode; - - if ((c & ~0x7F) == 0) - buf ~= cast(char) c; - else - encode(buf, cast(dchar) c); - if (c == terminator) - break; - } - if (ferror(fps)) - StdioException(); - return buf.length; - } - else - { - static assert(0); - } - } - return () @trusted { - import core.stdc.stdlib : free; - - static char *lineptr = null; - static size_t n = 0; - scope(exit) - { - if (n > 128 * 1024) - { - // Bound memory used by readln - free(lineptr); - lineptr = null; - n = 0; - } - } - - const s = core.sys.posix.stdio.getdelim(&lineptr, &n, terminator, fps); - if (s < 0) - { - if (ferror(fps)) - StdioException(); - buf.length = 0; // end of file - return 0; - } - - const line = lineptr[0 .. s]; - if (s <= buf.length) - { - buf = buf[0 .. s]; - buf[] = line; - } - else - { - buf = line.dup; - } - return s; - }(); - } - else // version (NO_GETDELIM) - { - import core.stdc.wchar_ : fwide; - - auto lf = LockedFile(fps); - if (orientation == File.Orientation.wide) - { - /* Stream is in wide characters. - * Read them and convert to chars. - */ - version (Windows) - { - buf.length = 0; - for (int c; (c = lf.fgetwc()) != -1; ) - { - if ((c & ~0x7F) == 0) - { buf ~= c; - if (c == terminator) - break; - } - else - { - if (c >= 0xD800 && c <= 0xDBFF) - { - int c2 = void; - if ((c2 = lf.fgetwc()) != -1 || - c2 < 0xDC00 && c2 > 0xDFFF) - { - StdioException("unpaired UTF-16 surrogate"); - } - c = ((c - 0xD7C0) << 10) + (c2 - 0xDC00); - } - import std.utf : encode; - encode(buf, c); - } - } - if (ferror(fps)) - StdioException(); - return buf.length; - } - else version (Posix) - { - import std.utf : encode; - buf.length = 0; - for (int c; (c = lf.fgetwc()) != -1; ) - { - if ((c & ~0x7F) == 0) - buf ~= cast(char) c; - else - encode(buf, cast(dchar) c); - if (c == terminator) - break; - } - if (ferror(fps)) - StdioException(); - return buf.length; - } - else - { - static assert(0); - } - } - - // Narrow stream - // First, fill the existing buffer - for (size_t bufPos = 0; bufPos < buf.length; ) - { - immutable c = lf.fgetc(); - if (c == -1) - { - buf.length = bufPos; - goto endGame; - } - buf[bufPos++] = cast(char) c; - if (c == terminator) - { - // No need to test for errors in file - buf.length = bufPos; - return bufPos; - } - } - // Then, append to it - for (int c; (c = lf.fgetc()) != -1; ) - { - buf ~= cast(char) c; - if (c == terminator) - { - // No need to test for errors in file - return buf.length; - } - } - - endGame: - if (ferror(fps)) - StdioException(); - return buf.length; - } -} - -@system unittest -{ - static import std.file; - auto deleteme = testFilename(); - scope(exit) std.file.remove(deleteme); - - std.file.write(deleteme, "abcd\n0123456789abcde\n1234\n"); - File f = File(deleteme, "rb"); - - char[] ln = new char[2]; - f.readln(ln); - - assert(ln == "abcd\n"); - char[] t = ln[0 .. 2]; - t ~= 't'; - assert(t == "abt"); - // https://issues.dlang.org/show_bug.cgi?id=13856: ln stomped to "abtd" - assert(ln == "abcd\n"); - - // it can also stomp the array length - ln = new char[4]; - f.readln(ln); - assert(ln == "0123456789abcde\n"); - - char[100] buf; - ln = buf[]; - f.readln(ln); - assert(ln == "1234\n"); - assert(ln.ptr == buf.ptr); // avoid allocation, buffer is good enough -} - -/** Experimental network access via the File interface - - Opens a TCP connection to the given host and port, then returns - a File struct with read and write access through the same interface - as any other file (meaning writef and the byLine ranges work!). - - Authors: - Adam D. Ruppe - - Bugs: - Only works on Linux -*/ -version (linux) -{ - File openNetwork(string host, ushort port) - { - import core.stdc.string : memcpy; - import core.sys.posix.arpa.inet : htons; - import core.sys.posix.netdb : gethostbyname; - import core.sys.posix.netinet.in_ : sockaddr_in; - static import core.sys.posix.unistd; - static import sock = core.sys.posix.sys.socket; - import std.conv : to; - import std.exception : enforce; - import std.internal.cstring : tempCString; - - auto h = enforce( gethostbyname(host.tempCString()), - new StdioException("gethostbyname")); - - int s = sock.socket(sock.AF_INET, sock.SOCK_STREAM, 0); - enforce(s != -1, new StdioException("socket")); - - scope(failure) - { - // want to make sure it doesn't dangle if something throws. Upon - // normal exit, the File struct's reference counting takes care of - // closing, so we don't need to worry about success - core.sys.posix.unistd.close(s); - } - - sockaddr_in addr; - - addr.sin_family = sock.AF_INET; - addr.sin_port = htons(port); - memcpy(&addr.sin_addr.s_addr, h.h_addr, h.h_length); - - enforce(sock.connect(s, cast(sock.sockaddr*) &addr, addr.sizeof) != -1, - new StdioException("Connect failed")); - - File f; - f.fdopen(s, "w+", host ~ ":" ~ to!string(port)); - return f; - } -} - -version (StdUnittest) private string testFilename(string file = __FILE__, size_t line = __LINE__) @safe -{ - import std.conv : text; - import std.file : deleteme; - import std.path : baseName; - - // filename intentionally contains non-ASCII (Russian) characters for - // https://issues.dlang.org/show_bug.cgi?id=7648 - return text(deleteme, "-детка.", baseName(file), ".", line); -} diff --git a/phobos/std/string.d b/phobos/std/string.d deleted file mode 100644 index ca14c23..0000000 --- a/phobos/std/string.d +++ /dev/null @@ -1,7284 +0,0 @@ -// Written in the D programming language. - -/** -String handling functions. - -$(SCRIPT inhibitQuickIndex = 1;) - -$(DIVC quickindex, -$(BOOKTABLE , -$(TR $(TH Category) $(TH Functions) ) -$(TR $(TDNW Searching) - $(TD - $(MYREF column) - $(MYREF indexOf) - $(MYREF indexOfAny) - $(MYREF indexOfNeither) - $(MYREF lastIndexOf) - $(MYREF lastIndexOfAny) - $(MYREF lastIndexOfNeither) - ) -) -$(TR $(TDNW Comparison) - $(TD - $(MYREF isNumeric) - ) -) -$(TR $(TDNW Mutation) - $(TD - $(MYREF capitalize) - ) -) -$(TR $(TDNW Pruning and Filling) - $(TD - $(MYREF center) - $(MYREF chomp) - $(MYREF chompPrefix) - $(MYREF chop) - $(MYREF detabber) - $(MYREF detab) - $(MYREF entab) - $(MYREF entabber) - $(MYREF leftJustify) - $(MYREF outdent) - $(MYREF rightJustify) - $(MYREF strip) - $(MYREF stripLeft) - $(MYREF stripRight) - $(MYREF wrap) - ) -) -$(TR $(TDNW Substitution) - $(TD - $(MYREF abbrev) - $(MYREF soundex) - $(MYREF soundexer) - $(MYREF succ) - $(MYREF tr) - $(MYREF translate) - ) -) -$(TR $(TDNW Miscellaneous) - $(TD - $(MYREF assumeUTF) - $(MYREF fromStringz) - $(MYREF lineSplitter) - $(MYREF representation) - $(MYREF splitLines) - $(MYREF toStringz) - ) -))) - -Objects of types `string`, `wstring`, and `dstring` are value types -and cannot be mutated element-by-element. For using mutation during building -strings, use `char[]`, `wchar[]`, or `dchar[]`. The `xxxstring` -types are preferable because they don't exhibit undesired aliasing, thus -making code more robust. - -The following functions are publicly imported: - -$(BOOKTABLE , -$(TR $(TH Module) $(TH Functions) ) -$(LEADINGROW Publicly imported functions) - $(TR $(TD std.algorithm) - $(TD - $(REF_SHORT cmp, std,algorithm,comparison) - $(REF_SHORT count, std,algorithm,searching) - $(REF_SHORT endsWith, std,algorithm,searching) - $(REF_SHORT startsWith, std,algorithm,searching) - )) - $(TR $(TD std.array) - $(TD - $(REF_SHORT join, std,array) - $(REF_SHORT replace, std,array) - $(REF_SHORT replaceInPlace, std,array) - $(REF_SHORT split, std,array) - $(REF_SHORT empty, std,array) - )) - $(TR $(TD std.format) - $(TD - $(REF_SHORT format, std,format) - $(REF_SHORT sformat, std,format) - )) - $(TR $(TD std.uni) - $(TD - $(REF_SHORT icmp, std,uni) - $(REF_SHORT toLower, std,uni) - $(REF_SHORT toLowerInPlace, std,uni) - $(REF_SHORT toUpper, std,uni) - $(REF_SHORT toUpperInPlace, std,uni) - )) -) - -There is a rich set of functions for string handling defined in other modules. -Functions related to Unicode and ASCII are found in $(MREF std, uni) -and $(MREF std, ascii), respectively. Other functions that have a -wider generality than just strings can be found in $(MREF std, algorithm) -and $(MREF std, range). - -See_Also: - $(LIST - $(MREF std, algorithm) and - $(MREF std, range) - for generic range algorithms - , - $(MREF std, ascii) - for functions that work with ASCII strings - , - $(MREF std, uni) - for functions that work with unicode strings - ) - -Copyright: Copyright The D Language Foundation 2007-. - -License: $(HTTP boost.org/LICENSE_1_0.txt, Boost License 1.0). - -Authors: $(HTTP digitalmars.com, Walter Bright), - $(HTTP erdani.org, Andrei Alexandrescu), - $(HTTP jmdavisprog.com, Jonathan M Davis), - and David L. 'SpottedTiger' Davis - -Source: $(PHOBOSSRC std/string.d) - -*/ -module std.string; - -version (StdUnittest) -{ -private: - struct TestAliasedString - { - string get() @safe @nogc pure nothrow return scope { return _s; } - alias get this; - @disable this(this); - string _s; - } - - bool testAliasedString(alias func, Args...)(string s, Args args) - { - import std.algorithm.comparison : equal; - auto a = func(TestAliasedString(s), args); - auto b = func(s, args); - static if (is(typeof(equal(a, b)))) - { - // For ranges, compare contents instead of object identity. - return equal(a, b); - } - else - { - return a == b; - } - } -} - -public import std.format : format, sformat; -import std.typecons : Flag, Yes, No; -public import std.uni : icmp, toLower, toLowerInPlace, toUpper, toUpperInPlace; - -import std.meta : AliasSeq, staticIndexOf; -import std.range.primitives : back, ElementEncodingType, ElementType, front, - hasLength, hasSlicing, isBidirectionalRange, isForwardRange, isInfinite, - isInputRange, isOutputRange, isRandomAccessRange, popBack, popFront, put, - save; -import std.traits : isConvertibleToString, isNarrowString, isSomeChar, - isSomeString, StringTypeOf, Unqual; - -//public imports for backward compatibility -public import std.algorithm.comparison : cmp; -public import std.algorithm.searching : startsWith, endsWith, count; -public import std.array : join, replace, replaceInPlace, split, empty; - -/* ************* Exceptions *************** */ - -/++ - Exception thrown on errors in std.string functions. - +/ -class StringException : Exception -{ - import std.exception : basicExceptionCtors; - - /// - mixin basicExceptionCtors; -} - -/// -@safe pure unittest -{ - import std.exception : assertThrown; - auto bad = " a\n\tb\n c"; - assertThrown!StringException(bad.outdent); -} - -/++ - Params: - cString = A null-terminated c-style string. - - Returns: A D-style array of `char`, `wchar` or `dchar` referencing the same - string. The returned array will retain the same type qualifiers as the input. - - $(RED Important Note:) The returned array is a slice of the original buffer. - The original data is not changed and not copied. -+/ -inout(Char)[] fromStringz(Char)(return scope inout(Char)* cString) @nogc @system pure nothrow -if (isSomeChar!Char) -{ - import core.stdc.stddef : wchar_t; - - static if (is(immutable Char == immutable char)) - import core.stdc.string : cstrlen = strlen; - else static if (is(immutable Char == immutable wchar_t)) - import core.stdc.wchar_ : cstrlen = wcslen; - else - static size_t cstrlen(scope const Char* s) - { - const(Char)* p = s; - while (*p) - ++p; - return p - s; - } - - return cString ? cString[0 .. cstrlen(cString)] : null; -} - -/// ditto -inout(Char)[] fromStringz(Char)(return scope inout(Char)[] cString) @nogc @safe pure nothrow -if (isSomeChar!Char) -{ - foreach (i; 0 .. cString.length) - if (cString[i] == '\0') - return cString[0 .. i]; - - return cString; -} - -/// -@system pure unittest -{ - assert(fromStringz("foo\0"c.ptr) == "foo"c); - assert(fromStringz("foo\0"w.ptr) == "foo"w); - assert(fromStringz("foo\0"d.ptr) == "foo"d); - - assert(fromStringz("福\0"c.ptr) == "福"c); - assert(fromStringz("福\0"w.ptr) == "福"w); - assert(fromStringz("福\0"d.ptr) == "福"d); -} - -/// -@nogc @safe pure nothrow unittest -{ - struct C - { - char[32] name; - } - assert(C("foo\0"c).name.fromStringz() == "foo"c); - - struct W - { - wchar[32] name; - } - assert(W("foo\0"w).name.fromStringz() == "foo"w); - - struct D - { - dchar[32] name; - } - assert(D("foo\0"d).name.fromStringz() == "foo"d); -} - -@nogc @safe pure nothrow unittest -{ - assert( string.init.fromStringz() == ""c); - assert(wstring.init.fromStringz() == ""w); - assert(dstring.init.fromStringz() == ""d); - - immutable char[3] a = "foo"c; - assert(a.fromStringz() == "foo"c); - - immutable wchar[3] b = "foo"w; - assert(b.fromStringz() == "foo"w); - - immutable dchar[3] c = "foo"d; - assert(c.fromStringz() == "foo"d); -} - -@system pure unittest -{ - char* a = null; - assert(fromStringz(a) == null); - wchar* b = null; - assert(fromStringz(b) == null); - dchar* c = null; - assert(fromStringz(c) == null); - - const char* d = "foo\0"; - assert(fromStringz(d) == "foo"); - - immutable char* e = "foo\0"; - assert(fromStringz(e) == "foo"); - - const wchar* f = "foo\0"; - assert(fromStringz(f) == "foo"); - - immutable wchar* g = "foo\0"; - assert(fromStringz(g) == "foo"); - - const dchar* h = "foo\0"; - assert(fromStringz(h) == "foo"); - - immutable dchar* i = "foo\0"; - assert(fromStringz(i) == "foo"); - - immutable wchar z = 0x0000; - // Test some surrogate pairs - // high surrogates are in the range 0xD800 .. 0xDC00 - // low surrogates are in the range 0xDC00 .. 0xE000 - // since UTF16 doesn't specify endianness we test both. - foreach (wchar[] t; [[0xD800, 0xDC00], [0xD800, 0xE000], [0xDC00, 0xDC00], - [0xDC00, 0xE000], [0xDA00, 0xDE00]]) - { - immutable hi = t[0], lo = t[1]; - assert(fromStringz([hi, lo, z].ptr) == [hi, lo]); - assert(fromStringz([lo, hi, z].ptr) == [lo, hi]); - } -} - -/++ - Params: - s = A D-style string. - - Returns: A C-style null-terminated string equivalent to `s`. `s` - must not contain embedded `'\0'`'s as any C function will treat the - first `'\0'` that it sees as the end of the string. If `s.empty` is - `true`, then a string containing only `'\0'` is returned. - - $(RED Important Note:) When passing a `char*` to a C function, and the C - function keeps it around for any reason, make sure that you keep a - reference to it in your D code. Otherwise, it may become invalid during a - garbage collection cycle and cause a nasty bug when the C code tries to use - it. - +/ -immutable(char)* toStringz(scope const(char)[] s) @trusted pure nothrow -out (result) -{ - import core.stdc.string : strlen, memcmp; - if (result) - { - auto slen = s.length; - while (slen > 0 && s[slen-1] == 0) --slen; - assert(strlen(result) == slen, - "The result c string is shorter than the in input string"); - assert(result[0 .. slen] == s[0 .. slen], - "The input and result string are not equal"); - } -} -do -{ - import std.exception : assumeUnique; - - if (s.empty) return "".ptr; - - /+ Unfortunately, this isn't reliable. - We could make this work if string literals are put - in read-only memory and we test if s[] is pointing into - that. - - /* Peek past end of s[], if it's 0, no conversion necessary. - * Note that the compiler will put a 0 past the end of static - * strings, and the storage allocator will put a 0 past the end - * of newly allocated char[]'s. - */ - char* p = &s[0] + s.length; - if (*p == 0) - return s; - +/ - - // Need to make a copy - auto copy = new char[s.length + 1]; - copy[0 .. s.length] = s[]; - copy[s.length] = 0; - - return &assumeUnique(copy)[0]; -} - -/// -pure nothrow @system unittest -{ - import core.stdc.string : strlen; - import std.conv : to; - - auto p = toStringz("foo"); - assert(strlen(p) == 3); - const(char)[] foo = "abbzxyzzy"; - p = toStringz(foo[3 .. 5]); - assert(strlen(p) == 2); - - string test = ""; - p = toStringz(test); - assert(*p == 0); - - test = "\0"; - p = toStringz(test); - assert(*p == 0); - - test = "foo\0"; - p = toStringz(test); - assert(p[0] == 'f' && p[1] == 'o' && p[2] == 'o' && p[3] == 0); - - const string test2 = ""; - p = toStringz(test2); - assert(*p == 0); - - assert(toStringz([]) is toStringz("")); -} - -pure nothrow @system unittest // https://issues.dlang.org/show_bug.cgi?id=15136 -{ - static struct S - { - immutable char[5] str; - ubyte foo; - this(char[5] str) pure nothrow - { - this.str = str; - } - } - auto s = S("01234"); - const str = s.str.toStringz; - assert(str !is s.str.ptr); - assert(*(str + 5) == 0); // Null terminated. - s.foo = 42; - assert(*(str + 5) == 0); // Still null terminated. -} - - -/** - Flag indicating whether a search is case-sensitive. -*/ -alias CaseSensitive = Flag!"caseSensitive"; - -/++ - Searches for character in range. - - Params: - s = string or InputRange of characters to search in correct UTF format - c = character to search for - startIdx = starting index to a well-formed code point - cs = `Yes.caseSensitive` or `No.caseSensitive` - - Returns: - the index of the first occurrence of `c` in `s` with - respect to the start index `startIdx`. If `c` - is not found, then `-1` is returned. - If `c` is found the value of the returned index is at least - `startIdx`. - If the parameters are not valid UTF, the result will still - be in the range [-1 .. s.length], but will not be reliable otherwise. - - Throws: - If the sequence starting at `startIdx` does not represent a well - formed codepoint, then a $(REF UTFException, std,utf) may be thrown. - - See_Also: $(REF countUntil, std,algorithm,searching) - +/ -ptrdiff_t indexOf(Range)(Range s, dchar c, CaseSensitive cs = Yes.caseSensitive) -if (isInputRange!Range && isSomeChar!(ElementType!Range) && !isSomeString!Range) -{ - return _indexOf(s, c, cs); -} - -/// Ditto -ptrdiff_t indexOf(C)(scope const(C)[] s, dchar c, CaseSensitive cs = Yes.caseSensitive) -if (isSomeChar!C) -{ - return _indexOf(s, c, cs); -} - -/// Ditto -ptrdiff_t indexOf(Range)(Range s, dchar c, size_t startIdx, CaseSensitive cs = Yes.caseSensitive) -if (isInputRange!Range && isSomeChar!(ElementType!Range) && !isSomeString!Range) -{ - return _indexOf(s, c, startIdx, cs); -} - -/// Ditto -ptrdiff_t indexOf(C)(scope const(C)[] s, dchar c, size_t startIdx, CaseSensitive cs = Yes.caseSensitive) -if (isSomeChar!C) -{ - return _indexOf(s, c, startIdx, cs); -} - -/// -@safe pure unittest -{ - import std.typecons : No; - - string s = "Hello World"; - assert(indexOf(s, 'W') == 6); - assert(indexOf(s, 'Z') == -1); - assert(indexOf(s, 'w', No.caseSensitive) == 6); -} - -/// -@safe pure unittest -{ - import std.typecons : No; - - string s = "Hello World"; - assert(indexOf(s, 'W', 4) == 6); - assert(indexOf(s, 'Z', 100) == -1); - assert(indexOf(s, 'w', 3, No.caseSensitive) == 6); -} - -@safe pure unittest -{ - assert(testAliasedString!indexOf("std/string.d", '/')); - - enum S : string { a = "std/string.d" } - assert(S.a.indexOf('/') == 3); - - char[S.a.length] sa = S.a[]; - assert(sa.indexOf('/') == 3); -} - -@safe pure unittest -{ - import std.conv : to; - import std.exception : assertCTFEable; - import std.traits : EnumMembers; - import std.utf : byChar, byWchar, byDchar; - - assertCTFEable!( - { - static foreach (S; AliasSeq!(string, wstring, dstring)) - {{ - assert(indexOf(cast(S) null, cast(dchar)'a') == -1); - assert(indexOf(to!S("def"), cast(dchar)'a') == -1); - assert(indexOf(to!S("abba"), cast(dchar)'a') == 0); - assert(indexOf(to!S("def"), cast(dchar)'f') == 2); - - assert(indexOf(to!S("def"), cast(dchar)'a', No.caseSensitive) == -1); - assert(indexOf(to!S("def"), cast(dchar)'a', No.caseSensitive) == -1); - assert(indexOf(to!S("Abba"), cast(dchar)'a', No.caseSensitive) == 0); - assert(indexOf(to!S("def"), cast(dchar)'F', No.caseSensitive) == 2); - assert(indexOf(to!S("Ăśdef"), 'Ăś', No.caseSensitive) == 0); - - S sPlts = "Mars: the fourth Rock (Planet) from the Sun."; - assert(indexOf("def", cast(char)'f', No.caseSensitive) == 2); - assert(indexOf(sPlts, cast(char)'P', No.caseSensitive) == 23); - assert(indexOf(sPlts, cast(char)'R', No.caseSensitive) == 2); - }} - - foreach (cs; EnumMembers!CaseSensitive) - { - assert(indexOf("hello\U00010143\u0100\U00010143", '\u0100', cs) == 9); - assert(indexOf("hello\U00010143\u0100\U00010143"w, '\u0100', cs) == 7); - assert(indexOf("hello\U00010143\u0100\U00010143"d, '\u0100', cs) == 6); - - assert(indexOf("hello\U00010143\u0100\U00010143".byChar, '\u0100', cs) == 9); - assert(indexOf("hello\U00010143\u0100\U00010143".byWchar, '\u0100', cs) == 7); - assert(indexOf("hello\U00010143\u0100\U00010143".byDchar, '\u0100', cs) == 6); - - assert(indexOf("hello\U000007FF\u0100\U00010143".byChar, 'l', cs) == 2); - assert(indexOf("hello\U000007FF\u0100\U00010143".byChar, '\u0100', cs) == 7); - assert(indexOf("hello\U0000EFFF\u0100\U00010143".byChar, '\u0100', cs) == 8); - - assert(indexOf("hello\U00010100".byWchar, '\U00010100', cs) == 5); - assert(indexOf("hello\U00010100".byWchar, '\U00010101', cs) == -1); - } - - char[10] fixedSizeArray = "0123456789"; - assert(indexOf(fixedSizeArray, '2') == 2); - }); -} - -@safe pure unittest -{ - assert(testAliasedString!indexOf("std/string.d", '/', 0)); - assert(testAliasedString!indexOf("std/string.d", '/', 1)); - assert(testAliasedString!indexOf("std/string.d", '/', 4)); - - enum S : string { a = "std/string.d" } - assert(S.a.indexOf('/', 0) == 3); - assert(S.a.indexOf('/', 1) == 3); - assert(S.a.indexOf('/', 4) == -1); - - char[S.a.length] sa = S.a[]; - assert(sa.indexOf('/', 0) == 3); - assert(sa.indexOf('/', 1) == 3); - assert(sa.indexOf('/', 4) == -1); -} - -@safe pure unittest -{ - import std.conv : to; - import std.traits : EnumMembers; - import std.utf : byCodeUnit, byChar, byWchar; - - assert("hello".byCodeUnit.indexOf(cast(dchar)'l', 1) == 2); - assert("hello".byWchar.indexOf(cast(dchar)'l', 1) == 2); - assert("hello".byWchar.indexOf(cast(dchar)'l', 6) == -1); - - static foreach (S; AliasSeq!(string, wstring, dstring)) - {{ - assert(indexOf(cast(S) null, cast(dchar)'a', 1) == -1); - assert(indexOf(to!S("def"), cast(dchar)'a', 1) == -1); - assert(indexOf(to!S("abba"), cast(dchar)'a', 1) == 3); - assert(indexOf(to!S("def"), cast(dchar)'f', 1) == 2); - - assert((to!S("def")).indexOf(cast(dchar)'a', 1, - No.caseSensitive) == -1); - assert(indexOf(to!S("def"), cast(dchar)'a', 1, - No.caseSensitive) == -1); - assert(indexOf(to!S("def"), cast(dchar)'a', 12, - No.caseSensitive) == -1); - assert(indexOf(to!S("AbbA"), cast(dchar)'a', 2, - No.caseSensitive) == 3); - assert(indexOf(to!S("def"), cast(dchar)'F', 2, No.caseSensitive) == 2); - - S sPlts = "Mars: the fourth Rock (Planet) from the Sun."; - assert(indexOf("def", cast(char)'f', cast(uint) 2, - No.caseSensitive) == 2); - assert(indexOf(sPlts, cast(char)'P', 12, No.caseSensitive) == 23); - assert(indexOf(sPlts, cast(char)'R', cast(ulong) 1, - No.caseSensitive) == 2); - }} - - foreach (cs; EnumMembers!CaseSensitive) - { - assert(indexOf("hello\U00010143\u0100\U00010143", '\u0100', 2, cs) - == 9); - assert(indexOf("hello\U00010143\u0100\U00010143"w, '\u0100', 3, cs) - == 7); - assert(indexOf("hello\U00010143\u0100\U00010143"d, '\u0100', 6, cs) - == 6); - } -} - -private ptrdiff_t _indexOf(Range)(Range s, dchar c, CaseSensitive cs = Yes.caseSensitive) -if (isInputRange!Range && isSomeChar!(ElementType!Range)) -{ - static import std.ascii; - static import std.uni; - import std.utf : byDchar, byCodeUnit, UTFException, codeLength; - alias Char = Unqual!(ElementEncodingType!Range); - - if (cs == Yes.caseSensitive) - { - static if (Char.sizeof == 1 && isSomeString!Range) - { - if (std.ascii.isASCII(c) && !__ctfe) - { // Plain old ASCII - static ptrdiff_t trustedmemchr(Range s, char c) @trusted - { - import core.stdc.string : memchr; - const p = cast(const(Char)*)memchr(s.ptr, c, s.length); - return p ? p - s.ptr : -1; - } - - return trustedmemchr(s, cast(char) c); - } - } - - static if (Char.sizeof == 1) - { - if (c <= 0x7F) - { - ptrdiff_t i; - foreach (const c2; s) - { - if (c == c2) - return i; - ++i; - } - } - else - { - ptrdiff_t i; - foreach (const c2; s.byDchar()) - { - if (c == c2) - return i; - i += codeLength!Char(c2); - } - } - } - else static if (Char.sizeof == 2) - { - if (c <= 0xFFFF) - { - ptrdiff_t i; - foreach (const c2; s) - { - if (c == c2) - return i; - ++i; - } - } - else if (c <= 0x10FFFF) - { - // Encode UTF-16 surrogate pair - const wchar c1 = cast(wchar)((((c - 0x10000) >> 10) & 0x3FF) + 0xD800); - const wchar c2 = cast(wchar)(((c - 0x10000) & 0x3FF) + 0xDC00); - ptrdiff_t i; - for (auto r = s.byCodeUnit(); !r.empty; r.popFront()) - { - if (c1 == r.front) - { - r.popFront(); - if (r.empty) // invalid UTF - missing second of pair - break; - if (c2 == r.front) - return i; - ++i; - } - ++i; - } - } - } - else static if (Char.sizeof == 4) - { - ptrdiff_t i; - foreach (const c2; s) - { - if (c == c2) - return i; - ++i; - } - } - else - static assert(0); - return -1; - } - else - { - if (std.ascii.isASCII(c)) - { // Plain old ASCII - immutable c1 = cast(char) std.ascii.toLower(c); - - ptrdiff_t i; - foreach (const c2; s.byCodeUnit()) - { - if (c1 == std.ascii.toLower(c2)) - return i; - ++i; - } - } - else - { // c is a universal character - immutable c1 = std.uni.toLower(c); - - ptrdiff_t i; - foreach (const c2; s.byDchar()) - { - if (c1 == std.uni.toLower(c2)) - return i; - i += codeLength!Char(c2); - } - } - } - return -1; -} - -private ptrdiff_t _indexOf(Range)(Range s, dchar c, size_t startIdx, CaseSensitive cs = Yes.caseSensitive) -if (isInputRange!Range && isSomeChar!(ElementType!Range)) -{ - static if (isSomeString!(typeof(s)) || - (hasSlicing!(typeof(s)) && hasLength!(typeof(s)))) - { - if (startIdx < s.length) - { - ptrdiff_t foundIdx = indexOf(s[startIdx .. $], c, cs); - if (foundIdx != -1) - { - return foundIdx + cast(ptrdiff_t) startIdx; - } - } - } - else - { - foreach (i; 0 .. startIdx) - { - if (s.empty) - return -1; - s.popFront(); - } - ptrdiff_t foundIdx = indexOf(s, c, cs); - if (foundIdx != -1) - { - return foundIdx + cast(ptrdiff_t) startIdx; - } - } - return -1; -} - -private template _indexOfStr(CaseSensitive cs) -{ - private ptrdiff_t _indexOfStr(Range, Char)(Range s, const(Char)[] sub) - if (isForwardRange!Range && isSomeChar!(ElementEncodingType!Range) && - isSomeChar!Char) - { - alias Char1 = Unqual!(ElementEncodingType!Range); - - static if (isSomeString!Range) - { - static if (is(Char1 == Char) && cs == Yes.caseSensitive) - { - import std.algorithm.searching : countUntil; - return s.representation.countUntil(sub.representation); - } - else - { - import std.algorithm.searching : find; - - const(Char1)[] balance; - static if (cs == Yes.caseSensitive) - { - balance = find(s, sub); - } - else - { - balance = find! - ((a, b) => toLower(a) == toLower(b)) - (s, sub); - } - return () @trusted { return balance.empty ? -1 : balance.ptr - s.ptr; } (); - } - } - else - { - if (s.empty) - return -1; - if (sub.empty) - return 0; // degenerate case - - import std.utf : byDchar, codeLength; - auto subr = sub.byDchar; // decode sub[] by dchar's - dchar sub0 = subr.front; // cache first character of sub[] - subr.popFront(); - - // Special case for single character search - if (subr.empty) - return indexOf(s, sub0, cs); - - static if (cs == No.caseSensitive) - sub0 = toLower(sub0); - - /* Classic double nested loop search algorithm - */ - ptrdiff_t index = 0; // count code unit index into s - for (auto sbydchar = s.byDchar(); !sbydchar.empty; sbydchar.popFront()) - { - dchar c2 = sbydchar.front; - static if (cs == No.caseSensitive) - c2 = toLower(c2); - if (c2 == sub0) - { - auto s2 = sbydchar.save; // why s must be a forward range - foreach (c; subr.save) - { - s2.popFront(); - if (s2.empty) - return -1; - static if (cs == Yes.caseSensitive) - { - if (c != s2.front) - goto Lnext; - } - else - { - if (toLower(c) != toLower(s2.front)) - goto Lnext; - } - } - return index; - } - Lnext: - index += codeLength!Char1(c2); - } - return -1; - } - } -} - -/++ - Searches for substring in `s`. - - Params: - s = string or ForwardRange of characters to search in correct UTF format - sub = substring to search for - startIdx = the index into s to start searching from - cs = `Yes.caseSensitive` (default) or `No.caseSensitive` - - Returns: - the index of the first occurrence of `sub` in `s` with - respect to the start index `startIdx`. If `sub` is not found, - then `-1` is returned. - If the arguments are not valid UTF, the result will still - be in the range [-1 .. s.length], but will not be reliable otherwise. - If `sub` is found the value of the returned index is at least - `startIdx`. - - Throws: - If the sequence starting at `startIdx` does not represent a well - formed codepoint, then a $(REF UTFException, std,utf) may be thrown. - - Bugs: - Does not work with case insensitive strings where the mapping of - tolower and toupper is not 1:1. - +/ -ptrdiff_t indexOf(Range, Char)(Range s, const(Char)[] sub) -if (isForwardRange!Range && isSomeChar!(ElementEncodingType!Range) && - isSomeChar!Char) -{ - return _indexOfStr!(Yes.caseSensitive)(s, sub); -} - -/// Ditto -ptrdiff_t indexOf(Range, Char)(Range s, const(Char)[] sub, in CaseSensitive cs) -if (isForwardRange!Range && isSomeChar!(ElementEncodingType!Range) && - isSomeChar!Char) -{ - if (cs == Yes.caseSensitive) - return indexOf(s, sub); - else - return _indexOfStr!(No.caseSensitive)(s, sub); -} - -/// Ditto -ptrdiff_t indexOf(Char1, Char2)(const(Char1)[] s, const(Char2)[] sub, - in size_t startIdx) -@safe -if (isSomeChar!Char1 && isSomeChar!Char2) -{ - if (startIdx >= s.length) - return -1; - ptrdiff_t foundIdx = indexOf(s[startIdx .. $], sub); - if (foundIdx == -1) - return -1; - return foundIdx + cast(ptrdiff_t) startIdx; -} - -/// Ditto -ptrdiff_t indexOf(Char1, Char2)(const(Char1)[] s, const(Char2)[] sub, - in size_t startIdx, in CaseSensitive cs) -@safe -if (isSomeChar!Char1 && isSomeChar!Char2) -{ - if (startIdx >= s.length) - return -1; - ptrdiff_t foundIdx = indexOf(s[startIdx .. $], sub, cs); - if (foundIdx == -1) - return -1; - return foundIdx + cast(ptrdiff_t) startIdx; -} - -/// -@safe pure unittest -{ - import std.typecons : No; - - string s = "Hello World"; - assert(indexOf(s, "Wo", 4) == 6); - assert(indexOf(s, "Zo", 100) == -1); - assert(indexOf(s, "wo", 3, No.caseSensitive) == 6); -} - -/// -@safe pure unittest -{ - import std.typecons : No; - - string s = "Hello World"; - assert(indexOf(s, "Wo") == 6); - assert(indexOf(s, "Zo") == -1); - assert(indexOf(s, "wO", No.caseSensitive) == 6); -} - -@safe pure nothrow @nogc unittest -{ - string s = "Hello World"; - assert(indexOf(s, "Wo", 4) == 6); - assert(indexOf(s, "Zo", 100) == -1); - assert(indexOf(s, "Wo") == 6); - assert(indexOf(s, "Zo") == -1); -} - -ptrdiff_t indexOf(Range, Char)(auto ref Range s, const(Char)[] sub) -if (!(isForwardRange!Range && isSomeChar!(ElementEncodingType!Range) && - isSomeChar!Char) && - is(StringTypeOf!Range)) -{ - return indexOf!(StringTypeOf!Range)(s, sub); -} - -ptrdiff_t indexOf(Range, Char)(auto ref Range s, const(Char)[] sub, - in CaseSensitive cs) -if (!(isForwardRange!Range && isSomeChar!(ElementEncodingType!Range) && - isSomeChar!Char) && - is(StringTypeOf!Range)) -{ - return indexOf!(StringTypeOf!Range)(s, sub, cs); -} - -@safe pure nothrow @nogc unittest -{ - assert(testAliasedString!indexOf("std/string.d", "string")); -} - -@safe pure unittest -{ - import std.conv : to; - import std.exception : assertCTFEable; - import std.traits : EnumMembers; - - assertCTFEable!( - { - static foreach (S; AliasSeq!(string, wstring, dstring)) - { - static foreach (T; AliasSeq!(string, wstring, dstring)) - {{ - assert(indexOf(cast(S) null, to!T("a")) == -1); - assert(indexOf(to!S("def"), to!T("a")) == -1); - assert(indexOf(to!S("abba"), to!T("a")) == 0); - assert(indexOf(to!S("def"), to!T("f")) == 2); - assert(indexOf(to!S("dfefffg"), to!T("fff")) == 3); - assert(indexOf(to!S("dfeffgfff"), to!T("fff")) == 6); - - assert(indexOf(to!S("dfeffgfff"), to!T("a"), No.caseSensitive) == -1); - assert(indexOf(to!S("def"), to!T("a"), No.caseSensitive) == -1); - assert(indexOf(to!S("abba"), to!T("a"), No.caseSensitive) == 0); - assert(indexOf(to!S("def"), to!T("f"), No.caseSensitive) == 2); - assert(indexOf(to!S("dfefffg"), to!T("fff"), No.caseSensitive) == 3); - assert(indexOf(to!S("dfeffgfff"), to!T("fff"), No.caseSensitive) == 6); - - S sPlts = "Mars: the fourth Rock (Planet) from the Sun."; - S sMars = "Who\'s \'My Favorite Maritian?\'"; - - assert(indexOf(sMars, to!T("MY fAVe"), No.caseSensitive) == -1); - assert(indexOf(sMars, to!T("mY fAVOriTe"), No.caseSensitive) == 7); - assert(indexOf(sPlts, to!T("mArS:"), No.caseSensitive) == 0); - assert(indexOf(sPlts, to!T("rOcK"), No.caseSensitive) == 17); - assert(indexOf(sPlts, to!T("Un."), No.caseSensitive) == 41); - assert(indexOf(sPlts, to!T(sPlts), No.caseSensitive) == 0); - - assert(indexOf("\u0100", to!T("\u0100"), No.caseSensitive) == 0); - - // Thanks to Carlos Santander B. and zwang - assert(indexOf("sus mejores cortesanos. Se embarcaron en el puerto de Dubai y", - to!T("page-break-before"), No.caseSensitive) == -1); - }} - - foreach (cs; EnumMembers!CaseSensitive) - { - assert(indexOf("hello\U00010143\u0100\U00010143", to!S("\u0100"), cs) == 9); - assert(indexOf("hello\U00010143\u0100\U00010143"w, to!S("\u0100"), cs) == 7); - assert(indexOf("hello\U00010143\u0100\U00010143"d, to!S("\u0100"), cs) == 6); - } - } - }); -} - -@safe pure @nogc nothrow -unittest -{ - import std.traits : EnumMembers; - import std.utf : byWchar; - - foreach (cs; EnumMembers!CaseSensitive) - { - assert(indexOf("".byWchar, "", cs) == -1); - assert(indexOf("hello".byWchar, "", cs) == 0); - assert(indexOf("hello".byWchar, "l", cs) == 2); - assert(indexOf("heLLo".byWchar, "LL", cs) == 2); - assert(indexOf("hello".byWchar, "lox", cs) == -1); - assert(indexOf("hello".byWchar, "betty", cs) == -1); - assert(indexOf("hello\U00010143\u0100*\U00010143".byWchar, "\u0100*", cs) == 7); - } -} - -@safe pure unittest -{ - import std.conv : to; - import std.traits : EnumMembers; - - static foreach (S; AliasSeq!(string, wstring, dstring)) - { - static foreach (T; AliasSeq!(string, wstring, dstring)) - {{ - assert(indexOf(cast(S) null, to!T("a"), 1337) == -1); - assert(indexOf(to!S("def"), to!T("a"), 0) == -1); - assert(indexOf(to!S("abba"), to!T("a"), 2) == 3); - assert(indexOf(to!S("def"), to!T("f"), 1) == 2); - assert(indexOf(to!S("dfefffg"), to!T("fff"), 1) == 3); - assert(indexOf(to!S("dfeffgfff"), to!T("fff"), 5) == 6); - - assert(indexOf(to!S("dfeffgfff"), to!T("a"), 1, No.caseSensitive) == -1); - assert(indexOf(to!S("def"), to!T("a"), 2, No.caseSensitive) == -1); - assert(indexOf(to!S("abba"), to!T("a"), 3, No.caseSensitive) == 3); - assert(indexOf(to!S("def"), to!T("f"), 1, No.caseSensitive) == 2); - assert(indexOf(to!S("dfefffg"), to!T("fff"), 2, No.caseSensitive) == 3); - assert(indexOf(to!S("dfeffgfff"), to!T("fff"), 4, No.caseSensitive) == 6); - assert(indexOf(to!S("dfeffgfffÜä"), to!T("Üä"), 9, No.caseSensitive) == 9, - to!string(indexOf(to!S("dfeffgfffÜä"), to!T("Üä"), 9, No.caseSensitive)) - ~ " " ~ S.stringof ~ " " ~ T.stringof); - - S sPlts = "Mars: the fourth Rock (Planet) from the Sun."; - S sMars = "Who\'s \'My Favorite Maritian?\'"; - - assert(indexOf(sMars, to!T("MY fAVe"), 10, - No.caseSensitive) == -1); - assert(indexOf(sMars, to!T("mY fAVOriTe"), 4, No.caseSensitive) == 7); - assert(indexOf(sPlts, to!T("mArS:"), 0, No.caseSensitive) == 0); - assert(indexOf(sPlts, to!T("rOcK"), 12, No.caseSensitive) == 17); - assert(indexOf(sPlts, to!T("Un."), 32, No.caseSensitive) == 41); - assert(indexOf(sPlts, to!T(sPlts), 0, No.caseSensitive) == 0); - - assert(indexOf("\u0100", to!T("\u0100"), 0, No.caseSensitive) == 0); - - // Thanks to Carlos Santander B. and zwang - assert(indexOf("sus mejores cortesanos. Se embarcaron en el puerto de Dubai y", - to!T("page-break-before"), 10, No.caseSensitive) == -1); - - // In order for indexOf with and without index to be consistent - assert(indexOf(to!S(""), to!T("")) == indexOf(to!S(""), to!T(""), 0)); - }} - - foreach (cs; EnumMembers!CaseSensitive) - { - assert(indexOf("hello\U00010143\u0100\U00010143", to!S("\u0100"), - 3, cs) == 9); - assert(indexOf("hello\U00010143\u0100\U00010143"w, to!S("\u0100"), - 3, cs) == 7); - assert(indexOf("hello\U00010143\u0100\U00010143"d, to!S("\u0100"), - 3, cs) == 6); - } - } -} - -/++ - Params: - s = string to search - c = character to search for - startIdx = the index into s to start searching from - cs = `Yes.caseSensitive` or `No.caseSensitive` - - Returns: - The index of the last occurrence of `c` in `s`. If `c` is not - found, then `-1` is returned. The `startIdx` slices `s` in - the following way $(D s[0 .. startIdx]). `startIdx` represents a - codeunit index in `s`. - - Throws: - If the sequence ending at `startIdx` does not represent a well - formed codepoint, then a $(REF UTFException, std,utf) may be thrown. - - `cs` indicates whether the comparisons are case sensitive. - +/ -ptrdiff_t lastIndexOf(Char)(const(Char)[] s, in dchar c, - in CaseSensitive cs = Yes.caseSensitive) @safe pure -if (isSomeChar!Char) -{ - static import std.ascii, std.uni; - import std.utf : canSearchInCodeUnits; - if (cs == Yes.caseSensitive) - { - if (canSearchInCodeUnits!Char(c)) - { - foreach_reverse (i, it; s) - { - if (it == c) - { - return i; - } - } - } - else - { - foreach_reverse (i, dchar it; s) - { - if (it == c) - { - return i; - } - } - } - } - else - { - if (std.ascii.isASCII(c)) - { - immutable c1 = std.ascii.toLower(c); - - foreach_reverse (i, it; s) - { - immutable c2 = std.ascii.toLower(it); - if (c1 == c2) - { - return i; - } - } - } - else - { - immutable c1 = std.uni.toLower(c); - - foreach_reverse (i, dchar it; s) - { - immutable c2 = std.uni.toLower(it); - if (c1 == c2) - { - return i; - } - } - } - } - - return -1; -} - -/// Ditto -ptrdiff_t lastIndexOf(Char)(const(Char)[] s, in dchar c, in size_t startIdx, - in CaseSensitive cs = Yes.caseSensitive) @safe pure -if (isSomeChar!Char) -{ - if (startIdx <= s.length) - { - return lastIndexOf(s[0u .. startIdx], c, cs); - } - - return -1; -} - -/// -@safe pure unittest -{ - import std.typecons : No; - - string s = "Hello World"; - assert(lastIndexOf(s, 'l') == 9); - assert(lastIndexOf(s, 'Z') == -1); - assert(lastIndexOf(s, 'L', No.caseSensitive) == 9); -} - -/// -@safe pure unittest -{ - import std.typecons : No; - - string s = "Hello World"; - assert(lastIndexOf(s, 'l', 4) == 3); - assert(lastIndexOf(s, 'Z', 1337) == -1); - assert(lastIndexOf(s, 'L', 7, No.caseSensitive) == 3); -} - -@safe pure unittest -{ - import std.conv : to; - import std.exception : assertCTFEable; - import std.traits : EnumMembers; - - assertCTFEable!( - { - static foreach (S; AliasSeq!(string, wstring, dstring)) - {{ - assert(lastIndexOf(cast(S) null, 'a') == -1); - assert(lastIndexOf(to!S("def"), 'a') == -1); - assert(lastIndexOf(to!S("abba"), 'a') == 3); - assert(lastIndexOf(to!S("def"), 'f') == 2); - assert(lastIndexOf(to!S("Ăśdef"), 'Ăś') == 0); - - assert(lastIndexOf(cast(S) null, 'a', No.caseSensitive) == -1); - assert(lastIndexOf(to!S("def"), 'a', No.caseSensitive) == -1); - assert(lastIndexOf(to!S("AbbA"), 'a', No.caseSensitive) == 3); - assert(lastIndexOf(to!S("def"), 'F', No.caseSensitive) == 2); - assert(lastIndexOf(to!S("Ăśdef"), 'Ăś', No.caseSensitive) == 0); - assert(lastIndexOf(to!S("i\u0100def"), to!dchar("\u0100"), - No.caseSensitive) == 1); - - S sPlts = "Mars: the fourth Rock (Planet) from the Sun."; - - assert(lastIndexOf(to!S("def"), 'f', No.caseSensitive) == 2); - assert(lastIndexOf(sPlts, 'M', No.caseSensitive) == 34); - assert(lastIndexOf(sPlts, 'S', No.caseSensitive) == 40); - }} - - foreach (cs; EnumMembers!CaseSensitive) - { - assert(lastIndexOf("\U00010143\u0100\U00010143hello", '\u0100', cs) == 4); - assert(lastIndexOf("\U00010143\u0100\U00010143hello"w, '\u0100', cs) == 2); - assert(lastIndexOf("\U00010143\u0100\U00010143hello"d, '\u0100', cs) == 1); - } - }); -} - -@safe pure unittest -{ - import std.conv : to; - import std.traits : EnumMembers; - - static foreach (S; AliasSeq!(string, wstring, dstring)) - {{ - assert(lastIndexOf(cast(S) null, 'a') == -1); - assert(lastIndexOf(to!S("def"), 'a') == -1); - assert(lastIndexOf(to!S("abba"), 'a', 3) == 0); - assert(lastIndexOf(to!S("deff"), 'f', 3) == 2); - - assert(lastIndexOf(cast(S) null, 'a', No.caseSensitive) == -1); - assert(lastIndexOf(to!S("def"), 'a', No.caseSensitive) == -1); - assert(lastIndexOf(to!S("AbbAa"), 'a', to!ushort(4), No.caseSensitive) == 3, - to!string(lastIndexOf(to!S("AbbAa"), 'a', 4, No.caseSensitive))); - assert(lastIndexOf(to!S("def"), 'F', 3, No.caseSensitive) == 2); - - S sPlts = "Mars: the fourth Rock (Planet) from the Sun."; - - assert(lastIndexOf(to!S("def"), 'f', 4, No.caseSensitive) == -1); - assert(lastIndexOf(sPlts, 'M', sPlts.length -2, No.caseSensitive) == 34); - assert(lastIndexOf(sPlts, 'S', sPlts.length -2, No.caseSensitive) == 40); - }} - - foreach (cs; EnumMembers!CaseSensitive) - { - assert(lastIndexOf("\U00010143\u0100\U00010143hello", '\u0100', cs) == 4); - assert(lastIndexOf("\U00010143\u0100\U00010143hello"w, '\u0100', cs) == 2); - assert(lastIndexOf("\U00010143\u0100\U00010143hello"d, '\u0100', cs) == 1); - } -} - -/++ - Params: - s = string to search - sub = substring to search for - startIdx = the index into s to start searching from - cs = `Yes.caseSensitive` or `No.caseSensitive` - - Returns: - the index of the last occurrence of `sub` in `s`. If `sub` is - not found, then `-1` is returned. The `startIdx` slices `s` - in the following way $(D s[0 .. startIdx]). `startIdx` represents a - codeunit index in `s`. - - Throws: - If the sequence ending at `startIdx` does not represent a well - formed codepoint, then a $(REF UTFException, std,utf) may be thrown. - - `cs` indicates whether the comparisons are case sensitive. - +/ -ptrdiff_t lastIndexOf(Char1, Char2)(const(Char1)[] s, const(Char2)[] sub, - in CaseSensitive cs = Yes.caseSensitive) @safe pure -if (isSomeChar!Char1 && isSomeChar!Char2) -{ - import std.algorithm.searching : endsWith; - import std.conv : to; - import std.range.primitives : walkLength; - static import std.uni; - import std.utf : strideBack; - if (sub.empty) - return -1; - - if (walkLength(sub) == 1) - return lastIndexOf(s, sub.front, cs); - - if (cs == Yes.caseSensitive) - { - static if (is(immutable Char1 == immutable Char2)) - { - import core.stdc.string : memcmp; - - immutable c = sub[0]; - - for (ptrdiff_t i = s.length - sub.length; i >= 0; --i) - { - if (s[i] == c) - { - if (__ctfe) - { - if (s[i + 1 .. i + sub.length] == sub[1 .. $]) - return i; - } - else - { - auto trustedMemcmp(in void* s1, in void* s2, size_t n) @trusted - { - return memcmp(s1, s2, n); - } - if (trustedMemcmp(&s[i + 1], &sub[1], - (sub.length - 1) * Char1.sizeof) == 0) - return i; - } - } - } - } - else - { - for (size_t i = s.length; !s.empty;) - { - if (s.endsWith(sub)) - return cast(ptrdiff_t) i - to!(const(Char1)[])(sub).length; - - i -= strideBack(s, i); - s = s[0 .. i]; - } - } - } - else - { - for (size_t i = s.length; !s.empty;) - { - if (endsWith!((a, b) => std.uni.toLower(a) == std.uni.toLower(b)) - (s, sub)) - { - return cast(ptrdiff_t) i - to!(const(Char1)[])(sub).length; - } - - i -= strideBack(s, i); - s = s[0 .. i]; - } - } - - return -1; -} - -/// Ditto -ptrdiff_t lastIndexOf(Char1, Char2)(const(Char1)[] s, const(Char2)[] sub, - in size_t startIdx, in CaseSensitive cs = Yes.caseSensitive) @safe pure -if (isSomeChar!Char1 && isSomeChar!Char2) -{ - if (startIdx <= s.length) - { - return lastIndexOf(s[0u .. startIdx], sub, cs); - } - - return -1; -} - -/// -@safe pure unittest -{ - import std.typecons : No; - - string s = "Hello World"; - assert(lastIndexOf(s, "ll") == 2); - assert(lastIndexOf(s, "Zo") == -1); - assert(lastIndexOf(s, "lL", No.caseSensitive) == 2); -} - -/// -@safe pure unittest -{ - import std.typecons : No; - - string s = "Hello World"; - assert(lastIndexOf(s, "ll", 4) == 2); - assert(lastIndexOf(s, "Zo", 128) == -1); - assert(lastIndexOf(s, "lL", 3, No.caseSensitive) == -1); -} - -@safe pure unittest -{ - import std.conv : to; - - static foreach (S; AliasSeq!(string, wstring, dstring)) - {{ - auto r = to!S("").lastIndexOf("hello"); - assert(r == -1, to!string(r)); - - r = to!S("hello").lastIndexOf(""); - assert(r == -1, to!string(r)); - - r = to!S("").lastIndexOf(""); - assert(r == -1, to!string(r)); - }} -} - -@safe pure unittest -{ - import std.conv : to; - import std.exception : assertCTFEable; - import std.traits : EnumMembers; - - assertCTFEable!( - { - static foreach (S; AliasSeq!(string, wstring, dstring)) - { - static foreach (T; AliasSeq!(string, wstring, dstring)) - {{ - enum typeStr = S.stringof ~ " " ~ T.stringof; - - assert(lastIndexOf(cast(S) null, to!T("a")) == -1, typeStr); - assert(lastIndexOf(to!S("abcdefcdef"), to!T("c")) == 6, typeStr); - assert(lastIndexOf(to!S("abcdefcdef"), to!T("cd")) == 6, typeStr); - assert(lastIndexOf(to!S("abcdefcdef"), to!T("ef")) == 8, typeStr); - assert(lastIndexOf(to!S("abcdefCdef"), to!T("c")) == 2, typeStr); - assert(lastIndexOf(to!S("abcdefCdef"), to!T("cd")) == 2, typeStr); - assert(lastIndexOf(to!S("abcdefcdef"), to!T("x")) == -1, typeStr); - assert(lastIndexOf(to!S("abcdefcdef"), to!T("xy")) == -1, typeStr); - assert(lastIndexOf(to!S("abcdefcdef"), to!T("")) == -1, typeStr); - assert(lastIndexOf(to!S("Ăśabcdefcdef"), to!T("Ăś")) == 0, typeStr); - - assert(lastIndexOf(cast(S) null, to!T("a"), No.caseSensitive) == -1, typeStr); - assert(lastIndexOf(to!S("abcdefCdef"), to!T("c"), No.caseSensitive) == 6, typeStr); - assert(lastIndexOf(to!S("abcdefCdef"), to!T("cD"), No.caseSensitive) == 6, typeStr); - assert(lastIndexOf(to!S("abcdefcdef"), to!T("x"), No.caseSensitive) == -1, typeStr); - assert(lastIndexOf(to!S("abcdefcdef"), to!T("xy"), No.caseSensitive) == -1, typeStr); - assert(lastIndexOf(to!S("abcdefcdef"), to!T(""), No.caseSensitive) == -1, typeStr); - assert(lastIndexOf(to!S("Ăśabcdefcdef"), to!T("Ăś"), No.caseSensitive) == 0, typeStr); - - assert(lastIndexOf(to!S("abcdefcdef"), to!T("c"), No.caseSensitive) == 6, typeStr); - assert(lastIndexOf(to!S("abcdefcdef"), to!T("cd"), No.caseSensitive) == 6, typeStr); - assert(lastIndexOf(to!S("abcdefcdef"), to!T("def"), No.caseSensitive) == 7, typeStr); - - assert(lastIndexOf(to!S("Ăśdfeffgfff"), to!T("Ăś"), Yes.caseSensitive) == 0); - - S sPlts = "Mars: the fourth Rock (Planet) from the Sun."; - S sMars = "Who\'s \'My Favorite Maritian?\'"; - - assert(lastIndexOf(sMars, to!T("RiTE maR"), No.caseSensitive) == 14, typeStr); - assert(lastIndexOf(sPlts, to!T("FOuRTh"), No.caseSensitive) == 10, typeStr); - assert(lastIndexOf(sMars, to!T("whO\'s \'MY"), No.caseSensitive) == 0, typeStr); - assert(lastIndexOf(sMars, to!T(sMars), No.caseSensitive) == 0, typeStr); - }} - - foreach (cs; EnumMembers!CaseSensitive) - { - enum csString = to!string(cs); - - assert(lastIndexOf("\U00010143\u0100\U00010143hello", to!S("\u0100"), cs) == 4, csString); - assert(lastIndexOf("\U00010143\u0100\U00010143hello"w, to!S("\u0100"), cs) == 2, csString); - assert(lastIndexOf("\U00010143\u0100\U00010143hello"d, to!S("\u0100"), cs) == 1, csString); - } - } - }); -} - -// https://issues.dlang.org/show_bug.cgi?id=13529 -@safe pure unittest -{ - import std.conv : to; - static foreach (S; AliasSeq!(string, wstring, dstring)) - { - static foreach (T; AliasSeq!(string, wstring, dstring)) - {{ - enum typeStr = S.stringof ~ " " ~ T.stringof; - auto idx = lastIndexOf(to!T("HällĂś WĂśrldĂś Ăś"),to!S("Ăś Ăś")); - assert(idx != -1, to!string(idx) ~ " " ~ typeStr); - - idx = lastIndexOf(to!T("HällĂś WĂśrldĂś Ăś"),to!S("Ăś Ăśd")); - assert(idx == -1, to!string(idx) ~ " " ~ typeStr); - }} - } -} - -@safe pure unittest -{ - import std.conv : to; - import std.traits : EnumMembers; - - static foreach (S; AliasSeq!(string, wstring, dstring)) - { - static foreach (T; AliasSeq!(string, wstring, dstring)) - {{ - enum typeStr = S.stringof ~ " " ~ T.stringof; - - assert(lastIndexOf(cast(S) null, to!T("a")) == -1, typeStr); - assert(lastIndexOf(to!S("abcdefcdef"), to!T("c"), 5) == 2, typeStr); - assert(lastIndexOf(to!S("abcdefcdef"), to!T("cd"), 3) == -1, typeStr); - assert(lastIndexOf(to!S("abcdefcdef"), to!T("ef"), 6) == 4, typeStr ~ - format(" %u", lastIndexOf(to!S("abcdefcdef"), to!T("ef"), 6))); - assert(lastIndexOf(to!S("abcdefCdef"), to!T("c"), 5) == 2, typeStr); - assert(lastIndexOf(to!S("abcdefCdef"), to!T("cd"), 3) == -1, typeStr); - assert(lastIndexOf(to!S("abcdefcdefx"), to!T("x"), 1) == -1, typeStr); - assert(lastIndexOf(to!S("abcdefcdefxy"), to!T("xy"), 6) == -1, typeStr); - assert(lastIndexOf(to!S("abcdefcdef"), to!T(""), 8) == -1, typeStr); - assert(lastIndexOf(to!S("ĂśafĂś"), to!T("Ăś"), 3) == 0, typeStr ~ - to!string(lastIndexOf(to!S("ĂśafĂś"), to!T("Ăś"), 3))); //BUG 10472 - - assert(lastIndexOf(cast(S) null, to!T("a"), 1, No.caseSensitive) == -1, typeStr); - assert(lastIndexOf(to!S("abcdefCdef"), to!T("c"), 5, No.caseSensitive) == 2, typeStr); - assert(lastIndexOf(to!S("abcdefCdef"), to!T("cD"), 4, No.caseSensitive) == 2, typeStr ~ - " " ~ to!string(lastIndexOf(to!S("abcdefCdef"), to!T("cD"), 3, No.caseSensitive))); - assert(lastIndexOf(to!S("abcdefcdef"), to!T("x"),3 , No.caseSensitive) == -1, typeStr); - assert(lastIndexOf(to!S("abcdefcdefXY"), to!T("xy"), 4, No.caseSensitive) == -1, typeStr); - assert(lastIndexOf(to!S("abcdefcdef"), to!T(""), 7, No.caseSensitive) == -1, typeStr); - - assert(lastIndexOf(to!S("abcdefcdef"), to!T("c"), 4, No.caseSensitive) == 2, typeStr); - assert(lastIndexOf(to!S("abcdefcdef"), to!T("cd"), 4, No.caseSensitive) == 2, typeStr); - assert(lastIndexOf(to!S("abcdefcdef"), to!T("def"), 6, No.caseSensitive) == 3, typeStr); - assert(lastIndexOf(to!S(""), to!T(""), 0) == lastIndexOf(to!S(""), to!T("")), typeStr); - }} - - foreach (cs; EnumMembers!CaseSensitive) - { - enum csString = to!string(cs); - - assert(lastIndexOf("\U00010143\u0100\U00010143hello", to!S("\u0100"), 6, cs) == 4, csString); - assert(lastIndexOf("\U00010143\u0100\U00010143hello"w, to!S("\u0100"), 6, cs) == 2, csString); - assert(lastIndexOf("\U00010143\u0100\U00010143hello"d, to!S("\u0100"), 3, cs) == 1, csString); - } - } -} - -// https://issues.dlang.org/show_bug.cgi?id=20783 -@safe pure @nogc unittest -{ - enum lastIndex = "aa".lastIndexOf("ab"); - assert(lastIndex == -1); -} - -@safe pure @nogc unittest -{ - enum lastIndex = "hello hello hell h".lastIndexOf("hello"); - assert(lastIndex == 6); -} - -private ptrdiff_t indexOfAnyNeitherImpl(bool forward, bool any, Char, Char2)( - const(Char)[] haystack, const(Char2)[] needles, - in CaseSensitive cs = Yes.caseSensitive) @safe pure -if (isSomeChar!Char && isSomeChar!Char2) -{ - import std.algorithm.searching : canFind, findAmong; - if (cs == Yes.caseSensitive) - { - static if (forward) - { - static if (any) - { - size_t n = haystack.findAmong(needles).length; - return n ? haystack.length - n : -1; - } - else - { - foreach (idx, dchar hay; haystack) - { - if (!canFind(needles, hay)) - { - return idx; - } - } - } - } - else - { - static if (any) - { - import std.range : retro; - import std.utf : strideBack; - size_t n = haystack.retro.findAmong(needles).source.length; - if (n) - { - return n - haystack.strideBack(n); - } - } - else - { - foreach_reverse (idx, dchar hay; haystack) - { - if (!canFind(needles, hay)) - { - return idx; - } - } - } - } - } - else - { - import std.range.primitives : walkLength; - if (needles.length <= 16 && needles.walkLength(17)) - { - size_t si = 0; - dchar[16] scratch = void; - foreach ( dchar c; needles) - { - scratch[si++] = toLower(c); - } - - static if (forward) - { - foreach (i, dchar c; haystack) - { - if (canFind(scratch[0 .. si], toLower(c)) == any) - { - return i; - } - } - } - else - { - foreach_reverse (i, dchar c; haystack) - { - if (canFind(scratch[0 .. si], toLower(c)) == any) - { - return i; - } - } - } - } - else - { - static bool f(dchar a, dchar b) - { - return toLower(a) == b; - } - - static if (forward) - { - foreach (i, dchar c; haystack) - { - if (canFind!f(needles, toLower(c)) == any) - { - return i; - } - } - } - else - { - foreach_reverse (i, dchar c; haystack) - { - if (canFind!f(needles, toLower(c)) == any) - { - return i; - } - } - } - } - } - - return -1; -} - -/** - Returns the index of the first occurrence of any of the elements in $(D - needles) in `haystack`. If no element of `needles` is found, - then `-1` is returned. The `startIdx` slices `haystack` in the - following way $(D haystack[startIdx .. $]). `startIdx` represents a - codeunit index in `haystack`. If the sequence ending at `startIdx` - does not represent a well formed codepoint, then a $(REF UTFException, std,utf) - may be thrown. - - Params: - haystack = String to search for needles in. - needles = Strings to search for in haystack. - startIdx = slices haystack like this $(D haystack[startIdx .. $]). If - the startIdx is greater than or equal to the length of haystack the - functions returns `-1`. - cs = Indicates whether the comparisons are case sensitive. -*/ -ptrdiff_t indexOfAny(Char,Char2)(const(Char)[] haystack, const(Char2)[] needles, - in CaseSensitive cs = Yes.caseSensitive) @safe pure -if (isSomeChar!Char && isSomeChar!Char2) -{ - return indexOfAnyNeitherImpl!(true, true)(haystack, needles, cs); -} - -/// Ditto -ptrdiff_t indexOfAny(Char,Char2)(const(Char)[] haystack, const(Char2)[] needles, - in size_t startIdx, in CaseSensitive cs = Yes.caseSensitive) @safe pure -if (isSomeChar!Char && isSomeChar!Char2) -{ - if (startIdx < haystack.length) - { - ptrdiff_t foundIdx = indexOfAny(haystack[startIdx .. $], needles, cs); - if (foundIdx != -1) - { - return foundIdx + cast(ptrdiff_t) startIdx; - } - } - - return -1; -} - -/// -@safe pure unittest -{ - import std.conv : to; - - ptrdiff_t i = "helloWorld".indexOfAny("Wr"); - assert(i == 5); - i = "Üällo world".indexOfAny("lo "); - assert(i == 4, to!string(i)); -} - -/// -@safe pure unittest -{ - import std.conv : to; - - ptrdiff_t i = "helloWorld".indexOfAny("Wr", 4); - assert(i == 5); - - i = "Foo Üällo world".indexOfAny("lh", 3); - assert(i == 8, to!string(i)); -} - -@safe pure unittest -{ - import std.conv : to; - - static foreach (S; AliasSeq!(string, wstring, dstring)) - {{ - auto r = to!S("").indexOfAny("hello"); - assert(r == -1, to!string(r)); - - r = to!S("hello").indexOfAny(""); - assert(r == -1, to!string(r)); - - r = to!S("").indexOfAny(""); - assert(r == -1, to!string(r)); - }} -} - -@safe pure unittest -{ - import std.conv : to; - import std.exception : assertCTFEable; - - assertCTFEable!( - { - static foreach (S; AliasSeq!(string, wstring, dstring)) - { - static foreach (T; AliasSeq!(string, wstring, dstring)) - { - assert(indexOfAny(cast(S) null, to!T("a")) == -1); - assert(indexOfAny(to!S("def"), to!T("rsa")) == -1); - assert(indexOfAny(to!S("abba"), to!T("a")) == 0); - assert(indexOfAny(to!S("def"), to!T("f")) == 2); - assert(indexOfAny(to!S("dfefffg"), to!T("fgh")) == 1); - assert(indexOfAny(to!S("dfeffgfff"), to!T("feg")) == 1); - - assert(indexOfAny(to!S("zfeffgfff"), to!T("ACDC"), - No.caseSensitive) == -1); - assert(indexOfAny(to!S("def"), to!T("MI6"), - No.caseSensitive) == -1); - assert(indexOfAny(to!S("abba"), to!T("DEA"), - No.caseSensitive) == 0); - assert(indexOfAny(to!S("def"), to!T("FBI"), No.caseSensitive) == 2); - assert(indexOfAny(to!S("dfefffg"), to!T("NSA"), No.caseSensitive) - == -1); - assert(indexOfAny(to!S("dfeffgfff"), to!T("BND"), - No.caseSensitive) == 0); - assert(indexOfAny(to!S("dfeffgfff"), to!T("BNDabCHIJKQEPÖÖSYXÄ??ß"), - No.caseSensitive) == 0); - - assert(indexOfAny("\u0100", to!T("\u0100"), No.caseSensitive) == 0); - } - } - } - ); -} - -@safe pure unittest -{ - import std.conv : to; - import std.traits : EnumMembers; - - static foreach (S; AliasSeq!(string, wstring, dstring)) - { - static foreach (T; AliasSeq!(string, wstring, dstring)) - { - assert(indexOfAny(cast(S) null, to!T("a"), 1337) == -1); - assert(indexOfAny(to!S("def"), to!T("AaF"), 0) == -1); - assert(indexOfAny(to!S("abba"), to!T("NSa"), 2) == 3); - assert(indexOfAny(to!S("def"), to!T("fbi"), 1) == 2); - assert(indexOfAny(to!S("dfefffg"), to!T("foo"), 2) == 3); - assert(indexOfAny(to!S("dfeffgfff"), to!T("fsb"), 5) == 6); - - assert(indexOfAny(to!S("dfeffgfff"), to!T("NDS"), 1, - No.caseSensitive) == -1); - assert(indexOfAny(to!S("def"), to!T("DRS"), 2, - No.caseSensitive) == -1); - assert(indexOfAny(to!S("abba"), to!T("SI"), 3, - No.caseSensitive) == -1); - assert(indexOfAny(to!S("deO"), to!T("ASIO"), 1, - No.caseSensitive) == 2); - assert(indexOfAny(to!S("dfefffg"), to!T("fbh"), 2, - No.caseSensitive) == 3); - assert(indexOfAny(to!S("dfeffgfff"), to!T("fEe"), 4, - No.caseSensitive) == 4); - assert(indexOfAny(to!S("dfeffgfffÜä"), to!T("fÜä"), 9, - No.caseSensitive) == 9); - - assert(indexOfAny("\u0100", to!T("\u0100"), 0, - No.caseSensitive) == 0); - } - - foreach (cs; EnumMembers!CaseSensitive) - { - assert(indexOfAny("hello\U00010143\u0100\U00010143", - to!S("e\u0100"), 3, cs) == 9); - assert(indexOfAny("hello\U00010143\u0100\U00010143"w, - to!S("h\u0100"), 3, cs) == 7); - assert(indexOfAny("hello\U00010143\u0100\U00010143"d, - to!S("l\u0100"), 5, cs) == 6); - } - } -} - -/** - Returns the index of the last occurrence of any of the elements in $(D - needles) in `haystack`. If no element of `needles` is found, - then `-1` is returned. The `stopIdx` slices `haystack` in the - following way $(D s[0 .. stopIdx]). `stopIdx` represents a codeunit - index in `haystack`. If the sequence ending at `startIdx` does not - represent a well formed codepoint, then a $(REF UTFException, std,utf) may be - thrown. - - Params: - haystack = String to search for needles in. - needles = Strings to search for in haystack. - stopIdx = slices haystack like this $(D haystack[0 .. stopIdx]). If - the stopIdx is greater than or equal to the length of haystack the - functions returns `-1`. - cs = Indicates whether the comparisons are case sensitive. -*/ -ptrdiff_t lastIndexOfAny(Char,Char2)(const(Char)[] haystack, - const(Char2)[] needles, in CaseSensitive cs = Yes.caseSensitive) - @safe pure -if (isSomeChar!Char && isSomeChar!Char2) -{ - return indexOfAnyNeitherImpl!(false, true)(haystack, needles, cs); -} - -/// Ditto -ptrdiff_t lastIndexOfAny(Char,Char2)(const(Char)[] haystack, - const(Char2)[] needles, in size_t stopIdx, - in CaseSensitive cs = Yes.caseSensitive) @safe pure -if (isSomeChar!Char && isSomeChar!Char2) -{ - if (stopIdx <= haystack.length) - { - return lastIndexOfAny(haystack[0u .. stopIdx], needles, cs); - } - - return -1; -} - -/// -@safe pure unittest -{ - ptrdiff_t i = "helloWorld".lastIndexOfAny("Wlo"); - assert(i == 8); - - i = "Foo ÜäÜllo world".lastIndexOfAny("ĂśF"); - assert(i == 8); -} - -/// -@safe pure unittest -{ - import std.conv : to; - - ptrdiff_t i = "helloWorld".lastIndexOfAny("Wlo", 4); - assert(i == 3); - - i = "Foo ÜäÜllo world".lastIndexOfAny("ĂśF", 3); - assert(i == 0); -} - -@safe pure unittest -{ - import std.conv : to; - - static foreach (S; AliasSeq!(string, wstring, dstring)) - {{ - auto r = to!S("").lastIndexOfAny("hello"); - assert(r == -1, to!string(r)); - - r = to!S("hello").lastIndexOfAny(""); - assert(r == -1, to!string(r)); - - r = to!S("").lastIndexOfAny(""); - assert(r == -1, to!string(r)); - }} -} - -@safe pure unittest -{ - import std.conv : to; - import std.exception : assertCTFEable; - - assertCTFEable!( - { - static foreach (S; AliasSeq!(string, wstring, dstring)) - { - static foreach (T; AliasSeq!(string, wstring, dstring)) - {{ - assert(lastIndexOfAny(cast(S) null, to!T("a")) == -1); - assert(lastIndexOfAny(to!S("def"), to!T("rsa")) == -1); - assert(lastIndexOfAny(to!S("abba"), to!T("a")) == 3); - assert(lastIndexOfAny(to!S("def"), to!T("f")) == 2); - assert(lastIndexOfAny(to!S("dfefffg"), to!T("fgh")) == 6); - - ptrdiff_t oeIdx = 9; - if (is(S == wstring) || is(S == dstring)) - { - oeIdx = 8; - } - - auto foundOeIdx = lastIndexOfAny(to!S("dfeffgfĂśf"), to!T("feg")); - assert(foundOeIdx == oeIdx, to!string(foundOeIdx)); - - assert(lastIndexOfAny(to!S("zfeffgfff"), to!T("ACDC"), - No.caseSensitive) == -1); - assert(lastIndexOfAny(to!S("def"), to!T("MI6"), - No.caseSensitive) == -1); - assert(lastIndexOfAny(to!S("abba"), to!T("DEA"), - No.caseSensitive) == 3); - assert(lastIndexOfAny(to!S("def"), to!T("FBI"), - No.caseSensitive) == 2); - assert(lastIndexOfAny(to!S("dfefffg"), to!T("NSA"), - No.caseSensitive) == -1); - - oeIdx = 2; - if (is(S == wstring) || is(S == dstring)) - { - oeIdx = 1; - } - assert(lastIndexOfAny(to!S("Ăśdfeffgfff"), to!T("BND"), - No.caseSensitive) == oeIdx); - - assert(lastIndexOfAny("\u0100", to!T("\u0100"), - No.caseSensitive) == 0); - }} - } - } - ); -} - -@safe pure unittest -{ - import std.conv : to; - import std.exception : assertCTFEable; - - assertCTFEable!( - { - static foreach (S; AliasSeq!(string, wstring, dstring)) - { - static foreach (T; AliasSeq!(string, wstring, dstring)) - {{ - enum typeStr = S.stringof ~ " " ~ T.stringof; - - assert(lastIndexOfAny(cast(S) null, to!T("a"), 1337) == -1, - typeStr); - assert(lastIndexOfAny(to!S("abcdefcdef"), to!T("c"), 7) == 6, - typeStr); - assert(lastIndexOfAny(to!S("abcdefcdef"), to!T("cd"), 5) == 3, - typeStr); - assert(lastIndexOfAny(to!S("abcdefcdef"), to!T("ef"), 6) == 5, - typeStr); - assert(lastIndexOfAny(to!S("abcdefCdef"), to!T("c"), 8) == 2, - typeStr); - assert(lastIndexOfAny(to!S("abcdefcdef"), to!T("x"), 7) == -1, - typeStr); - assert(lastIndexOfAny(to!S("abcdefcdef"), to!T("xy"), 4) == -1, - typeStr); - assert(lastIndexOfAny(to!S("Ăśabcdefcdef"), to!T("Ăś"), 2) == 0, - typeStr); - - assert(lastIndexOfAny(cast(S) null, to!T("a"), 1337, - No.caseSensitive) == -1, typeStr); - assert(lastIndexOfAny(to!S("abcdefcdef"), to!T("C"), 7, - No.caseSensitive) == 6, typeStr); - assert(lastIndexOfAny(to!S("ABCDEFCDEF"), to!T("cd"), 5, - No.caseSensitive) == 3, typeStr); - assert(lastIndexOfAny(to!S("abcdefcdef"), to!T("EF"), 6, - No.caseSensitive) == 5, typeStr); - assert(lastIndexOfAny(to!S("ABCDEFcDEF"), to!T("C"), 8, - No.caseSensitive) == 6, typeStr); - assert(lastIndexOfAny(to!S("ABCDEFCDEF"), to!T("x"), 7, - No.caseSensitive) == -1, typeStr); - assert(lastIndexOfAny(to!S("abCdefcdef"), to!T("XY"), 4, - No.caseSensitive) == -1, typeStr); - assert(lastIndexOfAny(to!S("ÖABCDEFCDEF"), to!T("Ăś"), 2, - No.caseSensitive) == 0, typeStr); - }} - } - } - ); -} - -/** - Returns the index of the first occurrence of any character not an elements - in `needles` in `haystack`. If all element of `haystack` are - element of `needles` `-1` is returned. - - Params: - haystack = String to search for needles in. - needles = Strings to search for in haystack. - startIdx = slices haystack like this $(D haystack[startIdx .. $]). If - the startIdx is greater than or equal to the length of haystack the - functions returns `-1`. - cs = Indicates whether the comparisons are case sensitive. -*/ -ptrdiff_t indexOfNeither(Char,Char2)(const(Char)[] haystack, - const(Char2)[] needles, in CaseSensitive cs = Yes.caseSensitive) - @safe pure -if (isSomeChar!Char && isSomeChar!Char2) -{ - return indexOfAnyNeitherImpl!(true, false)(haystack, needles, cs); -} - -/// Ditto -ptrdiff_t indexOfNeither(Char,Char2)(const(Char)[] haystack, - const(Char2)[] needles, in size_t startIdx, - in CaseSensitive cs = Yes.caseSensitive) - @safe pure -if (isSomeChar!Char && isSomeChar!Char2) -{ - if (startIdx < haystack.length) - { - ptrdiff_t foundIdx = indexOfAnyNeitherImpl!(true, false)( - haystack[startIdx .. $], needles, cs); - if (foundIdx != -1) - { - return foundIdx + cast(ptrdiff_t) startIdx; - } - } - return -1; -} - -/// -@safe pure unittest -{ - assert(indexOfNeither("abba", "a", 2) == 2); - assert(indexOfNeither("def", "de", 1) == 2); - assert(indexOfNeither("dfefffg", "dfe", 4) == 6); -} - -/// -@safe pure unittest -{ - assert(indexOfNeither("def", "a") == 0); - assert(indexOfNeither("def", "de") == 2); - assert(indexOfNeither("dfefffg", "dfe") == 6); -} - -@safe pure unittest -{ - import std.conv : to; - - static foreach (S; AliasSeq!(string, wstring, dstring)) - {{ - auto r = to!S("").indexOfNeither("hello"); - assert(r == -1, to!string(r)); - - r = to!S("hello").indexOfNeither(""); - assert(r == 0, to!string(r)); - - r = to!S("").indexOfNeither(""); - assert(r == -1, to!string(r)); - }} -} - -@safe pure unittest -{ - import std.conv : to; - import std.exception : assertCTFEable; - - assertCTFEable!( - { - static foreach (S; AliasSeq!(string, wstring, dstring)) - { - static foreach (T; AliasSeq!(string, wstring, dstring)) - { - assert(indexOfNeither(cast(S) null, to!T("a")) == -1); - assert(indexOfNeither("abba", "a") == 1); - - assert(indexOfNeither(to!S("dfeffgfff"), to!T("a"), - No.caseSensitive) == 0); - assert(indexOfNeither(to!S("def"), to!T("D"), - No.caseSensitive) == 1); - assert(indexOfNeither(to!S("ABca"), to!T("a"), - No.caseSensitive) == 1); - assert(indexOfNeither(to!S("def"), to!T("f"), - No.caseSensitive) == 0); - assert(indexOfNeither(to!S("DfEfffg"), to!T("dFe"), - No.caseSensitive) == 6); - if (is(S == string)) - { - assert(indexOfNeither(to!S("äDfEfffg"), to!T("ädFe"), - No.caseSensitive) == 8, - to!string(indexOfNeither(to!S("äDfEfffg"), to!T("ädFe"), - No.caseSensitive))); - } - else - { - assert(indexOfNeither(to!S("äDfEfffg"), to!T("ädFe"), - No.caseSensitive) == 7, - to!string(indexOfNeither(to!S("äDfEfffg"), to!T("ädFe"), - No.caseSensitive))); - } - } - } - } - ); -} - -@safe pure unittest -{ - import std.conv : to; - import std.exception : assertCTFEable; - - assertCTFEable!( - { - static foreach (S; AliasSeq!(string, wstring, dstring)) - { - static foreach (T; AliasSeq!(string, wstring, dstring)) - { - assert(indexOfNeither(cast(S) null, to!T("a"), 1) == -1); - assert(indexOfNeither(to!S("def"), to!T("a"), 1) == 1, - to!string(indexOfNeither(to!S("def"), to!T("a"), 1))); - - assert(indexOfNeither(to!S("dfeffgfff"), to!T("a"), 4, - No.caseSensitive) == 4); - assert(indexOfNeither(to!S("def"), to!T("D"), 2, - No.caseSensitive) == 2); - assert(indexOfNeither(to!S("ABca"), to!T("a"), 3, - No.caseSensitive) == -1); - assert(indexOfNeither(to!S("def"), to!T("tzf"), 2, - No.caseSensitive) == -1); - assert(indexOfNeither(to!S("DfEfffg"), to!T("dFe"), 5, - No.caseSensitive) == 6); - if (is(S == string)) - { - assert(indexOfNeither(to!S("ĂśDfEfffg"), to!T("äDi"), 2, - No.caseSensitive) == 3, to!string(indexOfNeither( - to!S("ĂśDfEfffg"), to!T("äDi"), 2, No.caseSensitive))); - } - else - { - assert(indexOfNeither(to!S("ĂśDfEfffg"), to!T("äDi"), 2, - No.caseSensitive) == 2, to!string(indexOfNeither( - to!S("ĂśDfEfffg"), to!T("äDi"), 2, No.caseSensitive))); - } - } - } - } - ); -} - -/** - Returns the last index of the first occurence of any character that is not - an elements in `needles` in `haystack`. If all element of - `haystack` are element of `needles` `-1` is returned. - - Params: - haystack = String to search for needles in. - needles = Strings to search for in haystack. - stopIdx = slices haystack like this $(D haystack[0 .. stopIdx]) If - the stopIdx is greater than or equal to the length of haystack the - functions returns `-1`. - cs = Indicates whether the comparisons are case sensitive. -*/ -ptrdiff_t lastIndexOfNeither(Char,Char2)(const(Char)[] haystack, - const(Char2)[] needles, in CaseSensitive cs = Yes.caseSensitive) - @safe pure -if (isSomeChar!Char && isSomeChar!Char2) -{ - return indexOfAnyNeitherImpl!(false, false)(haystack, needles, cs); -} - -/// Ditto -ptrdiff_t lastIndexOfNeither(Char,Char2)(const(Char)[] haystack, - const(Char2)[] needles, in size_t stopIdx, - in CaseSensitive cs = Yes.caseSensitive) - @safe pure -if (isSomeChar!Char && isSomeChar!Char2) -{ - if (stopIdx < haystack.length) - { - return indexOfAnyNeitherImpl!(false, false)(haystack[0 .. stopIdx], - needles, cs); - } - return -1; -} - -/// -@safe pure unittest -{ - assert(lastIndexOfNeither("abba", "a") == 2); - assert(lastIndexOfNeither("def", "f") == 1); -} - -/// -@safe pure unittest -{ - assert(lastIndexOfNeither("def", "rsa", 3) == -1); - assert(lastIndexOfNeither("abba", "a", 2) == 1); -} - -@safe pure unittest -{ - import std.conv : to; - - static foreach (S; AliasSeq!(string, wstring, dstring)) - {{ - auto r = to!S("").lastIndexOfNeither("hello"); - assert(r == -1, to!string(r)); - - r = to!S("hello").lastIndexOfNeither(""); - assert(r == 4, to!string(r)); - - r = to!S("").lastIndexOfNeither(""); - assert(r == -1, to!string(r)); - }} -} - -@safe pure unittest -{ - import std.conv : to; - import std.exception : assertCTFEable; - - assertCTFEable!( - { - static foreach (S; AliasSeq!(string, wstring, dstring)) - { - static foreach (T; AliasSeq!(string, wstring, dstring)) - {{ - assert(lastIndexOfNeither(cast(S) null, to!T("a")) == -1); - assert(lastIndexOfNeither(to!S("def"), to!T("rsa")) == 2); - assert(lastIndexOfNeither(to!S("dfefffg"), to!T("fgh")) == 2); - - ptrdiff_t oeIdx = 8; - if (is(S == string)) - { - oeIdx = 9; - } - - auto foundOeIdx = lastIndexOfNeither(to!S("Ăśdfefegff"), to!T("zeg")); - assert(foundOeIdx == oeIdx, to!string(foundOeIdx)); - - assert(lastIndexOfNeither(to!S("zfeffgfsb"), to!T("FSB"), - No.caseSensitive) == 5); - assert(lastIndexOfNeither(to!S("def"), to!T("MI6"), - No.caseSensitive) == 2, to!string(lastIndexOfNeither(to!S("def"), - to!T("MI6"), No.caseSensitive))); - assert(lastIndexOfNeither(to!S("abbadeafsb"), to!T("fSb"), - No.caseSensitive) == 6, to!string(lastIndexOfNeither( - to!S("abbadeafsb"), to!T("fSb"), No.caseSensitive))); - assert(lastIndexOfNeither(to!S("defbi"), to!T("FBI"), - No.caseSensitive) == 1); - assert(lastIndexOfNeither(to!S("dfefffg"), to!T("NSA"), - No.caseSensitive) == 6); - assert(lastIndexOfNeither(to!S("dfeffgfffĂś"), to!T("BNDabCHIJKQEPÖÖSYXÄ??ß"), - No.caseSensitive) == 8, to!string(lastIndexOfNeither(to!S("dfeffgfffĂś"), - to!T("BNDabCHIJKQEPÖÖSYXÄ??ß"), No.caseSensitive))); - }} - } - } - ); -} - -@safe pure unittest -{ - import std.conv : to; - import std.exception : assertCTFEable; - - assertCTFEable!( - { - static foreach (S; AliasSeq!(string, wstring, dstring)) - { - static foreach (T; AliasSeq!(string, wstring, dstring)) - {{ - assert(lastIndexOfNeither(cast(S) null, to!T("a"), 1337) == -1); - assert(lastIndexOfNeither(to!S("def"), to!T("f")) == 1); - assert(lastIndexOfNeither(to!S("dfefffg"), to!T("fgh")) == 2); - - ptrdiff_t oeIdx = 4; - if (is(S == string)) - { - oeIdx = 5; - } - - auto foundOeIdx = lastIndexOfNeither(to!S("Ăśdfefegff"), to!T("zeg"), - 7); - assert(foundOeIdx == oeIdx, to!string(foundOeIdx)); - - assert(lastIndexOfNeither(to!S("zfeffgfsb"), to!T("FSB"), 6, - No.caseSensitive) == 5); - assert(lastIndexOfNeither(to!S("def"), to!T("MI6"), 2, - No.caseSensitive) == 1, to!string(lastIndexOfNeither(to!S("def"), - to!T("MI6"), 2, No.caseSensitive))); - assert(lastIndexOfNeither(to!S("abbadeafsb"), to!T("fSb"), 6, - No.caseSensitive) == 5, to!string(lastIndexOfNeither( - to!S("abbadeafsb"), to!T("fSb"), 6, No.caseSensitive))); - assert(lastIndexOfNeither(to!S("defbi"), to!T("FBI"), 3, - No.caseSensitive) == 1); - assert(lastIndexOfNeither(to!S("dfefffg"), to!T("NSA"), 2, - No.caseSensitive) == 1, to!string(lastIndexOfNeither( - to!S("dfefffg"), to!T("NSA"), 2, No.caseSensitive))); - }} - } - } - ); -} - -/** - * Returns the _representation of a string, which has the same type - * as the string except the character type is replaced by `ubyte`, - * `ushort`, or `uint` depending on the character width. - * - * Params: - * s = The string to return the _representation of. - * - * Returns: - * The _representation of the passed string. - */ -auto representation(Char)(Char[] s) @safe pure nothrow @nogc -if (isSomeChar!Char) -{ - import std.traits : ModifyTypePreservingTQ; - alias ToRepType(T) = AliasSeq!(ubyte, ushort, uint)[T.sizeof / 2]; - return cast(ModifyTypePreservingTQ!(ToRepType, Char)[])s; -} - -/// -@safe pure unittest -{ - string s = "hello"; - static assert(is(typeof(representation(s)) == immutable(ubyte)[])); - assert(representation(s) is cast(immutable(ubyte)[]) s); - assert(representation(s) == [0x68, 0x65, 0x6c, 0x6c, 0x6f]); -} - -@system pure unittest -{ - import std.exception : assertCTFEable; - import std.traits : Fields; - import std.typecons : Tuple; - - assertCTFEable!( - { - void test(Char, T)(Char[] str) - { - static assert(is(typeof(representation(str)) == T[])); - assert(representation(str) is cast(T[]) str); - } - - static foreach (Type; AliasSeq!(Tuple!(char , ubyte ), - Tuple!(wchar, ushort), - Tuple!(dchar, uint ))) - {{ - alias Char = Fields!Type[0]; - alias Int = Fields!Type[1]; - enum immutable(Char)[] hello = "hello"; - - test!( immutable Char, immutable Int)(hello); - test!( const Char, const Int)(hello); - test!( Char, Int)(hello.dup); - test!( shared Char, shared Int)(cast(shared) hello.dup); - test!(const shared Char, const shared Int)(hello); - }} - }); -} - - -/** - * Capitalize the first character of `s` and convert the rest of `s` to - * lowercase. - * - * Params: - * input = The string to _capitalize. - * - * Returns: - * The capitalized string. - * - * See_Also: - * $(REF asCapitalized, std,uni) for a lazy range version that doesn't allocate memory - */ -S capitalize(S)(S input) @trusted pure -if (isSomeString!S) -{ - import std.array : array; - import std.uni : asCapitalized; - import std.utf : byUTF; - - return input.asCapitalized.byUTF!(ElementEncodingType!(S)).array; -} - -/// -pure @safe unittest -{ - assert(capitalize("hello") == "Hello"); - assert(capitalize("World") == "World"); -} - -auto capitalize(S)(auto ref S s) -if (!isSomeString!S && is(StringTypeOf!S)) -{ - return capitalize!(StringTypeOf!S)(s); -} - -@safe pure unittest -{ - assert(testAliasedString!capitalize("hello")); -} - -@safe pure unittest -{ - import std.algorithm.comparison : cmp; - import std.conv : to; - import std.exception : assertCTFEable; - - assertCTFEable!( - { - static foreach (S; AliasSeq!(string, wstring, dstring, char[], wchar[], dchar[])) - {{ - S s1 = to!S("FoL"); - S s2; - - s2 = capitalize(s1); - assert(cmp(s2, "Fol") == 0); - assert(s2 !is s1); - - s2 = capitalize(s1[0 .. 2]); - assert(cmp(s2, "Fo") == 0); - - s1 = to!S("fOl"); - s2 = capitalize(s1); - assert(cmp(s2, "Fol") == 0); - assert(s2 !is s1); - s1 = to!S("\u0131 \u0130"); - s2 = capitalize(s1); - assert(cmp(s2, "\u0049 i\u0307") == 0); - assert(s2 !is s1); - - s1 = to!S("\u017F \u0049"); - s2 = capitalize(s1); - assert(cmp(s2, "\u0053 \u0069") == 0); - assert(s2 !is s1); - }} - }); -} - -/++ - Split `s` into an array of lines according to the unicode standard using - `'\r'`, `'\n'`, `"\r\n"`, $(REF lineSep, std,uni), - $(REF paraSep, std,uni), `U+0085` (NEL), `'\v'` and `'\f'` - as delimiters. If `keepTerm` is set to `KeepTerminator.yes`, then the - delimiter is included in the strings returned. - - Does not throw on invalid UTF; such is simply passed unchanged - to the output. - - Allocates memory; use $(LREF lineSplitter) for an alternative that - does not. - - Adheres to $(HTTP www.unicode.org/versions/Unicode7.0.0/ch05.pdf, Unicode 7.0). - - Params: - s = a string of `chars`, `wchars`, or `dchars`, or any custom - type that casts to a `string` type - keepTerm = whether delimiter is included or not in the results - Returns: - array of strings, each element is a line that is a slice of `s` - See_Also: - $(LREF lineSplitter) - $(REF splitter, std,algorithm) - $(REF splitter, std,regex) - +/ -alias KeepTerminator = Flag!"keepTerminator"; - -/// ditto -C[][] splitLines(C)(C[] s, KeepTerminator keepTerm = No.keepTerminator) @safe pure -if (isSomeChar!C) -{ - import std.array : appender; - import std.uni : lineSep, paraSep; - - size_t iStart = 0; - auto retval = appender!(C[][])(); - - for (size_t i; i < s.length; ++i) - { - switch (s[i]) - { - case '\v', '\f', '\n': - retval.put(s[iStart .. i + (keepTerm == Yes.keepTerminator)]); - iStart = i + 1; - break; - - case '\r': - if (i + 1 < s.length && s[i + 1] == '\n') - { - retval.put(s[iStart .. i + (keepTerm == Yes.keepTerminator) * 2]); - iStart = i + 2; - ++i; - } - else - { - goto case '\n'; - } - break; - - static if (s[i].sizeof == 1) - { - /* Manually decode: - * lineSep is E2 80 A8 - * paraSep is E2 80 A9 - */ - case 0xE2: - if (i + 2 < s.length && - s[i + 1] == 0x80 && - (s[i + 2] == 0xA8 || s[i + 2] == 0xA9) - ) - { - retval.put(s[iStart .. i + (keepTerm == Yes.keepTerminator) * 3]); - iStart = i + 3; - i += 2; - } - else - goto default; - break; - /* Manually decode: - * NEL is C2 85 - */ - case 0xC2: - if (i + 1 < s.length && s[i + 1] == 0x85) - { - retval.put(s[iStart .. i + (keepTerm == Yes.keepTerminator) * 2]); - iStart = i + 2; - i += 1; - } - else - goto default; - break; - } - else - { - case lineSep: - case paraSep: - case '\u0085': - goto case '\n'; - } - - default: - break; - } - } - - if (iStart != s.length) - retval.put(s[iStart .. $]); - - return retval.data; -} - -/// -@safe pure nothrow unittest -{ - string s = "Hello\nmy\rname\nis"; - assert(splitLines(s) == ["Hello", "my", "name", "is"]); -} - -@safe pure nothrow unittest -{ - string s = "a\xC2\x86b"; - assert(splitLines(s) == [s]); -} - -@safe pure nothrow unittest -{ - assert(testAliasedString!splitLines("hello\nworld")); - - enum S : string { a = "hello\nworld" } - assert(S.a.splitLines() == ["hello", "world"]); -} - -@system pure nothrow unittest -{ - // dip1000 cannot express an array of scope arrays, so this is not @safe - char[11] sa = "hello\nworld"; - assert(sa.splitLines() == ["hello", "world"]); -} - -@safe pure unittest -{ - import std.conv : to; - import std.exception : assertCTFEable; - - assertCTFEable!( - { - static foreach (S; AliasSeq!(char[], wchar[], dchar[], string, wstring, dstring)) - {{ - auto s = to!S( - "\rpeter\n\rpaul\r\njerry\u2028ice\u2029cream\n\nsunday\n" ~ - "mon\u2030day\nschadenfreude\vkindergarten\f\vcookies\u0085" - ); - auto lines = splitLines(s); - assert(lines.length == 14); - assert(lines[0] == ""); - assert(lines[1] == "peter"); - assert(lines[2] == ""); - assert(lines[3] == "paul"); - assert(lines[4] == "jerry"); - assert(lines[5] == "ice"); - assert(lines[6] == "cream"); - assert(lines[7] == ""); - assert(lines[8] == "sunday"); - assert(lines[9] == "mon\u2030day"); - assert(lines[10] == "schadenfreude"); - assert(lines[11] == "kindergarten"); - assert(lines[12] == ""); - assert(lines[13] == "cookies"); - - - ubyte[] u = ['a', 0xFF, 0x12, 'b']; // invalid UTF - auto ulines = splitLines(cast(char[]) u); - assert(cast(ubyte[])(ulines[0]) == u); - - lines = splitLines(s, Yes.keepTerminator); - assert(lines.length == 14); - assert(lines[0] == "\r"); - assert(lines[1] == "peter\n"); - assert(lines[2] == "\r"); - assert(lines[3] == "paul\r\n"); - assert(lines[4] == "jerry\u2028"); - assert(lines[5] == "ice\u2029"); - assert(lines[6] == "cream\n"); - assert(lines[7] == "\n"); - assert(lines[8] == "sunday\n"); - assert(lines[9] == "mon\u2030day\n"); - assert(lines[10] == "schadenfreude\v"); - assert(lines[11] == "kindergarten\f"); - assert(lines[12] == "\v"); - assert(lines[13] == "cookies\u0085"); - - s.popBack(); // Lop-off trailing \n - lines = splitLines(s); - assert(lines.length == 14); - assert(lines[9] == "mon\u2030day"); - - lines = splitLines(s, Yes.keepTerminator); - assert(lines.length == 14); - assert(lines[13] == "cookies"); - }} - }); -} - -private struct LineSplitter(KeepTerminator keepTerm = No.keepTerminator, Range) -{ - import std.conv : unsigned; - import std.uni : lineSep, paraSep; -private: - Range _input; - - alias IndexType = typeof(unsigned(_input.length)); - enum IndexType _unComputed = IndexType.max; - IndexType iStart = _unComputed; - IndexType iEnd = 0; - IndexType iNext = 0; - -public: - this(Range input) - { - _input = input; - } - - static if (isInfinite!Range) - { - enum bool empty = false; - } - else - { - @property bool empty() - { - return iStart == _unComputed && iNext == _input.length; - } - } - - @property typeof(_input) front() - { - if (iStart == _unComputed) - { - iStart = iNext; - Loop: - for (IndexType i = iNext; ; ++i) - { - if (i == _input.length) - { - iEnd = i; - iNext = i; - break Loop; - } - switch (_input[i]) - { - case '\v', '\f', '\n': - iEnd = i + (keepTerm == Yes.keepTerminator); - iNext = i + 1; - break Loop; - - case '\r': - if (i + 1 < _input.length && _input[i + 1] == '\n') - { - iEnd = i + (keepTerm == Yes.keepTerminator) * 2; - iNext = i + 2; - break Loop; - } - else - { - goto case '\n'; - } - - static if (_input[i].sizeof == 1) - { - /* Manually decode: - * lineSep is E2 80 A8 - * paraSep is E2 80 A9 - */ - case 0xE2: - if (i + 2 < _input.length && - _input[i + 1] == 0x80 && - (_input[i + 2] == 0xA8 || _input[i + 2] == 0xA9) - ) - { - iEnd = i + (keepTerm == Yes.keepTerminator) * 3; - iNext = i + 3; - break Loop; - } - else - goto default; - /* Manually decode: - * NEL is C2 85 - */ - case 0xC2: - if (i + 1 < _input.length && _input[i + 1] == 0x85) - { - iEnd = i + (keepTerm == Yes.keepTerminator) * 2; - iNext = i + 2; - break Loop; - } - else - goto default; - } - else - { - case '\u0085': - case lineSep: - case paraSep: - goto case '\n'; - } - - default: - break; - } - } - } - return _input[iStart .. iEnd]; - } - - void popFront() - { - if (iStart == _unComputed) - { - assert(!empty, "Can not popFront an empty range"); - front; - } - iStart = _unComputed; - } - - static if (isForwardRange!Range) - { - @property typeof(this) save() - { - auto ret = this; - ret._input = _input.save; - return ret; - } - } -} - -/*********************************** - * Split an array or slicable range of characters into a range of lines - using `'\r'`, `'\n'`, `'\v'`, `'\f'`, `"\r\n"`, - $(REF lineSep, std,uni), $(REF paraSep, std,uni) and `'\u0085'` (NEL) - as delimiters. If `keepTerm` is set to `Yes.keepTerminator`, then the - delimiter is included in the slices returned. - - Does not throw on invalid UTF; such is simply passed unchanged - to the output. - - Adheres to $(HTTP www.unicode.org/versions/Unicode7.0.0/ch05.pdf, Unicode 7.0). - - Does not allocate memory. - - Params: - r = array of `chars`, `wchars`, or `dchars` or a slicable range - keepTerm = whether delimiter is included or not in the results - Returns: - range of slices of the input range `r` - - See_Also: - $(LREF splitLines) - $(REF splitter, std,algorithm) - $(REF splitter, std,regex) - */ -auto lineSplitter(KeepTerminator keepTerm = No.keepTerminator, Range)(Range r) -if (hasSlicing!Range && hasLength!Range && isSomeChar!(ElementType!Range) && !isSomeString!Range) -{ - return LineSplitter!(keepTerm, Range)(r); -} - -/// Ditto -auto lineSplitter(KeepTerminator keepTerm = No.keepTerminator, C)(C[] r) -if (isSomeChar!C) -{ - return LineSplitter!(keepTerm, C[])(r); -} - -/// -@safe pure unittest -{ - import std.array : array; - - string s = "Hello\nmy\rname\nis"; - - /* notice the call to 'array' to turn the lazy range created by - lineSplitter comparable to the string[] created by splitLines. - */ - assert(lineSplitter(s).array == splitLines(s)); -} - -@safe pure unittest -{ - import std.array : array; - import std.conv : to; - import std.exception : assertCTFEable; - - assertCTFEable!( - { - static foreach (S; AliasSeq!(char[], wchar[], dchar[], string, wstring, dstring)) - {{ - auto s = to!S( - "\rpeter\n\rpaul\r\njerry\u2028ice\u2029cream\n\n" ~ - "sunday\nmon\u2030day\nschadenfreude\vkindergarten\f\vcookies\u0085" - ); - - auto lines = lineSplitter(s).array; - assert(lines.length == 14); - assert(lines[0] == ""); - assert(lines[1] == "peter"); - assert(lines[2] == ""); - assert(lines[3] == "paul"); - assert(lines[4] == "jerry"); - assert(lines[5] == "ice"); - assert(lines[6] == "cream"); - assert(lines[7] == ""); - assert(lines[8] == "sunday"); - assert(lines[9] == "mon\u2030day"); - assert(lines[10] == "schadenfreude"); - assert(lines[11] == "kindergarten"); - assert(lines[12] == ""); - assert(lines[13] == "cookies"); - - - ubyte[] u = ['a', 0xFF, 0x12, 'b']; // invalid UTF - auto ulines = lineSplitter(cast(char[]) u).array; - assert(cast(ubyte[])(ulines[0]) == u); - - lines = lineSplitter!(Yes.keepTerminator)(s).array; - assert(lines.length == 14); - assert(lines[0] == "\r"); - assert(lines[1] == "peter\n"); - assert(lines[2] == "\r"); - assert(lines[3] == "paul\r\n"); - assert(lines[4] == "jerry\u2028"); - assert(lines[5] == "ice\u2029"); - assert(lines[6] == "cream\n"); - assert(lines[7] == "\n"); - assert(lines[8] == "sunday\n"); - assert(lines[9] == "mon\u2030day\n"); - assert(lines[10] == "schadenfreude\v"); - assert(lines[11] == "kindergarten\f"); - assert(lines[12] == "\v"); - assert(lines[13] == "cookies\u0085"); - - s.popBack(); // Lop-off trailing \n - lines = lineSplitter(s).array; - assert(lines.length == 14); - assert(lines[9] == "mon\u2030day"); - - lines = lineSplitter!(Yes.keepTerminator)(s).array; - assert(lines.length == 14); - assert(lines[13] == "cookies"); - }} - }); -} - -/// -@nogc @safe pure unittest -{ - auto s = "\rpeter\n\rpaul\r\njerry\u2028ice\u2029cream\n\nsunday\nmon\u2030day\n"; - auto lines = s.lineSplitter(); - static immutable witness = ["", "peter", "", "paul", "jerry", "ice", "cream", "", "sunday", "mon\u2030day"]; - uint i; - foreach (line; lines) - { - assert(line == witness[i++]); - } - assert(i == witness.length); -} - -@nogc @safe pure unittest -{ - import std.algorithm.comparison : equal; - import std.range : only; - - auto s = "std/string.d"; - auto as = TestAliasedString(s); - assert(equal(s.lineSplitter(), as.lineSplitter())); - - enum S : string { a = "hello\nworld" } - assert(equal(S.a.lineSplitter(), only("hello", "world"))); - - char[S.a.length] sa = S.a[]; - assert(equal(sa.lineSplitter(), only("hello", "world"))); -} - -@safe pure unittest -{ - auto s = "line1\nline2"; - auto spl0 = s.lineSplitter!(Yes.keepTerminator); - auto spl1 = spl0.save; - spl0.popFront; - assert(spl1.front ~ spl0.front == s); - string r = "a\xC2\x86b"; - assert(r.lineSplitter.front == r); -} - -/++ - Strips leading whitespace (as defined by $(REF isWhite, std,uni)) or - as specified in the second argument. - - Params: - input = string or $(REF_ALTTEXT forward range, isForwardRange, std,range,primitives) - of characters - chars = string of characters to be stripped - - Returns: `input` stripped of leading whitespace or characters - specified in the second argument. - - Postconditions: `input` and the returned value - will share the same tail (see $(REF sameTail, std,array)). - - See_Also: - Generic stripping on ranges: $(REF _stripLeft, std, algorithm, mutation) - +/ -auto stripLeft(Range)(Range input) -if (isForwardRange!Range && isSomeChar!(ElementEncodingType!Range) && - !isInfinite!Range && !isConvertibleToString!Range) -{ - import std.traits : isDynamicArray; - static import std.ascii; - static import std.uni; - - static if (is(immutable ElementEncodingType!Range == immutable dchar) - || is(immutable ElementEncodingType!Range == immutable wchar)) - { - // Decoding is never needed for dchar. It happens not to be needed - // here for wchar because no whitepace is outside the basic - // multilingual plane meaning every whitespace character is encoded - // with a single wchar and due to the design of UTF-16 those wchars - // will not occur as part of the encoding of multi-wchar codepoints. - static if (isDynamicArray!Range) - { - foreach (i; 0 .. input.length) - { - if (!std.uni.isWhite(input[i])) - return input[i .. $]; - } - return input[$ .. $]; - } - else - { - while (!input.empty) - { - if (!std.uni.isWhite(input.front)) - break; - input.popFront(); - } - return input; - } - } - else - { - static if (isDynamicArray!Range) - { - // ASCII optimization for dynamic arrays. - size_t i = 0; - for (const size_t end = input.length; i < end; ++i) - { - auto c = input[i]; - if (c >= 0x80) goto NonAsciiPath; - if (!std.ascii.isWhite(c)) break; - } - input = input[i .. $]; - return input; - - NonAsciiPath: - input = input[i .. $]; - // Fall through to standard case. - } - - import std.utf : decode, decodeFront, UseReplacementDchar; - - static if (isNarrowString!Range) - { - for (size_t index = 0; index < input.length;) - { - const saveIndex = index; - if (!std.uni.isWhite(decode!(UseReplacementDchar.yes)(input, index))) - return input[saveIndex .. $]; - } - return input[$ .. $]; - } - else - { - while (!input.empty) - { - auto c = input.front; - if (std.ascii.isASCII(c)) - { - if (!std.ascii.isWhite(c)) - break; - input.popFront(); - } - else - { - auto save = input.save; - auto dc = decodeFront!(UseReplacementDchar.yes)(input); - if (!std.uni.isWhite(dc)) - return save; - } - } - return input; - } - } -} - -/// -nothrow @safe pure unittest -{ - import std.uni : lineSep, paraSep; - assert(stripLeft(" hello world ") == - "hello world "); - assert(stripLeft("\n\t\v\rhello world\n\t\v\r") == - "hello world\n\t\v\r"); - assert(stripLeft(" \u2028hello world") == - "hello world"); - assert(stripLeft("hello world") == - "hello world"); - assert(stripLeft([lineSep] ~ "hello world" ~ lineSep) == - "hello world" ~ [lineSep]); - assert(stripLeft([paraSep] ~ "hello world" ~ paraSep) == - "hello world" ~ [paraSep]); - - import std.array : array; - import std.utf : byChar; - assert(stripLeft(" hello world "w.byChar).array == - "hello world "); - assert(stripLeft(" \u2022hello world ".byChar).array == - "\u2022hello world "); -} - -auto stripLeft(Range)(auto ref Range str) -if (isConvertibleToString!Range) -{ - return stripLeft!(StringTypeOf!Range)(str); -} - -@nogc nothrow @safe pure unittest -{ - assert(testAliasedString!stripLeft(" hello")); -} - -/// Ditto -auto stripLeft(Range, Char)(Range input, const(Char)[] chars) -if (((isForwardRange!Range && isSomeChar!(ElementEncodingType!Range)) || - isConvertibleToString!Range) && isSomeChar!Char) -{ - static if (isConvertibleToString!Range) - return stripLeft!(StringTypeOf!Range)(input, chars); - else - { - for (; !input.empty; input.popFront) - { - if (chars.indexOf(input.front) == -1) - break; - } - return input; - } -} - -/// -@safe pure unittest -{ - assert(stripLeft(" hello world ", " ") == - "hello world "); - assert(stripLeft("xxxxxhello world ", "x") == - "hello world "); - assert(stripLeft("xxxyy hello world ", "xy ") == - "hello world "); -} - -/// -@safe pure unittest -{ - import std.array : array; - import std.utf : byChar, byWchar, byDchar; - - assert(stripLeft(" xxxyy hello world "w.byChar, "xy ").array == - "hello world "); - - assert(stripLeft("\u2028\u2020hello world\u2028"w.byWchar, - "\u2028").array == "\u2020hello world\u2028"); - assert(stripLeft("\U00010001hello world"w.byWchar, " ").array == - "\U00010001hello world"w); - assert(stripLeft("\U00010001 xyhello world"d.byDchar, - "\U00010001 xy").array == "hello world"d); - - assert(stripLeft("\u2020hello"w, "\u2020"w) == "hello"w); - assert(stripLeft("\U00010001hello"d, "\U00010001"d) == "hello"d); - assert(stripLeft(" hello ", "") == " hello "); -} - -@safe pure unittest -{ - assert(testAliasedString!stripLeft(" xyz hello", "xyz ")); -} - -/++ - Strips trailing whitespace (as defined by $(REF isWhite, std,uni)) or - as specified in the second argument. - - Params: - str = string or random access range of characters - chars = string of characters to be stripped - - Returns: - slice of `str` stripped of trailing whitespace or characters - specified in the second argument. - - See_Also: - Generic stripping on ranges: $(REF _stripRight, std, algorithm, mutation) - +/ -auto stripRight(Range)(Range str) -if (isSomeString!Range || - isRandomAccessRange!Range && hasLength!Range && hasSlicing!Range && - !isConvertibleToString!Range && - isSomeChar!(ElementEncodingType!Range)) -{ - import std.traits : isDynamicArray; - import std.uni : isWhite; - alias C = Unqual!(ElementEncodingType!(typeof(str))); - - static if (isSomeString!(typeof(str)) && C.sizeof >= 2) - { - // No whitespace takes multiple wchars to encode and due to - // the design of UTF-16 those wchars will not occur as part - // of the encoding of multi-wchar codepoints. - foreach_reverse (i, C c; str) - { - if (!isWhite(c)) - return str[0 .. i + 1]; - } - return str[0 .. 0]; - } - else - { - // ASCII optimization for dynamic arrays. - static if (isDynamicArray!(typeof(str))) - { - static import std.ascii; - foreach_reverse (i, C c; str) - { - if (c >= 0x80) - { - str = str[0 .. i + 1]; - goto NonAsciiPath; - } - if (!std.ascii.isWhite(c)) - { - return str[0 .. i + 1]; - } - } - return str[0 .. 0]; - } - - NonAsciiPath: - - size_t i = str.length; - while (i--) - { - static if (C.sizeof >= 2) - { - // No whitespace takes multiple wchars to encode and due to - // the design of UTF-16 those wchars will not occur as part - // of the encoding of multi-wchar codepoints. - if (isWhite(str[i])) - continue; - break; - } - else static if (C.sizeof == 1) - { - const cx = str[i]; - if (cx <= 0x7F) - { - if (isWhite(cx)) - continue; - break; - } - else - { - if (i == 0 || (0b1100_0000 & cx) != 0b1000_0000) - break; - const uint d = 0b0011_1111 & cx; - const c2 = str[i - 1]; - if ((c2 & 0b1110_0000) == 0b1100_0000) // 2 byte encoding. - { - if (isWhite(d + (uint(c2 & 0b0001_1111) << 6))) - { - i--; - continue; - } - break; - } - if (i == 1 || (c2 & 0b1100_0000) != 0b1000_0000) - break; - const c3 = str[i - 2]; - // In UTF-8 all whitespace is encoded in 3 bytes or fewer. - if ((c3 & 0b1111_0000) == 0b1110_0000 && - isWhite(d + (uint(c2 & 0b0011_1111) << 6) + (uint(c3 & 0b0000_1111) << 12))) - { - i -= 2; - continue; - } - break; - } - } - else - static assert(0); - } - - return str[0 .. i + 1]; - } -} - -/// -nothrow @safe pure -unittest -{ - import std.uni : lineSep, paraSep; - assert(stripRight(" hello world ") == - " hello world"); - assert(stripRight("\n\t\v\rhello world\n\t\v\r") == - "\n\t\v\rhello world"); - assert(stripRight("hello world") == - "hello world"); - assert(stripRight([lineSep] ~ "hello world" ~ lineSep) == - [lineSep] ~ "hello world"); - assert(stripRight([paraSep] ~ "hello world" ~ paraSep) == - [paraSep] ~ "hello world"); -} - -auto stripRight(Range)(auto ref Range str) -if (isConvertibleToString!Range) -{ - return stripRight!(StringTypeOf!Range)(str); -} - -@nogc nothrow @safe pure unittest -{ - assert(testAliasedString!stripRight("hello ")); -} - -@safe pure unittest -{ - import std.array : array; - import std.uni : lineSep, paraSep; - import std.utf : byChar, byDchar, byUTF, byWchar, invalidUTFstrings; - assert(stripRight(" hello world ".byChar).array == " hello world"); - assert(stripRight("\n\t\v\rhello world\n\t\v\r"w.byWchar).array == "\n\t\v\rhello world"w); - assert(stripRight("hello world"d.byDchar).array == "hello world"d); - assert(stripRight("\u2028hello world\u2020\u2028".byChar).array == "\u2028hello world\u2020"); - assert(stripRight("hello world\U00010001"w.byWchar).array == "hello world\U00010001"w); - - static foreach (C; AliasSeq!(char, wchar, dchar)) - { - foreach (s; invalidUTFstrings!C()) - { - cast(void) stripRight(s.byUTF!C).array; - } - } - - cast(void) stripRight("a\x80".byUTF!char).array; - wstring ws = ['a', cast(wchar) 0xDC00]; - cast(void) stripRight(ws.byUTF!wchar).array; -} - -/// Ditto -auto stripRight(Range, Char)(Range str, const(Char)[] chars) -if (((isBidirectionalRange!Range && isSomeChar!(ElementEncodingType!Range)) || - isConvertibleToString!Range) && isSomeChar!Char) -{ - static if (isConvertibleToString!Range) - return stripRight!(StringTypeOf!Range)(str, chars); - else - { - for (; !str.empty; str.popBack) - { - if (chars.indexOf(str.back) == -1) - break; - } - return str; - } -} - -/// -@safe pure -unittest -{ - assert(stripRight(" hello world ", "x") == - " hello world "); - assert(stripRight(" hello world ", " ") == - " hello world"); - assert(stripRight(" hello worldxy ", "xy ") == - " hello world"); -} - -@safe pure unittest -{ - assert(testAliasedString!stripRight("hello xyz ", "xyz ")); -} - -@safe pure unittest -{ - import std.array : array; - import std.utf : byChar, byDchar, byUTF, byWchar; - - assert(stripRight(" hello world xyz ".byChar, - "xyz ").array == " hello world"); - assert(stripRight("\u2028hello world\u2020\u2028"w.byWchar, - "\u2028").array == "\u2028hello world\u2020"); - assert(stripRight("hello world\U00010001"w.byWchar, - " ").array == "hello world\U00010001"w); - assert(stripRight("hello world\U00010001 xy"d.byDchar, - "\U00010001 xy").array == "hello world"d); - assert(stripRight("hello\u2020"w, "\u2020"w) == "hello"w); - assert(stripRight("hello\U00010001"d, "\U00010001"d) == "hello"d); - assert(stripRight(" hello ", "") == " hello "); -} - - -/++ - Strips both leading and trailing whitespace (as defined by - $(REF isWhite, std,uni)) or as specified in the second argument. - - Params: - str = string or random access range of characters - chars = string of characters to be stripped - leftChars = string of leading characters to be stripped - rightChars = string of trailing characters to be stripped - - Returns: - slice of `str` stripped of leading and trailing whitespace - or characters as specified in the second argument. - - See_Also: - Generic stripping on ranges: $(REF _strip, std, algorithm, mutation) - +/ -auto strip(Range)(Range str) -if (isSomeString!Range || - isRandomAccessRange!Range && hasLength!Range && hasSlicing!Range && - !isConvertibleToString!Range && - isSomeChar!(ElementEncodingType!Range)) -{ - return stripRight(stripLeft(str)); -} - -/// -@safe pure unittest -{ - import std.uni : lineSep, paraSep; - assert(strip(" hello world ") == - "hello world"); - assert(strip("\n\t\v\rhello world\n\t\v\r") == - "hello world"); - assert(strip("hello world") == - "hello world"); - assert(strip([lineSep] ~ "hello world" ~ [lineSep]) == - "hello world"); - assert(strip([paraSep] ~ "hello world" ~ [paraSep]) == - "hello world"); -} - -auto strip(Range)(auto ref Range str) -if (isConvertibleToString!Range) -{ - return strip!(StringTypeOf!Range)(str); -} - -@safe pure unittest -{ - assert(testAliasedString!strip(" hello world ")); -} - -@safe pure unittest -{ - import std.algorithm.comparison : equal; - import std.conv : to; - import std.exception : assertCTFEable; - - assertCTFEable!( - { - static foreach (S; AliasSeq!( char[], const char[], string, - wchar[], const wchar[], wstring, - dchar[], const dchar[], dstring)) - { - assert(equal(stripLeft(to!S(" foo\t ")), "foo\t ")); - assert(equal(stripLeft(to!S("\u2008 foo\t \u2007")), "foo\t \u2007")); - assert(equal(stripLeft(to!S("\u0085 Îź \u0085 \u00BB \r")), "Îź \u0085 \u00BB \r")); - assert(equal(stripLeft(to!S("1")), "1")); - assert(equal(stripLeft(to!S("\U0010FFFE")), "\U0010FFFE")); - assert(equal(stripLeft(to!S("")), "")); - - assert(equal(stripRight(to!S(" foo\t ")), " foo")); - assert(equal(stripRight(to!S("\u2008 foo\t \u2007")), "\u2008 foo")); - assert(equal(stripRight(to!S("\u0085 Îź \u0085 \u00BB \r")), "\u0085 Îź \u0085 \u00BB")); - assert(equal(stripRight(to!S("1")), "1")); - assert(equal(stripRight(to!S("\U0010FFFE")), "\U0010FFFE")); - assert(equal(stripRight(to!S("")), "")); - - assert(equal(strip(to!S(" foo\t ")), "foo")); - assert(equal(strip(to!S("\u2008 foo\t \u2007")), "foo")); - assert(equal(strip(to!S("\u0085 Îź \u0085 \u00BB \r")), "Îź \u0085 \u00BB")); - assert(equal(strip(to!S("\U0010FFFE")), "\U0010FFFE")); - assert(equal(strip(to!S("")), "")); - } - }); -} - -@safe pure unittest -{ - import std.array : sameHead, sameTail; - import std.exception : assertCTFEable; - assertCTFEable!( - { - wstring s = " "; - assert(s.sameTail(s.stripLeft())); - assert(s.sameHead(s.stripRight())); - }); -} - -/// Ditto -auto strip(Range, Char)(Range str, const(Char)[] chars) -if (((isBidirectionalRange!Range && isSomeChar!(ElementEncodingType!Range)) || - isConvertibleToString!Range) && isSomeChar!Char) -{ - static if (isConvertibleToString!Range) - return strip!(StringTypeOf!Range)(str, chars); - else - return stripRight(stripLeft(str, chars), chars); -} - -/// -@safe pure unittest -{ - assert(strip(" hello world ", "x") == - " hello world "); - assert(strip(" hello world ", " ") == - "hello world"); - assert(strip(" xyxyhello worldxyxy ", "xy ") == - "hello world"); - assert(strip("\u2020hello\u2020"w, "\u2020"w) == "hello"w); - assert(strip("\U00010001hello\U00010001"d, "\U00010001"d) == "hello"d); - assert(strip(" hello ", "") == " hello "); -} - -@safe pure unittest -{ - assert(testAliasedString!strip(" xyz hello world xyz ", "xyz ")); -} - -/// Ditto -auto strip(Range, Char)(Range str, const(Char)[] leftChars, const(Char)[] rightChars) -if (((isBidirectionalRange!Range && isSomeChar!(ElementEncodingType!Range)) || - isConvertibleToString!Range) && isSomeChar!Char) -{ - static if (isConvertibleToString!Range) - return strip!(StringTypeOf!Range)(str, leftChars, rightChars); - else - return stripRight(stripLeft(str, leftChars), rightChars); -} - -/// -@safe pure unittest -{ - assert(strip("xxhelloyy", "x", "y") == "hello"); - assert(strip(" xyxyhello worldxyxyzz ", "xy ", "xyz ") == - "hello world"); - assert(strip("\u2020hello\u2028"w, "\u2020"w, "\u2028"w) == "hello"w); - assert(strip("\U00010001hello\U00010002"d, "\U00010001"d, "\U00010002"d) == - "hello"d); - assert(strip(" hello ", "", "") == " hello "); -} - -@safe pure unittest -{ - assert(testAliasedString!strip(" xy hello world pq ", "xy ", "pq ")); -} - -@safe pure unittest -{ - import std.algorithm.comparison : equal; - import std.conv : to; - import std.exception : assertCTFEable; - - assertCTFEable!( - { - static foreach (S; AliasSeq!( char[], const char[], string, - wchar[], const wchar[], wstring, - dchar[], const dchar[], dstring)) - { - assert(equal(stripLeft(to!S(" \tfoo\t "), "\t "), "foo\t ")); - assert(equal(stripLeft(to!S("\u2008 foo\t \u2007"), "\u2008 "), - "foo\t \u2007")); - assert(equal(stripLeft(to!S("\u0085 Îź \u0085 \u00BB \r"), "\u0085 "), - "Îź \u0085 \u00BB \r")); - assert(equal(stripLeft(to!S("1"), " "), "1")); - assert(equal(stripLeft(to!S("\U0010FFFE"), " "), "\U0010FFFE")); - assert(equal(stripLeft(to!S(""), " "), "")); - - assert(equal(stripRight(to!S(" foo\t "), "\t "), " foo")); - assert(equal(stripRight(to!S("\u2008 foo\t \u2007"), "\u2007\t "), - "\u2008 foo")); - assert(equal(stripRight(to!S("\u0085 Îź \u0085 \u00BB \r"), "\r "), - "\u0085 Îź \u0085 \u00BB")); - assert(equal(stripRight(to!S("1"), " "), "1")); - assert(equal(stripRight(to!S("\U0010FFFE"), " "), "\U0010FFFE")); - assert(equal(stripRight(to!S(""), " "), "")); - - assert(equal(strip(to!S(" foo\t "), "\t "), "foo")); - assert(equal(strip(to!S("\u2008 foo\t \u2007"), "\u2008\u2007\t "), - "foo")); - assert(equal(strip(to!S("\u0085 Îź \u0085 \u00BB \r"), "\u0085\r "), - "Îź \u0085 \u00BB")); - assert(equal(strip(to!S("\U0010FFFE"), " "), "\U0010FFFE")); - assert(equal(strip(to!S(""), " "), "")); - - assert(equal(strip(to!S(" \nfoo\t "), "\n ", "\t "), "foo")); - assert(equal(strip(to!S("\u2008\n foo\t \u2007"), - "\u2008\n ", "\u2007\t "), "foo")); - assert(equal(strip(to!S("\u0085 Îź \u0085 \u00BB Îź \u00BB\r"), - "\u0085 ", "\u00BB\r "), "Îź \u0085 \u00BB Îź")); - assert(equal(strip(to!S("\U0010FFFE"), " ", " "), "\U0010FFFE")); - assert(equal(strip(to!S(""), " ", " "), "")); - } - }); -} - -@safe pure unittest -{ - import std.array : sameHead, sameTail; - import std.exception : assertCTFEable; - assertCTFEable!( - { - wstring s = " xyz "; - assert(s.sameTail(s.stripLeft(" "))); - assert(s.sameHead(s.stripRight(" "))); - }); -} - - -/++ - If `str` ends with `delimiter`, then `str` is returned without - `delimiter` on its end. If it `str` does $(I not) end with - `delimiter`, then it is returned unchanged. - - If no `delimiter` is given, then one trailing `'\r'`, `'\n'`, - `"\r\n"`, `'\f'`, `'\v'`, $(REF lineSep, std,uni), $(REF paraSep, std,uni), or $(REF nelSep, std,uni) - is removed from the end of `str`. If `str` does not end with any of those characters, - then it is returned unchanged. - - Params: - str = string or indexable range of characters - delimiter = string of characters to be sliced off end of str[] - - Returns: - slice of str - +/ -Range chomp(Range)(Range str) -if ((isRandomAccessRange!Range && isSomeChar!(ElementEncodingType!Range) || - isNarrowString!Range) && - !isConvertibleToString!Range) -{ - import std.uni : lineSep, paraSep, nelSep; - if (str.empty) - return str; - - alias C = ElementEncodingType!Range; - - switch (str[$ - 1]) - { - case '\n': - { - if (str.length > 1 && str[$ - 2] == '\r') - return str[0 .. $ - 2]; - goto case; - } - case '\r', '\v', '\f': - return str[0 .. $ - 1]; - - // Pop off the last character if lineSep, paraSep, or nelSep - static if (is(C : const char)) - { - /* Manually decode: - * lineSep is E2 80 A8 - * paraSep is E2 80 A9 - */ - case 0xA8: // Last byte of lineSep - case 0xA9: // Last byte of paraSep - if (str.length > 2 && str[$ - 2] == 0x80 && str[$ - 3] == 0xE2) - return str [0 .. $ - 3]; - goto default; - - /* Manually decode: - * NEL is C2 85 - */ - case 0x85: - if (str.length > 1 && str[$ - 2] == 0xC2) - return str [0 .. $ - 2]; - goto default; - } - else - { - case lineSep: - case paraSep: - case nelSep: - return str[0 .. $ - 1]; - } - default: - return str; - } -} - -/// Ditto -Range chomp(Range, C2)(Range str, const(C2)[] delimiter) -if ((isBidirectionalRange!Range && isSomeChar!(ElementEncodingType!Range) || - isNarrowString!Range) && - !isConvertibleToString!Range && - isSomeChar!C2) -{ - if (delimiter.empty) - return chomp(str); - - alias C1 = ElementEncodingType!Range; - - static if (is(immutable C1 == immutable C2) && (isSomeString!Range || (hasSlicing!Range && C2.sizeof == 4))) - { - import std.algorithm.searching : endsWith; - if (str.endsWith(delimiter)) - return str[0 .. $ - delimiter.length]; - return str; - } - else - { - auto orig = str.save; - - static if (isSomeString!Range) - alias C = dchar; // because strings auto-decode - else - alias C = C1; // and ranges do not - - foreach_reverse (C c; delimiter) - { - if (str.empty || str.back != c) - return orig; - - str.popBack(); - } - - return str; - } -} - -/// -@safe pure -unittest -{ - import std.uni : lineSep, paraSep, nelSep; - import std.utf : decode; - assert(chomp(" hello world \n\r") == " hello world \n"); - assert(chomp(" hello world \r\n") == " hello world "); - assert(chomp(" hello world \f") == " hello world "); - assert(chomp(" hello world \v") == " hello world "); - assert(chomp(" hello world \n\n") == " hello world \n"); - assert(chomp(" hello world \n\n ") == " hello world \n\n "); - assert(chomp(" hello world \n\n" ~ [lineSep]) == " hello world \n\n"); - assert(chomp(" hello world \n\n" ~ [paraSep]) == " hello world \n\n"); - assert(chomp(" hello world \n\n" ~ [ nelSep]) == " hello world \n\n"); - assert(chomp(" hello world ") == " hello world "); - assert(chomp(" hello world") == " hello world"); - assert(chomp("") == ""); - - assert(chomp(" hello world", "orld") == " hello w"); - assert(chomp(" hello world", " he") == " hello world"); - assert(chomp("", "hello") == ""); - - // Don't decode pointlessly - assert(chomp("hello\xFE", "\r") == "hello\xFE"); -} - -StringTypeOf!Range chomp(Range)(auto ref Range str) -if (isConvertibleToString!Range) -{ - return chomp!(StringTypeOf!Range)(str); -} - -StringTypeOf!Range chomp(Range, C2)(auto ref Range str, const(C2)[] delimiter) -if (isConvertibleToString!Range) -{ - return chomp!(StringTypeOf!Range, C2)(str, delimiter); -} - -@safe pure unittest -{ - assert(testAliasedString!chomp(" hello world \n\r")); - assert(testAliasedString!chomp(" hello world", "orld")); -} - -@safe pure unittest -{ - import std.conv : to; - import std.exception : assertCTFEable; - - assertCTFEable!( - { - static foreach (S; AliasSeq!(char[], wchar[], dchar[], string, wstring, dstring)) - { - // @@@ BUG IN COMPILER, MUST INSERT CAST - assert(chomp(cast(S) null) is null); - assert(chomp(to!S("hello")) == "hello"); - assert(chomp(to!S("hello\n")) == "hello"); - assert(chomp(to!S("hello\r")) == "hello"); - assert(chomp(to!S("hello\r\n")) == "hello"); - assert(chomp(to!S("hello\n\r")) == "hello\n"); - assert(chomp(to!S("hello\n\n")) == "hello\n"); - assert(chomp(to!S("hello\r\r")) == "hello\r"); - assert(chomp(to!S("hello\nxxx\n")) == "hello\nxxx"); - assert(chomp(to!S("hello\u2028")) == "hello"); - assert(chomp(to!S("hello\u2029")) == "hello"); - assert(chomp(to!S("hello\u0085")) == "hello"); - assert(chomp(to!S("hello\u2028\u2028")) == "hello\u2028"); - assert(chomp(to!S("hello\u2029\u2029")) == "hello\u2029"); - assert(chomp(to!S("hello\u2029\u2129")) == "hello\u2029\u2129"); - assert(chomp(to!S("hello\u2029\u0185")) == "hello\u2029\u0185"); - - static foreach (T; AliasSeq!(char[], wchar[], dchar[], string, wstring, dstring)) - { - // @@@ BUG IN COMPILER, MUST INSERT CAST - assert(chomp(cast(S) null, cast(T) null) is null); - assert(chomp(to!S("hello\n"), cast(T) null) == "hello"); - assert(chomp(to!S("hello"), to!T("o")) == "hell"); - assert(chomp(to!S("hello"), to!T("p")) == "hello"); - // @@@ BUG IN COMPILER, MUST INSERT CAST - assert(chomp(to!S("hello"), cast(T) null) == "hello"); - assert(chomp(to!S("hello"), to!T("llo")) == "he"); - assert(chomp(to!S("\uFF28ello"), to!T("llo")) == "\uFF28e"); - assert(chomp(to!S("\uFF28el\uFF4co"), to!T("l\uFF4co")) == "\uFF28e"); - } - } - }); - - // Ranges - import std.array : array; - import std.utf : byChar, byWchar, byDchar; - assert(chomp("hello world\r\n" .byChar ).array == "hello world"); - assert(chomp("hello world\r\n"w.byWchar).array == "hello world"w); - assert(chomp("hello world\r\n"d.byDchar).array == "hello world"d); - - assert(chomp("hello world"d.byDchar, "ld").array == "hello wor"d); - - assert(chomp("hello\u2020" .byChar , "\u2020").array == "hello"); - assert(chomp("hello\u2020"d.byDchar, "\u2020"d).array == "hello"d); -} - - -/++ - If `str` starts with `delimiter`, then the part of `str` following - `delimiter` is returned. If `str` does $(I not) start with - - `delimiter`, then it is returned unchanged. - - Params: - str = string or $(REF_ALTTEXT forward range, isForwardRange, std,range,primitives) - of characters - delimiter = string of characters to be sliced off front of str[] - - Returns: - slice of str - +/ -Range chompPrefix(Range, C2)(Range str, const(C2)[] delimiter) -if ((isForwardRange!Range && isSomeChar!(ElementEncodingType!Range) || - isNarrowString!Range) && - !isConvertibleToString!Range && - isSomeChar!C2) -{ - alias C1 = ElementEncodingType!Range; - - static if (is(immutable C1 == immutable C2) && (isSomeString!Range || (hasSlicing!Range && C2.sizeof == 4))) - { - import std.algorithm.searching : startsWith; - if (str.startsWith(delimiter)) - return str[delimiter.length .. $]; - return str; - } - else - { - auto orig = str.save; - - static if (isSomeString!Range) - alias C = dchar; // because strings auto-decode - else - alias C = C1; // and ranges do not - - foreach (C c; delimiter) - { - if (str.empty || str.front != c) - return orig; - - str.popFront(); - } - - return str; - } -} - -/// -@safe pure unittest -{ - assert(chompPrefix("hello world", "he") == "llo world"); - assert(chompPrefix("hello world", "hello w") == "orld"); - assert(chompPrefix("hello world", " world") == "hello world"); - assert(chompPrefix("", "hello") == ""); -} - -StringTypeOf!Range chompPrefix(Range, C2)(auto ref Range str, const(C2)[] delimiter) -if (isConvertibleToString!Range) -{ - return chompPrefix!(StringTypeOf!Range, C2)(str, delimiter); -} - -@safe pure -unittest -{ - import std.algorithm.comparison : equal; - import std.conv : to; - import std.exception : assertCTFEable; - assertCTFEable!( - { - static foreach (S; AliasSeq!(char[], wchar[], dchar[], string, wstring, dstring)) - { - static foreach (T; AliasSeq!(char[], wchar[], dchar[], string, wstring, dstring)) - { - assert(equal(chompPrefix(to!S("abcdefgh"), to!T("abcde")), "fgh")); - assert(equal(chompPrefix(to!S("abcde"), to!T("abcdefgh")), "abcde")); - assert(equal(chompPrefix(to!S("\uFF28el\uFF4co"), to!T("\uFF28el\uFF4co")), "")); - assert(equal(chompPrefix(to!S("\uFF28el\uFF4co"), to!T("\uFF28el")), "\uFF4co")); - assert(equal(chompPrefix(to!S("\uFF28el"), to!T("\uFF28el\uFF4co")), "\uFF28el")); - } - } - }); - - // Ranges - import std.array : array; - import std.utf : byChar, byWchar, byDchar; - assert(chompPrefix("hello world" .byChar , "hello"d).array == " world"); - assert(chompPrefix("hello world"w.byWchar, "hello" ).array == " world"w); - assert(chompPrefix("hello world"d.byDchar, "hello"w).array == " world"d); - assert(chompPrefix("hello world"c.byDchar, "hello"w).array == " world"d); - - assert(chompPrefix("hello world"d.byDchar, "lx").array == "hello world"d); - assert(chompPrefix("hello world"d.byDchar, "hello world xx").array == "hello world"d); - - assert(chompPrefix("\u2020world" .byChar , "\u2020").array == "world"); - assert(chompPrefix("\u2020world"d.byDchar, "\u2020"d).array == "world"d); -} - -@safe pure unittest -{ - assert(testAliasedString!chompPrefix("hello world", "hello")); -} - -/++ - Returns `str` without its last character, if there is one. If `str` - ends with `"\r\n"`, then both are removed. If `str` is empty, then - it is returned unchanged. - - Params: - str = string (must be valid UTF) - Returns: - slice of str - +/ - -Range chop(Range)(Range str) -if ((isBidirectionalRange!Range && isSomeChar!(ElementEncodingType!Range) || - isNarrowString!Range) && - !isConvertibleToString!Range) -{ - if (str.empty) - return str; - - static if (isSomeString!Range) - { - if (str.length >= 2 && str[$ - 1] == '\n' && str[$ - 2] == '\r') - return str[0 .. $ - 2]; - str.popBack(); - return str; - } - else - { - alias C = Unqual!(ElementEncodingType!Range); - C c = str.back; - str.popBack(); - if (c == '\n') - { - if (!str.empty && str.back == '\r') - str.popBack(); - return str; - } - // Pop back a dchar, not just a code unit - static if (C.sizeof == 1) - { - int cnt = 1; - while ((c & 0xC0) == 0x80) - { - if (str.empty) - break; - c = str.back; - str.popBack(); - if (++cnt > 4) - break; - } - } - else static if (C.sizeof == 2) - { - if (c >= 0xD800 && c <= 0xDBFF) - { - if (!str.empty) - str.popBack(); - } - } - else static if (C.sizeof == 4) - { - } - else - static assert(0); - return str; - } -} - -/// -@safe pure unittest -{ - assert(chop("hello world") == "hello worl"); - assert(chop("hello world\n") == "hello world"); - assert(chop("hello world\r") == "hello world"); - assert(chop("hello world\n\r") == "hello world\n"); - assert(chop("hello world\r\n") == "hello world"); - assert(chop("Walter Bright") == "Walter Brigh"); - assert(chop("") == ""); -} - -StringTypeOf!Range chop(Range)(auto ref Range str) -if (isConvertibleToString!Range) -{ - return chop!(StringTypeOf!Range)(str); -} - -@safe pure unittest -{ - assert(testAliasedString!chop("hello world")); -} - -@safe pure unittest -{ - import std.array : array; - import std.utf : byChar, byWchar, byDchar, byCodeUnit, invalidUTFstrings; - - assert(chop("hello world".byChar).array == "hello worl"); - assert(chop("hello world\n"w.byWchar).array == "hello world"w); - assert(chop("hello world\r"d.byDchar).array == "hello world"d); - assert(chop("hello world\n\r".byChar).array == "hello world\n"); - assert(chop("hello world\r\n"w.byWchar).array == "hello world"w); - assert(chop("Walter Bright"d.byDchar).array == "Walter Brigh"d); - assert(chop("".byChar).array == ""); - - assert(chop(`ミツバチと科学者` .byCodeUnit).array == "ミツバチと科学"); - assert(chop(`ミツバチと科学者`w.byCodeUnit).array == "ミツバチと科学"w); - assert(chop(`ミツバチと科学者`d.byCodeUnit).array == "ミツバチと科学"d); - - auto ca = invalidUTFstrings!char(); - foreach (s; ca) - { - foreach (c; chop(s.byCodeUnit)) - { - } - } - - auto wa = invalidUTFstrings!wchar(); - foreach (s; wa) - { - foreach (c; chop(s.byCodeUnit)) - { - } - } -} - -@safe pure unittest -{ - import std.algorithm.comparison : equal; - import std.conv : to; - import std.exception : assertCTFEable; - - assertCTFEable!( - { - static foreach (S; AliasSeq!(char[], wchar[], dchar[], string, wstring, dstring)) - { - assert(chop(cast(S) null) is null); - assert(equal(chop(to!S("hello")), "hell")); - assert(equal(chop(to!S("hello\r\n")), "hello")); - assert(equal(chop(to!S("hello\n\r")), "hello\n")); - assert(equal(chop(to!S("VeritĂŠ")), "Verit")); - assert(equal(chop(to!S(`さいごの果実`)), "さいごの果")); - assert(equal(chop(to!S(`ミツバチと科学者`)), "ミツバチと科学")); - } - }); -} - - -/++ - Left justify `s` in a field `width` characters wide. `fillChar` - is the character that will be used to fill up the space in the field that - `s` doesn't fill. - - Params: - s = string - width = minimum field width - fillChar = used to pad end up to `width` characters - - Returns: - GC allocated string - - See_Also: - $(LREF leftJustifier), which does not allocate - +/ -S leftJustify(S)(S s, size_t width, dchar fillChar = ' ') -if (isSomeString!S) -{ - import std.array : array; - return leftJustifier(s, width, fillChar).array; -} - -/// -@safe pure unittest -{ - assert(leftJustify("hello", 7, 'X') == "helloXX"); - assert(leftJustify("hello", 2, 'X') == "hello"); - assert(leftJustify("hello", 9, 'X') == "helloXXXX"); -} - -/++ - Left justify `s` in a field `width` characters wide. `fillChar` - is the character that will be used to fill up the space in the field that - `s` doesn't fill. - - Params: - r = string or range of characters - width = minimum field width - fillChar = used to pad end up to `width` characters - - Returns: - a lazy range of the left justified result - - See_Also: - $(LREF rightJustifier) - +/ - -auto leftJustifier(Range)(Range r, size_t width, dchar fillChar = ' ') -if (isInputRange!Range && isSomeChar!(ElementEncodingType!Range) && - !isConvertibleToString!Range) -{ - alias C = Unqual!(ElementEncodingType!Range); - - static if (C.sizeof == 1) - { - import std.utf : byDchar, byChar; - return leftJustifier(r.byDchar, width, fillChar).byChar; - } - else static if (C.sizeof == 2) - { - import std.utf : byDchar, byWchar; - return leftJustifier(r.byDchar, width, fillChar).byWchar; - } - else static if (C.sizeof == 4) - { - static struct Result - { - private: - Range _input; - size_t _width; - dchar _fillChar; - size_t len; - - public: - - @property bool empty() - { - return len >= _width && _input.empty; - } - - @property C front() - { - return _input.empty ? _fillChar : _input.front; - } - - void popFront() - { - ++len; - if (!_input.empty) - _input.popFront(); - } - - static if (isForwardRange!Range) - { - @property typeof(this) save() return scope - { - auto ret = this; - ret._input = _input.save; - return ret; - } - } - } - - return Result(r, width, fillChar); - } - else - static assert(0); -} - -/// -@safe pure @nogc nothrow -unittest -{ - import std.algorithm.comparison : equal; - import std.utf : byChar; - assert(leftJustifier("hello", 2).equal("hello".byChar)); - assert(leftJustifier("hello", 7).equal("hello ".byChar)); - assert(leftJustifier("hello", 7, 'x').equal("helloxx".byChar)); -} - -auto leftJustifier(Range)(auto ref Range r, size_t width, dchar fillChar = ' ') -if (isConvertibleToString!Range) -{ - return leftJustifier!(StringTypeOf!Range)(r, width, fillChar); -} - -@safe pure unittest -{ - auto r = "hello".leftJustifier(8); - r.popFront(); - auto save = r.save; - r.popFront(); - assert(r.front == 'l'); - assert(save.front == 'e'); -} - -@safe pure unittest -{ - assert(testAliasedString!leftJustifier("hello", 2)); -} - -/++ - Right justify `s` in a field `width` characters wide. `fillChar` - is the character that will be used to fill up the space in the field that - `s` doesn't fill. - - Params: - s = string - width = minimum field width - fillChar = used to pad end up to `width` characters - - Returns: - GC allocated string - - See_Also: - $(LREF rightJustifier), which does not allocate - +/ -S rightJustify(S)(S s, size_t width, dchar fillChar = ' ') -if (isSomeString!S) -{ - import std.array : array; - return rightJustifier(s, width, fillChar).array; -} - -/// -@safe pure unittest -{ - assert(rightJustify("hello", 7, 'X') == "XXhello"); - assert(rightJustify("hello", 2, 'X') == "hello"); - assert(rightJustify("hello", 9, 'X') == "XXXXhello"); -} - -/++ - Right justify `s` in a field `width` characters wide. `fillChar` - is the character that will be used to fill up the space in the field that - `s` doesn't fill. - - Params: - r = string or $(REF_ALTTEXT forward range, isForwardRange, std,range,primitives) - of characters - width = minimum field width - fillChar = used to pad end up to `width` characters - - Returns: - a lazy range of the right justified result - - See_Also: - $(LREF leftJustifier) - +/ - -auto rightJustifier(Range)(Range r, size_t width, dchar fillChar = ' ') -if (isForwardRange!Range && isSomeChar!(ElementEncodingType!Range) && - !isConvertibleToString!Range) -{ - alias C = Unqual!(ElementEncodingType!Range); - - static if (C.sizeof == 1) - { - import std.utf : byDchar, byChar; - return rightJustifier(r.byDchar, width, fillChar).byChar; - } - else static if (C.sizeof == 2) - { - import std.utf : byDchar, byWchar; - return rightJustifier(r.byDchar, width, fillChar).byWchar; - } - else static if (C.sizeof == 4) - { - static struct Result - { - private: - Range _input; - size_t _width; - alias nfill = _width; // number of fill characters to prepend - dchar _fillChar; - bool inited; - - // Lazy initialization so constructor is trivial and cannot fail - void initialize() - { - // Replace _width with nfill - // (use alias instead of union because CTFE cannot deal with unions) - assert(_width, "width of 0 not allowed"); - static if (hasLength!Range) - { - immutable len = _input.length; - nfill = (_width > len) ? _width - len : 0; - } - else - { - // Lookahead to see now many fill characters are needed - import std.range : take; - import std.range.primitives : walkLength; - nfill = _width - walkLength(_input.save.take(_width), _width); - } - inited = true; - } - - public: - this(Range input, size_t width, dchar fillChar) pure nothrow - { - _input = input; - _fillChar = fillChar; - _width = width; - } - - @property bool empty() - { - return !nfill && _input.empty; - } - - @property C front() - { - if (!nfill) - return _input.front; // fast path - if (!inited) - initialize(); - return nfill ? _fillChar : _input.front; - } - - void popFront() - { - if (!nfill) - _input.popFront(); // fast path - else - { - if (!inited) - initialize(); - if (nfill) - --nfill; - else - _input.popFront(); - } - } - - @property typeof(this) save() - { - auto ret = this; - ret._input = _input.save; - return ret; - } - } - - return Result(r, width, fillChar); - } - else - static assert(0, "Invalid character type of " ~ C.stringof); -} - -/// -@safe pure @nogc nothrow -unittest -{ - import std.algorithm.comparison : equal; - import std.utf : byChar; - assert(rightJustifier("hello", 2).equal("hello".byChar)); - assert(rightJustifier("hello", 7).equal(" hello".byChar)); - assert(rightJustifier("hello", 7, 'x').equal("xxhello".byChar)); -} - -auto rightJustifier(Range)(auto ref Range r, size_t width, dchar fillChar = ' ') -if (isConvertibleToString!Range) -{ - return rightJustifier!(StringTypeOf!Range)(r, width, fillChar); -} - -@safe pure unittest -{ - assert(testAliasedString!rightJustifier("hello", 2)); -} - -@safe pure unittest -{ - auto r = "hello"d.rightJustifier(6); - r.popFront(); - auto save = r.save; - r.popFront(); - assert(r.front == 'e'); - assert(save.front == 'h'); - - auto t = "hello".rightJustifier(7); - t.popFront(); - assert(t.front == ' '); - t.popFront(); - assert(t.front == 'h'); - - auto u = "hello"d.rightJustifier(5); - u.popFront(); - u.popFront(); - u.popFront(); -} - -/++ - Center `s` in a field `width` characters wide. `fillChar` - is the character that will be used to fill up the space in the field that - `s` doesn't fill. - - Params: - s = The string to center - width = Width of the field to center `s` in - fillChar = The character to use for filling excess space in the field - - Returns: - The resulting _center-justified string. The returned string is - GC-allocated. To avoid GC allocation, use $(LREF centerJustifier) - instead. - +/ -S center(S)(S s, size_t width, dchar fillChar = ' ') -if (isSomeString!S) -{ - import std.array : array; - return centerJustifier(s, width, fillChar).array; -} - -/// -@safe pure unittest -{ - assert(center("hello", 7, 'X') == "XhelloX"); - assert(center("hello", 2, 'X') == "hello"); - assert(center("hello", 9, 'X') == "XXhelloXX"); -} - -@safe pure -unittest -{ - import std.conv : to; - import std.exception : assertCTFEable; - - assertCTFEable!( - { - static foreach (S; AliasSeq!(char[], wchar[], dchar[], string, wstring, dstring)) - {{ - S s = to!S("hello"); - - assert(leftJustify(s, 2) == "hello"); - assert(rightJustify(s, 2) == "hello"); - assert(center(s, 2) == "hello"); - - assert(leftJustify(s, 7) == "hello "); - assert(rightJustify(s, 7) == " hello"); - assert(center(s, 7) == " hello "); - - assert(leftJustify(s, 8) == "hello "); - assert(rightJustify(s, 8) == " hello"); - assert(center(s, 8) == " hello "); - - assert(leftJustify(s, 8, '\u0100') == "hello\u0100\u0100\u0100"); - assert(rightJustify(s, 8, '\u0100') == "\u0100\u0100\u0100hello"); - assert(center(s, 8, '\u0100') == "\u0100hello\u0100\u0100"); - - assert(leftJustify(s, 8, 'Ăś') == "helloÜÜÜ"); - assert(rightJustify(s, 8, 'Ăś') == "ÜÜÜhello"); - assert(center(s, 8, 'Ăś') == "ĂśhelloÜÜ"); - }} - }); -} - -/++ - Center justify `r` in a field `width` characters wide. `fillChar` - is the character that will be used to fill up the space in the field that - `r` doesn't fill. - - Params: - r = string or $(REF_ALTTEXT forward range, isForwardRange, std,range,primitives) - of characters - width = minimum field width - fillChar = used to pad end up to `width` characters - - Returns: - a lazy range of the center justified result - - See_Also: - $(LREF leftJustifier) - $(LREF rightJustifier) - +/ - -auto centerJustifier(Range)(Range r, size_t width, dchar fillChar = ' ') -if (isForwardRange!Range && isSomeChar!(ElementEncodingType!Range) && - !isConvertibleToString!Range) -{ - alias C = Unqual!(ElementEncodingType!Range); - - static if (C.sizeof == 1) - { - import std.utf : byDchar, byChar; - return centerJustifier(r.byDchar, width, fillChar).byChar; - } - else static if (C.sizeof == 2) - { - import std.utf : byDchar, byWchar; - return centerJustifier(r.byDchar, width, fillChar).byWchar; - } - else static if (C.sizeof == 4) - { - import std.range : chain, repeat; - import std.range.primitives : walkLength; - - auto len = walkLength(r.save, width); - if (len > width) - len = width; - const nleft = (width - len) / 2; - const nright = width - len - nleft; - return chain(repeat(fillChar, nleft), r, repeat(fillChar, nright)); - } - else - static assert(0); -} - -/// -@safe pure @nogc nothrow -unittest -{ - import std.algorithm.comparison : equal; - import std.utf : byChar; - assert(centerJustifier("hello", 2).equal("hello".byChar)); - assert(centerJustifier("hello", 8).equal(" hello ".byChar)); - assert(centerJustifier("hello", 7, 'x').equal("xhellox".byChar)); -} - -auto centerJustifier(Range)(auto ref Range r, size_t width, dchar fillChar = ' ') -if (isConvertibleToString!Range) -{ - return centerJustifier!(StringTypeOf!Range)(r, width, fillChar); -} - -@safe pure unittest -{ - assert(testAliasedString!centerJustifier("hello", 8)); -} - -@safe unittest -{ - static auto byFwdRange(dstring s) - { - static struct FRange - { - @safe: - dstring str; - this(dstring s) { str = s; } - @property bool empty() { return str.length == 0; } - @property dchar front() { return str[0]; } - void popFront() { str = str[1 .. $]; } - @property FRange save() { return this; } - } - return FRange(s); - } - - auto r = centerJustifier(byFwdRange("hello"d), 6); - r.popFront(); - auto save = r.save; - r.popFront(); - assert(r.front == 'l'); - assert(save.front == 'e'); - - auto t = "hello".centerJustifier(7); - t.popFront(); - assert(t.front == 'h'); - t.popFront(); - assert(t.front == 'e'); - - auto u = byFwdRange("hello"d).centerJustifier(6); - u.popFront(); - u.popFront(); - u.popFront(); - u.popFront(); - u.popFront(); - u.popFront(); -} - - -/++ - Replace each tab character in `s` with the number of spaces necessary - to align the following character at the next tab stop. - - Params: - s = string - tabSize = distance between tab stops - - Returns: - GC allocated string with tabs replaced with spaces - +/ -auto detab(Range)(auto ref Range s, size_t tabSize = 8) pure -if ((isForwardRange!Range && isSomeChar!(ElementEncodingType!Range)) - || __traits(compiles, StringTypeOf!Range)) -{ - import std.array : array; - return detabber(s, tabSize).array; -} - -/// -@safe pure unittest -{ - assert(detab(" \n\tx", 9) == " \n x"); -} - -@safe pure unittest -{ - static struct TestStruct - { - string s; - alias s this; - } - - static struct TestStruct2 - { - string s; - alias s this; - @disable this(this); - } - - string s = " \n\tx"; - string cmp = " \n x"; - auto t = TestStruct(s); - assert(detab(t, 9) == cmp); - assert(detab(TestStruct(s), 9) == cmp); - assert(detab(TestStruct(s), 9) == detab(TestStruct(s), 9)); - assert(detab(TestStruct2(s), 9) == detab(TestStruct2(s), 9)); - assert(detab(TestStruct2(s), 9) == cmp); -} - -/++ - Replace each tab character in `r` with the number of spaces - necessary to align the following character at the next tab stop. - - Params: - r = string or $(REF_ALTTEXT forward range, isForwardRange, std,range,primitives) - tabSize = distance between tab stops - - Returns: - lazy forward range with tabs replaced with spaces - +/ -auto detabber(Range)(Range r, size_t tabSize = 8) -if (isForwardRange!Range && isSomeChar!(ElementEncodingType!Range) && - !isConvertibleToString!Range) -{ - import std.uni : lineSep, paraSep, nelSep; - import std.utf : codeUnitLimit, decodeFront; - - assert(tabSize > 0); - - alias C = Unqual!(ElementEncodingType!(Range)); - - static struct Result - { - private: - Range _input; - size_t _tabSize; - size_t nspaces; - int column; - size_t index; - - public: - - this(Range input, size_t tabSize) - { - _input = input; - _tabSize = tabSize; - } - - static if (isInfinite!(Range)) - { - enum bool empty = false; - } - else - { - @property bool empty() - { - return _input.empty && nspaces == 0; - } - } - - @property C front() - { - if (nspaces) - return ' '; - static if (isSomeString!(Range)) - C c = _input[0]; - else - C c = _input.front; - if (index) - return c; - dchar dc; - if (c < codeUnitLimit!(immutable(C)[])) - { - dc = c; - index = 1; - } - else - { - auto r = _input.save; - dc = decodeFront(r, index); // lookahead to decode - } - switch (dc) - { - case '\r': - case '\n': - case paraSep: - case lineSep: - case nelSep: - column = 0; - break; - - case '\t': - nspaces = _tabSize - (column % _tabSize); - column += nspaces; - c = ' '; - break; - - default: - ++column; - break; - } - return c; - } - - void popFront() - { - if (!index) - front; - if (nspaces) - --nspaces; - if (!nspaces) - { - static if (isSomeString!(Range)) - _input = _input[1 .. $]; - else - _input.popFront(); - --index; - } - } - - @property typeof(this) save() - { - auto ret = this; - ret._input = _input.save; - return ret; - } - } - - return Result(r, tabSize); -} - -/// -@safe pure unittest -{ - import std.array : array; - - assert(detabber(" \n\tx", 9).array == " \n x"); -} - -/// ditto -auto detabber(Range)(auto ref Range r, size_t tabSize = 8) -if (isConvertibleToString!Range) -{ - return detabber!(StringTypeOf!Range)(r, tabSize); -} - -@safe pure unittest -{ - assert(testAliasedString!detabber( " ab\t asdf ", 8)); -} - -@safe pure unittest -{ - import std.algorithm.comparison : cmp; - import std.conv : to; - import std.exception : assertCTFEable; - - assertCTFEable!( - { - static foreach (S; AliasSeq!(char[], wchar[], dchar[], string, wstring, dstring)) - {{ - S s = to!S("This \tis\t a fofof\tof list"); - assert(cmp(detab(s), "This is a fofof of list") == 0); - - assert(detab(cast(S) null) is null); - assert(detab("").empty); - assert(detab("a") == "a"); - assert(detab("\t") == " "); - assert(detab("\t", 3) == " "); - assert(detab("\t", 9) == " "); - assert(detab( " ab\t asdf ") == " ab asdf "); - assert(detab( " \U00010000b\tasdf ") == " \U00010000b asdf "); - assert(detab("\r\t", 9) == "\r "); - assert(detab("\n\t", 9) == "\n "); - assert(detab("\u0085\t", 9) == "\u0085 "); - assert(detab("\u2028\t", 9) == "\u2028 "); - assert(detab(" \u2029\t", 9) == " \u2029 "); - }} - }); -} - -/// -@safe pure unittest -{ - import std.array : array; - import std.utf : byChar, byWchar; - - assert(detabber(" \u2029\t".byChar, 9).array == " \u2029 "); - auto r = "hel\tx".byWchar.detabber(); - assert(r.front == 'h'); - auto s = r.save; - r.popFront(); - r.popFront(); - assert(r.front == 'l'); - assert(s.front == 'h'); -} - -/++ - Replaces spaces in `s` with the optimal number of tabs. - All spaces and tabs at the end of a line are removed. - - Params: - s = String to convert. - tabSize = Tab columns are `tabSize` spaces apart. - - Returns: - GC allocated string with spaces replaced with tabs; - use $(LREF entabber) to not allocate. - - See_Also: - $(LREF entabber) - +/ -auto entab(Range)(Range s, size_t tabSize = 8) -if (isForwardRange!Range && isSomeChar!(ElementEncodingType!Range)) -{ - import std.array : array; - return entabber(s, tabSize).array; -} - -/// -@safe pure unittest -{ - assert(entab(" x \n") == "\tx\n"); -} - -auto entab(Range)(auto ref Range s, size_t tabSize = 8) -if (!(isForwardRange!Range && isSomeChar!(ElementEncodingType!Range)) && - is(StringTypeOf!Range)) -{ - return entab!(StringTypeOf!Range)(s, tabSize); -} - -@safe pure unittest -{ - assert(testAliasedString!entab(" x \n")); -} - -/++ - Replaces spaces in range `r` with the optimal number of tabs. - All spaces and tabs at the end of a line are removed. - - Params: - r = string or $(REF_ALTTEXT forward range, isForwardRange, std,range,primitives) - tabSize = distance between tab stops - - Returns: - lazy forward range with spaces replaced with tabs - - See_Also: - $(LREF entab) - +/ -auto entabber(Range)(Range r, size_t tabSize = 8) -if (isForwardRange!Range && !isConvertibleToString!Range) -{ - import std.uni : lineSep, paraSep, nelSep; - import std.utf : codeUnitLimit, decodeFront; - - assert(tabSize > 0, "tabSize must be greater than 0"); - alias C = Unqual!(ElementEncodingType!Range); - - static struct Result - { - private: - Range _input; - size_t _tabSize; - size_t nspaces; - size_t ntabs; - int column; - size_t index; - - @property C getFront() - { - static if (isSomeString!Range) - return _input[0]; // avoid autodecode - else - return _input.front; - } - - public: - - this(Range input, size_t tabSize) - { - _input = input; - _tabSize = tabSize; - } - - @property bool empty() - { - if (ntabs || nspaces) - return false; - - /* Since trailing spaces are removed, - * look ahead for anything that is not a trailing space - */ - static if (isSomeString!Range) - { - foreach (c; _input) - { - if (c != ' ' && c != '\t') - return false; - } - return true; - } - else - { - if (_input.empty) - return true; - immutable c = _input.front; - if (c != ' ' && c != '\t') - return false; - auto t = _input.save; - t.popFront(); - foreach (c2; t) - { - if (c2 != ' ' && c2 != '\t') - return false; - } - return true; - } - } - - @property C front() - { - //writefln(" front(): ntabs = %s nspaces = %s index = %s front = '%s'", ntabs, nspaces, index, getFront); - if (ntabs) - return '\t'; - if (nspaces) - return ' '; - C c = getFront; - if (index) - return c; - dchar dc; - if (c < codeUnitLimit!(immutable(C)[])) - { - index = 1; - dc = c; - if (c == ' ' || c == '\t') - { - // Consume input until a non-blank is encountered - immutable startcol = column; - C cx; - static if (isSomeString!Range) - { - while (1) - { - assert(_input.length, "input did not contain non " - ~ "whitespace character"); - cx = _input[0]; - if (cx == ' ') - ++column; - else if (cx == '\t') - column += _tabSize - (column % _tabSize); - else - break; - _input = _input[1 .. $]; - } - } - else - { - while (1) - { - assert(_input.length, "input did not contain non " - ~ "whitespace character"); - cx = _input.front; - if (cx == ' ') - ++column; - else if (cx == '\t') - column += _tabSize - (column % _tabSize); - else - break; - _input.popFront(); - } - } - // Compute ntabs+nspaces to get from startcol to column - immutable n = column - startcol; - if (n == 1) - { - nspaces = 1; - } - else - { - ntabs = column / _tabSize - startcol / _tabSize; - if (ntabs == 0) - nspaces = column - startcol; - else - nspaces = column % _tabSize; - } - //writefln("\tstartcol = %s, column = %s, _tabSize = %s", startcol, column, _tabSize); - //writefln("\tntabs = %s, nspaces = %s", ntabs, nspaces); - if (cx < codeUnitLimit!(immutable(C)[])) - { - dc = cx; - index = 1; - } - else - { - auto r = _input.save; - dc = decodeFront(r, index); // lookahead to decode - } - switch (dc) - { - case '\r': - case '\n': - case paraSep: - case lineSep: - case nelSep: - column = 0; - // Spaces followed by newline are ignored - ntabs = 0; - nspaces = 0; - return cx; - - default: - ++column; - break; - } - return ntabs ? '\t' : ' '; - } - } - else - { - auto r = _input.save; - dc = decodeFront(r, index); // lookahead to decode - } - //writefln("dc = x%x", dc); - switch (dc) - { - case '\r': - case '\n': - case paraSep: - case lineSep: - case nelSep: - column = 0; - break; - - default: - ++column; - break; - } - return c; - } - - void popFront() - { - //writefln("popFront(): ntabs = %s nspaces = %s index = %s front = '%s'", ntabs, nspaces, index, getFront); - if (!index) - front; - if (ntabs) - --ntabs; - else if (nspaces) - --nspaces; - else if (!ntabs && !nspaces) - { - static if (isSomeString!Range) - _input = _input[1 .. $]; - else - _input.popFront(); - --index; - } - } - - @property typeof(this) save() - { - auto ret = this; - ret._input = _input.save; - return ret; - } - } - - return Result(r, tabSize); -} - -/// -@safe pure unittest -{ - import std.array : array; - assert(entabber(" x \n").array == "\tx\n"); -} - -auto entabber(Range)(auto ref Range r, size_t tabSize = 8) -if (isConvertibleToString!Range) -{ - return entabber!(StringTypeOf!Range)(r, tabSize); -} - -@safe pure unittest -{ - assert(testAliasedString!entabber(" ab asdf ", 8)); -} - -@safe pure -unittest -{ - import std.conv : to; - import std.exception : assertCTFEable; - - assertCTFEable!( - { - assert(entab(cast(string) null) is null); - assert(entab("").empty); - assert(entab("a") == "a"); - assert(entab(" ") == ""); - assert(entab(" x") == "\tx"); - assert(entab(" ab asdf ") == " ab\tasdf"); - assert(entab(" ab asdf ") == " ab\t asdf"); - assert(entab(" ab \t asdf ") == " ab\t asdf"); - assert(entab("1234567 \ta") == "1234567\t\ta"); - assert(entab("1234567 \ta") == "1234567\t\ta"); - assert(entab("1234567 \ta") == "1234567\t\ta"); - assert(entab("1234567 \ta") == "1234567\t\ta"); - assert(entab("1234567 \ta") == "1234567\t\ta"); - assert(entab("1234567 \ta") == "1234567\t\ta"); - assert(entab("1234567 \ta") == "1234567\t\ta"); - assert(entab("1234567 \ta") == "1234567\t\ta"); - assert(entab("1234567 \ta") == "1234567\t\t\ta"); - - assert(entab("a ") == "a"); - assert(entab("a\v") == "a\v"); - assert(entab("a\f") == "a\f"); - assert(entab("a\n") == "a\n"); - assert(entab("a\n\r") == "a\n\r"); - assert(entab("a\r\n") == "a\r\n"); - assert(entab("a\u2028") == "a\u2028"); - assert(entab("a\u2029") == "a\u2029"); - assert(entab("a\u0085") == "a\u0085"); - assert(entab("a ") == "a"); - assert(entab("a\t") == "a"); - assert(entab("\uFF28\uFF45\uFF4C\uFF4C567 \t\uFF4F \t") == - "\uFF28\uFF45\uFF4C\uFF4C567\t\t\uFF4F"); - assert(entab(" \naa") == "\naa"); - assert(entab(" \r aa") == "\r aa"); - assert(entab(" \u2028 aa") == "\u2028 aa"); - assert(entab(" \u2029 aa") == "\u2029 aa"); - assert(entab(" \u0085 aa") == "\u0085 aa"); - }); -} - -@safe pure -unittest -{ - import std.array : array; - import std.utf : byChar; - assert(entabber(" \u0085 aa".byChar).array == "\u0085 aa"); - assert(entabber(" \u2028\t aa \t".byChar).array == "\u2028\t aa"); - - auto r = entabber("1234", 4); - r.popFront(); - auto rsave = r.save; - r.popFront(); - assert(r.front == '3'); - assert(rsave.front == '2'); -} - - -/++ - Replaces the characters in `str` which are keys in `transTable` with - their corresponding values in `transTable`. `transTable` is an AA - where its keys are `dchar` and its values are either `dchar` or some - type of string. Also, if `toRemove` is given, the characters in it are - removed from `str` prior to translation. `str` itself is unaltered. - A copy with the changes is returned. - - See_Also: - $(LREF tr), - $(REF replace, std,array), - $(REF substitute, std,algorithm,iteration) - - Params: - str = The original string. - transTable = The AA indicating which characters to replace and what to - replace them with. - toRemove = The characters to remove from the string. - +/ -C1[] translate(C1, C2 = immutable char)(C1[] str, - in dchar[dchar] transTable, - const(C2)[] toRemove = null) @safe pure -if (isSomeChar!C1 && isSomeChar!C2) -{ - import std.array : appender; - auto buffer = appender!(C1[])(); - translateImpl(str, transTable, toRemove, buffer); - return buffer.data; -} - -/// -@safe pure unittest -{ - dchar[dchar] transTable1 = ['e' : '5', 'o' : '7', '5': 'q']; - assert(translate("hello world", transTable1) == "h5ll7 w7rld"); - - assert(translate("hello world", transTable1, "low") == "h5 rd"); - - string[dchar] transTable2 = ['e' : "5", 'o' : "orange"]; - assert(translate("hello world", transTable2) == "h5llorange worangerld"); -} - -// https://issues.dlang.org/show_bug.cgi?id=13018 -@safe pure unittest -{ - immutable dchar[dchar] transTable1 = ['e' : '5', 'o' : '7', '5': 'q']; - assert(translate("hello world", transTable1) == "h5ll7 w7rld"); - - assert(translate("hello world", transTable1, "low") == "h5 rd"); - - immutable string[dchar] transTable2 = ['e' : "5", 'o' : "orange"]; - assert(translate("hello world", transTable2) == "h5llorange worangerld"); -} - -@system pure unittest -{ - import std.conv : to; - import std.exception : assertCTFEable; - - assertCTFEable!( - { - static foreach (S; AliasSeq!( char[], const( char)[], immutable( char)[], - wchar[], const(wchar)[], immutable(wchar)[], - dchar[], const(dchar)[], immutable(dchar)[])) - {(){ // workaround slow optimizations for large functions - // https://issues.dlang.org/show_bug.cgi?id=2396 - assert(translate(to!S("hello world"), cast(dchar[dchar])['h' : 'q', 'l' : '5']) == - to!S("qe55o wor5d")); - assert(translate(to!S("hello world"), cast(dchar[dchar])['o' : 'l', 'l' : '\U00010143']) == - to!S("he\U00010143\U00010143l wlr\U00010143d")); - assert(translate(to!S("hello \U00010143 world"), cast(dchar[dchar])['h' : 'q', 'l': '5']) == - to!S("qe55o \U00010143 wor5d")); - assert(translate(to!S("hello \U00010143 world"), cast(dchar[dchar])['o' : '0', '\U00010143' : 'o']) == - to!S("hell0 o w0rld")); - assert(translate(to!S("hello world"), cast(dchar[dchar]) null) == to!S("hello world")); - - static foreach (T; AliasSeq!( char[], const( char)[], immutable( char)[], - wchar[], const(wchar)[], immutable(wchar)[], - dchar[], const(dchar)[], immutable(dchar)[])) - (){ // workaround slow optimizations for large functions - // https://issues.dlang.org/show_bug.cgi?id=2396 - static foreach (R; AliasSeq!(dchar[dchar], const dchar[dchar], - immutable dchar[dchar])) - {{ - R tt = ['h' : 'q', 'l' : '5']; - assert(translate(to!S("hello world"), tt, to!T("r")) - == to!S("qe55o wo5d")); - assert(translate(to!S("hello world"), tt, to!T("helo")) - == to!S(" wrd")); - assert(translate(to!S("hello world"), tt, to!T("q5")) - == to!S("qe55o wor5d")); - }} - }(); - - auto s = to!S("hello world"); - dchar[dchar] transTable = ['h' : 'q', 'l' : '5']; - static assert(is(typeof(s) == typeof(translate(s, transTable)))); - assert(translate(s, transTable) == "qe55o wor5d"); - }();} - }); -} - -/++ Ditto +/ -C1[] translate(C1, S, C2 = immutable char)(C1[] str, - in S[dchar] transTable, - const(C2)[] toRemove = null) @safe pure -if (isSomeChar!C1 && isSomeString!S && isSomeChar!C2) -{ - import std.array : appender; - auto buffer = appender!(C1[])(); - translateImpl(str, transTable, toRemove, buffer); - return buffer.data; -} - -@system pure unittest -{ - import std.conv : to; - import std.exception : assertCTFEable; - - assertCTFEable!( - { - static foreach (S; AliasSeq!( char[], const( char)[], immutable( char)[], - wchar[], const(wchar)[], immutable(wchar)[], - dchar[], const(dchar)[], immutable(dchar)[])) - {(){ // workaround slow optimizations for large functions - // https://issues.dlang.org/show_bug.cgi?id=2396 - assert(translate(to!S("hello world"), ['h' : "yellow", 'l' : "42"]) == - to!S("yellowe4242o wor42d")); - assert(translate(to!S("hello world"), ['o' : "owl", 'l' : "\U00010143\U00010143"]) == - to!S("he\U00010143\U00010143\U00010143\U00010143owl wowlr\U00010143\U00010143d")); - assert(translate(to!S("hello \U00010143 world"), ['h' : "yellow", 'l' : "42"]) == - to!S("yellowe4242o \U00010143 wor42d")); - assert(translate(to!S("hello \U00010143 world"), ['o' : "owl", 'l' : "\U00010143\U00010143"]) == - to!S("he\U00010143\U00010143\U00010143\U00010143owl \U00010143 wowlr\U00010143\U00010143d")); - assert(translate(to!S("hello \U00010143 world"), ['h' : ""]) == - to!S("ello \U00010143 world")); - assert(translate(to!S("hello \U00010143 world"), ['\U00010143' : ""]) == - to!S("hello world")); - assert(translate(to!S("hello world"), cast(string[dchar]) null) == to!S("hello world")); - - static foreach (T; AliasSeq!( char[], const( char)[], immutable( char)[], - wchar[], const(wchar)[], immutable(wchar)[], - dchar[], const(dchar)[], immutable(dchar)[])) - (){ // workaround slow optimizations for large functions - // https://issues.dlang.org/show_bug.cgi?id=2396 - static foreach (R; AliasSeq!(string[dchar], const string[dchar], - immutable string[dchar])) - {{ - R tt = ['h' : "yellow", 'l' : "42"]; - assert(translate(to!S("hello world"), tt, to!T("r")) == - to!S("yellowe4242o wo42d")); - assert(translate(to!S("hello world"), tt, to!T("helo")) == - to!S(" wrd")); - assert(translate(to!S("hello world"), tt, to!T("y42")) == - to!S("yellowe4242o wor42d")); - assert(translate(to!S("hello world"), tt, to!T("hello world")) == - to!S("")); - assert(translate(to!S("hello world"), tt, to!T("42")) == - to!S("yellowe4242o wor42d")); - }} - }(); - - auto s = to!S("hello world"); - string[dchar] transTable = ['h' : "silly", 'l' : "putty"]; - static assert(is(typeof(s) == typeof(translate(s, transTable)))); - assert(translate(s, transTable) == "sillyeputtyputtyo worputtyd"); - }();} - }); -} - -/++ - This is an overload of `translate` which takes an existing buffer to write the contents to. - - Params: - str = The original string. - transTable = The AA indicating which characters to replace and what to - replace them with. - toRemove = The characters to remove from the string. - buffer = An output range to write the contents to. - +/ -void translate(C1, C2 = immutable char, Buffer)(const(C1)[] str, - in dchar[dchar] transTable, - const(C2)[] toRemove, - Buffer buffer) -if (isSomeChar!C1 && isSomeChar!C2 && isOutputRange!(Buffer, C1)) -{ - translateImpl(str, transTable, toRemove, buffer); -} - -/// -@safe pure unittest -{ - import std.array : appender; - dchar[dchar] transTable1 = ['e' : '5', 'o' : '7', '5': 'q']; - auto buffer = appender!(dchar[])(); - translate("hello world", transTable1, null, buffer); - assert(buffer.data == "h5ll7 w7rld"); - - buffer.clear(); - translate("hello world", transTable1, "low", buffer); - assert(buffer.data == "h5 rd"); - - buffer.clear(); - string[dchar] transTable2 = ['e' : "5", 'o' : "orange"]; - translate("hello world", transTable2, null, buffer); - assert(buffer.data == "h5llorange worangerld"); -} - -// https://issues.dlang.org/show_bug.cgi?id=13018 -@safe pure unittest -{ - import std.array : appender; - immutable dchar[dchar] transTable1 = ['e' : '5', 'o' : '7', '5': 'q']; - auto buffer = appender!(dchar[])(); - translate("hello world", transTable1, null, buffer); - assert(buffer.data == "h5ll7 w7rld"); - - buffer.clear(); - translate("hello world", transTable1, "low", buffer); - assert(buffer.data == "h5 rd"); - - buffer.clear(); - immutable string[dchar] transTable2 = ['e' : "5", 'o' : "orange"]; - translate("hello world", transTable2, null, buffer); - assert(buffer.data == "h5llorange worangerld"); -} - -/++ Ditto +/ -void translate(C1, S, C2 = immutable char, Buffer)(C1[] str, - in S[dchar] transTable, - const(C2)[] toRemove, - Buffer buffer) -if (isSomeChar!C1 && isSomeString!S && isSomeChar!C2 && isOutputRange!(Buffer, S)) -{ - translateImpl(str, transTable, toRemove, buffer); -} - -private void translateImpl(C1, T, C2, Buffer)(const(C1)[] str, - scope T transTable, - const(C2)[] toRemove, - Buffer buffer) -{ - bool[dchar] removeTable; - - foreach (dchar c; toRemove) - removeTable[c] = true; - - foreach (dchar c; str) - { - if (c in removeTable) - continue; - - auto newC = c in transTable; - - if (newC) - put(buffer, *newC); - else - put(buffer, c); - } -} - -/++ - This is an $(I $(RED ASCII-only)) overload of $(LREF _translate). It - will $(I not) work with Unicode. It exists as an optimization for the - cases where Unicode processing is not necessary. - - Unlike the other overloads of $(LREF _translate), this one does not take - an AA. Rather, it takes a `string` generated by $(LREF makeTransTable). - - The array generated by `makeTransTable` is `256` elements long such that - the index is equal to the ASCII character being replaced and the value is - equal to the character that it's being replaced with. Note that translate - does not decode any of the characters, so you can actually pass it Extended - ASCII characters if you want to (ASCII only actually uses `128` - characters), but be warned that Extended ASCII characters are not valid - Unicode and therefore will result in a `UTFException` being thrown from - most other Phobos functions. - - Also, because no decoding occurs, it is possible to use this overload to - translate ASCII characters within a proper UTF-8 string without altering the - other, non-ASCII characters. It's replacing any code unit greater than - `127` with another code unit or replacing any code unit with another code - unit greater than `127` which will cause UTF validation issues. - - See_Also: - $(LREF tr), - $(REF replace, std,array), - $(REF substitute, std,algorithm,iteration) - - Params: - str = The original string. - transTable = The string indicating which characters to replace and what - to replace them with. It is generated by $(LREF makeTransTable). - toRemove = The characters to remove from the string. - +/ -C[] translate(C = immutable char)(scope const(char)[] str, scope const(char)[] transTable, - scope const(char)[] toRemove = null) @trusted pure nothrow -if (is(immutable C == immutable char)) -in -{ - import std.conv : to; - assert(transTable.length == 256, "transTable had invalid length of " ~ - to!string(transTable.length)); -} -do -{ - bool[256] remTable = false; - - foreach (char c; toRemove) - remTable[c] = true; - - size_t count = 0; - foreach (char c; str) - { - if (!remTable[c]) - ++count; - } - - auto buffer = new char[count]; - - size_t i = 0; - foreach (char c; str) - { - if (!remTable[c]) - buffer[i++] = transTable[c]; - } - - return cast(C[])(buffer); -} - -/// -@safe pure nothrow unittest -{ - auto transTable1 = makeTrans("eo5", "57q"); - assert(translate("hello world", transTable1) == "h5ll7 w7rld"); - - assert(translate("hello world", transTable1, "low") == "h5 rd"); -} - -/** - * Do same thing as $(LREF makeTransTable) but allocate the translation table - * on the GC heap. - * - * Use $(LREF makeTransTable) instead. - */ -string makeTrans(scope const(char)[] from, scope const(char)[] to) @trusted pure nothrow -{ - return makeTransTable(from, to)[].idup; -} - -/// -@safe pure nothrow unittest -{ - auto transTable1 = makeTrans("eo5", "57q"); - assert(translate("hello world", transTable1) == "h5ll7 w7rld"); - - assert(translate("hello world", transTable1, "low") == "h5 rd"); -} - -/******* - * Construct 256 character translation table, where characters in from[] are replaced - * by corresponding characters in to[]. - * - * Params: - * from = array of chars, less than or equal to 256 in length - * to = corresponding array of chars to translate to - * Returns: - * translation array - */ -char[256] makeTransTable(scope const(char)[] from, scope const(char)[] to) @safe pure nothrow @nogc -in -{ - import std.ascii : isASCII; - assert(from.length == to.length, "from.length must match to.length"); - assert(from.length <= 256, "from.length must be <= 256"); - foreach (char c; from) - assert(isASCII(c), - "all characters in from must be valid ascii character"); - foreach (char c; to) - assert(isASCII(c), - "all characters in to must be valid ascii character"); -} -do -{ - char[256] result = void; - - foreach (i; 0 .. result.length) - result[i] = cast(char) i; - foreach (i, c; from) - result[c] = to[i]; - return result; -} - -/// -@safe pure unittest -{ - assert(translate("hello world", makeTransTable("hl", "q5")) == "qe55o wor5d"); - assert(translate("hello world", makeTransTable("12345", "67890")) == "hello world"); -} - -@safe pure unittest -{ - import std.conv : to; - import std.exception : assertCTFEable; - - assertCTFEable!( - { - static foreach (C; AliasSeq!(char, const char, immutable char)) - {{ - assert(translate!C("hello world", makeTransTable("hl", "q5")) == to!(C[])("qe55o wor5d")); - - auto s = to!(C[])("hello world"); - auto transTable = makeTransTable("hl", "q5"); - static assert(is(typeof(s) == typeof(translate!C(s, transTable)))); - assert(translate(s, transTable) == "qe55o wor5d"); - }} - - static foreach (S; AliasSeq!(char[], const(char)[], immutable(char)[])) - { - assert(translate(to!S("hello world"), makeTransTable("hl", "q5")) == to!S("qe55o wor5d")); - assert(translate(to!S("hello \U00010143 world"), makeTransTable("hl", "q5")) == - to!S("qe55o \U00010143 wor5d")); - assert(translate(to!S("hello world"), makeTransTable("ol", "1o")) == to!S("heoo1 w1rod")); - assert(translate(to!S("hello world"), makeTransTable("", "")) == to!S("hello world")); - assert(translate(to!S("hello world"), makeTransTable("12345", "67890")) == to!S("hello world")); - assert(translate(to!S("hello \U00010143 world"), makeTransTable("12345", "67890")) == - to!S("hello \U00010143 world")); - - static foreach (T; AliasSeq!(char[], const(char)[], immutable(char)[])) - { - assert(translate(to!S("hello world"), makeTransTable("hl", "q5"), to!T("r")) == - to!S("qe55o wo5d")); - assert(translate(to!S("hello \U00010143 world"), makeTransTable("hl", "q5"), to!T("r")) == - to!S("qe55o \U00010143 wo5d")); - assert(translate(to!S("hello world"), makeTransTable("hl", "q5"), to!T("helo")) == - to!S(" wrd")); - assert(translate(to!S("hello world"), makeTransTable("hl", "q5"), to!T("q5")) == - to!S("qe55o wor5d")); - } - } - }); -} - -/++ - This is an $(I $(RED ASCII-only)) overload of `translate` which takes an existing buffer to write the contents to. - - Params: - str = The original string. - transTable = The string indicating which characters to replace and what - to replace them with. It is generated by $(LREF makeTransTable). - toRemove = The characters to remove from the string. - buffer = An output range to write the contents to. - +/ -void translate(C = immutable char, Buffer)(scope const(char)[] str, scope const(char)[] transTable, - scope const(char)[] toRemove, Buffer buffer) @trusted pure -if (is(immutable C == immutable char) && isOutputRange!(Buffer, char)) -in -{ - assert(transTable.length == 256, format! - "transTable.length %s must equal 256"(transTable.length)); -} -do -{ - bool[256] remTable = false; - - foreach (char c; toRemove) - remTable[c] = true; - - foreach (char c; str) - { - if (!remTable[c]) - put(buffer, transTable[c]); - } -} - -/// -@safe pure unittest -{ - import std.array : appender; - auto buffer = appender!(char[])(); - auto transTable1 = makeTransTable("eo5", "57q"); - translate("hello world", transTable1, null, buffer); - assert(buffer.data == "h5ll7 w7rld"); - - buffer.clear(); - translate("hello world", transTable1, "low", buffer); - assert(buffer.data == "h5 rd"); -} - -/********************************************** - * Return string that is the 'successor' to s[]. - * If the rightmost character is a-zA-Z0-9, it is incremented within - * its case or digits. If it generates a carry, the process is - * repeated with the one to its immediate left. - */ - -S succ(S)(S s) @safe pure -if (isSomeString!S) -{ - import std.ascii : isAlphaNum; - - if (s.length && isAlphaNum(s[$ - 1])) - { - auto r = s.dup; - size_t i = r.length - 1; - - while (1) - { - dchar c = s[i]; - dchar carry; - - switch (c) - { - case '9': - c = '0'; - carry = '1'; - goto Lcarry; - case 'z': - case 'Z': - c -= 'Z' - 'A'; - carry = c; - Lcarry: - r[i] = cast(char) c; - if (i == 0) - { - auto t = new typeof(r[0])[r.length + 1]; - t[0] = cast(char) carry; - t[1 .. $] = r[]; - return t; - } - i--; - break; - - default: - if (isAlphaNum(c)) - r[i]++; - return r; - } - } - } - return s; -} - -/// -@safe pure unittest -{ - assert(succ("1") == "2"); - assert(succ("9") == "10"); - assert(succ("999") == "1000"); - assert(succ("zz99") == "aaa00"); -} - -@safe pure unittest -{ - import std.conv : to; - import std.exception : assertCTFEable; - - assertCTFEable!( - { - assert(succ(string.init) is null); - assert(succ("!@#$%") == "!@#$%"); - assert(succ("1") == "2"); - assert(succ("9") == "10"); - assert(succ("999") == "1000"); - assert(succ("zz99") == "aaa00"); - }); -} - - -/++ - Replaces the characters in `str` which are in `from` with the - the corresponding characters in `to` and returns the resulting string. - - `tr` is based on - $(HTTP pubs.opengroup.org/onlinepubs/9699919799/utilities/_tr.html, Posix's tr), - though it doesn't do everything that the Posix utility does. - - Params: - str = The original string. - from = The characters to replace. - to = The characters to replace with. - modifiers = String containing modifiers. - - Modifiers: - $(BOOKTABLE, - $(TR $(TD Modifier) $(TD Description)) - $(TR $(TD `'c'`) $(TD Complement the list of characters in `from`)) - $(TR $(TD `'d'`) $(TD Removes matching characters with no corresponding - replacement in `to`)) - $(TR $(TD `'s'`) $(TD Removes adjacent duplicates in the replaced - characters)) - ) - - If the modifier `'d'` is present, then the number of characters in - `to` may be only `0` or `1`. - - If the modifier `'d'` is $(I not) present, and `to` is empty, then - `to` is taken to be the same as `from`. - - If the modifier `'d'` is $(I not) present, and `to` is shorter than - `from`, then `to` is extended by replicating the last character in - `to`. - - Both `from` and `to` may contain ranges using the `'-'` character - (e.g. `"a-d"` is synonymous with `"abcd"`.) Neither accept a leading - `'^'` as meaning the complement of the string (use the `'c'` modifier - for that). - - See_Also: - $(LREF translate), - $(REF replace, std,array), - $(REF substitute, std,algorithm,iteration) - +/ -C1[] tr(C1, C2, C3, C4 = immutable char) - (C1[] str, const(C2)[] from, const(C3)[] to, const(C4)[] modifiers = null) -{ - import std.array : appender; - import std.conv : conv_to = to; - import std.utf : decode; - - bool mod_c; - bool mod_d; - bool mod_s; - - foreach (char c; modifiers) - { - switch (c) - { - case 'c': mod_c = 1; break; // complement - case 'd': mod_d = 1; break; // delete unreplaced chars - case 's': mod_s = 1; break; // squeeze duplicated replaced chars - default: assert(false, "modifier must be one of ['c', 'd', 's'] not " - ~ c); - } - } - - if (to.empty && !mod_d) - to = conv_to!(typeof(to))(from); - - auto result = appender!(C1[])(); - bool modified; - dchar lastc; - - foreach (dchar c; str) - { - dchar lastf; - dchar lastt; - dchar newc; - int n = 0; - - for (size_t i = 0; i < from.length; ) - { - immutable f = decode(from, i); - if (f == '-' && lastf != dchar.init && i < from.length) - { - immutable nextf = decode(from, i); - if (lastf <= c && c <= nextf) - { - n += c - lastf - 1; - if (mod_c) - goto Lnotfound; - goto Lfound; - } - n += nextf - lastf; - lastf = lastf.init; - continue; - } - - if (c == f) - { if (mod_c) - goto Lnotfound; - goto Lfound; - } - lastf = f; - n++; - } - if (!mod_c) - goto Lnotfound; - n = 0; // consider it 'found' at position 0 - - Lfound: - - // Find the nth character in to[] - dchar nextt; - for (size_t i = 0; i < to.length; ) - { - immutable t = decode(to, i); - if (t == '-' && lastt != dchar.init && i < to.length) - { - nextt = decode(to, i); - n -= nextt - lastt; - if (n < 0) - { - newc = nextt + n + 1; - goto Lnewc; - } - lastt = dchar.init; - continue; - } - if (n == 0) - { newc = t; - goto Lnewc; - } - lastt = t; - nextt = t; - n--; - } - if (mod_d) - continue; - newc = nextt; - - Lnewc: - if (mod_s && modified && newc == lastc) - continue; - result.put(newc); - assert(newc != dchar.init, "character must not be dchar.init"); - modified = true; - lastc = newc; - continue; - - Lnotfound: - result.put(c); - lastc = c; - modified = false; - } - - return result.data; -} - -/// -@safe pure unittest -{ - assert(tr("abcdef", "cd", "CD") == "abCDef"); - assert(tr("1st March, 2018", "March", "MAR", "s") == "1st MAR, 2018"); - assert(tr("abcdef", "ef", "", "d") == "abcd"); - assert(tr("14-Jul-87", "a-zA-Z", " ", "cs") == " Jul "); -} - -@safe pure unittest -{ - import std.algorithm.comparison : equal; - import std.conv : to; - import std.exception : assertCTFEable; - - // Complete list of test types; too slow to test'em all - // alias TestTypes = AliasSeq!( - // char[], const( char)[], immutable( char)[], - // wchar[], const(wchar)[], immutable(wchar)[], - // dchar[], const(dchar)[], immutable(dchar)[]); - - // Reduced list of test types - alias TestTypes = AliasSeq!(char[], const(wchar)[], immutable(dchar)[]); - - assertCTFEable!( - { - foreach (S; TestTypes) - { - foreach (T; TestTypes) - { - foreach (U; TestTypes) - { - assert(equal(tr(to!S("abcdef"), to!T("cd"), to!U("CD")), "abCDef")); - assert(equal(tr(to!S("abcdef"), to!T("b-d"), to!U("B-D")), "aBCDef")); - assert(equal(tr(to!S("abcdefgh"), to!T("b-dh"), to!U("B-Dx")), "aBCDefgx")); - assert(equal(tr(to!S("abcdefgh"), to!T("b-dh"), to!U("B-CDx")), "aBCDefgx")); - assert(equal(tr(to!S("abcdefgh"), to!T("b-dh"), to!U("B-BCDx")), "aBCDefgx")); - assert(equal(tr(to!S("abcdef"), to!T("ef"), to!U("*"), to!S("c")), "****ef")); - assert(equal(tr(to!S("abcdef"), to!T("ef"), to!U(""), to!T("d")), "abcd")); - assert(equal(tr(to!S("hello goodbye"), to!T("lo"), to!U(""), to!U("s")), "helo godbye")); - assert(equal(tr(to!S("hello goodbye"), to!T("lo"), to!U("x"), "s"), "hex gxdbye")); - assert(equal(tr(to!S("14-Jul-87"), to!T("a-zA-Z"), to!U(" "), "cs"), " Jul ")); - assert(equal(tr(to!S("Abc"), to!T("AAA"), to!U("XYZ")), "Xbc")); - } - } - - auto s = to!S("hello world"); - static assert(is(typeof(s) == typeof(tr(s, "he", "if")))); - assert(tr(s, "he", "if") == "ifllo world"); - } - }); -} - -@system pure unittest -{ - import core.exception : AssertError; - import std.exception : assertThrown; - assertThrown!AssertError(tr("abcdef", "cd", "CD", "X")); -} - -/** - * Takes a string `s` and determines if it represents a number. This function - * also takes an optional parameter, `bAllowSep`, which will accept the - * separator characters `','` and `'__'` within the string. But these - * characters should be stripped from the string before using any - * of the conversion functions like `to!int()`, `to!float()`, and etc - * else an error will occur. - * - * Also please note, that no spaces are allowed within the string - * anywhere whether it's a leading, trailing, or embedded space(s), - * thus they too must be stripped from the string before using this - * function, or any of the conversion functions. - * - * Params: - * s = the string or random access range to check - * bAllowSep = accept separator characters or not - * - * Returns: - * `bool` - */ -bool isNumeric(S)(S s, bool bAllowSep = false) -if (isSomeString!S || - (isRandomAccessRange!S && - hasSlicing!S && - isSomeChar!(ElementType!S) && - !isInfinite!S)) -{ - import std.algorithm.comparison : among; - import std.ascii : isASCII; - - // ASCII only case insensitive comparison with two ranges - static bool asciiCmp(S1)(S1 a, string b) - { - import std.algorithm.comparison : equal; - import std.algorithm.iteration : map; - import std.ascii : toLower; - import std.utf : byChar; - return a.map!toLower.equal(b.byChar.map!toLower); - } - - // auto-decoding special case, we're only comparing characters - // in the ASCII range so there's no reason to decode - static if (isSomeString!S) - { - import std.utf : byCodeUnit; - auto codeUnits = s.byCodeUnit; - } - else - { - alias codeUnits = s; - } - - if (codeUnits.empty) - return false; - - // Check for NaN (Not a Number) and for Infinity - if (codeUnits.among!((a, b) => asciiCmp(a.save, b)) - ("nan", "nani", "nan+nani", "inf", "-inf")) - return true; - - immutable frontResult = codeUnits.front; - if (frontResult == '-' || frontResult == '+') - codeUnits.popFront; - - immutable iLen = codeUnits.length; - bool bDecimalPoint, bExponent, bComplex, sawDigits; - - for (size_t i = 0; i < iLen; i++) - { - immutable c = codeUnits[i]; - - if (!c.isASCII) - return false; - - // Digits are good, skip to the next character - if (c >= '0' && c <= '9') - { - sawDigits = true; - continue; - } - - // Check for the complex type, and if found - // reset the flags for checking the 2nd number. - if (c == '+') - { - if (!i) - return false; - bDecimalPoint = false; - bExponent = false; - bComplex = true; - sawDigits = false; - continue; - } - - // Allow only one exponent per number - if (c == 'e' || c == 'E') - { - // A 2nd exponent found, return not a number - if (bExponent || i + 1 >= iLen) - return false; - // Look forward for the sign, and if - // missing then this is not a number. - if (codeUnits[i + 1] != '-' && codeUnits[i + 1] != '+') - return false; - bExponent = true; - i++; - continue; - } - // Allow only one decimal point per number to be used - if (c == '.') - { - // A 2nd decimal point found, return not a number - if (bDecimalPoint) - return false; - bDecimalPoint = true; - continue; - } - // Check for ending literal characters: "f,u,l,i,ul,fi,li", - // and whether they're being used with the correct datatype. - if (i == iLen - 2) - { - if (!sawDigits) - return false; - // Integer Whole Number - if (asciiCmp(codeUnits[i .. iLen], "ul") && - (!bDecimalPoint && !bExponent && !bComplex)) - return true; - // Floating-Point Number - if (codeUnits[i .. iLen].among!((a, b) => asciiCmp(a, b))("fi", "li") && - (bDecimalPoint || bExponent || bComplex)) - return true; - if (asciiCmp(codeUnits[i .. iLen], "ul") && - (bDecimalPoint || bExponent || bComplex)) - return false; - // Could be a Integer or a Float, thus - // all these suffixes are valid for both - return codeUnits[i .. iLen].among!((a, b) => asciiCmp(a, b)) - ("ul", "fi", "li") != 0; - } - if (i == iLen - 1) - { - if (!sawDigits) - return false; - // Integer Whole Number - if (c.among!('u', 'l', 'U', 'L')() && - (!bDecimalPoint && !bExponent && !bComplex)) - return true; - // Check to see if the last character in the string - // is the required 'i' character - if (bComplex) - return c.among!('i', 'I')() != 0; - // Floating-Point Number - return c.among!('l', 'L', 'f', 'F', 'i', 'I')() != 0; - } - // Check if separators are allowed to be in the numeric string - if (!bAllowSep || !c.among!('_', ',')()) - return false; - } - - return sawDigits; -} - -/** - * Integer Whole Number: (byte, ubyte, short, ushort, int, uint, long, and ulong) - * ['+'|'-']digit(s)[U|L|UL] - */ -@safe @nogc pure nothrow unittest -{ - assert(isNumeric("123")); - assert(isNumeric("123UL")); - assert(isNumeric("123L")); - assert(isNumeric("+123U")); - assert(isNumeric("-123L")); -} - -/** - * Floating-Point Number: (float, double, real, ifloat, idouble, and ireal) - * ['+'|'-']digit(s)[.][digit(s)][[e-|e+]digit(s)][i|f|L|Li|fi]] - * or [nan|nani|inf|-inf] - */ -@safe @nogc pure nothrow unittest -{ - assert(isNumeric("+123")); - assert(isNumeric("-123.01")); - assert(isNumeric("123.3e-10f")); - assert(isNumeric("123.3e-10fi")); - assert(isNumeric("123.3e-10L")); - - assert(isNumeric("nan")); - assert(isNumeric("nani")); - assert(isNumeric("-inf")); -} - -/** - * Floating-Point Number: (cfloat, cdouble, and creal) - * ['+'|'-']digit(s)[.][digit(s)][[e-|e+]digit(s)][+] - * [digit(s)[.][digit(s)][[e-|e+]digit(s)][i|f|L|Li|fi]] - * or [nan|nani|nan+nani|inf|-inf] - */ -@safe @nogc pure nothrow unittest -{ - assert(isNumeric("-123e-1+456.9e-10Li")); - assert(isNumeric("+123e+10+456i")); - assert(isNumeric("123+456")); -} - -@safe @nogc pure nothrow unittest -{ - assert(!isNumeric("F")); - assert(!isNumeric("L")); - assert(!isNumeric("U")); - assert(!isNumeric("i")); - assert(!isNumeric("fi")); - assert(!isNumeric("ul")); - assert(!isNumeric("li")); - assert(!isNumeric(".")); - assert(!isNumeric("-")); - assert(!isNumeric("+")); - assert(!isNumeric("e-")); - assert(!isNumeric("e+")); - assert(!isNumeric(".f")); - assert(!isNumeric("e+f")); - assert(!isNumeric("++1")); - assert(!isNumeric("")); - assert(!isNumeric("1E+1E+1")); - assert(!isNumeric("1E1")); - assert(!isNumeric("\x81")); -} - -// Test string types -@safe unittest -{ - import std.conv : to; - - static foreach (T; AliasSeq!(string, char[], wstring, wchar[], dstring, dchar[])) - { - assert("123".to!T.isNumeric()); - assert("123UL".to!T.isNumeric()); - assert("123fi".to!T.isNumeric()); - assert("123li".to!T.isNumeric()); - assert(!"--123L".to!T.isNumeric()); - } -} - -// test ranges -@system pure unittest -{ - import std.range : refRange; - import std.utf : byCodeUnit; - - assert("123".byCodeUnit.isNumeric()); - assert("123UL".byCodeUnit.isNumeric()); - assert("123fi".byCodeUnit.isNumeric()); - assert("123li".byCodeUnit.isNumeric()); - assert(!"--123L".byCodeUnit.isNumeric()); - - dstring z = "0"; - assert(isNumeric(refRange(&z))); - - dstring nani = "nani"; - assert(isNumeric(refRange(&nani))); -} - -/// isNumeric works with CTFE -@safe pure unittest -{ - enum a = isNumeric("123.00E-5+1234.45E-12Li"); - enum b = isNumeric("12345xxxx890"); - - static assert( a); - static assert(!b); -} - -@system unittest -{ - import std.conv : to; - import std.exception : assertCTFEable; - - assertCTFEable!( - { - // Test the isNumeric(in string) function - assert(isNumeric("1") == true ); - assert(isNumeric("1.0") == true ); - assert(isNumeric("1e-1") == true ); - assert(isNumeric("12345xxxx890") == false ); - assert(isNumeric("567L") == true ); - assert(isNumeric("23UL") == true ); - assert(isNumeric("-123..56f") == false ); - assert(isNumeric("12.3.5.6") == false ); - assert(isNumeric(" 12.356") == false ); - assert(isNumeric("123 5.6") == false ); - assert(isNumeric("1233E-1+1.0e-1i") == true ); - - assert(isNumeric("123.00E-5+1234.45E-12Li") == true); - assert(isNumeric("123.00e-5+1234.45E-12iL") == false); - assert(isNumeric("123.00e-5+1234.45e-12uL") == false); - assert(isNumeric("123.00E-5+1234.45e-12lu") == false); - - assert(isNumeric("123fi") == true); - assert(isNumeric("123li") == true); - assert(isNumeric("--123L") == false); - assert(isNumeric("+123.5UL") == false); - assert(isNumeric("123f") == true); - assert(isNumeric("123.u") == false); - - // @@@BUG@@ to!string(float) is not CTFEable. - // Related: formatValue(T) if (is(FloatingPointTypeOf!T)) - if (!__ctfe) - { - assert(isNumeric(to!string(real.nan)) == true); - assert(isNumeric(to!string(-real.infinity)) == true); - } - - string s = "$250.99-"; - assert(isNumeric(s[1 .. s.length - 2]) == true); - assert(isNumeric(s) == false); - assert(isNumeric(s[0 .. s.length - 1]) == false); - }); - - assert(!isNumeric("-")); - assert(!isNumeric("+")); -} - -/***************************** - * Soundex algorithm. - * - * The Soundex algorithm converts a word into 4 characters - * based on how the word sounds phonetically. The idea is that - * two spellings that sound alike will have the same Soundex - * value, which means that Soundex can be used for fuzzy matching - * of names. - * - * Params: - * str = String or InputRange to convert to Soundex representation. - * - * Returns: - * The four character array with the Soundex result in it. - * The array has zero's in it if there is no Soundex representation for the string. - * - * See_Also: - * $(LINK2 http://en.wikipedia.org/wiki/Soundex, Wikipedia), - * $(LUCKY The Soundex Indexing System) - * $(LREF soundex) - * - * Note: - * Only works well with English names. - */ -char[4] soundexer(Range)(Range str) -if (isInputRange!Range && isSomeChar!(ElementEncodingType!Range) && - !isConvertibleToString!Range) -{ - alias C = Unqual!(ElementEncodingType!Range); - - static immutable dex = - // ABCDEFGHIJKLMNOPQRSTUVWXYZ - "01230120022455012623010202"; - - char[4] result = void; - size_t b = 0; - C lastc; - foreach (C c; str) - { - if (c >= 'a' && c <= 'z') - c -= 'a' - 'A'; - else if (c >= 'A' && c <= 'Z') - { - } - else - { - lastc = lastc.init; - continue; - } - if (b == 0) - { - result[0] = cast(char) c; - b++; - lastc = dex[c - 'A']; - } - else - { - if (c == 'H' || c == 'W') - continue; - if (c == 'A' || c == 'E' || c == 'I' || c == 'O' || c == 'U') - lastc = lastc.init; - c = dex[c - 'A']; - if (c != '0' && c != lastc) - { - result[b] = cast(char) c; - b++; - lastc = c; - } - if (b == 4) - goto Lret; - } - } - if (b == 0) - result[] = 0; - else - result[b .. 4] = '0'; - Lret: - return result; -} - -/// ditto -char[4] soundexer(Range)(auto ref Range str) -if (isConvertibleToString!Range) -{ - return soundexer!(StringTypeOf!Range)(str); -} - -/// -@safe unittest -{ - assert(soundexer("Gauss") == "G200"); - assert(soundexer("Ghosh") == "G200"); - - assert(soundexer("Robert") == "R163"); - assert(soundexer("Rupert") == "R163"); - - assert(soundexer("0123^&^^**&^") == ['\0', '\0', '\0', '\0']); -} - -/***************************** - * Like $(LREF soundexer), but with different parameters - * and return value. - * - * Params: - * str = String to convert to Soundex representation. - * buffer = Optional 4 char array to put the resulting Soundex - * characters into. If null, the return value - * buffer will be allocated on the heap. - * Returns: - * The four character array with the Soundex result in it. - * Returns null if there is no Soundex representation for the string. - * See_Also: - * $(LREF soundexer) - */ -char[] soundex(scope const(char)[] str, return scope char[] buffer = null) - @safe pure nothrow -in -{ - assert(buffer is null || buffer.length >= 4); -} -out (result) -{ - if (result !is null) - { - assert(result.length == 4, "Result must have length of 4"); - assert(result[0] >= 'A' && result[0] <= 'Z', "The first character of " - ~ " the result must be an upper character not " ~ result); - foreach (char c; result[1 .. 4]) - assert(c >= '0' && c <= '6', "the last three character of the" - ~ " result must be number between 0 and 6 not " ~ result); - } -} -do -{ - char[4] result = soundexer(str); - if (result[0] == 0) - return null; - if (buffer is null) - buffer = new char[4]; - buffer[] = result[]; - return buffer; -} - -/// -@safe unittest -{ - assert(soundex("Gauss") == "G200"); - assert(soundex("Ghosh") == "G200"); - - assert(soundex("Robert") == "R163"); - assert(soundex("Rupert") == "R163"); - - assert(soundex("0123^&^^**&^") == null); -} - -@safe pure nothrow unittest -{ - import std.exception : assertCTFEable; - assertCTFEable!( - { - char[4] buffer; - - assert(soundex(null) == null); - assert(soundex("") == null); - assert(soundex("0123^&^^**&^") == null); - assert(soundex("Euler") == "E460"); - assert(soundex(" Ellery ") == "E460"); - assert(soundex("Gauss") == "G200"); - assert(soundex("Ghosh") == "G200"); - assert(soundex("Hilbert") == "H416"); - assert(soundex("Heilbronn") == "H416"); - assert(soundex("Knuth") == "K530"); - assert(soundex("Kant", buffer) == "K530"); - assert(soundex("Lloyd") == "L300"); - assert(soundex("Ladd") == "L300"); - assert(soundex("Lukasiewicz", buffer) == "L222"); - assert(soundex("Lissajous") == "L222"); - assert(soundex("Robert") == "R163"); - assert(soundex("Rupert") == "R163"); - assert(soundex("Rubin") == "R150"); - assert(soundex("Washington") == "W252"); - assert(soundex("Lee") == "L000"); - assert(soundex("Gutierrez") == "G362"); - assert(soundex("Pfister") == "P236"); - assert(soundex("Jackson") == "J250"); - assert(soundex("Tymczak") == "T522"); - assert(soundex("Ashcraft") == "A261"); - - assert(soundex("Woo") == "W000"); - assert(soundex("Pilgrim") == "P426"); - assert(soundex("Flingjingwaller") == "F452"); - assert(soundex("PEARSE") == "P620"); - assert(soundex("PIERCE") == "P620"); - assert(soundex("Price") == "P620"); - assert(soundex("CATHY") == "C300"); - assert(soundex("KATHY") == "K300"); - assert(soundex("Jones") == "J520"); - assert(soundex("johnsons") == "J525"); - assert(soundex("Hardin") == "H635"); - assert(soundex("Martinez") == "M635"); - - import std.utf : byChar, byDchar, byWchar; - assert(soundexer("Martinez".byChar ) == "M635"); - assert(soundexer("Martinez".byWchar) == "M635"); - assert(soundexer("Martinez".byDchar) == "M635"); - }); -} - -@safe pure unittest -{ - assert(testAliasedString!soundexer("Martinez")); -} - - -/*************************************************** - * Construct an associative array consisting of all - * abbreviations that uniquely map to the strings in values. - * - * This is useful in cases where the user is expected to type - * in one of a known set of strings, and the program will helpfully - * auto-complete the string once sufficient characters have been - * entered that uniquely identify it. - */ -string[string] abbrev(string[] values) @safe pure -{ - import std.algorithm.sorting : sort; - - string[string] result; - - // Make a copy when sorting so we follow COW principles. - values = values.dup; - sort(values); - - size_t values_length = values.length; - size_t lasti = values_length; - size_t nexti; - - string nv; - string lv; - - for (size_t i = 0; i < values_length; i = nexti) - { - string value = values[i]; - - // Skip dups - for (nexti = i + 1; nexti < values_length; nexti++) - { - nv = values[nexti]; - if (value != values[nexti]) - break; - } - - import std.utf : stride; - - for (size_t j = 0; j < value.length; j += stride(value, j)) - { - string v = value[0 .. j]; - - if ((nexti == values_length || j > nv.length || v != nv[0 .. j]) && - (lasti == values_length || j > lv.length || v != lv[0 .. j])) - { - result[v] = value; - } - } - result[value] = value; - lasti = i; - lv = value; - } - - return result; -} - -/// -@safe unittest -{ - import std.string; - - static string[] list = [ "food", "foxy" ]; - auto abbrevs = abbrev(list); - assert(abbrevs == ["fox": "foxy", "food": "food", - "foxy": "foxy", "foo": "food"]); -} - - -@system pure unittest -{ - import std.algorithm.sorting : sort; - import std.conv : to; - import std.exception : assertCTFEable; - - assertCTFEable!( - { - string[] values; - values ~= "hello"; - values ~= "hello"; - values ~= "he"; - - string[string] r; - - r = abbrev(values); - auto keys = r.keys.dup; - sort(keys); - - assert(keys.length == 4); - assert(keys[0] == "he"); - assert(keys[1] == "hel"); - assert(keys[2] == "hell"); - assert(keys[3] == "hello"); - - assert(r[keys[0]] == "he"); - assert(r[keys[1]] == "hello"); - assert(r[keys[2]] == "hello"); - assert(r[keys[3]] == "hello"); - }); -} - - -/****************************************** - * Compute _column number at the end of the printed form of the string, - * assuming the string starts in the leftmost _column, which is numbered - * starting from 0. - * - * Tab characters are expanded into enough spaces to bring the _column number - * to the next multiple of tabsize. - * If there are multiple lines in the string, the _column number of the last - * line is returned. - * - * Params: - * str = string or InputRange to be analyzed - * tabsize = number of columns a tab character represents - * - * Returns: - * column number - */ - -size_t column(Range)(Range str, in size_t tabsize = 8) -if ((isInputRange!Range && isSomeChar!(ElementEncodingType!Range) || - isNarrowString!Range) && - !isConvertibleToString!Range) -{ - static if (is(immutable ElementEncodingType!Range == immutable char)) - { - // decoding needed for chars - import std.utf : byDchar; - - return str.byDchar.column(tabsize); - } - else - { - // decoding not needed for wchars and dchars - import std.uni : lineSep, paraSep, nelSep; - - size_t column; - - foreach (const c; str) - { - switch (c) - { - case '\t': - column = (column + tabsize) / tabsize * tabsize; - break; - - case '\r': - case '\n': - case paraSep: - case lineSep: - case nelSep: - column = 0; - break; - - default: - column++; - break; - } - } - return column; - } -} - -/// -@safe pure unittest -{ - import std.utf : byChar, byWchar, byDchar; - - assert(column("1234 ") == 5); - assert(column("1234 "w) == 5); - assert(column("1234 "d) == 5); - - assert(column("1234 ".byChar()) == 5); - assert(column("1234 "w.byWchar()) == 5); - assert(column("1234 "d.byDchar()) == 5); - - // Tab stops are set at 8 spaces by default; tab characters insert enough - // spaces to bring the column position to the next multiple of 8. - assert(column("\t") == 8); - assert(column("1\t") == 8); - assert(column("\t1") == 9); - assert(column("123\t") == 8); - - // Other tab widths are possible by specifying it explicitly: - assert(column("\t", 4) == 4); - assert(column("1\t", 4) == 4); - assert(column("\t1", 4) == 5); - assert(column("123\t", 4) == 4); - - // New lines reset the column number. - assert(column("abc\n") == 0); - assert(column("abc\n1") == 1); - assert(column("abcdefg\r1234") == 4); - assert(column("abc\u20281") == 1); - assert(column("abc\u20291") == 1); - assert(column("abc\u00851") == 1); - assert(column("abc\u00861") == 5); -} - -size_t column(Range)(auto ref Range str, in size_t tabsize = 8) -if (isConvertibleToString!Range) -{ - return column!(StringTypeOf!Range)(str, tabsize); -} - -@safe pure unittest -{ - assert(testAliasedString!column("abc\u00861")); -} - -@safe @nogc unittest -{ - import std.conv : to; - import std.exception : assertCTFEable; - - assertCTFEable!( - { - assert(column(string.init) == 0); - assert(column("") == 0); - assert(column("\t") == 8); - assert(column("abc\t") == 8); - assert(column("12345678\t") == 16); - }); -} - -/****************************************** - * Wrap text into a paragraph. - * - * The input text string s is formed into a paragraph - * by breaking it up into a sequence of lines, delineated - * by \n, such that the number of columns is not exceeded - * on each line. - * The last line is terminated with a \n. - * Params: - * s = text string to be wrapped - * columns = maximum number of _columns in the paragraph - * firstindent = string used to _indent first line of the paragraph - * indent = string to use to _indent following lines of the paragraph - * tabsize = column spacing of tabs in firstindent[] and indent[] - * Returns: - * resulting paragraph as an allocated string - */ - -S wrap(S)(S s, in size_t columns = 80, S firstindent = null, -S indent = null, in size_t tabsize = 8) -if (isSomeString!S) -{ - import std.uni : isWhite; - typeof(s.dup) result; - bool inword; - bool first = true; - size_t wordstart; - - const indentcol = column(indent, tabsize); - - result.length = firstindent.length + s.length; - result.length = firstindent.length; - result[] = firstindent[]; - auto col = column(firstindent, tabsize); - foreach (size_t i, dchar c; s) - { - if (isWhite(c)) - { - if (inword) - { - if (first) - { - } - else if (col + 1 + (i - wordstart) > columns) - { - result ~= '\n'; - result ~= indent; - col = indentcol; - } - else - { - result ~= ' '; - col += 1; - } - result ~= s[wordstart .. i]; - col += i - wordstart; - inword = false; - first = false; - } - } - else - { - if (!inword) - { - wordstart = i; - inword = true; - } - } - } - - if (inword) - { - if (col + 1 + (s.length - wordstart) > columns) - { - result ~= '\n'; - result ~= indent; - } - else if (result.length != firstindent.length) - result ~= ' '; - result ~= s[wordstart .. s.length]; - } - result ~= '\n'; - - return result; -} - -/// -@safe pure unittest -{ - assert(wrap("a short string", 7) == "a short\nstring\n"); - - // wrap will not break inside of a word, but at the next space - assert(wrap("a short string", 4) == "a\nshort\nstring\n"); - - assert(wrap("a short string", 7, "\t") == "\ta\nshort\nstring\n"); - assert(wrap("a short string", 7, "\t", " ") == "\ta\n short\n string\n"); -} - -@safe pure unittest -{ - import std.conv : to; - import std.exception : assertCTFEable; - - assertCTFEable!( - { - assert(wrap(string.init) == "\n"); - assert(wrap(" a b df ") == "a b df\n"); - assert(wrap(" a b df ", 3) == "a b\ndf\n"); - assert(wrap(" a bc df ", 3) == "a\nbc\ndf\n"); - assert(wrap(" abcd df ", 3) == "abcd\ndf\n"); - assert(wrap("x") == "x\n"); - assert(wrap("u u") == "u u\n"); - assert(wrap("abcd", 3) == "\nabcd\n"); - assert(wrap("a de", 10, "\t", " ", 8) == "\ta\n de\n"); - }); -} - -@safe pure unittest // https://issues.dlang.org/show_bug.cgi?id=23298 -{ - assert("1 2 3 4 5 6 7 8 9".wrap(17) == "1 2 3 4 5 6 7 8 9\n"); - assert("1 2 3 4 5 6 7 8 9 ".wrap(17) == "1 2 3 4 5 6 7 8 9\n"); - assert("1 2 3 4 5 6 7 8 99".wrap(17) == "1 2 3 4 5 6 7 8\n99\n"); -} - -/****************************************** - * Removes one level of indentation from a multi-line string. - * - * This uniformly outdents the text as much as possible. - * Whitespace-only lines are always converted to blank lines. - * - * Does not allocate memory if it does not throw. - * - * Params: - * str = multi-line string - * - * Returns: - * outdented string - * - * Throws: - * StringException if indentation is done with different sequences - * of whitespace characters. - */ -S outdent(S)(S str) @safe pure -if (isSomeString!S) -{ - return str.splitLines(Yes.keepTerminator).outdent().join(); -} - -/// -@safe pure unittest -{ - enum pretty = q{ - import std.stdio; - void main() { - writeln("Hello"); - } - }.outdent(); - - enum ugly = q{ -import std.stdio; -void main() { - writeln("Hello"); -} -}; - - assert(pretty == ugly); -} - - -/****************************************** - * Removes one level of indentation from an array of single-line strings. - * - * This uniformly outdents the text as much as possible. - * Whitespace-only lines are always converted to blank lines. - * - * Params: - * lines = array of single-line strings - * - * Returns: - * lines[] is rewritten in place with outdented lines - * - * Throws: - * StringException if indentation is done with different sequences - * of whitespace characters. - */ -S[] outdent(S)(return scope S[] lines) @safe pure -if (isSomeString!S) -{ - import std.algorithm.searching : startsWith; - - if (lines.empty) - { - return null; - } - - static S leadingWhiteOf(S str) - { - return str[ 0 .. $ - stripLeft(str).length ]; - } - - S shortestIndent; - foreach (ref line; lines) - { - const stripped = line.stripLeft(); - - if (stripped.empty) - { - line = line[line.chomp().length .. $]; - } - else - { - const indent = leadingWhiteOf(line); - - // Comparing number of code units instead of code points is OK here - // because this function throws upon inconsistent indentation. - if (shortestIndent is null || indent.length < shortestIndent.length) - { - if (indent.empty) - return lines; - shortestIndent = indent; - } - } - } - - foreach (ref line; lines) - { - const stripped = line.stripLeft(); - - if (stripped.empty) - { - // Do nothing - } - else if (line.startsWith(shortestIndent)) - { - line = line[shortestIndent.length .. $]; - } - else - { - throw new StringException("outdent: Inconsistent indentation"); - } - } - - return lines; -} - -/// -@safe pure unittest -{ - auto str1 = [ - " void main()\n", - " {\n", - " test();\n", - " }\n" - ]; - auto str1Expected = [ - "void main()\n", - "{\n", - " test();\n", - "}\n" - ]; - assert(str1.outdent == str1Expected); - - auto str2 = [ - "void main()\n", - " {\n", - " test();\n", - " }\n" - ]; - assert(str2.outdent == str2); -} - -@safe pure unittest -{ - import std.conv : to; - import std.exception : assertCTFEable; - - template outdent_testStr(S) - { - enum S outdent_testStr = -" - \t\tX - \t\U00010143X - \t\t - - \t\t\tX -\t "; - } - - template outdent_expected(S) - { - enum S outdent_expected = -" -\tX -\U00010143X - - -\t\tX -"; - } - - assertCTFEable!( - { - - static foreach (S; AliasSeq!(string, wstring, dstring)) - {{ - enum S blank = ""; - assert(blank.outdent() == blank); - static assert(blank.outdent() == blank); - - enum S testStr1 = " \n \t\n "; - enum S expected1 = "\n\n"; - assert(testStr1.outdent() == expected1); - static assert(testStr1.outdent() == expected1); - - assert(testStr1[0..$-1].outdent() == expected1); - static assert(testStr1[0..$-1].outdent() == expected1); - - enum S testStr2 = "a\n \t\nb"; - assert(testStr2.outdent() == testStr2); - static assert(testStr2.outdent() == testStr2); - - enum S testStr3 = -" - \t\tX - \t\U00010143X - \t\t - - \t\t\tX -\t "; - - enum S expected3 = -" -\tX -\U00010143X - - -\t\tX -"; - assert(testStr3.outdent() == expected3); - static assert(testStr3.outdent() == expected3); - - enum testStr4 = " X\r X\n X\r\n X\u2028 X\u2029 X"; - enum expected4 = "X\rX\nX\r\nX\u2028X\u2029X"; - assert(testStr4.outdent() == expected4); - static assert(testStr4.outdent() == expected4); - - enum testStr5 = testStr4[0..$-1]; - enum expected5 = expected4[0..$-1]; - assert(testStr5.outdent() == expected5); - static assert(testStr5.outdent() == expected5); - - enum testStr6 = " \r \n \r\n \u2028 \u2029"; - enum expected6 = "\r\n\r\n\u2028\u2029"; - assert(testStr6.outdent() == expected6); - static assert(testStr6.outdent() == expected6); - - enum testStr7 = " a \n b "; - enum expected7 = "a \nb "; - assert(testStr7.outdent() == expected7); - static assert(testStr7.outdent() == expected7); - }} - }); -} - -@safe pure unittest -{ - import std.exception : assertThrown; - auto bad = " a\n\tb\n c"; - assertThrown!StringException(bad.outdent); -} - -/** Assume the given array of integers `arr` is a well-formed UTF string and -return it typed as a UTF string. - -`ubyte` becomes `char`, `ushort` becomes `wchar` and `uint` -becomes `dchar`. Type qualifiers are preserved. - -When compiled with debug mode, this function performs an extra check to make -sure the return value is a valid Unicode string. - -Params: - arr = array of bytes, ubytes, shorts, ushorts, ints, or uints - -Returns: - arr retyped as an array of chars, wchars, or dchars - -Throws: - In debug mode `AssertError`, when the result is not a well-formed UTF string. - -See_Also: $(LREF representation) -*/ -auto assumeUTF(T)(T[] arr) -if (staticIndexOf!(immutable T, immutable ubyte, immutable ushort, immutable uint) != -1) -{ - import std.traits : ModifyTypePreservingTQ; - import std.exception : collectException; - import std.utf : validate; - - alias ToUTFType(U) = AliasSeq!(char, wchar, dchar)[U.sizeof / 2]; - auto asUTF = cast(ModifyTypePreservingTQ!(ToUTFType, T)[]) arr; - - debug - { - scope ex = collectException(validate(asUTF)); - assert(!ex, ex.msg); - } - - return asUTF; -} - -/// -@safe pure unittest -{ - string a = "HĂślo World"; - immutable(ubyte)[] b = a.representation; - string c = b.assumeUTF; - - assert(c == "HĂślo World"); -} - -pure @system unittest -{ - import std.algorithm.comparison : equal; - static foreach (T; AliasSeq!(char[], wchar[], dchar[])) - {{ - immutable T jti = "Hello World"; - T jt = jti.dup; - - static if (is(T == char[])) - { - auto gt = cast(ubyte[]) jt; - auto gtc = cast(const(ubyte)[])jt; - auto gti = cast(immutable(ubyte)[])jt; - } - else static if (is(T == wchar[])) - { - auto gt = cast(ushort[]) jt; - auto gtc = cast(const(ushort)[])jt; - auto gti = cast(immutable(ushort)[])jt; - } - else static if (is(T == dchar[])) - { - auto gt = cast(uint[]) jt; - auto gtc = cast(const(uint)[])jt; - auto gti = cast(immutable(uint)[])jt; - } - - auto ht = assumeUTF(gt); - auto htc = assumeUTF(gtc); - auto hti = assumeUTF(gti); - assert(equal(jt, ht)); - assert(equal(jt, htc)); - assert(equal(jt, hti)); - }} -} - -pure @system unittest -{ - import core.exception : AssertError; - import std.exception : assertThrown, assertNotThrown; - - immutable(ubyte)[] a = [ 0xC0 ]; - - debug - assertThrown!AssertError( () nothrow @nogc @safe {cast(void) a.assumeUTF;} () ); - else - assertNotThrown!AssertError( () nothrow @nogc @safe {cast(void) a.assumeUTF;} () ); -} diff --git a/phobos/std/sumtype.d b/phobos/std/sumtype.d deleted file mode 100644 index 80ce73d..0000000 --- a/phobos/std/sumtype.d +++ /dev/null @@ -1,2596 +0,0 @@ -/++ -[SumType] is a generic discriminated union implementation that uses -design-by-introspection to generate safe and efficient code. Its features -include: - -* [Pattern matching.][match] -* Support for self-referential types. -* Full attribute correctness (`pure`, `@safe`, `@nogc`, and `nothrow` are - inferred whenever possible). -* A type-safe and memory-safe API compatible with DIP 1000 (`scope`). -* No dependency on runtime type information (`TypeInfo`). -* Compatibility with BetterC. - -License: Boost License 1.0 -Authors: Paul Backus -Source: $(PHOBOSSRC std/sumtype.d) -+/ -module std.sumtype; - -/// $(DIVID basic-usage,$(H3 Basic usage)) -version (D_BetterC) {} else -@safe unittest -{ - import std.math.operations : isClose; - - struct Fahrenheit { double degrees; } - struct Celsius { double degrees; } - struct Kelvin { double degrees; } - - alias Temperature = SumType!(Fahrenheit, Celsius, Kelvin); - - // Construct from any of the member types. - Temperature t1 = Fahrenheit(98.6); - Temperature t2 = Celsius(100); - Temperature t3 = Kelvin(273); - - // Use pattern matching to access the value. - Fahrenheit toFahrenheit(Temperature t) - { - return Fahrenheit( - t.match!( - (Fahrenheit f) => f.degrees, - (Celsius c) => c.degrees * 9.0/5 + 32, - (Kelvin k) => k.degrees * 9.0/5 - 459.4 - ) - ); - } - - assert(toFahrenheit(t1).degrees.isClose(98.6)); - assert(toFahrenheit(t2).degrees.isClose(212)); - assert(toFahrenheit(t3).degrees.isClose(32)); - - // Use ref to modify the value in place. - void freeze(ref Temperature t) - { - t.match!( - (ref Fahrenheit f) => f.degrees = 32, - (ref Celsius c) => c.degrees = 0, - (ref Kelvin k) => k.degrees = 273 - ); - } - - freeze(t1); - assert(toFahrenheit(t1).degrees.isClose(32)); - - // Use a catch-all handler to give a default result. - bool isFahrenheit(Temperature t) - { - return t.match!( - (Fahrenheit f) => true, - _ => false - ); - } - - assert(isFahrenheit(t1)); - assert(!isFahrenheit(t2)); - assert(!isFahrenheit(t3)); -} - -/** $(DIVID introspection-based-matching, $(H3 Introspection-based matching)) - * - * In the `length` and `horiz` functions below, the handlers for `match` do not - * specify the types of their arguments. Instead, matching is done based on how - * the argument is used in the body of the handler: any type with `x` and `y` - * properties will be matched by the `rect` handlers, and any type with `r` and - * `theta` properties will be matched by the `polar` handlers. - */ -version (D_BetterC) {} else -@safe unittest -{ - import std.math.operations : isClose; - import std.math.trigonometry : cos; - import std.math.constants : PI; - import std.math.algebraic : sqrt; - - struct Rectangular { double x, y; } - struct Polar { double r, theta; } - alias Vector = SumType!(Rectangular, Polar); - - double length(Vector v) - { - return v.match!( - rect => sqrt(rect.x^^2 + rect.y^^2), - polar => polar.r - ); - } - - double horiz(Vector v) - { - return v.match!( - rect => rect.x, - polar => polar.r * cos(polar.theta) - ); - } - - Vector u = Rectangular(1, 1); - Vector v = Polar(1, PI/4); - - assert(length(u).isClose(sqrt(2.0))); - assert(length(v).isClose(1)); - assert(horiz(u).isClose(1)); - assert(horiz(v).isClose(sqrt(0.5))); -} - -/** $(DIVID arithmetic-expression-evaluator, $(H3 Arithmetic expression evaluator)) - * - * This example makes use of the special placeholder type `This` to define a - * [recursive data type](https://en.wikipedia.org/wiki/Recursive_data_type): an - * [abstract syntax tree](https://en.wikipedia.org/wiki/Abstract_syntax_tree) for - * representing simple arithmetic expressions. - */ -version (D_BetterC) {} else -@system unittest -{ - import std.functional : partial; - import std.traits : EnumMembers; - import std.typecons : Tuple; - - enum Op : string - { - Plus = "+", - Minus = "-", - Times = "*", - Div = "/" - } - - // An expression is either - // - a number, - // - a variable, or - // - a binary operation combining two sub-expressions. - alias Expr = SumType!( - double, - string, - Tuple!(Op, "op", This*, "lhs", This*, "rhs") - ); - - // Shorthand for Tuple!(Op, "op", Expr*, "lhs", Expr*, "rhs"), - // the Tuple type above with Expr substituted for This. - alias BinOp = Expr.Types[2]; - - // Factory function for number expressions - Expr* num(double value) - { - return new Expr(value); - } - - // Factory function for variable expressions - Expr* var(string name) - { - return new Expr(name); - } - - // Factory function for binary operation expressions - Expr* binOp(Op op, Expr* lhs, Expr* rhs) - { - return new Expr(BinOp(op, lhs, rhs)); - } - - // Convenience wrappers for creating BinOp expressions - alias sum = partial!(binOp, Op.Plus); - alias diff = partial!(binOp, Op.Minus); - alias prod = partial!(binOp, Op.Times); - alias quot = partial!(binOp, Op.Div); - - // Evaluate expr, looking up variables in env - double eval(Expr expr, double[string] env) - { - return expr.match!( - (double num) => num, - (string var) => env[var], - (BinOp bop) - { - double lhs = eval(*bop.lhs, env); - double rhs = eval(*bop.rhs, env); - final switch (bop.op) - { - static foreach (op; EnumMembers!Op) - { - case op: - return mixin("lhs" ~ op ~ "rhs"); - } - } - } - ); - } - - // Return a "pretty-printed" representation of expr - string pprint(Expr expr) - { - import std.format : format; - - return expr.match!( - (double num) => "%g".format(num), - (string var) => var, - (BinOp bop) => "(%s %s %s)".format( - pprint(*bop.lhs), - cast(string) bop.op, - pprint(*bop.rhs) - ) - ); - } - - Expr* myExpr = sum(var("a"), prod(num(2), var("b"))); - double[string] myEnv = ["a":3, "b":4, "c":7]; - - assert(eval(*myExpr, myEnv) == 11); - assert(pprint(*myExpr) == "(a + (2 * b))"); -} - -import std.format.spec : FormatSpec, singleSpec; -import std.meta : AliasSeq, Filter, IndexOf = staticIndexOf, Map = staticMap; -import std.meta : NoDuplicates; -import std.meta : anySatisfy, allSatisfy; -import std.traits : hasElaborateCopyConstructor, hasElaborateDestructor; -import std.traits : isAssignable, isCopyable, isStaticArray, isRvalueAssignable; -import std.traits : ConstOf, ImmutableOf, InoutOf, TemplateArgsOf; -import std.traits : CommonType, DeducedParameterType; -import std.typecons : ReplaceTypeUnless; -import std.typecons : Flag; -import std.conv : toCtString; - -/// Placeholder used to refer to the enclosing [SumType]. -struct This {} - -// True if a variable of type T can appear on the lhs of an assignment -private enum isAssignableTo(T) = - isAssignable!T || (!isCopyable!T && isRvalueAssignable!T); - -// toHash is required by the language spec to be nothrow and @safe -private enum isHashable(T) = __traits(compiles, - () nothrow @safe { hashOf(T.init); } -); - -private enum hasPostblit(T) = __traits(hasPostblit, T); - -private enum isInout(T) = is(T == inout); - -/** - * A [tagged union](https://en.wikipedia.org/wiki/Tagged_union) that can hold a - * single value from any of a specified set of types. - * - * The value in a `SumType` can be operated on using [pattern matching][match]. - * - * To avoid ambiguity, duplicate types are not allowed (but see the - * ["basic usage" example](#basic-usage) for a workaround). - * - * The special type `This` can be used as a placeholder to create - * self-referential types, just like with `Algebraic`. See the - * ["Arithmetic expression evaluator" example](#arithmetic-expression-evaluator) for - * usage. - * - * A `SumType` is initialized by default to hold the `.init` value of its - * first member type, just like a regular union. The version identifier - * `SumTypeNoDefaultCtor` can be used to disable this behavior. - * - * See_Also: $(REF Algebraic, std,variant) - */ -struct SumType(Types...) -if (is(NoDuplicates!Types == Types) && Types.length > 0) -{ - /// The types a `SumType` can hold. - alias Types = AliasSeq!( - ReplaceTypeUnless!(isSumTypeInstance, This, typeof(this), TemplateArgsOf!SumType) - ); - -private: - - enum bool canHoldTag(T) = Types.length <= T.max; - alias unsignedInts = AliasSeq!(ubyte, ushort, uint, ulong); - - alias Tag = Filter!(canHoldTag, unsignedInts)[0]; - - union Storage - { - // Workaround for https://issues.dlang.org/show_bug.cgi?id=20068 - template memberName(T) - if (IndexOf!(T, Types) >= 0) - { - enum tid = IndexOf!(T, Types); - mixin("enum memberName = `values_", toCtString!tid, "`;"); - } - - static foreach (T; Types) - { - mixin("T ", memberName!T, ";"); - } - } - - Storage storage; - Tag tag; - - /* Accesses the value stored in a SumType. - * - * This method is memory-safe, provided that: - * - * 1. A SumType's tag is always accurate. - * 2. A SumType cannot be assigned to in @safe code if that assignment - * could cause unsafe aliasing. - * - * All code that accesses a SumType's tag or storage directly, including - * @safe code in this module, must be manually checked to ensure that it - * does not violate either of the above requirements. - */ - @trusted - ref inout(T) get(T)() inout - if (IndexOf!(T, Types) >= 0) - { - enum tid = IndexOf!(T, Types); - assert(tag == tid, - "This `" ~ SumType.stringof ~ - "` does not contain a(n) `" ~ T.stringof ~ "`" - ); - return __traits(getMember, storage, Storage.memberName!T); - } - -public: - - // Workaround for https://issues.dlang.org/show_bug.cgi?id=21399 - version (StdDdoc) - { - // Dummy type to stand in for loop variable - private struct T; - - /// Constructs a `SumType` holding a specific value. - this(T value); - - /// ditto - this(const(T) value) const; - - /// ditto - this(immutable(T) value) immutable; - - /// ditto - this(Value)(Value value) inout - if (is(Value == DeducedParameterType!(inout(T)))); - } - - static foreach (tid, T; Types) - { - /// Constructs a `SumType` holding a specific value. - this(T value) - { - import core.lifetime : forward; - - static if (isCopyable!T) - { - // Workaround for https://issues.dlang.org/show_bug.cgi?id=21542 - __traits(getMember, storage, Storage.memberName!T) = __ctfe ? value : forward!value; - } - else - { - __traits(getMember, storage, Storage.memberName!T) = forward!value; - } - - tag = tid; - } - - static if (isCopyable!(const(T))) - { - static if (IndexOf!(const(T), Map!(ConstOf, Types)) == tid) - { - /// ditto - this(const(T) value) const - { - __traits(getMember, storage, Storage.memberName!T) = value; - tag = tid; - } - } - } - else - { - @disable this(const(T) value) const; - } - - static if (isCopyable!(immutable(T))) - { - static if (IndexOf!(immutable(T), Map!(ImmutableOf, Types)) == tid) - { - /// ditto - this(immutable(T) value) immutable - { - __traits(getMember, storage, Storage.memberName!T) = value; - tag = tid; - } - } - } - else - { - @disable this(immutable(T) value) immutable; - } - - static if (isCopyable!(inout(T))) - { - static if (IndexOf!(inout(T), Map!(InoutOf, Types)) == tid) - { - /// ditto - this(Value)(Value value) inout - if (is(Value == DeducedParameterType!(inout(T)))) - { - __traits(getMember, storage, Storage.memberName!T) = value; - tag = tid; - } - } - } - else - { - @disable this(Value)(Value value) inout - if (is(Value == DeducedParameterType!(inout(T)))); - } - } - - static if (anySatisfy!(hasElaborateCopyConstructor, Types)) - { - static if - ( - allSatisfy!(isCopyable, Map!(InoutOf, Types)) - && !anySatisfy!(hasPostblit, Map!(InoutOf, Types)) - && allSatisfy!(isInout, Map!(InoutOf, Types)) - ) - { - /// Constructs a `SumType` that's a copy of another `SumType`. - this(ref inout(SumType) other) inout - { - storage = other.match!((ref value) { - alias OtherTypes = Map!(InoutOf, Types); - enum tid = IndexOf!(typeof(value), OtherTypes); - alias T = Types[tid]; - - mixin("inout(Storage) newStorage = { ", - Storage.memberName!T, ": value", - " };"); - - return newStorage; - }); - - tag = other.tag; - } - } - else - { - static if (allSatisfy!(isCopyable, Types)) - { - /// ditto - this(ref SumType other) - { - storage = other.match!((ref value) { - alias T = typeof(value); - - mixin("Storage newStorage = { ", - Storage.memberName!T, ": value", - " };"); - - return newStorage; - }); - - tag = other.tag; - } - } - else - { - @disable this(ref SumType other); - } - - static if (allSatisfy!(isCopyable, Map!(ConstOf, Types))) - { - /// ditto - this(ref const(SumType) other) const - { - storage = other.match!((ref value) { - alias OtherTypes = Map!(ConstOf, Types); - enum tid = IndexOf!(typeof(value), OtherTypes); - alias T = Types[tid]; - - mixin("const(Storage) newStorage = { ", - Storage.memberName!T, ": value", - " };"); - - return newStorage; - }); - - tag = other.tag; - } - } - else - { - @disable this(ref const(SumType) other) const; - } - - static if (allSatisfy!(isCopyable, Map!(ImmutableOf, Types))) - { - /// ditto - this(ref immutable(SumType) other) immutable - { - storage = other.match!((ref value) { - alias OtherTypes = Map!(ImmutableOf, Types); - enum tid = IndexOf!(typeof(value), OtherTypes); - alias T = Types[tid]; - - mixin("immutable(Storage) newStorage = { ", - Storage.memberName!T, ": value", - " };"); - - return newStorage; - }); - - tag = other.tag; - } - } - else - { - @disable this(ref immutable(SumType) other) immutable; - } - } - } - - version (SumTypeNoDefaultCtor) - { - @disable this(); - } - - // Workaround for https://issues.dlang.org/show_bug.cgi?id=21399 - version (StdDdoc) - { - // Dummy type to stand in for loop variable - private struct T; - - /** - * Assigns a value to a `SumType`. - * - * If any of the `SumType`'s members other than the one being assigned - * to contain pointers or references, it is possible for the assignment - * to cause memory corruption (see the - * ["Memory corruption" example](#memory-corruption) below for an - * illustration of how). Therefore, such assignments are considered - * `@system`. - * - * An individual assignment can be `@trusted` if the caller can - * guarantee that there are no outstanding references to any `SumType` - * members that contain pointers or references at the time the - * assignment occurs. - * - * Examples: - * - * $(DIVID memory-corruption, $(H3 Memory corruption)) - * - * This example shows how assignment to a `SumType` can be used to - * cause memory corruption in `@system` code. In `@safe` code, the - * assignment `s = 123` would not be allowed. - * - * --- - * SumType!(int*, int) s = new int; - * s.tryMatch!( - * (ref int* p) { - * s = 123; // overwrites `p` - * return *p; // undefined behavior - * } - * ); - * --- - */ - ref SumType opAssign(T rhs); - } - - static foreach (tid, T; Types) - { - static if (isAssignableTo!T) - { - /** - * Assigns a value to a `SumType`. - * - * If any of the `SumType`'s members other than the one being assigned - * to contain pointers or references, it is possible for the assignment - * to cause memory corruption (see the - * ["Memory corruption" example](#memory-corruption) below for an - * illustration of how). Therefore, such assignments are considered - * `@system`. - * - * An individual assignment can be `@trusted` if the caller can - * guarantee that there are no outstanding references to any `SumType` - * members that contain pointers or references at the time the - * assignment occurs. - * - * Examples: - * - * $(DIVID memory-corruption, $(H3 Memory corruption)) - * - * This example shows how assignment to a `SumType` can be used to - * cause memory corruption in `@system` code. In `@safe` code, the - * assignment `s = 123` would not be allowed. - * - * --- - * SumType!(int*, int) s = new int; - * s.tryMatch!( - * (ref int* p) { - * s = 123; // overwrites `p` - * return *p; // undefined behavior - * } - * ); - * --- - */ - ref SumType opAssign(T rhs) - { - import core.lifetime : forward; - import std.traits : hasIndirections, hasNested; - import std.meta : AliasSeq, Or = templateOr; - - alias OtherTypes = - AliasSeq!(Types[0 .. tid], Types[tid + 1 .. $]); - enum unsafeToOverwrite = - anySatisfy!(Or!(hasIndirections, hasNested), OtherTypes); - - static if (unsafeToOverwrite) - { - cast(void) () @system {}(); - } - - this.match!destroyIfOwner; - - static if (isCopyable!T) - { - // Workaround for https://issues.dlang.org/show_bug.cgi?id=21542 - mixin("Storage newStorage = { ", - Storage.memberName!T, ": __ctfe ? rhs : forward!rhs", - " };"); - } - else - { - mixin("Storage newStorage = { ", - Storage.memberName!T, ": forward!rhs", - " };"); - } - - storage = newStorage; - tag = tid; - - return this; - } - } - } - - static if (allSatisfy!(isAssignableTo, Types)) - { - static if (allSatisfy!(isCopyable, Types)) - { - /** - * Copies the value from another `SumType` into this one. - * - * See the value-assignment overload for details on `@safe`ty. - * - * Copy assignment is `@disable`d if any of `Types` is non-copyable. - */ - ref SumType opAssign(ref SumType rhs) - { - rhs.match!((ref value) { this = value; }); - return this; - } - } - else - { - @disable ref SumType opAssign(ref SumType rhs); - } - - /** - * Moves the value from another `SumType` into this one. - * - * See the value-assignment overload for details on `@safe`ty. - */ - ref SumType opAssign(SumType rhs) - { - import core.lifetime : move; - - rhs.match!((ref value) { - static if (isCopyable!(typeof(value))) - { - // Workaround for https://issues.dlang.org/show_bug.cgi?id=21542 - this = __ctfe ? value : move(value); - } - else - { - this = move(value); - } - }); - return this; - } - } - - /** - * Compares two `SumType`s for equality. - * - * Two `SumType`s are equal if they are the same kind of `SumType`, they - * contain values of the same type, and those values are equal. - */ - bool opEquals(this This, Rhs)(auto ref Rhs rhs) - if (!is(CommonType!(This, Rhs) == void)) - { - static if (is(This == Rhs)) - { - return AliasSeq!(this, rhs).match!((ref value, ref rhsValue) { - static if (is(typeof(value) == typeof(rhsValue))) - { - return value == rhsValue; - } - else - { - return false; - } - }); - } - else - { - alias CommonSumType = CommonType!(This, Rhs); - return cast(CommonSumType) this == cast(CommonSumType) rhs; - } - } - - // Workaround for https://issues.dlang.org/show_bug.cgi?id=19407 - static if (__traits(compiles, anySatisfy!(hasElaborateDestructor, Types))) - { - // If possible, include the destructor only when it's needed - private enum includeDtor = anySatisfy!(hasElaborateDestructor, Types); - } - else - { - // If we can't tell, always include it, even when it does nothing - private enum includeDtor = true; - } - - static if (includeDtor) - { - /// Calls the destructor of the `SumType`'s current value. - ~this() - { - this.match!destroyIfOwner; - } - } - - // Workaround for https://issues.dlang.org/show_bug.cgi?id=21400 - version (StdDdoc) - { - /** - * Returns a string representation of the `SumType`'s current value. - * - * Not available when compiled with `-betterC`. - */ - string toString(this This)(); - - /** - * Handles formatted writing of the `SumType`'s current value. - * - * Not available when compiled with `-betterC`. - * - * Params: - * sink = Output range to write to. - * fmt = Format specifier to use. - * - * See_Also: $(REF formatValue, std,format) - */ - void toString(this This, Sink, Char)(ref Sink sink, const ref FormatSpec!Char fmt); - } - - version (D_BetterC) {} else - /** - * Returns a string representation of the `SumType`'s current value. - * - * Not available when compiled with `-betterC`. - */ - string toString(this This)() - { - import std.conv : to; - - return this.match!(to!string); - } - - version (D_BetterC) {} else - /** - * Handles formatted writing of the `SumType`'s current value. - * - * Not available when compiled with `-betterC`. - * - * Params: - * sink = Output range to write to. - * fmt = Format specifier to use. - * - * See_Also: $(REF formatValue, std,format) - */ - void toString(this This, Sink, Char)(ref Sink sink, const ref FormatSpec!Char fmt) - { - import std.format.write : formatValue; - - this.match!((ref value) { - formatValue(sink, value, fmt); - }); - } - - static if (allSatisfy!(isHashable, Map!(ConstOf, Types))) - { - // Workaround for https://issues.dlang.org/show_bug.cgi?id=21400 - version (StdDdoc) - { - /** - * Returns the hash of the `SumType`'s current value. - * - * Not available when compiled with `-betterC`. - */ - size_t toHash() const; - } - - // Workaround for https://issues.dlang.org/show_bug.cgi?id=20095 - version (D_BetterC) {} else - /** - * Returns the hash of the `SumType`'s current value. - * - * Not available when compiled with `-betterC`. - */ - size_t toHash() const - { - return this.match!hashOf; - } - } -} - -// Construction -@safe unittest -{ - alias MySum = SumType!(int, float); - - MySum x = MySum(42); - MySum y = MySum(3.14); -} - -// Assignment -@safe unittest -{ - alias MySum = SumType!(int, float); - - MySum x = MySum(42); - x = 3.14; -} - -// Self assignment -@safe unittest -{ - alias MySum = SumType!(int, float); - - MySum x = MySum(42); - MySum y = MySum(3.14); - y = x; -} - -// Equality -@safe unittest -{ - alias MySum = SumType!(int, float); - - assert(MySum(123) == MySum(123)); - assert(MySum(123) != MySum(456)); - assert(MySum(123) != MySum(123.0)); - assert(MySum(123) != MySum(456.0)); - -} - -// Equality of differently-qualified SumTypes -// Disabled in BetterC due to use of dynamic arrays -version (D_BetterC) {} else -@safe unittest -{ - alias SumA = SumType!(int, float); - alias SumB = SumType!(const(int[]), int[]); - alias SumC = SumType!(int[], const(int[])); - - int[] ma = [1, 2, 3]; - const(int[]) ca = [1, 2, 3]; - - assert(const(SumA)(123) == SumA(123)); - assert(const(SumB)(ma[]) == SumB(ca[])); - assert(const(SumC)(ma[]) == SumC(ca[])); -} - -// Imported types -@safe unittest -{ - import std.typecons : Tuple; - - alias MySum = SumType!(Tuple!(int, int)); -} - -// const and immutable types -@safe unittest -{ - alias MySum = SumType!(const(int[]), immutable(float[])); -} - -// Recursive types -@safe unittest -{ - alias MySum = SumType!(This*); - assert(is(MySum.Types[0] == MySum*)); -} - -// Allowed types -@safe unittest -{ - import std.meta : AliasSeq; - - alias MySum = SumType!(int, float, This*); - - assert(is(MySum.Types == AliasSeq!(int, float, MySum*))); -} - -// Types with destructors and postblits -@system unittest -{ - int copies; - - static struct Test - { - bool initialized = false; - int* copiesPtr; - - this(this) { (*copiesPtr)++; } - ~this() { if (initialized) (*copiesPtr)--; } - } - - alias MySum = SumType!(int, Test); - - Test t = Test(true, &copies); - - { - MySum x = t; - assert(copies == 1); - } - assert(copies == 0); - - { - MySum x = 456; - assert(copies == 0); - } - assert(copies == 0); - - { - MySum x = t; - assert(copies == 1); - x = 456; - assert(copies == 0); - } - - { - MySum x = 456; - assert(copies == 0); - x = t; - assert(copies == 1); - } - - { - MySum x = t; - MySum y = x; - assert(copies == 2); - } - - { - MySum x = t; - MySum y; - y = x; - assert(copies == 2); - } -} - -// Doesn't destroy reference types -// Disabled in BetterC due to use of classes -version (D_BetterC) {} else -@system unittest -{ - bool destroyed; - - class C - { - ~this() - { - destroyed = true; - } - } - - struct S - { - ~this() {} - } - - alias MySum = SumType!(S, C); - - C c = new C(); - { - MySum x = c; - destroyed = false; - } - assert(!destroyed); - - { - MySum x = c; - destroyed = false; - x = S(); - assert(!destroyed); - } -} - -// Types with @disable this() -@safe unittest -{ - static struct NoInit - { - @disable this(); - } - - alias MySum = SumType!(NoInit, int); - - assert(!__traits(compiles, MySum())); - auto _ = MySum(42); -} - -// const SumTypes -version (D_BetterC) {} else // not @nogc, https://issues.dlang.org/show_bug.cgi?id=22117 -@safe unittest -{ - auto _ = const(SumType!(int[]))([1, 2, 3]); -} - -// Equality of const SumTypes -@safe unittest -{ - alias MySum = SumType!int; - - auto _ = const(MySum)(123) == const(MySum)(456); -} - -// Compares reference types using value equality -@safe unittest -{ - import std.array : staticArray; - - static struct Field {} - static struct Struct { Field[] fields; } - alias MySum = SumType!Struct; - - static arr1 = staticArray([Field()]); - static arr2 = staticArray([Field()]); - - auto a = MySum(Struct(arr1[])); - auto b = MySum(Struct(arr2[])); - - assert(a == b); -} - -// toString -// Disabled in BetterC due to use of std.conv.text -version (D_BetterC) {} else -@safe unittest -{ - import std.conv : text; - - static struct Int { int i; } - static struct Double { double d; } - alias Sum = SumType!(Int, Double); - - assert(Sum(Int(42)).text == Int(42).text, Sum(Int(42)).text); - assert(Sum(Double(33.3)).text == Double(33.3).text, Sum(Double(33.3)).text); - assert((const(Sum)(Int(42))).text == (const(Int)(42)).text, (const(Sum)(Int(42))).text); -} - -// string formatting -// Disabled in BetterC due to use of std.format.format -version (D_BetterC) {} else -@safe unittest -{ - import std.format : format; - - SumType!int x = 123; - - assert(format!"%s"(x) == format!"%s"(123)); - assert(format!"%x"(x) == format!"%x"(123)); -} - -// string formatting of qualified SumTypes -// Disabled in BetterC due to use of std.format.format and dynamic arrays -version (D_BetterC) {} else -@safe unittest -{ - import std.format : format; - - int[] a = [1, 2, 3]; - const(SumType!(int[])) x = a; - - assert(format!"%(%d, %)"(x) == format!"%(%s, %)"(a)); -} - -// Github issue #16 -// Disabled in BetterC due to use of dynamic arrays -version (D_BetterC) {} else -@safe unittest -{ - alias Node = SumType!(This[], string); - - // override inference of @system attribute for cyclic functions - assert((() @trusted => - Node([Node([Node("x")])]) - == - Node([Node([Node("x")])]) - )()); -} - -// Github issue #16 with const -// Disabled in BetterC due to use of dynamic arrays -version (D_BetterC) {} else -@safe unittest -{ - alias Node = SumType!(const(This)[], string); - - // override inference of @system attribute for cyclic functions - assert((() @trusted => - Node([Node([Node("x")])]) - == - Node([Node([Node("x")])]) - )()); -} - -// Stale pointers -// Disabled in BetterC due to use of dynamic arrays -version (D_BetterC) {} else -@system unittest -{ - alias MySum = SumType!(ubyte, void*[2]); - - MySum x = [null, cast(void*) 0x12345678]; - void** p = &x.get!(void*[2])[1]; - x = ubyte(123); - - assert(*p != cast(void*) 0x12345678); -} - -// Exception-safe assignment -// Disabled in BetterC due to use of exceptions -version (D_BetterC) {} else -@safe unittest -{ - static struct A - { - int value = 123; - } - - static struct B - { - int value = 456; - this(this) { throw new Exception("oops"); } - } - - alias MySum = SumType!(A, B); - - MySum x; - try - { - x = B(); - } - catch (Exception e) {} - - assert( - (x.tag == 0 && x.get!A.value == 123) || - (x.tag == 1 && x.get!B.value == 456) - ); -} - -// Types with @disable this(this) -@safe unittest -{ - import core.lifetime : move; - - static struct NoCopy - { - @disable this(this); - } - - alias MySum = SumType!NoCopy; - - NoCopy lval = NoCopy(); - - MySum x = NoCopy(); - MySum y = NoCopy(); - - - assert(!__traits(compiles, SumType!NoCopy(lval))); - - y = NoCopy(); - y = move(x); - assert(!__traits(compiles, y = lval)); - assert(!__traits(compiles, y = x)); - - bool b = x == y; -} - -// Github issue #22 -// Disabled in BetterC due to use of std.typecons.Nullable -version (D_BetterC) {} else -@safe unittest -{ - import std.typecons; - - static struct A - { - SumType!(Nullable!int) a = Nullable!int.init; - } -} - -// Static arrays of structs with postblits -// Disabled in BetterC due to use of dynamic arrays -version (D_BetterC) {} else -@safe unittest -{ - static struct S - { - int n; - this(this) { n++; } - } - - SumType!(S[1]) x = [S(0)]; - SumType!(S[1]) y = x; - - auto xval = x.get!(S[1])[0].n; - auto yval = y.get!(S[1])[0].n; - - assert(xval != yval); -} - -// Replacement does not happen inside SumType -// Disabled in BetterC due to use of associative arrays -version (D_BetterC) {} else -@safe unittest -{ - import std.typecons : Tuple, ReplaceTypeUnless; - alias A = Tuple!(This*,SumType!(This*))[SumType!(This*,string)[This]]; - alias TR = ReplaceTypeUnless!(isSumTypeInstance, This, int, A); - static assert(is(TR == Tuple!(int*,SumType!(This*))[SumType!(This*, string)[int]])); -} - -// Supports nested self-referential SumTypes -@safe unittest -{ - import std.typecons : Tuple, Flag; - alias Nat = SumType!(Flag!"0", Tuple!(This*)); - alias Inner = SumType!Nat; - alias Outer = SumType!(Nat*, Tuple!(This*, This*)); -} - -// Self-referential SumTypes inside Algebraic -// Disabled in BetterC due to use of std.variant.Algebraic -version (D_BetterC) {} else -@safe unittest -{ - import std.variant : Algebraic; - - alias T = Algebraic!(SumType!(This*)); - - assert(is(T.AllowedTypes[0].Types[0] == T.AllowedTypes[0]*)); -} - -// Doesn't call @system postblits in @safe code -@safe unittest -{ - static struct SystemCopy { @system this(this) {} } - SystemCopy original; - - assert(!__traits(compiles, () @safe - { - SumType!SystemCopy copy = original; - })); - - assert(!__traits(compiles, () @safe - { - SumType!SystemCopy copy; copy = original; - })); -} - -// Doesn't overwrite pointers in @safe code -@safe unittest -{ - alias MySum = SumType!(int*, int); - - MySum x; - - assert(!__traits(compiles, () @safe - { - x = 123; - })); - - assert(!__traits(compiles, () @safe - { - x = MySum(123); - })); -} - -// Calls value postblit on self-assignment -@safe unittest -{ - static struct S - { - int n; - this(this) { n++; } - } - - SumType!S x = S(); - SumType!S y; - y = x; - - auto xval = x.get!S.n; - auto yval = y.get!S.n; - - assert(xval != yval); -} - -// Github issue #29 -@safe unittest -{ - alias A = SumType!string; - - @safe A createA(string arg) - { - return A(arg); - } - - @safe void test() - { - A a = createA(""); - } -} - -// SumTypes as associative array keys -// Disabled in BetterC due to use of associative arrays -version (D_BetterC) {} else -@safe unittest -{ - int[SumType!(int, string)] aa; -} - -// toString with non-copyable types -// Disabled in BetterC due to use of std.conv.to (in toString) -version (D_BetterC) {} else -@safe unittest -{ - struct NoCopy - { - @disable this(this); - } - - SumType!NoCopy x; - - auto _ = x.toString(); -} - -// Can use the result of assignment -@safe unittest -{ - alias MySum = SumType!(int, float); - - MySum a = MySum(123); - MySum b = MySum(3.14); - - assert((a = b) == b); - assert((a = MySum(123)) == MySum(123)); - assert((a = 3.14) == MySum(3.14)); - assert(((a = b) = MySum(123)) == MySum(123)); -} - -// Types with copy constructors -@safe unittest -{ - static struct S - { - int n; - - this(ref return scope inout S other) inout - { - n = other.n + 1; - } - } - - SumType!S x = S(); - SumType!S y = x; - - auto xval = x.get!S.n; - auto yval = y.get!S.n; - - assert(xval != yval); -} - -// Copyable by generated copy constructors -@safe unittest -{ - static struct Inner - { - ref this(ref inout Inner other) {} - } - - static struct Outer - { - SumType!Inner inner; - } - - Outer x; - Outer y = x; -} - -// Types with qualified copy constructors -@safe unittest -{ - static struct ConstCopy - { - int n; - this(inout int n) inout { this.n = n; } - this(ref const typeof(this) other) const { this.n = other.n; } - } - - static struct ImmutableCopy - { - int n; - this(inout int n) inout { this.n = n; } - this(ref immutable typeof(this) other) immutable { this.n = other.n; } - } - - const SumType!ConstCopy x = const(ConstCopy)(1); - immutable SumType!ImmutableCopy y = immutable(ImmutableCopy)(1); -} - -// Types with disabled opEquals -@safe unittest -{ - static struct S - { - @disable bool opEquals(const S rhs) const; - } - - auto _ = SumType!S(S()); -} - -// Types with non-const opEquals -@safe unittest -{ - static struct S - { - int i; - bool opEquals(S rhs) { return i == rhs.i; } - } - - auto _ = SumType!S(S(123)); -} - -// Incomparability of different SumTypes -@safe unittest -{ - SumType!(int, string) x = 123; - SumType!(string, int) y = 123; - - assert(!__traits(compiles, x != y)); -} - -// Self-reference in return/parameter type of function pointer member -// Disabled in BetterC due to use of delegates -version (D_BetterC) {} else -@safe unittest -{ - alias T = SumType!(int, This delegate(This)); -} - -// Construction and assignment from implicitly-convertible lvalue -@safe unittest -{ - alias MySum = SumType!bool; - - const(bool) b = true; - - MySum x = b; - MySum y; y = b; -} - -// @safe assignment to the only pointer type in a SumType -@safe unittest -{ - SumType!(string, int) sm = 123; - sm = "this should be @safe"; -} - -// Pointers to local variables -// https://issues.dlang.org/show_bug.cgi?id=22117 -@safe unittest -{ - int n = 123; - immutable int ni = 456; - - SumType!(int*) s = &n; - const SumType!(int*) sc = &n; - immutable SumType!(int*) si = ∋ -} - -// Immutable member type with copy constructor -// https://issues.dlang.org/show_bug.cgi?id=22572 -@safe unittest -{ - static struct CopyConstruct - { - this(ref inout CopyConstruct other) inout {} - } - - static immutable struct Value - { - CopyConstruct c; - } - - SumType!Value s; -} - -// Construction of inout-qualified SumTypes -// https://issues.dlang.org/show_bug.cgi?id=22901 -@safe unittest -{ - static inout(SumType!(int[])) example(inout(int[]) arr) - { - return inout(SumType!(int[]))(arr); - } -} - -// Assignment of struct with overloaded opAssign in CTFE -// https://issues.dlang.org/show_bug.cgi?id=23182 -@safe unittest -{ - static struct HasOpAssign - { - void opAssign(HasOpAssign rhs) {} - } - - static SumType!HasOpAssign test() - { - SumType!HasOpAssign s; - // Test both overloads - s = HasOpAssign(); - s = SumType!HasOpAssign(); - return s; - } - - // Force CTFE - enum result = test(); -} - -/// True if `T` is an instance of the `SumType` template, otherwise false. -private enum bool isSumTypeInstance(T) = is(T == SumType!Args, Args...); - -@safe unittest -{ - static struct Wrapper - { - SumType!int s; - alias s this; - } - - assert(isSumTypeInstance!(SumType!int)); - assert(!isSumTypeInstance!Wrapper); -} - -/// True if `T` is a [SumType] or implicitly converts to one, otherwise false. -enum bool isSumType(T) = is(T : SumType!Args, Args...); - -/// -@safe unittest -{ - static struct ConvertsToSumType - { - SumType!int payload; - alias payload this; - } - - static struct ContainsSumType - { - SumType!int payload; - } - - assert(isSumType!(SumType!int)); - assert(isSumType!ConvertsToSumType); - assert(!isSumType!ContainsSumType); -} - -/** - * Calls a type-appropriate function with the value held in a [SumType]. - * - * For each possible type the [SumType] can hold, the given handlers are - * checked, in order, to see whether they accept a single argument of that type. - * The first one that does is chosen as the match for that type. (Note that the - * first match may not always be the most exact match. - * See ["Avoiding unintentional matches"](#avoiding-unintentional-matches) for - * one common pitfall.) - * - * Every type must have a matching handler, and every handler must match at - * least one type. This is enforced at compile time. - * - * Handlers may be functions, delegates, or objects with `opCall` overloads. If - * a function with more than one overload is given as a handler, all of the - * overloads are considered as potential matches. - * - * Templated handlers are also accepted, and will match any type for which they - * can be [implicitly instantiated](https://dlang.org/glossary.html#ifti). See - * ["Introspection-based matching"](#introspection-based-matching) for an - * example of templated handler usage. - * - * If multiple [SumType]s are passed to match, their values are passed to the - * handlers as separate arguments, and matching is done for each possible - * combination of value types. See ["Multiple dispatch"](#multiple-dispatch) for - * an example. - * - * Returns: - * The value returned from the handler that matches the currently-held type. - * - * See_Also: $(REF visit, std,variant) - */ -template match(handlers...) -{ - import std.typecons : Yes; - - /** - * The actual `match` function. - * - * Params: - * args = One or more [SumType] objects. - */ - auto ref match(SumTypes...)(auto ref SumTypes args) - if (allSatisfy!(isSumType, SumTypes) && args.length > 0) - { - return matchImpl!(Yes.exhaustive, handlers)(args); - } -} - -/** $(DIVID avoiding-unintentional-matches, $(H3 Avoiding unintentional matches)) - * - * Sometimes, implicit conversions may cause a handler to match more types than - * intended. The example below shows two solutions to this problem. - */ -@safe unittest -{ - alias Number = SumType!(double, int); - - Number x; - - // Problem: because int implicitly converts to double, the double - // handler is used for both types, and the int handler never matches. - assert(!__traits(compiles, - x.match!( - (double d) => "got double", - (int n) => "got int" - ) - )); - - // Solution 1: put the handler for the "more specialized" type (in this - // case, int) before the handler for the type it converts to. - assert(__traits(compiles, - x.match!( - (int n) => "got int", - (double d) => "got double" - ) - )); - - // Solution 2: use a template that only accepts the exact type it's - // supposed to match, instead of any type that implicitly converts to it. - alias exactly(T, alias fun) = function (arg) - { - static assert(is(typeof(arg) == T)); - return fun(arg); - }; - - // Now, even if we put the double handler first, it will only be used for - // doubles, not ints. - assert(__traits(compiles, - x.match!( - exactly!(double, d => "got double"), - exactly!(int, n => "got int") - ) - )); -} - -/** $(DIVID multiple-dispatch, $(H3 Multiple dispatch)) - * - * Pattern matching can be performed on multiple `SumType`s at once by passing - * handlers with multiple arguments. This usually leads to more concise code - * than using nested calls to `match`, as show below. - */ -@safe unittest -{ - struct Point2D { double x, y; } - struct Point3D { double x, y, z; } - - alias Point = SumType!(Point2D, Point3D); - - version (none) - { - // This function works, but the code is ugly and repetitive. - // It uses three separate calls to match! - @safe pure nothrow @nogc - bool sameDimensions(Point p1, Point p2) - { - return p1.match!( - (Point2D _) => p2.match!( - (Point2D _) => true, - _ => false - ), - (Point3D _) => p2.match!( - (Point3D _) => true, - _ => false - ) - ); - } - } - - // This version is much nicer. - @safe pure nothrow @nogc - bool sameDimensions(Point p1, Point p2) - { - alias doMatch = match!( - (Point2D _1, Point2D _2) => true, - (Point3D _1, Point3D _2) => true, - (_1, _2) => false - ); - - return doMatch(p1, p2); - } - - Point a = Point2D(1, 2); - Point b = Point2D(3, 4); - Point c = Point3D(5, 6, 7); - Point d = Point3D(8, 9, 0); - - assert( sameDimensions(a, b)); - assert( sameDimensions(c, d)); - assert(!sameDimensions(a, c)); - assert(!sameDimensions(d, b)); -} - -/** - * Attempts to call a type-appropriate function with the value held in a - * [SumType], and throws on failure. - * - * Matches are chosen using the same rules as [match], but are not required to - * be exhaustive—in other words, a type (or combination of types) is allowed to - * have no matching handler. If a type without a handler is encountered at - * runtime, a [MatchException] is thrown. - * - * Not available when compiled with `-betterC`. - * - * Returns: - * The value returned from the handler that matches the currently-held type, - * if a handler was given for that type. - * - * Throws: - * [MatchException], if the currently-held type has no matching handler. - * - * See_Also: $(REF tryVisit, std,variant) - */ -version (D_Exceptions) -template tryMatch(handlers...) -{ - import std.typecons : No; - - /** - * The actual `tryMatch` function. - * - * Params: - * args = One or more [SumType] objects. - */ - auto ref tryMatch(SumTypes...)(auto ref SumTypes args) - if (allSatisfy!(isSumType, SumTypes) && args.length > 0) - { - return matchImpl!(No.exhaustive, handlers)(args); - } -} - -/** - * Thrown by [tryMatch] when an unhandled type is encountered. - * - * Not available when compiled with `-betterC`. - */ -version (D_Exceptions) -class MatchException : Exception -{ - /// - pure @safe @nogc nothrow - this(string msg, string file = __FILE__, size_t line = __LINE__) - { - super(msg, file, line); - } -} - -/** - * True if `handler` is a potential match for `Ts`, otherwise false. - * - * See the documentation for [match] for a full explanation of how matches are - * chosen. - */ -template canMatch(alias handler, Ts...) -if (Ts.length > 0) -{ - enum canMatch = is(typeof((ref Ts args) => handler(args))); -} - -/// -@safe unittest -{ - alias handleInt = (int i) => "got an int"; - - assert( canMatch!(handleInt, int)); - assert(!canMatch!(handleInt, string)); -} - -// Includes all overloads of the given handler -@safe unittest -{ - static struct OverloadSet - { - static void fun(int n) {} - static void fun(double d) {} - } - - assert(canMatch!(OverloadSet.fun, int)); - assert(canMatch!(OverloadSet.fun, double)); -} - -// Like aliasSeqOf!(iota(n)), but works in BetterC -private template Iota(size_t n) -{ - static if (n == 0) - { - alias Iota = AliasSeq!(); - } - else - { - alias Iota = AliasSeq!(Iota!(n - 1), n - 1); - } -} - -@safe unittest -{ - assert(is(Iota!0 == AliasSeq!())); - assert(Iota!1 == AliasSeq!(0)); - assert(Iota!3 == AliasSeq!(0, 1, 2)); -} - -/* The number that the dim-th argument's tag is multiplied by when - * converting TagTuples to and from case indices ("caseIds"). - * - * Named by analogy to the stride that the dim-th index into a - * multidimensional static array is multiplied by to calculate the - * offset of a specific element. - */ -private size_t stride(size_t dim, lengths...)() -{ - import core.checkedint : mulu; - - size_t result = 1; - bool overflow = false; - - static foreach (i; 0 .. dim) - { - result = mulu(result, lengths[i], overflow); - } - - /* The largest number matchImpl uses, numCases, is calculated with - * stride!(SumTypes.length), so as long as this overflow check - * passes, we don't need to check for overflow anywhere else. - */ - assert(!overflow, "Integer overflow"); - return result; -} - -private template matchImpl(Flag!"exhaustive" exhaustive, handlers...) -{ - auto ref matchImpl(SumTypes...)(auto ref SumTypes args) - if (allSatisfy!(isSumType, SumTypes) && args.length > 0) - { - alias stride(size_t i) = .stride!(i, Map!(typeCount, SumTypes)); - alias TagTuple = .TagTuple!(SumTypes); - - /* - * A list of arguments to be passed to a handler needed for the case - * labeled with `caseId`. - */ - template handlerArgs(size_t caseId) - { - enum tags = TagTuple.fromCaseId(caseId); - enum argsFrom(size_t i : tags.length) = ""; - enum argsFrom(size_t i) = "args[" ~ toCtString!i ~ "].get!(SumTypes[" ~ toCtString!i ~ "]" ~ - ".Types[" ~ toCtString!(tags[i]) ~ "])(), " ~ argsFrom!(i + 1); - enum handlerArgs = argsFrom!0; - } - - /* An AliasSeq of the types of the member values in the argument list - * returned by `handlerArgs!caseId`. - * - * Note that these are the actual (that is, qualified) types of the - * member values, which may not be the same as the types listed in - * the arguments' `.Types` properties. - */ - template valueTypes(size_t caseId) - { - enum tags = TagTuple.fromCaseId(caseId); - - template getType(size_t i) - { - enum tid = tags[i]; - alias T = SumTypes[i].Types[tid]; - alias getType = typeof(args[i].get!T()); - } - - alias valueTypes = Map!(getType, Iota!(tags.length)); - } - - /* The total number of cases is - * - * Π SumTypes[i].Types.length for 0 ≤ i < SumTypes.length - * - * Or, equivalently, - * - * ubyte[SumTypes[0].Types.length]...[SumTypes[$-1].Types.length].sizeof - * - * Conveniently, this is equal to stride!(SumTypes.length), so we can - * use that function to compute it. - */ - enum numCases = stride!(SumTypes.length); - - /* Guaranteed to never be a valid handler index, since - * handlers.length <= size_t.max. - */ - enum noMatch = size_t.max; - - // An array that maps caseIds to handler indices ("hids"). - enum matches = () - { - size_t[numCases] result; - - // Workaround for https://issues.dlang.org/show_bug.cgi?id=19561 - foreach (ref match; result) - { - match = noMatch; - } - - static foreach (caseId; 0 .. numCases) - { - static foreach (hid, handler; handlers) - { - static if (canMatch!(handler, valueTypes!caseId)) - { - if (result[caseId] == noMatch) - { - result[caseId] = hid; - } - } - } - } - - return result; - }(); - - import std.algorithm.searching : canFind; - - // Check for unreachable handlers - static foreach (hid, handler; handlers) - { - static assert(matches[].canFind(hid), - "`handlers[" ~ toCtString!hid ~ "]` " ~ - "of type `" ~ ( __traits(isTemplate, handler) - ? "template" - : typeof(handler).stringof - ) ~ "` " ~ - "never matches. Perhaps the handler failed to compile" - ); - } - - // Workaround for https://issues.dlang.org/show_bug.cgi?id=19993 - enum handlerName(size_t hid) = "handler" ~ toCtString!hid; - - static foreach (size_t hid, handler; handlers) - { - mixin("alias ", handlerName!hid, " = handler;"); - } - - immutable argsId = TagTuple(args).toCaseId; - - final switch (argsId) - { - static foreach (caseId; 0 .. numCases) - { - case caseId: - static if (matches[caseId] != noMatch) - { - return mixin(handlerName!(matches[caseId]), "(", handlerArgs!caseId, ")"); - } - else - { - static if (exhaustive) - { - static assert(false, - "No matching handler for types `" ~ valueTypes!caseId.stringof ~ "`"); - } - else - { - throw new MatchException( - "No matching handler for types `" ~ valueTypes!caseId.stringof ~ "`"); - } - } - } - } - - assert(false, "unreachable"); - } -} - -private enum typeCount(SumType) = SumType.Types.length; - -/* A TagTuple represents a single possible set of tags that `args` - * could have at runtime. - * - * Because D does not allow a struct to be the controlling expression - * of a switch statement, we cannot dispatch on the TagTuple directly. - * Instead, we must map each TagTuple to a unique integer and generate - * a case label for each of those integers. - * - * This mapping is implemented in `fromCaseId` and `toCaseId`. It uses - * the same technique that's used to map index tuples to memory offsets - * in a multidimensional static array. - * - * For example, when `args` consists of two SumTypes with two member - * types each, the TagTuples corresponding to each case label are: - * - * case 0: TagTuple([0, 0]) - * case 1: TagTuple([1, 0]) - * case 2: TagTuple([0, 1]) - * case 3: TagTuple([1, 1]) - * - * When there is only one argument, the caseId is equal to that - * argument's tag. - */ -private struct TagTuple(SumTypes...) -{ - size_t[SumTypes.length] tags; - alias tags this; - - alias stride(size_t i) = .stride!(i, Map!(typeCount, SumTypes)); - - invariant - { - static foreach (i; 0 .. tags.length) - { - assert(tags[i] < SumTypes[i].Types.length, "Invalid tag"); - } - } - - this(ref const(SumTypes) args) - { - static foreach (i; 0 .. tags.length) - { - tags[i] = args[i].tag; - } - } - - static TagTuple fromCaseId(size_t caseId) - { - TagTuple result; - - // Most-significant to least-significant - static foreach_reverse (i; 0 .. result.length) - { - result[i] = caseId / stride!i; - caseId %= stride!i; - } - - return result; - } - - size_t toCaseId() - { - size_t result; - - static foreach (i; 0 .. tags.length) - { - result += tags[i] * stride!i; - } - - return result; - } -} - -// Matching -@safe unittest -{ - alias MySum = SumType!(int, float); - - MySum x = MySum(42); - MySum y = MySum(3.14); - - assert(x.match!((int v) => true, (float v) => false)); - assert(y.match!((int v) => false, (float v) => true)); -} - -// Missing handlers -@safe unittest -{ - alias MySum = SumType!(int, float); - - MySum x = MySum(42); - - assert(!__traits(compiles, x.match!((int x) => true))); - assert(!__traits(compiles, x.match!())); -} - -// Handlers with qualified parameters -// Disabled in BetterC due to use of dynamic arrays -version (D_BetterC) {} else -@safe unittest -{ - alias MySum = SumType!(int[], float[]); - - MySum x = MySum([1, 2, 3]); - MySum y = MySum([1.0, 2.0, 3.0]); - - assert(x.match!((const(int[]) v) => true, (const(float[]) v) => false)); - assert(y.match!((const(int[]) v) => false, (const(float[]) v) => true)); -} - -// Handlers for qualified types -// Disabled in BetterC due to use of dynamic arrays -version (D_BetterC) {} else -@safe unittest -{ - alias MySum = SumType!(immutable(int[]), immutable(float[])); - - MySum x = MySum([1, 2, 3]); - - assert(x.match!((immutable(int[]) v) => true, (immutable(float[]) v) => false)); - assert(x.match!((const(int[]) v) => true, (const(float[]) v) => false)); - // Tail-qualified parameters - assert(x.match!((immutable(int)[] v) => true, (immutable(float)[] v) => false)); - assert(x.match!((const(int)[] v) => true, (const(float)[] v) => false)); - // Generic parameters - assert(x.match!((immutable v) => true)); - assert(x.match!((const v) => true)); - // Unqualified parameters - assert(!__traits(compiles, - x.match!((int[] v) => true, (float[] v) => false) - )); -} - -// Delegate handlers -// Disabled in BetterC due to use of closures -version (D_BetterC) {} else -@safe unittest -{ - alias MySum = SumType!(int, float); - - int answer = 42; - MySum x = MySum(42); - MySum y = MySum(3.14); - - assert(x.match!((int v) => v == answer, (float v) => v == answer)); - assert(!y.match!((int v) => v == answer, (float v) => v == answer)); -} - -version (unittest) -{ - version (D_BetterC) - { - // std.math.isClose depends on core.runtime.math, so use a - // libc-based version for testing with -betterC - @safe pure @nogc nothrow - private bool isClose(double lhs, double rhs) - { - import core.stdc.math : fabs; - - return fabs(lhs - rhs) < 1e-5; - } - } - else - { - import std.math.operations : isClose; - } -} - -// Generic handler -@safe unittest -{ - alias MySum = SumType!(int, float); - - MySum x = MySum(42); - MySum y = MySum(3.14); - - assert(x.match!(v => v*2) == 84); - assert(y.match!(v => v*2).isClose(6.28)); -} - -// Fallback to generic handler -// Disabled in BetterC due to use of std.conv.to -version (D_BetterC) {} else -@safe unittest -{ - import std.conv : to; - - alias MySum = SumType!(int, float, string); - - MySum x = MySum(42); - MySum y = MySum("42"); - - assert(x.match!((string v) => v.to!int, v => v*2) == 84); - assert(y.match!((string v) => v.to!int, v => v*2) == 42); -} - -// Multiple non-overlapping generic handlers -@safe unittest -{ - import std.array : staticArray; - - alias MySum = SumType!(int, float, int[], char[]); - - static ints = staticArray([1, 2, 3]); - static chars = staticArray(['a', 'b', 'c']); - - MySum x = MySum(42); - MySum y = MySum(3.14); - MySum z = MySum(ints[]); - MySum w = MySum(chars[]); - - assert(x.match!(v => v*2, v => v.length) == 84); - assert(y.match!(v => v*2, v => v.length).isClose(6.28)); - assert(w.match!(v => v*2, v => v.length) == 3); - assert(z.match!(v => v*2, v => v.length) == 3); -} - -// Structural matching -@safe unittest -{ - static struct S1 { int x; } - static struct S2 { int y; } - alias MySum = SumType!(S1, S2); - - MySum a = MySum(S1(0)); - MySum b = MySum(S2(0)); - - assert(a.match!(s1 => s1.x + 1, s2 => s2.y - 1) == 1); - assert(b.match!(s1 => s1.x + 1, s2 => s2.y - 1) == -1); -} - -// Separate opCall handlers -@safe unittest -{ - static struct IntHandler - { - bool opCall(int arg) - { - return true; - } - } - - static struct FloatHandler - { - bool opCall(float arg) - { - return false; - } - } - - alias MySum = SumType!(int, float); - - MySum x = MySum(42); - MySum y = MySum(3.14); - - assert(x.match!(IntHandler.init, FloatHandler.init)); - assert(!y.match!(IntHandler.init, FloatHandler.init)); -} - -// Compound opCall handler -@safe unittest -{ - static struct CompoundHandler - { - bool opCall(int arg) - { - return true; - } - - bool opCall(float arg) - { - return false; - } - } - - alias MySum = SumType!(int, float); - - MySum x = MySum(42); - MySum y = MySum(3.14); - - assert(x.match!(CompoundHandler.init)); - assert(!y.match!(CompoundHandler.init)); -} - -// Ordered matching -@safe unittest -{ - alias MySum = SumType!(int, float); - - MySum x = MySum(42); - - assert(x.match!((int v) => true, v => false)); -} - -// Non-exhaustive matching -version (D_Exceptions) -@system unittest -{ - import std.exception : assertThrown, assertNotThrown; - - alias MySum = SumType!(int, float); - - MySum x = MySum(42); - MySum y = MySum(3.14); - - assertNotThrown!MatchException(x.tryMatch!((int n) => true)); - assertThrown!MatchException(y.tryMatch!((int n) => true)); -} - -// Non-exhaustive matching in @safe code -version (D_Exceptions) -@safe unittest -{ - SumType!(int, float) x; - - auto _ = x.tryMatch!( - (int n) => n + 1, - ); -} - -// Handlers with ref parameters -@safe unittest -{ - alias Value = SumType!(long, double); - - auto value = Value(3.14); - - value.match!( - (long) {}, - (ref double d) { d *= 2; } - ); - - assert(value.get!double.isClose(6.28)); -} - -// Unreachable handlers -@safe unittest -{ - alias MySum = SumType!(int, string); - - MySum s; - - assert(!__traits(compiles, - s.match!( - (int _) => 0, - (string _) => 1, - (double _) => 2 - ) - )); - - assert(!__traits(compiles, - s.match!( - _ => 0, - (int _) => 1 - ) - )); -} - -// Unsafe handlers -@system unittest -{ - SumType!int x; - alias unsafeHandler = (int x) @system { return; }; - - assert(!__traits(compiles, () @safe - { - x.match!unsafeHandler; - })); - - auto test() @system - { - return x.match!unsafeHandler; - } -} - -// Overloaded handlers -@safe unittest -{ - static struct OverloadSet - { - static string fun(int i) { return "int"; } - static string fun(double d) { return "double"; } - } - - alias MySum = SumType!(int, double); - - MySum a = 42; - MySum b = 3.14; - - assert(a.match!(OverloadSet.fun) == "int"); - assert(b.match!(OverloadSet.fun) == "double"); -} - -// Overload sets that include SumType arguments -@safe unittest -{ - alias Inner = SumType!(int, double); - alias Outer = SumType!(Inner, string); - - static struct OverloadSet - { - @safe: - static string fun(int i) { return "int"; } - static string fun(double d) { return "double"; } - static string fun(string s) { return "string"; } - static string fun(Inner i) { return i.match!fun; } - static string fun(Outer o) { return o.match!fun; } - } - - Outer a = Inner(42); - Outer b = Inner(3.14); - Outer c = "foo"; - - assert(OverloadSet.fun(a) == "int"); - assert(OverloadSet.fun(b) == "double"); - assert(OverloadSet.fun(c) == "string"); -} - -// Overload sets with ref arguments -@safe unittest -{ - static struct OverloadSet - { - static void fun(ref int i) { i = 42; } - static void fun(ref double d) { d = 3.14; } - } - - alias MySum = SumType!(int, double); - - MySum x = 0; - MySum y = 0.0; - - x.match!(OverloadSet.fun); - y.match!(OverloadSet.fun); - - assert(x.match!((value) => is(typeof(value) == int) && value == 42)); - assert(y.match!((value) => is(typeof(value) == double) && value == 3.14)); -} - -// Overload sets with templates -@safe unittest -{ - import std.traits : isNumeric; - - static struct OverloadSet - { - static string fun(string arg) - { - return "string"; - } - - static string fun(T)(T arg) - if (isNumeric!T) - { - return "numeric"; - } - } - - alias MySum = SumType!(int, string); - - MySum x = 123; - MySum y = "hello"; - - assert(x.match!(OverloadSet.fun) == "numeric"); - assert(y.match!(OverloadSet.fun) == "string"); -} - -// Github issue #24 -@safe unittest -{ - void test() @nogc - { - int acc = 0; - SumType!int(1).match!((int x) => acc += x); - } -} - -// Github issue #31 -@safe unittest -{ - void test() @nogc - { - int acc = 0; - - SumType!(int, string)(1).match!( - (int x) => acc += x, - (string _) => 0, - ); - } -} - -// Types that `alias this` a SumType -@safe unittest -{ - static struct A {} - static struct B {} - static struct D { SumType!(A, B) value; alias value this; } - - auto _ = D().match!(_ => true); -} - -// Multiple dispatch -@safe unittest -{ - alias MySum = SumType!(int, string); - - static int fun(MySum x, MySum y) - { - import std.meta : Args = AliasSeq; - - return Args!(x, y).match!( - (int xv, int yv) => 0, - (string xv, int yv) => 1, - (int xv, string yv) => 2, - (string xv, string yv) => 3 - ); - } - - assert(fun(MySum(0), MySum(0)) == 0); - assert(fun(MySum(""), MySum(0)) == 1); - assert(fun(MySum(0), MySum("")) == 2); - assert(fun(MySum(""), MySum("")) == 3); -} - -// inout SumTypes -@safe unittest -{ - inout(int[]) fun(inout(SumType!(int[])) x) - { - return x.match!((inout(int[]) a) => a); - } -} - -// return ref -// issue: https://issues.dlang.org/show_bug.cgi?id=23101 -@safe unittest -{ - static assert(!__traits(compiles, () { - SumType!(int, string) st; - return st.match!( - function int* (string x) => assert(0), - function int* (return ref int i) => &i, - ); - })); - - SumType!(int, string) st; - static assert(__traits(compiles, () { - return st.match!( - function int* (string x) => null, - function int* (return ref int i) => &i, - ); - })); -} - -private void destroyIfOwner(T)(ref T value) -{ - static if (hasElaborateDestructor!T) - { - destroy(value); - } -} diff --git a/phobos/std/system.d b/phobos/std/system.d deleted file mode 100644 index aa672a5..0000000 --- a/phobos/std/system.d +++ /dev/null @@ -1,158 +0,0 @@ -// Written in the D programming language. - -/** - * Information about the target operating system, environment, and CPU. - * - * Copyright: Copyright The D Language Foundation 2000 - 2011 - * License: $(HTTP www.boost.org/LICENSE_1_0.txt, Boost License 1.0). - * Authors: $(HTTP digitalmars.com, Walter Bright) and - $(HTTP jmdavisprog.com, Jonathan M Davis) - * Source: $(PHOBOSSRC std/system.d) - */ -module std.system; - -immutable -{ - /++ - Operating system. - - Note: - This is for cases where you need a value representing the OS at - runtime. If you're doing something which should compile differently - on different OSes, then please use `version (Windows)`, - `version (linux)`, etc. - - See_Also: - $(DDSUBLINK spec/version,PredefinedVersions, Predefined Versions) - +/ - enum OS - { - win32 = 1, /// Microsoft 32 bit Windows systems - win64, /// Microsoft 64 bit Windows systems - linux, /// All Linux Systems, except for Android - osx, /// Mac OS X - iOS, /// iOS - tvOS, /// tvOS - watchOS, /// watchOS - freeBSD, /// FreeBSD - netBSD, /// NetBSD - openBSD, /// OpenBSD - dragonFlyBSD, /// DragonFlyBSD - solaris, /// Solaris - android, /// Android - otherPosix, /// Other Posix Systems - unknown, /// Unknown - } - - /// The OS that the program was compiled for. - version (Win32) OS os = OS.win32; - else version (Win64) OS os = OS.win64; - else version (Android) OS os = OS.android; - else version (linux) OS os = OS.linux; - else version (OSX) OS os = OS.osx; - else version (iOS) OS os = OS.iOS; - else version (tvOS) OS os = OS.tvOS; - else version (watchOS) OS os = OS.watchOS; - else version (FreeBSD) OS os = OS.freeBSD; - else version (NetBSD) OS os = OS.netBSD; - else version (OpenBSD) OS os = OS.openBSD; - else version (DragonFlyBSD) OS os = OS.dragonFlyBSD; - else version (Posix) OS os = OS.otherPosix; - else OS os = OS.unknown; - - /++ - Byte order endianness. - - Note: - This is intended for cases where you need to deal with endianness at - runtime. If you're doing something which should compile differently - depending on whether you're compiling on a big endian or little - endian machine, then please use `version (BigEndian)` and - `version (LittleEndian)`. - - See_Also: - $(DDSUBLINK spec/version,PredefinedVersions, Predefined Versions) - +/ - enum Endian - { - bigEndian, /// Big endian byte order - littleEndian /// Little endian byte order - } - - /// The endianness that the program was compiled for. - version (LittleEndian) Endian endian = Endian.littleEndian; - else Endian endian = Endian.bigEndian; - /++ - Instruction Set Architecture. - - Note: - This is intended for cases where you need a value representing the - instruction set architecture at runtime. If you're doing something - which should compile differently depending on instruction set - architecture, then please use `version (X86_64)`, `version (ARM)`, - etc. - - See_Also: - $(DDSUBLINK spec/version,PredefinedVersions, Predefined Versions) - +/ - enum ISA - { - x86, /// Intel and AMD 32-bit processors - x86_64, /// Intel and AMD 64-bit processors - arm, /// The ARM architecture (32-bit) (AArch32 et al) - aarch64, /// The Advanced RISC Machine architecture (64-bit) - asmJS, /// The asm.js intermediate programming language - avr, /// 8-bit Atmel AVR Microcontrollers - epiphany, /// The Epiphany architecture - ppc, /// The PowerPC architecture, 32-bit - ppc64, /// The PowerPC architecture, 64-bit - ia64, /// The Itanium architecture (64-bit) - mips32, /// The MIPS architecture, 32-bit - mips64, /// The MIPS architecture, 64-bit - msp430, /// The MSP430 architecture - nvptx, /// The Nvidia Parallel Thread Execution (PTX) architecture, 32-bit - nvptx64, /// The Nvidia Parallel Thread Execution (PTX) architecture, 64-bit - riscv32, /// The RISC-V architecture, 32-bit - riscv64, /// The RISC-V architecture, 64-bit - sparc, /// The SPARC architecture, 32-bit - sparc64, /// The SPARC architecture, 64-bit - s390, /// The System/390 architecture, 32-bit - systemZ, /// The System Z architecture, 64-bit - hppa, /// The HP PA-RISC architecture, 32-bit - hppa64, /// The HP PA-RISC architecture, 64-bit - sh, /// The SuperH architecture, 32-bit - webAssembly, /// The WebAssembly virtual ISA (instruction set architecture), 32-bit - alpha, /// The Alpha architecture - unknown, /// Unknown - } - - /// The instruction set architecture that the program was compiled for. - version (X86) ISA instructionSetArchitecture = ISA.x86; - else version (X86_64) ISA instructionSetArchitecture = ISA.x86_64; - else version (ARM) ISA instructionSetArchitecture = ISA.arm; - else version (AArch64) ISA instructionSetArchitecture = ISA.aarch64; - else version (AsmJS) ISA instructionSetArchitecture = ISA.asmJS; - else version (AVR) ISA instructionSetArchitecture = ISA.avr; - else version (Epiphany) ISA instructionSetArchitecture = ISA.epiphany; - else version (PPC) ISA instructionSetArchitecture = ISA.ppc; - else version (PPC64) ISA instructionSetArchitecture = ISA.ppc64; - else version (IA64) ISA instructionSetArchitecture = ISA.ia64; - else version (MIPS32) ISA instructionSetArchitecture = ISA.mips32; - else version (MIPS64) ISA instructionSetArchitecture = ISA.mips64; - else version (MSP430) ISA instructionSetArchitecture = ISA.msp430; - else version (NVPTX) ISA instructionSetArchitecture = ISA.nvptx; - else version (NVPTX64) ISA instructionSetArchitecture = ISA.nvptx64; - else version (RISCV32) ISA instructionSetArchitecture = ISA.riscv32; - else version (RISCV64) ISA instructionSetArchitecture = ISA.riscv64; - else version (SPARC) ISA instructionSetArchitecture = ISA.sparc; - else version (SPARC64) ISA instructionSetArchitecture = ISA.sparc64; - else version (S390) ISA instructionSetArchitecture = ISA.s390; - else version (SystemZ) ISA instructionSetArchitecture = ISA.systemZ; - else version (HPPA) ISA instructionSetArchitecture = ISA.hppa; - else version (HPPA64) ISA instructionSetArchitecture = ISA.hppa64; - else version (SH) ISA instructionSetArchitecture = ISA.sh; - else version (WebAssembly) ISA instructionSetArchitecture = ISA.webAssembly; - else version (Alpha) ISA instructionSetArchitecture = ISA.alpha; - else ISA instructionSetArchitecture = ISA.unknown; -} - diff --git a/phobos/std/traits.d b/phobos/std/traits.d deleted file mode 100644 index 5ed37a1..0000000 --- a/phobos/std/traits.d +++ /dev/null @@ -1,9277 +0,0 @@ -// Written in the D programming language. - -/** - * Templates which extract information about types and symbols at compile time. - * - * $(SCRIPT inhibitQuickIndex = 1;) - * - * $(DIVC quickindex, - * $(BOOKTABLE , - * $(TR $(TH Category) $(TH Templates)) - * $(TR $(TD Symbol Name traits) $(TD - * $(LREF fullyQualifiedName) - * $(LREF mangledName) - * $(LREF moduleName) - * $(LREF packageName) - * )) - * $(TR $(TD Function traits) $(TD - * $(LREF isFunction) - * $(LREF arity) - * $(LREF functionAttributes) - * $(LREF hasFunctionAttributes) - * $(LREF functionLinkage) - * $(LREF FunctionTypeOf) - * $(LREF isSafe) - * $(LREF isUnsafe) - * $(LREF isFinal) - * $(LREF ParameterDefaults) - * $(LREF ParameterIdentifierTuple) - * $(LREF ParameterStorageClassTuple) - * $(LREF Parameters) - * $(LREF ReturnType) - * $(LREF SetFunctionAttributes) - * $(LREF variadicFunctionStyle) - * )) - * $(TR $(TD Aggregate Type traits) $(TD - * $(LREF BaseClassesTuple) - * $(LREF BaseTypeTuple) - * $(LREF classInstanceAlignment) - * $(LREF EnumMembers) - * $(LREF FieldNameTuple) - * $(LREF Fields) - * $(LREF hasAliasing) - * $(LREF hasElaborateAssign) - * $(LREF hasElaborateCopyConstructor) - * $(LREF hasElaborateDestructor) - * $(LREF hasElaborateMove) - * $(LREF hasIndirections) - * $(LREF hasMember) - * $(LREF hasStaticMember) - * $(LREF hasNested) - * $(LREF hasUnsharedAliasing) - * $(LREF InterfacesTuple) - * $(LREF isInnerClass) - * $(LREF isNested) - * $(LREF MemberFunctionsTuple) - * $(LREF RepresentationTypeTuple) - * $(LREF TemplateArgsOf) - * $(LREF TemplateOf) - * $(LREF TransitiveBaseTypeTuple) - * )) - * $(TR $(TD Type Conversion) $(TD - * $(LREF CommonType) - * $(LREF AllImplicitConversionTargets) - * $(LREF ImplicitConversionTargets) - * $(LREF CopyTypeQualifiers) - * $(LREF CopyConstness) - * $(LREF isAssignable) - * $(LREF isCovariantWith) - * $(LREF isImplicitlyConvertible) - * $(LREF isQualifierConvertible) - * )) - * $(TR $(TD Type Constructors) $(TD - * $(LREF InoutOf) - * $(LREF ConstOf) - * $(LREF SharedOf) - * $(LREF SharedInoutOf) - * $(LREF SharedConstOf) - * $(LREF SharedConstInoutOf) - * $(LREF ImmutableOf) - * $(LREF QualifierOf) - * )) - * $(TR $(TD Categories of types) $(TD - * $(LREF allSameType) - * $(LREF ifTestable) - * $(LREF isType) - * $(LREF isAggregateType) - * $(LREF isArray) - * $(LREF isAssociativeArray) - * $(LREF isAutodecodableString) - * $(LREF isBasicType) - * $(LREF isBoolean) - * $(LREF isBuiltinType) - * $(LREF isCopyable) - * $(LREF isDynamicArray) - * $(LREF isEqualityComparable) - * $(LREF isFloatingPoint) - * $(LREF isIntegral) - * $(LREF isNarrowString) - * $(LREF isConvertibleToString) - * $(LREF isNumeric) - * $(LREF isOrderingComparable) - * $(LREF isPointer) - * $(LREF isScalarType) - * $(LREF isSigned) - * $(LREF isSIMDVector) - * $(LREF isSomeChar) - * $(LREF isSomeString) - * $(LREF isStaticArray) - * $(LREF isUnsigned) - * )) - * $(TR $(TD Type behaviours) $(TD - * $(LREF isAbstractClass) - * $(LREF isAbstractFunction) - * $(LREF isCallable) - * $(LREF isDelegate) - * $(LREF isExpressions) - * $(LREF isFinalClass) - * $(LREF isFinalFunction) - * $(LREF isFunctionPointer) - * $(LREF isInstanceOf) - * $(LREF isIterable) - * $(LREF isMutable) - * $(LREF isSomeFunction) - * $(LREF isTypeTuple) - * )) - * $(TR $(TD General Types) $(TD - * $(LREF ForeachType) - * $(LREF KeyType) - * $(LREF Largest) - * $(LREF mostNegative) - * $(LREF OriginalType) - * $(LREF PointerTarget) - * $(LREF Signed) - * $(LREF Unconst) - * $(LREF Unshared) - * $(LREF Unqual) - * $(LREF Unsigned) - * $(LREF ValueType) - * $(LREF Promoted) - * )) - * $(TR $(TD Misc) $(TD - * $(LREF lvalueOf) - * $(LREF rvalueOf) - * $(LREF Select) - * $(LREF select) - * )) - * $(TR $(TD User-Defined Attributes) $(TD - * $(LREF hasUDA) - * $(LREF getUDAs) - * $(LREF getSymbolsByUDA) - * )) - * ) - * ) - * - * Copyright: Copyright The D Language Foundation 2005 - 2009. - * License: $(HTTP www.boost.org/LICENSE_1_0.txt, Boost License 1.0). - * Authors: $(HTTP digitalmars.com, Walter Bright), - * Tomasz Stachowiak (`isExpressions`), - * $(HTTP erdani.org, Andrei Alexandrescu), - * Shin Fujishiro, - * $(HTTP octarineparrot.com, Robert Clipsham), - * $(HTTP klickverbot.at, David Nadlinger), - * Kenji Hara, - * Shoichi Kato - * Source: $(PHOBOSSRC std/traits.d) - */ -/* Copyright The D Language Foundation 2005 - 2009. - * 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.traits; - -import std.meta : AliasSeq, allSatisfy, anySatisfy, ApplyLeft; - -// Legacy inheritance from std.typetuple -// See also: https://github.com/dlang/phobos/pull/5484#discussion_r122602797 -import std.meta : staticMapMeta = staticMap; -// TODO: find a way to trigger deprecation warnings -//deprecated("staticMap is part of std.meta: Please import std.meta") -alias staticMap = staticMapMeta; - -/////////////////////////////////////////////////////////////////////////////// -// Type lists -/////////////////////////////////////////////////////////////////////////////// - -private -{ - static if (is(ucent)) - { - alias CentTypeList = AliasSeq!(cent, ucent); - alias SignedCentTypeList = AliasSeq!(cent); - alias UnsignedCentTypeList = AliasSeq!(ucent); - } - else - { - alias CentTypeList = AliasSeq!(); - alias SignedCentTypeList = AliasSeq!(); - alias UnsignedCentTypeList = AliasSeq!(); - } - - alias IntegralTypeList = AliasSeq!(byte, ubyte, short, ushort, int, uint, long, ulong, CentTypeList); - alias SignedIntTypeList = AliasSeq!(byte, short, int, long, SignedCentTypeList); - alias UnsignedIntTypeList = AliasSeq!(ubyte, ushort, uint, ulong, UnsignedCentTypeList); - alias FloatingPointTypeList = AliasSeq!(float, double, real); - alias ImaginaryTypeList = AliasSeq!(ifloat, idouble, ireal); - alias ComplexTypeList = AliasSeq!(cfloat, cdouble, creal); - alias NumericTypeList = AliasSeq!(IntegralTypeList, FloatingPointTypeList); - alias CharTypeList = AliasSeq!(char, wchar, dchar); -} - -/** - * Params: - * T = The type to qualify - * Returns: - * `T` with the `inout` qualifier added. - */ -alias InoutOf(T) = inout(T); - -/// -@safe unittest -{ - static assert(is(InoutOf!(int) == inout int)); - static assert(is(InoutOf!(inout int) == inout int)); - static assert(is(InoutOf!(const int) == inout const int)); - static assert(is(InoutOf!(shared int) == inout shared int)); -} - -/** - * Params: - * T = The type to qualify - * Returns: - * `T` with the `const` qualifier added. - */ -alias ConstOf(T) = const(T); - -/// -@safe unittest -{ - static assert(is(ConstOf!(int) == const int)); - static assert(is(ConstOf!(const int) == const int)); - static assert(is(ConstOf!(inout int) == const inout int)); - static assert(is(ConstOf!(shared int) == const shared int)); -} - -/** - * Params: - * T = The type to qualify - * Returns: - * `T` with the `shared` qualifier added. - */ -alias SharedOf(T) = shared(T); - -/// -@safe unittest -{ - static assert(is(SharedOf!(int) == shared int)); - static assert(is(SharedOf!(shared int) == shared int)); - static assert(is(SharedOf!(inout int) == shared inout int)); - static assert(is(SharedOf!(immutable int) == shared immutable int)); -} - -/** - * Params: - * T = The type to qualify - * Returns: - * `T` with the `inout` and `shared` qualifiers added. - */ -alias SharedInoutOf(T) = shared(inout(T)); - -/// -@safe unittest -{ - static assert(is(SharedInoutOf!(int) == shared inout int)); - static assert(is(SharedInoutOf!(int) == inout shared int)); - - static assert(is(SharedInoutOf!(const int) == shared inout const int)); - static assert(is(SharedInoutOf!(immutable int) == shared inout immutable int)); -} - -/** - * Params: - * T = The type to qualify - * Returns: - * `T` with the `const` and `shared` qualifiers added. - */ -alias SharedConstOf(T) = shared(const(T)); - -/// -@safe unittest -{ - static assert(is(SharedConstOf!(int) == shared const int)); - static assert(is(SharedConstOf!(int) == const shared int)); - - static assert(is(SharedConstOf!(inout int) == shared inout const int)); - // immutable variables are implicitly shared and const - static assert(is(SharedConstOf!(immutable int) == immutable int)); -} - -/** - * Params: - * T = The type to qualify - * Returns: - * `T` with the `const`, `shared`, and `inout` qualifiers added. - */ -alias SharedConstInoutOf(T) = shared(const(inout(T))); - -/// -@safe unittest -{ - static assert(is(SharedConstInoutOf!(int) == shared const inout int)); - static assert(is(SharedConstInoutOf!(int) == const shared inout int)); - static assert(is(SharedConstInoutOf!(inout int) == shared inout const int)); - // immutable variables are implicitly shared and const - static assert(is(SharedConstInoutOf!(immutable int) == immutable int)); -} - -/** - * Params: - * T = The type to qualify - * Returns: - * `T` with the `immutable` qualifier added. - */ -alias ImmutableOf(T) = immutable(T); - -/// -@safe unittest -{ - static assert(is(ImmutableOf!(int) == immutable int)); - static assert(is(ImmutableOf!(const int) == immutable int)); - static assert(is(ImmutableOf!(inout int) == immutable int)); - static assert(is(ImmutableOf!(shared int) == immutable int)); -} - -@safe unittest -{ - static assert(is( InoutOf!int == inout int)); - static assert(is( ConstOf!int == const int)); - static assert(is( SharedOf!int == shared int)); - static assert(is(SharedInoutOf!int == shared inout int)); - static assert(is(SharedConstOf!int == shared const int)); - static assert(is( ImmutableOf!int == immutable int)); -} - -/** - * Gives a template that can be used to apply the same - * attributes that are on the given type `T`. E.g. passing - * `inout shared int` will return `SharedInoutOf`. - * - * Params: - * T = the type to check qualifiers from - * Returns: - * The qualifier template from the given type `T` - */ -template QualifierOf(T) -{ - static if (is(immutable T == T)) - { - alias QualifierOf = ImmutableOf; - } - else - { - private enum quals = is(const T == T) | (is(inout T == T) << 1) | (is(shared T == T) << 2); - static if (quals == 0) { import std.meta : Alias; alias QualifierOf = Alias; } - else static if (quals == 1) alias QualifierOf = ConstOf; - else static if (quals == 2) alias QualifierOf = InoutOf; - else static if (quals == 3) alias QualifierOf = ConstInoutOf; - else static if (quals == 4) alias QualifierOf = SharedOf; - else static if (quals == 5) alias QualifierOf = SharedConstOf; - else static if (quals == 6) alias QualifierOf = SharedInoutOf; - else alias QualifierOf = SharedConstInoutOf; - } -} - -/// -@safe unittest -{ - static assert(__traits(isSame, QualifierOf!(shared const inout int), SharedConstInoutOf)); - static assert(__traits(isSame, QualifierOf!(immutable int), ImmutableOf)); - static assert(__traits(isSame, QualifierOf!(shared int), SharedOf)); - static assert(__traits(isSame, QualifierOf!(shared inout int), SharedInoutOf)); - import std.meta : Alias; - static assert(__traits(isSame, QualifierOf!(int), Alias)); -} - -@safe unittest -{ - alias Qual1 = QualifierOf!( int); static assert(is(Qual1!long == long)); - alias Qual2 = QualifierOf!( inout int); static assert(is(Qual2!long == inout long)); - alias Qual3 = QualifierOf!( const int); static assert(is(Qual3!long == const long)); - alias Qual4 = QualifierOf!(shared int); static assert(is(Qual4!long == shared long)); - alias Qual5 = QualifierOf!(shared inout int); static assert(is(Qual5!long == shared inout long)); - alias Qual6 = QualifierOf!(shared const int); static assert(is(Qual6!long == shared const long)); - alias Qual7 = QualifierOf!( immutable int); static assert(is(Qual7!long == immutable long)); -} - -version (StdUnittest) -{ - import std.meta : Alias; - alias TypeQualifierList = AliasSeq!(Alias, ConstOf, SharedOf, SharedConstOf, ImmutableOf); - - struct SubTypeOf(T) - { - T val; - alias val this; - } -} - -private alias parentOf(alias sym) = Identity!(__traits(parent, sym)); -private alias parentOf(alias sym : T!Args, alias T, Args...) = Identity!(__traits(parent, T)); - -/** - * Get the full package name for the given symbol. - */ -template packageName(alias T) -{ - import std.algorithm.searching : startsWith; - - enum bool isNotFunc = !isSomeFunction!(T); - - static if (__traits(compiles, parentOf!T)) - enum parent = packageName!(parentOf!T); - else - enum string parent = null; - - static if (isNotFunc && T.stringof.startsWith("package ")) - enum packageName = (parent.length ? parent ~ '.' : "") ~ T.stringof[8 .. $]; - else static if (parent) - enum packageName = parent; - else - static assert(false, T.stringof ~ " has no parent"); -} - -/// -@safe unittest -{ - static assert(packageName!packageName == "std"); -} - -@safe unittest -{ - import std.array; - - static assert(packageName!std == "std"); - static assert(packageName!(std.traits) == "std"); // this module - static assert(packageName!packageName == "std"); // symbol in this module - static assert(packageName!(std.array) == "std"); // other module from same package - - import core.sync.barrier; // local import - static assert(packageName!core == "core"); - static assert(packageName!(core.sync) == "core.sync"); - static assert(packageName!Barrier == "core.sync"); - - struct X12287(T) { T i; } - static assert(packageName!(X12287!int.i) == "std"); -} - -version (none) @safe unittest //Please uncomment me when changing packageName to test global imports -{ - import core.sync.barrier; // global import - static assert(packageName!core == "core"); - static assert(packageName!(core.sync) == "core.sync"); - static assert(packageName!Barrier == "core.sync"); -} - -/// -@safe unittest -{ - static assert(packageName!moduleName == "std"); -} - -// https://issues.dlang.org/show_bug.cgi?id=13741 -@safe unittest -{ - import std.ascii : isWhite; - static assert(packageName!(isWhite) == "std"); - - struct Foo{void opCall(int){}} - static assert(packageName!(Foo.opCall) == "std"); - - @property void function(int) vf; - static assert(packageName!(vf) == "std"); -} - -/** - * Get the module name (including package) for the given symbol. - */ -template moduleName(alias T) -{ - import std.algorithm.searching : startsWith; - - enum bool isNotFunc = !isSomeFunction!(T); - - static if (isNotFunc) - static assert(!T.stringof.startsWith("package "), - "cannot get the module name for a package"); - - static if (isNotFunc && T.stringof.startsWith("module ")) - { - static if (__traits(compiles, packageName!T)) - enum packagePrefix = packageName!T ~ '.'; - else - enum packagePrefix = ""; - - enum moduleName = packagePrefix ~ T.stringof[7..$]; - } - else - alias moduleName = moduleName!(parentOf!T); // If you use enum, it will cause compiler ICE -} - -/// -@safe unittest -{ - static assert(moduleName!moduleName == "std.traits"); -} - -@safe unittest -{ - import std.array; - - static assert(!__traits(compiles, moduleName!std)); - static assert(moduleName!(std.traits) == "std.traits"); // this module - static assert(moduleName!moduleName == "std.traits"); // symbol in this module - static assert(moduleName!(std.array) == "std.array"); // other module - static assert(moduleName!(std.array.array) == "std.array"); // symbol in other module - - import core.sync.barrier; // local import - static assert(!__traits(compiles, moduleName!(core.sync))); - static assert(moduleName!(core.sync.barrier) == "core.sync.barrier"); - static assert(moduleName!Barrier == "core.sync.barrier"); - - struct X12287(T) { T i; } - static assert(moduleName!(X12287!int.i) == "std.traits"); -} - -// https://issues.dlang.org/show_bug.cgi?id=13741 -@safe unittest -{ - import std.ascii : isWhite; - static assert(moduleName!(isWhite) == "std.ascii"); - - struct Foo{void opCall(int){}} - static assert(moduleName!(Foo.opCall) == "std.traits"); - - @property void function(int) vf; - static assert(moduleName!(vf) == "std.traits"); -} - -version (none) @safe unittest //Please uncomment me when changing moduleName to test global imports -{ - import core.sync.barrier; // global import - static assert(!__traits(compiles, moduleName!(core.sync))); - static assert(moduleName!(core.sync.barrier) == "core.sync.barrier"); - static assert(moduleName!Barrier == "core.sync.barrier"); -} - -/*** - * Get the fully qualified name of a type or a symbol. Can act as an intelligent type/symbol to string converter. - -Example: ------------------ -module myModule; -struct MyStruct {} -static assert(fullyQualifiedName!(const MyStruct[]) == "const(myModule.MyStruct[])"); ------------------ -*/ -enum fullyQualifiedName(T) = fqnType!(T, false, false, false, false); - -/// ditto -enum fullyQualifiedName(alias T) = fqnSym!(T); - -/// -@safe unittest -{ - static assert(fullyQualifiedName!fullyQualifiedName == "std.traits.fullyQualifiedName"); -} - -version (StdUnittest) -{ - // Used for both fqnType and fqnSym unittests - private struct QualifiedNameTests - { - struct Inner - { - bool value; - } - - ref const(Inner[string]) func( ref Inner var1, lazy scope string var2 ); - ref const(Inner[string]) retfunc( return ref Inner var1 ); - Inner inoutFunc(inout Inner) inout; - shared(const(Inner[string])[]) data; - const Inner delegate(double, string) @safe nothrow deleg; - inout(int) delegate(inout int) inout inoutDeleg; - Inner function(out double, string) funcPtr; - extern(C) Inner function(double, string) cFuncPtr; - - extern(C) void cVarArg(int, ...); - void dVarArg(...); - void dVarArg2(int, ...); - void typesafeVarArg(int[] ...); - - Inner[] array; - Inner[16] sarray; - Inner[Inner] aarray; - const(Inner[const(Inner)]) qualAarray; - - shared(immutable(Inner) delegate(ref double, scope string) const shared @trusted nothrow) attrDeleg; - - struct Data(T) { int x; } - void tfunc(T...)(T args) {} - - template Inst(alias A) { int x; } - - class Test12309(T, int x, string s) {} - } - - private enum QualifiedEnum - { - a = 42 - } -} - -private template fqnSym(alias T : X!A, alias X, A...) -{ - template fqnTuple(T...) - { - static if (T.length == 0) - enum fqnTuple = ""; - else static if (T.length == 1) - { - static if (isExpressionTuple!T) - enum fqnTuple = T[0].stringof; - else - enum fqnTuple = fullyQualifiedName!(T[0]); - } - else - enum fqnTuple = fqnTuple!(T[0]) ~ ", " ~ fqnTuple!(T[1 .. $]); - } - - enum fqnSym = - fqnSym!(__traits(parent, X)) ~ - '.' ~ __traits(identifier, X) ~ "!(" ~ fqnTuple!A ~ ")"; -} - -private template fqnSym(alias T) -{ - static if (__traits(compiles, __traits(parent, T)) && !__traits(isSame, T, __traits(parent, T))) - enum parentPrefix = fqnSym!(__traits(parent, T)) ~ "."; - else - enum parentPrefix = null; - - static string adjustIdent(string s) - { - import std.algorithm.searching : findSplit, skipOver; - - if (s.skipOver("package ") || s.skipOver("module ")) - return s; - return s.findSplit("(")[0]; - } - enum fqnSym = parentPrefix ~ adjustIdent(__traits(identifier, T)); -} - -@safe unittest -{ - alias fqn = fullyQualifiedName; - - // Make sure those 2 are the same - static assert(fqnSym!fqn == fqn!fqn); - - static assert(fqn!fqn == "std.traits.fullyQualifiedName"); - - alias qnTests = QualifiedNameTests; - enum prefix = "std.traits.QualifiedNameTests."; - static assert(fqn!(qnTests.Inner) == prefix ~ "Inner"); - static assert(fqn!(qnTests.func) == prefix ~ "func"); - static assert(fqn!(qnTests.Data!int) == prefix ~ "Data!(int)"); - static assert(fqn!(qnTests.Data!int.x) == prefix ~ "Data!(int).x"); - static assert(fqn!(qnTests.tfunc!(int[])) == prefix ~ "tfunc!(int[])"); - static assert(fqn!(qnTests.Inst!(Object)) == prefix ~ "Inst!(object.Object)"); - static assert(fqn!(qnTests.Inst!(Object).x) == prefix ~ "Inst!(object.Object).x"); - - static assert(fqn!(qnTests.Test12309!(int, 10, "str")) - == prefix ~ "Test12309!(int, 10, \"str\")"); - - import core.sync.barrier; - static assert(fqn!Barrier == "core.sync.barrier.Barrier"); -} - -@safe unittest -{ - struct TemplatedStruct() - { - enum foo = 0; - } - alias TemplatedStructAlias = TemplatedStruct; - assert("TemplatedStruct.foo" == fullyQualifiedName!(TemplatedStructAlias!().foo)); -} - -private template fqnType(T, - bool alreadyConst, bool alreadyImmutable, bool alreadyShared, bool alreadyInout) -{ - // Convenience tags - enum { - _const = 0, - _immutable = 1, - _shared = 2, - _inout = 3 - } - - alias qualifiers = AliasSeq!(is(T == const), is(T == immutable), is(T == shared), is(T == inout)); - alias noQualifiers = AliasSeq!(false, false, false, false); - - string storageClassesString(uint psc)() @property - { - import std.conv : text; - - alias PSC = ParameterStorageClass; - - return text( - psc & PSC.scope_ ? "scope " : "", - psc & PSC.return_ ? "return " : "", - psc & PSC.in_ ? "in " : "", - psc & PSC.out_ ? "out " : "", - psc & PSC.ref_ ? "ref " : "", - psc & PSC.lazy_ ? "lazy " : "", - ); - } - - string parametersTypeString(T)() @property - { - alias parameters = Parameters!(T); - alias parameterStC = ParameterStorageClassTuple!(T); - - enum variadic = variadicFunctionStyle!T; - static if (variadic == Variadic.no) - enum variadicStr = ""; - else static if (variadic == Variadic.c) - enum variadicStr = ", ..."; - else static if (variadic == Variadic.d) - enum variadicStr = parameters.length ? ", ..." : "..."; - else static if (variadic == Variadic.typesafe) - enum variadicStr = " ..."; - else - static assert(0, "New variadic style has been added, please update fullyQualifiedName implementation"); - - static if (parameters.length) - { - import std.algorithm.iteration : map; - import std.array : join; - import std.meta : staticMap; - import std.range : zip; - - string result = join( - map!(a => (a[0] ~ a[1]))( - zip([staticMap!(storageClassesString, parameterStC)], - [staticMap!(fullyQualifiedName, parameters)]) - ), - ", " - ); - - return result ~= variadicStr; - } - else - return variadicStr; - } - - string linkageString(T)() @property - { - enum linkage = functionLinkage!T; - - if (linkage != "D") - return "extern(" ~ linkage ~ ") "; - else - return ""; - } - - string functionAttributeString(T)() @property - { - alias FA = FunctionAttribute; - enum attrs = functionAttributes!T; - - static if (attrs == FA.none) - return ""; - else - return - (attrs & FA.pure_ ? " pure" : "") - ~ (attrs & FA.nothrow_ ? " nothrow" : "") - ~ (attrs & FA.ref_ ? " ref" : "") - ~ (attrs & FA.property ? " @property" : "") - ~ (attrs & FA.trusted ? " @trusted" : "") - ~ (attrs & FA.safe ? " @safe" : "") - ~ (attrs & FA.nogc ? " @nogc" : "") - ~ (attrs & FA.return_ ? " return" : "") - ~ (attrs & FA.live ? " @live" : ""); - } - - string addQualifiers(string typeString, - bool addConst, bool addImmutable, bool addShared, bool addInout) - { - auto result = typeString; - if (addShared) - { - result = "shared(" ~ result ~")"; - } - if (addConst || addImmutable || addInout) - { - result = (addConst ? "const" : addImmutable ? "immutable" : "inout") - ~ "(" ~ result ~ ")"; - } - return result; - } - - // Convenience template to avoid copy-paste - template chain(string current) - { - enum chain = addQualifiers(current, - qualifiers[_const] && !alreadyConst, - qualifiers[_immutable] && !alreadyImmutable, - qualifiers[_shared] && !alreadyShared, - qualifiers[_inout] && !alreadyInout); - } - - static if (is(T == string)) - { - enum fqnType = "string"; - } - else static if (is(T == wstring)) - { - enum fqnType = "wstring"; - } - else static if (is(T == dstring)) - { - enum fqnType = "dstring"; - } - else static if (is(T == typeof(null))) - { - enum fqnType = "typeof(null)"; - } - else static if (isBasicType!T && !is(T == enum)) - { - enum fqnType = chain!((Unqual!T).stringof); - } - else static if (isAggregateType!T || is(T == enum)) - { - enum fqnType = chain!(fqnSym!T); - } - else static if (isStaticArray!T) - { - import std.conv : to; - enum fqnType = chain!( - fqnType!(typeof(T.init[0]), qualifiers) ~ "[" ~ to!string(T.length) ~ "]" - ); - } - else static if (isArray!T) - { - enum fqnType = chain!( - fqnType!(typeof(T.init[0]), qualifiers) ~ "[]" - ); - } - else static if (isAssociativeArray!T) - { - enum fqnType = chain!( - fqnType!(ValueType!T, qualifiers) ~ '[' ~ fqnType!(KeyType!T, noQualifiers) ~ ']' - ); - } - else static if (isSomeFunction!T) - { - static if (is(T F == delegate)) - { - enum qualifierString = - (is(F == shared) ? " shared" : "") - ~ (is(F == inout) ? " inout" : - is(F == immutable) ? " immutable" : - is(F == const) ? " const" : ""); - enum fqnType = chain!( - linkageString!T - ~ fqnType!(ReturnType!T, noQualifiers) - ~ " delegate(" ~ parametersTypeString!(T) ~ ")" - ~ functionAttributeString!T - ~ qualifierString - ); - } - else - { - enum fqnType = chain!( - linkageString!T - ~ fqnType!(ReturnType!T, noQualifiers) - ~ (isFunctionPointer!T ? " function(" : "(") - ~ parametersTypeString!(T) ~ ")" - ~ functionAttributeString!T - ); - } - } - else static if (is(T == U*, U)) - { - enum fqnType = chain!( - fqnType!(U, qualifiers) ~ "*" - ); - } - else static if (is(T : __vector(V[N]), V, size_t N)) - { - import std.conv : to; - enum fqnType = chain!( - "__vector(" ~ fqnType!(V, qualifiers) ~ "[" ~ N.to!string ~ "])" - ); - } - else - // In case something is forgotten - static assert(0, "Unrecognized type " ~ T.stringof ~ ", can't convert to fully qualified string"); -} - -@safe unittest -{ - import std.format : format; - alias fqn = fullyQualifiedName; - - // Verify those 2 are the same for simple case - alias Ambiguous = const(QualifiedNameTests.Inner); - static assert(fqn!Ambiguous == fqnType!(Ambiguous, false, false, false, false)); - - // Main tests - enum inner_name = "std.traits.QualifiedNameTests.Inner"; - with (QualifiedNameTests) - { - // Special cases - static assert(fqn!(string) == "string"); - static assert(fqn!(wstring) == "wstring"); - static assert(fqn!(dstring) == "dstring"); - static assert(fqn!(typeof(null)) == "typeof(null)"); - static assert(fqn!(void) == "void"); - static assert(fqn!(const(void)) == "const(void)"); - static assert(fqn!(shared(void)) == "shared(void)"); - static assert(fqn!(shared const(void)) == "const(shared(void))"); - static assert(fqn!(shared inout(void)) == "inout(shared(void))"); - static assert(fqn!(shared inout const(void)) == "const(shared(void))"); - static assert(fqn!(inout(void)) == "inout(void)"); - static assert(fqn!(inout const(void)) == "const(void)"); - static assert(fqn!(immutable(void)) == "immutable(void)"); - - // Basic qualified name - static assert(fqn!(Inner) == inner_name); - static assert(fqn!(QualifiedEnum) == "std.traits.QualifiedEnum"); // type - static assert(fqn!(QualifiedEnum.a) == "std.traits.QualifiedEnum.a"); // symbol - - // Array types - static assert(fqn!(typeof(array)) == format("%s[]", inner_name)); - static assert(fqn!(typeof(sarray)) == format("%s[16]", inner_name)); - static assert(fqn!(typeof(aarray)) == format("%s[%s]", inner_name, inner_name)); - - // qualified key for AA - static assert(fqn!(typeof(qualAarray)) == format("const(%s[const(%s)])", inner_name, inner_name)); - - // Qualified composed data types - static assert(fqn!(typeof(data)) == format("shared(const(%s[string])[])", inner_name)); - - // Function types + function attributes - static assert(fqn!(typeof(func)) == format("const(%s[string])(ref %s, scope lazy string) ref", - inner_name, inner_name)); - static assert(fqn!(typeof(retfunc)) == format("const(%s[string])(return %s) ref", inner_name, inner_name)); - static assert(fqn!(typeof(inoutFunc)) == format("inout(%s(inout(%s)))", inner_name, inner_name)); - static assert(fqn!(typeof(deleg)) == format("const(%s delegate(double, string) nothrow @safe)", inner_name)); - static assert(fqn!(typeof(inoutDeleg)) == "inout(int) delegate(inout(int)) inout"); - static assert(fqn!(typeof(funcPtr)) == format("%s function(out double, string)", inner_name)); - static assert(fqn!(typeof(cFuncPtr)) == format("extern(C) %s function(double, string)", inner_name)); - - // Delegate type with qualified function type - static assert(fqn!(typeof(attrDeleg)) == format("shared(immutable(%s) "~ - "delegate(ref double, scope string) nothrow @trusted shared const)", inner_name)); - - // Variable argument function types - static assert(fqn!(typeof(cVarArg)) == "extern(C) void(int, ...)"); - static assert(fqn!(typeof(dVarArg)) == "void(...)"); - static assert(fqn!(typeof(dVarArg2)) == "void(int, ...)"); - static assert(fqn!(typeof(typesafeVarArg)) == "void(int[] ...)"); - - // SIMD vector - static if (is(__vector(float[4]))) - { - static assert(fqn!(__vector(float[4])) == "__vector(float[4])"); - } - } -} - -/*** - * Get the type of the return value from a function, - * a pointer to function, a delegate, a struct - * with an opCall, a pointer to a struct with an opCall, - * or a class with an `opCall`. Please note that $(D_KEYWORD ref) - * is not part of a type, but the attribute of the function - * (see template $(LREF functionAttributes)). - * - * $(NOTE To reduce template instantiations, consider instead using - * $(D typeof(() { return func(args); } ())) if the argument types are known or - * $(D static if (is(typeof(func) Ret == return))) if only that basic test is needed.) - */ -template ReturnType(alias func) -if (isCallable!func) -{ - static if (is(FunctionTypeOf!func R == return)) - alias ReturnType = R; - else - static assert(0, "argument has no return type"); -} - -/// -@safe unittest -{ - int foo(); - ReturnType!foo x; // x is declared as int -} - -@safe unittest -{ - struct G - { - int opCall (int i) { return 1;} - } - - alias ShouldBeInt = ReturnType!G; - static assert(is(ShouldBeInt == int)); - - G g; - static assert(is(ReturnType!g == int)); - - G* p; - alias pg = ReturnType!p; - static assert(is(pg == int)); - - class C - { - int opCall (int i) { return 1;} - } - - static assert(is(ReturnType!C == int)); - - C c; - static assert(is(ReturnType!c == int)); - - class Test - { - int prop() @property { return 0; } - } - alias R_Test_prop = ReturnType!(Test.prop); - static assert(is(R_Test_prop == int)); - - alias R_dglit = ReturnType!((int a) { return a; }); - static assert(is(R_dglit == int)); -} - -/*** -Get, as a tuple, the types of the parameters to a function, a pointer -to function, a delegate, a struct with an `opCall`, a pointer to a -struct with an `opCall`, or a class with an `opCall`. -*/ -template Parameters(alias func) -if (isCallable!func) -{ - static if (is(FunctionTypeOf!func P == function)) - alias Parameters = P; - else - static assert(0, "argument has no parameters"); -} - -/// -@safe unittest -{ - int foo(int, long); - void bar(Parameters!foo); // declares void bar(int, long); - void abc(Parameters!foo[1]); // declares void abc(long); -} - -/** - * Alternate name for $(LREF Parameters), kept for legacy compatibility. - */ -alias ParameterTypeTuple = Parameters; - -@safe unittest -{ - int foo(int i, bool b) { return 0; } - static assert(is(ParameterTypeTuple!foo == AliasSeq!(int, bool))); - static assert(is(ParameterTypeTuple!(typeof(&foo)) == AliasSeq!(int, bool))); - - struct S { real opCall(real r, int i) { return 0.0; } } - S s; - static assert(is(ParameterTypeTuple!S == AliasSeq!(real, int))); - static assert(is(ParameterTypeTuple!(S*) == AliasSeq!(real, int))); - static assert(is(ParameterTypeTuple!s == AliasSeq!(real, int))); - - class Test - { - int prop() @property { return 0; } - } - alias P_Test_prop = ParameterTypeTuple!(Test.prop); - static assert(P_Test_prop.length == 0); - - alias P_dglit = ParameterTypeTuple!((int a){}); - static assert(P_dglit.length == 1); - static assert(is(P_dglit[0] == int)); -} - -/** -Returns the number of arguments of function `func`. -arity is undefined for variadic functions. -*/ -template arity(alias func) -if (isCallable!func && variadicFunctionStyle!func == Variadic.no) -{ - enum size_t arity = Parameters!func.length; -} - -/// -@safe unittest -{ - void foo(){} - static assert(arity!foo == 0); - void bar(uint){} - static assert(arity!bar == 1); - void variadicFoo(uint...){} - static assert(!__traits(compiles, arity!variadicFoo)); -} - -// https://issues.dlang.org/show_bug.cgi?id=11389 -@safe unittest -{ - alias TheType = size_t function( string[] ); - static assert(arity!TheType == 1); -} - -/** -Get a tuple of the storage classes of a function's parameters. -Params: - func = function symbol or type of function, delegate, or pointer to function -Returns: - A tuple of ParameterStorageClass bits - */ -enum ParameterStorageClass : uint -{ - /** - * These flags can be bitwise OR-ed together to represent complex storage - * class. - */ - none = 0x00, - in_ = 0x01, /// ditto - ref_ = 0x02, /// ditto - out_ = 0x04, /// ditto - lazy_ = 0x08, /// ditto - scope_ = 0x10, /// ditto - return_ = 0x20, /// ditto -} - -/// ditto -template ParameterStorageClassTuple(alias func) -if (isCallable!func) -{ - alias Func = FunctionTypeOf!func; - - static if (is(Func PT == __parameters)) - { - alias ParameterStorageClassTuple = AliasSeq!(); - static foreach (i; 0 .. PT.length) - { - ParameterStorageClassTuple = AliasSeq!(ParameterStorageClassTuple, - extractParameterStorageClassFlags!(__traits(getParameterStorageClasses, Func, i))); - } - } - else - { - static assert(0, func.stringof, " is not a function"); - alias ParameterStorageClassTuple = AliasSeq!(); - } -} - -/// -@safe unittest -{ - alias STC = ParameterStorageClass; // shorten the enum name - - void func(ref int ctx, out real result, in real param, void* ptr) - { - } - alias pstc = ParameterStorageClassTuple!func; - static assert(pstc.length == 4); // number of parameters - static assert(pstc[0] == STC.ref_); - static assert(pstc[1] == STC.out_); - version (none) - { - // TODO: When the DMD PR (dlang/dmd#11474) gets merged, - // remove the versioning and the second test - static assert(pstc[2] == STC.in_); - // This is the current behavior, before `in` is fixed to not be an alias - static assert(pstc[2] == STC.scope_); - } - static assert(pstc[3] == STC.none); -} - -/** -Convert the result of $(DDSUBLINK spec/traits, getParameterStorageClasses, `__traits(getParameterStorageClasses)`) -to $(LREF ParameterStorageClass) `enum`s. - -Params: - Attribs = The return value of `__traits(getParameterStorageClasses)` -Returns: - The bitwise OR of the equivalent $(LREF ParameterStorageClass) `enum`s. - */ -template extractParameterStorageClassFlags(Attribs...) -{ - enum ParameterStorageClass extractParameterStorageClassFlags = () - { - auto result = ParameterStorageClass.none; - static if (Attribs.length > 0) - { - static foreach (attrib; Attribs) - { - final switch (attrib) with (ParameterStorageClass) - { - case "scope": result |= scope_; break; - case "in": result |= in_; break; - case "out": result |= out_; break; - case "ref": result |= ref_; break; - case "lazy": result |= lazy_; break; - case "return": result |= return_; break; - } - } - /* Mimic behavor of original version of ParameterStorageClassTuple() - * to avoid breaking existing code. - */ - if (result == (ParameterStorageClass.ref_ | ParameterStorageClass.return_)) - result = ParameterStorageClass.return_; - } - return result; - }(); -} - -/// -@safe unittest -{ - static void func(ref int ctx, out real result); - - enum param1 = extractParameterStorageClassFlags!( - __traits(getParameterStorageClasses, func, 0) - ); - static assert(param1 == ParameterStorageClass.ref_); - - enum param2 = extractParameterStorageClassFlags!( - __traits(getParameterStorageClasses, func, 1) - ); - static assert(param2 == ParameterStorageClass.out_); - - enum param3 = extractParameterStorageClassFlags!( - __traits(getParameterStorageClasses, func, 0), - __traits(getParameterStorageClasses, func, 1) - ); - static assert(param3 == (ParameterStorageClass.ref_ | ParameterStorageClass.out_)); -} - -@safe unittest -{ - alias STC = ParameterStorageClass; - - void noparam() {} - static assert(ParameterStorageClassTuple!noparam.length == 0); - - ref int test(scope int*, ref int, out int, lazy int, int, return ref int i) { return i; } - alias test_pstc = ParameterStorageClassTuple!test; - static assert(test_pstc.length == 6); - static assert(test_pstc[0] == STC.scope_); - static assert(test_pstc[1] == STC.ref_); - static assert(test_pstc[2] == STC.out_); - static assert(test_pstc[3] == STC.lazy_); - static assert(test_pstc[4] == STC.none); - static assert(test_pstc[5] == STC.return_); - - interface Test - { - void test_const(int) const; - void test_sharedconst(int) shared const; - } - Test testi; - - alias test_const_pstc = ParameterStorageClassTuple!(Test.test_const); - static assert(test_const_pstc.length == 1); - static assert(test_const_pstc[0] == STC.none); - - alias test_sharedconst_pstc = ParameterStorageClassTuple!(testi.test_sharedconst); - static assert(test_sharedconst_pstc.length == 1); - static assert(test_sharedconst_pstc[0] == STC.none); - - alias dglit_pstc = ParameterStorageClassTuple!((ref int a) {}); - static assert(dglit_pstc.length == 1); - static assert(dglit_pstc[0] == STC.ref_); - - // https://issues.dlang.org/show_bug.cgi?id=9317 - static inout(int) func(inout int param) { return param; } - static assert(ParameterStorageClassTuple!(typeof(func))[0] == STC.none); -} - -@safe unittest -{ - // https://issues.dlang.org/show_bug.cgi?id=14253 - static struct Foo { - ref Foo opAssign(ref Foo rhs) return { return this; } - } - - alias tup = ParameterStorageClassTuple!(__traits(getOverloads, Foo, "opAssign")[0]); -} - - -/** -Get, as a tuple, the identifiers of the parameters to a function symbol. - */ -template ParameterIdentifierTuple(alias func) -if (isCallable!func) -{ - static if (is(FunctionTypeOf!func PT == __parameters)) - { - alias ParameterIdentifierTuple = AliasSeq!(); - static foreach (i; 0 .. PT.length) - { - static if (!isFunctionPointer!func && !isDelegate!func - // Unnamed parameters yield CT error. - && is(typeof(__traits(identifier, PT[i .. i+1]))) - // Filter out unnamed args, which look like (Type) instead of (Type name). - && PT[i].stringof != PT[i .. i+1].stringof[1..$-1]) - { - ParameterIdentifierTuple = AliasSeq!(ParameterIdentifierTuple, - __traits(identifier, PT[i .. i+1])); - } - else - { - ParameterIdentifierTuple = AliasSeq!(ParameterIdentifierTuple, ""); - } - } - } - else - { - static assert(0, func.stringof ~ " is not a function"); - // avoid pointless errors - alias ParameterIdentifierTuple = AliasSeq!(); - } -} - -/// -@safe unittest -{ - int foo(int num, string name, int); - static assert([ParameterIdentifierTuple!foo] == ["num", "name", ""]); -} - -// https://issues.dlang.org/show_bug.cgi?id=19456 -@safe unittest -{ - struct SomeType {} - void foo(SomeType); - void bar(int); - static assert([ParameterIdentifierTuple!foo] == [""]); - static assert([ParameterIdentifierTuple!bar] == [""]); -} - -@safe unittest -{ - alias PIT = ParameterIdentifierTuple; - - void bar(int num, string name, int[] array){} - static assert([PIT!bar] == ["num", "name", "array"]); - - // might be changed in the future? - void function(int num, string name) fp; - static assert([PIT!fp] == ["", ""]); - - // might be changed in the future? - void delegate(int num, string name, int[long] aa) dg; - static assert([PIT!dg] == ["", "", ""]); - - interface Test - { - @property string getter(); - @property void setter(int a); - Test method(int a, long b, string c); - } - static assert([PIT!(Test.getter)] == []); - static assert([PIT!(Test.setter)] == ["a"]); - static assert([PIT!(Test.method)] == ["a", "b", "c"]); - -/+ - // depends on internal - void baw(int, string, int[]){} - static assert([PIT!baw] == ["_param_0", "_param_1", "_param_2"]); - - // depends on internal - void baz(AliasSeq!(int, string, int[]) args){} - static assert([PIT!baz] == ["_param_0", "_param_1", "_param_2"]); -+/ -} - - -/** -Get, as a tuple, the default values of the parameters to a function symbol. -If a parameter doesn't have the default value, `void` is returned instead. - */ -template ParameterDefaults(alias func) -if (isCallable!func) -{ - alias param_names = ParameterIdentifierTuple!func; - static if (is(FunctionTypeOf!(func) PT == __parameters)) - { - template Get(size_t i) - { - // `PT[i .. i+1]` declares a parameter with an arbitrary name. - // To avoid a name clash, generate local names that are distinct - // from the parameter name, and mix them in. - enum name = param_names[i]; - enum args = "args" ~ (name == "args" ? "_" : ""); - enum val = "val" ~ (name == "val" ? "_" : ""); - enum ptr = "ptr" ~ (name == "ptr" ? "_" : ""); - enum hasDefaultArg = mixin("(PT[i .. i+1] ", args, ") => true"); - static if (is(typeof(hasDefaultArg()))) - { - enum get = mixin("(return scope PT[i .. i+1] ", args, ") - { - // If the parameter is lazy, we force it to be evaluated - // like this. - auto ", val, " = ", args, "[0]; - auto ", ptr, " = &", val, "; - return *", ptr, "; - }"); - enum Get = get(); - } - else - alias Get = void; - // If default arg doesn't exist, returns void instead. - } - alias ParameterDefaults = AliasSeq!(); - static foreach (i; 0 .. PT.length) - { - ParameterDefaults = AliasSeq!(ParameterDefaults, - Get!i); - } - } - else - { - static assert(0, func.stringof ~ " is not a function"); - // avoid pointless errors - alias ParameterDefaults = AliasSeq!(); - } -} - -/// -@safe unittest -{ - int foo(int num, string name = "hello", int[] = [1,2,3], lazy int x = 0); - static assert(is(ParameterDefaults!foo[0] == void)); - static assert( ParameterDefaults!foo[1] == "hello"); - static assert( ParameterDefaults!foo[2] == [1,2,3]); - static assert( ParameterDefaults!foo[3] == 0); -} - -// https://issues.dlang.org/show_bug.cgi?id=17192 -@safe unittest -{ - static void func(int i, int PT, int __pd_value, int __pd_val, int __args, - int name, int args, int val, int ptr, int args_, int val_, int ptr_) - { - } - alias Voids = ParameterDefaults!func; - static assert(Voids.length == 12); - static foreach (V; Voids) static assert(is(V == void)); -} - -// https://issues.dlang.org/show_bug.cgi?id=20182 -@safe pure nothrow @nogc unittest -{ - struct S - { - this(ref S) {} - } - - static assert(__traits(compiles, ParameterDefaults!(S.__ctor))); -} - -/** - * Alternate name for $(LREF ParameterDefaults), kept for legacy compatibility. - */ -alias ParameterDefaultValueTuple = ParameterDefaults; - -@safe unittest -{ - alias PDVT = ParameterDefaultValueTuple; - - void bar(int n = 1, string s = "hello"){} - static assert(PDVT!bar.length == 2); - static assert(PDVT!bar[0] == 1); - static assert(PDVT!bar[1] == "hello"); - static assert(is(typeof(PDVT!bar) == typeof(AliasSeq!(1, "hello")))); - - void baz(int x, int n = 1, string s = "hello"){} - static assert(PDVT!baz.length == 3); - static assert(is(PDVT!baz[0] == void)); - static assert( PDVT!baz[1] == 1); - static assert( PDVT!baz[2] == "hello"); - static assert(is(typeof(PDVT!baz) == typeof(AliasSeq!(void, 1, "hello")))); - - // property functions return empty string - // https://issues.dlang.org/show_bug.cgi?id=10800 - @property void foo(int x = 3) { } - static assert(PDVT!foo.length == 1); - static assert(PDVT!foo[0] == 3); - static assert(is(typeof(PDVT!foo) == typeof(AliasSeq!(3)))); - - struct Colour - { - ubyte a,r,g,b; - - static immutable Colour white = Colour(255,255,255,255); - } - // https://issues.dlang.org/show_bug.cgi?id=8106 - void bug8106(Colour c = Colour.white) {} - //pragma(msg, PDVT!bug8106); - static assert(PDVT!bug8106[0] == Colour.white); - // https://issues.dlang.org/show_bug.cgi?id=16582 - void bug16582(scope int* val = null) {} - static assert(PDVT!bug16582[0] is null); -} - - -/** -Returns the FunctionAttribute mask for function `func`. - -See_Also: - $(LREF hasFunctionAttributes) - */ -enum FunctionAttribute : uint -{ - /** - * These flags can be bitwise OR-ed together to represent a complex attribute. - */ - none = 0, - pure_ = 1 << 0, /// ditto - nothrow_ = 1 << 1, /// ditto - ref_ = 1 << 2, /// ditto - property = 1 << 3, /// ditto - trusted = 1 << 4, /// ditto - safe = 1 << 5, /// ditto - nogc = 1 << 6, /// ditto - system = 1 << 7, /// ditto - const_ = 1 << 8, /// ditto - immutable_ = 1 << 9, /// ditto - inout_ = 1 << 10, /// ditto - shared_ = 1 << 11, /// ditto - return_ = 1 << 12, /// ditto - scope_ = 1 << 13, /// ditto - live = 1 << 14, /// ditto -} - -/// ditto -template functionAttributes(alias func) -if (isCallable!func) -{ - // @bug: workaround for opCall - alias FuncSym = Select!(is(typeof(__traits(getFunctionAttributes, func))), - func, Unqual!(FunctionTypeOf!func)); - - enum FunctionAttribute functionAttributes = - extractAttribFlags!(__traits(getFunctionAttributes, FuncSym))(); -} - -/// -@safe unittest -{ - alias FA = FunctionAttribute; // shorten the enum name - - real func(real x) pure nothrow @safe - { - return x; - } - static assert(functionAttributes!func & FA.pure_); - static assert(functionAttributes!func & FA.safe); - static assert(!(functionAttributes!func & FA.trusted)); // not @trusted -} - -@system unittest -{ - alias FA = FunctionAttribute; - - struct S - { - int noF() { return 0; } - int constF() const { return 0; } - int immutableF() immutable { return 0; } - int inoutF() inout { return 0; } - int sharedF() shared { return 0; } - - int x; - ref int refF() return { return x; } - int propertyF() @property { return 0; } - int nothrowF() nothrow { return 0; } - int nogcF() @nogc { return 0; } - - int systemF() @system { return 0; } - int trustedF() @trusted { return 0; } - int safeF() @safe { return 0; } - - int pureF() pure { return 0; } - - int liveF() @live { return 0; } - } - - static assert(functionAttributes!(S.noF) == FA.system); - static assert(functionAttributes!(typeof(S.noF)) == FA.system); - - static assert(functionAttributes!(S.constF) == (FA.const_ | FA.system)); - static assert(functionAttributes!(typeof(S.constF)) == (FA.const_ | FA.system)); - - static assert(functionAttributes!(S.immutableF) == (FA.immutable_ | FA.system)); - static assert(functionAttributes!(typeof(S.immutableF)) == (FA.immutable_ | FA.system)); - - static assert(functionAttributes!(S.inoutF) == (FA.inout_ | FA.system)); - static assert(functionAttributes!(typeof(S.inoutF)) == (FA.inout_ | FA.system)); - - static assert(functionAttributes!(S.sharedF) == (FA.shared_ | FA.system)); - static assert(functionAttributes!(typeof(S.sharedF)) == (FA.shared_ | FA.system)); - - static assert(functionAttributes!(S.refF) == (FA.ref_ | FA.system | FA.return_)); - static assert(functionAttributes!(typeof(S.refF)) == (FA.ref_ | FA.system | FA.return_)); - - static assert(functionAttributes!(S.propertyF) == (FA.property | FA.system)); - static assert(functionAttributes!(typeof(&S.propertyF)) == (FA.property | FA.system)); - - static assert(functionAttributes!(S.nothrowF) == (FA.nothrow_ | FA.system)); - static assert(functionAttributes!(typeof(S.nothrowF)) == (FA.nothrow_ | FA.system)); - - static assert(functionAttributes!(S.nogcF) == (FA.nogc | FA.system)); - static assert(functionAttributes!(typeof(S.nogcF)) == (FA.nogc | FA.system)); - - static assert(functionAttributes!(S.systemF) == FA.system); - static assert(functionAttributes!(typeof(S.systemF)) == FA.system); - - static assert(functionAttributes!(S.trustedF) == FA.trusted); - static assert(functionAttributes!(typeof(S.trustedF)) == FA.trusted); - - static assert(functionAttributes!(S.safeF) == FA.safe); - static assert(functionAttributes!(typeof(S.safeF)) == FA.safe); - - static assert(functionAttributes!(S.pureF) == (FA.pure_ | FA.system)); - static assert(functionAttributes!(typeof(S.pureF)) == (FA.pure_ | FA.system)); - - static assert(functionAttributes!(S.liveF) == (FA.live | FA.system)); - static assert(functionAttributes!(typeof(S.liveF)) == (FA.live | FA.system)); - - int pure_nothrow() nothrow pure; - void safe_nothrow() @safe nothrow; - static ref int static_ref_property() @property; - ref int ref_property() @property; - - static assert(functionAttributes!(pure_nothrow) == (FA.pure_ | FA.nothrow_ | FA.system)); - static assert(functionAttributes!(typeof(pure_nothrow)) == (FA.pure_ | FA.nothrow_ | FA.system)); - - static assert(functionAttributes!(safe_nothrow) == (FA.safe | FA.nothrow_)); - static assert(functionAttributes!(typeof(safe_nothrow)) == (FA.safe | FA.nothrow_)); - - static assert(functionAttributes!(static_ref_property) == (FA.property | FA.ref_ | FA.system)); - static assert(functionAttributes!(typeof(&static_ref_property)) == (FA.property | FA.ref_ | FA.system)); - - static assert(functionAttributes!(ref_property) == (FA.property | FA.ref_ | FA.system)); - static assert(functionAttributes!(typeof(&ref_property)) == (FA.property | FA.ref_ | FA.system)); - - struct S2 - { - int pure_const() const pure { return 0; } - int pure_sharedconst() const shared pure { return 0; } - } - - static assert(functionAttributes!(S2.pure_const) == (FA.const_ | FA.pure_ | FA.system)); - static assert(functionAttributes!(typeof(S2.pure_const)) == (FA.const_ | FA.pure_ | FA.system)); - - static assert(functionAttributes!(S2.pure_sharedconst) == (FA.const_ | FA.shared_ | FA.pure_ | FA.system)); - static assert(functionAttributes!(typeof(S2.pure_sharedconst)) == (FA.const_ | FA.shared_ | FA.pure_ | FA.system)); - - static assert(functionAttributes!((int a) { }) == (FA.pure_ | FA.nothrow_ | FA.nogc | FA.safe)); - static assert(functionAttributes!(typeof((int a) { })) == (FA.pure_ | FA.nothrow_ | FA.nogc | FA.safe)); - - auto safeDel = delegate() @safe { }; - static assert(functionAttributes!(safeDel) == (FA.pure_ | FA.nothrow_ | FA.nogc | FA.safe)); - static assert(functionAttributes!(typeof(safeDel)) == (FA.pure_ | FA.nothrow_ | FA.nogc | FA.safe)); - - auto trustedDel = delegate() @trusted { }; - static assert(functionAttributes!(trustedDel) == (FA.pure_ | FA.nothrow_ | FA.nogc | FA.trusted)); - static assert(functionAttributes!(typeof(trustedDel)) == (FA.pure_ | FA.nothrow_ | FA.nogc | FA.trusted)); - - auto systemDel = delegate() @system { }; - static assert(functionAttributes!(systemDel) == (FA.pure_ | FA.nothrow_ | FA.nogc | FA.system)); - static assert(functionAttributes!(typeof(systemDel)) == (FA.pure_ | FA.nothrow_ | FA.nogc | FA.system)); -} - -private FunctionAttribute extractAttribFlags(Attribs...)() -{ - auto res = FunctionAttribute.none; - - static foreach (attrib; Attribs) - { - switch (attrib) with (FunctionAttribute) - { - case "pure": res |= pure_; break; - case "nothrow": res |= nothrow_; break; - case "ref": res |= ref_; break; - case "@property": res |= property; break; - case "@trusted": res |= trusted; break; - case "@safe": res |= safe; break; - case "@nogc": res |= nogc; break; - case "@system": res |= system; break; - case "const": res |= const_; break; - case "immutable": res |= immutable_; break; - case "inout": res |= inout_; break; - case "shared": res |= shared_; break; - case "return": res |= return_; break; - case "scope": res |= scope_; break; - case "@live": res |= live; break; - default: assert(0, attrib); - } - } - - return res; -} - -/** -Checks whether a function has the given attributes attached. - -Params: - args = Function to check, followed by a - variadic number of function attributes as strings - -Returns: - `true`, if the function has the list of attributes attached and `false` otherwise. - -See_Also: - $(LREF functionAttributes) -*/ -template hasFunctionAttributes(args...) -if (args.length > 0 && isCallable!(args[0]) - && allSatisfy!(isSomeString, typeof(args[1 .. $]))) -{ - enum bool hasFunctionAttributes = { - import std.algorithm.searching : canFind; - import std.range : only; - enum funcAttribs = only(__traits(getFunctionAttributes, args[0])); - static foreach (attribute; args[1 .. $]) - { - if (!funcAttribs.canFind(attribute)) - return false; - } - return true; - }(); -} - -/// -@safe unittest -{ - real func(real x) pure nothrow @safe; - static assert(hasFunctionAttributes!(func, "@safe", "pure")); - static assert(!hasFunctionAttributes!(func, "@trusted")); - - // for templates attributes are automatically inferred - bool myFunc(T)(T b) - { - return !b; - } - static assert(hasFunctionAttributes!(myFunc!bool, "@safe", "pure", "@nogc", "nothrow")); - static assert(!hasFunctionAttributes!(myFunc!bool, "shared")); -} - -@system unittest -{ - struct S - { - int noF(); - int constF() const; - int immutableF() immutable; - int inoutF() inout; - int sharedF() shared; - - ref int refF() return; - int propertyF() @property; - int nothrowF() nothrow; - int nogcF() @nogc; - - int systemF() @system; - int trustedF() @trusted; - int safeF() @safe; - - int pureF() pure; - - int liveF() @live; - } - - // true if no args passed - static assert(hasFunctionAttributes!(S.noF)); - - static assert(hasFunctionAttributes!(S.noF, "@system")); - static assert(hasFunctionAttributes!(typeof(S.noF), "@system")); - static assert(!hasFunctionAttributes!(S.noF, "@system", "pure")); - - static assert(hasFunctionAttributes!(S.constF, "const", "@system")); - static assert(hasFunctionAttributes!(typeof(S.constF), "const", "@system")); - static assert(!hasFunctionAttributes!(S.constF, "const", "@system", "@nogc")); - - static assert(hasFunctionAttributes!(S.immutableF, "immutable", "@system")); - static assert(hasFunctionAttributes!(typeof(S.immutableF), "immutable", "@system")); - static assert(!hasFunctionAttributes!(S.immutableF, "immutable", "@system", "pure")); - - static assert(hasFunctionAttributes!(S.inoutF, "inout", "@system")); - static assert(hasFunctionAttributes!(typeof(S.inoutF), "inout", "@system")); - static assert(!hasFunctionAttributes!(S.inoutF, "inout", "@system", "pure")); - - static assert(hasFunctionAttributes!(S.sharedF, "shared", "@system")); - static assert(hasFunctionAttributes!(typeof(S.sharedF), "shared", "@system")); - static assert(!hasFunctionAttributes!(S.sharedF, "shared", "@system", "@trusted")); - - static assert(hasFunctionAttributes!(S.refF, "ref", "@system", "return")); - static assert(hasFunctionAttributes!(typeof(S.refF), "ref", "@system", "return")); - static assert(!hasFunctionAttributes!(S.refF, "ref", "@system", "return", "pure")); - - static assert(hasFunctionAttributes!(S.propertyF, "@property", "@system")); - static assert(hasFunctionAttributes!(typeof(&S.propertyF), "@property", "@system")); - static assert(!hasFunctionAttributes!(S.propertyF, "@property", "@system", "ref")); - - static assert(hasFunctionAttributes!(S.nothrowF, "nothrow", "@system")); - static assert(hasFunctionAttributes!(typeof(S.nothrowF), "nothrow", "@system")); - static assert(!hasFunctionAttributes!(S.nothrowF, "nothrow", "@system", "@trusted")); - - static assert(hasFunctionAttributes!(S.nogcF, "@nogc", "@system")); - static assert(hasFunctionAttributes!(typeof(S.nogcF), "@nogc", "@system")); - static assert(!hasFunctionAttributes!(S.nogcF, "@nogc", "@system", "ref")); - - static assert(hasFunctionAttributes!(S.systemF, "@system")); - static assert(hasFunctionAttributes!(typeof(S.systemF), "@system")); - static assert(!hasFunctionAttributes!(S.systemF, "@system", "ref")); - - static assert(hasFunctionAttributes!(S.trustedF, "@trusted")); - static assert(hasFunctionAttributes!(typeof(S.trustedF), "@trusted")); - static assert(!hasFunctionAttributes!(S.trustedF, "@trusted", "@safe")); - - static assert(hasFunctionAttributes!(S.safeF, "@safe")); - static assert(hasFunctionAttributes!(typeof(S.safeF), "@safe")); - static assert(!hasFunctionAttributes!(S.safeF, "@safe", "nothrow")); - - static assert(hasFunctionAttributes!(S.pureF, "pure", "@system")); - static assert(hasFunctionAttributes!(typeof(S.pureF), "pure", "@system")); - static assert(!hasFunctionAttributes!(S.pureF, "pure", "@system", "ref")); - - static assert(hasFunctionAttributes!(S.liveF, "@live", "@system")); - static assert(hasFunctionAttributes!(typeof(S.liveF), "@live", "@system")); - static assert(!hasFunctionAttributes!(S.liveF, "@live", "@system", "ref")); - - int pure_nothrow() nothrow pure { return 0; } - void safe_nothrow() @safe nothrow { } - static ref int static_ref_property() @property { return *(new int); } - ref int ref_property() @property { return *(new int); } - - static assert(hasFunctionAttributes!(pure_nothrow, "pure", "nothrow", "@safe")); - static assert(hasFunctionAttributes!(typeof(pure_nothrow), "pure", "nothrow", "@safe")); - static assert(!hasFunctionAttributes!(pure_nothrow, "pure", "nothrow", "@safe", "@trusted")); - - static assert(hasFunctionAttributes!(safe_nothrow, "@safe", "nothrow")); - static assert(hasFunctionAttributes!(typeof(safe_nothrow), "@safe", "nothrow")); - static assert(hasFunctionAttributes!(safe_nothrow, "@safe", "nothrow", "pure")); - static assert(!hasFunctionAttributes!(safe_nothrow, "@safe", "nothrow", "pure", "@trusted")); - - static assert(hasFunctionAttributes!(static_ref_property, "@property", "ref", "@safe")); - static assert(hasFunctionAttributes!(typeof(&static_ref_property), "@property", "ref", "@safe")); - static assert(hasFunctionAttributes!(static_ref_property, "@property", "ref", "@safe", "nothrow")); - static assert(!hasFunctionAttributes!(static_ref_property, "@property", "ref", "@safe", "nothrow", "@nogc")); - - static assert(hasFunctionAttributes!(ref_property, "@property", "ref", "@safe")); - static assert(hasFunctionAttributes!(typeof(&ref_property), "@property", "ref", "@safe")); - static assert(!hasFunctionAttributes!(ref_property, "@property", "ref", "@safe", "@nogc")); - - struct S2 - { - int pure_const() const pure { return 0; } - int pure_sharedconst() const shared pure { return 0; } - } - - static assert(hasFunctionAttributes!(S2.pure_const, "const", "pure", "@system")); - static assert(hasFunctionAttributes!(typeof(S2.pure_const), "const", "pure", "@system")); - static assert(!hasFunctionAttributes!(S2.pure_const, "const", "pure", "@system", "ref")); - - static assert(hasFunctionAttributes!(S2.pure_sharedconst, "const", "shared", "pure", "@system")); - static assert(hasFunctionAttributes!(typeof(S2.pure_sharedconst), "const", "shared", "pure", "@system")); - static assert(!hasFunctionAttributes!(S2.pure_sharedconst, "const", "shared", "pure", "@system", "@nogc")); - - static assert(hasFunctionAttributes!((int a) { }, "pure", "nothrow", "@nogc", "@safe")); - static assert(hasFunctionAttributes!(typeof((int a) { }), "pure", "nothrow", "@nogc", "@safe")); - static assert(!hasFunctionAttributes!((int a) { }, "pure", "nothrow", "@nogc", "@safe", "ref")); - - auto safeDel = delegate() @safe { }; - static assert(hasFunctionAttributes!(safeDel, "pure", "nothrow", "@nogc", "@safe")); - static assert(hasFunctionAttributes!(typeof(safeDel), "pure", "nothrow", "@nogc", "@safe")); - static assert(!hasFunctionAttributes!(safeDel, "pure", "nothrow", "@nogc", "@safe", "@system")); - - auto trustedDel = delegate() @trusted { }; - static assert(hasFunctionAttributes!(trustedDel, "pure", "nothrow", "@nogc", "@trusted")); - static assert(hasFunctionAttributes!(typeof(trustedDel), "pure", "nothrow", "@nogc", "@trusted")); - static assert(!hasFunctionAttributes!(trustedDel, "pure", "nothrow", "@nogc", "@trusted", "ref")); - - auto systemDel = delegate() @system { }; - static assert(hasFunctionAttributes!(systemDel, "pure", "nothrow", "@nogc", "@system")); - static assert(hasFunctionAttributes!(typeof(systemDel), "pure", "nothrow", "@nogc", "@system")); - static assert(!hasFunctionAttributes!(systemDel, "pure", "nothrow", "@nogc", "@system", "@property")); - - - // call functions to make CodeCov happy - { - assert(pure_nothrow == 0); - safe_nothrow; - assert(static_ref_property == 0); - assert(ref_property == 0); - assert(S2().pure_const == 0); - assert((shared S2()).pure_sharedconst == 0); - cast(void) safeDel; - cast(void) trustedDel; - cast(void) systemDel; - } -} - -/** -`true` if `func` is `@safe` or `@trusted`. - */ -template isSafe(alias func) -if (isCallable!func) -{ - enum isSafe = (functionAttributes!func & FunctionAttribute.safe) != 0 || - (functionAttributes!func & FunctionAttribute.trusted) != 0; -} - -/// -@safe unittest -{ - @safe int add(int a, int b) {return a+b;} - @trusted int sub(int a, int b) {return a-b;} - @system int mul(int a, int b) {return a*b;} - - static assert( isSafe!add); - static assert( isSafe!sub); - static assert(!isSafe!mul); -} - - -@safe unittest -{ - //Member functions - interface Set - { - int systemF() @system; - int trustedF() @trusted; - int safeF() @safe; - } - static assert( isSafe!(Set.safeF)); - static assert( isSafe!(Set.trustedF)); - static assert(!isSafe!(Set.systemF)); - - //Functions - @safe static void safeFunc() {} - @trusted static void trustedFunc() {} - @system static void systemFunc() {} - - static assert( isSafe!safeFunc); - static assert( isSafe!trustedFunc); - static assert(!isSafe!systemFunc); - - //Delegates - auto safeDel = delegate() @safe {}; - auto trustedDel = delegate() @trusted {}; - auto systemDel = delegate() @system {}; - - static assert( isSafe!safeDel); - static assert( isSafe!trustedDel); - static assert(!isSafe!systemDel); - - //Lambdas - static assert( isSafe!({safeDel();})); - static assert( isSafe!({trustedDel();})); - static assert(!isSafe!({systemDel();})); - - //Static opCall - struct SafeStatic { @safe static SafeStatic opCall() { return SafeStatic.init; } } - struct TrustedStatic { @trusted static TrustedStatic opCall() { return TrustedStatic.init; } } - struct SystemStatic { @system static SystemStatic opCall() { return SystemStatic.init; } } - - static assert( isSafe!(SafeStatic())); - static assert( isSafe!(TrustedStatic())); - static assert(!isSafe!(SystemStatic())); - - //Non-static opCall - struct Safe { @safe Safe opCall() { return Safe.init; } } - struct Trusted { @trusted Trusted opCall() { return Trusted.init; } } - struct System { @system System opCall() { return System.init; } } - - static assert( isSafe!(Safe.init())); - static assert( isSafe!(Trusted.init())); - static assert(!isSafe!(System.init())); -} - - -/** -`true` if `func` is `@system`. -*/ -template isUnsafe(alias func) -{ - enum isUnsafe = !isSafe!func; -} - -/// -@safe unittest -{ - @safe int add(int a, int b) {return a+b;} - @trusted int sub(int a, int b) {return a-b;} - @system int mul(int a, int b) {return a*b;} - - static assert(!isUnsafe!add); - static assert(!isUnsafe!sub); - static assert( isUnsafe!mul); -} - -@safe unittest -{ - //Member functions - interface Set - { - int systemF() @system; - int trustedF() @trusted; - int safeF() @safe; - } - static assert(!isUnsafe!(Set.safeF)); - static assert(!isUnsafe!(Set.trustedF)); - static assert( isUnsafe!(Set.systemF)); - - //Functions - @safe static void safeFunc() {} - @trusted static void trustedFunc() {} - @system static void systemFunc() {} - - static assert(!isUnsafe!safeFunc); - static assert(!isUnsafe!trustedFunc); - static assert( isUnsafe!systemFunc); - - //Delegates - auto safeDel = delegate() @safe {}; - auto trustedDel = delegate() @trusted {}; - auto systemDel = delegate() @system {}; - - static assert(!isUnsafe!safeDel); - static assert(!isUnsafe!trustedDel); - static assert( isUnsafe!systemDel); - - //Lambdas - static assert(!isUnsafe!({safeDel();})); - static assert(!isUnsafe!({trustedDel();})); - static assert( isUnsafe!({systemDel();})); - - //Static opCall - struct SafeStatic { @safe static SafeStatic opCall() { return SafeStatic.init; } } - struct TrustedStatic { @trusted static TrustedStatic opCall() { return TrustedStatic.init; } } - struct SystemStatic { @system static SystemStatic opCall() { return SystemStatic.init; } } - - static assert(!isUnsafe!(SafeStatic())); - static assert(!isUnsafe!(TrustedStatic())); - static assert( isUnsafe!(SystemStatic())); - - //Non-static opCall - struct Safe { @safe Safe opCall() { return Safe.init; } } - struct Trusted { @trusted Trusted opCall() { return Trusted.init; } } - struct System { @system System opCall() { return System.init; } } - - static assert(!isUnsafe!(Safe.init())); - static assert(!isUnsafe!(Trusted.init())); - static assert( isUnsafe!(System.init())); -} - - -/** -Determine the linkage attribute of the function. -Params: - func = the function symbol, or the type of a function, delegate, or pointer to function -Returns: - one of the strings "D", "C", "C++", "Windows", "Objective-C", or "System". -*/ -template functionLinkage(alias func) -if (isCallable!func) -{ - enum string functionLinkage = __traits(getLinkage, FunctionTypeOf!func); -} - -/// -@safe unittest -{ - extern(D) void Dfunc() {} - extern(C) void Cfunc() {} - static assert(functionLinkage!Dfunc == "D"); - static assert(functionLinkage!Cfunc == "C"); - - string a = functionLinkage!Dfunc; - assert(a == "D"); - - auto fp = &Cfunc; - string b = functionLinkage!fp; - assert(b == "C"); -} - -@safe unittest -{ - interface Test - { - void const_func() const; - void sharedconst_func() shared const; - } - static assert(functionLinkage!(Test.const_func) == "D"); - static assert(functionLinkage!(Test.sharedconst_func) == "D"); - - static assert(functionLinkage!((int a){}) == "D"); -} - - -/** -Determines what kind of variadic parameters function has. -Params: - func = function symbol or type of function, delegate, or pointer to function -Returns: - enum Variadic - */ -enum Variadic -{ - /// Function is not variadic. - no, - /// Function is a _C-style variadic function, which uses - /// `core.stdc.stdarg` - c, - /// Function is a _D-style variadic function, which uses - /// `__argptr` and `__arguments`. - d, - /// Function is a typesafe variadic function. - typesafe, -} - -/// ditto -template variadicFunctionStyle(alias func) -if (isCallable!func) -{ - enum string varargs = __traits(getFunctionVariadicStyle, FunctionTypeOf!func); - enum Variadic variadicFunctionStyle = - (varargs == "stdarg") ? Variadic.c : - (varargs == "argptr") ? Variadic.d : - (varargs == "typesafe") ? Variadic.typesafe : - (varargs == "none") ? Variadic.no : Variadic.no; -} - -/// -@safe unittest -{ - void func() {} - static assert(variadicFunctionStyle!func == Variadic.no); - - extern(C) int printf(const char*, ...); - static assert(variadicFunctionStyle!printf == Variadic.c); -} - -@safe unittest -{ - import core.vararg; - - extern(D) void novar() {} - extern(C) void cstyle(int, ...) {} - extern(D) void dstyle(...) {} - extern(D) void typesafe(int[]...) {} - - static assert(variadicFunctionStyle!novar == Variadic.no); - static assert(variadicFunctionStyle!cstyle == Variadic.c); - static assert(variadicFunctionStyle!dstyle == Variadic.d); - static assert(variadicFunctionStyle!typesafe == Variadic.typesafe); - - static assert(variadicFunctionStyle!((int[] a...) {}) == Variadic.typesafe); -} - - -/** -Get the function type from a callable object `func`, or from a function pointer/delegate type. - -Using builtin `typeof` on a property function yields the types of the -property value, not of the property function itself. Still, -`FunctionTypeOf` is able to obtain function types of properties. - -Note: -Do not confuse function types with function pointer types; function types are -usually used for compile-time reflection purposes. - */ -template FunctionTypeOf(alias func) -if (isCallable!func) -{ - static if ((is(typeof(& func) Fsym : Fsym*) && is(Fsym == function)) || is(typeof(& func) Fsym == delegate)) - { - alias FunctionTypeOf = Fsym; // HIT: (nested) function symbol - } - else static if (is(typeof(& func.opCall) Fobj == delegate) || is(typeof(& func.opCall!()) Fobj == delegate)) - { - alias FunctionTypeOf = Fobj; // HIT: callable object - } - else static if ( - (is(typeof(& func.opCall) Ftyp : Ftyp*) && is(Ftyp == function)) || - (is(typeof(& func.opCall!()) Ftyp : Ftyp*) && is(Ftyp == function)) - ) - { - alias FunctionTypeOf = Ftyp; // HIT: callable type - } - else static if (is(func T) || is(typeof(func) T)) - { - static if (is(T == function)) - alias FunctionTypeOf = T; // HIT: function - else static if (is(T Fptr : Fptr*) && is(Fptr == function)) - alias FunctionTypeOf = Fptr; // HIT: function pointer - else static if (is(T Fdlg == delegate)) - alias FunctionTypeOf = Fdlg; // HIT: delegate - else - static assert(0); - } - else - static assert(0); -} - -/// -@safe unittest -{ - class C - { - int value() @property => 0; - static string opCall() => "hi"; - } - static assert(is( typeof(C.value) == int )); - static assert(is( FunctionTypeOf!(C.value) == function )); - static assert(is( FunctionTypeOf!C == typeof(C.opCall) )); - - int function() fp; - alias IntFn = int(); - static assert(is( typeof(fp) == IntFn* )); - static assert(is( FunctionTypeOf!fp == IntFn )); -} - -@system unittest -{ - int test(int a); - int propGet() @property; - int propSet(int a) @property; - int function(int) test_fp; - int delegate(int) test_dg; - static assert(is( typeof(test) == FunctionTypeOf!(typeof(test)) )); - static assert(is( typeof(test) == FunctionTypeOf!test )); - static assert(is( typeof(test) == FunctionTypeOf!test_fp )); - static assert(is( typeof(test) == FunctionTypeOf!test_dg )); - alias int GetterType() @property; - alias int SetterType(int) @property; - static assert(is( FunctionTypeOf!propGet == GetterType )); - static assert(is( FunctionTypeOf!propSet == SetterType )); - - interface Prop { int prop() @property; } - Prop prop; - static assert(is( FunctionTypeOf!(Prop.prop) == GetterType )); - static assert(is( FunctionTypeOf!(prop.prop) == GetterType )); - - class Callable { int opCall(int) { return 0; } } - auto call = new Callable; - static assert(is( FunctionTypeOf!call == typeof(test) )); - - struct StaticCallable { static int opCall(int) { return 0; } } - StaticCallable stcall_val; - StaticCallable* stcall_ptr; - static assert(is( FunctionTypeOf!stcall_val == typeof(test) )); - static assert(is( FunctionTypeOf!stcall_ptr == typeof(test) )); - - struct TemplatedOpCallF { int opCall()(int) { return 0; } } - static assert(is( FunctionTypeOf!TemplatedOpCallF == typeof(TemplatedOpCallF.opCall!()) )); - - int foovar; - struct TemplatedOpCallDg { int opCall()() { return foovar; } } - static assert(is( FunctionTypeOf!TemplatedOpCallDg == typeof(TemplatedOpCallDg.opCall!()) )); - - interface Overloads - { - void test(string); - real test(real); - int test(int); - int test() @property; - } - alias ov = __traits(getVirtualMethods, Overloads, "test"); - alias F_ov0 = FunctionTypeOf!(ov[0]); - alias F_ov1 = FunctionTypeOf!(ov[1]); - alias F_ov2 = FunctionTypeOf!(ov[2]); - alias F_ov3 = FunctionTypeOf!(ov[3]); - static assert(is(F_ov0* == void function(string))); - static assert(is(F_ov1* == real function(real))); - static assert(is(F_ov2* == int function(int))); - static assert(is(F_ov3* == int function() @property)); - - alias F_dglit = FunctionTypeOf!((int a){ return a; }); - static assert(is(F_dglit* : int function(int))); -} - -/** - * Constructs a new function or delegate type with the same basic signature - * as the given one, but different attributes (including linkage). - * - * This is especially useful for adding/removing attributes to/from types in - * generic code, where the actual type name cannot be spelt out. - * - * Params: - * T = The base type. - * linkage = The desired linkage of the result type. - * attrs = The desired $(LREF FunctionAttribute)s of the result type. - */ -template SetFunctionAttributes(T, string linkage, uint attrs) -if (isFunctionPointer!T || isDelegate!T) -{ - mixin({ - import std.algorithm.searching : canFind; - - static assert(!(attrs & FunctionAttribute.trusted) || - !(attrs & FunctionAttribute.safe), - "Cannot have a function/delegate that is both trusted and safe."); - - static immutable linkages = ["D", "C", "Windows", "C++", "System"]; - static assert(canFind(linkages, linkage), "Invalid linkage '" ~ - linkage ~ "', must be one of " ~ linkages.stringof ~ "."); - - string result = "alias "; - - static if (linkage != "D") - result ~= "extern(" ~ linkage ~ ") "; - - static if (attrs & FunctionAttribute.ref_) - result ~= "ref "; - - result ~= "ReturnType!T"; - - static if (isDelegate!T) - result ~= " delegate"; - else - result ~= " function"; - - result ~= "("; - - static if (Parameters!T.length > 0) - result ~= "Parameters!T"; - - enum varStyle = variadicFunctionStyle!T; - static if (varStyle == Variadic.c) - result ~= ", ..."; - else static if (varStyle == Variadic.d) - result ~= "..."; - else static if (varStyle == Variadic.typesafe) - result ~= "..."; - - result ~= ")"; - - static if (attrs & FunctionAttribute.pure_) - result ~= " pure"; - static if (attrs & FunctionAttribute.nothrow_) - result ~= " nothrow"; - static if (attrs & FunctionAttribute.property) - result ~= " @property"; - static if (attrs & FunctionAttribute.trusted) - result ~= " @trusted"; - static if (attrs & FunctionAttribute.safe) - result ~= " @safe"; - static if (attrs & FunctionAttribute.nogc) - result ~= " @nogc"; - static if (attrs & FunctionAttribute.system) - result ~= " @system"; - static if (attrs & FunctionAttribute.const_) - result ~= " const"; - static if (attrs & FunctionAttribute.immutable_) - result ~= " immutable"; - static if (attrs & FunctionAttribute.inout_) - result ~= " inout"; - static if (attrs & FunctionAttribute.shared_) - result ~= " shared"; - static if (attrs & FunctionAttribute.return_) - result ~= " return"; - static if (attrs & FunctionAttribute.live) - result ~= " @live"; - - result ~= " SetFunctionAttributes;"; - return result; - }()); -} - -/// Ditto -template SetFunctionAttributes(T, string linkage, uint attrs) -if (is(T == function)) -{ - // To avoid a lot of syntactic headaches, we just use the above version to - // operate on the corresponding function pointer type and then remove the - // indirection again. - alias SetFunctionAttributes = FunctionTypeOf!(SetFunctionAttributes!(T*, linkage, attrs)); -} - -/// -@safe unittest -{ - alias ExternC(T) = SetFunctionAttributes!(T, "C", functionAttributes!T); - - auto assumePure(T)(T t) - if (isFunctionPointer!T || isDelegate!T) - { - enum attrs = functionAttributes!T | FunctionAttribute.pure_; - return cast(SetFunctionAttributes!(T, functionLinkage!T, attrs)) t; - } - - int f() - { - import core.thread : getpid; - return getpid(); - } - - int g() pure @trusted - { - auto pureF = assumePure(&f); - return pureF(); - } - assert(g() > 0); -} - -version (StdUnittest) -{ -private: - // Some function types to test. - int sc(scope int, ref int, out int, lazy int, int); - extern(System) int novar(); - extern(C) int cstyle(int, ...); - extern(D) int dstyle(...); - extern(D) int typesafe(int[]...); -} -@safe unittest -{ - import std.algorithm.iteration : reduce; - - alias FA = FunctionAttribute; - static foreach (BaseT; AliasSeq!(typeof(&sc), typeof(&novar), typeof(&cstyle), - typeof(&dstyle), typeof(&typesafe))) - { - static foreach (T; AliasSeq!(BaseT, FunctionTypeOf!BaseT)) - {{ - enum linkage = functionLinkage!T; - enum attrs = functionAttributes!T; - - static assert(is(SetFunctionAttributes!(T, linkage, attrs) == T), - "Identity check failed for: " ~ T.stringof); - - // Check that all linkage types work (D-style variadics require D linkage). - static if (variadicFunctionStyle!T != Variadic.d) - { - static foreach (newLinkage; AliasSeq!("D", "C", "Windows", "C++")) - {{ - alias New = SetFunctionAttributes!(T, newLinkage, attrs); - static assert(functionLinkage!New == newLinkage, - "Linkage test failed for: " ~ T.stringof ~ ", " ~ newLinkage ~ - " (got " ~ New.stringof ~ ")"); - }} - } - - // Add @safe. - alias T1 = SetFunctionAttributes!(T, functionLinkage!T, FA.safe); - static assert(functionAttributes!T1 == FA.safe); - - // Add all known attributes, excluding conflicting ones. - enum allAttrs = reduce!"a | b"([EnumMembers!FA]) - & ~FA.safe & ~FA.property & ~FA.const_ & ~FA.immutable_ & ~FA.inout_ - & ~FA.shared_ & ~FA.system & ~FA.return_ & ~FA.scope_; - - alias T2 = SetFunctionAttributes!(T1, functionLinkage!T, allAttrs); - static assert(functionAttributes!T2 == allAttrs); - - // Strip all attributes again. - alias T3 = SetFunctionAttributes!(T2, functionLinkage!T, FA.none); - static assert(is(T3 == T)); - }} - } -} - - -//:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::// -// Aggregate Types -//:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::// - -/** -Determines whether `T` is a class nested inside another class -and that `T.outer` is the implicit reference to the outer class -(i.e. `outer` has not been used as a field or method name) - -Params: - T = type to test - -Returns: -`true` if `T` is a class nested inside another, with the conditions described above; -`false` otherwise -*/ -template isInnerClass(T) -if (is(T == class)) -{ - static if (is(typeof(T.outer))) - { - bool hasOuterMember(string[] members...) - { - foreach (m; members) - { - if (m == "outer") - return true; - } - return false; - } - enum isInnerClass = __traits(isSame, typeof(T.outer), __traits(parent, T)) && - !hasOuterMember(__traits(allMembers, T)); - } - else - enum isInnerClass = false; -} - -/// -@safe unittest -{ - class C - { - int outer; - } - static assert(!isInnerClass!C); - - class Outer1 - { - class Inner1 { } - class Inner2 - { - int outer; - } - } - static assert(isInnerClass!(Outer1.Inner1)); - static assert(!isInnerClass!(Outer1.Inner2)); - - static class Outer2 - { - static class Inner - { - int outer; - } - } - static assert(!isInnerClass!(Outer2.Inner)); -} - -/** -Determines whether `T` has its own context pointer. -`T` must be either `class`, `struct`, or `union`. - -See also: $(DDSUBLINK spec/traits, isNested, `__traits(isNested, T)`) -*/ -template isNested(T) -if (is(T == class) || is(T == struct) || is(T == union)) -{ - enum isNested = __traits(isNested, T); -} - -/// -@safe unittest -{ - static struct S { } - static assert(!isNested!S); - - int i; - struct NestedStruct { void f() { ++i; } } - static assert(isNested!NestedStruct); -} - -/** -Determines whether `T` or any of its representation types -have a context pointer. -*/ -template hasNested(T) -{ - import std.meta : Filter; - - static if (isStaticArray!T && T.length) - enum hasNested = hasNested!(typeof(T.init[0])); - else static if (is(T == class) || is(T == struct) || is(T == union)) - { - // prevent infinite recursion for class with member of same type - enum notSame(U) = !is(immutable T == immutable U); - enum hasNested = isNested!T || - anySatisfy!(.hasNested, Filter!(notSame, Fields!T)); - } - else - enum hasNested = false; -} - -/// -@safe unittest -{ - static struct S { } - - int i; - struct NS { void f() { ++i; } } - - static assert(!hasNested!(S[2])); - static assert(hasNested!(NS[2])); -} - -@safe unittest -{ - static assert(!__traits(compiles, isNested!int)); - static assert(!hasNested!int); - - static struct StaticStruct { } - static assert(!isNested!StaticStruct); - static assert(!hasNested!StaticStruct); - - int i; - struct NestedStruct { void f() { ++i; } } - static assert( isNested!NestedStruct); - static assert( hasNested!NestedStruct); - static assert( isNested!(immutable NestedStruct)); - static assert( hasNested!(immutable NestedStruct)); - - static assert(!__traits(compiles, isNested!(NestedStruct[1]))); - static assert( hasNested!(NestedStruct[1])); - static assert(!hasNested!(NestedStruct[0])); - - struct S1 { NestedStruct nested; } - static assert(!isNested!S1); - static assert( hasNested!S1); - - static struct S2 { NestedStruct nested; } - static assert(!isNested!S2); - static assert( hasNested!S2); - - static struct S3 { NestedStruct[0] nested; } - static assert(!isNested!S3); - static assert(!hasNested!S3); - - static union U { NestedStruct nested; } - static assert(!isNested!U); - static assert( hasNested!U); - - static class StaticClass { } - static assert(!isNested!StaticClass); - static assert(!hasNested!StaticClass); - - class NestedClass { void f() { ++i; } } - static assert( isNested!NestedClass); - static assert( hasNested!NestedClass); - static assert( isNested!(immutable NestedClass)); - static assert( hasNested!(immutable NestedClass)); - - static assert(!__traits(compiles, isNested!(NestedClass[1]))); - static assert( hasNested!(NestedClass[1])); - static assert(!hasNested!(NestedClass[0])); - - static class A - { - A a; - } - static assert(!hasNested!A); -} - - -/*** - * Get as a tuple the types of the fields of a struct, class, or union. - * This consists of the fields that take up memory space, - * excluding the hidden fields like the virtual function - * table pointer or a context pointer for nested types. - * If `T` isn't a struct, class, interface or union returns a tuple - * with one element `T`. - * - * History: - * - Returned `AliasSeq!(Interface)` for interfaces prior to 2.097 - */ -template Fields(T) -{ - import core.internal.traits : _Fields = Fields; - alias Fields = _Fields!T; -} - -/// -@safe unittest -{ - import std.meta : AliasSeq; - struct S { int x; float y; } - static assert(is(Fields!S == AliasSeq!(int, float))); -} - -/** - * Alternate name for $(LREF Fields), kept for legacy compatibility. - */ -alias FieldTypeTuple = Fields; - -@safe unittest -{ - static assert(is(FieldTypeTuple!int == AliasSeq!int)); - - static struct StaticStruct1 { } - static assert(is(FieldTypeTuple!StaticStruct1 == AliasSeq!())); - - static struct StaticStruct2 { int a, b; } - static assert(is(FieldTypeTuple!StaticStruct2 == AliasSeq!(int, int))); - - int i; - - struct NestedStruct1 { void f() { ++i; } } - static assert(is(FieldTypeTuple!NestedStruct1 == AliasSeq!())); - - struct NestedStruct2 { int a; void f() { ++i; } } - static assert(is(FieldTypeTuple!NestedStruct2 == AliasSeq!int)); - - class NestedClass { int a; void f() { ++i; } } - static assert(is(FieldTypeTuple!NestedClass == AliasSeq!int)); - - static interface I {} - static assert(is(Fields!I == AliasSeq!())); -} - -//Required for FieldNameTuple -private enum NameOf(alias T) = T.stringof; - -/** - * Get as an expression tuple the names of the fields of a struct, class, or - * union. This consists of the fields that take up memory space, excluding the - * hidden fields like the virtual function table pointer or a context pointer - * for nested types. - * Inherited fields (for classes) are not included. - * If `T` isn't a struct, class, interface or union, an - * expression tuple with an empty string is returned. - * - * History: - * - Returned `AliasSeq!""` for interfaces prior to 2.097 - */ -template FieldNameTuple(T) -{ - import std.meta : staticMap; - static if (is(T == struct) || is(T == union)) - alias FieldNameTuple = staticMap!(NameOf, T.tupleof[0 .. $ - isNested!T]); - else static if (is(T == class) || is(T == interface)) - alias FieldNameTuple = staticMap!(NameOf, T.tupleof); - else - alias FieldNameTuple = AliasSeq!""; -} - -/// -@safe unittest -{ - import std.meta : AliasSeq; - struct S { int x; float y; } - static assert(FieldNameTuple!S == AliasSeq!("x", "y")); - static assert(FieldNameTuple!int == AliasSeq!""); -} - -@safe unittest -{ - static assert(FieldNameTuple!int == AliasSeq!""); - - static struct StaticStruct1 { } - static assert(is(FieldNameTuple!StaticStruct1 == AliasSeq!())); - - static struct StaticStruct2 { int a, b; } - static assert(FieldNameTuple!StaticStruct2 == AliasSeq!("a", "b")); - - static class StaticClass1 { } - static assert(is(FieldNameTuple!StaticClass1 == AliasSeq!())); - - static class StaticClass2 : StaticClass1 { int a, b; } - static assert(FieldNameTuple!StaticClass2 == AliasSeq!("a", "b")); - - static class StaticClass3 : StaticClass2 { int c; } - static assert(FieldNameTuple!StaticClass3 == AliasSeq!("c")); - - int i; - - struct NestedStruct1 { void f() { ++i; } } - static assert(is(FieldNameTuple!NestedStruct1 == AliasSeq!())); - - struct NestedStruct2 { int a; void f() { ++i; } } - static assert(FieldNameTuple!NestedStruct2 == AliasSeq!"a"); - - class NestedClass { int a; void f() { ++i; } } - static assert(FieldNameTuple!NestedClass == AliasSeq!"a"); - - interface I {} - static assert(FieldNameTuple!I == AliasSeq!()); -} - - -/*** -Get the primitive types of the fields of a struct or class, in -topological order. -*/ -template RepresentationTypeTuple(T) -{ - static if (is(T == struct) || is(T == union) || is(T == class)) - { - alias RepresentationTypeTuple = staticMapMeta!(RepresentationTypeTupleImpl, FieldTypeTuple!T); - } - else - { - alias RepresentationTypeTuple = RepresentationTypeTupleImpl!T; - } -} - -/// -@safe unittest -{ - struct S1 { int a; float b; } - struct S2 { char[] a; union { S1 b; S1 * c; } } - alias R = RepresentationTypeTuple!S2; - assert(R.length == 4 - && is(R[0] == char[]) && is(R[1] == int) - && is(R[2] == float) && is(R[3] == S1*)); -} - -@safe unittest -{ - alias S1 = RepresentationTypeTuple!int; - static assert(is(S1 == AliasSeq!int)); - - struct S2 { int a; } - struct S3 { int a; char b; } - struct S4 { S1 a; int b; S3 c; } - static assert(is(RepresentationTypeTuple!S2 == AliasSeq!int)); - static assert(is(RepresentationTypeTuple!S3 == AliasSeq!(int, char))); - static assert(is(RepresentationTypeTuple!S4 == AliasSeq!(int, int, int, char))); - - struct S11 { int a; float b; } - struct S21 { char[] a; union { S11 b; S11 * c; } } - alias R = RepresentationTypeTuple!S21; - assert(R.length == 4 - && is(R[0] == char[]) && is(R[1] == int) - && is(R[2] == float) && is(R[3] == S11*)); - - class C { int a; float b; } - alias R1 = RepresentationTypeTuple!C; - static assert(R1.length == 2 && is(R1[0] == int) && is(R1[1] == float)); - - /* https://issues.dlang.org/show_bug.cgi?id=6642 */ - import std.typecons : Rebindable; - - struct S5 { int a; Rebindable!(immutable Object) b; } - alias R2 = RepresentationTypeTuple!S5; - static assert(R2.length == 2 && is(R2[0] == int) && is(R2[1] == immutable(Object))); - - static assert(is(RepresentationTypeTuple!noreturn == AliasSeq!noreturn)); -} - -@safe unittest -{ - struct VeryLargeType - { - import std.format : format; - import std.range : iota; - - static foreach (i; 500.iota) - { - mixin(format!"int v%s;"(i)); - } - } - - alias BigList = RepresentationTypeTuple!VeryLargeType; -} - -private template RepresentationTypeTupleImpl(T) -{ - import std.typecons : Rebindable; - - static if (is(immutable T == immutable Rebindable!R, R)) - { - alias RepresentationTypeTupleImpl - = staticMapMeta!(.RepresentationTypeTupleImpl, RepresentationTypeTupleImpl!R); - } - else static if (is(T == struct) || is(T == union)) - { - // @@@BUG@@@ this should work - //alias .RepresentationTypes!(T[0].tupleof) - // RepresentationTypes; - alias RepresentationTypeTupleImpl - = staticMapMeta!(.RepresentationTypeTupleImpl, FieldTypeTuple!(T)); - } - else - { - alias RepresentationTypeTupleImpl - = AliasSeq!T; - } -} - -/* -Statically evaluates to `true` if and only if `T`'s -representation contains at least one field of pointer or array type. -Members of class types are not considered raw pointers. Pointers to -immutable objects are not considered raw aliasing. -*/ -private template hasRawAliasing(T) -{ - enum hasRawAliasing = anySatisfy!(hasRawAliasingImpl, RepresentationTypeTuple!T); -} - -// -@safe unittest -{ - // simple types - static assert(!hasRawAliasing!int); - static assert( hasRawAliasing!(char*)); - // references aren't raw pointers - static assert(!hasRawAliasing!Object); - // built-in arrays do contain raw pointers - static assert( hasRawAliasing!(int[])); - // aggregate of simple types - struct S1 { int a; double b; } - static assert(!hasRawAliasing!S1); - // indirect aggregation - struct S2 { S1 a; double b; } - static assert(!hasRawAliasing!S2); -} - -// https://issues.dlang.org/show_bug.cgi?id=19228 -@safe unittest -{ - static struct C - { - int*[1] a; - } - static assert(hasRawAliasing!C); -} - -@safe unittest -{ - // struct with a pointer member - struct S3 { int a; double * b; } - static assert( hasRawAliasing!S3); - // struct with an indirect pointer member - struct S4 { S3 a; double b; } - static assert( hasRawAliasing!S4); - struct S5 { int a; Object z; int c; } - static assert( hasRawAliasing!S3); - static assert( hasRawAliasing!S4); - static assert(!hasRawAliasing!S5); - - union S6 { int a; int b; } - union S7 { int a; int * b; } - static assert(!hasRawAliasing!S6); - static assert( hasRawAliasing!S7); - - static assert(!hasRawAliasing!(void delegate())); - static assert(!hasRawAliasing!(void delegate() const)); - static assert(!hasRawAliasing!(void delegate() immutable)); - static assert(!hasRawAliasing!(void delegate() shared)); - static assert(!hasRawAliasing!(void delegate() shared const)); - static assert(!hasRawAliasing!(const(void delegate()))); - static assert(!hasRawAliasing!(immutable(void delegate()))); - - struct S8 { void delegate() a; int b; Object c; } - class S12 { typeof(S8.tupleof) a; } - class S13 { typeof(S8.tupleof) a; int* b; } - static assert(!hasRawAliasing!S8); - static assert(!hasRawAliasing!S12); - static assert( hasRawAliasing!S13); - - enum S9 { a } - static assert(!hasRawAliasing!S9); - - // indirect members - struct S10 { S7 a; int b; } - struct S11 { S6 a; int b; } - static assert( hasRawAliasing!S10); - static assert(!hasRawAliasing!S11); - - static assert( hasRawAliasing!(int[string])); - static assert(!hasRawAliasing!(immutable(int[string]))); -} - -private template hasRawAliasingImpl(T) -{ - static if (is(T foo : U*, U) && !isFunctionPointer!T) - enum hasRawAliasingImpl = !is(U == immutable); - else static if (is(T foo : U[N], U, size_t N)) - // separate static ifs to avoid forward reference - static if (is(U == class) || is(U == interface)) - enum hasRawAliasingImpl = false; - else - enum hasRawAliasingImpl = hasRawAliasingImpl!U; - else static if (is(T foo : U[], U) && !isStaticArray!(T)) - enum hasRawAliasingImpl = !is(U == immutable); - else static if (isAssociativeArray!(T)) - enum hasRawAliasingImpl = !is(T == immutable); - else - enum hasRawAliasingImpl = false; -} - -/* -Statically evaluates to `true` if and only if `T`'s -representation contains at least one non-shared field of pointer or -array type. Members of class types are not considered raw pointers. -Pointers to immutable objects are not considered raw aliasing. -*/ -private template hasRawUnsharedAliasing(T) -{ - enum hasRawUnsharedAliasing = anySatisfy!(hasRawUnsharedAliasingImpl, RepresentationTypeTuple!T); -} - -// -@safe unittest -{ - // simple types - static assert(!hasRawUnsharedAliasing!int); - static assert( hasRawUnsharedAliasing!(char*)); - static assert(!hasRawUnsharedAliasing!(shared char*)); - // references aren't raw pointers - static assert(!hasRawUnsharedAliasing!Object); - // built-in arrays do contain raw pointers - static assert( hasRawUnsharedAliasing!(int[])); - static assert(!hasRawUnsharedAliasing!(shared int[])); - // aggregate of simple types - struct S1 { int a; double b; } - static assert(!hasRawUnsharedAliasing!S1); - // indirect aggregation - struct S2 { S1 a; double b; } - static assert(!hasRawUnsharedAliasing!S2); - // struct with a pointer member - struct S3 { int a; double * b; } - static assert( hasRawUnsharedAliasing!S3); - struct S4 { int a; shared double * b; } - static assert(!hasRawUnsharedAliasing!S4); -} - -@safe unittest -{ - // struct with a pointer member - struct S3 { int a; double * b; } - static assert( hasRawUnsharedAliasing!S3); - struct S4 { int a; shared double * b; } - static assert(!hasRawUnsharedAliasing!S4); - // struct with an indirect pointer member - struct S5 { S3 a; double b; } - static assert( hasRawUnsharedAliasing!S5); - struct S6 { S4 a; double b; } - static assert(!hasRawUnsharedAliasing!S6); - struct S7 { int a; Object z; int c; } - static assert( hasRawUnsharedAliasing!S5); - static assert(!hasRawUnsharedAliasing!S6); - static assert(!hasRawUnsharedAliasing!S7); - - union S8 { int a; int b; } - union S9 { int a; int* b; } - union S10 { int a; shared int* b; } - static assert(!hasRawUnsharedAliasing!S8); - static assert( hasRawUnsharedAliasing!S9); - static assert(!hasRawUnsharedAliasing!S10); - - static assert(!hasRawUnsharedAliasing!(void delegate())); - static assert(!hasRawUnsharedAliasing!(void delegate() const)); - static assert(!hasRawUnsharedAliasing!(void delegate() immutable)); - static assert(!hasRawUnsharedAliasing!(void delegate() shared)); - static assert(!hasRawUnsharedAliasing!(void delegate() shared const)); - static assert(!hasRawUnsharedAliasing!(const(void delegate()))); - static assert(!hasRawUnsharedAliasing!(const(void delegate() const))); - static assert(!hasRawUnsharedAliasing!(const(void delegate() immutable))); - static assert(!hasRawUnsharedAliasing!(const(void delegate() shared))); - static assert(!hasRawUnsharedAliasing!(const(void delegate() shared const))); - static assert(!hasRawUnsharedAliasing!(immutable(void delegate()))); - static assert(!hasRawUnsharedAliasing!(immutable(void delegate() const))); - static assert(!hasRawUnsharedAliasing!(immutable(void delegate() immutable))); - static assert(!hasRawUnsharedAliasing!(immutable(void delegate() shared))); - static assert(!hasRawUnsharedAliasing!(immutable(void delegate() shared const))); - static assert(!hasRawUnsharedAliasing!(shared(void delegate()))); - static assert(!hasRawUnsharedAliasing!(shared(void delegate() const))); - static assert(!hasRawUnsharedAliasing!(shared(void delegate() immutable))); - static assert(!hasRawUnsharedAliasing!(shared(void delegate() shared))); - static assert(!hasRawUnsharedAliasing!(shared(void delegate() shared const))); - static assert(!hasRawUnsharedAliasing!(shared(const(void delegate())))); - static assert(!hasRawUnsharedAliasing!(shared(const(void delegate() const)))); - static assert(!hasRawUnsharedAliasing!(shared(const(void delegate() immutable)))); - static assert(!hasRawUnsharedAliasing!(shared(const(void delegate() shared)))); - static assert(!hasRawUnsharedAliasing!(shared(const(void delegate() shared const)))); - static assert(!hasRawUnsharedAliasing!(void function())); - - enum S13 { a } - static assert(!hasRawUnsharedAliasing!S13); - - // indirect members - struct S14 { S9 a; int b; } - struct S15 { S10 a; int b; } - struct S16 { S6 a; int b; } - static assert( hasRawUnsharedAliasing!S14); - static assert(!hasRawUnsharedAliasing!S15); - static assert(!hasRawUnsharedAliasing!S16); - - static assert( hasRawUnsharedAliasing!(int[string])); - static assert(!hasRawUnsharedAliasing!(shared(int[string]))); - static assert(!hasRawUnsharedAliasing!(immutable(int[string]))); - - struct S17 - { - void delegate() shared a; - void delegate() immutable b; - void delegate() shared const c; - shared(void delegate()) d; - shared(void delegate() shared) e; - shared(void delegate() immutable) f; - shared(void delegate() shared const) g; - immutable(void delegate()) h; - immutable(void delegate() shared) i; - immutable(void delegate() immutable) j; - immutable(void delegate() shared const) k; - shared(const(void delegate())) l; - shared(const(void delegate() shared)) m; - shared(const(void delegate() immutable)) n; - shared(const(void delegate() shared const)) o; - } - struct S18 { typeof(S17.tupleof) a; void delegate() p; } - struct S19 { typeof(S17.tupleof) a; Object p; } - struct S20 { typeof(S17.tupleof) a; int* p; } - class S21 { typeof(S17.tupleof) a; } - class S22 { typeof(S17.tupleof) a; void delegate() p; } - class S23 { typeof(S17.tupleof) a; Object p; } - class S24 { typeof(S17.tupleof) a; int* p; } - static assert(!hasRawUnsharedAliasing!S17); - static assert(!hasRawUnsharedAliasing!(immutable(S17))); - static assert(!hasRawUnsharedAliasing!(shared(S17))); - static assert(!hasRawUnsharedAliasing!S18); - static assert(!hasRawUnsharedAliasing!(immutable(S18))); - static assert(!hasRawUnsharedAliasing!(shared(S18))); - static assert(!hasRawUnsharedAliasing!S19); - static assert(!hasRawUnsharedAliasing!(immutable(S19))); - static assert(!hasRawUnsharedAliasing!(shared(S19))); - static assert( hasRawUnsharedAliasing!S20); - static assert(!hasRawUnsharedAliasing!(immutable(S20))); - static assert(!hasRawUnsharedAliasing!(shared(S20))); - static assert(!hasRawUnsharedAliasing!S21); - static assert(!hasRawUnsharedAliasing!(immutable(S21))); - static assert(!hasRawUnsharedAliasing!(shared(S21))); - static assert(!hasRawUnsharedAliasing!S22); - static assert(!hasRawUnsharedAliasing!(immutable(S22))); - static assert(!hasRawUnsharedAliasing!(shared(S22))); - static assert(!hasRawUnsharedAliasing!S23); - static assert(!hasRawUnsharedAliasing!(immutable(S23))); - static assert(!hasRawUnsharedAliasing!(shared(S23))); - static assert( hasRawUnsharedAliasing!S24); - static assert(!hasRawUnsharedAliasing!(immutable(S24))); - static assert(!hasRawUnsharedAliasing!(shared(S24))); - struct S25 {} - class S26 {} - interface S27 {} - union S28 {} - static assert(!hasRawUnsharedAliasing!S25); - static assert(!hasRawUnsharedAliasing!S26); - static assert(!hasRawUnsharedAliasing!S27); - static assert(!hasRawUnsharedAliasing!S28); -} - -private template hasRawUnsharedAliasingImpl(T) -{ - static if (is(T foo : U*, U) && !isFunctionPointer!T) - enum hasRawUnsharedAliasingImpl = !is(U == immutable) && !is(U == shared); - else static if (is(T foo : U[], U) && !isStaticArray!T) - enum hasRawUnsharedAliasingImpl = !is(U == immutable) && !is(U == shared); - else static if (isAssociativeArray!T) - enum hasRawUnsharedAliasingImpl = !is(T == immutable) && !is(T == shared); - else - enum hasRawUnsharedAliasingImpl = false; -} - -/* -Statically evaluates to `true` if and only if `T`'s -representation includes at least one non-immutable object reference. -*/ - -private template hasObjects(T) -{ - static if (is(T == struct)) - { - enum hasObjects = anySatisfy!(.hasObjects, RepresentationTypeTuple!T); - } - else - { - enum hasObjects = (is(T == class) || is(T == interface)) && !is(T == immutable); - } -} - -/* -Statically evaluates to `true` if and only if `T`'s -representation includes at least one non-immutable non-shared object -reference. -*/ -private template hasUnsharedObjects(T) -{ - static if (is(T == struct)) - { - enum hasUnsharedObjects = anySatisfy!(.hasUnsharedObjects, RepresentationTypeTuple!T); - } - else - { - enum hasUnsharedObjects = (is(T == class) || is(T == interface)) && - !is(T == immutable) && !is(T == shared); - } -} - -/** -Returns `true` if and only if `T`'s representation includes at -least one of the following: $(OL $(LI a raw pointer `U*` and `U` -is not immutable;) $(LI an array `U[]` and `U` is not -immutable;) $(LI a reference to a class or interface type `C` and `C` is -not immutable.) $(LI an associative array that is not immutable.) -$(LI a delegate.)) -*/ -template hasAliasing(T...) -{ - enum hasAliasing = anySatisfy!(hasAliasingImpl, T); -} - -/// -@safe unittest -{ - struct S1 { int a; Object b; } - struct S2 { string a; } - struct S3 { int a; immutable Object b; } - struct S4 { float[3] vals; } - static assert( hasAliasing!S1); - static assert(!hasAliasing!S2); - static assert(!hasAliasing!S3); - static assert(!hasAliasing!S4); -} - -@safe unittest -{ - static assert( hasAliasing!(uint[uint])); - static assert(!hasAliasing!(immutable(uint[uint]))); - static assert( hasAliasing!(void delegate())); - static assert( hasAliasing!(void delegate() const)); - static assert(!hasAliasing!(void delegate() immutable)); - static assert( hasAliasing!(void delegate() shared)); - static assert( hasAliasing!(void delegate() shared const)); - static assert( hasAliasing!(const(void delegate()))); - static assert( hasAliasing!(const(void delegate() const))); - static assert(!hasAliasing!(const(void delegate() immutable))); - static assert( hasAliasing!(const(void delegate() shared))); - static assert( hasAliasing!(const(void delegate() shared const))); - static assert(!hasAliasing!(immutable(void delegate()))); - static assert(!hasAliasing!(immutable(void delegate() const))); - static assert(!hasAliasing!(immutable(void delegate() immutable))); - static assert(!hasAliasing!(immutable(void delegate() shared))); - static assert(!hasAliasing!(immutable(void delegate() shared const))); - static assert( hasAliasing!(shared(const(void delegate())))); - static assert( hasAliasing!(shared(const(void delegate() const)))); - static assert(!hasAliasing!(shared(const(void delegate() immutable)))); - static assert( hasAliasing!(shared(const(void delegate() shared)))); - static assert( hasAliasing!(shared(const(void delegate() shared const)))); - static assert(!hasAliasing!(void function())); - - interface I; - static assert( hasAliasing!I); - - import std.typecons : Rebindable; - static assert( hasAliasing!(Rebindable!(const Object))); - static assert(!hasAliasing!(Rebindable!(immutable Object))); - static assert( hasAliasing!(Rebindable!(shared Object))); - static assert( hasAliasing!(Rebindable!Object)); - - struct S5 - { - void delegate() immutable b; - shared(void delegate() immutable) f; - immutable(void delegate() immutable) j; - shared(const(void delegate() immutable)) n; - } - struct S6 { typeof(S5.tupleof) a; void delegate() p; } - static assert(!hasAliasing!S5); - static assert( hasAliasing!S6); - - struct S7 { void delegate() a; int b; Object c; } - class S8 { int a; int b; } - class S9 { typeof(S8.tupleof) a; } - class S10 { typeof(S8.tupleof) a; int* b; } - static assert( hasAliasing!S7); - static assert( hasAliasing!S8); - static assert( hasAliasing!S9); - static assert( hasAliasing!S10); - struct S11 {} - class S12 {} - interface S13 {} - union S14 {} - static assert(!hasAliasing!S11); - static assert( hasAliasing!S12); - static assert( hasAliasing!S13); - static assert(!hasAliasing!S14); - - class S15 { S15[1] a; } - static assert( hasAliasing!S15); - static assert(!hasAliasing!(immutable(S15))); - - static assert(!hasAliasing!noreturn); -} - -private template hasAliasingImpl(T) -{ - import std.typecons : Rebindable; - - static if (is(immutable T == immutable Rebindable!R, R)) - { - enum hasAliasingImpl = hasAliasingImpl!R; - } - else - { - template isAliasingDelegate(T) - { - enum isAliasingDelegate = isDelegate!T - && !is(T == immutable) - && !is(FunctionTypeOf!T == immutable); - } - enum hasAliasingImpl = hasRawAliasing!T || hasObjects!T || - anySatisfy!(isAliasingDelegate, T, RepresentationTypeTuple!T); - } -} - -/** -Returns `true` if and only if `T`'s representation includes at -least one of the following: $(OL $(LI a raw pointer `U*`;) $(LI an -array `U[]`;) $(LI a reference to a class type `C`;) -$(LI an associative array;) $(LI a delegate;) -$(LI a [context pointer][isNested].)) - */ -template hasIndirections(T) -{ - import core.internal.traits : _hasIndirections = hasIndirections; - alias hasIndirections = _hasIndirections!T; -} - -/// -@safe unittest -{ - static assert( hasIndirections!(int[string])); - static assert( hasIndirections!(void delegate())); - static assert( hasIndirections!(void delegate() immutable)); - static assert( hasIndirections!(immutable(void delegate()))); - static assert( hasIndirections!(immutable(void delegate() immutable))); - - static assert(!hasIndirections!(void function())); - static assert( hasIndirections!(void*[1])); - static assert(!hasIndirections!(byte[1])); -} - -@safe unittest -{ - // void static array hides actual type of bits, so "may have indirections". - static assert( hasIndirections!(void[1])); - interface I {} - struct S1 {} - struct S2 { int a; } - struct S3 { int a; int b; } - struct S4 { int a; int* b; } - struct S5 { int a; Object b; } - struct S6 { int a; string b; } - struct S7 { int a; immutable Object b; } - struct S8 { int a; immutable I b; } - struct S9 { int a; void delegate() b; } - struct S10 { int a; immutable(void delegate()) b; } - struct S11 { int a; void delegate() immutable b; } - struct S12 { int a; immutable(void delegate() immutable) b; } - class S13 {} - class S14 { int a; } - class S15 { int a; int b; } - class S16 { int a; Object b; } - class S17 { string a; } - class S18 { int a; immutable Object b; } - class S19 { int a; immutable(void delegate() immutable) b; } - union S20 {} - union S21 { int a; } - union S22 { int a; int b; } - union S23 { int a; Object b; } - union S24 { string a; } - union S25 { int a; immutable Object b; } - union S26 { int a; immutable(void delegate() immutable) b; } - static assert( hasIndirections!I); - static assert(!hasIndirections!S1); - static assert(!hasIndirections!S2); - static assert(!hasIndirections!S3); - static assert( hasIndirections!S4); - static assert( hasIndirections!S5); - static assert( hasIndirections!S6); - static assert( hasIndirections!S7); - static assert( hasIndirections!S8); - static assert( hasIndirections!S9); - static assert( hasIndirections!S10); - static assert( hasIndirections!S12); - static assert( hasIndirections!S13); - static assert( hasIndirections!S14); - static assert( hasIndirections!S15); - static assert( hasIndirections!S16); - static assert( hasIndirections!S17); - static assert( hasIndirections!S18); - static assert( hasIndirections!S19); - static assert(!hasIndirections!S20); - static assert(!hasIndirections!S21); - static assert(!hasIndirections!S22); - static assert( hasIndirections!S23); - static assert( hasIndirections!S24); - static assert( hasIndirections!S25); - static assert( hasIndirections!S26); - int local; - struct HasContextPointer { int opCall() { return ++local; } } - static assert(hasIndirections!HasContextPointer); - - static assert(!hasIndirections!noreturn); -} - -// https://issues.dlang.org/show_bug.cgi?id=12000 -@safe unittest -{ - static struct S(T) - { - static assert(hasIndirections!T); - } - - static class A(T) - { - S!A a; - } - - A!int dummy; -} - -/** -Returns `true` if and only if `T`'s representation includes at -least one of the following: $(OL $(LI a raw pointer `U*` and `U` -is not immutable or shared;) $(LI an array `U[]` and `U` is not -immutable or shared;) $(LI a reference to a class type `C` and -`C` is not immutable or shared.) $(LI an associative array that is not -immutable or shared.) $(LI a delegate that is not shared.)) -*/ - -template hasUnsharedAliasing(T...) -{ - enum hasUnsharedAliasing = anySatisfy!(hasUnsharedAliasingImpl, T); -} - -/// -@safe unittest -{ - struct S1 { int a; Object b; } - struct S2 { string a; } - struct S3 { int a; immutable Object b; } - static assert( hasUnsharedAliasing!S1); - static assert(!hasUnsharedAliasing!S2); - static assert(!hasUnsharedAliasing!S3); - - struct S4 { int a; shared Object b; } - struct S5 { char[] a; } - struct S6 { shared char[] b; } - struct S7 { float[3] vals; } - static assert(!hasUnsharedAliasing!S4); - static assert( hasUnsharedAliasing!S5); - static assert(!hasUnsharedAliasing!S6); - static assert(!hasUnsharedAliasing!S7); -} - -@safe unittest -{ - /* https://issues.dlang.org/show_bug.cgi?id=6642 */ - import std.typecons : Rebindable; - struct S8 { int a; Rebindable!(immutable Object) b; } - static assert(!hasUnsharedAliasing!S8); - - static assert( hasUnsharedAliasing!(uint[uint])); - - static assert( hasUnsharedAliasing!(void delegate())); - static assert( hasUnsharedAliasing!(void delegate() const)); - static assert(!hasUnsharedAliasing!(void delegate() immutable)); - static assert(!hasUnsharedAliasing!(void delegate() shared)); - static assert(!hasUnsharedAliasing!(void delegate() shared const)); -} - -@safe unittest -{ - import std.typecons : Rebindable; - static assert( hasUnsharedAliasing!(const(void delegate()))); - static assert( hasUnsharedAliasing!(const(void delegate() const))); - static assert(!hasUnsharedAliasing!(const(void delegate() immutable))); - static assert(!hasUnsharedAliasing!(const(void delegate() shared))); - static assert(!hasUnsharedAliasing!(const(void delegate() shared const))); - static assert(!hasUnsharedAliasing!(immutable(void delegate()))); - static assert(!hasUnsharedAliasing!(immutable(void delegate() const))); - static assert(!hasUnsharedAliasing!(immutable(void delegate() immutable))); - static assert(!hasUnsharedAliasing!(immutable(void delegate() shared))); - static assert(!hasUnsharedAliasing!(immutable(void delegate() shared const))); - static assert(!hasUnsharedAliasing!(shared(void delegate()))); - static assert(!hasUnsharedAliasing!(shared(void delegate() const))); - static assert(!hasUnsharedAliasing!(shared(void delegate() immutable))); - static assert(!hasUnsharedAliasing!(shared(void delegate() shared))); - static assert(!hasUnsharedAliasing!(shared(void delegate() shared const))); - static assert(!hasUnsharedAliasing!(shared(const(void delegate())))); - static assert(!hasUnsharedAliasing!(shared(const(void delegate() const)))); - static assert(!hasUnsharedAliasing!(shared(const(void delegate() immutable)))); - static assert(!hasUnsharedAliasing!(shared(const(void delegate() shared)))); - static assert(!hasUnsharedAliasing!(shared(const(void delegate() shared const)))); - static assert(!hasUnsharedAliasing!(void function())); - - interface I {} - static assert(hasUnsharedAliasing!I); - - static assert( hasUnsharedAliasing!(Rebindable!(const Object))); - static assert(!hasUnsharedAliasing!(Rebindable!(immutable Object))); - static assert(!hasUnsharedAliasing!(Rebindable!(shared Object))); - static assert( hasUnsharedAliasing!(Rebindable!Object)); - - /* https://issues.dlang.org/show_bug.cgi?id=6979 */ - static assert(!hasUnsharedAliasing!(int, shared(int)*)); - static assert( hasUnsharedAliasing!(int, int*)); - static assert( hasUnsharedAliasing!(int, const(int)[])); - static assert( hasUnsharedAliasing!(int, shared(int)*, Rebindable!Object)); - static assert(!hasUnsharedAliasing!(shared(int)*, Rebindable!(shared Object))); - static assert(!hasUnsharedAliasing!()); - - struct S9 - { - void delegate() shared a; - void delegate() immutable b; - void delegate() shared const c; - shared(void delegate()) d; - shared(void delegate() shared) e; - shared(void delegate() immutable) f; - shared(void delegate() shared const) g; - immutable(void delegate()) h; - immutable(void delegate() shared) i; - immutable(void delegate() immutable) j; - immutable(void delegate() shared const) k; - shared(const(void delegate())) l; - shared(const(void delegate() shared)) m; - shared(const(void delegate() immutable)) n; - shared(const(void delegate() shared const)) o; - } - struct S10 { typeof(S9.tupleof) a; void delegate() p; } - struct S11 { typeof(S9.tupleof) a; Object p; } - struct S12 { typeof(S9.tupleof) a; int* p; } - class S13 { typeof(S9.tupleof) a; } - class S14 { typeof(S9.tupleof) a; void delegate() p; } - class S15 { typeof(S9.tupleof) a; Object p; } - class S16 { typeof(S9.tupleof) a; int* p; } - static assert(!hasUnsharedAliasing!S9); - static assert(!hasUnsharedAliasing!(immutable(S9))); - static assert(!hasUnsharedAliasing!(shared(S9))); - static assert( hasUnsharedAliasing!S10); - static assert(!hasUnsharedAliasing!(immutable(S10))); - static assert(!hasUnsharedAliasing!(shared(S10))); - static assert( hasUnsharedAliasing!S11); - static assert(!hasUnsharedAliasing!(immutable(S11))); - static assert(!hasUnsharedAliasing!(shared(S11))); - static assert( hasUnsharedAliasing!S12); - static assert(!hasUnsharedAliasing!(immutable(S12))); - static assert(!hasUnsharedAliasing!(shared(S12))); - static assert( hasUnsharedAliasing!S13); - static assert(!hasUnsharedAliasing!(immutable(S13))); - static assert(!hasUnsharedAliasing!(shared(S13))); - static assert( hasUnsharedAliasing!S14); - static assert(!hasUnsharedAliasing!(immutable(S14))); - static assert(!hasUnsharedAliasing!(shared(S14))); - static assert( hasUnsharedAliasing!S15); - static assert(!hasUnsharedAliasing!(immutable(S15))); - static assert(!hasUnsharedAliasing!(shared(S15))); - static assert( hasUnsharedAliasing!S16); - static assert(!hasUnsharedAliasing!(immutable(S16))); - static assert(!hasUnsharedAliasing!(shared(S16))); - struct S17 {} - class S18 {} - interface S19 {} - union S20 {} - static assert(!hasUnsharedAliasing!S17); - static assert( hasUnsharedAliasing!S18); - static assert( hasUnsharedAliasing!S19); - static assert(!hasUnsharedAliasing!S20); - - static assert(!hasUnsharedAliasing!noreturn); -} - -private template hasUnsharedAliasingImpl(T) -{ - import std.typecons : Rebindable; - - static if (is(immutable T == immutable Rebindable!R, R)) - { - enum hasUnsharedAliasingImpl = hasUnsharedAliasingImpl!R; - } - else - { - template unsharedDelegate(T) - { - enum bool unsharedDelegate = isDelegate!T - && !is(T == shared) - && !is(T == immutable) - && !is(FunctionTypeOf!T == shared) - && !is(FunctionTypeOf!T == immutable); - } - - enum hasUnsharedAliasingImpl = - hasRawUnsharedAliasing!T || - anySatisfy!(unsharedDelegate, RepresentationTypeTuple!T) || - hasUnsharedObjects!T; - } -} - -version (StdDdoc) -{ - /** - True if `S` or any type embedded directly in the representation of `S` - defines an elaborate copy constructor. Elaborate copy constructors are - introduced by defining `this(this)` for a `struct`. - - Classes and unions never have elaborate copy constructors. - */ - template hasElaborateCopyConstructor(S) - { - import core.internal.traits : hasElabCCtor = hasElaborateCopyConstructor; - alias hasElaborateCopyConstructor = hasElabCCtor!(S); - } -} -else -{ - import core.internal.traits : hasElabCCtor = hasElaborateCopyConstructor; - alias hasElaborateCopyConstructor = hasElabCCtor; -} - -/// -@safe unittest -{ - static assert(!hasElaborateCopyConstructor!int); - - static struct S1 { } - static struct S2 { this(this) {} } - static struct S3 { S2 field; } - static struct S4 { S3[1] field; } - static struct S5 { S3[] field; } - static struct S6 { S3[0] field; } - static struct S7 { @disable this(); S3 field; } - static assert(!hasElaborateCopyConstructor!S1); - static assert( hasElaborateCopyConstructor!S2); - static assert( hasElaborateCopyConstructor!(immutable S2)); - static assert( hasElaborateCopyConstructor!S3); - static assert( hasElaborateCopyConstructor!(S3[1])); - static assert(!hasElaborateCopyConstructor!(S3[0])); - static assert( hasElaborateCopyConstructor!S4); - static assert(!hasElaborateCopyConstructor!S5); - static assert(!hasElaborateCopyConstructor!S6); - static assert( hasElaborateCopyConstructor!S7); -} - -/** - True if `S` or any type directly embedded in the representation of `S` - defines an elaborate assignment. Elaborate assignments are introduced by - defining `opAssign(typeof(this))` or $(D opAssign(ref typeof(this))) - for a `struct` or when there is a compiler-generated `opAssign`. - - A type `S` gets compiler-generated `opAssign` if it has - an elaborate destructor. - - Classes and unions never have elaborate assignments. - - Note: Structs with (possibly nested) postblit operator(s) will have a - hidden yet elaborate compiler generated assignment operator (unless - explicitly disabled). - */ -template hasElaborateAssign(S) -{ - static if (isStaticArray!S && S.length) - { - enum bool hasElaborateAssign = hasElaborateAssign!(typeof(S.init[0])); - } - else static if (is(S == struct)) - { - enum hasElaborateAssign = is(typeof(S.init.opAssign(rvalueOf!S))) || - is(typeof(S.init.opAssign(lvalueOf!S))) || - anySatisfy!(.hasElaborateAssign, FieldTypeTuple!S); - } - else - { - enum bool hasElaborateAssign = false; - } -} - -/// -@safe unittest -{ - static assert(!hasElaborateAssign!int); - - static struct S { void opAssign(S) {} } - static assert( hasElaborateAssign!S); - static assert(!hasElaborateAssign!(const(S))); - - static struct S1 { void opAssign(ref S1) {} } - static struct S2 { void opAssign(int) {} } - static struct S3 { S s; } - static assert( hasElaborateAssign!S1); - static assert(!hasElaborateAssign!S2); - static assert( hasElaborateAssign!S3); - static assert( hasElaborateAssign!(S3[1])); - static assert(!hasElaborateAssign!(S3[0])); -} - -@safe unittest -{ - static struct S { void opAssign(S) {} } - static struct S4 - { - void opAssign(U)(U u) {} - @disable void opAssign(U)(ref U u); - } - static assert( hasElaborateAssign!S4); - - static struct S41 - { - void opAssign(U)(ref U u) {} - @disable void opAssign(U)(U u); - } - static assert( hasElaborateAssign!S41); - - static struct S5 { @disable this(); this(int n){ s = S(); } S s; } - static assert( hasElaborateAssign!S5); - - static struct S6 { this(this) {} } - static struct S7 { this(this) {} @disable void opAssign(S7); } - static struct S8 { this(this) {} @disable void opAssign(S8); void opAssign(int) {} } - static struct S9 { this(this) {} void opAssign(int) {} } - static struct S10 { ~this() { } } - static assert( hasElaborateAssign!S6); - static assert(!hasElaborateAssign!S7); - static assert(!hasElaborateAssign!S8); - static assert( hasElaborateAssign!S9); - static assert( hasElaborateAssign!S10); - static struct SS6 { S6 s; } - static struct SS7 { S7 s; } - static struct SS8 { S8 s; } - static struct SS9 { S9 s; } - static assert( hasElaborateAssign!SS6); - static assert(!hasElaborateAssign!SS7); - static assert(!hasElaborateAssign!SS8); - static assert( hasElaborateAssign!SS9); -} - -version (StdDdoc) -{ - /** - True if `S` or any type directly embedded in the representation - of `S` defines an elaborate destructor. Elaborate destructors - are introduced by defining `~this()` for a $(D - struct). - - Classes and unions never have elaborate destructors, even - though classes may define `~this()`. - */ - template hasElaborateDestructor(S) - { - import core.internal.traits : hasElabDest = hasElaborateDestructor; - alias hasElaborateDestructor = hasElabDest!(S); - } -} -else -{ - import core.internal.traits : hasElabDest = hasElaborateDestructor; - alias hasElaborateDestructor = hasElabDest; -} - -/// -@safe unittest -{ - static assert(!hasElaborateDestructor!int); - - static struct S1 { } - static struct S2 { ~this() {} } - static struct S3 { S2 field; } - static struct S4 { S3[1] field; } - static struct S5 { S3[] field; } - static struct S6 { S3[0] field; } - static struct S7 { @disable this(); S3 field; } - static assert(!hasElaborateDestructor!S1); - static assert( hasElaborateDestructor!S2); - static assert( hasElaborateDestructor!(immutable S2)); - static assert( hasElaborateDestructor!S3); - static assert( hasElaborateDestructor!(S3[1])); - static assert(!hasElaborateDestructor!(S3[0])); - static assert( hasElaborateDestructor!S4); - static assert(!hasElaborateDestructor!S5); - static assert(!hasElaborateDestructor!S6); - static assert( hasElaborateDestructor!S7); -} - -version (StdDdoc) -{ - /** - True if `S` or any type embedded directly in the representation of `S` - defines elaborate move semantics. Elaborate move semantics are - introduced by defining `opPostMove(ref typeof(this))` for a `struct`. - - Classes and unions never have elaborate move semantics. - */ - template hasElaborateMove(S) - { - import core.internal.traits : hasElabMove = hasElaborateMove; - alias hasElaborateMove = hasElabMove!(S); - } -} -else -{ - import core.internal.traits : hasElabMove = hasElaborateMove; - alias hasElaborateMove = hasElabMove; -} - -/// -@safe unittest -{ - static assert(!hasElaborateMove!int); - - static struct S1 { } - static struct S2 { void opPostMove(ref S2) {} } - static struct S3 { void opPostMove(inout ref S3) inout {} } - static struct S4 { void opPostMove(const ref S4) {} } - static struct S5 { void opPostMove(S5) {} } - static struct S6 { void opPostMove(int) {} } - static struct S7 { S3[1] field; } - static struct S8 { S3[] field; } - static struct S9 { S3[0] field; } - static struct S10 { @disable this(); S3 field; } - static assert(!hasElaborateMove!S1); - static assert( hasElaborateMove!S2); - static assert( hasElaborateMove!S3); - static assert( hasElaborateMove!(immutable S3)); - static assert( hasElaborateMove!S4); - static assert(!hasElaborateMove!S5); - static assert(!hasElaborateMove!S6); - static assert( hasElaborateMove!S7); - static assert(!hasElaborateMove!S8); - static assert(!hasElaborateMove!S9); - static assert( hasElaborateMove!S10); -} - -package alias Identity(alias A) = A; - -/** - Yields `true` if and only if `T` is an aggregate that defines - a symbol called `name`. - - See also: $(DDSUBLINK spec/traits, hasMember, `__traits(hasMember, T, name)`) - */ -enum hasMember(T, string name) = __traits(hasMember, T, name); - -/// -@safe unittest -{ - static assert(!hasMember!(int, "blah")); - struct S1 { int blah; } - struct S2 { int blah(){ return 0; } } - class C1 { int blah; } - class C2 { int blah(){ return 0; } } - static assert(hasMember!(S1, "blah")); - static assert(hasMember!(S2, "blah")); - static assert(hasMember!(C1, "blah")); - static assert(hasMember!(C2, "blah")); -} - -@safe unittest -{ - // https://issues.dlang.org/show_bug.cgi?id=8321 - struct S { - int x; - void f(){} - void t()(){} - template T(){} - } - struct R1(T) { - T t; - alias t this; - } - struct R2(T) { - T t; - @property ref inout(T) payload() inout { return t; } - alias t this; - } - static assert(hasMember!(S, "x")); - static assert(hasMember!(S, "f")); - static assert(hasMember!(S, "t")); - static assert(hasMember!(S, "T")); - static assert(hasMember!(R1!S, "x")); - static assert(hasMember!(R1!S, "f")); - static assert(hasMember!(R1!S, "t")); - static assert(hasMember!(R1!S, "T")); - static assert(hasMember!(R2!S, "x")); - static assert(hasMember!(R2!S, "f")); - static assert(hasMember!(R2!S, "t")); - static assert(hasMember!(R2!S, "T")); -} - -@safe unittest -{ - static struct S - { - void opDispatch(string n, A)(A dummy) {} - } - static assert(hasMember!(S, "foo")); -} - -/** - * Whether the symbol represented by the string, member, exists and is a static member of T. - * - * Params: - * T = Type containing symbol `member`. - * member = Name of symbol to test that resides in `T`. - * - * Returns: - * `true` iff `member` exists and is static. - */ -template hasStaticMember(T, string member) -{ - static if (__traits(hasMember, T, member)) - { - static if (is(T == V*, V)) - alias U = V; - else - alias U = T; - - import std.meta : Alias; - alias sym = Alias!(__traits(getMember, U, member)); - - static if (__traits(getOverloads, U, member).length == 0) - enum bool hasStaticMember = __traits(compiles, &sym); - else - enum bool hasStaticMember = __traits(isStaticFunction, sym); - } - else - { - enum bool hasStaticMember = false; - } -} - -/// -@safe unittest -{ - static struct S - { - static void sf() {} - void f() {} - - static int si; - int i; - } - - static assert( hasStaticMember!(S, "sf")); - static assert(!hasStaticMember!(S, "f")); - - static assert( hasStaticMember!(S, "si")); - static assert(!hasStaticMember!(S, "i")); - - static assert(!hasStaticMember!(S, "hello")); -} - -@safe unittest -{ - static struct S - { - enum X = 10; - enum Y - { - i = 10 - } - struct S {} - class C {} - - static int sx = 0; - __gshared int gx = 0; - - Y y; - static Y sy; - - static void f(); - static void f2() pure nothrow @nogc @safe; - - void g() shared; - - static void function() fp; - __gshared void function() gfp; - void function() fpm; - - void delegate() dm; - static void delegate() sd; - - void m(); - void m2() const pure nothrow @nogc @safe; - - inout(int) iom() inout; - static inout(int) iosf(inout int x); - - @property int p(); - static @property int sp(); - } - - static class C - { - enum X = 10; - enum Y - { - i = 10 - } - struct S {} - class C {} - - static int sx = 0; - __gshared int gx = 0; - - Y y; - static Y sy; - - static void f(); - static void f2() pure nothrow @nogc @safe; - - void g() shared { } - - static void function() fp; - __gshared void function() gfp; - void function() fpm; - - void delegate() dm; - static void delegate() sd; - - void m() {} - final void m2() const pure nothrow @nogc @safe; - - inout(int) iom() inout { return 10; } - static inout(int) iosf(inout int x); - - @property int p() { return 10; } - static @property int sp(); - } - - static assert(!hasStaticMember!(S, "na")); - static assert(!hasStaticMember!(S, "X")); - static assert(!hasStaticMember!(S, "Y")); - static assert(!hasStaticMember!(S, "Y.i")); - static assert(!hasStaticMember!(S, "S")); - static assert(!hasStaticMember!(S, "C")); - static assert( hasStaticMember!(S, "sx")); - static assert( hasStaticMember!(S, "gx")); - static assert(!hasStaticMember!(S, "y")); - static assert( hasStaticMember!(S, "sy")); - static assert( hasStaticMember!(S, "f")); - static assert( hasStaticMember!(S, "f2")); - static assert(!hasStaticMember!(S, "dm")); - static assert( hasStaticMember!(S, "sd")); - static assert(!hasStaticMember!(S, "g")); - static assert( hasStaticMember!(S, "fp")); - static assert( hasStaticMember!(S, "gfp")); - static assert(!hasStaticMember!(S, "fpm")); - static assert(!hasStaticMember!(S, "m")); - static assert(!hasStaticMember!(S, "m2")); - static assert(!hasStaticMember!(S, "iom")); - static assert( hasStaticMember!(S, "iosf")); - static assert(!hasStaticMember!(S, "p")); - static assert( hasStaticMember!(S, "sp")); - - static assert(!hasStaticMember!(C, "na")); - static assert(!hasStaticMember!(C, "X")); - static assert(!hasStaticMember!(C, "Y")); - static assert(!hasStaticMember!(C, "Y.i")); - static assert(!hasStaticMember!(C, "S")); - static assert(!hasStaticMember!(C, "C")); - static assert( hasStaticMember!(C, "sx")); - static assert( hasStaticMember!(C, "gx")); - static assert(!hasStaticMember!(C, "y")); - static assert( hasStaticMember!(C, "sy")); - static assert( hasStaticMember!(C, "f")); - static assert( hasStaticMember!(C, "f2")); - static assert(!hasStaticMember!(C, "dm")); - static assert( hasStaticMember!(C, "sd")); - static assert(!hasStaticMember!(C, "g")); - static assert( hasStaticMember!(C, "fp")); - static assert( hasStaticMember!(C, "gfp")); - static assert(!hasStaticMember!(C, "fpm")); - static assert(!hasStaticMember!(C, "m")); - static assert(!hasStaticMember!(C, "m2")); - static assert(!hasStaticMember!(C, "iom")); - static assert( hasStaticMember!(C, "iosf")); - static assert(!hasStaticMember!(C, "p")); - static assert( hasStaticMember!(C, "sp")); - - alias P = S*; - static assert(!hasStaticMember!(P, "na")); - static assert(!hasStaticMember!(P, "X")); - static assert(!hasStaticMember!(P, "Y")); - static assert(!hasStaticMember!(P, "Y.i")); - static assert(!hasStaticMember!(P, "S")); - static assert(!hasStaticMember!(P, "C")); - static assert( hasStaticMember!(P, "sx")); - static assert( hasStaticMember!(P, "gx")); - static assert(!hasStaticMember!(P, "y")); - static assert( hasStaticMember!(P, "sy")); - static assert( hasStaticMember!(P, "f")); - static assert( hasStaticMember!(P, "f2")); - static assert(!hasStaticMember!(P, "dm")); - static assert( hasStaticMember!(P, "sd")); - static assert(!hasStaticMember!(P, "g")); - static assert( hasStaticMember!(P, "fp")); - static assert( hasStaticMember!(P, "gfp")); - static assert(!hasStaticMember!(P, "fpm")); - static assert(!hasStaticMember!(P, "m")); - static assert(!hasStaticMember!(P, "m2")); - static assert(!hasStaticMember!(P, "iom")); - static assert( hasStaticMember!(P, "iosf")); - static assert(!hasStaticMember!(P, "p")); - static assert( hasStaticMember!(P, "sp")); -} - -/** -Retrieves the members of an enumerated type `enum E`. - -Params: - E = An enumerated type. `E` may have duplicated values. - -Returns: - Static tuple composed of the members of the enumerated type `E`. - The members are arranged in the same order as declared in `E`. - The name of the enum can be found by querying the compiler for the - name of the identifier, i.e. `__traits(identifier, EnumMembers!MyEnum[i])`. - For enumerations with unique values, $(REF to, std,conv) can also be used. - -Note: - An enum can have multiple members which have the same value. If you want - to use EnumMembers to e.g. generate switch cases at compile-time, - you should use the $(REF NoDuplicates, std,meta) template to avoid - generating duplicate switch cases. - -Note: - Returned values are strictly typed with `E`. Thus, the following code - does not work without the explicit cast: --------------------- -enum E : int { a, b, c } -int[] abc = cast(int[]) [ EnumMembers!E ]; --------------------- - Cast is not necessary if the type of the variable is inferred. See the - example below. - */ -template EnumMembers(E) -if (is(E == enum)) -{ - alias EnumMembers = AliasSeq!(); - static foreach (M; __traits(allMembers, E)) - EnumMembers = AliasSeq!(EnumMembers, __traits(getMember, E, M)); -} - -/// Create an array of enumerated values -@safe unittest -{ - enum Sqrts : real - { - one = 1, - two = 1.41421, - three = 1.73205 - } - auto sqrts = [EnumMembers!Sqrts]; - assert(sqrts == [Sqrts.one, Sqrts.two, Sqrts.three]); -} - -/** -A generic function `rank(v)` in the following example uses this -template for finding a member `e` in an enumerated type `E`. - */ -@safe unittest -{ - // Returns i if e is the i-th enumerator of E. - static size_t rank(E)(E e) - if (is(E == enum)) - { - static foreach (i, member; EnumMembers!E) - { - if (e == member) - return i; - } - assert(0, "Not an enum member"); - } - - enum Mode - { - read = 1, - write = 2, - map = 4 - } - assert(rank(Mode.read) == 0); - assert(rank(Mode.write) == 1); - assert(rank(Mode.map) == 2); -} - -/** -Use EnumMembers to generate a switch statement using static foreach. -*/ - -@safe unittest -{ - import std.conv : to; - class FooClass - { - string calledMethod; - void foo() @safe { calledMethod = "foo"; } - void bar() @safe { calledMethod = "bar"; } - void baz() @safe { calledMethod = "baz"; } - } - - enum FooEnum { foo, bar, baz } - - auto var = FooEnum.bar; - auto fooObj = new FooClass(); - s: final switch (var) - { - static foreach (member; EnumMembers!FooEnum) - { - case member: // Generate a case for each enum value. - // Call fooObj.{name of enum value}(). - __traits(getMember, fooObj, to!string(member))(); - break s; - } - } - // As we pass in FooEnum.bar, the bar() method gets called. - assert(fooObj.calledMethod == "bar"); -} - -@safe unittest -{ - enum A { a } - static assert([ EnumMembers!A ] == [ A.a ]); - enum B { a, b, c, d, e } - static assert([ EnumMembers!B ] == [ B.a, B.b, B.c, B.d, B.e ]); -} - -@safe unittest // typed enums -{ - enum A : string { a = "alpha", b = "beta" } - static assert([ EnumMembers!A ] == [ A.a, A.b ]); - - static struct S - { - int value; - int opCmp(S rhs) const nothrow { return value - rhs.value; } - } - enum B : S { a = S(1), b = S(2), c = S(3) } - static assert([ EnumMembers!B ] == [ B.a, B.b, B.c ]); -} - -@safe unittest // duplicated values -{ - enum A - { - a = 0, b = 0, - c = 1, d = 1, e - } - static assert([ EnumMembers!A ] == [ A.a, A.b, A.c, A.d, A.e ]); -} - -// https://issues.dlang.org/show_bug.cgi?id=14561: huge enums -@safe unittest -{ - string genEnum() - { - string result = "enum TLAs {"; - foreach (c0; '0'..'2'+1) - foreach (c1; '0'..'9'+1) - foreach (c2; '0'..'9'+1) - foreach (c3; '0'..'9'+1) - { - result ~= '_'; - result ~= c0; - result ~= c1; - result ~= c2; - result ~= c3; - result ~= ','; - } - result ~= '}'; - return result; - } - mixin(genEnum); - static assert(EnumMembers!TLAs[0] == TLAs._0000); - static assert(EnumMembers!TLAs[$-1] == TLAs._2999); -} - -@safe unittest -{ - enum E { member, a = 0, b = 0 } - static assert(__traits(identifier, EnumMembers!E[0]) == "member"); - static assert(__traits(identifier, EnumMembers!E[1]) == "a"); - static assert(__traits(identifier, EnumMembers!E[2]) == "b"); -} - - -//:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::// -// Classes and Interfaces -//:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::// - -/*** - * Get a $(D_PARAM AliasSeq) of the base class and base interfaces of - * this class or interface. $(D_PARAM BaseTypeTuple!Object) returns - * the empty type tuple. - */ -template BaseTypeTuple(A) -{ - static if (is(A P == super)) - alias BaseTypeTuple = P; - else - static assert(0, "argument is not a class or interface"); -} - -/// -@safe unittest -{ - import std.meta : AliasSeq; - - interface I1 { } - interface I2 { } - interface I12 : I1, I2 { } - static assert(is(BaseTypeTuple!I12 == AliasSeq!(I1, I2))); - - interface I3 : I1 { } - interface I123 : I1, I2, I3 { } - static assert(is(BaseTypeTuple!I123 == AliasSeq!(I1, I2, I3))); -} - -@safe unittest -{ - interface I1 { } - interface I2 { } - class A { } - class C : A, I1, I2 { } - - alias TL = BaseTypeTuple!C; - assert(TL.length == 3); - assert(is (TL[0] == A)); - assert(is (TL[1] == I1)); - assert(is (TL[2] == I2)); - - assert(BaseTypeTuple!Object.length == 0); -} - -/** - * Get a $(D_PARAM AliasSeq) of $(I all) base classes of this class, - * in decreasing order. Interfaces are not included. $(D_PARAM - * BaseClassesTuple!Object) yields the empty type tuple. - */ -template BaseClassesTuple(T) -if (is(T == class)) -{ - static if (is(T == Object)) - { - alias BaseClassesTuple = AliasSeq!(); - } - else static if (is(BaseTypeTuple!T[0] == Object)) - { - alias BaseClassesTuple = AliasSeq!Object; - } - else static if (!is(BaseTypeTuple!T[0] == Object) && !is(BaseTypeTuple!T[0] == class)) - { - alias BaseClassesTuple = AliasSeq!(); - } - else - { - alias BaseClassesTuple = - AliasSeq!(BaseTypeTuple!T[0], - BaseClassesTuple!(BaseTypeTuple!T[0])); - } -} - -/// -@safe unittest -{ - import std.meta : AliasSeq; - - class C1 { } - class C2 : C1 { } - class C3 : C2 { } - static assert(!BaseClassesTuple!Object.length); - static assert(is(BaseClassesTuple!C1 == AliasSeq!(Object))); - static assert(is(BaseClassesTuple!C2 == AliasSeq!(C1, Object))); - static assert(is(BaseClassesTuple!C3 == AliasSeq!(C2, C1, Object))); -} - -// https://issues.dlang.org/show_bug.cgi?id=17276 -@safe unittest -{ - extern (C++) static interface Ext - { - void someext(); - } - - extern (C++) static class E : Ext - { - void someext() {} - } - - alias BaseClassesWithNoObject = BaseClassesTuple!E; -} - -@safe unittest -{ - struct S { } - static assert(!__traits(compiles, BaseClassesTuple!S)); - interface I { } - static assert(!__traits(compiles, BaseClassesTuple!I)); - class C4 : I { } - class C5 : C4, I { } - static assert(is(BaseClassesTuple!C5 == AliasSeq!(C4, Object))); -} - -/** -Params: - T = The `class` or `interface` to search. - -Returns: - $(REF AliasSeq,std,meta) of all interfaces directly or - indirectly inherited by this class or interface. Interfaces - do not repeat if multiply implemented. - - `InterfacesTuple!Object` yields an empty `AliasSeq`. - */ -template InterfacesTuple(T) -{ - import std.meta : NoDuplicates; - template Flatten(H, T...) - { - static if (T.length) - { - alias Flatten = AliasSeq!(Flatten!H, Flatten!T); - } - else - { - static if (is(H == interface)) - alias Flatten = AliasSeq!(H, InterfacesTuple!H); - else - alias Flatten = InterfacesTuple!H; - } - } - - static if (is(T S == super) && S.length) - alias InterfacesTuple = NoDuplicates!(Flatten!S); - else - alias InterfacesTuple = AliasSeq!(); -} - -/// -@safe unittest -{ - interface I1 {} - interface I2 {} - class A : I1, I2 {} - class B : A, I1 {} - class C : B {} - - alias TL = InterfacesTuple!C; - static assert(is(TL[0] == I1) && is(TL[1] == I2)); -} - -@safe unittest -{ - interface Iaa {} - interface Iab {} - interface Iba {} - interface Ibb {} - interface Ia : Iaa, Iab {} - interface Ib : Iba, Ibb {} - interface I : Ia, Ib {} - interface J {} - class B2 : J {} - class C2 : B2, Ia, Ib {} - static assert(is(InterfacesTuple!I == - AliasSeq!(Ia, Iaa, Iab, Ib, Iba, Ibb))); - static assert(is(InterfacesTuple!C2 == - AliasSeq!(J, Ia, Iaa, Iab, Ib, Iba, Ibb))); - -} - -/** - * Get a $(D_PARAM AliasSeq) of $(I all) base classes of $(D_PARAM - * T), in decreasing order, followed by $(D_PARAM T)'s - * interfaces. $(D_PARAM TransitiveBaseTypeTuple!Object) yields the - * empty type tuple. - */ -alias TransitiveBaseTypeTuple(T) = AliasSeq!(BaseClassesTuple!T, InterfacesTuple!T); - -/// -@safe unittest -{ - interface J1 {} - interface J2 {} - class B1 {} - class B2 : B1, J1, J2 {} - class B3 : B2, J1 {} - alias TL = TransitiveBaseTypeTuple!B3; - assert(TL.length == 5); - assert(is (TL[0] == B2)); - assert(is (TL[1] == B1)); - assert(is (TL[2] == Object)); - assert(is (TL[3] == J1)); - assert(is (TL[4] == J2)); - - assert(TransitiveBaseTypeTuple!Object.length == 0); -} - - -/** -Returns a tuple of non-static functions with the name `name` declared in the -class or interface `C`. Covariant duplicates are shrunk into the most -derived one. - */ -template MemberFunctionsTuple(C, string name) -if (is(C == class) || is(C == interface)) -{ - static if (__traits(hasMember, C, name)) - { - /* - * First, collect all overloads in the class hierarchy. - */ - template CollectOverloads(Node) - { - static if (__traits(hasMember, Node, name) && __traits(compiles, __traits(getMember, Node, name))) - { - // Get all overloads in sight (not hidden). - alias inSight = __traits(getVirtualMethods, Node, name); - - // And collect all overloads in ancestor classes to reveal hidden - // methods. The result may contain duplicates. - template walkThru(Parents...) - { - static if (Parents.length > 0) - alias walkThru = AliasSeq!( - CollectOverloads!(Parents[0]), - walkThru!(Parents[1 .. $]) - ); - else - alias walkThru = AliasSeq!(); - } - - static if (is(Node Parents == super)) - alias CollectOverloads = AliasSeq!(inSight, walkThru!Parents); - else - alias CollectOverloads = AliasSeq!inSight; - } - else - alias CollectOverloads = AliasSeq!(); // no overloads in this hierarchy - } - - static if (name == "__ctor" || name == "__dtor") - alias overloads = AliasSeq!(__traits(getOverloads, C, name)); - else - // duplicates in this tuple will be removed by shrink() - alias overloads = CollectOverloads!C; - - // shrinkOne!args[0] = the most derived one in the covariant siblings of target - // shrinkOne!args[1..$] = non-covariant others - template shrinkOne(/+ alias target, rest... +/ args...) - { - import std.meta : AliasSeq; - alias target = args[0 .. 1]; // prevent property functions from being evaluated - alias rest = args[1 .. $]; - - static if (rest.length > 0) - { - alias Target = FunctionTypeOf!target; - alias Rest0 = FunctionTypeOf!(rest[0]); - - static if (isCovariantWith!(Target, Rest0) && isCovariantWith!(Rest0, Target)) - { - // One of these overrides the other. Choose the one from the most derived parent. - static if (is(__traits(parent, target) : __traits(parent, rest[0]))) - alias shrinkOne = shrinkOne!(target, rest[1 .. $]); - else - alias shrinkOne = shrinkOne!(rest[0], rest[1 .. $]); - } - else static if (isCovariantWith!(Target, Rest0)) - // target overrides rest[0] -- erase rest[0]. - alias shrinkOne = shrinkOne!(target, rest[1 .. $]); - else static if (isCovariantWith!(Rest0, Target)) - // rest[0] overrides target -- erase target. - alias shrinkOne = shrinkOne!(rest[0], rest[1 .. $]); - else - // target and rest[0] are distinct. - alias shrinkOne = AliasSeq!( - shrinkOne!(target, rest[1 .. $]), - rest[0] // keep - ); - } - else - alias shrinkOne = AliasSeq!target; // done - } - - /* - * Now shrink covariant overloads into one. - */ - template shrink(overloads...) - { - static if (overloads.length > 0) - { - alias temp = shrinkOne!overloads; - alias shrink = AliasSeq!(temp[0], shrink!(temp[1 .. $])); - } - else - alias shrink = AliasSeq!(); // done - } - - // done. - alias MemberFunctionsTuple = shrink!overloads; - } - else - alias MemberFunctionsTuple = AliasSeq!(); -} - -/// -@safe unittest -{ - interface I { I foo(); } - class B - { - real foo(real v) { return v; } - } - class C : B, I - { - override C foo() { return this; } // covariant overriding of I.foo() - } - alias foos = MemberFunctionsTuple!(C, "foo"); - static assert(foos.length == 2); - static assert(__traits(isSame, foos[0], C.foo)); - static assert(__traits(isSame, foos[1], B.foo)); -} - -// https://issues.dlang.org/show_bug.cgi?id=15920 -@safe unittest -{ - import std.meta : AliasSeq; - class A - { - void f(){} - void f(int){} - } - class B : A - { - override void f(){} - override void f(int){} - } - alias fs = MemberFunctionsTuple!(B, "f"); - alias bfs = __traits(getOverloads, B, "f"); - assert(__traits(isSame, fs[0], bfs[0]) || __traits(isSame, fs[0], bfs[1])); - assert(__traits(isSame, fs[1], bfs[0]) || __traits(isSame, fs[1], bfs[1])); -} - -// https://issues.dlang.org/show_bug.cgi?id=8388 -@safe unittest -{ - class C - { - this() {} - this(int i) {} - this(int i, float j) {} - this(string s) {} - - /* - Commented out, because this causes a cyclic dependency - between module constructors/destructors error. Might - be caused by https://issues.dlang.org/show_bug.cgi?id=20529. */ - // static this() {} - - ~this() {} - } - - class D : C - { - this() {} - ~this() {} - } - - alias test_ctor = MemberFunctionsTuple!(C, "__ctor"); - assert(test_ctor.length == 4); - alias test_dtor = MemberFunctionsTuple!(C, "__dtor"); - assert(test_dtor.length == 1); - alias test2_ctor = MemberFunctionsTuple!(D, "__ctor"); - assert(test2_ctor.length == 1); - alias test2_dtor = MemberFunctionsTuple!(D, "__dtor"); - assert(test2_dtor.length == 1); -} - -@safe unittest -{ - interface I { I test(); } - interface J : I { J test(); } - interface K { K test(int); } - class B : I, K - { - K test(int) { return this; } - B test() { return this; } - static void test(string) { } - } - class C : B, J - { - override C test() { return this; } - } - alias test =MemberFunctionsTuple!(C, "test"); - static assert(test.length == 2); - static assert(is(FunctionTypeOf!(test[0]) == FunctionTypeOf!(C.test))); - static assert(is(FunctionTypeOf!(test[1]) == FunctionTypeOf!(K.test))); - alias noexist = MemberFunctionsTuple!(C, "noexist"); - static assert(noexist.length == 0); - - interface L { int prop() @property; } - alias prop = MemberFunctionsTuple!(L, "prop"); - static assert(prop.length == 1); - - interface Test_I - { - void foo(); - void foo(int); - void foo(int, int); - } - interface Test : Test_I {} - alias Test_foo = MemberFunctionsTuple!(Test, "foo"); - static assert(Test_foo.length == 3); - static assert(is(typeof(&Test_foo[0]) == void function())); - static assert(is(typeof(&Test_foo[2]) == void function(int))); - static assert(is(typeof(&Test_foo[1]) == void function(int, int))); -} - - -/** -Returns an alias to the template that `T` is an instance of. -It will return `void` if a symbol without a template is given. - */ -alias TemplateOf(alias T : Base!Args, alias Base, Args...) = Base; - -/// ditto -alias TemplateOf(T : Base!Args, alias Base, Args...) = Base; - -/// ditto -alias TemplateOf(T) = void; - -/// -@safe unittest -{ - struct Foo(T, U) {} - static assert(__traits(isSame, TemplateOf!(Foo!(int, real)), Foo)); -} - -@safe unittest -{ - template Foo1(A) {} - template Foo2(A, B) {} - template Foo3(alias A) {} - template Foo4(string A) {} - struct Foo5(A) {} - struct Foo6(A, B) {} - struct Foo7(alias A) {} - template Foo8(A) { template Foo9(B) {} } - template Foo10() {} - - static assert(__traits(isSame, TemplateOf!(Foo1!(int)), Foo1)); - static assert(__traits(isSame, TemplateOf!(Foo2!(int, int)), Foo2)); - static assert(__traits(isSame, TemplateOf!(Foo3!(123)), Foo3)); - static assert(__traits(isSame, TemplateOf!(Foo4!("123")), Foo4)); - static assert(__traits(isSame, TemplateOf!(Foo5!(int)), Foo5)); - static assert(__traits(isSame, TemplateOf!(Foo6!(int, int)), Foo6)); - static assert(__traits(isSame, TemplateOf!(Foo7!(123)), Foo7)); - static assert(__traits(isSame, TemplateOf!(Foo8!(int).Foo9!(real)), Foo8!(int).Foo9)); - static assert(__traits(isSame, TemplateOf!(Foo10!()), Foo10)); -} - -// https://issues.dlang.org/show_bug.cgi?id=18214 -@safe unittest -{ - static assert(is(TemplateOf!(int[]) == void)); - static assert(is(TemplateOf!bool == void)); -} - -/** -Returns a `AliasSeq` of the template arguments used to instantiate `T`. - */ -alias TemplateArgsOf(alias T : Base!Args, alias Base, Args...) = Args; - -/// ditto -alias TemplateArgsOf(T : Base!Args, alias Base, Args...) = Args; - -/// -@safe unittest -{ - import std.meta : AliasSeq; - - struct Foo(T, U) {} - static assert(is(TemplateArgsOf!(Foo!(int, real)) == AliasSeq!(int, real))); -} - -@safe unittest -{ - template Foo1(A) {} - template Foo2(A, B) {} - template Foo3(alias A) {} - template Foo4(string A) {} - struct Foo5(A) {} - struct Foo6(A, B) {} - struct Foo7(alias A) {} - template Foo8(A) { template Foo9(B) {} } - template Foo10() {} - - enum x = 123; - enum y = "123"; - static assert(is(TemplateArgsOf!(Foo1!(int)) == AliasSeq!(int))); - static assert(is(TemplateArgsOf!(Foo2!(int, int)) == AliasSeq!(int, int))); - static assert(__traits(isSame, TemplateArgsOf!(Foo3!(x)), AliasSeq!(x))); - static assert(TemplateArgsOf!(Foo4!(y)) == AliasSeq!(y)); - static assert(is(TemplateArgsOf!(Foo5!(int)) == AliasSeq!(int))); - static assert(is(TemplateArgsOf!(Foo6!(int, int)) == AliasSeq!(int, int))); - static assert(__traits(isSame, TemplateArgsOf!(Foo7!(x)), AliasSeq!(x))); - static assert(is(TemplateArgsOf!(Foo8!(int).Foo9!(real)) == AliasSeq!(real))); - static assert(is(TemplateArgsOf!(Foo10!()) == AliasSeq!())); -} - -// Returns the largest alignment in a type tuple. -package enum maxAlignment(U...) = -{ - size_t result = U[0].alignof; - static foreach (T; U[1 .. $]) - if (result < T.alignof) - result = T.alignof; - return result; -}(); - -/** -Returns class instance alignment. - -See also: $(DDSUBLINK spec/traits, classInstanceAlignment, `__traits(classInstanceAlignment, T)`) - */ -template classInstanceAlignment(T) -if (is(T == class)) -{ - enum classInstanceAlignment = __traits(classInstanceAlignment, T); -} - -/// -@safe unittest -{ - class A { byte b; } - class B { long l; } - - // As class instance always has a hidden pointer - static assert(classInstanceAlignment!A == (void*).alignof); - static assert(classInstanceAlignment!B == long.alignof); -} - - -//:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::// -// Type Conversion -//:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::// - -/** -Get the type that all types can be implicitly converted to. Useful -e.g. in figuring out an array type from a bunch of initializing -values. Returns $(D_PARAM void) if passed an empty list, or if the -types have no common type. - */ -template CommonType(T...) -{ - static if (T.length == 1) - alias CommonType = typeof(T[0].init); - else static if (is(typeof(true ? T[0].init : T[1].init) U)) - alias CommonType = CommonType!(U, T[2 .. $]); - else - alias CommonType = void; -} - -/// -@safe unittest -{ - alias X = CommonType!(int, long, short); - assert(is(X == long)); - alias Y = CommonType!(int, char[], short); - assert(is(Y == void)); -} - -/// -@safe unittest -{ - static assert(is(CommonType!(3) == int)); - static assert(is(CommonType!(double, 4, float) == double)); - static assert(is(CommonType!(string, char[]) == const(char)[])); - static assert(is(CommonType!(3, 3U) == uint)); - static assert(is(CommonType!(double, int) == double)); -} - - -/** -Params: - T = The type to check - -Returns: - An $(REF AliasSeq,std,meta) with all possible target types of an implicit - conversion `T`. - - If `T` is a class derived from `Object`, the result of - $(LREF TransitiveBaseTypeTuple) is returned. - - If the type is not a built-in value type or a class derived from - `Object`, an empty $(REF AliasSeq,std,meta) is returned. - -See_Also: - $(LREF isImplicitlyConvertible) - */ -template AllImplicitConversionTargets(T) -{ - static if (is(T == bool)) - alias AllImplicitConversionTargets = - AliasSeq!(byte, AllImplicitConversionTargets!byte); - else static if (is(T == byte)) - alias AllImplicitConversionTargets = - AliasSeq!(char, ubyte, short, AllImplicitConversionTargets!short); - else static if (is(T == ubyte)) - alias AllImplicitConversionTargets = - AliasSeq!(byte, char, short, AllImplicitConversionTargets!short); - else static if (is(T == short)) - alias AllImplicitConversionTargets = - AliasSeq!(ushort, wchar, int, AllImplicitConversionTargets!int); - else static if (is(T == ushort)) - alias AllImplicitConversionTargets = - AliasSeq!(short, wchar, dchar, AllImplicitConversionTargets!dchar); - else static if (is(T == int)) - alias AllImplicitConversionTargets = - AliasSeq!(dchar, uint, long, AllImplicitConversionTargets!long); - else static if (is(T == uint)) - alias AllImplicitConversionTargets = - AliasSeq!(dchar, int, long, AllImplicitConversionTargets!long); - else static if (is(T == long)) - alias AllImplicitConversionTargets = AliasSeq!(ulong, CentTypeList, float, double, real); - else static if (is(T == ulong)) - alias AllImplicitConversionTargets = AliasSeq!(long, CentTypeList, float, double, real); - else static if (is(T == float)) - alias AllImplicitConversionTargets = AliasSeq!(double, real); - else static if (is(T == double)) - alias AllImplicitConversionTargets = AliasSeq!(float, real); - else static if (is(T == real)) - alias AllImplicitConversionTargets = AliasSeq!(float, double); - else static if (is(T == char)) - alias AllImplicitConversionTargets = - AliasSeq!(byte, ubyte, short, AllImplicitConversionTargets!short); - else static if (is(T == wchar)) - alias AllImplicitConversionTargets = - AliasSeq!(short, ushort, dchar, AllImplicitConversionTargets!dchar); - else static if (is(T == dchar)) - alias AllImplicitConversionTargets = - AliasSeq!(int, uint, long, AllImplicitConversionTargets!long); - else static if (is(T == class)) - alias AllImplicitConversionTargets = staticMap!(ApplyLeft!(CopyConstness, T), TransitiveBaseTypeTuple!T); - else static if (is(T == interface)) - alias AllImplicitConversionTargets = staticMap!(ApplyLeft!(CopyConstness, T), InterfacesTuple!T); - else static if (isDynamicArray!T && !is(typeof(T.init[0]) == const)) - { - static if (is(typeof(T.init[0]) == shared)) - alias AllImplicitConversionTargets = - AliasSeq!(const(shared(Unqual!(typeof(T.init[0]))))[]); - else - alias AllImplicitConversionTargets = - AliasSeq!(const(Unqual!(typeof(T.init[0])))[]); - } - else static if (is(T : void*) && !is(T == void*)) - alias AllImplicitConversionTargets = AliasSeq!(void*); - else static if (is(cent) && is(T == cent)) - alias AllImplicitConversionTargets = AliasSeq!(UnsignedCentTypeList, float, double, real); - else static if (is(ucent) && is(T == ucent)) - alias AllImplicitConversionTargets = AliasSeq!(SignedCentTypeList, float, double, real); - else - alias AllImplicitConversionTargets = AliasSeq!(); -} - -/// -@safe unittest -{ - import std.meta : AliasSeq; - - static assert(is(AllImplicitConversionTargets!(ulong) == AliasSeq!(long, float, double, real))); - static assert(is(AllImplicitConversionTargets!(int) == AliasSeq!(dchar, uint, long, ulong, float, double, real))); - static assert(is(AllImplicitConversionTargets!(float) == AliasSeq!(double, real))); - static assert(is(AllImplicitConversionTargets!(double) == AliasSeq!(float, real))); - - static assert(is(AllImplicitConversionTargets!(char) == - AliasSeq!(byte, ubyte, short, ushort, wchar, int, dchar, uint, long, - ulong, float, double, real) - )); - static assert(is(AllImplicitConversionTargets!(wchar) == AliasSeq!( - short, ushort, dchar, int, uint, long, ulong, float, double, real - ))); - static assert(is(AllImplicitConversionTargets!(dchar) == AliasSeq!( - int, uint, long, ulong, float, double, real - ))); - - static assert(is(AllImplicitConversionTargets!(string) == AliasSeq!(const(char)[]))); - static assert(is(AllImplicitConversionTargets!(int*) == AliasSeq!(void*))); - - interface A {} - interface B {} - class C : A, B {} - - static assert(is(AllImplicitConversionTargets!(C) == AliasSeq!(Object, A, B))); - static assert(is(AllImplicitConversionTargets!(const C) == AliasSeq!(const Object, const A, const B))); - static assert(is(AllImplicitConversionTargets!(immutable C) == AliasSeq!( - immutable Object, immutable A, immutable B - ))); - - interface I : A, B {} - - static assert(is(AllImplicitConversionTargets!(I) == AliasSeq!(A, B))); - static assert(is(AllImplicitConversionTargets!(const I) == AliasSeq!(const A, const B))); - static assert(is(AllImplicitConversionTargets!(immutable I) == AliasSeq!( - immutable A, immutable B - ))); -} - -@safe unittest -{ - static assert(is(AllImplicitConversionTargets!(double)[0] == float)); - static assert(is(AllImplicitConversionTargets!(double)[1] == real)); - static assert(is(AllImplicitConversionTargets!(string)[0] == const(char)[])); -} - - -/** -Params: - T = The type to check - -Warning: - This template is considered out-dated. It will be removed from - Phobos in 2.107.0. Please use $(LREF AllImplicitConversionTargets) instead. - -Returns: - An $(REF AliasSeq,std,meta) with all possible target types of an implicit - conversion `T`. - - If `T` is a class derived from `Object`, the result of - $(LREF TransitiveBaseTypeTuple) is returned. - - If the type is not a built-in value type or a class derived from - `Object`, an empty $(REF AliasSeq,std,meta) is returned. - -Note: - The possible targets are computed more conservatively than the - language allows, eliminating all dangerous conversions. For example, - `ImplicitConversionTargets!double` does not include `float`. - -See_Also: - $(LREF isImplicitlyConvertible) - */ -// @@@DEPRECATED_[2.107.0]@@@ -deprecated("ImplicitConversionTargets has been deprecated in favour of AllImplicitConversionTargets " - ~ "and will be removed in 2.107.0") -template ImplicitConversionTargets(T) -{ - static if (is(T == bool)) - alias ImplicitConversionTargets = - AliasSeq!(byte, ubyte, short, ushort, int, uint, long, ulong, CentTypeList, - float, double, real, char, wchar, dchar); - else static if (is(T == byte)) - alias ImplicitConversionTargets = - AliasSeq!(short, ushort, int, uint, long, ulong, CentTypeList, - float, double, real, char, wchar, dchar); - else static if (is(T == ubyte)) - alias ImplicitConversionTargets = - AliasSeq!(short, ushort, int, uint, long, ulong, CentTypeList, - float, double, real, char, wchar, dchar); - else static if (is(T == short)) - alias ImplicitConversionTargets = - AliasSeq!(int, uint, long, ulong, CentTypeList, float, double, real); - else static if (is(T == ushort)) - alias ImplicitConversionTargets = - AliasSeq!(int, uint, long, ulong, CentTypeList, float, double, real); - else static if (is(T == int)) - alias ImplicitConversionTargets = - AliasSeq!(long, ulong, CentTypeList, float, double, real); - else static if (is(T == uint)) - alias ImplicitConversionTargets = - AliasSeq!(long, ulong, CentTypeList, float, double, real); - else static if (is(T == long)) - alias ImplicitConversionTargets = AliasSeq!(float, double, real); - else static if (is(T == ulong)) - alias ImplicitConversionTargets = AliasSeq!(float, double, real); - else static if (is(cent) && is(T == cent)) - alias ImplicitConversionTargets = AliasSeq!(float, double, real); - else static if (is(ucent) && is(T == ucent)) - alias ImplicitConversionTargets = AliasSeq!(float, double, real); - else static if (is(T == float)) - alias ImplicitConversionTargets = AliasSeq!(double, real); - else static if (is(T == double)) - alias ImplicitConversionTargets = AliasSeq!real; - else static if (is(T == char)) - alias ImplicitConversionTargets = - AliasSeq!(wchar, dchar, byte, ubyte, short, ushort, - int, uint, long, ulong, CentTypeList, float, double, real); - else static if (is(T == wchar)) - alias ImplicitConversionTargets = - AliasSeq!(dchar, short, ushort, int, uint, long, ulong, CentTypeList, - float, double, real); - else static if (is(T == dchar)) - alias ImplicitConversionTargets = - AliasSeq!(int, uint, long, ulong, CentTypeList, float, double, real); - else static if (is(T : typeof(null))) - alias ImplicitConversionTargets = AliasSeq!(typeof(null)); - else static if (is(T == class)) - alias ImplicitConversionTargets = staticMap!(ApplyLeft!(CopyConstness, T), TransitiveBaseTypeTuple!(T)); - else static if (isDynamicArray!T && !is(typeof(T.init[0]) == const)) - { - static if (is(typeof(T.init[0]) == shared)) - alias ImplicitConversionTargets = - AliasSeq!(const(shared(Unqual!(typeof(T.init[0]))))[]); - else - alias ImplicitConversionTargets = - AliasSeq!(const(Unqual!(typeof(T.init[0])))[]); - } - else static if (is(T : void*)) - alias ImplicitConversionTargets = AliasSeq!(void*); - else - alias ImplicitConversionTargets = AliasSeq!(); -} - -deprecated @safe unittest -{ - import std.meta : AliasSeq; - - static assert(is(ImplicitConversionTargets!(ulong) == AliasSeq!(float, double, real))); - static assert(is(ImplicitConversionTargets!(int) == AliasSeq!(long, ulong, float, double, real))); - static assert(is(ImplicitConversionTargets!(float) == AliasSeq!(double, real))); - static assert(is(ImplicitConversionTargets!(double) == AliasSeq!(real))); - - static assert(is(ImplicitConversionTargets!(char) == AliasSeq!( - wchar, dchar, byte, ubyte, short, ushort, int, uint, long, ulong, float, double, real - ))); - static assert(is(ImplicitConversionTargets!(wchar) == AliasSeq!( - dchar, short, ushort, int, uint, long, ulong, float, double, real - ))); - static assert(is(ImplicitConversionTargets!(dchar) == AliasSeq!( - int, uint, long, ulong, float, double, real - ))); - - static assert(is(ImplicitConversionTargets!(string) == AliasSeq!(const(char)[]))); - static assert(is(ImplicitConversionTargets!(void*) == AliasSeq!(void*))); - - interface A {} - interface B {} - class C : A, B {} - - static assert(is(ImplicitConversionTargets!(C) == AliasSeq!(Object, A, B))); - static assert(is(ImplicitConversionTargets!(const C) == AliasSeq!(const Object, const A, const B))); - static assert(is(ImplicitConversionTargets!(immutable C) == AliasSeq!( - immutable Object, immutable A, immutable B - ))); -} - -deprecated @safe unittest -{ - static assert(is(ImplicitConversionTargets!(double)[0] == real)); - static assert(is(ImplicitConversionTargets!(string)[0] == const(char)[])); -} - -/** -Is `From` implicitly convertible to `To`? - */ -enum bool isImplicitlyConvertible(From, To) = is(From : To); - -/// -@safe unittest -{ - static assert( isImplicitlyConvertible!(immutable(char), char)); - static assert( isImplicitlyConvertible!(const(char), char)); - static assert( isImplicitlyConvertible!(char, wchar)); - static assert(!isImplicitlyConvertible!(wchar, char)); - - static assert(!isImplicitlyConvertible!(const(ushort), ubyte)); - static assert(!isImplicitlyConvertible!(const(uint), ubyte)); - static assert(!isImplicitlyConvertible!(const(ulong), ubyte)); - - static assert(!isImplicitlyConvertible!(const(char)[], string)); - static assert( isImplicitlyConvertible!(string, const(char)[])); -} - -/** -Is `From` $(DDSUBLINK spec/const3, implicit_qualifier_conversions, qualifier-convertible) to `To`? -*/ -enum bool isQualifierConvertible(From, To) = - is(immutable From == immutable To) && is(From* : To*); - -/// -@safe unittest -{ - // Mutable and immmutable both convert to const... - static assert( isQualifierConvertible!(char, const(char))); - static assert( isQualifierConvertible!(immutable(char), const(char))); - // ...but const does not convert back to mutable or immutable - static assert(!isQualifierConvertible!(const(char), char)); - static assert(!isQualifierConvertible!(const(char), immutable(char))); -} - -@safe unittest -{ - import std.meta : AliasSeq; - - alias Ts = AliasSeq!(int, const int, shared int, inout int, const shared int, - const inout int, inout shared int, const inout shared int, immutable int); - - // https://dlang.org/spec/const3.html#implicit_qualifier_conversions - enum _ = 0; - static immutable bool[Ts.length][Ts.length] conversions = [ - // m c s i cs ci is cis im - [1, 1, _, _, _, _, _, _, _], // mutable - [_, 1, _, _, _, _, _, _, _], // const - [_, _, 1, _, 1, _, _, _, _], // shared - [_, 1, _, 1, _, 1, _, _, _], // inout - [_, _, _, _, 1, _, _, _, _], // const shared - [_, 1, _, _, _, 1, _, _, _], // const inout - [_, _, _, _, 1, _, 1, 1, _], // inout shared - [_, _, _, _, 1, _, _, 1, _], // const inout shared - [_, 1, _, _, 1, 1, _, 1, 1], // immutable - ]; - - static foreach (i, From; Ts) - { - static foreach (j, To; Ts) - { - static assert(isQualifierConvertible!(From, To) == conversions[i][j], - "`isQualifierConvertible!(" ~ From.stringof ~ ", " ~ To.stringof ~ ")`" - ~ " should be `" ~ (conversions[i][j] ? "true" : "false") ~ "`"); - } - } -} - -@safe unittest -{ - // int* -> void* is not a qualifier conversion - static assert(!isQualifierConvertible!(int, void)); -} - -/** -Returns `true` iff a value of type `Rhs` can be assigned to a variable of -type `Lhs`. - -`isAssignable` returns whether both an lvalue and rvalue can be assigned. - -If you omit `Rhs`, `isAssignable` will check identity assignable of `Lhs`. -*/ -enum isAssignable(Lhs, Rhs = Lhs) = isRvalueAssignable!(Lhs, Rhs) && isLvalueAssignable!(Lhs, Rhs); - -/// -@safe unittest -{ - static assert( isAssignable!(long, int)); - static assert(!isAssignable!(int, long)); - static assert( isAssignable!(const(char)[], string)); - static assert(!isAssignable!(string, char[])); - - // int is assignable to int - static assert( isAssignable!int); - - // immutable int is not assignable to immutable int - static assert(!isAssignable!(immutable int)); -} - -/** -Returns `true` iff an rvalue of type `Rhs` can be assigned to a variable of -type `Lhs`. -*/ -enum isRvalueAssignable(Lhs, Rhs = Lhs) = __traits(compiles, { lvalueOf!Lhs = rvalueOf!Rhs; }); - -/// -@safe unittest -{ - struct S1 - { - void opAssign(S1); - } - - struct S2 - { - void opAssign(ref S2); - } - - static assert( isRvalueAssignable!(long, int)); - static assert(!isRvalueAssignable!(int, long)); - static assert( isRvalueAssignable!S1); - static assert(!isRvalueAssignable!S2); -} - -/** -Returns `true` iff an lvalue of type `Rhs` can be assigned to a variable of -type `Lhs`. -*/ -enum isLvalueAssignable(Lhs, Rhs = Lhs) = __traits(compiles, { lvalueOf!Lhs = lvalueOf!Rhs; }); - -/// -@safe unittest -{ - struct S1 - { - void opAssign(S1); - } - - struct S2 - { - void opAssign(ref S2); - } - - static assert( isLvalueAssignable!(long, int)); - static assert(!isLvalueAssignable!(int, long)); - static assert( isLvalueAssignable!S1); - static assert( isLvalueAssignable!S2); -} - -@safe unittest -{ - static assert(!isAssignable!(immutable int, int)); - static assert( isAssignable!(int, immutable int)); - - static assert(!isAssignable!(inout int, int)); - static assert( isAssignable!(int, inout int)); - static assert(!isAssignable!(inout int)); - - static assert( isAssignable!(shared int, int)); - static assert( isAssignable!(int, shared int)); - static assert( isAssignable!(shared int)); - - static assert( isAssignable!(void[1], void[1])); - - struct S { @disable this(); this(int n){} } - static assert( isAssignable!(S, S)); - - struct S2 { this(int n){} } - static assert( isAssignable!(S2, S2)); - static assert(!isAssignable!(S2, int)); - - struct S3 { @disable void opAssign(); } - static assert( isAssignable!(S3, S3)); - - struct S3X { @disable void opAssign(S3X); } - static assert(!isAssignable!(S3X, S3X)); - - struct S4 { void opAssign(int); } - static assert( isAssignable!(S4, S4)); - static assert( isAssignable!(S4, int)); - static assert( isAssignable!(S4, immutable int)); - - struct S5 { @disable this(); @disable this(this); } - // https://issues.dlang.org/show_bug.cgi?id=21210 - static assert(!isAssignable!S5); - - // `-preview=in` is enabled - alias DScannerBug895 = int[256]; - static if (((in DScannerBug895 a) { return __traits(isRef, a); })(DScannerBug895.init)) - { - struct S6 { void opAssign(in S5); } - - static assert(isRvalueAssignable!(S6, S5)); - static assert(isLvalueAssignable!(S6, S5)); - static assert(isAssignable!(S6, S5)); - static assert(isAssignable!(S6, immutable S5)); - } - else - { - mixin(q{ struct S6 { void opAssign(scope const ref S5); } }); - - static assert(!isRvalueAssignable!(S6, S5)); - static assert( isLvalueAssignable!(S6, S5)); - static assert(!isAssignable!(S6, S5)); - static assert( isLvalueAssignable!(S6, immutable S5)); - } -} - - -// Equivalent with TypeStruct::isAssignable in compiler code. -package template isBlitAssignable(T) -{ - static if (is(T == enum)) - { - enum isBlitAssignable = isBlitAssignable!(OriginalType!T); - } - else static if (isStaticArray!T && is(T == E[n], E, size_t n)) - // Workaround for https://issues.dlang.org/show_bug.cgi?id=11499 : isStaticArray!T should not be necessary. - { - enum isBlitAssignable = isBlitAssignable!E; - } - else static if (is(T == struct) || is(T == union)) - { - enum isBlitAssignable = isMutable!T && - { - size_t offset = 0; - bool assignable = true; - foreach (i, F; FieldTypeTuple!T) - { - static if (i == 0) - { - } - else - { - if (T.tupleof[i].offsetof == offset) - { - if (assignable) - continue; - } - else - { - if (!assignable) - return false; - } - } - assignable = isBlitAssignable!(typeof(T.tupleof[i])); - offset = T.tupleof[i].offsetof; - } - return assignable; - }(); - } - else - enum isBlitAssignable = isMutable!T; -} - -@safe unittest -{ - static assert( isBlitAssignable!int); - static assert(!isBlitAssignable!(const int)); - - class C{ const int i; } - static assert( isBlitAssignable!C); - - struct S1{ int i; } - struct S2{ const int i; } - static assert( isBlitAssignable!S1); - static assert(!isBlitAssignable!S2); - - struct S3X { union { int x; int y; } } - struct S3Y { union { int x; const int y; } } - struct S3Z { union { const int x; const int y; } } - static assert( isBlitAssignable!(S3X)); - static assert( isBlitAssignable!(S3Y)); - static assert(!isBlitAssignable!(S3Z)); - static assert(!isBlitAssignable!(const S3X)); - static assert(!isBlitAssignable!(inout S3Y)); - static assert(!isBlitAssignable!(immutable S3Z)); - static assert( isBlitAssignable!(S3X[3])); - static assert( isBlitAssignable!(S3Y[3])); - static assert(!isBlitAssignable!(S3Z[3])); - enum ES3X : S3X { a = S3X() } - enum ES3Y : S3Y { a = S3Y() } - enum ES3Z : S3Z { a = S3Z() } - static assert( isBlitAssignable!(ES3X)); - static assert( isBlitAssignable!(ES3Y)); - static assert(!isBlitAssignable!(ES3Z)); - static assert(!isBlitAssignable!(const ES3X)); - static assert(!isBlitAssignable!(inout ES3Y)); - static assert(!isBlitAssignable!(immutable ES3Z)); - static assert( isBlitAssignable!(ES3X[3])); - static assert( isBlitAssignable!(ES3Y[3])); - static assert(!isBlitAssignable!(ES3Z[3])); - - union U1X { int x; int y; } - union U1Y { int x; const int y; } - union U1Z { const int x; const int y; } - static assert( isBlitAssignable!(U1X)); - static assert( isBlitAssignable!(U1Y)); - static assert(!isBlitAssignable!(U1Z)); - static assert(!isBlitAssignable!(const U1X)); - static assert(!isBlitAssignable!(inout U1Y)); - static assert(!isBlitAssignable!(immutable U1Z)); - static assert( isBlitAssignable!(U1X[3])); - static assert( isBlitAssignable!(U1Y[3])); - static assert(!isBlitAssignable!(U1Z[3])); - enum EU1X : U1X { a = U1X() } - enum EU1Y : U1Y { a = U1Y() } - enum EU1Z : U1Z { a = U1Z() } - static assert( isBlitAssignable!(EU1X)); - static assert( isBlitAssignable!(EU1Y)); - static assert(!isBlitAssignable!(EU1Z)); - static assert(!isBlitAssignable!(const EU1X)); - static assert(!isBlitAssignable!(inout EU1Y)); - static assert(!isBlitAssignable!(immutable EU1Z)); - static assert( isBlitAssignable!(EU1X[3])); - static assert( isBlitAssignable!(EU1Y[3])); - static assert(!isBlitAssignable!(EU1Z[3])); - - struct SA - { - @property int[3] foo() { return [1,2,3]; } - alias foo this; - const int x; // SA is not blit assignable - } - static assert(!isStaticArray!SA); - static assert(!isBlitAssignable!(SA[3])); -} - - -/* -Works like `isImplicitlyConvertible`, except this cares only about storage -classes of the arguments. - */ -private template isStorageClassImplicitlyConvertible(From, To) -{ - alias Pointify(T) = void*; - - enum isStorageClassImplicitlyConvertible = is( - ModifyTypePreservingTQ!(Pointify, From) : - ModifyTypePreservingTQ!(Pointify, To) ); -} - -@safe unittest -{ - static assert( isStorageClassImplicitlyConvertible!( int, const int)); - static assert( isStorageClassImplicitlyConvertible!(immutable int, const int)); - - static assert(!isStorageClassImplicitlyConvertible!(const int, int)); - static assert(!isStorageClassImplicitlyConvertible!(const int, immutable int)); - static assert(!isStorageClassImplicitlyConvertible!(int, shared int)); - static assert(!isStorageClassImplicitlyConvertible!(shared int, int)); -} - - -/** -Determines whether the function type `F` is covariant with `G`, i.e., -functions of the type `F` can override ones of the type `G`. - */ -template isCovariantWith(F, G) -if (is(F == function) && is(G == function) || - is(F == delegate) && is(G == delegate) || - isFunctionPointer!F && isFunctionPointer!G) -{ - static if (is(F : G)) - enum isCovariantWith = true; - else - { - alias Upr = F; - alias Lwr = G; - - /* - * Check for calling convention: require exact match. - */ - template checkLinkage() - { - enum ok = functionLinkage!Upr == functionLinkage!Lwr; - } - /* - * Check for variadic parameter: require exact match. - */ - template checkVariadicity() - { - enum ok = variadicFunctionStyle!Upr == variadicFunctionStyle!Lwr; - } - /* - * Check for function storage class: - * - overrider can have narrower storage class than base - */ - template checkSTC() - { - // Note the order of arguments. The convertion order Lwr -> Upr is - // correct since Upr should be semantically 'narrower' than Lwr. - enum ok = isStorageClassImplicitlyConvertible!(Lwr, Upr); - } - /* - * Check for function attributes: - * - require exact match for ref and @property - * - overrider can add pure and nothrow, but can't remove them - * - @safe and @trusted are covariant with each other, unremovable - */ - template checkAttributes() - { - alias FA = FunctionAttribute; - enum uprAtts = functionAttributes!Upr; - enum lwrAtts = functionAttributes!Lwr; - // - enum wantExact = FA.ref_ | FA.property; - enum safety = FA.safe | FA.trusted; - enum ok = - ( (uprAtts & wantExact) == (lwrAtts & wantExact)) && - ( (uprAtts & FA.pure_ ) >= (lwrAtts & FA.pure_ )) && - ( (uprAtts & FA.nothrow_) >= (lwrAtts & FA.nothrow_)) && - (!!(uprAtts & safety ) >= !!(lwrAtts & safety )) ; - } - /* - * Check for return type: usual implicit convertion. - */ - template checkReturnType() - { - enum ok = is(ReturnType!Upr : ReturnType!Lwr); - } - /* - * Check for parameters: - * - require exact match for types - * (cf. https://issues.dlang.org/show_bug.cgi?id=3075) - * - require exact match for in, out, ref and lazy - * - overrider can add scope, but can't remove - */ - template checkParameters() - { - alias STC = ParameterStorageClass; - alias UprParams = Parameters!Upr; - alias LwrParams = Parameters!Lwr; - alias UprPSTCs = ParameterStorageClassTuple!Upr; - alias LwrPSTCs = ParameterStorageClassTuple!Lwr; - // - template checkNext(size_t i) - { - static if (i < UprParams.length) - { - enum uprStc = UprPSTCs[i]; - enum lwrStc = LwrPSTCs[i]; - // - enum wantExact = STC.out_ | STC.ref_ | STC.lazy_ | STC.return_; - enum ok = - ((uprStc & wantExact ) == (lwrStc & wantExact )) && - ((uprStc & STC.scope_) >= (lwrStc & STC.scope_)) && - checkNext!(i + 1).ok; - } - else - enum ok = true; // done - } - static if (UprParams.length == LwrParams.length) - enum ok = is(UprParams == LwrParams) && checkNext!(0).ok; - else - enum ok = false; - } - - /* run all the checks */ - enum isCovariantWith = - checkLinkage !().ok && - checkVariadicity!().ok && - checkSTC !().ok && - checkAttributes !().ok && - checkReturnType !().ok && - checkParameters !().ok ; - } -} - -/// -@safe unittest -{ - interface I { I clone(); } - interface J { J clone(); } - class C : I - { - override C clone() // covariant overriding of I.clone() - { - return new C; - } - } - - // C.clone() can override I.clone(), indeed. - static assert(isCovariantWith!(typeof(C.clone), typeof(I.clone))); - - // C.clone() can't override J.clone(); the return type C is not implicitly - // convertible to J. - static assert(!isCovariantWith!(typeof(C.clone), typeof(J.clone))); -} - -@safe unittest -{ - enum bool isCovariantWith(alias f, alias g) = .isCovariantWith!(typeof(f), typeof(g)); - - // covariant return type - interface I {} - interface J : I {} - interface BaseA { const(I) test(int); } - interface DerivA_1 : BaseA { override const(J) test(int); } - interface DerivA_2 : BaseA { override J test(int); } - static assert( isCovariantWith!(DerivA_1.test, BaseA.test)); - static assert( isCovariantWith!(DerivA_2.test, BaseA.test)); - static assert(!isCovariantWith!(BaseA.test, DerivA_1.test)); - static assert(!isCovariantWith!(BaseA.test, DerivA_2.test)); - static assert( isCovariantWith!(BaseA.test, BaseA.test)); - static assert( isCovariantWith!(DerivA_1.test, DerivA_1.test)); - static assert( isCovariantWith!(DerivA_2.test, DerivA_2.test)); - - // function, function pointer and delegate - J function() derived_function; - I function() base_function; - J delegate() derived_delegate; - I delegate() base_delegate; - static assert(.isCovariantWith!(typeof(derived_function), typeof(base_function))); - static assert(.isCovariantWith!(typeof(*derived_function), typeof(*base_function))); - static assert(.isCovariantWith!(typeof(derived_delegate), typeof(base_delegate))); - - // scope parameter - interface BaseB { void test( int*, int*); } - interface DerivB_1 : BaseB { override void test(scope int*, int*); } - interface DerivB_2 : BaseB { override void test( int*, scope int*); } - interface DerivB_3 : BaseB { override void test(scope int*, scope int*); } - static assert( isCovariantWith!(DerivB_1.test, BaseB.test)); - static assert( isCovariantWith!(DerivB_2.test, BaseB.test)); - static assert( isCovariantWith!(DerivB_3.test, BaseB.test)); - static assert(!isCovariantWith!(BaseB.test, DerivB_1.test)); - static assert(!isCovariantWith!(BaseB.test, DerivB_2.test)); - static assert(!isCovariantWith!(BaseB.test, DerivB_3.test)); - - // function storage class - interface BaseC { void test() ; } - interface DerivC_1 : BaseC { override void test() const; } - static assert( isCovariantWith!(DerivC_1.test, BaseC.test)); - static assert(!isCovariantWith!(BaseC.test, DerivC_1.test)); - - // increasing safety - interface BaseE { void test() ; } - interface DerivE_1 : BaseE { override void test() @safe ; } - interface DerivE_2 : BaseE { override void test() @trusted; } - static assert( isCovariantWith!(DerivE_1.test, BaseE.test)); - static assert( isCovariantWith!(DerivE_2.test, BaseE.test)); - static assert(!isCovariantWith!(BaseE.test, DerivE_1.test)); - static assert(!isCovariantWith!(BaseE.test, DerivE_2.test)); - - // @safe and @trusted - interface BaseF - { - void test1() @safe; - void test2() @trusted; - } - interface DerivF : BaseF - { - override void test1() @trusted; - override void test2() @safe; - } - static assert( isCovariantWith!(DerivF.test1, BaseF.test1)); - static assert( isCovariantWith!(DerivF.test2, BaseF.test2)); -} - - -// Needed for rvalueOf/lvalueOf because "inout on return means -// inout must be on a parameter as well" -private struct __InoutWorkaroundStruct{} - -/** -Creates an lvalue or rvalue of type `T` for `typeof(...)` and -$(DDSUBLINK spec/traits, compiles, `__traits(compiles, ...)`) purposes. No actual value is returned. - -Params: - T = The type to transform - -Note: Trying to use returned value will result in a -"Symbol Undefined" error at link time. -*/ -@property T rvalueOf(T)(inout __InoutWorkaroundStruct = __InoutWorkaroundStruct.init); - -/// ditto -@property ref T lvalueOf(T)(inout __InoutWorkaroundStruct = __InoutWorkaroundStruct.init); - -// Note: can't put these unittests together as function overloads -// aren't allowed inside functions. -/// -@system unittest -{ - static int f(int); - static assert(is(typeof(f(rvalueOf!int)) == int)); -} - -/// -@system unittest -{ - static bool f(ref int); - static assert(is(typeof(f(lvalueOf!int)) == bool)); -} - -@system unittest -{ - void needLvalue(T)(ref T); - static struct S { } - int i; - struct Nested { void f() { ++i; } } - static foreach (T; AliasSeq!(int, immutable int, inout int, string, S, Nested, Object)) - { - static assert(!__traits(compiles, needLvalue(rvalueOf!T))); - static assert( __traits(compiles, needLvalue(lvalueOf!T))); - static assert(is(typeof(rvalueOf!T) == T)); - static assert(is(typeof(lvalueOf!T) == T)); - } - - static assert(!__traits(compiles, rvalueOf!int = 1)); - static assert( __traits(compiles, lvalueOf!byte = 127)); - static assert(!__traits(compiles, lvalueOf!byte = 128)); -} - - -//:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::// -// SomethingTypeOf -//:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::// - -/* - */ -template BooleanTypeOf(T) -{ - static if (is(typeof(__traits(getMember, T.init, __traits(getAliasThis, T)[0])) AT) && !is(AT[] == AT)) - alias X = BooleanTypeOf!AT; - else - alias X = OriginalType!T; - - static if (is(immutable X == immutable bool)) - { - alias BooleanTypeOf = X; - } - else - static assert(0, T.stringof~" is not boolean type"); -} - -@safe unittest -{ - // unexpected failure, maybe dmd type-merging bug - static foreach (T; AliasSeq!bool) - static foreach (Q; TypeQualifierList) - { - static assert( is(Q!T == BooleanTypeOf!( Q!T ))); - static assert( is(Q!T == BooleanTypeOf!( SubTypeOf!(Q!T) ))); - } - - static foreach (T; AliasSeq!(void, NumericTypeList, /*ImaginaryTypeList, ComplexTypeList,*/ CharTypeList)) - static foreach (Q; TypeQualifierList) - { - static assert(!is(BooleanTypeOf!( Q!T )), Q!T.stringof); - static assert(!is(BooleanTypeOf!( SubTypeOf!(Q!T) ))); - } -} - -@safe unittest -{ - struct B - { - bool val; - alias val this; - } - struct S - { - B b; - alias b this; - } - static assert(is(BooleanTypeOf!B == bool)); - static assert(is(BooleanTypeOf!S == bool)); -} - -/* - */ -template IntegralTypeOf(T) -{ - static if (is(typeof(__traits(getMember, T.init, __traits(getAliasThis, T)[0])) AT) && !is(AT[] == AT)) - alias X = IntegralTypeOf!AT; - else - alias X = OriginalType!T; - - static if (__traits(isIntegral, X) && __traits(isZeroInit, X) // Not char, wchar, or dchar. - && !is(immutable X == immutable bool) && !is(X == __vector)) - { - alias IntegralTypeOf = X; - } - else - static assert(0, T.stringof~" is not an integral type"); -} - -@safe unittest -{ - static foreach (T; IntegralTypeList) - static foreach (Q; TypeQualifierList) - { - static assert( is(Q!T == IntegralTypeOf!( Q!T ))); - static assert( is(Q!T == IntegralTypeOf!( SubTypeOf!(Q!T) ))); - } - - static foreach (T; AliasSeq!(void, bool, FloatingPointTypeList, - /*ImaginaryTypeList, ComplexTypeList,*/ CharTypeList)) - static foreach (Q; TypeQualifierList) - { - static assert(!is(IntegralTypeOf!( Q!T ))); - static assert(!is(IntegralTypeOf!( SubTypeOf!(Q!T) ))); - } -} - -/* - */ -template FloatingPointTypeOf(T) -{ - static if (is(typeof(__traits(getMember, T.init, __traits(getAliasThis, T)[0])) AT) && !is(AT[] == AT)) - alias X = FloatingPointTypeOf!AT; - else - alias X = OriginalType!T; - - static if (is(immutable X == immutable U, U) && is(U == float) || is(U == double) || is(U == real)) - { - alias FloatingPointTypeOf = X; - } - else - static assert(0, T.stringof~" is not a floating point type"); -} - -@safe unittest -{ - static foreach (T; FloatingPointTypeList) - static foreach (Q; TypeQualifierList) - { - static assert( is(Q!T == FloatingPointTypeOf!( Q!T ))); - static assert( is(Q!T == FloatingPointTypeOf!( SubTypeOf!(Q!T) ))); - } - - static foreach (T; AliasSeq!(void, bool, IntegralTypeList, /*ImaginaryTypeList, ComplexTypeList,*/ CharTypeList)) - static foreach (Q; TypeQualifierList) - { - static assert(!is(FloatingPointTypeOf!( Q!T ))); - static assert(!is(FloatingPointTypeOf!( SubTypeOf!(Q!T) ))); - } -} - -/* - */ -template NumericTypeOf(T) -{ - static if (is(IntegralTypeOf!T X) || is(FloatingPointTypeOf!T X)) - { - alias NumericTypeOf = X; - } - else - static assert(0, T.stringof~" is not a numeric type"); -} - -@safe unittest -{ - static foreach (T; NumericTypeList) - static foreach (Q; TypeQualifierList) - { - static assert( is(Q!T == NumericTypeOf!( Q!T ))); - static assert( is(Q!T == NumericTypeOf!( SubTypeOf!(Q!T) ))); - } - - static foreach (T; AliasSeq!(void, bool, CharTypeList, /*ImaginaryTypeList, ComplexTypeList*/)) - static foreach (Q; TypeQualifierList) - { - static assert(!is(NumericTypeOf!( Q!T ))); - static assert(!is(NumericTypeOf!( SubTypeOf!(Q!T) ))); - } -} - -/* - */ -template UnsignedTypeOf(T) -{ - static if (is(IntegralTypeOf!T X) && __traits(isUnsigned, X)) - alias UnsignedTypeOf = X; - else - static assert(0, T.stringof~" is not an unsigned type."); -} - -/* - */ -template SignedTypeOf(T) -{ - static if (is(IntegralTypeOf!T X) && !__traits(isUnsigned, X)) - alias SignedTypeOf = X; - else static if (is(FloatingPointTypeOf!T X)) - alias SignedTypeOf = X; - else - static assert(0, T.stringof~" is not an signed type."); -} - -/* - */ -template CharTypeOf(T) -{ - static if (is(typeof(__traits(getMember, T.init, __traits(getAliasThis, T)[0])) AT) && !is(AT[] == AT)) - alias X = CharTypeOf!AT; - else - alias X = OriginalType!T; - - static if (is(immutable X == immutable U, U) && is(U == char) || is(U == wchar) || is(U == dchar)) - { - alias CharTypeOf = X; - } - else - static assert(0, T.stringof~" is not a character type"); -} - -@safe unittest -{ - static foreach (T; CharTypeList) - static foreach (Q; TypeQualifierList) - { - static assert( is(CharTypeOf!( Q!T ))); - static assert( is(CharTypeOf!( SubTypeOf!(Q!T) ))); - } - - static foreach (T; AliasSeq!(void, bool, NumericTypeList, /*ImaginaryTypeList, ComplexTypeList*/)) - static foreach (Q; TypeQualifierList) - { - static assert(!is(CharTypeOf!( Q!T ))); - static assert(!is(CharTypeOf!( SubTypeOf!(Q!T) ))); - } - - static foreach (T; AliasSeq!(string, wstring, dstring, char[4])) - static foreach (Q; TypeQualifierList) - { - static assert(!is(CharTypeOf!( Q!T ))); - static assert(!is(CharTypeOf!( SubTypeOf!(Q!T) ))); - } -} - -/* - */ -template StaticArrayTypeOf(T) -{ - static if (is(typeof(__traits(getMember, T.init, __traits(getAliasThis, T)[0])) AT) && !is(AT[] == AT)) - alias X = StaticArrayTypeOf!AT; - else - alias X = OriginalType!T; - - static if (__traits(isStaticArray, X)) - alias StaticArrayTypeOf = X; - else - static assert(0, T.stringof~" is not a static array type"); -} - -@safe unittest -{ - static foreach (T; AliasSeq!(bool, NumericTypeList, /*ImaginaryTypeList, ComplexTypeList*/)) - static foreach (Q; AliasSeq!(TypeQualifierList, InoutOf, SharedInoutOf)) - { - static assert(is( Q!( T[1] ) == StaticArrayTypeOf!( Q!( T[1] ) ) )); - - static foreach (P; TypeQualifierList) - { // SubTypeOf cannot have inout type - static assert(is( Q!(P!(T[1])) == StaticArrayTypeOf!( Q!(SubTypeOf!(P!(T[1]))) ) )); - } - } - - static foreach (T; AliasSeq!void) - static foreach (Q; AliasSeq!TypeQualifierList) - { - static assert(is( StaticArrayTypeOf!( Q!(void[1]) ) == Q!(void[1]) )); - } -} - -/* - */ -template DynamicArrayTypeOf(T) -{ - import core.internal.traits : _DynamicArrayTypeOf = DynamicArrayTypeOf; - alias DynamicArrayTypeOf = _DynamicArrayTypeOf!T; -} - -@safe unittest -{ - import std.meta : Alias; - static foreach (T; AliasSeq!(/*void, */bool, NumericTypeList, /*ImaginaryTypeList, ComplexTypeList*/)) - static foreach (Q; AliasSeq!(TypeQualifierList, InoutOf, SharedInoutOf)) - { - static assert(is( Q!T[] == DynamicArrayTypeOf!( Q!T[] ) )); - static assert(is( Q!(T[]) == DynamicArrayTypeOf!( Q!(T[]) ) )); - - static foreach (P; AliasSeq!(Alias, ConstOf, ImmutableOf)) - { - static assert(is( Q!(P!T[]) == DynamicArrayTypeOf!( Q!(SubTypeOf!(P!T[])) ) )); - static assert(is( Q!(P!(T[])) == DynamicArrayTypeOf!( Q!(SubTypeOf!(P!(T[]))) ) )); - } - } - - static assert(!is(DynamicArrayTypeOf!(int[3]))); - static assert(!is(DynamicArrayTypeOf!(void[3]))); - static assert(!is(DynamicArrayTypeOf!(typeof(null)))); -} - -/* - */ -template ArrayTypeOf(T) -{ - static if (is(StaticArrayTypeOf!T X) || is(DynamicArrayTypeOf!T X)) - { - alias ArrayTypeOf = X; - } - else - static assert(0, T.stringof~" is not an array type"); -} - -/* - * Converts strings and string-like types to the corresponding dynamic array of characters. - * Params: - * T = one of the following: - * 1. dynamic arrays of `char`, `wchar`, or `dchar` that are implicitly convertible to `const` - * (`shared` is rejected) - * 2. static arrays of `char`, `wchar`, or `dchar` that are implicitly convertible to `const` - * (`shared` is rejected) - * 3. aggregates that use `alias this` to refer to a field that is (1), (2), or (3) - * - * Other cases are rejected with a compile time error. - * `typeof(null)` is rejected. - * - * Returns: - * The result of `[]` applied to the qualified character type. - */ -template StringTypeOf(T) -{ - static if (is(T == typeof(null))) - { - // It is impossible to determine exact string type from typeof(null) - - // it means that StringTypeOf!(typeof(null)) is undefined. - // Then this behavior is convenient for template constraint. - static assert(0, T.stringof~" is not a string type"); - } - else static if (is(T : const char[]) || is(T : const wchar[]) || is(T : const dchar[])) - { - static if (is(T : U[], U)) - alias StringTypeOf = U[]; - else - static assert(0); - } - else - static assert(0, T.stringof~" is not a string type"); -} - -@safe unittest -{ - import std.meta : Alias; - static foreach (T; CharTypeList) - static foreach (Q; AliasSeq!(Alias, ConstOf, ImmutableOf, InoutOf)) - { - static assert(is(Q!T[] == StringTypeOf!( Q!T[] ))); - - static if (!__traits(isSame, Q, InoutOf)) - {{ - static assert(is(Q!T[] == StringTypeOf!( SubTypeOf!(Q!T[]) ))); - - alias Str = Q!T[]; - struct C(S) { S val; alias val this; } - static assert(is(StringTypeOf!(C!Str) == Str)); - }} - } - - static foreach (T; CharTypeList) - static foreach (Q; AliasSeq!(SharedOf, SharedConstOf, SharedInoutOf)) - { - static assert(!is(StringTypeOf!( Q!T[] ))); - } -} - -@safe unittest -{ - static assert(is(StringTypeOf!(char[4]) == char[])); - - struct S - { - string s; - alias s this; - } - - struct T - { - S s; - alias s this; - } - - static assert(is(StringTypeOf!S == string)); - static assert(is(StringTypeOf!T == string)); -} - -/* - */ -template AssocArrayTypeOf(T) -{ - static if (is(typeof(__traits(getMember, T.init, __traits(getAliasThis, T)[0])) AT) && !is(AT[] == AT)) - alias X = AssocArrayTypeOf!AT; - else - alias X = OriginalType!T; - - static if (__traits(isAssociativeArray, X)) - { - alias AssocArrayTypeOf = X; - } - else - static assert(0, T.stringof~" is not an associative array type"); -} - -@safe unittest -{ - static foreach (T; AliasSeq!(int/*bool, CharTypeList, NumericTypeList, ImaginaryTypeList, ComplexTypeList*/)) - static foreach (P; AliasSeq!(TypeQualifierList, InoutOf, SharedInoutOf)) - static foreach (Q; AliasSeq!(TypeQualifierList, InoutOf, SharedInoutOf)) - static foreach (R; AliasSeq!(TypeQualifierList, InoutOf, SharedInoutOf)) - { - static assert(is( P!(Q!T[R!T]) == AssocArrayTypeOf!( P!(Q!T[R!T]) ) )); - } - - static foreach (T; AliasSeq!(int/*bool, CharTypeList, NumericTypeList, ImaginaryTypeList, ComplexTypeList*/)) - static foreach (O; AliasSeq!(TypeQualifierList, InoutOf, SharedInoutOf)) - static foreach (P; AliasSeq!TypeQualifierList) - static foreach (Q; AliasSeq!TypeQualifierList) - static foreach (R; AliasSeq!TypeQualifierList) - { - static assert(is( O!(P!(Q!T[R!T])) == AssocArrayTypeOf!( O!(SubTypeOf!(P!(Q!T[R!T]))) ) )); - } -} - -/* - */ -template BuiltinTypeOf(T) -{ - static if (is(T : void)) - alias BuiltinTypeOf = void; - else - { - static if (is(typeof(__traits(getMember, T.init, __traits(getAliasThis, T)[0])) AT) && !is(AT[] == AT)) - alias X = BuiltinTypeOf!AT; - else - alias X = OriginalType!T; - static if (__traits(isArithmetic, X) && !is(X == __vector) || - __traits(isStaticArray, X) || is(X == E[], E) || - __traits(isAssociativeArray, X) || is(X == typeof(null))) - alias BuiltinTypeOf = X; - else - static assert(0); - } -} - -//:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::// -// isSomething -//:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::// - -/** - * Detect whether `T` is a built-in boolean type or enum of boolean base type. - */ -enum bool isBoolean(T) = __traits(isUnsigned, T) && is(T : bool); - -/// -@safe unittest -{ - static assert( isBoolean!bool); - enum EB : bool { a = true } - static assert( isBoolean!EB); - - struct SubTypeOfBool - { - bool val; - alias val this; - } - static assert(!isBoolean!(SubTypeOfBool)); -} - -@safe unittest -{ - static struct S(T) - { - T t; - alias t this; - } - static assert(!isIntegral!(S!bool)); -} - -/** - * Detect whether `T` is a built-in integral type. - * Integral types are `byte`, `ubyte`, `short`, `ushort`, `int`, `uint`, `long`, `ulong`, `cent`, `ucent`, - * and enums with an integral type as its base type. - * Params: - * T = type to test - * Returns: - * `true` if `T` is an integral type - * Note: - * this is not the same as $(LINK2 https://dlang.org/spec/traits.html#isIntegral, `__traits(isIntegral)`) - */ -template isIntegral(T) -{ - static if (!__traits(isIntegral, T)) - enum isIntegral = false; - else static if (is(T U == enum)) - enum isIntegral = isIntegral!U; - else - enum isIntegral = __traits(isZeroInit, T) // Not char, wchar, or dchar. - && !is(immutable T == immutable bool) && !is(T == __vector); -} - -/// -@safe unittest -{ - static assert( - isIntegral!byte && - isIntegral!short && - isIntegral!int && - isIntegral!long && - isIntegral!(const(long)) && - isIntegral!(immutable(long)) - ); - - static assert( - !isIntegral!bool && - !isIntegral!char && - !isIntegral!double - ); - - // types which act as integral values do not pass - struct S - { - int val; - alias val this; - } - - static assert(!isIntegral!S); -} - -@safe unittest -{ - static foreach (T; IntegralTypeList) - { - static foreach (Q; TypeQualifierList) - { - static assert( isIntegral!(Q!T)); - static assert(!isIntegral!(SubTypeOf!(Q!T))); - } - } - - static assert(!isIntegral!float); - - enum EU : uint { a = 0, b = 1, c = 2 } // base type is unsigned - // base type is signed (https://issues.dlang.org/show_bug.cgi?id=7909) - enum EI : int { a = -1, b = 0, c = 1 } - static assert(isIntegral!EU && isUnsigned!EU && !isSigned!EU); - static assert(isIntegral!EI && !isUnsigned!EI && isSigned!EI); -} - -/** - * Detect whether `T` is a built-in floating point type. - * - * See also: $(DDSUBLINK spec/traits, isFloating, `__traits(isFloating, T)`) - */ -// is(T : real) to discount complex types -enum bool isFloatingPoint(T) = __traits(isFloating, T) && is(T : real); - -/// -@safe unittest -{ - static assert( - isFloatingPoint!float && - isFloatingPoint!double && - isFloatingPoint!real && - isFloatingPoint!(const(real)) && - isFloatingPoint!(immutable(real)) - ); - - static assert(!isFloatingPoint!int); - - // types which act as floating point values do not pass - struct S - { - float val; - alias val this; - } - - static assert(!isFloatingPoint!S); -} - -@safe unittest -{ - enum EF : real { a = 1.414, b = 1.732, c = 2.236 } - - static foreach (T; AliasSeq!(FloatingPointTypeList, EF)) - { - static foreach (Q; TypeQualifierList) - { - static assert( isFloatingPoint!(Q!T)); - static assert(!isFloatingPoint!(SubTypeOf!(Q!T))); - } - } - static foreach (T; IntegralTypeList) - { - static foreach (Q; TypeQualifierList) - { - static assert(!isFloatingPoint!(Q!T)); - } - } - static if (is(__vector(float[4]))) - { - static assert(!isFloatingPoint!(__vector(float[4]))); - } -} - -/** - * Detect whether `T` is a built-in numeric type (integral or floating - * point). - */ -template isNumeric(T) -{ - static if (!__traits(isArithmetic, T)) - enum isNumeric = false; - else static if (__traits(isFloating, T)) - enum isNumeric = is(T : real); // Not __vector, imaginary, or complex. - else static if (is(T U == enum)) - enum isNumeric = isNumeric!U; - else - enum isNumeric = __traits(isZeroInit, T) // Not char, wchar, or dchar. - && !is(immutable T == immutable bool) && !is(T == __vector); -} - -/// -@safe unittest -{ - static assert( - isNumeric!byte && - isNumeric!short && - isNumeric!int && - isNumeric!long && - isNumeric!float && - isNumeric!double && - isNumeric!real && - isNumeric!(const(real)) && - isNumeric!(immutable(real)) - ); - - static assert( - !isNumeric!void && - !isNumeric!bool && - !isNumeric!char && - !isNumeric!wchar && - !isNumeric!dchar - ); - - // types which act as numeric values do not pass - struct S - { - int val; - alias val this; - } - - static assert(!isNumeric!S); -} - -@safe unittest -{ - static foreach (T; AliasSeq!(NumericTypeList)) - { - static foreach (Q; TypeQualifierList) - { - static assert( isNumeric!(Q!T)); - static assert(!isNumeric!(SubTypeOf!(Q!T))); - } - } - - static struct S(T) - { - T t; - alias t this; - } - static assert(!isNumeric!(S!int)); - - enum EChar : char { a = 0, } - static assert(!isNumeric!EChar); - - static if (is(__vector(float[4]))) - { - static assert(!isNumeric!(__vector(float[4]))); - } - static if (is(__vector(int[4]))) - { - static assert(!isNumeric!(__vector(int[4]))); - } - - static assert(!isNumeric!ifloat); - static assert(!isNumeric!cfloat); -} - -/** - * Detect whether `T` is a scalar type (a built-in numeric, character or - * boolean type). - * - * See also: $(DDSUBLINK spec/traits, isScalar, `__traits(isScalar, T)`) - */ -// is(T : real) to discount complex types -enum bool isScalarType(T) = __traits(isScalar, T) && is(T : real); - -/// -@safe unittest -{ - static assert(!isScalarType!void); - static assert( isScalarType!(immutable(byte))); - static assert( isScalarType!(immutable(ushort))); - static assert( isScalarType!(immutable(int))); - static assert( isScalarType!(ulong)); - static assert( isScalarType!(shared(float))); - static assert( isScalarType!(shared(const bool))); - static assert( isScalarType!(const(char))); - static assert( isScalarType!(wchar)); - static assert( isScalarType!(const(dchar))); - static assert( isScalarType!(const(double))); - static assert( isScalarType!(const(real))); -} - -@safe unittest -{ - static struct S(T) - { - T t; - alias t this; - } - static assert(!isScalarType!(S!int)); -} - -/** - * Detect whether `T` is a basic type (scalar type or void). - */ -enum bool isBasicType(T) = isScalarType!T || is(immutable T == immutable void); - -/// -@safe unittest -{ - static assert(isBasicType!void); - static assert(isBasicType!(const(void))); - static assert(isBasicType!(shared(void))); - static assert(isBasicType!(immutable(void))); - static assert(isBasicType!(shared const(void))); - static assert(isBasicType!(shared inout(void))); - static assert(isBasicType!(shared inout const(void))); - static assert(isBasicType!(inout(void))); - static assert(isBasicType!(inout const(void))); - static assert(isBasicType!(immutable(int))); - static assert(isBasicType!(shared(float))); - static assert(isBasicType!(shared(const bool))); - static assert(isBasicType!(const(dchar))); -} - -/** - * Detect whether `T` is a built-in unsigned numeric type. - */ -template isUnsigned(T) -{ - static if (!__traits(isUnsigned, T)) - enum isUnsigned = false; - else static if (is(T U == enum)) - enum isUnsigned = isUnsigned!U; - else - enum isUnsigned = __traits(isZeroInit, T) // Not char, wchar, or dchar. - && !is(immutable T == immutable bool) && !is(T == __vector); -} - -/// -@safe unittest -{ - static assert( - isUnsigned!uint && - isUnsigned!ulong - ); - - static assert( - !isUnsigned!char && - !isUnsigned!int && - !isUnsigned!long && - !isUnsigned!char && - !isUnsigned!wchar && - !isUnsigned!dchar - ); -} - -@safe unittest -{ - static foreach (T; AliasSeq!(UnsignedIntTypeList)) - { - static foreach (Q; TypeQualifierList) - { - static assert( isUnsigned!(Q!T)); - static assert(!isUnsigned!(SubTypeOf!(Q!T))); - } - } - - static struct S(T) - { - T t; - alias t this; - } - static assert(!isUnsigned!(S!uint)); - - enum EChar : char { a = 0, } - static assert(!isUnsigned!EChar); - - static if (is(__vector(uint[4]))) - { - static assert(!isUnsigned!(__vector(uint[4]))); - } -} - -/** - * Detect whether `T` is a built-in signed numeric type. - */ -enum bool isSigned(T) = __traits(isArithmetic, T) && !__traits(isUnsigned, T) - && is(T : real); - -/// -@safe unittest -{ - static assert( - isSigned!int && - isSigned!long - ); - - static assert( - !isSigned!uint && - !isSigned!ulong - ); -} - -@safe unittest -{ - enum E { e1 = 0 } - static assert(isSigned!E); - - enum Eubyte : ubyte { e1 = 0 } - static assert(!isSigned!Eubyte); - - static foreach (T; AliasSeq!(SignedIntTypeList)) - { - static foreach (Q; TypeQualifierList) - { - static assert( isSigned!(Q!T)); - static assert(!isSigned!(SubTypeOf!(Q!T))); - } - } - - static struct S(T) - { - T t; - alias t this; - } - static assert(!isSigned!(S!uint)); - - static if (is(__vector(int[4]))) - { - static assert(!isSigned!(__vector(int[4]))); - } - - static assert(!isSigned!ifloat); - static assert(!isSigned!cfloat); -} - -// https://issues.dlang.org/show_bug.cgi?id=17196 -@safe unittest -{ - static assert(isUnsigned!bool == false); - static assert(isSigned!bool == false); -} - -/** - * Detect whether `T` is one of the built-in character types. - * - * The built-in char types are any of `char`, `wchar` or `dchar`, with - * or without qualifiers. - */ -template isSomeChar(T) -{ - static if (!__traits(isUnsigned, T)) - enum isSomeChar = false; - else static if (is(T U == enum)) - enum isSomeChar = isSomeChar!U; - else - enum isSomeChar = !__traits(isZeroInit, T); -} - -/// -@safe unittest -{ - //Char types - static assert( isSomeChar!char); - static assert( isSomeChar!wchar); - static assert( isSomeChar!dchar); - static assert( isSomeChar!(typeof('c'))); - static assert( isSomeChar!(immutable char)); - static assert( isSomeChar!(const dchar)); - - //Non char types - static assert(!isSomeChar!int); - static assert(!isSomeChar!byte); - static assert(!isSomeChar!string); - static assert(!isSomeChar!wstring); - static assert(!isSomeChar!dstring); - static assert(!isSomeChar!(char[4])); -} - -@safe unittest -{ - enum EC : char { a = 'x', b = 'y' } - - static foreach (T; AliasSeq!(CharTypeList, EC)) - { - static foreach (Q; TypeQualifierList) - { - static assert( isSomeChar!( Q!T )); - static assert(!isSomeChar!( SubTypeOf!(Q!T) )); - } - } - - // alias-this types are not allowed - static struct S(T) - { - T t; - alias t this; - } - static assert(!isSomeChar!(S!char)); -} - -/** -Detect whether `T` is one of the built-in string types. - -The built-in string types are `Char[]`, where `Char` is any of `char`, -`wchar` or `dchar`, with or without qualifiers. - -Static arrays of characters (like `char[80]`) are not considered -built-in string types. - */ -enum bool isSomeString(T) = is(immutable T == immutable C[], C) && (is(C == char) || is(C == wchar) || is(C == dchar)); - -/// -@safe unittest -{ - //String types - static assert( isSomeString!string); - static assert( isSomeString!(wchar[])); - static assert( isSomeString!(dchar[])); - static assert( isSomeString!(typeof("aaa"))); - static assert( isSomeString!(const(char)[])); - - //Non string types - static assert(!isSomeString!int); - static assert(!isSomeString!(int[])); - static assert(!isSomeString!(byte[])); - static assert(!isSomeString!(typeof(null))); - static assert(!isSomeString!(char[4])); - - enum ES : string { a = "aaa", b = "bbb" } - static assert(!isSomeString!ES); - - static struct Stringish - { - string str; - alias str this; - } - static assert(!isSomeString!Stringish); -} - -@safe unittest -{ - static foreach (T; AliasSeq!(char[], dchar[], string, wstring, dstring)) - { - static assert( isSomeString!( T )); - static assert(!isSomeString!(SubTypeOf!(T))); - } - enum C : char { _ = 0 } - static assert(!isSomeString!(C[])); -} - -/** - * Detect whether type `T` is a narrow string. - * - * All arrays that use char, wchar, and their qualified versions are narrow - * strings. (Those include string and wstring). - */ -enum bool isNarrowString(T) = is(immutable T == immutable C[], C) && (is(C == char) || is(C == wchar)); - -/// -@safe unittest -{ - static assert(isNarrowString!string); - static assert(isNarrowString!wstring); - static assert(isNarrowString!(char[])); - static assert(isNarrowString!(wchar[])); - - static assert(!isNarrowString!dstring); - static assert(!isNarrowString!(dchar[])); - - static assert(!isNarrowString!(typeof(null))); - static assert(!isNarrowString!(char[4])); - - enum ES : string { a = "aaa", b = "bbb" } - static assert(!isNarrowString!ES); - - static struct Stringish - { - string str; - alias str this; - } - static assert(!isNarrowString!Stringish); -} - -@safe unittest -{ - import std.meta : Alias; - static foreach (T; AliasSeq!(char[], string, wstring)) - { - static foreach (Q; AliasSeq!(Alias, ConstOf, ImmutableOf)/*TypeQualifierList*/) - { - static assert( isNarrowString!( Q!T )); - static assert(!isNarrowString!( SubTypeOf!(Q!T) )); - } - } - - static foreach (T; AliasSeq!(int, int[], byte[], dchar[], dstring, char[4])) - { - static foreach (Q; TypeQualifierList) - { - static assert(!isNarrowString!( Q!T )); - static assert(!isNarrowString!( SubTypeOf!(Q!T) )); - } - } - enum C : char { _ = 0 } - static assert(!isNarrowString!(C[])); -} - -/** - * Detects whether `T` is a comparable type. Basic types and structs and - * classes that implement opCmp are ordering comparable. - */ -enum bool isOrderingComparable(T) = is(typeof((ref T a) => a < a ? 1 : 0)); - -/// -@safe unittest -{ - static assert(isOrderingComparable!int); - static assert(isOrderingComparable!string); - - static struct Foo {} - static assert(!isOrderingComparable!Foo); - - static struct Bar - { - int a; - auto opCmp(Bar b1) const { return a - b1.a; } - } - - Bar b1 = Bar(5); - Bar b2 = Bar(7); - assert(isOrderingComparable!Bar && b2 > b1); -} - -/// ditto -enum bool isEqualityComparable(T) = is(typeof((ref T a) => a == a ? 1 : 0)); - -@safe unittest -{ - static assert(isEqualityComparable!int); - static assert(isEqualityComparable!string); - static assert(!isEqualityComparable!void); - - struct Foo {} - static assert(isEqualityComparable!Foo); - - struct Bar - { - int a; - auto opEquals(Bar b1) const { return a == b1.a; } - } - - Bar b1 = Bar(5); - Bar b2 = Bar(5); - Bar b3 = Bar(7); - static assert(isEqualityComparable!Bar); - assert(b1 == b2); - assert(b1 != b3); -} - -/** - $(RED Warning: This trait will be deprecated as soon as it is no longer used - in Phobos. For a function parameter to safely accept a type - that implicitly converts to string as a string, the conversion - needs to happen at the callsite; otherwise, the conversion is - done inside the function, and in many cases, that means that - local memory is sliced (e.g. if a static array is passed to - the function, then it's copied, and the resulting dynamic - array will be a slice of a local variable). So, if the - resulting string escapes the function, the string refers to - invalid memory, and accessing it would mean accessing invalid - memory. As such, the only safe way for a function to accept - types that implicitly convert to string is for the implicit - conversion to be done at the callsite, and that can only occur - if the parameter is explicitly typed as an array, whereas - using isConvertibleToString in a template constraint would - result in the conversion being done inside the function. As - such, isConvertibleToString is inherently unsafe and is going - to be deprecated.) - - Detect whether `T` is a struct, static array, or enum that is implicitly - convertible to a string. - */ -template isConvertibleToString(T) -{ - enum isConvertibleToString = - (isAggregateType!T || isStaticArray!T || is(T == enum)) - && is(StringTypeOf!T); -} - -/// -@safe unittest -{ - static struct AliasedString - { - string s; - alias s this; - } - - enum StringEnum { a = "foo" } - - assert(!isConvertibleToString!string); - assert(isConvertibleToString!AliasedString); - assert(isConvertibleToString!StringEnum); - assert(isConvertibleToString!(char[25])); - assert(!isConvertibleToString!(char[])); -} - -// https://issues.dlang.org/show_bug.cgi?id=16573 -@safe unittest -{ - enum I : int { foo = 1 } - enum S : string { foo = "foo" } - assert(!isConvertibleToString!I); - assert(isConvertibleToString!S); -} - -package template convertToString(T) -{ - static if (isConvertibleToString!T) - alias convertToString = StringTypeOf!T; - else - alias convertToString = T; -} - -/** - * Detect whether type `T` is a string that will be autodecoded. - * - * Given a type `S` that is one of: - * $(OL - * $(LI `const(char)[]`) - * $(LI `const(wchar)[]`) - * ) - * Type `T` can be one of: - * $(OL - * $(LI `S`) - * $(LI implicitly convertible to `T`) - * $(LI an enum with a base type `T`) - * $(LI an aggregate with a base type `T`) - * ) - * with the proviso that `T` cannot be a static array. - * - * Params: - * T = type to be tested - * - * Returns: - * true if T represents a string that is subject to autodecoding - * - * See Also: - * $(LREF isNarrowString) - */ -template isAutodecodableString(T) -{ - import std.range.primitives : autodecodeStrings; - - enum isAutodecodableString = autodecodeStrings && - (is(T : const char[]) || is(T : const wchar[])) - && !is(T : U[n], U, size_t n) - && !is(immutable T : immutable noreturn[]); -} - -/// -@safe unittest -{ - static struct Stringish - { - string s; - alias s this; - } - static assert(isAutodecodableString!wstring); - static assert(isAutodecodableString!Stringish); - static assert(!isAutodecodableString!dstring); - - enum E : const(char)[3] { X = "abc" } - enum F : const(char)[] { X = "abc" } - enum G : F { X = F.init } - - static assert(isAutodecodableString!(char[])); - static assert(!isAutodecodableString!(E)); - static assert(isAutodecodableString!(F)); - static assert(isAutodecodableString!(G)); - - struct Stringish2 - { - Stringish s; - alias s this; - } - - enum H : Stringish { X = Stringish() } - enum I : Stringish2 { X = Stringish2() } - - static assert(isAutodecodableString!(H)); - static assert(isAutodecodableString!(I)); - - static assert(!isAutodecodableString!(noreturn[])); - static assert(!isAutodecodableString!(immutable(noreturn)[])); -} - -/** - * Detect whether type `T` is a static array. - * - * See also: $(DDSUBLINK spec/traits, isStaticArray, `__traits(isStaticArray, T)`) - */ -enum bool isStaticArray(T) = __traits(isStaticArray, T); - -/// -@safe unittest -{ - static assert( isStaticArray!(int[3])); - static assert( isStaticArray!(const(int)[5])); - static assert( isStaticArray!(const(int)[][5])); - - static assert(!isStaticArray!(const(int)[])); - static assert(!isStaticArray!(immutable(int)[])); - static assert(!isStaticArray!(const(int)[4][])); - static assert(!isStaticArray!(int[])); - static assert(!isStaticArray!(int[char])); - static assert(!isStaticArray!(int[1][])); - static assert(!isStaticArray!(int[int])); - static assert(!isStaticArray!int); -} - -@safe unittest -{ - static foreach (T; AliasSeq!(int[51], int[][2], - char[][int][11], immutable char[13u], - const(real)[1], const(real)[1][1], void[0])) - { - static foreach (Q; TypeQualifierList) - { - static assert( isStaticArray!( Q!T )); - static assert(!isStaticArray!( SubTypeOf!(Q!T) )); - } - } - - //enum ESA : int[1] { a = [1], b = [2] } - //static assert( isStaticArray!ESA); -} - -/** - * Detect whether type `T` is a dynamic array. - */ -template isDynamicArray(T) -{ - static if (is(T == U[], U)) - enum bool isDynamicArray = true; - else static if (is(T U == enum)) - // BUG: isDynamicArray / isStaticArray considers enums - // with appropriate base types as dynamic/static arrays - // Retain old behaviour for now, see - // https://github.com/dlang/phobos/pull/7574 - enum bool isDynamicArray = isDynamicArray!U; - else - enum bool isDynamicArray = false; -} - -/// -@safe unittest -{ - static assert( isDynamicArray!(int[])); - static assert( isDynamicArray!(string)); - static assert( isDynamicArray!(long[3][])); - - static assert(!isDynamicArray!(int[5])); - static assert(!isDynamicArray!(typeof(null))); -} - -@safe unittest -{ - import std.meta : AliasSeq; - static foreach (T; AliasSeq!(int[], char[], string, long[3][], double[string][])) - { - static foreach (Q; TypeQualifierList) - { - static assert( isDynamicArray!( Q!T )); - static assert(!isDynamicArray!( SubTypeOf!(Q!T) )); - } - } - - static assert(!isDynamicArray!(int[5])); - - static struct AliasThis - { - int[] values; - alias values this; - } - - static assert(!isDynamicArray!AliasThis); - - // https://github.com/dlang/phobos/pull/7574/files#r464115492 - enum E : string - { - a = "a", - b = "b", - } - static assert( isDynamicArray!E); -} - -/** - * Detect whether type `T` is an array (static or dynamic; for associative - * arrays see $(LREF isAssociativeArray)). - */ -enum bool isArray(T) = isStaticArray!T || isDynamicArray!T; - -/// -@safe unittest -{ - static assert( isArray!(int[])); - static assert( isArray!(int[5])); - static assert( isArray!(string)); - - static assert(!isArray!uint); - static assert(!isArray!(uint[uint])); - static assert(!isArray!(typeof(null))); -} - -@safe unittest -{ - import std.meta : AliasSeq; - static foreach (T; AliasSeq!(int[], int[5], void[])) - { - static foreach (Q; TypeQualifierList) - { - static assert( isArray!(Q!T)); - static assert(!isArray!(SubTypeOf!(Q!T))); - } - } -} - -/** - * Detect whether `T` is an associative array type - * - * See also: $(DDSUBLINK spec/traits, isAssociativeArray, `__traits(isAssociativeArray, T)`) - */ -enum bool isAssociativeArray(T) = __traits(isAssociativeArray, T); - -/// -@safe unittest -{ - struct S; - - static assert( isAssociativeArray!(int[string])); - static assert( isAssociativeArray!(S[S])); - static assert(!isAssociativeArray!(string[])); - static assert(!isAssociativeArray!S); - static assert(!isAssociativeArray!(int[4])); -} - -@safe unittest -{ - struct Foo - { - @property uint[] keys() { return null; } - @property uint[] values() { return null; } - } - - static foreach (T; AliasSeq!(int[int], int[string], immutable(char[5])[int])) - { - static foreach (Q; TypeQualifierList) - { - static assert( isAssociativeArray!(Q!T)); - static assert(!isAssociativeArray!(SubTypeOf!(Q!T))); - } - } - - static assert(!isAssociativeArray!Foo); - static assert(!isAssociativeArray!int); - static assert(!isAssociativeArray!(int[])); - static assert(!isAssociativeArray!(typeof(null))); - - //enum EAA : int[int] { a = [1:1], b = [2:2] } - //static assert( isAssociativeArray!EAA); -} - -/** - * Detect whether type `T` is a builtin type. - */ -enum bool isBuiltinType(T) = is(BuiltinTypeOf!T) && !isAggregateType!T; - -/// -@safe unittest -{ - class C; - union U; - struct S; - interface I; - - static assert( isBuiltinType!void); - static assert( isBuiltinType!string); - static assert( isBuiltinType!(int[])); - static assert( isBuiltinType!(C[string])); - static assert( isBuiltinType!(typeof(null))); - static assert(!isBuiltinType!C); - static assert(!isBuiltinType!U); - static assert(!isBuiltinType!S); - static assert(!isBuiltinType!I); - static assert(!isBuiltinType!(void delegate(int))); -} - -/** - * Detect whether type `T` is a SIMD vector type. - */ -enum bool isSIMDVector(T) = is(T : __vector(V[N]), V, size_t N); - -/// -@safe unittest -{ - static if (is(__vector(float[4]))) - { - alias SimdVec = __vector(float[4]); - static assert(isSIMDVector!(__vector(float[4]))); - static assert(isSIMDVector!SimdVec); - } - static assert(!isSIMDVector!uint); - static assert(!isSIMDVector!(float[4])); -} - -/** - * Detect whether type `T` is a pointer. - */ -enum bool isPointer(T) = is(T == U*, U); - -/// -@safe unittest -{ - void fun(); - - static assert( isPointer!(int*)); - static assert( isPointer!(int function())); - static assert(!isPointer!int); - static assert(!isPointer!string); - static assert(!isPointer!(typeof(null))); - static assert(!isPointer!(typeof(fun))); - static assert(!isPointer!(int delegate())); -} - -@safe unittest -{ - static foreach (T; AliasSeq!(int*, void*, char[]*)) - { - static foreach (Q; TypeQualifierList) - { - static assert( isPointer!(Q!T)); - static assert(!isPointer!(SubTypeOf!(Q!T))); - } - } - - static assert(!isPointer!uint); - static assert(!isPointer!(uint[uint])); - static assert(!isPointer!(char[])); - static assert(!isPointer!(typeof(null))); -} - -/** -Returns the target type of a pointer. -*/ -alias PointerTarget(T : T*) = T; - -/// -@safe unittest -{ - static assert(is(PointerTarget!(int*) == int)); - static assert(is(PointerTarget!(void*) == void)); -} - -/** - * Detect whether type `T` is an aggregate type. - */ -enum bool isAggregateType(T) = is(T == struct) || is(T == union) || - is(T == class) || is(T == interface); - -/// -@safe unittest -{ - class C; - union U; - struct S; - interface I; - - static assert( isAggregateType!C); - static assert( isAggregateType!U); - static assert( isAggregateType!S); - static assert( isAggregateType!I); - static assert(!isAggregateType!void); - static assert(!isAggregateType!string); - static assert(!isAggregateType!(int[])); - static assert(!isAggregateType!(C[string])); - static assert(!isAggregateType!(void delegate(int))); -} - -/** - * Returns `true` if T can be iterated over using a `foreach` loop with - * a single loop variable of automatically inferred type, regardless of how - * the `foreach` loop is implemented. This includes ranges, structs/classes - * that define `opApply` with a single loop variable, and builtin dynamic, - * static and associative arrays. - */ -enum bool isIterable(T) = is(typeof({ foreach (elem; T.init) {} })); - -/// -@safe unittest -{ - struct OpApply - { - int opApply(scope int delegate(ref uint) dg) { assert(0); } - } - - struct Range - { - @property uint front() { assert(0); } - void popFront() { assert(0); } - enum bool empty = false; - } - - static assert( isIterable!(uint[])); - static assert( isIterable!OpApply); - static assert( isIterable!(uint[string])); - static assert( isIterable!Range); - - static assert(!isIterable!uint); -} - -/** - * Returns true if T is not const or immutable. Note that isMutable is true for - * string, or immutable(char)[], because the 'head' is mutable. - */ -enum bool isMutable(T) = !is(T == const) && !is(T == immutable) && !is(T == inout); - -/// -@safe unittest -{ - static assert( isMutable!int); - static assert( isMutable!string); - static assert( isMutable!(shared int)); - static assert( isMutable!(shared const(int)[])); - - static assert(!isMutable!(const int)); - static assert(!isMutable!(inout int)); - static assert(!isMutable!(shared(const int))); - static assert(!isMutable!(shared(inout int))); - static assert(!isMutable!(immutable string)); -} - -/** - * Returns true if T is an instance of the template S. - */ -enum bool isInstanceOf(alias S, T) = is(T == S!Args, Args...); -/// ditto -template isInstanceOf(alias S, alias T) -{ - enum impl(alias T : S!Args, Args...) = true; - enum impl(alias T) = false; - enum isInstanceOf = impl!T; -} - -/// -@safe unittest -{ - static struct Foo(T...) { } - static struct Bar(T...) { } - static struct Doo(T) { } - static struct ABC(int x) { } - static void fun(T)() { } - template templ(T) { } - - static assert(isInstanceOf!(Foo, Foo!int)); - static assert(!isInstanceOf!(Foo, Bar!int)); - static assert(!isInstanceOf!(Foo, int)); - static assert(isInstanceOf!(Doo, Doo!int)); - static assert(isInstanceOf!(ABC, ABC!1)); - static assert(!isInstanceOf!(Foo, Foo)); - static assert(isInstanceOf!(fun, fun!int)); - static assert(isInstanceOf!(templ, templ!int)); -} - -/** - * To use `isInstanceOf` to check the identity of a template while inside of said - * template, use $(LREF TemplateOf). - */ -@safe unittest -{ - static struct A(T = void) - { - // doesn't work as expected, only accepts A when T = void - void func(B)(B b) if (isInstanceOf!(A, B)) {} - - // correct behavior - void method(B)(B b) if (isInstanceOf!(TemplateOf!(A), B)) {} - } - - A!(void) a1; - A!(void) a2; - A!(int) a3; - - static assert(!__traits(compiles, a1.func(a3))); - static assert( __traits(compiles, a1.method(a2))); - static assert( __traits(compiles, a1.method(a3))); -} - -@safe unittest -{ - static void fun1(T)() { } - static void fun2(T)() { } - template templ1(T) { } - template templ2(T) { } - - static assert(!isInstanceOf!(fun1, fun2!int)); - static assert(!isInstanceOf!(templ1, templ2!int)); -} - -/** - * Check whether the tuple T is an expression tuple. - * An expression tuple only contains expressions. - * - * See_Also: $(LREF isTypeTuple). - */ -template isExpressions(T...) -{ - static foreach (Ti; T) - { - static if (!is(typeof(isExpressions) == bool) && // not yet defined - (is(Ti) || !__traits(compiles, { auto ex = Ti; }))) - { - enum isExpressions = false; - } - } - static if (!is(typeof(isExpressions) == bool)) // if not yet defined - { - enum isExpressions = true; - } -} - -/// -@safe unittest -{ - static assert(isExpressions!(1, 2.0, "a")); - static assert(!isExpressions!(int, double, string)); - static assert(!isExpressions!(int, 2.0, "a")); -} - -/** - * Alternate name for $(LREF isExpressions), kept for legacy compatibility. - */ - -alias isExpressionTuple = isExpressions; - -@safe unittest -{ - void foo(); - static int bar() { return 42; } - immutable aa = [ 1: -1 ]; - alias myint = int; - - static assert( isExpressionTuple!(42)); - static assert( isExpressionTuple!aa); - static assert( isExpressionTuple!("cattywampus", 2.7, aa)); - static assert( isExpressionTuple!(bar())); - - static assert(!isExpressionTuple!isExpressionTuple); - static assert(!isExpressionTuple!foo); - static assert(!isExpressionTuple!( (a) { } )); - static assert(!isExpressionTuple!int); - static assert(!isExpressionTuple!myint); -} - - -/** - * Check whether the tuple `T` is a type tuple. - * A type tuple only contains types. - * - * See_Also: $(LREF isExpressions). - */ -enum isTypeTuple(T...) = -{ - static foreach (U; T) - static if (!is(U)) - if (__ctfe) - return false; - return true; -}(); - -/// -@safe unittest -{ - static assert(isTypeTuple!(int, float, string)); - static assert(!isTypeTuple!(1, 2.0, "a")); - static assert(!isTypeTuple!(1, double, string)); -} - -@safe unittest -{ - class C {} - void func(int) {} - auto c = new C; - enum CONST = 42; - - static assert( isTypeTuple!int); - static assert( isTypeTuple!string); - static assert( isTypeTuple!C); - static assert( isTypeTuple!(typeof(func))); - static assert( isTypeTuple!(int, char, double)); - - static assert(!isTypeTuple!c); - static assert(!isTypeTuple!isTypeTuple); - static assert(!isTypeTuple!CONST); -} - - -/** -Detect whether symbol or type `T` is a function pointer. - */ -enum bool isFunctionPointer(alias T) = is(typeof(*T) == function); - -/// -@safe unittest -{ - static void foo() {} - void bar() {} - - auto fpfoo = &foo; - static assert( isFunctionPointer!fpfoo); - static assert( isFunctionPointer!(void function())); - - auto dgbar = &bar; - static assert(!isFunctionPointer!dgbar); - static assert(!isFunctionPointer!(void delegate())); - static assert(!isFunctionPointer!foo); - static assert(!isFunctionPointer!bar); - - static assert( isFunctionPointer!((int a) {})); -} - -/** -Detect whether symbol or type `T` is a delegate. -*/ -enum bool isDelegate(alias T) = is(typeof(T) == delegate) || is(T == delegate); - -/// -@safe unittest -{ - static void sfunc() { } - int x; - void func() { x++; } - - int delegate() dg; - assert(isDelegate!dg); - assert(isDelegate!(int delegate())); - assert(isDelegate!(typeof(&func))); - - int function() fp; - assert(!isDelegate!fp); - assert(!isDelegate!(int function())); - assert(!isDelegate!(typeof(&sfunc))); -} - -/** -Detect whether symbol or type `T` is a function, a function pointer or a delegate. - -Params: - T = The type to check -Returns: - A `bool` - */ -enum bool isSomeFunction(alias T) = - is(T == return) || - is(typeof(T) == return) || - is(typeof(&T) == return); // @property - -/// -@safe unittest -{ - static real func(ref int) { return 0; } - static void prop() @property { } - class C - { - real method(ref int) { return 0; } - real prop() @property { return 0; } - } - auto c = new C; - auto fp = &func; - auto dg = &c.method; - - static assert( isSomeFunction!func); - static assert( isSomeFunction!prop); - static assert( isSomeFunction!(C.method)); - static assert( isSomeFunction!(C.prop)); - static assert( isSomeFunction!(c.prop)); - static assert( isSomeFunction!fp); - static assert( isSomeFunction!dg); - - real val; - static assert(!isSomeFunction!int); - static assert(!isSomeFunction!val); -} - -@safe unittest -{ - void nestedFunc() { } - void nestedProp() @property { } - static assert(isSomeFunction!nestedFunc); - static assert(isSomeFunction!nestedProp); - static assert(isSomeFunction!(real function(ref int))); - static assert(isSomeFunction!(real delegate(ref int))); - static assert(isSomeFunction!((int a) { return a; })); - static assert(!isSomeFunction!isSomeFunction); -} - -/** -Detect whether `T` is a callable object, which can be called with the -function call operator `$(LPAREN)...$(RPAREN)`. - */ -template isCallable(alias callable) -{ - static if (is(typeof(&callable.opCall) == delegate)) - // T is a object which has a member function opCall(). - enum bool isCallable = true; - else static if (is(typeof(&callable.opCall) V : V*) && is(V == function)) - // T is a type which has a static member function opCall(). - enum bool isCallable = true; - else static if (is(typeof(&callable.opCall!()) TemplateInstanceType)) - { - enum bool isCallable = isCallable!TemplateInstanceType; - } - else static if (is(typeof(&callable!()) TemplateInstanceType)) - { - enum bool isCallable = isCallable!TemplateInstanceType; - } - else - { - enum bool isCallable = isSomeFunction!callable; - } -} - -/// Functions, lambdas, and aggregate types with (static) opCall. -@safe unittest -{ - void f() { } - int g(int x) { return x; } - - static assert( isCallable!f); - static assert( isCallable!g); - - class C { int opCall(int) { return 0; } } - auto c = new C; - struct S { static int opCall(int) { return 0; } } - interface I { real value() @property; } - - static assert( isCallable!c); - static assert( isCallable!(c.opCall)); - static assert( isCallable!S); - static assert( isCallable!(I.value)); - static assert( isCallable!((int a) { return a; })); - - static assert(!isCallable!I); -} - -/// Templates -@safe unittest -{ - void f()() { } - T g(T = int)(T x) { return x; } - struct S1 { static void opCall()() { } } - struct S2 { static T opCall(T = int)(T x) {return x; } } - - static assert( isCallable!f); - static assert( isCallable!g); - static assert( isCallable!S1); - static assert( isCallable!S2); -} - -/// Overloaded functions and function templates. -@safe unittest -{ - static struct Wrapper - { - void f() { } - int f(int x) { return x; } - - void g()() { } - T g(T = int)(T x) { return x; } - } - - static assert(isCallable!(Wrapper.f)); - static assert(isCallable!(Wrapper.g)); -} - - -/** -Detect whether `S` is an abstract function. - -See also: $(DDSUBLINK spec/traits, isAbstractFunction, `__traits(isAbstractFunction, S)`) -Params: - S = The symbol to check -Returns: - A `bool` - */ -enum isAbstractFunction(alias S) = __traits(isAbstractFunction, S); - -/// -@safe unittest -{ - struct S { void foo() { } } - class C { void foo() { } } - class AC { abstract void foo(); } - static assert(!isAbstractFunction!(int)); - static assert(!isAbstractFunction!(S.foo)); - static assert(!isAbstractFunction!(C.foo)); - static assert( isAbstractFunction!(AC.foo)); -} - -/** - * Detect whether `S` is a final function. - * - * See also: $(DDSUBLINK spec/traits, isFinalFunction, `__traits(isFinalFunction, S)`) - */ -enum isFinalFunction(alias S) = __traits(isFinalFunction, S); - -/// -@safe unittest -{ - struct S { void bar() { } } - final class FC { void foo(); } - class C - { - void bar() { } - final void foo(); - } - static assert(!isFinalFunction!(int)); - static assert(!isFinalFunction!(S.bar)); - static assert( isFinalFunction!(FC.foo)); - static assert(!isFinalFunction!(C.bar)); - static assert( isFinalFunction!(C.foo)); -} - -/** -Determines if `f` is a function that requires a context pointer. - -Params: - f = The type to check -Returns - A `bool` -*/ -template isNestedFunction(alias f) -{ - enum isNestedFunction = __traits(isNested, f) && isSomeFunction!(f); -} - -/// -@safe unittest -{ - static void f() {} - static void fun() - { - int i; - int f() { return i; } - - static assert(isNestedFunction!(f)); - } - - static assert(!isNestedFunction!f); -} - -// https://issues.dlang.org/show_bug.cgi?id=18669 -@safe unittest -{ - static class Outer - { - class Inner - { - } - } - int i; - struct SS - { - int bar() { return i; } - } - static assert(!isNestedFunction!(Outer.Inner)); - static assert(!isNestedFunction!(SS)); -} - -/** - * Detect whether `S` is an abstract class. - * - * See also: $(DDSUBLINK spec/traits, isAbstractClass, `__traits(isAbstractClass, S)`) - */ -enum isAbstractClass(alias S) = __traits(isAbstractClass, S); - -/// -@safe unittest -{ - struct S { } - class C { } - abstract class AC { } - static assert(!isAbstractClass!S); - static assert(!isAbstractClass!C); - static assert( isAbstractClass!AC); - C c; - static assert(!isAbstractClass!c); - AC ac; - static assert( isAbstractClass!ac); -} - -/** - * Detect whether `S` is a final class. - * - * See also: $(DDSUBLINK spec/traits, isFinalClass, `__traits(isFinalClass, S)`) - */ -enum isFinalClass(alias S) = __traits(isFinalClass, S); - -/// -@safe unittest -{ - class C { } - abstract class AC { } - final class FC1 : C { } - final class FC2 { } - static assert(!isFinalClass!C); - static assert(!isFinalClass!AC); - static assert( isFinalClass!FC1); - static assert( isFinalClass!FC2); - C c; - static assert(!isFinalClass!c); - FC1 fc1; - static assert( isFinalClass!fc1); -} - -//:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::// -// General Types -//:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::// - -version (StdDdoc) -{ - /** - Removes `const`, `inout` and `immutable` qualifiers, if any, from type `T`. - */ - template Unconst(T) - { - import core.internal.traits : CoreUnconst = Unconst; - alias Unconst = CoreUnconst!(T); - } -} -else -{ - import core.internal.traits : CoreUnconst = Unconst; - alias Unconst = CoreUnconst; -} - -/// -@safe unittest -{ - static assert(is(Unconst!int == int)); - static assert(is(Unconst!(const int) == int)); - static assert(is(Unconst!(immutable int) == int)); - static assert(is(Unconst!(shared int) == shared int)); - static assert(is(Unconst!(shared(const int)) == shared int)); -} - -@safe unittest -{ - static assert(is(Unconst!( int) == int)); - static assert(is(Unconst!( const int) == int)); - static assert(is(Unconst!( inout int) == int)); - static assert(is(Unconst!( inout const int) == int)); - static assert(is(Unconst!(shared int) == shared int)); - static assert(is(Unconst!(shared const int) == shared int)); - static assert(is(Unconst!(shared inout int) == shared int)); - static assert(is(Unconst!(shared inout const int) == shared int)); - static assert(is(Unconst!( immutable int) == int)); - - alias ImmIntArr = immutable(int[]); - static assert(is(Unconst!ImmIntArr == immutable(int)[])); -} - -/++ - Removes `shared` qualifier, if any, from type `T`. - - Note that while `immutable` is implicitly `shared`, it is unaffected by - Unshared. Only explict `shared` is removed. - +/ -template Unshared(T) -{ - static if (is(T == shared U, U)) - alias Unshared = U; - else - alias Unshared = T; -} - -/// -@safe unittest -{ - static assert(is(Unshared!int == int)); - static assert(is(Unshared!(const int) == const int)); - static assert(is(Unshared!(immutable int) == immutable int)); - - static assert(is(Unshared!(shared int) == int)); - static assert(is(Unshared!(shared(const int)) == const int)); - - static assert(is(Unshared!(shared(int[])) == shared(int)[])); -} - -@safe unittest -{ - static assert(is(Unshared!( int) == int)); - static assert(is(Unshared!( const int) == const int)); - static assert(is(Unshared!( inout int) == inout int)); - static assert(is(Unshared!( inout const int) == inout const int)); - static assert(is(Unshared!(shared int) == int)); - static assert(is(Unshared!(shared const int) == const int)); - static assert(is(Unshared!(shared inout int) == inout int)); - static assert(is(Unshared!(shared inout const int) == inout const int)); - static assert(is(Unshared!( immutable int) == immutable int)); -} - -version (StdDdoc) -{ - /** - Removes all qualifiers, if any, from type `T`. - */ - template Unqual(T) - { - import core.internal.traits : CoreUnqual = Unqual; - alias Unqual = CoreUnqual!(T); - } -} -else -{ - import core.internal.traits : CoreUnqual = Unqual; - alias Unqual = CoreUnqual; -} - -/// -@safe unittest -{ - static assert(is(Unqual!int == int)); - static assert(is(Unqual!(const int) == int)); - static assert(is(Unqual!(immutable int) == int)); - static assert(is(Unqual!(shared int) == int)); - static assert(is(Unqual!(shared(const int)) == int)); -} - -@safe unittest -{ - static assert(is(Unqual!( int) == int)); - static assert(is(Unqual!( const int) == int)); - static assert(is(Unqual!( inout int) == int)); - static assert(is(Unqual!( inout const int) == int)); - static assert(is(Unqual!(shared int) == int)); - static assert(is(Unqual!(shared const int) == int)); - static assert(is(Unqual!(shared inout int) == int)); - static assert(is(Unqual!(shared inout const int) == int)); - static assert(is(Unqual!( immutable int) == int)); - - alias ImmIntArr = immutable(int[]); - static assert(is(Unqual!ImmIntArr == immutable(int)[])); -} - -// [For internal use] -package template ModifyTypePreservingTQ(alias Modifier, T) -{ - import core.internal.traits : _ModifyTypePreservingTQ = ModifyTypePreservingTQ; - alias ModifyTypePreservingTQ = _ModifyTypePreservingTQ!(Modifier, T); -} - -/** - * Copies type qualifiers from `FromType` to `ToType`. - * - * Supported type qualifiers: - * $(UL - * $(LI `const`) - * $(LI `inout`) - * $(LI `immutable`) - * $(LI `shared`) - * ) - */ -template CopyTypeQualifiers(FromType, ToType) -{ - alias T(U) = ToType; - alias CopyTypeQualifiers = ModifyTypePreservingTQ!(T, FromType); -} - -/// -@safe unittest -{ - static assert(is(CopyTypeQualifiers!(inout const real, int) == inout const int)); -} - -@safe unittest -{ - static assert(is(CopyTypeQualifiers!( real, int) == int)); - static assert(is(CopyTypeQualifiers!( const real, int) == const int)); - static assert(is(CopyTypeQualifiers!( inout real, int) == inout int)); - static assert(is(CopyTypeQualifiers!( inout const real, int) == inout const int)); - static assert(is(CopyTypeQualifiers!(shared real, int) == shared int)); - static assert(is(CopyTypeQualifiers!(shared const real, int) == shared const int)); - static assert(is(CopyTypeQualifiers!(shared inout real, int) == shared inout int)); - static assert(is(CopyTypeQualifiers!(shared inout const real, int) == shared inout const int)); - static assert(is(CopyTypeQualifiers!( immutable real, int) == immutable int)); -} - -/** -Returns the type of `ToType` with the "constness" of `FromType`. A type's $(B constness) -refers to whether it is `const`, `immutable`, or `inout`. If `FromType` has no constness, the -returned type will be the same as `ToType`. -*/ -template CopyConstness(FromType, ToType) -{ - alias Unshared(T) = T; - alias Unshared(T: shared U, U) = U; - - alias CopyConstness = Unshared!(CopyTypeQualifiers!(FromType, ToType)); -} - -/// -@safe unittest -{ - const(int) i; - CopyConstness!(typeof(i), float) f; - assert( is(typeof(f) == const float)); - - CopyConstness!(char, uint) u; - assert( is(typeof(u) == uint)); - - //The 'shared' qualifier will not be copied - assert(!is(CopyConstness!(shared bool, int) == shared int)); - - //But the constness will be - assert( is(CopyConstness!(shared const real, double) == const double)); - - //Careful, const(int)[] is a mutable array of const(int) - alias MutT = CopyConstness!(const(int)[], int); - assert(!is(MutT == const(int))); - - //Okay, const(int[]) applies to array and contained ints - alias CstT = CopyConstness!(const(int[]), int); - assert( is(CstT == const(int))); -} - -@safe unittest -{ - struct Test - { - void method1() {} - void method2() const {} - void method3() immutable {} - } - - assert(is(CopyConstness!(typeof(Test.method1), real) == real)); - - assert(is(CopyConstness!(typeof(Test.method2), byte) == const(byte))); - - assert(is(CopyConstness!(typeof(Test.method3), string) == immutable(string))); -} - -@safe unittest -{ - assert(is(CopyConstness!(inout(int)[], int[]) == int[])); - assert(is(CopyConstness!(inout(int[]), int[]) == inout(int[]))); -} - -@safe unittest -{ - static assert(is(CopyConstness!( int, real) == real)); - static assert(is(CopyConstness!(const int, real) == const real)); - static assert(is(CopyConstness!(inout int, real) == inout real)); - static assert(is(CopyConstness!(inout const int, real) == inout const real)); - static assert(is(CopyConstness!(shared int, real) == real)); - static assert(is(CopyConstness!(shared const int, real) == const real)); - static assert(is(CopyConstness!(shared inout int, real) == inout real)); - static assert(is(CopyConstness!(shared inout const int, real) == inout const real)); - static assert(is(CopyConstness!(immutable int, real) == immutable real)); -} - -/** -Returns the inferred type of the loop variable when a variable of type T -is iterated over using a `foreach` loop with a single loop variable and -automatically inferred return type. Note that this may not be the same as -`std.range.ElementType!Range` in the case of narrow strings, or if T -has both opApply and a range interface. -*/ -template ForeachType(T) -{ - alias ForeachType = typeof( - (inout int x = 0) - { - foreach (elem; T.init) - { - return elem; - } - assert(0); - }()); -} - -/// -@safe unittest -{ - static assert(is(ForeachType!(uint[]) == uint)); - static assert(is(ForeachType!string == immutable(char))); - static assert(is(ForeachType!(string[string]) == string)); - static assert(is(ForeachType!(inout(int)[]) == inout(int))); -} - - -/** - * Strips off all `enum`s from type `T`. - */ -template OriginalType(T) -{ - import core.internal.traits : _OriginalType = OriginalType; - alias OriginalType = _OriginalType!T; -} - -/// -@safe unittest -{ - enum E : real { a = 0 } // NOTE: explicit initialization to 0 required during Enum init deprecation cycle - enum F : E { a = E.a } - alias G = const(F); - static assert(is(OriginalType!E == real)); - static assert(is(OriginalType!F == real)); - static assert(is(OriginalType!G == const real)); -} - -/** - * Get the Key type of an Associative Array. - */ -alias KeyType(V : V[K], K) = K; - -/// -@safe unittest -{ - alias Hash = int[string]; - static assert(is(KeyType!Hash == string)); - static assert(is(ValueType!Hash == int)); - KeyType!Hash str = "a"; // str is declared as string - ValueType!Hash num = 1; // num is declared as int -} - -/** - * Get the Value type of an Associative Array. - */ -alias ValueType(V : V[K], K) = V; - -/// -@safe unittest -{ - alias Hash = int[string]; - static assert(is(KeyType!Hash == string)); - static assert(is(ValueType!Hash == int)); - KeyType!Hash str = "a"; // str is declared as string - ValueType!Hash num = 1; // num is declared as int -} - -/** -Params: - T = A built in integral or vector type. - -Returns: - The corresponding unsigned numeric type for `T` with the - same type qualifiers. - - If `T` is not a integral or vector, a compile-time error is given. - */ -template Unsigned(T) -{ - template Impl(T) - { - static if (is(T : __vector(V[N]), V, size_t N)) - alias Impl = __vector(Impl!V[N]); - else static if (isUnsigned!T) - alias Impl = T; - else static if (isSigned!T && !isFloatingPoint!T) - { - static if (is(T == byte )) alias Impl = ubyte; - static if (is(T == short)) alias Impl = ushort; - static if (is(T == int )) alias Impl = uint; - static if (is(T == long )) alias Impl = ulong; - static if (is(ucent) && is(T == cent )) alias Impl = ucent; - } - else - static assert(false, "Type " ~ T.stringof ~ - " does not have an Unsigned counterpart"); - } - - alias Unsigned = ModifyTypePreservingTQ!(Impl, OriginalType!T); -} - -/// -@safe unittest -{ - static assert(is(Unsigned!(int) == uint)); - static assert(is(Unsigned!(long) == ulong)); - static assert(is(Unsigned!(const short) == const ushort)); - static assert(is(Unsigned!(immutable byte) == immutable ubyte)); - static assert(is(Unsigned!(inout int) == inout uint)); -} - - -/// Unsigned types are forwarded -@safe unittest -{ - static assert(is(Unsigned!(uint) == uint)); - static assert(is(Unsigned!(const uint) == const uint)); - - static assert(is(Unsigned!(ubyte) == ubyte)); - static assert(is(Unsigned!(immutable uint) == immutable uint)); -} - -@safe unittest -{ - alias U1 = Unsigned!int; - alias U2 = Unsigned!(const(int)); - alias U3 = Unsigned!(immutable(int)); - static assert(is(U1 == uint)); - static assert(is(U2 == const(uint))); - static assert(is(U3 == immutable(uint))); - static if (is(__vector(int[4])) && is(__vector(uint[4]))) - { - alias UV1 = Unsigned!(__vector(int[4])); - alias UV2 = Unsigned!(const(__vector(int[4]))); - static assert(is(UV1 == __vector(uint[4]))); - static assert(is(UV2 == const(__vector(uint[4])))); - } - //struct S {} - //alias U2 = Unsigned!S; - //alias U3 = Unsigned!double; - static if (is(ucent)) - { - alias U4 = Unsigned!cent; - alias U5 = Unsigned!(const(cent)); - alias U6 = Unsigned!(immutable(cent)); - static assert(is(U4 == ucent)); - static assert(is(U5 == const(ucent))); - static assert(is(U6 == immutable(ucent))); - } -} - -/** -Returns the largest type, i.e. T such that T.sizeof is the largest. If more -than one type is of the same size, the leftmost argument of these in will be -returned. -*/ -template Largest(T...) -if (T.length >= 1) -{ - alias Largest = T[0]; - static foreach (U; T[1 .. $]) - Largest = Select!(U.sizeof > Largest.sizeof, U, Largest); -} - -/// -@safe unittest -{ - static assert(is(Largest!(uint, ubyte, ushort, real) == real)); - static assert(is(Largest!(ulong, double) == ulong)); - static assert(is(Largest!(double, ulong) == double)); - static assert(is(Largest!(uint, byte, double, short) == double)); - static if (is(ucent)) - static assert(is(Largest!(uint, ubyte, ucent, ushort) == ucent)); -} - -/** -Returns the corresponding signed type for T. T must be a numeric integral type, -otherwise a compile-time error occurs. - */ -template Signed(T) -{ - template Impl(T) - { - static if (is(T : __vector(V[N]), V, size_t N)) - alias Impl = __vector(Impl!V[N]); - else static if (isSigned!T) - alias Impl = T; - else static if (isUnsigned!T) - { - static if (is(T == ubyte )) alias Impl = byte; - static if (is(T == ushort)) alias Impl = short; - static if (is(T == uint )) alias Impl = int; - static if (is(T == ulong )) alias Impl = long; - static if (is(ucent) && is(T == ucent )) alias Impl = cent; - } - else - static assert(false, "Type " ~ T.stringof ~ - " does not have an Signed counterpart"); - } - - alias Signed = ModifyTypePreservingTQ!(Impl, OriginalType!T); -} - -/// -@safe unittest -{ - alias S1 = Signed!uint; - static assert(is(S1 == int)); - alias S2 = Signed!(const(uint)); - static assert(is(S2 == const(int))); - alias S3 = Signed!(immutable(uint)); - static assert(is(S3 == immutable(int))); - static if (is(ucent)) - { - alias S4 = Signed!ucent; - static assert(is(S4 == cent)); - } -} - -@safe unittest -{ - static assert(is(Signed!float == float)); - static if (is(__vector(int[4])) && is(__vector(uint[4]))) - { - alias SV1 = Signed!(__vector(uint[4])); - alias SV2 = Signed!(const(__vector(uint[4]))); - static assert(is(SV1 == __vector(int[4]))); - static assert(is(SV2 == const(__vector(int[4])))); - } -} - - -/** -Returns the most negative value of the numeric type T. -*/ -template mostNegative(T) -if (isNumeric!T || isSomeChar!T || isBoolean!T) -{ - static if (is(typeof(T.min_normal))) - enum mostNegative = -T.max; - else static if (T.min == 0) - enum byte mostNegative = 0; - else - enum mostNegative = T.min; -} - -/// -@safe unittest -{ - static assert(mostNegative!float == -float.max); - static assert(mostNegative!double == -double.max); - static assert(mostNegative!real == -real.max); - static assert(mostNegative!bool == false); -} - -/// -@safe unittest -{ - import std.meta : AliasSeq; - - static foreach (T; AliasSeq!(bool, byte, short, int, long)) - static assert(mostNegative!T == T.min); - - static foreach (T; AliasSeq!(ubyte, ushort, uint, ulong, char, wchar, dchar)) - static assert(mostNegative!T == 0); -} - -/** -Get the type that a scalar type `T` will $(LINK2 $(ROOT_DIR)spec/type.html#integer-promotions, promote) -to in multi-term arithmetic expressions. -*/ -template Promoted(T) -if (isScalarType!T) -{ - alias Promoted = CopyTypeQualifiers!(T, typeof(T.init + T.init)); -} - -/// -@safe unittest -{ - ubyte a = 3, b = 5; - static assert(is(typeof(a * b) == Promoted!ubyte)); - static assert(is(Promoted!ubyte == int)); - - static assert(is(Promoted!(shared(bool)) == shared(int))); - static assert(is(Promoted!(const(int)) == const(int))); - static assert(is(Promoted!double == double)); -} - -@safe unittest -{ - // promote to int: - static foreach (T; AliasSeq!(bool, byte, ubyte, short, ushort, char, wchar)) - { - static assert(is(Promoted!T == int)); - static assert(is(Promoted!(shared(const T)) == shared(const int))); - } - - // already promoted: - static foreach (T; AliasSeq!(int, uint, long, ulong, float, double, real)) - { - static assert(is(Promoted!T == T)); - static assert(is(Promoted!(immutable(T)) == immutable(T))); - } -} - -//::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::// -// Misc. -//::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::// - -/** -Returns the mangled name of symbol or type `sth`. - -`mangledName` is the same as builtin `.mangleof` property, but -might be more convenient in generic code, e.g. as a template argument -when invoking staticMap. - */ -enum mangledName(alias sth) = sth.mangleof; - -/// -@safe unittest -{ - import std.meta : AliasSeq; - alias TL = staticMap!(mangledName, int, const int, immutable int); - static assert(TL == AliasSeq!("i", "xi", "yi")); -} - -version (StdUnittest) private void freeFunc(string); - -@safe unittest -{ - class C { int value() @property { return 0; } } - static assert(mangledName!int == int.mangleof); - static assert(mangledName!C == C.mangleof); - static assert(mangledName!(C.value) == C.value.mangleof); - static assert(mangledName!(C.value)[$ - 12 .. $] == "5valueMFNdZi"); - static assert(mangledName!mangledName == "3std6traits11mangledName"); - static assert(mangledName!freeFunc == "_D3std6traits8freeFuncFAyaZv"); - int x; - // https://issues.dlang.org/show_bug.cgi?id=9148 - static if (is(typeof({ return x; }) : int delegate() pure)) - static assert(mangledName!((int a) { return a+x; }) == "DFNaNbNiNfiZi"); // pure nothrow @safe @nogc - else - static assert(mangledName!((int a) { return a+x; }) == "DFNbNiNfiZi"); // nothrow @safe @nnogc -} - -@system unittest -{ - // @system due to demangle - // Test for https://issues.dlang.org/show_bug.cgi?id=5718 - import std.demangle : demangle; - int foo; - auto foo_demangled = demangle(mangledName!foo); - assert(foo_demangled[0 .. 4] == "int " && foo_demangled[$-3 .. $] == "foo", - foo_demangled); - - void bar(); - auto bar_demangled = demangle(mangledName!bar); - assert(bar_demangled[0 .. 5] == "void " && bar_demangled[$-5 .. $] == "bar()"); -} - - - -// XXX Select & select should go to another module. (functional or algorithm?) - -/** -Aliases itself to `T[0]` if the boolean `condition` is `true` -and to `T[1]` otherwise. - */ -template Select(bool condition, T...) -if (T.length == 2) -{ - import std.meta : Alias; - alias Select = Alias!(T[!condition]); -} - -/// -@safe unittest -{ - // can select types - static assert(is(Select!(true, int, long) == int)); - static assert(is(Select!(false, int, long) == long)); - static struct Foo {} - static assert(is(Select!(false, const(int), const(Foo)) == const(Foo))); - - // can select symbols - int a = 1; - int b = 2; - alias selA = Select!(true, a, b); - alias selB = Select!(false, a, b); - assert(selA == 1); - assert(selB == 2); - - // can select (compile-time) expressions - enum val = Select!(false, -4, 9 - 6); - static assert(val == 3); -} - -/** -Select one of two functions to run via template parameter. - -Params: - cond = A `bool` which determines which function is run - a = The first function - b = The second function - -Returns: - `a` without evaluating `b` if `cond` is `true`. - Otherwise, returns `b` without evaluating `a`. - */ -A select(bool cond : true, A, B)(A a, lazy B b) { return a; } -/// Ditto -B select(bool cond : false, A, B)(lazy A a, B b) { return b; } - -/// -@safe unittest -{ - real run() { return 0; } - int fail() { assert(0); } - auto a = select!true(run(), fail()); - auto b = select!false(fail(), run()); - static assert(is(typeof(a) == real)); - static assert(is(typeof(b) == real)); -} - -/++ - Determine if a symbol has a given - $(DDSUBLINK spec/attribute, uda, user-defined attribute). - - See_Also: - $(LREF getUDAs) - +/ -enum hasUDA(alias symbol, alias attribute) = getUDAs!(symbol, attribute).length != 0; - -/// -@safe unittest -{ - enum E; - struct S {} - - @("alpha") int a; - static assert(hasUDA!(a, "alpha")); - static assert(!hasUDA!(a, S)); - static assert(!hasUDA!(a, E)); - - @(E) int b; - static assert(!hasUDA!(b, "alpha")); - static assert(!hasUDA!(b, S)); - static assert(hasUDA!(b, E)); - - @E int c; - static assert(!hasUDA!(c, "alpha")); - static assert(!hasUDA!(c, S)); - static assert(hasUDA!(c, E)); - - @(S, E) int d; - static assert(!hasUDA!(d, "alpha")); - static assert(hasUDA!(d, S)); - static assert(hasUDA!(d, E)); - - @S int e; - static assert(!hasUDA!(e, "alpha")); - static assert(hasUDA!(e, S)); - static assert(!hasUDA!(e, S())); - static assert(!hasUDA!(e, E)); - - @S() int f; - static assert(!hasUDA!(f, "alpha")); - static assert(hasUDA!(f, S)); - static assert(hasUDA!(f, S())); - static assert(!hasUDA!(f, E)); - - @(S, E, "alpha") int g; - static assert(hasUDA!(g, "alpha")); - static assert(hasUDA!(g, S)); - static assert(hasUDA!(g, E)); - - @(100) int h; - static assert(hasUDA!(h, 100)); - - struct Named { string name; } - - @Named("abc") int i; - static assert(hasUDA!(i, Named)); - static assert(hasUDA!(i, Named("abc"))); - static assert(!hasUDA!(i, Named("def"))); - - struct AttrT(T) - { - string name; - T value; - } - - @AttrT!int("answer", 42) int j; - static assert(hasUDA!(j, AttrT)); - static assert(hasUDA!(j, AttrT!int)); - static assert(!hasUDA!(j, AttrT!string)); - - @AttrT!string("hello", "world") int k; - static assert(hasUDA!(k, AttrT)); - static assert(!hasUDA!(k, AttrT!int)); - static assert(hasUDA!(k, AttrT!string)); - - struct FuncAttr(alias f) { alias func = f; } - static int fourtyTwo() { return 42; } - static size_t getLen(string s) { return s.length; } - - @FuncAttr!getLen int l; - static assert(hasUDA!(l, FuncAttr)); - static assert(!hasUDA!(l, FuncAttr!fourtyTwo)); - static assert(hasUDA!(l, FuncAttr!getLen)); - static assert(!hasUDA!(l, FuncAttr!fourtyTwo())); - static assert(!hasUDA!(l, FuncAttr!getLen())); - - @FuncAttr!getLen() int m; - static assert(hasUDA!(m, FuncAttr)); - static assert(!hasUDA!(m, FuncAttr!fourtyTwo)); - static assert(hasUDA!(m, FuncAttr!getLen)); - static assert(!hasUDA!(m, FuncAttr!fourtyTwo())); - static assert(hasUDA!(m, FuncAttr!getLen())); -} - -/++ - Gets the matching $(DDSUBLINK spec/attribute, uda, user-defined attributes) - from the given symbol. - - If the UDA is a type, then any UDAs of the same type on the symbol will - match. If the UDA is a template for a type, then any UDA which is an - instantiation of that template will match. And if the UDA is a value, - then any UDAs on the symbol which are equal to that value will match. - - See_Also: - $(LREF hasUDA) - +/ -template getUDAs(alias symbol, alias attribute) -{ - import std.meta : Filter; - - alias getUDAs = Filter!(isDesiredUDA!attribute, __traits(getAttributes, symbol)); -} - -/// -@safe unittest -{ - struct Attr - { - string name; - int value; - } - - @Attr("Answer", 42) int a; - static assert(getUDAs!(a, Attr).length == 1); - static assert(getUDAs!(a, Attr)[0].name == "Answer"); - static assert(getUDAs!(a, Attr)[0].value == 42); - - @(Attr("Answer", 42), "string", 9999) int b; - static assert(getUDAs!(b, Attr).length == 1); - static assert(getUDAs!(b, Attr)[0].name == "Answer"); - static assert(getUDAs!(b, Attr)[0].value == 42); - - @Attr("Answer", 42) @Attr("Pi", 3) int c; - static assert(getUDAs!(c, Attr).length == 2); - static assert(getUDAs!(c, Attr)[0].name == "Answer"); - static assert(getUDAs!(c, Attr)[0].value == 42); - static assert(getUDAs!(c, Attr)[1].name == "Pi"); - static assert(getUDAs!(c, Attr)[1].value == 3); - - static assert(getUDAs!(c, Attr("Answer", 42)).length == 1); - static assert(getUDAs!(c, Attr("Answer", 42))[0].name == "Answer"); - static assert(getUDAs!(c, Attr("Answer", 42))[0].value == 42); - - static assert(getUDAs!(c, Attr("Answer", 99)).length == 0); - - struct AttrT(T) - { - string name; - T value; - } - - @AttrT!uint("Answer", 42) @AttrT!int("Pi", 3) @AttrT int d; - static assert(getUDAs!(d, AttrT).length == 2); - static assert(getUDAs!(d, AttrT)[0].name == "Answer"); - static assert(getUDAs!(d, AttrT)[0].value == 42); - static assert(getUDAs!(d, AttrT)[1].name == "Pi"); - static assert(getUDAs!(d, AttrT)[1].value == 3); - - static assert(getUDAs!(d, AttrT!uint).length == 1); - static assert(getUDAs!(d, AttrT!uint)[0].name == "Answer"); - static assert(getUDAs!(d, AttrT!uint)[0].value == 42); - - static assert(getUDAs!(d, AttrT!int).length == 1); - static assert(getUDAs!(d, AttrT!int)[0].name == "Pi"); - static assert(getUDAs!(d, AttrT!int)[0].value == 3); - - struct SimpleAttr {} - - @SimpleAttr int e; - static assert(getUDAs!(e, SimpleAttr).length == 1); - static assert(is(getUDAs!(e, SimpleAttr)[0] == SimpleAttr)); - - @SimpleAttr() int f; - static assert(getUDAs!(f, SimpleAttr).length == 1); - static assert(is(typeof(getUDAs!(f, SimpleAttr)[0]) == SimpleAttr)); - - struct FuncAttr(alias f) { alias func = f; } - static int add42(int v) { return v + 42; } - static string concat(string l, string r) { return l ~ r; } - - @FuncAttr!add42 int g; - static assert(getUDAs!(g, FuncAttr).length == 1); - static assert(getUDAs!(g, FuncAttr)[0].func(5) == 47); - - static assert(getUDAs!(g, FuncAttr!add42).length == 1); - static assert(getUDAs!(g, FuncAttr!add42)[0].func(5) == 47); - - static assert(getUDAs!(g, FuncAttr!add42()).length == 0); - - static assert(getUDAs!(g, FuncAttr!concat).length == 0); - static assert(getUDAs!(g, FuncAttr!concat()).length == 0); - - @FuncAttr!add42() int h; - static assert(getUDAs!(h, FuncAttr).length == 1); - static assert(getUDAs!(h, FuncAttr)[0].func(5) == 47); - - static assert(getUDAs!(h, FuncAttr!add42).length == 1); - static assert(getUDAs!(h, FuncAttr!add42)[0].func(5) == 47); - - static assert(getUDAs!(h, FuncAttr!add42()).length == 1); - static assert(getUDAs!(h, FuncAttr!add42())[0].func(5) == 47); - - static assert(getUDAs!(h, FuncAttr!concat).length == 0); - static assert(getUDAs!(h, FuncAttr!concat()).length == 0); - - @("alpha") @(42) int i; - static assert(getUDAs!(i, "alpha").length == 1); - static assert(getUDAs!(i, "alpha")[0] == "alpha"); - - static assert(getUDAs!(i, 42).length == 1); - static assert(getUDAs!(i, 42)[0] == 42); - - static assert(getUDAs!(i, 'c').length == 0); -} - -private template isDesiredUDA(alias attribute) -{ - template isDesiredUDA(alias toCheck) - { - static if (is(typeof(attribute)) && !__traits(isTemplate, attribute)) - { - static if (__traits(compiles, toCheck == attribute)) - enum isDesiredUDA = toCheck == attribute; - else - enum isDesiredUDA = false; - } - else static if (is(typeof(toCheck))) - { - static if (__traits(isTemplate, attribute)) - enum isDesiredUDA = isInstanceOf!(attribute, typeof(toCheck)); - else - enum isDesiredUDA = is(typeof(toCheck) == attribute); - } - else static if (__traits(isTemplate, attribute)) - enum isDesiredUDA = isInstanceOf!(attribute, toCheck); - else - enum isDesiredUDA = is(toCheck == attribute); - } -} - -/** -Params: - symbol = The aggregate type or module to search - attribute = The user-defined attribute to search for - -Returns: - All symbols within `symbol` that have the given UDA `attribute`. - -Note: - This is not recursive; it will not search for symbols within symbols such as - nested structs or unions. - */ -template getSymbolsByUDA(alias symbol, alias attribute) -{ - alias membersWithUDA = getSymbolsByUDAImpl!(symbol, attribute, __traits(allMembers, symbol)); - - // if the symbol itself has the UDA, tack it on to the front of the list - static if (hasUDA!(symbol, attribute)) - alias getSymbolsByUDA = AliasSeq!(symbol, membersWithUDA); - else - alias getSymbolsByUDA = membersWithUDA; -} - -/// -@safe unittest -{ - enum Attr; - struct A - { - @Attr int a; - int b; - } - - static assert(getSymbolsByUDA!(A, Attr).length == 1); - static assert(hasUDA!(getSymbolsByUDA!(A, Attr)[0], Attr)); -} - -/// -@safe unittest -{ - enum Attr; - - static struct A - { - @Attr int a; - int b; - @Attr void doStuff() {} - void doOtherStuff() {} - static struct Inner - { - // Not found by getSymbolsByUDA - @Attr int c; - } - } - - // Finds both variables and functions with the attribute, but - // doesn't include the variables and functions without it. - static assert(getSymbolsByUDA!(A, Attr).length == 2); - // Can access attributes on the symbols returned by getSymbolsByUDA. - static assert(hasUDA!(getSymbolsByUDA!(A, Attr)[0], Attr)); - static assert(hasUDA!(getSymbolsByUDA!(A, Attr)[1], Attr)); -} - -/// Finds multiple attributes -@safe unittest -{ - static struct UDA { string name; } - - static struct B - { - @UDA("X") - int x; - @UDA("Y") - int y; - @(100) - int z; - } - - // Finds both UDA attributes. - static assert(getSymbolsByUDA!(B, UDA).length == 2); - // Finds one `100` attribute. - static assert(getSymbolsByUDA!(B, 100).length == 1); - // Can get the value of the UDA from the return value - static assert(getUDAs!(getSymbolsByUDA!(B, UDA)[0], UDA)[0].name == "X"); -} - -/// Checks for UDAs on the aggregate symbol itself -@safe unittest -{ - static struct UDA { string name; } - - @UDA("A") - static struct C - { - @UDA("B") - int d; - } - - static assert(getSymbolsByUDA!(C, UDA).length == 2); - static assert(getSymbolsByUDA!(C, UDA)[0].stringof == "C"); - static assert(getSymbolsByUDA!(C, UDA)[1].stringof == "d"); -} - -/// Finds nothing if there is no member with specific UDA -@safe unittest -{ - static struct UDA { string name; } - - static struct D - { - int x; - } - - static assert(getSymbolsByUDA!(D, UDA).length == 0); -} - -// https://issues.dlang.org/show_bug.cgi?id=18314 -@safe unittest -{ - enum attr1; - enum attr2; - - struct A - { - @attr1 - int n; - // Removed due to https://issues.dlang.org/show_bug.cgi?id=16206 - //@attr1 - //void foo()(string){} - @attr1 - void foo(); - @attr2 - void foo(int a); - } - - static assert(getSymbolsByUDA!(A, attr1).length == 2); - static assert(getSymbolsByUDA!(A, attr2).length == 1); -} - -// getSymbolsByUDA fails if type has private members -// https://issues.dlang.org/show_bug.cgi?id=15335 -@safe unittest -{ - // HasPrivateMembers has, well, private members, one of which has a UDA. - import std.internal.test.uda : Attr, HasPrivateMembers; - // Trying access to private member from another file therefore we do not have access - // for this otherwise we get deprecation warning - not visible from module - // This line is commented because `__traits(getMember)` should also consider - // private members; this is not currently the case, but the PR that - // fixes `__traits(getMember)` is blocked by this specific test. - //static assert(getSymbolsByUDA!(HasPrivateMembers, Attr).length == 1); - static assert(hasUDA!(getSymbolsByUDA!(HasPrivateMembers, Attr)[0], Attr)); -} - -// getSymbolsByUDA works with structs but fails with classes -// https://issues.dlang.org/show_bug.cgi?id=16387 -@safe unittest -{ - enum Attr; - class A - { - @Attr uint a; - } - - alias res = getSymbolsByUDA!(A, Attr); - static assert(res.length == 1); - static assert(res[0].stringof == "a"); -} - -// getSymbolsByUDA fails on AliasSeq members -// https://issues.dlang.org/show_bug.cgi?id=18884 -@safe unittest -{ - struct X - { - alias A = AliasSeq!(ulong, uint); - } - - static assert(is(getSymbolsByUDA!(X, X) == AliasSeq!())); -} - -// https://issues.dlang.org/show_bug.cgi?id=23776 -@safe pure nothrow @nogc unittest -{ - struct T - { - struct Tag {} - @Tag struct MyStructA {} - @Tag struct MyStructB {} - @Tag struct MyStructC {} - } - alias tcomponents = getSymbolsByUDA!(T, T.Tag); - static assert(tcomponents.length > 0); - - struct X - { - struct Tag {} - @Tag enum MyEnumA; - @Tag enum MyEnumB; - @Tag enum MyEnumC; - } - alias xcomponents = getSymbolsByUDA!(X, X.Tag); - static assert(xcomponents.length > 0); -} - -// getSymbolsByUDA produces wrong result if one of the symbols having the UDA is a function -// https://issues.dlang.org/show_bug.cgi?id=18624 -@safe unittest -{ - enum Attr; - struct A - { - @Attr void a(); - @Attr void a(int n); - void b(); - @Attr void c(); - } - - alias ola = __traits(getOverloads, A, "a"); - static assert(__traits(isSame, getSymbolsByUDA!(A, Attr), - AliasSeq!(ola[0], ola[1], A.c))); -} - -// getSymbolsByUDA no longer works on modules -// https://issues.dlang.org/show_bug.cgi?id=20054 -version (StdUnittest) -{ - @("Issue20054") - void issue20054() {} - static assert(__traits(compiles, getSymbolsByUDA!(mixin(__MODULE__), "Issue20054"))); -} - -private template isAliasSeq(Args...) -{ - static if (Args.length != 1) - enum isAliasSeq = true; - else - enum isAliasSeq = false; -} - -private template getSymbolsByUDAImpl(alias symbol, alias attribute, names...) -{ - import std.meta : Alias, AliasSeq, Filter; - static if (names.length == 0) - { - alias getSymbolsByUDAImpl = AliasSeq!(); - } - else - { - alias tail = getSymbolsByUDAImpl!(symbol, attribute, names[1 .. $]); - - // Filtering inaccessible members. - static if (!__traits(compiles, __traits(getMember, symbol, names[0]))) - { - alias getSymbolsByUDAImpl = tail; - } - else - { - alias member = __traits(getMember, symbol, names[0]); - - // Filtering not compiled members such as alias of basic types. - static if (isAliasSeq!member || - (isType!member && !isAggregateType!member && !is(member == enum))) - { - alias getSymbolsByUDAImpl = tail; - } - // If a symbol is overloaded, get UDAs for each overload (including templated overlaods). - else static if (__traits(getOverloads, symbol, names[0], true).length > 0) - { - enum hasSpecificUDA(alias member) = hasUDA!(member, attribute); - alias overloadsWithUDA = Filter!(hasSpecificUDA, __traits(getOverloads, symbol, names[0])); - alias getSymbolsByUDAImpl = AliasSeq!(overloadsWithUDA, tail); - } - else static if (hasUDA!(member, attribute)) - { - alias getSymbolsByUDAImpl = AliasSeq!(member, tail); - } - else - { - alias getSymbolsByUDAImpl = tail; - } - } - } -} - -/** - Returns: `true` iff all types `Ts` are the same. -*/ -enum bool allSameType(Ts...) = -{ - static foreach (T; Ts[Ts.length > 1 .. $]) - static if (!is(Ts[0] == T)) - if (__ctfe) // Dodge the "statement is unreachable" warning - return false; - return true; -}(); - -/// -@safe unittest -{ - static assert(allSameType!()); - static assert(allSameType!(int)); - static assert(allSameType!(int, int)); - static assert(allSameType!(int, int, int)); - static assert(allSameType!(float, float, float)); - static assert(!allSameType!(int, double)); - static assert(!allSameType!(int, float, double)); - static assert(!allSameType!(int, float, double, real)); - static assert(!allSameType!(short, int, float, double, real)); -} - -/** - Returns: `true` iff the type `T` can be tested in an $(D - if)-expression, that is if $(D if (pred(T.init)) {}) is compilable. -*/ -enum ifTestable(T, alias pred = a => a) = __traits(compiles, { if (pred(T.init)) {} }); - -/// -@safe unittest -{ - class C; - struct S1; - struct S2 - { - T opCast(T)() const; - } - - static assert( ifTestable!bool); - static assert( ifTestable!int); - static assert( ifTestable!(S1*)); - static assert( ifTestable!(typeof(null))); - static assert( ifTestable!(int[])); - static assert( ifTestable!(int[string])); - static assert( ifTestable!S2); - static assert( ifTestable!C); - static assert(!ifTestable!S1); -} - -@safe unittest -{ - import std.meta : AliasSeq, allSatisfy; - static assert(allSatisfy!(ifTestable, AliasSeq!(bool, int, float, double, string))); - struct BoolWrapper { bool value; } - static assert(!ifTestable!(bool, a => BoolWrapper(a))); -} - -/** - * Detect whether `X` is a type. Analogous to `is(X)`. This is useful when used - * in conjunction with other templates, e.g. `allSatisfy!(isType, X)`. - * - * Returns: - * `true` if `X` is a type, `false` otherwise - */ -enum isType(alias X) = is(X); - -/// -@safe unittest -{ - struct S { - template Test() {} - } - class C {} - interface I {} - union U {} - static assert(isType!int); - static assert(isType!string); - static assert(isType!(int[int])); - static assert(isType!S); - static assert(isType!C); - static assert(isType!I); - static assert(isType!U); - - int n; - void func(){} - static assert(!isType!n); - static assert(!isType!func); - static assert(!isType!(S.Test)); - static assert(!isType!(S.Test!())); -} - -/** - * Detect whether symbol or type `X` is a function. This is different that finding - * if a symbol is callable or satisfying `is(X == function)`, it finds - * specifically if the symbol represents a normal function declaration, i.e. - * not a delegate or a function pointer. - * - * Returns: - * `true` if `X` is a function, `false` otherwise - * - * See_Also: - * Use $(LREF isFunctionPointer) or $(LREF isDelegate) for detecting those types - * respectively. - */ -template isFunction(alias X) -{ - static if (is(typeof(&X) U : U*) && is(U == function) || - is(typeof(&X) U == delegate)) - { - // x is a (nested) function symbol. - enum isFunction = true; - } - else static if (is(X T)) - { - // x is a type. Take the type of it and examine. - enum isFunction = is(T == function); - } - else - enum isFunction = false; -} - -/// -@safe unittest -{ - static void func(){} - static assert(isFunction!func); - - struct S - { - void func(){} - } - static assert(isFunction!(S.func)); -} - -/** - * Detect whether `X` is a final method or class. - * - * Returns: - * `true` if `X` is final, `false` otherwise - */ -template isFinal(alias X) -{ - static if (is(X == class)) - enum isFinal = __traits(isFinalClass, X); - else static if (isFunction!X) - enum isFinal = __traits(isFinalFunction, X); - else - enum isFinal = false; -} - -/// -@safe unittest -{ - class C - { - void nf() {} - static void sf() {} - final void ff() {} - } - final class FC { } - - static assert(!isFinal!(C)); - static assert( isFinal!(FC)); - - static assert(!isFinal!(C.nf)); - static assert(!isFinal!(C.sf)); - static assert( isFinal!(C.ff)); -} - -/++ - + Determines whether the type `S` can be copied. - + If a type cannot be copied, then code such as `MyStruct x; auto y = x;` will fail to compile. - + Copying for structs can be disabled by using `@disable this(this)`. - + - + See also: $(DDSUBLINK spec/traits, isCopyable, `__traits(isCopyable, S)`) - + Params: - + S = The type to check. - + - + Returns: - + `true` if `S` can be copied. `false` otherwise. - +/ -enum isCopyable(S) = __traits(isCopyable, S); - -/// -@safe unittest -{ - struct S1 {} // Fine. Can be copied - struct S2 { this(this) {}} // Fine. Can be copied - struct S3 {@disable this(this); } // Not fine. Copying is disabled. - struct S4 {S3 s;} // Not fine. A field has copying disabled. - - class C1 {} - - static assert( isCopyable!S1); - static assert( isCopyable!S2); - static assert(!isCopyable!S3); - static assert(!isCopyable!S4); - - static assert(isCopyable!C1); - static assert(isCopyable!int); - static assert(isCopyable!(int[])); -} - -/** - * The parameter type deduced by IFTI when an expression of type T is passed as - * an argument to a template function. - * - * For all types other than pointer and slice types, `DeducedParameterType!T` - * is the same as `T`. For pointer and slice types, it is `T` with the - * outer-most layer of qualifiers dropped. - */ -package(std) template DeducedParameterType(T) -{ - static if (is(T == U*, U) || is(T == U[], U)) - alias DeducedParameterType = Unqual!T; - else - alias DeducedParameterType = T; -} - -@safe unittest -{ - static assert(is(DeducedParameterType!(const(int)) == const(int))); - static assert(is(DeducedParameterType!(const(int[2])) == const(int[2]))); - - static assert(is(DeducedParameterType!(const(int*)) == const(int)*)); - static assert(is(DeducedParameterType!(const(int[])) == const(int)[])); -} - -@safe unittest -{ - static struct NoCopy - { - @disable this(this); - } - - static assert(is(DeducedParameterType!NoCopy == NoCopy)); -} - -@safe unittest -{ - static assert(is(DeducedParameterType!(inout(int[])) == inout(int)[])); -} - -private auto dip1000Test(int x) {return *&x;} -// We don't use isSafe, because betterC client code needs to instantiate -// core.internal.array.comparison.__cmp in the client side. isSafe uses -// __cmp of two strings, so using it would instantate that here instead. That -// won't do because betterC compilations do not link the Phobos binary in. -package(std) enum dip1000Enabled - = is(typeof(&dip1000Test) : int function(int) @safe); diff --git a/phobos/std/typecons.d b/phobos/std/typecons.d deleted file mode 100644 index 3c425c7..0000000 --- a/phobos/std/typecons.d +++ /dev/null @@ -1,11040 +0,0 @@ -// Written in the D programming language. - -/** -This module implements a variety of type constructors, i.e., templates -that allow construction of new, useful general-purpose types. - -$(SCRIPT inhibitQuickIndex = 1;) -$(DIVC quickindex, -$(BOOKTABLE, -$(TR $(TH Category) $(TH Symbols)) -$(TR $(TD Tuple) $(TD - $(LREF isTuple) - $(LREF Tuple) - $(LREF tuple) - $(LREF reverse) -)) -$(TR $(TD Flags) $(TD - $(LREF BitFlags) - $(LREF isBitFlagEnum) - $(LREF Flag) - $(LREF No) - $(LREF Yes) -)) -$(TR $(TD Memory allocation) $(TD - $(LREF SafeRefCounted) - $(LREF safeRefCounted) - $(LREF RefCountedAutoInitialize) - $(LREF scoped) - $(LREF Unique) -)) -$(TR $(TD Code generation) $(TD - $(LREF AutoImplement) - $(LREF BlackHole) - $(LREF generateAssertTrap) - $(LREF generateEmptyFunction) - $(LREF WhiteHole) -)) -$(TR $(TD Nullable) $(TD - $(LREF Nullable) - $(LREF nullable) - $(LREF NullableRef) - $(LREF nullableRef) -)) -$(TR $(TD Proxies) $(TD - $(LREF Proxy) - $(LREF rebindable) - $(LREF Rebindable) - $(LREF ReplaceType) - $(LREF unwrap) - $(LREF wrap) -)) -$(TR $(TD Types) $(TD - $(LREF alignForSize) - $(LREF Ternary) - $(LREF Typedef) - $(LREF TypedefType) - $(LREF UnqualRef) -)) -)) - -Copyright: Copyright the respective authors, 2008- -License: $(HTTP boost.org/LICENSE_1_0.txt, Boost License 1.0). -Source: $(PHOBOSSRC std/typecons.d) -Authors: $(HTTP erdani.org, Andrei Alexandrescu), - $(HTTP bartoszmilewski.wordpress.com, Bartosz Milewski), - Don Clugston, - Shin Fujishiro, - Kenji Hara - */ -module std.typecons; - -import std.format.spec : singleSpec, FormatSpec; -import std.format.write : formatValue; -import std.meta : AliasSeq, allSatisfy; -import std.range.primitives : isOutputRange; -import std.traits; -import std.internal.attributes : betterC; - -/// Value tuples -@safe unittest -{ - alias Coord = Tuple!(int, "x", int, "y", int, "z"); - Coord c; - c[1] = 1; // access by index - c.z = 1; // access by given name - assert(c == Coord(0, 1, 1)); - - // names can be omitted, types can be mixed - alias DictEntry = Tuple!(string, int); - auto dict = DictEntry("seven", 7); - - // element types can be inferred - assert(tuple(2, 3, 4)[1] == 3); - // type inference works with names too - auto tup = tuple!("x", "y", "z")(2, 3, 4); - assert(tup.y == 3); -} - -/// Rebindable references to const and immutable objects -@safe unittest -{ - class Widget - { - void foo() const @safe {} - } - const w1 = new Widget, w2 = new Widget; - w1.foo(); - // w1 = w2 would not work; can't rebind const object - - auto r = Rebindable!(const Widget)(w1); - // invoke method as if r were a Widget object - r.foo(); - // rebind r to refer to another object - r = w2; -} - -/** -Encapsulates unique ownership of a resource. - -When a `Unique!T` goes out of scope it will call `destroy` -on the resource `T` that it manages, unless it is transferred. -One important consequence of `destroy` is that it will call the -destructor of the resource `T`. GC-managed references are not -guaranteed to be valid during a destructor call, but other members of -`T`, such as file handles or pointers to `malloc` memory, will -still be valid during the destructor call. This allows the resource -`T` to deallocate or clean up any non-GC resources. - -If it is desirable to persist a `Unique!T` outside of its original -scope, then it can be transferred. The transfer can be explicit, by -calling `release`, or implicit, when returning Unique from a -function. The resource `T` can be a polymorphic class object or -instance of an interface, in which case Unique behaves polymorphically -too. - -If `T` is a value type, then `Unique!T` will be implemented -as a reference to a `T`. -*/ -struct Unique(T) -{ -/** Represents a reference to `T`. Resolves to `T*` if `T` is a value type. */ -static if (is(T == class) || is(T == interface)) - alias RefT = T; -else - alias RefT = T*; - -public: - // Deferred in case we get some language support for checking uniqueness. - version (None) - /** - Allows safe construction of `Unique`. It creates the resource and - guarantees unique ownership of it (unless `T` publishes aliases of - `this`). - Note: Nested structs/classes cannot be created. - Params: - args = Arguments to pass to `T`'s constructor. - --- - static class C {} - auto u = Unique!(C).create(); - --- - */ - static Unique!T create(A...)(auto ref A args) - if (__traits(compiles, new T(args))) - { - Unique!T u; - u._p = new T(args); - return u; - } - - /** - Constructor that takes an rvalue. - It will ensure uniqueness, as long as the rvalue - isn't just a view on an lvalue (e.g., a cast). - Typical usage: - ---- - Unique!Foo f = new Foo; - ---- - */ - this(RefT p) - { - _p = p; - } - /** - Constructor that takes an lvalue. It nulls its source. - The nulling will ensure uniqueness as long as there - are no previous aliases to the source. - */ - this(ref RefT p) - { - _p = p; - p = null; - assert(p is null); - } - /** - Constructor that takes a `Unique` of a type that is convertible to our type. - - Typically used to transfer a `Unique` rvalue of derived type to - a `Unique` of base type. - Example: - --- - class C : Object {} - - Unique!C uc = new C; - Unique!Object uo = uc.release; - --- - */ - this(U)(Unique!U u) - if (is(u.RefT:RefT)) - { - _p = u._p; - u._p = null; - } - - /// Transfer ownership from a `Unique` of a type that is convertible to our type. - void opAssign(U)(Unique!U u) - if (is(u.RefT:RefT)) - { - // first delete any resource we own - destroy(this); - _p = u._p; - u._p = null; - } - - ~this() - { - if (_p !is null) - { - static if (is(T == class) || is(T == interface)) - destroy(_p); - else - destroy(*_p); - _p = null; - } - } - - /** Returns whether the resource exists. */ - @property bool isEmpty() const - { - return _p is null; - } - /** Transfer ownership to a `Unique` rvalue. Nullifies the current contents. - Same as calling std.algorithm.move on it. - */ - Unique release() - { - import std.algorithm.mutation : move; - return this.move; - } - - /** Forwards member access to contents. */ - mixin Proxy!_p; - - /** - Postblit operator is undefined to prevent the cloning of `Unique` objects. - */ - @disable this(this); - -private: - RefT _p; -} - -/// -@safe unittest -{ - struct S - { - int i; - this(int i){this.i = i;} - } - Unique!S produce() - { - // Construct a unique instance of S on the heap - Unique!S ut = new S(5); - // Implicit transfer of ownership - return ut; - } - // Borrow a unique resource by ref - void increment(ref Unique!S ur) - { - ur.i++; - } - void consume(Unique!S u2) - { - assert(u2.i == 6); - // Resource automatically deleted here - } - Unique!S u1; - assert(u1.isEmpty); - u1 = produce(); - assert(u1.i == 5); - increment(u1); - assert(u1.i == 6); - //consume(u1); // Error: u1 is not copyable - // Transfer ownership of the resource - consume(u1.release); - assert(u1.isEmpty); -} - -@safe unittest -{ - int i; - struct S - { - ~this() - { - // check context pointer still exists - dtor also called before GC frees struct - if (this.tupleof[0]) - i++; - } - } - { - Unique!S u = new S; - } - assert(i == 1); -} - -@system unittest -{ - // test conversion to base ref - int deleted = 0; - class C - { - ~this(){deleted++;} - } - // constructor conversion - Unique!Object u = Unique!C(new C); - static assert(!__traits(compiles, {u = new C;})); - assert(!u.isEmpty); - destroy(u); - assert(deleted == 1); - - Unique!C uc = new C; - static assert(!__traits(compiles, {Unique!Object uo = uc;})); - Unique!Object uo = new C; - // opAssign conversion, deleting uo resource first - uo = uc.release; - assert(uc.isEmpty); - assert(!uo.isEmpty); - assert(deleted == 2); -} - -@system unittest -{ - class Bar - { - ~this() { debug(Unique) writeln(" Bar destructor"); } - int val() const { return 4; } - } - alias UBar = Unique!(Bar); - UBar g(UBar u) - { - debug(Unique) writeln("inside g"); - return u.release; - } - auto ub = UBar(new Bar); - assert(!ub.isEmpty); - assert(ub.val == 4); - static assert(!__traits(compiles, {auto ub3 = g(ub);})); - auto ub2 = g(ub.release); - assert(ub.isEmpty); - assert(!ub2.isEmpty); -} - -@system unittest -{ - interface Bar - { - int val() const; - } - class BarImpl : Bar - { - static int count; - this() - { - count++; - } - ~this() - { - count--; - } - int val() const { return 4; } - } - alias UBar = Unique!Bar; - UBar g(UBar u) - { - debug(Unique) writeln("inside g"); - return u.release; - } - void consume(UBar u) - { - assert(u.val() == 4); - // Resource automatically deleted here - } - auto ub = UBar(new BarImpl); - assert(BarImpl.count == 1); - assert(!ub.isEmpty); - assert(ub.val == 4); - static assert(!__traits(compiles, {auto ub3 = g(ub);})); - auto ub2 = g(ub.release); - assert(ub.isEmpty); - assert(!ub2.isEmpty); - consume(ub2.release); - assert(BarImpl.count == 0); -} - -@safe unittest -{ - struct Foo - { - ~this() { } - int val() const { return 3; } - @disable this(this); - } - alias UFoo = Unique!(Foo); - - UFoo f(UFoo u) - { - return u.release; - } - - auto uf = UFoo(new Foo); - assert(!uf.isEmpty); - assert(uf.val == 3); - static assert(!__traits(compiles, {auto uf3 = f(uf);})); - auto uf2 = f(uf.release); - assert(uf.isEmpty); - assert(!uf2.isEmpty); -} - -// ensure Unique behaves correctly through const access paths -@system unittest -{ - struct Bar {int val;} - struct Foo - { - Unique!Bar bar = new Bar; - } - - Foo foo; - foo.bar.val = 6; - const Foo* ptr = &foo; - static assert(is(typeof(ptr) == const(Foo*))); - static assert(is(typeof(ptr.bar) == const(Unique!Bar))); - static assert(is(typeof(ptr.bar.val) == const(int))); - assert(ptr.bar.val == 6); - foo.bar.val = 7; - assert(ptr.bar.val == 7); -} - -// Used in Tuple.toString -private template sharedToString(alias field) -if (is(typeof(field) == shared)) -{ - static immutable sharedToString = typeof(field).stringof; -} - -private template sharedToString(alias field) -if (!is(typeof(field) == shared)) -{ - alias sharedToString = field; -} - -private enum bool distinctFieldNames(names...) = __traits(compiles, -{ - static foreach (__name; names) - static if (is(typeof(__name) : string)) - mixin("enum int " ~ __name ~ " = 0;"); -}); - -@safe unittest -{ - static assert(!distinctFieldNames!(string, "abc", string, "abc")); - static assert(distinctFieldNames!(string, "abc", int, "abd")); - static assert(!distinctFieldNames!(int, "abc", string, "abd", int, "abc")); - // https://issues.dlang.org/show_bug.cgi?id=19240 - static assert(!distinctFieldNames!(int, "int")); -} - - -// Parse (type,name) pairs (FieldSpecs) out of the specified -// arguments. Some fields would have name, others not. -private template parseSpecs(Specs...) -{ - static if (Specs.length == 0) - { - alias parseSpecs = AliasSeq!(); - } - else static if (is(Specs[0])) - { - static if (is(typeof(Specs[1]) : string)) - { - alias parseSpecs = - AliasSeq!(FieldSpec!(Specs[0 .. 2]), - parseSpecs!(Specs[2 .. $])); - } - else - { - alias parseSpecs = - AliasSeq!(FieldSpec!(Specs[0]), - parseSpecs!(Specs[1 .. $])); - } - } - else - { - static assert(0, "Attempted to instantiate Tuple with an " - ~"invalid argument: "~ Specs[0].stringof); - } -} - -private template FieldSpec(T, string s = "") -{ - alias Type = T; - alias name = s; -} - -// Used with staticMap. -private alias extractType(alias spec) = spec.Type; -private alias extractName(alias spec) = spec.name; -private template expandSpec(alias spec) -{ - static if (spec.name.length == 0) - alias expandSpec = AliasSeq!(spec.Type); - else - alias expandSpec = AliasSeq!(spec.Type, spec.name); -} - - -private enum areCompatibleTuples(Tup1, Tup2, string op) = - isTuple!(OriginalType!Tup2) && Tup1.Types.length == Tup2.Types.length && is(typeof( - (ref Tup1 tup1, ref Tup2 tup2) - { - static foreach (i; 0 .. Tup1.Types.length) - {{ - auto lhs = typeof(tup1.field[i]).init; - auto rhs = typeof(tup2.field[i]).init; - static if (op == "=") - lhs = rhs; - else - auto result = mixin("lhs "~op~" rhs"); - }} - })); - -private enum areBuildCompatibleTuples(Tup1, Tup2) = - isTuple!Tup2 && Tup1.Types.length == Tup2.Types.length && is(typeof( - { - static foreach (i; 0 .. Tup1.Types.length) - static assert(isBuildable!(Tup1.Types[i], Tup2.Types[i])); - })); - -// Returns `true` iff a `T` can be initialized from a `U`. -private enum isBuildable(T, U) = is(typeof( - { - U u = U.init; - T t = u; - })); -// Helper for partial instantiation -private template isBuildableFrom(U) -{ - enum isBuildableFrom(T) = isBuildable!(T, U); -} - -private enum hasCopyCtor(T) = __traits(hasCopyConstructor, T); - -// T is expected to be an instantiation of Tuple. -private template noMemberHasCopyCtor(T) -{ - import std.meta : anySatisfy; - enum noMemberHasCopyCtor = !anySatisfy!(hasCopyCtor, T.Types); -} - -/** -_Tuple of values, for example $(D Tuple!(int, string)) is a record that -stores an `int` and a `string`. `Tuple` can be used to bundle -values together, notably when returning multiple values from a -function. If `obj` is a `Tuple`, the individual members are -accessible with the syntax `obj[0]` for the first field, `obj[1]` -for the second, and so on. - -See_Also: $(LREF tuple). - -Params: - Specs = A list of types (and optionally, member names) that the `Tuple` contains. -*/ -template Tuple(Specs...) -if (distinctFieldNames!(Specs)) -{ - import std.meta : staticMap; - - alias fieldSpecs = parseSpecs!Specs; - - // Generates named fields as follows: - // alias name_0 = Identity!(field[0]); - // alias name_1 = Identity!(field[1]); - // : - // NOTE: field[k] is an expression (which yields a symbol of a - // variable) and can't be aliased directly. - enum injectNamedFields = () - { - string decl = ""; - static foreach (i, val; fieldSpecs) - {{ - immutable si = i.stringof; - decl ~= "alias _" ~ si ~ " = Identity!(field[" ~ si ~ "]);"; - if (val.name.length != 0) - { - decl ~= "alias " ~ val.name ~ " = _" ~ si ~ ";"; - } - }} - return decl; - }; - - // Returns Specs for a subtuple this[from .. to] preserving field - // names if any. - alias sliceSpecs(size_t from, size_t to) = - staticMap!(expandSpec, fieldSpecs[from .. to]); - - struct Tuple - { - /** - * The types of the `Tuple`'s components. - */ - alias Types = staticMap!(extractType, fieldSpecs); - - private alias _Fields = Specs; - - /// - static if (Specs.length == 0) @safe unittest - { - import std.meta : AliasSeq; - alias Fields = Tuple!(int, "id", string, float); - static assert(is(Fields.Types == AliasSeq!(int, string, float))); - } - - /** - * The names of the `Tuple`'s components. Unnamed fields have empty names. - */ - alias fieldNames = staticMap!(extractName, fieldSpecs); - - /// - static if (Specs.length == 0) @safe unittest - { - import std.meta : AliasSeq; - alias Fields = Tuple!(int, "id", string, float); - static assert(Fields.fieldNames == AliasSeq!("id", "", "")); - } - - /** - * Use `t.expand` for a `Tuple` `t` to expand it into its - * components. The result of `expand` acts as if the `Tuple`'s components - * were listed as a list of values. (Ordinarily, a `Tuple` acts as a - * single value.) - */ - Types expand; - mixin(injectNamedFields()); - - /// - static if (Specs.length == 0) @safe unittest - { - auto t1 = tuple(1, " hello ", 'a'); - assert(t1.toString() == `Tuple!(int, string, char)(1, " hello ", 'a')`); - - void takeSeveralTypes(int n, string s, bool b) - { - assert(n == 4 && s == "test" && b == false); - } - - auto t2 = tuple(4, "test", false); - //t.expand acting as a list of values - takeSeveralTypes(t2.expand); - } - - static if (is(Specs)) - { - // This is mostly to make t[n] work. - alias expand this; - } - else - { - @property - ref inout(Tuple!Types) _Tuple_super() inout @trusted - { - static foreach (i; 0 .. Types.length) // Rely on the field layout - { - static assert(typeof(return).init.tupleof[i].offsetof == - expand[i].offsetof); - } - return *cast(typeof(return)*) &(field[0]); - } - // This is mostly to make t[n] work. - alias _Tuple_super this; - } - - // backwards compatibility - alias field = expand; - - /** - * Constructor taking one value for each field. - * - * Params: - * values = A list of values that are either the same - * types as those given by the `Types` field - * of this `Tuple`, or can implicitly convert - * to those types. They must be in the same - * order as they appear in `Types`. - */ - static if (Types.length > 0) - { - this(Types values) - { - field[] = values[]; - } - } - - /// - static if (Specs.length == 0) @safe unittest - { - alias ISD = Tuple!(int, string, double); - auto tup = ISD(1, "test", 3.2); - assert(tup.toString() == `Tuple!(int, string, double)(1, "test", 3.2)`); - } - - /** - * Constructor taking a compatible array. - * - * Params: - * values = A compatible static array to build the `Tuple` from. - * Array slices are not supported. - */ - this(U, size_t n)(U[n] values) - if (n == Types.length && allSatisfy!(isBuildableFrom!U, Types)) - { - static foreach (i; 0 .. Types.length) - { - field[i] = values[i]; - } - } - - /// - static if (Specs.length == 0) @safe unittest - { - int[2] ints; - Tuple!(int, int) t = ints; - } - - /** - * Constructor taking a compatible `Tuple`. Two `Tuple`s are compatible - * $(B iff) they are both of the same length, and, for each type `T` on the - * left-hand side, the corresponding type `U` on the right-hand side can - * implicitly convert to `T`. - * - * Params: - * another = A compatible `Tuple` to build from. Its type must be - * compatible with the target `Tuple`'s type. - */ - this(U)(U another) - if (areBuildCompatibleTuples!(typeof(this), U) && - (noMemberHasCopyCtor!(typeof(this)) || !is(Unqual!U == Unqual!(typeof(this))))) - { - field[] = another.field[]; - } - - /// - static if (Specs.length == 0) @safe unittest - { - alias IntVec = Tuple!(int, int, int); - alias DubVec = Tuple!(double, double, double); - - IntVec iv = tuple(1, 1, 1); - - //Ok, int can implicitly convert to double - DubVec dv = iv; - //Error: double cannot implicitly convert to int - //IntVec iv2 = dv; - } - - /** - * Comparison for equality. Two `Tuple`s are considered equal - * $(B iff) they fulfill the following criteria: - * - * $(UL - * $(LI Each `Tuple` is the same length.) - * $(LI For each type `T` on the left-hand side and each type - * `U` on the right-hand side, values of type `T` can be - * compared with values of type `U`.) - * $(LI For each value `v1` on the left-hand side and each value - * `v2` on the right-hand side, the expression `v1 == v2` is - * true.)) - * - * Params: - * rhs = The `Tuple` to compare against. It must meeting the criteria - * for comparison between `Tuple`s. - * - * Returns: - * true if both `Tuple`s are equal, otherwise false. - */ - bool opEquals(R)(R rhs) - if (areCompatibleTuples!(typeof(this), R, "==")) - { - return field[] == rhs.field[]; - } - - /// ditto - bool opEquals(R)(R rhs) const - if (areCompatibleTuples!(typeof(this), R, "==")) - { - return field[] == rhs.field[]; - } - - /// ditto - bool opEquals(R...)(auto ref R rhs) - if (R.length > 1 && areCompatibleTuples!(typeof(this), Tuple!R, "==")) - { - static foreach (i; 0 .. Types.length) - if (field[i] != rhs[i]) - return false; - - return true; - } - - /// - static if (Specs.length == 0) @safe unittest - { - Tuple!(int, string) t1 = tuple(1, "test"); - Tuple!(double, string) t2 = tuple(1.0, "test"); - //Ok, int can be compared with double and - //both have a value of 1 - assert(t1 == t2); - } - - /** - * Comparison for ordering. - * - * Params: - * rhs = The `Tuple` to compare against. It must meet the criteria - * for comparison between `Tuple`s. - * - * Returns: - * For any values `v1` contained by the left-hand side tuple and any - * values `v2` contained by the right-hand side: - * - * 0 if `v1 == v2` for all members or the following value for the - * first position were the mentioned criteria is not satisfied: - * - * $(UL - * $(LI NaN, in case one of the operands is a NaN.) - * $(LI A negative number if the expression `v1 < v2` is true.) - * $(LI A positive number if the expression `v1 > v2` is true.)) - */ - auto opCmp(R)(R rhs) - if (areCompatibleTuples!(typeof(this), R, "<")) - { - static foreach (i; 0 .. Types.length) - { - if (field[i] != rhs.field[i]) - { - import std.math.traits : isNaN; - static if (isFloatingPoint!(Types[i])) - { - if (isNaN(field[i])) - return float.nan; - } - static if (isFloatingPoint!(typeof(rhs.field[i]))) - { - if (isNaN(rhs.field[i])) - return float.nan; - } - static if (is(typeof(field[i].opCmp(rhs.field[i]))) && - isFloatingPoint!(typeof(field[i].opCmp(rhs.field[i])))) - { - if (isNaN(field[i].opCmp(rhs.field[i]))) - return float.nan; - } - - return field[i] < rhs.field[i] ? -1 : 1; - } - } - return 0; - } - - /// ditto - auto opCmp(R)(R rhs) const - if (areCompatibleTuples!(typeof(this), R, "<")) - { - static foreach (i; 0 .. Types.length) - { - if (field[i] != rhs.field[i]) - { - import std.math.traits : isNaN; - static if (isFloatingPoint!(Types[i])) - { - if (isNaN(field[i])) - return float.nan; - } - static if (isFloatingPoint!(typeof(rhs.field[i]))) - { - if (isNaN(rhs.field[i])) - return float.nan; - } - static if (is(typeof(field[i].opCmp(rhs.field[i]))) && - isFloatingPoint!(typeof(field[i].opCmp(rhs.field[i])))) - { - if (isNaN(field[i].opCmp(rhs.field[i]))) - return float.nan; - } - - return field[i] < rhs.field[i] ? -1 : 1; - } - } - return 0; - } - - /** - The first `v1` for which `v1 > v2` is true determines - the result. This could lead to unexpected behaviour. - */ - static if (Specs.length == 0) @safe unittest - { - auto tup1 = tuple(1, 1, 1); - auto tup2 = tuple(1, 100, 100); - assert(tup1 < tup2); - - //Only the first result matters for comparison - tup1[0] = 2; - assert(tup1 > tup2); - } - - /** - Concatenate Tuples. - Tuple concatenation is only allowed if all named fields are distinct (no named field of this tuple occurs in `t` - and no named field of `t` occurs in this tuple). - - Params: - t = The `Tuple` to concatenate with - - Returns: A concatenation of this tuple and `t` - */ - auto opBinary(string op, T)(auto ref T t) - if (op == "~" && !(is(T : U[], U) && isTuple!U)) - { - static if (isTuple!T) - { - static assert(distinctFieldNames!(_Fields, T._Fields), - "Cannot concatenate tuples with duplicate fields: " ~ fieldNames.stringof ~ - " - " ~ T.fieldNames.stringof); - return Tuple!(_Fields, T._Fields)(expand, t.expand); - } - else - { - return Tuple!(_Fields, T)(expand, t); - } - } - - /// ditto - auto opBinaryRight(string op, T)(auto ref T t) - if (op == "~" && !(is(T : U[], U) && isTuple!U)) - { - static if (isTuple!T) - { - static assert(distinctFieldNames!(_Fields, T._Fields), - "Cannot concatenate tuples with duplicate fields: " ~ T.stringof ~ - " - " ~ fieldNames.fieldNames.stringof); - return Tuple!(T._Fields, _Fields)(t.expand, expand); - } - else - { - return Tuple!(T, _Fields)(t, expand); - } - } - - /** - * Assignment from another `Tuple`. - * - * Params: - * rhs = The source `Tuple` to assign from. Each element of the - * source `Tuple` must be implicitly assignable to each - * respective element of the target `Tuple`. - */ - ref Tuple opAssign(R)(auto ref R rhs) - if (areCompatibleTuples!(typeof(this), R, "=")) - { - import std.algorithm.mutation : swap; - - static if (is(R == Tuple!Types) && !__traits(isRef, rhs) && isTuple!R) - { - if (__ctfe) - { - // Cannot use swap at compile time - field[] = rhs.field[]; - } - else - { - // Use swap-and-destroy to optimize rvalue assignment - swap!(Tuple!Types)(this, rhs); - } - } - else - { - // Do not swap; opAssign should be called on the fields. - field[] = rhs.field[]; - } - return this; - } - - /** - * Renames the elements of a $(LREF Tuple). - * - * `rename` uses the passed `names` and returns a new - * $(LREF Tuple) using these names, with the content - * unchanged. - * If fewer names are passed than there are members - * of the $(LREF Tuple) then those trailing members are unchanged. - * An empty string will remove the name for that member. - * It is an compile-time error to pass more names than - * there are members of the $(LREF Tuple). - */ - ref rename(names...)() inout return - if (names.length == 0 || allSatisfy!(isSomeString, typeof(names))) - { - import std.algorithm.comparison : equal; - // to circumvent https://issues.dlang.org/show_bug.cgi?id=16418 - static if (names.length == 0 || equal([names], [fieldNames])) - return this; - else - { - enum nT = Types.length; - enum nN = names.length; - static assert(nN <= nT, "Cannot have more names than tuple members"); - alias allNames = AliasSeq!(names, fieldNames[nN .. $]); - - import std.meta : Alias, aliasSeqOf; - - template GetItem(size_t idx) - { - import std.array : empty; - static if (idx < nT) - alias GetItem = Alias!(Types[idx]); - else static if (allNames[idx - nT].empty) - alias GetItem = AliasSeq!(); - else - alias GetItem = Alias!(allNames[idx - nT]); - } - - import std.range : roundRobin, iota; - alias NewTupleT = Tuple!(staticMap!(GetItem, aliasSeqOf!( - roundRobin(iota(nT), iota(nT, 2*nT))))); - return *(() @trusted => cast(NewTupleT*)&this)(); - } - } - - /// - static if (Specs.length == 0) @safe unittest - { - auto t0 = tuple(4, "hello"); - - auto t0Named = t0.rename!("val", "tag"); - assert(t0Named.val == 4); - assert(t0Named.tag == "hello"); - - Tuple!(float, "dat", size_t[2], "pos") t1; - t1.pos = [2, 1]; - auto t1Named = t1.rename!"height"; - t1Named.height = 3.4f; - assert(t1Named.height == 3.4f); - assert(t1Named.pos == [2, 1]); - t1Named.rename!"altitude".altitude = 5; - assert(t1Named.height == 5); - - Tuple!(int, "a", int, int, "c") t2; - t2 = tuple(3,4,5); - auto t2Named = t2.rename!("", "b"); - // "a" no longer has a name - static assert(!__traits(hasMember, typeof(t2Named), "a")); - assert(t2Named[0] == 3); - assert(t2Named.b == 4); - assert(t2Named.c == 5); - - // not allowed to specify more names than the tuple has members - static assert(!__traits(compiles, t2.rename!("a","b","c","d"))); - - // use it in a range pipeline - import std.range : iota, zip; - import std.algorithm.iteration : map, sum; - auto res = zip(iota(1, 4), iota(10, 13)) - .map!(t => t.rename!("a", "b")) - .map!(t => t.a * t.b) - .sum; - assert(res == 68); - - const tup = Tuple!(int, "a", int, "b")(2, 3); - const renamed = tup.rename!("c", "d"); - assert(renamed.c + renamed.d == 5); - } - - /** - * Overload of $(LREF _rename) that takes an associative array - * `translate` as a template parameter, where the keys are - * either the names or indices of the members to be changed - * and the new names are the corresponding values. - * Every key in `translate` must be the name of a member of the - * $(LREF tuple). - * The same rules for empty strings apply as for the variadic - * template overload of $(LREF _rename). - */ - ref rename(alias translate)() inout - if (is(typeof(translate) : V[K], V, K) && isSomeString!V && - (isSomeString!K || is(K : size_t))) - { - import std.meta : aliasSeqOf; - import std.range : ElementType; - static if (isSomeString!(ElementType!(typeof(translate.keys)))) - { - { - import std.conv : to; - import std.algorithm.iteration : filter; - import std.algorithm.searching : canFind; - enum notFound = translate.keys - .filter!(k => fieldNames.canFind(k) == -1); - static assert(notFound.empty, "Cannot find members " - ~ notFound.to!string ~ " in type " - ~ typeof(this).stringof); - } - return this.rename!(aliasSeqOf!( - { - import std.array : empty; - auto names = [fieldNames]; - foreach (ref n; names) - if (!n.empty) - if (auto p = n in translate) - n = *p; - return names; - }())); - } - else - { - { - import std.algorithm.iteration : filter; - import std.conv : to; - enum invalid = translate.keys. - filter!(k => k < 0 || k >= this.length); - static assert(invalid.empty, "Indices " ~ invalid.to!string - ~ " are out of bounds for tuple with length " - ~ this.length.to!string); - } - return this.rename!(aliasSeqOf!( - { - auto names = [fieldNames]; - foreach (k, v; translate) - names[k] = v; - return names; - }())); - } - } - - /// - static if (Specs.length == 0) @safe unittest - { - //replacing names by their current name - - Tuple!(float, "dat", size_t[2], "pos") t1; - t1.pos = [2, 1]; - auto t1Named = t1.rename!(["dat": "height"]); - t1Named.height = 3.4; - assert(t1Named.pos == [2, 1]); - t1Named.rename!(["height": "altitude"]).altitude = 5; - assert(t1Named.height == 5); - - Tuple!(int, "a", int, "b") t2; - t2 = tuple(3, 4); - auto t2Named = t2.rename!(["a": "b", "b": "c"]); - assert(t2Named.b == 3); - assert(t2Named.c == 4); - - const t3 = Tuple!(int, "a", int, "b")(3, 4); - const t3Named = t3.rename!(["a": "b", "b": "c"]); - assert(t3Named.b == 3); - assert(t3Named.c == 4); - } - - /// - static if (Specs.length == 0) @system unittest - { - //replace names by their position - - Tuple!(float, "dat", size_t[2], "pos") t1; - t1.pos = [2, 1]; - auto t1Named = t1.rename!([0: "height"]); - t1Named.height = 3.4; - assert(t1Named.pos == [2, 1]); - t1Named.rename!([0: "altitude"]).altitude = 5; - assert(t1Named.height == 5); - - Tuple!(int, "a", int, "b", int, "c") t2; - t2 = tuple(3, 4, 5); - auto t2Named = t2.rename!([0: "c", 2: "a"]); - assert(t2Named.a == 5); - assert(t2Named.b == 4); - assert(t2Named.c == 3); - } - - static if (Specs.length == 0) @system unittest - { - //check that empty translations work fine - enum string[string] a0 = null; - enum string[int] a1 = null; - Tuple!(float, "a", float, "b") t0; - - auto t1 = t0.rename!a0; - - t1.a = 3; - t1.b = 4; - auto t2 = t0.rename!a1; - t2.a = 3; - t2.b = 4; - auto t3 = t0.rename; - t3.a = 3; - t3.b = 4; - } - - /** - * Takes a slice by-reference of this `Tuple`. - * - * Params: - * from = A `size_t` designating the starting position of the slice. - * to = A `size_t` designating the ending position (exclusive) of the slice. - * - * Returns: - * A new `Tuple` that is a slice from `[from, to$(RPAREN)` of the original. - * It has the same types and values as the range `[from, to$(RPAREN)` in - * the original. - */ - @property - ref inout(Tuple!(sliceSpecs!(from, to))) slice(size_t from, size_t to)() inout @trusted - if (from <= to && to <= Types.length) - { - static assert( - (typeof(this).alignof % typeof(return).alignof == 0) && - (expand[from].offsetof % typeof(return).alignof == 0), - "Slicing by reference is impossible because of an alignment mistmatch" ~ - " (See https://issues.dlang.org/show_bug.cgi?id=15645)."); - - return *cast(typeof(return)*) &(field[from]); - } - - /// - static if (Specs.length == 0) @safe unittest - { - Tuple!(int, string, float, double) a; - a[1] = "abc"; - a[2] = 4.5; - auto s = a.slice!(1, 3); - static assert(is(typeof(s) == Tuple!(string, float))); - assert(s[0] == "abc" && s[1] == 4.5); - - // https://issues.dlang.org/show_bug.cgi?id=15645 - Tuple!(int, short, bool, double) b; - static assert(!__traits(compiles, b.slice!(2, 4))); - } - - /** - Creates a hash of this `Tuple`. - - Returns: - A `size_t` representing the hash of this `Tuple`. - */ - size_t toHash() const nothrow @safe - { - size_t h = 0; - static foreach (i, T; Types) - {{ - static if (__traits(compiles, h = .hashOf(field[i]))) - const k = .hashOf(field[i]); - else - { - // Workaround for when .hashOf is not both @safe and nothrow. - static if (is(T : shared U, U) && __traits(compiles, (U* a) nothrow @safe => .hashOf(*a)) - && !__traits(hasMember, T, "toHash")) - // BUG: Improperly casts away `shared`! - const k = .hashOf(*(() @trusted => cast(U*) &field[i])()); - else - // BUG: Improperly casts away `shared`! - const k = typeid(T).getHash((() @trusted => cast(const void*) &field[i])()); - } - static if (i == 0) - h = k; - else - // As in boost::hash_combine - // https://www.boost.org/doc/libs/1_55_0/doc/html/hash/reference.html#boost.hash_combine - h ^= k + 0x9e3779b9 + (h << 6) + (h >>> 2); - }} - return h; - } - - /** - * Converts to string. - * - * Returns: - * The string representation of this `Tuple`. - */ - string toString()() const - { - import std.array : appender; - auto app = appender!string(); - this.toString((const(char)[] chunk) => app ~= chunk); - return app.data; - } - - import std.format.spec : FormatSpec; - - /** - * Formats `Tuple` with either `%s`, `%(inner%)` or `%(inner%|sep%)`. - * - * $(TABLE2 Formats supported by Tuple, - * $(THEAD Format, Description) - * $(TROW $(P `%s`), $(P Format like `Tuple!(types)(elements formatted with %s each)`.)) - * $(TROW $(P `%(inner%)`), $(P The format `inner` is applied the expanded `Tuple`$(COMMA) so - * it may contain as many formats as the `Tuple` has fields.)) - * $(TROW $(P `%(inner%|sep%)`), $(P The format `inner` is one format$(COMMA) that is applied - * on all fields of the `Tuple`. The inner format must be compatible to all - * of them.))) - * - * Params: - * sink = A `char` accepting delegate - * fmt = A $(REF FormatSpec, std,format) - */ - void toString(DG)(scope DG sink) const - { - auto f = FormatSpec!char(); - toString(sink, f); - } - - /// ditto - void toString(DG, Char)(scope DG sink, scope const ref FormatSpec!Char fmt) const - { - import std.format : format, FormatException; - import std.format.write : formattedWrite; - import std.range : only; - if (fmt.nested) - { - if (fmt.sep) - { - foreach (i, Type; Types) - { - static if (i > 0) - { - sink(fmt.sep); - } - // TODO: Change this once formattedWrite() works for shared objects. - static if (is(Type == class) && is(Type == shared)) - { - sink(Type.stringof); - } - else - { - formattedWrite(sink, fmt.nested, this.field[i]); - } - } - } - else - { - formattedWrite(sink, fmt.nested, staticMap!(sharedToString, this.expand)); - } - } - else if (fmt.spec == 's') - { - enum header = Unqual!(typeof(this)).stringof ~ "(", - footer = ")", - separator = ", "; - sink(header); - foreach (i, Type; Types) - { - static if (i > 0) - { - sink(separator); - } - // TODO: Change this once format() works for shared objects. - static if (is(Type == class) && is(Type == shared)) - { - sink(Type.stringof); - } - else - { - sink(format!("%(%s%)")(only(field[i]))); - } - } - sink(footer); - } - else - { - const spec = fmt.spec; - throw new FormatException( - "Expected '%s' or '%(...%)' or '%(...%|...%)' format specifier for type '" ~ - Unqual!(typeof(this)).stringof ~ "', not '%" ~ spec ~ "'."); - } - } - - /// - static if (Specs.length == 0) @safe unittest - { - import std.format : format; - - Tuple!(int, double)[3] tupList = [ tuple(1, 1.0), tuple(2, 4.0), tuple(3, 9.0) ]; - - // Default format - assert(format("%s", tuple("a", 1)) == `Tuple!(string, int)("a", 1)`); - - // One Format for each individual component - assert(format("%(%#x v %.4f w %#x%)", tuple(1, 1.0, 10)) == `0x1 v 1.0000 w 0xa`); - assert(format( "%#x v %.4f w %#x" , tuple(1, 1.0, 10).expand) == `0x1 v 1.0000 w 0xa`); - - // One Format for all components - assert(format("%(>%s<%| & %)", tuple("abc", 1, 2.3, [4, 5])) == `>abc< & >1< & >2.3< & >[4, 5]<`); - - // Array of Tuples - assert(format("%(%(f(%d) = %.1f%); %)", tupList) == `f(1) = 1.0; f(2) = 4.0; f(3) = 9.0`); - } - - /// - static if (Specs.length == 0) @safe unittest - { - import std.exception : assertThrown; - import std.format : format, FormatException; - - // Error: %( %) missing. - assertThrown!FormatException( - format("%d, %f", tuple(1, 2.0)) == `1, 2.0` - ); - - // Error: %( %| %) missing. - assertThrown!FormatException( - format("%d", tuple(1, 2)) == `1, 2` - ); - - // Error: %d inadequate for double - assertThrown!FormatException( - format("%(%d%|, %)", tuple(1, 2.0)) == `1, 2.0` - ); - } - } -} - -/// -@safe unittest -{ - Tuple!(int, int) point; - // assign coordinates - point[0] = 5; - point[1] = 6; - // read coordinates - auto x = point[0]; - auto y = point[1]; -} - -/** - `Tuple` members can be named. It is legal to mix named and unnamed - members. The method above is still applicable to all fields. - */ -@safe unittest -{ - alias Entry = Tuple!(int, "index", string, "value"); - Entry e; - e.index = 4; - e.value = "Hello"; - assert(e[1] == "Hello"); - assert(e[0] == 4); -} - -/** - A `Tuple` with named fields is a distinct type from a `Tuple` with unnamed - fields, i.e. each naming imparts a separate type for the `Tuple`. Two - `Tuple`s differing in naming only are still distinct, even though they - might have the same structure. - */ -@safe unittest -{ - Tuple!(int, "x", int, "y") point1; - Tuple!(int, int) point2; - assert(!is(typeof(point1) == typeof(point2))); -} - -/// Use tuples as ranges -@safe unittest -{ - import std.algorithm.iteration : sum; - import std.range : only; - auto t = tuple(1, 2); - assert(t.expand.only.sum == 3); -} - -// https://issues.dlang.org/show_bug.cgi?id=4582 -@safe unittest -{ - static assert(!__traits(compiles, Tuple!(string, "id", int, "id"))); - static assert(!__traits(compiles, Tuple!(string, "str", int, "i", string, "str", float))); -} - -/// Concatenate tuples -@safe unittest -{ - import std.meta : AliasSeq; - auto t = tuple(1, "2") ~ tuple(ushort(42), true); - static assert(is(t.Types == AliasSeq!(int, string, ushort, bool))); - assert(t[1] == "2"); - assert(t[2] == 42); - assert(t[3] == true); -} - -// https://issues.dlang.org/show_bug.cgi?id=14637 -// tuple concat -@safe unittest -{ - auto t = tuple!"foo"(1.0) ~ tuple!"bar"("3"); - static assert(is(t.Types == AliasSeq!(double, string))); - static assert(t.fieldNames == tuple("foo", "bar")); - assert(t.foo == 1.0); - assert(t.bar == "3"); -} - -// https://issues.dlang.org/show_bug.cgi?id=18824 -// tuple concat -@safe unittest -{ - alias Type = Tuple!(int, string); - Type[] arr; - auto t = tuple(2, "s"); - // Test opBinaryRight - arr = arr ~ t; - // Test opBinary - arr = t ~ arr; - static assert(is(typeof(arr) == Type[])); - immutable Type[] b; - auto c = b ~ t; - static assert(is(typeof(c) == immutable(Type)[])); -} - -// tuple concat -@safe unittest -{ - auto t = tuple!"foo"(1.0) ~ "3"; - static assert(is(t.Types == AliasSeq!(double, string))); - assert(t.foo == 1.0); - assert(t[1]== "3"); -} - -// tuple concat -@safe unittest -{ - auto t = "2" ~ tuple!"foo"(1.0); - static assert(is(t.Types == AliasSeq!(string, double))); - assert(t.foo == 1.0); - assert(t[0]== "2"); -} - -// tuple concat -@safe unittest -{ - auto t = "2" ~ tuple!"foo"(1.0) ~ tuple(42, 3.0f) ~ real(1) ~ "a"; - static assert(is(t.Types == AliasSeq!(string, double, int, float, real, string))); - assert(t.foo == 1.0); - assert(t[0] == "2"); - assert(t[1] == 1.0); - assert(t[2] == 42); - assert(t[3] == 3.0f); - assert(t[4] == 1.0); - assert(t[5] == "a"); -} - -// ensure that concatenation of tuples with non-distinct fields is forbidden -@safe unittest -{ - static assert(!__traits(compiles, - tuple!("a")(0) ~ tuple!("a")("1"))); - static assert(!__traits(compiles, - tuple!("a", "b")(0, 1) ~ tuple!("b", "a")("3", 1))); - static assert(!__traits(compiles, - tuple!("a")(0) ~ tuple!("b", "a")("3", 1))); - static assert(!__traits(compiles, - tuple!("a1", "a")(1.0, 0) ~ tuple!("a2", "a")("3", 0))); -} - -// Ensure that Tuple comparison with non-const opEquals works -@safe unittest -{ - static struct Bad - { - int a; - - bool opEquals(Bad b) - { - return a == b.a; - } - } - - auto t = Tuple!(int, Bad, string)(1, Bad(1), "asdf"); - - //Error: mutable method Bad.opEquals is not callable using a const object - assert(t == AliasSeq!(1, Bad(1), "asdf")); -} - -// Ensure Tuple.toHash works -@safe unittest -{ - Tuple!(int, int) point; - assert(point.toHash == typeof(point).init.toHash); - assert(tuple(1, 2) != point); - assert(tuple(1, 2) == tuple(1, 2)); - point[0] = 1; - assert(tuple(1, 2) != point); - point[1] = 2; - assert(tuple(1, 2) == point); -} - -@safe @betterC unittest -{ - auto t = tuple(1, 2); - assert(t == tuple(1, 2)); - auto t3 = tuple(1, 'd'); -} - -// https://issues.dlang.org/show_bug.cgi?id=20850 -// Assignment to enum tuple -@safe unittest -{ - enum T : Tuple!(int*) { a = T(null) } - T t; - t = T.a; -} - -// https://issues.dlang.org/show_bug.cgi?id=13663 -@safe unittest -{ - auto t = tuple(real.nan); - assert(!(t > t)); - assert(!(t < t)); - assert(!(t == t)); -} - -@safe unittest -{ - struct S - { - float opCmp(S s) { return float.nan; } - bool opEquals(S s) { return false; } - } - - auto t = tuple(S()); - assert(!(t > t)); - assert(!(t < t)); - assert(!(t == t)); -} - -// https://issues.dlang.org/show_bug.cgi?id=8015 -@safe unittest -{ - struct MyStruct - { - string str; - @property string toStr() - { - return str; - } - alias toStr this; - } - - Tuple!(MyStruct) t; -} - -// https://issues.dlang.org/show_bug.cgi?id=24465 -@safe unittest -{ - { - static struct S - { - this(ref return scope inout(S) rhs) scope @trusted inout pure nothrow {} - } - - static void foo(Tuple!S) - { - } - - Tuple!S t; - foo(t); - - auto t2 = Tuple!S(t); - } - - { - static struct S {} - Tuple!S t; - auto t2 = Tuple!S(t); - - // This can't be done if Tuple has a copy constructor, because it's not - // allowed to have an rvalue constructor at that point, and the - // compiler doesn't to something intelligent like transform it into a - // move instead. However, it has been legal with Tuple for a while - // (maybe even since it was first added) when the type doesn't have a - // copy constructor, so this is testing to make sure that the fix to - // make copy constructors work doesn't mess up the rvalue constructor - // when none of the Tuple's members have copy constructors. - auto t3 = Tuple!S(Tuple!S.init); - } -} - -/** - Creates a copy of a $(LREF Tuple) with its fields in _reverse order. - - Params: - t = The `Tuple` to copy. - - Returns: - A new `Tuple`. - */ -auto reverse(T)(T t) -if (isTuple!T) -{ - import std.meta : Reverse; - // @@@BUG@@@ Cannot be an internal function due to forward reference issues. - - // @@@BUG@@@ 9929 Need 'this' when calling template with expanded tuple - // return tuple(Reverse!(t.expand)); - - ReverseTupleType!T result; - auto tup = t.expand; - result.expand = Reverse!tup; - return result; -} - -/// -@safe unittest -{ - auto tup = tuple(1, "2"); - assert(tup.reverse == tuple("2", 1)); -} - -/* Get a Tuple type with the reverse specification of Tuple T. */ -private template ReverseTupleType(T) -if (isTuple!T) -{ - static if (is(T : Tuple!A, A...)) - alias ReverseTupleType = Tuple!(ReverseTupleSpecs!A); -} - -/* Reverse the Specs of a Tuple. */ -private template ReverseTupleSpecs(T...) -{ - static if (T.length > 1) - { - static if (is(typeof(T[$-1]) : string)) - { - alias ReverseTupleSpecs = AliasSeq!(T[$-2], T[$-1], ReverseTupleSpecs!(T[0 .. $-2])); - } - else - { - alias ReverseTupleSpecs = AliasSeq!(T[$-1], ReverseTupleSpecs!(T[0 .. $-1])); - } - } - else - { - alias ReverseTupleSpecs = T; - } -} - -// ensure that internal Tuple unittests are compiled -@safe unittest -{ - Tuple!() t; -} - -@safe unittest -{ - import std.conv; - { - Tuple!(int, "a", int, "b") nosh; - static assert(nosh.length == 2); - nosh.a = 5; - nosh.b = 6; - assert(nosh.a == 5); - assert(nosh.b == 6); - } - { - Tuple!(short, double) b; - static assert(b.length == 2); - b[1] = 5; - auto a = Tuple!(int, real)(b); - assert(a[0] == 0 && a[1] == 5); - a = Tuple!(int, real)(1, 2); - assert(a[0] == 1 && a[1] == 2); - auto c = Tuple!(int, "a", double, "b")(a); - assert(c[0] == 1 && c[1] == 2); - } - { - Tuple!(int, real) nosh; - nosh[0] = 5; - nosh[1] = 0; - assert(nosh[0] == 5 && nosh[1] == 0); - assert(nosh.to!string == "Tuple!(int, real)(5, 0)", nosh.to!string); - Tuple!(int, int) yessh; - nosh = yessh; - } - { - class A {} - Tuple!(int, shared A) nosh; - nosh[0] = 5; - assert(nosh[0] == 5 && nosh[1] is null); - assert(nosh.to!string == "Tuple!(int, shared(A))(5, shared(A))"); - } - { - Tuple!(int, string) t; - t[0] = 10; - t[1] = "str"; - assert(t[0] == 10 && t[1] == "str"); - assert(t.to!string == `Tuple!(int, string)(10, "str")`, t.to!string); - } - { - Tuple!(int, "a", double, "b") x; - static assert(x.a.offsetof == x[0].offsetof); - static assert(x.b.offsetof == x[1].offsetof); - x.b = 4.5; - x.a = 5; - assert(x[0] == 5 && x[1] == 4.5); - assert(x.a == 5 && x.b == 4.5); - } - // indexing - { - Tuple!(int, real) t; - static assert(is(typeof(t[0]) == int)); - static assert(is(typeof(t[1]) == real)); - int* p0 = &t[0]; - real* p1 = &t[1]; - t[0] = 10; - t[1] = -200.0L; - assert(*p0 == t[0]); - assert(*p1 == t[1]); - } - // slicing - { - Tuple!(int, "x", real, "y", double, "z", string) t; - t[0] = 10; - t[1] = 11; - t[2] = 12; - t[3] = "abc"; - auto a = t.slice!(0, 3); - assert(a.length == 3); - assert(a.x == t.x); - assert(a.y == t.y); - assert(a.z == t.z); - auto b = t.slice!(2, 4); - assert(b.length == 2); - assert(b.z == t.z); - assert(b[1] == t[3]); - } - // nesting - { - Tuple!(Tuple!(int, real), Tuple!(string, "s")) t; - static assert(is(typeof(t[0]) == Tuple!(int, real))); - static assert(is(typeof(t[1]) == Tuple!(string, "s"))); - static assert(is(typeof(t[0][0]) == int)); - static assert(is(typeof(t[0][1]) == real)); - static assert(is(typeof(t[1].s) == string)); - t[0] = tuple(10, 20.0L); - t[1].s = "abc"; - assert(t[0][0] == 10); - assert(t[0][1] == 20.0L); - assert(t[1].s == "abc"); - } - // non-POD - { - static struct S - { - int count; - this(this) { ++count; } - ~this() { --count; } - void opAssign(S rhs) { count = rhs.count; } - } - Tuple!(S, S) ss; - Tuple!(S, S) ssCopy = ss; - assert(ssCopy[0].count == 1); - assert(ssCopy[1].count == 1); - ssCopy[1] = ssCopy[0]; - assert(ssCopy[1].count == 2); - } - // https://issues.dlang.org/show_bug.cgi?id=2800 - { - static struct R - { - Tuple!(int, int) _front; - @property ref Tuple!(int, int) front() return { return _front; } - @property bool empty() { return _front[0] >= 10; } - void popFront() { ++_front[0]; } - } - foreach (a; R()) - { - static assert(is(typeof(a) == Tuple!(int, int))); - assert(0 <= a[0] && a[0] < 10); - assert(a[1] == 0); - } - } - // Construction with compatible elements - { - auto t1 = Tuple!(int, double)(1, 1); - - // https://issues.dlang.org/show_bug.cgi?id=8702 - auto t8702a = tuple(tuple(1)); - auto t8702b = Tuple!(Tuple!(int))(Tuple!(int)(1)); - } - // Construction with compatible tuple - { - Tuple!(int, int) x; - x[0] = 10; - x[1] = 20; - Tuple!(int, "a", double, "b") y = x; - assert(y.a == 10); - assert(y.b == 20); - // incompatible - static assert(!__traits(compiles, Tuple!(int, int)(y))); - } - // https://issues.dlang.org/show_bug.cgi?id=6275 - { - const int x = 1; - auto t1 = tuple(x); - alias T = Tuple!(const(int)); - auto t2 = T(1); - } - // https://issues.dlang.org/show_bug.cgi?id=9431 - { - alias T = Tuple!(int[1][]); - auto t = T([[10]]); - } - // https://issues.dlang.org/show_bug.cgi?id=7666 - { - auto tup = tuple(1, "2"); - assert(tup.reverse == tuple("2", 1)); - } - { - Tuple!(int, "x", string, "y") tup = tuple(1, "2"); - auto rev = tup.reverse; - assert(rev == tuple("2", 1)); - assert(rev.x == 1 && rev.y == "2"); - } - { - Tuple!(wchar, dchar, int, "x", string, "y", char, byte, float) tup; - tup = tuple('a', 'b', 3, "4", 'c', cast(byte) 0x0D, 0.00); - auto rev = tup.reverse; - assert(rev == tuple(0.00, cast(byte) 0x0D, 'c', "4", 3, 'b', 'a')); - assert(rev.x == 3 && rev.y == "4"); - } -} -@safe unittest -{ - // opEquals - { - struct Equ1 { bool opEquals(Equ1) { return true; } } - auto tm1 = tuple(Equ1.init); - const tc1 = tuple(Equ1.init); - static assert( is(typeof(tm1 == tm1))); - static assert(!is(typeof(tm1 == tc1))); - static assert(!is(typeof(tc1 == tm1))); - static assert(!is(typeof(tc1 == tc1))); - - struct Equ2 { bool opEquals(const Equ2) const { return true; } } - auto tm2 = tuple(Equ2.init); - const tc2 = tuple(Equ2.init); - static assert( is(typeof(tm2 == tm2))); - static assert( is(typeof(tm2 == tc2))); - static assert( is(typeof(tc2 == tm2))); - static assert( is(typeof(tc2 == tc2))); - - // https://issues.dlang.org/show_bug.cgi?id=8686 - struct Equ3 { bool opEquals(T)(T) { return true; } } - auto tm3 = tuple(Equ3.init); - const tc3 = tuple(Equ3.init); - static assert( is(typeof(tm3 == tm3))); - static assert( is(typeof(tm3 == tc3))); - static assert(!is(typeof(tc3 == tm3))); - static assert(!is(typeof(tc3 == tc3))); - - struct Equ4 { bool opEquals(T)(T) const { return true; } } - auto tm4 = tuple(Equ4.init); - const tc4 = tuple(Equ4.init); - static assert( is(typeof(tm4 == tm4))); - static assert( is(typeof(tm4 == tc4))); - static assert( is(typeof(tc4 == tm4))); - static assert( is(typeof(tc4 == tc4))); - } - // opCmp - { - struct Cmp1 { int opCmp(Cmp1) { return 0; } } - auto tm1 = tuple(Cmp1.init); - const tc1 = tuple(Cmp1.init); - static assert( is(typeof(tm1 < tm1))); - static assert(!is(typeof(tm1 < tc1))); - static assert(!is(typeof(tc1 < tm1))); - static assert(!is(typeof(tc1 < tc1))); - - struct Cmp2 { int opCmp(const Cmp2) const { return 0; } } - auto tm2 = tuple(Cmp2.init); - const tc2 = tuple(Cmp2.init); - static assert( is(typeof(tm2 < tm2))); - static assert( is(typeof(tm2 < tc2))); - static assert( is(typeof(tc2 < tm2))); - static assert( is(typeof(tc2 < tc2))); - - struct Cmp3 { int opCmp(T)(T) { return 0; } } - auto tm3 = tuple(Cmp3.init); - const tc3 = tuple(Cmp3.init); - static assert( is(typeof(tm3 < tm3))); - static assert( is(typeof(tm3 < tc3))); - static assert(!is(typeof(tc3 < tm3))); - static assert(!is(typeof(tc3 < tc3))); - - struct Cmp4 { int opCmp(T)(T) const { return 0; } } - auto tm4 = tuple(Cmp4.init); - const tc4 = tuple(Cmp4.init); - static assert( is(typeof(tm4 < tm4))); - static assert( is(typeof(tm4 < tc4))); - static assert( is(typeof(tc4 < tm4))); - static assert( is(typeof(tc4 < tc4))); - } - // https://issues.dlang.org/show_bug.cgi?id=14890 - static void test14890(inout int[] dummy) - { - alias V = Tuple!(int, int); - - V mv; - const V cv; - immutable V iv; - inout V wv; // OK <- NG - inout const V wcv; // OK <- NG - - static foreach (v1; AliasSeq!(mv, cv, iv, wv, wcv)) - static foreach (v2; AliasSeq!(mv, cv, iv, wv, wcv)) - { - assert(!(v1 < v2)); - } - } - { - int[2] ints = [ 1, 2 ]; - Tuple!(int, int) t = ints; - assert(t[0] == 1 && t[1] == 2); - Tuple!(long, uint) t2 = ints; - assert(t2[0] == 1 && t2[1] == 2); - } -} -@safe unittest -{ - auto t1 = Tuple!(int, "x", string, "y")(1, "a"); - assert(t1.x == 1); - assert(t1.y == "a"); - void foo(Tuple!(int, string) t2) {} - foo(t1); - - Tuple!(int, int)[] arr; - arr ~= tuple(10, 20); // OK - arr ~= Tuple!(int, "x", int, "y")(10, 20); // NG -> OK - - static assert(is(typeof(Tuple!(int, "x", string, "y").tupleof) == - typeof(Tuple!(int, string ).tupleof))); -} -@safe unittest -{ - // https://issues.dlang.org/show_bug.cgi?id=10686 - immutable Tuple!(int) t1; - auto r1 = t1[0]; // OK - immutable Tuple!(int, "x") t2; - auto r2 = t2[0]; // error -} -@safe unittest -{ - import std.exception : assertCTFEable; - - // https://issues.dlang.org/show_bug.cgi?id=10218 - assertCTFEable!( - { - auto t = tuple(1); - t = tuple(2); // assignment - }); -} -@safe unittest -{ - class Foo{} - Tuple!(immutable(Foo)[]) a; -} - -@safe unittest -{ - //Test non-assignable - static struct S - { - int* p; - } - alias IS = immutable S; - static assert(!isAssignable!IS); - - auto s = IS.init; - - alias TIS = Tuple!IS; - TIS a = tuple(s); - TIS b = a; - - alias TISIS = Tuple!(IS, IS); - TISIS d = tuple(s, s); - IS[2] ss; - TISIS e = TISIS(ss); -} - -// https://issues.dlang.org/show_bug.cgi?id=9819 -@safe unittest -{ - alias T = Tuple!(int, "x", double, "foo"); - static assert(T.fieldNames[0] == "x"); - static assert(T.fieldNames[1] == "foo"); - - alias Fields = Tuple!(int, "id", string, float); - static assert(Fields.fieldNames == AliasSeq!("id", "", "")); -} - -// https://issues.dlang.org/show_bug.cgi?id=13837 -@safe unittest -{ - // New behaviour, named arguments. - static assert(is( - typeof(tuple!("x")(1)) == Tuple!(int, "x"))); - static assert(is( - typeof(tuple!("x")(1.0)) == Tuple!(double, "x"))); - static assert(is( - typeof(tuple!("x")("foo")) == Tuple!(string, "x"))); - static assert(is( - typeof(tuple!("x", "y")(1, 2.0)) == Tuple!(int, "x", double, "y"))); - - auto a = tuple!("a", "b", "c")("1", 2, 3.0f); - static assert(is(typeof(a.a) == string)); - static assert(is(typeof(a.b) == int)); - static assert(is(typeof(a.c) == float)); - - // Old behaviour, but with explicit type parameters. - static assert(is( - typeof(tuple!(int, double)(1, 2.0)) == Tuple!(int, double))); - static assert(is( - typeof(tuple!(const int)(1)) == Tuple!(const int))); - static assert(is( - typeof(tuple()) == Tuple!())); - - // Nonsensical behaviour - static assert(!__traits(compiles, tuple!(1)(2))); - static assert(!__traits(compiles, tuple!("x")(1, 2))); - static assert(!__traits(compiles, tuple!("x", "y")(1))); - static assert(!__traits(compiles, tuple!("x")())); - static assert(!__traits(compiles, tuple!("x", int)(2))); -} - -@safe unittest -{ - class C { override size_t toHash() const nothrow @safe { return 0; } } - Tuple!(Rebindable!(const C)) a; - Tuple!(const C) b; - a = b; -} - -@nogc @safe unittest -{ - alias T = Tuple!(string, "s"); - T x; - x = T.init; -} - -@safe unittest -{ - import std.format : format, FormatException; - import std.exception : assertThrown; - - //enum tupStr = tuple(1, 1.0).toString; // toString is *impure*. - //static assert(tupStr == `Tuple!(int, double)(1, 1)`); -} - -// https://issues.dlang.org/show_bug.cgi?id=17803, parte uno -@safe unittest -{ - auto a = tuple(3, "foo"); - assert(__traits(compiles, { a = (a = a); })); -} -// Ditto -@safe unittest -{ - Tuple!(int[]) a, b, c; - a = tuple([0, 1, 2]); - c = b = a; - assert(a[0].length == b[0].length && b[0].length == c[0].length); - assert(a[0].ptr == b[0].ptr && b[0].ptr == c[0].ptr); -} - -/** - Constructs a $(LREF Tuple) object instantiated and initialized according to - the given arguments. - - Params: - Names = An optional list of strings naming each successive field of the `Tuple` - or a list of types that the elements are being casted to. - For a list of names, - each name matches up with the corresponding field given by `Args`. - A name does not have to be provided for every field, but as - the names must proceed in order, it is not possible to skip - one field and name the next after it. - For a list of types, - there must be exactly as many types as parameters. -*/ -template tuple(Names...) -{ - /** - Params: - args = Values to initialize the `Tuple` with. The `Tuple`'s type will - be inferred from the types of the values given. - - Returns: - A new `Tuple` with its type inferred from the arguments given. - */ - auto tuple(Args...)(Args args) - { - static if (Names.length == 0) - { - // No specified names, just infer types from Args... - return Tuple!Args(args); - } - else static if (!is(typeof(Names[0]) : string)) - { - // Names[0] isn't a string, must be explicit types. - return Tuple!Names(args); - } - else - { - // Names[0] is a string, so must be specifying names. - static assert(Names.length == Args.length, - "Insufficient number of names given."); - - // Interleave(a, b).and(c, d) == (a, c, b, d) - // This is to get the interleaving of types and names for Tuple - // e.g. Tuple!(int, "x", string, "y") - template Interleave(A...) - { - template and(B...) if (B.length == 1) - { - alias and = AliasSeq!(A[0], B[0]); - } - - template and(B...) if (B.length != 1) - { - alias and = AliasSeq!(A[0], B[0], - Interleave!(A[1..$]).and!(B[1..$])); - } - } - return Tuple!(Interleave!(Args).and!(Names))(args); - } - } -} - -/// -@safe unittest -{ - auto value = tuple(5, 6.7, "hello"); - assert(value[0] == 5); - assert(value[1] == 6.7); - assert(value[2] == "hello"); - - // Field names can be provided. - auto entry = tuple!("index", "value")(4, "Hello"); - assert(entry.index == 4); - assert(entry.value == "Hello"); -} - -/** - Returns `true` if and only if `T` is an instance of `std.typecons.Tuple`. - - Params: - T = The type to check. - - Returns: - true if `T` is a `Tuple` type, false otherwise. - */ -enum isTuple(T) = __traits(compiles, - { - void f(Specs...)(Tuple!Specs tup) {} - f(T.init); - } ); - -/// -@safe unittest -{ - static assert(isTuple!(Tuple!())); - static assert(isTuple!(Tuple!(int))); - static assert(isTuple!(Tuple!(int, real, string))); - static assert(isTuple!(Tuple!(int, "x", real, "y"))); - static assert(isTuple!(Tuple!(int, Tuple!(real), string))); -} - -@safe unittest -{ - static assert(isTuple!(const Tuple!(int))); - static assert(isTuple!(immutable Tuple!(int))); - - static assert(!isTuple!(int)); - static assert(!isTuple!(const int)); - - struct S {} - static assert(!isTuple!(S)); -} - -// used by both Rebindable and UnqualRef -private mixin template RebindableCommon(T, U, alias This) -if (is(T == class) || is(T == interface) || isAssociativeArray!T) -{ - private union - { - T original; - U stripped; - } - - void opAssign(return scope T another) pure nothrow @nogc - { - // If `T` defines `opCast` we must infer the safety - static if (hasMember!(T, "opCast")) - { - // This will allow the compiler to infer the safety of `T.opCast!U` - // without generating any runtime cost - if (false) { stripped = cast(U) another; } - } - () @trusted { stripped = cast(U) another; }(); - } - - void opAssign(typeof(this) another) @trusted pure nothrow @nogc - { - stripped = another.stripped; - } - - static if (is(T == const U) && is(T == const shared U)) - { - // safely assign immutable to const / const shared - void opAssign(This!(immutable U) another) @trusted pure nothrow @nogc - { - stripped = another.stripped; - } - } - - this(T initializer) pure nothrow @nogc - { - // Infer safety from opAssign - opAssign(initializer); - } - - @property inout(T) get() @trusted pure nothrow @nogc return scope inout - { - return original; - } - - bool opEquals()(auto ref const(typeof(this)) rhs) const - { - // Must forward explicitly because 'stripped' is part of a union. - // The necessary 'toHash' is forwarded to the class via alias this. - return stripped == rhs.stripped; - } - - bool opEquals(const(U) rhs) const - { - return stripped == rhs; - } - - alias get this; -} - -/** -`Rebindable!(T)` is a simple, efficient wrapper that behaves just -like an object of type `T`, except that you can reassign it to -refer to another object. For completeness, `Rebindable!(T)` aliases -itself away to `T` if `T` is a non-const object type. - -You may want to use `Rebindable` when you want to have mutable -storage referring to `const` objects, for example an array of -references that must be sorted in place. `Rebindable` does not -break the soundness of D's type system and does not incur any of the -risks usually associated with `cast`. - -Params: - T = Any type. - */ -template Rebindable(T) -if (is(T == class) || is(T == interface) || isDynamicArray!T || isAssociativeArray!T) -{ - static if (is(T == const U, U) || is(T == immutable U, U)) - { - static if (isDynamicArray!T) - { - import std.range.primitives : ElementEncodingType; - alias Rebindable = const(ElementEncodingType!T)[]; - } - else - { - struct Rebindable - { - mixin RebindableCommon!(T, U, Rebindable); - } - } - } - else - { - alias Rebindable = T; - } -} - -///Regular `const` object references cannot be reassigned. -@safe unittest -{ - class Widget { int x; int y() @safe const { return x; } } - const a = new Widget; - // Fine - a.y(); - // error! can't modify const a - // a.x = 5; - // error! can't modify const a - // a = new Widget; -} - -/** - However, `Rebindable!(Widget)` does allow reassignment, - while otherwise behaving exactly like a $(D const Widget). - */ -@safe unittest -{ - class Widget { int x; int y() const @safe { return x; } } - auto a = Rebindable!(const Widget)(new Widget); - // Fine - a.y(); - // error! can't modify const a - // a.x = 5; - // Fine - a = new Widget; -} - -// https://issues.dlang.org/show_bug.cgi?id=16054 -@safe unittest -{ - Rebindable!(immutable Object) r; - static assert(__traits(compiles, r.get())); - static assert(!__traits(compiles, &r.get())); -} - -/// ditto -struct Rebindable(T) -if (!is(T == class) && !is(T == interface) && !isDynamicArray!T && !isAssociativeArray!T) -{ -private: - static if (isAssignable!(typeof(cast() T.init))) - { - enum useQualifierCast = true; - - typeof(cast() T.init) data; - } - else - { - enum useQualifierCast = false; - - align(T.alignof) - static struct Payload - { - static if (hasIndirections!T) - { - void[T.sizeof] data; - } - else - { - ubyte[T.sizeof] data; - } - } - - Payload data; - } - -public: - - static if (!__traits(compiles, { T value; })) - { - @disable this(); - } - - /** - * Constructs a `Rebindable` from a given value. - */ - this(T value) @trusted - { - static if (useQualifierCast) - { - this.data = cast() value; - } - else - { - set(value); - } - } - - /** - * Overwrites the currently stored value with `value`. - */ - void opAssign(this This)(T value) @trusted - { - clear; - set(value); - } - - /** - * Returns the value currently stored in the `Rebindable`. - */ - T get(this This)() @property @trusted - { - static if (useQualifierCast) - { - return cast(T) this.data; - } - else - { - return *cast(T*) &this.data; - } - } - - static if (!useQualifierCast) - { - ~this() @trusted - { - clear; - } - } - - /// - alias get this; - -private: - - void set(this This)(T value) - { - static if (useQualifierCast) - { - this.data = cast() value; - } - else - { - // As we're escaping a copy of `value`, deliberately leak a copy: - static union DontCallDestructor - { - T value; - } - DontCallDestructor copy = DontCallDestructor(value); - this.data = *cast(Payload*) © - } - } - - void clear(this This)() - { - // work around reinterpreting cast being impossible in CTFE - if (__ctfe) - { - return; - } - - // call possible struct destructors - .destroy!(No.initialize)(*cast(T*) &this.data); - } -} - -/// Using Rebindable in a generic algorithm: -@safe unittest -{ - import std.range.primitives : front, popFront; - - // simple version of std.algorithm.searching.maxElement - typeof(R.init.front) maxElement(R)(R r) - { - auto max = rebindable(r.front); - r.popFront; - foreach (e; r) - if (e > max) - max = e; // Rebindable allows const-correct reassignment - return max; - } - struct S - { - char[] arr; - alias arr this; // for comparison - } - // can't convert to mutable - const S cs; - static assert(!__traits(compiles, { S s = cs; })); - - alias CS = const S; - CS[] arr = [CS("harp"), CS("apple"), CS("pot")]; - CS ms = maxElement(arr); - assert(ms.arr == "pot"); -} - -// https://issues.dlang.org/show_bug.cgi?id=18615 -// Rebindable!A should use A.opEqualsa -@system unittest -{ - class CustomOpEq - { - int x; - override bool opEquals(Object rhsObj) - { - if (auto rhs = cast(const(CustomOpEq)) rhsObj) - return this.x == rhs.x; - else - return false; - } - } - CustomOpEq a = new CustomOpEq(); - CustomOpEq b = new CustomOpEq(); - assert(a !is b); - assert(a == b, "a.x == b.x should be true (0 == 0)."); - - Rebindable!(const(CustomOpEq)) ra = a; - Rebindable!(const(CustomOpEq)) rb = b; - assert(ra !is rb); - assert(ra == rb, "Rebindable should use CustomOpEq's opEquals, not 'is'."); - assert(ra == b, "Rebindable!(someQualifier(A)) should be comparable" - ~ " against const(A) via A.opEquals."); - assert(a == rb, "Rebindable!(someQualifier(A)) should be comparable" - ~ " against const(A) via A.opEquals."); - - b.x = 1; - assert(a != b); - assert(ra != b, "Rebindable!(someQualifier(A)) should be comparable" - ~ " against const(A) via A.opEquals."); - assert(a != rb, "Rebindable!(someQualifier(A)) should be comparable" - ~ " against const(A) via A.opEquals."); - - Rebindable!(const(Object)) o1 = new Object(); - Rebindable!(const(Object)) o2 = new Object(); - assert(o1 !is o2); - assert(o1 == o1, "When the class doesn't provide its own opEquals," - ~ " Rebindable treats 'a == b' as 'a is b' like Object.opEquals."); - assert(o1 != o2, "When the class doesn't provide its own opEquals," - ~ " Rebindable treats 'a == b' as 'a is b' like Object.opEquals."); - assert(o1 != new Object(), "Rebindable!(const(Object)) should be" - ~ " comparable against Object itself and use Object.opEquals."); -} - -/// -@system unittest -{ - static struct S - { - int* ptr; - } - S s = S(new int); - - const cs = s; - // Can't assign s.ptr to cs.ptr - static assert(!__traits(compiles, {s = cs;})); - - Rebindable!(const S) rs = s; - assert(rs.ptr is s.ptr); - // rs.ptr is const - static assert(!__traits(compiles, {rs.ptr = null;})); - - // Can't assign s.ptr to rs.ptr - static assert(!__traits(compiles, {s = rs;})); - - const S cs2 = rs; - // Rebind rs - rs = cs2; - rs = S(); - assert(rs.ptr is null); -} - -// https://issues.dlang.org/show_bug.cgi?id=18755 -@safe unittest -{ - static class Foo - { - auto opCast(T)() @system immutable pure nothrow - { - *(cast(uint*) 0xdeadbeef) = 0xcafebabe; - return T.init; - } - } - - static assert(!__traits(compiles, () @safe { - auto r = Rebindable!(immutable Foo)(new Foo); - })); - static assert(__traits(compiles, () @system { - auto r = Rebindable!(immutable Foo)(new Foo); - })); -} - -@safe unittest -{ - class CustomToHash - { - override size_t toHash() const nothrow @trusted { return 42; } - } - Rebindable!(immutable(CustomToHash)) a = new immutable CustomToHash(); - assert(a.toHash() == 42, "Rebindable!A should offer toHash()" - ~ " by forwarding to A.toHash()."); -} - -// Test Rebindable!immutable -@safe unittest -{ - static struct S - { - int* ptr; - } - S s = S(new int); - - Rebindable!(immutable S) ri = S(new int); - assert(ri.ptr !is null); - static assert(!__traits(compiles, {ri.ptr = null;})); - - // ri is not compatible with mutable S - static assert(!__traits(compiles, {s = ri;})); - static assert(!__traits(compiles, {ri = s;})); - - auto ri2 = ri; - assert(ri2.ptr == ri.ptr); - - const S cs3 = ri; - static assert(!__traits(compiles, {ri = cs3;})); - - immutable S si = ri; - // Rebind ri - ri = si; - ri = S(); - assert(ri.ptr is null); - - // Test RB!immutable -> RB!const - Rebindable!(const S) rc = ri; - assert(rc.ptr is null); - ri = S(new int); - rc = ri; - assert(rc.ptr !is null); - - // test rebindable, opAssign - rc.destroy; - assert(rc.ptr is null); - rc = rebindable(cs3); - rc = rebindable(si); - assert(rc.ptr !is null); - - ri.destroy; - assert(ri.ptr is null); - ri = rebindable(si); - assert(ri.ptr !is null); -} - -// Test disabled default ctor -@safe unittest -{ - static struct ND - { - int i; - @disable this(); - this(int i) inout {this.i = i;} - } - static assert(!__traits(compiles, Rebindable!ND())); - - Rebindable!(const ND) rb = const ND(1); - assert(rb.i == 1); - rb = immutable ND(2); - assert(rb.i == 2); - rb = rebindable(const ND(3)); - assert(rb.i == 3); - static assert(!__traits(compiles, rb.i++)); -} - -// Test copying -@safe unittest -{ - int del; - int post; - struct S - { - int* ptr; - int level; - this(this) - { - post++; - level++; - } - ~this() - { - del++; - } - } - - // test ref count - { - Rebindable!S rc = S(new int); - } - assert(post == del - 1); -} - -@safe unittest -{ - int del; - int post; - struct S - { - immutable int x; - int level; - this(this) - { - post++; - level++; - } - ~this() - { - del++; - } - } - - // test ref count - { - Rebindable!S rc = S(0); - } - assert(post == del - 1); -} - -/** -Convenience function for creating a `Rebindable` using automatic type -inference. - -Params: - obj = A reference to a value to initialize the `Rebindable` with. - -Returns: - A newly constructed `Rebindable` initialized with the given reference. -*/ -Rebindable!T rebindable(T)(T obj) -if (is(T == class) || is(T == interface) || isDynamicArray!T || isAssociativeArray!T) -{ - typeof(return) ret; - ret = obj; - return ret; -} - -/// -@system unittest -{ - class C - { - int payload; - this(int p) { payload = p; } - } - const c = new C(1); - - auto c2 = c.rebindable; - assert(c2.payload == 1); - // passing Rebindable to rebindable - c2 = c2.rebindable; - - c2 = new C(2); - assert(c2.payload == 2); - - const c3 = c2.get; - assert(c3.payload == 2); -} - -/// ditto -Rebindable!T rebindable(T)(T value) -if (!is(T == class) && !is(T == interface) && !isDynamicArray!T && !isAssociativeArray!T - && !is(T : Rebindable!U, U)) -{ - return Rebindable!T(value); -} - -/// -@safe unittest -{ - immutable struct S - { - int[] array; - } - auto s1 = [3].idup.rebindable; - s1 = [4].idup.rebindable; - assert(s1 == [4]); -} - -/** -This function simply returns the `Rebindable` object passed in. It's useful -in generic programming cases when a given object may be either a regular -`class` or a `Rebindable`. - -Params: - obj = An instance of Rebindable!T. - -Returns: - `obj` without any modification. -*/ -Rebindable!T rebindable(T)(Rebindable!T obj) -{ - return obj; -} - -// TODO: remove me once the rebindable overloads have been joined -/// -@system unittest -{ - class C - { - int payload; - this(int p) { payload = p; } - } - const c = new C(1); - - auto c2 = c.rebindable; - assert(c2.payload == 1); - // passing Rebindable to rebindable - c2 = c2.rebindable; - assert(c2.payload == 1); -} - -@system unittest -{ - interface CI { int foo() const; } - class C : CI { - int foo() const { return 42; } - @property int bar() const { return 23; } - } - Rebindable!(C) obj0; - static assert(is(typeof(obj0) == C)); - - Rebindable!(const(C)) obj1; - static assert(is(typeof(obj1.get) == const(C)), typeof(obj1.get).stringof); - static assert(is(typeof(obj1.stripped) == C)); - obj1 = new C; - assert(obj1.get !is null); - obj1 = new const(C); - assert(obj1.get !is null); - - Rebindable!(immutable(C)) obj2; - static assert(is(typeof(obj2.get) == immutable(C))); - static assert(is(typeof(obj2.stripped) == C)); - obj2 = new immutable(C); - assert(obj1.get !is null); - - // test opDot - assert(obj2.foo() == 42); - assert(obj2.bar == 23); - - interface I { final int foo() const { return 42; } } - Rebindable!(I) obj3; - static assert(is(typeof(obj3) == I)); - - Rebindable!(const I) obj4; - static assert(is(typeof(obj4.get) == const I)); - static assert(is(typeof(obj4.stripped) == I)); - static assert(is(typeof(obj4.foo()) == int)); - obj4 = new class I {}; - - Rebindable!(immutable C) obj5i; - Rebindable!(const C) obj5c; - obj5c = obj5c; - obj5c = obj5i; - obj5i = obj5i; - static assert(!__traits(compiles, obj5i = obj5c)); - - // Test the convenience functions. - auto obj5convenience = rebindable(obj5i); - assert(obj5convenience is obj5i); - - auto obj6 = rebindable(new immutable(C)); - static assert(is(typeof(obj6) == Rebindable!(immutable C))); - assert(obj6.foo() == 42); - - auto obj7 = rebindable(new C); - CI interface1 = obj7; - auto interfaceRebind1 = rebindable(interface1); - assert(interfaceRebind1.foo() == 42); - - const interface2 = interface1; - auto interfaceRebind2 = rebindable(interface2); - assert(interfaceRebind2.foo() == 42); - - auto arr = [1,2,3,4,5]; - const arrConst = arr; - assert(rebindable(arr) == arr); - assert(rebindable(arrConst) == arr); - - // https://issues.dlang.org/show_bug.cgi?id=7654 - immutable(char[]) s7654; - Rebindable!(typeof(s7654)) r7654 = s7654; - - static foreach (T; AliasSeq!(char, wchar, char, int)) - { - static assert(is(Rebindable!(immutable(T[])) == immutable(T)[])); - static assert(is(Rebindable!(const(T[])) == const(T)[])); - static assert(is(Rebindable!(T[]) == T[])); - } - - // Pull request 3341 - Rebindable!(immutable int[int]) pr3341 = [123:345]; - assert(pr3341[123] == 345); - immutable int[int] pr3341_aa = [321:543]; - pr3341 = pr3341_aa; - assert(pr3341[321] == 543); - assert(rebindable(pr3341_aa)[321] == 543); -} - -package(std) struct Rebindable2(T) -{ -private: - static if (isAssignable!(typeof(cast() T.init))) - { - enum useQualifierCast = true; - - typeof(cast() T.init) data; - } - else - { - enum useQualifierCast = false; - - align(T.alignof) - static struct Payload - { - static if (hasIndirections!T) - { - void[T.sizeof] data; - } - else - { - ubyte[T.sizeof] data; - } - } - - Payload data; - } - -public: - - static if (!__traits(compiles, { T value; })) - { - @disable this(); - } - - /** - * Constructs a `Rebindable2` from a given value. - */ - this(T value) @trusted - { - static if (useQualifierCast) - { - this.data = cast() value; - } - else - { - set(value); - } - } - - /** - * Overwrites the currently stored value with `value`. - */ - void opAssign(this This)(T value) @trusted - { - clear; - set(value); - } - - /** - * Returns the value currently stored in the `Rebindable2`. - */ - T get(this This)() @property @trusted - { - static if (useQualifierCast) - { - return cast(T) this.data; - } - else - { - return *cast(T*) &this.data; - } - } - - /// Ditto - inout(T) get() inout @property @trusted - { - static if (useQualifierCast) - { - return cast(inout(T)) this.data; - } - else - { - return *cast(inout(T)*) &this.data; - } - } - - static if (!useQualifierCast) - { - ~this() @trusted - { - clear; - } - } - -private: - - void set(this This)(T value) - { - static if (useQualifierCast) - { - this.data = cast() value; - } - else - { - // As we're escaping a copy of `value`, deliberately leak a copy: - static union DontCallDestructor - { - T value; - } - DontCallDestructor copy = DontCallDestructor(value); - this.data = *cast(Payload*) © - } - } - - void clear(this This)() - { - // work around reinterpreting cast being impossible in CTFE - if (__ctfe) - { - return; - } - - // call possible struct destructors - static if (is(T == struct)) - { - .destroy!(No.initialize)(*cast(T*) &this.data); - } - } -} - -package(std) Rebindable2!T rebindable2(T)(T value) -{ - return Rebindable2!T(value); -} - -/** - Similar to `Rebindable!(T)` but strips all qualifiers from the reference as - opposed to just constness / immutability. Primary intended use case is with - shared (having thread-local reference to shared class data) - - Params: - T = A class or interface type. - */ -template UnqualRef(T) -if (is(T == class) || is(T == interface)) -{ - static if (is(T == immutable U, U) - || is(T == const shared U, U) - || is(T == const U, U) - || is(T == shared U, U)) - { - struct UnqualRef - { - mixin RebindableCommon!(T, U, UnqualRef); - } - } - else - { - alias UnqualRef = T; - } -} - -/// -@system unittest -{ - class Data {} - - static shared(Data) a; - static UnqualRef!(shared Data) b; - - import core.thread; - - auto thread = new core.thread.Thread({ - a = new shared Data(); - b = new shared Data(); - }); - - thread.start(); - thread.join(); - - assert(a !is null); - assert(b is null); -} - -@safe unittest -{ - class C { } - alias T = UnqualRef!(const shared C); - static assert(is(typeof(T.stripped) == C)); -} - - - -/** - Order the provided members to minimize size while preserving alignment. - Alignment is not always optimal for 80-bit reals, nor for structs declared - as align(1). - - Params: - E = A list of the types to be aligned, representing fields - of an aggregate such as a `struct` or `class`. - - names = The names of the fields that are to be aligned. - - Returns: - A string to be mixed in to an aggregate, such as a `struct` or `class`. -*/ -string alignForSize(E...)(const char[][] names...) -{ - // Sort all of the members by .alignof. - // BUG: Alignment is not always optimal for align(1) structs - // or 80-bit reals or 64-bit primitives on x86. - // TRICK: Use the fact that .alignof is always a power of 2, - // and maximum 16 on extant systems. Thus, we can perform - // a very limited radix sort. - // Contains the members with .alignof = 64,32,16,8,4,2,1 - - assert(E.length == names.length, - "alignForSize: There should be as many member names as the types"); - - string[7] declaration = ["", "", "", "", "", "", ""]; - - foreach (i, T; E) - { - auto a = T.alignof; - auto k = a >= 64? 0 : a >= 32? 1 : a >= 16? 2 : a >= 8? 3 : a >= 4? 4 : a >= 2? 5 : 6; - declaration[k] ~= T.stringof ~ " " ~ names[i] ~ ";\n"; - } - - auto s = ""; - foreach (decl; declaration) - s ~= decl; - return s; -} - -/// -@safe unittest -{ - struct Banner { - mixin(alignForSize!(byte[6], double)(["name", "height"])); - } -} - -@safe unittest -{ - enum x = alignForSize!(int[], char[3], short, double[5])("x", "y","z", "w"); - struct Foo { int x; } - enum y = alignForSize!(ubyte, Foo, double)("x", "y", "z"); - - enum passNormalX = x == "double[5] w;\nint[] x;\nshort z;\nchar[3] y;\n"; - enum passNormalY = y == "double z;\nFoo y;\nubyte x;\n"; - - enum passAbnormalX = x == "int[] x;\ndouble[5] w;\nshort z;\nchar[3] y;\n"; - enum passAbnormalY = y == "Foo y;\ndouble z;\nubyte x;\n"; - // ^ blame https://issues.dlang.org/show_bug.cgi?id=231 - - static assert(passNormalX || passAbnormalX && double.alignof <= (int[]).alignof); - static assert(passNormalY || passAbnormalY && double.alignof <= int.alignof); -} - -// https://issues.dlang.org/show_bug.cgi?id=12914 -@safe unittest -{ - immutable string[] fieldNames = ["x", "y"]; - struct S - { - mixin(alignForSize!(byte, int)(fieldNames)); - } -} - -/** -Defines a value paired with a distinctive "null" state that denotes -the absence of a value. If default constructed, a $(D -Nullable!T) object starts in the null state. Assigning it renders it -non-null. Calling `nullify` can nullify it again. - -Practically `Nullable!T` stores a `T` and a `bool`. - -See also: - $(LREF apply), an alternative way to use the payload. - */ -struct Nullable(T) -{ - private union DontCallDestructorT - { - import std.traits : hasIndirections; - static if (hasIndirections!T) - T payload; - else - T payload = void; - } - - private DontCallDestructorT _value = DontCallDestructorT.init; - - private bool _isNull = true; - - /** - * Constructor initializing `this` with `value`. - * - * Params: - * value = The value to initialize this `Nullable` with. - */ - static if (isCopyable!T) - this(inout T value) inout - { - _value.payload = value; - _isNull = false; - } - else - this(T value) inout - { - import std.algorithm.mutation : move; - _value.payload = move(value); - _isNull = false; - } - - static if (hasElaborateDestructor!T) - { - ~this() - { - if (!_isNull) - { - import std.traits : Unqual; - auto ptr = () @trusted { return cast(Unqual!T*) &_value.payload; }(); - destroy!false(*ptr); - } - } - } - - static if (!isCopyable!T) - @disable this(this); - else - static if (__traits(hasPostblit, T)) - { - this(this) - { - if (!_isNull) - _value.payload.__xpostblit(); - } - } - else static if (__traits(hasCopyConstructor, T)) - { - this(ref return scope inout Nullable!T rhs) inout - { - _isNull = rhs._isNull; - if (!_isNull) - _value.payload = rhs._value.payload; - else - _value = DontCallDestructorT.init; - } - } - - /** - * If they are both null, then they are equal. If one is null and the other - * is not, then they are not equal. If they are both non-null, then they are - * equal if their values are equal. - */ - bool opEquals(this This, Rhs)(auto ref Rhs rhs) - if (!is(CommonType!(This, Rhs) == void)) - { - static if (is(This == Rhs)) - { - if (_isNull) - return rhs._isNull; - if (rhs._isNull) - return false; - return _value.payload == rhs._value.payload; - } - else - { - alias Common = CommonType!(This, Rhs); - return cast(Common) this == cast(Common) rhs; - } - } - - /// Ditto - bool opEquals(this This, Rhs)(auto ref Rhs rhs) - if (is(CommonType!(This, Rhs) == void) && is(typeof(this.get == rhs))) - { - return _isNull ? false : rhs == _value.payload; - } - - /// - @safe unittest - { - Nullable!int empty; - Nullable!int a = 42; - Nullable!int b = 42; - Nullable!int c = 27; - - assert(empty == empty); - assert(empty == Nullable!int.init); - assert(empty != a); - assert(empty != b); - assert(empty != c); - - assert(a == b); - assert(a != c); - - assert(empty != 42); - assert(a == 42); - assert(c != 42); - } - - @safe unittest - { - // Test constness - immutable Nullable!int a = 42; - Nullable!int b = 42; - immutable Nullable!int c = 29; - Nullable!int d = 29; - immutable e = 42; - int f = 29; - assert(a == a); - assert(a == b); - assert(a != c); - assert(a != d); - assert(a == e); - assert(a != f); - - // Test rvalue - assert(a == const Nullable!int(42)); - assert(a != Nullable!int(29)); - } - - // https://issues.dlang.org/show_bug.cgi?id=17482 - @system unittest - { - import std.variant : Variant; - Nullable!Variant a = Variant(12); - assert(a == 12); - Nullable!Variant e; - assert(e != 12); - } - - size_t toHash() const @safe nothrow - { - static if (__traits(compiles, .hashOf(_value.payload))) - return _isNull ? 0 : .hashOf(_value.payload); - else - // Workaround for when .hashOf is not both @safe and nothrow. - return _isNull ? 0 : typeid(T).getHash(&_value.payload); - } - - /** - * Gives the string `"Nullable.null"` if `isNull` is `true`. Otherwise, the - * result is equivalent to calling $(REF formattedWrite, std,format) on the - * underlying value. - * - * Params: - * writer = A `char` accepting - * $(REF_ALTTEXT output range, isOutputRange, std, range, primitives) - * fmt = A $(REF FormatSpec, std,format) which is used to represent - * the value if this Nullable is not null - * Returns: - * A `string` if `writer` and `fmt` are not set; `void` otherwise. - */ - string toString() - { - import std.array : appender; - auto app = appender!string(); - auto spec = singleSpec("%s"); - toString(app, spec); - return app.data; - } - - /// ditto - string toString() const - { - import std.array : appender; - auto app = appender!string(); - auto spec = singleSpec("%s"); - toString(app, spec); - return app.data; - } - - /// ditto - void toString(W)(ref W writer, scope const ref FormatSpec!char fmt) - if (isOutputRange!(W, char)) - { - import std.range.primitives : put; - if (isNull) - put(writer, "Nullable.null"); - else - formatValue(writer, _value.payload, fmt); - } - - /// ditto - void toString(W)(ref W writer, scope const ref FormatSpec!char fmt) const - if (isOutputRange!(W, char)) - { - import std.range.primitives : put; - if (isNull) - put(writer, "Nullable.null"); - else - formatValue(writer, _value.payload, fmt); - } - - /** - * Check if `this` is in the null state. - * - * Returns: - * true $(B iff) `this` is in the null state, otherwise false. - */ - @property bool isNull() const @safe pure nothrow - { - return _isNull; - } - - /// - @safe unittest - { - Nullable!int ni; - assert(ni.isNull); - - ni = 0; - assert(!ni.isNull); - } - - // https://issues.dlang.org/show_bug.cgi?id=14940 - @safe unittest - { - import std.array : appender; - import std.format.write : formattedWrite; - - auto app = appender!string(); - Nullable!int a = 1; - formattedWrite(app, "%s", a); - assert(app.data == "1"); - } - - // https://issues.dlang.org/show_bug.cgi?id=19799 - @safe unittest - { - import std.format : format; - - const Nullable!string a = const(Nullable!string)(); - - format!"%s"(a); - } - - /** - * Forces `this` to the null state. - */ - void nullify()() - { - static if (is(T == class) || is(T == interface)) - _value.payload = null; - else - .destroy(_value.payload); - _isNull = true; - } - - /// - @safe unittest - { - Nullable!int ni = 0; - assert(!ni.isNull); - - ni.nullify(); - assert(ni.isNull); - } - - /** - * Assigns `value` to the internally-held state. If the assignment - * succeeds, `this` becomes non-null. - * - * Params: - * value = A value of type `T` to assign to this `Nullable`. - */ - ref Nullable opAssign()(T value) return - { - import std.algorithm.mutation : moveEmplace, move; - - if (_isNull) - { - // trusted since payload is known to be uninitialized. - () @trusted { moveEmplace(value, _value.payload); }(); - } - else - { - move(value, _value.payload); - } - _isNull = false; - return this; - } - - /** - * If this `Nullable` wraps a type that already has a null value - * (such as a pointer), then assigning the null value to this - * `Nullable` is no different than assigning any other value of - * type `T`, and the resulting code will look very strange. It - * is strongly recommended that this be avoided by instead using - * the version of `Nullable` that takes an additional `nullValue` - * template argument. - */ - @safe unittest - { - //Passes - Nullable!(int*) npi; - assert(npi.isNull); - - //Passes?! - npi = null; - assert(!npi.isNull); - } - - /** - * Gets the value if not null. If `this` is in the null state, and the optional - * parameter `fallback` was provided, it will be returned. Without `fallback`, - * calling `get` with a null state is invalid. - * - * When the fallback type is different from the Nullable type, `get(T)` returns - * the common type. - * - * Params: - * fallback = the value to return in case the `Nullable` is null. - * - * Returns: - * The value held internally by this `Nullable`. - */ - @property ref inout(T) get() inout @safe pure nothrow - { - enum message = "Called `get' on null Nullable!" ~ T.stringof ~ "."; - assert(!isNull, message); - return _value.payload; - } - - /// ditto - @property inout(T) get()(inout(T) fallback) inout - { - return isNull ? fallback : _value.payload; - } - - /// ditto - @property auto get(U)(inout(U) fallback) inout - { - return isNull ? fallback : _value.payload; - } - - /// $(MREF_ALTTEXT Range interface, std, range, primitives) functions. - alias empty = isNull; - - /// ditto - alias popFront = nullify; - - /// ditto - alias popBack = nullify; - - /// ditto - @property ref inout(T) front() inout @safe pure nothrow - { - return get(); - } - - /// ditto - alias back = front; - - /// ditto - static if (isCopyable!T) - @property inout(typeof(this)) save() inout - { - return this; - } - - /// ditto - static if (isCopyable!T) - inout(typeof(this)) opIndex(size_t[2] dim) inout - in (dim[0] <= length && dim[1] <= length && dim[1] >= dim[0]) - { - return (dim[0] == 0 && dim[1] == 1) ? this : this.init; - } - /// ditto - size_t[2] opSlice(size_t dim : 0)(size_t from, size_t to) const - { - return [from, to]; - } - - /// ditto - @property size_t length() const @safe pure nothrow - { - return !empty; - } - - /// ditto - alias opDollar(size_t dim : 0) = length; - - /// ditto - ref inout(T) opIndex(size_t index) inout @safe pure nothrow - in (index < length) - { - return get(); - } - - /** - * Converts `Nullable` to a range. Works even when the contained type is `immutable`. - */ - auto opSlice(this This)() - { - static struct NullableRange - { - private This value; - - // starts out true if value is null - private bool empty_; - - @property bool empty() const @safe pure nothrow - { - return empty_; - } - - void popFront() @safe pure nothrow - { - empty_ = true; - } - - alias popBack = popFront; - - @property ref inout(typeof(value.get())) front() inout @safe pure nothrow - { - return value.get(); - } - - alias back = front; - - @property inout(typeof(this)) save() inout - { - return this; - } - - size_t[2] opSlice(size_t dim : 0)(size_t from, size_t to) const - { - return [from, to]; - } - - @property size_t length() const @safe pure nothrow - { - return !empty; - } - - alias opDollar(size_t dim : 0) = length; - - ref inout(typeof(value.get())) opIndex(size_t index) inout @safe pure nothrow - in (index < length) - { - return value.get(); - } - - inout(typeof(this)) opIndex(size_t[2] dim) inout - in (dim[0] <= length && dim[1] <= length && dim[1] >= dim[0]) - { - return (dim[0] == 0 && dim[1] == 1) ? this : this.init; - } - - auto opIndex() inout - { - return this; - } - } - return NullableRange(this, isNull); - } -} - -/// ditto -auto nullable(T)(T t) -{ - return Nullable!T(t); -} - -/// -@safe unittest -{ - struct CustomerRecord - { - string name; - string address; - int customerNum; - } - - Nullable!CustomerRecord getByName(string name) - { - //A bunch of hairy stuff - - return Nullable!CustomerRecord.init; - } - - auto queryResult = getByName("Doe, John"); - if (!queryResult.isNull) - { - //Process Mr. Doe's customer record - auto address = queryResult.get.address; - auto customerNum = queryResult.get.customerNum; - - //Do some things with this customer's info - } - else - { - //Add the customer to the database - } -} - -/// -@system unittest -{ - import std.exception : assertThrown; - - auto a = 42.nullable; - assert(!a.isNull); - assert(a.get == 42); - - a.nullify(); - assert(a.isNull); - assertThrown!Throwable(a.get); -} -/// -@safe unittest -{ - import std.algorithm.iteration : each, joiner; - Nullable!int a = 42; - Nullable!int b; - // Add each value to an array - int[] arr; - a.each!((n) => arr ~= n); - assert(arr == [42]); - b.each!((n) => arr ~= n); - assert(arr == [42]); - // Take first value from an array of Nullables - Nullable!int[] c = new Nullable!int[](10); - c[7] = Nullable!int(42); - assert(c.joiner.front == 42); -} -@safe unittest -{ - auto k = Nullable!int(74); - assert(k == 74); - k.nullify(); - assert(k.isNull); -} -@safe unittest -{ - static int f(scope const Nullable!int x) { - return x.isNull ? 42 : x.get; - } - Nullable!int a; - assert(f(a) == 42); - a = 8; - assert(f(a) == 8); - a.nullify(); - assert(f(a) == 42); -} -@system unittest -{ - import std.exception : assertThrown; - - static struct S { int x; } - Nullable!S s; - assert(s.isNull); - s = S(6); - assert(s == S(6)); - assert(s != S(0)); - assert(s.get != S(0)); - s.get.x = 9190; - assert(s.get.x == 9190); - s.nullify(); - assertThrown!Throwable(s.get.x = 9441); -} -@safe unittest -{ - // Ensure Nullable can be used in pure/nothrow/@safe environment. - function() @safe pure nothrow - { - Nullable!int n; - assert(n.isNull); - n = 4; - assert(!n.isNull); - assert(n == 4); - n.nullify(); - assert(n.isNull); - }(); -} -@system unittest -{ - // Ensure Nullable can be used when the value is not pure/nothrow/@safe - static struct S - { - int x; - this(this) @system {} - } - - Nullable!S s; - assert(s.isNull); - s = S(5); - assert(!s.isNull); - assert(s.get.x == 5); - s.nullify(); - assert(s.isNull); -} - -// https://issues.dlang.org/show_bug.cgi?id=9404 -@safe unittest -{ - alias N = Nullable!int; - - void foo(N a) - { - N b; - b = a; // `N b = a;` works fine - } - N n; - foo(n); -} -@safe unittest -{ - //Check nullable immutable is constructable - { - auto a1 = Nullable!(immutable int)(); - auto a2 = Nullable!(immutable int)(1); - auto i = a2.get; - } - //Check immutable nullable is constructable - { - auto a1 = immutable (Nullable!int)(); - auto a2 = immutable (Nullable!int)(1); - auto i = a2.get; - } -} -@safe unittest -{ - alias NInt = Nullable!int; - - //Construct tests - { - //from other Nullable null - NInt a1; - NInt b1 = a1; - assert(b1.isNull); - - //from other Nullable non-null - NInt a2 = NInt(1); - NInt b2 = a2; - assert(b2 == 1); - - //Construct from similar nullable - auto a3 = immutable(NInt)(); - NInt b3 = a3; - assert(b3.isNull); - } - - //Assign tests - { - //from other Nullable null - NInt a1; - NInt b1; - b1 = a1; - assert(b1.isNull); - - //from other Nullable non-null - NInt a2 = NInt(1); - NInt b2; - b2 = a2; - assert(b2 == 1); - - //Construct from similar nullable - auto a3 = immutable(NInt)(); - NInt b3 = a3; - b3 = a3; - assert(b3.isNull); - } -} -@safe unittest -{ - //Check nullable is nicelly embedable in a struct - static struct S1 - { - Nullable!int ni; - } - static struct S2 //inspired from 9404 - { - Nullable!int ni; - this(ref S2 other) - { - ni = other.ni; - } - void opAssign(ref S2 other) - { - ni = other.ni; - } - } - static foreach (S; AliasSeq!(S1, S2)) - {{ - S a; - S b = a; - S c; - c = a; - }} -} - -// https://issues.dlang.org/show_bug.cgi?id=10268 -@system unittest -{ - import std.json; - JSONValue value = null; - auto na = Nullable!JSONValue(value); - - struct S1 { int val; } - struct S2 { int* val; } - struct S3 { immutable int* val; } - - { - auto sm = S1(1); - immutable si = immutable S1(1); - auto x1 = Nullable!S1(sm); - auto x2 = immutable Nullable!S1(sm); - auto x3 = Nullable!S1(si); - auto x4 = immutable Nullable!S1(si); - assert(x1.get.val == 1); - assert(x2.get.val == 1); - assert(x3.get.val == 1); - assert(x4.get.val == 1); - } - - auto nm = 10; - immutable ni = 10; - - { - auto sm = S2(&nm); - immutable si = immutable S2(&ni); - auto x1 = Nullable!S2(sm); - static assert(!__traits(compiles, { auto x2 = immutable Nullable!S2(sm); })); - static assert(!__traits(compiles, { auto x3 = Nullable!S2(si); })); - auto x4 = immutable Nullable!S2(si); - assert(*x1.get.val == 10); - assert(*x4.get.val == 10); - } - - { - auto sm = S3(&ni); - immutable si = immutable S3(&ni); - auto x1 = Nullable!S3(sm); - auto x2 = immutable Nullable!S3(sm); - auto x3 = Nullable!S3(si); - auto x4 = immutable Nullable!S3(si); - assert(*x1.get.val == 10); - assert(*x2.get.val == 10); - assert(*x3.get.val == 10); - assert(*x4.get.val == 10); - } -} - -// https://issues.dlang.org/show_bug.cgi?id=10357 -@safe unittest -{ - import std.datetime; - Nullable!SysTime time = SysTime(0); -} - -// https://issues.dlang.org/show_bug.cgi?id=10915 -@system unittest -{ - import std.conv : to; - import std.array; - - Appender!string buffer; - - Nullable!int ni; - assert(ni.to!string() == "Nullable.null"); - assert((cast(const) ni).to!string() == "Nullable.null"); - - struct Test { string s; } - alias NullableTest = Nullable!Test; - - NullableTest nt = Test("test"); - // test output range version - assert(nt.to!string() == `Test("test")`); - // test appender version - assert(nt.toString() == `Test("test")`); - // test const version - assert((cast(const) nt).toString() == `const(Test)("test")`); - - NullableTest ntn = Test("null"); - assert(ntn.to!string() == `Test("null")`); - - class TestToString - { - double d; - - this (double d) - { - this.d = d; - } - - override string toString() - { - return d.to!string(); - } - } - Nullable!TestToString ntts = new TestToString(2.5); - assert(ntts.to!string() == "2.5"); -} - -// https://issues.dlang.org/show_bug.cgi?id=14477 -@safe unittest -{ - static struct DisabledDefaultConstructor - { - @disable this(); - this(int i) { } - } - Nullable!DisabledDefaultConstructor var; - var = DisabledDefaultConstructor(5); - var.nullify; -} - -// https://issues.dlang.org/show_bug.cgi?id=17440 -@system unittest -{ - static interface I { } - - static class C : I - { - int canary; - ~this() - { - canary = 0x5050DEAD; - } - } - auto c = new C; - c.canary = 0xA71FE; - auto nc = nullable(c); - nc.nullify; - assert(c.canary == 0xA71FE); - - I i = c; - auto ni = nullable(i); - ni.nullify; - assert(c.canary == 0xA71FE); -} - -// https://issues.dlang.org/show_bug.cgi?id=19037 -@safe unittest -{ - import std.datetime : SysTime; - - struct Test - { - SysTime _st; - - static bool destroyed; - - @disable this(); - this(int _dummy) {} - ~this() @safe { destroyed = true; } - - // mustn't call opAssign on Test.init in Nullable!Test, because the invariant - // will be called before opAssign on the Test.init that is in Nullable - // and Test.init violates its invariant. - void opAssign(Test rhs) @safe { assert(false); } - } - - { - Nullable!Test nt; - - nt = Test(1); - - // destroy value - Test.destroyed = false; - - nt.nullify; - - assert(Test.destroyed); - - Test.destroyed = false; - } - // don't run destructor on T.init in Nullable on scope exit! - assert(!Test.destroyed); -} -// check that the contained type's destructor is called on assignment -@system unittest -{ - struct S - { - // can't be static, since we need a specific value's pointer - bool* destroyedRef; - - ~this() - { - if (this.destroyedRef) - { - *this.destroyedRef = true; - } - } - } - - Nullable!S ns; - - bool destroyed; - - ns = S(&destroyed); - - // reset from rvalue destruction in Nullable's opAssign - destroyed = false; - - // overwrite Nullable - ns = S(null); - - // the original S should be destroyed. - assert(destroyed == true); -} -// check that the contained type's destructor is still called when required -@system unittest -{ - bool destructorCalled = false; - - struct S - { - bool* destroyed; - ~this() { *this.destroyed = true; } - } - - { - Nullable!S ns; - } - assert(!destructorCalled); - { - Nullable!S ns = Nullable!S(S(&destructorCalled)); - - destructorCalled = false; // reset after S was destroyed in the NS constructor - } - assert(destructorCalled); -} - -// check that toHash on Nullable is forwarded to the contained type -@system unittest -{ - struct S - { - size_t toHash() const @safe pure nothrow { return 5; } - } - - Nullable!S s1 = S(); - Nullable!S s2 = Nullable!S(); - - assert(typeid(Nullable!S).getHash(&s1) == 5); - assert(typeid(Nullable!S).getHash(&s2) == 0); -} - -// https://issues.dlang.org/show_bug.cgi?id=21704 -@safe unittest -{ - import std.array : staticArray; - - bool destroyed; - - struct Probe - { - ~this() { destroyed = true; } - } - - { - Nullable!(Probe[1]) test = [Probe()].staticArray; - destroyed = false; - } - assert(destroyed); -} - -// https://issues.dlang.org/show_bug.cgi?id=21705 -@safe unittest -{ - static struct S - { - int n; - bool opEquals(S rhs) { return n == rhs.n; } - } - - Nullable!S test1 = S(1), test2 = S(1); - S s = S(1); - - assert(test1 == s); - assert(test1 == test2); -} - -// https://issues.dlang.org/show_bug.cgi?id=22101 -@safe unittest -{ - static int impure; - - struct S - { - ~this() { impure++; } - } - - Nullable!S s; - s.get(S()); -} - -// https://issues.dlang.org/show_bug.cgi?id=22100 -@safe unittest -{ - Nullable!int a, b, c; - a = b = c = 5; - a = b = c = nullable(5); -} - -// https://issues.dlang.org/show_bug.cgi?id=18374 -@safe pure nothrow unittest -{ - import std.algorithm.comparison : equal; - import std.range : only, takeNone; - import std.range.primitives : hasAssignableElements, hasLength, - hasLvalueElements, hasSlicing, hasSwappableElements, - isRandomAccessRange; - Nullable!int a = 42; - assert(!a.empty); - assert(a.front == 42); - assert(a.back == 42); - assert(a[0] == 42); - assert(a.equal(only(42))); - assert(a[0 .. $].equal(only(42))); - a[0] = 43; - assert(a.equal(only(43))); - --a[0]; - assert(a.equal(only(42))); - Nullable!int b; - assert(b.empty); - assert(b.equal(takeNone(b))); - Nullable!int c = a.save(); - assert(!c.empty); - c.popFront(); - assert(!a.empty); - assert(c.empty); - - assert(isRandomAccessRange!(Nullable!int)); - assert(hasLength!(Nullable!int)); - assert(hasSlicing!(Nullable!int)); - assert(hasAssignableElements!(Nullable!int)); - assert(hasSwappableElements!(Nullable!int)); - assert(hasLvalueElements!(Nullable!int)); -} - -// https://issues.dlang.org/show_bug.cgi?id=23640 -@safe pure nothrow unittest -{ - import std.algorithm.comparison : equal; - import std.range : only; - import std.range.primitives : hasLength, hasSlicing, - isRandomAccessRange; - static immutable struct S { int[] array; } - auto value = S([42]); - alias ImmutableNullable = immutable Nullable!S; - auto a = ImmutableNullable(value)[]; - alias Range = typeof(a); - assert(isRandomAccessRange!Range); - assert(hasLength!Range); - assert(hasSlicing!Range); - assert(!a.empty); - assert(a.front == value); - assert(a.back == value); - assert(a[0] == value); - assert(a.equal(only(value))); - assert(a[0 .. $].equal(only(value))); - Range b = a.save(); - assert(!b.empty); - b.popFront(); - assert(!a.empty); - assert(b.empty); -} - -// https://issues.dlang.org/show_bug.cgi?id=24403 -@safe unittest -{ - static bool destroyed; - static struct S { ~this() { destroyed = true; } } - - { - Nullable!S s = S.init; - destroyed = false; - } - assert(destroyed); - - { - Nullable!(const S) s = S.init; - destroyed = false; - } - assert(destroyed); - - { - Nullable!(immutable S) s = S.init; - destroyed = false; - } - assert(destroyed); - - { - Nullable!(shared S) s = S.init; - destroyed = false; - } - assert(destroyed); -} - -/** -Just like `Nullable!T`, except that the null state is defined as a -particular value. For example, $(D Nullable!(uint, uint.max)) is an -`uint` that sets aside the value `uint.max` to denote a null -state. $(D Nullable!(T, nullValue)) is more storage-efficient than $(D -Nullable!T) because it does not need to store an extra `bool`. - -Params: - T = The wrapped type for which Nullable provides a null value. - - nullValue = The null value which denotes the null state of this - `Nullable`. Must be of type `T`. - */ -struct Nullable(T, T nullValue) -{ - private T _value = nullValue; - -/** -Constructor initializing `this` with `value`. - -Params: - value = The value to initialize this `Nullable` with. - */ - this(T value) - { - _value = value; - } - - template toString() - { - import std.format.spec : FormatSpec; - import std.format.write : formatValue; - // Needs to be a template because of https://issues.dlang.org/show_bug.cgi?id=13737. - void toString()(scope void delegate(const(char)[]) sink, scope const ref FormatSpec!char fmt) - { - if (isNull) - { - sink.formatValue("Nullable.null", fmt); - } - else - { - sink.formatValue(_value, fmt); - } - } - - void toString()(scope void delegate(const(char)[]) sink, scope const ref FormatSpec!char fmt) const - { - if (isNull) - { - sink.formatValue("Nullable.null", fmt); - } - else - { - sink.formatValue(_value, fmt); - } - } - } - -@system unittest -{ - import std.conv : to; - - const Nullable!(ulong, 0) x = 1; - assert(x.to!string == "1"); -} - -/** -Check if `this` is in the null state. - -Returns: - true $(B iff) `this` is in the null state, otherwise false. - */ - @property bool isNull() const - { - //Need to use 'is' if T is a nullable type and - //nullValue is null, or it's a compiler error - static if (is(CommonType!(T, typeof(null)) == T) && nullValue is null) - { - return _value is nullValue; - } - //Need to use 'is' if T is a float type - //because NaN != NaN - else static if (__traits(isFloating, T) || __traits(compiles, { static assert(!(nullValue == nullValue)); })) - { - return _value is nullValue; - } - else - { - return _value == nullValue; - } - } - -/// -@safe unittest -{ - Nullable!(int, -1) ni; - //Initialized to "null" state - assert(ni.isNull); - - ni = 0; - assert(!ni.isNull); -} - -@system unittest -{ - assert(typeof(this).init.isNull, typeof(this).stringof ~ - ".isNull does not work correctly because " ~ T.stringof ~ - " has an == operator that is non-reflexive and could not be" ~ - " determined before runtime to be non-reflexive!"); -} - -// https://issues.dlang.org/show_bug.cgi?id=11135 -// disable test until https://issues.dlang.org/show_bug.cgi?id=15316 gets fixed -version (none) @system unittest -{ - static foreach (T; AliasSeq!(float, double, real)) - {{ - Nullable!(T, T.init) nf; - //Initialized to "null" state - assert(nf.isNull); - assert(nf is typeof(nf).init); - - nf = 0; - assert(!nf.isNull); - - nf.nullify(); - assert(nf.isNull); - }} -} - -/** -Forces `this` to the null state. - */ - void nullify()() - { - _value = nullValue; - } - -/// -@safe unittest -{ - Nullable!(int, -1) ni = 0; - assert(!ni.isNull); - - ni = -1; - assert(ni.isNull); -} - -/** -Assigns `value` to the internally-held state. If the assignment -succeeds, `this` becomes non-null. No null checks are made. Note -that the assignment may leave `this` in the null state. - -Params: - value = A value of type `T` to assign to this `Nullable`. - If it is `nullvalue`, then the internal state of - this `Nullable` will be set to null. - */ - void opAssign()(T value) - { - import std.algorithm.mutation : swap; - - swap(value, _value); - } - -/** - If this `Nullable` wraps a type that already has a null value - (such as a pointer), and that null value is not given for - `nullValue`, then assigning the null value to this `Nullable` - is no different than assigning any other value of type `T`, - and the resulting code will look very strange. It is strongly - recommended that this be avoided by using `T`'s "built in" - null value for `nullValue`. - */ -@system unittest -{ - //Passes - enum nullVal = cast(int*) 0xCAFEBABE; - Nullable!(int*, nullVal) npi; - assert(npi.isNull); - - //Passes?! - npi = null; - assert(!npi.isNull); -} - -/** -Gets the value. `this` must not be in the null state. -This function is also called for the implicit conversion to `T`. - -Preconditions: `isNull` must be `false`. -Returns: - The value held internally by this `Nullable`. - */ - @property ref inout(T) get() inout - { - //@@@6169@@@: We avoid any call that might evaluate nullValue's %s, - //Because it might messup get's purity and safety inference. - enum message = "Called `get' on null Nullable!(" ~ T.stringof ~ ",nullValue)."; - assert(!isNull, message); - return _value; - } - -/// -@system unittest -{ - import std.exception : assertThrown, assertNotThrown; - - Nullable!(int, -1) ni; - //`get` is implicitly called. Will throw - //an error in non-release mode - assertThrown!Throwable(ni == 0); - - ni = 0; - assertNotThrown!Throwable(ni == 0); -} - -/** -Implicitly converts to `T`. -`this` must not be in the null state. - */ - alias get this; -} - -/// ditto -auto nullable(alias nullValue, T)(T t) -if (is (typeof(nullValue) == T)) -{ - return Nullable!(T, nullValue)(t); -} - -/// -@safe unittest -{ - Nullable!(size_t, size_t.max) indexOf(string[] haystack, string needle) - { - //Find the needle, returning -1 if not found - - return Nullable!(size_t, size_t.max).init; - } - - void sendLunchInvite(string name) - { - } - - //It's safer than C... - auto coworkers = ["Jane", "Jim", "Marry", "Fred"]; - auto pos = indexOf(coworkers, "Bob"); - if (!pos.isNull) - { - //Send Bob an invitation to lunch - sendLunchInvite(coworkers[pos]); - } - else - { - //Bob not found; report the error - } - - //And there's no overhead - static assert(Nullable!(size_t, size_t.max).sizeof == size_t.sizeof); -} - -/// -@system unittest -{ - import std.exception : assertThrown; - - Nullable!(int, int.min) a; - assert(a.isNull); - assertThrown!Throwable(a.get); - a = 5; - assert(!a.isNull); - assert(a == 5); - static assert(a.sizeof == int.sizeof); -} - -/// -@safe unittest -{ - auto a = nullable!(int.min)(8); - assert(a == 8); - a.nullify(); - assert(a.isNull); -} - -@nogc nothrow pure @safe unittest -{ - // https://issues.dlang.org/show_bug.cgi?id=19226 - // fully handle non-self-equal nullValue - static struct Fraction - { - int denominator; - bool isNaN() const - { - return denominator == 0; - } - bool opEquals(const Fraction rhs) const - { - return !isNaN && denominator == rhs.denominator; - } - } - alias N = Nullable!(Fraction, Fraction.init); - assert(N.init.isNull); -} - -@safe unittest -{ - static int f(scope const Nullable!(int, int.min) x) { - return x.isNull ? 42 : x.get; - } - Nullable!(int, int.min) a; - assert(f(a) == 42); - a = 8; - assert(f(a) == 8); - a.nullify(); - assert(f(a) == 42); -} -@safe unittest -{ - // Ensure Nullable can be used in pure/nothrow/@safe environment. - function() @safe pure nothrow - { - Nullable!(int, int.min) n; - assert(n.isNull); - n = 4; - assert(!n.isNull); - assert(n == 4); - n.nullify(); - assert(n.isNull); - }(); -} -@system unittest -{ - // Ensure Nullable can be used when the value is not pure/nothrow/@system - static struct S - { - int x; - bool opEquals(const S s) const @system { return s.x == x; } - } - - Nullable!(S, S(711)) s; - assert(s.isNull); - s = S(5); - assert(!s.isNull); - assert(s.x == 5); - s.nullify(); - assert(s.isNull); -} -@safe unittest -{ - //Check nullable is nicelly embedable in a struct - static struct S1 - { - Nullable!(int, 0) ni; - } - static struct S2 //inspired from 9404 - { - Nullable!(int, 0) ni; - this(S2 other) - { - ni = other.ni; - } - void opAssign(S2 other) - { - ni = other.ni; - } - } - static foreach (S; AliasSeq!(S1, S2)) - {{ - S a; - S b = a; - S c; - c = a; - }} -} -@system unittest -{ - import std.conv : to; - - // https://issues.dlang.org/show_bug.cgi?id=10915 - Nullable!(int, 1) ni = 1; - assert(ni.to!string() == "Nullable.null"); - - struct Test { string s; } - alias NullableTest = Nullable!(Test, Test("null")); - - NullableTest nt = Test("test"); - assert(nt.to!string() == `Test("test")`); - - NullableTest ntn = Test("null"); - assert(ntn.to!string() == "Nullable.null"); - - class TestToString - { - double d; - - this(double d) - { - this.d = d; - } - - override string toString() - { - return d.to!string(); - } - } - alias NullableTestToString = Nullable!(TestToString, null); - - NullableTestToString ntts = new TestToString(2.5); - assert(ntts.to!string() == "2.5"); -} - -// apply -/** -Unpacks the content of a `Nullable`, performs an operation and packs it again. Does nothing if isNull. - -When called on a `Nullable`, `apply` will unpack the value contained in the `Nullable`, -pass it to the function you provide and wrap the result in another `Nullable` (if necessary). -If the `Nullable` is null, `apply` will return null itself. - -Params: - t = a `Nullable` - fun = a function operating on the content of the nullable - -Returns: - `fun(t.get).nullable` if `!t.isNull`, else `Nullable.init`. - -See also: - $(HTTPS en.wikipedia.org/wiki/Monad_(functional_programming)#The_Maybe_monad, The `Maybe` monad) -*/ -template apply(alias fun) -{ - import std.functional : unaryFun; - - auto apply(T)(auto ref T t) - if (isInstanceOf!(Nullable, T)) - { - alias FunType = typeof(unaryFun!fun(T.init.get)); - - enum MustWrapReturn = !isInstanceOf!(Nullable, FunType); - - static if (MustWrapReturn) - { - alias ReturnType = Nullable!FunType; - } - else - { - alias ReturnType = FunType; - } - - if (!t.isNull) - { - static if (MustWrapReturn) - { - return unaryFun!fun(t.get).nullable; - } - else - { - return unaryFun!fun(t.get); - } - } - else - { - return ReturnType.init; - } - } -} - -/// -nothrow pure @nogc @safe unittest -{ - alias toFloat = i => cast(float) i; - - Nullable!int sample; - - // apply(null) results in a null `Nullable` of the function's return type. - Nullable!float f = sample.apply!toFloat; - assert(sample.isNull && f.isNull); - - sample = 3; - - // apply(non-null) calls the function and wraps the result in a `Nullable`. - f = sample.apply!toFloat; - assert(!sample.isNull && !f.isNull); - assert(f.get == 3.0f); -} - -/// -nothrow pure @nogc @safe unittest -{ - alias greaterThree = i => (i > 3) ? i.nullable : Nullable!(typeof(i)).init; - - Nullable!int sample; - - // when the function already returns a `Nullable`, that `Nullable` is not wrapped. - auto result = sample.apply!greaterThree; - assert(sample.isNull && result.isNull); - - // The function may decide to return a null `Nullable`. - sample = 3; - result = sample.apply!greaterThree; - assert(!sample.isNull && result.isNull); - - // Or it may return a value already wrapped in a `Nullable`. - sample = 4; - result = sample.apply!greaterThree; - assert(!sample.isNull && !result.isNull); - assert(result.get == 4); -} - -// test that Nullable.get(default) can merge types -@safe @nogc nothrow pure -unittest -{ - Nullable!ubyte sample = Nullable!ubyte(); - - // Test that get(U) returns the common type of the Nullable type and the parameter type. - assert(sample.get(1000) == 1000); -} - -// Workaround for https://issues.dlang.org/show_bug.cgi?id=20670 -@safe @nogc nothrow pure -unittest -{ - immutable struct S { } - - S[] array = Nullable!(S[])().get(S[].init); -} - -// regression test for https://issues.dlang.org/show_bug.cgi?id=21199 -@safe @nogc nothrow pure -unittest -{ - struct S { int i; } - assert(S(5).nullable.apply!"a.i" == 5); -} - -// regression test for https://issues.dlang.org/show_bug.cgi?id=22176 -@safe @nogc nothrow pure -unittest -{ - struct S - { - int i; - invariant(i != 0); - - // Nullable shouldn't cause S to generate an - // opAssign that would check the invariant. - Nullable!int j; - } - S s; - s = S(5); -} - -/** -Just like `Nullable!T`, except that the object refers to a value -sitting elsewhere in memory. This makes assignments overwrite the -initially assigned value. Internally `NullableRef!T` only stores a -pointer to `T` (i.e., $(D Nullable!T.sizeof == (T*).sizeof)). - */ -struct NullableRef(T) -{ - private T* _value; - -/** -Constructor binding `this` to `value`. - -Params: - value = The value to bind to. - */ - this(T* value) @safe pure nothrow - { - _value = value; - } - - template toString() - { - import std.format.spec : FormatSpec; - import std.format.write : formatValue; - // Needs to be a template because of https://issues.dlang.org/show_bug.cgi?id=13737. - void toString()(scope void delegate(const(char)[]) sink, scope const ref FormatSpec!char fmt) - { - if (isNull) - { - sink.formatValue("Nullable.null", fmt); - } - else - { - sink.formatValue(*_value, fmt); - } - } - - void toString()(scope void delegate(const(char)[]) sink, scope const ref FormatSpec!char fmt) const - { - if (isNull) - { - sink.formatValue("Nullable.null", fmt); - } - else - { - sink.formatValue(*_value, fmt); - } - } - } - -@system unittest -{ - import std.conv : to; - - const NullableRef!(ulong) x = new ulong(1); - assert(x.to!string == "1"); -} - -/** -Binds the internal state to `value`. - -Params: - value = A pointer to a value of type `T` to bind this `NullableRef` to. - */ - void bind(T* value) @safe pure nothrow - { - _value = value; - } - - /// - @safe unittest - { - NullableRef!int nr = new int(42); - assert(nr == 42); - - int* n = new int(1); - nr.bind(n); - assert(nr == 1); - } - -/** -Returns `true` if and only if `this` is in the null state. - -Returns: - true if `this` is in the null state, otherwise false. - */ - @property bool isNull() const @safe pure nothrow - { - return _value is null; - } - - /// - @safe unittest - { - NullableRef!int nr; - assert(nr.isNull); - - int* n = new int(42); - nr.bind(n); - assert(!nr.isNull && nr == 42); - } - -/** -Forces `this` to the null state. - */ - void nullify() @safe pure nothrow - { - _value = null; - } - - /// - @safe unittest - { - NullableRef!int nr = new int(42); - assert(!nr.isNull); - - nr.nullify(); - assert(nr.isNull); - } - -/** -Assigns `value` to the internally-held state. - -Params: - value = A value of type `T` to assign to this `NullableRef`. - If the internal state of this `NullableRef` has not - been initialized, an error will be thrown in - non-release mode. - */ - void opAssign()(T value) - if (isAssignable!T) //@@@9416@@@ - { - enum message = "Called `opAssign' on null NullableRef!" ~ T.stringof ~ "."; - assert(!isNull, message); - *_value = value; - } - - /// - @system unittest - { - import std.exception : assertThrown, assertNotThrown; - - NullableRef!int nr; - assert(nr.isNull); - assertThrown!Throwable(nr = 42); - - nr.bind(new int(0)); - assert(!nr.isNull); - assertNotThrown!Throwable(nr = 42); - assert(nr == 42); - } - -/** -Gets the value. `this` must not be in the null state. -This function is also called for the implicit conversion to `T`. - */ - @property ref inout(T) get() inout @safe pure nothrow - { - enum message = "Called `get' on null NullableRef!" ~ T.stringof ~ "."; - assert(!isNull, message); - return *_value; - } - - /// - @system unittest - { - import std.exception : assertThrown, assertNotThrown; - - NullableRef!int nr; - //`get` is implicitly called. Will throw - //an error in non-release mode - assertThrown!Throwable(nr == 0); - - nr.bind(new int(0)); - assertNotThrown!Throwable(nr == 0); - } - -/** -Implicitly converts to `T`. -`this` must not be in the null state. - */ - alias get this; -} - -/// ditto -auto nullableRef(T)(T* t) -{ - return NullableRef!T(t); -} - -/// -@system unittest -{ - import std.exception : assertThrown; - - int x = 5, y = 7; - auto a = nullableRef(&x); - assert(!a.isNull); - assert(a == 5); - assert(x == 5); - a = 42; - assert(x == 42); - assert(!a.isNull); - assert(a == 42); - a.nullify(); - assert(x == 42); - assert(a.isNull); - assertThrown!Throwable(a.get); - assertThrown!Throwable(a = 71); - a.bind(&y); - assert(a == 7); - y = 135; - assert(a == 135); -} -@system unittest -{ - static int f(scope const NullableRef!int x) { - return x.isNull ? 42 : x.get; - } - int x = 5; - auto a = nullableRef(&x); - assert(f(a) == 5); - a.nullify(); - assert(f(a) == 42); -} -@safe unittest -{ - // Ensure NullableRef can be used in pure/nothrow/@safe environment. - function() @safe pure nothrow - { - auto storage = new int; - *storage = 19902; - NullableRef!int n; - assert(n.isNull); - n.bind(storage); - assert(!n.isNull); - assert(n == 19902); - n = 2294; - assert(n == 2294); - assert(*storage == 2294); - n.nullify(); - assert(n.isNull); - }(); -} -@system unittest -{ - // Ensure NullableRef can be used when the value is not pure/nothrow/@safe - static struct S - { - int x; - this(this) @system {} - bool opEquals(const S s) const @system { return s.x == x; } - } - - auto storage = S(5); - - NullableRef!S s; - assert(s.isNull); - s.bind(&storage); - assert(!s.isNull); - assert(s.x == 5); - s.nullify(); - assert(s.isNull); -} -@safe unittest -{ - //Check nullable is nicelly embedable in a struct - static struct S1 - { - NullableRef!int ni; - } - static struct S2 //inspired from 9404 - { - NullableRef!int ni; - this(S2 other) - { - ni = other.ni; - } - void opAssign(S2 other) - { - ni = other.ni; - } - } - static foreach (S; AliasSeq!(S1, S2)) - {{ - S a; - S b = a; - S c; - c = a; - }} -} - -// https://issues.dlang.org/show_bug.cgi?id=10915 -@system unittest -{ - import std.conv : to; - - NullableRef!int nri; - assert(nri.to!string() == "Nullable.null"); - - struct Test - { - string s; - } - NullableRef!Test nt = new Test("test"); - assert(nt.to!string() == `Test("test")`); - - class TestToString - { - double d; - - this(double d) - { - this.d = d; - } - - override string toString() - { - return d.to!string(); - } - } - TestToString tts = new TestToString(2.5); - NullableRef!TestToString ntts = &tts; - assert(ntts.to!string() == "2.5"); -} - - -/** -`BlackHole!Base` is a subclass of `Base` which automatically implements -all abstract member functions in `Base` as do-nothing functions. Each -auto-implemented function just returns the default value of the return type -without doing anything. - -The name came from -$(HTTP search.cpan.org/~sburke/Class-_BlackHole-0.04/lib/Class/_BlackHole.pm, Class::_BlackHole) -Perl module by Sean M. Burke. - -Params: - Base = A non-final class for `BlackHole` to inherit from. - -See_Also: - $(LREF AutoImplement), $(LREF generateEmptyFunction) - */ -alias BlackHole(Base) = AutoImplement!(Base, generateEmptyFunction, isAbstractFunction); - -/// -@system unittest -{ - import std.math.traits : isNaN; - - static abstract class C - { - int m_value; - this(int v) { m_value = v; } - int value() @property { return m_value; } - - abstract real realValue() @property; - abstract void doSomething(); - } - - auto c = new BlackHole!C(42); - assert(c.value == 42); - - // Returns real.init which is NaN - assert(c.realValue.isNaN); - // Abstract functions are implemented as do-nothing - c.doSomething(); -} - -@system unittest -{ - import std.math.traits : isNaN; - - // return default - { - interface I_1 { real test(); } - auto o = new BlackHole!I_1; - assert(o.test().isNaN()); // NaN - } - // doc example - { - static class C - { - int m_value; - this(int v) { m_value = v; } - int value() @property { return m_value; } - - abstract real realValue() @property; - abstract void doSomething(); - } - - auto c = new BlackHole!C(42); - assert(c.value == 42); - - assert(c.realValue.isNaN); // NaN - c.doSomething(); - } - - // https://issues.dlang.org/show_bug.cgi?id=12058 - interface Foo - { - inout(Object) foo() inout; - } - BlackHole!Foo o; -} - -nothrow pure @nogc @safe unittest -{ - static interface I - { - I foo() nothrow pure @nogc @safe return scope; - } - - scope cb = new BlackHole!I(); - cb.foo(); -} - - -/** -`WhiteHole!Base` is a subclass of `Base` which automatically implements -all abstract member functions as functions that always fail. These functions -simply throw an `Error` and never return. `Whitehole` is useful for -trapping the use of class member functions that haven't been implemented. - -The name came from -$(HTTP search.cpan.org/~mschwern/Class-_WhiteHole-0.04/lib/Class/_WhiteHole.pm, Class::_WhiteHole) -Perl module by Michael G Schwern. - -Params: - Base = A non-final class for `WhiteHole` to inherit from. - -See_Also: - $(LREF AutoImplement), $(LREF generateAssertTrap) - */ -alias WhiteHole(Base) = AutoImplement!(Base, generateAssertTrap, isAbstractFunction); - -/// -@system unittest -{ - import std.exception : assertThrown; - - static class C - { - abstract void notYetImplemented(); - } - - auto c = new WhiteHole!C; - assertThrown!NotImplementedError(c.notYetImplemented()); // throws an Error -} - -// https://issues.dlang.org/show_bug.cgi?id=20232 -nothrow pure @safe unittest -{ - static interface I - { - I foo() nothrow pure @safe return scope; - } - - if (0) // Just checking attribute interference - { - scope cw = new WhiteHole!I(); - cw.foo(); - } -} - -// / ditto -class NotImplementedError : Error -{ - this(string method) nothrow pure @safe - { - super(method ~ " is not implemented"); - } -} - -@system unittest -{ - import std.exception : assertThrown; - // nothrow - { - interface I_1 - { - void foo(); - void bar() nothrow; - } - auto o = new WhiteHole!I_1; - assertThrown!NotImplementedError(o.foo()); - assertThrown!NotImplementedError(o.bar()); - } - // doc example - { - static class C - { - abstract void notYetImplemented(); - } - - auto c = new WhiteHole!C; - try - { - c.notYetImplemented(); - assert(0); - } - catch (Error e) {} - } -} - - -/** -`AutoImplement` automatically implements (by default) all abstract member -functions in the class or interface `Base` in specified way. - -The second version of `AutoImplement` automatically implements -`Interface`, while deriving from `BaseClass`. - -Params: - how = template which specifies _how functions will be implemented/overridden. - - Two arguments are passed to `how`: the type `Base` and an alias - to an implemented function. Then `how` must return an implemented - function body as a string. - - The generated function body can use these keywords: - $(UL - $(LI `a0`, `a1`, …: arguments passed to the function;) - $(LI `args`: a tuple of the arguments;) - $(LI `self`: an alias to the function itself;) - $(LI `parent`: an alias to the overridden function (if any).) - ) - - You may want to use templated property functions (instead of Implicit - Template Properties) to generate complex functions: --------------------- -// Prints log messages for each call to overridden functions. -string generateLogger(C, alias fun)() @property -{ - import std.traits; - enum qname = C.stringof ~ "." ~ __traits(identifier, fun); - string stmt; - - stmt ~= q{ struct Importer { import std.stdio; } }; - stmt ~= `Importer.writeln("Log: ` ~ qname ~ `(", args, ")");`; - static if (!__traits(isAbstractFunction, fun)) - { - static if (is(ReturnType!fun == void)) - stmt ~= q{ parent(args); }; - else - stmt ~= q{ - auto r = parent(args); - Importer.writeln("--> ", r); - return r; - }; - } - return stmt; -} --------------------- - - what = template which determines _what functions should be - implemented/overridden. - - An argument is passed to `what`: an alias to a non-final member - function in `Base`. Then `what` must return a boolean value. - Return `true` to indicate that the passed function should be - implemented/overridden. - --------------------- -// Sees if fun returns something. -enum bool hasValue(alias fun) = !is(ReturnType!(fun) == void); --------------------- - - -Note: - -Generated code is inserted in the scope of `std.typecons` module. Thus, -any useful functions outside `std.typecons` cannot be used in the generated -code. To workaround this problem, you may `import` necessary things in a -local struct, as done in the `generateLogger()` template in the above -example. - - -BUGS: - -$(UL - $(LI Variadic arguments to constructors are not forwarded to super.) - $(LI Deep interface inheritance causes compile error with messages like - "Error: function std.typecons._AutoImplement!(Foo)._AutoImplement.bar - does not override any function". [$(BUGZILLA 2525)] ) - $(LI The `parent` keyword is actually a delegate to the super class' - corresponding member function. [$(BUGZILLA 2540)] ) - $(LI Using alias template parameter in `how` and/or `what` may cause - strange compile error. Use template tuple parameter instead to workaround - this problem. [$(BUGZILLA 4217)] ) -) - */ -class AutoImplement(Base, alias how, alias what = isAbstractFunction) : Base -if (!is(how == class)) -{ - private alias autoImplement_helper_ = - AutoImplement_Helper!("autoImplement_helper_", "Base", Base, typeof(this), how, what); - mixin(autoImplement_helper_.code); -} - -/// ditto -class AutoImplement( - Interface, BaseClass, alias how, - alias what = isAbstractFunction) : BaseClass, Interface -if (is(Interface == interface) && is(BaseClass == class)) -{ - private alias autoImplement_helper_ = AutoImplement_Helper!( - "autoImplement_helper_", "Interface", Interface, typeof(this), how, what); - mixin(autoImplement_helper_.code); -} - -/// -@system unittest -{ - interface PackageSupplier - { - int foo(); - int bar(); - } - - static abstract class AbstractFallbackPackageSupplier : PackageSupplier - { - protected PackageSupplier default_, fallback; - - this(PackageSupplier default_, PackageSupplier fallback) - { - this.default_ = default_; - this.fallback = fallback; - } - - abstract int foo(); - abstract int bar(); - } - - template fallback(T, alias func) - { - import std.format : format; - // for all implemented methods: - // - try default first - // - only on a failure run & return fallback - enum fallback = q{ - try - { - return default_.%1$s(args); - } - catch (Exception) - { - return fallback.%1$s(args); - } - }.format(__traits(identifier, func)); - } - - // combines two classes and use the second one as fallback - alias FallbackPackageSupplier = AutoImplement!(AbstractFallbackPackageSupplier, fallback); - - class FailingPackageSupplier : PackageSupplier - { - int foo(){ throw new Exception("failure"); } - int bar(){ return 2;} - } - - class BackupPackageSupplier : PackageSupplier - { - int foo(){ return -1; } - int bar(){ return -1;} - } - - auto registry = new FallbackPackageSupplier(new FailingPackageSupplier(), new BackupPackageSupplier()); - - assert(registry.foo() == -1); - assert(registry.bar() == 2); -} - -/* - * Code-generating stuffs are encupsulated in this helper template so that - * namespace pollution, which can cause name confliction with Base's public - * members, should be minimized. - */ -private template AutoImplement_Helper(string myName, string baseName, - Base, Self, alias generateMethodBody, alias cherrypickMethod) -{ -private static: - //::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::// - // Internal stuffs - //::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::// - - // Returns function overload sets in the class C, filtered with pred. - template enumerateOverloads(C, alias pred) - { - template Impl(names...) - { - import std.meta : Filter; - static if (names.length > 0) - { - alias methods = Filter!(pred, MemberFunctionsTuple!(C, names[0])); - alias next = Impl!(names[1 .. $]); - - static if (methods.length > 0) - alias Impl = AliasSeq!(OverloadSet!(names[0], methods), next); - else - alias Impl = next; - } - else - alias Impl = AliasSeq!(); - } - - alias enumerateOverloads = Impl!(__traits(allMembers, C)); - } - - //::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::// - // Target functions - //::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::// - - // Add a non-final check to the cherrypickMethod. - enum bool canonicalPicker(fun.../+[https://issues.dlang.org/show_bug.cgi?id=4217]+/) = - !__traits(isFinalFunction, fun[0]) && cherrypickMethod!(fun); - - /* - * A tuple of overload sets, each item of which consists of functions to be - * implemented by the generated code. - */ - alias targetOverloadSets = enumerateOverloads!(Base, canonicalPicker); - - /* - * Super class of this AutoImplement instance - */ - alias Super = BaseTypeTuple!(Self)[0]; - static assert(is(Super == class)); - static assert(is(Base == interface) || is(Super == Base)); - - /* - * A tuple of the super class' constructors. Used for forwarding - * constructor calls. - */ - static if (__traits(hasMember, Super, "__ctor")) - alias ctorOverloadSet = OverloadSet!("__ctor", __traits(getOverloads, Super, "__ctor")); - else - alias ctorOverloadSet = OverloadSet!("__ctor"); // empty - - - //::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::// - // Type information - //::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::// - - /* - * The generated code will be mixed into AutoImplement, which will be - * instantiated in this module's scope. Thus, any user-defined types are - * out of scope and cannot be used directly (i.e. by their names). - * - * We will use FuncInfo instances for accessing return types and parameter - * types of the implemented functions. The instances will be populated to - * the AutoImplement's scope in a certain way; see the populate() below. - */ - - // Returns the preferred identifier for the FuncInfo instance for the i-th - // overloaded function with the name. - template INTERNAL_FUNCINFO_ID(string name, size_t i) - { - import std.format : format; - - enum string INTERNAL_FUNCINFO_ID = format("F_%s_%s", name, i); - } - - /* - * Insert FuncInfo instances about all the target functions here. This - * enables the generated code to access type information via, for example, - * "autoImplement_helper_.F_foo_1". - */ - template populate(overloads...) - { - static if (overloads.length > 0) - { - mixin populate!(overloads[0].name, overloads[0].contents); - mixin populate!(overloads[1 .. $]); - } - } - template populate(string name, methods...) - { - static if (methods.length > 0) - { - mixin populate!(name, methods[0 .. $ - 1]); - // - alias target = methods[$ - 1]; - enum ith = methods.length - 1; - mixin("alias " ~ INTERNAL_FUNCINFO_ID!(name, ith) ~ " = FuncInfo!target;"); - } - } - - public mixin populate!(targetOverloadSets); - public mixin populate!( ctorOverloadSet ); - - - //::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::// - // Code-generating policies - //::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::// - - /* Common policy configurations for generating constructors and methods. */ - template CommonGeneratingPolicy() - { - // base class identifier which generated code should use - enum string BASE_CLASS_ID = baseName; - - // FuncInfo instance identifier which generated code should use - template FUNCINFO_ID(string name, size_t i) - { - enum string FUNCINFO_ID = - myName ~ "." ~ INTERNAL_FUNCINFO_ID!(name, i); - } - } - - /* Policy configurations for generating constructors. */ - template ConstructorGeneratingPolicy() - { - mixin CommonGeneratingPolicy; - - /* Generates constructor body. Just forward to the base class' one. */ - string generateFunctionBody(ctor.../+[https://issues.dlang.org/show_bug.cgi?id=4217]+/)() @property - { - enum varstyle = variadicFunctionStyle!(typeof(&ctor[0])); - - static if (varstyle & (Variadic.c | Variadic.d)) - { - // the argptr-forwarding problem - //pragma(msg, "Warning: AutoImplement!(", Base, ") ", - // "ignored variadic arguments to the constructor ", - // FunctionTypeOf!(typeof(&ctor[0])) ); - } - return "super(args);"; - } - } - - /* Policy configurations for genearting target methods. */ - template MethodGeneratingPolicy() - { - mixin CommonGeneratingPolicy; - - /* Geneartes method body. */ - string generateFunctionBody(func.../+[https://issues.dlang.org/show_bug.cgi?id=4217]+/)() @property - { - return generateMethodBody!(Base, func); // given - } - } - - - //::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::// - // Generated code - //::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::// - - alias ConstructorGenerator = MemberFunctionGenerator!(ConstructorGeneratingPolicy!()); - alias MethodGenerator = MemberFunctionGenerator!(MethodGeneratingPolicy!()); - - public enum string code = - ConstructorGenerator.generateCode!( ctorOverloadSet ) ~ "\n" ~ - MethodGenerator.generateCode!(targetOverloadSets); - - debug (SHOW_GENERATED_CODE) - { - pragma(msg, "-------------------- < ", Base, " >"); - pragma(msg, code); - pragma(msg, "--------------------"); - } -} - -//debug = SHOW_GENERATED_CODE; -@system unittest -{ - import core.vararg; - // no function to implement - { - interface I_1 {} - auto o = new BlackHole!I_1; - } - // parameters - { - interface I_3 { void test(int, in int, out int, ref int, lazy int); } - auto o = new BlackHole!I_3; - } - // use of user-defined type - { - struct S {} - interface I_4 { S test(); } - auto o = new BlackHole!I_4; - } - // overloads - { - interface I_5 - { - void test(string); - real test(real); - int test(); - } - auto o = new BlackHole!I_5; - } - // constructor forwarding - { - static class C_6 - { - this(int n) { assert(n == 42); } - this(string s) { assert(s == "Deeee"); } - this(...) {} - } - auto o1 = new BlackHole!C_6(42); - auto o2 = new BlackHole!C_6("Deeee"); - auto o3 = new BlackHole!C_6(1, 2, 3, 4); - } - // attributes - { - interface I_7 - { - ref int test_ref(); - int test_pure() pure; - int test_nothrow() nothrow; - int test_property() @property; - int test_safe() @safe; - int test_trusted() @trusted; - int test_system() @system; - int test_pure_nothrow() pure nothrow; - } - auto o = new BlackHole!I_7; - } - // storage classes - { - interface I_8 - { - void test_const() const; - void test_immutable() immutable; - void test_shared() shared; - void test_shared_const() shared const; - } - auto o = new BlackHole!I_8; - } - // use baseclass - { - static class C_9 - { - private string foo_; - - this(string s) { - foo_ = s; - } - - protected string boilerplate() @property - { - return "Boilerplate stuff."; - } - - public string foo() @property - { - return foo_; - } - } - - interface I_10 - { - string testMethod(size_t); - } - - static string generateTestMethod(C, alias fun)() @property - { - return "return this.boilerplate[0 .. a0];"; - } - - auto o = new AutoImplement!(I_10, C_9, generateTestMethod)("Testing"); - assert(o.testMethod(11) == "Boilerplate"); - assert(o.foo == "Testing"); - } - /+ // deep inheritance - { - // https://issues.dlang.org/show_bug.cgi?id=2525 - // https://issues.dlang.org/show_bug.cgi?id=3525 - // NOTE: [r494] func.c(504-571) FuncDeclaration::semantic() - interface I { void foo(); } - interface J : I {} - interface K : J {} - static abstract class C_9 : K {} - auto o = new BlackHole!C_9; - }+/ - // test `parent` alias - { - interface I_11 - { - void simple(int) @safe; - int anotherSimple(string); - int overloaded(int); - /+ XXX [BUG 19715] - void overloaded(string) @safe; - +/ - } - - static class C_11 - { - import std.traits : Parameters, ReturnType; - import std.meta : Alias; - - protected ReturnType!fn _impl(alias fn)(Parameters!fn) - if (is(Alias!(__traits(parent, fn)) == interface)) - { - static if (!is(typeof(return) == void)) - return typeof(return).init; - } - } - - template tpl(I, alias fn) - if (is(I == interface) && __traits(isSame, __traits(parent, fn), I)) - { - enum string tpl = q{ - enum bool haveReturn = !is(typeof(return) == void); - - static if (is(typeof(return) == void)) - _impl!parent(args); - else - return _impl!parent(args); - }; - } - - auto o = new AutoImplement!(I_11, C_11, tpl); - } -} - -// https://issues.dlang.org/show_bug.cgi?id=17177 -// AutoImplement fails on function overload sets with -// "cannot infer type from overloaded function symbol" -@system unittest -{ - static class Issue17177 - { - private string n_; - - public { - Issue17177 overloaded(string n) - { - this.n_ = n; - - return this; - } - - string overloaded() - { - return this.n_; - } - } - } - - static string how(C, alias fun)() - { - static if (!is(ReturnType!fun == void)) - { - return q{ - return parent(args); - }; - } - else - { - return q{ - parent(args); - }; - } - } - - import std.meta : templateNot; - alias Implementation = AutoImplement!(Issue17177, how, templateNot!isFinalFunction); -} - -version (StdUnittest) -{ - // https://issues.dlang.org/show_bug.cgi?id=10647 - // Add prefix "issue10647_" as a workaround for - // https://issues.dlang.org/show_bug.cgi?id=1238 - private string issue10647_generateDoNothing(C, alias fun)() @property - { - string stmt; - - static if (is(ReturnType!fun == void)) - stmt ~= ""; - else - { - string returnType = ReturnType!fun.stringof; - stmt ~= "return "~returnType~".init;"; - } - return stmt; - } - - private template issue10647_isAlwaysTrue(alias fun) - { - enum issue10647_isAlwaysTrue = true; - } - - // Do nothing template - private template issue10647_DoNothing(Base) - { - alias issue10647_DoNothing = AutoImplement!(Base, issue10647_generateDoNothing, issue10647_isAlwaysTrue); - } - - // A class to be overridden - private class issue10647_Foo{ - void bar(int a) { } - } -} - -@system unittest -{ - auto foo = new issue10647_DoNothing!issue10647_Foo(); - foo.bar(13); -} - -/* -Used by MemberFunctionGenerator. - */ -package template OverloadSet(string nam, T...) -{ - enum string name = nam; - alias contents = T; -} - -/* -Used by MemberFunctionGenerator. - */ -package template FuncInfo(alias func) -if (is(typeof(&func))) -{ - alias RT = ReturnType!(typeof(&func)); - alias PT = Parameters!(typeof(&func)); -} -package template FuncInfo(Func) -{ - alias RT = ReturnType!Func; - alias PT = Parameters!Func; -} - -/* -General-purpose member function generator. --------------------- -template GeneratingPolicy() -{ - // [optional] the name of the class where functions are derived - enum string BASE_CLASS_ID; - - // [optional] define this if you have only function types - enum bool WITHOUT_SYMBOL; - - // [optional] Returns preferred identifier for i-th parameter. - template PARAMETER_VARIABLE_ID(size_t i); - - // Returns the identifier of the FuncInfo instance for the i-th overload - // of the specified name. The identifier must be accessible in the scope - // where generated code is mixed. - template FUNCINFO_ID(string name, size_t i); - - // Returns implemented function body as a string. When WITHOUT_SYMBOL is - // defined, the latter is used. - template generateFunctionBody(alias func); - template generateFunctionBody(string name, FuncType); -} --------------------- - */ -package template MemberFunctionGenerator(alias Policy) -{ -private static: - //::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::// - // Internal stuffs - //::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::// - import std.format; - alias format = std.format.format; - - enum CONSTRUCTOR_NAME = "__ctor"; - - // true if functions are derived from a base class - enum WITH_BASE_CLASS = __traits(hasMember, Policy, "BASE_CLASS_ID"); - - // true if functions are specified as types, not symbols - enum WITHOUT_SYMBOL = __traits(hasMember, Policy, "WITHOUT_SYMBOL"); - - // preferred identifier for i-th parameter variable - static if (__traits(hasMember, Policy, "PARAMETER_VARIABLE_ID")) - { - alias PARAMETER_VARIABLE_ID = Policy.PARAMETER_VARIABLE_ID; - } - else - { - enum string PARAMETER_VARIABLE_ID(size_t i) = format("a%s", i); - // default: a0, a1, ... - } - - // Returns a tuple consisting of 0,1,2,...,n-1. For static foreach. - template CountUp(size_t n) - { - static if (n > 0) - alias CountUp = AliasSeq!(CountUp!(n - 1), n - 1); - else - alias CountUp = AliasSeq!(); - } - - - //::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::// - // Code generator - //::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::// - - /* - * Runs through all the target overload sets and generates D code which - * implements all the functions in the overload sets. - */ - public string generateCode(overloads...)() @property - { - string code = ""; - - // run through all the overload sets - foreach (i_; CountUp!(0 + overloads.length)) // workaround - { - enum i = 0 + i_; // workaround - alias oset = overloads[i]; - - code ~= generateCodeForOverloadSet!(oset); - - static if (WITH_BASE_CLASS && oset.name != CONSTRUCTOR_NAME) - { - // The generated function declarations may hide existing ones - // in the base class (cf. HiddenFuncError), so we put an alias - // declaration here to reveal possible hidden functions. - code ~= format("alias %s = %s.%s;\n", - oset.name, - // super: https://issues.dlang.org/show_bug.cgi?id=2540 - Policy.BASE_CLASS_ID, - oset.name); - } - } - return code; - } - - // handle each overload set - string generateCodeForOverloadSet(alias oset)() @property - { - string code = ""; - - foreach (i_; CountUp!(0 + oset.contents.length)) // workaround - { - enum i = 0 + i_; // workaround - code ~= generateFunction!( - Policy.FUNCINFO_ID!(oset.name, i), oset.name, - oset.contents[i]) ~ "\n"; - } - return code; - } - - /* - * Returns D code which implements the function func. This function - * actually generates only the declarator part; the function body part is - * generated by the functionGenerator() policy. - */ - public string generateFunction( - string myFuncInfo, string name, func... )() @property - { - import std.format : format; - - enum isCtor = (name == CONSTRUCTOR_NAME); - - string code; // the result - - auto paramsRes = generateParameters!(myFuncInfo, func)(); - code ~= paramsRes.imports; - - /*** Function Declarator ***/ - { - alias Func = FunctionTypeOf!(func); - alias FA = FunctionAttribute; - enum atts = functionAttributes!(func); - enum realName = isCtor ? "this" : name; - - // FIXME?? Make it so that these aren't CTFE funcs any more, since - // Format is deprecated, and format works at compile time? - /* Made them CTFE funcs just for the sake of Format!(...) */ - - // return type with optional "ref" - static string make_returnType() - { - string rtype = ""; - - if (!isCtor) - { - if (atts & FA.ref_) rtype ~= "ref "; - rtype ~= myFuncInfo ~ ".RT"; - } - return rtype; - } - enum returnType = make_returnType(); - - // function attributes attached after declaration - static string make_postAtts() - { - string poatts = ""; - if (atts & FA.pure_ ) poatts ~= " pure"; - if (atts & FA.nothrow_) poatts ~= " nothrow"; - if (atts & FA.property) poatts ~= " @property"; - if (atts & FA.safe ) poatts ~= " @safe"; - if (atts & FA.trusted ) poatts ~= " @trusted"; - if (atts & FA.scope_ ) poatts ~= " scope"; - if (atts & FA.return_ ) poatts ~= " return"; - return poatts; - } - enum postAtts = make_postAtts(); - - // function storage class - static string make_storageClass() - { - string postc = ""; - if (is(Func == shared)) postc ~= " shared"; - if (is(Func == const)) postc ~= " const"; - if (is(Func == inout)) postc ~= " inout"; - if (is(Func == immutable)) postc ~= " immutable"; - return postc; - } - enum storageClass = make_storageClass(); - - // - if (__traits(isVirtualMethod, func)) - code ~= "override "; - code ~= format("extern(%s) %s %s(%s) %s %s\n", - functionLinkage!(func), - returnType, - realName, - paramsRes.params, - postAtts, storageClass ); - } - - /*** Function Body ***/ - code ~= "{\n"; - { - enum nparams = Parameters!(func).length; - - /* Declare keywords: args, self and parent. */ - string preamble; - - preamble ~= "alias args = AliasSeq!(" ~ enumerateParameters!(nparams) ~ ");\n"; - if (!isCtor) - { - preamble ~= "alias self = " ~ name ~ ";\n"; - static if (WITH_BASE_CLASS) - preamble ~= `alias parent = __traits(getMember, ` ~ Policy.BASE_CLASS_ID ~ `, "` ~ name ~ `");`; - } - - // Function body - static if (WITHOUT_SYMBOL) - enum fbody = Policy.generateFunctionBody!(name, func); - else - enum fbody = Policy.generateFunctionBody!(func); - - code ~= preamble; - code ~= fbody; - } - code ~= "}"; - - return code; - } - - /* - * Returns D code which declares function parameters, - * and optionally any imports (e.g. core.vararg) - * "ref int a0, real a1, ..." - */ - static struct GenParams { string imports, params; } - GenParams generateParameters(string myFuncInfo, func...)() - { - alias STC = ParameterStorageClass; - alias stcs = ParameterStorageClassTuple!(func); - enum nparams = stcs.length; - - string imports = ""; // any imports required - string params = ""; // parameters - - foreach (i, stc; stcs) - { - if (i > 0) params ~= ", "; - - // Parameter storage classes. - if (stc & STC.scope_) params ~= "scope "; - if (stc & STC.in_) params ~= "in "; - if (stc & STC.out_ ) params ~= "out "; - if (stc & STC.ref_ ) params ~= "ref "; - if (stc & STC.lazy_ ) params ~= "lazy "; - - // Take parameter type from the FuncInfo. - params ~= format("%s.PT[%s]", myFuncInfo, i); - - // Declare a parameter variable. - params ~= " " ~ PARAMETER_VARIABLE_ID!(i); - } - - // Add some ellipsis part if needed. - auto style = variadicFunctionStyle!(func); - final switch (style) - { - case Variadic.no: - break; - - case Variadic.c, Variadic.d: - imports ~= "import core.vararg;\n"; - // (...) or (a, b, ...) - params ~= (nparams == 0) ? "..." : ", ..."; - break; - - case Variadic.typesafe: - params ~= " ..."; - break; - } - - return typeof(return)(imports, params); - } - - // Returns D code which enumerates n parameter variables using comma as the - // separator. "a0, a1, a2, a3" - string enumerateParameters(size_t n)() @property - { - string params = ""; - - foreach (i_; CountUp!(n)) - { - enum i = 0 + i_; // workaround - if (i > 0) params ~= ", "; - params ~= PARAMETER_VARIABLE_ID!(i); - } - return params; - } -} - - -/** -Predefined how-policies for `AutoImplement`. These templates are also used by -`BlackHole` and `WhiteHole`, respectively. - */ -template generateEmptyFunction(C, func.../+[https://issues.dlang.org/show_bug.cgi?id=4217]+/) -{ - static if (is(ReturnType!(func) == void)) - enum string generateEmptyFunction = q{ - }; - else static if (functionAttributes!(func) & FunctionAttribute.ref_) - enum string generateEmptyFunction = q{ - static typeof(return) dummy; - return dummy; - }; - else - enum string generateEmptyFunction = q{ - return typeof(return).init; - }; -} - -/// -@system unittest -{ - alias BlackHole(Base) = AutoImplement!(Base, generateEmptyFunction); - - interface I - { - int foo(); - string bar(); - } - - auto i = new BlackHole!I(); - // generateEmptyFunction returns the default value of the return type without doing anything - assert(i.foo == 0); - assert(i.bar is null); -} - -/// ditto -template generateAssertTrap(C, func...) -{ - enum string generateAssertTrap = - `throw new NotImplementedError("` ~ C.stringof ~ "." - ~ __traits(identifier, func) ~ `");`; -} - -/// -@system unittest -{ - import std.exception : assertThrown; - - alias WhiteHole(Base) = AutoImplement!(Base, generateAssertTrap); - - interface I - { - int foo(); - string bar(); - } - - auto i = new WhiteHole!I(); - // generateAssertTrap throws an exception for every unimplemented function of the interface - assertThrown!NotImplementedError(i.foo); - assertThrown!NotImplementedError(i.bar); -} - -private -{ - pragma(mangle, "_d_toObject") - extern(C) pure nothrow Object typecons_d_toObject(void* p); -} - -/* - * Avoids opCast operator overloading. - */ -private template dynamicCast(T) -if (is(T == class) || is(T == interface)) -{ - @trusted - T dynamicCast(S)(inout S source) - if (is(S == class) || is(S == interface)) - { - static if (is(Unqual!S : Unqual!T)) - { - import std.traits : QualifierOf; - alias Qual = QualifierOf!S; // SharedOf or MutableOf - alias TmpT = Qual!(Unqual!T); - inout(TmpT) tmp = source; // bypass opCast by implicit conversion - return *cast(T*)(&tmp); // + variable pointer cast + dereference - } - else - { - return cast(T) typecons_d_toObject(*cast(void**)(&source)); - } - } -} - -@system unittest -{ - class C { @disable void opCast(T)(); } - auto c = new C; - static assert(!__traits(compiles, cast(Object) c)); - auto o = dynamicCast!Object(c); - assert(c is o); - - interface I { @disable void opCast(T)(); Object instance(); } - interface J { @disable void opCast(T)(); Object instance(); } - class D : I, J { Object instance() { return this; } } - I i = new D(); - static assert(!__traits(compiles, cast(J) i)); - J j = dynamicCast!J(i); - assert(i.instance() is j.instance()); -} - -/** -Supports structural based typesafe conversion. - -If `Source` has structural conformance with the `interface` `Targets`, -wrap creates an internal wrapper class which inherits `Targets` and -wraps the `src` object, then returns it. - -`unwrap` can be used to extract objects which have been wrapped by `wrap`. -*/ -template wrap(Targets...) -if (Targets.length >= 1 && allSatisfy!(isMutable, Targets)) -{ - import std.meta : staticMap; - - // strict upcast - auto wrap(Source)(inout Source src) @trusted pure nothrow - if (Targets.length == 1 && is(Source : Targets[0])) - { - alias T = Select!(is(Source == shared), shared Targets[0], Targets[0]); - return dynamicCast!(inout T)(src); - } - // structural upcast - template wrap(Source) - if (!allSatisfy!(Bind!(isImplicitlyConvertible, Source), Targets)) - { - auto wrap(inout Source src) - { - static assert(hasRequireMethods!(), - "Source "~Source.stringof~ - " does not have structural conformance to "~ - Targets.stringof); - - alias T = Select!(is(Source == shared), shared Impl, Impl); - return new inout T(src); - } - - template FuncInfo(string s, F) - { - enum name = s; - alias type = F; - } - - // https://issues.dlang.org/show_bug.cgi?id=12064: Remove NVI members - template OnlyVirtual(members...) - { - enum notFinal(alias T) = !__traits(isFinalFunction, T); - import std.meta : Filter; - alias OnlyVirtual = Filter!(notFinal, members); - } - - // Concat all Targets function members into one tuple - template Concat(size_t i = 0) - { - static if (i >= Targets.length) - alias Concat = AliasSeq!(); - else - { - alias Concat = AliasSeq!(OnlyVirtual!(GetOverloadedMethods!(Targets[i]), Concat!(i + 1))); - } - } - - // Remove duplicated functions based on the identifier name and function type covariance - template Uniq(members...) - { - static if (members.length == 0) - alias Uniq = AliasSeq!(); - else - { - alias func = members[0]; - enum name = __traits(identifier, func); - alias type = FunctionTypeOf!func; - template check(size_t i, mem...) - { - static if (i >= mem.length) - enum ptrdiff_t check = -1; - else - { - enum ptrdiff_t check = - __traits(identifier, func) == __traits(identifier, mem[i]) && - !is(DerivedFunctionType!(type, FunctionTypeOf!(mem[i])) == void) - ? i : check!(i + 1, mem); - } - } - enum ptrdiff_t x = 1 + check!(0, members[1 .. $]); - static if (x >= 1) - { - alias typex = DerivedFunctionType!(type, FunctionTypeOf!(members[x])); - alias remain = Uniq!(members[1 .. x], members[x + 1 .. $]); - - static if (remain.length >= 1 && remain[0].name == name && - !is(DerivedFunctionType!(typex, remain[0].type) == void)) - { - alias F = DerivedFunctionType!(typex, remain[0].type); - alias Uniq = AliasSeq!(FuncInfo!(name, F), remain[1 .. $]); - } - else - alias Uniq = AliasSeq!(FuncInfo!(name, typex), remain); - } - else - { - alias Uniq = AliasSeq!(FuncInfo!(name, type), Uniq!(members[1 .. $])); - } - } - } - alias TargetMembers = Uniq!(Concat!()); // list of FuncInfo - alias SourceMembers = GetOverloadedMethods!Source; // list of function symbols - - // Check whether all of SourceMembers satisfy covariance target in TargetMembers - template hasRequireMethods(size_t i = 0) - { - static if (i >= TargetMembers.length) - enum hasRequireMethods = true; - else - { - enum hasRequireMethods = - findCovariantFunction!(TargetMembers[i], Source, SourceMembers) != -1 && - hasRequireMethods!(i + 1); - } - } - - // Internal wrapper class - final class Impl : Structural, Targets - { - private: - Source _wrap_source; - - this( inout Source s) inout @safe pure nothrow { _wrap_source = s; } - this(shared inout Source s) shared inout @safe pure nothrow { _wrap_source = s; } - - // BUG: making private should work with NVI. - protected final inout(Object) _wrap_getSource() inout @trusted - { - return dynamicCast!(inout Object)(_wrap_source); - } - - import std.conv : to; - import core.lifetime : forward; - template generateFun(size_t i) - { - enum name = TargetMembers[i].name; - enum fa = functionAttributes!(TargetMembers[i].type); - static @property stc() - { - string r; - if (fa & FunctionAttribute.property) r ~= "@property "; - if (fa & FunctionAttribute.ref_) r ~= "ref "; - if (fa & FunctionAttribute.pure_) r ~= "pure "; - if (fa & FunctionAttribute.nothrow_) r ~= "nothrow "; - if (fa & FunctionAttribute.trusted) r ~= "@trusted "; - if (fa & FunctionAttribute.safe) r ~= "@safe "; - return r; - } - static @property mod() - { - alias type = AliasSeq!(TargetMembers[i].type)[0]; - string r; - static if (is(type == immutable)) r ~= " immutable"; - else - { - static if (is(type == shared)) r ~= " shared"; - static if (is(type == const)) r ~= " const"; - else static if (is(type == inout)) r ~= " inout"; - //else --> mutable - } - return r; - } - enum n = to!string(i); - static if (fa & FunctionAttribute.property) - { - static if (Parameters!(TargetMembers[i].type).length == 0) - enum fbody = "_wrap_source."~name; - else - enum fbody = "_wrap_source."~name~" = forward!args"; - } - else - { - enum fbody = "_wrap_source."~name~"(forward!args)"; - } - enum generateFun = - "override "~stc~"ReturnType!(TargetMembers["~n~"].type) " - ~ name~"(Parameters!(TargetMembers["~n~"].type) args) "~mod~ - "{ return "~fbody~"; }"; - } - - public: - static foreach (i; 0 .. TargetMembers.length) - mixin(generateFun!i); - } - } -} -/// ditto -template wrap(Targets...) -if (Targets.length >= 1 && !allSatisfy!(isMutable, Targets)) -{ - import std.meta : staticMap; - - alias wrap = .wrap!(staticMap!(Unqual, Targets)); -} - -/// ditto -template unwrap(Target) -if (isMutable!Target) -{ - // strict downcast - auto unwrap(Source)(inout Source src) @trusted pure nothrow - if (is(Target : Source)) - { - alias T = Select!(is(Source == shared), shared Target, Target); - return dynamicCast!(inout T)(src); - } - // structural downcast - auto unwrap(Source)(inout Source src) @trusted pure nothrow - if (!is(Target : Source)) - { - alias T = Select!(is(Source == shared), shared Target, Target); - Object o = dynamicCast!(Object)(src); // remove qualifier - do - { - if (auto a = dynamicCast!(Structural)(o)) - { - if (auto d = dynamicCast!(inout T)(o = a._wrap_getSource())) - return d; - } - else if (auto d = dynamicCast!(inout T)(o)) - return d; - else - break; - } while (o); - return null; - } -} - -/// ditto -template unwrap(Target) -if (!isMutable!Target) -{ - alias unwrap = .unwrap!(Unqual!Target); -} - -/// -@system unittest -{ - interface Quack - { - int quack(); - @property int height(); - } - interface Flyer - { - @property int height(); - } - class Duck : Quack - { - int quack() { return 1; } - @property int height() { return 10; } - } - class Human - { - int quack() { return 2; } - @property int height() { return 20; } - } - - Duck d1 = new Duck(); - Human h1 = new Human(); - - interface Refleshable - { - int reflesh(); - } - - // does not have structural conformance - static assert(!__traits(compiles, d1.wrap!Refleshable)); - static assert(!__traits(compiles, h1.wrap!Refleshable)); - - // strict upcast - Quack qd = d1.wrap!Quack; - assert(qd is d1); - assert(qd.quack() == 1); // calls Duck.quack - // strict downcast - Duck d2 = qd.unwrap!Duck; - assert(d2 is d1); - - // structural upcast - Quack qh = h1.wrap!Quack; - assert(qh.quack() == 2); // calls Human.quack - // structural downcast - Human h2 = qh.unwrap!Human; - assert(h2 is h1); - - // structural upcast (two steps) - Quack qx = h1.wrap!Quack; // Human -> Quack - Flyer fx = qx.wrap!Flyer; // Quack -> Flyer - assert(fx.height == 20); // calls Human.height - // structural downcast (two steps) - Quack qy = fx.unwrap!Quack; // Flyer -> Quack - Human hy = qy.unwrap!Human; // Quack -> Human - assert(hy is h1); - // structural downcast (one step) - Human hz = fx.unwrap!Human; // Flyer -> Human - assert(hz is h1); -} - -/// -@system unittest -{ - import std.traits : FunctionAttribute, functionAttributes; - interface A { int run(); } - interface B { int stop(); @property int status(); } - class X - { - int run() { return 1; } - int stop() { return 2; } - @property int status() { return 3; } - } - - auto x = new X(); - auto ab = x.wrap!(A, B); - A a = ab; - B b = ab; - assert(a.run() == 1); - assert(b.stop() == 2); - assert(b.status == 3); - static assert(functionAttributes!(typeof(ab).status) & FunctionAttribute.property); -} - -// Internal class to support dynamic cross-casting -private interface Structural -{ - inout(Object) _wrap_getSource() inout @safe pure nothrow; -} - -@system unittest -{ - class A - { - int draw() { return 1; } - int draw(int v) { return v; } - - int draw() const { return 2; } - int draw() shared { return 3; } - int draw() shared const { return 4; } - int draw() immutable { return 5; } - } - interface Drawable - { - int draw(); - int draw() const; - int draw() shared; - int draw() shared const; - int draw() immutable; - } - interface Drawable2 - { - int draw(int v); - } - - auto ma = new A(); - auto sa = new shared A(); - auto ia = new immutable A(); - { - Drawable md = ma.wrap!Drawable; - const Drawable cd = ma.wrap!Drawable; - shared Drawable sd = sa.wrap!Drawable; - shared const Drawable scd = sa.wrap!Drawable; - immutable Drawable id = ia.wrap!Drawable; - assert( md.draw() == 1); - assert( cd.draw() == 2); - assert( sd.draw() == 3); - assert(scd.draw() == 4); - assert( id.draw() == 5); - } - { - Drawable2 d = ma.wrap!Drawable2; - static assert(!__traits(compiles, d.draw())); - assert(d.draw(10) == 10); - } -} - -// https://issues.dlang.org/show_bug.cgi?id=10377 -@system unittest -{ - import std.range, std.algorithm; - - interface MyInputRange(T) - { - @property T front(); - void popFront(); - @property bool empty(); - } - - //auto o = iota(0,10,1).inputRangeObject(); - //pragma(msg, __traits(allMembers, typeof(o))); - auto r = iota(0,10,1).inputRangeObject().wrap!(MyInputRange!int)(); - assert(equal(r, [0, 1, 2, 3, 4, 5, 6, 7, 8, 9])); -} - -// https://issues.dlang.org/show_bug.cgi?id=10536 -@system unittest -{ - interface Interface - { - int foo(); - } - class Pluggable - { - int foo() { return 1; } - @disable void opCast(T, this X)(); // ! - } - - Interface i = new Pluggable().wrap!Interface; - assert(i.foo() == 1); -} -@system unittest -{ - // Enhancement 10538 - interface Interface - { - int foo(); - int bar(int); - } - class Pluggable - { - int opDispatch(string name, A...)(A args) { return 100; } - } - - Interface i = wrap!Interface(new Pluggable()); - assert(i.foo() == 100); - assert(i.bar(10) == 100); -} - -// https://issues.dlang.org/show_bug.cgi?id=12064 -@system unittest -{ - interface I - { - int foo(); - final int nvi1(){return foo();} - } - - interface J - { - int bar(); - final int nvi2(){return bar();} - } - - class Baz - { - int foo() { return 42;} - int bar() { return 12064;} - } - - auto baz = new Baz(); - auto foobar = baz.wrap!(I, J)(); - assert(foobar.nvi1 == 42); - assert(foobar.nvi2 == 12064); -} - -// Make a tuple of non-static function symbols -package template GetOverloadedMethods(T) -{ - import std.meta : Filter; - - alias allMembers = __traits(allMembers, T); - template follows(size_t i = 0) - { - static if (i >= allMembers.length) - { - alias follows = AliasSeq!(); - } - else static if (!__traits(compiles, mixin("T."~allMembers[i]))) - { - alias follows = follows!(i + 1); - } - else - { - enum name = allMembers[i]; - - template isMethod(alias f) - { - static if (is(typeof(&f) F == F*) && is(F == function)) - enum isMethod = !__traits(isStaticFunction, f); - else - enum isMethod = false; - } - alias follows = AliasSeq!( - Filter!(isMethod, __traits(getOverloads, T, name)), - follows!(i + 1)); - } - } - alias GetOverloadedMethods = follows!(); -} -// find a function from Fs that has same identifier and covariant type with f -private template findCovariantFunction(alias finfo, Source, Fs...) -{ - template check(size_t i = 0) - { - static if (i >= Fs.length) - enum ptrdiff_t check = -1; - else - { - enum ptrdiff_t check = - (finfo.name == __traits(identifier, Fs[i])) && - isCovariantWith!(FunctionTypeOf!(Fs[i]), finfo.type) - ? i : check!(i + 1); - } - } - enum x = check!(); - static if (x == -1 && is(typeof(Source.opDispatch))) - { - alias Params = Parameters!(finfo.type); - enum ptrdiff_t findCovariantFunction = - is(typeof(( Source).init.opDispatch!(finfo.name)(Params.init))) || - is(typeof(( const Source).init.opDispatch!(finfo.name)(Params.init))) || - is(typeof(( immutable Source).init.opDispatch!(finfo.name)(Params.init))) || - is(typeof(( shared Source).init.opDispatch!(finfo.name)(Params.init))) || - is(typeof((shared const Source).init.opDispatch!(finfo.name)(Params.init))) - ? ptrdiff_t.max : -1; - } - else - enum ptrdiff_t findCovariantFunction = x; -} - -private enum TypeModifier -{ - mutable = 0, // type is mutable - const_ = 1, // type is const - immutable_ = 2, // type is immutable - shared_ = 4, // type is shared - inout_ = 8, // type is wild -} -private template TypeMod(T) -{ - static if (is(T == immutable)) - { - enum mod1 = TypeModifier.immutable_; - enum mod2 = 0; - } - else - { - enum mod1 = is(T == shared) ? TypeModifier.shared_ : 0; - static if (is(T == const)) - enum mod2 = TypeModifier.const_; - else static if (is(T == inout)) - enum mod2 = TypeModifier.inout_; - else - enum mod2 = TypeModifier.mutable; - } - enum TypeMod = cast(TypeModifier)(mod1 | mod2); -} - -@system unittest -{ - template UnittestFuncInfo(alias f) - { - enum name = __traits(identifier, f); - alias type = FunctionTypeOf!f; - } - - class A - { - int draw() { return 1; } - @property int value() { return 2; } - final int run() { return 3; } - } - alias methods = GetOverloadedMethods!A; - - alias int F1(); - alias @property int F2(); - alias string F3(); - alias nothrow @trusted uint F4(); - alias int F5(Object); - alias bool F6(Object); - static assert(methods.length == 3 + 4); - static assert(__traits(identifier, methods[0]) == "draw" && is(typeof(&methods[0]) == F1*)); - static assert(__traits(identifier, methods[1]) == "value" && is(typeof(&methods[1]) == F2*)); - static assert(__traits(identifier, methods[2]) == "run" && is(typeof(&methods[2]) == F1*)); - - int draw(); - @property int value(); - void opEquals(); - int nomatch(); - static assert(findCovariantFunction!(UnittestFuncInfo!draw, A, methods) == 0); - static assert(findCovariantFunction!(UnittestFuncInfo!value, A, methods) == 1); - static assert(findCovariantFunction!(UnittestFuncInfo!opEquals, A, methods) == -1); - static assert(findCovariantFunction!(UnittestFuncInfo!nomatch, A, methods) == -1); - - // considering opDispatch - class B - { - void opDispatch(string name, A...)(A) {} - } - alias methodsB = GetOverloadedMethods!B; - static assert(findCovariantFunction!(UnittestFuncInfo!draw, B, methodsB) == ptrdiff_t.max); - static assert(findCovariantFunction!(UnittestFuncInfo!value, B, methodsB) == ptrdiff_t.max); - static assert(findCovariantFunction!(UnittestFuncInfo!opEquals, B, methodsB) == ptrdiff_t.max); - static assert(findCovariantFunction!(UnittestFuncInfo!nomatch, B, methodsB) == ptrdiff_t.max); -} - -package template DerivedFunctionType(T...) -{ - static if (!T.length) - { - alias DerivedFunctionType = void; - } - else static if (T.length == 1) - { - static if (is(T[0] == function)) - { - alias DerivedFunctionType = T[0]; - } - else - { - alias DerivedFunctionType = void; - } - } - else static if (is(T[0] P0 == function) && is(T[1] P1 == function)) - { - alias FA = FunctionAttribute; - - alias F0 = T[0], R0 = ReturnType!F0, PSTC0 = ParameterStorageClassTuple!F0; - alias F1 = T[1], R1 = ReturnType!F1, PSTC1 = ParameterStorageClassTuple!F1; - enum FA0 = functionAttributes!F0; - enum FA1 = functionAttributes!F1; - - template CheckParams(size_t i = 0) - { - static if (i >= P0.length) - enum CheckParams = true; - else - { - enum CheckParams = (is(P0[i] == P1[i]) && PSTC0[i] == PSTC1[i]) && - CheckParams!(i + 1); - } - } - static if (R0.sizeof == R1.sizeof && !is(CommonType!(R0, R1) == void) && - P0.length == P1.length && CheckParams!() && TypeMod!F0 == TypeMod!F1 && - variadicFunctionStyle!F0 == variadicFunctionStyle!F1 && - functionLinkage!F0 == functionLinkage!F1 && - ((FA0 ^ FA1) & (FA.ref_ | FA.property)) == 0) - { - alias R = Select!(is(R0 : R1), R0, R1); - alias FX = FunctionTypeOf!(R function(P0)); - // @system is default - alias FY = SetFunctionAttributes!(FX, functionLinkage!F0, (FA0 | FA1) & ~FA.system); - alias DerivedFunctionType = DerivedFunctionType!(FY, T[2 .. $]); - } - else - alias DerivedFunctionType = void; - } - else - alias DerivedFunctionType = void; -} -@safe unittest -{ - // attribute covariance - alias int F1(); - static assert(is(DerivedFunctionType!(F1, F1) == F1)); - alias int F2() pure nothrow; - static assert(is(DerivedFunctionType!(F1, F2) == F2)); - alias int F3() @safe; - alias int F23() @safe pure nothrow; - static assert(is(DerivedFunctionType!(F2, F3) == F23)); - - // return type covariance - alias long F4(); - static assert(is(DerivedFunctionType!(F1, F4) == void)); - class C {} - class D : C {} - alias C F5(); - alias D F6(); - static assert(is(DerivedFunctionType!(F5, F6) == F6)); - alias typeof(null) F7(); - alias int[] F8(); - alias int* F9(); - static assert(is(DerivedFunctionType!(F5, F7) == F7)); - static assert(is(DerivedFunctionType!(F7, F8) == void)); - static assert(is(DerivedFunctionType!(F7, F9) == F7)); - - // variadic type equality - alias int F10(int); - alias int F11(int...); - alias int F12(int, ...); - static assert(is(DerivedFunctionType!(F10, F11) == void)); - static assert(is(DerivedFunctionType!(F10, F12) == void)); - static assert(is(DerivedFunctionType!(F11, F12) == void)); - - // linkage equality - alias extern(C) int F13(int); - alias extern(D) int F14(int); - alias extern(Windows) int F15(int); - static assert(is(DerivedFunctionType!(F13, F14) == void)); - static assert(is(DerivedFunctionType!(F13, F15) == void)); - static assert(is(DerivedFunctionType!(F14, F15) == void)); - - // ref & @property equality - alias int F16(int); - alias ref int F17(int); - alias @property int F18(int); - static assert(is(DerivedFunctionType!(F16, F17) == void)); - static assert(is(DerivedFunctionType!(F16, F18) == void)); - static assert(is(DerivedFunctionType!(F17, F18) == void)); -} - -package template Bind(alias Template, args1...) -{ - alias Bind(args2...) = Template!(args1, args2); -} - - -/** -Options regarding auto-initialization of a `SafeRefCounted` object (see -the definition of `SafeRefCounted` below). - */ -enum RefCountedAutoInitialize -{ - /// Do not auto-initialize the object - no, - /// Auto-initialize the object - yes, -} - -/// -@system unittest -{ - import core.exception : AssertError; - import std.exception : assertThrown; - - struct Foo - { - int a = 42; - } - - SafeRefCounted!(Foo, RefCountedAutoInitialize.yes) rcAuto; - SafeRefCounted!(Foo, RefCountedAutoInitialize.no) rcNoAuto; - - assert(rcAuto.refCountedPayload.a == 42); - - assertThrown!AssertError(rcNoAuto.refCountedPayload); - rcNoAuto.refCountedStore.ensureInitialized; - assert(rcNoAuto.refCountedPayload.a == 42); -} - -// Same the above but for old RefCounted and not documented -@system unittest -{ - import core.exception : AssertError; - import std.exception : assertThrown; - - struct Foo - { - int a = 42; - } - - RefCounted!(Foo, RefCountedAutoInitialize.yes) rcAuto; - RefCounted!(Foo, RefCountedAutoInitialize.no) rcNoAuto; - - assert(rcAuto.refCountedPayload.a == 42); - - assertThrown!AssertError(rcNoAuto.refCountedPayload); - rcNoAuto.refCountedStore.ensureInitialized; - assert(rcNoAuto.refCountedPayload.a == 42); -} - -/** -Defines a reference-counted object containing a `T` value as -payload. - -An instance of `SafeRefCounted` is a reference to a structure, -which is referred to as the $(I store), or $(I storage implementation -struct) in this documentation. The store contains a reference count -and the `T` payload. `SafeRefCounted` uses `malloc` to allocate -the store. As instances of `SafeRefCounted` are copied or go out of -scope, they will automatically increment or decrement the reference -count. When the reference count goes down to zero, `SafeRefCounted` -will call `destroy` against the payload and call `free` to -deallocate the store. If the `T` payload contains any references -to GC-allocated memory, then `SafeRefCounted` will add it to the GC memory -that is scanned for pointers, and remove it from GC scanning before -`free` is called on the store. - -One important consequence of `destroy` is that it will call the -destructor of the `T` payload. GC-managed references are not -guaranteed to be valid during a destructor call, but other members of -`T`, such as file handles or pointers to `malloc` memory, will -still be valid during the destructor call. This allows the `T` to -deallocate or clean up any non-GC resources immediately after the -reference count has reached zero. - -Without -preview=dip1000, `SafeRefCounted` is unsafe and should be -used with care. No references to the payload should be escaped outside -the `SafeRefCounted` object. - -With -preview=dip1000, `SafeRefCounted` is safe if it's payload is accessed only -with the $(LREF borrow) function. Scope semantics can also prevent accidental -escaping of `refCountedPayload`, but it's still up to the user to not destroy -the last counted reference while the payload is in use. Due to that, -`refCountedPayload` remains accessible only in `@system` code. - -The `autoInit` option makes the object ensure the store is -automatically initialized. Leaving $(D autoInit == -RefCountedAutoInitialize.yes) (the default option) is convenient but -has the cost of a test whenever the payload is accessed. If $(D -autoInit == RefCountedAutoInitialize.no), user code must call either -`refCountedStore.isInitialized` or `refCountedStore.ensureInitialized` -before attempting to access the payload. Not doing so results in null -pointer dereference. - -If `T.this()` is annotated with `@disable` then `autoInit` must be -`RefCountedAutoInitialize.no` in order to compile. - -See_Also: - $(LREF RefCounted) - */ -struct SafeRefCounted(T, RefCountedAutoInitialize autoInit = - RefCountedAutoInitialize.yes) -if (!is(T == class) && !(is(T == interface))) -{ - version (D_BetterC) - { - private enum enableGCScan = false; - } - else - { - private enum enableGCScan = hasIndirections!T; - } - - extern(C) private pure nothrow @nogc static - { - pragma(mangle, "free") void pureFree( void *ptr ); - static if (enableGCScan) - import core.memory : GC; - } - - pragma(inline, true) private void checkInit()() - if (autoInit == RefCountedAutoInitialize.yes) - { - _refCounted.ensureInitialized(); - } - - pragma(inline, true) private void checkInit()() inout - if (autoInit == RefCountedAutoInitialize.no) - { - assert(_refCounted.isInitialized, - "Attempted to use an uninitialized payload."); - } - - /// `SafeRefCounted` storage implementation. - struct RefCountedStore - { - private struct Impl - { - T _payload; - size_t _count; - } - - private Impl* _store; - - private void initialize(A...)(auto ref A args) - { - import core.lifetime : emplace, forward; - - allocateStore(); - version (D_Exceptions) scope(failure) () @trusted { deallocateStore(); }(); - emplace(&_store._payload, forward!args); - _store._count = 1; - } - - private void move(ref T source) nothrow pure - { - import std.algorithm.mutation : moveEmplace; - - allocateStore(); - () @trusted { moveEmplace(source, _store._payload); }(); - _store._count = 1; - } - - // 'nothrow': can only generate an Error - private void allocateStore() nothrow pure - { - static if (enableGCScan) - { - import std.internal.memory : enforceCalloc; - auto ptr = enforceCalloc(1, Impl.sizeof); - _store = () @trusted { return cast(Impl*) ptr; }(); - () @trusted { GC.addRange(&_store._payload, T.sizeof); }(); - } - else - { - import std.internal.memory : enforceMalloc; - auto ptr = enforceMalloc(Impl.sizeof); - _store = () @trusted { return cast(Impl*) ptr; }(); - } - } - - private void deallocateStore() nothrow pure - { - static if (enableGCScan) - { - GC.removeRange(&this._store._payload); - } - pureFree(_store); - _store = null; - } - - /** - Returns `true` if and only if the underlying store has been - allocated and initialized. - */ - @property nothrow @safe pure @nogc - bool isInitialized() const - { - return _store !is null; - } - - /** - Returns underlying reference count if it is allocated and initialized - (a positive integer), and `0` otherwise. - */ - @property nothrow @safe pure @nogc - size_t refCount() const - { - return isInitialized ? _store._count : 0; - } - - /** - Makes sure the payload was properly initialized. Such a - call is typically inserted before using the payload. - - This function is unavailable if `T.this()` is annotated with - `@disable`. - */ - @safe pure nothrow - void ensureInitialized()() - { - // By checking for `@disable this()` and failing early we can - // produce a clearer error message. - static assert(__traits(compiles, { static T t; }), - "Cannot automatically initialize `" ~ fullyQualifiedName!T ~ - "` because `" ~ fullyQualifiedName!T ~ - ".this()` is annotated with `@disable`."); - if (!isInitialized) initialize(); - } - - } - RefCountedStore _refCounted; - - /// Returns storage implementation struct. - @property nothrow @safe - ref inout(RefCountedStore) refCountedStore() inout - { - return _refCounted; - } - -/** -Constructor that initializes the payload. - -Postcondition: `refCountedStore.isInitialized` - */ - this(A...)(auto ref A args) if (A.length > 0) - out - { - assert(refCountedStore.isInitialized); - } - do - { - import core.lifetime : forward; - _refCounted.initialize(forward!args); - } - - /// Ditto - this(return scope T val) - { - _refCounted.move(val); - } - -/** -Constructor that tracks the reference count appropriately. If $(D -!refCountedStore.isInitialized), does nothing. - */ - this(this) @safe pure nothrow @nogc - { - if (!_refCounted.isInitialized) return; - ++_refCounted._store._count; - } - -/** -Destructor that tracks the reference count appropriately. If $(D -!refCountedStore.isInitialized), does nothing. When the reference count goes -down to zero, calls `destroy` agaist the payload and calls `free` -to deallocate the corresponding resource. - */ - ~this() - { - import std.traits : dip1000Enabled; - - // This prevents the same reference from decrementing the count twice. - scope(exit) _refCounted = _refCounted.init; - - if (!_refCounted.isInitialized) return; - assert(_refCounted._store._count > 0); - if (--_refCounted._store._count) return; - // Done, destroy and deallocate - .destroy(_refCounted._store._payload); - - static if (dip1000Enabled) - { - () @trusted { _refCounted.deallocateStore(); }(); - } - else _refCounted.deallocateStore(); - } - -/** -Assignment operators. - -Note: You may not assign a new payload to an uninitialized SafeRefCounted, if -auto initialization is off. Assigning another counted reference is still okay. -*/ - void opAssign(typeof(this) rhs) - { - import std.algorithm.mutation : swap; - - swap(_refCounted._store, rhs._refCounted._store); - } - -/// Ditto - void opAssign(T rhs) - { - import std.algorithm.mutation : move; - - checkInit(); - move(rhs, _refCounted._store._payload); - } - - //version to have a single properly ddoc'ed function (w/ correct sig) - version (StdDdoc) - { - /** - Returns a reference to the payload. If (autoInit == - RefCountedAutoInitialize.yes), calls $(D - refCountedStore.ensureInitialized). Otherwise, just issues $(D - assert(refCountedStore.isInitialized)). Used with $(D alias - refCountedPayload this;), so callers can just use the `SafeRefCounted` - object as a `T`. - - $(BLUE The first overload exists only if $(D autoInit == RefCountedAutoInitialize.yes).) - So if $(D autoInit == RefCountedAutoInitialize.no) - or called for a constant or immutable object, then - `refCountedPayload` will also be qualified as nothrow - (but will still assert if not initialized). - */ - @property @system - ref T refCountedPayload() return; - - /// ditto - @property nothrow @system pure @nogc - ref inout(T) refCountedPayload() inout return; - } - else - { - static if (autoInit == RefCountedAutoInitialize.yes) - { - //Can't use inout here because of potential mutation - @property @system - ref T refCountedPayload() return - { - checkInit(); - return _refCounted._store._payload; - } - } - else - { - @property nothrow @system pure @nogc - ref inout(T) refCountedPayload() inout return - { - checkInit(); - return _refCounted._store._payload; - } - } - } - -/** -Returns a reference to the payload. If (autoInit == -RefCountedAutoInitialize.yes), calls $(D -refCountedStore.ensureInitialized). Otherwise, just issues $(D -assert(refCountedStore.isInitialized)). - */ - alias refCountedPayload this; - - static if (is(T == struct) && !is(typeof((ref T t) => t.toString()))) - { - string toString(this This)() - { - import std.conv : to; - - static if (autoInit) - return to!string(refCountedPayload); - else - { - if (!_refCounted.isInitialized) - return This.stringof ~ "(RefCountedStore(null))"; - else - return to!string(_refCounted._store._payload); - } - } - } -} - -/// -@betterC pure @system nothrow @nogc unittest -{ - // A pair of an `int` and a `size_t` - the latter being the - // reference count - will be dynamically allocated - auto rc1 = SafeRefCounted!int(5); - assert(rc1 == 5); - // No more allocation, add just one extra reference count - auto rc2 = rc1; - // Reference semantics - rc2 = 42; - assert(rc1 == 42); - // the pair will be freed when rc1 and rc2 go out of scope -} - -// This test can't be betterC because the test extractor won't see the private -// `initialize` method accessed here -pure @safe nothrow @nogc unittest -{ - auto rc1 = SafeRefCounted!(int, RefCountedAutoInitialize.no)(5); - rc1._refCounted.initialize(); -} - -pure @system unittest -{ - foreach (MyRefCounted; AliasSeq!(SafeRefCounted, RefCounted)) - { - MyRefCounted!int* p; - auto rc1 = MyRefCounted!int(5); - p = &rc1; - assert(rc1 == 5); - assert(rc1._refCounted._store._count == 1); - { - auto rc2 = rc1; - assert(rc1._refCounted._store._count == 2); - // Reference semantics - rc2 = 42; - assert(rc1 == 42); - rc2 = rc2; - assert(rc2._refCounted._store._count == 2); - rc1 = rc2; - assert(rc1._refCounted._store._count == 2); - } - // Artificially end scope of rc1 by calling ~this() explicitly - rc1.__xdtor(); - assert(p._refCounted._store == null); - - // [Safe]RefCounted as a member - struct A - { - MyRefCounted!int x; - this(int y) - { - x._refCounted.initialize(y); - } - A copy() - { - auto another = this; - return another; - } - } - auto a = A(4); - auto b = a.copy(); - assert(a.x._refCounted._store._count == 2, - "https://issues.dlang.org/show_bug.cgi?id=4356 still unfixed"); - } -} - -@betterC pure @safe nothrow @nogc unittest -{ - import std.algorithm.mutation : swap; - - SafeRefCounted!int p1, p2; - swap(p1, p2); -} - -// Same as above but for old RefCounted and not @safe -@betterC pure @system nothrow @nogc unittest -{ - import std.algorithm.mutation : swap; - - RefCounted!int p1, p2; - swap(p1, p2); -} - -// https://issues.dlang.org/show_bug.cgi?id=6606 -@betterC @safe pure nothrow @nogc unittest -{ - union U { - size_t i; - void* p; - } - - struct S { - U u; - } - - alias SRC = SafeRefCounted!S; -} - -// Same as above but for old RefCounted and not @safe -@betterC @system pure nothrow @nogc unittest -{ - union U { - size_t i; - void* p; - } - - struct S { - U u; - } - - alias SRC = RefCounted!S; -} - -// https://issues.dlang.org/show_bug.cgi?id=6436 -@betterC @system pure unittest -{ - import std.meta : AliasSeq; - struct S - { - this(int rval) { assert(rval == 1); } - this(ref int lval) { assert(lval == 3); ++lval; } - } - - foreach (MyRefCounted; AliasSeq!(SafeRefCounted, RefCounted)) - { - auto s1 = MyRefCounted!S(1); - int lval = 3; - auto s2 = MyRefCounted!S(lval); - assert(lval == 4); - } -} - -// gc_addRange coverage -@betterC @safe pure unittest -{ - struct S { int* p; } - - auto s = SafeRefCounted!S(null); -} - -// Same as above but for old RefCounted and not @safe -@betterC @system pure unittest -{ - struct S { int* p; } - - auto s = RefCounted!S(null); -} - -@betterC @system pure nothrow @nogc unittest -{ - import std.meta : AliasSeq; - foreach (MyRefCounted; AliasSeq!(SafeRefCounted, RefCounted)) - { - MyRefCounted!int a; - a = 5; //This should not assert - assert(a == 5); - - MyRefCounted!int b; - b = a; //This should not assert either - assert(b == 5); - - MyRefCounted!(int*) c; - } -} - -// https://issues.dlang.org/show_bug.cgi?id=21638 -@betterC @system pure nothrow @nogc unittest -{ - import std.meta : AliasSeq; - static struct NoDefaultCtor - { - @disable this(); - this(int x) @nogc nothrow pure { this.x = x; } - int x; - } - - foreach (MyRefCounted; AliasSeq!(SafeRefCounted, RefCounted)) - { - auto rc = MyRefCounted!(NoDefaultCtor, RefCountedAutoInitialize.no)(5); - assert(rc.x == 5); - } -} - -// https://issues.dlang.org/show_bug.cgi?id=20502 -@system unittest -{ - alias Types = AliasSeq!(SafeRefCounted, RefCounted); - alias funcs = AliasSeq!(safeRefCounted, refCounted); - static foreach (aliasI; 0 .. 2) - {{ - alias MyRefCounted = Types[aliasI]; - alias myRefCounted = funcs[aliasI]; - import std.conv : to; - - // Check that string conversion is transparent for refcounted - // structs that do not have either toString or alias this. - static struct A { Object a; } - auto a = A(new Object()); - auto r = myRefCounted(a); - assert(to!string(r) == to!string(a)); - assert(to!string(cast(const) r) == to!string(cast(const) a)); - // Check that string conversion is still transparent for refcounted - // structs that have alias this. - static struct B { int b; alias b this; } - static struct C { B b; alias b this; } - assert(to!string(myRefCounted(C(B(123)))) == to!string(C(B(123)))); - // https://issues.dlang.org/show_bug.cgi?id=22093 - // Check that uninitialized refcounted structs that previously could be - // converted to strings still can be. - alias R = typeof(r); - R r2; - cast(void) (((const ref R a) => to!string(a))(r2)); - cast(void) to!string(MyRefCounted!(A, RefCountedAutoInitialize.no).init); - }} -} - -// We tried to make `refCountedPayload` `@safe` in -// https://github.com/dlang/phobos/pull/8368 . It proved impossible, but it may -// be possible in the future. This test checks for false `@safe` issues we -// encountered. -@safe unittest -{ - struct Container - { - int[] data; - } - - int[] getArr1 (scope Container local) - { - // allowed because the argument is inferred as return scope. - return local.data; - } - - int[] getArr2 (scope Container local) - { - SafeRefCounted!Container rc = local; - // Escapes a reference to expired reference counted struct - // don't do this! - return rc.refCountedPayload().data; - } - - int destroyFirstAndUseLater() - { - auto rc = SafeRefCounted!int(123); - int* ptr = &rc.refCountedPayload(); - destroy(rc); - return *ptr; - } - - // This is here mainly to test that safety gets inferred correctly for the - // next tests - static assert(isSafe!getArr1); - // https://github.com/dlang/phobos/pull/8101#issuecomment-843017282 - // This got apparently fixed automatically by compiler updates. - static assert(!isSafe!getArr2); - // As of writing, this is the issue that is still preventing payload access - // from being `@safe` - static assert(!isSafe!destroyFirstAndUseLater); -} - -/** -Borrows the payload of $(LREF SafeRefCounted) for use in `fun`. Inferred as `@safe` -if `fun` is `@safe` and does not escape a reference to the payload. -The reference count will be incremented for the duration of the operation, -so destroying the last reference will not leave dangling references in -`fun`. - -Params: - fun = A callable accepting the payload either by value or by reference. - refCount = The counted reference to the payload. -Returns: - The return value of `fun`, if any. `ref` in the return value will be - forwarded. -Issues: - For yet unknown reason, code that uses this function with UFCS syntax - will not be inferred as `@safe`. It will still compile if the code is - explicitly marked `@safe` and nothing in `fun` prevents that. -*/ -template borrow(alias fun) -{ - import std.functional : unaryFun; - - auto ref borrow(RC)(RC refCount) if - ( - isInstanceOf!(SafeRefCounted, RC) - && is(typeof(unaryFun!fun(refCount.refCountedPayload))) - ) - { - refCount.checkInit(); - - // If `fun` escapes a reference to the payload, it will be inferred - // as unsafe due to the scope storage class here. - scope plPtr = &refCount._refCounted._store._payload; - return unaryFun!fun(*plPtr); - - // We destroy our copy of the reference here, automatically destroying - // the payload if `fun` destroyed the last reference outside. - } -} - -/// This example can be marked `@safe` with `-preview=dip1000`. -@safe pure nothrow unittest -{ - auto rcInt = safeRefCounted(5); - assert(rcInt.borrow!(theInt => theInt) == 5); - auto sameInt = rcInt; - assert(sameInt.borrow!"a" == 5); - - // using `ref` in the function - auto arr = [0, 1, 2, 3, 4, 5, 6]; - sameInt.borrow!(ref (x) => arr[x]) = 10; - assert(arr == [0, 1, 2, 3, 4, 10, 6]); - - // modifying the payload via an alias - sameInt.borrow!"a*=2"; - assert(rcInt.borrow!"a" == 10); -} - -// Some memory safety penetration testing. -@system unittest -{ - int* globalPtr; - int torpedoesFired = 0; - struct Destroyer { ~this() @safe { torpedoesFired++; } } - - alias RcInt = typeof(safeRefCounted(0)); - auto standardUsage(RcInt arg) - { - return borrow!((ref x) => x)(arg); - } - ref harmlessRefReturn(RcInt arg) - { - return borrow!(ref (ref x) => *globalPtr = x)(arg); - } - ref problematicRefReturn(RcInt arg) - { - return borrow!(ref (ref x) => x)(arg); - } - auto sideChannelEscape(RcInt arg) - { - return borrow!((ref x) - { - globalPtr = &x; - return x; - })(arg); - } - auto destroyDuringApply() - { - auto rc = safeRefCounted(Destroyer()); - return borrow!((ref x) - { - // Destroys the last reference to the payload, decrementing it's - // reference count. - rc.__dtor(); - // Destroy again! rc should be set to it's init value so that this - // won't decrement the reference count of the original payload. - rc.__dtor(); - // The internal reference count increment done for duration of - // `apply` should make sure that the payload destructor is not yet - // run, and this value thus not incremented. - return torpedoesFired; - })(rc); - } - - // First, let's verify the dangerous functions really do what they are - // supposed to do. - auto testRc = safeRefCounted(42); - assert(sideChannelEscape(testRc) == 42); - assert(&problematicRefReturn(testRc) == globalPtr); - - // Now, are the @safe attributes inferred correctly? - assert(isSafe!standardUsage); - assert(isSafe!harmlessRefReturn); - assert(!isSafe!problematicRefReturn); - assert(!isSafe!sideChannelEscape); - assert(isSafe!destroyDuringApply); - - // Finally, we test protection against early destruction during `apply`. - auto torpedoesUpToReturn = destroyDuringApply(); - assert(torpedoesFired == torpedoesUpToReturn + 1); -} - -/** - * Initializes a `SafeRefCounted` with `val`. The template parameter - * `T` of `SafeRefCounted` is inferred from `val`. - * This function can be used to move non-copyable values to the heap. - * It also disables the `autoInit` option of `SafeRefCounted`. - * - * Params: - * val = The value to be reference counted - * Returns: - * An initialized `SafeRefCounted` containing `val`. - * See_Also: - * $(LREF refCounted) - * $(HTTP en.cppreference.com/w/cpp/memory/shared_ptr/make_shared, C++'s make_shared) - */ -SafeRefCounted!(T, RefCountedAutoInitialize.no) safeRefCounted(T)(T val) -{ - typeof(return) res; - res._refCounted.move(val); - return res; -} - -/// -@system unittest -{ - static struct File - { - static size_t nDestroyed; - string name; - @disable this(this); // not copyable - ~this() { name = null; ++nDestroyed; } - } - - auto file = File("name"); - assert(file.name == "name"); - // file cannot be copied and has unique ownership - static assert(!__traits(compiles, {auto file2 = file;})); - - assert(File.nDestroyed == 0); - - // make the file ref counted to share ownership - // Note: - // We write a compound statement (brace-delimited scope) in which all `SafeRefCounted!File` handles are created and deleted. - // This allows us to see (after the scope) what happens after all handles have been destroyed. - { - // We move the content of `file` to a separate (and heap-allocated) `File` object, - // managed-and-accessed via one-or-multiple (initially: one) `SafeRefCounted!File` objects ("handles"). - // This "moving": - // (1) invokes `file`'s destructor (=> `File.nDestroyed` is incremented from 0 to 1 and `file.name` becomes `null`); - // (2) overwrites `file` with `File.init` (=> `file.name` becomes `null`). - // It appears that writing `name = null;` in the destructor is redundant, - // but please note that (2) is only performed if `File` defines a destructor (or post-blit operator), - // and in the absence of the `nDestroyed` instrumentation there would have been no reason to define a destructor. - import std.algorithm.mutation : move; - auto rcFile = safeRefCounted(move(file)); - assert(rcFile.name == "name"); - assert(File.nDestroyed == 1); - assert(file.name == null); - - // We create another `SafeRefCounted!File` handle to the same separate `File` object. - // While any of the handles is still alive, the `File` object is kept alive (=> `File.nDestroyed` is not modified). - auto rcFile2 = rcFile; - assert(rcFile.refCountedStore.refCount == 2); - assert(File.nDestroyed == 1); - } - // The separate `File` object is deleted when the last `SafeRefCounted!File` handle is destroyed - // (i.e. at the closing brace of the compound statement above, which destroys both handles: `rcFile` and `rcFile2`) - // (=> `File.nDestroyed` is incremented again, from 1 to 2): - assert(File.nDestroyed == 2); -} - -/** - Creates a proxy for the value `a` that will forward all operations - while disabling implicit conversions. The aliased item `a` must be - an $(B lvalue). This is useful for creating a new type from the - "base" type (though this is $(B not) a subtype-supertype - relationship; the new type is not related to the old type in any way, - by design). - - The new type supports all operations that the underlying type does, - including all operators such as `+`, `--`, `<`, `[]`, etc. - - Params: - a = The value to act as a proxy for all operations. It must - be an lvalue. - */ -mixin template Proxy(alias a) -{ - private alias ValueType = typeof({ return a; }()); - - /* Determine if 'T.a' can referenced via a const(T). - * Use T* as the parameter because 'scope' inference needs a fully - * analyzed T, which doesn't work when accessibleFrom() is used in a - * 'static if' in the definition of Proxy or T. - */ - private enum bool accessibleFrom(T) = - is(typeof((T* self){ cast(void) mixin("(*self)."~__traits(identifier, a)); })); - - static if (is(typeof(this) == class)) - { - override bool opEquals(Object o) - { - if (auto b = cast(typeof(this))o) - { - return a == mixin("b."~__traits(identifier, a)); - } - return false; - } - - bool opEquals(T)(T b) - if (is(ValueType : T) || is(typeof(a.opEquals(b))) || is(typeof(b.opEquals(a)))) - { - static if (is(typeof(a.opEquals(b)))) - return a.opEquals(b); - else static if (is(typeof(b.opEquals(a)))) - return b.opEquals(a); - else - return a == b; - } - - override int opCmp(Object o) - { - if (auto b = cast(typeof(this))o) - { - return a < mixin("b."~__traits(identifier, a)) ? -1 - : a > mixin("b."~__traits(identifier, a)) ? +1 : 0; - } - static if (is(ValueType == class)) - return a.opCmp(o); - else - throw new Exception("Attempt to compare a "~typeid(this).toString~" and a "~typeid(o).toString); - } - - int opCmp(T)(auto ref const T b) - if (is(ValueType : T) || is(typeof(a.opCmp(b))) || is(typeof(b.opCmp(a)))) - { - static if (is(typeof(a.opCmp(b)))) - return a.opCmp(b); - else static if (is(typeof(b.opCmp(a)))) - return -b.opCmp(a); - else - return a < b ? -1 : a > b ? +1 : 0; - } - - static if (accessibleFrom!(const typeof(this))) - { - override size_t toHash() const nothrow @safe - { - static if (__traits(compiles, .hashOf(a))) - return .hashOf(a); - else - // Workaround for when .hashOf is not both @safe and nothrow. - { - static if (is(typeof(&a) == ValueType*)) - alias v = a; - else - auto v = a; // if a is (property) function - // BUG: Improperly casts away `shared`! - return typeid(ValueType).getHash((() @trusted => cast(const void*) &v)()); - } - } - } - } - else - { - auto ref opEquals(this X, B)(auto ref B b) - { - static if (is(immutable B == immutable typeof(this))) - { - return a == mixin("b."~__traits(identifier, a)); - } - else - return a == b; - } - - auto ref opCmp(this X, B)(auto ref B b) - { - static if (is(typeof(a.opCmp(b)))) - return a.opCmp(b); - else static if (is(typeof(b.opCmp(a)))) - return -b.opCmp(a); - else static if (isFloatingPoint!ValueType || isFloatingPoint!B) - return a < b ? -1 : a > b ? +1 : a == b ? 0 : float.nan; - else - return a < b ? -1 : (a > b); - } - - static if (accessibleFrom!(const typeof(this))) - { - size_t toHash() const nothrow @safe - { - static if (__traits(compiles, .hashOf(a))) - return .hashOf(a); - else - // Workaround for when .hashOf is not both @safe and nothrow. - { - static if (is(typeof(&a) == ValueType*)) - alias v = a; - else - auto v = a; // if a is (property) function - // BUG: Improperly casts away `shared`! - return typeid(ValueType).getHash((() @trusted => cast(const void*) &v)()); - } - } - } - } - - auto ref opCall(this X, Args...)(auto ref Args args) { return a(args); } - - auto ref opCast(T, this X)() { return cast(T) a; } - - auto ref opIndex(this X, D...)(auto ref D i) { return a[i]; } - auto ref opSlice(this X )() { return a[]; } - auto ref opSlice(this X, B, E)(auto ref B b, auto ref E e) { return a[b .. e]; } - - auto ref opUnary (string op, this X )() { return mixin(op~"a"); } - auto ref opIndexUnary(string op, this X, D...)(auto ref D i) { return mixin(op~"a[i]"); } - auto ref opSliceUnary(string op, this X )() { return mixin(op~"a[]"); } - auto ref opSliceUnary(string op, this X, B, E)(auto ref B b, auto ref E e) { return mixin(op~"a[b .. e]"); } - - auto ref opBinary(string op, this X, B)(auto ref B b) - if (op == "in" && is(typeof(a in b)) || op != "in") - { - return mixin("a "~op~" b"); - } - auto ref opBinaryRight(string op, this X, B)(auto ref B b) { return mixin("b "~op~" a"); } - - static if (!is(typeof(this) == class)) - { - import std.traits; - static if (isAssignable!ValueType) - { - auto ref opAssign(this X)(auto ref typeof(this) v) - { - a = mixin("v."~__traits(identifier, a)); - return this; - } - } - else - { - @disable void opAssign(this X)(auto ref typeof(this) v); - } - } - - auto ref opAssign (this X, V )(auto ref V v) if (!is(V == typeof(this))) { return a = v; } - auto ref opIndexAssign(this X, V, D...)(auto ref V v, auto ref D i) { return a[i] = v; } - auto ref opSliceAssign(this X, V )(auto ref V v) { return a[] = v; } - auto ref opSliceAssign(this X, V, B, E)(auto ref V v, auto ref B b, auto ref E e) { return a[b .. e] = v; } - - auto ref opOpAssign (string op, this X, V )(auto ref V v) - { - return mixin("a = a "~op~" v"); - } - auto ref opIndexOpAssign(string op, this X, V, D...)(auto ref V v, auto ref D i) - { - return mixin("a[i] " ~op~"= v"); - } - auto ref opSliceOpAssign(string op, this X, V )(auto ref V v) - { - return mixin("a[] " ~op~"= v"); - } - auto ref opSliceOpAssign(string op, this X, V, B, E)(auto ref V v, auto ref B b, auto ref E e) - { - return mixin("a[b .. e] "~op~"= v"); - } - - template opDispatch(string name) - { - static if (is(typeof(__traits(getMember, a, name)) == function)) - { - // non template function - auto ref opDispatch(this X, Args...)(auto ref Args args) { return mixin("a."~name~"(args)"); } - } - else static if (is(typeof({ enum x = mixin("a."~name); }))) - { - // built-in type field, manifest constant, and static non-mutable field - enum opDispatch = mixin("a."~name); - } - else static if (__traits(isTemplate, mixin("a."~name))) - { - // member template - template opDispatch(T...) - { - enum targs = T.length ? "!T" : ""; - auto ref opDispatch(this X, Args...)(auto ref Args args){ return mixin("a."~name~targs~"(args)"); } - } - } - else - { - // field or property function - @property auto ref opDispatch(this X)() { return mixin("a."~name); } - @property auto ref opDispatch(this X, V)(auto ref V v) { return mixin("a."~name~" = v"); } - } - - } - - import std.traits : isArray; - - static if (isArray!ValueType) - { - auto opDollar() const { return a.length; } - } - else static if (is(typeof(a.opDollar!0))) - { - auto ref opDollar(size_t pos)() { return a.opDollar!pos(); } - } - else static if (is(typeof(a.opDollar) == function)) - { - auto ref opDollar() { return a.opDollar(); } - } - else static if (is(typeof(a.opDollar))) - { - alias opDollar = a.opDollar; - } -} - -/// -@safe unittest -{ - struct MyInt - { - private int value; - mixin Proxy!value; - - this(int n){ value = n; } - } - - MyInt n = 10; - - // Enable operations that original type has. - ++n; - assert(n == 11); - assert(n * 2 == 22); - - void func(int n) { } - - // Disable implicit conversions to original type. - //int x = n; - //func(n); -} - -///The proxied value must be an $(B lvalue). -@safe unittest -{ - struct NewIntType - { - //Won't work; the literal '1' - //is an rvalue, not an lvalue - //mixin Proxy!1; - - //Okay, n is an lvalue - int n; - mixin Proxy!n; - - this(int n) { this.n = n; } - } - - NewIntType nit = 0; - nit++; - assert(nit == 1); - - - struct NewObjectType - { - Object obj; - //Ok, obj is an lvalue - mixin Proxy!obj; - - this (Object o) { obj = o; } - } - - NewObjectType not = new Object(); - assert(__traits(compiles, not.toHash())); -} - -/** - There is one exception to the fact that the new type is not related to the - old type. $(DDSUBLINK spec/function,pseudo-member, Pseudo-member) - functions are usable with the new type; they will be forwarded on to the - proxied value. - */ -@safe unittest -{ - import std.math.traits : isInfinity; - - float f = 1.0; - assert(!f.isInfinity); - - struct NewFloat - { - float _; - mixin Proxy!_; - - this(float f) { _ = f; } - } - - NewFloat nf = 1.0f; - assert(!nf.isInfinity); -} - -@safe unittest -{ - static struct MyInt - { - private int value; - mixin Proxy!value; - this(int n) inout { value = n; } - - enum str = "str"; - static immutable arr = [1,2,3]; - } - - static foreach (T; AliasSeq!(MyInt, const MyInt, immutable MyInt)) - {{ - T m = 10; - static assert(!__traits(compiles, { int x = m; })); - static assert(!__traits(compiles, { void func(int n){} func(m); })); - assert(m == 10); - assert(m != 20); - assert(m < 20); - assert(+m == 10); - assert(-m == -10); - assert(cast(double) m == 10.0); - assert(m + 10 == 20); - assert(m - 5 == 5); - assert(m * 20 == 200); - assert(m / 2 == 5); - assert(10 + m == 20); - assert(15 - m == 5); - assert(20 * m == 200); - assert(50 / m == 5); - static if (is(T == MyInt)) // mutable - { - assert(++m == 11); - assert(m++ == 11); assert(m == 12); - assert(--m == 11); - assert(m-- == 11); assert(m == 10); - m = m; - m = 20; assert(m == 20); - } - static assert(T.max == int.max); - static assert(T.min == int.min); - static assert(T.init == int.init); - static assert(T.str == "str"); - static assert(T.arr == [1,2,3]); - }} -} -@system unittest -{ - static struct MyArray - { - private int[] value; - mixin Proxy!value; - this(int[] arr) { value = arr; } - this(immutable int[] arr) immutable { value = arr; } - } - - static foreach (T; AliasSeq!(MyArray, const MyArray, immutable MyArray)) - {{ - static if (is(T == immutable) && !is(typeof({ T a = [1,2,3,4]; }))) - T a = [1,2,3,4].idup; // workaround until qualified ctor is properly supported - else - T a = [1,2,3,4]; - assert(a == [1,2,3,4]); - assert(a != [5,6,7,8]); - assert(+a[0] == 1); - version (LittleEndian) - assert(cast(ulong[]) a == [0x0000_0002_0000_0001, 0x0000_0004_0000_0003]); - else - assert(cast(ulong[]) a == [0x0000_0001_0000_0002, 0x0000_0003_0000_0004]); - assert(a ~ [10,11] == [1,2,3,4,10,11]); - assert(a[0] == 1); - assert(a[] == [1,2,3,4]); - assert(a[2 .. 4] == [3,4]); - static if (is(T == MyArray)) // mutable - { - a = a; - a = [5,6,7,8]; assert(a == [5,6,7,8]); - a[0] = 0; assert(a == [0,6,7,8]); - a[] = 1; assert(a == [1,1,1,1]); - a[0 .. 3] = 2; assert(a == [2,2,2,1]); - a[0] += 2; assert(a == [4,2,2,1]); - a[] *= 2; assert(a == [8,4,4,2]); - a[0 .. 2] /= 2; assert(a == [4,2,4,2]); - } - }} -} -@system unittest -{ - class Foo - { - int field; - - @property int val1() const { return field; } - @property void val1(int n) { field = n; } - - @property ref int val2() { return field; } - - int func(int x, int y) const { return x; } - void func1(ref int a) { a = 9; } - - T ifti1(T)(T t) { return t; } - void ifti2(Args...)(Args args) { } - void ifti3(T, Args...)(Args args) { } - - T opCast(T)(){ return T.init; } - - T tempfunc(T)() { return T.init; } - } - class Hoge - { - Foo foo; - mixin Proxy!foo; - this(Foo f) { foo = f; } - } - - auto h = new Hoge(new Foo()); - int n; - - static assert(!__traits(compiles, { Foo f = h; })); - - // field - h.field = 1; // lhs of assign - n = h.field; // rhs of assign - assert(h.field == 1); // lhs of BinExp - assert(1 == h.field); // rhs of BinExp - assert(n == 1); - - // getter/setter property function - h.val1 = 4; - n = h.val1; - assert(h.val1 == 4); - assert(4 == h.val1); - assert(n == 4); - - // ref getter property function - h.val2 = 8; - n = h.val2; - assert(h.val2 == 8); - assert(8 == h.val2); - assert(n == 8); - - // member function - assert(h.func(2,4) == 2); - h.func1(n); - assert(n == 9); - - // IFTI - assert(h.ifti1(4) == 4); - h.ifti2(4); - h.ifti3!int(4, 3); - - // https://issues.dlang.org/show_bug.cgi?id=5896 test - assert(h.opCast!int() == 0); - assert(cast(int) h == 0); - const ih = new const Hoge(new Foo()); - static assert(!__traits(compiles, ih.opCast!int())); - static assert(!__traits(compiles, cast(int) ih)); - - // template member function - assert(h.tempfunc!int() == 0); -} - -@system unittest // about Proxy inside a class -{ - class MyClass - { - int payload; - mixin Proxy!payload; - this(int i){ payload = i; } - string opCall(string msg){ return msg; } - int pow(int i){ return payload ^^ i; } - } - - class MyClass2 - { - MyClass payload; - mixin Proxy!payload; - this(int i){ payload = new MyClass(i); } - } - - class MyClass3 - { - int payload; - mixin Proxy!payload; - this(int i){ payload = i; } - } - - // opEquals - Object a = new MyClass(5); - Object b = new MyClass(5); - Object c = new MyClass2(5); - Object d = new MyClass3(5); - assert(a == b); - assert((cast(MyClass) a) == 5); - assert(5 == (cast(MyClass) b)); - assert(5 == cast(MyClass2) c); - assert(a != d); - - assert(c != a); - // oops! above line is unexpected, isn't it? - // the reason is below. - // MyClass2.opEquals knows MyClass but, - // MyClass.opEquals doesn't know MyClass2. - // so, c.opEquals(a) is true, but a.opEquals(c) is false. - // furthermore, opEquals(T) couldn't be invoked. - assert((cast(MyClass2) c) != (cast(MyClass) a)); - - // opCmp - Object e = new MyClass2(7); - assert(a < cast(MyClass2) e); // OK. and - assert(e > a); // OK, but... - // assert(a < e); // RUNTIME ERROR! - // assert((cast(MyClass) a) < e); // RUNTIME ERROR! - assert(3 < cast(MyClass) a); - assert((cast(MyClass2) e) < 11); - - // opCall - assert((cast(MyClass2) e)("hello") == "hello"); - - // opCast - assert((cast(MyClass)(cast(MyClass2) c)) == a); - assert((cast(int)(cast(MyClass2) c)) == 5); - - // opIndex - class MyClass4 - { - string payload; - mixin Proxy!payload; - this(string s){ payload = s; } - } - class MyClass5 - { - MyClass4 payload; - mixin Proxy!payload; - this(string s){ payload = new MyClass4(s); } - } - auto f = new MyClass4("hello"); - assert(f[1] == 'e'); - auto g = new MyClass5("hello"); - assert(f[1] == 'e'); - - // opSlice - assert(f[2 .. 4] == "ll"); - - // opUnary - assert(-(cast(MyClass2) c) == -5); - - // opBinary - assert((cast(MyClass) a) + (cast(MyClass2) c) == 10); - assert(5 + cast(MyClass) a == 10); - - // opAssign - (cast(MyClass2) c) = 11; - assert((cast(MyClass2) c) == 11); - (cast(MyClass2) c) = new MyClass(13); - assert((cast(MyClass2) c) == 13); - - // opOpAssign - assert((cast(MyClass2) c) += 4); - assert((cast(MyClass2) c) == 17); - - // opDispatch - assert((cast(MyClass2) c).pow(2) == 289); - - // opDollar - assert(f[2..$-1] == "ll"); - - // toHash - int[Object] hash; - hash[a] = 19; - hash[c] = 21; - assert(hash[b] == 19); - assert(hash[c] == 21); -} - -@safe unittest -{ - struct MyInt - { - int payload; - - mixin Proxy!payload; - } - - MyInt v; - v = v; - - struct Foo - { - @disable void opAssign(typeof(this)); - } - struct MyFoo - { - Foo payload; - - mixin Proxy!payload; - } - MyFoo f; - static assert(!__traits(compiles, f = f)); - - struct MyFoo2 - { - Foo payload; - - mixin Proxy!payload; - - // override default Proxy behavior - void opAssign(typeof(this) rhs){} - } - MyFoo2 f2; - f2 = f2; -} - -// https://issues.dlang.org/show_bug.cgi?id=8613 -@safe unittest -{ - static struct Name - { - mixin Proxy!val; - private string val; - this(string s) { val = s; } - } - - bool[Name] names; - names[Name("a")] = true; - bool* b = Name("a") in names; -} - -// workaround for https://issues.dlang.org/show_bug.cgi?id=19669 -private enum isDIP1000 = __traits(compiles, () @safe { - int x; - int* p; - p = &x; -}); -// excludes struct S; it's 'mixin Proxy!foo' doesn't compile with -dip1000 -static if (isDIP1000) {} else -@system unittest -{ - // https://issues.dlang.org/show_bug.cgi?id=14213 - // using function for the payload - static struct S - { - int foo() { return 12; } - mixin Proxy!foo; - } - S s; - assert(s + 1 == 13); - assert(s * 2 == 24); -} - -@system unittest -{ - static class C - { - int foo() { return 12; } - mixin Proxy!foo; - } - C c = new C(); -} - -// Check all floating point comparisons for both Proxy and Typedef, -// also against int and a Typedef!int, to be as regression-proof -// as possible. https://issues.dlang.org/show_bug.cgi?id=15561 -@safe unittest -{ - static struct MyFloatImpl - { - float value; - mixin Proxy!value; - } - static void allFail(T0, T1)(T0 a, T1 b) - { - assert(!(a == b)); - assert(!(a<b)); - assert(!(a <= b)); - assert(!(a>b)); - assert(!(a >= b)); - } - static foreach (T1; AliasSeq!(MyFloatImpl, Typedef!float, Typedef!double, - float, real, Typedef!int, int)) - { - static foreach (T2; AliasSeq!(MyFloatImpl, Typedef!float)) - {{ - T1 a; - T2 b; - - static if (isFloatingPoint!T1 || isFloatingPoint!(TypedefType!T1)) - allFail(a, b); - a = 3; - allFail(a, b); - - b = 4; - assert(a != b); - assert(a<b); - assert(a <= b); - assert(!(a>b)); - assert(!(a >= b)); - - a = 4; - assert(a == b); - assert(!(a<b)); - assert(a <= b); - assert(!(a>b)); - assert(a >= b); - }} - } -} - -/** -$(B Typedef) allows the creation of a unique type which is -based on an existing type. Unlike the `alias` feature, -$(B Typedef) ensures the two types are not considered as equals. - -Params: - - init = Optional initial value for the new type. - cookie = Optional, used to create multiple unique types which are - based on the same origin type `T` - -Note: If a library routine cannot handle the Typedef type, -you can use the `TypedefType` template to extract the -type which the Typedef wraps. - */ -struct Typedef(T, T init = T.init, string cookie=null) -{ - private T Typedef_payload = init; - - // https://issues.dlang.org/show_bug.cgi?id=18415 - // prevent default construction if original type does too. - static if ((is(T == struct) || is(T == union)) && !is(typeof({T t;}))) - { - @disable this(); - } - - this(T init) - { - Typedef_payload = init; - } - - this(Typedef tdef) - { - this(tdef.Typedef_payload); - } - - // We need to add special overload for cast(Typedef!X) exp, - // thus we can't simply inherit Proxy!Typedef_payload - T2 opCast(T2 : Typedef!(T, Unused), this X, T, Unused...)() - { - return T2(cast(T) Typedef_payload); - } - - auto ref opCast(T2, this X)() - { - return cast(T2) Typedef_payload; - } - - mixin Proxy!Typedef_payload; - - pure nothrow @nogc @safe @property - { - alias TD = typeof(this); - static if (isIntegral!T) - { - static TD min() {return TD(T.min);} - static TD max() {return TD(T.max);} - } - else static if (isFloatingPoint!T) - { - static TD infinity() {return TD(T.infinity);} - static TD nan() {return TD(T.nan);} - static TD dig() {return TD(T.dig);} - static TD epsilon() {return TD(T.epsilon);} - static TD mant_dig() {return TD(T.mant_dig);} - static TD max_10_exp() {return TD(T.max_10_exp);} - static TD max_exp() {return TD(T.max_exp);} - static TD min_10_exp() {return TD(T.min_10_exp);} - static TD min_exp() {return TD(T.min_exp);} - static TD max() {return TD(T.max);} - static TD min_normal() {return TD(T.min_normal);} - TD re() {return TD(Typedef_payload.re);} - TD im() {return TD(Typedef_payload.im);} - } - } - - /** - * Convert wrapped value to a human readable string - */ - string toString(this T)() - { - import std.array : appender; - auto app = appender!string(); - auto spec = singleSpec("%s"); - toString(app, spec); - return app.data; - } - - /// ditto - void toString(this T, W)(ref W writer, scope const ref FormatSpec!char fmt) - if (isOutputRange!(W, char)) - { - formatValue(writer, Typedef_payload, fmt); - } - - /// - @safe unittest - { - import std.conv : to; - - int i = 123; - auto td = Typedef!int(i); - assert(i.to!string == td.to!string); - } -} - -/// -@safe unittest -{ - alias MyInt = Typedef!int; - MyInt foo = 10; - foo++; - assert(foo == 11); -} - -/// custom initialization values -@safe unittest -{ - alias MyIntInit = Typedef!(int, 42); - static assert(is(TypedefType!MyIntInit == int)); - static assert(MyIntInit() == 42); -} - -/// Typedef creates a new type -@safe unittest -{ - alias MyInt = Typedef!int; - static void takeInt(int) {} - static void takeMyInt(MyInt) {} - - int i; - takeInt(i); // ok - static assert(!__traits(compiles, takeMyInt(i))); - - MyInt myInt; - static assert(!__traits(compiles, takeInt(myInt))); - takeMyInt(myInt); // ok -} - -/// Use the optional `cookie` argument to create different types of the same base type -@safe unittest -{ - alias TypeInt1 = Typedef!int; - alias TypeInt2 = Typedef!int; - - // The two Typedefs are the same type. - static assert(is(TypeInt1 == TypeInt2)); - - alias MoneyEuros = Typedef!(float, float.init, "euros"); - alias MoneyDollars = Typedef!(float, float.init, "dollars"); - - // The two Typedefs are _not_ the same type. - static assert(!is(MoneyEuros == MoneyDollars)); -} - -// https://issues.dlang.org/show_bug.cgi?id=12461 -@safe unittest -{ - alias Int = Typedef!int; - - Int a, b; - a += b; - assert(a == 0); -} - -/** -Get the underlying type which a `Typedef` wraps. -If `T` is not a `Typedef` it will alias itself to `T`. -*/ -template TypedefType(T) -{ - static if (is(T : Typedef!Arg, Arg)) - alias TypedefType = Arg; - else - alias TypedefType = T; -} - -/// -@safe unittest -{ - import std.conv : to; - - alias MyInt = Typedef!int; - static assert(is(TypedefType!MyInt == int)); - - /// Instantiating with a non-Typedef will return that type - static assert(is(TypedefType!int == int)); - - string num = "5"; - - // extract the needed type - MyInt myInt = MyInt( num.to!(TypedefType!MyInt) ); - assert(myInt == 5); - - // cast to the underlying type to get the value that's being wrapped - int x = cast(TypedefType!MyInt) myInt; - - alias MyIntInit = Typedef!(int, 42); - static assert(is(TypedefType!MyIntInit == int)); - static assert(MyIntInit() == 42); -} - -@safe unittest -{ - Typedef!int x = 10; - static assert(!__traits(compiles, { int y = x; })); - static assert(!__traits(compiles, { long z = x; })); - - Typedef!int y = 10; - assert(x == y); - - static assert(Typedef!int.init == int.init); - - Typedef!(float, 1.0) z; // specifies the init - assert(z == 1.0); - - static assert(typeof(z).init == 1.0); - - alias Dollar = Typedef!(int, 0, "dollar"); - alias Yen = Typedef!(int, 0, "yen"); - static assert(!is(Dollar == Yen)); - - Typedef!(int[3]) sa; - static assert(sa.length == 3); - static assert(typeof(sa).length == 3); - - Typedef!(int[3]) dollar1; - assert(dollar1[0..$] is dollar1[0 .. 3]); - - Typedef!(int[]) dollar2; - dollar2.length = 3; - assert(dollar2[0..$] is dollar2[0 .. 3]); - - static struct Dollar1 - { - static struct DollarToken {} - enum opDollar = DollarToken.init; - auto opSlice(size_t, DollarToken) { return 1; } - auto opSlice(size_t, size_t) { return 2; } - } - - Typedef!Dollar1 drange1; - assert(drange1[0..$] == 1); - assert(drange1[0 .. 1] == 2); - - static struct Dollar2 - { - size_t opDollar(size_t pos)() { return pos == 0 ? 1 : 100; } - size_t opIndex(size_t i, size_t j) { return i + j; } - } - - Typedef!Dollar2 drange2; - assert(drange2[$, $] == 101); - - static struct Dollar3 - { - size_t opDollar() { return 123; } - size_t opIndex(size_t i) { return i; } - } - - Typedef!Dollar3 drange3; - assert(drange3[$] == 123); -} - -// https://issues.dlang.org/show_bug.cgi?id=18415 -@safe @nogc pure nothrow unittest -{ - struct NoDefCtorS{@disable this();} - union NoDefCtorU{@disable this();} - static assert(!is(typeof({Typedef!NoDefCtorS s;}))); - static assert(!is(typeof({Typedef!NoDefCtorU u;}))); -} - -// https://issues.dlang.org/show_bug.cgi?id=11703 -@safe @nogc pure nothrow unittest -{ - alias I = Typedef!int; - static assert(is(typeof(I.min) == I)); - static assert(is(typeof(I.max) == I)); - - alias F = Typedef!double; - static assert(is(typeof(F.infinity) == F)); - static assert(is(typeof(F.epsilon) == F)); - - F f; - assert(!is(typeof(F.re).stringof == double)); - assert(!is(typeof(F.im).stringof == double)); -} - -@safe unittest -{ - // https://issues.dlang.org/show_bug.cgi?id=8655 - import std.typecons; - import std.bitmanip; - static import core.stdc.config; - - alias c_ulong = Typedef!(core.stdc.config.c_ulong); - - static struct Foo - { - mixin(bitfields!( - c_ulong, "NameOffset", 31, - c_ulong, "NameIsString", 1 - )); - } -} - -// https://issues.dlang.org/show_bug.cgi?id=12596 -@safe unittest -{ - import std.typecons; - alias TD = Typedef!int; - TD x = TD(1); - TD y = TD(x); - assert(x == y); -} - -@safe unittest // about toHash -{ - import std.typecons; - { - alias TD = Typedef!int; - int[TD] td; - td[TD(1)] = 1; - assert(td[TD(1)] == 1); - } - - { - alias TD = Typedef!(int[]); - int[TD] td; - td[TD([1,2,3,4])] = 2; - assert(td[TD([1,2,3,4])] == 2); - } - - { - alias TD = Typedef!(int[][]); - int[TD] td; - td[TD([[1,0,0,0], [0,1,0,0], [0,0,1,0], [0,0,0,1]])] = 3; - assert(td[TD([[1,0,0,0], [0,1,0,0], [0,0,1,0], [0,0,0,1]])] == 3); - } - - { - struct MyStruct{ int x; } - alias TD = Typedef!MyStruct; - int[TD] td; - td[TD(MyStruct(10))] = 4; - assert(TD(MyStruct(20)) !in td); - assert(td[TD(MyStruct(10))] == 4); - } - - { - static struct MyStruct2 - { - int x; - size_t toHash() const nothrow @safe { return x; } - bool opEquals(ref const MyStruct2 r) const { return r.x == x; } - } - - alias TD = Typedef!MyStruct2; - int[TD] td; - td[TD(MyStruct2(50))] = 5; - assert(td[TD(MyStruct2(50))] == 5); - } - - { - class MyClass{} - alias TD = Typedef!MyClass; - int[TD] td; - auto c = new MyClass; - td[TD(c)] = 6; - assert(TD(new MyClass) !in td); - assert(td[TD(c)] == 6); - } -} - -@system unittest -{ - alias String = Typedef!(char[]); - alias CString = Typedef!(const(char)[]); - CString cs = "fubar"; - String s = cast(String) cs; - assert(cs == s); - char[] s2 = cast(char[]) cs; - const(char)[] cs2 = cast(const(char)[])s; - assert(s2 == cs2); -} - -@system unittest // toString -{ - import std.meta : AliasSeq; - import std.conv : to; - - struct TestS {} - class TestC {} - - static foreach (T; AliasSeq!(int, bool, float, double, real, - char, dchar, wchar, - TestS, TestC, - int*, int[], int[2], int[int])) - {{ - T t; - - Typedef!T td; - Typedef!(const T) ctd; - Typedef!(immutable T) itd; - - assert(t.to!string() == td.to!string()); - - static if (!(is(T == TestS) || is(T == TestC))) - { - assert(t.to!string() == ctd.to!string()); - assert(t.to!string() == itd.to!string()); - } - }} -} - -@safe @nogc unittest // typedef'ed type with custom operators -{ - static struct MyInt - { - int value; - int opCmp(MyInt other) - { - if (value < other.value) - return -1; - return !(value == other.value); - } - } - - auto m1 = Typedef!MyInt(MyInt(1)); - auto m2 = Typedef!MyInt(MyInt(2)); - assert(m1 < m2); -} - -/** -Allocates a `class` object right inside the current scope, -therefore avoiding the overhead of `new`. This facility is unsafe; -it is the responsibility of the user to not escape a reference to the -object outside the scope. - -The class destructor will be called when the result of `scoped()` is -itself destroyed. - -Scoped class instances can be embedded in a parent `class` or `struct`, -just like a child struct instance. Scoped member variables must have -type `typeof(scoped!Class(args))`, and be initialized with a call to -scoped. See below for an example. - -Note: -It's illegal to move a class instance even if you are sure there -are no pointers to it. As such, it is illegal to move a scoped object. - */ -template scoped(T) -if (is(T == class)) -{ - // _d_newclass now use default GC alignment (looks like (void*).sizeof * 2 for - // small objects). We will just use the maximum of filed alignments. - enum alignment = __traits(classInstanceAlignment, T); - alias aligned = _alignUp!alignment; - - static struct Scoped - { - // Addition of `alignment` is required as `Scoped_store` can be misaligned in memory. - private void[aligned(__traits(classInstanceSize, T) + size_t.sizeof) + alignment] Scoped_store = void; - - @property inout(T) Scoped_payload() inout - { - void* alignedStore = cast(void*) aligned(cast(size_t) Scoped_store.ptr); - // As `Scoped` can be unaligned moved in memory class instance should be moved accordingly. - immutable size_t d = alignedStore - Scoped_store.ptr; - size_t* currD = cast(size_t*) &Scoped_store[$ - size_t.sizeof]; - if (d != *currD) - { - import core.stdc.string : memmove; - memmove(alignedStore, Scoped_store.ptr + *currD, __traits(classInstanceSize, T)); - *currD = d; - } - return cast(inout(T)) alignedStore; - } - alias Scoped_payload this; - - @disable this(); - @disable this(this); - - ~this() - { - // `destroy` will also write .init but we have no functions in druntime - // for deterministic finalization and memory releasing for now. - .destroy(Scoped_payload); - } - } - - /** Returns the _scoped object. - Params: args = Arguments to pass to `T`'s constructor. - */ - @system auto scoped(Args...)(auto ref Args args) - { - import core.lifetime : emplace, forward; - - Scoped result = void; - void* alignedStore = cast(void*) aligned(cast(size_t) result.Scoped_store.ptr); - immutable size_t d = alignedStore - result.Scoped_store.ptr; - *cast(size_t*) &result.Scoped_store[$ - size_t.sizeof] = d; - emplace!(Unqual!T)(result.Scoped_store[d .. $ - size_t.sizeof], forward!args); - return result; - } -} - -/// -@system unittest -{ - class A - { - int x; - this() {x = 0;} - this(int i){x = i;} - ~this() {} - } - - // Standard usage, constructing A on the stack - auto a1 = scoped!A(); - a1.x = 42; - - // Result of `scoped` call implicitly converts to a class reference - A aRef = a1; - assert(aRef.x == 42); - - // Scoped destruction - { - auto a2 = scoped!A(1); - assert(a2.x == 1); - aRef = a2; - // a2 is destroyed here, calling A's destructor - } - // aRef is now an invalid reference - - // Here the temporary scoped A is immediately destroyed. - // This means the reference is then invalid. - version (Bug) - { - // Wrong, should use `auto` - A invalid = scoped!A(); - } - - // Restrictions - version (Bug) - { - import std.algorithm.mutation : move; - auto invalid = a1.move; // illegal, scoped objects can't be moved - } - static assert(!is(typeof({ - auto e1 = a1; // illegal, scoped objects can't be copied - assert([a1][0].x == 42); // ditto - }))); - static assert(!is(typeof({ - alias ScopedObject = typeof(a1); - auto e2 = ScopedObject(); // illegal, must be built via scoped!A - auto e3 = ScopedObject(1); // ditto - }))); - - // Use with alias - alias makeScopedA = scoped!A; - auto a3 = makeScopedA(); - auto a4 = makeScopedA(1); - - // Use as member variable - struct B - { - typeof(scoped!A()) a; // note the trailing parentheses - - this(int i) - { - // construct member - a = scoped!A(i); - } - } - - // Stack-allocate - auto b1 = B(5); - aRef = b1.a; - assert(aRef.x == 5); - destroy(b1); // calls A's destructor for b1.a - // aRef is now an invalid reference - - // Heap-allocate - auto b2 = new B(6); - assert(b2.a.x == 6); - destroy(*b2); // calls A's destructor for b2.a -} - -private size_t _alignUp(size_t alignment)(size_t n) -if (alignment > 0 && !((alignment - 1) & alignment)) -{ - enum badEnd = alignment - 1; // 0b11, 0b111, ... - return (n + badEnd) & ~badEnd; -} - -// https://issues.dlang.org/show_bug.cgi?id=6580 testcase -@system unittest -{ - enum alignment = (void*).alignof; - - static class C0 { } - static class C1 { byte b; } - static class C2 { byte[2] b; } - static class C3 { byte[3] b; } - static class C7 { byte[7] b; } - static assert(scoped!C0().sizeof % alignment == 0); - static assert(scoped!C1().sizeof % alignment == 0); - static assert(scoped!C2().sizeof % alignment == 0); - static assert(scoped!C3().sizeof % alignment == 0); - static assert(scoped!C7().sizeof % alignment == 0); - - enum longAlignment = long.alignof; - static class C1long - { - long long_; byte byte_ = 4; - this() { } - this(long _long, ref int i) { long_ = _long; ++i; } - } - static class C2long { byte[2] byte_ = [5, 6]; long long_ = 7; } - static assert(scoped!C1long().sizeof % longAlignment == 0); - static assert(scoped!C2long().sizeof % longAlignment == 0); - - void alignmentTest() - { - int var = 5; - auto c1long = scoped!C1long(3, var); - assert(var == 6); - auto c2long = scoped!C2long(); - assert(cast(uint)&c1long.long_ % longAlignment == 0); - assert(cast(uint)&c2long.long_ % longAlignment == 0); - assert(c1long.long_ == 3 && c1long.byte_ == 4); - assert(c2long.byte_ == [5, 6] && c2long.long_ == 7); - } - - alignmentTest(); - - version (DigitalMars) - { - void test(size_t size) - { - import core.stdc.stdlib : alloca; - cast(void) alloca(size); - alignmentTest(); - } - foreach (i; 0 .. 10) - test(i); - } - else - { - void test(size_t size)() - { - byte[size] arr; - alignmentTest(); - } - static foreach (i; 0 .. 11) - test!i(); - } -} - -// Original https://issues.dlang.org/show_bug.cgi?id=6580 testcase -@system unittest -{ - class C { int i; byte b; } - - auto sa = [scoped!C(), scoped!C()]; - assert(cast(uint)&sa[0].i % int.alignof == 0); - assert(cast(uint)&sa[1].i % int.alignof == 0); // fails -} - -@system unittest -{ - class A { int x = 1; } - auto a1 = scoped!A(); - assert(a1.x == 1); - auto a2 = scoped!A(); - a1.x = 42; - a2.x = 53; - assert(a1.x == 42); -} - -@system unittest -{ - class A { int x = 1; this() { x = 2; } } - auto a1 = scoped!A(); - assert(a1.x == 2); - auto a2 = scoped!A(); - a1.x = 42; - a2.x = 53; - assert(a1.x == 42); -} - -@system unittest -{ - class A { int x = 1; this(int y) { x = y; } ~this() {} } - auto a1 = scoped!A(5); - assert(a1.x == 5); - auto a2 = scoped!A(42); - a1.x = 42; - a2.x = 53; - assert(a1.x == 42); -} - -@system unittest -{ - class A { static bool dead; ~this() { dead = true; } } - class B : A { static bool dead; ~this() { dead = true; } } - { - auto b = scoped!B(); - } - assert(B.dead, "asdasd"); - assert(A.dead, "asdasd"); -} - -// https://issues.dlang.org/show_bug.cgi?id=8039 testcase -@system unittest -{ - static int dels; - static struct S { ~this(){ ++dels; } } - - static class A { S s; } - dels = 0; { scoped!A(); } - assert(dels == 1); - - static class B { S[2] s; } - dels = 0; { scoped!B(); } - assert(dels == 2); - - static struct S2 { S[3] s; } - static class C { S2[2] s; } - dels = 0; { scoped!C(); } - assert(dels == 6); - - static class D: A { S2[2] s; } - dels = 0; { scoped!D(); } - assert(dels == 1+6); -} - -@system unittest -{ - // https://issues.dlang.org/show_bug.cgi?id=4500 - class A - { - this() { a = this; } - this(int i) { a = this; } - A a; - bool check() { return this is a; } - } - - auto a1 = scoped!A(); - assert(a1.check()); - - auto a2 = scoped!A(1); - assert(a2.check()); - - a1.a = a1; - assert(a1.check()); -} - -@system unittest -{ - static class A - { - static int sdtor; - - this() { ++sdtor; assert(sdtor == 1); } - ~this() { assert(sdtor == 1); --sdtor; } - } - - interface Bob {} - - static class ABob : A, Bob - { - this() { ++sdtor; assert(sdtor == 2); } - ~this() { assert(sdtor == 2); --sdtor; } - } - - A.sdtor = 0; - scope(exit) assert(A.sdtor == 0); - auto abob = scoped!ABob(); -} - -@safe unittest -{ - static class A { this(int) {} } - static assert(!__traits(compiles, scoped!A())); -} - -@system unittest -{ - static class A { @property inout(int) foo() inout { return 1; } } - - auto a1 = scoped!A(); - assert(a1.foo == 1); - static assert(is(typeof(a1.foo) == int)); - - auto a2 = scoped!(const(A))(); - assert(a2.foo == 1); - static assert(is(typeof(a2.foo) == const(int))); - - auto a3 = scoped!(immutable(A))(); - assert(a3.foo == 1); - static assert(is(typeof(a3.foo) == immutable(int))); - - const c1 = scoped!A(); - assert(c1.foo == 1); - static assert(is(typeof(c1.foo) == const(int))); - - const c2 = scoped!(const(A))(); - assert(c2.foo == 1); - static assert(is(typeof(c2.foo) == const(int))); - - const c3 = scoped!(immutable(A))(); - assert(c3.foo == 1); - static assert(is(typeof(c3.foo) == immutable(int))); -} - -@system unittest -{ - class C - { - this(int rval) { assert(rval == 1); } - this(ref int lval) { assert(lval == 3); ++lval; } - } - - auto c1 = scoped!C(1); - int lval = 3; - auto c2 = scoped!C(lval); - assert(lval == 4); -} - -@system unittest -{ - class C - { - this(){} - this(int){} - this(int, int){} - } - alias makeScopedC = scoped!C; - - auto a = makeScopedC(); - auto b = makeScopedC(1); - auto c = makeScopedC(1, 1); - - static assert(is(typeof(a) == typeof(b))); - static assert(is(typeof(b) == typeof(c))); -} - -/** -Defines a simple, self-documenting yes/no flag. This makes it easy for -APIs to define functions accepting flags without resorting to $(D -bool), which is opaque in calls, and without needing to define an -enumerated type separately. Using `Flag!"Name"` instead of $(D -bool) makes the flag's meaning visible in calls. Each yes/no flag has -its own type, which makes confusions and mix-ups impossible. - -Example: - -Code calling `getLine` (usually far away from its definition) can't be -understood without looking at the documentation, even by users familiar with -the API: ----- -string getLine(bool keepTerminator) -{ - ... - if (keepTerminator) ... - ... -} -... -auto line = getLine(false); ----- - -Assuming the reverse meaning (i.e. "ignoreTerminator") and inserting the wrong -code compiles and runs with erroneous results. - -After replacing the boolean parameter with an instantiation of `Flag`, code -calling `getLine` can be easily read and understood even by people not -fluent with the API: - ----- -string getLine(Flag!"keepTerminator" keepTerminator) -{ - ... - if (keepTerminator) ... - ... -} -... -auto line = getLine(Yes.keepTerminator); ----- - -The structs `Yes` and `No` are provided as shorthand for -`Flag!"Name".yes` and `Flag!"Name".no` and are preferred for brevity and -readability. These convenience structs mean it is usually unnecessary and -counterproductive to create an alias of a `Flag` as a way of avoiding typing -out the full type while specifying the affirmative or negative options. - -Passing categorical data by means of unstructured `bool` -parameters is classified under "simple-data coupling" by Steve -McConnell in the $(LUCKY Code Complete) book, along with three other -kinds of coupling. The author argues citing several studies that -coupling has a negative effect on code quality. `Flag` offers a -simple structuring method for passing yes/no flags to APIs. - */ -template Flag(string name) { - /// - enum Flag : bool - { - /** - When creating a value of type `Flag!"Name"`, use $(D - Flag!"Name".no) for the negative option. When using a value - of type `Flag!"Name"`, compare it against $(D - Flag!"Name".no) or just `false` or `0`. */ - no = false, - - /** When creating a value of type `Flag!"Name"`, use $(D - Flag!"Name".yes) for the affirmative option. When using a - value of type `Flag!"Name"`, compare it against $(D - Flag!"Name".yes). - */ - yes = true - } -} - -/// -@safe unittest -{ - Flag!"abc" flag; - - assert(flag == Flag!"abc".no); - assert(flag == No.abc); - assert(!flag); - if (flag) assert(0); -} - -/// -@safe unittest -{ - auto flag = Yes.abc; - - assert(flag); - assert(flag == Yes.abc); - if (!flag) assert(0); - if (flag) {} else assert(0); -} - -/** -Convenience names that allow using e.g. `Yes.encryption` instead of -`Flag!"encryption".yes` and `No.encryption` instead of $(D -Flag!"encryption".no). -*/ -struct Yes -{ - template opDispatch(string name) - { - enum opDispatch = Flag!name.yes; - } -} -//template yes(string name) { enum Flag!name yes = Flag!name.yes; } - -/// Ditto -struct No -{ - template opDispatch(string name) - { - enum opDispatch = Flag!name.no; - } -} - -/// -@safe unittest -{ - Flag!"abc" flag; - - assert(flag == Flag!"abc".no); - assert(flag == No.abc); - assert(!flag); - if (flag) assert(0); -} - -/// -@safe unittest -{ - auto flag = Yes.abc; - - assert(flag); - assert(flag == Yes.abc); - if (!flag) assert(0); - if (flag) {} else assert(0); -} - -/** -Detect whether an enum is of integral type and has only "flag" values -(i.e. values with a bit count of exactly 1). -Additionally, a zero value is allowed for compatibility with enums including -a "None" value. -*/ -template isBitFlagEnum(E) -{ - static if (is(E Base == enum) && isIntegral!Base) - { - enum isBitFlagEnum = (E.min >= 0) && - { - static foreach (immutable flag; EnumMembers!E) - {{ - Base value = flag; - value &= value - 1; - if (value != 0) return false; - }} - return true; - }(); - } - else - { - enum isBitFlagEnum = false; - } -} - -/// -@safe pure nothrow unittest -{ - enum A - { - None, - A = 1 << 0, - B = 1 << 1, - C = 1 << 2, - D = 1 << 3, - } - - static assert(isBitFlagEnum!A); -} - -/// Test an enum with default (consecutive) values -@safe pure nothrow unittest -{ - enum B - { - A, - B, - C, - D // D == 3 - } - - static assert(!isBitFlagEnum!B); -} - -/// Test an enum with non-integral values -@safe pure nothrow unittest -{ - enum C: double - { - A = 1 << 0, - B = 1 << 1 - } - - static assert(!isBitFlagEnum!C); -} - -/** -A typesafe structure for storing combinations of enum values. - -This template defines a simple struct to represent bitwise OR combinations of -enum values. It can be used if all the enum values are integral constants with -a bit count of at most 1, or if the `unsafe` parameter is explicitly set to -Yes. -This is much safer than using the enum itself to store -the OR combination, which can produce surprising effects like this: ----- -enum E -{ - A = 1 << 0, - B = 1 << 1 -} -E e = E.A | E.B; -// will throw SwitchError -final switch (e) -{ - case E.A: - return; - case E.B: - return; -} ----- -*/ -struct BitFlags(E, Flag!"unsafe" unsafe = No.unsafe) -if (unsafe || isBitFlagEnum!(E)) -{ -@safe @nogc pure nothrow: -private: - enum isBaseEnumType(T) = is(E == T); - alias Base = OriginalType!E; - Base mValue; - -public: - this(E flag) - { - this = flag; - } - - this(T...)(T flags) - if (allSatisfy!(isBaseEnumType, T)) - { - this = flags; - } - - bool opCast(B: bool)() const - { - return mValue != 0; - } - - Base opCast(B)() const - if (is(Base : B)) - { - return mValue; - } - - auto opUnary(string op)() const - if (op == "~") - { - return BitFlags(cast(E) cast(Base) ~mValue); - } - - auto ref opAssign(T...)(T flags) - if (allSatisfy!(isBaseEnumType, T)) - { - mValue = 0; - foreach (E flag; flags) - { - mValue |= flag; - } - return this; - } - - auto ref opAssign(E flag) - { - mValue = flag; - return this; - } - - auto ref opOpAssign(string op: "|")(BitFlags flags) - { - mValue |= flags.mValue; - return this; - } - - auto ref opOpAssign(string op: "&")(BitFlags flags) - { - mValue &= flags.mValue; - return this; - } - - auto ref opOpAssign(string op: "|")(E flag) - { - mValue |= flag; - return this; - } - - auto ref opOpAssign(string op: "&")(E flag) - { - mValue &= flag; - return this; - } - - auto opBinary(string op)(BitFlags flags) const - if (op == "|" || op == "&") - { - BitFlags result = this; - result.opOpAssign!op(flags); - return result; - } - - auto opBinary(string op)(E flag) const - if (op == "|" || op == "&") - { - BitFlags result = this; - result.opOpAssign!op(flag); - return result; - } - - auto opBinaryRight(string op)(E flag) const - if (op == "|" || op == "&") - { - return opBinary!op(flag); - } - - bool opDispatch(string name)() const - if (__traits(hasMember, E, name)) - { - enum e = __traits(getMember, E, name); - return (mValue & e) == e; - } - - void opDispatch(string name)(bool set) - if (__traits(hasMember, E, name)) - { - enum e = __traits(getMember, E, name); - if (set) - mValue |= e; - else - mValue &= ~e; - } -} - -/// Set values with the | operator and test with & -@safe @nogc pure nothrow unittest -{ - enum Enum - { - A = 1 << 0, - } - - // A default constructed BitFlags has no value set - immutable BitFlags!Enum flags_empty; - assert(!flags_empty.A); - - // Value can be set with the | operator - immutable flags_A = flags_empty | Enum.A; - - // and tested using property access - assert(flags_A.A); - - // or the & operator - assert(flags_A & Enum.A); - // which commutes. - assert(Enum.A & flags_A); -} - -/// A default constructed BitFlags has no value set -@safe @nogc pure nothrow unittest -{ - enum Enum - { - None, - A = 1 << 0, - B = 1 << 1, - C = 1 << 2 - } - - immutable BitFlags!Enum flags_empty; - assert(!(flags_empty & (Enum.A | Enum.B | Enum.C))); - assert(!(flags_empty & Enum.A) && !(flags_empty & Enum.B) && !(flags_empty & Enum.C)); -} - -// BitFlags can be variadically initialized -@safe @nogc pure nothrow unittest -{ - import std.traits : EnumMembers; - - enum Enum - { - A = 1 << 0, - B = 1 << 1, - C = 1 << 2 - } - - // Values can also be set using property access - BitFlags!Enum flags; - flags.A = true; - assert(flags & Enum.A); - flags.A = false; - assert(!(flags & Enum.A)); - - // BitFlags can be variadically initialized - immutable BitFlags!Enum flags_AB = BitFlags!Enum(Enum.A, Enum.B); - assert(flags_AB.A && flags_AB.B && !flags_AB.C); - - // You can use the EnumMembers template to set all flags - immutable BitFlags!Enum flags_all = EnumMembers!Enum; - assert(flags_all.A && flags_all.B && flags_all.C); -} - -/// Binary operations: subtracting and intersecting flags -@safe @nogc pure nothrow unittest -{ - enum Enum - { - A = 1 << 0, - B = 1 << 1, - C = 1 << 2, - } - immutable BitFlags!Enum flags_AB = BitFlags!Enum(Enum.A, Enum.B); - immutable BitFlags!Enum flags_BC = BitFlags!Enum(Enum.B, Enum.C); - - // Use the ~ operator for subtracting flags - immutable BitFlags!Enum flags_B = flags_AB & ~BitFlags!Enum(Enum.A); - assert(!flags_B.A && flags_B.B && !flags_B.C); - - // use & between BitFlags for intersection - assert(flags_B == (flags_BC & flags_AB)); -} - -/// All the binary operators work in their assignment version -@safe @nogc pure nothrow unittest -{ - enum Enum - { - A = 1 << 0, - B = 1 << 1, - } - - BitFlags!Enum flags_empty, temp, flags_AB; - flags_AB = Enum.A | Enum.B; - - temp |= flags_AB; - assert(temp == (flags_empty | flags_AB)); - - temp = flags_empty; - temp |= Enum.B; - assert(temp == (flags_empty | Enum.B)); - - temp = flags_empty; - temp &= flags_AB; - assert(temp == (flags_empty & flags_AB)); - - temp = flags_empty; - temp &= Enum.A; - assert(temp == (flags_empty & Enum.A)); -} - -/// Conversion to bool and int -@safe @nogc pure nothrow unittest -{ - enum Enum - { - A = 1 << 0, - B = 1 << 1, - } - - BitFlags!Enum flags; - - // BitFlags with no value set evaluate to false - assert(!flags); - - // BitFlags with at least one value set evaluate to true - flags |= Enum.A; - assert(flags); - - // This can be useful to check intersection between BitFlags - BitFlags!Enum flags_AB = Enum.A | Enum.B; - assert(flags & flags_AB); - assert(flags & Enum.A); - - // You can of course get you raw value out of flags - auto value = cast(int) flags; - assert(value == Enum.A); -} - -/// You need to specify the `unsafe` parameter for enums with custom values -@safe @nogc pure nothrow unittest -{ - enum UnsafeEnum - { - A = 1, - B = 2, - C = 4, - BC = B|C - } - static assert(!__traits(compiles, { BitFlags!UnsafeEnum flags; })); - BitFlags!(UnsafeEnum, Yes.unsafe) flags; - - // property access tests for exact match of unsafe enums - flags.B = true; - assert(!flags.BC); // only B - flags.C = true; - assert(flags.BC); // both B and C - flags.B = false; - assert(!flags.BC); // only C - - // property access sets all bits of unsafe enum group - flags = flags.init; - flags.BC = true; - assert(!flags.A && flags.B && flags.C); - flags.A = true; - flags.BC = false; - assert(flags.A && !flags.B && !flags.C); -} - -// Negation of BitFlags should work with any base type. -// Double-negation of BitFlags should work. -@safe @nogc pure nothrow unittest -{ - static foreach (alias Base; AliasSeq!( - byte, - ubyte, - short, - ushort, - int, - uint, - long, - ulong, - )) - {{ - enum Enum : Base - { - A = 1 << 0, - B = 1 << 1, - C = 1 << 2, - } - - auto flags = BitFlags!Enum(Enum.A); - - assert(flags == ~~flags); - }} -} - -private enum false_(T) = false; - -// ReplaceType -/** -Replaces all occurrences of `From` into `To`, in one or more types `T`. For -example, `ReplaceType!(int, uint, Tuple!(int, float)[string])` yields -`Tuple!(uint, float)[string]`. The types in which replacement is performed -may be arbitrarily complex, including qualifiers, built-in type constructors -(pointers, arrays, associative arrays, functions, and delegates), and template -instantiations; replacement proceeds transitively through the type definition. -However, member types in `struct`s or `class`es are not replaced because there -are no ways to express the types resulting after replacement. - -This is an advanced type manipulation necessary e.g. for replacing the -placeholder type `This` in $(REF Algebraic, std,variant). - -Returns: `ReplaceType` aliases itself to the type(s) that result after -replacement. -*/ -alias ReplaceType(From, To, T...) = ReplaceTypeUnless!(false_, From, To, T); - -/// -@safe unittest -{ - static assert( - is(ReplaceType!(int, string, int[]) == string[]) && - is(ReplaceType!(int, string, int[int]) == string[string]) && - is(ReplaceType!(int, string, const(int)[]) == const(string)[]) && - is(ReplaceType!(int, string, Tuple!(int[], float)) - == Tuple!(string[], float)) - ); -} - -/** -Like $(LREF ReplaceType), but does not perform replacement in types for which -`pred` evaluates to `true`. -*/ -template ReplaceTypeUnless(alias pred, From, To, T...) -{ - import std.meta; - - static if (T.length == 1) - { - static if (pred!(T[0])) - alias ReplaceTypeUnless = T[0]; - else static if (is(T[0] == From)) - alias ReplaceTypeUnless = To; - else static if (is(T[0] == const(U), U)) - alias ReplaceTypeUnless = const(ReplaceTypeUnless!(pred, From, To, U)); - else static if (is(T[0] == immutable(U), U)) - alias ReplaceTypeUnless = immutable(ReplaceTypeUnless!(pred, From, To, U)); - else static if (is(T[0] == shared(U), U)) - alias ReplaceTypeUnless = shared(ReplaceTypeUnless!(pred, From, To, U)); - else static if (is(T[0] == U*, U)) - { - static if (is(U == function)) - alias ReplaceTypeUnless = replaceTypeInFunctionTypeUnless!(pred, From, To, T[0]); - else - alias ReplaceTypeUnless = ReplaceTypeUnless!(pred, From, To, U)*; - } - else static if (is(T[0] == delegate)) - { - alias ReplaceTypeUnless = replaceTypeInFunctionTypeUnless!(pred, From, To, T[0]); - } - else static if (is(T[0] == function)) - { - static assert(0, "Function types not supported," ~ - " use a function pointer type instead of " ~ T[0].stringof); - } - else static if (is(T[0] == U!V, alias U, V...)) - { - template replaceTemplateArgs(T...) - { - static if (is(typeof(T[0]))) { // template argument is value or symbol - static if (__traits(compiles, { alias _ = T[0]; })) - // it's a symbol - alias replaceTemplateArgs = T[0]; - else - // it's a value - enum replaceTemplateArgs = T[0]; - } else - alias replaceTemplateArgs = ReplaceTypeUnless!(pred, From, To, T[0]); - } - alias ReplaceTypeUnless = U!(staticMap!(replaceTemplateArgs, V)); - } - else static if (is(T[0] == struct)) - // don't match with alias this struct below - // https://issues.dlang.org/show_bug.cgi?id=15168 - alias ReplaceTypeUnless = T[0]; - else static if (is(T[0] == U[], U)) - alias ReplaceTypeUnless = ReplaceTypeUnless!(pred, From, To, U)[]; - else static if (is(T[0] == U[n], U, size_t n)) - alias ReplaceTypeUnless = ReplaceTypeUnless!(pred, From, To, U)[n]; - else static if (is(T[0] == U[V], U, V)) - alias ReplaceTypeUnless = - ReplaceTypeUnless!(pred, From, To, U)[ReplaceTypeUnless!(pred, From, To, V)]; - else - alias ReplaceTypeUnless = T[0]; - } - else static if (T.length > 1) - { - alias ReplaceTypeUnless = AliasSeq!(ReplaceTypeUnless!(pred, From, To, T[0]), - ReplaceTypeUnless!(pred, From, To, T[1 .. $])); - } - else - { - alias ReplaceTypeUnless = AliasSeq!(); - } -} - -/// -@safe unittest -{ - import std.traits : isArray; - - static assert( - is(ReplaceTypeUnless!(isArray, int, string, int*) == string*) && - is(ReplaceTypeUnless!(isArray, int, string, int[]) == int[]) && - is(ReplaceTypeUnless!(isArray, int, string, Tuple!(int, int[])) - == Tuple!(string, int[])) - ); -} - -private template replaceTypeInFunctionTypeUnless(alias pred, From, To, fun) -{ - alias RX = ReplaceTypeUnless!(pred, From, To, ReturnType!fun); - alias PX = AliasSeq!(ReplaceTypeUnless!(pred, From, To, Parameters!fun)); - // Wrapping with AliasSeq is neccesary because ReplaceType doesn't return - // tuple if Parameters!fun.length == 1 - - string gen() - { - enum linkage = functionLinkage!fun; - alias attributes = functionAttributes!fun; - enum variadicStyle = variadicFunctionStyle!fun; - alias storageClasses = ParameterStorageClassTuple!fun; - - string result; - - result ~= "extern(" ~ linkage ~ ") "; - static if (attributes & FunctionAttribute.ref_) - { - result ~= "ref "; - } - - result ~= "RX"; - static if (is(fun == delegate)) - result ~= " delegate"; - else - result ~= " function"; - - result ~= "("; - static foreach (i; 0 .. PX.length) - { - if (i) - result ~= ", "; - if (storageClasses[i] & ParameterStorageClass.scope_) - result ~= "scope "; - if (storageClasses[i] & ParameterStorageClass.in_) - result ~= "in "; - if (storageClasses[i] & ParameterStorageClass.out_) - result ~= "out "; - if (storageClasses[i] & ParameterStorageClass.ref_) - result ~= "ref "; - if (storageClasses[i] & ParameterStorageClass.lazy_) - result ~= "lazy "; - if (storageClasses[i] & ParameterStorageClass.return_) - result ~= "return "; - - result ~= "PX[" ~ i.stringof ~ "]"; - } - static if (variadicStyle == Variadic.typesafe) - result ~= " ..."; - else static if (variadicStyle != Variadic.no) - result ~= ", ..."; - result ~= ")"; - - static if (attributes & FunctionAttribute.pure_) - result ~= " pure"; - static if (attributes & FunctionAttribute.nothrow_) - result ~= " nothrow"; - static if (attributes & FunctionAttribute.property) - result ~= " @property"; - static if (attributes & FunctionAttribute.trusted) - result ~= " @trusted"; - static if (attributes & FunctionAttribute.safe) - result ~= " @safe"; - static if (attributes & FunctionAttribute.nogc) - result ~= " @nogc"; - static if (attributes & FunctionAttribute.system) - result ~= " @system"; - static if (attributes & FunctionAttribute.const_) - result ~= " const"; - static if (attributes & FunctionAttribute.immutable_) - result ~= " immutable"; - static if (attributes & FunctionAttribute.inout_) - result ~= " inout"; - static if (attributes & FunctionAttribute.shared_) - result ~= " shared"; - static if (attributes & FunctionAttribute.return_) - result ~= " return"; - static if (attributes & FunctionAttribute.live) - result ~= " @live"; - - return result; - } - - mixin("alias replaceTypeInFunctionTypeUnless = " ~ gen() ~ ";"); -} - -@safe unittest -{ - template Test(Ts...) - { - static if (Ts.length) - { - //pragma(msg, "Testing: ReplaceType!("~Ts[0].stringof~", " - // ~Ts[1].stringof~", "~Ts[2].stringof~")"); - static assert(is(ReplaceType!(Ts[0], Ts[1], Ts[2]) == Ts[3]), - "ReplaceType!("~Ts[0].stringof~", "~Ts[1].stringof~", " - ~Ts[2].stringof~") == " - ~ReplaceType!(Ts[0], Ts[1], Ts[2]).stringof); - alias Test = Test!(Ts[4 .. $]); - } - else alias Test = void; - } - - //import core.stdc.stdio; - alias RefFun1 = ref int function(float, long); - alias RefFun2 = ref float function(float, long); - extern(C) int printf(const char*, ...) nothrow @nogc @system; - extern(C) float floatPrintf(const char*, ...) nothrow @nogc @system; - int func(float); - - int x; - struct S1 { void foo() { x = 1; } } - struct S2 { void bar() { x = 2; } } - - alias Pass = Test!( - int, float, typeof(&func), float delegate(float), - int, float, typeof(&printf), typeof(&floatPrintf), - int, float, int function(out long, ...), - float function(out long, ...), - int, float, int function(ref float, long), - float function(ref float, long), - int, float, int function(ref int, long), - float function(ref float, long), - int, float, int function(out int, long), - float function(out float, long), - int, float, int function(lazy int, long), - float function(lazy float, long), - int, float, int function(out long, ref const int), - float function(out long, ref const float), - int, float, int function(in long, ref const int), - float function(in long, ref const float), - int, float, int function(long, in int), - float function(long, in float), - int, int, int, int, - int, float, int, float, - int, float, const int, const float, - int, float, immutable int, immutable float, - int, float, shared int, shared float, - int, float, int*, float*, - int, float, const(int)*, const(float)*, - int, float, const(int*), const(float*), - const(int)*, float, const(int*), const(float), - int*, float, const(int)*, const(int)*, - int, float, int[], float[], - int, float, int[42], float[42], - int, float, const(int)[42], const(float)[42], - int, float, const(int[42]), const(float[42]), - int, float, int[int], float[float], - int, float, int[double], float[double], - int, float, double[int], double[float], - int, float, int function(float, long), float function(float, long), - int, float, int function(float), float function(float), - int, float, int function(float, int), float function(float, float), - int, float, int delegate(float, long), float delegate(float, long), - int, float, int delegate(float), float delegate(float), - int, float, int delegate(float, int), float delegate(float, float), - int, float, Unique!int, Unique!float, - int, float, Tuple!(float, int), Tuple!(float, float), - int, float, RefFun1, RefFun2, - S1, S2, - S1[1][][S1]* function(), - S2[1][][S2]* function(), - int, string, - int[3] function( int[] arr, int[2] ...) pure @trusted, - string[3] function(string[] arr, string[2] ...) pure @trusted, - ); - - // https://issues.dlang.org/show_bug.cgi?id=15168 - static struct T1 { string s; alias s this; } - static struct T2 { char[10] s; alias s this; } - static struct T3 { string[string] s; alias s this; } - alias Pass2 = Test!( - ubyte, ubyte, T1, T1, - ubyte, ubyte, T2, T2, - ubyte, ubyte, T3, T3, - ); -} - -// https://issues.dlang.org/show_bug.cgi?id=17116 -@safe unittest -{ - alias ConstDg = void delegate(float) const; - alias B = void delegate(int) const; - alias A = ReplaceType!(float, int, ConstDg); - static assert(is(B == A)); -} - - // https://issues.dlang.org/show_bug.cgi?id=19696 -@safe unittest -{ - static struct T(U) {} - static struct S { T!int t; alias t this; } - static assert(is(ReplaceType!(float, float, S) == S)); -} - - // https://issues.dlang.org/show_bug.cgi?id=19697 -@safe unittest -{ - class D(T) {} - class C : D!C {} - static assert(is(ReplaceType!(float, float, C))); -} - -// https://issues.dlang.org/show_bug.cgi?id=16132 -@safe unittest -{ - interface I(T) {} - class C : I!int {} - static assert(is(ReplaceType!(int, string, C) == C)); -} - -// https://issues.dlang.org/show_bug.cgi?id=22325 -@safe unittest -{ - static struct Foo(alias f) {} - static void bar() {} - alias _ = ReplaceType!(int, int, Foo!bar); -} - -/** -Ternary type with three truth values: - -$(UL - $(LI `Ternary.yes` for `true`) - $(LI `Ternary.no` for `false`) - $(LI `Ternary.unknown` as an unknown state) -) - -Also known as trinary, trivalent, or trilean. - -See_Also: - $(HTTP en.wikipedia.org/wiki/Three-valued_logic, - Three Valued Logic on Wikipedia) -*/ -struct Ternary -{ - @safe @nogc nothrow pure: - - private ubyte value = 6; - private static Ternary make(ubyte b) - { - Ternary r = void; - r.value = b; - return r; - } - - /** - The possible states of the `Ternary` - */ - enum no = make(0); - /// ditto - enum yes = make(2); - /// ditto - enum unknown = make(6); - - /** - Construct and assign from a `bool`, receiving `no` for `false` and `yes` - for `true`. - */ - this(bool b) { value = b << 1; } - - /// ditto - void opAssign(bool b) { value = b << 1; } - - /** - Construct a ternary value from another ternary value - */ - this(const Ternary b) { value = b.value; } - - /** - $(TABLE Truth table for logical operations, - $(TR $(TH `a`) $(TH `b`) $(TH `$(TILDE)a`) $(TH `a | b`) $(TH `a & b`) $(TH `a ^ b`)) - $(TR $(TD `no`) $(TD `no`) $(TD `yes`) $(TD `no`) $(TD `no`) $(TD `no`)) - $(TR $(TD `no`) $(TD `yes`) $(TD) $(TD `yes`) $(TD `no`) $(TD `yes`)) - $(TR $(TD `no`) $(TD `unknown`) $(TD) $(TD `unknown`) $(TD `no`) $(TD `unknown`)) - $(TR $(TD `yes`) $(TD `no`) $(TD `no`) $(TD `yes`) $(TD `no`) $(TD `yes`)) - $(TR $(TD `yes`) $(TD `yes`) $(TD) $(TD `yes`) $(TD `yes`) $(TD `no`)) - $(TR $(TD `yes`) $(TD `unknown`) $(TD) $(TD `yes`) $(TD `unknown`) $(TD `unknown`)) - $(TR $(TD `unknown`) $(TD `no`) $(TD `unknown`) $(TD `unknown`) $(TD `no`) $(TD `unknown`)) - $(TR $(TD `unknown`) $(TD `yes`) $(TD) $(TD `yes`) $(TD `unknown`) $(TD `unknown`)) - $(TR $(TD `unknown`) $(TD `unknown`) $(TD) $(TD `unknown`) $(TD `unknown`) $(TD `unknown`)) - ) - */ - Ternary opUnary(string s)() if (s == "~") - { - return make((386 >> value) & 6); - } - - /// ditto - Ternary opBinary(string s)(Ternary rhs) if (s == "|") - { - return make((25_512 >> (value + rhs.value)) & 6); - } - - /// ditto - Ternary opBinary(string s)(Ternary rhs) if (s == "&") - { - return make((26_144 >> (value + rhs.value)) & 6); - } - - /// ditto - Ternary opBinary(string s)(Ternary rhs) if (s == "^") - { - return make((26_504 >> (value + rhs.value)) & 6); - } - - /// ditto - Ternary opBinary(string s)(bool rhs) - if (s == "|" || s == "&" || s == "^") - { - return this.opBinary!s(Ternary(rhs)); - } -} - -/// -@safe @nogc nothrow pure -unittest -{ - Ternary a; - assert(a == Ternary.unknown); - - assert(~Ternary.yes == Ternary.no); - assert(~Ternary.no == Ternary.yes); - assert(~Ternary.unknown == Ternary.unknown); -} - -@safe @nogc nothrow pure -unittest -{ - alias f = Ternary.no, t = Ternary.yes, u = Ternary.unknown; - Ternary[27] truthTableAnd = - [ - t, t, t, - t, u, u, - t, f, f, - u, t, u, - u, u, u, - u, f, f, - f, t, f, - f, u, f, - f, f, f, - ]; - - Ternary[27] truthTableOr = - [ - t, t, t, - t, u, t, - t, f, t, - u, t, t, - u, u, u, - u, f, u, - f, t, t, - f, u, u, - f, f, f, - ]; - - Ternary[27] truthTableXor = - [ - t, t, f, - t, u, u, - t, f, t, - u, t, u, - u, u, u, - u, f, u, - f, t, t, - f, u, u, - f, f, f, - ]; - - for (auto i = 0; i != truthTableAnd.length; i += 3) - { - assert((truthTableAnd[i] & truthTableAnd[i + 1]) - == truthTableAnd[i + 2]); - assert((truthTableOr[i] | truthTableOr[i + 1]) - == truthTableOr[i + 2]); - assert((truthTableXor[i] ^ truthTableXor[i + 1]) - == truthTableXor[i + 2]); - } - - Ternary a; - assert(a == Ternary.unknown); - static assert(!is(typeof({ if (a) {} }))); - assert(!is(typeof({ auto b = Ternary(3); }))); - a = true; - assert(a == Ternary.yes); - a = false; - assert(a == Ternary.no); - a = Ternary.unknown; - assert(a == Ternary.unknown); - Ternary b; - b = a; - assert(b == a); - assert(~Ternary.yes == Ternary.no); - assert(~Ternary.no == Ternary.yes); - assert(~Ternary.unknown == Ternary.unknown); -} - -@safe @nogc nothrow pure -unittest -{ - Ternary a = Ternary(true); - assert(a == Ternary.yes); - assert((a & false) == Ternary.no); - assert((a | false) == Ternary.yes); - assert((a ^ true) == Ternary.no); - assert((a ^ false) == Ternary.yes); -} - -// https://issues.dlang.org/show_bug.cgi?id=22511 -@safe unittest -{ - static struct S - { - int b; - @disable this(this); - this(ref return scope inout S rhs) inout - { - this.b = rhs.b + 1; - } - } - - Nullable!S s1 = S(1); - assert(s1.get().b == 2); - Nullable!S s2 = s1; - assert(s2.get().b == 3); -} - -@safe unittest -{ - static struct S - { - int b; - this(this) { ++b; } - } - - Nullable!S s1 = S(1); - assert(s1.get().b == 2); - Nullable!S s2 = s1; - assert(s2.get().b == 3); -} - -// https://issues.dlang.org/show_bug.cgi?id=24318 -@system unittest -{ - static struct S - { - @disable this(this); - int i; - } - - Nullable!S s = S(1); - assert(s.get().i == 1); - s = S(2); - assert(s.get().i == 2); -} - -/// The old version of $(LREF SafeRefCounted), before $(LREF borrow) existed. -/// Old code may be relying on `@safe`ty of some of the member functions which -/// cannot be safe in the new scheme, and -/// can avoid breakage by continuing to use this. `SafeRefCounted` should be -/// preferred, as this type is outdated and unrecommended for new code. -struct RefCounted(T, RefCountedAutoInitialize autoInit = - RefCountedAutoInitialize.yes) -{ - version (D_BetterC) - { - private enum enableGCScan = false; - } - else - { - private enum enableGCScan = hasIndirections!T; - } - - extern(C) private pure nothrow @nogc static - { - pragma(mangle, "free") void pureFree( void *ptr ); - static if (enableGCScan) - import core.memory : GC; - } - - struct RefCountedStore - { - private struct Impl - { - T _payload; - size_t _count; - } - - private Impl* _store; - - private void initialize(A...)(auto ref A args) - { - import core.lifetime : emplace, forward; - - allocateStore(); - version (D_Exceptions) scope(failure) deallocateStore(); - emplace(&_store._payload, forward!args); - _store._count = 1; - } - - private void move(ref T source) nothrow pure - { - import std.algorithm.mutation : moveEmplace; - - allocateStore(); - moveEmplace(source, _store._payload); - _store._count = 1; - } - - // 'nothrow': can only generate an Error - private void allocateStore() nothrow pure - { - static if (enableGCScan) - { - import std.internal.memory : enforceCalloc; - _store = cast(Impl*) enforceCalloc(1, Impl.sizeof); - GC.addRange(&_store._payload, T.sizeof); - } - else - { - import std.internal.memory : enforceMalloc; - _store = cast(Impl*) enforceMalloc(Impl.sizeof); - } - } - - private void deallocateStore() nothrow pure - { - static if (enableGCScan) - { - GC.removeRange(&this._store._payload); - } - pureFree(_store); - _store = null; - } - - @property nothrow @safe pure @nogc - bool isInitialized() const - { - return _store !is null; - } - - @property nothrow @safe pure @nogc - size_t refCount() const - { - return isInitialized ? _store._count : 0; - } - - void ensureInitialized()() - { - // By checking for `@disable this()` and failing early we can - // produce a clearer error message. - static assert(__traits(compiles, { static T t; }), - "Cannot automatically initialize `" ~ fullyQualifiedName!T ~ - "` because `" ~ fullyQualifiedName!T ~ - ".this()` is annotated with `@disable`."); - if (!isInitialized) initialize(); - } - - } - RefCountedStore _refCounted; - - @property nothrow @safe - ref inout(RefCountedStore) refCountedStore() inout - { - return _refCounted; - } - - this(A...)(auto ref A args) if (A.length > 0) - out - { - assert(refCountedStore.isInitialized); - } - do - { - import core.lifetime : forward; - _refCounted.initialize(forward!args); - } - - this(T val) - { - _refCounted.move(val); - } - - this(this) @safe pure nothrow @nogc - { - if (!_refCounted.isInitialized) return; - ++_refCounted._store._count; - } - - ~this() - { - if (!_refCounted.isInitialized) return; - assert(_refCounted._store._count > 0); - if (--_refCounted._store._count) - return; - // Done, destroy and deallocate - .destroy(_refCounted._store._payload); - _refCounted.deallocateStore(); - } - - void opAssign(typeof(this) rhs) - { - import std.algorithm.mutation : swap; - - swap(_refCounted._store, rhs._refCounted._store); - } - - void opAssign(T rhs) - { - import std.algorithm.mutation : move; - - static if (autoInit == RefCountedAutoInitialize.yes) - { - _refCounted.ensureInitialized(); - } - else - { - assert(_refCounted.isInitialized); - } - move(rhs, _refCounted._store._payload); - } - - static if (autoInit == RefCountedAutoInitialize.yes) - { - //Can't use inout here because of potential mutation - @property - ref T refCountedPayload() return - { - _refCounted.ensureInitialized(); - return _refCounted._store._payload; - } - } - - @property nothrow @safe pure @nogc - ref inout(T) refCountedPayload() inout return - { - assert(_refCounted.isInitialized, "Attempted to access an uninitialized payload."); - return _refCounted._store._payload; - } - - alias refCountedPayload this; - - static if (is(T == struct) && !is(typeof((ref T t) => t.toString()))) - { - string toString(this This)() - { - import std.conv : to; - - static if (autoInit) - return to!string(refCountedPayload); - else - { - if (!_refCounted.isInitialized) - return This.stringof ~ "(RefCountedStore(null))"; - else - return to!string(_refCounted._store._payload); - } - } - } -} - -/// -@betterC pure @system nothrow @nogc unittest -{ - auto rc1 = RefCounted!int(5); - assert(rc1 == 5); - auto rc2 = rc1; - rc2 = 42; - assert(rc1 == 42); -} - -// More unit tests below SafeRefCounted - -/** - * Like $(LREF safeRefCounted) but used to initialize $(LREF RefCounted) - * instead. Intended for backwards compatibility, otherwise it is preferable - * to use `safeRefCounted`. - */ -RefCounted!(T, RefCountedAutoInitialize.no) refCounted(T)(T val) -{ - typeof(return) res; - res._refCounted.move(val); - return res; -} - -/// -@system unittest -{ - static struct File - { - static size_t nDestroyed; - string name; - @disable this(this); // not copyable - ~this() { name = null; ++nDestroyed; } - } - - auto file = File("name"); - assert(file.name == "name"); - static assert(!__traits(compiles, {auto file2 = file;})); - assert(File.nDestroyed == 0); - - { - import std.algorithm.mutation : move; - auto rcFile = refCounted(move(file)); - assert(rcFile.name == "name"); - assert(File.nDestroyed == 1); - assert(file.name == null); - - auto rcFile2 = rcFile; - assert(rcFile.refCountedStore.refCount == 2); - assert(File.nDestroyed == 1); - } - - assert(File.nDestroyed == 2); -} - -// More unit tests below safeRefCounted diff --git a/phobos/std/typetuple.d b/phobos/std/typetuple.d deleted file mode 100644 index ecf2cc2..0000000 --- a/phobos/std/typetuple.d +++ /dev/null @@ -1,41 +0,0 @@ -/** - * This module was renamed to disambiguate the term tuple, use - * $(MREF std, meta) instead. - * - * Copyright: Copyright The D Language Foundation 2005 - 2015. - * License: $(HTTP www.boost.org/LICENSE_1_0.txt, Boost License 1.0). - * Authors: - * Source: $(PHOBOSSRC std/typetuple.d) - * - * $(SCRIPT inhibitQuickIndex = 1;) - */ -module std.typetuple; - -public import std.meta; - -/** - * Alternate name for $(REF AliasSeq, std,meta) for legacy compatibility. - */ -alias TypeTuple = AliasSeq; - -/// -@safe unittest -{ - import std.typetuple; - alias TL = TypeTuple!(int, double); - - int foo(TL td) // same as int foo(int, double); - { - return td[0] + cast(int) td[1]; - } - assert(foo(1, 2.5) == 3); -} - -/// -@safe unittest -{ - alias TL = TypeTuple!(int, double); - - alias Types = TypeTuple!(TL, char); - static assert(is(Types == TypeTuple!(int, double, char))); -} diff --git a/phobos/std/uni/package.d b/phobos/std/uni/package.d deleted file mode 100644 index 632704b..0000000 --- a/phobos/std/uni/package.d +++ /dev/null @@ -1,10825 +0,0 @@ -// Written in the D programming language. - -/++ - $(P The `std.uni` module provides an implementation - of fundamental Unicode algorithms and data structures. - This doesn't include UTF encoding and decoding primitives, - see $(REF decode, std,_utf) and $(REF encode, std,_utf) in $(MREF std, utf) - for this functionality. ) - -$(SCRIPT inhibitQuickIndex = 1;) -$(DIVC quickindex, -$(BOOKTABLE, -$(TR $(TH Category) $(TH Functions)) -$(TR $(TD Decode) $(TD - $(LREF byCodePoint) - $(LREF byGrapheme) - $(LREF decodeGrapheme) - $(LREF graphemeStride) -)) -$(TR $(TD Comparison) $(TD - $(LREF icmp) - $(LREF sicmp) -)) -$(TR $(TD Classification) $(TD - $(LREF isAlpha) - $(LREF isAlphaNum) - $(LREF isCodepointSet) - $(LREF isControl) - $(LREF isFormat) - $(LREF isGraphical) - $(LREF isIntegralPair) - $(LREF isMark) - $(LREF isNonCharacter) - $(LREF isNumber) - $(LREF isPrivateUse) - $(LREF isPunctuation) - $(LREF isSpace) - $(LREF isSurrogate) - $(LREF isSurrogateHi) - $(LREF isSurrogateLo) - $(LREF isSymbol) - $(LREF isWhite) -)) -$(TR $(TD Normalization) $(TD - $(LREF NFC) - $(LREF NFD) - $(LREF NFKD) - $(LREF NormalizationForm) - $(LREF normalize) -)) -$(TR $(TD Decompose) $(TD - $(LREF decompose) - $(LREF decomposeHangul) - $(LREF UnicodeDecomposition) -)) -$(TR $(TD Compose) $(TD - $(LREF compose) - $(LREF composeJamo) -)) -$(TR $(TD Sets) $(TD - $(LREF CodepointInterval) - $(LREF CodepointSet) - $(LREF InversionList) - $(LREF unicode) -)) -$(TR $(TD Trie) $(TD - $(LREF codepointSetTrie) - $(LREF CodepointSetTrie) - $(LREF codepointTrie) - $(LREF CodepointTrie) - $(LREF toTrie) - $(LREF toDelegate) -)) -$(TR $(TD Casing) $(TD - $(LREF asCapitalized) - $(LREF asLowerCase) - $(LREF asUpperCase) - $(LREF isLower) - $(LREF isUpper) - $(LREF toLower) - $(LREF toLowerInPlace) - $(LREF toUpper) - $(LREF toUpperInPlace) -)) -$(TR $(TD Utf8Matcher) $(TD - $(LREF isUtfMatcher) - $(LREF MatcherConcept) - $(LREF utfMatcher) -)) -$(TR $(TD Separators) $(TD - $(LREF lineSep) - $(LREF nelSep) - $(LREF paraSep) -)) -$(TR $(TD Building blocks) $(TD - $(LREF allowedIn) - $(LREF combiningClass) - $(LREF Grapheme) -)) -)) - - $(P All primitives listed operate on Unicode characters and - sets of characters. For functions which operate on ASCII characters - and ignore Unicode $(CHARACTERS), see $(MREF std, ascii). - For definitions of Unicode $(CHARACTER), $(CODEPOINT) and other terms - used throughout this module see the $(S_LINK Terminology, terminology) section - below. - ) - $(P The focus of this module is the core needs of developing Unicode-aware - applications. To that effect it provides the following optimized primitives: - ) - $(UL - $(LI Character classification by category and common properties: - $(LREF isAlpha), $(LREF isWhite) and others. - ) - $(LI - Case-insensitive string comparison ($(LREF sicmp), $(LREF icmp)). - ) - $(LI - Converting text to any of the four normalization forms via $(LREF normalize). - ) - $(LI - Decoding ($(LREF decodeGrapheme)) and iteration ($(LREF byGrapheme), $(LREF graphemeStride)) - by user-perceived characters, that is by $(LREF Grapheme) clusters. - ) - $(LI - Decomposing and composing of individual character(s) according to canonical - or compatibility rules, see $(LREF compose) and $(LREF decompose), - including the specific version for Hangul syllables $(LREF composeJamo) - and $(LREF decomposeHangul). - ) - ) - $(P It's recognized that an application may need further enhancements - and extensions, such as less commonly known algorithms, - or tailoring existing ones for region specific needs. To help users - with building any extra functionality beyond the core primitives, - the module provides: - ) - $(UL - $(LI - $(LREF CodepointSet), a type for easy manipulation of sets of characters. - Besides the typical set algebra it provides an unusual feature: - a D source code generator for detection of $(CODEPOINTS) in this set. - This is a boon for meta-programming parser frameworks, - and is used internally to power classification in small - sets like $(LREF isWhite). - ) - $(LI - A way to construct optimal packed multi-stage tables also known as a - special case of $(LINK2 https://en.wikipedia.org/wiki/Trie, Trie). - The functions $(LREF codepointTrie), $(LREF codepointSetTrie) - construct custom tries that map dchar to value. - The end result is a fast and predictable $(BIGOH 1) lookup that powers - functions like $(LREF isAlpha) and $(LREF combiningClass), - but for user-defined data sets. - ) - $(LI - A useful technique for Unicode-aware parsers that perform - character classification of encoded $(CODEPOINTS) - is to avoid unnecassary decoding at all costs. - $(LREF utfMatcher) provides an improvement over the usual workflow - of decode-classify-process, combining the decoding and classification - steps. By extracting necessary bits directly from encoded - $(S_LINK Code unit, code units) matchers achieve - significant performance improvements. See $(LREF MatcherConcept) for - the common interface of UTF matchers. - ) - $(LI - Generally useful building blocks for customized normalization: - $(LREF combiningClass) for querying combining class - and $(LREF allowedIn) for testing the Quick_Check - property of a given normalization form. - ) - $(LI - Access to a large selection of commonly used sets of $(CODEPOINTS). - $(S_LINK Unicode properties, Supported sets) include Script, - Block and General Category. The exact contents of a set can be - observed in the CLDR utility, on the - $(HTTP www.unicode.org/cldr/utility/properties.jsp, property index) page - of the Unicode website. - See $(LREF unicode) for easy and (optionally) compile-time checked set - queries. - ) - ) - $(SECTION Synopsis) - --- - import std.uni; - void main() - { - // initialize code point sets using script/block or property name - // now 'set' contains code points from both scripts. - auto set = unicode("Cyrillic") | unicode("Armenian"); - // same thing but simpler and checked at compile-time - auto ascii = unicode.ASCII; - auto currency = unicode.Currency_Symbol; - - // easy set ops - auto a = set & ascii; - assert(a.empty); // as it has no intersection with ascii - a = set | ascii; - auto b = currency - a; // subtract all ASCII, Cyrillic and Armenian - - // some properties of code point sets - assert(b.length > 45); // 46 items in Unicode 6.1, even more in 6.2 - // testing presence of a code point in a set - // is just fine, it is O(logN) - assert(!b['$']); - assert(!b['\u058F']); // Armenian dram sign - assert(b['ÂĽ']); - - // building fast lookup tables, these guarantee O(1) complexity - // 1-level Trie lookup table essentially a huge bit-set ~262Kb - auto oneTrie = toTrie!1(b); - // 2-level far more compact but typically slightly slower - auto twoTrie = toTrie!2(b); - // 3-level even smaller, and a bit slower yet - auto threeTrie = toTrie!3(b); - assert(oneTrie['ÂŁ']); - assert(twoTrie['ÂŁ']); - assert(threeTrie['ÂŁ']); - - // build the trie with the most sensible trie level - // and bind it as a functor - auto cyrillicOrArmenian = toDelegate(set); - auto balance = find!(cyrillicOrArmenian)("Hello ընկեր!"); - assert(balance == "ընկեր!"); - // compatible with bool delegate(dchar) - bool delegate(dchar) bindIt = cyrillicOrArmenian; - - // Normalization - string s = "Plain ascii (and not only), is always normalized!"; - assert(s is normalize(s));// is the same string - - string nonS = "A\u0308ffin"; // A ligature - auto nS = normalize(nonS); // to NFC, the W3C endorsed standard - assert(nS == "Äffin"); - assert(nS != nonS); - string composed = "Äffin"; - - assert(normalize!NFD(composed) == "A\u0308ffin"); - // to NFKD, compatibility decomposition useful for fuzzy matching/searching - assert(normalize!NFKD("2š⁰") == "210"); - } - --- - $(SECTION Terminology) - $(P The following is a list of important Unicode notions - and definitions. Any conventions used specifically in this - module alone are marked as such. The descriptions are based on the formal - definition as found in $(HTTP www.unicode.org/versions/Unicode6.2.0/ch03.pdf, - chapter three of The Unicode Standard Core Specification.) - ) - $(P $(DEF Abstract character) A unit of information used for the organization, - control, or representation of textual data. - Note that: - $(UL - $(LI When representing data, the nature of that data - is generally symbolic as opposed to some other - kind of data (for example, visual). - ) - $(LI An abstract character has no concrete form - and should not be confused with a $(S_LINK Glyph, glyph). - ) - $(LI An abstract character does not necessarily - correspond to what a user thinks of as a “character” - and should not be confused with a $(LREF Grapheme). - ) - $(LI The abstract characters encoded (see Encoded character) - are known as Unicode abstract characters. - ) - $(LI Abstract characters not directly - encoded by the Unicode Standard can often be - represented by the use of combining character sequences. - ) - ) - ) - $(P $(DEF Canonical decomposition) - The decomposition of a character or character sequence - that results from recursively applying the canonical - mappings found in the Unicode Character Database - and these described in Conjoining Jamo Behavior - (section 12 of - $(HTTP www.unicode.org/uni2book/ch03.pdf, Unicode Conformance)). - ) - $(P $(DEF Canonical composition) - The precise definition of the Canonical composition - is the algorithm as specified in $(HTTP www.unicode.org/uni2book/ch03.pdf, - Unicode Conformance) section 11. - Informally it's the process that does the reverse of the canonical - decomposition with the addition of certain rules - that e.g. prevent legacy characters from appearing in the composed result. - ) - $(P $(DEF Canonical equivalent) - Two character sequences are said to be canonical equivalents if - their full canonical decompositions are identical. - ) - $(P $(DEF Character) Typically differs by context. - For the purpose of this documentation the term $(I character) - implies $(I encoded character), that is, a code point having - an assigned abstract character (a symbolic meaning). - ) - $(P $(DEF Code point) Any value in the Unicode codespace; - that is, the range of integers from 0 to 10FFFF (hex). - Not all code points are assigned to encoded characters. - ) - $(P $(DEF Code unit) The minimal bit combination that can represent - a unit of encoded text for processing or interchange. - Depending on the encoding this could be: - 8-bit code units in the UTF-8 (`char`), - 16-bit code units in the UTF-16 (`wchar`), - and 32-bit code units in the UTF-32 (`dchar`). - $(I Note that in UTF-32, a code unit is a code point - and is represented by the D `dchar` type.) - ) - $(P $(DEF Combining character) A character with the General Category - of Combining Mark(M). - $(UL - $(LI All characters with non-zero canonical combining class - are combining characters, but the reverse is not the case: - there are combining characters with a zero combining class. - ) - $(LI These characters are not normally used in isolation - unless they are being described. They include such characters - as accents, diacritics, Hebrew points, Arabic vowel signs, - and Indic matras. - ) - ) - ) - $(P $(DEF Combining class) - A numerical value used by the Unicode Canonical Ordering Algorithm - to determine which sequences of combining marks are to be - considered canonically equivalent and which are not. - ) - $(P $(DEF Compatibility decomposition) - The decomposition of a character or character sequence that results - from recursively applying both the compatibility mappings and - the canonical mappings found in the Unicode Character Database, and those - described in Conjoining Jamo Behavior no characters - can be further decomposed. - ) - $(P $(DEF Compatibility equivalent) - Two character sequences are said to be compatibility - equivalents if their full compatibility decompositions are identical. - ) - $(P $(DEF Encoded character) An association (or mapping) - between an abstract character and a code point. - ) - $(P $(DEF Glyph) The actual, concrete image of a glyph representation - having been rasterized or otherwise imaged onto some display surface. - ) - $(P $(DEF Grapheme base) A character with the property - Grapheme_Base, or any standard Korean syllable block. - ) - $(P $(DEF Grapheme cluster) Defined as the text between - grapheme boundaries as specified by Unicode Standard Annex #29, - $(HTTP www.unicode.org/reports/tr29/, Unicode text segmentation). - Important general properties of a grapheme: - $(UL - $(LI The grapheme cluster represents a horizontally segmentable - unit of text, consisting of some grapheme base (which may - consist of a Korean syllable) together with any number of - nonspacing marks applied to it. - ) - $(LI A grapheme cluster typically starts with a grapheme base - and then extends across any subsequent sequence of nonspacing marks. - A grapheme cluster is most directly relevant to text rendering and - processes such as cursor placement and text selection in editing, - but may also be relevant to comparison and searching. - ) - $(LI For many processes, a grapheme cluster behaves as if it was a - single character with the same properties as its grapheme base. - Effectively, nonspacing marks apply $(I graphically) to the base, - but do not change its properties. - ) - ) - $(P This module defines a number of primitives that work with graphemes: - $(LREF Grapheme), $(LREF decodeGrapheme) and $(LREF graphemeStride). - All of them are using $(I extended grapheme) boundaries - as defined in the aforementioned standard annex. - ) - ) - $(P $(DEF Nonspacing mark) A combining character with the - General Category of Nonspacing Mark (Mn) or Enclosing Mark (Me). - ) - $(P $(DEF Spacing mark) A combining character that is not a nonspacing mark. - ) - $(SECTION Normalization) - $(P The concepts of $(S_LINK Canonical equivalent, canonical equivalent) - or $(S_LINK Compatibility equivalent, compatibility equivalent) - characters in the Unicode Standard make it necessary to have a full, formal - definition of equivalence for Unicode strings. - String equivalence is determined by a process called normalization, - whereby strings are converted into forms which are compared - directly for identity. This is the primary goal of the normalization process, - see the function $(LREF normalize) to convert into any of - the four defined forms. - ) - $(P A very important attribute of the Unicode Normalization Forms - is that they must remain stable between versions of the Unicode Standard. - A Unicode string normalized to a particular Unicode Normalization Form - in one version of the standard is guaranteed to remain in that Normalization - Form for implementations of future versions of the standard. - ) - $(P The Unicode Standard specifies four normalization forms. - Informally, two of these forms are defined by maximal decomposition - of equivalent sequences, and two of these forms are defined - by maximal $(I composition) of equivalent sequences. - $(UL - $(LI Normalization Form D (NFD): The $(S_LINK Canonical decomposition, - canonical decomposition) of a character sequence.) - $(LI Normalization Form KD (NFKD): The $(S_LINK Compatibility decomposition, - compatibility decomposition) of a character sequence.) - $(LI Normalization Form C (NFC): The canonical composition of the - $(S_LINK Canonical decomposition, canonical decomposition) - of a coded character sequence.) - $(LI Normalization Form KC (NFKC): The canonical composition - of the $(S_LINK Compatibility decomposition, - compatibility decomposition) of a character sequence) - ) - ) - $(P The choice of the normalization form depends on the particular use case. - NFC is the best form for general text, since it's more compatible with - strings converted from legacy encodings. NFKC is the preferred form for - identifiers, especially where there are security concerns. NFD and NFKD - are the most useful for internal processing. - ) - $(SECTION Construction of lookup tables) - $(P The Unicode standard describes a set of algorithms that - depend on having the ability to quickly look up various properties - of a code point. Given the codespace of about 1 million $(CODEPOINTS), - it is not a trivial task to provide a space-efficient solution for - the multitude of properties. - ) - $(P Common approaches such as hash-tables or binary search over - sorted code point intervals (as in $(LREF InversionList)) are insufficient. - Hash-tables have enormous memory footprint and binary search - over intervals is not fast enough for some heavy-duty algorithms. - ) - $(P The recommended solution (see Unicode Implementation Guidelines) - is using multi-stage tables that are an implementation of the - $(HTTP en.wikipedia.org/wiki/Trie, Trie) data structure with integer - keys and a fixed number of stages. For the remainder of the section - this will be called a fixed trie. The following describes a particular - implementation that is aimed for the speed of access at the expense - of ideal size savings. - ) - $(P Taking a 2-level Trie as an example the principle of operation is as follows. - Split the number of bits in a key (code point, 21 bits) into 2 components - (e.g. 15 and 8). The first is the number of bits in the index of the trie - and the other is number of bits in each page of the trie. - The layout of the trie is then an array of size 2^^bits-of-index followed - an array of memory chunks of size 2^^bits-of-page/bits-per-element. - ) - $(P The number of pages is variable (but not less then 1) - unlike the number of entries in the index. The slots of the index - all have to contain a number of a page that is present. The lookup is then - just a couple of operations - slice the upper bits, - lookup an index for these, take a page at this index and use - the lower bits as an offset within this page. - - Assuming that pages are laid out consequently - in one array at `pages`, the pseudo-code is: - ) - --- - auto elemsPerPage = (2 ^^ bits_per_page) / Value.sizeOfInBits; - pages[index[n >> bits_per_page]][n & (elemsPerPage - 1)]; - --- - $(P Where if `elemsPerPage` is a power of 2 the whole process is - a handful of simple instructions and 2 array reads. Subsequent levels - of the trie are introduced by recursing on this notion - the index array - is treated as values. The number of bits in index is then again - split into 2 parts, with pages over 'current-index' and the new 'upper-index'. - ) - - $(P For completeness a level 1 trie is simply an array. - The current implementation takes advantage of bit-packing values - when the range is known to be limited in advance (such as `bool`). - See also $(LREF BitPacked) for enforcing it manually. - The major size advantage however comes from the fact - that multiple $(B identical pages on every level are merged) by construction. - ) - $(P The process of constructing a trie is more involved and is hidden from - the user in a form of the convenience functions $(LREF codepointTrie), - $(LREF codepointSetTrie) and the even more convenient $(LREF toTrie). - In general a set or built-in AA with `dchar` type - can be turned into a trie. The trie object in this module - is read-only (immutable); it's effectively frozen after construction. - ) - $(SECTION Unicode properties) - $(P This is a full list of Unicode properties accessible through $(LREF unicode) - with specific helpers per category nested within. Consult the - $(HTTP www.unicode.org/cldr/utility/properties.jsp, CLDR utility) - when in doubt about the contents of a particular set. - ) - $(P General category sets listed below are only accessible with the - $(LREF unicode) shorthand accessor.) - $(BOOKTABLE $(B General category ), - $(TR $(TH Abb.) $(TH Long form) - $(TH Abb.) $(TH Long form)$(TH Abb.) $(TH Long form)) - $(TR $(TD L) $(TD Letter) - $(TD Cn) $(TD Unassigned) $(TD Po) $(TD Other_Punctuation)) - $(TR $(TD Ll) $(TD Lowercase_Letter) - $(TD Co) $(TD Private_Use) $(TD Ps) $(TD Open_Punctuation)) - $(TR $(TD Lm) $(TD Modifier_Letter) - $(TD Cs) $(TD Surrogate) $(TD S) $(TD Symbol)) - $(TR $(TD Lo) $(TD Other_Letter) - $(TD N) $(TD Number) $(TD Sc) $(TD Currency_Symbol)) - $(TR $(TD Lt) $(TD Titlecase_Letter) - $(TD Nd) $(TD Decimal_Number) $(TD Sk) $(TD Modifier_Symbol)) - $(TR $(TD Lu) $(TD Uppercase_Letter) - $(TD Nl) $(TD Letter_Number) $(TD Sm) $(TD Math_Symbol)) - $(TR $(TD M) $(TD Mark) - $(TD No) $(TD Other_Number) $(TD So) $(TD Other_Symbol)) - $(TR $(TD Mc) $(TD Spacing_Mark) - $(TD P) $(TD Punctuation) $(TD Z) $(TD Separator)) - $(TR $(TD Me) $(TD Enclosing_Mark) - $(TD Pc) $(TD Connector_Punctuation) $(TD Zl) $(TD Line_Separator)) - $(TR $(TD Mn) $(TD Nonspacing_Mark) - $(TD Pd) $(TD Dash_Punctuation) $(TD Zp) $(TD Paragraph_Separator)) - $(TR $(TD C) $(TD Other) - $(TD Pe) $(TD Close_Punctuation) $(TD Zs) $(TD Space_Separator)) - $(TR $(TD Cc) $(TD Control) $(TD Pf) - $(TD Final_Punctuation) $(TD -) $(TD Any)) - $(TR $(TD Cf) $(TD Format) - $(TD Pi) $(TD Initial_Punctuation) $(TD -) $(TD ASCII)) - ) - $(P Sets for other commonly useful properties that are - accessible with $(LREF unicode):) - $(BOOKTABLE $(B Common binary properties), - $(TR $(TH Name) $(TH Name) $(TH Name)) - $(TR $(TD Alphabetic) $(TD Ideographic) $(TD Other_Uppercase)) - $(TR $(TD ASCII_Hex_Digit) $(TD IDS_Binary_Operator) $(TD Pattern_Syntax)) - $(TR $(TD Bidi_Control) $(TD ID_Start) $(TD Pattern_White_Space)) - $(TR $(TD Cased) $(TD IDS_Trinary_Operator) $(TD Quotation_Mark)) - $(TR $(TD Case_Ignorable) $(TD Join_Control) $(TD Radical)) - $(TR $(TD Dash) $(TD Logical_Order_Exception) $(TD Soft_Dotted)) - $(TR $(TD Default_Ignorable_Code_Point) $(TD Lowercase) $(TD STerm)) - $(TR $(TD Deprecated) $(TD Math) $(TD Terminal_Punctuation)) - $(TR $(TD Diacritic) $(TD Noncharacter_Code_Point) $(TD Unified_Ideograph)) - $(TR $(TD Extender) $(TD Other_Alphabetic) $(TD Uppercase)) - $(TR $(TD Grapheme_Base) $(TD Other_Default_Ignorable_Code_Point) $(TD Variation_Selector)) - $(TR $(TD Grapheme_Extend) $(TD Other_Grapheme_Extend) $(TD White_Space)) - $(TR $(TD Grapheme_Link) $(TD Other_ID_Continue) $(TD XID_Continue)) - $(TR $(TD Hex_Digit) $(TD Other_ID_Start) $(TD XID_Start)) - $(TR $(TD Hyphen) $(TD Other_Lowercase) ) - $(TR $(TD ID_Continue) $(TD Other_Math) ) - ) - $(P Below is the table with block names accepted by $(LREF unicode.block). - Note that the shorthand version $(LREF unicode) requires "In" - to be prepended to the names of blocks so as to disambiguate - scripts and blocks. - ) - $(BOOKTABLE $(B Blocks), - $(TR $(TD Aegean Numbers) $(TD Ethiopic Extended) $(TD Mongolian)) - $(TR $(TD Alchemical Symbols) $(TD Ethiopic Extended-A) $(TD Musical Symbols)) - $(TR $(TD Alphabetic Presentation Forms) $(TD Ethiopic Supplement) $(TD Myanmar)) - $(TR $(TD Ancient Greek Musical Notation) $(TD General Punctuation) $(TD Myanmar Extended-A)) - $(TR $(TD Ancient Greek Numbers) $(TD Geometric Shapes) $(TD New Tai Lue)) - $(TR $(TD Ancient Symbols) $(TD Georgian) $(TD NKo)) - $(TR $(TD Arabic) $(TD Georgian Supplement) $(TD Number Forms)) - $(TR $(TD Arabic Extended-A) $(TD Glagolitic) $(TD Ogham)) - $(TR $(TD Arabic Mathematical Alphabetic Symbols) $(TD Gothic) $(TD Ol Chiki)) - $(TR $(TD Arabic Presentation Forms-A) $(TD Greek and Coptic) $(TD Old Italic)) - $(TR $(TD Arabic Presentation Forms-B) $(TD Greek Extended) $(TD Old Persian)) - $(TR $(TD Arabic Supplement) $(TD Gujarati) $(TD Old South Arabian)) - $(TR $(TD Armenian) $(TD Gurmukhi) $(TD Old Turkic)) - $(TR $(TD Arrows) $(TD Halfwidth and Fullwidth Forms) $(TD Optical Character Recognition)) - $(TR $(TD Avestan) $(TD Hangul Compatibility Jamo) $(TD Oriya)) - $(TR $(TD Balinese) $(TD Hangul Jamo) $(TD Osmanya)) - $(TR $(TD Bamum) $(TD Hangul Jamo Extended-A) $(TD Phags-pa)) - $(TR $(TD Bamum Supplement) $(TD Hangul Jamo Extended-B) $(TD Phaistos Disc)) - $(TR $(TD Basic Latin) $(TD Hangul Syllables) $(TD Phoenician)) - $(TR $(TD Batak) $(TD Hanunoo) $(TD Phonetic Extensions)) - $(TR $(TD Bengali) $(TD Hebrew) $(TD Phonetic Extensions Supplement)) - $(TR $(TD Block Elements) $(TD High Private Use Surrogates) $(TD Playing Cards)) - $(TR $(TD Bopomofo) $(TD High Surrogates) $(TD Private Use Area)) - $(TR $(TD Bopomofo Extended) $(TD Hiragana) $(TD Rejang)) - $(TR $(TD Box Drawing) $(TD Ideographic Description Characters) $(TD Rumi Numeral Symbols)) - $(TR $(TD Brahmi) $(TD Imperial Aramaic) $(TD Runic)) - $(TR $(TD Braille Patterns) $(TD Inscriptional Pahlavi) $(TD Samaritan)) - $(TR $(TD Buginese) $(TD Inscriptional Parthian) $(TD Saurashtra)) - $(TR $(TD Buhid) $(TD IPA Extensions) $(TD Sharada)) - $(TR $(TD Byzantine Musical Symbols) $(TD Javanese) $(TD Shavian)) - $(TR $(TD Carian) $(TD Kaithi) $(TD Sinhala)) - $(TR $(TD Chakma) $(TD Kana Supplement) $(TD Small Form Variants)) - $(TR $(TD Cham) $(TD Kanbun) $(TD Sora Sompeng)) - $(TR $(TD Cherokee) $(TD Kangxi Radicals) $(TD Spacing Modifier Letters)) - $(TR $(TD CJK Compatibility) $(TD Kannada) $(TD Specials)) - $(TR $(TD CJK Compatibility Forms) $(TD Katakana) $(TD Sundanese)) - $(TR $(TD CJK Compatibility Ideographs) $(TD Katakana Phonetic Extensions) $(TD Sundanese Supplement)) - $(TR $(TD CJK Compatibility Ideographs Supplement) $(TD Kayah Li) $(TD Superscripts and Subscripts)) - $(TR $(TD CJK Radicals Supplement) $(TD Kharoshthi) $(TD Supplemental Arrows-A)) - $(TR $(TD CJK Strokes) $(TD Khmer) $(TD Supplemental Arrows-B)) - $(TR $(TD CJK Symbols and Punctuation) $(TD Khmer Symbols) $(TD Supplemental Mathematical Operators)) - $(TR $(TD CJK Unified Ideographs) $(TD Lao) $(TD Supplemental Punctuation)) - $(TR $(TD CJK Unified Ideographs Extension A) $(TD Latin-1 Supplement) $(TD Supplementary Private Use Area-A)) - $(TR $(TD CJK Unified Ideographs Extension B) $(TD Latin Extended-A) $(TD Supplementary Private Use Area-B)) - $(TR $(TD CJK Unified Ideographs Extension C) $(TD Latin Extended Additional) $(TD Syloti Nagri)) - $(TR $(TD CJK Unified Ideographs Extension D) $(TD Latin Extended-B) $(TD Syriac)) - $(TR $(TD Combining Diacritical Marks) $(TD Latin Extended-C) $(TD Tagalog)) - $(TR $(TD Combining Diacritical Marks for Symbols) $(TD Latin Extended-D) $(TD Tagbanwa)) - $(TR $(TD Combining Diacritical Marks Supplement) $(TD Lepcha) $(TD Tags)) - $(TR $(TD Combining Half Marks) $(TD Letterlike Symbols) $(TD Tai Le)) - $(TR $(TD Common Indic Number Forms) $(TD Limbu) $(TD Tai Tham)) - $(TR $(TD Control Pictures) $(TD Linear B Ideograms) $(TD Tai Viet)) - $(TR $(TD Coptic) $(TD Linear B Syllabary) $(TD Tai Xuan Jing Symbols)) - $(TR $(TD Counting Rod Numerals) $(TD Lisu) $(TD Takri)) - $(TR $(TD Cuneiform) $(TD Low Surrogates) $(TD Tamil)) - $(TR $(TD Cuneiform Numbers and Punctuation) $(TD Lycian) $(TD Telugu)) - $(TR $(TD Currency Symbols) $(TD Lydian) $(TD Thaana)) - $(TR $(TD Cypriot Syllabary) $(TD Mahjong Tiles) $(TD Thai)) - $(TR $(TD Cyrillic) $(TD Malayalam) $(TD Tibetan)) - $(TR $(TD Cyrillic Extended-A) $(TD Mandaic) $(TD Tifinagh)) - $(TR $(TD Cyrillic Extended-B) $(TD Mathematical Alphanumeric Symbols) $(TD Transport And Map Symbols)) - $(TR $(TD Cyrillic Supplement) $(TD Mathematical Operators) $(TD Ugaritic)) - $(TR $(TD Deseret) $(TD Meetei Mayek) $(TD Unified Canadian Aboriginal Syllabics)) - $(TR $(TD Devanagari) $(TD Meetei Mayek Extensions) $(TD Unified Canadian Aboriginal Syllabics Extended)) - $(TR $(TD Devanagari Extended) $(TD Meroitic Cursive) $(TD Vai)) - $(TR $(TD Dingbats) $(TD Meroitic Hieroglyphs) $(TD Variation Selectors)) - $(TR $(TD Domino Tiles) $(TD Miao) $(TD Variation Selectors Supplement)) - $(TR $(TD Egyptian Hieroglyphs) $(TD Miscellaneous Mathematical Symbols-A) $(TD Vedic Extensions)) - $(TR $(TD Emoticons) $(TD Miscellaneous Mathematical Symbols-B) $(TD Vertical Forms)) - $(TR $(TD Enclosed Alphanumerics) $(TD Miscellaneous Symbols) $(TD Yijing Hexagram Symbols)) - $(TR $(TD Enclosed Alphanumeric Supplement) $(TD Miscellaneous Symbols and Arrows) $(TD Yi Radicals)) - $(TR $(TD Enclosed CJK Letters and Months) $(TD Miscellaneous Symbols And Pictographs) $(TD Yi Syllables)) - $(TR $(TD Enclosed Ideographic Supplement) $(TD Miscellaneous Technical) ) - $(TR $(TD Ethiopic) $(TD Modifier Tone Letters) ) - ) - $(P Below is the table with script names accepted by $(LREF unicode.script) - and by the shorthand version $(LREF unicode):) - $(BOOKTABLE $(B Scripts), - $(TR $(TD Arabic) $(TD Hanunoo) $(TD Old_Italic)) - $(TR $(TD Armenian) $(TD Hebrew) $(TD Old_Persian)) - $(TR $(TD Avestan) $(TD Hiragana) $(TD Old_South_Arabian)) - $(TR $(TD Balinese) $(TD Imperial_Aramaic) $(TD Old_Turkic)) - $(TR $(TD Bamum) $(TD Inherited) $(TD Oriya)) - $(TR $(TD Batak) $(TD Inscriptional_Pahlavi) $(TD Osmanya)) - $(TR $(TD Bengali) $(TD Inscriptional_Parthian) $(TD Phags_Pa)) - $(TR $(TD Bopomofo) $(TD Javanese) $(TD Phoenician)) - $(TR $(TD Brahmi) $(TD Kaithi) $(TD Rejang)) - $(TR $(TD Braille) $(TD Kannada) $(TD Runic)) - $(TR $(TD Buginese) $(TD Katakana) $(TD Samaritan)) - $(TR $(TD Buhid) $(TD Kayah_Li) $(TD Saurashtra)) - $(TR $(TD Canadian_Aboriginal) $(TD Kharoshthi) $(TD Sharada)) - $(TR $(TD Carian) $(TD Khmer) $(TD Shavian)) - $(TR $(TD Chakma) $(TD Lao) $(TD Sinhala)) - $(TR $(TD Cham) $(TD Latin) $(TD Sora_Sompeng)) - $(TR $(TD Cherokee) $(TD Lepcha) $(TD Sundanese)) - $(TR $(TD Common) $(TD Limbu) $(TD Syloti_Nagri)) - $(TR $(TD Coptic) $(TD Linear_B) $(TD Syriac)) - $(TR $(TD Cuneiform) $(TD Lisu) $(TD Tagalog)) - $(TR $(TD Cypriot) $(TD Lycian) $(TD Tagbanwa)) - $(TR $(TD Cyrillic) $(TD Lydian) $(TD Tai_Le)) - $(TR $(TD Deseret) $(TD Malayalam) $(TD Tai_Tham)) - $(TR $(TD Devanagari) $(TD Mandaic) $(TD Tai_Viet)) - $(TR $(TD Egyptian_Hieroglyphs) $(TD Meetei_Mayek) $(TD Takri)) - $(TR $(TD Ethiopic) $(TD Meroitic_Cursive) $(TD Tamil)) - $(TR $(TD Georgian) $(TD Meroitic_Hieroglyphs) $(TD Telugu)) - $(TR $(TD Glagolitic) $(TD Miao) $(TD Thaana)) - $(TR $(TD Gothic) $(TD Mongolian) $(TD Thai)) - $(TR $(TD Greek) $(TD Myanmar) $(TD Tibetan)) - $(TR $(TD Gujarati) $(TD New_Tai_Lue) $(TD Tifinagh)) - $(TR $(TD Gurmukhi) $(TD Nko) $(TD Ugaritic)) - $(TR $(TD Han) $(TD Ogham) $(TD Vai)) - $(TR $(TD Hangul) $(TD Ol_Chiki) $(TD Yi)) - ) - $(P Below is the table of names accepted by $(LREF unicode.hangulSyllableType).) - $(BOOKTABLE $(B Hangul syllable type), - $(TR $(TH Abb.) $(TH Long form)) - $(TR $(TD L) $(TD Leading_Jamo)) - $(TR $(TD LV) $(TD LV_Syllable)) - $(TR $(TD LVT) $(TD LVT_Syllable) ) - $(TR $(TD T) $(TD Trailing_Jamo)) - $(TR $(TD V) $(TD Vowel_Jamo)) - ) - References: - $(HTTP www.digitalmars.com/d/ascii-table.html, ASCII Table), - $(HTTP en.wikipedia.org/wiki/Unicode, Wikipedia), - $(HTTP www.unicode.org, The Unicode Consortium), - $(HTTP www.unicode.org/reports/tr15/, Unicode normalization forms), - $(HTTP www.unicode.org/reports/tr29/, Unicode text segmentation) - $(HTTP www.unicode.org/uni2book/ch05.pdf, - Unicode Implementation Guidelines) - $(HTTP www.unicode.org/uni2book/ch03.pdf, - Unicode Conformance) - Trademarks: - Unicode(tm) is a trademark of Unicode, Inc. - - Copyright: Copyright 2013 - - License: $(HTTP www.boost.org/LICENSE_1_0.txt, Boost License 1.0). - Authors: Dmitry Olshansky - Source: $(PHOBOSSRC std/uni/package.d) - Standards: $(HTTP www.unicode.org/versions/Unicode6.2.0/, Unicode v6.2) - -Macros: - -SECTION = <h3><a id="$1">$0</a></h3> -DEF = <div><a id="$1"><i>$0</i></a></div> -S_LINK = <a href="#$1">$+</a> -CODEPOINT = $(S_LINK Code point, code point) -CODEPOINTS = $(S_LINK Code point, code points) -CHARACTER = $(S_LINK Character, character) -CHARACTERS = $(S_LINK Character, characters) -CLUSTER = $(S_LINK Grapheme cluster, grapheme cluster) -+/ -module std.uni; - -import std.meta : AliasSeq; -import std.range.primitives : back, ElementEncodingType, ElementType, empty, - front, hasLength, hasSlicing, isForwardRange, isInputRange, - isRandomAccessRange, popFront, put, save; -import std.traits : isConvertibleToString, isIntegral, isSomeChar, - isSomeString, Unqual, isDynamicArray; -// debug = std_uni; - -import std.internal.unicode_tables; // generated file - -debug(std_uni) import std.stdio; // writefln, writeln - -private: - - -void copyBackwards(T,U)(T[] src, U[] dest) -{ - assert(src.length == dest.length); - for (size_t i=src.length; i-- > 0; ) - dest[i] = src[i]; -} - -void copyForward(T,U)(T[] src, U[] dest) -{ - assert(src.length == dest.length); - for (size_t i=0; i<src.length; i++) - dest[i] = src[i]; -} - -// TODO: update to reflect all major CPUs supporting unaligned reads -version (X86) - enum hasUnalignedReads = true; -else version (X86_64) - enum hasUnalignedReads = true; -else version (SystemZ) - enum hasUnalignedReads = true; -else - enum hasUnalignedReads = false; // better be safe then sorry - -public enum dchar lineSep = '\u2028'; /// Constant $(CODEPOINT) (0x2028) - line separator. -public enum dchar paraSep = '\u2029'; /// Constant $(CODEPOINT) (0x2029) - paragraph separator. -public enum dchar nelSep = '\u0085'; /// Constant $(CODEPOINT) (0x0085) - next line. - -// test the intro example -@safe unittest -{ - import std.algorithm.searching : find; - // initialize code point sets using script/block or property name - // set contains code points from both scripts. - auto set = unicode("Cyrillic") | unicode("Armenian"); - // or simpler and statically-checked look - auto ascii = unicode.ASCII; - auto currency = unicode.Currency_Symbol; - - // easy set ops - auto a = set & ascii; - assert(a.empty); // as it has no intersection with ascii - a = set | ascii; - auto b = currency - a; // subtract all ASCII, Cyrillic and Armenian - - // some properties of code point sets - assert(b.length > 45); // 46 items in Unicode 6.1, even more in 6.2 - // testing presence of a code point in a set - // is just fine, it is O(logN) - assert(!b['$']); - assert(!b['\u058F']); // Armenian dram sign - assert(b['ÂĽ']); - - // building fast lookup tables, these guarantee O(1) complexity - // 1-level Trie lookup table essentially a huge bit-set ~262Kb - auto oneTrie = toTrie!1(b); - // 2-level far more compact but typically slightly slower - auto twoTrie = toTrie!2(b); - // 3-level even smaller, and a bit slower yet - auto threeTrie = toTrie!3(b); - assert(oneTrie['ÂŁ']); - assert(twoTrie['ÂŁ']); - assert(threeTrie['ÂŁ']); - - // build the trie with the most sensible trie level - // and bind it as a functor - auto cyrillicOrArmenian = toDelegate(set); - auto balance = find!(cyrillicOrArmenian)("Hello ընկեր!"); - assert(balance == "ընկեր!"); - // compatible with bool delegate(dchar) - bool delegate(dchar) bindIt = cyrillicOrArmenian; - - // Normalization - string s = "Plain ascii (and not only), is always normalized!"; - assert(s is normalize(s));// is the same string - - string nonS = "A\u0308ffin"; // A ligature - auto nS = normalize(nonS); // to NFC, the W3C endorsed standard - assert(nS == "Äffin"); - assert(nS != nonS); - string composed = "Äffin"; - - assert(normalize!NFD(composed) == "A\u0308ffin"); - // to NFKD, compatibility decomposition useful for fuzzy matching/searching - assert(normalize!NFKD("2š⁰") == "210"); -} - -enum lastDchar = 0x10FFFF; - -auto force(T, F)(F from) -if (isIntegral!T && !is(T == F)) -{ - assert(from <= T.max && from >= T.min); - return cast(T) from; -} - -auto force(T, F)(F from) -if (isBitPacked!T && !is(T == F)) -{ - assert(from <= 2^^bitSizeOf!T-1); - return T(cast(TypeOfBitPacked!T) from); -} - -auto force(T, F)(F from) -if (is(T == F)) -{ - return from; -} - -// repeat X times the bit-pattern in val assuming it's length is 'bits' -size_t replicateBits(size_t times, size_t bits)(size_t val) @safe pure nothrow @nogc -{ - static if (times == 1) - return val; - else static if (bits == 1) - { - static if (times == size_t.sizeof*8) - return val ? size_t.max : 0; - else - return val ? (1 << times)-1 : 0; - } - else static if (times % 2) - return (replicateBits!(times-1, bits)(val)<<bits) | val; - else - return replicateBits!(times/2, bits*2)((val << bits) | val); -} - -@safe pure nothrow @nogc unittest // for replicate -{ - import std.algorithm.iteration : sum, map; - import std.range : iota; - size_t m = 0b111; - size_t m2 = 0b01; - static foreach (i; AliasSeq!(1, 2, 3, 4, 5, 6, 7, 8, 9, 10)) - { - assert(replicateBits!(i, 3)(m)+1 == (1<<(3*i))); - assert(replicateBits!(i, 2)(m2) == iota(0, i).map!"2^^(2*a)"().sum()); - } -} - -// multiple arrays squashed into one memory block -struct MultiArray(Types...) -{ - import std.range.primitives : isOutputRange; - this(size_t[] sizes...) @safe pure nothrow - { - assert(dim == sizes.length); - size_t full_size; - foreach (i, v; Types) - { - full_size += spaceFor!(bitSizeOf!v)(sizes[i]); - sz[i] = sizes[i]; - static if (i >= 1) - offsets[i] = offsets[i-1] + - spaceFor!(bitSizeOf!(Types[i-1]))(sizes[i-1]); - } - - storage = new size_t[full_size]; - } - - this(const(size_t)[] raw_offsets, - const(size_t)[] raw_sizes, - return scope const(size_t)[] data) return scope const @safe pure nothrow @nogc - { - offsets[] = raw_offsets[]; - sz[] = raw_sizes[]; - storage = data; - } - - @property auto slice(size_t n)()inout pure nothrow @nogc - { - auto ptr = raw_ptr!n; - return packedArrayView!(Types[n])(ptr, sz[n]); - } - - @property auto ptr(size_t n)()inout pure nothrow @nogc - { - auto ptr = raw_ptr!n; - return inout(PackedPtr!(Types[n]))(ptr); - } - - template length(size_t n) - { - @property size_t length()const @safe pure nothrow @nogc{ return sz[n]; } - - @property void length(size_t new_size) - { - if (new_size > sz[n]) - {// extend - size_t delta = (new_size - sz[n]); - sz[n] += delta; - delta = spaceFor!(bitSizeOf!(Types[n]))(delta); - storage.length += delta;// extend space at end - // raw_slice!x must follow resize as it could be moved! - // next stmts move all data past this array, last-one-goes-first - static if (n != dim-1) - { - auto start = raw_ptr!(n+1); - // len includes delta - size_t len = (storage.ptr+storage.length-start); - - copyBackwards(start[0 .. len-delta], start[delta .. len]); - - start[0 .. delta] = 0; - // offsets are used for raw_slice, ptr etc. - foreach (i; n+1 .. dim) - offsets[i] += delta; - } - } - else if (new_size < sz[n]) - {// shrink - size_t delta = (sz[n] - new_size); - sz[n] -= delta; - delta = spaceFor!(bitSizeOf!(Types[n]))(delta); - // move all data past this array, forward direction - static if (n != dim-1) - { - auto start = raw_ptr!(n+1); - size_t len = (storage.ptr+storage.length-start); - copyForward(start[0 .. len-delta], start[delta .. len]); - - // adjust offsets last, they affect raw_slice - foreach (i; n+1 .. dim) - offsets[i] -= delta; - } - storage.length -= delta; - } - // else - NOP - } - } - - @property size_t bytes(size_t n=size_t.max)() const @safe - { - static if (n == size_t.max) - return storage.length*size_t.sizeof; - else static if (n != Types.length-1) - return (raw_ptr!(n+1)-raw_ptr!n)*size_t.sizeof; - else - return (storage.ptr+storage.length - raw_ptr!n)*size_t.sizeof; - } - - void store(OutRange)(scope OutRange sink) const - if (isOutputRange!(OutRange, char)) - { - import std.format.write : formattedWrite; - formattedWrite(sink, "[%( 0x%x, %)]", offsets[]); - formattedWrite(sink, ", [%( 0x%x, %)]", sz[]); - formattedWrite(sink, ", [%( 0x%x, %)]", storage); - } - -private: - import std.meta : staticMap; - @property auto raw_ptr(size_t n)()inout pure nothrow @nogc - { - static if (n == 0) - return storage.ptr; - else - { - return storage.ptr+offsets[n]; - } - } - enum dim = Types.length; - size_t[dim] offsets;// offset for level x - size_t[dim] sz;// size of level x - alias bitWidth = staticMap!(bitSizeOf, Types); - size_t[] storage; -} - -@system unittest -{ - import std.conv : text; - enum dg = (){ - // sizes are: - // lvl0: 3, lvl1 : 2, lvl2: 1 - auto m = MultiArray!(int, ubyte, int)(3,2,1); - - static void check(size_t k, T)(ref T m, int n) - { - foreach (i; 0 .. n) - assert(m.slice!(k)[i] == i+1, text("level:",i," : ",m.slice!(k)[0 .. n])); - } - - static void checkB(size_t k, T)(ref T m, int n) - { - foreach (i; 0 .. n) - assert(m.slice!(k)[i] == n-i, text("level:",i," : ",m.slice!(k)[0 .. n])); - } - - static void fill(size_t k, T)(ref T m, int n) - { - foreach (i; 0 .. n) - m.slice!(k)[i] = force!ubyte(i+1); - } - - static void fillB(size_t k, T)(ref T m, int n) - { - foreach (i; 0 .. n) - m.slice!(k)[i] = force!ubyte(n-i); - } - - m.length!1 = 100; - fill!1(m, 100); - check!1(m, 100); - - m.length!0 = 220; - fill!0(m, 220); - check!1(m, 100); - check!0(m, 220); - - m.length!2 = 17; - fillB!2(m, 17); - checkB!2(m, 17); - check!0(m, 220); - check!1(m, 100); - - m.length!2 = 33; - checkB!2(m, 17); - fillB!2(m, 33); - checkB!2(m, 33); - check!0(m, 220); - check!1(m, 100); - - m.length!1 = 195; - fillB!1(m, 195); - checkB!1(m, 195); - checkB!2(m, 33); - check!0(m, 220); - - auto marr = MultiArray!(BitPacked!(uint, 4), BitPacked!(uint, 6))(20, 10); - marr.length!0 = 15; - marr.length!1 = 30; - fill!1(marr, 30); - fill!0(marr, 15); - check!1(marr, 30); - check!0(marr, 15); - return 0; - }; - enum ct = dg(); - auto rt = dg(); -} - -@system unittest -{// more bitpacking tests - import std.conv : text; - - alias Bitty = - MultiArray!(BitPacked!(size_t, 3) - , BitPacked!(size_t, 4) - , BitPacked!(size_t, 3) - , BitPacked!(size_t, 6) - , bool); - alias fn1 = sliceBits!(13, 16); - alias fn2 = sliceBits!( 9, 13); - alias fn3 = sliceBits!( 6, 9); - alias fn4 = sliceBits!( 0, 6); - static void check(size_t lvl, MA)(ref MA arr){ - for (size_t i = 0; i< arr.length!lvl; i++) - assert(arr.slice!(lvl)[i] == i, text("Mismatch on lvl ", lvl, " idx ", i, " value: ", arr.slice!(lvl)[i])); - } - - static void fillIdx(size_t lvl, MA)(ref MA arr){ - for (size_t i = 0; i< arr.length!lvl; i++) - arr.slice!(lvl)[i] = i; - } - Bitty m1; - - m1.length!4 = 10; - m1.length!3 = 2^^6; - m1.length!2 = 2^^3; - m1.length!1 = 2^^4; - m1.length!0 = 2^^3; - - m1.length!4 = 2^^16; - - for (size_t i = 0; i< m1.length!4; i++) - m1.slice!(4)[i] = i % 2; - - fillIdx!1(m1); - check!1(m1); - fillIdx!2(m1); - check!2(m1); - fillIdx!3(m1); - check!3(m1); - fillIdx!0(m1); - check!0(m1); - check!3(m1); - check!2(m1); - check!1(m1); - for (size_t i=0; i < 2^^16; i++) - { - m1.slice!(4)[i] = i % 2; - m1.slice!(0)[fn1(i)] = fn1(i); - m1.slice!(1)[fn2(i)] = fn2(i); - m1.slice!(2)[fn3(i)] = fn3(i); - m1.slice!(3)[fn4(i)] = fn4(i); - } - for (size_t i=0; i < 2^^16; i++) - { - assert(m1.slice!(4)[i] == i % 2); - assert(m1.slice!(0)[fn1(i)] == fn1(i)); - assert(m1.slice!(1)[fn2(i)] == fn2(i)); - assert(m1.slice!(2)[fn3(i)] == fn3(i)); - assert(m1.slice!(3)[fn4(i)] == fn4(i)); - } -} - -size_t spaceFor(size_t _bits)(size_t new_len) @safe pure nothrow @nogc -{ - import std.math.algebraic : nextPow2; - enum bits = _bits == 1 ? 1 : nextPow2(_bits - 1);// see PackedArrayView - static if (bits > 8*size_t.sizeof) - { - static assert(bits % (size_t.sizeof*8) == 0); - return new_len * bits/(8*size_t.sizeof); - } - else - { - enum factor = size_t.sizeof*8/bits; - return (new_len+factor-1)/factor; // rounded up - } -} - -template isBitPackableType(T) -{ - enum isBitPackableType = isBitPacked!T - || isIntegral!T || is(T == bool) || isSomeChar!T; -} - -//============================================================================ -template PackedArrayView(T) -if ((is(T dummy == BitPacked!(U, sz), U, size_t sz) - && isBitPackableType!U) || isBitPackableType!T) -{ - import std.math.algebraic : nextPow2; - private enum bits = bitSizeOf!T; - alias PackedArrayView = PackedArrayViewImpl!(T, bits > 1 ? nextPow2(bits - 1) : 1); -} - -//unsafe and fast access to a chunk of RAM as if it contains packed values -template PackedPtr(T) -if ((is(T dummy == BitPacked!(U, sz), U, size_t sz) - && isBitPackableType!U) || isBitPackableType!T) -{ - import std.math.algebraic : nextPow2; - private enum bits = bitSizeOf!T; - alias PackedPtr = PackedPtrImpl!(T, bits > 1 ? nextPow2(bits - 1) : 1); -} - -struct PackedPtrImpl(T, size_t bits) -{ -pure nothrow: - static assert(isPow2OrZero(bits)); - - this(inout(size_t)* ptr)inout @safe @nogc - { - origin = ptr; - } - - private T simpleIndex(size_t n) inout - { - immutable q = n / factor; - immutable r = n % factor; - return cast(T)((origin[q] >> bits*r) & mask); - } - - private void simpleWrite(TypeOfBitPacked!T val, size_t n) - in - { - static if (isIntegral!T) - assert(val <= mask); - } - do - { - immutable q = n / factor; - immutable r = n % factor; - immutable tgt_shift = bits*r; - immutable word = origin[q]; - origin[q] = (word & ~(mask << tgt_shift)) - | (cast(size_t) val << tgt_shift); - } - - static if (factor == bytesPerWord// can safely pack by byte - || factor == 1 // a whole word at a time - || ((factor == bytesPerWord/2 || factor == bytesPerWord/4) - && hasUnalignedReads)) // this needs unaligned reads - { - static if (factor == bytesPerWord) - alias U = ubyte; - else static if (factor == bytesPerWord/2) - alias U = ushort; - else static if (factor == bytesPerWord/4) - alias U = uint; - else static if (size_t.sizeof == 8 && factor == bytesPerWord/8) - alias U = ulong; - - T opIndex(size_t idx) inout - { - T ret; - version (LittleEndian) - ret = __ctfe ? simpleIndex(idx) : - cast(inout(T))(cast(U*) origin)[idx]; - else - ret = simpleIndex(idx); - return ret; - } - - static if (isBitPacked!T) // lack of user-defined implicit conversion - { - void opIndexAssign(T val, size_t idx) - { - return opIndexAssign(cast(TypeOfBitPacked!T) val, idx); - } - } - - void opIndexAssign(TypeOfBitPacked!T val, size_t idx) - { - version (LittleEndian) - { - if (__ctfe) - simpleWrite(val, idx); - else - (cast(U*) origin)[idx] = cast(U) val; - } - else - simpleWrite(val, idx); - } - } - else - { - T opIndex(size_t n) inout - { - return simpleIndex(n); - } - - static if (isBitPacked!T) // lack of user-defined implicit conversion - { - void opIndexAssign(T val, size_t idx) - { - return opIndexAssign(cast(TypeOfBitPacked!T) val, idx); - } - } - - void opIndexAssign(TypeOfBitPacked!T val, size_t n) - { - return simpleWrite(val, n); - } - } - -private: - // factor - number of elements in one machine word - enum factor = size_t.sizeof*8/bits, mask = 2^^bits-1; - enum bytesPerWord = size_t.sizeof; - size_t* origin; -} - -// data is packed only by power of two sized packs per word, -// thus avoiding mul/div overhead at the cost of ultimate packing -// this construct doesn't own memory, only provides access, see MultiArray for usage -struct PackedArrayViewImpl(T, size_t bits) -{ -pure nothrow: - - this(inout(size_t)* origin, size_t offset, size_t items) inout @safe - { - ptr = inout(PackedPtr!(T))(origin); - ofs = offset; - limit = items; - } - - bool zeros(size_t s, size_t e) - in - { - assert(s <= e); - } - do - { - s += ofs; - e += ofs; - immutable pad_s = roundUp(s); - if ( s >= e) - { - foreach (i; s .. e) - if (ptr[i]) - return false; - return true; - } - immutable pad_e = roundDown(e); - size_t i; - for (i=s; i<pad_s; i++) - if (ptr[i]) - return false; - // all in between is x*factor elements - for (size_t j=i/factor; i<pad_e; i+=factor, j++) - if (ptr.origin[j]) - return false; - for (; i<e; i++) - if (ptr[i]) - return false; - return true; - } - - T opIndex(size_t idx) inout - in - { - assert(idx < limit); - } - do - { - return ptr[ofs + idx]; - } - - static if (isBitPacked!T) // lack of user-defined implicit conversion - { - void opIndexAssign(T val, size_t idx) - { - return opIndexAssign(cast(TypeOfBitPacked!T) val, idx); - } - } - - void opIndexAssign(TypeOfBitPacked!T val, size_t idx) - in - { - assert(idx < limit); - } - do - { - ptr[ofs + idx] = val; - } - - static if (isBitPacked!T) // lack of user-defined implicit conversions - { - void opSliceAssign(T val, size_t start, size_t end) - { - opSliceAssign(cast(TypeOfBitPacked!T) val, start, end); - } - } - - void opSliceAssign(TypeOfBitPacked!T val, size_t start, size_t end) - in - { - assert(start <= end); - assert(end <= limit); - } - do - { - // account for ofsetted view - start += ofs; - end += ofs; - // rounded to factor granularity - immutable pad_start = roundUp(start);// rounded up - if (pad_start >= end) //rounded up >= then end of slice - { - //nothing to gain, use per element assignment - foreach (i; start .. end) - ptr[i] = val; - return; - } - immutable pad_end = roundDown(end); // rounded down - size_t i; - for (i=start; i<pad_start; i++) - ptr[i] = val; - // all in between is x*factor elements - if (pad_start != pad_end) - { - immutable repval = replicateBits!(factor, bits)(val); - for (size_t j=i/factor; i<pad_end; i+=factor, j++) - ptr.origin[j] = repval;// so speed it up by factor - } - for (; i<end; i++) - ptr[i] = val; - } - - auto opSlice(size_t from, size_t to)inout - in - { - assert(from <= to); - assert(ofs + to <= limit); - } - do - { - return typeof(this)(ptr.origin, ofs + from, to - from); - } - - auto opSlice(){ return opSlice(0, length); } - - bool opEquals(T)(auto ref T arr) const - { - if (limit != arr.limit) - return false; - size_t s1 = ofs, s2 = arr.ofs; - size_t e1 = s1 + limit, e2 = s2 + limit; - if (s1 % factor == 0 && s2 % factor == 0 && length % factor == 0) - { - return ptr.origin[s1/factor .. e1/factor] - == arr.ptr.origin[s2/factor .. e2/factor]; - } - for (size_t i=0;i<limit; i++) - if (this[i] != arr[i]) - return false; - return true; - } - - @property size_t length()const{ return limit; } - -private: - auto roundUp()(size_t val){ return (val+factor-1)/factor*factor; } - auto roundDown()(size_t val){ return val/factor*factor; } - // factor - number of elements in one machine word - enum factor = size_t.sizeof*8/bits; - PackedPtr!(T) ptr; - size_t ofs, limit; -} - - -private struct SliceOverIndexed(T) -{ - enum assignableIndex = is(typeof((){ T.init[0] = Item.init; })); - enum assignableSlice = is(typeof((){ T.init[0 .. 0] = Item.init; })); - auto opIndex(size_t idx)const - in - { - assert(idx < to - from); - } - do - { - return (*arr)[from+idx]; - } - - static if (assignableIndex) - void opIndexAssign(Item val, size_t idx) - in - { - assert(idx < to - from); - } - do - { - (*arr)[from+idx] = val; - } - - auto opSlice(size_t a, size_t b) - { - return typeof(this)(from+a, from+b, arr); - } - - // static if (assignableSlice) - void opSliceAssign(T)(T val, size_t start, size_t end) - { - (*arr)[start+from .. end+from] = val; - } - - auto opSlice() - { - return typeof(this)(from, to, arr); - } - - @property size_t length()const { return to-from;} - - alias opDollar = length; - - @property bool empty()const { return from == to; } - - @property auto front()const { return (*arr)[from]; } - - static if (assignableIndex) - @property void front(Item val) { (*arr)[from] = val; } - - @property auto back()const { return (*arr)[to-1]; } - - static if (assignableIndex) - @property void back(Item val) { (*arr)[to-1] = val; } - - @property auto save() inout { return this; } - - void popFront() { from++; } - - void popBack() { to--; } - - bool opEquals(T)(auto ref T arr) const - { - if (arr.length != length) - return false; - for (size_t i=0; i <length; i++) - if (this[i] != arr[i]) - return false; - return true; - } -private: - alias Item = typeof(T.init[0]); - size_t from, to; - T* arr; -} - -@safe pure nothrow @nogc unittest -{ - static assert(isRandomAccessRange!(SliceOverIndexed!(int[]))); -} - -SliceOverIndexed!(const(T)) sliceOverIndexed(T)(size_t a, size_t b, const(T)* x) -if (is(Unqual!T == T)) -{ - return SliceOverIndexed!(const(T))(a, b, x); -} - -// BUG? inout is out of reach -//...SliceOverIndexed.arr only parameters or stack based variables can be inout -SliceOverIndexed!T sliceOverIndexed(T)(size_t a, size_t b, T* x) -if (is(Unqual!T == T)) -{ - return SliceOverIndexed!T(a, b, x); -} - -@system unittest -{ - int[] idxArray = [2, 3, 5, 8, 13]; - auto sliced = sliceOverIndexed(0, idxArray.length, &idxArray); - - assert(!sliced.empty); - assert(sliced.front == 2); - sliced.front = 1; - assert(sliced.front == 1); - assert(sliced.back == 13); - sliced.popFront(); - assert(sliced.front == 3); - assert(sliced.back == 13); - sliced.back = 11; - assert(sliced.back == 11); - sliced.popBack(); - - assert(sliced.front == 3); - assert(sliced[$-1] == 8); - sliced = sliced[]; - assert(sliced[0] == 3); - assert(sliced.back == 8); - sliced = sliced[1..$]; - assert(sliced.front == 5); - sliced = sliced[0..$-1]; - assert(sliced[$-1] == 5); - - int[] other = [2, 5]; - assert(sliced[] == sliceOverIndexed(1, 2, &other)); - sliceOverIndexed(0, 2, &idxArray)[0 .. 2] = -1; - assert(idxArray[0 .. 2] == [-1, -1]); - uint[] nullArr = null; - auto nullSlice = sliceOverIndexed(0, 0, &idxArray); - assert(nullSlice.empty); -} - -private inout(PackedArrayView!T) packedArrayView(T)(inout(size_t)* ptr, size_t items) -{ - return inout(PackedArrayView!T)(ptr, 0, items); -} - - -//============================================================================ -// Partially unrolled binary search using Shar's method -//============================================================================ - -string genUnrolledSwitchSearch(size_t size) @safe pure nothrow -{ - import core.bitop : bsr; - import std.array : replace; - import std.conv : to; - assert(isPow2OrZero(size)); - string code = ` - import core.bitop : bsr; - auto power = bsr(m)+1; - switch (power){`; - size_t i = bsr(size); - foreach_reverse (val; 0 .. bsr(size)) - { - auto v = 2^^val; - code ~= ` - case pow: - if (pred(range[idx+m], needle)) - idx += m; - goto case; - `.replace("m", to!string(v)) - .replace("pow", to!string(i)); - i--; - } - code ~= ` - case 0: - if (pred(range[idx], needle)) - idx += 1; - goto default; - `; - code ~= ` - default: - }`; - return code; -} - -bool isPow2OrZero(size_t sz) @safe pure nothrow @nogc -{ - // See also: std.math.isPowerOf2() - return (sz & (sz-1)) == 0; -} - -size_t uniformLowerBound(alias pred, Range, T)(Range range, T needle) -if (is(T : ElementType!Range)) -{ - assert(isPow2OrZero(range.length)); - size_t idx = 0, m = range.length/2; - while (m != 0) - { - if (pred(range[idx+m], needle)) - idx += m; - m /= 2; - } - if (pred(range[idx], needle)) - idx += 1; - return idx; -} - -size_t switchUniformLowerBound(alias pred, Range, T)(Range range, T needle) -if (is(T : ElementType!Range)) -{ - assert(isPow2OrZero(range.length)); - size_t idx = 0, m = range.length/2; - enum max = 1 << 10; - while (m >= max) - { - if (pred(range[idx+m], needle)) - idx += m; - m /= 2; - } - mixin(genUnrolledSwitchSearch(max)); - return idx; -} - -template sharMethod(alias uniLowerBound) -{ - size_t sharMethod(alias _pred="a<b", Range, T)(Range range, T needle) - if (is(T : ElementType!Range)) - { - import std.functional : binaryFun; - import std.math.algebraic : nextPow2, truncPow2; - alias pred = binaryFun!_pred; - if (range.length == 0) - return 0; - if (isPow2OrZero(range.length)) - return uniLowerBound!pred(range, needle); - size_t n = truncPow2(range.length); - if (pred(range[n-1], needle)) - {// search in another 2^^k area that fully covers the tail of range - size_t k = nextPow2(range.length - n + 1); - return range.length - k + uniLowerBound!pred(range[$-k..$], needle); - } - else - return uniLowerBound!pred(range[0 .. n], needle); - } -} - -alias sharLowerBound = sharMethod!uniformLowerBound; -alias sharSwitchLowerBound = sharMethod!switchUniformLowerBound; - -@safe unittest -{ - import std.array : array; - import std.range : assumeSorted, iota; - - auto stdLowerBound(T)(T[] range, T needle) - { - return assumeSorted(range).lowerBound(needle).length; - } - immutable MAX = 5*1173; - auto arr = array(iota(5, MAX, 5)); - assert(arr.length == MAX/5-1); - foreach (i; 0 .. MAX+5) - { - auto st = stdLowerBound(arr, i); - assert(st == sharLowerBound(arr, i)); - assert(st == sharSwitchLowerBound(arr, i)); - } - arr = []; - auto st = stdLowerBound(arr, 33); - assert(st == sharLowerBound(arr, 33)); - assert(st == sharSwitchLowerBound(arr, 33)); -} -//============================================================================ - -@safe -{ -// hope to see simillar stuff in public interface... once Allocators are out -//@@@BUG moveFront and friends? dunno, for now it's POD-only - -@trusted size_t genericReplace(Policy=void, T, Range) - (ref T dest, size_t from, size_t to, Range stuff) -{ - import std.algorithm.mutation : copy; - size_t delta = to - from; - size_t stuff_end = from+stuff.length; - if (stuff.length > delta) - {// replace increases length - delta = stuff.length - delta;// now, new is > old by delta - static if (is(Policy == void)) - dest.length = dest.length+delta;//@@@BUG lame @property - else - dest = Policy.realloc(dest, dest.length+delta); - copyBackwards(dest[to .. dest.length-delta], - dest[to+delta .. dest.length]); - copyForward(stuff, dest[from .. stuff_end]); - } - else if (stuff.length == delta) - { - copy(stuff, dest[from .. to]); - } - else - {// replace decreases length by delta - delta = delta - stuff.length; - copy(stuff, dest[from .. stuff_end]); - copyForward(dest[to .. dest.length], - dest[stuff_end .. dest.length-delta]); - static if (is(Policy == void)) - dest.length = dest.length - delta;//@@@BUG lame @property - else - dest = Policy.realloc(dest, dest.length-delta); - } - return stuff_end; -} - - -// Simple storage manipulation policy -@safe private struct GcPolicy -{ - import std.traits : isDynamicArray; - - static T[] dup(T)(const T[] arr) - { - return arr.dup; - } - - static T[] alloc(T)(size_t size) - { - return new T[size]; - } - - static T[] realloc(T)(T[] arr, size_t sz) - { - arr.length = sz; - return arr; - } - - static void replaceImpl(T, Range)(ref T[] dest, size_t from, size_t to, Range stuff) - { - replaceInPlace(dest, from, to, stuff); - } - - static void append(T, V)(ref T[] arr, V value) - if (!isInputRange!V) - { - arr ~= force!T(value); - } - - static void append(T, V)(ref T[] arr, V value) - if (isInputRange!V) - { - insertInPlace(arr, arr.length, value); - } - - static void destroy(T)(ref T arr) pure // pure required for -dip25, inferred for -dip1000 - if (isDynamicArray!T && is(Unqual!T == T)) - { - debug - { - arr[] = cast(typeof(T.init[0]))(0xdead_beef); - } - arr = null; - } - - static void destroy(T)(ref T arr) pure // pure required for -dip25, inferred for -dip1000 - if (isDynamicArray!T && !is(Unqual!T == T)) - { - arr = null; - } -} - -// ditto -@safe struct ReallocPolicy -{ - import std.range.primitives : hasLength; - - static T[] dup(T)(const T[] arr) - { - auto result = alloc!T(arr.length); - result[] = arr[]; - return result; - } - - static T[] alloc(T)(size_t size) @trusted - { - import std.internal.memory : enforceMalloc; - - import core.checkedint : mulu; - bool overflow; - size_t nbytes = mulu(size, T.sizeof, overflow); - if (overflow) assert(0); - - auto ptr = cast(T*) enforceMalloc(nbytes); - return ptr[0 .. size]; - } - - static T[] realloc(T)(return scope T[] arr, size_t size) @trusted - { - import std.internal.memory : enforceRealloc; - if (!size) - { - destroy(arr); - return null; - } - - import core.checkedint : mulu; - bool overflow; - size_t nbytes = mulu(size, T.sizeof, overflow); - if (overflow) assert(0); - - auto ptr = cast(T*) enforceRealloc(arr.ptr, nbytes); - return ptr[0 .. size]; - } - - static void replaceImpl(T, Range)(ref T[] dest, size_t from, size_t to, Range stuff) - { - genericReplace!(ReallocPolicy)(dest, from, to, stuff); - } - - static void append(T, V)(ref T[] arr, V value) - if (!isInputRange!V) - { - if (arr.length == size_t.max) assert(0); - arr = realloc(arr, arr.length+1); - arr[$-1] = force!T(value); - } - - pure @safe unittest - { - int[] arr; - ReallocPolicy.append(arr, 3); - - import std.algorithm.comparison : equal; - assert(equal(arr, [3])); - } - - static void append(T, V)(ref T[] arr, V value) - if (isInputRange!V && hasLength!V) - { - import core.checkedint : addu; - bool overflow; - size_t nelems = addu(arr.length, value.length, overflow); - if (overflow) assert(0); - - arr = realloc(arr, nelems); - - import std.algorithm.mutation : copy; - copy(value, arr[$-value.length..$]); - } - - pure @safe unittest - { - int[] arr; - ReallocPolicy.append(arr, [1,2,3]); - - import std.algorithm.comparison : equal; - assert(equal(arr, [1,2,3])); - } - - static void destroy(T)(scope ref T[] arr) @trusted - { - import core.memory : pureFree; - if (arr.ptr) - pureFree(arr.ptr); - arr = null; - } -} - -//build hack -alias _RealArray = CowArray!ReallocPolicy; - -pure @safe unittest -{ - import std.algorithm.comparison : equal; - - with(ReallocPolicy) - { - bool test(T, U, V)(T orig, size_t from, size_t to, U toReplace, V result, - string file = __FILE__, size_t line = __LINE__) - { - { - replaceImpl(orig, from, to, toReplace); - scope(exit) destroy(orig); - if (!equal(orig, result)) - return false; - } - return true; - } - static T[] arr(T)(T[] args... ) - { - return dup(args); - } - - assert(test(arr([1, 2, 3, 4]), 0, 0, [5, 6, 7], [5, 6, 7, 1, 2, 3, 4])); - assert(test(arr([1, 2, 3, 4]), 0, 2, cast(int[])[], [3, 4])); - assert(test(arr([1, 2, 3, 4]), 0, 4, [5, 6, 7], [5, 6, 7])); - assert(test(arr([1, 2, 3, 4]), 0, 2, [5, 6, 7], [5, 6, 7, 3, 4])); - assert(test(arr([1, 2, 3, 4]), 2, 3, [5, 6, 7], [1, 2, 5, 6, 7, 4])); - } -} - -/** - Tests if T is some kind a set of code points. Intended for template constraints. -*/ -public template isCodepointSet(T) -{ - static if (is(T dummy == InversionList!(Args), Args...)) - enum isCodepointSet = true; - else - enum isCodepointSet = false; -} - -/** - Tests if `T` is a pair of integers that implicitly convert to `V`. - The following code must compile for any pair `T`: - --- - (T x){ V a = x[0]; V b = x[1];} - --- - The following must not compile: - --- - (T x){ V c = x[2];} - --- -*/ -public template isIntegralPair(T, V=uint) -{ - enum isIntegralPair = is(typeof((T x){ V a = x[0]; V b = x[1];})) - && !is(typeof((T x){ V c = x[2]; })); -} - - -/** - The recommended default type for set of $(CODEPOINTS). - For details, see the current implementation: $(LREF InversionList). -*/ -public alias CodepointSet = InversionList!GcPolicy; - - -//@@@BUG: std.typecons tuples depend on std.format to produce fields mixin -// which relies on std.uni.isGraphical and this chain blows up with Forward reference error -// hence below doesn't seem to work -// public alias CodepointInterval = Tuple!(uint, "a", uint, "b"); - -/** - The recommended type of $(REF Tuple, std,_typecons) - to represent [a, b$(RPAREN) intervals of $(CODEPOINTS). As used in $(LREF InversionList). - Any interval type should pass $(LREF isIntegralPair) trait. -*/ -public struct CodepointInterval -{ -pure: - uint[2] _tuple; - alias _tuple this; - -@safe pure nothrow @nogc: - - this(uint low, uint high) - { - _tuple[0] = low; - _tuple[1] = high; - } - bool opEquals(T)(T val) const - { - return this[0] == val[0] && this[1] == val[1]; - } - @property ref inout(uint) a() return inout { return _tuple[0]; } - @property ref inout(uint) b() return inout { return _tuple[1]; } -} - -/** - $(P - `InversionList` is a set of $(CODEPOINTS) - represented as an array of open-right [a, b$(RPAREN) - intervals (see $(LREF CodepointInterval) above). - The name comes from the way the representation reads left to right. - For instance a set of all values [10, 50$(RPAREN), [80, 90$(RPAREN), - plus a singular value 60 looks like this: - ) - --- - 10, 50, 60, 61, 80, 90 - --- - $(P - The way to read this is: start with negative meaning that all numbers - smaller then the next one are not present in this set (and positive - - the contrary). Then switch positive/negative after each - number passed from left to right. - ) - $(P This way negative spans until 10, then positive until 50, - then negative until 60, then positive until 61, and so on. - As seen this provides a space-efficient storage of highly redundant data - that comes in long runs. A description which Unicode $(CHARACTER) - properties fit nicely. The technique itself could be seen as a variation - on $(LINK2 https://en.wikipedia.org/wiki/Run-length_encoding, RLE encoding). - ) - - $(P Sets are value types (just like `int` is) thus they - are never aliased. - ) - Example: - --- - auto a = CodepointSet('a', 'z'+1); - auto b = CodepointSet('A', 'Z'+1); - auto c = a; - a = a | b; - assert(a == CodepointSet('A', 'Z'+1, 'a', 'z'+1)); - assert(a != c); - --- - $(P See also $(LREF unicode) for simpler construction of sets - from predefined ones. - ) - - $(P Memory usage is 8 bytes per each contiguous interval in a set. - The value semantics are achieved by using the - $(HTTP en.wikipedia.org/wiki/Copy-on-write, COW) technique - and thus it's $(RED not) safe to cast this type to $(D_KEYWORD shared). - ) - - Note: - $(P It's not recommended to rely on the template parameters - or the exact type of a current $(CODEPOINT) set in `std.uni`. - The type and parameters may change when the standard - allocators design is finalized. - Use $(LREF isCodepointSet) with templates or just stick with the default - alias $(LREF CodepointSet) throughout the whole code base. - ) -*/ -public struct InversionList(SP=GcPolicy) -{ - import std.range : assumeSorted; - - /** - Construct from another code point set of any type. - */ - this(Set)(Set set) pure - if (isCodepointSet!Set) - { - uint[] arr; - foreach (v; set.byInterval) - { - arr ~= v.a; - arr ~= v.b; - } - data = CowArray!(SP).reuse(arr); - } - - /** - Construct a set from a forward range of code point intervals. - */ - this(Range)(Range intervals) pure - if (isForwardRange!Range && isIntegralPair!(ElementType!Range)) - { - uint[] arr; - foreach (v; intervals) - { - SP.append(arr, v.a); - SP.append(arr, v.b); - } - data = CowArray!(SP).reuse(arr); - sanitize(); //enforce invariant: sort intervals etc. - } - - //helper function that avoids sanity check to be CTFE-friendly - private static fromIntervals(Range)(Range intervals) pure - { - import std.algorithm.iteration : map; - import std.range : roundRobin; - auto flattened = roundRobin(intervals.save.map!"a[0]"(), - intervals.save.map!"a[1]"()); - InversionList set; - set.data = CowArray!(SP)(flattened); - return set; - } - //ditto untill sort is CTFE-able - private static fromIntervals()(uint[] intervals...) pure - in - { - import std.conv : text; - assert(intervals.length % 2 == 0, "Odd number of interval bounds [a, b)!"); - for (uint i = 0; i < intervals.length; i += 2) - { - auto a = intervals[i], b = intervals[i+1]; - assert(a < b, text("illegal interval [a, b): ", a, " > ", b)); - } - } - do - { - InversionList set; - set.data = CowArray!(SP)(intervals); - return set; - } - - /** - Construct a set from plain values of code point intervals. - */ - this()(uint[] intervals...) - in - { - import std.conv : text; - assert(intervals.length % 2 == 0, "Odd number of interval bounds [a, b)!"); - for (uint i = 0; i < intervals.length; i += 2) - { - auto a = intervals[i], b = intervals[i+1]; - assert(a < b, text("illegal interval [a, b): ", a, " > ", b)); - } - } - do - { - data = CowArray!(SP)(intervals); - sanitize(); //enforce invariant: sort intervals etc. - } - - /// - pure @safe unittest - { - import std.algorithm.comparison : equal; - - auto set = CodepointSet('a', 'z'+1, 'Đ°', 'я'+1); - foreach (v; 'a'..'z'+1) - assert(set[v]); - // Cyrillic lowercase interval - foreach (v; 'Đ°'..'я'+1) - assert(set[v]); - //specific order is not required, intervals may interesect - auto set2 = CodepointSet('Đ°', 'я'+1, 'a', 'd', 'b', 'z'+1); - //the same end result - assert(set2.byInterval.equal(set.byInterval)); - // test constructor this(Range)(Range intervals) - auto chessPiecesWhite = CodepointInterval(9812, 9818); - auto chessPiecesBlack = CodepointInterval(9818, 9824); - auto set3 = CodepointSet([chessPiecesWhite, chessPiecesBlack]); - foreach (v; '♔'..'♟'+1) - assert(set3[v]); - } - - /** - Get range that spans all of the $(CODEPOINT) intervals in this $(LREF InversionList). - */ - @property auto byInterval() scope - { - // TODO: change this to data[] once the -dip1000 errors have been fixed - // see e.g. https://github.com/dlang/phobos/pull/6638 - import std.array : array; - return Intervals!(typeof(data.array))(data.array); - } - - @safe unittest - { - import std.algorithm.comparison : equal; - import std.typecons : tuple; - - auto set = CodepointSet('A', 'D'+1, 'a', 'd'+1); - - assert(set.byInterval.equal([tuple('A','E'), tuple('a','e')])); - } - - package(std) @property const(CodepointInterval)[] intervals() const - { - import std.array : array; - return Intervals!(typeof(data[]))(data[]).array; - } - - /** - Tests the presence of code point `val` in this set. - */ - bool opIndex(uint val) const - { - // the <= ensures that searching in interval of [a, b) for 'a' you get .length == 1 - // return assumeSorted!((a,b) => a <= b)(data[]).lowerBound(val).length & 1; - return sharSwitchLowerBound!"a <= b"(data[], val) & 1; - } - - /// - pure @safe unittest - { - auto gothic = unicode.Gothic; - // Gothic letter ahsa - assert(gothic['\U00010330']); - // no ascii in Gothic obviously - assert(!gothic['$']); - } - - - // Linear scan for `ch`. Useful only for small sets. - // TODO: - // used internally in std.regex - // should be properly exposed in a public API ? - package(std) auto scanFor()(dchar ch) const - { - immutable len = data.length; - for (size_t i = 0; i < len; i++) - if (ch < data[i]) - return i & 1; - return 0; - } - - /// Number of $(CODEPOINTS) in this set - @property size_t length() - { - size_t sum = 0; - foreach (iv; byInterval) - { - sum += iv.b - iv.a; - } - return sum; - } - -// bootstrap full set operations from 4 primitives (suitable as a template mixin): -// addInterval, skipUpTo, dropUpTo & byInterval iteration -//============================================================================ -public: - /** - $(P Sets support natural syntax for set algebra, namely: ) - $(BOOKTABLE , - $(TR $(TH Operator) $(TH Math notation) $(TH Description) ) - $(TR $(TD &) $(TD a ∊ b) $(TD intersection) ) - $(TR $(TD |) $(TD a ∪ b) $(TD union) ) - $(TR $(TD -) $(TD a ∖ b) $(TD subtraction) ) - $(TR $(TD ~) $(TD a ~ b) $(TD symmetric set difference i.e. (a ∪ b) \ (a ∊ b)) ) - ) - */ - This opBinary(string op, U)(U rhs) - if (isCodepointSet!U || is(U:dchar)) - { - static if (op == "&" || op == "|" || op == "~") - {// symmetric ops thus can swap arguments to reuse r-value - static if (is(U:dchar)) - { - auto tmp = this; - mixin("tmp "~op~"= rhs; "); - return tmp; - } - else - { - static if (is(Unqual!U == U)) - { - // try hard to reuse r-value - mixin("rhs "~op~"= this;"); - return rhs; - } - else - { - auto tmp = this; - mixin("tmp "~op~"= rhs;"); - return tmp; - } - } - } - else static if (op == "-") // anti-symmetric - { - auto tmp = this; - tmp -= rhs; - return tmp; - } - else - static assert(0, "no operator "~op~" defined for Set"); - } - - /// - pure @safe unittest - { - import std.algorithm.comparison : equal; - import std.range : iota; - - auto lower = unicode.LowerCase; - auto upper = unicode.UpperCase; - auto ascii = unicode.ASCII; - - assert((lower & upper).empty); // no intersection - auto lowerASCII = lower & ascii; - assert(lowerASCII.byCodepoint.equal(iota('a', 'z'+1))); - // throw away all of the lowercase ASCII - assert((ascii - lower).length == 128 - 26); - - auto onlyOneOf = lower ~ ascii; - assert(!onlyOneOf['Δ']); // not ASCII and not lowercase - assert(onlyOneOf['$']); // ASCII and not lowercase - assert(!onlyOneOf['a']); // ASCII and lowercase - assert(onlyOneOf['я']); // not ASCII but lowercase - - // throw away all cased letters from ASCII - auto noLetters = ascii - (lower | upper); - assert(noLetters.length == 128 - 26*2); - } - - /// The 'op=' versions of the above overloaded operators. - ref This opOpAssign(string op, U)(U rhs) - if (isCodepointSet!U || is(U:dchar)) - { - static if (op == "|") // union - { - static if (is(U:dchar)) - { - this.addInterval(rhs, rhs+1); - return this; - } - else - return this.add(rhs); - } - else static if (op == "&") // intersection - return this.intersect(rhs);// overloaded - else static if (op == "-") // set difference - return this.sub(rhs);// overloaded - else static if (op == "~") // symmetric set difference - { - auto copy = this & rhs; - this |= rhs; - this -= copy; - return this; - } - else - static assert(0, "no operator "~op~" defined for Set"); - } - - /** - Tests the presence of codepoint `ch` in this set, - the same as $(LREF opIndex). - */ - bool opBinaryRight(string op: "in", U)(U ch) const - if (is(U : dchar)) - { - return this[ch]; - } - - /// - pure @safe unittest - { - assert('я' in unicode.Cyrillic); - assert(!('z' in unicode.Cyrillic)); - } - - - - /** - * Obtains a set that is the inversion of this set. - * - * See_Also: $(LREF inverted) - */ - auto opUnary(string op: "!")() - { - return this.inverted; - } - - /** - A range that spans each $(CODEPOINT) in this set. - */ - @property auto byCodepoint() - { - static struct CodepointRange - { - this(This set) - { - r = set.byInterval; - if (!r.empty) - cur = r.front.a; - } - - @property dchar front() const - { - return cast(dchar) cur; - } - - @property bool empty() const - { - return r.empty; - } - - void popFront() - { - cur++; - while (cur >= r.front.b) - { - r.popFront(); - if (r.empty) - break; - cur = r.front.a; - } - } - private: - uint cur; - typeof(This.init.byInterval) r; - } - - return CodepointRange(this); - } - - /// - pure @safe unittest - { - import std.algorithm.comparison : equal; - import std.range : iota; - - auto set = unicode.ASCII; - set.byCodepoint.equal(iota(0, 0x80)); - } - - /** - $(P Obtain textual representation of this set in from of - open-right intervals and feed it to `sink`. - ) - $(P Used by various standard formatting facilities such as - $(REF formattedWrite, std,format), $(REF write, std,stdio), - $(REF writef, std,stdio), $(REF to, std,conv) and others. - ) - Example: - --- - import std.conv; - assert(unicode.ASCII.to!string == "[0..128$(RPAREN)"); - --- - */ - - private import std.format.spec : FormatSpec; - - /*************************************** - * Obtain a textual representation of this InversionList - * in form of open-right intervals. - * - * The formatting flag is applied individually to each value, for example: - * $(LI $(B %s) and $(B %d) format the intervals as a [low .. high$(RPAREN) range of integrals) - * $(LI $(B %x) formats the intervals as a [low .. high$(RPAREN) range of lowercase hex characters) - * $(LI $(B %X) formats the intervals as a [low .. high$(RPAREN) range of uppercase hex characters) - */ - void toString(Writer)(scope Writer sink, scope const ref FormatSpec!char fmt) /* const */ - { - import std.format.write : formatValue; - auto range = byInterval; - if (range.empty) - return; - - while (1) - { - auto i = range.front; - range.popFront(); - - put(sink, "["); - formatValue(sink, i.a, fmt); - put(sink, ".."); - formatValue(sink, i.b, fmt); - put(sink, ")"); - if (range.empty) return; - put(sink, " "); - } - } - - /// - pure @safe unittest - { - import std.conv : to; - import std.format : format; - import std.uni : unicode; - - // This was originally using Cyrillic script. - // Unfortunately this is a pretty active range for changes, - // and hence broke in an update. - // Therefore the range Basic latin was used instead as it - // unlikely to ever change. - - assert(unicode.InBasic_latin.to!string == "[0..128)"); - - // The specs '%s' and '%d' are equivalent to the to!string call above. - assert(format("%d", unicode.InBasic_latin) == unicode.InBasic_latin.to!string); - - assert(format("%#x", unicode.InBasic_latin) == "[0..0x80)"); - assert(format("%#X", unicode.InBasic_latin) == "[0..0X80)"); - } - - pure @safe unittest - { - import std.exception : assertThrown; - import std.format : format, FormatException; - assertThrown!FormatException(format("%z", unicode.ASCII)); - } - - - /** - Add an interval [a, b$(RPAREN) to this set. - */ - ref add()(uint a, uint b) - { - addInterval(a, b); - return this; - } - - /// - pure @safe unittest - { - CodepointSet someSet; - someSet.add('0', '5').add('A','Z'+1); - someSet.add('5', '9'+1); - assert(someSet['0']); - assert(someSet['5']); - assert(someSet['9']); - assert(someSet['Z']); - } - -private: - - package(std) // used from: std.regex.internal.parser - ref intersect(U)(U rhs) - if (isCodepointSet!U) - { - Marker mark; - foreach ( i; rhs.byInterval) - { - mark = this.dropUpTo(i.a, mark); - mark = this.skipUpTo(i.b, mark); - } - this.dropUpTo(uint.max, mark); - return this; - } - - ref intersect()(dchar ch) - { - foreach (i; byInterval) - if (i.a <= ch && ch < i.b) - return this = This.init.add(ch, ch+1); - this = This.init; - return this; - } - - pure @safe unittest - { - assert(unicode.Cyrillic.intersect('-').byInterval.empty); - } - - ref sub()(dchar ch) - { - return subChar(ch); - } - - // same as the above except that skip & drop parts are swapped - package(std) // used from: std.regex.internal.parser - ref sub(U)(U rhs) - if (isCodepointSet!U) - { - Marker mark; - foreach (i; rhs.byInterval) - { - mark = this.skipUpTo(i.a, mark); - mark = this.dropUpTo(i.b, mark); - } - return this; - } - - package(std) // used from: std.regex.internal.parse - ref add(U)(U rhs) - if (isCodepointSet!U) - { - Marker start; - foreach (i; rhs.byInterval) - { - start = addInterval(i.a, i.b, start); - } - return this; - } - -// end of mixin-able part -//============================================================================ -public: - /** - Obtains a set that is the inversion of this set. - - See the '!' $(LREF opUnary) for the same but using operators. - */ - @property auto inverted() - { - InversionList inversion = this; - if (inversion.data.length == 0) - { - inversion.addInterval(0, lastDchar+1); - return inversion; - } - if (inversion.data[0] != 0) - genericReplace(inversion.data, 0, 0, [0]); - else - genericReplace(inversion.data, 0, 1, cast(uint[]) null); - if (data[data.length-1] != lastDchar+1) - genericReplace(inversion.data, - inversion.data.length, inversion.data.length, [lastDchar+1]); - else - genericReplace(inversion.data, - inversion.data.length-1, inversion.data.length, cast(uint[]) null); - - return inversion; - } - - /// - pure @safe unittest - { - auto set = unicode.ASCII; - // union with the inverse gets all of the code points in the Unicode - assert((set | set.inverted).length == 0x110000); - // no intersection with the inverse - assert((set & set.inverted).empty); - } - - package(std) static string toSourceCode(const(CodepointInterval)[] range, string funcName) - { - import std.algorithm.searching : countUntil; - import std.format : format; - enum maxBinary = 3; - static string linearScope(R)(R ivals, string indent) - { - string result = indent~"{\n"; - string deeper = indent~" "; - foreach (ival; ivals) - { - immutable span = ival[1] - ival[0]; - assert(span != 0); - if (span == 1) - { - result ~= format("%sif (ch == %s) return true;\n", deeper, ival[0]); - } - else if (span == 2) - { - result ~= format("%sif (ch == %s || ch == %s) return true;\n", - deeper, ival[0], ival[0]+1); - } - else - { - if (ival[0] != 0) // dchar is unsigned and < 0 is useless - result ~= format("%sif (ch < %s) return false;\n", deeper, ival[0]); - result ~= format("%sif (ch < %s) return true;\n", deeper, ival[1]); - } - } - result ~= format("%sreturn false;\n%s}\n", deeper, indent); // including empty range of intervals - return result; - } - - static string binaryScope(R)(R ivals, string indent) @safe - { - // time to do unrolled comparisons? - if (ivals.length < maxBinary) - return linearScope(ivals, indent); - else - return bisect(ivals, ivals.length/2, indent); - } - - // not used yet if/elsebinary search is far better with DMD as of 2.061 - // and GDC is doing fine job either way - static string switchScope(R)(R ivals, string indent) - { - string result = indent~"switch (ch){\n"; - string deeper = indent~" "; - foreach (ival; ivals) - { - if (ival[0]+1 == ival[1]) - { - result ~= format("%scase %s: return true;\n", - deeper, ival[0]); - } - else - { - result ~= format("%scase %s: .. case %s: return true;\n", - deeper, ival[0], ival[1]-1); - } - } - result ~= deeper~"default: return false;\n"~indent~"}\n"; - return result; - } - - static string bisect(R)(R range, size_t idx, string indent) - { - string deeper = indent ~ " "; - // bisect on one [a, b) interval at idx - string result = indent~"{\n"; - // less branch, < a - result ~= format("%sif (ch < %s)\n%s", - deeper, range[idx][0], binaryScope(range[0 .. idx], deeper)); - // middle point, >= a && < b - result ~= format("%selse if (ch < %s) return true;\n", - deeper, range[idx][1]); - // greater or equal branch, >= b - result ~= format("%selse\n%s", - deeper, binaryScope(range[idx+1..$], deeper)); - return result~indent~"}\n"; - } - - string code = format("bool %s(dchar ch) @safe pure nothrow @nogc\n", - funcName.empty ? "function" : funcName); - // special case first bisection to be on ASCII vs beyond - auto tillAscii = countUntil!"a[0] > 0x80"(range); - if (tillAscii <= 0) // everything is ASCII or nothing is ascii (-1 & 0) - code ~= binaryScope(range, ""); - else - code ~= bisect(range, tillAscii, ""); - return code; - } - - /** - Generates string with D source code of unary function with name of - `funcName` taking a single `dchar` argument. If `funcName` is empty - the code is adjusted to be a lambda function. - - The function generated tests if the $(CODEPOINT) passed - belongs to this set or not. The result is to be used with string mixin. - The intended usage area is aggressive optimization via meta programming - in parser generators and the like. - - Note: Use with care for relatively small or regular sets. It - could end up being slower then just using multi-staged tables. - - Example: - --- - import std.stdio; - - // construct set directly from [a, b$RPAREN intervals - auto set = CodepointSet(10, 12, 45, 65, 100, 200); - writeln(set); - writeln(set.toSourceCode("func")); - --- - - The above outputs something along the lines of: - --- - bool func(dchar ch) @safe pure nothrow @nogc - { - if (ch < 45) - { - if (ch == 10 || ch == 11) return true; - return false; - } - else if (ch < 65) return true; - else - { - if (ch < 100) return false; - if (ch < 200) return true; - return false; - } - } - --- - */ - string toSourceCode(string funcName="") - { - import std.array : array; - auto range = byInterval.array(); - return toSourceCode(range, funcName); - } - - /** - True if this set doesn't contain any $(CODEPOINTS). - */ - @property bool empty() const - { - return data.length == 0; - } - - /// - pure @safe unittest - { - CodepointSet emptySet; - assert(emptySet.length == 0); - assert(emptySet.empty); - } - -private: - alias This = typeof(this); - alias Marker = size_t; - - // a random-access range of integral pairs - static struct Intervals(Range) - { - import std.range.primitives : hasAssignableElements; - - this(Range sp) scope - { - slice = sp; - start = 0; - end = sp.length; - } - - this(Range sp, size_t s, size_t e) scope - { - slice = sp; - start = s; - end = e; - } - - @property auto front()const - { - immutable a = slice[start]; - immutable b = slice[start+1]; - return CodepointInterval(a, b); - } - - //may break sorted property - but we need std.sort to access it - //hence package(std) protection attribute - static if (hasAssignableElements!Range) - package(std) @property void front(CodepointInterval val) - { - slice[start] = val.a; - slice[start+1] = val.b; - } - - @property auto back()const - { - immutable a = slice[end-2]; - immutable b = slice[end-1]; - return CodepointInterval(a, b); - } - - //ditto about package - static if (hasAssignableElements!Range) - package(std) @property void back(CodepointInterval val) - { - slice[end-2] = val.a; - slice[end-1] = val.b; - } - - void popFront() - { - start += 2; - } - - void popBack() - { - end -= 2; - } - - auto opIndex(size_t idx) const - { - immutable a = slice[start+idx*2]; - immutable b = slice[start+idx*2+1]; - return CodepointInterval(a, b); - } - - //ditto about package - static if (hasAssignableElements!Range) - package(std) void opIndexAssign(CodepointInterval val, size_t idx) - { - slice[start+idx*2] = val.a; - slice[start+idx*2+1] = val.b; - } - - auto opSlice(size_t s, size_t e) - { - return Intervals(slice, s*2+start, e*2+start); - } - - @property size_t length()const { return slice.length/2; } - - @property bool empty()const { return start == end; } - - @property auto save(){ return this; } - private: - size_t start, end; - Range slice; - } - - // called after construction from intervals - // to make sure invariants hold - void sanitize() - { - import std.algorithm.comparison : max; - import std.algorithm.mutation : SwapStrategy; - import std.algorithm.sorting : sort; - if (data.length == 0) - return; - alias Ival = CodepointInterval; - //intervals wrapper for a _range_ over packed array - auto ivals = Intervals!(typeof(data[]))(data[]); - //@@@BUG@@@ can't use "a.a < b.a" see - // https://issues.dlang.org/show_bug.cgi?id=12265 - sort!((a,b) => a.a < b.a, SwapStrategy.stable)(ivals); - // what follows is a variation on stable remove - // differences: - // - predicate is binary, and is tested against - // the last kept element (at 'i'). - // - predicate mutates lhs (merges rhs into lhs) - size_t len = ivals.length; - size_t i = 0; - size_t j = 1; - while (j < len) - { - if (ivals[i].b >= ivals[j].a) - { - ivals[i] = Ival(ivals[i].a, max(ivals[i].b, ivals[j].b)); - j++; - } - else //unmergable - { - // check if there is a hole after merges - // (in the best case we do 0 writes to ivals) - if (j != i+1) - ivals[i+1] = ivals[j]; //copy over - i++; - j++; - } - } - len = i + 1; - for (size_t k=0; k + 1 < len; k++) - { - assert(ivals[k].a < ivals[k].b); - assert(ivals[k].b < ivals[k+1].a); - } - data.length = len * 2; - } - - // special case for normal InversionList - ref subChar(dchar ch) - { - auto mark = skipUpTo(ch); - if (mark != data.length - && data[mark] == ch && data[mark-1] == ch) - { - // it has split, meaning that ch happens to be in one of intervals - data[mark] = data[mark]+1; - } - return this; - } - - // - Marker addInterval(int a, int b, Marker hint=Marker.init) scope - in - { - assert(a <= b); - } - do - { - import std.range : assumeSorted, SearchPolicy; - auto range = assumeSorted(data[]); - size_t pos; - size_t a_idx = hint + range[hint..$].lowerBound!(SearchPolicy.gallop)(a).length; - if (a_idx == range.length) - { - // [---+++----++++----++++++] - // [ a b] - data.append(a, b); - return data.length-1; - } - size_t b_idx = range[a_idx .. range.length].lowerBound!(SearchPolicy.gallop)(b).length+a_idx; - uint[3] buf = void; - uint to_insert; - debug(std_uni) - { - writefln("a_idx=%d; b_idx=%d;", a_idx, b_idx); - } - if (b_idx == range.length) - { - // [-------++++++++----++++++-] - // [ s a b] - if (a_idx & 1)// a in positive - { - buf[0] = b; - to_insert = 1; - } - else// a in negative - { - buf[0] = a; - buf[1] = b; - to_insert = 2; - } - pos = genericReplace(data, a_idx, b_idx, buf[0 .. to_insert]); - return pos - 1; - } - - uint top = data[b_idx]; - - debug(std_uni) - { - writefln("a_idx=%d; b_idx=%d;", a_idx, b_idx); - writefln("a=%s; b=%s; top=%s;", a, b, top); - } - if (a_idx & 1) - {// a in positive - if (b_idx & 1)// b in positive - { - // [-------++++++++----++++++-] - // [ s a b ] - buf[0] = top; - to_insert = 1; - } - else // b in negative - { - // [-------++++++++----++++++-] - // [ s a b ] - if (top == b) - { - assert(b_idx+1 < data.length); - buf[0] = data[b_idx+1]; - pos = genericReplace(data, a_idx, b_idx+2, buf[0 .. 1]); - return pos - 1; - } - buf[0] = b; - buf[1] = top; - to_insert = 2; - } - } - else - { // a in negative - if (b_idx & 1) // b in positive - { - // [----------+++++----++++++-] - // [ a b ] - buf[0] = a; - buf[1] = top; - to_insert = 2; - } - else// b in negative - { - // [----------+++++----++++++-] - // [ a s b ] - if (top == b) - { - assert(b_idx+1 < data.length); - buf[0] = a; - buf[1] = data[b_idx+1]; - pos = genericReplace(data, a_idx, b_idx+2, buf[0 .. 2]); - return pos - 1; - } - buf[0] = a; - buf[1] = b; - buf[2] = top; - to_insert = 3; - } - } - pos = genericReplace(data, a_idx, b_idx+1, buf[0 .. to_insert]); - debug(std_uni) - { - writefln("marker idx: %d; length=%d", pos, data[pos], data.length); - writeln("inserting ", buf[0 .. to_insert]); - } - return pos - 1; - } - - // - Marker dropUpTo(uint a, Marker pos=Marker.init) - in - { - assert(pos % 2 == 0); // at start of interval - } - do - { - auto range = assumeSorted!"a <= b"(data[pos .. data.length]); - if (range.empty) - return pos; - size_t idx = pos; - idx += range.lowerBound(a).length; - - debug(std_uni) - { - writeln("dropUpTo full length=", data.length); - writeln(pos,"~~~", idx); - } - if (idx == data.length) - return genericReplace(data, pos, idx, cast(uint[])[]); - if (idx & 1) - { // a in positive - //[--+++----++++++----+++++++------...] - // |<---si s a t - genericReplace(data, pos, idx, [a]); - } - else - { // a in negative - //[--+++----++++++----+++++++-------+++...] - // |<---si s a t - genericReplace(data, pos, idx, cast(uint[])[]); - } - return pos; - } - - // - Marker skipUpTo(uint a, Marker pos=Marker.init) - out(result) - { - assert(result % 2 == 0);// always start of interval - //(may be 0-width after-split) - } - do - { - assert(data.length % 2 == 0); - auto range = assumeSorted!"a <= b"(data[pos .. data.length]); - size_t idx = pos+range.lowerBound(a).length; - - if (idx >= data.length) // could have Marker point to recently removed stuff - return data.length; - - if (idx & 1)// inside of interval, check for split - { - - immutable top = data[idx]; - if (top == a)// no need to split, it's end - return idx+1; - immutable start = data[idx-1]; - if (a == start) - return idx-1; - // split it up - genericReplace(data, idx, idx+1, [a, a, top]); - return idx+1; // avoid odd index - } - return idx; - } - - CowArray!SP data; -} - -pure @safe unittest -{ - import std.conv : to; - assert(unicode.ASCII.to!string() == "[0..128)"); -} - -// pedantic version for ctfe, and aligned-access only architectures -@system private uint safeRead24(scope const ubyte* ptr, size_t idx) pure nothrow @nogc -{ - idx *= 3; - version (LittleEndian) - return ptr[idx] + (cast(uint) ptr[idx+1]<<8) - + (cast(uint) ptr[idx+2]<<16); - else - return (cast(uint) ptr[idx]<<16) + (cast(uint) ptr[idx+1]<<8) - + ptr[idx+2]; -} - -// ditto -@system private void safeWrite24(scope ubyte* ptr, uint val, size_t idx) pure nothrow @nogc -{ - idx *= 3; - version (LittleEndian) - { - ptr[idx] = val & 0xFF; - ptr[idx+1] = (val >> 8) & 0xFF; - ptr[idx+2] = (val >> 16) & 0xFF; - } - else - { - ptr[idx] = (val >> 16) & 0xFF; - ptr[idx+1] = (val >> 8) & 0xFF; - ptr[idx+2] = val & 0xFF; - } -} - -// unaligned x86-like read/write functions -@system private uint unalignedRead24(scope const ubyte* ptr, size_t idx) pure nothrow @nogc -{ - uint* src = cast(uint*)(ptr+3*idx); - version (LittleEndian) - return *src & 0xFF_FFFF; - else - return *src >> 8; -} - -// ditto -@system private void unalignedWrite24(scope ubyte* ptr, uint val, size_t idx) pure nothrow @nogc -{ - uint* dest = cast(uint*)(cast(ubyte*) ptr + 3*idx); - version (LittleEndian) - *dest = val | (*dest & 0xFF00_0000); - else - *dest = (val << 8) | (*dest & 0xFF); -} - -@system private uint read24(scope const ubyte* ptr, size_t idx) pure nothrow @nogc -{ - static if (hasUnalignedReads) - return __ctfe ? safeRead24(ptr, idx) : unalignedRead24(ptr, idx); - else - return safeRead24(ptr, idx); -} - -@system private void write24(scope ubyte* ptr, uint val, size_t idx) pure nothrow @nogc -{ - static if (hasUnalignedReads) - return __ctfe ? safeWrite24(ptr, val, idx) : unalignedWrite24(ptr, val, idx); - else - return safeWrite24(ptr, val, idx); -} - -struct CowArray(SP=GcPolicy) -{ - import std.range.primitives : hasLength; - - @safe: - static auto reuse(uint[] arr) - { - CowArray cow; - cow.data = arr; - SP.append(cow.data, 1); - assert(cow.refCount == 1); - assert(cow.length == arr.length); - return cow; - } - - this(Range)(Range range) - if (isInputRange!Range && hasLength!Range) - { - import std.algorithm.mutation : copy; - length = range.length; - copy(range, data[0..$-1]); - } - - this(Range)(Range range) - if (isForwardRange!Range && !hasLength!Range) - { - import std.algorithm.mutation : copy; - import std.range.primitives : walkLength; - immutable len = walkLength(range.save); - length = len; - copy(range, data[0..$-1]); - } - - this(this) - { - if (!empty) - { - refCount = refCount + 1; - } - } - - ~this() - { - if (!empty) - { - immutable cnt = refCount; - if (cnt == 1) - SP.destroy(data); - else - refCount = cnt - 1; - } - } - - // no ref-count for empty U24 array - @property bool empty() const { return data.length == 0; } - - // report one less then actual size - @property size_t length() const - { - return data.length ? data.length - 1 : 0; - } - - //+ an extra slot for ref-count - @property void length(size_t len) - { - import std.algorithm.comparison : min; - import std.algorithm.mutation : copy; - if (len == 0) - { - if (!empty) - freeThisReference(); - return; - } - immutable total = len + 1; // including ref-count - if (empty) - { - data = SP.alloc!uint(total); - refCount = 1; - return; - } - immutable cur_cnt = refCount; - if (cur_cnt != 1) // have more references to this memory - { - refCount = cur_cnt - 1; - auto new_data = SP.alloc!uint(total); - // take shrinking into account - auto to_copy = min(total, data.length) - 1; - copy(data[0 .. to_copy], new_data[0 .. to_copy]); - data = new_data; // before setting refCount! - refCount = 1; - } - else // 'this' is the only reference - { - // use the realloc (hopefully in-place operation) - data = SP.realloc(data, total); - refCount = 1; // setup a ref-count in the new end of the array - } - } - - alias opDollar = length; - - uint opIndex()(size_t idx)const - { - return data[idx]; - } - - void opIndexAssign(uint val, size_t idx) - { - auto cnt = refCount; - if (cnt != 1) - dupThisReference(cnt); - data[idx] = val; - } - - // - auto opSlice(size_t from, size_t to) - { - if (!empty) - { - auto cnt = refCount; - if (cnt != 1) - dupThisReference(cnt); - } - return data[from .. to]; - - } - - // - auto opSlice(size_t from, size_t to) const - { - return data[from .. to]; - } - - // length slices before the ref count - auto opSlice() - { - return opSlice(0, length); - } - - // ditto - auto opSlice() const - { - return opSlice(0, length); - } - - void append(Range)(Range range) - if (isInputRange!Range && hasLength!Range && is(ElementType!Range : uint)) - { - size_t nl = length + range.length; - length = nl; - copy(range, this[nl-range.length .. nl]); - } - - void append()(uint[] val...) - { - length = length + val.length; - data[$-val.length-1 .. $-1] = val[]; - } - - bool opEquals()(auto const ref CowArray rhs)const - { - if (empty ^ rhs.empty) - return false; // one is empty and the other isn't - return empty || data[0..$-1] == rhs.data[0..$-1]; - } - -private: - // ref-count is right after the data - @property uint refCount() const - { - return data[$-1]; - } - - @property void refCount(uint cnt) - { - data[$-1] = cnt; - } - - void freeThisReference() - { - immutable count = refCount; - if (count != 1) // have more references to this memory - { - // dec shared ref-count - refCount = count - 1; - data = []; - } - else - SP.destroy(data); - assert(!data.ptr); - } - - void dupThisReference(uint count) - in - { - assert(!empty && count != 1 && count == refCount); - } - do - { - import std.algorithm.mutation : copy; - // dec shared ref-count - refCount = count - 1; - // copy to the new chunk of RAM - auto new_data = SP.alloc!uint(data.length); - // bit-blit old stuff except the counter - copy(data[0..$-1], new_data[0..$-1]); - data = new_data; // before setting refCount! - refCount = 1; // so that this updates the right one - } - - uint[] data; -} - -pure @safe unittest// Uint24 tests -{ - import std.algorithm.comparison : equal; - import std.algorithm.mutation : copy; - import std.conv : text; - import std.range : iota, chain; - import std.range.primitives : isBidirectionalRange, isOutputRange; - void funcRef(T)(ref T u24) - { - u24.length = 2; - u24[1] = 1024; - T u24_c = u24; - assert(u24[1] == 1024); - u24.length = 0; - assert(u24.empty); - u24.append([1, 2]); - assert(equal(u24[], [1, 2])); - u24.append(111); - assert(equal(u24[], [1, 2, 111])); - assert(!u24_c.empty && u24_c[1] == 1024); - u24.length = 3; - copy(iota(0, 3), u24[]); - assert(equal(u24[], iota(0, 3))); - assert(u24_c[1] == 1024); - } - - void func2(T)(T u24) - { - T u24_2 = u24; - T u24_3; - u24_3 = u24_2; - assert(u24_2 == u24_3); - assert(equal(u24[], u24_2[])); - assert(equal(u24_2[], u24_3[])); - funcRef(u24_3); - - assert(equal(u24_3[], iota(0, 3))); - assert(!equal(u24_2[], u24_3[])); - assert(equal(u24_2[], u24[])); - u24_2 = u24_3; - assert(equal(u24_2[], iota(0, 3))); - // to test that passed arg is intact outside - // plus try out opEquals - u24 = u24_3; - u24 = T.init; - u24_3 = T.init; - assert(u24.empty); - assert(u24 == u24_3); - assert(u24 != u24_2); - } - - static foreach (Policy; AliasSeq!(GcPolicy, ReallocPolicy)) - {{ - alias Range = typeof(CowArray!Policy.init[]); - alias U24A = CowArray!Policy; - static assert(isForwardRange!Range); - static assert(isBidirectionalRange!Range); - static assert(isOutputRange!(Range, uint)); - static assert(isRandomAccessRange!(Range)); - - auto arr = U24A([42u, 36, 100]); - assert(arr[0] == 42); - assert(arr[1] == 36); - arr[0] = 72; - arr[1] = 0xFE_FEFE; - assert(arr[0] == 72); - assert(arr[1] == 0xFE_FEFE); - assert(arr[2] == 100); - U24A arr2 = arr; - assert(arr2[0] == 72); - arr2[0] = 11; - // test COW-ness - assert(arr[0] == 72); - assert(arr2[0] == 11); - // set this to about 100M to stress-test COW memory management - foreach (v; 0 .. 10_000) - func2(arr); - assert(equal(arr[], [72, 0xFE_FEFE, 100])); - - auto r2 = U24A(iota(0, 100)); - assert(equal(r2[], iota(0, 100)), text(r2[])); - copy(iota(10, 170, 2), r2[10 .. 90]); - assert(equal(r2[], chain(iota(0, 10), iota(10, 170, 2), iota(90, 100))) - , text(r2[])); - }} -} - -pure @safe unittest// core set primitives test -{ - import std.conv : text; - alias AllSets = AliasSeq!(InversionList!GcPolicy, InversionList!ReallocPolicy); - foreach (CodeList; AllSets) - { - CodeList a; - //"plug a hole" test - a.add(10, 20).add(25, 30).add(15, 27); - assert(a == CodeList(10, 30), text(a)); - - auto x = CodeList.init; - x.add(10, 20).add(30, 40).add(50, 60); - - a = x; - a.add(20, 49);//[10, 49) [50, 60) - assert(a == CodeList(10, 49, 50 ,60)); - - a = x; - a.add(20, 50); - assert(a == CodeList(10, 60), text(a)); - - // simple unions, mostly edge effects - x = CodeList.init; - x.add(10, 20).add(40, 60); - - a = x; - a.add(10, 25); //[10, 25) [40, 60) - assert(a == CodeList(10, 25, 40, 60)); - - a = x; - a.add(5, 15); //[5, 20) [40, 60) - assert(a == CodeList(5, 20, 40, 60)); - - a = x; - a.add(0, 10); // [0, 20) [40, 60) - assert(a == CodeList(0, 20, 40, 60)); - - a = x; - a.add(0, 5); // prepand - assert(a == CodeList(0, 5, 10, 20, 40, 60), text(a)); - - a = x; - a.add(5, 20); - assert(a == CodeList(5, 20, 40, 60)); - - a = x; - a.add(3, 37); - assert(a == CodeList(3, 37, 40, 60)); - - a = x; - a.add(37, 65); - assert(a == CodeList(10, 20, 37, 65)); - - // some tests on helpers for set intersection - x = CodeList.init.add(10, 20).add(40, 60).add(100, 120); - a = x; - - auto m = a.skipUpTo(60); - a.dropUpTo(110, m); - assert(a == CodeList(10, 20, 40, 60, 110, 120), text(a.data[])); - - a = x; - a.dropUpTo(100); - assert(a == CodeList(100, 120), text(a.data[])); - - a = x; - m = a.skipUpTo(50); - a.dropUpTo(140, m); - assert(a == CodeList(10, 20, 40, 50), text(a.data[])); - a = x; - a.dropUpTo(60); - assert(a == CodeList(100, 120), text(a.data[])); - } -} - - -//test constructor to work with any order of intervals -pure @safe unittest -{ - import std.algorithm.comparison : equal; - import std.conv : text, to; - import std.range : chain, iota; - import std.typecons : tuple; - //ensure constructor handles bad ordering and overlap - auto c1 = CodepointSet('Đ°', 'я'+1, 'А','ĐŻ'+1); - foreach (ch; chain(iota('Đ°', 'я'+1), iota('А','ĐŻ'+1))) - assert(ch in c1, to!string(ch)); - - //contiguos - assert(CodepointSet(1000, 1006, 1006, 1009) - .byInterval.equal([tuple(1000, 1009)])); - //contains - assert(CodepointSet(900, 1200, 1000, 1100) - .byInterval.equal([tuple(900, 1200)])); - //intersect left - assert(CodepointSet(900, 1100, 1000, 1200) - .byInterval.equal([tuple(900, 1200)])); - //intersect right - assert(CodepointSet(1000, 1200, 900, 1100) - .byInterval.equal([tuple(900, 1200)])); - - //ditto with extra items at end - assert(CodepointSet(1000, 1200, 900, 1100, 800, 850) - .byInterval.equal([tuple(800, 850), tuple(900, 1200)])); - assert(CodepointSet(900, 1100, 1000, 1200, 800, 850) - .byInterval.equal([tuple(800, 850), tuple(900, 1200)])); - - //"plug a hole" test - auto c2 = CodepointSet(20, 40, - 60, 80, 100, 140, 150, 200, - 40, 60, 80, 100, 140, 150 - ); - assert(c2.byInterval.equal([tuple(20, 200)])); - - auto c3 = CodepointSet( - 20, 40, 60, 80, 100, 140, 150, 200, - 0, 10, 15, 100, 10, 20, 200, 220); - assert(c3.byInterval.equal([tuple(0, 140), tuple(150, 220)])); -} - - -pure @safe unittest -{ // full set operations - import std.conv : text; - alias AllSets = AliasSeq!(InversionList!GcPolicy, InversionList!ReallocPolicy); - foreach (CodeList; AllSets) - { - CodeList a, b, c, d; - - //"plug a hole" - a.add(20, 40).add(60, 80).add(100, 140).add(150, 200); - b.add(40, 60).add(80, 100).add(140, 150); - c = a | b; - d = b | a; - assert(c == CodeList(20, 200), text(CodeList.stringof," ", c)); - assert(c == d, text(c," vs ", d)); - - b = CodeList.init.add(25, 45).add(65, 85).add(95,110).add(150, 210); - c = a | b; //[20,45) [60, 85) [95, 140) [150, 210) - d = b | a; - assert(c == CodeList(20, 45, 60, 85, 95, 140, 150, 210), text(c)); - assert(c == d, text(c," vs ", d)); - - b = CodeList.init.add(10, 20).add(30,100).add(145,200); - c = a | b;//[10, 140) [145, 200) - d = b | a; - assert(c == CodeList(10, 140, 145, 200)); - assert(c == d, text(c," vs ", d)); - - b = CodeList.init.add(0, 10).add(15, 100).add(10, 20).add(200, 220); - c = a | b;//[0, 140) [150, 220) - d = b | a; - assert(c == CodeList(0, 140, 150, 220)); - assert(c == d, text(c," vs ", d)); - - - a = CodeList.init.add(20, 40).add(60, 80); - b = CodeList.init.add(25, 35).add(65, 75); - c = a & b; - d = b & a; - assert(c == CodeList(25, 35, 65, 75), text(c)); - assert(c == d, text(c," vs ", d)); - - a = CodeList.init.add(20, 40).add(60, 80).add(100, 140).add(150, 200); - b = CodeList.init.add(25, 35).add(65, 75).add(110, 130).add(160, 180); - c = a & b; - d = b & a; - assert(c == CodeList(25, 35, 65, 75, 110, 130, 160, 180), text(c)); - assert(c == d, text(c," vs ", d)); - - a = CodeList.init.add(20, 40).add(60, 80).add(100, 140).add(150, 200); - b = CodeList.init.add(10, 30).add(60, 120).add(135, 160); - c = a & b;//[20, 30)[60, 80) [100, 120) [135, 140) [150, 160) - d = b & a; - - assert(c == CodeList(20, 30, 60, 80, 100, 120, 135, 140, 150, 160),text(c)); - assert(c == d, text(c, " vs ",d)); - assert((c & a) == c); - assert((d & b) == d); - assert((c & d) == d); - - b = CodeList.init.add(40, 60).add(80, 100).add(140, 200); - c = a & b; - d = b & a; - assert(c == CodeList(150, 200), text(c)); - assert(c == d, text(c, " vs ",d)); - assert((c & a) == c); - assert((d & b) == d); - assert((c & d) == d); - - assert((a & a) == a); - assert((b & b) == b); - - a = CodeList.init.add(20, 40).add(60, 80).add(100, 140).add(150, 200); - b = CodeList.init.add(30, 60).add(75, 120).add(190, 300); - c = a - b;// [30, 40) [60, 75) [120, 140) [150, 190) - d = b - a;// [40, 60) [80, 100) [200, 300) - assert(c == CodeList(20, 30, 60, 75, 120, 140, 150, 190), text(c)); - assert(d == CodeList(40, 60, 80, 100, 200, 300), text(d)); - assert(c - d == c, text(c-d, " vs ", c)); - assert(d - c == d, text(d-c, " vs ", d)); - assert(c - c == CodeList.init); - assert(d - d == CodeList.init); - - a = CodeList.init.add(20, 40).add( 60, 80).add(100, 140).add(150, 200); - b = CodeList.init.add(10, 50).add(60, 160).add(190, 300); - c = a - b;// [160, 190) - d = b - a;// [10, 20) [40, 50) [80, 100) [140, 150) [200, 300) - assert(c == CodeList(160, 190), text(c)); - assert(d == CodeList(10, 20, 40, 50, 80, 100, 140, 150, 200, 300), text(d)); - assert(c - d == c, text(c-d, " vs ", c)); - assert(d - c == d, text(d-c, " vs ", d)); - assert(c - c == CodeList.init); - assert(d - d == CodeList.init); - - a = CodeList.init.add(20, 40).add(60, 80).add(100, 140).add(150, 200); - b = CodeList.init.add(10, 30).add(45, 100).add(130, 190); - c = a ~ b; // [10, 20) [30, 40) [45, 60) [80, 130) [140, 150) [190, 200) - d = b ~ a; - assert(c == CodeList(10, 20, 30, 40, 45, 60, 80, 130, 140, 150, 190, 200), - text(c)); - assert(c == d, text(c, " vs ", d)); - } -} - -} - -pure @safe unittest// vs single dchar -{ - import std.conv : text; - CodepointSet a = CodepointSet(10, 100, 120, 200); - assert(a - 'A' == CodepointSet(10, 65, 66, 100, 120, 200), text(a - 'A')); - assert((a & 'B') == CodepointSet(66, 67)); -} - -pure @safe unittest// iteration & opIndex -{ - import std.algorithm.comparison : equal; - import std.conv : text; - import std.typecons : tuple, Tuple; - - static foreach (CodeList; AliasSeq!(InversionList!(ReallocPolicy))) - {{ - auto arr = "ABCDEFGHIJKLMabcdefghijklm"d; - auto a = CodeList('A','N','a', 'n'); - assert(equal(a.byInterval, - [tuple(cast(uint)'A', cast(uint)'N'), tuple(cast(uint)'a', cast(uint)'n')] - ), text(a.byInterval)); - - // same @@@BUG as in https://issues.dlang.org/show_bug.cgi?id=8949 ? - version (bug8949) - { - import std.range : retro; - assert(equal(retro(a.byInterval), - [tuple(cast(uint)'a', cast(uint)'n'), tuple(cast(uint)'A', cast(uint)'N')] - ), text(retro(a.byInterval))); - } - auto achr = a.byCodepoint; - assert(equal(achr, arr), text(a.byCodepoint)); - foreach (ch; a.byCodepoint) - assert(a[ch]); - auto x = CodeList(100, 500, 600, 900, 1200, 1500); - assert(equal(x.byInterval, [ tuple(100, 500), tuple(600, 900), tuple(1200, 1500)]), text(x.byInterval)); - foreach (ch; x.byCodepoint) - assert(x[ch]); - static if (is(CodeList == CodepointSet)) - { - auto y = CodeList(x.byInterval); - assert(equal(x.byInterval, y.byInterval)); - } - assert(equal(CodepointSet.init.byInterval, cast(Tuple!(uint, uint)[])[])); - assert(equal(CodepointSet.init.byCodepoint, cast(dchar[])[])); - }} -} - -//============================================================================ -// Generic Trie template and various ways to build it -//============================================================================ - -// debug helper to get a shortened array dump -auto arrayRepr(T)(T x) -{ - import std.conv : text; - if (x.length > 32) - { - return text(x[0 .. 16],"~...~", x[x.length-16 .. x.length]); - } - else - return text(x); -} - -/** - Maps `Key` to a suitable integer index within the range of `size_t`. - The mapping is constructed by applying predicates from `Prefix` left to right - and concatenating the resulting bits. - - The first (leftmost) predicate defines the most significant bits of - the resulting index. - */ -template mapTrieIndex(Prefix...) -{ - size_t mapTrieIndex(Key)(Key key) - if (isValidPrefixForTrie!(Key, Prefix)) - { - alias p = Prefix; - size_t idx; - foreach (i, v; p[0..$-1]) - { - idx |= p[i](key); - idx <<= p[i+1].bitSize; - } - idx |= p[$-1](key); - return idx; - } -} - -/* - `TrieBuilder` is a type used for incremental construction - of $(LREF Trie)s. - - See $(LREF buildTrie) for generic helpers built on top of it. -*/ -@trusted private struct TrieBuilder(Value, Key, Args...) -if (isBitPackableType!Value && isValidArgsForTrie!(Key, Args)) -{ - import std.exception : enforce; - -private: - // last index is not stored in table, it is used as an offset to values in a block. - static if (is(Value == bool))// always pack bool - alias V = BitPacked!(Value, 1); - else - alias V = Value; - static auto deduceMaxIndex(Preds...)() - { - size_t idx = 1; - foreach (v; Preds) - idx *= 2^^v.bitSize; - return idx; - } - - static if (is(typeof(Args[0]) : Key)) // Args start with upper bound on Key - { - alias Prefix = Args[1..$]; - enum lastPageSize = 2^^Prefix[$-1].bitSize; - enum translatedMaxIndex = mapTrieIndex!(Prefix)(Args[0]); - enum roughedMaxIndex = - (translatedMaxIndex + lastPageSize-1)/lastPageSize*lastPageSize; - // check warp around - if wrapped, use the default deduction rule - enum maxIndex = roughedMaxIndex < translatedMaxIndex ? - deduceMaxIndex!(Prefix)() : roughedMaxIndex; - } - else - { - alias Prefix = Args; - enum maxIndex = deduceMaxIndex!(Prefix)(); - } - - alias getIndex = mapTrieIndex!(Prefix); - - enum lastLevel = Prefix.length-1; - struct ConstructState - { - size_t idx_zeros, idx_ones; - } - // iteration over levels of Trie, each indexes its own level and thus a shortened domain - size_t[Prefix.length] indices; - // default filler value to use - Value defValue; - // this is a full-width index of next item - size_t curIndex; - // all-zeros page index, all-ones page index (+ indicator if there is such a page) - ConstructState[Prefix.length] state; - // the table being constructed - MultiArray!(idxTypes!(Key, fullBitSize!(Prefix), Prefix[0..$]), V) table; - - @disable this(); - - //shortcut for index variable at level 'level' - @property ref idx(size_t level)(){ return indices[level]; } - - // this function assumes no holes in the input so - // indices are going one by one - void addValue(size_t level, T)(T val, size_t numVals) - { - alias j = idx!level; - enum pageSize = 1 << Prefix[level].bitSize; - if (numVals == 0) - return; - auto ptr = table.slice!(level); - if (numVals == 1) - { - static if (level == Prefix.length-1) - ptr[j] = val; - else - {// can incur narrowing conversion - assert(j < ptr.length); - ptr[j] = force!(typeof(ptr[j]))(val); - } - j++; - if (j % pageSize == 0) - spillToNextPage!level(ptr); - return; - } - // longer row of values - // get to the next page boundary - immutable nextPB = (j + pageSize) & ~(pageSize-1); - immutable n = nextPB - j;// can fill right in this page - if (numVals < n) //fits in current page - { - ptr[j .. j+numVals] = val; - j += numVals; - return; - } - static if (level != 0)//on the first level it always fits - { - numVals -= n; - //write till the end of current page - ptr[j .. j+n] = val; - j += n; - //spill to the next page - spillToNextPage!level(ptr); - // page at once loop - if (state[level].idx_zeros != size_t.max && val == T.init) - { - alias NextIdx = typeof(table.slice!(level-1)[0]); - addValue!(level-1)(force!NextIdx(state[level].idx_zeros), - numVals/pageSize); - ptr = table.slice!level; //table structure might have changed - numVals %= pageSize; - } - else - { - while (numVals >= pageSize) - { - numVals -= pageSize; - ptr[j .. j+pageSize] = val; - j += pageSize; - spillToNextPage!level(ptr); - } - } - if (numVals) - { - // the leftovers, an incomplete page - ptr[j .. j+numVals] = val; - j += numVals; - } - } - } - - void spillToNextPage(size_t level, Slice)(ref Slice ptr) - { - // last level (i.e. topmost) has 1 "page" - // thus it need not to add a new page on upper level - static if (level != 0) - spillToNextPageImpl!(level)(ptr); - } - - // this can re-use the current page if duplicate or allocate a new one - // it also makes sure that previous levels point to the correct page in this level - void spillToNextPageImpl(size_t level, Slice)(ref Slice ptr) - { - alias NextIdx = typeof(table.slice!(level-1)[0]); - NextIdx next_lvl_index; - enum pageSize = 1 << Prefix[level].bitSize; - assert(idx!level % pageSize == 0); - immutable last = idx!level-pageSize; - const slice = ptr[idx!level - pageSize .. idx!level]; - size_t j; - for (j=0; j<last; j+=pageSize) - { - if (ptr[j .. j+pageSize] == slice) - { - // get index to it, reuse ptr space for the next block - next_lvl_index = force!NextIdx(j/pageSize); - version (none) - { - import std.stdio : writefln, writeln; - writefln("LEVEL(%s) page mapped idx: %s: 0..%s ---> [%s..%s]" - ,level - ,indices[level-1], pageSize, j, j+pageSize); - writeln("LEVEL(", level - , ") mapped page is: ", slice, ": ", arrayRepr(ptr[j .. j+pageSize])); - writeln("LEVEL(", level - , ") src page is :", ptr, ": ", arrayRepr(slice[0 .. pageSize])); - } - idx!level -= pageSize; // reuse this page, it is duplicate - break; - } - } - if (j == last) - { - L_allocate_page: - next_lvl_index = force!NextIdx(idx!level/pageSize - 1); - if (state[level].idx_zeros == size_t.max && ptr.zeros(j, j+pageSize)) - { - state[level].idx_zeros = next_lvl_index; - } - // allocate next page - version (none) - { - import std.stdio : writefln; - writefln("LEVEL(%s) page allocated: %s" - , level, arrayRepr(slice[0 .. pageSize])); - writefln("LEVEL(%s) index: %s ; page at this index %s" - , level - , next_lvl_index - , arrayRepr( - table.slice!(level) - [pageSize*next_lvl_index..(next_lvl_index+1)*pageSize] - )); - } - table.length!level = table.length!level + pageSize; - } - L_know_index: - // for the previous level, values are indices to the pages in the current level - addValue!(level-1)(next_lvl_index, 1); - ptr = table.slice!level; //re-load the slice after moves - } - - // idx - full-width index to fill with v (full-width index != key) - // fills everything in the range of [curIndex, idx) with filler - void putAt(size_t idx, Value v) - { - assert(idx >= curIndex); - immutable numFillers = idx - curIndex; - addValue!lastLevel(defValue, numFillers); - addValue!lastLevel(v, 1); - curIndex = idx + 1; - } - - // ditto, but sets the range of [idxA, idxB) to v - void putRangeAt(size_t idxA, size_t idxB, Value v) - { - assert(idxA >= curIndex); - assert(idxB >= idxA); - size_t numFillers = idxA - curIndex; - addValue!lastLevel(defValue, numFillers); - addValue!lastLevel(v, idxB - idxA); - curIndex = idxB; // open-right - } - - enum errMsg = "non-monotonic prefix function(s), an unsorted range or "~ - "duplicate key->value mapping"; - -public: - /** - Construct a builder, where `filler` is a value - to indicate empty slots (or "not found" condition). - */ - this(Value filler) - { - curIndex = 0; - defValue = filler; - // zeros-page index, ones-page index - foreach (ref v; state) - v = ConstructState(size_t.max, size_t.max); - table = typeof(table)(indices); - // one page per level is a bootstrap minimum - foreach (i, Pred; Prefix) - table.length!i = (1 << Pred.bitSize); - } - - /** - Put a value `v` into interval as - mapped by keys from `a` to `b`. - All slots prior to `a` are filled with - the default filler. - */ - void putRange(Key a, Key b, Value v) - { - auto idxA = getIndex(a), idxB = getIndex(b); - // indexes of key should always grow - enforce(idxB >= idxA && idxA >= curIndex, errMsg); - putRangeAt(idxA, idxB, v); - } - - /** - Put a value `v` into slot mapped by `key`. - All slots prior to `key` are filled with the - default filler. - */ - void putValue(Key key, Value v) - { - auto idx = getIndex(key); - enforce(idx >= curIndex, errMsg); - putAt(idx, v); - } - - /// Finishes construction of Trie, yielding an immutable Trie instance. - auto build() - { - static if (maxIndex != 0) // doesn't cover full range of size_t - { - assert(curIndex <= maxIndex); - addValue!lastLevel(defValue, maxIndex - curIndex); - } - else - { - if (curIndex != 0 // couldn't wrap around - || (Prefix.length != 1 && indices[lastLevel] == 0)) // can be just empty - { - addValue!lastLevel(defValue, size_t.max - curIndex); - addValue!lastLevel(defValue, 1); - } - // else curIndex already completed the full range of size_t by wrapping around - } - return Trie!(V, Key, maxIndex, Prefix)(table); - } -} - -/** - $(P A generic Trie data-structure for a fixed number of stages. - The design goal is optimal speed with smallest footprint size. - ) - $(P It's intentionally read-only and doesn't provide constructors. - To construct one use a special builder, - see $(LREF TrieBuilder) and $(LREF buildTrie). - ) - -*/ -@trusted private struct Trie(Value, Key, Args...) -if (isValidPrefixForTrie!(Key, Args) - || (isValidPrefixForTrie!(Key, Args[1..$]) - && is(typeof(Args[0]) : size_t))) -{ - import std.range.primitives : isOutputRange; - static if (is(typeof(Args[0]) : size_t)) - { - private enum maxIndex = Args[0]; - private enum hasBoundsCheck = true; - private alias Prefix = Args[1..$]; - } - else - { - private enum hasBoundsCheck = false; - private alias Prefix = Args; - } - - private this()(typeof(_table) table) - { - _table = table; - } - - // only for constant Tries constructed from precompiled tables - private this()(const(size_t)[] offsets, const(size_t)[] sizes, - const(size_t)[] data) const - { - _table = typeof(_table)(offsets, sizes, data); - } - - /** - $(P Lookup the `key` in this `Trie`. ) - - $(P The lookup always succeeds if key fits the domain - provided during construction. The whole domain defined - is covered so instead of not found condition - the sentinel (filler) value could be used. ) - - $(P See $(LREF buildTrie), $(LREF TrieBuilder) for how to - define a domain of `Trie` keys and the sentinel value. ) - - Note: - Domain range-checking is only enabled in debug builds - and results in assertion failure. - */ - TypeOfBitPacked!Value opIndex()(Key key) const - { - static if (hasBoundsCheck) - assert(mapTrieIndex!Prefix(key) < maxIndex); - size_t idx; - alias p = Prefix; - idx = cast(size_t) p[0](key); - foreach (i, v; p[0..$-1]) - idx = cast(size_t)((_table.ptr!i[idx]<<p[i+1].bitSize) + p[i+1](key)); - return _table.ptr!(p.length-1)[idx]; - } - - /// - @property size_t bytes(size_t n=size_t.max)() const - { - return _table.bytes!n; - } - - /// - @property size_t pages(size_t n)() const - { - return (bytes!n+2^^(Prefix[n].bitSize-1)) - /2^^Prefix[n].bitSize; - } - - /// - void store(OutRange)(scope OutRange sink) const - if (isOutputRange!(OutRange, char)) - { - _table.store(sink); - } - -private: - MultiArray!(idxTypes!(Key, fullBitSize!(Prefix), Prefix[0..$]), Value) _table; -} - -// create a tuple of 'sliceBits' that slice the 'top' of bits into pieces of sizes 'sizes' -// left-to-right, the most significant bits first -template GetBitSlicing(size_t top, sizes...) -{ - static if (sizes.length > 0) - alias GetBitSlicing = - AliasSeq!(sliceBits!(top - sizes[0], top), - GetBitSlicing!(top - sizes[0], sizes[1..$])); - else - alias GetBitSlicing = AliasSeq!(); -} - -template callableWith(T) -{ - template callableWith(alias Pred) - { - static if (!is(typeof(Pred(T.init)))) - enum callableWith = false; - else - { - alias Result = typeof(Pred(T.init)); - enum callableWith = isBitPackableType!(TypeOfBitPacked!(Result)); - } - } -} - -/* - Check if `Prefix` is a valid set of predicates - for `Trie` template having `Key` as the type of keys. - This requires all predicates to be callable, take - single argument of type `Key` and return unsigned value. -*/ -template isValidPrefixForTrie(Key, Prefix...) -{ - import std.meta : allSatisfy; - enum isValidPrefixForTrie = allSatisfy!(callableWith!Key, Prefix); // TODO: tighten the screws -} - -/* - Check if `Args` is a set of maximum key value followed by valid predicates - for `Trie` template having `Key` as the type of keys. -*/ -template isValidArgsForTrie(Key, Args...) -{ - static if (Args.length > 1) - { - enum isValidArgsForTrie = isValidPrefixForTrie!(Key, Args) - || (isValidPrefixForTrie!(Key, Args[1..$]) && is(typeof(Args[0]) : Key)); - } - else - enum isValidArgsForTrie = isValidPrefixForTrie!Args; -} - -@property size_t sumOfIntegerTuple(ints...)() -{ - size_t count=0; - foreach (v; ints) - count += v; - return count; -} - -/** - A shorthand for creating a custom multi-level fixed Trie - from a `CodepointSet`. `sizes` are numbers of bits per level, - with the most significant bits used first. - - Note: The sum of `sizes` must be equal 21. - - See_Also: $(LREF toTrie), which is even simpler. - - Example: - --- - { - import std.stdio; - auto set = unicode("Number"); - auto trie = codepointSetTrie!(8, 5, 8)(set); - writeln("Input code points to test:"); - foreach (line; stdin.byLine) - { - int count=0; - foreach (dchar ch; line) - if (trie[ch])// is number - count++; - writefln("Contains %d number code points.", count); - } - } - --- -*/ -public template codepointSetTrie(sizes...) -if (sumOfIntegerTuple!sizes == 21) -{ - auto codepointSetTrie(Set)(Set set) - if (isCodepointSet!Set) - { - auto builder = TrieBuilder!(bool, dchar, lastDchar+1, GetBitSlicing!(21, sizes))(false); - foreach (ival; set.byInterval) - builder.putRange(ival[0], ival[1], true); - return builder.build(); - } -} - -/// Type of Trie generated by codepointSetTrie function. -public template CodepointSetTrie(sizes...) -if (sumOfIntegerTuple!sizes == 21) -{ - alias Prefix = GetBitSlicing!(21, sizes); - alias CodepointSetTrie = typeof(TrieBuilder!(bool, dchar, lastDchar+1, Prefix)(false).build()); -} - -/** - A slightly more general tool for building fixed `Trie` - for the Unicode data. - - Specifically unlike `codepointSetTrie` it's allows creating mappings - of `dchar` to an arbitrary type `T`. - - Note: Overload taking `CodepointSet`s will naturally convert - only to bool mapping `Trie`s. - - CodepointTrie is the type of Trie as generated by codepointTrie function. -*/ -public template codepointTrie(T, sizes...) -if (sumOfIntegerTuple!sizes == 21) -{ - alias Prefix = GetBitSlicing!(21, sizes); - - static if (is(TypeOfBitPacked!T == bool)) - { - auto codepointTrie(Set)(const scope Set set) - if (isCodepointSet!Set) - { - return codepointSetTrie(set); - } - } - - /// - auto codepointTrie()(T[dchar] map, T defValue=T.init) - { - return buildTrie!(T, dchar, Prefix)(map, defValue); - } - - // unsorted range of pairs - /// - auto codepointTrie(R)(R range, T defValue=T.init) - if (isInputRange!R - && is(typeof(ElementType!R.init[0]) : T) - && is(typeof(ElementType!R.init[1]) : dchar)) - { - // build from unsorted array of pairs - // TODO: expose index sorting functions for Trie - return buildTrie!(T, dchar, Prefix)(range, defValue, true); - } -} - -@system pure unittest -{ - import std.algorithm.comparison : max; - import std.algorithm.searching : count; - - // pick characters from the Greek script - auto set = unicode.Greek; - - // a user-defined property (or an expensive function) - // that we want to look up - static uint luckFactor(dchar ch) - { - // here we consider a character lucky - // if its code point has a lot of identical hex-digits - // e.g. arabic letter DDAL (\u0688) has a "luck factor" of 2 - ubyte[6] nibbles; // 6 4-bit chunks of code point - uint value = ch; - foreach (i; 0 .. 6) - { - nibbles[i] = value & 0xF; - value >>= 4; - } - uint luck; - foreach (n; nibbles) - luck = cast(uint) max(luck, count(nibbles[], n)); - return luck; - } - - // only unsigned built-ins are supported at the moment - alias LuckFactor = BitPacked!(uint, 3); - - // create a temporary associative array (AA) - LuckFactor[dchar] map; - foreach (ch; set.byCodepoint) - map[ch] = LuckFactor(luckFactor(ch)); - - // bits per stage are chosen randomly, fell free to optimize - auto trie = codepointTrie!(LuckFactor, 8, 5, 8)(map); - - // from now on the AA is not needed - foreach (ch; set.byCodepoint) - assert(trie[ch] == luckFactor(ch)); // verify - // CJK is not Greek, thus it has the default value - assert(trie['\u4444'] == 0); - // and here is a couple of quite lucky Greek characters: - // Greek small letter epsilon with dasia - assert(trie['\u1F11'] == 3); - // Ancient Greek metretes sign - assert(trie['\U00010181'] == 3); - -} - -/// ditto -public template CodepointTrie(T, sizes...) -if (sumOfIntegerTuple!sizes == 21) -{ - alias Prefix = GetBitSlicing!(21, sizes); - alias CodepointTrie = typeof(TrieBuilder!(T, dchar, lastDchar+1, Prefix)(T.init).build()); -} - -package(std) template cmpK0(alias Pred) -{ - import std.typecons : Tuple; - static bool cmpK0(Value, Key) - (Tuple!(Value, Key) a, Tuple!(Value, Key) b) - { - return Pred(a[1]) < Pred(b[1]); - } -} - -/** - The most general utility for construction of `Trie`s - short of using `TrieBuilder` directly. - - Provides a number of convenience overloads. - `Args` is tuple of maximum key value followed by - predicates to construct index from key. - - Alternatively if the first argument is not a value convertible to `Key` - then the whole tuple of `Args` is treated as predicates - and the maximum Key is deduced from predicates. -*/ -private template buildTrie(Value, Key, Args...) -if (isValidArgsForTrie!(Key, Args)) -{ - static if (is(typeof(Args[0]) : Key)) // prefix starts with upper bound on Key - { - alias Prefix = Args[1..$]; - } - else - alias Prefix = Args; - - alias getIndex = mapTrieIndex!(Prefix); - - // for multi-sort - template GetComparators(size_t n) - { - static if (n > 0) - alias GetComparators = - AliasSeq!(GetComparators!(n-1), cmpK0!(Prefix[n-1])); - else - alias GetComparators = AliasSeq!(); - } - - /* - Build `Trie` from a range of a Key-Value pairs, - assuming it is sorted by Key as defined by the following lambda: - ------ - (a, b) => mapTrieIndex!(Prefix)(a) < mapTrieIndex!(Prefix)(b) - ------ - Exception is thrown if it's detected that the above order doesn't hold. - - In other words $(LREF mapTrieIndex) should be a - monotonically increasing function that maps `Key` to an integer. - - See_Also: $(REF sort, std,_algorithm), - $(REF SortedRange, std,range), - $(REF setUnion, std,_algorithm). - */ - auto buildTrie(Range)(Range range, Value filler=Value.init) - if (isInputRange!Range && is(typeof(Range.init.front[0]) : Value) - && is(typeof(Range.init.front[1]) : Key)) - { - auto builder = TrieBuilder!(Value, Key, Prefix)(filler); - foreach (v; range) - builder.putValue(v[1], v[0]); - return builder.build(); - } - - /* - If `Value` is bool (or BitPacked!(bool, x)) then it's possible - to build `Trie` from a range of open-right intervals of `Key`s. - The requirement on the ordering of keys (and the behavior on the - violation of it) is the same as for Key-Value range overload. - - Intervals denote ranges of !`filler` i.e. the opposite of filler. - If no filler provided keys inside of the intervals map to true, - and `filler` is false. - */ - auto buildTrie(Range)(Range range, Value filler=Value.init) - if (is(TypeOfBitPacked!Value == bool) - && isInputRange!Range && is(typeof(Range.init.front[0]) : Key) - && is(typeof(Range.init.front[1]) : Key)) - { - auto builder = TrieBuilder!(Value, Key, Prefix)(filler); - foreach (ival; range) - builder.putRange(ival[0], ival[1], !filler); - return builder.build(); - } - - auto buildTrie(Range)(Range range, Value filler, bool unsorted) - if (isInputRange!Range - && is(typeof(Range.init.front[0]) : Value) - && is(typeof(Range.init.front[1]) : Key)) - { - import std.algorithm.sorting : multiSort; - alias Comps = GetComparators!(Prefix.length); - if (unsorted) - multiSort!(Comps)(range); - return buildTrie(range, filler); - } - - /* - If `Value` is bool (or BitPacked!(bool, x)) then it's possible - to build `Trie` simply from an input range of `Key`s. - The requirement on the ordering of keys (and the behavior on the - violation of it) is the same as for Key-Value range overload. - - Keys found in range denote !`filler` i.e. the opposite of filler. - If no filler provided keys map to true, and `filler` is false. - */ - auto buildTrie(Range)(Range range, Value filler=Value.init) - if (is(TypeOfBitPacked!Value == bool) - && isInputRange!Range && is(typeof(Range.init.front) : Key)) - { - auto builder = TrieBuilder!(Value, Key, Prefix)(filler); - foreach (v; range) - builder.putValue(v, !filler); - return builder.build(); - } - - /* - If `Key` is unsigned integer `Trie` could be constructed from array - of values where array index serves as key. - */ - auto buildTrie()(Value[] array, Value filler=Value.init) - if (isUnsigned!Key) - { - auto builder = TrieBuilder!(Value, Key, Prefix)(filler); - foreach (idx, v; array) - builder.putValue(idx, v); - return builder.build(); - } - - /* - Builds `Trie` from associative array. - */ - auto buildTrie(Key, Value)(Value[Key] map, Value filler=Value.init) - { - import std.array : array; - import std.range : zip; - auto range = array(zip(map.values, map.keys)); - return buildTrie(range, filler, true); // sort it - } -} - -// helper in place of assumeSize to -//reduce mangled name & help DMD inline Trie functors -struct clamp(size_t bits) -{ - static size_t opCall(T)(T arg){ return arg; } - enum bitSize = bits; -} - -struct clampIdx(size_t idx, size_t bits) -{ - static size_t opCall(T)(T arg){ return arg[idx]; } - enum bitSize = bits; -} - -/** - Conceptual type that outlines the common properties of all UTF Matchers. - - Note: For illustration purposes only, every method - call results in assertion failure. - Use $(LREF utfMatcher) to obtain a concrete matcher - for UTF-8 or UTF-16 encodings. -*/ -public struct MatcherConcept -{ - /** - $(P Perform a semantic equivalent 2 operations: - decoding a $(CODEPOINT) at front of `inp` and testing if - it belongs to the set of $(CODEPOINTS) of this matcher. ) - - $(P The effect on `inp` depends on the kind of function called:) - - $(P Match. If the codepoint is found in the set then range `inp` - is advanced by its size in $(S_LINK Code unit, code units), - otherwise the range is not modifed.) - - $(P Skip. The range is always advanced by the size - of the tested $(CODEPOINT) regardless of the result of test.) - - $(P Test. The range is left unaffected regardless - of the result of test.) - */ - public bool match(Range)(ref Range inp) - if (isRandomAccessRange!Range && is(ElementType!Range : char)) - { - assert(false); - } - - ///ditto - public bool skip(Range)(ref Range inp) - if (isRandomAccessRange!Range && is(ElementType!Range : char)) - { - assert(false); - } - - ///ditto - public bool test(Range)(ref Range inp) - if (isRandomAccessRange!Range && is(ElementType!Range : char)) - { - assert(false); - } - /// - pure @safe unittest - { - string truth = "2² = 4"; - auto m = utfMatcher!char(unicode.Number); - assert(m.match(truth)); // '2' is a number all right - assert(truth == "² = 4"); // skips on match - assert(m.match(truth)); // so is the superscript '2' - assert(!m.match(truth)); // space is not a number - assert(truth == " = 4"); // unaffected on no match - assert(!m.skip(truth)); // same test ... - assert(truth == "= 4"); // but skips a codepoint regardless - assert(!m.test(truth)); // '=' is not a number - assert(truth == "= 4"); // test never affects argument - } - - /** - Advanced feature - provide direct access to a subset of matcher based a - set of known encoding lengths. Lengths are provided in - $(S_LINK Code unit, code units). The sub-matcher then may do less - operations per any `test`/`match`. - - Use with care as the sub-matcher won't match - any $(CODEPOINTS) that have encoded length that doesn't belong - to the selected set of lengths. Also the sub-matcher object references - the parent matcher and must not be used past the liftetime - of the latter. - - Another caveat of using sub-matcher is that skip is not available - preciesly because sub-matcher doesn't detect all lengths. - */ - @property auto subMatcher(Lengths...)() - { - assert(0); - return this; - } - - pure @safe unittest - { - auto m = utfMatcher!char(unicode.Number); - string square = "2²"; - // about sub-matchers - assert(!m.subMatcher!(2,3,4).test(square)); // ASCII no covered - assert(m.subMatcher!1.match(square)); // ASCII-only, works - assert(!m.subMatcher!1.test(square)); // unicode '²' - assert(m.subMatcher!(2,3,4).match(square)); // - assert(square == ""); - wstring wsquare = "2²"; - auto m16 = utfMatcher!wchar(unicode.Number); - // may keep ref, but the orignal (m16) must be kept alive - auto bmp = m16.subMatcher!1; - assert(bmp.match(wsquare)); // Okay, in basic multilingual plan - assert(bmp.match(wsquare)); // And '²' too - } -} - -/** - Test if `M` is an UTF Matcher for ranges of `Char`. -*/ -public enum isUtfMatcher(M, C) = __traits(compiles, (){ - C[] s; - auto d = s.decoder; - M m; - assert(is(typeof(m.match(d)) == bool)); - assert(is(typeof(m.test(d)) == bool)); - static if (is(typeof(m.skip(d)))) - { - assert(is(typeof(m.skip(d)) == bool)); - assert(is(typeof(m.skip(s)) == bool)); - } - assert(is(typeof(m.match(s)) == bool)); - assert(is(typeof(m.test(s)) == bool)); -}); - -pure @safe unittest -{ - alias CharMatcher = typeof(utfMatcher!char(CodepointSet.init)); - alias WcharMatcher = typeof(utfMatcher!wchar(CodepointSet.init)); - static assert(isUtfMatcher!(CharMatcher, char)); - static assert(isUtfMatcher!(CharMatcher, immutable(char))); - static assert(isUtfMatcher!(WcharMatcher, wchar)); - static assert(isUtfMatcher!(WcharMatcher, immutable(wchar))); -} - -enum Mode { - alwaysSkip, - neverSkip, - skipOnMatch -} - -mixin template ForwardStrings() -{ - private bool fwdStr(string fn, C)(ref C[] str) const @trusted - { - import std.utf : byCodeUnit; - alias type = typeof(byCodeUnit(str)); - return mixin(fn~"(*cast(type*)&str)"); - } -} - -template Utf8Matcher() -{ - enum validSize(int sz) = sz >= 1 && sz <= 4; - - void badEncoding() pure @safe - { - import std.utf : UTFException; - throw new UTFException("Invalid UTF-8 sequence"); - } - - //for 1-stage ASCII - alias AsciiSpec = AliasSeq!(bool, char, clamp!7); - //for 2-stage lookup of 2 byte UTF-8 sequences - alias Utf8Spec2 = AliasSeq!(bool, char[2], - clampIdx!(0, 5), clampIdx!(1, 6)); - //ditto for 3 byte - alias Utf8Spec3 = AliasSeq!(bool, char[3], - clampIdx!(0, 4), - clampIdx!(1, 6), - clampIdx!(2, 6) - ); - //ditto for 4 byte - alias Utf8Spec4 = AliasSeq!(bool, char[4], - clampIdx!(0, 3), clampIdx!(1, 6), - clampIdx!(2, 6), clampIdx!(3, 6) - ); - alias Tables = AliasSeq!( - typeof(TrieBuilder!(AsciiSpec)(false).build()), - typeof(TrieBuilder!(Utf8Spec2)(false).build()), - typeof(TrieBuilder!(Utf8Spec3)(false).build()), - typeof(TrieBuilder!(Utf8Spec4)(false).build()) - ); - alias Table(int size) = Tables[size-1]; - - enum leadMask(size_t size) = (cast(size_t) 1<<(7 - size))-1; - enum encMask(size_t size) = ((1 << size)-1)<<(8-size); - - char truncate()(char ch) pure @safe - { - ch -= 0x80; - if (ch < 0x40) - { - return ch; - } - else - { - badEncoding(); - return cast(char) 0; - } - } - - static auto encode(size_t sz)(dchar ch) - if (sz > 1) - { - import std.utf : encodeUTF = encode; - char[4] buf; - encodeUTF(buf, ch); - char[sz] ret; - buf[0] &= leadMask!sz; - foreach (n; 1 .. sz) - buf[n] = buf[n] & 0x3f; //keep 6 lower bits - ret[] = buf[0 .. sz]; - return ret; - } - - auto build(Set)(Set set) - { - import std.algorithm.iteration : map; - auto ascii = set & unicode.ASCII; - auto utf8_2 = set & CodepointSet(0x80, 0x800); - auto utf8_3 = set & CodepointSet(0x800, 0x1_0000); - auto utf8_4 = set & CodepointSet(0x1_0000, lastDchar+1); - auto asciiT = ascii.byCodepoint.map!(x=>cast(char) x).buildTrie!(AsciiSpec); - auto utf8_2T = utf8_2.byCodepoint.map!(x=>encode!2(x)).buildTrie!(Utf8Spec2); - auto utf8_3T = utf8_3.byCodepoint.map!(x=>encode!3(x)).buildTrie!(Utf8Spec3); - auto utf8_4T = utf8_4.byCodepoint.map!(x=>encode!4(x)).buildTrie!(Utf8Spec4); - alias Ret = Impl!(1,2,3,4); - return Ret(asciiT, utf8_2T, utf8_3T, utf8_4T); - } - - // Bootstrap UTF-8 static matcher interface - // from 3 primitives: tab!(size), lookup and Sizes - mixin template DefMatcher() - { - import std.format : format; - import std.meta : Erase, staticIndexOf; - enum hasASCII = staticIndexOf!(1, Sizes) >= 0; - alias UniSizes = Erase!(1, Sizes); - - //generate dispatch code sequence for unicode parts - static auto genDispatch() - { - string code; - foreach (size; UniSizes) - code ~= format(q{ - if ((ch & ~leadMask!%d) == encMask!(%d)) - return lookup!(%d, mode)(inp); - else - }, size, size, size); - static if (Sizes.length == 4) //covers all code unit cases - code ~= "{ badEncoding(); return false; }"; - else - code ~= "return false;"; //may be just fine but not covered - return code; - } - enum dispatch = genDispatch(); - - public bool match(Range)(ref Range inp) const - if (isRandomAccessRange!Range && is(ElementType!Range : char) && - !isDynamicArray!Range) - { - enum mode = Mode.skipOnMatch; - assert(!inp.empty); - immutable ch = inp[0]; - static if (hasASCII) - { - if (ch < 0x80) - { - immutable r = tab!1[ch]; - if (r) - inp.popFront(); - return r; - } - else - mixin(dispatch); - } - else - mixin(dispatch); - } - - static if (Sizes.length == 4) // can skip iff can detect all encodings - { - public bool skip(Range)(ref Range inp) const - if (isRandomAccessRange!Range && is(ElementType!Range : char) && - !isDynamicArray!Range) - { - enum mode = Mode.alwaysSkip; - assert(!inp.empty); - auto ch = inp[0]; - static if (hasASCII) - { - if (ch < 0x80) - { - inp.popFront(); - return tab!1[ch]; - } - else - mixin(dispatch); - } - else - mixin(dispatch); - } - } - - public bool test(Range)(ref Range inp) const - if (isRandomAccessRange!Range && is(ElementType!Range : char) && - !isDynamicArray!Range) - { - enum mode = Mode.neverSkip; - assert(!inp.empty); - auto ch = inp[0]; - - static if (hasASCII) - { - if (ch < 0x80) - return tab!1[ch]; - else - mixin(dispatch); - } - else - mixin(dispatch); - } - - bool match(C)(ref C[] str) const - if (isSomeChar!C) - { - return fwdStr!"match"(str); - } - - bool skip(C)(ref C[] str) const - if (isSomeChar!C) - { - return fwdStr!"skip"(str); - } - - bool test(C)(ref C[] str) const - if (isSomeChar!C) - { - return fwdStr!"test"(str); - } - - mixin ForwardStrings; - } - - struct Impl(Sizes...) - { - import std.meta : allSatisfy, staticMap; - static assert(allSatisfy!(validSize, Sizes), - "Only lengths of 1, 2, 3 and 4 code unit are possible for UTF-8"); - private: - //pick tables for chosen sizes - alias OurTabs = staticMap!(Table, Sizes); - OurTabs tables; - mixin DefMatcher; - //static disptach helper UTF size ==> table - alias tab(int i) = tables[i - 1]; - - package(std) @property CherryPick!(Impl, SizesToPick) subMatcher(SizesToPick...)() - { - return CherryPick!(Impl, SizesToPick)(&this); - } - - bool lookup(int size, Mode mode, Range)(ref Range inp) const - { - import std.range : popFrontN; - if (inp.length < size) - { - badEncoding(); - return false; - } - char[size] needle = void; - needle[0] = leadMask!size & inp[0]; - static foreach (i; 1 .. size) - { - needle[i] = truncate(inp[i]); - } - //overlong encoding checks - static if (size == 2) - { - //0x80-0x7FF - //got 6 bits in needle[1], must use at least 8 bits - //must use at least 2 bits in needle[1] - if (needle[0] < 2) badEncoding(); - } - else static if (size == 3) - { - //0x800-0xFFFF - //got 6 bits in needle[2], must use at least 12bits - //must use 6 bits in needle[1] or anything in needle[0] - if (needle[0] == 0 && needle[1] < 0x20) badEncoding(); - } - else static if (size == 4) - { - //0x800-0xFFFF - //got 2x6=12 bits in needle[2 .. 3] must use at least 17bits - //must use 5 bits (or above) in needle[1] or anything in needle[0] - if (needle[0] == 0 && needle[1] < 0x10) badEncoding(); - } - static if (mode == Mode.alwaysSkip) - { - inp.popFrontN(size); - return tab!size[needle]; - } - else static if (mode == Mode.neverSkip) - { - return tab!size[needle]; - } - else - { - static assert(mode == Mode.skipOnMatch); - - if (tab!size[needle]) - { - inp.popFrontN(size); - return true; - } - else - return false; - } - } - } - - struct CherryPick(I, Sizes...) - { - import std.meta : allSatisfy; - static assert(allSatisfy!(validSize, Sizes), - "Only lengths of 1, 2, 3 and 4 code unit are possible for UTF-8"); - private: - I* m; - @property auto tab(int i)() const { return m.tables[i - 1]; } - bool lookup(int size, Mode mode, Range)(ref Range inp) const - { - return m.lookup!(size, mode)(inp); - } - mixin DefMatcher; - } -} - -template Utf16Matcher() -{ - enum validSize(int sz) = sz >= 1 && sz <= 2; - - void badEncoding() pure @safe - { - import std.utf : UTFException; - throw new UTFException("Invalid UTF-16 sequence"); - } - - // 1-stage ASCII - alias AsciiSpec = AliasSeq!(bool, wchar, clamp!7); - //2-stage BMP - alias BmpSpec = AliasSeq!(bool, wchar, sliceBits!(7, 16), sliceBits!(0, 7)); - //4-stage - full Unicode - //assume that 0xD800 & 0xDC00 bits are cleared - //thus leaving 10 bit per wchar to worry about - alias UniSpec = AliasSeq!(bool, wchar[2], - assumeSize!(x=>x[0]>>4, 6), assumeSize!(x=>x[0]&0xf, 4), - assumeSize!(x=>x[1]>>6, 4), assumeSize!(x=>x[1]&0x3f, 6), - ); - alias Ascii = typeof(TrieBuilder!(AsciiSpec)(false).build()); - alias Bmp = typeof(TrieBuilder!(BmpSpec)(false).build()); - alias Uni = typeof(TrieBuilder!(UniSpec)(false).build()); - - auto encode2(dchar ch) - { - ch -= 0x1_0000; - assert(ch <= 0xF_FFFF); - wchar[2] ret; - //do not put surrogate bits, they are sliced off - ret[0] = cast(wchar)(ch >> 10); - ret[1] = (ch & 0xFFF); - return ret; - } - - auto build(Set)(Set set) - { - import std.algorithm.iteration : map; - auto ascii = set & unicode.ASCII; - auto bmp = (set & CodepointSet.fromIntervals(0x80, 0xFFFF+1)) - - CodepointSet.fromIntervals(0xD800, 0xDFFF+1); - auto other = set - (bmp | ascii); - auto asciiT = ascii.byCodepoint.map!(x=>cast(char) x).buildTrie!(AsciiSpec); - auto bmpT = bmp.byCodepoint.map!(x=>cast(wchar) x).buildTrie!(BmpSpec); - auto otherT = other.byCodepoint.map!(x=>encode2(x)).buildTrie!(UniSpec); - alias Ret = Impl!(1,2); - return Ret(asciiT, bmpT, otherT); - } - - //bootstrap full UTF-16 matcher interace from - //sizeFlags, lookupUni and ascii - mixin template DefMatcher() - { - public bool match(Range)(ref Range inp) const - if (isRandomAccessRange!Range && is(ElementType!Range : wchar) && - !isDynamicArray!Range) - { - enum mode = Mode.skipOnMatch; - assert(!inp.empty); - immutable ch = inp[0]; - static if (sizeFlags & 1) - { - if (ch < 0x80) - { - if (ascii[ch]) - { - inp.popFront(); - return true; - } - else - return false; - } - return lookupUni!mode(inp); - } - else - return lookupUni!mode(inp); - } - - static if (Sizes.length == 2) - { - public bool skip(Range)(ref Range inp) const - if (isRandomAccessRange!Range && is(ElementType!Range : wchar) && - !isDynamicArray!Range) - { - enum mode = Mode.alwaysSkip; - assert(!inp.empty); - immutable ch = inp[0]; - static if (sizeFlags & 1) - { - if (ch < 0x80) - { - inp.popFront(); - return ascii[ch]; - } - else - return lookupUni!mode(inp); - } - else - return lookupUni!mode(inp); - } - } - - public bool test(Range)(ref Range inp) const - if (isRandomAccessRange!Range && is(ElementType!Range : wchar) && - !isDynamicArray!Range) - { - enum mode = Mode.neverSkip; - assert(!inp.empty); - auto ch = inp[0]; - static if (sizeFlags & 1) - return ch < 0x80 ? ascii[ch] : lookupUni!mode(inp); - else - return lookupUni!mode(inp); - } - - bool match(C)(ref C[] str) const - if (isSomeChar!C) - { - return fwdStr!"match"(str); - } - - bool skip(C)(ref C[] str) const - if (isSomeChar!C) - { - return fwdStr!"skip"(str); - } - - bool test(C)(ref C[] str) const - if (isSomeChar!C) - { - return fwdStr!"test"(str); - } - - mixin ForwardStrings; //dispatch strings to range versions - } - - struct Impl(Sizes...) - if (Sizes.length >= 1 && Sizes.length <= 2) - { - private: - import std.meta : allSatisfy; - static assert(allSatisfy!(validSize, Sizes), - "Only lengths of 1 and 2 code units are possible in UTF-16"); - static if (Sizes.length > 1) - enum sizeFlags = Sizes[0] | Sizes[1]; - else - enum sizeFlags = Sizes[0]; - - static if (sizeFlags & 1) - { - Ascii ascii; - Bmp bmp; - } - static if (sizeFlags & 2) - { - Uni uni; - } - mixin DefMatcher; - - package(std) @property CherryPick!(Impl, SizesToPick) subMatcher(SizesToPick...)() - { - return CherryPick!(Impl, SizesToPick)(&this); - } - - bool lookupUni(Mode mode, Range)(ref Range inp) const - { - wchar x = cast(wchar)(inp[0] - 0xD800); - //not a high surrogate - if (x > 0x3FF) - { - //low surrogate - if (x <= 0x7FF) badEncoding(); - static if (sizeFlags & 1) - { - auto ch = inp[0]; - static if (mode == Mode.alwaysSkip) - inp.popFront(); - static if (mode == Mode.skipOnMatch) - { - if (bmp[ch]) - { - inp.popFront(); - return true; - } - else - return false; - } - else - return bmp[ch]; - } - else //skip is not available for sub-matchers, so just false - return false; - } - else - { - import std.range : popFrontN; - static if (sizeFlags & 2) - { - if (inp.length < 2) - badEncoding(); - wchar y = cast(wchar)(inp[1] - 0xDC00); - //not a low surrogate - if (y > 0x3FF) - badEncoding(); - wchar[2] needle = [inp[0] & 0x3ff, inp[1] & 0x3ff]; - static if (mode == Mode.alwaysSkip) - inp.popFrontN(2); - static if (mode == Mode.skipOnMatch) - { - if (uni[needle]) - { - inp.popFrontN(2); - return true; - } - else - return false; - } - else - return uni[needle]; - } - else //ditto - return false; - } - } - } - - struct CherryPick(I, Sizes...) - if (Sizes.length >= 1 && Sizes.length <= 2) - { - private: - import std.meta : allSatisfy; - I* m; - enum sizeFlags = I.sizeFlags; - - static if (sizeFlags & 1) - { - @property auto ascii()() const { return m.ascii; } - } - - bool lookupUni(Mode mode, Range)(ref Range inp) const - { - return m.lookupUni!mode(inp); - } - mixin DefMatcher; - static assert(allSatisfy!(validSize, Sizes), - "Only lengths of 1 and 2 code units are possible in UTF-16"); - } -} - -private auto utf8Matcher(Set)(Set set) -{ - return Utf8Matcher!().build(set); -} - -private auto utf16Matcher(Set)(Set set) -{ - return Utf16Matcher!().build(set); -} - -/** - Constructs a matcher object - to classify $(CODEPOINTS) from the `set` for encoding - that has `Char` as code unit. - - See $(LREF MatcherConcept) for API outline. -*/ -public auto utfMatcher(Char, Set)(Set set) -if (isCodepointSet!Set) -{ - static if (is(Char : char)) - return utf8Matcher(set); - else static if (is(Char : wchar)) - return utf16Matcher(set); - else static if (is(Char : dchar)) - static assert(false, "UTF-32 needs no decoding, - and thus not supported by utfMatcher"); - else - static assert(false, "Only character types 'char' and 'wchar' are allowed"); -} - - -//a range of code units, packed with index to speed up forward iteration -package(std) auto decoder(C)(C[] s, size_t offset=0) -if (is(C : wchar) || is(C : char)) -{ - static struct Decoder - { - pure nothrow: - C[] str; - size_t idx; - @property C front(){ return str[idx]; } - @property C back(){ return str[$-1]; } - void popFront(){ idx++; } - void popBack(){ str = str[0..$-1]; } - void popFrontN(size_t n){ idx += n; } - @property bool empty(){ return idx == str.length; } - @property auto save(){ return this; } - auto opIndex(size_t i){ return str[idx+i]; } - @property size_t length(){ return str.length - idx; } - alias opDollar = length; - auto opSlice(size_t a, size_t b){ return Decoder(str[0 .. idx+b], idx+a); } - } - static assert(isRandomAccessRange!Decoder); - static assert(is(ElementType!Decoder : C)); - return Decoder(s, offset); -} - -pure @safe unittest -{ - string rs = "hi! ネемног砀 текста"; - auto codec = rs.decoder; - auto utf8 = utf8Matcher(unicode.Letter); - auto asc = utf8.subMatcher!(1); - auto uni = utf8.subMatcher!(2,3,4); - - // h - assert(asc.test(codec)); - assert(!uni.match(codec)); - assert(utf8.skip(codec)); - assert(codec.idx == 1); - - // i - assert(asc.test(codec)); - assert(!uni.match(codec)); - assert(utf8.skip(codec)); - assert(codec.idx == 2); - - // ! - assert(!asc.match(codec)); - assert(!utf8.test(codec)); - assert(!utf8.skip(codec)); - assert(codec.idx == 3); - - // space - assert(!asc.test(codec)); - assert(!utf8.test(codec)); - assert(!utf8.skip(codec)); - assert(codec.idx == 4); - - assert(utf8.test(codec)); - foreach (i; 0 .. 7) - { - assert(!asc.test(codec)); - assert(uni.test(codec)); - assert(utf8.skip(codec)); - } - assert(!utf8.test(codec)); - assert(!utf8.skip(codec)); - - //the same with match where applicable - codec = rs.decoder; - assert(utf8.match(codec)); - assert(codec.idx == 1); - assert(utf8.match(codec)); - assert(codec.idx == 2); - assert(!utf8.match(codec)); - assert(codec.idx == 2); - assert(!utf8.skip(codec)); - assert(!utf8.skip(codec)); - - foreach (i; 0 .. 7) - { - assert(!asc.test(codec)); - assert(utf8.test(codec)); - assert(utf8.match(codec)); - } - auto i = codec.idx; - assert(!utf8.match(codec)); - assert(codec.idx == i); -} - -pure @system unittest -{ - import std.range : stride; - static bool testAll(Matcher, Range)(ref Matcher m, ref Range r) @safe - { - bool t = m.test(r); - auto save = r.idx; - assert(t == m.match(r)); - assert(r.idx == save || t); //ether no change or was match - r.idx = save; - static if (is(typeof(m.skip(r)))) - { - assert(t == m.skip(r)); - assert(r.idx != save); //always changed - r.idx = save; - } - return t; - } - auto utf16 = utfMatcher!wchar(unicode.L); - auto bmp = utf16.subMatcher!1; - auto nonBmp = utf16.subMatcher!1; - auto utf8 = utfMatcher!char(unicode.L); - auto ascii = utf8.subMatcher!1; - auto uni2 = utf8.subMatcher!2; - auto uni3 = utf8.subMatcher!3; - auto uni24 = utf8.subMatcher!(2,4); - foreach (ch; unicode.L.byCodepoint.stride(3)) - { - import std.utf : encode; - char[4] buf; - wchar[2] buf16; - auto len = encode(buf, ch); - auto len16 = encode(buf16, ch); - auto c8 = buf[0 .. len].decoder; - auto c16 = buf16[0 .. len16].decoder; - assert(testAll(utf16, c16)); - assert(testAll(bmp, c16) || len16 != 1); - assert(testAll(nonBmp, c16) || len16 != 2); - - assert(testAll(utf8, c8)); - - //submatchers return false on out of their domain - assert(testAll(ascii, c8) || len != 1); - assert(testAll(uni2, c8) || len != 2); - assert(testAll(uni3, c8) || len != 3); - assert(testAll(uni24, c8) || (len != 2 && len != 4)); - } -} - -// cover decode fail cases of Matcher -pure @safe unittest -{ - import std.algorithm.iteration : map; - import std.exception : collectException; - import std.format : format; - auto utf16 = utfMatcher!wchar(unicode.L); - auto utf8 = utfMatcher!char(unicode.L); - //decode failure cases UTF-8 - alias fails8 = AliasSeq!("\xC1", "\x80\x00","\xC0\x00", "\xCF\x79", - "\xFF\x00\0x00\0x00\x00", "\xC0\0x80\0x80\x80", "\x80\0x00\0x00\x00", - "\xCF\x00\0x00\0x00\x00"); - foreach (msg; fails8) - { - assert(collectException((){ - auto s = msg; - size_t idx = 0; - utf8.test(s); - }()), format("%( %2x %)", cast(immutable(ubyte)[]) msg)); - } - //decode failure cases UTF-16 - alias fails16 = AliasSeq!([0xD811], [0xDC02]); - foreach (msg; fails16) - { - assert(collectException((){ - auto s = msg.map!(x => cast(wchar) x); - utf16.test(s); - }())); - } -} - -/++ - Convenience function to construct optimal configurations for - packed Trie from any `set` of $(CODEPOINTS). - - The parameter `level` indicates the number of trie levels to use, - allowed values are: 1, 2, 3 or 4. Levels represent different trade-offs - speed-size wise. - - $(P Level 1 is fastest and the most memory hungry (a bit array). ) - $(P Level 4 is the slowest and has the smallest footprint. ) - - See the $(S_LINK Synopsis, Synopsis) section for example. - - Note: - Level 4 stays very practical (being faster and more predictable) - compared to using direct lookup on the `set` itself. - - -+/ -public auto toTrie(size_t level, Set)(Set set) -if (isCodepointSet!Set) -{ - static if (level == 1) - return codepointSetTrie!(21)(set); - else static if (level == 2) - return codepointSetTrie!(10, 11)(set); - else static if (level == 3) - return codepointSetTrie!(8, 5, 8)(set); - else static if (level == 4) - return codepointSetTrie!(6, 4, 4, 7)(set); - else - static assert(false, - "Sorry, toTrie doesn't support levels > 4, use codepointSetTrie directly"); -} - -/** - $(P Builds a `Trie` with typically optimal speed-size trade-off - and wraps it into a delegate of the following type: - $(D bool delegate(dchar ch)). ) - - $(P Effectively this creates a 'tester' lambda suitable - for algorithms like std.algorithm.find that take unary predicates. ) - - See the $(S_LINK Synopsis, Synopsis) section for example. -*/ -public auto toDelegate(Set)(Set set) -if (isCodepointSet!Set) -{ - // 3 is very small and is almost as fast as 2-level (due to CPU caches?) - auto t = toTrie!3(set); - return (dchar ch) => t[ch]; -} - -/** - $(P Opaque wrapper around unsigned built-in integers and - code unit (char/wchar/dchar) types. - Parameter `sz` indicates that the value is confined - to the range of [0, 2^^sz$(RPAREN). With this knowledge it can be - packed more tightly when stored in certain - data-structures like trie. ) - - Note: - $(P The $(D BitPacked!(T, sz)) is implicitly convertible to `T` - but not vise-versa. Users have to ensure the value fits in - the range required and use the `cast` - operator to perform the conversion.) -*/ -struct BitPacked(T, size_t sz) -if (isIntegral!T || is(T:dchar)) -{ - enum bitSize = sz; - T _value; - alias _value this; -} - -/* - Depending on the form of the passed argument `bitSizeOf` returns - the amount of bits required to represent a given type - or a return type of a given functor. -*/ -template bitSizeOf(Args...) -if (Args.length == 1) -{ - import std.traits : ReturnType; - alias T = Args[0]; - static if (__traits(compiles, { size_t val = T.bitSize; })) //(is(typeof(T.bitSize) : size_t)) - { - enum bitSizeOf = T.bitSize; - } - else static if (is(ReturnType!T dummy == BitPacked!(U, bits), U, size_t bits)) - { - enum bitSizeOf = bitSizeOf!(ReturnType!T); - } - else - { - enum bitSizeOf = T.sizeof*8; - } -} - -/** - Tests if `T` is some instantiation of $(LREF BitPacked)!(U, x) - and thus suitable for packing. -*/ -template isBitPacked(T) -{ - static if (is(T dummy == BitPacked!(U, bits), U, size_t bits)) - enum isBitPacked = true; - else - enum isBitPacked = false; -} - -/** - Gives the type `U` from $(LREF BitPacked)!(U, x) - or `T` itself for every other type. -*/ -template TypeOfBitPacked(T) -{ - static if (is(T dummy == BitPacked!(U, bits), U, size_t bits)) - alias TypeOfBitPacked = U; - else - alias TypeOfBitPacked = T; -} - -/* - Wrapper, used in definition of custom data structures from `Trie` template. - Applying it to a unary lambda function indicates that the returned value always - fits within `bits` of bits. -*/ -struct assumeSize(alias Fn, size_t bits) -{ - enum bitSize = bits; - static auto ref opCall(T)(auto ref T arg) - { - return Fn(arg); - } -} - -/* - A helper for defining lambda function that yields a slice - of certain bits from an unsigned integral value. - The resulting lambda is wrapped in assumeSize and can be used directly - with `Trie` template. -*/ -struct sliceBits(size_t from, size_t to) -{ - //for now bypass assumeSize, DMD has trouble inlining it - enum bitSize = to-from; - static auto opCall(T)(T x) - out(result) - { - assert(result < (1 << to-from)); - } - do - { - static assert(from < to); - static if (from == 0) - return x & ((1 << to)-1); - else - return (x >> from) & ((1<<(to-from))-1); - } -} - -@safe pure nothrow @nogc uint low_8(uint x) { return x&0xFF; } -@safe pure nothrow @nogc uint midlow_8(uint x){ return (x&0xFF00)>>8; } -alias lo8 = assumeSize!(low_8, 8); -alias mlo8 = assumeSize!(midlow_8, 8); - -@safe pure nothrow @nogc unittest -{ - static assert(bitSizeOf!lo8 == 8); - static assert(bitSizeOf!(sliceBits!(4, 7)) == 3); - static assert(bitSizeOf!(BitPacked!(uint, 2)) == 2); -} - -template Sequence(size_t start, size_t end) -{ - static if (start < end) - alias Sequence = AliasSeq!(start, Sequence!(start+1, end)); - else - alias Sequence = AliasSeq!(); -} - -//---- TRIE TESTS ---- -@system unittest -{ - import std.algorithm.iteration : map; - import std.algorithm.sorting : sort; - import std.array : array; - import std.conv : text, to; - import std.range : iota; - static trieStats(TRIE)(TRIE t) - { - version (std_uni_stats) - { - import std.stdio : writefln, writeln; - writeln("---TRIE FOOTPRINT STATS---"); - static foreach (i; 0 .. t.table.dim) - { - writefln("lvl%s = %s bytes; %s pages" - , i, t.bytes!i, t.pages!i); - } - writefln("TOTAL: %s bytes", t.bytes); - version (none) - { - writeln("INDEX (excluding value level):"); - static foreach (i; 0 .. t.table.dim-1) - writeln(t.table.slice!(i)[0 .. t.table.length!i]); - } - writeln("---------------------------"); - } - } - //@@@BUG link failure, lambdas not found by linker somehow (in case of trie2) - // alias lo8 = assumeSize!(8, function (uint x) { return x&0xFF; }); - // alias next8 = assumeSize!(7, function (uint x) { return (x&0x7F00)>>8; }); - alias Set = CodepointSet; - auto set = Set('A','Z','a','z'); - auto trie = buildTrie!(bool, uint, 256, lo8)(set.byInterval);// simple bool array - for (int a='a'; a<'z';a++) - assert(trie[a]); - for (int a='A'; a<'Z';a++) - assert(trie[a]); - for (int a=0; a<'A'; a++) - assert(!trie[a]); - for (int a ='Z'; a<'a'; a++) - assert(!trie[a]); - trieStats(trie); - - auto redundant2 = Set( - 1, 18, 256+2, 256+111, 512+1, 512+18, 768+2, 768+111); - auto trie2 = buildTrie!(bool, uint, 1024, mlo8, lo8)(redundant2.byInterval); - trieStats(trie2); - foreach (e; redundant2.byCodepoint) - assert(trie2[e], text(cast(uint) e, " - ", trie2[e])); - foreach (i; 0 .. 1024) - { - assert(trie2[i] == (i in redundant2)); - } - - - auto redundant3 = Set( - 2, 4, 6, 8, 16, - 2+16, 4+16, 16+6, 16+8, 16+16, - 2+32, 4+32, 32+6, 32+8, - ); - - enum max3 = 256; - // sliceBits - auto trie3 = buildTrie!(bool, uint, max3, - sliceBits!(6,8), sliceBits!(4,6), sliceBits!(0,4) - )(redundant3.byInterval); - trieStats(trie3); - foreach (i; 0 .. max3) - assert(trie3[i] == (i in redundant3), text(cast(uint) i)); - - auto redundant4 = Set( - 10, 64, 64+10, 128, 128+10, 256, 256+10, 512, - 1000, 2000, 3000, 4000, 5000, 6000 - ); - enum max4 = 2^^16; - auto trie4 = buildTrie!(bool, size_t, max4, - sliceBits!(13, 16), sliceBits!(9, 13), sliceBits!(6, 9) , sliceBits!(0, 6) - )(redundant4.byInterval); - foreach (i; 0 .. max4) - { - if (i in redundant4) - assert(trie4[i], text(cast(uint) i)); - } - trieStats(trie4); - - alias mapToS = mapTrieIndex!(useItemAt!(0, char)); - string[] redundantS = ["tea", "start", "orange"]; - redundantS.sort!((a,b) => mapToS(a) < mapToS(b))(); - auto strie = buildTrie!(bool, string, useItemAt!(0, char))(redundantS); - // using first char only - assert(redundantS == ["orange", "start", "tea"]); - assert(strie["test"], text(strie["test"])); - assert(!strie["aea"]); - assert(strie["s"]); - - // a bit size test - auto a = array(map!(x => to!ubyte(x))(iota(0, 256))); - auto bt = buildTrie!(bool, ubyte, sliceBits!(7, 8), sliceBits!(5, 7), sliceBits!(0, 5))(a); - trieStats(bt); - foreach (i; 0 .. 256) - assert(bt[cast(ubyte) i]); -} - -template useItemAt(size_t idx, T) -if (isIntegral!T || is(T: dchar)) -{ - size_t impl(const scope T[] arr){ return arr[idx]; } - alias useItemAt = assumeSize!(impl, 8*T.sizeof); -} - -template useLastItem(T) -{ - size_t impl(const scope T[] arr){ return arr[$-1]; } - alias useLastItem = assumeSize!(impl, 8*T.sizeof); -} - -template fullBitSize(Prefix...) -{ - static if (Prefix.length > 0) - enum fullBitSize = bitSizeOf!(Prefix[0])+fullBitSize!(Prefix[1..$]); - else - enum fullBitSize = 0; -} - -template idxTypes(Key, size_t fullBits, Prefix...) -{ - static if (Prefix.length == 1) - {// the last level is value level, so no index once reduced to 1-level - alias idxTypes = AliasSeq!(); - } - else - { - // Important note on bit packing - // Each level has to hold enough of bits to address the next one - // The bottom level is known to hold full bit width - // thus it's size in pages is full_bit_width - size_of_last_prefix - // Recourse on this notion - alias idxTypes = - AliasSeq!( - idxTypes!(Key, fullBits - bitSizeOf!(Prefix[$-1]), Prefix[0..$-1]), - BitPacked!(typeof(Prefix[$-2](Key.init)), fullBits - bitSizeOf!(Prefix[$-1])) - ); - } -} - -//============================================================================ - -@safe pure int comparePropertyName(Char1, Char2)(const(Char1)[] a, const(Char2)[] b) -if (is(Char1 : dchar) && is(Char2 : dchar)) -{ - import std.algorithm.comparison : cmp; - import std.algorithm.iteration : map, filter; - import std.ascii : toLower; - static bool pred(dchar c) {return !c.isWhite && c != '-' && c != '_';} - return cmp( - a.map!toLower.filter!pred, - b.map!toLower.filter!pred); -} - -@safe pure unittest -{ - assert(!comparePropertyName("foo-bar", "fooBar")); -} - -bool propertyNameLess(Char1, Char2)(const(Char1)[] a, const(Char2)[] b) @safe pure -if (is(Char1 : dchar) && is(Char2 : dchar)) -{ - return comparePropertyName(a, b) < 0; -} - -//============================================================================ -// Utilities for compression of Unicode code point sets -//============================================================================ - -@safe void compressTo(uint val, ref scope ubyte[] arr) pure nothrow -{ - // not optimized as usually done 1 time (and not public interface) - if (val < 128) - arr ~= cast(ubyte) val; - else if (val < (1 << 13)) - { - arr ~= (0b1_00 << 5) | cast(ubyte)(val >> 8); - arr ~= val & 0xFF; - } - else - { - assert(val < (1 << 21)); - arr ~= (0b1_01 << 5) | cast(ubyte)(val >> 16); - arr ~= (val >> 8) & 0xFF; - arr ~= val & 0xFF; - } -} - -@safe uint decompressFrom(scope const(ubyte)[] arr, ref size_t idx) pure -{ - import std.exception : enforce; - immutable first = arr[idx++]; - if (!(first & 0x80)) // no top bit -> [0 .. 127] - return first; - immutable extra = ((first >> 5) & 1) + 1; // [1, 2] - uint val = (first & 0x1F); - enforce(idx + extra <= arr.length, "bad code point interval encoding"); - foreach (j; 0 .. extra) - val = (val << 8) | arr[idx+j]; - idx += extra; - return val; -} - - -package(std) ubyte[] compressIntervals(Range)(Range intervals) -if (isInputRange!Range && isIntegralPair!(ElementType!Range)) -{ - ubyte[] storage; - uint base = 0; - // RLE encode - foreach (val; intervals) - { - compressTo(val[0]-base, storage); - base = val[0]; - if (val[1] != lastDchar+1) // till the end of the domain so don't store it - { - compressTo(val[1]-base, storage); - base = val[1]; - } - } - return storage; -} - -@safe pure unittest -{ - import std.algorithm.comparison : equal; - import std.typecons : tuple; - - auto run = [tuple(80, 127), tuple(128, (1 << 10)+128)]; - ubyte[] enc = [cast(ubyte) 80, 47, 1, (0b1_00 << 5) | (1 << 2), 0]; - assert(compressIntervals(run) == enc); - auto run2 = [tuple(0, (1 << 20)+512+1), tuple((1 << 20)+512+4, lastDchar+1)]; - ubyte[] enc2 = [cast(ubyte) 0, (0b1_01 << 5) | (1 << 4), 2, 1, 3]; // odd length-ed - assert(compressIntervals(run2) == enc2); - size_t idx = 0; - assert(decompressFrom(enc, idx) == 80); - assert(decompressFrom(enc, idx) == 47); - assert(decompressFrom(enc, idx) == 1); - assert(decompressFrom(enc, idx) == (1 << 10)); - idx = 0; - assert(decompressFrom(enc2, idx) == 0); - assert(decompressFrom(enc2, idx) == (1 << 20)+512+1); - assert(equal(decompressIntervals(compressIntervals(run)), run)); - assert(equal(decompressIntervals(compressIntervals(run2)), run2)); -} - -// Creates a range of `CodepointInterval` that lazily decodes compressed data. -@safe package(std) auto decompressIntervals(const(ubyte)[] data) pure -{ - return DecompressedIntervals(data); -} - -@safe struct DecompressedIntervals -{ -pure: - const(ubyte)[] _stream; - size_t _idx; - CodepointInterval _front; - - this(const(ubyte)[] stream) - { - _stream = stream; - popFront(); - } - - @property CodepointInterval front() - { - assert(!empty); - return _front; - } - - void popFront() - { - if (_idx == _stream.length) - { - _idx = size_t.max; - return; - } - uint base = _front[1]; - _front[0] = base + decompressFrom(_stream, _idx); - if (_idx == _stream.length)// odd length ---> till the end - _front[1] = lastDchar+1; - else - { - base = _front[0]; - _front[1] = base + decompressFrom(_stream, _idx); - } - } - - @property bool empty() const - { - return _idx == size_t.max; - } - - @property DecompressedIntervals save() return scope { return this; } -} - -@safe pure nothrow @nogc unittest -{ - static assert(isInputRange!DecompressedIntervals); - static assert(isForwardRange!DecompressedIntervals); -} - -//============================================================================ - -version (std_uni_bootstrap){} -else -{ - -// helper for looking up code point sets -ptrdiff_t findUnicodeSet(alias table, C)(const scope C[] name) -{ - import std.algorithm.iteration : map; - import std.range : assumeSorted; - auto range = assumeSorted!((a,b) => propertyNameLess(a,b)) - (table.map!"a.name"()); - size_t idx = range.lowerBound(name).length; - if (idx < range.length && comparePropertyName(range[idx], name) == 0) - return idx; - return -1; -} - -// another one that loads it -bool loadUnicodeSet(alias table, Set, C)(const scope C[] name, ref Set dest) -{ - auto idx = findUnicodeSet!table(name); - if (idx >= 0) - { - dest = Set(asSet(table[idx].compressed)); - return true; - } - return false; -} - -bool loadProperty(Set=CodepointSet, C) - (const scope C[] name, ref Set target) pure -{ - import std.internal.unicode_tables : uniProps; // generated file - alias ucmp = comparePropertyName; - // conjure cumulative properties by hand - if (ucmp(name, "L") == 0 || ucmp(name, "Letter") == 0) - { - target = asSet(uniProps.Lu); - target |= asSet(uniProps.Ll); - target |= asSet(uniProps.Lt); - target |= asSet(uniProps.Lo); - target |= asSet(uniProps.Lm); - } - else if (ucmp(name,"LC") == 0 || ucmp(name,"Cased Letter")==0) - { - target = asSet(uniProps.Ll); - target |= asSet(uniProps.Lu); - target |= asSet(uniProps.Lt);// Title case - } - else if (ucmp(name, "M") == 0 || ucmp(name, "Mark") == 0) - { - target = asSet(uniProps.Mn); - target |= asSet(uniProps.Mc); - target |= asSet(uniProps.Me); - } - else if (ucmp(name, "N") == 0 || ucmp(name, "Number") == 0) - { - target = asSet(uniProps.Nd); - target |= asSet(uniProps.Nl); - target |= asSet(uniProps.No); - } - else if (ucmp(name, "P") == 0 || ucmp(name, "Punctuation") == 0) - { - target = asSet(uniProps.Pc); - target |= asSet(uniProps.Pd); - target |= asSet(uniProps.Ps); - target |= asSet(uniProps.Pe); - target |= asSet(uniProps.Pi); - target |= asSet(uniProps.Pf); - target |= asSet(uniProps.Po); - } - else if (ucmp(name, "S") == 0 || ucmp(name, "Symbol") == 0) - { - target = asSet(uniProps.Sm); - target |= asSet(uniProps.Sc); - target |= asSet(uniProps.Sk); - target |= asSet(uniProps.So); - } - else if (ucmp(name, "Z") == 0 || ucmp(name, "Separator") == 0) - { - target = asSet(uniProps.Zs); - target |= asSet(uniProps.Zl); - target |= asSet(uniProps.Zp); - } - else if (ucmp(name, "C") == 0 || ucmp(name, "Other") == 0) - { - target = asSet(uniProps.Cc); - target |= asSet(uniProps.Cf); - target |= asSet(uniProps.Cs); - target |= asSet(uniProps.Co); - target |= asSet(uniProps.Cn); - } - else if (ucmp(name, "graphical") == 0) - { - target = asSet(uniProps.Alphabetic); - - target |= asSet(uniProps.Mn); - target |= asSet(uniProps.Mc); - target |= asSet(uniProps.Me); - - target |= asSet(uniProps.Nd); - target |= asSet(uniProps.Nl); - target |= asSet(uniProps.No); - - target |= asSet(uniProps.Pc); - target |= asSet(uniProps.Pd); - target |= asSet(uniProps.Ps); - target |= asSet(uniProps.Pe); - target |= asSet(uniProps.Pi); - target |= asSet(uniProps.Pf); - target |= asSet(uniProps.Po); - - target |= asSet(uniProps.Zs); - - target |= asSet(uniProps.Sm); - target |= asSet(uniProps.Sc); - target |= asSet(uniProps.Sk); - target |= asSet(uniProps.So); - } - else if (ucmp(name, "any") == 0) - target = Set.fromIntervals(0, 0x110000); - else if (ucmp(name, "ascii") == 0) - target = Set.fromIntervals(0, 0x80); - else - return loadUnicodeSet!(uniProps.tab)(name, target); - return true; -} - -// CTFE-only helper for checking property names at compile-time -@safe bool isPrettyPropertyName(C)(const scope C[] name) -{ - import std.algorithm.searching : find; - auto names = [ - "L", "Letter", - "LC", "Cased Letter", - "M", "Mark", - "N", "Number", - "P", "Punctuation", - "S", "Symbol", - "Z", "Separator", - "Graphical", - "any", - "ascii" - ]; - auto x = find!(x => comparePropertyName(x, name) == 0)(names); - return !x.empty; -} - -// ditto, CTFE-only, not optimized -@safe private static bool findSetName(alias table, C)(const scope C[] name) -{ - return findUnicodeSet!table(name) >= 0; -} - -template SetSearcher(alias table, string kind) -{ - /// Run-time checked search. - static auto opCall(C)(const scope C[] name) - if (is(C : dchar)) - { - import std.conv : to; - CodepointSet set; - if (loadUnicodeSet!table(name, set)) - return set; - throw new Exception("No unicode set for "~kind~" by name " - ~name.to!string()~" was found."); - } - /// Compile-time checked search. - static @property auto opDispatch(string name)() - { - static if (findSetName!table(name)) - { - CodepointSet set; - loadUnicodeSet!table(name, set); - return set; - } - else - static assert(false, "No unicode set for "~kind~" by name " - ~name~" was found."); - } -} - -// Characters that need escaping in string posed as regular expressions -package(std) alias Escapables = AliasSeq!('[', ']', '\\', '^', '$', '.', '|', '?', ',', '-', - ';', ':', '#', '&', '%', '/', '<', '>', '`', '*', '+', '(', ')', '{', '}', '~'); - -package(std) CodepointSet memoizeExpr(string expr)() -{ - if (__ctfe) - return mixin(expr); - alias T = typeof(mixin(expr)); - static T slot; - static bool initialized; - if (!initialized) - { - slot = mixin(expr); - initialized = true; - } - return slot; -} - -//property for \w character class -package(std) @property CodepointSet wordCharacter() @safe -{ - return memoizeExpr!("unicode.Alphabetic | unicode.Mn | unicode.Mc - | unicode.Me | unicode.Nd | unicode.Pc")(); -} - -//basic stack, just in case it gets used anywhere else then Parser -package(std) struct Stack(T) -{ -@safe: - T[] data; - @property bool empty(){ return data.empty; } - - @property size_t length(){ return data.length; } - - void push(T val){ data ~= val; } - - @trusted T pop() - { - assert(!empty); - auto val = data[$ - 1]; - data = data[0 .. $ - 1]; - if (!__ctfe) - cast(void) data.assumeSafeAppend(); - return val; - } - - @property ref T top() - { - assert(!empty); - return data[$ - 1]; - } -} - -//test if a given string starts with hex number of maxDigit that's a valid codepoint -//returns it's value and skips these maxDigit chars on success, throws on failure -package(std) dchar parseUniHex(Range)(ref Range str, size_t maxDigit) -{ - import std.exception : enforce; - //std.conv.parse is both @system and bogus - uint val; - for (int k = 0; k < maxDigit; k++) - { - enforce(!str.empty, "incomplete escape sequence"); - //accepts ascii only, so it's OK to index directly - immutable current = str.front; - if ('0' <= current && current <= '9') - val = val * 16 + current - '0'; - else if ('a' <= current && current <= 'f') - val = val * 16 + current -'a' + 10; - else if ('A' <= current && current <= 'F') - val = val * 16 + current - 'A' + 10; - else - throw new Exception("invalid escape sequence"); - str.popFront(); - } - enforce(val <= 0x10FFFF, "invalid codepoint"); - return val; -} - -@safe unittest -{ - import std.algorithm.searching : canFind; - import std.exception : collectException; - string[] non_hex = [ "000j", "000z", "FffG", "0Z"]; - string[] hex = [ "01", "ff", "00af", "10FFFF" ]; - int[] value = [ 1, 0xFF, 0xAF, 0x10FFFF ]; - foreach (v; non_hex) - assert(collectException(parseUniHex(v, v.length)).msg - .canFind("invalid escape sequence")); - foreach (i, v; hex) - assert(parseUniHex(v, v.length) == value[i]); - string over = "0011FFFF"; - assert(collectException(parseUniHex(over, over.length)).msg - .canFind("invalid codepoint")); -} - -auto caseEnclose(CodepointSet set) -{ - auto cased = set & unicode.LC; - foreach (dchar ch; cased.byCodepoint) - { - foreach (c; simpleCaseFoldings(ch)) - set |= c; - } - return set; -} - -/+ - fetch codepoint set corresponding to a name (InBlock or binary property) -+/ -CodepointSet getUnicodeSet(const scope char[] name, bool negated, bool casefold) @safe -{ - CodepointSet s = unicode(name); - //FIXME: caseEnclose for new uni as Set | CaseEnclose(SET && LC) - if (casefold) - s = caseEnclose(s); - if (negated) - s = s.inverted; - return s; -} - -struct UnicodeSetParser(Range) -{ - import std.exception : enforce; - import std.typecons : tuple, Tuple; - Range range; - bool casefold_; - - @property bool empty(){ return range.empty; } - @property dchar front(){ return range.front; } - void popFront(){ range.popFront(); } - - //CodepointSet operations relatively in order of priority - enum Operator:uint { - Open = 0, Negate, Difference, SymDifference, Intersection, Union, None - } - - //parse unit of CodepointSet spec, most notably escape sequences and char ranges - //also fetches next set operation - Tuple!(CodepointSet,Operator) parseCharTerm() - { - import std.range : drop; - enum privateUseStart = '\U000F0000', privateUseEnd ='\U000FFFFD'; - enum State{ Start, Char, Escape, CharDash, CharDashEscape, - PotentialTwinSymbolOperator } - Operator op = Operator.None; - dchar last; - CodepointSet set; - State state = State.Start; - - void addWithFlags(ref CodepointSet set, uint ch) - { - if (casefold_) - { - auto foldings = simpleCaseFoldings(ch); - foreach (v; foldings) - set |= v; - } - else - set |= ch; - } - - static Operator twinSymbolOperator(dchar symbol) - { - switch (symbol) - { - case '|': - return Operator.Union; - case '-': - return Operator.Difference; - case '~': - return Operator.SymDifference; - case '&': - return Operator.Intersection; - default: - assert(false); - } - } - - L_CharTermLoop: - for (;;) - { - final switch (state) - { - case State.Start: - switch (front) - { - case '|': - case '-': - case '~': - case '&': - state = State.PotentialTwinSymbolOperator; - last = front; - break; - case '[': - op = Operator.Union; - goto case; - case ']': - break L_CharTermLoop; - case '\\': - state = State.Escape; - break; - default: - state = State.Char; - last = front; - } - break; - case State.Char: - // xxx last front xxx - switch (front) - { - case '|': - case '~': - case '&': - // then last is treated as normal char and added as implicit union - state = State.PotentialTwinSymbolOperator; - addWithFlags(set, last); - last = front; - break; - case '-': // still need more info - state = State.CharDash; - break; - case '\\': - set |= last; - state = State.Escape; - break; - case '[': - op = Operator.Union; - goto case; - case ']': - addWithFlags(set, last); - break L_CharTermLoop; - default: - state = State.Char; - addWithFlags(set, last); - last = front; - } - break; - case State.PotentialTwinSymbolOperator: - // xxx last front xxxx - // where last = [|-&~] - if (front == last) - { - op = twinSymbolOperator(last); - popFront();//skip second twin char - break L_CharTermLoop; - } - goto case State.Char; - case State.Escape: - // xxx \ front xxx - switch (front) - { - case 'f': - last = '\f'; - state = State.Char; - break; - case 'n': - last = '\n'; - state = State.Char; - break; - case 'r': - last = '\r'; - state = State.Char; - break; - case 't': - last = '\t'; - state = State.Char; - break; - case 'v': - last = '\v'; - state = State.Char; - break; - case 'c': - last = unicode.parseControlCode(this); - state = State.Char; - break; - foreach (val; Escapables) - { - case val: - } - last = front; - state = State.Char; - break; - case 'p': - set.add(unicode.parsePropertySpec(this, false, casefold_)); - state = State.Start; - continue L_CharTermLoop; //next char already fetched - case 'P': - set.add(unicode.parsePropertySpec(this, true, casefold_)); - state = State.Start; - continue L_CharTermLoop; //next char already fetched - case 'x': - popFront(); - last = parseUniHex(this, 2); - state = State.Char; - continue L_CharTermLoop; - case 'u': - popFront(); - last = parseUniHex(this, 4); - state = State.Char; - continue L_CharTermLoop; - case 'U': - popFront(); - last = parseUniHex(this, 8); - state = State.Char; - continue L_CharTermLoop; - case 'd': - set.add(unicode.Nd); - state = State.Start; - break; - case 'D': - set.add(unicode.Nd.inverted); - state = State.Start; - break; - case 's': - set.add(unicode.White_Space); - state = State.Start; - break; - case 'S': - set.add(unicode.White_Space.inverted); - state = State.Start; - break; - case 'w': - set.add(wordCharacter); - state = State.Start; - break; - case 'W': - set.add(wordCharacter.inverted); - state = State.Start; - break; - default: - if (front >= privateUseStart && front <= privateUseEnd) - enforce(false, "no matching ']' found while parsing character class"); - enforce(false, "invalid escape sequence"); - } - break; - case State.CharDash: - // xxx last - front xxx - switch (front) - { - case '[': - op = Operator.Union; - goto case; - case ']': - //means dash is a single char not an interval specifier - addWithFlags(set, last); - addWithFlags(set, '-'); - break L_CharTermLoop; - case '-'://set Difference again - addWithFlags(set, last); - op = Operator.Difference; - popFront();//skip '-' - break L_CharTermLoop; - case '\\': - state = State.CharDashEscape; - break; - default: - enforce(last <= front, "inverted range"); - if (casefold_) - { - for (uint ch = last; ch <= front; ch++) - addWithFlags(set, ch); - } - else - set.add(last, front + 1); - state = State.Start; - } - break; - case State.CharDashEscape: - //xxx last - \ front xxx - uint end; - switch (front) - { - case 'f': - end = '\f'; - break; - case 'n': - end = '\n'; - break; - case 'r': - end = '\r'; - break; - case 't': - end = '\t'; - break; - case 'v': - end = '\v'; - break; - foreach (val; Escapables) - { - case val: - } - end = front; - break; - case 'c': - end = unicode.parseControlCode(this); - break; - case 'x': - popFront(); - end = parseUniHex(this, 2); - enforce(last <= end,"inverted range"); - set.add(last, end + 1); - state = State.Start; - continue L_CharTermLoop; - case 'u': - popFront(); - end = parseUniHex(this, 4); - enforce(last <= end,"inverted range"); - set.add(last, end + 1); - state = State.Start; - continue L_CharTermLoop; - case 'U': - popFront(); - end = parseUniHex(this, 8); - enforce(last <= end,"inverted range"); - set.add(last, end + 1); - state = State.Start; - continue L_CharTermLoop; - default: - if (front >= privateUseStart && front <= privateUseEnd) - enforce(false, "no matching ']' found while parsing character class"); - enforce(false, "invalid escape sequence"); - } - // Lookahead to check if it's a \T - // where T is sub-pattern terminator in multi-pattern scheme - auto lookahead = range.save.drop(1); - if (end == '\\' && !lookahead.empty) - { - if (lookahead.front >= privateUseStart && lookahead.front <= privateUseEnd) - enforce(false, "no matching ']' found while parsing character class"); - } - enforce(last <= end,"inverted range"); - set.add(last, end + 1); - state = State.Start; - break; - } - popFront(); - enforce(!empty, "unexpected end of CodepointSet"); - } - return tuple(set, op); - } - - alias ValStack = Stack!(CodepointSet); - alias OpStack = Stack!(Operator); - - CodepointSet parseSet() - { - ValStack vstack; - OpStack opstack; - import std.functional : unaryFun; - enforce(!empty, "unexpected end of input"); - enforce(front == '[', "expected '[' at the start of unicode set"); - // - static bool apply(Operator op, ref ValStack stack) - { - switch (op) - { - case Operator.Negate: - enforce(!stack.empty, "no operand for '^'"); - stack.top = stack.top.inverted; - break; - case Operator.Union: - auto s = stack.pop();//2nd operand - enforce(!stack.empty, "no operand for '||'"); - stack.top.add(s); - break; - case Operator.Difference: - auto s = stack.pop();//2nd operand - enforce(!stack.empty, "no operand for '--'"); - stack.top.sub(s); - break; - case Operator.SymDifference: - auto s = stack.pop();//2nd operand - enforce(!stack.empty, "no operand for '~~'"); - stack.top ~= s; - break; - case Operator.Intersection: - auto s = stack.pop();//2nd operand - enforce(!stack.empty, "no operand for '&&'"); - stack.top.intersect(s); - break; - default: - return false; - } - return true; - } - static bool unrollWhile(alias cond)(ref ValStack vstack, ref OpStack opstack) - { - while (cond(opstack.top)) - { - if (!apply(opstack.pop(),vstack)) - return false;//syntax error - if (opstack.empty) - return false; - } - return true; - } - - L_CharsetLoop: - do - { - switch (front) - { - case '[': - opstack.push(Operator.Open); - popFront(); - enforce(!empty, "unexpected end of character class"); - if (front == '^') - { - opstack.push(Operator.Negate); - popFront(); - enforce(!empty, "unexpected end of character class"); - } - else if (front == ']') // []...] is special cased - { - popFront(); - enforce(!empty, "wrong character set"); - auto pair = parseCharTerm(); - pair[0].add(']', ']'+1); - if (pair[1] != Operator.None) - { - if (opstack.top == Operator.Union) - unrollWhile!(unaryFun!"a == a.Union")(vstack, opstack); - opstack.push(pair[1]); - } - vstack.push(pair[0]); - } - break; - case ']': - enforce(unrollWhile!(unaryFun!"a != a.Open")(vstack, opstack), - "character class syntax error"); - enforce(!opstack.empty, "unmatched ']'"); - opstack.pop(); - popFront(); - if (opstack.empty) - break L_CharsetLoop; - auto pair = parseCharTerm(); - if (!pair[0].empty)//not only operator e.g. -- or ~~ - { - vstack.top.add(pair[0]);//apply union - } - if (pair[1] != Operator.None) - { - if (opstack.top == Operator.Union) - unrollWhile!(unaryFun!"a == a.Union")(vstack, opstack); - opstack.push(pair[1]); - } - break; - // - default://yet another pair of term(op)? - auto pair = parseCharTerm(); - if (pair[1] != Operator.None) - { - if (opstack.top == Operator.Union) - unrollWhile!(unaryFun!"a == a.Union")(vstack, opstack); - opstack.push(pair[1]); - } - vstack.push(pair[0]); - } - - }while (!empty || !opstack.empty); - while (!opstack.empty) - apply(opstack.pop(),vstack); - assert(vstack.length == 1); - return vstack.top; - } -} - -/** - A single entry point to lookup Unicode $(CODEPOINT) sets by name or alias of - a block, script or general category. - - It uses well defined standard rules of property name lookup. - This includes fuzzy matching of names, so that - 'White_Space', 'white-SpAce' and 'whitespace' are all considered equal - and yield the same set of white space $(CHARACTERS). -*/ -@safe public struct unicode -{ - import std.exception : enforce; - /** - Performs the lookup of set of $(CODEPOINTS) - with compile-time correctness checking. - This short-cut version combines 3 searches: - across blocks, scripts, and common binary properties. - - Note that since scripts and blocks overlap the - usual trick to disambiguate is used - to get a block use - `unicode.InBlockName`, to search a script - use `unicode.ScriptName`. - - See_Also: $(LREF block), $(LREF script) - and (not included in this search) $(LREF hangulSyllableType). - */ - - static @property auto opDispatch(string name)() pure - { - static if (findAny(name)) - return loadAny(name); - else - static assert(false, "No unicode set by name "~name~" was found."); - } - - /// - @safe unittest - { - import std.exception : collectException; - auto ascii = unicode.ASCII; - assert(ascii['A']); - assert(ascii['~']); - assert(!ascii['\u00e0']); - // matching is case-insensitive - assert(ascii == unicode.ascII); - assert(!ascii['Ă ']); - // underscores, '-' and whitespace in names are ignored too - auto latin = unicode.in_latin1_Supplement; - assert(latin['Ă ']); - assert(!latin['$']); - // BTW Latin 1 Supplement is a block, hence "In" prefix - assert(latin == unicode("In Latin 1 Supplement")); - // run-time look up throws if no such set is found - assert(collectException(unicode("InCyrilliac"))); - } - - /** - The same lookup across blocks, scripts, or binary properties, - but performed at run-time. - This version is provided for cases where `name` - is not known beforehand; otherwise compile-time - checked $(LREF opDispatch) is typically a better choice. - - See the $(S_LINK Unicode properties, table of properties) for available - sets. - */ - static auto opCall(C)(const scope C[] name) - if (is(C : dchar)) - { - return loadAny(name); - } - - /** - Narrows down the search for sets of $(CODEPOINTS) to all Unicode blocks. - - Note: - Here block names are unambiguous as no scripts are searched - and thus to search use simply `unicode.block.BlockName` notation. - - See $(S_LINK Unicode properties, table of properties) for available sets. - See_Also: $(S_LINK Unicode properties, table of properties). - */ - struct block - { - import std.internal.unicode_tables : blocks; // generated file - mixin SetSearcher!(blocks.tab, "block"); - } - - /// - @safe unittest - { - // use .block for explicitness - assert(unicode.block.Greek_and_Coptic == unicode.InGreek_and_Coptic); - } - - /** - Narrows down the search for sets of $(CODEPOINTS) to all Unicode scripts. - - See the $(S_LINK Unicode properties, table of properties) for available - sets. - */ - struct script - { - import std.internal.unicode_tables : scripts; // generated file - mixin SetSearcher!(scripts.tab, "script"); - } - - /// - @safe unittest - { - auto arabicScript = unicode.script.arabic; - auto arabicBlock = unicode.block.arabic; - // there is an intersection between script and block - assert(arabicBlock['؁']); - assert(arabicScript['؁']); - // but they are different - assert(arabicBlock != arabicScript); - assert(arabicBlock == unicode.inArabic); - assert(arabicScript == unicode.arabic); - } - - /** - Fetch a set of $(CODEPOINTS) that have the given hangul syllable type. - - Other non-binary properties (once supported) follow the same - notation - `unicode.propertyName.propertyValue` for compile-time - checked access and `unicode.propertyName(propertyValue)` - for run-time checked one. - - See the $(S_LINK Unicode properties, table of properties) for available - sets. - */ - struct hangulSyllableType - { - import std.internal.unicode_tables : hangul; // generated file - mixin SetSearcher!(hangul.tab, "hangul syllable type"); - } - - /// - @safe unittest - { - // L here is syllable type not Letter as in unicode.L short-cut - auto leadingVowel = unicode.hangulSyllableType("L"); - // check that some leading vowels are present - foreach (vowel; '\u1110'..'\u115F') - assert(leadingVowel[vowel]); - assert(leadingVowel == unicode.hangulSyllableType.L); - } - - //parse control code of form \cXXX, c assumed to be the current symbol - static package(std) dchar parseControlCode(Parser)(ref Parser p) - { - with(p) - { - popFront(); - enforce(!empty, "Unfinished escape sequence"); - enforce(('a' <= front && front <= 'z') - || ('A' <= front && front <= 'Z'), - "Only letters are allowed after \\c"); - return front & 0x1f; - } - } - - //parse and return a CodepointSet for \p{...Property...} and \P{...Property..}, - //\ - assumed to be processed, p - is current - static package(std) CodepointSet parsePropertySpec(Range)(ref Range p, - bool negated, bool casefold) - { - static import std.ascii; - with(p) - { - enum MAX_PROPERTY = 128; - char[MAX_PROPERTY] result; - uint k = 0; - popFront(); - enforce(!empty, "eof parsing unicode property spec"); - if (front == '{') - { - popFront(); - while (k < MAX_PROPERTY && !empty && front !='}' - && front !=':') - { - if (front != '-' && front != ' ' && front != '_') - result[k++] = cast(char) std.ascii.toLower(front); - popFront(); - } - enforce(k != MAX_PROPERTY, "invalid property name"); - enforce(front == '}', "} expected "); - } - else - {//single char properties e.g.: \pL, \pN ... - enforce(front < 0x80, "invalid property name"); - result[k++] = cast(char) front; - } - auto s = getUnicodeSet(result[0 .. k], negated, casefold); - enforce(!s.empty, "unrecognized unicode property spec"); - popFront(); - return s; - } - } - - /** - Parse unicode codepoint set from given `range` using standard regex - syntax '[...]'. The range is advanced skiping over regex set definition. - `casefold` parameter determines if the set should be casefolded - that is - include both lower and upper case versions for any letters in the set. - */ - static CodepointSet parseSet(Range)(ref Range range, bool casefold=false) - if (isInputRange!Range && is(ElementType!Range : dchar)) - { - auto usParser = UnicodeSetParser!Range(range, casefold); - auto set = usParser.parseSet(); - range = usParser.range; - return set; - } - - /// - @safe unittest - { - import std.uni : unicode; - string pat = "[a-zA-Z0-9]hello"; - auto set = unicode.parseSet(pat); - // check some of the codepoints - assert(set['a'] && set['A'] && set['9']); - assert(pat == "hello"); - } - -private: - alias ucmp = comparePropertyName; - - static bool findAny(string name) - { - import std.internal.unicode_tables : blocks, scripts, uniProps; // generated file - return isPrettyPropertyName(name) - || findSetName!(uniProps.tab)(name) || findSetName!(scripts.tab)(name) - || (ucmp(name[0 .. 2],"In") == 0 && findSetName!(blocks.tab)(name[2..$])); - } - - static auto loadAny(Set=CodepointSet, C)(const scope C[] name) pure - { - import std.conv : to; - import std.internal.unicode_tables : blocks, scripts; // generated file - Set set; - immutable loaded = loadProperty(name, set) || loadUnicodeSet!(scripts.tab)(name, set) - || (name.length > 2 && ucmp(name[0 .. 2],"In") == 0 - && loadUnicodeSet!(blocks.tab)(name[2..$], set)); - if (loaded) - return set; - throw new Exception("No unicode set by name "~name.to!string()~" was found."); - } - - // FIXME: re-disable once the compiler is fixed - // Disabled to prevent the mistake of creating instances of this pseudo-struct. - //@disable ~this(); -} - -@safe unittest -{ - import std.internal.unicode_tables : blocks, uniProps; // generated file - assert(unicode("InHebrew") == asSet(blocks.Hebrew)); - assert(unicode("separator") == (asSet(uniProps.Zs) | asSet(uniProps.Zl) | asSet(uniProps.Zp))); - assert(unicode("In-Kharoshthi") == asSet(blocks.Kharoshthi)); -} - -enum EMPTY_CASE_TRIE = ushort.max;// from what gen_uni uses internally - -// TODO: redo the most of hangul stuff algorithmically in case of Graphemes too -// Use combined trie instead of checking for '\r' | '\n' | ccTrie, -// or extend | '\u200D' separately - -private static bool isRegionalIndicator(dchar ch) @safe pure @nogc nothrow -{ - return ch >= '\U0001F1E6' && ch <= '\U0001F1FF'; -} - -// Our grapheme decoder is a state machine, this is list of all possible -// states before each code point. -private enum GraphemeState -{ - Start, - CR, - RI, - L, - V, - LVT, - Emoji, - EmojiZWJ, - Prepend, - End -} - -// Message values whether end of grapheme is reached -private enum TransformRes -{ - // No, unless the source range ends here - // (GB2 - break at end of text, unless text is empty) - goOn, - redo, // Run last character again with new state - retInclude, // Yes, after the just iterated character - retExclude // Yes, before the just iterated character -} - -// The logic of the grapheme decoding is all here -// GB# means Grapheme Breaking rule number # - see Unicode standard annex #29 -// Note, getting GB1 (break at start of text, unless text is empty) right -// relies on the user starting grapheme walking from beginning of the text, and -// not attempting to walk an empty text. -private enum TransformRes - function(ref GraphemeState, dchar) @safe pure nothrow @nogc [] graphemeTransforms = -[ - GraphemeState.Start: (ref state, ch) - { - // GB4. Break after controls. - if (graphemeControlTrie[ch] || ch == '\n') - return TransformRes.retInclude; - - with (GraphemeState) state = - ch == '\r' ? CR : - isRegionalIndicator(ch) ? RI : - isHangL(ch) ? L : - hangLV[ch] || isHangV(ch) ? V : - hangLVT[ch] || isHangT(ch) ? LVT : - prependTrie[ch] ? Prepend : - xpictoTrie[ch] ? Emoji : - End; - - // No matter what we encountered, we always include the - // first code point in the grapheme. - return TransformRes.goOn; - }, - - // GB3, GB4. Do not break between a CR and LF. - // Otherwise, break after controls. - GraphemeState.CR: (ref state, ch) => ch == '\n' ? - TransformRes.retInclude : - TransformRes.retExclude, - - // GB12 - GB13. Do not break within emoji flag sequences. - // That is, do not break between regional indicator (RI) symbols if - // there is an odd number of RI characters before the break point. - // This state applies if one and only one RI code point has been - // encountered. - GraphemeState.RI: (ref state, ch) - { - state = GraphemeState.End; - - return isRegionalIndicator(ch) ? - TransformRes.goOn : - TransformRes.redo; - }, - - // GB6. Do not break Hangul syllable sequences. - GraphemeState.L: (ref state, ch) - { - if (isHangL(ch)) - return TransformRes.goOn; - else if (isHangV(ch) || hangLV[ch]) - { - state = GraphemeState.V; - return TransformRes.goOn; - } - else if (hangLVT[ch]) - { - state = GraphemeState.LVT; - return TransformRes.goOn; - } - - state = GraphemeState.End; - return TransformRes.redo; - }, - - // GB7. Do not break Hangul syllable sequences. - GraphemeState.V: (ref state, ch) - { - if (isHangV(ch)) - return TransformRes.goOn; - else if (isHangT(ch)) - { - state = GraphemeState.LVT; - return TransformRes.goOn; - } - - state = GraphemeState.End; - return TransformRes.redo; - }, - - // GB8. Do not break Hangul syllable sequences. - GraphemeState.LVT: (ref state, ch) - { - if (isHangT(ch)) - return TransformRes.goOn; - - state = GraphemeState.End; - return TransformRes.redo; - }, - - // GB11. Do not break within emoji modifier sequences or emoji - // zwj sequences. This state applies when the last code point was - // NOT a ZWJ. - GraphemeState.Emoji: (ref state, ch) - { - if (graphemeExtendTrie[ch]) - return TransformRes.goOn; - - static assert(!graphemeExtendTrie['\u200D']); - - if (ch == '\u200D') - { - state = GraphemeState.EmojiZWJ; - return TransformRes.goOn; - } - - state = GraphemeState.End; - // There might still be spacing marks are - // at the end, which are not allowed in - // middle of emoji sequences - return TransformRes.redo; - }, - - // GB11. Do not break within emoji modifier sequences or emoji - // zwj sequences. This state applies when the last code point was - // a ZWJ. - GraphemeState.EmojiZWJ: (ref state, ch) - { - state = GraphemeState.Emoji; - if (xpictoTrie[ch]) - return TransformRes.goOn; - return TransformRes.redo; - }, - - // GB9b. Do not break after Prepend characters. - GraphemeState.Prepend: (ref state, ch) - { - // GB5. Break before controls. - if (graphemeControlTrie[ch] || ch == '\r' || ch == '\n') - return TransformRes.retExclude; - - state = GraphemeState.Start; - return TransformRes.redo; - }, - - // GB9, GB9a. Do not break before extending characters, ZWJ - // or SpacingMarks. - // GB999. Otherwise, break everywhere. - GraphemeState.End: (ref state, ch) - => !graphemeExtendTrie[ch] && !spacingMarkTrie[ch] && ch != '\u200D' ? - TransformRes.retExclude : - TransformRes.goOn -]; - -template genericDecodeGrapheme(bool getValue) -{ - static if (getValue) - alias Value = Grapheme; - else - alias Value = void; - - Value genericDecodeGrapheme(Input)(ref Input range) - { - static if (getValue) - Grapheme grapheme; - auto state = GraphemeState.Start; - dchar ch; - - assert(!range.empty, "Attempting to decode grapheme from an empty " ~ Input.stringof); - outer: - while (!range.empty) - { - ch = range.front; - - rerun: - final switch (graphemeTransforms[state](state, ch)) - with(TransformRes) - { - case goOn: - static if (getValue) - grapheme ~= ch; - range.popFront(); - continue; - - case redo: - goto rerun; - - case retInclude: - static if (getValue) - grapheme ~= ch; - range.popFront(); - break outer; - - case retExclude: - break outer; - } - } - - static if (getValue) - return grapheme; - } -} - -public: // Public API continues - -/++ - Computes the length of grapheme cluster starting at `index`. - Both the resulting length and the `index` are measured - in $(S_LINK Code unit, code units). - - Params: - C = type that is implicitly convertible to `dchars` - input = array of grapheme clusters - index = starting index into `input[]` - - Returns: - length of grapheme cluster -+/ -size_t graphemeStride(C)(const scope C[] input, size_t index) @safe pure -if (is(C : dchar)) -{ - auto src = input[index..$]; - auto n = src.length; - genericDecodeGrapheme!(false)(src); - return n - src.length; -} - -/// -@safe unittest -{ - assert(graphemeStride(" ", 1) == 1); - // A + combing ring above - string city = "A\u030Arhus"; - size_t first = graphemeStride(city, 0); - assert(first == 3); //\u030A has 2 UTF-8 code units - assert(city[0 .. first] == "A\u030A"); - assert(city[first..$] == "rhus"); -} - -@safe unittest -{ - // Ensure that graphemeStride is usable from CTFE. - enum c1 = graphemeStride("A", 0); - static assert(c1 == 1); - - enum c2 = graphemeStride("A\u0301", 0); - static assert(c2 == 3); // \u0301 has 2 UTF-8 code units -} - -// TODO: make this @nogc. Probably no big deal since the state machine is -// already GC-free. -@safe pure nothrow unittest -{ - // grinning face ~ emoji modifier fitzpatrick type-5 ~ grinning face - assert(graphemeStride("\U0001F600\U0001f3FE\U0001F600"d, 0) == 2); - // skier ~ female sign ~ '€' - assert(graphemeStride("\u26F7\u2640€"d, 0) == 1); - // skier ~ emoji modifier fitzpatrick type-5 ~ female sign ~ '€' - assert(graphemeStride("\u26F7\U0001f3FE\u2640€"d, 0) == 2); - // skier ~ zero-width joiner ~ female sign ~ '€' - assert(graphemeStride("\u26F7\u200D\u2640€"d, 0) == 3); - // skier ~ emoji modifier fitzpatrick type-5 ~ zero-width joiner - // ~ female sign ~ '€' - assert(graphemeStride("\u26F7\U0001f3FE\u200D\u2640€"d, 0) == 4); - // skier ~ zero-width joiner ~ '€' - assert(graphemeStride("\u26F7\u200D€"d, 0) == 2); - //'€' ~ zero-width joiner ~ skier - assert(graphemeStride("€\u200D\u26F7"d, 0) == 2); - // Kaithi number sign ~ Devanagari digit four ~ Devanagari digit two - assert(graphemeStride("\U000110BD\u096A\u0968"d, 0) == 2); - // Kaithi number sign ~ null - assert(graphemeStride("\U000110BD\0"d, 0) == 1); -} - -/++ - Reads one full grapheme cluster from an - $(REF_ALTTEXT input range, isInputRange, std,range,primitives) of dchar `inp`. - - For examples see the $(LREF Grapheme) below. - - Note: - This function modifies `inp` and thus `inp` - must be an L-value. -+/ -Grapheme decodeGrapheme(Input)(ref Input inp) -if (isInputRange!Input && is(immutable ElementType!Input == immutable dchar)) -{ - return genericDecodeGrapheme!true(inp); -} - -@safe unittest -{ - import std.algorithm.comparison : equal; - - Grapheme gr; - string s = " \u0020\u0308 "; - gr = decodeGrapheme(s); - assert(gr.length == 1 && gr[0] == ' '); - gr = decodeGrapheme(s); - assert(gr.length == 2 && equal(gr[0 .. 2], " \u0308")); - s = "\u0300\u0308\u1100"; - assert(equal(decodeGrapheme(s)[], "\u0300\u0308")); - assert(equal(decodeGrapheme(s)[], "\u1100")); - s = "\u11A8\u0308\uAC01"; - assert(equal(decodeGrapheme(s)[], "\u11A8\u0308")); - assert(equal(decodeGrapheme(s)[], "\uAC01")); - - // Two Union Jacks of the Great Britain - s = "\U0001F1EC\U0001F1E7\U0001F1EC\U0001F1E7"; - assert(equal(decodeGrapheme(s)[], "\U0001F1EC\U0001F1E7")); -} - -/++ - $(P Iterate a string by $(LREF Grapheme).) - - $(P Useful for doing string manipulation that needs to be aware - of graphemes.) - - See_Also: - $(LREF byCodePoint) -+/ -auto byGrapheme(Range)(Range range) -if (isInputRange!Range && is(immutable ElementType!Range == immutable dchar)) -{ - // TODO: Bidirectional access - static struct Result(R) - { - private R _range; - private Grapheme _front; - - bool empty() @property - { - return _front.length == 0; - } - - Grapheme front() @property - { - return _front; - } - - void popFront() - { - _front = _range.empty ? Grapheme.init : _range.decodeGrapheme(); - } - - static if (isForwardRange!R) - { - Result save() @property - { - return Result(_range.save, _front); - } - } - } - - auto result = Result!(Range)(range); - result.popFront(); - return result; -} - -/// -@safe unittest -{ - import std.algorithm.comparison : equal; - import std.range.primitives : walkLength; - import std.range : take, drop; - auto text = "noe\u0308l"; // noĂŤl using e + combining diaeresis - assert(text.walkLength == 5); // 5 code points - - auto gText = text.byGrapheme; - assert(gText.walkLength == 4); // 4 graphemes - - assert(gText.take(3).equal("noe\u0308".byGrapheme)); - assert(gText.drop(3).equal("l".byGrapheme)); -} - -// For testing non-forward-range input ranges -version (StdUnittest) -private static @safe struct InputRangeString -{ - private string s; - - bool empty() @property { return s.empty; } - dchar front() @property { return s.front; } - void popFront() { s.popFront(); } -} - -@safe unittest -{ - import std.algorithm.comparison : equal; - import std.array : array; - import std.range : retro; - import std.range.primitives : walkLength; - assert("".byGrapheme.walkLength == 0); - - auto reverse = "le\u0308on"; - assert(reverse.walkLength == 5); - - auto gReverse = reverse.byGrapheme; - assert(gReverse.walkLength == 4); - - static foreach (text; AliasSeq!("noe\u0308l"c, "noe\u0308l"w, "noe\u0308l"d)) - {{ - assert(text.walkLength == 5); - static assert(isForwardRange!(typeof(text))); - - auto gText = text.byGrapheme; - static assert(isForwardRange!(typeof(gText))); - assert(gText.walkLength == 4); - assert(gText.array.retro.equal(gReverse)); - }} - - auto nonForwardRange = InputRangeString("noe\u0308l").byGrapheme; - static assert(!isForwardRange!(typeof(nonForwardRange))); - assert(nonForwardRange.walkLength == 4); -} - -// Issue 23474 -@safe pure unittest -{ - import std.range.primitives : walkLength; - assert(byGrapheme("\r\u0308").walkLength == 2); -} - -/++ - $(P Lazily transform a range of $(LREF Grapheme)s to a range of code points.) - - $(P Useful for converting the result to a string after doing operations - on graphemes.) - - $(P If passed in a range of code points, returns a range with equivalent capabilities.) -+/ -auto byCodePoint(Range)(Range range) -if (isInputRange!Range && is(immutable ElementType!Range == immutable Grapheme)) -{ - // TODO: Propagate bidirectional access - static struct Result - { - private Range _range; - private size_t i = 0; - - bool empty() @property - { - return _range.empty; - } - - dchar front() @property - { - return _range.front[i]; - } - - void popFront() - { - ++i; - - if (i >= _range.front.length) - { - _range.popFront(); - i = 0; - } - } - - static if (isForwardRange!Range) - { - Result save() @property - { - return Result(_range.save, i); - } - } - } - - return Result(range); -} - -/// Ditto -auto byCodePoint(Range)(Range range) -if (isInputRange!Range && is(immutable ElementType!Range == immutable dchar)) -{ - import std.range.primitives : isBidirectionalRange, popBack; - import std.traits : isNarrowString; - static if (isNarrowString!Range) - { - static struct Result - { - private Range _range; - @property bool empty() { return _range.empty; } - @property dchar front(){ return _range.front; } - void popFront(){ _range.popFront; } - @property auto save() { return Result(_range.save); } - @property dchar back(){ return _range.back; } - void popBack(){ _range.popBack; } - } - static assert(isBidirectionalRange!(Result)); - return Result(range); - } - else - return range; -} - -/// -@safe unittest -{ - import std.array : array; - import std.conv : text; - import std.range : retro; - - string s = "noe\u0308l"; // noĂŤl - - // reverse it and convert the result to a string - string reverse = s.byGrapheme - .array - .retro - .byCodePoint - .text; - - assert(reverse == "le\u0308on"); // lĂŤon -} - -@safe unittest -{ - import std.algorithm.comparison : equal; - import std.range.primitives : walkLength; - import std.range : retro; - assert("".byGrapheme.byCodePoint.equal("")); - - string text = "noe\u0308l"; - static assert(!__traits(compiles, "noe\u0308l".byCodePoint.length)); - - auto gText = InputRangeString(text).byGrapheme; - static assert(!isForwardRange!(typeof(gText))); - - auto cpText = gText.byCodePoint; - static assert(!isForwardRange!(typeof(cpText))); - - assert(cpText.walkLength == text.walkLength); - - auto plainCp = text.byCodePoint; - static assert(isForwardRange!(typeof(plainCp))); - assert(equal(plainCp, text)); - assert(equal(retro(plainCp.save), retro(text.save))); - // Check that we still have length for dstring - assert("айвгд"d.byCodePoint.length == 5); -} - -/++ - $(P A structure designed to effectively pack $(CHARACTERS) - of a $(CLUSTER). - ) - - $(P `Grapheme` has value semantics so 2 copies of a `Grapheme` - always refer to distinct objects. In most actual scenarios a `Grapheme` - fits on the stack and avoids memory allocation overhead for all but quite - long clusters. - ) - - See_Also: $(LREF decodeGrapheme), $(LREF graphemeStride) -+/ -@safe struct Grapheme -{ - import std.exception : enforce; - import std.traits : isDynamicArray; - -public: - /// Ctor - this(C)(const scope C[] chars...) - if (is(C : dchar)) - { - this ~= chars; - } - - ///ditto - this(Input)(Input seq) - if (!isDynamicArray!Input - && isInputRange!Input && is(ElementType!Input : dchar)) - { - this ~= seq; - } - - /// Gets a $(CODEPOINT) at the given index in this cluster. - dchar opIndex(size_t index) const @nogc nothrow pure @trusted - { - assert(index < length); - return read24(isBig ? ptr_ : small_.ptr, index); - } - - /++ - Writes a $(CODEPOINT) `ch` at given index in this cluster. - - Warning: - Use of this facility may invalidate grapheme cluster, - see also $(LREF Grapheme.valid). - +/ - void opIndexAssign(dchar ch, size_t index) @nogc nothrow pure @trusted - { - assert(index < length); - write24(isBig ? ptr_ : small_.ptr, ch, index); - } - - /// - @safe unittest - { - auto g = Grapheme("A\u0302"); - assert(g[0] == 'A'); - assert(g.valid); - g[1] = '~'; // ASCII tilda is not a combining mark - assert(g[1] == '~'); - assert(!g.valid); - } - - /++ - Random-access range over Grapheme's $(CHARACTERS). - - Warning: Invalidates when this Grapheme leaves the scope, - attempts to use it then would lead to memory corruption. - +/ - SliceOverIndexed!Grapheme opSlice(size_t a, size_t b) @nogc nothrow pure return - { - return sliceOverIndexed(a, b, &this); - } - - /// ditto - SliceOverIndexed!Grapheme opSlice() @nogc nothrow pure return - { - return sliceOverIndexed(0, length, &this); - } - - /// Grapheme cluster length in $(CODEPOINTS). - @property size_t length() const @nogc nothrow pure - { - return isBig ? len_ : slen_ & 0x7F; - } - - /++ - Append $(CHARACTER) `ch` to this grapheme. - Warning: - Use of this facility may invalidate grapheme cluster, - see also `valid`. - - See_Also: $(LREF Grapheme.valid) - +/ - ref opOpAssign(string op)(dchar ch) @trusted - { - static if (op == "~") - { - import std.internal.memory : enforceRealloc; - if (!isBig) - { - if (slen_ == small_cap) - convertToBig();// & fallthrough to "big" branch - else - { - write24(small_.ptr, ch, smallLength); - slen_++; - return this; - } - } - - assert(isBig); - if (len_ == cap_) - { - import core.checkedint : addu, mulu; - bool overflow; - cap_ = addu(cap_, grow, overflow); - auto nelems = mulu(3, addu(cap_, 1, overflow), overflow); - if (overflow) assert(0); - ptr_ = cast(ubyte*) enforceRealloc(ptr_, nelems); - } - write24(ptr_, ch, len_++); - return this; - } - else - static assert(false, "No operation "~op~" defined for Grapheme"); - } - - /// - @safe unittest - { - import std.algorithm.comparison : equal; - auto g = Grapheme("A"); - assert(g.valid); - g ~= '\u0301'; - assert(g[].equal("A\u0301")); - assert(g.valid); - g ~= "B"; - // not a valid grapheme cluster anymore - assert(!g.valid); - // still could be useful though - assert(g[].equal("A\u0301B")); - } - - /// Append all $(CHARACTERS) from the input range `inp` to this Grapheme. - ref opOpAssign(string op, Input)(scope Input inp) - if (isInputRange!Input && is(ElementType!Input : dchar)) - { - static if (op == "~") - { - foreach (dchar ch; inp) - this ~= ch; - return this; - } - else - static assert(false, "No operation "~op~" defined for Grapheme"); - } - - // This is not a good `opEquals`, but formerly the automatically generated - // opEquals was used, which was inferred `@safe` because of bugzilla 20655: - // https://issues.dlang.org/show_bug.cgi?id=20655 - // This `@trusted opEquals` is only here to prevent breakage. - bool opEquals(R)(const auto ref R other) const @trusted - { - return this.tupleof == other.tupleof; - } - - // Define a default toHash to allow AA usage - size_t toHash() const @trusted - { - return hashOf(slen_, hashOf(small_)); - } - - /++ - True if this object contains valid extended grapheme cluster. - Decoding primitives of this module always return a valid `Grapheme`. - - Appending to and direct manipulation of grapheme's $(CHARACTERS) may - render it no longer valid. Certain applications may chose to use - Grapheme as a "small string" of any $(CODEPOINTS) and ignore this property - entirely. - +/ - @property bool valid()() /*const*/ - { - auto r = this[]; - genericDecodeGrapheme!false(r); - return r.length == 0; - } - - this(this) @nogc nothrow pure @trusted - { - import std.internal.memory : enforceMalloc; - if (isBig) - {// dup it - import core.checkedint : addu, mulu; - bool overflow; - auto raw_cap = mulu(3, addu(cap_, 1, overflow), overflow); - if (overflow) assert(0); - - auto p = cast(ubyte*) enforceMalloc(raw_cap); - p[0 .. raw_cap] = ptr_[0 .. raw_cap]; - ptr_ = p; - } - } - - ~this() @nogc nothrow pure @trusted - { - import core.memory : pureFree; - if (isBig) - { - pureFree(ptr_); - } - } - - -private: - enum small_bytes = ((ubyte*).sizeof+3*size_t.sizeof-1); - // "out of the blue" grow rate, needs testing - // (though graphemes are typically small < 9) - enum grow = 20; - enum small_cap = small_bytes/3; - enum small_flag = 0x80, small_mask = 0x7F; - // 16 bytes in 32bits, should be enough for the majority of cases - union - { - struct - { - ubyte* ptr_; - size_t cap_; - size_t len_; - size_t padding_; - } - struct - { - ubyte[small_bytes] small_; - ubyte slen_; - } - } - - void convertToBig() @nogc nothrow pure @trusted - { - import std.internal.memory : enforceMalloc; - static assert(grow.max / 3 - 1 >= grow); - enum nbytes = 3 * (grow + 1); - size_t k = smallLength; - ubyte* p = cast(ubyte*) enforceMalloc(nbytes); - for (int i=0; i<k; i++) - write24(p, read24(small_.ptr, i), i); - // now we can overwrite small array data - ptr_ = p; - len_ = slen_; - assert(grow > len_); - cap_ = grow; - setBig(); - } - - void setBig() @nogc nothrow pure { slen_ |= small_flag; } - - @property size_t smallLength() const @nogc nothrow pure - { - return slen_ & small_mask; - } - @property ubyte isBig() const @nogc nothrow pure - { - return slen_ & small_flag; - } -} - -static assert(Grapheme.sizeof == size_t.sizeof*4); - - -@safe pure /*nothrow @nogc*/ unittest // TODO: string .front is GC and throw -{ - import std.algorithm.comparison : equal; - Grapheme[3] data = [Grapheme("ĐŽ"), Grapheme("ĐŁ"), Grapheme("З")]; - assert(byGrapheme("ЮУЗ").equal(data[])); -} - -/// -@safe unittest -{ - import std.algorithm.comparison : equal; - import std.algorithm.iteration : filter; - import std.range : isRandomAccessRange; - - string bold = "ku\u0308hn"; - - // note that decodeGrapheme takes parameter by ref - auto first = decodeGrapheme(bold); - - assert(first.length == 1); - assert(first[0] == 'k'); - - // the next grapheme is 2 characters long - auto wideOne = decodeGrapheme(bold); - // slicing a grapheme yields a random-access range of dchar - assert(wideOne[].equal("u\u0308")); - assert(wideOne.length == 2); - static assert(isRandomAccessRange!(typeof(wideOne[]))); - - // all of the usual range manipulation is possible - assert(wideOne[].filter!isMark().equal("\u0308")); - - auto g = Grapheme("A"); - assert(g.valid); - g ~= '\u0301'; - assert(g[].equal("A\u0301")); - assert(g.valid); - g ~= "B"; - // not a valid grapheme cluster anymore - assert(!g.valid); - // still could be useful though - assert(g[].equal("A\u0301B")); -} - -@safe unittest -{ - auto g = Grapheme("A\u0302"); - assert(g[0] == 'A'); - assert(g.valid); - g[1] = '~'; // ASCII tilda is not a combining mark - assert(g[1] == '~'); - assert(!g.valid); -} - -@safe unittest -{ - import std.algorithm.comparison : equal; - import std.algorithm.iteration : map; - import std.conv : text; - import std.range : iota; - - // not valid clusters (but it just a test) - auto g = Grapheme('a', 'b', 'c', 'd', 'e'); - assert(g[0] == 'a'); - assert(g[1] == 'b'); - assert(g[2] == 'c'); - assert(g[3] == 'd'); - assert(g[4] == 'e'); - g[3] = 'Й'; - assert(g[2] == 'c'); - assert(g[3] == 'Й', text(g[3], " vs ", 'Й')); - assert(g[4] == 'e'); - assert(!g.valid); - - g ~= 'ц'; - g ~= '~'; - assert(g[0] == 'a'); - assert(g[1] == 'b'); - assert(g[2] == 'c'); - assert(g[3] == 'Й'); - assert(g[4] == 'e'); - assert(g[5] == 'ц'); - assert(g[6] == '~'); - assert(!g.valid); - - Grapheme copy = g; - copy[0] = 'X'; - copy[1] = '-'; - assert(g[0] == 'a' && copy[0] == 'X'); - assert(g[1] == 'b' && copy[1] == '-'); - assert(equal(g[2 .. g.length], copy[2 .. copy.length])); - copy = Grapheme("АБВГДЕЁЖЗИКЛМ"); - assert(equal(copy[0 .. 8], "АБВГДЕЁЖ"), text(copy[0 .. 8])); - copy ~= "xyz"; - assert(equal(copy[13 .. 15], "xy"), text(copy[13 .. 15])); - assert(!copy.valid); - - Grapheme h; - foreach (dchar v; iota(cast(int)'A', cast(int)'Z'+1).map!"cast(dchar)a"()) - h ~= v; - assert(equal(h[], iota(cast(int)'A', cast(int)'Z'+1))); -} - -// ensure Grapheme can be used as an AA key. -@safe unittest -{ - int[Grapheme] aa; -} - -/++ - $(P Does basic case-insensitive comparison of `r1` and `r2`. - This function uses simpler comparison rule thus achieving better performance - than $(LREF icmp). However keep in mind the warning below.) - - Params: - r1 = an $(REF_ALTTEXT input range, isInputRange, std,range,primitives) of characters - r2 = an $(REF_ALTTEXT input range, isInputRange, std,range,primitives) of characters - - Returns: - An `int` that is 0 if the strings match, - <0 if `r1` is lexicographically "less" than `r2`, - >0 if `r1` is lexicographically "greater" than `r2` - - Warning: - This function only handles 1:1 $(CODEPOINT) mapping - and thus is not sufficient for certain alphabets - like German, Greek and few others. - - See_Also: - $(LREF icmp) - $(REF cmp, std,algorithm,comparison) -+/ -int sicmp(S1, S2)(scope S1 r1, scope S2 r2) -if (isInputRange!S1 && isSomeChar!(ElementEncodingType!S1) - && isInputRange!S2 && isSomeChar!(ElementEncodingType!S2)) -{ - import std.internal.unicode_tables : sTable = simpleCaseTable; // generated file - import std.range.primitives : isInfinite; - import std.utf : decodeFront; - import std.traits : isDynamicArray; - import std.typecons : Yes; - static import std.ascii; - - static if ((isDynamicArray!S1 || isRandomAccessRange!S1) - && (isDynamicArray!S2 || isRandomAccessRange!S2) - && !(isInfinite!S1 && isInfinite!S2) - && __traits(compiles, - { - size_t s = size_t.sizeof / 2; - r1 = r1[s .. $]; - r2 = r2[s .. $]; - })) - {{ - // ASCII optimization for dynamic arrays & similar. - size_t i = 0; - static if (isInfinite!S1) - immutable end = r2.length; - else static if (isInfinite!S2) - immutable end = r1.length; - else - immutable end = r1.length > r2.length ? r2.length : r1.length; - for (; i < end; ++i) - { - auto lhs = r1[i]; - auto rhs = r2[i]; - if ((lhs | rhs) >= 0x80) goto NonAsciiPath; - if (lhs == rhs) continue; - auto lowDiff = std.ascii.toLower(lhs) - std.ascii.toLower(rhs); - if (lowDiff) return lowDiff; - } - static if (isInfinite!S1) - return 1; - else static if (isInfinite!S2) - return -1; - else - return (r1.length > r2.length) - (r2.length > r1.length); - - NonAsciiPath: - r1 = r1[i .. $]; - r2 = r2[i .. $]; - // Fall through to standard case. - }} - - while (!r1.empty) - { - immutable lhs = decodeFront!(Yes.useReplacementDchar)(r1); - if (r2.empty) - return 1; - immutable rhs = decodeFront!(Yes.useReplacementDchar)(r2); - int diff = lhs - rhs; - if (!diff) - continue; - if ((lhs | rhs) < 0x80) - { - immutable d = std.ascii.toLower(lhs) - std.ascii.toLower(rhs); - if (!d) continue; - return d; - } - size_t idx = simpleCaseTrie[lhs]; - size_t idx2 = simpleCaseTrie[rhs]; - // simpleCaseTrie is packed index table - if (idx != EMPTY_CASE_TRIE) - { - if (idx2 != EMPTY_CASE_TRIE) - {// both cased chars - // adjust idx --> start of bucket - idx = idx - sTable(idx).n; - idx2 = idx2 - sTable(idx2).n; - if (idx == idx2)// one bucket, equivalent chars - continue; - else// not the same bucket - diff = sTable(idx).ch - sTable(idx2).ch; - } - else - diff = sTable(idx - sTable(idx).n).ch - rhs; - } - else if (idx2 != EMPTY_CASE_TRIE) - { - diff = lhs - sTable(idx2 - sTable(idx2).n).ch; - } - // one of chars is not cased at all - return diff; - } - return int(r2.empty) - 1; -} - -/// -@safe @nogc pure nothrow unittest -{ - assert(sicmp("Август", "авгусТ") == 0); - // Greek also works as long as there is no 1:M mapping in sight - assert(sicmp("ΌΎ", "όύ") == 0); - // things like the following won't get matched as equal - // Greek small letter iota with dialytika and tonos - assert(sicmp("ΐ", "\u03B9\u0308\u0301") != 0); - - // while icmp has no problem with that - assert(icmp("ΐ", "\u03B9\u0308\u0301") == 0); - assert(icmp("ΌΎ", "όύ") == 0); -} - -// overloads for the most common cases to reduce compile time -@safe @nogc pure nothrow -{ - int sicmp(scope const(char)[] str1, scope const(char)[] str2) - { return sicmp!(const(char)[], const(char)[])(str1, str2); } - - int sicmp(scope const(wchar)[] str1, scope const(wchar)[] str2) - { return sicmp!(const(wchar)[], const(wchar)[])(str1, str2); } - - int sicmp(scope const(dchar)[] str1, scope const(dchar)[] str2) - { return sicmp!(const(dchar)[], const(dchar)[])(str1, str2); } -} - -private int fullCasedCmp(Range)(dchar lhs, dchar rhs, ref Range rtail) -{ - import std.algorithm.searching : skipOver; - import std.internal.unicode_tables : fullCaseTable; // generated file - alias fTable = fullCaseTable; - size_t idx = fullCaseTrie[lhs]; - // fullCaseTrie is packed index table - if (idx == EMPTY_CASE_TRIE) - return lhs; - immutable start = idx - fTable(idx).n; - immutable end = fTable(idx).size + start; - assert(fTable(start).entry_len == 1); - for (idx=start; idx<end; idx++) - { - const entryLen = fTable(idx).entry_len; - if (entryLen == 1) - { - if (fTable(idx).seq[0] == rhs) - { - return 0; - } - } - else - {// OK it's a long chunk, like 'ss' for German - dchar[3] arr = fTable(idx).seq; - const dchar[] seq = arr[0 .. entryLen]; - if (rhs == seq[0] - && rtail.skipOver(seq[1..$])) - { - // note that this path modifies rtail - // iff we managed to get there - return 0; - } - } - } - return fTable(start).seq[0]; // new remapped character for accurate diffs -} - -/++ - Does case insensitive comparison of `r1` and `r2`. - Follows the rules of full case-folding mapping. - This includes matching as equal german ß with "ss" and - other 1:M $(CODEPOINT) mappings unlike $(LREF sicmp). - The cost of `icmp` being pedantically correct is - slightly worse performance. - - Params: - r1 = a forward range of characters - r2 = a forward range of characters - - Returns: - An `int` that is 0 if the strings match, - <0 if `str1` is lexicographically "less" than `str2`, - >0 if `str1` is lexicographically "greater" than `str2` - - See_Also: - $(LREF sicmp) - $(REF cmp, std,algorithm,comparison) -+/ -int icmp(S1, S2)(S1 r1, S2 r2) -if (isForwardRange!S1 && isSomeChar!(ElementEncodingType!S1) - && isForwardRange!S2 && isSomeChar!(ElementEncodingType!S2)) -{ - import std.range.primitives : isInfinite; - import std.traits : isDynamicArray; - import std.utf : byDchar; - static import std.ascii; - - static if ((isDynamicArray!S1 || isRandomAccessRange!S1) - && (isDynamicArray!S2 || isRandomAccessRange!S2) - && !(isInfinite!S1 && isInfinite!S2) - && __traits(compiles, - { - size_t s = size_t.max / 2; - r1 = r1[s .. $]; - r2 = r2[s .. $]; - })) - {{ - // ASCII optimization for dynamic arrays & similar. - size_t i = 0; - static if (isInfinite!S1) - immutable end = r2.length; - else static if (isInfinite!S2) - immutable end = r1.length; - else - immutable end = r1.length > r2.length ? r2.length : r1.length; - for (; i < end; ++i) - { - auto lhs = r1[i]; - auto rhs = r2[i]; - if ((lhs | rhs) >= 0x80) goto NonAsciiPath; - if (lhs == rhs) continue; - auto lowDiff = std.ascii.toLower(lhs) - std.ascii.toLower(rhs); - if (lowDiff) return lowDiff; - } - static if (isInfinite!S1) - return 1; - else static if (isInfinite!S2) - return -1; - else - return (r1.length > r2.length) - (r2.length > r1.length); - - NonAsciiPath: - r1 = r1[i .. $]; - r2 = r2[i .. $]; - // Fall through to standard case. - }} - - auto str1 = r1.byDchar; - auto str2 = r2.byDchar; - - for (;;) - { - if (str1.empty) - return str2.empty ? 0 : -1; - immutable lhs = str1.front; - if (str2.empty) - return 1; - immutable rhs = str2.front; - str1.popFront(); - str2.popFront(); - if (!(lhs - rhs)) - continue; - // first try to match lhs to <rhs,right-tail> sequence - immutable cmpLR = fullCasedCmp(lhs, rhs, str2); - if (!cmpLR) - continue; - // then rhs to <lhs,left-tail> sequence - immutable cmpRL = fullCasedCmp(rhs, lhs, str1); - if (!cmpRL) - continue; - // cmpXX contain remapped codepoints - // to obtain stable ordering of icmp - return cmpLR - cmpRL; - } -} - -/// -@safe @nogc pure nothrow unittest -{ - assert(icmp("Rußland", "Russland") == 0); - assert(icmp("ដ -> \u1F70\u03B9", "\u1F61\u03B9 -> ឲ") == 0); -} - -/** - * By using $(REF byUTF, std,utf) and its aliases, GC allocations via auto-decoding - * and thrown exceptions can be avoided, making `icmp` `@safe @nogc nothrow pure`. - */ -@safe @nogc nothrow pure unittest -{ - import std.utf : byDchar; - - assert(icmp("Rußland".byDchar, "Russland".byDchar) == 0); - assert(icmp("ដ -> \u1F70\u03B9".byDchar, "\u1F61\u03B9 -> ឲ".byDchar) == 0); -} - -// test different character types -@safe unittest -{ - assert(icmp("Rußland", "Russland") == 0); - assert(icmp("Rußland"w, "Russland") == 0); - assert(icmp("Rußland", "Russland"w) == 0); - assert(icmp("Rußland"w, "Russland"w) == 0); - assert(icmp("Rußland"d, "Russland"w) == 0); - assert(icmp("Rußland"w, "Russland"d) == 0); -} - -// overloads for the most common cases to reduce compile time -@safe @nogc pure nothrow -{ - int icmp(const(char)[] str1, const(char)[] str2) - { return icmp!(const(char)[], const(char)[])(str1, str2); } - int icmp(const(wchar)[] str1, const(wchar)[] str2) - { return icmp!(const(wchar)[], const(wchar)[])(str1, str2); } - int icmp(const(dchar)[] str1, const(dchar)[] str2) - { return icmp!(const(dchar)[], const(dchar)[])(str1, str2); } -} - -@safe unittest -{ - import std.algorithm.sorting : sort; - import std.conv : to; - import std.exception : assertCTFEable; - assertCTFEable!( - { - static foreach (cfunc; AliasSeq!(icmp, sicmp)) - {{ - static foreach (S1; AliasSeq!(string, wstring, dstring)) - static foreach (S2; AliasSeq!(string, wstring, dstring)) - { - assert(cfunc("".to!S1(), "".to!S2()) == 0); - assert(cfunc("A".to!S1(), "".to!S2()) > 0); - assert(cfunc("".to!S1(), "0".to!S2()) < 0); - assert(cfunc("abc".to!S1(), "abc".to!S2()) == 0); - assert(cfunc("abcd".to!S1(), "abc".to!S2()) > 0); - assert(cfunc("abc".to!S1(), "abcd".to!S2()) < 0); - assert(cfunc("Abc".to!S1(), "aBc".to!S2()) == 0); - assert(cfunc("авГуст".to!S1(), "АВгУСТ".to!S2()) == 0); - // Check example: - assert(cfunc("Август".to!S1(), "авгусТ".to!S2()) == 0); - assert(cfunc("ΌΎ".to!S1(), "όύ".to!S2()) == 0); - } - // check that the order is properly agnostic to the case - auto strs = [ "Apple", "ORANGE", "orAcle", "amp", "banana"]; - sort!((a,b) => cfunc(a,b) < 0)(strs); - assert(strs == ["amp", "Apple", "banana", "orAcle", "ORANGE"]); - }} - assert(icmp("ßb", "ssa") > 0); - // Check example: - assert(icmp("Russland", "Rußland") == 0); - assert(icmp("ដ -> \u1F70\u03B9", "\u1F61\u03B9 -> ឲ") == 0); - assert(icmp("ΐ"w, "\u03B9\u0308\u0301") == 0); - assert(sicmp("ΐ", "\u03B9\u0308\u0301") != 0); - // https://issues.dlang.org/show_bug.cgi?id=11057 - assert( icmp("K", "L") < 0 ); - }); -} - -// https://issues.dlang.org/show_bug.cgi?id=17372 -@safe pure unittest -{ - import std.algorithm.iteration : joiner, map; - import std.algorithm.sorting : sort; - import std.array : array; - auto a = [["foo", "bar"], ["baz"]].map!(line => line.joiner(" ")).array.sort!((a, b) => icmp(a, b) < 0); -} - -// This is package(std) for the moment to be used as a support tool for std.regex -// It needs a better API -/* - Return a range of all $(CODEPOINTS) that casefold to - and from this `ch`. -*/ -package(std) auto simpleCaseFoldings(dchar ch) @safe -{ - import std.internal.unicode_tables : simpleCaseTable; // generated file - alias sTable = simpleCaseTable; - static struct Range - { - @safe pure nothrow: - uint idx; //if == uint.max, then read c. - union - { - dchar c; // == 0 - empty range - uint len; - } - @property bool isSmall() const { return idx == uint.max; } - - this(dchar ch) - { - idx = uint.max; - c = ch; - } - - this(uint start, uint size) - { - idx = start; - len = size; - } - - @property dchar front() const - { - assert(!empty); - if (isSmall) - { - return c; - } - auto ch = sTable(idx).ch; - return ch; - } - - @property bool empty() const - { - if (isSmall) - { - return c == 0; - } - return len == 0; - } - - @property size_t length() const - { - if (isSmall) - { - return c == 0 ? 0 : 1; - } - return len; - } - - void popFront() - { - if (isSmall) - c = 0; - else - { - idx++; - len--; - } - } - } - immutable idx = simpleCaseTrie[ch]; - if (idx == EMPTY_CASE_TRIE) - return Range(ch); - auto entry = sTable(idx); - immutable start = idx - entry.n; - return Range(start, entry.size); -} - -@safe unittest -{ - import std.algorithm.comparison : equal; - import std.algorithm.searching : canFind; - import std.array : array; - import std.exception : assertCTFEable; - assertCTFEable!((){ - auto r = simpleCaseFoldings('Đ­').array; - assert(r.length == 2); - assert(r.canFind('э') && r.canFind('Đ­')); - auto sr = simpleCaseFoldings('~'); - assert(sr.equal("~")); - //A with ring above - casefolds to the same bucket as Angstrom sign - sr = simpleCaseFoldings('Å'); - assert(sr.length == 3); - assert(sr.canFind('ĂĽ') && sr.canFind('Å') && sr.canFind('\u212B')); - }); -} - -/++ - $(P Returns the $(S_LINK Combining class, combining class) of `ch`.) -+/ -ubyte combiningClass(dchar ch) @safe pure nothrow @nogc -{ - return combiningClassTrie[ch]; -} - -/// -@safe unittest -{ - // shorten the code - alias CC = combiningClass; - - // combining tilda - assert(CC('\u0303') == 230); - // combining ring below - assert(CC('\u0325') == 220); - // the simple consequence is that "tilda" should be - // placed after a "ring below" in a sequence -} - -@safe pure nothrow @nogc unittest -{ - foreach (ch; 0 .. 0x80) - assert(combiningClass(ch) == 0); - assert(combiningClass('\u05BD') == 22); - assert(combiningClass('\u0300') == 230); - assert(combiningClass('\u0317') == 220); - assert(combiningClass('\u1939') == 222); -} - -/// Unicode character decomposition type. -enum UnicodeDecomposition { - /// Canonical decomposition. The result is canonically equivalent sequence. - Canonical, - /** - Compatibility decomposition. The result is compatibility equivalent sequence. - Note: Compatibility decomposition is a $(B lossy) conversion, - typically suitable only for fuzzy matching and internal processing. - */ - Compatibility -} - -/** - Shorthand aliases for character decomposition type, passed as a - template parameter to $(LREF decompose). -*/ -enum { - Canonical = UnicodeDecomposition.Canonical, - Compatibility = UnicodeDecomposition.Compatibility -} - -/++ - Try to canonically compose 2 $(CHARACTERS). - Returns the composed $(CHARACTER) if they do compose and dchar.init otherwise. - - The assumption is that `first` comes before `second` in the original text, - usually meaning that the first is a starter. - - Note: Hangul syllables are not covered by this function. - See `composeJamo` below. -+/ -public dchar compose(dchar first, dchar second) pure nothrow @safe -{ - import std.algorithm.iteration : map; - import std.internal.unicode_comp : compositionTable, composeCntShift, composeIdxMask; - import std.range : assumeSorted, stride; - immutable packed = compositionJumpTrie[first]; - if (packed == ushort.max) - return dchar.init; - // unpack offset and length - immutable idx = packed & composeIdxMask, cnt = packed >> composeCntShift; - // TODO: optimize this micro binary search (no more then 4-5 steps) - auto r = compositionTable.stride(2)[idx .. idx+cnt].assumeSorted(); - immutable target = r.lowerBound(second).length; - if (target == cnt) - return dchar.init; - immutable entry = compositionTable[(idx+target)*2]; - if (entry != second) - return dchar.init; - return compositionTable[(idx+target)*2 + 1]; -} - -/// -@safe unittest -{ - assert(compose('A','\u0308') == '\u00C4'); - assert(compose('A', 'B') == dchar.init); - assert(compose('C', '\u0301') == '\u0106'); - // note that the starter is the first one - // thus the following doesn't compose - assert(compose('\u0308', 'A') == dchar.init); -} - -/++ - Returns a full $(S_LINK Canonical decomposition, Canonical) - (by default) or $(S_LINK Compatibility decomposition, Compatibility) - decomposition of $(CHARACTER) `ch`. - If no decomposition is available returns a $(LREF Grapheme) - with the `ch` itself. - - Note: - This function also decomposes hangul syllables - as prescribed by the standard. - - See_Also: $(LREF decomposeHangul) for a restricted version - that takes into account only hangul syllables but - no other decompositions. -+/ -public Grapheme decompose(UnicodeDecomposition decompType=Canonical)(dchar ch) @safe -{ - import std.algorithm.searching : until; - import std.internal.unicode_decomp : decompCompatTable, decompCanonTable; - static if (decompType == Canonical) - { - alias table = decompCanonTable; - alias mapping = canonMappingTrie; - } - else static if (decompType == Compatibility) - { - alias table = decompCompatTable; - alias mapping = compatMappingTrie; - } - immutable idx = mapping[ch]; - if (!idx) // not found, check hangul arithmetic decomposition - return decomposeHangul(ch); - auto decomp = table[idx..$].until(0); - return Grapheme(decomp); -} - -/// -@safe unittest -{ - import std.algorithm.comparison : equal; - - assert(compose('A','\u0308') == '\u00C4'); - assert(compose('A', 'B') == dchar.init); - assert(compose('C', '\u0301') == '\u0106'); - // note that the starter is the first one - // thus the following doesn't compose - assert(compose('\u0308', 'A') == dchar.init); - - assert(decompose('Ĉ')[].equal("C\u0302")); - assert(decompose('D')[].equal("D")); - assert(decompose('\uD4DC')[].equal("\u1111\u1171\u11B7")); - assert(decompose!Compatibility('š')[].equal("1")); -} - -//---------------------------------------------------------------------------- -// Hangul specific composition/decomposition -enum jamoSBase = 0xAC00; -enum jamoLBase = 0x1100; -enum jamoVBase = 0x1161; -enum jamoTBase = 0x11A7; -enum jamoLCount = 19, jamoVCount = 21, jamoTCount = 28; -enum jamoNCount = jamoVCount * jamoTCount; -enum jamoSCount = jamoLCount * jamoNCount; - -// Tests if `ch` is a Hangul leading consonant jamo. -bool isJamoL(dchar ch) pure nothrow @nogc @safe -{ - // first cmp rejects ~ 1M code points above leading jamo range - return ch < jamoLBase+jamoLCount && ch >= jamoLBase; -} - -// Tests if `ch` is a Hangul vowel jamo. -bool isJamoT(dchar ch) pure nothrow @nogc @safe -{ - // first cmp rejects ~ 1M code points above trailing jamo range - // Note: ch == jamoTBase doesn't indicate trailing jamo (TIndex must be > 0) - return ch < jamoTBase+jamoTCount && ch > jamoTBase; -} - -// Tests if `ch` is a Hangul trailnig consonant jamo. -bool isJamoV(dchar ch) pure nothrow @nogc @safe -{ - // first cmp rejects ~ 1M code points above vowel range - return ch < jamoVBase+jamoVCount && ch >= jamoVBase; -} - -int hangulSyllableIndex(dchar ch) pure nothrow @nogc @safe -{ - int idxS = cast(int) ch - jamoSBase; - return idxS >= 0 && idxS < jamoSCount ? idxS : -1; -} - -// internal helper: compose hangul syllables leaving dchar.init in holes -void hangulRecompose(scope dchar[] seq) pure nothrow @nogc @safe -{ - for (size_t idx = 0; idx + 1 < seq.length; ) - { - if (isJamoL(seq[idx]) && isJamoV(seq[idx+1])) - { - immutable int indexL = seq[idx] - jamoLBase; - immutable int indexV = seq[idx+1] - jamoVBase; - immutable int indexLV = indexL * jamoNCount + indexV * jamoTCount; - if (idx + 2 < seq.length && isJamoT(seq[idx+2])) - { - seq[idx] = jamoSBase + indexLV + seq[idx+2] - jamoTBase; - seq[idx+1] = dchar.init; - seq[idx+2] = dchar.init; - idx += 3; - } - else - { - seq[idx] = jamoSBase + indexLV; - seq[idx+1] = dchar.init; - idx += 2; - } - } - else - idx++; - } -} - -//---------------------------------------------------------------------------- -public: - -/** - Decomposes a Hangul syllable. If `ch` is not a composed syllable - then this function returns $(LREF Grapheme) containing only `ch` as is. -*/ -Grapheme decomposeHangul(dchar ch) nothrow pure @safe -{ - immutable idxS = cast(int) ch - jamoSBase; - if (idxS < 0 || idxS >= jamoSCount) return Grapheme(ch); - immutable idxL = idxS / jamoNCount; - immutable idxV = (idxS % jamoNCount) / jamoTCount; - immutable idxT = idxS % jamoTCount; - - immutable partL = jamoLBase + idxL; - immutable partV = jamoVBase + idxV; - if (idxT > 0) // there is a trailling consonant (T); <L,V,T> decomposition - return Grapheme(partL, partV, jamoTBase + idxT); - else // <L, V> decomposition - return Grapheme(partL, partV); -} - -/// -@safe unittest -{ - import std.algorithm.comparison : equal; - assert(decomposeHangul('\uD4DB')[].equal("\u1111\u1171\u11B6")); -} - -/++ - Try to compose hangul syllable out of a leading consonant (`lead`), - a `vowel` and optional `trailing` consonant jamos. - - On success returns the composed LV or LVT hangul syllable. - - If any of `lead` and `vowel` are not a valid hangul jamo - of the respective $(CHARACTER) class returns dchar.init. -+/ -dchar composeJamo(dchar lead, dchar vowel, dchar trailing=dchar.init) pure nothrow @nogc @safe -{ - if (!isJamoL(lead)) - return dchar.init; - immutable indexL = lead - jamoLBase; - if (!isJamoV(vowel)) - return dchar.init; - immutable indexV = vowel - jamoVBase; - immutable indexLV = indexL * jamoNCount + indexV * jamoTCount; - immutable dchar syllable = jamoSBase + indexLV; - return isJamoT(trailing) ? syllable + (trailing - jamoTBase) : syllable; -} - -/// -@safe unittest -{ - assert(composeJamo('\u1111', '\u1171', '\u11B6') == '\uD4DB'); - // leaving out T-vowel, or passing any codepoint - // that is not trailing consonant composes an LV-syllable - assert(composeJamo('\u1111', '\u1171') == '\uD4CC'); - assert(composeJamo('\u1111', '\u1171', ' ') == '\uD4CC'); - assert(composeJamo('\u1111', 'A') == dchar.init); - assert(composeJamo('A', '\u1171') == dchar.init); -} - -@safe unittest -{ - import std.algorithm.comparison : equal; - import std.conv : text; - - static void testDecomp(UnicodeDecomposition T)(dchar ch, string r) - { - Grapheme g = decompose!T(ch); - assert(equal(g[], r), text(g[], " vs ", r)); - } - testDecomp!Canonical('\u1FF4', "\u03C9\u0301\u0345"); - testDecomp!Canonical('\uF907', "\u9F9C"); - testDecomp!Compatibility('\u33FF', "\u0067\u0061\u006C"); - testDecomp!Compatibility('\uA7F9', "\u0153"); - - // check examples - assert(decomposeHangul('\uD4DB')[].equal("\u1111\u1171\u11B6")); - assert(composeJamo('\u1111', '\u1171', '\u11B6') == '\uD4DB'); - assert(composeJamo('\u1111', '\u1171') == '\uD4CC'); // leave out T-vowel - assert(composeJamo('\u1111', '\u1171', ' ') == '\uD4CC'); - assert(composeJamo('\u1111', 'A') == dchar.init); - assert(composeJamo('A', '\u1171') == dchar.init); -} - -/** - Enumeration type for normalization forms, - passed as template parameter for functions like $(LREF normalize). -*/ -enum NormalizationForm { - NFC, - NFD, - NFKC, - NFKD -} - - -enum { - /** - Shorthand aliases from values indicating normalization forms. - */ - NFC = NormalizationForm.NFC, - ///ditto - NFD = NormalizationForm.NFD, - ///ditto - NFKC = NormalizationForm.NFKC, - ///ditto - NFKD = NormalizationForm.NFKD -} - -/++ - Returns `input` string normalized to the chosen form. - Form C is used by default. - - For more information on normalization forms see - the $(S_LINK Normalization, normalization section). - - Note: - In cases where the string in question is already normalized, - it is returned unmodified and no memory allocation happens. -+/ -/* - WARNING: @trusted lambda inside - handle with same care as @trusted - functions - - Despite being a template, the attributes do no harm since this doesn't work - with user-defined range or character types anyway. -*/ -pure @safe inout(C)[] normalize(NormalizationForm norm=NFC, C) - (return scope inout(C)[] input) -{ - import std.algorithm.mutation : SwapStrategy; - import std.algorithm.sorting : sort; - import std.array : appender; - import std.range : zip; - - auto anchors = splitNormalized!norm(input); - if (anchors[0] == input.length && anchors[1] == input.length) - return input; - dchar[] decomposed; - decomposed.reserve(31); - ubyte[] ccc; - ccc.reserve(31); - auto app = appender!(C[])(); - do - { - app.put(input[0 .. anchors[0]]); - foreach (dchar ch; input[anchors[0]..anchors[1]]) - static if (norm == NFD || norm == NFC) - { - foreach (dchar c; decompose!Canonical(ch)[]) - decomposed ~= c; - } - else // NFKD & NFKC - { - foreach (dchar c; decompose!Compatibility(ch)[]) - decomposed ~= c; - } - ccc.length = decomposed.length; - size_t firstNonStable = 0; - ubyte lastClazz = 0; - - foreach (idx, dchar ch; decomposed) - { - immutable clazz = combiningClass(ch); - ccc[idx] = clazz; - if (clazz == 0 && lastClazz != 0) - { - // found a stable code point after unstable ones - sort!("a[0] < b[0]", SwapStrategy.stable) - (zip(ccc[firstNonStable .. idx], decomposed[firstNonStable .. idx])); - firstNonStable = decomposed.length; - } - else if (clazz != 0 && lastClazz == 0) - { - // found first unstable code point after stable ones - firstNonStable = idx; - } - lastClazz = clazz; - } - sort!("a[0] < b[0]", SwapStrategy.stable) - (zip(ccc[firstNonStable..$], decomposed[firstNonStable..$])); - static if (norm == NFC || norm == NFKC) - { - import std.algorithm.searching : countUntil; - auto first = countUntil(ccc, 0); - if (first >= 0) // no starters?? no recomposition - { - for (;;) - { - immutable second = recompose(first, decomposed, ccc); - if (second == decomposed.length) - break; - first = second; - } - // 2nd pass for hangul syllables - hangulRecompose(decomposed); - } - } - static if (norm == NFD || norm == NFKD) - app.put(decomposed); - else - { - import std.algorithm.mutation : remove; - auto clean = remove!("a == dchar.init", SwapStrategy.stable)(decomposed); - app.put(decomposed[0 .. clean.length]); - } - // reset variables - decomposed.length = 0; - () @trusted { - // assumeSafeAppend isn't considered pure as of writing, hence the - // cast. It isn't pure in the sense that the elements after - // the array in question are affected, but we don't use those - // making the call pure for our purposes. - (cast(void delegate() pure nothrow) {decomposed.assumeSafeAppend();})(); - ccc.length = 0; - (cast(void delegate() pure nothrow) {ccc.assumeSafeAppend();})(); - } (); - input = input[anchors[1]..$]; - // and move on - anchors = splitNormalized!norm(input); - } while (anchors[0] != input.length); - app.put(input[0 .. anchors[0]]); - return () @trusted inout { return cast(inout(C)[]) app.data; } (); -} - -/// -@safe pure unittest -{ - // any encoding works - wstring greet = "Hello world"; - assert(normalize(greet) is greet); // the same exact slice - - // An example of a character with all 4 forms being different: - // Greek upsilon with acute and hook symbol (code point 0x03D3) - assert(normalize!NFC("ϓ") == "\u03D3"); - assert(normalize!NFD("ϓ") == "\u03D2\u0301"); - assert(normalize!NFKC("ϓ") == "\u038E"); - assert(normalize!NFKD("ϓ") == "\u03A5\u0301"); -} - -@safe pure unittest -{ - import std.conv : text; - - assert(normalize!NFD("abc\uF904def") == "abc\u6ED1def", text(normalize!NFD("abc\uF904def"))); - assert(normalize!NFKD("2š⁰") == "210", normalize!NFKD("2š⁰")); - assert(normalize!NFD("Äffin") == "A\u0308ffin"); - - // test with dstring - dstring greet = "Hello world"; - assert(normalize(greet) is greet); // the same exact slice -} - -// canonically recompose given slice of code points, works in-place and mutates data -private size_t recompose(size_t start, scope dchar[] input, scope ubyte[] ccc) pure nothrow @safe -{ - assert(input.length == ccc.length); - int accumCC = -1;// so that it's out of 0 .. 255 range - // writefln("recomposing %( %04x %)", input); - // first one is always a starter thus we start at i == 1 - size_t i = start+1; - for (; ; ) - { - if (i == input.length) - break; - immutable curCC = ccc[i]; - // In any character sequence beginning with a starter S - // a character C is blocked from S if and only if there - // is some character B between S and C, and either B - // is a starter or it has the same or higher combining class as C. - //------------------------ - // Applying to our case: - // S is input[0] - // accumCC is the maximum CCC of characters between C and S, - // as ccc are sorted - // C is input[i] - - if (curCC > accumCC) - { - immutable comp = compose(input[start], input[i]); - if (comp != dchar.init) - { - input[start] = comp; - input[i] = dchar.init;// put a sentinel - // current was merged so its CCC shouldn't affect - // composing with the next one - } - else - { - // if it was a starter then accumCC is now 0, end of loop - accumCC = curCC; - if (accumCC == 0) - break; - } - } - else - { - // ditto here - accumCC = curCC; - if (accumCC == 0) - break; - } - i++; - } - return i; -} - -// returns tuple of 2 indexes that delimit: -// normalized text, piece that needs normalization and -// the rest of input starting with stable code point -private auto splitNormalized(NormalizationForm norm, C)(scope const(C)[] input) -{ - import std.typecons : tuple; - ubyte lastCC = 0; - - foreach (idx, dchar ch; input) - { - static if (norm == NFC) - if (ch < 0x0300) - { - lastCC = 0; - continue; - } - immutable ubyte CC = combiningClass(ch); - if (lastCC > CC && CC != 0) - { - return seekStable!norm(idx, input); - } - - if (notAllowedIn!norm(ch)) - { - return seekStable!norm(idx, input); - } - lastCC = CC; - } - return tuple(input.length, input.length); -} - -private auto seekStable(NormalizationForm norm, C)(size_t idx, const scope C[] input) -{ - import std.typecons : tuple; - import std.utf : codeLength; - - auto br = input[0 .. idx]; - size_t region_start = 0;// default - for (;;) - { - if (br.empty)// start is 0 - break; - dchar ch = br.back; - if (combiningClass(ch) == 0 && allowedIn!norm(ch)) - { - region_start = br.length - codeLength!C(ch); - break; - } - br.popFront(); - } - ///@@@BUG@@@ can't use find: " find is a nested function and can't be used..." - size_t region_end=input.length;// end is $ by default - foreach (i, dchar ch; input[idx..$]) - { - if (combiningClass(ch) == 0 && allowedIn!norm(ch)) - { - region_end = i+idx; - break; - } - } - // writeln("Region to normalize: ", input[region_start .. region_end]); - return tuple(region_start, region_end); -} - -/** - Tests if dchar `ch` is always allowed (Quick_Check=YES) in normalization - form `norm`. -*/ -public bool allowedIn(NormalizationForm norm)(dchar ch) -{ - return !notAllowedIn!norm(ch); -} - -/// -@safe unittest -{ - // e.g. Cyrillic is always allowed, so is ASCII - assert(allowedIn!NFC('я')); - assert(allowedIn!NFD('я')); - assert(allowedIn!NFKC('я')); - assert(allowedIn!NFKD('я')); - assert(allowedIn!NFC('Z')); -} - -// not user friendly name but more direct -private bool notAllowedIn(NormalizationForm norm)(dchar ch) -{ - static if (norm == NFC) - alias qcTrie = nfcQCTrie; - else static if (norm == NFD) - alias qcTrie = nfdQCTrie; - else static if (norm == NFKC) - alias qcTrie = nfkcQCTrie; - else static if (norm == NFKD) - alias qcTrie = nfkdQCTrie; - else - static assert("Unknown normalization form "~norm); - return qcTrie[ch]; -} - -@safe unittest -{ - assert(allowedIn!NFC('я')); - assert(allowedIn!NFD('я')); - assert(allowedIn!NFKC('я')); - assert(allowedIn!NFKD('я')); - assert(allowedIn!NFC('Z')); -} - -} - -version (std_uni_bootstrap) -{ - // old version used for bootstrapping of gen_uni.d that generates - // up to date optimal versions of all of isXXX functions - @safe pure nothrow @nogc public bool isWhite(dchar c) - { - import std.ascii : isWhite; - return isWhite(c) || - c == lineSep || c == paraSep || - c == '\u0085' || c == '\u00A0' || c == '\u1680' || c == '\u180E' || - (c >= '\u2000' && c <= '\u200A') || - c == '\u202F' || c == '\u205F' || c == '\u3000'; - } -} -else -{ - -// trusted -> avoid bounds check -@trusted pure nothrow @nogc private -{ - import std.internal.unicode_tables; // : toLowerTable, toTitleTable, toUpperTable; // generated file - - // hide template instances behind functions - // https://issues.dlang.org/show_bug.cgi?id=13232 - ushort toLowerIndex(dchar c) { return toLowerIndexTrie[c]; } - ushort toLowerSimpleIndex(dchar c) { return toLowerSimpleIndexTrie[c]; } - dchar toLowerTab(size_t idx) { return toLowerTable[idx]; } - - ushort toTitleIndex(dchar c) { return toTitleIndexTrie[c]; } - ushort toTitleSimpleIndex(dchar c) { return toTitleSimpleIndexTrie[c]; } - dchar toTitleTab(size_t idx) { return toTitleTable[idx]; } - - ushort toUpperIndex(dchar c) { return toUpperIndexTrie[c]; } - ushort toUpperSimpleIndex(dchar c) { return toUpperSimpleIndexTrie[c]; } - dchar toUpperTab(size_t idx) { return toUpperTable[idx]; } -} - -public: - -/++ - Whether or not `c` is a Unicode whitespace $(CHARACTER). - (general Unicode category: Part of C0(tab, vertical tab, form feed, - carriage return, and linefeed characters), Zs, Zl, Zp, and NEL(U+0085)) -+/ -@safe pure nothrow @nogc -public bool isWhite(dchar c) -{ - import std.internal.unicode_tables : isWhiteGen; // generated file - return isWhiteGen(c); // call pregenerated binary search -} - -/++ - Return whether `c` is a Unicode lowercase $(CHARACTER). -+/ -@safe pure nothrow @nogc -bool isLower(dchar c) -{ - import std.ascii : isLower, isASCII; - if (isASCII(c)) - return isLower(c); - return lowerCaseTrie[c]; -} - -@safe unittest -{ - import std.ascii : isLower; - foreach (v; 0 .. 0x80) - assert(isLower(v) == .isLower(v)); - assert(.isLower('я')); - assert(.isLower('Đš')); - assert(!.isLower('Ж')); - // Greek HETA - assert(!.isLower('\u0370')); - assert(.isLower('\u0371')); - assert(!.isLower('\u039C')); // capital MU - assert(.isLower('\u03B2')); // beta - // from extended Greek - assert(!.isLower('\u1F18')); - assert(.isLower('\u1F00')); - foreach (v; unicode.lowerCase.byCodepoint) - assert(.isLower(v) && !isUpper(v)); -} - - -/++ - Return whether `c` is a Unicode uppercase $(CHARACTER). -+/ -@safe pure nothrow @nogc -bool isUpper(dchar c) -{ - import std.ascii : isUpper, isASCII; - if (isASCII(c)) - return isUpper(c); - return upperCaseTrie[c]; -} - -@safe unittest -{ - import std.ascii : isLower; - foreach (v; 0 .. 0x80) - assert(isLower(v) == .isLower(v)); - assert(!isUpper('Đš')); - assert(isUpper('Ж')); - // Greek HETA - assert(isUpper('\u0370')); - assert(!isUpper('\u0371')); - assert(isUpper('\u039C')); // capital MU - assert(!isUpper('\u03B2')); // beta - // from extended Greek - assert(!isUpper('\u1F00')); - assert(isUpper('\u1F18')); - foreach (v; unicode.upperCase.byCodepoint) - assert(isUpper(v) && !.isLower(v)); -} - - -//TODO: Hidden for now, needs better API. -//Other transforms could use better API as well, but this one is a new primitive. -@safe pure nothrow @nogc -private dchar toTitlecase(dchar c) -{ - // optimize ASCII case - if (c < 0xAA) - { - if (c < 'a') - return c; - if (c <= 'z') - return c - 32; - return c; - } - size_t idx = toTitleSimpleIndex(c); - if (idx != ushort.max) - { - return toTitleTab(idx); - } - return c; -} - -private alias UpperTriple = AliasSeq!(toUpperIndex, MAX_SIMPLE_UPPER, toUpperTab); -private alias LowerTriple = AliasSeq!(toLowerIndex, MAX_SIMPLE_LOWER, toLowerTab); - -// generic toUpper/toLower on whole string, creates new or returns as is -private ElementEncodingType!S[] toCase(alias indexFn, uint maxIdx, alias tableFn, alias asciiConvert, S)(S s) -if (isSomeString!S || (isRandomAccessRange!S && hasLength!S && hasSlicing!S && isSomeChar!(ElementType!S))) -{ - import std.array : appender, array; - import std.ascii : isASCII; - import std.utf : byDchar, codeLength; - - alias C = ElementEncodingType!S; - - auto r = s.byDchar; - for (size_t i; !r.empty; i += r.front.codeLength!C , r.popFront()) - { - auto cOuter = r.front; - ushort idx = indexFn(cOuter); - if (idx == ushort.max) - continue; - auto result = appender!(C[])(); - result.reserve(s.length); - result.put(s[0 .. i]); - foreach (dchar c; s[i .. $].byDchar) - { - if (c.isASCII) - { - result.put(asciiConvert(c)); - } - else - { - idx = indexFn(c); - if (idx == ushort.max) - result.put(c); - else if (idx < maxIdx) - { - c = tableFn(idx); - result.put(c); - } - else - { - auto val = tableFn(idx); - // unpack length + codepoint - immutable uint len = val >> 24; - result.put(cast(dchar)(val & 0xFF_FFFF)); - foreach (j; idx+1 .. idx+len) - result.put(tableFn(j)); - } - } - } - return result.data; - } - - static if (isSomeString!S) - return s; - else - return s.array; -} - -// https://issues.dlang.org/show_bug.cgi?id=12428 -@safe unittest -{ - import std.array : replicate; - auto s = "abcdefghij".replicate(300); - s = s[0 .. 10]; - - toUpper(s); - - assert(s == "abcdefghij"); -} - -// https://issues.dlang.org/show_bug.cgi?id=18993 -@safe unittest -{ - static assert(`몬스터/A`.toLower.length == `몬스터/a`.toLower.length); -} - - -// generic toUpper/toLower on whole range, returns range -private auto toCaser(alias indexFn, uint maxIdx, alias tableFn, alias asciiConvert, Range)(Range str) - // Accept range of dchar's -if (isInputRange!Range && - isSomeChar!(ElementEncodingType!Range) && - ElementEncodingType!Range.sizeof == dchar.sizeof) -{ - static struct ToCaserImpl - { - @property bool empty() - { - return !nLeft && r.empty; - } - - @property auto front() - { - import std.ascii : isASCII; - - if (!nLeft) - { - dchar c = r.front; - if (c.isASCII) - { - buf[0] = asciiConvert(c); - nLeft = 1; - } - else - { - const idx = indexFn(c); - if (idx == ushort.max) - { - buf[0] = c; - nLeft = 1; - } - else if (idx < maxIdx) - { - buf[0] = tableFn(idx); - nLeft = 1; - } - else - { - immutable val = tableFn(idx); - // unpack length + codepoint - nLeft = val >> 24; - if (nLeft == 0) - nLeft = 1; - assert(nLeft <= buf.length); - buf[nLeft - 1] = cast(dchar)(val & 0xFF_FFFF); - foreach (j; 1 .. nLeft) - buf[nLeft - j - 1] = tableFn(idx + j); - } - } - } - return buf[nLeft - 1]; - } - - void popFront() - { - if (!nLeft) - front; - assert(nLeft); - --nLeft; - if (!nLeft) - r.popFront(); - } - - static if (isForwardRange!Range) - { - @property auto save() - { - auto ret = this; - ret.r = r.save; - return ret; - } - } - - private: - Range r; - uint nLeft; - dchar[3] buf = void; - } - - return ToCaserImpl(str); -} - -/********************* - * Convert an $(REF_ALTTEXT input range, isInputRange, std,range,primitives) - * or a string to upper or lower case. - * - * Does not allocate memory. - * Characters in UTF-8 or UTF-16 format that cannot be decoded - * are treated as $(REF replacementDchar, std,utf). - * - * Params: - * str = string or range of characters - * - * Returns: - * an input range of `dchar`s - * - * See_Also: - * $(LREF toUpper), $(LREF toLower) - */ - -auto asLowerCase(Range)(Range str) -if (isInputRange!Range && isSomeChar!(ElementEncodingType!Range) && - !isConvertibleToString!Range) -{ - static if (ElementEncodingType!Range.sizeof < dchar.sizeof) - { - import std.utf : byDchar; - - // Decode first - return asLowerCase(str.byDchar); - } - else - { - static import std.ascii; - return toCaser!(LowerTriple, std.ascii.toLower)(str); - } -} - -/// ditto -auto asUpperCase(Range)(Range str) -if (isInputRange!Range && isSomeChar!(ElementEncodingType!Range) && - !isConvertibleToString!Range) -{ - static if (ElementEncodingType!Range.sizeof < dchar.sizeof) - { - import std.utf : byDchar; - - // Decode first - return asUpperCase(str.byDchar); - } - else - { - static import std.ascii; - return toCaser!(UpperTriple, std.ascii.toUpper)(str); - } -} - -/// -@safe pure unittest -{ - import std.algorithm.comparison : equal; - - assert("hEllo".asUpperCase.equal("HELLO")); -} - -// explicitly undocumented -auto asLowerCase(Range)(auto ref Range str) -if (isConvertibleToString!Range) -{ - import std.traits : StringTypeOf; - return asLowerCase!(StringTypeOf!Range)(str); -} - -// explicitly undocumented -auto asUpperCase(Range)(auto ref Range str) -if (isConvertibleToString!Range) -{ - import std.traits : StringTypeOf; - return asUpperCase!(StringTypeOf!Range)(str); -} - -@safe unittest -{ - static struct TestAliasedString - { - string get() @safe @nogc pure nothrow { return _s; } - alias get this; - @disable this(this); - string _s; - } - - static bool testAliasedString(alias func, Args...)(string s, Args args) - { - import std.algorithm.comparison : equal; - auto a = func(TestAliasedString(s), args); - auto b = func(s, args); - static if (is(typeof(equal(a, b)))) - { - // For ranges, compare contents instead of object identity. - return equal(a, b); - } - else - { - return a == b; - } - } - assert(testAliasedString!asLowerCase("hEllo")); - assert(testAliasedString!asUpperCase("hEllo")); - assert(testAliasedString!asCapitalized("hEllo")); -} - -@safe unittest -{ - import std.array : array; - - auto a = "HELLo".asLowerCase; - auto savea = a.save; - auto s = a.array; - assert(s == "hello"); - s = savea.array; - assert(s == "hello"); - - string[] lower = ["123", "abcфеж", "\u0131\u023f\u03c9", "i\u0307\u1Fe2"]; - string[] upper = ["123", "ABCФЕЖ", "I\u2c7e\u2126", "\u0130\u03A5\u0308\u0300"]; - - foreach (i, slwr; lower) - { - import std.utf : byChar; - - auto sx = slwr.asUpperCase.byChar.array; - assert(sx == toUpper(slwr)); - auto sy = upper[i].asLowerCase.byChar.array; - assert(sy == toLower(upper[i])); - } - - // Not necessary to call r.front - for (auto r = lower[3].asUpperCase; !r.empty; r.popFront()) - { - } - - import std.algorithm.comparison : equal; - - "HELLo"w.asLowerCase.equal("hello"d); - "HELLo"w.asUpperCase.equal("HELLO"d); - "HELLo"d.asLowerCase.equal("hello"d); - "HELLo"d.asUpperCase.equal("HELLO"d); - - import std.utf : byChar; - assert(toLower("\u1Fe2") == asLowerCase("\u1Fe2").byChar.array); -} - -// generic capitalizer on whole range, returns range -private auto toCapitalizer(alias indexFnUpper, uint maxIdxUpper, alias tableFnUpper, - Range)(Range str) - // Accept range of dchar's -if (isInputRange!Range && - isSomeChar!(ElementEncodingType!Range) && - ElementEncodingType!Range.sizeof == dchar.sizeof) -{ - static struct ToCapitalizerImpl - { - @property bool empty() - { - return lower ? lwr.empty : !nLeft && r.empty; - } - - @property auto front() - { - if (lower) - return lwr.front; - - if (!nLeft) - { - immutable dchar c = r.front; - const idx = indexFnUpper(c); - if (idx == ushort.max) - { - buf[0] = c; - nLeft = 1; - } - else if (idx < maxIdxUpper) - { - buf[0] = tableFnUpper(idx); - nLeft = 1; - } - else - { - immutable val = tableFnUpper(idx); - // unpack length + codepoint - nLeft = val >> 24; - if (nLeft == 0) - nLeft = 1; - assert(nLeft <= buf.length); - buf[nLeft - 1] = cast(dchar)(val & 0xFF_FFFF); - foreach (j; 1 .. nLeft) - buf[nLeft - j - 1] = tableFnUpper(idx + j); - } - } - return buf[nLeft - 1]; - } - - void popFront() - { - if (lower) - lwr.popFront(); - else - { - if (!nLeft) - front; - assert(nLeft); - --nLeft; - if (!nLeft) - { - r.popFront(); - lwr = r.asLowerCase(); - lower = true; - } - } - } - - static if (isForwardRange!Range) - { - @property auto save() - { - auto ret = this; - ret.r = r.save; - ret.lwr = lwr.save; - return ret; - } - } - - private: - Range r; - typeof(r.asLowerCase) lwr; // range representing the lower case rest of string - bool lower = false; // false for first character, true for rest of string - dchar[3] buf = void; - uint nLeft = 0; - } - - return ToCapitalizerImpl(str); -} - -/********************* - * Capitalize an $(REF_ALTTEXT input range, isInputRange, std,range,primitives) - * or string, meaning convert the first - * character to upper case and subsequent characters to lower case. - * - * Does not allocate memory. - * Characters in UTF-8 or UTF-16 format that cannot be decoded - * are treated as $(REF replacementDchar, std,utf). - * - * Params: - * str = string or range of characters - * - * Returns: - * an InputRange of dchars - * - * See_Also: - * $(LREF toUpper), $(LREF toLower) - * $(LREF asUpperCase), $(LREF asLowerCase) - */ - -auto asCapitalized(Range)(Range str) -if (isInputRange!Range && isSomeChar!(ElementEncodingType!Range) && - !isConvertibleToString!Range) -{ - static if (ElementEncodingType!Range.sizeof < dchar.sizeof) - { - import std.utf : byDchar; - - // Decode first - return toCapitalizer!UpperTriple(str.byDchar); - } - else - { - return toCapitalizer!UpperTriple(str); - } -} - -/// -@safe pure unittest -{ - import std.algorithm.comparison : equal; - - assert("hEllo".asCapitalized.equal("Hello")); -} - -auto asCapitalized(Range)(auto ref Range str) -if (isConvertibleToString!Range) -{ - import std.traits : StringTypeOf; - return asCapitalized!(StringTypeOf!Range)(str); -} - -@safe pure nothrow @nogc unittest -{ - auto r = "hEllo".asCapitalized(); - assert(r.front == 'H'); -} - -@safe unittest -{ - import std.array : array; - - auto a = "hELLo".asCapitalized; - auto savea = a.save; - auto s = a.array; - assert(s == "Hello"); - s = savea.array; - assert(s == "Hello"); - - string[2][] cases = - [ - ["", ""], - ["h", "H"], - ["H", "H"], - ["3", "3"], - ["123", "123"], - ["h123A", "H123a"], - ["феж", "ФоМ"], - ["\u1Fe2", "\u03a5\u0308\u0300"], - ]; - - foreach (i; 0 .. cases.length) - { - import std.utf : byChar; - - auto r = cases[i][0].asCapitalized.byChar.array; - auto result = cases[i][1]; - assert(r == result); - } - - // Don't call r.front - for (auto r = "\u1Fe2".asCapitalized; !r.empty; r.popFront()) - { - } - - import std.algorithm.comparison : equal; - - "HELLo"w.asCapitalized.equal("Hello"d); - "hElLO"w.asCapitalized.equal("Hello"d); - "hello"d.asCapitalized.equal("Hello"d); - "HELLO"d.asCapitalized.equal("Hello"d); - - import std.utf : byChar; - assert(asCapitalized("\u0130").byChar.array == asUpperCase("\u0130").byChar.array); -} - -// TODO: helper, I wish std.utf was more flexible (and stright) -private size_t encodeTo(scope char[] buf, size_t idx, dchar c) @trusted pure nothrow @nogc -{ - if (c <= 0x7F) - { - buf[idx] = cast(char) c; - idx++; - } - else if (c <= 0x7FF) - { - buf[idx] = cast(char)(0xC0 | (c >> 6)); - buf[idx+1] = cast(char)(0x80 | (c & 0x3F)); - idx += 2; - } - else if (c <= 0xFFFF) - { - buf[idx] = cast(char)(0xE0 | (c >> 12)); - buf[idx+1] = cast(char)(0x80 | ((c >> 6) & 0x3F)); - buf[idx+2] = cast(char)(0x80 | (c & 0x3F)); - idx += 3; - } - else if (c <= 0x10FFFF) - { - buf[idx] = cast(char)(0xF0 | (c >> 18)); - buf[idx+1] = cast(char)(0x80 | ((c >> 12) & 0x3F)); - buf[idx+2] = cast(char)(0x80 | ((c >> 6) & 0x3F)); - buf[idx+3] = cast(char)(0x80 | (c & 0x3F)); - idx += 4; - } - else - assert(0); - return idx; -} - -@safe unittest -{ - char[] s = "abcd".dup; - size_t i = 0; - i = encodeTo(s, i, 'X'); - assert(s == "Xbcd"); - - i = encodeTo(s, i, cast(dchar)'\u00A9'); - assert(s == "X\xC2\xA9d"); -} - -// TODO: helper, I wish std.utf was more flexible (and stright) -private size_t encodeTo(scope wchar[] buf, size_t idx, dchar c) @trusted pure -{ - import std.utf : UTFException; - if (c <= 0xFFFF) - { - if (0xD800 <= c && c <= 0xDFFF) - throw (new UTFException("Encoding an isolated surrogate code point in UTF-16")).setSequence(c); - buf[idx] = cast(wchar) c; - idx++; - } - else if (c <= 0x10FFFF) - { - buf[idx] = cast(wchar)((((c - 0x10000) >> 10) & 0x3FF) + 0xD800); - buf[idx+1] = cast(wchar)(((c - 0x10000) & 0x3FF) + 0xDC00); - idx += 2; - } - else - assert(0); - return idx; -} - -private size_t encodeTo(scope dchar[] buf, size_t idx, dchar c) @trusted pure nothrow @nogc -{ - buf[idx] = c; - idx++; - return idx; -} - -private void toCaseInPlace(alias indexFn, uint maxIdx, alias tableFn, C)(ref C[] s) @trusted pure -if (is(C == char) || is(C == wchar) || is(C == dchar)) -{ - import std.utf : decode, codeLength; - size_t curIdx = 0; - size_t destIdx = 0; - alias slowToCase = toCaseInPlaceAlloc!(indexFn, maxIdx, tableFn); - size_t lastUnchanged = 0; - // in-buffer move of bytes to a new start index - // the trick is that it may not need to copy at all - static size_t moveTo(C[] str, size_t dest, size_t from, size_t to) - { - // Interestingly we may just bump pointer for a while - // then have to copy if a re-cased char was smaller the original - // later we may regain pace with char that got bigger - // In the end it sometimes flip-flops between the 2 cases below - if (dest == from) - return to; - // got to copy - foreach (C c; str[from .. to]) - str[dest++] = c; - return dest; - } - while (curIdx != s.length) - { - size_t startIdx = curIdx; - immutable ch = decode(s, curIdx); - // TODO: special case for ASCII - immutable caseIndex = indexFn(ch); - if (caseIndex == ushort.max) // unchanged, skip over - { - continue; - } - else if (caseIndex < maxIdx) // 1:1 codepoint mapping - { - // previous cased chars had the same length as uncased ones - // thus can just adjust pointer - destIdx = moveTo(s, destIdx, lastUnchanged, startIdx); - lastUnchanged = curIdx; - immutable cased = tableFn(caseIndex); - immutable casedLen = codeLength!C(cased); - if (casedLen + destIdx > curIdx) // no place to fit cased char - { - // switch to slow codepath, where we allocate - return slowToCase(s, startIdx, destIdx); - } - else - { - destIdx = encodeTo(s, destIdx, cased); - } - } - else // 1:m codepoint mapping, slow codepath - { - destIdx = moveTo(s, destIdx, lastUnchanged, startIdx); - lastUnchanged = curIdx; - return slowToCase(s, startIdx, destIdx); - } - assert(destIdx <= curIdx); - } - if (lastUnchanged != s.length) - { - destIdx = moveTo(s, destIdx, lastUnchanged, s.length); - } - s = s[0 .. destIdx]; -} - -// helper to precalculate size of case-converted string -private template toCaseLength(alias indexFn, uint maxIdx, alias tableFn) -{ - size_t toCaseLength(C)(const scope C[] str) - { - import std.utf : decode, codeLength; - size_t codeLen = 0; - size_t lastNonTrivial = 0; - size_t curIdx = 0; - while (curIdx != str.length) - { - immutable startIdx = curIdx; - immutable ch = decode(str, curIdx); - immutable ushort caseIndex = indexFn(ch); - if (caseIndex == ushort.max) - continue; - else if (caseIndex < maxIdx) - { - codeLen += startIdx - lastNonTrivial; - lastNonTrivial = curIdx; - immutable cased = tableFn(caseIndex); - codeLen += codeLength!C(cased); - } - else - { - codeLen += startIdx - lastNonTrivial; - lastNonTrivial = curIdx; - immutable val = tableFn(caseIndex); - immutable len = val >> 24; - immutable dchar cased = val & 0xFF_FFFF; - codeLen += codeLength!C(cased); - foreach (j; caseIndex+1 .. caseIndex+len) - codeLen += codeLength!C(tableFn(j)); - } - } - if (lastNonTrivial != str.length) - codeLen += str.length - lastNonTrivial; - return codeLen; - } -} - -@safe unittest -{ - alias toLowerLength = toCaseLength!(LowerTriple); - assert(toLowerLength("abcd") == 4); - assert(toLowerLength("аБВгд456") == 10+3); -} - -// slower code path that preallocates and then copies -// case-converted stuf to the new string -private template toCaseInPlaceAlloc(alias indexFn, uint maxIdx, alias tableFn) -{ - void toCaseInPlaceAlloc(C)(ref C[] s, size_t curIdx, - size_t destIdx) @trusted pure - if (is(C == char) || is(C == wchar) || is(C == dchar)) - { - import std.utf : decode; - alias caseLength = toCaseLength!(indexFn, maxIdx, tableFn); - auto trueLength = destIdx + caseLength(s[curIdx..$]); - C[] ns = new C[trueLength]; - ns[0 .. destIdx] = s[0 .. destIdx]; - size_t lastUnchanged = curIdx; - while (curIdx != s.length) - { - immutable startIdx = curIdx; // start of current codepoint - immutable ch = decode(s, curIdx); - immutable caseIndex = indexFn(ch); - if (caseIndex == ushort.max) // skip over - { - continue; - } - else if (caseIndex < maxIdx) // 1:1 codepoint mapping - { - immutable cased = tableFn(caseIndex); - auto toCopy = startIdx - lastUnchanged; - ns[destIdx .. destIdx+toCopy] = s[lastUnchanged .. startIdx]; - lastUnchanged = curIdx; - destIdx += toCopy; - destIdx = encodeTo(ns, destIdx, cased); - } - else // 1:m codepoint mapping, slow codepath - { - auto toCopy = startIdx - lastUnchanged; - ns[destIdx .. destIdx+toCopy] = s[lastUnchanged .. startIdx]; - lastUnchanged = curIdx; - destIdx += toCopy; - auto val = tableFn(caseIndex); - // unpack length + codepoint - immutable uint len = val >> 24; - destIdx = encodeTo(ns, destIdx, cast(dchar)(val & 0xFF_FFFF)); - foreach (j; caseIndex+1 .. caseIndex+len) - destIdx = encodeTo(ns, destIdx, tableFn(j)); - } - } - if (lastUnchanged != s.length) - { - auto toCopy = s.length - lastUnchanged; - ns[destIdx .. destIdx+toCopy] = s[lastUnchanged..$]; - destIdx += toCopy; - } - assert(ns.length == destIdx); - s = ns; - } -} - -/++ - Converts `s` to lowercase (by performing Unicode lowercase mapping) in place. - For a few characters string length may increase after the transformation, - in such a case the function reallocates exactly once. - If `s` does not have any uppercase characters, then `s` is unaltered. -+/ -void toLowerInPlace(C)(ref C[] s) @trusted pure -if (is(C == char) || is(C == wchar) || is(C == dchar)) -{ - toCaseInPlace!(LowerTriple)(s); -} -// overloads for the most common cases to reduce compile time -@safe pure /*TODO nothrow*/ -{ - void toLowerInPlace(ref char[] s) - { toLowerInPlace!char(s); } - void toLowerInPlace(ref wchar[] s) - { toLowerInPlace!wchar(s); } - void toLowerInPlace(ref dchar[] s) - { toLowerInPlace!dchar(s); } -} - -/++ - Converts `s` to uppercase (by performing Unicode uppercase mapping) in place. - For a few characters string length may increase after the transformation, - in such a case the function reallocates exactly once. - If `s` does not have any lowercase characters, then `s` is unaltered. -+/ -void toUpperInPlace(C)(ref C[] s) @trusted pure -if (is(C == char) || is(C == wchar) || is(C == dchar)) -{ - toCaseInPlace!(UpperTriple)(s); -} -// overloads for the most common cases to reduce compile time/code size -@safe pure /*TODO nothrow*/ -{ - void toUpperInPlace(ref char[] s) - { toUpperInPlace!char(s); } - void toUpperInPlace(ref wchar[] s) - { toUpperInPlace!wchar(s); } - void toUpperInPlace(ref dchar[] s) - { toUpperInPlace!dchar(s); } -} - -/++ - If `c` is a Unicode uppercase $(CHARACTER), then its lowercase equivalent - is returned. Otherwise `c` is returned. - - Warning: certain alphabets like German and Greek have no 1:1 - upper-lower mapping. Use overload of toLower which takes full string instead. -+/ -@safe pure nothrow @nogc -dchar toLower(dchar c) -{ - // optimize ASCII case - if (c < 0xAA) - { - if (c < 'A') - return c; - if (c <= 'Z') - return c + 32; - return c; - } - size_t idx = toLowerSimpleIndex(c); - if (idx != ushort.max) - { - return toLowerTab(idx); - } - return c; -} - -/++ - Creates a new array which is identical to `s` except that all of its - characters are converted to lowercase (by performing Unicode lowercase mapping). - If none of `s` characters were affected, then `s` itself is returned if `s` is a - `string`-like type. - - Params: - s = A $(REF_ALTTEXT random access range, isRandomAccessRange, std,range,primitives) - of characters - Returns: - An array with the same element type as `s`. -+/ -ElementEncodingType!S[] toLower(S)(return scope S s) @trusted -if (isSomeString!S) -{ - static import std.ascii; - return toCase!(LowerTriple, std.ascii.toLower)(s); -} - -/// ditto -ElementEncodingType!S[] toLower(S)(S s) -if (!isSomeString!S && (isRandomAccessRange!S && hasLength!S && hasSlicing!S && isSomeChar!(ElementType!S))) -{ - static import std.ascii; - return toCase!(LowerTriple, std.ascii.toLower)(s); -} - -// overloads for the most common cases to reduce compile time -@safe pure /*TODO nothrow*/ -{ - string toLower(return scope string s) - { return toLower!string(s); } - wstring toLower(return scope wstring s) - { return toLower!wstring(s); } - dstring toLower(return scope dstring s) - { return toLower!dstring(s); } - - @safe unittest - { - // https://issues.dlang.org/show_bug.cgi?id=16663 - - static struct String - { - string data; - alias data this; - } - - void foo() - { - auto u = toLower(String("")); - } - } -} - - -@safe unittest -{ - static import std.ascii; - import std.format : format; - foreach (ch; 0 .. 0x80) - assert(std.ascii.toLower(ch) == toLower(ch)); - assert(toLower('ĐŻ') == 'я'); - assert(toLower('Δ') == 'δ'); - foreach (ch; unicode.upperCase.byCodepoint) - { - dchar low = ch.toLower(); - assert(low == ch || isLower(low), format("%s -> %s", ch, low)); - } - assert(toLower("АЯ") == "ая"); - - assert("\u1E9E".toLower == "\u00df"); - assert("\u00df".toUpper == "SS"); -} - -// https://issues.dlang.org/show_bug.cgi?id=9629 -@safe unittest -{ - wchar[] test = "hello Ăž world"w.dup; - auto piece = test[6 .. 7]; - toUpperInPlace(piece); - assert(test == "hello Þ world"); -} - - -@safe unittest -{ - import std.algorithm.comparison : cmp; - string s1 = "FoL"; - string s2 = toLower(s1); - assert(cmp(s2, "fol") == 0, s2); - assert(s2 != s1); - - char[] s3 = s1.dup; - toLowerInPlace(s3); - assert(s3 == s2); - - s1 = "A\u0100B\u0101d"; - s2 = toLower(s1); - s3 = s1.dup; - assert(cmp(s2, "a\u0101b\u0101d") == 0); - assert(s2 !is s1); - toLowerInPlace(s3); - assert(s3 == s2); - - s1 = "A\u0460B\u0461d"; - s2 = toLower(s1); - s3 = s1.dup; - assert(cmp(s2, "a\u0461b\u0461d") == 0); - assert(s2 !is s1); - toLowerInPlace(s3); - assert(s3 == s2); - - s1 = "\u0130"; - s2 = toLower(s1); - s3 = s1.dup; - assert(s2 == "i\u0307"); - assert(s2 !is s1); - toLowerInPlace(s3); - assert(s3 == s2); - - // Test on wchar and dchar strings. - assert(toLower("Some String"w) == "some string"w); - assert(toLower("Some String"d) == "some string"d); - - // https://issues.dlang.org/show_bug.cgi?id=12455 - dchar c = 'Ä°'; // '\U0130' LATIN CAPITAL LETTER I WITH DOT ABOVE - assert(isUpper(c)); - assert(toLower(c) == 'i'); - // extends on https://issues.dlang.org/show_bug.cgi?id=12455 report - // check simple-case toUpper too - c = '\u1f87'; - assert(isLower(c)); - assert(toUpper(c) == '\u1F8F'); -} - -@safe pure unittest -{ - import std.algorithm.comparison : cmp, equal; - import std.utf : byCodeUnit; - auto r1 = "FoL".byCodeUnit; - assert(r1.toLower.cmp("fol") == 0); - auto r2 = "A\u0460B\u0461d".byCodeUnit; - assert(r2.toLower.cmp("a\u0461b\u0461d") == 0); -} - -/++ - If `c` is a Unicode lowercase $(CHARACTER), then its uppercase equivalent - is returned. Otherwise `c` is returned. - - Warning: - Certain alphabets like German and Greek have no 1:1 - upper-lower mapping. Use overload of toUpper which takes full string instead. - - toUpper can be used as an argument to $(REF map, std,algorithm,iteration) - to produce an algorithm that can convert a range of characters to upper case - without allocating memory. - A string can then be produced by using $(REF copy, std,algorithm,mutation) - to send it to an $(REF appender, std,array). -+/ -@safe pure nothrow @nogc -dchar toUpper(dchar c) -{ - // optimize ASCII case - if (c < 0xAA) - { - if (c < 'a') - return c; - if (c <= 'z') - return c - 32; - return c; - } - size_t idx = toUpperSimpleIndex(c); - if (idx != ushort.max) - { - return toUpperTab(idx); - } - return c; -} - -/// -@safe unittest -{ - import std.algorithm.iteration : map; - import std.algorithm.mutation : copy; - import std.array : appender; - - auto abuf = appender!(char[])(); - "hello".map!toUpper.copy(abuf); - assert(abuf.data == "HELLO"); -} - -@safe unittest -{ - static import std.ascii; - import std.format : format; - foreach (ch; 0 .. 0x80) - assert(std.ascii.toUpper(ch) == toUpper(ch)); - assert(toUpper('я') == 'ĐŻ'); - assert(toUpper('δ') == 'Δ'); - auto title = unicode.Titlecase_Letter; - foreach (ch; unicode.lowerCase.byCodepoint) - { - dchar up = ch.toUpper(); - assert(up == ch || isUpper(up) || title[up], - format("%x -> %x", ch, up)); - } -} - -/++ - Allocates a new array which is identical to `s` except that all of its - characters are converted to uppercase (by performing Unicode uppercase mapping). - If none of `s` characters were affected, then `s` itself is returned if `s` - is a `string`-like type. - - Params: - s = A $(REF_ALTTEXT random access range, isRandomAccessRange, std,range,primitives) - of characters - Returns: - An new array with the same element type as `s`. -+/ -ElementEncodingType!S[] toUpper(S)(return scope S s) @trusted -if (isSomeString!S) -{ - static import std.ascii; - return toCase!(UpperTriple, std.ascii.toUpper)(s); -} - -/// ditto -ElementEncodingType!S[] toUpper(S)(S s) -if (!isSomeString!S && (isRandomAccessRange!S && hasLength!S && hasSlicing!S && isSomeChar!(ElementType!S))) -{ - static import std.ascii; - return toCase!(UpperTriple, std.ascii.toUpper)(s); -} - -// overloads for the most common cases to reduce compile time -@safe pure /*TODO nothrow*/ -{ - string toUpper(return scope string s) - { return toUpper!string(s); } - wstring toUpper(return scope wstring s) - { return toUpper!wstring(s); } - dstring toUpper(return scope dstring s) - { return toUpper!dstring(s); } - - @safe unittest - { - // https://issues.dlang.org/show_bug.cgi?id=16663 - - static struct String - { - string data; - alias data this; - } - - void foo() - { - auto u = toUpper(String("")); - } - } -} - -@safe unittest -{ - import std.algorithm.comparison : cmp; - - string s1 = "FoL"; - string s2; - char[] s3; - - s2 = toUpper(s1); - s3 = s1.dup; toUpperInPlace(s3); - assert(s3 == s2, s3); - assert(cmp(s2, "FOL") == 0); - assert(s2 !is s1); - - s1 = "a\u0100B\u0101d"; - s2 = toUpper(s1); - s3 = s1.dup; toUpperInPlace(s3); - assert(s3 == s2); - assert(cmp(s2, "A\u0100B\u0100D") == 0); - assert(s2 !is s1); - - s1 = "a\u0460B\u0461d"; - s2 = toUpper(s1); - s3 = s1.dup; toUpperInPlace(s3); - assert(s3 == s2); - assert(cmp(s2, "A\u0460B\u0460D") == 0); - assert(s2 !is s1); -} - -@safe unittest -{ - static void doTest(C)(const(C)[] s, const(C)[] trueUp, const(C)[] trueLow) - { - import std.format : format; - string diff = "src: %( %x %)\nres: %( %x %)\ntru: %( %x %)"; - auto low = s.toLower() , up = s.toUpper(); - auto lowInp = s.dup, upInp = s.dup; - lowInp.toLowerInPlace(); - upInp.toUpperInPlace(); - assert(low == trueLow, format(diff, low, trueLow)); - assert(up == trueUp, format(diff, up, trueUp)); - assert(lowInp == trueLow, - format(diff, cast(const(ubyte)[]) s, cast(const(ubyte)[]) lowInp, cast(const(ubyte)[]) trueLow)); - assert(upInp == trueUp, - format(diff, cast(const(ubyte)[]) s, cast(const(ubyte)[]) upInp, cast(const(ubyte)[]) trueUp)); - } - static foreach (S; AliasSeq!(dstring, wstring, string)) - {{ - - S easy = "123"; - S good = "abCФоМ"; - S awful = "\u0131\u023f\u2126"; - S wicked = "\u0130\u1FE2"; - auto options = [easy, good, awful, wicked]; - S[] lower = ["123", "abcфеж", "\u0131\u023f\u03c9", "i\u0307\u1Fe2"]; - S[] upper = ["123", "ABCФЕЖ", "I\u2c7e\u2126", "\u0130\u03A5\u0308\u0300"]; - - foreach (val; [easy, good]) - { - auto e = val.dup; - auto g = e; - e.toUpperInPlace(); - assert(e is g); - e.toLowerInPlace(); - assert(e is g); - } - foreach (i, v; options) - { - doTest(v, upper[i], lower[i]); - } - - // a few combinatorial runs - foreach (i; 0 .. options.length) - foreach (j; i .. options.length) - foreach (k; j .. options.length) - { - auto sample = options[i] ~ options[j] ~ options[k]; - auto sample2 = options[k] ~ options[j] ~ options[i]; - doTest(sample, upper[i] ~ upper[j] ~ upper[k], - lower[i] ~ lower[j] ~ lower[k]); - doTest(sample2, upper[k] ~ upper[j] ~ upper[i], - lower[k] ~ lower[j] ~ lower[i]); - } - }} -} - -// test random access ranges -@safe pure unittest -{ - import std.algorithm.comparison : cmp; - import std.utf : byCodeUnit; - auto s1 = "FoL".byCodeUnit; - assert(s1.toUpper.cmp("FOL") == 0); - auto s2 = "a\u0460B\u0461d".byCodeUnit; - assert(s2.toUpper.cmp("A\u0460B\u0460D") == 0); -} - -/++ - Returns whether `c` is a Unicode alphabetic $(CHARACTER) - (general Unicode category: Alphabetic). -+/ -@safe pure nothrow @nogc -bool isAlpha(dchar c) -{ - // optimization - if (c < 0xAA) - { - return ('A' <= c && c <= 'Z') || ('a' <= c && c <= 'z'); - } - - return alphaTrie[c]; -} - -@safe unittest -{ - auto alpha = unicode("Alphabetic"); - foreach (ch; alpha.byCodepoint) - assert(isAlpha(ch)); - foreach (ch; 0 .. 0x4000) - assert((ch in alpha) == isAlpha(ch)); -} - - -/++ - Returns whether `c` is a Unicode mark - (general Unicode category: Mn, Me, Mc). -+/ -@safe pure nothrow @nogc -bool isMark(dchar c) -{ - return markTrie[c]; -} - -@safe unittest -{ - auto mark = unicode("Mark"); - foreach (ch; mark.byCodepoint) - assert(isMark(ch)); - foreach (ch; 0 .. 0x4000) - assert((ch in mark) == isMark(ch)); -} - -/++ - Returns whether `c` is a Unicode numerical $(CHARACTER) - (general Unicode category: Nd, Nl, No). -+/ -@safe pure nothrow @nogc -bool isNumber(dchar c) -{ - // optimization for ascii case - if (c <= 0x7F) - { - return c >= '0' && c <= '9'; - } - else - { - return numberTrie[c]; - } -} - -@safe unittest -{ - auto n = unicode("N"); - foreach (ch; n.byCodepoint) - assert(isNumber(ch)); - foreach (ch; 0 .. 0x4000) - assert((ch in n) == isNumber(ch)); -} - -/++ - Returns whether `c` is a Unicode alphabetic $(CHARACTER) or number. - (general Unicode category: Alphabetic, Nd, Nl, No). - - Params: - c = any Unicode character - Returns: - `true` if the character is in the Alphabetic, Nd, Nl, or No Unicode - categories -+/ -@safe pure nothrow @nogc -bool isAlphaNum(dchar c) -{ - static import std.ascii; - - // optimization for ascii case - if (std.ascii.isASCII(c)) - { - return std.ascii.isAlphaNum(c); - } - else - { - return isAlpha(c) || isNumber(c); - } -} - -@safe unittest -{ - auto n = unicode("N"); - auto alpha = unicode("Alphabetic"); - - foreach (ch; n.byCodepoint) - assert(isAlphaNum(ch)); - - foreach (ch; alpha.byCodepoint) - assert(isAlphaNum(ch)); - - foreach (ch; 0 .. 0x4000) - { - assert(((ch in n) || (ch in alpha)) == isAlphaNum(ch)); - } -} - -/++ - Returns whether `c` is a Unicode punctuation $(CHARACTER) - (general Unicode category: Pd, Ps, Pe, Pc, Po, Pi, Pf). -+/ -@safe pure nothrow @nogc -bool isPunctuation(dchar c) -{ - static import std.ascii; - - // optimization for ascii case - if (c <= 0x7F) - { - return std.ascii.isPunctuation(c); - } - else - { - return punctuationTrie[c]; - } -} - -@safe unittest -{ - assert(isPunctuation('\u0021')); - assert(isPunctuation('\u0028')); - assert(isPunctuation('\u0029')); - assert(isPunctuation('\u002D')); - assert(isPunctuation('\u005F')); - assert(isPunctuation('\u00AB')); - assert(isPunctuation('\u00BB')); - foreach (ch; unicode("P").byCodepoint) - assert(isPunctuation(ch)); -} - -/++ - Returns whether `c` is a Unicode symbol $(CHARACTER) - (general Unicode category: Sm, Sc, Sk, So). -+/ -@safe pure nothrow @nogc -bool isSymbol(dchar c) -{ - return symbolTrie[c]; -} - -@safe unittest -{ - import std.format : format; - assert(isSymbol('\u0024')); - assert(isSymbol('\u002B')); - assert(isSymbol('\u005E')); - assert(isSymbol('\u00A6')); - foreach (ch; unicode("S").byCodepoint) - assert(isSymbol(ch), format("%04x", ch)); -} - -/++ - Returns whether `c` is a Unicode space $(CHARACTER) - (general Unicode category: Zs) - Note: This doesn't include '\n', '\r', \t' and other non-space $(CHARACTER). - For commonly used less strict semantics see $(LREF isWhite). -+/ -@safe pure nothrow @nogc -bool isSpace(dchar c) -{ - import std.internal.unicode_tables : isSpaceGen; // generated file - return isSpaceGen(c); -} - -@safe unittest -{ - assert(isSpace('\u0020')); - auto space = unicode.Zs; - foreach (ch; space.byCodepoint) - assert(isSpace(ch)); - foreach (ch; 0 .. 0x1000) - assert(isSpace(ch) == space[ch]); -} - - -/++ - Returns whether `c` is a Unicode graphical $(CHARACTER) - (general Unicode category: L, M, N, P, S, Zs). - -+/ -@safe pure nothrow @nogc -bool isGraphical(dchar c) -{ - return graphicalTrie[c]; -} - - -@safe unittest -{ - auto set = unicode("Graphical"); - import std.format : format; - foreach (ch; set.byCodepoint) - assert(isGraphical(ch), format("%4x", ch)); - foreach (ch; 0 .. 0x4000) - assert((ch in set) == isGraphical(ch)); -} - - -/++ - Returns whether `c` is a Unicode control $(CHARACTER) - (general Unicode category: Cc). -+/ -@safe pure nothrow @nogc -bool isControl(dchar c) -{ - import std.internal.unicode_tables : isControlGen; // generated file - return isControlGen(c); -} - -@safe unittest -{ - assert(isControl('\u0000')); - assert(isControl('\u0081')); - assert(!isControl('\u0100')); - auto cc = unicode.Cc; - foreach (ch; cc.byCodepoint) - assert(isControl(ch)); - foreach (ch; 0 .. 0x1000) - assert(isControl(ch) == cc[ch]); -} - - -/++ - Returns whether `c` is a Unicode formatting $(CHARACTER) - (general Unicode category: Cf). -+/ -@safe pure nothrow @nogc -bool isFormat(dchar c) -{ - import std.internal.unicode_tables : isFormatGen; // generated file - return isFormatGen(c); -} - - -@safe unittest -{ - assert(isFormat('\u00AD')); - foreach (ch; unicode("Format").byCodepoint) - assert(isFormat(ch)); -} - -// code points for private use, surrogates are not likely to change in near feature -// if need be they can be generated from unicode data as well - -/++ - Returns whether `c` is a Unicode Private Use $(CODEPOINT) - (general Unicode category: Co). -+/ -@safe pure nothrow @nogc -bool isPrivateUse(dchar c) -{ - return (0x00_E000 <= c && c <= 0x00_F8FF) - || (0x0F_0000 <= c && c <= 0x0F_FFFD) - || (0x10_0000 <= c && c <= 0x10_FFFD); -} - -/++ - Returns whether `c` is a Unicode surrogate $(CODEPOINT) - (general Unicode category: Cs). -+/ -@safe pure nothrow @nogc -bool isSurrogate(dchar c) -{ - return (0xD800 <= c && c <= 0xDFFF); -} - -/++ - Returns whether `c` is a Unicode high surrogate (lead surrogate). -+/ -@safe pure nothrow @nogc -bool isSurrogateHi(dchar c) -{ - return (0xD800 <= c && c <= 0xDBFF); -} - -/++ - Returns whether `c` is a Unicode low surrogate (trail surrogate). -+/ -@safe pure nothrow @nogc -bool isSurrogateLo(dchar c) -{ - return (0xDC00 <= c && c <= 0xDFFF); -} - -/++ - Returns whether `c` is a Unicode non-character i.e. - a $(CODEPOINT) with no assigned abstract character. - (general Unicode category: Cn) -+/ -@safe pure nothrow @nogc -bool isNonCharacter(dchar c) -{ - return nonCharacterTrie[c]; -} - -@safe unittest -{ - auto set = unicode("Cn"); - foreach (ch; set.byCodepoint) - assert(isNonCharacter(ch)); -} - -private: -// load static data from pre-generated tables into usable datastructures - - -@safe auto asSet(const (ubyte)[] compressed) pure -{ - return CodepointSet.fromIntervals(decompressIntervals(compressed)); -} - -@safe pure nothrow auto asTrie(T...)(const scope TrieEntry!T e) -{ - return const(CodepointTrie!T)(e.offsets, e.sizes, e.data); -} - -@safe pure nothrow @nogc @property -{ - // It's important to use auto return here, so that the compiler - // only runs semantic on the return type if the function gets - // used. Also these are functions rather than templates to not - // increase the object size of the caller. - auto lowerCaseTrie() { static immutable res = asTrie(lowerCaseTrieEntries); return res; } - auto upperCaseTrie() { static immutable res = asTrie(upperCaseTrieEntries); return res; } - auto simpleCaseTrie() { static immutable res = asTrie(simpleCaseTrieEntries); return res; } - auto fullCaseTrie() { static immutable res = asTrie(fullCaseTrieEntries); return res; } - auto alphaTrie() { static immutable res = asTrie(alphaTrieEntries); return res; } - auto markTrie() { static immutable res = asTrie(markTrieEntries); return res; } - auto numberTrie() { static immutable res = asTrie(numberTrieEntries); return res; } - auto punctuationTrie() { static immutable res = asTrie(punctuationTrieEntries); return res; } - auto symbolTrie() { static immutable res = asTrie(symbolTrieEntries); return res; } - auto graphicalTrie() { static immutable res = asTrie(graphicalTrieEntries); return res; } - auto nonCharacterTrie() { static immutable res = asTrie(nonCharacterTrieEntries); return res; } - - //normalization quick-check tables - auto nfcQCTrie() - { - import std.internal.unicode_norm : nfcQCTrieEntries; - static immutable res = asTrie(nfcQCTrieEntries); - return res; - } - - auto nfdQCTrie() - { - import std.internal.unicode_norm : nfdQCTrieEntries; - static immutable res = asTrie(nfdQCTrieEntries); - return res; - } - - auto nfkcQCTrie() - { - import std.internal.unicode_norm : nfkcQCTrieEntries; - static immutable res = asTrie(nfkcQCTrieEntries); - return res; - } - - auto nfkdQCTrie() - { - import std.internal.unicode_norm : nfkdQCTrieEntries; - static immutable res = asTrie(nfkdQCTrieEntries); - return res; - } - - //grapheme breaking algorithm tables - auto spacingMarkTrie() - { - import std.internal.unicode_grapheme : spacingMarkTrieEntries; - static immutable res = asTrie(spacingMarkTrieEntries); - return res; - } - - auto graphemeExtendTrie() - { - import std.internal.unicode_grapheme : graphemeExtendTrieEntries; - static immutable res = asTrie(graphemeExtendTrieEntries); - return res; - } - - auto hangLV() - { - import std.internal.unicode_grapheme : hangulLVTrieEntries; - static immutable res = asTrie(hangulLVTrieEntries); - return res; - } - - auto hangLVT() - { - import std.internal.unicode_grapheme : hangulLVTTrieEntries; - static immutable res = asTrie(hangulLVTTrieEntries); - return res; - } - - auto prependTrie() - { - import std.internal.unicode_grapheme : prependTrieEntries; - static immutable res = asTrie(prependTrieEntries); - return res; - } - - auto graphemeControlTrie() - { - import std.internal.unicode_grapheme : controlTrieEntries; - static immutable res = asTrie(controlTrieEntries); - return res; - } - - auto xpictoTrie() - { - import std.internal.unicode_grapheme : Extended_PictographicTrieEntries; - static immutable res = asTrie(Extended_PictographicTrieEntries); - return res; - } - - // tables below are used for composition/decomposition - auto combiningClassTrie() - { - import std.internal.unicode_comp : combiningClassTrieEntries; - static immutable res = asTrie(combiningClassTrieEntries); - return res; - } - - auto compatMappingTrie() - { - import std.internal.unicode_decomp : compatMappingTrieEntries; - static immutable res = asTrie(compatMappingTrieEntries); - return res; - } - - auto canonMappingTrie() - { - import std.internal.unicode_decomp : canonMappingTrieEntries; - static immutable res = asTrie(canonMappingTrieEntries); - return res; - } - - auto compositionJumpTrie() - { - import std.internal.unicode_comp : compositionJumpTrieEntries; - static immutable res = asTrie(compositionJumpTrieEntries); - return res; - } - - //case conversion tables - auto toUpperIndexTrie() { static immutable res = asTrie(toUpperIndexTrieEntries); return res; } - auto toLowerIndexTrie() { static immutable res = asTrie(toLowerIndexTrieEntries); return res; } - auto toTitleIndexTrie() { static immutable res = asTrie(toTitleIndexTrieEntries); return res; } - //simple case conversion tables - auto toUpperSimpleIndexTrie() { static immutable res = asTrie(toUpperSimpleIndexTrieEntries); return res; } - auto toLowerSimpleIndexTrie() { static immutable res = asTrie(toLowerSimpleIndexTrieEntries); return res; } - auto toTitleSimpleIndexTrie() { static immutable res = asTrie(toTitleSimpleIndexTrieEntries); return res; } - -} - -}// version (!std_uni_bootstrap) diff --git a/phobos/std/uri.d b/phobos/std/uri.d deleted file mode 100644 index bf7cbc0..0000000 --- a/phobos/std/uri.d +++ /dev/null @@ -1,642 +0,0 @@ -// Written in the D programming language. - -/** - * Encode and decode Uniform Resource Identifiers (URIs). - * URIs are used in internet transfer protocols. - * Valid URI characters consist of letters, digits, - * and the characters $(B ;/?:@&=+$,-_.!~*'()) - * Reserved URI characters are $(B ;/?:@&=+$,) - * Escape sequences consist of $(B %) followed by two hex digits. - * - * See_Also: - * $(LINK2 https://www.ietf.org/rfc/rfc3986.txt, RFC 3986)<br> - * $(LINK2 http://en.wikipedia.org/wiki/Uniform_resource_identifier, Wikipedia) - * Copyright: Copyright The D Language Foundation 2000 - 2009. - * License: $(HTTP www.boost.org/LICENSE_1_0.txt, Boost License 1.0). - * Authors: $(HTTP digitalmars.com, Walter Bright) - * Source: $(PHOBOSSRC std/uri.d) - */ -/* Copyright The D Language Foundation 2000 - 2009. - * 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.uri; - -//debug=uri; // uncomment to turn on debugging writefln's -debug(uri) import std.stdio; -import std.traits : isSomeChar; - -/** This Exception is thrown if something goes wrong when encoding or -decoding a URI. -*/ -class URIException : Exception -{ - import std.exception : basicExceptionCtors; - mixin basicExceptionCtors; -} - -/// -@safe unittest -{ - import std.exception : assertThrown; - assertThrown!URIException("%ab".decode); -} - -private enum -{ - URI_Alpha = 1, - URI_Reserved = 2, - URI_Mark = 4, - URI_Digit = 8, - URI_Hash = 0x10, // '#' -} - -private immutable char[16] hex2ascii = "0123456789ABCDEF"; - -private immutable ubyte[128] uri_flags = // indexed by character - ({ - ubyte[128] uflags; - - // Compile time initialize - uflags['#'] |= URI_Hash; - - foreach (c; 'A' .. 'Z' + 1) - { - uflags[c] |= URI_Alpha; - uflags[c + 0x20] |= URI_Alpha; // lowercase letters - } - foreach (c; '0' .. '9' + 1) uflags[c] |= URI_Digit; - foreach (c; ";/?:@&=+$,") uflags[c] |= URI_Reserved; - foreach (c; "-_.!~*'()") uflags[c] |= URI_Mark; - return uflags; - })(); - -private string URI_Encode(dstring str, uint unescapedSet) @safe pure -{ - uint j; - uint k; - dchar V; - dchar C; - - // result buffer - char[50] buffer = void; - char[] R; - uint Rlen; - uint Rsize; // alloc'd size - - immutable len = str.length; - - R = buffer[]; - Rsize = buffer.length; - Rlen = 0; - - for (k = 0; k != len; k++) - { - C = str[k]; - // if (C in unescapedSet) - if (C < uri_flags.length && uri_flags[C] & unescapedSet) - { - if (Rlen == Rsize) - { - char[] R2; - - Rsize *= 2; - R2 = new char[Rsize]; - R2[0 .. Rlen] = R[0 .. Rlen]; - R = R2; - } - R[Rlen] = cast(char) C; - Rlen++; - } - else - { - char[6] Octet; - uint L; - - V = C; - - // Transform V into octets - if (V <= 0x7F) - { - Octet[0] = cast(char) V; - L = 1; - } - else if (V <= 0x7FF) - { - Octet[0] = cast(char)(0xC0 | (V >> 6)); - Octet[1] = cast(char)(0x80 | (V & 0x3F)); - L = 2; - } - else if (V <= 0xFFFF) - { - Octet[0] = cast(char)(0xE0 | (V >> 12)); - Octet[1] = cast(char)(0x80 | ((V >> 6) & 0x3F)); - Octet[2] = cast(char)(0x80 | (V & 0x3F)); - L = 3; - } - else if (V <= 0x1FFFFF) - { - Octet[0] = cast(char)(0xF0 | (V >> 18)); - Octet[1] = cast(char)(0x80 | ((V >> 12) & 0x3F)); - Octet[2] = cast(char)(0x80 | ((V >> 6) & 0x3F)); - Octet[3] = cast(char)(0x80 | (V & 0x3F)); - L = 4; - } - else - { - throw new URIException("Undefined UTF-32 code point"); - } - - if (Rlen + L * 3 > Rsize) - { - char[] R2; - - Rsize = 2 * (Rlen + L * 3); - R2 = new char[Rsize]; - R2[0 .. Rlen] = R[0 .. Rlen]; - R = R2; - } - - for (j = 0; j < L; j++) - { - R[Rlen] = '%'; - R[Rlen + 1] = hex2ascii[Octet[j] >> 4]; - R[Rlen + 2] = hex2ascii[Octet[j] & 15]; - - Rlen += 3; - } - } - } - - return R[0 .. Rlen].idup; -} - -@safe pure unittest -{ - import std.exception : assertThrown; - - assert(URI_Encode("", 0) == ""); - assert(URI_Encode(URI_Decode("%F0%BF%BF%BF", 0), 0) == "%F0%BF%BF%BF"); - dstring a; - a ~= cast(dchar) 0xFFFFFFFF; - assertThrown(URI_Encode(a, 0)); - assert(URI_Encode("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", 0).length == 3 * 60); -} - -private uint ascii2hex(dchar c) @nogc @safe pure nothrow -{ - return (c <= '9') ? c - '0' : - (c <= 'F') ? c - 'A' + 10 : - c - 'a' + 10; -} - -private dstring URI_Decode(Char)(scope const(Char)[] uri, uint reservedSet) -if (isSomeChar!Char) -{ - import std.ascii : isHexDigit; - - uint j; - uint k; - uint V; - dchar C; - - uint Rlen; - immutable len = uri.length; - auto s = uri; - - auto Rsize = len; - dchar[] R = new dchar[Rsize]; - Rlen = 0; - - for (k = 0; k != len; k++) - { - char B; - uint start; - - C = s[k]; - if (C != '%') - { - R[Rlen] = C; - Rlen++; - continue; - } - start = k; - if (k + 2 >= len) - throw new URIException("Unexpected end of URI"); - if (!isHexDigit(s[k + 1]) || !isHexDigit(s[k + 2])) - throw new URIException("Expected two hexadecimal digits after '%'"); - B = cast(char)((ascii2hex(s[k + 1]) << 4) + ascii2hex(s[k + 2])); - k += 2; - if ((B & 0x80) == 0) - { - C = B; - } - else - { - uint n; - - for (n = 1; ; n++) - { - if (n > 4) - throw new URIException("UTF-32 code point size too large"); - if (((B << n) & 0x80) == 0) - { - if (n == 1) - throw new URIException("UTF-32 code point size too small"); - break; - } - } - - // Pick off (7 - n) significant bits of B from first byte of octet - V = B & ((1 << (7 - n)) - 1); // (!!!) - - if (k + (3 * (n - 1)) >= len) - throw new URIException("UTF-32 unaligned String"); - for (j = 1; j != n; j++) - { - k++; - if (s[k] != '%') - throw new URIException("Expected: '%'"); - if (!isHexDigit(s[k + 1]) || !isHexDigit(s[k + 2])) - throw new URIException("Expected two hexadecimal digits after '%'"); - B = cast(char)((ascii2hex(s[k + 1]) << 4) + ascii2hex(s[k + 2])); - if ((B & 0xC0) != 0x80) - throw new URIException("Incorrect UTF-32 multi-byte sequence"); - k += 2; - V = (V << 6) | (B & 0x3F); - } - if (V > 0x10FFFF) - throw new URIException("Unknown UTF-32 code point"); - C = V; - } - if (C < uri_flags.length && uri_flags[C] & reservedSet) - { - // R ~= s[start .. k + 1]; - immutable width = (k + 1) - start; - for (int ii = 0; ii < width; ii++) - R[Rlen + ii] = s[start + ii]; - Rlen += width; - } - else - { - R[Rlen] = C; - Rlen++; - } - } - assert(Rlen <= Rsize); // enforce our preallocation size guarantee - - // Copy array on stack to array in memory - return R[0 .. Rlen].idup; -} - -@safe pure unittest -{ - import std.exception : assertThrown; - - assert(URI_Decode("", 0) == ""); - assertThrown!URIException(URI_Decode("%", 0)); - assertThrown!URIException(URI_Decode("%xx", 0)); - assertThrown!URIException(URI_Decode("%FF", 0)); - assertThrown!URIException(URI_Decode("%C0", 0)); - assertThrown!URIException(URI_Decode("%C0000000", 0)); - assertThrown!URIException(URI_Decode("%C0%xx0000", 0)); - assertThrown!URIException(URI_Decode("%C0%C00000", 0)); - assertThrown!URIException(URI_Decode("%F7%BF%BF%BF", 0)); - assert(URI_Decode("%23", URI_Hash) == "%23"); -} - -/************************************* - * Decodes the URI string encodedURI into a UTF-8 string and returns it. - * Escape sequences that resolve to reserved URI characters are not replaced. - * Escape sequences that resolve to the '#' character are not replaced. - */ -string decode(Char)(scope const(Char)[] encodedURI) -if (isSomeChar!Char) -{ - import std.algorithm.iteration : each; - import std.utf : encode; - auto s = URI_Decode(encodedURI, URI_Reserved | URI_Hash); - char[] r; - s.each!(c => encode(r, c)); - return r; -} - -/// -@safe unittest -{ - assert("foo%20bar".decode == "foo bar"); - assert("%3C%3E.@.%E2%84%A2".decode == "<>.@.™"); - assert("foo&/".decode == "foo&/"); - assert("!@#$&*(".decode == "!@#$&*("); -} - -/******************************* - * Decodes the URI string encodedURI into a UTF-8 string and returns it. All - * escape sequences are decoded. - */ -string decodeComponent(Char)(scope const(Char)[] encodedURIComponent) -if (isSomeChar!Char) -{ - import std.algorithm.iteration : each; - import std.utf : encode; - auto s = URI_Decode(encodedURIComponent, 0); - char[] r; - s.each!(c => encode(r, c)); - return r; -} - -/// -@safe unittest -{ - assert("foo%2F%26".decodeComponent == "foo/&"); - assert("dl%C3%A4ng%20r%C3%B6cks".decodeComponent == "dläng rĂścks"); - assert("!%40%23%24%25%5E%26*(".decodeComponent == "!@#$%^&*("); -} - -/***************************** - * Encodes the UTF-8 string uri into a URI and returns that URI. Any character - * not a valid URI character is escaped. The '#' character is not escaped. - */ -string encode(Char)(scope const(Char)[] uri) -if (isSomeChar!Char) -{ - import std.utf : toUTF32; - auto s = toUTF32(uri); - return URI_Encode(s, URI_Reserved | URI_Hash | URI_Alpha | URI_Digit | URI_Mark); -} - -/// -@safe unittest -{ - assert("foo bar".encode == "foo%20bar"); - assert("<>.@.™".encode == "%3C%3E.@.%E2%84%A2"); - assert("foo/#?a=1&b=2".encode == "foo/#?a=1&b=2"); - assert("dlang+rocks!".encode == "dlang+rocks!"); - assert("!@#$%^&*(".encode == "!@#$%25%5E&*("); -} - -/******************************** - * Encodes the UTF-8 string uriComponent into a URI and returns that URI. - * Any character not a letter, digit, or one of -_.!~*'() is escaped. - */ -string encodeComponent(Char)(scope const(Char)[] uriComponent) -if (isSomeChar!Char) -{ - import std.utf : toUTF32; - auto s = toUTF32(uriComponent); - return URI_Encode(s, URI_Alpha | URI_Digit | URI_Mark); -} - -/// -@safe unittest -{ - assert("!@#$%^&*(".encodeComponent == "!%40%23%24%25%5E%26*("); - assert("<>.@.™".encodeComponent == "%3C%3E.%40.%E2%84%A2"); - assert("foo/&".encodeComponent == "foo%2F%26"); - assert("dläng rĂścks".encodeComponent == "dl%C3%A4ng%20r%C3%B6cks"); - assert("dlang+rocks!".encodeComponent == "dlang%2Brocks!"); -} - -/* Encode associative array using www-form-urlencoding - * - * Params: - * values = an associative array containing the values to be encoded. - * - * Returns: - * A string encoded using www-form-urlencoding. - */ -package string urlEncode(scope string[string] values) @safe pure -{ - if (values.length == 0) - return ""; - - import std.array : Appender; - import std.format.write : formattedWrite; - - Appender!string enc; - enc.reserve(values.length * 128); - - bool first = true; - foreach (k, v; values) - { - if (!first) - enc.put('&'); - formattedWrite(enc, "%s=%s", encodeComponent(k), encodeComponent(v)); - first = false; - } - return enc.data; -} - -@safe pure unittest -{ - // @system because urlEncode -> encodeComponent -> URI_Encode - // URI_Encode uses alloca and pointer slicing - string[string] a; - assert(urlEncode(a) == ""); - assert(urlEncode(["name1" : "value1"]) == "name1=value1"); - auto enc = urlEncode(["name1" : "value1", "name2" : "value2"]); - assert(enc == "name1=value1&name2=value2" || enc == "name2=value2&name1=value1"); -} - -/*************************** - * Does string s[] start with a URL? - * Returns: - * -1 it does not - * len it does, and s[0 .. len] is the slice of s[] that is that URL - */ - -ptrdiff_t uriLength(Char)(scope const(Char)[] s) -if (isSomeChar!Char) -{ - /* Must start with one of: - * http:// - * https:// - * www. - */ - import std.ascii : isAlphaNum; - import std.uni : icmp; - - ptrdiff_t i; - - if (s.length <= 4) - return -1; - - if (s.length > 7 && icmp(s[0 .. 7], "http://") == 0) - { - i = 7; - } - else - { - if (s.length > 8 && icmp(s[0 .. 8], "https://") == 0) - i = 8; - else - return -1; - } - - ptrdiff_t lastdot; - for (; i < s.length; i++) - { - auto c = s[i]; - if (isAlphaNum(c)) - continue; - if (c == '-' || c == '_' || c == '?' || - c == '=' || c == '%' || c == '&' || - c == '/' || c == '+' || c == '#' || - c == '~' || c == '$') - continue; - if (c == '.') - { - lastdot = i; - continue; - } - break; - } - if (!lastdot) - return -1; - - return i; -} - -/// -@safe pure unittest -{ - string s1 = "http://www.digitalmars.com/~fred/fredsRX.html#foo end!"; - assert(uriLength(s1) == 49); - string s2 = "no uri here"; - assert(uriLength(s2) == -1); - assert(uriLength("issue 14924") < 0); -} - -@safe pure nothrow @nogc unittest -{ - assert(uriLength("") == -1); - assert(uriLength("https://www") == -1); -} - -/*************************** - * Does string s[] start with an email address? - * Returns: - * -1 it does not - * len it does, and s[0 .. i] is the slice of s[] that is that email address - * References: - * RFC2822 - */ -ptrdiff_t emailLength(Char)(scope const(Char)[] s) -if (isSomeChar!Char) -{ - import std.ascii : isAlpha, isAlphaNum; - - ptrdiff_t i; - - if (s.length == 0) - return -1; - - if (!isAlpha(s[0])) - return -1; - - for (i = 1; 1; i++) - { - if (i == s.length) - return -1; - auto c = s[i]; - if (isAlphaNum(c)) - continue; - if (c == '-' || c == '_' || c == '.') - continue; - if (c != '@') - return -1; - i++; - break; - } - - /* Now do the part past the '@' - */ - ptrdiff_t lastdot; - for (; i < s.length; i++) - { - auto c = s[i]; - if (isAlphaNum(c)) - continue; - if (c == '-' || c == '_') - continue; - if (c == '.') - { - lastdot = i; - continue; - } - break; - } - if (!lastdot || (i - lastdot != 3 && i - lastdot != 4)) - return -1; - - return i; -} - -/// -@safe pure unittest -{ - string s1 = "my.e-mail@www.example-domain.com with garbage added"; - assert(emailLength(s1) == 32); - string s2 = "no email address here"; - assert(emailLength(s2) == -1); - assert(emailLength("issue 14924") < 0); -} - -@safe pure unittest -{ - //@system because of encode -> URI_Encode - debug(uri) writeln("uri.encodeURI.unittest"); - - string source = "http://www.digitalmars.com/~fred/fred's RX.html#foo"; - string target = "http://www.digitalmars.com/~fred/fred's%20RX.html#foo"; - - auto result = encode(source); - debug(uri) writefln("result = '%s'", result); - assert(result == target); - result = decode(target); - debug(uri) writefln("result = '%s'", result); - assert(result == source); - - result = encode(decode("%E3%81%82%E3%81%82")); - assert(result == "%E3%81%82%E3%81%82"); - - result = encodeComponent("c++"); - assert(result == "c%2B%2B"); - - auto str = new char[10_000_000]; - str[] = 'A'; - result = encodeComponent(str); - foreach (char c; result) - assert(c == 'A'); - - result = decode("%41%42%43"); - debug(uri) writeln(result); - - import std.meta : AliasSeq; - static foreach (StringType; AliasSeq!(char[], wchar[], dchar[], string, wstring, dstring)) - {{ - import std.conv : to; - StringType decoded1 = source.to!StringType; - string encoded1 = encode(decoded1); - assert(decoded1 == source.to!StringType); // check that `decoded1` wasn't changed - assert(encoded1 == target); - assert(decoded1 == decode(encoded1).to!StringType); - - StringType encoded2 = target.to!StringType; - string decoded2 = decode(encoded2); - assert(encoded2 == target.to!StringType); // check that `encoded2` wasn't changed - assert(decoded2 == source); - assert(encoded2 == encode(decoded2).to!StringType); - }} -} - -@safe pure nothrow @nogc unittest -{ - assert(emailLength("") == -1); - assert(emailLength("@") == -1); - assert(emailLength("abcd") == -1); - assert(emailLength("blah@blub") == -1); - assert(emailLength("blah@blub.") == -1); - assert(emailLength("blah@blub.domain") == -1); -} diff --git a/phobos/std/utf.d b/phobos/std/utf.d deleted file mode 100644 index c0cd386..0000000 --- a/phobos/std/utf.d +++ /dev/null @@ -1,4731 +0,0 @@ -// Written in the D programming language. - -/++ - Encode and decode UTF-8, UTF-16 and UTF-32 strings. - - UTF character support is restricted to - $(D '\u0000' <= character <= '\U0010FFFF'). - -$(SCRIPT inhibitQuickIndex = 1;) -$(DIVC quickindex, -$(BOOKTABLE, -$(TR $(TH Category) $(TH Functions)) -$(TR $(TD Decode) $(TD - $(LREF decode) - $(LREF decodeFront) -)) -$(TR $(TD Lazy decode) $(TD - $(LREF byCodeUnit) - $(LREF byChar) - $(LREF byWchar) - $(LREF byDchar) - $(LREF byUTF) -)) -$(TR $(TD Encode) $(TD - $(LREF encode) - $(LREF toUTF8) - $(LREF toUTF16) - $(LREF toUTF32) - $(LREF toUTFz) - $(LREF toUTF16z) -)) -$(TR $(TD Length) $(TD - $(LREF codeLength) - $(LREF count) - $(LREF stride) - $(LREF strideBack) -)) -$(TR $(TD Index) $(TD - $(LREF toUCSindex) - $(LREF toUTFindex) -)) -$(TR $(TD Validation) $(TD - $(LREF isValidDchar) - $(LREF isValidCodepoint) - $(LREF validate) -)) -$(TR $(TD Miscellaneous) $(TD - $(LREF replacementDchar) - $(LREF UseReplacementDchar) - $(LREF UTFException) -)) -)) - See_Also: - $(LINK2 http://en.wikipedia.org/wiki/Unicode, Wikipedia)<br> - $(LINK http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8)<br> - $(LINK http://anubis.dkuug.dk/JTC1/SC2/WG2/docs/n1335) - Copyright: Copyright The D Language Foundation 2000 - 2012. - License: $(HTTP www.boost.org/LICENSE_1_0.txt, Boost License 1.0). - Authors: $(HTTP digitalmars.com, Walter Bright) and - $(HTTP jmdavisprog.com, Jonathan M Davis) - Source: $(PHOBOSSRC std/utf.d) - +/ -module std.utf; - -import std.exception : basicExceptionCtors; -import core.exception : UnicodeException; -import std.meta : AliasSeq; -import std.range; -import std.traits : isAutodecodableString, isConvertibleToString, - isSomeChar, isSomeString, isStaticArray, Unqual; -import std.typecons : Flag, Yes, No; - - -/++ - Exception thrown on errors in std.utf functions. - +/ -class UTFException : UnicodeException -{ - import core.internal.string : unsignedToTempString, UnsignedStringBuf; - - uint[4] sequence; - size_t len; - - @safe pure nothrow @nogc - UTFException setSequence(scope uint[] data...) return - { - assert(data.length <= 4); - - len = data.length < 4 ? data.length : 4; - sequence[0 .. len] = data[0 .. len]; - - return this; - } - - // FIXME: Use std.exception.basicExceptionCtors here once - // https://issues.dlang.org/show_bug.cgi?id=11500 is fixed - - /** - Standard exception constructors. - */ - this(string msg, string file = __FILE__, size_t line = __LINE__, - Throwable next = null) @nogc @safe pure nothrow - { - super(msg, 0, file, line, next); - } - /// ditto - this(string msg, size_t index, string file = __FILE__, - size_t line = __LINE__, Throwable next = null) @safe pure nothrow - { - UnsignedStringBuf buf = void; - msg ~= " (at index " ~ unsignedToTempString(index, buf) ~ ")"; - super(msg, index, file, line, next); - } - - /** - Returns: - A `string` detailing the invalid UTF sequence. - */ - override string toString() const - { - if (len == 0) - { - /* Exception.toString() is not marked as const, although - * it is const-compatible. - */ - //return super.toString(); - auto e = () @trusted { return cast(Exception) super; } (); - return e.toString(); - } - - string result = "Invalid UTF sequence:"; - - foreach (i; sequence[0 .. len]) - { - UnsignedStringBuf buf = void; - result ~= ' '; - auto h = unsignedToTempString!16(i, buf); - if (h.length == 1) - result ~= '0'; - result ~= h; - result ~= 'x'; - } - - if (super.msg.length > 0) - { - result ~= " - "; - result ~= super.msg; - } - - return result; - } -} - -/// -@safe unittest -{ - import std.exception : assertThrown; - - char[4] buf; - assertThrown!UTFException(encode(buf, cast(dchar) 0xD800)); - assertThrown!UTFException(encode(buf, cast(dchar) 0xDBFF)); - assertThrown!UTFException(encode(buf, cast(dchar) 0xDC00)); - assertThrown!UTFException(encode(buf, cast(dchar) 0xDFFF)); - assertThrown!UTFException(encode(buf, cast(dchar) 0x110000)); -} - -/* - Provide array of invalidly encoded UTF strings. Useful for testing. - - Params: - Char = char, wchar, or dchar - - Returns: - an array of invalidly encoded UTF strings - */ - -package auto invalidUTFstrings(Char)() @safe pure @nogc nothrow -if (isSomeChar!Char) -{ - static if (is(Char == char)) - { - enum x = 0xDC00; // invalid surrogate value - enum y = 0x110000; // out of range - - static immutable string[8] result = - [ - "\x80", // not a start byte - "\xC0", // truncated - "\xC0\xC0", // invalid continuation - "\xF0\x82\x82\xAC", // overlong - [ - 0xE0 | (x >> 12), - 0x80 | ((x >> 6) & 0x3F), - 0x80 | (x & 0x3F) - ], - [ - cast(char)(0xF0 | (y >> 18)), - cast(char)(0x80 | ((y >> 12) & 0x3F)), - cast(char)(0x80 | ((y >> 6) & 0x3F)), - cast(char)(0x80 | (y & 0x3F)) - ], - [ - cast(char)(0xF8 | 3), // 5 byte encoding - cast(char)(0x80 | 3), - cast(char)(0x80 | 3), - cast(char)(0x80 | 3), - cast(char)(0x80 | 3), - ], - [ - cast(char)(0xFC | 3), // 6 byte encoding - cast(char)(0x80 | 3), - cast(char)(0x80 | 3), - cast(char)(0x80 | 3), - cast(char)(0x80 | 3), - cast(char)(0x80 | 3), - ], - ]; - - return result[]; - } - else static if (is(Char == wchar)) - { - static immutable wstring[5] result = - [ - [ - cast(wchar) 0xDC00, - ], - [ - cast(wchar) 0xDFFF, - ], - [ - cast(wchar) 0xDBFF, - cast(wchar) 0xDBFF, - ], - [ - cast(wchar) 0xDBFF, - cast(wchar) 0xE000, - ], - [ - cast(wchar) 0xD800, - ], - ]; - - return result[]; - } - else static if (is(Char == dchar)) - { - static immutable dstring[3] result = - [ - [ cast(dchar) 0x110000 ], - [ cast(dchar) 0x00D800 ], - [ cast(dchar) 0x00DFFF ], - ]; - - return result; - } - else - static assert(0); -} - -/++ - Check whether the given Unicode code point is valid. - - Params: - c = code point to check - - Returns: - `true` if and only if `c` is a valid Unicode code point - - Note: - `'\uFFFE'` and `'\uFFFF'` are considered valid by `isValidDchar`, - as they are permitted for internal use by an application, but they are - not allowed for interchange by the Unicode standard. - +/ -bool isValidDchar(dchar c) pure nothrow @safe @nogc -{ - return c < 0xD800 || (c > 0xDFFF && c <= 0x10FFFF); -} - -/// -@safe @nogc pure nothrow unittest -{ - assert( isValidDchar(cast(dchar) 0x41)); - assert( isValidDchar(cast(dchar) 0x00)); - assert(!isValidDchar(cast(dchar) 0xD800)); - assert(!isValidDchar(cast(dchar) 0x11FFFF)); -} - -pure nothrow @safe @nogc unittest -{ - import std.exception; - - assertCTFEable!( - { - assert( isValidDchar(cast(dchar)'a') == true); - assert( isValidDchar(cast(dchar) 0x1FFFFF) == false); - - assert(!isValidDchar(cast(dchar) 0x00D800)); - assert(!isValidDchar(cast(dchar) 0x00DBFF)); - assert(!isValidDchar(cast(dchar) 0x00DC00)); - assert(!isValidDchar(cast(dchar) 0x00DFFF)); - assert( isValidDchar(cast(dchar) 0x00FFFE)); - assert( isValidDchar(cast(dchar) 0x00FFFF)); - assert( isValidDchar(cast(dchar) 0x01FFFF)); - assert( isValidDchar(cast(dchar) 0x10FFFF)); - assert(!isValidDchar(cast(dchar) 0x110000)); - }); -} - -/** -Checks if a single character forms a valid code point. - -When standing alone, some characters are invalid code points. For -example the `wchar` `0xD800` is a so called high surrogate, which can -only be interpreted together with a low surrogate following it. As a -standalone character it is considered invalid. - -See $(LINK2 http://www.unicode.org/versions/Unicode13.0.0/, -Unicode Standard, D90, D91 and D92) for more details. - -Params: - c = character to test - Char = character type of `c` - -Returns: - `true`, if `c` forms a valid code point. - */ -bool isValidCodepoint(Char)(Char c) -if (isSomeChar!Char) -{ - alias UChar = typeof(cast() c); - static if (is(UChar == char)) - { - return c <= 0x7F; - } - else static if (is(UChar == wchar)) - { - return c <= 0xD7FF || c >= 0xE000; - } - else static if (is(UChar == dchar)) - { - return isValidDchar(c); - } - else - static assert(false, "unknown character type: `" ~ Char.stringof ~ "`"); -} - -/// -@safe pure nothrow unittest -{ - assert( isValidCodepoint(cast(char) 0x40)); - assert(!isValidCodepoint(cast(char) 0x80)); - assert( isValidCodepoint(cast(wchar) 0x1234)); - assert(!isValidCodepoint(cast(wchar) 0xD800)); - assert( isValidCodepoint(cast(dchar) 0x0010FFFF)); - assert(!isValidCodepoint(cast(dchar) 0x12345678)); -} - -/++ - Calculate the length of the UTF sequence starting at `index` - in `str`. - - Params: - str = $(REF_ALTTEXT input range, isInputRange, std,range,primitives) - of UTF code units. Must be random access if `index` is passed - index = starting index of UTF sequence (default: `0`) - - Returns: - The number of code units in the UTF sequence. For UTF-8, this is a - value between 1 and 4 (as per $(HTTP tools.ietf.org/html/rfc3629#section-3, RFC 3629$(COMMA) section 3)). - For UTF-16, it is either 1 or 2. For UTF-32, it is always 1. - - Throws: - May throw a `UTFException` if `str[index]` is not the start of a - valid UTF sequence. - - Note: - `stride` will only analyze the first `str[index]` element. It - will not fully verify the validity of the UTF sequence, nor even verify - the presence of the sequence: it will not actually guarantee that - $(D index + stride(str, index) <= str.length). - +/ -uint stride(S)(auto ref S str, size_t index) -if (is(S : const char[]) || - (isRandomAccessRange!S && is(immutable ElementType!S == immutable char))) -{ - static if (is(typeof(str.length) : ulong)) - assert(index < str.length, "Past the end of the UTF-8 sequence"); - immutable c = str[index]; - - if (c < 0x80) - return 1; - else - return strideImpl(c, index); -} - -/// Ditto -uint stride(S)(auto ref S str) -if (is(S : const char[]) || - (isInputRange!S && is(immutable ElementType!S == immutable char))) -{ - static if (is(S : const char[])) - immutable c = str[0]; - else - immutable c = str.front; - - if (c < 0x80) - return 1; - else - return strideImpl(c, 0); -} - -@system unittest -{ - import core.exception : AssertError; - import std.conv : to; - import std.exception; - import std.string : format; - import std.traits : FunctionAttribute, functionAttributes, isSafe; - static void test(string s, dchar c, size_t i = 0, size_t line = __LINE__) - { - enforce(stride(s, i) == codeLength!char(c), - new AssertError(format("Unit test failure string: %s", s), __FILE__, line)); - - enforce(stride(RandomCU!char(s), i) == codeLength!char(c), - new AssertError(format("Unit test failure range: %s", s), __FILE__, line)); - - auto refRandom = new RefRandomCU!char(s); - immutable randLen = refRandom.length; - enforce(stride(refRandom, i) == codeLength!char(c), - new AssertError(format("Unit test failure rand ref range: %s", s), __FILE__, line)); - enforce(refRandom.length == randLen, - new AssertError(format("Unit test failure rand ref range length: %s", s), __FILE__, line)); - - if (i == 0) - { - enforce(stride(s) == codeLength!char(c), - new AssertError(format("Unit test failure string 0: %s", s), __FILE__, line)); - - enforce(stride(InputCU!char(s)) == codeLength!char(c), - new AssertError(format("Unit test failure range 0: %s", s), __FILE__, line)); - - auto refBidir = new RefBidirCU!char(s); - immutable bidirLen = refBidir.length; - enforce(stride(refBidir) == codeLength!char(c), - new AssertError(format("Unit test failure bidir ref range code length: %s", s), __FILE__, line)); - enforce(refBidir.length == bidirLen, - new AssertError(format("Unit test failure bidir ref range length: %s", s), __FILE__, line)); - } - } - - assertCTFEable!( - { - test("a", 'a'); - test(" ", ' '); - test("\u2029", '\u2029'); //paraSep - test("\u0100", '\u0100'); - test("\u0430", '\u0430'); - test("\U00010143", '\U00010143'); - test("abcdefcdef", 'a'); - test("hello\U00010143\u0100\U00010143", 'h', 0); - test("hello\U00010143\u0100\U00010143", 'e', 1); - test("hello\U00010143\u0100\U00010143", 'l', 2); - test("hello\U00010143\u0100\U00010143", 'l', 3); - test("hello\U00010143\u0100\U00010143", 'o', 4); - test("hello\U00010143\u0100\U00010143", '\U00010143', 5); - test("hello\U00010143\u0100\U00010143", '\u0100', 9); - test("hello\U00010143\u0100\U00010143", '\U00010143', 11); - - foreach (S; AliasSeq!(char[], const char[], string)) - { - enum str = to!S("hello world"); - static assert(isSafe!({ stride(str, 0); })); - static assert(isSafe!({ stride(str); })); - static assert((functionAttributes!({ stride(str, 0); }) & FunctionAttribute.pure_) != 0); - static assert((functionAttributes!({ stride(str); }) & FunctionAttribute.pure_) != 0); - } - }); -} - -@safe unittest // invalid start bytes -{ - import std.exception : assertThrown; - immutable char[] invalidStartBytes = [ - 0b1111_1000, // indicating a sequence length of 5 - 0b1111_1100, // 6 - 0b1111_1110, // 7 - 0b1111_1111, // 8 - 0b1000_0000, // continuation byte - ]; - foreach (c; invalidStartBytes) - assertThrown!UTFException(stride([c])); -} - -/// Ditto -uint stride(S)(auto ref S str, size_t index) -if (is(S : const wchar[]) || - (isRandomAccessRange!S && is(immutable ElementType!S == immutable wchar))) -{ - static if (is(typeof(str.length) : ulong)) - assert(index < str.length, "Past the end of the UTF-16 sequence"); - immutable uint u = str[index]; - return 1 + (u >= 0xD800 && u <= 0xDBFF); -} - -/// Ditto -uint stride(S)(auto ref S str) @safe pure -if (is(S : const wchar[])) -{ - return stride(str, 0); -} - -/// Ditto -uint stride(S)(auto ref S str) -if (isInputRange!S && is(immutable ElementType!S == immutable wchar) && - !is(S : const wchar[])) -{ - assert(!str.empty, "UTF-16 sequence is empty"); - immutable uint u = str.front; - return 1 + (u >= 0xD800 && u <= 0xDBFF); -} - -@system unittest -{ - import core.exception : AssertError; - import std.conv : to; - import std.exception; - import std.string : format; - import std.traits : FunctionAttribute, functionAttributes, isSafe; - static void test(wstring s, dchar c, size_t i = 0, size_t line = __LINE__) - { - enforce(stride(s, i) == codeLength!wchar(c), - new AssertError(format("Unit test failure string: %s", s), __FILE__, line)); - - enforce(stride(RandomCU!wchar(s), i) == codeLength!wchar(c), - new AssertError(format("Unit test failure range: %s", s), __FILE__, line)); - - auto refRandom = new RefRandomCU!wchar(s); - immutable randLen = refRandom.length; - enforce(stride(refRandom, i) == codeLength!wchar(c), - new AssertError(format("Unit test failure rand ref range: %s", s), __FILE__, line)); - enforce(refRandom.length == randLen, - new AssertError(format("Unit test failure rand ref range length: %s", s), __FILE__, line)); - - if (i == 0) - { - enforce(stride(s) == codeLength!wchar(c), - new AssertError(format("Unit test failure string 0: %s", s), __FILE__, line)); - - enforce(stride(InputCU!wchar(s)) == codeLength!wchar(c), - new AssertError(format("Unit test failure range 0: %s", s), __FILE__, line)); - - auto refBidir = new RefBidirCU!wchar(s); - immutable bidirLen = refBidir.length; - enforce(stride(refBidir) == codeLength!wchar(c), - new AssertError(format("Unit test failure bidir ref range code length: %s", s), __FILE__, line)); - enforce(refBidir.length == bidirLen, - new AssertError(format("Unit test failure bidir ref range length: %s", s), __FILE__, line)); - } - } - - assertCTFEable!( - { - test("a", 'a'); - test(" ", ' '); - test("\u2029", '\u2029'); //paraSep - test("\u0100", '\u0100'); - test("\u0430", '\u0430'); - test("\U00010143", '\U00010143'); - test("abcdefcdef", 'a'); - test("hello\U00010143\u0100\U00010143", 'h', 0); - test("hello\U00010143\u0100\U00010143", 'e', 1); - test("hello\U00010143\u0100\U00010143", 'l', 2); - test("hello\U00010143\u0100\U00010143", 'l', 3); - test("hello\U00010143\u0100\U00010143", 'o', 4); - test("hello\U00010143\u0100\U00010143", '\U00010143', 5); - test("hello\U00010143\u0100\U00010143", '\u0100', 7); - test("hello\U00010143\u0100\U00010143", '\U00010143', 8); - - foreach (S; AliasSeq!(wchar[], const wchar[], wstring)) - { - enum str = to!S("hello world"); - static assert(isSafe!(() => stride(str, 0))); - static assert(isSafe!(() => stride(str) )); - static assert((functionAttributes!(() => stride(str, 0)) & FunctionAttribute.pure_) != 0); - static assert((functionAttributes!(() => stride(str) ) & FunctionAttribute.pure_) != 0); - } - }); -} - -/// Ditto -uint stride(S)(auto ref S str, size_t index = 0) -if (is(S : const dchar[]) || - (isInputRange!S && is(immutable ElementEncodingType!S == immutable dchar))) -{ - static if (is(typeof(str.length) : ulong)) - assert(index < str.length, "Past the end of the UTF-32 sequence"); - else - assert(!str.empty, "UTF-32 sequence is empty."); - return 1; -} - -/// -@safe unittest -{ - assert("a".stride == 1); - assert("Îť".stride == 2); - assert("aÎť".stride == 1); - assert("aÎť".stride(1) == 2); - assert("𐐡".stride == 4); -} - -@system unittest -{ - import core.exception : AssertError; - import std.conv : to; - import std.exception; - import std.string : format; - import std.traits : FunctionAttribute, functionAttributes, isSafe; - static void test(dstring s, dchar c, size_t i = 0, size_t line = __LINE__) - { - enforce(stride(s, i) == codeLength!dchar(c), - new AssertError(format("Unit test failure string: %s", s), __FILE__, line)); - - enforce(stride(RandomCU!dchar(s), i) == codeLength!dchar(c), - new AssertError(format("Unit test failure range: %s", s), __FILE__, line)); - - auto refRandom = new RefRandomCU!dchar(s); - immutable randLen = refRandom.length; - enforce(stride(refRandom, i) == codeLength!dchar(c), - new AssertError(format("Unit test failure rand ref range: %s", s), __FILE__, line)); - enforce(refRandom.length == randLen, - new AssertError(format("Unit test failure rand ref range length: %s", s), __FILE__, line)); - - if (i == 0) - { - enforce(stride(s) == codeLength!dchar(c), - new AssertError(format("Unit test failure string 0: %s", s), __FILE__, line)); - - enforce(stride(InputCU!dchar(s)) == codeLength!dchar(c), - new AssertError(format("Unit test failure range 0: %s", s), __FILE__, line)); - - auto refBidir = new RefBidirCU!dchar(s); - immutable bidirLen = refBidir.length; - enforce(stride(refBidir) == codeLength!dchar(c), - new AssertError(format("Unit test failure bidir ref range code length: %s", s), __FILE__, line)); - enforce(refBidir.length == bidirLen, - new AssertError(format("Unit test failure bidir ref range length: %s", s), __FILE__, line)); - } - } - - assertCTFEable!( - { - test("a", 'a'); - test(" ", ' '); - test("\u2029", '\u2029'); //paraSep - test("\u0100", '\u0100'); - test("\u0430", '\u0430'); - test("\U00010143", '\U00010143'); - test("abcdefcdef", 'a'); - test("hello\U00010143\u0100\U00010143", 'h', 0); - test("hello\U00010143\u0100\U00010143", 'e', 1); - test("hello\U00010143\u0100\U00010143", 'l', 2); - test("hello\U00010143\u0100\U00010143", 'l', 3); - test("hello\U00010143\u0100\U00010143", 'o', 4); - test("hello\U00010143\u0100\U00010143", '\U00010143', 5); - test("hello\U00010143\u0100\U00010143", '\u0100', 6); - test("hello\U00010143\u0100\U00010143", '\U00010143', 7); - - foreach (S; AliasSeq!(dchar[], const dchar[], dstring)) - { - enum str = to!S("hello world"); - static assert(isSafe!(() => stride(str, 0))); - static assert(isSafe!(() => stride(str) )); - static assert((functionAttributes!(() => stride(str, 0)) & FunctionAttribute.pure_) != 0); - static assert((functionAttributes!(() => stride(str) ) & FunctionAttribute.pure_) != 0); - } - }); -} - -private uint strideImpl(char c, size_t index) @trusted pure -in { assert(c & 0x80); } -do -{ - import core.bitop : bsr; - immutable msbs = 7 - bsr((~uint(c)) & 0xFF); - if (c == 0xFF || msbs < 2 || msbs > 4) - throw new UTFException("Invalid UTF-8 sequence", index); - return msbs; -} - -/++ - Calculate the length of the UTF sequence ending one code unit before - `index` in `str`. - - Params: - str = bidirectional range of UTF code units. Must be random access if - `index` is passed - index = index one past end of UTF sequence (default: `str.length`) - - Returns: - The number of code units in the UTF sequence. For UTF-8, this is a - value between 1 and 4 (as per $(HTTP tools.ietf.org/html/rfc3629#section-3, RFC 3629$(COMMA) section 3)). - For UTF-16, it is either 1 or 2. For UTF-32, it is always 1. - - Throws: - May throw a `UTFException` if `str[index]` is not one past the - end of a valid UTF sequence. - - Note: - `strideBack` will only analyze the element at $(D str[index - 1]) - element. It will not fully verify the validity of the UTF sequence, nor - even verify the presence of the sequence: it will not actually - guarantee that $(D strideBack(str, index) <= index). - +/ -uint strideBack(S)(auto ref S str, size_t index) -if (is(S : const char[]) || - (isRandomAccessRange!S && is(immutable ElementType!S == immutable char))) -{ - static if (is(typeof(str.length) : ulong)) - assert(index <= str.length, "Past the end of the UTF-8 sequence"); - assert(index > 0, "Not the end of the UTF-8 sequence"); - - if ((str[index-1] & 0b1100_0000) != 0b1000_0000) - return 1; - - if (index >= 4) //single verification for most common case - { - static foreach (i; 2 .. 5) - { - if ((str[index-i] & 0b1100_0000) != 0b1000_0000) - return i; - } - } - else - { - static foreach (i; 2 .. 4) - { - if (index >= i && (str[index-i] & 0b1100_0000) != 0b1000_0000) - return i; - } - } - throw new UTFException("Not the end of the UTF sequence", index); -} - -/// Ditto -uint strideBack(S)(auto ref S str) -if (is(S : const char[]) || - (isRandomAccessRange!S && hasLength!S && is(immutable ElementType!S == immutable char))) -{ - return strideBack(str, str.length); -} - -/// Ditto -uint strideBack(S)(auto ref S str) -if (isBidirectionalRange!S && is(immutable ElementType!S == immutable char) && !isRandomAccessRange!S) -{ - assert(!str.empty, "Past the end of the UTF-8 sequence"); - auto temp = str.save; - foreach (i; AliasSeq!(1, 2, 3, 4)) - { - if ((temp.back & 0b1100_0000) != 0b1000_0000) - return i; - temp.popBack(); - if (temp.empty) - break; - } - throw new UTFException("The last code unit is not the end of the UTF-8 sequence"); -} - -@system unittest -{ - import core.exception : AssertError; - import std.conv : to; - import std.exception; - import std.string : format; - import std.traits : FunctionAttribute, functionAttributes, isSafe; - static void test(string s, dchar c, size_t i = size_t.max, size_t line = __LINE__) - { - enforce(strideBack(s, i == size_t.max ? s.length : i) == codeLength!char(c), - new AssertError(format("Unit test failure string: %s", s), __FILE__, line)); - - enforce(strideBack(RandomCU!char(s), i == size_t.max ? s.length : i) == codeLength!char(c), - new AssertError(format("Unit test failure range: %s", s), __FILE__, line)); - - auto refRandom = new RefRandomCU!char(s); - immutable randLen = refRandom.length; - enforce(strideBack(refRandom, i == size_t.max ? s.length : i) == codeLength!char(c), - new AssertError(format("Unit test failure rand ref range: %s", s), __FILE__, line)); - enforce(refRandom.length == randLen, - new AssertError(format("Unit test failure rand ref range length: %s", s), __FILE__, line)); - - if (i == size_t.max) - { - enforce(strideBack(s) == codeLength!char(c), - new AssertError(format("Unit test failure string code length: %s", s), __FILE__, line)); - - enforce(strideBack(BidirCU!char(s)) == codeLength!char(c), - new AssertError(format("Unit test failure range code length: %s", s), __FILE__, line)); - - auto refBidir = new RefBidirCU!char(s); - immutable bidirLen = refBidir.length; - enforce(strideBack(refBidir) == codeLength!char(c), - new AssertError(format("Unit test failure bidir ref range code length: %s", s), __FILE__, line)); - enforce(refBidir.length == bidirLen, - new AssertError(format("Unit test failure bidir ref range length: %s", s), __FILE__, line)); - } - } - - assertCTFEable!( - { - test("a", 'a'); - test(" ", ' '); - test("\u2029", '\u2029'); //paraSep - test("\u0100", '\u0100'); - test("\u0430", '\u0430'); - test("\U00010143", '\U00010143'); - test("abcdefcdef", 'f'); - test("\U00010143\u0100\U00010143hello", 'o', 15); - test("\U00010143\u0100\U00010143hello", 'l', 14); - test("\U00010143\u0100\U00010143hello", 'l', 13); - test("\U00010143\u0100\U00010143hello", 'e', 12); - test("\U00010143\u0100\U00010143hello", 'h', 11); - test("\U00010143\u0100\U00010143hello", '\U00010143', 10); - test("\U00010143\u0100\U00010143hello", '\u0100', 6); - test("\U00010143\u0100\U00010143hello", '\U00010143', 4); - - foreach (S; AliasSeq!(char[], const char[], string)) - { - enum str = to!S("hello world"); - static assert(isSafe!({ strideBack(str, 0); })); - static assert(isSafe!({ strideBack(str); })); - static assert((functionAttributes!({ strideBack(str, 0); }) & FunctionAttribute.pure_) != 0); - static assert((functionAttributes!({ strideBack(str); }) & FunctionAttribute.pure_) != 0); - } - }); -} - -//UTF-16 is self synchronizing: The length of strideBack can be found from -//the value of a single wchar -/// Ditto -uint strideBack(S)(auto ref S str, size_t index) -if (is(S : const wchar[]) || - (isRandomAccessRange!S && is(immutable ElementType!S == immutable wchar))) -{ - static if (is(typeof(str.length) : ulong)) - assert(index <= str.length, "Past the end of the UTF-16 sequence"); - assert(index > 0, "Not the end of a UTF-16 sequence"); - - immutable c2 = str[index-1]; - return 1 + (0xDC00 <= c2 && c2 < 0xE000); -} - -/// Ditto -uint strideBack(S)(auto ref S str) -if (is(S : const wchar[]) || - (isBidirectionalRange!S && is(immutable ElementType!S == immutable wchar))) -{ - assert(!str.empty, "UTF-16 sequence is empty"); - - static if (is(S : const(wchar)[])) - immutable c2 = str[$ - 1]; - else - immutable c2 = str.back; - - return 1 + (0xDC00 <= c2 && c2 <= 0xE000); -} - -@system unittest -{ - import core.exception : AssertError; - import std.conv : to; - import std.exception; - import std.string : format; - import std.traits : FunctionAttribute, functionAttributes, isSafe; - static void test(wstring s, dchar c, size_t i = size_t.max, size_t line = __LINE__) - { - enforce(strideBack(s, i == size_t.max ? s.length : i) == codeLength!wchar(c), - new AssertError(format("Unit test failure string: %s", s), __FILE__, line)); - - enforce(strideBack(RandomCU!wchar(s), i == size_t.max ? s.length : i) == codeLength!wchar(c), - new AssertError(format("Unit test failure range: %s", s), __FILE__, line)); - - auto refRandom = new RefRandomCU!wchar(s); - immutable randLen = refRandom.length; - enforce(strideBack(refRandom, i == size_t.max ? s.length : i) == codeLength!wchar(c), - new AssertError(format("Unit test failure rand ref range: %s", s), __FILE__, line)); - enforce(refRandom.length == randLen, - new AssertError(format("Unit test failure rand ref range length: %s", s), __FILE__, line)); - - if (i == size_t.max) - { - enforce(strideBack(s) == codeLength!wchar(c), - new AssertError(format("Unit test failure string code length: %s", s), __FILE__, line)); - - enforce(strideBack(BidirCU!wchar(s)) == codeLength!wchar(c), - new AssertError(format("Unit test failure range code length: %s", s), __FILE__, line)); - - auto refBidir = new RefBidirCU!wchar(s); - immutable bidirLen = refBidir.length; - enforce(strideBack(refBidir) == codeLength!wchar(c), - new AssertError(format("Unit test failure bidir ref range code length: %s", s), __FILE__, line)); - enforce(refBidir.length == bidirLen, - new AssertError(format("Unit test failure bidir ref range length: %s", s), __FILE__, line)); - } - } - - assertCTFEable!( - { - test("a", 'a'); - test(" ", ' '); - test("\u2029", '\u2029'); //paraSep - test("\u0100", '\u0100'); - test("\u0430", '\u0430'); - test("\U00010143", '\U00010143'); - test("abcdefcdef", 'f'); - test("\U00010143\u0100\U00010143hello", 'o', 10); - test("\U00010143\u0100\U00010143hello", 'l', 9); - test("\U00010143\u0100\U00010143hello", 'l', 8); - test("\U00010143\u0100\U00010143hello", 'e', 7); - test("\U00010143\u0100\U00010143hello", 'h', 6); - test("\U00010143\u0100\U00010143hello", '\U00010143', 5); - test("\U00010143\u0100\U00010143hello", '\u0100', 3); - test("\U00010143\u0100\U00010143hello", '\U00010143', 2); - - foreach (S; AliasSeq!(wchar[], const wchar[], wstring)) - { - enum str = to!S("hello world"); - static assert(isSafe!(() => strideBack(str, 0))); - static assert(isSafe!(() => strideBack(str) )); - static assert((functionAttributes!(() => strideBack(str, 0)) & FunctionAttribute.pure_) != 0); - static assert((functionAttributes!(() => strideBack(str) ) & FunctionAttribute.pure_) != 0); - } - }); -} - -/// Ditto -uint strideBack(S)(auto ref S str, size_t index) -if (isRandomAccessRange!S && is(immutable ElementEncodingType!S == immutable dchar)) -{ - static if (is(typeof(str.length) : ulong)) - assert(index <= str.length, "Past the end of the UTF-32 sequence"); - assert(index > 0, "Not the end of the UTF-32 sequence"); - return 1; -} - -/// Ditto -uint strideBack(S)(auto ref S str) -if (isBidirectionalRange!S && is(immutable ElementEncodingType!S == immutable dchar)) -{ - assert(!str.empty, "Empty UTF-32 sequence"); - return 1; -} - -/// -@safe unittest -{ - assert("a".strideBack == 1); - assert("Îť".strideBack == 2); - assert("aÎť".strideBack == 2); - assert("aÎť".strideBack(1) == 1); - assert("𐐡".strideBack == 4); -} - -@system unittest -{ - import core.exception : AssertError; - import std.conv : to; - import std.exception; - import std.string : format; - import std.traits : FunctionAttribute, functionAttributes, isSafe; - static void test(dstring s, dchar c, size_t i = size_t.max, size_t line = __LINE__) - { - enforce(strideBack(s, i == size_t.max ? s.length : i) == codeLength!dchar(c), - new AssertError(format("Unit test failure string: %s", s), __FILE__, line)); - - enforce(strideBack(RandomCU!dchar(s), i == size_t.max ? s.length : i) == codeLength!dchar(c), - new AssertError(format("Unit test failure range: %s", s), __FILE__, line)); - - auto refRandom = new RefRandomCU!dchar(s); - immutable randLen = refRandom.length; - enforce(strideBack(refRandom, i == size_t.max ? s.length : i) == codeLength!dchar(c), - new AssertError(format("Unit test failure rand ref range: %s", s), __FILE__, line)); - enforce(refRandom.length == randLen, - new AssertError(format("Unit test failure rand ref range length: %s", s), __FILE__, line)); - - if (i == size_t.max) - { - enforce(strideBack(s) == codeLength!dchar(c), - new AssertError(format("Unit test failure string code length: %s", s), __FILE__, line)); - - enforce(strideBack(BidirCU!dchar(s)) == codeLength!dchar(c), - new AssertError(format("Unit test failure range code length: %s", s), __FILE__, line)); - - auto refBidir = new RefBidirCU!dchar(s); - immutable bidirLen = refBidir.length; - enforce(strideBack(refBidir) == codeLength!dchar(c), - new AssertError(format("Unit test failure bidir ref range code length: %s", s), __FILE__, line)); - enforce(refBidir.length == bidirLen, - new AssertError(format("Unit test failure bidir ref range length: %s", s), __FILE__, line)); - } - } - - assertCTFEable!( - { - test("a", 'a'); - test(" ", ' '); - test("\u2029", '\u2029'); //paraSep - test("\u0100", '\u0100'); - test("\u0430", '\u0430'); - test("\U00010143", '\U00010143'); - test("abcdefcdef", 'f'); - test("\U00010143\u0100\U00010143hello", 'o', 8); - test("\U00010143\u0100\U00010143hello", 'l', 7); - test("\U00010143\u0100\U00010143hello", 'l', 6); - test("\U00010143\u0100\U00010143hello", 'e', 5); - test("\U00010143\u0100\U00010143hello", 'h', 4); - test("\U00010143\u0100\U00010143hello", '\U00010143', 3); - test("\U00010143\u0100\U00010143hello", '\u0100', 2); - test("\U00010143\u0100\U00010143hello", '\U00010143', 1); - - foreach (S; AliasSeq!(dchar[], const dchar[], dstring)) - { - enum str = to!S("hello world"); - static assert(isSafe!(() => strideBack(str, 0))); - static assert(isSafe!(() => strideBack(str) )); - static assert((functionAttributes!(() => strideBack(str, 0)) & FunctionAttribute.pure_) != 0); - static assert((functionAttributes!(() => strideBack(str) ) & FunctionAttribute.pure_) != 0); - } - }); -} - - -/++ - Given `index` into `str` and assuming that `index` is at the start - of a UTF sequence, `toUCSindex` determines the number of UCS characters - up to `index`. So, `index` is the index of a code unit at the - beginning of a code point, and the return value is how many code points into - the string that that code point is. - +/ -size_t toUCSindex(C)(const(C)[] str, size_t index) @safe pure -if (isSomeChar!C) -{ - static if (is(immutable C == immutable dchar)) - return index; - else - { - size_t n = 0; - size_t j = 0; - - for (; j < index; ++n) - j += stride(str, j); - - if (j > index) - { - static if (is(immutable C == immutable char)) - throw new UTFException("Invalid UTF-8 sequence", index); - else - throw new UTFException("Invalid UTF-16 sequence", index); - } - - return n; - } -} - -/// -@safe unittest -{ - assert(toUCSindex(`hello world`, 7) == 7); - assert(toUCSindex(`hello world`w, 7) == 7); - assert(toUCSindex(`hello world`d, 7) == 7); - - assert(toUCSindex(`Ma ChĂŠrie`, 7) == 6); - assert(toUCSindex(`Ma ChĂŠrie`w, 7) == 7); - assert(toUCSindex(`Ma ChĂŠrie`d, 7) == 7); - - assert(toUCSindex(`さいごの果実 / ミツバチと科学者`, 9) == 3); - assert(toUCSindex(`さいごの果実 / ミツバチと科学者`w, 9) == 9); - assert(toUCSindex(`さいごの果実 / ミツバチと科学者`d, 9) == 9); -} - - -/++ - Given a UCS index `n` into `str`, returns the UTF index. - So, `n` is how many code points into the string the code point is, and - the array index of the code unit is returned. - +/ -size_t toUTFindex(C)(const(C)[] str, size_t n) @safe pure -if (isSomeChar!C) -{ - static if (is(immutable C == immutable dchar)) - { - return n; - } - else - { - size_t i; - while (n--) - { - i += stride(str, i); - } - return i; - } -} - -/// -@safe unittest -{ - assert(toUTFindex(`hello world`, 7) == 7); - assert(toUTFindex(`hello world`w, 7) == 7); - assert(toUTFindex(`hello world`d, 7) == 7); - - assert(toUTFindex(`Ma ChĂŠrie`, 6) == 7); - assert(toUTFindex(`Ma ChĂŠrie`w, 7) == 7); - assert(toUTFindex(`Ma ChĂŠrie`d, 7) == 7); - - assert(toUTFindex(`さいごの果実 / ミツバチと科学者`, 3) == 9); - assert(toUTFindex(`さいごの果実 / ミツバチと科学者`w, 9) == 9); - assert(toUTFindex(`さいごの果実 / ミツバチと科学者`d, 9) == 9); -} - - -/* =================== Decode ======================= */ - -/// Whether or not to replace invalid UTF with $(LREF replacementDchar) -alias UseReplacementDchar = Flag!"useReplacementDchar"; - -/++ - Decodes and returns the code point starting at `str[index]`. `index` - is advanced to one past the decoded code point. If the code point is not - well-formed, then a `UTFException` is thrown and `index` remains - unchanged. - - decode will only work with strings and random access ranges of code units - with length and slicing, whereas $(LREF decodeFront) will work with any - input range of code units. - - Params: - useReplacementDchar = if invalid UTF, return replacementDchar rather than throwing - str = input string or indexable Range - index = starting index into s[]; incremented by number of code units processed - - Returns: - decoded character - - Throws: - $(LREF UTFException) if `str[index]` is not the start of a valid UTF - sequence and useReplacementDchar is `No.useReplacementDchar` - +/ -dchar decode(UseReplacementDchar useReplacementDchar = No.useReplacementDchar, S)(auto ref S str, ref size_t index) -if (!isSomeString!S && - isRandomAccessRange!S && hasSlicing!S && hasLength!S && isSomeChar!(ElementType!S)) -in -{ - assert(index < str.length, "Attempted to decode past the end of a string"); -} -out (result) -{ - assert(isValidDchar(result)); -} -do -{ - if (str[index] < codeUnitLimit!S) - return str[index++]; - else - return decodeImpl!(true, useReplacementDchar)(str, index); -} - -/// ditto -dchar decode(UseReplacementDchar useReplacementDchar = No.useReplacementDchar, S)( -auto ref scope S str, ref size_t index) @trusted pure -if (isSomeString!S) -in -{ - assert(index < str.length, "Attempted to decode past the end of a string"); -} -out (result) -{ - assert(isValidDchar(result)); -} -do -{ - if (str[index] < codeUnitLimit!S) - return str[index++]; - else static if (is(immutable S == immutable C[], C)) - return decodeImpl!(true, useReplacementDchar)(cast(const(C)[]) str, index); -} - -/// -@safe pure unittest -{ - size_t i; - - assert("a".decode(i) == 'a' && i == 1); - i = 0; - assert("ĂĽ".decode(i) == 'ĂĽ' && i == 2); - i = 1; - assert("aĂĽ".decode(i) == 'ĂĽ' && i == 3); - i = 0; - assert("ĂĽ"w.decode(i) == 'ĂĽ' && i == 1); - - // ĂŤ as a multi-code point grapheme - i = 0; - assert("e\u0308".decode(i) == 'e' && i == 1); - // ĂŤ as a single code point grapheme - i = 0; - assert("ĂŤ".decode(i) == 'ĂŤ' && i == 2); - i = 0; - assert("ĂŤ"w.decode(i) == 'ĂŤ' && i == 1); -} - -@safe pure unittest // https://issues.dlang.org/show_bug.cgi?id=22867 -{ - import std.conv : hexString; - string data = hexString!"f787a598"; - size_t offset = 0; - try data.decode(offset); - catch (UTFException ex) assert(offset == 0); -} - -/++ - `decodeFront` is a variant of $(LREF decode) which specifically decodes - the first code point. Unlike $(LREF decode), `decodeFront` accepts any - $(REF_ALTTEXT input range, isInputRange, std,range,primitives) - of code units (rather than just a string or random access - range). It also takes the range by `ref` and pops off the elements as it - decodes them. If `numCodeUnits` is passed in, it gets set to the number - of code units which were in the code point which was decoded. - - Params: - useReplacementDchar = if invalid UTF, return replacementDchar rather than throwing - str = input string or indexable Range - numCodeUnits = set to number of code units processed - - Returns: - decoded character - - Throws: - $(LREF UTFException) if `str.front` is not the start of a valid UTF - sequence. If an exception is thrown, then there is no guarantee as to - the number of code units which were popped off, as it depends on the - type of range being used and how many code units had to be popped off - before the code point was determined to be invalid. - +/ -dchar decodeFront(UseReplacementDchar useReplacementDchar = No.useReplacementDchar, S)( -ref S str, out size_t numCodeUnits) -if (!isSomeString!S && isInputRange!S && isSomeChar!(ElementType!S)) -in -{ - assert(!str.empty); -} -out (result) -{ - assert(isValidDchar(result)); -} -do -{ - immutable fst = str.front; - - if (fst < codeUnitLimit!S) - { - str.popFront(); - numCodeUnits = 1; - return fst; - } - else - { - // https://issues.dlang.org/show_bug.cgi?id=14447 forces canIndex to be - // done outside of decodeImpl, which is undesirable, since not all - // overloads of decodeImpl need it. So, it should be moved back into - // decodeImpl once https://issues.dlang.org/show_bug.cgi?id=8521 - // has been fixed. - enum canIndex = is(S : const char[]) || isRandomAccessRange!S && hasSlicing!S && hasLength!S; - immutable retval = decodeImpl!(canIndex, useReplacementDchar)(str, numCodeUnits); - - // The other range types were already popped by decodeImpl. - static if (isRandomAccessRange!S && hasSlicing!S && hasLength!S) - str = str[numCodeUnits .. str.length]; - - return retval; - } -} - -/// ditto -dchar decodeFront(UseReplacementDchar useReplacementDchar = No.useReplacementDchar, S)( -ref scope S str, out size_t numCodeUnits) @trusted pure -if (isSomeString!S) -in -{ - assert(!str.empty); -} -out (result) -{ - assert(isValidDchar(result)); -} -do -{ - if (str[0] < codeUnitLimit!S) - { - numCodeUnits = 1; - immutable retval = str[0]; - str = str[1 .. $]; - return retval; - } - else static if (is(immutable S == immutable C[], C)) - { - immutable retval = decodeImpl!(true, useReplacementDchar)(cast(const(C)[]) str, numCodeUnits); - str = str[numCodeUnits .. $]; - return retval; - } -} - -/++ Ditto +/ -dchar decodeFront(UseReplacementDchar useReplacementDchar = No.useReplacementDchar, S)(ref S str) -if (isInputRange!S && isSomeChar!(ElementType!S)) -{ - size_t numCodeUnits; - return decodeFront!useReplacementDchar(str, numCodeUnits); -} - -/// -@safe pure unittest -{ - import std.range.primitives; - string str = "Hello, World!"; - - assert(str.decodeFront == 'H' && str == "ello, World!"); - str = "ĂĽ"; - assert(str.decodeFront == 'ĂĽ' && str.empty); - str = "ĂĽ"; - size_t i; - assert(str.decodeFront(i) == 'ĂĽ' && i == 2 && str.empty); -} - -/++ - `decodeBack` is a variant of $(LREF decode) which specifically decodes - the last code point. Unlike $(LREF decode), `decodeBack` accepts any - bidirectional range of code units (rather than just a string or random access - range). It also takes the range by `ref` and pops off the elements as it - decodes them. If `numCodeUnits` is passed in, it gets set to the number - of code units which were in the code point which was decoded. - - Params: - useReplacementDchar = if invalid UTF, return `replacementDchar` rather than throwing - str = input string or bidirectional Range - numCodeUnits = gives the number of code units processed - - Returns: - A decoded UTF character. - - Throws: - $(LREF UTFException) if `str.back` is not the end of a valid UTF - sequence. If an exception is thrown, the `str` itself remains unchanged, - but there is no guarantee as to the value of `numCodeUnits` (when passed). - +/ -dchar decodeBack(UseReplacementDchar useReplacementDchar = No.useReplacementDchar, S)( - ref S str, out size_t numCodeUnits) -if (isSomeString!S) -in -{ - assert(!str.empty); -} -out (result) -{ - assert(isValidDchar(result)); -} -do -{ - if (str[$ - 1] < codeUnitLimit!S) - { - numCodeUnits = 1; - immutable retval = str[$ - 1]; - str = str[0 .. $ - 1]; - return retval; - } - else static if (is(immutable S == immutable C[], C)) - { - numCodeUnits = strideBack(str); - immutable newLength = str.length - numCodeUnits; - size_t index = newLength; - immutable retval = decodeImpl!(true, useReplacementDchar)(cast(const(C)[]) str, index); - str = str[0 .. newLength]; - return retval; - } -} - -/++ Ditto +/ -dchar decodeBack(UseReplacementDchar useReplacementDchar = No.useReplacementDchar, S)( - ref S str, out size_t numCodeUnits) -if (!isSomeString!S && isSomeChar!(ElementType!S) && isBidirectionalRange!S - && ((isRandomAccessRange!S && hasLength!S) || !isRandomAccessRange!S)) -in -{ - assert(!str.empty); -} -out (result) -{ - assert(isValidDchar(result)); -} -do -{ - if (str.back < codeUnitLimit!S) - { - numCodeUnits = 1; - immutable retval = str.back; - str.popBack(); - return retval; - } - else - { - numCodeUnits = strideBack(str); - static if (isRandomAccessRange!S) - { - size_t index = str.length - numCodeUnits; - immutable retval = decodeImpl!(true, useReplacementDchar)(str, index); - str.popBackExactly(numCodeUnits); - return retval; - } - else - { - alias Char = typeof(cast() ElementType!S.init); - Char[4] codeUnits = void; - S tmp = str.save; - for (size_t i = numCodeUnits; i > 0; ) - { - codeUnits[--i] = tmp.back; - tmp.popBack(); - } - const Char[] codePoint = codeUnits[0 .. numCodeUnits]; - size_t index = 0; - immutable retval = decodeImpl!(true, useReplacementDchar)(codePoint, index); - str = tmp; - return retval; - } - } -} - -/++ Ditto +/ -dchar decodeBack(UseReplacementDchar useReplacementDchar = No.useReplacementDchar, S)(ref S str) -if (isSomeString!S - || (isRandomAccessRange!S && hasLength!S && isSomeChar!(ElementType!S)) - || (!isRandomAccessRange!S && isBidirectionalRange!S && isSomeChar!(ElementType!S))) -in -{ - assert(!str.empty); -} -out (result) -{ - assert(isValidDchar(result)); -} -do -{ - size_t numCodeUnits; - return decodeBack!useReplacementDchar(str, numCodeUnits); -} - -/// -@system pure unittest -{ - import std.range.primitives; - string str = "Hello, World!"; - - assert(str.decodeBack == '!' && str == "Hello, World"); - str = "ĂĽ"; - assert(str.decodeBack == 'ĂĽ' && str.empty); - str = "ĂĽ"; - size_t i; - assert(str.decodeBack(i) == 'ĂĽ' && i == 2 && str.empty); -} - -// For the given range, code unit values less than this -// are guaranteed to be valid single-codepoint encodings. -package template codeUnitLimit(S) -if (isSomeChar!(ElementEncodingType!S)) -{ - static if (is(immutable ElementEncodingType!S == immutable char)) - enum char codeUnitLimit = 0x80; - else static if (is(immutable ElementEncodingType!S == immutable wchar)) - enum wchar codeUnitLimit = 0xD800; - else - enum dchar codeUnitLimit = 0xD800; -} - -/* - * For strings, this function does its own bounds checking to give a - * more useful error message when attempting to decode past the end of a string. - * Subsequently it uses a pointer instead of an array to avoid - * redundant bounds checking. - * - * The three overloads of this operate on chars, wchars, and dchars. - * - * Params: - * canIndex = if S is indexable - * useReplacementDchar = if invalid UTF, return replacementDchar rather than throwing - * str = input string or Range - * index = starting index into s[]; incremented by number of code units processed - * - * Returns: - * decoded character - */ -private dchar decodeImpl(bool canIndex, UseReplacementDchar useReplacementDchar = No.useReplacementDchar, S)( - auto ref S str, ref size_t index) -if ( - is(S : const char[]) || (isInputRange!S && is(immutable ElementEncodingType!S == immutable char))) -{ - /* The following encodings are valid, except for the 5 and 6 byte - * combinations: - * 0xxxxxxx - * 110xxxxx 10xxxxxx - * 1110xxxx 10xxxxxx 10xxxxxx - * 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx - * 111110xx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx - * 1111110x 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx - */ - - /* Dchar bitmask for different numbers of UTF-8 code units. - */ - alias bitMask = AliasSeq!((1 << 7) - 1, (1 << 11) - 1, (1 << 16) - 1, (1 << 21) - 1); - - static if (is(S : const char[])) - auto pstr = str.ptr + index; // this is what makes decodeImpl() @system code - else static if (isRandomAccessRange!S && hasSlicing!S && hasLength!S) - auto pstr = str[index .. str.length]; - else - alias pstr = str; - - // https://issues.dlang.org/show_bug.cgi?id=14447 forces this to be done - // outside of decodeImpl - //enum canIndex = is(S : const char[]) || (isRandomAccessRange!S && hasSlicing!S && hasLength!S); - - static if (canIndex) - { - immutable length = str.length - index; - ubyte fst = pstr[0]; - } - else - { - ubyte fst = pstr.front; - pstr.popFront(); - } - - static if (!useReplacementDchar) - { - static if (canIndex) - { - static UTFException exception(S)(S str, string msg) - { - uint[4] sequence = void; - size_t i; - - do - { - sequence[i] = str[i]; - } while (++i < str.length && i < 4 && (str[i] & 0xC0) == 0x80); - - return new UTFException(msg, i).setSequence(sequence[0 .. i]); - } - } - - UTFException invalidUTF() - { - static if (canIndex) - return exception(pstr[0 .. length], "Invalid UTF-8 sequence"); - else - { - //We can't include the invalid sequence with input strings without - //saving each of the code units along the way, and we can't do it with - //forward ranges without saving the entire range. Both would incur a - //cost for the decoding of every character just to provide a better - //error message for the (hopefully) rare case when an invalid UTF-8 - //sequence is encountered, so we don't bother trying to include the - //invalid sequence here, unlike with strings and sliceable ranges. - return new UTFException("Invalid UTF-8 sequence"); - } - } - - UTFException outOfBounds() - { - static if (canIndex) - return exception(pstr[0 .. length], "Attempted to decode past the end of a string"); - else - return new UTFException("Attempted to decode past the end of a string"); - } - } - - if ((fst & 0b1100_0000) != 0b1100_0000) - { - static if (useReplacementDchar) - { - ++index; // always consume bad input to avoid infinite loops - return replacementDchar; - } - else - throw invalidUTF(); // starter must have at least 2 first bits set - } - ubyte tmp = void; - dchar d = fst; // upper control bits are masked out later - fst <<= 1; - - foreach (i; AliasSeq!(1, 2, 3)) - { - - static if (canIndex) - { - if (i == length) - { - static if (useReplacementDchar) - { - index += i; - return replacementDchar; - } - else - throw outOfBounds(); - } - } - else - { - if (pstr.empty) - { - static if (useReplacementDchar) - { - index += i; - return replacementDchar; - } - else - throw outOfBounds(); - } - } - - static if (canIndex) - tmp = pstr[i]; - else - { - tmp = pstr.front; - pstr.popFront(); - } - - if ((tmp & 0xC0) != 0x80) - { - static if (useReplacementDchar) - { - index += i + 1; - return replacementDchar; - } - else - throw invalidUTF(); - } - - d = (d << 6) | (tmp & 0x3F); - fst <<= 1; - - if (!(fst & 0x80)) // no more bytes - { - d &= bitMask[i]; // mask out control bits - - // overlong, could have been encoded with i bytes - if ((d & ~bitMask[i - 1]) == 0) - { - static if (useReplacementDchar) - { - index += i + 1; - return replacementDchar; - } - else - throw invalidUTF(); - } - - // check for surrogates only needed for 3 bytes - static if (i == 2) - { - if (!isValidDchar(d)) - { - static if (useReplacementDchar) - { - index += i + 1; - return replacementDchar; - } - else - throw invalidUTF(); - } - } - - static if (i == 3) - { - if (d > dchar.max) - { - static if (useReplacementDchar) - d = replacementDchar; - else - throw invalidUTF(); - } - } - - index += i + 1; - return d; - } - } - - static if (useReplacementDchar) - { - index += 4; // read 4 chars by now - return replacementDchar; - } - else - throw invalidUTF(); -} - -@safe pure @nogc nothrow -unittest -{ - // Add tests for useReplacemendDchar == yes path - - static struct R - { - @safe pure @nogc nothrow: - this(string s) { this.s = s; } - @property bool empty() { return idx == s.length; } - @property char front() { return s[idx]; } - void popFront() { ++idx; } - size_t idx; - string s; - } - - foreach (s; invalidUTFstrings!char()) - { - auto r = R(s); - size_t index; - dchar dc = decodeImpl!(false, Yes.useReplacementDchar)(r, index); - assert(dc == replacementDchar); - assert(1 <= index && index <= s.length); - } -} - -private dchar decodeImpl(bool canIndex, UseReplacementDchar useReplacementDchar = No.useReplacementDchar, S) -(auto ref S str, ref size_t index) -if (is(S : const wchar[]) || (isInputRange!S && is(immutable ElementEncodingType!S == immutable wchar))) -{ - static if (is(S : const wchar[])) - auto pstr = str.ptr + index; - else static if (isRandomAccessRange!S && hasSlicing!S && hasLength!S) - auto pstr = str[index .. str.length]; - else - alias pstr = str; - - // https://issues.dlang.org/show_bug.cgi?id=14447 forces this to be done - // outside of decodeImpl - //enum canIndex = is(S : const wchar[]) || (isRandomAccessRange!S && hasSlicing!S && hasLength!S); - - static if (canIndex) - { - immutable length = str.length - index; - uint u = pstr[0]; - } - else - { - uint u = pstr.front; - pstr.popFront(); - } - - static if (!useReplacementDchar) - { - UTFException exception(string msg) - { - static if (canIndex) - return new UTFException(msg).setSequence(pstr[0]); - else - return new UTFException(msg); - } - } - - // The < case must be taken care of before decodeImpl is called. - assert(u >= 0xD800); - - if (u <= 0xDBFF) - { - static if (canIndex) - immutable onlyOneCodeUnit = length == 1; - else - immutable onlyOneCodeUnit = pstr.empty; - - if (onlyOneCodeUnit) - { - static if (useReplacementDchar) - { - ++index; - return replacementDchar; - } - else - throw exception("surrogate UTF-16 high value past end of string"); - } - - static if (canIndex) - immutable uint u2 = pstr[1]; - else - { - immutable uint u2 = pstr.front; - pstr.popFront(); - } - - if (u2 < 0xDC00 || u2 > 0xDFFF) - { - static if (useReplacementDchar) - u = replacementDchar; - else - throw exception("surrogate UTF-16 low value out of range"); - } - else - u = ((u - 0xD7C0) << 10) + (u2 - 0xDC00); - ++index; - } - else if (u >= 0xDC00 && u <= 0xDFFF) - { - static if (useReplacementDchar) - u = replacementDchar; - else - throw exception("unpaired surrogate UTF-16 value"); - } - ++index; - - // Note: u+FFFE and u+FFFF are specifically permitted by the - // Unicode standard for application internal use (see isValidDchar) - - return cast(dchar) u; -} - -@safe pure @nogc nothrow -unittest -{ - // Add tests for useReplacemendDchar == true path - - static struct R - { - @safe pure @nogc nothrow: - this(wstring s) { this.s = s; } - @property bool empty() { return idx == s.length; } - @property wchar front() { return s[idx]; } - void popFront() { ++idx; } - size_t idx; - wstring s; - } - - foreach (s; invalidUTFstrings!wchar()) - { - auto r = R(s); - size_t index; - dchar dc = decodeImpl!(false, Yes.useReplacementDchar)(r, index); - assert(dc == replacementDchar); - assert(1 <= index && index <= s.length); - } -} - -private dchar decodeImpl(bool canIndex, UseReplacementDchar useReplacementDchar = No.useReplacementDchar, S)( - auto ref S str, ref size_t index) -if (is(S : const dchar[]) || (isInputRange!S && is(immutable ElementEncodingType!S == immutable dchar))) -{ - static if (is(S : const dchar[])) - auto pstr = str.ptr; - else - alias pstr = str; - - static if (is(S : const dchar[]) || isRandomAccessRange!S) - { - dchar dc = pstr[index]; - if (!isValidDchar(dc)) - { - static if (useReplacementDchar) - dc = replacementDchar; - else - throw new UTFException("Invalid UTF-32 value").setSequence(dc); - } - ++index; - return dc; - } - else - { - dchar dc = pstr.front; - if (!isValidDchar(dc)) - { - static if (useReplacementDchar) - dc = replacementDchar; - else - throw new UTFException("Invalid UTF-32 value").setSequence(dc); - } - ++index; - pstr.popFront(); - return dc; - } -} - -@safe pure @nogc nothrow -unittest -{ - // Add tests for useReplacemendDchar == true path - - static struct R - { - @safe pure @nogc nothrow: - this(dstring s) { this.s = s; } - @property bool empty() { return idx == s.length; } - @property dchar front() { return s[idx]; } - void popFront() { ++idx; } - size_t idx; - dstring s; - } - - foreach (s; invalidUTFstrings!dchar()) - { - auto r = R(s); - size_t index; - dchar dc = decodeImpl!(false, Yes.useReplacementDchar)(r, index); - assert(dc == replacementDchar); - assert(1 <= index && index <= s.length); - } -} - - -version (StdUnittest) private void testDecode(R)(R range, - size_t index, - dchar expectedChar, - size_t expectedIndex, - size_t line = __LINE__) -{ - import core.exception : AssertError; - import std.exception : enforce; - import std.string : format; - import std.traits : isNarrowString; - - static if (hasLength!R) - immutable lenBefore = range.length; - - static if (isRandomAccessRange!R && !isNarrowString!R) - { - { - immutable result = decode(range, index); - enforce(result == expectedChar, - new AssertError(format("decode: Wrong character: %s", result), __FILE__, line)); - enforce(index == expectedIndex, - new AssertError(format("decode: Wrong index: %s", index), __FILE__, line)); - static if (hasLength!R) - { - enforce(range.length == lenBefore, - new AssertError(format("decode: length changed: %s", range.length), __FILE__, line)); - } - } - } -} - -version (StdUnittest) private void testDecodeFront(R)(ref R range, - dchar expectedChar, - size_t expectedNumCodeUnits, - size_t line = __LINE__) -{ - import core.exception : AssertError; - import std.exception : enforce; - import std.string : format; - - static if (hasLength!R) - immutable lenBefore = range.length; - - size_t numCodeUnits; - immutable result = decodeFront(range, numCodeUnits); - enforce(result == expectedChar, - new AssertError(format("decodeFront: Wrong character: %s", result), __FILE__, line)); - enforce(numCodeUnits == expectedNumCodeUnits, - new AssertError(format("decodeFront: Wrong numCodeUnits: %s", numCodeUnits), __FILE__, line)); - - static if (hasLength!R) - { - enforce(range.length == lenBefore - numCodeUnits, - new AssertError(format("decodeFront: wrong length: %s", range.length), __FILE__, line)); - } -} - -version (StdUnittest) private void testDecodeBack(R)(ref R range, - dchar expectedChar, - size_t expectedNumCodeUnits, - size_t line = __LINE__) -{ - // This condition is to allow unit testing all `decode` functions together - static if (!isBidirectionalRange!R) - return; - else - { - import core.exception : AssertError; - import std.exception : enforce; - import std.string : format; - - static if (hasLength!R) - immutable lenBefore = range.length; - - size_t numCodeUnits; - immutable result = decodeBack(range, numCodeUnits); - enforce(result == expectedChar, - new AssertError(format("decodeBack: Wrong character: %s", result), __FILE__, line)); - enforce(numCodeUnits == expectedNumCodeUnits, - new AssertError(format("decodeBack: Wrong numCodeUnits: %s", numCodeUnits), __FILE__, line)); - - static if (hasLength!R) - { - enforce(range.length == lenBefore - numCodeUnits, - new AssertError(format("decodeBack: wrong length: %s", range.length), __FILE__, line)); - } - } -} - -version (StdUnittest) private void testAllDecode(R)(R range, - dchar expectedChar, - size_t expectedIndex, - size_t line = __LINE__) -{ - testDecode(range, 0, expectedChar, expectedIndex, line); - static if (isBidirectionalRange!R) - { - auto rangeCopy = range.save; - testDecodeBack(rangeCopy, expectedChar, expectedIndex, line); - } - testDecodeFront(range, expectedChar, expectedIndex, line); -} - -version (StdUnittest) private void testBadDecode(R)(R range, size_t index, size_t line = __LINE__) -{ - import core.exception : AssertError; - import std.exception : assertThrown, enforce; - import std.string : format; - - immutable initialIndex = index; - - static if (hasLength!R) - immutable lenBefore = range.length; - - static if (isRandomAccessRange!R) - { - assertThrown!UTFException(decode(range, index), null, __FILE__, line); - enforce(index == initialIndex, - new AssertError(format("decode: Wrong index: %s", index), __FILE__, line)); - static if (hasLength!R) - { - enforce(range.length == lenBefore, - new AssertError(format("decode: length changed:", range.length), __FILE__, line)); - } - } - - if (initialIndex == 0) - assertThrown!UTFException(decodeFront(range, index), null, __FILE__, line); -} - -version (StdUnittest) private void testBadDecodeBack(R)(R range, size_t line = __LINE__) -{ - // This condition is to allow unit testing all `decode` functions together - static if (!isBidirectionalRange!R) - return; - else - { - import core.exception : AssertError; - import std.exception : assertThrown, enforce; - import std.string : format; - - static if (hasLength!R) - immutable lenBefore = range.length; - - static if (isRandomAccessRange!R) - { - assertThrown!UTFException(decodeBack(range), null, __FILE__, line); - static if (hasLength!R) - { - enforce(range.length == lenBefore, - new AssertError(format("decodeBack: length changed:", range.length), __FILE__, line)); - } - } - } -} - -@system unittest -{ - import std.conv : to; - import std.exception; - - assertCTFEable!( - { - foreach (S; AliasSeq!(to!string, InputCU!char, RandomCU!char, - (string s) => new RefBidirCU!char(s), - (string s) => new RefRandomCU!char(s))) - { - enum sHasLength = hasLength!(typeof(S("abcd"))); - - { - auto range = S("abcd"); - testDecode(range, 0, 'a', 1); - testDecode(range, 1, 'b', 2); - testDecodeFront(range, 'a', 1); - testDecodeFront(range, 'b', 1); - assert(decodeFront(range) == 'c'); - assert(decodeFront(range) == 'd'); - } - - { - auto range = S("ウェブサイト"); - testDecode(range, 0, 'ウ', 3); - testDecode(range, 3, 'ェ', 6); - testDecodeFront(range, 'ウ', 3); - testDecodeFront(range, 'ェ', 3); - assert(decodeFront(range) == 'ブ'); - assert(decodeFront(range) == 'サ'); - } - - { - auto range = S("abcd"); - testDecodeBack(range, 'd', 1); - testDecodeBack(range, 'c', 1); - testDecodeBack(range, 'b', 1); - testDecodeBack(range, 'a', 1); - } - - { - auto range = S("ウェブサイト"); - testDecodeBack(range, 'ト', 3); - testDecodeBack(range, 'イ', 3); - testDecodeBack(range, 'サ', 3); - testDecodeBack(range, 'ブ', 3); - } - - testAllDecode(S("\xC2\xA9"), '\u00A9', 2); - testAllDecode(S("\xE2\x89\xA0"), '\u2260', 3); - - foreach (str; ["\xE2\x89", // too short - "\xC0\x8A", - "\xE0\x80\x8A", - "\xF0\x80\x80\x8A", - "\xF8\x80\x80\x80\x8A", - "\xFC\x80\x80\x80\x80\x8A"]) - { - testBadDecode(S(str), 0); - testBadDecode(S(str), 1); - testBadDecodeBack(S(str)); - } - - //Invalid UTF-8 sequence where the first code unit is valid. - testAllDecode(S("\xEF\xBF\xBE"), cast(dchar) 0xFFFE, 3); - testAllDecode(S("\xEF\xBF\xBF"), cast(dchar) 0xFFFF, 3); - - //Invalid UTF-8 sequence where the first code unit isn't valid. - foreach (str; ["\xED\xA0\x80", - "\xED\xAD\xBF", - "\xED\xAE\x80", - "\xED\xAF\xBF", - "\xED\xB0\x80", - "\xED\xBE\x80", - "\xED\xBF\xBF"]) - { - testBadDecode(S(str), 0); - testBadDecodeBack(S(str)); - } - } - }); -} - -@system unittest -{ - import std.exception; - assertCTFEable!( - { - foreach (S; AliasSeq!((wstring s) => s, InputCU!wchar, RandomCU!wchar, - (wstring s) => new RefBidirCU!wchar(s), - (wstring s) => new RefRandomCU!wchar(s))) - { - testAllDecode(S([cast(wchar) 0x1111]), cast(dchar) 0x1111, 1); - testAllDecode(S([cast(wchar) 0xD800, cast(wchar) 0xDC00]), cast(dchar) 0x10000, 2); - testAllDecode(S([cast(wchar) 0xDBFF, cast(wchar) 0xDFFF]), cast(dchar) 0x10FFFF, 2); - testAllDecode(S([cast(wchar) 0xFFFE]), cast(dchar) 0xFFFE, 1); - testAllDecode(S([cast(wchar) 0xFFFF]), cast(dchar) 0xFFFF, 1); - - testBadDecode(S([ cast(wchar) 0xD801 ]), 0); - testBadDecode(S([ cast(wchar) 0xD800, cast(wchar) 0x1200 ]), 0); - - testBadDecodeBack(S([ cast(wchar) 0xD801 ])); - testBadDecodeBack(S([ cast(wchar) 0x0010, cast(wchar) 0xD800 ])); - - { - auto range = S("ウェブサイト"); - testDecode(range, 0, 'ウ', 1); - testDecode(range, 1, 'ェ', 2); - testDecodeFront(range, 'ウ', 1); - testDecodeFront(range, 'ェ', 1); - assert(decodeFront(range) == 'ブ'); - assert(decodeFront(range) == 'サ'); - } - - { - auto range = S("ウェブサイト"); - testDecodeBack(range, 'ト', 1); - testDecodeBack(range, 'イ', 1); - testDecodeBack(range, 'サ', 1); - testDecodeBack(range, 'ブ', 1); - } - } - - foreach (S; AliasSeq!((wchar[] s) => s.idup, RandomCU!wchar, (wstring s) => new RefRandomCU!wchar(s))) - { - auto str = S([cast(wchar) 0xD800, cast(wchar) 0xDC00, - cast(wchar) 0x1400, - cast(wchar) 0xDAA7, cast(wchar) 0xDDDE]); - testDecode(str, 0, cast(dchar) 0x10000, 2); - testDecode(str, 2, cast(dchar) 0x1400, 3); - testDecode(str, 3, cast(dchar) 0xB9DDE, 5); - testDecodeBack(str, cast(dchar) 0xB9DDE, 2); - testDecodeBack(str, cast(dchar) 0x1400, 1); - testDecodeBack(str, cast(dchar) 0x10000, 2); - } - }); -} - -@system unittest -{ - import std.exception; - assertCTFEable!( - { - foreach (S; AliasSeq!((dstring s) => s, RandomCU!dchar, InputCU!dchar, - (dstring s) => new RefBidirCU!dchar(s), - (dstring s) => new RefRandomCU!dchar(s))) - { - testAllDecode(S([cast(dchar) 0x1111]), cast(dchar) 0x1111, 1); - testAllDecode(S([cast(dchar) 0x10000]), cast(dchar) 0x10000, 1); - testAllDecode(S([cast(dchar) 0x10FFFF]), cast(dchar) 0x10FFFF, 1); - testAllDecode(S([cast(dchar) 0xFFFE]), cast(dchar) 0xFFFE, 1); - testAllDecode(S([cast(dchar) 0xFFFF]), cast(dchar) 0xFFFF, 1); - - testBadDecode(S([cast(dchar) 0xD800]), 0); - testBadDecode(S([cast(dchar) 0xDFFE]), 0); - testBadDecode(S([cast(dchar) 0x110000]), 0); - - testBadDecodeBack(S([cast(dchar) 0xD800])); - testBadDecodeBack(S([cast(dchar) 0xDFFE])); - testBadDecodeBack(S([cast(dchar) 0x110000])); - - { - auto range = S("ウェブサイト"); - testDecode(range, 0, 'ウ', 1); - testDecode(range, 1, 'ェ', 2); - testDecodeFront(range, 'ウ', 1); - testDecodeFront(range, 'ェ', 1); - assert(decodeFront(range) == 'ブ'); - assert(decodeFront(range) == 'サ'); - } - - { - auto range = S("ウェブサイト"); - testDecodeBack(range, 'ト', 1); - testDecodeBack(range, 'イ', 1); - testDecodeBack(range, 'サ', 1); - testDecodeBack(range, 'ブ', 1); - } - } - - foreach (S; AliasSeq!((dchar[] s) => s.idup, RandomCU!dchar, (dstring s) => new RefRandomCU!dchar(s))) - { - auto str = S([cast(dchar) 0x10000, cast(dchar) 0x1400, cast(dchar) 0xB9DDE]); - testDecode(str, 0, 0x10000, 1); - testDecode(str, 1, 0x1400, 2); - testDecode(str, 2, 0xB9DDE, 3); - testDecodeBack(str, cast(dchar) 0xB9DDE, 1); - testDecodeBack(str, cast(dchar) 0x1400, 1); - testDecodeBack(str, cast(dchar) 0x10000, 1); - } - }); -} - -@safe unittest -{ - import std.exception; - import std.traits : FunctionAttribute, functionAttributes, isSafe; - assertCTFEable!( - { - foreach (S; AliasSeq!( char[], const( char)[], string, - wchar[], const(wchar)[], wstring, - dchar[], const(dchar)[], dstring)) - { - static assert(isSafe!({ S str; size_t i = 0; decode(str, i); })); - static assert(isSafe!({ S str; size_t i = 0; decodeFront(str, i); })); - static assert(isSafe!({ S str; decodeFront(str); })); - static assert((functionAttributes!({ S str; size_t i = 0; decode(str, i); }) & FunctionAttribute.pure_) != 0); - static assert((functionAttributes!({ - S str; size_t i = 0; decodeFront(str, i); - }) & FunctionAttribute.pure_) != 0); - static assert((functionAttributes!({ S str; decodeFront(str); }) & FunctionAttribute.pure_) != 0); - static assert((functionAttributes!({ - S str; size_t i = 0; decodeBack(str, i); - }) & FunctionAttribute.pure_) != 0); - static assert((functionAttributes!({ S str; decodeBack(str); }) & FunctionAttribute.pure_) != 0); - } - }); -} - -@safe unittest -{ - import std.exception; - char[4] val; - val[0] = 0b1111_0111; - val[1] = 0b1011_1111; - val[2] = 0b1011_1111; - val[3] = 0b1011_1111; - size_t i = 0; - assertThrown!UTFException((){ dchar ch = decode(val[], i); }()); -} -/* =================== Encode ======================= */ - -private dchar _utfException(UseReplacementDchar useReplacementDchar)(string msg, dchar c) -{ - static if (useReplacementDchar) - return replacementDchar; - else - throw new UTFException(msg).setSequence(c); -} - -/++ - Encodes `c` into the static array, `buf`, and returns the actual - length of the encoded character (a number between `1` and `4` for - `char[4]` buffers and a number between `1` and `2` for - `wchar[2]` buffers). - - Throws: - `UTFException` if `c` is not a valid UTF code point. - +/ -size_t encode(UseReplacementDchar useReplacementDchar = No.useReplacementDchar)( - out char[4] buf, dchar c) @safe pure -{ - if (c <= 0x7F) - { - assert(isValidDchar(c)); - buf[0] = cast(char) c; - return 1; - } - if (c <= 0x7FF) - { - assert(isValidDchar(c)); - buf[0] = cast(char)(0xC0 | (c >> 6)); - buf[1] = cast(char)(0x80 | (c & 0x3F)); - return 2; - } - if (c <= 0xFFFF) - { - if (0xD800 <= c && c <= 0xDFFF) - c = _utfException!useReplacementDchar("Encoding a surrogate code point in UTF-8", c); - - assert(isValidDchar(c)); - L3: - buf[0] = cast(char)(0xE0 | (c >> 12)); - buf[1] = cast(char)(0x80 | ((c >> 6) & 0x3F)); - buf[2] = cast(char)(0x80 | (c & 0x3F)); - return 3; - } - if (c <= 0x10FFFF) - { - assert(isValidDchar(c)); - buf[0] = cast(char)(0xF0 | (c >> 18)); - buf[1] = cast(char)(0x80 | ((c >> 12) & 0x3F)); - buf[2] = cast(char)(0x80 | ((c >> 6) & 0x3F)); - buf[3] = cast(char)(0x80 | (c & 0x3F)); - return 4; - } - - assert(!isValidDchar(c)); - c = _utfException!useReplacementDchar("Encoding an invalid code point in UTF-8", c); - goto L3; -} - -/// -@safe unittest -{ - import std.exception : assertThrown; - import std.typecons : Yes; - - char[4] buf; - - assert(encode(buf, '\u0000') == 1 && buf[0 .. 1] == "\u0000"); - assert(encode(buf, '\u007F') == 1 && buf[0 .. 1] == "\u007F"); - assert(encode(buf, '\u0080') == 2 && buf[0 .. 2] == "\u0080"); - assert(encode(buf, '\uE000') == 3 && buf[0 .. 3] == "\uE000"); - assert(encode(buf, 0xFFFE) == 3 && buf[0 .. 3] == "\xEF\xBF\xBE"); - assertThrown!UTFException(encode(buf, cast(dchar) 0x110000)); - - encode!(Yes.useReplacementDchar)(buf, cast(dchar) 0x110000); - auto slice = buf[]; - assert(slice.decodeFront == replacementDchar); -} - -/// -@safe unittest -{ - import std.exception : assertThrown; - import std.typecons : Yes; - - wchar[2] buf; - - assert(encode(buf, '\u0000') == 1 && buf[0 .. 1] == "\u0000"); - assert(encode(buf, '\uD7FF') == 1 && buf[0 .. 1] == "\uD7FF"); - assert(encode(buf, '\uE000') == 1 && buf[0 .. 1] == "\uE000"); - assert(encode(buf, '\U00010000') == 2 && buf[0 .. 2] == "\U00010000"); - assert(encode(buf, '\U0010FFFF') == 2 && buf[0 .. 2] == "\U0010FFFF"); - assertThrown!UTFException(encode(buf, cast(dchar) 0xD800)); - - encode!(Yes.useReplacementDchar)(buf, cast(dchar) 0x110000); - auto slice = buf[]; - assert(slice.decodeFront == replacementDchar); -} - -/// -@safe unittest -{ - import std.exception : assertThrown; - import std.typecons : Yes; - - dchar[1] buf; - - assert(encode(buf, '\u0000') == 1 && buf[0] == '\u0000'); - assert(encode(buf, '\uD7FF') == 1 && buf[0] == '\uD7FF'); - assert(encode(buf, '\uE000') == 1 && buf[0] == '\uE000'); - assert(encode(buf, '\U0010FFFF') == 1 && buf[0] == '\U0010FFFF'); - assertThrown!UTFException(encode(buf, cast(dchar) 0xD800)); - - encode!(Yes.useReplacementDchar)(buf, cast(dchar) 0x110000); - assert(buf[0] == replacementDchar); -} - -@safe unittest -{ - import std.exception; - assertCTFEable!( - { - char[4] buf; - - assert(encode(buf, '\u0000') == 1 && buf[0 .. 1] == "\u0000"); - assert(encode(buf, '\u007F') == 1 && buf[0 .. 1] == "\u007F"); - assert(encode(buf, '\u0080') == 2 && buf[0 .. 2] == "\u0080"); - assert(encode(buf, '\u07FF') == 2 && buf[0 .. 2] == "\u07FF"); - assert(encode(buf, '\u0800') == 3 && buf[0 .. 3] == "\u0800"); - assert(encode(buf, '\uD7FF') == 3 && buf[0 .. 3] == "\uD7FF"); - assert(encode(buf, '\uE000') == 3 && buf[0 .. 3] == "\uE000"); - assert(encode(buf, 0xFFFE) == 3 && buf[0 .. 3] == "\xEF\xBF\xBE"); - assert(encode(buf, 0xFFFF) == 3 && buf[0 .. 3] == "\xEF\xBF\xBF"); - assert(encode(buf, '\U00010000') == 4 && buf[0 .. 4] == "\U00010000"); - assert(encode(buf, '\U0010FFFF') == 4 && buf[0 .. 4] == "\U0010FFFF"); - - assertThrown!UTFException(encode(buf, cast(dchar) 0xD800)); - assertThrown!UTFException(encode(buf, cast(dchar) 0xDBFF)); - assertThrown!UTFException(encode(buf, cast(dchar) 0xDC00)); - assertThrown!UTFException(encode(buf, cast(dchar) 0xDFFF)); - assertThrown!UTFException(encode(buf, cast(dchar) 0x110000)); - - assert(encode!(Yes.useReplacementDchar)(buf, cast(dchar) 0x110000) == buf.stride); - enum replacementDcharString = "\uFFFD"; - assert(buf[0 .. replacementDcharString.length] == replacementDcharString); - }); -} - - -/// Ditto -size_t encode(UseReplacementDchar useReplacementDchar = No.useReplacementDchar)( - out wchar[2] buf, dchar c) @safe pure -{ - if (c <= 0xFFFF) - { - if (0xD800 <= c && c <= 0xDFFF) - c = _utfException!useReplacementDchar("Encoding an isolated surrogate code point in UTF-16", c); - - assert(isValidDchar(c)); - L1: - buf[0] = cast(wchar) c; - return 1; - } - if (c <= 0x10FFFF) - { - assert(isValidDchar(c)); - buf[0] = cast(wchar)((((c - 0x10000) >> 10) & 0x3FF) + 0xD800); - buf[1] = cast(wchar)(((c - 0x10000) & 0x3FF) + 0xDC00); - return 2; - } - - c = _utfException!useReplacementDchar("Encoding an invalid code point in UTF-16", c); - goto L1; -} - -@safe unittest -{ - import std.exception; - assertCTFEable!( - { - wchar[2] buf; - - assert(encode(buf, '\u0000') == 1 && buf[0 .. 1] == "\u0000"); - assert(encode(buf, '\uD7FF') == 1 && buf[0 .. 1] == "\uD7FF"); - assert(encode(buf, '\uE000') == 1 && buf[0 .. 1] == "\uE000"); - assert(encode(buf, 0xFFFE) == 1 && buf[0] == 0xFFFE); - assert(encode(buf, 0xFFFF) == 1 && buf[0] == 0xFFFF); - assert(encode(buf, '\U00010000') == 2 && buf[0 .. 2] == "\U00010000"); - assert(encode(buf, '\U0010FFFF') == 2 && buf[0 .. 2] == "\U0010FFFF"); - - assertThrown!UTFException(encode(buf, cast(dchar) 0xD800)); - assertThrown!UTFException(encode(buf, cast(dchar) 0xDBFF)); - assertThrown!UTFException(encode(buf, cast(dchar) 0xDC00)); - assertThrown!UTFException(encode(buf, cast(dchar) 0xDFFF)); - assertThrown!UTFException(encode(buf, cast(dchar) 0x110000)); - - assert(encode!(Yes.useReplacementDchar)(buf, cast(dchar) 0x110000) == buf.stride); - assert(buf.front == replacementDchar); - }); -} - - -/// Ditto -size_t encode(UseReplacementDchar useReplacementDchar = No.useReplacementDchar)( - out dchar[1] buf, dchar c) @safe pure -{ - if ((0xD800 <= c && c <= 0xDFFF) || 0x10FFFF < c) - c = _utfException!useReplacementDchar("Encoding an invalid code point in UTF-32", c); - else - assert(isValidDchar(c)); - buf[0] = c; - return 1; -} - -@safe unittest -{ - import std.exception; - assertCTFEable!( - { - dchar[1] buf; - - encode(buf, '\u0000'); assert(buf[0] == '\u0000'); - encode(buf, '\uD7FF'); assert(buf[0] == '\uD7FF'); - encode(buf, '\uE000'); assert(buf[0] == '\uE000'); - encode(buf, 0xFFFE ); assert(buf[0] == 0xFFFE); - encode(buf, 0xFFFF ); assert(buf[0] == 0xFFFF); - encode(buf, '\U0010FFFF'); assert(buf[0] == '\U0010FFFF'); - - assertThrown!UTFException(encode(buf, cast(dchar) 0xD800)); - assertThrown!UTFException(encode(buf, cast(dchar) 0xDBFF)); - assertThrown!UTFException(encode(buf, cast(dchar) 0xDC00)); - assertThrown!UTFException(encode(buf, cast(dchar) 0xDFFF)); - assertThrown!UTFException(encode(buf, cast(dchar) 0x110000)); - - assert(encode!(Yes.useReplacementDchar)(buf, cast(dchar) 0x110000) == buf.stride); - assert(buf.front == replacementDchar); - }); -} - - -/++ - Encodes `c` in `str`'s encoding and appends it to `str`. - - Throws: - `UTFException` if `c` is not a valid UTF code point. - +/ -void encode(UseReplacementDchar useReplacementDchar = No.useReplacementDchar)( - ref scope char[] str, dchar c) @safe pure -{ - if (c <= 0x7F) - { - assert(isValidDchar(c)); - str ~= cast(char) c; - } - else - { - char[4] buf; - uint L; - - if (c <= 0x7FF) - { - assert(isValidDchar(c)); - buf[0] = cast(char)(0xC0 | (c >> 6)); - buf[1] = cast(char)(0x80 | (c & 0x3F)); - L = 2; - } - else if (c <= 0xFFFF) - { - if (0xD800 <= c && c <= 0xDFFF) - c = _utfException!useReplacementDchar("Encoding a surrogate code point in UTF-8", c); - - assert(isValidDchar(c)); - L3: - buf[0] = cast(char)(0xE0 | (c >> 12)); - buf[1] = cast(char)(0x80 | ((c >> 6) & 0x3F)); - buf[2] = cast(char)(0x80 | (c & 0x3F)); - L = 3; - } - else if (c <= 0x10FFFF) - { - assert(isValidDchar(c)); - buf[0] = cast(char)(0xF0 | (c >> 18)); - buf[1] = cast(char)(0x80 | ((c >> 12) & 0x3F)); - buf[2] = cast(char)(0x80 | ((c >> 6) & 0x3F)); - buf[3] = cast(char)(0x80 | (c & 0x3F)); - L = 4; - } - else - { - assert(!isValidDchar(c)); - c = _utfException!useReplacementDchar("Encoding an invalid code point in UTF-8", c); - goto L3; - } - str ~= buf[0 .. L]; - } -} - -/// -@safe unittest -{ - char[] s = "abcd".dup; - dchar d1 = 'a'; - dchar d2 = 'ø'; - - encode(s, d1); - assert(s.length == 5); - assert(s == "abcda"); - encode(s, d2); - assert(s.length == 7); - assert(s == "abcdaø"); -} - -@safe unittest -{ - import std.exception; - - assertCTFEable!( - { - char[] s = "abcd".dup; - encode(s, cast(dchar)'a'); - assert(s.length == 5); - assert(s == "abcda"); - - encode(s, cast(dchar)'\u00A9'); - assert(s.length == 7); - assert(s == "abcda\xC2\xA9"); - //assert(s == "abcda\u00A9"); // BUG: fix compiler - - encode(s, cast(dchar)'\u2260'); - assert(s.length == 10); - assert(s == "abcda\xC2\xA9\xE2\x89\xA0"); - }); -} - -@safe unittest -{ - import std.exception; - assertCTFEable!( - { - char[] buf; - - encode(buf, '\u0000'); assert(buf[0 .. $] == "\u0000"); - encode(buf, '\u007F'); assert(buf[1 .. $] == "\u007F"); - encode(buf, '\u0080'); assert(buf[2 .. $] == "\u0080"); - encode(buf, '\u07FF'); assert(buf[4 .. $] == "\u07FF"); - encode(buf, '\u0800'); assert(buf[6 .. $] == "\u0800"); - encode(buf, '\uD7FF'); assert(buf[9 .. $] == "\uD7FF"); - encode(buf, '\uE000'); assert(buf[12 .. $] == "\uE000"); - encode(buf, 0xFFFE); assert(buf[15 .. $] == "\xEF\xBF\xBE"); - encode(buf, 0xFFFF); assert(buf[18 .. $] == "\xEF\xBF\xBF"); - encode(buf, '\U00010000'); assert(buf[21 .. $] == "\U00010000"); - encode(buf, '\U0010FFFF'); assert(buf[25 .. $] == "\U0010FFFF"); - - assertThrown!UTFException(encode(buf, cast(dchar) 0xD800)); - assertThrown!UTFException(encode(buf, cast(dchar) 0xDBFF)); - assertThrown!UTFException(encode(buf, cast(dchar) 0xDC00)); - assertThrown!UTFException(encode(buf, cast(dchar) 0xDFFF)); - assertThrown!UTFException(encode(buf, cast(dchar) 0x110000)); - - enum replacementDcharString = "\uFFFD"; - enum rdcslen = replacementDcharString.length; - assert(buf[$ - rdcslen .. $] != replacementDcharString); - encode!(Yes.useReplacementDchar)(buf, cast(dchar) 0x110000); - assert(buf[$ - rdcslen .. $] == replacementDcharString); - }); -} - -/// ditto -void encode(UseReplacementDchar useReplacementDchar = No.useReplacementDchar)( - ref scope wchar[] str, dchar c) @safe pure -{ - if (c <= 0xFFFF) - { - if (0xD800 <= c && c <= 0xDFFF) - c = _utfException!useReplacementDchar("Encoding an isolated surrogate code point in UTF-16", c); - - assert(isValidDchar(c)); - L1: - str ~= cast(wchar) c; - } - else if (c <= 0x10FFFF) - { - wchar[2] buf; - - assert(isValidDchar(c)); - buf[0] = cast(wchar)((((c - 0x10000) >> 10) & 0x3FF) + 0xD800); - buf[1] = cast(wchar)(((c - 0x10000) & 0x3FF) + 0xDC00); - str ~= buf; - } - else - { - assert(!isValidDchar(c)); - c = _utfException!useReplacementDchar("Encoding an invalid code point in UTF-16", c); - goto L1; - } -} - -@safe unittest -{ - import std.exception; - assertCTFEable!( - { - wchar[] buf; - - encode(buf, '\u0000'); assert(buf[0] == '\u0000'); - encode(buf, '\uD7FF'); assert(buf[1] == '\uD7FF'); - encode(buf, '\uE000'); assert(buf[2] == '\uE000'); - encode(buf, 0xFFFE); assert(buf[3] == 0xFFFE); - encode(buf, 0xFFFF); assert(buf[4] == 0xFFFF); - encode(buf, '\U00010000'); assert(buf[5 .. $] == "\U00010000"); - encode(buf, '\U0010FFFF'); assert(buf[7 .. $] == "\U0010FFFF"); - - assertThrown!UTFException(encode(buf, cast(dchar) 0xD800)); - assertThrown!UTFException(encode(buf, cast(dchar) 0xDBFF)); - assertThrown!UTFException(encode(buf, cast(dchar) 0xDC00)); - assertThrown!UTFException(encode(buf, cast(dchar) 0xDFFF)); - assertThrown!UTFException(encode(buf, cast(dchar) 0x110000)); - - assert(buf.back != replacementDchar); - encode!(Yes.useReplacementDchar)(buf, cast(dchar) 0x110000); - assert(buf.back == replacementDchar); - }); -} - -/// ditto -void encode(UseReplacementDchar useReplacementDchar = No.useReplacementDchar)( - ref scope dchar[] str, dchar c) @safe pure -{ - if ((0xD800 <= c && c <= 0xDFFF) || 0x10FFFF < c) - c = _utfException!useReplacementDchar("Encoding an invalid code point in UTF-32", c); - else - assert(isValidDchar(c)); - str ~= c; -} - -@safe unittest -{ - import std.exception; - assertCTFEable!( - { - dchar[] buf; - - encode(buf, '\u0000'); assert(buf[0] == '\u0000'); - encode(buf, '\uD7FF'); assert(buf[1] == '\uD7FF'); - encode(buf, '\uE000'); assert(buf[2] == '\uE000'); - encode(buf, 0xFFFE ); assert(buf[3] == 0xFFFE); - encode(buf, 0xFFFF ); assert(buf[4] == 0xFFFF); - encode(buf, '\U0010FFFF'); assert(buf[5] == '\U0010FFFF'); - - assertThrown!UTFException(encode(buf, cast(dchar) 0xD800)); - assertThrown!UTFException(encode(buf, cast(dchar) 0xDBFF)); - assertThrown!UTFException(encode(buf, cast(dchar) 0xDC00)); - assertThrown!UTFException(encode(buf, cast(dchar) 0xDFFF)); - assertThrown!UTFException(encode(buf, cast(dchar) 0x110000)); - - assert(buf.back != replacementDchar); - encode!(Yes.useReplacementDchar)(buf, cast(dchar) 0x110000); - assert(buf.back == replacementDchar); - }); -} - - -/++ - Returns the number of code units that are required to encode the code point - `c` when `C` is the character type used to encode it. - +/ -ubyte codeLength(C)(dchar c) @safe pure nothrow @nogc -if (isSomeChar!C) -{ - static if (C.sizeof == 1) - { - if (c <= 0x7F) return 1; - if (c <= 0x7FF) return 2; - if (c <= 0xFFFF) return 3; - if (c <= 0x10FFFF) return 4; - assert(false); - } - else static if (C.sizeof == 2) - { - return c <= 0xFFFF ? 1 : 2; - } - else - { - static assert(C.sizeof == 4); - return 1; - } -} - -/// -@safe pure nothrow @nogc unittest -{ - assert(codeLength!char('a') == 1); - assert(codeLength!wchar('a') == 1); - assert(codeLength!dchar('a') == 1); - - assert(codeLength!char('\U0010FFFF') == 4); - assert(codeLength!wchar('\U0010FFFF') == 2); - assert(codeLength!dchar('\U0010FFFF') == 1); -} - - -/++ - Returns the number of code units that are required to encode `str` - in a string whose character type is `C`. This is particularly useful - when slicing one string with the length of another and the two string - types use different character types. - - Params: - C = the character type to get the encoding length for - input = the $(REF_ALTTEXT input range, isInputRange, std,range,primitives) - to calculate the encoding length from - Returns: - The number of code units in `input` when encoded to `C` - +/ -size_t codeLength(C, InputRange)(InputRange input) -if (isSomeFiniteCharInputRange!InputRange) -{ - alias EncType = typeof(cast() ElementEncodingType!InputRange.init); - static if (isSomeString!InputRange && is(EncType == C) && is(typeof(input.length))) - return input.length; - else - { - size_t total = 0; - - foreach (c; input.byDchar) - total += codeLength!C(c); - - return total; - } -} - -/// -@safe unittest -{ - assert(codeLength!char("hello world") == - "hello world".length); - assert(codeLength!wchar("hello world") == - "hello world"w.length); - assert(codeLength!dchar("hello world") == - "hello world"d.length); - - assert(codeLength!char(`プログラミング`) == - `プログラミング`.length); - assert(codeLength!wchar(`プログラミング`) == - `プログラミング`w.length); - assert(codeLength!dchar(`プログラミング`) == - `プログラミング`d.length); - - string haystack = `Être sans la veritĂŠ, ça, ce ne serait pas bien.`; - wstring needle = `Être sans la veritĂŠ`; - assert(haystack[codeLength!char(needle) .. $] == - `, ça, ce ne serait pas bien.`); -} - -@safe unittest -{ - import std.algorithm.iteration : filter; - import std.conv : to; - import std.exception; - - assertCTFEable!( - { - foreach (S; AliasSeq!( char[], const char[], string, - wchar[], const wchar[], wstring, - dchar[], const dchar[], dstring)) - { - foreach (C; AliasSeq!(char, wchar, dchar)) - { - assert(codeLength!C(to!S("Walter Bright")) == to!(C[])("Walter Bright").length); - assert(codeLength!C(to!S(`言語`)) == to!(C[])(`言語`).length); - assert(codeLength!C(to!S(`ウェブサイト@La_VeritĂŠ.com`)) == - to!(C[])(`ウェブサイト@La_VeritĂŠ.com`).length); - assert(codeLength!C(to!S(`ウェブサイト@La_VeritĂŠ.com`).filter!(x => true)()) == - to!(C[])(`ウェブサイト@La_VeritĂŠ.com`).length); - } - } - }); -} - -/+ -Internal helper function: - -Returns true if it is safe to search for the Codepoint `c` inside -code units, without decoding. - -This is a runtime check that is used an optimization in various functions, -particularly, in `std.string`. - +/ -package bool canSearchInCodeUnits(C)(dchar c) -if (isSomeChar!C) -{ - static if (C.sizeof == 1) - return c <= 0x7F; - else static if (C.sizeof == 2) - return c <= 0xD7FF || (0xE000 <= c && c <= 0xFFFF); - else static if (C.sizeof == 4) - return true; - else - static assert(0); -} -@safe unittest -{ - assert( canSearchInCodeUnits! char('a')); - assert( canSearchInCodeUnits!wchar('a')); - assert( canSearchInCodeUnits!dchar('a')); - assert(!canSearchInCodeUnits! char('Ăś')); //Important test: Ăś <= 0xFF - assert(!canSearchInCodeUnits! char(cast(char)'Ăś')); //Important test: Ăś <= 0xFF - assert( canSearchInCodeUnits!wchar('Ăś')); - assert( canSearchInCodeUnits!dchar('Ăś')); - assert(!canSearchInCodeUnits! char('日')); - assert( canSearchInCodeUnits!wchar('日')); - assert( canSearchInCodeUnits!dchar('日')); - assert(!canSearchInCodeUnits!wchar(cast(wchar) 0xDA00)); - assert( canSearchInCodeUnits!dchar(cast(dchar) 0xDA00)); - assert(!canSearchInCodeUnits! char('\U00010001')); - assert(!canSearchInCodeUnits!wchar('\U00010001')); - assert( canSearchInCodeUnits!dchar('\U00010001')); -} - -/* =================== Validation ======================= */ - -/++ - Checks to see if `str` is well-formed unicode or not. - - Throws: - `UTFException` if `str` is not well-formed. - +/ -void validate(S)(in S str) @safe pure -if (isSomeString!S) -{ - immutable len = str.length; - for (size_t i = 0; i < len; ) - { - decode(str, i); - } -} - -/// -@safe unittest -{ - import std.exception : assertThrown; - char[] a = [167, 133, 175]; - assertThrown!UTFException(validate(a)); -} - -// https://issues.dlang.org/show_bug.cgi?id=12923 -@safe unittest -{ - import std.exception; - assertThrown((){ - char[3]a=[167, 133, 175]; - validate(a[]); - }()); -} - -/** - * Encodes the elements of `s` to UTF-8 and returns a newly allocated - * string of the elements. - * - * Params: - * s = the string to encode - * Returns: - * A UTF-8 string - * See_Also: - * For a lazy, non-allocating version of these functions, see $(LREF byUTF). - */ -string toUTF8(S)(S s) -if (isSomeFiniteCharInputRange!S) -{ - return toUTFImpl!string(s); -} - -/// -@safe pure unittest -{ - import std.algorithm.comparison : equal; - - // The Ăś is represented by two UTF-8 code units - assert("Hellø"w.toUTF8.equal(['H', 'e', 'l', 'l', 0xC3, 0xB8])); - - // 𐐡 is four code units in UTF-8 - assert("𐐡"d.toUTF8.equal([0xF0, 0x90, 0x90, 0xB7])); -} - -@system pure unittest -{ - import std.algorithm.comparison : equal; - import std.internal.test.dummyrange : ReferenceInputRange; - - alias RT = ReferenceInputRange!(ElementType!(string)); - auto r1 = new RT("Hellø"); - auto r2 = new RT("𐐡"); - - assert(r1.toUTF8.equal(['H', 'e', 'l', 'l', 0xC3, 0xB8])); - assert(r2.toUTF8.equal([0xF0, 0x90, 0x90, 0xB7])); -} - -/** - * Encodes the elements of `s` to UTF-16 and returns a newly GC allocated - * `wstring` of the elements. - * - * Params: - * s = the range to encode - * Returns: - * A UTF-16 string - * See_Also: - * For a lazy, non-allocating version of these functions, see $(LREF byUTF). - */ -wstring toUTF16(S)(S s) -if (isSomeFiniteCharInputRange!S) -{ - return toUTFImpl!wstring(s); -} - -/// -@safe pure unittest -{ - import std.algorithm.comparison : equal; - - // these graphemes are two code units in UTF-16 and one in UTF-32 - assert("𤭢"d.length == 1); - assert("𐐡"d.length == 1); - - assert("𤭢"d.toUTF16.equal([0xD852, 0xDF62])); - assert("𐐡"d.toUTF16.equal([0xD801, 0xDC37])); -} - -@system pure unittest -{ - import std.algorithm.comparison : equal; - import std.internal.test.dummyrange : ReferenceInputRange; - - alias RT = ReferenceInputRange!(ElementType!(string)); - auto r1 = new RT("𤭢"); - auto r2 = new RT("𐐡"); - - assert(r1.toUTF16.equal([0xD852, 0xDF62])); - assert(r2.toUTF16.equal([0xD801, 0xDC37])); -} - - -/** - * Encodes the elements of `s` to UTF-32 and returns a newly GC allocated - * `dstring` of the elements. - * - * Params: - * s = the range to encode - * Returns: - * A UTF-32 string - * See_Also: - * For a lazy, non-allocating version of these functions, see $(LREF byUTF). - */ -dstring toUTF32(S)(scope S s) -if (isSomeFiniteCharInputRange!S) -{ - return toUTFImpl!dstring(s); -} - -/// -@safe pure unittest -{ - import std.algorithm.comparison : equal; - - // these graphemes are two code units in UTF-16 and one in UTF-32 - assert("𤭢"w.length == 2); - assert("𐐡"w.length == 2); - - assert("𤭢"w.toUTF32.equal([0x00024B62])); - assert("𐐡"w.toUTF32.equal([0x00010437])); -} - -private T toUTFImpl(T, S)(scope S s) -{ - static if (is(S : T)) - { - return s.idup; - } - else - { - import std.array : appender; - auto app = appender!T(); - - static if (is(S == C[], C) || hasLength!S) - app.reserve(s.length); - - ElementEncodingType!T e = void; - foreach (c; s.byUTF!(typeof(cast() ElementEncodingType!T.init))) - app.put(c); - - return app.data; - } -} - -/* =================== toUTFz ======================= */ - -/++ - Returns a C-style zero-terminated string equivalent to `str`. `str` - must not contain embedded `'\0'`'s as any C function will treat the first - `'\0'` that it sees as the end of the string. If `str.empty` is - `true`, then a string containing only `'\0'` is returned. - - `toUTFz` accepts any type of string and is templated on the type of - character pointer that you wish to convert to. It will avoid allocating a - new string if it can, but there's a decent chance that it will end up having - to allocate a new string - particularly when dealing with character types - other than `char`. - - $(RED Warning 1:) If the result of `toUTFz` equals `str.ptr`, then if - anything alters the character one past the end of `str` (which is the - `'\0'` character terminating the string), then the string won't be - zero-terminated anymore. The most likely scenarios for that are if you - append to `str` and no reallocation takes place or when `str` is a - slice of a larger array, and you alter the character in the larger array - which is one character past the end of `str`. Another case where it could - occur would be if you had a mutable character array immediately after - `str` in memory (for example, if they're member variables in a - user-defined type with one declared right after the other) and that - character array happened to start with `'\0'`. Such scenarios will never - occur if you immediately use the zero-terminated string after calling - `toUTFz` and the C function using it doesn't keep a reference to it. - Also, they are unlikely to occur even if you save the zero-terminated string - (the cases above would be among the few examples of where it could happen). - However, if you save the zero-terminate string and want to be absolutely - certain that the string stays zero-terminated, then simply append a - `'\0'` to the string and use its `ptr` property rather than calling - `toUTFz`. - - $(RED Warning 2:) When passing a character pointer to a C function, and the - C function keeps it around for any reason, make sure that you keep a - reference to it in your D code. Otherwise, it may go away during a garbage - collection cycle and cause a nasty bug when the C code tries to use it. - +/ -template toUTFz(P) -if (is(P == C*, C) && isSomeChar!C) -{ - P toUTFz(S)(S str) @safe pure - if (isSomeString!S) - { - return toUTFzImpl!(P, S)(str); - } -} - -/// -@safe pure unittest -{ - auto p1 = toUTFz!(char*)("hello world"); - auto p2 = toUTFz!(const(char)*)("hello world"); - auto p3 = toUTFz!(immutable(char)*)("hello world"); - auto p4 = toUTFz!(char*)("hello world"d); - auto p5 = toUTFz!(const(wchar)*)("hello world"); - auto p6 = toUTFz!(immutable(dchar)*)("hello world"w); -} - -private P toUTFzImpl(P, S)(return scope S str) @safe pure -if (is(immutable typeof(*P.init) == typeof(str[0]))) -//immutable(C)[] -> C*, const(C)*, or immutable(C)* -{ - if (str.empty) - { - typeof(*P.init)[] retval = ['\0']; - - auto trustedPtr() @trusted { return retval.ptr; } - return trustedPtr(); - } - - alias C = typeof(cast() ElementEncodingType!S.init); - - //If the P is mutable, then we have to make a copy. - static if (is(typeof(cast() *P.init) == typeof(*P.init))) - { - return toUTFzImpl!(P, const(C)[])(cast(const(C)[])str); - } - else - { - if (!__ctfe) - { - auto trustedPtrAdd(S s) @trusted { return s.ptr + s.length; } - immutable p = trustedPtrAdd(str); - - // Peek past end of str, if it's 0, no conversion necessary. - // Note that the compiler will put a 0 past the end of static - // strings, and the storage allocator will put a 0 past the end - // of newly allocated char[]'s. - // Is p dereferenceable? A simple test: if the p points to an - // address multiple of 4, then conservatively assume the pointer - // might be pointing to a new block of memory, which might be - // unreadable. Otherwise, it's definitely pointing to valid - // memory. - if ((cast(size_t) p & 3) && *p == '\0') - return &str[0]; - } - - return toUTFzImpl!(P, const(C)[])(cast(const(C)[])str); - } -} - -private P toUTFzImpl(P, S)(return scope S str) @safe pure -if (is(typeof(str[0]) C) && is(immutable typeof(*P.init) == immutable C) && !is(C == immutable)) -//C[] or const(C)[] -> C*, const(C)*, or immutable(C)* -{ - alias InChar = typeof(str[0]); - alias UInChar = typeof(cast() str[0]); // unqualified version of InChar - alias OutChar = typeof(*P.init); - alias UOutChar = typeof(cast() *P.init); // unqualified version - - //const(C)[] -> const(C)* or - //C[] -> C* or const(C)* - static if (( is(const(UInChar) == InChar) && is( const(UOutChar) == OutChar)) || - (!is(const(UInChar) == InChar) && !is(immutable(UOutChar) == OutChar))) - { - if (!__ctfe) - { - auto trustedPtrAdd(S s) @trusted { return s.ptr + s.length; } - auto p = trustedPtrAdd(str); - - if ((cast(size_t) p & 3) && *p == '\0') - return &str[0]; - } - - str ~= '\0'; - return &str[0]; - } - //const(C)[] -> C* or immutable(C)* or - //C[] -> immutable(C)* - else - { - import std.array : uninitializedArray; - auto copy = uninitializedArray!(UOutChar[])(str.length + 1); - copy[0 .. $ - 1] = str[]; - copy[$ - 1] = '\0'; - - auto trustedCast(typeof(copy) c) @trusted { return cast(P) c.ptr; } - return trustedCast(copy); - } -} - -private P toUTFzImpl(P, S)(S str) @safe pure -if (!is(immutable typeof(*P.init) == immutable typeof(str[0]))) -//C1[], const(C1)[], or immutable(C1)[] -> C2*, const(C2)*, or immutable(C2)* -{ - import std.array : appender; - auto retval = appender!(typeof(*P.init)[])(); - - foreach (dchar c; str) - retval.put(c); - retval.put('\0'); - - return () @trusted { return cast(P) retval.data.ptr; } (); -} - -@safe pure unittest -{ - import core.exception : AssertError; - import std.algorithm; - import std.conv : to; - import std.exception; - import std.string : format; - - assertCTFEable!( - { - foreach (S; AliasSeq!(string, wstring, dstring)) - { - alias C = Unqual!(ElementEncodingType!S); - - auto s1 = to!S("hello\U00010143\u0100\U00010143"); - auto temp = new C[](s1.length + 1); - temp[0 .. $ - 1] = s1[0 .. $]; - temp[$ - 1] = '\n'; - --temp.length; - auto trustedAssumeUnique(T)(T t) @trusted { return assumeUnique(t); } - auto s2 = trustedAssumeUnique(temp); - assert(s1 == s2); - - void trustedCStringAssert(P, S)(S s) @trusted - { - auto p = toUTFz!P(s); - assert(p[0 .. s.length] == s); - assert(p[s.length] == '\0'); - } - - foreach (P; AliasSeq!(C*, const(C)*, immutable(C)*)) - { - trustedCStringAssert!P(s1); - trustedCStringAssert!P(s2); - } - } - }); - - static void test(P, S)(S s, size_t line = __LINE__) @trusted - { - static size_t zeroLen(C)(const(C)* ptr) @trusted - { - size_t len = 0; - while (*ptr != '\0') { ++ptr; ++len; } - return len; - } - - auto p = toUTFz!P(s); - immutable len = zeroLen(p); - enforce(cmp(s, p[0 .. len]) == 0, - new AssertError(format("Unit test failed: %s %s", P.stringof, S.stringof), - __FILE__, line)); - } - - assertCTFEable!( - { - foreach (P; AliasSeq!(wchar*, const(wchar)*, immutable(wchar)*, - dchar*, const(dchar)*, immutable(dchar)*)) - { - test!P("hello\U00010143\u0100\U00010143"); - } - foreach (P; AliasSeq!( char*, const( char)*, immutable( char)*, - dchar*, const(dchar)*, immutable(dchar)*)) - { - test!P("hello\U00010143\u0100\U00010143"w); - } - foreach (P; AliasSeq!( char*, const( char)*, immutable( char)*, - wchar*, const(wchar)*, immutable(wchar)*)) - { - test!P("hello\U00010143\u0100\U00010143"d); - } - foreach (S; AliasSeq!( char[], const( char)[], - wchar[], const(wchar)[], - dchar[], const(dchar)[])) - { - auto s = to!S("hello\U00010143\u0100\U00010143"); - - foreach (P; AliasSeq!( char*, const( char)*, immutable( char)*, - wchar*, const(wchar)*, immutable(wchar)*, - dchar*, const(dchar)*, immutable(dchar)*)) - { - test!P(s); - } - } - }); -} - - -/++ - `toUTF16z` is a convenience function for `toUTFz!(const(wchar)*)`. - - Encodes string `s` into UTF-16 and returns the encoded string. - `toUTF16z` is suitable for calling the 'W' functions in the Win32 API - that take an `LPCWSTR` argument. - +/ -const(wchar)* toUTF16z(C)(const(C)[] str) @safe pure -if (isSomeChar!C) -{ - return toUTFz!(const(wchar)*)(str); -} - -/// -@system unittest -{ - string str = "Hello, World!"; - const(wchar)* p = str.toUTF16z; - assert(p[str.length] == '\0'); -} - -@safe pure unittest -{ - import std.conv : to; - //toUTFz is already thoroughly tested, so this will just verify that - //toUTF16z compiles properly for the various string types. - foreach (S; AliasSeq!(string, wstring, dstring)) - assert(toUTF16z(to!S("hello world")) !is null); -} - - -/* ================================ tests ================================== */ - -@safe pure unittest -{ - import std.exception; - - assertCTFEable!( - { - assert(toUTF16("hello"c) == "hello"); - assert(toUTF32("hello"c) == "hello"); - assert(toUTF8 ("hello"w) == "hello"); - assert(toUTF32("hello"w) == "hello"); - assert(toUTF8 ("hello"d) == "hello"); - assert(toUTF16("hello"d) == "hello"); - - assert(toUTF16("hel\u1234o"c) == "hel\u1234o"); - assert(toUTF32("hel\u1234o"c) == "hel\u1234o"); - assert(toUTF8 ("hel\u1234o"w) == "hel\u1234o"); - assert(toUTF32("hel\u1234o"w) == "hel\u1234o"); - assert(toUTF8 ("hel\u1234o"d) == "hel\u1234o"); - assert(toUTF16("hel\u1234o"d) == "hel\u1234o"); - - assert(toUTF16("he\U0010AAAAllo"c) == "he\U0010AAAAllo"); - assert(toUTF32("he\U0010AAAAllo"c) == "he\U0010AAAAllo"); - assert(toUTF8 ("he\U0010AAAAllo"w) == "he\U0010AAAAllo"); - assert(toUTF32("he\U0010AAAAllo"w) == "he\U0010AAAAllo"); - assert(toUTF8 ("he\U0010AAAAllo"d) == "he\U0010AAAAllo"); - assert(toUTF16("he\U0010AAAAllo"d) == "he\U0010AAAAllo"); - }); -} - - -/++ - Returns the total number of code points encoded in `str`. - - Supercedes: This function supercedes $(LREF toUCSindex). - - Standards: Unicode 5.0, ASCII, ISO-8859-1, WINDOWS-1252 - - Throws: - `UTFException` if `str` is not well-formed. - +/ -size_t count(C)(const(C)[] str) @safe pure nothrow @nogc -if (isSomeChar!C) -{ - return walkLength(str.byDchar); -} - -/// -@safe pure nothrow @nogc unittest -{ - assert(count("") == 0); - assert(count("a") == 1); - assert(count("abc") == 3); - assert(count("\u20AC100") == 4); -} - -@safe pure nothrow @nogc unittest -{ - import std.exception; - assertCTFEable!( - { - assert(count("") == 0); - assert(count("a") == 1); - assert(count("abc") == 3); - assert(count("\u20AC100") == 4); - }); -} - - -// Ranges of code units for testing. -version (StdUnittest) -{ -private: - struct InputCU(C) - { - import std.conv : to; - @property bool empty() { return _str.empty; } - @property C front() { return _str[0]; } - void popFront() { _str = _str[1 .. $]; } - - this(inout(C)[] str) - { - _str = to!(C[])(str); - } - - C[] _str; - } - - struct BidirCU(C) - { - import std.conv : to; - @property bool empty() { return _str.empty; } - @property C front() { return _str[0]; } - void popFront() { _str = _str[1 .. $]; } - @property C back() { return _str[$ - 1]; } - void popBack() { _str = _str[0 .. $ - 1]; } - @property auto save() { return BidirCU(_str); } - @property size_t length() { return _str.length; } - - this(inout(C)[] str) - { - _str = to!(C[])(str); - } - - C[] _str; - } - - struct RandomCU(C) - { - import std.conv : to; - @property bool empty() { return _str.empty; } - @property C front() { return _str[0]; } - void popFront() { _str = _str[1 .. $]; } - @property C back() { return _str[$ - 1]; } - void popBack() { _str = _str[0 .. $ - 1]; } - @property auto save() { return RandomCU(_str); } - @property size_t length() { return _str.length; } - C opIndex(size_t i) { return _str[i]; } - auto opSlice(size_t i, size_t j) { return RandomCU(_str[i .. j]); } - - this(inout(C)[] str) - { - _str = to!(C[])(str); - } - - C[] _str; - } - - class RefBidirCU(C) - { - import std.conv : to; - @property bool empty() { return _str.empty; } - @property C front() { return _str[0]; } - void popFront() { _str = _str[1 .. $]; } - @property C back() { return _str[$ - 1]; } - void popBack() { _str = _str[0 .. $ - 1]; } - @property auto save() { return new RefBidirCU(_str); } - @property size_t length() { return _str.length; } - - this(inout(C)[] str) - { - _str = to!(C[])(str); - } - - C[] _str; - } - - class RefRandomCU(C) - { - import std.conv : to; - @property bool empty() { return _str.empty; } - @property C front() { return _str[0]; } - void popFront() { _str = _str[1 .. $]; } - @property C back() { return _str[$ - 1]; } - void popBack() { _str = _str[0 .. $ - 1]; } - @property auto save() { return new RefRandomCU(_str); } - @property size_t length() { return _str.length; } - C opIndex(size_t i) { return _str[i]; } - auto opSlice(size_t i, size_t j) { return new RefRandomCU(_str[i .. j]); } - - this(inout(C)[] str) - { - _str = to!(C[])(str); - } - - C[] _str; - } -} - - -/** - * Inserted in place of invalid UTF sequences. - * - * References: - * $(LINK http://en.wikipedia.org/wiki/Replacement_character#Replacement_character) - */ -enum dchar replacementDchar = '\uFFFD'; - -/******************************************** - * Iterate a range of char, wchar, or dchars by code unit. - * - * The purpose is to bypass the special case decoding that - * $(REF front, std,range,primitives) does to character arrays. As a result, - * using ranges with `byCodeUnit` can be `nothrow` while - * $(REF front, std,range,primitives) throws when it encounters invalid Unicode - * sequences. - * - * A code unit is a building block of the UTF encodings. Generally, an - * individual code unit does not represent what's perceived as a full - * character (a.k.a. a grapheme cluster in Unicode terminology). Many characters - * are encoded with multiple code units. For example, the UTF-8 code units for - * `ø` are `0xC3 0xB8`. That means, an individual element of `byCodeUnit` - * often does not form a character on its own. Attempting to treat it as - * one while iterating over the resulting range will give nonsensical results. - * - * Params: - * r = an $(REF_ALTTEXT input range, isInputRange, std,range,primitives) - * of characters (including strings) or a type that implicitly converts to a string type. - * Returns: - * If `r` is not an auto-decodable string (i.e. a narrow string or a - * user-defined type that implicitly converts to a string type), then `r` - * is returned. - * - * Otherwise, `r` is converted to its corresponding string type (if it's - * not already a string) and wrapped in a random-access range where the - * element encoding type of the string (its code unit) is the element type - * of the range, and that range returned. The range has slicing. - * - * If `r` is quirky enough to be a struct or class which is an input range - * of characters on its own (i.e. it has the input range API as member - * functions), $(I and) it's implicitly convertible to a string type, then - * `r` is returned, and no implicit conversion takes place. - * - * If `r` is wrapped in a new range, then that range has a `source` - * property for returning the string that's currently contained within that - * range. - * - * See_Also: - * Refer to the $(MREF std, uni) docs for a reference on Unicode - * terminology. - * - * For a range that iterates by grapheme cluster (written character) see - * $(REF byGrapheme, std,uni). - */ -auto byCodeUnit(R)(R r) -if ((isConvertibleToString!R && !isStaticArray!R) || - (isInputRange!R && isSomeChar!(ElementEncodingType!R))) -{ - import std.traits : StringTypeOf; - static if (// This would be cleaner if we had a way to check whether a type - // was a range without any implicit conversions. - (isAutodecodableString!R && !__traits(hasMember, R, "empty") && - !__traits(hasMember, R, "front") && !__traits(hasMember, R, "popFront"))) - { - static struct ByCodeUnitImpl - { - @safe pure nothrow @nogc: - - @property bool empty() const { return source.length == 0; } - @property auto ref front() inout { return source[0]; } - void popFront() { source = source[1 .. $]; } - - @property auto save() { return ByCodeUnitImpl(source.save); } - - @property auto ref back() inout { return source[$ - 1]; } - void popBack() { source = source[0 .. $-1]; } - - auto ref opIndex(size_t index) inout { return source[index]; } - auto opSlice(size_t lower, size_t upper) { return ByCodeUnitImpl(source[lower .. upper]); } - - @property size_t length() const { return source.length; } - alias opDollar = length; - - StringTypeOf!R source; - } - - static assert(isRandomAccessRange!ByCodeUnitImpl); - - return ByCodeUnitImpl(r); - } - else static if (!isInputRange!R || - (is(R : const dchar[]) && !__traits(hasMember, R, "empty") && - !__traits(hasMember, R, "front") && !__traits(hasMember, R, "popFront"))) - { - return cast(StringTypeOf!R) r; - } - else - { - // byCodeUnit for ranges and dchar[] is a no-op - return r; - } -} - -/// -@safe unittest -{ - import std.range.primitives; - import std.traits : isAutodecodableString; - - auto r = "Hello, World!".byCodeUnit(); - static assert(hasLength!(typeof(r))); - static assert(hasSlicing!(typeof(r))); - static assert(isRandomAccessRange!(typeof(r))); - static assert(is(ElementType!(typeof(r)) == immutable char)); - - // contrast with the range capabilities of standard strings (with or - // without autodecoding enabled). - auto s = "Hello, World!"; - static assert(isBidirectionalRange!(typeof(r))); - static if (isAutodecodableString!(typeof(s))) - { - // with autodecoding enabled, strings are non-random-access ranges of - // dchar. - static assert(is(ElementType!(typeof(s)) == dchar)); - static assert(!isRandomAccessRange!(typeof(s))); - static assert(!hasSlicing!(typeof(s))); - static assert(!hasLength!(typeof(s))); - } - else - { - // without autodecoding, strings are normal arrays. - static assert(is(ElementType!(typeof(s)) == immutable char)); - static assert(isRandomAccessRange!(typeof(s))); - static assert(hasSlicing!(typeof(s))); - static assert(hasLength!(typeof(s))); - } -} - -/// `byCodeUnit` does no Unicode decoding -@safe unittest -{ - string noel1 = "noe\u0308l"; // noĂŤl using e + combining diaeresis - assert(noel1.byCodeUnit[2] != 'ĂŤ'); - assert(noel1.byCodeUnit[2] == 'e'); - - string noel2 = "no\u00EBl"; // noĂŤl using a precomposed ĂŤ character - // Because string is UTF-8, the code unit at index 2 is just - // the first of a sequence that encodes 'ĂŤ' - assert(noel2.byCodeUnit[2] != 'ĂŤ'); -} - -/// `byCodeUnit` exposes a `source` property when wrapping narrow strings. -@safe unittest -{ - import std.algorithm.comparison : equal; - import std.range : popFrontN; - import std.traits : isAutodecodableString; - { - auto range = byCodeUnit("hello world"); - range.popFrontN(3); - assert(equal(range.save, "lo world")); - static if (isAutodecodableString!string) // only enabled with autodecoding - { - string str = range.source; - assert(str == "lo world"); - } - } - // source only exists if the range was wrapped - { - auto range = byCodeUnit("hello world"d); - static assert(!__traits(compiles, range.source)); - } -} - -@safe pure nothrow @nogc unittest -{ - import std.range; - { - enum testStr = "𐁄𐂌𐃯 hello ディラン"; - char[testStr.length] s; - int i; - foreach (c; testStr.byCodeUnit().byCodeUnit()) - { - s[i++] = c; - } - assert(s == testStr); - } - { - enum testStr = "𐁄𐂌𐃯 hello ディラン"w; - wchar[testStr.length] s; - int i; - foreach (c; testStr.byCodeUnit().byCodeUnit()) - { - s[i++] = c; - } - assert(s == testStr); - } - { - enum testStr = "𐁄𐂌𐃯 hello ディラン"d; - dchar[testStr.length] s; - int i; - foreach (c; testStr.byCodeUnit().byCodeUnit()) - { - s[i++] = c; - } - assert(s == testStr); - } - { - auto bcu = "hello".byCodeUnit(); - assert(bcu.length == 5); - assert(bcu[3] == 'l'); - assert(bcu[2 .. 4][1] == 'l'); - } - { - char[5] orig = "hello"; - auto bcu = orig[].byCodeUnit(); - bcu.front = 'H'; - assert(bcu.front == 'H'); - bcu[1] = 'E'; - assert(bcu[1] == 'E'); - } - { - auto bcu = "hello".byCodeUnit().byCodeUnit(); - static assert(isForwardRange!(typeof(bcu))); - static assert(is(typeof(bcu) == struct) == isAutodecodableString!string); - auto s = bcu.save; - bcu.popFront(); - assert(s.front == 'h'); - } - { - auto bcu = "hello".byCodeUnit(); - static assert(hasSlicing!(typeof(bcu))); - static assert(isBidirectionalRange!(typeof(bcu))); - static assert(is(typeof(bcu) == struct) == isAutodecodableString!string); - static assert(is(typeof(bcu) == typeof(bcu.byCodeUnit()))); - auto ret = bcu.retro; - assert(ret.front == 'o'); - ret.popFront(); - assert(ret.front == 'l'); - } - { - auto bcu = "κ὚σΟξ"w.byCodeUnit(); - static assert(hasSlicing!(typeof(bcu))); - static assert(isBidirectionalRange!(typeof(bcu))); - static assert(is(typeof(bcu) == struct) == isAutodecodableString!wstring); - static assert(is(typeof(bcu) == typeof(bcu.byCodeUnit()))); - auto ret = bcu.retro; - assert(ret.front == 'Îľ'); - ret.popFront(); - assert(ret.front == 'Îź'); - } - { - static struct Stringish - { - string s; - alias s this; - } - - auto orig = Stringish("\U0010fff8 𐁊 foo 𐂓"); - auto bcu = orig.byCodeUnit(); - static assert(is(typeof(bcu) == struct)); - static assert(!is(typeof(bcu) == Stringish) == isAutodecodableString!Stringish); - static assert(is(typeof(bcu) == typeof(bcu.byCodeUnit()))); - static assert(is(ElementType!(typeof(bcu)) == immutable char)); - assert(bcu.front == cast(char) 244); - } - { - static struct WStringish - { - wstring s; - alias s this; - } - - auto orig = WStringish("\U0010fff8 𐁊 foo 𐂓"w); - auto bcu = orig.byCodeUnit(); - static assert(is(typeof(bcu) == struct)); - static assert(!is(typeof(bcu) == WStringish) == isAutodecodableString!WStringish); - static assert(is(typeof(bcu) == typeof(bcu.byCodeUnit()))); - static assert(is(ElementType!(typeof(bcu)) == immutable wchar)); - assert(bcu.front == cast(wchar) 56319); - } - { - static struct DStringish - { - dstring s; - alias s this; - } - - auto orig = DStringish("\U0010fff8 𐁊 foo 𐂓"d); - auto bcu = orig.byCodeUnit(); - static assert(is(typeof(bcu) == dstring)); - static assert(is(typeof(bcu) == typeof(bcu.byCodeUnit()))); - static assert(is(ElementType!(typeof(bcu)) == immutable dchar)); - assert(bcu.front == cast(dchar) 1114104); - } - { - static struct FuncStringish - { - string str; - string s() pure nothrow @nogc { return str; } - alias s this; - } - - auto orig = FuncStringish("\U0010fff8 𐁊 foo 𐂓"); - auto bcu = orig.byCodeUnit(); - static if (isAutodecodableString!FuncStringish) - static assert(is(typeof(bcu) == struct)); - else - static assert(is(typeof(bcu) == string)); - static assert(!is(typeof(bcu) == FuncStringish)); - static assert(is(typeof(bcu) == typeof(bcu.byCodeUnit()))); - static assert(is(ElementType!(typeof(bcu)) == immutable char)); - assert(bcu.front == cast(char) 244); - } - { - static struct Range - { - string data; - bool empty() pure nothrow @nogc { return data.empty; } - char front() pure nothrow @nogc { return data[0]; } - void popFront() pure nothrow @nogc { data = data[1 .. $]; } - } - - auto orig = Range("\U0010fff8 𐁊 foo 𐂓"); - auto bcu = orig.byCodeUnit(); - static assert(is(typeof(bcu) == Range)); - static assert(is(typeof(bcu) == typeof(bcu.byCodeUnit()))); - static assert(is(ElementType!(typeof(bcu)) == char)); - assert(bcu.front == cast(char) 244); - } - { - static struct WRange - { - wstring data; - bool empty() pure nothrow @nogc { return data.empty; } - wchar front() pure nothrow @nogc { return data[0]; } - void popFront() pure nothrow @nogc { data = data[1 .. $]; } - } - - auto orig = WRange("\U0010fff8 𐁊 foo 𐂓"w); - auto bcu = orig.byCodeUnit(); - static assert(is(typeof(bcu) == WRange)); - static assert(is(typeof(bcu) == typeof(bcu.byCodeUnit()))); - static assert(is(ElementType!(typeof(bcu)) == wchar)); - assert(bcu.front == 56319); - } - { - static struct DRange - { - dstring data; - bool empty() pure nothrow @nogc { return data.empty; } - dchar front() pure nothrow @nogc { return data[0]; } - void popFront() pure nothrow @nogc { data = data[1 .. $]; } - } - - auto orig = DRange("\U0010fff8 𐁊 foo 𐂓"d); - auto bcu = orig.byCodeUnit(); - static assert(is(typeof(bcu) == DRange)); - static assert(is(typeof(bcu) == typeof(bcu.byCodeUnit()))); - static assert(is(ElementType!(typeof(bcu)) == dchar)); - assert(bcu.front == 1114104); - } - { - static struct RangeAndStringish - { - bool empty() pure nothrow @nogc { return data.empty; } - char front() pure nothrow @nogc { return data[0]; } - void popFront() pure nothrow @nogc { data = data[1 .. $]; } - - string data; - string s; - alias s this; - } - - auto orig = RangeAndStringish("test.d", "other"); - auto bcu = orig.byCodeUnit(); - static assert(is(typeof(bcu) == RangeAndStringish)); - static assert(is(typeof(bcu) == typeof(bcu.byCodeUnit()))); - static assert(is(ElementType!(typeof(bcu)) == char)); - assert(bcu.front == 't'); - } - { - static struct WRangeAndStringish - { - bool empty() pure nothrow @nogc { return data.empty; } - wchar front() pure nothrow @nogc { return data[0]; } - void popFront() pure nothrow @nogc { data = data[1 .. $]; } - - wstring data; - wstring s; - alias s this; - } - - auto orig = WRangeAndStringish("test.d"w, "other"w); - auto bcu = orig.byCodeUnit(); - static assert(is(typeof(bcu) == WRangeAndStringish)); - static assert(is(typeof(bcu) == typeof(bcu.byCodeUnit()))); - static assert(is(ElementType!(typeof(bcu)) == wchar)); - assert(bcu.front == 't'); - } - { - static struct DRangeAndStringish - { - bool empty() pure nothrow @nogc { return data.empty; } - dchar front() pure nothrow @nogc { return data[0]; } - void popFront() pure nothrow @nogc { data = data[1 .. $]; } - - dstring data; - dstring s; - alias s this; - } - - auto orig = DRangeAndStringish("test.d"d, "other"d); - auto bcu = orig.byCodeUnit(); - static assert(is(typeof(bcu) == DRangeAndStringish)); - static assert(is(typeof(bcu) == typeof(bcu.byCodeUnit()))); - static assert(is(ElementType!(typeof(bcu)) == dchar)); - assert(bcu.front == 't'); - } - { - enum Enum : string { a = "test.d" } - - auto orig = Enum.a; - auto bcu = orig.byCodeUnit(); - static assert(!is(typeof(bcu) == Enum)); - static if (isAutodecodableString!Enum) - static assert(is(typeof(bcu) == struct)); - else - static assert(is(typeof(bcu) == string)); - static assert(is(ElementType!(typeof(bcu)) == immutable char)); - assert(bcu.front == 't'); - } - { - enum WEnum : wstring { a = "test.d"w } - - auto orig = WEnum.a; - auto bcu = orig.byCodeUnit(); - static assert(!is(typeof(bcu) == WEnum)); - static if (isAutodecodableString!WEnum) - static assert(is(typeof(bcu) == struct)); - else - static assert(is(typeof(bcu) == wstring)); - static assert(is(ElementType!(typeof(bcu)) == immutable wchar)); - assert(bcu.front == 't'); - } - { - enum DEnum : dstring { a = "test.d"d } - - auto orig = DEnum.a; - auto bcu = orig.byCodeUnit(); - static assert(is(typeof(bcu) == dstring)); - static assert(is(ElementType!(typeof(bcu)) == immutable dchar)); - assert(bcu.front == 't'); - } - - static if (autodecodeStrings) - { - static assert(!is(typeof(byCodeUnit("hello")) == string)); - static assert(!is(typeof(byCodeUnit("hello"w)) == wstring)); - } - else - { - static assert(is(typeof(byCodeUnit("hello")) == string)); - static assert(is(typeof(byCodeUnit("hello"w)) == wstring)); - } - static assert(is(typeof(byCodeUnit("hello"d)) == dstring)); - - static assert(!__traits(compiles, byCodeUnit((char[5]).init))); - static assert(!__traits(compiles, byCodeUnit((wchar[5]).init))); - static assert(!__traits(compiles, byCodeUnit((dchar[5]).init))); - - enum SEnum : char[5] { a = "hello" } - enum WSEnum : wchar[5] { a = "hello"w } - enum DSEnum : dchar[5] { a = "hello"d } - - static assert(!__traits(compiles, byCodeUnit(SEnum.a))); - static assert(!__traits(compiles, byCodeUnit(WSEnum.a))); - static assert(!__traits(compiles, byCodeUnit(DSEnum.a))); -} - -/**************************** - * Iterate an $(REF_ALTTEXT input range, isInputRange, std,range,primitives) - * of characters by char, wchar, or dchar. - * These aliases simply forward to $(LREF byUTF) with the - * corresponding C argument. - * - * Params: - * r = input range of characters, or array of characters - */ -alias byChar = byUTF!char; - -/// Ditto -alias byWchar = byUTF!wchar; - -/// Ditto -alias byDchar = byUTF!dchar; - -@safe pure nothrow @nogc unittest -{ - { - char[5] s; - int i; - foreach (c; "hello".byChar.byChar()) - { - //writefln("[%d] '%c'", i, c); - s[i++] = c; - } - assert(s == "hello"); - } - { - char[5+2+3+4+3+3] s; - int i; - dchar[10] a; - a[0 .. 8] = "hello\u07FF\uD7FF\U0010FFFF"d; - a[8] = 0xD800; // invalid - a[9] = cast(dchar) 0x110000; // invalid - foreach (c; a[].byChar()) - { - //writefln("[%d] '%c'", i, c); - s[i++] = c; - } - assert(s == "hello\u07FF\uD7FF\U0010FFFF\uFFFD\uFFFD"); - } - { - auto r = "hello"w.byChar(); - r.popFront(); - r.popFront(); - assert(r.front == 'l'); - } - { - auto r = "hello"d.byChar(); - r.popFront(); - r.popFront(); - assert(r.front == 'l'); - } - { - auto r = "hello"d.byChar(); - assert(isForwardRange!(typeof(r))); - auto s = r.save; - r.popFront(); - assert(s.front == 'h'); - } -} - -@safe pure nothrow @nogc unittest -{ - { - wchar[11] s; - int i; - dchar[10] a; - a[0 .. 8] = "hello\u07FF\uD7FF\U0010FFFF"d; - a[8] = 0xD800; // invalid - a[9] = cast(dchar) 0x110000; // invalid - foreach (c; a[].byWchar()) - { - //writefln("[%d] '%c' x%x", i, c, c); - s[i++] = c; - } - foreach (j, wchar c; "hello\u07FF\uD7FF\U0010FFFF\uFFFD\uFFFD"w) - { - //writefln("[%d] '%c' x%x", j, c, c); - } - assert(s == "hello\u07FF\uD7FF\U0010FFFF\uFFFD\uFFFD"w); - } - - { - auto r = "hello".byWchar(); - r.popFront(); - r.popFront(); - assert(r.front == 'l'); - } - { - auto r = "hello"d.byWchar(); - r.popFront(); - r.popFront(); - assert(r.front == 'l'); - } - { - auto r = "hello"d.byWchar(); - assert(isForwardRange!(typeof(r))); - auto s = r.save; - r.popFront(); - assert(s.front == 'h'); - } -} - -@safe pure nothrow @nogc unittest -{ - { - dchar[9] s; - int i; - string a = "hello\u07FF\uD7FF\U00010000\U0010FFFF"; // 1,2,3,4 byte sequences - foreach (c; a.byDchar()) - { - s[i++] = c; - } - assert(s == "hello\u07FF\uD7FF\U00010000\U0010FFFF"d); - } - { - foreach (s; invalidUTFstrings!char()) - { - auto r = s.byDchar(); - assert(!r.empty); - assert(r.front == r.front); - dchar c = r.front; - assert(c == replacementDchar); - } - } - { - auto r = "hello".byDchar(); - r.popFront(); - r.popFront(); - assert(r.front == 'l'); - } - - { - dchar[8] s; - int i; - wstring a = "hello\u07FF\uD7FF\U0010FFFF"w; - foreach (c; a.byDchar()) - { - //writefln("[%d] '%c' x%x", i, c, c); - s[i++] = c; - } - assert(s == "hello\u07FF\uD7FF\U0010FFFF"d); - } - { - foreach (s; invalidUTFstrings!wchar()) - { - auto r = s.byDchar(); - assert(!r.empty); - assert(r.front == r.front); - dchar c = r.front; - assert(c == replacementDchar); - } - } - { - wchar[2] ws; - ws[0] = 0xD800; - ws[1] = 0xDD00; // correct surrogate pair - auto r = ws[].byDchar(); - assert(!r.empty); - assert(r.front == r.front); - dchar c = r.front; - assert(c == '\U00010100'); - } - { - auto r = "hello"w.byDchar(); - r.popFront(); - r.popFront(); - assert(r.front == 'l'); - } - - { - dchar[5] s; - int i; - dstring a = "hello"d; - foreach (c; a.byDchar.byDchar()) - { - //writefln("[%d] '%c' x%x", i, c, c); - s[i++] = c; - } - assert(s == "hello"d); - } - { - auto r = "hello".byDchar(); - assert(isForwardRange!(typeof(r))); - auto s = r.save; - r.popFront(); - assert(s.front == 'h'); - } - { - auto r = "hello"w.byDchar(); - assert(isForwardRange!(typeof(r))); - auto s = r.save; - r.popFront(); - assert(s.front == 'h'); - } -} - -// test pure, @safe, nothrow, @nogc correctness of byChar/byWchar/byDchar, -// which needs to support ranges with and without those attributes - -pure @safe nothrow @nogc unittest -{ - dchar[5] s = "hello"d; - foreach (c; s[].byChar()) { } - foreach (c; s[].byWchar()) { } - foreach (c; s[].byDchar()) { } -} - -version (StdUnittest) -private int impureVariable; - -@system unittest -{ - static struct ImpureThrowingSystemRange(Char) - { - @property bool empty() const { return true; } - @property Char front() const { return Char.init; } - void popFront() - { - impureVariable++; - throw new Exception("only for testing nothrow"); - } - } - - foreach (Char; AliasSeq!(char, wchar, dchar)) - { - ImpureThrowingSystemRange!Char range; - foreach (c; range.byChar()) { } - foreach (c; range.byWchar()) { } - foreach (c; range.byDchar()) { } - } -} - -/**************************** - * Iterate an $(REF_ALTTEXT input range, isInputRange, std,range,primitives) - * of characters by char type `C` by encoding the elements of the range. - * - * UTF sequences that cannot be converted to the specified encoding are either - * replaced by U+FFFD per "5.22 Best Practice for U+FFFD Substitution" - * of the Unicode Standard 6.2 or result in a thrown UTFException. - * Hence byUTF is not symmetric. - * This algorithm is lazy, and does not allocate memory. - * `@nogc`, `pure`-ity, `nothrow`, and `@safe`-ty are inferred from the - * `r` parameter. - * - * Params: - * C = `char`, `wchar`, or `dchar` - * useReplacementDchar = UseReplacementDchar.yes means replace invalid UTF with `replacementDchar`, - * UseReplacementDchar.no means throw `UTFException` for invalid UTF - * - * Throws: - * `UTFException` if invalid UTF sequence and `useReplacementDchar` is set to `UseReplacementDchar.no` - * - * GC: - * Does not use GC if `useReplacementDchar` is set to `UseReplacementDchar.yes` - * - * Returns: - * A bidirectional range if `R` is a bidirectional range and not auto-decodable, - * as defined by $(REF isAutodecodableString, std, traits). - * - * A forward range if `R` is a forward range and not auto-decodable. - * - * Or, if `R` is a range and it is auto-decodable and - * `is(ElementEncodingType!typeof(r) == C)`, then the range is passed - * to $(LREF byCodeUnit). - * - * Otherwise, an input range of characters. - */ -template byUTF(C, UseReplacementDchar useReplacementDchar = Yes.useReplacementDchar) -if (isSomeChar!C) -{ - static if (is(immutable C == immutable UC, UC) && !is(C == UC)) - alias byUTF = byUTF!UC; - else: - - auto ref byUTF(R)(R r) - if (isAutodecodableString!R && isInputRange!R && isSomeChar!(ElementEncodingType!R)) - { - return byUTF(r.byCodeUnit()); - } - - auto ref byUTF(R)(R r) - if (!isAutodecodableString!R && isInputRange!R && isSomeChar!(ElementEncodingType!R)) - { - static if (is(immutable ElementEncodingType!R == immutable RC, RC) && is(RC == C)) - { - return r.byCodeUnit(); - } - else static if (is(C == dchar)) - { - static struct Result - { - enum Empty = uint.max; // range is empty or just constructed - - this(return scope R r) - { - this.r = r; - } - - this(return scope R r, uint buff) - { - this.r = r; - this.buff = buff; - } - - static if (isBidirectionalRange!R) - { - this(return scope R r, uint frontBuff, uint backBuff) - { - this.r = r; - this.buff = frontBuff; - this.backBuff = backBuff; - } - } - - @property bool empty() - { - static if (isBidirectionalRange!R) - return buff == Empty && backBuff == Empty && r.empty; - else - return buff == Empty && r.empty; - } - - @property dchar front() scope // 'scope' required by call to decodeFront() below - { - if (buff == Empty) - { - auto c = r.front; - - static if (is(RC == wchar)) - enum firstMulti = 0xD800; // First high surrogate. - else - enum firstMulti = 0x80; // First non-ASCII. - if (c < firstMulti) - { - r.popFront; - buff = cast(dchar) c; - } - else - { - buff = () @trusted { return decodeFront!(useReplacementDchar)(r); }(); - } - } - return cast(dchar) buff; - } - - void popFront() - { - if (buff == Empty) - front(); - buff = Empty; - } - - static if (isForwardRange!R) - { - @property auto save() - { - static if (isBidirectionalRange!R) - { - return Result(r.save, buff, backBuff); - } - else - { - return Result(r.save, buff); - } - } - } - - static if (isBidirectionalRange!R) - { - @property dchar back() scope // 'scope' required by call to decodeBack() below - { - if (backBuff != Empty) - return cast(dchar) backBuff; - - auto c = r.back; - static if (is(RC == wchar)) - enum firstMulti = 0xD800; // First high surrogate. - else - enum firstMulti = 0x80; // First non-ASCII. - if (c < firstMulti) - { - r.popBack; - backBuff = cast(dchar) c; - } - else - { - backBuff = () @trusted { return decodeBack!useReplacementDchar(r); }(); - } - return cast(dchar) backBuff; - - } - - void popBack() - { - if (backBuff == Empty) - back(); - backBuff = Empty; - } - } - - private: - - R r; - uint buff = Empty; // one character lookahead buffer - static if (isBidirectionalRange!R) - uint backBuff = Empty; - } - - return Result(r); - } - else - { - static struct Result - { - this(return scope R r) - { - this.r = r; - } - - this(return scope R r, ushort pos, ushort fill, C[4 / C.sizeof] buf) - { - this.r = r; - this.pos = pos; - this.fill = fill; - this.buf = buf; - } - - static if (isBidirectionalRange!R) - { - this(return scope R r, ushort frontPos, ushort frontFill, - ushort backPos, ushort backFill, C[4 / C.sizeof] buf) - { - this.r = r; - this.pos = frontPos; - this.fill = frontFill; - this.backPos = backPos; - this.backFill = backFill; - this.buf = buf; - } - } - - @property bool empty() - { - static if (isBidirectionalRange!R) - return pos == fill && backPos == backFill && r.empty; - else - return pos == fill && r.empty; - } - - @property auto front() scope // 'scope' required by call to decodeFront() below - { - if (pos == fill) - { - pos = 0; - auto c = r.front; - - static if (C.sizeof >= 2 && RC.sizeof >= 2) - enum firstMulti = 0xD800; // First high surrogate. - else - enum firstMulti = 0x80; // First non-ASCII. - if (c < firstMulti) - { - fill = 1; - r.popFront; - buf[pos] = cast(C) c; - } - else - { - static if (is(RC == dchar)) - { - r.popFront; - dchar dc = c; - } - else - dchar dc = () @trusted { return decodeFront!(useReplacementDchar)(r); }(); - fill = cast(ushort) encode!(useReplacementDchar)(buf, dc); - } - } - return buf[pos]; - } - - void popFront() - { - if (pos == fill) - front; - ++pos; - } - - static if (isForwardRange!R) - { - @property auto save() - { - static if (isBidirectionalRange!R) - { - return Result(r.save, pos, fill, backPos, backFill, buf); - } - else - { - return Result(r.save, pos, fill, buf); - } - } - } - - static if (isBidirectionalRange!R) - { - @property auto back() scope // 'scope' required by call to decodeBack() below - { - if (backPos != backFill) - return buf[cast(ushort) (backFill - backPos - 1)]; - - backPos = 0; - auto c = r.back; - static if (C.sizeof >= 2 && RC.sizeof >= 2) - enum firstMulti = 0xD800; // First high surrogate. - else - enum firstMulti = 0x80; // First non-ASCII. - if (c < firstMulti) - { - backFill = 1; - r.popBack; - buf[cast(ushort) (backFill - backPos - 1)] = cast(C) c; - } - else - { - static if (is(RC == dchar)) - { - r.popBack; - dchar dc = c; - } - else - dchar dc = () @trusted { return decodeBack!(useReplacementDchar)(r); }(); - backFill = cast(ushort) encode!(useReplacementDchar)(buf, dc); - } - return buf[cast(ushort) (backFill - backPos - 1)]; - } - - void popBack() - { - if (backPos == backFill) - back; - ++backPos; - } - } - - private: - - R r; - ushort pos, fill; - static if (isBidirectionalRange!R) - ushort backPos, backFill; - C[4 / C.sizeof] buf = void; - } - - return Result(r); - } - } -} - -/// -@safe pure nothrow unittest -{ - import std.algorithm.comparison : equal; - - // hellĂś as a range of `char`s, which are UTF-8 - assert("hell\u00F6".byUTF!char().equal(['h', 'e', 'l', 'l', 0xC3, 0xB6])); - - // `wchar`s are able to hold the Ăś in a single element (UTF-16 code unit) - assert("hell\u00F6".byUTF!wchar().equal(['h', 'e', 'l', 'l', 'Ăś'])); - - // 𐐡 is four code units in UTF-8, two in UTF-16, and one in UTF-32 - assert("𐐡".byUTF!char().equal([0xF0, 0x90, 0x90, 0xB7])); - assert("𐐡".byUTF!wchar().equal([0xD801, 0xDC37])); - assert("𐐡".byUTF!dchar().equal([0x00010437])); -} - -/// -@safe unittest -{ - import std.algorithm.comparison : equal; - import std.exception : assertThrown; - - assert("hello\xF0betty".byChar.byUTF!(dchar, UseReplacementDchar.yes).equal("hello\uFFFDetty")); - assertThrown!UTFException("hello\xF0betty".byChar.byUTF!(dchar, UseReplacementDchar.no).equal("hello betty")); -} - -@safe unittest -{ - { - wchar[] s = ['a', 'b', 0x219]; - auto r = s.byUTF!char; - assert(isBidirectionalRange!(typeof(r))); - assert(r.back == 0x99); - r.popBack; - assert(r.back == 0xc8); - r.popBack; - assert(r.back == 'b'); - - } - - { - wchar[] s = ['a', 'b', 0x219]; - auto r = s.byUTF!wchar; - uint i; - assert(isBidirectionalRange!(typeof(r))); - assert(r.back == 0x219); - r.popBack; - assert(r.back == 'b'); - } - - { - wchar[] s = ['a', 'b', 0x219]; - auto r = s.byUTF!dchar; - assert(isBidirectionalRange!(typeof(r))); - assert(r.back == 0x219); - r.popBack; - assert(r.back == 'b'); - } - - { - dchar[] s = ['𐐡', '😁']; - auto r = s.byUTF!wchar; - assert(r.back == 0xde01); - r.popBack; - assert(r.back == 0xd83d); - r.popBack; - assert(r.back == 0xdc37); - r.popBack; - assert(r.back == 0xd801); - } - - { - dchar[] s = ['𐐡', '😁']; - auto r = s.byUTF!char; - char[] res; - while (!r.empty) - { - res ~= r.back; - r.popBack; - } - import std.algorithm.comparison : equal; - assert(res.equal([0x81, 0x98, 0x9f, 0xf0, 0xb7, 0x90, 0x90, 0xf0])); - } - - { - dchar[] res; - auto r = ['a', 'b', 'c', 'd', 'e'].byUTF!dchar; - while (!r.empty) - { - res ~= r.back; - r.popBack; - } - import std.algorithm.comparison : equal; - assert(res.equal(['e', 'd', 'c', 'b', 'a'])); - } - - { - //testing the save() function - wchar[] s = ['Ă','ț']; - - auto rc = s.byUTF!char; - rc.popBack; - auto rcCopy = rc.save; - assert(rc.back == rcCopy.back); - assert(rcCopy.back == 0xc8); - - auto rd = s.byUTF!dchar; - rd.popBack; - auto rdCopy = rd.save; - assert(rd.back == rdCopy.back); - assert(rdCopy.back == 'Ă'); - } -} - -/// -@safe pure nothrow unittest -{ - import std.range.primitives; - wchar[] s = ['ă', 'ĂŽ']; - - auto rc = s.byUTF!char; - static assert(isBidirectionalRange!(typeof(rc))); - assert(rc.back == 0xae); - rc.popBack; - assert(rc.back == 0xc3); - rc.popBack; - assert(rc.back == 0x83); - rc.popBack; - assert(rc.back == 0xc4); - - auto rw = s.byUTF!wchar; - static assert(isBidirectionalRange!(typeof(rw))); - assert(rw.back == 'ĂŽ'); - rw.popBack; - assert(rw.back == 'ă'); - - auto rd = s.byUTF!dchar; - static assert(isBidirectionalRange!(typeof(rd))); - assert(rd.back == 'ĂŽ'); - rd.popBack; - assert(rd.back == 'ă'); -} diff --git a/phobos/std/uuid.d b/phobos/std/uuid.d deleted file mode 100644 index b8cac6e..0000000 --- a/phobos/std/uuid.d +++ /dev/null @@ -1,1758 +0,0 @@ -/** - * A $(LINK2 http://en.wikipedia.org/wiki/Universally_unique_identifier, UUID), or - * $(LINK2 http://en.wikipedia.org/wiki/Universally_unique_identifier, Universally unique identifier), - * is intended to uniquely identify information in a distributed environment - * without significant central coordination. It can be - * used to tag objects with very short lifetimes, or to reliably identify very - * persistent objects across a network. - * -$(SCRIPT inhibitQuickIndex = 1;) - -$(DIVC quickindex, -$(BOOKTABLE , -$(TR $(TH Category) $(TH Functions) -) -$(TR $(TDNW Parsing UUIDs) - $(TD $(MYREF parseUUID) - $(MYREF UUID) - $(MYREF UUIDParsingException) - $(MYREF uuidRegex) - ) - ) -$(TR $(TDNW Generating UUIDs) - $(TD $(MYREF sha1UUID) - $(MYREF randomUUID) - $(MYREF md5UUID) - ) - ) -$(TR $(TDNW Using UUIDs) - $(TD $(MYREF2 UUID.uuidVersion, uuidVersion) - $(MYREF2 UUID.variant, variant) - $(MYREF2 UUID.toString, toString) - $(MYREF2 UUID.data, data) - $(MYREF2 UUID.swap, swap) - $(MYREF2 UUID.opEquals, opEquals) - $(MYREF2 UUID.opCmp, opCmp) - $(MYREF2 UUID.toHash, toHash) - ) - ) -$(TR $(TDNW UUID namespaces) - $(TD $(MYREF dnsNamespace) - $(MYREF urlNamespace) - $(MYREF oidNamespace) - $(MYREF x500Namespace) - ) - ) -) -) - - * UUIDs have many applications. Some examples follow: Databases may use UUIDs to identify - * rows or records in order to ensure that they are unique across different - * databases, or for publication/subscription services. Network messages may be - * identified with a UUID to ensure that different parts of a message are put back together - * again. Distributed computing may use UUIDs to identify a remote procedure call. - * Transactions and classes involved in serialization may be identified by UUIDs. - * Microsoft's component object model (COM) uses UUIDs to distinguish different software - * component interfaces. UUIDs are inserted into documents from Microsoft Office programs. - * UUIDs identify audio or video streams in the Advanced Systems Format (ASF). UUIDs are - * also a basis for OIDs (object identifiers), and URNs (uniform resource name). - * - * An attractive feature of UUIDs when compared to alternatives is their relative small size, - * of 128 bits, or 16 bytes. Another is that the creation of UUIDs does not require - * a centralized authority. - * - * When UUIDs are generated by one of the defined mechanisms, they are either guaranteed - * to be unique, different from all other generated UUIDs (that is, it has never been - * generated before and it will never be generated again), or it is extremely likely - * to be unique (depending on the mechanism). - * - * For efficiency, UUID is implemented as a struct. UUIDs are therefore empty if not explicitly - * initialized. An UUID is empty if $(MYREF3 UUID.empty, empty) is true. Empty UUIDs are equal to - * `UUID.init`, which is a UUID with all 16 bytes set to 0. - * Use UUID's constructors or the UUID generator functions to get an initialized UUID. - * - * This is a port of $(LINK2 http://www.boost.org/doc/libs/1_42_0/libs/uuid/uuid.html, - * boost.uuid) from the Boost project with some minor additions and API - * changes for a more D-like API. - * - * Standards: - * $(LINK2 http://www.ietf.org/rfc/rfc4122.txt, RFC 4122) - * - * See_Also: - * $(LINK http://en.wikipedia.org/wiki/Universally_unique_identifier) - * - * Copyright: Copyright Johannes Pfau 2011 - . - * License: $(HTTP www.boost.org/LICENSE_1_0.txt, Boost License 1.0). - * Authors: Johannes Pfau - * Source: $(PHOBOSSRC std/uuid.d) - * - * Macros: - * MYREF2 = <a href="#$2">$(TT $1)</a>  - * MYREF3 = <a href="#$2">`$1`</a> - */ -/* Copyright Johannes Pfau 2011 - 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.uuid; - -/// -@safe unittest -{ - import std.uuid; - - UUID[] ids; - ids ~= randomUUID(); - ids ~= md5UUID("test.name.123"); - ids ~= sha1UUID("test.name.123"); - - foreach (entry; ids) - { - assert(entry.variant == UUID.Variant.rfc4122); - } - assert(ids[0].uuidVersion == UUID.Version.randomNumberBased); - assert(ids[1].toString() == "22390768-cced-325f-8f0f-cfeaa19d0ccd"); - assert(ids[1].data == [34, 57, 7, 104, 204, 237, 50, 95, 143, 15, 207, - 234, 161, 157, 12, 205]); - UUID id; - assert(id.empty); -} - -import std.range.primitives; -import std.traits; - -/** - * - */ -public struct UUID -{ - import std.meta : AliasSeq, allSatisfy; - - private: - alias skipSeq = AliasSeq!(8, 13, 18, 23); - alias byteSeq = AliasSeq!(0,2,4,6,9,11,14,16,19,21,24,26,28,30,32,34); - - @safe pure nothrow @nogc Char toChar(Char)(size_t i) const - { - if (i <= 9) - return cast(Char)('0' + i); - else - return cast(Char)('a' + (i-10)); - } - - @safe pure nothrow unittest - { - assert(UUID(cast(ubyte[16])[138, 179, 6, 14, 44, 186, 79, 35, 183, 76, 181, 45, - 179, 189, 251, 70]).toString() == "8ab3060e-2cba-4f23-b74c-b52db3bdfb46"); - } - - // Reinterpret the UUID as an array of some other primitive. - @trusted ref T[16 / T.sizeof] asArrayOf(T)() return - if (isIntegral!T) - { - return *cast(typeof(return)*)&data; - } - - public: - /** - * RFC 4122 defines different internal data layouts for UUIDs. These are - * the UUID formats supported by this module. It's - * possible to read, compare and use all these Variants, but - * UUIDs generated by this module will always be in rfc4122 format. - * - * Note: Do not confuse this with $(REF _Variant, std,_variant). - */ - enum Variant - { - ncs, /// NCS backward compatibility - rfc4122, /// Defined in RFC 4122 document - microsoft, /// Microsoft Corporation backward compatibility - future ///Reserved for future use - } - - /** - * RFC 4122 defines different UUID versions. The version shows - * how a UUID was generated, e.g. a version 4 UUID was generated - * from a random number, a version 3 UUID from an MD5 hash of a name. - * - * Note: - * All of these UUID versions can be read and processed by - * `std.uuid`, but only version 3, 4 and 5 UUIDs can be generated. - */ - enum Version - { - ///Unknown version - unknown = -1, - ///Version 1 - timeBased = 1, - ///Version 2 - dceSecurity = 2, - ///Version 3 (Name based + MD5) - nameBasedMD5 = 3, - ///Version 4 (Random) - randomNumberBased = 4, - ///Version 5 (Name based + SHA-1) - nameBasedSHA1 = 5 - } - - union - { - /** - * It is sometimes useful to get or set the 16 bytes of a UUID - * directly. - * - * Note: - * UUID uses a 16-ubyte representation for the UUID data. - * RFC 4122 defines a UUID as a special structure in big-endian - * format. These 16-ubytes always equal the big-endian structure - * defined in RFC 4122. - * - * Example: - * ----------------------------------------------- - * auto rawData = uuid.data; //get data - * rawData[0] = 1; //modify - * uuid.data = rawData; //set data - * uuid.data[1] = 2; //modify directly - * ----------------------------------------------- - */ - ubyte[16] data; - private ulong[2] ulongs; - static if (size_t.sizeof == 4) - private uint[4] uints; - } - - /* - * We could use a union here to also provide access to the - * fields specified in RFC 4122, but as we never have to access - * those (only necessary for version 1 (and maybe 2) UUIDs), - * that is not needed right now. - */ - - @safe pure unittest - { - UUID tmp; - tmp.data = cast(ubyte[16])[0,1,2,3,4,5,6,7,8,9,10,11,12, - 13,14,15]; - assert(tmp.data == cast(ubyte[16])[0,1,2,3,4,5,6,7,8,9,10,11, - 12,13,14,15]); - tmp.data[2] = 3; - assert(tmp.data == cast(ubyte[16])[0,1,3,3,4,5,6,7,8,9,10,11, - 12,13,14,15]); - - auto tmp2 = cast(immutable UUID) tmp; - assert(tmp2.data == cast(ubyte[16])[0,1,3,3,4,5,6,7,8,9,10,11, - 12,13,14,15]); - } - - /** - * Construct a UUID struct from the 16 byte representation - * of a UUID. - */ - @safe pure nothrow @nogc this(ref const scope ubyte[16] uuidData) - { - data = uuidData; - } - /// ditto - @safe pure nothrow @nogc this(const ubyte[16] uuidData) - { - data = uuidData; - } - - /// - @safe pure unittest - { - enum ubyte[16] data = [0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15]; - auto uuid = UUID(data); - enum ctfe = UUID(data); - assert(uuid.data == data); - assert(ctfe.data == data); - } - - /** - * Construct a UUID struct from the 16 byte representation - * of a UUID. Variadic constructor to allow a simpler syntax, see examples. - * You need to pass exactly 16 ubytes. - */ - @safe pure this(T...)(T uuidData) - if (uuidData.length == 16 && allSatisfy!(isIntegral, T)) - { - import std.conv : to; - - foreach (idx, it; uuidData) - { - this.data[idx] = to!ubyte(it); - } - } - - /// - @safe unittest - { - auto tmp = UUID(0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15); - assert(tmp.data == cast(ubyte[16])[0,1,2,3,4,5,6,7,8,9,10,11, - 12,13,14,15]); - } - - @safe unittest - { - UUID tmp = UUID(0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15); - assert(tmp.data == cast(ubyte[16])[0,1,2,3,4,5,6,7,8,9,10,11, - 12,13,14,15]); - - enum UUID ctfeID = UUID(0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15); - assert(ctfeID == tmp); - - //Too few arguments - assert(!__traits(compiles, typeof(UUID(0,1,2,3,4,5,6,7,8,9,10,11,12,13,14)))); - - //Too many arguments - assert(!__traits(compiles, typeof(UUID(0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,1)))); - } - - /** - * <a name="UUID(string)"></a> - * Parse a UUID from its canonical string form. An UUID in its - * canonical form looks like this: 8ab3060e-2cba-4f23-b74c-b52db3bdfb46 - * - * Throws: - * $(LREF UUIDParsingException) if the input is invalid - * - * CTFE: - * This function is supported in CTFE code. Note that error messages - * caused by a malformed UUID parsed at compile time can be cryptic, - * but errors are detected and reported at - * compile time. - * - * Note: - * This is a strict parser. It only accepts the pattern above. - * It doesn't support any leading or trailing characters. It only - * accepts characters used for hex numbers and the string must have - * hyphens exactly like above. - * - * For a less strict parser, see $(LREF parseUUID) - */ - this(T)(in T[] uuid) if (isSomeChar!T) - { - import std.conv : to, parse; - if (uuid.length < 36) - { - throw new UUIDParsingException(to!string(uuid), 0, - UUIDParsingException.Reason.tooLittle, "Insufficient Input"); - } - if (uuid.length > 36) - { - throw new UUIDParsingException(to!string(uuid), 35, UUIDParsingException.Reason.tooMuch, - "Input is too long, need exactly 36 characters"); - } - static immutable skipInd = [skipSeq]; - foreach (pos; skipInd) - if (uuid[pos] != '-') - throw new UUIDParsingException(to!string(uuid), pos, - UUIDParsingException.Reason.invalidChar, "Expected '-'"); - - ubyte[16] data2; //ctfe bug - uint pos = void; - - foreach (i, p; byteSeq) - { - enum uint s = 'a'-10-'0'; - uint h = uuid[p]; - uint l = uuid[p+1]; - pos = p; - if (h < '0') goto Lerr; - if (l < '0') goto Lerr; - if (h > '9') - { - h |= 0x20; //poorman's tolower - if (h < 'a') goto Lerr; - if (h > 'f') goto Lerr; - h -= s; - } - if (l > '9') - { - l |= 0x20; //poorman's tolower - if (l < 'a') goto Lerr; - if (l > 'f') goto Lerr; - l -= s; - } - h -= '0'; - l -= '0'; - - data2[i] = cast(ubyte)((h << 4) ^ l); - } - this.data = data2; - return; - - Lerr: throw new UUIDParsingException(to!string(uuid), pos, - UUIDParsingException.Reason.invalidChar, "Couldn't parse ubyte"); - } - - /// - @safe pure unittest - { - auto id = UUID("8AB3060E-2cba-4f23-b74c-b52db3bdfb46"); - assert(id.data == [138, 179, 6, 14, 44, 186, 79, 35, 183, 76, - 181, 45, 179, 189, 251, 70]); - assert(id.toString() == "8ab3060e-2cba-4f23-b74c-b52db3bdfb46"); - - //Can also be used in CTFE, for example as UUID literals: - enum ctfeID = UUID("8ab3060e-2cba-4f23-b74c-b52db3bdfb46"); - //here parsing is done at compile time, no runtime overhead! - } - - @safe pure unittest - { - import std.conv : to; - import std.exception; - import std.meta : AliasSeq; - - static foreach (S; AliasSeq!(char[], const(char)[], immutable(char)[], - wchar[], const(wchar)[], immutable(wchar)[], - dchar[], const(dchar)[], immutable(dchar)[], - immutable(char[]), immutable(wchar[]), immutable(dchar[]))) - {{ - //Test valid, working cases - assert(UUID(to!S("00000000-0000-0000-0000-000000000000")).empty); - - auto id = UUID(to!S("8AB3060E-2cba-4f23-b74c-b52db3bdfb46")); - assert(id.data == [138, 179, 6, 14, 44, 186, 79, 35, 183, 76, - 181, 45, 179, 189, 251, 70]); - assert(id.toString() == "8ab3060e-2cba-4f23-b74c-b52db3bdfb46"); - - enum UUID ctfe = UUID(to!S("8ab3060e-2cba-4f23-b74c-b52db3bdfb46")); - assert(ctfe == id); - - assert(UUID(to!S("5668122d-9df0-49a4-ad0b-b9b0a57f886a")).data - == [86, 104, 18, 45, 157, 240, 73, 164, 173, 11, 185, 176, 165, 127, 136, 106]); - - //Test too short UUIDS - auto except = collectException!UUIDParsingException( - UUID(to!S("5668122d-9df0-49a4-ad0b-b9b0a57f886"))); - assert(except && except.reason == UUIDParsingException.Reason.tooLittle); - - //Test too long UUIDS - except = collectException!UUIDParsingException( - UUID(to!S("5668122d-9df0-49a4-ad0b-b9b0a57f886aa"))); - assert(except && except.reason == UUIDParsingException.Reason.tooMuch); - - //Test dashes - except = collectException!UUIDParsingException( - UUID(to!S("8ab3060e2cba-4f23-b74c-b52db3bdfb-46"))); - assert(except && except.reason == UUIDParsingException.Reason.invalidChar); - - //Test dashes 2 - except = collectException!UUIDParsingException( - UUID(to!S("8ab3-060e2cba-4f23-b74c-b52db3bdfb46"))); - assert(except && except.reason == UUIDParsingException.Reason.invalidChar); - - //Test invalid characters - //make sure 36 characters in total or we'll get a 'tooMuch' reason - except = collectException!UUIDParsingException( - UUID(to!S("{8ab3060e-2cba-4f23-b74c-b52db3bdf6}"))); - assert(except && except.reason == UUIDParsingException.Reason.invalidChar); - - //Boost test - assert(UUID(to!S("01234567-89ab-cdef-0123-456789ABCDEF")) - == UUID(cast(ubyte[16])[0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef,0x01, - 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef])); - } - }} - - /** - * Returns true if and only if the UUID is equal - * to {00000000-0000-0000-0000-000000000000} - */ - @trusted pure nothrow @nogc @property bool empty() const - { - if (__ctfe) - return data == (ubyte[16]).init; - - auto p = cast(const(size_t*))data.ptr; - static if (size_t.sizeof == 4) - return p[0] == 0 && p[1] == 0 && p[2] == 0 && p[3] == 0; - else static if (size_t.sizeof == 8) - return p[0] == 0 && p[1] == 0; - else - static assert(false, "nonsense, it's not 32 or 64 bit"); - } - - /// - @safe pure unittest - { - UUID id; - assert(id.empty); - id = UUID("00000000-0000-0000-0000-000000000001"); - assert(!id.empty); - } - - @safe pure unittest - { - ubyte[16] getData(size_t i) - { - ubyte[16] data; - data[i] = 1; - return data; - } - - for (size_t i = 0; i < 16; i++) - { - assert(!UUID(getData(i)).empty); - } - - enum ctfeEmpty = UUID.init.empty; - assert(ctfeEmpty); - - bool ctfeTest() - { - for (size_t i = 0; i < 16; i++) - { - auto ctfeEmpty2 = UUID(getData(i)).empty; - assert(!ctfeEmpty2); - } - return true; - } - enum res = ctfeTest(); - } - - /** - * RFC 4122 defines different internal data layouts for UUIDs. - * Returns the format used by this UUID. - * - * Note: Do not confuse this with $(REF _Variant, std,_variant). - * The type of this property is $(MYREF3 std.uuid.UUID.Variant, _Variant). - * - * See_Also: - * $(MYREF3 UUID.Variant, Variant) - */ - @safe pure nothrow @nogc @property Variant variant() const - { - //variant is stored in octet 7 - //which is index 8, since indexes count backwards - immutable octet7 = data[8]; //octet 7 is array index 8 - - if ((octet7 & 0x80) == 0x00) //0b0xxxxxxx - return Variant.ncs; - else if ((octet7 & 0xC0) == 0x80) //0b10xxxxxx - return Variant.rfc4122; - else if ((octet7 & 0xE0) == 0xC0) //0b110xxxxx - return Variant.microsoft; - else - { - //assert((octet7 & 0xE0) == 0xE0, "Unknown UUID variant!") //0b111xxxx - return Variant.future; - } - } - - /// - @safe pure unittest - { - assert(UUID("8ab3060e-2cba-4f23-b74c-b52db3bdfb46").variant - == UUID.Variant.rfc4122); - } - @system pure unittest - { - // @system due to Variant - Variant[ubyte] tests = cast(Variant[ubyte])[0x00 : Variant.ncs, - 0x10 : Variant.ncs, - 0x20 : Variant.ncs, - 0x30 : Variant.ncs, - 0x40 : Variant.ncs, - 0x50 : Variant.ncs, - 0x60 : Variant.ncs, - 0x70 : Variant.ncs, - 0x80 : Variant.rfc4122, - 0x90 : Variant.rfc4122, - 0xa0 : Variant.rfc4122, - 0xb0 : Variant.rfc4122, - 0xc0 : Variant.microsoft, - 0xd0 : Variant.microsoft, - 0xe0 : Variant.future, - 0xf0 : Variant.future]; - foreach (key, value; tests) - { - UUID u; - u.data[8] = key; - assert(u.variant == value); - } - } - - /** - * RFC 4122 defines different UUID versions. The version shows - * how a UUID was generated, e.g. a version 4 UUID was generated - * from a random number, a version 3 UUID from an MD5 hash of a name. - * Returns the version used by this UUID. - * - * See_Also: - * $(MYREF3 UUID.Version, Version) - */ - @safe pure nothrow @nogc @property Version uuidVersion() const - { - //version is stored in octet 9 - //which is index 6, since indexes count backwards - immutable octet9 = data[6]; - if ((octet9 & 0xF0) == 0x10) - return Version.timeBased; - else if ((octet9 & 0xF0) == 0x20) - return Version.dceSecurity; - else if ((octet9 & 0xF0) == 0x30) - return Version.nameBasedMD5; - else if ((octet9 & 0xF0) == 0x40) - return Version.randomNumberBased; - else if ((octet9 & 0xF0) == 0x50) - return Version.nameBasedSHA1; - else - return Version.unknown; - } - - /// - @safe unittest - { - assert(UUID("8ab3060e-2cba-4f23-b74c-b52db3bdfb46").uuidVersion - == UUID.Version.randomNumberBased); - } - @system unittest - { - // @system due to cast - Version[ubyte] tests = cast(Version[ubyte]) [ - 0x00 : UUID.Version.unknown, - 0x10 : UUID.Version.timeBased, - 0x20 : UUID.Version.dceSecurity, - 0x30 : UUID.Version.nameBasedMD5, - 0x40 : UUID.Version.randomNumberBased, - 0x50 : UUID.Version.nameBasedSHA1, - 0x60 : UUID.Version.unknown, - 0x70 : UUID.Version.unknown, - 0x80 : UUID.Version.unknown, - 0x90 : UUID.Version.unknown, - 0xa0 : UUID.Version.unknown, - 0xb0 : UUID.Version.unknown, - 0xc0 : UUID.Version.unknown, - 0xd0 : UUID.Version.unknown, - 0xe0 : UUID.Version.unknown, - 0xf0 : UUID.Version.unknown]; - foreach (key, value; tests) - { - UUID u; - u.data[6] = key; - assert(u.uuidVersion == value); - } - } - - /** - * Swap the data of this UUID with the data of rhs. - */ - @safe pure nothrow @nogc void swap(ref UUID rhs) - { - immutable bck = data; - data = rhs.data; - rhs.data = bck; - } - - /// - @safe unittest - { - immutable ubyte[16] data = [0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15]; - UUID u1; - UUID u2 = UUID(data); - u1.swap(u2); - - assert(u1 == UUID(data)); - assert(u2 == UUID.init); - } - - /** - * All of the standard numeric operators are defined for - * the UUID struct. - */ - @safe pure nothrow @nogc bool opEquals(const UUID s) const - { - return ulongs[0] == s.ulongs[0] && ulongs[1] == s.ulongs[1]; - } - - /// - @safe pure unittest - { - //compare UUIDs - assert(UUID("00000000-0000-0000-0000-000000000000") == UUID.init); - - //UUIDs in associative arrays: - int[UUID] test = [UUID("8a94f585-d180-44f7-8929-6fca0189c7d0") : 1, - UUID("7c351fd4-b860-4ee3-bbdc-7f79f3dfb00a") : 2, - UUID("9ac0a4e5-10ee-493a-86fc-d29eeb82ecc1") : 3]; - - assert(test[UUID("9ac0a4e5-10ee-493a-86fc-d29eeb82ecc1")] == 3); - - //UUIDS can be sorted: - import std.algorithm; - UUID[] ids = [UUID("8a94f585-d180-44f7-8929-6fca0189c7d0"), - UUID("7c351fd4-b860-4ee3-bbdc-7f79f3dfb00a"), - UUID("9ac0a4e5-10ee-493a-86fc-d29eeb82ecc1")]; - sort(ids); - } - - /** - * ditto - */ - @safe pure nothrow @nogc bool opEquals(ref const scope UUID s) const - { - return ulongs[0] == s.ulongs[0] && ulongs[1] == s.ulongs[1]; - } - - /** - * ditto - */ - @safe pure nothrow @nogc int opCmp(const UUID s) const - { - import std.algorithm.comparison : cmp; - return cmp(this.data[], s.data[]); - } - - /** - * ditto - */ - @safe pure nothrow @nogc int opCmp(ref const scope UUID s) const - { - import std.algorithm.comparison : cmp; - return cmp(this.data[], s.data[]); - } - - /** - * ditto - */ - @safe pure nothrow @nogc UUID opAssign(const UUID s) - { - ulongs[0] = s.ulongs[0]; - ulongs[1] = s.ulongs[1]; - return this; - } - - /** - * ditto - */ - @safe pure nothrow @nogc UUID opAssign(ref const scope UUID s) - { - ulongs[0] = s.ulongs[0]; - ulongs[1] = s.ulongs[1]; - return this; - } - - /** - * ditto - */ - //MurmurHash2 - @safe pure nothrow @nogc size_t toHash() const - { - static if (size_t.sizeof == 4) - { - enum uint m = 0x5bd1e995; - enum uint n = 16; - enum uint r = 24; - - uint h = n; - - uint k = uints[0]; - k *= m; - k ^= k >> r; - k *= m; - - h ^= k; - h *= m; - - k = uints[1]; - k *= m; - k ^= k >> r; - k *= m; - - h ^= k; - h *= m; - - k = uints[2]; - k *= m; - k ^= k >> r; - k *= m; - - h ^= k; - h *= m; - - k = uints[3]; - k *= m; - k ^= k >> r; - k *= m; - - h ^= k; - h *= m; - } - else - { - enum ulong m = 0xc6a4a7935bd1e995UL; - enum ulong n = m * 16; - enum uint r = 47; - - ulong h = n; - - ulong k = ulongs[0]; - k *= m; - k ^= k >> r; - k *= m; - - h ^= k; - h *= m; - - k = ulongs[1]; - k *= m; - k ^= k >> r; - k *= m; - - h ^= k; - h *= m; - } - return h; - } - @safe unittest - { - assert(UUID("00000000-0000-0000-0000-000000000000") == UUID.init); - int[UUID] test = [UUID("8a94f585-d180-44f7-8929-6fca0189c7d0") : 1, - UUID("7c351fd4-b860-4ee3-bbdc-7f79f3dfb00a") : 2, - UUID("9ac0a4e5-10ee-493a-86fc-d29eeb82ecc1") : 3]; - - assert(test[UUID("9ac0a4e5-10ee-493a-86fc-d29eeb82ecc1")] == 3); - - import std.algorithm; - UUID[] ids = [UUID("8a94f585-d180-44f7-8929-6fca0189c7d0"), - UUID("7c351fd4-b860-4ee3-bbdc-7f79f3dfb00a"), - UUID("9ac0a4e5-10ee-493a-86fc-d29eeb82ecc1")]; - sort(ids); - auto id2 = ids.dup; - - ids = [UUID("7c351fd4-b860-4ee3-bbdc-7f79f3dfb00a"), - UUID("8a94f585-d180-44f7-8929-6fca0189c7d0"), - UUID("9ac0a4e5-10ee-493a-86fc-d29eeb82ecc1")]; - sort(ids); - assert(ids == id2); - - //test comparsion - UUID u1; - UUID u2 = UUID(cast(ubyte[16])[1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0]); - UUID u3 = UUID(cast(ubyte[16])[255,255,255,255,255,255,255,255,255, - 255,255,255,255,255,255,255]); - - assert(u1 == u1); - - assert(u1 != u2); - - assert(u1 < u2); - assert(u2 < u3); - - assert(u1 <= u1); - assert(u1 <= u2); - assert(u2 <= u3); - - assert(u2 >= u2); - assert(u3 >= u2); - - assert(u3 >= u3); - assert(u2 >= u1); - assert(u3 >= u1); - - // test hash - assert(u1.toHash() != u2.toHash()); - assert(u2.toHash() != u3.toHash()); - assert(u3.toHash() != u1.toHash()); - } - - - /** - * Write the UUID into `sink` as an ASCII string in the canonical form, - * which is 36 characters in the form "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx" - * Params: - * sink = OutputRange or writeable array at least 36 entries long - */ - void toString(Writer)(scope Writer sink) const - { - char[36] result = void; - foreach (pos; skipSeq) - result[pos] = '-'; - foreach (i, pos; byteSeq) - { - const uint entry = this.data[i]; - const uint hi = entry >> 4; - result[pos ] = toChar!char(hi); - const uint lo = (entry) & 0x0F; - result[pos+1] = toChar!char(lo); - } - static if (!__traits(compiles, put(sink, result[])) || isSomeString!Writer) - { - foreach (i, c; result) - sink[i] = cast(typeof(sink[i]))c; - } - else - { - put(sink, result[]); - } - } - - /** - * Return the UUID as a string in the canonical form. - */ - @trusted pure nothrow string toString() const - { - import std.exception : assumeUnique; - auto result = new char[36]; - toString(result); - return result.assumeUnique; - } - - /// - @safe pure unittest - { - immutable str = "8ab3060e-2cba-4f23-b74c-b52db3bdfb46"; - auto id = UUID(str); - assert(id.toString() == str); - } - - @safe pure nothrow @nogc unittest - { - import std.meta : AliasSeq; - static foreach (Char; AliasSeq!(char, wchar, dchar)) - {{ - alias String = immutable(Char)[]; - //CTFE - enum String s = "8ab3060e-2cba-4f23-b74c-b52db3bdfb46"; - enum id = UUID(s); - static if (is(Char == char)) - { - enum p = id.toString(); - static assert(s == p); - } - //nogc - Char[36] str; - id.toString(str[]); - assert(str == s); - }} - } - - @system pure nothrow @nogc unittest - { - // @system due to cast - import std.encoding : Char = AsciiChar; - enum utfstr = "8ab3060e-2cba-4f23-b74c-b52db3bdfb46"; - alias String = immutable(Char)[]; - enum String s = cast(String) utfstr; - enum id = UUID(utfstr); - //nogc - Char[36] str; - id.toString(str[]); - assert(str == s); - } - - @safe unittest - { - auto u1 = UUID(cast(ubyte[16])[138, 179, 6, 14, 44, 186, 79, - 35, 183, 76, 181, 45, 179, 189, 251, 70]); - assert(u1.toString() == "8ab3060e-2cba-4f23-b74c-b52db3bdfb46"); - u1 = UUID("8ab3060e-2cba-4f23-b74c-b52db3bdfb46"); - assert(u1.toString() == "8ab3060e-2cba-4f23-b74c-b52db3bdfb46"); - - char[] buf; - void sink(scope const(char)[] data) - { - buf ~= data; - } - u1.toString(&sink); - assert(buf == "8ab3060e-2cba-4f23-b74c-b52db3bdfb46"); - } -} - -/// -@safe unittest -{ - UUID id; - assert(id.empty); - - id = randomUUID; - assert(!id.empty); - - id = UUID(cast(ubyte[16]) [138, 179, 6, 14, 44, 186, 79, - 35, 183, 76, 181, 45, 179, 189, 251, 70]); - assert(id.toString() == "8ab3060e-2cba-4f23-b74c-b52db3bdfb46"); -} - -/** - * This function generates a name based (Version 3) UUID from a namespace UUID and a name. - * If no namespace UUID was passed, the empty UUID `UUID.init` is used. - * - * Note: - * The default namespaces ($(LREF dnsNamespace), ...) defined by - * this module should be used when appropriate. - * - * RFC 4122 recommends to use Version 5 UUIDs (SHA-1) instead of Version 3 - * UUIDs (MD5) for new applications. - * - * CTFE: - * CTFE is not supported. - * - * Note: - * RFC 4122 isn't very clear on how UUIDs should be generated from names. - * It is possible that different implementations return different UUIDs - * for the same input, so be warned. The implementation for UTF-8 strings - * and byte arrays used by `std.uuid` is compatible with Boost's implementation. - * `std.uuid` guarantees that the same input to this function will generate - * the same output at any time, on any system (this especially means endianness - * doesn't matter). - * - * Note: - * This function does not provide overloads for wstring and dstring, as - * there's no clear answer on how that should be implemented. It could be - * argued, that string, wstring and dstring input should have the same output, - * but that wouldn't be compatible with Boost, which generates different output - * for strings and wstrings. It's always possible to pass wstrings and dstrings - * by using the ubyte[] function overload (but be aware of endianness issues!). - */ -@safe pure nothrow @nogc UUID md5UUID(const(char[]) name, const UUID namespace = UUID.init) -{ - return md5UUID(cast(const(ubyte[]))name, namespace); -} - -/// ditto -@safe pure nothrow @nogc UUID md5UUID(const(ubyte[]) data, const UUID namespace = UUID.init) -{ - import std.digest.md : MD5; - - MD5 hash; - hash.start(); - - /* - * NOTE: RFC 4122 says namespace should be converted to big-endian. - * We always keep the UUID data in big-endian representation, so - * that's fine - */ - hash.put(namespace.data[]); - hash.put(data[]); - - UUID u; - u.data = hash.finish(); - - //set variant - //must be 0b10xxxxxx - u.data[8] &= 0b10111111; - u.data[8] |= 0b10000000; - - //set version - //must be 0b0011xxxx - u.data[6] &= 0b00111111; - u.data[6] |= 0b00110000; - - return u; -} - -/// -@safe unittest -{ - //Use default UUID.init namespace - auto simpleID = md5UUID("test.uuid.any.string"); - - //use a name-based id as namespace - auto namespace = md5UUID("my.app"); - auto id = md5UUID("some-description", namespace); -} - -@safe pure unittest -{ - auto simpleID = md5UUID("test.uuid.any.string"); - assert(simpleID.data == cast(ubyte[16])[126, 206, 86, 72, 29, 233, 62, 213, 178, 139, 198, 136, - 188, 135, 153, 123]); - auto namespace = md5UUID("my.app"); - auto id = md5UUID("some-description", namespace); - assert(id.data == cast(ubyte[16])[166, 138, 167, 79, 48, 219, 55, 166, 170, 103, 39, 73, 216, - 150, 144, 164]); - - auto constTest = md5UUID(cast(const(char)[])"test"); - constTest = md5UUID(cast(const(char[]))"test"); - - char[] mutable = "test".dup; - id = md5UUID(mutable, namespace); - - const(ubyte)[] data = cast(ubyte[])[0,1,2,244,165,222]; - id = md5UUID(data); - assert(id.data == cast(ubyte[16])[16, 50, 29, 247, 243, 185, 61, 178, 157, 100, 253, 236, 73, - 76, 51, 47]); - - assert(id.variant == UUID.Variant.rfc4122); - assert(id.uuidVersion == UUID.Version.nameBasedMD5); - - auto correct = UUID("3d813cbb-47fb-32ba-91df-831e1593ac29"); - - auto u = md5UUID("www.widgets.com", dnsNamespace); - //enum ctfeId = md5UUID("www.widgets.com", dnsNamespace); - //assert(ctfeId == u); - assert(u == correct); - assert(u.variant == UUID.Variant.rfc4122); - assert(u.uuidVersion == UUID.Version.nameBasedMD5); -} - - /** - * This function generates a name based (Version 5) UUID from a namespace - * UUID and a name. - * If no namespace UUID was passed, the empty UUID `UUID.init` is used. - * - * Note: - * The default namespaces ($(LREF dnsNamespace), ...) defined by - * this module should be used when appropriate. - * - * CTFE: - * CTFE is not supported. - * - * Note: - * RFC 4122 isn't very clear on how UUIDs should be generated from names. - * It is possible that different implementations return different UUIDs - * for the same input, so be warned. The implementation for UTF-8 strings - * and byte arrays used by `std.uuid` is compatible with Boost's implementation. - * `std.uuid` guarantees that the same input to this function will generate - * the same output at any time, on any system (this especially means endianness - * doesn't matter). - * - * Note: - * This function does not provide overloads for wstring and dstring, as - * there's no clear answer on how that should be implemented. It could be - * argued, that string, wstring and dstring input should have the same output, - * but that wouldn't be compatible with Boost, which generates different output - * for strings and wstrings. It's always possible to pass wstrings and dstrings - * by using the ubyte[] function overload (but be aware of endianness issues!). - */ -@safe pure nothrow @nogc UUID sha1UUID(scope const(char)[] name, scope const UUID namespace = UUID.init) -{ - return sha1UUID(cast(const(ubyte[]))name, namespace); -} - -/// ditto -@safe pure nothrow @nogc UUID sha1UUID(scope const(ubyte)[] data, scope const UUID namespace = UUID.init) -{ - import std.digest.sha : SHA1; - - SHA1 sha; - sha.start(); - - /* - * NOTE: RFC 4122 says namespace should be converted to big-endian. - * We always keep the UUID data in big-endian representation, so - * that's fine - */ - sha.put(namespace.data[]); - sha.put(data[]); - - auto hash = sha.finish(); - auto u = UUID(); - u.data[] = hash[0 .. 16]; - - //set variant - //must be 0b10xxxxxx - u.data[8] &= 0b10111111; - u.data[8] |= 0b10000000; - - //set version - //must be 0b0101xxxx - u.data[6] &= 0b01011111; - u.data[6] |= 0b01010000; - - return u; -} - -/// -@safe unittest -{ - //Use default UUID.init namespace - auto simpleID = sha1UUID("test.uuid.any.string"); - - //use a name-based id as namespace - auto namespace = sha1UUID("my.app"); - auto id = sha1UUID("some-description", namespace); -} - -@safe pure unittest -{ - auto simpleID = sha1UUID("test.uuid.any.string"); - assert(simpleID.data == cast(ubyte[16])[16, 209, 239, 61, 99, 12, 94, 70, 159, 79, 255, 250, - 131, 79, 14, 147]); - auto namespace = sha1UUID("my.app"); - auto id = sha1UUID("some-description", namespace); - assert(id.data == cast(ubyte[16])[225, 94, 195, 219, 126, 75, 83, 71, 157, 52, 247, 43, 238, 248, - 148, 46]); - - auto constTest = sha1UUID(cast(const(char)[])"test"); - constTest = sha1UUID(cast(const(char[]))"test"); - - char[] mutable = "test".dup; - id = sha1UUID(mutable, namespace); - - const(ubyte)[] data = cast(ubyte[])[0,1,2,244,165,222]; - id = sha1UUID(data); - assert(id.data == cast(ubyte[16])[60, 65, 92, 240, 96, 46, 95, 238, 149, 100, 12, 64, 199, 194, - 243, 12]); - - auto correct = UUID("21f7f8de-8051-5b89-8680-0195ef798b6a"); - - auto u = sha1UUID("www.widgets.com", dnsNamespace); - assert(u == correct); - assert(u.variant == UUID.Variant.rfc4122); - assert(u.uuidVersion == UUID.Version.nameBasedSHA1); -} - -/** - * This function generates a random number based UUID from a random - * number generator. - * - * This function is not supported at compile time. - * - * Params: - * randomGen = uniform RNG - * See_Also: $(REF isUniformRNG, std,random) - */ -@nogc nothrow @safe UUID randomUUID() -{ - import std.random : rndGen; - // A PRNG with fewer than `n` bytes of state cannot produce - // every distinct `n` byte sequence. - static if (typeof(rndGen).sizeof >= UUID.sizeof) - { - return randomUUID(rndGen); - } - else - { - import std.random : unpredictableSeed, Xorshift192; - static assert(Xorshift192.sizeof >= UUID.sizeof); - static Xorshift192 rng; - static bool initialized; - if (!initialized) - { - rng.seed(unpredictableSeed); - initialized = true; - } - return randomUUID(rng); - } -} - -/// ditto -UUID randomUUID(RNG)(ref RNG randomGen) -if (isInputRange!RNG && isIntegral!(ElementType!RNG)) -{ - import std.random : isUniformRNG; - static assert(isUniformRNG!RNG, "randomGen must be a uniform RNG"); - - alias E = ElementEncodingType!RNG; - enum size_t elemSize = E.sizeof; - static assert(elemSize <= 16); - static assert(16 % elemSize == 0); - - UUID u; - foreach (ref E e ; u.asArrayOf!E()) - { - e = randomGen.front; - randomGen.popFront(); - } - - //set variant - //must be 0b10xxxxxx - u.data[8] &= 0b10111111; - u.data[8] |= 0b10000000; - - //set version - //must be 0b0100xxxx - u.data[6] &= 0b01001111; - u.data[6] |= 0b01000000; - - return u; -} - -/// -@safe unittest -{ - import std.random : Xorshift192, unpredictableSeed; - - //simple call - auto uuid = randomUUID(); - - //provide a custom RNG. Must be seeded manually. - Xorshift192 gen; - - gen.seed(unpredictableSeed); - auto uuid3 = randomUUID(gen); -} - -@safe unittest -{ - import std.random : Xorshift192, unpredictableSeed; - //simple call - auto uuid = randomUUID(); - - //provide a custom RNG. Must be seeded manually. - Xorshift192 gen; - gen.seed(unpredictableSeed); - auto uuid3 = randomUUID(gen); - - auto u1 = randomUUID(); - auto u2 = randomUUID(); - assert(u1 != u2); - assert(u1.variant == UUID.Variant.rfc4122); - assert(u1.uuidVersion == UUID.Version.randomNumberBased); -} - -/** - * This is a less strict parser compared to the parser used in the - * UUID constructor. It enforces the following rules: - * - * $(UL - * $(LI hex numbers are always two hexdigits([0-9a-fA-F])) - * $(LI there must be exactly 16 such pairs in the input, not less, not more) - * $(LI there can be exactly one dash between two hex-pairs, but not more) - * $(LI there can be multiple characters enclosing the 16 hex pairs, - * as long as these characters do not contain [0-9a-fA-F]) - * ) - * - * Note: - * Like most parsers, it consumes its argument. This means: - * ------------------------- - * string s = "8AB3060E-2CBA-4F23-b74c-B52Db3BDFB46"; - * parseUUID(s); - * assert(s == ""); - * ------------------------- - * - * Throws: - * $(LREF UUIDParsingException) if the input is invalid - * - * CTFE: - * This function is supported in CTFE code. Note that error messages - * caused by a malformed UUID parsed at compile time can be cryptic, - * but errors are detected and reported at compile time. - */ -UUID parseUUID(T)(T uuidString) -if (isSomeString!T) -{ - return parseUUID(uuidString); -} - -///ditto -UUID parseUUID(Range)(ref Range uuidRange) -if (isInputRange!Range && isSomeChar!(ElementType!Range)) -{ - import std.ascii : isHexDigit; - import std.conv : ConvException, parse; - - static if (isForwardRange!Range) - auto errorCopy = uuidRange.save; - - void parserError()(size_t pos, UUIDParsingException.Reason reason, string message, Throwable next = null, - string file = __FILE__, size_t line = __LINE__) - { - static if (isForwardRange!Range) - { - import std.conv : to; - static if (isInfinite!Range) - { - throw new UUIDParsingException(to!string(take(errorCopy, pos)), pos, reason, message, - next, file, line); - } - else - { - throw new UUIDParsingException(to!string(errorCopy), pos, reason, message, next, file, - line); - } - } - else - { - throw new UUIDParsingException("", pos, reason, message, next, file, line); - } - } - - static if (hasLength!Range) - { - import std.conv : to; - if (uuidRange.length < 32) - { - throw new UUIDParsingException(to!string(uuidRange), 0, UUIDParsingException.Reason.tooLittle, - "Insufficient Input"); - } - } - - UUID result; - size_t consumed; - size_t element = 0; - - //skip garbage - size_t skip()() - { - size_t skipped; - while (!uuidRange.empty && !isHexDigit(uuidRange.front)) - { - skipped++; - uuidRange.popFront(); - } - return skipped; - } - - consumed += skip(); - - if (uuidRange.empty) - parserError(consumed, UUIDParsingException.Reason.tooLittle, "Insufficient Input"); - - bool dashAllowed = false; - - parseLoop: while (!uuidRange.empty) - { - immutable character = uuidRange.front; - - if (character == '-') - { - if (!dashAllowed) - parserError(consumed, UUIDParsingException.Reason.invalidChar, "Unexpected '-'"); - else - dashAllowed = false; - - consumed++; - } - else if (!isHexDigit(character)) - { - parserError(consumed, UUIDParsingException.Reason.invalidChar, - "Unexpected character (wanted a hexDigit)"); - } - else - { - try - { - consumed += 2; - static if (isSomeString!Range) - { - if (uuidRange.length < 2) - { - parserError(consumed, UUIDParsingException.Reason.tooLittle, - "Insufficient Input"); - } - auto part = uuidRange[0 .. 2]; - result.data[element++] = parse!ubyte(part, 16); - uuidRange.popFront(); - } - else - { - dchar[2] copyBuf; - copyBuf[0] = character; - uuidRange.popFront(); - if (uuidRange.empty) - { - parserError(consumed, UUIDParsingException.Reason.tooLittle, - "Insufficient Input"); - } - copyBuf[1] = uuidRange.front; - auto part = copyBuf[]; - result.data[element++] = parse!ubyte(part, 16); - } - - if (element == 16) - { - uuidRange.popFront(); - break parseLoop; - } - - dashAllowed = true; - } - catch (ConvException e) - { - parserError(consumed, UUIDParsingException.Reason.invalidChar, - "Couldn't parse ubyte", e); - } - } - uuidRange.popFront(); - } - assert(element <= 16); - - if (element < 16) - parserError(consumed, UUIDParsingException.Reason.tooLittle, "Insufficient Input"); - - consumed += skip(); - if (!uuidRange.empty) - parserError(consumed, UUIDParsingException.Reason.invalidChar, "Unexpected character"); - - return result; -} - -/// -@safe unittest -{ - auto id = parseUUID("8AB3060E-2CBA-4F23-b74c-B52Db3BDFB46"); - //no dashes - id = parseUUID("8ab3060e2cba4f23b74cb52db3bdfb46"); - //dashes at different positions - id = parseUUID("8a-b3-06-0e2cba4f23b74c-b52db3bdfb-46"); - //leading / trailing characters - id = parseUUID("{8ab3060e-2cba-4f23-b74c-b52db3bdfb46}"); - //unicode - id = parseUUID("Ăź8ab3060e2cba4f23b74cb52db3bdfb46Ăź"); - //multiple trailing/leading characters - id = parseUUID("///8ab3060e2cba4f23b74cb52db3bdfb46||"); - - //Can also be used in CTFE, for example as UUID literals: - enum ctfeID = parseUUID("8ab3060e-2cba-4f23-b74c-b52db3bdfb46"); - //here parsing is done at compile time, no runtime overhead! -} - -@safe pure unittest -{ - import std.conv : to; - import std.exception; - import std.meta; - - struct TestRange(bool forward) - { - dstring input; - - @property dchar front() - { - return input.front; - } - - void popFront() - { - input.popFront(); - } - - @property bool empty() - { - return input.empty; - } - - static if (forward) - { - @property TestRange!true save() - { - return this; - } - } - } - alias TestInputRange = TestRange!false; - alias TestForwardRange = TestRange!true; - - assert(isInputRange!TestInputRange); - assert(is(ElementType!TestInputRange == dchar)); - assert(isInputRange!TestForwardRange); - assert(isForwardRange!TestForwardRange); - assert(is(ElementType!TestForwardRange == dchar)); - - //Helper function for unittests - Need to pass ranges by ref - UUID parseHelper(T)(string input) - { - static if (is(T == TestInputRange) || is(T == TestForwardRange)) - { - T range = T(to!dstring(input)); - return parseUUID(range); - } - else - return parseUUID(to!T(input)); - } - - static foreach (S; AliasSeq!(char[], const(char)[], immutable(char)[], - wchar[], const(wchar)[], immutable(wchar)[], - dchar[], const(dchar)[], immutable(dchar)[], - immutable(char[]), immutable(wchar[]), immutable(dchar[]), - TestForwardRange, TestInputRange)) - {{ - //Verify examples. - auto id = parseHelper!S("8AB3060E-2CBA-4F23-b74c-B52Db3BDFB46"); - //no dashes - id = parseHelper!S("8ab3060e2cba4f23b74cb52db3bdfb46"); - //dashes at different positions - id = parseHelper!S("8a-b3-06-0e2cba4f23b74c-b52db3bdfb-46"); - //leading / trailing characters - id = parseHelper!S("{8ab3060e-2cba-4f23-b74c-b52db3bdfb46}"); - //unicode - id = parseHelper!S("Ăź8ab3060e2cba4f23b74cb52db3bdfb46Ăź"); - //multiple trailing/leading characters - id = parseHelper!S("///8ab3060e2cba4f23b74cb52db3bdfb46||"); - enum ctfeId = parseHelper!S("8ab3060e-2cba-4f23-b74c-b52db3bdfb46"); - assert(parseHelper!S("8AB3060E-2cba-4f23-b74c-b52db3bdfb46") == ctfeId); - - //Test valid, working cases - assert(parseHelper!S("00000000-0000-0000-0000-000000000000").empty); - assert(parseHelper!S("8AB3060E-2CBA-4F23-b74c-B52Db3BDFB46").data - == [138, 179, 6, 14, 44, 186, 79, 35, 183, 76, 181, 45, 179, 189, 251, 70]); - - assert(parseHelper!S("5668122d-9df0-49a4-ad0b-b9b0a57f886a").data - == [86, 104, 18, 45, 157, 240, 73, 164, 173, 11, 185, 176, 165, 127, 136, 106]); - - //wstring / dstring - assert(parseHelper!S("5668122d-9df0-49a4-ad0b-b9b0a57f886a").data - == [86, 104, 18, 45, 157, 240, 73, 164, 173, 11, 185, 176, 165, 127, 136, 106]); - assert(parseHelper!S("5668122d-9df0-49a4-ad0b-b9b0a57f886a").data - == [86, 104, 18, 45, 157, 240, 73, 164, 173, 11, 185, 176, 165, 127, 136, 106]); - - //Test too short UUIDS - auto except = collectException!UUIDParsingException( - parseHelper!S("5668122d-9df0-49a4-ad0b-b9b0a57f886")); - assert(except && except.reason == UUIDParsingException.Reason.tooLittle); - - //Test too long UUIDS - except = collectException!UUIDParsingException( - parseHelper!S("5668122d-9df0-49a4-ad0b-b9b0a57f886aa")); - assert(except && except.reason == UUIDParsingException.Reason.invalidChar); - - //Test too long UUIDS 2 - except = collectException!UUIDParsingException( - parseHelper!S("5668122d-9df0-49a4-ad0b-b9b0a57f886a-aa")); - assert(except && except.reason == UUIDParsingException.Reason.invalidChar); - - //Test dashes - assert(parseHelper!S("8ab3060e2cba-4f23-b74c-b52db3bdfb46") - == parseUUID("8ab3060e-2cba-4f23-b74c-b52db3bdfb46")); - assert(parseHelper!S("8ab3-060e2cba-4f23-b74c-b52db3bdfb46") - == parseUUID("8ab3060e-2cba-4f23-b74c-b52db3bdfb46")); - assert(parseHelper!S("8ab3060e2cba4f23b74cb52db3bdfb46") - == parseUUID("8ab3060e-2cba-4f23-b74c-b52db3bdfb46")); - - except = collectException!UUIDParsingException( - parseHelper!S("8-ab3060e2cba-4f23-b74c-b52db3bdfb46")); - assert(except && except.reason == UUIDParsingException.Reason.invalidChar); - - //Test leading/trailing characters - assert(parseHelper!S("{8ab3060e-2cba-4f23-b74c-b52db3bdfb46}") - == parseUUID("8ab3060e-2cba-4f23-b74c-b52db3bdfb46")); - assert(parseHelper!S("{8ab3060e2cba4f23b74cb52db3bdfb46}") - == parseUUID("8ab3060e-2cba-4f23-b74c-b52db3bdfb46")); - - //Boost test - auto u_increasing = UUID(cast(ubyte[16])[0x01, 0x23, 0x45, 0x67, 0x89, 0xab, - 0xcd, 0xef,0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef]); - assert(parseHelper!S("0123456789abcdef0123456789ABCDEF") == UUID(cast(ubyte[16])[0x01, - 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef,0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef])); - - //unicode - assert(parseHelper!S("Ăź8ab3060e2cba4f23b74cb52db3bdfb46Ăź") - == parseUUID("8ab3060e-2cba-4f23-b74c-b52db3bdfb46")); - - //multiple trailing/leading characters - assert(parseHelper!S("///8ab3060e2cba4f23b74cb52db3bdfb46||") - == parseUUID("8ab3060e-2cba-4f23-b74c-b52db3bdfb46")); - }} - - // Test input range with non-dchar element type. - { - import std.utf : byCodeUnit; - auto range = "8AB3060E-2CBA-4F23-b74c-B52Db3BDFB46".byCodeUnit; - assert(parseUUID(range).data == [138, 179, 6, 14, 44, 186, 79, 35, 183, 76, 181, 45, 179, 189, 251, 70]); - } -} - -/** - * Default namespace from RFC 4122 - * - * Name string is a fully-qualified domain name - */ -enum dnsNamespace = UUID("6ba7b810-9dad-11d1-80b4-00c04fd430c8"); - -/** - * Default namespace from RFC 4122 - * - * Name string is a URL - */ -enum urlNamespace = UUID("6ba7b811-9dad-11d1-80b4-00c04fd430c8"); - -/** - * Default namespace from RFC 4122 - * - * Name string is an ISO OID - */ -enum oidNamespace = UUID("6ba7b812-9dad-11d1-80b4-00c04fd430c8"); - -/** - * Default namespace from RFC 4122 - * - * Name string is an X.500 DN (in DER or a text output format) - */ -enum x500Namespace = UUID("6ba7b814-9dad-11d1-80b4-00c04fd430c8"); - -/** - * Regex string to extract UUIDs from text. - */ -enum uuidRegex = "[a-fA-F0-9]{8}-[a-fA-F0-9]{4}-[a-fA-F0-9]{4}"~ - "-[a-fA-F0-9]{4}-[a-fA-F0-9]{12}"; - -/// -@safe unittest -{ - import std.algorithm; - import std.regex; - - string test = "Lorem ipsum dolor sit amet, consetetur "~ - "6ba7b814-9dad-11d1-80b4-00c04fd430c8 sadipscing \n"~ - "elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore \r\n"~ - "magna aliquyam erat, sed diam voluptua. "~ - "8ab3060e-2cba-4f23-b74c-b52db3bdfb46 At vero eos et accusam et "~ - "justo duo dolores et ea rebum."; - - auto r = regex(uuidRegex, "g"); - UUID[] found; - foreach (c; match(test, r)) - { - found ~= UUID(c.hit); - } - assert(found == [ - UUID("6ba7b814-9dad-11d1-80b4-00c04fd430c8"), - UUID("8ab3060e-2cba-4f23-b74c-b52db3bdfb46"), - ]); -} - -/** - * This exception is thrown if an error occurs when parsing a UUID - * from a string. - */ -public class UUIDParsingException : Exception -{ - /** - * The reason why parsing the UUID string failed (if known) - */ - enum Reason - { - unknown, /// - tooLittle, ///The passed in input was correct, but more input was expected. - tooMuch, ///The input data is too long (There's no guarantee the first part of the data is valid) - invalidChar, ///Encountered an invalid character - - } - ///ditto - Reason reason; - ///The original input string which should have been parsed. - string input; - ///The position in the input string where the error occurred. - size_t position; - - private this(string input, size_t pos, Reason why = Reason.unknown, string msg = "", - Throwable next = null, string file = __FILE__, size_t line = __LINE__) pure @trusted - { - import std.array : replace; - import std.format : format; - this.input = input; - this.position = pos; - this.reason = why; - string message = format("An error occured in the UUID parser: %s\n" ~ - " * Input:\t'%s'\n * Position:\t%s", msg, replace(replace(input, - "\r", "\\r"), "\n", "\\n"), pos); - super(message, file, line, next); - } -} - -/// -@safe unittest -{ - import std.exception : collectException; - - const inputUUID = "this-is-an-invalid-uuid"; - auto ex = collectException!UUIDParsingException(UUID(inputUUID)); - assert(ex !is null); // check that exception was thrown - assert(ex.input == inputUUID); - assert(ex.position == 0); - assert(ex.reason == UUIDParsingException.Reason.tooLittle); -} - -@safe unittest -{ - auto ex = new UUIDParsingException("foo", 10, UUIDParsingException.Reason.tooMuch); - assert(ex.input == "foo"); - assert(ex.position == 10); - assert(ex.reason == UUIDParsingException.Reason.tooMuch); -} diff --git a/phobos/std/variant.d b/phobos/std/variant.d deleted file mode 100644 index f783210..0000000 --- a/phobos/std/variant.d +++ /dev/null @@ -1,3256 +0,0 @@ -// Written in the D programming language. - -/** -This module implements a -$(HTTP erdani.org/publications/cuj-04-2002.php.html,discriminated union) -type (a.k.a. -$(HTTP en.wikipedia.org/wiki/Tagged_union,tagged union), -$(HTTP en.wikipedia.org/wiki/Algebraic_data_type,algebraic type)). -Such types are useful -for type-uniform binary interfaces, interfacing with scripting -languages, and comfortable exploratory programming. - -A $(LREF Variant) object can hold a value of any type, with very few -restrictions (such as `shared` types and noncopyable types). Setting the value -is as immediate as assigning to the `Variant` object. To read back the value of -the appropriate type `T`, use the $(LREF get) method. To query whether a -`Variant` currently holds a value of type `T`, use $(LREF peek). To fetch the -exact type currently held, call $(LREF type), which returns the `TypeInfo` of -the current value. - -In addition to $(LREF Variant), this module also defines the $(LREF Algebraic) -type constructor. Unlike `Variant`, `Algebraic` only allows a finite set of -types, which are specified in the instantiation (e.g. $(D Algebraic!(int, -string)) may only hold an `int` or a `string`). - -$(RED Warning: $(LREF Algebraic) is outdated and not recommended for use in new -code. Instead, use $(REF SumType, std,sumtype).) - -Credits: Reviewed by Brad Roberts. Daniel Keep provided a detailed code review -prompting the following improvements: (1) better support for arrays; (2) support -for associative arrays; (3) friendlier behavior towards the garbage collector. -Copyright: Copyright Andrei Alexandrescu 2007 - 2015. -License: $(HTTP www.boost.org/LICENSE_1_0.txt, Boost License 1.0). -Authors: $(HTTP erdani.org, Andrei Alexandrescu) -Source: $(PHOBOSSRC std/variant.d) -*/ -module std.variant; - -import std.meta, std.traits, std.typecons; - -/// -@system unittest -{ - Variant a; // Must assign before use, otherwise exception ensues - // Initialize with an integer; make the type int - Variant b = 42; - assert(b.type == typeid(int)); - // Peek at the value - assert(b.peek!(int) !is null && *b.peek!(int) == 42); - // Automatically convert per language rules - auto x = b.get!(real); - - // Assign any other type, including other variants - a = b; - a = 3.14; - assert(a.type == typeid(double)); - // Implicit conversions work just as with built-in types - assert(a < b); - // Check for convertibility - assert(!a.convertsTo!(int)); // double not convertible to int - // Strings and all other arrays are supported - a = "now I'm a string"; - assert(a == "now I'm a string"); - - // can also assign arrays - a = new int[42]; - assert(a.length == 42); - a[5] = 7; - assert(a[5] == 7); - - // Can also assign class values - class Foo {} - auto foo = new Foo; - a = foo; - assert(*a.peek!(Foo) == foo); // and full type information is preserved -} - -/++ - Gives the `sizeof` the largest type given. - - See_Also: $(LINK https://forum.dlang.org/thread/wbpnncxepehgcswhuazl@forum.dlang.org?page=1) - +/ -template maxSize(Ts...) -{ - align(1) union Impl - { - static foreach (i, T; Ts) - { - static if (!is(T == void)) - mixin("T _field_", i, ";"); - } - } - enum maxSize = Impl.sizeof; -} - -/// -@safe unittest -{ - struct Cat { int a, b, c; } - - align(1) struct S - { - long l; - ubyte b; - } - - align(1) struct T - { - ubyte b; - long l; - } - - static assert(maxSize!(int, long) == 8); - static assert(maxSize!(bool, byte) == 1); - static assert(maxSize!(bool, Cat) == 12); - static assert(maxSize!(char) == 1); - static assert(maxSize!(char, short, ubyte) == 2); - static assert(maxSize!(char, long, ubyte) == 8); - import std.algorithm.comparison : max; - static assert(maxSize!(long, S) == max(long.sizeof, S.sizeof)); - static assert(maxSize!(S, T) == max(S.sizeof, T.sizeof)); - static assert(maxSize!(int, ubyte[7]) == 7); - static assert(maxSize!(int, ubyte[3]) == 4); - static assert(maxSize!(int, int, ubyte[3]) == 4); - static assert(maxSize!(void, int, ubyte[3]) == 4); - static assert(maxSize!(void) == 1); -} - -struct This; - -private alias This2Variant(V, T...) = AliasSeq!(ReplaceTypeUnless!(isAlgebraic, This, V, T)); - -// We can't just use maxAlignment because no types might be specified -// to VariantN, so handle that here and then pass along the rest. -private template maxVariantAlignment(U...) -if (isTypeTuple!U) -{ - static if (U.length == 0) - { - import std.algorithm.comparison : max; - enum maxVariantAlignment = max(real.alignof, size_t.alignof); - } - else - enum maxVariantAlignment = maxAlignment!(U); -} - -/** - * Back-end type seldom used directly by user - * code. Two commonly-used types using `VariantN` are: - * - * $(OL $(LI $(LREF Algebraic): A closed discriminated union with a - * limited type universe (e.g., $(D Algebraic!(int, double, - * string)) only accepts these three types and rejects anything - * else).) $(LI $(LREF Variant): An open discriminated union allowing an - * unbounded set of types. If any of the types in the `Variant` - * are larger than the largest built-in type, they will automatically - * be boxed. This means that even large types will only be the size - * of a pointer within the `Variant`, but this also implies some - * overhead. `Variant` can accommodate all primitive types and - * all user-defined types.)) - * - * Both `Algebraic` and `Variant` share $(D - * VariantN)'s interface. (See their respective documentations below.) - * - * `VariantN` is a discriminated union type parameterized - * with the largest size of the types stored (`maxDataSize`) - * and with the list of allowed types (`AllowedTypes`). If - * the list is empty, then any type up of size up to $(D - * maxDataSize) (rounded up for alignment) can be stored in a - * `VariantN` object without being boxed (types larger - * than this will be boxed). - * - */ -struct VariantN(size_t maxDataSize, AllowedTypesParam...) -{ - /** - The list of allowed types. If empty, any type is allowed. - */ - alias AllowedTypes = This2Variant!(VariantN, AllowedTypesParam); - -private: - // Compute the largest practical size from maxDataSize - struct SizeChecker - { - int function() fptr; - ubyte[maxDataSize] data; - } - enum size = SizeChecker.sizeof - (int function()).sizeof; - - /** Tells whether a type `T` is statically _allowed for - * storage inside a `VariantN` object by looking - * `T` up in `AllowedTypes`. - */ - public template allowed(T) - { - enum bool allowed - = is(T == VariantN) - || - //T.sizeof <= size && - (AllowedTypes.length == 0 || staticIndexOf!(T, AllowedTypes) >= 0); - } - - // Each internal operation is encoded with an identifier. See - // the "handler" function below. - enum OpID { getTypeInfo, get, compare, equals, testConversion, toString, - index, indexAssign, catAssign, copyOut, length, - apply, postblit, destruct } - - // state - union - { - align(maxVariantAlignment!(AllowedTypes)) ubyte[size] store; - // conservatively mark the region as pointers - static if (size >= (void*).sizeof) - void*[size / (void*).sizeof] p; - } - ptrdiff_t function(OpID selector, ubyte[size]* store, void* data) fptr - = &handler!(void); - - // internals - // Handler for an uninitialized value - static ptrdiff_t handler(A : void)(OpID selector, ubyte[size]*, void* parm) - { - switch (selector) - { - case OpID.getTypeInfo: - *cast(TypeInfo *) parm = typeid(A); - break; - case OpID.copyOut: - auto target = cast(VariantN *) parm; - target.fptr = &handler!(A); - // no need to copy the data (it's garbage) - break; - case OpID.compare: - case OpID.equals: - auto rhs = cast(const VariantN *) parm; - return rhs.peek!(A) - ? 0 // all uninitialized are equal - : ptrdiff_t.min; // uninitialized variant is not comparable otherwise - case OpID.toString: - string * target = cast(string*) parm; - *target = "<Uninitialized VariantN>"; - break; - case OpID.postblit: - case OpID.destruct: - break; - case OpID.get: - case OpID.testConversion: - case OpID.index: - case OpID.indexAssign: - case OpID.catAssign: - case OpID.length: - throw new VariantException( - "Attempt to use an uninitialized VariantN"); - default: assert(false, "Invalid OpID"); - } - return 0; - } - - // Handler for all of a type's operations - static ptrdiff_t handler(A)(OpID selector, ubyte[size]* pStore, void* parm) - { - import std.conv : to; - static A* getPtr(void* untyped) - { - if (untyped) - { - static if (A.sizeof <= size) - return cast(A*) untyped; - else - return *cast(A**) untyped; - } - return null; - } - - static ptrdiff_t compare(A* rhsPA, A* zis, OpID selector) - { - static if (is(typeof(*rhsPA == *zis))) - { - enum isEmptyStructWithoutOpEquals = is(A == struct) && A.tupleof.length == 0 && - !__traits(hasMember, A, "opEquals"); - static if (isEmptyStructWithoutOpEquals) - { - // The check above will always succeed if A is an empty struct. - // Don't generate unreachable code as seen in - // https://issues.dlang.org/show_bug.cgi?id=21231 - return 0; - } - else - { - if (*rhsPA == *zis) - return 0; - static if (is(typeof(*zis < *rhsPA))) - { - // Many types (such as any using the default Object opCmp) - // will throw on an invalid opCmp, so do it only - // if the caller requests it. - if (selector == OpID.compare) - return *zis < *rhsPA ? -1 : 1; - else - return ptrdiff_t.min; - } - else - { - // Not equal, and type does not support ordering - // comparisons. - return ptrdiff_t.min; - } - } - } - else - { - // Type does not support comparisons at all. - return ptrdiff_t.min; - } - } - - auto zis = getPtr(pStore); - // Input: TypeInfo object - // Output: target points to a copy of *me, if me was not null - // Returns: true iff the A can be converted to the type represented - // by the incoming TypeInfo - static bool tryPutting(A* src, TypeInfo targetType, void* target) - { - alias UA = Unqual!A; - static if (isStaticArray!A && is(typeof(UA.init[0]))) - { - alias MutaTypes = AliasSeq!(UA, typeof(UA.init[0])[], AllImplicitConversionTargets!UA); - } - else - { - alias MutaTypes = AliasSeq!(UA, AllImplicitConversionTargets!UA); - } - alias ConstTypes = staticMap!(ConstOf, MutaTypes); - alias SharedTypes = staticMap!(SharedOf, MutaTypes); - alias SharedConstTypes = staticMap!(SharedConstOf, MutaTypes); - alias ImmuTypes = staticMap!(ImmutableOf, MutaTypes); - - static if (is(A == immutable)) - alias AllTypes = AliasSeq!(ImmuTypes, ConstTypes, SharedConstTypes); - else static if (is(A == shared)) - { - static if (is(A == const)) - alias AllTypes = SharedConstTypes; - else - alias AllTypes = AliasSeq!(SharedTypes, SharedConstTypes); - } - else - { - static if (is(A == const)) - alias AllTypes = ConstTypes; - else - alias AllTypes = AliasSeq!(MutaTypes, ConstTypes); - } - - foreach (T ; AllTypes) - { - if (targetType != typeid(T)) - continue; - - // SPECIAL NOTE: variant only will ever create a new value with - // tryPutting (effectively), and T is ALWAYS the same type of - // A, but with different modifiers (and a limited set of - // implicit targets). So this checks to see if we can construct - // a T from A, knowing that prerequisite. This handles issues - // where the type contains some constant data aside from the - // modifiers on the type itself. - static if (is(typeof(delegate T() {return *src;})) || - is(T == const(U), U) || - is(T == shared(U), U) || - is(T == shared const(U), U) || - is(T == immutable(U), U)) - { - import core.internal.lifetime : emplaceRef; - - auto zat = cast(T*) target; - if (src) - { - static if (T.sizeof > 0) - assert(target, "target must be non-null"); - - static if (isStaticArray!A && isDynamicArray!T) - { - auto this_ = (*src)[]; - emplaceRef(*cast(Unqual!T*) zat, cast() cast(T) this_); - } - else - { - emplaceRef(*cast(Unqual!T*) zat, *cast(UA*) src); - } - } - } - else - { - // type T is not constructible from A - if (src) - assert(false, A.stringof); - } - return true; - } - return false; - } - - switch (selector) - { - case OpID.getTypeInfo: - *cast(TypeInfo *) parm = typeid(A); - break; - case OpID.copyOut: - auto target = cast(VariantN *) parm; - assert(target); - - static if (target.size < A.sizeof) - { - if (target.type.tsize < A.sizeof) - { - static if (is(A == U[n], U, size_t n)) - { - A* p = cast(A*)(new U[n]).ptr; - } - else - { - A* p = new A; - } - *cast(A**)&target.store = p; - } - } - tryPutting(zis, typeid(A), cast(void*) getPtr(&target.store)) - || assert(false); - target.fptr = &handler!(A); - break; - case OpID.get: - auto t = * cast(Tuple!(TypeInfo, void*)*) parm; - return !tryPutting(zis, t[0], t[1]); - case OpID.testConversion: - return !tryPutting(null, *cast(TypeInfo*) parm, null); - case OpID.compare: - case OpID.equals: - auto rhsP = cast(VariantN *) parm; - auto rhsType = rhsP.type; - // Are we the same? - if (rhsType == typeid(A)) - { - // cool! Same type! - auto rhsPA = getPtr(&rhsP.store); - return compare(rhsPA, zis, selector); - } - else if (rhsType == typeid(void)) - { - // No support for ordering comparisons with - // uninitialized vars - return ptrdiff_t.min; - } - VariantN temp; - // Do I convert to rhs? - if (tryPutting(zis, rhsType, &temp.store)) - { - // cool, I do; temp's store contains my data in rhs's type! - // also fix up its fptr - temp.fptr = rhsP.fptr; - // now lhsWithRhsType is a full-blown VariantN of rhs's type - if (selector == OpID.compare) - return temp.opCmp(*rhsP); - else - return temp.opEquals(*rhsP) ? 0 : 1; - } - // Does rhs convert to zis? - auto t = tuple(typeid(A), &temp.store); - if (rhsP.fptr(OpID.get, &rhsP.store, &t) == 0) - { - // cool! Now temp has rhs in my type! - auto rhsPA = getPtr(&temp.store); - return compare(rhsPA, zis, selector); - } - // Generate the function below only if the Variant's type is - // comparable with 'null' - static if (__traits(compiles, () => A.init == null)) - { - if (rhsType == typeid(null)) - { - // if rhsType is typeof(null), then we're comparing with 'null' - // this takes into account 'opEquals' and 'opCmp' - // all types that can compare with null have to following properties: - // if it's 'null' then it's equal to null, otherwise it's always greater - // than 'null' - return *zis == null ? 0 : 1; - } - } - return ptrdiff_t.min; // dunno - case OpID.toString: - auto target = cast(string*) parm; - static if (is(typeof(to!(string)(*zis)))) - { - *target = to!(string)(*zis); - break; - } - // TODO: The following test evaluates to true for shared objects. - // Use __traits for now until this is sorted out. - // else static if (is(typeof((*zis).toString))) - else static if (__traits(compiles, {(*zis).toString();})) - { - *target = (*zis).toString(); - break; - } - else - { - throw new VariantException(typeid(A), typeid(string)); - } - - case OpID.index: - auto result = cast(Variant*) parm; - static if (isArray!(A) && !is(immutable typeof(A.init[0]) == immutable void)) - { - // array type; input and output are the same VariantN - size_t index = result.convertsTo!(int) - ? result.get!(int) : result.get!(size_t); - *result = (*zis)[index]; - break; - } - else static if (isAssociativeArray!(A)) - { - *result = (*zis)[result.get!(typeof(A.init.keys[0]))]; - break; - } - else - { - throw new VariantException(typeid(A), result[0].type); - } - - case OpID.indexAssign: - // array type; result comes first, index comes second - auto args = cast(Variant*) parm; - static if (isArray!(A) && is(typeof((*zis)[0] = (*zis)[0]))) - { - size_t index = args[1].convertsTo!(int) - ? args[1].get!(int) : args[1].get!(size_t); - (*zis)[index] = args[0].get!(typeof((*zis)[0])); - break; - } - else static if (isAssociativeArray!(A) && is(typeof((*zis)[A.init.keys[0]] = A.init.values[0]))) - { - (*zis)[args[1].get!(typeof(A.init.keys[0]))] - = args[0].get!(typeof(A.init.values[0])); - break; - } - else - { - throw new VariantException(typeid(A), args[0].type); - } - - case OpID.catAssign: - static if (!is(immutable typeof((*zis)[0]) == immutable void) && - is(typeof((*zis)[0])) && is(typeof(*zis ~= *zis))) - { - // array type; parm is the element to append - auto arg = cast(Variant*) parm; - alias E = typeof((*zis)[0]); - if (arg[0].convertsTo!(E)) - { - // append one element to the array - (*zis) ~= [ arg[0].get!(E) ]; - } - else - { - // append a whole array to the array - (*zis) ~= arg[0].get!(A); - } - break; - } - else - { - throw new VariantException(typeid(A), typeid(void[])); - } - - case OpID.length: - static if (isArray!(A) || isAssociativeArray!(A)) - { - return zis.length; - } - else - { - throw new VariantException(typeid(A), typeid(void[])); - } - - case OpID.apply: - static if (!isFunctionPointer!A && !isDelegate!A) - { - import std.conv : text; - import std.exception : enforce; - enforce(0, text("Cannot apply `()' to a value of type `", - A.stringof, "'.")); - } - else - { - import std.conv : text; - import std.exception : enforce; - alias ParamTypes = Parameters!A; - auto p = cast(Variant*) parm; - auto argCount = p.get!size_t; - // To assign the tuple we need to use the unqualified version, - // otherwise we run into issues such as with const values. - // We still get the actual type from the Variant though - // to ensure that we retain const correctness. - Tuple!(staticMap!(Unqual, ParamTypes)) t; - enforce(t.length == argCount, - text("Argument count mismatch: ", - A.stringof, " expects ", t.length, - " argument(s), not ", argCount, ".")); - auto variantArgs = p[1 .. argCount + 1]; - foreach (i, T; ParamTypes) - { - t[i] = cast() variantArgs[i].get!T; - } - - auto args = cast(Tuple!(ParamTypes))t; - static if (is(ReturnType!A == void)) - { - (*zis)(args.expand); - *p = Variant.init; // void returns uninitialized Variant. - } - else - { - *p = (*zis)(args.expand); - } - } - break; - - case OpID.postblit: - static if (hasElaborateCopyConstructor!A) - { - zis.__xpostblit(); - } - break; - - case OpID.destruct: - static if (hasElaborateDestructor!A) - { - zis.__xdtor(); - } - break; - - default: assert(false); - } - return 0; - } - -public: - /** Constructs a `VariantN` value given an argument of a - * generic type. Statically rejects disallowed types. - */ - - this(T)(T value) - { - static assert(allowed!(T), "Cannot store a " ~ T.stringof - ~ " in a " ~ VariantN.stringof); - opAssign(value); - } - - /// Allows assignment from a subset algebraic type - this(T : VariantN!(tsize, Types), size_t tsize, Types...)(T value) - if (!is(T : VariantN) && Types.length > 0 && allSatisfy!(allowed, Types)) - { - opAssign(value); - } - - static if (!AllowedTypes.length || anySatisfy!(hasElaborateCopyConstructor, AllowedTypes)) - { - this(this) - { - fptr(OpID.postblit, &store, null); - } - } - - static if (!AllowedTypes.length || anySatisfy!(hasElaborateDestructor, AllowedTypes)) - { - ~this() - { - // Infer the safety of the provided types - static if (AllowedTypes.length) - { - if (0) - { - AllowedTypes var; - } - } - (() @trusted => fptr(OpID.destruct, &store, null))(); - } - } - - /** Assigns a `VariantN` from a generic - * argument. Statically rejects disallowed types. */ - - VariantN opAssign(T)(T rhs) - { - static assert(allowed!(T), "Cannot store a " ~ T.stringof - ~ " in a " ~ VariantN.stringof ~ ". Valid types are " - ~ AllowedTypes.stringof); - - static if (is(T : VariantN)) - { - rhs.fptr(OpID.copyOut, &rhs.store, &this); - } - else static if (is(T : const(VariantN))) - { - static assert(false, - "Assigning Variant objects from const Variant"~ - " objects is currently not supported."); - } - else - { - import core.lifetime : copyEmplace; - - static if (!AllowedTypes.length || anySatisfy!(hasElaborateDestructor, AllowedTypes)) - { - // Assignment should destruct previous value - fptr(OpID.destruct, &store, null); - } - - static if (T.sizeof <= size) - copyEmplace(rhs, *cast(T*) &store); - else - { - static if (is(T == U[n], U, size_t n)) - auto p = cast(T*) (new U[n]).ptr; - else - auto p = new T; - copyEmplace(rhs, *p); - *(cast(T**) &store) = p; - } - - fptr = &handler!(T); - } - return this; - } - - // Allow assignment from another variant which is a subset of this one - VariantN opAssign(T : VariantN!(tsize, Types), size_t tsize, Types...)(T rhs) - if (!is(T : VariantN) && Types.length > 0 && allSatisfy!(allowed, Types)) - { - // discover which type rhs is actually storing - foreach (V; T.AllowedTypes) - if (rhs.type == typeid(V)) - return this = rhs.get!V; - assert(0, T.AllowedTypes.stringof); - } - - - Variant opCall(P...)(auto ref P params) - { - Variant[P.length + 1] pack; - pack[0] = P.length; - foreach (i, _; params) - { - pack[i + 1] = params[i]; - } - fptr(OpID.apply, &store, &pack); - return pack[0]; - } - - /** Returns true if and only if the `VariantN` object - * holds a valid value (has been initialized with, or assigned - * from, a valid value). - */ - @property bool hasValue() const pure nothrow - { - // @@@BUG@@@ in compiler, the cast shouldn't be needed - return cast(typeof(&handler!(void))) fptr != &handler!(void); - } - - /// - version (StdDdoc) - @system unittest - { - Variant a; - assert(!a.hasValue); - Variant b; - a = b; - assert(!a.hasValue); // still no value - a = 5; - assert(a.hasValue); - } - - /** - * If the `VariantN` object holds a value of the - * $(I exact) type `T`, returns a pointer to that - * value. Otherwise, returns `null`. In cases - * where `T` is statically disallowed, $(D - * peek) will not compile. - */ - @property inout(T)* peek(T)() inout - { - static if (!is(T == void)) - static assert(allowed!(T), "Cannot store a " ~ T.stringof - ~ " in a " ~ VariantN.stringof); - if (type != typeid(T)) - return null; - static if (T.sizeof <= size) - return cast(inout T*)&store; - else - return *cast(inout T**)&store; - } - - /// - version (StdDdoc) - @system unittest - { - Variant a = 5; - auto b = a.peek!(int); - assert(b !is null); - *b = 6; - assert(a == 6); - } - - /** - * Returns the `typeid` of the currently held value. - */ - - @property TypeInfo type() const nothrow @trusted - { - scope(failure) assert(0); - - TypeInfo result; - fptr(OpID.getTypeInfo, null, &result); - return result; - } - - /** - * Returns `true` if and only if the `VariantN` - * object holds an object implicitly convertible to type `T`. - * Implicit convertibility is defined as per - * $(REF_ALTTEXT AllImplicitConversionTargets, AllImplicitConversionTargets, std,traits). - */ - - @property bool convertsTo(T)() const - { - TypeInfo info = typeid(T); - return fptr(OpID.testConversion, null, &info) == 0; - } - - /** - Returns the value stored in the `VariantN` object, either by specifying the - needed type or the index in the list of allowed types. The latter overload - only applies to bounded variants (e.g. $(LREF Algebraic)). - - Params: - T = The requested type. The currently stored value must implicitly convert - to the requested type, in fact `DecayStaticToDynamicArray!T`. If an - implicit conversion is not possible, throws a `VariantException`. - index = The index of the type among `AllowedTypesParam`, zero-based. - */ - @property inout(T) get(T)() inout - { - inout(T) result = void; - static if (is(T == shared)) - alias R = shared Unqual!T; - else - alias R = Unqual!T; - auto buf = tuple(typeid(T), cast(R*)&result); - - if (fptr(OpID.get, cast(ubyte[size]*) &store, &buf)) - { - throw new VariantException(type, typeid(T)); - } - return result; - } - - /// Ditto - @property auto get(uint index)() inout - if (index < AllowedTypes.length) - { - foreach (i, T; AllowedTypes) - { - static if (index == i) return get!T; - } - assert(0); - } - - /** - * Returns the value stored in the `VariantN` object, - * explicitly converted (coerced) to the requested type $(D - * T). If `T` is a string type, the value is formatted as - * a string. If the `VariantN` object is a string, a - * parse of the string to type `T` is attempted. If a - * conversion is not possible, throws a $(D - * VariantException). - */ - - @property T coerce(T)() - { - import std.conv : to, text; - static if (isNumeric!T || isBoolean!T) - { - if (convertsTo!real) - { - // maybe optimize this fella; handle ints separately - return to!T(get!real); - } - else if (convertsTo!(const(char)[])) - { - return to!T(get!(const(char)[])); - } - // I'm not sure why this doesn't convert to const(char), - // but apparently it doesn't (probably a deeper bug). - // - // Until that is fixed, this quick addition keeps a common - // function working. "10".coerce!int ought to work. - else if (convertsTo!(immutable(char)[])) - { - return to!T(get!(immutable(char)[])); - } - else - { - import std.exception : enforce; - enforce(false, text("Type ", type, " does not convert to ", - typeid(T))); - assert(0); - } - } - else static if (is(T : Object)) - { - return to!(T)(get!(Object)); - } - else static if (isSomeString!(T)) - { - return to!(T)(toString()); - } - else - { - // Fix for bug 1649 - static assert(false, "unsupported type for coercion"); - } - } - - /** - * Formats the stored value as a string. - */ - - string toString() - { - string result; - fptr(OpID.toString, &store, &result) == 0 || assert(false); - return result; - } - - /** - * Comparison for equality used by the "==" and "!=" operators. - */ - - // returns 1 if the two are equal - bool opEquals(T)(auto ref T rhs) const - if (allowed!T || is(immutable T == immutable VariantN)) - { - static if (is(immutable T == immutable VariantN)) - alias temp = rhs; - else - auto temp = VariantN(rhs); - return !fptr(OpID.equals, cast(ubyte[size]*) &store, - cast(void*) &temp); - } - - // workaround for bug 10567 fix - int opCmp(ref const VariantN rhs) const - { - return (cast() this).opCmp!(VariantN)(cast() rhs); - } - - /** - * Ordering comparison used by the "<", "<=", ">", and ">=" - * operators. In case comparison is not sensible between the held - * value and `rhs`, an exception is thrown. - */ - - int opCmp(T)(T rhs) - if (allowed!T) // includes T == VariantN - { - static if (is(T == VariantN)) - alias temp = rhs; - else - auto temp = VariantN(rhs); - auto result = fptr(OpID.compare, &store, &temp); - if (result == ptrdiff_t.min) - { - throw new VariantException(type, temp.type); - } - - assert(result >= -1 && result <= 1); // Should be true for opCmp. - return cast(int) result; - } - - /** - * Computes the hash of the held value. - */ - - size_t toHash() const nothrow @safe - { - return type.getHash(&store); - } - - private VariantN opArithmetic(T, string op)(T other) - { - static if (isInstanceOf!(.VariantN, T)) - { - string tryUseType(string tp) - { - import std.format : format; - return q{ - static if (allowed!%1$s && T.allowed!%1$s) - if (convertsTo!%1$s && other.convertsTo!%1$s) - return VariantN(get!%1$s %2$s other.get!%1$s); - }.format(tp, op); - } - - mixin(tryUseType("uint")); - mixin(tryUseType("int")); - mixin(tryUseType("ulong")); - mixin(tryUseType("long")); - mixin(tryUseType("float")); - mixin(tryUseType("double")); - mixin(tryUseType("real")); - } - else - { - static if (allowed!T) - if (auto pv = peek!T) return VariantN(mixin("*pv " ~ op ~ " other")); - static if (allowed!uint && is(typeof(T.max) : uint) && isUnsigned!T) - if (convertsTo!uint) return VariantN(mixin("get!(uint) " ~ op ~ " other")); - static if (allowed!int && is(typeof(T.max) : int) && !isUnsigned!T) - if (convertsTo!int) return VariantN(mixin("get!(int) " ~ op ~ " other")); - static if (allowed!ulong && is(typeof(T.max) : ulong) && isUnsigned!T) - if (convertsTo!ulong) return VariantN(mixin("get!(ulong) " ~ op ~ " other")); - static if (allowed!long && is(typeof(T.max) : long) && !isUnsigned!T) - if (convertsTo!long) return VariantN(mixin("get!(long) " ~ op ~ " other")); - static if (allowed!float && is(T : float)) - if (convertsTo!float) return VariantN(mixin("get!(float) " ~ op ~ " other")); - static if (allowed!double && is(T : double)) - if (convertsTo!double) return VariantN(mixin("get!(double) " ~ op ~ " other")); - static if (allowed!real && is (T : real)) - if (convertsTo!real) return VariantN(mixin("get!(real) " ~ op ~ " other")); - } - - throw new VariantException("No possible match found for VariantN "~op~" "~T.stringof); - } - - private VariantN opLogic(T, string op)(T other) - { - VariantN result; - static if (is(T == VariantN)) - { - if (convertsTo!(uint) && other.convertsTo!(uint)) - result = mixin("get!(uint) " ~ op ~ " other.get!(uint)"); - else if (convertsTo!(int) && other.convertsTo!(int)) - result = mixin("get!(int) " ~ op ~ " other.get!(int)"); - else if (convertsTo!(ulong) && other.convertsTo!(ulong)) - result = mixin("get!(ulong) " ~ op ~ " other.get!(ulong)"); - else - result = mixin("get!(long) " ~ op ~ " other.get!(long)"); - } - else - { - if (is(typeof(T.max) : uint) && T.min == 0 && convertsTo!(uint)) - result = mixin("get!(uint) " ~ op ~ " other"); - else if (is(typeof(T.max) : int) && T.min < 0 && convertsTo!(int)) - result = mixin("get!(int) " ~ op ~ " other"); - else if (is(typeof(T.max) : ulong) && T.min == 0 - && convertsTo!(ulong)) - result = mixin("get!(ulong) " ~ op ~ " other"); - else - result = mixin("get!(long) " ~ op ~ " other"); - } - return result; - } - - /** - * Arithmetic between `VariantN` objects and numeric - * values. All arithmetic operations return a `VariantN` - * object typed depending on the types of both values - * involved. The conversion rules mimic D's built-in rules for - * arithmetic conversions. - */ - VariantN opBinary(string op, T)(T rhs) - if ((op == "+" || op == "-" || op == "*" || op == "/" || op == "^^" || op == "%") && - is(typeof(opArithmetic!(T, op)(rhs)))) - { return opArithmetic!(T, op)(rhs); } - ///ditto - VariantN opBinary(string op, T)(T rhs) - if ((op == "&" || op == "|" || op == "^" || op == ">>" || op == "<<" || op == ">>>") && - is(typeof(opLogic!(T, op)(rhs)))) - { return opLogic!(T, op)(rhs); } - ///ditto - VariantN opBinaryRight(string op, T)(T lhs) - if ((op == "+" || op == "*") && - is(typeof(opArithmetic!(T, op)(lhs)))) - { return opArithmetic!(T, op)(lhs); } - ///ditto - VariantN opBinaryRight(string op, T)(T lhs) - if ((op == "&" || op == "|" || op == "^") && - is(typeof(opLogic!(T, op)(lhs)))) - { return opLogic!(T, op)(lhs); } - ///ditto - VariantN opBinary(string op, T)(T rhs) - if (op == "~") - { - auto temp = this; - temp ~= rhs; - return temp; - } - // ///ditto - // VariantN opBinaryRight(string op, T)(T rhs) - // if (op == "~") - // { - // VariantN temp = rhs; - // temp ~= this; - // return temp; - // } - - ///ditto - VariantN opOpAssign(string op, T)(T rhs) - { - static if (op != "~") - { - mixin("return this = this" ~ op ~ "rhs;"); - } - else - { - auto toAppend = Variant(rhs); - fptr(OpID.catAssign, &store, &toAppend) == 0 || assert(false); - return this; - } - } - - /** - * Array and associative array operations. If a $(D - * VariantN) contains an (associative) array, it can be indexed - * into. Otherwise, an exception is thrown. - */ - inout(Variant) opIndex(K)(K i) inout - { - auto result = Variant(i); - fptr(OpID.index, cast(ubyte[size]*) &store, &result) == 0 || assert(false); - return result; - } - - /// - version (StdDdoc) - @system unittest - { - Variant a = new int[10]; - a[5] = 42; - assert(a[5] == 42); - a[5] += 8; - assert(a[5] == 50); - - int[int] hash = [ 42:24 ]; - a = hash; - assert(a[42] == 24); - a[42] /= 2; - assert(a[42] == 12); - } - - /// ditto - Variant opIndexAssign(T, N)(T value, N i) - { - static if (AllowedTypes.length && !isInstanceOf!(.VariantN, T)) - { - enum canAssign(U) = __traits(compiles, (U u){ u[i] = value; }); - static assert(anySatisfy!(canAssign, AllowedTypes), - "Cannot assign " ~ T.stringof ~ " to " ~ VariantN.stringof ~ - " indexed with " ~ N.stringof); - } - Variant[2] args = [ Variant(value), Variant(i) ]; - fptr(OpID.indexAssign, &store, &args) == 0 || assert(false); - return args[0]; - } - - /// ditto - Variant opIndexOpAssign(string op, T, N)(T value, N i) - { - return opIndexAssign(mixin(`opIndex(i)` ~ op ~ `value`), i); - } - - /** If the `VariantN` contains an (associative) array, - * returns the _length of that array. Otherwise, throws an - * exception. - */ - @property size_t length() - { - return cast(size_t) fptr(OpID.length, &store, null); - } - - /** - If the `VariantN` contains an array, applies `dg` to each - element of the array in turn. Otherwise, throws an exception. - */ - int opApply(Delegate)(scope Delegate dg) if (is(Delegate == delegate)) - { - alias A = Parameters!(Delegate)[0]; - if (type == typeid(A[])) - { - auto arr = get!(A[]); - foreach (ref e; arr) - { - if (dg(e)) return 1; - } - } - else static if (is(A == VariantN)) - { - foreach (i; 0 .. length) - { - // @@@TODO@@@: find a better way to not confuse - // clients who think they change values stored in the - // Variant when in fact they are only changing tmp. - auto tmp = this[i]; - debug scope(exit) assert(tmp == this[i]); - if (dg(tmp)) return 1; - } - } - else - { - import std.conv : text; - import std.exception : enforce; - enforce(false, text("Variant type ", type, - " not iterable with values of type ", - A.stringof)); - } - return 0; - } -} - -/// -@system unittest -{ - alias Var = VariantN!(maxSize!(int, double, string)); - - Var a; // Must assign before use, otherwise exception ensues - // Initialize with an integer; make the type int - Var b = 42; - assert(b.type == typeid(int)); - // Peek at the value - assert(b.peek!(int) !is null && *b.peek!(int) == 42); - // Automatically convert per language rules - auto x = b.get!(real); - - // Assign any other type, including other variants - a = b; - a = 3.14; - assert(a.type == typeid(double)); - // Implicit conversions work just as with built-in types - assert(a < b); - // Check for convertibility - assert(!a.convertsTo!(int)); // double not convertible to int - // Strings and all other arrays are supported - a = "now I'm a string"; - assert(a == "now I'm a string"); -} - -/// can also assign arrays -@system unittest -{ - alias Var = VariantN!(maxSize!(int[])); - - Var a = new int[42]; - assert(a.length == 42); - a[5] = 7; - assert(a[5] == 7); -} - -@safe unittest -{ - alias V = VariantN!24; - const alignMask = V.alignof - 1; - assert(V.sizeof == ((24 + (void*).sizeof + alignMask) & ~alignMask)); -} - -/// Can also assign class values -@system unittest -{ - alias Var = VariantN!(maxSize!(int*)); // classes are pointers - Var a; - - class Foo {} - auto foo = new Foo; - a = foo; - assert(*a.peek!(Foo) == foo); // and full type information is preserved -} - -@system unittest -{ - import std.conv : to; - Variant v; - int foo() { return 42; } - v = &foo; - assert(v() == 42); - - static int bar(string s) { return to!int(s); } - v = &bar; - assert(v("43") == 43); -} - -@system unittest -{ - int[int] hash = [ 42:24 ]; - Variant v = hash; - assert(v[42] == 24); - v[42] = 5; - assert(v[42] == 5); -} - -// opIndex with static arrays, https://issues.dlang.org/show_bug.cgi?id=12771 -@system unittest -{ - int[4] elements = [0, 1, 2, 3]; - Variant v = elements; - assert(v == elements); - assert(v[2] == 2); - assert(v[3] == 3); - v[2] = 6; - assert(v[2] == 6); - assert(v != elements); -} - -@system unittest -{ - import std.exception : assertThrown; - Algebraic!(int[]) v = [2, 2]; - - assert(v == [2, 2]); - v[0] = 1; - assert(v[0] == 1); - assert(v != [2, 2]); - - // opIndexAssign from Variant - v[1] = v[0]; - assert(v[1] == 1); - - static assert(!__traits(compiles, (v[1] = null))); - assertThrown!VariantException(v[1] = Variant(null)); -} - -// https://issues.dlang.org/show_bug.cgi?id=10879 -@system unittest -{ - int[10] arr = [1,2,3,4,5,6,7,8,9,10]; - Variant v1 = arr; - Variant v2; - v2 = arr; - assert(v1 == arr); - assert(v2 == arr); - foreach (i, e; arr) - { - assert(v1[i] == e); - assert(v2[i] == e); - } - static struct LargeStruct - { - int[100] data; - } - LargeStruct ls; - ls.data[] = 4; - v1 = ls; - Variant v3 = ls; - assert(v1 == ls); - assert(v3 == ls); -} - -// https://issues.dlang.org/show_bug.cgi?id=8195 -@system unittest -{ - struct S - { - int a; - long b; - string c; - real d = 0.0; - bool e; - } - - static assert(S.sizeof >= Variant.sizeof); - alias Types = AliasSeq!(string, int, S); - alias MyVariant = VariantN!(maxSize!Types, Types); - - auto v = MyVariant(S.init); - assert(v == S.init); -} - -// https://issues.dlang.org/show_bug.cgi?id=10961 -@system unittest -{ - // Primarily test that we can assign a void[] to a Variant. - void[] elements = cast(void[])[1, 2, 3]; - Variant v = elements; - void[] returned = v.get!(void[]); - assert(returned == elements); -} - -// https://issues.dlang.org/show_bug.cgi?id=13352 -@system unittest -{ - alias TP = Algebraic!(long); - auto a = TP(1L); - auto b = TP(2L); - assert(!TP.allowed!ulong); - assert(a + b == 3L); - assert(a + 2 == 3L); - assert(1 + b == 3L); - - alias TP2 = Algebraic!(long, string); - auto c = TP2(3L); - assert(a + c == 4L); -} - -// https://issues.dlang.org/show_bug.cgi?id=13354 -@system unittest -{ - alias A = Algebraic!(string[]); - A a = ["a", "b"]; - assert(a[0] == "a"); - assert(a[1] == "b"); - a[1] = "c"; - assert(a[1] == "c"); - - alias AA = Algebraic!(int[string]); - AA aa = ["a": 1, "b": 2]; - assert(aa["a"] == 1); - assert(aa["b"] == 2); - aa["b"] = 3; - assert(aa["b"] == 3); -} - -// https://issues.dlang.org/show_bug.cgi?id=14198 -@system unittest -{ - Variant a = true; - assert(a.type == typeid(bool)); -} - -// https://issues.dlang.org/show_bug.cgi?id=14233 -@system unittest -{ - alias Atom = Algebraic!(string, This[]); - - Atom[] values = []; - auto a = Atom(values); -} - -pure nothrow @nogc -@system unittest -{ - Algebraic!(int, double) a; - a = 100; - a = 1.0; -} - -// https://issues.dlang.org/show_bug.cgi?id=14457 -@system unittest -{ - alias A = Algebraic!(int, float, double); - alias B = Algebraic!(int, float); - - A a = 1; - B b = 6f; - a = b; - - assert(a.type == typeid(float)); - assert(a.get!float == 6f); -} - -// https://issues.dlang.org/show_bug.cgi?id=14585 -@system unittest -{ - static struct S - { - int x = 42; - ~this() {assert(x == 42);} - } - Variant(S()).get!S; -} - -// https://issues.dlang.org/show_bug.cgi?id=14586 -@system unittest -{ - const Variant v = new immutable Object; - v.get!(immutable Object); -} - -@system unittest -{ - static struct S - { - T opCast(T)() {assert(false);} - } - Variant v = S(); - v.get!S; -} - -// https://issues.dlang.org/show_bug.cgi?id=13262 -@system unittest -{ - static void fun(T)(Variant v){ - T x; - v = x; - auto r = v.get!(T); - } - Variant v; - fun!(shared(int))(v); - fun!(shared(int)[])(v); - - static struct S1 - { - int c; - string a; - } - - static struct S2 - { - string a; - shared int[] b; - } - - static struct S3 - { - string a; - shared int[] b; - int c; - } - - fun!(S1)(v); - fun!(shared(S1))(v); - fun!(S2)(v); - fun!(shared(S2))(v); - fun!(S3)(v); - fun!(shared(S3))(v); - - // ensure structs that are shared, but don't have shared postblits - // can't be used. - static struct S4 - { - int x; - this(this) {x = 0;} - } - - fun!(S4)(v); - static assert(!is(typeof(fun!(shared(S4))(v)))); -} - -@safe unittest -{ - Algebraic!(int) x; - - static struct SafeS - { - @safe ~this() {} - } - - Algebraic!(SafeS) y; -} - -// https://issues.dlang.org/show_bug.cgi?id=19986 -@system unittest -{ - VariantN!32 v; - v = const(ubyte[33]).init; - - struct S - { - ubyte[33] s; - } - - VariantN!32 v2; - v2 = const(S).init; -} - -// https://issues.dlang.org/show_bug.cgi?id=21021 -@system unittest -{ - static struct S - { - int h; - int[5] array; - alias h this; - } - - S msg; - msg.array[] = 3; - Variant a = msg; - auto other = a.get!S; - assert(msg.array[0] == 3); - assert(other.array[0] == 3); -} - -// https://issues.dlang.org/show_bug.cgi?id=21231 -// Compatibility with -preview=fieldwise -@system unittest -{ - static struct Empty - { - bool opCmp(const scope ref Empty) const - { return false; } - } - - Empty a, b; - assert(a == b); - assert(!(a < b)); - - VariantN!(4, Empty) v = a; - assert(v == b); - assert(!(v < b)); -} - -// Compatibility with -preview=fieldwise -@system unittest -{ - static struct Empty - { - bool opEquals(const scope ref Empty) const - { return false; } - } - - Empty a, b; - assert(a != b); - - VariantN!(4, Empty) v = a; - assert(v != b); -} - -// https://issues.dlang.org/show_bug.cgi?id=22647 -// Can compare with 'null' -@system unittest -{ - static struct Bar - { - int* ptr; - alias ptr this; - } - - static class Foo {} - int* iptr; - int[] arr; - - Variant v = Foo.init; // 'null' - assert(v != null); // can only compare objects with 'null' by using 'is' - - v = iptr; - assert(v == null); // pointers can be compared with 'null' - - v = arr; - assert(v == null); // arrays can be compared with 'null' - - v = ""; - assert(v == null); // strings are arrays, an empty string is considered 'null' - - v = Bar.init; - assert(v == null); // works with alias this - - v = [3]; - assert(v != null); - assert(v > null); - assert(v >= null); - assert(!(v < null)); -} - -/** -_Algebraic data type restricted to a closed set of possible -types. It's an alias for $(LREF VariantN) with an -appropriately-constructed maximum size. `Algebraic` is -useful when it is desirable to restrict what a discriminated type -could hold to the end of defining simpler and more efficient -manipulation. - -$(RED Warning: $(LREF Algebraic) is outdated and not recommended for use in new -code. Instead, use $(REF SumType, std,sumtype).) -*/ -template Algebraic(T...) -{ - alias Algebraic = VariantN!(maxSize!T, T); -} - -/// -@system unittest -{ - auto v = Algebraic!(int, double, string)(5); - assert(v.peek!(int)); - v = 3.14; - assert(v.peek!(double)); - // auto x = v.peek!(long); // won't compile, type long not allowed - // v = '1'; // won't compile, type char not allowed -} - -/** -$(H4 Self-Referential Types) - -A useful and popular use of algebraic data structures is for defining $(LUCKY -self-referential data structures), i.e. structures that embed references to -values of their own type within. - -This is achieved with `Algebraic` by using `This` as a placeholder whenever a -reference to the type being defined is needed. The `Algebraic` instantiation -will perform $(LINK2 https://en.wikipedia.org/wiki/Name_resolution_(programming_languages)#Alpha_renaming_to_make_name_resolution_trivial, -alpha renaming) on its constituent types, replacing `This` -with the self-referenced type. The structure of the type involving `This` may -be arbitrarily complex. -*/ -@system unittest -{ - import std.typecons : Tuple, tuple; - - // A tree is either a leaf or a branch of two other trees - alias Tree(Leaf) = Algebraic!(Leaf, Tuple!(This*, This*)); - Tree!int tree = tuple(new Tree!int(42), new Tree!int(43)); - Tree!int* right = tree.get!1[1]; - assert(*right == 43); - - // An object is a double, a string, or a hash of objects - alias Obj = Algebraic!(double, string, This[string]); - Obj obj = "hello"; - assert(obj.get!1 == "hello"); - obj = 42.0; - assert(obj.get!0 == 42); - obj = ["customer": Obj("John"), "paid": Obj(23.95)]; - assert(obj.get!2["customer"] == "John"); -} - -private struct FakeComplexReal -{ - real re, im; -} - -/** -Alias for $(LREF VariantN) instantiated with the largest size of `creal`, -`char[]`, and `void delegate()`. This ensures that `Variant` is large enough -to hold all of D's predefined types unboxed, including all numeric types, -pointers, delegates, and class references. You may want to use -`VariantN` directly with a different maximum size either for -storing larger types unboxed, or for saving memory. - */ -alias Variant = VariantN!(maxSize!(FakeComplexReal, char[], void delegate())); - -/// -@system unittest -{ - Variant a; // Must assign before use, otherwise exception ensues - // Initialize with an integer; make the type int - Variant b = 42; - assert(b.type == typeid(int)); - // Peek at the value - assert(b.peek!(int) !is null && *b.peek!(int) == 42); - // Automatically convert per language rules - auto x = b.get!(real); - - // Assign any other type, including other variants - a = b; - a = 3.14; - assert(a.type == typeid(double)); - // Implicit conversions work just as with built-in types - assert(a < b); - // Check for convertibility - assert(!a.convertsTo!(int)); // double not convertible to int - // Strings and all other arrays are supported - a = "now I'm a string"; - assert(a == "now I'm a string"); -} - -/// can also assign arrays -@system unittest -{ - Variant a = new int[42]; - assert(a.length == 42); - a[5] = 7; - assert(a[5] == 7); -} - -/// Can also assign class values -@system unittest -{ - Variant a; - - class Foo {} - auto foo = new Foo; - a = foo; - assert(*a.peek!(Foo) == foo); // and full type information is preserved -} - -/** - * Returns an array of variants constructed from `args`. - * - * This is by design. During construction the `Variant` needs - * static type information about the type being held, so as to store a - * pointer to function for fast retrieval. - */ -Variant[] variantArray(T...)(T args) -{ - Variant[] result; - foreach (arg; args) - { - result ~= Variant(arg); - } - return result; -} - -/// -@system unittest -{ - auto a = variantArray(1, 3.14, "Hi!"); - assert(a[1] == 3.14); - auto b = Variant(a); // variant array as variant - assert(b[1] == 3.14); -} - -/** - * Thrown in three cases: - * - * $(OL $(LI An uninitialized `Variant` is used in any way except - * assignment and `hasValue`;) $(LI A `get` or - * `coerce` is attempted with an incompatible target type;) - * $(LI A comparison between `Variant` objects of - * incompatible types is attempted.)) - * - */ - -// @@@ BUG IN COMPILER. THE 'STATIC' BELOW SHOULD NOT COMPILE -static class VariantException : Exception -{ - /// The source type in the conversion or comparison - TypeInfo source; - /// The target type in the conversion or comparison - TypeInfo target; - this(string s) - { - super(s); - } - this(TypeInfo source, TypeInfo target) - { - super("Variant: attempting to use incompatible types " - ~ source.toString() - ~ " and " ~ target.toString()); - this.source = source; - this.target = target; - } -} - -/// -@system unittest -{ - import std.exception : assertThrown; - - Variant v; - - // uninitialized use - assertThrown!VariantException(v + 1); - assertThrown!VariantException(v.length); - - // .get with an incompatible target type - assertThrown!VariantException(Variant("a").get!int); - - // comparison between incompatible types - assertThrown!VariantException(Variant(3) < Variant("a")); -} - -@system unittest -{ - alias W1 = This2Variant!(char, int, This[int]); - alias W2 = AliasSeq!(int, char[int]); - static assert(is(W1 == W2)); - - alias var_t = Algebraic!(void, string); - var_t foo = "quux"; -} - -@system unittest -{ - alias A = Algebraic!(real, This[], This[int], This[This]); - A v1, v2, v3; - v2 = 5.0L; - v3 = 42.0L; - //v1 = [ v2 ][]; - auto v = v1.peek!(A[]); - //writeln(v[0]); - v1 = [ 9 : v3 ]; - //writeln(v1); - v1 = [ v3 : v3 ]; - //writeln(v1); -} - -@system unittest -{ - import std.conv : ConvException; - import std.exception : assertThrown, collectException; - // try it with an oddly small size - VariantN!(1) test; - assert(test.size > 1); - - // variantArray tests - auto heterogeneous = variantArray(1, 4.5, "hi"); - assert(heterogeneous.length == 3); - auto variantArrayAsVariant = Variant(heterogeneous); - assert(variantArrayAsVariant[0] == 1); - assert(variantArrayAsVariant.length == 3); - - // array tests - auto arr = Variant([1.2].dup); - auto e = arr[0]; - assert(e == 1.2); - arr[0] = 2.0; - assert(arr[0] == 2); - arr ~= 4.5; - assert(arr[1] == 4.5); - - // general tests - Variant a; - auto b = Variant(5); - assert(!b.peek!(real) && b.peek!(int)); - // assign - a = *b.peek!(int); - // comparison - assert(a == b, a.type.toString() ~ " " ~ b.type.toString()); - auto c = Variant("this is a string"); - assert(a != c); - // comparison via implicit conversions - a = 42; b = 42.0; assert(a == b); - - // try failing conversions - bool failed = false; - try - { - auto d = c.get!(int); - } - catch (Exception e) - { - //writeln(stderr, e.toString); - failed = true; - } - assert(failed); // :o) - - // toString tests - a = Variant(42); assert(a.toString() == "42"); - a = Variant(42.22); assert(a.toString() == "42.22"); - - // coerce tests - a = Variant(42.22); assert(a.coerce!(int) == 42); - a = cast(short) 5; assert(a.coerce!(double) == 5); - a = Variant("10"); assert(a.coerce!int == 10); - - a = Variant(1); - assert(a.coerce!bool); - a = Variant(0); - assert(!a.coerce!bool); - - a = Variant(1.0); - assert(a.coerce!bool); - a = Variant(0.0); - assert(!a.coerce!bool); - a = Variant(float.init); - assertThrown!ConvException(a.coerce!bool); - - a = Variant("true"); - assert(a.coerce!bool); - a = Variant("false"); - assert(!a.coerce!bool); - a = Variant(""); - assertThrown!ConvException(a.coerce!bool); - - // Object tests - class B1 {} - class B2 : B1 {} - a = new B2; - assert(a.coerce!(B1) !is null); - a = new B1; - assert(collectException(a.coerce!(B2) is null)); - a = cast(Object) new B2; // lose static type info; should still work - assert(a.coerce!(B2) !is null); - -// struct Big { int a[45]; } -// a = Big.init; - - // hash - assert(a.toHash() != 0); -} - -// tests adapted from -// http://www.dsource.org/projects/tango/browser/trunk/tango/core/Variant.d?rev=2601 -@system unittest -{ - Variant v; - - assert(!v.hasValue); - v = 42; - assert( v.peek!(int) ); - assert( v.convertsTo!(long) ); - assert( v.get!(int) == 42 ); - assert( v.get!(long) == 42L ); - assert( v.get!(ulong) == 42uL ); - - v = "Hello, World!"; - assert( v.peek!(string) ); - - assert( v.get!(string) == "Hello, World!" ); - assert(!is(char[] : wchar[])); - assert( !v.convertsTo!(wchar[]) ); - assert( v.get!(string) == "Hello, World!" ); - - // Literal arrays are dynamically-typed - v = cast(int[4]) [1,2,3,4]; - assert( v.peek!(int[4]) ); - assert( v.get!(int[4]) == [1,2,3,4] ); - - { - v = [1,2,3,4,5]; - assert( v.peek!(int[]) ); - assert( v.get!(int[]) == [1,2,3,4,5] ); - } - - v = 3.1413; - assert( v.peek!(double) ); - assert( v.convertsTo!(real) ); - //@@@ BUG IN COMPILER: DOUBLE SHOULD NOT IMPLICITLY CONVERT TO FLOAT - assert( v.convertsTo!(float) ); - assert( *v.peek!(double) == 3.1413 ); - - auto u = Variant(v); - assert( u.peek!(double) ); - assert( *u.peek!(double) == 3.1413 ); - - // operators - v = 38; - assert( v + 4 == 42 ); - assert( 4 + v == 42 ); - assert( v - 4 == 34 ); - assert( Variant(4) - v == -34 ); - assert( v * 2 == 76 ); - assert( 2 * v == 76 ); - assert( v / 2 == 19 ); - assert( Variant(2) / v == 0 ); - assert( v % 2 == 0 ); - assert( Variant(2) % v == 2 ); - assert( (v & 6) == 6 ); - assert( (6 & v) == 6 ); - assert( (v | 9) == 47 ); - assert( (9 | v) == 47 ); - assert( (v ^ 5) == 35 ); - assert( (5 ^ v) == 35 ); - assert( v << 1 == 76 ); - assert( Variant(1) << Variant(2) == 4 ); - assert( v >> 1 == 19 ); - assert( Variant(4) >> Variant(2) == 1 ); - assert( Variant("abc") ~ "def" == "abcdef" ); - assert( Variant("abc") ~ Variant("def") == "abcdef" ); - - v = 38; - v += 4; - assert( v == 42 ); - v = 38; v -= 4; assert( v == 34 ); - v = 38; v *= 2; assert( v == 76 ); - v = 38; v /= 2; assert( v == 19 ); - v = 38; v %= 2; assert( v == 0 ); - v = 38; v &= 6; assert( v == 6 ); - v = 38; v |= 9; assert( v == 47 ); - v = 38; v ^= 5; assert( v == 35 ); - v = 38; v <<= 1; assert( v == 76 ); - v = 38; v >>= 1; assert( v == 19 ); - v = 38; v += 1; assert( v < 40 ); - - v = "abc"; - v ~= "def"; - assert( v == "abcdef", *v.peek!(char[]) ); - assert( Variant(0) < Variant(42) ); - assert( Variant(42) > Variant(0) ); - assert( Variant(42) > Variant(0.1) ); - assert( Variant(42.1) > Variant(1) ); - assert( Variant(21) == Variant(21) ); - assert( Variant(0) != Variant(42) ); - assert( Variant("bar") == Variant("bar") ); - assert( Variant("foo") != Variant("bar") ); - - { - auto v1 = Variant(42); - auto v2 = Variant("foo"); - - int[Variant] hash; - hash[v1] = 0; - hash[v2] = 1; - - assert( hash[v1] == 0 ); - assert( hash[v2] == 1 ); - } - - { - int[char[]] hash; - hash["a"] = 1; - hash["b"] = 2; - hash["c"] = 3; - Variant vhash = hash; - - assert( vhash.get!(int[char[]])["a"] == 1 ); - assert( vhash.get!(int[char[]])["b"] == 2 ); - assert( vhash.get!(int[char[]])["c"] == 3 ); - } -} - -@system unittest -{ - // check comparisons incompatible with AllowedTypes - Algebraic!int v = 2; - - assert(v == 2); - assert(v < 3); - static assert(!__traits(compiles, () => v == long.max)); - static assert(!__traits(compiles, () => v == null)); - static assert(!__traits(compiles, () => v < long.max)); - static assert(!__traits(compiles, () => v > null)); -} - -// https://issues.dlang.org/show_bug.cgi?id=1558 -@system unittest -{ - Variant va=1; - Variant vb=-2; - assert((va+vb).get!(int) == -1); - assert((va-vb).get!(int) == 3); -} - -@system unittest -{ - Variant a; - a=5; - Variant b; - b=a; - Variant[] c; - c = variantArray(1, 2, 3.0, "hello", 4); - assert(c[3] == "hello"); -} - -@system unittest -{ - Variant v = 5; - assert(!__traits(compiles, v.coerce!(bool delegate()))); -} - - -@system unittest -{ - struct Huge { - real a, b, c, d, e, f, g; - } - - Huge huge; - huge.e = 42; - Variant v; - v = huge; // Compile time error. - assert(v.get!(Huge).e == 42); -} - -@system unittest -{ - const x = Variant(42); - auto y1 = x.get!(const int); - // @@@BUG@@@ - //auto y2 = x.get!(immutable int)(); -} - -// test iteration -@system unittest -{ - auto v = Variant([ 1, 2, 3, 4 ][]); - auto j = 0; - foreach (int i; v) - { - assert(i == ++j); - } - assert(j == 4); -} - -// test convertibility -@system unittest -{ - auto v = Variant("abc".dup); - assert(v.convertsTo!(char[])); -} - -// https://issues.dlang.org/show_bug.cgi?id=5424 -@system unittest -{ - interface A { - void func1(); - } - static class AC: A { - void func1() { - } - } - - A a = new AC(); - a.func1(); - Variant b = Variant(a); -} - -// https://issues.dlang.org/show_bug.cgi?id=7070 -@system unittest -{ - Variant v; - v = null; -} - -// Class and interface opEquals, https://issues.dlang.org/show_bug.cgi?id=12157 -@system unittest -{ - class Foo { } - - class DerivedFoo : Foo { } - - Foo f1 = new Foo(); - Foo f2 = new DerivedFoo(); - - Variant v1 = f1, v2 = f2; - assert(v1 == f1); - assert(v1 != new Foo()); - assert(v1 != f2); - assert(v2 != v1); - assert(v2 == f2); -} - -// Const parameters with opCall, https://issues.dlang.org/show_bug.cgi?id=11361 -@system unittest -{ - static string t1(string c) { - return c ~ "a"; - } - - static const(char)[] t2(const(char)[] p) { - return p ~ "b"; - } - - static char[] t3(int p) { - import std.conv : text; - return p.text.dup; - } - - Variant v1 = &t1; - Variant v2 = &t2; - Variant v3 = &t3; - - assert(v1("abc") == "abca"); - assert(v1("abc").type == typeid(string)); - assert(v2("abc") == "abcb"); - - assert(v2(cast(char[])("abc".dup)) == "abcb"); - assert(v2("abc").type == typeid(const(char)[])); - - assert(v3(4) == ['4']); - assert(v3(4).type == typeid(char[])); -} - -// https://issues.dlang.org/show_bug.cgi?id=12071 -@system unittest -{ - static struct Structure { int data; } - alias VariantTest = Algebraic!(Structure delegate() pure nothrow @nogc @safe); - - bool called = false; - Structure example() pure nothrow @nogc @safe - { - called = true; - return Structure.init; - } - auto m = VariantTest(&example); - m(); - assert(called); -} - -// Ordering comparisons of incompatible types -// e.g. https://issues.dlang.org/show_bug.cgi?id=7990 -@system unittest -{ - import std.exception : assertThrown; - assertThrown!VariantException(Variant(3) < "a"); - assertThrown!VariantException("a" < Variant(3)); - assertThrown!VariantException(Variant(3) < Variant("a")); - - assertThrown!VariantException(Variant.init < Variant(3)); - assertThrown!VariantException(Variant(3) < Variant.init); -} - -// Handling of unordered types -// https://issues.dlang.org/show_bug.cgi?id=9043 -@system unittest -{ - import std.exception : assertThrown; - static struct A { int a; } - - assert(Variant(A(3)) != A(4)); - - assertThrown!VariantException(Variant(A(3)) < A(4)); - assertThrown!VariantException(A(3) < Variant(A(4))); - assertThrown!VariantException(Variant(A(3)) < Variant(A(4))); -} - -// Handling of empty types and arrays -// https://issues.dlang.org/show_bug.cgi?id=10958 -@system unittest -{ - class EmptyClass { } - struct EmptyStruct { } - alias EmptyArray = void[0]; - alias Alg = Algebraic!(EmptyClass, EmptyStruct, EmptyArray); - - Variant testEmpty(T)() - { - T inst; - Variant v = inst; - assert(v.get!T == inst); - assert(v.peek!T !is null); - assert(*v.peek!T == inst); - Alg alg = inst; - assert(alg.get!T == inst); - return v; - } - - testEmpty!EmptyClass(); - testEmpty!EmptyStruct(); - testEmpty!EmptyArray(); - - // EmptyClass/EmptyStruct sizeof is 1, so we have this to test just size 0. - EmptyArray arr = EmptyArray.init; - Algebraic!(EmptyArray) a = arr; - assert(a.length == 0); - assert(a.get!EmptyArray == arr); -} - -// Handling of void function pointers / delegates -// https://issues.dlang.org/show_bug.cgi?id=11360 -@system unittest -{ - static void t1() { } - Variant v = &t1; - assert(v() == Variant.init); - - static int t2() { return 3; } - Variant v2 = &t2; - assert(v2() == 3); -} - -// Using peek for large structs -// https://issues.dlang.org/show_bug.cgi?id=8580 -@system unittest -{ - struct TestStruct(bool pad) - { - int val1; - static if (pad) - ubyte[Variant.size] padding; - int val2; - } - - void testPeekWith(T)() - { - T inst; - inst.val1 = 3; - inst.val2 = 4; - Variant v = inst; - T* original = v.peek!T; - assert(original.val1 == 3); - assert(original.val2 == 4); - original.val1 = 6; - original.val2 = 8; - T modified = v.get!T; - assert(modified.val1 == 6); - assert(modified.val2 == 8); - } - - testPeekWith!(TestStruct!false)(); - testPeekWith!(TestStruct!true)(); -} - -// https://issues.dlang.org/show_bug.cgi?id=18780 -@system unittest -{ - int x = 7; - Variant a = x; - assert(a.convertsTo!ulong); - assert(a.convertsTo!uint); -} - -/** - * Applies a delegate or function to the given $(LREF Algebraic) depending on the held type, - * ensuring that all types are handled by the visiting functions. - * - * The delegate or function having the currently held value as parameter is called - * with `variant`'s current value. Visiting handlers are passed - * in the template parameter list. - * It is statically ensured that all held types of - * `variant` are handled across all handlers. - * `visit` allows delegates and static functions to be passed - * as parameters. - * - * If a function with an untyped parameter is specified, this function is called - * when the variant contains a type that does not match any other function. - * This can be used to apply the same function across multiple possible types. - * Exactly one generic function is allowed. - * - * If a function without parameters is specified, this function is called - * when `variant` doesn't hold a value. Exactly one parameter-less function - * is allowed. - * - * Duplicate overloads matching the same type in one of the visitors are disallowed. - * - * Returns: The return type of visit is deduced from the visiting functions and must be - * the same across all overloads. - * Throws: $(LREF VariantException) if `variant` doesn't hold a value and no - * parameter-less fallback function is specified. - */ -template visit(Handlers...) -if (Handlers.length > 0) -{ - /// - auto visit(VariantType)(VariantType variant) - if (isAlgebraic!VariantType) - { - return visitImpl!(true, VariantType, Handlers)(variant); - } -} - -/// -@system unittest -{ - Algebraic!(int, string) variant; - - variant = 10; - assert(variant.visit!((string s) => cast(int) s.length, - (int i) => i)() - == 10); - variant = "string"; - assert(variant.visit!((int i) => i, - (string s) => cast(int) s.length)() - == 6); - - // Error function usage - Algebraic!(int, string) emptyVar; - auto rslt = emptyVar.visit!((string s) => cast(int) s.length, - (int i) => i, - () => -1)(); - assert(rslt == -1); - - // Generic function usage - Algebraic!(int, float, real) number = 2; - assert(number.visit!(x => x += 1) == 3); - - // Generic function for int/float with separate behavior for string - Algebraic!(int, float, string) something = 2; - assert(something.visit!((string s) => s.length, x => x) == 2); // generic - something = "asdf"; - assert(something.visit!((string s) => s.length, x => x) == 4); // string - - // Generic handler and empty handler - Algebraic!(int, float, real) empty2; - assert(empty2.visit!(x => x + 1, () => -1) == -1); -} - -@system unittest -{ - Algebraic!(size_t, string) variant; - - // not all handled check - static assert(!__traits(compiles, variant.visit!((size_t i){ })() )); - - variant = cast(size_t) 10; - auto which = 0; - variant.visit!( (string s) => which = 1, - (size_t i) => which = 0 - )(); - - // integer overload was called - assert(which == 0); - - // mustn't compile as generic Variant not supported - Variant v; - static assert(!__traits(compiles, v.visit!((string s) => which = 1, - (size_t i) => which = 0 - )() - )); - - static size_t func(string s) { - return s.length; - } - - variant = "test"; - assert( 4 == variant.visit!(func, - (size_t i) => i - )()); - - Algebraic!(int, float, string) variant2 = 5.0f; - // Shouldn' t compile as float not handled by visitor. - static assert(!__traits(compiles, variant2.visit!( - (int _) {}, - (string _) {})())); - - Algebraic!(size_t, string, float) variant3; - variant3 = 10.0f; - auto floatVisited = false; - - assert(variant3.visit!( - (float f) { floatVisited = true; return cast(size_t) f; }, - func, - (size_t i) { return i; } - )() == 10); - assert(floatVisited == true); - - Algebraic!(float, string) variant4; - - assert(variant4.visit!(func, (float f) => cast(size_t) f, () => size_t.max)() == size_t.max); - - // double error func check - static assert(!__traits(compiles, - visit!(() => size_t.max, func, (float f) => cast(size_t) f, () => size_t.max)(variant4)) - ); -} - -// disallow providing multiple generic handlers to visit -// disallow a generic handler that does not apply to all types -@system unittest -{ - Algebraic!(int, float) number = 2; - // ok, x + 1 valid for int and float - static assert( __traits(compiles, number.visit!(x => x + 1))); - // bad, two generic handlers - static assert(!__traits(compiles, number.visit!(x => x + 1, x => x + 2))); - // bad, x ~ "a" does not apply to int or float - static assert(!__traits(compiles, number.visit!(x => x ~ "a"))); - // bad, x ~ "a" does not apply to int or float - static assert(!__traits(compiles, number.visit!(x => x + 1, x => x ~ "a"))); - - Algebraic!(int, string) maybenumber = 2; - // ok, x ~ "a" valid for string, x + 1 valid for int, only 1 generic - static assert( __traits(compiles, maybenumber.visit!((string x) => x ~ "a", x => "foobar"[0 .. x + 1]))); - // bad, x ~ "a" valid for string but not int - static assert(!__traits(compiles, maybenumber.visit!(x => x ~ "a"))); - // bad, two generics, each only applies in one case - static assert(!__traits(compiles, maybenumber.visit!(x => x + 1, x => x ~ "a"))); -} - -/** - * Behaves as $(LREF visit) but doesn't enforce that all types are handled - * by the visiting functions. - * - * If a parameter-less function is specified it is called when - * either `variant` doesn't hold a value or holds a type - * which isn't handled by the visiting functions. - * - * Returns: The return type of tryVisit is deduced from the visiting functions and must be - * the same across all overloads. - * Throws: $(LREF VariantException) if `variant` doesn't hold a value or - * `variant` holds a value which isn't handled by the visiting functions, - * when no parameter-less fallback function is specified. - */ -template tryVisit(Handlers...) -if (Handlers.length > 0) -{ - /// - auto tryVisit(VariantType)(VariantType variant) - if (isAlgebraic!VariantType) - { - return visitImpl!(false, VariantType, Handlers)(variant); - } -} - -/// -@system unittest -{ - Algebraic!(int, string) variant; - - variant = 10; - auto which = -1; - variant.tryVisit!((int i) { which = 0; })(); - assert(which == 0); - - // Error function usage - variant = "test"; - variant.tryVisit!((int i) { which = 0; }, - () { which = -100; })(); - assert(which == -100); -} - -@system unittest -{ - import std.exception : assertThrown; - Algebraic!(int, string) variant; - - variant = 10; - auto which = -1; - variant.tryVisit!((int i){ which = 0; })(); - - assert(which == 0); - - variant = "test"; - - assertThrown!VariantException(variant.tryVisit!((int i) { which = 0; })()); - - void errorfunc() - { - which = -1; - } - - variant.tryVisit!((int i) { which = 0; }, errorfunc)(); - - assert(which == -1); -} - -private template isAlgebraic(Type) -{ - static if (is(Type _ == VariantN!T, T...)) - enum isAlgebraic = T.length >= 2; // T[0] == maxDataSize, T[1..$] == AllowedTypesParam - else - enum isAlgebraic = false; -} - -@system unittest -{ - static assert(!isAlgebraic!(Variant)); - static assert( isAlgebraic!(Algebraic!(string))); - static assert( isAlgebraic!(Algebraic!(int, int[]))); -} - -private auto visitImpl(bool Strict, VariantType, Handler...)(VariantType variant) -if (isAlgebraic!VariantType && Handler.length > 0) -{ - alias AllowedTypes = VariantType.AllowedTypes; - - - /** - * Returns: Struct where `indices` is an array which - * contains at the n-th position the index in Handler which takes the - * n-th type of AllowedTypes. If an Handler doesn't match an - * AllowedType, -1 is set. If a function in the delegates doesn't - * have parameters, the field `exceptionFuncIdx` is set; - * otherwise it's -1. - */ - auto visitGetOverloadMap() - { - struct Result { - int[AllowedTypes.length] indices; - int exceptionFuncIdx = -1; - int generalFuncIdx = -1; - } - - Result result; - - enum int nonmatch = () - { - foreach (int dgidx, dg; Handler) - { - bool found = false; - foreach (T; AllowedTypes) - { - found |= __traits(compiles, { static assert(isSomeFunction!(dg!T)); }); - found |= __traits(compiles, (T t) { dg(t); }); - found |= __traits(compiles, dg()); - } - if (!found) return dgidx; - } - return -1; - }(); - static assert(nonmatch == -1, "No match for visit handler #"~ - nonmatch.stringof~" ("~Handler[nonmatch].stringof~")"); - - foreach (tidx, T; AllowedTypes) - { - bool added = false; - foreach (dgidx, dg; Handler) - { - // Handle normal function objects - static if (isSomeFunction!dg) - { - alias Params = Parameters!dg; - static if (Params.length == 0) - { - // Just check exception functions in the first - // inner iteration (over delegates) - if (tidx > 0) - continue; - else - { - if (result.exceptionFuncIdx != -1) - assert(false, "duplicate parameter-less (error-)function specified"); - result.exceptionFuncIdx = dgidx; - } - } - else static if (is(Params[0] == T) || is(Unqual!(Params[0]) == T)) - { - if (added) - assert(false, "duplicate overload specified for type '" ~ T.stringof ~ "'"); - - added = true; - result.indices[tidx] = dgidx; - } - } - else static if (__traits(compiles, { static assert(isSomeFunction!(dg!T)); })) - { - assert(result.generalFuncIdx == -1 || - result.generalFuncIdx == dgidx, - "Only one generic visitor function is allowed"); - result.generalFuncIdx = dgidx; - } - // Handle composite visitors with opCall overloads - } - - if (!added) - result.indices[tidx] = -1; - } - - return result; - } - - enum HandlerOverloadMap = visitGetOverloadMap(); - - if (!variant.hasValue) - { - // Call the exception function. The HandlerOverloadMap - // will have its exceptionFuncIdx field set to value != -1 if an - // exception function has been specified; otherwise we just through an exception. - static if (HandlerOverloadMap.exceptionFuncIdx != -1) - return Handler[ HandlerOverloadMap.exceptionFuncIdx ](); - else - throw new VariantException("variant must hold a value before being visited."); - } - - foreach (idx, T; AllowedTypes) - { - if (auto ptr = variant.peek!T) - { - enum dgIdx = HandlerOverloadMap.indices[idx]; - - static if (dgIdx == -1) - { - static if (HandlerOverloadMap.generalFuncIdx >= 0) - return Handler[HandlerOverloadMap.generalFuncIdx](*ptr); - else static if (Strict) - static assert(false, "overload for type '" ~ T.stringof ~ "' hasn't been specified"); - else static if (HandlerOverloadMap.exceptionFuncIdx != -1) - return Handler[HandlerOverloadMap.exceptionFuncIdx](); - else - throw new VariantException( - "variant holds value of type '" - ~ T.stringof ~ - "' but no visitor has been provided" - ); - } - else - { - return Handler[ dgIdx ](*ptr); - } - } - } - - assert(false); -} - -// https://issues.dlang.org/show_bug.cgi?id=21253 -@system unittest -{ - static struct A { int n; } - static struct B { } - - auto a = Algebraic!(A, B)(B()); - assert(a.visit!( - (B _) => 42, - (a ) => a.n - ) == 42); -} - -@system unittest -{ - // validate that visit can be called with a const type - struct Foo { int depth; } - struct Bar { int depth; } - alias FooBar = Algebraic!(Foo, Bar); - - int depth(in FooBar fb) { - return fb.visit!((Foo foo) => foo.depth, - (Bar bar) => bar.depth); - } - - FooBar fb = Foo(3); - assert(depth(fb) == 3); -} - -// https://issues.dlang.org/show_bug.cgi?id=16383 -@system unittest -{ - class Foo {this() immutable {}} - alias V = Algebraic!(immutable Foo); - - auto x = V(new immutable Foo).visit!( - (immutable(Foo) _) => 3 - ); - assert(x == 3); -} - -// https://issues.dlang.org/show_bug.cgi?id=5310 -@system unittest -{ - const Variant a; - assert(a == a); - Variant b; - assert(a == b); - assert(b == a); -} - -@system unittest -{ - const Variant a = [2]; - assert(a[0] == 2); -} - -// https://issues.dlang.org/show_bug.cgi?id=10017 -@system unittest -{ - static struct S - { - ubyte[Variant.size + 1] s; - } - - Variant v1, v2; - v1 = S(); // the payload is allocated on the heap - v2 = v1; // AssertError: target must be non-null - assert(v1 == v2); -} - -// https://issues.dlang.org/show_bug.cgi?id=7069 -@system unittest -{ - import std.exception : assertThrown; - Variant v; - - int i = 10; - v = i; - static foreach (qual; AliasSeq!(Alias, ConstOf)) - { - assert(v.get!(qual!int) == 10); - assert(v.get!(qual!float) == 10.0f); - } - static foreach (qual; AliasSeq!(ImmutableOf, SharedOf, SharedConstOf)) - { - assertThrown!VariantException(v.get!(qual!int)); - } - - const(int) ci = 20; - v = ci; - static foreach (qual; AliasSeq!(ConstOf)) - { - assert(v.get!(qual!int) == 20); - assert(v.get!(qual!float) == 20.0f); - } - static foreach (qual; AliasSeq!(Alias, ImmutableOf, SharedOf, SharedConstOf)) - { - assertThrown!VariantException(v.get!(qual!int)); - assertThrown!VariantException(v.get!(qual!float)); - } - - immutable(int) ii = ci; - v = ii; - static foreach (qual; AliasSeq!(ImmutableOf, ConstOf, SharedConstOf)) - { - assert(v.get!(qual!int) == 20); - assert(v.get!(qual!float) == 20.0f); - } - static foreach (qual; AliasSeq!(Alias, SharedOf)) - { - assertThrown!VariantException(v.get!(qual!int)); - assertThrown!VariantException(v.get!(qual!float)); - } - - int[] ai = [1,2,3]; - v = ai; - static foreach (qual; AliasSeq!(Alias, ConstOf)) - { - assert(v.get!(qual!(int[])) == [1,2,3]); - assert(v.get!(qual!(int)[]) == [1,2,3]); - } - static foreach (qual; AliasSeq!(ImmutableOf, SharedOf, SharedConstOf)) - { - assertThrown!VariantException(v.get!(qual!(int[]))); - assertThrown!VariantException(v.get!(qual!(int)[])); - } - - const(int[]) cai = [4,5,6]; - v = cai; - static foreach (qual; AliasSeq!(ConstOf)) - { - assert(v.get!(qual!(int[])) == [4,5,6]); - assert(v.get!(qual!(int)[]) == [4,5,6]); - } - static foreach (qual; AliasSeq!(Alias, ImmutableOf, SharedOf, SharedConstOf)) - { - assertThrown!VariantException(v.get!(qual!(int[]))); - assertThrown!VariantException(v.get!(qual!(int)[])); - } - - immutable(int[]) iai = [7,8,9]; - v = iai; - //assert(v.get!(immutable(int[])) == [7,8,9]); // Bug ??? runtime error - assert(v.get!(immutable(int)[]) == [7,8,9]); - assert(v.get!(const(int[])) == [7,8,9]); - assert(v.get!(const(int)[]) == [7,8,9]); - //assert(v.get!(shared(const(int[]))) == cast(shared const)[7,8,9]); // Bug ??? runtime error - //assert(v.get!(shared(const(int))[]) == cast(shared const)[7,8,9]); // Bug ??? runtime error - static foreach (qual; AliasSeq!(Alias)) - { - assertThrown!VariantException(v.get!(qual!(int[]))); - assertThrown!VariantException(v.get!(qual!(int)[])); - } - - class A {} - class B : A {} - B b = new B(); - v = b; - static foreach (qual; AliasSeq!(Alias, ConstOf)) - { - assert(v.get!(qual!B) is b); - assert(v.get!(qual!A) is b); - assert(v.get!(qual!Object) is b); - } - static foreach (qual; AliasSeq!(ImmutableOf, SharedOf, SharedConstOf)) - { - assertThrown!VariantException(v.get!(qual!B)); - assertThrown!VariantException(v.get!(qual!A)); - assertThrown!VariantException(v.get!(qual!Object)); - } - - const(B) cb = new B(); - v = cb; - static foreach (qual; AliasSeq!(ConstOf)) - { - assert(v.get!(qual!B) is cb); - assert(v.get!(qual!A) is cb); - assert(v.get!(qual!Object) is cb); - } - static foreach (qual; AliasSeq!(Alias, ImmutableOf, SharedOf, SharedConstOf)) - { - assertThrown!VariantException(v.get!(qual!B)); - assertThrown!VariantException(v.get!(qual!A)); - assertThrown!VariantException(v.get!(qual!Object)); - } - - immutable(B) ib = new immutable(B)(); - v = ib; - static foreach (qual; AliasSeq!(ImmutableOf, ConstOf, SharedConstOf)) - { - assert(v.get!(qual!B) is ib); - assert(v.get!(qual!A) is ib); - assert(v.get!(qual!Object) is ib); - } - static foreach (qual; AliasSeq!(Alias, SharedOf)) - { - assertThrown!VariantException(v.get!(qual!B)); - assertThrown!VariantException(v.get!(qual!A)); - assertThrown!VariantException(v.get!(qual!Object)); - } - - shared(B) sb = new shared B(); - v = sb; - static foreach (qual; AliasSeq!(SharedOf, SharedConstOf)) - { - assert(v.get!(qual!B) is sb); - assert(v.get!(qual!A) is sb); - assert(v.get!(qual!Object) is sb); - } - static foreach (qual; AliasSeq!(Alias, ImmutableOf, ConstOf)) - { - assertThrown!VariantException(v.get!(qual!B)); - assertThrown!VariantException(v.get!(qual!A)); - assertThrown!VariantException(v.get!(qual!Object)); - } - - shared(const(B)) scb = new shared const B(); - v = scb; - static foreach (qual; AliasSeq!(SharedConstOf)) - { - assert(v.get!(qual!B) is scb); - assert(v.get!(qual!A) is scb); - assert(v.get!(qual!Object) is scb); - } - static foreach (qual; AliasSeq!(Alias, ConstOf, ImmutableOf, SharedOf)) - { - assertThrown!VariantException(v.get!(qual!B)); - assertThrown!VariantException(v.get!(qual!A)); - assertThrown!VariantException(v.get!(qual!Object)); - } -} - -// https://issues.dlang.org/show_bug.cgi?id=12540 -@system unittest -{ - static struct DummyScope - { - alias Alias12540 = Algebraic!Class12540; - - static class Class12540 - { - Alias12540 entity; - } - } -} - -@system unittest -{ - // https://issues.dlang.org/show_bug.cgi?id=10194 - // Also test for elaborate copying - static struct S - { - @disable this(); - this(int dummy) - { - ++cnt; - } - - this(this) - { - ++cnt; - } - - @disable S opAssign(); - - ~this() - { - --cnt; - assert(cnt >= 0); - } - static int cnt = 0; - } - - { - Variant v; - { - v = S(0); - assert(S.cnt == 1); - } - assert(S.cnt == 1); - - // assigning a new value should destroy the existing one - v = 0; - assert(S.cnt == 0); - - // destroying the variant should destroy it's current value - v = S(0); - assert(S.cnt == 1); - } - assert(S.cnt == 0); -} - -@system unittest -{ - // https://issues.dlang.org/show_bug.cgi?id=13300 - static struct S - { - this(this) {} - ~this() {} - } - - static assert( hasElaborateCopyConstructor!(Variant)); - static assert(!hasElaborateCopyConstructor!(Algebraic!bool)); - static assert( hasElaborateCopyConstructor!(Algebraic!S)); - static assert( hasElaborateCopyConstructor!(Algebraic!(bool, S))); - - static assert( hasElaborateDestructor!(Variant)); - static assert(!hasElaborateDestructor!(Algebraic!bool)); - static assert( hasElaborateDestructor!(Algebraic!S)); - static assert( hasElaborateDestructor!(Algebraic!(bool, S))); - - import std.array; - alias Value = Algebraic!bool; - - static struct T - { - Value value; - @disable this(); - } - auto a = appender!(T[]); -} - -// https://issues.dlang.org/show_bug.cgi?id=13871 -@system unittest -{ - alias A = Algebraic!(int, typeof(null)); - static struct B { A value; } - alias C = std.variant.Algebraic!B; - - C var; - var = C(B()); -} - -@system unittest -{ - import std.exception : assertThrown, assertNotThrown; - // Make sure Variant can handle types with opDispatch but no length field. - struct SWithNoLength - { - void opDispatch(string s)() { } - } - - struct SWithLength - { - @property int opDispatch(string s)() - { - // Assume that s == "length" - return 5; // Any value is OK for test. - } - } - - SWithNoLength sWithNoLength; - Variant v = sWithNoLength; - assertThrown!VariantException(v.length); - - SWithLength sWithLength; - v = sWithLength; - assertNotThrown!VariantException(v.get!SWithLength.length); - assertThrown!VariantException(v.length); -} - -// https://issues.dlang.org/show_bug.cgi?id=13534 -@system unittest -{ - static assert(!__traits(compiles, () @safe { - auto foo() @system { return 3; } - auto v = Variant(&foo); - v(); // foo is called in safe code!? - })); -} - -// https://issues.dlang.org/show_bug.cgi?id=15039 -@system unittest -{ - import std.typecons; - import std.variant; - - alias IntTypedef = Typedef!int; - alias Obj = Algebraic!(int, IntTypedef, This[]); - - Obj obj = 1; - - obj.visit!( - (int x) {}, - (IntTypedef x) {}, - (Obj[] x) {}, - ); -} - -// https://issues.dlang.org/show_bug.cgi?id=15791 -@system unittest -{ - int n = 3; - struct NS1 { int foo() { return n + 10; } } - struct NS2 { int foo() { return n * 10; } } - - Variant v; - v = NS1(); - assert(v.get!NS1.foo() == 13); - v = NS2(); - assert(v.get!NS2.foo() == 30); -} - -// https://issues.dlang.org/show_bug.cgi?id=15827 -@system unittest -{ - static struct Foo15827 { Variant v; this(Foo15827 v) {} } - Variant v = Foo15827.init; -} - -// https://issues.dlang.org/show_bug.cgi?id=18934 -@system unittest -{ - static struct S - { - const int x; - } - - auto s = S(42); - Variant v = s; - auto s2 = v.get!S; - assert(s2.x == 42); - Variant v2 = v; // support copying from one variant to the other - v2 = S(2); - v = v2; - assert(v.get!S.x == 2); -} - -// https://issues.dlang.org/show_bug.cgi?id=19200 -@system unittest -{ - static struct S - { - static int opBinaryRight(string op : "|", T)(T rhs) - { - return 3; - } - } - - S s; - Variant v; - auto b = v | s; - assert(b == 3); -} - -// https://issues.dlang.org/show_bug.cgi?id=11061 -@system unittest -{ - int[4] el = [0, 1, 2, 3]; - int[3] nl = [0, 1, 2]; - Variant v1 = el; - assert(v1 == el); // Compare Var(static) to static - assert(v1 != nl); // Compare static arrays of different length - assert(v1 == [0, 1, 2, 3]); // Compare Var(static) to dynamic. - assert(v1 != [0, 1, 2]); - int[] dyn = [0, 1, 2, 3]; - v1 = dyn; - assert(v1 == el); // Compare Var(dynamic) to static. - assert(v1 == [0, 1] ~ [2, 3]); // Compare Var(dynamic) to dynamic -} - -// https://issues.dlang.org/show_bug.cgi?id=15940 -@system unittest -{ - class C { } - struct S - { - C a; - alias a this; - } - S s = S(new C()); - auto v = Variant(s); // compile error -} - -@system unittest -{ - // Test if we don't have scoping issues. - Variant createVariant(int[] input) - { - int[2] el = [input[0], input[1]]; - Variant v = el; - return v; - } - Variant v = createVariant([0, 1]); - createVariant([2, 3]); - assert(v == [0,1]); -} - -// https://issues.dlang.org/show_bug.cgi?id=19994 -@safe unittest -{ - alias Inner = Algebraic!(This*); - alias Outer = Algebraic!(Inner, This*); - - static assert(is(Outer.AllowedTypes == AliasSeq!(Inner, Outer*))); -} - -// https://issues.dlang.org/show_bug.cgi?id=21296 -@system unittest -{ - immutable aa = ["0": 0]; - auto v = Variant(aa); // compile error -} diff --git a/phobos/std/windows/charset.d b/phobos/std/windows/charset.d deleted file mode 100644 index ce33616..0000000 --- a/phobos/std/windows/charset.d +++ /dev/null @@ -1,111 +0,0 @@ -// Written in the D programming language. - -/** - * Support UTF-8 on Windows 95, 98 and ME systems. - * - * Copyright: Copyright The D Language Foundation" 2005 - 2009. - * License: $(HTTP www.boost.org/LICENSE_1_0.txt, Boost License 1.0). - * Authors: $(HTTP digitalmars.com, Walter Bright) - */ -/* Copyright The D Language Foundation" 2005 - 2009. - * 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.windows.charset; - -version (StdDdoc) -{ - /****************************************** - * Converts the UTF-8 string s into a null-terminated string in a Windows - * 8-bit character set. - * - * Params: - * s = UTF-8 string to convert. - * codePage = is the number of the target codepage, or - * 0 - ANSI, - * 1 - OEM, - * 2 - Mac - * - * Authors: - * yaneurao, Walter Bright, Stewart Gordon - */ - const(char)* toMBSz(scope const(char)[] s, uint codePage = 0); - - /********************************************** - * Converts the null-terminated string s from a Windows 8-bit character set - * into a UTF-8 char array. - * - * Params: - * s = UTF-8 string to convert. - * codePage = is the number of the source codepage, or - * 0 - ANSI, - * 1 - OEM, - * 2 - Mac - * Authors: Stewart Gordon, Walter Bright - */ - string fromMBSz(immutable(char)* s, int codePage = 0); -} -else: - -version (Windows): - -import core.sys.windows.winbase, core.sys.windows.winnls; -import std.conv; -import std.string; -import std.windows.syserror; - -import std.internal.cstring; - -const(char)* toMBSz(scope const(char)[] s, uint codePage = 0) -{ - // Only need to do this if any chars have the high bit set - foreach (char c; s) - { - if (c >= 0x80) - { - char[] result; - int readLen; - auto wsTmp = s.tempCStringW(); - result.length = WideCharToMultiByte(codePage, 0, wsTmp, -1, null, 0, - null, null); - - if (result.length) - { - readLen = WideCharToMultiByte(codePage, 0, wsTmp, -1, result.ptr, - to!int(result.length), null, null); - } - - wenforce(readLen && readLen == result.length, "Couldn't convert string"); - return result.ptr; - } - } - return std.string.toStringz(s); -} - -string fromMBSz(return scope immutable(char)* s, int codePage = 0) -{ - const(char)* c; - - for (c = s; *c != 0; c++) - { - if (*c >= 0x80) - { - wchar[] result; - int readLen; - - result.length = MultiByteToWideChar(codePage, 0, s, -1, null, 0); - - if (result.length) - { - readLen = MultiByteToWideChar(codePage, 0, s, -1, result.ptr, - to!int(result.length)); - } - - wenforce(readLen && readLen == result.length, "Couldn't convert string"); - - return result[0 .. result.length-1].to!string; // omit trailing null - } - } - return s[0 .. c-s]; // string is ASCII, no conversion necessary -} diff --git a/phobos/std/windows/registry.d b/phobos/std/windows/registry.d deleted file mode 100644 index 7ee1f7a..0000000 --- a/phobos/std/windows/registry.d +++ /dev/null @@ -1,1863 +0,0 @@ -/** - This library provides Win32 Registry facilities. - - Copyright: Copyright 2003-2004 by Matthew Wilson and Synesis Software - Written by Matthew Wilson - - License: $(HTTP www.boost.org/LICENSE_1_0.txt, Boost License 1.0). - - Author: Matthew Wilson, Kenji Hara - - History: - Created 15th March 2003, - Updated 25th April 2004, - - Source: $(PHOBOSSRC std/windows/registry.d) -*/ -/* ///////////////////////////////////////////////////////////////////////////// - * - * This software is provided 'as-is', without any express or implied - * warranty. In no event will the authors be held liable for any damages - * arising from the use of this software. - * - * Permission is granted to anyone to use this software for any purpose, - * including commercial applications, and to alter it and redistribute it - * freely, in both source and binary form, subject to the following - * restrictions: - * - * - The origin of this software must not be misrepresented; you must not - * claim that you wrote the original software. If you use this software - * in a product, an acknowledgment in the product documentation would be - * appreciated but is not required. - * - Altered source versions must be plainly marked as such, and must not - * be misrepresented as being the original software. - * - This notice may not be removed or altered from any source - * distribution. - * - * ////////////////////////////////////////////////////////////////////////// */ -module std.windows.registry; -version (Windows): - -import core.sys.windows.winbase, core.sys.windows.windef, core.sys.windows.winreg; -import std.array; -import std.conv; -import std.exception; -import std.internal.cstring; -import std.internal.windows.advapi32; -import std.system : Endian, endian; -import std.windows.syserror; - -//debug = winreg; -debug(winreg) import std.stdio; - -private -{ - import core.sys.windows.winbase : lstrlenW; - - void enforceSucc(LONG res, lazy string message, string fn = __FILE__, size_t ln = __LINE__) - { - if (res != ERROR_SUCCESS) - throw new RegistryException(message, res, fn, ln); - } -} - -/* ************* Exceptions *************** */ - -// Do not use. Left for compatibility. -class Win32Exception : WindowsException -{ - @safe - this(string message, string fn = __FILE__, size_t ln = __LINE__, Throwable next = null) - { - super(0, message, fn, ln); - } - - @safe - this(string message, int errnum, string fn = __FILE__, size_t ln = __LINE__, Throwable next = null) - { - super(errnum, message, fn, ln); - } - - @property int error() { return super.code; } -} - -version (StdUnittest) import std.string : startsWith, endsWith; - -@safe unittest -{ - // Test that we can throw and catch one by its own type - string message = "Test W1"; - - auto e = collectException!Win32Exception( - enforce(false, new Win32Exception(message))); - assert(e.msg.startsWith(message)); -} - -@system unittest -{ - // ditto - string message = "Test W2"; - int code = 5; - - auto e = collectException!Win32Exception( - enforce(false, new Win32Exception(message, code))); - assert(e.error == code); - assert(e.msg.startsWith(message)); -} - -/** - Exception class thrown by the std.windows.registry classes. - */ -class RegistryException - : Win32Exception -{ -public: - /** - Creates an instance of the exception. - - Params: - message = The message associated with the exception. - */ - @safe - this(string message, string fn = __FILE__, size_t ln = __LINE__, Throwable next = null) - { - super(message, fn, ln, next); - } - - /** - Creates an instance of the exception, with the given. - - Params: - message = The message associated with the exception. - error = The Win32 error number associated with the exception. - */ - @safe - this(string message, int error, string fn = __FILE__, size_t ln = __LINE__, Throwable next = null) - { - super(message, error, fn, ln, next); - } -} - -@system unittest -{ - // (i) Test that we can throw and catch one by its own type - string message = "Test 1"; - int code = 3; - - auto e = collectException!RegistryException( - enforce(false, new RegistryException(message, code))); - assert(e.error == code); - assert(e.msg.startsWith(message)); -} - -@safe unittest -{ - // ditto - string message = "Test 2"; - - auto e = collectException!RegistryException( - enforce(false, new RegistryException(message))); - assert(e.msg.startsWith(message)); -} - -/* ************* public enumerations *************** */ - -/** - Enumeration of the recognised registry access modes. - */ -enum REGSAM -{ - KEY_QUERY_VALUE = 0x0001, /// Permission to query subkey data - KEY_SET_VALUE = 0x0002, /// Permission to set subkey data - KEY_CREATE_SUB_KEY = 0x0004, /// Permission to create subkeys - KEY_ENUMERATE_SUB_KEYS = 0x0008, /// Permission to enumerate subkeys - KEY_NOTIFY = 0x0010, /// Permission for change notification - KEY_CREATE_LINK = 0x0020, /// Permission to create a symbolic link - KEY_WOW64_32KEY = 0x0200, /// Enables a 64- or 32-bit application to open a 32-bit key - KEY_WOW64_64KEY = 0x0100, /// Enables a 64- or 32-bit application to open a 64-bit key - KEY_WOW64_RES = 0x0300, /// - KEY_READ = (STANDARD_RIGHTS_READ - | KEY_QUERY_VALUE | KEY_ENUMERATE_SUB_KEYS | KEY_NOTIFY) - & ~(SYNCHRONIZE), - /// Combines the STANDARD_RIGHTS_READ, KEY_QUERY_VALUE, - /// KEY_ENUMERATE_SUB_KEYS, and KEY_NOTIFY access rights - KEY_WRITE = (STANDARD_RIGHTS_WRITE - | KEY_SET_VALUE | KEY_CREATE_SUB_KEY) - & ~(SYNCHRONIZE), - /// Combines the STANDARD_RIGHTS_WRITE, KEY_SET_VALUE, - /// and KEY_CREATE_SUB_KEY access rights - KEY_EXECUTE = KEY_READ & ~(SYNCHRONIZE), - /// Permission for read access - KEY_ALL_ACCESS = (STANDARD_RIGHTS_ALL - | KEY_QUERY_VALUE | KEY_SET_VALUE | KEY_CREATE_SUB_KEY - | KEY_ENUMERATE_SUB_KEYS | KEY_NOTIFY | KEY_CREATE_LINK) - & ~(SYNCHRONIZE), - /// Combines the KEY_QUERY_VALUE, KEY_ENUMERATE_SUB_KEYS, - /// KEY_NOTIFY, KEY_CREATE_SUB_KEY, KEY_CREATE_LINK, and - /// KEY_SET_VALUE access rights, plus all the standard - /// access rights except SYNCHRONIZE -} - -/** - Enumeration of the recognised registry value types. - */ -enum REG_VALUE_TYPE : DWORD -{ - REG_UNKNOWN = -1, /// - REG_NONE = 0, /// The null value type. (In practise this is treated as a zero-length binary array by the Win32 registry) - REG_SZ = 1, /// A zero-terminated string - REG_EXPAND_SZ = 2, /// A zero-terminated string containing expandable environment variable references - REG_BINARY = 3, /// A binary blob - REG_DWORD = 4, /// A 32-bit unsigned integer - REG_DWORD_LITTLE_ENDIAN = 4, /// A 32-bit unsigned integer, stored in little-endian byte order - REG_DWORD_BIG_ENDIAN = 5, /// A 32-bit unsigned integer, stored in big-endian byte order - REG_LINK = 6, /// A registry link - REG_MULTI_SZ = 7, /// A set of zero-terminated strings - REG_RESOURCE_LIST = 8, /// A hardware resource list - REG_FULL_RESOURCE_DESCRIPTOR = 9, /// A hardware resource descriptor - REG_RESOURCE_REQUIREMENTS_LIST = 10, /// A hardware resource requirements list - REG_QWORD = 11, /// A 64-bit unsigned integer - REG_QWORD_LITTLE_ENDIAN = 11, /// A 64-bit unsigned integer, stored in little-endian byte order -} - - -/* ************* private *************** */ - -import core.sys.windows.winnt : - DELETE , - READ_CONTROL , - WRITE_DAC , - WRITE_OWNER , - SYNCHRONIZE , - - STANDARD_RIGHTS_REQUIRED, - - STANDARD_RIGHTS_READ , - STANDARD_RIGHTS_WRITE , - STANDARD_RIGHTS_EXECUTE , - - STANDARD_RIGHTS_ALL , - - SPECIFIC_RIGHTS_ALL ; - -import core.sys.windows.winreg : - REG_CREATED_NEW_KEY , - REG_OPENED_EXISTING_KEY ; - -// Returns samDesired but without WoW64 flags if not in WoW64 mode -// for compatibility with Windows 2000 -private REGSAM compatibleRegsam(in REGSAM samDesired) -{ - return isWow64 ? samDesired : cast(REGSAM)(samDesired & ~REGSAM.KEY_WOW64_RES); -} - -///Returns true, if we are in WoW64 mode and have WoW64 flags -private bool haveWoW64Job(in REGSAM samDesired) -{ - return isWow64 && (samDesired & REGSAM.KEY_WOW64_RES); -} - -private REG_VALUE_TYPE _RVT_from_Endian(Endian endian) -{ - final switch (endian) - { - case Endian.bigEndian: - return REG_VALUE_TYPE.REG_DWORD_BIG_ENDIAN; - - case Endian.littleEndian: - return REG_VALUE_TYPE.REG_DWORD_LITTLE_ENDIAN; - } -} - -private LONG regCloseKey(in HKEY hkey) -in -{ - assert(hkey !is null); -} -do -{ - /* No need to attempt to close any of the standard hive keys. - * Although it's documented that calling RegCloseKey() on any of - * these hive keys is ignored, we'd rather not trust the Win32 - * API. - */ - if (cast(uint) hkey & 0x80000000) - { - switch (cast(uint) hkey) - { - case HKEY_CLASSES_ROOT: - case HKEY_CURRENT_USER: - case HKEY_LOCAL_MACHINE: - case HKEY_USERS: - case HKEY_PERFORMANCE_DATA: - case HKEY_PERFORMANCE_TEXT: - case HKEY_PERFORMANCE_NLSTEXT: - case HKEY_CURRENT_CONFIG: - case HKEY_DYN_DATA: - return ERROR_SUCCESS; - default: - /* Do nothing */ - break; - } - } - - return RegCloseKey(hkey); -} - -private void regFlushKey(in HKEY hkey) -in -{ - assert(hkey !is null); -} -do -{ - immutable res = RegFlushKey(hkey); - enforceSucc(res, "Key cannot be flushed"); -} - -private HKEY regCreateKey(in HKEY hkey, in string subKey, in DWORD dwOptions, in REGSAM samDesired, - in LPSECURITY_ATTRIBUTES lpsa, out DWORD disposition) -in -{ - assert(hkey !is null); - assert(subKey !is null); -} -do -{ - HKEY hkeyResult; - enforceSucc(RegCreateKeyExW( - hkey, subKey.tempCStringW(), 0, null, dwOptions, - compatibleRegsam(samDesired), cast(LPSECURITY_ATTRIBUTES) lpsa, - &hkeyResult, &disposition), - "Failed to create requested key: \"" ~ subKey ~ "\""); - - return hkeyResult; -} - -private void regDeleteKey(in HKEY hkey, in string subKey, in REGSAM samDesired) -in -{ - assert(hkey !is null); - assert(subKey !is null); -} -do -{ - LONG res; - if (haveWoW64Job(samDesired)) - { - loadAdvapi32(); - res = pRegDeleteKeyExW(hkey, subKey.tempCStringW(), samDesired, 0); - } - else - { - res = RegDeleteKeyW(hkey, subKey.tempCStringW()); - } - enforceSucc(res, "Key cannot be deleted: \"" ~ subKey ~ "\""); -} - -private void regDeleteValue(in HKEY hkey, in string valueName) -in -{ - assert(hkey !is null); - assert(valueName !is null); -} -do -{ - enforceSucc(RegDeleteValueW(hkey, valueName.tempCStringW()), - "Value cannot be deleted: \"" ~ valueName ~ "\""); -} - -private HKEY regDup(HKEY hkey) -in -{ - assert(hkey !is null); -} -do -{ - /* Can't duplicate standard keys, but don't need to, so can just return */ - if (cast(uint) hkey & 0x80000000) - { - switch (cast(uint) hkey) - { - case HKEY_CLASSES_ROOT: - case HKEY_CURRENT_USER: - case HKEY_LOCAL_MACHINE: - case HKEY_USERS: - case HKEY_PERFORMANCE_DATA: - case HKEY_PERFORMANCE_TEXT: - case HKEY_PERFORMANCE_NLSTEXT: - case HKEY_CURRENT_CONFIG: - case HKEY_DYN_DATA: - return hkey; - default: - /* Do nothing */ - break; - } - } - - HKEY hkeyDup; - immutable res = RegOpenKeyW(hkey, null, &hkeyDup); - - debug(winreg) - { - if (res != ERROR_SUCCESS) - { - writefln("regDup() failed: 0x%08x 0x%08x %d", hkey, hkeyDup, res); - } - - assert(res == ERROR_SUCCESS); - } - - return (res == ERROR_SUCCESS) ? hkeyDup : null; -} - -private LONG regEnumKeyName(in HKEY hkey, in DWORD index, ref wchar[] name, out DWORD cchName) -in -{ - assert(hkey !is null); - assert(name !is null); - assert(name.length > 0); -} -out(res) -{ - assert(res != ERROR_MORE_DATA); -} -do -{ - // The Registry API lies about the lengths of a very few sub-key lengths - // so we have to test to see if it whinges about more data, and provide - // more if it does. - for (;;) - { - cchName = to!DWORD(name.length); - immutable res = RegEnumKeyExW(hkey, index, name.ptr, &cchName, null, null, null, null); - if (res != ERROR_MORE_DATA) - return res; - - // Now need to increase the size of the buffer and try again - name.length *= 2; - } - - assert(0); -} - - -private LONG regEnumValueName(in HKEY hkey, in DWORD dwIndex, ref wchar[] name, out DWORD cchName) -in -{ - assert(hkey !is null); -} -do -{ - for (;;) - { - cchName = to!DWORD(name.length); - immutable res = RegEnumValueW(hkey, dwIndex, name.ptr, &cchName, null, null, null, null); - if (res != ERROR_MORE_DATA) - return res; - - name.length *= 2; - } - - assert(0); -} - -private LONG regGetNumSubKeys(in HKEY hkey, out DWORD cSubKeys, out DWORD cchSubKeyMaxLen) -in -{ - assert(hkey !is null); -} -do -{ - return RegQueryInfoKeyW(hkey, null, null, null, &cSubKeys, - &cchSubKeyMaxLen, null, null, null, null, null, null); -} - -private LONG regGetNumValues(in HKEY hkey, out DWORD cValues, out DWORD cchValueMaxLen) -in -{ - assert(hkey !is null); -} -do -{ - return RegQueryInfoKeyW(hkey, null, null, null, null, null, null, - &cValues, &cchValueMaxLen, null, null, null); -} - -private REG_VALUE_TYPE regGetValueType(in HKEY hkey, in string name) -in -{ - assert(hkey !is null); -} -do -{ - REG_VALUE_TYPE type; - enforceSucc(RegQueryValueExW(hkey, name.tempCStringW(), null, cast(LPDWORD) &type, null, null), - "Value cannot be opened: \"" ~ name ~ "\""); - - return type; -} - -private HKEY regOpenKey(in HKEY hkey, in string subKey, in REGSAM samDesired) -in -{ - assert(hkey !is null); - assert(subKey !is null); -} -do -{ - HKEY hkeyResult; - enforceSucc(RegOpenKeyExW(hkey, subKey.tempCStringW(), 0, compatibleRegsam(samDesired), &hkeyResult), - "Failed to open requested key: \"" ~ subKey ~ "\""); - - return hkeyResult; -} - -private void regQueryValue(in HKEY hkey, string name, out string value, REG_VALUE_TYPE reqType) -in -{ - assert(hkey !is null); -} -do -{ - import core.bitop : bswap; - - REG_VALUE_TYPE type; - - // See https://issues.dlang.org/show_bug.cgi?id=961 on this - union U - { - uint dw; - ulong qw; - } - U u; - void* data = &u.qw; - DWORD cbData = u.qw.sizeof; - - auto keynameTmp = name.tempCStringW(); - LONG res = RegQueryValueExW(hkey, keynameTmp, null, cast(LPDWORD) &type, data, &cbData); - if (res == ERROR_MORE_DATA) - { - data = (new ubyte[cbData]).ptr; - res = RegQueryValueExW(hkey, keynameTmp, null, cast(LPDWORD) &type, data, &cbData); - } - - enforceSucc(res, - "Cannot read the requested value"); - enforce(type == reqType, - new RegistryException("Value type has been changed since the value was acquired")); - - switch (type) - { - case REG_VALUE_TYPE.REG_SZ: - case REG_VALUE_TYPE.REG_EXPAND_SZ: - auto wstr = (cast(immutable(wchar)*)data)[0 .. cbData / wchar.sizeof]; - assert(wstr.length > 0 && wstr[$-1] == '\0'); - if (wstr.length && wstr[$-1] == '\0') - wstr.length = wstr.length - 1; - assert(wstr.length == 0 || wstr[$-1] != '\0'); - value = wstr.to!string; - break; - - case REG_VALUE_TYPE.REG_DWORD_LITTLE_ENDIAN: - version (LittleEndian) - value = to!string(u.dw); - else - value = to!string(bswap(u.dw)); - break; - - case REG_VALUE_TYPE.REG_DWORD_BIG_ENDIAN: - version (LittleEndian) - value = to!string(bswap(u.dw)); - else - value = to!string(u.dw); - break; - - case REG_VALUE_TYPE.REG_QWORD_LITTLE_ENDIAN: - value = to!string(u.qw); - break; - - case REG_VALUE_TYPE.REG_BINARY: - case REG_VALUE_TYPE.REG_MULTI_SZ: - default: - throw new RegistryException("Cannot read the given value as a string"); - } -} - -private void regQueryValue(in HKEY hkey, in string name, out string[] value, REG_VALUE_TYPE reqType) -in -{ - assert(hkey !is null); -} -do -{ - REG_VALUE_TYPE type; - - auto keynameTmp = name.tempCStringW(); - wchar[] data = new wchar[256]; - DWORD cbData = to!DWORD(data.length * wchar.sizeof); - LONG res = RegQueryValueExW(hkey, keynameTmp, null, cast(LPDWORD) &type, data.ptr, &cbData); - if (res == ERROR_MORE_DATA) - { - data.length = cbData / wchar.sizeof; - res = RegQueryValueExW(hkey, keynameTmp, null, cast(LPDWORD) &type, data.ptr, &cbData); - } - else if (res == ERROR_SUCCESS) - { - data.length = cbData / wchar.sizeof; - } - enforceSucc(res, "Cannot read the requested value"); - enforce(type == REG_VALUE_TYPE.REG_MULTI_SZ, - new RegistryException("Cannot read the given value as a string")); - enforce(type == reqType, - new RegistryException("Value type has been changed since the value was acquired")); - - // Remove last two (or one) null terminator - assert(data.length > 0 && data[$-1] == '\0'); - data.length = data.length - 1; - if (data.length > 0 && data[$-1] == '\0') - data.length = data.length - 1; - - auto list = std.array.split(data[], "\0"); - value.length = list.length; - foreach (i, ref v; value) - { - v = list[i].to!string; - } -} - -private void regQueryValue(in HKEY hkey, in string name, out uint value, REG_VALUE_TYPE reqType) -in -{ - assert(hkey !is null); -} -do -{ - import core.bitop : bswap; - - REG_VALUE_TYPE type; - - DWORD cbData = value.sizeof; - enforceSucc(RegQueryValueExW(hkey, name.tempCStringW(), null, cast(LPDWORD) &type, &value, &cbData), - "Cannot read the requested value"); - enforce(type == reqType, - new RegistryException("Value type has been changed since the value was acquired")); - - switch (type) - { - case REG_VALUE_TYPE.REG_DWORD_LITTLE_ENDIAN: - version (LittleEndian) - static assert(REG_VALUE_TYPE.REG_DWORD == REG_VALUE_TYPE.REG_DWORD_LITTLE_ENDIAN); - else - value = bswap(value); - break; - - case REG_VALUE_TYPE.REG_DWORD_BIG_ENDIAN: - version (LittleEndian) - value = bswap(value); - else - static assert(REG_VALUE_TYPE.REG_DWORD == REG_VALUE_TYPE.REG_DWORD_BIG_ENDIAN); - break; - - default: - throw new RegistryException("Cannot read the given value as a 32-bit integer"); - } -} - -private void regQueryValue(in HKEY hkey, in string name, out ulong value, REG_VALUE_TYPE reqType) -in -{ - assert(hkey !is null); -} -do -{ - REG_VALUE_TYPE type; - - DWORD cbData = value.sizeof; - enforceSucc(RegQueryValueExW(hkey, name.tempCStringW(), null, cast(LPDWORD) &type, &value, &cbData), - "Cannot read the requested value"); - enforce(type == reqType, - new RegistryException("Value type has been changed since the value was acquired")); - - switch (type) - { - case REG_VALUE_TYPE.REG_QWORD_LITTLE_ENDIAN: - break; - - default: - throw new RegistryException("Cannot read the given value as a 64-bit integer"); - } -} - -private void regQueryValue(in HKEY hkey, in string name, out byte[] value, REG_VALUE_TYPE reqType) -in -{ - assert(hkey !is null); -} -do -{ - REG_VALUE_TYPE type; - - byte[] data = new byte[100]; - DWORD cbData = to!DWORD(data.length); - LONG res; - auto keynameTmp = name.tempCStringW(); - res = RegQueryValueExW(hkey, keynameTmp, null, cast(LPDWORD) &type, data.ptr, &cbData); - if (res == ERROR_MORE_DATA) - { - data.length = cbData; - res = RegQueryValueExW(hkey, keynameTmp, null, cast(LPDWORD) &type, data.ptr, &cbData); - } - enforceSucc(res, "Cannot read the requested value"); - enforce(type == reqType, - new RegistryException("Value type has been changed since the value was acquired")); - - switch (type) - { - case REG_VALUE_TYPE.REG_BINARY: - data.length = cbData; - value = data; - break; - - default: - throw new RegistryException("Cannot read the given value as a string"); - } -} - -private void regSetValue(in HKEY hkey, in string subKey, in REG_VALUE_TYPE type, in LPCVOID lpData, in DWORD cbData) -in -{ - assert(hkey !is null); -} -do -{ - enforceSucc(RegSetValueExW(hkey, subKey.tempCStringW(), 0, type, cast(BYTE*) lpData, cbData), - "Value cannot be set: \"" ~ subKey ~ "\""); -} - -private void regProcessNthKey(Key key, scope void delegate(scope LONG delegate(DWORD, out string)) dg) -{ - DWORD cSubKeys; - DWORD cchSubKeyMaxLen; - - immutable res = regGetNumSubKeys(key.m_hkey, cSubKeys, cchSubKeyMaxLen); - assert(res == ERROR_SUCCESS); - - wchar[] sName = new wchar[cchSubKeyMaxLen + 1]; - - // Capture `key` in the lambda to keep the object alive (and so its HKEY handle open). - dg((DWORD index, out string name) - { - DWORD cchName; - immutable res = regEnumKeyName(key.m_hkey, index, sName, cchName); - if (res == ERROR_SUCCESS) - { - name = sName[0 .. cchName].to!string; - } - return res; - }); -} - -private void regProcessNthValue(Key key, scope void delegate(scope LONG delegate(DWORD, out string)) dg) -{ - DWORD cValues; - DWORD cchValueMaxLen; - - immutable res = regGetNumValues(key.m_hkey, cValues, cchValueMaxLen); - assert(res == ERROR_SUCCESS); - - wchar[] sName = new wchar[cchValueMaxLen + 1]; - - // Capture `key` in the lambda to keep the object alive (and so its HKEY handle open). - dg((DWORD index, out string name) - { - DWORD cchName; - immutable res = regEnumValueName(key.m_hkey, index, sName, cchName); - if (res == ERROR_SUCCESS) - { - name = sName[0 .. cchName].to!string; - } - return res; - }); -} - -/* ************* public classes *************** */ - -/** - This class represents a registry key. - */ -class Key -{ - @safe pure nothrow - invariant() - { - assert(m_hkey !is null); - } - -private: - @safe pure nothrow - this(HKEY hkey, string name, bool created) - in - { - assert(hkey !is null); - } - do - { - m_hkey = hkey; - m_name = name; - } - - ~this() - { - regCloseKey(m_hkey); - - // Even though this is horried waste-of-cycles programming - // we're doing it here so that the - m_hkey = null; - } - -public: - /// The name of the key - @property string name() @safe pure nothrow const - { - return m_name; - } - - /** - The number of sub keys. - */ - @property size_t keyCount() const - { - uint cSubKeys; - uint cchSubKeyMaxLen; - enforceSucc(regGetNumSubKeys(m_hkey, cSubKeys, cchSubKeyMaxLen), - "Number of sub-keys cannot be determined"); - - return cSubKeys; - } - - /** - An enumerable sequence of all the sub-keys of this key. - */ - @property KeySequence keys() @safe pure - { - return new KeySequence(this); - } - - /** - An enumerable sequence of the names of all the sub-keys of this key. - */ - @property KeyNameSequence keyNames() @safe pure - { - return new KeyNameSequence(this); - } - - /** - The number of values. - */ - @property size_t valueCount() const - { - uint cValues; - uint cchValueMaxLen; - enforceSucc(regGetNumValues(m_hkey, cValues, cchValueMaxLen), - "Number of values cannot be determined"); - - return cValues; - } - - /** - An enumerable sequence of all the values of this key. - */ - @property ValueSequence values() @safe pure - { - return new ValueSequence(this); - } - - /** - An enumerable sequence of the names of all the values of this key. - */ - @property ValueNameSequence valueNames() @safe pure - { - return new ValueNameSequence(this); - } - - /** - Returns the named sub-key of this key. - - Params: - name = The name of the subkey to create. May not be `null`. - Returns: - The created key. - Throws: - `RegistryException` is thrown if the key cannot be created. - */ - Key createKey(string name, REGSAM access = REGSAM.KEY_ALL_ACCESS) - { - enforce(!name.empty, new RegistryException("Key name is invalid")); - - DWORD disposition; - HKEY hkey = regCreateKey(m_hkey, name, 0, access, null, disposition); - assert(hkey !is null); - - // Potential resource leak here!! - // - // If the allocation of the memory for Key fails, the HKEY could be - // lost. Hence, we catch such a failure by the finally, and release - // the HKEY there. If the creation of - try - { - Key key = new Key(hkey, name, disposition == REG_CREATED_NEW_KEY); - hkey = null; - return key; - } - finally - { - if (hkey !is null) - { - regCloseKey(hkey); - } - } - } - - /** - Returns the named sub-key of this key. - - Params: - name = The name of the subkey to aquire. If name is the empty - string, then the called key is duplicated. - access = The desired access; one of the `REGSAM` enumeration. - Returns: - The aquired key. - Throws: - This function never returns `null`. If a key corresponding to - the requested name is not found, `RegistryException` is thrown. - */ - Key getKey(string name, REGSAM access = REGSAM.KEY_READ) - { - if (name.empty) - return new Key(regDup(m_hkey), m_name, false); - - HKEY hkey = regOpenKey(m_hkey, name, access); - assert(hkey !is null); - - // Potential resource leak here!! - // - // If the allocation of the memory for Key fails, the HKEY could be - // lost. Hence, we catch such a failure by the finally, and release - // the HKEY there. If the creation of - try - { - Key key = new Key(hkey, name, false); - hkey = null; - return key; - } - finally - { - if (hkey != null) - { - regCloseKey(hkey); - } - } - } - - /** - Deletes the named key. - - Params: - name = The name of the key to delete. May not be `null`. - */ - void deleteKey(string name, REGSAM access = cast(REGSAM) 0) - { - enforce(!name.empty, new RegistryException("Key name is invalid")); - - regDeleteKey(m_hkey, name, access); - } - - /** - Returns the named value. - If `name` is the empty string, then the default value is returned. - - Returns: - This function never returns `null`. If a value corresponding - to the requested name is not found, `RegistryException` is thrown. - */ - Value getValue(string name) - { - return new Value(this, name, regGetValueType(m_hkey, name)); - } - - /** - Sets the named value with the given 32-bit unsigned integer value. - - Params: - name = The name of the value to set. If it is the empty string, - sets the default value. - value = The 32-bit unsigned value to set. - Throws: - If a value corresponding to the requested name is not found, - `RegistryException` is thrown. - */ - void setValue(string name, uint value) - { - setValue(name, value, endian); - } - - /** - Sets the named value with the given 32-bit unsigned integer value, - according to the desired byte-ordering. - - Params: - name = The name of the value to set. If it is the empty string, - sets the default value. - value = The 32-bit unsigned value to set. - endian = Can be `Endian.BigEndian` or `Endian.LittleEndian`. - Throws: - If a value corresponding to the requested name is not found, - `RegistryException` is thrown. - */ - void setValue(string name, uint value, Endian endian) - { - REG_VALUE_TYPE type = _RVT_from_Endian(endian); - - assert(type == REG_VALUE_TYPE.REG_DWORD_BIG_ENDIAN || - type == REG_VALUE_TYPE.REG_DWORD_LITTLE_ENDIAN); - - regSetValue(m_hkey, name, type, &value, value.sizeof); - } - - /** - Sets the named value with the given 64-bit unsigned integer value. - - Params: - name = The name of the value to set. If it is the empty string, - sets the default value. - value = The 64-bit unsigned value to set. - Throws: - If a value corresponding to the requested name is not found, - `RegistryException` is thrown. - */ - void setValue(string name, ulong value) - { - regSetValue(m_hkey, name, REG_VALUE_TYPE.REG_QWORD, &value, value.sizeof); - } - - /** - Sets the named value with the given string value. - - Params: - name = The name of the value to set. If it is the empty string, - sets the default value. - value = The string value to set. - Throws: - If a value corresponding to the requested name is not found, - `RegistryException` is thrown. - */ - void setValue(string name, string value) - { - setValue(name, value, false); - } - - /** - Sets the named value with the given string value. - - Params: - name = The name of the value to set. If it is the empty string, - sets the default value. - value = The string value to set. - asEXPAND_SZ = If `true`, the value will be stored as an - expandable environment string, otherwise as a normal string. - Throws: - If a value corresponding to the requested name is not found, - `RegistryException` is thrown. - */ - void setValue(string name, string value, bool asEXPAND_SZ) - { - auto pszTmp = value.tempCStringW(); - const(void)* data = pszTmp; - DWORD len = to!DWORD(lstrlenW(pszTmp) * wchar.sizeof); - - regSetValue(m_hkey, name, - asEXPAND_SZ ? REG_VALUE_TYPE.REG_EXPAND_SZ - : REG_VALUE_TYPE.REG_SZ, - data, len); - } - - /** - Sets the named value with the given multiple-strings value. - - Params: - name = The name of the value to set. If it is the empty string, - sets the default value. - value = The multiple-strings value to set. - Throws: - If a value corresponding to the requested name is not found, - `RegistryException` is thrown. - */ - void setValue(string name, string[] value) - { - wstring[] data = new wstring[value.length+1]; - foreach (i, ref s; data[0..$-1]) - { - s = value[i].to!wstring; - } - data[$-1] = "\0"; - auto ws = std.array.join(data, "\0"w); - - regSetValue(m_hkey, name, REG_VALUE_TYPE.REG_MULTI_SZ, ws.ptr, to!uint(ws.length * wchar.sizeof)); - } - - /** - Sets the named value with the given binary value. - - Params: - name = The name of the value to set. If it is the empty string, - sets the default value. - value = The binary value to set. - Throws: - If a value corresponding to the requested name is not found, - `RegistryException` is thrown. - */ - void setValue(string name, byte[] value) - { - regSetValue(m_hkey, name, REG_VALUE_TYPE.REG_BINARY, value.ptr, to!DWORD(value.length)); - } - - /** - Deletes the named value. - - Params: - name = The name of the value to delete. May not be `null`. - Throws: - If a value of the requested name is not found, - `RegistryException` is thrown. - */ - void deleteValue(string name) - { - regDeleteValue(m_hkey, name); - } - - /** - Flushes any changes to the key to disk. - */ - void flush() - { - regFlushKey(m_hkey); - } - -private: - HKEY m_hkey; - string m_name; -} - -/** - This class represents a value of a registry key. - */ -class Value -{ - @safe pure nothrow - invariant() - { - assert(m_key !is null); - } - -private: - @safe pure nothrow - this(Key key, string name, REG_VALUE_TYPE type) - in - { - assert(null !is key); - } - do - { - m_key = key; - m_type = type; - m_name = name; - } - -public: - /** - The name of the value. - If the value represents a default value of a key, which has no name, - the returned string will be of zero length. - */ - @property string name() @safe pure nothrow const - { - return m_name; - } - - /** - The type of value. - */ - @property REG_VALUE_TYPE type() @safe pure nothrow const - { - return m_type; - } - - /** - Obtains the current value of the value as a string. - If the value's type is REG_EXPAND_SZ the returned value is <b>not</b> - expanded; `value_EXPAND_SZ` should be called - - Returns: - The contents of the value. - Throws: - `RegistryException` if the type of the value is not REG_SZ, - REG_EXPAND_SZ, REG_DWORD, or REG_QWORD. - */ - @property string value_SZ() const - { - string value; - - regQueryValue(m_key.m_hkey, m_name, value, m_type); - - return value; - } - - /** - Obtains the current value as a string, within which any environment - variables have undergone expansion. - This function works with the same value-types as `value_SZ`. - - Returns: - The contents of the value. - */ - @property string value_EXPAND_SZ() const - { - string value = value_SZ; - - // ExpandEnvironemntStrings(): - // http://msdn2.microsoft.com/en-us/library/ms724265.aspx - const srcTmp = value.tempCStringW(); - DWORD cchRequired = ExpandEnvironmentStringsW(srcTmp, null, 0); - wchar[] newValue = new wchar[cchRequired]; - - immutable DWORD count = enforce!Win32Exception( - ExpandEnvironmentStringsW(srcTmp, newValue.ptr, to!DWORD(newValue.length)), - "Failed to expand environment variables"); - - return newValue[0 .. count-1].to!string; // remove trailing 0 - } - - /** - Obtains the current value as an array of strings. - - Returns: - The contents of the value. - Throws: - `RegistryException` if the type of the value is not REG_MULTI_SZ. - */ - @property string[] value_MULTI_SZ() const - { - string[] value; - - regQueryValue(m_key.m_hkey, m_name, value, m_type); - - return value; - } - - /** - Obtains the current value as a 32-bit unsigned integer, ordered - correctly according to the current architecture. - - Returns: - The contents of the value. - Throws: - `RegistryException` is thrown for all types other than - REG_DWORD, REG_DWORD_LITTLE_ENDIAN and REG_DWORD_BIG_ENDIAN. - */ - @property uint value_DWORD() const - { - uint value; - - regQueryValue(m_key.m_hkey, m_name, value, m_type); - - return value; - } - - /** - Obtains the value as a 64-bit unsigned integer, ordered correctly - according to the current architecture. - - Returns: - The contents of the value. - Throws: - `RegistryException` if the type of the value is not REG_QWORD. - */ - @property ulong value_QWORD() const - { - ulong value; - - regQueryValue(m_key.m_hkey, m_name, value, m_type); - - return value; - } - - /** - Obtains the value as a binary blob. - - Returns: - The contents of the value. - Throws: - `RegistryException` if the type of the value is not REG_BINARY. - */ - @property byte[] value_BINARY() const - { - byte[] value; - - regQueryValue(m_key.m_hkey, m_name, value, m_type); - - return value; - } - -private: - Key m_key; - REG_VALUE_TYPE m_type; - string m_name; -} - -/** - Represents the local system registry. - */ -final class Registry -{ -private: - @disable this(); - -public: - /// Returns the root key for the HKEY_CLASSES_ROOT hive - static @property Key classesRoot() { return new Key(HKEY_CLASSES_ROOT, "HKEY_CLASSES_ROOT", false); } - /// Returns the root key for the HKEY_CURRENT_USER hive - static @property Key currentUser() { return new Key(HKEY_CURRENT_USER, "HKEY_CURRENT_USER", false); } - /// Returns the root key for the HKEY_LOCAL_MACHINE hive - static @property Key localMachine() { return new Key(HKEY_LOCAL_MACHINE, "HKEY_LOCAL_MACHINE", false); } - /// Returns the root key for the HKEY_USERS hive - static @property Key users() { return new Key(HKEY_USERS, "HKEY_USERS", false); } - /// Returns the root key for the HKEY_PERFORMANCE_DATA hive - static @property Key performanceData() { return new Key(HKEY_PERFORMANCE_DATA, "HKEY_PERFORMANCE_DATA", false); } - /// Returns the root key for the HKEY_CURRENT_CONFIG hive - static @property Key currentConfig() { return new Key(HKEY_CURRENT_CONFIG, "HKEY_CURRENT_CONFIG", false); } - /// Returns the root key for the HKEY_DYN_DATA hive - static @property Key dynData() { return new Key(HKEY_DYN_DATA, "HKEY_DYN_DATA", false); } -} - -/** - An enumerable sequence representing the names of the sub-keys of a registry Key. - -Example: ----- -Key key = ... -foreach (string subkeyName; key.keyNames) -{ - // using subkeyName -} ----- - */ -class KeyNameSequence -{ - @safe pure nothrow - invariant() - { - assert(m_key !is null); - } - -private: - @safe pure nothrow - this(Key key) - { - m_key = key; - } - -public: - /** - The number of keys. - */ - @property size_t count() const - { - return m_key.keyCount; - } - - /** - The name of the key at the given index. - - Params: - index = The 0-based index of the key to retrieve. - Returns: - The name of the key corresponding to the given index. - Throws: - RegistryException if no corresponding key is retrieved. - */ - string getKeyName(size_t index) - { - string name; - regProcessNthKey(m_key, (scope LONG delegate(DWORD, out string) getName) - { - enforceSucc(getName(to!DWORD(index), name), "Invalid key"); - }); - return name; - } - - /** - The name of the key at the given index. - - Params: - index = The 0-based index of the key to retrieve. - Returns: - The name of the key corresponding to the given index. - Throws: - `RegistryException` if no corresponding key is retrieved. - */ - string opIndex(size_t index) - { - return getKeyName(index); - } - - /// - int opApply(scope int delegate(ref string name) dg) - { - int result; - regProcessNthKey(m_key, (scope LONG delegate(DWORD, out string) getName) - { - for (DWORD index = 0; !result; ++index) - { - string name; - immutable res = getName(index, name); - if (res == ERROR_NO_MORE_ITEMS) // Enumeration complete - break; - enforceSucc(res, "Key name enumeration incomplete"); - - result = dg(name); - } - }); - return result; - } - -private: - Key m_key; -} - - -/** - An enumerable sequence representing the sub-keys of a registry Key. - -Example: ----- -Key key = ... -foreach (Key subkey; key.keys) -{ - // using subkey -} ----- - */ -class KeySequence -{ - @safe pure nothrow - invariant() - { - assert(m_key !is null); - } - -private: - @safe pure nothrow - this(Key key) - { - m_key = key; - } - -public: - /** - The number of keys. - */ - @property size_t count() const - { - return m_key.keyCount; - } - - /** - The key at the given index. - - Params: - index = The 0-based index of the key to retrieve. - Returns: - The key corresponding to the given index. - Throws: - `RegistryException` if no corresponding key is retrieved. - */ - Key getKey(size_t index) - { - string name; - regProcessNthKey(m_key, (scope LONG delegate(DWORD, out string) getName) - { - enforceSucc(getName(to!DWORD(index), name), "Invalid key"); - }); - return m_key.getKey(name); - } - - /** - The key at the given index. - - Params: - index = The 0-based index of the key to retrieve. - Returns: - The key corresponding to the given index. - Throws: - `RegistryException` if no corresponding key is retrieved. - */ - Key opIndex(size_t index) - { - return getKey(index); - } - - /// - int opApply(scope int delegate(ref Key key) dg) - { - int result = 0; - regProcessNthKey(m_key, (scope LONG delegate(DWORD, out string) getName) - { - for (DWORD index = 0; !result; ++index) - { - string name; - immutable res = getName(index, name); - if (res == ERROR_NO_MORE_ITEMS) // Enumeration complete - break; - enforceSucc(res, "Key enumeration incomplete"); - - try - { - Key key = m_key.getKey(name); - result = dg(key); - } - catch (RegistryException e) - { - // Skip inaccessible keys; they are - // accessible via the KeyNameSequence - if (e.error == ERROR_ACCESS_DENIED) - continue; - - throw e; - } - } - }); - return result; - } - -private: - Key m_key; -} - -/** - An enumerable sequence representing the names of the values of a registry Key. - -Example: ----- -Key key = ... -foreach (string valueName; key.valueNames) -{ - // using valueName -} ----- - */ -class ValueNameSequence -{ - @safe pure nothrow - invariant() - { - assert(m_key !is null); - } - -private: - @safe pure nothrow - this(Key key) - { - m_key = key; - } - -public: - /** - The number of values. - */ - @property size_t count() const - { - return m_key.valueCount; - } - - /** - The name of the value at the given index. - - Params: - index = The 0-based index of the value to retrieve. - Returns: - The name of the value corresponding to the given index. - Throws: - `RegistryException` if no corresponding value is retrieved. - */ - string getValueName(size_t index) - { - string name; - regProcessNthValue(m_key, (scope LONG delegate(DWORD, out string) getName) - { - enforceSucc(getName(to!DWORD(index), name), "Invalid value"); - }); - return name; - } - - /** - The name of the value at the given index. - - Params: - index = The 0-based index of the value to retrieve. - Returns: - The name of the value corresponding to the given index. - Throws: - `RegistryException` if no corresponding value is retrieved. - */ - string opIndex(size_t index) - { - return getValueName(index); - } - - /// - int opApply(scope int delegate(ref string name) dg) - { - int result = 0; - regProcessNthValue(m_key, (scope LONG delegate(DWORD, out string) getName) - { - for (DWORD index = 0; !result; ++index) - { - string name; - immutable res = getName(index, name); - if (res == ERROR_NO_MORE_ITEMS) // Enumeration complete - break; - enforceSucc(res, "Value name enumeration incomplete"); - - result = dg(name); - } - }); - return result; - } - -private: - Key m_key; -} - -/** - An enumerable sequence representing the values of a registry Key. - -Example: ----- -Key key = ... -foreach (Value value; key.values) -{ - // using value -} ----- - */ -class ValueSequence -{ - @safe pure nothrow - invariant() - { - assert(m_key !is null); - } - -private: - @safe pure nothrow - this(Key key) - { - m_key = key; - } - -public: - /// The number of values - @property size_t count() const - { - return m_key.valueCount; - } - - /** - The value at the given `index`. - - Params: - index = The 0-based index of the value to retrieve - Returns: - The value corresponding to the given index. - Throws: - `RegistryException` if no corresponding value is retrieved - */ - Value getValue(size_t index) - { - string name; - regProcessNthValue(m_key, (scope LONG delegate(DWORD, out string) getName) - { - enforceSucc(getName(to!DWORD(index), name), "Invalid value"); - }); - return m_key.getValue(name); - } - - /** - The value at the given `index`. - - Params: - index = The 0-based index of the value to retrieve. - Returns: - The value corresponding to the given index. - Throws: - `RegistryException` if no corresponding value is retrieved. - */ - Value opIndex(size_t index) - { - return getValue(index); - } - - /// - int opApply(scope int delegate(ref Value value) dg) - { - int result = 0; - regProcessNthValue(m_key, (scope LONG delegate(DWORD, out string) getName) - { - for (DWORD index = 0; !result; ++index) - { - string name; - immutable res = getName(index, name); - if (res == ERROR_NO_MORE_ITEMS) // Enumeration complete - break; - enforceSucc(res, "Value enumeration incomplete"); - - Value value = m_key.getValue(name); - result = dg(value); - } - }); - return result; - } - -private: - Key m_key; -} - - -@system unittest -{ - debug(winreg) scope(success) writeln("unittest @", __FILE__, ":", __LINE__, " succeeded."); - debug(winreg) writefln("std.windows.registry.unittest read"); - -/+ - // Mask for test speed up - - Key HKCR = Registry.classesRoot; - Key CLSID = HKCR.getKey("CLSID"); - - foreach (Key key; CLSID.keys) - { - foreach (Value val; key.values) - { - } - } -+/ - Key HKCU = Registry.currentUser; - assert(HKCU); - - // Enumerate all subkeys of key Software - Key softwareKey = HKCU.getKey("Software"); - assert(softwareKey); - foreach (Key key; softwareKey.keys) - { - //writefln("Key %s", key.name); - foreach (Value val; key.values) - { - } - } -} - -@system unittest -{ - debug(winreg) scope(success) writeln("unittest @", __FILE__, ":", __LINE__, " succeeded."); - debug(winreg) writefln("std.windows.registry.unittest write"); - - // Warning: This unit test writes to the registry. - // The test can fail if you don't have sufficient rights - - Key HKCU = Registry.currentUser; - assert(HKCU); - - // Create a new key - string unittestKeyName = "Temporary key for a D UnitTest which can be deleted afterwards"; - Key unittestKey = HKCU.createKey(unittestKeyName); - assert(unittestKey); - Key cityKey = unittestKey.createKey( - "CityCollection using foreign names with umlauts and accents: " - ~"\u00f6\u00e4\u00fc\u00d6\u00c4\u00dc\u00e0\u00e1\u00e2\u00df" - ); - cityKey.setValue("K\u00f6ln", "Germany"); // Cologne - cityKey.setValue("\u041c\u0438\u043d\u0441\u043a", "Belarus"); // Minsk - cityKey.setValue("\u5317\u4eac", "China"); // Bejing - bool foundCologne, foundMinsk, foundBeijing; - foreach (Value v; cityKey.values) - { - auto vname = v.name; - auto vvalue_SZ = v.value_SZ; - if (v.name == "K\u00f6ln") - { - foundCologne = true; - assert(v.value_SZ == "Germany"); - } - if (v.name == "\u041c\u0438\u043d\u0441\u043a") - { - foundMinsk = true; - assert(v.value_SZ == "Belarus"); - } - if (v.name == "\u5317\u4eac") - { - foundBeijing = true; - assert(v.value_SZ == "China"); - } - } - assert(foundCologne); - assert(foundMinsk); - assert(foundBeijing); - - Key stateKey = unittestKey.createKey("StateCollection"); - stateKey.setValue("Germany", ["D\u00fcsseldorf", "K\u00f6ln", "Hamburg"]); - Value v = stateKey.getValue("Germany"); - string[] actual = v.value_MULTI_SZ; - assert(actual.length == 3); - assert(actual[0] == "D\u00fcsseldorf"); - assert(actual[1] == "K\u00f6ln"); - assert(actual[2] == "Hamburg"); - - Key numberKey = unittestKey.createKey("Number"); - numberKey.setValue("One", 1); - Value one = numberKey.getValue("One"); - assert(one.value_SZ == "1"); - assert(one.value_DWORD == 1); - - unittestKey.deleteKey(numberKey.name); - unittestKey.deleteKey(stateKey.name); - unittestKey.deleteKey(cityKey.name); - HKCU.deleteKey(unittestKeyName); - - auto e = collectException!RegistryException(HKCU.getKey("cDhmxsX9K23a8Uf869uB")); - assert(e.msg.endsWith(" (error 2)")); -} - -@system unittest -{ - Key HKCU = Registry.currentUser; - assert(HKCU); - - Key key = HKCU.getKey("Control Panel"); - assert(key); - assert(key.keyCount >= 2); - - // Make sure `key` isn't garbage-collected while iterating over it. - // Trigger a collection in the first iteration and check whether we - // make it successfully to the second iteration. - int i = 0; - foreach (name; key.keyNames) - { - if (i++ > 0) - break; - - import core.memory : GC; - GC.collect(); - } - assert(i == 2); -} diff --git a/phobos/std/windows/syserror.d b/phobos/std/windows/syserror.d deleted file mode 100644 index 3d8c5e7..0000000 --- a/phobos/std/windows/syserror.d +++ /dev/null @@ -1,258 +0,0 @@ -// Written in the D programming language. - -/** - * Convert Win32 error code to string. - * - * Copyright: Copyright The D Language Foundation" 2006 - 2013. - * License: $(HTTP www.boost.org/LICENSE_1_0.txt, Boost License 1.0). - * Authors: $(HTTP digitalmars.com, Walter Bright) - * Credits: Based on code written by Regan Heath - */ -/* Copyright The D Language Foundation" 2006 - 2013. - * 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.windows.syserror; -import std.traits : isSomeString; - -version (StdDdoc) -{ - private - { - alias DWORD = uint; - enum LANG_NEUTRAL = 0, SUBLANG_DEFAULT = 1; - } - - /** Query the text for a Windows error code, as returned by - $(LINK2 http://msdn.microsoft.com/en-us/library/windows/desktop/ms679360.aspx, - `GetLastError`), as a D string. - */ - string sysErrorString( - DWORD errCode, - // MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT) is the user's default language - int langId = LANG_NEUTRAL, - int subLangId = SUBLANG_DEFAULT) @trusted; - - /********************* - Thrown if errors that set - $(LINK2 http://msdn.microsoft.com/en-us/library/windows/desktop/ms679360.aspx, - `GetLastError`) occur. - */ - class WindowsException : Exception - { - private alias DWORD = int; - final @property DWORD code(); /// `GetLastError`'s return value. - this(DWORD code, string str=null, string file = null, size_t line = 0) nothrow @trusted; - } - - /++ - If `!!value` is true, `value` is returned. Otherwise, - $(D new WindowsException(GetLastError(), msg)) is thrown. - `WindowsException` assumes that the last operation set - `GetLastError()` appropriately. - - Example: - -------------------- - wenforce(DeleteFileA("junk.tmp"), "DeleteFile failed"); - -------------------- - +/ - T wenforce(T, S)(T value, lazy S msg = null, - string file = __FILE__, size_t line = __LINE__) @safe - if (isSomeString!S); -} -else: - -version (Windows): - -import core.sys.windows.winbase, core.sys.windows.winnt; -import std.array : appender, Appender; -import std.conv : to, toTextRange, text; -import std.exception; -import std.windows.charset; - -string sysErrorString( - DWORD errCode, - // MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT) is the user's default language - int langId = LANG_NEUTRAL, - int subLangId = SUBLANG_DEFAULT) @trusted -{ - auto buf = appender!string(); - - wenforce( - // Ignore unlikely UTF decoding errors, always report the actual error (`errCode`) - putSysError(errCode, buf, MAKELANGID(langId, subLangId)).ifThrown(false), - text("Could not fetch error string for WinAPI code ", errCode) - ); - - return buf.data; -} - -@safe unittest -{ - import std.algorithm.searching; - - assert(sysErrorString(ERROR_PATH_NOT_FOUND) !is null); - - const msg = collectExceptionMsg!WindowsException(sysErrorString(DWORD.max)); - assert(msg.startsWith(`Could not fetch error string for WinAPI code 4294967295: `)); -} - -bool putSysError(Writer)(DWORD code, Writer w, /*WORD*/int langId = 0) -{ - wchar *lpMsgBuf = null; - auto res = FormatMessageW( - FORMAT_MESSAGE_ALLOCATE_BUFFER | - FORMAT_MESSAGE_FROM_SYSTEM | - FORMAT_MESSAGE_IGNORE_INSERTS, - null, - code, - langId, - cast(LPWSTR)&lpMsgBuf, - 0, - null); - scope(exit) if (lpMsgBuf) LocalFree(lpMsgBuf); - - if (lpMsgBuf) - { - import std.string : strip; - w.put(lpMsgBuf[0 .. res].strip()); - return true; - } - else - return false; -} - -class WindowsException : Exception -{ - import core.sys.windows.windef : DWORD; - - final @property DWORD code() { return _code; } /// `GetLastError`'s return value. - private DWORD _code; - - this(DWORD code, string str=null, string file = null, size_t line = 0) nothrow @trusted - { - _code = code; - - auto buf = appender!(char[]); - - if (str != null) - { - buf.put(str); - if (code) - buf.put(": "); - } - - if (code && writeErrorMessage(code, buf)) - { - buf.put(" (error "); - toTextRange(code, buf); - buf.put(')'); - } - - super(cast(immutable) buf.data, file, line); - } -} - -/// Writes the error string associated to `code` into `buf`. -/// Writes `Error <code>` when the error message lookup fails -private bool writeErrorMessage(DWORD code, ref Appender!(char[]) buf) nothrow -{ - bool success; - try - { - // Reset the buffer to undo partial changes - const len = buf[].length; - scope (failure) buf.shrinkTo(len); - - success = putSysError(code, buf); - } - catch (Exception) {} - - // Write the error code instead if we couldn't find the string - if (!success) - { - buf.put("Error "); - toTextRange(code, buf); - } - - return success; -} - -T wenforce(T, S = string)(T value, lazy S msg = null, -string file = __FILE__, size_t line = __LINE__) -if (isSomeString!S) -{ - if (!value) - throw new WindowsException(GetLastError(), to!string(msg), file, line); - return value; -} - -T wenforce(T)(T condition, const(char)[] name, const(wchar)* namez, string file = __FILE__, size_t line = __LINE__) -{ - if (condition) - return condition; - string names; - if (!name) - { - static string trustedToString(const(wchar)* stringz) @trusted - { - import core.stdc.wchar_ : wcslen; - import std.conv : to; - auto len = wcslen(stringz); - return to!string(stringz[0 .. len]); - } - - names = trustedToString(namez); - } - else - names = to!string(name); - throw new WindowsException(GetLastError(), names, file, line); -} - -@system unittest -{ - import std.algorithm.searching : startsWith, endsWith; - import std.string; - - auto e = collectException!WindowsException( - DeleteFileA("unexisting.txt").wenforce("DeleteFile") - ); - assert(e.code == ERROR_FILE_NOT_FOUND); - assert(e.msg.startsWith("DeleteFile: ")); - // can't test the entire message, as it depends on Windows locale - assert(e.msg.endsWith(" (error 2)")); - - // Test code zero - e = new WindowsException(0); - assert(e.msg == ""); - - e = new WindowsException(0, "Test"); - assert(e.msg == "Test"); -} - -@safe nothrow unittest -{ - import std.algorithm.searching : endsWith; - - auto e = new WindowsException(ERROR_FILE_NOT_FOUND); - assert(e.msg.endsWith("(error 2)")); - - e = new WindowsException(DWORD.max); - assert(e.msg == "Error 4294967295"); -} - -/// Tries to translate an error code from the Windows API to the corresponding -/// error message. Returns `Error <code>` on failure -package (std) string generateSysErrorMsg(DWORD errCode = GetLastError()) nothrow @trusted -{ - auto buf = appender!(char[]); - cast(void) writeErrorMessage(errCode, buf); - return cast(immutable) buf[]; -} - -nothrow @safe unittest -{ - assert(generateSysErrorMsg(ERROR_PATH_NOT_FOUND) !is null); - assert(generateSysErrorMsg(DWORD.max) == "Error 4294967295"); -} diff --git a/phobos/std/zip.d b/phobos/std/zip.d deleted file mode 100644 index 72d1287..0000000 --- a/phobos/std/zip.d +++ /dev/null @@ -1,1710 +0,0 @@ -// Written in the D programming language. - -/** -Read and write data in the -$(LINK2 https://en.wikipedia.org/wiki/Zip_%28file_format%29, zip archive) -format. - -Standards: - -The current implementation mostly conforms to -$(LINK2 https://www.iso.org/standard/60101.html, ISO/IEC 21320-1:2015), -which means, -$(UL -$(LI that files can only be stored uncompressed or using the deflate mechanism,) -$(LI that encryption features are not used,) -$(LI that digital signature features are not used,) -$(LI that patched data features are not used, and) -$(LI that archives may not span multiple volumes.) -) - -Additionally, archives are checked for malware attacks and rejected if detected. -This includes -$(UL -$(LI $(LINK2 https://news.ycombinator.com/item?id=20352439, zip bombs) which - generate gigantic amounts of unpacked data) -$(LI zip archives that contain overlapping records) -$(LI chameleon zip archives which generate different unpacked data, depending - on the implementation of the unpack algorithm) -) - -The current implementation makes use of the zlib compression library. - -Usage: - -There are two main ways of usage: Extracting files from a zip archive -and storing files into a zip archive. These can be mixed though (e.g. -read an archive, remove some files, add others and write the new -archive). - -Examples: - -Example for reading an existing zip archive: ---- -import std.stdio : writeln, writefln; -import std.file : read; -import std.zip; - -void main(string[] args) -{ - // read a zip file into memory - auto zip = new ZipArchive(read(args[1])); - - // iterate over all zip members - writefln("%-10s %-8s Name", "Length", "CRC-32"); - foreach (name, am; zip.directory) - { - // print some data about each member - writefln("%10s %08x %s", am.expandedSize, am.crc32, name); - assert(am.expandedData.length == 0); - - // decompress the archive member - zip.expand(am); - assert(am.expandedData.length == am.expandedSize); - } -} ---- - -Example for writing files into a zip archive: ---- -import std.file : write; -import std.string : representation; -import std.zip; - -void main() -{ - // Create an ArchiveMembers for each file. - ArchiveMember file1 = new ArchiveMember(); - file1.name = "test1.txt"; - file1.expandedData("Test data.\n".dup.representation); - file1.compressionMethod = CompressionMethod.none; // don't compress - - ArchiveMember file2 = new ArchiveMember(); - file2.name = "test2.txt"; - file2.expandedData("More test data.\n".dup.representation); - file2.compressionMethod = CompressionMethod.deflate; // compress - - // Create an archive and add the member. - ZipArchive zip = new ZipArchive(); - - // add ArchiveMembers - zip.addMember(file1); - zip.addMember(file2); - - // Build the archive - void[] compressed_data = zip.build(); - - // Write to a file - write("test.zip", compressed_data); -} ---- - - * Copyright: Copyright The D Language Foundation 2000 - 2009. - * License: $(HTTP www.boost.org/LICENSE_1_0.txt, Boost License 1.0). - * Authors: $(HTTP digitalmars.com, Walter Bright) - * Source: $(PHOBOSSRC std/zip.d) - */ - -/* Copyright The D Language Foundation 2000 - 2009. - * 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.zip; - -import std.exception : enforce; - -// Non-Android/Apple ARM POSIX-only, because we can't rely on the unzip -// command being available on Android, Apple ARM or Windows -version (Android) {} -else version (iOS) {} -else version (TVOS) {} -else version (WatchOS) {} -else version (Posix) - version = HasUnzip; - -//debug=print; - -/// Thrown on error. -class ZipException : Exception -{ - import std.exception : basicExceptionCtors; - /// - mixin basicExceptionCtors; -} - -/// Compression method used by `ArchiveMember`. -enum CompressionMethod : ushort -{ - none = 0, /// No compression, just archiving. - deflate = 8 /// Deflate algorithm. Use zlib library to compress. -} - -/// A single file or directory inside the archive. -final class ArchiveMember -{ - import std.conv : to, octal; - import std.datetime.systime : DosFileTime, SysTime, SysTimeToDosFileTime; - - /** - * The name of the archive member; it is used to index the - * archive directory for the member. Each member must have a - * unique name. Do not change without removing member from the - * directory first. - */ - string name; - - /** - * The content of the extra data field for this member. See - * $(LINK2 https://pkware.cachefly.net/webdocs/casestudies/APPNOTE.TXT, - * original documentation) - * for a description of the general format of this data. May contain - * undocumented 3rd-party data. - */ - ubyte[] extra; - - string comment; /// Comment associated with this member. - - private ubyte[] _compressedData; - private ubyte[] _expandedData; - private uint offset; - private uint _crc32; - private uint _compressedSize; - private uint _expandedSize; - private CompressionMethod _compressionMethod; - private ushort _madeVersion = 20; - private ushort _extractVersion = 20; - private uint _externalAttributes; - private DosFileTime _time; - // by default, no explicit order goes after explicit order - private uint _index = uint.max; - - /** - * Contains some information on how to extract this archive. See - * $(LINK2 https://pkware.cachefly.net/webdocs/casestudies/APPNOTE.TXT, - * original documentation) - * for details. - */ - ushort flags; - - /** - * Internal attributes. Bit 1 is set, if the member is apparently in binary format - * and bit 2 is set, if each record is preceded by the length of the record. - */ - ushort internalAttributes; - - /** - * The zip file format version needed to extract this member. - * - * Returns: Format version needed to extract this member. - */ - @property @safe pure nothrow @nogc ushort extractVersion() const { return _extractVersion; } - - /** - * Cyclic redundancy check (CRC) value. - * - * Returns: CRC32 value. - */ - @property @safe pure nothrow @nogc uint crc32() const { return _crc32; } - - /** - * Size of data of member in compressed form. - * - * Returns: Size of the compressed archive. - */ - @property @safe pure nothrow @nogc uint compressedSize() const { return _compressedSize; } - - /** - * Size of data of member in uncompressed form. - * - * Returns: Size of uncompressed archive. - */ - @property @safe pure nothrow @nogc uint expandedSize() const { return _expandedSize; } - - /** - * Data of member in compressed form. - * - * Returns: The file data in compressed form. - */ - @property @safe pure nothrow @nogc ubyte[] compressedData() { return _compressedData; } - - /** - * Get or set data of member in uncompressed form. When an existing archive is - * read `ZipArchive.expand` needs to be called before this can be accessed. - * - * Params: - * ed = Expanded Data. - * - * Returns: The file data. - */ - @property @safe pure nothrow @nogc ubyte[] expandedData() { return _expandedData; } - - /// ditto - @property @safe void expandedData(ubyte[] ed) - { - _expandedData = ed; - _expandedSize = to!uint(_expandedData.length); - - // Clean old compressed data, if any - _compressedData.length = 0; - _compressedSize = 0; - } - - /** - * Get or set the OS specific file attributes for this archive member. - * - * Params: - * attr = Attributes as obtained by $(REF getAttributes, std,file) or - * $(REF DirEntry.attributes, std,file). - * - * Returns: The file attributes or 0 if the file attributes were - * encoded for an incompatible OS (Windows vs. POSIX). - */ - @property @safe void fileAttributes(uint attr) - { - version (Posix) - { - _externalAttributes = (attr & 0xFFFF) << 16; - _madeVersion &= 0x00FF; - _madeVersion |= 0x0300; // attributes are in UNIX format - } - else version (Windows) - { - _externalAttributes = attr; - _madeVersion &= 0x00FF; // attributes are in MS-DOS and OS/2 format - } - else - { - static assert(0, "Unimplemented platform"); - } - } - - version (Posix) @safe unittest - { - auto am = new ArchiveMember(); - am.fileAttributes = octal!100644; - assert(am._externalAttributes == octal!100644 << 16); - assert((am._madeVersion & 0xFF00) == 0x0300); - } - - /// ditto - @property @nogc nothrow uint fileAttributes() const - { - version (Posix) - { - if ((_madeVersion & 0xFF00) == 0x0300) - return _externalAttributes >> 16; - return 0; - } - else version (Windows) - { - if ((_madeVersion & 0xFF00) == 0x0000) - return _externalAttributes; - return 0; - } - else - { - static assert(0, "Unimplemented platform"); - } - } - - /** - * Get or set the last modification time for this member. - * - * Params: - * time = Time to set (will be saved as DosFileTime, which is less accurate). - * - * Returns: - * The last modification time in DosFileFormat. - */ - @property DosFileTime time() const @safe pure nothrow @nogc - { - return _time; - } - - /// ditto - @property void time(SysTime time) - { - _time = SysTimeToDosFileTime(time); - } - - /// ditto - @property void time(DosFileTime time) @safe pure nothrow @nogc - { - _time = time; - } - - /** - * Get or set compression method used for this member. - * - * Params: - * cm = Compression method. - * - * Returns: Compression method. - * - * See_Also: - * $(LREF CompressionMethod) - **/ - @property @safe @nogc pure nothrow CompressionMethod compressionMethod() const { return _compressionMethod; } - - /// ditto - @property @safe pure void compressionMethod(CompressionMethod cm) - { - if (cm == _compressionMethod) return; - - enforce!ZipException(_compressedSize == 0, "Can't change compression method for a compressed element"); - - _compressionMethod = cm; - } - - /** - * The index of this archive member within the archive. Set this to a - * different value for reordering the members of an archive. - * - * Params: - * value = Index value to set. - * - * Returns: The index. - */ - @property uint index(uint value) @safe pure nothrow @nogc { return _index = value; } - @property uint index() const @safe pure nothrow @nogc { return _index; } /// ditto - - debug(print) - { - void print() - { - printf("name = '%.*s'\n", cast(int) name.length, name.ptr); - printf("\tcomment = '%.*s'\n", cast(int) comment.length, comment.ptr); - printf("\tmadeVersion = x%04x\n", _madeVersion); - printf("\textractVersion = x%04x\n", extractVersion); - printf("\tflags = x%04x\n", flags); - printf("\tcompressionMethod = %d\n", compressionMethod); - printf("\ttime = %d\n", time); - printf("\tcrc32 = x%08x\n", crc32); - printf("\texpandedSize = %d\n", expandedSize); - printf("\tcompressedSize = %d\n", compressedSize); - printf("\tinternalAttributes = x%04x\n", internalAttributes); - printf("\texternalAttributes = x%08x\n", externalAttributes); - printf("\tindex = x%08x\n", index); - } - } -} - -@safe pure unittest -{ - import std.exception : assertThrown, assertNotThrown; - - auto am = new ArchiveMember(); - - assertNotThrown(am.compressionMethod(CompressionMethod.deflate)); - assertNotThrown(am.compressionMethod(CompressionMethod.none)); - - am._compressedData = [0x65]; // not strictly necessary, but for consistency - am._compressedSize = 1; - - assertThrown!ZipException(am.compressionMethod(CompressionMethod.deflate)); -} - -/** - * Object representing the entire archive. - * ZipArchives are collections of ArchiveMembers. - */ -final class ZipArchive -{ - import std.algorithm.comparison : max; - import std.bitmanip : littleEndianToNative, nativeToLittleEndian; - import std.conv : to; - import std.datetime.systime : DosFileTime; - -private: - // names are taken directly from the specification - // https://pkware.cachefly.net/webdocs/casestudies/APPNOTE.TXT - static immutable ubyte[] centralFileHeaderSignature = [ 0x50, 0x4b, 0x01, 0x02 ]; - static immutable ubyte[] localFileHeaderSignature = [ 0x50, 0x4b, 0x03, 0x04 ]; - static immutable ubyte[] endOfCentralDirSignature = [ 0x50, 0x4b, 0x05, 0x06 ]; - static immutable ubyte[] archiveExtraDataSignature = [ 0x50, 0x4b, 0x06, 0x08 ]; - static immutable ubyte[] digitalSignatureSignature = [ 0x50, 0x4b, 0x05, 0x05 ]; - static immutable ubyte[] zip64EndOfCentralDirSignature = [ 0x50, 0x4b, 0x06, 0x06 ]; - static immutable ubyte[] zip64EndOfCentralDirLocatorSignature = [ 0x50, 0x4b, 0x06, 0x07 ]; - - enum centralFileHeaderLength = 46; - enum localFileHeaderLength = 30; - enum endOfCentralDirLength = 22; - enum archiveExtraDataLength = 8; - enum digitalSignatureLength = 6; - enum zip64EndOfCentralDirLength = 56; - enum zip64EndOfCentralDirLocatorLength = 20; - enum dataDescriptorLength = 12; - -public: - string comment; /// The archive comment. Must be less than 65536 bytes in length. - - private ubyte[] _data; - - private bool _isZip64; - static const ushort zip64ExtractVersion = 45; - - private Segment[] _segs; - - /** - * Array representing the entire contents of the archive. - * - * Returns: Data of the entire contents of the archive. - */ - @property @safe @nogc pure nothrow ubyte[] data() { return _data; } - - /** - * Number of ArchiveMembers in the directory. - * - * Returns: The number of files in this archive. - */ - @property @safe @nogc pure nothrow uint totalEntries() const { return cast(uint) _directory.length; } - - /** - * True when the archive is in Zip64 format. Set this to true to force building a Zip64 archive. - * - * Params: - * value = True, when the archive is forced to be build in Zip64 format. - * - * Returns: True, when the archive is in Zip64 format. - */ - @property @safe @nogc pure nothrow bool isZip64() const { return _isZip64; } - - /// ditto - @property @safe @nogc pure nothrow void isZip64(bool value) { _isZip64 = value; } - - /** - * Associative array indexed by the name of each member of the archive. - * - * All the members of the archive can be accessed with a foreach loop: - * - * Example: - * -------------------- - * ZipArchive archive = new ZipArchive(data); - * foreach (ArchiveMember am; archive.directory) - * { - * writefln("member name is '%s'", am.name); - * } - * -------------------- - * - * Returns: Associative array with all archive members. - */ - @property @safe @nogc pure nothrow ArchiveMember[string] directory() { return _directory; } - - private ArchiveMember[string] _directory; - - debug (print) - { - @safe void print() - { - printf("\tdiskNumber = %u\n", diskNumber); - printf("\tdiskStartDir = %u\n", diskStartDir); - printf("\tnumEntries = %u\n", numEntries); - printf("\ttotalEntries = %u\n", totalEntries); - printf("\tcomment = '%.*s'\n", cast(int) comment.length, comment.ptr); - } - } - - /* ============ Creating a new archive =================== */ - - /** - * Constructor to use when creating a new archive. - */ - this() @safe @nogc pure nothrow - { - } - - /** - * Add a member to the archive. The file is compressed on the fly. - * - * Params: - * de = Member to be added. - * - * Throws: ZipException when an unsupported compression method is used or when - * compression failed. - */ - @safe void addMember(ArchiveMember de) - { - _directory[de.name] = de; - if (!de._compressedData.length) - { - switch (de.compressionMethod) - { - case CompressionMethod.none: - de._compressedData = de._expandedData; - break; - - case CompressionMethod.deflate: - import std.zlib : compress; - () @trusted - { - de._compressedData = cast(ubyte[]) compress(cast(void[]) de._expandedData); - }(); - de._compressedData = de._compressedData[2 .. de._compressedData.length - 4]; - break; - - default: - throw new ZipException("unsupported compression method"); - } - - de._compressedSize = to!uint(de._compressedData.length); - import std.zlib : crc32; - () @trusted { de._crc32 = crc32(0, cast(void[]) de._expandedData); }(); - } - assert(de._compressedData.length == de._compressedSize, "Archive member compressed failed."); - } - - @safe unittest - { - import std.exception : assertThrown; - - ArchiveMember am = new ArchiveMember(); - am.compressionMethod = cast(CompressionMethod) 3; - - ZipArchive zip = new ZipArchive(); - - assertThrown!ZipException(zip.addMember(am)); - } - - /** - * Delete member `de` from the archive. Uses the name of the member - * to detect which element to delete. - * - * Params: - * de = Member to be deleted. - */ - @safe void deleteMember(ArchiveMember de) - { - _directory.remove(de.name); - } - - // https://issues.dlang.org/show_bug.cgi?id=20398 - @safe unittest - { - import std.string : representation; - - ArchiveMember file1 = new ArchiveMember(); - file1.name = "test1.txt"; - file1.expandedData("Test data.\n".dup.representation); - - ZipArchive zip = new ZipArchive(); - - zip.addMember(file1); - assert(zip.totalEntries == 1); - - zip.deleteMember(file1); - assert(zip.totalEntries == 0); - } - - /** - * Construct the entire contents of the current members of the archive. - * - * Fills in the properties data[], totalEntries, and directory[]. - * For each ArchiveMember, fills in properties crc32, compressedSize, - * compressedData[]. - * - * Returns: Array representing the entire archive. - * - * Throws: ZipException when the archive could not be build. - */ - void[] build() @safe pure - { - import std.array : array, uninitializedArray; - import std.algorithm.sorting : sort; - import std.string : representation; - - uint i; - uint directoryOffset; - - enforce!ZipException(comment.length <= 0xFFFF, "archive comment longer than 65535"); - - // Compress each member; compute size - uint archiveSize = 0; - uint directorySize = 0; - auto directory = _directory.byValue.array.sort!((x, y) => x.index < y.index).release; - foreach (ArchiveMember de; directory) - { - enforce!ZipException(to!ulong(archiveSize) + localFileHeaderLength + de.name.length - + de.extra.length + de.compressedSize + directorySize - + centralFileHeaderLength + de.name.length + de.extra.length - + de.comment.length + endOfCentralDirLength + comment.length - + zip64EndOfCentralDirLocatorLength + zip64EndOfCentralDirLength <= uint.max, - "zip files bigger than 4 GB are unsupported"); - - archiveSize += localFileHeaderLength + de.name.length + - de.extra.length + - de.compressedSize; - directorySize += centralFileHeaderLength + de.name.length + - de.extra.length + - de.comment.length; - } - - if (!isZip64 && _directory.length > ushort.max) - _isZip64 = true; - uint dataSize = archiveSize + directorySize + endOfCentralDirLength + cast(uint) comment.length; - if (isZip64) - dataSize += zip64EndOfCentralDirLocatorLength + zip64EndOfCentralDirLength; - - _data = uninitializedArray!(ubyte[])(dataSize); - - // Populate the data[] - - // Store each archive member - i = 0; - foreach (ArchiveMember de; directory) - { - de.offset = i; - _data[i .. i + 4] = localFileHeaderSignature; - putUshort(i + 4, de.extractVersion); - putUshort(i + 6, de.flags); - putUshort(i + 8, de._compressionMethod); - putUint (i + 10, cast(uint) de.time); - putUint (i + 14, de.crc32); - putUint (i + 18, de.compressedSize); - putUint (i + 22, to!uint(de.expandedSize)); - putUshort(i + 26, cast(ushort) de.name.length); - putUshort(i + 28, cast(ushort) de.extra.length); - i += localFileHeaderLength; - - _data[i .. i + de.name.length] = (de.name.representation)[]; - i += de.name.length; - _data[i .. i + de.extra.length] = (cast(ubyte[]) de.extra)[]; - i += de.extra.length; - _data[i .. i + de.compressedSize] = de.compressedData[]; - i += de.compressedSize; - } - - // Write directory - directoryOffset = i; - foreach (ArchiveMember de; directory) - { - _data[i .. i + 4] = centralFileHeaderSignature; - putUshort(i + 4, de._madeVersion); - putUshort(i + 6, de.extractVersion); - putUshort(i + 8, de.flags); - putUshort(i + 10, de._compressionMethod); - putUint (i + 12, cast(uint) de.time); - putUint (i + 16, de.crc32); - putUint (i + 20, de.compressedSize); - putUint (i + 24, de.expandedSize); - putUshort(i + 28, cast(ushort) de.name.length); - putUshort(i + 30, cast(ushort) de.extra.length); - putUshort(i + 32, cast(ushort) de.comment.length); - putUshort(i + 34, cast(ushort) 0); - putUshort(i + 36, de.internalAttributes); - putUint (i + 38, de._externalAttributes); - putUint (i + 42, de.offset); - i += centralFileHeaderLength; - - _data[i .. i + de.name.length] = (de.name.representation)[]; - i += de.name.length; - _data[i .. i + de.extra.length] = (cast(ubyte[]) de.extra)[]; - i += de.extra.length; - _data[i .. i + de.comment.length] = (de.comment.representation)[]; - i += de.comment.length; - } - - if (isZip64) - { - // Write zip64 end of central directory record - uint eocd64Offset = i; - _data[i .. i + 4] = zip64EndOfCentralDirSignature; - putUlong (i + 4, zip64EndOfCentralDirLength - 12); - putUshort(i + 12, zip64ExtractVersion); - putUshort(i + 14, zip64ExtractVersion); - putUint (i + 16, cast(ushort) 0); - putUint (i + 20, cast(ushort) 0); - putUlong (i + 24, directory.length); - putUlong (i + 32, directory.length); - putUlong (i + 40, directorySize); - putUlong (i + 48, directoryOffset); - i += zip64EndOfCentralDirLength; - - // Write zip64 end of central directory record locator - _data[i .. i + 4] = zip64EndOfCentralDirLocatorSignature; - putUint (i + 4, cast(ushort) 0); - putUlong (i + 8, eocd64Offset); - putUint (i + 16, 1); - i += zip64EndOfCentralDirLocatorLength; - } - - // Write end record - _data[i .. i + 4] = endOfCentralDirSignature; - putUshort(i + 4, cast(ushort) 0); - putUshort(i + 6, cast(ushort) 0); - putUshort(i + 8, (totalEntries > ushort.max ? ushort.max : cast(ushort) totalEntries)); - putUshort(i + 10, (totalEntries > ushort.max ? ushort.max : cast(ushort) totalEntries)); - putUint (i + 12, directorySize); - putUint (i + 16, directoryOffset); - putUshort(i + 20, cast(ushort) comment.length); - i += endOfCentralDirLength; - - // Write archive comment - assert(i + comment.length == data.length, "Writing the archive comment failed."); - _data[i .. data.length] = (comment.representation)[]; - - return cast(void[]) data; - } - - @safe pure unittest - { - import std.exception : assertNotThrown; - - ZipArchive zip = new ZipArchive(); - zip.comment = "A"; - assertNotThrown(zip.build()); - } - - @safe pure unittest - { - import std.range : repeat, array; - import std.exception : assertThrown; - - ZipArchive zip = new ZipArchive(); - zip.comment = 'A'.repeat(70_000).array; - assertThrown!ZipException(zip.build()); - } - - /* ============ Reading an existing archive =================== */ - - /** - * Constructor to use when reading an existing archive. - * - * Fills in the properties data[], totalEntries, comment[], and directory[]. - * For each ArchiveMember, fills in - * properties madeVersion, extractVersion, flags, compressionMethod, time, - * crc32, compressedSize, expandedSize, compressedData[], - * internalAttributes, externalAttributes, name[], extra[], comment[]. - * Use expand() to get the expanded data for each ArchiveMember. - * - * Params: - * buffer = The entire contents of the archive. - * - * Throws: ZipException when the archive was invalid or when malware was detected. - */ - this(void[] buffer) - { - this._data = cast(ubyte[]) buffer; - - enforce!ZipException(data.length <= uint.max - 2, "zip files bigger than 4 GB are unsupported"); - - _segs = [Segment(0, cast(uint) data.length)]; - - uint i = findEndOfCentralDirRecord(); - - int endCommentLength = getUshort(i + 20); - comment = cast(string)(_data[i + endOfCentralDirLength .. i + endOfCentralDirLength + endCommentLength]); - - // end of central dir record - removeSegment(i, i + endOfCentralDirLength + endCommentLength); - - uint k = i - zip64EndOfCentralDirLocatorLength; - if (k < i && _data[k .. k + 4] == zip64EndOfCentralDirLocatorSignature) - { - _isZip64 = true; - i = k; - - // zip64 end of central dir record locator - removeSegment(k, k + zip64EndOfCentralDirLocatorLength); - } - - uint directorySize; - uint directoryOffset; - uint directoryCount; - - if (isZip64) - { - // Read Zip64 record data - ulong eocdOffset = getUlong(i + 8); - enforce!ZipException(eocdOffset + zip64EndOfCentralDirLength <= _data.length, - "corrupted directory"); - - i = to!uint(eocdOffset); - enforce!ZipException(_data[i .. i + 4] == zip64EndOfCentralDirSignature, - "invalid Zip EOCD64 signature"); - - ulong eocd64Size = getUlong(i + 4); - enforce!ZipException(eocd64Size + i - 12 <= data.length, - "invalid Zip EOCD64 size"); - - // zip64 end of central dir record - removeSegment(i, cast(uint) (i + 12 + eocd64Size)); - - ulong numEntriesUlong = getUlong(i + 24); - ulong totalEntriesUlong = getUlong(i + 32); - ulong directorySizeUlong = getUlong(i + 40); - ulong directoryOffsetUlong = getUlong(i + 48); - - enforce!ZipException(numEntriesUlong <= uint.max, - "supposedly more than 4294967296 files in archive"); - - enforce!ZipException(numEntriesUlong == totalEntriesUlong, - "multiple disk zips not supported"); - - enforce!ZipException(directorySizeUlong <= i && directoryOffsetUlong <= i - && directorySizeUlong + directoryOffsetUlong <= i, - "corrupted directory"); - - directoryCount = to!uint(totalEntriesUlong); - directorySize = to!uint(directorySizeUlong); - directoryOffset = to!uint(directoryOffsetUlong); - } - else - { - // Read end record data - directoryCount = getUshort(i + 10); - directorySize = getUint(i + 12); - directoryOffset = getUint(i + 16); - } - - i = directoryOffset; - for (int n = 0; n < directoryCount; n++) - { - /* The format of an entry is: - * 'PK' 1, 2 - * directory info - * path - * extra data - * comment - */ - - uint namelen; - uint extralen; - uint commentlen; - - enforce!ZipException(_data[i .. i + 4] == centralFileHeaderSignature, - "wrong central file header signature found"); - ArchiveMember de = new ArchiveMember(); - de._index = n; - de._madeVersion = getUshort(i + 4); - de._extractVersion = getUshort(i + 6); - de.flags = getUshort(i + 8); - de._compressionMethod = cast(CompressionMethod) getUshort(i + 10); - de.time = cast(DosFileTime) getUint(i + 12); - de._crc32 = getUint(i + 16); - de._compressedSize = getUint(i + 20); - de._expandedSize = getUint(i + 24); - namelen = getUshort(i + 28); - extralen = getUshort(i + 30); - commentlen = getUshort(i + 32); - de.internalAttributes = getUshort(i + 36); - de._externalAttributes = getUint(i + 38); - de.offset = getUint(i + 42); - - // central file header - removeSegment(i, i + centralFileHeaderLength + namelen + extralen + commentlen); - - i += centralFileHeaderLength; - - enforce!ZipException(i + namelen + extralen + commentlen <= directoryOffset + directorySize, - "invalid field lengths in file header found"); - - de.name = cast(string)(_data[i .. i + namelen]); - i += namelen; - de.extra = _data[i .. i + extralen]; - i += extralen; - de.comment = cast(string)(_data[i .. i + commentlen]); - i += commentlen; - - auto localFileHeaderNamelen = getUshort(de.offset + 26); - auto localFileHeaderExtralen = getUshort(de.offset + 28); - - // file data - removeSegment(de.offset, de.offset + localFileHeaderLength + localFileHeaderNamelen - + localFileHeaderExtralen + de._compressedSize); - - immutable uint dataOffset = de.offset + localFileHeaderLength - + localFileHeaderNamelen + localFileHeaderExtralen; - de._compressedData = _data[dataOffset .. dataOffset + de.compressedSize]; - - _directory[de.name] = de; - } - - enforce!ZipException(i == directoryOffset + directorySize, "invalid directory entry 3"); - } - - @system unittest - { - import std.exception : assertThrown; - - // contains wrong directorySize (extra byte 0xff) - auto file = - "\x50\x4b\x03\x04\x0a\x00\x00\x00\x00\x00\x8f\x72\x4a\x4f\x86\xa6"~ - "\x10\x36\x05\x00\x00\x00\x05\x00\x00\x00\x04\x00\x1c\x00\x66\x69"~ - "\x6c\x65\x55\x54\x09\x00\x03\x0d\x22\x9f\x5d\x12\x22\x9f\x5d\x75"~ - "\x78\x0b\x00\x01\x04\xf0\x03\x00\x00\x04\xf0\x03\x00\x00\x68\x65"~ - "\x6c\x6c\x6f\x50\x4b\x01\x02\x1e\x03\x0a\x00\x00\x00\x00\x00\x8f"~ - "\x72\x4a\x4f\x86\xa6\x10\x36\x05\x00\x00\x00\x05\x00\x00\x00\x04"~ - "\x00\x18\x00\x00\x00\x00\x00\x01\x00\x00\x00\xb0\x81\x00\x00\x00"~ - "\x00\x66\x69\x6c\x65\x55\x54\x05\x00\x03\x0d\x22\x9f\x5d\x75\x78"~ - "\x0b\x00\x01\x04\xf0\x03\x00\x00\x04\xf0\x03\x00\x00\xff\x50\x4b\x05"~ - "\x06\x00\x00\x00\x00\x01\x00\x01\x00\x4b\x00\x00\x00\x43\x00\x00"~ - "\x00\x00\x00"; - - assertThrown!ZipException(new ZipArchive(cast(void[]) file)); - } - - @system unittest - { - import std.exception : assertThrown; - - // wrong eocdOffset - auto file = - "\x50\x4b\x06\x06\x2c\x00\x00\x00\x00\x00\x00\x00\x1e\x03\x2d\x00"~ - "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"~ - "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"~ - "\x00\x00\x00\x00\x00\x00\x00\x00\x50\x4b\x06\x07\x00\x00\x00\x00"~ - "\xff\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x50\x4B\x05\x06"~ - "\x00\x00\x00\x00\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff"~ - "\x00\x00"; - - assertThrown!ZipException(new ZipArchive(cast(void[]) file)); - } - - @system unittest - { - import std.exception : assertThrown; - - // wrong signature of zip64 end of central directory - auto file = - "\x50\x4b\x06\x07\x2c\x00\x00\x00\x00\x00\x00\x00\x1e\x03\x2d\x00"~ - "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"~ - "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"~ - "\x00\x00\x00\x00\x00\x00\x00\x00\x50\x4b\x06\x07\x00\x00\x00\x00"~ - "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x50\x4B\x05\x06"~ - "\x00\x00\x00\x00\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff"~ - "\x00\x00"; - - assertThrown!ZipException(new ZipArchive(cast(void[]) file)); - } - - @system unittest - { - import std.exception : assertThrown; - - // wrong size of zip64 end of central directory - auto file = - "\x50\x4b\x06\x06\xff\x00\x00\x00\x00\x00\x00\x00\x1e\x03\x2d\x00"~ - "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"~ - "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"~ - "\x00\x00\x00\x00\x00\x00\x00\x00\x50\x4b\x06\x07\x00\x00\x00\x00"~ - "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x50\x4B\x05\x06"~ - "\x00\x00\x00\x00\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff"~ - "\x00\x00"; - - assertThrown!ZipException(new ZipArchive(cast(void[]) file)); - } - - @system unittest - { - import std.exception : assertThrown; - - // too many entries in zip64 end of central directory - auto file = - "\x50\x4b\x06\x06\x2c\x00\x00\x00\x00\x00\x00\x00\x1e\x03\x2d\x00"~ - "\x00\x00\x00\x00\x00\x00\x00\x00\xff\xff\xff\xff\xff\x00\x00\x00"~ - "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"~ - "\x00\x00\x00\x00\x00\x00\x00\x00\x50\x4b\x06\x07\x00\x00\x00\x00"~ - "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x50\x4B\x05\x06"~ - "\x00\x00\x00\x00\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff"~ - "\x00\x00"; - - assertThrown!ZipException(new ZipArchive(cast(void[]) file)); - } - - @system unittest - { - import std.exception : assertThrown; - - // zip64: numEntries and totalEntries differ - auto file = - "\x50\x4b\x06\x06\x2c\x00\x00\x00\x00\x00\x00\x00\x1e\x03\x2d\x00"~ - "\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00"~ - "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"~ - "\x00\x00\x00\x00\x00\x00\x00\x00\x50\x4b\x06\x07\x00\x00\x00\x00"~ - "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x50\x4B\x05\x06"~ - "\x00\x00\x00\x00\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff"~ - "\x00\x00"; - - assertThrown!ZipException(new ZipArchive(cast(void[]) file)); - } - - @system unittest - { - import std.exception : assertThrown; - - // zip64: directorySize too large - auto file = - "\x50\x4b\x06\x06\x2c\x00\x00\x00\x00\x00\x00\x00\x1e\x03\x2d\x00"~ - "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"~ - "\x00\x00\x00\x00\x00\x00\x00\x00\xff\xff\x00\x00\x00\x00\x00\x00"~ - "\x00\x00\x00\x00\x00\x00\x00\x00\x50\x4b\x06\x07\x00\x00\x00\x00"~ - "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x50\x4B\x05\x06"~ - "\x00\x00\x00\x00\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff"~ - "\x00\x00"; - - assertThrown!ZipException(new ZipArchive(cast(void[]) file)); - - // zip64: directoryOffset too large - file = - "\x50\x4b\x06\x06\x2c\x00\x00\x00\x00\x00\x00\x00\x1e\x03\x2d\x00"~ - "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"~ - "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"~ - "\xff\xff\x00\x00\x00\x00\x00\x00\x50\x4b\x06\x07\x00\x00\x00\x00"~ - "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x50\x4B\x05\x06"~ - "\x00\x00\x00\x00\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff"~ - "\x00\x00"; - - assertThrown!ZipException(new ZipArchive(cast(void[]) file)); - - // zip64: directorySize + directoryOffset too large - // we need to add a useless byte at the beginning to avoid that one of the other two checks allready fires - file = - "\x00\x50\x4b\x06\x06\x2c\x00\x00\x00\x00\x00\x00\x00\x1e\x03\x2d\x00"~ - "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"~ - "\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00"~ - "\x01\x00\x00\x00\x00\x00\x00\x00\x50\x4b\x06\x07\x00\x00\x00\x00"~ - "\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x50\x4B\x05\x06"~ - "\x00\x00\x00\x00\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff"~ - "\x00\x00"; - - assertThrown!ZipException(new ZipArchive(cast(void[]) file)); - } - - @system unittest - { - import std.exception : assertThrown; - - // wrong central file header signature - auto file = - "\x50\x4b\x03\x04\x0a\x00\x00\x00\x00\x00\x8f\x72\x4a\x4f\x86\xa6"~ - "\x10\x36\x05\x00\x00\x00\x05\x00\x00\x00\x04\x00\x1c\x00\x66\x69"~ - "\x6c\x65\x55\x54\x09\x00\x03\x0d\x22\x9f\x5d\x12\x22\x9f\x5d\x75"~ - "\x78\x0b\x00\x01\x04\xf0\x03\x00\x00\x04\xf0\x03\x00\x00\x68\x65"~ - "\x6c\x6c\x6f\x50\x4b\x01\x03\x1e\x03\x0a\x00\x00\x00\x00\x00\x8f"~ - "\x72\x4a\x4f\x86\xa6\x10\x36\x05\x00\x00\x00\x05\x00\x00\x00\x04"~ - "\x00\x18\x00\x00\x00\x00\x00\x01\x00\x00\x00\xb0\x81\x00\x00\x00"~ - "\x00\x66\x69\x6c\x65\x55\x54\x05\x00\x03\x0d\x22\x9f\x5d\x75\x78"~ - "\x0b\x00\x01\x04\xf0\x03\x00\x00\x04\xf0\x03\x00\x00\x50\x4b\x05"~ - "\x06\x00\x00\x00\x00\x01\x00\x01\x00\x4a\x00\x00\x00\x43\x00\x00"~ - "\x00\x00\x00"; - - assertThrown!ZipException(new ZipArchive(cast(void[]) file)); - } - - @system unittest - { - import std.exception : assertThrown; - - // invalid field lengths in file header - auto file = - "\x50\x4b\x03\x04\x0a\x00\x00\x00\x00\x00\x8f\x72\x4a\x4f\x86\xa6"~ - "\x10\x36\x05\x00\x00\x00\x05\x00\x00\x00\x04\x00\x1c\x00\x66\x69"~ - "\x6c\x65\x55\x54\x09\x00\x03\x0d\x22\x9f\x5d\x12\x22\x9f\x5d\x75"~ - "\x78\x0b\x00\x01\x04\xf0\x03\x00\x00\x04\xf0\x03\x00\x00\x68\x65"~ - "\x6c\x6c\x6f\x50\x4b\x01\x02\x1e\x03\x0a\x00\x00\x00\x00\x00\x8f"~ - "\x72\x4a\x4f\x86\xa6\x10\x36\x05\x00\x00\x00\x05\x00\x00\x00\x04"~ - "\x00\x18\x00\x01\x00\x00\x00\x01\x00\x00\x00\xb0\x81\x00\x00\x00"~ - "\x00\x66\x69\x6c\x65\x55\x54\x05\x00\x03\x0d\x22\x9f\x5d\x75\x78"~ - "\x0b\x00\x01\x04\xf0\x03\x00\x00\x04\xf0\x03\x00\x00\xff\x50\x4b\x05"~ - "\x06\x00\x00\x00\x00\x01\x00\x01\x00\x4a\x00\x00\x00\x43\x00\x00"~ - "\x00\x00\x00"; - - assertThrown!ZipException(new ZipArchive(cast(void[]) file)); - } - - private uint findEndOfCentralDirRecord() - { - // end of central dir record can be followed by a comment of up to 2^^16-1 bytes - // therefore we have to scan 2^^16 positions - - uint endrecOffset = to!uint(data.length); - foreach (i; 0 .. 2 ^^ 16) - { - if (endOfCentralDirLength + i > data.length) break; - uint start = to!uint(data.length) - endOfCentralDirLength - i; - - if (data[start .. start + 4] != endOfCentralDirSignature) continue; - - auto numberOfThisDisc = getUshort(start + 4); - if (numberOfThisDisc != 0) continue; // no support for multiple volumes yet - - auto numberOfStartOfCentralDirectory = getUshort(start + 6); - if (numberOfStartOfCentralDirectory != 0) continue; // dito - - if (numberOfThisDisc < numberOfStartOfCentralDirectory) continue; - - uint k = start - zip64EndOfCentralDirLocatorLength; - auto maybeZip64 = k < start && _data[k .. k + 4] == zip64EndOfCentralDirLocatorSignature; - - auto totalNumberOfEntriesOnThisDisk = getUshort(start + 8); - auto totalNumberOfEntriesInCentralDir = getUshort(start + 10); - - if (totalNumberOfEntriesOnThisDisk > totalNumberOfEntriesInCentralDir && - (!maybeZip64 || totalNumberOfEntriesOnThisDisk < 0xffff)) continue; - - auto sizeOfCentralDirectory = getUint(start + 12); - if (sizeOfCentralDirectory > start && - (!maybeZip64 || sizeOfCentralDirectory < 0xffff)) continue; - - auto offsetOfCentralDirectory = getUint(start + 16); - if (offsetOfCentralDirectory > start - sizeOfCentralDirectory && - (!maybeZip64 || offsetOfCentralDirectory < 0xffff)) continue; - - auto zipfileCommentLength = getUshort(start + 20); - if (start + zipfileCommentLength + endOfCentralDirLength != data.length) continue; - - enforce!ZipException(endrecOffset == to!uint(data.length), - "found more than one valid 'end of central dir record'"); - - endrecOffset = start; - } - - enforce!ZipException(endrecOffset != to!uint(data.length), - "found no valid 'end of central dir record'"); - - return endrecOffset; - } - - /** - * Decompress the contents of a member. - * - * Fills in properties extractVersion, flags, compressionMethod, time, - * crc32, compressedSize, expandedSize, expandedData[], name[], extra[]. - * - * Params: - * de = Member to be decompressed. - * - * Returns: The expanded data. - * - * Throws: ZipException when the entry is invalid or the compression method is not supported. - */ - ubyte[] expand(ArchiveMember de) - { - import std.string : representation; - - uint namelen; - uint extralen; - - enforce!ZipException(_data[de.offset .. de.offset + 4] == localFileHeaderSignature, - "wrong local file header signature found"); - - // These values should match what is in the main zip archive directory - de._extractVersion = getUshort(de.offset + 4); - de.flags = getUshort(de.offset + 6); - de._compressionMethod = cast(CompressionMethod) getUshort(de.offset + 8); - de.time = cast(DosFileTime) getUint(de.offset + 10); - de._crc32 = getUint(de.offset + 14); - de._compressedSize = max(getUint(de.offset + 18), de.compressedSize); - de._expandedSize = max(getUint(de.offset + 22), de.expandedSize); - namelen = getUshort(de.offset + 26); - extralen = getUshort(de.offset + 28); - - debug(print) - { - printf("\t\texpandedSize = %d\n", de.expandedSize); - printf("\t\tcompressedSize = %d\n", de.compressedSize); - printf("\t\tnamelen = %d\n", namelen); - printf("\t\textralen = %d\n", extralen); - } - - enforce!ZipException((de.flags & 1) == 0, "encryption not supported"); - - switch (de.compressionMethod) - { - case CompressionMethod.none: - de._expandedData = de.compressedData; - return de.expandedData; - - case CompressionMethod.deflate: - // -15 is a magic value used to decompress zip files. - // It has the effect of not requiring the 2 byte header - // and 4 byte trailer. - import std.zlib : uncompress; - de._expandedData = cast(ubyte[]) uncompress(cast(void[]) de.compressedData, de.expandedSize, -15); - return de.expandedData; - - default: - throw new ZipException("unsupported compression method"); - } - } - - @system unittest - { - import std.exception : assertThrown; - - // check for correct local file header signature - auto file = - "\x50\x4b\x04\x04\x0a\x00\x00\x00\x00\x00\x8f\x72\x4a\x4f\x86\xa6"~ - "\x10\x36\x05\x00\x00\x00\x05\x00\x00\x00\x04\x00\x1c\x00\x66\x69"~ - "\x6c\x65\x55\x54\x09\x00\x03\x0d\x22\x9f\x5d\x12\x22\x9f\x5d\x75"~ - "\x78\x0b\x00\x01\x04\xf0\x03\x00\x00\x04\xf0\x03\x00\x00\x68\x65"~ - "\x6c\x6c\x6f\x50\x4b\x01\x02\x1e\x03\x0a\x00\x00\x00\x00\x00\x8f"~ - "\x72\x4a\x4f\x86\xa6\x10\x36\x05\x00\x00\x00\x05\x00\x00\x00\x04"~ - "\x00\x18\x00\x00\x00\x00\x00\x01\x00\x00\x00\xb0\x81\x00\x00\x00"~ - "\x00\x66\x69\x6c\x65\x55\x54\x05\x00\x03\x0d\x22\x9f\x5d\x75\x78"~ - "\x0b\x00\x01\x04\xf0\x03\x00\x00\x04\xf0\x03\x00\x00\x50\x4b\x05"~ - "\x06\x00\x00\x00\x00\x01\x00\x01\x00\x4a\x00\x00\x00\x43\x00\x00"~ - "\x00\x00\x00"; - - auto za = new ZipArchive(cast(void[]) file); - - assertThrown!ZipException(za.expand(za._directory["file"])); - } - - @system unittest - { - import std.exception : assertThrown; - - // check for encryption flag - auto file = - "\x50\x4b\x03\x04\x0a\x00\x01\x00\x00\x00\x8f\x72\x4a\x4f\x86\xa6"~ - "\x10\x36\x05\x00\x00\x00\x05\x00\x00\x00\x04\x00\x1c\x00\x66\x69"~ - "\x6c\x65\x55\x54\x09\x00\x03\x0d\x22\x9f\x5d\x12\x22\x9f\x5d\x75"~ - "\x78\x0b\x00\x01\x04\xf0\x03\x00\x00\x04\xf0\x03\x00\x00\x68\x65"~ - "\x6c\x6c\x6f\x50\x4b\x01\x02\x1e\x03\x0a\x00\x00\x00\x00\x00\x8f"~ - "\x72\x4a\x4f\x86\xa6\x10\x36\x05\x00\x00\x00\x05\x00\x00\x00\x04"~ - "\x00\x18\x00\x00\x00\x00\x00\x01\x00\x00\x00\xb0\x81\x00\x00\x00"~ - "\x00\x66\x69\x6c\x65\x55\x54\x05\x00\x03\x0d\x22\x9f\x5d\x75\x78"~ - "\x0b\x00\x01\x04\xf0\x03\x00\x00\x04\xf0\x03\x00\x00\x50\x4b\x05"~ - "\x06\x00\x00\x00\x00\x01\x00\x01\x00\x4a\x00\x00\x00\x43\x00\x00"~ - "\x00\x00\x00"; - - auto za = new ZipArchive(cast(void[]) file); - - assertThrown!ZipException(za.expand(za._directory["file"])); - } - - @system unittest - { - import std.exception : assertThrown; - - // check for invalid compression method - auto file = - "\x50\x4b\x03\x04\x0a\x00\x00\x00\x03\x00\x8f\x72\x4a\x4f\x86\xa6"~ - "\x10\x36\x05\x00\x00\x00\x05\x00\x00\x00\x04\x00\x1c\x00\x66\x69"~ - "\x6c\x65\x55\x54\x09\x00\x03\x0d\x22\x9f\x5d\x12\x22\x9f\x5d\x75"~ - "\x78\x0b\x00\x01\x04\xf0\x03\x00\x00\x04\xf0\x03\x00\x00\x68\x65"~ - "\x6c\x6c\x6f\x50\x4b\x01\x02\x1e\x03\x0a\x00\x00\x00\x00\x00\x8f"~ - "\x72\x4a\x4f\x86\xa6\x10\x36\x05\x00\x00\x00\x05\x00\x00\x00\x04"~ - "\x00\x18\x00\x00\x00\x00\x00\x01\x00\x00\x00\xb0\x81\x00\x00\x00"~ - "\x00\x66\x69\x6c\x65\x55\x54\x05\x00\x03\x0d\x22\x9f\x5d\x75\x78"~ - "\x0b\x00\x01\x04\xf0\x03\x00\x00\x04\xf0\x03\x00\x00\x50\x4b\x05"~ - "\x06\x00\x00\x00\x00\x01\x00\x01\x00\x4a\x00\x00\x00\x43\x00\x00"~ - "\x00\x00\x00"; - - auto za = new ZipArchive(cast(void[]) file); - - assertThrown!ZipException(za.expand(za._directory["file"])); - } - - /* ============ Utility =================== */ - - @safe @nogc pure nothrow ushort getUshort(uint i) - { - ubyte[2] result = data[i .. i + 2]; - return littleEndianToNative!ushort(result); - } - - @safe @nogc pure nothrow uint getUint(uint i) - { - ubyte[4] result = data[i .. i + 4]; - return littleEndianToNative!uint(result); - } - - @safe @nogc pure nothrow ulong getUlong(uint i) - { - ubyte[8] result = data[i .. i + 8]; - return littleEndianToNative!ulong(result); - } - - @safe @nogc pure nothrow void putUshort(uint i, ushort us) - { - data[i .. i + 2] = nativeToLittleEndian(us); - } - - @safe @nogc pure nothrow void putUint(uint i, uint ui) - { - data[i .. i + 4] = nativeToLittleEndian(ui); - } - - @safe @nogc pure nothrow void putUlong(uint i, ulong ul) - { - data[i .. i + 8] = nativeToLittleEndian(ul); - } - - /* ============== for detecting overlaps =============== */ - -private: - - // defines a segment of the zip file, including start, excluding end - struct Segment - { - uint start; - uint end; - } - - // removes Segment start .. end from _segs - // throws zipException if start .. end is not completely available in _segs; - void removeSegment(uint start, uint end) pure @safe - in (start < end, "segment invalid") - { - auto found = false; - size_t pos; - foreach (i,seg;_segs) - if (seg.start <= start && seg.end >= end - && (!found || seg.start > _segs[pos].start)) - { - found = true; - pos = i; - } - - enforce!ZipException(found, "overlapping data detected"); - - if (start>_segs[pos].start) - _segs ~= Segment(_segs[pos].start, start); - if (end<_segs[pos].end) - _segs ~= Segment(end, _segs[pos].end); - _segs = _segs[0 .. pos] ~ _segs[pos + 1 .. $]; - } - - pure @safe unittest - { - with (new ZipArchive()) - { - _segs = [Segment(0,100)]; - removeSegment(10,20); - assert(_segs == [Segment(0,10),Segment(20,100)]); - - _segs = [Segment(0,100)]; - removeSegment(0,20); - assert(_segs == [Segment(20,100)]); - - _segs = [Segment(0,100)]; - removeSegment(10,100); - assert(_segs == [Segment(0,10)]); - - _segs = [Segment(0,100), Segment(200,300), Segment(400,500)]; - removeSegment(220,230); - assert(_segs == [Segment(0,100),Segment(400,500),Segment(200,220),Segment(230,300)]); - - _segs = [Segment(200,300), Segment(0,100), Segment(400,500)]; - removeSegment(20,30); - assert(_segs == [Segment(200,300),Segment(400,500),Segment(0,20),Segment(30,100)]); - - import std.exception : assertThrown; - - _segs = [Segment(0,100), Segment(200,300), Segment(400,500)]; - assertThrown(removeSegment(120,230)); - - _segs = [Segment(0,100), Segment(200,300), Segment(400,500)]; - removeSegment(0,100); - assertThrown(removeSegment(0,100)); - - _segs = [Segment(0,100)]; - removeSegment(0,100); - assertThrown(removeSegment(0,100)); - } - } -} - -debug(print) -{ - @safe void arrayPrint(ubyte[] array) - { - printf("array %p,%d\n", cast(void*) array, array.length); - for (int i = 0; i < array.length; i++) - { - printf("%02x ", array[i]); - if (((i + 1) & 15) == 0) - printf("\n"); - } - printf("\n"); - } -} - -@system unittest -{ - // @system due to (at least) ZipArchive.build - auto zip1 = new ZipArchive(); - auto zip2 = new ZipArchive(); - auto am1 = new ArchiveMember(); - am1.name = "foo"; - am1.expandedData = new ubyte[](1024); - zip1.addMember(am1); - auto data1 = zip1.build(); - zip2.addMember(zip1.directory["foo"]); - zip2.build(); - auto am2 = zip2.directory["foo"]; - zip2.expand(am2); - assert(am1.expandedData == am2.expandedData); - auto zip3 = new ZipArchive(data1); - zip3.build(); - assert(zip3.directory["foo"].compressedSize == am1.compressedSize); - - // Test if packing and unpacking produces the original data - import std.conv, std.stdio; - import std.random : uniform, MinstdRand0; - MinstdRand0 gen; - const uint itemCount = 20, minSize = 10, maxSize = 500; - foreach (variant; 0 .. 2) - { - bool useZip64 = !!variant; - zip1 = new ZipArchive(); - zip1.isZip64 = useZip64; - ArchiveMember[itemCount] ams; - foreach (i; 0 .. itemCount) - { - ams[i] = new ArchiveMember(); - ams[i].name = to!string(i); - ams[i].expandedData = new ubyte[](uniform(minSize, maxSize)); - foreach (ref ubyte c; ams[i].expandedData) - c = cast(ubyte)(uniform(0, 256)); - ams[i].compressionMethod = CompressionMethod.deflate; - zip1.addMember(ams[i]); - } - auto zippedData = zip1.build(); - zip2 = new ZipArchive(zippedData); - assert(zip2.isZip64 == useZip64); - foreach (am; ams) - { - am2 = zip2.directory[am.name]; - zip2.expand(am2); - assert(am.crc32 == am2.crc32); - assert(am.expandedData == am2.expandedData); - } - } -} - -@system unittest -{ - import std.conv : to; - import std.random : Mt19937, randomShuffle; - // Test if packing and unpacking preserves order. - auto rand = Mt19937(15966); - string[] names; - int value = 0; - // Generate a series of unique numbers as filenames. - foreach (i; 0 .. 20) - { - value += 1 + rand.front & 0xFFFF; - rand.popFront; - names ~= value.to!string; - } - // Insert them in a random order. - names.randomShuffle(rand); - auto zip1 = new ZipArchive(); - foreach (i, name; names) - { - auto member = new ArchiveMember(); - member.name = name; - member.expandedData = cast(ubyte[]) name; - member.index = cast(int) i; - zip1.addMember(member); - } - auto data = zip1.build(); - - // Ensure that they appear in the same order. - auto zip2 = new ZipArchive(data); - foreach (i, name; names) - { - const member = zip2.directory[name]; - assert(member.index == i, "member " ~ name ~ " had index " ~ - member.index.to!string ~ " but we expected index " ~ i.to!string ~ - ". The input array was " ~ names.to!string); - } -} - -@system unittest -{ - import std.zlib; - - ubyte[] src = cast(ubyte[]) -"the quick brown fox jumps over the lazy dog\r -the quick brown fox jumps over the lazy dog\r -"; - auto dst = cast(ubyte[]) compress(cast(void[]) src); - auto after = cast(ubyte[]) uncompress(cast(void[]) dst); - assert(src == after); -} - -@system unittest -{ - // @system due to ZipArchive.build - import std.datetime; - ubyte[] buf = [1, 2, 3, 4, 5, 0, 7, 8, 9]; - - auto ar = new ZipArchive; - auto am = new ArchiveMember; // 10 - am.name = "buf"; - am.expandedData = buf; - am.compressionMethod = CompressionMethod.deflate; - am.time = SysTimeToDosFileTime(Clock.currTime()); - ar.addMember(am); // 15 - - auto zip1 = ar.build(); - auto arAfter = new ZipArchive(zip1); - assert(arAfter.directory.length == 1); - auto amAfter = arAfter.directory["buf"]; - arAfter.expand(amAfter); - assert(amAfter.name == am.name); - assert(amAfter.expandedData == am.expandedData); - assert(amAfter.time == am.time); -} - -@system unittest -{ - // invalid format of end of central directory entry - import std.exception : assertThrown; - assertThrown!ZipException(new ZipArchive(cast(void[]) "\x50\x4B\x05\x06aaaaaaaaaaaaaaaaaaaa")); -} - -@system unittest -{ - // minimum (empty) archive should pass - auto za = new ZipArchive(cast(void[]) "\x50\x4B\x05\x06\x00\x00\x00\x00\x00\x00\x00"~ - "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"); - assert(za.directory.length == 0); - - // one byte too short or too long should not pass - import std.exception : assertThrown; - assertThrown!ZipException(new ZipArchive(cast(void[]) "\x50\x4B\x05\x06\x00\x00\x00\x00\x00\x00\x00"~ - "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00")); - assertThrown!ZipException(new ZipArchive(cast(void[]) "\x50\x4B\x05\x06\x00\x00\x00\x00\x00\x00\x00"~ - "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00")); -} - -@system unittest -{ - // https://issues.dlang.org/show_bug.cgi?id=20239 - // chameleon file, containing two valid end of central directory entries - auto file = - "\x50\x4B\x03\x04\x0A\x00\x00\x00\x00\x00\x89\x36\x39\x4F\x04\x6A\xB3\xA3\x01\x00"~ - "\x00\x00\x01\x00\x00\x00\x0D\x00\x1C\x00\x62\x65\x73\x74\x5F\x6C\x61\x6E\x67\x75"~ - "\x61\x67\x65\x55\x54\x09\x00\x03\x82\xF2\x8A\x5D\x82\xF2\x8A\x5D\x75\x78\x0B\x00"~ - "\x01\x04\xEB\x03\x00\x00\x04\xEB\x03\x00\x00\x44\x50\x4B\x01\x02\x1E\x03\x0A\x00"~ - "\x00\x00\x00\x00\x89\x36\x39\x4F\x04\x6A\xB3\xA3\x01\x00\x00\x00\x01\x00\x00\x00"~ - "\x0D\x00\x18\x00\x00\x00\x00\x00\x01\x00\x00\x00\xB0\x81\x00\x00\x00\x00\x62\x65"~ - "\x73\x74\x5F\x6C\x61\x6E\x67\x75\x61\x67\x65\x55\x54\x05\x00\x03\x82\xF2\x8A\x5D"~ - "\x75\x78\x0B\x00\x01\x04\xEB\x03\x00\x00\x04\xEB\x03\x00\x00\x50\x4B\x05\x06\x00"~ - "\x00\x00\x00\x01\x00\x01\x00\x53\x00\x00\x00\x48\x00\x00\x00\xB7\x00\x50\x4B\x03"~ - "\x04\x0A\x00\x00\x00\x00\x00\x94\x36\x39\x4F\xD7\xCB\x3B\x55\x07\x00\x00\x00\x07"~ - "\x00\x00\x00\x0D\x00\x1C\x00\x62\x65\x73\x74\x5F\x6C\x61\x6E\x67\x75\x61\x67\x65"~ - "\x55\x54\x09\x00\x03\x97\xF2\x8A\x5D\x8C\xF2\x8A\x5D\x75\x78\x0B\x00\x01\x04\xEB"~ - "\x03\x00\x00\x04\xEB\x03\x00\x00\x46\x4F\x52\x54\x52\x41\x4E\x50\x4B\x01\x02\x1E"~ - "\x03\x0A\x00\x00\x00\x00\x00\x94\x36\x39\x4F\xD7\xCB\x3B\x55\x07\x00\x00\x00\x07"~ - "\x00\x00\x00\x0D\x00\x18\x00\x00\x00\x00\x00\x01\x00\x00\x00\xB0\x81\xB1\x00\x00"~ - "\x00\x62\x65\x73\x74\x5F\x6C\x61\x6E\x67\x75\x61\x67\x65\x55\x54\x05\x00\x03\x97"~ - "\xF2\x8A\x5D\x75\x78\x0B\x00\x01\x04\xEB\x03\x00\x00\x04\xEB\x03\x00\x00\x50\x4B"~ - "\x05\x06\x00\x00\x00\x00\x01\x00\x01\x00\x53\x00\x00\x00\xFF\x00\x00\x00\x00\x00"; - - import std.exception : assertThrown; - assertThrown!ZipException(new ZipArchive(cast(void[]) file)); -} - -@system unittest -{ - // https://issues.dlang.org/show_bug.cgi?id=20287 - // check for correct compressed data - auto file = - "\x50\x4b\x03\x04\x0a\x00\x00\x00\x00\x00\x8f\x72\x4a\x4f\x86\xa6"~ - "\x10\x36\x05\x00\x00\x00\x05\x00\x00\x00\x04\x00\x1c\x00\x66\x69"~ - "\x6c\x65\x55\x54\x09\x00\x03\x0d\x22\x9f\x5d\x12\x22\x9f\x5d\x75"~ - "\x78\x0b\x00\x01\x04\xf0\x03\x00\x00\x04\xf0\x03\x00\x00\x68\x65"~ - "\x6c\x6c\x6f\x50\x4b\x01\x02\x1e\x03\x0a\x00\x00\x00\x00\x00\x8f"~ - "\x72\x4a\x4f\x86\xa6\x10\x36\x05\x00\x00\x00\x05\x00\x00\x00\x04"~ - "\x00\x18\x00\x00\x00\x00\x00\x01\x00\x00\x00\xb0\x81\x00\x00\x00"~ - "\x00\x66\x69\x6c\x65\x55\x54\x05\x00\x03\x0d\x22\x9f\x5d\x75\x78"~ - "\x0b\x00\x01\x04\xf0\x03\x00\x00\x04\xf0\x03\x00\x00\x50\x4b\x05"~ - "\x06\x00\x00\x00\x00\x01\x00\x01\x00\x4a\x00\x00\x00\x43\x00\x00"~ - "\x00\x00\x00"; - - auto za = new ZipArchive(cast(void[]) file); - assert(za.directory["file"].compressedData == [104, 101, 108, 108, 111]); -} - -// https://issues.dlang.org/show_bug.cgi?id=20027 -@system unittest -{ - // central file header overlaps end of central directory - auto file = - // lfh - "\x50\x4b\x03\x04\x0a\x00\x00\x00\x00\x00\x8f\x72\x4a\x4f\x86\xa6"~ - "\x10\x36\x05\x00\x00\x00\x05\x00\x00\x00\x04\x00\x1c\x00\x66\x69"~ - "\x6c\x65\x55\x54\x09\x00\x03\x0d\x22\x9f\x5d\x12\x22\x9f\x5d\x75"~ - "\x78\x0b\x00\x01\x04\xf0\x03\x00\x00\x04\xf0\x03\x00\x00\x68\x65"~ - "\x6c\x6c\x6f\x50\x4b\x01\x02\x1e\x03\x0a\x00\x00\x00\x00\x00\x8f"~ - "\x72\x4a\x4f\x86\xa6\x10\x36\x05\x00\x00\x00\x05\x00\x00\x00\x04"~ - "\x00\x18\x00\x04\x00\x00\x00\x01\x00\x00\x00\xb0\x81\x00\x00\x00"~ - "\x00\x66\x69\x6c\x65\x55\x54\x05\x00\x03\x0d\x22\x9f\x5d\x75\x78"~ - "\x0b\x00\x01\x04\xf0\x03\x00\x00\x04\xf0\x03\x00\x00\x50\x4b\x05"~ - "\x06\x00\x00\x00\x00\x01\x00\x01\x00\x4a\x00\x00\x00\x43\x00\x00"~ - "\x00\x00\x00"; - - import std.exception : assertThrown; - assertThrown!ZipException(new ZipArchive(cast(void[]) file)); - - // local file header and file data overlap second local file header and file data - file = - "\x50\x4b\x03\x04\x0a\x00\x00\x00\x00\x00\x8f\x72\x4a\x4f\x86\xa6"~ - "\x10\x36\x05\x00\x00\x00\x05\x00\x00\x00\x04\x00\x1e\x00\x66\x69"~ - "\x6c\x65\x55\x54\x09\x00\x03\x0d\x22\x9f\x5d\x12\x22\x9f\x5d\x75"~ - "\x78\x0b\x00\x01\x04\xf0\x03\x00\x00\x04\xf0\x03\x00\x00\x68\x65"~ - "\x6c\x6c\x6f\x50\x4b\x01\x02\x1e\x03\x0a\x00\x00\x00\x00\x00\x8f"~ - "\x72\x4a\x4f\x86\xa6\x10\x36\x05\x00\x00\x00\x05\x00\x00\x00\x04"~ - "\x00\x18\x00\x04\x00\x00\x00\x01\x00\x00\x00\xb0\x81\x00\x00\x00"~ - "\x00\x66\x69\x6c\x65\x55\x54\x05\x00\x03\x0d\x22\x9f\x5d\x75\x78"~ - "\x0b\x00\x01\x04\xf0\x03\x00\x00\x04\xf0\x03\x00\x00\x50\x4b\x05"~ - "\x06\x00\x00\x00\x00\x01\x00\x01\x00\x4a\x00\x00\x00\x43\x00\x00"~ - "\x00\x00\x00"; - - assertThrown!ZipException(new ZipArchive(cast(void[]) file)); -} - -@system unittest -{ - // https://issues.dlang.org/show_bug.cgi?id=20295 - // zip64 with 0xff bytes in end of central dir record do not work - // minimum (empty zip64) archive should pass - auto file = - "\x50\x4b\x06\x06\x2c\x00\x00\x00\x00\x00\x00\x00\x1e\x03\x2d\x00"~ - "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"~ - "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"~ - "\x00\x00\x00\x00\x00\x00\x00\x00\x50\x4b\x06\x07\x00\x00\x00\x00"~ - "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x50\x4B\x05\x06"~ - "\x00\x00\x00\x00\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff"~ - "\x00\x00"; - - auto za = new ZipArchive(cast(void[]) file); - assert(za.directory.length == 0); -} - -version (HasUnzip) -@system unittest -{ - import std.datetime, std.file, std.format, std.path, std.process, std.stdio; - - if (executeShell("unzip").status != 0) - { - writeln("Can't run unzip, skipping unzip test"); - return; - } - - auto zr = new ZipArchive(); - auto am = new ArchiveMember(); - am.compressionMethod = CompressionMethod.deflate; - am.name = "foo.bar"; - am.time = SysTimeToDosFileTime(Clock.currTime()); - am.expandedData = cast(ubyte[])"We all live in a yellow submarine, a yellow submarine"; - zr.addMember(am); - auto data2 = zr.build(); - - mkdirRecurse(deleteme); - scope(exit) rmdirRecurse(deleteme); - string zipFile = buildPath(deleteme, "foo.zip"); - std.file.write(zipFile, cast(byte[]) data2); - - auto result = executeShell(format("unzip -l %s", zipFile)); - scope(failure) writeln(result.output); - assert(result.status == 0); -} diff --git a/phobos/std/zlib.d b/phobos/std/zlib.d deleted file mode 100644 index beb280f..0000000 --- a/phobos/std/zlib.d +++ /dev/null @@ -1,888 +0,0 @@ -// Written in the D programming language. - -/** - * Compress/decompress data using the $(HTTP www.zlib.net, zlib library). - * - * Examples: - * - * If you have a small buffer you can use $(LREF compress) and - * $(LREF uncompress) directly. - * - * ------- - * import std.zlib; - * - * auto src = - * "the quick brown fox jumps over the lazy dog\r - * the quick brown fox jumps over the lazy dog\r"; - * - * ubyte[] dst; - * ubyte[] result; - * - * dst = compress(src); - * result = cast(ubyte[]) uncompress(dst); - * assert(result == src); - * ------- - * - * When the data to be compressed doesn't fit in one buffer, use - * $(LREF Compress) and $(LREF UnCompress). - * - * ------- - * import std.zlib; - * import std.stdio; - * import std.conv : to; - * import std.algorithm.iteration : map; - * - * UnCompress decmp = new UnCompress; - * foreach (chunk; stdin.byChunk(4096).map!(x => decmp.uncompress(x))) - * { - * chunk.to!string.write; - * } - - * ------- - * - * References: - * $(HTTP en.wikipedia.org/wiki/Zlib, Wikipedia) - * - * Copyright: Copyright The D Language Foundation 2000 - 2011. - * License: $(HTTP www.boost.org/LICENSE_1_0.txt, Boost License 1.0). - * Authors: $(HTTP digitalmars.com, Walter Bright) - * Source: $(PHOBOSSRC std/zlib.d) - */ -/* Copyright The D Language Foundation 2000 - 2011. - * 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.zlib; - -//debug=zlib; // uncomment to turn on debugging printf's - -import etc.c.zlib; - -// Values for 'mode' - -enum -{ - Z_NO_FLUSH = 0, - Z_SYNC_FLUSH = 2, - Z_FULL_FLUSH = 3, - Z_FINISH = 4, -} - -/************************************* - * Errors throw a ZlibException. - */ - -class ZlibException : Exception -{ - private static string getmsg(int errnum) nothrow @nogc pure @safe - { - string msg; - switch (errnum) - { - case Z_STREAM_END: msg = "stream end"; break; - case Z_NEED_DICT: msg = "need dict"; break; - case Z_ERRNO: msg = "errno"; break; - case Z_STREAM_ERROR: msg = "stream error"; break; - case Z_DATA_ERROR: msg = "data error"; break; - case Z_MEM_ERROR: msg = "mem error"; break; - case Z_BUF_ERROR: msg = "buf error"; break; - case Z_VERSION_ERROR: msg = "version error"; break; - default: msg = "unknown error"; break; - } - return msg; - } - - this(int errnum) - { - super(getmsg(errnum)); - } -} - -/** - * $(P Compute the Adler-32 checksum of a buffer's worth of data.) - * - * Params: - * adler = the starting checksum for the computation. Use 1 - * for a new checksum. Use the output of this function - * for a cumulative checksum. - * buf = buffer containing input data - * - * Returns: - * A `uint` checksum for the provided input data and starting checksum - * - * See_Also: - * $(LINK http://en.wikipedia.org/wiki/Adler-32) - */ - -uint adler32(uint adler, const(void)[] buf) -{ - import std.range : chunks; - foreach (chunk; (cast(ubyte[]) buf).chunks(0xFFFF0000)) - { - adler = etc.c.zlib.adler32(adler, chunk.ptr, cast(uint) chunk.length); - } - return adler; -} - -/// -@system unittest -{ - static ubyte[] data = [1,2,3,4,5,6,7,8,9,10]; - - uint adler = adler32(0u, data); - assert(adler == 0xdc0037); -} - -@system unittest -{ - static string data = "test"; - - uint adler = adler32(1, data); - assert(adler == 0x045d01c1); -} - -/** - * $(P Compute the CRC32 checksum of a buffer's worth of data.) - * - * Params: - * crc = the starting checksum for the computation. Use 0 - * for a new checksum. Use the output of this function - * for a cumulative checksum. - * buf = buffer containing input data - * - * Returns: - * A `uint` checksum for the provided input data and starting checksum - * - * See_Also: - * $(LINK http://en.wikipedia.org/wiki/Cyclic_redundancy_check) - */ - -uint crc32(uint crc, const(void)[] buf) -{ - import std.range : chunks; - foreach (chunk; (cast(ubyte[]) buf).chunks(0xFFFF0000)) - { - crc = etc.c.zlib.crc32(crc, chunk.ptr, cast(uint) chunk.length); - } - return crc; -} - -@system unittest -{ - static ubyte[] data = [1,2,3,4,5,6,7,8,9,10]; - - uint crc; - - debug(zlib) printf("D.zlib.crc32.unittest\n"); - crc = crc32(0u, cast(void[]) data); - debug(zlib) printf("crc = %x\n", crc); - assert(crc == 0x2520577b); -} - -/** - * $(P Compress data) - * - * Params: - * srcbuf = buffer containing the data to compress - * level = compression level. Legal values are -1 .. 9, with -1 indicating - * the default level (6), 0 indicating no compression, 1 being the - * least compression and 9 being the most. - * - * Returns: - * the compressed data - */ - -ubyte[] compress(const(void)[] srcbuf, int level) -in -{ - assert(-1 <= level && level <= 9, "Compression level needs to be within [-1, 9]."); -} -do -{ - import core.memory : GC; - import std.array : uninitializedArray; - auto destlen = srcbuf.length + ((srcbuf.length + 1023) / 1024) + 12; - auto destbuf = uninitializedArray!(ubyte[])(destlen); - auto err = etc.c.zlib.compress2(destbuf.ptr, &destlen, cast(ubyte *) srcbuf.ptr, srcbuf.length, level); - if (err) - { - GC.free(destbuf.ptr); - throw new ZlibException(err); - } - - destbuf.length = destlen; - return destbuf; -} - -/********************************************* - * ditto - */ - -ubyte[] compress(const(void)[] srcbuf) -{ - return compress(srcbuf, Z_DEFAULT_COMPRESSION); -} - -/********************************************* - * Decompresses the data in srcbuf[]. - * Params: - * srcbuf = buffer containing the compressed data. - * destlen = size of the uncompressed data. - * It need not be accurate, but the decompression will be faster - * if the exact size is supplied. - * winbits = the base two logarithm of the maximum window size. - * Returns: the decompressed data. - */ - -void[] uncompress(const(void)[] srcbuf, size_t destlen = 0u, int winbits = 15) -{ - import std.conv : to; - int err; - ubyte[] destbuf; - - if (!destlen) - destlen = srcbuf.length * 2 + 1; - - etc.c.zlib.z_stream zs; - zs.next_in = cast(typeof(zs.next_in)) srcbuf.ptr; - zs.avail_in = to!uint(srcbuf.length); - err = etc.c.zlib.inflateInit2(&zs, winbits); - if (err) - { - throw new ZlibException(err); - } - - size_t olddestlen = 0u; - - loop: - while (true) - { - destbuf.length = destlen; - zs.next_out = cast(typeof(zs.next_out)) &destbuf[olddestlen]; - zs.avail_out = to!uint(destlen - olddestlen); - olddestlen = destlen; - - err = etc.c.zlib.inflate(&zs, Z_NO_FLUSH); - switch (err) - { - case Z_OK: - destlen = destbuf.length * 2; - continue loop; - - case Z_STREAM_END: - destbuf.length = zs.total_out; - err = etc.c.zlib.inflateEnd(&zs); - if (err != Z_OK) - throw new ZlibException(err); - return destbuf; - - default: - etc.c.zlib.inflateEnd(&zs); - throw new ZlibException(err); - } - } - assert(0, "Unreachable code"); -} - -@system unittest -{ - auto src = -"the quick brown fox jumps over the lazy dog\r -the quick brown fox jumps over the lazy dog\r -"; - ubyte[] dst; - ubyte[] result; - - //arrayPrint(src); - dst = compress(src); - //arrayPrint(dst); - result = cast(ubyte[]) uncompress(dst); - //arrayPrint(result); - assert(result == src); -} - -@system unittest -{ - ubyte[] src = new ubyte[1000000]; - ubyte[] dst; - ubyte[] result; - - src[] = 0x80; - dst = compress(src); - assert(dst.length*2 + 1 < src.length); - result = cast(ubyte[]) uncompress(dst); - assert(result == src); -} - -/+ -void arrayPrint(ubyte[] array) -{ - //printf("array %p,%d\n", cast(void*) array, array.length); - for (size_t i = 0; i < array.length; i++) - { - printf("%02x ", array[i]); - if (((i + 1) & 15) == 0) - printf("\n"); - } - printf("\n\n"); -} -+/ - -/// the header format the compressed stream is wrapped in -enum HeaderFormat { - deflate, /// a standard zlib header - gzip, /// a gzip file format header - determineFromData /// used when decompressing. Try to automatically detect the stream format by looking at the data -} - -/********************************************* - * Used when the data to be compressed is not all in one buffer. - */ - -class Compress -{ - import std.conv : to; - - private: - z_stream zs; - int level = Z_DEFAULT_COMPRESSION; - int inited; - immutable bool gzip; - - void error(int err) - { - if (inited) - { deflateEnd(&zs); - inited = 0; - } - throw new ZlibException(err); - } - - public: - - /** - * Constructor. - * - * Params: - * level = compression level. Legal values are 1 .. 9, with 1 being the least - * compression and 9 being the most. The default value is 6. - * header = sets the compression type to one of the options available - * in $(LREF HeaderFormat). Defaults to HeaderFormat.deflate. - * - * See_Also: - * $(LREF compress), $(LREF HeaderFormat) - */ - this(int level, HeaderFormat header = HeaderFormat.deflate) - in - { - assert(1 <= level && level <= 9, "Legal compression level are in [1, 9]."); - } - do - { - this.level = level; - this.gzip = header == HeaderFormat.gzip; - } - - /// ditto - this(HeaderFormat header = HeaderFormat.deflate) - { - this.gzip = header == HeaderFormat.gzip; - } - - ~this() - { int err; - - if (inited) - { - inited = 0; - deflateEnd(&zs); - } - } - - /** - * Compress the data in buf and return the compressed data. - * Params: - * buf = data to compress - * - * Returns: - * the compressed data. The buffers returned from successive calls to this should be concatenated together. - * - */ - const(void)[] compress(const(void)[] buf) - { - import core.memory : GC; - import std.array : uninitializedArray; - int err; - ubyte[] destbuf; - - if (buf.length == 0) - return null; - - if (!inited) - { - err = deflateInit2(&zs, level, Z_DEFLATED, 15 + (gzip ? 16 : 0), 8, Z_DEFAULT_STRATEGY); - if (err) - error(err); - inited = 1; - } - - destbuf = uninitializedArray!(ubyte[])(zs.avail_in + buf.length); - zs.next_out = destbuf.ptr; - zs.avail_out = to!uint(destbuf.length); - - if (zs.avail_in) - buf = zs.next_in[0 .. zs.avail_in] ~ cast(ubyte[]) buf; - - zs.next_in = cast(typeof(zs.next_in)) buf.ptr; - zs.avail_in = to!uint(buf.length); - - err = deflate(&zs, Z_NO_FLUSH); - if (err != Z_STREAM_END && err != Z_OK) - { - GC.free(destbuf.ptr); - error(err); - } - destbuf.length = destbuf.length - zs.avail_out; - return destbuf; - } - - /*** - * Compress and return any remaining data. - * The returned data should be appended to that returned by compress(). - * Params: - * mode = one of the following: - * $(DL - $(DT Z_SYNC_FLUSH ) - $(DD Syncs up flushing to the next byte boundary. - Used when more data is to be compressed later on.) - $(DT Z_FULL_FLUSH ) - $(DD Syncs up flushing to the next byte boundary. - Used when more data is to be compressed later on, - and the decompressor needs to be restartable at this - point.) - $(DT Z_FINISH) - $(DD (default) Used when finished compressing the data. ) - ) - */ - void[] flush(int mode = Z_FINISH) - in - { - assert(mode == Z_FINISH || mode == Z_SYNC_FLUSH || mode == Z_FULL_FLUSH, - "Mode must be either Z_FINISH, Z_SYNC_FLUSH or Z_FULL_FLUSH."); - } - do - { - import core.memory : GC; - ubyte[] destbuf; - ubyte[512] tmpbuf = void; - int err; - - if (!inited) - return null; - - /* may be zs.avail_out+<some constant> - * zs.avail_out is set nonzero by deflate in previous compress() - */ - //tmpbuf = new void[zs.avail_out]; - zs.next_out = tmpbuf.ptr; - zs.avail_out = tmpbuf.length; - - while ( (err = deflate(&zs, mode)) != Z_STREAM_END) - { - if (err == Z_OK) - { - if (zs.avail_out != 0 && mode != Z_FINISH) - break; - else if (zs.avail_out == 0) - { - destbuf ~= tmpbuf; - zs.next_out = tmpbuf.ptr; - zs.avail_out = tmpbuf.length; - continue; - } - err = Z_BUF_ERROR; - } - GC.free(destbuf.ptr); - error(err); - } - destbuf ~= tmpbuf[0 .. (tmpbuf.length - zs.avail_out)]; - - if (mode == Z_FINISH) - { - err = deflateEnd(&zs); - inited = 0; - if (err) - error(err); - } - return destbuf; - } -} - -/****** - * Used when the data to be decompressed is not all in one buffer. - */ - -class UnCompress -{ - import std.conv : to; - - private: - z_stream zs; - int inited; - int done; - bool inputEnded; - size_t destbufsize; - - HeaderFormat format; - - void error(int err) - { - if (inited) - { inflateEnd(&zs); - inited = 0; - } - throw new ZlibException(err); - } - - public: - - /** - * Construct. destbufsize is the same as for D.zlib.uncompress(). - */ - this(uint destbufsize) - { - this.destbufsize = destbufsize; - } - - /** ditto */ - this(HeaderFormat format = HeaderFormat.determineFromData) - { - this.format = format; - } - - ~this() - { int err; - - if (inited) - { - inited = 0; - inflateEnd(&zs); - } - done = 1; - } - - /** - * Decompress the data in buf and return the decompressed data. - * The buffers returned from successive calls to this should be concatenated - * together. - */ - const(void)[] uncompress(const(void)[] buf) - in - { - assert(!done, "Buffer has been flushed."); - } - do - { - if (inputEnded || !buf.length) - return null; - - import core.memory : GC; - import std.array : uninitializedArray; - int err; - - if (!inited) - { - int windowBits = 15; - if (format == HeaderFormat.gzip) - windowBits += 16; - else if (format == HeaderFormat.determineFromData) - windowBits += 32; - - err = inflateInit2(&zs, windowBits); - if (err) - error(err); - inited = 1; - } - - if (!destbufsize) - destbufsize = to!uint(buf.length) * 2; - auto destbuf = uninitializedArray!(ubyte[])(destbufsize); - size_t destFill; - - zs.next_in = cast(ubyte*) buf.ptr; - zs.avail_in = to!uint(buf.length); - - while (true) - { - auto oldAvailIn = zs.avail_in; - - zs.next_out = destbuf[destFill .. $].ptr; - zs.avail_out = to!uint(destbuf.length - destFill); - - err = inflate(&zs, Z_NO_FLUSH); - if (err == Z_STREAM_END) - { - inputEnded = true; - break; - } - else if (err != Z_OK) - { - GC.free(destbuf.ptr); - error(err); - } - else if (!zs.avail_in) - break; - - /* - According to the zlib manual inflate() stops when either there's - no more data to uncompress or the output buffer is full - So at this point, the output buffer is too full - */ - - destFill = destbuf.length; - - if (destbuf.capacity) - { - if (destbuf.length < destbuf.capacity) - destbuf.length = destbuf.capacity; - else - { - auto newLength = GC.extend(destbuf.ptr, destbufsize, destbufsize); - - if (newLength && destbuf.length < destbuf.capacity) - destbuf.length = destbuf.capacity; - else - destbuf.length += destbufsize; - } - } - else - destbuf.length += destbufsize; - } - - destbuf.length = destbuf.length - zs.avail_out; - return destbuf; - } - - // Test for https://issues.dlang.org/show_bug.cgi?id=3191 and - // https://issues.dlang.org/show_bug.cgi?id=9505 - @system unittest - { - import std.algorithm.comparison; - import std.array; - import std.file; - import std.zlib; - - // Data that can be easily compressed - ubyte[1024] originalData; - - // This should yield a compression ratio of at least 1/2 - auto compressedData = compress(originalData, 9); - assert(compressedData.length < originalData.length / 2, - "The compression ratio is too low to accurately test this situation"); - - auto chunkSize = compressedData.length / 4; - assert(chunkSize < compressedData.length, - "The length of the compressed data is too small to accurately test this situation"); - - auto decompressor = new UnCompress(); - ubyte[originalData.length] uncompressedData; - ubyte[] reusedBuf; - int progress; - - reusedBuf.length = chunkSize; - - for (int i = 0; i < compressedData.length; i += chunkSize) - { - auto len = min(chunkSize, compressedData.length - i); - // simulate reading from a stream in small chunks - reusedBuf[0 .. len] = compressedData[i .. i + len]; - - // decompress using same input buffer - auto chunk = decompressor.uncompress(reusedBuf); - assert(progress + chunk.length <= originalData.length, - "The uncompressed result is bigger than the original data"); - - uncompressedData[progress .. progress + chunk.length] = cast(const ubyte[]) chunk[]; - progress += chunk.length; - } - - auto chunk = decompressor.flush(); - assert(progress + chunk.length <= originalData.length, - "The uncompressed result is bigger than the original data"); - - uncompressedData[progress .. progress + chunk.length] = cast(const ubyte[]) chunk[]; - progress += chunk.length; - - assert(progress == originalData.length, - "The uncompressed and the original data sizes differ"); - assert(originalData[] == uncompressedData[], - "The uncompressed and the original data differ"); - } - - @system unittest - { - ubyte[1024] invalidData; - auto decompressor = new UnCompress(); - - try - { - auto uncompressedData = decompressor.uncompress(invalidData); - } - catch (ZlibException e) - { - assert(e.msg == "data error"); - return; - } - - assert(false, "Corrupted data didn't result in an error"); - } - - @system unittest - { - ubyte[2014] originalData = void; - auto compressedData = compress(originalData, 9); - - auto decompressor = new UnCompress(); - auto uncompressedData = decompressor.uncompress(compressedData ~ cast(ubyte[]) "whatever"); - - assert(originalData.length == uncompressedData.length, - "The uncompressed and the original data sizes differ"); - assert(originalData[] == uncompressedData[], - "The uncompressed and the original data differ"); - assert(!decompressor.uncompress("whatever").length, - "Compression continued after the end"); - } - - /** - * Decompress and return any remaining data. - * The returned data should be appended to that returned by uncompress(). - * The UnCompress object cannot be used further. - */ - void[] flush() - in - { - assert(!done, "Buffer has been flushed before."); - } - out - { - assert(done, "Flushing failed."); - } - do - { - done = 1; - return null; - } - - /// Returns true if all input data has been decompressed and no further data - /// can be decompressed (inflate() returned Z_STREAM_END) - @property bool empty() const - { - return inputEnded; - } - - /// - @system unittest - { - // some random data - ubyte[1024] originalData = void; - - // append garbage data (or don't, this works in both cases) - auto compressedData = cast(ubyte[]) compress(originalData) ~ cast(ubyte[]) "whatever"; - - auto decompressor = new UnCompress(); - auto uncompressedData = decompressor.uncompress(compressedData); - - assert(uncompressedData[] == originalData[], - "The uncompressed and the original data differ"); - assert(decompressor.empty, "The UnCompressor reports not being done"); - } -} - -/* ========================== unittest ========================= */ - -import std.random; -import std.stdio; - -@system unittest // by Dave -{ - debug(zlib) writeln("std.zlib.unittest"); - - bool CompressThenUncompress (void[] src) - { - ubyte[] dst = std.zlib.compress(src); - double ratio = (dst.length / cast(double) src.length); - debug(zlib) writef("src.length: %1$d, dst: %2$d, Ratio = %3$f", src.length, dst.length, ratio); - ubyte[] uncompressedBuf; - uncompressedBuf = cast(ubyte[]) std.zlib.uncompress(dst); - assert(src.length == uncompressedBuf.length); - assert(src == uncompressedBuf); - - return true; - } - - - // smallish buffers - for (int idx = 0; idx < 25; idx++) - { - char[] buf = new char[uniform(0, 100)]; - - // Alternate between more & less compressible - foreach (ref char c; buf) - c = cast(char) (' ' + (uniform(0, idx % 2 ? 91 : 2))); - - if (CompressThenUncompress(buf)) - { - debug(zlib) writeln("; Success."); - } - else - { - return; - } - } - - // larger buffers - for (int idx = 0; idx < 25; idx++) - { - char[] buf = new char[uniform(0, 1000/*0000*/)]; - - // Alternate between more & less compressible - foreach (ref char c; buf) - c = cast(char) (' ' + (uniform(0, idx % 2 ? 91 : 10))); - - if (CompressThenUncompress(buf)) - { - debug(zlib) writefln("; Success."); - } - else - { - return; - } - } - - debug(zlib) writefln("PASSED std.zlib.unittest"); -} - - -@system unittest // by Artem Rebrov -{ - Compress cmp = new Compress; - UnCompress decmp = new UnCompress; - - const(void)[] input; - input = "tesatdffadf"; - - const(void)[] buf = cmp.compress(input); - buf ~= cmp.flush(); - const(void)[] output = decmp.uncompress(buf); - - //writefln("input = '%s'", cast(char[]) input); - //writefln("output = '%s'", cast(char[]) output); - assert( output[] == input[] ); -} - -// https://issues.dlang.org/show_bug.cgi?id=15457 -@system unittest -{ - static assert(__traits(compiles, etc.c.zlib.gzclose(null))); -} diff --git a/phobos/test/.gitignore b/phobos/test/.gitignore deleted file mode 100644 index 5e4860d..0000000 --- a/phobos/test/.gitignore +++ /dev/null @@ -1,2 +0,0 @@ -dub_stdx_allocator -dub_stdx_checkedint diff --git a/phobos/test/betterc_module_tests.d b/phobos/test/betterc_module_tests.d deleted file mode 100644 index 8783654..0000000 --- a/phobos/test/betterc_module_tests.d +++ /dev/null @@ -1,30 +0,0 @@ -static immutable bettercModules = [ - "std.sumtype" -]; - -template from(string modname) -{ - mixin("import from = ", modname, ";"); -} - -void testModule(string modname)() -{ - import core.stdc.stdio : printf; - - printf("Running BetterC tests for %.*s\n", cast(int) modname.length, modname.ptr); - - static foreach (test; __traits(getUnitTests, from!modname)) - { - test(); - } -} - -extern(C) int main() -{ - static foreach (modname; bettercModules) - { - testModule!modname; - } - - return 0; -} diff --git a/phobos/test/dub_stdx_allocator.d b/phobos/test/dub_stdx_allocator.d deleted file mode 100755 index bcd24b8..0000000 --- a/phobos/test/dub_stdx_allocator.d +++ /dev/null @@ -1,26 +0,0 @@ -#!/usr/bin/env dub -/++dub.sdl: -dependency "phobos:allocator" path=".." -+/ - -void main(string[] args) -{ - import stdx.allocator.mallocator : Mallocator; // From latest Phobos - import std.stdio; // current phobos - auto buf = Mallocator.instance.allocate(10); - writeln("allocate: ", buf); - scope(exit) Mallocator.instance.deallocate(buf); -} - -void test() -{ - import stdx.allocator : make; - import stdx.allocator.mallocator : Mallocator; - alias alloc = Mallocator.instance; - struct Node - { - int x; - this(int, int){} - } - auto newNode = alloc.make!Node(2, 3); -} diff --git a/phobos/test/dub_stdx_checkedint.d b/phobos/test/dub_stdx_checkedint.d deleted file mode 100755 index d0a920d..0000000 --- a/phobos/test/dub_stdx_checkedint.d +++ /dev/null @@ -1,11 +0,0 @@ -#!/usr/bin/env dub -/++dub.sdl: -dependency "phobos:checkedint" path=".." -+/ - -void main(string[] args) -{ - import stdx.checkedint; // From latest Phobos - import std.stdio; // DMD's Phobos - writeln("checkedint: ", 2.checked + 3); -} diff --git a/phobos/tools/unicode_table_generator.d b/phobos/tools/unicode_table_generator.d deleted file mode 100644 index 0bbe80f..0000000 --- a/phobos/tools/unicode_table_generator.d +++ /dev/null @@ -1,1485 +0,0 @@ -/** -This is a tool to automatically generate source code for unicode data structures. - -If not present, the script will automatically try to download the files from: https://www.unicode.org/Public - -Make sure the current working directory is the /tools folder. - -To update `std.internal.unicode*.d` files, run: -``` -rdmd -m32 unicode_table_generator.d -rdmd -m64 unicode_table_generator.d --min -``` - -The -m32 run will replace the files, while the -m64 run with --min will append 64-bit specific parts. -The 32-bit compilation of the generator is needed because it depends on 32-bit data structures defined in `std.uni`. -To make -m32 work on linux, you may need to grab a 32-bit `libphobos2.a` from `dmd2/linux/lib32` and pass it as argument: - -``` -rdmd -m32 -Llibphobos2.a -defaultlib= unicode_table_generator.d -``` - -Pull Requests to untangle this complex bootstrap process are welcome! :) - -TODO: Support emitting of Turkic casefolding mappings - -Authors: Dmitry Olshansky - -License: Boost -*/ -module std.unicode_table_generator; -// this shouldn't be in std package, but stuff is package'd as that in std.uni. - -/// Directory in which unicode files are downloaded -enum unicodeDir = "ucd-15-1-0/"; - -/// Url from which unicode files are downloaded -enum unicodeBaseUrl = "https://www.unicode.org/Public/15.1.0/ucd/"; - -/// Where to put generated files -enum outputDir = "../std/internal/"; - -import std.uni, std.stdio, std.traits, std.typetuple, - std.exception, std.format, std.algorithm, std.typecons, - std.regex, std.range, std.conv, std.getopt, std.path; - -import std.file : exists; -static import std.ascii, std.string; - -//common binary property sets and their aliases -struct PropertyTable -{ - CodepointSet[string] table; - string[string] aliases; -} - -PropertyTable general; -PropertyTable blocks; -PropertyTable scripts; -PropertyTable hangul; -PropertyTable graphemeBreaks; -PropertyTable emojiData; - -//quick NO/MAYBE charaсter sets -CodepointSet[string] normalization; - -//axuilary sets for case mapping -CodepointSet lowerCaseSet, upperCaseSet; -//CodepointSet titleCaseSet; //no sensible version found for isTitlecase - -// sets for toLower/toUpper/toTitle -uint[] toLowerTab; -ushort toLowerTabSimpleLen; //start of long mappings -ushort[dchar] toLowerSimpleIndex, toLowerIndex; -uint[] toUpperTab; -ushort toUpperTabSimpleLen; //ditto for Upper -ushort[dchar] toUpperSimpleIndex, toUpperIndex; -uint[] toTitleTab; -ushort toTitleTabSimpleLen; //ditto for Title -ushort[dchar] toTitleSimpleIndex, toTitleIndex; - -mixin(mixedCCEntry); - -//case folding mapping -SimpleCaseEntry[] simpleTable; -FullCaseEntry[] fullTable; - -///canonical combining class -CodepointSet[256] combiningClass; -//same but packaged per dchar -ubyte[dchar] combiningMapping; - -//unrolled decompositions -dstring[dchar] canonDecomp; -dstring[dchar] compatDecomp; - -//canonical composition tables -dchar[] canonicalyComposableLeft; -dchar[] canonicalyComposableRight; - - -//canonical composition exclusions -CodepointSet compExclusions; - -//property names to discard -string[] blacklist = []; - -struct FullCaseEntry -{ - dchar[3] seq = 0; - ubyte n; /// number in batch - ubyte size; /// size - size of batch - ubyte entry_len; - - auto value() const @safe pure nothrow @nogc return - { - return seq[0 .. entry_len]; - } -} - -/// 8 byte easy SimpleCaseEntry, will be compressed to SCE which bit packs values to 4 bytes -struct SimpleCaseEntry -{ - uint ch; - ubyte n; // number in bucket - ubyte size; - bool isLower; - bool isUpper; -} - -enum mixedCCEntry = ` -/// Simple Case Entry, wrapper around uint to extract bit fields from simpleCaseTable() -struct SCE -{ - uint x; - - nothrow @nogc pure @safe: - - this(uint x) - { - this.x = x; - } - - this(uint ch, ubyte n, ubyte size) - { - this.x = ch | n << 20 | size << 24; - } - - int ch() const { return this.x & 0x1FFFF; } - int n() const { return (this.x >> 20) & 0xF; } - int size() const { return this.x >> 24; } -} - -/// Bit backed FullCaseEntry -struct FCE -{ - ulong x; // bit field sizes: 18, 12, 12, 4, 4, 4 - -nothrow @nogc pure @safe: - - this(ulong x) - { - this.x = x; - } - - this(dchar[3] seq, ubyte n, ubyte size, ubyte entry_len) - { - this.x = ulong(seq[0]) << 36 | ulong(seq[1]) << 24 | seq[2] << 12 | n << 8 | size << 4 | entry_len << 0; - } - - dchar[3] seq() const { return [(x >> 36) & 0x1FFFF, (x >> 24) & 0xFFF, (x >> 12) & 0xFFF]; } - ubyte n() const { return (x >> 8) & 0xF; } - ubyte size() const { return (x >> 4) & 0xF; } - ubyte entry_len() const { return (x >> 0) & 0xF; } -} - -struct UnicodeProperty -{ - string name; - ubyte[] compressed; -} - -struct TrieEntry(T...) -{ - immutable(size_t)[] offsets; - immutable(size_t)[] sizes; - immutable(size_t)[] data; -} - -`; - -auto fullCaseEntry(dstring value, ubyte num, ubyte batch_size) -{ - dchar[3] val = 0; - val[0 .. value.length] = value[]; - return FullCaseEntry(val, num, batch_size, cast(ubyte) value.length); -} - -enum { - caseFoldingSrc = "CaseFolding.txt", - blocksSrc = "Blocks.txt", - propListSrc = "PropList.txt", - graphemeSrc = "auxiliary/GraphemeBreakProperty.txt", - emojiDataSrc = "emoji/emoji-data.txt", - propertyValueAliases = "PropertyValueAliases.txt", - corePropSrc = "DerivedCoreProperties.txt", - normalizationPropSrc = "DerivedNormalizationProps.txt", - scriptsSrc = "Scripts.txt", - hangulSyllableSrc = "HangulSyllableType.txt", - unicodeDataSrc = "UnicodeData.txt", - compositionExclusionsSrc = "CompositionExclusions.txt", - specialCasingSrc = "SpecialCasing.txt" -} - -void ensureFilesAreDownloaded() -{ - import std.file : write, mkdirRecurse; - import std.net.curl : download; - import std.path : dirName, buildPath; - - string[] files = [ - caseFoldingSrc, - blocksSrc, - propListSrc, - graphemeSrc, - emojiDataSrc, - propertyValueAliases, - corePropSrc, - normalizationPropSrc, - scriptsSrc, - hangulSyllableSrc, - unicodeDataSrc, - compositionExclusionsSrc, - specialCasingSrc - ]; - - mkdirRecurse(unicodeDir); - foreach (string file; files) - { - string dest = unicodeDir.buildPath(file); - if (exists(dest)) - continue; - if (file.canFind("/")) - { - mkdirRecurse(dest.dirName); - } - writeln("downloading ", unicodeBaseUrl ~ file); - download(unicodeBaseUrl ~ file, dest); - } -} - -enum HeaderComment = `//Written in the D programming language -/** - * License: $(HTTP boost.org/LICENSE_1_0.txt, Boost License 1.0). - * - * Authors: Dmitry Olshansky - * - */ -// !!! DO NOT EDIT !!! -// !!! Did you even read the comment? !!! -// This module is automatically generated from Unicode Character Database files -// https://github.com/dlang/phobos/blob/master/tools/unicode_table_generator.d -//dfmt off`; - -auto toPairs(K, V)(V[K] aa) -{ - return aa.values.zip(aa.keys).array; -} - -void main(string[] argv) -{ - string mode = "w"; - - bool minimal = false; - getopt(argv, "min", &minimal); - if (minimal) - mode = "a"; - - ensureFilesAreDownloaded(); - - auto baseSink = File(outputDir.buildPath("unicode_tables.d"), mode); - auto compSink = File(outputDir.buildPath("unicode_comp.d"), mode); - auto decompSink = File(outputDir.buildPath("unicode_decomp.d"), mode); - auto normSink = File(outputDir.buildPath("unicode_norm.d"), mode); - auto graphSink = File(outputDir.buildPath("unicode_grapheme.d"), mode); - if (!minimal) - { - baseSink.writeln(HeaderComment); - baseSink.writeln("module std.internal.unicode_tables;"); - baseSink.writeln("\n@safe pure nothrow @nogc package(std):\n"); - - compSink.writeln("module std.internal.unicode_comp;"); - compSink.writeln("import std.internal.unicode_tables;"); - compSink.writeln("\n@safe pure nothrow @nogc package(std):\n"); - - decompSink.writeln(HeaderComment); - decompSink.writeln("module std.internal.unicode_decomp;"); - decompSink.writeln("import std.internal.unicode_tables;"); - decompSink.writeln("\n@safe pure nothrow @nogc package(std):\n"); - - normSink.writeln(HeaderComment); - normSink.writeln("module std.internal.unicode_norm;"); - normSink.writeln("import std.internal.unicode_tables;"); - normSink.writeln("\npackage(std):\n"); - - graphSink.writeln(HeaderComment); - graphSink.writeln("module std.internal.unicode_grapheme;"); - graphSink.writeln("import std.internal.unicode_tables;"); - graphSink.writeln("\npackage(std):\n"); - } - - loadBlocks(unicodeDir.buildPath(blocksSrc), blocks); - loadProperties(unicodeDir.buildPath(propListSrc), general); - loadProperties(unicodeDir.buildPath(corePropSrc), general); - loadProperties(unicodeDir.buildPath(scriptsSrc), scripts); - loadProperties(unicodeDir.buildPath(hangulSyllableSrc), hangul); - loadProperties(unicodeDir.buildPath(graphemeSrc), graphemeBreaks); - loadProperties(unicodeDir.buildPath(emojiDataSrc), emojiData); - loadPropertyAliases(unicodeDir.buildPath(propertyValueAliases)); - - loadUnicodeData(unicodeDir.buildPath(unicodeDataSrc)); - loadSpecialCasing(unicodeDir.buildPath(specialCasingSrc)); - loadExclusions(unicodeDir.buildPath(compositionExclusionsSrc)); - loadCaseFolding(unicodeDir.buildPath(caseFoldingSrc)); - loadNormalization(unicodeDir.buildPath(normalizationPropSrc)); - - static void writeTableOfSets(File sink, string prefix, PropertyTable tab) - { - sink.writeln(); - writeAliasTable(sink, prefix, tab); - } - - if (!minimal) - { - writeCaseFolding(baseSink); - writeTableOfSets(baseSink, "uniProps", general); - writeTableOfSets(baseSink, "blocks", blocks); - writeTableOfSets(baseSink, "scripts", scripts); - writeTableOfSets(baseSink, "hangul", hangul); - writeFunctions(baseSink); - } - - static void trieProlog(File file) - { - file.writefln("\nstatic if (size_t.sizeof == %d)\n{", size_t.sizeof); - } - - static void trieEpilog(File file) - { - file.writeln(" -}\n"); - } - - trieProlog(baseSink); - trieProlog(compSink); - trieProlog(decompSink); - trieProlog(graphSink); - trieProlog(normSink); - - writeTries(baseSink); - writeNormalizationTries(normSink); - writeGraphemeTries(graphSink); - writeCaseCoversion(baseSink); - writeCombining(compSink); - writeDecomposition(decompSink); - writeCompositionTable(compSink); - - trieEpilog(decompSink); - trieEpilog(compSink); - trieEpilog(baseSink); - trieEpilog(graphSink); - trieEpilog(normSink); -} - -void scanUniData(alias Fn)(string name, Regex!char r) -{ - File f = File(name); - foreach (line; f.byLine) - { - auto m = match(line, r); - if (!m.empty) - Fn(m); - } -} - -void loadCaseFolding(string f) -{ - dchar[dchar] simple; - dstring[dchar] full; - - auto r = regex("([^;]*); ([CFST]);\\s*([^;]*);"); - scanUniData!((m){ - auto s1 = m.captures[1]; - auto code = m.captures[2].front; - auto s2 = m.captures[3]; - auto left = parse!int(s1, 16); - if (code == 'C') - { - auto right = parse!int(s2, 16); - simple[left] = right; - full[left] = [right]; - } - else if (code == 'S') - { - auto right = parse!int(s2, 16); - simple[left] = right; - } - else if (code == 'F') - { - dstring right; - foreach (x; match(s2, regex("[0-9A-Fa-f]+", "g"))) - { - right ~= to!int(x[0], 16); - } - full[left] = right.idup; - } - else if (code == 'T') - { - // TODO: ignore Turkic languages for now. - } - })(f, r); - - //make some useful sets by hand - - lowerCaseSet = general.table["Lowercase"]; - upperCaseSet = general.table["Uppercase"]; - //titleCaseSet = general.table["Lt"]; - - foreach (ch; simple.keys()) - { - dchar[8] entry; - int size=0; - entry[size++] = ch; - dchar x = simple[ch]; - entry[size++] = x; - //simple is many:1 mapping - foreach (key, v; simple) - { - if (v == x && !canFind(entry[], key)) - { - entry[size++] = key; - } - } - sort(entry[0 .. size]); - foreach (i, value; entry[0 .. size]) - { - simpleTable ~= SimpleCaseEntry( - value, - cast(ubyte) i, - cast(ubyte) size, - cast(bool) (value in lowerCaseSet), - cast(bool) (value in upperCaseSet) - ); - } - } - - foreach (ch; full.keys()) - { - dstring[8] entry; - int size=0; - entry[size++] = [ch]; - auto x = full[ch]; - entry[size++] = x; - - //full is many:many mapping - //sort single-char versions, and let them come first - foreach (key, v; full) - { - if (v == x && !canFind(entry[], [key])) - { - entry[size++] = [key]; - } - - } - auto right = partition!(a => a.length == 1)(entry[0 .. size]); - sort(entry[0 .. size - right.length]); - foreach (i, value; entry[0 .. size]) - { - fullTable ~= fullCaseEntry(value, cast(ubyte) i, cast(ubyte) size); - } - } -} - -void loadBlocks(string f, ref PropertyTable target) -{ - auto r = regex(`^([0-9A-F]+)\.\.([0-9A-F]+);\s*(.*)\s*$`); - scanUniData!((m){ - auto s1 = m.captures[1]; - auto s2 = m.captures[2]; - auto a1 = parse!uint(s1, 16); - auto a2 = parse!uint(s2, 16); - //@@@BUG 6178 memory corruption with - //target[to!string(m.captures[3])] = CodepointSet(a1, a2+1); - auto set = CodepointSet(a1, a2+1); - target.table[to!string(m.captures[3])] = set; - })(f, r); -} - -void loadProperties(string inp, ref PropertyTable target) -{ - auto acceptProp = (string name) => countUntil(blacklist, name) < 0 && !name.startsWith("Changes"); - auto r = regex(`^(?:(?:([0-9A-F]+)\.\.([0-9A-F]+)|([0-9A-F]+))\s*;\s*([a-zA-Z_0-9]*)\s*#|# [a-zA-Z_0-9]+=([a-zA-Z_0-9]+))`); - string aliasStr; - auto set = CodepointSet.init; //workaround @@@BUG 6178 - scanUniData!((m){ - auto name = to!string(m.captures[4]); - if (!acceptProp(name)) - return; - if (!m.captures[5].empty) - aliasStr = to!string(m.captures[5]); - else if (!m.captures[1].empty) - { - auto sa = m.captures[1]; - auto sb = m.captures[2]; - uint a = parse!uint(sa, 16); - uint b = parse!uint(sb, 16); - if (name !in target.table) - { - target.table[name] = set; - } - auto p = name in target.table; - p.add(a,b+1); // unicode lists [a, b] we need [a,b) - if (!aliasStr.empty) - { - target.aliases[name] = aliasStr; - aliasStr = ""; - } - } - else if (!m.captures[3].empty) - { - auto sx = m.captures[3]; - uint x = parse!uint(sx, 16); - if (name !in target.table) - { - target.table[name] = set; - } - auto p = name in target.table; - *p |= x; - if (!aliasStr.empty) - { - target.aliases[name] = aliasStr; - aliasStr = ""; - } - } - })(inp, r); -} - -void loadPropertyAliases(string inp) -{ - auto r = regex(`^([\w0-9_]+)\s*;\s*([\w0-9_]+)\s*;\s*([\w0-9_]+)`); - scanUniData!((m){ - auto type = m.captures[1]; - auto target = m.captures[2].idup; - auto label = m.captures[3].idup; - if (target != label) - { - if (type == "blk") - blocks.aliases[target] = label; - else if (type == "gc") - general.aliases[target] = label; - else if (type == "sc") - scripts.aliases[target] = label; - } - })(inp, r); -} - -void loadNormalization(string inp) -{ - auto r = regex(`^(?:([0-9A-F]+)\.\.([0-9A-F]+)|([0-9A-F]+))\s*;\s*(NFK?[CD]_QC)\s*;\s*([NM])|#\s*[a-zA-Z_0-9]+=([a-zA-Z_0-9]+)`); - CodepointSet set; //workaround @@@BUG 6178 - scanUniData!((m){ - auto name = to!string(m.captures[4]) ~ to!string(m.captures[5]); - if (!m.captures[1].empty) - { - auto sa = m.captures[1]; - auto sb = m.captures[2]; - uint a = parse!uint(sa, 16); - uint b = parse!uint(sb, 16); - if (name !in normalization) - { - normalization[name] = set; - } - auto p = name in normalization; - p.add(a,b+1); - } - else if (!m.captures[3].empty) - { - auto sx = m.captures[3]; - uint x = parse!uint(sx, 16); - if (name !in normalization) - { - normalization[name] = set; - } - auto p = name in normalization; - *p |= x; - } - })(inp, r); -} - -void loadUnicodeData(string inp) -{ - auto f = File(inp); - - dchar characterStart; - bool haveRangeStart, haveRangeEnd; - - CodepointSet all; - foreach (line; f.byLine) - { - auto fields = split(line, ";"); - //codepoint, name, General_Category, Canonical_Combining_Class, Bidi_Class, - //Decomp_Type&Mapping, upper case mapping, lower case mapping, title case mapping - auto codepoint = fields[0]; - auto decomp = fields[5]; - auto name = fields[1]; - - dchar src = parse!uint(codepoint, 16); - - if (name.endsWith("First>")) - { - assert(!haveRangeStart); - characterStart = src; - haveRangeStart = true; - continue; - } - else if (name.endsWith("Last>")) - { - haveRangeStart = false; - haveRangeEnd = true; - } - else - { - assert(!haveRangeStart); - haveRangeEnd = false; - characterStart = src; - } - - auto generalCategory = fields[2]; - auto ccc = parse!uint(fields[3]); - auto upperCasePart = fields[12]; - auto lowerCasePart = fields[13]; - auto titleCasePart = fields[14]; - - void appendCaseTab(ref ushort[dchar] index, ref uint[] chars, char[] casePart) - { - if (!casePart.empty) - { - // if you have a range, you shouldn't have any casing provided - assert(!haveRangeEnd); - - uint ch = parse!uint(casePart, 16); - chars ~= ch; - assert(chars.length < ushort.max); - index[src] = cast(ushort)(chars.length-1); - } - } - - if (generalCategory !in general.table) - general.table[generalCategory.idup] = CodepointSet.init; - - all.add(characterStart, src+1); - combiningClass[ccc].add(characterStart, src+1); - general.table[generalCategory].add(characterStart, src+1); - - appendCaseTab(toLowerSimpleIndex, toLowerTab, lowerCasePart); - appendCaseTab(toUpperSimpleIndex, toUpperTab, upperCasePart); - appendCaseTab(toTitleSimpleIndex, toTitleTab, titleCasePart); - - if (!decomp.empty) - { - // none of the ranges in UnicodeData.txt have decompositions provided - assert(!haveRangeEnd); - - //stderr.writeln(codepoint, " ---> ", decomp); - - dstring dest; - bool compat = false; - if (decomp.startsWith(" ")) - decomp = decomp[1..$]; - if (decomp.front == '<') - { - decomp = findSplitAfter(decomp, ">")[1]; - compat = true; - } - auto vals = split(decomp, " "); - foreach (v; vals) - { - if (!v.empty) - dest ~= cast(dchar) parse!uint(v, 16); - } - if (!compat) - { - assert(dest.length <= 2, "cannonical decomposition has more then 2 codepoints?!"); - canonDecomp[src] = dest; - } - compatDecomp[src] = dest; - } - } - // compute Cn as all dchar we have not found in UnicodeData.txt - general.table["Cn"] = all.inverted; - general.aliases["Cn"] = "Unassigned"; - auto arr = combiningClass[1 .. 255]; - foreach (i, clazz; arr)//0 is a default for all of 1M+ codepoints - { - auto y = clazz.byCodepoint; - foreach (ch; y) - combiningMapping[ch] = cast(ubyte)(i+1); - } -} - -void loadSpecialCasing(string f) -{ - { - toLowerTabSimpleLen = cast(ushort) toLowerTab.length; - toUpperTabSimpleLen = cast(ushort) toUpperTab.length; - toTitleTabSimpleLen = cast(ushort) toTitleTab.length; - - // duplicate the simple indexes prior to adding our uncondtional rules also - toLowerIndex = toLowerSimpleIndex.dup; - toTitleIndex = toTitleSimpleIndex.dup; - toUpperIndex = toUpperSimpleIndex.dup; - } - - auto file = File(f); - auto r = regex(`([0-9A-F]+(?:\s*[0-9A-F]+)+);`, "g"); - foreach (line; file.byLine) - { - if (!line.empty && line[0] == '#') - { - if (line.canFind("Conditional Mappings")) - { - // TODO: we kinda need the conditional mappings for languages like Turkish - break; - } - else - continue; - } - - auto entries = line.match(r); - if (entries.empty) - continue; - auto pieces = array(entries.map!"a[1]"); - dchar ch = parse!uint(pieces[0], 16); - void processPiece(ref ushort[dchar] index, ref uint[] table, char[] piece) - { - uint[] mapped = piece.split - .map!(x=>parse!uint(x, 16)).array; - if (mapped.length == 1) - { - table ~= mapped[0]; - index[ch] = cast(ushort)(table.length - 1); - } - else - { - ushort idx = cast(ushort) table.length; - table ~= mapped; - table[idx] |= (mapped.length << 24); //upper 8bits - length of sequence - index[ch] = idx; - } - } - - // lower, title, upper - processPiece(toLowerIndex, toLowerTab, pieces[1]); - processPiece(toTitleIndex, toTitleTab, pieces[2]); - processPiece(toUpperIndex, toUpperTab, pieces[3]); - } -} - -auto recursivelyDecompose(dstring[dchar] decompTable) -{ - //apply recursively: - dstring[dchar] full; - foreach (k, v; decompTable) - { - dstring old, decomp=v; - do - { - old = decomp; - decomp = ""; - foreach (dchar ch; old) - { - if (ch in decompTable) - decomp ~= decompTable[ch]; - else - decomp ~= ch; - } - } - while (old != decomp); - full[k] = decomp; - } - return full; -} - -void loadExclusions(string inp) -{ - auto r = regex(`^([0-9A-F]+)`); - scanUniData!((m){ - auto piece = m.captures[1]; - uint a = parse!uint(piece, 16); - compExclusions |= cast(dchar) a; - })(inp, r); -} - -string charsetString(CodepointSet set, string sep=";\n") -{ - auto app = appender!(char[])(); - ubyte[] data = compressIntervals(set.byInterval); - assert(CodepointSet(decompressIntervals(data)) == set); - formattedWrite(app, "x\"%(%02X%)\";", data); - return cast(string) app.data; -} - -string identName(string s) -{ - auto app = appender!(char[])(); - foreach (c; s) - { - if (c == '-' || c == ' ') - app.put('_'); - else - app.put(c); - } - return cast(string) app.data; -} - -string uniformName(string s) -{ - auto app = appender!(char[])(); - foreach (c; s) - { - if (c != '-' && c != ' ' && c != '_') - app.put(std.ascii.toLower(c)); - } - return cast(string) app.data; -} - -void writeSets(File sink, PropertyTable src) -{ - with(sink) - { - writeln("private alias _T = ubyte[];"); - foreach (k, v; src.table) - { - writef("_T %s = ", identName(k)); - writeln(charsetString(v)); - } - } -} - -void writeAliasTable(File sink, string prefix, PropertyTable src) -{ - with(sink) - { - writeln("struct ", prefix); - writeln("{"); - writeln("private alias _U = immutable(UnicodeProperty);"); - writeln("@property static _U[] tab() pure { return _tab; }"); - writeln("static immutable:"); - writeSets(sink, src); - writeln("_U[] _tab = ["); - } - string[] lines; - string[] namesOnly; - auto app = appender!(char[])(); - auto keys = src.table.keys; - foreach (k; keys) - { - formattedWrite(app, "_U(\"%s\", %s),\n", k, identName(k)); - lines ~= app.data.idup; - namesOnly ~= uniformName(k); - app.shrinkTo(0); - if (k in src.aliases) - { - formattedWrite(app, "_U(\"%s\", %s),\n", src.aliases[k], identName(k)); - lines ~= app.data.idup; - namesOnly ~= uniformName(src.aliases[k]); - app.shrinkTo(0); - } - } - static bool ucmp(T)(T a, T b) { return propertyNameLess(a[0], b[0]); } - sort!ucmp(zip(namesOnly, lines)); - - with(sink) - { - foreach (i, v; lines) - { - write(lines[i]); - } - writeln("];"); - writeln("}"); - } -} - -void writeCaseFolding(File sink) -{ - with(sink) - { - write(mixedCCEntry); - - writeln("SCE simpleCaseTable(size_t i)"); - writeln("{"); - writef("static immutable uint[] t = x\""); - foreach (i, v; simpleTable) - { - if (i % 12 == 0) writeln(); - writef("%08X", SCE(v.ch, v.n, v.size).x); - } - - // Inspect max integer size, so efficient bit packing can be found: - stderr.writefln("max n: %X", simpleTable.maxElement!(x => x.n).n); // n: 2-bit - stderr.writefln("max ch: %X", simpleTable.maxElement!(x => x.ch).ch); // ch: 17-bit - stderr.writefln("max size: %X", simpleTable.maxElement!(x => x.size).size); // size: 3-bit - - writeln("\";"); - writeln("return SCE(t[i]);"); - writeln("}"); - writeln("@property FCE fullCaseTable(size_t index) nothrow @nogc @safe pure"); - writeln("{"); - writef("static immutable ulong[] t = x\""); - int[4] maxS = 0; - foreach (i, v; fullTable) - { - foreach (j; 0 .. v.entry_len) - maxS[j] = max(maxS[j], v.value[j]); - - if (v.entry_len > 1) - { - assert(v.n >= 1); // meaning that start of bucket is always single char - } - if (i % 6 == 0) writeln(); - writef("%016X", FCE(v.seq, v.n, v.size, v.entry_len).x); - } - writeln("\";"); - writeln("return FCE(t[index]);"); - writeln("}"); - import core.bitop : bsr; - stderr.writefln("max seq bits: [%d, %d, %d]", 1 + bsr(maxS[0]), 1 + bsr(maxS[1]), 1 + bsr(maxS[2])); //[17, 11, 10] - stderr.writefln("max n = %d", fullTable.map!(x => x.n).maxElement); // 3 - stderr.writefln("max size = %d", fullTable.map!(x => x.size).maxElement); // 4 - stderr.writefln("max entry_len = %d", fullTable.map!(x => x.entry_len).maxElement); // 3 - } -} - -void writeTries(File sink) -{ - ushort[dchar] simpleIndices; - foreach (i, v; array(map!(x => x.ch)(simpleTable))) - simpleIndices[v] = cast(ushort) i; - - ushort[dchar] fullIndices; - foreach (i, v; fullTable) - { - if (v.entry_len == 1) - fullIndices[v.seq[0]] = cast(ushort) i; - } - - //these 2 only for verification of Trie code itself - auto st = codepointTrie!(ushort, 12, 9)( - zip(simpleIndices.values, simpleIndices.keys).array, ushort.max); - auto ft = codepointTrie!(ushort, 12, 9)( - zip(fullIndices.values, fullIndices.keys).array, ushort.max); - - foreach (k, v; simpleIndices) - { - assert(st[k] == simpleIndices[k]); - } - - foreach (k, v; fullIndices) - { - assert(ft[k] == fullIndices[k]); - } - - writeBest3Level(sink, "lowerCase", lowerCaseSet); - writeBest3Level(sink, "upperCase", upperCaseSet); - //writeBest3Level("titleCase", titleCaseSet); - writeBest3Level(sink, "simpleCase", simpleIndices, ushort.max); - writeBest3Level(sink, "fullCase", fullIndices, ushort.max); - - //common isXXX properties - auto props = general.table; - CodepointSet alpha = props["Alphabetic"]; //it includes some numbers, symbols & marks - CodepointSet mark = props["Mn"] | props["Me"] | props["Mc"]; - CodepointSet number = props["Nd"] | props["Nl"] | props["No"]; - CodepointSet punctuation = props["Pd"] | props["Ps"] | props["Pe"] - | props["Pc"] | props["Po"] | props["Pi"] | props["Pf"]; - CodepointSet symbol = props["Sm"] | props["Sc"] | props["Sk"] | props["So"]; - CodepointSet graphical = alpha | mark | number | punctuation | symbol | props["Zs"]; - CodepointSet nonCharacter = props["Cn"]; - - - writeBest3Level(sink, "alpha", alpha); - writeBest3Level(sink, "mark", mark); - writeBest3Level(sink, "number", number); - writeBest3Level(sink, "punctuation", punctuation); - writeBest3Level(sink, "symbol", symbol); - writeBest3Level(sink, "graphical", graphical); - writeBest4Level(sink, "nonCharacter", nonCharacter); - -} - -void writeNormalizationTries(File sink) -{ - CodepointSet nfcQC = normalization["NFC_QCN"] | normalization["NFC_QCM"]; - CodepointSet nfdQC = normalization["NFD_QCN"]; - CodepointSet nfkcQC = normalization["NFKC_QCN"] | normalization["NFKC_QCM"]; - CodepointSet nfkdQC = normalization["NFKD_QCN"]; - writeBest3Level(sink, "nfcQC", nfcQC); - writeBest3Level(sink, "nfdQC", nfdQC); - writeBest3Level(sink, "nfkcQC", nfkcQC); - writeBest3Level(sink, "nfkdQC", nfkdQC); -} - -void writeGraphemeTries(File sink) -{ - //few specifics for grapheme cluster breaking algorithm - // - auto props = general.table; - writeBest3Level(sink, "hangulLV", hangul.table["LV"]); - writeBest3Level(sink, "hangulLVT", hangul.table["LVT"]); - - // Grapheme specific information - writeBest3Level(sink, "prepend", graphemeBreaks.table["Prepend"]); - writeBest3Level(sink, "control", graphemeBreaks.table["Control"]); - - // We use Grapheme_Cluster_Break=SpacingMark instead of GC=Mc, - // Grapheme_Cluster_Break=SpacingMark is derived from GC=Mc and includes other general category values - writeBest3Level(sink, "spacingMark", graphemeBreaks.table["SpacingMark"]); - - // We use the Grapheme_Cluster_Break=Extend instead of Grapheme_Extend, - // Grapheme_Cluster_Break=Extend is derived from Grapheme_Extend and is more complete - writeBest3Level(sink, "graphemeExtend", graphemeBreaks.table["Extend"]); - - // emoji related data - writeBest3Level(sink, "Extended_Pictographic", emojiData.table["Extended_Pictographic"]); -} - -/// Write a function that returns a dchar[] with data stored in `table` -void writeDstringTable(File sink, string name, const dchar[] table) -{ - sink.writefln("dstring %s() nothrow @nogc pure @safe {\nstatic immutable dchar[%d] t =", name, table.length); - sink.writeDstring(table); - sink.writeln(";\nreturn t[];\n}"); -} - -/// Write a function that returns a uint[] with data stored in `table` -void writeUintTable(File sink, string name, const uint[] table) -{ - sink.writefln("immutable(uint)[] %s() nothrow @nogc pure @safe {\nstatic immutable uint[] t =", name, ); - sink.writeUintArray(table); - sink.writeln(";\nreturn t;\n}"); -} - -void writeCaseCoversion(File sink) -{ - { - sink.writefln("enum MAX_SIMPLE_LOWER = %d;", toLowerTabSimpleLen); - sink.writefln("enum MAX_SIMPLE_UPPER = %d;", toUpperTabSimpleLen); - sink.writefln("enum MAX_SIMPLE_TITLE = %d;", toTitleTabSimpleLen); - } - - { - // these are case mappings that also utilize the unconditional SpecialCasing.txt rules - writeBest3Level(sink, "toUpperIndex", toUpperIndex, ushort.max); - writeBest3Level(sink, "toLowerIndex", toLowerIndex, ushort.max); - writeBest3Level(sink, "toTitleIndex", toTitleIndex, ushort.max); - } - - { - // these are all case mapping tables that are 1:1 acquired from UnicodeData.txt - writeBest3Level(sink, "toUpperSimpleIndex", toUpperSimpleIndex, ushort.max); - writeBest3Level(sink, "toLowerSimpleIndex", toLowerSimpleIndex, ushort.max); - writeBest3Level(sink, "toTitleSimpleIndex", toTitleSimpleIndex, ushort.max); - } - - writeUintTable(sink, "toUpperTable", toUpperTab); - writeUintTable(sink, "toLowerTable", toLowerTab); - writeUintTable(sink, "toTitleTable", toTitleTab); -} - -void writeDecomposition(File sink) -{ - auto fullCanon = recursivelyDecompose(canonDecomp); - auto fullCompat = recursivelyDecompose(compatDecomp); - dstring decompCanonFlat = "\0"~array(fullCanon.values).sort.uniq.join("\0")~"\0"; - dstring decompCompatFlat = "\0"~array(fullCompat.values).sort.uniq.join("\0")~"\0"; - stderr.writeln("Canon flattened: ", decompCanonFlat.length); - stderr.writeln("Compat flattened: ", decompCompatFlat.length); - - ushort[dchar] mappingCanon; - ushort[dchar] mappingCompat; - //0 serves as doesn't decompose value - foreach (k, v; fullCanon) - { - size_t idx = decompCanonFlat.countUntil(v~"\0"); - enforce(idx != 0); - enforce(decompCanonFlat[idx .. idx+v.length] == v); - mappingCanon[k] = cast(ushort) idx; - } - foreach (k, v; fullCompat) - { - size_t idx = decompCompatFlat.countUntil(v~"\0"); - enforce(idx != 0); - enforce(decompCompatFlat[idx .. idx+v.length] == v); - mappingCompat[k] = cast(ushort) idx; - } - enforce(decompCanonFlat.length < 2^^16); - enforce(decompCompatFlat.length < 2^^16); - - //these 2 are just self-test for Trie template code - auto compatRange = zip(mappingCompat.values, mappingCompat.keys).array; - auto canonRange = zip(mappingCanon.values, mappingCanon.keys).array; - auto compatTrie = codepointTrie!(ushort, 12, 9)(compatRange, 0); - auto canonTrie = codepointTrie!(ushort, 12, 9)(canonRange, 0); - import std.string; - foreach (k, v; fullCompat) - { - auto idx = compatTrie[k]; - enforce(idx == mappingCompat[k], "failed on compat"); - size_t len = decompCompatFlat[idx..$].countUntil(0); - enforce(decompCompatFlat[idx .. idx+len] == v, - format("failed on compat: '%( 0x0%5x %)' not found", v)); - } - foreach (k, v; fullCanon) - { - auto idx = canonTrie[k]; - enforce(idx == mappingCanon[k], "failed on canon"); - size_t len = decompCanonFlat[idx..$].countUntil(0); - enforce(decompCanonFlat[idx .. idx+len] == v, - format("failed on canon: '%( 0x%5x %)' not found", v)); - } - - writeBest3Level(sink, "compatMapping", mappingCompat, cast(ushort) 0); - writeBest3Level(sink, "canonMapping", mappingCanon, cast(ushort) 0); - - writeDstringTable(sink, "decompCanonTable", decompCanonFlat); - writeDstringTable(sink, "decompCompatTable", decompCompatFlat); -} - -void writeFunctions(File sink) -{ - auto format = general.table["Cf"]; - auto space = general.table["Zs"]; - auto control = general.table["Cc"]; - auto whitespace = general.table["White_Space"]; - - //hangul L, V, T - auto hangL = hangul.table["L"]; - auto hangV = hangul.table["V"]; - auto hangT = hangul.table["T"]; - with(sink) - { - writeln(format.toSourceCode("isFormatGen")); - writeln(control.toSourceCode("isControlGen")); - writeln(space.toSourceCode("isSpaceGen")); - writeln(whitespace.toSourceCode("isWhiteGen")); - writeln(hangL.toSourceCode("isHangL")); - writeln(hangV.toSourceCode("isHangV")); - writeln(hangT.toSourceCode("isHangT")); - } -} - -/// Write a `dchar[]` as hex string -void writeUintArray(T:dchar)(File sink, const T[] tab) -{ - size_t lineCount = 1; - sink.write("x\""); - foreach (i, elem; tab) - { - if ((i % 12) == 0) - sink.write("\n"); - - sink.writef("%08X", elem); - } - sink.write("\""); -} - -/// Write a `dchar[]` as a dstring ""d -void writeDstring(T:dchar)(File sink, const T[] tab) -{ - size_t lineCount = 1; - sink.write("\""); - foreach (elem; tab) - { - if (lineCount >= 110) - { - sink.write("\"d~\n\""); - lineCount = 1; - } - if (elem >= 0x10FFFF) - { - // invalid dchar, but might have extra info bit-packed in upper bits - sink.writef("\"d~cast(dchar) 0x%08X~\"", elem); - lineCount += 25; - } - else if (elem <= 0xFFFF) - { - sink.writef("\\u%04X", elem); - lineCount += 6; - } - else - { - sink.writef("\\U%08X", elem); - lineCount += 10; - } - } - sink.write("\"d"); -} - - -void writeCompositionTable(File sink) -{ - dchar[dstring] composeTab; - //construct compositions table - foreach (dchar k, dstring v; canonDecomp) - { - if (v.length != 2)//singleton - continue; - if (v[0] in combiningMapping) //non-starter - continue; - if (k in combiningMapping) //combines to non-starter - continue; - if (compExclusions[k]) // non-derivable exclusions - continue; - composeTab[v] = k; - } - - Tuple!(dchar, dchar, dchar)[] triples; - foreach (dstring key, dchar val; composeTab) - triples ~= Tuple!(dchar, dchar, dchar)(key[0], key[1], val); - multiSort!("a[0] < b[0]", "a[1] < b[1]")(triples); - //map to the triplets array - ushort[dchar] trimap; - dchar old = triples[0][0]; - auto r = triples[]; - for (size_t idx = 0;;) - { - ptrdiff_t cnt = countUntil!(x => x[0] != old)(r); - if (cnt == -1)//end of input - cnt = r.length; - assert(idx < 2048); - assert(cnt < 32); - trimap[old] = to!ushort(idx | (cnt << 11)); - idx += cnt; - if (idx == triples.length) - break; - old = r[cnt][0]; - r = r[cnt..$]; - } - - auto triT = codepointTrie!(ushort, 12, 9)(trimap.toPairs, ushort.max); - auto dupletes = triples.map!(x => tuple(x[1], x[2])).array; - foreach (dstring key, dchar val; composeTab) - { - size_t pack = triT[key[0]]; - assert(pack != ushort.max); - size_t idx = pack & ((1 << 11) - 1), cnt = pack >> 11; - auto f = dupletes[idx .. idx+cnt].find!(x => x[0] == key[1]); - assert(!f.empty); - // & starts with the right value - assert(f.front[1] == val); - } - with(sink) - { - writeln("enum composeIdxMask = (1 << 11) - 1, composeCntShift = 11;"); - write("enum compositionJumpTrieEntries = TrieEntry!(ushort, 12, 9)("); - triT.storeTrie(sink.lockingTextWriter()); - writeln(");"); - writeDstringTable(sink, "compositionTable", dupletes.map!(x => only(x.expand)).joiner.array); - } -} - -void writeCombining(File sink) -{ - auto ct = codepointTrie!(ubyte, 7, 5, 9)(combiningMapping.toPairs); - foreach (i, clazz; combiningClass[1 .. 255])//0 is a default for all of 1M+ codepoints - { - foreach (ch; clazz.byCodepoint) - assert(ct[ch] == i+1); - } - writeBest3Level(sink, "combiningClass", combiningMapping); -} - -//fussy compare for unicode property names as per UTS-18 -int comparePropertyName(Char)(const(Char)[] a, const(Char)[] b) -{ - for (;;) - { - while (!a.empty && (isWhite(a.front) || a.front == '-' || a.front =='_')) - { - a.popFront(); - } - while (!b.empty && (isWhite(b.front) || b.front == '-' || b.front =='_')) - { - b.popFront(); - } - if (a.empty) - return b.empty ? 0 : -1; - if (b.empty) - return 1; - // names are all in ASCII either way though whitespace might be unicode - auto ca = std.ascii.toLower(a.front), cb = std.ascii.toLower(b.front); - if (ca > cb) - return 1; - else if ( ca < cb) - return -1; - a.popFront(); - b.popFront(); - } -} - -bool propertyNameLess(Char)(const(Char)[] a, const(Char)[] b) -{ - return comparePropertyName(a, b) < 0; -} - -//meta helpers to generate and pick the best trie by size & levels - -void writeBest2Level(Set)(File sink, string name, Set set) - if (isCodepointSet!Set) -{ - alias List = TypeTuple!(5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16); - size_t min = size_t.max; - void delegate(File) write; - foreach (lvl_1; List) - { - enum lvl_2 = 21-lvl_1; - auto t = codepointSetTrie!(lvl_1, lvl_2)(set); - if (t.bytes < min) - { - min = t.bytes; - write = createPrinter!(lvl_1, lvl_2)(name, t); - } - } - write(sink); -} - -void writeBest2Level(V, K)(File sink, string name, V[K] map, V defValue=V.init) -{ - alias List = TypeTuple!(5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16); - size_t min = size_t.max; - void delegate(File) write; - auto range = zip(map.values, map.keys).array; - foreach (lvl_1; List) - { - enum lvl_2 = 21-lvl_1; - alias codepointTrie!(V, lvl_1, lvl_2) CurTrie; - CurTrie t = CurTrie(range, defValue); - if (t.bytes < min) - { - min = t.bytes; - write = createPrinter!(lvl_1, lvl_2)(name, t); - } - } - write(sink); -} - -alias List_1 = TypeTuple!(4, 5, 6, 7, 8); - -auto writeBest3Level(Set)(File sink, string name, Set set) - if (isCodepointSet!Set) -{ - // access speed trumps size, power of 2 is faster to access - // e.g. 9, 5, 7 is far slower then 8, 5, 8 because of how bits breakdown: - // 8-5-8: indexes are 21-8 = 13 bits, 13-5 = 8 bits, fits into a byte - // 9-5-7: indexes are 21-7 = 14 bits, 14-5 = 9 bits, doesn't fit into a byte (!) - - // e.g. 8-5-8 is one of hand picked that is a very close match - // to the best packing - void delegate(File) write; - - alias List = TypeTuple!(4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15); - size_t min = size_t.max; - foreach (lvl_1; List_1)//to have the first stage index fit in byte - { - foreach (lvl_2; List) - { - static if (lvl_1 + lvl_2 <= 16)//so that 2nd stage fits in ushort - { - enum lvl_3 = 21-lvl_2-lvl_1; - auto t = codepointSetTrie!(lvl_1, lvl_2, lvl_3)(set); - if (t.bytes < min) - { - min = t.bytes; - write = createPrinter!(lvl_1, lvl_2, lvl_3)(name, t); - } - } - } - } - write(sink); -} - -void writeBest3Level(V, K)(File sink, string name, V[K] map, V defValue=V.init) -{ - void delegate(File) write; - alias List = TypeTuple!(4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15); - size_t min = size_t.max; - auto range = zip(map.values, map.keys).array; - foreach (lvl_1; List_1)//to have the first stage index fit in byte - { - foreach (lvl_2; List) - { - static if (lvl_1 + lvl_2 <= 16)// into ushort - { - enum lvl_3 = 21-lvl_2-lvl_1; - auto t = codepointTrie!(V, lvl_1, lvl_2, lvl_3) (range, defValue); - if (t.bytes < min) - { - min = t.bytes; - write = createPrinter!(lvl_1, lvl_2, lvl_3)(name, t); - } - } - } - } - write(sink); -} - -void writeBest4Level(Set)(File sink, string name, Set set) -{ - alias List = TypeTuple!(4, 5, 6, 7, 8, 9, 10, 11, 12, 13); - size_t min = size_t.max; - void delegate(File) write; - foreach (lvl_1; List_1)//to have the first stage index fit in byte - { - foreach (lvl_2; List) - { - foreach (lvl_3; List) - { - static if (lvl_1 + lvl_2 + lvl_3 <= 16) - { - enum lvl_4 = 21-lvl_3-lvl_2-lvl_1; - auto t = codepointSetTrie!(lvl_1, lvl_2, lvl_3, lvl_4)(set); - if (t.bytes < min) - { - min = t.bytes; - write = createPrinter!(lvl_1, lvl_2, lvl_3, lvl_4)(name, t); - } - } - } - } - } - write(sink); -} - -template createPrinter(Params...) -{ - void delegate(File) createPrinter(T)(string name, T trie) - { - return (File sink){ - sink.writef("//%d bytes\nenum %sTrieEntries = TrieEntry!(%s", - trie.bytes, name, Unqual!(typeof(T.init[0])).stringof); - foreach (lvl; Params[0..$]) - sink.writef(", %d", lvl); - sink.write(")("); - trie.storeTrie(sink.lockingTextWriter()); - sink.writeln(");"); - }; - } -} - -void storeTrie(T, O)(T trie, O sink) -{ - import std.format.write : formattedWrite; - void store(size_t[] arr) - { - formattedWrite(sink, "x\""); - foreach (i; 0 .. arr.length) - { - static if (size_t.sizeof == 8) - { - formattedWrite(sink, (i % 6) == 0 ? "\n" : ""); - formattedWrite(sink, "%016X", arr[i]); - } - else - { - formattedWrite(sink, (i % 12) == 0 ? "\n" : ""); - formattedWrite(sink, "%08X", arr[i]); - } - } - formattedWrite(sink, "\",\n"); - } - // Access private members - auto table = __traits(getMember, trie, "_table"); - store(__traits(getMember, table, "offsets")); - store(__traits(getMember, table, "sz")); - store(__traits(getMember, table, "storage")); -} diff --git a/phobos/unittest.d b/phobos/unittest.d deleted file mode 100644 index 153d2a8..0000000 --- a/phobos/unittest.d +++ /dev/null @@ -1,121 +0,0 @@ -// Written in the D programming language. - -/** - * This test program pulls in all the library modules in order to run the unit - * tests on them. Then, it prints out the arguments passed to main(). - * - * Copyright: Copyright Digital Mars 2000 - 2009. - * License: $(HTTP www.boost.org/LICENSE_1_0.txt, Boost License 1.0). - * Authors: $(HTTP digitalmars.com, Walter Bright) - * - * Copyright Digital Mars 2000 - 2009. - * 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) - */ - -public import std.base64; -public import std.compiler; -public import std.concurrency; -public import std.conv; -public import std.container; -public import std.datetime; -public import std.demangle; -public import std.file; -public import std.format; -public import std.getopt; -public import std.math; -public import std.mathspecial; -public import std.mmfile; -public import std.outbuffer; -public import std.parallelism; -public import std.path; -public import std.process; -public import std.random; -public import std.regex; -public import std.signals; -//public import std.slist; -public import std.socket; -public import std.stdint; -public import std.stdio; -public import std.string; -public import std.system; -public import std.traits; -public import std.typetuple; -public import std.uni; -public import std.uri; -public import std.utf; -public import std.uuid; -public import std.variant; -public import std.zip; -public import std.zlib; -public import std.net.isemail; -public import std.net.curl; -public import std.digest; -public import std.digest.crc; -public import std.digest.sha; -public import std.digest.md; -public import std.digest.hmac; - -int main(string[] args) -{ - // Bring in unit test for module by referencing function in it - - cast(void) cmp("foo", "bar"); // string - cast(void) filenameCharCmp('a', 'b'); // path - cast(void) isNaN(1.0); // math - std.conv.to!double("1.0"); // std.conv - OutBuffer b = new OutBuffer(); // outbuffer - auto r = regex(""); // regex - uint ranseed = std.random.unpredictableSeed; - thisTid; - int[] a; - import std.algorithm.sorting : sort; - import std.algorithm.mutation : reverse; - reverse(a); // adi - sort(a); // qsort - Clock.currTime(); // datetime - cast(void) isValidDchar(cast(dchar) 0); // utf - string s1 = "http://www.digitalmars.com/~fred/fredsRX.html#foo end!"; - assert(uriLength(s1) == 49); - std.zlib.adler32(0,null); // D.zlib - auto t = task!cmp("foo", "bar"); // parallelism - - printf("args.length = %d\n", cast(int)args.length); - for (int i = 0; i < args.length; i++) - printf("args[%d] = '%.*s'\n", i, cast(int)args[i].length, args[i].ptr); - - int[3] x; - x[0] = 3; - x[1] = 45; - x[2] = -1; - sort(x[]); - assert(x[0] == -1); - assert(x[1] == 3); - assert(x[2] == 45); - - cast(void) std.math.sin(3.0); - cast(void) std.mathspecial.gamma(6.2); - - cast(void) std.demangle.demangle("hello"); - - cast(void) std.uni.isAlpha('A'); - - std.file.exists("foo"); - - foreach_reverse (dchar d; "hello"c) { } - foreach_reverse (k, dchar d; "hello"c) { } - - std.signals.linkin(); - - bool isEmail = std.net.isemail.isEmail("abc"); - auto http = std.net.curl.HTTP("dlang.org"); - auto uuid = randomUUID(); - - auto md5 = md5Of("hello"); - auto sha1 = sha1Of("hello"); - auto crc = crc32Of("hello"); - auto string = toHexString(crc); - puts("Success!"); - return 0; -}